1window.AB = window.AB || {}; 2window.AB.DATALAYER = {}; 3window.appEventData = window.appEventData || []; 4window.digitalData = window.digitalData || {}; 5 6'use strict'; 7 8// Comments in this file are of pure consistency reasons and offer no description externally 9(function () { 10 this.pathSections = []; 11 this.oneTagEnabledBanners = ['safeway', 'albertsons', 'randalls', 'vons', 'shaws', 'starmarket', 'jewelosco', 'tomthumb', 'acmemarkets', 'pavilions', 'shopamigos', 'shopalbertsonsmarket', 'shopmarketstreet', 'shopunitedsupermarkets', 'unitedsupermarkets', 'albertsonsmarket', 'amigos', 'marketstreet']; 12 13 this.initPathSections = function (getExistingPathSections) { 14 const pageUrl = window.location.pathname.startsWith('/orderahead') ? document.location.pathname.replace(/\/(orderahead|store\/\d+)/gi, '') : window.location.pathname; 15 if (getExistingPathSections) { 16 return [digitalData?.pageDetails?.subSection1, digitalData?.pageDetails?.subSection2, digitalData?.pageDetails?.subSection3, digitalData?.pageDetails?.subSection4].filter((x) => { return x && x !== '' }); 17 } else { 18 this.pathSections = (pageUrl.indexOf('.') > -1 ? pageUrl.substring(0, pageUrl.indexOf('.')) : pageUrl).split('/').filter(x => { return x && x !== 'shop' && x !== 'mf' && x !== 'vineandcellar' && x != '' && x?.toLowerCase()?.indexOf('chat_') == -1 }); 19 } 20 } 21 22 this.initPathSections(); 23 this.pathSections[0] = this.pathSections[0] ? (this.pathSections[0] === 'weeklyad' ? 'weekly-ad' : this.pathSections[0]) : 'home'; 24 25 this.DEFAULT_DELIVERY_BANNER = 'same banner delivery'; 26 27 this.p13nData = ''; 28 this.pfmSourceMktpCategories = ''; 29 30 this.selectedSearchFacets = []; 31 this.selectedSearchSelections = []; 32 this.pastPurchasedProducts = 0; 33 34 this.LoginStatus = { 35 GUEST: 'guest', 36 REGISTERED: 'registered' 37 }; 38 39 this.Flag = { 40 SIGN_IN: 1, // 00000001 41 COUPON_CLIPPED: 2, // 00000010 42 PRODUCT_ADDED: 4 // 00000100 43 }; 44 45 this.KmsiStatus = { 46 'true': 'kmsi', 47 'false': 'nonkmsi', 48 'logout': 'nonkmsi', 49 'error': 'errorkmsi' 50 }; 51 52 this.KmsiStatusReSignIn = { 53 'true': 'kmsi-reSignIn', 54 'false': 'nonkmsi-reSignIn', 55 }; 56 57 this.cookieNames = { 58 MODAL_LINK: 'SAFEWAY_MODAL_LINK', 59 PASSWORD_RESET: 'PasswordReset', 60 KMSI: 'SAFEWAY_KMSI', 61 RE_SIGN_IN: 'SAFEWAY_RE_SIGN_IN' 62 }; 63 64 this.JourneyStatusCode = { 65 EMPTY: 0, // 00000000 66 NO_ACTION: this.Flag.SIGN_IN, // 00000001 67 COUPON_CLIPPED: this.Flag.SIGN_IN | this.Flag.COUPON_CLIPPED, // 00000011 68 PRODUCT_ADDED: this.Flag.SIGN_IN | this.Flag.PRODUCT_ADDED, // 00000101 69 COUPONS_CLIPPED_PRODUCTS_ADDED: this.Flag.SIGN_IN | this.Flag.COUPON_CLIPPED | this.Flag.PRODUCT_ADDED // 00000111 70 }; 71 72 this.JOURNEY_STATUSES = []; 73 74 this.JOURNEY_STATUSES[this.JourneyStatusCode.EMPTY] = ''; 75 this.JOURNEY_STATUSES[this.JourneyStatusCode.NO_ACTION] = 'no_action'; 76 this.JOURNEY_STATUSES[this.JourneyStatusCode.COUPON_CLIPPED] = 'clipped_coupon'; 77 this.JOURNEY_STATUSES[this.JourneyStatusCode.PRODUCT_ADDED] = 'add_products'; 78 this.JOURNEY_STATUSES[this.JourneyStatusCode.COUPONS_CLIPPED_PRODUCTS_ADDED] = 'coupon_products_added'; 79 80 this.arrThirParty = ['instacart']; 81 82 83 this.EVENTS = { 84 ERROR: 'error', 85 PAGE_LOADED: 'pageLoaded', 86 ACTION_LINK: 'actionLink', 87 MODAL_VIEW: 'modalView', 88 ACCORDION_CLICKED: 'accordion_header_clicked', 89 REGISTRATION: 'registration', 90 SEARCH: 'search_results_page', 91 SEARCH_IMPRESSIONS: 'search_results_impressions', 92 CATEGORY_IMPRESSIONS: 'category_impressions', 93 DEALS_IMPRESSIONS: 'deals_impressions', 94 ZERO_SEARCH_RESULTS: 'zero_search_results', 95 UNCLIP: 'remove_coupon', 96 REWARDS: 'reward_redeemed', 97 REWARD_UNCLIPPED: 'reward_unclipped', 98 PRODUCT_LOADED: 'pdp_page', 99 MINICART: 'mini_cart', 100 MODAL_CLICK: 'modalClick', 101 MODAL_MESSAGE: 'userMessage', 102 LINK_CLICK: 'linkClick', 103 CRS_CLICK: 'crs-click', 104 COUPONNR: 'num_coupons', 105 COUPONCLIPPED: 'coupon_clipped', 106 BATCHCOUPONCLIPPED: 'batch_coupon_clipped', 107 SIGNIN: 'sign-in', 108 CREATEACC: 'create-account-success', 109 SIGN_OUT: 'ecom_logout', 110 SIGN_OUT_USER: 'ecom_logout_user', 111 PATHWAY_WIDGET_VIEW: 'br_pathway_view', 112 ADA: 'ada_user_input', 113 DS_CONFIRMATION: 'delivery_subscription_confirmation', 114 FULL_CART: 'full_cart', 115 TRANSACTION_COMPLETE: 'transaction_complete', 116 EDIT_CART: 'edit_cart', 117 SELECTIVE_CART: 'selective_view_cart' 118 }; 119 120 this.timeLimitedFeatures = { 121 /** 122 * used for setting expiration date for features 123 * to use, add feature name here with timeLimit populated with date, eg.: 124 * featureName: { timeLimit: new Date('September 21, 2024').getTime() } 125 * then send isTrackingExpired flag in feature you want to timebox based on value under featureName.timeLimit 126 */ 127 }; 128 129 this.isB2BPage = function () { 130 return window.location.hostname.indexOf('business') !== -1; 131 } 132 133 /** 134 * Returns banner name 135 */ 136 this.getBanner = function (shouldNotReturnB2B) { 137 let banner = ''; 138 if (document.getElementById('banner')?.dataset?.banner) { 139 banner = document.getElementById('banner').dataset.banner; 140 } else if (typeof (window.SWY?.CONFIGSERVICE?.getResolvedBanner) === 'function' && window.SWY?.CONFIGSERVICE?.getResolvedBanner() && window.SWY?.CONFIGSERVICE?.getResolvedBanner() !== undefined) { 141 banner = window.SWY.CONFIGSERVICE.getResolvedBanner(); 142 } else { 143 try { 144 banner = window.location.hostname.split('.')?.filter(x => { return x !== 'com' && x.search('www') === -1 && x.search('business') === -1 })[0] 145 } catch (e) { 146 console.log(`@AnalyticsTeam - Banner resolve error: ${e}`); 147 }; 148 } 149 banner = `${AB.DATALAYER.isB2BPage() && !shouldNotReturnB2B ? 'business-' : ''}${banner}`; 150 return banner; 151 } 152 153 this.isPageLoadEvent = function (event) { 154 const pageLoadEvents = ['pageLoaded', 'pdp_page', 'search_results_page', 'search_results_impressions', 'checkout', 'checkout_step1', 'checkout_step2', 'checkout_step3', 'checkout_step4']; 155 return pageLoadEvents.indexOf(event) !== -1; 156 } 157 158 this.isProductActionEvent = function (event) { 159 const productActions = ['cart_add', 'add_quantity', 'remove_cart', 'remove_quantity']; 160 return productActions.indexOf(event) !== -1; 161 } 162 163 this.isOneTagCartEvent = function (event) { 164 const cartEvents = ['cart_add', 'mini_cart', 'add_quantity', 'remove_cart', 'remove_quantity']; 165 return cartEvents.indexOf(event) !== -1; 166 } 167 168 this.isOneTagCheckoutEvent = function (event) { 169 const checkoutSteps = ['checkout', 'checkout_step1', 'checkout_step2', 'checkout_step3', 'transaction_complete']; 170 return checkoutSteps.indexOf(event) !== -1; 171 } 172 173 this.shouldDelayEvent = function (event) { 174 const delayedEventsArray = ['search_results_page', 'zero_search_results', 'pageLoaded', 'pdp_page', 'search_results_impressions', 'category_impressions']; 175 return delayedEventsArray.indexOf(event) !== -1; 176 } 177 178 this.isCRSClickEvent = function (event, data) { 179 const crsLinkNames = ['product-click', 'coupon-clip-click', 'offer-details-click', 'add-to-list-click', 'pause-click', 'play-click', 'item-click', 'swipe-left-click', 'swipe-right-click'];
180 const isUniversalCRSClickEvent = event === 'crs-click' 181 && data?.link 182 && data.link.isCRSClick === true 183 && !!data?.absPlacement; 184 return AB.DATALAYER.isHomePage() 185 && !AB.DATALAYER.isB2BPage() 186 && (isUniversalCRSClickEvent 187 || (event === 'linkClick' 188 && data?.link 189 && (data.link.isCRSClick === true || crsLinkNames.indexOf(data.link.linkName) !== -1) 190 && !!data?.absPlacement)); 191 } 192 193 this.isOneTagTrackedLink = function (event, data) { 194 const oneTagEnabledLinkClicks = [`${digitalData?.pageDetails?.subSection1}-product-click`, 'flex-impressions', 'flex-experiment-exposure-loaded', 'bundle-impressions', 'image-impressions', 'text-impressions', 'recipe-impressions', 'deals-impressions', 'experiment-exposure-loaded-deals']; 195 const stickyLinkName = data?.link?.linkName || ''; 196 const isStickyBannerClick = stickyLinkName.indexOf('bannerClicked|sticky-banner:') === 0 197 || stickyLinkName.indexOf('sticky-banner|link|') === 0 198 || stickyLinkName.indexOf('cta|sticky-banner-dismiss-') === 0 199 || stickyLinkName.indexOf('cta|sticky-promo-ad-banner-dismiss-') === 0; 200 const oneTagSpecificLinkNames = ['offer-details|coupon', 'banner-clicked', 'deals-carousel-right', 'deals-carousel-left', 'product-carousel-right', 'product-carousel-left', 'flex-impressions', 'flex-experiment-exposure-loaded', 'bundle-impressions']; 201 return (!AB.DATALAYER.isB2BPage() 202 && event === 'linkClick' 203 && data?.link 204 && (oneTagEnabledLinkClicks.indexOf(data.link.linkName) !== -1 || oneTagSpecificLinkNames.indexOf(data.link.oneTagLinkName) !== -1 || isStickyBannerClick)) 205 || AB.DATALAYER.isCRSClickEvent(event, data); 206 } 207 208 this.isOneTagImpressionLink = function (event, data) { 209 const linksList = ['experiment-exposure-loaded', 'experiment-exposure-loaded-splash-screen', 'experiment-exposure-loaded-deals', 'experiment-exposure-loaded-banner', 'experiment-exposure-loaded-bundle', 'experiment-exposure', 'flex-experiment-exposure-loaded', 'banner-impressions-engagement-ladder', 'banner-impressions', 'headline-impressions', 'headline-standard-impressions', 'headline-hero-impressions', 'flex-impressions', 'bundle-impressions', 'image-impressions', 'text-impressions', 'recipe-impressions']; 210 const oneTagSpecificLinkNames = ['banner-clicked']; 211 return event === 'linkClick' && data?.link && ( 212 linksList.indexOf(data.link.linkName) !== -1 213 || oneTagSpecificLinkNames.indexOf(data.link.oneTagLinkName) !== -1 214 || (data.link.linkName === 'deals-impressions' && data?.pageFilter?.mediaType === 'deals-carousel-mktng-card') 215 ); 216 } 217 218 this.isOneTagGamOrAmcEvent = function (event, data) { 219 const linksList = ['gam-banner-impressions', 'gam-banner-exposure']; 220 const oneTagSpecificLinkNames = []; 221 return event === 'linkClick' && data?.link && (linksList.indexOf(data.link.linkName) !== -1 || oneTagSpecificLinkNames.indexOf(data.link.oneTagLinkName) !== -1); 222 } 223 224 this.isOneTagMiscEvent = function (event) { 225 const miscEvents = ['reward_redeemed', 'reward_unclipped']; 226 return miscEvents.indexOf(event) !== -1; 227 } 228 229 this.isOneTagMiscLinkClick = function (data) { 230 const oneTagLinkName = data?.link?.oneTagLinkName 231 const miscEvents = ['offer-details|coupon', 'deals-carousel-right', 'deals-carousel-left', 'product-carousel-right', 'product-carousel-left']; 232 return miscEvents.indexOf(oneTagLinkName) !== -1; 233 } 234 235 this.shouldSendOneTagLinkClick = function (event) { 236 const eventsToBeSentAsLC = ['reward_redeemed', 'reward_unclipped']; 237 return eventsToBeSentAsLC.indexOf(event) !== -1; 238 } 239 240 this.shouldSendDatatoOneTag = function (event, data) { 241 const oneTagEnabledEventsB2B = ['search_results_impressions', 'pdp_page', 'cart_add', 'category_impressions', 'transaction_complete','target-experiment-exposure']; 242 const oneTagEnabledEvents = ['product-exposed', 'product-impressions', 'banner-impressions', 'reward_redeemed', 'reward_unclipped', 'search_results_page', 'zero_search_results', 'target-experiment-exposure', 'custom-cake-exposed']; 243 return (oneTagEnabledEventsB2B.indexOf(event) !== -1 && AB.DATALAYER.isB2BPage() && AB.DATALAYER.oneTagEnabledBanners.indexOf(AB.DATALAYER.getBanner(true)) !== -1) || (oneTagEnabledEvents.indexOf(event) !== -1 && !AB.DATALAYER.isB2BPage()) || AB.DATALAYER.isOneTagTrackedLink(event, data); 244 } 245 246 /** 247 * Rounds up the passed value to specified number of decimals 248 * @param value - full value passed to be processed 249 * @param decimals - number of decimals that should be kept when value is rounded up 250 */ 251 this.roundUpValue = function (value, decimals) { 252 return Number(Math.round(value + 'e' + decimals) + 'e-' + decimals); 253 }; 254 255 this.getLSItem = function (item) { 256 if (typeof (Storage) !== 'undefined') { 257 let LSValue = localStorage.getItem(item) ? localStorage.getItem(item) : ''; 258 return LSValue; 259 } 260 } 261 262 this.setLSItem = async function (item, value) { 263 if (typeof (Storage) !== 'undefined') { 264 localStorage.setItem(item, value); 265 } 266 } 267 268 this.removeLSItem = async function (item) { 269 if (typeof (Storage) !== 'undefined') { 270 localStorage.removeItem(item); 271 } 272 } 273 274 this.handleGetLSValue = function (item) { 275 let value = AB.DATALAYER.getLSItem(item); 276 AB.DATALAYER.removeLSItem(item); 277 return value; 278 } 279
280 this.getSSItem = function (item) { 281 if (typeof (Storage) !== 'undefined') { 282 let SSValue = sessionStorage.getItem(item) ? sessionStorage.getItem(item) : ''; 283 return SSValue; 284 } 285 } 286 287 this.removeSSItem = async function (item) { 288 if (typeof (Storage) !== 'undefined') { 289 sessionStorage.removeItem(item); 290 } 291 } 292 293 this.safeJSONParse = function (valueToBeParsed) { 294 if (valueToBeParsed && valueToBeParsed !== '' && valueToBeParsed !== 'undefined') { 295 try { 296 return JSON.parse(valueToBeParsed); 297 } catch (error) { 298 console.error(`@AnalyticsTeam - Failed to parse JSON: ${error}`); 299 } 300 } 301 return {}; 302 } 303 304 /** 305 * Find the visible html element - equivalent of checking :visible in jquery 306 * @param queryElements html querySelectorAll value to find the visible status 307 * @returns Filtered Visible element array 308 */ 309 this.findVisibleElement = function (queryElements) { 310 if (queryElements) { 311 const visibleElems = []; 312 for (let i = 0; i < queryElements.length; ++i) { 313 if ((window.getComputedStyle(queryElements[i])).display !== 'none' && queryElements[i].offsetParent) { 314 visibleElems.push((queryElements[i])); 315 } 316 } 317 return visibleElems; 318 } else { 319 return []; 320 } 321 } 322 323 this.areCookiesEnabled = function () { 324 let isEnabled = navigator?.cookieEnabled ? true : false; 325 // return if cookies are enabled from browser 326 return isEnabled; 327 } 328 329 this.getResolvedCookieName = function (cname) { 330 let isB2B = false, 331 siteType = window.location.hostname && window.location.hostname.startsWith('business'), 332 cookieName = cname; 333 const sessionCookiePrefixes = ['SWY_', 'EComm', 'USED_', 'SAFEWAY_', 'abs_', 'absV']; 334 // check if cookie name does not start with B_ and on B2B site 335 if (cname && !cname.startsWith('B_') && siteType) { 336 for (let i = 0; i < sessionCookiePrefixes.length; i++) { 337 if (cname.startsWith(sessionCookiePrefixes[i])) { 338 isB2B = true; 339 } 340 } 341 } 342 343 isB2B && (cookieName = 'B_' + cname) 344 345 return cookieName; 346 } 347 348 /** 349 * Function created to set the carouselName 350 * @param {*} elementReference name of the attribute that we want to reset 351 */ 352 this.getAnalyticsCarouselTitle = function (elementReference) { 353 let analyticsCarouselTitle = ''; 354 if (elementReference && elementReference instanceof Element) { 355 const isBIACrossSell = elementReference.closest('.slick-product-carousel')?.querySelectorAll('[data-carousel-behaviour="bia-crossSell"]')?.length; 356 const isBIASpaCarousel = elementReference.closest('.slick-product-carousel')?.querySelectorAll('[data-carousel-behaviour="bia-spaCarousel"]')?.length; 357 if (isBIACrossSell) { 358 analyticsCarouselTitle = elementReference.closest('cross-sell')?.querySelector('.cross-sell__header .cross-sell__title')?.textContent; 359 } else if (isBIASpaCarousel) { 360 analyticsCarouselTitle = elementReference.closest('.bia-spa-carousel-container')?.querySelector('.bia-spa-carousel-container-header')?.textContent?.trim(); 361 } else if (elementReference.closest('.master-product-carousel')) { 362 analyticsCarouselTitle = elementReference.closest('.master-product-carousel')?.querySelector('[data-carousel-title]')?.getAttribute('data-carousel-title'); 363 } else { 364 analyticsCarouselTitle = elementReference.closest('.slick-product-carousel, .slick-coupon-carousel')?.querySelector('lazy-wrapper')?.getAttribute('data-carousel-title') || 365 elementReference.closest('.slick-product-carousel, .slick-coupon-carousel')?.querySelector('.carousel-header-text, .carousel-nav__header-title')?.textContent?.trim() || elementReference.closest('.cms-coupon-grid')?.querySelector('.grid-title')?.textContent?.trim(); 366 } 367 } 368 return analyticsCarouselTitle; 369 } 370 371 // Retrieves a cookie based on name, returns its value as string 372 this.getCookie = function (cname) { 373 // resolve cookie name based on which site a user is on 374 if (window?.AB?.COMMON && window?.SWY?.CONFIGSERVICE) { 375 if (cname === 'SAFEWAY_KMSI' && !AB.COMMON.isB2BSite()) { 376 cvalue = SWY.CONFIGSERVICE.getKmsiValue('SAFEWAY_KMSI'); 377 if (cvalue === null) { 378 cvalue = SWY.CONFIGSERVICE.getKmsiValue('ACI_S_SAFEWAY_KMSI'); 379 } 380 return cvalue; 381 } else if (cname === 'B_SAFEWAY_KMSI' && AB.COMMON.isB2BSite()) { 382 cvalue = SWY.CONFIGSERVICE.getKmsiValue('B_SAFEWAY_KMSI'); 383 if (cvalue === null) { 384 cvalue = SWY.CONFIGSERVICE.getKmsiValue('ACI_S_B_SAFEWAY_KMSI'); 385 } 386 return cvalue; 387 } 388 } 389 390 const name = cname === 'absVisitorId' ? 'absVisitorId=' : this.getResolvedCookieName(cname) + '='; 391 const decodedCookie = decodeURIComponent(document.cookie); 392 const ca = decodedCookie.split(';'); 393 for (let i = 0; i < ca.length; i++) { 394 let c = ca[i]; 395 while (c.charAt(0) === ' ') { 396 c = c.substring(1); 397 } 398 if (c.indexOf(name) === 0) { 399 return c.substring(name.length, c.length); 400 } 401 } 402 return ''; 403 }; 404 405 this.deleteCookie = function (name, domain) { 406 let domainStr = domain ? 'domain=' + domain : ''; 407 if (Array.isArray(name)) { 408 name.forEach(cookieName => { 409 document.cookie = cookieName + '=; expires=Thu, 05-Mar-1992 00:00:01 GMT; path=/;' + domainStr; 410 }); 411 } else { 412 document.cookie = name + '=; expires=Thu, 05-Mar-1992 00:00:01 GMT; path=/;' + domainStr; 413 } 414 } 415 416 this.getUserChannel = function () { 417 const deliveryPreference = digitalData?.user?.deliveryPreference?.toLowerCase() || ''; 418 return deliveryPreference === 'dug' ? 'pickup' : deliveryPreference; 419 } 420 421 this.analyticsGetURLParameterByName = function (name, doDecode) { 422 let match = RegExp('[?&]' + name + '=([^&]*)', 'i').exec(window.location.search); 423 424 if (doDecode == false) { 425 return match ? match[1].replace(/\+/g, ' ') : null; 426 } else { 427 return match ? decodeURIComponent(match[1].replace(/\+/g, ' ')) : null; 428 } 429 }; 430 431 this.checkAuthorization = function () { 432 return (typeof AB?.userInfo?.userType != 'undefined' && (String(AB?.userInfo?.userType).toLowerCase() == null || 433 String(AB?.userInfo?.userType).toLowerCase() == '' || String(AB?.userInfo?.userType).toLowerCase() == 'g' 434 || String(AB?.userInfo?.userType).toLowerCase() == 'a')) ? false : true; 435 }; 436 437 this.sanitizeAnalyticsText = function (text) { 438 if (text && (typeof (text) === 'string' || text instanceof String)) { 439 return text.toLowerCase()?.replace(',', '-')?.replace('&', 'and')?.split(' ')?.join('-')?.replace(/[^\w-,|#:]/gi, '')?.replace('--', '-')?.split('-')?.filter(x => { return x })?.join('-'); 440 } else { 441 return ''; 442 } 443 } 444 445 this.cleanPrice = function (amt) { 446 return Math.round(amt * 100) / 100; 447 } 448 449 this.toStringAll = function (o) {
450 Object.keys(o).forEach(k => { 451 if (typeof o[k] === 'object') { 452 return this.toStringAll(o[k]); 453 } 454 if ((o[k] === 0) || o[k]) { 455 o[k] = o[k].toString(); 456 }; 457 }); 458 return o; 459 } 460 461 this.getLocalISOTime = function (time) { 462 const timezoneOffset = time.getTimezoneOffset(); 463 const convertedTimezone = Math.abs(timezoneOffset); 464 const isoFormatted = new Date(time.getTime() - timezoneOffset * 60 * 1000).toISOString(); 465 return `${isoFormatted.slice(0, -1)}${timezoneOffset > 0 ? '-' : '+'}${String(Math.floor(convertedTimezone / 60)).padStart(2, '0')}:${String(convertedTimezone % 60).padStart(2, '0')}` 466 } 467 468 this.getBannerEnvironment = function () { 469 let sub = location.hostname.split('.')[0]; 470 471 function bannerEnv(subDomain) { 472 let subDomainEnv = ''; 473 switch (true) { 474 case subDomain.includes('dev1'): 475 subDomainEnv = 'dev1'; 476 break; 477 case subDomain.includes('dev2'): 478 subDomainEnv = 'dev2'; 479 break; 480 case subDomain.includes('dev3'): 481 subDomainEnv = 'dev3'; 482 break; 483 case subDomain.includes('qa1'): 484 subDomainEnv = 'qa1'; 485 break; 486 case subDomain.includes('qa2'): 487 subDomainEnv = 'qa2'; 488 break; 489 case subDomain.includes('qa3'): 490 subDomainEnv = 'qa3'; 491 break; 492 case subDomain.includes('stage'): 493 subDomainEnv = 'stage'; 494 break; 495 case subDomain.includes('beta'): 496 subDomainEnv = 'beta'; 497 break; 498 default: 499 break; 500 } 501 return subDomainEnv; 502 } 503 504 return (/-/).test(sub) ? bannerEnv(sub.replace(/[^-]*-/, '')) : 'prod'; 505 } 506 507 this.getAPIInfo = function () { 508 let APIInfo = { 509 subscriptionKey: '', 510 serverEnv: '', 511 impressionsSubKey: '', 512 searchSubKey: '' 513 }; 514 switch (this.getBannerEnvironment()) { 515 case 'dev1': 516 case 'dev2': 517 case 'dev3': 518 APIInfo.subscriptionKey = '9d4e44621ea0402ab85b74707d88d726'; 519 APIInfo.serverEnv = 'devint'; 520 break; 521 case 'qa1': 522 APIInfo.subscriptionKey = '319576f956fa4a0d8de8588c95a83d43'; 523 APIInfo.serverEnv = 'qa1int'; 524 break; 525 case 'qa2': 526 APIInfo.subscriptionKey = 'ee45b60e76f846d7a7121895c6ce3e1d'; 527 APIInfo.serverEnv = 'qa2int'; 528 break; 529 case 'qa3': 530 APIInfo.subscriptionKey = '16aef1f2843741b097417cebcf4f8afc'; 531 APIInfo.serverEnv = 'perfint'; 532 break; 533 case 'stage': 534 APIInfo.subscriptionKey = '47223223fb8e450189a5bb98314617cc'; 535 APIInfo.serverEnv = 'stageint'; 536 break; 537 case 'prod': 538 APIInfo.subscriptionKey = '64713b635f004b0b81d6e4531b8c966d'; 539 APIInfo.serverEnv = 'int'; 540 break; 541 default: 542 break; 543 } 544 return APIInfo; 545 } 546 547 this.setDataInteger = function (value) { 548 return isNaN(parseFloat(value)) ? undefined : parseFloat(value); 549 } 550 551 this.setDataBoolean = function (value) { 552 if (typeof value === 'boolean') { 553 return value; 554 } else if (typeof value === 'string') { 555 let booleanStr = value.toLowerCase(); 556 booleanStr = booleanStr === 'true' ? true : booleanStr === 'false' ? false : undefined; 557 return booleanStr; 558 } else { 559 return undefined; 560 } 561 } 562 563 this.safeReducer = function (a, b) { 564 a = isNaN(parseFloat(a)) ? 0 : parseFloat(a); 565 b = isNaN(parseFloat(b)) ? 0 : parseFloat(b); 566 return a + b; 567 }; 568
569 this.getSearchImpressionProducts = function (impressions, isProductClick) { 570 return impressions.map(impression => { 571 const item = isProductClick ? impression : impression?.product; 572 return { 573 pId: item?.id, 574 bia: item?.bia, 575 oos: item?.oos, 576 sponsored: item?.sponsored, 577 mp: item?.mp, 578 display: item?.display, 579 placement: isProductClick ? impression?.placement : undefined, 580 pName: item?.name 581 } 582 }); 583 } 584 585 /** 586 * Function to return date time in below format 587 * 'Wednesday, April 17, 2024 1:00 PM' 588 * @param {*} dateTimeString example value for dateTimeString 2024-04-17T13:00:00 589 */ 590 this.getFullDateWithTime = function (dateTimeString) { 591 const curruntDate = new Date(dateTimeString); 592 const day = curruntDate.toLocaleDateString('en-US', { weekday: 'long' }); 593 const month = curruntDate.toLocaleDateString('en-US', { month: 'long' }); 594 const date = curruntDate.toLocaleDateString('en-US', { day: '2-digit' }); 595 const year = curruntDate.toLocaleDateString('en-US', { year: 'numeric' }); 596 const time = curruntDate.toLocaleString('en-US', { hour: '2-digit', minute: 'numeric', hour12: true }); 597 return `${day}, ${month} ${date}, ${year} ${time},`; 598 } 599 600 this.generateEventId = function () { 601 const currentDate = new Date(); 602 const currentTime = currentDate.getTime(); 603 const eventId = `${currentTime}${Math.floor(Math.random() * 10000000000)}${window.crypto && window.crypto.randomUUID ? window.crypto.randomUUID() : ''}`; 604 return eventId; 605 }; 606 607 this.generateCurrentTime = function () { 608 const currentDate = new Date(); 609 const eventTime = AB.DATALAYER.getLocalISOTime(currentDate); 610 return eventTime; 611 }; 612 613 this.getDeviceInfo = function () { 614 let deviceOS = '' 615 if (window?.navigator?.userAgent?.toLowerCase()?.includes('windows')) { 616 deviceOS = window?.navigator?.userAgent?.match(/\(.*?\)/g)?.map(x => x?.replace(/[()]/g, ''))[0]?.split(';')[0]?.trim()?.toLowerCase()?.replace(/\s/g, '-')?.replace(/undefined/gi, '-') || 'not-defined'; 617 } else { 618 deviceOS = window?.navigator?.userAgent?.match(/\(.*?\)/g)?.map(x => x?.replace(/[()]/g, ''))[0]?.split(';')[1]?.trim()?.toLowerCase()?.replace(/\s/g, '-')?.replace(/undefined/gi, '-') || 'not-defined'; 619 } 620 return deviceOS; 621 } 622 623 this.getBrowserType = function () { 624 function isMobile() { 625 const regex = /Mobi|Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i; 626 return (regex.test(window.navigator.userAgent)) && ('ontouchstart' in window || navigator.maxTouchPoints > 0); 627 } 628 629 return `${isMobile() ? 'mobile' : ''}web`; 630 } 631 632 this.throttleEvent = function (func, delay) { 633 // Capture the current time 634 let time = Date.now(); 635 636 return () => { 637 if ((time + delay - Date.now()) <= 0) { 638 func(); 639 time = Date.now(); 640 } 641 } 642 } 643 644 this.isEditOrderMode = function () { 645 let result = false;
646 // check for edit order cookies set by ERUMS 647 if (AB.DATALAYER.getCookie('SlotInfo') !== 'undefined' && AB.DATALAYER.getCookie('OrderStoreId') !== 'undefined' && AB.DATALAYER.getCookie('SlotInfo') !== '' && AB.DATALAYER.getCookie('OrderStoreId') !== '') { 648 result = true; 649 } 650 return result; 651 }; 652 653 // Function to get zone & slot information for v218 654 this.getZoneInfo = function (element) { 655 const sectionInfo = { 656 section: '0', 657 row: '0', 658 zone: '0' 659 }; 660 //find zone 661 if (element) { 662 let rowComponents = []; 663 const closestMarkerComponent = element?.closest('.marker-component'); 664 665 const zoneElement = element?.closest('.zone')?.querySelector('.zone-component'); 666 const zoneId = zoneElement?.dataset?.zoneType?.split('zone') && zoneElement?.dataset?.zoneType?.split('zone')?.length > 0 ? zoneElement?.dataset?.zoneType?.split('zone')[1] : '0'; 667 sectionInfo.zone = zoneId || '0'; 668 //find section Id 669 if (closestMarkerComponent) { 670 rowComponents = this.getRowComponents(closestMarkerComponent); 671 const sectionId = closestMarkerComponent.getAttribute('data-section-id'); 672 sectionInfo.section = sectionId ? `${sectionId}` : '0'; 673 } 674 //find row 675 if (rowComponents.length > 0) { 676 for (let i = 0; i < rowComponents.length; i++) { 677 if (rowComponents[i].contains(element) || rowComponents[i] === element) { 678 sectionInfo.row = i + 1; 679 break; 680 } 681 } 682 } 683 } 684 return sectionInfo; 685 } 686 687 // Helper function for zone function 688 this.getRowComponents = function (markerComponent) { 689 let rowComponents = []; 690 const rowComponentClassList = ['nextgen-banner-unit_HeroBanner', 'google-adManager--HeroBanner']; 691 const classList = [ 692 'hero-intro', 693 'standard-intro', 694 'flex-card', 695 'master-product-carousel', 696 'title-header', 697 'shopbycategory', 698 'coupon-carousel', 699 'meal-carousel', 700 'raw-html', 701 'nextgen-banner-unit-proxy', 702 'web-stories', 703 'product-carousel-v2', 704 'multi-columns-control', 705 'nextgen-banner-unit', 706 'slick-meal-carousel', 707 'banner-headline', 708 'slick-product-carousel', 709 'image.section', 710 'button.section', 711 'bundles-box', 712 'meals-bundles-carousel' 713 ]; 714 const containerClassList = [ 715 'flex-container', 716 'nextgen-abs-carousel-proxy', 717 'nextgen-banner-carousel', 718 'nextgen-abs-carousel-wrapper', 719 ]; 720 const classSelector = '.' + classList.join(',.'); 721 const containerClassSelector = '.' + containerClassList.join(',.'); 722 const rowComponentClassListSelector = '.' + rowComponentClassList.join(',.'); 723 const markerElements = markerComponent.querySelectorAll(classSelector); 724 for (let i = 0; i < markerElements.length; i++) { 725 //Check if component is empty or hidden 726 if (markerElements[i].offsetHeight <= 0 || markerElements[i].offsetWidth <= 0) { 727 //Skip the component as its empty or hidden 728 //Move to next component 729 continue; 730 } 731 //Check if element is already child of one of the row components 732 if (rowComponents.some(t => t.contains(markerElements[i]) || t === markerElements[i])) { 733 //Skip the component as its part of the container already or it is itself is row component 734 //Move to next component 735 continue; 736 } 737 //Check if the component is marked as row component 738 if (markerElements[i].querySelector(rowComponentClassListSelector) || rowComponentClassList.some(c => markerElements[i].classList.contains(c))) { 739 //Skip the row component if it is a cloned component 740 if (!markerElements[i]?.closest('.slick-cloned')) { 741 rowComponents.push(markerElements[i]); 742 } 743 continue; 744 } 745 //Check if the component is inside the ROW component by finding closest container is either of Container classes 746 const rowComponent = markerElements[i].closest(containerClassSelector); 747 if (rowComponent) { 748 rowComponents.push(rowComponent); 749 continue; 750 } 751 //if component is not in either of the container classes then it is ROW component itself as previous condition would have skipped it 752 rowComponents.push(markerElements[i]); 753 } 754 return rowComponents; 755 } 756 757}).call(AB.DATALAYER); 758 759'use strict'; 760// Serverside tracking setup - OneTag Platform 761(function () { 762 this.getBannerSubTotal = function (event, data) { 763 let subtotal = data?.cart?.transaction?.subTotal; 764 const marketPlaceCarts = data?.cart?.transaction?.subTotalMarketplaceCarts; 765 766 if (AB.DATALAYER.isOneTagCartEvent(event)) { 767 return AB.DATALAYER.setDataInteger(subtotal) || 0; 768 } else if (AB.DATALAYER.isOneTagCheckoutEvent(event)) { 769 if ((!marketPlaceCarts || marketPlaceCarts === 'undefined') && (!data?.cart?.transaction?.subTotalWineCart)) { 770 return AB.DATALAYER.setDataInteger(subtotal) 771 } else { 772 return 0 773 } 774 } else { 775 return undefined; 776 } 777 } 778 779 this.getMpSubTtl = function (event, data) { 780 let subTtlMpCarts = []; 781 const marketPlaceCarts = data?.cart?.transaction?.subTotalMarketplaceCarts; 782 783 if ((AB.DATALAYER.isOneTagCartEvent(event) || event === 'full_cart' || AB.DATALAYER.isOneTagCheckoutEvent(event)) && marketPlaceCarts && marketPlaceCarts !== 'undefined') { 784 let mpCartsArr = marketPlaceCarts.split('#'); 785 subTtlMpCarts = mpCartsArr.map(cart => { 786 let sellerCartArr = cart.split(':'); 787 let sellerCartId = sellerCartArr[0]; 788 let sellerCartSubTtl = sellerCartArr[1]; 789 return { 790 sellerId: sellerCartId, 791 subTotal: sellerCartSubTtl && AB.DATALAYER.setDataInteger(sellerCartSubTtl) 792 }; 793 }); 794 795 if (AB.DATALAYER.isOneTagCheckoutEvent(event)) { 796 let sellerId = data?.page?.sellerId; 797 subTtlMpCarts = subTtlMpCarts.filter(item => item.sellerId === sellerId); 798 } 799 return subTtlMpCarts.length > 0 ? subTtlMpCarts : undefined; 800 } else { 801 return undefined; 802 } 803 } 804 805 this.getWineSubTtl = function (wineCart) { 806 if (wineCart) { 807 return { 808 sellerId: wineCart.sellerId, 809 subTotal: wineCart.subTotal 810 } 811 } 812 else { 813 return undefined; 814 } 815 } 816 817 this.getAnalyticsCartItems = function (item) { 818 let tempCartItems = item && Array.isArray(item) ? item : []; 819 let mappedCartItems = []; 820 for (let i = 0; i < tempCartItems.length; i++) { 821 const itemId = tempCartItems[i] && tempCartItems[i].productID && tempCartItems[i].productID.toString(); 822 let item = { 823 prdID: itemId, 824 prdNam: tempCartItems[i].productName, 825 units: tempCartItems[i].units, 826 lp: tempCartItems[i].listPrice, 827 bp: tempCartItems[i].basePrice, 828 ppUnit: tempCartItems[i].pricePerUnit, 829 uTyp: tempCartItems[i].unitType, 830 totp: tempCartItems[i].totalPrice, 831 itemP: tempCartItems[i].itemPrice, 832 inv: tempCartItems[i].inventory, 833 cpnClpd: tempCartItems[i].couponClipped, 834 cpnApld: tempCartItems[i].couponApplied, 835 ccSav: tempCartItems[i].clubcardItemSavings, 836 j4uSav: tempCartItems[i].j4uItemSavings, 837 rSav: tempCartItems[i].rewardsItemSavings, 838 eSav: tempCartItems[i].employeeItemSavings, 839 tSav: tempCartItems[i].totalItemSavings, 840 oTyp: tempCartItems[i].offerType, 841 oText: tempCartItems[i].offerText, 842 isFound: tempCartItems[i]?.isFound && AB.DATALAYER.setDataBoolean(tempCartItems[i]?.isFound), 843 sId: tempCartItems[i]?.productSellerId, 844 cId: tempCartItems[i]?.productCartId?.toString() 845 }; 846 mappedCartItems.push(item); 847 } 848 return mappedCartItems; 849 } 850 851 this.getCookiePref = function () { 852 if (window.OptanonActiveGroups) { 853 return window.OptanonActiveGroups; 854 } else { 855 let cookie = AB.DATALAYER.getCookie('OptanonConsent'); 856 if (cookie) { 857 cookie = cookie.split(/[=&]/); 858 cookie = cookie.find(e =>
858 e.includes('C000')); 859 if (cookie) { 860 cookie = cookie.split(','); 861 cookie = cookie.filter(e => e.includes(':1')); 862 //assuming the [necessary only cookies] are always included 863 cookie = cookie.join(',').replaceAll(':1', ''); 864 return cookie; 865 } 866 //if this is reached then the cookie is malformed 867 else return 868 } 869 //we have no valid cookie or window.OptanonActiveGroups 870 else return 871 } 872 } 873 874 this.getSubEvent = function (event, data) { 875 if (event === 'linkClick' && data?.link?.linkName === 'flex-impressions') { 876 return 'flex-impressions'; 877 } 878 879 if (event === 'linkClick' && data?.link?.linkName === 'bundle-impressions') { 880 return 'bundle-impressions'; 881 } 882 883 if (event === 'linkClick' 884 && typeof data?.link?.linkName === 'string' 885 && /^(image|text|recipe)-impressions$/.test(data.link.linkName)) { 886 return data.link.linkName; 887 } 888 889 if (event === 'linkClick' && data?.link?.linkName === 'deals-impressions') { 890 return 'deals-impressions'; 891 } 892 893 if (AB.DATALAYER.isCRSClickEvent(event, data)) { 894 return 'crs:clicks'; 895 } 896 897 if (event === 'linkClick' && data?.link?.linkName === 'experiment-exposure-loaded') { 898 return 'experiment-exposure'; 899 } 900 901 if (event === 'linkClick' && data?.link?.linkName === 'flex-experiment-exposure-loaded') { 902 return 'experiment-exposure'; 903 } 904 905 if (event === 'linkClick' && typeof data?.link?.linkName === 'string' && data.link.linkName.startsWith('experiment-exposure-loaded-')) { 906 return 'experiment-exposure'; 907 } 908 909 switch (event) { 910 case 'experiment-exposure-loaded': 911 case 'experiment-exposure-loaded-splash-screen': 912 case 'experiment-exposure-loaded-deals':
913 case 'experiment-exposure-loaded-banner': 914 case 'gam-banner-exposure': 915 case 'experiment-exposure-loaded-bundle': 916 return 'experiment-exposure'; 917 case 'banner-impressions': 918 case 'banner-impressions-engagement-ladder': 919 case 'gam-banner-impressions': 920 return 'banner-impressions'; 921 case 'headline-impressions': 922 case 'headline-standard-impressions': 923 case 'headline-hero-impressions': 924 return 'headline-impressions'; 925 case 'reward_redeemed': 926 case 'reward_unclipped': 927 return 'linkClick'; 928 case 'target-experiment-exposure': 929 return 'target-experiment-exposure'; 930 default: 931 if (event === 'pageLoaded' && data?.page?.subSection1 === 'aisles') { 932 return 'category_page'; 933 } 934 if (event.includes('experiment-exposure')) { 935 return 'experiment-exposure'; 936 } 937 return event; 938 } 939 } 940 941 this.getSubscriptionKey = function () { 942 return AB.DATALAYER.getAPIInfo()?.subscriptionKey; 943 } 944 945 this.getSubEventEndpoint = function (event, data) { 946 switch (true) { 947 case AB.DATALAYER.isB2BPage(): 948 return '/b2b'; 949 case AB.DATALAYER.isCRSClickEvent(event, data): 950 return '/ces'; 951 case event === 'product-impressions' && data?.pageFilter?.mediaType === 'product-carousel-mktng-card': 952 return '/impression'; 953 case event === 'search_results_impressions' 954 || event === 'product-exposed' 955 || (event === 'product-impressions' && data.link?.linkName === `${digitalData?.pageDetails?.subSection1}-product-impressions`) 956 || (event === 'linkClick' && data.link?.linkName === `${digitalData?.pageDetails?.subSection1}-product-click`) 957 || event === 'search_results_page' 958 || event === 'zero_search_results': 959 return '/search'; 960 case AB.DATALAYER.isOneTagImpressionLink(event, data): 961 return '/impression'; 962 case event === 'batch_coupon_clipped': 963 return '/coupon'; 964 case AB.DATALAYER.isOneTagMiscEvent(event) || AB.DATALAYER.isOneTagMiscLinkClick(data): 965 return '/misc'; 966 case AB.DATALAYER.isOneTagGamOrAmcEvent(event, data): 967 return '/amc'; 968 case event === 'target-experiment-exposure': 969 return '/experimentation'; 970 default: 971 return ''; 972 } 973 } 974 975 this.updateOneTagMapping = function (event, data) { 976 let serverSideData = {}; 977 serverSideData.event = { 978 name: AB.DATALAYER.isPageLoadEvent(event) ? 'pageLoaded' : 'pageAction', 979 subEvent: AB.DATALAYER.getSubEvent(event, data), 980 id: data?.serverData?.eventId, 981 time: data?.serverData?.currentTime 982 }; 983 serverSideData.page = { 984 pgName: data?.page?.pageName, 985 channel: data?.page?.channel, 986 bnr: data?.page?.banner, 987 ss1: data?.page?.subSection1, 988 ss2: data?.page?.subSection2, 989 ss3: data?.page?.subSection3, 990 ss4: data?.page?.subSection4, 991 siteTP: data?.page?.siteTypePath, 992 pURL: data?.page?.pageUrl, 993 platform: data?.page?.platform, 994 rURL: data?.page?.referringURL, 995 tp: data?.page?.thirdParty, 996 sId: data?.page?.sellerId, 997 pt: data?.page?.pageType, 998 params: data?.page?.parameters?.toLowerCase(), 999 msgDetail: data?.page?.msgDetail || data?.page?.abTestDetails, 1000 bnrName: data?.page?.bnrName, 1001 dlsCpnIds: data?.page?.dlsCpnIds, 1002 expVariant: data?.page?.expVariant, 1003 deviceType: data?.page?.deviceType, 1004 prevPgname: data?.page?.previousPageName, 1005 icmpId: digitalData?.crossPageData?.analyticsQueryParamIcmpid?.data 1006 }; 1007 // When the sponsored-impressions fix is disabled (killswitch ON), retain the 1008 // legacy rTyp fallback derived from the pfm string (eVar14 = 4th '#' segment). 1009 // pfm layout: "<pfmPath>#<section/mediaType>#<search|non-search>
1009#<recType>#<placement>[#<badge>]" 1010 let rTypFromPfm; 1011 if (window?.SWY?.CONFIGSERVICE?.featureToggleConfig?.isSponsoredImpressionFixDisabled) { 1012 const carouselPfm = Array.isArray(data?.carousel) 1013 ? data.carousel.find(c => c?.pfm)?.pfm 1014 : undefined; 1015 const pfmStr = carouselPfm || data?.productInfo?.pfmDetail; 1016 const pfmParts = typeof pfmStr === 'string' ? pfmStr.split('#') : []; 1017 rTypFromPfm = pfmParts.length >= 5 ? pfmParts[3] : undefined; 1018 } 1019 1020 serverSideData.pageFilter = { 1021 filTyp: data?.pageFilter?.filterType, 1022 filSel: (event === 'linkClick' && data.link?.linkName === `${digitalData?.pageDetails?.subSection1}-product-click`) ? data?.pageFilter?.pdpID : data?.pageFilter?.filterSelection, 1023 sortSel: data?.pageFilter?.sortSelection, 1024 topNav: data?.pageFilter?.topNav, 1025 carousel: data?.pageFilter?.carousel, 1026 adaFlg: data?.pageFilter?.adaFlag, 1027 rTyp: data?.pageFilter?.recommendationType || rTypFromPfm, 1028 placement: data?.pageFilter?.placement, 1029 fpDisMon: data?.pageFilter?.freshPassDiscountedMonths?.toString(), 1030 premSlots: data?.pageFilter?.premiumSlots, 1031 promoCode: data?.pageFilter?.promoCode, 1032 mTyp: data?.pageFilter?.mediaType, 1033 subsFunnel: data?.pageFilter?.subscriptionFunnel, 1034 fpPlanPrice: data?.pageFilter?.freshPassPlanPrice ? Number(data.pageFilter.freshPassPlanPrice) : undefined, 1035 bnrLoc: data?.pageFilter?.bannerCardCarousel 1036 }; 1037 serverSideData.user = { 1038 shopZC: data?.user?.shopZipCode, 1039 ssId: data?.user?.shopStoreId, 1040 loginSts: data?.user?.loginStatus, 1041 ccn: data?.user?.clubCardNumber, 1042 hhId: data?.user?.houseHoldId, 1043 uType: data?.user?.userType, 1044 custId: data?.user?.customerId, 1045 subSts: data?.user?.subscriptionStatus, 1046 subDate: data?.user?.subscriptionDate, 1047 dataLC: data?.user?.dataLoadComplete, 1048 jrnSts: data?.user?.journeyStatus, 1049 addType: data?.user?.addressType, 1050 type: data?.user?.type, 1051 dlvPre: data?.user?.deliveryPreference, 1052 uuid: data?.user?.uuid, 1053 odrSts: data?.user?.orderStatus, 1054 ordCnt: data?.user?.orderCount?.toString() || data?.user?.orderStatus, 1055 custSts: data?.user?.customerStatus, 1056 dlvBnr: data?.user?.deliveryBanner, 1057 picLoc: data?.user?.pickupLocation, 1058 dlvComb: data?.user?.deliveryCombo, 1059 locTck: data?.user?.shopZipCode || data?.user?.locationTracking, // currently duplicate of shopZC, as per Raju; keeping the alternate mapping to locationTracking as well just in case 1060 absVisitorId: data?.user?.absVisitorId || AB.DATALAYER.getCookie('absVisitorId'), 1061 sessionId: data?.user?.sessionId, 1062 cookiePref: AB.DATALAYER.getCookiePref(), 1063 clientUA: window.navigator?.userAgent, 1064 osType: data?.user?.deviceInfo, 1065 adobeVisitorId: window['Visitor'] && window['Visitor'].getInstance('A7BF3BC75245ADF20A490D4D@AdobeOrg')?.getMarketingCloudVisitorID(), 1066 vendor: [ 1067 { 1068 name: 'fb', 1069 vars: [ 1070 { 1071 key: 'fbp', 1072 value: AB.DATALAYER.getCookie('_fbp') 1073 }, 1074 { 1075 key: 'fbc', 1076 value: '' 1077 } 1078 ] 1079 } 1080 ] 1081 }; 1082 1083 if (data?.link?.linkName === 'gam-banner-impressions' || data?.link?.linkName === 'gam-banner-exposure') { 1084 const gamData = { 1085 name: 'gamadData', 1086 vars: [ 1087 { 1088 key: 'adUnitPath', 1089 value: data?.user?.gamAdData?.adUnitPath || 'no-value' 1090 }, 1091 { 1092 key: 'lineItemId', 1093 value: data?.user?.gamAdData?.lineItemId || 'no-value' 1094 }, 1095 { 1096 key: 'creativeId', 1097 value: data?.user?.gamAdData?.creativeId || 'no-value' 1098 }, 1099 { 1100 key: 'creativeTemplateId', 1101 value: data?.user?.gamAdData?.creativeTemplateId || 'no-value' 1102 }, 1103 { 1104 key: 'responseStatus', 1105 value: data?.user?.gamAdData?.responseStatus || 'no-value' 1106 }, 1107 { 1108 key: 'uniqueID', 1109 value: data?.user?.gamAdData?.uniqueID || 'no-value' 1110 }, 1111 { 1112 key: 'responseTime', 1113 value: data?.user?.gamAdData?.responseTime || 'no-value' 1114 }, 1115 { 1116 key: 'targetingMap', 1117 value: data?.user?.gamAdData?.targetingMap || 'no-value' 1118 } 1119 ] 1120 };
1121 serverSideData.user.vendor = serverSideData.user.vendor || []; 1122 serverSideData.user.vendor.push(gamData); 1123 } 1124 serverSideData.global = { 1125 expIds: data?.serverData?.experimentIds, 1126 attrToken: data?.serverData?.attributionToken 1127 }; 1128 1129 serverSideData.productInfo = {}; 1130 1131 serverSideData.search = { 1132 asf: data?.search?.autoSearchFlag?.toString() || 'false', 1133 ist: data?.search?.internalSearchTerm, 1134 resCnt: data?.search?.searchResults?.toString(), 1135 type: data?.search?.searchType, 1136 tfc: data?.search?.typedSearchCount, 1137 tst: data?.search?.typedSearchTerm, 1138 filter: data?.search?.filter, 1139 orderBy: data?.search?.orderBy, 1140 offset: data?.search?.offset && AB.DATALAYER.setDataInteger(data?.search?.offset), 1141 completionAttrToken: data?.search?.completionAttrToken, 1142 selPosition: data?.search?.selectedPosition ? Number(data?.search?.selectedPosition) : undefined, 1143 apiSearchTerm: data?.search?.apiSearchTerm, 1144 pageCategories: event !== 'search_results_impressions' && data?.search?.pageCategories ? data?.search?.pageCategories : undefined, 1145 impressions: (event === 'search_results_impressions' || event === 'category_impressions' || (event === 'linkClick' && data.link?.linkName === `${digitalData?.pageDetails?.subSection1}-product-click`)) && data?.search?.impressions ? AB.DATALAYER.getSearchImpressionProducts(data?.search?.impressions, event === 'linkClick') : undefined 1146 }; 1147 serverSideData.cart = { 1148 cartInfo: { 1149 cId: data?.cart?.cartInfo?.currentCartId, 1150 sId: data?.productInfo?.sellerId, 1151 actvCarts: data?.cart?.cartInfo?.activeCarts, 1152 cType: data?.cart?.cartInfo?.cartType, 1153 isMFC: data?.cart?.cartInfo?.isMFC, 1154 mfcUnavCnt: data?.cart?.cartInfo?.mfcUnavailableCount, 1155 totUnits: data?.cart?.cartInfo?.totalUnits, 1156 uniqueSKUCnt: data?.cart?.cartInfo?.uniqueSKUCnt && AB.DATALAYER.setDataInteger(data?.cart?.cartInfo?.uniqueSKUCnt), 1157 }, 1158 transaction: { 1159 dlvTime: data?.cart?.transaction?.deliveryTime, 1160 dlvWin: data?.cart?.transaction?.deliveryWindow, 1161 dlvST: data?.cart?.transaction?.deliverySlotType, 1162 ordId: data?.cart?.transaction?.orderID, 1163 payMthd: data?.cart?.transaction?.paymentMethod, 1164 rev: data?.cart?.transaction?.revenue, 1165 shipFee: data?.cart?.transaction?.shippingFee?.toString(), 1166 fuelSur: data?.cart?.transaction?.fuelSurcharge, 1167 servcFee: data?.cart?.transaction?.serviceFee?.toString(), 1168 tRev: data?.cart?.transaction?.tippingRevenue, 1169 tMode: data?.cart?.transaction?.tippingMode, 1170 uniqueOrderFlag: data?.cart?.transaction?.uniqueOrderFlag && AB?.DATALAYER?.setDataInteger(data?.cart?.transaction?.uniqueOrderFlag), 1171 icmpId: data?.cart?.transaction?.icmpId, 1172 emailClipCpn: data?.cart?.transaction?.emailClipAllCoupon, 1173 promoExist: data?.cart?.transaction?.isPromoApplied, 1174 rewardsExist: data?.cart?.transaction?.isRewardsApplied, 1175 subTtl: AB?.DATALAYER?.getBannerSubTotal(event, data), 1176 subTtlMpCarts: AB?.DATALAYER?.getMpSubTtl(event, data), 1177 subTtlWineCart: data?.cart?.transaction?.subTotalWineCart && AB.DATALAYER.getWineSubTtl(data?.cart?.transaction?.subTotalWineCart), 1178 }, 1179 item: data?.cart?.item && AB.DATALAYER.getAnalyticsCartItems(data?.cart?.item), 1180 savings: { 1181 ccSav: data?.cart?.savings?.clubcardSavings, 1182 j4uSav: data?.cart?.savings?.j4uSavings, 1183 rwdSav: data?.cart?.savings?.rewardsSavings, 1184 empSav: data?.cart?.savings?.employeeSavings, 1185 pcSav: data?.cart?.savings?.promocodeSavings, 1186 catInvalidId: data?.cart?.savings?.categoryInvali
1186dId, 1187 catInvalidName: data?.cart?.savings?.categoryInvalidName, 1188 catInvalidNone: data?.cart?.savings?.categoryInvalidNone, 1189 ttlSav: data?.cart?.savings?.totalSavings ? data?.cart?.savings?.totalSavings.toString() : '0', 1190 1191 } 1192 }; 1193 1194 // Populate productInfo values if they exist 1195 if ((AB.DATALAYER.isPdp() 1196 && (event === 'product-exposed' 1197 || event === 'product-impressions' || event === 'custom-cake-exposed')) 1198 || (event !== 'search_results_impressions' 1199 && event !== 'product-exposed' 1200 && event !== 'product-impressions') || (event === 'linkClick' 1201 && data.link?.linkName === `${digitalData?.pageDetails?.subSection1}-product-click`)) { 1202 serverSideData.productInfo.pId = data?.productInfo?.productID; 1203 serverSideData.productInfo.pName = data?.productInfo?.productName + (data?.productInfo?.isCustomCake && event === 'custom-cake-exposed' ? '|event424=1' : ''); 1204 serverSideData.productInfo.fName = data?.productInfo?.fullName; 1205 serverSideData.productInfo.units = data?.productInfo?.units && AB.DATALAYER.setDataInteger(data?.productInfo?.units); 1206 serverSideData.productInfo.uTyp = data?.productInfo?.unitType; 1207 serverSideData.productInfo.ppunit = data?.productInfo?.pricePerUnit; 1208 serverSideData.productInfo.lp = data?.productInfo?.listPrice; 1209 serverSideData.productInfo.carouselSec = data?.productInfo?.carouselSection; 1210 serverSideData.productInfo.bp = data?.productInfo?.basePrice?.toString(); 1211 serverSideData.productInfo.pType = data?.productInfo?.pricingType; 1212 serverSideData.productInfo.cpnFlg = data?.productInfo?.couponFlag; 1213 serverSideData.productInfo.cpnClpd = data?.productInfo?.couponClipped; 1214 serverSideData.productInfo.cpnId = data?.productInfo?.couponID; 1215 serverSideData.productInfo.aId = data?.productInfo?.aisleId ? data.productInfo.aisleId : ''; 1216 serverSideData.productInfo.upc = data?.productInfo?.upc; 1217 serverSideData.productInfo.pfmDtl = data?.productInfo?.pfmDetail; 1218 serverSideData.productInfo.ics = data?.productInfo?.isCrossSell; 1219 serverSideData.productInfo.mto = data?.productInfo?.mto; 1220 serverSideData.productInfo.deptNam = data?.productInfo?.departmentName; 1221 serverSideData.productInfo.lbs = data?.productInfo?.lbs?.toString(); 1222 serverSideData.productInfo.each = data?.productInfo?.each; 1223 serverSideData.productInfo.aisle = data?.productInfo?.aisleName; 1224 serverSideData.productInfo.shelf = data?.productInfo?.shelfName; 1225 serverSideData.productInfo.offTyp = data?.productInfo?.offerType; 1226 serverSideData.productInfo.offText = data?.productInfo?.offerText; 1227 serverSideData.productInfo.carouselSz = data?.productInfo?.carouselSize && AB.DATALAYER.setDataInteger(data?.productInfo?.carouselSize); 1228 serverSideData.productInfo.isRcmnd = data?.productInfo?.isRecommended; 1229 serverSideData.productInfo.iQn = data.link?.linkName === `${digitalData?.pageDetails?.subSection1}-product-click` ? undefined : data?.productInfo?.units ? data?.productInfo.units.toString() : '0'; 1230 serverSideData.productInfo.iv = data?.productInfo?.listPrice?.toString(); 1231 serverSideData.productInfo.sellerId = data?.productInfo?.sellerId; 1232 serverSideData.productInfo.sellerNam = data?.productInfo?.sellerName; 1233 serverSideData.productInfo.isMHMD = data?.productInfo?.isProductTrackingDisabled; 1234 serverSideData.productInfo.bia = data?.productInfo?.bia; 1235 serverSideData.productInfo.oos = data?.productInfo?.oos; 1236 serverSideData.productInfo.sponsored = data?.productInfo?.sponsored; 1237 serverSideData.productInfo.mp = data?.productInfo?.mp; 1238 serverSideData.productInfo.display = data?.productInfo?.display; 1239 serverSideData.productInfo.placement = data?.productInfo?.placement; 1240 serverSideData.productInfo.isCustomCake = data?.productInfo?.isCustomCake ? "Yes" : "No"; 1241 } 1242 1243 if ((event === 'transaction_complete' || event === 'delivery_subscription_confirmation') && AB.DATALAYER.getCookie('cje')) {
1244 serverSideData.user.vendor.push({ 1245 name: 'cj', 1246 vars: [{ 1247 key: 'cje', 1248 value: AB.DATALAYER.getCookie('cje') 1249 } 1250 ] 1251 }) 1252 } 1253 1254 if (AB.DATALAYER.isPageLoadEvent() || AB.DATALAYER.isOneTagCheckoutEvent()) { 1255 const gclid = AB?.DATALAYER?.analyticsGetURLParameterByName && AB.DATALAYER.analyticsGetURLParameterByName('gclid'); 1256 const isCmpidFromGclid = ['search_', 'kw_', 'ps_', 'PS_', 'co_kw'].some((value) => serverSideData?.page?.params?.startsWith(value)); 1257 if (gclid || isCmpidFromGclid) { 1258 serverSideData.user.vendor.push({ 1259 name: 'google', 1260 vars: [{ 1261 key: 'gclid', 1262 value: gclid || 'no-value' 1263 } 1264 ] 1265 }) 1266 } 1267 } 1268 1269 if (AB.DATALAYER.isProductActionEvent(event)) { 1270 serverSideData.cart.updateTime = data?.serverData?.currentTime; 1271 } 1272 1273 if (data?.batchCouponsClipped && Array.isArray(data?.batchCouponsClipped) && (event === 'batch_coupon_clipped')) { 1274 serverSideData.batchCpnsClp = data?.batchCouponsClipped.map(coupon => ({ 1275 type: coupon.couponType, 1276 name: coupon.couponName, 1277 id: coupon.couponId, 1278 clipMthd: coupon.couponclipmethod, 1279 clipCnt: coupon.numberofcouponsclipped, 1280 savings: coupon.couponSavings || '0', 1281 sku: coupon.productSKU?.toString(), 1282 carouselSec: coupon.carouselSection 1283 })); 1284 1285 } 1286 serverSideData.rewards = { 1287 id: data?.rewards?.rewardsID, 1288 redeemed: data?.rewards?.rewardsRedeemed 1289 }; 1290 1291 if (event === 'target-experiment-exposure') { 1292 serverSideData.experiment = { ...data?.experiment }; 1293 serverSideData.xerargs = { ...data?.xerargs }; 1294 serverSideData.misc = { ...data?.misc }; 1295 serverSideData.compMetaData = { ...data?.compMetaData }; 1296 serverSideData.page = { ...serverSideData.page, 1297 mktChannel: data?.page.mktChannel, 1298 slot: data?.page?.slot, 1299 subSlot: data?.page?.subSlot, 1300 zone: data?.page?.zone, 1301 deviceType:data?.page?.deviceType 1302 }; 1303 } 1304 1305 // Handles linkClick setup 1306 serverSideData.link = { 1307 name: AB.DATALAYER.shouldSendOneTagLinkClick(event) ? event : data?.link?.linkName, 1308 c14: data?.link?.c14 1309 }; 1310 if (!window?.SWY?.CONFIGSERVICE?.featureToggleConfig?.isSponsoredImpressionFixDisabled && Array.isArray(data?.carousel)) { 1311 serverSideData.carousel = data.carousel.map(item => { 1312 const mapped = { ...item }; 1313 // Ensure `detail` is set. Prefer explicit detail, then the raw 1314 // impression detail field used by some flows (adobeMetrics.metricA). 1315 if (!mapped.detail) { 1316 mapped.detail = item?.impressionAdditionalDetail 1317 || item?.adobeMetrics?.metricA 1318 || undefined; 1319 } 1320 // Remove the raw intermediate field to keep payload clean 1321 delete mapped.impressionAdditionalDetail; 1322 return mapped; 1323 }); 1324 } else { 1325 serverSideData.carousel = data?.carousel; 1326 } 1327 serverSideData.component = data?.component; 1328 serverSideData.absPlacement = data?.absPlacement; 1329 serverSideData.crs = data?.crs; 1330 1331 if (AB.DATALAYER.isCRSClickEvent(event, data)) { 1332 delete serverSideData.cart; 1333 delete serverSideData.global; 1334 delete serverSideData.productInfo; 1335 delete serverSideData.search; 1336 1337 if (data?.product) { 1338 serverSideData.product = data.product; 1339 } 1340 if (Array.isArray(data?.batchCouponsClipped)) { 1341 serverSideData.batchCouponsClipped = data.batchCouponsClipped; 1342 } 1343 } 1344 1345 // Story-aligned banner impression payload should exclude unrelated sections. 1346 if (serverSideData?.event?.subEvent === 'banner-impressions') { 1347 delete serverSideData.cart; 1348 delete serverSideData.global; 1349 delete serverSideData.productInfo; 1350 delete serverSideData.search; 1351 } 1352 1353 // Story-aligned flex impression payload should exclude unrelated sections. 1354 if (serverSideData?.event?.subEvent === 'flex-impressions') { 1355 delete serverSideData.cart; 1356 delete serverSideData.global; 1357 delete serverSideData.productInfo; 1358 delete serverSideData.search; 1359 1360 serverSideData.page = { 1361 ...serverSideData.page, 1362 rURL: serverSideData?.page?.rURL || data?.page?.referringURL || AB.DATALAYER.getReferringUrl(), 1363 deviceType: serverSideData?.page?.deviceType || data?.page?.deviceType || digitalData?.pageDetails?.deviceType 1364 }; 1365 1366 if (serverSideData.page) { 1367 delete serverSideData.page.prevPgname; 1368 } 1369 } 1370 1371 // Story-aligned flex experiment exposure payload should exclude unrelated sections. 1372 if (serverSideData?.event?.subEvent === 'experiment-exposure' && serverSideData?.link?.name === 'flex-experiment-exposure-loaded') { 1373 delete serverSideData.cart; 1374 delete serverSideData.global; 1375 delete serverSideData.productInfo; 1376 delete serverSideData.search; 1377 1378 serverSideData.page = { 1379 ...serverSideData.page, 1380 abTestDetails: serverSideData?.page?.msgDetail || data?.page?.abTestDetails || data?.component?.[0]?.detail, 1381 rURL: serverSideData?.page?.rURL || data?.page?.referringURL || AB.DATALAYER.getReferringUrl(), 1382 deviceType: serverSideData?.page?.deviceType || data?.page?.deviceType || digitalData?.pageDetails?.deviceType 1383 }; 1384 1385 if (serverSideData.page) { 1386 delete serverSideData.page.prevPgname; 1387 } 1388 } 1389 1390 const isCRSEvent = AB.DATALAYER.isCRSClickEvent(event, data); 1391
1392 Object.keys(serverSideData).forEach(key => { 1393 if (typeof serverSideData[key] == 'object' && serverSideData[key]) { 1394 Object.keys(serverSideData[key]).forEach(subKey => { 1395 if (typeof serverSideData[key][subKey] == 'object' && serverSideData[key][subKey]) { 1396 Object.keys(serverSideData[key][subKey]).forEach(subChildKey => { 1397 if (serverSideData[key][subKey][subChildKey] === '' || serverSideData[key][subKey][subChildKey] === undefined || serverSideData[key][subKey][subChildKey] === 'no-value' || serverSideData[key][subKey][subChildKey] === 'undefined' || serverSideData[key][subKey][subChildKey] === null) { 1398 delete serverSideData[key][subKey][subChildKey]; 1399 } 1400 }); 1401 } 1402 if (serverSideData[key][subKey] === undefined || serverSideData[key][subKey] === 'no-value' || serverSideData[key][subKey] === 'undefined' || serverSideData[key][subKey] === null) { 1403 delete serverSideData[key][subKey]; 1404 } else if (serverSideData[key][subKey] === '' && !(isCRSEvent && key === 'pageFilter' && subKey === 'mTyp')) { 1405 delete serverSideData[key][subKey]; 1406 } 1407 }); 1408 } 1409 }); 1410 1411 Object.keys(serverSideData).forEach(key => { 1412 if (typeof serverSideData[key] == 'object' && serverSideData[key]) { 1413 if (Object.entries(serverSideData[key]).length <= 0) { 1414 delete serverSideData[key] 1415 } else if (Object.entries(serverSideData[key]).length > 0) { 1416 Object.keys(serverSideData[key]).forEach(subKey => { 1417 if (typeof serverSideData[key][subKey] == 'object' && serverSideData[key][subKey] && Object.entries(serverSideData[key][subKey]).length <= 0) { 1418 delete serverSideData[key][subKey] 1419 } else if (serverSideData[key][subKey] && typeof serverSideData[key][subKey] == 'object' && Object.entries(serverSideData[key][subKey]).length > 0) { 1420 Object.keys(serverSideData[key][subKey]).forEach(subChildKey => { 1421 if (serverSideData[key][subKey][subChildKey] && typeof serverSideData[key][subKey][subChildKey] == 'object' && Object.entries(serverSideData[key][subKey][subChildKey]).length <= 0) { 1422 delete serverSideData[key][subKey][subChildKey]; 1423 } 1424 }); 1425 } 1426 if (typeof serverSideData[key][subKey] == 'object' && serverSideData[key][subKey] && Object.entries(serverSideData[key][subKey]).length <= 0) { 1427 delete serverSideData[key][subKey] 1428 } 1429 }); 1430 if (Object.entries(serverSideData[key]).length <= 0) { 1431 delete serverSideData[key] 1432 } 1433 } 1434 1435 } 1436 }); 1437 1438 return serverSideData; 1439 }; 1440 1441 this.sendDataToOneTag = function (eventName, data) { 1442 const absVisitorId = data?.user?.absVisitorId || ''; 1443 const urlFormat = `${AB.DATALAYER.isB2BPage() ? 'business' : 'www'}${AB.DATALAYER.getBannerEnvironment() !== 'prod' ? `-${AB.DATALAYER.getBannerEnvironment()}` : ''}`; 1444 const subEventEndpoint = AB.DATALAYER.getSubEventEndpoint(eventName, data); 1445 const subscriptionKey = AB.DATALAYER.getSubscriptionKey(eventName, data); 1446 const URL = `https://${urlFormat}.${AB.DATALAYER.getBanner(true)}.com/abs/${AB.DATALAYER.getAPIInfo()?.serverEnv}/ocat${subEventEndpoint}/serversidetag/event/`; 1447 const serverSideData = AB.DATALAYER.updateOneTagMapping(eventName, data); 1448 let resendAttempted = false; 1449 1450 const fetchPromise = fetch && fetch(URL, { 1451 method: 'POST', 1452 headers: { 1453 'Content-Type': 'text/plain', 1454 absVisitorId: `${absVisitorId}`, 1455 'Ocp-Apim-Subscription-Key': subscriptionKey, 1456 cookiePref: `${AB.DATALAYER.getCookiePref()}`, 1457 }, 1458 body: JSON.stringify(serverSideData), 1459 }); 1460 1461 // If returnPromise is true, return the promise with error handling 1462 if (data?.experimentationReturnPromise && eventName === 'target-experiment-exposure') { 1463 return fetchPromise 1464 .then(result => { 1465 if (result?.status !== 200) { 1466 throw new Error(`HTTP ${result.status}`); 1467 } 1468 return result; 1469 }) 1470 .catch(error => { 1471 console.error(`[DATALAYER] ${eventName} tracking failed:`, error); 1472 throw error; 1473 }); 1474 } 1475 1476 // Otherwise, fire and forget with retry logic for transaction_complete 1477 fetchPromise.catch(() => { 1478 if (!resendAttempted && eventName === 'transaction_complete') { 1479 setTimeout(() => { 1480 fetch(URL, { 1481 method: 'POST', 1482 headers: { 1483 'Content-Type': 'text/plain', 1484 absVisitorId: `${absVisitorId}`, 1485 'Ocp-Apim-Subscription-Key': subscriptionKey, 1486 cookiePref: `${AB.DATALAYER.getCookiePref()}`, 1487 }, 1488 body: JSON.stringify(serverSideData), 1489 }) 1490 resendAttempted = true; 1491 }, 5000); 1492 } 1493 }); 1494 } 1495 1496 this.sendExperimentDataToOneTag = function (experimentData) { 1497 const hasValidHouseholdId = digitalData?.user?.houseHoldId && digitalData.user.houseHoldId !== 'ID not found'; 1498 const isGuestUser = digitalData?.user?.type === 'A' || digitalData?.user?.type === 'G'; 1499 const isDataReady = isGuestUser 1500 ? !!(digitalData?.user?.shopStoreId && digitalData?.user?.shopZipCode) 1501 : !!hasValidHouseholdId; 1502 const waitForDigitalData = () => { 1503 return new Promise((resolve) => { 1504 if (isDataReady) { 1505 resolve(); 1506 } else { 1507 const handler = () => resolve(); 1508 window.addEventListener('digitalDataReady', handler, { once: true }); 1509 } 1510 }); 1511 }; 1512 1513 const sendExperimentPayload = () => { 1514 const { pageDetails, user, pageFilter } = digitalData || {}; 1515 1516 const experimentPayload = { 1517 compMetaData: { 1518 type: experimentData?.compMetaData?.type, 1519 name: experimentData?.compMetaData?.name 1520 }, 1521 experiment: { 1522 id: experimentData?.experiment?.id, 1523 group: experimentData?.experiment?.group, 1524 eligible: experimentData?.experiment?.eligible.toString(), 1525 featureId: experimentData?.experiment?.featureId 1526 }, 1527 misc: { ...experimentData?.misc }, 1528 page: { 1529 ...pageDetails, 1530 mktChannel: experimentData?.mktChannel, 1531 slot: experimentData?.slot, 1532 subSlot: experimentData?.subSlot, 1533 zone: experimentData?.zone 1534 }, 1535 pageFilter: { ...pageFilter }, 1536 user: { ...user }, 1537 experimentationReturnPromise: true 1538 }; 1539 1540 return this.pushEvent('target-experiment-exposure', experimentPayload) 1541 .then(result => result.json()) 1542 .then(body => { 1543 if (!body || Object.keys(body).length === 0 || body?.response === false) { 1544 throw new Error('API returned failure status'); 1545 } 1546 1547 if (body.response && typeof body.response === 'string' && body.response.toLowerCase() !== 'success') { 1548 throw new Error(`API returned non-success response: ${body.response}`); 1549 } 1550 1551 return body; 1552 }); 1553 }; 1554 1555 if (isDataReady) { 1556 return sendExperimentPayload(); 1557 1558 } else { 1559 return waitForDigitalData().then(() => sendExperimentPayload()); 1560 } 1561 }; 1562 1563}).call(AB.DATALAYER); 1564 1565'use strict'; 1566 1567// Comments in this file are of pure consistency reasons and offer no description externally 1568(function () { 1569 /** 1570 * Checks if current page is the homepage 1571 */ 1572 this.isHomePage = function () { 1573 if (digitalData?.pageDetails) { 1574 return digitalData.pageDetails.subSection1 === 'home' 1575 && digitalData.pageDetails.subSection2 === '' 1576 && digitalData.pageDetails.subSection3 === '' 1577 && digitalData.pageDetails.subSection4 === ''; 1578 } else { 1579 return false;
1580 } 1581 } 1582 1583 this.isShopPage = function () { 1584 return window.location.pathname.search('shop') !== -1; 1585 } 1586 1587 /** 1588 * Checks if the current page is a landing page 1589 */ 1590 this.isLpPage = function () { 1591 if (digitalData?.pageDetails) { 1592 return window.digitalData.pageDetails.pageUrl.indexOf('/lp') !== -1 || window.digitalData.pageDetails.pageUrl.indexOf('/subscribe') !== -1; 1593 } else { 1594 return false; 1595 } 1596 } 1597 1598 /** 1599 * Checks if the current page is Subscription Delivery landing page 1600 */ 1601 this.isSubscriptionDeliveryPage = function () { 1602 if (digitalData?.pageDetails) { 1603 return ((window.digitalData.pageDetails.subSection1 === 'lp' || window.digitalData.pageDetails.subSection1 === 'freshpass') && (window.digitalData.pageDetails.subSection2 === 'subscriptions-delivery' || window.digitalData.pageDetails.subSection3 === 'subscribe')); 1604 } else { 1605 return false; 1606 } 1607 } 1608 1609 /** 1610 * Checks if the current page is a L4 aisle page (e.g. https://www-qa2.vons.com/shop/aisles/beverages/coffee/coffee-ground.2053.html) 1611 */ 1612 this.isAislesL4Page = function () { 1613 if (digitalData?.pageDetails) { 1614 return !!(window.digitalData.pageDetails.subSection1 === 'aisles' 1615 && window.digitalData.pageDetails.subSection2 1616 && window.digitalData.pageDetails.subSection3 1617 && window.digitalData.pageDetails.subSection4); 1618 } else { 1619 return false; 1620 } 1621 } 1622 1623 /** 1624 * Checks if the current page is a L3 aisle page (e.g. https://shop-qa2.tomthumb.com/aisles/beverages/energy-drinks-shots.2053.html) 1625 */ 1626 this.isAislesL3Page = function () { 1627 if (digitalData?.pageDetails) { 1628 return window.digitalData.pageDetails.subSection1 === 'aisles' 1629 && window.digitalData.pageDetails.subSection2 1630 && window.digitalData.pageDetails.subSection3 1631 && !window.digitalData.pageDetails.subSection4; 1632 } else { 1633 return false; 1634 } 1635 } 1636 1637 /** 1638 * Checks if the current page is a L2 aisle page (e.g. https://shop-qa2.tomthumb.com/aisles/beverages.2574.html) 1639 */ 1640 this.isAislesL2Page = function () { 1641 if (digitalData?.pageDetails) { 1642 return window.digitalData.pageDetails.subSection1 === 'aisles' 1643 && window.digitalData.pageDetails.subSection2 1644 && !window.digitalData.pageDetails.subSection3 1645 && !window.digitalData.pageDetails.subSection4; 1646 } else { 1647 return false; 1648 } 1649 } 1650 1651 /** 1652 * Checks if the current page is a L2, L3 or L4 aisle page 1653 */ 1654 this.isAislesPage = function () { 1655 return this.isAislesL2Page() || this.isAislesL3Page() || this.isAislesL4Page(); 1656 } 1657 1658 /** 1659 * Checks if the current page is the j4u page 1660 */ 1661 this.isJ4UPage = function () { 1662 if (digitalData?.pageDetails) { 1663 return window.digitalData.pageDetails.subSection1 === 'justforu' && 1664 window.digitalData.pageDetails.subSection2 && 1665 !window.digitalData.pageDetails.subSection3 && 1666 !window.digitalData.pageDetails.subSection4; 1667 } else { 1668 return false; 1669 } 1670 } 1671 1672 /** 1673 * Checks if the current page is a re-sign-in page 1674 */ 1675 this.isReSignInPage = function () { 1676 if (digitalData?.pageDetails) { 1677 return (window.digitalData.pageDetails.subSection1 === 'account' || window.digitalData.pageDetails.subSection1 === 'welcome') && window.digitalData.pageDetails.subSection2 === 're-sign-in'; 1678 } else { 1679 return false; 1680 } 1681 } 1682 1683 this.isAuthSignInPage = function () { 1684 return window.location.pathname.indexOf('/account/auth/service/sign-in') != -1; 1685 } 1686 1687 /** 1688 * Checks if the current page is displaying search results 1689 */ 1690 this.isSearchResultsPage = function () { 1691 return digitalData?.pageDetails?.subSection1 === 'search-results' || digitalData?.pageDetails?.subSection1 === 'zero-search-results' || (this.isWineShopPage() && digitalData?.pageDetails?.subSection1 === 'search-results') || (window.location.pathname.startsWith('/foru/coupons-deals') && window.location.search.includes('?q=')); 1692 } 1693 1694 /** 1695 * Checks if the current page is a product detail page 1696 */ 1697 this.isPdp = function () { 1698 if (digitalData?.pageDetails) { 1699 return window.digitalData.pageDetails.subSection1 === 'product-details' || window.digitalData.pageDetails.subSection1 === 'pd' || 1700 (window.digitalData.pageDetails.subSection1 === 'product' && window.digitalData.pageDetails.subSection2 === 'details') || 1701 (window.digitalData.pageDetails.subSection1 === 'vineandcellar' && window.digitalData.pageDetails.subSection2 === 'product-details') || 1702 (window.digitalData.pageDetails.subSection1 === 'marketplace' && window.digitalData.pageDetails.subSection2 === 'product-details') 1703 } else { 1704 return false;
1705 } 1706 } 1707 1708 this.isNewPdp = function () { 1709 return window.location.pathname.startsWith('/product/details'); 1710 } 1711 1712 /** 1713 * Checks if the current page is part of the Auto Replenishment flow 1714 */ 1715 this.isARPage = function () { 1716 return window.location.pathname.startsWith('/schedule-and-save'); 1717 } 1718 1719 /** 1720 * Checks if the current page is part of the Manage Schedule & Save SPA flow 1721 */ 1722 this.isARManagePage = function () { 1723 return window.location.pathname.startsWith('/schedule-and-save/manage'); 1724 } 1725 1726 /** 1727 * Checks if the current page is part of the Manage Schedule & Save SPA flow 1728 */ 1729 this.isARTestManagePage = function () { 1730 return window.location.pathname.startsWith('/hey-sirius/manage'); 1731 } 1732 1733 /** 1734 * Checks if the current page is part of the Freshpass SPA flow 1735 */ 1736 this.isFreshpassManagePage = function () { 1737 return window.location.pathname.startsWith('/freshpass/manage'); 1738 } 1739 1740 this.isFreshpassConfirmationPage = function () { 1741 return window.location.pathname.startsWith('/delivery-subscription/dsconfirmation') || window.location.pathname.startsWith('/freshpass/manage/confirmation'); 1742 } 1743 1744 /** 1745 * Checks if current page is part of the Drive Up and Go notification flow for pickup 1746 */ 1747 this.isDUGNotification = function () { 1748 if (digitalData?.pageDetails) { 1749 return window.digitalData.pageDetails.subSection1 === 'mobile-pickup'; 1750 } else { 1751 return false; 1752 } 1753 } 1754 1755 /** 1756 * Checks if current page is the Deals page 1757 */ 1758 this.isDealsPage = function () { 1759 return window.digitalData?.pageDetails?.subSection1 === 'deals'; 1760 } 1761 1762 this.isCouponsAndDealsPage = function () { 1763 return window.location.pathname.indexOf('coupons-deals') !== -1; 1764 } 1765 1766 this.isB4USPA = function () { 1767 return window.location.pathname.indexOf('loyalty/coupons-deals') !== -1 1768 || window.location.pathname.indexOf('loyalty/deals') !== -1 1769 || window.location.pathname.indexOf('loyalty/mylist') !== -1 1770 || window.location.pathname.indexOf('loyalty/guest') !== -1 1771 || window.location.pathname.indexOf('loyalty/foru-guest') !== -1 1772 || window.location.pathname.indexOf('loyalty/offer-details') !== -1; 1773 } 1774 1775 /** 1776 * Checks if the current page belongs to the eRums project 1777 * It is used to exclude the AEM init functions from running on these pages 1778 */ 1779 this.isErumsPage = function () { 1780 return window.location.pathname.startsWith('/erums'); 1781 }; 1782 1783 /** 1784 * Checks if current page is Don't forget to add page 1785 */ 1786 this.isDFTAPage = function () { 1787 return window.location.pathname.startsWith('/erums/cart/dnf'); 1788 } 1789 1790 1791 /** 1792 * Checks if the current page belongs to the IAUC UCA React project 1793 */ 1794 this.isAccountPage = function () { 1795 return window.location.pathname.startsWith('/customer-account'); 1796 } 1797 1798 /** 1799 * Checks if the current page is the Payment Methods page 1800 */ 1801 this.isPaymentPage = function () { 1802 return window.location.pathname.startsWith('/customer-account/payment'); 1803 } 1804 1805 /** 1806 * Checks if the current page is the Unsubscribe page 1807 */ 1808 this.isUnsubscribePage = function () { 1809 return window.location.pathname.startsWith('/customer-account/unsubscribe'); 1810 } 1811 1812 this.isResetPasswordPage = function () { 1813 return window.digitalData?.pageDetails?.subSection2 === 'reset-password'; 1814 } 1815 1816 /** 1817 * Check if the current page is forgot password/reset password page 1818 */ 1819 this.isAccountResetPage = function () { 1820 return window.location.pathname.startsWith('/account-reset'); 1821 } 1822 1823 /** 1824 * Checks if the current page is the Students page 1825 */ 1826 this.isStudentsPage = function () { 1827 return window.location.pathname.startsWith('/customer-account/students'); 1828 } 1829 1830 /** 1831 * Checks if the current page belongs to the SVSS Orders React project 1832 */ 1833 this.isOrdersPage = function () { 1834 return window.location.pathname.startsWith('/order-account'); 1835 } 1836 1837 /** 1838 * Checks if the current page is part of the Recipes section of the website 1839 */ 1840 this.isRecipesPage = function () { 1841 if (digitalData?.pageDetails) { 1842 return window.digitalData.pageDetails.subSection1 === 'recipes'; 1843 } 1844 } 1845 1846 /** 1847 * Checks if the current page is OHC page 1848 */ 1849 this.isHelpCenterPage = function () { 1850 return window.location.pathname.startsWith('/help'); 1851 } 1852 1853 /** 1854 * Checks if the current page is Customer Care page 1855 */
1856 this.isCustomerCarePage = function () { 1857 return window.location.pathname.startsWith('/customer-care'); 1858 } 1859 1860 /** 1861 * Checks if the current page is Spotlight page 1862 */ 1863 this.isSpotlightPage = function () { 1864 return document?.title?.toLowerCase() === 'spotlight'; 1865 } 1866 1867 /** 1868 * Checks if the current page is the new design for the BIA page 1869 */ 1870 this.isBIAPage = function () { 1871 return window.location.pathname.startsWith('/shop/purchases/buy-it-again'); 1872 } 1873 1874 /** 1875 * Checks if the current page is a games page 1876 */ 1877 this.isGamesPage = function () { 1878 return window.location.pathname.startsWith('/foru/games') || window.location.pathname.startsWith('/flavoradventure/start'); 1879 } 1880 1881 this.isGamesSPAPage = function () { 1882 return window.location.pathname.startsWith('/games'); 1883 } 1884 1885 this.setMarketplacePage = function () { 1886 if (window.digitalData && window.digitalData.pageDetails) { 1887 window.isAnalyticsMarketplacePage = digitalData.pageDetails.subSection1 === 'marketplace' || digitalData.pageDetails.subSection1 === 'm' || (window.location.pathname.startsWith('/shop/m') && !window.location.pathname.startsWith('/shop/mf')) || window.location.pathname.startsWith('/shop/marketplace'); 1888 } 1889 } 1890 1891 this.isMarketplaceCategoryPage = function () { 1892 return window.isAnalyticsMarketplacePage && window.location.pathname.indexOf('aisles') != -1; 1893 } 1894 1895 this.isWineShopCategoryPage = function () { 1896 return window.location.pathname.indexOf('vineandcellar/aisles') !== -1; 1897 } 1898 1899 this.isWineShopL2Page = function () { 1900 return this.isWineShopCategoryPage() && this.pathSections?.length === 2; 1901 } 1902 1903 this.isWineShopL3Page = function () { 1904 return this.isWineShopCategoryPage() && this.pathSections?.length === 3; 1905 } 1906 1907 this.isWineShopL4Page = function () { 1908 return this.isWineShopCategoryPage() && this.pathSections?.length === 4; 1909 } 1910 1911 this.isWineShopPage = function () { 1912 return window.location.pathname.indexOf('vineandcellar') !== -1; 1913 } 1914 1915 this.isMarketplaceSearchPage = function () { 1916 return window.isAnalyticsMarketplacePage && (window.location.pathname.indexOf('search-results') != -1 || window.location.pathname.indexOf('seller-search') !== -1); 1917 } 1918 1919 this.isWineShopSearchPage = function () { 1920 return window.location.pathname.indexOf('vineandcellar/search-results') !== -1; 1921 } 1922 1923 this.isPharmacyRxPage = function () { 1924 return window.location.pathname.startsWith('/pharmacy/create-account'); 1925 } 1926 1927 this.isCategoriesPage = function () { 1928 return window.digitalData?.pageDetails?.subSection1 === 'aisles' && !window.digitalData?.pageDetails?.subSection2; 1929 } 1930 1931 /** 1932 * Checks if the current page is weekly-ad page 1933 */ 1934 this.isWeeklyAdPage = function () { 1935 return window.location.pathname.startsWith('/weeklyad') || window.location.pathname.startsWith('/emju-weeklyad'); 1936 } 1937 1938 this.isWeeklyAdDealsPage = function () { 1939 return window.location.pathname.startsWith('/weeklyad/yourdeals') || window.location.pathname.startsWith('/emju-weeklyad/yourdeals'); 1940 } 1941 1942 this.getBannerSellerId = function () { 1943 return `${AB.DATALAYER.isB2BPage() ? '00000000' : '11111111'}`; 1944 } 1945 1946 /** 1947 * Checks if the current page is one of the Cart and checkout pages 1948 */ 1949 this.isCartAndCheckoutPage = function (page, isEditPage) { 1950 return !this.isDFTAPage() && window.location.pathname.startsWith(`/erums/${isEditPage ? 'order/edit' : ''}`) && window.location.pathname.includes(`${page ? page : ''}`); 1951 } 1952 1953 /** 1954 * Checks if the current page is weekly-ad page 1955 */ 1956 this.isOrderAheadPage = function () { 1957 return window.location.pathname.startsWith('/orderahead'); 1958 } 1959 1960 this.isRecipesAndMealsPage = function (page, type) { 1961 if (type) { 1962 return window.location.pathname.startsWith(type === 'recipes' ? 'recipes' : type === 'meals' ? '/meal-plans-recipes' : '') 1963 && window.location.pathname.includes(`${page ? page : ''}`); 1964 } else { 1965 return (window.location.pathname.startsWith('/meal-plans-recipes') || window.location.pathname.startsWith('/recipes')) 1966 && window.location.pathname.includes(`${page ? page : ''}`); 1967 } 1968 1969 } 1970 1971 this.isMealsPlansPage = function () { 1972 return window.location.pathname.startsWith('/meal-plans-recipes'); 1973 } 1974 1975 this.isBundlesLP = function () { 1976 return window.location.pathname.startsWith('/bundles') || window.location.pathname.startsWith('/meal-plans-recipes/bundles'); 1977 } 1978 1979 this.isAssistantPage = function () { 1980 return window.location.pathname.startsWith('/assistant/chat'); 1981 } 1982 1983 this.isReviewsPage = function () { 1984 return window.location.pathname.startsWith('/product/reviews'); 1985 } 1986 1987 this.isRewardsPage = function () { 1988 return window.location.pathname.startsWith('/rewards'); 1989 } 1990 1991 this.isSelfMergeAccountsPage = function () { 1992 return window.location.pathname.startsWith('/customer-account/account-settings/merge'); 1993 } 1994 1995 this.isExpediaPageSource = function () {
1996 return window.location.search?.indexOf('webSource=WEB-EXPEDIA') != -1; 1997 } 1998 1999 this.isSelectiveCartPage = function () { 2000 return AB.DATALAYER.analyticsGetURLParameterByName('selective') == 'true'; 2001 } 2002 2003 this.getExperimentIds = function () { 2004 const targetFlagsValues = window.analyticsSwy?.testFlags 2005 && typeof window.analyticsSwy?.testFlags === 'object' 2006 && Object.keys(window.analyticsSwy.testFlags).filter((key) => key.toLowerCase().includes('acip')).map((key) => `${key}_${window.analyticsSwy.testFlags[key]}`); 2007 const searchVariant = targetFlagsValues && targetFlagsValues.length > 0 ? targetFlagsValues 2008 : localStorage ? Object.keys(localStorage).filter((key) => key.toLowerCase().includes('acip')).map((key) => `${key.replace('ab', '')}_${localStorage[key]}`) : []; 2009 return searchVariant ? searchVariant : [] 2010 } 2011 2012 this.setEventOncePerPage = function (eventName, event, productID) { 2013 try { 2014 let eventSent = false; 2015 // Find the index of the last untilNextEvent in appEventData 2016 let eventIndex = -1; 2017 const listOfPageLoadEvents = [ 2018 'pageLoaded', 2019 'search_results_page', 2020 'zero_search_results', 2021 'pdp_page', 2022 'full_cart', 2023 'checkout_step1', 2024 'checkout_step2', 2025 'checkout_step3', 2026 'transaction_complete', 2027 'pageLoaded_mediacollective', 2028 'selective_view_cart', 2029 'edit_cart', 2030 'pageLoaded_fc', 2031 'pageLoaded_spotlight', 2032 'pageLoaded_albspartner' 2033 ]; 2034 2035 eventIndex = appEventData.findLastIndex((eventObj) => listOfPageLoadEvents.includes(eventObj?.event)); 2036 2037 if (eventIndex !== -1) { 2038 eventSent = appEventData.slice(eventIndex).some((eventObj) => 2039 eventObj?.[`${event.includes('modal') ? 'modal' : event.includes('link') ? 'link' : event.includes('hover') ? 'hover' : ''}`]?.[`${event.includes('modal') ? 'modalAction' : 'linkName'}`] === eventName && (!productID || eventObj?.productInfo?.productID === productID)); 2040 } 2041 return eventSent; 2042 } catch (e) { 2043 console.error('@AnalyticsTeam - Failed to send setEventOncePerPage value due to following error: ', e); 2044 } 2045 } 2046 2047 this.getFullDeliveryTime = function (reservedSlot) { 2048 function getDeliveryHours(dataTime) { 2049 const time = new Date(dataTime); 2050 let dHours = time.getHours(); 2051 const amPm = dHours >= 12 ? 'PM' : 'AM'; 2052 dHours = (dHours % 12) ? (dHours % 12) : 12; 2053 return `${dHours}:00 ${amPm}`; 2054 } 2055 2056 function getDeliveryDate(dataTime) { 2057 if (!dataTime) { 2058 return ''; 2059 } 2060 const tempDate = (new Date(dataTime)).toString().split(' '); 2061 return `${tempDate[0]} ${tempDate[1]} ${tempDate[2]}`; 2062 } 2063 2064 const fullDeliveryTime = `${getDeliveryDate(reservedSlot.displayStartTS)} ${getDeliveryHours(reservedSlot.displayStartTS)} - ${getDeliveryHours(reservedSlot.displayEndTS)}`; 2065 return fullDeliveryTime; 2066 } 2067 2068 this.setDeliveryWindow = function (reservedSlot) { 2069 const slotStartTS = reservedSlot.displayStartTS ? reservedSlot.displayStartTS : reservedSlot.slotStartTS; 2070 const slotEndTS = reservedSlot.displayEndTS ? reservedSlot.displayEndTS : reservedSlot.lastPickupTS ? reservedSlot.lastPickupTS : reservedSlot.slotEndTS; 2071 2072 function toLocal(timestamp) { 2073 const ds = timestamp.replace(/-|:|T/gi, ',').split(','); 2074 return new Date(Date.UTC(ds[0], ds[1] - 1, ds[2], ds[3], ds[4], ds[5])) 2075 } 2076 2077 function getDayDelta(endDate) { 2078 const current = new Date(); 2079 const final = toLocal(endDate); 2080 const deltaT = (final.getTime() - current.getTime()) / 1000 / 60 / 60, eot = 24 - current.getHours(); 2081 return (eot < deltaT ? (Math.ceil((deltaT - eot) / 24)) : 0) + 1; 2082 } 2083 2084 let day_time = { 2085 day: getDayDelta(slotStartTS),
2086 time: Math.ceil(new Date(slotEndTS).getTime() / 1000 / 60 / 60 - new Date(slotStartTS).getTime() / 1000 / 60 / 60), 2087 value: '' 2088 } 2089 day_time.value = `${day_time.day}|${day_time.time}-hour${day_time.time > 1 ? 's' : ''}`; 2090 2091 let elite_tag_value = ''; 2092 if (this.isCartAndCheckoutPage('prebook') || this.isCartAndCheckoutPage('erums/checkout')) { 2093 let elite_tag = document.querySelector('app-delivery-window-type .card[aria-checked="true"] #elite-slot-body'); 2094 let non_elite = document.querySelector('#timesForTheDay input:checked'); 2095 non_elite = non_elite ? non_elite.parentElement.querySelector('.badge-style') : null; 2096 2097 let non_elite_value = non_elite ? non_elite.textContent.toLowerCase().replace(/\s/g, '-') : 'more-delivery-times'; 2098 let eliteValue = elite_tag?.textContent?.toLowerCase()?.replace(/^\s|\s$/g, '')?.replace(/slot type|\s{2,}/g, '')?.replace(/\s/g, '-') || ''; 2099 elite_tag_value = elite_tag && eliteValue ? eliteValue : non_elite_value; 2100 2101 switch (true) { 2102 case elite_tag_value.indexOf('flash') !== -1: 2103 elite_tag_value = 'flash'; 2104 break; 2105 case elite_tag_value.indexOf('premium') !== -1: 2106 elite_tag_value = 'premium'; 2107 break; 2108 case elite_tag_value.indexOf('best-value') !== -1: 2109 elite_tag_value = 'best-value'; 2110 break; 2111 case elite_tag_value.indexOf('fastest') !== -1: 2112 elite_tag_value = 'fastest'; 2113 break; 2114 default: 2115 break; 2116 } 2117 } 2118 !this.isCartAndCheckoutPage('order/confirmation') && this.setLSItem('elite_tag', elite_tag_value); 2119 2120 digitalData.cart.transaction.deliveryTime = AB.DATALAYER.getFullDeliveryTime(reservedSlot); 2121 digitalData.cart.transaction.deliveryWindow = day_time.value; 2122 digitalData.cart.transaction.deliverySlotType = elite_tag_value; 2123 } 2124 2125 this.getPaymentMethod = function (payments) { 2126 let types = { 2127 'CREDITCARD': 'cc', 2128 'EBT': 'ebt', 2129 'EBT-CARD': 'snap_ebt_card', 2130 'EBT-CASH': 'snap_ebt_cash', 2131 'CREDITONACCOUNT': 'coa', 2132 'EWALLET': 'ewallet' 2133 }, 2134 subTypes = { 2135 'PAYPAL': 'paypal' 2136 }, 2137 ret = { 2138 methods: [], 2139 type: {}, 2140 method: '' 2141 }; 2142 2143 if (payments && (payments.length > 0)) { 2144 payments.forEach((payment) => { 2145 const paymentType = payment.type !== undefined ? payment.type : payment.paymentType; 2146 const paymentSubtype = payment.subType !== undefined ? payment.subType : payment.paymentSubType; 2147 if (types[paymentType] === 'ewallet') 2148 ret.methods.push(subTypes[paymentSubtype]); 2149 else { 2150 ret.methods.push(types[paymentType]); 2151 } 2152 ret.type[types[payment.type]] = payment.originalTransactionAmount; 2153 }); 2154 } 2155 ret.method = ret.methods.join('|'); 2156 return ret; 2157 } 2158 2159 /** 2160 * Checks if GRS search_results_impressions event should be triggered 2161 */ 2162 this.shouldSetGRSSearchImpression = function () { 2163 return this.isSearchResultsPage() && !this.isMarketplaceSearchPage() && !this.isMarketplaceCategoryPage() && !this.isWineShopPage(); 2164 } 2165 2166 /** 2167 * Sets a cookie to track modal link clicks 2168 * @param value - the dynamic value stored in the cookie 2169 */ 2170 this.setModalLinkCookie = function (value) { 2171 SWY.CONFIGSERVICE.setDomainCookie(this.cookieNames.MODAL_LINK, value, `${SWY.CONFIGSERVICE.resolvedBanner}.com`, 0); 2172 } 2173 2174 /** 2175 * Sets a cookie for tracking the forgot password/password recovery flow 2176 */ 2177 this.setForgotPasswordCookie = function () { 2178 // SWY.CONFIGSERVICE.setDomainCookie(this.cookieNames.PASSWORD_RESET, 'forgotpassword', `${SWY.CONFIGSERVICE.resolvedBanner}.com`, 0 ); 2179 this.setLinkClickEvents('passwordReset'); 2180 } 2181 2182 /** 2183 * Sets a local storage item to track the creation of the a new account 2184 */ 2185 this.setCreateAccountClicked = function () { 2186 localStorage.setItem('create_account_clicked', true); 2187 } 2188 2189 /** 2190 * Checks if the user is authorized to add/remove products to/from cart 2191 */ 2192 this.isUserAuthorized = function () { 2193 let isErumStore = false;
2194 if (AB?.userInfo?.hasOwnProperty('isErumStore')) { 2195 isErumStore = AB?.userInfo?.isErumStore; 2196 } 2197 let isRegistered = AB.DATALAYER.checkAuthorization(); 2198 return !isRegistered && ((isErumStore && AB?.userInfo?.erumsGuestEnabled === false) || !isErumStore) ? false : true; 2199 } 2200 2201 /** 2202 * Updates the datalayer with information about the coupon that is clipped 2203 * @param coupon - information about the coupon that is clipped 2204 * TO DO: remove after 2024 7.1 release, as all references to this function have been removed 2205 */ 2206 this.analyticsCouponUpdate = function (coupon, analyticsData) { 2207 if (window.AB && window.AB.DATALAYER) { 2208 window.AB.DATALAYER.clipCoupon(coupon, analyticsData); 2209 } else { 2210 console.warn('AB.DATALAYER is not defined on this page'); 2211 } 2212 } 2213 2214 /** 2215 * Sets modal click events 2216 * @param modalClicked - name of the modal that is clicked 2217 */ 2218 this.setModalClickEvents = function (modalClicked, modalMessage, oncePerPageView) { 2219 try { 2220 const updatedModalMessage = modalMessage && ['sendMealsData', 'sendRecipesData'].indexOf(modalMessage) === -1 ? modalMessage : digitalData?.modal?.modalMessage; 2221 if (window.digitalData?.modal) { 2222 window.digitalData.modal.modalAction = modalClicked; 2223 window.digitalData.modal.modalMessage = updatedModalMessage; 2224 } 2225 if (modalClicked === 'pdp:guaranteed-fresh-link|terms-of-service') { 2226 digitalData.productInfo = digitalData?.productInfo || {}; 2227 digitalData.productInfo.productID = modalMessage; 2228 } 2229 2230 if (modalClicked.indexOf('createAccount Modal|continue-with-') !== -1) { 2231 const smsCheckbox = document.querySelector('#textSubscribe')?.checked; 2232 const emailCheckbox = document.querySelector('#emailSubscribe')?.checked; 2233 digitalData.pageFilter.isSocialSmsCheckedOut = !smsCheckbox; 2234 digitalData.pageFilter.isSocialEmailCheckedOut = !emailCheckbox; 2235 } 2236 2237 const eventTrigger = oncePerPageView ? this.setEventOncePerPage(modalClicked, 'modalView') : false; 2238 let payload = { 2239 modal: { 2240 modalAction: modalClicked, 2241 modalMessage: updatedModalMessage 2242 }, 2243 page: digitalData?.pageDetails, 2244 pageFilter: digitalData?.pageFilter, 2245 user: digitalData?.user, 2246 productInfo: digitalData?.productInfo 2247 }; 2248 2249 if (modalMessage === 'sendMealsData') { 2250 payload.meals = digitalData.meals; 2251 } else if (modalMessage === 'sendRecipesData') { 2252 payload.recipes = digitalData.recipes; 2253 } 2254 2255 !eventTrigger && this.pushEvent( 2256 this.EVENTS.MODAL_CLICK, payload 2257 ); 2258 const clearTopNavActions = ['otp:lets-find-your-account|close', 'signin modal|close', 'createaccount modal|close']; 2259 if (clearTopNavActions.indexOf(modalClicked) !== -1) { 2260 AB.DATALAYER.removeLSItem('topNavPreviousPage'); 2261 } 2262 if (window.digitalData?.modal?.modalMessage) { 2263 window.digitalData.modal.modalMessage = ''; 2264 } 2265 this.setAnalyticsCrossPageData({ action: 'remove', name: 'acip371626Checkbox' }); 2266 if (modalClicked.indexOf('|start-shopping') != -1) { 2267 this.setAnalyticsCrossPageData({ action: 'remove', name: 'acip371626Verified' }); 2268 } 2269 if (modalClicked?.indexOf('account-social-verify') != -1) { 2270 this.setAnalyticsCrossPageData({ action: 'remove', name: 'acip371626Social' }); 2271 } 2272 } catch (e) { 2273 console.error('@AnalyticsTeam - Failed to send ModalClicked value due to following error: ', e); 2274 } 2275 } 2276 2277 this.setB2BModalEvent = function (modalEvent, modalAction) { 2278 if (AB.DATALAYER.isB2BPage()) { 2279 this.pushEvent( 2280 modalEvent, { 2281 modal: { 2282 modalAction: modalAction, 2283 } 2284 } 2285 ); 2286 } 2287 } 2288 2289 this.trackShopFlyout = function (classes) { 2290 const isShopButton = classes && Array.from(classes)?.length > 0 && Array.from(classes).indexOf('shop-class') !== -1; 2291 if (isShopButton && AB.DATALAYER.isB2BPage()) { 2292 const targetNode = document.querySelector('.menu-nav__list-item.shop-class'); 2293 const config = { attributes: false, childList: true, subtree: true }; 2294 let isModalEventSet = false;
2295 const callback = function (mutations, observer) { 2296 if (window.jQuery && !isModalEventSet) { 2297 const shopModal = jQuery('#flyoutContainer #shopFlyOutModal'); 2298 isModalEventSet = true; 2299 if (shopModal) { 2300 shopModal.on('shown.bs.modal', () => { 2301 AB.DATALAYER.setB2BModalEvent('modalView', 'shop_leftnavigation|view'); 2302 shopModal.off('shown.bs.modal'); 2303 observer.disconnect(); 2304 }) 2305 } 2306 } 2307 2308 }; 2309 const observer = new MutationObserver(callback); 2310 observer.observe(targetNode, config); 2311 } 2312 } 2313 2314 this.trackAccFlyout = function () { 2315 if (AB.DATALAYER.isB2BPage()) { 2316 let elem = document.querySelector('.menu-nav__profile-button[aria-label="Account menu"]'); 2317 if (elem?.getAttribute('aria-expanded') === 'false') { 2318 AB.DATALAYER.setB2BModalEvent('modalView', 'account_rightnavigation|view'); 2319 } 2320 } 2321 } 2322 2323 // Sets the Add to Cart Clicked flag, used for sign in modal tracking when this modal is triggered from ATC button 2324 this.setATCFlag = function (flag) { 2325 digitalData.modal.isATCClicked = flag; 2326 } 2327 2328 // return the node(s) if visible 2329 this.isVisible = function (node) { 2330 let filteredArray; 2331 if (typeof (node?.[Symbol.iterator]) === 'function') { 2332 filteredArray = Array.from(node).filter((element) => { 2333 return ( 2334 element?.offsetWidth !== 0 && 2335 element?.offsetHeight !== 0 && 2336 element?.getClientRects().length && 2337 !element?.classList?.contains('d-none') && 2338 !element?.classList?.value?.includes('d-none') && 2339 !element?.className?.includes('d-none') 2340 ); 2341 }); 2342 } else { 2343 if (node?.offsetWidth !== 0 && 2344 node?.offsetHeight !== 0 && 2345 node?.getClientRects().length && 2346 !node?.classList?.contains('d-none') && 2347 !node?.classList?.value?.includes('d-none') && 2348 !node?.className?.includes('d-none')) { 2349 filteredArray = node; 2350 } 2351 } 2352 return filteredArray; 2353 } 2354 2355 // check if element is within viewport; used for AMC impressions, Rokt impressions 2356 this.isElementVisible = function (element) { 2357 const noHiddenClass = !element?.classList?.contains('d-none') && 2358 !element?.classList?.value?.includes('d-none') && 2359 !element?.className?.includes('d-none'); 2360 const elHeightPosition = element?.getBoundingClientRect()?.bottom; 2361 const elWidth = element?.getBoundingClientRect()?.width || 0; 2362 const variableWidthReduction = (25 * elWidth) / 100; 2363 const elWidthPosition = element?.getBoundingClientRect()?.right - variableWidthReduction; 2364 const screenHeight = screen?.height; 2365 const windowWidth = window.innerWidth; 2366 const elHeight = element?.getBoundingClientRect()?.height; 2367 return elHeightPosition && screenHeight >= elHeightPosition && windowWidth >= elWidthPosition && elHeightPosition >= elHeight && noHiddenClass; 2368 } 2369 2370 /** 2371 * Sets modal view events 2372 * @param modalViewed - - name of the modal that has been opened and is viewed 2373 * @param oncePerPageView - true or false value, if the event should trigger once per pageView 2374 */ 2375 this.setModalViewEvents = function (modalViewed, oncePerPageView, productID) { 2376 try { 2377 if (window.digitalData?.modal) { 2378 window.digitalData.modal.modalAction = modalViewed; 2379 } 2380 if (productID && ['sendMealsData', 'sendRecipesData'].indexOf(productID) === -1) { 2381 digitalData.productInfo.productID = productID; 2382 digitalData.productInfo.isProductTrackingDisabled = digitalData.pageFlags?.isProductTrackingDisabled; 2383 } 2384 digitalData.modal.modalMessage = digitalData?.modal?.isATCClicked ? 'sign-in-to-add' : digitalData.modal.modalMessage === 'sign-in-to-add' ? '' : digitalData.modal.modalMessage; 2385 2386 if (this.getLSItem('analyticsCTAValue')?.indexOf('cta:customer-account:account-dashboard:account-security|link') != -1) { 2387 digitalData.pageFilter.topNav = this.initTopNav(); 2388 } 2389 2390 const eventTrigger = oncePerPageView ? this.setEventOncePerPage(modalViewed, 'modalView') : false;
2391 2392 let payload = { 2393 modal: digitalData?.modal, 2394 page: digitalData?.pageDetails, 2395 pageFilter: digitalData?.pageFilter, 2396 user: digitalData?.user, 2397 productInfo: digitalData?.productInfo 2398 }; 2399 2400 if (productID === 'sendMealsData') { 2401 payload.meals = digitalData.meals; 2402 } else if (productID === 'sendRecipesData') { 2403 payload.recipes = digitalData.recipes; 2404 } else if (productID === 'name-exists' || productID === 'name-doesnt-exist') { 2405 payload.modal.modalMessage = productID; 2406 } else if(productID === 'customCakeData' && digitalData?.pageFilter?.topNav) { 2407 digitalData.pageFilter.topNav = `cta:${digitalData?.page?.previousPageName}|link|customise`; 2408 } 2409 2410 const eventName = productID === 'custom-cake-view' ? 'custom-cake-exposed' : this.EVENTS.MODAL_VIEW; 2411 2412 !eventTrigger && this.pushEvent( 2413 eventName, payload, 2414 oncePerPageView 2415 ); 2416 AB.DATALAYER.setATCFlag(false); 2417 } catch (e) { 2418 console.error('@AnalyticsTeam - Failed to send ModalView value due to following error: ', e); 2419 } 2420 } 2421 2422 /** 2423 * Fire the requested event with the apropriate params 2424 * 2425 */ 2426 this.handleModalAndLinkEvents = function ({ eventType, modalAndLinkData, modalMessage, topNav, oncePerPageView } = {}) { 2427 try { 2428 switch (eventType) { 2429 case 'modalView': 2430 this.setModalViewEvents(modalAndLinkData, oncePerPageView, modalMessage); 2431 break; 2432 case 'goToAccount': 2433 this.setSignInModalEvents(eventType, modalAndLinkData, modalMessage, topNav); 2434 break; 2435 case 'modalClick': 2436 this.setModalClickEvents(modalAndLinkData, modalMessage, oncePerPageView); 2437 break;
2438 case 'linkClick': 2439 this.setLinkClickEvents(modalAndLinkData, oncePerPageView); 2440 break; 2441 case 'topNav': 2442 this.analyticsCTATracking(modalAndLinkData); 2443 break; 2444 case 'crossPageData': 2445 this.setAnalyticsCrossPageData({ action: 'set', name: modalAndLinkData, data: modalMessage, isAcrossPages: false }); 2446 break; 2447 default: 2448 break; 2449 } 2450 } catch (error) { 2451 console.error('@AnalyticsTeam error:', error); 2452 } 2453 } 2454 2455 /** 2456 * Sets localStorage item for SBC items link 2457 * Used for pageFilter.topNav setting on the next page 2458 * @param element - DOM element of the image 2459 */ 2460 this.setSBCLinkClickEvents = function (element) { 2461 if (element) { 2462 const currentPill = element.closest('.categories-item'); 2463 const elementTitle = element.closest('.c-shop-by-category__view') && element.closest('.c-shop-by-category__view').querySelector('.shop-by-category-heading__title') ? element.closest('.c-shop-by-category__view').querySelector('.shop-by-category-heading__title').textContent : 'no-value'; 2464 const componentTitle = element.closest('.c-shop-by-category__main-wrapper') ? element.closest('.c-shop-by-category__main-wrapper').getAttribute('component-title') : ''; 2465 const allSBCs = document.querySelectorAll('.shopbycategory'); 2466 const currentSBC = element.closest('.shopbycategory'); 2467 const SBCindex = allSBCs && currentSBC ? Array?.prototype?.indexOf?.call(allSBCs, currentSBC) + 1 : 0; 2468 2469 const currentPillMktpTitle = currentPill?.querySelector('.aisle-category') ? currentPill.querySelector('.aisle-category').textContent?.toLowerCase().replace(' - ', ' ').replace(/\s/g, '-') : 'no-value'; 2470 const allCurrentSBCPills = currentSBC?.querySelectorAll('.categories-item'); 2471 const currentZoneSlot = SBCindex > 0 ? `z${SBCindex < 10 ? '0' : ''}${SBCindex}` : 'z00'; 2472 const currentPillSlotValue = allCurrentSBCPills && currentPill ? Array?.prototype?.indexOf?.call(allCurrentSBCPills, currentPill) + 1 : 0; 2473 const currentPillSlot = currentPillSlotValue > 0 ? `sp${currentPillSlotValue < 10 ? '0' : ''}${currentPillSlotValue}` : `sp00`; 2474 2475 const aisleName = element.dataset['aisleName']; 2476 let aislesPreviousPage = `aisles-${aisleName}`; 2477 let categoryPill = `${currentZoneSlot}|click|category-pill:${currentPillMktpTitle}|${currentPillSlot}`; 2478 2479 if (!elementTitle && SBCindex === 1 && !componentTitle) { 2480 if (this.isHomePage()) { 2481 aislesPreviousPage += '-home'; 2482 } else if (this.isPdp()) { 2483 aislesPreviousPage += '-pdp'; 2484 } 2485 } 2486 if (this.isMarketplaceCategoryPage()) { 2487 if (window.digitalData.pageDetails && !window.digitalData.pageDetails.subSection3 && !window.digitalData.pageDetails.subSection4) { 2488 aislesPreviousPage += '-l1'; 2489 } 2490 } 2491 localStorage.setItem('aislesPreviousPage', aislesPreviousPage); 2492 if (window.isAnalyticsMarketplacePage) { 2493 localStorage.setItem('mktpCategoryPillClicked', categoryPill); 2494 } 2495 } 2496 } 2497 2498 /** 2499 * Sets the default link click event 2500 * @param linkClicked - name of the link that is clicked 2501 * @param oncePerPageView - boolean type, if the event should trigger once per pageView 2502 */ 2503 this.setLinkClickEvents = function (linkClicked, oncePerPageView, modalMessage, isSendToSS) { 2504 try { 2505 let productId; 2506 const isStickyBannerDismissLink = linkClicked?.indexOf('cta|sticky-banner-dismiss-') === 0 2507 || linkClicked?.indexOf('cta|sticky-promo-ad-banner-dismiss-') === 0; 2508 if (window.digitalData && window.digitalData.link) { 2509 window.digitalData.link.linkName = linkClicked; 2510 } 2511
2512 if (modalMessage && ['sendMealsData', 'sendRecipesData'].indexOf(modalMessage) === -1) { 2513 digitalData.modal.modalMessage = modalMessage; 2514 } 2515 2516 let payload = { 2517 link: { 2518 linkName: linkClicked 2519 }, 2520 page: digitalData?.pageDetails, 2521 pageFilter: digitalData?.pageFilter, 2522 user: digitalData?.user, 2523 productInfo: digitalData?.productInfo || {}, 2524 modal: digitalData?.modal, 2525 cart: digitalData?.cart 2526 }; 2527 if (isSendToSS !== undefined) { 2528 payload.link.isSendToSS = isSendToSS; 2529 } 2530 if (isStickyBannerDismissLink) { 2531 payload.link.c14 = 'sticky-banner|shop-ex|564521|538994'; 2532 } 2533 if (linkClicked === 'selective-cart|check' || linkClicked === 'selective-cart|uncheck') { 2534 productId = modalMessage; 2535 payload.link.selectiveCartProductId = productId; 2536 payload.productInfo.productID = productId; 2537 } 2538 const eventTrigger = oncePerPageView ? this.setEventOncePerPage(linkClicked, 'linkClick', productId) : false; 2539 if (modalMessage === 'sendMealsData') { 2540 payload.meals = digitalData.meals; 2541 } else if (modalMessage === 'sendRecipesData') { 2542 payload.recipes = digitalData.recipes; 2543 } else if ((['grid-product-clicks-oneTag', 'top-nav-oneTag'].indexOf(modalMessage) != -1) || (linkClicked === 'full-cart|add-note')) { 2544 payload.link.feature = modalMessage; 2545 } 2546 2547 !eventTrigger && this.pushEvent( 2548 this.EVENTS.LINK_CLICK, payload 2549 ); 2550 } catch (e) { 2551 console.error('@AnalyticsTeam - Failed to send LinkClicked value due to following error: ', e); 2552 } 2553 } 2554 2555 /** 2556 * Sets the link click events for the offer drawer 2557 * @param linkClicked - default link click name 2558 * @param offerName - the name of the offer drawer that has been opened 2559 */ 2560 this.setLinkClickOfferDrawer = function (linkClicked, offerName, element) { 2561 if (AB?.DATALAYER && digitalData) { 2562 let carouselTitle; 2563 const payload = { 2564 link: { 2565 linkName: linkClicked, 2566 linkDetail: offerName, 2567 oneTagLinkName: 'offer-details|coupon' 2568 }, 2569 page: structuredClone(digitalData?.pageDetails), 2570 pageFilter: structuredClone(digitalData?.pageFilter) 2571 }; 2572 if (element && element instanceof Element) { 2573 const modelId = element.closest('lazy-wrapper')?.getAttribute('data-model-configuration-id'); 2574 if (modelId) { 2575 payload.page.abTestDetails = digitalData.couponTrackingInfo && digitalData.couponTrackingInfo[modelId] && digitalData.couponTrackingInfo[modelId].msgExperiment; 2576 } 2577 carouselTitle = this.getAnalyticsCarouselTitle(element); 2578 this.trackAEMZone('offer-drawer', element, `${carouselTitle}:drawer`); 2579 } 2580 if (AB.DATALAYER.isWeeklyAdDealsPage()) { 2581 linkClicked = `weekly-ad:${carouselTitle ? AB.DATALAYER.sanitizeAnalyticsText(carouselTitle) : ''}|offer-details`; 2582 } 2583 payload.pageFilter.placement = AB.DATALAYER.setCouponPlacement({}, element, 'offer-drawer'); 2584 2585 AB.DATALAYER.pushEvent( 2586 this.EVENTS.LINK_CLICK, payload 2587 ); 2588 if (AB.DATALAYER.isHomePage()) { 2589 const placement = payload.pageFilter?.placement; 2590 const mTyp = element?.closest('.slick-product-carousel, .master-product-carousel') ? 'product-carousel' : 'deals-carousel'; 2591 AB.DATALAYER.setCRSClickEvent({ 2592 linkName: 'offer-details-click', 2593 mTyp: mTyp, 2594 element: element, 2595 placement: placement, 2596 component: AB.DATALAYER.getCRSComponent({ 2597 element: element, 2598 section: carouselTitle, 2599 detail: payload.page?.abTestDetails, 2600 placement: placement 2601 }) 2602 }); 2603 } 2604 digitalData.pageFilter.bannerCardCarousel = ''; 2605 } else { 2606 console.warn('AB.DATALAYER is not defined on this page'); 2607 } 2608 } 2609 2610 /** 2611 * Sets the modal message event 2612 * @param userMessage - message that is displayed to the user 2613 */ 2614 this.setUserMessageEvents = function (userMessage) { 2615 if (window.digitalData && window.digitalData.modal) {
2616 window.digitalData.modal.modalMessage = userMessage; 2617 } 2618 if (window.AB && window.AB.DATALAYER) { 2619 AB.DATALAYER.pushEvent( 2620 this.EVENTS.MODAL_MESSAGE, { 2621 modal: { 2622 modalMessage: userMessage 2623 }, 2624 page: digitalData?.pageDetails, 2625 pageFilter: digitalData?.pageFilter 2626 } 2627 ); 2628 } else { 2629 console.warn('AB.DATALAYER is not defined on this page'); 2630 } 2631 } 2632 2633 /** 2634 * Updates datalayer when location tracking is enabled 2635 */ 2636 this.geoEnabled = function () { 2637 if (window.digitalData && window.digitalData.user) { 2638 window.digitalData.user.locationTracking = 'enabled'; 2639 } 2640 } 2641 2642 /** 2643 * Updates datalayer when location tracking is disabled or throws errors 2644 */ 2645 this.geoBlocked = function () { 2646 if (window.digitalData && window.digitalData.user) { 2647 window.digitalData.user.locationTracking = 'blocked'; 2648 } 2649 } 2650 2651 /** 2652 * Returns cart savings types and values 2653 * @param savings - the savings object from the API response, either cart savings or item savings 2654 * @param calledBy - component that is calling the method, can be called on minicart open or on product action 2655 */ 2656 this.calculateCartSavings = function (savings, calledBy) { 2657 const clubcardSavings = []; 2658 const j4uSavings = []; 2659 const employeeSavings = []; 2660 const rewardsSavings = []; 2661 const promoCodeSavings = []; 2662 const totalSavings = {}; 2663 if (savings && Array.isArray(savings) && savings.length > 0) { 2664 if (calledBy === 'cart') { 2665 // Separating cart savings into multiple sections 2666 savings.forEach(x => { 2667 if (x.savingsCategoryName === 'Club Card Savings') { 2668 clubcardSavings.push(x?.savingsAmount && !isNaN(parseFloat(x.savingsAmount)) ? parseFloat(x.savingsAmount) : 0); 2669 } else if (x.savingsCategoryName === 'J4U Savings' || x.savingsCategoryName === 'Just for U Savings') { 2670 j4uSavings.push(x?.savingsAmount && !isNaN(parseFloat(x.savingsAmount)) ? parseFloat(x.savingsAmount) : 0); 2671 } else if (x.savingsCategoryName === 'Employee Savings') { 2672 employeeSavings.push(x?.savingsAmount && !isNaN(parseFloat(x.savingsAmount)) ? parseFloat(x.savingsAmount) : 0); 2673 } else if (x.savingsCategoryName === 'Rewards Savings') { 2674 rewardsSavings.push(x?.savingsAmount && !isNaN(parseFloat(x.savingsAmount)) ? parseFloat(x.savingsAmount) : 0); 2675 } else if (x.savingsCategoryName === 'Rewards and CashOff') { 2676 rewardsSavings.push(x?.savingsAmount && !isNaN(parseFloat(x.savingsAmount)) ? parseFloat(x.savingsAmount) : 0); 2677 } 2678 }); 2679 } else if (calledBy === 'item') { 2680 // Separating item savings into multiple sections 2681 const savingsCategories = { 2682 'Club Card Savings': 'clubcardSavings', 2683 'Just for U Savings': 'j4uSavings', 2684 'Employee Savings': 'employeeSavings', 2685 'Rewards Savings': 'rewardsSavings', 2686 'Rewards and CashOff': 'rewardsSavings', 2687 18: 'clubcardSavings', 2688 42: 'j4uSavings', 2689 67: 'clubcardSavings', 2690 68: 'clubcardSavings', 2691 69: 'j4uSavings', 2692 70: 'j4uSavings', 2693 72: 'employeeSavings', 2694 75: 'j4uSavings', 2695 76: 'j4uSavings', 2696 77: 'clubcardSavings', 2697 80: 'clubcardSavings', 2698 85: 'clubcardSavings', 2699 89: 'rewardsSavings' 2700 }; 2701 savings.forEach(x => { 2702 if (savingsCategories[x.categoryId] === 'j4uSavings') { 2703 j4uSavings.push(x?.discountAmount && !isNaN(parseFloat(x.discountAmount)) ? parseFloat(x.discountAmount) : 0); 2704 } else if (savingsCategories[x.categoryId] === 'employeeSavings') { 2705 employeeSavings.push(x?.discountAmount && !isNaN(parseFloat(x.discountAmount)) ? parseFloat(x.discountAmount) : 0); 2706 } else if (savingsCategories[x.categoryId] === 'rewardsSavings') { 2707 rewardsSavings.push(x?.discountAmount && !isNaN(parseFloat(x.discountAmount)) ? parseFloat(x.discountAmount) : 0); 2708 } else if (savingsCategories[x.categoryId] === 'clubcardSavings') { 2709 clubcardSavings.push(x?.discountAmount && !isNaN(parseFloat(x.discountAmount)) ? parseFloat(x.discountAmount) : 0); 2710 } 2711 }) 2712 } 2713 } 2714 2715 const clubcardSavingsValue = clubcardSavings.reduce(AB.DATALAYER.safeReducer, 0); 2716 const j4uSavingsValue = j4uSavings.reduce(AB.DATALAYER.safeReducer, 0); 2717 const employeeSavingsValue = employeeSavings.reduce(AB.DATALAYER.safeReducer, 0); 2718 const rewardsSavingsValue = rewardsSavings.reduce(AB.DATALAYER.safeReducer, 0); 2719 const promoCodeSavingsValue = promoCodeSavings.reduce(AB.DATALAYER.safeReducer, 0); 2720 2721 totalSavings.clubcardSavings = clubcardSavingsValue.toString(); 2722 totalSavings.j4uSavings = j4uSavingsValue.toString(); 2723 totalSavings.employeeSavings = employeeSavingsValue.toString(); 2724 totalSavings.rewardsSavings = rewardsSavingsValue.toString(); 2725 totalSavings.promocodeSavings = promoCodeSavingsValue.toString(); 2726 2727 totalSavings.total = [clubcardSavingsValue, j4uSavingsValue, employeeSavingsValue, rewardsSavingsValue, promoCodeSavingsValue].reduce(AB.DATALAYER.safeReducer, 0); 2728 2729 return totalSavings; 2730 } 2731 2732 /** 2733 * Returns the offer drawer status - open or closed 2734 * @param productID - the ID of the product that the offer drawer open has been triggered from 2735 */ 2736 this.getDrawerStatus = function (productID) { 2737 const currentProduct = document.querySelector(`.product-item-${productID}`); 2738 return currentProduct?.closest('.sidebar__right-drawer[aria-label="offer details"]'); 2739 } 2740 2741 /** 2742 * Returns the type of the offer drawer - basic offer drawer, BOGO or j4u (coupons drawer) 2743 * @param productID - the ID of the product that the offer drawer open has been triggered from 2744 */ 2745 this.getDrawerType = function (productID) { 2746 const currentProduct = document.querySelector(`.product-item-${productID}`); 2747 let drawerType = 'drawer'; 2748 if (currentProduct?.closest('right-drawer product-generic-grid')?.getAttribute('data-api-type') === 's-bogo') { 2749 drawerType = 'BOGO-drawer'; 2750 } else if (currentProduct?.closest('right-drawer product-generic-grid')?.getAttribute('data-api-type') === 'c-products-by-upc-drawer') { 2751 drawerType = 'J4U-drawer'; 2752 } 2753 return drawerType; 2754 } 2755 2756 /** 2757 * Returns the minicart drawer status 2758 */ 2759 this.getMinicartStatus = function () { 2760 return document.querySelector('.sidebar__right-drawer') && document.querySelector('.sidebar__right-drawer')?.ariaLabel === 'Shopping cart'; 2761 } 2762 2763 /** 2764 * Separates pages into sections and types based on subSection values 2765 */ 2766 this.sortSiteSections = function () {
2767 let siteSection = 'not-available'; 2768 let pageType = 'not-available'; 2769 if (digitalData?.pageDetails) { 2770 let subSection1 = digitalData.pageDetails.subSection1 === 'loyalty' ? digitalData.pageDetails.subSection2 : digitalData.pageDetails.subSection1; 2771 let subSection2 = digitalData.pageDetails.subSection2; 2772 if (this.isWineShopPage()) { 2773 subSection1 = 'vineandcellar'; 2774 } 2775 2776 switch (subSection1) { 2777 case 'home': 2778 siteSection = 'Home'; 2779 pageType = 'Home'; 2780 break; 2781 case 'search-results': 2782 siteSection = 'Shop'; 2783 pageType = 'Search Results'; 2784 break; 2785 case 'product-details': 2786 case 'product': 2787 siteSection = 'Shop'; 2788 pageType = 'PDP'; 2789 break; 2790 case 'aisles': 2791 siteSection = 'Shop'; 2792 if (AB.DATALAYER.isAislesL2Page()) { 2793 pageType = 'Aisles L2'; 2794 } else if (AB.DATALAYER.isAislesL3Page()) { 2795 pageType = 'Aisles L3'; 2796 } else if (AB.DATALAYER.isAislesL4Page()) { 2797 pageType = 'Aisles L4'; 2798 } else { 2799 pageType = 'Aisles'; 2800 } 2801 break; 2802 case 'deals': 2803 siteSection = 'Shop'; 2804 pageType = 'Deals'; 2805 break; 2806 case 'product-review': 2807 siteSection = 'Shop'; 2808 pageType = 'Product Review'; 2809 break; 2810 case 'justforu': 2811 case 'offer-details': 2812 siteSection = 'Banners for you'; 2813 subSection2 === 'coupons-deals' ? pageType = 'Coupons Page' : pageType = 'Banners for you'; 2814 break; 2815 case 'justforu-guest': 2816 case 'foru-guest': 2817 case 'guest': 2818 siteSection = 'Banners for you'; 2819 pageType = 'Banners for you'; 2820 break; 2821 case 'account': 2822 siteSection = 'Account'; 2823 if (subSection2 === 'sign-in') { 2824 pageType = 'Sign-In'; 2825 } else if (subSection2 === 'short-registration') { 2826 pageType = 'Registration'; 2827 } 2828 break; 2829 case 'customer-account': 2830 siteSection = 'Account'; 2831 if (subSection2 === 'rewards') { 2832 pageType = 'Rewards'; 2833 } else { 2834 pageType = 'My Account'; 2835 } 2836 break; 2837 case 'order-account': 2838 siteSection = 'Account'; 2839 pageType = 'Orders'; 2840 break; 2841 case 'pet-program': 2842 siteSection = 'Account'; 2843 pageType = 'Pet Program'; 2844 break; 2845 case 'freshpass': 2846 case 'delivery-subscription': 2847 siteSection = 'Account'; 2848 pageType = 'Delivery Subscription'; 2849 break; 2850 case 'schedule-and-save': 2851 siteSection = 'Account'; 2852 pageType = 'Schedule and Save'; 2853 break; 2854 case 'recipes': 2855 siteSection = 'Recipes'; 2856 pageType = 'Recipes'; 2857 break; 2858 case 'pharmacy': 2859 siteSection = subSection2 === 'create-account' ? 'Shop' : 'Pharmacy'; 2860 pageType = 'Pharmacy'; 2861 break; 2862 case 'vaccinations': 2863 siteSection = 'Pharmacy'; 2864 pageType = 'Pharmacy'; 2865 break; 2866 case 'sp': 2867 case 'vs': 2868 case 'lp': 2869 siteSection = this.isFreshpassManagePage() ? 'Account' : 'Landing Page'; 2870 pageType = 'Landing Page'; 2871 break; 2872 case 'mylist': 2873 siteSection = 'My List'; 2874 pageType = 'My List'; 2875 break; 2876 case 'foru': 2877 case 'rewards': 2878 case 'coupons-deals': 2879 siteSection = 'Banners for you'; 2880 subSection2 === 'coupons-deals' ? pageType = 'Coupons Page' : pageType = 'Banners for you'; 2881 break; 2882 case 'foru-guest': 2883 siteSection = 'Banners for you'; 2884 pageType = 'Banners for you'; 2885 break; 2886 case 'flavoradventure': 2887 siteSection = 'Banners for you'; 2888 pageType = 'Flavor Adventure'; 2889 break; 2890 case 'm': 2891 case 'marketplace': 2892 siteSection = 'marketplace'; 2893 pageType = 'marketplace'; 2894 break; 2895 case 'household': 2896 siteSection = 'Shop'; 2897 pageType = 'Pharmacy'; 2898 break; 2899 case 'vineandcellar': 2900 siteSection = 'wine-shop'; 2901 pageType = 'wine-shop'; 2902 break; 2903 case 'weekly-ad': 2904 siteSection = 'weekly-ad'; 2905 pageType = 'weekly-ad'; 2906 break; 2907 case 'erums': 2908 case 'Full Cart': 2909 case 'Checkout': 2910 case 'reserve time': 2911 siteSection = 'Checkout'; 2912 pageType = 'Checkout'; 2913 break; 2914 case 'purchases': 2915 siteSection = 'shop'; 2916 pageType = 'buy-it-again'; 2917 break; 2918 case 'meals': 2919 siteSection = 'meals'; 2920 pageType = 'meals'; 2921 break; 2922 case 'assistant': 2923 siteSection = 'albertsons-ai'; 2924 pageType = 'albertsons-ai'; 2925 break; 2926 default: 2927 break; 2928 } 2929 } 2930 if (window.digitalData && window.digitalData.pageDetails) { 2931 if (digitalData.pageDetails.banner === 'sincerely-food') { 2932 siteSection = 'e-community'; 2933 pageType = 'Sincerely-Food'; 2934 } 2935 digitalData.pageDetails.channel = siteSection; 2936 digitalData.pageDetails.pageType = pageType; 2937 } 2938 }; 2939 2940 /** 2941 * return autoReplenishment object with deliveryWindow 2942 * @param slotPreference 2943 */ 2944 this.setDeliveryWindowAR = function (slotPreference, caller) { 2945 2946 let deliveryDay; 2947 let deliveryTime; 2948 let delieveryStartTime; 2949 let delieveryEndTime; 2950 let autoReplenishment = { 2951 arDeliveryWindow: '' 2952 }; 2953 const days = { 2954 sunday: 1, 2955 monday: 2, 2956 tuesday: 3, 2957 wednesday: 4, 2958 thursday: 5, 2959 friday: 6, 2960 saturday: 7 2961 }; 2962 2963 // set delivery day as number 2964 if (slotPreference?.fulfillmentDay || slotPreference?.dayOfWeek) { 2965 deliveryDay = caller === 'newSNSPage' ? slotPreference?.dayOfWeek.toLowerCase() : days[slotPreference?.fulfillmentDay?.toLowerCase()]; 2966 } 2967 2968 // set delivery time 2969 if (slotPreference?.slotType || slotPreference?.priceType?.[0]) { 2970 const checkVal = slotPreference?.priceType?.[0] || slotPreference?.slotType; 2971 switch (checkVal) { 2972 case 'B2B_DELIVERY_1HR': 2973 case 'B2C_DELIVERY_1HR': 2974 case 'B2B_DUG': 2975 case 'B2C_DUG': 2976 case 'ONEHR': 2977 deliveryTime = caller === 'newSNSPage' ? '1' : '1-hour'; 2978 break; 2979 case 'B2B_DELIVERY_2HR': 2980 case 'B2C_DELIVERY_2HR': 2981 case 'TWOHR': 2982 deliveryTime = caller === 'newSNSPage' ? '2' : '2-hours'; 2983 break; 2984 case 'B2B_DELIVERY_4HR': 2985 case 'B2C_DELIVERY_4HR': 2986 case 'FOURHR': 2987 deliveryTime = caller === 'newSNSPage' ? '4' : '4-hours'; 2988 break; 2989 default: 2990 break; 2991 } 2992 } 2993 2994 // set delivery time 2995 if (caller === 'newSNSPage' && slotPreference?.slotStartTime && slotPreference?.slotEndTime) { 2996 const timeConvertor = (rawTime) => { 2997 const time = rawTime > 24 ? Math.floor(rawTime / 100) : rawTime; 2998 return `${time > 12 ? time % 12 : time}${time > 11 ? 'pm' : 'am'}`; 2999 } 3000 const slotStartTime = slotPreference?.slotStartTime?.toString()?.includes('T') ? new Date(slotPreference?.slotStartTime).getHours() : slotPreference?.slotStartTime; 3001 const slotEndTime = slotPreference?.slotEndTime?.toString()?.includes('T') ? new Date(slotPreference?.slotEndTime).getHours() : slotPreference?.slotEndTime; 3002 delieveryStartTime = timeConvertor(slotStartTime); 3003 delieveryEndTime = timeConvertor(slotEndTime); 3004 } 3005 3006 3007 if (deliveryDay && deliveryTime) { 3008 autoReplenishment.arDeliveryWindow = `${deliveryDay}|${deliveryTime}${caller === 'newSNSPage' && delieveryStartTime && delieveryEndTime ? `|${delieveryStartTime}-${delieveryEndTime}` : ''}`; 3009 } 3010 3011 return autoReplenishment; 3012 } 3013 3014 /** 3015 * sets the error event for
3016 * error messages generated when the user is writing a review for a product 3017 */ 3018 this.setErrorsReviewProductsForm = function () { 3019 document.querySelectorAll('form.review input#headline, form.review textarea#comments').forEach((element) => { 3020 element.addEventListener('keyup', () => { 3021 if (document.querySelector(`.review__field__error#review-${element.id}-error`) && 3022 AB.DATALAYER.isVisible(document.querySelector(`.review__field__error#review-${element.id}-error`))) { 3023 const errorMessage = document.querySelector(`.review__field__error#review-${element.id}-error`)?.innerText?.trim()?.replace('Error:\n', ''); 3024 AB.DATALAYER.setError('3365', 'reviews', errorMessage); 3025 } 3026 }); 3027 }); 3028 } 3029 3030 /** 3031 * Sets OTP modal events 3032 * @param event - VIEW or CLICK event 3033 * @param modal - The modal being interacted with 3034 * @param message - The message sent if any 3035 */ 3036 this.OTPModalEvents = function (event, modal, message) { 3037 if (!event) { 3038 return 3039 } else { 3040 event = event == 'view' ? this.EVENTS.MODAL_VIEW : this.EVENTS.MODAL_CLICK; 3041 } 3042 3043 if (window.digitalData && window.digitalData.modal) { 3044 window.digitalData.modal.modalAction = modal; 3045 if (message) { 3046 window.digitalData.modal.modalMessage = message; 3047 } 3048 } 3049 if (window.AB && window.AB.DATALAYER) { 3050 let payload = { 3051 modal: { 3052 modalAction: modal 3053 }, 3054 page: digitalData?.pageDetails, 3055 pageFilter: digitalData?.pageFilter 3056 }; 3057 if (message) { 3058 payload.modal.modalMessage = message; 3059 } 3060 AB.DATALAYER.pushEvent(event, payload); 3061 } else { 3062 console.warn('AB.DATALAYER is not defined on this page'); 3063 } 3064 } 3065 3066 /** 3067 * Sets OTP modal events 3068 * @param event - VIEW or CLICK event 3069 * @param payload - The subsections object or link_click value 3070 */ 3071 3072 this.OTPPageEvents = function (event, payload) { 3073 if (!event) { 3074 return; 3075 } else if (window.AB && window.AB.DATALAYER) { 3076 if (event === 'view' && payload && typeof (payload) === 'object') { 3077 if (payload.ss3) { 3078 digitalData.pageDetails.subSection3 = payload.ss3; 3079 } 3080 if (payload.ss4) { 3081 digitalData.pageDetails.subSection4 = payload.ss4; 3082 } 3083 AB.DATALAYER.setPageLoad(); 3084 sessionStorage.removeItem('OTPtype'); 3085 } else if (event === 'click' && payload && typeof (payload) === 'string') { 3086 AB.DATALAYER.setLinkClickEvents(payload); 3087 sessionStorage.setItem('OTPtype', 'page'); 3088 } 3089 } else { 3090 console.warn('AB.DATALAYER is not defined on this page'); 3091 } 3092 } 3093 3094 this.analyticsCTATracking = function (value) { 3095 const santitizedValue = value?.toLowerCase()?.indexOf('cta:foru:coupons-deals|button|') !== -1 ? 3096 (value?.toLowerCase().indexOf('freshpass') !== -1 ? 'cta:foru:coupons-deals|button|fresh-pass' 3097 : this.sanitizeAnalyticsText(value.replace('&', 'and'))) 3098 : this.sanitizeAnalyticsText(value); 3099 this.setLSItem('analyticsCTAValue', santitizedValue); 3100 } 3101 3102 /** 3103 * Returns the referring URL with the current URL 'r' parameter, if found 3104 * the default value is document.referrer 3105 */ 3106 this.getReferringUrl = function () { 3107 return AB.DATALAYER.analyticsGetURLParameterByName('r') || document.referrer; 3108 }; 3109 3110 /** 3111 * Returns the third party info 3112 */ 3113 this.getThirdParty = function () { 3114 let reportThirdParty = ''; 3115 this.arrThirParty.forEach(x => { 3116 if (document.querySelectorAll(`.${x}`).length > 0) { 3117 reportThirdParty = x; 3118 } 3119 }); 3120 return reportThirdParty; 3121 }; 3122 3123 /** 3124 * Returns topNav value
3125 * topNav = nav structure from previous page (level) > nav structure from current page (level) 3126 * nav structure = concatenation of subSection1 to subSection4 3127 * level = number of subsections populated 3128 */ 3129 this.initTopNav = function () { 3130 let topNav = ''; 3131 let isMarketplace = window.isAnalyticsMarketplacePage; 3132 if (localStorage.getItem('analyticsPageInfo')) { 3133 localStorage.removeItem('analyticsPageInfo'); 3134 } 3135 if (localStorage.getItem('topNavPreviousPage') || this.getCookie('topNavPreviousPage')) { 3136 const topNavPreviousPage = localStorage.getItem('topNavPreviousPage') || this.getCookie('topNavPreviousPage'); 3137 const subSections = [digitalData.pageDetails.subSection1, digitalData.pageDetails.subSection2, digitalData.pageDetails.subSection3, digitalData.pageDetails.subSection4].filter(function (x) { 3138 return x; 3139 }); 3140 const topNavCurrentPage = subSections.join(':') + ' (L' + subSections.length + ')'; 3141 topNav = `${topNavPreviousPage} > ${isMarketplace ? 'mkp|' : ''}${topNavCurrentPage}`; 3142 localStorage.removeItem('topNavPreviousPage'); 3143 if (this.getCookie('topNavPreviousPage')) { 3144 this.setLSItem('shopOrTopNavFlag', 'true'); 3145 this.deleteCookie('topNavPreviousPage', `${digitalData.pageDetails.banner}.com`); 3146 } 3147 if (window.location.pathname.startsWith('/freshpass/manage/subscribe')) { 3148 this.setLSItem('analyticsGetFreshPass', topNav); 3149 } 3150 } else if (localStorage.getItem('aislesPreviousPage')) { 3151 topNav = localStorage.getItem('aislesPreviousPage'); 3152 localStorage.removeItem('aislesPreviousPage'); 3153 if (this.isMarketplaceCategoryPage()) { 3154 this.pfmSourceMktpCategories = topNav.substring(0, topNav.lastIndexOf('-')); 3155 } 3156 } else if (localStorage.getItem('topNavDFTAPage')) { 3157 topNav = localStorage.getItem('topNavDFTAPage'); 3158 localStorage.removeItem('topNavDFTAPage'); 3159 } else if (localStorage.getItem('analyticsTopNavStudent')) { 3160 topNav = localStorage.getItem('analyticsTopNavStudent'); 3161 localStorage.removeItem('analyticsTopNavStudent'); 3162 } else if (localStorage.getItem('topNavSimilarItemsBIA')) { 3163 topNav = localStorage.getItem('topNavSimilarItemsBIA'); 3164 localStorage.removeItem('topNavSimilarItemsBIA'); 3165 } else if (localStorage.getItem('analyticsOTPFlowPP')) { 3166 topNav = localStorage.getItem('analyticsOTPFlowPP'); 3167 localStorage.removeItem('analyticsOTPFlowPP'); 3168 } else if (this.getLSItem('analyticsCTAValue') && !this.isExpediaPageSource()) { 3169 topNav = this.handleGetLSValue('analyticsCTAValue'); 3170 } else if (this.getLSItem('analyticsSNSlink')) { 3171 topNav = this.handleGetLSValue('analyticsSNSlink'); 3172 } else if (this.getLSItem('topNavCheckoutPage')) { 3173 topNav = this.handleGetLSValue('topNavCheckoutPage'); 3174 } else if (this.getLSItem('analyticsMealsCarouselTopNav')) { 3175 topNav = this.handleGetLSValue('analyticsMealsCarouselTopNav'); 3176 } 3177 if (this.getLSItem('manageSnsFromOrderConfirm')) { 3178 topNav = this.handleGetLSValue('manageSnsFromOrderConfirm'); 3179 } 3180 return topNav; 3181 }; 3182 3183 this.handleFeatureExpiration = function (featureTimeLimit) { 3184 let isFeatureExpired = true; 3185 const currentTime = new Date().getTime(); 3186 if (featureTimeLimit >= currentTime) { 3187 isFeatureExpired = false; 3188 } 3189 return isFeatureExpired; 3190 } 3191 3192 /** 3193 * Legal/compliance: alcohol products in featured sponsored ad slots must not 3194 * be tracked in Adobe or OneTag. 3195 * @param product 3196 */ 3197 this.isAlcoholAdTrackingDisabled = function (product) { 3198 if (!window?.SWY?.CONFIGSERVICE?.featureToggleConfig?.sponsoredAlcohol) { 3199 return false; 3200 } 3201 return !!(product?.containsAlcohol && product?.featured); 3202 }; 3203 3204 /** 3205 * pushes a new event in the data layer 3206 * @param name must be a non blank string 3207 * @param data associated with the event 3208 */ 3209 this.pushEvent = function (event, data) { 3210 data = data && typeof data === 'object' && !Array.isArray(data) ? data : {}; 3211 if (!data.isTrackingExpired) { 3212 digitalData = digitalData || {}; 3213 digitalData.pageFlags = digitalData.pageFlags || {}; 3214 digitalData.serverData = digitalData.serverData || {}; 3215 digitalData.user = digitalData.user || {}; 3216 3217 if (typeof event === 'string' || event instanceof String) { 3218 data.serverData = data.serverData || {}; 3219 data.serverData.eventId = this.generateEventId(); 3220 data.serverData.currentTime = this.generateCurrentTime(); 3221 data.serverData.experimentIds = this.getExperimentIds(); 3222 3223 if (AB.DATALAYER.getLSItem('analyticsDUGPharmacyInfoBanner')) { 3224 if (data?.page) { 3225 data.page.engagementLadderImpressions = 'pharmacy:need-prescription-banner'; 3226 AB.DATALAYER.removeLSItem('analyticsDUGPharmacyInfoBanner'); 3227 } 3228 } 3229 if (data?.page && !data.page.feature) { 3230 data.page.feature = AB.DATALAYER.getFeatureValue(event); 3231 } 3232 3233 data.user = data.user ? { ...digitalData.user, ...data.user } : digitalData.user; 3234 3235 let dataClone = structuredClone({ 3236 event, 3237 ...data 3238 }); 3239 // add serverData for batch coupon event 3240 if (event === 'coupon_clipped') { 3241 const lsCouponGroup = AB.DATALAYER.getLSItem('couponGroup') ? AB.DATALAYER.safeJSONParse(AB.DATALAYER.getLSItem('couponGroup')) : []; 3242 if (lsCouponGroup.length > 0) { 3243 lsCouponGroup[lsCouponGroup.length - 1].serverData = data.serverData;
3244 AB.DATALAYER.setLSItem('couponGroup', JSON.stringify(lsCouponGroup)) 3245 } 3246 } 3247 3248 let sessionIdStatus = function () { 3249 AB?.DATALAYER?.initUser(); 3250 const isSessionIdLoaded = digitalData.user.sessionId && digitalData.user.sessionId !== 'ID not found'; 3251 return isSessionIdLoaded; 3252 } 3253 3254 let isSessionIdLoaded = digitalData.user.sessionId && digitalData.user.sessionId !== 'ID not found'; 3255 3256 if (!isSessionIdLoaded) { 3257 isSessionIdLoaded = sessionIdStatus(); 3258 } 3259 3260 let pushClonedData = function () { 3261 isSessionIdLoaded = sessionIdStatus(); 3262 if (isSessionIdLoaded) { 3263 appEventData.push(dataClone); 3264 if (AB.DATALAYER.shouldSendDatatoOneTag(event, dataClone)) { 3265 if(dataClone?.experimentationReturnPromise && event === 'target-experiment-exposure'){ 3266 return AB.DATALAYER.sendDataToOneTag(event, dataClone); 3267 }else{ 3268 AB.DATALAYER.sendDataToOneTag(event, dataClone); 3269 } 3270 } 3271 } 3272 } 3273 3274 if (AB?.DATALAYER?.shouldDelayEvent(event) && !AB.DATALAYER.isOrderAheadPage()) { 3275 if (isSessionIdLoaded) { 3276 appEventData.push(dataClone); 3277 if (AB.DATALAYER.shouldSendDatatoOneTag(event, dataClone)) { 3278 if(dataClone?.experimentationReturnPromise && event === 'target-experiment-exposure'){ 3279 return AB.DATALAYER.sendDataToOneTag(event, dataClone); 3280 }else{ 3281 AB.DATALAYER.sendDataToOneTag(event, dataClone); 3282 } 3283 } 3284 } else { 3285 document.addEventListener('onGetUserInfoLoad', pushClonedData); 3286 setTimeout(() => { 3287 isSessionIdLoaded = sessionIdStatus(); 3288 if (!isSessionIdLoaded) { 3289 appEventData.push(dataClone); 3290 if (AB.DATALAYER.shouldSendDatatoOneTag(event, dataClone)) { 3291 AB.DATALAYER.sendDataToOneTag(event, dataClone); 3292 } 3293 document.removeEventListener('onGetUserInfoLoad', pushClonedData); 3294 } 3295 }, 3000); 3296 } 3297 } else { 3298 appEventData.push(dataClone); 3299 if (AB.DATALAYER.shouldSendDatatoOneTag(event, dataClone)) { 3300 if(dataClone?.experimentationReturnPromise && event === 'target-experiment-exposure'){ 3301 return AB.DATALAYER.sendDataToOneTag(event, dataClone); 3302 }else{ 3303 AB.DATALAYER.sendDataToOneTag(event, dataClone); 3304 } 3305 } 3306 } 3307 digitalData = { ...digitalData, ...data }; 3308 if (event !== AB.DATALAYER.BATCHCOUPONCLIPPED && event !== 'coupon_clipped' && !AB.DATALAYER.isCRSClickEvent(event, dataClone)) { 3309 AB.DATALAYER.setBatchCoupon(); 3310 } 3311 } 3312 } 3313 } 3314 3315 // set pageFilter.recommendationType 3316 this.setRecommendationType = function (recommendationType) { 3317 if (digitalData && digitalData.pageFilter) { 3318 digitalData.pageFilter.recommendationType = recommendationType; 3319 } 3320 }; 3321 3322 /** 3323 * Initializes the user journey status and status code, using the status code from local storage 3324 */ 3325 this.getJourneyStatus = function () { 3326 return this.JOURNEY_STATUSES[this.journeyStatusCode]; 3327 }; 3328 3329 /** 3330 * Initializes the user journey status code, using the status code from local storage 3331 */ 3332 this.initJourneyStatus = function () { 3333 this.journeyStatusCode = localStorage.getItem('journeyStatusCode') || this.JourneyStatusCode.EMPTY; 3334 }; 3335 3336 /** 3337 * Sets the user journey status and the status code 3338 * @param code 3339 */ 3340 this.setJourneyStatus = function (code) { 3341 this.journeyStatusCode = code; 3342 digitalData.user.journeyStatus = this.JOURNEY_STATUSES[this.journeyStatusCode]; 3343 localStorage.setItem('journeyStatusCode', this.journeyStatusCode); 3344 }; 3345 3346 /** 3347 * Updates the user journey status and the status code 3348 * @param flag 3349 */ 3350 this.updateJourneyStatus = function (flag) { 3351 this.journeyStatusCode |= flag; 3352 digitalData.user.journeyStatus = this.JOURNEY_STATUSES[this.journeyStatusCode]; 3353 localStorage.setItem('journeyStatusCode', this.journeyStatusCode); 3354 }; 3355 3356 // set pageFilter.carousel
3357 this.setCarouselName = function (carouselName) { 3358 if (typeof (carouselName) === 'undefined') { 3359 const carouselNamefromLS = this.handleGetLSValue('analyticsCarouselName'); 3360 return carouselNamefromLS; 3361 } 3362 if (digitalData?.pageFilter) { 3363 digitalData.pageFilter.carousel = this.sanitizeAnalyticsText(carouselName); 3364 } 3365 } 3366 3367 this.setPageFilterPlacement = (placement, mediaType, recommendationType) => { 3368 if (digitalData) { 3369 const { pageFilter } = digitalData; 3370 pageFilter.placement = placement; 3371 pageFilter.mediaType = mediaType; 3372 pageFilter.recommendationType = recommendationType ? recommendationType : pageFilter.recommendationType ? pageFilter.recommendationType : ''; 3373 } 3374 } 3375 3376 /** 3377 * return the pageName string 3378 * @param {*} channel in case we need a specific channel 3379 * @param {*} getExistingPathSections in case we need subsections other than what we have in the pathSections 3380 * @param {*} checkoutStepIndex index of the checkout step 3381 */ 3382 this.setPageName = function (channel, getExistingPathSections, checkoutStepIndex) { 3383 if (digitalData) { 3384 if (this.isCartAndCheckoutPage('checkout') && checkoutStepIndex) { 3385 digitalData.pageDetails.pageName = `${this.getBanner()}:delivery:${channel ? channel : digitalData.pageDetails.channel}:step ${checkoutStepIndex} - ${digitalData.pageDetails.subSection2}`; 3386 } else { 3387 if (this.isDFTAPage() || this.isCartAndCheckoutPage('cart') || this.isCartAndCheckoutPage('confirmation') || this.isCartAndCheckoutPage('prebook') || this.isCartAndCheckoutPage('', true)) { 3388 channel = 'delivery'; 3389 } else if (this.isWeeklyAdPage()) { 3390 channel = 'weekly-ad'; 3391 } 3392 digitalData.pageDetails.pageName = `${this.getBanner()}${!this.isRecipesAndMealsPage() ? (`:${channel ? channel : digitalData.pageDetails.channel}`) : ''}:${this.isCartAndCheckoutPage('prebook') ? 'Prebook Store' : (getExistingPathSections ? this.initPathSections(getExistingPathSections).join(':') : this.pathSections.join(':'))}`; 3393 } 3394 } 3395 } 3396 3397 /** 3398 * Set Previous Page name 3399 */ 3400 this.setPreviousPageName = function () { 3401 if (digitalData && digitalData.pageDetails) { 3402 const pName = digitalData.pageDetails.pageName; 3403 const previousPName = digitalData.crossPageData?.analyticsPreviousPageName?.data || this.handleGetLSValue('analyticsPreviousPageName') || pName; 3404 this.setAnalyticsCrossPageData({ action: 'remove', name: 'analyticsPreviousPageName' }); 3405 digitalData.pageDetails.previousPageName = previousPName; 3406 if (pName) { 3407 this.setLSItem('analyticsPreviousPageName', pName); 3408 this.setAnalyticsCrossPageData({ action: 'set', name: 'analyticsPreviousPageName', data: pName, isAcrossPages: true }); 3409 } 3410 } 3411 } 3412 3413 /** 3414 * Set the values from pathsections to subsections from 1 to 4 3415 */ 3416 this.setSubsections = function () { 3417 if (digitalData) { 3418 digitalData.pageDetails.subSection1 = this.pathSections[0]; 3419 digitalData.pageDetails.subSection2 = this.pathSections[1]; 3420 digitalData.pageDetails.subSection3 = this.pathSections[2]; 3421 digitalData.pageDetails.subSection4 = this.pathSections[3]; 3422 } 3423 } 3424 3425 /** 3426 * Set the restricted product flag for search and aisle pages 3427 */ 3428 this.setRestrictedProductPageFlag = function (isProductTrackingDisabled) { 3429 if (digitalData) { 3430 digitalData.pageFlags = digitalData.pageFlags || {}; 3431 if (isProductTrackingDisabled) { 3432 digitalData.pageFlags.isProductTrackingDisabled = isProductTrackingDisabled; 3433 this.setAnalyticsCrossPageData({ action: 'set', name: 'analyticsRestrictedProductPage', data: isProductTrackingDisabled, isAcrossPages: true }); 3434 } 3435 } 3436 } 3437 3438 this.getAllItems = function (data) { 3439 const allItems = [];
3440 data.orderItems.forEach((item) => { 3441 if (item.name && item.totalPrice > 0 && item.isFound) { 3442 allItems.push({ 3443 'productID': item.itemId, 3444 'productName': item.name, 3445 'units': item.quantity.toString(), 3446 'basePrice': item.basePrice.toString(), 3447 'totalPrice': item.totalPrice.toString(), 3448 'isProductTrackingDisabled': !!item.isProductTrackingDisabled 3449 }); 3450 } 3451 }); 3452 3453 return allItems; 3454 } 3455 3456 /** 3457 * Function which will handle analytics data and flags used across the pages 3458 * @param {*} action get or set the content of the pageFlags 3459 * @param {*} name set the name of the flag 3460 * @param {*} data set the value of the flag 3461 * @param {*} isAcrossPages should this flag be available when the user refreshes the page or navigates to another 3462 */ 3463 this.setAnalyticsCrossPageData = function ({ action, name, data, isAcrossPages } = {}) { 3464 if (digitalData) { 3465 const { crossPageData } = digitalData; 3466 if (action === 'set' && name) { 3467 crossPageData[`${name}`] = { 3468 'data': data ? data : false, 3469 'isAcrossPages': isAcrossPages ? isAcrossPages : false 3470 }; 3471 isAcrossPages && this.setLSItem('analyticsLSObject', JSON.stringify(crossPageData)); 3472 //Should check the behaviour of the top nav 3473 } else if (action === 'get') { 3474 const crossPageDataFromLS = this.getLSItem('analyticsLSObject') && this.safeJSONParse(this.getLSItem('analyticsLSObject')); 3475 Object.assign(digitalData.crossPageData, crossPageDataFromLS); 3476 } else if (action === 'remove' && name) { 3477 if (crossPageData.hasOwnProperty(`${name}`)) { 3478 delete crossPageData[`${name}`]; 3479 } 3480 let onlyCrossPageData = {}; 3481 for (const [key, value] of Object.entries(crossPageData)) { 3482 if (value?.isAcrossPages === true) { 3483 onlyCrossPageData[key] = value; 3484 } 3485 } 3486 Object.keys(onlyCrossPageData).length > 0 && this.setLSItem('analyticsLSObject', JSON.stringify(onlyCrossPageData)); 3487 } 3488 } 3489 } 3490 3491 /** 3492 * Function to set impression on the same page 3493 * @param {*} impression impression value that needs to be set 3494 */ 3495 this.setImpression = function (impression) { 3496 if (digitalData?.pageFilter && !digitalData?.pageFilter?.impressions?.includes(impression)) { 3497 digitalData.pageFilter.impressions = digitalData.pageFilter.impressions ? `${digitalData.pageFilter.impressions},${impression}` : impression; 3498 } 3499 } 3500 3501 /** 3502 * Function to clear impression on the same page 3503 * @param {*} impression impression value that needs to be cleared 3504 */ 3505 this.removeImpression = function (impression) { 3506 if (digitalData?.pageFilter?.impressions && digitalData?.pageFilter?.impressions?.includes(impression)) { 3507 let regEX = new RegExp(',?' + impression + ''); 3508 digitalData.pageFilter.impressions = digitalData.pageFilter.impressions.replace(regEX, '').replace(/^,/, ''); 3509 } 3510 } 3511 3512 this.setTopNavLinkClicks = function (linkTitle) { 3513 if (linkTitle.indexOf('Health') != -1) { 3514 this.setLinkClickEvents('navigation|health'); 3515 } else if (linkTitle.indexOf('Pharmacy') != -1) { 3516 this.setLinkClickEvents('navigation|pharmacy'); 3517 } else if (linkTitle.indexOf('Business') != -1) { 3518 this.setLinkClickEvents('navigation|for-business'); 3519 } else if (linkTitle.indexOf('Grocery') != -1) { 3520 AB.DATALAYER.setLSItem('analyticsNavTracking', 'top-nav'); 3521 } 3522 } 3523 3524 this.setTopNavCategory = function (element) { 3525 if (element && element instanceof Element) { 3526 let topNav; 3527 const l1Title = AB.DATALAYER.sanitizeAnalyticsText(element.closest('.categories-item__drilldown > div[role="menu"]')?.ariaLabel?.trim()); 3528 const l2Title = AB.DATALAYER.sanitizeAnalyticsText(element.closest('ul.t_nav__dd__parent-list > li')?.querySelector('a')?.textContent?.trim()); 3529 const l3Title = AB.DATALAYER.sanitizeAnalyticsText(element.closest('ul.t_nav__dd__parent-list-2 > li')?.querySelector('a')?.textContent?.trim()); 3530 const l4Title = AB.DATALAYER.sanitizeAnalyticsText(element.closest('ul.t_nav__dd__parent-list-3 > li')?.querySelector('a')?.textContent?.trim()); 3531 topNav = `${l1Title}${l2Title ? `>${l2Title}` : ''}${l3Title ? `>${l3Title}` : ''}${l4Title ? `>${l4Title}` : ''}`;
3532 AB.DATALAYER.setLSItem('analyticsTopTertiaryNavTracking', topNav); 3533 } 3534 } 3535 3536 this.trackTopNavOneTag = function (action, categoryName) { 3537 if (action === 'hover') { 3538 const eventTrigger = this.setEventOncePerPage(categoryName, 'hover-over-impressions'); 3539 !eventTrigger && AB.DATALAYER.pushEvent('hover-over-impressions', { 3540 hover: { 3541 linkName: categoryName, 3542 feature: 'top-nav-oneTag', 3543 isSendToSS: true, 3544 }, 3545 user: digitalData.user, 3546 page: digitalData.pageDetails, 3547 pageFilter: digitalData.pageFilter 3548 }); 3549 } else if (action === 'click') { 3550 AB.DATALAYER.setLinkClickEvents(categoryName, true, 'top-nav-oneTag', true); 3551 } 3552 } 3553 3554 /** 3555 * Track clicks on products for AMC 3556 */ 3557 this.trackOneTagProductClick = function ({ product, placement, feature } = {}) { 3558 if (this.isHomePage() && feature === 'carousel') { 3559 const component = AB.DATALAYER.getCRSComponent({ 3560 section: product?.carouselSection, 3561 detail: product?.p13nAdobeMetrics?.metricA || product?.adobeMetrics?.metricA || product?.impressionAdditionalDetail || product?.adobeMetricA, 3562 sponsored: product?.featured, 3563 placement: placement 3564 }); 3565 AB.DATALAYER.setCRSClickEvent({ 3566 linkName: 'product-click', 3567 mTyp: 'product-carousel', 3568 placement: placement, 3569 component: component, 3570 product: product 3571 }); 3572 } 3573 if ((this.isSearchResultsPage() || this.isAislesPage() || (this.isPdp() && feature === 'carousel')) && !this.isWineShopPage() && !this.isAnalyticsMarketplacePage) { 3574 let payload = { 3575 link: { 3576 linkName: `${digitalData.pageDetails.subSection1}-product-click`, 3577 isSendToSS: true, 3578 feature: `${feature}-product-clicks-oneTag` 3579 }, 3580 serverData: { 3581 attributionToken: digitalData.user.storedAttributionToken 3582 }, 3583 search: digitalData.search || {}, 3584 page: digitalData.pageDetails, 3585 pageFilter: digitalData.pageFilter, 3586 user: digitalData.user, 3587 }; 3588 if (product) { 3589 payload.productInfo = { 3590 productID: product?.id, 3591 bia: product?.pastPurchased, 3592 oos: product?.inventoryAvailable > 0 ? false : true, 3593 sponsored: product?.featured, 3594 mp: product?.isMarketplaceItem || 'N', 3595 display: feature, 3596 placement: placement, 3597 isProductTrackingDisabled: !!product.isProductTrackingDisabled, 3598 isCustomCake: product?.isCustomCake, 3599 productName: product?.name + (product?.isCustomCake ? '|event424=1' : '') 3600 } 3601 } 3602 3603 payload.pageFilter.placement = ''; 3604 payload.pageFilter.pdpID = feature === 'carousel' && this.isPdp() && AB.DATALAYER.getLSItem('analyticsAMCPDPInfo') ? AB.DATALAYER.safeJSONParse(AB.DATALAYER.getLSItem('analyticsAMCPDPInfo'))?.productID : ''; 3605 payload.pageFilter.carousel = feature === 'carousel' ? product?.carouselSection : ''; 3606 payload.pageFilter.mediaType = feature === 'carousel' ? 'product-carousel' : 'product-grid'; 3607 delete payload.search.impressions; 3608 3609 this.pushEvent(this.EVENTS.LINK_CLICK, payload); 3610 } 3611 } 3612 3613 this.trackWeeklyAdIcmpid = function (feature, navPath) { 3614 let trackingValue = ''; 3615 if (AB.DATALAYER.isWeeklyAdPage()) { 3616 trackingValue = `${navPath && navPath.includes('?') ? '&' : '?'}icmpid=weekly-ad-${AB.DATALAYER.isWeeklyAdDealsPage() ? 'pv-' : ''}guest-${feature}`; 3617 } 3618 return trackingValue; 3619 } 3620 3621 this.defaultCTANavTracking = function (value) { 3622 window['AB']['DATALAYER'].analyticsCTATracking(`cta:${window['AB']['DATALAYER'].pathSections?.join(':')}|${value}`); 3623 } 3624 3625 this.trackPartnerNavLink = function (link) { 3626 if (link?.indexOf('/rewards/partners') !== -1) { 3627 window['AB']['DATALAYER'].setAnalyticsCrossPageData({ action: 'set', name: 'analyticsFeatureTracking', data: 'partnerHub413315', isAcrossPages: true }); 3628 this.defaultCTANavTracking('button|partner-offers') 3629 } 3630 if (link?.indexOf('/loyalty/discovery') !== -1) { 3631 window['AB']?.['DATALAYER']?.analyticsCTATracking('cta:' + window['AB']?.['DATALAYER']?.initPathSections(true).join(':') + '|link|' + link); 3632 } 3633 } 3634
3635 this.handlePDPAccordionClick = function (tab, element) { 3636 if (element && tab) { 3637 if (element?.nodeName === 'SPAN') { 3638 element = element.closest('button'); 3639 } 3640 this.setLinkClickEvents(`pdp|${tab}:${element?.getAttribute('aria-expanded') == 'true' ? 'expand' : 'collapse'}`, true); 3641 } 3642 } 3643 3644 this.getFeatureValue = function (eventName) { 3645 let feature = ''; 3646 if (AB.DATALAYER.isB2BPage()) { 3647 switch (eventName) { 3648 case 'cart_add': 3649 case 'add_quantity': 3650 case 'remove_quantity': 3651 case 'remove_cart': 3652 feature = 'productactions|b2b|342524|302266'; 3653 break; 3654 case 'pdp_page': 3655 feature = 'pdp-page|b2b-grocery|ACIP-345643|ACIP-302266'; 3656 break; 3657 case 'checkout': 3658 case 'checkout_step1': 3659 case 'checkout_step2': 3660 case 'checkout_step3': 3661 case 'checkout_step4': 3662 feature = 'checkout-page|b2b-grocery|ACIP-345643|ACIP-302266'; 3663 break; 3664 default: 3665 break; 3666 } 3667 } else { 3668 switch (digitalData.crossPageData?.analyticsFeatureTracking?.data) { 3669 case 'bundlesACIP400316': 3670 feature = 'standard-bundle|pzn|389325|364106'; 3671 break; 3672 case 'mediumbannerACIP397009': 3673 feature = 'deals-advert-medium-banner|collective|392111|391882'; 3674 break; 3675 case 'freshpassDiscountACIP392731': 3676 feature = 'fp|fp-pre|392731|353274'; 3677 break; 3678 case 'partnerCodesACIP404189': 3679 feature = 'fp|loyalty|ACIP-404189|397110'; 3680 break; 3681 case 'subsPrefACIP405518': 3682 feature = 'sub-phase-shop-assist|shp-exp|397917|357630'; 3683 break; 3684 case 'casSubPrefACIP409006': 3685 feature = 'cas-sub-phase|shp-exp|409006|357630'; 3686 break; 3687 case 'partnerHub413315': 3688 feature = 'partners|loyalty|403134|413315'; 3689 break; 3690 case 'expedia414687': 3691 feature = 'expedia|shp-exp|394681|383353'; 3692 break; 3693 case 'expediaMobile508929': 3694 feature = 'loyalty|exp|500714|495176'; 3695 break; 3696 case 'dietaryPrefACIP455391': 3697 feature = 'dietary-pref|shp-exp|452129|338908'; 3698 break; 3699 case 'selectiveCartACIP462764': 3700 feature = 'selective-cart|shp-exp|460977|452569'; 3701 break; 3702 case 'selectiveCartACIP498475': 3703 feature = 'selective-cart|shp-exp|492731|492728'; 3704 break; 3705 case 'subsUpdateACIP468396': 3706 feature = 'substitution|shp-exp|459602|270001'; 3707 break; 3708 case 'smartBasketACIP416781': 3709 feature = 'smart-basket|pzn|395920|396472'; 3710 break; 3711 default: 3712 break; 3713 } 3714 delete digitalData.crossPageData?.analyticsFeatureTracking; 3715 AB.DATALAYER.setAnalyticsCrossPageData({ action: 'remove', name: 'analyticsFeatureTracking' }); 3716 } 3717 return feature; 3718 } 3719 3720 /** 3721 * Returns the quantity currently in the cart for a given product id. 3722 * Checks every known cart shape used across the app (live + persisted). 3723 * Gated by SWY.CONFIGSERVICE.featureToggleConfig.isSponsoredImpressionFixDisabled 3724 * (killswitch). When the flag is truthy the fix is OFF and callers fall back 3725 * to inline `quantity || qty || 0` logic. 3726 */ 3727 this.getProductCartUnits = function (productId) { 3728 if (window?.SWY?.CONFIGSERVICE?.featureToggleConfig?.isSponsoredImpressionFixDisabled) { 3729 return 0; 3730 } 3731 if (!productId) return 0; 3732 try { 3733 const cart = window?.digitalData?.cart || {}; 3734 const absCart = AB.DATALAYER.safeJSONParse(AB.DATALAYER.getLSItem('abCart')) || {}; 3735 const pools = [
3736 cart.absItems, cart.cartItems, cart.cartItemsList, cart.orderItems, 3737 absCart?.cart?.abs?.cartItems, absCart?.cartItems, absCart?.cartItemsList 3738 ]; 3739 const target = String(productId); 3740 for (const list of pools) { 3741 if (!Array.isArray(list) || !list.length) continue; 3742 const found = list.find(i => String(i?.id ?? i?.itemId ?? i?.productId ?? i?.pId) === target); 3743 if (found) return Number(found.quantity ?? found.qty ?? found.units ?? 0) || 0; 3744 } 3745 } catch (e) { /* noop */ } 3746 return 0; 3747 } 3748 3749 this.getBadgeInfo = function (badges) { 3750 let badgeInfo = 'no-badge'; 3751 if (badges && Array.isArray(badges)) { 3752 if (badges.length === 1 && badges[0]?.badgeName) { 3753 badgeInfo = AB.DATALAYER.sanitizeAnalyticsText(badges[0].badgeName); 3754 } else if (badges.length > 1) { 3755 badgeInfo = badges.map(badge => { 3756 return badge.badgeName && AB.DATALAYER.sanitizeAnalyticsText(badge.badgeName) 3757 }).join(':'); 3758 } 3759 } 3760 return badgeInfo; 3761 } 3762 3763 this.getUIBadge = function (carouselElement, productId) { 3764 if (!window?.SWY?.CONFIGSERVICE?.featureToggleConfig?.isSponsoredImpressionFixDisabled) { 3765 const slideWrapper = carouselElement?.querySelector(`[data-bpn="${productId}"]`)?.closest('.slick-slide, product-carousel-item, product-carousel-item-v2, product-carousel-item-al-v1, product-carousel-item-al-v2'); 3766 const rawBadge = slideWrapper?.querySelector('.pc__badge, .pds-tag')?.textContent?.trim(); 3767 if (!rawBadge) return 'no-badge'; 3768 return AB.DATALAYER.sanitizeAnalyticsText(rawBadge) || 'no-badge'; 3769 } 3770 const UIBadge = carouselElement?.querySelector(`[data-bpn="${productId}"]`)?.closest('.slick-slide')?.querySelector('.pc__badge, .pds-tag')?.textContent || 'no-value'; 3771 return AB.DATALAYER.sanitizeAnalyticsText(UIBadge); 3772 } 3773 3774}).call(AB.DATALAYER); 3775 3776'use strict'; 3777 3778(function () { 3779 3780 /** 3781 * Handles modal view, modal click and link click events 3782 */ 3783 this.handleEvents = function () { 3784 AB.DATALAYER.initModalClickEvents(); 3785 AB.DATALAYER.initForgotPasswordReset(); 3786 AB.DATALAYER.initModalViewEvents(); 3787 }; 3788 3789 /** 3790 * Handles create account, sign in and sign out events 3791 */ 3792 this.handleAccountEvents = function () { 3793 AB.DATALAYER.handleRegistration(); 3794 AB.DATALAYER.handleSignIn(); 3795 AB.DATALAYER.handleSignOut(); 3796 localStorage.setItem('previousPageStatus', AB.DATALAYER.checkAuthorization()); 3797 localStorage.setItem('pageNameTrackForLogout', digitalData.pageDetails.pageName); 3798 localStorage.setItem('pageURLTrackForLogout', window.location.href); 3799 } 3800 3801 /** 3802 * Sets create account event 3803 */ 3804 this.handleRegistration = function () { 3805 if (this.getLSItem('create-account-success') && !window.location.pathname.includes('error')) { 3806 this.isRegistrationSuccess = true; 3807 this.initEmailAndSmsOptIn(); 3808 if (digitalData.user && Object.keys(digitalData.user).length > 0) { 3809 digitalData.user.accountType = `${this.getLSItem('create-account-success') === 'convert' ? 'converted' : 'new'}`; 3810 } 3811 this.pushEvent( 3812 this.EVENTS.CREATEACC, { 3813 page: digitalData?.pageDetails, 3814 user: digitalData?.user 3815 } 3816 ); 3817 } 3818 if (this.getLSItem('create-account-success') === 'convert' && !window.location.pathname.includes('error')) { 3819 if (typeof (window.gtag) === 'function') { 3820 this.removeLSItem('create-account-success'); 3821 } else { 3822 document.addEventListener('ga4LoadComplete', function () { 3823 window.AB?.DATALAYER?.removeLSItem('create-account-success'); 3824 }, false); 3825 } 3826 } else this.removeLSItem('create-account-success'); 3827 if (this.isRegistrationSuccess) { 3828 this.setJourneyStatus(this.JourneyStatusCode.NO_ACTION); 3829 } 3830 }; 3831 3832 /** 3833 * Sets the sign-in event 3834 */ 3835 this.handleSignIn = function () { 3836 // the subscription status is only set on sign in 3837 if (!this.isRegistrationSuccess && AB.DATALAYER.checkAuthorization() && localStorage.getItem('previousPageStatus') === 'false') { 3838 AB.DATALAYER.pushEventSignIn(); 3839 } 3840 }; 3841 3842 /** 3843 * Sets the sign-in event name, the journey status 3844 */ 3845 this.pushEventSignIn = function () { 3846 let otpPrefix = localStorage.getItem('otp_sign_in') || ''; // set on signInWithOTP 3847 if (otpPrefix) { 3848 otpPrefix = `otp-${otpPrefix}-`; 3849 localStorage.removeItem('otp_sign_in'); 3850 } 3851 3852 this.setJourneyStatus(this.JourneyStatusCode.NO_ACTION); 3853 AB.DATALAYER.pushEvent( 3854 otpPrefix + AB.DATALAYER.EVENTS.SIGNIN, { 3855 user: digitalData.user, 3856 page: digitalData.pageDetails, 3857 pageFilter: digitalData.pageFilter 3858 } 3859 ); 3860 }; 3861 3862 /** 3863 * Sets the sign out event 3864 */ 3865 this.handleSignOut = function () { 3866 this.setAnalyticsCrossPageData({ action: 'remove', name: 'acip371626Social' }); 3867 let signOutDetails = { 3868 pageName: localStorage.getItem('pageNameTrackForLogout'), 3869 channel: digitalData.pageDetails.channel, 3870 banner: digitalData.pageDetails.banner, 3871 subSection1: digitalData.pageDetails.subSection1, 3872 subSection2: digitalData.pageDetails.subSection2, 3873 subSection3: digitalData.pageDetails.subSection3, 3874 subSection4: digitalData.pageDetails.subSection4, 3875 pageUrl: digitalData.pageDetails.pageUrl, 3876 platform: digitalData.pageDetails.platform, 3877 previousPageUrl: localStorage.getItem('pageURLTrackForLogout'), 3878 referringURL: this.getReferringUrl(), 3879 thirdParty: this.getThirdParty() 3880 } 3881 if (localStorage.getItem('pageNameTrackForLogout') === null) { 3882 signOutDetails.pageName = digitalData.pageDetails.pageName; 3883 } 3884 if (!this.isRegistrationSuccess && !AB.DATALAYER.checkAuthorization() && localStorage.getItem('previousPageStatus') === 'true') { 3885 if (localStorage.getItem('analyticsLogoutFlag') === undefined) { 3886 if (localStorage.getItem('analyticsSellerCartsInfo')) { 3887 localStorage.removeItem('analyticsSellerCartsInfo'); 3888 } 3889 if (localStorage.getItem('analyticsSellerIds')) { 3890 localStorage.removeItem('analyticsSellerIds') 3891 } 3892 if (this.getLSItem('analyticsPromoBannerView')) { 3893 this.removeLSItem('analyticsPromoBannerView'); 3894 } 3895 AB.DATALAYER.pushEvent( 3896 this.EVENTS.SIGN_OUT, { 3897 user: digitalData.user, 3898 page: signOutDetails 3899 } 3900 ); 3901 } 3902 } 3903 if (localStorage.getItem('analyticsLogoutFlag')) { 3904 if (localStorage.getItem('analyticsSellerCartsInfo')) { 3905 localStorage.removeItem('analyticsSellerCartsInfo'); 3906 } 3907 if (localStorage.getItem('analyticsSellerIds')) { 3908 localStorage.removeItem('analyticsSellerIds') 3909 } 3910 if (this.getLSItem('analyticsPromoBannerView')) { 3911 this.removeLSItem('analyticsPromoBannerView'); 3912 } 3913 if (localStorage.getItem('analyticsLogoutFlag') === 'false') { 3914 AB.DATALAYER.pushEvent( 3915 AB.DATALAYER.EVENTS.SIGN_OUT, { 3916 user: digitalData.user, 3917 page: signOutDetails 3918 } 3919 ); 3920 } else { 3921 AB.DATALAYER.pushEvent( 3922 AB.DATALAYER.EVENTS.SIGN_OUT_USER, { 3923 user: digitalData.user, 3924 page: signOutDetails 3925 } 3926 ); 3927 } 3928 localStorage.removeItem('analyticsLogoutFlag'); 3929 } 3930 }; 3931 3932 /** 3933 * Checks the modal link URL parameter (ml) and the modal link cookie(SAFEWAY_MODAL_LINK) 3934 * (digitalData.event.push) 3935 * Kept the Jquery since the event listener can't listen to the shown.bs.modal bootstrap custom event 3936 */ 3937 this.initModalViewEvents = function () { 3938 // multiaddress delivery unavailable modal 3939 if (jQuery && typeof jQuery !== 'undefined') { 3940 $('#address-not-deliverable-modal').on('shown.bs.modal', function () { 3941 AB.DATALAYER.setModalViewEvents('multiaddress-deliveryunavailable|view'); 3942 }); 3943 3944 // check if sign in modal is displayed 3945 $('#signInModal').on('shown.bs.modal', function () { 3946 AB.DATALAYER.setModalViewEvents('signIn Modal|view'); 3947 }); 3948 3949 // create account modal 3950 $('#unifiedRegistrationModal').on('shown.bs.modal', function () { 3951 AB.DATALAYER.setModalViewEvents('createAccount Modal|view'); 3952 }); 3953 } 3954 3955 // on-boarding widget 3956 const urlStateParameter = AB.DATALAYER.analyticsGetURLParameterByName('state'); 3957 if (document.querySelectorAll('on-boarding-widget')?.length > 0) { 3958 if (urlStateParameter === 'results' || urlStateParameter === 'pre-results' || urlStateParameter === 'locator') { 3959 AB.DATALAYER.setModalViewEvents('fulfillment-landing|view'); 3960 } else if (urlStateParameter === 'signin') { 3961 AB.DATALAYER.setModalViewEvents('signIn Modal|view'); 3962 } else if (urlStateParameter === 'signup') { 3963 AB.DATALAYER.setModalViewEvents('createAccount Modal|view'); 3964 } 3965 } 3966 }; 3967 3968 /** 3969 * Sets modal click event from 'SAFEWAY_MODAL_LINK' cookie, url parameter 'ml' or 'analyticsModalAction' localStorage item 3970 */ 3971 this.initModalClickEvents = function () { 3972 const modalLinkParameter = AB.DATALAYER.analyticsGetURLParameterByName('ml'); 3973 const modalLinkCookie = decodeURI(AB.DATALAYER.getCookie(this.cookieNames.MODAL_LINK)); 3974 if (localStorage.getItem('analyticsModalAction')) { 3975 const unavailableItems = this.handleGetLSValue('analyticsUnavailableCount'); 3976 const modalAction = this.handleGetLSValue('analyticsModalAction'); 3977 const modalMessage = this.handleGetLSValue('analyticsModalMessage'); 3978 this.setModalClickEvents(modalAction, unavailableItems ? `Items Not Available${unavailableItems}` : modalMessage); 3979 } else if (modalLinkCookie) { 3980 const modalAction = modalLinkCookie.split(/(-u|-m)/)[0]; 3981 const unavailableCount = modalLinkCookie.split(/(-u)/)[2]; 3982 const modalMessage = modalLinkCookie.split(/(-m)/)[2]; 3983 if (modalAction) { 3984 this.setModalClickEvents(modalAction, unavailableCount ? `Items Not Available${unavailableCount}` : modalMessage); 3985 } 3986 } else if (modalLinkParameter) { 3987 this.setModalClickEvents(decodeURIComponent(modalLinkParameter)); 3988 } 3989 if (window.SWY?.CONFIGSERVICE) { 3990 SWY.CONFIGSERVICE.setDomainCookie(this.cookieNames.MODAL_LINK, '', SWY.CONFIGSERVICE.resolvedBanner + '.com', 0); 3991 } 3992 } 3993 3994 /** 3995 * Sets fulfillment modal view event 3996 */ 3997 this.setFulfillmentModalView = function () { 3998 if (digitalData?.user?.userType !== 'R') { 3999 digitalData.pageFilter.topNav = AB.DATALAYER.initTopNav(); 4000 } 4001 if (localStorage.getItem('analyticsTwoTabsModal') === 'true') { 4002 AB.DATALAYER.pushEvent(
4003 this.EVENTS.MODAL_VIEW, 4004 { 4005 modal: { 4006 modalAction: 'fulfillment-shop|view' 4007 }, 4008 page: digitalData?.pageDetails, 4009 pageFilter: digitalData?.pageFilter 4010 } 4011 ); 4012 localStorage.removeItem('analyticsTwoTabsModal'); 4013 } else { 4014 AB.DATALAYER.pushEvent( 4015 this.EVENTS.MODAL_VIEW, 4016 { 4017 modal: { 4018 modalAction: 'fulfillment-global|view' 4019 }, 4020 page: digitalData?.pageDetails, 4021 pageFilter: digitalData?.pageFilter 4022 } 4023 ); 4024 } 4025 digitalData.pageFilter.topNav = ''; 4026 } 4027 4028 /** 4029 * Sets link click events for forgot password flow 4030 */ 4031 this.initForgotPasswordReset = function () { 4032 // create account on forgot password page 4033 if (localStorage.getItem('create_account_clicked') === 'true') { 4034 this.setLinkClickEvents('createAccount'); 4035 localStorage.removeItem('create_account_clicked'); 4036 } 4037 // forgot password email sent event 4038 if (localStorage.getItem('analyticsPwdEmail') === 'true') { 4039 this.setLinkClickEvents('passwordEmailSent'); 4040 localStorage.removeItem('analyticsPwdEmail'); 4041 } 4042 // reset password success event 4043 if (localStorage.getItem('pwdResetAnalytics')) { 4044 // new forgot password flow for email and phone number 4045 this.setLinkClickEvents(localStorage.getItem('pwdResetAnalytics')); 4046 localStorage.removeItem('pwdResetAnalytics'); 4047 localStorage.removeItem('analyticsPwdResetSuccess'); 4048 } else if (localStorage.getItem('analyticsPwdResetSuccess') === 'true') { 4049 this.setLinkClickEvents('passwordResetSuccess'); 4050 localStorage.removeItem('analyticsPwdResetSuccess'); 4051 } 4052 } 4053 4054 /** 4055 * Sets the reward Id and the number of rewards used to redeem the specific reward Id 4056 * @param reward 4057 */ 4058 this.clipReward = function (reward) { 4059 if (digitalData) { 4060 digitalData.rewards = { 4061 rewardsID: `${reward.groceryRewardId}|${reward.title}`, 4062 rewardsRedeemed: reward.rewardsRequired?.toString(), 4063 multipleRewardsRedeemed: reward?.clipDetails?.clippedCount || reward?.clipDetails?.clippedCount === 0 ? (Number(reward.clipDetails.clippedCount) + 1).toString() : '' 4064 } 4065 this.pushEvent( 4066 this.EVENTS.REWARDS, { 4067 rewards: digitalData.rewards, 4068 page: digitalData?.pageDetails, 4069 pageFilter: digitalData?.pageFilter 4070 } 4071 ); 4072 } 4073 } 4074 4075 /** 4076 * Sets the reward Id and title for the unclipped reward 4077 * @param reward 4078 */ 4079 this.unclipReward = function (reward) { 4080 if (digitalData) { 4081 digitalData.rewards = { 4082 rewardsID: `${reward.groceryRewardId}|${reward.title}` 4083 } 4084 this.pushEvent( 4085 this.EVENTS.REWARD_UNCLIPPED, { 4086 rewards: digitalData.rewards, 4087 page: digitalData?.pageDetails, 4088 pageFilter: digitalData?.pageFilter 4089 } 4090 ); 4091 } 4092 } 4093 4094 /** 4095 * sets modal view event for substitution drawer 4096 * @param substitutionProducts 4097 * @param substitutionPreference 4098 */ 4099 this.setSubstitutionModalView = function (substitutionProducts, substitutionPreference, item, isAutoClipEnabled) { 4100 if (typeof item !== 'object' || item === null) { 4101 return; 4102 } 4103 4104 let productID = item?.itemId || ''; 4105 let substitutionData = []; 4106 let modalAction = this.isCartAndCheckoutPage('cart') ? 'sub-pref' : 'substitution'; 4107 let modalMessage = ''; 4108 4109 if (substitutionProducts?.length > 0 && item?.substitutionPreference !== 'Do Not Sub') { 4110 substitutionProducts.forEach((product, index) => { 4111 const offersCount = isAutoClipEnabled && product.offers?.filter(offer => offer.isDisplayable)?.length || 0; 4112 substitutionData.push({ 4113 isSubstitution: product.Subflag ? 'true' : 'false', 4114 productID: productID, 4115 substitutionOption: `${product.algoType === 'selected' ? 'past-choice' : 'substitute'}:${product?.id}`, 4116 searchTerm: '', 4117 searchResults: '', 4118 rowSlot: `r${index + 1 > 9 ? index + 1 : '0' + (index + 1)}#s01`, 4119 isCouponAvailable: offersCount > 0 ? true : false, 4120 couponsCount: offersCount, 4121 }); 4122 }); 4123 if (item?.substituteItems?.length) { 4124 modalAction = `edit-substitution${item.substituteItems[0].algoType === 'selected' ? '-past-choice' : ''}`; 4125 } 4126 } else if (item?.substitutionPreference === 'Do Not Sub') { 4127 substitutionData.push({ 4128 productID: productID, 4129 substitutionOption: 'dont-substitute', 4130 }); 4131 modalAction = 'dont-substitute'; 4132 modalMessage = 'substitution-dont-substitute'; 4133 } else { 4134 modalMessage = 'substitution-not-available'; 4135 } 4136 window['AB']['DATALAYER'].setAnalyticsCrossPageData({ action: 'set', name: 'analyticsFeatureTracking', data: 'subsUpdateACIP468396' }); 4137 AB.DATALAYER.pushEvent(
4138 this.EVENTS.MODAL_VIEW, 4139 { 4140 modal: { 4141 modalAction: `${modalAction}|view`, 4142 modalMessage: modalMessage ? modalMessage : '' 4143 }, 4144 productInfo: { 4145 productID: productID, 4146 substitution: substitutionData 4147 }, 4148 page: digitalData?.pageDetails, 4149 pageFilter: digitalData?.pageFilter 4150 } 4151 ); 4152 digitalData.modal = {}; 4153 digitalData.productInfo = {}; 4154 } 4155 4156 /** 4157 * Sets modal click events for substitution drawer 4158 * @param modalAction 4159 * @param substitutionObj 4160 * @param modalMessage 4161 */ 4162 this.setModalClickEventsSubstitution = function (modalAction, substitutionObj, substitutionProducts, item) { 4163 if (typeof item !== 'object' || item === null) { 4164 return; 4165 } 4166 4167 const productID = item.itemId || ''; 4168 const searchTerm = modalAction === 'search' ? substitutionObj?.searchTerm : ''; 4169 const searchResults = modalAction === 'search' ? substitutionObj?.searchResults : ''; 4170 let substitutionData = []; 4171 let modalMessage = ''; 4172 4173 let modalName = item?.substituteItems?.length > 0 4174 ? `edit-substitution${item.substituteItems[0].algoType === 'selected' ? '-past-choice' : ''}` 4175 : item?.substitutionPreference === 'Do Not Sub' || item?.substitutionPreferenceV2 === 'Do Not Substitute' 4176 ? 'dont-substitute' : 'substitution'; 4177 4178 if (modalAction === 'substitute' && substitutionObj) { 4179 const lastTwoEvents = appEventData?.slice(-2); 4180 if (lastTwoEvents?.some(event => event?.event === 'modalClick' && event?.modal?.modalAction?.includes('|search'))) { 4181 modalAction = 'search-substitute'; 4182 modalMessage = `substitution-search-substitute-${substitutionObj.rowSlot}`; 4183 } else { 4184 modalMessage = `substitution-substitute-${substitutionObj.rowSlot}`; 4185 } 4186 } else if (modalAction === 'search') { 4187 modalAction = searchTerm ? modalAction : `not-available-search`; 4188 modalMessage = searchTerm ? `substitution-search-${searchTerm}` : `substitution-search-not-available-${searchTerm}`; 4189 } 4190 4191 4192 if (substitutionProducts === null && modalAction === 'dont-substitute') { 4193 if (this.isCartAndCheckoutPage('cart')) { 4194 modalName = 'sub-pref'; 4195 modalAction = 'dont-substitute:save-substitution'; 4196 } 4197 substitutionData.push({ 4198 productID: productID, 4199 substitutionOption: 'dont-substitute', 4200 }); 4201 } else if (substitutionProducts) { 4202 const substitutionProductsArray = Array.isArray(substitutionProducts) ? substitutionProducts : [substitutionProducts]; 4203 if (substitutionProductsArray?.length > 0) { 4204 substitutionData = substitutionProductsArray.map((product, index) => { 4205 const offersCount = substitutionObj?.isAutoClipEnabled && product.offers?.filter(offer => offer.isDisplayable)?.length || 0; 4206 return { 4207 isSubstitution: product.Subflag ? 'true' : 'false', 4208 productID: productID, 4209 substitutionOption: `${product.algoType === 'selected' ? 'past-choice' : modalAction === 'search-substitute' ? 'search-substitute' : 'substitute'}:${product?.id}`, 4210 searchTerm: searchTerm, 4211 searchResults: searchResults, 4212 rowSlot: `r${index + 1 > 9 ? index + 1 : '0' + (index + 1)}#s01`, 4213 isCouponAvailable: offersCount > 0 ? true : false, 4214 couponsCount: offersCount 4215 } 4216 }); 4217 } 4218 } 4219 4220 if (modalAction === 'substitute-with-next-best-match:save-substitution' && this.isCartAndCheckoutPage('cart')) { 4221 modalName = 'sub-pref'; 4222 substitutionData.push({ 4223 productID: productID, 4224 substitutionOption: 'next-best-match', 4225 }); 4226 } 4227 4228 AB.DATALAYER.pushEvent(
4229 this.EVENTS.MODAL_CLICK, 4230 { 4231 modal: { 4232 modalAction: `${modalName}|${modalAction}`, 4233 modalMessage: modalMessage || '' 4234 }, 4235 productInfo: { 4236 substitution: substitutionData 4237 }, 4238 page: digitalData?.pageDetails, 4239 pageFilter: digitalData?.pageFilter 4240 } 4241 ); 4242 digitalData.modal = {}; 4243 digitalData.productInfo = {}; 4244 } 4245 4246 this.setModalClickSubstitutionAllItems = function (modalAction, substitutionObj) { 4247 const allCartItems = substitutionObj.cartItems; 4248 const cartItemsSubs = []; 4249 allCartItems.forEach(cartItem => { 4250 const substitutions = cartItem.substituteData; 4251 const substitutionData = substitutions && Array.isArray(substitutions) && substitutions.length > 0 ? substitutions.map((product) => { 4252 return { 4253 isSubstitution: product.Subflag ? 'true' : 'false', 4254 productID: cartItem.id, 4255 substitutionOption: cartItem.substitutionPreferenceV2 == 'Do Not Substitute' ? 'dont-substitute' : cartItem.substitutionPreferenceV2 == 'Best Available' ? 'next-best-match' : `${product.algoType === 'selected' ? 'past-choice' : 'substitute'}:${product?.id}`, 4256 } 4257 })[0] : { 4258 productID: cartItem.id, 4259 substitutionOption: cartItem.substitutionPreferenceV2 == 'Do Not Substitute' ? 'dont-substitute' : cartItem.substitutionPreferenceV2 == 'Best Available' ? 'next-best-match' : '' 4260 }; 4261 cartItemsSubs.push(substitutionData); 4262 }); 4263 const payload = { 4264 modal: { 4265 modalAction: `sub-pref|${modalAction}` 4266 }, 4267 productInfo: { 4268 substitution: cartItemsSubs 4269 }, 4270 page: digitalData?.pageDetails, 4271 pageFilter: digitalData?.pageFilter 4272 }; 4273 AB.DATALAYER.pushEvent(this.EVENTS.MODAL_CLICK, payload); 4274 digitalData.modal = {}; 4275 digitalData.productInfo = {}; 4276 } 4277 4278 /** 4279 * Sets modal click events for Schedule & Save (AR) 4280 * @param modalAction 4281 * @param autoReplenishmentObj 4282 */ 4283 this.setModalClickEventsAR = function (modalAction, autoReplenishmentObj) { 4284 AB.DATALAYER.pushEvent( 4285 this.EVENTS.MODAL_CLICK, 4286 { 4287 modal: { 4288 modalAction: modalAction 4289 }, 4290 page: digitalData?.pageDetails, 4291 pageFilter: digitalData?.pageFilter, 4292 autoReplenishment: autoReplenishmentObj || undefined 4293 } 4294 ); 4295 } 4296 4297 /** 4298 * Sends BR widget obj for pixel 4299 * @param widgetObj 4300 */ 4301 this.pathwayWidgetView = function (widgetObj) { 4302 AB.DATALAYER.pushEvent( 4303 this.EVENTS.PATHWAY_WIDGET_VIEW, 4304 { 4305 productInfo: { 4306 pathwaysWidget: widgetObj 4307 }, 4308 page: digitalData?.pageDetails, 4309 pageFilter: digitalData?.pageFilter 4310 } 4311 ); 4312 } 4313 4314 /** 4315 * Banner clicks tracking - WeeklyAd page 4316 */ 4317 this.bannerClickWeeklyAd = function (adTitle, adPosition) { 4318 if (adTitle && adPosition) { 4319 // placement 4320 let bannerPlacement = ''; 4321 if (adPosition === 'weeklyadtop') { 4322 bannerPlacement = 'R01'; 4323 } else if (adPosition === 'weeklyadbottom') { 4324 bannerPlacement = 'R02'; 4325 } 4326 // link name 4327 const currentBannerTitle = 'bannerClicked|' + adTitle.replace(/\s/g, ''); 4328 4329 window.digitalData.pageFilter = window.digitalData.pageFilter || {}; 4330 4331 window.digitalData.pageFilter.placement = bannerPlacement; 4332 window.digitalData.pageFilter.recommendationType = 'sponsored'; 4333 window.digitalData.pageFilter.mediaType = 'banner'; 4334 window.digitalData.link = { 4335 linkName: currentBannerTitle 4336 }; 4337 4338 AB.DATALAYER.pushEvent( 4339 this.EVENTS.LINK_CLICK, 4340 { 4341 link: digitalData.link, 4342 page: digitalData?.pageDetails, 4343 pageFilter: digitalData?.pageFilter 4344 } 4345 ); 4346 } 4347 } 4348 4349 /** 4350 * Sets the link click event for the reviews tab 4351 */ 4352 this.setReviewsTabLinkClick = function () { 4353 if (window.analyticsReviewLinkClickFlag !== true) { 4354 const productID = sessionStorage.getItem('analyticsProductIdPDP') || '';
4355 AB.DATALAYER.setLinkClickCustomerReviews('pdp-page-reviews|reviews-tab', productID); 4356 } 4357 }; 4358 4359 /** 4360 * Sets the link click events for customer reviews on PDP 4361 * @param linkClicked 4362 * @param productID 4363 */ 4364 this.setLinkClickCustomerReviews = function (linkClicked, productID) { 4365 AB.DATALAYER.pushEvent( 4366 this.EVENTS.LINK_CLICK, 4367 { 4368 link: { 4369 linkName: linkClicked 4370 }, 4371 productInfo: { 4372 productID: productID, 4373 isProductTrackingDisabled: digitalData.pageFlags?.isProductTrackingDisabled 4374 }, 4375 page: digitalData?.pageDetails, 4376 pageFilter: digitalData?.pageFilter 4377 } 4378 ); 4379 } 4380 4381 /** 4382 * Sets the modal click events for customer reviews 4383 * @param modalAction 4384 * @param isPDP 4385 */ 4386 this.setModalClickCustomerReviews = function (modalAction, isPDP) { 4387 if (isPDP) { 4388 const productID = sessionStorage.getItem('analyticsProductIdPDP') || ''; 4389 AB.DATALAYER.pushEvent( 4390 this.EVENTS.MODAL_CLICK, 4391 { 4392 modal: { 4393 modalAction: modalAction 4394 }, 4395 productInfo: { 4396 productID: productID, 4397 isProductTrackingDisabled: digitalData?.pageFlags?.isProductTrackingDisabled 4398 }, 4399 page: digitalData?.pageDetails, 4400 pageFilter: digitalData?.pageFilter 4401 } 4402 ); 4403 if (window.digitalData && window.digitalData.modal) { 4404 window.digitalData.modal.modalAction = modalAction; 4405 } 4406 } else { 4407 AB.DATALAYER.setModalClickEvents(modalAction); 4408 } 4409 } 4410 4411 /** 4412 * Sets the modal view event for customer reviews modal on PDP 4413 * @param modalAction 4414 * @param productID 4415 */ 4416 this.setModalViewCustomerReviews = function (modalAction, productID) { 4417 if (window.digitalData && window.digitalData.modal) { 4418 window.digitalData.modal.modalAction = modalAction; 4419 } 4420 AB.DATALAYER.pushEvent( 4421 this.EVENTS.MODAL_VIEW, 4422 { 4423 modal: { 4424 modalAction: modalAction, 4425 }, 4426 productInfo: { 4427 productID: productID 4428 }, 4429 page: digitalData?.pageDetails, 4430 pageFilter: digitalData?.pageFilter 4431 } 4432 ); 4433 } 4434 4435 /** 4436 * Sets the link click event for cancel order confirmation 4437 * @param linkClicked 4438 * @param orderId 4439 */ 4440 this.setLinkClickCancelOrder = function (linkClicked, order) { 4441 const orderId = order?.orderID || ''; 4442 const revenueCancelled = order?.orderTotal || ''; 4443 const isMarketplaceOrder = order?.isMarketPlace; 4444 const isWineShop = order?.isWine; 4445 const isB2b = order?.isB2B; 4446 4447 AB.DATALAYER.pushEvent( 4448 this.EVENTS.LINK_CLICK, 4449 { 4450 link: { 4451 linkName: linkClicked 4452 }, 4453 transaction: { 4454 orderID: isMarketplaceOrder || isB2b ? orderId : isWineShop ? `wine-shop:${orderId}` : '', 4455 revenueCancelled: revenueCancelled 4456 }, 4457 page: digitalData?.pageDetails, 4458 pageFilter: digitalData?.pageFilter 4459 } 4460 ); 4461 } 4462 4463 this.setModalViewEventsSnsSubs = function (data) { 4464 if (data) { 4465 const substitutionOption = data.subData?.substitutionValue 4466 ? data.subData.substitutionValue.toLowerCase() === 'best available' ? 'best-available' : `substitute:${data.subData?.substituteItem?.[0]?.itemBpn}` 4467 : 'dont-substitute'; 4468 AB.DATALAYER.pushEvent( 4469 this.EVENTS.MODAL_VIEW, 4470 { 4471 modal: { 4472 modalAction: `${data.showSetSubstitution ? 'substitution' : 'edit-substitution'}|view`, 4473 modalMessage: substitutionOption === 'dont-substitute' ? 'substitution-dont-substitute' : '' 4474 }, 4475 productInfo: { 4476 substitution: { 4477 isSubstitution: substitutionOption === 'dont-substitute' ? 'false' : 'true', 4478 productID: data.productID || '', 4479 substitutionOption: substitutionOption 4480 } 4481 }, 4482 page: digitalData?.pageDetails, 4483 pageFilter: digitalData?.pageFilter 4484 } 4485 ); 4486 } 4487 } 4488 4489 this.setModalClickEventsSnsSubs = function (action, data) { 4490 if (data) { 4491 let modalMessage = ''; 4492 let substitutionObj = { 4493 productID: data.productID || '', 4494 isSubstitution: action === 'dont-substitute' ? 'false' : data.subData?.substituteItem?.length > 0 ? 'true' : 'false', 4495 substitutionOption: action === 'dont-substitute' ? 'dont-substitute' : action === 'substitute' ? `substitute:${data.selectedProduct?.itemBpn}` : '' 4496 }; 4497 if (action === 'search') { 4498 modalMessage = data.subData?.substituteItem ? `substitution-search-${data.searchTerm}` : `substitution-search-not-available-${data.searchTerm}`; 4499 substitutionObj.searchResults = data.subData?.substituteItem?.length || '0'; 4500 substitutionObj.searchTerm = data.searchTerm; 4501 if (!data.subData?.substituteItem) {
4502 action = 'not-available-search'; 4503 } 4504 } else if (action === 'substitute') { 4505 const index = data.subData?.substituteItem?.findIndex(item => item.itemBpn === data.selectedProduct?.itemBpn) + 1; 4506 substitutionObj.rowSlot = index ? `R0${index}#S01` : ''; 4507 modalMessage = `substitution-substitute-${substitutionObj.rowSlot}`; 4508 } 4509 4510 AB.DATALAYER.pushEvent( 4511 this.EVENTS.MODAL_CLICK, 4512 { 4513 modal: { 4514 modalAction: `${data?.showSetSubstitution ? 'substitution' : 'edit-substitution'}|${action}`, 4515 modalMessage: modalMessage 4516 }, 4517 productInfo: { 4518 substitution: substitutionObj 4519 }, 4520 page: digitalData?.pageDetails, 4521 pageFilter: digitalData?.pageFilter 4522 } 4523 ); 4524 } 4525 } 4526 4527 this.casReviewSubsModalEvents = function (item, status) { 4528 if (item && item.bpnNum) { 4529 const currentSubCard = document.querySelector(`a[data-bpn="${item.bpnNum}"]`)?.closest('.sub-card'); 4530 const currentSubItem = document.querySelector(`a[data-bpn="${item.bpnNum}"]`)?.closest('.sub-item-container'); 4531 const isSubPref = item?.replacements?.some(item => !!item.saveAsFutureSubPreference) || false; 4532 let modalMessage = ''; 4533 if (currentSubCard && currentSubItem) { 4534 const itemIndex = Array.from(currentSubCard.querySelectorAll('.sub-item-container'))?.indexOf(currentSubItem) + 1; 4535 modalMessage = `substitution-substitute-${itemIndex < 10 ? `0${itemIndex}` : itemIndex}#01`; 4536 } 4537 let substitutionObj = { 4538 productID: item.bpnNum, 4539 substitutionOption: item.replacements ? `substitute:${item.replacements.map(item => item.itemId)?.join(':')}` : '' 4540 }; 4541 if (isSubPref) { 4542 this.setAnalyticsCrossPageData({ action: 'set', name: 'analyticsFeatureTracking', data: 'casSubPrefACIP409006' }); 4543 } 4544 AB.DATALAYER.pushEvent( 4545 this.EVENTS.MODAL_CLICK, 4546 { 4547 modal: { 4548 modalAction: `cas-review-substitutions${isSubPref ? '-pref-sub-tag' : ''}|${status === 'APPROVED' ? 'approve' : 'decline'}`, 4549 modalMessage: modalMessage 4550 }, 4551 productInfo: { 4552 substitution: substitutionObj 4553 }, 4554 page: digitalData?.pageDetails, 4555 pageFilter: digitalData?.pageFilter 4556 } 4557 ); 4558 } 4559 } 4560 4561}).call(AB.DATALAYER); 4562 4563'use strict'; 4564 4565(function () { 4566 /** 4567 * Updates mapping for product info 4568 * @param infoObj 4569 * @param cart 4570 * @returns 4571 */ 4572 this.getProductInfo = function (infoObj, cart) { 4573 const units = infoObj.units || infoObj.qty || infoObj.quantity; 4574 const carouselSec = infoObj.carouselSection && ((infoObj.carouselSection?.toLowerCase()?.indexOf('picks for you') !== -1) || (infoObj.carouselSection?.toLowerCase()?.indexOf('picked for you') !== -1)) ? infoObj.carouselSection.trim().toLowerCase().split(' ').join('-').replace(/[^\w-,|#:]/gi, '').replace('--', '-') : infoObj.carouselSection; 4575 4576 let productInfo = { 4577 productID: infoObj.id || infoObj.itemId?.toString(), 4578 productName: infoObj.name, 4579 impressionAdditionalDetail: infoObj.adobeMetricA ? infoObj.adobeMetricA : this.isBundlesLP() ? digitalData.pageDetails?.abTestDetails : '', 4580 channel: infoObj.channel ? infoObj.channel : {}, 4581 fullName: infoObj.fullName, 4582 units: units, 4583 unitType: infoObj.unitType ? infoObj.unitType : infoObj.unitQuantity ? infoObj.unitQuantity : '', 4584 lbs: this.isCartAndCheckoutPage() ? infoObj.unitQuantity == 'LB' ? infoObj.selectedWeight : '0' : infoObj.lbs && infoObj.lbs !== '0' ? infoObj.lbs : '', 4585 each: this.isCartAndCheckoutPage() ? (infoObj.unitQuantity == 'EACH' ? units : '0') : (infoObj.unitType == 'EACH' ? infoObj.each : '0'), 4586 pricePerUnit: infoObj.pricePerUnit ? infoObj.pricePerUnit.toString() : infoObj.unitPrice ? infoObj.unitPrice.toString() : '', 4587 listPrice: this.isCartAndCheckoutPage() ? infoObj.price : infoObj.listPrice ? infoObj.listPrice : infoObj.basePrice?.toString(), 4588 totalPrice: infoObj.totalPrice ? infoObj.totalPrice.toString() : infoObj.basePrice && units ? (infoObj.basePrice * (units || 1))?.toString() : '0', 4589 carouselSection: carouselSec ? AB.DATALAYER.sanitizeAnalyticsText(carouselSec) : '', 4590 carouselSectionUncurated: infoObj.carouselSection, 4591 pathwaysWidget: infoObj.pathwaysWidget || '', 4592 basePrice: infoObj.basePrice, 4593 pricingType: infoObj.pricingType || infoObj.promoDescription || 'not available', 4594 couponFlag: infoObj.couponFlag || 'not available', 4595 couponClipped: infoObj.couponClipped || 'not available', 4596 couponID: infoObj.couponID || 'not available', 4597 aisleId: infoObj.aisleId, 4598 upc: infoObj.upc, 4599 pfmDetail: !this.isCartAndCheckoutPage() ? (infoObj.pfmDetail || digitalData.pageDetails.pfmDetail) : '', 4600 crossSellTitle: infoObj.crossSellTitle, 4601 isCrossSell: infoObj.isCrossSell, 4602 isFound: infoObj.isFound, 4603 mto: (!!infoObj.isMTO).toString(), 4604 departmentName: infoObj.departmentName || '', 4605 aisleName: infoObj.aisleName || '',
4606 shelfName: infoObj.shelfName || '', 4607 sellerId: this.isCartAndCheckoutPage() ? (cart?.seller?.sellerId ? cart?.seller.sellerId : cart?.cartCategory === '1P_WINE' ? '22222222' : '11111111') : (infoObj.sellerId || ''), 4608 sellerName: infoObj.sellerName || '', 4609 editOrderFlow: infoObj.editOrderFlow ? infoObj.editOrderFlow : this.isEditOrderMode() ? this.isEditOrderMode() : this.isCartAndCheckoutPage('edit/cart') ? this.isCartAndCheckoutPage('edit/cart') : false, 4610 isUndoClick: infoObj.isUndoClick || false, 4611 isBIACarousalProduct: infoObj.isBIACarousalProduct, 4612 snapEligible: !this.isCartAndCheckoutPage() ? infoObj.snapEligible === true ? 'true' : 'false' : '', 4613 isProductTrackingDisabled: !!infoObj.isProductTrackingDisabled, 4614 inventory: infoObj.isAvailable ? 'available' : 'not-available', 4615 placement: infoObj.placement, 4616 isP13nGrid: infoObj.isP13nGrid, 4617 scheduleandSaveSource: infoObj.scheduleandSaveSource, 4618 previousPrice: infoObj.previousPrice, 4619 isPriceDecreased: infoObj.isPriceDecreased, 4620 badges: infoObj.badges, 4621 isPreOrderItem: infoObj.isPreOrderItem, 4622 mealsPfmSource: infoObj.mealsPfmSource ? `${this.isMealsPlansPage() ? 'meals_' : ''}${infoObj.mealsPfmSource}` : undefined, 4623 isCustomCake: infoObj.isCustomCake, 4624 isOrderAmendsFlow: infoObj.isOrderAmendsFlow || false, 4625 containsAlcohol: infoObj.containsAlcohol, 4626 featured: infoObj.featured 4627 }; 4628 return productInfo; 4629 } 4630 4631 /** 4632 * Sets the product information 4633 * @param infoObj 4634 */ 4635 this.productInfo = function (infoObj, calledBy, cart) { 4636 if (digitalData && infoObj && Object.keys(infoObj).length > 0) { 4637 digitalData.productInfo = AB.DATALAYER.getProductInfo(infoObj, cart); 4638 digitalData.pageFilter.pfmSource = digitalData.pageFilter.pfmSource || infoObj.adobeMetricC; 4639 if (calledBy === 'product-detail') { 4640 digitalData.pageFlags = digitalData.pageFlags || {}; 4641 digitalData.pageFlags.isProductTrackingDisabled = !!digitalData?.productInfo?.isProductTrackingDisabled; 4642 AB.DATALAYER._bentoPdpPfmMirror = ''; 4643 } 4644 4645 if (this.isDFTAPage()) { 4646 if (infoObj.previousQty !== undefined) { 4647 digitalData.productInfo.previousQty = infoObj.previousQty; 4648 } else { 4649 this.setPreviousQuantityDFTA(digitalData.productInfo.productID); 4650 } 4651 } 4652 if (this.isCartAndCheckoutPage()) { 4653 digitalData.cart.cartInfo.currentCartId = cart?.cartId?.toString() || ''; 4654 if (infoObj.isArProduct && infoObj.snsSub && infoObj.snsSub.isSnsSub) { 4655 const snsFrequency = infoObj?.snsSub?.snsFrequency?.unit?.toLowerCase().replace(/\s/g, '-'); 4656 digitalData.productInfo.scheduleandSaveSource = `full-cart#subscribe#every-${snsFrequency}`; 4657 } else { 4658 digitalData.productInfo.scheduleandSaveSource = ''; 4659 } 4660 if (cart?.isWYSIWYGEnabled) { 4661 digitalData.productInfo = { ...digitalData.productInfo, ...AB.DATALAYER.setCartItemsWYSIWYG(infoObj, digitalData.productInfo?.sellerId, digitalData.cart?.cartInfo?.currentCartId) }; 4662 } else { 4663 digitalData.productInfo = { ...digitalData.productInfo, ...AB.DATALAYER.setCartItemsNonWYSIWYG(infoObj, digitalData.productInfo?.sellerId, digitalData.cart?.cartInfo?.currentCartId) }; 4664 } 4665 } 4666 } 4667 }; 4668 4669 // To remove after 8.1 release [CLEANUP] 4670 this.setProductInfoSavingsCnc = function (item) { 4671 if (item.savings) { 4672 let couponsClipped = 0, couponsApplied = 0, totalItemSavings = 0; 4673 let itemSavings = item.savings || []; 4674 let savings = AB.DATALAYER.calculateCartSavings(itemSavings, 'item'); 4675 let ccSavings = item.clubCardSaving && !isNaN(parseFloat(item.clubCardSaving)) ? parseFloat(item.clubCardSaving) : 0; 4676 let totalCCSavings = savings.clubcardSavings + ccSavings; 4677 let totalItemSavingsWithCC = savings.total + ccSavings; 4678 4679 digitalData.productInfo.j4uItemSavings = savings.j4uSavings; 4680 digitalData.productInfo.rewardsItemSavings = savings.rewardsSavings; 4681 digitalData.productInfo.employeeItemSavings = savings.employeeSavings; 4682 digitalData.productInfo.promocodeItemSavings = savings.promoCodeSavings; 4683 digitalData.productInfo.clubcardItemSavings = totalCCSavings; 4684 totalItemSavings = totalItemSavingsWithCC; 4685 4686 let offerTypeArray = []; 4687 let offerTextArray = []; 4688 if (itemSavings.length > 0) {
4689 itemSavings.forEach(offer => { 4690 let externalOfferId = offer.externalOfferId || ''; 4691 let isNDOffer = externalOfferId.indexOf('-ND') !== -1; 4692 if (offer.categoryName) { 4693 offerTypeArray.push(`${isNDOffer ? 'ND:' : ''}${offer.categoryName}`); 4694 } 4695 if (offer.displayOfferTxt) { 4696 offerTextArray.push(`${isNDOffer ? 'ND:' : ''}${offer.displayOfferTxt}`); 4697 } else if (offer.linkPluNumber && offer.source === 'CMS') { 4698 offerTextArray.push(`${isNDOffer ? 'ND:' : ''}Buy 1 Get 1 Free`); 4699 } else if (offer.discountMessage) { 4700 offerTextArray.push(`${isNDOffer ? 'ND:' : ''}${offer.discountMessage}`); 4701 } 4702 }); 4703 } 4704 4705 if (item.clipped) { 4706 item.clipped.forEach(e => { couponsClipped++; }); 4707 } 4708 digitalData.productInfo.offerText = offerTextArray.join('#'); 4709 digitalData.productInfo.offerType = offerTypeArray.join('#'); 4710 digitalData.productInfo.totalItemSavings = totalItemSavings; 4711 4712 digitalData.productInfo.itemPrice = this.cleanPrice(((item.basePrice * item.deltaQty) - totalItemSavings) / item.deltaQty); 4713 digitalData.productInfo.totalPrice = this.cleanPrice((item.basePrice * item.deltaQty) - totalItemSavings); 4714 4715 digitalData.productInfo.couponFlag = ''; 4716 digitalData.productInfo.couponClipped = couponsClipped; 4717 digitalData.productInfo.couponApplied = couponsApplied; 4718 4719 digitalData.productInfo = this.toStringAll(digitalData.productInfo); 4720 } 4721 } 4722 4723 /** 4724 * sets the pfm detail 4725 * (product finding method) 4726 * the templates for pfmDetail are: 4727 * subSection1:subSection2:subSection3:subSection4 4728 * subSection1:subSection2:subSection3:subSection4:carouselSection 4729 * subSection1:search-term 4730 * subSection1:search-term:filterSelection 4731 * @param pfmDetail 4732 */ 4733 this.setPfmDetail = function (pfmDetail) { 4734 if (digitalData && digitalData.productInfo) { 4735 digitalData.productInfo.pfmDetail = pfmDetail; 4736 } 4737 }; 4738 4739 /** 4740 * Sets localStorage items pfmDetail and pfmDetailSearch to be used on PDP init 4741 */ 4742 this.setLsPfmDetailOnInit = function () { 4743 if (this.isSearchResultsPage()) { 4744 this.setLSItem('pfmDetail', 'search-results'); 4745 const urlParams = AB.DATALAYER.analyticsGetURLParameterByName('q'); 4746 if (urlParams) { 4747 this.setLSItem('pfmDetailSearch', urlParams); 4748 } 4749 } else { 4750 localStorage.setItem('pfmDetail', this.pathSections.join(':')); 4751 this.removeLSItem('pfmDetailSearch'); 4752 } 4753 } 4754 4755 // Set product placement in grid and cross-sell placement 4756 this.setProductGridPosition = function ({ productId, eventName, nativeElement } = {}) { 4757 if (digitalData) { 4758 let placement = ''; 4759 if (document.getElementById('miniCart')?.style.visibility !== 'visible') { 4760 let productPosition = ''; 4761 const currentProductId = productId ? productId : digitalData.productInfo.productID; 4762 const isCrossSell = digitalData.productInfo.isCrossSell; 4763 4764 // recipes detail page 4765 if (AB.DATALAYER.isRecipesPage()) { 4766 const currentProductRecipe = document.querySelector(`.recipe-details-card #pg${currentProductId}`)?.closest('.recipe-card-pad'); 4767 const currentGrid = currentProductRecipe?.closest('meals-product-grid'); 4768 const allProducts = Array.from(currentGrid?.querySelectorAll('.recipe-card-pad')); 4769 const productPosition = allProducts ? allProducts.indexOf(currentProductRecipe) + 1 : 0; 4770 4771 // main products 4772 const mainProducts = Array.from(document.querySelectorAll('.main-products .recipe-card-pad')); 4773 const mainProductPosition = mainProducts ? mainProducts.indexOf(currentProductRecipe) + 1 : 0; 4774 const mainProductsCount = mainProducts?.length; 4775 4776 // pantry products 4777 const pantryProducts = Array.from(document.querySelectorAll('.pantry-products .recipe-card-pad')); 4778 const pantryProductPosition = pantryProducts ? pantryProducts.indexOf(currentProductRecipe) + 1 : 0; 4779 4780 // set pageFilter.mediaType 4781 if (digitalData.pageFilter) { 4782 digitalData.pageFilter.mediaType = 'product-grid'; 4783 } 4784 4785 // determine product row 4786 let productRow = 0; 4787 if (window.innerWidth >= 992) { 4788 // 4 products/row 4789 if (mainProductPosition > 0) { 4790 productRow = mainProductPosition <= 4 ? 1 : Math.ceil(mainProductPosition / 4); 4791 } else { 4792 const mainProductsRows = Math.ceil(mainProductsCount / 4); 4793 const pantryProductRow = pantryProductPosition <= 4 ? 1 : Math.ceil(pantryProductPosition / 4); 4794 productRow = mainProductsRows + pantryProductRow; 4795 } 4796 } else if (window.innerWidth >= 768 && window.innerWidth < 992) { 4797 // 3 products/row 4798 if (mainProductPosition > 0) { 4799 productRow = mainProductPosition <= 3 ? 1 : Math.ceil(mainProductPosition / 3); 4800 } else { 4801 const mainProductsRows = Math.ceil(mainProductsCount / 3); 4802 const pantryProductRow = pantryProductPosition <= 3 ? 1 : Math.ceil(pantryProductPosition / 3); 4803 productRow = mainProductsRows + pantryProductRow; 4804 } 4805 } else { 4806 // 1 product/row 4807 productRow = productPosition; 4808 } 4809 // position to be displayed 4810 const displayableSPosition = productPosition < 10 ? `0${productPosition}` : productPosition; 4811 const displayablePGPosition = productRow < 10 ? `0${productRow}` : productRow; 4812 placement = `R${displayablePGPosition}#S${displayableSPosition}`; 4813 4814 } else { 4815 let currentProduct; 4816 let currentGrid; 4817 let gridProducts; 4818 let productRow; 4819 4820 currentProduct = isCrossSell 4821 ? document.querySelector(`cross-sell product-item .product-item-${currentProductId}, cross-sell product-item-v2 .product-item-${currentProductId}, cross-sell product-item-al-v1 .product-item-${currentProductId}, cross-sell product-item-al-v2 div[data-bpn='${currentProductId}']`)?.closest('product-item, product-item-v2, product-item-al-v1, product-item-al-v2') 4822 : document.querySelector(`product-item-al-v1 .product-item-${currentProductId}, product-item .product-item-${currentProductId}, product-item-v2 .product-item-${currentProductId}, product-item-al-v2 div[data-bpn='${currentProductId}']`)?.closest('product-item-al-v1, product-item, product-item-v2, product-item-al-v2'); 4823 4824 if (currentProduct) { 4825 let isNewDesign = currentProduct.localName === 'product-item-al-v2'; 4826 if (digitalData.pageFilter) { 4827 if (isCrossSell) { 4828 digitalData.pageFilter.mediaType = 'product-carou
4828sel'; 4829 currentGrid = currentProduct.closest('cross-sell'); 4830 gridProducts = Array.from(currentGrid?.querySelectorAll('product-item, product-item-v2, product-item-al-v1, product-item-al-v2')); 4831 } else { 4832 // Placement for grid products 4833 digitalData.pageFilter.mediaType = 'product-grid'; 4834 currentGrid = currentProduct.closest('product-generic-grid-al-v1, search-grid, product-generic-grid, buy-it-again-grid'); 4835 gridProducts = Array.from(currentGrid?.querySelectorAll('product-item-al-v1, product-item, product-item-v2, product-item-al-v2')); 4836 } 4837 } 4838 productPosition = gridProducts ? gridProducts.indexOf(currentProduct) + 1 : 0; 4839 productRow = productPosition < 4 ? 1 : Math.ceil(productPosition / 4); 4840 if (window.innerWidth < 768 && !isCrossSell) { 4841 productRow = productPosition < 2 ? 1 : Math.ceil(productPosition / 2); 4842 } 4843 4844 if (isNewDesign && !isCrossSell) { 4845 if (window.innerWidth < 470) { 4846 productRow = productPosition < 2 ? 1 : Math.ceil(productPosition / 2); 4847 } else if (window.innerWidth >= 470 && window.innerWidth < 616) { 4848 productRow = productPosition < 3 ? 1 : Math.ceil(productPosition / 3); 4849 } else if ((window.innerWidth >= 762 && window.innerWidth < 908) || (window.innerWidth >= 1019 && window.innerWidth < 1214)) { 4850 productRow = productPosition < 5 ? 1 : Math.ceil(productPosition / 5); 4851 } else if ((window.innerWidth >= 908 && window.innerWidth < 992) || (window.innerWidth >= 1214 && window.innerWidth < 1280)) { 4852 productRow = productPosition < 6 ? 1 : Math.ceil(productPosition / 6); 4853 } 4854 } 4855 4856 const displayableSPosition = productPosition < 10 ? `0${productPosition}` : productPosition; 4857 const displayablePGPosition = productRow < 10 ? `0${productRow}` : productRow; 4858 placement = `R${displayablePGPosition}#S${displayableSPosition}`; 4859 4860 } 4861 this.trackAEMZone(eventName, nativeElement ? nativeElement : currentProduct, 'product-grid'); 4862 } 4863 if (digitalData.pageFilter) { 4864 digitalData.pageFilter.placement = placement; 4865 } 4866 } 4867 return placement; 4868 } 4869 } 4870 4871 // Set product placement in carousel and carousel position in page 4872 this.setCarouselPosition = function ({ productId, carouselSection, eventName, nativeElement } = {}) { 4873 if (digitalData) { 4874 let placement = ''; 4875 const currentCarouselSection = carouselSection ? carouselSection : digitalData.productInfo?.carouselSectionUncurated; 4876 const currentProductId = productId ? productId : digitalData.productInfo?.productID; 4877 let currentDepartment = digitalData.productInfo?.departmentName; 4878 const isCrossSell = digitalData.productInfo?.isCrossSell; 4879 const isSpImpFixOn = !window?.SWY?.CONFIGSERVICE?.featureToggleConfig?.isSponsoredImpressionFixDisabled; 4880 const carouselsAvailable = digitalData.pageDetails?.subSection3 === 'category-view' 4881 ? this.isVisible(document.querySelectorAll('.bia-carousel-list-item')) 4882 : this.isVisible(document.querySelectorAll(isSpImpFixOn ? '.slick-product-carousel, .master-product-carousel' : '.slick-product-carousel')); 4883 let currentCarouselParent = carouselsAvailable && Array.from(carouselsAvailable).filter((element) => { 4884 return (this.isDFTAPage() && element?.previousElementSibling?.textContent?.trim() === currentCarouselSection) 4885 || (isCrossSell && element?.querySelector(`a[data-bpn="${currentProductId}"]`)) 4886 || (currentCarouselSection && (element?.querySelector(`lazy-wrapper[data-carousel-title="${currentCarouselSection}"]`) || 4887 element?.querySelector('.carousel-header-text')?.textContent?.trim() === currentCarouselSection || 4888 element?.previousElementSibling?.textContent?.trim() === currentCarouselSection) || element?.querySelector('.carousel-nav span.carousel-nav__header-title')?.textContent?.trim() === currentCarouselSection) 4889 || (currentDepartment && element?.querySelector('h2')?.textContent?.trim()?.includes(`${currentDepartment}`)); 4890 }); 4891 currentCarouselParent = currentCarouselParent[0]; 4892 if ((!currentCarouselParent || (currentCarouselParent && Array.isArray(currentCarouselParent) && currentCarouselParent.length === 0)) && nativeElement && nativeElement instanceof Element) { 4893 currentCarouselParent = nativeElement.closest(isSpImpFixOn ? '.master-product-carou
4893sel, .slick-product-carousel' : '.slick-product-carousel'); 4894 } 4895 4896 const currentProduct = currentCarouselParent && currentCarouselParent.querySelector(`[data-bpn="${currentProductId}"]`)?.closest('product-carousel-item, product-carousel-item-v2, product-carousel-item-al-v1, product-carousel-item-al-v2'); 4897 let displayableCRPosition; 4898 let productsInCarousel; 4899 let displayableSPosition; 4900 if (isSpImpFixOn) { 4901 const carouselPosition = carouselsAvailable && Array.from(carouselsAvailable).indexOf(currentCarouselParent) + 1; 4902 const safeCarouselPosition = Number.isFinite(carouselPosition) && carouselPosition > 0 ? carouselPosition : 0; 4903 displayableCRPosition = safeCarouselPosition < 10 ? `0${safeCarouselPosition}` : safeCarouselPosition; 4904 productsInCarousel = currentCarouselParent?.querySelectorAll('product-carousel-item, product-carousel-item-v2, product-carousel-item-al-v1, product-carousel-item-al-v2'); 4905 if (digitalData.productInfo) { 4906 digitalData.productInfo.carouselSize = productsInCarousel?.length; 4907 } 4908 const productPositionCarousel = productsInCarousel && Array.from(productsInCarousel).indexOf(currentProduct) + 1; 4909 const safeProductPosition = Number.isFinite(productPositionCarousel) && productPositionCarousel > 0 ? productPositionCarousel : 0; 4910 displayableSPosition = safeProductPosition < 10 ? `0${safeProductPosition}` : safeProductPosition; 4911 } else { 4912 const carouselPosition = carouselsAvailable && Array.from(carouselsAvailable).indexOf(currentCarouselParent) + 1; 4913 displayableCRPosition = carouselPosition < 10 ? `0${carouselPosition}` : carouselPosition; 4914 productsInCarousel = currentCarouselParent?.querySelectorAll('product-carousel-item, product-carousel-item-v2, product-carousel-item-al-v1, product-carousel-item-al-v2'); 4915 if (digitalData.productInfo) { 4916 digitalData.productInfo.carouselSize = productsInCarousel?.length; 4917 } 4918 const productPositionCarousel = productsInCarousel && Array.from(productsInCarousel).indexOf(currentProduct) + 1; 4919 displayableSPosition = productPositionCarousel < 10 ? `0${productPositionCarousel}` : productPositionCarousel; 4920 } 4921 placement = `R${displayableCRPosition}#S${displayableSPosition}`; 4922 if (digitalData.pageFilter) { 4923 digitalData.pageFilter.placement = placement; 4924 digitalData.pageFilter.mediaType = 'product-carousel'; 4925 } 4926 this.trackAEMZone(eventName, nativeElement ? nativeElement : currentProduct, currentCarouselSection); 4927 return placement; 4928 } 4929 } 4930 4931 this.setImpressionGridPosition = function ({ productId } = {}) { 4932 let placement; 4933 let productRow; 4934 let productPosition; 4935 const products = digitalData?.gridImpressionsProducts; 4936 4937 productPosition = products ? products.indexOf(productId) + 1 : 0; 4938 productRow = productPosition < 4 ? 1 : Math.ceil(productPosition / 4); 4939 if (window.innerWidth < 768) { 4940 productRow = productPosition < 2 ? 1 : Math.ceil(productPosition / 2); 4941 } 4942 4943 // TODO: This check is for Target flag for new product cards design; it needs to be removed once flag is disabled and new product cards are enabled globally 4944 if (window.analyticsSwy?.testFlags?.ACIP248676 === 'b' || AB.DATALAYER.getLSItem('abACIP248676') === 'b') { 4945 if (window.innerWidth < 470) { 4946 productRow = productPosition < 2 ? 1 : Math.ceil(productPosition / 2); 4947 } else if (window.innerWidth >= 470 && window.innerWidth < 616) { 4948 productRow = productPosition < 3 ? 1 : Math.ceil(productPosition / 3); 4949 } else if ((window.innerWidth >= 762 && window.innerWidth < 908) || (window.innerWidth >= 1019 && window.innerWidth < 1214)) { 4950 productRow = productPosition < 5 ? 1 : Math.ceil(productPosition / 5); 4951 } else if ((window.innerWidth >= 908 && window.innerWidth < 992) || (window.innerWidth >= 1214 && window.innerWidth < 1280)) { 4952 productRow = productPosition < 6 ? 1 : Math.ceil(productPosition / 6); 4953 } 4954 } 4955 4956 // position to be displayed 4957 const displayableSPosition = productPosition < 10 ? `0${productPosition}` : productPosition; 4958 const displayablePGPosition = productRow < 10 ? `0${productRow}` : productRow; 4959 placement = `R${displayablePGPosition}#S${displayableSPosition}`; 4960 4961 return placement; 4962 } 4963 4964 this.updatePfmDetail = function (filter) { 4965 if (digitalData?.productInfo) { 4966 digitalData.productInfo.pfmDetail = `${AB.DATALAYER.getLSItem('pfmDetail')}:${filter}`; 4967 } 4968 }; 4969 4970 /** 4971 * Updates cart items and subtotal when an item is removed from cart 4972 * @param removedItem 4973 */ 4974 this.removeFromCart = function (removedItem) { 4975 if (digitalData?.cart && removedItem) { 4976 digitalData.cart.absItems = digitalData.cart.absItems && digitalData.cart.absItems.filter((item) => { 4977 return item.productName !== removedItem.name; 4978 }); 4979 digitalData.cart.item = digitalData.cart.item && digitalData.cart.item.filter((item) => { 4980 return item.productName !== removedItem.name; 4981 }); 4982 let subTotal = 0;
4983 digitalData.cart.absItems.forEach(item => { 4984 subTotal += Number(item.totalPrice); 4985 }); 4986 if (digitalData.cart.transaction) { 4987 digitalData.cart.transaction.subTotal = subTotal; 4988 } 4989 } 4990 }; 4991 4992 /** 4993 * Handles add to cart event 4994 * Sets event name, pfmDetail 4995 * @param product 4996 * @param cartId 4997 * @param source 4998 * @param deltaQty 4999 * @param isUndoClick 5000 */ 5001 this.addItemToCart = function (product, cartId, source, cart, deltaQty, isUndoClick = false) { 5002 if (this.isCartAndCheckoutPage() && !this.isCartAndCheckoutPage('erums/cart/dnf') && product) { 5003 digitalData.pageFilter.carousel = 'shop|full_cart'; 5004 product.deltaQty = deltaQty && deltaQty < 0 ? Math.abs(deltaQty) : deltaQty; 5005 product.isUndoClick = isUndoClick; 5006 product.sellerId = cart?.seller?.sellerId ? cart?.seller?.sellerId : cart?.cartCategory === '1P_WINE' ? '22222222' : AB.DATALAYER.getBannerSellerId(); 5007 this.updateCart(cart, product); 5008 if (localStorage.getItem('grs-search-attribution-token')) { 5009 localStorage.removeItem('grs-search-attribution-token'); 5010 } 5011 } 5012 if (window.location?.pathname?.startsWith('/rewards/point-reward-details')) { 5013 AB.DATALAYER.setLinkClickEvents('cta:rewards-dashboard|link|rewards:add'); 5014 } 5015 if (AB.DATALAYER.isARManagePage()) { 5016 AB.DATALAYER.snsProdAction('cart_add'); 5017 } else { 5018 this.handleItemEvent('cart_add', product, cartId, source, cart); 5019 } 5020 this.updateJourneyStatus(this.Flag.PRODUCT_ADDED); 5021 }; 5022 5023 /** 5024 * Handles remove from cart event 5025 * Sets event name, pfmDetail 5026 * @param product 5027 * @param cart 5028 */ 5029 this.removeItemFromCart = function (product, cart) { 5030 if (this.isCartAndCheckoutPage() && !this.isCartAndCheckoutPage('erums/cart/dnf') && product) { 5031 digitalData.pageFilter.carousel = 'shop|full_cart'; 5032 product.sellerId = cart?.seller?.sellerId ? cart?.seller?.sellerId : cart?.cartCategory === '1P_WINE' ? '22222222' : AB.DATALAYER.getBannerSellerId(); 5033 this.updateCart(cart); 5034 } 5035 if (AB.DATALAYER.isARManagePage()) { 5036 AB.DATALAYER.snsProdAction('remove_cart'); 5037 } else { 5038 this.handleItemEvent('remove_cart', product, undefined, undefined, cart); 5039 } 5040 }; 5041 5042 /** 5043 * Handles increased cart quantity event 5044 * Sets event name, pfmDetail 5045 * @param product 5046 * @param cart 5047 * @param deltaQty 5048 * @param isUndoClick 5049 */ 5050 this.increaseItemQuantity = function (product, cart, deltaQty, isUndoClick = false) { 5051 if (this.isCartAndCheckoutPage() && !this.isCartAndCheckoutPage('erums/cart/dnf') && product) { 5052 digitalData.pageFilter.carousel = 'shop|full_cart'; 5053 product.isUndoClick = isUndoClick; 5054 product.deltaQty = deltaQty; 5055 product.sellerId = cart?.seller?.sellerId ? cart?.seller?.sellerId : cart?.cartCategory === '1P_WINE' ? '22222222' : AB.DATALAYER.getBannerSellerId(); 5056 this.updateCart(cart); 5057 } 5058 if (AB.DATALAYER.isARManagePage()) { 5059 AB.DATALAYER.snsProdAction('add_quantity'); 5060 } else { 5061 this.handleItemEvent('add_quantity', product, undefined, undefined, cart); 5062 } 5063 }; 5064 5065 /** 5066 * Handles decreased cart quantity event 5067 * Sets event name, pfmDetail 5068 * @param product 5069 * @param cart 5070 * @param deltaQty 5071 */ 5072 this.decreaseItemQuantity = function (product, cart, deltaQty) { 5073 if (this.isCartAndCheckoutPage() && !this.isCartAndCheckoutPage('erums/cart/dnf') && product) { 5074 digitalData.pageFilter.carousel = 'shop|full_cart'; 5075 product.deltaQty = deltaQty; 5076 product.sellerId = cart?.seller?.sellerId ? cart?.seller?.sellerId : cart?.cartCategory === '1P_WINE' ? '22222222' : AB.DATALAYER.getBannerSellerId(); 5077 this.updateCart(cart); 5078 } 5079 if (AB.DATALAYER.isARManagePage()) { 5080 AB.DATALAYER.snsProdAction('remove_quantity'); 5081 } else { 5082 this.handleItemEvent('remove_quantity', product, undefined, undefined, cart); 5083 } 5084 }; 5085 5086 // To remove after 8.1 release [CLEANUP] 5087 this.getCurrentItem = function (cart, itemFromCart, deltaQty, isUndoClick) { 5088 let currentItemId = itemFromCart?.itemId; 5089 let cartItems = cart?.cartItemsList || cart?.orderItems || []; 5090 let itemObj = {};
5091 cartItems.forEach((product) => { 5092 if (product.itemId?.toString() === currentItemId?.toString()) { 5093 itemObj = product; 5094 } 5095 }); 5096 itemObj.deltaQty = deltaQty && deltaQty < 0 ? Math.abs(deltaQty) : deltaQty; 5097 itemObj.isUndoClick = isUndoClick; 5098 return itemObj; 5099 }; 5100 5101 /** 5102 * Handles cart item events (add, remove, increase or decrease quantity) 5103 * Sets event name, pfmDetail 5104 * @param eventName 5105 * @param product 5106 */ 5107 this.handleItemEvent = function (eventName, product, cartId, source, cart) { 5108 if (AB.DATALAYER.isAlcoholAdTrackingDisabled(product || digitalData.productInfo)) { 5109 return; 5110 } 5111 if (digitalData) { 5112 if (product) { 5113 this.productInfo(product, undefined, cart); 5114 } 5115 if (digitalData.productInfo && (!digitalData.productInfo.sellerId || digitalData.productInfo.sellerId === `${AB.DATALAYER.getBannerSellerId()}`) && !AB.DATALAYER.isOrderAheadPage()) { 5116 this.martechProductAction(eventName, digitalData.productInfo); 5117 } 5118 window['analyticsSellerIds'] = window['analyticsSellerIds'] || []; 5119 this.setCarouselValueDFTA(eventName, digitalData.productInfo); 5120 if (eventName === 'cart_add') { 5121 if (!this.isRecipesAndMealsPage()) { 5122 this.setPfmSourceCategories(); 5123 this.setP13nData(); 5124 this.similarItemsBiaAddToCart(); 5125 this.biaCarousalProductPDPAddToCart(); 5126 if (digitalData.pageFilter?.mediaType === 'product-carousel' || digitalData.productInfo?.carouselSection) { 5127 this.setPastPurchasedImpressions(digitalData.pageFilter?.recommendationType === 'pastPurchased' ? 'buyItAgainCarousel' : ''); 5128 } 5129 } else { 5130 digitalData.pageFilter.pfmSource = source || ''; 5131 digitalData.martech.cartId = cartId || '' 5132 digitalData.cart.cartInfo.cartId = cartId || '' 5133 } 5134 if (AB.DATALAYER.isPdp()) { 5135 const snsCheckbox = document.querySelector('.product-info[data-pagetype="pdp"] .sns-container input.checkbox__input'); 5136 if (snsCheckbox?.checked) { 5137 const subscriptionValue = `pdp#subscribe#${digitalData.crossPageData?.snsAnalyticsFrequency?.data && digitalData.crossPageData?.snsAnalyticsFrequency?.data[digitalData.productInfo?.productID]}`; 5138 digitalData.productInfo.scheduleandSaveSource = subscriptionValue; 5139 this.setSnsProductInStorage(digitalData.productInfo?.productID); 5140 } 5141 if(product?.isCustomCake) { 5142 let itemObj = cart.cartItems.find((item) => { 5143 return item.itemId?.toString() === product.id?.toString(); 5144 }); 5145 if(itemObj && itemObj.options && itemObj.options.length > 0) { 5146 digitalData.modal.modalMessage = `pdp-ck-add|${itemObj.options.map(option => option.name).join('|')}`; 5147 } 5148 digitalData.pageFilter.topNav = `cta:${digitalData.page?.previousPageName}|link|customise`; 5149 } 5150 } 5151 if (digitalData.crossPageData?.recipeRelatedPfm && digitalData.crossPageData.recipeRelatedPfm[digitalData.productInfo?.productID]) { 5152 digitalData.productInfo.pfmDetail = digitalData.crossPageData.recipeRelatedPfm[digitalData.productInfo.productID]; 5153 } 5154 if (product?.aiProductType === 'recipe' && product?.attributes?.recipeId && digitalData.crossPageData.aiAssistantRecipePFM && digitalData.crossPageData.aiAssistantRecipePFM[product?.attributes?.recipeId]) { 5155 digitalData.productInfo.pfmDetail = digitalData.crossPageData.aiAssistantRecipePFM[product?.attributes?.recipeId]; 5156 } else if (digitalData.crossPageData?.aiAssistantProductPFM && digitalData.crossPageData.aiAssistantProductPFM[digitalData.productInfo?.productID]) { 5157 digitalData.productInfo.pfmDetail = digitalData.crossPageData.aiAssistantProductPFM[digitalData.productInfo.productID]; 5158 delete digitalData.crossPageData.aiAssistantProductPFM[digitalData.productInfo.productID]; 5159 } else if (product?.aiProductType && !(digitalData.crossPageData?.aiAssistantProductPFM && digitalData.crossPageData.aiAssistantProductPFM[digitalData.productInfo?.productID]) && !(product?.attributes?.recipeId && digitalData.crossPageData.aiAssistantRecipePFM && digitalData.crossPageData.aiAssistantRecipePFM[product?.attributes?.recipeId])) { 5160 digitalData.productInfo.pfmDetail = `${AB.DATALAYER.pathSections?.join(':')}#added-by-ai-assistant#non-search#not-recommended#R00#S00#no-badge`; 5161 } 5162 } else { 5163 if (digitalData.productInfo.isBIACarousalProduct) { 5164 digitalData.productInfo.impressionAdditionalDetail = ''; 5165 digitalData.pageFilter.pfmSource = ''; 5166 } 5167 } 5168 digitalData.pageFilter.placement = ''; 5169 5170 !this.isRecipesAndMealsPage() && !this.isAssistantPage() && this.setPfmDetailOnProductAction(eventName); 5171 if (digitalData.cart.cartInfo) { 5172 digitalData.cart.cartInfo.currentCartId = window.digitalData.cart.cartInfo.cartIDMap && window.digitalData.cart.cartInfo.cartIDMap[digitalData.productInfo.sellerId] ? window.digitalData.cart.cartInfo.cartIDMap[digitalData.productInfo.sellerId].toString() : ''; 5173 } 5174 5175 // Handles persistent pfm tracking - used on order confirmation, updated across pages 5176 if (digitalData.productInfo.sellerId && digitalData.productInfo.sellerId !== `${AB.DATALAYER.getBannerSellerId()}` && digitalData.productInfo.sellerId !== '22222222') { 5177 let sellerPFM = `${digitalData.productInfo.sellerId}#${digitalData.productInfo.pfmDetail}`; 5178 window.digitalData.cart.marketplace = window.digitalData.cart.marketplace || {}; 5179 if (Object.keys(window.digitalData.cart.marketplace).length === 0 && localStorage.getItem('analyticsSellerCartsInfo') && localStorage.getItem('analyticsSellerCartsInfo') !== 'undefined') { 5180 window.digitalData.cart.marketplace = AB.DATALAYER.safeJSONParse(AB.DATALAYER.getLSItem('analyticsSellerCartsInfo')); 5181 } 5182 5183 for (let i = 1; i <= 8; i++) { 5184 if (digitalData.cart.marketplace[`seller${i}`] && digitalData.cart.marketplace[`seller${i}`].sellerId === digitalData.productInfo.sellerId) { 5185 if (eventName === 'cart_add') { 5186 digitalData.cart.marketplace[`seller${i}`].sellerPfm = digitalData.cart.marketplace[`seller${i}`].sellerPfm || {}; 5187 digitalData.cart.marketplace[`seller${i}`].sellerPfm[`${digitalData.productInfo.productID}`] = sellerPFM; 5188 break; 5189 } else if (eventName === 'remove_cart' && digitalData.cart.marketplace[`seller${i}`].sellerPfm[`${digitalData.productInfo.productID}`]) { 5190 delete digitalData.cart.marketplace[`seller${i}`].sellerPfm[`${digitalData.productInfo.productID}`]; 5191 if (digitalData.cart.marketplace[`seller${i}
5191`].sellerPfm && Object.keys(digitalData.cart.marketplace[`seller${i}`].sellerPfm)?.length === 0) { 5192 delete digitalData.cart.marketplace[`seller${i}`]; 5193 } 5194 } 5195 } 5196 } 5197 5198 localStorage.setItem('analyticsSellerCartsInfo', JSON.stringify(window.digitalData.cart.marketplace)); 5199 } 5200 5201 if (eventName === 'remove_cart') { 5202 if (digitalData.cart.cartInfo) { 5203 const currentProduct = digitalData.cart.item.find(item => { 5204 return item.productID == digitalData.productInfo.productID; 5205 }); 5206 if (currentProduct) { 5207 digitalData.cart.cartInfo.currentCartId = currentProduct?.productCartId?.toString(); 5208 } 5209 digitalData.productInfo.sellerId = digitalData.productInfo.sellerId || currentProduct?.productSellerId?.toString(); 5210 digitalData.cart.item = digitalData.cart.item.filter(item => { 5211 return item.productID != digitalData.productInfo.productID; 5212 }); 5213 } 5214 if (window.digitalData.cart.sellerCarts && window.digitalData.cart.sellerCarts[`sellerItem${digitalData.productInfo.sellerId}`] && window.digitalData.cart.sellerCarts[`sellerItem${digitalData.productInfo.sellerId}`].length === 1) { 5215 delete digitalData.cart.sellerCarts[`sellerItem${digitalData.productInfo.sellerId}`]; 5216 digitalData.cart.item = digitalData.cart.item.filter(item => { 5217 return item.productID !== digitalData.productInfo.productID; 5218 }); 5219 if (digitalData.productInfo.sellerId === '22222222') { 5220 digitalData.cart.transaction.subTotalWineCart = digitalData.cart.transaction.subTotalWineCart || {}; 5221 digitalData.cart.transaction.subTotalWineCart.subTotal = 0; 5222 digitalData.cart.transaction.subTotalWineCart.sellerId = '22222222'; 5223 } else { 5224 digitalData.cart.transaction.subTotalMarketplaceCarts = digitalData.cart.transaction.subTotalMarketplaceCarts || ''; 5225 const splitMarketplaceCarts = digitalData.cart.transaction.subTotalMarketplaceCarts.split('#'); 5226 let newMarketplaceCarts = []; 5227 splitMarketplaceCarts.forEach(cartInfo => { 5228 let id = cartInfo.split(':')[0]; 5229 let newCartInfo = []; 5230 if (id == digitalData.productInfo?.sellerId) { 5231 newCartInfo = [id, 0].join(':'); 5232 } else { 5233 newCartInfo = cartInfo; 5234 }; 5235 newMarketplaceCarts.push(newCartInfo); 5236 }) 5237 digitalData.cart.transaction.subTotalMarketplaceCarts = newMarketplaceCarts.join('#'); 5238 } 5239 window['analyticsSellerIds'].splice(window['analyticsSellerIds'].indexOf(digitalData.productInfo.sellerId), 1); 5240 } 5241 if (digitalData.cart.absItems && digitalData.cart.absItems.length === 1 && digitalData.productInfo.sellerId === AB.DATALAYER.getBannerSellerId()) { 5242 let isItemInSwyCart = false; 5243 digitalData.cart.absItems.forEach(item => { 5244 if (item.productID == digitalData.productInfo.productID) { 5245 isItemInSwyCart = true; 5246 } 5247 }); 5248 if (isItemInSwyCart) { 5249 digitalData.cart.absItems = digitalData.cart.absItems.filter(item => { 5250 return item.productID != digitalData.productInfo.productID; 5251 }); 5252 } 5253 window['analyticsSellerIds'].splice(window['anal
5253yticsSellerIds'].indexOf(digitalData.productInfo.sellerId), 1); 5254 } 5255 this.removeFromCart(digitalData.productInfo); 5256 } 5257 AB.DATALAYER.setActiveCarts(digitalData.cart?.item); 5258 // Track user message toast on order edit product action 5259 if (digitalData.productInfo.previousPrice && eventName === 'add_quantity' && digitalData.productInfo.editOrderFlow) { 5260 digitalData.modal = digitalData.modal || {}; 5261 digitalData.modal.modalMessage = `pricechange|price${digitalData.productInfo.isPriceDecreased ? 'decreased' : 'increased'}`; 5262 } 5263 digitalData.pageDetails.sellerId = window['analyticsSellerIds'].join(','); 5264 5265 this.pushEvent( 5266 eventName, { 5267 productInfo: digitalData.productInfo ? AB.DATALAYER.safeJSONParse(JSON.stringify(digitalData.productInfo)) : {}, 5268 page: digitalData.pageDetails, 5269 pageFilter: digitalData.pageFilter ? AB.DATALAYER.safeJSONParse(JSON.stringify(digitalData.pageFilter)) : {}, 5270 cart: digitalData.cart, 5271 martech: digitalData.martech, 5272 modal: digitalData.modal 5273 } 5274 ); 5275 this.resetDataLayerPfmValues(); 5276 if (window['analyticsSellerIds']) { 5277 localStorage.setItem('analyticsSellerIds', window['analyticsSellerIds'].join(',')); 5278 } 5279 digitalData.modal = {}; 5280 } 5281 }; 5282 5283 this.setActiveCarts = function (products) { 5284 let activeCarts = '' 5285 if (products && products.length > 0) { 5286 const activeCartArray = products.map(product => (`${product.productCartId}:${product.productSellerId}`)) 5287 let unique = [...new Set(activeCartArray)] 5288 activeCarts = unique.join(); 5289 } 5290 if (digitalData.cart.cartInfo) { 5291 digitalData.cart.cartInfo.activeCarts = activeCarts; 5292 } 5293 } 5294 5295 this.resetDataLayerPfmValues = function () { 5296 digitalData.productInfo.impressionAdditionalDetail = ''; 5297 digitalData.pageFilter.pfmSource = ''; 5298 // Resetting bannerCardCarousel here for convenience, needs to be reset to not persist information between hits 5299 digitalData.pageFilter.bannerCardCarousel = ''; 5300 digitalData.pageFilter.impressions = ''; 5301 } 5302 5303 /** 5304 * Determines if the product actions is an increases or decrease to item quantity on weight quantity dropdowns 5305 * @param productAddedId 5306 * @param newQuantity 5307 * @returns 5308 */ 5309 this.weightSelectionActionType = function (productAddedId, newQuantity) { 5310 let actionType = ''; 5311 let existingCart = this.safeJSONParse(localStorage.getItem('itemsInCartBeforeUpdate')) || []; 5312 5313 existingCart.forEach(item => { 5314 let productID = item.itemId || item.id || item.productBpn || item.productID; 5315 if (Number(productID) === Number(productAddedId)) { 5316 const quantity = item.quantityValue || item.units; 5317 actionType = Number(newQuantity) > Number(quantity) ? 'increase' : Number(newQuantity) < Number(quantity) ? 'decrease' : ''; 5318 } 5319 }); 5320 return actionType; 5321 } 5322 5323 /** 5324 * Processes info when adding products to cart for martech initiatives, such as cart abandonment 5325 * @param {String} eventName - current product action 5326 * @param {Object} product - product object with all attached information passed 5327 */ 5328 this.martechProductAction = function (eventName, product, cart) { 5329 // Initialize/Reset the martech object on each product action 5330 window.digitalData.martech = {}; 5331 if (product && !product.sellerId || product.sellerId === `${AB.DATALAYER.getBannerSellerId()}`) { 5332 // Set current product info to build into martech string 5333 const productID = product?.productID ? Number(product.productID) : product?.itemId ? Number(product.itemId) : product?.id ? Number(product.id) : 'no-value'; 5334 const storeID = window.digitalData.user?.shopStoreId; 5335 const potentialUnits = product?.lbs && product.lbs !== '0' ? Number(product.lbs) : product?.each && product.each !== '0' ? Number(product.each) : product?.units ? product.units : product?.qty; 5336 const currentListPrice = Number(product.listPrice) || Number(product.price); 5337 const currentBasePrice = product?.basePrice; 5338 const currentUnitType = product?.unitType || product?.quantityType; 5339 const currentUnitPrice = product?.pricePerUnit || product?.pricePer; 5340 const currentProductPrice = Number(currentListPrice) * Number(potentialUnits); 5341 const dlProductName = window.digitalData.productInfo?.productName || 'no-value'; 5342 const currentName = product?.fullName ? product.fullName : dlProductName; 5343 let finalValue; 5344 let cartId; 5345 /** 5346 * List of all products in cart after products with undefined or empty values are removed 5347 */ 5348 let filteredProducts = []; 5349 /** 5350 * List of the last 6 products added to cart 5351 */ 5352 let lastProductsAdded = []; 5353 /** 5354 * List of IDs of the last 6 products added to cart 5355 */ 5356 let lastProductsSKUs = []; 5357 // Initialized with 0 because there is addition reduce perfomed later that throws an error if the array is empty 5358 let itemPrices = [0]; 5359 /** 5360 * Counter for the number of product actions done by the user, stored in 'absItemCounter' cookie 5361 */ 5362 let currentCounter = Number(AB.DATALAYER.getCookie('absItemCounter')); 5363 /** 5364 * Items currently in cart, before invalid products are removed 5365 * On the first action on the page, the datalayer isn't populated so this information is taken from the 'abCart' localStorage item 5366 * If the add to cart actions are triggered from the BIA Add All Items button, we need to get the cart items from a different LS 5367 * because the API cart update does not trigger on each product added, but rather updates in bulk 5368 */ 5369 let cartItems = []; 5370 if (this.getLSItem('analyticsBIACartUpdate') ) { 5371 cartItems = this.safeJSONParse(this.getLSItem('analyticsBIACartUpdate')).bi
5371aItems || []; 5372 } else if (this.getLSItem('analyticsBundleCartUpdate')) { 5373 cartItems = this.safeJSONParse(this.getLSItem('analyticsBundleCartUpdate')).bundleItems || []; 5374 } 5375 else { 5376 cartItems = window?.digitalData?.cart?.absItems?.length > 0 ? window.digitalData.cart.absItems : cart?.cartItemsList ? cart.cartItemsList : cart?.orderItems ? cart?.orderItems : this.safeJSONParse(this.getLSItem('abCart'))?.cart?.abs?.cartItems ? this.safeJSONParse(this.getLSItem('abCart')).cart.abs.cartItems : this.safeJSONParse(this.getLSItem('abCart'))?.cartItems ? this.safeJSONParse(this.getLSItem('abCart')).cartItems : []; 5377 } 5378 let lsCartId = this.getLSItem('cartId') && this.safeJSONParse(this.getLSItem('cartId')) && this.safeJSONParse(this.getLSItem('cartId'))?.abs ? this.safeJSONParse(this.getLSItem('cartId')).abs : this.safeJSONParse(this.getLSItem('cartId')); 5379 cartId = window?.digitalData?.cart?.item?.length > 0 ? window.digitalData.cart.cartInfo.cartId : lsCartId; 5380 5381 cartItems.forEach(item => { 5382 if ((item.name && item.price) || (item.productName && item.listPrice)) { 5383 filteredProducts.push(item); 5384 } 5385 }); 5386 5387 function setSapientCookie(hitValue) { 5388 document.cookie = `absItemCounter=${hitValue}; expires=0; path=/; secure`; 5389 }; 5390 5391 // Increase the counter and set the new value in cookie 5392 currentCounter++; 5393 setSapientCookie(currentCounter); 5394 5395 /** 5396 * Creates the product string for martech variables from the cart datalayer info 5397 * @param counter - current product action counter 5398 * @param product - product information from cart 5399 */ 5400 function setProductString(counter, product) { 5401 let productString = `${counter}|${product.productID || product.itemId || product.id}|${product.productName || product.name}|${product.basePrice}|${product.listPrice || product.price}|${product.pricePerUnit || product.unitPrice}|${product.unitType || product.unitQuantity}`; 5402 return productString; 5403 } 5404 5405 // Remove invalid products from cart items 5406 filteredProducts.forEach(item => { 5407 let id = item.productID || item.itemId || item.id; 5408 if (id && productID && productID !== 'no-value' && id !== productID) { 5409 const totalPrice = item.totalPrice && item.units && item.basePrice ? Number(item.units) * Number(item.basePrice) : 0; 5410 itemPrices.push(Number(totalPrice)); 5411 } 5412 }); 5413 // Populate the martech object based on the type of product action / eventName 5414 switch (eventName) { 5415 case 'cart_add': 5416 if (this.getLSItem('analyticsBIACartUpdate') || this.getLSItem('analyticsBundleCartUpdate')) { 5417 // The current product added info is not available in the item array in the datalayer so the information is taken from passed productInfo 5418 digitalData.martech.product1 = `${currentCounter}|${productID}|${currentName}|${currentBasePrice}|${currentListPrice}|${currentUnitPrice}|${currentUnitType}`; 5419 lastProductsAdded = filteredProducts.slice(0, 5); 5420 lastProductsSKUs.push(productID); 5421 } else { 5422 lastProductsAdded = filteredProducts.slice(0, 6); 5423 } 5424 lastProductsAdded.forEach(item => { 5425 let cartProductId = item.productID || item.itemId || item.id; 5426 // productNumber = this.getLSItem('analyticsBIACartUpdate') ? lastProductsAdded.indexOf(item) + 2 : lastProductsAdded.indexOf(item) + 1; 5427 productNumber = this.getLSItem('analyticsBundleCartUpdate') 5428 ? lastProductsAdded.indexOf(item) + 2 5429 : this.getLSItem('analyticsBIACartUpdate') 5430 ? lastProductsAdded.indexOf(item) + 2 5431 : lastProductsAdded.indexOf(item) + 1; 5432 digitalData.martech[`product${productNumber}`] = setProductString(currentCounter, item); 5433 lastProductsSKUs.push(cartProductId); 5434 }); 5435 break; 5436 case 'add_quantity': 5437 lastProductsAdded = filteredProducts.slice(0, 6); 5438 lastProductsAdded.forEach(item => { 5439 let cartProductId = item.productID || item.itemId || item.id; 5440 productNumber = lastProductsAdded.indexOf(item) + 1; 5441 digitalData.martech[`product${productNumber}`] = setProductString(currentCounter, item); 5442 lastProductsSKUs.push(cartProductId); 5443 }); 5444 break; 5445 case 'remove_cart': 5446 let finalProductsList = []; 5447 let finalProductsSKUs = []; 5448 let arrayToBeReduced = filteredProducts.slice(0, 7); 5449 lastProductsAdded = filteredProducts.slice(0, 6); 5450 lastProductsAdded.forEach(item => { 5451 let cartProductId = item.productID || item.itemId || item.id; 5452 lastProductsSKUs.push(cartProductId); 5453 }); 5454 // On product removal, if the removed product is within the last 6 products added, we iterate through the last 7 products and remove current product 5455 if (lastProductsSKUs.indexOf(productID) >= 0) { 5456 arrayToBeReduced.forEach(item => { 5457 let cartProductId = item.productID ? item.productID : item.itemId; 5458 if (cartProductId !== productID) { 5459 finalProductsSKUs.push(cartProductId); 5460 finalProductsList.push(item); 5461 } 5462 }); 5463 lastProductsSKUs = finalProductsSKUs; 5464 } else { 5465 lastProductsAdded.forEach(item => { 5466 finalProductsList.push(item); 5467 }); 5468 } 5469 finalProductsList.forEach(item => { 5470 if (lastProductsAdded.length === 1 && lastProductsSKUs.indexOf(digitalData.productInfo.productID?.toString()) !== -1) { 5471 itemPrices = [0]; 5472 lastProductsSKUs = []; 5473 } else { 5474 productNumber = finalProductsList.indexOf(item) + 1; 5475 digitalData.martech[`product${productNumber}`] = setProductString(currentCounter, item); 5476 } 5477 }); 5478 break; 5479 case 'remove_quantity': 5480 lastProductsAdded = filteredProducts.slice(0, 6); 5481 lastProductsAdded.forEach(item => { 5482 let cartProductId = item.productID || item.itemId || item.id; 5483 productNumber = lastProductsAdded.indexOf(item) + 1; 5484 digitalData.martech[`product${productNumber}`] = setProductString(currentCounter, item); 5485 lastProductsSKUs.push(cartProductId); 5486 }); 5487 break; 5488 default: 5489 break; 5490 }; 5491 // Value of the entire cart, keep only 2 decimals after comma 5492 finalValue = window.AB.DATALAYER.roundUpValue(itemPrices.filter(price => !isNaN(parseFloat(price))).reduce(AB.DATALAYER.safeReducer, 0), 2); 5493 digitalData.martech.cartValue = finalValue; 5494 digitalData.martech.cartId = cartId; 5495 digitalData.martech.cartDetails = `${currentCounter}#${lastProductsSKUs.join('|')}#${finalValue}#${storeID}`; 5496 } 5497 } 5498 5499 /** 5500 * Sets placement, mediaType and recommendationType based on pfmDetail for add to cart on pdp 5501 * @param pfmDetail 5502 */ 5503 this.setPDPPlacementMediaType = function (pfmDetail) { 5504 const pfmParts = pfmDetail.split('#'); 5505 const [page, mediaType, search, recommendationType, row, slot] = pfmParts; 5506 const isProductGrid = mediaType === 'product-grid' || mediaType?.includes('drawer'); 5507 5508 digitalData.pageFilter.mediaType = isProductGrid 5509 ? 'product-grid' 5510 : mediaType?.toLowerCase() === 'pdp-landing' ? 'PDP-landing' : 'product-carou
5510sel'; 5511 digitalData.pageFilter.placement = `${row}#${slot}`; 5512 digitalData.pageFilter.recommendationType = recommendationType === 'not-recommended' ? 'notrecommended' : recommendationType; 5513 } 5514 5515 /** 5516 * Sets pfmDetail on product actions 5517 */ 5518 this.setPfmDetailOnProductAction = function (eventName) { 5519 const bentoCtx = digitalData._bentoProductContext; 5520 if (bentoCtx) { 5521 const badgeInfo = AB.DATALAYER.getBadgeInfo(digitalData.productInfo?.badges); 5522 const pfmStart = `${AB.DATALAYER.pfmFromCurrentPage}#${bentoCtx.content}#non-search`; 5523 digitalData.productInfo.pfmDetail = this.createPaPfmBasedOnRecType(pfmStart, bentoCtx.recommendationType, bentoCtx.placement, badgeInfo); 5524 digitalData.pageFilter.mediaType = bentoCtx.mediaType; // V80 media type -> bento-hero-card:product-grid 5525 // V80 recommendation (rTyp): set explicitly so it reflects this click, not a stale value. 5526 digitalData.pageFilter.recommendationType = bentoCtx.recommendationType || 'no-value'; 5527 // V80 ROW#SLOT: onetag composes V80 from mediaType#recommendationType#placement, and handleItemEvent 5528 // clears pageFilter.placement just before this â restore the bento card position (e.g. R01#S03). 5529 digitalData.pageFilter.placement = bentoCtx.placement; 5530 digitalData.pageFilter.bannerCardCarousel = bentoCtx.bannerCardCarousel; // V218 5531 if (bentoCtx.mediaPlacementClick) { 5532 digitalData.pageFilter.mediaPlacementClick = bentoCtx.mediaPlacementClick; // V80 (canonical mediaType#rec#r#s) 5533 } 5534 return; 5535 } 5536 if (digitalData.productInfo.carouselSection || digitalData.pageDetails.subSection3 === 'category-view') { 5537 // product actions on carousel items 5538 this.setCarouselPosition({ eventName: eventName }); 5539 this.paPfmCarousel(); 5540 } else { 5541 // product actions on product grid, offer drawer, cross-sell, pdp 5542 this.setProductGridPosition({ eventName: eventName }); 5543 if (this.isPdp()) { 5544 // for product actions on pdp (for products that are not in a carousel)
5545 this.paPfmPDP(); 5546 } else if (this.isSearchResultsPage()) { 5547 // sets pfm detail on product actions on search-results pages 5548 this.paPfmOnSearch(); 5549 } else if (this.isARManagePage()) { 5550 // sets pfm detail on product actions on schedule & save page 5551 this.paPfmOnSns(); 5552 } else { 5553 // sets pfm detail on non-search pages product actions 5554 this.paPfmNonSearch(); 5555 } 5556 } 5557 } 5558 5559 /** 5560 * Product actions (add, remove, increase/decrease quantity) on carousels 5561 */ 5562 this.paPfmCarousel = function () { 5563 let carouselTitle = digitalData.productInfo.carouselSection || digitalData.productInfo.departmentName; 5564 const productID = digitalData.productInfo.productID; 5565 let isPicksForYou = false; 5566 let isP13nGeneric = false; 5567 const badgeInfo = AB.DATALAYER.getBadgeInfo(digitalData.productInfo?.badges); 5568 5569 if (carouselTitle) { 5570 isPicksForYou = carouselTitle.search(/picks for you|picked for you|picks-for-you|picked-for-you/i) !== -1; 5571 carouselTitle = carouselTitle.toLowerCase(); 5572 carouselTitle = carouselTitle.includes('picks for you') || carouselTitle.includes('picked for you') 5573 ? this.sanitizeTitlePicksForYouAndP13n(carouselTitle) : carouselTitle.replace(/[, ]+/g, '-'); 5574 } 5575 if (productID) { 5576 const currentCarousel = document.querySelector(`a[data-bpn="${productID}"]`)?.closest('.slick-product-carousel'); 5577 const p13nCarousel = currentCarousel?.querySelector('[data-carousel-driven=p13n-generic]'); 5578 isP13nGeneric = p13nCarousel && p13nCarousel.length > 0; 5579 } 5580 5581 const recType = isPicksForYou || isP13nGeneric ? 'recommended' : digitalData.pageFilter.recommendationType; 5582 const pfmDetailStart = `${AB.DATALAYER.pfmFromCurrentPage}#${carouselTitle}#non-search`; 5583 const placement = digitalData.productInfo.placement; 5584 digitalData.productInfo.pfmDetail = this.createPaPfmBasedOnRecType(pfmDetailStart, recType, placement, badgeInfo); 5585 } 5586 5587 /** 5588 * Product actions (add, remove, increase/decrease quantity) on search results pages 5589 */ 5590 this.paPfmOnSearch = function () { 5591 const pfmDetailSearch = this.getLSItem('pfmDetailSearch'); 5592 const badgeInfo = AB.DATALAYER.getBadgeInfo(digitalData.productInfo?.badges); 5593 5594 if (pfmDetailSearch) { 5595 const currentProductId = digitalData.productInfo.productID; 5596 const pfmFromCurrentPage = AB.DATALAYER.pfmFromCurrentPage; 5597 const isCrossSell = digitalData.productInfo.isCrossSell; 5598 5599 let pfmDetailStart = `${pfmFromCurrentPage}#product-grid#${pfmDetailSearch}`; 5600 if (isCrossSell) { 5601 const currentProduct = document.querySelector(`cross-sell .product-item-${currentProductId}, cross-sell product-item-al-v2 div[data-bpn='${currentProductId}']`)?.closest('product-item-v2, product-item-al-v1, product-item-al-v2'); 5602 const crossSellTitle = currentProduct?.closest('cross-sell')?.querySelector('.cross-sell__title')?.textContent?.trim()?.replace(/[, ]+/g, '-'); 5603 pfmDetailStart = `${pfmFromCurrentPage}#${crossSellTitle}#${pfmDetailSearch}`; 5604 } else if (this.getDrawerStatus(currentProductId)) { 5605 const drawerType = this.getDrawerType(currentProductId); 5606 pfmDetailStart = `${pfmFromCurrentPage}#${drawerType}#${pfmDetailSearch}`; 5607 } 5608 digitalData.productInfo.pfmDetail = this.createPaPfmBasedOnRecType(pfmDetailStart, null, null, badgeInfo); 5609 } else if (this.isARManagePage()) { 5610 this.paPfmOnSns(); 5611 } 5612 } 5613 5614 /** 5615 * Product actions (add, remove, increase/decrease quantity) on all non-search-results pages 5616 */ 5617 this.paPfmNonSearch = function () { 5618 const currentProductId = digitalData.productInfo.productID; 5619 const isCrossSell = digitalData.productInfo.isCrossSell; 5620 const pfmFromCurrentPage = AB.DATALAYER.pfmFromCurrentPage; 5621 const instoreReceiptProductPlacement = this.getLSItem('instoreReceiptProductPlacement') 5622 let pfmDetailStart = `${pfmFromCurrentPage}#product-grid#non-search`; 5623 const badgeInfo = AB.DATALAYER.getBadgeInfo(digitalData.productInfo?.badges); 5624 5625 if (this.getDrawerStatus(currentProductId)) { 5626 const drawerType = this.getDrawerType(currentProductId); 5627 pfmDetailStart = `${pfmFromCurrentPage}#${drawerType}#non-search`; 5628 } else if (isCrossSell) { 5629 const currentProduct = document.querySelector(`cross-sell .product-item-${currentProductId}, cross-sell product-item-al-v2 div[data-bpn='${currentProductId}']`)?.closest('product-item-v2, product-item-al-v1, product-item-al-v2'); 5630 const crossSellTitle = currentProduct?.closest('cross-sell')?.querySelector('.cross-sell__title')?.textContent?.trim()?.replace(/[, ]+/g, '-'); 5631 pfmDetailStart = `${pfmFromCurrentPage}#${crossSellTitle}#non-search`; 5632 } else if (instoreReceiptProductPlacement) { 5633 pfmDetailStart = `${AB.DATALAYER.initPathSections(true).join(':')}#instore-receipt#non-search`; 5634 digitalData.pageFilter.placement = instoreReceiptProductPlacement 5635 } 5636 digitalData.productInfo.pfmDetail = this.createPaPfmBasedOnRecType(pfmDetailStart, null, null, badgeInfo); 5637 } 5638 5639 /** 5640 * Product actions (add, remove, increase/decrease quantity) on PDP 5641 */ 5642 this.paPfmPDP = function () { 5643 const lsPfmDetail = this.getLSItem('pfmDetail'); 5644 const drawerStatus = this.getDrawerStatus(digitalData.productInfo?.productID); 5645 const badgeInfo = AB.DATALAYER.getBadgeInfo(digitalData.productInfo?.badges); 5646 5647 if (drawerStatus) { 5648 const drawerType = this.getDrawerType(digitalData.productInfo.productID); 5649 digitalData.productInfo.pfmDetail = `product-details#${drawerType}#non-search#${digitalData.pageFilter.recommendationType}#${digitalData.pageFilter.placement}#${badgeInfo}`; 5650 } else if (lsPfmDetail) { 5651 digitalData.productInfo.pfmDetail = lsPfmDetail; 5652 this.setPDPPlacementMediaType(lsPfmDetail); 5653 } 5654 } 5655 5656 /** 5657 * Product actions (add, increase/decrease quantity) on Schedule & Save
5658 */ 5659 this.paPfmOnSns = function () { 5660 if (digitalData?.productInfo) { 5661 const searchTerm = document.querySelector('.sns-modal-content-wrapper input')?.value; 5662 const badgeInfo = AB.DATALAYER.getBadgeInfo(digitalData.productInfo?.badges); 5663 const pfmDetailStart = searchTerm ? `search-results#product-grid#${searchTerm}:ota` : 'customer-account:ota:bia#buy-it-again#non-search'; 5664 digitalData.productInfo.pfmDetail = this.createPaPfmBasedOnRecType(pfmDetailStart, searchTerm ? 'notRecommended' : 'pastPurchased', null, badgeInfo); 5665 } 5666 } 5667 5668 this.setPlacementInfoCarouselItems = function (element) { 5669 if (element) { 5670 const parentCarousel = element.closest('.slick-product-carousel, .bia-carousel-list-item'); 5671 const visibleProds = this.findVisibleElement(document.querySelectorAll('.slick-product-carousel, .bia-carousel-list-item')); 5672 const carouselIndex = visibleProds?.indexOf(parentCarousel) + 1; 5673 const currentProduct = element.closest('product-carousel-item, product-carousel-item-v2, product-carousel-item-al-v1, product-carousel-item-al-v2'); 5674 const productPosition = parentCarousel ? Array.from(parentCarousel.querySelectorAll('product-carousel-item-v2, product-carousel-item-al-v1, product-carousel-item-al-v2'))?.indexOf(currentProduct) + 1 : 0; 5675 const displayableSPosition = productPosition < 10 ? `0${productPosition}` : productPosition; 5676 const displayableCRPosition = carouselIndex < 10 ? `0${carouselIndex}` : carouselIndex; 5677 const placement = `R${displayableCRPosition}#S${displayableSPosition}`; 5678 return placement; 5679 } 5680 } 5681 5682 /** 5683 * 5684 * @param pfmDetailStart The starting part of the pfmDetail string 5685 * @param recType the recommendation type 5686 * @returns the pfmDetail string 5687 */ 5688 this.createPaPfmBasedOnRecType = function (pfmDetailStart, recType, placement, badgeInfo) { 5689 let pfmDetailValue = ''; 5690 const recommendationType = recType ? recType : digitalData.pageFilter.recommendationType; 5691 const placementValue = placement ? placement : digitalData.pageFilter.placement; 5692 digitalData.pageFilter.placement = placementValue; 5693 switch (recommendationType) { 5694 case 'sponsored': 5695 case 'cross-sell:sponsored': 5696 case 'cross-sell': 5697 case 'recipes': 5698 pfmDetailValue = `${pfmDetailStart}#${recommendationType}#${placementValue}#${badgeInfo}`; 5699 break; 5700 case 'pastPurchased': 5701 const page = this.isBIAPage() && localStorage.getItem('analyticsBiaLoadMore') ? `:${localStorage.getItem('analyticsBiaLoadMore')}` : ''; 5702 pfmDetailValue = `${pfmDetailStart}#past-purchase${page}#${placementValue}#${badgeInfo}`; 5703 break; 5704 case 'recommended': 5705 pfmDetailValue = `${pfmDetailStart}#recs#${placementValue}#${badgeInfo}`; 5706 break; 5707 default: 5708 pfmDetailValue = `${pfmDetailStart}#not-recommended#${placementValue}#${badgeInfo}`; 5709 } 5710 return pfmDetailValue; 5711 } 5712 /** 5713 * Sets the pfmDetail in localStorage when clicking on the product item to navigate to the product details page 5714 * @param element html element 5715 * @param product product object 5716 * @param productInfoObj additional information from componenet 5717 */ 5718 this.setPfmDetailProductItemToPDP = function (element, product, productInfoObj) { 5719 if (AB.DATALAYER.isAlcoholAdTrackingDisabled(product)) { 5720 return; 5721 } 5722 let productRow; 5723 let productPosition; 5724 const badgeInfo = AB.DATALAYER.getBadgeInfo(product?.badges); 5725 const crossSellElement = element?.closest('cross-sell'); 5726 if (element) { 5727 const modelId = element.closest('lazy-wrapper')?.getAttribute('data-model-id'); 5728 if (modelId) { 5729 this.setLSItem('analyticsProductMsg', digitalData.productTrackingInfo && digitalData.productTrackingInfo[modelId] && digitalData.productTrackingInfo[modelId].msgExperiment || 'no-value'); 5730 } 5731 } 5732 5733 if (productInfoObj.isSpotlight) { 5734 const currentProduct = element?.closest('.banner-unit-spotlight__content__product'); 5735 const allProducts = currentProduct ? Array.from(currentProduct.closest('.banner-unit-spotlight__content').querySelectorAll('.banner-unit-spotlight__content__product')) : []; 5736 productPosition = allProducts.indexOf(currentProduct) + 1; 5737 productRow = 1; 5738 this.setLSItem('analyticsMediaType', 'spotlight-banner'); 5739 } else { 5740 const currentGrid = crossSellElement ? crossSellElement : element.closest('search-grid, product-generic-grid, buy-it-again-grid, product-generic-grid-al-v1'); 5741 const gridProducts = currentGrid ? Array.from(currentGrid.querySelectorAll('product-item-v2, product-item-al-v1, product-item-al-v2')) : []; 5742 let isNewDesign = element?.localName === 'product-item-al-v2'; 5743 productPosition = gridProducts.indexOf(element) + 1; 5744 productRow = window.innerWidth < 768 && !crossSellElement 5745 ? productPosition < 2 ? 1 : Math.ceil(productPosition / 2) 5746 : productPosition < 4 ? 1 : Math.ceil(productPosition / 4); 5747 5748 if (isNewDesign && !crossSellElement) { 5749 if (window.innerWidth < 470) { 5750 productRow = productPosition < 2 ? 1 : Math.ceil(productPosition / 2); 5751 } else if (window.innerWidth >= 470 && window.innerWidth < 616) { 5752 productRow = productPosition < 3 ? 1 : Math.ceil(productPosition / 3); 5753 } else if ((window.innerWidth >= 762 && window.innerWidth < 908) || (window.innerWidth >= 1019 && window.innerWidth < 1214)) { 5754 productRow = productPosition < 5 ? 1 : Math.ceil(productPosition / 5); 5755 } else if ((window.innerWidth >= 908 && window.innerWidth < 992) || (window.innerWidth >= 1214 && window.innerWidth < 1280)) { 5756 productRow = productPosition < 6 ? 1 : Math.ceil(productPosition / 6); 5757 } 5758 } 5759 this.setLSItem('analyticsMediaType', 'product-grid'); 5760 const crossSellTitle = crossSellElement?.querySelector('.cross-sell__title')?.textContent?.trim()?.replace(/[, ]+/g, '-'); 5761 if (crossSellTitle) { 5762 this.setLSItem('analyticsCrossSellTitle', crossSellTitle); 5763 this.setLSItem('analyticsMediaType', 'product-carou
5763sel'); 5764 } 5765 } 5766 const displayableSPosition = productPosition < 10 ? `0${productPosition}` : productPosition; 5767 const displayablePGPosition = productRow < 10 ? `0${productRow}` : productRow; 5768 const placement = `R${displayablePGPosition}#S${displayableSPosition}`; 5769 this.setLSItem('analyticsProductPlacement', placement); 5770 5771 let pfmDetailPDP = `not-recommended#${placement}#${badgeInfo}`; 5772 if (productInfoObj.sponsoredProduct || product.adId || product.eventTracking) { 5773 pfmDetailPDP = crossSellElement ? `cross-sell:sponsored#${placement}#${badgeInfo}` : `sponsored#${placement}#${badgeInfo}`; 5774 } else if (productInfoObj.isPastPurchased || productInfoObj.calledBy === 'buy-it-again' || productInfoObj.calledBy === 'buy-it-again-qsc') { 5775 const page = this.isBIAPage() && localStorage.getItem('analyticsBiaLoadMore') ? `|${this.handleGetLSValue('analyticsBiaLoadMore')}` : ''; 5776 pfmDetailPDP = `past-purchase${page}#${placement}#${badgeInfo}`; 5777 } 5778 5779 if (product && placement) { 5780 this.trackOneTagProductClick({ product: product, placement: placement, feature: 'grid' }); 5781 } 5782 this.setLSItem('pfmDetailProductPDP', pfmDetailPDP); 5783 5784 if (this.getDrawerStatus(product.id)) { 5785 let drawerType = 'drawer'; 5786 const productGridApiType = element.closest('product-generic-grid')?.getAttribute('data-api-type'); 5787 if (productGridApiType === 's-bogo') { 5788 drawerType = 'BOGO-drawer'; 5789 } else if (productGridApiType === 'c-products-by-upc-drawer') { 5790 drawerType = 'J4U-drawer'; 5791 } 5792 this.setLSItem('analyticsDrawerPfm', drawerType); 5793 } 5794 5795 if (productInfoObj.pathwaysWidget) { 5796 this.setLSItem('analyticsPathwaysWidget', JSON.stringify(productInfoObj.pathwaysWidget)); 5797 } 5798 5799 this.trackAEMZone('pdp-card', element, 'product-grid'); 5800 5801 this.setPfmSourceCategoriesPDP(); 5802 this.setPfmSourceBiaPDP(); 5803 } 5804 5805 /** 5806 * Sets the pfmDetail in localStorage when clicking on the carousel product item to navigate to the product details page 5807 * @param carousel carousel object 5808 * @param element product html element 5809 * @param product product object 5810 */ 5811 this.setPfmDetailCarouselToPDP = function (carousel, element, product) { 5812 if (AB.DATALAYER.isAlcoholAdTrackingDisabled(product)) { 5813 return; 5814 } 5815 const badgeInfo = AB.DATALAYER.getBadgeInfo(product?.badges); 5816 const biaCarousel = element.closest('.bia-carousel-list-item'); 5817 const isSponsored = carousel.dataDriven === 'featured-products' || product['adId'] || product.eventTracking; 5818 const isPastPurchased = carousel.behaviour === 'buy-it-again' || carousel.dataDriven === 'buy-it-again' || biaCarousel; 5819 const isBrPathway = carousel.dataDriven === 'pathway-search'; 5820 const isPicksForYou = carousel.dataDriven === 'picked-for-you'; 5821 const isP13nGeneric = carousel.dataDriven === 'p13n-generic'; 5822 const isBiaSpaCarousel = carousel.behaviour === 'bia-spaCarousel'; 5823 const currentPagePfm = AB.DATALAYER.pfmFromCurrentPage; 5824 const parentCarousel = element.closest('.slick-product-carousel, .bia-carousel-list-item'); 5825 const alternateCarouselTitle = parentCarousel.querySelector('lazy-wrapper') 5826 ? parentCarousel.querySelector('lazy-wrapper').dataset?.carouselTitle 5827 : isBiaSpaCarousel ? parentCarousel.closest('.bia-spa-carousel-container')?.querySelector('.bia-spa-carousel-container-header')?.textContent?.trim() : ''; 5828 const visibleProds = this.findVisibleElement(document.querySelectorAll('.slick-product-carousel, .bia-carousel-list-item')); 5829 const carouselIndex = visibleProds.indexOf(parentCarousel) + 1; 5830 const productPosition = parentCarousel ? Array.from(parentCarousel.querySelectorAll('product-carousel-item-v2, product-carousel-item-al-v1, product-carousel-item-al-v2')).indexOf(element) + 1 : 0; 5831 const displayableSPosition = productPosition < 10 ? `0${productPosition}` : productPosition; 5832 const displayableCRPosition = carouselIndex < 10 ? `0${carouselIndex}` : carouselIndex; 5833 const placement = `R${displayableCRPosition}#S${displayableSPosition}`; 5834 const productAdobeMetricA = product['adobeMetrics']?.metricA; 5835 const productAdobeMetricC = product['adobeMetrics']?.metricC; 5836 this.setLSItem('analyticsBIAProductImpressionAdDetail', productAdobeMetricA); 5837 this.setLSItem('analyticsBIAPfmSource', productAdobeMetricC);
5838 this.setLSItem('analyticsIsBIACarousal', isBiaSpaCarousel); 5839 this.setLSItem('analyticsBIACarousalProductId', product.id); 5840 5841 this.setLSItem('analyticsProductPlacement', placement); 5842 this.setLSItem('analyticsMediaType', 'product-carousel'); 5843 5844 let title = carousel.title ? carousel.title : alternateCarouselTitle; 5845 if (title) { 5846 title = isPicksForYou || isP13nGeneric ? this.sanitizeTitlePicksForYouAndP13n(title) : title.trim().replace(/[, ]+/g, '-'); 5847 this.setLSItem('analyticsCarouselName', `shop|${title}`); 5848 } 5849 5850 //TO DO: verify this.pathwaysWidget=this.carousel.pathwaysWidget 5851 if (carousel.pathwaysWidget) { 5852 this.setLSItem('analyticsPathwaysWidget', JSON.stringify(carousel.pathwaysWidget)); 5853 } 5854 if (element) { 5855 const modelId = element.closest('lazy-wrapper')?.getAttribute('data-model-id'); 5856 if (modelId) { 5857 this.setLSItem('analyticsProductMsg', digitalData.productTrackingInfo && digitalData.productTrackingInfo[modelId] && digitalData.productTrackingInfo[modelId].msgExperiment || 'no-value'); 5858 } 5859 } 5860 5861 const pfmDetailCarouselPDPObj = { 5862 sponsored: `${currentPagePfm}#${title}#non-search#sponsored#${placement}#${badgeInfo}`, 5863 recs: `${currentPagePfm}#${title}#non-search#recs#${placement}#${badgeInfo}`, 5864 pastPurchase: `${currentPagePfm}#${biaCarousel?.querySelector('.bia-carousel-title .department-name')?.textContent || title}#non-search#past-purchase#${placement}#${badgeInfo}`, 5865 br: `${currentPagePfm}#${title}#non-search#br#${placement}#${badgeInfo}`, 5866 notRecommended: `${currentPagePfm}#${title}#non-search#not-recommended#${placement}#${badgeInfo}` 5867 }; 5868 5869 let pfmDetailCarouselPDP = pfmDetailCarouselPDPObj.notRecommended; 5870 if (isSponsored) { 5871 pfmDetailCarouselPDP = pfmDetailCarouselPDPObj.sponsored; 5872 } else if (carousel.isRecommended || isPicksForYou || isP13nGeneric) { 5873 pfmDetailCarouselPDP = pfmDetailCarouselPDPObj.recs; 5874 } else if (isPastPurchased) { 5875 pfmDetailCarouselPDP = pfmDetailCarouselPDPObj.pastPurchase; 5876 } else if (isBrPathway) { 5877 pfmDetailCarouselPDP = pfmDetailCarouselPDPObj.br; 5878 } 5879 5880 if (product && placement && carousel.dataDriven === 'featured-products') { 5881 product.carouselSection = title; 5882 this.trackOneTagProductClick({ product: product, placement: placement, feature: 'carousel' }); 5883 } 5884 this.trackAEMZone('pdp-card', element, title); 5885 this.setLSItem('pfmDetailCarouselPDP', pfmDetailCarouselPDP); 5886 if (isP13nGeneric) { 5887 const pId = element.querySelector('.product-carousel-item-v3__image-link')?.getAttribute('data-bpn') 5888 const currentDietPrefItem = digitalData.dietPrefItems?.find(item => item.productID === pId); 5889 const dataImpressionTracking = parentCarousel.querySelector('lazy-wrapper')?.getAttribute('data-impression-tracking') 5890 const isImpTrackEnabled = dataImpressionTracking ? dataImpressionTracking : 'no-value'; 5891 this.setLSItem('analyticsp13nDataPDP', `${currentDietPrefItem?.impressionAdditionalDetail}#${isImpTrackEnabled}`) 5892 } 5893 this.setPfmSourceCategoriesPDP(); 5894 if (this.isSearchResultsPage() && carousel.dataDriven === 'featured-products') { 5895 this.analyticsCTATracking(`cta:${AB.DATALAYER.pathSections.join(':')}|link|${title}`); 5896 } 5897 } 5898 5899 /** 5900 * Initializes pfmDetail on pdp pages 5901 */ 5902 this.initPDPPfm = function (productInfoObj) { 5903 const badgeInfo = AB.DATALAYER.getBadgeInfo(productInfoObj?.badges); 5904 const lsPfmDetail = localStorage.getItem('pfmDetail'); 5905 let pfmDetailValue = ''; 5906 5907 // carousel item to pdp 5908 const handlePfmDetailCarouselPDP = () => { 5909 const lsPfmDetailCarouselPDP = this.handleGetLSValue('pfmDetailCarouselPDP'); 5910 if (lsPfmDetailCarouselPDP) { 5911 pfmDetailValue = lsPfmDetailCarouselPDP; 5912 5913 // save p13nData so it can be used on add to cart on pdp-TO DO: check if it can be move inside setPDPevent
5914 const analyticsp13nDataPDP = this.handleGetLSValue('analyticsp13nDataPDP'); 5915 if (analyticsp13nDataPDP) { 5916 AB.DATALAYER.p13nData = analyticsp13nDataPDP; 5917 } 5918 } 5919 }; 5920 5921 // product grid item to pdp 5922 const handlePfmDetailProductPDP = () => { 5923 const lsPfmDetailProductPDP = this.handleGetLSValue('pfmDetailProductPDP'); 5924 if (lsPfmDetailProductPDP) { 5925 const pfmDetailProduct = lsPfmDetailProductPDP; 5926 const lsPfmDetailSearch = this.handleGetLSValue('pfmDetailSearch'); 5927 if (lsPfmDetailSearch) { 5928 handlePfmDetailSearch(pfmDetailProduct, lsPfmDetailSearch); 5929 } else { 5930 handleNonSearchGridProducts(pfmDetailProduct); 5931 } 5932 } 5933 }; 5934 5935 // search-results grid to pdp 5936 const handlePfmDetailSearch = (pfmDetailProduct, lsPfmDetailSearch) => { 5937 const drawerType = this.handleGetLSValue('analyticsDrawerPfm'); 5938 const mediaType = this.getLSItem('analyticsMediaType'); 5939 const crossSellTitle = this.handleGetLSValue('analyticsCrossSellTitle'); 5940 if (drawerType) { 5941 // product on offer-drawer to PDP 5942 pfmDetailValue = `${lsPfmDetail}#${drawerType}#${lsPfmDetailSearch}#${pfmDetailProduct}`; 5943 } else if (mediaType === 'spotlight-banner') { 5944 // product on spotlight banner to PDP 5945 pfmDetailValue = `${lsPfmDetail}#spotlight-banner#${lsPfmDetailSearch}#${pfmDetailProduct}`; 5946 } else if (crossSellTitle) { 5947 // cross sell product to PDP 5948 pfmDetailValue = `${lsPfmDetail}#${crossSellTitle}#${lsPfmDetailSearch}#${pfmDetailProduct}`; 5949 } else { 5950 pfmDetailValue = `${lsPfmDetail}#product-grid#${lsPfmDetailSearch}#${pfmDetailProduct}`; 5951 } 5952 }; 5953 5954 // non-search grid products to pdp 5955 const handleNonSearchGridProducts = (pfmDetailProduct) => { 5956 const drawerType = this.handleGetLSValue('analyticsDrawerPfm'); 5957 const crossSellTitle = this.handleGetLSValue('analyticsCrossSellTitle'); 5958 const receiptType = this.handleGetLSValue('analyticsReceiptType'); 5959 if (drawerType) { 5960 if (digitalData.pageDetails?.referringURL?.indexOf('product-details') !== -1) { 5961 // PDP drawer to PDP 5962 pfmDetailValue = `product-details#${drawerType}#non-search#${pfmDetailProduct}`; 5963 } else { 5964 // other pages to PDP - from offer-drawer 5965 pfmDetailValue = `${lsPfmDetail}#${drawerType}#non-search#${pfmDetailProduct}`; 5966 } 5967 } else if (crossSellTitle) { 5968 // cross sell product to PDP 5969 pfmDetailValue = `${lsPfmDetail}#${crossSellTitle}#non-search#${pfmDetailProduct}`; 5970 } else if (receiptType) { 5971 // receipt product to pdp 5972 pfmDetailValue = `${lsPfmDetail}#${receiptType}#non-search#${pfmDetailProduct}`; 5973 } else { 5974 pfmDetailValue = `${lsPfmDetail}#product-grid#non-search#${pfmDetailProduct}`; 5975 } 5976 }; 5977 5978 const handleBentoPdpPfm = () => { 5979 const lsBentoPfm = this.handleGetLSValue('bentoHeroPdpPfm') || AB.DATALAYER._bentoPdpPfmMirror; 5980 if (lsBentoPfm) { 5981 AB.DATALAYER._bentoPdpPfmMirror = lsBentoPfm; 5982 pfmDetailValue = `product-details#${lsBentoPfm}`; 5983 } 5984 }; 5985 5986 // directly landing on pdp 5987 const setDefaultPfmDetail = () => { 5988 pfmDetailValue = `product-details#PDP-landing#non-search#not-recommended#R01#S01#${badgeInfo}`; 5989 }; 5990 5991 handleBentoPdpPfm(); 5992 5993 if (!pfmDetailValue && lsPfmDetail) { 5994 handlePfmDetailCarouselPDP(); 5995 handlePfmDetailProductPDP(); 5996 } 5997 5998 if (!pfmDetailValue) { 5999 setDefaultPfmDetail(); 6000 } 6001 6002 digitalData.pageDetails.pfmDetail = pfmDetailValue; 6003 this.setLSItem('pfmDetail', pfmDetailValue); 6004 6005 // set recommendation type 6006 const recommendationType = this.getRecommendationType(digitalData.pageDetails.pfmDetail); 6007 this.setRecommendationType(recommendationType); 6008 }; 6009 6010 /** 6011 * Initializes placement and mediaType on pdp pages 6012 */ 6013 this.initPDPPlacementMediaType = function () { 6014 if (this.isPdp()) { 6015 digitalData.pageFilter.placement = this.handleGetLSValue('analyticsProductPlacement') || 'R01#S01'; 6016 digitalData.pageFilter.mediaType = this.handleGetLSValue('analyticsMediaType') || 'product-details'; 6017 } 6018 } 6019 6020 this.addAllBundleItemsToCart = function (items, cartId, source = 'bundle-products', calledBy = 'bundleCTA') { 6021 let itemsToAdd = []; 6022 let itemsToUpdate = []; 6023 // Fetch cart from localStorage 6024 let cartData = AB.DATALAYER.safeJSONParse(AB.DATALAYER.getLSItem('abCart')); 6025 let cart = cartData && cartData.cart ? cartData.cart : cartData; 6026 let cartItems = cart?.abs?.cartItems || cart?.cartItems || []; 6027 // Helper function robustly checks if product is in cart 6028 const isProductInCart = (productId) => { 6029 return cartItems.some((item) => item.id == productId || item.itemId == productId); 6030 }; 6031 // Set eVar12 manually 6032 digitalData.pageFilter.pfmSource = source; 6033 // Iterate through items
6034 items.forEach((item) => { 6035 const isInCart = isProductInCart(item.id); 6036 const productInfo = this.getProductInfoObject(item); 6037 if (isInCart) { 6038 // Prepare for quantity update 6039 itemsToUpdate.push({ ...productInfo, deltaQty: item.qty || 1 }); 6040 } else { 6041 // Prepare for add to cart 6042 itemsToAdd.push(productInfo); 6043 } 6044 }); 6045 // Update cart type, cart, and cartId before analytics calls 6046 window['AB']['DATALAYER'].updateCartType(calledBy === 'minicart' ? 'mini_cart' : ''); 6047 window['AB']['DATALAYER'].updateCart(cartItems, itemsToAdd[0] || itemsToUpdate[0] || {}, cart.isWYSIWYGEnabled, false); 6048 window['AB']['DATALAYER'].updateCartId(cartId); 6049 let itemsAddedToCart = this.setItemDataFromCartList(cartItems); 6050 let updatedCartObj = { 6051 cartWithBundleItems: itemsAddedToCart 6052 }; 6053 localStorage.setItem('analyticsBundleCartUpdate', JSON.stringify(updatedCartObj)); 6054 // Batch analytics calls for add 6055 itemsToAdd.forEach((item) => { 6056 AB.DATALAYER.addItemToCart(item, cartId, source, cart); 6057 }); 6058 // Batch analytics calls for update 6059 itemsToUpdate.forEach((item) => { 6060 AB.DATALAYER.increaseItemQuantity(item, cart, item.deltaQty); 6061 }); 6062 }; 6063 6064 this.addAllBIAItemsToCart = function (items, caller, cartId, calledBy) { 6065 const pageDetails = { ...digitalData.pageDetails }; 6066 let itemsToAdd = []; 6067 let cartProducts = []; 6068 let linkClickName = caller === 'quickAdd' ? 'add-products-to-cart' : caller === 'qs-component' ? 'bia:instant-basket:add-all-to-cart' : 'no-value'; 6069 if (caller === 'quickAdd' || caller === 'quickAdd-topCTA') { 6070 let quickAddProductsId = AB.DATALAYER.safeJSONParse(AB.DATALAYER.getLSItem('quickAddBasket'))?.cartItemsList ? AB.DATALAYER.safeJSONParse(AB.DATALAYER.getLSItem('quickAddBasket')).cartItemsList : []; 6071 let quickAddItems = items.cartItems.filter(i => quickAddProductsId.some(i2 => parseInt(i2.itemId) === i.itemId)); 6072 itemsToAdd = quickAddItems; 6073 cartProducts = items.cartItems; 6074 cartId = items.cartId; 6075 } else if (caller === 'qs-component') { 6076 itemsToAdd = items; 6077 if (AB.DATALAYER.getLSItem('abCart')) { 6078 cartProducts = AB.DATALAYER.safeJSONParse(AB.DATALAYER.getLSItem('abCart')).cart?.abs?.cartItems ? AB.DATALAYER.safeJSONParse(AB.DATALAYER.getLSItem('abCart')).cart.abs.cartItems : AB.DATALAYER.safeJSONParse(AB.DATALAYER.getLSItem('abCart')).cartItems ? AB.DATALAYER.safeJSONParse(AB.DATALAYER.getLSItem('abCart')).cartItems : []; 6079 } 6080 } 6081 const lastIndex = itemsToAdd.length - 1; 6082 let itemsAddedToCart = this.setItemDataFromCartList(cartProducts); 6083 let quickAddItemsTop = []; 6084 itemsToAdd.forEach((item, index) => { 6085 setTimeout(() => { 6086 if (item.name) { 6087 let productInfoObj = { 6088 id: item.id || item.itemId, 6089 productID: item.id || item.itemId, 6090 name: item.name, 6091 productName: item.name, 6092 fullName: item.fullName, 6093 units: item.qty, 6094 unitType: item.unitQuantity, 6095 lbs: '0', 6096 each: '0', 6097 pricePerUnit: item.pricePer?.toString(), 6098 basePrice: item.basePrice?.toString(), 6099 listPrice: item.price?.toString(), 6100 price: item.price, 6101 totalPrice: item.price?.toString(), 6102 carouselSection: '', 6103 pricingType: item.promoType, 6104 couponFlag: '0', 6105 couponClipped: '0', 6106 couponID: '', 6107 aisleId: item.aisleId, 6108 upc: item.upc, 6109 isMTO: item.isMtoProduct, 6110 isCrossSell: false, 6111 isPastPurchased: true, 6112 departmentName: item.departmentName 6113 }; 6114 if (item.unitQuantity == 'EACH') { 6115 productInfoObj.each = item.quantity ? item.quantity.toString() : item.qty?.toString(); 6116 } 6117 let updatedCartObj = { 6118 biaItems: itemsAddedToCart 6119 }; 6120 6121 window['AB']['DATALAYER'].updateCartType(calledBy === 'minicart' ? 'mini_cart' : ''); 6122 AB.DATALAYER.updateCart(itemsAddedToCart, productInfoObj, items.isWYSIWYGEnabled, true); 6123 AB.DATALAYER.updateCartId(cartId); 6124 localStorage.setItem('analyticsBIACartUpdate', JSON.stringify(updatedCartObj)); 6125 AB.DATALAYER.setRecommendationType('past-purchase'); 6126 if (caller !== 'quickAdd-topCTA') { 6127 AB.DATALAYER.addItemToCart(productInfoObj); 6128 } else { 6129 let qckAddProductInfo = AB.DATALAYER.getProductInfo(item) || {}; 6130 quickAddItemsTop.unshift(qckAddProductInfo); 6131 } 6132 itemsAddedToCart.unshift(productInfoObj); 6133 if (index === lastIndex && caller !== 'quickAdd-topCTA') {
6134 AB.DATALAYER.setLinkClickEvents(linkClickName); 6135 localStorage.removeItem('analyticsBIACartUpdate'); 6136 } else if (index === lastIndex && caller === 'quickAdd-topCTA') { 6137 digitalData.cart = digitalData.cart || {}; 6138 digitalData.cart.quickAddItemsTop = quickAddItemsTop; 6139 AB.DATALAYER.pushEvent( 6140 this.EVENTS.LINK_CLICK, { 6141 link: { 6142 linkName: 'cart_add_all' 6143 }, 6144 page: pageDetails, 6145 cart: digitalData.cart 6146 } 6147 ); 6148 } 6149 } 6150 }, 500); 6151 }); 6152 } 6153 6154 /** 6155 * Sets the pfmDetail for all prroducts added to cart and sends the link click event for add all items to cart on instore receipt details page 6156 * @param selectedReceipt receipt data object 6157 * @param cartResponse cart Response after add to cart action 6158 */ 6159 this.addAllInstoreReceiptItemsToCart = function (selectedReceipt, cartResponse) { 6160 const receiptItems = selectedReceipt?.items; 6161 const cartItems = []; 6162 cartResponse.forEach(cart => cartItems.push(...cart.cartItemsList)); 6163 let itemsAddedToCart = cartItems.filter(item => receiptItems.find(({ bpn }) => bpn == item.itemId)); 6164 const noOfItemsAddedToCart = itemsAddedToCart.length; 6165 const cartModified = { 6166 cart: cartResponse 6167 } 6168 6169 if (window.digitalData) { 6170 digitalData.modal = digitalData.modal || {}; 6171 digitalData.modal.modalMessage = `toast-message|${noOfItemsAddedToCart}-actual-items-were-added-to-cart`; 6172 digitalData.pageFilter = digitalData.pageFilter || {}; 6173 digitalData.pageFilter.orderID = selectedReceipt?.transactionId; 6174 } 6175 const receiptProducts = this.setItemDataFromCartList(itemsAddedToCart); 6176 receiptItems.forEach(item => { 6177 const cartItem = receiptProducts.find(rItem => rItem.productID == item.bpn); 6178 const cartItemIndex = receiptProducts.findIndex(rItem => rItem.productID == item.bpn); 6179 const cartObj = cartResponse.find(cart => cart.cartItemsList.filter(cItem => cItem.itemId == item.bpn)?.length > 0); 6180 if (cartItem && cartItem.name && cartItemIndex >= 0) { 6181 const placement = cartItemIndex + 1 <= 9 ? `r0${cartItemIndex + 1}#s01` : `r${cartItemIndex + 1}#s01` 6182 AB.DATALAYER.setLSItem('instoreReceiptProductPlacement', placement) 6183 const productInfoObj = this.getProductInfoObject(cartItem); 6184 AB.DATALAYER.updateCart(cartModified, productInfoObj, cartObj.isWYSIWYGEnabled, false); 6185 AB.DATALAYER.updateCartId(cartObj.cartId); 6186 AB.DATALAYER.setRecommendationType('not-recommended'); 6187 AB.DATALAYER.addItemToCart(productInfoObj); 6188 } 6189 }) 6190 AB.DATALAYER.removeLSItem('instoreReceiptProductPlacement') 6191 AB.DATALAYER.pushEvent( 6192 this.EVENTS.LINK_CLICK, { 6193 link: { 6194 linkName: 'account|add-all-to-cart' 6195 }, 6196 page: digitalData?.pageDetails, 6197 pageFilter: digitalData?.pageFilter, 6198 user: digitalData?.user, 6199 productInfo: digitalData?.productInfo, 6200 modal: digitalData?.modal, 6201 receiptItems: receiptProducts 6202 } 6203 ) 6204 } 6205 6206 this.setItemDataFromCartList = function (ItemArray) { 6207 const products = []; 6208 ItemArray.forEach(item => { 6209 if (item.name) { 6210 products.push(this.getProductInfoObject(item)); 6211 } 6212 }) 6213 return products 6214 } 6215 6216 this.getProductInfoObject = function (item) { 6217 let cartItem = { 6218 id: item.id || item.itemId, 6219 productID: item.id || item.itemId, 6220 name: item.name, 6221 productName: item.name, 6222 fullName: item.name || item.fullName, 6223 units: item.qty, 6224 unitType: item.unitQuantity, 6225 lbs: '0', 6226 each: '0', 6227 pricePerUnit: item?.pricePer?.toString(), 6228 basePrice: item?.basePrice?.toString(), 6229 listPrice: item?.price?.toString(), 6230 price: item.price, 6231 totalPrice: (item.price * item.qty)?.toString(), 6232 carouselSection: '', 6233 pricingType: item.promoType, 6234 couponFlag: '0', 6235 couponClipped: '0', 6236 couponID: '', 6237 aisleId: item.aisleId, 6238 upc: item.upc, 6239 isMTO: item.isMtoProduct, 6240 isCrossSell: false, 6241 sellerId: item.sellerId || AB.DATALAYER.getBannerSellerId(), 6242 isPastPurchased: item['pastPurchased'] || true, 6243 departmentName: item.departmentName 6244 }; 6245 if (item.unitQuantity == 'EACH') { 6246 cartItem.each = item.quantity ? item.quantity?.toString() : item.qty?.toString(); 6247 } 6248 return cartItem; 6249 } 6250 6251}).call(AB.DATALAYER); 6252 6253'use strict'; 6254 6255(function () { 6256 this.trackEliteSlots = function (data) { 6257 const eliteSlotsValues = ['flash', 'flash_unavailable', 'premium', 'best', 'fastest']; 6258 const eliteSlotSubstring = `${this.isCartAndCheckoutPage('prebook') ? 'prebook' : 'checkout'}-slot-`; 6259 let eliteSlotsAvailable = []; 6260 let eliteSlotsFromAPI = []; 6261 const slotsArray = data?.eliteSlots?.fulfillmentType?.delivery || data?.fulfillmentType?.delivery || []; 6262 if (slotsArray.length > 0) { 6263 slotsArray.forEach(slot => eliteSlotsFromAPI.push(slot?.type?.toLowerCase())); 6264 } 6265 6266 if (eliteSlotsFromAPI.length > 0) { 6267 eliteSlotsFromAPI.forEach(slotType => { 6268 if (slotType && eliteSlotsValues.indexOf(slotType) !== -1 && !(slotType?.toLowerCase() === 'fastest' && eliteSlotsFromAPI.indexOf('flash_unavailable') === -1 && this.isCartAndCheckoutPage('prebook'))) { 6269 eliteSlotsAvailable.push(`${eliteSlotSubstring}${slotType?.toLowerCase() === 'flash_unavailable' 6270 ? 'flash' : slotType?.toLowerCase() === 'best' 6271 ? 'best-value' : slotType?.toLowerCase()}`); 6272 } 6273 }); 6274 } 6275 6276 digitalData.pageFilter.impressions = eliteSlotsAvailable.length > 0 6277 ? (digitalData.pageFilter.impressions ? `${digitalData.pageFilter.impressions},${eliteSlotsAvailable.join(',')}` : eliteSlotsAvailable.join(',')) : 6278 (digitalData.pageFilter.impressions ? `${digitalData.pageFilter.impressions},${eliteSlotSubstring}more-delivery-times` : `${eliteSlotSubstring}more-delivery-times`); 6279 } 6280 6281 this.setPaymentMethodPaypal = function (caller, paymentMethod) { 6282 if (caller === 'checkout') { 6283 const isContinueClicked = this.handleGetLSValue('continueButtonClicked'); 6284 if ((digitalData.pageDetails.subSection2 === 'checkout_step3' || digitalData.pageDetails.subSection2 === 'checkout_step4') && isContinueClicked && paymentMethod) { 6285 digitalData.pageFilter.impressions = `${digitalData.pageFilter.impressions ? `${digitalData.pageFilter.impressions},` : ''}chkout-paypal`; 6286 } 6287 } else if (caller === 'edit_checkout' && paymentMethod) { 6288 digitalData.pageFilter.impressions = `${digitalData.pageFilter.impressions ? `${digitalData.pageFilter.impressions},` : ''}edit-order-paypal`; 6289 } 6290 } 6291 6292 this.sendCOALinkEvent = function (appliedCoa, coaBalance, orderSummaryEstimatedTotal) { 6293 let coaLabel = ''; 6294 switch (true) { 6295 case (appliedCoa > 0 && appliedCoa === coaBalance): 6296 coaLabel = `credit-on-account-applied-${(coaBalance - orderSummaryEstimatedTotal) > 0 ? 'partial' : 'full'}-credit`; 6297 break; 6298 case (appliedCoa > 0 && coaBalance === 0): 6299 coaLabel = 'credit-on-account-applied-full-credit'; 6300 break; 6301 case (appliedCoa > 0 && appliedCoa < coaBalance): 6302 coaLabel = 'credit-on-account-applied-partial-credit'; 6303 break; 6304 case appliedCoa === 0: 6305 coaLabel = 'Credit-on-account-no-credit-applied'; 6306 break; 6307 6308 } 6309 this.setLinkClickEvents(`checkout|${coaLabel}`); 6310 } 6311 6312 this.checkoutMoreDeliveryTimesLinkClick = function () { 6313 if (digitalData) { 6314 digitalData.pageDetails.subSection3 = 'more-delivery-times'; 6315 this.pushEvent(this.EVENTS.LINK_CLICK, { 6316 link: { 6317 linkName: 'checkout-more-delivery-times' 6318 }, 6319 page: digitalData?.pageDetails, 6320 pageFilter: digitalData?.pageFilter, 6321 user: digitalData?.user, 6322 cart: digitalData?.cart 6323 }); 6324 } 6325 } 6326 6327 this.applyPromoCodeLinkClick = function (promoCode) { 6328 if (digitalData) { 6329 this.pushEvent(this.EVENTS.LINK_CLICK, { 6330 link: { 6331 linkName: 'checkout|promocode' 6332 }, 6333 modal: { 6334 modalMessage: `checkout|${promoCode}` 6335 }, 6336 page: digitalData?.pageDetails, 6337 pageFilter: digitalData?.pageFilter, 6338 user: digitalData?.user 6339 }); 6340 } 6341 } 6342 6343 this.setLinkClickToastMessage = function (modalMessage) { 6344 if (modalMessage) {
6345 let modalMessageLC = modalMessage.toLowerCase(); 6346 if (modalMessageLC.indexOf('freshpass') === -1) { 6347 this.pushEvent(this.EVENTS.LINK_CLICK, { 6348 link: { 6349 linkName: 'toast-message' 6350 }, 6351 modal: { 6352 modalMessage: `toast-message|${modalMessage}` 6353 }, 6354 page: digitalData?.pageDetails, 6355 pageFilter: digitalData?.pageFilter, 6356 user: digitalData?.user 6357 }); 6358 } 6359 } 6360 } 6361 6362 this.setActiveCartsModalView = function (cartData) { 6363 if (cartData) { 6364 let activeCarts = []; 6365 cartData.forEach(cart => { 6366 if (cart.seller) { 6367 activeCarts.push(cart.seller.sellerName); 6368 } else if (cart.cartCategory === '1P_WINE') { 6369 activeCarts.push('wineshop'); 6370 } else { 6371 activeCarts.unshift(cart.banner); 6372 } 6373 }); 6374 const modalAction = `view|active-carts-${activeCarts.join(',')}`; 6375 this.setModalViewEvents(modalAction); 6376 } 6377 } 6378 6379 this.setActiveCartsModalClick = function (cart) { 6380 const cartClicked = cart?.seller?.sellerName ? cart.seller.sellerName : cart?.cartCategory === '1P_WINE' ? 'wineshop' : cart?.banner; 6381 this.setModalClickEvents(`active-carts-${cartClicked}`); 6382 } 6383 6384 6385 // Full Cart methods 6386 this.updateScheduleAndSaveInfo = function (items) { 6387 if (items.some(item => item.isArProduct)) { 6388 digitalData.cart.cartInfo.scheduleAndSaveUnchecked = items.filter(item => item.isArProduct && item.isAvailable && item.price > 0 && (!item.snsSub || !item.snsSub.isSnsSub))?.length?.toString(); 6389 const scheduledProducts = items.filter(item => item.isArProduct && item.isAvailable && item.price > 0 && item.snsSub && item.snsSub.isSnsSub)?.length?.toString(); 6390 this.setLSItem('analyticsscheduledProducts', scheduledProducts); 6391 digitalData.pageFilter.impressions = `${digitalData.pageFilter.impressions ? `${digitalData.pageFilter.impressions},` : ''}sschkd:${scheduledProducts}`; 6392 } 6393 digitalData.cart.cartInfo.eligiblescheduleAndSaveproducts = items.filter(item => item.isArProduct && item.isAvailable && item.price > 0)?.length?.toString(); 6394 } 6395 6396 this.initScheduledItemsImpressions = function () { 6397 if ((this.isCartAndCheckoutPage('erums/checkout') || this.isDFTAPage()) && digitalData?.cart?.cartInfo) { 6398 digitalData.cart.cartInfo.scheduledProducts = this.handleGetLSValue('analyticsscheduledProducts'); 6399 } 6400 } 6401 6402 this.setPromoBannerImpressions = function (cart) { 6403 if (Number(localStorage.getItem('abOrderCount')) == 0 && cart?.cartCategory === '1P') { 6404 digitalData.pageFilter = digitalData.pageFilter || {}; 6405 digitalData.pageFilter.impressions = `${digitalData.pageFilter.impressions ? `${digitalData.pageFilter.impressions},` : ''}cart-promo-banner-${cart?.serviceType?.toLowerCase() === 'delivery' ? 'save20' : 'save30'}`; 6406 this.setLSItem('analyticsPromoBannerView', 'checkout-promo-banner-visible'); 6407 } 6408 } 6409 6410 this.setPromoBannerInfo = function () { 6411 const modalMessagePromoBanner = digitalData.crossPageData?.promoBannerView?.data || this.handleGetLSValue('analyticsPromoBannerView'); 6412 if (modalMessagePromoBanner) { 6413 const { pageFilter, modal } = digitalData; 6414 if (pageFilter?.promoCode && (pageFilter.promoCode.includes('SAVE20') || pageFilter.promoCode.includes('SAVE30'))) { 6415 modal.modalMessage = modal.modalMessage ? `${modal.modalMessage},${modalMessagePromoBanner}` : modalMessagePromoBanner; 6416 } 6417 this.setAnalyticsCrossPageData({ action: 'remove', name: 'promoBannerView' }); 6418 } 6419 } 6420 6421 this.initMkpGoToCart = function () { 6422 if (localStorage.getItem('analyticsMkpGoToCart')) { 6423 digitalData.pageFilter = digitalData.pageFilter || {}; 6424 digitalData.pageFilter.topNav = localStorage.getItem('analyticsMkpGoToCart'); 6425 this.removeLSItem('analyticsMkpGoToCart'); 6426 } 6427 } 6428 6429 this.initCart = function (cart, isEditCart) { 6430 if (digitalData.crossPageData?.subsShouldSendCartEvent?.data) { 6431 AB.DATALAYER.setAnalyticsCrossPageData({ action: 'remove', name: 'subsShouldSendCartEvent' }); 6432 return; 6433 }
6434 if (AB.DATALAYER.initCartAndCheckoutPages) { 6435 AB.DATALAYER.initCartAndCheckoutPages(); 6436 } 6437 const isSelectiveCart = cart?.selectiveCartBannerEnabled && cart?.selectiveCartEnabled && AB.DATALAYER.analyticsGetURLParameterByName('selective') == 'true'; 6438 window['analyticsCartnumber'] = window['analyticsNumberOfCarts'] == 1 ? 1 : isSelectiveCart ? window['analyticsNumberOfCarts'] : window['analyticsCartnumber'] && window['analyticsNumberOfCarts'] && window['analyticsNumberOfCarts'] > 1 && window['analyticsCartnumber'] < window['analyticsNumberOfCarts'] ? Number(window['analyticsCartnumber']) + 1 : window['analyticsCartnumber'] && window['analyticsNumberOfCarts'] && window['analyticsCartnumber'] == window['analyticsNumberOfCarts'] ? window['analyticsCartnumber'] : 1; 6439 6440 window['analyticsSellerIds'] = window['analyticsSellerIds'] ? window['analyticsSellerIds'] : localStorage.getItem('analyticsSellerIds') && localStorage.getItem('analyticsSellerIds').split(',').length > 0 ? localStorage.getItem('analyticsSellerIds').split(',') : []; 6441 digitalData.cart.marketplace = digitalData.cart.marketplace || {}; 6442 digitalData.cart.cartInfo.cartIDMap = digitalData.cart.cartInfo.cartIDMap || {}; 6443 6444 if (Object.keys(digitalData.cart.marketplace).length === 0 && localStorage.getItem('analyticsSellerCartsInfo') && localStorage.getItem('analyticsSellerCartsInfo') !== 'undefined') { 6445 digitalData.cart.marketplace = AB.DATALAYER.safeJSONParse(AB.DATALAYER.getLSItem('analyticsSellerCartsInfo')); 6446 } 6447 digitalData.cart.sellerCarts = digitalData.cart.sellerCarts || []; 6448 6449 AB.DATALAYER.updateCartType(isSelectiveCart ? 'selective_cart' : 'Full_Cart'); 6450 AB.DATALAYER.setCartObj(cart, null, false, isEditCart); 6451 if (isSelectiveCart) { 6452 digitalData.pageFilter.topNav = AB.DATALAYER.initTopNav(); 6453 digitalData.pageDetails.subSection2 = 'selective-cart'; 6454 AB.DATALAYER.setPageName(false, true); 6455 this.setAnalyticsCrossPageData({ action: 'set', name: 'analyticsFeatureTracking', data: 'selectiveCartACIP462764' }); 6456 } else { 6457 digitalData.pageDetails.subSection2 = ''; 6458 } 6459 this.setPromoBannerImpressions(cart); 6460 6461 //if there is a banner related to the 'Disclaimer for WA DUG orders containing Alcohol' add alcohol-disclaimer to impressions 6462 if (cart?.alcDisclamer && cart?.serviceType?.toLowerCase() === 'dug') { 6463 digitalData.pageFilter.impressions = digitalData.pageFilter.impressions 6464 ? `${digitalData.pageFilter.impressions},alcohol-disclaimer` 6465 : 'alcohol-disclaimer'; 6466 } 6467 6468 if ((window['analyticsNumberOfCarts'] && Number(window['analyticsCartnumber']) === Number(window['analyticsNumberOfCarts'])) || (!window['analyticsNumberOfCarts'])) { 6469 digitalData.pageDetails.sellerId = window['analyticsSellerIds'].join(','); 6470 digitalData.cart.cartInfo.currentCartId = 6471 digitalData.cart.cartInfo.cartIDMap 6472 ? Object.values(digitalData.cart.cartInfo.cartIDMap).join() 6473 : ''; 6474 sellerCartsCombined = Array.prototype.concat.apply([], Object.values(digitalData.cart.sellerCarts)); 6475 digitalData.cart.item = (digitalData.cart?.absItems && Array.isArray(digitalData.cart.absItems) ? digitalData.cart.absItems : []).concat(sellerCartsCombined); 6476 if (digitalData.cart.marketplace && Object.keys(digitalData.cart.marketplace).length > 0) { 6477 for (let i = 1; i <= 8; i++) { 6478 if (digitalData.cart.marketplace[`seller${i}`] && window['analyticsSellerIds'].indexOf(digitalData.cart.marketplace[`seller${i}`].sellerId) === -1) { 6479 delete digitalData.cart.marketplace[`seller${i}`]; 6480 } 6481 } 6482 localStorage.setItem('analyticsSellerCartsInfo', JSON.stringify(window.digitalData.cart.marketplace)); 6483 this.setAnalyticsCrossPageData({ action: 'set', name: 'analyticsSellerCartsInfo', data: window.digitalData.cart.marketplace, isAcrossPages: true }); 6484 } 6485 this.setLSItem('analyticsSellerCartsInfo', JSON.stringify(digitalData.cart.marketplace)); 6486 AB.DATALAYER.setPreviousPageName(); 6487 AB.DATALAYER.setRestrictedPageCnc(); 6488 6489 this.pushEvent(isEditCart ? AB.DATALAYER.EVENTS.EDIT_CART : isSelectiveCart ? AB.DATALAYER.EVENTS.SELECTIVE_CART : AB.DATALAYER.EVENTS.FULL_CART, { 6490 page: digitalData.pageDetails, 6491 pageFilter: digitalData.pageFilter, 6492 user: digitalData.user, 6493 productInfo: digitalData.productInfo, 6494 modal: digitalData.modal, 6495 cart: digitalData.cart 6496 }); 6497 } 6498 } 6499 6500 this.resetPfmDetailsAndMartechInfo = function () { 6501 if (digitalData?.martech) { 6502 delete digitalData.martech; 6503 } 6504 if (digitalData?.productInfo?.pfmDetail) { 6505 delete digitalData.productInfo.pfmDetail; 6506 } 6507 if (digitalData?.pageFilter) { 6508 delete digitalData.pageFilter?.mediaType; 6509 delete digitalData.pageFilter?.placement; 6510 delete digitalData.pageFilter?.recommendationType; 6511 } 6512 } 6513 6514 this.sendUndoAnalytics = function (cartData, cartItem, addRemovedItemToCart) { 6515 if (addRemovedItemToCart === true && cartItem) { 6516 let qty = cartItem.qty ? cartItem.qty : cartItem.quantity; 6517 if (qty > 0) { 6518 for (let i = 0; i < qty; i++) { 6519 if (i === 0) { 6520 this.addItemToCart(cartItem, undefined, undefined, cartData, 1, true); 6521 this.resetPfmDetailsAndMartechInfo(); 6522 } else { 6523 this.increaseItemQuantity(cartItem, cartData, 1, true); 6524 } 6525 } 6526 } 6527 } 6528 } 6529 6530 this.initCoupons = function (couponCount, couponsApplied) { 6531 digitalData.cart.savings.totalCoupons = couponCount + ''; 6532 digitalData.cart.savings.couponsApplied = couponsApplied + ''; 6533 } 6534 6535 /** 6536 * Substitution impressions on modal view 6537 */
6538 this.setSubstitutionImpressionClick = function (substitutionData, substitutePreference) { 6539 let productID = window.sessionStorage.getItem('analytics_subs_prodId') || ''; 6540 const substitution = []; 6541 substitutionData?.forEach((product, i) => { 6542 substitution.push({ 6543 isSubstitution: product?.Subflag ? 'true' : 'false', 6544 productID: productID, 6545 substitutionOption: `${substitutePreference}:${product?.id}`, 6546 rowSlot: `r${i + 1 > 9 ? i + 1 : '0' + (i + 1)}#s01`, 6547 impressionAdditionalDetail: product?.adobeMetrics?.metricA || 'no-value#no-value#no-value#no-value#no-value#no-value#no-value', 6548 subsAdditionalDetail: product?.adobeMetrics?.metricB || 'no-value#no-value#no-value#no-value#no-value#no-value#no-value#no-value#no-value' 6549 6550 }) 6551 }) 6552 if (substitution.length > 0) { 6553 this.pushEvent('linkClick', 6554 { 6555 link: { 6556 linkName: 'cart-substitution-impressions' 6557 }, 6558 productInfo: { 6559 substitution: substitution 6560 } 6561 }); 6562 } 6563 } 6564 6565 /** 6566 * Substitution impressions on search 6567 * To do: merge substitution impresssion methods 6568 */ 6569 this.setSubstitutionImpressionEvent = function (subsImpressionData) { 6570 if (subsImpressionData?.data?.length > 0) { 6571 const substitution = []; 6572 subsImpressionData?.data?.forEach((product, i) => { 6573 substitution.push({ 6574 isSubstitution: `${subsImpressionData?.subConfirmFlag}`, 6575 productID: subsImpressionData?.productID, 6576 substitutionOption: `${subsImpressionData?.substitutionValue}:${product?.id}`, 6577 searchTerm: subsImpressionData?.searchTerm, 6578 searchResults: subsImpressionData?.searchResults, 6579 rowSlot: `r${i > 9 ? i : '0' + i}#s01`, 6580 impressionAdditionalDetail: product?.adobeMetrics?.metricA || 'no-value#no-value#no-value#no-value#no-value#no-value#no-value', 6581 subsAdditionalDetail: product?.adobeMetrics?.metricB || 'no-value#no-value#no-value#no-value#no-value#no-value#no-value#no-value#no-value#no-value#no-value' 6582 }) 6583 }) 6584 this.pushEvent( 6585 this.EVENTS.LINK_CLICK, { 6586 link: { 6587 linkName: 'cart-substitution-impressions' 6588 }, 6589 productInfo: { 6590 substitution: substitution 6591 } 6592 } 6593 ); 6594 } 6595 } 6596 6597 this.setCheckoutPaymentAccordion = function (updateTopNav = false) { 6598 digitalData.pageDetails.subSection3 = 'saved-payments'; 6599 6600 digitalData.pageFilter.topNav = updateTopNav ? 'cta:checkout|payment|arrow' : ''; 6601 const payload = { 6602 link: { 6603 linkName: 'checkout-payment-saved-payments' 6604 } 6605 }; 6606 this.pushEvent(this.EVENTS.LINK_CLICK, payload); 6607 } 6608 6609 this.setSnsProductInStorage = function (productId, calledBy) { 6610 const snsProductObject = AB.DATALAYER.safeJSONParse(AB.DATALAYER.getLSItem('snsSubscriptionProducts')); 6611 const isFullCartChecked = this.isCartAndCheckoutPage('erums/cart') && (document.querySelector(`#sns-checkbox-${productId}`)?.checked || calledBy === 'frequencyModal'); 6612 const isPdpChecked = this.isPdp() && document.querySelector('.product-info[data-pagetype="pdp"] .sns-container input.checkbox__input')?.checked; 6613 6614 if (productId) { 6615 if (isFullCartChecked || isPdpChecked) { 6616 snsProductObject[productId] = isFullCartChecked ? 'full-cart' : isPdpChecked ? 'pdp' : ''; 6617 } else if (snsProductObject.hasOwnProperty(productId)) { 6618 delete snsProductObject[productId] 6619 } 6620 } 6621 localStorage.setItem('snsSubscriptionProducts', JSON.stringify(snsProductObject)) 6622 } 6623 6624 this.snsFrequencyCheckboxClick = function (linkName, subscribe, item, frequency, isSnSmodal) { 6625 const eventSent = appEventData.some(eventObj => eventObj?.link?.linkName === linkName); 6626 this.setSnsProductInStorage(item?.itemId); 6627 const snsFrequency = frequency ? frequency.toLowerCase().replace(/\s/g, '-') : item?.snsSub?.snsFrequency?.unit?.toLowerCase().replace(/\s/g, '-'); 6628 linkName = `${linkName}${subscribe === 'subscribe' && isSnSmodal ? '|subscribed' : ''}`; 6629 if (!eventSent && !isSnSmodal) { 6630 this.pushEvent('linkClick', 6631 { 6632 link: { 6633 linkName: linkName 6634 }, 6635 productInfo: { 6636 productID: item?.itemId, 6637 scheduleandSaveSource: `full-cart#${subscribe}#every-${snsFrequency}` 6638 } 6639 }); 6640 } 6641 if (isSnSmodal) { 6642 this.pushEvent('modalClick', 6643 { 6644 modal: { 6645 modalAction: linkName, 6646 modalMessage: frequency === 'week' ? 'frequency-not-modified' : 'frequency-modified' 6647 }, 6648 productInfo: { 6649 productID: item?.itemId, 6650 scheduleandSaveSource: `cart#${subscribe}#every-${snsFrequency}` 6651 } 6652 }); 6653 } 6654 if (linkName === 'ar:unsubscribe-schedule-and-save') { 6655 this.updateScheduledProductsCount('unsubscribe'); 6656 } 6657 } 6658 6659 this.getDefautTipping = function (checkout) { 6660 if (checkout.isDriverTippingEnabled && checkout.driverTipInfo?.driverTipOptions) { 6661 const driverTipOptions = checkout.driverTipInfo.driverTipOptions; 6662 const driverTipLookup = driverTipOptions.filter((tip) => { if (tip.tipSelected === true) { return tip } }); 6663 const selectedOption = driverTipLookup.length > 0 ? driverTipLookup[0] : {}; 6664 const tipPercentage = selectedOption?.tipPercentage ? (selectedOption.tipPercentage.toLowerCase() === 'other' ? 'default' : selectedOption.tipPercentage) : 'no-option'; 6665 const tipAmount = selectedOption?.tipAmount && !isNaN(parseFloat(selectedOption?.tipAmount)) ? Number(selectedOption.tipAmount).toFixed(2) : 'zero'; 6666 const defaultTippingMode = `${tipPercentage}|${tipAmount}`; 6667 this.setLSItem('analyticsTippingMode', defaultTippingMode); 6668 this.setAnalyticsCrossPageData({ action: 'set', name: 'analyticsTippingMode', data: defaultTippingMode, isAcrossPages: true }); 6669 } 6670 } 6671 6672 this.totalPromoSavings = function (promotions) { 6673 let totalPromotions = []; 6674 if (promotions && Array.isArray(promotions) && promotions.length > 0) { 6675 promotions.forEach((promotion) => { 6676 totalPromotions.push(promotion.discount || 0); 6677 }); 6678 } 6679 return totalPromotions.reduce(AB.DATALAYER.safeReducer, 0); 6680 } 6681 6682 this.allPromoCodes = function (promotions) { 6683 const allPromoCodes = []; 6684 if (promotions && promotions.length) { 6685 promotions.forEach((promotion) => { 6686 allPromoCodes.push(promotion.code); 6687 }); 6688 } 6689 return allPromoCodes.toString(); 6690 } 6691 6692 this.frequencyModalTracking = function (event, modalAction, item, frequency, modalMessage) { 6693 const subscribe = `${modalAction === 'ar:subscribe-schedule-and-save|view' ? 'un' : ''}subscribe`; 6694 const frequencyModalObj = { 6695 modal: { 6696 modalAction: modalAction, 6697 modalMessage: modalMessage || '' 6698 }, 6699 productInfo: { 6700 productID: item?.itemId, 6701 scheduleandSaveSource: `full-cart#${subscribe}#${frequency?.count && frequency?.count !== 1 ? `every-${frequency.count}-weeks` : 'every-1-week'}` 6702 } 6703 } 6704 this.pushEvent(event, frequencyModalObj); 6705 if (modalAction === 'ar:subscribe-schedule-and-save|subscribed') { 6706 this.setSnsProductInStorage(item?.itemId, 'frequencyModal'); 6707 this.updateScheduledProductsCount('subscribe'); 6708 } 6709 } 6710 6711 this.updateScheduledProductsCount = function (action) { 6712 const scheduledProductsCount = Number(this.getLSItem('analyticsscheduledProducts')) || 0; 6713 const updatedScheduledProductsCount = action === 'unsubscribe' ? scheduledProductsCount - 1 : scheduledProductsCount + 1; 6714 this.setLSItem('analyticsscheduledProducts', updatedScheduledProductsCount); 6715 } 6716
6717 this.setExperimentExposureLoaded = function (carousels) { 6718 carousels.forEach(carousel => { 6719 if (carousel.p13nAdobeMetrics?.msgExperiment) { 6720 this.setP13nExperimentExposure(carousel); 6721 } 6722 }); 6723 } 6724 6725 this.setMarketplaceCartInfo = function (orderInfo, orderSummary, cartType) { 6726 if ((orderInfo.seller && orderInfo.seller.sellerName) || (orderInfo.cartCategory === '1P_WINE')) { 6727 const marketplaceCartId = orderInfo.cartId; 6728 const sellerId = orderInfo.seller && orderInfo.seller.sellerId ? orderInfo.seller.sellerId : orderInfo.cartCategory === '1P_WINE' ? '22222222' : AB.DATALAYER.getBannerSellerId(); 6729 let marketplaceCartItems; 6730 if (cartType === 'checkout') { 6731 marketplaceCartItems = orderInfo.cartItems || []; 6732 } else { 6733 marketplaceCartItems = orderInfo.orderItems || []; 6734 } 6735 const marketplaceCartTotal = orderSummary.totalBasePrice.toString(); 6736 let marketplaceCartSKUs = []; 6737 marketplaceCartItems.forEach(item => { 6738 marketplaceCartSKUs.push(item.itemId); 6739 }); 6740 const marketplaceCartInfo = `${sellerId}#${marketplaceCartId}#${cartType}#${marketplaceCartSKUs.join(':')}#${marketplaceCartTotal}`; 6741 if (sellerId === '22222222') { 6742 digitalData.cart.cartInfo.cartIDWineShop = `${sellerId}#${marketplaceCartId}#${cartType}#${marketplaceCartSKUs.join(',')}#${marketplaceCartTotal}`; 6743 digitalData.cart.transaction.subTotalWineCart = 6744 digitalData.cart.transaction.subTotalWineCart || {}; 6745 digitalData.cart.transaction.subTotalWineCart.subTotal = +marketplaceCartTotal; 6746 digitalData.cart.transaction.subTotalWineCart.sellerId = sellerId; 6747 } else if (sellerId && sellerId !== '22222222' && sellerId !== `${AB.DATALAYER.getBannerSellerId()}`) { 6748 digitalData.cart.marketplace = digitalData.cart.marketplace || {}; 6749 if (Object.keys(digitalData.cart.marketplace).length === 0 && this.getLSItem('analyticsSellerCartsInfo') && this.getLSItem('analyticsSellerCartsInfo') !== 'undefined') { 6750 digitalData.cart.marketplace = this.safeJSONParse(this.getLSItem('analyticsSellerCartsInfo')) || digitalData.pageFlags.analyticsSellerCartsInfo.flagValue; 6751 } 6752 for (let i = 1; i <= 8; i++) { 6753 if (digitalData.cart.marketplace[`seller${i}`] && digitalData.cart.marketplace[`seller${i}`].sellerId === sellerId) { 6754 digitalData.cart.marketplace[`seller${i}`].sellerCartID = marketplaceCartInfo; 6755 break; 6756 } 6757 } 6758 if (digitalData.cart.transaction) { 6759 const marketplaceCart = digitalData.cart.marketplace; 6760 for (const key in marketplaceCart) { 6761 const sellerInfo = marketplaceCart[key]; 6762 const sellerSubTotal = sellerInfo.sellerCartID.split('#').pop(); 6763 const subTotalMarketplaceCarts = digitalData.cart.transaction.subTotalMarketplaceCarts; 6764 digitalData.cart.transaction.subTotalMarketplaceCarts = subTotalMarketplaceCarts 6765 ? `${subTotalMarketplaceCarts}#${sellerInfo.sellerId}:${sellerSubTotal}` 6766 : `${sellerInfo.sellerId}:${sellerSubTotal}`; 6767 } 6768 } 6769 this.setLSItem('analyticsSellerCartsInfo', JSON.stringify(digitalData.cart.marketplace)); 6770 this.setAnalyticsCrossPageData({ action: 'set', name: 'analyticsSellerCartsInfo', data: digitalData.cart.marketplace, isAcrossPages: true }); 6771 } 6772 } 6773 } 6774 6775 this.getPaymentUpdatedStatus = function () { 6776 const paymentUpdated = this.getLSItem('edit-order-payment-updated') || this.getLSItem('checkout-order-payment-updated') || digitalData.crossPageData?.confirmationPaymentUpdated?.data; 6777 return `${this.isCartAndCheckoutPage('order/confirmation') ? '' : 'edit-'}order-confirmation|payment-arrow-${paymentUpdated ? '' : 'not-'}selected`; 6778 } 6779 6780 this.updateDefaultStoreValues = function (serviceType, deliveryType, premiumSelected, storeId) { 6781 digitalData.user.deliveryPreference = serviceType || ''; 6782 digitalData.user.deliveryCombo = deliveryType || ''; 6783 digitalData.pageFilter.premiumSlots = premiumSelected || ''; 6784 digitalData.user.shopStoreId = storeId?.toString() || ''; 6785 } 6786 6787 this.updateSaveSlot = function (slot) { 6788 this.setDeliveryWindow(slot); 6789 this.pushEvent(this.EVENTS.LINK_CLICK, { 6790 link: { 6791 linkName: 'prebook-update' 6792 }, 6793 page: digitalData?.pageDetails, 6794 cart: { 6795 transaction: digitalData?.cart?.transaction 6796 }, 6797 user: digitalData?.user 6798 }); 6799 } 6800 6801 this.updateSlotPlan = function (slotPlanValue) { 6802 digitalData.pageFilter.premiumSlots = slotPlanValue === 'false' ? '' : 'true'; 6803 } 6804 6805 this.updateCheckout = function (checkout) { 6806 digitalData.productInfo.substitution = digitalData.productInfo.substitution || {}; 6807 const sellerId = checkout?.seller?.sellerId ? checkout.seller.sellerId : checkout?.cartCategory === '1P_WINE' ? '22222222' : AB.DATALAYER.getBannerSellerId(); 6808 6809 if (this.getLSItem('erums-referral') || digitalData.crossPageData?.erumsReferral) { 6810 digitalData.pageDetails.referringURL = this.getLSItem('erums-referral') || digitalData.crossPageData?.erumsReferral?.data; 6811 } 6812 6813 if (checkout?.eliteSlots?.fulfillmentType?.delivery && Array.isArray(checkout.eliteSlots.fulfillmentType.delivery) && checkout.eliteSlots.fulfillmentType.delivery.length > 0) {
6814 checkout.eliteSlots.fulfillmentType.delivery.forEach(slot => { 6815 if (slot.type === 'FLASH') { 6816 this.isFlashSlotAvailable = true; 6817 } 6818 }); 6819 } 6820 6821 if (checkout?.slots?.reservedSlot) { 6822 digitalData.pageFilter.premiumSlots = this.getLSItem('dtm-premiumslots') || digitalData.crossPageData?.dtmPremiumslots?.data; 6823 } 6824 6825 digitalData.pageDetails.sellerId = sellerId; 6826 digitalData.user.deliveryPreference = checkout.serviceType || ''; 6827 digitalData.user.deliveryCombo = checkout.deliveryType || ''; 6828 digitalData.user.shopStoreId = checkout?.storeId?.toString(); 6829 6830 if (checkout.orderSummary) { 6831 let summary = checkout.wysiwygOrderSummary || checkout.orderSummary; 6832 digitalData.pageFilter.promoCode = this.allPromoCodes(summary.promotions); 6833 6834 digitalData.cart.transaction.subscriptionStatus = checkout.customerPreferences?.subscriptionType ? checkout.customerPreferences.subscriptionType : 'no subscription'; 6835 digitalData.cart.transaction.paymentMethod = (checkout.orderSummary.orderSummary) ? 'cc & coa' : 'cc'; 6836 6837 if (checkout?.slots?.reservedSlot) { 6838 let reservedSlotInfo = checkout?.slots?.reservedSlot ? this.safeJSONParse(JSON.stringify(checkout.slots.reservedSlot)) : {}; 6839 6840 if (checkout?.slots?.serviceType === 'DUG' && checkout?.slots?.isPickupStore === false) { 6841 let lastPickupTS = ''; 6842 if (checkout?.slots?.availableSlots && Array.isArray(checkout.slots.availableSlots) && checkout.slots.availableSlots.length > 0) { 6843 checkout.slots.availableSlots?.forEach(function (s) { 6844 for (let slot in s) { 6845 if (s[slot].displayLastPickupTime) { 6846 lastPickupTS = s[slot].displayLastPickupTime; 6847 } 6848 } 6849 }); 6850 } 6851 if (lastPickupTS) { 6852 reservedSlotInfo['lastPickupTS'] = lastPickupTS; 6853 } 6854 } 6855 this.setDeliveryWindow(reservedSlotInfo); 6856 } 6857 6858 if (checkout.orderSummary?.deliveryFee) { 6859 digitalData.cart.transaction.shippingFee = !checkout.orderSummary.deliveryFee.feeWaived ? checkout.orderSummary.deliveryFee.actualFee?.toString() : checkout.orderSummary.deliveryFee.currentFee?.toString(); 6860 } 6861 if (checkout.orderSummary?.fuelSurcharge) { 6862 digitalData.cart.transaction.fuelSurcharge = !checkout.orderSummary.fuelSurcharge.feeWaived ? checkout.orderSummary?.fuelSurcharge?.actualFee?.toString() : checkout.orderSummary.fuelSurcharge.currentFee?.toString(); 6863 } 6864 if (checkout.orderSummary?.serviceFee) { 6865 digitalData.cart.transaction.serviceFee = checkout.orderSummary.serviceFee && !checkout.orderSummary.serviceFee.feeWaived ? checkout.orderSummary?.serviceFee?.actualFee?.toString() : checkout.orderSummary.serviceFee.currentFee?.toString(); 6866 } 6867 6868 let totalSavings = summary.totalCardSavings && !isNaN(parseFloat(summary.totalCardSavings)) ? parseFloat(summary.totalCardSavings) : summary.estimatedClubCardSavings && !isNaN(parseFloat(summary.estimatedClubCardSavings)) ? parseFloat(summary.estimatedClubCardSavings) : 0; 6869 let totalPromos = this.totalPromoSavings(summary.promotions); 6870 let totalPrice = summary.totalBasePrice && !isNaN(parseFloat(summary.totalBasePrice)) ? parseFloat(summary.totalBasePrice) : 0; 6871 6872 digitalData.cart.savings.clubcardSavings = totalSavings?.toString(); 6873 digitalData.cart.savings.promocodeSavings = totalPromos?.toString(); 6874 digitalData.cart.savings.totalSavings = this.cleanPrice(totalSavings + totalPromos)?.toString(); 6875 digitalData.cart.cartInfo.totalunits = summary.totalItemQuantity?.toString(); 6876 digitalData.cart.transaction.subTotal = totalPrice.toString(); 6877 digitalData.cart.transaction.revenue = this.cleanPrice(totalPrice - (totalSavings + totalPromos))?.toString(); 6878 6879 this.setMarketplaceCartInfo(checkout, summary, 'checkout'); 6880 } 6881 6882 digitalData.cart.cartInfo.cartId = checkout.cartId; 6883 digitalData.cart.cartInfo.currentCartId = checkout?.cartId?.toString(); 6884 digitalData.cart.cartInfo.cartType = digitalData.pageDetails.subSection2; 6885 digitalData.productInfo.substitution.isSubstitution = checkout?.['isSubstitutions2Enabled'] || ''; 6886 6887 this.getDefautTipping(checkout); 6888 if (this.getLSItem('abOrderCount') || digitalData.crossPageData?.abOrderCount) { 6889 const orderStatus = this.getLSItem('abOrderCount') || digitalData.crossPageData?.abOrderCount?.data; 6890 this.setLSItem('abOrderCountAnalyticsCC', orderStatus); 6891 this.setAnalyticsCrossPageData({ action: 'set', name: 'abOrderCountAnalyticsCC', data: orderStatus, isAcrossPages: true }); 6892 } 6893 } 6894 6895 this.updateDigitalData = function ({ checkoutData, ACTION, PAGE } = {}) { 6896 digitalData.productInfo.substitution = digitalData.productInfo.substitution || {}; 6897 let fullDeliveryTime = '', actionType = ACTION === 'RESCHEDULE' ? 'reschedule' : 'edit', reservedSlotInfo; 6898 let event = ''; 6899 //Here we should call initCartAndCheckoutPages() in order to set the values for this page but for the moment we leave this here 6900 6901 this.initCartAndCheckoutPages(); 6902 6903 switch (ACTION) { 6904 case 'CONFIRM': 6905 case 'CANCEL': 6906 digitalData.pageDetails.subSection1 = 'edit order'; 6907 digitalData.pageDetails.subSection2 = `edit ${PAGE === 'editSlot' ? 'date and time' : PAGE === 'editCheckout' ? 'order checkout' : ''}`; 6908 this.setPageName('', true, undefined); 6909 event = ACTION === 'CONFIRM' ? 'confirm_changes' : ACTION === 'CANCEL' ? 'cancel_edits' : ''; 6910 break; 6911 case 'LOAD': 6912 digitalData.pageDetails.subSection1 = 'edit order'; 6913 digitalData.pageDetails.subSection2 = `edit ${PAGE === 'editSlot' ? 'delivery date and time' : 'order '}${PAGE === 'editCheckout' ? 'checkout' : PAGE === 'editCheckoutConfirmation' ? 'confirmation' : ''}`; 6914 event = PAGE === 'editSlot' ? 'edit_order_date_time' : PAGE === 'editCheckout' || PAGE === 'editCheckoutConfirmation' ? digitalData.pageDetails.subSection2.replaceAll(' ', '_') : ''; 6915 this.setPageName('', true, undefined); 6916 break; 6917 case 'RESCHEDULE': 6918 digitalData.pageDetails.subSection1 = 'reschedule order'; 6919 digitalData.pageDetails.subSection2 = `reschedule ${PAGE === 'expired' ? 'order expired' : 'delivery date and time'}`; 6920 event = this.EVENTS.PAGE_LOADED; 6921 this.setPageName('', true, undefined); 6922 break; 6923 } 6924 6925 digitalData.cart.cartInfo.cartId = ''; 6926 digitalData.cart.cartInfo.currentCartId = ''; 6927 6928 if (checkoutData.order) { 6929 digitalData.user.deliveryPreference = checkoutData.order.serviceType; 6930 digitalData.user.deliveryCombo = checkoutData.order.deliveryType; 6931 digitalData.user.shopStoreId = checkoutData.order.storeId.toString(); 6932 digitalData.cart.transaction.orderID = checkoutData.order.orderId; 6933 digitalData.cart.cartInfo.cartId = checkoutData.order.cartId; 6934 digitalData.cart.cartInfo.currentCartId = checkoutData.order.cartId?.toString(); 6935 } else if (checkoutData) { 6936 digitalData.user.deliveryPreference = checkoutData.serviceType; 6937 digitalData.user.deliveryCombo = checkoutData.deliveryType; 6938 digitalData.user.shopStoreId = (checkoutData.storeId || checkoutData.storeInfo.storeId)?.toString(); 6939 } 6940 if (checkoutData.orderSummary) { 6941 let summary = checkoutData.wysiwygOrderSummary || checkoutData.orderSummary, totalSavings = 0, totalPromos = this.totalPromoSavings(summary.promotions); 6942 6943 digitalData.pageFilter.promoCode = this.allPromoCodes(checkoutData.orderSummary.promotions); 6944 6945 digitalData.cart.transaction.orderID = checkoutData.orderId ? checkoutData.orderId : ''; 6946 if (checkoutData.orderSummary.deliveryFee) { 6947 digitalData.cart.transaction.shippingFee = (!checkoutData.orderSummary.deliveryFee.feeWaived) ? checkoutData.orderSummary.deliveryFee.actualFee?.toString() : checkoutData.orderSummary.deliveryFee.currentFee?.toString(); 6948 } 6949 if (checkoutData.orderSummary.fuelSurcharge) { 6950 digitalData.cart.transaction.fuelSurcharge = (!checkoutData.orderSummary.fuelSurcharge.feeWaived) ? checkoutData.orderSummary.fuelSurcharge.actualFee?.toString() : checkoutData.orderSummary.fuelSurcharge.currentFee?.toString(); 6951 } 6952 if (checkoutData.orderSummary.serviceFee) { 6953 digitalData.cart.transaction.serviceFee = (checkoutData.orderSummary.serviceFee && !checkoutData.orderSummary.serviceFee.feeWaived) ? checkoutData.orderSummary.serviceFee.actualFee?.toString() : checkoutData.orderSummary.serviceFee.currentFee?.toString(); 6954 } 6955 digitalData.cart.transaction.deliveryTime = fullDeliveryTime; 6956 digitalData.cart.transaction.paymentMethod = (checkoutData.orderSummary.orderSummary) ? 'cc & coa' : 'cc'; 6957 6958 totalSavings = (summary.cardSavings || summary.totalCardSavings !== undefined) ? summary.totalCardSavings : summary.estimatedClubCardSavings; 6959 6960 digitalData.cart.savings.clubcardSavings = totalSavings?.toString(); 6961 digitalData.cart.savings.promocodeSavings = totalPromos?.toString(); 6962 digitalData.cart.savings.totalSavings = this.cleanPrice(totalSavings + totalPromos)?.toString(); 6963 6964 digitalData.cart.transaction.subTotal = summary.totalBasePrice?.toString(); 6965 digitalData.cart.cartInfo.totalUnits = summary.totalItemQuantity?.toString(); 6966 digitalData.cart.transaction.revenue = this.cleanPrice(summary.totalBasePrice - (totalSavings + totalPromos))?.toString(); 6967 6968 } 6969 digitalData.cart.cartInfo.cartType = actionType + '_date_time'; 6970 6971 if (checkoutData.order) { 6972 if (checkoutData.order.orderId.length) { 6973 event = 'edit_order_confirmation'; 6974 digitalData.cart.transaction.editOrderID = `${checkoutData.order.orderId}|${checkoutData.order.versionNumber}`; 6975 } 6976 if (checkoutData.order.orderItems.length) { 6977 digitalData.cart.item = this.getAllItems(checkoutData.order); 6978 } 6979 } 6980 reservedSlotInfo = checkoutData?.order?.deliveryInfo?.slotInfo ? AB.DATALAYER.safeJSONParse(JSON.stringify(checkoutData.order.deliveryInfo.slotInfo)) : checkoutData?.deliveryInfo?.slotInfo ? AB.DATALAYER.safeJSONParse(JSON.stringify(checkoutData.deliveryInfo.slotInfo)) : {}; 6981 6982 if (reservedSlotInfo) { 6983 if ((checkoutData.serviceType === 'DUG' && checkoutData.isPickupStore === false) || (checkoutData.order?.serviceType === 'DUG' && checkoutData.order?.isPickupStore === false)) { 6984 reservedSlotInfo['lastPickupTS'] = reservedSlotInfo['displayLastPickupTime']; 6985 } 6986 this.setDeliveryWindow(reservedSlotInfo); 6987 } 6988 6989 if (event === 'edit_order_checkout') { 6990 const isPaypalPayment = checkoutData.customerPreferences?.fundsAllocation?.tenderAllocationSummary?.some((x) => x.paymentSubType === 'PAYPAL') 6991 this.setPaymentMethodPaypal('edit_checkout', isPaypalPayment) 6992 } else if (event === 'edit_order_confirmation') { 6993 digitalData.cart.transaction.paymentMethod = this.getPaymentMethod(checkoutData.order.payments).method; 6994 } 6995 AB.DATALAYER.setPreviousPageName(); 6996 if (event === 'edit_cart' || event === 'edit_order_confirmation') { 6997 AB.DATALAYER.setRestrictedPageCnc(); 6998 } 6999 7000 this.pushEvent( 7001 event, { 7002 page: digitalData?.pageDetails, 7003 pageFilter: digitalData?.pageFilter, 7004 user: digitalData?.user, 7005 productInfo: digitalData?.productInfo, 7006 modal: digitalData?.modal, 7007 cart: digitalData?.cart 7008 } 7009 ); 7010 } 7011 7012 this.confirmationPage = function ({ orderData, isRoktEnabled } = {}) { 7013 const { pageDetails, pageFilter, user, cart, crossPageData } = digitalData; 7014 7015 this.initCartAndCheckoutPages({ pageSpecificData: orderData }); 7016 7017 const order = orderData.order, 7018 isWysiwyg = orderData.isWYSIWYGEnabled, 7019 orderSummary = orderData.wysiwygOrderSummary; 7020 7021 pageDetails.sellerId = order?.seller?.sellerId ? order?.seller?.sellerId : order?.cartCategory === '1P_WINE' ? '22222222' : AB.DATALAYER.getBannerSellerId(); 7022 pageFilter.premiumSlots = this.getLSItem('dtm-premiumslots') || crossPageData?.dtmPremiumslots?.data || ''; 7023 7024 cart.isWYSIWYGEnabled = orderData.isWYSIWYGEnabled; 7025 cart.transaction.orderID = order.orderId; 7026 cart.cartInfo.cartId = order.cartId || ''; 7027 cart.cartInfo.currentCartId = order?.cartId?.toString() || ''; 7028 if (order?.snsSubExists === true) { 7029 cart.cartInfo.scheduleandsaveproducts = order.snsSubCount || ''; 7030 } 7031 if (order.deliveryInfo?.slotInfo?.displayStartTS) { 7032 let reservedSlotInfo = order?.deliveryInfo?.slotInfo ? AB.DATALAYER.safeJSONParse(JSON.stringify(order.deliveryInfo.slotInfo)) : {}; 7033 if (order.serviceType === 'DUG' && order.isPickupStore === false && reservedSlotInfo.displayLastPickupTime) { 7034 reservedSlotInfo['lastPickupTS'] = reservedSlotInfo['displayLastPickupTime']; 7035 } 7036 this.setDeliveryWindow(reservedSlotInfo); 7037 } 7038 7039 user.shopStoreId = order.storeId; 7040 user.deliveryPreference = order.serviceType; 7041 user.deliveryCombo = order.deliveryType; 7042 user.isGiftOrder = order.deliveryInfo?.gift?.isGift; 7043 cart.transaction.deliverySlotType = sessionStorage.getItem('elite_tag') || 'more-delivery-times'; 7044 7045 // payment method 7046 cart.transaction.paymentMethod = this.getPaymentMethod(order.payments).method; 7047 if (order.isSnap2) { 7048 cart.transaction.paymentType = this.getPaymentMethod(order.payments).type; 7049 } 7050 if (order.orderItems?.length) { 7051 if (isWysiwyg) {
7052 order.orderItems.forEach(orderItem => { 7053 let product = this.setCartItemsWYSIWYG(orderItem, pageDetails.sellerId); 7054 cart?.item?.push(product); 7055 }); 7056 } else { 7057 order.orderItems?.forEach(orderItem => { 7058 let product = this.setCartItemsNonWYSIWYG(orderItem, pageDetails.sellerId); 7059 cart?.item?.push(product); 7060 }); 7061 } 7062 } 7063 7064 this.updateCartSavings(0, 0, orderData); 7065 pageFilter.promoCode = this.allPromoCodes(orderSummary?.promotions); 7066 cart.transaction.subTotal = orderSummary?.totalBasePrice?.toString(); 7067 cart.cartInfo.totalUnits = orderSummary?.totalItemQuantity?.toString(); 7068 cart.transaction.revenue = this.cleanPrice(orderSummary?.totalBasePrice - cart.savings?.totalSavings)?.toString(); 7069 cart.transaction.isPromoApplied = !isNaN(parseFloat(digitalData?.cart?.savings?.promocodeSavings)) && Number(digitalData?.cart?.savings?.promocodeSavings) > 0 ? true : false; 7070 cart.transaction.isRewardsApplied = !isNaN(parseFloat(digitalData?.cart?.savings?.rewardsSavings)) && Number(digitalData?.cart?.savings?.rewardsSavings) > 0 ? true : false; 7071 7072 //get icmpid and emailClipAllCoupon from query params and set uniqueSKUCount from items 7073 cart.cartInfo.uniqueSKUCnt = [...new Set(cart.item?.map((item) => item.productID))].length; 7074 if (this.getLSItem('analyticsQueryParamEmailClipAllCoupon') || crossPageData.analyticsQueryParamEmailClipAllCoupon?.data) { 7075 const analyticsQueryParamEmailClipAllCoupon = crossPageData.analyticsQueryParamEmailClipAllCoupon?.data || this.handleGetLSValue('analyticsQueryParamEmailClipAllCoupon'); 7076 cart.transaction.emailClipAllCoupon = analyticsQueryParamEmailClipAllCoupon; 7077 this.removeLSItem('analyticsQueryParamEmailClipAllCoupon'); 7078 this.setAnalyticsCrossPageData({ action: 'remove', name: 'analyticsQueryParamEmailClipAllCoupon' }); 7079 } 7080 if (crossPageData.analyticsQueryParamIcmpid?.data || this.getLSItem('analyticsQueryParamIcmpid')) { 7081 const analyticsQueryParamIcmpid = crossPageData.analyticsQueryParamIcmpid?.data || this.handleGetLSValue('analyticsQueryParamIcmpid'); 7082 cart.transaction.icmpId = analyticsQueryParamIcmpid; 7083 this.setAnalyticsCrossPageData({ action: 'remove', name: 'analyticsQueryParamIcmpid' }); 7084 } 7085 if (crossPageData.analyticsQueryParamCmpid?.data || this.getLSItem('analyticsQueryParamCmpid')) { 7086 const analyticsQueryParamCmpid = crossPageData.analyticsQueryParamCmpid?.data || this.handleGetLSValue('analyticsQueryParamCmpid'); 7087 pageDetails.parameters = analyticsQueryParamCmpid; 7088 this.setAnalyticsCrossPageData({ action: 'remove', name: 'analyticsQueryParamCmpid' }); 7089 } 7090 if (crossPageData.analyticsQueryParamReferringUrl?.data || this.getLSItem('analyticsQueryParamReferringUrl')) { 7091 const analyticsQueryParamReferringUrl = crossPageData.analyticsQueryParamReferringUrl?.data || this.handleGetLSValue('analyticsQueryParamReferringUrl'); 7092 pageDetails.referringURL = analyticsQueryParamReferringUrl; 7093 this.setAnalyticsCrossPageData({ action: 'remove', name: 'analyticsQueryParamReferringUrl' }); 7094 } 7095 7096 //set topNav 7097 let subSections = [pageDetails.subSection1, pageDetails.subSection2, pageDetails.subSection3, pageDetails.subSection4].filter(
7097Boolean); 7098 let topNavPreviousPage = subSections.join(':') + ' (L' + subSections.length + ')'; 7099 localStorage.setItem('topNavPreviousPage', topNavPreviousPage); 7100 7101 // set pickupLocation 7102 if (order.serviceType === 'Delivery') { 7103 user.pickupLocation = 'delivery'; 7104 } else if (order.serviceType === 'DUG') { 7105 if (order.isPickupStore && order.deliveryInfo.locationType) { 7106 const locationInfo = order.deliveryInfo.locationType; 7107 let pickupLocation; 7108 switch (locationInfo) { 7109 case 'LOCKER-OUTSIDE': 7110 case 'LOCKER-INSIDE': 7111 case 'DRIVEUP': 7112 pickupLocation = 'locker'; 7113 break; 7114 case 'COUNTER-OUTSIDE': 7115 case 'COUNTER-INSIDE': 7116 pickupLocation = 'counter'; 7117 break; 7118 case 'KIOSK-OUTSIDE': 7119 case 'KIOSK-INSIDE': 7120 pickupLocation = 'kiosk'; 7121 break; 7122 } 7123 if (pickupLocation) { 7124 user.pickupLocation = pickupLocation; 7125 } 7126 } else { 7127 user.pickupLocation = 'dug'; 7128 } 7129 } 7130 7131 //set defaults 7132 cart.transaction.shippingFee = '0'; 7133 cart.transaction.fuelSurcharge = '0'; 7134 cart.transaction.serviceFee = '0'; 7135 cart.transaction.bagFee = '0'; 7136 7137 if (orderSummary?.deliveryFee && Object.keys(orderSummary.deliveryFee).length > 0) { 7138 cart.transaction.shippingFee = (orderSummary?.deliveryFee?.feeWaived) 7139 ? orderSummary?.deliveryFee?.actualFee?.toString() : orderSummary?.deliveryFee?.currentFee ? orderSummary?.deliveryFee?.currentFee?.toString() : ''; 7140 } 7141 7142 if (orderSummary?.fuelSurcharge && Object.keys(orderSummary.fuelSurcharge).length > 0) { 7143 cart.transaction.fuelSurcharge = (orderSummary?.fuelSurcharge?.feeWaived) 7144 ? orderSummary?.fuelSurcharge?.actualFee?.toString() : orderSummary?.fuelSurcharge?.currentFee ? orderSummary?.fuelSurcharge?.currentFee?.toString() : ''; 7145 } 7146 if (orderSummary?.serviceFee && Object.keys(orderSummary.serviceFee).length > 0) { 7147 cart.transaction.serviceFee = (orderSummary?.serviceFee?.feeWaived) 7148 ? orderSummary?.serviceFee?.actualFee?.toString() : orderSummary?.serviceFee?.currentFee ? orderSummary?.serviceFee?.currentFee?.toString() : ''; 7149 } 7150 7151 if (order.charges?.length > 0) { 7152 order.charges.forEach(charge => { 7153 switch (charge.category) { 7154 case 'shipping': 7155 if (!cart.transaction.shippingFee || cart.transaction.shippingFee == '0') { 7156 cart.transaction.shippingFee = charge.amount?.toString(); 7157 } 7158 break; 7159 case 'service': 7160 if ((!cart.transaction.serviceFee || cart.transaction.serviceFee == '0') && charge.name != 'BagFee') { 7161 cart.transaction.serviceFee = charge.amount?.toString(); 7162 } 7163 break; 7164 case 'ServiceFee': 7165 if ((!cart.transaction.bagFee || cart.transaction.bagFee == '0') && charge.name == 'BagFee') { 7166 cart.transaction.bagFee = charge.amount?.toString(); 7167 } 7168 break; 7169 default: 7170 break; 7171 } 7172 7173 }) 7174 } 7175 7176 // tipping 7177 if ((crossPageData?.tippingMode || this.getLSItem('analyticsTippingMode')) && orderSummary.tipAmount !== undefined && orderSummary.tipAmount !== null) { 7178 cart.transaction.tippingRevenue = Number(orderSummary.tipAmount).toFixed(2); 7179 cart.transaction.tippingMode = crossPageData.tippingMode?.data || this.handleGetLSValue('analyticsTippingMode'); 7180 this.setAnalyticsCrossPageData({ action: 'remove', name: 'tippingMode' }); 7181 } 7182 7183 if (order?.bagFeePreference) { 7184 cart.transaction.bagPreference = order.bagFeePreference?.toggleEnabled === true ? 'true' : 'false'; 7185 } 7186 7187 //counting unique transactions: uniqueOrderFlag will only be 1 when the transaction_complete event fires the first time, and 0 whenever the 7188 //Order Confirmation page is reloaded and the event re-fired 7189 cart.transaction.uniqueOrderFlag = (crossPageData.uniqueTransactionComplete?.data || this.getLSItem('uniqueTransactionComplete')) ? 0 : 1; 7190 this.setMarketplaceCartInfo(order, orderSummary, 'order_confirmation'); 7191 let analyticsSellerIds = crossPageData.sellerIds?.data ? crossPageData.sellerIds.data?.split(',') : this.getLSItem('analyticsSellerIds') 7192 ? this.getLSItem('analyticsSellerIds').split(',') : []; 7193 if (analyticsSellerIds.indexOf(pageDetails.sellerId) !== -1) { 7194 analyticsSellerIds.splice(analyticsSellerIds.indexOf(pageDetails.sellerId), 1); 7195 } 7196 this.setLSItem('analyticsSellerIds', analyticsSellerIds.join(',')); 7197 this.setAnalyticsCrossPageData({ action: 'add', name: 'sellerIds', data: analyticsSellerIds.join(','), isAcrossPages: true }); 7198 7199 7200 7201 if (crossPageData.switchedToPaypal?.data || this.getLSItem('analyticsSwitchedToPaypal')) { 7202 pageFilter.topNav = crossPageData?.switchedToPaypal?.data || this.handleGetLSValue('analyticsSwitchedToPaypal'); 7203 this.setAnalyticsCrossPageData({ action: 'remove', name: 'switchedToPaypal' }); 7204 } 7205 7206 if (this.getLSItem('analyticsExpressCTAValue') || crossPageData.expressCTAValue?.data) { 7207 pageFilter.topNav = crossPageData?.expressCTAValue?.data || this.handleGetLSValue('analyticsExpressCTAValue'); 7208 this.setAnalyticsCrossPageData({ action: 'remove', name: 'expressCTAValue' }); 7209 } 7210 7211 this.setPromoBannerImpressions(); 7212 AB.DATALAYER.setPreviousPageName(); 7213 AB.DATALAYER.setRestrictedPageCnc(); 7214 7215 this.pushEvent( 7216 this.EVENTS.TRANSACTION_COMPLETE, { 7217 page: digitalData?.pageDetails, 7218 pageFilter: digitalData?.pageFilter, 7219 user: digitalData?.user, 7220 productInfo: digitalData?.productInfo, 7221 modal: digitalData?.modal, 7222 cart: digitalData?.cart 7223 } 7224 ); 7225 7226 if (pageFilter?.subscriptionFunnel?.includes('confirmation')) { 7227 this.pushEvent( 7228 this.EVENTS.DS_CONFIRMATION, { 7229 page: digitalData?.pageDetails, 7230 pageFilter: digitalData?.pageFilter, 7231 user: digitalData?.user, 7232 productInfo: digitalData?.productInfo, 7233 modal: digitalData?.modal, 7234 cart: digitalData?.cart 7235 } 7236 ); 7237 } 7238 7239 this.removeLSItem('checkout-order-payment-updated'); 7240 this.removeLSItem('edit-order-payment-updated'); 7241 this.removeLSItem('continueButtonClicked'); 7242 this.setAnalyticsCrossPageData({ action: 'remove', name: 'confirmationPaymentUpdated' }); 7243 this.setAnalyticsCrossPageData({ action: 'remove', name: 'continueButtonClicked' }); 7244 this.setAnalyticsCrossPageData({ action: 'set', name: 'shouldRemovePfm', data: true, isAcrossPages: true }); 7245 AB.DATALAYER.trackRoktAdImpression(isRoktEnabled); 7246 }; 7247 7248 this.removeAnalyticsPlanData = function () { 7249 this.removeLSItem('analyticsSubscriptionPlan'); 7250 this.removeLSItem('analyticsFPPlanPrice'); 7251 this.removeLSItem('analyticsFPDiscountedMonths'); 7252 if (digitalData?.pageFilter?.subscriptionFunnel) { 7253 digitalData.pageFilter.subscriptionFunnel = ''; 7254 } 7255 } 7256 7257 this.updateCheckoutModalData = function (event, modalType, modalInfo, conflict) { 7258 try { 7259 conflict = modalType || digitalData.modal.conflict || 'store'; 7260 7261 let eventInfo = (modalType && modalType.toLowerCase() === 'custom' && modalInfo) ? modalInfo : { 7262 appendEvent: true, 7263 name: (modalInfo ? modalInfo.name : '') || this.EVENTS.MODA
7263L_CLICK, 7264 prefix: (modalInfo ? modalInfo.prefix : '') || 'checkout-address-' 7265 }; 7266 7267 if (event.toLowerCase() === 'view') { 7268 eventInfo.name = this.EVENTS.MODAL_VIEW; 7269 digitalData.modal.conflict = conflict; 7270 } 7271 7272 eventInfo['link'] = ((modalInfo ? modalInfo.link : '') || 7273 eventInfo.prefix + 7274 conflict + 7275 (eventInfo.appendEvent ? '|' + event : ''))?.toLowerCase(); 7276 7277 if (eventInfo.link) { 7278 this.pushEvent(eventInfo.name, { 7279 modal: { 7280 modalAction: eventInfo.link, 7281 conflict 7282 }, 7283 page: digitalData?.pageDetails, 7284 pageFilter: digitalData?.pageFilter, 7285 user: digitalData?.user 7286 }); 7287 if (eventInfo.name?.includes('Click')) { 7288 digitalData.modal.modalAction = ''; 7289 digitalData.modal.conflict = ''; 7290 } 7291 } 7292 } catch (e) { console.error('[DD]ERROR: @updateCheckoutModalData: %O', e) } 7293 } 7294 7295 this.updateRescheduleSlot = function (slot) { 7296 if (slot) { 7297 this.setDeliveryWindow(slot); 7298 } else { 7299 this.pushEvent(this.EVENTS.LINK_CLICK, { 7300 link: { 7301 linkName: 'reschedule-order-update' 7302 }, 7303 page: digitalData?.pageDetails, 7304 pageFilter: digitalData?.pageFilter, 7305 cart: digitalData?.cart, 7306 user: digitalData.user 7307 }); 7308 } 7309 } 7310 7311 this.setDefaultValues = function (resp) { 7312 const { pageFilter, cart } = digitalData; 7313 if (resp?.deliveryInfo?.slotInfo?.slotPlan) { 7314 pageFilter.premiumSlots = this.premiumSelected(resp.deliveryInfo.slotInfo.slotPlan); 7315 } 7316 cart.transaction = cart.transaction || {}; 7317 cart.savings = cart.savings || {}; 7318 7319 cart.transaction.orderID = resp?.orderId; 7320 if (resp?.deliveryInfo?.slotInfo && resp?.deliveryInfo?.slotInfo?.displayStartTS) { 7321 let reservedSlotInfo = resp?.deliveryInfo?.slotInfo ? AB.DATALAYER.safeJSONParse(JSON.stringify(resp.deliveryInfo.slotInfo)) : {}; 7322 let lastPickupTS = reservedSlotInfo?.displayLastPickupTime; 7323 if (lastPickupTS && resp?.serviceType === 'DUG' && resp?.isPickupStore === false) { 7324 reservedSlotInfo['lastPickupTS'] = lastPickupTS; 7325 } 7326 cart.transaction.deliveryTime = this.getFullDeliveryTime(reservedSlotInfo); 7327 } 7328 if (resp?.orderSummary || resp?.wysiwygOrderSummary) { 7329 const orderSummary = resp?.orderSummary || resp?.wysiwygOrderSummary || {}; 7330 pageFilter.promoCode = orderSummary.promotions ? this.allPromoCodes(orderSummary.promotions) : ''; 7331 cart.transaction.revenue = orderSummary?.totalEstimatedPrice?.toString() || ''; 7332 cart.transaction.subTotal = orderSummary?.totalBasePrice?.toString() || ''; 7333 cart.transaction.totalSavings = orderSummary?.estimatedClubCardSavings?.toString() || ''; 7334 7335 if (orderSummary?.deliveryFee) { 7336 cart.transaction.shippingFee = !orderSummary?.deliveryFee?.feeWaived && (orderSummary?.deliveryFee?.actualFee || orderSummary?.deliveryFee?.actualFee == 0) ? orderSummary?.deliveryFee?.actualFee?.toString() : orderSummary?.deliveryFee?.currentFee || orderSummary?.deliveryFee?.currentFee == 0 ? orderSummary?.deliveryFee?.currentFee?.toString() : '0'; 7337 } 7338 if (orderSummary?.fuelSurcharge) { 7339 cart.transaction.fuelSurcharge = !orderSummary?.fuelSurcharge?.feeWaived && (orderSummary?.fuelSurcharge?.actualFee || orderSummary?.fuelSurcharge?.actualFee == 0) ? orderSummary?.fuelSurcharge?.actualFee?.toString() : orderSummary?.fuelSurcharge?.currentFee || orderSummary?.fuelSurcharge?.currentFee == 0 ? orderSummary?.fuelSurcharge?.currentFee?.toString() : '0'; 7340 } 7341 if (orderSummary?.serviceFee) { 7342 cart.transaction.serviceFee = !orderSummary?.serviceFee?.feeWaived && (
7342orderSummary?.serviceFee?.actualFee || orderSummary?.serviceFee?.actualFee == 0) ? orderSummary?.serviceFee?.actualFee?.toString() : orderSummary?.serviceFee?.currentFee || orderSummary?.serviceFee?.currentFee == 0 ? orderSummary?.serviceFee?.currentFee?.toString() : '0'; 7343 } 7344 cart.transaction.paymentMethod = (orderSummary?.orderSummary) ? 'cc & coa' : 'cc'; 7345 7346 cart.savings.clubcardSavings = cart.savings?.clubcardSavings ? cart.savings?.clubcardSavings : orderSummary?.estimatedClubCardSavings ? orderSummary?.estimatedClubCardSavings?.toString() : '0'; 7347 cart.savings.promocodeSavings = cart.savings.promocodeSavings ? cart.savings.promocodeSavings : orderSummary?.promotions ? this.totalPromoSavings(orderSummary?.promotions) : 0; 7348 cart.savings.totalSavings = cart.savings.totalSavings ? cart.savings.totalSavings : (Number(cart.savings.clubcardSavings) + Number(cart.savings.promocodeSavings)).toString(); 7349 } 7350 } 7351 7352 this.premiumSelected = function (slotPlan) { 7353 return slotPlan === 'PREMIUM' ? 'true-default' : ''; 7354 } 7355 7356 this.subscriptionLearnMoreClick = function () { 7357 let isDUG = digitalData.user?.deliveryPreference?.toLowerCase() === 'dug'; 7358 digitalData.pageFilter.subscriptionFunnel = isDUG ? 'pickup-freshpass-view-plans' : ''; 7359 AB.DATALAYER.setLinkClickEvents(isDUG ? 'subscription-checkout-pickup-view-plans' : 'subscription-checkout-learnmore'); 7360 } 7361 7362 //set topNav for when the user departs the confirmation screen via the Manage SNS link 7363 this.setSnsManageTopNav = function () { 7364 AB.DATALAYER.setLSItem('manageSnsFromOrderConfirm', `ar:manage-schedule-and-save-${digitalData?.cart?.transaction?.paymentMethod?.includes('ebt') ? 'pending-payment-ebt' : 'successful-submission'}`); 7365 } 7366 7367 this.setTippingModeAnalytics = function (orderInfo) { 7368 const defaultTippingMode = this.getLSItem('analyticsTippingMode'); 7369 let driverTip = orderInfo?.driverTip ? orderInfo.driverTip : {}; 7370 const driverTipInfo = orderInfo?.driverTipInfo?.driverTipOptions; 7371 7372 if (driverTip && Object.keys(driverTip).length === 0 && driverTipInfo) { 7373 const driverTipLookup = driverTipInfo.filter((tip) => { if (tip.tipSelected === true) { return tip } }); 7374 driverTip = driverTipLookup.length > 0 ? driverTipLookup[0] : {}; 7375 } 7376 7377 if (defaultTippingMode && driverTip && Object.keys(driverTip).length > 0) { 7378 const tipAmount = driverTip.tipAmount ? Number(driverTip.tipAmount).toFixed(2) : 'zero'; 7379 const tipPercentage = driverTip.tipPercentage ? (driverTip.tipPercentage.toLowerCase() === 'other' ? 'custom' : driverTip.tipPercentage) : 'no-option'; 7380 const tippingMode = `${defaultTippingMode},${tipPercentage}|${tipAmount}`; 7381 this.setLSItem('analyticsTippingMode', tippingMode); 7382 } else { 7383 this.setLSItem('analyticsTippingMode', 'no-option|zero,no-option|zero'); 7384 } 7385 } 7386 7387 this.analyticsTipChangedEvent = function (driverTipInfo, selectedTip) { 7388 const initialTip = driverTipInfo?.['driverTipOptions']?.filter(data => data.tipSelected)[0]?.['tipAmount']; 7389 if (initialTip !== selectedTip) { 7390 this.setLinkClickEvents('edit-order|tip-changed'); 7391 } 7392 } 7393 7394 this.setRestrictedPageCnc = function () { 7395 if (digitalData?.cart) { 7396 if (AB.DATALAYER.isCartAndCheckoutPage('erums/checkout') && digitalData.cart.item?.length === 0 && AB.DATALAYER.getLSItem('analyticsCartItemLS')) { 7397 digitalData.cart.item = AB.DATALAYER.getLSItem('analyticsCartItemLS') ? AB.DATALAYER.safeJSONParse(AB.DATALAYER.getLSItem('analyticsCartItemLS')) : []; 7398 } 7399 if (digitalData.cart.item?.length > 0) { 7400 const hasTrackingDisabledItems = digitalData.cart.item.some(item => item.isProductTrackingDisabled === true); 7401 AB.DATALAYER.setRestrictedProductPageFlag(hasTrackingDisabledItems); 7402 AB.DATALAYER.updatePageNameAndSubSectionRestrictedFlag(); 7403 if (AB.DATALAYER.isCartAndCheckoutPage('erums/cart')) {
7404 AB.DATALAYER.setLSItem('analyticsCartItemLS', JSON.stringify(digitalData.cart.item)); 7405 } else if (this.isCartAndCheckoutPage('order/confirmation')) { 7406 AB.DATALAYER.removeLSItem('analyticsCartItemLS'); 7407 } 7408 } 7409 } 7410 } 7411 7412 this.setSelectiveCartImpressions = function (impressionType) { 7413 if (digitalData?.pageFilter && impressionType) { 7414 const isMtoImpressionAdded = digitalData?.pageFilter?.impressions && digitalData?.pageFilter?.impressions?.indexOf('mto-') != -1; 7415 digitalData.pageFilter.impressions = `${digitalData.pageFilter?.impressions ? digitalData.pageFilter.impressions : ''}${digitalData.pageFilter?.impressions && !isMtoImpressionAdded ? ',' : ''}${!isMtoImpressionAdded ? `mto-${impressionType === 'deli' ? 'sp' : impressionType === 'extra-time' ? 'lp' : ''}` : ''}`; 7416 } 7417 } 7418 7419}).call(AB.DATALAYER); 7420 7421'use strict'; 7422 7423(function () { 7424 try { 7425 digitalData.pageDetails = { 7426 subSection1: this.pathSections[0], 7427 subSection2: this.pathSections[1] || '', 7428 subSection3: this.pathSections[2] || '', 7429 subSection4: this.pathSections[3] || '', 7430 banner: this.getBanner(), 7431 platform: 'web', 7432 siteTypePath: this.isShopPage() || this.isCartAndCheckoutPage() || this.isDFTAPage() ? 'eCom' : 'loyalty', 7433 channel: this.isWineShopPage() ? 'vineandcellar' : this.isShopPage() ? 'delivery' : 'loyalty', 7434 server: window.location.hostname, 7435 pageUrl: window.location.pathname === '/' ? '/home.html' : (this.isCartAndCheckoutPage() || this.isDFTAPage()) ? window.location.href : window.location.pathname === '/weeklyad/' ? '/weeklyad' : window.location.pathname, 7436 referringURL: this.getReferringUrl(), 7437 deliveryBanner: 'no off banner delivery', 7438 thirdParty: this.getThirdParty(), 7439 sellerId: this.isWineShopPage() ? '22222222' : AB.DATALAYER.getBannerSellerId(), 7440 pfmDetail: this.pathSections.join(':'), 7441 deviceType: this.getBrowserType() 7442 }; 7443 this.setPageName(); 7444 7445 digitalData.pageFilter = { 7446 carousel: this.setCarouselName(), 7447 filterType: '', 7448 filterSelection: '', 7449 sortSelection: '', 7450 adaFlag: 'no', 7451 topNav: this.initTopNav(), 7452 isNewLeftNav: this.getLSItem('analyticsNavTracking') === 'left-nav', 7453 isNewTopNav: this.handleGetLSValue('analyticsNavTracking') === 'top-nav' || !!this.getLSItem('analyticsTopTertiaryNavTracking'), 7454 tertiaryTopNav: this.handleGetLSValue('analyticsTopTertiaryNavTracking') 7455 }; 7456 digitalData.user = { 7457 deviceInfo: this.getDeviceInfo() 7458 }; 7459 digitalData.modal = digitalData.modal || { 7460 modalAction: '', 7461 modalMessage: '' 7462 }; 7463 digitalData.link = digitalData.link || { 7464 linkName: '' 7465 }; 7466 digitalData.boxtop = digitalData.boxtop || {}; 7467 digitalData.cart = digitalData.cart || { 7468 cartInfo: {}, 7469 item: [], 7470 transaction: { 7471 subTotal: '' 7472 }, 7473 savings: {} 7474 }; 7475 digitalData.productInfo = {}; 7476 digitalData.pageFlags = { 7477 shouldTrackGiftOption: true, 7478 // Tracked below flag for gift option on angular lib instead of the one used on checkout so I am adding here to enable tracking for lib component 7479 // both shouldTrackGiftLinkClicks and shouldTrackGiftOption should be removed from here and anywhere else it is referenced anytime after 2.1 release 7480 shouldTrackGiftLinkClicks: true, 7481 isStickyBannerImpressionSent: false, 7482 isStickyBannerExposureSent: false 7483 }; 7484 digitalData.crossPageData = {}; 7485 digitalData.storeFlags = window.AB?.storeFlagsData || {}; 7486 document.addEventListener('storeFlagsLoadComplete', () => { 7487 digitalData.storeFlags = window.AB?.storeFlagsData; 7488 }); 7489 this.setAnalyticsCrossPageData({ action: 'get' }); 7490 if (digitalData.crossPageData?.analyticsRestrictedProductPage?.data) { 7491 digitalData.pageFlags.isPreviousPageTrackingRestricted = digitalData.crossPageData.analyticsRestrictedProductPage.data; 7492 this.setAnalyticsCrossPageData({ action: 'remove', name: 'analyticsRestrictedProductPage' }); 7493 } 7494 if (digitalData.crossPageData?.expediaBookingSignIn?.data && !this.isAuthSignInPage()) { 7495 delete digitalData.crossPageData.expediaBookingSignIn; 7496 this.setAnalyticsCrossPageData({ action: 'remove', name: 'expediaBookingSignIn' }); 7497 } 7498 } catch (e) { 7499 console.warn('@AnalyticsTeam - Failed to initialise digitalData value due to following error: ', e); 7500 } 7501 7502 /** 7503 * Sets events and info from previous pages before pageLoaded event 7504 */ 7505 this.initEventsFromPreviousPage = function () { 7506 this.initPrebookAEM(); 7507 this.initRequestRefund(); 7508 this.initOrderReschedule(); 7509 this.initBannerPosition(); 7510 this.removeLSItem('uniqueTransactionComplete'); 7511 this.removeLSItem('abOrderCountAnalyticsCC'); 7512 } 7513 7514 /** 7515 * Prevents the pageLoad event from being globally triggered on pages that have a separate pageLoad setup 7516 */ 7517 this.globalPageLoadRules = function () { 7518 return !this.isDUGNotification() && 7519 !this.isSearchResultsPage() && 7520 !this.isPdp() && 7521 !this.isAislesPage() && 7522 !this.isErumsPage() && 7523 !this.isARManagePage() && 7524 !this.isFreshpassManagePage() && 7525 !this.isBIAPage() && 7526 !this.isOrdersPage() && 7527 !this.isHelpCenterPage() && 7528 !this.isMarketplaceSearchPage() &&
7529 !this.isPharmacyRxPage() && 7530 !this.isStudentsPage() && 7531 !this.isARTestManagePage() && 7532 !this.isResetPasswordPage() && 7533 !this.isCartAndCheckoutPage() && 7534 !this.isReviewsPage() && 7535 !this.isRewardsPage() && 7536 !this.isGamesSPAPage() && 7537 !this.isSelfMergeAccountsPage() && 7538 !this.isWeeklyAdPage() && 7539 !this.isB4USPA() && 7540 !this.isSpotlightPage(); 7541 } 7542 7543 /** 7544 * Initializes data layer pageDetails object additional information 7545 */ 7546 this.initPage = function () { 7547 if (!this.isOrdersPage() && !this.isARManagePage() && !this.isHelpCenterPage() && !this.isAccountResetPage() && !this.isSpotlightPage()) { 7548 let mktpCategoryPillClicked = this.handleGetLSValue('mktpCategoryPillClicked') 7549 // Set pageDetails.channel and pageDetails.pageType 7550 AB.DATALAYER.sortSiteSections(); 7551 // Set custom page name for just for U Registration - Welcome Offer 7552 this.justforURegistration(); 7553 // Set custom page info for Sincerely-food 7554 this.initSCFPage(); 7555 // Sets flag isAnalyticsMarketplacePage on window object 7556 this.setMarketplacePage(); 7557 // Set custom subSections 7558 if (window.isAnalyticsMarketplacePage) { 7559 if (this.isMarketplaceCategoryPage()) { 7560 this.initMarketplaceCategory(); 7561 } 7562 if (!this.isMarketplaceCategoryPage() && !this.isMarketplaceSearchPage() && !this.isPdp()) { 7563 if (digitalData.pageDetails.subSection2 && !digitalData.pageDetails.subSection3) { 7564 this.initMarketplaceSellerListing(); 7565 } else { 7566 this.initMarketplaceSellerLanding(); 7567 } 7568 } 7569 } else if (this.isGamesPage()) { 7570 this.initGamesPages(); 7571 } 7572 this.initContactUs(); 7573 AB.DATALAYER.pfmFromCurrentPage = [digitalData.pageDetails.subSection1, digitalData.pageDetails.subSection2, digitalData.pageDetails.subSection3, digitalData.pageDetails.subSection4].filter(function (x) { 7574 return x; 7575 }).join(':'); 7576 if (mktpCategoryPillClicked) { 7577 digitalData.pageFilter.bannerCardCarousel = mktpCategoryPillClicked; 7578 } 7579 } 7580 }; 7581 7582 /** 7583 * gets and stores the 'cmpid', 'icmpid' and EmailClipAllCoupon attributes 'totalCoupons,couponsClipped,autoclipError,clipFailCode,clipFailMessage' in local storage 7584 */ 7585 this.initQueryParams = function () { 7586 const icmpid = AB.DATALAYER.analyticsGetURLParameterByName('icmpid'); 7587 if (icmpid) { 7588 this.setLSItem('analyticsQueryParamIcmpid', icmpid); 7589 this.setAnalyticsCrossPageData({ action: 'set', name: 'analyticsQueryParamIcmpid', data: icmpid, isAcrossPages: true }); 7590 } 7591 digitalData.pageDetails = digitalData.pageDetails || {}; 7592 const cmpid = AB.DATALAYER.analyticsGetURLParameterByName('cmpid'); 7593 if (cmpid) { 7594 this.setLSItem('analyticsQueryParamCmpid', cmpid); 7595 this.setAnalyticsCrossPageData({ action: 'set', name: 'analyticsQueryParamCmpid', data: cmpid, isAcrossPages: true }); 7596 digitalData.pageDetails.parameters = cmpid; 7597 } 7598 const totalcoupons = AB.DATALAYER.analyticsGetURLParameterByName('totalcoupons'); 7599 const couponsclipped = AB.DATALAYER.analyticsGetURLParameterByName('couponsclipped'); 7600 const autocliperror = AB.DATALAYER.analyticsGetURLParameterByName('autocliperror'); 7601 const clipfailcode = AB.DATALAYER.analyticsGetURLParameterByName('clipfailcode'); 7602 const clipfailmessage = AB.DATALAYER.analyticsGetURLParameterByName('clipfailmessage'); 7603 if (totalcoupons || couponsclipped || autocliperror || clipfailcode || clipfailmessage) { 7604 const emailClipAllCoupon = [totalcoupons || '', couponsclipped || '', autocliperror || '', clipfailcode || '', clipfailmessage || ''].join(':') 7605 this.setLSItem('analyticsQueryParamEmailClipAllCoupon', emailClipAllCoupon); 7606 this.setAnalyticsCrossPageData({ action: 'set', name: 'analyticsQueryParamEmailClipAllCoupon', data: emailClipAllCoupon, isAcrossPages: true }); 7607 } 7608 const referringUrl = AB.DATALAYER.getReferringUrl(); 7609 if (referringUrl && referringUrl?.toLowerCase()?.indexOf(AB?.DATALAYER?.getBanner()) == -1) { 7610 this.setLSItem('analyticsQueryParamReferringUrl', referringUrl); 7611 this.setAnalyticsCrossPageData({ action: 'set', name: 'analyticsQueryParamReferringUrl', data: referringUrl, isAcrossPages: true }); 7612 digitalData.pageDetails.referringURL = referringUrl; 7613 } 7614 AB.DATALAYER.setParamsandRurlSessionBased(); 7615 } 7616 7617 /** 7618 * to set params and referring Url session based 7619 */ 7620 this.setParamsandRurlSessionBased = function () { 7621 const isFirstVisit = !AB.DATALAYER.getCookie('absVisitorId'); 7622 const cmpid = !!this.getLSItem('analyticsQueryParamCmpid'); 7623 const referringUrlStored = this.getLSItem('analyticsQueryParamReferringUrl'); 7624 const referringUrl = AB.DATALAYER.getReferringUrl(); 7625 const icmpid = !!this.getLSItem('analyticsQueryParamIcmpid'); 7626 let paramValue = ''; 7627 if (isFirstVisit && !cmpid && !icmpid && !referringUrlStored && referringUrl && referringUrl?.toLowerCase()?.indexOf(AB?.DATALAYER?.getBanner()) > -1) { 7628 paramValue = 'refresh' 7629 } 7630 if (isFirstVisit && !cmpid && !icmpid && referringUrlStored && referringUrlStored?.toLowerCase()?.indexOf('direct') > -1) { 7631 paramValue = 'true' 7632 } 7633 if (!cmpid && !icmpid && !referringUrlStored && !referringUrl) { 7634 this.setLSItem('analyticsQueryParamReferringUrl', 'direct'); 7635 this.setAnalyticsCrossPageData({ action: 'set', name: 'analyticsQueryParamReferringUrl', data: 'direct', isAcrossPages: true }); 7636 digitalData.pageDetails.referringURL = 'direct'; 7637 } 7638 if (paramValue) { 7639 this.setLSItem('analyticsQueryParamCmpid', paramValue); 7640 this.setAnalyticsCrossPageData({ action: 'set', name: 'analyticsQueryParamCmpid', data: paramValue, isAcrossPages: true }); 7641 digitalData.pageDetails.parameters = paramValue; 7642 } 7643 7644 } 7645 7646 /** 7647 * Initializes the data layer user object information 7648 */ 7649 this.initUser = function () { 7650 digitalData.user = digitalData.user || {}; 7651 this.initJourneyStatus(); 7652 digitalData.user.journeyStatus = this.getJourneyStatus(); 7653 digitalData.user.addressType = this.handleGetLSValue('addressType'); 7654 7655 this.initUserDataFromCookies(); 7656 this.initOrderCount(); 7657 if (digitalData.user?.loginStatus === 'guest') { 7658 this.setAnalyticsCrossPageData({ action: 'remove', name: 'acip371626Social' }); 7659 } 7660 }; 7661 7662 /** 7663 * Initializes the datalayer with info from LS 7664 * Updates LS items 7665 * Method is called before sending the 'pageLoaded' event 7666 */ 7667 this.updatesFromLSBeforePageLoad = function () { 7668 // Remove LS items on new pageLoad 7669 this.removeLSItem('analyticsSimilarItemsPDP'); 7670 this.removeLSItem('analyticsSimilarItemsCart'); 7671 this.removeLSItem('analyticsSimilarItemsBIA'); 7672 this.removeLSItem('analyticsMetaInfo'); 7673 this.removeLSItem('analyticsProductIdPDP'); 7674 this.removeLSItem('itemsInCartBeforeUpdate'); 7675 this.removeLSItem('aaBiaSimilarItemsMetrics'); 7676 this.removeLSItem('analyticsBiaLoadMore'); 7677 if (!this.isWeeklyAdPage()) { 7678 this.removeLSItem('analyticsWeeklyAdPageInfo'); 7679 } 7680 if (!this.isPdp() || this.isWineShopPage() || window.isAnalyticsMarketplacePage) { 7681 this.removeLSItem('analyticsAMCPDPInfo'); 7682 } 7683 // Remove sns frequency trackig on non-PDP pages 7684 if (!this.isPdp()) { 7685 AB.DATALAYER.setAnalyticsCrossPageData({ action: 'remove', name: 'snsAnalyticsFrequency' }); 7686 } 7687 7688 // Remove data related to freshpass cancel plan benefits flow on pages other than Manage Freshpass 7689 if (!this.isFreshpassManagePage()) { 7690 this.setAnalyticsCrossPageData({ action: 'remove', name: 'analyticsTrackDiscBnftFunnel' }) 7691 this.setAnalyticsCrossPageData({ action: 'remove', name: 'analyticsTrackCancelReasonId' }); 7692 this.setAnalyticsCrossPageData({ action: 'remove', name: 'analyticsTrackCancelReasonText' }); 7693 } 7694 7695 // Initialise various info from other pages 7696 this.removeLSItemFromBIACarousal(); 7697 this.initFPSItems(); 7698 this.initPetDetails(); 7699 this.initEngagementLadderImpressions(); 7700 this.initCartPromoBannerImpressions();
7701 this.initFlexCardDetails(); 7702 this.initAEMZoneTracking(); 7703 this.initDFTACarousel(); 7704 this.initSignInMessage(); 7705 this.initTopNavPageFlag(); 7706 this.initMkpGoToCart(); 7707 this.initScheduledItemsImpressions(); 7708 this.initHeroBannerAbTestDetails(); 7709 this.initBundleAbTestDetails(); 7710 } 7711 7712 this.removeLSItemFromBIACarousal = function () { 7713 if (!this.isPdp()) { 7714 localStorage.removeItem('analyticsBIAPfmSource'); 7715 localStorage.removeItem('analyticsBIAProductImpressionAdDetail'); 7716 localStorage.removeItem('analyticsIsBIACarousal'); 7717 localStorage.removeItem('analyticsBIACarousalProductId'); 7718 } 7719 } 7720 7721 this.updatePathsectionsForSPA = function () { 7722 if (this.isAccountResetPage()) { 7723 this.initPathSections(); 7724 this.setSubsections(); 7725 this.setPageName('loyalty'); 7726 } 7727 } 7728 7729 /** 7730 * Sets isShopOrTopNav pageFlag when clicking on shop or top nav links 7731 */ 7732 this.initTopNavPageFlag = function () { 7733 if (this.getLSItem('shopOrTopNavFlag') && digitalData) { 7734 digitalData.pageFlags = digitalData.pageFlags || {}; 7735 digitalData.pageFlags.isShopOrTopNav = this.handleGetLSValue('shopOrTopNavFlag'); 7736 } 7737 } 7738 7739 /** 7740 * update page name with :hpp(health restricted product page) if its a restricted page 7741 */ 7742 this.updatePageNameAndSubSectionRestrictedFlag = function () { 7743 if (digitalData?.pageFlags?.isProductTrackingDisabled && digitalData?.pageDetails?.pageName.indexOf(':hpp') == -1) { 7744 digitalData.pageDetails.pageName += ':hpp'; 7745 } 7746 if (digitalData?.pageFlags?.isProductTrackingDisabled) { 7747 if (digitalData.pageDetails.subSection2) { 7748 if (digitalData.pageDetails.subSection3) { 7749 if (digitalData.pageDetails.subSection4) { 7750 digitalData.pageDetails.subSection4 = digitalData.pageDetails.subSection4 + ':hpp'; 7751 } else { 7752 digitalData.pageDetails.subSection4 = 'hpp'; 7753 } 7754 } else { 7755 digitalData.pageDetails.subSection3 = 'hpp'; 7756 } 7757 } else { 7758 digitalData.pageDetails.subSection2 = 'hpp'; 7759 } 7760 } 7761 } 7762 7763 /** 7764 * Sets the 'pageLoaded' event 7765 */ 7766 this.setPageLoad = function () { 7767 this.updatesFromLSBeforePageLoad(); 7768 if (digitalData?.user && Object.keys(digitalData.user).length <= 1) { 7769 this.initUser(); 7770 } 7771 if (window.digitalData?.pageDetails?.pageUrl?.indexOf('/offer-details-modal-simple') !== -1) { 7772 AB.DATALAYER.setModalViewEvents('offer-details-modal|view'); 7773 } else { 7774 AB.DATALAYER.updatePageNameAndSubSectionRestrictedFlag(); 7775 AB.DATALAYER.setPreviousPageName(); 7776 AB.DATALAYER.pushEvent( 7777 AB.DATALAYER.EVENTS.PAGE_LOADED, { 7778 page: digitalData.pageDetails, 7779 pageFilter: digitalData.pageFilter, 7780 user: digitalData.user, 7781 recipes: digitalData.recipes, 7782 productInfo: digitalData.productInfo, 7783 modal: digitalData.modal 7784 } 7785 ); 7786 } 7787 // Reset property so that values don't persist between hits 7788 digitalData.pageFilter.bannerCardCarousel = ''; 7789 digitalData.pageFilter.impressions = ''; 7790 digitalData.pageFilter.topNav = ''; 7791 } 7792 7793 /** 7794 * Initializes the datalayer with info from LS on aisles page 7795 * Method is called before sending the 'pageLoaded' event 7796 */ 7797 this.updatesFromLSforAislesPage = function (productInfo) { 7798 // pfmSource set for Similar Items flow 7799 if (localStorage.getItem('analyticsSimilarItemsPDP')) { 7800 if (localStorage.getItem('analyticsSimilarItemsPDP') === productInfo.aisleId) { 7801 window.digitalData.pageFilter.pfmSource = 'similar-items_pdp'; 7802 } else { 7803 localStorage.removeItem('analyticsSimilarItemsPDP'); 7804 } 7805 } 7806 if (localStorage.getItem('analyticsSimilarItemsCart')) { 7807 if (localStorage.getItem('analyticsSimilarItemsCart') === productInfo.aisleId) { 7808 window.digitalData.pageFilter.pfmSource = 'similar-items_cart'; 7809 } else { 7810 localStorage.removeItem('analyticsSimilarItemsCart'); 7811 } 7812 } 7813 if (localStorage.getItem('analyticsSimilarItemsBIA')) { 7814 window.digitalData.pageFilter.pfmSource = localStorage.getItem('analyticsSimilarItemsBIA'); 7815 localStorage.removeItem('analyticsSimilarItemsBIA'); 7816 } 7817 if (localStorage.getItem('analyticsMetaInfo')) { 7818 localStorage.removeItem('analyticsMetaInfo'); 7819 } 7820 if (sessionStorage.getItem('analyticsProductIdPDP')) { 7821 sessionStorage.removeItem('analyticsProductIdPDP'); 7822 } 7823 this.removeLSItemFromBIACarousal(); 7824 this.initEngagementLadderImpressions(); 7825 this.initCartPromoBannerImpressions();
7826 this.initFlexCardDetails(); 7827 this.initDFTACarousel(); 7828 this.initTopNavPageFlag(); 7829 this.initAEMZoneTracking(); 7830 this.initHeroBannerAbTestDetails(); 7831 this.initBundleAbTestDetails(); 7832 } 7833 7834 this.initAislesL3L4Page = function (flag) { 7835 if (this.isAislesL3Page() || this.isAislesL4Page()) { 7836 this.setRestrictedProductPageFlag(flag); 7837 this.initAislesPage(); 7838 } 7839 } 7840 7841 /** 7842 * Sets the pageLoaded event on aisles pages with specific aisles attributes 7843 */ 7844 this.initAislesPage = function () { 7845 // Sets the aisle ID on aisles pages from the digitalData object, which is updated in product-carousel service, dynamic-tile-item-l2 and search-filter-selected components 7846 const productInfo = { 7847 aisleId: digitalData.productInfo ? digitalData.productInfo.aisleId : '', 7848 upc: digitalData.productInfo ? digitalData.productInfo.upc : '' 7849 }; 7850 7851 this.updatesFromLSforAislesPage(productInfo); 7852 AB.DATALAYER.updatePageNameAndSubSectionRestrictedFlag(); 7853 AB.DATALAYER.setPreviousPageName(); 7854 AB.DATALAYER.pushEvent( 7855 AB.DATALAYER.EVENTS.PAGE_LOADED, { 7856 page: digitalData.pageDetails, 7857 pageFilter: digitalData.pageFilter, 7858 user: digitalData.user, 7859 productInfo: productInfo 7860 } 7861 ); 7862 7863 // Reset property so that values don't persist between hits 7864 digitalData.pageFilter.bannerCardCarousel = ''; 7865 7866 this.setLinkClickSimilarItems(); 7867 7868 // Handles sign-in, sign-out event flows for analytics 7869 this.handleAccountEvents(); 7870 } 7871 7872 /** 7873 * Initializes the PDP page events 7874 */ 7875 this.initPDP = function () { 7876 if (localStorage.getItem('productInfoAnalyticsPDP')) { 7877 const productInfoPDP = this.safeJSONParse(AB.DATALAYER.getLSItem('productInfoAnalyticsPDP')); 7878 this.setPDPevent(productInfoPDP); 7879 AB.DATALAYER.removeLSItem('productInfoAnalyticsPDP'); 7880 } 7881 } 7882 7883 /** 7884 * Initializes BIA page load events 7885 */ 7886 this.initBIAPage = function () { 7887 if (this.isBIAPage()) { 7888 if (sessionStorage.getItem('analyticsBIADelayedPageLoad')) { 7889 this.updateBIAViewType(); 7890 sessionStorage.removeItem('analyticsBIADelayedPageLoad'); 7891 } else if (localStorage.getItem('analyticsBIAview')) { 7892 this.updateBIAViewType(localStorage.getItem('analyticsBIAview')); 7893 localStorage.removeItem('analyticsBIAview'); 7894 } 7895 } 7896 } 7897 7898 /** 7899 * Sets link click event if the similar items link for an unavailable product was clicked on minicart or fullcart 7900 */ 7901 this.setLinkClickSimilarItems = function () { 7902 if (localStorage.getItem('analyticsSimilarItemsMini')) { 7903 AB.DATALAYER.setLinkClickEvents('view-similar-items|mini'); 7904 localStorage.removeItem('analyticsSimilarItemsMini'); 7905 } 7906 if (localStorage.getItem('analyticsViewSimilarItemsCart')) { 7907 AB.DATALAYER.setLinkClickEvents('view-similar-items|cart'); 7908 localStorage.removeItem('analyticsViewSimilarItemsCart'); 7909 } 7910 } 7911 7912 /** 7913 * Initializes pfmDetail and recommendationType field on product-details page and set value inside localStorage for pfmDetail for all the other pages 7914 * pfmDetail = [PAGE SUBSECTIONS]#[CAROUSEL NAME]#[RECOMMENDATION]#[ROW]#[SLOT] 7915 */ 7916 this.initPfmDetail = function () { 7917 if (!this.isPdp()) { 7918 // for all the other pages save the pfmDetail value in ls to be used for the pdp 7919 this.setLsPfmDetailOnInit(); 7920 } 7921 }; 7922 7923 /** 7924 * Initializes the data layer user information with data that can be retrieved from cookies 7925 */ 7926 this.initUserDataFromCookies = function () { 7927 this.initSharedSessionInfo(); 7928 this.initSharedPiiSessionInfo(); 7929 this.initKmsi(); 7930 this.initAbsVisitorId(); 7931 window.dispatchEvent(new CustomEvent('digitalDataReady')); 7932 }; 7933 7934 7935 /** 7936 * Initializes the user object with info from 'absVisitorId' cookie 7937 */ 7938 this.initAbsVisitorId = function () { 7939 if (digitalData.user) { 7940 digitalData.user.absVisitorId = AB.DATALAYER.getCookie('absVisitorId') || ''; 7941 } 7942 } 7943 7944 /** 7945 * Initializes the user object with info from 'SWY_SHARED_SESSION_INFO' cookie 7946 */ 7947 this.initSharedSessionInfo = function () { 7948 const sharedInfoCookie = AB.DATALAYER.getCookie('ACI_S_SWY_SHARED_SESSION_INFO') || AB.DATALAYER.getCookie('SWY_SHARED_SESSION_INFO'); 7949 let user = digitalData.user; 7950 if (sharedInfoCookie && sharedInfoCookie != '') { 7951 let sharedSessionInfo = { 'info': '' }; 7952 try { 7953 sharedSessionInfo = JSON.parse(decodeURI(sharedInfoCookie)); 7954 } catch (e) { 7955 console.error('DATALAYER: Unable to parse the SWY_SHARED_SESSION_INFO cookie!'); 7956 } 7957 7958 try { 7959 let userCookieData = sharedSessionInfo?.info?.SHOP || {}; 7960 let userCookieCommonData = sharedSessionInfo?.info?.COMMON || {}; 7961 7962 userCookieData.userType = userCookieData.userType || userCookieCommonData.userType; 7963 7964 user.userType = userCookieData.userType || ''; 7965 user.type = userCookieData.userType || ''; 7966 switch (userCookieData.userType) { 7967 case 'A': 7968 case 'G': 7969 user.loginStatus = this.LoginStatus.GUEST; 7970 break; 7971 case 'R': 7972 case 'C': 7973 case 'D': 7974 case 'M': 7975 user.loginStatus = this.LoginStatus.REGISTERED; 7976 break; 7977 default: 7978 break; 7979 } 7980 7981 user.shopStoreId = userCookieData.storeId; 7982 user.shopZipCode = userCookieData.zipcode; 7983 7984 if (userCookieData.userData?.deliveryBanner && digitalData.pageDetails.banner) { 7985 const banner = this.getBanner(); 7986 const deliveryBanner = userCookieData.userData?.deliveryBanner.toLowerCase().trim(); 7987 if (banner !== deliveryBanner) { 7988 user.deliveryPreference.deliveryBanner = `${banner}|'${deliveryBanner}`; 7989 } else { 7990 user.deliveryPreference.deliveryBanner = this.DEFAULT_DELIVERY_BANNER; 7991 } 7992 } 7993 7994 user.houseHoldId = userCookieCommonData.houseId || 'ID not found'; 7995 user.clubCardNumber = userCookieCommonData.clubCard || 'ID not found'; 7996 user.sessionId = userCookieCommonData.grsSessionId || 'ID not found'; 7997 user.sscustomerstatus = typeof (userCookieCommonData.isARenrolled) !== 'undefined' 7998 ? (userCookieCommonData.isARenrolled === 'true' ? 'returning' : 'first') : 'ID not found'; 7999 if (userCookieCommonData.preference) { 8000 switch (userCookieCommonData.preference?.toLowerCase()) { 8001 case 'dug': 8002 user.deliveryPreference = 'dug'; 8003 break; 8004 case 'delivery': 8005 user.deliveryPreference = 'delivery'; 8006 break; 8007 case 'j4u': 8008 user.deliveryPreference = 'inStore'; 8009 break; 8010 } 8011 } 8012 } catch (e) { 8013 console.error('DATALAYER: Missing fields from SWY_SHARED_SESSION_INFO cookie! (SHOP, J4U, COMMON)'); 8014 } 8015 } else if (this.getLSItem('userInfo') || AB?.userInfo) { 8016 const userInfoLS = this.getLSItem('userInfo') ? this.safeJSONParse(this.getLSItem('userInfo')) : {}; 8017 user.clubCardNumber = userInfoLS?.clubCardNumber || AB?.userInfo?.clubCard || ''; 8018 const cookieInfo = AB.DATALAYER.getCookie('ACI_S_SWY_SYND_USER_INFO') || AB.DATALAYER.getCookie('SWY_SYND_USER_INFO'); 8019 user.loginStatus = `${cookieInfo && cookieInfo !== '' && this.safeJSONParse(cookieInfo).hhid ? this.LoginStatus.REGISTERED : this.LoginStatus.GUEST}|${AB.DATALAYER.getCookie('SAFEWAY_KMSI') && AB.DATALAYER.getCookie('SAFEWAY_KMSI') !== '' ? this.KmsiStatus[AB.DATALAYER.getCookie('SAFEWAY_KMSI')] : 'nonkmsi'}`; 8020 } 8021 }; 8022 8023 /** 8024 * Initializes the user object with info from 'SWY_SHARED_PII_SESSION_INFO' cookie 8025 */ 8026 this.initSharedPiiSessionInfo = function () { 8027 const sharedPiiInfoCookie = AB.DATALAYER.getCookie('ACI_S_SWY_SHARED_PII_SESSION_INFO') || AB.DATALAYER.getCookie('SWY_SHARED_PII_SESSION_INFO'); 8028 if (sharedPiiInfoCookie && sharedPiiInfoCookie != '') { 8029 let sharedPiiInfo; 8030 try { 8031 sharedPiiInfo = JSON.parse(decodeURI(sharedPiiInfoCookie)); 8032 } catch (e) { 8033 console.error('DATALAYER: Unable to parse the SWY_SHARED_PII_SESSION_INFO cookie!'); 8034 } 8035 8036 if (sharedPiiInfo) { 8037 let user = digitalData.user; 8038 user.customerId = sharedPiiInfo.gid || 'ID not found'; 8039 user.uuid = sharedPiiInfo.uuid || 'ID not found'; 8040 } 8041 } 8042 }; 8043 8044 /** 8045 * Initializes the user object with info from 'SAFEWAY_KMSI' c
8045ookie 8046 * (digitalData.user.loginStatus) 8047 */ 8048 this.initKmsi = function () { 8049 const kmsiCookie = AB.DATALAYER.getCookie('SAFEWAY_KMSI').toLowerCase(); 8050 let user = digitalData.user; 8051 if (kmsiCookie && user.type) { 8052 // after re-sign-in, the kmsi status has special values 8053 const reSignInCookie = AB.DATALAYER.getCookie(this.cookieNames.RE_SIGN_IN); 8054 if (reSignInCookie == 'true') { 8055 SWY.CONFIGSERVICE.setDomainCookie(this.cookieNames.RE_SIGN_IN, false, `${SWY.CONFIGSERVICE.resolvedBanner}.com`, 0); 8056 if (this.KmsiStatusReSignIn[kmsiCookie]) { 8057 user.loginStatus += '|' + this.KmsiStatusReSignIn[kmsiCookie]; 8058 } 8059 } else { 8060 if (this.KmsiStatus[kmsiCookie]) { 8061 user.loginStatus += '|' + this.KmsiStatus[kmsiCookie]; 8062 } 8063 } 8064 } 8065 }; 8066 8067 /** 8068 * Initializes the user object info from the LS item 'abOrderCount' or 'SWY_SHARED_SESSION_INFO' cookie 8069 */ 8070 this.initOrderCount = function () { 8071 if (digitalData.user?.userType !== 'R' && digitalData.user?.userType !== 'C') { 8072 return; 8073 } 8074 8075 // try to get the order count from the local storage, otherwise from the shared info cookie 8076 if (localStorage) { 8077 this.updateOrderCount(this.getLSItem('abOrderCount') || this.getLSItem('abOrderCountAnalyticsCC')); 8078 } else { 8079 const sharedInfoCookie = AB.DATALAYER.getCookie('ACI_S_SWY_SHARED_SESSION_INFO') || AB.DATALAYER.getCookie('SWY_SHARED_SESSION_INFO'); 8080 if (sharedInfoCookie && sharedInfoCookie != '') { 8081 let sharedSessionInfo = { 'info': '' }; 8082 try { 8083 sharedSessionInfo = JSON.parse(decodeURI(sharedInfoCookie)); 8084 } catch (e) { 8085 console.error('DATA Layer: Unable to parse the cookie'); 8086 } 8087 8088 if (sharedSessionInfo?.info?.SHOP?.userData) { 8089 this.updateOrderCount(sharedSessionInfo.info.SHOP.userData.orderCount); 8090 } 8091 } 8092 } 8093 }; 8094 8095 /** 8096 * Sets the SAFEWAY_RE_SIGN_IN cookie, used when setting the kmsi flag 8097 */ 8098 this.handleReSignIn = function () { 8099 SWY.CONFIGSERVICE.setDomainCookie(this.cookieNames.RE_SIGN_IN, AB.DATALAYER.isReSignInPage(), `${SWY.CONFIGSERVICE.resolvedBanner}.com`, 0); 8100 } 8101 8102 /** 8103 * Sets the cart savings (total number of coupons, total number of clipped coupons) 8104 * @param total 8105 * @param clipped 8106 */ 8107 this.updateCartSavings = function (total, clipped, response) { 8108 if (response && response['isWYSIWYGEnabled'] === true) { 8109 const cartSavings = AB.DATALAYER.calculateCartSavings(response?.summary?.cardSavings || response?.order?.cardSavings, 'cart'); 8110 8111 // Promocode savings are handled separately from other cart savings as they are in a different object in the response 8112 // They are initialized in cartSavings object as well, but they are set to 0 8113 const promocodeSavings = response?.wysiwygOrderSummary?.promotions ? AB.DATALAYER.totalPromoSavings(response?.wysiwygOrderSummary?.promotions) : 0; 8114 cartSavings.promocodeSavings = promocodeSavings; 8115 8116 // Get savings total value from cart response if it's available, otherwise attempt to calculate using analytics function 8117 const responseSavings = response?.summary?.totalCardSavings && !isNaN(parseFloat(response?.summary?.totalCardSavings)) ? parseFloat(response.summary.totalCardSavings) : 0; 8118 const analyticsSavings = cartSavings.total + promocodeSavings; 8119 const totalSavings = responseSavings ? responseSavings : analyticsSavings ? analyticsSavings : 0; 8120 const itemsList = response['cartItemsList'] || response?.['order']?.['orderItems'] || []; 8121 const couponIds = []; 8122 8123 if (itemsList && Array.isArray(itemsList) && itemsList.length > 0) { 8124 itemsList.forEach(item => { 8125 if (item.name && item.price && item.isFound && item.savings && item.savings.length > 0) {
8126 item.savings.forEach(offer => { 8127 if (offer.offerId) { 8128 couponIds.push(offer.offerId); 8129 } 8130 }) 8131 } 8132 }); 8133 } 8134 if (digitalData?.cart) { 8135 digitalData.cart.savings = { 8136 totalCoupons: total.toString(), 8137 couponApplied: couponIds.join(), 8138 clubcardSavings: cartSavings.clubcardSavings, 8139 j4uSavings: cartSavings.j4uSavings, 8140 rewardsSavings: cartSavings.rewardsSavings, 8141 promocodeSavings: cartSavings.promocodeSavings.toString(), 8142 employeeSavings: cartSavings.employeeSavings, 8143 totalSavings: totalSavings.toString() 8144 }; 8145 } 8146 } else if (response === undefined || response && response['isWYSIWYGEnabled'] === false || response && response['isWYSIWYGEnabled'] === undefined) { 8147 if (digitalData?.cart) { 8148 digitalData.cart.savings = { 8149 totalCoupons: total, 8150 couponsClipped: clipped 8151 }; 8152 } 8153 } 8154 }; 8155 8156 /** 8157 * Manage the link click data for the substritution and chat 8158 * @param {*} action chat / bottomchat / approve 8159 * @param {*} item reference to the item that is going to be substituted 8160 * @param {*} section section that might not be available in the item object 8161 */ 8162 this.trackSubsAndChatLinkClick = function ({ action, item, section, element }) { 8163 digitalData.crossPageData = digitalData.crossPageData || {} 8164 const { productInfo, modal, crossPageData } = digitalData; 8165 const isSubPref = item?.replacements?.some(item => !!item.saveAsFutureSubPreference) || false; 8166 productInfo.substitution = productInfo.substitution || {}; 8167 8168 const productID = section ? item.itemCode : item?.orderedItemId; 8169 8170 if (productID && action === 'approve') { 8171 const substitutionItems = item?.replacements?.map(item => item?.itemId ? item?.itemId : item?.itemCode); 8172 productInfo.substitution.productID = productID || ''; 8173 productInfo.substitution.isSubstitution = true; 8174 productInfo.substitution.substitutionOption = `substitute:${substitutionItems ? substitutionItems.join('#') : ''}`; 8175 } 8176 if (isSubPref) { 8177 this.setAnalyticsCrossPageData({ action: 'set', name: 'analyticsFeatureTracking', data: 'subsPrefACIP405518' }); 8178 } 8179 8180 const sectionFromAPI = section ? section : item?.substitutionPrefType === 'CustomerChoiceNotSubstitution' ? 'substitute-not-available' : `items-to-review${isSubPref ? '-pref-sub-tag' : ''}`; 8181 const eventString = action === 'saveSub' ? 'items-to-review-chat-pref-sub-tag|substitutions-approve' : action.includes('chat') ? `cta:order-details|${action === 'bottomchat' ? 'bottom-' : ''}button|chat-icon` : `${sectionFromAPI}|substitutions-approve`; 8182 const selectElement = action === 'approve' && document.querySelectorAll(`a[data-bpn="${productID}"]`) && Array.from(document.querySelectorAll(`a[data-bpn="${productID}"]`)).find(element => element.closest('.card.p-3')); 8183 const slotNumber = action === 'approve' && document.querySelectorAll('.card.p-3')?.length > 0 ? 8184 document.querySelectorAll('.card.p-3') && Array.from(document.querySelectorAll('.card.p-3'))?.indexOf(selectElement.closest('.card.p-3')) + 1 : 0; 8185 8186 modal.modalMessage = slotNumber ? `substitution-substitute-${slotNumber > 9 ? slotNumber : `0${slotNumber}`}#01` : ''; 8187 8188 const setOncePerPage = (eventName, event, section) => { 8189 let eventSent = false; 8190 // Find the index of the last untilNextEvent in appEventData 8191 let eventIndex = -1; 8192 eventIndex = appEventData.findLastIndex((eventObj) => eventObj?.event === this.EVENTS.PAGE_LOADED); 8193 8194 if (eventIndex !== -1) { 8195 eventSent = appEventData.slice(eventIndex).some((eventObj) => 8196 eventObj?.[`${event.includes('modal') ? 'modal' : 'link'}`]?.[`${event.includes('modal') ? 'modalAction' : 'linkName'}`] === eventName && 8197 (action === 'bottomchat' || crossPageData?.chatSection?.data?.includes(section)) 8198 ); 8199 } 8200 return eventSent; 8201 } 8202 const eventSent = action.includes('chat') && setOncePerPage(eventString, this.EVENTS.LINK_CLICK, sectionFromAPI); 8203 if (action.includes('chat')) { 8204 !eventSent && this.setLinkClickEvents(eventString); 8205 } else { 8206 AB.DATALAYER.pushEvent( 8207 this.EVENTS.LINK_CLICK, { 8208 link: { 8209 linkName: eventString 8210 }, 8211 page: digitalData?.pageDetails, 8212 pageFilter: digitalData?.pageFilter, 8213 user: digitalData?.user, 8214 productInfo: digitalData?.productInfo, 8215 modal: digitalData?.modal 8216 } 8217 ) 8218 } 8219 8220 if (action.includes('chat') && sectionFromAPI) { 8221 let listOfSections = []; 8222 8223 if (crossPageData?.chatSection?.data?.length > 0) { 8224 crossPageData?.chatSection?.data?.forEach(section => { listOfSections.push(section) }); 8225 if (!listOfSections.includes(sectionFromAPI)) { 8226 listOfSections.push(sectionFromAPI); 8227 } 8228 } else listOfSections.push(sectionFromAPI); 8229 this.setAnalyticsCrossPageData({ action: 'set', name: 'chatSection', data: listOfSections }); 8230 } 8231 8232 digitalData.modal = {}; 8233 digitalData.productInfo = {}; 8234 } 8235 8236 /** 8237 * Manage the link modal interaction for the substritution and chat 8238 * @param {*} action chat / bottomchat / approve 8239 * @param {*} item reference to the item that is going to be substituted 8240 */ 8241 this.trackSubsModalEvents = function ({ action, item, element }) { 8242 digitalData.crossPageData = digitalData.crossPageData || {} 8243 const { productInfo, modal, crossPageData } = digitalData; 8244 productInfo.substitution = productInfo.substitution || {}; 8245 8246 const setOncePerPage = (eventName, event, productID) => { 8247 let eventSent = false;
8248 // Find the index of the last untilNextEvent in appEventData 8249 let eventIndex = -1; 8250 eventIndex = appEventData.findLastIndex((eventObj) => eventObj?.event === this.EVENTS.PAGE_LOADED); 8251 8252 if (eventIndex !== -1) { 8253 eventSent = appEventData.slice(eventIndex).some((eventObj) => 8254 eventObj?.[`${event.includes('modal') ? 'modal' : 'link'}`]?.[`${event.includes('modal') ? 'modalAction' : 'linkName'}`] === eventName && 8255 eventName.includes(section) && 8256 (!productID || (action === 'view' || action === 'cancel' 8257 ? crossPageData.listOfIDs?.data?.includes(productID) : eventObj?.productInfo?.substitution?.productId === productID)) 8258 ); 8259 } 8260 return eventSent; 8261 } 8262 8263 8264 const productID = action === 'view' || action === 'cancel' ? document.getElementById('exampleModal')?.closest('.p-3')?.querySelector('a')?.dataset?.bpn : action === 'confirm' ? item?.itemCode ? item?.itemCode : item?.orderedItemId : ''; 8265 const isSubPref = item?.replacements?.some(item => !!item.saveAsFutureSubPreference) || false; 8266 productInfo.substitution = productInfo.substitution || {}; 8267 const section = (action === 'view' || action === 'cancel' ? (item?.replacedItems?.find(item => item?.orderedItemId === productID)) : item)?.substitutionPrefType === 'CustomerChoiceNotSubstitution' ? 'substitute-not-available' : 'items-to-review'; 8268 const event = action === 'view' ? this.EVENTS.MODAL_VIEW : this.EVENTS.MODAL_CLICK; 8269 const eventString = `${section}-don't-substitute${isSubPref ? '-pref-sub-tag' : ''}|${action}`; 8270 const selectElement = action === 'view' && document.querySelectorAll(`a[data-bpn="${productID}"]`) && Array.from(document.querySelectorAll(`a[data-bpn="${productID}"]`)).find(element => element.closest('.card.p-3')); 8271 const slotNumber = action === 'view' && document.querySelectorAll('.card.p-3')?.length > 0 ? 8272 document.querySelectorAll('.card.p-3') && Array.from(document.querySelectorAll('.card.p-3'))?.indexOf(selectElement.closest('.card.p-3')) + 1 : 0; 8273 8274 modal.modalMessage = action !== 'confirm' ? '' : modal.modalMessage; 8275 if (isSubPref) { 8276 this.setAnalyticsCrossPageData({ action: 'set', name: 'analyticsFeatureTracking', data: 'subsPrefACIP405518' }); 8277 } 8278 if (productID && (action === 'confirm')) { 8279 productInfo.substitution.productID = productID || ''; 8280 productInfo.substitution.isSubstitution = false; 8281 productInfo.substitution.substitutionOption = `do-not-substitute:${productID}`; 8282 } 8283 8284 const eventSent = setOncePerPage(eventString, event, productID); 8285 8286 if (action === 'view') { 8287 !eventSent && this.setModalViewEvents(eventString); 8288 } else if (action === 'cancel') { 8289 !eventSent && this.setModalClickEvents(eventString); 8290 } else { 8291 AB.DATALAYER.pushEvent( 8292 event, { 8293 modal: { 8294 modalAction: eventString, 8295 modalMessage: modal.modalMessage 8296 }, 8297 page: digitalData?.pageDetails, 8298 pageFilter: digitalData?.pageFilter, 8299 user: digitalData?.user, 8300 productInfo: digitalData?.productInfo 8301 } 8302 ) 8303 } 8304 modal.modalMessage = slotNumber ? `do-not-substitute-${slotNumber > 9 ? slotNumber : `0${slotNumber}`}#01` : ''; 8305 if (productID) { 8306 let listOfIDs = []; 8307 8308 if (crossPageData?.listOfIDs?.data?.length > 0) { 8309 crossPageData?.listOfIDs?.data?.forEach(id => { listOfIDs.push(id) }); 8310 if (!listOfIDs.includes(productID)) { 8311 listOfIDs.push(productID); 8312 } 8313 } else listOfIDs.push(productID); 8314 this.setAnalyticsCrossPageData({ action: 'set', name: 'listOfIDs', data: listOfIDs }); 8315 } 8316 8317 if (action === 'cancel' || action === 'confirm') { 8318 digitalData.modal = {}; 8319 digitalData.productInfo = {}; 8320 } 8321 } 8322 8323 /** 8324 * Sets the cart id 8325 * @param id 8326 */ 8327 this.updateCartId = function (id) { 8328 if (digitalData?.cart?.cartInfo) { 8329 digitalData.cart.cartInfo.cartId = id; 8330 } 8331 }; 8332 8333 /** 8334 * Sets the cart type 8335 * @param type 8336 */ 8337 this.updateCartType = function (type) { 8338 if (digitalData?.cart?.cartInfo) { 8339 digitalData.cart.cartInfo.cartType = type; 8340 } 8341 }; 8342 8343 /** 8344 * Sets cart isMFC 8345 * @param isMFC 8346 */ 8347 this.updateCartIsMFC = function (isMFC) { 8348 if (digitalData?.cart?.cartInfo) { 8349 digitalData.cart.cartInfo.isMFC = isMFC; 8350 } 8351 } 8352 8353 /** 8354 * Sets cart mfcUnavailableCount 8355 * @param mfcUnavailableCount 8356 */ 8357 this.updateCartMFCUnavailableCount = function (mfcUnavailableCount) { 8358 if (digitalData?.cart?.cartInfo) { 8359 let computedStateCart = digitalData.cart; 8360 computedStateCart.cartInfo.mfcUnavailableCount = mfcUnavailableCount; 8361 } 8362 } 8363 8364 this.setCartObj = function (cart, newItem, isCalledFromBIA, isEditCart) { 8365 let wysiwygFlag = cart.guestCartId ? true : cart.isWYSIWYGEnabled; 8366 let allItems = cart && cart['cartItemsList'] ? cart['cartItemsList'] : cart && cart['orderItems'] ? cart['orderItems'] : cart && cart['cartItems'] ? cart['cartItems'] : cart ? cart : []; 8367 let sellerCartsCombined = []; 8368 digitalData.cart.sellerCarts = digitalData.cart.sellerCarts || []; 8369 digitalData.cart.absItems = digitalData.cart.absItems || []; 8370 const isSelectiveCart = cart?.selectiveCart; 8371 8372 let filtereredItems = []; 8373 if (Array.isArray(allItems) && allItems.length > 0) { 8374 if (!isCalledFromBIA) { 8375 let newFiltereredItems = allItems.filter(function (item) { 8376 if (isSelectiveCart) { 8377 return item.itemId && item.isFound && item.isAvailable; 8378 } 8379 return item.name && item.price && item.isFound; 8380 }); 8381 filtereredItems = newFiltereredItems; 8382 } else { 8383 let BIAFiltered = allItems.filter(function (item) { 8384 return item.name && item.price; 8385 }); 8386 filtereredItems = BIAFiltered; 8387 } 8388 } 8389 8390 if (cart && (cart['seller'] && Object.keys(cart['seller']).length > 0 || cart.cartCategory === '1P_WINE')) { 8391 let mktpCartId = cart.guestCartId ? cart.guestCartId : cart.cartId; 8392 let mktpCartType = digitalData?.cart?.cartInfo?.cartType || ''; 8393 let mktpSKUS = []; 8394 let mktpSubtotal = cart.summary?.totalBasePrice?.toString(); 8395 let mktpSellerId = cart.seller ? cart.seller.sellerId : '22222222'; 8396 digitalData.cart.cartInfo.cartIDMap[mktpSellerId] = mktpCartId; 8397
8398 if (window['analyticsSellerIds']?.indexOf(mktpSellerId) === -1) { 8399 window['analyticsSellerIds'].push(mktpSellerId); 8400 } 8401 8402 if (filtereredItems.length > 0) { 8403 if (wysiwygFlag && cart && cart.summary) { 8404 filtereredItems.forEach(item => { 8405 digitalData.cart.sellerCarts[`sellerItem${mktpSellerId}`] = filtereredItems.map((item) => { 8406 return AB.DATALAYER.setCartItemsWYSIWYG(item, mktpSellerId, mktpCartId); 8407 }) 8408 mktpSKUS.push(item.itemId); 8409 }); 8410 } else { 8411 filtereredItems.forEach(item => { 8412 digitalData.cart.sellerCarts[`sellerItem${mktpSellerId}`] = filtereredItems.map((item) => { 8413 return AB.DATALAYER.setCartItemsNonWYSIWYG(item, mktpSellerId, mktpCartId); 8414 }); 8415 mktpSKUS.push(item.itemId || item.productID || item.id); 8416 }); 8417 } 8418 } 8419 8420 let mktpCart = `${mktpSellerId}#${mktpCartId}#${mktpCartType}#${mktpSKUS.join(':')}#${mktpSubtotal}`; 8421 if (cart.cartCategory === '1P_WINE') { 8422 digitalData.cart.cartInfo.cartIDWineShop = `${mktpSellerId}#${mktpCartId}#${mktpCartType}#${mktpSKUS.join(',')}#${mktpSubtotal}`; 8423 digitalData.cart.transaction.subTotalWineCart = digitalData.cart.transaction.subTotalWineCart || {}; 8424 digitalData.cart.transaction.subTotalWineCart.subTotal = +mktpSubtotal; 8425 digitalData.cart.transaction.subTotalWineCart.sellerId = mktpSellerId; 8426 } else { 8427 let sellerCartsIDs = []; 8428 if (Object.values(digitalData.cart.marketplace).length > 0) { 8429 Object.values(digitalData.cart.marketplace).forEach(seller => { 8430 sellerCartsIDs.push(seller['sellerId']); 8431 }); 8432 } 8433 for (let i = 1; i <= 8; i++) { 8434 if (digitalData.cart.marketplace[`seller${i}`] && digitalData.cart.marketplace[`seller${i}`].sellerId === mktpSellerId) { 8435 digitalData.cart.marketplace[`seller${i}`].sellerCartID = mktpCart; 8436 break; 8437 } else if (!digitalData.cart.marketplace[`seller${i}`] && sellerCartsIDs.indexOf(mktpSellerId) === -1) { 8438 digitalData.cart.marketplace[`seller${i}`] = { 8439 sellerCartID: mktpCart, 8440 sellerId: mktpSellerId, 8441 sellerPfm: {} 8442 }; 8443 mktpSKUS.forEach(id => { 8444 if (id) { 8445 digitalData.cart.marketplace[`seller${i}`].sellerPfm[id] = digitalData.cart.marketplace[`seller${i}`].sellerPfm[id] ? digitalData.cart.marketplace[`seller${i}`].sellerPfm[id] : `${mktpSellerId}#product-details#PDP-landing#non-search#not-recommended#R01#S01`; 8446 } 8447 }); 8448 break; 8449 } 8450 } 8451 if (digitalData.cart.transaction) { 8452 digitalData.cart.transaction.subTotalMarketplaceCarts = digitalData.cart.transaction.subTotalMarketplaceCarts 8453 ? digitalData.cart.transaction.subTotalMarketplaceCarts.includes(`${mktpSellerId}:`) 8454 ? digitalData.cart.transaction.subTotalMarketplaceCarts 8455 : `${digitalData.cart.transaction.subTotalMarketplaceCarts}#${mktpSellerId}:${mktpSubtotal}` 8456 : `${mktpSellerId}:${mktpSubtotal}`; 8457 } 8458 } 8459 } else if (cart.cartCategory === '1P' || cart.cartCategory === '1P_B2B' || isEditCart) { 8460 let cartId = cart.guestCartId ? cart.guestCartId : cart.cartId; 8461 let sellerId = cart.seller ? cart.seller.sellerId : AB.DATALAYER.getBannerSellerId();
8462 if (window['analyticsSellerIds']?.indexOf(AB.DATALAYER.getBannerSellerId()) === -1) { 8463 window['analyticsSellerIds'].push(AB.DATALAYER.getBannerSellerId()); 8464 } 8465 8466 // Banner is NOT WYSIWYG enabled 8467 if (wysiwygFlag === false || wysiwygFlag == undefined) { 8468 if (digitalData && digitalData.cart) { 8469 if (filtereredItems.length > 0) { 8470 digitalData.cart.absItems = filtereredItems.map((item) => { 8471 return AB.DATALAYER.setCartItemsNonWYSIWYG(item, sellerId, cartId); 8472 }); 8473 8474 let subTotal = 0; 8475 // Add item prices to calculate subtotal 8476 digitalData.cart.absItems.forEach(function (item) { 8477 if (!isNaN(parseFloat(item.totalPrice))) { 8478 subTotal += parseFloat(item.totalPrice); 8479 } 8480 }); 8481 if (digitalData.cart.transaction) { 8482 digitalData.cart.transaction.subTotal = subTotal; 8483 } 8484 } 8485 digitalData.cart.isWYSIWYGEnabled = false; 8486 digitalData.cart.cartInfo.cartId = cart.guestCartId ? cart.guestCartId : cart.cartId ? cart.cartId : ''; 8487 } 8488 if (digitalData && digitalData.productInfo && newItem) { 8489 digitalData.productInfo.productName = newItem.name; 8490 digitalData.productInfo.listPrice = newItem.price?.toString(); 8491 } 8492 } else if (wysiwygFlag === true) { 8493 // Banner IS WYSIWYG enabled 8494 if (digitalData && digitalData.cart) { 8495 if (filtereredItems.length > 0) { 8496 digitalData.cart.absItems = filtereredItems.map((item) => { 8497 return AB.DATALAYER.setCartItemsWYSIWYG(item, sellerId, cartId); 8498 }); 8499 8500 let subTotal = 0; 8501 digitalData.cart.absItems.forEach(function (item) { 8502 if (!isNaN(parseFloat(item.totalPrice))) { 8503 subTotal += parseFloat(item.totalPrice); 8504 } 8505 }); 8506 if (digitalData.cart.transaction) { 8507 digitalData.cart.transaction.subTotal = subTotal; 8508 } 8509 } 8510 digitalData.cart.isWYSIWYGEnabled = true; 8511 digitalData.cart.cartInfo.cartId = cart.guestCartId ? cart.guestCartId : cart.cartId ? cart.cartId : ''; 8512 } 8513 8514 if (digitalData && digitalData.productInfo) { 8515 if (newItem) { 8516 digitalData.productInfo.productName = newItem.name; 8517 digitalData.productInfo.listPrice = newItem.price?.toString(); 8518 } 8519 if (digitalData.productInfo.productID && digitalData.cart && digitalData.cart.item && digitalData.cart.item.length > 0) { 8520 const productAdded = digitalData.cart.item.filter(item => item.productID == digitalData.productInfo.productID)[0]; 8521 if (productAdded) { 8522 digitalData.productInfo.offerType = productAdded.offerType; 8523 digitalData.productInfo.offerText = productAdded.offerText; 8524 } 8525 } 8526 } 8527 } 8528 window.digitalData.cart.cartInfo.cartIDMap[`${AB.DATALAYER.getBannerSellerId()}`] = cart.guestCartId ? cart.guestCartId : cart.cartId; 8529 if (cart?.summary?.cardSavings) { 8530 digitalData.cart.transaction.revenue = cart?.summary?.transactionTotal?.toString() || '0'; 8531 this.updateCartSavings(0, 0, cart); 8532 } else { 8533 digitalData.cart.transaction.revenue = cart?.summary?.totalEstimatedPrice?.toString() || '0'; 8534 digitalData.cart.savings.totalSavings = cart?.summary?.estimatedClubCardSavings?.toString() || ''; 8535 } 8536 } else if (cart?.cartCategory === '1P_SNS') { 8537 digitalData.cart.cartInfo.cartId = cart?.cartId ? cart.cartId : ''; 8538 } 8539 const currentProduct = digitalData.cart.item?.find(item => { 8540 return item.productID == digitalData.productInfo.productID; 8541 }); 8542 digitalData.cart.cartInfo.currentCartId = currentProduct?.productCartId?.toString(); 8543 digitalData.productInfo.sellerId = digitalData.productInfo.sellerId || currentProduct?.productSellerId?.toString(); 8544 sellerCartsCombined = Array.prototype.concat.apply([], Object.values(digitalData.cart.sellerCarts)); 8545 digitalData.cart.item = digitalData.cart.absItems.concat(sellerCartsCombined); 8546 if (digitalData.cart?.cartInfo?.cartType === 'mini_cart' || this.isCartAndCheckoutPage('erums/cart')) {
8547 digitalData.pageDetails.sellerId = window['analyticsSellerIds'].join(','); 8548 } 8549 AB.DATALAYER.setActiveCarts(digitalData.cart.item); 8550 } 8551 8552 /** 8553 * Updates the cart items list on product actions and minicart open 8554 * @param allItems 8555 * @param newItem 8556 * @param wysiwygFlag 8557 */ 8558 this.updateCart = function (cart, newItem, wysiwygFlag, isCalledFromBIA) { 8559 digitalData.cart = digitalData.cart || {}; 8560 window['analyticsSellerIds'] = window['analyticsSellerIds'] ? window['analyticsSellerIds'] : localStorage.getItem('analyticsSellerIds') && localStorage.getItem('analyticsSellerIds').split(',').length > 0 ? localStorage.getItem('analyticsSellerIds').split(',') : []; 8561 8562 //if taxes table is visible add corresponding impression 8563 if (cart?.summary?.tax && !digitalData?.pageFilter?.impressions?.includes('minicart-tax') && digitalData?.cart?.cartInfo?.cartType === 'mini_cart') { 8564 digitalData.pageFilter.impressions = digitalData.pageFilter.impressions ? `${digitalData.pageFilter.impressions},minicart-tax` : 'minicart-tax'; 8565 } 8566 8567 // Handling multicart response 8568 if (cart.cart && Object.values(cart.cart).length > 0) { 8569 digitalData.cart.cartsFromAPI = Object.values(cart.cart); 8570 } else { 8571 digitalData.cart.cartsFromAPI = [cart]; 8572 } 8573 digitalData.cart.marketplace = digitalData.cart.marketplace || {}; 8574 digitalData.cart.cartInfo = digitalData.cart.cartInfo || {}; 8575 digitalData.cart.cartInfo.cartIDMap = digitalData.cart.cartInfo.cartIDMap || {}; 8576 8577 if (Object.keys(digitalData.cart.marketplace).length === 0 && localStorage.getItem('analyticsSellerCartsInfo') && localStorage.getItem('analyticsSellerCartsInfo') !== 'undefined') { 8578 digitalData.cart.marketplace = AB.DATALAYER.safeJSONParse(AB.DATALAYER.getLSItem('analyticsSellerCartsInfo')); 8579 } 8580 8581 if (digitalData?.cart?.cartsFromAPI && digitalData.cart.cartsFromAPI.length > 0) { 8582 digitalData.cart.cartsFromAPI.forEach(cart => { 8583 this.setCartObj(cart, newItem, isCalledFromBIA); 8584 }); 8585 if (digitalData.cart.marketplace && Object.keys(digitalData.cart.marketplace).length > 0) { 8586 for (let i = 1; i <= 8; i++) { 8587 if (digitalData.cart.marketplace[`seller${i}`] && window['analyticsSellerIds'].indexOf(digitalData.cart.marketplace[`seller${i}`].sellerId) === -1) { 8588 delete digitalData.cart.marketplace[`seller${i}`]; 8589 } 8590 } 8591 localStorage.setItem('analyticsSellerCartsInfo', JSON.stringify(window.digitalData.cart.marketplace)); 8592 } 8593 } 8594 }; 8595 8596 this.setCartItemsWYSIWYG = function (item, sellerId, cartId) { 8597 let itemSavings = item.savings || []; 8598 let savings = AB.DATALAYER.calculateCartSavings(itemSavings, 'item'); 8599 // Clubcard savings can be found in and out of the savings array, we must add the two values for the full CC Savings value 8600 let ccSavings = item.clubCardSaving && !isNaN(parseFloat(item.clubCardSaving)) ? item.clubCardSaving : 0; 8601 // savings.clubcardSavings is returned as a string from the method in utils, which we are also using for cart so it cannot be passed as integer from there 8602 let totalCCSavings = !isNaN(parseFloat(savings.clubcardSavings)) ? parseFloat(savings.clubcardSavings) + ccSavings : 0; 8603 let totalItemSavingsWithCC = savings.total + ccSavings; 8604 8605 let offerTypeArray = []; 8606 let offerTextArray = []; 8607 if (itemSavings.length > 0) { 8608 itemSavings.forEach(offer => { 8609 let externalOfferId = offer.externalOfferId || ''; 8610 let isNDOffer = externalOfferId.indexOf('-ND') !== -1; 8611 if (offer.categoryName) { 8612 offerTypeArray.push(`${isNDOffer ? 'ND:' : ''}${offer.categoryName}`); 8613 } 8614 if (offer.displayOfferTxt) { 8615 offerTextArray.push(`${isNDOffer ? 'ND:' : ''}${offer.displayOfferTxt}`); 8616 } else if (offer.linkPluNumber && offer.source === 'CMS') { 8617 offerTextArray.push(`${isNDOffer ? 'ND:' : ''}Buy 1 Get 1 Free`); 8618 } else if (offer.discountMessage) { 8619 offerTextArray.push(`${isNDOffer ? 'ND:' : ''}${offer.discountMessage}`); 8620 } 8621 }); 8622 } 8623 8624 // Setting default value for prices used for calculations, as the cart API response sometimes doesn't send a value for prices for some items 8625 let basePrice = item.basePrice ? item.basePrice : 0; 8626 let units = item.quantity ? item.quantity : item.qty ? item.qty : item.units ? item.units : 0; 8627 let totalBasePrice = basePrice * units; 8628 let totalItemPrice = totalBasePrice - totalItemSavingsWithCC; 8629 let itemPrice = totalItemPrice / units; 8630 if (itemPrice <= 0) { 8631 itemPrice = 0; 8632 } 8633 let clippedCoupons = '0'; 8634 let savedCoupons = '0'; 8635 if (item.clipped) { 8636 clippedCoupons = (item.clipped.length > 0) ? item.clipped.length.toString() : '0'; 8637 } 8638 if (item.savings) { 8639 savedCoupons = (item.savings.length > 0) ? item.savings.length.toString() : '0'; 8640 } 8641 8642 let product = { 8643 productID: item.id ? item.id.toString() : item.itemId ? item.itemId.toString() : '', 8644 productName: item.name, 8645 units: (units > 0) ? units.toString() : '', 8646 listPrice: item.price ? item.price.toString() : '', 8647 basePrice: (basePrice > 0) ? basePrice.toString() : '', 8648 pricePerUnit: item.pricePer ? item.pricePer.toString() : item.pricePerUnit ? item.pricePerUnit.toString() : item.unitPrice ? item.unitPrice.toString() : '', 8649 unitType: item.unitType ? item.unitType : item.unitQuantity ? item.unitQuantity : '', 8650 totalPrice: item.totalPrice ? item.totalPrice.toString() : '', 8651 itemPrice: (itemPrice > 0) ? itemPrice.toString() : '', 8652 inventory: (item.isAvailable ? '' : 'not-') + 'available', 8653 couponClipped: clippedCoupons, 8654 couponApplied: savedCoupons, 8655 clubcardItemSavings: totalCCSavings.toString(), 8656 j4uItemSavings: savings.j4uSavings, 8657 rewardsItemSavings: savings.rewardsSavings, 8658 employeeItemSavings: savings.employeeSavings, 8659 totalItemSavings: totalItemSavingsWithCC.toString(), 8660 offerType: offerTypeArray.join('#'), 8661 offerText: offerTextArray.join('#'), 8662 isFound: item.isFound, 8663 productSellerId: sellerId ? sellerId : '', 8664 source: item.source ? item.source.toLowerCase() : '',
8665 snapEligible: item.snapEligible === true ? 'true' : 'false', 8666 productCartId: cartId ? cartId : '', 8667 shelfName: item?.shelfName || '', 8668 isProductTrackingDisabled: !!item.isProductTrackingDisabled, 8669 productBrand: item?.brand 8670 } 8671 8672 // Set schedule and save source for order confirmation page 8673 if (item?.snsSub?.isSnsSub && item?.snsSub?.snsFrequency) { 8674 const snsProductObject = AB.DATALAYER.safeJSONParse(AB.DATALAYER.getLSItem('snsSubscriptionProducts')); 8675 product.scheduleandSaveSource = `${snsProductObject[item.itemId?.toString()] || 'no-value'}#subscribe#every-${item?.snsSub?.snsFrequency.count}-${item?.snsSub?.snsFrequency.unit}${item?.snsSub?.snsFrequency.count > 1 ? 's' : ''}`; 8676 if (snsProductObject.hasOwnProperty(item.itemId?.toString())) { 8677 delete snsProductObject[item.itemId?.toString()]; 8678 } 8679 } 8680 8681 return product; 8682 } 8683 8684 // We need to check if non WYSIWYG flow is still needed/available anywhere or if we can remove these functions 8685 this.setCartItemsNonWYSIWYG = function (item, sellerId, cartId) { 8686 let clippedCoupons = '0'; 8687 let savedCoupons = '0'; 8688 if (item.clipped) { 8689 clippedCoupons = (item.clipped.length > 0) ? item.clipped.length.toString() : '0'; 8690 } 8691 if (item.savings) { 8692 savedCoupons = (item.savings.length > 0) ? item.savings.length.toString() : '0'; 8693 } 8694 8695 let product = { 8696 productID: item.id ? item.id.toString() : item.itemId ? item.itemId.toString() : '', 8697 productName: item.name, 8698 units: item.quantity ? item.quantity.toString() : item.qty ? item.qty.toString() : item.units ? item.units.toString() : '', 8699 unitType: item.unitType ? item.unitType : item.unitQuantity ? item.unitQuantity : '', 8700 pricePerUnit: item.pricePer ? item.pricePer.toString() : item.pricePerUnit ? item.pricePerUnit.toString() : item.unitPrice ? item.unitPrice.toString() : '', 8701 basePrice: item.basePrice + '', 8702 listPrice: item.price + '', 8703 totalPrice: item.quantity && item.price ? (item.quantity * item.price).toString() : item.qty && item.price ? (item.qty * item.price).toString() : '', 8704 inventory: (item.isAvailable ? '' : 'not-') + 'available', 8705 isFound: item.isFound ? item.isFound : '', 8706 productSellerId: sellerId ? sellerId : '', 8707 couponClipped: clippedCoupons, 8708 couponApplied: savedCoupons, 8709 productCartId: cartId ? cartId : '',
8710 shelfName: item?.shelfName || '', 8711 isProductTrackingDisabled: !!item.isProductTrackingDisabled 8712 }; 8713 8714 // Set schedule and save source for order confirmation page 8715 if (item?.snsSub?.isSnsSub && item?.snsSub?.snsFrequency) { 8716 const snsProductObject = AB.DATALAYER.safeJSONParse(AB.DATALAYER.getLSItem('snsSubscriptionProducts')); 8717 product.scheduleandSaveSource = `${snsProductObject[item.itemId?.toString()] || 'no-value'}#subscribe#every-${item?.snsSub?.snsFrequency.count}-${item?.snsSub?.snsFrequency.unit}${item?.snsSub?.snsFrequency.count > 1 ? 's' : ''}`; 8718 if (snsProductObject.hasOwnProperty(item.itemId?.toString())) { 8719 delete snsProductObject[item.itemId?.toString()]; 8720 } 8721 } 8722 8723 return product; 8724 } 8725 8726 /** 8727 * Sets the 'mini_cart' event on minicart open 8728 */ 8729 this.updateMiniCartEvent = function () { 8730 window['analyticsCartnumber'] = window['analyticsCartnumber'] ? Number(window['analyticsCartnumber']) + 1 : 1; 8731 if ((window['analyticsNumberOfCarts'] && Number(window['analyticsCartnumber']) === Number(window['analyticsNumberOfCarts'])) || (!window['analyticsNumberOfCarts']) || (Number(window['analyticsCartnumber']) > Number(window['analyticsNumberOfCarts']))) { 8732 digitalData.pageDetails.sellerId = window['analyticsSellerIds'].join(','); 8733 window.digitalData.cart.cartInfo.currentCartId = window.digitalData.cart.cartInfo.cartIDMap ? Object.values(window.digitalData.cart.cartInfo.cartIDMap).join() : ''; 8734 localStorage.setItem('analyticsSellerIds', window['analyticsSellerIds'].join(',')); 8735 if (digitalData) { 8736 this.pushEvent( 8737 this.EVENTS.MINICART, 8738 { 8739 cart: digitalData.cart, 8740 page: digitalData.pageDetails, 8741 pageFilter: digitalData.pageFilter 8742 } 8743 ); 8744 } 8745 8746 AB.DATALAYER.removeImpression('minicart-tax'); 8747 AB.DATALAYER.removeImpression('exp-mini-chk'); 8748 8749 } 8750 } 8751 8752 /** 8753 * Sets minicart promo banner impressions 8754 */ 8755 this.setMiniCartBannerImpressions = function (cart, isNewUser) { 8756 if (digitalData?.pageFilter && cart?.cartCategory === '1P' && isNewUser) { 8757 const deliveryPreference = digitalData.user?.deliveryPreference; 8758 if (deliveryPreference && deliveryPreference !== 'inStore') { 8759 digitalData.pageFilter.impressions = `minicart-promo-banner-save${deliveryPreference === 'dug' ? '3' : '2'}0`; 8760 } 8761 this.setLSItem('analyticsPromoBannerView', 'checkout-promo-banner-visible'); 8762 } 8763 } 8764 8765 /** 8766 * Track scroll impressions on initial product load 8767 */ 8768 this.setAMCImpressionsGrid = function () { 8769 if ((this.isSearchResultsPage() || this.isAislesPage()) && !this.isWineShopPage() && !window.isAnalyticsMarketplacePage) { 8770 if (digitalData?.search.offset == 0) { 8771 digitalData.scrollImpressionsToTrack = digitalData.search.impressions ? structuredClone(digitalData.search.impressions) : []; 8772 digitalData.scrollImpressionsTracked = digitalData.scrollImpressionsTracked || []; 8773 let waitForElements; 8774 8775 function triggerImpression() { 8776 let numberOfProducts; 8777 const visibleProducts = []; 8778 digitalData.scrollImpressionsToTrack.forEach((impression) => { 8779 if (impression?.product?.id) { 8780 const element = document.querySelector(`#pg${impression?.product?.id}`)?.closest('product-item, product-item-v2, product-item-al-v1, product-item-al-v2'); 8781 if (element) { 8782 const currentGridTagName = element.closest('product-generic-grid-al-v1, search-grid, product-generic-grid, buy-it-again-grid')?.tagName; 8783 numberOfProducts = document.querySelectorAll(`${currentGridTagName} product-item, ${currentGridTagName} product-item-v2, ${currentGridTagName} product-item-al-v1, ${currentGridTagName} product-item-al-v2`)?.length || 0; 8784 if (AB.DATALAYER.isElementVisible(element) && digitalData.scrollImpressionsTracked.indexOf(impression.product.id) == -1) { 8785 if (waitForElements) { 8786 clearInterval(waitForElements); 8787 waitForElements = undefined; 8788 } 8789 digitalData.scrollImpressionsTracked.push(impression.product.id); 8790 visibleProducts.push(impression.product); 8791 } 8792 } 8793 } 8794 }) 8795 if (visibleProducts.length > 0) { 8796 let payload = { 8797 link: { 8798 linkName: `${digitalData.pageDetails.subSection1}-scrolled-impressions` 8799 }, 8800 serverData: { 8801 attributionToken: digitalData.user.storedAttributionToken 8802 }, 8803 page: digitalData.pageDetails, 8804 search: digitalData.search, 8805 user: digitalData.user 8806 }; 8807 payload.search.impressions = visibleProducts; 8808 AB.DATALAYER.pushEvent('scrolled-impressions', payload); 8809 } 8810 if ((Array.isArray(digitalData.scrollImpressionsTracked) && numberOfProducts && digitalData.scrollImpressionsTracked.length == numberOfProducts) || digitalData.scrollImpressionsToTrack?.length === 0) { 8811 document.removeEventListener('scroll', triggerImpression); 8812 } 8813 } 8814 8815 if (Array.isArray(digitalData.scrollImpressionsToTrack) && digitalData.scrollImpressionsToTrack.length > 0) { 8816 waitForElements = setInterval(triggerImpression, 300); 8817 document.addEventListener('scroll', triggerImpression); 8818 } 8819 } 8820 } 8821 } 8822
8823 this.setAMCImpressionsCarousel = function (lazyWrapper, productList, action) { 8824 if (((this.isSearchResultsPage() || this.isAislesPage() || this.isPdp()) && !this.isWineShopPage() && !window.isAnalyticsMarketplacePage) && lazyWrapper?.dataset?.carouselDriven === 'featured-products') { 8825 const carouselTitle = lazyWrapper?.closest('.master-product-carousel')?.querySelector('.carousel-header-text') ? lazyWrapper?.closest('.master-product-carousel')?.querySelector('.carousel-header-text')?.textContent : lazyWrapper?.closest('.master-product-carousel')?.querySelector('.carousel-nav__header-title') ? lazyWrapper?.closest('.master-product-carousel')?.querySelector('.carousel-nav__header-title')?.textContent : 'no-value'; 8826 const formattedCarouselTitle = lazyWrapper?.closest('.master-product-carousel')?.querySelector('.carousel-header-text') ? AB.DATALAYER.sanitizeAnalyticsText(lazyWrapper?.closest('.master-product-carousel')?.querySelector('.carousel-header-text')?.textContent) : lazyWrapper?.closest('.master-product-carousel')?.querySelector('.carousel-nav__header-title') ? AB.DATALAYER.sanitizeAnalyticsText(lazyWrapper?.closest('.master-product-carousel')?.querySelector('.carousel-nav__header-title')?.textContent) : 'no-value'; 8827 digitalData.carouselAMCImpressionsToTrack = digitalData.carouselAMCImpressionsToTrack || {}; 8828 digitalData.carouselAMCImpressionsTracked = digitalData.carouselAMCImpressionsTracked || {}; 8829 if (formattedCarouselTitle) { 8830 digitalData.carouselAMCImpressionsToTrack[formattedCarouselTitle] = digitalData.carouselAMCImpressionsToTrack[formattedCarouselTitle] && Array.isArray(digitalData.carouselAMCImpressionsToTrack[formattedCarouselTitle]) && productList && Array.isArray(productList) ? digitalData.carouselAMCImpressionsToTrack[formattedCarouselTitle].concat(productList) : productList && Array.isArray(productList) ? productList : []; 8831 digitalData.carouselAMCImpressionsTracked[formattedCarouselTitle] = digitalData.carouselAMCImpressionsTracked[formattedCarouselTitle] || []; 8832 } 8833 8834 function triggerImpression() { 8835 const visibleProducts = []; 8836 digitalData.carouselAMCImpressionsToTrack[formattedCarouselTitle]?.forEach((impression) => { 8837 if (AB.DATALAYER.isAlcoholAdTrackingDisabled(impression)) { 8838 return; 8839 } 8840 if (impression?.id) { 8841 const element = lazyWrapper?.querySelector(`#pg${impression?.id}`)?.closest('product-carousel-item, product-carousel-item-v2, product-carousel-item-al-v1, product-carousel-item-al-v2'); 8842 if (element) { 8843 if (AB.DATALAYER.isElementVisible(element) && digitalData.carouselAMCImpressionsTracked && digitalData.carouselAMCImpressionsTracked[formattedCarouselTitle].indexOf(impression.id) == -1) { 8844 document.removeEventListener('scroll', triggerImpression); 8845 digitalData.carouselAMCImpressionsTracked[formattedCarouselTitle].push(impression.id); 8846 const isSpImpFixOn = !window?.SWY?.CONFIGSERVICE?.featureToggleConfig?.isSponsoredImpressionFixDisabled; 8847 if (isSpImpFixOn) { 8848 const placement = AB.DATALAYER.setCarouselPosition({ productId: impression.id, carouselSection: carouselTitle, nativeElement: element }) || 'R00#S00'; 8849 const isSponsored = !!(impression.adId || impression.featured); 8850 // recType is used only inside the pfm string; rTyp is the canonical eVar14 value. 8851 const recType = isSponsored ? 'sponsored' : 'recs'; 8852 const rTyp = isSponsored ? 'sponsored' : 'recommended'; 8853 const currentProductPfm = `${AB.DATALAYER.pfmFromCurrentPage}#${formattedCarouselTitle}#${AB.DATALAYER.isSearchResultsPage() ? digitalData.search?.internalSearchTerm : 'non-search'}#${recType}#${placement}`; 8854 const detail = impression?.adobeMetrics?.metricA 8855 || (AB.DATALAYER.returnImpressionAdditionalDetail ? AB.DATALAYER.returnImpressionAdditionalDetail(impression) : ''); 8856 8857 visibleProducts.push({ 8858 section: carouselTitle ? AB.DATALAYER.sanitizeAnalyticsText(carouselTitle) : '', 8859 lp: impression.price, 8860 pfm: currentProductPfm, 8861 pId: impression.id, 8862 units: AB.DATALAYER.getProductCartUnits(impression.id), 8863 pName: AB.DATALAYER.isPdp() ? impression.name : undefined, 8864 sponsored: isSponsored, 8865 badge: AB.DATALAYER.getUIBadge(lazyWra
8865pper, impression.id), 8866 display: 'carousel', 8867 placement: placement, 8868 detail: detail, 8869 rTyp: rTyp 8870 }); 8871 } else { 8872 const placement = AB.DATALAYER.setCarouselPosition({ productId: impression.id, carouselSection: carouselTitle }); 8873 const recType = impression.adId || impression.featured ? 'sponsored' : 'recs'; 8874 const currentProductPfm = `${AB.DATALAYER.pfmFromCurrentPage}#${formattedCarouselTitle}#${AB.DATALAYER.isSearchResultsPage() ? digitalData.search?.internalSearchTerm : 'non-search'}#${recType}#${placement}`; 8875 8876 visibleProducts.push({ 8877 section: carouselTitle ? AB.DATALAYER.sanitizeAnalyticsText(carouselTitle) : '', 8878 lp: impression.price, 8879 pfm: currentProductPfm, 8880 pId: impression.id, 8881 units: impression.quantity || impression.qty || 0, 8882 pName: AB.DATALAYER.isPdp() ? impression.name : undefined, 8883 sponsored: true, 8884 badge: AB.DATALAYER.getUIBadge(lazyWrapper, impression.id) 8885 }); 8886 } 8887 } 8888 } 8889 } 8890 }) 8891 if (visibleProducts.length > 0) { 8892 let payload = { 8893 link: { 8894 linkName: `${digitalData.pageDetails.subSection1}-product-impressions` 8895 }, 8896 page: digitalData.pageDetails, 8897 search: digitalData.search, 8898 user: digitalData.user, 8899 pageFilter: digitalData.pageFilter 8900 }; 8901 if (AB.DATALAYER.isSearchResultsPage()) { 8902 payload.search = digitalData.search; 8903 } 8904 if (AB.DATALAYER.isPdp()) { 8905 payload.productInfo = AB.DATALAYER.safeJSONParse(AB.DATALAYER.getLSItem('analyticsAMCPDPInfo')); 8906 } 8907 payload.carousel = visibleProducts; 8908 payload.pageFilter.mediaType = 'product-carousel'; 8909 payload.pageFilter.placement = ''; 8910 if (!window?.SWY?.CONFIGSERVICE?.featureToggleConfig?.isSponsoredImpressionFixDisabled) { 8911 // Drive rTyp (eVar14) from the first visible product's classification 8912 const firstRTyp = visibleProducts[0]?.rTyp; 8913 if (firstRTyp) { 8914 payload.pageFilter.recommendationType = firstRTyp; 8915 } 8916 } 8917 AB.DATALAYER.pushEvent('product-impressions', payload); 8918 digitalData.pageFilter.mediaType = ''; 8919 } 8920 } 8921 8922 if (Array.isArray(digitalData.carouselAMCImpressionsToTrack[formattedCarouselTitle]) && digitalData.carouselAMCImpressionsToTrack[formattedCarouselTitle].length > 0 && action === 'load') { 8923 document.addEventListener('scroll', triggerImpression); 8924 } 8925 } 8926 } 8927
8928 this.setSmartBasketImpressions = function (lazyWrapper) { 8929 if (lazyWrapper?.dataset?.targetedComponent === 'instant-grocery-basket') { 8930 const parsedData = AB.DATALAYER.safeJSONParse(lazyWrapper?.getAttribute('data-zone-response')); 8931 const productList = parsedData?.productsinfo || []; 8932 const formattedCarouselTitle = AB.DATALAYER.sanitizeAnalyticsText(parsedData?.title); 8933 const trackedProducts = []; 8934 8935 productList.forEach((impression) => { 8936 if (impression?.id) { 8937 const slot = productList.indexOf(impression) + 1; 8938 const sectionInfo = AB.DATALAYER.getZoneInfo(lazyWrapper); 8939 const placement = `R01#S${slot < 10 ? `0${slot}` : slot}`; 8940 const currentProductPfm = `${AB.DATALAYER.pfmFromCurrentPage}#${formattedCarouselTitle}#${AB.DATALAYER.isSearchResultsPage() ? digitalData.search?.internalSearchTerm : 'non-search'}#pastPurchased#${placement}`; 8941 8942 trackedProducts.push({ 8943 section: formattedCarouselTitle, 8944 pageNbr: '0', 8945 lp: impression.price, 8946 pfm: currentProductPfm, 8947 pId: impression.id, 8948 units: impression.quantity || impression.qty || 0, 8949 pName: impression.name, 8950 detail: impression?.adobeMetrics?.metricA, 8951 display: 'carousel', 8952 placement: `${placement}#ZONE${sectionInfo?.zone ? (sectionInfo?.zone < 10 ? `0${sectionInfo?.zone}` : sectionInfo?.zone) : '00'}` 8953 }); 8954 8955 } 8956 }) 8957 if (trackedProducts.length > 0) { 8958 let payload = { 8959 link: { 8960 linkName: 'product-impressions' 8961 }, 8962 page: digitalData.pageDetails, 8963 search: digitalData.search, 8964 user: digitalData.user, 8965 pageFilter: digitalData.pageFilter 8966 }; 8967 if (AB.DATALAYER.isSearchResultsPage()) { 8968 payload.search = digitalData.search; 8969 } 8970 payload.carousel = trackedProducts; 8971 payload.pageFilter.mediaType = 'product-carousel'; 8972 payload.pageFilter.placement = ''; 8973 AB.DATALAYER.pushEvent('product-impressions', payload); 8974 digitalData.pageFilter.mediaType = ''; 8975 } 8976 } 8977 } 8978 8979 /** 8980 * Sets the GRS impressions information 8981 * @param eventName 8982 * @param data 8983 * @param isCarousel // true if the impression is for a carousal 8984 */ 8985 this.setGRSImpressions = function (eventName, data, isCarousel) { 8986 const searchData = this.setGRSVariables(data, isCarousel); 8987 let searchInfo = digitalData.search || {}; 8988 8989 let search = { 8990 ...searchInfo, 8991 ...searchData 8992 }; 8993 const miscInfo = data && data['miscInfo'] ? data['miscInfo'] : {}; 8994 const attributionToken = miscInfo && miscInfo['attributionToken'] ? miscInfo['attributionToken'] : ''; 8995 digitalData.user.storedAttributionToken = attributionToken; 8996 if (!this.isSearchResultsPage() && eventName === AB.DATALAYER.EVENTS.CATEGORY_IMPRESSIONS) { 8997 if (this.isDealsPage()) { 8998 eventName = AB.DATALAYER.EVENTS.DEALS_IMPRESSIONS; 8999 } 9000 search.searchType = 'search'; 9001 AB.DATALAYER.pushEvent( 9002 eventName, { 9003 search: search, 9004 page: digitalData.pageDetails, 9005 pageFilter: digitalData.pageFilter, 9006 user: digitalData.user, 9007 serverData: { 9008 attributionToken: attributionToken 9009 } 9010 }); 9011 } else if (eventName === AB.DATALAYER.EVENTS.SEARCH_IMPRESSIONS || (this.isMarketplaceSearchPage() || this.isWineShopSearchPage()) || (miscInfo?.query && data?.isLoadMore)) { 9012 if (!this.isDealsPage()) { 9013 eventName = AB.DATALAYER.EVENTS.SEARCH_IMPRESSIONS; 9014 if (search.pageCategories) { 9015 delete search.pageCategories; 9016 } 9017 if (search.autoSearchFlag) { 9018 delete search.autoSearchFlag; 9019 } 9020 search.apiSearchTerm = miscInfo && miscInfo['query'] ? miscInfo['query'] : ''; 9021 const selectPostion = this.handleGetLSValue('auto-search-selection-index'); 9022 if (selectPostion) { 9023 search.selectedPosition = selectPostion; 9024 search.completionAttrToken = attributionToken || ''; 9025 search.autoSearchFlag = true; 9026 } 9027 AB.DATALAYER.pushEvent( 9028 eventName, { 9029 search: search, 9030 page: digitalData.pageDetails, 9031 pageFilter: digitalData.pageFilter, 9032 user: digitalData.user, 9033 productInfo: digitalData.productInfo || {}, 9034 serverData: { 9035 attributionToken: attributionToken 9036 } 9037 }); 9038 } 9039 } 9040 // Commented out as requested in ACIP-490883 9041 // this.setAMCImpressionsGrid(); 9042 } 9043 9044 /** 9045 * Sets the GRS impressions information 9046 * @param data 9047 * @param isCarousel // true if the impression is for a carousal 9048 */ 9049 this.setGRSVariables = function (data, isCarousel) { 9050 const products = data && data['docs'] ? data['docs'] : data && data['products'] ? data['products'] : [];
9051 const productCount = data && data['numFound'] ? data['numFound'] : data && data['productsCounter'] ? data['productsCounter'] : 0; 9052 const miscInfo = data && data['miscInfo'] ? data['miscInfo'] : {}; 9053 const offset = data && data['start'] ? data['start'] : 0; 9054 const searchInfo = {} 9055 const impressions = []; 9056 if (!isCarousel) { 9057 digitalData.gridImpressionsProducts = digitalData.gridImpressionsProducts || []; 9058 products.forEach(product => { 9059 if (digitalData.gridImpressionsProducts && product.id && digitalData.gridImpressionsProducts.indexOf(product.id) === -1) { 9060 digitalData.gridImpressionsProducts.push(product.id); 9061 } 9062 }) 9063 } 9064 9065 products.forEach(product => { 9066 if (AB.DATALAYER.isAlcoholAdTrackingDisabled(product)) { 9067 return; 9068 } 9069 impressions.push({ 9070 product: { 9071 id: product?.pid, 9072 bia: product?.pastPurchased, //Buy it Again indicator | pastPurchased | boolean | true,false 9073 oos: product?.inventoryAvailable > 0 ? false : true, //Out of Stock indicator | inventoryAvailable | boolean | if '0' = product, then not in stock (true), if '1' = product, then in stock (false) 9074 sponsored: product?.featured, //Sponsored indicator | featured | boolean | true, false 9075 mp: product?.isMarketplaceItem, //Marketplace indicator | isMarketplaceItem | string | 'Y' if marketplace item, 'N' if not marketplace item 9076 display: isCarousel ? 'carousel' : 'grid', //Grid Carousel indicator | string | if product from primaryProdcuts, then 'grid'. if product from pgmList, then 'carousel'. 9077 placement: !isCarousel ? AB.DATALAYER.setImpressionGridPosition({ productId: product?.pid }) : AB.DATALAYER.setCarouselPosition({ productId: product?.pid }), 9078 pageNumber: offset && Number(offset) ? (Number(offset) / 30 + 1).toString() : '1' 9079 } 9080 }) 9081 }); 9082 searchInfo.offset = offset || 0; 9083 searchInfo.searchResults = productCount?.toString() || '0'; 9084 searchInfo.filter = miscInfo && miscInfo['filter'] ? miscInfo['filter'] : ''; 9085 searchInfo.orderBy = miscInfo && miscInfo['sort'] ? miscInfo['sort'] : ''; 9086 searchInfo.impressions = impressions; 9087 const categoryRegex = /(?:categories: ANY\(")(.*?)(?:"\))/; 9088 const matchedCategories = categoryRegex.exec(searchInfo.filter); 9089 let pageCategory = matchedCategories?.length > 0 ? matchedCategories[1] : 'All Products'; 9090 if (this.isWineShopCategoryPage() && pageCategory === 'All Products') { 9091 const departmentName = `${products[0]?.departmentName}`; 9092 const aisleName = `${products[0]?.aisleName && products[0]?.aisleName?.split('|')[0]}`; 9093 const shelfName = `${products[0]?.shelfName}`;
9094 // Wine Shop handled for BR feed, where categories are not available in the API filter property 9095 pageCategory = this.isWineShopL2Page() ? departmentName 9096 : this.isWineShopL3Page() ? `${departmentName} > ${aisleName}` 9097 : this.isWineShopL4Page() ? `${departmentName} > ${aisleName} > ${shelfName}` : ''; 9098 } 9099 searchInfo.pageCategories = [pageCategory] || []; 9100 9101 return searchInfo; 9102 } 9103 9104 /** 9105 * Sets the search information 9106 * @param searchInfo 9107 */ 9108 this.setSearch = function (searchInfo) { 9109 if (!this.isAislesL2Page() && !this.isAislesL3Page() && !this.isAislesL4Page() && !this.isDealsPage() && !this.isMarketplaceCategoryPage() && !this.isWineShopCategoryPage() && !this.isLpPage()) { 9110 this.removeLSItem('analyticsSimilarItemsPDP'); 9111 this.removeLSItem('analyticsSimilarItemsCart'); 9112 this.removeLSItem('analyticsMetaInfo'); 9113 this.removeSSItem('analyticsProductIdPDP'); 9114 if (digitalData) { 9115 digitalData.search = digitalData.search || {}; 9116 digitalData.search.searchType = searchInfo.searchType; 9117 digitalData.search.typedSearchTerm = searchInfo.typedSearchTerm || 'not available'; 9118 digitalData.search.autoSearchFlag = searchInfo.autoSearchFlag; 9119 digitalData.search.typedSearchCount = searchInfo.typedSearchCount || 'not available'; 9120 digitalData.search.internalSearchTerm = searchInfo.internalSearchTerm; 9121 digitalData.search.searchResults = searchInfo.searchResults; 9122 digitalData.search.searchResultPositionClicked = this.handleGetLSValue('analyticsBrandSearch'); 9123 9124 // suggested category 9125 const departmentName = AB.DATALAYER.analyticsGetURLParameterByName('departmentName'); 9126 if (departmentName && AB.DATALAYER.analyticsGetURLParameterByName('ref') === 'suggest') { 9127 digitalData.search.internalSearchTerm = `${digitalData.search.internalSearchTerm}-in-${departmentName.toLowerCase().replace(/[, ]+/g, '-')}`; 9128 digitalData.search.suggestedCategory = departmentName; 9129 } 9130 9131 9132 digitalData.pageDetails.sellerId = searchInfo.sellerId 9133 ? searchInfo.sellerId 9134 : searchInfo.crossSellerIds 9135 ? searchInfo.crossSellerIds 9136 : this.isWineShopPage() ? '22222222' : this.getBannerSellerId(); 9137 9138 let zeroSearchResults = Number(digitalData.search.searchResults) === 0; 9139 9140 if (searchInfo.pastPurchased > 0) { 9141 this.setImpression(`pastPurchased#${searchInfo.pastPurchased}`); 9142 } 9143 if (searchInfo.dynamicFacets) { 9144 this.setImpression('dynmc'); 9145 digitalData.pageFlags = digitalData.pageFlags || {}; 9146 digitalData.pageFlags.isDynamicFacetsEnabled = true; 9147 } 9148 if (this.getLSItem('analyticsOwnBrandImpressions')) { 9149 this.setImpression('v1'); 9150 this.removeLSItem('analyticsOwnBrandImpressions'); 9151 } 9152 // AR - set link object when a user clicks on 'View All' link - Schedule & Save carousel 9153 const link = { 9154 linkName: '' 9155 }; 9156 if (this.getLSItem('analyticsARviewAll')) { 9157 link.linkName = this.handleGetLSValue('analyticsARviewAll'); 9158 } 9159 9160 if (digitalData.pageDetails.subSection1 === 'm' || digitalData.pageDetails.subSection1 === 'marketplace' || this.isWineShopPage()) { 9161 if (digitalData.pageDetails.subSection2 === 'seller-search') { 9162 digitalData.pageDetails.subSection1 = 'search-results'; 9163 digitalData.pageDetails.subSection2 = 'cross-sellers'; 9164 digitalData.pageDetails.pageName = digitalData.pageDetails.pageName.replace('seller-search', 'search-results:cross-sellers'); 9165 } else { 9166 digitalData.pageDetails.subSection1 = 'search-results'; 9167 digitalData.pageDetails.subSection2 = ''; 9168 } 9169 } else if (window.location.pathname.startsWith('/schedule-and-save/manage')) { 9170 digitalData.pageDetails.subSection1 = 'search-results'; 9171 digitalData.pageDetails.subSection2 = ''; 9172 digitalData.pageDetails.subSection3 = ''; 9173 digitalData.pageDetails.subSection4 = ''; 9174 digitalData.pageDetails.pageName = 'safeway:delivery:search-results'; 9175 digitalData.pageFilter = digitalData.pageFilter || {}; 9176 digitalData.pageFilter.topNav = ''; 9177 } 9178 9179 if (zeroSearchResults) { 9180 digitalData.pageDetails.subSection1 = 'zero-search-results'; 9181 digitalData.pageDetails.pageName = digitalData.pageDetails.pageName.replace('search-results', 'zero-search-results'); 9182 9183 if (digitalData.pageDetails.subSection2 === 'cross-sellers') { 9184 this.mkpCrossSellersNoResultsClicks(); 9185 } 9186 } 9187 9188 if (this.getLSItem('analyticsCrossSellersNoResults') && digitalData.pageFilter) { 9189 digitalData.pageFilter.topNav = this.handleGetLSValue('analyticsCrossSellersNoResults'); 9190 } 9191 9192 if (!this.isRecipesAndMealsPage() && !window.location.pathname.startsWith('/schedule-and-save/manage')) { 9193 this.initEngagementLadderImpressions(); 9194 this.initCartPromoBannerImpressions();
9195 this.initFlexCardDetails(); 9196 this.initAEMZoneTracking(); 9197 this.initDFTACarousel(); 9198 this.initHeroBannerAbTestDetails(); 9199 this.initBundleAbTestDetails(); 9200 } 9201 9202 AB.DATALAYER.updatePageNameAndSubSectionRestrictedFlag(); 9203 AB.DATALAYER.setPreviousPageName(); 9204 // If there are no search results, set the 'zero_search_results' event with all existing info otherwise set the regular search event 9205 AB.DATALAYER.pushEvent( 9206 zeroSearchResults ? AB.DATALAYER.EVENTS.ZERO_SEARCH_RESULTS : AB.DATALAYER.EVENTS.SEARCH, { 9207 search: digitalData.search, 9208 page: digitalData.pageDetails, 9209 pageFilter: digitalData.pageFilter, 9210 user: digitalData.user, 9211 productInfo: digitalData.productInfo || {}, 9212 link: link.linkName ? link : undefined, 9213 modal: digitalData.modal 9214 }); 9215 9216 // Reset property so that values don't persist between hits 9217 digitalData.pageFilter.bannerCardCarousel = ''; 9218 this.handleAccountEvents(); 9219 this.setFeaturedFacetsFilter(); 9220 } 9221 } 9222 }; 9223 9224 /** 9225 * updates the search information; still used in product-generic-grid - unsure if component is still in use anywhere so keeping method 9226 * @param searchInfo 9227 */ 9228 this.updateSearch = function (searchTerm, numOfResults) { 9229 if (!this.isAislesL2Page() && !this.isAislesL3Page() && !this.isAislesL4Page()) { 9230 if (digitalData && digitalData.search) { 9231 let searchData = digitalData.search; 9232 searchData.internalSearchTerm = searchTerm; 9233 if (numOfResults === 0) { 9234 searchData.searchResults = 'zero'; 9235 } else { 9236 searchData.searchResults = numOfResults.toString(); 9237 } 9238 this.pushEvent( 9239 this.EVENTS.SEARCH, { 9240 search: searchData, 9241 page: digitalData?.pageDetails, 9242 pageFilter: digitalData?.pageFilter 9243 } 9244 ); 9245 } 9246 } 9247 }; 9248 9249 /** 9250 * Sets the PDP event on product page load 9251 * Separate setup & event than global pageLoad 9252 */ 9253 this.setPDPevent = function (productInfoObj) { 9254 AB.DATALAYER.productInfo(productInfoObj, 'product-detail'); 9255 this.initPDPPfm(productInfoObj); 9256 this.initPDPPlacementMediaType(); 9257 9258 if (productInfoObj.isPastPurchased) { 9259 AB.DATALAYER.setPastPurchasedImpressions('buyItAgainPdp'); 9260 } 9261 this.removeLSItem('analyticsSimilarItemsPDP'); 9262 this.removeLSItem('analyticsSimilarItemsCart'); 9263 this.removeLSItem('analyticsMetaInfo'); 9264 this.removeLSItem('changeCardAnalyticsAR'); 9265 this.removeLSItem('analyticsAMCPDPInfo'); 9266 9267 if (digitalData && digitalData.pageDetails) { 9268 const productInfo = digitalData.productInfo || {}; 9269 productInfo.pfmDetail = digitalData.pageDetails.pfmDetail; 9270 if (digitalData.productInfo.channel) { 9271 const delivery = digitalData.productInfo.channel.delivery ? digitalData.productInfo.channel.delivery : false; 9272 const pickup = digitalData.productInfo.channel.pickUp ? digitalData.productInfo.channel.pickUp : false; 9273 const inStore = digitalData.productInfo.channel.inStore ? digitalData.productInfo.channel.inStore : false; 9274 if (inStore === true && (delivery === true || pickup === true)) { 9275 digitalData.productInfo.productAvailability = 'online,in-store'; 9276 } else if (inStore === false && (delivery === true || pickup === true)) { 9277 digitalData.productInfo.productAvailability = 'online-only'; 9278 } else if (inStore === true && delivery === false && pickup === false) { 9279 digitalData.productInfo.productAvailability = 'in-store-only'; 9280 } 9281 } 9282 9283 // AR - set link object when a user clicks on 'Schedule & Save' on the product cards 9284 const link = { 9285 linkName: '' 9286 }; 9287 if (this.getLSItem('analyticsARproductCard')) { 9288 link.linkName = this.getLSItem('analyticsARproductCard'); 9289 this.removeLSItem('analyticsARproductCard'); 9290 } 9291 9292 if (this.getLSItem('pfmSourceCategories')) { 9293 digitalData.pageFilter.pfmSource = this.handleGetLSValue('pfmSourceCategories'); 9294 } else if (this.getLSItem('pfmSourceSimilarItemsBIA')) { 9295 digitalData.pageFilter.pfmSource = this.handleGetLSValue('pfmSourceSimilarItemsBIA'); 9296 } 9297 if (this.getLSItem('analyticsBIAPfmSource') && this.getLSItem('analyticsBIAPfmSource') !== 'undefined') { 9298 digitalData.pageFilter.pfmSource = this.getLSItem('analyticsBIAPfmSource'); 9299 } 9300 if (this.getLSItem('analyticsBIAProductImpressionAdDetail') && this.getLSItem('analyticsBIAProductImpressionAdDetail') !== 'undefined') { 9301 productInfo.impressionAdditionalDetail = this.getLSItem('analyticsBIAProductImpressionAdDetail'); 9302 } 9303 if (this.getLSItem('analyticsIsBIACarousal') && this.getLSItem('analyticsIsBIACarousal') !== 'undefined') { 9304 productInfo.isBIACarousalProduct = this.getLSItem('analyticsIsBIACarousal') === 'true' ? true : false;
9305 } 9306 if (this.getLSItem('analyticsProductMsg')) { 9307 digitalData.pageDetails.abTestDetails = this.getLSItem('analyticsProductMsg'); 9308 } 9309 this.initMarketplacePDP(); 9310 this.initWineShopPDP(); 9311 this.initEngagementLadderImpressions(); 9312 this.initCartPromoBannerImpressions(); 9313 this.initFlexCardDetails(); 9314 this.initDFTACarousel(); 9315 this.initAEMZoneTracking(); 9316 this.initHeroBannerAbTestDetails(); 9317 this.initBundleAbTestDetails(); 9318 9319 if (AB.DATALAYER.isNewPdp()) { 9320 digitalData.pageDetails.subSection1 = 'product-details'; 9321 digitalData.pageDetails.subSection2 = ''; 9322 digitalData.pageDetails.subSection3 = ''; 9323 AB.DATALAYER.setPageName(false, true); 9324 digitalData.pageFilter.topNav = this.initTopNav(); 9325 } 9326 9327 AB.DATALAYER.updatePageNameAndSubSectionRestrictedFlag(); 9328 AB.DATALAYER.setPreviousPageName(); 9329 AB.DATALAYER.setLSItem('analyticsAMCPDPInfo', JSON.stringify(productInfo)); 9330 AB.DATALAYER.pushEvent( 9331 AB.DATALAYER.EVENTS.PRODUCT_LOADED, 9332 { 9333 page: digitalData.pageDetails, 9334 pageFilter: digitalData.pageFilter, 9335 user: digitalData.user, 9336 productInfo: productInfo, 9337 link: link.linkName ? link : undefined 9338 } 9339 ); 9340 9341 // Reset property so that values don't persist between hits 9342 digitalData.pageFilter.bannerCardCarousel = ''; 9343 digitalData.pageDetails.abTestDetails = ''; 9344 this.handleAccountEvents(); 9345 } 9346 }; 9347 9348 this.initReviewsPage = function () { 9349 const { pageDetails, pageFilter } = digitalData; 9350 pageDetails.subSection1 = 'product-details'; 9351 pageDetails.subSection2 = 'reviews'; 9352 pageDetails.subSection3 = ''; 9353 pageDetails.subSection4 = ''; 9354 pageFilter.topNav = this.initTopNav(); 9355 this.setPageName('', true); 9356 digitalData?.pageFlags?.isPageLoadSent && (digitalData.pageFlags.isPageLoadSent = false); 9357 this.setPageLoad(); 9358 pageFilter.topNav = ''; 9359 } 9360 9361 /** 9362 * Sends event when coupon is unclipped - currently (04/10/2021) only from MyList page 9363 * @param coupon coupon details object 9364 */ 9365 this.unClipCoupon = function (coupon) { 9366 if (digitalData) { 9367 digitalData.coupon = { 9368 couponName: coupon.brand, 9369 couponType: coupon.offerType, 9370 couponId: coupon.offerId, 9371 couponSavings: coupon.offerPrice, 9372 carouselSection: coupon.carouselSection ? AB.DATALAYER.sanitizeAnalyticsText(coupon.carouselSection) : '' 9373 }; 9374 9375 this.pushEvent( 9376 this.EVENTS.UNCLIP, { 9377 coupon: digitalData.coupon, 9378 page: digitalData?.pageDetails, 9379 pageFilter: digitalData?.pageFilter 9380 } 9381 ); 9382 } 9383 }; 9384 9385 /** 9386 * Sends event when coupon is clipped 9387 * @param coupon coupon details object 9388 */ 9389 this.clipCoupon = function (coupon, analyticsData) { 9390 let element = analyticsData?.nativeElement; 9391 digitalData.modal = digitalData.modal || {}; 9392 if (digitalData) { 9393 if (coupon && digitalData.productInfo && digitalData.productInfo.carouselSection && digitalData.productInfo.carouselSection.toLowerCase().indexOf('picks for you') !== -1) { 9394 coupon.carouselSection = digitalData.productInfo.carouselSection; 9395 if (coupon.carouselSection.trim().toLowerCase() === 'picks for you') { 9396 coupon.carouselSection = 'Picked for You'; 9397 } 9398 } 9399 coupon = { 9400 couponName: coupon.brand, 9401 couponType: coupon.offerType, 9402 couponId: coupon.offerId, 9403 couponclipmethod: 'singleclip', // 'barcodescan' and 'stackandsave' not available on web 9404 numberofcouponsclipped: '1', // no multi-clip available on web 9405 couponSavings: coupon.offerPrice?.toString() || '0', 9406 productSKU: (typeof (digitalData.productInfo) === 'object') && digitalData.productInfo?.productID ? digitalData.productInfo.productID.toString() : '', 9407 carouselSection: (typeof (digitalData.coupon) === 'object') ? (coupon.carouselSection && coupon.carouselSection.toLowerCase().indexOf('picks for you') !== -1 || coupon.carouselSection && coupon.carouselSection.toLowerCase().indexOf('picked for you') !== -1 ? `${digitalData.coupon.carouselSection}-${coupon.carouselSection.trim().toLowerCase().split(' ').join('-').replace(/[^\w-,|#:]/gi, '').replace('--', '-')}` : digitalData.coupon.carouselSection) :
9408 (coupon.carouselSection ? coupon.carouselSection : digitalData.pageFilter.carousel && digitalData.pageFilter.carousel.substring(digitalData.pageFilter.carousel.indexOf('|') + 1)) 9409 }; 9410 // Needed in order to accurately get the carousel name on weekly ad deals page 9411 if (AB.DATALAYER.isWeeklyAdDealsPage()) { 9412 coupon.carouselSection = AB.DATALAYER.getAnalyticsCarouselTitle(element); 9413 digitalData.modal.modalMessage = `weekly-ad:${AB.DATALAYER.sanitizeAnalyticsText(coupon.carouselSection)}`; 9414 } 9415 if (!coupon.carouselSection && digitalData.pageFilter && digitalData.pageFilter.bentoCarouselTitle) { 9416 coupon.carouselSection = digitalData.pageFilter.bentoCarouselTitle; 9417 digitalData.pageFilter.bentoCarouselTitle = ''; 9418 } 9419 coupon.carouselSection = this.sanitizeAnalyticsText(coupon.carouselSection); 9420 9421 // Division name for batch coupon clip tracking 9422 coupon.divisionName = digitalData.storeFlags?.flags?.division?.name || 'no-value'; 9423 9424 if (element) { 9425 const modelId = element.closest('lazy-wrapper')?.getAttribute('data-model-configuration-id'); 9426 if (modelId) { 9427 coupon.abTestDetails = digitalData.couponTrackingInfo && digitalData.couponTrackingInfo[modelId] && digitalData.couponTrackingInfo[modelId].msgExperiment || 'no-value'; 9428 } 9429 } 9430 9431 // Setting metricA tracking for batch coupon clip tracking 9432 if (digitalData.couponTrackingInfo?.couponMetricAList && digitalData.couponTrackingInfo.couponMetricAList[coupon.couponId]) { 9433 coupon.msgData = digitalData.couponTrackingInfo.couponMetricAList[coupon.couponId]; 9434 } 9435 9436 this.updateJourneyStatus(this.Flag.COUPON_CLIPPED); 9437 9438 let currentProduct = digitalData.productInfo; 9439 if (currentProduct && Object.keys(currentProduct).length > 0 && currentProduct.productID) { 9440 currentProduct.couponID = currentProduct.couponID || ''; 9441 9442 if (currentProduct.couponID === '') { 9443 currentProduct.couponID = coupon.offerId; 9444 currentProduct.couponClipped = (parseInt(currentProduct.couponClipped) + 1).toString(); 9445 } else { 9446 let couponIds = currentProduct.couponID.split(','); 9447 if (couponIds.indexOf(coupon.offerId) === -1) { 9448 couponIds.push(coupon.offerId); 9449 currentProduct.couponID = couponIds.toString(); 9450 currentProduct.couponClipped = (parseInt(currentProduct.couponClipped) + 1).toString(); 9451 } 9452 } 9453 if (!this.isDFTAPage()) { 9454 if (currentProduct.carouselSection) { 9455 digitalData.pageFilter.carousel = currentProduct.carouselSectionUncurated ? currentProduct.carouselSectionUncurated : currentProduct.carouselSection; 9456 this.setCarouselPosition({ productId: currentProduct.productID, carouselSection: currentProduct.carouselSectionUncurated ? currentProduct.carouselSectionUncurated : currentProduct.carouselSection, eventName: 'coupon_clipped', nativeElement: element }); 9457 } else { 9458 this.setProductGridPosition({ productId: currentProduct.productID, eventName: 'coupon_clipped', nativeElement: element }); 9459 } 9460 } else { 9461 if (digitalData.pageFilter) { 9462 digitalData.pageFilter.carousel = ''; 9463 } 9464 } 9465 } else { 9466 if (!this.isDFTAPage()) { 9467 coupon.clipLocation = this.setCouponPlacement(coupon, element, 'clip-coupon'); 9468 } 9469 } 9470 // Set abTestDetails and modalMessage when clipping coupon from splash modal 9471 if (digitalData.pageDetails) {
9472 digitalData.pageDetails.abTestDetails = analyticsData?.abTestDetails ? analyticsData.abTestDetails : '' 9473 digitalData.modal.modalMessage = analyticsData?.modalMessage ? analyticsData.modalMessage : digitalData.modal.modalMessage ? digitalData.modal.modalMessage : ''; 9474 coupon.pageData = digitalData.pageDetails; 9475 } 9476 // Set page & user data where coupon was clipped for batch coupon clip tracking on next event/page 9477 if (digitalData.pageFilter) { 9478 coupon.pageFilterData = digitalData.pageFilter; 9479 } 9480 if (digitalData.user) { 9481 coupon.userData = digitalData.user; 9482 } 9483 if (digitalData.modal) { 9484 coupon.modalData = digitalData.modal; 9485 } 9486 9487 // Batch coupon tracking 9488 this.setBatchCouponTimer(coupon); 9489 9490 9491 if (digitalData._bentoCouponV218 && digitalData.pageFilter) { 9492 digitalData.pageFilter.bannerCardCarousel = digitalData._bentoCouponV218; 9493 delete digitalData._bentoCouponV218; 9494 } 9495 9496 this.pushEvent( 9497 this.EVENTS.COUPONCLIPPED, 9498 { 9499 coupon: coupon, 9500 page: digitalData.pageDetails, 9501 pageFilter: digitalData.pageFilter, 9502 productInfo: currentProduct, 9503 modal: digitalData.modal 9504 } 9505 ); 9506 9507 if (AB.DATALAYER.isHomePage()) { 9508 const isProductCarouselCoupon = !!(currentProduct && Object.keys(currentProduct).length > 0 && currentProduct.productID); 9509 const crsMediaType = isProductCarouselCoupon ? 'product-carousel' : 'deals-carousel'; 9510 const crsSection = isProductCarouselCoupon 9511 ? (currentProduct.carouselSectionUncurated || currentProduct.carouselSection) 9512 : coupon.carouselSection; 9513 AB.DATALAYER.setCRSClickEvent({ 9514 linkName: 'coupon-clip-click', 9515 mTyp: crsMediaType, 9516 element: element, 9517 placement: digitalData.pageFilter?.placement || coupon.clipLocation, 9518 component: AB.DATALAYER.getCRSComponent({ 9519 element: element, 9520 section: crsSection, 9521 detail: coupon.msgData, 9522 placement: digitalData.pageFilter?.placement || coupon.clipLocation 9523 }), 9524 batchCouponsClipped: [coupon] 9525 }); 9526 } 9527 9528 if (digitalData.coupon && digitalData.coupon.carouselSection && digitalData.coupon.carouselSection.indexOf('offer-drawer') !== -1) { 9529 delete digitalData.coupon; 9530 } 9531 digitalData.productInfo = {}; 9532 // Resetting property to prevent data persistence between htis 9533 digitalData.pageFilter.bannerCardCarousel = ''; 9534 // Defensive: clear the bento skip-flag so it can't leak to a later non-bento coupon clip. 9535 digitalData.pageFilter.bentoCouponV218Set = false; 9536 } 9537 }; 9538 9539 /** 9540 * store's coupon object in local storage and starts timer for batch coupon even 9541 */ 9542 this.setBatchCoupon = function () { 9543 let lsCurrentCouponClips = this.handleGetLSValue('couponGroup'); 9544 9545 if (lsCurrentCouponClips) { 9546 const couponArray = AB.DATALAYER.safeJSONParse(lsCurrentCouponClips); 9547 if (couponArray.length > 0) { 9548 const pageDetails = couponArray[0].pageData; 9549 const pageFilter = couponArray[0].pageFilterData; 9550 const user = couponArray[0].userData; 9551 const modal = couponArray[0].modalData; 9552 9553 this.pushEvent( 9554 this.EVENTS.BATCHCOUPONCLIPPED, { 9555 batchCouponsClipped: couponArray, 9556 page: pageDetails, 9557 pageFilter: pageFilter, 9558 user: user, 9559 modal: modal 9560 }); 9561 } 9562 } 9563 } 9564 9565 /** 9566 * store's coupon object in local storage and starts timer for batch coupon event 9567 * @param coupon coupon object when coupon clip happens 9568 */ 9569 this.setBatchCouponTimer = function (coupon) { 9570 let lsCurrentCouponClips = this.getLSItem('couponGroup'); 9571 let couponArray = []; 9572 if (lsCurrentCouponClips) { 9573 couponArray = AB.DATALAYER.safeJSONParse(lsCurrentCouponClips); 9574 } 9575 couponArray.push(coupon) 9576 this.setLSItem('couponGroup', JSON.stringify(couponArray)) 9577 if (this.getLSItem('couponGroupTimeoutId')) { 9578 clearTimeout(Number(this.getLSItem('couponGroupTimeoutId')))
9579 localStorage.removeItem('couponGroupTimeoutId') 9580 } 9581 let timeoutID = setTimeout(() => { 9582 if (this.getLSItem('couponGroup')) { 9583 this.setBatchCoupon() 9584 } 9585 }, 15000) 9586 this.setLSItem('couponGroupTimeoutId', timeoutID) 9587 } 9588 9589 /** 9590 * sets the total number of available coupons and the coupon source 9591 * @param nr number of coupons available 9592 * @param source source of coupon clipping 9593 */ 9594 this.setCouponsDetails = function (nr, source) { 9595 if (digitalData) { 9596 digitalData.coupon = { 9597 numberofcoupons: nr, 9598 couponSource: source 9599 }; 9600 9601 this.pushEvent( 9602 this.EVENTS.COUPONNR, { 9603 coupon: digitalData.coupon, 9604 page: digitalData.pageDetails, 9605 pageFilter: digitalData.pageFilter 9606 } 9607 ); 9608 } 9609 }; 9610 9611 /** 9612 * Sets the sort event 9613 * @param sort the sort selection value, eg. Recently Purchsed, Recommended etc 9614 */ 9615 this.sortBy = function (sort) { 9616 if (digitalData) { 9617 digitalData.pageFilter.sortSelection = sort; 9618 digitalData.gridImpressionsProducts = []; 9619 this.pushEvent( 9620 'page_sort', { 9621 page: digitalData?.pageDetails, 9622 pageFilter: digitalData?.pageFilter 9623 } 9624 ); 9625 } 9626 }; 9627 9628 this.setFilterType = function (type) { 9629 if (digitalData) { 9630 digitalData.gridImpressionsProducts = []; 9631 if (!localStorage.getItem('dontsetfilter')) { 9632 digitalData.pageFilter.filterType = type; 9633 this.pushEvent( 9634 'page_filter', { 9635 page: digitalData?.pageDetails, 9636 pageFilter: digitalData?.pageFilter 9637 } 9638 ); 9639 } 9640 localStorage.removeItem('dontsetfilter'); 9641 } 9642 }; 9643 9644 this.setFilterSelection = function (selection) { 9645 if (digitalData) { 9646 digitalData.gridImpressionsProducts = []; 9647 if (!localStorage.getItem('dontsetfilter')) { 9648 digitalData.pageFilter.filterSelection = selection; 9649 this.pushEvent( 9650 'page_filter', { 9651 page: digitalData?.pageDetails, 9652 pageFilter: digitalData?.pageFilter 9653 } 9654 ); 9655 } 9656 localStorage.removeItem('dontsetfilter'); 9657 } 9658 }; 9659 this.setPageFilter = function (linkName) { 9660 if (digitalData) { 9661 digitalData.gridImpressionsProducts = []; 9662 if (!localStorage.getItem('dontsetfilter')) { 9663 this.pushEvent( 9664 'page_filter', { 9665 page: digitalData?.pageDetails, 9666 pageFilter: digitalData?.pageFilter, 9667 link: { 9668 linkName: linkName || digitalData?.link?.linkName 9669 } 9670 } 9671 ); 9672 digitalData.pageFilter.dynamicFilters = ''; 9673 } 9674 localStorage.removeItem('dontsetfilter'); 9675 } 9676 } 9677 9678 this.setFiltersSelectionsFromUrl = function () { 9679 if (digitalData) { 9680 if (digitalData.pageFilter.filterSelection === '') { 9681 let categories = AB.DATALAYER.analyticsGetURLParameterByName('category', false) 9682 ? AB.DATALAYER.analyticsGetURLParameterByName('category', false).split(',').map(function (x) { 9683 return `category: ${decodeURIComponent(x)}`; 9684 }).join(',') 9685 : ''; 9686 let events = AB.DATALAYER.analyticsGetURLParameterByName('event', false) 9687 ? AB.DATALAYER.analyticsGetURLParameterByName('event', false).split(',').map(function (x) { 9688 return `event: ${decodeURIComponent(x)}`; 9689 }).join(',') 9690 : ''; 9691 let offerTypes = AB.DATALAYER.analyticsGetURLParameterByName('offerPgm', false) 9692 ? AB.DATALAYER.analyticsGetURLParameterByName('offerPgm', false).split(',').map(function (x) { 9693 return `offerPgm: ${decodeURIComponent(x)}`; 9694 }).join(',') 9695 : ''; 9696 9697 let filters = []; 9698 let filterTypes = []; 9699 if (categories) { 9700 filters.push(categories); 9701 filterTypes.push('category'); 9702 } 9703 if (events) { 9704 filters.push(events); 9705 filterTypes.push('events'); 9706 } 9707 if (offerTypes) { 9708 filters.push(offerTypes); 9709 filterTypes.push('offerPgm'); 9710 } 9711 9712 digitalData.pageFilter.filterSelection = filters.join(', '); 9713 digitalData.pageFilter.filterType = filterTypes.join(', '); 9714 } 9715 } 9716 }; 9717 9718 this.setBoxtopAuthState = function (state) { 9719 if (digitalData) { 9720 digitalData.boxtop.authState = state; 9721 } 9722 }; 9723 9724 this.setBoxtopActionName = function (action) { 9725 if (digitalData) { 9726 digitalData.boxtop.actionName = action; 9727 } 9728 }; 9729 9730 this.updateOrderCount = function (orderCount) { 9731 if (digitalData) { 9732 let user = digitalData.user; 9733 user.orderCount = orderCount?.toString(); 9734 user.orderStatus = orderCount?.toString(); 9735 if (user.orderStatus === '0') { 9736 user.customerStatus = 'first'; 9737 } else if (user.orderStatus > 0) { 9738 user.customerStatus = 'returning'; 9739 } 9740 if (digitalData.user) { 9741 digitalData.user.orderCount = digitalData.user.orderCount; 9742 digitalData.user.orderStatus = digitalData.user.orderStatus; 9743 digitalData.user.customerStatus = digitalData.user.customerStatus; 9744 } 9745 9746 } 9747 }; 9748 9749 /** 9750 * sets the error information 9751 * @param id error id 9752 * @param feature where the error happened 9753 * @param message message the user received on error 9754 */ 9755 this.setError = function (id, feature, message) { 9756 if (digitalData) { 9757 this.pushEvent( 9758 this.EVENTS.ERROR, 9759 { 9760 error: { 9761 errorID: id, 9762 errorFeature: feature, 9763 errorMessage: message 9764 }, 9765 page: digitalData?.pageDetails, 9766 pageFilter: digitalData?.pageFilter 9767 } 9768 ); 9769 } 9770 }; 9771 9772 /* 9773 * adds the filter type (search facet) to the selected filter types list, if not already added 9774 * @param filterType 9775 */ 9776 this.addFilterType = function (filterType) { 9777 if (digitalData && digitalData.pageFilter) { 9778 const filterTypeValue = filterType.trim(); 9779 if (this.selectedSearchFacets.indexOf(filterTypeValue) === -1) { 9780 this.selectedSearchFacets.push(filterTypeValue); 9781 digitalData.pageFilter.filterType = this.selectedSearchFacets.join(', '); 9782 } 9783 digitalData.link = digitalData.link || {}; 9784 digitalData.link.linkName = 'page_filter'; 9785 } 9786 }; 9787 9788 /** 9789 * adds the filter selection to the selected filter selections list, if not already added 9790 * @param filterSelection 9791 */ 9792 9793 this.addFilterSelection = function (filterSelection, filterGroupName) { 9794 if (digitalData && digitalData.pageFilter) { 9795 // if only one filter in a group can be selected at the same time check if a filter from group was previously selected and replace with new filter 9796 let previousFilterSelection; 9797 if (filterGroupName) { 9798 previousFilterSelection = this.selectedSearchSelections.find(selection => selection.includes(filterGroupName)); 9799 } 9800 if (previousFilterSelection) { 9801 const filterSelectionIndex = this.selectedSearchSelections.indexOf(previousFilterSelection); 9802 const filterSelectionValue = filterSelection.trim(); 9803 this.selectedSearchSelections[filterSelectionIndex] = filterSelectionValue; 9804 digitalData.pageFilter.filterSelection = this.selectedSearchSelections.join(', '); 9805 } else { 9806 const filterSelectionValue = filterSelection.trim(); 9807 if (this.selectedSearchSelections.indexOf(filterSelectionValue) === -1) { 9808 this.selectedSearchSelections.push(filterSelectionValue); 9809 digitalData.pageFilter.filterSelection = this.selectedSearchSelections.join(', '); 9810 } 9811 } 9812 this.setTopNavMktpFilters(filterSelection); 9813 } 9814 }; 9815 9816 /** 9817 * removes the filter type (search facet) from the selected filter types list 9818 * @param filterType 9819 */ 9820 this.removeFilterType = function (filterType) { 9821 if (digitalData && digitalData.pageFilter) { 9822 if (filterType !== 'clearAll') { 9823 this.selectedSearchFacets = this.selectedSearchFacets.filter(function (x) { 9824 return x !== filterType 9825 }); 9826 digitalData.pageFilter.filterType = this.selectedSearchFacets.join(', '); 9827 } else { 9828 this.selectedSearchFacets = []; 9829 digitalData.pageFilter.filterType = this.selectedSearchFacets.join(); 9830 } 9831 digitalData.link = digitalData.link || {}; 9832 digitalData.link.linkName = this.isBIAPage() && filterType === 'clearAll' ? 'page_filter|clear-all' : 'page_filter'; 9833 } 9834 }; 9835 9836 this.removeFilterSelection = function (filterSelection) { 9837 if (digitalData && digitalData.pageFilter) { 9838 if (filterSelection !== 'clearAll') { 9839 this.selectedSearchSelections = this.selectedSearchSelections.filter(function (x) { 9840 return x !== filterSelection 9841 }); 9842 digitalData.pageFilter.filterSelection = this.selectedSearchSelections.join(', '); 9843 } else { 9844 this.selectedSearchSelections = []; 9845 digitalData.pageFilter.filterSelection = this.selectedSearchSelections.join(); 9846 } 9847 } 9848 }; 9849 9850 this.setDynamicFilters = function (filterType, filterSelection, facets, dynamicTile, hasConversationData) { 9851 if (filterType === 'Schedule & Save' || filterType === 'Deals') { 9852 return; 9853 } 9854 if (dynamicTile && digitalData?.pageFilter) { 9855 const position = facets?.findIndex(option => option.filterName === filterSelection) + 1; 9856 const updatedFilterSelection = position > 0 ? facets[position - 1]?.displayText : filterSelection; 9857 digitalData.pageFilter.dynamicFilters = position > 0 9858 ? `${hasConversationData ? 'cs-' : ''}ft:${updatedFilterSelection}:${position < 10 ? `0${position}` : position}${dynamicTile === 'remove' ? ':us' : ''}` 9859 : ''; 9860 } else if (this.isSearchResultsPage() && !this.isMarketplaceSearchPage() && digitalData?.pageFilter && digitalData?.pageFlags?.isDynamicFacetsEnabled && !digitalData?.pageFilter?.dynamicFilters) { 9861 const filteredOptions = facets?.options?.filter(option => option.name !== 'Miscellaneous'); 9862 const position = filteredOptions?.findIndex(option => option.name === filterSelection) + 1; 9863 digitalData.pageFilter.dynamicFilters = position > 0 9864 ? `${filterType}:${filterSelection}:${position < 10 ? `0${position}` : position}` 9865 : ''; 9866 } 9867 } 9868 9869 // Set localStorage item to track navigation using the top nav links 9870 this.setTopNavPreviousPage = function (link, dontSetFilter) { 9871 if (digitalData && digitalData.pageDetails) { 9872 if (dontSetFilter) { 9873 localStorage.setItem('dontsetfilter', true); 9874 } 9875 9876 const isShopOrTopNavLink = link && typeof link === 'object' && link.closest('.shop-flyout-main-nav, .menu-nav__left-container') ? true : false;
9877 const isWeeklyAdOrYourStore = link?.hostname?.startsWith('coupons') || window.location.hostname.startsWith('coupons') || link?.textContent?.toLowerCase()?.includes('weekly ad') || link?.hostname?.startsWith('local') || window.location.hostname.startsWith('local') ? true : false; 9878 let topNavPreviousPage = `${window.isAnalyticsMarketplacePage ? 'mkp|' : ''}${AB.DATALAYER.pathSections.join(':')} (L${AB.DATALAYER.pathSections.length})`; 9879 9880 if (isShopOrTopNavLink && isWeeklyAdOrYourStore) { 9881 const oneTrustLoadedEvent = window['dataLayer'] && window['dataLayer'].find(x => x.event === 'OneTrustGroupsUpdated'); 9882 if (oneTrustLoadedEvent?.['OnetrustActiveGroups']?.indexOf('C0002') !== -1) { 9883 SWY.CONFIGSERVICE.setDomainCookie('topNavPreviousPage', topNavPreviousPage, `${digitalData.pageDetails.banner}.com`, 0); 9884 } 9885 return; 9886 } 9887 if (link && link.hostname && link.hostname !== window.location.hostname) { 9888 return; 9889 } 9890 if (link === 'checkout-minicart' || link === 'checkout-dnf' || link === 'express-checkout-minicart' || link === 'checkout-full-cart') { 9891 topNavPreviousPage = `cta:${link === 'checkout-minicart' ? 'mini-cart' : link === 'checkout-dnf' ? AB.DATALAYER.initPathSections(true).join(':') : (AB.DATALAYER.pathSections.join(':')).replace(/\s+/g, '-').toLowerCase()}|button|${link === 'checkout-dnf' ? 'continue-to-' : ''}${link === 'express-checkout-minicart' ? link : 'checkout'}`; 9892 localStorage.setItem('topNavCheckoutPage', topNavPreviousPage); 9893 } else { 9894 if (isShopOrTopNavLink) { 9895 this.setLSItem('shopOrTopNavFlag', 'true'); 9896 } 9897 localStorage.setItem('topNavPreviousPage', topNavPreviousPage); 9898 } 9899 } 9900 }; 9901 9902 // Send the prebook information & event sent from the eRums prebook page 9903 this.initPrebookAEM = function () { 9904 const lsPrebookDl = localStorage.getItem('pageload_hit_buffer'); 9905 if (lsPrebookDl) { 9906 const parsedPrebookDl = AB.DATALAYER.safeJSONParse(lsPrebookDl); 9907 this.pushEvent( 9908 parsedPrebookDl.event, parsedPrebookDl 9909 ); 9910 localStorage.removeItem('pageload_hit_buffer'); 9911 } 9912 } 9913 9914 // Set coupon placement & media-type for coupon carousel and coupon grid 9915 this.setCouponPlacement = function (coupon, nativeElement, calledBy) { 9916 let couponID = coupon?.couponID || coupon?.couponId; 9917 let placement = ''; 9918 let contentType = ''; 9919 let displayableCRPosition = ''; 9920 const currentCoupon = calledBy === 'offer-drawer' 9921 ? nativeElement?.closest('coupon-carousel-item, coupon-card, loyalty-card-al-v1') 9922 : calledBy === 'deals-impressions-carousel' ? nativeElement?.querySelector('coupon-carousel-item, coupon-card, loyalty-card-al-v1') 9923 : (document.getElementById(`coupon-title-${couponID}`)?.closest('coupon-carousel-item, coupon-card') || 9924 document.getElementById(`coupon-title-${couponID}`)?.closest('coupon-item') || 9925 document.querySelectorAll('loyalty-card-al-v1')?.length > 0 && Array.from(document.querySelectorAll('loyalty-card-al-v1')).find(element => { 9926 return element.querySelector('a')?.attributes['aria-describedby']?.value?.includes(`${couponID}`); 9927 } 9928 )); 9929 9930 if (currentCoupon && currentCoupon?.closest('coupon-carousel, loyalty-card-carousel-al-v1')) { 9931 //Placement for carousel in page and coupon in carousel 9932 const currentCouponCarousel = currentCoupon?.closest('coupon-carousel, loyalty-card-carousel-al-v1'); 9933 const couponCarousels = document.querySelectorAll('coupon-carousel, loyalty-card-carousel-al-v1'); 9934 const currentCCPosition = couponCarousels && Array.from(couponCarousels).indexOf(currentCouponCarousel) + 1; 9935 displayableCRPosition = currentCCPosition && currentCCPosition < 10 ? `0${currentCCPosition}` : currentCCPosition; 9936 const currentCouponPosition = currentCoupon && Number(currentCoupon?.closest('div.slick-slide:not(.slick-cloned)')?.getAttribute('data-slick-index')) + 1; 9937 const displayableSPosition = currentCouponPosition && currentCouponPosition < 10 ? `0${currentCouponPosition}` : currentCouponPosition; 9938 placement = `R${displayableCRPosition}#S${displayableSPosition}`; 9939 this.setPageFilterPlacement(placement, 'coupon-carousel', 'notRecommended'); 9940 contentType = coupon?.carouselSection; 9941 } else if (currentCoupon && currentCoupon?.closest('coupon-grid')) { 9942 // Placement for coupon in grid 9943 const couponGrid = currentCoupon?.closest('coupon-grid'); 9944 const gridCoupons = couponGrid?.querySelectorAll('coupon-item, loyalty-card-al-v1'); 9945 const couponPosition = gridCoupons && Array.from(gridCoupons).indexOf(currentCoupon) + 1; 9946 let couponRow = couponPosition && couponPosition < 3 ? 1 : Math.ceil(couponPosition / 3); 9947 if (window.innerWidth < 992 && window.innerWidth > 767) { 9948 couponRow = couponPosition && couponPosition < 2 ? 1 : Math.ceil(couponPosition / 2); 9949 } else if (window.innerWidth < 768) { 9950 couponRow = couponPosition && couponPosition; 9951 } 9952 const displayableSPosition = couponPosition && couponPosition < 10 ? `0${couponPosition}` : couponPosition; 9953 const displayablePGPosition = couponRow && couponRow < 10 ? `0${couponRow}` : couponRow; 9954 placement = `R${displayablePGPosition}#S${displayableSPosition}`; 9955 this.setPageFilterPlacement(placement, 'coupon-grid', 'notRecommended'); 9956 contentType = 'coupon-grid'; 9957 } 9958 if (calledBy === 'clip-coupon') { 9959 if (digitalData && digitalData.pageFilter && digitalData.pageFilter.bentoCouponV218Set) { 9960 digitalData.pageFilter.bentoCouponV218Set = false;
9961 return placement; 9962 } 9963 this.trackAEMZone('coupon_clipped', nativeElement ? nativeElement : currentCoupon, contentType); 9964 } 9965 return calledBy === 'deals-impressions-carousel' ? `R${displayableCRPosition}` : placement; 9966 } 9967 9968 // Initialize banner placement from previous page 9969 this.initBannerPosition = function () { 9970 const lsBannerClicked = localStorage.getItem('bannerClickedDatalayer') || localStorage.getItem('skinnyBannerClickedDatalayer'); 9971 if (lsBannerClicked) { 9972 const parsedBannerDl = AB.DATALAYER.safeJSONParse(lsBannerClicked); 9973 digitalData.pageFilter = parsedBannerDl.pageFilter; 9974 this.pushEvent( 9975 parsedBannerDl.event, parsedBannerDl 9976 ); 9977 localStorage.removeItem('bannerClickedDatalayer'); 9978 localStorage.removeItem('skinnyBannerClickedDatalayer'); 9979 } 9980 } 9981 9982 // get the recommendation type for products based on the value from local storage for pdp 9983 this.getRecommendationType = function (recommendation) { 9984 let recommendationType = ''; 9985 if (recommendation.indexOf('cross-sell') !== -1) { 9986 recommendationType = 'cross-sell'; 9987 if (recommendation.indexOf('sponsored') !== -1) { 9988 recommendationType += ':sponsored'; 9989 } 9990 } else if (recommendation.indexOf('sponsored') !== -1) { 9991 recommendationType = 'sponsored'; 9992 } else if (recommendation.indexOf('recs') !== -1) { 9993 recommendationType = 'recommended'; 9994 } else if (recommendation.indexOf('past-purchase') !== -1) { 9995 recommendationType = 'pastPurchased'; 9996 } else if (recommendation.indexOf('#br') !== -1) { 9997 recommendationType = 'br'; 9998 } else { 9999 recommendationType = 'notRecommended'; 10000 } 10001 return recommendationType; 10002 } 10003 10004 // set pageFilter.recommendationType for product actions 10005 this.recommendationInfo = function (recommendationObj) { 10006 if (digitalData && digitalData.pageFilter) { 10007 let recommendationType = ''; 10008 if (this.isRecipesPage()) { 10009 recommendationType = 'recipes'; 10010 } else { 10011 if (recommendationObj.isCrossSell === true) { 10012 recommendationType = 'cross-sell'; 10013 if (recommendationObj.isSponsored === true) { 10014 recommendationType += ':sponsored'; 10015 } 10016 } else if (recommendationObj.isSponsored === true) { 10017 recommendationType = 'sponsored'; 10018 } else if (recommendationObj.isPastPurchased === true) { 10019 recommendationType = 'pastPurchased'; 10020 } else if (recommendationObj.isRecommended === true) { 10021 recommendationType = 'recommended'; 10022 } else if (recommendationObj.isBrPathway === true) { 10023 recommendationType = 'br'; 10024 } else { 10025 recommendationType = 'notRecommended'; 10026 } 10027 } 10028 digitalData.pageFilter.recommendationType = recommendationType; 10029 } 10030 } 10031 10032 // set info from refunds page 10033 this.initRequestRefund = function () { 10034 const lsRefund = localStorage.getItem('refundContinueDatalayer'); 10035 if (lsRefund) { 10036 const refundValue = AB.DATALAYER.safeJSONParse(lsRefund); 10037 this.pushEvent( 10038 refundValue.event, refundValue 10039 ); 10040 localStorage.removeItem('refundContinueDatalayer'); 10041 } 10042 } 10043 10044 // set info from Order Reschedule page 10045 this.initOrderReschedule = function () { 10046 const lsOrderReschedule = localStorage.getItem('rescheduleOrderDatalayer'); 10047 if (lsOrderReschedule) { 10048 const orderRescheduleValue = AB.DATALAYER.safeJSONParse(lsOrderReschedule); 10049 this.pushEvent( 10050 orderRescheduleValue.event, orderRescheduleValue 10051 ); 10052 localStorage.removeItem('rescheduleOrderDatalayer'); 10053 } 10054 } 10055 10056 /** 10057 * Handle subscription registration/FreshPass SPA flow 10058 */ 10059 this.updateAnalyticsSubscriptionInfo = function (subscriptionStep, subscriptionInfoObj, impressions) { 10060 if (digitalData) { 10061 digitalData.modal = {}; 10062 10063 if (subscriptionStep === 'landing' || subscriptionStep === 'snaplanding') { 10064 if (!localStorage.getItem('preventAnalyticsLanding')) { 10065 const pageNamePrefix = `${digitalData.pageDetails.banner}:loyalty`; 10066 digitalData.pageDetails.subSection1 = 'lp'; 10067 digitalData.pageDetails.subSection2 = 'subscriptions-delivery'; 10068 digitalData.pageDetails.subSection3 = subscriptionStep === 'snaplanding' ? 'government-benefits' : ''; 10069 if (subscriptionInfoObj) { 10070 this.setFPSPlanType(subscriptionStep, subscriptionInfoObj); 10071 } else { 10072 digitalData.pageFilter.subscriptionFunnel = `${subscriptionStep === 'snaplanding' ? 'snap:' : ''}freshpass-landing`; 10073 } 10074 digitalData.pageFilter.topNav = this.getLSItem('analyticsGetFreshPass') 10075 ? this.handleGetLSValue('analyticsGetFreshPass') 10076 : this.getLSItem('analyticsCTAValue') ? this.handleGetLSValue('analyticsCTAValue') 10077 : digitalData.pageFilter.topNav ? digitalData.pageFilter.topNav : ''; 10078 10079 digitalData.pageDetails.pageName = `${pageNamePrefix}:${digitalData.pageDetails.subSection1}:${digitalData.pageDetails.subSection2}${digitalData.pageDetails.subSection3 ? `:${digitalData.pageDetails.subSection3}` : ''}`; 10080 if (subscriptionStep === 'landing') { 10081 this.setImpression(document.querySelector('app-partnerscarousel') ? 'prtnrs' : 'no-prtnrs'); 10082 if (impressions && typeof impressions === 'object' && Object.keys(impressions).length > 0 && impressions.snapBanner) { 10083 this.setImpression(impressions.snapBanner); 10084 digitalData.modal.modalMessage = 'fresh-pass-goverment-programs-get-50-percent-off'; 10085 } 10086 } 10087 if (digitalData.crossPageData?.analyticsTrackFPBannerMessage?.data) { 10088 digitalData.modal.modalMessage = `guest-user|${digitalData.crossPageData?.analyticsTrackFPBannerMessage.data}`; 10089 this.setAnalyticsCrossPageData({ action: 'remove', name: 'analyticsTrackFPBannerMessage' }); 10090 } 10091 this.handleAccountEvents(); 10092 this.setPageLoad(); 10093 digitalData.pageFilter.impressions = ''; 10094 } else { 10095 localStorage.removeItem('preventAnalyticsLanding') 10096 } 10097 } else { 10098 let pageNamePrefix = `${digitalData.pageDetails.banner}:loyalty:lp`; 10099 digitalData.pageDetails.subSection1 = 'delivery-subscription'; 10100 if (subscriptionStep !== 'step4') { 10101 switch (subscriptionStep) { 10102 case 'step1': 10103 this.setFPSPlanType(subscriptionStep, subscriptionInfoObj); 10104 digitalData.pageDetails.subSection2 = 'step1_plan-selection'; 10105 digitalData.pageFilter.topNav = ''; 10106 if (subscriptionInfoObj?.campaignType === 'SHEER') { 10107 this.setImpression('gap'); 10108 } 10109 break; 10110 case 'step2': 10111 digitalData.pageDetails.subSection2 = 'step2_setup-account'; 10112 this.setFPSPlanType(subscriptionStep, subscriptionInfoObj); 10113 localStorage.setItem('freshpass-analytics-confirmation', JSON.stringify(subscriptionInfoObj)); 10114 break; 10115 case 'step3': 10116 digitalData.pageDetails.subSection2 = 'step3_payment-details'; 10117 this.setFPSPlanType(subscriptionStep, subscriptionInfoObj); 10118 localStorage.setItem('freshpass-analytics-confirmation', JSON.stringify(subscriptionInfoObj)); 10119 break; 10120 case 'confirmation': 10121 digitalData.pageDetails.subSection1 = 'freshpass'; 10122 digitalData.pageDetails.subSection2 = 'manage'; 10123 digitalData.pageDetails.subSection3 = 'confirmation'; 10124 pageNamePrefix = `${digitalData.pageDetails.banner}:loyalty`; 10125 if (impressions && typeof impressions === 'object' && Object.keys(impressions).length > 0 && impressions.partnerCards) { 10126 this.setImpression(impressions.partnerCards); 10127 } 10128 this.setFPSPlanType(subscriptionStep, subscriptionInfoObj); 10129 if (digitalData.crossPageData?.analyticsTrackFPBannerMessage?.data) { 10130 digitalData.modal.modalMessage = `fp-${digitalData.user?.subscriptionStatus?.indexOf('annual') != -1 ? 'annual' : 'monthly'}|${digitalData.crossPageData?.analyticsTrackFPBannerMessage.data}`; 10131 this.setAnalyticsCrossPageData({ action: 'remove', name: 'analyticsTrackFPBannerMessage' }); 10132 } 10133 break; 10134 case 'manage': 10135 digitalData.pageDetails.pageUrl = window.location.pathname; 10136 digitalData.pageDetails.subSection1 = 'freshpass'; 10137 digitalData.pageDetails.subSection2 = 'manage'; 10138 digitalData.pageDetails.subSection3 = ''; 10139 pageNamePrefix = `${digitalData.pageDetails.banner}:loyalty`; 10140 digitalData.pageFilter.subscriptionFunnel = ''; 10141 digitalData.user.subscriptionStatus = ''; 10142 if (this.getLSItem('analyticsFPLSitem') === 'changeToA
10142nnual' || this.getLSItem('analyticsFPLSitem') === 'changeToMonthly') { 10143 this.setFPSPlanType('changePlan'); 10144 this.removeLSItem('analyticsFPLSitem'); 10145 } 10146 if (digitalData.crossPageData?.analyticsTrackCancelReasonId?.data && digitalData.crossPageData?.analyticsTrackDiscBnftFunnel?.data && digitalData.crossPageData?.analyticsTrackCancelReasonText?.data) { 10147 digitalData.modal.modalMessage = `${digitalData.crossPageData.analyticsTrackCancelReasonId.data}-${digitalData.crossPageData.analyticsTrackCancelReasonText.data?.toLowerCase()?.replace(/\s/g, '-')}`; 10148 } 10149 if (digitalData.crossPageData?.analyticsTrackDiscBnftFunnel?.data) { 10150 digitalData.pageFilter.subscriptionFunnel = `disc-bnft:freshpass-${digitalData.crossPageData?.analyticsTrackDiscBnftFunnel.data}`; 10151 digitalData.user.subscriptionStatus = `disc-bnft:freshpass|${digitalData.crossPageData?.analyticsTrackDiscBnftFunnel.data}-success`; 10152 this.setAnalyticsCrossPageData({ action: 'remove', name: 'analyticsTrackDiscBnftFunnel' }) 10153 } 10154 this.setAnalyticsCrossPageData({ action: 'remove', name: 'analyticsTrackCancelReasonId' }); 10155 this.setAnalyticsCrossPageData({ action: 'remove', name: 'analyticsTrackCancelReasonText' }); 10156 break; 10157 case 'change': 10158 localStorage.removeItem('analyticsChangeSnapPlan'); 10159 digitalData.pageDetails.pageUrl = window.location.pathname; 10160 digitalData.pageDetails.subSection1 = 'freshpass'; 10161 digitalData.pageDetails.subSection2 = 'manage'; 10162 digitalData.pageDetails.subSection3 = 'change'; 10163 pageNamePrefix = `${digitalData.pageDetails.banner}:loyalty`; 10164 digitalData.pageFilter.subscriptionFunnel = subscriptionInfoObj?.campaignType === 'SHEER' 10165 ? `snap-plan-selection:${subscriptionInfoObj?.planCardTitle?.toLowerCase()?.includes('annual') ? 'monthly|annual' : 'annual|monthly'}` : ''; 10166 digitalData.user.subscriptionStatus = ''; 10167 if (subscriptionInfoObj?.campaignType === 'SHEER') { 10168 localStorage.setItem('analyticsChangeSnapPlan', 'true'); 10169 } 10170 break; 10171 case 'service-unavailable': 10172 digitalData.pageDetails.subSection2 = 'subscription-unavailable'; 10173 digitalData.pageFilter.subscriptionFunnel = 'service not available'; 10174 break; 10175 case 'cancel': 10176 digitalData.pageDetails.subSection1 = 'freshpass'; 10177 digitalData.pageDetails.subSection2 = 'manage'; 10178 digitalData.pageDetails.subSection3 = 'cancel-plan'; 10179 this.setFPSPlanType(subscriptionStep, subscriptionInfoObj); 10180 this.setAnalyticsCrossPageData({ action: 'set', name: 'analyticsFeatureTracking', data: 'freshpassDiscountACIP392731', isAcrossPages: false }); 10181 break; 10182 case 'pre-discount-page': 10183 digitalData.pageDetails.subSection1 = 'freshpass'; 10184 digitalData.pageDetails.subSection2 = 'manage'; 10185 digitalData.pageDetails.subSection3 = 'pre-discount-page'; 10186 this.setAnalyticsCrossPageData({ action: 'set', name: 'analyticsFeatureTracking', data: 'freshpassDiscountACIP392731', isAcrossPages: false }); 10187 break; 10188 case 'discount-page': 10189 digitalData.pageDetails.subSection1 = 'freshpass'; 10190 digitalData.pageDetails.subSection2 = 'manage'; 10191 digitalData.pageDetails.subSection3 = 'discount-page'; 10192 this.setAnalyticsCrossPageData({ action: 'set', name: 'analyticsFeatureTracking', data: 'freshpassDiscountACIP392731', isAcrossPages: false }); 10193 break; 10194 case 'update-payment': 10195 digitalData.pageDetails.subSection1 = 'freshpass'; 10196 digitalData.pageDetails.subSection2 = 'manage'; 10197 digitalData.pageDetails.subSection3 = 'update'; 10198 this.setAnalyticsCrossPageData({ action: 'set', name: 'analyticsFeatureTracking', data: 'freshpassDiscountACIP392731', isAcrossPages: false }); 10199 break; 10200 default: 10201 break; 10202 }; 10203 digitalData.pageDetails.pageName = `${pageNamePrefix}:${digitalData.pageDetails.subSection1}:${digitalData.pageDetails.subSection2}${digitalData.pageDetails.subSection3 ? `:${digitalData.pageDetails.subSection3}` : ''}`; 10204 if (this.getLSItem('analyticsCTAValue')) { 10205 digitalData.pageFilter.topNav = this.handleGetLSValue('analyticsCTAValue'); 10206 } 10207 if (subscriptionStep === 'change') { 10208 setTimeout(function () { 10209 const userMessage = document.querySelector('.benefits .description.success-color')?.textContent?.trim()?.startsWith('Your eligibility for government assistance has been verified'); 10210 digitalData.modal.modalMessage = userMe
10210ssage ? 'your-eligibility-for-gov-assistance-has-been-verified-your-plan-will-auto-renew' : ''; 10211 AB.DATALAYER.setPageLoad(); 10212 }, 2000); 10213 } else { 10214 this.setPageLoad(); 10215 } 10216 10217 if (this.isFreshpassConfirmationPage()) { 10218 AB.DATALAYER.pushEvent( 10219 AB.DATALAYER.EVENTS.DS_CONFIRMATION, { 10220 page: digitalData?.pageDetails, 10221 pageFilter: digitalData?.pageFilter, 10222 user: digitalData?.user 10223 } 10224 ); 10225 } 10226 this.handleAccountEvents(); 10227 } else if (subscriptionStep === 'step4') { 10228 if (digitalData.pageDetails.subSection2 !== 'step4_payment-confirmation') { 10229 digitalData.pageDetails.subSection2 = 'step4_payment-confirmation'; 10230 this.setFPSPlanType(subscriptionStep, subscriptionInfoObj); 10231 if (subscriptionInfoObj.planType) { 10232 localStorage.setItem('freshpass-analytics-confirmation', JSON.stringify(subscriptionInfoObj)); 10233 } 10234 digitalData.pageDetails.pageName = `${pageNamePrefix}:${digitalData.pageDetails.subSection1}:${digitalData.pageDetails.subSection2}`; 10235 this.setPageLoad(); 10236 } 10237 } 10238 } 10239 digitalData.modal = {}; 10240 digitalData.pageFilter.topNav = ''; 10241 } 10242 } 10243 /** 10244 * Function to set the impression value 10245 * @param caller set as the place where the function was called 10246 * set pageFilter.impressions for Buy It Again carousel 10247 */ 10248 this.setPastPurchasedImpressions = function (caller) { 10249 let value = ''; 10250 switch (caller) { 10251 case 'buyItAgainPdp': 10252 value = '1'; 10253 break; 10254 case 'buyItAgainCarousel': 10255 value = 'carousel'; 10256 break; 10257 case 'aisles': 10258 if (AB.DATALAYER.isAislesL3Page() || AB.DATALAYER.isAislesL4Page()) { 10259 this.pastPurchasedProducts += 1; 10260 value = `${this.pastPurchasedProducts}`; 10261 } 10262 break; 10263 default: 10264 break; 10265 } 10266 10267 if (digitalData?.pageFilter) { 10268 digitalData.pageFilter.impressions = value ? `pastpurchased#${value}` : ''; 10269 } 10270 } 10271 10272 // set pageName for just for U Registration 10273 this.justforURegistration = function () { 10274 if (digitalData.pageDetails && digitalData.pageDetails.subSection1 === 'account' && digitalData.pageDetails.subSection2 === 'short-registration') { 10275 if (AB.DATALAYER.analyticsGetURLParameterByName('source') === 'CARDLESS_ENROLLMENT') { 10276 digitalData.pageDetails.pageName += ':cardless_enrollment'; 10277 } 10278 } 10279 } 10280 10281 // set pageFilter.metaInfo for multiple images on pdp 10282 this.setPageFilterMetaInfo = function (setMetaInfo, shouldSetLsItem) { 10283 const numberOfImagesClicked = localStorage.getItem('analyticsMetaInfo') || '0'; 10284 if (shouldSetLsItem) { 10285 localStorage.setItem('analyticsMetaInfo', parseInt(numberOfImagesClicked) + 1); 10286 } 10287 else { 10288 digitalData.pageFilter.metaInfo = setMetaInfo && numberOfImagesClicked !== '0' ? `PDP-Images|${numberOfImagesClicked}` : ''; 10289 } 10290 } 10291 10292 /** 10293 * Sets tracking for Hero Collection/Canvas 10294 * @param element element clicked 10295 */ 10296 this.setHeroCollectionsTracking = function (element) { 10297 if (element && digitalData?.pageFilter) { 10298 let cardPlacement = ''; 10299 const currentBanner = element.closest('.slick-slide:not(.slick-cloned)')?.querySelector('.hero-canvas'); 10300 const currentBannerTitle = currentBanner?.querySelector('.hero-canvas__text-plate-wrapper .hero-canvas__text-plate-title')?.textContent.trim().toLowerCase().replace(/[^a-z0-9|-]/gi, '
10300-'); 10301 10302 const currentCarousel = element.closest('.nextgen-abs-carousel-wrapper'); 10303 const currentCarouselBanners = currentCarousel?.querySelectorAll('.slick-slide:not(.slick-cloned) .hero-canvas'); 10304 const bannerSlot = currentCarouselBanners && currentBanner ? Array.from(currentCarouselBanners).indexOf(currentBanner) + 1 : 0; 10305 const bannerPlacement = bannerSlot > 0 ? `R01#S0${bannerSlot}` : 'R01'; 10306 10307 const isWithCollections = currentBanner?.querySelector('div#withCollections') !== null; 10308 let bannerCardCarousel = ''; 10309 if (isWithCollections) { 10310 bannerCardCarousel = `z01|banner|hero-banner:${currentBannerTitle}|sp00`; 10311 // element clicked is a card 10312 if (element.classList.contains('hero-collection__link')) { 10313 const currentCard = element.closest('.hero-collection__card'); 10314 const cardsContainer = element.closest('.hero-collection__wrapper'); 10315 const allCards = cardsContainer?.querySelectorAll('.hero-collection__card'); 10316 cardPlacement = allCards && currentCard ? Array.from(allCards).indexOf(currentCard) + 1 : 0; 10317 const cardTitle = currentCard?.querySelector('.hero-collection__title')?.textContent.trim().toLowerCase().replace(/[^a-z0-9|-]/gi, '-'); 10318 bannerCardCarousel = `z01|banner|hero-banner-${cardPlacement}:${cardTitle}|sp0${cardPlacement}`; 10319 } 10320 } 10321 10322 digitalData.pageFilter.bannerCardCarousel = bannerCardCarousel; 10323 digitalData.pageFilter.placement = bannerPlacement; 10324 digitalData.pageFilter.mediaType = 'banner'; 10325 digitalData.pageFilter.recommendationType = 'notRecommended'; 10326 digitalData.pageDetails.bnrName = `${currentBannerTitle}|no-value`; 10327 digitalData.link = { 10328 linkName: `bannerClicked|${currentBannerTitle}` 10329 }; 10330 10331 this.trackAEMZone('hero-collections', element, `hero-collections-${isWithCollections ? `card-${cardPlacement}` : 'banner'}`); 10332 10333 this.pushEvent( 10334 this.EVENTS.LINK_CLICK, 10335 { 10336 link: digitalData.link, 10337 pageFilter: digitalData.pageFilter, 10338 page: digitalData.pageDetails 10339 } 10340 ); 10341 digitalData.pageFilter.bannerCardCarousel = ''; 10342 digitalData.pageFilter.placement = ''; 10343 digitalData.pageFilter.mediaType = ''; 10344 digitalData.pageFilter.recommendationType = ''; 10345 digitalData.pageDetails.bnrName = ''; 10346 digitalData.link = {}; 10347 } 10348 } 10349 10350 this.setLinkClickViewAll = function ({ linkTarget, carouselTitle, caller, event } = {}) { 10351 const title = carouselTitle ? carouselTitle.trim() : 'not-available'; 10352 if (event) { 10353 let element = event instanceof Element ? event : event.target; 10354 this.trackAEMZone('link', element, carouselTitle); 10355 AB.DATALAYER.trackP13nViewAll(element); 10356 } 10357 if (this.isARPage()) { 10358 localStorage.setItem('analyticsARviewAll', 'AR:LP:view-all'); 10359 } else if (this.isAccountPage()) { 10360 this.analyticsCTATracking(`cta:${this.pathSections?.join(':')}|link|view-all`); 10361 } else if (linkTarget && linkTarget.indexOf('buy-it-again') !== -1) { 10362 this.setLinkClickEvents(`bia:${title}:view-all`); 10363 } else if (window.isAnalyticsMarketplacePage) { 10364 // Marketplace carousels 10365 if (caller === 'seeAllSellers') { 10366 // Send only one modal view event per page view 10367 let eventSent = false; 10368 // Find the index of the last 'pageLoaded' event in appEventData 10369 const pageLoadedIndex = appEventData.map(eventObj => eventObj?.event).lastIndexOf('pageLoaded'); 10370 if (pageLoadedIndex !== -1) { 10371 // Check if the link event has already been sent after the 'pageLoaded' event
10372 eventSent = appEventData.slice(pageLoadedIndex).some(eventObj => eventObj?.link?.linkName === 'mkp|sidebar-open'); 10373 } 10374 if (!eventSent) { 10375 digitalData.pageFilter.topNav = `cta:${this.initPathSections(true).join(':')}|link|view-all-side-menu`; 10376 this.setLinkClickEvents('mkp|sidebar-open'); 10377 } 10378 } else { 10379 this.analyticsCTATracking(`cta:${this.initPathSections(true).join(':')}|link|view-all-merch-carousel`); 10380 } 10381 } 10382 } 10383 10384 this.setLinkClickViewAllAEMZone = function (carouselTitle, linkText, zoneId) { 10385 let element = document.querySelector(`#${zoneId}`); 10386 let sanitizedLinkText = linkText ? linkText.toLowerCase().split(' ').join('-').replace(/[^\w-,|#:]/gi, '').replace('--', '-') : 'no-value'; 10387 this.trackAEMZone(`link:${sanitizedLinkText}`, element, carouselTitle); 10388 } 10389 10390 this.setLinkClickFreshPassAlert = function ({ messageTitle, messageText, messageShowMore, event } = {}) { 10391 if (messageTitle?.toLowerCase()?.includes('freshpass') || messageText?.toLowerCase()?.includes('freshpass')) { 10392 if (event === 'start' && messageShowMore?.toLowerCase()?.includes('start free trial')) { 10393 AB.DATALAYER.analyticsCTATracking(`cta:${AB.DATALAYER.initPathSections(true).join(':')}|link|freshpass-banner`) 10394 } else if (event === 'close') { 10395 AB.DATALAYER.setLinkClickEvents('fp:banner-closed'); 10396 } 10397 } 10398 } 10399 10400 /** 10401 * Handles the BIA page view & instant basket flow 10402 * @param viewType - view type for the BIA page new design 10403 */ 10404 this.updateBIAViewType = function (viewType, productsCount) { 10405 if (digitalData && digitalData.pageDetails) { 10406 digitalData.pageDetails.subSection1 = AB.DATALAYER.pathSections[0] || ''; 10407 digitalData.pageDetails.subSection2 = AB.DATALAYER.pathSections[1] || ''; 10408 digitalData.pageFilter = digitalData.pageFilter || {}; 10409 digitalData.pageFilter.topNav = digitalData.pageFilter.topNav ? digitalData.pageFilter.topNav : AB.DATALAYER.initTopNav(); 10410 10411 switch (true) { 10412 case (viewType === 'item' || window.location.search.indexOf('view=item') !== -1) && window.biaAnalyticsTrackView !== 'item': 10413 digitalData.pageDetails.subSection3 = 'item-view'; 10414 digitalData.pageDetails.pageName = `${digitalData.pageDetails.banner}:delivery:${digitalData.pageDetails.subSection1}:${digitalData.pageDetails.subSection2}:${digitalData.pageDetails.subSection3}`; 10415 AB.DATALAYER.setPageLoad(); 10416 window.biaAnalyticsTrackView = 'item'; 10417 break; 10418 case (viewType === 'category' || window.location.search.indexOf('view=category') !== -1) && window.biaAnalyticsTrackView !== 'category': 10419 digitalData.pageDetails.subSection3 = 'category-view'; 10420 digitalData.pageDetails.pageName = `${digitalData.pageDetails.banner}:delivery:${digitalData.pageDetails.subSection1}:${digitalData.pageDetails.subSection2}:${digitalData.pageDetails.subSection3}`; 10421 AB.DATALAYER.setPageLoad(); 10422 window.biaAnalyticsTrackView = 'category'; 10423 break; 10424 case (viewType === 'viewAll' || window.location.search.indexOf('view=viewAll') !== -1) && window.biaAnalyticsTrackView !== 'viewAll': 10425 digitalData.pageDetails.subSection3 = 'view-all'; 10426 digitalData.pageDetails.pageName = `${digitalData.pageDetails.banner}:delivery:${digitalData.pageDetails.subSection1}:${digitalData.pageDetails.subSection2}:${digitalData.pageDetails.subSection3}`; 10427 AB.DATALAYER.setPageLoad(); 10428 window.biaAnalyticsTrackView = 'viewAll'; 10429 break; 10430 case (viewType === 'instantCart' || window.location.search.indexOf('view=instantCart') !== -1) && window.biaAnalyticsTrackView !== 'instantCart': 10431 digitalData.pageDetails.subSection1 = 'smart-basket'; 10432 digitalData.pageDetails.subSection2 = 'quick-start-cart'; 10433 digitalData.pageDetails.subSection3 = ''; 10434 digitalData.modal = digitalData.modal || {}; 10435 digitalData.modal.modalMessage = `quick-start-cart|recs:${productsCount || 'no-value'}`; 10436 AB.DATALAYER.setPageName(false, true); 10437 AB.DATALAYER.setPageLoad(); 10438 window.biaAnalyticsTrackView = 'instantCart'; 10439 break; 10440 default: 10441 break; 10442 } 10443 10444 digitalData.pageDetails.pfmDetail = `${digitalData.pageDetails.subSection1}:${digitalData.pageDetails.subSection2}:${digitalData.pageDetails.subSection3}`; 10445 AB.DATALAYER.pfmFromCurrentPage = `${digitalData.pageDetails.subSection1}:${digitalData.pageDetails.subSection2}:${digitalData.pageDetails.subSection3}`; 10446 localStorage.setItem('pfmDetail', digitalData.pageDetails.pfmDetail); 10447 digitalData.pageFilter.topNav = ''; 10448 } else { 10449 sessionStorage.setItem('analyticsBIADelayedPageLoad', 'foo'); 10450 } 10451 localStorage.removeItem('analyticsBiaLoadMore'); 10452 } 10453 10454 // Tracking for BIA instant/quick cart, from the old BIA grid component loading on qsc landing 10455 this.updateBiaQsc = function (viewType) { 10456 if (window.location.search.indexOf('view=instantCart') !== -1 && window.biaAnalyticsTrackView !== 'instantCart') { 10457 if (viewType === 'instantCart') { 10458 digitalData.pageDetails.subSection3 = 'instant-cart'; 10459 digitalData.pageDetails.pageName = `${digitalData.pageDetails.banner}:delivery:${digitalData.pageDetails.subSection1}:${digitalData.pageDetails.subSection2}:${digitalData.pageDetails.subSection3}`; 10460 } else if (viewType === 'quickAdd') { 10461 digitalData.pageDetails.subSection1 = 'quick-start-cart'; 10462 digitalData.pageDetails.subSection2 = ''; 10463 digitalData.pageDetails.subSection3 = ''; 10464 digitalData.pageDetails.channel = 'shop'; 10465 digitalData.pageDetails.pageName = `${digitalData.pageDetails.banner}:${digitalData.pageDetails.channel}:${digitalData.pageDetails.subSection1}`; 10466 let noOfRecommandation = []; 10467 if (localStorage.getItem('quickAddBasket')) { 10468 noOfRecommandation = AB.DATALAYER.safeJSONParse(AB.DATALAYER.getLSItem('quickAddBasket'))?.cartItemsList || []; 10469 } 10470 digitalData.modal.modalMessage = `quick-add|recs:${noOfRecommandation.length > 0 ? noOfRecommandation.length : 0}`; 10471 } 10472 AB.DATALAYER.pfmFromCurrentPage = `${digitalData.pageDetails.subSection1}${digitalData.pageDetails.subSection2 ? `:${digitalData.pageDetails.subSection2}` : ''}${digitalData.pageDetails.subSection3 ? `:${digitalData.pageDetails.subSection3}` : ''}`; 10473 AB.DATALAYER.setPageLoad(); 10474 window.biaAnalyticsTrackView = 'instantCart'; 10475 } 10476 } 10477 10478 this.setCheckBoxTrackingValues = function (elementId) { 10479 const quickadd = AB.DATALAYER.safeJSONParse(AB.DATALAYER.getLSItem('quickAddBasket'))?.cartItemsList || []; 10480 const valueChecked = quickadd.find(item => item.itemId === elementId).selected;
10481 if (valueChecked) { 10482 AB.DATALAYER.setLinkClickEvents('quick-start-cart|check'); 10483 } else { 10484 AB.DATALAYER.setLinkClickEvents('quick-start-cart|uncheck'); 10485 } 10486 } 10487 10488 /** 10489 * Track the user clicks on checkBox 1 time per action per item per page 10490 */ 10491 this.trackCheckBoxButtonClicks = function () { 10492 document.querySelectorAll('.checkbox__input').forEach(element => { 10493 let clickCount = 0; 10494 10495 const handleCheckBoxClick = () => { 10496 const elementId = element.id?.replace(/[^0-9]/g, ''); 10497 AB.DATALAYER.setCheckBoxTrackingValues(elementId); 10498 clickCount++; 10499 if (clickCount === 2) { 10500 element.removeEventListener('click', handleCheckBoxClick); 10501 } 10502 }; 10503 10504 element.addEventListener('click', handleCheckBoxClick); 10505 }); 10506 } 10507 10508 this.setAttributionToken = function (attrToken) { 10509 if (digitalData && digitalData.user) { 10510 digitalData.user.googleAttributionToken = attrToken; 10511 } 10512 } 10513 10514 // EDDL migration - 10515 // Init datalayer for Orders Page 10516 this.initOrdersPage = function (reportedIssues, subSection3, subSection4, orderID) { 10517 /* 10518 order history page /order-account/orders 10519 order details page /order-account/orders/#orderId 10520 report an issue page /order-account/orders/#orderId/report-issue 10521 refund confirmation page /order-account/orders/#orderId/report-issue/confirmation 10522 */ 10523 const { pageDetails, pageFilter, user, crossPageData } = digitalData; 10524 this.initPathSections(); 10525 /** 10526 * If the pathSections contains 3 elements, the subSection3 should be 'order-details' 10527 * If the pathSections contains more than 3 elements, we remove the orderID and start setting the subSection3 from the the third element in the pathSections 10528 */ 10529 if (this.pathSections.length === 3) { 10530 this.pathSections[2] = 'order-details'; 10531 } else if (this.pathSections.length > 3) { 10532 this.pathSections.splice(2, 1); 10533 } 10534 for (let i = 0; i < 4; i++) { 10535 pageDetails[`subSection${i + 1}`] = this.pathSections[i] || ''; 10536 } 10537 10538 // update subSections for in-store and online purchases 10539 if (subSection3) { 10540 pageDetails.subSection3 = subSection3; 10541 pageDetails.subSection4 = subSection4 ? subSection4 : ''; 10542 pageFilter.orderID = orderID ? orderID : ''; 10543 } 10544 10545 if (subSection4) { 10546 // set subSection3 to refund status 10547 if (pageDetails.subSection3 === 'refund-status') { 10548 pageDetails.subSection3 = subSection4; 10549 pageDetails.subSection4 = ''; 10550 } else { 10551 // set subSection4 to order status 10552 pageDetails.subSection4 = subSection4; 10553 } 10554 } 10555 10556 pageDetails.pageName = [pageDetails.banner, 'loyalty', pageDetails.subSection1, pageDetails.subSection2, pageDetails.subSection3, pageDetails.subSection4].filter(
10556Boolean).join(':'); 10557 pageDetails.pageUrl = window.location.pathname; 10558 pageDetails.channel = 'Account'; 10559 pageDetails.orderNumber = window.location.pathname.split('/')[3] || ''; 10560 /** Page Filter **/ 10561 // set report issue 10562 if (reportedIssues) { 10563 let reportedIssuesArray = []; 10564 Object.keys(reportedIssues).map(item => { 10565 if (reportedIssues[item].count > 0 && reportedIssues[item].reasontext) { 10566 reportedIssuesArray.push(reportedIssues[item].reasontext.replace(' ', '-').toLowerCase()); 10567 } 10568 }); 10569 if (reportedIssuesArray) { 10570 pageFilter.reportIssue = reportedIssuesArray.join(', '); 10571 } 10572 } 10573 10574 if (!user || (user && Object.keys(user).length === 0)) { 10575 this.initUser(); 10576 } 10577 10578 pageFilter.impressions = crossPageData.orderPageSections?.data 10579 ? pageFilter.impressions !== '' ? crossPageData.orderPageSections.data : `${pageFilter.impressions}, ${crossPageData.orderPageSections.data}` : ''; 10580 10581 pageFilter.topNav = pageFilter.topNav || AB.DATALAYER.initTopNav(); 10582 10583 this.setPageLoad(); 10584 pageFilter.topNav = ''; 10585 10586 const af_sub1Param = AB.DATALAYER.analyticsGetURLParameterByName('af_sub1'); 10587 const cParam = AB.DATALAYER.analyticsGetURLParameterByName('c'); 10588 const reviewSubstitutionsParam = AB.DATALAYER.analyticsGetURLParameterByName('reviewSubstitutions'); 10589 const setDeeplink = (campaign) => { pageDetails.campaign = campaign; AB.DATALAYER.setLinkClickEvents('deepLink'); } 10590 if (pageDetails.subSection3 === 'item-details' && (af_sub1Param === 'sms_allb_alld_ecom_2waychat' || cParam === 'sms_allb_alld_ecom_2waychat')) { 10591 setDeeplink(af_sub1Param); 10592 } else if (pageDetails.subSection3 === 'order-details' && reviewSubstitutionsParam === 'true') { 10593 if (af_sub1Param === 'sms_allb_alld_ecom_dlvryrdy' || cParam === 'sms_allb_alld_ecom_dlvryrdy') { 10594 setDeeplink('psh_allb_alld_ecom_dlvryrdy'); 10595 } else if (af_sub1Param === 'sms_allb_alld_ecom_pkrdy' || cParam === 'sms_allb_alld_ecom_pkrdy') { 10596 setDeeplink('psh_allb_alld_ecom_pkrdy'); 10597 } 10598 } 10599 10600 } 10601 10602 this.initOrderStatus = function (data) { 10603 let orderStatus = ''; 10604 if (data) { 10605 switch (data.event) { 10606 case 'CREATED': 10607 orderStatus = 'order-confirmed'; 10608 break; 10609 case 'IN-PROGRESS': 10610 orderStatus = 'order-processed'; 10611 break; 10612 case 'DRIVER-DELAY-ALERT': 10613 orderStatus = 'behind-schedule'; 10614 break; 10615 case 'PICKING-BEHIND-SCHEDULE': 10616 orderStatus = 'picking-behind-schedule'; 10617 break; 10618 case 'PICKED-UP': 10619 orderStatus = data?.fulfillmentType === 'DELIVERY' ? 'ready-for-delivery' : 'order-complete'; 10620 break; 10621 case 'CREATED-AFTER-CUTOFF': 10622 orderStatus = 'created-after-cutoff'; 10623 break; 10624 case 'RELEASED': 10625 orderStatus = 'released'; 10626 break; 10627 case 'PACKED': 10628 orderStatus = 'packed'; 10629 break; 10630 case 'PICKUP-READY': 10631 orderStatus = 'ready-for-pickup'; 10632 break; 10633 case 'DELIVERY-CONFIRMED': 10634 orderStatus = 'ready-for-delivery'; 10635 break; 10636 case 'DELIVERY-ON-THE-WAY': 10637 orderStatus = 'out-for-delivery'; 10638 break; 10639 case 'DELIVERED': 10640 orderStatus = 'order-delivered'; 10641 break; 10642 case 'REFUND': 10643 orderStatus = 'refund'; 10644 break; 10645 case 'LATE-PICK-ALERT': 10646 orderStatus = 'late-pick-alert'; 10647 break; 10648 case 'DELIVERY-MISSED': 10649 orderStatus = 'delivery-missed'; 10650 break; 10651 case 'ENROUTE-TO-PICKUP': 10652 orderStatus = 'drive-on-the-way'; 10653 break; 10654 default: 10655 break; 10656 } 10657 } 10658 return orderStatus; 10659 } 10660 10661 this.initRefundStatus = function (status) { 10662 let refundStatus = ''; 10663 if (status) { 10664 switch (status) { 10665 case 'AWAITING-SHIPPING-LABEL': 10666 refundStatus = 'refund-requested'; 10667 break; 10668 case 'AWAITING-SHIPPING-RETURN': 10669 refundStatus = 'return-label-created'; 10670 break; 10671 case 'REFUND-APPROVED': 10672 refundStatus = 'refund-issued'; 10673 break; 10674 case 'REFUND-PARTIALLY-APPROVED': 10675 refundStatus = 'partial-refund-issued'; 10676 break; 10677 case 'REFUND-REJECTED': 10678 refundStatus = 'refund-not-issued'; 10679 break; 10680 } 10681 } 10682 return refundStatus; 10683 } 10684 10685 this.initResetPwdPage = function (subSectionInfo) { 10686 if (this.isResetPasswordPage()) { 10687 if (subSectionInfo === 'expired') { 10688 digitalData.pageDetails.subSection2 = 'reset-password-expired'; 10689 } else { 10690 digitalData.pageDetails.subSection2 = `reset-${subSectionInfo}-password`; 10691 } 10692 this.setPageName(false, true); 10693 this.setPageLoad(); 10694 } 10695 } 10696 10697 this.initPaymentPage = function () { 10698 if (this.isPaymentPage()) { 10699 digitalData.pageDetails.subSection2 = 'payment'; 10700 digitalData.pageDetails.subSection3 = 'add-payment-methods'; 10701 digitalData.pageDetails.pageName = `${digitalData.pageDetails.banner}:loyalty:${digitalData.pageDetails.subSection1}:${digitalData.pageDetails.subSection2}:${digitalData.pageDetails.subSection3}`; 10702 } 10703 } 10704 10705 this.initReceiptPage = function (subSection3, topNav, modalMessage) { 10706 if (this.isPaymentPage()) { 10707 digitalData.pageDetails.channel = 'account' 10708 digitalData.pageDetails.subSection3 = subSection3; 10709 AB.DATALAYER.setPageName(digitalData.pageDetails.channel, true) 10710 digitalData.pageFilter.topNav = topNav; 10711 digitalData.modal = digitalData.modal || {}; 10712 digitalData.modal.modalMessage = modalMessage; 10713 digitalData.pageFlags.isPageLoadSent = false;
10714 AB.DATALAYER.setPageLoad() 10715 } 10716 } 10717 10718 /** 10719 * Initializes datalayer info and sets pageLoaded call on Unsubscribe SPA 10720 */ 10721 this.initUnsubscribePage = function (screen) { 10722 if (!digitalData.user || (digitalData.user && Object.keys(digitalData.user).length === 0)) { 10723 AB.DATALAYER.initUser(); 10724 } 10725 const unsubscribeSubsction3 = screen === 'unsubscribe' 10726 ? 'unsubscribed' : screen === 'we-miss-you' && digitalData?.modal?.modalMessage 10727 ? 'unsubscribed-confirmed-with-response' : screen === 'we-miss-you' && !digitalData?.modal?.modalMessage 10728 ? 'unsubscribed-confirmed' : screen === 'subscribed' 10729 ? 'resubscribed' : ''; 10730 if (!digitalData?.pageDetails?.subSection3 || digitalData?.pageDetails?.subSection3 && digitalData?.pageDetails?.subSection3 !== unsubscribeSubsction3) { 10731 digitalData.pageDetails.subSection3 = unsubscribeSubsction3; 10732 digitalData.pageDetails.pageName = `${digitalData?.pageDetails?.banner}:account:${digitalData?.pageDetails?.subSection1}:${digitalData?.pageDetails?.subSection2}:${unsubscribeSubsction3}`; 10733 AB.DATALAYER.setPreviousPageName(); 10734 AB.DATALAYER.pushEvent( 10735 AB.DATALAYER.EVENTS.PAGE_LOADED, { 10736 page: digitalData.pageDetails, 10737 pageFilter: digitalData.pageFilter, 10738 user: digitalData.user, 10739 modal: digitalData.modal 10740 } 10741 ); 10742 digitalData.modal = {}; 10743 } 10744 } 10745 10746 /** 10747 * Init datalayer for AR pages 10748 */ 10749 this.initARpages = function (isEmptyState) { 10750 if (AB.DATALAYER.isARManagePage()) { 10751 // init pageInfo object 10752 const bannerEl = document.getElementById('banner'); 10753 const pageInfo = { 10754 banner: bannerEl ? bannerEl.dataset.banner : '', 10755 platform: 'web', 10756 channel: 'Account', 10757 pageUrl: window.location.pathname, 10758 server: window.location.hostname, 10759 referringURL: AB.DATALAYER.getReferringUrl(), 10760 thirdParty: AB.DATALAYER.getThirdParty(), 10761 sellerId: AB.DATALAYER.getBannerSellerId() 10762 }; 10763 const pathSections = (window.location.pathname.indexOf('.') > -1 ? window.location.pathname.substring(0, window.location.pathname.indexOf('.')) : window.location.pathname).split('/').slice(1); 10764 if (pathSections) { 10765 pathSections.length > 0 && (pageInfo.subSection1 = pathSections[0]); 10766 pathSections.length > 1 && (pageInfo.subSection2 = pathSections[1]); 10767 pathSections.length > 2 && (pageInfo.subSection3 = pathSections[2]); 10768 pathSections.length > 3 && (pageInfo.subSection4 = pathSections[3]); 10769 } 10770 pageInfo.pageName = [ 10771 pageInfo.banner, 10772 'loyalty', 10773 pageInfo.subSection1, 10774 pageInfo.subSection2, 10775 pageInfo.subSection3, 10776 pageInfo.subSection4 10777 ].filter(Boolean).join(':'); 10778 if (isEmptyState) { 10779 pageInfo.pageName += ' - empty state'; 10780 } 10781 window.digitalData.page = { 10782 pageInfo: pageInfo 10783 }; 10784 window.digitalData.pageDetails = pageInfo; 10785 10786 //init pageFilter object 10787 const pageFilter = { 10788 topNav: digitalData?.pageFilter?.topNav ? digitalData.pageFilter.topNav : AB.DATALAYER.initTopNav() 10789 }; 10790 window.digitalData.pageFilter = pageFilter; 10791 10792 AB.DATALAYER.setPageLoad(); 10793 } 10794 } 10795 10796 /** 10797 * Init datalayer for Freshpass SPA pages 10798 */ 10799 this.initFreshpassPage = function () { 10800 if (AB.DATALAYER.isFreshpassManagePage()) { 10801 // init pageInfo object 10802 const bannerEl = document.getElementById('banner'); 10803 const pageInfo = { 10804 banner: bannerEl ? bannerEl.dataset.banner : '', 10805 platform: 'web', 10806 channel: 'Account', 10807 pageUrl: window.location.pathname, 10808 server: window.location.hostname, 10809 referringURL: AB.DATALAYER.getReferringUrl(), 10810 thirdParty: AB.DATALAYER.getThirdParty(), 10811 sellerId: AB.DATALAYER.getBannerSellerId() 10812 }; 10813 const pathSections = (window.location.pathname.indexOf('.') > -1 ? window.location.pathname.substring(0, window.location.pathname.indexOf('.')) : window.location.pathname).split('/').slice(1); 10814 if (pathSections) { 10815 pathSections.length > 0 && (pageInfo.subSection1 = pathSections[0]); 10816 pathSections.length > 1 && (pageInfo.subSection2 = pathSections[1]); 10817 pathSections.length > 2 && (pageInfo.subSection3 = pathSections[2]); 10818 pathSections.length > 3 && (pageInfo.subSection4 = pathSections[3]); 10819 } 10820 pageInfo.pageName = [ 10821 pageInfo.banner, 10822 'loyalty', 10823 pageInfo.subSection1, 10824 pageInfo.subSection2, 10825 pageInfo.subSection3, 10826 pageInfo.subSection4
10827 ].filter(Boolean).join(':'); 10828 window.digitalData.page = { 10829 pageInfo: pageInfo 10830 }; 10831 window.digitalData.pageDetails = pageInfo; 10832 10833 //init pageFilter object 10834 const pageFilter = { 10835 topNav: digitalData?.pageFilter?.topNav ? digitalData.pageFilter.topNav : AB.DATALAYER.initTopNav() 10836 }; 10837 window.digitalData.pageFilter = pageFilter; 10838 } 10839 } 10840 10841 this.initRewardsPage = function (subsection2) { 10842 // Set page variables 10843 AB.DATALAYER.pathSections = ['rewards', subsection2]; 10844 digitalData.pageDetails.subSection1 = AB.DATALAYER.pathSections[0]; 10845 digitalData.pageDetails.subSection2 = AB.DATALAYER.pathSections[1] || ''; 10846 digitalData.pageDetails.pfmDetail = AB.DATALAYER.pathSections.join(':'); 10847 AB.DATALAYER.setPageName(); 10848 digitalData.pageFilter.topNav = AB.DATALAYER.initTopNav(); 10849 10850 // Send pageLoad event 10851 AB.DATALAYER.setPageLoad(); 10852 digitalData.pageFilter.topNav = ''; 10853 } 10854 10855 this.initAngularSPA = function (subSection1, subSection2, subSection3, subSection4, impressions) { 10856 // Set page variables 10857 if (subSection1) { 10858 AB.DATALAYER.pathSections = [subSection1, subSection2 || '', subSection3 || '', subSection4 || ''].filter((x) => { return x && x !== '' }); 10859 } else { 10860 AB.DATALAYER.initPathSections(); 10861 } 10862 digitalData.pageDetails.subSection1 = AB.DATALAYER.pathSections[0]; 10863 digitalData.pageDetails.subSection2 = AB.DATALAYER.pathSections[1] || ''; 10864 digitalData.pageDetails.subSection3 = AB.DATALAYER.pathSections[2] || ''; 10865 digitalData.pageDetails.subSection4 = AB.DATALAYER.pathSections[3] || ''; 10866 digitalData.pageDetails.channel = digitalData.pageDetails.subSection1 === 'games' || digitalData.pageDetails.subSection1 === 'rewards' ? 'Banners for you' : digitalData.pageDetails.channel; 10867 if (digitalData.pageDetails.subSection1 === 'games') { 10868 if (digitalData.pageDetails.subSection3 && !digitalData.pageDetails.subSection4) { 10869 AB.DATALAYER.pathSections[2] = `${AB.DATALAYER.pathSections[2]}-landing`; 10870 digitalData.pageDetails.subSection3 = `${digitalData.pageDetails.subSection3}-landing`; 10871 } else if (digitalData.pageDetails.subSection4 && AB.DATALAYER.pathSections[4]) { 10872 AB.DATALAYER.pathSections[3] = `${AB.DATALAYER.pathSections[3]}-${AB.DATALAYER.pathSections[4]}`; 10873 digitalData.pageDetails.subSection4 = `${digitalData.pageDetails.subSection4}-${AB.DATALAYER.pathSections[4]}`; 10874 } 10875 } 10876 digitalData.pageDetails.pfmDetail = AB.DATALAYER.pathSections.join(':'); 10877 digitalData.pageDetails.pageUrl = window.location.pathname; 10878 AB.DATALAYER.setPageName(digitalData.pageDetails.channel, true); 10879 digitalData.pageFilter.topNav = digitalData?.pageFilter?.topNav ? digitalData.pageFilter.topNav : AB.DATALAYER.initTopNav(); 10880 if (subSection2 === 'partners' && impressions) { 10881 this.setImpression(impressions); 10882 } 10883 digitalData.modal = digitalData.modal || {}; 10884 digitalData.modal.modalMessage = subSection2 === 'dashboard' && impressions ? impressions : ''; 10885 // Send pageLoad event 10886 AB.DATALAYER.setPageLoad(); 10887 digitalData.pageFilter.topNav = ''; 10888 digitalData.modal.modalMessage = ''; 10889 } 10890 10891 /** 10892 * Init datalayer for OHC pages 10893 */ 10894 this.initHelpCenterPages = function () { 10895 const { pageDetails, user } = digitalData; 10896 if (pageDetails) { 10897 AB.DATALAYER.initPathSections(); 10898 for (let i = 0; i < 4; i++) { 10899 pageDetails[`subSection${i + 1}`] = AB.DATALAYER.pathSections[i] || ''; 10900 } 10901 pageDetails.subSection2 = pageDetails.subSection2 === 'home' ? 'main' : pageDetails.subSection2; 10902 pageDetails.pageName = [pageDetails.banner, 'loyalty', pageDetails.subSection1, pageDetails.subSection2, pageDetails.subSection3, pageDetails.subSection4].filter(
10902Boolean).join(':'); 10903 pageDetails.pageUrl = window.location.pathname; 10904 pageDetails.channel = 'help-center'; 10905 } 10906 if (!user || (user && Object.keys(user).length === 0)) { 10907 AB.DATALAYER.initUser(); 10908 } 10909 AB.DATALAYER.setPageLoad(); 10910 } 10911 10912 this.trackWeeklyAd = function (eventName, payload) { 10913 const flyerName = payload?.flyer?.flyerName?.replace(/\s/g, '-').toLowerCase(); 10914 const dateRange = payload?.validFrom && payload?.validTo ? `${(new Date(payload.validFrom)).toLocaleDateString('en-GB').split('/').reverse().join('').replace(/^20/, '')}-${(new Date(payload.validTo)).toLocaleDateString('en-GB').split('/').reverse().join('').replace(/^20/, '')}` : ''; 10915 let flyerType = 'current'; 10916 10917 if (!sessionStorage.getItem('weekly-ad-type-first')) { 10918 sessionStorage.setItem('weekly-ad-type-first', true); 10919 } else { 10920 flyerType = flyerName == 'weekly-ad' ? 'next' : flyerName; 10921 } 10922 10923 if (eventName == 'analyticsOpen') { 10924 window.digitalData.pageDetails.pageName = `${digitalData.pageDetails.banner}:weekly-ad:weekly-ad:${flyerType}:${dateRange}`; 10925 window.digitalData.pageDetails.subSection2 = flyerType; 10926 window.digitalData.pageDetails.subSection3 = dateRange; 10927 window.digitalData.pageFilter = { topNav: digitalData?.pageFilter?.topNav ? digitalData.pageFilter.topNav : AB.DATALAYER.initTopNav() }; 10928 AB.DATALAYER.initTopNavPageFlag(); 10929 const pageInfo = { 10930 pageName: `${digitalData.pageDetails.banner}:weekly-ad:weekly-ad:${flyerType}:${dateRange}`, 10931 subsection2: flyerType, 10932 subSection3: dateRange 10933 } 10934 localStorage.setItem('analyticsWeeklyAdPageInfo', JSON.stringify(pageInfo)); 10935 AB.DATALAYER.setPageLoad(); 10936 } else if (eventName == 'analyticsItemClick') { 10937 this.setLinkClickEvents(`weekly-ad:${flyerType}|item-view`); 10938 } 10939 } 10940 10941 this.initWeeklyAdPages = function (screen) { 10942 const flyerInfo = AB.DATALAYER.safeJSONParse(AB.DATALAYER.handleGetLSValue('analyticsWeeklyAdPageInfo')); 10943 digitalData.pageDetails.subSection1 = 'weekly-ad'; 10944 digitalData.pageDetails.subSection2 = screen === 'deals' ? 'your-deals' : flyerInfo.flyerType; 10945 digitalData.pageDetails.subSection3 = screen === 'deals' ? '' : flyerInfo.dateRange; 10946 screen === 'deals' ? AB.DATALAYER.setPageName(digitalData.pageDetails.channel, true) : digitalData.pageDetails.pageName = flyerInfo.pageName; 10947 if (screen === 'deals' || Object.keys(flyerInfo)?.length > 0) { 10948 AB.DATALAYER.setPageLoad(); 10949 } 10950 } 10951 10952 this.initSpotlightPages = function (userInfo, pageName, reportId) { 10953 const pageId = AB.DATALAYER.analyticsGetURLParameterByName('Id') || 'no-value'; 10954 window.digitalData.pageDetails = { 10955 pageName: pageName?.startsWith('admin-panel') ? pageName : pageName ? `${AB.DATALAYER.sanitizeAnalyticsText(pageName)}:${pageId}` : 'no-value', 10956 pageUrl: reportId ? `${window.location.pathname}?id=${reportId}` : window.location.pathname + window.location.search, 10957 platform: 'web', 10958 previousPageName: this.handleGetLSValue('analyticsPreviousPageName') || '', 10959 portalBanner: 'spotlight' 10960 }; 10961 window.digitalData.user = { 10962 userRole: userInfo?.userRole, 10963 userType: userInfo?.userType, 10964 partnerCompany: userInfo?.partnerCompany || 'no-value' 10965 }; 10966 this.setLSItem('analyticsPreviousPageName', window.digitalData.pageDetails.pageName); 10967 this.pushEvent( 10968 'pageLoaded_spotlight', 10969 { 10970 page: window.digitalData.pageDetails, 10971 user: window.digitalData.user, 10972 } 10973 ); 10974 } 10975 10976 this.initFulfillmentCenterPages = function (userInfo) { 10977 AB.DATALAYER.initPathSections(); 10978 if (digitalData?.pageDetails?.pageName !== AB.DATALAYER.pathSections?.join(':')) { 10979 digitalData.pageDetails = { 10980 pageName: AB.DATALAYER.pathSections?.join(':'), 10981 pageUrl: window.location.pathname, 10982 platform: 'web', 10983 previousPageName: this.handleGetLSValue('analyticsPreviousPageName') || '', 10984 portalBanner: 'fulfillment-center' 10985 }; 10986 digitalData.user = { 10987 userRole: userInfo?.userRole || 'no-value', 10988 userType: userInfo?.userType || 'no-value', 10989 partnerCompany: userInfo?.partnerCompany || 'no-value' 10990 }; 10991 digitalData.pageFilter = { 10992 filterType: '', 10993 filterSelection: '', 10994 filterSelection: '' 10995 }; 10996 this.setLSItem('analyticsPreviousPageName', digitalData.pageDetails.pageName); 10997 this.pushEvent( 10998 'pageLoaded_fc', 10999 { 11000 page: digitalData.pageDetails, 11001 pageFilter: digitalData.pageFilter, 11002 user: digitalData.user, 11003 } 11004 ); 11005 } 11006 } 11007 11008 /** 11009 * Sets the page load event on albertsonsmediacollective pages 11010 */ 11011 this.initMediaCollective = function (userData) { 11012 window.digitalData = window.digitalData || {}; 11013 if ((window.digitalData?.pageFlags?.previousPageUrl !== window.location.pathname) || !window.digitalData.pageFlags?.previousPageUrl) { 11014 window.digitalData.pageDetails = { 11015 pageName: document.title, 11016 pageUrl: window.location.pathname, 11017 platform: 'web', 11018 previousPageName: this.handleGetLSValue('analyticsPreviousPageName') || '' 11019 }; 11020 11021 let userType = ''; 11022 if (userData?.userCompanyRole?.role) { 11023 userType = ['CPG_ADMIN', 'CPG_BUYER', 'AGENCY_ADMIN', 'AGENCY_BUYER'].includes(userData.userCompanyRole.role) 11024 ? 'external' 11025 : ['HUB_ADMIN', 'CSM_ADMIN', 'CLIENT_SUCCESS_PARTNER', 'MEDIA_PLANNER_ADMIN', 'MEDIA_PLANNER', 'AUDIENCE_ANALYST', 'PERFORMANCE_ANALYST'].includes(userData.userCompanyRole.role) 11026 ? 'internal' 11027 : ''; 11028 } 11029 window.digitalData.user = { 11030 userRole: userData?.userCompanyRole?.role ? userData.userCompanyRole.role : window.digitalData.user?.userRole ? window.digitalData.user.userRole : '', 11031 userType: userType ? userType : window.digitalData.user?.userType ? window.digitalData.user.userType : '', 11032 partnerCompany: userData?.userCompanyRole?.partnerCompany ? userData.userCompanyRole.partnerCompany : window.digitalData.user?.partnerCompany ? window.digitalData.user.partnerCompany : '' 11033 }; 11034 11035 window.digitalData.pageFilter = { 11036 topNav: AB.DATALAYER.initTopNav(), 11037 filterType: '', 11038 filterSelection: '', 11039 filterZone: '' 11040 }; 11041 // use pageFlags.previousPageUrl to prevent duplicate calls 11042 window.digitalData.pageFlags = window.digitalData.pageFlags || {}; 11043 window.digitalData.pageFlags.previousPageUrl = window.digitalData.pageDetails.pageUrl; 11044 11045 this.setLSItem('analyticsPreviousPageName', window.digitalData.pageDetails.pageName); 11046 this.pushEvent( 11047 'pageLoaded_mediacollective', 11048 { 11049 page: window.digitalData.pageDetails, 11050 user: window.digitalData.user, 11051 pageFilter: window.digitalData.pageFilter 11052 } 11053 ); 11054 } 11055 } 11056 11057 /** 11058 * Sends event when downloading a report from media collective portal 11059 */ 11060 this.downloadReportEvent = function (reportName, reportFormat) { 11061 if (window.digitalData?.pageDetails) { 11062 window.digitalData.pageDetails.downloadReportDetails = `${reportName === 'searchReport' ? window.digitalData.search?.searchValue : reportName}:${reportFormat}`; 11063 AB.DATALAYER.pushEvent( 11064 'download', 11065 { 11066 page: window.digitalData.pageDetails 11067 } 11068 ); 11069 } 11070 } 11071 11072 /** 11073 * Tracks download events on Media Hub view wrap reports page 11074 * @param {Object} data 11075 */ 11076 this.downloadWrapReport = function (data) { 11077 if (window.digitalData?.pageDetails && data) { 11078 const docInfo = data?.documents && data?.documents[0] ? data?.documents[0] : {}; 11079 const partnerName = data?.company; 11080 const docValuesArray = docInfo.originalFileName?.split('.'); 11081 const docFormat = docValuesArray.pop(); 11082 const docName = docValuesArray.join('.'); 11083 11084 window.digitalData.user = window.digitalData.user || {}; 11085 window.digitalData.user.partnerCompany = partnerName; 11086 window.digitalData.pageDetails.downloadReportDetails = `${docName}:${docFormat}`; 11087 AB.DATALAYER.pushEvent( 11088 'download', 11089 { 11090 page: window.digitalData.pageDetails, 11091 user: window.digitalData.user 11092 } 11093 ); 11094 } 11095 } 11096 11097 /** 11098 * Sends event when selecting a filter on media collective portal 11099 */ 11100 this.setFilterEventMediaCollective = function (filterSelection, filterType, pageSection, eventType) { 11101 if (window.digitalData?.pageFilter && filterSelection) { 11102 11103 const eventName = eventType ? eventType : (filterSelection === 'reset' || filterSelection === 'reset_all') ? 'filter_reset' : 'filter'; 11104 const { pageFilter } = window.digitalData; 11105 pageFilter.filterType = filterType; 11106 pageFilter.filterZone = pageSection; 11107 11108 const formatFilterDate = (dateString) => { 11109 const date = new Date(dateString);
11110 const month = String(date.getMonth() + 1).padStart(2, '0'); 11111 const day = String(date.getDate()).padStart(2, '0'); 11112 const year = date.getFullYear(); 11113 return `${month}/${day}/${year}`; 11114 } 11115 11116 const updatedFilterSelection = Array.isArray(filterSelection) 11117 ? filterType === 'date range' && filterSelection.length === 2 11118 ? `${formatFilterDate(filterSelection[0])}-${formatFilterDate(filterSelection[1])}` 11119 : filterSelection.join(',') 11120 : filterSelection; 11121 11122 const eventSent = filterType === 'date range' 11123 ? appEventData.some(eventObj => eventObj?.pageFilter?.filterSelection === updatedFilterSelection) 11124 : false; 11125 pageFilter.filterSelection = updatedFilterSelection; 11126 if (!eventSent) { 11127 AB.DATALAYER.pushEvent( 11128 eventName, 11129 { 11130 pageFilter: pageFilter 11131 } 11132 ); 11133 } 11134 } 11135 } 11136 11137 /** 11138 * Track filter events on Media Hub wrap reports page 11139 * @param {Object} filterData 11140 * @param {String} pageType 11141 * @param {String} eventType 11142 */ 11143 this.setFilterEventWrapReportsMediaCollective = function (filterData, pageType, eventType) { 11144 const { pageFilter } = window.digitalData; 11145 const eventName = eventType === 'reset_all' ? eventType : 'filter'; 11146 const filterPairs = []; 11147 const filterTypeArray = []; 11148 pageFilter.filterZone = pageType; 11149 if (filterData) { 11150 Object.entries(filterData)?.forEach(dataPair => { 11151 if (dataPair && dataPair[0] && dataPair[1]) { 11152 if (dataPair[0] === 'partners') { 11153 dataPair[0] = 'Advertisers'; 11154 } 11155 if (dataPair[1] && Array.isArray(dataPair[1]) && dataPair[1].length === 0) { 11156 dataPair[1] = 'All'; 11157 } 11158 filterTypeArray.push(dataPair[0]); 11159 filterPairs.push(dataPair.join(': ')?.toLowerCase()); 11160 } 11161 }) 11162 } 11163 11164 pageFilter.filterSelection = filterPairs.join(','); 11165 pageFilter.filterType = filterTypeArray.join(','); 11166 11167 AB.DATALAYER.pushEvent( 11168 eventName, 11169 { 11170 pageFilter: pageFilter 11171 } 11172 ); 11173 } 11174 11175 /** 11176 * Sets the search results event on Media Collective Portal 11177 */ 11178 this.setSearchEventMediaCollective = function (data) { 11179 if (window.digitalData && data) { 11180 const isBasicSearch = data.searchPage === 'help-center' || data.searchPage === 'wrap-reports'; 11181 const shouldSetHCSearch = isBasicSearch && data.searchTerm && data.searchTerm != window.digitalData.search?.searchValue; 11182 const eventName = data.searchPage === 'help-center' ? 'search_results_hc' : data.searchPage === 'wrap-reports' ? 'search_results_cwr' : this.EVENTS.SEARCH; 11183 const shouldSendSearchEvent = !isBasicSearch || shouldSetHCSearch; 11184 window.digitalData.search = {}; 11185 11186 if (shouldSetHCSearch) { 11187 window.digitalData.search.searchValue = data.searchTerm; 11188 window.digitalData.search.searchType = data.searchType; 11189 } else if (!isBasicSearch) { 11190 window.digitalData.search.searchValue = data.searchBy === 'brand' 11191 ? `company:${data.company?.toLowerCase()}|brand:${data.brands?.length === 0 ? 'no-value' : data.brands?.join(',').toLowerCase()}` 11192 : `upc:${data.upcs?.length === 0 ? 'null' : data.upcs?.join(',')}`; 11193 window.digitalData.search.searchType = data.searchBy === 'brand' ? 'company/brand' : 'upc'; 11194 } 11195 11196 shouldSendSearchEvent && this.pushEvent( 11197 eventName, 11198 { 11199 page: window.digitalData.pageDetails, 11200 user: window.digitalData.user, 11201 search: window.digitalData.search 11202 } 11203 ); 11204 } 11205 } 11206 11207 /** 11208 * Sets the page load event on albertsons partner pages 11209 */ 11210 this.initAlbsPartner = function (data) { 11211 window.digitalData = window.digitalData || {}; 11212 if (((window.digitalData?.pageFlags?.previousPageUrl !== window.location.pathname) || !window.digitalData.pageFlags?.previousPageUrl) && data?.userProfileData && Object.keys(data.userProfileData).length > 0) { 11213 const eventType = AB.DATALAYER.analyticsGetURLParameterByName('eventType'); 11214 const viewType = window.sessionStorage?.viewType; 11215 11216 window.digitalData.pageDetails = { 11217 pageName: document.title, 11218 pageUrl: window.location.pathname, 11219 platform: 'web', 11220 previousPageName: this.handleGetLSValue('analyticsPreviousPageName') || '', 11221 company: data?.userProfileData?.analytics?.companyInfo?.[0]?.companyDetail?.legalBusinessName ? data.userProfileData.analytics.companyInfo[0].companyDetail.legalBusinessName : window.digitalData.pageDetails?.company ? window.digitalData.pageDetails.company : '' 11222 }; 11223 11224 window.digitalData.user = { 11225 userAcess: data?.userProfileData?.analytics?.access?.[0] ? data.userProfileData.analytics.access[0] : window.digitalData.user?.userAcess ? window.digitalData.user.userAcess : '', 11226 userEmail: data?.userProfileData?.profileInfo?.emailAddress, 11227 userRole: data?.roleName, 11228 userId: data?.userProfileData?.userId 11229 }; 11230 11231 switch (true) { 11232 case digitalData.pageDetails.pageName == 'APP : Promotion - Events': 11233 digitalData.pageDetails.pageName = 'APP : Promotion-Management:Create-Event'; 11234 break; 11235 case digitalData.pageDetails.pageName == 'APP : Promotion - Create' && eventType == 'DP': 11236 digitalData.pageDetails.pageName = 'APP : Promotion-Management:Division-Promotion'; 11237 break; 11238 case digitalData.pageDetails.pageName == 'APP : Promotion - Create' && eventType == 'AO': 11239 digitalData.pageDetails.pageName = 'APP : Promotion-Management:Division-Allowances-Only'; 11240 break; 11241 case digitalData.pageDetails.pageName == 'APP : Promotion - Create' && eventType == 'NDP': 11242 digitalData.pageDetails.pageName = 'APP : Promotion-Management:National'; 11243 break; 11244 case ((digitalData.pageDetails.pageName == 'APP : Promotion - Meupp' || digitalData.pageDetails.pageName == 'APP : Meupp') && viewType === 'taskView') || ((digitalData.pageDetails.pageName == 'APP : Promotion - Meupp' || digitalData.pageDetails.pageName == 'APP : Meupp') && !viewType): 11245 digitalData.pageDetails.pageName = 'APP : Promotion-Management:Tasks '; 11246 break; 11247 case (digitalData.pageDetails.pageName == 'APP : Promotion - Meupp' || digitalData.pageDetails.pageName == 'APP : Meupp') && viewType === 'planningView': 11248 digitalData.pageDetails.pageName = 'APP : Promotion-Management:Planning '; 11249 break; 11250 case (digitalData.pageDetails.pageName == 'APP : Promotion - Meupp' || digitalData.pageDetails.pageName == 'APP : Meupp') && viewType === 'allowanceView': 11251 digitalData.pageDetails.pageName = 'APP : Promotion-Management:Allowances '; 11252 break; 11253 case (digitalData.pageDetails.pageName == 'APP : Promotion - Meupp' || digitalData.pageDetails.pageName == 'APP : Meupp') && viewType === 'alertView': 11254 digitalData.pageDetails.pageName = 'APP : Promotion-Management:Alerts '; 11255 break; 11256 default: 11257 break; 11258 } 11259 11260 // use pageFlags.previousPageUrl to prevent duplicate calls 11261 window.digitalData.pageFlags = window.digitalData.pageFlags || {}; 11262 window.digitalData.pageFlags.previousPageUrl = window.digitalData.pageDetails.pageUrl; 11263 11264 this.setLSItem('analyticsPreviousPageName', window.digitalData.pageDetails.pageName); 11265 this.pushEvent( 11266 'pageLoaded_albspartner', 11267 { 11268 page: window.digitalData.pageDetails, 11269 user: window.digitalData.user 11270 } 11271 ); 11272 } 11273 } 11274 11275 this.trackTabChangeAlbsPartner = function (tabName) { 11276 digitalData.pageDetails.pageName = `APP : Promotion-Management:${tabName}` 11277 this.setLSItem('analyticsPreviousPageName', window.digitalData.pageDetails.pageName); 11278 this.pushEvent( 11279 'pageLoaded_albspartner', 11280 { 11281 page: window.digitalData.pageDetails, 11282 user: window.digitalData.user 11283 } 11284 ); 11285 } 11286 11287 /** 11288 * Sets the search results event on Albertsons Partner - Help Center 11289 */ 11290 this.setSearchEventAlbsPartner = function (searchTerm, data) { 11291 if (window.digitalData && data !== undefined) { 11292 window.digitalData.search = {}; 11293 window.digitalData.search.searchTerm = searchTerm; 11294 window.digitalData.search.searchResults = data?.count || 0; 11295 this.pushEvent( 11296 this.EVENTS.SEARCH, 11297 { 11298 page: window.digitalData.pageDetails, 11299 user: window.digitalData.user, 11300 search: window.digitalData.search 11301 } 11302 ); 11303 } 11304 } 11305 11306 /** 11307 * Subscribes to Firework Video events 11308 */ 11309 this.initFireworkEvents = function () { 11310 // Video loads on the page 11311 document.addEventListener('fw:video:impression', (event) => { 11312 this.setFireworkEvents('videoImpression', event.detail); 11313 }); 11314 11315 // Video started 11316 document.addEventListener('fw:video:start', (event) => { 11317 this.setFireworkEvents('videoStart', event.detail); 11318 }); 11319 11320 // Video reached 25% 11321 document.addEventListener('fw:video:first-quartile', (event) => { 11322 this.setFireworkEvents('videoFirstQuartile', event.detail, '25'); 11323 }); 11324 11325 // Video reached at least 90% 11326 document.addEventListener('fw:video:complete', (event) => { 11327 this.setFireworkEvents('videoComplete', event.detail, '100'); 11328 }); 11329 } 11330 11331 /** 11332 * Sets events for Firework videos 11333 * @param eventName 11334 * @param eventDetails 11335 * @param percentage 11336 */ 11337 this.setFireworkEvents = function (eventName, eventDetails, percentage) { 11338 if (eventDetails && eventDetails.video && eventDetails.extra) { 11339 11340 // get playlistName 11341 const currentPlaylist = document.querySelector(`.web-stories [playlist=${eventDetails.playlist}]`); 11342 const currentPlaylistTitle = currentPlaylist?.closest('.web-stories')?.querySelector('.abs-short-videos__cta-title')?.textContent?.trim(); 11343 11344 this.pushEvent( 11345 eventName, 11346 { 11347 video: { 11348 name: `${currentPlaylistTitle}:${eventDetails.video.caption}`, 11349 duration: `${eventDetails.extra.duration} seconds`, 11350 videoId: eventDetails.video.encoded_id, 11351 percentage: percentage, 11352 playerName: 'firework' 11353 }, 11354 page: digitalData?.pageDetails, 11355 pageFilter: digitalData?.pageFilter 11356 } 11357 ); 11358 } 11359 } 11360 11361 /**
11362 * Returns analytics context from a [data-video-enterprise] container element. 11363 */ 11364 this._getInHouseVideoContext = function (container) { 11365 return { 11366 campId: container.dataset.campId || 'no-value', 11367 orientation: container.dataset.orientation || 'vertical', 11368 isSponsored: container.dataset.isSponsored === 'true', 11369 rowIndex: parseInt(container.dataset.rowIndex) || 1, 11370 slotIndex: parseInt(container.dataset.slotIndex) || 1, 11371 }; 11372 }; 11373 11374 /** 11375 * Fires a linkClick event for the in-house enterprise video player, matching the 11376 * full standard payload (page, pageFilter, user, productInfo, modal, cart) from digitalData, 11377 * exactly as setLinkClickEvents does internally. 11378 */ 11379 this.setInHouseVideoLinkClickEvent = function (linkName, customData, oncePerPageView) { 11380 try { 11381 const alreadySent = oncePerPageView ? this.setEventOncePerPage(linkName, 'linkClick') : false; 11382 if (alreadySent) return; 11383 const { mediaPlacement, pageFilter: pageFilterOverride, ...rest } = customData || {}; 11384 // Adobe Launch reads V80 from pageFilter.mediaPlacementClick â mutate before pushEvent 11385 // so both the EDDL event snapshot and any Launch rules reading digitalData directly see the value. 11386 if (mediaPlacement) { 11387 digitalData = digitalData || {}; 11388 digitalData.pageFilter = digitalData.pageFilter || {}; 11389 digitalData.pageFilter.mediaPlacementClick = mediaPlacement; 11390 } 11391 this.pushEvent(this.EVENTS.LINK_CLICK, { 11392 link: { linkName }, 11393 page: digitalData?.pageDetails, 11394 pageFilter: pageFilterOverride ? { ...digitalData?.pageFilter, ...pageFilterOverride } : digitalData?.pageFilter, 11395 user: digitalData?.user, 11396 productInfo: digitalData?.productInfo || {}, 11397 modal: digitalData?.modal, 11398 cart: digitalData?.cart, 11399 ...rest, 11400 }); 11401 } catch (e) { 11402 console.error('@AnalyticsTeam - Failed to send in-house video event: ', e); 11403 } 11404 }; 11405 11406 /** 11407 * Subscribes to in-house enterprise video player DOM events. 11408 * Analytics context is read from [data-video-enterprise] attributes set by the Angular microapp. 11409 * Follows the same pattern as initFireworkEvents. 11410 */ 11411 this.initInHouseVideoEvents = function () { 11412 // Tracks which campId+page combos have already fired a play event this page view. 11413 // Keyed by `${campId}::${pathname}` so SPA navigation to a new URL resets the guard automatically. 11414 const _firedVideoPlay = new Map(); 11415 11416 // AC#1 â Track play click on in-house video player (once per page view per video/campId) 11417 document.addEventListener('abs-video-play', (event) => { 11418 const container = event.target.closest('[data-video-enterprise]'); 11419 if (!container) return; 11420 const { campId, orientation, isSponsored, rowIndex, slotIndex } = this._getInHouseVideoContext(container); 11421 const deduKey = `${campId}::${window.location.pathname}`; 11422 if (_firedVideoPlay.has(deduKey)) return; 11423 _firedVideoPlay.set(deduKey, true); 11424 const rec = isSponsored ? 'sponsored' : 'not-recommended'; 11425 const row = `r${String(rowIndex).padStart(2, '0')}`; 11426 const slot = `s${String(slotIndex).padStart(2, '0')}`; 11427 const placementUC = `R${String(rowIndex).padStart(2, '0')}#S${String(slotIndex).padStart(2, '0')}`; 11428 const mediaPlacementVal = `video-enterprise-${orientation}#${rec}#${row}#${slot}`; 11429 const videoBentoCtx = { 11430 content: `video-enterprise-${orientation}`, 11431 mediaType: `video-enterprise-${orientation}`, 11432 recommendationType: isSponsored ? 'sponsored' : 'not-recommended', 11433 placement: placementUC, 11434 mediaPlacementClick: mediaPlacementVal, 11435 }; 11436 digitalData._bentoProductContext = videoBentoCtx; 11437 setTimeout(() => { 11438 if (digitalData._bentoProductContext === videoBentoCtx) { 11439 delete digitalData._bentoProductContext; 11440 } 11441 }, 5000); 11442 this.setInHouseVideoLinkClickEvent( 11443 `video-enterprise-${orientation}|play`, 11444 { 11445 video: { name: `video-enterprise:${campId}` }, 11446 page: { ...digitalData?.pageDetails, feature: 'video-enterprise|shp-exp|595549|53801' }, 11447 pageFilter: { mediaType: `video-enterprise-${orientation}`, placement: placementUC, recommendationType: rec }, 11448 mediaPlacement: mediaPlacementVal, 11449 } 11450 ); 11451 }); 11452 11453 // AC#2 â Track add to cart click â injects _bentoProductContext for ALL products so 11454 // setPfmDetailOnProductAction uses video-enterprise pfmDetail on every page/orientation.
11455 // The cart_add linkClick event fires for the first product only; existing analytics handles the rest. 11456 document.addEventListener('click', (event) => { 11457 const container = event.target.closest('[data-video-enterprise]'); 11458 if (!container || !event.target.closest('[data-qa="addbutton"], [data-qa="addwgtBtn"]')) return; 11459 const clickedProduct = event.target.closest('[data-qa="prd-itm"]'); 11460 if (!clickedProduct) return; 11461 const { campId, orientation, isSponsored, rowIndex, slotIndex } = this._getInHouseVideoContext(container); 11462 // Horizontal add-to-cart tracking is scoped to the horizontal video grid product list only. 11463 if (orientation === 'horizontal' && !event.target.closest('.horizontal-video-grid__products')) return; 11464 const rec = isSponsored ? 'sponsored' : 'not-recommended'; 11465 const row = `r${String(rowIndex).padStart(2, '0')}`; 11466 const slot = `s${String(slotIndex).padStart(2, '0')}`; 11467 const placementUC = `R${String(rowIndex).padStart(2, '0')}#S${String(slotIndex).padStart(2, '0')}`; 11468 // Inject bento-style context for ALL products so handleItemEvent â setPfmDetailOnProductAction 11469 // sets the correct video-enterprise evar14 regardless of orientation (vertical/search, horizontal/PDP). 11470 const videoBentoCtx = { 11471 content: `video-enterprise-${orientation}`, 11472 mediaType: `video-enterprise-${orientation}`, 11473 recommendationType: isSponsored ? 'sponsored' : 'not-recommended', 11474 placement: placementUC, 11475 mediaPlacementClick: `video-enterprise-${orientation}#${rec}#${row}#${slot}`, 11476 }; 11477 digitalData._bentoProductContext = videoBentoCtx; 11478 // Auto-clear after generous cart API timeout; reference check avoids clobbering a later context. 11479 setTimeout(() => { 11480 if (digitalData._bentoProductContext === videoBentoCtx) { 11481 delete digitalData._bentoProductContext; 11482 } 11483 }, 5000); 11484 // Only fire the cart_add linkClick for the first product; existing analytics handles the rest. 11485 const firstProduct = container.querySelector('[data-qa="prd-itm"]'); 11486 if (clickedProduct !== firstProduct) return; 11487 this.setInHouseVideoLinkClickEvent('cart_add', { 11488 video: { name: `video-enterprise:${campId}` }, 11489 page: { ...digitalData?.pageDetails, feature: 'video-enterprise|shp-exp|595549|53801' }, 11490 pageFilter: { mediaType: `video-enterprise-${orientation}`, placement: placementUC, recommendationType: rec }, 11491 mediaPlacement: `video-enterprise-${orientation}#${rec}#${row}#${slot}`, 11492 }); 11493 }, true); 11494 11495 // AC#3 â Track product image click to PDP â stores bentoHeroPdpPfm + mediaPlacementClick for ALL 11496 // products so PDP page load picks up the correct evar14/V80 for any orientation (vertical/search, 11497 // horizontal/PDP). The linkClick event + CTA tracking fire for the first product only. 11498 document.addEventListener('click', (event) => { 11499 const container = event.target.closest('[data-video-enterprise]'); 11500 if (!container || !event.target.closest('a.pc__tap-zone, [data-qa="prd-itm-pttl"], [data-qa="prd-itm-img"]')) return; 11501 const clickedProduct = event.target.closest('[data-qa="prd-itm"]'); 11502 if (!clickedProduct) return; 11503 const { campId, orientation, isSponsored, rowIndex, slotIndex } = this._getInHouseVideoContext(container); 11504 // Horizontal click-to-PDP tracking is scoped to the horizontal video grid product list only. 11505 if (orientation === 'horizontal' && !event.target.closest('.horizontal-video-grid__products')) return; 11506 const rec = isSponsored ? 'sponsored' : 'not-recommended'; 11507 const row = `r${String(rowIndex).padStart(2, '0')}`; 11508 const slot = `s${String(slotIndex).padStart(2, '0')}`;
11509 const placementUC = `R${String(rowIndex).padStart(2, '0')}#S${String(slotIndex).padStart(2, '0')}`; 11510 const subSection = digitalData?.pageDetails?.subSection1 || 'search-results'; 11511 const ctaValue = `cta:${subSection}|link|video-enterprise-${orientation}`; 11512 // Store video-enterprise pfm context for ALL products so PDP page load picks up correct 11513 // evar14/V80 regardless of which product was clicked or what orientation the container uses. 11514 const videoCtxPdp = { 11515 content: `video-enterprise-${orientation}`, 11516 mediaType: `video-enterprise-${orientation}`, 11517 recommendationType: isSponsored ? 'sponsored' : 'not-recommended', 11518 placement: placementUC, 11519 }; 11520 this.setBentoPdpPfm(videoCtxPdp, null, null); // â localStorage.bentoHeroPdpPfm â evar14 on PDP load 11521 this.setBentoMediaPlacement(videoCtxPdp, true); // â localStorage.mediaPlacementClick â V80 on PDP load 11522 // Only fire linkClick + CTA tracking for the first product; existing analytics handles the rest. 11523 const firstProduct = container.querySelector('[data-qa="prd-itm"]'); 11524 if (clickedProduct !== firstProduct) return; 11525 // V171 â save to localStorage so PDP page load picks it up as pageFilter.topNav 11526 this.analyticsCTATracking(ctaValue); 11527 // V95/V208/v80 â immediate linkClick event on click page 11528 this.setInHouseVideoLinkClickEvent(ctaValue, { 11529 video: { name: `video-enterprise:${campId}` }, 11530 page: { ...digitalData?.pageDetails, feature: 'video-enterprise|shp-exp|595549|53801' }, 11531 pageFilter: { mediaType: `video-enterprise-${orientation}`, placement: placementUC, recommendationType: rec }, 11532 mediaPlacement: `video-enterprise-${orientation}#${rec}#${row}#${slot}`, 11533 }); 11534 }, true); 11535 11536 // AC#4 â Capture errors from in-house video player 11537 document.addEventListener('abs-video-error', (event) => { 11538 const container = event.target.closest('[data-video-enterprise]'); 11539 if (!container) return; 11540 const { campId, orientation, isSponsored, rowIndex, slotIndex } = this._getInHouseVideoContext(container); 11541 const rec4 = isSponsored ? 'sponsored' : 'not-recommended'; 11542 const placementUC4 = `R${String(rowIndex).padStart(2, '0')}#S${String(slotIndex).padStart(2, '0')}`; 11543 const { errorId = 'no-value', errorName = 'no-value', errorMessage = 'no-value' } = event.detail || {}; 11544 try { 11545 this.pushEvent(this.EVENTS.ERROR, { 11546 video: { name: `video-enterprise:${campId}` }, 11547 page: { ...digitalData?.pageDetails, feature: 'video-enterprise|shp-exp|595549|53801' }, 11548 pageFilter: { ...digitalData?.pageFilter, mediaType: `video-enterprise-${orientation}`, placement: placementUC4, recommendationType: rec4 }, 11549 error: { 11550 errorID: `video-enterprise:${errorId}`, 11551 errorFeature: errorName, 11552 errorMessage: `video-enterprise:${errorMessage}`, 11553 }, 11554 }); 11555 } catch (e) { 11556 console.error('@AnalyticsTeam - Failed to send in-house video event: ', e); 11557 } 11558 }); 11559 }; 11560 11561 this.analyticsSetLSFPUpdate = function (source, isTrial) { 11562 if (source === 'upgradeBtn') { 11563 this.setLSItem('analyticsFPLSitem', 'clickedBanner'); 11564 } else if (source === 'annualBtn' && this.getLSItem('analyticsFPLSitem') === 'clickedBanner') { 11565 this.setLSItem('analyticsFPLSitem', 'clickedAnnualBtn'); 11566 } else if (source === 'submitBtn' && this.getLSItem('analyticsFPLSitem') === 'clickedAnnualBtn') { 11567 this.setLSItem('analyticsFPLSitem', 'clickedPaymentSubmit'); 11568 } 11569 if (source === 'submitBtn' && this.getLSItem('analyticsChangePlan') === 'annual' || source === 'changePlanToAnnualBtn') {
11570 this.setLSItem('analyticsFPLSitem', 'changeToAnnual'); 11571 this.removeLSItem('analyticsChangePlan'); 11572 } else if (source === 'submitBtn' && this.getLSItem('analyticsChangePlan') === 'monthly' || source === 'changePlanToMonthlyBtn') { 11573 this.setLSItem('analyticsFPLSitem', 'changeToMonthly'); 11574 this.removeLSItem('analyticsChangePlan'); 11575 } 11576 if (isTrial === 'TRIAL') { 11577 this.setLSItem('analyticsTrialFPUpdate', true); 11578 } 11579 } 11580 11581 /** 11582 * Returns all visible banners 11583 */ 11584 this.getAllBanners = function () { 11585 11586 const selectors = ['.nextgen-banner-unit-proxy', '.banner-unit-proxy', 11587 '.nextgen-abs-carousel-wrapper', '.abs-carousel-wrapper', '.as-hero__carousel', 11588 '.mod-unit-unified-proxy', '.mod-unit-skinny-proxy', '.mod-unit-carousel-container', '.merch-targeted-promo-banner', '.nextgen-banner-unit', '.banner-unit-hero', 'search-grid-merch-banner', '.banner-unit-medium, .abs-carousel']; 11589 const allElements = document.querySelectorAll(selectors.join(', ')); 11590 11591 const allBanners = allElements && Array.from(allElements).filter(el => { 11592 return AB.DATALAYER.isVisible(el) && 11593 ((el.classList.contains('nextgen-banner-unit-proxy') && 11594 el.querySelector('.nextgen-banner-unit')?.children?.length > 0 && 11595 !el.classList.contains('d-none') && 11596 !el.closest('.nextgen-abs-carousel-wrapper')) || 11597 (el.classList.contains('nextgen-banner-unit-proxy') && 11598 el?.children[0]?.classList?.contains('merch-targeted-promo-banner')) || 11599 (el.classList.contains('nextgen-abs-carousel-wrapper') && 11600 el.querySelector('.nextgen-banner-unit')?.children?.length > 0) || 11601 (el.classList.contains('as-hero__carousel') && 11602 el.querySelector('.as-hero__item')?.children?.length > 0) || 11603 (el.classList.contains('banner-unit-proxy') && 11604 el.querySelector('.banner-unit-regular')?.children?.length > 0 && 11605 !el.closest('.abs-carousel-wrapper') && 11606 !el.classList.contains('d-none') && 11607 !el.classList.contains('collapse')) || 11608 (el.classList.contains('nextgen-abs-carousel-wrapper') && 11609 el.querySelector('.hero-canvas')?.children?.length > 0) || 11610 (el.classList.contains('mod-unit-unified-proxy') && 11611 !el.closest('.mod-unit-carousel-container')) || 11612 (el.classList.contains('mod-unit-skinny-proxy') && 11613 !el.closest('.mod-unit-carousel-container')) || 11614 el.classList.contains('mod-unit-carousel-container') || 11615 (el.classList.contains('abs-carousel-wrapper') && 11616 !el.querySelector('.nextgen-banner-unit-proxy')?.children[0]?.classList?.contains('merch-targeted-promo-banner') && !el.parentElement?.classList?.contains('d-none')) || 11617 (el.classList.contains('merch-targeted-promo-banner') && !el.classList.contains('d-none')) || 11618 (!el.parentElement?.classList?.contains('nextgen-banner-unit-proxy') && el.parentElement?.querySelector('webgen-banner-unit')) || 11619 (el.nodeName == 'SEARCH-GRID-MERCH-BANNER') || 11620 (el.classList?.contains('banner-unit-hero')) || 11621 (el.classList?.contains('banner-unit-medium')) || 11622 (el.classList.contains('abs-carousel') && el.querySelector('.hero-canvas'))) 11623 }); 11624 11625 return allBanners || []; 11626 }; 11627 11628 /** 11629 * Checks if it's a single banner or multiple banners per row 11630 */ 11631 this.isMultipleSlotsBanner = function (bannerClassMappings, currentBanner) { 11632 for (const className in bannerClassMappings) { 11633 const wrapper = bannerClassMappings[className]; 11634 const unitSelector = `.${className}`; 11635 if (currentBanner.classList.contains(className) && currentBanner.closest(wrapper) && currentBanner.closest(wrapper).querySelector(unitSelector)) { 11636 return true 11637 } 11638 } 11639 return false;
11640 } 11641 11642 /** 11643 * Returns the banner title 11644 */ 11645 this.getBannerClickedTitle = function (bannerClicked, selectors, bannerTypeString) { 11646 const currentBanner = bannerClicked?.closest(selectors.currentBanner); 11647 const titleElement = currentBanner?.querySelector(selectors.titleElement); 11648 const targetTitle = titleElement?.querySelector('.target-visually-shown')?.textContent || ''; 11649 let titleText = titleElement ? titleElement.textContent?.trim().toLowerCase().replace(/[^a-z0-9|-]/gi, '-').replace(/^-+|-+$/g, '').replace(/-+/g, '-') : ''; 11650 11651 if (targetTitle) { 11652 titleText = AB.DATALAYER.sanitizeAnalyticsText(targetTitle); 11653 } 11654 11655 if (bannerTypeString === 'mediumBanner' && !titleText) { 11656 const bannerImageAltAttr = bannerClicked?.querySelector('img')?.getAttribute('alt'); 11657 titleText = bannerImageAltAttr ? bannerImageAltAttr.trim().replace(/\s/g, '') : 'no-title'; 11658 } 11659 11660 return titleText; 11661 } 11662 11663 /** 11664 * Returns the banner placement 11665 */ 11666 this.getBannerPlacement = function (bannerClicked, selectors, bannerClassMappings) { 11667 let currentBanner = bannerClicked?.closest(selectors.currentBanner); 11668 if (!currentBanner) { 11669 return ''; 11670 } 11671 let isMultipleSlotsBanner = bannerClassMappings ? AB.DATALAYER.isMultipleSlotsBanner(bannerClassMappings, currentBanner) : false; 11672 const totalBannerSlots = AB.DATALAYER.getAllBanners(); 11673 let bannerPlacement = ''; 11674 if (isMultipleSlotsBanner) { 11675 if (currentBanner?.classList?.contains('nextgen-banner-unit') && currentBanner.closest('.nextgen-banner-unit-proxy')) { 11676 currentBanner = currentBanner.closest('.nextgen-banner-unit-proxy'); 11677 } 11678 const currentBannerContainer = bannerClicked?.closest(selectors.wrapperElement); 11679 const bannerRow = currentBannerContainer ? totalBannerSlots?.indexOf(currentBannerContainer) + 1 : 0; 11680 const currentContainerBanners = currentBannerContainer 11681 ? Array.from(currentBannerContainer.querySelectorAll(selectors.currentBanner)) 11682 .filter(banner => !(banner.classList?.contains('slick-cloned') || banner.closest('.slick-cloned') || banner.classList?.contains('nextgen-banner-unit'))) 11683 : []; 11684 const bannerSlot = Array.from(currentContainerBanners)?.indexOf(currentBanner) + 1 || 0; 11685 const displayableBannerRow = bannerRow < 10 ? `R0${bannerRow}` : `R${bannerRow}`; 11686 const displayableBannerSlot = bannerSlot < 10 ? `S0${bannerSlot}` : `S${bannerSlot}`; 11687 11688 bannerPlacement = bannerSlot > 0 && currentContainerBanners.length > 1 11689 ? `${displayableBannerRow}#${displayableBannerSlot}` 11690 : displayableBannerRow; 11691 } else { 11692 if (currentBanner.classList?.contains('nextgen-banner-unit') && currentBanner.closest('.nextgen-banner-unit-proxy')) { 11693 currentBanner = currentBanner.closest('.nextgen-banner-unit-proxy'); 11694 } 11695 if (currentBanner.classList?.contains('flex-medium-fw')) { 11696 currentBanner = currentBanner.closest(selectors.wrapperElement); 11697 } 11698 const bannerRow = totalBannerSlots?.indexOf(currentBanner) + 1 || 0; 11699 bannerPlacement = bannerRow < 10 ? `R0${bannerRow}#S00` : `R${bannerRow}#S00`; 11700 } 11701 return bannerPlacement; 11702 } 11703 11704 /** 11705 * Returns the banner recommendation type 11706 */ 11707 this.getBannerRecommendationType = function (selectors, currentBanner, bannerType) { 11708 if (!currentBanner) { 11709 return ''; 11710 } 11711 let bannerApiSource = ''; 11712 if (bannerType === 'hero') { 11713 const sponsoredBannerIndexes = []; 11714 const bannerIndex = currentBanner.getAttribute('data-slick-index'); 11715 const heroContainer = currentBanner.closest('.as-hero'); 11716 heroContainer?.querySelectorAll('hero-sponsored-wc').forEach(sponsoredBanner => { 11717 sponsoredBannerIndexes.push(sponsoredBanner?.dataset?.bannerPosition); 11718 }); 11719 if (sponsoredBannerIndexes.includes(parseInt(bannerIndex))) { 11720 bannerApiSource = heroContainer?.querySelector('hero-sponsored-wc')?.dataset?.bannerApiSource || currentBanner.querySelector('.as-hero__background--desktop .as-hero__item__footer')?.textContent; 11721 } 11722 } else { 11723 bannerApiSource = currentBanner.querySelector(selectors.bannerSource)?.dataset?.bannerApiSource; 11724 } 11725 if (bannerApiSource === 'sponsoredBannerApi' || bannerApiSource === 'sponsoredad
11725api' || bannerApiSource === 'sponsoredadapiviadomainapi' || bannerApiSource === 'sponsoredbannerapi' || bannerApiSource === 'sponsored') { 11726 return 'sponsored'; 11727 } 11728 return 'notRecommended'; 11729 } 11730 11731 /** 11732 * Sets the link click event when clicking on banners 11733 */ 11734 this.setBannerClickedEvent = function (placement, title, recommendationType, mediaType, bannerTitle) { 11735 if (window.digitalData && window.digitalData.pageFilter) { 11736 const payload = { 11737 link: { 11738 linkName: title, 11739 oneTagLinkName: 'banner-clicked' 11740 }, 11741 pageFilter: structuredClone(digitalData.pageFilter), 11742 page: structuredClone(digitalData.pageDetails) 11743 } 11744 payload.pageFilter.recommendationType = recommendationType; 11745 payload.pageFilter.placement = placement; 11746 payload.pageFilter.mediaType = mediaType; 11747 payload.page.bnrName = `${bannerTitle}|no-value`; 11748 11749 this.pushEvent( 11750 this.EVENTS.LINK_CLICK, payload 11751 ); 11752 digitalData.pageFilter.bannerType = ''; 11753 if (digitalData.pageFilter.isMerchBanner) { 11754 delete digitalData.pageFilter.isMerchBanner; 11755 } 11756 } 11757 } 11758 11759 /** 11760 * Tracking for medium banner 11761 */ 11762 this.setMediumBannerTracking = function (bannerClicked) { 11763 const selectors = { 11764 currentBanner: '.nextgen-banner-unit, .nextgen-banner-unit-proxy, .banner-unit-medium', 11765 titleElement: '.banner-unit-medium__text-plate__title p', 11766 wrapperElement: '.nextgen-abs-carousel-wrapper, .banner-unit-medium', 11767 bannerSource: 'nextgen-banner-unit-wc' 11768 }; 11769 11770 const bannerClassMappings = { 11771 'nextgen-banner-unit-proxy': '.nextgen-abs-carousel-wrapper', 11772 'nextgen-banner-unit': '.nextgen-abs-carousel-wrapper' 11773 } 11774 11775 // get banner title 11776 const currentBannerTitle = AB.DATALAYER.getBannerClickedTitle(bannerClicked, selectors, 'mediumBanner'); 11777 11778 // get banner placement 11779 const bannerPlacement = AB.DATALAYER.getBannerPlacement(bannerClicked, selectors, bannerClassMappings); 11780 11781 // get recommendation type 11782 const currentBanner = bannerClicked?.closest(selectors.currentBanner); 11783 const recommendationType = AB.DATALAYER.getBannerRecommendationType(selectors, currentBanner); 11784 11785 if (window.isAnalyticsMarketplacePage) { 11786 AB.DATALAYER.analyticsCTATracking(`cta:${AB.DATALAYER.initPathSections(true).join(':')}|link|view-all-from-banner`) 11787 } 11788 11789 AB.DATALAYER.trackAEMZone('banner', currentBanner, `banner:${currentBannerTitle}`); 11790 11791 // send link click event 11792 AB.DATALAYER.setBannerClickedEvent(bannerPlacement, `mediumBannerClicked|${currentBannerTitle}`, recommendationType, 'medium-banner', currentBannerTitle); 11793 } 11794 11795 this.setMerchFlexBannerTracking = function (bannerClicked) { 11796 const selectors = { 11797 currentBanner: '.flex-medium-fw', 11798 titleElement: '.flex-medium-fw__text-plate__heading', 11799 wrapperElement: 'search-grid-merch-banner', 11800 }; 11801 11802 // get banner title 11803 const currentBannerTitle = AB.DATALAYER.getBannerClickedTitle(bannerClicked, selectors, 'merch-baner'); 11804 11805 // get banner placement 11806 const bannerPlacement = AB.DATALAYER.getBannerPlacement(bannerClicked, selectors, null, 'merch-banner'); 11807 11808 // get recommendation type 11809 const currentBanner = bannerClicked?.closest(selectors.currentBanner) || bannerClicked?.closest('webgen-flex-module'); 11810 let bannerId = ''; 11811 let recommendationType = ''; 11812 const bannerData = currentBanner?.closest(selectors.wrapperElement)?.querySelector('webgen-flex-module')?.getAttribute('data-flex-module-content') 11813 || currentBanner?.getAttribute?.('data-flex-module-content'); 11814 11815 if (bannerData && typeof bannerData === 'string') { 11816 const parsedBannerData = AB.DATALAYER.safeJSONParse(bannerData); 11817 bannerId = parsedBannerData.promoId ? `|${parsedBannerData.promoId}` : ''; 11818 recommendationType = parsedBannerData.program && parsedBannerData.program !== 'abs' ? 'sponsored' : 'notRecommended'; 11819 } 11820 digitalData.pageFilter = digitalData.pageFilter || {}; 11821 digitalData.pageFilter.isMerchBanner = true; 11822 11823 AB.DATALAYER.trackAEMZone('merch-banner', currentBanner, currentBannerTitle); 11824 11825 // send link click event 11826 AB.DATALAYER.setBannerClickedEvent(bannerPlacement, `bannerclicked|${currentBannerTitle}${bannerId}`, recommendationType, 'medium-banner', currentBannerTitle); 11827 } 11828 11829 /** 11830 * Tracking for banner unit 11831 */ 11832 this.setBannerUnitTracking = function (bannerClicked) { 11833 const selectors = { 11834 currentBanner: '.banner-unit-proxy, .nextgen-banner-unit-proxy, .as-hero__item, promo-banner, .merch-targeted-promo-banner, .banner-unit-hero', 11835 titleElement: '.banner-unit-regular__headline p, .banner-unit-hero__text-plate__title, .title, .mod-unit__headline p, .banner-unit-regular__headline .h2, .banner__title p', 11836 wrapperElement: '.abs-carousel-wrapper, .nextgen-abs-carousel-wrapper, .as-hero__carousel, .banner-unit-hero', 11837 bannerSource: 'banner-unit-regular-wc, banner-unit-hero-wc, promo-banner-regular-wc' 11838 }; 11839 const bannerClassMappings = { 11840 'banner-unit-proxy': '.abs-carousel-wrapper', 11841 'nextgen-banner-unit-proxy': '.nextgen-abs-carousel-wrapper', 11842 'as-hero__item': '.as-hero__carousel', 11843 }; 11844 11845 // get banner title 11846 const currentBannerTitle = AB.DATALAYER.getBannerClickedTitle(bannerClicked, selectors); 11847 11848 // get banner placement 11849 const bannerPlacement = AB.DATALAYER.getBannerPlacement(bannerClicked, selectors, bannerClassMappings); 11850 11851 // get recommendation type 11852 const currentBanner = bannerClicked?.closest(selectors.currentBanner); 11853 let recommendationType = 'notRecommended'; 11854 if (currentBanner?.classList.contains('banner-unit-proxy') || currentBanner?.classList.contains('nextgen-banner-unit-proxy') || currentBanner?.classList.contains('merch-targeted-promo-banner')) { 11855 recommendationType = AB.DATALAYER.getBannerRecommendationType(selectors, currentBanner); 11856 } else if (currentBanner?.classList.contains('as-hero__item')) { 11857 recommendationType = AB.DATALAYER.getBannerRecommendationType(selectors, currentBanner, 'hero'); 11858 } 11859 if (bannerClicked?.closest('.banner-unit-hero') || currentBanner?.classList.contains('as-hero__item')) { 11860 digitalData.pageFilter.bannerType = 'hero'; 11861 } 11862 AB.DATALAYER.trackAEMZone('banner', currentBanner, `${digitalData.pageFilter?.bannerType === 'hero' ? `${AB.DATALAYER.isHomePage() ? 'home-hero' : 'hero'}` : 'large'}-banner:${currentBannerTitle}`); 11863 11864 // Removed restriction to homepage as per request to send metricA across the website 11865 const heroBannerContent = currentBanner?.querySelector(selectors.bannerSource)?.getAttribute('data-hero-banner-content'); 11866 const heroBannerContentObj = AB.DATALAYER.safeJSONParse(heroBannerContent); 11867 if (heroBannerContentObj?.p13nAdobeMetrics) { 11868 const abTestDetails = heroBannerContentObj.p13nAdobeMetrics?.metricA || 'generic'; 11869 localStorage.setItem('heroBannerAbTestDetails', abTestDetails); 11870 } 11871 11872 11873 // send link click event 11874 AB.DATALAYER.setBannerClickedEvent(bannerPlacement, `bannerClicked|${currentBannerTitle}`, recommendationType, 'banner', currentBannerTitle); 11875 } 11876 11877 /** 11878 * Tracking for sidebar banner 11879 */ 11880 this.setSidebarBannerTracking = function (bannerClicked) { 11881 const selectors = { 11882 currentBanner: '.nextgen-banner-unit-proxy', 11883 titleElement: '.banner-unit-sidebar__card__content__headline p', 11884 bannerSource: 'nextgen-banner-unit-sidebar' 11885 }; 11886 11887 // get banner title 11888 const currentBannerTitle = AB.DATALAYER.getBannerClickedTitle(bannerClicked, selectors); 11889 11890 // get banner placement 11891 const bannerPlacement = AB.DATALAYER.getBannerPlacement(bannerClicked, selectors); 11892 11893 // get recommendation type 11894 const currentBanner = bannerClicked?.closest(selectors.currentBanner); 11895 const recommendationType = AB.DATALAYER.getBannerRecommendationType(selectors, currentBanner); 11896 11897 // send link click event 11898 AB.DATALAYER.setBannerClickedEvent(bannerPlacement, `sidebarBannerClicked|${currentBannerTitle}`, recommendationType, 'sidebar-banner', currentBannerTitle); 11899 } 11900 11901 /** 11902 * Tracking for ultra skinny banner 11903 */ 11904 this.setUltraSkinnyBannerTracking = function (bannerClicked) { 11905 const selectors = { 11906 currentBanner: '.nextgen-banner-unit-proxy', 11907 titleElement: '.banner-unit-ultra-skinny__text-plate__title', 11908 bannerSource: 'nextgen-banner-unit-ultra-skinny' 11909 }; 11910 11911 // get banner title 11912 const currentBannerTitle = AB.DATALAYER.getBannerClickedTitle(bannerClicked, selectors); 11913 11914 // get banner placement 11915 const bannerPlacement = AB.DATALAYER.getBannerPlacement(bannerClicked, selectors); 11916 11917 // get recommendation type 11918 const currentBanner = bannerClicked?.closest(selectors.currentBanner); 11919 const recommendationType = AB.DATALAYER.getBannerRecommendationType(selectors, currentBanner); 11920 11921 this.trackAEMZone('banner', currentBanner, `skinny-banner:${currentBannerTitle}`); 11922 11923 // send link click event 11924 AB.DATALAYER.setBannerClickedEvent(bannerPlacement, `ultraSkinnyBannerClicked|${currentBannerTitle}`, recommendationType, 'ultra-skinny-banner', currentBannerTitle); 11925 } 11926 11927 this.setHeroCanvasTracking = function (bannerClicked) { 11928 const selectors = { 11929 currentBanner: '.hero-canvas', 11930 titleElement: '.hero-canvas__text-plate-wrapper, .hero-canvas__text-plate-title, .hero-canvas__text-plate__title', 11931 wrapperElement: '.abs-carousel' 11932 }; 11933 11934 const currentBanner = bannerClicked?.closest(selectors.currentBanner); 11935 // get banner title 11936 const currentBannerTitle = AB.DATALAYER.getBannerClickedTitle(bannerClicked, selectors); 11937 11938 const currentCarousel = bannerClicked.closest(selectors.wrapperElement); 11939 const currentCarouselBanners = currentCarousel?.querySelectorAll('.slick-slide:not(.slick-cloned) .hero-canvas'); 11940 const bannerRow = AB.DATALAYER.getAllBanners() && currentCarousel ? AB.DATALAYER.getAllBanners().indexOf(currentCarousel) + 1 : 0; 11941 const bannerSlot = currentCarouselBanners && currentBanner ? Array.from(currentCarouselBanners).indexOf(currentBanner) + 1 : 0; 11942 const bannerPlacement = `R${bannerRow < 10 ? `0${bannerRow}` : bannerRow}#S${bannerSlot < 10 ? `0${bannerSlot}` : bannerSlot}`; 11943 11944 // get recommendation type 11945 const recommendationType = AB.DATALAYER.getBannerRecommendationType(selectors, currentBanner); 11946 11947 this.trackAEMZone('banner', currentBanner, `hero-canvas:${currentBannerTitle}`); 11948 11949 // send link click event 11950 AB.DATALAYER.setBannerClickedEvent(bannerPlacement, `bannerclicked|hero-canvas:${currentBannerTitle}`, recommendationType, 'hero-canvas', currentBannerTitle); 11951 } 11952 11953 this.setHeroCardsTracking = function (bannerClicked) { 11954 const selectors = { 11955 currentBanner: '.hero-flex__card', 11956 titleElement: '.hero-flex-full-bleed__text-box__title', 11957 localWrapper: '.hero-canvas-row__flex', 11958 wrapperElement: '#herocanvasflex' 11959 }; 11960 11961 // get banner title 11962 const currentBannerTitle = AB.DATALAYER.getBannerClickedTitle(bannerClicked, selectors); 11963 11964 // get recommendation type 11965 const currentBanner = bannerClicked?.closest(selectors.currentBanner); 11966 const allCards = currentBanner.closest(selectors.localWrapper)?.querySelectorAll(selectors.currentBanner); 11967 const currentCardSlot = allCards && currentBanner ? Array.from(allCards)?.filter(card => AB.DATALAYER.isVisible(card))?.indexOf(currentBanner) + 1 : 0; 11968 11969 // get banner placement 11970 const bannerPlacement = `R01#S${currentCardSlot < 10 ? `0${currentCardSlot}` : currentCardSlot}`; 11971 11972 const recommendationType = AB.DATALAYER.getBannerRecommendationType(selectors, currentBanner); 11973 const renderedFlexElement = currentBanner?.closest?.('.hero-flex') || currentBanner?.closest?.(selectors.localWrapper) || currentBanner; 11974 const carouselProxy = renderedFlexElement?.closest?.('.nextgen-abs-carousel-proxy'); 11975 const allVisibleFlexRows = document.querySelectorAll('.nextgen-abs-carousel-proxy .flex-medium-fw, .nextgen-abs-carousel-proxy .flex-container, .nextgen-abs-carousel-proxy .hero-flex')?.length > 0 11976 ? Array.from(document.querySelectorAll('.nextgen-abs-carousel-proxy .flex-medium-fw, .nextgen-abs-carousel-proxy .flex-container, .nextgen-abs-carousel-proxy .hero-flex')) 11977 : []; 11978 const flexIndex = allVisibleFlexRows.indexOf(renderedFlexElement) + 1 || 1; 11979 const zoneInfo = AB.DATALAYER.getZoneInfo(renderedFlexElement)?.zone; 11980 const zone = this.normalizeFlexBannerZone(zoneInfo ? `zone${zoneInfo}` : ''); 11981 const spotIndex = currentCardSlot > 0 ? currentCardSlot : 1; 11982 const spot = `sp${this.padFlexBannerAnalyticsIndex(spotIndex)}`; 11983 const cardTitle = this.getFlexBannerAnalyticsValue(currentBannerTitle); 11984 const carouselTitle = this.getFlexBannerAnalyticsValue( 11985 carouselProxy?.querySelector?.('h2.flex-title')?.textContent 11986 || renderedFlexElement?.closest?.('.hero-canvas-row')?.querySelector?.('h2.flex-title')?.textContent 11987 || renderedFlexElement?.getAttribute?.('aria-label') 11988 ); 11989 const uniqueId = this.getFlexBannerAnalyticsValue( 11990 currentBanner?.getAttribute?.('data-gam-slot-id') 11991 || currentBanner?.getAttribute?.('data-target') 11992 ); 11993 const bannerCardCarousel = `${zone}|flex|flex-${this.padFlexBannerAnalyticsIndex(flexIndex)}:${cardTitle}:${carouselTitle}|${spot}|${uniqueId}`; 11994 11995 digitalData.pageFilter.bannerCardCarousel = bannerCardCarousel; 11996 digitalData.pageDetails.feature = 'flex-banner|shop-ex|501655|423334'; 11997 11998 this.trackAEMZone('banner', currentBanner, `hero-card${currentCardSlot}
11998:${currentBannerTitle}`); 11999 12000 // send link click event 12001 AB.DATALAYER.setBannerClickedEvent(bannerPlacement, `bannerclicked|hero-card${currentCardSlot}:${currentBannerTitle}`, recommendationType, `hero-card${currentCardSlot}`, currentBannerTitle); 12002 digitalData.pageFilter.bannerCardCarousel = ''; 12003 digitalData.pageDetails.feature = ''; 12004 } 12005 12006 /** 12007 * Tracking for sponsored banners 12008 */ 12009 this.setBannerUnitTrackingGAM = function (eventInfo) { 12010 let data = eventInfo.data ? eventInfo.data : eventInfo; 12011 let element = eventInfo?.target; 12012 let bannerTitle = data.bannerTitle ? data.bannerTitle.trim().toLowerCase().replace(/[^a-z0-9|-]/gi, '-') : ''; 12013 12014 // Get only banner title for oneTag tracking 12015 const bnrArr = bannerTitle.split('|'); 12016 const oneTagBnrTitle = bnrArr.length === 2 ? bnrArr[1] : bannerTitle; 12017 12018 if (data) { 12019 /** this part is not functional, as we cannot get the element from an iframe, element var is always window object - GAM banners are not currently in scope for AEM zone tracking 12020 * so there is no need to spend more time on this solution at the moment - to be revisited if the GAM banners are loaded in AEM zone 12021 * */ 12022 if (element && element instanceof Element) { 12023 const jsCurrentBanner = element.closest('.banner-unit-proxy, .nextgen-banner-unit-proxy, .as-hero__item'); 12024 this.trackAEMZone('banner', jsCurrentBanner, `large-banner:${bannerTitle}`); 12025 } 12026 12027 if (this.isCouponsAndDealsPage() && data.bannerType?.includes('medium-banner')) { 12028 data.bannerType = `advert-${data.bannerType}`; 12029 this.setAnalyticsCrossPageData({ action: 'set', name: 'analyticsFeatureTracking', data: 'mediumbannerACIP397009', isAcrossPages: false }); 12030 } 12031 12032 // send link click event 12033 AB.DATALAYER.setBannerClickedEvent(data.bannerPlacement, bannerTitle, data.recommendationType, data.bannerType, oneTagBnrTitle); 12034 } 12035 } 12036 12037 /** 12038 * Returns placement for sponsored banners 12039 */ 12040 this.getBannerPlacementForGAM = function (data) { 12041 const selectors = { 12042 currentBanner: '.banner-unit-proxy, .nextgen-banner-unit-proxy, .as-hero__item, promo-banner, .hero-flex__card, .hero-canvas', 12043 titleElement: '.banner-unit-regular__headline p, .banner-unit-hero__text-plate__title, .title, .mod-unit__headline p', 12044 wrapperElement: '.abs-carousel-wrapper, .nextgen-abs-carousel-wrapper, .as-hero__carousel, #herocanvasflex, .abs-carousel', 12045 bannerSource: '.banner-unit-regular-wc, .banner-unit-hero-wc' 12046 }; 12047 const bannerClassMappings = { 12048 'banner-unit-proxy': '.abs-carousel-wrapper', 12049 'nextgen-banner-unit-proxy': '.nextgen-abs-carousel-wrapper', 12050 'as-hero__item': '.as-hero__carousel', 12051 }; 12052 // get banner placement 12053 const currentBanner = data?.closest(selectors.currentBanner); 12054 let bannerPlacement = ''; 12055 12056 try { 12057 if (currentBanner?.classList?.contains('hero-flex__card')) { 12058 const allCards = currentBanner.closest(selectors.localWrapper)?.querySelectorAll(selectors.currentBanner); 12059 const currentCardSlot = allCards && currentBanner ? Array.from(allCards)?.filter(card => AB.DATALAYER.isVisible(card))?.indexOf(currentBanner) + 1 : 0; 12060 bannerPlacement = `R01#S${currentCardSlot < 10 ? `0${currentCardSlot}` : currentCardSlot}`; 12061 } else if (currentBanner?.classList?.contains('hero-canvas') && data) { 12062 const currentCarousel = data.closest(selectors.wrapperElement); 12063 const currentCarouselBanners = currentCarousel?.querySelectorAll('.slick-slide:not(.slick-cloned) .hero-canvas'); 12064 const bannerRow = AB.DATALAYER.getAllBanners() && currentCarousel ? AB.DATALAYER.getAllBanners().indexOf(currentCarousel) + 1 : 0; 12065 const bannerSlot = currentCarouselBanners && currentBanner ? Array.from(currentCarouselBanners).indexOf(currentBanner) + 1 : 0; 12066 bannerPlacement = `R${bannerRow < 10 ? `0${bannerRow}` : bannerRow}#S${bannerSlot < 10 ? `0${bannerSlot}` : bannerSlot}`; 12067 } else { 12068 bannerPlacement = AB.DATALAYER.getBannerPlacement(data, selectors, bannerClassMappings); 12069 } 12070 } catch (e) { 12071 console.warn('GAM banner placement error: ', e); 12072 } 12073 return bannerPlacement; 12074 } 12075 12076 /** 12077 * Sends impressions event for GAM banners, eventInfo is received from GAM handler functions which anal
12077ytics team doesn't own/update 12078 * @param {*} eventInfo 12079 */ 12080 this.setBannerImpressionsGAM = function (eventInfo) { 12081 let data = eventInfo.data ? eventInfo.data : eventInfo; 12082 let bannerTitle = data.bannerTitle ? AB.DATALAYER.sanitizeAnalyticsText(data?.bannerTitle) : ''; 12083 12084 // Get only banner title for oneTag tracking 12085 const bnrArr = bannerTitle.split('|'); 12086 const oneTagBnrTitle = bnrArr.length === 2 ? bnrArr[1] : bannerTitle; 12087 12088 if (data) { 12089 // Set GAM impression data 12090 const pageDetails = { ...digitalData.pageDetails }; 12091 const pageFilter = { ...digitalData.pageFilter }; 12092 const user = { ...digitalData.user }; 12093 pageDetails.bnrName = oneTagBnrTitle; 12094 pageFilter.mediaType = data.bannerType; 12095 pageFilter.placement = data.bannerPlacement; 12096 pageFilter.recommendationType = data.recommendationType; 12097 user.gamAdData = { 12098 adUnitPath: data.adUnitPath, 12099 lineItemId: data.lineItemId, 12100 creativeId: data.creativeId, 12101 creativeTemplateId: data.creativeTemplateId, 12102 responseStatus: data.responseStatus, 12103 uniqueID: data.uniqueID || 'no-value', 12104 responseTime: data.responseTime, 12105 targetingMap: data.targetingMap 12106 }; 12107 12108 AB.DATALAYER.pushEvent( 12109 AB.DATALAYER.EVENTS.LINK_CLICK, { 12110 link: { 12111 linkName: `gam-banner-impressions`, 12112 isSendToSS: true 12113 }, 12114 page: pageDetails, 12115 pageFilter: pageFilter, 12116 user: user 12117 } 12118 ); 12119 digitalData.pageFilter.placement = ''; 12120 digitalData.pageFilter.mediaType = ''; 12121 digitalData.pageFilter.recommendationType = ''; 12122 digitalData.pageDetails.bnrName = ''; 12123 } 12124 } 12125 12126 /** 12127 * Sends exposure event for GAM banners, eventInfo is received from GAM handler functions which analytics team doesn't own/update 12128 * @param {*} eventInfo 12129 */ 12130 this.setExperimentExposureGAM = function (eventInfo) { 12131 let data = eventInfo.data ? eventInfo.data : eventInfo; 12132 let bannerTitle = data.bannerTitle ? AB.DATALAYER.sanitizeAnalyticsText(data?.bannerTitle) : ''; 12133 12134 // Get only banner title for oneTag tracking 12135 const bnrArr = bannerTitle.split('|'); 12136 const oneTagBnrTitle = bnrArr.length === 2 ? bnrArr[1] : bannerTitle; 12137 12138 if (data) { 12139 // Set GAM impression data 12140 const pageDetails = { ...digitalData.pageDetails }; 12141 const pageFilter = { ...digitalData.pageFilter }; 12142 const user = { ...digitalData.user }; 12143 pageDetails.bnrName = oneTagBnrTitle; 12144 pageFilter.mediaType = data.bannerType; 12145 pageFilter.placement = data.bannerPlacement; 12146 pageFilter.recommendationType = data.recommendationType; 12147 user.gamAdData = { 12148 adUnitPath: data.adUnitPath, 12149 lineItemId: data.lineItemId, 12150 creativeId: data.creativeId, 12151 creativeTemplateId: data.creativeTemplateId, 12152 responseStatus: data.responseStatus, 12153 uniqueID: data.uniqueID || 'no-value', 12154 responseTime: data.responseTime, 12155 targetingMap: data.targetingMap 12156 }; 12157 12158 AB.DATALAYER.pushEvent( 12159 AB.DATALAYER.EVENTS.LINK_CLICK, { 12160 link: { 12161 linkName: `gam-banner-exposure`, 12162 isSendToSS: true 12163 }, 12164 page: pageDetails, 12165 pageFilter: pageFilter, 12166 user: user 12167 } 12168 ); 12169 digitalData.pageFilter.placement = ''; 12170 digitalData.pageFilter.mediaType = ''; 12171 digitalData.pageDetails.bnrName = ''; 12172 digitalData.pageFilter.recommendationType = ''; 12173 } 12174 } 12175 12176 /** 12177 * Tracking for mod units 12178 * @param bannerClicked 12179 */ 12180 this.setModUnitTracking = function (banner) { 12181 12182 const bannerClicked = banner instanceof jQuery ? banner[0] : banner; 12183 let isAEMZoneBanner = false;
12184 let isBirthdayBanner = false; 12185 const selectors = { 12186 currentBanner: '.mod-unit-unified-proxy, .mod-unit-skinny-proxy', 12187 titleElement: '.mod-unit__headline, .mod-unit-skinny__headline', 12188 wrapperElement: '.mod-unit-carousel-container' 12189 }; 12190 const bannerClassMappings = { 12191 'mod-unit-unified-proxy': '.mod-unit-carousel-container', 12192 'mod-unit-skinny-proxy': '.mod-unit-carousel-container' 12193 }; 12194 const currentBanner = bannerClicked?.closest(selectors.currentBanner); 12195 12196 // get banner title 12197 let bannerLinkName = ''; 12198 let currentBannerTitle = AB.DATALAYER.getBannerClickedTitle(bannerClicked, selectors); 12199 12200 if (currentBanner.classList.contains('mod-unit-unified-proxy')) { 12201 if (!currentBannerTitle && (currentBanner.parentElement?.querySelectorAll('h3').length > 0 || currentBanner.parentElement?.querySelectorAll('p b').length > 0)) { 12202 currentBannerTitle = currentBanner.parentElement?.querySelector('h3')?.textContent?.trim()?.replace(/\s/g, '') !== '' 12203 ? currentBanner.parentElement?.querySelector('h3').textContent?.trim()?.replace(/\s/g, '') 12204 : currentBanner.parentElement?.querySelector('p b')?.textContent?.trim()?.replace(/\s/g, ''); 12205 isAEMZoneBanner = true; 12206 } 12207 bannerLinkName = `bannerClicked|${currentBannerTitle}`; 12208 12209 } else if (currentBanner.classList.contains('.mod-unit-skinny-proxy')) { 12210 bannerLinkName = `skinnyBannerClicked|${currentBannerTitle}`; 12211 } 12212 // set custom title for birthday banner 12213 if (currentBannerTitle.toLowerCase() === 'abirthdaytreatonus') { 12214 isBirthdayBanner = true; 12215 bannerLinkName = 'B4U:birthday-banner'; 12216 } 12217 12218 // get banner placement 12219 const bannerPlacement = AB.DATALAYER.getBannerPlacement(bannerClicked, selectors, bannerClassMappings); 12220 12221 //get media type 12222 const mediaType = isAEMZoneBanner 12223 ? 'medium-banner' 12224 : currentBanner.classList.contains('mod-unit-unified-proxy') 12225 ? 'banner' 12226 : currentBanner.classList.contains('mod-unit-skinny-proxy') 12227 ? 'skinny-banner' 12228 : ''; 12229 12230 this.trackAEMZone('banner', currentBanner, `large-banner:${currentBannerTitle}`); 12231 12232 // send link click event 12233 AB.DATALAYER.setBannerClickedEvent(bannerPlacement, bannerLinkName, 'notRecommended', mediaType, isBirthdayBanner ? 'birthday-banner' : currentBannerTitle); 12234 } 12235 12236 /** 12237 * Sets Episodic events 12238 * @param linkClicked 12239 * @param triggerPageLoad 12240 */ 12241 this.episodicOptInEvents = function (linkClicked, triggerPageLoad) { 12242 AB.DATALAYER.setLinkClickEvents(linkClicked); 12243 if (triggerPageLoad && digitalData && digitalData.pageDetails) { 12244 digitalData.pageDetails.subSection3 = 'verify-contact-info'; 12245 AB.DATALAYER.setPageLoad(); 12246 } 12247 } 12248 12249 /** 12250 * Tracks clicks on the sign in button on the OTP sign in flow for the e-community Sincerely-Food 12251 */ 12252 this.setLinkClickSignInEcom = function (source) { 12253 if (window.location.hostname.indexOf('sincerely-food') != -1) { 12254 if (source === 'otp') { 12255 AB.DATALAYER.setLinkClickEvents('passwordless-sign-in|click'); 12256 } else if (source === 'sign-in') { 12257 AB.DATALAYER.setLinkClickEvents('ecommunity|signed-in'); 12258 } else if (source === 'create-account') { 12259 AB.DATALAYER.setLinkClickEvents('ecommunity|created-account'); 12260 } 12261 } else { 12262 if (this.isSubscriptionDeliveryPage()) { 12263 localStorage.setItem('preventAnalyticsLanding', true); 12264 } 12265 } 12266 } 12267 12268 /** 12269 * Sets subsection3 on Episodic pages 12270 */ 12271 this.initEpisodicOptInPage = function () { 12272 if (digitalData && digitalData.pageDetails && digitalData.pageDetails.subSection1 === 'flavoradventure') { 12273 if (AB.DATALAYER.analyticsGetURLParameterByName('validate') === 'true') { 12274 digitalData.pageDetails.subSection3 = 'verify-contact-info'; 12275 } else { 12276 digitalData.pageDetails.subSection3 = ''; 12277 } 12278 } 12279 } 12280 12281 this.isProductInAnalyticsCart = function (productAddedId) { 12282 let existingCart = []; 12283 if (window.location.pathname.startsWith('/schedule-and-save/manage')) { 12284 localStorage.getItem('analyticsOneTimeOrderItems') 12285 existingCart = AB.DATALAYER.getLSItem('analyticsOneTimeOrderItems') ? AB.DATALAYER.safeJSONParse(AB.DATALAYER.getLSItem('analyticsOneTimeOrderItems')) : []; 12286 } else { 12287 let carts = AB.DATALAYER.safeJSONParse(AB.DATALAYER.getLSItem('abCart'))?.cart; 12288 if (carts && Object.keys(carts)?.length > 0) { 12289 Object.values(carts)?.forEach(cart => { 12290 if (cart.cartItems) {
12291 cart.cartItems.forEach(item => { 12292 existingCart.push(item); 12293 }); 12294 } 12295 }); 12296 } else { 12297 existingCart = AB.DATALAYER.safeJSONParse(AB.DATALAYER.getLSItem('abCart'))?.cartItems ? AB.DATALAYER.safeJSONParse(AB.DATALAYER.getLSItem('abCart')).cartItems : []; 12298 } 12299 } 12300 let isProductInCart = false; 12301 existingCart.forEach(item => { 12302 let productID = item.itemId || item.id || item.productBpn || item.productID; 12303 if (Number(productID) === Number(productAddedId)) { 12304 isProductInCart = true; 12305 } 12306 }); 12307 if (isProductInCart) { 12308 localStorage.setItem('itemsInCartBeforeUpdate', JSON.stringify(existingCart)); 12309 } 12310 return isProductInCart; 12311 } 12312 12313 /** 12314 * Sets banner, channel, subSections and pageName for Sincerely,Food 12315 */ 12316 this.initSCFPage = function () { 12317 if (digitalData && digitalData.pageDetails && digitalData.pageDetails.banner === 'sincerely-food') { 12318 12319 // get banner from SincerelyFoodBanner cookie 12320 const sincerelyFoodBannerCookie = AB.DATALAYER.getCookie('SincerelyFoodBanner'); 12321 if (sincerelyFoodBannerCookie) { 12322 const sincerelyFoodBanner = AB.DATALAYER.safeJSONParse(sincerelyFoodBannerCookie); 12323 if (sincerelyFoodBanner && sincerelyFoodBanner.banner) { 12324 digitalData.pageDetails.banner = sincerelyFoodBanner.banner; 12325 } 12326 } 12327 12328 switch (digitalData.pageDetails.subSection1) { 12329 case 'home': 12330 digitalData.pageDetails.subSection1 = 'scf-main'; 12331 digitalData.pageDetails.subSection2 = 'fulfillment'; 12332 break; 12333 case 'registration': 12334 digitalData.pageDetails.subSection1 = 'scf-create-account'; 12335 digitalData.pageDetails.subSection2 = 'scf-short-registration'; 12336 break; 12337 case 'sign-in': 12338 digitalData.pageDetails.subSection1 = 'scf-account'; 12339 digitalData.pageDetails.subSection2 = 'scf-sign-in'; 12340 break; 12341 default: 12342 break; 12343 } 12344 12345 digitalData.pageDetails.channel = 'e-community'; 12346 digitalData.pageDetails.pageName = `${digitalData.pageDetails.banner}:${digitalData.pageDetails.channel}:${digitalData.pageDetails.subSection1}:${digitalData.pageDetails.subSection2}`; 12347 } 12348 } 12349 12350 /** 12351 * Tracks the marketplace sellers' info on search-results page 12352 * @param sellerCount 12353 * @param sellerResultsString 12354 * @param pgmResponse 12355 */ 12356 this.marketplaceSellerUpdates = function (sellerCount, sellerResultsString, pgmResponse) { 12357 digitalData.search = digitalData.search || {}; 12358 if (pgmResponse) { 12359 const mkpData = pgmResponse?.find((data) => data?.pgmName?.includes('mkp')); 12360 const mkpResponse = mkpData?.response ? mkpData?.response : {}; 12361 const pgmDocs = mkpResponse?.docs || []; 12362 const sellers = {}; 12363 let sellersCount = 0; 12364 let sellerCountResults = []; 12365 pgmDocs.forEach(product => { 12366 const sellerId = product.sellerId || 'sellerIdNotFound' 12367 if (sellers[sellerId]) { 12368 sellers[sellerId]++; 12369 } else { 12370 sellersCount++; 12371 sellers[sellerId] = 1 12372 } 12373 }); 12374 for (const key in sellers) { 12375 if (sellers.hasOwnProperty(key)) { 12376 sellerCountResults.push(`${key}:${sellers[key]}`); 12377 } 12378 } 12379 sellerCount = sellersCount; 12380 sellerResultsString = sellerCountResults.join(); 12381 digitalData.search.mkpSearchResultsCount = mkpResponse?.numFound ? mkpResponse?.numFound.toString() : '0'; 12382 } 12383 if (sellerCount && sellerCount !== 0) { 12384 digitalData.search.sellerCount = sellerCount ? sellerCount.toString() : '0';
12385 digitalData.search.sellerCountResults = sellerResultsString ? sellerResultsString : 'no-value:0'; 12386 } 12387 } 12388 12389 /** 12390 * Tracks the number of products on wineshop carousel and the number of recipes on recipe carousel on search-results page 12391 * @param wineResultsReturn 12392 * @param recipeResults 12393 */ 12394 this.searchResultsCarouselsProductCount = function (wineResultsReturn, recipeResults, programType) { 12395 if (programType !== 'mkp-carousel') { 12396 digitalData.search = digitalData.search || {}; 12397 digitalData.search.wineResultsReturn = digitalData.search.wineResultsReturn || '0'; 12398 digitalData.search.recipeResults = digitalData.search.recipeResults || '0'; 12399 12400 if (wineResultsReturn) { 12401 digitalData.search.wineResultsReturn = wineResultsReturn.toString(); 12402 } 12403 if (recipeResults) { 12404 digitalData.search.recipeResults = recipeResults.toString(); 12405 } 12406 } 12407 } 12408 12409 /** 12410 * Capture the carousel, total items added (not unique) and the total value of the products added on DFTA page 12411 * expected output: prodctCarouselValue = [CAROUSEL]|[TOTAL PRODUCT COUNT]|[VALUE OF PRODUCTS ADDED] 12412 */ 12413 this.setCarouselValueDFTA = function (eventName, productInfo) { 12414 if (this.isDFTAPage() && productInfo) { 12415 12416 const currentProductID = productInfo.productID; 12417 const currentProductCarousel = document.querySelector(`a[data-bpn="${currentProductID}"]`)?.closest('.slick-product-carousel'); 12418 const currentTitle = currentProductCarousel?.previousElementSibling?.textContent?.trim()?.replace(/\s/g, '-'); 12419 12420 if (currentTitle) { 12421 let carouselValueArray = []; 12422 let updatedCarouselValueArray = []; 12423 12424 const listPrice = Number(productInfo.listPrice); 12425 const previousUnits = Number(productInfo.previousQty) || 0; 12426 const currentLbs = (productInfo.unitType == 'LB' && productInfo.lbs !== '0') ? (productInfo.lbs === '' ? 0 : Number(productInfo.lbs)) : 0; 12427 const currentUnits = productInfo.units ? Number(productInfo.units) : currentLbs; 12428 12429 let units = 1; 12430 if (previousUnits > currentUnits) { 12431 units = previousUnits - currentUnits; 12432 } else if (previousUnits < currentUnits) { 12433 units = currentUnits - previousUnits; 12434 } else { 12435 // for 'remove_cart' productinfo.units is not updated with the new value(0)? 12436 units = previousUnits; 12437 } 12438 const totalPrice = listPrice * units; 12439 12440 const carouselName = currentTitle.replace(' - ', ' ').replace(/\s/g, '-'); 12441 this.setCarouselName(`shop|${currentTitle}`); 12442 digitalData.productInfo.carouselSection = this.sanitizeAnalyticsText(currentTitle); 12443 12444 AB.DATALAYER.pfmFromCurrentPage = `${digitalData.pageDetails.subSection1}:${digitalData.pageDetails.subSection2}:${digitalData.pageDetails.subSection3}`; 12445 12446 if (localStorage.getItem('analyticsDFTACarouselValue')) { 12447 const carouselValueLS = localStorage.getItem('analyticsDFTACarouselValue'); 12448 carouselValueArray = carouselValueLS.split(','); 12449 } 12450 12451 // we already have one ore more products added 12452 if (carouselValueArray.length > 0) { 12453 12454 let found = false; 12455 12456 carouselValueArray.forEach((carouselInfo) => { 12457 12458 let carousel = carouselInfo.split('|')[0]; 12459 let totalProductCount = Number(carouselInfo.split('|')[1]); 12460 let totalValue = Number(carouselInfo.split('|')[2]); 12461 let updatedCarouselInfo = ''; 12462 12463 if (carousel.toLowerCase() === carouselName.toLowerCase()) { 12464 switch (eventName) { 12465 case 'cart_add': 12466 case 'add_quantity': 12467 totalProductCount = totalProductCount + units; 12468 totalValue = (Math.round((totalValue + totalPrice) * 100) / 100).toFixed(2); 12469 updatedCarouselInfo = `${carousel}|${totalProductCount}|${totalValue}`; 12470 updatedCarouselValueArray.push(updatedCarouselInfo); 12471 break; 12472 case 'remove_quantity': 12473 totalProductCount = totalProductCount - units; 12474 totalValue = (Math.round((totalValue - totalPrice) * 100) / 100).toFixed(2); 12475 updatedCarouselInfo = `${carousel}|${totalProductCount}|${totalValue}`; 12476 updatedCarouselValueArray.push(updatedCarouselInfo); 12477 break; 12478 case 'remove_cart': 12479 totalProductCount = totalProductCount - units; 12480 totalValue = (Math.round((totalValue - totalPrice) * 100) / 100).toFixed(2); 12481 updatedCarouselInfo = `${carousel}|${totalProductCount}|${totalValue}`; 12482 if (totalProductCount > 0) { 12483 updatedCarouselValueArray.push(updatedCarouselInfo); 12484 } 12485 break; 12486 default: 12487 break 12488 } 12489 found = true; 12490 } else { 12491 updatedCarouselValueArray.push(carouselInfo); 12492 } 12493 }); 12494 12495 if (!found && eventName === 'cart_add') { 12496 updatedCarouselValueArray.push(`${carouselName}|${units}|${totalPrice.toFixed(2)}`); 12497 } 12498 12499 } else { 12500 // first product added 12501 if (eventName === 'cart_add') { 12502 updatedCarouselValueArray.push(`${carouselName}|${units}|${totalPrice.toFixed(2)}`); 12503 } 12504 } 12505 if (updatedCarouselValueArray.length > 0) { 12506 const updatedCarouselString = updatedCarouselValueArray.filter(
12506Boolean).join(','); 12507 localStorage.setItem('analyticsDFTACarouselValue', updatedCarouselString); 12508 } else { 12509 localStorage.removeItem('analyticsDFTACarouselValue'); 12510 } 12511 } 12512 } 12513 } 12514 12515 this.initDFTACarousel = function () { 12516 if (this.getLSItem('analyticsDFTACarouselValue') && digitalData) { 12517 digitalData.productInfo = digitalData.productInfo || {}; 12518 digitalData.productInfo.productCarouselValue = this.handleGetLSValue('analyticsDFTACarouselValue'); 12519 } 12520 } 12521 12522 this.setPreviousQuantityDFTA = function (productUpdatedID) { 12523 const cartItems = window.digitalData && window.digitalData.cart && window.digitalData.cart.item && window.digitalData.cart.item.length > 0 ? window.digitalData.cart.item : AB.DATALAYER.safeJSONParse(AB.DATALAYER.getLSItem('abCart'))?.cartItems ? AB.DATALAYER.safeJSONParse(AB.DATALAYER.getLSItem('abCart')).cartItems : []; 12524 12525 if (productUpdatedID && cartItems.length > 0) { 12526 cartItems.forEach(item => { 12527 let productID = item.itemId || item.id || item.productID; 12528 if (productID == productUpdatedID) { 12529 digitalData.productInfo.previousQty = item.qty || item.units; 12530 } 12531 }); 12532 } 12533 } 12534 12535 this.setFeaturedFacetsFilter = function () { 12536 if (localStorage.getItem('analyticsFeaturedFacet')) { 12537 const featureSelection = localStorage.getItem('analyticsFeaturedFacet'); 12538 window['AB']['DATALAYER'].addFilterSelection(`featured: ${featureSelection}`); 12539 window['AB']['DATALAYER'].addFilterType('featured'); 12540 window['AB']['DATALAYER'].setPageFilter(); 12541 localStorage.removeItem('analyticsFeaturedFacet'); 12542 } 12543 } 12544 12545 this.initMarketplaceSellerListing = function () { 12546 if (digitalData.pageDetails && (digitalData.pageDetails.subSection1 === 'marketplace' || digitalData.pageDetails.subSection1 === 'm') && digitalData.pageDetails.subSection2 && !digitalData.pageDetails.subSection3) { 12547 if (digitalData.pageDetails.subSection2 === 'seller-listing') { 12548 const marketType = document.querySelector('.mktp-ship-info')?.getAttribute('data-mktp-type'); 12549 digitalData.pageDetails.subSection1 = marketType ? `${marketType.toLowerCase()}-mart` : ''; 12550 } else { 12551 digitalData.pageDetails.subSection1 = digitalData.pageDetails.subSection2; 12552 } 12553 digitalData.pageDetails.subSection1 = 'marketplace-landing'; 12554 digitalData.pageDetails.subSection2 = 'seller-listing'; 12555 digitalData.pageDetails.pageName = `${digitalData.pageDetails.banner}:${digitalData.pageDetails.channel}:${digitalData.pageDetails.subSection1}:${digitalData.pageDetails.subSection2}`; 12556 } 12557 } 12558 12559 this.initPharmacyRx = function (subSection2) { 12560 if (digitalData && digitalData.pageDetails) { 12561 digitalData.pageDetails.subSection1 = 'household'; 12562 digitalData.pageDetails.subSection2 = subSection2; 12563 digitalData.pageDetails.pageName = `${digitalData.pageDetails.banner}:${digitalData.pageDetails.channel}:${digitalData.pageDetails.subSection1}:${digitalData.pageDetails.subSection2}`; 12564 this.setPageLoad(); 12565 this.handleAccountEvents(); 12566 } 12567 } 12568 12569 this.initMarketplacePDP = function () { 12570 if (digitalData.productInfo && digitalData.productInfo.sellerId && digitalData.productInfo.sellerId !== '22222222') { 12571 digitalData.pageDetails.sellerId = digitalData.productInfo.sellerId; 12572 digitalData.pageDetails.channel = 'marketplace'; 12573 digitalData.pageDetails.subSection1 = 'product-details'; 12574 digitalData.pageDetails.subSection2 = ''; 12575 digitalData.pageDetails.pageName = `${digitalData.pageDetails.banner}:${digitalData.pageDetails.channel}:${digitalData.pageDetails.subSection1}`; 12576 } 12577 } 12578 12579 this.initMarketplaceCategory = function () { 12580 let newSubsections = digitalData.pageDetails.pageUrl.split('/'); 12581 let sellerId = localStorage.getItem('sellerId') ? localStorage.getItem('sellerId') : AB.DATALAYER.analyticsGetURLParameterByName('sellerId') ? AB.DATALAYER.analyticsGetURLParameterByName('sellerId') : newSubsections[6] ? newSubsections[6].split('.')[1] : ''; 12582 if (!sellerId && AB.DATALAYER.safeJSONParse(AB.DATALAYER.getLSItem('mkpSeller'))) { 12583 if (AB.DATALAYER.safeJSONParse(AB.DATALAYER.getLSItem('mkpSeller'))?.sellerInfo) { 12584 sellerId = Object.keys(AB.DATALAYER.safeJSONParse(AB.DATALAYER.getLSItem('mkpSeller')).sellerInfo)[0]; 12585 } 12586 } 12587 if (digitalData.pageDetails.subSection2 === 'aisles') { 12588 digitalData.pageDetails.subSection1 = newSubsections[3]; 12589 digitalData.pageDetails.subSection2 = newSubsections[4]; 12590 digitalData.pageDetails.subSection3 = newSubsections[5]; 12591 digitalData.pageDetails.subSection4 = newSubsections[6] ? newSubsections[6].split('.')[0] : ''; 12592 digitalData.pageDetails.pageName = `${digitalData.pageDetails.banner}:${digitalData.pageDetails.channel}:${digitalData.pageDetails.subSection1}${digitalData.pageDetails.subSection2 ? `:${digitalData.pageDetails.subSection2}` : ''}${digitalData.pageDetails.subSection3 ? `:${digitalData.pageDetails.subSection3}` : ''}${digitalData.pageDetails.subSection4 ? `:${digitalData.pageDetails.subSection4}` : ''}`; 12593 } 12594 digitalData.pageDetails.channel = 'marketplace'; 12595 digitalData.pageDetails.sellerId = sellerId; 12596 } 12597 12598 this.initMarketplaceSellerLanding = function () { 12599 let sellerId = ''; 12600 if (document.querySelector('.mktp__seller-info')?.attributes?.getNamedItem('data-seller-id')?.textContent) { 12601 sellerId = document.querySelector('.mktp__seller-info')?.attributes?.getNamedItem('data-seller-id')?.textContent; 12602 } else if (AB.DATALAYER.safeJSONParse(AB.DATALAYER.getLSItem('mkpSeller'))?.sellerInfo) { 12603 sellerId = Object.keys(AB.DATALAYER.safeJSONParse(AB.DATALAYER.getLSItem('mkpSeller')).sellerInfo)[0]; 12604 } 12605 digitalData.pageDetails.subSection1 = 'landing'; 12606 digitalData.pageDetails.subSection2 = digitalData.pageDetails.subSection3 = digitalData.pageDetails.subSection4 = ''; 12607 digitalData.pageDetails.pageName = `${digitalData.pageDetails.banner}:${digitalData.pageDetails.channel}:${digitalData.pageDetails.subSection1}`; 12608 digitalData.pageDetails.channel = 'marketplace'; 12609 digitalData.pageDetails.sellerId = sellerId; 12610 } 12611 12612 this.setTopNavMarketplace = function () { 12613 if (digitalData.pageDetails && digitalData.pageDetails.subSection1 === 'landing') { 12614 this.setTopNavPreviousPage(); 12615 } 12616 } 12617 12618 // To be removed 12619 this.updateCartIdMarketplace = function (allItems, newItem) { 12620 if (((newItem && newItem.sellerName) || (digitalData.productInfo && digitalData.productInfo.sellerName)) && digitalData.cart && digitalData.cart.cartInfo) { 12621 const cartId = digitalData.cart.cartInfo.cartId; 12622 const cartType = digitalData.cart.cartInfo.cartType || ''; 12623 const marketplaceCartTotal = digitalData.cart.transaction?.subTotal; 12624 const marketplaceCartItems = allItems || []; 12625 let marketplaceCartSKUs = [];
12626 marketplaceCartItems.forEach(item => { 12627 marketplaceCartSKUs.push(item.id); 12628 }); 12629 const marketplaceCartSKUsString = marketplaceCartSKUs.join(','); 12630 const marketplaceCartInfo = `${cartId}|${cartType}|${marketplaceCartSKUsString}|${marketplaceCartTotal}`; 12631 12632 const sellerName = newItem?.sellerName || digitalData.productInfo?.sellerName; 12633 if (sellerName.toLowerCase().indexOf('kehe') !== -1) { 12634 digitalData.cart.cartInfo.cartIDkehe = marketplaceCartInfo; 12635 } else { 12636 digitalData.cart.cartInfo.cartIDnaturalcommerce = marketplaceCartInfo; 12637 } 12638 } 12639 } 12640 12641 this.initContactUs = function () { 12642 if (digitalData.pageDetails.subSection1 === 'inquiry' && digitalData.pageDetails.subSection2 === 'contact-us') { 12643 digitalData.pageDetails.subSection1 = 'help'; 12644 digitalData.pageDetails.subSection2 = 'inquiry'; 12645 digitalData.pageDetails.subSection3 = 'contact-us'; 12646 digitalData.pageDetails.channel = 'help-center'; 12647 digitalData.pageDetails.pageName = `${digitalData.pageDetails.banner}:${digitalData.pageDetails.channel}:${digitalData.pageDetails.subSection1}:${digitalData.pageDetails.subSection2}:${digitalData.pageDetails.subSection3}`; 12648 } 12649 } 12650 12651 this.setLinkClickRemoveAll = function (linkClicked, productIdsList) { 12652 digitalData.cart = digitalData.cart || {}; 12653 digitalData.cart.unavailableItemsIds = productIdsList; 12654 AB.DATALAYER.pushEvent( 12655 this.EVENTS.LINK_CLICK, { 12656 link: { 12657 linkName: linkClicked 12658 }, 12659 cart: digitalData.cart, 12660 page: digitalData?.pageDetails, 12661 pageFilter: digitalData?.pageFilter 12662 } 12663 ); 12664 } 12665 12666 this.setWalletLinkClick = function (linkName, key) { 12667 if (AB.DATALAYER.isPaymentPage()) { 12668 AB.DATALAYER.pushEvent( 12669 this.EVENTS.LINK_CLICK, { 12670 link: { 12671 linkName: linkName 12672 }, 12673 page: digitalData?.pageDetails, 12674 pageFilter: digitalData?.pageFilter 12675 } 12676 ); 12677 let newSubsection3 = key === 'addCreditOrDebitCard' ? 'add-card-details' : key === 'addEBTCard' ? 'add-ebt-card-details' : 'add-payment-methods'; 12678 if (digitalData && digitalData.pageDetails) { 12679 digitalData.pageDetails.subSection3 = newSubsection3; 12680 } 12681 } 12682 } 12683 12684 this.setWalletOtherClicks = function (linkName, subSection3, subSection4, topNav, moodalMessage) { 12685 if (AB.DATALAYER.isPaymentPage()) { 12686 if (digitalData && digitalData.pageDetails) { 12687 digitalData.pageDetails.subSection3 = subSection3; 12688 digitalData.pageDetails.subSection4 = subSection4; 12689 } 12690 if (digitalData && digitalData.pageFilter) { 12691 digitalData.pageFilter.topNav = topNav; 12692 } 12693 if (digitalData && digitalData.modal) { 12694 digitalData.modal.modalMessage = moodalMessage; 12695 } 12696 AB.DATALAYER.pushEvent( 12697 this.EVENTS.LINK_CLICK, { 12698 link: { 12699 linkName: linkName 12700 }, 12701 page: digitalData?.pageDetails, 12702 pageFilter: digitalData?.pageFilter, 12703 modal: digitalData?.modal 12704 } 12705 ); 12706 } 12707 } 12708 12709 /** 12710 * PayPal tracking - my account screen 12711 */ 12712 this.payPalLinkClicks = function (linkName, userMessage) { 12713 if (AB.DATALAYER.isPaymentPage() && digitalData?.pageDetails) { 12714 const subSections = [digitalData.pageDetails.subSection1, digitalData.pageDetails.subSection2, digitalData.pageDetails.subSection3, digitalData.pageDetails.subSection4].filter(
12714Boolean).join(':'); 12715 digitalData.modal = digitalData.modal || {}; 12716 digitalData.modal.modalMessage = userMessage ? `toast-message|${userMessage.toLowerCase().replace(/ /g, '-')}` : `cta:${subSections}|button|paypal`; 12717 12718 const eventSent = userMessage ? false : appEventData.some(eventObj => eventObj?.link?.linkName === linkName); 12719 if (!eventSent) { 12720 AB.DATALAYER.setLinkClickEvents(linkName); 12721 } 12722 } 12723 } 12724 12725 12726 this.trackP13nCarousel = function (lazyWrapper, products, carouselAction, p13nData) { 12727 // AMC tracking 12728 if (products && Array.isArray(products) && products.length > 0 && lazyWrapper?.localName === 'product-carousel-al-v1') { 12729 AB.DATALAYER.setAMCImpressionsCarousel(lazyWrapper, products, carouselAction); 12730 } 12731 12732 // PZN tracking 12733 if (products && lazyWrapper && (lazyWrapper.dataset?.carouselDriven === 'featured-products' || lazyWrapper.dataset?.carouselDriven === 'featured-items' || lazyWrapper.dataset?.carouselDriven === 'picked-for-you' || lazyWrapper.dataset?.carouselDriven === 'p13n-generic' || AB.DATALAYER.safeJSONParse(lazyWrapper.closest('targeted-wrapper')?.dataset?.products)?.apiDriven === 'p13n-generic') && !AB.DATALAYER.isDFTAPage() && !AB.DATALAYER.isBIAPage()) { 12734 let currentCarouselParent = lazyWrapper.closest('.master-product-carousel') || lazyWrapper.closest('.slick-product-carousel') || lazyWrapper.querySelector('.carousel-content-wrapper') || lazyWrapper; 12735 let currentCarouselTitle = currentCarouselParent?.querySelector('.carousel-header-text') ? currentCarouselParent.querySelector('.carousel-header-text').textContent : currentCarouselParent?.querySelector('.carousel-nav__header-title') ? currentCarouselParent.querySelector('.carousel-nav__header-title').textContent : 'Picked for You'; 12736 let curatedCarouselTitle = currentCarouselParent?.querySelector('.carousel-header-text') ? currentCarouselParent.querySelector('.carousel-header-text').textContent.trim().toLowerCase().split(' ').join('-').replace(/[^\w-,|#:]/gi, '').replace('--', '-') : currentCarouselParent?.querySelector('.carousel-nav__header-title') ? currentCarouselParent.querySelector('.carousel-nav__header-title').textContent.trim().toLowerCase().split(' ').join('-').replace(/[^\w-,|#:]/gi, '').replace('--', '-') : 'picked-for-you'; 12737 12738 if (curatedCarouselTitle === 'picks-for-you') { 12739 curatedCarouselTitle = 'picked-for-you'; 12740 } 12741 window.slideNumber = window.slideNumber || {}; 12742 if (carouselAction && carouselAction !== 'back') { 12743 setTimeout(function () { 12744 AB.DATALAYER.sendProductImpressionsCarousel(products, p13nData, currentCarouselParent, currentCarouselTitle, curatedCarouselTitle); 12745 }, 200) 12746 } else if (carouselAction && carouselAction === 'back') { 12747 if (window.slideNumber?.hasOwnProperty(curatedCarouselTitle)) { 12748 window.slideNumber[curatedCarouselTitle]--; 12749 } 12750 } 12751 } else if (((AB.DATALAYER.isDFTAPage() && (lazyWrapper?.dataset?.carouselDriven === 'p13n-generic' || AB.DATALAYER.safeJSONParse(lazyWrapper?.closest('targeted-wrapper')?.dataset?.products)?.apiDriven === 'p13n-generic')) || (AB.DATALAYER.isBIAPage() && lazyWrapper?.dataset?.carouselBehaviour === 'bia-spaCarousel')) && p13nData && products) { 12752 12753 const currentCarouselParent = lazyWrapper.closest('.slick-product-carousel'); 12754 const carouselTitle = AB.DATALAYER.isDFTAPage() 12755 ? currentCarouselParent?.previousElementSibling?.textContent?.trim() 12756 : currentCarouselParent?.closest('.bia-spa-carousel-container')?.querySelector('.bia-spa-carousel-container-header')?.textContent?.trim(); 12757 const formattedCarouselTitle = carouselTitle?.toLowerCase().split(' ').join('-').replace(/[^\w-,|#:]/gi, '').replace('--', '-'); 12758 window.slideNumber = window.slideNumber || {}; 12759 12760 if (carouselAction === 'load') { 12761 const options = { 12762 root: null, 12763 rootMargin: '0px', 12764 threshold: 0.5 12765 }; 12766 const observer = new IntersectionObserver((entries, observer) => {
12767 entries.forEach(entry => { 12768 if (entry.isIntersecting) { 12769 AB.DATALAYER.sendProductImpressionsCarousel(products, p13nData, currentCarouselParent, carouselTitle, formattedCarouselTitle); 12770 observer.unobserve(lazyWrapper.children[0]); 12771 } 12772 }); 12773 }, options); 12774 observer.observe(lazyWrapper.children[0]); 12775 } else if (carouselAction === 'forward') { 12776 AB.DATALAYER.sendProductImpressionsCarousel(products, p13nData, currentCarouselParent, carouselTitle, formattedCarouselTitle); 12777 } else if (carouselAction === 'back') { 12778 if (window.slideNumber?.hasOwnProperty(formattedCarouselTitle)) { 12779 window.slideNumber[formattedCarouselTitle]--; 12780 } 12781 } 12782 } else if (lazyWrapper && lazyWrapper.localName === 'meals-carousel' && products && Array.isArray(products) && products.length > 0 && lazyWrapper.closest('targeted-wrapper')?.dataset?.meals && typeof lazyWrapper.closest('targeted-wrapper')?.dataset?.meals === 'string' && AB.DATALAYER.safeJSONParse(lazyWrapper.closest('targeted-wrapper').dataset.meals)?.dataSource === 'p13n') { 12783 const currentCarouselParent = lazyWrapper.closest('.slick-meal-carousel'); 12784 const currentCarouselTitle = currentCarouselParent?.querySelector('.meal-carousel-title')?.textContent?.trim()?.toLowerCase()?.split(' ')?.join('-')?.replace(/[^\w-,|#:]/gi, '')?.replace('--', '-'); 12785 window.slideNumber = window.slideNumber || {}; 12786 if (carouselAction && carouselAction !== 'back') { 12787 setTimeout(function () { 12788 AB.DATALAYER.sendRecipeImpressions(products, currentCarouselParent, currentCarouselTitle); 12789 }, 200) 12790 } else if (carouselAction && carouselAction === 'back') { 12791 if (window.slideNumber.hasOwnProperty(currentCarouselTitle)) { 12792 window.slideNumber[currentCarouselTitle]--; 12793 } 12794 } 12795 } else if (lazyWrapper && lazyWrapper.dataset?.lazyComponent === 'coupon-carousel' && lazyWrapper.dataset?.carouselFilter === 'p13n' && products && Array.isArray(products) && products.length > 0) { 12796 const currentCarouselParent = lazyWrapper.closest('.coupon-carousel'); 12797 const currentCarouselTitle = lazyWrapper.dataset?.carouselTitle?.trim()?.toLowerCase()?.split(' ')?.join('-')?.replace(/[^\w-,|#:]/gi, '')?.replace('--', '-'); 12798 window.slideNumber = window.slideNumber || {}; 12799 if (carouselAction && carouselAction !== 'back') { 12800 setTimeout(function () { 12801 AB.DATALAYER.sendCouponImpressions(products, currentCarouselParent, currentCarouselTitle); 12802 }, 200) 12803 } else if (carouselAction && carouselAction === 'back') { 12804 if (window.slideNumber?.hasOwnProperty(currentCarouselTitle)) { 12805 window.slideNumber[currentCarouselTitle]--; 12806 } 12807 } 12808 } 12809 12810 if (carouselAction === 'load' && lazyWrapper && lazyWrapper.closest) { 12811 var mktTileWrapper = lazyWrapper.closest('[data-mt-includeMarketingTile="true"]'); 12812 if (mktTileWrapper) { 12813 var mktTileJson = mktTileWrapper.getAttribute('data-mt-marketingTileJson'); 12814 var mktTile = mktTileJson && AB.DATALAYER.safeJSONParse(mktTileJson); 12815 if (mktTile) { 12816 var mktCarouselTitle = mktTileWrapper.getAttribute('data-carousel-title') || ''; 12817 if (lazyWrapper.dataset?.lazyComponent === 'coupon-carousel') { 12818 AB.DATALAYER.sendDealsCarouselMarketingCardImpression(mktTile, mktCarouselTitle, lazyWrapper, null); 12819 } else { 12820 AB.DATALAYER.sendMarketingCardImpression(mktTile, mktCarouselTitle, mktTileWrapper); 12821 } 12822 } 12823 } else { 12824 // deals-carousel-wrapper path: marketing tile data lives in data-deals-carousel-content JSON 12825 var dealsContentEl = lazyWrapper.hasAttribute && lazyWrapper.hasAttribute('data-deals-carousel-content') 12826 ? lazyWrapper 12827 : lazyWrapper.closest('[data-deals-carousel-content]'); 12828 if (dealsContentEl) { 12829 var dealsCarouselData = AB.DATALAYER.safeJSONParse(dealsContentEl.getAttribute('data-deals-carousel-content')); 12830 if (dealsCarouselData && dealsCarouselData.includeMarketingTile === 'true' && dealsCarouselData.marketingTile) { 12831 var dealsCarouselTitle = dealsContentEl.getAttribute('data-carousel-title') || dealsCarouselData.title || ''; 12832 AB.DATALAYER.sendDealsCarouselMarketingCardImpression(dealsCarouselData.marketingTile, dealsCarouselTitle, dealsContentEl, null); 12833 } 12834 } 12835 if (!dealsContentEl) { 12836 var _twWrapper = lazyWrapper.closest('targeted-wrapper'); 12837 if (_twWrapper) { 12838 var _twData = AB.DATALAYER.safeJSONParse(_twWrapper.getAttribute('data-products')) || {}; 12839 if (_twData.includeMarketingTile === 'true' && _twData.marketingTile) { 12840 var _twTitle = _twWrapper.getAttribute('data-carousel-title') || _twData.carouselTitle || _twData.title || ''; 12841 AB.DATALAYER.sendMarketingCardImpression(_twData.marketingTile, _twTitle, _twWrapper); 12842 } 12843 } 12844 } 12845 } 12846 } 12847 } 12848 12849 this.trackP13nGrid = function (wrapper, response) { 12850 const products = response?.products || []; 12851 if (products && Array.isArray(products) && products.length > 0 && (wrapper?.parentElement?.dataset?.driven === 'p13n' || wrapper?.closest('dynamic-product-grid-wrapper')?.getAttribute('data-driven') === 'p13n')) { 12852 const gridTitle = wrapper.parentElement?.parentElement?.parentElement?.querySelector('.simple-page-title')?.textContent?.trim() || 'no-value'; 12853 AB.DATALAYER.sendProductImpressionsGrid(products, gridTitle); 12854 AB.DATALAYER.setP13nExperimentExposure(response); 12855 } 12856 } 12857 12858 /** 12859 * Sends the product impressions event 12860 */ 12861 this.sendProductImpressionsCarousel = function (products, p13nData, currentCarouselParent, carouselTitle, formattedCarouselTitle) { 12862 if (!window.slideNumber?.hasOwnProperty(formattedCarouselTitle)) {
12863 window.slideNumber[formattedCarouselTitle] = 1; 12864 } 12865 let carouselVisibleWidth = currentCarouselParent?.offsetWidth * window.slideNumber[formattedCarouselTitle] - 100; 12866 let visibleProductsNotSent = []; 12867 window.analyticsDietPrefSent = window.analyticsDietPrefSent || []; 12868 window.digitalData.dietPrefItems = window.digitalData.dietPrefItems || []; 12869 12870 let impressionAdditionalDetail = typeof p13nData === 'object' && Object.keys(p13nData).length > 0 12871 ? AB.DATALAYER.returnImpressionAdditionalDetail(p13nData) 12872 : ''; 12873 12874 if (p13nData?.experimentData?.experimentGroup && p13nData?.experimentData?.experimentId) { 12875 digitalData.pageDetails.expVariant = `${digitalData.pageDetails.subSection1}#${p13nData.experimentData.experimentGroup.toLowerCase()}#${p13nData.experimentData.experimentId}`; 12876 } 12877 12878 products.forEach(product => { 12879 if (AB.DATALAYER.isAlcoholAdTrackingDisabled(product)) { 12880 return; 12881 } 12882 if (product.name && product.price) { 12883 const isSpImpFixOn = !window?.SWY?.CONFIGSERVICE?.featureToggleConfig?.isSponsoredImpressionFixDisabled; 12884 const placement = isSpImpFixOn 12885 ? (AB.DATALAYER.setCarouselPosition({ productId: product.id, carouselSection: carouselTitle, nativeElement: currentCarouselParent }) || 'R00#S00') 12886 : AB.DATALAYER.setCarouselPosition({ productId: product.id, carouselSection: carouselTitle }); 12887 AB.DATALAYER.pfmFromCurrentPage = AB.DATALAYER.isDFTAPage() ? `${digitalData.pageDetails?.subSection1}:${digitalData.pageDetails?.subSection2}:${digitalData.pageDetails?.subSection3}` : AB.DATALAYER.pfmFromCurrentPage; 12888 const isSponsored = !!(product.adId || product.featured); 12889 const recType = isSponsored ? 'sponsored' : 'recs'; 12890 const rTyp = isSponsored ? 'sponsored' : 'recommended'; 12891 const currentProductPfm = `${AB.DATALAYER.pfmFromCurrentPage}#${formattedCarouselTitle}#${this.isSearchResultsPage() ? digitalData.search?.searchTerm : 'non-search'}#${recType}#${placement}`; 12892 impressionAdditionalDetail = product.adobeMetrics?.metricA ? product.adobeMetrics?.metricA : impressionAdditionalDetail; 12893 const productInfo = { 12894 productID: product.id, 12895 units: isSpImpFixOn 12896 ? AB.DATALAYER.getProductCartUnits(product.id) 12897 : (product.quantity ? product.quantity : product.qty ? product.qty : 0), 12898 listPrice: product.price, 12899 carouselSection: formattedCarouselTitle, 12900 pfmImpression: currentProductPfm, 12901 impressionAdditionalDetail: impressionAdditionalDetail, 12902 isImpTrackEnabled: typeof p13nData === 'object' && p13nData?.isImpTrackEnabled ? p13nData.isImpTrackEnabled : 'no-value', 12903 placement: placement, 12904 display: 'carousel', 12905 badge: AB.DATALAYER.getUIBadge(currentCarouselParent, product.id) 12906 }; 12907 if (isSpImpFixOn) { 12908 productInfo.rTyp = rTyp; 12909 } 12910 const currentProductCard = currentCarouselParent?.querySelector(`a[data-bpn="${product.id}"]`)?.closest('.slick-slide'); 12911 const currentProductPosition = currentProductCard && currentProductCard.offsetLeft ? currentProductCard.offsetLeft : 0; 12912 12913 if (carouselVisibleWidth > currentProductPosition && window.analyticsDietPrefSent.indexOf(product.id) === -1 && currentProductCard) { 12914 visibleProductsNotSent.push(productInfo); 12915 window.analyticsDietPrefSent.push(product.id); 12916 window.digitalData.dietPrefItems.push(productInfo); 12917 } 12918 } 12919 }); 12920 if (visibleProductsNotSent.length > 0) { 12921 if (!window?.SWY?.CONFIGSERVICE?.featureToggleConfig?.isSponsoredImpressionFixDisabled) { 12922 // Drive eVar14 (rTyp) from the first visible product's classification. 12923 const firstRTyp = visibleProductsNotSent[0]?.rTyp; 12924 if (firstRTyp && digitalData?.pageFilter) { 12925 digitalData.pageFilter.recommendationType = firstRTyp; 12926 } 12927 } 12928 AB.DATALAYER.pushEvent( 12929 AB.DATALAYER.EVENTS.LINK_CLICK, { 12930 link: { 12931 linkName: 'product-impressions' 12932 }, 12933 page: digitalData?.pageDetails, 12934 pageFilter: digitalData?.pageFilter, 12935 carousel: visibleProductsNotSent 12936 12937 } 12938 ); 12939 } 12940 if (window.slideNumber) { 12941 window.slideNumber[formattedCarouselTitle]++; 12942 } 12943 } 12944 12945 this.sendProductImpressionsGrid = function (products, gridTitle) { 12946 let visibleProductsNotSent = []; 12947 window.analyticsProductImpressionsSent = window.analyticsProductImpressionsSent || []; 12948
12949 products.forEach(product => { 12950 if (AB.DATALAYER.isAlcoholAdTrackingDisabled(product)) { 12951 return; 12952 } 12953 if (product?.name && product?.price) { 12954 const placement = AB.DATALAYER.setProductGridPosition({ productId: product.id }); 12955 const recType = `${product.adId || product.featured ? '' : 'not-'}recommended`; 12956 digitalData.pageFilter.recommendationType = `${product.adId || product.featured ? '' : 'not'}Recommended`; 12957 const currentProductPfm = `${AB.DATALAYER.pfmFromCurrentPage}#product-grid#non-search#${recType}#${placement}`; 12958 const impressionDetail = product.adobeMetrics?.metricA || 'generic'; 12959 12960 const productInfo = { 12961 productID: product.id, 12962 units: product.quantity ? product.quantity : product.qty ? product.qty : 0, 12963 listPrice: product.price, 12964 carouselSection: gridTitle ? AB.DATALAYER.sanitizeAnalyticsText(gridTitle) : '', 12965 pfmImpression: currentProductPfm, 12966 impressionAdditionalDetail: impressionDetail, 12967 placement: placement, 12968 display: 'grid', 12969 pageNumber: '1' 12970 }; 12971 12972 if (window.analyticsProductImpressionsSent.indexOf(product.id) === -1) { 12973 visibleProductsNotSent.push(productInfo); 12974 window.analyticsProductImpressionsSent.push(product.id); 12975 } 12976 } 12977 }); 12978 if (visibleProductsNotSent.length > 0) { 12979 AB.DATALAYER.pushEvent( 12980 AB.DATALAYER.EVENTS.LINK_CLICK, { 12981 link: { 12982 linkName: 'product-impressions' 12983 }, 12984 page: digitalData?.pageDetails, 12985 pageFilter: digitalData?.pageFilter, 12986 carousel: visibleProductsNotSent 12987 } 12988 ); 12989 } 12990 } 12991 12992 this.sendRecipeImpressions = function (recipes, currentCarouselParent, formattedCarouselTitle) { 12993 if (!window.slideNumber?.hasOwnProperty(formattedCarouselTitle)) { 12994 window.slideNumber[formattedCarouselTitle] = 1; 12995 } 12996 let carouselVisibleWidth = currentCarouselParent.offsetWidth * window.slideNumber[formattedCarouselTitle] - 100; 12997 let visibleRecipesNotSent = []; 12998 window.analyticsRecipesImpressionsSent = window.analyticsRecipesImpressionsSent || []; 12999 window.digitalData.analyticsRecipesImpressionsSent = window.digitalData.analyticsRecipesImpressionsSent || []; 13000 13001 recipes.forEach(recipe => { 13002 if (recipe.id) { 13003 const recipeInfo = { 13004 recipeId: recipe.id, 13005 detail: recipe.adobeMetrics?.metricA || 'no-value' 13006 }; 13007 const currentRecipeCard = currentCarouselParent?.querySelector(`a[id='meal${recipe.id}']`)?.closest('.slick-slide'); 13008 const currentRecipePosition = currentRecipeCard && currentRecipeCard.offsetLeft ? currentRecipeCard.offsetLeft : 0; 13009 13010 if (carouselVisibleWidth > currentRecipePosition && window.analyticsRecipesImpressionsSent.indexOf(recipe.id) === -1 && currentRecipeCard) { 13011 visibleRecipesNotSent.push(recipeInfo); 13012 window.analyticsRecipesImpressionsSent.push(recipe.id); 13013 window.digitalData.analyticsRecipesImpressionsSent.push(recipeInfo); 13014 } 13015 } 13016 }); 13017 if (visibleRecipesNotSent.length > 0) { 13018 AB.DATALAYER.pushEvent( 13019 AB.DATALAYER.EVENTS.LINK_CLICK, { 13020 link: { 13021 linkName: 'recipes-impressions' 13022 }, 13023 page: digitalData?.pageDetails, 13024 pageFilter: digitalData?.pageFilter, 13025 recipeCarouselItems: visibleRecipesNotSent 13026 } 13027 ); 13028 } 13029 if (window.slideNumber) { 13030 window.slideNumber[formattedCarouselTitle]++; 13031 } 13032 } 13033 13034 this.sendCouponImpressions = function (coupons, currentCarouselParent, formattedCarouselTitle, isGrid) { 13035 if (!isGrid && !window.slideNumber?.hasOwnProperty(formattedCarouselTitle)) {
13036 window.slideNumber[formattedCarouselTitle] = 1; 13037 } 13038 let carouselVisibleWidth = !isGrid ? currentCarouselParent.offsetWidth * window.slideNumber[formattedCarouselTitle] - 100 : 0; 13039 let visibleCouponsNotSent = []; 13040 let metricA = []; 13041 let carouselPlacement = ''; 13042 13043 window.analyticsCouponsImpressionsSent = window.analyticsCouponsImpressionsSent || []; 13044 digitalData.analyticsCouponsImpressionsSent = digitalData.analyticsCouponsImpressionsSent || []; 13045 digitalData.couponTrackingInfo = digitalData.couponTrackingInfo || {}; 13046 digitalData.couponTrackingInfo.couponMetricAList = digitalData.couponTrackingInfo.couponMetricAList || []; 13047 13048 coupons.forEach(coupon => { 13049 if (coupon.offerId) { 13050 const currentCouponCard = !isGrid && currentCarouselParent?.querySelector(`button[id='couponAddBtn${coupon.offerId}']`)?.closest('.slick-slide'); 13051 const currentCouponPosition = !isGrid && currentCouponCard && currentCouponCard.offsetLeft ? currentCouponCard.offsetLeft : 0; 13052 if (!carouselPlacement && !isGrid) { 13053 carouselPlacement = AB.DATALAYER.setCouponPlacement(coupon, currentCouponCard, 'deals-impressions-carousel'); 13054 } 13055 13056 if ((isGrid || (carouselVisibleWidth > currentCouponPosition && currentCouponCard)) && window.analyticsCouponsImpressionsSent.indexOf(coupon.offerId) === -1) { 13057 const metricAValue = coupon?.adobeMetrics?.metricA ? `impdeals#${coupon?.adobeMetrics?.metricA}` : 'impdeals#no-value'; 13058 metricA.push(metricAValue); 13059 digitalData.couponTrackingInfo.couponMetricAList[coupon.offerId] = metricAValue; 13060 visibleCouponsNotSent.push(coupon.offerId); 13061 window.analyticsCouponsImpressionsSent.push(coupon.offerId); 13062 digitalData.analyticsCouponsImpressionsSent.push(coupon.offerId); 13063 } 13064 } 13065 }); 13066 if (visibleCouponsNotSent.length > 0) { 13067 const payload = { 13068 link: { 13069 linkName: 'deals-impressions' 13070 }, 13071 page: structuredClone(digitalData?.pageDetails), 13072 pageFilter: structuredClone(digitalData?.pageFilter), 13073 dealsCouponIds: visibleCouponsNotSent.join(',') 13074 } 13075 payload.page.abTestDetails = metricA.join(','); 13076 payload.pageFilter.placement = carouselPlacement; 13077 13078 AB.DATALAYER.pushEvent( 13079 AB.DATALAYER.EVENTS.LINK_CLICK, payload 13080 ); 13081 digitalData.pageFilter.placement = ''; 13082 digitalData.pageFilter.recommendationType = ''; 13083 digitalData.pageFilter.mediaType = ''; 13084 } 13085 if (!isGrid && window.slideNumber) { 13086 window.slideNumber[formattedCarouselTitle]++; 13087 } 13088 } 13089 13090 this.setTopNavCategoriesPage = function () { 13091 if (this.isCategoriesPage()) { 13092 const topNav = digitalData.pageFilter && digitalData.pageFilter.topNav ? digitalData.pageFilter.topNav.toLowerCase().trim() : ''; 13093 if (topNav === 'aisles-view all-home' || topNav === 'aisles-view all-pdp') { 13094 const previousPage = topNav === 'aisles-view all-home' ? 'home' : 'pdp'; 13095 document.querySelectorAll('a.aisle-two')?.forEach(element => { 13096 element.addEventListener('click', () => { 13097 const title = element.querySelector('.product-title')?.textContent; 13098 const aisleName = `aisles-${title}-${previousPage}`; 13099 localStorage.setItem('aislesPreviousPage', aisleName); 13100 }); 13101 }); 13102 document.querySelectorAll('.product-subcats a, a.view-all-subcats')?.forEach(element => { 13103 element.addEventListener('click', () => { 13104 const title = element.textContent; 13105 const aisleName = `aisles-${title}-${previousPage}`; 13106 localStorage.setItem('aislesPreviousPage', aisleName); 13107 }); 13108 }); 13109 } else if (topNav.indexOf('> aisles (l1)') !== -1) { 13110 document.querySelectorAll('a.aisle-two, .product-subcats a, a.view-all-subcats')?.forEach(element => { 13111 element.addEventListener('click', () => { 13112 AB.DATALAYER.setTopNavPreviousPage(); 13113 }); 13114 }); 13115 } 13116 } 13117 } 13118 13119 this.setPfmSourceCategories = function () { 13120 if (this.isAislesL2Page() || this.isAislesL3Page()) { 13121 const pfmSource = this.returnPfmSource(); 13122 if (pfmSource) { 13123 digitalData.pageFilter.pfmSource = pfmSource; 13124 } 13125 } else if (this.isMarketplaceCategoryPage() && this.pfmSourceMktpCategories) { 13126 digitalData.pageFilter.pfmSource = this.pfmSourceMktpCategories; 13127 } 13128 } 13129 13130 this.setPfmSourceCategoriesPDP = function () { 13131 if (this.isAislesL2Page() || this.isAislesL3Page()) { 13132 const pfmSource = this.returnPfmSource(); 13133 if (pfmSource) { 13134 localStorage.setItem('pfmSourceCategories', pfmSource) 13135 } 13136 } 13137 } 13138 13139 this.returnPfmSource = function () { 13140 let pfmSource = ''; 13141 if (digitalData.pageFilter && digitalData.pageFilter.topNav && digitalData.pageDetails) { 13142 const topNav = digitalData.pageFilter.topNav; 13143 if (topNav.indexOf('aisles-') !== -1) { 13144 pfmSource = topNav.toLowerCase().replace(/,/g, ' -'); 13145 } else if (topNav.toLowerCase().indexOf('aisles (l1) >') !== -1 || topNav.toLowerCase().indexOf('> aisles:') !== -1) { 13146 const category = digitalData.pageDetails.subSection3 || digitalData.pageDetails.subSection2; 13147 pfmSource = `aisles-${category}-flyout`; 13148 } 13149 } 13150 return pfmSource; 13151 } 13152 13153 this.initWineShopPDP = function () { 13154 if (digitalData.productInfo && digitalData.productInfo.sellerId === '22222222') { 13155 digitalData.pageDetails.sellerId = '22222222'; 13156 digitalData.pageDetails.channel = 'wine-shop'; 13157 digitalData.pageDetails.subSection1 = 'product-details'; 13158 digitalData.pageDetails.subSection2 = ''; 13159 digitalData.pageDetails.pageName = `${digitalData.pageDetails.banner}:vineandcellar:${digitalData.pageDetails.subSection1}`; 13160 } 13161 } 13162 13163 this.initPetProgramHandlers = function () { 13164 // Handles page loads on Pet Savings and My Pets section dropdowns 13165 let petCardAccordion = document.querySelectorAll('pet-card span[data-toggle="collapse"]')[0]; 13166 let petProfileAccordion = document.querySelectorAll('pet-profile span[data-toggle="collapse"]')[0];
13167 function handleAccordionClick(element) { 13168 if (element.classList.contains('collapsed')) { 13169 if (digitalData.pageDetails) { 13170 digitalData.pageDetails.subSection2 = element.closest('pet-card') ? 'pet-health-savings-card' : element.closest('pet-profile') ? 'my-pets' : ''; 13171 digitalData.pageDetails.pageName = `${digitalData.pageDetails.banner}:loyalty:${digitalData.pageDetails.subSection1}:${digitalData.pageDetails.subSection2}`; 13172 } 13173 AB.DATALAYER.setPageLoad(); 13174 } 13175 } 13176 if (petProfileAccordion) { 13177 petProfileAccordion.addEventListener('click', (event) => { 13178 handleAccordionClick(event.target); 13179 }); 13180 } 13181 if (petCardAccordion) { 13182 petCardAccordion.addEventListener('click', (event) => { 13183 handleAccordionClick(event.target); 13184 }); 13185 } 13186 } 13187 13188 /** 13189 * Sets impressionAdditionalDetail on add to cart 13190 */ 13191 this.setP13nData = function () { 13192 if (digitalData && digitalData.productInfo) { 13193 let currentCarousel; 13194 if (this.isDFTAPage() || this.isBIAPage()) { 13195 currentCarousel = document.querySelector(`a[data-bpn="${digitalData.productInfo.productID}"]`)?.closest('.slick-product-carousel'); 13196 } else { 13197 const carousels = document.querySelectorAll('.slick-product-carousel'); 13198 currentCarousel = Array.from(carousels).filter(carousel => { 13199 return (carousel.querySelector('h2')?.textContent?.trim()?.includes(`${digitalData.productInfo.carouselSectionUncurated}`) 13200 || carousel.querySelector(`lazy-wrapper[data-carousel-title="${digitalData.productInfo.carouselSectionUncurated}"]`)); 13201 })[0]; 13202 } 13203 const p13nCarousel = this.isBIAPage() 13204 ? currentCarousel?.querySelector('[data-carousel-behaviour="bia-spaCarousel"]') 13205 : currentCarousel?.querySelector('[data-carousel-driven="p13n-generic"]'); 13206 if (p13nCarousel) { 13207 const currentDietPrefItem = digitalData.dietPrefItems?.find(item => item.productID === digitalData.productInfo.productID); 13208 const isImpTrackEnabled = this.isDFTAPage() ? currentDietPrefItem?.isImpTrackEnabled : p13nCarousel.getAttribute('data-impression-tracking') ? p13nCarousel.getAttribute('data-impression-tracking') : 'no-value'; 13209 digitalData.productInfo.impressionAdditionalDetail = `${currentDietPrefItem?.impressionAdditionalDetail}#${isImpTrackEnabled}`; 13210 } else if (this.isPdp() && this.p13nData && !digitalData.productInfo.carouselSection) { 13211 digitalData.productInfo.impressionAdditionalDetail = this.p13nData; 13212 } 13213 } 13214 } 13215 13216 this.returnImpressionAdditionalDetail = function (p13nData) { 13217 const modelData = p13nData.modelData; 13218 const experimentData = p13nData.experimentData; 13219 return `${modelData?.modelConfigurationId || 'no-value'}#${modelData?.modelId || 'no-value'}#${modelData?.modelName || 'no-value'}#${experimentData?.experimentId || 'no-value'}#${experimentData?.experimentGroup || 'no-value'}#${experimentData?.eligible?.toString() || 'no-value'}`; 13220 } 13221 13222 /** 13223 * Duplicate of setP13nExperimentExposure, changing function name to better show its functionality 13224 * TO BE REMOVED after moving all function calls to the setP13nExperimentExposure 13225 */ 13226 this.setP13nImpressions = function (response, caller) { 13227 if (response?.p13nAdobeMetrics?.msgExperiment && digitalData?.pageDetails) { 13228 const pageDetails = { ...digitalData.pageDetails }; 13229 if ((caller === 'banner' || caller === 'hero-banner' || caller === 'hero-canvas') && response?.optionalAttributes?.segments) { 13230 pageDetails.bnrName = `web-home-engagement-ladder|${response?.optionalAttributes?.segments}` 13231 digitalData.pageFilter.mediaType = caller === 'hero-banner' ? 'hero-banner' : caller === 'hero-canvas' ? 'hero-canvas' : 'medium-banner'; 13232 caller = 'banner'; 13233 } 13234 const modelId = response.p13nData?.modelData?.modelConfigurationId || response.modelData?.modelConfigurationId; 13235
13236 if (caller === 'deals' && modelId) { 13237 digitalData.couponTrackingInfo = digitalData.couponTrackingInfo || {}; 13238 digitalData.couponTrackingInfo[modelId] = digitalData.couponTrackingInfo[modelId] || {}; 13239 digitalData.couponTrackingInfo[modelId].msgExperiment = response?.p13nAdobeMetrics?.msgExperiment; 13240 digitalData.couponTrackingInfo.msgExperiment = response?.p13nAdobeMetrics?.msgExperiment; 13241 } else if (!caller && modelId) { 13242 digitalData.productTrackingInfo = digitalData.productTrackingInfo || {}; 13243 digitalData.productTrackingInfo[modelId] = digitalData.productTrackingInfo[modelId] || {}; 13244 digitalData.productTrackingInfo[modelId].msgExperiment = response?.p13nAdobeMetrics?.msgExperiment; 13245 if (response.p13nData?.experimentData?.experimentGroup && response.p13nData?.experimentData?.experimentId) { 13246 pageDetails.expVariant = `${pageDetails.subSection1}#${response.p13nData.experimentData.experimentGroup.toLowerCase()}#${response.p13nData.experimentData.experimentId}`; 13247 } 13248 } 13249 pageDetails.abTestDetails = response.p13nAdobeMetrics.msgExperiment; 13250 AB.DATALAYER.pushEvent( 13251 AB.DATALAYER.EVENTS.LINK_CLICK, { 13252 link: { 13253 linkName: `experiment-exposure-loaded${caller ? `-${caller}` : ''}` 13254 }, 13255 page: pageDetails, 13256 pageFilter: digitalData?.pageFilter, 13257 user: digitalData?.user 13258 }); 13259 pageDetails.bnrName = '' 13260 } 13261 } 13262 13263 /** 13264 * This function is used to track a specific set of impressions/exposure for p13n team only when p13nAdobeMetrics.msgExperiment has a value in the API response 13265 */ 13266 this.setP13nExperimentExposure = function (response, caller) { 13267 if (!caller && response?.includeMarketingTile === 'true' && response?.marketingTile && digitalData?.pageDetails) { 13268 var mktCarouselTitle = response.p13nData?.metadata?.carouselTitle || ''; 13269 AB.DATALAYER.sendMarketingCardImpression(response.marketingTile, mktCarouselTitle, null); 13270 } 13271 13272 if (caller === 'deals' && digitalData?.pageDetails) { 13273 var mktDealsCarouselTitle = response.p13nData?.metadata?.carouselTitle || ''; 13274 var mktDealsTileData = response?.marketingTile; 13275 if (response?.includeMarketingTile !== 'true' || !mktDealsTileData) { 13276 var mktDealsDomEl = mktDealsCarouselTitle 13277 ? document.querySelector('[data-carousel-title="' + mktDealsCarouselTitle + '"]') 13278 : document.querySelector('[data-deals-carousel-content]'); 13279 if (mktDealsDomEl) { 13280 var mktDealsDomData = AB.DATALAYER.safeJSONParse(mktDealsDomEl.getAttribute('data-deals-carousel-content')); 13281 if (mktDealsDomData && mktDealsDomData.includeMarketingTile === 'true' && mktDealsDomData.marketingTile) { 13282 mktDealsTileData = mktDealsTileData || mktDealsDomData.marketingTile; 13283 if (!mktDealsCarouselTitle) { 13284 mktDealsCarouselTitle = mktDealsDomEl.getAttribute('data-carousel-title') || mktDealsDomData.title || ''; 13285 } 13286 } 13287 } 13288 } 13289 if (mktDealsTileData) { 13290 var mktDealsCarouselEl = mktDealsCarouselTitle ? document.querySelector('[data-carousel-title="' + mktDealsCarouselTitle + '"]') : null; 13291 // Fallback: data-carousel-title stores raw title; API carouselTitle may differ in case/format â compare sanitized 13292 if (!mktDealsCarouselEl && mktDealsCarouselTitle) { 13293 var _impSanitizedTarget = AB.DATALAYER.sanitizeAnalyticsText(mktDealsCarouselTitle); 13294 var _impDealEls = document.querySelectorAll('[data-deals-carousel-content]'); 13295 for (var _impDi = 0; _impDi < _impDealEls.length; _impDi++) { 13296 var _impEl = _impDealEls[_impDi]; 13297 var _impAttrTitle = _impEl.getAttribute('data-carousel-title') || ''; 13298 var _impDcd = AB.DATALAYER.safeJSONParse(_impEl.getAttribute('data-deals-carousel-content')) || {}; 13299 if ( 13300 AB.DATALAYER.sanitizeAnalyticsText(_impAttrTitle) === _impSanitizedTarget || 13301 AB.DATALAYER.sanitizeAnalyticsText(_impDcd.title || '') === _impSanitizedTarget || 13302 _impDcd.itemName === mktDealsCarouselTitle 13303 ) { 13304 mktDealsCarouselEl = _impEl; 13305 break; 13306 } 13307 } 13308 } 13309 // Only fire from this path when the element is found â if null, initDealsCarouselMktCardTracking handles it 13310 if (mktDealsCarouselEl) { 13311 AB.DATALAYER.sendDealsCarouselMarketingCardImpression(mktDealsTileData, mktDealsCarouselTitle, mktDealsCarouselEl, { 13312 msgExperiment: response.p13nAdobeMetrics?.msgExperiment 13313 }); 13314 } 13315 } 13316 } 13317 13318 if ((response?.p13nAdobeMetrics?.msgExperiment || response?.response?.p13nAdobeMetrics?.msgExperiment || (!response?.p13nAdobeMetrics?.msgExperiment && response?.containerItems?.some(item => item?.p13nAdobeMetrics?.msgExperiment))) && digitalData?.pageDetails) { 13319 const pageDetails = { ...digitalData.pageDetails }; 13320 if ((caller === 'banner' || caller === 'hero-banner' || caller === 'hero-canvas') && response?.optionalAttributes?.segments) { 13321 pageDetails.bnrName = `web-home-engagement-ladder|${response?.optionalAttributes?.segments}` 13322 digitalData.pageFilter.mediaType = caller === 'hero-banner' ? 'hero-banner' : caller === 'hero-canvas' ? 'hero-canvas' : 'medium-banner'; 13323 caller = 'banner'; 13324 } 13325 const modelId = response.p13nData?.modelData?.modelConfigurationId || response.modelData?.modelConfigurationId; 13326
13327 if (caller === 'deals' && modelId) { 13328 digitalData.couponTrackingInfo = digitalData.couponTrackingInfo || {}; 13329 digitalData.couponTrackingInfo[modelId] = digitalData.couponTrackingInfo[modelId] || {}; 13330 digitalData.couponTrackingInfo[modelId].msgExperiment = response?.p13nAdobeMetrics?.msgExperiment; 13331 digitalData.couponTrackingInfo.msgExperiment = response?.p13nAdobeMetrics?.msgExperiment; 13332 } else if (!caller && modelId) { 13333 digitalData.productTrackingInfo = digitalData.productTrackingInfo || {}; 13334 digitalData.productTrackingInfo[modelId] = digitalData.productTrackingInfo[modelId] || {}; 13335 digitalData.productTrackingInfo[modelId].msgExperiment = response?.p13nAdobeMetrics?.msgExperiment; 13336 if (response.p13nData?.experimentData?.experimentGroup && response.p13nData?.experimentData?.experimentId) { 13337 pageDetails.expVariant = `${pageDetails.subSection1}#${response.p13nData.experimentData.experimentGroup.toLowerCase()}#${response.p13nData.experimentData.experimentId}`; 13338 } 13339 } 13340 if (caller === 'bundle' && !response?.p13nAdobeMetrics?.msgExperiment && response?.bannerViewType === 'carousel' && response?.containerItems?.some(item => item?.p13nAdobeMetrics?.msgExperiment)) { 13341 response?.containerItems?.forEach(bundle => { 13342 if (bundle.p13nAdobeMetrics?.msgExperiment) { 13343 AB.DATALAYER.setP13nExperimentExposure(bundle, 'bundle'); 13344 } 13345 }) 13346 return; 13347 } 13348 pageDetails.abTestDetails = response.p13nAdobeMetrics?.msgExperiment || response?.response?.p13nAdobeMetrics?.msgExperiment; 13349 AB.DATALAYER.pushEvent( 13350 AB.DATALAYER.EVENTS.LINK_CLICK, { 13351 link: { 13352 linkName: `experiment-exposure-loaded${caller ? `-${caller}` : ''}` 13353 }, 13354 page: pageDetails, 13355 pageFilter: digitalData?.pageFilter, 13356 user: digitalData?.user 13357 }); 13358 pageDetails.bnrName = '' 13359 13360 if (!caller && response?.includeMarketingTile === 'true' && response?.marketingTile) { 13361 var mktCarouselTitle = response.p13nData?.metadata?.carouselTitle || ''; 13362 AB.DATALAYER.sendMarketingCardExposure(response.marketingTile, mktCarouselTitle, null, { 13363 msgExperiment: response.p13nAdobeMetrics?.msgExperiment 13364 }); 13365 } 13366 13367 if (caller === 'deals') { 13368 var mktDealsExpCarouselTitle = response.p13nData?.metadata?.carouselTitle || ''; 13369 var mktDealsExpTileData = response?.marketingTile; 13370 if (response?.includeMarketingTile !== 'true' || !mktDealsExpTileData) { 13371 var mktDealsExpDomEl = mktDealsExpCarouselTitle 13372 ? document.querySelector('[data-carousel-title="' + mktDealsExpCarouselTitle + '"]') 13373 : document.querySelector('[data-deals-carousel-content]'); 13374 if (mktDealsExpDomEl) { 13375 var mktDealsExpDomData = AB.DATALAYER.safeJSONParse(mktDealsExpDomEl.getAttribute('data-deals-carousel-content')); 13376 if (mktDealsExpDomData && mktDealsExpDomData.includeMarketingTile === 'true' && mktDealsExpDomData.marketingTile) { 13377 mktDealsExpTileData = mktDealsExpTileData || mktDealsExpDomData.marketingTile; 13378 if (!mktDealsExpCarouselTitle) { 13379 mktDealsExpCarouselTitle = mktDealsExpDomEl.getAttribute('data-carousel-title') || mktDealsExpDomData.title || ''; 13380 } 13381 } 13382 } 13383 } 13384 if (mktDealsExpTileData) { 13385 var mktDealsExpCarouselEl = mktDealsExpCarouselTitle ? document.querySelector('[data-carousel-title="' + mktDealsExpCarouselTitle + '"]') : null; 13386 if (!mktDealsExpCarouselEl && mktDealsExpCarouselTitle) { 13387 var _expSanitizedTarget = AB.DATALAYER.sanitizeAnalyticsText(mktDealsExpCarouselTitle); 13388 var _expDealEls = document.querySelectorAll('[data-deals-carousel-content]'); 13389 for (var _expDi = 0; _expDi < _expDealEls.length; _expDi++) { 13390 var _expEl = _expDealEls[_expDi]; 13391 var _expAttrTitle = _expEl.getAttribute('data-carousel-title') || ''; 13392 var _expDcd = AB.DATALAYER.safeJSONParse(_expEl.getAttribute('data-deals-carousel-content')) || {}; 13393 if ( 13394 AB.DATALAYER.sanitizeAnalyticsText(_expAttrTitle) === _expSanitizedTarget || 13395 AB.DATALAYER.sanitizeAnalyticsText(_expDcd.title || '') === _expSanitizedTarget || 13396 _expDcd.itemName === mktDealsExpCarouselTitle 13397 ) { 13398 mktDealsExpCarouselEl = _expEl; 13399 break; 13400 } 13401 } 13402 } 13403 if (mktDealsExpCarouselEl) { 13404 AB.DATALAYER.sendDealsCarouselMarketingCardExposure(mktDealsExpTileData, mktDealsExpCarouselTitle, mktDealsExpCarouselEl, { 13405 msgExperiment: response.p13nAdobeMetrics?.msgExperiment 13406 }); 13407 } 13408 } 13409 } 13410 } 13411 } 13412 13413 this.setP13nHeroBannerImpressions = function (bannerUnitWrapper) { 13414 const getParsedBannerContent = (slide) => { 13415 const heroBannerContent = slide?.querySelector('banner-unit-hero-wc')?.getAttribute('data-hero-banner-content') || slide?.querySelector('webgen-hero-canvas-card')?.getAttribute('data-hero-canvas-content'); 13416 const heroBannerContentObj = AB.DATALAYER.safeJSONParse(heroBannerContent); 13417 return heroBannerContentObj; 13418 } 13419 13420 const selectors = { 13421 currentBanner: '.banner-unit-proxy, .nextgen-banner-unit-proxy, .as-hero__item, promo-banner, .merch-targeted-promo-banner, .hero-canvas', 13422 titleElement: '.banner-unit-regular__headline p, .banner-unit-hero__text-plate__title, .title, .mod-unit__headline p, .banner-unit-regular__headline .h2, .banner__title p', 13423 wrapperElement: '.abs-carousel-wrapper, .nextgen-abs-carousel-wrapper, .as-hero__carousel, .abs-carousel', 13424 bannerSource: 'banner-unit-regular-wc, banner-unit-hero-wc, promo-banner-regular-wc' 13425 }; 13426 const bannerClassMappings = { 13427 'banner-unit-proxy': '.abs-carousel-wrapper', 13428 'nextgen-banner-unit-proxy': '.nextgen-abs-carousel-wrapper', 13429 'as-hero__item': '.as-hero__carousel', 13430 }; 13431 13432 const options = { 13433 root: null, 13434 rootMargin: '0px', 13435 threshold: 0.5 13436 }; 13437 const observer = new IntersectionObserver((entries, self) => {
13438 entries.forEach(entry => { 13439 if (entry?.isIntersecting) { 13440 const slide = entry?.target; 13441 const heroBannerContentObj = getParsedBannerContent(slide); 13442 const currentBanner = slide.querySelector(selectors.currentBanner); 13443 if (heroBannerContentObj?.p13nAdobeMetrics?.metricA) { 13444 const pageDetails = { ...digitalData.pageDetails }; 13445 pageDetails.msgDetail = heroBannerContentObj?.p13nAdobeMetrics?.metricA || 'generic'; 13446 pageDetails.bnrName = heroBannerContentObj?.bannerTitle 13447 && heroBannerContentObj?.campaign 13448 && `${AB.DATALAYER.sanitizeAnalyticsText(heroBannerContentObj.bannerTitle.replace(/[<]\/?[a-z]*[>]/gi, ''))}|${heroBannerContentObj.campaign}`; 13449 const zoneInfo = AB.DATALAYER.getZoneInfo(currentBanner)?.zone || '0'; 13450 digitalData.pageFilter.mediaType = currentBanner?.classList?.contains('hero-canvas') ? 'hero-canvas' : 'hero-banner'; 13451 digitalData.pageFilter.placement = `${AB.DATALAYER.getBannerPlacement(currentBanner, selectors, bannerClassMappings)}#Z${zoneInfo < 10 ? `0${zoneInfo}` : zoneInfo}`; 13452 AB.DATALAYER.pushEvent( 13453 AB.DATALAYER.EVENTS.LINK_CLICK, { 13454 link: { 13455 linkName: 'banner-impressions', 13456 isSendToSS: true 13457 }, 13458 page: pageDetails, 13459 pageFilter: digitalData?.pageFilter, 13460 user: digitalData?.user 13461 } 13462 ); 13463 digitalData.pageFilter.placement = ''; 13464 digitalData.pageFilter.mediaType = ''; 13465 digitalData.pageDetails.bnrName = ''; 13466 digitalData.pageFilter.recommendationType = ''; 13467 self.unobserve(slide); 13468 } 13469 } 13470 }); 13471 }, options); 13472 const heroBannerSlides = bannerUnitWrapper?.[0]?.querySelectorAll('.slick-slide:not(.slick-cloned)'); 13473 heroBannerSlides?.forEach(slide => { 13474 const bannerContentObj = getParsedBannerContent(slide); 13475 if (bannerContentObj?.p13nAdobeMetrics?.metricA) { 13476 observer.observe(slide); 13477 } 13478 }); 13479 } 13480 13481 this.heroBannnerExposureLoaded = function (response, type) { 13482 if (response && Array.isArray(response) && response.length > 0) { 13483 let uniqueItems = []; 13484 response.forEach(item => { 13485 const msgExperiment = item.p13nAdobeMetrics?.msgExperiment; 13486 if (msgExperiment && !uniqueItems.includes(msgExperiment)) { 13487 uniqueItems.push(msgExperiment); 13488 this.setP13nExperimentExposure(item, type === 'hero-canvas' ? 'hero-canvas' : 'hero-banner'); 13489 } 13490 }); 13491 } 13492 } 13493 13494 /** 13495 * Hero Bento carousel experiment-exposure (React component, drop-in replacement 13496 * for hero-canvas). Dedupes by msgExperiment - same rule as heroBannnerExposureLoaded - 13497 * so a given experiment fires at most once even if multiple cards share it. 13498 * cards: Array<{ element: <DOM node inside the slide>, info: { msgExperiment, title, subTitle, promotionId } }> 13499 */ 13500 this.heroBentoExposureLoaded = function (cards) { 13501 if (!Array.isArray(cards) || cards.length === 0) { 13502 return; 13503 } 13504 const uniqueExperiments = []; 13505 cards.forEach(card => { 13506 const msgExperiment = card?.info?.msgExperiment; 13507 if (msgExperiment && !uniqueExperiments.includes(msgExperiment)) { 13508 uniqueExperiments.push(msgExperiment); 13509 AB.DATALAYER.setHeroBentoExposure(card.element, card.info); 13510 } 13511 }); 13512 } 13513 13514 /** 13515 * Fires a single experiment-exposure-loaded-banner event for one Bento card. 13516 * Placement follows the hero-canvas derivation: row is fixed to 01 (Bento is always 13517 * the top hero), slot = card index among non-cloned slick slides, zone via getZoneInfo. 13518 */ 13519 this.setHeroBentoExposure = function (bannerEl, info) { 13520 if (!info?.msgExperiment || !bannerEl || !digitalData?.pageDetails) { 13521 return; 13522 } 13523 const pad = (value) => (Number(value) < 10 ? `0${value}` : `${value}`); 13524 13525 const carousel = bannerEl.closest('.abs-carousel'); 13526 const currentSlide = bannerEl.closest('.slick-slide'); 13527 const allSlides = carousel ? carousel.querySelectorAll('.slick-slide:not(.slick-cloned)') : []; 13528 const bannerSlot = (allSlides.length && currentSlide) ? Array.from(allSlides).indexOf(currentSlide) + 1 : 0; 13529 const zoneInfo = AB.DATALAYER.getZoneInfo(bannerEl)?.zone || '0'; 13530 13531 // bnrName: "<title>-<subTitle>|<promotionId>". Titles from textPlateModule can be 13532 // rich text (e.g. "<p><b>..</b></p>"), so strip HTML tags BEFORE sanitizing - 13533 // otherwise the tag letters leak in ("pb..bp"). Spaces -> "-" via sanitizeAnalyticsText. 13534 // Empty title+subTitle => "no-value"; no promotionId => "no-value". 13535 const stripHtml = (value) => (value || '').replace(/<[^>]*>/g, ''); 13536 const titlePart = [info.title, info.subTitle] 13537 .map(part => AB.DATALAYER.sanitizeAnalyticsText(stripHtml(part))) 13538 .filter(Boolean) 13539 .join('-') || 'no-value'; 13540 const bnrName = `${titlePart}|${info.promotionId || 'no-value'}`; 13541 13542 const pageDetails = { ...digitalData.pageDetails }; 13543 pageDetails.abTestDetails = info.msgExperiment; 13544 pageDetails.bnrName = bnrName; 13545 13546 digitalData.pageFilter.mediaType = 'vertical-hero-banner'; 13547 digitalData.pageFilter.placement = `R01#S${pad(bannerSlot)}#Z${pad(zoneInfo)}`; 13548 13549 AB.DATALAYER.pushEvent( 13550 AB.DATALAYER.EVENTS.LINK_CLICK, { 13551 link: { 13552 linkName: 'experiment-exposure-loaded-banner' 13553 }, 13554 page: pageDetails, 13555 pageFilter: digitalData.pageFilter, 13556 user: digitalData.user 13557 }); 13558 13559 pageDetails.bnrName = ''; 13560 digitalData.pageFilter.placement = ''; 13561 digitalData.pageFilter.mediaType = ''; 13562 } 13563 13564 // Fires the Vertical Hero unit Banner impression (banner-impressions); mirrors the vertical-hero exposure setHeroBentoExposure. 13565 this.setVerticalHeroBannerImpressions = function (bannerEl, info) { 13566 if (!info || !digitalData?.pageDetails) return; 13567 const pad = (value) => (Number(value) < 10 ? `0${value}` : `${value}`); 13568 const stripHtml = (value) => `${value || ''}`.replace(/<[^>]*>/g, ''); 13569 const metricA = info.metricA || 'generic'; 13570 13571 const carousel = bannerEl?.closest?.('.abs-carousel'); 13572 const currentSlide = bannerEl?.closest?.('.slick-slide'); 13573 const allSlides = carousel ? carousel.querySelectorAll('.slick-slide:not(.slick-cloned)') : []; 13574 const domSlot = (allSlides.length && currentSlide) ? Array.from(allSlides).indexOf(currentSlide) + 1 : 0; 13575 const bannerSlot = domSlot || info.slideIndex || 1; 13576 const placementInfo = AB.DATALAYER.getZoneInfo(bannerEl) || {}; 13577 const rowInfo = Number(placementInfo.row) > 0 ? placementInfo.row : 1; 13578 const zoneInfo = placementInfo.zone || '0'; 13579 13580 const normalizedTitle = AB.DATALAYER.sanitizeAnalyticsText(stripHtml(info.title)); 13581 const normalizedSubTitle = AB.DATALAYER.sanitizeAnalyticsText(stripHtml(info.subTitle)); 13582 const titlePart = normalizedTitle || normalizedSubTitle 13583 ? `${normalizedTitle || 'no-value'}-${normalizedSubTitle || 'no-value'}` 13584 : 'no-value'; 13585 13586 const pageDetails = { ...digitalData.pageDetails }; 13587 pageDetails.msgDetail = metricA; 13588 pageDetails.abTestDetails = metricA; 13589 pageDetails.bnrName = `${titlePart}|${info.promotionId || 'no-value'}`; 13590
13591 if (info.variant === 'deals') { 13592 pageDetails.dlsCpnIds = Array.isArray(info.dealsCouponIds) 13593 ? info.dealsCouponIds 13594 .map(couponId => `${couponId ?? ''}`.trim()) 13595 .filter(Boolean) 13596 .join(',') 13597 : ''; 13598 } else { 13599 // dlsCpnIds belongs only to coupon/deals banner impressions. 13600 delete pageDetails.dlsCpnIds; 13601 } 13602 13603 digitalData.pageFilter.mediaType = 'vertical-hero-banner'; 13604 digitalData.pageFilter.placement = `R${pad(rowInfo)}#S${pad(bannerSlot)}#Z${pad(zoneInfo)}`; 13605 13606 const payload = { 13607 link: { 13608 linkName: 'banner-impressions', 13609 isSendToSS: true 13610 }, 13611 page: pageDetails, 13612 pageFilter: digitalData?.pageFilter, 13613 user: digitalData?.user 13614 }; 13615 13616 if (info.variant === 'products') { 13617 payload.carousel = Array.isArray(info.products) ? info.products : []; 13618 } 13619 13620 AB.DATALAYER.pushEvent("banner-impressions", payload); 13621 13622 digitalData.pageFilter.placement = ''; 13623 digitalData.pageFilter.mediaType = ''; 13624 digitalData.pageDetails.bnrName = ''; 13625 digitalData.pageFilter.recommendationType = ''; 13626 } 13627 13628 // Bento Hero Card click tracking. React passes info { contentType, title, subTitle, promotionId, recommendationType, ctaValue, msg }; content prefix is `bento-hero-card`. 13629 13630 // Resolve zone/row/slot + `bento-hero-card:<contentType>:<title>`; bannerEl may be null (falls back to the current/first bento slide). 13631 this.getBentoClickContext = function (bannerEl, info) { 13632 const pad = (v) => (Number(v) < 10 ? `0${v}` : `${v}`); 13633 const stripHtml = (v) => `${v || ''}`.replace(/<[^>]*>/g, ''); 13634 13635 const el = bannerEl 13636 || document.querySelector('.abs-carousel .slick-current .hero-canvas') 13637 || document.querySelector('.abs-carousel .hero-canvas'); 13638 13639 const carousel = el?.closest?.('.abs-carousel'); 13640 const currentSlide = el?.closest?.('.slick-slide'); 13641 const allSlides = carousel ? carousel.querySelectorAll('.slick-slide:not(.slick-cloned)') : []; 13642 const carouselSlot = (allSlides.length && currentSlide) ? Array.from(allSlides).indexOf(currentSlide) + 1 : (info?.slideIndex || 1); 13643 const slot = Number(info?.slot) > 0 ? Number(info.slot) : carouselSlot; 13644 const row = Number(info?.row) > 0 ? Number(info.row) : 1; 13645 13646 const title = [info?.title, info?.subTitle] 13647 .map((p) => AB.DATALAYER.sanitizeAnalyticsText(stripHtml(p))) 13648 .filter(Boolean).join('-') || 'no-value'; 13649 13650 const designType = info?.contentType || 'full-bleed'; 13651 const suppliedRecommendationType = `${info?.recommendationType || ''}`.trim(); 13652 const recommendationType = !suppliedRecommendationType || suppliedRecommendationType === 'no-value' 13653 ? 'not-recommended' 13654 : suppliedRecommendationType; 13655 13656 // Cap content + uniqueId so the assembled value (z|action|content|sp|id) stays within 256 chars. 13657 const V218_MAX = 256; 13658 const V218_WRAPPER_RESERVE = 32; 13659 const uniqueId = (info?.promotionId ? `${info.promotionId}` : 'no-value').slice(0, 64); 13660 const contentBudget = V218_MAX - V218_WRAPPER_RESERVE - uniqueId.length; 13661 const rawContent = `bento-hero-card:${designType}:${title}`; 13662 const content = rawContent.length > contentBudget ? rawContent.slice(0, contentBudget) : rawContent; 13663 13664 return { 13665 element: el, 13666 placement: `R${pad(row)}#S${pad(slot)}`, // V80 row#slot (matches hero-card convention; no zone suffix) 13667 mediaType: `bento-hero-card:${designType}`, // V80 MEDIA TYPE / V218 content prefix 13668 content, // V218 CONTENT value (capped to keep V218 <= 256) 13669 title, 13670 uniqueId, 13671 recommendationType 13672 }; 13673 }; 13674 13675 // L2 (MSG): onetag maps `page.msgDetail`; set before a push, reset after so it doesn't leak into other events. 13676 this.setBentoMsgDetail = function (msg) { 13677 if (!msg || !digitalData?.pageDetails) return; 13678 this._bentoPrevMsgDetail = digitalData.pageDetails.msgDetail; 13679 digitalData.pageDetails.msgDetail = msg; 13680 }; 13681 this.resetBentoMsgDetail = function () { 13682 if (this._bentoPrevMsgDetail !== undefined && digitalData?.pageDetails) { 13683 digitalData.pageDetails.msgDetail = this._bentoPrevMsgDetail; 13684 } 13685 this._bentoPrevMsgDetail = undefined; 13686 }; 13687 13688 // Writes the V218 aemZoneAnalytics string (read on the next page load
13688) with a literal action, mirroring trackAEMZone's z<section>|action|content|sp<row>|id format but without its 'link' -> 'link:<textContent>' rewrite. 13689 this.setBentoAemZone = function (el, action, content, id) { 13690 const zi = AB.DATALAYER.getZoneInfo(el) || {}; 13691 if (!zi.section || zi.section === '' || !zi.row || zi.row === '') return; 13692 const pad = (v) => (Number(v) < 10 ? `0${v}` : `${v}`); 13693 const value = `z${pad(zi.section)}|${action}|${this.sanitizeAnalyticsText(content) || 'no-value'}|sp${pad(zi.row)}|${id || 'no-value'}`; 13694 try { localStorage.setItem('aemZoneAnalytics', value); } catch { /* no-op */ } 13695 }; 13696 13697 this.getBentoV80 = function (c) { 13698 const rec = `${c.recommendationType || ''}`; 13699 if (!rec || rec === 'no-value') return ''; 13700 return `${c.mediaType}#${rec}#${String(c.placement).toLowerCase()}`; 13701 }; 13702 this.setBentoMediaPlacement = function (c, crossPage) { 13703 const v80 = this.getBentoV80(c); 13704 if (!v80) return ''; 13705 if (crossPage) { 13706 try { localStorage.setItem('mediaPlacementClick', v80); } catch (e) { /* no-op */ } 13707 try { localStorage.setItem('bentoHeroMediaPlacement', v80); } catch (e) { /* no-op */ } 13708 } else if (digitalData?.pageFilter) { 13709 digitalData.pageFilter.mediaPlacementClick = v80; 13710 } 13711 return v80; 13712 }; 13713 13714 this.setBentoPdpPfm = function (c, badge, msg) { 13715 const b = this.sanitizeAnalyticsText(badge) || 'no-badge'; 13716 const bentoPdpPfm = `${c.content}#non-search#${this.getBentoPfmRecSegment(c.recommendationType)}#${c.placement}#${b}`; 13717 try { 13718 localStorage.setItem('bentoHeroPdpPfm', bentoPdpPfm); 13719 localStorage.setItem('analyticsMediaType', c.mediaType); 13720 localStorage.setItem('analyticsProductPlacement', c.placement); 13721 if (msg) localStorage.setItem('analyticsProductMsg', msg); 13722 } catch { /* no-op */ } 13723 return bentoPdpPfm; 13724 }; 13725 13726 this.buildBentoV218 = function (action, c) { 13727 const zi = AB.DATALAYER.getZoneInfo(c.element) || {}; 13728 const pad = (v) => (Number(v) < 10 ? `0${Number(v) || 0}` : `${v}`); 13729 const zone = pad(Number(zi.section) > 0 ? Number(zi.section) : 0); 13730 const spot = pad(Number(zi.row) > 0 ? Number(zi.row) : 1); 13731 return `z${zone}|${action}|${c.content}|sp${spot}|${c.uniqueId}`; 13732 }; 13733 13734 this.setBentoBannerClick = function (bannerEl, info) { 13735 if (!info) return; 13736 const c = this.getBentoClickContext(bannerEl, info); 13737 // V218 -> localStorage.aemZoneAnalytics (next page). Built via buildBentoV218 (sp01 fallback) so the 13738 // spot is consistent with cart_add/coupon when the React grid's AEM zone can't be resolved (sp00). 13739 const v218 = this.buildBentoV218('banner', c); 13740 try { localStorage.setItem('aemZoneAnalytics', v218); } catch { /* no-op */ } 13741 this.setBentoMediaPlacement(c, true); // V80 -> localStorage.mediaPlacementClick (next page) 13742 if (info.destinationIsPdp) { 13743 this.setBentoPdpPfm(c, info.badge, info.msg); // main-CTA -> PDP: eVar14/V80/L2 source (AC4 "any design type") 13744 } 13745 this.setBentoMsgDetail(info.msg); // L2 (before push) 13746 const previousV218 = digitalData?.pageFilter?.bannerCardCarousel; 13747 if (digitalData?.pageFilter) digitalData.pageFilter.bannerCardCarousel = v218; 13748 try { 13749 this.setBannerClickedEvent(c.placement, `bannerclicked|${c.content}`, // single immediate banner-clicked with V218 + L2 + media type 13750 c.recommendationType, c.mediaType, c.title); 13751 } finally { 13752 if (digitalData?.pageFilter) digitalData.pageFilter.bannerCardCarousel = previousV218 || ''; 13753 this.resetBentoMsgDetail(); 13754 } 13755 }; 13756 13757 // Text-link CTA (e.g. T&C): V171 only, no V218/V80; subsections from existing page values via initPathSections. 13758 this.setBentoTextLinkClick = function (info) { 13759 const subs = (AB.DATALAYER.initPathSections(true) || []).join(':') || 'no-value'; 13760 const ctaValue = AB.DATALAYER.sanitizeAnalyticsText(info?.ctaValue) || 'no-value'; // self-sanitize (consistent with setBentoDealLineClick) so the emitter is correct regardless of caller
13761 AB.DATALAYER.setLinkClickEvents(`cta:${subs}|link|bento-hero-card:${ctaValue}`); 13762 }; 13763 13764 // Add to cart in banner: fires the canonical cart_add (products string + V218 + L2) via handleItemEvent; call AFTER the cart update succeeds. info.product from the card, info.cart optional. 13765 this.setBentoAddToCart = function (bannerEl, info) { 13766 if (!info || !info.product) return; 13767 const c = this.getBentoClickContext(bannerEl, info); 13768 const v218 = this.buildBentoV218('cart_add', c); 13769 digitalData.pageFilter.bannerCardCarousel = v218; 13770 digitalData._bentoProductContext = { 13771 content: c.content, // bento-hero-card:product-grid:<title> 13772 mediaType: c.mediaType, // bento-hero-card:product-grid 13773 placement: c.placement, 13774 bannerCardCarousel: v218, 13775 mediaPlacementClick: this.getBentoV80(c), // (same-page cart_add rides pageFilter.mediaPlacementClick) 13776 recommendationType: c.recommendationType 13777 }; 13778 this.setBentoMsgDetail(info.msg); // L2 (page.msgDetail) 13779 try { 13780 this.handleItemEvent('cart_add', info.product, undefined, 'bento-hero-card', info.cart); // fires the single cart_add + products string 13781 } finally { 13782 // Always clear so a throw in handleItemEvent can't leak bento context into a later non-bento product action. 13783 delete digitalData._bentoProductContext; 13784 } 13785 if (String(c.recommendationType).includes('sponsored')) { 13786 this.setBannerClickedEvent(c.placement, `bannerclicked|${c.content}`, c.recommendationType, c.mediaType, c.title); 13787 } 13788 this.resetBentoMsgDetail(); 13789 }; 13790 13791 // Maps the bento recommendationType to the eVar14 pfm segment (mirrors createPaPfmBasedOnRecType). 13792 this.getBentoPfmRecSegment = function (recommendationType) { 13793 const rec = `${recommendationType || ''}`; 13794 if (rec.includes('sponsored')) return 'sponsored'; 13795 if (rec === 'recommended') return 'recs'; 13796 return 'not-recommended'; 13797 }; 13798 13799 this.setBentoProductClick = function (bannerEl, info) { 13800 if (!info) return; 13801 const c = this.getBentoClickContext(bannerEl, info); 13802 // V218 -> localStorage.aemZoneAnalytics (next page). buildBentoV218 (sp01 fallback) keeps the spot 13803 // consistent with cart_add/coupon when the React grid's AEM zone can't be resolved (sp00). 13804 try { localStorage.setItem('aemZoneAnalytics', this.buildBentoV218('pdp-card', c)); } catch { /* no-op */ } 13805 this.setBentoMediaPlacement(c, true); 13806 this.setBentoMsgDetail(info.msg); 13807 // eVar14 tail (PDP prefixes the page pfm): <content>#non-search#<rec>#<placement>#<badge>. 13808 this.setBentoPdpPfm(c, info.badge, info.msg); 13809 if (String(c.recommendationType).includes('sponsored')) { 13810 this.setBannerClickedEvent(c.placement, `bannerclicked|${c.content}`, c.recommendationType, c.mediaType, c.title); // immediate banner-clicked 13811 } 13812 this.resetBentoMsgDetail(); 13813 }; 13814 13815 this.setBentoDealLineClick = function (bannerEl, info) { 13816 if (!info) return; 13817 const c = this.getBentoClickContext(bannerEl, info); 13818 const subs = (AB.DATALAYER.initPathSections(true) || []).join(':') || 'no-value'; 13819 this.setBentoMsgDetail(info.msg); // L2 (page.msgDetail) 13820 AB.DATALAYER.setLinkClickEvents( 13821 `cta:${subs}|link|${c.content}:${AB.DATALAYER.sanitizeAnalyticsText(info.ctaValue) || 'no-value'}`); 13822 this.setBentoAemZone(c.element, 'link', `${c.mediaType}:offer details`, c.uniqueId); 13823 this.setBentoMediaPlacement(c, true); 13824 if (String(c.recommendationType).includes('sponsored')) { 13825 this.setBannerClickedEvent(c.placement, `bannerclicked|${c.content}`, c.recommendationType, c.mediaType, c.title); // immediate banner-clicked 13826 } 13827 this.resetBentoMsgDetail(); 13828 }; 13829 13830 this.setBentoCouponClipContext = function (couponEl, info) { 13831 if (!info || !digitalData.pageFilter) return; 13832 const c = this.getBentoClickContext(couponEl, { ...info, contentType: info.contentType || 'deals-grid' }); 13833 digitalData.pageFilter.bentoCarouselTitle = c.content; 13834 const v218 = this.buildBentoV218('coupon_clipped', c); 13835 digitalData._bentoCouponV218 = v218; // consumed by clipCoupon's re-assert (always set now) 13836 digitalData.pageFilter.bannerCardCarousel = v218; 13837 // Flag makes setCouponPlacement skip its no-value overwrite so the bento V218 content survives. 13838 digitalData.pageFilter.bentoCouponV218Set = true; 13839 }; 13840 13841 this.resetBentoCouponClipContext = function () { 13842 if (!digitalData || !digitalData.pageFilter) return; 13843 if (digitalData.pageFilter.bannerCardCarousel === digitalData._bentoCouponV218) { 13844 digitalData.pageFilter.bannerCardCarousel = ''; 13845 } 13846 delete digitalData._bentoCouponV218; 13847 digitalData.pageFilter.bentoCouponV218Set = false;
13848 }; 13849 13850 this.initHeroBannerAbTestDetails = function () { 13851 const heroBannerAbTestDetails = AB.DATALAYER.handleGetLSValue('heroBannerAbTestDetails'); 13852 if (digitalData?.pageDetails && heroBannerAbTestDetails) { 13853 digitalData.pageDetails.abTestDetails = heroBannerAbTestDetails; 13854 } 13855 } 13856 13857 this.initBundleAbTestDetails = function () { 13858 const bundleAbTestDetails = AB.DATALAYER.handleGetLSValue('bundleAbTestDetails'); 13859 if (digitalData?.pageDetails && bundleAbTestDetails) { 13860 digitalData.pageDetails.abTestDetails = bundleAbTestDetails; 13861 } 13862 } 13863 13864 /** 13865 * This function is used to track a specific set of impressions for p13n banner team only when p13nAdobeMetrics.msgExperiment has a value in the API response 13866 */ 13867 this.setP13nEngagementLadderImpressions = function (data, bannerWrapper) { 13868 const bannerInfo = data && data['banners'] && data['banners'][0]; 13869 const metricA = bannerInfo?.p13nAdobeMetrics?.metricA || 'generic'; 13870 const pageDetails = { ...digitalData.pageDetails }; 13871 pageDetails.msgDetail = metricA; 13872 pageDetails.bnrName = bannerInfo?.optionalAttributes?.title 13873 && bannerInfo?.optionalAttributes?.segments 13874 && `${AB.DATALAYER.sanitizeAnalyticsText(bannerInfo.optionalAttributes.title.replace(/[<]\/?[a-z]*[>]/gi, ''))}|${bannerInfo.optionalAttributes.segments}`; 13875 const selectors = { 13876 currentBanner: '.nextgen-banner-unit, .nextgen-banner-unit-proxy', 13877 titleElement: '.banner-unit-medium__text-plate__title p', 13878 wrapperElement: '.nextgen-abs-carousel-wrapper', 13879 bannerSource: 'nextgen-banner-unit-wc' 13880 }; 13881 13882 const bannerClassMappings = { 13883 'nextgen-banner-unit-proxy': '.nextgen-abs-carousel-wrapper', 13884 'nextgen-banner-unit': '.nextgen-abs-carousel-wrapper' 13885 } 13886 const bannerSource = bannerWrapper?.querySelector('nextgen-banner-unit-wc')?.getAttribute('data-banner-data-source'); 13887 const currentBanner = bannerWrapper.closest('.nextgen-banner-unit, .nextgen-banner-unit-proxy'); 13888 const zoneInfo = AB.DATALAYER.getZoneInfo(currentBanner)?.zone || '0'; 13889 digitalData.pageFilter.mediaType = bannerSource && bannerSource == 'MediumBanner' ? 'medium-banner' : ''; 13890 digitalData.pageFilter.placement = `${AB.DATALAYER.getBannerPlacement(currentBanner, selectors, bannerClassMappings)}#Z${zoneInfo < 10 ? `0${zoneInfo}` : zoneInfo}`; 13891 AB.DATALAYER.pushEvent( 13892 AB.DATALAYER.EVENTS.LINK_CLICK, { 13893 link: { 13894 linkName: `banner-impressions`, 13895 isSendToSS: true 13896 }, 13897 page: pageDetails, 13898 pageFilter: digitalData?.pageFilter, 13899 user: digitalData?.user 13900 } 13901 ); 13902 digitalData.pageFilter.placement = ''; 13903 digitalData.pageFilter.mediaType = ''; 13904 digitalData.pageDetails.bnrName = ''; 13905 } 13906 13907 /** 13908 * This function is used to track a specific set of headline impressions for p13n team 13909 */ 13910 this.setP13nHeadlineImpressions = function (data, caller) { 13911 if (data?.dataSource === 'p13n') { 13912 const metricA = data?.p13nAdobeMetrics?.metricA || 'generic'; 13913 const pageDetails = { ...digitalData.pageDetails }; 13914 pageDetails.msgDetail = metricA; 13915 AB.DATALAYER.pushEvent( 13916 AB.DATALAYER.EVENTS.LINK_CLICK, { 13917 link: { 13918 linkName: `${caller}-impressions`, 13919 isSendToSS: true 13920 }, 13921 page: pageDetails, 13922 pageFilter: digitalData?.pageFilter, 13923 user: digitalData?.user 13924 } 13925 ); 13926 } 13927 } 13928 13929 /** 13930 * This function is used to track a specific set of bundles impressions for p13n team 13931 */ 13932 this.setP13nBundleImpressions = function ({ data, caller, wrapper, action } = {}) { 13933 const pageDetails = { ...digitalData.pageDetails }; 13934 const bundlesToSend = []; 13935 window.digitalData.bundlesTrackedIds = window.digitalData.bundlesTrackedIds || []; 13936 const bundleDataContainer = wrapper?.querySelector('webgen-bundles-carousel')?.getAttribute('data-bundles-carousel-content'); 13937 13938 if (data && Object.keys(data)?.length > 0 && caller === 'bundle') { 13939 if (data.p13nAdobeMetrics?.metricA) { 13940 const allSingleBundles = document.querySelectorAll('div:has(>webgen-bundles)') && Array.from(document.querySelectorAll('div:has(>webgen-bundles)'))?.filter(bundle => AB.DATALAYER.isVisible(bundle)); 13941 const bundleRow = wrapper && allSingleBundles ? allSingleBundles.indexOf(wrapper) + 1 : '0'; 13942 const currentBundle = wrapper?.querySelector('.bundles-box'); 13943 const sectionInfo = AB.DATALAYER.getZoneInfo(currentBundle); 13944 const bundlePlacement = `R${bundleRow < 10 ? `0${bundleRow}` : bundleRow}#S01#ZONE${sectionInfo?.zone ? (sectionInfo?.zone < 10 ? `0${sectionInfo?.zone}` : sectionInfo?.zone) : '00'}`; 13945 const bundleInfo = { 13946 section: data.bannerTitle, 13947 detail: data.p13nAdobeMetrics.metricA, 13948 placement: bundlePlacement, 13949 type: 'bundle', 13950 name: AB.DATALAYER.sanitizeAnalyticsText(data.bannerTitle) 13951 } 13952 bundlesToSend.push(bundleInfo); 13953 } 13954 }
13954 else if (caller === 'bundle-carousel' && bundleDataContainer) { 13955 const parsedData = AB.DATALAYER.safeJSONParse(bundleDataContainer); 13956 13957 if (parsedData?.containerItems?.length > 0) { 13958 parsedData?.containerItems.forEach((bundle) => { 13959 if (bundle.p13nAdobeMetrics?.metricA && bundle.bundleSlugId && window.digitalData.bundlesTrackedIds.indexOf(bundle.bundleSlugId) === -1) { 13960 const currentBundle = wrapper?.querySelector(`.bundles-box[data-analytics-id="${bundle.bundleSlugId}"]`); 13961 if (currentBundle?.closest('.slick-active')) { 13962 const allBundleCarousels = document.querySelectorAll('div:has(>webgen-bundles)') && Array.from(document.querySelectorAll('div:has(>webgen-bundles-carousel)'))?.filter(bundle => AB.DATALAYER.isVisible(bundle)); 13963 const allBundles = wrapper?.querySelectorAll('.bundles-box') && Array.from(wrapper?.querySelectorAll('.bundles-box')) || []; 13964 const bundleRow = wrapper && allBundleCarousels ? allBundleCarousels?.indexOf(wrapper) + 1 : '0'; 13965 const bundleSlot = allBundles.indexOf(currentBundle) + 1; 13966 const sectionInfo = AB.DATALAYER.getZoneInfo(currentBundle); 13967 const bundlePlacement = `R${bundleRow < 10 ? `0${bundleRow}` : bundleRow}#S${bundleSlot < 10 ? `0${bundleSlot}` : bundleSlot}#ZONE${sectionInfo?.zone ? (sectionInfo?.zone < 10 ? `0${sectionInfo?.zone}` : sectionInfo?.zone) : '00'}`; 13968 const bundleInfo = { 13969 section: parsedData.headerTitle, 13970 detail: bundle.p13nAdobeMetrics.metricA, 13971 placement: bundlePlacement, 13972 type: 'carousel-bundle', 13973 name: AB.DATALAYER.sanitizeAnalyticsText(bundle.bannerTitle) 13974 } 13975 bundlesToSend.push(bundleInfo); 13976 window.digitalData.bundlesTrackedIds.push(bundle.bundleSlugId); 13977 } 13978 } 13979 }) 13980 } 13981 } 13982 const payload = { 13983 link: { 13984 linkName: 'bundle-impressions', 13985 isSendToSS: true 13986 }, 13987 page: pageDetails, 13988 pageFilter: { ...digitalData?.pageFilter }, 13989 user: { ...digitalData?.user }, 13990 component: bundlesToSend 13991 } 13992 if (bundlesToSend.length > 0) { 13993 AB.DATALAYER.pushEvent(AB.DATALAYER.EVENTS.LINK_CLICK, payload); 13994 } 13995 } 13996 13997 this.setAMCProductsExposed = function (response, carousel, element) { 13998 if (((this.isSearchResultsPage() || this.isAislesPage() || this.isPdp()) && !this.isWineShopPage() && !window.isAnalyticsMarketplacePage) && carousel?.dataDriven === 'featured-products') { 13999 if (response?.products && response?.products?.length > 0) { 14000 let carouselProducts = []; 14001 14002 // get carousel row 14003 const parentCarousel = element?.closest('.slick-product-carousel, .bia-carousel-list-item'); 14004 const visibleCarousels = this.findVisibleElement(document.querySelectorAll('.slick-product-carousel, .bia-carousel-list-item')); 14005 const carouselIndex = visibleCarousels?.indexOf(parentCarousel) + 1; 14006 const carouselRow = carouselIndex < 10 ? `R0${carouselIndex}` : `R${carouselIndex}`; 14007 14008 response.products.forEach((product, index) => { 14009 if (AB.DATALAYER.isAlcoholAdTrackingDisabled(product)) { 14010 return; 14011 } 14012 if (product.adId || product.featured || product.eventTracking) { 14013 const productSlot = (index + 1) < 10 ? `S0${index + 1}` : `S${index + 1}`; 14014 const placement = `${carouselRow}#${productSlot}`; 14015 const currentProductPfm = `${AB.DATALAYER.pfmFromCurrentPage}#${this.sanitizeAnalyticsText(carousel?.title)}#${this.isSearchResultsPage() ? digitalData.search?.internalSearchTerm : 'non-search'}#sponsored#${placement}`; 14016 carouselProducts.push({ 14017 section: carousel?.title ? AB.DATALAYER.sanitizeAnalyticsText(carousel.title) : '', 14018 detail: product?.adobeMetrics?.metricA, 14019 lp: product.price, 14020 pfm: currentProductPfm, 14021 pId: product.id, 14022 units: product.quantity || product.qty || 0, 14023 sponsored: true, 14024 placement: placement, 14025 pName: AB.DATALAYER.isPdp() ? product.name : undefined 14026 }); 14027 } 14028 }); 14029 if (carouselProducts.length > 0) { 14030 let payload = { 14031 link: { 14032 linkName: `${digitalData.pageDetails.subSection1}-product-exposed` 14033 }, 14034 page: digitalData?.pageDetails, 14035 user: digitalData?.user, 14036 pageFilter: digitalData?.pageFilter, 14037 carousel: carouselProducts 14038 }; 14039 if (AB.DATALAYER.isSearchResultsPage()) { 14040 payload.search = digitalData.search; 14041 } 14042 if (AB.DATALAYER.isPdp()) { 14043 payload.productInfo = AB.DATALAYER.safeJSONParse(AB.DATALAYER.getLSItem('analyticsAMCPDPInfo')); 14044 } 14045 payload.pageFilter.mediaType = 'product-carou
14045sel'; 14046 payload.pageFilter.placement = ''; 14047 AB.DATALAYER.pushEvent('product-exposed', payload); 14048 digitalData.pageFilter.mediaType = ''; 14049 } 14050 } 14051 } 14052 } 14053 14054 /** 14055 * Sets LS item for marketplace categories 14056 */ 14057 this.setMktpCategoriesLinkClick = function (category, level) { 14058 if (category) { 14059 if (this.isMarketplaceCategoryPage() && level) { 14060 const aislesPreviousPage = `aisles-${category}-${level}`; 14061 localStorage.setItem('aislesPreviousPage', aislesPreviousPage); 14062 } else if (window.location.pathname.indexOf('/marketplace/landing') !== -1) { 14063 const categoryName = document.querySelector(category)?.querySelector('.marketplace-header__categories-item-text')?.textContent; 14064 const aislesPreviousPage = `aisles-${categoryName}-home`; 14065 localStorage.setItem('aislesPreviousPage', aislesPreviousPage); 14066 } 14067 } 14068 } 14069 14070 /** 14071 * Sets topNav when filter is selected on L1, L2 and L3 marketplace 14072 */ 14073 this.setTopNavMktpFilters = function (filter) { 14074 if (this.isMarketplaceCategoryPage() && window.digitalData.pageDetails) { 14075 if (!window.digitalData.pageDetails.subSection3 && !window.digitalData.pageDetails.subSection4) { 14076 digitalData.pageFilter.topNav = `aisles-${filter}-l1`; 14077 } else if (window.digitalData.pageDetails.subSection3 && !window.digitalData.pageDetails.subSection4) { 14078 digitalData.pageFilter.topNav = `aisles-${filter}-l2`; 14079 } else if (window.digitalData.pageDetails.subSection3 && window.digitalData.pageDetails.subSection4) { 14080 digitalData.pageFilter.topNav = `aisles-${filter}-l3`; 14081 } 14082 } 14083 } 14084 14085 /** 14086 * Sets custom page name and subSections on Games pages 14087 */ 14088 this.initGamesPages = function () { 14089 if (!digitalData.pageDetails.subSection3) { 14090 if (digitalData.pageDetails.subSection1 === 'foru') { 14091 digitalData.pageDetails.subSection2 = 'games-landing'; 14092 } 14093 digitalData.pageDetails.pageName = `${digitalData.pageDetails.banner}:${digitalData.pageDetails.channel}:${digitalData.pageDetails.subSection1}:${digitalData.pageDetails.subSection2}`; 14094 } else if (digitalData.pageDetails.subSection3 === 'game-details' || digitalData.pageDetails.subSection3 === 'prize-details') { 14095 digitalData.pageDetails.subSection2 = 'games'; 14096 const subSection3 = digitalData.pageDetails.subSection3; 14097 const gameName = subSection3 === 'game-details' ? document.querySelector('game-grid')?.getAttribute('data-selected-game') : document.querySelector('prize-details')?.getAttribute('data-game-id'); 14098 digitalData.pageDetails.subSection3 = `${gameName}${subSection3 === 'game-details' ? '-landing' : '-prize-details'}`; 14099 digitalData.pageDetails.pageName = `${digitalData.pageDetails.banner}:${digitalData.pageDetails.channel}:${digitalData.pageDetails.subSection1}:${digitalData.pageDetails.subSection2}:${digitalData.pageDetails.subSection3}`; 14100 } 14101 if (localStorage.getItem('analyticsPlayNow')) { 14102 digitalData.modal = digitalData.modal || {}; 14103 digitalData.modal.modalMessage = localStorage.getItem('analyticsPlayNow'); 14104 localStorage.removeItem('analyticsPlayNow'); 14105 } 14106 } 14107 14108 /** 14109 * Sets modal click and modal view events for Games modals 14110 */ 14111 this.setGamesModalEvents = function (gameName, action, event) { 14112 const eventType = event === 'view' ? this.EVENTS.MODAL_VIEW : this.EVENTS.MODAL_CLICK; 14113 this.pushEvent( 14114 eventType, 14115 { 14116 modal: { 14117 modalAction: `${gameName}:${action}`, 14118 }, 14119 page: digitalData?.pageDetails, 14120 pageFilter: digitalData?.pageFilter 14121 } 14122 ); 14123 } 14124 14125 /** 14126 * Sets game_point_redeemed event when a user redeems game points 14127 */ 14128 this.clipGamePoints = function (prize) { 14129 if (digitalData && prize) { 14130 digitalData.rewards = { 14131 perkPointID: `${prize.programCode}|${prize.prizeId}|${prize.name}`, 14132 perkPointsRedeemed: `${prize.programCode}|${prize.burnPoints}` 14133 } 14134 this.pushEvent( 14135 'perk_point_redeemed', { 14136 rewards: digitalData.rewards, 14137 page: digitalData?.pageDetails, 14138 pageFilter: digitalData?.pageFilter 14139 } 14140 ); 14141 } 14142 } 14143 14144 /** 14145 * Sets top nav when a user clicks on 'start playing' or 'connect to play' to land on the Sign page and clicks on create account 14146 */ 14147 this.createAcountHandler = function () { 14148 if (digitalData.pageFilter && digitalData.pageFilter.topNav && digitalData.pageFilter.topNav.startsWith('foru:games:')) { 14149 document.querySelector('.sign-in-wrapper.sign-in-page .btn-create-account')?.addEventListener('click', () => { 14150 this.setTopNavPreviousPage(); 14151 }); 14152 } 14153 } 14154 14155 this.gamesModalHandler = function (gameName, action) { 14156 document.querySelector('modal-container')?.addEventListener('click', event => { 14157 if (event.target.classList.contains('modal') && !event.target.classList.contains('shown')) { 14158 this.setGamesModalEvents(gameName, action, 'click'); 14159 } 14160 }); 14161 } 14162 14163 this.trackEngagementLadder = function (data, bannerWrapper) { 14164 if (data) { 14165 const bannerInfo = data['banners'] && data['banners'][0] && data['banners'][0]['optionalAttributes']; 14166 const p13nData = data['p13nData'];
14167 const campaign = data['analyticsCampaign']; 14168 if (this.isHomePage()) { 14169 let bannerP13nInfo = ''; 14170 if (p13nData) { 14171 // [MODEL CONFIGURATION ID]#[EXPERIMENT ID]#[EXPERIMENT GROUP]#[ELIGIBLE]#[MODEL ID]#[MODEL VERSION]#[FALL BACK MODEL]#[BANNERID]#[RANK]#[CAMPAIGN] 14172 bannerP13nInfo = `${p13nData?.modelData?.modelConfigurationId || 'no-value'}#${p13nData?.experimentData?.experimentId || 'no-value'}#${p13nData?.experimentData?.experimentGroup || 'no-value'}#${p13nData?.experimentData?.eligible}#${p13nData?.modelData?.modelId || 'no-value'}#${p13nData?.modelData?.modelVersion || 'no-value'}#${p13nData?.modelData?.fallbackModel === false ? 'false' : p13nData?.modelData?.fallbackModel ? p13nData?.modelData.fallbackModel : 'no-value'}#${bannerInfo?.name || 'no-value'}#${p13nData?.recommendationData?.rank || 'no-value'}#${campaign || 'no-value'}`; 14173 } 14174 if (bannerInfo && bannerInfo.segments) { 14175 localStorage.setItem('analyticsEngagementLadder', bannerInfo.segments); 14176 if (bannerP13nInfo) { 14177 localStorage.setItem('analyticsEngagementLadderAbTestInfo', bannerP13nInfo); 14178 } 14179 document.querySelectorAll('.banner-unit-medium__text-plate__cta, .banner-unit-medium__image')?.forEach(element => { 14180 element.addEventListener('click', event => { 14181 const bannerApiSource = event.target.closest('.nextgen-banner-unit-wrapper')?.querySelector('nextgen-banner-unit-wc')?.dataset?.bannerApiSource; 14182 if (bannerApiSource === 'p13nSegmentedApi') { 14183 localStorage.setItem('analyticsEngagementLadderClicked', bannerInfo.segments); 14184 if (bannerP13nInfo) { 14185 localStorage.setItem('analyticsEngagementLadderAbTestInfo', bannerP13nInfo); 14186 } 14187 } 14188 }); 14189 }); 14190 } 14191 const isP13nBanner = bannerWrapper?.closest('.nextgen-banner-unit-wrapper')?.querySelector('nextgen-banner-unit-wc')?.dataset?.bannerApiSource === 'p13nSegmentedApi'; 14192 if (isP13nBanner) { 14193 this.setP13nEngagementLadderImpressions(data, bannerWrapper) 14194 } 14195 } 14196 } 14197 } 14198 14199 this.initEngagementLadderImpressions = function () { 14200 let analyticsEngagementLadder = AB.DATALAYER.handleGetLSValue('analyticsEngagementLadder'); 14201 let analyticsEngagementLadderAbTestInfo = AB.DATALAYER.handleGetLSValue('analyticsEngagementLadderAbTestInfo'); 14202 let analyticsEngagementLadderClicked = AB.DATALAYER.handleGetLSValue('analyticsEngagementLadderClicked'); 14203 14204 if (analyticsEngagementLadder) { 14205 digitalData.pageDetails.engagementLadderImpressions = `web-home-engagement-ladder|${analyticsEngagementLadder}`; 14206 } else if (this.isBIAPage() && digitalData.pageDetails?.engagementLadderImpressions) { 14207 digitalData.pageDetails.engagementLadderImpressions = ''; 14208 } 14209 if (analyticsEngagementLadderAbTestInfo) {
14210 digitalData.pageDetails.abTestDetails = analyticsEngagementLadderAbTestInfo; 14211 } 14212 if (analyticsEngagementLadderClicked) { 14213 digitalData.pageDetails.engagementLadderImpressions = `web-home-engagement-ladder|${analyticsEngagementLadderClicked}`; 14214 digitalData.pageFilter.topNav = `cta:home|engagement-ladder|${analyticsEngagementLadderClicked}`; 14215 } 14216 } 14217 14218 this.initCartPromoBannerImpressions = function () { 14219 if (this.getLSItem('analyticsCartPromoBanner')) { 14220 digitalData.pageFilter.impressions = `${digitalData.pageFilter.impressions ? `${digitalData.pageFilter.impressions},` : ''}${this.handleGetLSValue('analyticsCartPromoBanner')}`; 14221 } 14222 } 14223 14224 14225 this.getFlexBannerAnalyticsValue = function (value, fallback = 'no-value') { 14226 return this.sanitizeAnalyticsText(value?.trim() || fallback) || fallback; 14227 } 14228 14229 this.padFlexBannerAnalyticsIndex = function (value) { 14230 const parsedValue = Number(value); 14231 if (!Number.isFinite(parsedValue)) { 14232 return '01'; 14233 } 14234 return parsedValue < 10 ? `0${parsedValue}` : `${parsedValue}`; 14235 } 14236 14237 this.normalizeFlexBannerZone = function (zone) { 14238 const zoneValue = zone?.trim(); 14239 const zoneNumber = zoneValue?.match(/^zone(\d+)$/i)?.[1]; 14240 14241 return zoneNumber ? `z${this.padFlexBannerAnalyticsIndex(zoneNumber)}` : zoneValue || 'z01'; 14242 } 14243 14244 this.setFlexBannerClickTracking = function (linkElement) { 14245 if (!window.digitalData || !linkElement) { 14246 return false; 14247 } 14248 14249 const clickedElement = linkElement; 14250 const moduleElement = clickedElement?.closest?.('webgen-flex-module'); 14251 const renderedFlexElement = clickedElement?.closest?.('.flex-medium-fw, .flex-container, .hero-flex') || moduleElement?.querySelector?.('.flex-medium-fw, .flex-container, .hero-flex') || moduleElement; 14252 const carouselProxy = renderedFlexElement?.closest?.('.nextgen-abs-carousel-proxy'); 14253 const allVisibleFlexRows = document.querySelectorAll('.nextgen-abs-carousel-proxy .flex-medium-fw, .nextgen-abs-carousel-proxy .flex-container, .nextgen-abs-carousel-proxy .hero-flex')?.length > 0 14254 ? Array.from(document.querySelectorAll('.nextgen-abs-carousel-proxy .flex-medium-fw, .nextgen-abs-carousel-proxy .flex-container, .nextgen-abs-carousel-proxy .hero-flex')) 14255 : []; 14256 const flexIndex = allVisibleFlexRows.indexOf(renderedFlexElement) + 1 || 1; 14257 const currentCard = clickedElement?.closest?.('.flex-card, .hero-flex__card'); 14258 const availableSpots = renderedFlexElement?.querySelectorAll?.('.flex-card, .hero-flex__card')?.length > 0 14259 ? Array.from(renderedFlexElement.querySelectorAll('.flex-card, .hero-flex__card')) 14260 : []; 14261 const derivedSpotIndex = currentCard && availableSpots.length > 0 ? availableSpots.indexOf(currentCard) + 1 : 1; 14262 const spotIndex = derivedSpotIndex > 0 ? derivedSpotIndex : 1; 14263 const parsedFlexData = this.safeJSONParse(moduleElement?.getAttribute?.('data-flex-module-content')) || {}; 14264 const containerItems = Array.isArray(parsedFlexData?.containerItems) ? parsedFlexData.containerItems : []; 14265 const currentItemData = containerItems[spotIndex - 1] || {}; 14266 const zoneInfo = AB.DATALAYER.getZoneInfo(renderedFlexElement)?.zone; 14267 const zone = this.normalizeFlexBannerZone(zoneInfo ? `zone${zoneInfo}` : ''); 14268 const spot = `sp${this.padFlexBannerAnalyticsIndex(spotIndex)}`; 14269 const placement = `r${this.padFlexBannerAnalyticsIndex(flexIndex)}#s${this.padFlexBannerAnalyticsIndex(spotIndex)}`; 14270 const cardTitle = this.getFlexBannerAnalyticsValue( 14271 currentCard?.querySelector?.('.flex-card__title')?.textContent 14272 || renderedFlexElement?.querySelector?.('.flex-medium-fw__text-plate__heading')?.textContent 14273 || currentCard?.querySelector?.('.hero-flex-full-bleed__text-box__title, .hero-flex-color-block__text-box__title')?.textContent 14274 || currentItemData?.title 14275 || parsedFlexData?.title 14276 || parsedFlexData?.bannerTitle 14277 || parsedFlexData?.heading 14278 ); 14279 const carouselTitle = this.getFlexBannerAnalyticsValue( 14280 carouselProxy?.querySelector?.('h2.flex-title')?.textContent 14281 || renderedFlexElement?.closest?.('.hero-canvas-row')?.querySelector?.('h2.flex-title')?.textContent 14282 || renderedFlexElement?.getAttribute?.('aria-label') 14283 || parsedFlexData?.headerText 14284 || parsedFlexData?.containerHeading 14285 || parsedFlexData?.sectionHeading 14286 || parsedFlexData?.name 14287 ); 14288 const uniqueId = this.getFlexBannerAnalyticsValue( 14289 currentItemData?.promotionId 14290 || currentItemData?.promoId 14291 || currentItemData?.id 14292 || currentCard?.getAttribute?.('data-gam-slot-id') 14293 || clickedElement?.getAttribute?.('data-target') 14294 || parsedFlexData?.promotionId 14295 || parsedFlexData?.promoId 14296 || parsedFlexData?.id 14297 ); 14298 const metricA = parsedFlexData?.p13nAdobeMetrics?.metricA 14299 || parsedFlexData?.adobeMetrics?.metricA 14300 || currentItemData?.p13nAdobeMetrics?.metricA 14301 || currentItemData?.adobeMetrics?.metricA 14302 || 'generic'; 14303 const bannerCardCarousel = `${zone}|flex|flex-${this.padFlexBannerAnalyticsIndex(flexIndex)}:${cardTitle}:${carouselTitle}|${spot}|${uniqueId}`; 14304 const isSponsoredFlexBanner = (parsedFlexData?.program && parsedFlexData.program !== 'abs') || (currentItemData?.program && currentItemData.program !== 'abs'); 14305 const recommendationType = isSponsoredFlexBanner 14306 ? 'sponsored'
14307 : 'not-sponsored'; 14308 14309 digitalData.pageFilter = digitalData.pageFilter || {}; 14310 digitalData.pageFilter.bannerCardCarousel = bannerCardCarousel; 14311 digitalData.pageFilter.mediaType = 'flex-sponsored'; 14312 digitalData.pageFilter.recommendationType = recommendationType; 14313 digitalData.pageFilter.placement = placement; 14314 14315 this.pushEvent( 14316 this.EVENTS.LINK_CLICK, 14317 { 14318 link: { 14319 linkName: `bannerclicked|${cardTitle}`, 14320 oneTagLinkName: 'banner-clicked' 14321 }, 14322 pageFilter: structuredClone(digitalData.pageFilter), 14323 page: { 14324 ...structuredClone(digitalData.pageDetails), 14325 feature: 'flex-banner|shop-ex|501655|423334', 14326 bnrName: `${cardTitle}|no-value`, 14327 abTestDetails: metricA 14328 } 14329 } 14330 ); 14331 14332 return true; 14333 }; 14334 14335 this.setFlexCardDetails = function (element) { 14336 const currentParent = element?.closest('.flex-container'); 14337 const flexZones = document.querySelectorAll('.nextgen-abs-carousel-proxy .flex-container')?.length > 0 ? Array.from(document.querySelectorAll('.nextgen-abs-carousel-proxy .flex-container')) : []; 14338 const currentZonePosition = flexZones.indexOf(currentParent) + 1 < 10 ? `0${flexZones.indexOf(currentParent) + 1}` : `${flexZones.indexOf(currentParent) + 1}`; 14339 const availableSpots = currentParent.querySelectorAll('.flex-card').length > 0 ? Array.from(currentParent.querySelectorAll('.flex-card')) : []; 14340 const currentCard = element?.closest('.flex-card'); 14341 const currentSpot = availableSpots.indexOf(currentCard) + 1; 14342 const currentTitle = currentCard?.querySelector('.flex-card__title') ? this.sanitizeAnalyticsText(currentCard.querySelector('.flex-card__title').textContent?.trim()) : 'Error - Cannot find title element'; 14343 const currentParentTitle = this.sanitizeAnalyticsText(element.closest('.nextgen-abs-carousel-proxy')?.querySelector('h2.flex-title')?.textContent?.trim()) || 'no-value'; 14344 const contentType = `flex-card-${currentSpot}:${currentTitle}:${currentParentTitle}`; 14345 const flexPosition = `z${currentZonePosition}|flex|${contentType}|sp${currentSpot < 10 ? `0${currentSpot}` : currentSpot}`; 14346 // localStorage.setItem('flexCardAnalytics', flexPosition); 14347 14348 this.trackAEMZone('flex', element, contentType); 14349 } 14350 14351 this.trackCRSHomeModuleImpression = function ({ element, mediaType, placement, component } = {}) { 14352 if (!AB.DATALAYER.isCESEventsEnabled() || !element || element.dataset?.crsImpressionTracked === 'true' || !AB.DATALAYER.isHomePage()) { 14353 return; 14354 } 14355 14356 AB.DATALAYER.setCRSClickEvent({ 14357 linkName: 'item-click', 14358 mTyp: mediaType, 14359 element: element, 14360 placement: placement, 14361 caasEnbld: AB.DATALAYER.isCRSHomeCaasEnabled(element), 14362 component: component 14363 }); 14364 element.dataset.crsImpressionTracked = 'true'; 14365 } 14366 14367 this.trackFlexModuleImpression = function (moduleElement) { 14368 if (!moduleElement || !digitalData) { 14369 return; 14370 } 14371 14372 const renderedFlexElement = moduleElement.querySelector('.flex-medium-fw, .flex-container') || moduleElement; 14373 if (!renderedFlexElement || renderedFlexElement.dataset?.flexImpressionTracked === 'true') { 14374 return; 14375 } 14376 14377 const carouselProxy = renderedFlexElement.closest('.nextgen-abs-carousel-proxy'); 14378 const allVisibleFlexRows = document.querySelectorAll('.nextgen-abs-carousel-proxy .flex-medium-fw, .nextgen-abs-carousel-proxy .flex-container')?.length > 0 14379 ? Array.from(document.querySelectorAll('.nextgen-abs-carousel-proxy .flex-medium-fw, .nextgen-abs-carousel-proxy .flex-container')) 14380 : []; 14381 14382 const parsedFlexData = this.safeJSONParse(moduleElement.getAttribute('data-flex-module-content')) || {}; 14383 const rowIndex = allVisibleFlexRows.indexOf(renderedFlexElement) + 1;
14384 const row = rowIndex > 0 ? rowIndex : 1; 14385 const sectionTitle = this.sanitizeAnalyticsText( 14386 carouselProxy?.querySelector('h2.flex-title')?.textContent?.trim() 14387 || renderedFlexElement?.querySelector('.flex-medium-fw__text-plate__heading, .flex-title, [class*="heading"]')?.textContent?.trim() 14388 || parsedFlexData?.title 14389 || parsedFlexData?.bannerTitle 14390 || parsedFlexData?.heading 14391 || parsedFlexData?.containerHeading 14392 || parsedFlexData?.sectionHeading 14393 || parsedFlexData?.name 14394 || '' 14395 ) || 'flex-module'; 14396 const containerItems = Array.isArray(parsedFlexData?.containerItems) ? parsedFlexData.containerItems : []; 14397 const type = containerItems.length > 1 ? 'flex-tiles' : 'flex-no-tile'; 14398 const metricA = parsedFlexData?.p13nAdobeMetrics?.metricA 14399 || parsedFlexData?.adobeMetrics?.metricA 14400 || containerItems.find(item => item?.p13nAdobeMetrics?.metricA || item?.adobeMetrics?.metricA)?.p13nAdobeMetrics?.metricA 14401 || containerItems.find(item => item?.p13nAdobeMetrics?.metricA || item?.adobeMetrics?.metricA)?.adobeMetrics?.metricA; 14402 const detail = metricA || 'generic'; 14403 const zoneInfo = AB.DATALAYER.getZoneInfo(renderedFlexElement)?.zone; 14404 const placement = `r${row < 10 ? `0${row}` : row}#s01#z${zoneInfo && zoneInfo > 0 ? (zoneInfo < 10 ? `0${zoneInfo}` : zoneInfo) : '00'}`; 14405 14406 const payload = { 14407 link: { 14408 linkName: 'flex-impressions', 14409 isSendToSS: true 14410 }, 14411 page: { 14412 ...digitalData?.pageDetails, 14413 deviceType: digitalData?.pageDetails?.deviceType 14414 }, 14415 pageFilter: { 14416 ...digitalData?.pageFilter, 14417 mediaType: 'flex-module', 14418 placement: placement 14419 }, 14420 user: digitalData?.user, 14421 component: [{ 14422 section: sectionTitle, 14423 detail: detail, 14424 placement: placement, 14425 type: type, 14426 name: 'flex-module' 14427 }] 14428 }; 14429 14430 AB.DATALAYER.pushEvent(AB.DATALAYER.EVENTS.LINK_CLICK, payload); 14431 if (AB.DATALAYER.isCESEventsEnabled() || AB.DATALAYER.isHomePage()) { 14432 AB.DATALAYER.trackCRSHomeModuleImpression({ 14433 element: renderedFlexElement, 14434 mediaType: 'flex-module', 14435 placement: placement, 14436 component: payload.component 14437 }); 14438 } 14439 14440 renderedFlexElement.dataset.flexImpressionTracked = 'true'; 14441 } 14442 14443 this.trackFlexModuleExposureOnLoad = function (moduleElement) { 14444 if (!moduleElement || !digitalData) { 14445 return; 14446 } 14447 14448 if (moduleElement.dataset?.flexExposureLoadedTracked === 'true') { 14449 return; 14450 } 14451 14452 const parsedFlexData = this.safeJSONParse(moduleElement.getAttribute('data-flex-module-content')) || {}; 14453 if (!parsedFlexData || Object.keys(parsedFlexData).length === 0) { 14454 return; 14455 } 14456 14457 const renderedFlexElement = moduleElement.querySelector('.flex-medium-fw, .flex-container') || moduleElement; 14458 const allVisibleFlexRows = document.querySelectorAll('.nextgen-abs-carousel-proxy .flex-medium-fw, .nextgen-abs-carousel-proxy .flex-container')?.length > 0 14459 ? Array.from(document.querySelectorAll('.nextgen-abs-carousel-proxy .flex-medium-fw, .nextgen-abs-carousel-proxy .flex-container')) 14460 : []; 14461 14462 const rowIndex = allVisibleFlexRows.indexOf(renderedFlexElement) + 1; 14463 const zoneInfoObj = AB.DATALAYER.getZoneInfo(renderedFlexElement); 14464 const rowFromZone = Number(zoneInfoObj?.row) > 0 ? Number(zoneInfoObj.row) : 0; 14465 const row = rowFromZone || (rowIndex > 0 ? rowIndex : 1); 14466 const zoneFromInfo = Number(zoneInfoObj?.zone) > 0 ? Number(zoneInfoObj.zone) : 0; 14467 const zoneSource = renderedFlexElement?.getAttribute('data-zone') 14468 || moduleElement?.getAttribute('data-zone') 14469 || renderedFlexElement?.closest?.('[data-zone]')?.getAttribute?.('data-zone') 14470 || moduleElement?.closest?.('[data-zone]')?.getAttribute?.('data-zone') 14471 || ''; 14472 const zoneMatch = `${zoneSource}`.match(/(\d+)/); 14473 const zoneFromData = zoneMatch && zoneMatch[1] ? Number(zoneMatch[1]) : 0; 14474 const zone = zoneFromInfo || zoneFromData || row || 1; 14475 const slotSource = renderedFlexElement?.getAttribute('data-abs-carousel-slot') 14476 || moduleElement?.getAttribute('data-abs-carousel-slot') 14477 || parsedFlexData?.slot 14478 || parsedFlexData?.position 14479 || ''; 14480 const slotMatch = `${slotSource}`.match(/(\d+)/); 14481 const slot = slotMatch && slotMatch[1] ? Number(slotMatch[1]) : 1; 14482 const placement = `r${row < 10 ? `0${row}` : row}#s${slot < 10 ? `0${slot}` : slot}#z${zone < 10 ? `0${zone}` : zone}`; 14483 14484 const containerItems = Array.isArray(parsedFlexData?.containerItems) ? parsedFlexData.containerItems : []; 14485 const sectionTitle = this.sanitizeAnalyticsText( 14486 renderedFlexElement?.querySelector('.flex-medium-fw__text-plate__heading, .flex-title, [class*="heading"]')?.textContent?.trim() 14487 || parsedFlexData?.title 14488 || parsedFlexData?.bannerTitle 14489 || parsedFlexData?.heading 14490 || parsedFlexData?.containerHeading 14491 || parsedFlexData?.sectionHeading 14492 || parsedFlexData?.name 14493 || '' 14494 ) || 'flex-module'; 14495 const type = containerItems.length > 1 ? 'flex-tiles' : 'flex-no-tile'; 14496 const normalizeMsgExperiment = (value) => { 14497 if (value === undefined || value === null) { 14498 return ''; 14499 } 14500 const normalized = `${value}`.trim(); 14501 if (!normalized) { 14502 return ''; 14503 } 14504 const lowered = normalized.toLowerCase(); 14505 if (lowered === 'undefined' || lowered === 'null' || lowered === 'no-value') { 14506 return ''; 14507 } 14508 return normalized; 14509 }; 14510 14511 const itemWithMsgExperiment = containerItems.find(item => item?.p13nAdobeMetrics?.msgExperiment 14512 || item?.p13adobemetric?.msgExperiment 14513 || item?.adobeMetrics?.msgExperiment 14514 || item?.response?.p13nAdobeMetrics?.msgExperiment 14515 || item?.response?.p13adobemetric?.msgExperiment 14516 || item?.response?.adobeMetrics?.msgExperiment) || {}; 14517 const msgExperimentCandidates = [ 14518 parsedFlexData?.p13nAdobeMetrics?.msgExperiment, 14519 parsedFlexData?.p13adobemetric?.msgExperiment, 14520 parsedFlexData?.adobeMetrics?.msgExperiment, 14521 parsedFlexData?.response?.p13nAdobeMetrics?.msgExperiment, 14522 parsedFlexData?.response?.p13adobemetric?.msgExperiment, 14523 parsedFlexData?.response?.adobeMetrics?.msgExperiment, 14524 itemWithMsgExperiment?.p13nAdobeMetrics?.msgExperiment, 14525 itemWithMsgExperiment?.p13adobemetric?.msgExperiment, 14526 itemWithMsgExperiment?.adobeMetrics?.msgExperiment, 14527 itemWithMsgExperiment?.response?.p13nAdobeMetrics?.msgExperiment, 14528 itemWithMsgExperiment?.response?.p13adobemetric?.msgExperiment, 14529 itemWithMsgExperiment?.response?.adobeMetrics?.msgExperiment 14530 ]; 14531 const msgExperiment = msgExperimentCandidates 14532 .map(candidate => normalizeMsgExperiment(candidate)) 14533 .find(candidate => !!candidate) || 'generic'; 14534 14535 const payload = { 14536 event: { 14537 name: 'pageAction', 14538 subEvent: 'experiment-exposure' 14539 }, 14540 link: { 14541 name: 'flex-experiment-exposure-loaded', 14542 linkName: 'flex-experiment-exposure-loaded', 14543 isSendToSS: true 14544 }, 14545 page: { 14546 ...digitalData?.pageDetails, 14547 abTestDetails: msgExperiment, 14548 deviceType: digitalData?.pageDetails?.deviceType 14549 }, 14550 pageFilter: { 14551 ...digitalData?.pageFilter, 14552 mediaType: 'flex-module', 14553 placement: placement 14554 }, 14555 user: digitalData?.user, 14556 component: [{ 14557 section: sectionTitle, 14558 detail: msgExperiment, 14559 placement: placement, 14560 type: type, 14561 name: 'flex-module' 14562 }] 14563 }; 14564 14565 AB.DATALAYER.pushEvent(AB.DATALAYER.EVENTS.LINK_CLICK, payload); 14566 moduleElement.dataset.flexExposureLoadedTracked = 'true'; 14567 } 14568 14569 this.initFlexModuleExposureOnLoad = function () { 14570 const flexModules = document.querySelectorAll('webgen-flex-module'); 14571 if (!flexModules || flexModules.length === 0) { 14572 return; 14573 } 14574
14575 flexModules.forEach(moduleElement => { 14576 AB.DATALAYER.trackFlexModuleExposureOnLoad(moduleElement); 14577 }); 14578 } 14579 14580 this.initFlexModuleImpressions = function () { 14581 const flexModules = document.querySelectorAll('webgen-flex-module'); 14582 if (!flexModules || flexModules.length === 0) { 14583 return; 14584 } 14585 14586 if (!this.flexImpressionObserver && typeof IntersectionObserver !== 'undefined') { 14587 this.flexImpressionObserver = new IntersectionObserver((entries, observer) => { 14588 entries.forEach(entry => { 14589 if (entry?.isIntersecting && entry.intersectionRatio >= 0.75) { 14590 AB.DATALAYER.trackFlexModuleImpression(entry.target); 14591 observer.unobserve(entry.target); 14592 } 14593 }); 14594 }, { 14595 root: null, 14596 rootMargin: '0px', 14597 threshold: [0.75] 14598 }); 14599 } 14600 14601 flexModules.forEach(moduleElement => { 14602 if (moduleElement?.dataset?.flexObserverAttached === 'true') { 14603 return; 14604 } 14605 14606 if (this.flexImpressionObserver) { 14607 this.flexImpressionObserver.observe(moduleElement); 14608 moduleElement.dataset.flexObserverAttached = 'true'; 14609 } else { 14610 AB.DATALAYER.trackFlexModuleImpression(moduleElement); 14611 } 14612 }); 14613 } 14614 14615 this.getCollectionTrackingData = function (collectionElement) { 14616 const hostElement = collectionElement?.closest?.('webgen-bundles, webgen-bundles-carousel') || collectionElement; 14617 const wrapper = hostElement?.parentElement || hostElement; 14618 const isCarousel = hostElement?.tagName?.toLowerCase?.() === 'webgen-bundles-carousel'; 14619 const rawData = hostElement?.getAttribute?.(isCarousel ? 'data-bundles-carousel-content' : 'data-bundles-content') 14620 || hostElement?.getAttribute?.('data-bundles-content') 14621 || hostElement?.getAttribute?.('data-bundles-carousel-content'); 14622 const parsedData = AB.DATALAYER.safeJSONParse(rawData) || {}; 14623 14624 return { 14625 hostElement: hostElement, 14626 wrapper: wrapper, 14627 isCarousel: isCarousel, 14628 data: parsedData 14629 }; 14630 } 14631 14632 this.getCollectionImpressionItems = function (collectionElement) { 14633 const trackingData = AB.DATALAYER.getCollectionTrackingData(collectionElement); 14634 const hostElement = trackingData.hostElement; 14635 const wrapper = trackingData.wrapper; 14636 const data = trackingData.data; 14637 const itemsToSend = []; 14638 14639 if (!hostElement || hostElement.dataset?.collectionImpressionTracked === 'true') { 14640 return itemsToSend; 14641 } 14642 14643 window.digitalData.bundlesTrackedIds = window.digitalData.bundlesTrackedIds || []; 14644 14645 if (trackingData.isCarousel && Array.isArray(data?.containerItems) && data.containerItems.length > 0) { 14646 const allBundleCarousels = Array.from(document.querySelectorAll('webgen-bundles-carousel')) 14647 .map(element => element.parentElement || element) 14648 .filter(element => AB.DATALAYER.isVisible(element)); 14649 const allBundles = wrapper?.querySelectorAll?.('.bundles-box') ? Array.from(wrapper.querySelectorAll('.bundles-box')) : []; 14650 const row = Math.max(allBundleCarousels.indexOf(wrapper) + 1, 1); 14651 14652 data.containerItems.forEach((bundle, index) => { 14653 const bundleId = bundle?.bundleSlugId || `${hostElement.id || 'bundle-carousel'}-${index}`; 14654 if (window.digitalData.bundlesTrackedIds.indexOf(bundleId) !== -1) { 14655 return; 14656 } 14657 14658 const currentBundle = bundle?.bundleSlugId ? wrapper?.querySelector?.(`.bundles-box[data-analytics-id="${bundle.bundleSlugId}"]`) : allBundles[index]; 14659 if (currentBundle && !currentBundle.closest('.slick-active') && !AB.DATALAYER.isVisible(currentBundle)) { 14660 return; 14661 } 14662 14663 const slot = allBundles.indexOf(currentBundle) + 1 > 0 ? allBundles.indexOf(currentBundle) + 1 : index + 1; 14664 const sectionInfo = AB.DATALAYER.getZoneInfo(currentBundle || hostElement); 14665 const placement = `R${row < 10 ? `0${row}` : row}#S${slot < 10 ? `0${slot}` : slot}#ZONE${sectionInfo?.zone ? (sectionInfo.zone < 10 ? `0${sectionInfo.zone}` : sectionInfo.zone) : '00'}`; 14666 const title = bundle?.bannerTitle || bundle?.title || data?.headerTitle || 'collection'; 14667 itemsToSend.push({ 14668 section: data?.headerTitle || title, 14669 detail: bundle?.p13nAdobeMetrics?.metricA || bundle?.adobeMetrics?.metricA || data?.p13nAdobeMetrics?.metricA || data?.adobeMetrics?.metricA || 'generic', 14670 placement: placement, 14671 type: 'carousel-bundle', 14672 name: AB.DATALAYER.sanitizeAnalyticsText(title) 14673 }); 14674 window.digitalData.bundlesTrackedIds.push(bundleId); 14675 }); 14676 } else if (data && Object.keys(data).length > 0) { 14677 const allSingleBundles = Array.from(document.querySelectorAll('webgen-bundles')) 14678 .map(element => element.parentElement || element) 14679 .filter(element => AB.DATALAYER.isVisible(element));
14680 const row = Math.max(allSingleBundles.indexOf(wrapper) + 1, 1); 14681 const currentBundle = wrapper?.querySelector?.('.bundles-box') || hostElement; 14682 const sectionInfo = AB.DATALAYER.getZoneInfo(currentBundle); 14683 const placement = `R${row < 10 ? `0${row}` : row}#S01#ZONE${sectionInfo?.zone ? (sectionInfo.zone < 10 ? `0${sectionInfo.zone}` : sectionInfo.zone) : '00'}`; 14684 const title = data?.bannerTitle || data?.title || data?.headerTitle || 'collection'; 14685 const bundleId = data?.bundleSlugId || hostElement.id || `${title}-${placement}`; 14686 14687 if (window.digitalData.bundlesTrackedIds.indexOf(bundleId) === -1) { 14688 itemsToSend.push({ 14689 section: title, 14690 detail: data?.p13nAdobeMetrics?.metricA || data?.adobeMetrics?.metricA || 'generic', 14691 placement: placement, 14692 type: 'bundle', 14693 name: AB.DATALAYER.sanitizeAnalyticsText(title) 14694 }); 14695 window.digitalData.bundlesTrackedIds.push(bundleId); 14696 } 14697 } 14698 14699 return itemsToSend; 14700 } 14701 14702 this.trackCollectionImpression = function (collectionElement) { 14703 if (!AB.DATALAYER.isCESEventsEnabled() || !collectionElement || !digitalData) { 14704 return; 14705 } 14706 14707 const trackingData = AB.DATALAYER.getCollectionTrackingData(collectionElement); 14708 const hostElement = trackingData.hostElement; 14709 const component = AB.DATALAYER.getCollectionImpressionItems(collectionElement); 14710 14711 if (!hostElement || hostElement.dataset?.collectionImpressionTracked === 'true' || component.length === 0) { 14712 return; 14713 } 14714 14715 const firstPlacement = component[0]?.placement || ''; 14716 const payload = { 14717 link: { 14718 linkName: 'bundle-impressions', 14719 isSendToSS: true 14720 }, 14721 page: { 14722 ...digitalData?.pageDetails, 14723 deviceType: digitalData?.pageDetails?.deviceType 14724 }, 14725 pageFilter: { 14726 ...digitalData?.pageFilter, 14727 mediaType: 'collections', 14728 placement: firstPlacement 14729 }, 14730 user: digitalData?.user, 14731 component: component 14732 }; 14733 14734 AB.DATALAYER.pushEvent(AB.DATALAYER.EVENTS.LINK_CLICK, payload); 14735 AB.DATALAYER.trackCRSHomeModuleImpression({ 14736 element: hostElement, 14737 mediaType: 'collections', 14738 placement: firstPlacement, 14739 component: component 14740 }); 14741 hostElement.dataset.collectionImpressionTracked = 'true'; 14742 } 14743 14744 this.initCollectionImpressions = function () { 14745 const collectionModules = document.querySelectorAll('webgen-bundles, webgen-bundles-carousel'); 14746 if (!collectionModules || collectionModules.length === 0) { 14747 return; 14748 } 14749 14750 if (!this.collectionImpressionObserver && typeof IntersectionObserver !== 'undefined') { 14751 this.collectionImpressionObserver = new IntersectionObserver((entries, observer) => { 14752 entries.forEach(entry => { 14753 if (entry?.isIntersecting && entry.intersectionRatio >= 0.75) { 14754 AB.DATALAYER.trackCollectionImpression(entry.target); 14755 observer.unobserve(entry.target); 14756 } 14757 }); 14758 }, { 14759 root: null, 14760 rootMargin: '0px', 14761 threshold: [0.75] 14762 }); 14763 } 14764 14765 collectionModules.forEach(moduleElement => { 14766 if (moduleElement?.dataset?.collectionObserverAttached === 'true') { 14767 return; 14768 } 14769 14770 if (this.collectionImpressionObserver) { 14771 this.collectionImpressionObserver.observe(moduleElement); 14772 moduleElement.dataset.collectionObserverAttached = 'true'; 14773 } else { 14774 AB.DATALAYER.trackCollectionImpression(moduleElement); 14775 } 14776 }); 14777 } 14778 14779 /** 14780 * Maps a Blog component designType (imageBlog | textBlog | recipeBlog) to the 14781 * analytics "type" token used for the [TYPE]-impressions subEvent. 14782 */ 14783 this.getBlogImpressionType = function (designType) { 14784 const normalized = (designType || '').toString().toLowerCase(); 14785 if (normalized.indexOf('image') !== -1) { 14786 return 'image'; 14787 } 14788 if (normalized.indexOf('recipe') !== -1) { 14789 return 'recipe'; 14790 } 14791 if (normalized.indexOf('text') !== -1) { 14792 return 'text'; 14793 } 14794 return ''; 14795 } 14796 14797 /** 14798 * Tracks a single Blog component (image / text / recipe variation) impression once it 14799 * reaches the 75% in-viewport threshold. Sends a [TYPE]-impressions linkClick event 14800 * which is routed by the OneTag layer to /ocat/impression/serversidetag/event/. 14801 */ 14802 this.trackBlogComponentImpression = function (blogElement) { 14803 if (!blogElement || !digitalData) { 14804 return; 14805 } 14806 if (blogElement.dataset?.blogImpressionTracked === 'true') { 14807 return; 14808 } 14809 14810 const parsedBlogData = this.safeJSONParse( 14811 blogElement.getAttribute('data-blog-component') 14812 || blogElement.getAttribute('data-blog-component-content') 14813 ) || {}; 14814 14815 const blogType = AB.DATALAYER.getBlogImpressionType(parsedBlogData?.designType); 14816 if (!blogType) { 14817 return; 14818 } 14819 14820 const allBlogElements = Array.from(document.querySelectorAll('webgen-blog-component')) 14821 .filter(element => AB.DATALAYER.isVisible(element)); 14822 const rowIndex = allBlogElements.indexOf(blogElement) + 1;
14823 const row = rowIndex > 0 ? rowIndex : 1; 14824 const zoneInfo = AB.DATALAYER.getZoneInfo(blogElement)?.zone; 14825 const placement = `r${row < 10 ? `0${row}` : row}#s01#z${zoneInfo && zoneInfo > 0 ? (zoneInfo < 10 ? `0${zoneInfo}` : zoneInfo) : '00'}`; 14826 14827 const sectionTitle = AB.DATALAYER.sanitizeAnalyticsText(parsedBlogData?.title || parsedBlogData?.eyebrow || '') || 'blog'; 14828 const detail = parsedBlogData?.p13nAdobeMetrics?.metricA 14829 || parsedBlogData?.adobeMetrics?.metricA 14830 || 'no-value'; 14831 14832 // Name: itemName/title slugified per spec (lowercase, spaces -> hyphens). 14833 const rawName = parsedBlogData?.itemName || parsedBlogData?.title || `${blogType}-blog`; 14834 const name = `${rawName}` 14835 .toString() 14836 .toLowerCase() 14837 .replace(/[^a-z0-9]+/g, '-') 14838 .replace(/^-+|-+$/g, ''); 14839 14840 // Sponsored: derived from disclaimer/secondaryDisclaimer fields. 14841 const sponsoredText = `${parsedBlogData?.secondaryWebDisclaimerText || parsedBlogData?.secondaryDisclaimerText || parsedBlogData?.disclaimerText || ''}`.toLowerCase(); 14842 const sponsored = sponsoredText.indexOf('sponsor') !== -1 ? 'true' : 'false'; 14843 14844 const linkName = `${blogType}-impressions`; 14845 14846 const payload = { 14847 event: { 14848 name: 'pageAction', 14849 subEvent: linkName 14850 }, 14851 link: { 14852 linkName: linkName, 14853 name: linkName, 14854 isSendToSS: true 14855 }, 14856 page: { ...digitalData?.pageDetails }, 14857 pageFilter: { 14858 ...digitalData?.pageFilter, 14859 mediaType: 'collections', 14860 placement: placement 14861 }, 14862 user: { ...digitalData?.user }, 14863 component: [{ 14864 section: sectionTitle, 14865 detail: detail, 14866 placement: placement, 14867 type: blogType, 14868 name: name, 14869 sponsored: sponsored 14870 }] 14871 }; 14872 14873 AB.DATALAYER.pushEvent(AB.DATALAYER.EVENTS.LINK_CLICK, payload); 14874 blogElement.dataset.blogImpressionTracked = 'true'; 14875 } 14876 14877 this.initBlogImpressions = function () { 14878 const blogElements = document.querySelectorAll('webgen-blog-component'); 14879 if (!blogElements || blogElements.length === 0) { 14880 return; 14881 } 14882 14883 if (!this.blogImpressionObserver && typeof IntersectionObserver !== 'undefined') { 14884 this.blogImpressionObserver = new IntersectionObserver((entries, observer) => { 14885 entries.forEach(entry => { 14886 if (entry?.isIntersecting && entry.intersectionRatio >= 0.75) { 14887 AB.DATALAYER.trackBlogComponentImpression(entry.target); 14888 observer.unobserve(entry.target); 14889 } 14890 }); 14891 }, { 14892 root: null, 14893 rootMargin: '0px', 14894 threshold: [0.75] 14895 }); 14896 } 14897 14898 blogElements.forEach(blogElement => { 14899 if (blogElement?.dataset?.blogObserverAttached === 'true') { 14900 return; 14901 } 14902 14903 if (this.blogImpressionObserver) { 14904 this.blogImpressionObserver.observe(blogElement); 14905 blogElement.dataset.blogObserverAttached = 'true'; 14906 } else { 14907 AB.DATALAYER.trackBlogComponentImpression(blogElement); 14908 } 14909 }); 14910 } 14911 14912 /** 14913 * Reads and parses the JSON payload authored on a blog component host element. 14914 * Supports both `data-blog-component` (publish render) and `data-blog-component-content`. 14915 */ 14916 this.getBlogComponentParsedData = function (blogElement) { 14917 if (!blogElement) { 14918 return {}; 14919 } 14920 const host = blogElement.tagName && blogElement.tagName.toLowerCase() === 'webgen-blog-component' 14921 ? blogElement 14922 : (blogElement.querySelector?.('webgen-blog-component') || blogElement); 14923 return this.safeJSONParse( 14924 host.getAttribute?.('data-blog-component') 14925 || host.getAttribute?.('data-blog-component-content') 14926 ) || {}; 14927 } 14928 14929 /** 14930 * Per spec there is no `isSponsored` API attribute. Sponsorship is inferred from 14931 * authored AEM disclaimer fields containing the substring "sponsor". 14932 */ 14933 this.isBlogComponentSponsored = function (parsedBlogData) { 14934 const sponsoredText = `${parsedBlogData?.secondaryWebDisclaimerText
14935 || parsedBlogData?.secondaryDisclaimerText 14936 || parsedBlogData?.disclaimerText 14937 || ''}`.toLowerCase(); 14938 return sponsoredText.indexOf('sponsor') !== -1; 14939 } 14940 14941 /** 14942 * Builds the V218 bannerCardCarousel string for a blog CTA click per spec: 14943 * [ZONE]|banner|[blog-type]-blog:[BANNER TITLE/SUBTITLE]|[SPOT]|[UNIQUE ID] 14944 * e.g. "z01|banner|recipe-blog:shop-recipe|sp01|no-value" 14945 */ 14946 this.getBlogComponentBannerLocation = function (options) { 14947 const blogElement = options?.blogElement; 14948 const parsedBlogData = options?.parsedBlogData || {}; 14949 const blogType = options?.blogType || ''; 14950 const spot = options?.spot || 1; 14951 14952 const zoneInfo = AB.DATALAYER.getZoneInfo(blogElement)?.zone; 14953 const zoneNumber = zoneInfo && zoneInfo > 0 ? zoneInfo : 1; 14954 const zoneToken = `z${zoneNumber < 10 ? `0${zoneNumber}` : zoneNumber}`; 14955 const spotToken = `sp${spot < 10 ? `0${spot}` : spot}`; 14956 14957 const titleSource = parsedBlogData?.title 14958 || parsedBlogData?.subTitle 14959 || parsedBlogData?.eyebrow 14960 || `${blogType}-blog`; 14961 const titleToken = AB.DATALAYER.sanitizeAnalyticsText(`${titleSource}`) || `${blogType}-blog`; 14962 14963 const uniqueIdSource = parsedBlogData?.modelConfigurationId 14964 || parsedBlogData?.analyticsTag 14965 || parsedBlogData?.itemId 14966 || parsedBlogData?.id; 14967 const uniqueIdToken = uniqueIdSource 14968 ? (AB.DATALAYER.sanitizeAnalyticsText(`${uniqueIdSource}`) || 'no-value') 14969 : 'no-value'; 14970 14971 const value = `${zoneToken}|banner|${blogType}-blog:${titleToken}|${spotToken}|${uniqueIdToken}`; 14972 return value.length > 256 ? value.substring(0, 256) : value; 14973 } 14974 14975 /** 14976 * Persists V218 (bannerCardCarousel) and V80 (mediaPlacementClick) to localStorage 14977 * for a Blog component CTA click that navigates to a new page. The next pageLoaded 14978 * event picks these values up via initAEMZoneTracking, so no additional linkClick 14979 * is fired and existing V151-V154 values on the outbound click are preserved. 14980 * Per ACIP-576827 spec. Same-page clicks (empty/hash href) are skipped by the caller. 14981 */ 14982 this.setBlogComponentClickAnalytics = function (blogElement, anchorElement) { 14983 if (!blogElement) { 14984 return; 14985 } 14986 14987 const parsedBlogData = AB.DATALAYER.getBlogComponentParsedData(blogElement); 14988 const blogType = AB.DATALAYER.getBlogImpressionType(parsedBlogData?.designType); 14989 if (!blogType) { 14990 return; 14991 } 14992 14993 const hostElement = blogElement.tagName && blogElement.tagName.toLowerCase() === 'webgen-blog-component' 14994 ? blogElement 14995 : (blogElement.querySelector?.('webgen-blog-component') || blogElement); 14996 14997 const allBlogElements = Array.from(document.querySelectorAll('webgen-blog-component, .blog-component-wrapper_left-align, .blog-component-wrapper_right-align, .blog-component-recipe')) 14998 .filter(element => AB.DATALAYER.isVisible(element)); 14999 const rowIndex = allBlogElements.indexOf(hostElement); 15000 const row = rowIndex >= 0 ? rowIndex + 1 : 1; 15001 const slot = 1; 15002 15003 const isSponsored = AB.DATALAYER.isBlogComponentSponsored(parsedBlogData); 15004 const recommendationType = isSponsored ? 'sponsored' : 'notRecommended'; 15005 const mediaType = `${blogType}-blog`; 15006 const v80 = `${mediaType}#${recommendationType}#r${row < 10 ? `0${row}` : row}#s${slot < 10 ? `0${slot}` : slot}`; 15007 15008 const bannerLocation = AB.DATALAYER.getBlogComponentBannerLocation({ 15009 blogElement: hostElement, 15010 parsedBlogData: parsedBlogData, 15011 blogType: blogType, 15012 spot: slot 15013 }); 15014 15015 try { 15016 if (bannerLocation) { 15017 localStorage.setItem('aemZoneAnalytics', bannerLocation); 15018 } 15019 if (v80) { 15020 localStorage.setItem('mediaPlacementClick', v80); 15021 } 15022 } catch (e) { /* no-op */ } 15023 } 15024 15025 this.initCaasModuleTracking = function () { 15026 this.initFlexModuleExposureOnLoad(); 15027 this.initFlexModuleImpressions(); 15028 this.initCollectionImpressions(); 15029 this.initBlogTracking(); 15030 15031 if (this.caasModuleTrackingObserver || !document?.body || typeof MutationObserver === 'undefined') { 15032 return; 15033 } 15034 15035 this.caasModuleTrackingObserver = new MutationObserver(() => { 15036 clearTimeout(this.caasModuleTrackingTimer); 15037 this.caasModuleTrackingTimer = setTimeout(() => { 15038 AB.DATALAYER.initFlexModuleExposureOnLoad(); 15039 AB.DATALAYER.initFlexModuleImpressions();
15040 AB.DATALAYER.initCollectionImpressions(); 15041 AB.DATALAYER.initBlogTracking(); 15042 }, 100); 15043 }); 15044 this.caasModuleTrackingObserver.observe(document.body, { childList: true, subtree: true }); 15045 } 15046 15047 /** 15048 * Page-agnostic entry point for Blog component analytics (impressions only). 15049 * Blog CTA V218/V80 tracking piggybacks on the existing CRS-Home universal 15050 * click layer (see crsHomeUniversalClickHandler); no dedicated click listener 15051 * is registered here. Owns a lightweight MutationObserver so late-injected blog 15052 * components (SPA route changes, lazy loads on pages that do not run 15053 * initCaasModuleTracking) still get impression tracking. 15054 */ 15055 this.initBlogTracking = function () { 15056 this.initBlogImpressions(); 15057 15058 if (this.blogTrackingObserver || !document?.body || typeof MutationObserver === 'undefined') { 15059 return; 15060 } 15061 15062 this.blogTrackingObserver = new MutationObserver((mutations) => { 15063 const hasRelevantChange = mutations.some(mutation => { 15064 if (!mutation.addedNodes || mutation.addedNodes.length === 0) { 15065 return false; 15066 } 15067 return Array.from(mutation.addedNodes).some(node => { 15068 if (node?.nodeType !== 1) { 15069 return false; 15070 } 15071 return node.matches?.('webgen-blog-component, .blog-component-wrapper_left-align, .blog-component-wrapper_right-align, .blog-component-recipe') 15072 || node.querySelector?.('webgen-blog-component, .blog-component-wrapper_left-align, .blog-component-wrapper_right-align, .blog-component-recipe'); 15073 }); 15074 }); 15075 if (!hasRelevantChange) { 15076 return; 15077 } 15078 clearTimeout(this.blogTrackingTimer); 15079 this.blogTrackingTimer = setTimeout(() => { 15080 AB.DATALAYER.initBlogImpressions(); 15081 }, 100); 15082 }); 15083 this.blogTrackingObserver.observe(document.body, { childList: true, subtree: true }); 15084 } 15085 15086 this.initFlexCardDetails = function () { 15087 if (this.getLSItem('flexCardAnalytics') && digitalData?.pageFilter) { 15088 digitalData.pageFilter.bannerCardCarousel = this.handleGetLSValue('flexCardAnalytics'); 15089 } 15090 15091 this.initCaasModuleTracking(); 15092 } 15093 15094 this.getStickyBannerData = function (stickyBannerElement) { 15095 const stickyBannerHost = stickyBannerElement?.closest('webgen-sticky-banner') || document.querySelector('webgen-sticky-banner'); 15096 const parsedAttributeData = this.safeJSONParse(stickyBannerHost?.getAttribute('data-sticky-banner-content')) 15097 || this.safeJSONParse(stickyBannerHost?.getAttribute('data-sticky-banner')) 15098 || this.safeJSONParse(stickyBannerHost?.getAttribute('data-content')) 15099 || this.safeJSONParse(stickyBannerHost?.getAttribute('data')) 15100 || {}; 15101 const bannerData = stickyBannerHost?.currentBanner || parsedAttributeData; 15102 15103 return { 15104 host: stickyBannerHost, 15105 data: bannerData && typeof bannerData === 'object' ? bannerData : {} 15106 }; 15107 } 15108 15109 this.getStickyBannerPlacement = function (stickyBannerElement) { 15110 const sectionInfo = AB.DATALAYER.getZoneInfo(stickyBannerElement); 15111 const section = Number(sectionInfo?.section) > 0 ? Number(sectionInfo.section) : 1; 15112 const row = Number(sectionInfo?.row) > 0 ? Number(sectionInfo.row) : 1; 15113 const zone = Number(sectionInfo?.zone) > 0 ? Number(sectionInfo.zone) : 99; 15114 return `r${section < 10 ? `0${section}` : section}#s${row < 10 ? `0${row}` : row}#z${zone < 10 ? `0${zone}` : zone}`; 15115 } 15116 15117 this.getStickyBannerMetricA = function (stickyBannerData) { 15118 const responseData = stickyBannerData?.response || {}; 15119 const metricA = stickyBannerData?.p13nAdobeMetrics?.metricA 15120 || stickyBannerData?.adobeMetrics?.metricA 15121 || responseData?.p13nAdobeMetrics?.metricA 15122 || responseData?.adobeMetrics?.metricA 15123 || ''; 15124 15125 const normalizedMetricA = typeof metricA === 'string' ? metricA.trim() : ''; 15126 if (!normalizedMetricA) { 15127 return ''; 15128 } 15129 15130 const metricAParts = normalizedMetricA.split('#').map(part => (part || '').trim()); 15131 const hasEmptyCarouselToken = metricAParts.some(part => part.toLowerCase() === 'empty_carousel'); 15132 15133 // Requirement: when MetricA is empty (empty_carousel variant), send generic instead. 15134 return hasEmptyCarouselToken ? '' : normalizedMetricA; 15135 } 15136
15137 this.getStickyBannerAbTestDetails = function (stickyBannerData) { 15138 const responseData = stickyBannerData?.response || {}; 15139 const modelData = stickyBannerData?.p13nData?.modelData || stickyBannerData?.modelData || responseData?.p13nData?.modelData || responseData?.modelData || {}; 15140 const experimentData = stickyBannerData?.p13nData?.experimentData || stickyBannerData?.experimentData || responseData?.p13nData?.experimentData || responseData?.experimentData || {}; 15141 const recommendationData = stickyBannerData?.p13nData?.recommendationData || stickyBannerData?.recommendationData || responseData?.p13nData?.recommendationData || responseData?.recommendationData || {}; 15142 const msgExperiment = stickyBannerData?.p13nAdobeMetrics?.msgExperiment 15143 || stickyBannerData?.adobeMetrics?.msgExperiment 15144 || responseData?.p13nAdobeMetrics?.msgExperiment 15145 || responseData?.adobeMetrics?.msgExperiment 15146 || ''; 15147 const metricA = AB.DATALAYER.getStickyBannerMetricA(stickyBannerData); 15148 const metricAParts = metricA ? metricA.split('#').map(part => (part || '').trim()) : []; 15149 const msgExperimentParts = msgExperiment ? msgExperiment.split('#').map(part => (part || '').trim()) : []; 15150 const successfullyServed = stickyBannerData?.p13nAdobeMetrics?.successfullyServed 15151 || stickyBannerData?.adobeMetrics?.successfullyServed 15152 || responseData?.p13nAdobeMetrics?.successfullyServed 15153 || responseData?.adobeMetrics?.successfullyServed 15154 || stickyBannerData?.successfullyServed 15155 || metricAParts[2]; 15156 15157 const normalizePart = (value) => { 15158 if (value === false) { 15159 return 'false'; 15160 } 15161 if (value === true) { 15162 return 'true'; 15163 } 15164 if (value === null || value === undefined || value === '') { 15165 return 'no-value'; 15166 } 15167 return AB.DATALAYER.sanitizeAnalyticsText(String(value)) || 'no-value'; 15168 }; 15169 15170 return [ 15171 normalizePart(msgExperimentParts[0] || modelData?.modelConfigurationId || metricAParts[0]), 15172 normalizePart(msgExperimentParts[1] || experimentData?.experimentId || metricAParts[1]), 15173 normalizePart(msgExperimentParts[2] || experimentData?.experimentGroup), 15174 normalizePart(msgExperimentParts[3] || modelData?.modelId || metricAParts[3]), 15175 normalizePart(msgExperimentParts[4] || modelData?.modelVersion || metricAParts[4]), 15176 normalizePart(msgExperimentParts[5] || modelData?.fallbackModel), 15177 normalizePart(msgExperimentParts[6] || recommendationData?.rank), 15178 normalizePart(msgExperimentParts[7] || successfullyServed) 15179 ].join('#'); 15180 } 15181 15182 this.getStickyBannerLocation = function (stickyBannerElement, stickyBannerData) { 15183 const title = AB.DATALAYER.sanitizeAnalyticsText(stickyBannerData?.title || stickyBannerData?.bannerTitle || 'sticky-banner'); 15184 const subTitle = AB.DATALAYER.sanitizeAnalyticsText(stickyBannerData?.subTitle || stickyBannerData?.bannerSubTitle || ''); 15185 const promotionId = AB.DATALAYER.sanitizeAnalyticsText((stickyBannerData?.promotionId || '').toString()) || 'no-value'; 15186 const bannerName = `${title}${subTitle ? `-${subTitle}` : ''}`; 15187 15188 return `z99|banner|sticky-banner:${bannerName}|sp99|${promotionId}`; 15189 } 15190 15191 this.setStickyBannerClickedEvent = function (stickyBannerElement, title) { 15192 if (!window.digitalData || !window.digitalData.pageFilter || !stickyBannerElement) { 15193 return; 15194 } 15195 15196 const stickyBannerInfo = AB.DATALAYER.getStickyBannerData(stickyBannerElement); 15197 const stickyBannerData = stickyBannerInfo?.data || {}; 15198 const msgDetail = AB.DATALAYER.getStickyBannerMetricA(stickyBannerData) || 'generic'; 15199 const abTestDetails = AB.DATALAYER.getStickyBannerAbTestDetails(stickyBannerData); 15200 const bannerTitle = AB.DATALAYER.sanitizeAnalyticsText(stickyBannerData?.title || stickyBannerData?.bannerTitle || 'sticky-banner'); 15201 const subTitle = AB.DATALAYER.sanitizeAnalyticsText(stickyBannerData?.subTitle || stickyBannerData?.bannerSubTitle || ''); 15202 const promotionId = AB.DATALAYER.sanitizeAnalyticsText((stickyBannerData?.promotionId || '').toString()) || 'no-value'; 15203 const bannerName = `${bannerTitle}${subTitle ? `-${subTitle}` : ''}`; 15204 const placement = AB.DATALAYER.getStickyBannerPlacement(stickyBannerElement); 15205 const bannerLocation = AB.DATALAYER.getStickyBannerLocation(stickyBannerElement, stickyBannerData); 15206 const pageDetails = { 15207 ...structuredClone(digitalData?.pageDetails || {}), 15208 msgDetail: msgDetail, 15209 abTestDetails: abTestDetails, 15210 bnrName: `${bannerName}|${promotionId}` 15211 }; 15212 const pageFilter = { 15213 ...structuredClone(digitalData?.pageFilter || {}), 15214 recommendationType: 'notRecommended', 15215 placement: placement, 15216 mediaType: 'sticky-banner', 15217 bannerCardCarousel: bannerLocation 15218 }; 15219 const payload = { 15220 link: { 15221 linkName: title, 15222 oneTagLinkName: 'banner-clicked' 15223 }, 15224 page: pageDetails, 15225 pageFilter: pageFilter, 15226 user: structuredClone(digitalData?.user || {}), 15227 modal: { 15228 ...structuredClone(digitalData?.modal || {}), 15229 modalMessage: msgDetail 15230 } 15231 }; 15232 15233 this.pushEvent(this.EVENTS.LINK_CLICK, payload); 15234 } 15235 15236 this.trackStickyBannerExposure = function (stickyBannerElement) { 15237 if (!stickyBannerElement || digitalData?.pageFlags?.isStickyBannerExposureSent) { 15238 return; 15239 } 15240 15241 const stickyBannerInfo = AB.DATALAYER.getStickyBannerData(stickyBannerElement); 15242 const stickyBannerData = stickyBannerInfo?.data || {}; 15243 const hasStickyBannerData = Object.keys(stickyBannerData).length > 0; 15244 if (!hasStickyBannerData) { 15245 return; 15246 } 15247 15248 const title = AB.DATALAYER.sanitizeAnalyticsText(stickyBannerData?.title || stickyBannerData?.bannerTitle || 'sticky-banner'); 15249 const subTitle = AB.DATALAYER.sanitizeAnalyticsText(stickyBannerData?.subTitle || stickyBannerData?.bannerSubTitle || ''); 15250 const promotionId = AB.DATALAYER.sanitizeAnalyticsText((stickyBannerData?.promotionId || '').toString()) || 'no-value'; 15251 const placement = AB.DATALAYER.getStickyBannerPlacement(stickyBannerElement); 15252 const pageDetails = { 15253 ...digitalData?.pageDetails, 15254 abTestDetails: AB.DATALAYER.getStickyBannerAbTestDetails(stickyBannerData), 15255 bnrName: `${title}${subTitle ? `-${subTitle}` : ''}|${promotionId}` 15256 }; 15257 15258 AB.DATALAYER.pushEvent( 15259 AB.DATALAYER.EVENTS.LINK_CLICK, 15260 { 15261 link: { 15262 linkName: 'experiment-exposure-loaded-banner', 15263 isSendToSS: true 15264 }, 15265 page: pageDetails, 15266 pageFilter: { 15267 ...digitalData?.pageFilter, 15268 mediaType: 'sticky-banner', 15269 placement: placement 15270 }, 15271 user: digitalData?.user 15272 } 15273 ); 15274 15275 digitalData.pageFlags.isStickyBannerExposureSent = true; 15276 } 15277 15278 this.trackStickyBannerImpression = function (stickyBannerElement) { 15279 if (!AB.DATALAYER.isCESEventsEnabled() || !stickyBannerElement || digitalData?.pageFlags?.isStickyBannerImpressionSent) { 15280 return; 15281 } 15282 15283 const stickyBannerInfo = AB.DATALAYER.getStickyBannerData(stickyBannerElement); 15284 const stickyBannerData = stickyBannerInfo?.data || {}; 15285 const msgDetail = AB.DATALAYER.getStickyBannerMetricA(stickyBannerData) || 'generic'; 15286 const title = AB.DATALAYER.sanitizeAnalyticsText(stickyBannerData?.title || stickyBannerData?.bannerTitle || 'sticky-banner'); 15287 const subTitle = AB.DATALAYER.sanitizeAnalyticsText(stickyBannerData?.subTitle || stickyBannerData?.bannerSubTitle || ''); 15288 const promotionId = AB.DATALAYER.sanitizeAnalyticsText((stickyBannerData?.promotionId || '').toString()) || 'no-value'; 15289 const placement = AB.DATALAYER.getStickyBannerPlacement(stickyBannerElement); 15290 const pageDetails = { 15291 ...digitalData?.pageDetails, 15292 msgDetail: msgDetail, 15293 bnrName: `${title}${subTitle ? `-${subTitle}` : ''}|${promotionId}` 15294 }; 15295 15296 AB.DATALAYER.pushEvent( 15297 AB.DATALAYER.EVENTS.LINK_CLICK, 15298 { 15299 link: { 15300 linkName: 'banner-impressions', 15301 isSendToSS: true 15302 }, 15303 page: pageDetails, 15304 pageFilter: { 15305 ...digitalData?.pageFilter, 15306 mediaType: 'sticky-banner', 15307 placement: placement 15308 }, 15309 user: digitalData?.user 15310 } 15311 ); 15312 15313 digitalData.pageFlags.isStickyBannerImpressionSent = true; 15314 stickyBannerElement.dataset.stickyBannerImpressionTracked = 'true'; 15315 15316 if (AB.DATALAYER.stickyBannerImpressionObserver) { 15317 AB.DATALAYER.stickyBannerImpressionObserver.disconnect(); 15318 AB.DATALAYER.stickyBannerImpressionObserver = null; 15319 } 15320 if (AB.DATALAYER.stickyBannerMutationObserver) { 15321 AB.DATALAYER.stickyBannerMutationObserver.disconnect(); 15322 AB.DATALAYER.stickyBannerMutationObserver = null; 15323 } 15324 } 15325 15326 this.observeStickyBannerImpression = function (stickyBannerElement) { 15327 if (!stickyBannerElement || digitalData?.pageFlags?.isStickyBannerImpressionSent || stickyBannerElement?.dataset?.stickyBannerObserverAttached === 'true') { 15328 return; 15329 } 15330 15331 if (typeof IntersectionObserver === 'undefined') { 15332 AB.DATALAYER.trackStickyBannerImpression(stickyBannerElement); 15333 return; 15334 } 15335 15336 if (!AB.DATALAYER.stickyBannerImpressionObserver) { 15337 AB.DATALAYER.stickyBannerImpressionObserver = new IntersectionObserver((entries, observer) => {
15338 entries.forEach(entry => { 15339 if (entry?.isIntersecting && entry?.intersectionRatio >= 0.5) { 15340 AB.DATALAYER.trackStickyBannerImpression(entry.target); 15341 observer.unobserve(entry.target); 15342 } 15343 }); 15344 }, { 15345 root: null, 15346 rootMargin: '0px', 15347 threshold: [0.5] 15348 }); 15349 } 15350 15351 AB.DATALAYER.stickyBannerImpressionObserver.observe(stickyBannerElement); 15352 stickyBannerElement.dataset.stickyBannerObserverAttached = 'true'; 15353 } 15354 15355 this.initStickyBannerImpressions = function () { 15356 if (digitalData?.pageFlags?.isStickyBannerImpressionSent && digitalData?.pageFlags?.isStickyBannerExposureSent) { 15357 return; 15358 } 15359 15360 const stickyBannerElement = document.querySelector('webgen-sticky-banner .sticky-banner__container, .sticky-banner__container'); 15361 if (stickyBannerElement) { 15362 AB.DATALAYER.trackStickyBannerExposure(stickyBannerElement); 15363 AB.DATALAYER.observeStickyBannerImpression(stickyBannerElement); 15364 return; 15365 } 15366 15367 if (AB.DATALAYER.stickyBannerMutationObserver || typeof MutationObserver === 'undefined') { 15368 return; 15369 } 15370 15371 AB.DATALAYER.stickyBannerMutationObserver = new MutationObserver(() => { 15372 const stickyElement = document.querySelector('webgen-sticky-banner .sticky-banner__container, .sticky-banner__container'); 15373 if (stickyElement) { 15374 AB.DATALAYER.trackStickyBannerExposure(stickyElement); 15375 AB.DATALAYER.observeStickyBannerImpression(stickyElement); 15376 } 15377 }); 15378 15379 AB.DATALAYER.stickyBannerMutationObserver.observe(document.body, { 15380 childList: true, 15381 subtree: true 15382 }); 15383 } 15384 15385 /** 15386 * Set the subscription / plan change / conffirmation pages values for subscriptionFunnel and subscription status 15387 * @param fpsObject - The object which contains the properties properties are going to be used to change the values in the subscriptionFunnel and subscriptionStatus 15388 * @param subscriptionStep - A string type variable which hold the information about the current step in ther page 15389 */ 15390 this.setFPSPlanType = function (analyticsStep, fpsObject) { 15391 const isPrebook = fpsObject?.isPrebookReq === 'prebook'; 15392 15393 const FPSConfirmationStep = this.getLSItem('freshpass-analytics-confirmation') && this.isFreshpassConfirmationPage(); 15394 const FPSConfirmationLanding = !this.getLSItem('freshpass-analytics-confirmation') && this.isFreshpassConfirmationPage(); 15395 15396 if (FPSConfirmationStep) { 15397 fpsObject = this.safeJSONParse(this.handleGetLSValue('freshpass-analytics-confirmation')); 15398 } 15399 15400 let isDiscounted = this.getLSItem('analyticsFPDiscountPlan') || (fpsObject?.isMonthlyDiscounted || fpsObject?.isAnnualDiscounted || fpsObject?.isDiscounted); 15401 let isTrial = this.getLSItem('analyticsTrialFPUpdate') || fpsObject?.isTrialElligible; 15402 let isExtTrial = this.getLSItem('analyticsFPExtPlan') || fpsObject?.isExtendedTrial; 15403 const isSnap = fpsObject?.campaignType === 'SHEER' ? true : false; 15404 const isSnapChangePlan = this.getLSItem('analyticsChangeSnapPlan') ? this.handleGetLSValue('analyticsChangeSnapPlan') : false; 15405 15406 const planType = fpsObject?.planType === 'monthlyplan' ? 'monthly' : 'annual'; 15407 15408 const changePlan = (this.getLSItem('analyticsFPLSitem') === 'changeToAnnual') 15409 ? 'monthly|annual' : (this.getLSItem('analyticsFPLSitem') === 'changeToMonthly' ? 'annual|monthly' : ''); 15410 15411 let stepChange = analyticsStep === 'step1' ? '-plan-selection' : (analyticsStep === 'landing' ? '-landing' 15412 : (analyticsStep === 'changePlan' ? `${changePlan}` 15413 : `${(isTrial ? '-trial' : '')}-${planType}`)); 15414 15415 15416 if (analyticsStep === 'step2' || analyticsStep === 'step3' || FPSConfirmationStep) { 15417 digitalData.pageFilter.freshPassPlanPrice = planType === 'monthly' ? fpsObject?.monthlyPlanPrice : fpsObject?.annualPlanPrice; 15418 } 15419 15420 if (analyticsStep !== 'changePlan') { 15421 digitalData.pageFilter.freshPassDiscountedMonths = fpsObject?.monthlyDiscountMonths; 15422 } 15423 15424 const setEmployeeSubscription = function () { 15425 return `emp:freshpass${stepChange}`; 15426 } 15427 15428 const setSnapSubscription = function () { 15429 return `snap:freshpass${stepChange}`; 15430 } 15431 15432 const setSubscription = function (preebookStatus) { 15433 if (preebookStatus) { 15434 return `prebook:freshpass${isExtTrial ? `-${fpsObject.trialExtDuration}-extd` : ''}${stepChange}`; 15435 } else { 15436 return `${isDiscounted ? 'disc:' : ''}freshpass${isExtTrial ? `-${fpsObject.trialExtDuration}-extd` : ''}${stepChange}`; 15437 } 15438 } 15439 15440 const setChangePlan = function () { 15441 return isExtTrial ? `extd-plan-changed:${stepChange}` 15442 : `${(isDiscounted ? 'disc-' : isSnapChangePlan ? 'snap-' : '')}plan-changed:${(isTrial ? 'trial-' : '')}${stepChange}`; 15443 } 15444 15445 if (analyticsStep === 'confirmation') { 15446 digitalData.pageFilter.subscriptionFunnel = FPSConfirmationStep 15447 ? fpsObject.isEmployee 15448 ? `${setEmployeeSubscription()}-confirmation` 15449 : isSnap ? `${setSnapSubscription()}-confirmation` : `${setSubscription(isPrebook)}-confirmation` 15450 : 'freshpass-manage-plan'; 15451 } else if (analyticsStep === 'changePlan') { 15452 digitalData.pageFilter.subscriptionFunnel = setChangePlan(); 15453 } else { 15454 digitalData.pageFilter.subscriptionFunnel = fpsObject.isEmployee ? setEmployeeSubscription() : isSnap ? setSnapSubscription() : setSubscription(isPrebook); 15455 } 15456 15457 digitalData.user.subscriptionStatus = FPSConfirmationStep 15458 ? `${isSnap ? 'snap:' : ''}freshpass|${(!!fpsObject.isTrialElligible ? 'trial-' : '')}${planType}-success` : analyticsStep === 'changePlan' 15459 ? `freshpass|${(isTrial ? 'trial-' : '')}${changePlan === 'monthly|annual' ? 'monthly-to-annual-success' : 'annual-to-monthly-success'}` : FPSConfirmationLanding 15460 ? 'freshpass-manage-plan' : 'not-available'; 15461 } 15462 15463 this.initFPSItems = function () { 15464 if (!window.location.pathname.startsWith('/freshpass/manage/subscribe') && !this.isFreshpassConfirmationPage()) { 15465 this.removeLSItem('freshpass-analytics-confirmation'); 15466 } 15467 if (this.getLSItem('analyticsFPLSitem') === 'clickedBanner' && (!window.location.pathname.startsWith('/freshpass/manage'))) { 15468 this.removeLSItem('analyticsFPLSitem'); 15469 } 15470 if (this.getLSItem('analyticsFPLSitem') === 'clickedAnnualBtn' && (!window.location.pathname.startsWith('/freshpass/manage'))) { 15471 this.removeLSItem('analyticsFPLSitem'); 15472 } 15473 if (!window.location.pathname.startsWith('/delivery-subscription') || !window.location.pathname.startsWith('/freshpass')) { 15474 this.removeLSItem('analyticsTrialFPUpdate'); 15475 this.removeLSItem('analyticsFPDiscountPlanAnnual'); 15476 this.removeLSItem('analyticsFPDiscountPlan'); 15477 this.removeLSItem('analyticsFPExtPlan'); 15478 this.removeLSItem('analyticsFPExtPlanAnnual'); 15479 this.removeLSItem('analyticsFPDiscountPlanMonthly'); 15480 this.removeLSItem('analyticsFPExtPlanMonthly'); 15481 this.removeLSItem('analyticsChangePlan'); 15482 } 15483 } 15484 15485 this.initPetDetails = function () { 15486 if (localStorage.getItem('analyticsEditPetDetails') && digitalData?.pageDetails) { 15487 digitalData.pageDetails.subSection2 = 'edit-pet-details'; 15488 digitalData.pageDetails.pageName = `${digitalData.pageDetails.banner}:loyalty:${digitalData.pageDetails.subSection1}:${digitalData.pageDetails.subSection2}`; 15489 localStorage.removeItem('analyticsEditPetDetails'); 15490 } 15491 } 15492 15493 this.similarItemsTrackingBIA = function () { 15494 if (this.isBIAPage()) { 15495 localStorage.setItem('topNavSimilarItemsBIA', 'cta:purchases:buy-it-again|view-similar-items|similar-items'); 15496 localStorage.setItem('analyticsSimilarItemsBIA', 'similar-items_buy-it-again'); 15497 } 15498 } 15499 15500 this.setPfmSourceBiaPDP = function () { 15501 if (digitalData.pageFilter && digitalData.pageFilter.topNav === 'cta:purchases:buy-it-again|view-similar-items|similar-items') { 15502 localStorage.setItem('pfmSourceSimilarItemsBIA', 'similar-items_buy-it-again'); 15503 } 15504 } 15505 15506 this.setEmailAndSmsOptIn = function () { 15507 const emailOptIn = document.querySelector('.terms-text-unified #terms-email')?.checked; 15508 const smsOptIn = document.querySelector('.terms-text-unified #terms-sms')?.checked; 15509 const userObj = { 15510 emailOptIn: emailOptIn, 15511 smsOptIn: smsOptIn 15512 }; 15513 localStorage.setItem('analyticsEmailAndSmsOptIn', JSON.stringify(userObj)); 15514 } 15515 15516 this.initEmailAndSmsOptIn = function () { 15517 if (window.digitalData && localStorage.getItem('analyticsEmailAndSmsOptIn')) { 15518 const userObj = AB.DATALAYER.safeJSONParse(AB.DATALAYER.handleGetLSValue('analyticsEmailAndSmsOptIn')); 15519 window.digitalData.user = window.digitalData.user || {}; 15520 window.digitalData.user.smsOptIn = userObj.smsOptIn; 15521 window.digitalData.user.emailOptIn = userObj.emailOptIn; 15522 } 15523 } 15524 15525 this.progressiveProfilingTracking = function ({ subSection1, subSection2, topNav } = {}) { 15526 const profileCompletionShown = document.querySelector('#profile-completion')?.classList?.contains('show'); 15527 if (digitalData && digitalData.pageDetails && digitalData.pageFilter && !profileCompletionShown) { 15528 digitalData.pageDetails.subSection1 = subSection1 ? subSection1 : AB.DATALAYER.pathSections[0] ? AB.DATALAYER.pathSections[0] : ''; 15529 digitalData.pageDetails.subSection2 = subSection2 ? subSection2 : AB.DATALAYER.pathSections[1] ? AB.DATALAYER.pathSections[1] : ''; 15530 digitalData.pageDetails.pageName = `${digitalData.pageDetails.banner}:loyalty:${digitalData.pageDetails.subSection1}:${digitalData.pageDetails.subSection2}`; 15531 digitalData.pageFilter.topNav = topNav ? topNav : AB.DATALAYER.initTopNav(); 15532 digitalData.pageFlags.isPageLoadSent = false;
15533 AB.DATALAYER.setPageLoad(); 15534 } 15535 } 15536 15537 this.setTopNavMealsCard = function (mealCard, mealId) { 15538 const currentMealSlot = Number(mealCard?.closest('div.slick-slide')?.getAttribute('data-slick-index')) + 1; 15539 const currentCarouselTitle = mealCard?.closest('.slick-meal-carousel')?.querySelector('h2.meal-carousel-title')?.textContent?.trim()?.replace(/\s/g, ''); 15540 localStorage.setItem('analyticsMealsCarouselTopNav', `cta:${AB.DATALAYER.pathSections?.join(':')}|meals-carousel|${currentCarouselTitle}-variantID:${mealId}-${currentMealSlot}`); 15541 if (AB.DATALAYER.isHomePage()) { 15542 const currentCarousel = mealCard?.closest('.slick-meal-carousel'); 15543 const mealCarousels = AB.DATALAYER.isVisible(document.querySelectorAll('.slick-meal-carousel')); 15544 const carouselRow = mealCarousels && Array.from(mealCarousels).indexOf(currentCarousel) + 1; 15545 const placement = `R${carouselRow && carouselRow < 10 ? `0${carouselRow}` : carouselRow || '00'}#S${currentMealSlot && currentMealSlot < 10 ? `0${currentMealSlot}` : currentMealSlot || '00'}`; 15546 AB.DATALAYER.setCRSRecipeClick({ 15547 element: mealCard, 15548 placement: placement, 15549 component: AB.DATALAYER.getCRSComponent({ 15550 element: mealCard, 15551 section: currentCarouselTitle, 15552 spot: currentMealSlot, 15553 placement: placement 15554 }) 15555 }); 15556 } 15557 } 15558 15559 this.getCRSPlacementParts = function (placement) { 15560 const placementValue = placement || digitalData?.pageFilter?.placement || ''; 15561 const rowMatch = placementValue.match(/R(\d+)/i); 15562 const spotMatch = placementValue.match(/S(\d+)/i); 15563 const zoneMatch = placementValue.match(/Z(?:ONE)?(\d+)/i); 15564 return { 15565 row: rowMatch ? parseInt(rowMatch[1], 10) : 0, 15566 spot: spotMatch ? parseInt(spotMatch[1], 10) : 0, 15567 zone: zoneMatch ? parseInt(zoneMatch[1], 10) : 0 15568 }; 15569 } 15570 15571 this.isCRSHomeAEMComponent = function (element) { 15572 return !!element?.closest?.('[data-zone-response], [data-api-driven], [data-carousel-driven], [data-abs-carousel-position], [data-abs-carousel-slot], lazy-wrapper[data-model-configuration-id], lazy-wrapper[data-carousel-filter="p13n"], targeted-wrapper, webgen-flex-module, webgen-bundles, webgen-bundles-carousel'); 15573 } 15574 15575 this.getCRSFrontendAPIZone = function (element) { 15576 const dataElement = element?.closest?.('[data-zone-response]'); 15577 const zoneData = AB.DATALAYER.safeJSONParse(dataElement?.getAttribute?.('data-zone-response')) || {}; 15578 return zoneData?.zone || null; 15579 } 15580 15581 this.getCRSAbsPlacement = function ({ element, placement, caasEnbld } = {}) { 15582 const placementParts = this.getCRSPlacementParts(placement); 15583 const zoneInfo = element ? AB.DATALAYER.getZoneInfo(element) : {}; 15584 const isAEMComponent = element ? AB.DATALAYER.isCRSHomeAEMComponent(element) : true; 15585 15586 let zoneNumber = placementParts.zone || parseInt(zoneInfo?.zone, 10) || 0; 15587 let zoneValue; 15588 let slotValue; 15589 15590 if (isAEMComponent) { 15591 const apiZone = AB.DATALAYER.getCRSFrontendAPIZone(element); 15592 zoneValue = apiZone || `zone${zoneNumber}`; 15593 slotValue = placementParts.row || parseInt(zoneInfo?.row, 10) || 0; 15594 } else { 15595 zoneValue = `nzone${zoneNumber}`; 15596 slotValue = undefined; 15597 } 15598 15599 return { 15600 zone: zoneValue, 15601 zSlot: slotValue, 15602 absLoc: placementParts.spot || 0, 15603 caasEnbld: caasEnbld === undefined ? true : !!caasEnbld 15604 }; 15605 } 15606 15607 this.getCRSComponent = function ({ element, section, detail, type, name, spot, sponsored, placement } = {}) { 15608 const placementParts = this.getCRSPlacementParts(placement); 15609 const componentSection = section || AB.DATALAYER.getAnalyticsCarouselTitle(element) || ''; 15610 return [{ 15611 section: componentSection ? AB.DATALAYER.sanitizeAnalyticsText(componentSection) : '', 15612 detail: detail || 'generic', 15613 type: type || 'no-val', 15614 name: name || 'no-val', 15615 spot: spot === undefined ? placementParts.spot || 0 : spot, 15616 sponsored: sponsored === undefined || sponsored === '' ? 'no-val' : sponsored 15617 }]; 15618 } 15619 15620 this.getCRSProduct = function (product) { 15621 const productData = product || digitalData?.productInfo || {}; 15622 return { 15623 pId: productData.productID || productData.id || productData.itemId?.toString(), 15624 pName: productData.productName || productData.name, 15625 units: productData.units || productData.quantity || productData.qty, 15626 uTyp: productData.unitType || productData.unitQuantity, 15627 lp: productData.listPrice || productData.price, 15628 bp: productData.basePrice?.toString(), 15629 pType: productData.pricingType || productData.promoDescription || 'not available', 15630 cpnFlg: productData.couponFlag || 'not available', 15631 cpnClpd: productData.couponClipped || 'not available', 15632 cpnId: productData.couponID || 'not available', 15633 aId: productData.aisleId || '', 15634 upc: productData.upc, 15635 pfmDtl: productData.pfmDetail, 15636 mto: productData.mto || (!!productData.isMTO).toString(), 15637 deptNam: productData.departmentName, 15638 each: productData.each, 15639 aisle: productData.aisleName, 15640 shelf: productData.shelfName, 15641 iv: productData.listPrice?.toString() || productData.price?.toString(), 15642 isMHMD: !!productData.isProductTrackingDisabled 15643 }; 15644 } 15645 15646 this.getCRSCoupon = function (coupon) { 15647 return { 15648 type: coupon?.couponType, 15649 name: coupon?.couponName, 15650 id: coupon?.couponId, 15651 clipMthd: coupon?.couponclipmethod, 15652 carouselSec: coupon?.carouselSection, 15653 clipCnt: coupon?.numberofcouponsclipped, 15654 clipID: coupon?.clipID || coupon?.serverData?.eventId, 15655 clipTime: coupon?.clipTime || coupon?.serverData?.currentTime, 15656 divisionName: coupon?.divisionName, 15657 msgData: coupon?.msgData, 15658 savings: coupon?.couponSavings || '0' 15659 }; 15660 } 15661 15662 this.isCESEventsEnabled = function () { 15663 return window['SWY']?.CONFIGSERVICE?.featureToggleConfig?.enableCESEvents === true; 15664 }; 15665 15666 this.setCRSClickEvent = function ({ linkName, mTyp, element, placement, component, product, batchCouponsClipped, caasEnbld } = {}) { 15667 if (!AB.DATALAYER.isCESEventsEnabled() || !AB.DATALAYER.isHomePage() || !linkName) { 15668 return; 15669 } 15670 const resolvedPlacement = placement || digitalData?.pageFilter?.placement; 15671 const payload = { 15672 link: { linkName: linkName, isCRSClick: true, isSendToSS: true }, 15673 page: digitalData?.pageDetails, 15674 pageFilter: { mediaType: mTyp }, 15675 absPlacement: AB.DATALAYER.getCRSAbsPlacement({ element: element, placement: resolvedPlacement, caasEnbld: caasEnbld }), 15676 component: component || AB.DATALAYER.getCRSComponent({ element: element, placement: resolvedPlacement }), 15677 user: digitalData?.user 15678 }; 15679 if (product) { 15680 payload.product = AB.DATALAYER.getCRSProduct(product); 15681 } 15682 if (Array.isArray(batchCouponsClipped)) { 15683 payload.batchCouponsClipped = batchCouponsClipped.map(coupon => AB.DATALAYER.getCRSCoupon(coupon)); 15684 } 15685 this.pushEvent(this.EVENTS.LINK_CLICK, payload); 15686 } 15687 15688 this.setCRSAddToListClick = function ({ product, element, placement, component } = {}) { 15689 AB.DATALAYER.setCRSClickEvent({ 15690 linkName: 'add-to-list-click', 15691 mTyp: '', 15692 element: element, 15693 placement: placement, 15694 component: component, 15695 product: product 15696 }); 15697 } 15698 15699 this.setCRSPausePlayClick = function ({ action, element, placement, component } = {}) { 15700 AB.DATALAYER.setCRSClickEvent({ 15701 linkName: action === 'pause' ? 'pause-click' : 'play-click',
15702 mTyp: 'product-carousel', 15703 element: element, 15704 placement: placement, 15705 component: component 15706 }); 15707 } 15708 15709 this.setCRSRecipeClick = function ({ element, placement, component } = {}) { 15710 AB.DATALAYER.setCRSClickEvent({ 15711 linkName: 'item-click', 15712 mTyp: 'product-carousel', 15713 element: element, 15714 placement: placement, 15715 component: component 15716 }); 15717 } 15718 15719 this.getCRSHomeCarouselSelector = function () { 15720 return '.nextgen-abs-carousel-wrapper, .abs-carousel-wrapper, .abs-carousel, .mod-unit-carousel, .mod-unit-carousel-container, .slick-slider, coupon-carousel, loyalty-card-carousel-al-v1, .slick-coupon-carousel, .slick-product-carousel, .master-product-carousel, product-carousel-al-v1, meals-carousel, .slick-meal-carousel, .meal-carousel, .horizontal-video-carousel-wrapper, .marketplace-search-carousel-container, .nextgen-abs-carousel-proxy, .nextgen-banner-carousel, webgen-hero-canvas-carousel, .hero-carousel, webgen-bundles-carousel'; 15721 } 15722 15723 this.getCRSHomeCarouselElement = function (element) { 15724 return element?.closest?.(AB.DATALAYER.getCRSHomeCarouselSelector()); 15725 } 15726 15727 this.getCRSHomeArrowDirection = function (element) { 15728 const ariaLabel = element?.getAttribute?.('aria-label') || ''; 15729 const className = element?.className?.toString() || ''; 15730 const controlText = `${ariaLabel} ${className} ${element?.textContent || ''}`.toLowerCase(); 15731 return /prev|previous|back|left|slick-prev|carousel-control-prev|</.test(controlText) ? 'left' : 'right'; 15732 } 15733 15734 this.getCRSCarouselControlPlacement = function (element) { 15735 const carousel = AB.DATALAYER.getCRSHomeCarouselElement(element); 15736 const zoneInfo = AB.DATALAYER.getZoneInfo(carousel || element); 15737 let carouselRow = parseInt(zoneInfo?.row, 10) || 0; 15738 const zoneNumber = parseInt(zoneInfo?.zone, 10) || 0; 15739 15740 if (!carouselRow && carousel) { 15741 const allCarousels = Array.from(document.querySelectorAll(AB.DATALAYER.getCRSHomeCarouselSelector())) 15742 .filter(item => !item.parentElement?.closest?.(AB.DATALAYER.getCRSHomeCarouselSelector())); 15743 const visibleCarousels = AB.DATALAYER.findVisibleElement(allCarousels); 15744 carouselRow = visibleCarousels?.indexOf(carousel) + 1 || 1; 15745 } 15746 15747 const controlSpot = AB.DATALAYER.getCRSHomeArrowDirection(element) === 'left' ? 1 : 2; 15748 return `R${carouselRow < 10 ? `0${carouselRow}` : carouselRow}#S0${controlSpot}#ZONE${zoneNumber < 10 ? `0${zoneNumber}` : zoneNumber}`; 15749 } 15750 15751 this.getCRSCarouselControlTitle = function (element) { 15752 const carousel = AB.DATALAYER.getCRSHomeCarouselElement(element); 15753 return AB.DATALAYER.getAnalyticsCarouselTitle(element) 15754 || carousel?.closest?.('.master-product-carousel, .slick-product-carousel, .slick-coupon-carousel, .coupon-carousel, .meal-carousel, .nextgen-abs-carousel-wrapper, .abs-carousel-wrapper, .mod-unit-carousel, .nextgen-abs-carousel-proxy, .nextgen-banner-carousel, webgen-hero-canvas-carousel, .hero-carousel')?.querySelector?.('.hero-canvas__text-plate-title, .carousel-header-text, .carousel-nav__header-title, .meal-carousel-title, .flex-title, h2, h3')?.textContent?.trim() 15755 || carousel?.querySelector?.('.hero-canvas__text-plate-title, .carousel-header-text, .carousel-nav__header-title, .meal-carousel-title, .flex-title, h2, h3')?.textContent?.trim() 15756 || ''; 15757 } 15758 15759 this.getCRSHomeClickText = function (element) { 15760 const visibleText = element?.textContent?.replace(/\s+/g, ' ')?.trim(); 15761 const rawText = visibleText 15762 || element?.value 15763 || element?.getAttribute?.('aria-label') 15764 || element?.getAttribute?.('title') 15765 || element?.getAttribute?.('alt') 15766 || ''; 15767 return `${rawText}`.replace(/\s+/g, ' ').trim(); 15768 } 15769 15770 this.getCRSHomeClickLinkName = function (element) { 15771 const clickText = AB.DATALAYER.getCRSHomeClickText(element); 15772 const sanitizedText = AB.DATALAYER.sanitizeAnalyticsText(clickText); 15773 return `${sanitizedText || 'cta'}-click`; 15774 } 15775 15776 this.getCRSHomeArrowLinkName = function (element) { 15777 return AB.DATALAYER.getCRSHomeArrowDirection(element) === 'left' ? 'swipe-left-click' : 'swipe-right-click'; 15778 } 15779 15780 this.isCRSHomeCarouselArrow = function (element) { 15781 const carousel = AB.DATALAYER.getCRSHomeCarouselElement(element); 15782 if (!carousel) { 15783 return false;
15784 } 15785 const ariaLabel = element?.getAttribute?.('aria-label') || ''; 15786 const className = element?.className?.toString() || ''; 15787 const controlText = `${ariaLabel} ${className} ${element?.textContent || ''}`.toLowerCase(); 15788 return /slick-prev|slick-next|carousel-control-prev|carousel-control-next|\bprevious\b|\bnext\b|swipe-left|swipe-right|^<$|^>$/.test(controlText.trim()); 15789 } 15790 15791 this.getCRSHomeMediaType = function (element) { 15792 if (element?.closest?.('coupon-carousel, loyalty-card-carousel-al-v1, .slick-coupon-carousel, coupon-grid, .cms-coupon-grid')) { 15793 return 'deals-carousel'; 15794 } 15795 if (element?.closest?.('.slick-product-carousel, .master-product-carousel, product-carousel, product-carousel-v2, product-carousel-al-v1')) { 15796 return 'product-carousel'; 15797 } 15798 if (element?.closest?.('meals-carousel, .slick-meal-carousel, .meal-carousel')) { 15799 return 'meals-carousel'; 15800 } 15801 if (element?.closest?.('.horizontal-video-carousel-wrapper, horizontal-video-carousel-wrapper')) { 15802 return 'video-carousel'; 15803 } 15804 if (element?.closest?.('webgen-flex-module, .flex-container, .flex-card')) { 15805 return 'flex-module'; 15806 } 15807 if (element?.closest?.('webgen-bundles, webgen-bundles-carousel, .bundles-box, .shopbycategory, .hero-collections')) { 15808 return 'collections'; 15809 } 15810 if (element?.closest?.('.nextgen-banner-carousel, webgen-hero-canvas-carousel, .hero-carousel, .nextgen-banner-unit, .nextgen-banner-unit-proxy, .banner-unit, .mod-unit, .mod-unit-carousel, .mod-unit-carousel-container, .nextgen-abs-carousel-wrapper, .abs-carousel-wrapper, .abs-carousel')) { 15811 return 'banner'; 15812 } 15813 if (element?.closest?.('utility-bar-wrapper, utility-bar, .utility-bar-wrapper, .utility-bar, .special-occasions-modal__banner, .profile-completion-bar')) { 15814 return 'utility-bar'; 15815 } 15816 if (element?.closest?.('webgen-sticky-banner .sticky-banner__container, .sticky-banner__container')) { 15817 return 'sticky-banner'; 15818 } 15819 return 'collections'; 15820 } 15821 15822 this.getCRSHomeClickSpot = function (element) { 15823 const rowElement = element?.closest?.('.nextgen-abs-carousel-wrapper, .abs-carousel-wrapper, .mod-unit-carousel, .slick-slider, .slick-product-carousel, .master-product-carousel, .slick-coupon-carousel, coupon-carousel, loyalty-card-carousel-al-v1, .nextgen-banner-unit, .nextgen-banner-unit-proxy, .flex-card, .flex-container, webgen-bundles, webgen-bundles-carousel, .bundles-box, .shopbycategory, .title-header, .button.section, .image.section, .marker-component'); 15824 const interactiveSelector = 'a, button, [role="button"], input[type="button"], input[type="submit"]'; 15825 const clickables = rowElement ? Array.from(rowElement.querySelectorAll(interactiveSelector)).filter(item => item.offsetHeight > 0 && item.offsetWidth > 0) : []; 15826 const spot = clickables.indexOf(element) + 1; 15827 return spot > 0 ? spot : -1; 15828 } 15829 15830 this.getCRSHomeClickPlacement = function (element) { 15831 const isUtilityBar = !!element?.closest?.('utility-bar-wrapper, utility-bar, .utility-bar-wrapper, .utility-bar, .special-occasions-modal__banner, .profile-completion-bar'); 15832 const zoneInfo = AB.DATALAYER.getZoneInfo(element); 15833 const row = parseInt(zoneInfo?.row, 10) || 1; 15834 const spot = AB.DATALAYER.getCRSHomeClickSpot(element); 15835 const zone = isUtilityBar ? 99 : (parseInt(zoneInfo?.zone, 10) || 0); 15836 return `R${row < 10 ? `0${row}` : row}#S${spot < 10 ? `0${spot}` : spot}#ZONE${zone < 10 ? `0${zone}` : zone}`; 15837 } 15838 15839 this.getCRSHomeMetricDetail = function (element) { 15840 const dataElement = element?.closest?.('[data-zone-response], [data-flex-module-content], [data-bundles-content], [data-bundles-carousel-content], lazy-wrapper[data-impression-tracking], [data-impression-tracking]'); 15841 const zoneData = AB.DATALAYER.safeJSONParse(dataElement?.getAttribute?.('data-zone-response')) || {}; 15842 const flexData = AB.DATALAYER.safeJSONParse(dataElement?.getAttribute?.('data-flex-module-content')) || {}; 15843 const bundleData = AB.DATALAYER.safeJSONParse(dataElement?.getAttribute?.('data-bundles-content')) 15844 || AB.DATALAYER.safeJSONParse(dataElement?.getAttribute?.('data-bundles-carousel-content')) 15845 || {}; 15846 const containerItems = Array.isArray(flexData?.containerItems) ? flexData.containerItems : []; 15847 const bundleItems = Array.isArray(bundleData?.containerItems) ? bundleData.containerItems : []; 15848 const currentBundleId = element?.closest?.('.bundles-box')?.getAttribute?.('data-analytics-id'); 15849 const currentBundle = currentBundleId ? bundleItems.find(bundle => bundle?.bundleSlugId == currentBundleId) : null; 15850 return zoneData?.p13nAdobeMetrics?.metricA 15851 || zoneData?.adobeMetrics?.metricA 15852 || flexData?.p13nAdobeMetrics?.metricA 15853 || flexData?.adobeMetrics?.metricA 15854 || containerItems.find(item => item?.p13nAdobeMetrics?.metricA || item?.adobeMetrics?.metricA)?.p13nAdobeMetrics?.metricA 15855 || containerItems.find(item => item?.p13nAdobeMetrics?.metricA || item?.adobeMetrics?.metricA)?.adobeMetrics?.metricA 15856 || currentBundle?.p13nAdobeMetrics?.metricA 15857 || currentBundle?.adobeMetrics?.metricA 15858 || bundleData?.p13nAdobeMetrics?.metricA 15859 || bundleData?.adobeMetrics?.metricA 15860 || bundleItems.find(item => item?.p13nAdobeMetrics?.metricA || item?.adobeMetrics?.metricA)?.p13nAdobeMetrics?.metricA 15861 || bundleItems.find(item => item?.p13nAdobeMetrics?.metricA || item?.adobeMetrics?.metricA)?.adobeMetrics?.metricA 15862 || dataElement?.getAttribute?.('data-impression-tracking') 15863 || 'no-val'; 15864 } 15865 15866 this.getCRSHomeComponentSection = function (element) { 15867 const componentContainer = element?.closest?.('.nextgen-banner-unit, .nextgen-banner-unit-proxy, .flex-card, .mod-unit, .shopbycategory, .title-header, .button.section, .image.section, webgen-bundles, webgen-bundles-carousel, .bundles-box, .slick-product-carou
15867sel, .master-product-carousel, .slick-coupon-carousel, coupon-carousel, loyalty-card-carousel-al-v1, .meal-carousel, .nextgen-abs-carousel-wrapper, .abs-carousel-wrapper, .mod-unit-carousel, .nextgen-abs-carousel-proxy, .nextgen-banner-carousel, webgen-hero-canvas-carousel, .hero-carousel'); 15868 return AB.DATALAYER.getAnalyticsCarouselTitle(element) 15869 || element?.closest?.('[data-carousel-title]')?.getAttribute?.('data-carousel-title') 15870 || componentContainer?.querySelector?.('.hero-canvas__text-plate-title, .carousel-header-text, .carousel-nav__header-title, .meal-carousel-title, .flex-title, .flex-card__title, .bundles-content-header, [class*="title"], h1, h2, h3')?.textContent?.trim() 15871 || 'no-val'; 15872 } 15873 15874 this.getCRSHomeComponentName = function (element) { 15875 const componentContainer = element?.closest?.('.nextgen-banner-unit, .nextgen-banner-unit-proxy, .flex-card, .mod-unit, .shopbycategory, .title-header, .button.section, .image.section, webgen-bundles, webgen-bundles-carousel, .bundles-box, .slick-product-carousel, .master-product-carousel, .slick-coupon-carousel, coupon-carousel, loyalty-card-carousel-al-v1, .meal-carousel, .nextgen-abs-carousel-wrapper, .abs-carousel-wrapper, .mod-unit-carousel, .nextgen-abs-carousel-proxy, .nextgen-banner-carousel, webgen-hero-canvas-carousel, .hero-carousel'); 15876 const subtitle = componentContainer?.querySelector?.('.carousel-header-subtitle, .carousel-subtitle, .carousel-nav__subtitle, .carousel-nav .body-text-xs, .meal-carousel-subtitle, .hero-canvas__text-plate-subtitle, .flex-card__subtitle, .flex-subtitle, .bundles-content-description, .sub-title, .subtitle, [class*="subtitle"], [class*="sub-title"], p')?.textContent?.trim(); 15877 return subtitle ? AB.DATALAYER.sanitizeAnalyticsText(subtitle) : 'no-val'; 15878 } 15879 15880 this.isCRSHomeCaasEnabled = function (element) { 15881 return !!element?.closest?.('[data-api-driven], [data-carousel-driven], [data-abs-carousel-position], [data-abs-carousel-slot], [data-zone-response], [data-flex-module-content], [data-bundles-content], [data-bundles-carousel-content], lazy-wrapper[data-model-configuration-id], lazy-wrapper[data-carousel-filter="p13n"], targeted-wrapper, webgen-flex-module, webgen-bundles, webgen-bundles-carousel'); 15882 } 15883 15884 this.isCRSHomeMainContentClick = function (element) { 15885 const mainWrapper = document.querySelector('.nextgen-main-wrapper'); 15886 const isInMainWrapper = !!(mainWrapper && element && mainWrapper.contains(element) && !element?.closest?.('.unified-header, header, footer, .modal, [role="dialog"]')); 15887 const isUtilityBar = !!element?.closest?.('utility-bar-wrapper, utility-bar, .utility-bar-wrapper, .utility-bar, .special-occasions-modal__banner, .profile-completion-bar'); 15888 return isInMainWrapper || isUtilityBar; 15889 } 15890 15891 this.shouldSkipCRSGenericHomeClick = function (element) { 15892 const text = AB.DATALAYER.getCRSHomeClickText(element).toLowerCase(); 15893 const isKnownCTA = /^add\b|view\s*all|shop\s*now|learn\s*more|schedule|sign\s*in\s*to\s*add|sign\s*up|subscribe|see\s*all|reserve|join|start|explore|browse|save/.test(text); 15894 const isProductOrCouponCard = !!element?.closest?.('product-item, product-item-v2, product-item-al-v1, product-item-al-v2, product-carousel-item, product-carousel-item-v2, product-carousel-item-al-v1, product-carousel-item-al-v2, coupon-carousel-item, coupon-card, coupon-item, loyalty-card-al-v1'); 15895 return isProductOrCouponCard && !isKnownCTA; 15896 } 15897 15898 this.shouldSkipCRSAddToListClick = function (element) { 15899 return !!element?.closest?.('coupon-carousel-item, coupon-card, coupon-item, loyalty-card-al-v1, loyalty-non-digital-coupon-card-al-v1, ndc-offer-detail-al-v1'); 15900 } 15901 15902 this.setCRSHomeGenericClick = function (element) { 15903 const placement = AB.DATALAYER.getCRSHomeClickPlacement(element); 15904 AB.DATALAYER.setCRSClickEvent({ 15905 linkName: AB.DATALAYER.getCRSHomeClickLinkName(element), 15906 mTyp: AB.DATALAYER.getCRSHomeMediaType(element), 15907 element: element, 15908 placement: placement, 15909 caasEnbld: AB.DATALAYER.isCRSHomeCaasEnabled(element), 15910 component: AB.DATALAYER.getCRSComponent({ 15911 element: element, 15912 section: AB.DATALAYER.getCRSHomeComponentSection(element), 15913 detail: AB.DATALAYER.getCRSHomeMetricDetail(element), 15914 name: AB.DATALAYER.getCRSHomeComponentName(element), 15915 spot: -1, 15916 placement: placement 15917 }) 15918 }); 15919 } 15920 15921 this.getCRSHomeDomPath = function (element) { 15922 const path = []; 15923 let currentElement = element; 15924 while (currentElement && currentElement.nodeType === 1 && path.length < 5 && currentElement !== document.body) { 15925 const tagName = currentElement.tagName?.toLowerCase(); 15926 const dataQa = currentElement.getAttribute?.('data-qa'); 15927 const id = currentElement.id; 15928 if (id) { 15929 path.unshift(`${tagName}#${id}`); 15930 break; 15931 } 15932 if (dataQa) { 15933 path.unshift(`${tagName}[data-qa="${dataQa}"]`); 15934 break; 15935 } 15936 const parent = currentElement.parentElement; 15937 const siblings = parent ? Array.from(parent.children).filter(child => child.tagName === currentElement.tagName) : []; 15938 const index = siblings.indexOf(currentElement) + 1; 15939 path.unshift(`${tagName}${index > 1 ? `:nth-of-type(${index})` : ''}`); 15940 currentElement = parent; 15941 } 15942 return path.join('>'); 15943 } 15944 15945 this.getCRSHomeElementIdentifier = function (element) { 15946 const href = (element?.getAttribute?.('href') || '').split('?')[0]; 15947 return element?.id 15948 || element?.getAttribute?.('data-qa') 15949 || element?.getAttribute?.('data-analytics-id') 15950 || element?.getAttribute?.('aria-label') 15951 || href 15952 || AB.DATALAYER.getCRSHomeDomPath(element) 15953 || 'no-val'; 15954 } 15955 15956 this.getCRSHomeComponentDetails = function (element) { 15957 const accordionContainer = element?.closest?.('.accordion-v2-pds__top-level-header, webgen-accordion'); 15958 const accordionItem = element?.closest?.('.pds-accordion'); 15959 if (accordionItem && accordionContainer && !element?.closest?.('.accordion-v2-pds__top-level-header')) { 15960 return { name: 'Accordion Item', element: accordionItem }; 15961 } 15962 const componentRules = [ 15963 { name: 'Accordion Container', selector: 'webgen-accordion, .accordion-v2-pds' }, 15964 { name: 'Rich Text Editor', selector: 'webgen-rich-text, .rte-content' }, 15965 { name: 'Blog Component', selector: 'webgen-blog-component, .blog-component-wrapper_left-align, .blog-component-wrapper_right-align, .blog-component-recipe' }, 15966 { name: 'Bundle', selector: 'webgen-bundles, webgen-bundles-carousel, .bundles-box' }, 15967 { name: 'Collection', selector: 'webgen-hero-canvas-collections, .promo-container, .promo__card, .hero-collections, .shopbycategory' }, 15968 { name: 'Container Unit', selector: 'webgen-container-unit, .container-unit, .hero-canvas-flex' } 15969 ]; 15970 const matchingRule = componentRules.find(rule => element?.closest?.(rule.selector)); 15971 if (matchingRule) { 15972 return { name: matchingRule.name, element: element.closest(matchingRule.selector) }; 15973 } 15974 const closestHost = element?.closest?.('webgen-flex-module, webgen-hero-canvas-carousel, .nextgen-banner-unit, .nextgen-banner-unit-proxy, .flex-card, .mod-unit, .slick-product-carou
15974sel, .slick-coupon-carousel, .meal-carousel'); 15975 return { 15976 name: closestHost?.tagName?.toLowerCase?.() || closestHost?.className?.toString?.()?.split(' ')?.[0] || 'Home Content', 15977 element: closestHost || element 15978 }; 15979 } 15980 15981 this.getCRSHomeSafeCSSValue = function (element, attrName, cssProperty) { 15982 const rawValue = element?.getAttribute?.(attrName) || element?.style?.getPropertyValue?.(cssProperty) || ''; 15983 if (!rawValue) { 15984 return ''; 15985 } 15986 try { 15987 return window.CSS?.supports?.(cssProperty, rawValue) ? rawValue : ''; 15988 } catch (error) { 15989 return ''; 15990 } 15991 } 15992 15993 this.getCRSHomeUniversalPayload = function ({ element, action, linkName, placement, elementIdentifier } = {}) { 15994 const targetElement = element?.nodeType === 1 ? element : element?.parentElement; 15995 if (!targetElement) { 15996 return null; 15997 } 15998 const componentDetails = AB.DATALAYER.getCRSHomeComponentDetails(targetElement); 15999 const componentElement = componentDetails.element || targetElement; 16000 const isStickyBannerElement = targetElement?.closest?.('webgen-sticky-banner .sticky-banner__container, .sticky-banner__container'); 16001 const resolvedPlacement = isStickyBannerElement ? AB.DATALAYER.getStickyBannerPlacement(targetElement) : placement || AB.DATALAYER.getCRSHomeClickPlacement(targetElement); 16002 const mediaType = AB.DATALAYER.getCRSHomeMediaType(targetElement); 16003 const componentSection = AB.DATALAYER.getCRSHomeComponentSection(componentElement); 16004 const componentName = componentDetails.name || 'Home Content'; 16005 const contentText = AB.DATALAYER.getCRSHomeClickText(targetElement).substring(0, 120); 16006 const identifier = `${elementIdentifier || AB.DATALAYER.getCRSHomeElementIdentifier(targetElement)}`.substring(0, 160); 16007 const href = (targetElement?.closest?.('a')?.getAttribute?.('href') || targetElement?.getAttribute?.('href') || '').split('?')[0]; 16008 const timestamp = AB.DATALAYER.generateCurrentTime(); 16009 16010 return { 16011 link: { 16012 linkName: `${mediaType}-${linkName}` || (action === 'impression' ? 'impression' : 'click'), 16013 isCRSClick: true, 16014 isSendToSS: true 16015 }, 16016 page: { ...digitalData?.pageDetails }, 16017 pageFilter: { 16018 ...digitalData?.pageFilter, 16019 mediaType: mediaType 16020 }, 16021 absPlacement: AB.DATALAYER.getCRSAbsPlacement({ 16022 element: targetElement, 16023 placement: resolvedPlacement, 16024 caasEnbld: AB.DATALAYER.isCRSHomeCaasEnabled(targetElement) 16025 }), 16026 component: AB.DATALAYER.getCRSComponent({ 16027 element: componentElement, 16028 section: componentSection, 16029 detail: AB.DATALAYER.getCRSHomeMetricDetail(targetElement), 16030 type: componentName, 16031 name: AB.DATALAYER.getCRSHomeComponentName(componentElement), 16032 placement: resolvedPlacement, 16033 section: this.getCRSPlacementParts(placement).spot 16034 }), 16035 user: { ...digitalData?.user } 16036 }; 16037 } 16038 16039 this.isCRSHomeAuthorMode = function () { 16040 const wcmmode = new URLSearchParams(window.location?.search || '').get('wcmmode'); 16041 return wcmmode === 'edit' 16042 || (wcmmode !== 'disabled' && !!document.querySelector('.cq-Editor, .editor-GlobalBar, [data-cq-editor]')); 16043 } 16044 16045 this.isCRSHomeTrackingDisabledElement = function (element) { 16046 return !!element?.closest?.('[data-tracking-disabled]'); 16047 } 16048 16049 this.isHandledByExistingCRSLayer = function (element) { 16050 if (!element) { 16051 return false; 16052 } 16053 const pausePlayElement = element.closest?.('.svg-slick-play-icon, .svg-slick-pause-icon, .abs-carousel-wrapper__items__play-button, .abs-carousel-wrapper__items__pause-button, .mod-unit-play-button, .mod-unit-pause-button, .hero-carousel__control--play, .hero-carousel__control--pause, .svg-play-icon-with-background, .svg-pause-icon-with-background'); 16054 if (pausePlayElement) { 16055 return true; 16056 } 16057 const arrowElement = element.closest?.('.slick-prev, .slick-next, .carousel-control-prev, .carousel-control-next, button, a, [role="button"]'); 16058 if (arrowElement && AB.DATALAYER.isCRSHomeCarouselArrow(arrowElement)) { 16059 return true; 16060 } 16061 const productOrCouponElement = element.closest?.('product-item, product-item-v2, product-item-al-v1, product-item-al-v2, product-carou
16061sel-item, product-carousel-item-v2, product-carousel-item-al-v1, product-carousel-item-al-v2, coupon-carousel-item, coupon-card, coupon-item, loyalty-card-al-v1'); 16062 if (productOrCouponElement) { 16063 return true; 16064 } 16065 const addToListElement = element.closest?.('button, a, [role="button"]'); 16066 const addToListText = `${addToListElement?.getAttribute?.('aria-label') || ''} ${addToListElement?.textContent || ''}`.trim().toLowerCase(); 16067 if (addToListElement && /add\s+to\s+(my\s+)?list/.test(addToListText) && !AB.DATALAYER.shouldSkipCRSAddToListClick(addToListElement)) { 16068 return true; 16069 } 16070 const genericClickElement = element.closest?.('a, button, [role="button"], input[type="button"], input[type="submit"]'); 16071 return !!(genericClickElement 16072 && AB.DATALAYER.isCRSHomeMainContentClick(genericClickElement) 16073 && !AB.DATALAYER.shouldSkipCRSGenericHomeClick(genericClickElement)); 16074 } 16075 16076 this.pushCRSHomeUniversalEvent = function ({ element, action, linkName, placement, elementIdentifier } = {}) { 16077 if (!AB.DATALAYER.isCESEventsEnabled() 16078 || !AB.DATALAYER.isHomePage() 16079 || AB.DATALAYER.isB2BPage() 16080 || AB.DATALAYER.isCRSHomeAuthorMode() 16081 || AB.DATALAYER.isCRSHomeTrackingDisabledElement(element)) { 16082 return; 16083 } 16084 const payload = AB.DATALAYER.getCRSHomeUniversalPayload({ element: element, action: action, linkName: linkName, placement: placement, elementIdentifier: elementIdentifier }); 16085 if (payload) { 16086 AB.DATALAYER.pushEvent(AB.DATALAYER.EVENTS.CRS_CLICK, payload); 16087 } 16088 } 16089 16090 this.initCRSHomeUniversalClickLayer = function () { 16091 if (this.crsHomeUniversalClickLayerInitialized || !AB.DATALAYER.isHomePage()) { 16092 return; 16093 } 16094 this.crsHomeUniversalClickLayerInitialized = true; 16095 this.crsHomeUniversalClickHandler = function (event) { 16096 const originalTarget = event.target?.nodeType === 1 ? event.target : event.target?.parentElement; 16097 if (!AB.DATALAYER.isHomePage()) { 16098 AB.DATALAYER.cleanupCRSHomeUniversalTracking(); 16099 return; 16100 } 16101 const trackedElement = originalTarget?.closest?.('a, button, [role="button"], input, select, textarea, label, summary, [tabindex]') || originalTarget; 16102 if (!AB.DATALAYER.isCRSHomeMainContentClick(originalTarget) 16103 || AB.DATALAYER.isCRSHomeAuthorMode() 16104 || AB.DATALAYER.isCRSHomeTrackingDisabledElement(trackedElement) 16105 || AB.DATALAYER.isHandledByExistingCRSLayer(trackedElement)) { 16106 return; 16107 } 16108 const placement = AB.DATALAYER.getCRSHomeClickPlacement(trackedElement); 16109 const elementIdentifier = AB.DATALAYER.getCRSHomeElementIdentifier(trackedElement); 16110 const dedupeKey = `click|${elementIdentifier}|${placement}`; 16111 const currentTime = Date.now(); 16112 if (AB.DATALAYER.lastCRSHomeUniversalClick?.key === dedupeKey && currentTime - AB.DATALAYER.lastCRSHomeUniversalClick.time < 500) { 16113 return; 16114 } 16115 AB.DATALAYER.lastCRSHomeUniversalClick = { key: dedupeKey, time: currentTime }; 16116 // ACIP-576827: piggyback on the existing CRS-Home click (which carries V151-V154)
16117 // to persist V218/V80 for the next pageLoaded when a blog CTA navigates away. 16118 // Same-page (empty/hash) hrefs are skipped per spec. 16119 const blogHost = trackedElement?.closest?.('webgen-blog-component, .blog-component-wrapper_left-align, .blog-component-wrapper_right-align, .blog-component-recipe'); 16120 if (blogHost) { 16121 const blogHref = (trackedElement?.getAttribute?.('href') || '').trim(); 16122 const isNavigating = trackedElement?.tagName?.toLowerCase?.() === 'a' 16123 && blogHref && blogHref !== '#' && !blogHref.startsWith('#') 16124 && !trackedElement.hasAttribute?.('disabled'); 16125 if (isNavigating) { 16126 AB.DATALAYER.setBlogComponentClickAnalytics(blogHost, trackedElement); 16127 } 16128 } 16129 AB.DATALAYER.pushCRSHomeUniversalEvent({ 16130 element: trackedElement, 16131 action: 'click', 16132 linkName: 'click', 16133 placement: placement, 16134 elementIdentifier: elementIdentifier 16135 }); 16136 }; 16137 document.addEventListener('click', this.crsHomeUniversalClickHandler, true); 16138 } 16139 16140 this.getCRSHomeUniversalImpressionKey = function (element) { 16141 const pageKey = this.crsHomeUniversalImpressionPageKey || digitalData?.pageDetails?.pageUrl || window.location?.pathname || 'home'; 16142 const elementKey = AB.DATALAYER.getCRSHomeElementIdentifier(element); 16143 return `${pageKey}|${elementKey}`; 16144 } 16145 16146 this.initCRSHomeUniversalImpressions = function () { 16147 if (!AB.DATALAYER.isCESEventsEnabled() || !AB.DATALAYER.isHomePage() || AB.DATALAYER.isCRSHomeAuthorMode()) { 16148 return; 16149 } 16150 const mainWrapper = document.querySelector('.nextgen-main-wrapper'); 16151 if (!mainWrapper) { 16152 return; 16153 } 16154 const impressionElements = mainWrapper.querySelectorAll('webgen-accordion, .accordion-v2-pds, .accordion-v2-pds .pds-accordion, webgen-rich-text, webgen-blog-component, .blog-component-wrapper_left-align, .blog-component-wrapper_right-align, webgen-container-unit'); 16155 if (!impressionElements || impressionElements.length === 0) { 16156 return; 16157 } 16158 if (!this.crsHomeUniversalImpressionObserver && typeof IntersectionObserver !== 'undefined') { 16159 this.crsHomeUniversalImpressionObserver = new IntersectionObserver((entries, observer) => { 16160 if (!AB.DATALAYER.isHomePage()) { 16161 AB.DATALAYER.cleanupCRSHomeUniversalTracking(); 16162 return; 16163 } 16164 entries.forEach(entry => { 16165 if (entry?.isIntersecting && entry.intersectionRatio >= 0.5) { 16166 const element = entry.target; 16167 const impressionKey = AB.DATALAYER.getCRSHomeUniversalImpressionKey(element); 16168 if (element?.dataset?.crsUniversalImpressionTracked === impressionKey 16169 || AB.DATALAYER.isCRSHomeTrackingDisabledElement(element) 16170 || AB.DATALAYER.isCRSHomeAuthorMode()) { 16171 observer.unobserve(element); 16172 return; 16173 } 16174 AB.DATALAYER.pushCRSHomeUniversalEvent({ 16175 element: element, 16176 action: 'impression', 16177 linkName: 'impressions', 16178 placement: AB.DATALAYER.getCRSHomeClickPlacement(element), 16179 elementIdentifier: AB.DATALAYER.getCRSHomeElementIdentifier(element) 16180 }); 16181 element.dataset.crsUniversalImpressionTracked = impressionKey; 16182 observer.unobserve(element); 16183 } 16184 }); 16185 }, { 16186 root: null, 16187 rootMargin: '0px', 16188 threshold: [0.5] 16189 }); 16190 } 16191 impressionElements.forEach(element => { 16192 const impressionKey = AB.DATALAYER.getCRSHomeUniversalImpressionKey(element); 16193 if (element?.dataset?.crsUniversalObserverAttached === impressionKey 16194 || element?.dataset?.crsUniversalImpressionTracked === impressionKey 16195 || AB.DATALAYER.isCRSHomeTrackingDisabledElement(element)) { 16196 return; 16197 } 16198 if (this.crsHomeUniversalImpressionObserver) { 16199 this.crsHomeUniversalImpressionObserver.observe(element); 16200 element.dataset.crsUniversalObserverAttached = impressionKey; 16201 } else { 16202 AB.DATALAYER.pushCRSHomeUniversalEvent({ 16203 element: element, 16204 action: 'impression', 16205 linkName: 'impressions', 16206 placement: AB.DATALAYER.getCRSHomeClickPlacement(element), 16207 elementIdentifier: AB.DATALAYER.getCRSHomeElementIdentifier(element) 16208 }); 16209 element.dataset.crsUniversalImpressionTracked = impressionKey; 16210 } 16211 }); 16212 } 16213 16214 this.cleanupCRSHomeUniversalTracking = function () { 16215 if (this.crsHomeUniversalClickHandler) { 16216 document.removeEventListener('click', this.crsHomeUniversalClickHandler, true); 16217 this.crsHomeUniversalClickHandler = null; 16218 } 16219 this.crsHomeUniversalClickLayerInitialized = false;
16220 16221 if (this.crsHomeUniversalImpressionObserver) { 16222 this.crsHomeUniversalImpressionObserver.disconnect(); 16223 this.crsHomeUniversalImpressionObserver = null; 16224 } 16225 if (this.crsHomeUniversalMutationObserver) { 16226 this.crsHomeUniversalMutationObserver.disconnect(); 16227 this.crsHomeUniversalMutationObserver = null; 16228 } 16229 clearTimeout(this.crsHomeUniversalMutationTimer); 16230 this.crsHomeUniversalMutationTimer = null; 16231 this.crsHomeUniversalImpressionPageKey = null; 16232 document.querySelectorAll('[data-crs-universal-observer-attached]').forEach(element => { 16233 delete element.dataset.crsUniversalObserverAttached; 16234 }); 16235 } 16236 16237 this.initCRSHomeUniversalRouteLifecycle = function () { 16238 if (this.crsHomeUniversalRouteLifecycleInitialized) { 16239 return; 16240 } 16241 this.crsHomeUniversalRouteLifecycleInitialized = true; 16242 this.crsHomeUniversalRouteHandler = function () { 16243 clearTimeout(AB.DATALAYER.crsHomeUniversalRouteTimer); 16244 AB.DATALAYER.crsHomeUniversalRouteTimer = setTimeout(() => { 16245 if (AB.DATALAYER.isHomePage()) { 16246 AB.DATALAYER.initCRSHomeUniversalTracking(); 16247 } else { 16248 AB.DATALAYER.cleanupCRSHomeUniversalTracking(); 16249 } 16250 }, 0); 16251 }; 16252 window.addEventListener('popstate', this.crsHomeUniversalRouteHandler); 16253 16254 if (window.history && !window.history.crsHomeUniversalWrapped) { 16255 const originalPushState = history.pushState; 16256 const originalReplaceState = history.replaceState; 16257 history.pushState = function () { 16258 const result = originalPushState.apply(this, arguments); 16259 AB.DATALAYER.crsHomeUniversalRouteHandler?.(); 16260 return result; 16261 }; 16262 history.replaceState = function () { 16263 const result = originalReplaceState.apply(this, arguments); 16264 AB.DATALAYER.crsHomeUniversalRouteHandler?.(); 16265 return result; 16266 }; 16267 window.history.crsHomeUniversalWrapped = true; 16268 } 16269 } 16270 16271 this.initCRSHomeUniversalTracking = function () { 16272 AB.DATALAYER.initCRSHomeUniversalRouteLifecycle(); 16273 if (!AB.DATALAYER.isHomePage()) { 16274 AB.DATALAYER.cleanupCRSHomeUniversalTracking(); 16275 return; 16276 } 16277 if (AB.DATALAYER.isCRSHomeAuthorMode()) { 16278 AB.DATALAYER.cleanupCRSHomeUniversalTracking(); 16279 return; 16280 } 16281 this.crsHomeUniversalImpressionPageKey = this.crsHomeUniversalImpressionPageKey 16282 || `${digitalData?.pageDetails?.pageUrl || window.location?.pathname || 'home'}|${Date.now()}`; 16283 AB.DATALAYER.initCRSHomeUniversalClickLayer(); 16284 AB.DATALAYER.initCRSHomeUniversalImpressions(); 16285 const mainWrapper = document.querySelector('.nextgen-main-wrapper'); 16286 if (this.crsHomeUniversalMutationObserver || !mainWrapper || typeof MutationObserver === 'undefined') { 16287 return; 16288 } 16289 this.crsHomeUniversalMutationObserver = new MutationObserver(() => { 16290 if (!AB.DATALAYER.isHomePage()) { 16291 AB.DATALAYER.cleanupCRSHomeUniversalTracking(); 16292 return; 16293 } 16294 clearTimeout(this.crsHomeUniversalMutationTimer); 16295 this.crsHomeUniversalMutationTimer = setTimeout(() => { 16296 if (!AB.DATALAYER.isHomePage()) { 16297 AB.DATALAYER.cleanupCRSHomeUniversalTracking(); 16298 return; 16299 } 16300 AB.DATALAYER.initCRSHomeUniversalImpressions(); 16301 }, 100); 16302 }); 16303 this.crsHomeUniversalMutationObserver.observe(mainWrapper, { childList: true, subtree: true }); 16304 } 16305 16306 this.setCRSHomeArrowClick = function (element) { 16307 const placement = AB.DATALAYER.getCRSCarouselControlPlacement(element); 16308 const linkName = AB.DATALAYER.getCRSHomeArrowLinkName(element); 16309 const dedupeKey = `${linkName}|${placement}|${AB.DATALAYER.getCRSHomeMediaType(element)}`; 16310 const currentTime = Date.now(); 16311 if (AB.DATALAYER.lastCRSHomeArrowClick?.key === dedupeKey && currentTime - AB.DATALAYER.lastCRSHomeArrowClick.time < 500) { 16312 return; 16313 } 16314 AB.DATALAYER.lastCRSHomeArrowClick = { key: dedupeKey, time: currentTime }; 16315 16316 AB.DATALAYER.setCRSClickEvent({ 16317 linkName: linkName, 16318 mTyp: AB.DATALAYER.getCRSHomeMediaType(element), 16319 element: element, 16320 placement: placement, 16321 caasEnbld: AB.DATALAYER.isCRSHomeCaasEnabled(element), 16322 component: AB.DATALAYER.getCRSComponent({ 16323 element: element, 16324 section: AB.DATALAYER.getCRSCarouselControlTitle(element), 16325 detail: AB.DATALAYER.getCRSHomeMetricDetail(element), 16326 spot: -1, 16327 placement: placement 16328 }) 16329 }); 16330 } 16331 16332 this.initCRSHomeClickLayer = function () { 16333 // Keep this legacy CRS layer document-bound across SPA navigation; only the universal layer is cleaned up per route. 16334 if (this.crsHomeClickLayerInitialized || !AB.DATALAYER.isHomePage()) { 16335 return; 16336 } 16337 this.crsHomeClickLayerInitialized = true; 16338 document.addEventListener('click', function (event) { 16339 if (!AB.DATALAYER.isHomePage()) { 16340 return; 16341 } 16342 const target = event.target; 16343 if (!AB.DATALAYER.isCRSHomeMainContentClick(target)) { 16344 return; 16345 } 16346 16347 const pausePlayElement = target?.closest?.('.svg-slick-play-icon, .svg-slick-pause-icon, .abs-carousel-wrapper__items__play-button, .abs-carousel-wrapper__items__pause-button, .mod-unit-play-button, .mod-unit-pause-button, .hero-carousel__control--play, .hero-carousel__control--pause, .svg-play-icon-with-background, .svg-pause-icon-with-background'); 16348 if (pausePlayElement) { 16349 const ariaLabel = pausePlayElement.getAttribute('aria-label') || ''; 16350 const className = pausePlayElement.className?.toString() || ''; 16351 const action = ariaLabel.toLowerCase().indexOf('play') !== -1 || className.indexOf('play') !== -1 ? 'play' : 'pause'; 16352 const placement = AB.DATALAYER.getCRSCarouselControlPlacement(pausePlayElement); 16353 AB.DATALAYER.setCRSPausePlayClick({ 16354 action: action, 16355 element: pausePlayElement, 16356 placement: placement, 16357 component: AB.DATALAYER.getCRSComponent({ 16358 element: pausePlayElement, 16359 section: AB.DATALAYER.getCRSCarouselControlTitle(pausePlayElement), 16360 placement: placement 16361 }) 16362 }); 16363 return; 16364 } 16365 16366 const arrowElement = target?.closest?.('.slick-prev, .slick-next, .carousel-control-prev, .carousel-control-next, button, a, [role="button"]'); 16367 if (arrowElement && AB.DATALAYER.isCRSHomeCarouselArrow(arrowElement)) { 16368 AB.DATALAYER.setCRSHomeArrowClick(arrowElement); 16369 return; 16370 } 16371 16372 const addToListElement = target?.closest?.('button, a, [role="button"]'); 16373 const addToListText = `${addToListElement?.getAttribute?.('aria-label') || ''} ${addToListElement?.textContent || ''}`.trim().toLowerCase(); 16374 if (addToListElement && /add\s+to\s+(my\s+)?list/.test(addToListText) && !AB.DATALAYER.shouldSkipCRSAddToListClick(addToListElement)) { 16375 const placement = localStorage.getItem('analyticsProductPlacement') || digitalData?.pageFilter?.placement || AB.DATALAYER.setCouponPlacement({}, addToListElement, 'add-to-list'); 16376 AB.DATALAYER.setCRSAddToListClick({ 16377 product: digitalData?.productInfo, 16378 element: addToListElement, 16379 placement: placement, 16380 component: AB.DATALAYER.getCRSComponent({ 16381 element: addToListElement, 16382 placement: placement 16383 }) 16384 }); 16385 return; 16386 } 16387 16388 const genericClickElement = target?.closest?.('a, button, [role="button"], input[type="button"], input[type="submit"]'); 16389 if (genericClickElement && AB.DATALAYER.isCRSHomeMainContentClick(genericClickElement) && !AB.DATALAYER.shouldSkipCRSGenericHomeClick(genericClickElement)) { 16390 AB.DATALAYER.setCRSHomeGenericClick(genericClickElement); 16391 } 16392 }); 16393 } 16394 16395 /** 16396 * Global click layer for flex banner tracking 16397 * Server-rendered templates (AEM HTML / handlebars) already wire these 16398 * handlers via inline onclick attributes, so we skip any anchor that 16399 * already has one to avoid double-firing on those pages. 16400 */ 16401 this.initFlexBannerClickLayer = function () { 16402 if (this.flexBannerClickLayerInitialized) { 16403 return; 16404 } 16405 this.flexBannerClickLayerInitialized = true; 16406 16407 document.addEventListener('click', function (event) { 16408 const target = event.target; 16409 const link = target && target.closest ? target.closest('a') : null; 16410 if (!link) { 16411 return; 16412 } 16413 16414 // Server-rendered templates already call the legacy handlers via
16415 // inline onclick. Skip those anchors to prevent duplicate events. 16416 if (typeof link.hasAttribute === 'function' && link.hasAttribute('onclick')) { 16417 return; 16418 } 16419 16420 const inMerch = link.closest('.flex-medium-fw') || link.closest('search-grid-merch-banner'); 16421 const inHero = link.closest('.hero-flex__card') || link.closest('.hero-canvas-row__flex'); 16422 const inFlexCard = link.closest('.flex-container') || link.closest('.flex-card'); 16423 16424 if (!inMerch && !inHero && !inFlexCard) { 16425 return; 16426 } 16427 16428 if (inHero) { 16429 AB.DATALAYER.setHeroCardsTracking(link); 16430 } else if (inMerch) { 16431 AB.DATALAYER.setMerchFlexBannerTracking(link); 16432 } else { 16433 AB.DATALAYER.setFlexBannerClickTracking(link); 16434 } 16435 }); 16436 }; 16437 16438 16439 /** 16440 * Manage add to seller for both matrketplace and wineshop 16441 * @param {*} sellerType Marketplace or Wineshop 16442 */ 16443 this.addedToSellerTypeModalTracking = function (sellerType) { 16444 this.setModalViewEvents(`${sellerType === 'marketplace' ? 'mkp:added-to-seller-order' : 'added-to-wine-shop'}|view`); 16445 document.querySelector('.modal-footer__action-btn__cart')?.addEventListener('click', () => { 16446 sellerType === 'marketplace' ? 16447 this.setLSItem('analyticsMkpGoToCart', 'mkp:added-to-seller-order|go-to-cart') : 16448 this.setModalClickEvents('added-to-wine-shop|go-to-cart'); 16449 }); 16450 } 16451 16452 this.mkpCrossSellersNoResultsClicks = function () { 16453 document.querySelector('search-grid .mkp-cross-seller-no-results a')?.addEventListener('click', function () { 16454 localStorage.setItem('analyticsCrossSellersNoResults', 'cta:cross-sellers-null-search|button|search-on-banner'); 16455 }); 16456 } 16457 16458 this.sanitizeTitlePicksForYouAndP13n = function (title) { 16459 const transformedTitle = title.trim().toLowerCase().split(' ').join('-').replace(/[^\w-,|#:]/gi, '').replace('--', '-'); 16460 return transformedTitle === 'picks-for-you' ? 'picked-for-you' : transformedTitle; 16461 } 16462 16463 /** 16464 * Sets link click event when clicking on View Similar Items for Out of stock products 16465 * @param product 16466 */ 16467 this.setViewSimilarItemsClick = function (product) { 16468 if (product?.itemNotAvailableTxt?.toLowerCase() === 'out of stock') { 16469 this.setLinkClickEvents('bia-oos:similar-items'); 16470 } else { 16471 this.removeLSItem('aaBiaSimilarItemsMetrics'); 16472 } 16473 } 16474 16475 /** 16476 * Check if a BIA product is OOS 16477 * @param product 16478 * @returns true is the product is OOS, false otherwise 16479 */ 16480 this.isBiaItemOos = function (product) { 16481 const { status, price, channelEligibility, channelInventory } = product; 16482 const userChannel = this.getUserChannel(); 16483 const channelKeyName = userChannel === 'pickup' ? 'pickUp' : userChannel === 'instore' ? 'inStore' : userChannel; 16484 const isProductEligible = channelEligibility && channelEligibility[channelKeyName] 16485 ? channelEligibility[channelKeyName] 16486 : false; 16487 16488 if (status === 'inactive' || price <= 0) { 16489 return true; 16490 } 16491 if (isProductEligible && channelInventory?.[userChannel]) { 16492 return ['', null, undefined, '0'].includes(channelInventory[userChannel]) && ['pickup', 'delivery', 'shipping'].includes(userChannel); 16493 } else { 16494 return !(channelEligibility?.['delivery'] || channelEligibility?.['inStore'] || channelEligibility?.['pickUp']) 16495 } 16496 } 16497 16498 /** 16499 * Gets adobeMetrics for cross sell component on BIA landing page and stores them in local storage for later use in cart add actions 16500 * @param products 16501 */ 16502 this.getBiaOosAdobeMetrics = function (products) { 16503 if (products && products.length) { 16504 const productsObj = {}; 16505 products.forEach(product => { 16506 if (product.adobeMetrics) { 16507 const productId = product.id; 16508 productsObj[productId] = product.adobeMetrics; 16509 } 16510 }); 16511 16512 if (Object.keys(productsObj).length > 0) { 16513 const productsString = JSON.stringify(productsObj); 16514 this.setLSItem('aaBiaSimilarItemsMetrics', productsString); 16515 } 16516 } 16517 } 16518 16519 /** 16520 * Sets impressionAdditionalDetail, subsAdditionalDetail and pfmSorce for Similar Items on BIA on add to cart 16521 */ 16522 this.similarItemsBiaAddToCart = function () { 16523 if (this.isBIAPage()) { 16524 const lsAdobeMetrics = this.getLSItem('aaBiaSimilarItemsMetrics'); 16525 if (lsAdobeMetrics && digitalData?.productInfo?.isCrossSell) { 16526 const currentProductId = digitalData.productInfo.productID; 16527 const lsAdobeMetricsParsed = this.safeJSONParse(lsAdobeMetrics); 16528 const adobeMetrics = currentProductId ? lsAdobeMetricsParsed[currentProductId] : ''; 16529 digitalData.productInfo.impressionAdditionalDetail = adobeMetrics?.metricA || 'no-value#no-value#no-value#no-value#no-value#no-value'; 16530 digitalData.productInfo.subsAdditionalDetail = adobeMetrics?.metricB || 'no-value#no-value#no-value#no-value#no-value#no-value#no-value#no-value#no-value#no-value'; 16531 digitalData.pageFilter.pfmSource = 'similar-items_buy-it-again'; 16532 } 16533 } 16534 } 16535 16536 /** 16537 * Sets impressionAdditionalDetail, and pfmSorce for BIA carousal product on PDP add to cart 16538 */ 16539 this.biaCarousalProductPDPAddToCart = function () { 16540 if (this.isPdp() && this.getLSItem('analyticsBIAPfmSource') && this.getLSItem('analyticsBIAPfmSource') !== 'undefined' && this.getLSItem('analyticsBIAProductImpressionAdDetail') && this.getLSItem('analyticsBIAProductImpressionAdDetail') !== 'undefined' && this.getLSItem('analyticsIsBIACarousal') && this.getLSItem('analyticsIsBIACarousal') !== 'undefined' && this.getLSItem('analyticsBIACarousalProductId') == digitalData?.productInfo?.productID) { 16541 digitalData.pageFilter.pfmSource = this.getLSItem('analyticsBIAPfmSource'); 16542 digitalData.productInfo.impressionAdditionalDetail = this.getLSItem('analyticsBIAProductImpressionAdDetail'); 16543 digitalData.productInfo.isBIACarousalProduct = this.getLSItem('analyticsIsBIACarousal') === 'true' ? true : false;
16544 } 16545 } 16546 16547 // add carousel name to LS 16548 this.setCarouselNameRecipes = function (element) { 16549 const carouselName = element?.querySelector('.recipe-carousel')?.querySelector('h2.carousel-header-text')?.textContent || element?.querySelector('.related-recipe-carousel')?.querySelector('.coupons-text-wrapper h2')?.textContent || ''; 16550 if (carouselName) { 16551 localStorage.setItem('analyticsCarouselName', 'recipes|' + carouselName); 16552 } 16553 } 16554 16555 // set digitalData.recipes on recipe details pages 16556 this.initRecipeDetails = function () { 16557 if (digitalData?.pageDetails?.subSection2 && digitalData?.pageDetails?.subSection2?.indexOf('detail') != -1) { 16558 const recipeTitle = document.querySelector('h2#recipeTitle')?.textContent.trim(); 16559 digitalData.recipes = { 16560 name: recipeTitle || '', 16561 source: digitalData.pageDetails.subSection3 16562 } 16563 } 16564 } 16565 16566 /** 16567 * Sets ls item when clicking on sns link from product cards 16568 * Used for setting topNav on the next page 16569 * @param snsOffer 16570 */ 16571 this.setSnsLinkClick = function (snsOffer) { 16572 if (digitalData?.pageDetails) { 16573 const subSections = [digitalData.pageDetails.subSection1, digitalData.pageDetails.subSection2, digitalData.pageDetails.subSection3, digitalData.pageDetails.subSection4].filter(
16573Boolean).join(':'); 16574 const snsLink = `cta:${subSections}|link|schdule-and-save-${snsOffer?.replace('%', '')}-percent`; 16575 if (window.location?.pathname?.startsWith('/rewards/point-reward-details')) { 16576 AB.DATALAYER.setLinkClickEvents('cta:rewards-dashboard|link|rewards:schedule-and-save'); 16577 } 16578 this.setLSItem('analyticsSNSlink', snsLink); 16579 } 16580 } 16581 16582 /** 16583 * Sets ls item when clicking on FAQs link on sns modals on PDP 16584 * Used for setting topNav on the next page 16585 * @param dataConfig 16586 */ 16587 this.setSnsModalLinkClick = function (link, dataConfig) { 16588 if (this.isPdp() && digitalData?.pageDetails) { 16589 const subSections = [digitalData.pageDetails.subSection1, digitalData.pageDetails.subSection2, digitalData.pageDetails.subSection3, digitalData.pageDetails.subSection4].filter(Boolean).join(':'); 16590 if (link === 'manage') { 16591 this.setLSItem('analyticsSNSlink', `cta:${subSections}|link|modal-manage-ss`); 16592 } else { 16593 const linkName = dataConfig?.modal === 'infoModal' 16594 ? 'link' 16595 : dataConfig?.modal === 'freqModal' ? 'frequency:link' : 'adding-to-recurring-order:link'; 16596 const faqsLink = `cta:${subSections}|${linkName}|faq-and-terms`; 16597 this.setLSItem('analyticsSNSlink', faqsLink); 16598 } 16599 } 16600 } 16601 16602 /** 16603 * Sets modalView event for S&S modals on PDP 16604 * @param data 16605 * @param product 16606 * @param isChecked 16607 */ 16608 this.setSnsModalViewEvents = function (data, product, isChecked, selectedFrequency) { 16609 if (data) { 16610 const { modal, productInfo } = digitalData; 16611 let subscribe = ''; 16612 let frequency = ''; 16613 switch (data.modal) { 16614 case 'infoModal': 16615 if (this.isPdp() || this.isCartAndCheckoutPage('erums/cart')) { 16616 modal.modalAction = 'ar:learn-more|view'; 16617 subscribe = 'unsubscribe'; 16618 frequency = isChecked === false 16619 ? 'no-frequency' 16620 : selectedFrequency === 1 ? 'every-week' : `every-${selectedFrequency}-weeks`; 16621 this.setLSItem('analyticsInfoModalSubscribe', subscribe); 16622 } 16623 break; 16624 case 'freqModal': 16625 if (this.isPdp()) { 16626 modal.modalAction = 'ar:frequency|view'; 16627 frequency = data.selectedFrequency === 1 ? 'every-week' : `every-${data.selectedFrequency}-weeks`; 16628 } 16629 subscribe = 'unsubscribe'; 16630 break; 16631 case 'addRecurringModal': 16632 modal.modalAction = `ar:adding-to-recurring-order${this.isPdp() ? '-pdp' : '-plp'}|view`; 16633 subscribe = 'unsubscribe'; 16634 frequency = 'every-week'; 16635 modal.modalMessage = `cta:schedule-and-save-5-percent|button|${this.isPdp() ? 'pdp' : 'plp'}`; 16636 break; 16637 case 'successModal': 16638 modal.modalAction = `ar:item-scheduled${this.isPdp() ? '' : '-plp'}|view`; 16639 subscribe = 'subscribe'; 16640 frequency = data.selectedFrequency === 1 ? 'every-week' : `every-${data.selectedFrequency}-weeks`; 16641 modal.modalMessage = this.isPdp() ? 'cta:schedule-and-save-5-percent|schedule-it|pdp' : ''; 16642 break; 16643 default: 16644 break; 16645 } 16646 productInfo.productID = product ? product.id : data.productId; 16647 productInfo.scheduleandSaveSource = `${this.isPdp() ? 'pdp' : 'plp'}#${subscribe}#${frequency}`; 16648 const eventSent = this.setEventOncePerPage(modal.modalAction, 'modalView', this.isPdp() ? undefined : productInfo.productID); 16649 // on PDP don't send 'ar:item-scheduled|view' event 16650 const shouldSendEvent = !(this.isPdp() && modal.modalAction === 'ar:item-scheduled|view');
16651 if (!eventSent && shouldSendEvent) { 16652 this.pushEvent( 16653 this.EVENTS.MODAL_VIEW, 16654 { 16655 modal: digitalData?.modal, 16656 productInfo: digitalData?.productInfo, 16657 page: digitalData?.pageDetails, 16658 pageFilter: digitalData?.pageFilter 16659 } 16660 ); 16661 } 16662 } 16663 } 16664 16665 /** 16666 * Sets linkClick event when user checks/unchecks the S&S checkbox on PDP 16667 * @param isChecked 16668 * @param product 16669 * @param frequency 16670 */ 16671 this.snsCheckboxClick = function (isChecked, product, frequency) { 16672 if (this.isPdp()) { 16673 const subscribe = 'unsubscribe'; 16674 const linkName = `ar:${isChecked ? '' : 'un'}subscribe-schedule-and-save`; 16675 const snsFrequency = frequency === 1 ? 'every-week' : `every-${frequency}-weeks`; 16676 digitalData.crossPageData.snsAnalyticsFrequency = digitalData.crossPageData.snsAnalyticsFrequency || {}; 16677 digitalData.crossPageData.snsAnalyticsFrequency.data = digitalData.crossPageData.snsAnalyticsFrequency.data || {}; 16678 digitalData.crossPageData.snsAnalyticsFrequency.data[product?.id] = snsFrequency; 16679 AB.DATALAYER.setAnalyticsCrossPageData({ action: 'set', name: `snsAnalyticsFrequency`, data: digitalData.crossPageData.snsAnalyticsFrequency.data, isAcrossPages: true }); 16680 const eventSent = this.setEventOncePerPage(linkName, 'linkCl
16680ick'); 16681 if (!eventSent) { 16682 this.pushEvent( 16683 this.EVENTS.LINK_CLICK, 16684 { 16685 link: { 16686 linkName: linkName 16687 }, 16688 productInfo: { 16689 productID: product?.id, 16690 scheduleandSaveSource: `pdp#${subscribe}#${snsFrequency}` 16691 }, 16692 page: digitalData?.pageDetails, 16693 pageFilter: digitalData?.pageFilter 16694 } 16695 ); 16696 } 16697 } 16698 } 16699 16700 /** 16701 * Sets modalClick events for S&S modals on PDP 16702 * @param modalAction 16703 * @param data 16704 * @param formModel 16705 * @param recurringFrequency 16706 */ 16707 this.setSnsModalClickEvents = function (modalAction, data, formModel, recurringFrequency) { 16708 const { modal, productInfo } = digitalData; 16709 const eventSent = this.isPdp() ? this.setEventOncePerPage(modalAction, 'modalClick') : false; 16710 modal.modalAction = (modalAction === 'ar:item-scheduled|continue-shopping' && !this.isPdp() && data?.source !== 'full-cart') 16711 ? 'ar:item-scheduled-plp|continue-shopping' 16712 : modalAction.indexOf('ar:item-scheduled') !== -1 && this.isPdp() 16713 ? modalAction.replace('ar:item-scheduled', 'ar:subscription-confirmed-pdp') 16714 : modalAction; 16715 // on PLP only send 'ar:item-scheduled-plp|continue-shopping' and 'ar:subscribe-schedule-and-save-plp|subscribed' events 16716 const shouldSendAction = this.isPdp() 16717 ? true 16718 : modal.modalAction === 'ar:item-scheduled-plp|continue-shopping' || modal.modalAction === 'ar:subscribe-schedule-and-save-plp|subscribed' ? true : data?.source === 'full-cart' ? true : false; 16719 16720 if (!eventSent && shouldSendAction) { 16721 16722 let productId; 16723 if (this.isPdp()) { 16724 if (data?.product?.id) { 16725 productId = data?.product?.id; 16726 } else { 16727 const urlParts = window.location.pathname.split('.'); 16728 productId = urlParts[urlParts.length - 2]; 16729 } 16730 } else { 16731 productId = data?.product?.id; 16732 } 16733 16734 let subscribe = 'subscribe'; 16735 let frequencyValue; 16736 16737 if (data?.modal === 'infoModal') { 16738 subscribe = this.handleGetLSValue('analyticsInfoModalSubscribe'); 16739 frequencyValue = subscribe === 'subscribe' ? data?.selectedFrequency ? data.selectedFrequency : 1 : ''; 16740 } else if (data?.modal === 'addRecurringModal') { 16741 frequencyValue = recurringFrequency; 16742 if (this.isPdp()) { 16743 modal.modalAction = modal.modalAction === 'ar:subscribe-schedule-and-save-plp|subscribed' 16744 ? 'ar:adding-to-recurring-order-pdp|subscribed' 16745 : modal.modalAction.replace('|', '-pdp|'); 16746 } 16747 } else { 16748 frequencyValue = modal.modalAction === 'ar:frequency|save' ? formModel?.frequency : data?.selectedFrequency; 16749 } 16750 const frequency = !frequencyValue 16751 ? 'no-frequency' 16752 : frequencyValue == 1 ? 'every-week' : `every-${frequencyValue}-weeks`; 16753 16754 modal.modalMessage = (modal.modalAction === 'ar:subscribe-schedule-and-save-plp|subscribed' || modal.modalAction === 'ar:adding-to-recurring-order-pdp|subscribed') 16755 ? frequency === 'every-week' ? 'frequency-not-modified' : 'frequency-modified' 16756 : ''; 16757 16758 if (['ar:frequency|save', 'ar:frequency|close', 'ar:subscribe-schedule-and-save|close', 'ar:subscribe-schedule-and-save-rpt|remove', 'ar:subscribe-schedule-and-save|not-now'].indexOf(modal.modalAction) !== -1) { 16759 subscribe = 'unsubscribe'; 16760 } 16761 16762 productInfo.productID = productId; 16763 productInfo.scheduleandSaveSource = `${this.isPdp() ? 'pdp' : data?.source === 'full-cart' ? data.source : 'plp'}#${subscribe}#${frequency}`; 16764 16765 if (modal.modalAction === 'ar:frequency|save' && this.isPdp()) { 16766 digitalData.crossPageData.snsAnalyticsFrequency = digitalData.crossPageData.snsAnalyticsFrequency || {}; 16767 digitalData.crossPageData.snsAnalyticsFrequency.data = digitalData.crossPageData.snsAnalyticsFrequency.data || {}; 16768 digitalData.crossPageData.snsAnalyticsFrequency.data[productId] = frequency; 16769 AB.DATALAYER.setAnalyticsCrossPageData({ action: 'set', name: `snsAnalyticsFrequency`, data: digitalData.crossPageData.snsAnalyticsFrequency.data, isAcrossPages: true }); 16770 } 16771 16772 this.pushEvent(
16773 this.EVENTS.MODAL_CLICK, 16774 { 16775 modal: digitalData?.modal, 16776 productInfo: digitalData?.productInfo, 16777 page: digitalData?.pageDetails, 16778 pageFilter: digitalData?.pageFilter 16779 } 16780 ); 16781 } 16782 } 16783 16784 /** 16785 * initialize the modalAction, modalMessage, linklName, productId and scheduleandSaveSource 16786 */ 16787 this.SNSValInitialize = function () { 16788 digitalData.modal = { 16789 modalAction: '', 16790 modalMessage: '' 16791 }; 16792 digitalData.link = { 16793 linkName: '' 16794 }; 16795 digitalData.productInfo = digitalData.productInfo || {}; 16796 digitalData.productInfo.productID = ''; 16797 digitalData.productInfo.scheduleandSaveSource = ''; 16798 digitalData.pageFilter = digitalData.pageFilter || {}; 16799 } 16800 16801 /** 16802 * Sets page load event when clicking on 'Scheduled Items' tab on Manage Schedule & Save page 16803 */ 16804 this.SNSInit = function (pageView, SNSObject) { 16805 if (digitalData?.pageDetails) { 16806 this.SNSValInitialize(); 16807 localStorage.removeItem('analyticsOneTimeOrderItems'); 16808 digitalData.pageFlags.isSnSItemsListExpanded = false; 16809 digitalData.pageDetails.subSection1 = 'customer-account'; 16810 digitalData.pageDetails.subSection2 = 'manage-schedule-and-save'; 16811 if (pageView === 'upcoming-orders') { 16812 const userState = `${SNSObject?.isNewUser ? 'never-enrolled' : (SNSObject?.userWithNoActiveSubscriptions ? 'previous-enrolled' : 'enrolled')}`; 16813 digitalData.pageDetails.subSection3 = `upcoming-orders-${userState}`; 16814 if (SNSObject?.upcomingOrder?.orderStatus === 'Created') { 16815 digitalData.modal.modalMessage = 'upcoming-orders-enrolled-t1-state'; 16816 } 16817 } else if (pageView === 'scheduled-items') { 16818 digitalData.pageDetails.subSection3 = 'scheduled-items'; 16819 // Set number of ss active and canceled items 16820 digitalData.modal.modalMessage = `ss#${SNSObject?.activeItems?.length || '0'}|cancelled#${SNSObject?.cancelledItems?.length || '0'}`; 16821 } else if (pageView === 'preferences-tab') { 16822 digitalData.pageDetails.subSection3 = 'preferences-tab'; 16823 // Set the message on the preference tab as modalMessage 16824 digitalData.modal.modalMessage = SNSObject?.preferenceTabMessage ? SNSObject?.preferenceTabMessage?.toLowerCase().replace(/ /g, '-') : ''; 16825 } else if (pageView === 'bia' || pageView === 'bia-no-results') { 16826 if (SNSObject?.upcomingOrder?.oneTimeOrderItems) { 16827 localStorage.setItem('analyticsOneTimeOrderItems', JSON.stringify(SNSObject.upcomingOrder.oneTimeOrderItems)); 16828 } 16829 const todayDate = new Date(); 16830 const nextOrderDate = new Date(SNSObject?.upcomingOrder?.orderDate); 16831 const todayDateUtc = Date.UTC(todayDate.getFullYear(), todayDate.getMonth(), todayDate.getDate()); 16832 const nextOrderDateUtc = Date.UTC( 16833 nextOrderDate.getFullYear(), 16834 nextOrderDate.getMonth(), 16835 nextOrderDate.getDate(), 16836 ); 16837 const numOfDays = (nextOrderDateUtc - todayDateUtc) / (1000 * 60 * 60 * 24); 16838 digitalData.pageDetails.subSection2 = 'ota'; 16839 digitalData.pageDetails.subSection3 = pageView; 16840 digitalData.pageFilter.topNav = this.handleGetLSValue('analyticsCTAValue'); 16841 digitalData.modal.modalMessage = numOfDays ? numOfDays : ''; 16842 16843 const modal = document.querySelector('#snsOneTimeAddModal'); 16844 if (modal) { 16845 modal.addEventListener('click', event => { 16846 if (digitalData.pageDetails.subSection3 !== 'upcoming-orders-enrolled' && ((!event.target?.closest('.sns-modal-content-wrapper') && event.target !== document.querySelector('.sns-modal-content-wrapper')?.parentElement && !event.target?.closest('button') && !event.target?.closest('quantity-stepper-al-v2') && !event.target?.classList?.contains('dropdown-item')) || event.target?.closest('button')?.getAttribute('aria-label') === 'Close')) { 16847 digitalData.pageDetails.subSection1 = 'customer-account'; 16848 digitalData.pageDetails.subSection2 = 'manage-schedule-and-save'; 16849 digitalData.pageDetails.subSection3 = 'upcoming-orders-enrolled'; 16850 digitalData.pageDetails.pageName = `${digitalData.pageDetails.banner}:loyalty:${digitalData.pageDetails.subSection1}:${digitalData.pageDetails.subSection2}:${digitalData.pageDetails.subSection3}`; 16851 digitalData.pageFilter.topNav = ''; 16852 digitalData.modal.modalMessage = ''; 16853 AB.DATALAYER.setPageLoad(); 16854 } 16855 }); 16856 } 16857 } 16858 digitalData.pageDetails.pageName = `${digitalData.pageDetails.banner}:loyalty:${digitalData.pageDetails.subSection1}:${digitalData.pageDetails.subSection2}:${digitalData.pageDetails.subSection3}`; 16859 this.removeLSItem('analyticsSNSProdAction'); 16860 } 16861 AB.DATALAYER.setPageLoad(); 16862 digitalData.pageFilter.topNav = ''; 16863 } 16864 16865 this.setOtaSnsSearch = function (response, keyword) { 16866 const searchInfo = { 16867 searchType: 'internalsearch:typeahead:suggestion', 16868 typedSearchTerm: '', 16869 autoSearchFlag: 'true', 16870 typedSearchCount: '', 16871 internalSearchTerm: keyword, 16872 searchResults: response?.numFound ? response.numFound.toString() : '0', 16873 }; 16874 if (localStorage.getItem('analyticsSearchTerm')) { 16875 searchInfo.typedSearchTerm = this.handleGetLSValue('analyticsSearchTerm'); 16876 searchInfo.typedSearchCount = searchInfo.typedSearchTerm?.length?.toString(); 16877 } 16878 digitalData.modal = {}; 16879 digitalData.modal.modalMessage = response?.docs?.length <= 0 || response?.numFound === 0 ? `sorry-we-couldn't-find-any-products-matching-${keyword}` : ''; 16880 this.setSearch(searchInfo); 16881 } 16882 16883 this.snsProdAction = function (action) { 16884 AB.DATALAYER.setPfmDetailOnProductAction();
16885 AB.DATALAYER.setLinkClickEvents(action); 16886 let analyticsOneTimeOrderItems = AB.DATALAYER.getLSItem('analyticsOneTimeOrderItems') ? AB.DATALAYER.safeJSONParse(AB.DATALAYER.getLSItem('analyticsOneTimeOrderItems')) : []; 16887 let updatedItems = analyticsOneTimeOrderItems; 16888 if (action === 'cart_add') { 16889 updatedItems.push(digitalData.productInfo); 16890 } else if (action === 'remove_cart') { 16891 updatedItems = analyticsOneTimeOrderItems.filter(item => item?.productID !== digitalData?.productInfo?.productID); 16892 } else { 16893 updatedItems = analyticsOneTimeOrderItems.map(item => item?.productID === digitalData?.productInfo?.productID ? digitalData?.productInfo : item); 16894 } 16895 localStorage.setItem('analyticsOneTimeOrderItems', JSON.stringify(updatedItems)); 16896 } 16897 16898 /** 16899 * Sets linkClick events on Manage Schedule & Save page, Scheduled Items Tab 16900 */ 16901 this.scheduledAndSaveItemsLinkClick = function (linkClicked, data, orderDate) { 16902 this.SNSValInitialize(); 16903 let linkName; 16904 if (linkClicked === 'ar:subscribe-schedule-and-save|confirm-substitutions-for-cancel-items' || linkClicked === 'ota-remove-item|yes-remove-item' || linkClicked === 'ota-remove-item|never-mind') { 16905 linkName = linkClicked; 16906 } else { 16907 const prefix = 'ar:subscribe-schedule-and-save'; 16908 linkName = data?.item?.otaToSnsFlag || data?.item?.isReplinishItem === false 16909 ? `${prefix}-ota|${linkClicked}` 16910 : data?.item?.isActiveSubscription || data?.item?.nextOrderItemId ? `${prefix}|${linkClicked}` : `${prefix}|cancel-${linkClicked}`; 16911 } 16912 const subscribe = linkName === 'ar:subscribe-schedule-and-save' ? 'unsubscribe' : 'subscribe'; 16913 16914 if (['quantity-increase', 'quantity-decrease', 'ar:subscribe-schedule-and-save|confirm-substitutions-for-cancel-items', 'ota-remove-item|yes-remove-item', 'ota-remove-item|never-mind'].indexOf(linkClicked) !== -1) { 16915 digitalData.productInfo.productID = data?.item?.productBpn || ''; 16916 digitalData.productInfo.scheduleandSaveSource = data?.item?.frequency?.displayFrequency ? 16917 `${digitalData.pageDetails.pageName}#${subscribe}#every-${data?.item?.frequency?.displayFrequency?.replace(/ /g, '-')}` : ''; 16918 digitalData.productInfo.units = data?.item?.quantity?.toString(); 16919 digitalData.productInfo.listPrice = data?.item?.itemPrice; 16920 16921 // for ota get numOfDays = next order date - date when user clicks the cta 16922 let numOfDays = ''; 16923 if ((data?.item?.otaToSnsFlag || data?.item?.isReplinishItem === false) && orderDate) { 16924 const todayDate = new Date(); 16925 const nextOrderDate = new Date(orderDate); 16926 const todayDateUtc = Date.UTC(todayDate.getFullYear(), todayDate.getMonth(), todayDate.getDate()); 16927 const nextOrderDateUtc = Date.UTC(nextOrderDate.getFullYear(), nextOrderDate.getMonth(), nextOrderDate.getDate()); 16928 numOfDays = (nextOrderDateUtc - todayDateUtc) / (1000 * 60 * 60 * 24); 16929 } 16930 16931 this.setLinkClickEvents(linkName, false, numOfDays); 16932 } else { 16933 this.setLinkClickEvents(linkName, true); 16934 } 16935 } 16936 16937 /** 16938 * Sets modalClick and modalView events on Manage Schedule & Save page, Scheduled Items Tab 16939 */ 16940 this.scheduledAndSaveItemsModalEvents = function (eventType, modalAction, data, modalMessage) { 16941 this.SNSValInitialize(); 16942 const subSections = [digitalData.pageDetails.subSection1, digitalData.pageDetails.subSection2, digitalData.pageDetails.subSection3, digitalData.pageDetails.subSection4].filter(
16942Boolean).join(':'); 16943 digitalData.modal.modalMessage = modalMessage ? `cta:${subSections}|button|${modalMessage.toLowerCase().replace(/ /g, '-')}` : ''; 16944 digitalData.productInfo.productID = data?.productID || ''; 16945 digitalData.productInfo.scheduleandSaveSource = data?.updatedFrequency ? `${digitalData.pageDetails.pageName}#subscribe#${data.updatedFrequency.toLowerCase().replace(/ /g, '-')}` : ''; 16946 if (eventType === 'view') { 16947 this.setModalViewEvents(modalAction); 16948 } else { 16949 this.setModalClickEvents(modalAction, undefined, false); 16950 } 16951 } 16952 16953 this.snsNextPickupDeliveryClick = function (event, calledBy, productFulfillType, productFulfillDate) { 16954 if ((AB.DATALAYER.isHomePage() || AB.DATALAYER.isSearchResultsPage() || AB.DATALAYER.isLpPage()) && productFulfillDate && productFulfillType) { 16955 let ctaValue = 'product-card'; 16956 if (calledBy === 'product-carousel-item') { 16957 ctaValue = event?.target?.closest('.slick-product-carousel')?.querySelector('.carousel-header-text') 16958 ? event?.target?.closest('.slick-product-carousel')?.querySelector('.carousel-header-text')?.textContent?.toLowerCase()?.trim()?.replace(/\s/g, '-') 16959 : event?.target?.closest('.slick-product-carousel')?.querySelector('.carousel-nav__header-title') 16960 ? event?.target?.closest('.slick-product-carousel')?.querySelector('.carousel-nav__header-title').textContent?.toLowerCase()?.trim()?.replace(/\s/g, '-') 16961 : 'no-value'; 16962 } 16963 const formattedDate = new Date(productFulfillDate)?.toLocaleDateString('en-US', { timeZone: 'UTC', month: 'short', day: '2-digit' }); 16964 const updatedFulfillType = productFulfillType === 'dug' ? 'pickup' : productFulfillType; 16965 AB.DATALAYER.analyticsCTATracking(`cta:${AB.DATALAYER.initPathSections(true).join(':')}|${ctaValue}-link|${updatedFulfillType}-${formattedDate.replace(' ', '-')}`); 16966 } else if (AB.DATALAYER.isLpPage()) { 16967 AB.DATALAYER.analyticsCTATracking(`cta:${AB.DATALAYER.initPathSections(true).join(':')}|link|manage-scheduled-${productFulfillType === 'dug' ? 'pickup' : productFulfillType}`); 16968 } 16969 } 16970 16971 /** 16972 * Sets modalView and modalClick events for new sign in flow 16973 */ 16974 this.setSignInModalEvents = function (eventType, modalAction, modalMessage, topNav) { 16975 if (digitalData) { 16976 const subSections = [digitalData.pageDetails.subSection1, digitalData.pageDetails.subSection2, digitalData.pageDetails.subSection3, digitalData.pageDetails.subSection4].filter(
16976Boolean).join(':'); 16977 digitalData.pageFilter.topNav = topNav ? `cta:${subSections}|button|${topNav}` : ''; 16978 digitalData.modal.modalMessage = modalMessage ? modalMessage.toLowerCase().replace(/ /g, '-') : ''; 16979 const isOncePerPage = modalAction?.includes('use-existing-account') ? false : true; 16980 if (eventType === 'click') { 16981 // if action = 'shopped-with-us-before|close' send only one modal click event per page 16982 AB.DATALAYER.setModalClickEvents(modalAction, undefined, isOncePerPage); 16983 } else { 16984 AB.DATALAYER.setModalViewEvents(modalAction, true); 16985 } 16986 } 16987 } 16988 16989 /** 16990 * Sets modalView and modalClick events for new sign in flow 16991 */ 16992 this.setConvertAccountModalEvents = function (eventType, action) { 16993 if (digitalData) { 16994 const banner = digitalData.pageDetails.banner && digitalData.pageDetails.banner.indexOf('business') !== -1 ? 16995 digitalData.pageDetails.banner.replace('business-', '') : digitalData.pageDetails.banner; 16996 const modalAction = eventType && `${eventType === 'view' ? 'view' : eventType === 'goToBanner' ? banner : eventType === 'new' || eventType === 'convert' ? 'continue' : ''}`; 16997 if (action === 'click') { 16998 digitalData.modal.modalMessage = eventType === 'new' || eventType === 'convert' ? 16999 document.getElementsByTagName('login-wrapper')?.[0]?.getAttribute(`data-${eventType === 'convert' ? 'convert-to' : 'create-new'}-business-acct`)?.toLowerCase()?.replace(/ /g, '-') : ''; 17000 AB.DATALAYER.setModalClickEvents(`use-existing-account|${modalAction}`); 17001 } else { 17002 AB.DATALAYER.setModalViewEvents(`use-existing-account|${modalAction}`); 17003 } 17004 17005 } 17006 } 17007 17008 this.initSignInMessage = function () { 17009 const signInMessage = this.handleGetLSValue('analyticsSignInMessage'); 17010 if (signInMessage && digitalData?.modal) { 17011 digitalData.modal.modalMessage = signInMessage; 17012 } 17013 } 17014 17015 this.accountInputErrorTracking = function (element) { 17016 let checkInput = element?.getAttribute('aria-describedby') 17017 && element?.getAttribute('aria-describedby') === 'error-username' 17018 && element?.getAttribute('formcontrolname') === 'phoneNumber' ? 'error-phoneNumber' : element?.getAttribute('aria-describedby'); 17019 const inputFieldError = document.querySelector(`#${checkInput}`)?.innerText; 17020 const featureError = (element?.getAttribute('formcontrolname') === 'username' || element?.getAttribute('formcontrolname') === 'password') ? 'OTP-error' : 'create-account-error'; 17021 if (!digitalData.pageFlags[checkInput] && inputFieldError) { 17022 AB.DATALAYER.setError('7814', featureError, inputFieldError); 17023 digitalData.pageFlags[checkInput] = true; 17024 } 17025 } 17026 17027 /** 17028 * Sets modalView and modaClick events for splash screen 17029 */ 17030 this.setSplashScreenEvents = function (modalAction, event, analyticsData) { 17031 if (digitalData?.pageDetails) {
17032 digitalData.pageDetails.abTestDetails = analyticsData?.abTestDetails; 17033 if (event === 'view') { 17034 this.setModalViewEvents(modalAction); 17035 this.splashModalHandler(); 17036 } else { 17037 this.setModalClickEvents(modalAction); 17038 } 17039 } 17040 } 17041 17042 /** 17043 * Sets modalClick event when clicking outside splash modal 17044 */ 17045 this.splashModalHandler = function () { 17046 const modal = document.querySelector('#splashModal'); 17047 if (modal) { 17048 modal.addEventListener('click', event => { 17049 if (!event.target.closest('.modal-content') && !event.target.classList.contains('show')) { 17050 AB.DATALAYER.setModalClickEvents('splash-screen-deals-for-you|close'); 17051 } 17052 }); 17053 } 17054 } 17055 17056 this.snsExpandButtonClick = function (element) { 17057 const allOrders = document?.querySelector('div#tabpanel-0 div')?.childNodes; 17058 const nextOrder = allOrders?.[0]?.querySelectorAll('button[aria-label="expand"]')?.length > 0 ? Array.from(allOrders?.[0]?.querySelectorAll('button[aria-label="expand"]')) : []; 17059 const futureOrder = allOrders?.[1]?.querySelectorAll('button[aria-label="expand"]')?.length > 0 ? Array.from(allOrders?.[1]?.querySelectorAll('button[aria-label="expand"]')) : []; 17060 if (nextOrder?.indexOf(element) !== -1) { 17061 this.setLinkClickEvents('upcoming-orders|next-order', true); 17062 } else if (futureOrder?.indexOf(element) !== -1) { 17063 this.setLinkClickEvents(`upcoming-orders|future-orders-[${(futureOrder?.indexOf(element)) + 1}]`, true); 17064 } 17065 } 17066 17067 17068 this.trackAEMZone = function (action, nativeElement, content, id) { 17069 try { 17070 // Set zone number 17071 let element = nativeElement; 17072 // This is exclusively for carousel links, as the element received is the actual zone component, not the link 17073 if (nativeElement?.classList && Array.from(nativeElement.classList).includes('zone-component')) { 17074 element = nativeElement.querySelector(`[data-carousel-title="${content}"]`); 17075 } 17076 17077 const sectionInfo = AB.DATALAYER.getZoneInfo(element); 17078 17079 if (action === 'link') { 17080 action = `link:${element?.textContent?.trim() ? AB.DATALAYER.sanitizeAnalyticsText(element?.textContent?.trim()) : 'no-value'}` 17081 } 17082 if (action === 'campaign-link') { 17083 sectionInfo.section = 1; 17084 sectionInfo.row = 1; 17085 action = 'campaign'; 17086 } 17087 if (action === 'campaign-banner') { 17088 sectionInfo.section = 1; 17089 sectionInfo.row = 1; 17090 action = 'banner'; 17091 content = `small-banner:${content}`; 17092 } 17093 if (action === 'merch-banner') { 17094 action = 'banner'; 17095 content = `merch-banner:${content}`; 17096 } 17097 17098 if (action === 'banner' && typeof content === 'string' && content.indexOf('sticky-banner:') === 0) { 17099 sectionInfo.section = '99'; 17100 sectionInfo.row = '99'; 17101 } 17102 17103 const currentZonePosition = sectionInfo.section < 10 ? `0${sectionInfo.section}` : sectionInfo.section; 17104 const currentSpot = sectionInfo.row < 10 ? `0${sectionInfo.row}` : sectionInfo.row; 17105 17106 // currently set to no-value due to lack of implementation on UMA 17107 const uniqueId = id ? id : 'no-value'; 17108 17109 content = content ? this.sanitizeAnalyticsText(content) : 'no-value'; 17110 17111 if (sectionInfo.section && sectionInfo.section != '' && sectionInfo.row && sectionInfo.row != '') { 17112 // Set tracking string 17113 const aemZone = `z${currentZonePosition}|${action}|${content}|sp${currentSpot}|${uniqueId}`; 17114 17115 if (['pdp-card', 'banner', 'hero-collections', 'flex', 'hero-canvas', 'collections', 'disclaimer', 'view', 'link', 'campaign', 'carousel'].filter((x) => { return action ? action.indexOf(x) != -1 : false }).length > 0) { 17116 localStorage.setItem('aemZoneAnalytics', aemZone); 17117 } else if (['remove_cart', 'cart_add', 'add_quantity', 'remove_quantity', 'coupon_clipped', 'offer-drawer'].filter((x) => { return action ? action.indexOf(x) != -1 : false }).length > 0) { 17118 digitalData.pageFilter.bannerCardCarousel = aemZone; 17119 } 17120 return aemZone; 17121 } 17122 } catch (error) {
17123 console.error('@AnalyticsTeam: graceful failure, error in trackAEMZone: ', error); 17124 } 17125 } 17126 17127 this.setFreshpassPageFilterValues = function () { 17128 const { pageFilter, user } = digitalData; 17129 let subscriptionInfoObj; 17130 if (this.getLSItem('freshpass-analytics-confirmation') && this.isCartAndCheckoutPage('prebook')) { 17131 subscriptionInfoObj = this.safeJSONParse(this.getLSItem('freshpass-analytics-confirmation')); 17132 } 17133 17134 const subscriptionPlanLS = !this.isCartAndCheckoutPage('prebook') ? this.getLSItem('analyticsSubscriptionPlan') : subscriptionInfoObj ? `prebook:freshpass-${subscriptionInfoObj?.isExtendedTrial 17135 ? `${subscriptionInfoObj?.trialExtDuration}-extd-` : ''}${(!!subscriptionInfoObj?.isTrialElligible 17136 ? 'trial-' : '')}${subscriptionInfoObj?.planType?.includes('monthlyplan') ? 'monthly-' : 'annual-'}confirmation` : ''; 17137 const subscriptionPlanPrice = !this.isCartAndCheckoutPage('prebook') ? this.getLSItem('analyticsFPPlanPrice') : (subscriptionInfoObj?.monthlyPlanPrice || subscriptionInfoObj?.annualPlanPrice || ''); 17138 const subscriptionDiscountedMonths = !this.isCartAndCheckoutPage('prebook') ? this.getLSItem('analyticsFPDiscountedMonths') : (subscriptionInfoObj?.monthlyDiscountMonths || subscriptionInfoObj?.annualDiscountMonths || ''); 17139 17140 if (subscriptionPlanLS) { 17141 pageFilter.subscriptionFunnel = this.isCartAndCheckoutPage('order/confirmation') 17142 ? subscriptionPlanLS.replace('checkout', 'confirmation') 17143 : subscriptionPlanLS; 17144 } 17145 if (subscriptionPlanPrice) { 17146 pageFilter.freshPassPlanPrice = subscriptionPlanPrice; 17147 pageFilter.freshPassDiscountedMonths = subscriptionDiscountedMonths; 17148 } 17149 if (subscriptionInfoObj && this.isCartAndCheckoutPage('prebook')) { 17150 user.subscriptionStatus = subscriptionInfoObj?.planType?.includes('monthlyplan') ? 'freshpass-monthly' : subscriptionInfoObj?.planType?.includes('annualplan') ? 'freshpass-annual' : ''; 17151 } 17152 if (this.isCartAndCheckoutPage('order/confirmation')) { 17153 this.removeLSItem('analyticsSubscriptionPlan'); 17154 this.removeLSItem('analyticsFPPlanPrice'); 17155 this.removeLSItem('analyticsFPDiscountedMonths'); 17156 } 17157 } 17158 17159 this.initCartAndCheckoutPages = function ({ accordionData, pageSpecificData } = {}) { 17160 if (digitalData) { 17161 const openedAccordion = accordionData && (accordionData.filter((accordion) => accordion.isOpen == true))[0]; 17162 const currentPage = digitalData?.pageDetails?.pageName; 17163 const { pageDetails, pageFilter, user, modal, link } = digitalData; 17164 if (!this.isDFTAPage()) { 17165 for (let i = 0; i <= 3; i++) { 17166 pageDetails[`subSection${i + 1}`] = ''; 17167 } 17168 } 17169 17170 pageDetails.channel = 'Checkout'; 17171 17172 switch (true) { 17173 // Full Cart page 17174 case this.isCartAndCheckoutPage('erums/cart'): 17175 pageDetails.subSection1 = 'Full Cart'; 17176 break; 17177 // Order confirmation page 17178 case this.isCartAndCheckoutPage('order/confirmation'): 17179 pageDetails.subSection1 = 'Checkout'; 17180 pageDetails.subSection2 = 'Order Confirmation'; 17181 break; 17182 // Slots 17183 case this.isCartAndCheckoutPage('prebook'): 17184 pageDetails.subSection1 = 'reserve time'; 17185 pageDetails.subSection2 = 'delivery date and time'; 17186 if (pageSpecificData?.availableSlots && pageSpecificData?.serviceType === 'DELIVERY') { 17187 link.linkName = 'prebook-more-delivery-times'; 17188 pageDetails.subSection3 = 'more-delivery-times'; 17189 } 17190 break; 17191 // Checkout 17192 case this.isCartAndCheckoutPage('erums/checkout'): 17193 pageDetails.subSection1 = 'Checkout'; 17194 pageDetails.subSection2 = openedAccordion?.referenceId === 'COA' ? 'credit on account' : openedAccordion?.label; 17195 if (pageSpecificData?.bagFeePreference) { 17196 this.setImpression(pageSpecificData.bagFeePreference?.toggleEnabled === true ? 'bagprf-on' : 'bagprf-off'); 17197 } 17198 if (pageSpecificData?.selectiveCart) { 17199 // Need to reset cart info since it gets set on pageLoad from LS 17200 digitalData.cart = { 17201 cartInfo: {}, 17202 item: [], 17203 transaction: { 17204 subTotal: '' 17205 }, 17206 savings: {} 17207 }; 17208 AB.DATALAYER.updateCart(pageSpecificData); 17209 pageDetails.isSelectiveCart = true; 17210 } 17211 break; 17212 // Edit Full Cart 17213 case this.isCartAndCheckoutPage('edit/cart'): 17214 pageDetails.subSection1 = 'edit order'; 17215 pageDetails.subSection2 = 'edit cart'; 17216 break; 17217 //Edit Confirmation 17218 case this.isCartAndCheckoutPage('checkout/confirmation'): 17219 pageDetails.subSection1 = 'edit order'; 17220 pageDetails.subSection2 = 'edit order confirmation'; 17221 break; 17222 // Edit Slot 17223 case this.isCartAndCheckoutPage('edit/slot'): 17224 pageDetails.subSection1 = 'edit order'; 17225 pageDetails.subSection2 = 'edit delivery date and time'; 17226 break; 17227 // Edit Checkout 17228 case this.isCartAndCheckoutPage('edit/checkout'): 17229 pageDetails.subSection1 = 'edit order'; 17230 pageDetails.subSection2 = 'edit order checkout'; 17231 break; 17232 // Don't forget to add 17233 case this.isDFTAPage(): 17234 pageDetails.pageUrl = window.location.href; 17235 pageDetails.referringURL = this.getReferringUrl(); 17236 pageFilter.topNav = this.initTopNav(); 17237 pageDetails.subSection1 = 'erums'; 17238 pageDetails.subSection2 = 'cart'; 17239 pageDetails.subSection3 = 'dnf'; 17240 break; 17241 } 17242 17243 this.setPageName('', true, openedAccordion ? openedAccordion?.id : undefined); 17244 17245 this.initUser(); 17246 17247 this.setFreshpassPageFilterValues(); 17248 17249 this.updatesFromLSBeforePageLoad(); 17250 17251 if ((/PromoNPay/gi).test(openedAccordion?.uniqueId) && pageSpecificData?.storeConfiguration?.isSNAPEligible) { 17252 this.setImpression('ebt-available'); 17253 } 17254 openedAccordion?.id === 2 && this.trackEliteSlots(pageSpecificData); 17255 17256 if (pageSpecificData?.slots?.reservedSlot) { 17257 pageFilter.premiumSlots = localStorage.getItem('dtm-premiumslots'); 17258 } 17259 17260 user && Object.keys(user).length !== 0 && (user.deliveryCombo = pageSpecificData?.deliveryType ? pageSpecificData?.deliveryType : pageSpecificData?.order?.deliveryType ? pageSpecificData?.order?.deliveryType : ''); 17261 if (pageSpecificData) { 17262 AB.DATALAYER.setDefaultValues(pageSpecificData); 17263 } 17264 17265 const isPaypalPayment = pageSpecificData?.customerPreferences?.fundsAllocation?.tenderAllocationSummary?.some((x) => x?.paymentSubType === 'PAYPAL'); 17266 this.setPaymentMethodPaypal('checkout', isPaypalPayment); 17267 17268 if (this.isCartAndCheckoutPage('order/confirmation')) { 17269 pageDetails.sellerId = pageSpecificData?.order?.seller?.sellerId ? pageSpecificData?.order?.seller?.sellerId : pageSpecificData?.order?.cartCategory === '1P_WINE' ? '22222222' : AB.DATALAYER.getBannerSellerId(); 17270 } 17271 17272 if (this.isCartAndCheckoutPage('order/confirmation') || this.isCartAndCheckoutPage('checkout/confirmation')) { 17273 modal.modalMessage = this.getPaymentUpdatedStatus(); 17274 } 17275 17276 if (openedAccordion && openedAccordion?.id && currentPage?.indexOf(openedAccordion.label) < 0) { 17277 const isFirstCallSent = appEventData.length === 0; 17278 let event = openedAccordion && openedAccordion?.id === 1 && isFirstCallSent ? 'checkout' : `checkout_step${openedAccordion?.id}`; 17279 AB.DATALAYER.setPreviousPageName(); 17280 AB.DATALAYER.setRestrictedPageCnc(); 17281 this.pushEvent(event, { 17282 page: digitalData?.pageDetails, 17283 pageFilter: digitalData?.pageFilter, 17284 user: digitalData?.user, 17285 productInfo: digitalData?.productInfo, 17286 modal: digitalData?.modal, 17287 cart: digitalData?.cart 17288 }); 17289 } else if (this.isCartAndCheckoutPage('prebook') || this.isDFTAPage()) { 17290 AB.DATALAYER.setPreviousPageName(); 17291 this.pushEvent(this.EVENTS.PAGE_LOADED, { 17292 page: digitalData?.pageDetails, 17293 pageFilter: digitalData?.pageFilter, 17294 link: digitalData?.link, 17295 user: digitalData?.user, 17296 cart: digitalData?.cart 17297 }); 17298 } 17299 pageFilter.topNav = ''; 17300 } 17301 } 17302 this.setBentoHeroTracking = function (action, payload, _element) { 17303 try { 17304 if (!payload || !window.digitalData) return; 17305 17306 var isCartOrCoupon = action === 'cart_add' || action === 'coupon_clipped'; 17307 var isNavToNextPage = action === 'pdp-card'; 17308 var isCrossPage = action === 'banner' || isNavToNextPage || action === 'deal-line'; 17309 17310 var applyBentoProducts = function (products) { 17311 if (!products) return; 17312 digitalData.productInfo = digitalData.productInfo || {}; 17313 var productId = products.split(';')[1] || ''; 17314 var eVar14Match = products.match(/(?:^|\|)eVar14=([^|]+)/); 17315 var eVar14 = eVar14Match ? eVar14Match[1] : ''; 17316 digitalData.productInfo.products = products; 17317 digitalData.productInfo.productString = products; 17318 if (productId && !digitalData.productInfo.productID) { 17319 digitalData.productInfo.productID = productId; 17320 } 17321 if (eVar14) { 17322 digitalData.productInfo.pfmDetail = eVar14; 17323 } 17324 }; 17325 17326 if (payload.v218) { 17327 if (isCrossPage) { 17328 try { localStorage.setItem('aemZoneAnalytics', payload.v218); } catch (e) { } 17329 } else { 17330 digitalData.pageFilter.bannerCardCarousel = payload.v218; 17331 } 17332 } 17333 17334 if (payload.l2) { 17335 digitalData.pageDetails.abTestDetails = payload.l2; 17336 if (isCrossPage) { 17337 try { localStorage.setItem('analyticsProductMsg', payload.l2); } catch (e) { } 17338 } 17339 } 17340 17341 if (payload.products) { 17342 applyBentoProducts(payload.products); 17343 if (isNavToNextPage) { 17344 try { localStorage.setItem('bentoHeroProducts', payload.products); } catch (e) { } 17345 } 17346 } 17347 17348 var v80Fields = null; 17349 if (payload.v80 && action !== 'coupon_clipped') { 17350 var parts = payload.v80.split('#'); 17351 v80Fields = { 17352 mediaType: parts[0], 17353 recommendationType: parts[1] || undefined, 17354 placement: parts.length > 2 ? parts.slice(2).join('#') : undefined, 17355 }; 17356 } 17357 17358 if (payload.v171) { 17359 if (action === 'deal-line') { 17360 try { localStorage.setItem('bentoHeroCTAValue', payload.v171); } catch (e) { } 17361 } else { 17362 this.analyticsCTATracking(payload.v171); 17363 } 17364 } 17365 17366 if (payload.carouselTitle && action === 'coupon_clipped' && digitalData.pageFilter) { 17367 digitalData.pageFilter.bentoCarouselTitle = payload.carouselTitle; 17368 } 17369 if (action === 'coupon_clipped' && payload.v218 && digitalData.pageFilter) { 17370 digitalData.pageFilter.bentoCouponV218Set = true; 17371 } 17372 17373 if (action === 'cart_add') { 17374 digitalData.link = digitalData.link || {}; 17375 digitalData.link.linkName = 'cart_add'; 17376 } 17377 17378 if (isCartOrCoupon) { 17379 if (v80Fields) Object.assign(digitalData.pageFilter, v80Fields); 17380 } else if (isCrossPage) { 17381 if (payload.v80) { 17382 try { localStorage.setItem('bentoHeroMediaPlacement', payload.v80); } catch (e) { } 17383 } 17384 if (action === 'banner' && payload.v218) { 17385 var eventPageFilter = Object.assign({}, digitalData.pageFilter); 17386 eventPageFilter.bannerCardCarousel = payload.v218; 17387 if (v80Fields) Object.assign(eventPageFilter, v80Fields); 17388 this.pushEvent(this.EVENTS.LINK_CLICK, { 17389 link: { 17390 linkName: payload.v218, 17391 oneTagLinkName: 'banner-clicked' 17392 }, 17393 pageFilter: eventPageFilter, 17394 page: Object.assign({}, digitalData.pageDetails), 17395 }); 17396 } 17397 } 17398 } catch (error) {
17399 console.error('@AnalyticsTeam: graceful failure in setBentoHeroTracking:', error); 17400 } 17401 }; 17402 17403 this.initAEMZoneTracking = function () { 17404 if (localStorage.getItem('aemZoneAnalytics') && digitalData?.pageFilter) { 17405 digitalData.pageFilter.bannerCardCarousel = localStorage.getItem('aemZoneAnalytics'); 17406 localStorage.removeItem('aemZoneAnalytics'); 17407 } 17408 17409 // ACIP-576827: V80 carry-over for Blog component CTA click that navigated to this page. 17410 if (localStorage.getItem('mediaPlacementClick') && digitalData?.pageFilter) { 17411 digitalData.pageFilter.mediaPlacementClick = localStorage.getItem('mediaPlacementClick'); 17412 localStorage.removeItem('mediaPlacementClick'); 17413 } 17414 17415 var bentoMediaPlacement = localStorage.getItem('bentoHeroMediaPlacement'); 17416 if (bentoMediaPlacement && digitalData?.pageFilter) { 17417 var mpParts = bentoMediaPlacement.split('#'); 17418 digitalData.pageFilter.mediaType = mpParts[0]; 17419 if (mpParts[1] !== undefined) digitalData.pageFilter.recommendationType = mpParts[1]; 17420 if (mpParts.length > 2) digitalData.pageFilter.placement = mpParts.slice(2).join('#'); 17421 localStorage.removeItem('bentoHeroMediaPlacement'); 17422 } 17423 17424 var bentoHeroProducts = localStorage.getItem('bentoHeroProducts'); 17425 if (bentoHeroProducts && digitalData) { 17426 digitalData.productInfo = digitalData.productInfo || {}; 17427 var productId = bentoHeroProducts.split(';')[1] || ''; 17428 var eVar14Match = bentoHeroProducts.match(/(?:^|\|)eVar14=([^|]+)/); 17429 var eVar14 = eVar14Match ? eVar14Match[1] : ''; 17430 digitalData.productInfo.products = bentoHeroProducts; 17431 digitalData.productInfo.productString = bentoHeroProducts; 17432 if (productId && !digitalData.productInfo.productID) { 17433 digitalData.productInfo.productID = productId; 17434 } 17435 if (eVar14) { 17436 digitalData.productInfo.pfmDetail = eVar14; 17437 } 17438 localStorage.removeItem('bentoHeroProducts'); 17439 } 17440 17441 var bentoHeroCTAValue = localStorage.getItem('bentoHeroCTAValue'); 17442 if (bentoHeroCTAValue && digitalData?.pageFilter) { 17443 digitalData.pageFilter.topNav = bentoHeroCTAValue; 17444 localStorage.removeItem('bentoHeroCTAValue'); 17445 } 17446 } 17447 17448 this.initMealsPages = function ({ page, path, data, topNavTracking } = {}) { 17449 if (digitalData) { 17450 const { pageDetails, pageFilter } = digitalData; 17451 let legacyId, mealsRelatedData = {}, recipeType = digitalData.pageDetails.subSection1.includes('meal') ? 'meals' : 'recipes'; 17452 if (data && typeof (data) === 'object' && Object.keys(data).length > 0) { 17453 ({ legacyId, ...mealsRelatedData } = data); 17454 mealsRelatedData.variantId = legacyId || ''; 17455 } 17456 for (let i = 0; i <= 3; i++) { 17457 pageDetails[`subSection${i + 1}`] = ''; 17458 } 17459 const parts = path.split(':'); 17460 pageDetails.channel = page ? page : ''; 17461 pageDetails.subSection1 = page ? page : ''; 17462 pageDetails.subSection2 = parts[0] || ''; 17463 pageDetails.subSection3 = parts[1] || ''; 17464 pageDetails.subSection4 = parts[2] || ''; 17465 pageDetails.pageUrl = window.location.pathname; 17466 pageDetails.pfmDetail = window.location.pathname.split('/').filter(x => x != '').join(':'); 17467 pageFilter.topNav = pageFilter.topNav || topNavTracking; 17468 // update subSection3 on edit order flow 17469 if (pageDetails.subSection2 === 'srp' && !pageDetails.subSection3 && !pageDetails.subSection4 && AB.DATALAYER.getCookie('SlotInfo') && AB.DATALAYER.getCookie('OrderNumber')) { 17470 pageDetails.subSection3 = 'order-edit'; 17471 } 17472 this.setPageName(undefined, true, undefined); 17473 this.updatesFromLSBeforePageLoad(); 17474 AB.DATALAYER.setPreviousPageName(); 17475 if (pageDetails.subSection2 === 'bundle' && pageDetails.subSection3.startsWith('bundle+')) { 17476 setTimeout(() => { 17477 mealsRelatedData.bundleProducts = AB.DATALAYER.getBundleProducts(); 17478 mealsRelatedData.bundleTitle = AB.DATALAYER.handleGetLSValue('analyticsBundleTitle'); 17479 AB.DATALAYER.pushEvent(AB.DATALAYER.EVENTS.PAGE_LOADED, { 17480 page: digitalData.pageDetails, 17481 pageFilter: digitalData.pageFilter, 17482 modal: digitalData.modal, 17483 [recipeType]: mealsRelatedData || {}, 17484 user: digitalData?.user 17485 }) 17486 pageFilter.topNav = ''; 17487 }, 2000); 17488 } else { 17489 this.pushEvent(this.EVENTS.PAGE_LOADED, { 17490 page: digitalData?.pageDetails, 17491 pageFilter: digitalData?.pageFilter, 17492 modal: digitalData?.modal, 17493 [recipeType]: mealsRelatedData || {}, 17494 user: digitalData?.user 17495 }) 17496 pageFilter.topNav = ''; 17497 } 17498 } 17499 } 17500 17501 this.setMealsActions = function ({ event, action, path, modalMessage, mealsData } = {}) { 17502 let legacyId, mealsRelatedData = {}, recipeType = digitalData.pageDetails.subSection1.includes('meal') ? 'meals' : 're
17502cipes'; 17503 if (mealsData && typeof (mealsData) === 'object' && Object.keys(mealsData).length > 0) { 17504 ({ legacyId, ...mealsRelatedData } = mealsData); 17505 mealsRelatedData.variantId = legacyId || ''; 17506 } 17507 const { pageDetails, modal, link } = digitalData; 17508 if (path) { 17509 const parts = path.split(':'); 17510 pageDetails.subSection2 = parts[0] || ''; 17511 pageDetails.subSection3 = parts[1] || ''; 17512 pageDetails.subSection4 = parts[2] || ''; 17513 } 17514 link.linkName = event.includes('link') ? action : ''; 17515 modal.modalAction = event.includes('modal') ? action : ''; 17516 modal.modalMessage = modalMessage || ''; 17517 digitalData[recipeType] = mealsRelatedData || {}; 17518 let oncePerPageView = false; 17519 let actionType = recipeType === 'meals' ? 'sendMealsData' : recipeType === 'recipes' ? 'sendRecipesData' : ''; 17520 17521 if (event === 'linkClick') { 17522 if (['favorite-recipe', 'unfavorite-recipe', 'scale-recipe', 'recipe|print-recipe', 'share-recipe', 'tab-instruction', 'tab-detail', 'tab-ingredient'].indexOf(action) !== -1) { 17523 oncePerPageView = true; 17524 } 17525 this.setLinkClickEvents(action, oncePerPageView, actionType); 17526 } else { 17527 if (event === 'modalView') { 17528 if (['select-replacement|view'].indexOf(action) !== -1) { 17529 oncePerPageView = true; 17530 } 17531 this.setModalViewEvents(action, oncePerPageView, actionType); 17532 } else { 17533 this.setModalClickEvents(action, actionType, oncePerPageView); 17534 } 17535 } 17536 17537 digitalData.modal = { 17538 modalAction: '', 17539 modalMessage: '' 17540 } 17541 digitalData.link = { 17542 linkName: '', 17543 } 17544 digitalData.productInfo = {}; 17545 digitalData?.meals && (digitalData.meals = {}) 17546 digitalData?.recipes && (digitalData.recipes = {}) 17547 } 17548 17549 this.getBundleProducts = function () { 17550 const productsIds = document.querySelectorAll('button[id^="select-button-"]'); 17551 const productsPrices = document.querySelectorAll('span')?.length > 0 && Array.from(document.querySelectorAll('span')).filter(el => el.textContent?.trim()?.startsWith('Total $')); 17552 let bundleProducts = []; 17553 if (productsIds.length > 0 && productsPrices.length > 0) { 17554 bundleProducts = Array.from(productsIds).map((product, index) => { 17555 const productId = product.id?.split('-')?.pop(); 17556 const productPrice = productsPrices[index]?.textContent?.split('$')[1]; 17557 return { 17558 bpn: productId, 17559 price: productPrice, 17560 selectedAmount: 1 17561 } 17562 }); 17563 } 17564 return bundleProducts; 17565 } 17566 17567 this.trackRelatedProductAction = function (action, product, element) { 17568 this.productInfo(product); 17569 if (element) { 17570 const subSection1 = digitalData.pageDetails.subSection1 ? digitalData.pageDetails.subSection1 : ''; 17571 const subSection2 = digitalData.pageDetails.subSection2 ? digitalData.pageDetails.subSection2 : ''; 17572 const subSection3 = digitalData.pageDetails.subSection3 ? digitalData.pageDetails.subSection3 : ''; 17573 const subSection4 = digitalData.pageDetails.subSection4 ? digitalData.pageDetails.subSection4 : ''; 17574 17575 const currentSlide = element.closest('[aria-roledescription="slide"]'); 17576 const currentCarousel = element.closest('#carousel-items-related-products'); 17577 const allCarousels = document.querySelectorAll('#carousel-items-related-products')?.length > 0 ? Array.from(document.querySelectorAll('#carousel-items-related-products')) : []; 17578 const allSlides = currentCarousel?.querySelectorAll('[aria-roledescription="slide"]')?.length > 0 ? Array.from(currentCarousel?.querySelectorAll('[aria-roledescription="slide"]')) : []; 17579 const rowValue = allCarousels.indexOf(currentCarousel) < 10 ? `0${allCarousels.indexOf(currentCarousel) + 1}` : `${allCarousels.indexOf(currentCarousel) + 1}`; 17580 const slotValue = allSlides.indexOf(currentSlide) < 10 ? `0${allSlides.indexOf(currentSlide) + 1}` : `${allSlides.indexOf(currentSlide) + 1}`; 17581 const carouselTitle = element.closest('div:has(h3)')?.querySelector('h3')?.innerText; 17582 const sanitizedTitle = carouselTitle ? AB.DATALAYER.sanitizeAnalyticsText(carouselTitle) : 'no-value'; 17583 const recommendationType = currentSlide?.innerText?.toLowerCase()?.indexOf('sponsored') !== -1 ? 'sponsored' : 'not-recommended'; 17584 digitalData.productInfo.pfmDetail = `${[subSection1, subSection2, subSection3, subSection4]?.join(':')}#${sanitizedTitle}#non-search#${recommendationType}#R${rowValue}#S${slotValue}`; 17585 if (action === 'add-item' && digitalData.productInfo?.productID) { 17586 digitalData.crossPageData = digitalData.crossPageData || {}; 17587 digitalData.crossPageData.recipeRelatedPfm = digitalData.crossPageData.recipeRelatedPfm || {}; 17588 digitalData.crossPageData.recipeRelatedPfm[digitalData.productInfo?.productID] = digitalData.productInfo.pfmDetail; 17589 } else if (action === 'recipe|remove-item' && digitalData.productInfo.productID) { 17590 digitalData.crossPageData = digitalData.crossPageData || {}; 17591 digitalData.crossPageData.recipeRelatedPfm = digitalData.crossPageData.recipeRelatedPfm || {}; 17592 delete digitalData.crossPageData.recipeRelatedPfm[digitalData.productInfo.productID]; 17593 } 17594 } 17595 let payload = { 17596 link: { 17597 linkName: action, 17598 }, 17599 page: digitalData?.pageDetails, 17600 pageFilter: digitalData?.pageFilter, 17601 user: digitalData?.user, 17602 productInfo: digitalData?.productInfo, 17603 }; 17604 this.pushEvent(this.EVENTS.LINK_CLICK, payload); 17605 } 17606 17607 this.saveProductPFMonAIAssistant = function (product, element, type) { 17608 if (element) { 17609 const carouselTitle = element.closest('div:has(h3)')?.querySelector('h3')?.innerText; 17610 const sanitizedTitle = carouselTitle ? AB.DATALAYER.sanitizeAnalyticsText(carouselTitle) : 'no-value'; 17611 if (type === 'recipe') { 17612 const pfmDetail = `${AB.DATALAYER.pathSections?.join(':')}#${sanitizedTitle}:chat#non-search#not-recommended#R00#S00#no-badge`; 17613 digitalData.crossPageData = digitalData.crossPageData || {}; 17614 digitalData.crossPageData.aiAssistantRecipePFM = digitalData.crossPageData.aiAssistantRecipePFM || {}; 17615 digitalData.crossPageData.aiAssistantRecipePFM[product.recipeId] = pfmDetail; 17616 } else if (type === 'item') { 17617 const currentSlide = element.closest('[data-testid="product-card"]'); 17618 const currentCarousel = element.closest('[data-testid="products-row"]'); 17619 const allCarousels = document.querySelectorAll('[data-testid="products-row"]')?.length > 0 ? Array.from(document.querySelectorAll('[data-testid="products-row"]')) : []; 17620 const allSlides = currentCarousel?.querySelectorAll('[data-testid="product-card"]')?.length > 0 ? Array.from(currentCarousel?.querySelectorAll('[data-testid="product-card"]')) : []; 17621 const rowValue = allCarousels.indexOf(currentCarousel) < 10 ? `0${allCarousels.indexOf(currentCarousel) + 1}` : `${allCarousels.indexOf(currentCarousel) + 1}`; 17622 const slotValue = allSlides.indexOf(currentSlide) < 10 ? `0${allSlides.indexOf(currentSlide) + 1}` : `${allSlides.indexOf(currentSlide) + 1}`; 17623 const recommendationType = currentSlide?.innerText?.toLowerCase()?.indexOf('sponsored') !== -1 ? 'sponsored' : 'not-recommended'; 17624 const pfmDetail = `${AB.DATALAYER.pathSections?.join(':')}#${sanitizedTitle}#non-search#${recommendationType}#R${rowValue}#S${slotValue}#no-badge`; 17625 17626 digitalData.crossPageData = digitalData.crossPageData || {}; 17627 digitalData.crossPageData.aiAssistantProductPFM = digitalData.crossPageData.aiAssistantProductPFM || {}; 17628 digitalData.crossPageData.aiAssistantProductPFM[product.id] = pfmDetail; 17629 } 17630 } 17631 } 17632 17633 // Order Ahead Data Layer Migration 17634 // Start 17635 this.orderAheadSetUserInfo = function (storeValue) {
17636 const storeNumber = storeValue?.storeNumber ? storeValue.storeNumber.toString() : ''; 17637 if (digitalData) { 17638 digitalData.user = { 17639 shopStoreId: storeNumber, 17640 j4uStoreId: storeNumber 17641 }; 17642 this.initAbsVisitorId(); 17643 } 17644 } 17645 17646 this.orderAheadSetPageDetails = function () { 17647 const pageDetails = digitalData?.pageDetails || {}; 17648 AB.DATALAYER.initPathSections(); 17649 if (AB.DATALAYER.pathSections.length < 1) { 17650 pageDetails.subSection1 = 'home'; 17651 } else { 17652 AB.DATALAYER.setSubsections(); 17653 } 17654 pageDetails.banner = this.getBanner(); 17655 pageDetails.channel = 'orderahead'; 17656 AB.DATALAYER.setPageName(pageDetails.channel, true) 17657 pageDetails.pageUrl = window.location.href; 17658 pageDetails.referringUrl = this.getReferringUrl() || window.location.href || ''; 17659 } 17660 17661 this.initOrderAhead = function (storeValue) { 17662 digitalData = digitalData || {}; 17663 digitalData.pageDetails = {} 17664 digitalData.user = {} 17665 this.orderAheadSetUserInfo(storeValue); 17666 this.orderAheadSetPageDetails(); 17667 } 17668 17669 this.orderAheadUpdateOrderInfo = function (order) { 17670 let cartInfo = []; 17671 let totalOrderValue = 0; 17672 cartInfo = order.items.map(function (item) { 17673 return { 17674 productId: item.id.toString(), 17675 productName: item.name, 17676 units: item.quantity.toString(), 17677 listPrice: (item.price / item.quantity).toString(), 17678 totalPrice: item.price.toString() 17679 }; 17680 }); 17681 cartInfo.forEach(function (item) { 17682 totalOrderValue += Number(item.totalPrice); 17683 }); 17684 17685 digitalData.cart = {}; 17686 digitalData.cart.item = cartInfo; 17687 digitalData.cart.transaction = { 17688 orderID: order.orderInformation.customerOrderId, 17689 revenue: totalOrderValue.toString(), 17690 deliveryTime: this.getFullDateWithTime(order.orderInformation.pickupDate) 17691 } 17692 digitalData.user = { 17693 shopZipCode: order.store.zipCode, 17694 j4UZipCode: order.store.zipCode 17695 }; 17696 } 17697 17698 17699 this.setOrderAheadOrderEvent = function (orderEvent) { 17700 this.pushEvent(orderEvent, { 17701 user: digitalData.user, 17702 page: digitalData.pageDetails, 17703 cart: digitalData.cart 17704 }) 17705 } 17706 17707 17708 this.trackP13nViewAll = function (element) { 17709 if (AB.DATALAYER.isLpPage()) { 17710 const wrapper = element?.closest('.coupon-carousel, .master-product-carousel')?.querySelector('lazy-wrapper'); 17711 const carouselType = wrapper?.getAttribute('data-lazy-component') === 'coupon-carousel' && wrapper?.getAttribute('data-carousel-filter') === 'p13n' ? 'deals' : wrapper?.getAttribute('data-lazy-component') === 'product-carousel' && wrapper?.getAttribute('data-carousel-driven') === 'p13n-generic' ? 'products' : ''; 17712 carouselType && AB.DATALAYER.analyticsCTATracking(`cta:${AB.DATALAYER.pathSections.join(':')}|link|${carouselType}-view-all`); 17713 } 17714 } 17715 17716 this.initSelfAccountsMergePages = function (screen, accInfo) { 17717 if (digitalData?.pageDetails) { 17718 digitalData.pageDetails.subSection1 = 'customer-account'; 17719 digitalData.pageFilter.topNav = digitalData?.pageFilter?.topNav ? digitalData.pageFilter.topNav : AB.DATALAYER.initTopNav(); 17720 17721 switch (screen) { 17722 case 'step1': 17723 digitalData.pageDetails.subSection2 = 'merge-accounts-step-1'; 17724 digitalData.pageDetails.subSection3 = ''; 17725 digitalData.pageDetails.subSection4 = ''; 17726 AB.DATALAYER.setImpression(`ac:${accInfo}`); 17727 break; 17728 case 'step2-email': 17729 digitalData.pageDetails.subSection2 = 'merge-accounts-step-2'; 17730 digitalData.pageDetails.subSection3 = 'email-verification-needed'; 17731 digitalData.pageDetails.subSection4 = ''; 17732 break; 17733 case 'step2-phone': 17734 digitalData.pageDetails.subSection2 = 'merge-accounts-step-2'; 17735 digitalData.pageDetails.subSection3 = 'phone-verification-needed'; 17736 digitalData.pageDetails.subSection4 = ''; 17737 break; 17738 case 'step2-email-verify-account': 17739 digitalData.pageDetails.subSection2 = 'merge-accounts-step-2'; 17740 digitalData.pageDetails.subSection3 = 'email-verification-needed'; 17741 digitalData.pageDetails.subSection4 = 'verify-account'; 17742 break; 17743 case 'step2-phone-verify-account': 17744 digitalData.pageDetails.subSection2 = 'merge-accounts-step-2'; 17745 digitalData.pageDetails.subSection3 = 'phone-verification-needed'; 17746 digitalData.pageDetails.subSection4 = 'verify-account'; 17747 break; 17748 case 'step2-email-failed': 17749 digitalData.pageDetails.subSection2 = 'merge-accounts-step-2'; 17750 digitalData.pageDetails.subSection3 = 'email-verification-needed'; 17751 digitalData.pageDetails.subSection4 = 'verify-failed'; 17752 break; 17753 case 'step2-phone-failed': 17754 digitalData.pageDetails.subSection2 = 'merge-accounts-step-2'; 17755 digitalData.pageDetails.subSection3 = 'email-verification-needed'; 17756 digitalData.pageDetails.subSection4 = 'verify-failed'; 17757 break; 17758 case 'step3': 17759 digitalData.pageDetails.subSection2 = 'merge-accounts-step-3'; 17760 digitalData.pageDetails.subSection3 = 'choose-main-account'; 17761 digitalData.pageDetails.subSection4 = ''; 17762 break; 17763 case 'step4': 17764 digitalData.pageDetails.subSection2 = 'merge-accounts-step-4'; 17765 digitalData.pageDetails.subSection3 = 'review-accounts'; 17766 digitalData.pageDetails.subSection4 = ''; 17767 const message = 'We\'re merging your accounts. Track progress here in the Account settings'; 17768 AB.DATALAYER.setAnalyticsCrossPageData({ action: 'set', name: 'analyticsModalMessage', data: message, isAcrossPages: true }); 17769 break; 17770 default: 17771 break; 17772 } 17773 this.setPageName('loyalty', true); 17774 } 17775 AB.DATALAYER.setPageLoad(); 17776 digitalData.pageFilter.topNav = ''; 17777 } 17778 17779 this.initBundlesComponent = function () { 17780 const bundleEl = document.querySelectorAll('.image.section'); 17781 if (bundleEl.length > 0) { 17782 bundleEl?.forEach(element => { 17783 element.addEventListener('click', () => { 17784 const title = element.closest('div.image')?.parentElement?.querySelector('h3')?.textContent?.trim(); 17785 localStorage.setItem('analyticsBundleTitle', title); 17786 AB.DATALAYER.trackAEMZone('carousel', element, title); 17787 }); 17788 }); 17789 } 17790 } 17791 17792 this.trackBundleClicks = function (element) { 17793 const currentBundleParent = element?.closest('div:has(>webgen-bundles), div:has(>webgen-bundles-carousel)'); 17794 const currentBundle = element.closest('.bundles-box'); 17795 const title = currentBundle?.querySelector('.bundles-content-header')?.textContent?.trim(); 17796 const bundleContent = currentBundleParent?.querySelector('webgen-bundles')?.getAttribute('data-bundles-content') || currentBundleParent?.querySelector('webgen-bundles-carousel')?.getAttribute('data-bundles-carousel-content'); 17797 const bundleContentObj = AB.DATALAYER.safeJSONParse(bundleContent); 17798 let abTestDetails = ''; 17799 let bundleId = ''; 17800 17801 if (bundleContentObj.containerItems?.length > 0) { 17802 abTestDetails = bundleContentObj.p13nAdobeMetrics?.metricA; 17803 const currentBundleId = element?.closest('.bundles-box')?.getAttribute('data-analytics-id'); 17804 const currentBundleInfo = bundleContentObj.containerItems.find(bundle => bundle.bundleSlugId == currentBundleId); 17805 if (currentBundleInfo?.p13nAdobeMetrics) { 17806 abTestDetails = currentBundleInfo.p13nAdobeMetrics?.metricA; 17807 } 17808 bundleId = currentBundleId; 17809 } else { 17810 if ((bundleContentObj?.p13nAdobeMetrics)) { 17811 abTestDetails = bundleContentObj.p13nAdobeMetrics?.metricA; 17812 } 17813 bundleId = bundleContentObj?.bundleSlugId; 17814 } 17815 localStorage.setItem('bundleAbTestDetails', abTestDetails); 17816 this.setAnalyticsCrossPageData({ action: 'set', name: 'analyticsFeatureTracking', data: 'bundlesACIP400316', isAcrossPages: true }); 17817 this.analyticsCTATracking(`cta:${AB.DATALAYER.pathSections?.join(':')}|link|bundle`); 17818 AB.DATALAYER.trackAEMZone('carousel', element, `standard-bundle:${title}`, bundleId); 17819 } 17820 17821 this.trackRoktAdImpression = function (isRoktEnabled) { 17822 if (AB.DATALAYER.isCartAndCheckoutPage('confirmation') && isRoktEnabled) { 17823 function triggerAdImpression() { 17824 const roktAd = document.querySelector('#RoktAds'); 17825 if (roktAd && AB.DATALAYER.isElementVisible(roktAd)) {
17826 AB.DATALAYER.setLinkClickEvents('rokt-ad|impressions', true); 17827 document.removeEventListener('scroll', throttledImpression); 17828 } 17829 } 17830 const throttledImpression = AB.DATALAYER.throttleEvent(triggerAdImpression, 200); 17831 document.addEventListener('scroll', throttledImpression); 17832 } 17833 } 17834 17835 this.trackDietaryPrefs = function (prefType, prefOptions, ctaValue) { 17836 let curatedOptions = prefType === 'diets-you-shop-for' ? prefOptions : prefOptions?.map((option) => { 17837 if (option?.name) { 17838 return AB.DATALAYER.sanitizeAnalyticsText(option.name); 17839 } 17840 })?.join(':'); 17841 this.setAnalyticsCrossPageData({ action: 'set', name: 'analyticsFeatureTracking', data: 'dietaryPrefACIP455391' }); 17842 this.setModalClickEvents(`${prefType}|${AB.DATALAYER.sanitizeAnalyticsText(ctaValue)}`, `${prefType}|${curatedOptions ? curatedOptions : 'no-selection'}`); 17843 } 17844 17845 // Tracks clicks on forward/back arrows on coupons and bia carousels on homepage 17846 this.trackSlickArrows = function ({ caller, action, component } = {}) { 17847 if (AB.DATALAYER.isHomePage() && (caller === 'deals' || component?.dataset?.carouselDriven === 'buy-it-again' || component?.dataset?.apiDriven === 'buy-it-again' || component?.closest('targeted-wrapper, lazy-wrapper')?.dataset?.carouselBehaviour === 'buy-it-again')) { 17848 const arrowSelector = action === 'left' 17849 ? '.slick-prev, .slick-redesign-prev, .carousel-control-prev' 17850 : '.slick-next, .slick-redesign-next, .carousel-control-next'; 17851 const arrowElement = component?.matches?.(arrowSelector) ? component : component?.querySelector?.(arrowSelector); 17852 if (arrowElement) { 17853 AB.DATALAYER.setCRSHomeArrowClick(arrowElement); 17854 } 17855 return; 17856 } 17857 } 17858 17859 this.initSlickArrowTracking = function (component) { 17860 let leftArrow = component?.querySelector('.slick-prev'); 17861 let rightArrow = component?.querySelector('.slick-next'); 17862 if (leftArrow && rightArrow) { 17863 leftArrow?.addEventListener('click', () => { 17864 AB.DATALAYER.trackSlickArrows({ caller: 'deals', action: 'left', component: component }); 17865 }); 17866 rightArrow?.addEventListener('click', () => { 17867 AB.DATALAYER.trackSlickArrows({ caller: 'deals', action: 'right', component: component }); 17868 }); 17869 } else { 17870 setTimeout(() => { 17871 leftArrow = component?.querySelector('.slick-prev'); 17872 rightArrow = component?.querySelector('.slick-next'); 17873 if (leftArrow && rightArrow) { 17874 leftArrow?.addEventListener('click', () => { 17875 AB.DATALAYER.trackSlickArrows({ caller: 'deals', action: 'left', component: component }); 17876 }); 17877 rightArrow?.addEventListener('click', () => { 17878 AB.DATALAYER.trackSlickArrows({ caller: 'deals', action: 'right', component: component }); 17879 }); 17880 } 17881 }, 500); 17882 } 17883 } 17884 17885 this.trackProfileCompletion = function (profileObj) { 17886 if (profileObj.flowType === 'dismiss') { 17887 digitalData.pageDetails.subSection1 = AB.DATALAYER.pathSections[0]; 17888 digitalData.pageDetails.subSection2 = AB.DATALAYER.pathSections[1] || ''; 17889 digitalData.pageDetails.subSection3 = AB.DATALAYER.pathSections[2] || ''; 17890 digitalData.pageDetails.subSection4 = AB.DATALAYER.pathSections[3] || ''; 17891 digitalData.modal.modalMessage = ''; 17892 this.setAnalyticsCrossPageData({ action: 'remove', name: 'profileCompletionCheckBox' }); 17893 this.setAnalyticsCrossPageData({ action: 'remove', name: 'profileCompletionPoints' }); 17894 this.analyticsCTATracking(''); 17895 digitalData.pageFilter.topNav = ''; 17896 AB.DATALAYER.setPageName('loyalty'); 17897 } else { 17898 const rewardPoints = digitalData.crossPageData?.profileCompletionPoints?.data || 0; 17899 digitalData.pageDetails.subSection1 = 'complete-your-profile'; 17900 digitalData.pageDetails.subSection2 = `${profileObj.flowType}`; 17901 digitalData.modal.modalMessage = `${rewardPoints}#${profileObj.percentage}#${profileObj.step}`; 17902 digitalData.pageDetails.pageName = `${this.getBanner()}:loyalty:${digitalData.pageDetails.subSection1}:${digitalData.pageDetails.subSection2}`; 17903 digitalData.pageFilter.topNav = AB.DATALAYER.initTopNav(); 17904 } 17905 this.setPageLoad(); 17906 digitalData.pageFilter.topNav = ''; 17907 } 17908 17909 /** 17910 * Initializes the datalayer 17911 * (called by AB.COMMON.commonPreInit()) - will try to move this init within these files as well 17912 * Keep at the bottom of this file (datalayer-event-driven) to prevent any issues with functions not existing 17913 */ 17914 this.init = function () { 17915 console.info('@AnalyticsTeam - Initializing datalayer'); 17916 if (this.areCookiesEnabled() && !digitalData.initialisationCompleted) { 17917 this.isInitialized = true; 17918 this.initQueryParams(); 17919 this.initEventsFromPreviousPage(); 17920 this.initPage(); 17921 this.initPaymentPage(); 17922 this.initUser(); 17923 this.initPfmDetail(); 17924 this.initRecipeDetails(); 17925 this.initEpisodicOptInPage(); 17926 this.initCRSHomeClickLayer(); 17927 this.initFlexBannerClickLayer(); 17928 this.initCRSHomeUniversalTracking(); 17929 this.initBlogTracking(); 17930 this.initFlexModuleExposureOnLoad(); 17931 this.updatePathsectionsForSPA(); 17932 if (this.globalPageLoadRules()) { 17933 if (digitalData.crossPageData?.analyticsModalMessage?.data && digitalData.modal) { 17934 digitalData.modal.modalMessage = digitalData.crossPageData.analyticsModalMessage.data; 17935 } 17936 this.setPageLoad(); 17937 if (digitalData.crossPageData?.analyticsModalMessage) { 17938 delete digitalData.crossPageData.analyticsModalMessage; 17939 digitalData.modal.modalMessage = ''; 17940 this.setAnalyticsCrossPageData({ action: 'remove', name: 'analyticsModalMessage' }); 17941 } 17942 !this.isAccountResetPage() && this.handleAccountEvents(); 17943 } else if (this.isAislesL2Page()) { 17944 this.initAislesPage(); 17945 } else if (this.isPdp()) { 17946 this.initPDP(); 17947 } else if (this.isSubscriptionDeliveryPage()) { 17948 if (localStorage.getItem('analyticsLPpageLoad')) { 17949 const freshpassObj = AB.DATALAYER.safeJSONParse(AB.DATALAYER.handleGetLSValue('analyticsLPpageLoad')); 17950 this.updateAnalyticsSubscriptionInfo('landing', freshpassObj); 17951 } 17952 }
17952 else if (this.isPharmacyRxPage()) { 17953 if (localStorage.getItem('analyticsPharmacyPageLoad')) { 17954 this.initPharmacyRx(localStorage.getItem('analyticsPharmacyPageLoad')); 17955 localStorage.removeItem('analyticsPharmacyPageLoad'); 17956 } 17957 } else if (this.isSearchResultsPage()) { 17958 console.info('analytics datalayer page load event type is set in setSearch method'); 17959 } 17960 this.initBIAPage(); 17961 this.handleEvents(); 17962 this.initFireworkEvents(); 17963 this.initInHouseVideoEvents(); 17964 this.initStickyBannerImpressions(); 17965 if (window.location.pathname === '/pet-program.html') { 17966 this.initPetProgramHandlers(); 17967 } 17968 if (document.getElementById('delete-profile-modal')) { 17969 // Handles modal view for delete pet profile 17970 // Kept the Jquery since the event listener can't listen to the shown.bs.modal bootstrap custom event 17971 if (jQuery && typeof jQuery !== 'undefined') { 17972 $('#delete-profile-modal').on('shown.bs.modal', () => { 17973 AB.DATALAYER.setModalViewEvents('pet-profile-delete|view'); 17974 }); 17975 } 17976 } 17977 this.setTopNavCategoriesPage(); 17978 if (window.location.pathname === '/account/sign-in.html') { 17979 this.createAcountHandler(); 17980 } 17981 this.initBundlesComponent(); 17982 !this.isAccountResetPage() && (digitalData.initialisationCompleted = true); 17983 } else { 17984 console.warn('@AnalyticsTeam - datalayer is undefined or cookies are not enabled'); 17985 } 17986 }; 17987 17988 /** 17989 * Fires a product-impressions event for a Marketing Card embedded within a Product Carousel. 17990 * Triggered via IntersectionObserver once the carousel enters the viewport. 17991 * 17992 * Payload distinctions vs standard product-impressions: 17993 * page.bnrName = "<mktCardTitle>-<carouselTitle>|<promotionId>" 17994 * pageFilter.mTyp = "product-carousel-mktng-card" 17995 * pageFilter.carousel = sanitized carousel title 17996 * 17997 * Note â displayType "imageOnly" â mktCardTitle = sanitized backgroundImgAltText value. 17998 * 17999 * @param {Object} marketingTileData IMarketingTile object from CaaS / zone API response. 18000 * @param {string} carouselTitle Raw carousel title (data-carousel-title attribute value). 18001 * @param {Element} carouselWrapper DOM element used as IntersectionObserver target. 18002 */ 18003 this.sendMarketingCardImpression = function (marketingTileData, carouselTitle, carouselWrapper) { 18004 if (!marketingTileData) return; 18005 18006 const formattedCarouselTitle = AB.DATALAYER.sanitizeAnalyticsText(carouselTitle) || 'no-value'; 18007 18008 const mktCardTitle = marketingTileData.displayType === 'imageOnly' 18009 ? (AB.DATALAYER.sanitizeAnalyticsText(marketingTileData.backgroundImgAltText) || 'no-value') 18010 : (AB.DATALAYER.sanitizeAnalyticsText(marketingTileData.marketingTileTitle) || 'no-value'); 18011 18012 const promotionId = marketingTileData.promotionId || 'no-value'; 18013 const bnrName = `${mktCardTitle}-${formattedCarouselTitle}|${promotionId}`; 18014 18015 const observeTarget = (carouselWrapper && carouselWrapper.closest && ( 18016 carouselWrapper.closest('.master-product-carousel') || 18017 carouselWrapper.closest('.slick-product-carousel') || 18018 (carouselWrapper.localName === 'product-carousel-al-v1' ? carouselWrapper.querySelector('.carousel-content-wrapper') : null) || 18019 carouselWrapper 18020 )) || carouselWrapper; 18021 18022 var _pcData = AB.DATALAYER.safeJSONParse(carouselWrapper && carouselWrapper.getAttribute && carouselWrapper.getAttribute('data-products')) || {}; 18023 var _pcZoneMatch = (_pcData.zone || '').match(/\d+/); 18024 var _pcZoneNum = _pcZoneMatch ? parseInt(_pcZoneMatch[0], 10) : 0; 18025 var _pcZone = _pcZoneNum > 0 ? (_pcZoneNum < 10 ? '0' + _pcZoneNum : '' + _pcZoneNum) : '00'; 18026 var mktCardPlacement = 'r01#s01#z' + _pcZone; 18027 18028 const fireImpression = function () { 18029 const pageDetails = Object.assign({}, digitalData.pageDetails, { bnrName: bnrName }); 18030 const payload = { 18031 link: { linkName: 'product-impressions' }, 18032 page: pageDetails, 18033 pageFilter: Object.assign({}, digitalData.pageFilter, {
18034 mediaType: 'product-carousel-mktng-card', 18035 carousel: formattedCarouselTitle, 18036 placement: mktCardPlacement 18037 }), 18038 user: digitalData.user 18039 }; 18040 AB.DATALAYER.pushEvent('product-impressions', payload); 18041 digitalData.pageDetails.bnrName = ''; 18042 }; 18043 18044 if (observeTarget && typeof IntersectionObserver !== 'undefined') { 18045 var observer = new IntersectionObserver(function (entries, obs) { 18046 entries.forEach(function (entry) { 18047 if (entry.isIntersecting) { 18048 fireImpression(); 18049 obs.unobserve(observeTarget); 18050 } 18051 }); 18052 }, { root: null, rootMargin: '0px', threshold: 0.5 }); 18053 observer.observe(observeTarget); 18054 } else { 18055 fireImpression(); 18056 } 18057 }; 18058 18059 /** 18060 * Fires an experiment-exposure-loaded event for a Marketing Card embedded within a Product Carousel. 18061 * Triggered via IntersectionObserver once the carousel enters the viewport. 18062 * 18063 * Payload distinctions: 18064 * event.subEvent = "experiment-exposure" 18065 * link.name = "experiment-exposure-loaded" 18066 * page.bnrName = "<mktCardTitle>-<carouselTitle>|<promotionId>" 18067 * page.abTestDetails = p13nAdobeMetrics.msgExperiment (pre-formatted experiment string) 18068 * pageFilter.mTyp = "product-carousel-mktng-card" 18069 * pageFilter.carousel = sanitized carousel title 18070 * 18071 * @param {Object} marketingTileData IMarketingTile object from CaaS / zone API response. 18072 * @param {string} carouselTitle Raw carousel title (data-carousel-title attribute value). 18073 * @param {Element} carouselWrapper DOM element used as IntersectionObserver target. 18074 * @param {Object} p13nData p13n data object; expects p13nData.msgExperiment for abTestDetails. 18075 */ 18076 this.sendMarketingCardExposure = function (marketingTileData, carouselTitle, carouselWrapper, p13nData) { 18077 if (!marketingTileData) return; 18078 18079 const formattedCarouselTitle = AB.DATALAYER.sanitizeAnalyticsText(carouselTitle) || 'no-value'; 18080 18081 const mktCardTitle = marketingTileData.displayType === 'imageOnly' 18082 ? (AB.DATALAYER.sanitizeAnalyticsText(marketingTileData.backgroundImgAltText) || 'no-value') 18083 : (AB.DATALAYER.sanitizeAnalyticsText(marketingTileData.marketingTileTitle) || 'no-value'); 18084 18085 const promotionId = marketingTileData.promotionId || 'no-value'; 18086 const bnrName = `${mktCardTitle}-${formattedCarouselTitle}|${promotionId}`; 18087 const abTestDetails = p13nData?.msgExperiment || 'no-value'; 18088 18089 const observeTarget = (carouselWrapper && carouselWrapper.closest && carouselWrapper.closest('.master-product-carousel')) 18090 || carouselWrapper; 18091 18092 const fireExposure = function () { 18093 const pageDetails = Object.assign({}, digitalData.pageDetails, { bnrName: bnrName, abTestDetails: abTestDetails }); 18094 const payload = { 18095 link: { linkName: 'experiment-exposure-loaded' }, 18096 page: pageDetails, 18097 pageFilter: Object.assign({}, digitalData.pageFilter, { 18098 mediaType: 'product-carousel-mktng-card', 18099 carousel: formattedCarouselTitle 18100 }), 18101 user: digitalData.user 18102 }; 18103 AB.DATALAYER.pushEvent(AB.DATALAYER.EVENTS.LINK_CLICK, payload); 18104 digitalData.pageDetails.bnrName = ''; 18105 digitalData.pageDetails.abTestDetails = ''; 18106 }; 18107 18108 if (observeTarget && typeof IntersectionObserver !== 'undefined') { 18109 var observer = new IntersectionObserver(function (entries, obs) { 18110 entries.forEach(function (entry) { 18111 if (entry.isIntersecting) { 18112 fireExposure(); 18113 obs.unobserve(observeTarget); 18114 } 18115 }); 18116 }, { root: null, rootMargin: '0px', threshold: 0.5 }); 18117 observer.observe(observeTarget); 18118 } else { 18119 fireExposure(); 18120 } 18121 }; 18122 18123 /** 18124 * Fires a deals-impressions event for a Marketing Card embedded in a Deals / Coupon Carousel. 18125 * 18126 * Payload highlights:
18127 * link.linkName = "deals-impressions" 18128 * page.bnrName = "<mktCardTitle>-<carouselTitle>|<promotionId>" 18129 * page.abTestDetails = "impdeals#<p13nAdobeMetrics.msgExperiment>" (when experiment data available) 18130 * pageFilter.mTyp = "deals-carousel-mktng-card" (via mediaType â mTyp OneTag mapping) 18131 * pageFilter.carousel = sanitized carousel title 18132 * 18133 * @param {Object} marketingTileData IMarketingTile object. 18134 * @param {string} carouselTitle Raw carousel title. 18135 * @param {Element} carouselWrapper DOM element for IntersectionObserver (pass null for zone-API path). 18136 * @param {Object} p13nData Optional; expects p13nData.msgExperiment for abTestDetails prefix. 18137 */ 18138 this.sendDealsCarouselMarketingCardImpression = function (marketingTileData, carouselTitle, carouselWrapper, p13nData) { 18139 if (!marketingTileData) return; 18140 18141 const formattedCarouselTitle = AB.DATALAYER.sanitizeAnalyticsText(carouselTitle) || 'no-value'; 18142 18143 const mktCardTitle = marketingTileData.displayType === 'imageOnly' 18144 ? (AB.DATALAYER.sanitizeAnalyticsText(marketingTileData.backgroundImgAltText) || 'no-value') 18145 : (AB.DATALAYER.sanitizeAnalyticsText(marketingTileData.marketingTileTitle) || 'no-value'); 18146 18147 const promotionId = marketingTileData.promotionId || 'no-value'; 18148 const bnrName = `${mktCardTitle}-${formattedCarouselTitle}|${promotionId}`; 18149 18150 if (carouselWrapper && !carouselWrapper.hasAttribute('data-deals-carousel-content') && carouselWrapper.closest) { 18151 var _normalized = carouselWrapper.closest('[data-deals-carousel-content]'); 18152 if (_normalized) carouselWrapper = _normalized; 18153 } 18154 18155 if (carouselWrapper && carouselWrapper._dealsCardImpSetup) return; 18156 if (carouselWrapper) carouselWrapper._dealsCardImpSetup = true; 18157 18158 const observeTarget = (carouselWrapper && ( 18159 (carouselWrapper.querySelector && carouselWrapper.querySelector('.slick-coupon-carousel')) || 18160 (carouselWrapper.querySelector && carouselWrapper.querySelector('.coupon-carousel')) || 18161 carouselWrapper 18162 )) || carouselWrapper; 18163 18164 // Row from position among all [data-deals-carousel-content] wrappers 18165 var impAllDealsWrappers = Array.from(document.querySelectorAll('[data-deals-carousel-content]')); 18166 var impRowIdx = impAllDealsWrappers.indexOf(carouselWrapper) + 1; 18167 if (impRowIdx < 1) impRowIdx = 1; 18168 var impRow = impRowIdx < 10 ? '0' + impRowIdx : '' + impRowIdx; 18169 // Zone and slot from carousel JSON â zone API returns "zone":"zone1","zoneRenderedSlot":"7" in component data 18170 var _impCd = AB.DATALAYER.safeJSONParse(carouselWrapper?.getAttribute?.('data-deals-carousel-content')) || {}; 18171 var _impZoneMatch = (_impCd.zone || '').match(/\d+/); 18172 var impZoneNum = _impZoneMatch ? parseInt(_impZoneMatch[0], 10) : 0; 18173 var impZone = impZoneNum > 0 ? (impZoneNum < 10 ? '0' + impZoneNum : '' + impZoneNum) : '00'; 18174 var impSlotNum = 1; 18175 var impSlot = '01'; 18176 var mktCardImpPlacement = 'r' + impRow + '#s' + impSlot + '#z' + impZone; 18177 18178 const fireImpression = function () { 18179 const pageDetails = Object.assign({}, digitalData.pageDetails, { bnrName: bnrName }); 18180 if (p13nData?.msgExperiment) { 18181 pageDetails.abTestDetails = `impdeals#${p13nData.msgExperiment}`; 18182 } 18183 const payload = { 18184 link: { linkName: 'deals-impressions' }, 18185 page: pageDetails, 18186 pageFilter: Object.assign({}, digitalData.pageFilter, { 18187 mediaType: 'deals-carousel-mktng-card', 18188 carousel: formattedCarouselTitle, 18189 placement: mktCardImpPlacement 18190 }), 18191 user: digitalData.user 18192 }; 18193 AB.DATALAYER.pushEvent(AB.DATALAYER.EVENTS.LINK_CLICK, payload); 18194 digitalData.pageDetails.bnrName = ''; 18195 }; 18196 18197 if (observeTarget && typeof IntersectionObserver !== 'undefined') { 18198 var observer = new IntersectionObserver(function (entries, obs) {
18199 entries.forEach(function (entry) { 18200 if (entry.isIntersecting) { 18201 fireImpression(); 18202 obs.unobserve(observeTarget); 18203 } 18204 }); 18205 }, { root: null, rootMargin: '0px', threshold: 0.5 }); 18206 observer.observe(observeTarget); 18207 } else { 18208 fireImpression(); 18209 } 18210 }; 18211 18212 /** 18213 * Fires an experiment-exposure event for a Marketing Card embedded in a Deals / Coupon Carousel. 18214 * Only called when p13n experiment data is present in the zone API response. 18215 * 18216 * Payload highlights: 18217 * link.linkName = "experiment-exposure-loaded-deals" 18218 * page.bnrName = "<mktCardTitle>-<carouselTitle>|<promotionId>" 18219 * page.abTestDetails = p13nAdobeMetrics.msgExperiment 18220 * pageFilter.mTyp = "deals-carousel-mktng-card" 18221 * pageFilter.carousel = sanitized carousel title 18222 * 18223 * @param {Object} marketingTileData IMarketingTile object. 18224 * @param {string} carouselTitle Raw carousel title. 18225 * @param {Element} carouselWrapper DOM element for IntersectionObserver (pass null for zone-API path). 18226 * @param {Object} p13nData Expects p13nData.msgExperiment for abTestDetails. 18227 */ 18228 this.sendDealsCarouselMarketingCardExposure = function (marketingTileData, carouselTitle, carouselWrapper, p13nData) { 18229 if (!marketingTileData) return; 18230 18231 const formattedCarouselTitle = AB.DATALAYER.sanitizeAnalyticsText(carouselTitle) || 'no-value'; 18232 18233 const mktCardTitle = marketingTileData.displayType === 'imageOnly' 18234 ? (AB.DATALAYER.sanitizeAnalyticsText(marketingTileData.backgroundImgAltText) || 'no-value') 18235 : (AB.DATALAYER.sanitizeAnalyticsText(marketingTileData.marketingTileTitle) || 'no-value'); 18236 18237 const promotionId = marketingTileData.promotionId || 'no-value'; 18238 const bnrName = `${mktCardTitle}-${formattedCarouselTitle}|${promotionId}`; 18239 const abTestDetails = p13nData?.msgExperiment || 'no-value'; 18240 18241 // Normalize to the [data-deals-carousel-content] element for consistent zone/slot/row reading 18242 if (carouselWrapper && !carouselWrapper.hasAttribute('data-deals-carousel-content') && carouselWrapper.closest) { 18243 var _expNormalized = carouselWrapper.closest('[data-deals-carousel-content]'); 18244 if (_expNormalized) carouselWrapper = _expNormalized; 18245 } 18246 18247 const observeTarget = (carouselWrapper && ( 18248 (carouselWrapper.querySelector && carouselWrapper.querySelector('.slick-coupon-carousel')) || 18249 (carouselWrapper.querySelector && carouselWrapper.querySelector('.coupon-carousel')) || 18250 carouselWrapper 18251 )) || carouselWrapper; 18252 18253 var expAllDealsWrappers = Array.from(document.querySelectorAll('[data-deals-carousel-content]')); 18254 var expRowIdx = expAllDealsWrappers.indexOf(carouselWrapper) + 1; 18255 if (expRowIdx < 1) expRowIdx = 1; 18256 var expRow = expRowIdx < 10 ? '0' + expRowIdx : '' + expRowIdx; 18257 var _expCd = AB.DATALAYER.safeJSONParse(carouselWrapper?.getAttribute?.('data-deals-carousel-content')) || {}; 18258 var _expZoneMatch = (_expCd.zone || '').match(/\d+/); 18259 var expZoneNum = _expZoneMatch ? parseInt(_expZoneMatch[0], 10) : 0; 18260 var expZone = expZoneNum > 0 ? (expZoneNum < 10 ? '0' + expZoneNum : '' + expZoneNum) : '00'; 18261 var expSlotNum = parseInt(_expCd.zoneRenderedSlot, 10) || 1; 18262 var expSlot = expSlotNum < 10 ? '0' + expSlotNum : '' + expSlotNum; 18263 var mktCardExpPlacement = 'r' + expRow + '#s' + expSlot + '#z' + expZone; 18264 18265 const fireExposure = function () { 18266 const pageDetails = Object.assign({}, digitalData.pageDetails, { bnrName: bnrName, abTestDetails: abTestDetails }); 18267 const payload = { 18268 link: { linkName: 'experiment-exposure-loaded-deals' }, 18269 page: pageDetails, 18270 pageFilter: Object.assign({}, digitalData.pageFilter, { 18271 mediaType: 'deals-carousel-mktng-card', 18272 carousel: formattedCarouselTitle, 18273 placement: mktCardExpPlacement 18274 }), 18275 user: digitalData.user 18276 }; 18277 AB.DATALAYER.pushEvent(AB.DATALAYER.EVENTS.LINK_CLICK, payload); 18278 digitalData.pageDetails.bnrName = ''; 18279 digitalData.pageDetails.abTestDetails = ''; 18280 }; 18281 18282 if (observeTarget && typeof IntersectionObserver !== 'undefined') { 18283 var observer = new IntersectionObserver(function (entries, obs) {
18284 entries.forEach(function (entry) { 18285 if (entry.isIntersecting) { 18286 fireExposure(); 18287 obs.unobserve(observeTarget); 18288 } 18289 }); 18290 }, { root: null, rootMargin: '0px', threshold: 0.5 }); 18291 observer.observe(observeTarget); 18292 } else { 18293 fireExposure(); 18294 } 18295 }; 18296 18297 /** 18298 * Fires V218 (bannerCardCarousel) and V80 (mediaPlacementClick) click-tracking when a 18299 * Marketing Card CTA is clicked in a product carousel (cardType='product') or a deals / 18300 * coupon carousel (cardType='deals'). Follows the same bento pattern: set values on 18301 * digitalData.pageFilter + localStorage before calling setBannerClickedEvent, then restore. 18302 * 18303 * @param {Element} clickedElement - The anchor/button element that was clicked 18304 * @param {Object} marketingTileData - IMarketingTile object (marketingTileTitle, backgroundImgAltText, displayType, promotionId) 18305 * @param {string} carouselTitle - Unformatted carousel title string 18306 * @param {string} cardType - 'product' | 'deals' 18307 */ 18308 this.sendMarketingCardClickEvent = function (clickedElement, marketingTileData, carouselTitle, cardType) { 18309 if (!marketingTileData || !carouselTitle || !digitalData?.pageFilter) return; 18310 18311 const pad = function (v) { return Number(v) < 10 ? '0' + Number(v) : String(v); }; 18312 const formattedCarouselTitle = AB.DATALAYER.sanitizeAnalyticsText(carouselTitle) || 'no-value'; 18313 const mktCardTitle = marketingTileData.displayType === 'imageOnly' 18314 ? (AB.DATALAYER.sanitizeAnalyticsText(marketingTileData.backgroundImgAltText) || 'no-value') 18315 : (AB.DATALAYER.sanitizeAnalyticsText(marketingTileData.marketingTileTitle) || 'no-value'); 18316 const promotionId = marketingTileData.promotionId || 'no-value'; 18317 const cardTypeStr = cardType === 'deals' ? 'deal-card' : 'mkt-card'; 18318 const mediaType = cardType === 'deals' ? 'deals-carousel-mktng-card' : 'product-carousel-mktng-card'; 18319 const content = mktCardTitle + ':' + formattedCarouselTitle; 18320 18321 // For zone-API deals carousels: zone/slot/row from [data-deals-carousel-content] JSON. 18322 // For zone-API product carousels: zone from targeted-wrapper data-products JSON; row/slot always 01. 18323 // For AEM-authored carousels: fall back to getZoneInfo which walks the AEM zone DOM hierarchy. 18324 var _clickDealsEl = clickedElement && clickedElement.closest ? clickedElement.closest('[data-deals-carousel-content]') : null; 18325 var _clickTwEl = !_clickDealsEl && clickedElement && clickedElement.closest ? clickedElement.closest('targeted-wrapper') : null; 18326 var zone, row, slot; 18327 if (_clickDealsEl) { 18328 var _clickCd = AB.DATALAYER.safeJSONParse(_clickDealsEl.getAttribute('data-deals-carousel-content')) || {}; 18329 var _clickWrappers = Array.from(document.querySelectorAll('[data-deals-carousel-content]')); 18330 var _clickRowIdx = Math.max(_clickWrappers.indexOf(_clickDealsEl) + 1, 1); 18331 row = pad(_clickRowIdx); 18332 var _clickZoneNum = parseInt(((_clickCd.zone || '').match(/\d+/) || [])[0], 10) || 0; 18333 zone = pad(_clickZoneNum); 18334 slot = '01'; 18335 } else if (_clickTwEl) { 18336 var _clickTwData = AB.DATALAYER.safeJSONParse(_clickTwEl.getAttribute('data-products')) || {}; 18337 var _clickTwZoneNum = parseInt(((_clickTwData.zone || '').match(/\d+/) || [])[0], 10) || 0; 18338 zone = pad(_clickTwZoneNum); 18339 row = '01'; 18340 slot = '01'; 18341 } else { 18342 var _zi = {}; 18343 try { _zi = AB.DATALAYER.getZoneInfo(clickedElement) || {}; } catch (e) { /* no-op */ } 18344 zone = pad(parseInt(_zi.zone, 10) || 0); 18345 row = pad(Number(_zi.row) > 0 ? Number(_zi.row) : 1); 18346 slot = '01'; 18347 } 18348 18349 // V218: z<zone>|<card-type>|<card title>:<carousel title>|sp<row>|<unique id> (max 256 chars) 18350 const v218 = ('z' + zone + '|' + cardTypeStr + '|' + content + '|sp' + row + '|' + promotionId).slice(0, 256); 18351 18352 // Recommendation type and V80 placement
18353 const recommendationType = cardType === 'deals' ? 'not-recommended' : 'sponsored'; 18354 const placement = 'r' + row + '#s' + slot; 18355 // V80: <card-type>:<card title>#<recommendation>#r<row>#s01 (max 256 chars) 18356 const v80 = (cardTypeStr + ':' + mktCardTitle + '#' + recommendationType + '#' + placement).slice(0, 256); 18357 18358 // Cross-page persistence (read by next-page datalayer init) 18359 try { localStorage.setItem('aemZoneAnalytics', v218); } catch (e) { /* no-op */ } 18360 try { localStorage.setItem('mediaPlacementClick', v80); } catch (e) { /* no-op */ } 18361 18362 // Same-page snapshot â setBannerClickedEvent takes structuredClone of pageFilter immediately 18363 const previousV218 = digitalData.pageFilter.bannerCardCarousel; 18364 digitalData.pageFilter.bannerCardCarousel = v218; 18365 digitalData.pageFilter.mediaPlacementClick = v80; 18366 18367 try { 18368 AB.DATALAYER.setBannerClickedEvent( 18369 placement, // placement â pageFilter.placement 18370 'bannerclicked|' + content, // linkName 18371 recommendationType, // â pageFilter.recommendationType 18372 mediaType, // â pageFilter.mediaType 18373 content // bannerTitle â page.bnrName = content + '|no-value' 18374 ); 18375 } finally { 18376 digitalData.pageFilter.bannerCardCarousel = previousV218 || ''; 18377 digitalData.pageFilter.mediaPlacementClick = ''; 18378 } 18379 }; 18380 18381 /** 18382 * Registers a single document-level click listener (idempotent â guarded by _mktCardClickInit) 18383 * that fires sendMarketingCardClickEvent when any Marketing Card CTA inside a 18384 * .pds-marketing-card element is clicked. 18385 * 18386 * Data resolution order: 18387 * 1. DOM-attribute path: [data-mt-includeMarketingTile="true"] ancestor carries tile JSON 18388 * 2. Zone-API deals path: [data-deals-carousel-content] ancestor carries carousel JSON 18389 * 18390 * Call this once when any marketing-card carousel loads (trackP13nCarousel / setP13nExperimentExposure). 18391 */ 18392 /** 18393 * DOM-scan impression initialiser for Marketing Cards in AEM-authored Deals Carousels. 18394 * Mirrors the initStickyBannerImpressions pattern: runs at datalayer init time, 18395 * finds server-rendered [data-deals-carousel-content] wrappers, then uses a 18396 * MutationObserver to wait for Angular to render the .slick-coupon-carousel container 18397 * before handing off to sendDealsCarouselMarketingCardImpression + IntersectionObserver. 18398 */ 18399 this.initDealsCarouselMktCardTracking = function () { 18400 if (AB.DATALAYER._dealsCarouselMktCardTrackingInit || typeof MutationObserver === 'undefined') return; 18401 AB.DATALAYER._dealsCarouselMktCardTrackingInit = true; 18402 18403 var wrappers = document.querySelectorAll('[data-deals-carousel-content]'); 18404 wrappers.forEach(function (wrapper) { 18405 var carouselData = AB.DATALAYER.safeJSONParse(wrapper.getAttribute('data-deals-carousel-content')); 18406 if (!carouselData || carouselData.includeMarketingTile !== 'true' || !carouselData.marketingTile) return; 18407 18408 var carouselTitle = wrapper.getAttribute('data-carousel-title') || carouselData.title || ''; 18409 var marketingTile = carouselData.marketingTile; 18410 18411 var trySetup = function () { 18412 if (!wrapper.querySelector('.slick-coupon-carousel')) return false; 18413 AB.DATALAYER.sendDealsCarouselMarketingCardImpression(marketingTile, carouselTitle, wrapper, null); 18414 return true; 18415 }; 18416 18417 if (!trySetup()) { 18418 var mutObs = new MutationObserver(function (mutations, observer) { 18419 if (trySetup()) { observer.disconnect(); } 18420 }); 18421 mutObs.observe(wrapper, { childList: true, subtree: true }); 18422 } 18423 }); 18424 }; 18425 18426 this.initMarketingCardClickTracking = function () { 18427 if (AB.DATALAYER._mktCardClickInit) return; 18428 AB.DATALAYER._mktCardClickInit = true; 18429 18430 document.addEventListener('click', function (e) { 18431 if (!e.target || !e.target.closest) return; 18432 var link = e.target.closest('a') || e.target.closest('button'); 18433 if (!link) return; 18434 var card = link.closest('.pds-marketing-card'); 18435 if (!card) return; 18436 18437 // DOM-attribute path: tile JSON on lazy-wrapper (AEM-authored carousels) 18438 var lazyWrapper = card.closest('[data-mt-includeMarketingTile="true"]');
18439 if (lazyWrapper) { 18440 var mktTileJson = lazyWrapper.getAttribute('data-mt-marketingTileJson'); 18441 var mktTile = mktTileJson && AB.DATALAYER.safeJSONParse(mktTileJson); 18442 if (mktTile) { 18443 var carouselTitle = lazyWrapper.getAttribute('data-carousel-title') || ''; 18444 var isCoupon = !!card.closest('.slick-coupon-carousel'); 18445 AB.DATALAYER.sendMarketingCardClickEvent(link, mktTile, carouselTitle, isCoupon ? 'deals' : 'product'); 18446 return; 18447 } 18448 } 18449 18450 // Zone-API deals path: carousel JSON on deals-carousel-wrapper ancestor 18451 var dealsWrapper = card.closest('[data-deals-carousel-content]'); 18452 if (dealsWrapper) { 18453 var carouselData = AB.DATALAYER.safeJSONParse(dealsWrapper.getAttribute('data-deals-carousel-content')); 18454 if (carouselData && carouselData.includeMarketingTile === 'true' && carouselData.marketingTile) { 18455 var dealsCarouselTitle = dealsWrapper.getAttribute('data-carousel-title') || carouselData.title || ''; 18456 AB.DATALAYER.sendMarketingCardClickEvent(link, carouselData.marketingTile, dealsCarouselTitle, 'deals'); 18457 return; 18458 } 18459 } 18460 18461 // Zone-API product carousel path: marketing tile data inside targeted-wrapper data-products JSON 18462 var twWrapper = card.closest('targeted-wrapper'); 18463 if (twWrapper) { 18464 var twData = AB.DATALAYER.safeJSONParse(twWrapper.getAttribute('data-products')); 18465 if (twData && twData.includeMarketingTile === 'true' && twData.marketingTile) { 18466 var twCarouselTitle = twWrapper.getAttribute('data-carousel-title') || twData.carouselTitle || twData.title || ''; 18467 AB.DATALAYER.sendMarketingCardClickEvent(link, twData.marketingTile, twCarouselTitle, 'product'); 18468 } 18469 } 18470 }); 18471 }; 18472 18473}).call(AB.DATALAYER); 18474 18475if (AB?.DATALAYER && Object.keys(AB.DATALAYER).length > 0) { 18476 window.eddlLoadComplete = true; 18477 window.shouldCnCCallEddl = true; 18478 window.shouldOrderAheadCallEddl = true; 18479 AB.DATALAYER.initMarketingCardClickTracking(); 18480 AB.DATALAYER.initDealsCarouselMktCardTracking(); 18481 let eddlLoadComplete = new Event('eddlLoadComplete'); 18482 document.dispatchEvent(eddlLoadComplete); 18483 let cartUpdateFromLs = function () { 18484 if (digitalData?.cart?.cartInfo && Object.keys(digitalData.cart.cartInfo).length === 0 && AB.DATALAYER.getLSItem('abCart') && !AB.DATALAYER.isSelectiveCartPage()) { 18485 AB.DATALAYER.updateCart(AB.DATALAYER.safeJSONParse(AB.DATALAYER.getLSItem('abCart'))); 18486 } 18487 } 18488 if (AB.DATALAYER.getLSItem('abCart')) { 18489 cartUpdateFromLs(); 18490 } else { 18491 document.addEventListener('cartSavedToLS', cartUpdateFromLs); 18492 } 18493 18494} 18495 18496if (window.AB?.DATALAYER?.isWeeklyAdPage()) { 18497 window.AB.DATALAYER.init(); 18498} 18499
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.