{"version":3,"file":"get-the-look.js","sources":["../../../resources/javascript/features/hotspots/index.js","../../../resources/javascript/features/content-block/get-the-look/index.js","../../../resources/javascript/entrypoint/get-the-look.js"],"sourcesContent":["export const hotspotsReady = new Promise((resolve) => {\n  if (typeof 'HotspotsMedia' !== undefined) {\n    resolve();\n  } else {\n    document.addEventListener('hotspots-ready', () => { resolve(); }, { once: true });\n  }\n});\n","import { getContentBlockListName } from '..';\nimport { getProductElementData, trackProductElementClick, trackProductListImpressions } from '../../tracking/list';\nimport { addCacheParams, fixSiteURL } from '../../../util/url';\nimport { addFeatureURLParams } from '../../../util/features';\nimport { hotspotsReady } from '../../hotspots';\n\nconst loadLookCard = async (lookId, params = {}) => {\n  const url = fixSiteURL(`/look/${lookId}/card`);\n  url.searchParams.set('ajax', '1');\n  addCacheParams(url);\n\n  for (const [key, value] of Object.entries(params)) {\n    url.searchParams.set(key, value);\n  }\n\n  addFeatureURLParams(url.searchParams, [\n    // keep-sorted start\n    'hotspot-qatb-wishlist',\n    'show-gtl-quick-add',\n    // keep-sorted end\n  ]);\n\n  url.searchParams.sort();\n\n  try {\n    const response = await fetch(url, {\n      credentials: 'include',\n    });\n    if (response.status !== 200) {\n      throw new Error(`HTTP error ${response.status}`);\n    }\n\n    return await response.json();\n  } catch (e) {\n    throw new Error('Failed to load get the look card', {\n      cause: e,\n    });\n  }\n};\n\nconst replaceLookCard = async (placeholder, params) => {\n  try {\n    const lookId = placeholder.dataset.lookId;\n    if (!lookId) {\n      throw new Error('No look id found');\n    }\n\n    const { html } = await loadLookCard(lookId, params);\n\n    const div = document.createElement('div');\n    div.innerHTML = html;\n    const card = div.children[0];\n\n    placeholder.replaceWith(card);\n\n    return card;\n  } catch (e) {\n    placeholder.remove();\n    return null;\n  }\n};\n\n/**\n * Initialise Get The Look content blocks.\n *\n * This function must be called on pages that include Get The Look content\n * blocks, but no more than once per page load.\n */\nexport const initGetTheLookBlocks = async () => {\n  const observer = new IntersectionObserver((entries) => {\n    for (const entry of entries) {\n      if (!entry.isIntersecting) {\n        continue;\n      }\n\n      // Unobserve, so we only record impressions once per carousel.\n      observer.unobserve(entry.target);\n\n      // set up tracking for each look in the block. Each look has 2 lists, one\n      // for the product cards and one for hotspots\n      const looks = entry.target.querySelectorAll('.get-the-look');\n      hotspotImpression(entry.target, 0, looks[0]);\n\n      if (entry.target.querySelector('.slick-initialized')) {\n        lookComponentImpression(entry.target, 0, looks[0], 0);\n      } else {\n        $(entry.target).one('init', () => {\n          lookComponentImpression(entry.target, 0, looks[0], 0);\n        });\n      }\n\n      for (const [lookPosition, look] of looks.entries()) {\n        // Add click event listeners to each product card in the look\n        const cardsList = getContentBlockListName(entry.target, [lookPosition + 1, 'Cards']);\n        const productLinks = look.querySelectorAll('.get-the-look-preview__link');\n        for (const [position, productLink] of productLinks.entries()) {\n          productLink.addEventListener('click', (e) => trackProductElementClick(e, cardsList, { position }));\n        }\n\n        // Add click event listeners to each hotspot in the look and get the\n        // tracking data from the DOM.\n        const hotspotsList = getContentBlockListName(entry.target, [lookPosition + 1, 'Hotspots']);\n        const hotspots = [...look.querySelectorAll('.hotspots_position__point')]\n          .sort((a, b) => b.getBoundingClientRect().x < a.getBoundingClientRect().x);\n        for (const [position, hotspot] of hotspots.entries()) {\n\n          hotspot.addEventListener('click', (e) => {\n            // setup details to be tracked for this, actual handling is in hotspots.js\n            const product = e.target.closest('.hotspots_position__point');\n            MV.tracking.update(product.dataset.gaid, hotspotsList, position, window.location.pathname);\n          }, { once: true });\n        }\n      }\n    }\n  });\n\n  const blocks = document.querySelectorAll('.block--get-the-look');\n\n  await Promise.all([...blocks].map((block) => (async () => {\n    const placeholders = block.querySelectorAll('.get-the-look--placeholder');\n    await Promise.all([...placeholders].map(async (placeholder) => replaceLookCard(placeholder)));\n\n    block.querySelectorAll('.carousel--get-the-look').forEach((carousel) => {\n      const trackedLooks = [true];\n      const trackedLookComponents = [\n        [true]\n      ];\n      const looks = carousel.querySelectorAll('.get-the-look');\n\n      // ensure the carousel is active before tracking the first look\n      if (carousel.querySelector('.js-carousel--get-the-look.slick-initialized')) {\n        observeCarousel(observer, carousel, looks, trackedLooks, trackedLookComponents);\n      } else {\n        $(carousel).one('init', () => {\n          observeCarousel(observer, carousel, looks, trackedLooks, trackedLookComponents);\n        });\n      }\n    });\n  })()));\n\n  // Initialise the carousels once all the blocks have been loaded.\n  // It doesn't seem to work if we do it as each one loads in.\n  //\n  // NB: Carousel is in the global scope.\n  // See resources/javascript/mv2020/plugins/carousel.js\n  $('.js-carousel--get-the-look').each(function (i) {\n    var nav = this.parentNode.querySelector('.carousel__nav');\n\n    nav.classList.add('carousel__nav--active');\n    this.classList.add('js-carousel--get-the-look--' + i);\n\n    $(this).on('init', () => { updateCardVisibility(this); });\n    $(this).on('afterChange', () => { updateCardVisibility(this); });\n\n    var getTheLook = new Carousel({\n      arrows: true,\n      dots: false,\n      infinite: false,\n      appendArrows: $(nav),\n      swipe: false,\n    }, 'get-the-look--' + i);\n    getTheLook.init($(this));\n  });\n\n  $('.js-carousel--get-the-look-components').each(function (i) {\n    var nav = this.parentNode.querySelector('.carousel__nav');\n\n    nav.classList.add('carousel__nav--active');\n    this.classList.add('js-carousel--get-the-look-components--' + i);\n\n    var getTheLookComponents = new Carousel({\n      arrows: false,\n      dots: true,\n      rows: 2,\n      slidesPerRow: 3,\n      appendDots: $(nav),\n      infinite: false,\n      variableWidth: false,\n      responsive: [{\n        breakpoint: 999,\n        settings: 'unslick',\n      }]\n    }, 'get-the-look-components--' + i);\n    getTheLookComponents.init($(this));\n  });\n\n  await hotspotsReady;\n  for (const block of blocks) {\n    const looks = block.querySelectorAll('.get-the-look');\n\n    for (const look of looks) {\n      const mediaId = look.querySelector('.hotspots_position__container')?.dataset.media;\n      if (mediaId) {\n        HotspotsMedia.factory(false, mediaId, block.id);\n      }\n    }\n  }\n};\n\nconst observeCarousel = (observer, carousel, looks, trackedLooks, trackedLookComponents) => {\n  observer.observe(carousel);\n\n  $(carousel).find('>.js-carousel--get-the-look').on('afterChange', (event, slick, thisLook) => {\n    if (trackedLooks.hasOwnProperty(thisLook)) {\n      return;\n    }\n    trackedLooks[thisLook] = true;\n\n    // track the hotspots and initial cards for the look\n    hotspotImpression(carousel, thisLook, looks[thisLook]);\n    lookComponentImpression(carousel, thisLook, looks[thisLook], 0);\n  });\n\n  // setup tracking for each look component in each look\n  [...looks].forEach((look, lookPosition) => {\n    $(look).find('.js-carousel--get-the-look-components').on('afterChange', (event, slick, thisLookComponent) => {\n      event.stopPropagation(); // dont trigger the parent afterChange event\n\n      if (trackedLookComponents[lookPosition].hasOwnProperty(thisLookComponent)) {\n        return;\n      }\n      trackedLookComponents[lookPosition][thisLookComponent] = true;\n\n      // track the cards for the visible look component\n      lookComponentImpression(carousel, lookPosition, looks[lookPosition], thisLookComponent);\n    });\n  });\n};\n\n// push productImpressions to the dataLayer for cards in a look on a sub slide\nconst lookComponentImpression = (carousel, lookPosition, look, thisLookComponentPosition,) => {\n  // get the cards for the current slide on the current look\n  const lookComponentSlide = look.querySelector('.slick-slide[data-slick-index=\"' + thisLookComponentPosition + '\"]');\n  const productLinks = lookComponentSlide?.querySelectorAll('.get-the-look-preview__link');\n\n  if (productLinks == undefined || 0 === productLinks.length) return;\n\n  const cardItems = [];\n\n  for (const [position, productLink] of productLinks.entries()) {\n    const relativePosition = (thisLookComponentPosition * 6) + position;\n    cardItems.push(getProductElementData(productLink, { position: relativePosition }));\n  }\n  trackProductListImpressions({ list: getContentBlockListName(carousel, [lookPosition + 1, 'Cards']), items: cardItems });\n};\n\n// push productImpressions to the dataLayer for hotspots in a look\nconst hotspotImpression = (carousel, lookPosition, look) => {\n  // get the hotspots for the current look\n  const hotspots = [...look.querySelectorAll('.hotspots_position__point')]\n    .sort((a, b) => b.getBoundingClientRect().x < a.getBoundingClientRect().x);\n\n  if (0 === hotspots.length) return;\n\n  const hotspotItems = [];\n\n  for (const [position, hotspot] of hotspots.entries()) {\n    hotspotItems.push(getProductElementData(hotspot, { position }));\n  }\n\n  trackProductListImpressions({ list: getContentBlockListName(carousel, [lookPosition + 1, 'Hotspots']), items: hotspotItems });\n};\n\n/**\n * Update component state to match the visibility of the look cards in the carousel.\n * @param {HTMLElement} carousel\n */\nconst updateCardVisibility = (carousel) => {\n  for (const slide of carousel.querySelectorAll('.get-the-look.slick-slide')) {\n    const visible = slide.classList.contains('slick-active');\n\n    if (visible) {\n      for (const el of slide.querySelectorAll('[data-old-tab-index]')) {\n        el.tabIndex = parseInt(el.dataset.oldTabIndex, 10); // restore the old tabindex\n        delete el.dataset.oldTabIndex; // remove the old tabindex\n      }\n    } else {\n      for (const el of slide.querySelectorAll('[tabindex=\"0\"]')) {\n        el.dataset.oldTabIndex = el.tabIndex.toString(); // store the old tabindex\n        el.tabIndex = -1; // remove focus from elements that are not in view\n      }\n    }\n  }\n};\n","import { initGetTheLookBlocks } from '../features/content-block/get-the-look';\nimport { onDOMContentLoaded } from '../util/loaded';\n\nonDOMContentLoaded(initGetTheLookBlocks);\n"],"names":["hotspotsReady","Promise","resolve","replaceLookCard","async","placeholder","params","lookId","dataset","Error","html","url","fixSiteURL","searchParams","set","addCacheParams","key","value","Object","entries","addFeatureURLParams","sort","response","fetch","credentials","status","json","e","cause","loadLookCard","div","document","createElement","innerHTML","card","children","replaceWith","remove","observeCarousel","observer","carousel","looks","trackedLooks","trackedLookComponents","observe","$","find","on","event","slick","thisLook","hasOwnProperty","hotspotImpression","lookComponentImpression","forEach","look","lookPosition","thisLookComponent","stopPropagation","thisLookComponentPosition","lookComponentSlide","querySelector","productLinks","querySelectorAll","undefined","length","cardItems","position","productLink","relativePosition","push","getProductElementData","trackProductListImpressions","list","getContentBlockListName","items","hotspots","a","b","getBoundingClientRect","x","hotspotItems","hotspot","updateCardVisibility","slide","classList","contains","el","tabIndex","parseInt","oldTabIndex","toString","onDOMContentLoaded","IntersectionObserver","entry","isIntersecting","unobserve","target","one","cardsList","addEventListener","trackProductElementClick","hotspotsList","product","closest","MV","tracking","update","gaid","window","location","pathname","once","blocks","all","map","block","placeholders","each","i","nav","this","parentNode","add","Carousel","arrows","dots","infinite","appendArrows","swipe","init","rows","slidesPerRow","appendDots","variableWidth","responsive","breakpoint","settings","_look$querySelector","mediaId","media","HotspotsMedia","factory","id"],"mappings":"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"}