@liquidcommercedev/rmn-sdk 1.5.0-beta.18 → 1.5.0-beta.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -15947,7 +15947,7 @@ class LocalStorageService {
15947
15947
  LocalStorageService.localStorageKeyPrefix = 'lc_rmn';
15948
15948
  LocalStorageService.localStorageKey = '';
15949
15949
  LocalStorageService.spotExpirationTime = 1000 * 60 * 60 * 24 * 7; // 7 days
15950
- LocalStorageService.encryptData = true;
15950
+ LocalStorageService.encryptData = false;
15951
15951
 
15952
15952
  /**
15953
15953
  * PubsubService class
@@ -16108,6 +16108,12 @@ const CAROUSEL_COMPONENT_STYLE = ({ width, height, fluid }) => `
16108
16108
  height: ${fluid ? '100%' : `${height}px`};
16109
16109
  }
16110
16110
 
16111
+ .container {
16112
+ position: relative;
16113
+ width: 100%;
16114
+ height: 100%;
16115
+ }
16116
+
16111
16117
  .slides {
16112
16118
  position: relative;
16113
16119
  height: 100%;
@@ -16303,57 +16309,74 @@ const CAROUSEL_COMPONENT_STYLE = ({ width, height, fluid }) => `
16303
16309
  let CarouselElement;
16304
16310
  if (typeof window !== 'undefined' && typeof window.customElements !== 'undefined') {
16305
16311
  class CustomCarouselElement extends HTMLElement {
16312
+ /**
16313
+ * Initializes the web component and attaches shadow DOM
16314
+ * Binds all event handlers to maintain proper 'this' context
16315
+ */
16306
16316
  constructor() {
16307
16317
  super();
16308
- this.currentSlide = 0;
16309
- this.dotElements = [];
16310
- this.prevButton = null;
16311
- this.nextButton = null;
16312
- this.autoplayInterval = null;
16313
- this.useDots = false;
16314
- this.useButtons = false;
16318
+ this.state = {
16319
+ currentSlide: 0,
16320
+ autoplayInterval: null,
16321
+ isAutoplayPaused: false,
16322
+ useDots: false,
16323
+ useButtons: false,
16324
+ autoplay: true,
16325
+ interval: CustomCarouselElement.defaultConfigs.interval,
16326
+ dots: { ...CustomCarouselElement.defaultConfigs.dots },
16327
+ buttons: { ...CustomCarouselElement.defaultConfigs.buttons },
16328
+ isDragging: false,
16329
+ startX: 0,
16330
+ currentX: 0,
16331
+ dragStartTime: 0,
16332
+ dragDistance: 0,
16333
+ containerWidth: 0,
16334
+ isTransitioning: false,
16335
+ realIndex: 0, // Track the actual slide index
16336
+ virtualIndex: 1, // Track the virtual slide position (start at 1 to show clone)
16337
+ isVirtualizing: false, // Flag for handling virtual slide transitions
16338
+ };
16339
+ this.elements = {
16340
+ slidesContainer: null,
16341
+ dots: [],
16342
+ prevButton: null,
16343
+ nextButton: null,
16344
+ };
16315
16345
  this.originalFontSizes = new Map();
16346
+ this.cloneToOriginalMap = new WeakMap();
16316
16347
  this.attachShadow({ mode: 'open' });
16348
+ this.handleTransitionEnd = this.handleTransitionEnd.bind(this);
16349
+ this.handleTouchStart = this.handleTouchStart.bind(this);
16350
+ this.handleTouchMove = this.handleTouchMove.bind(this);
16351
+ this.handleTouchEnd = this.handleTouchEnd.bind(this);
16352
+ this.handleDragStart = this.handleDragStart.bind(this);
16353
+ this.handleDrag = this.handleDrag.bind(this);
16354
+ this.handleDragEnd = this.handleDragEnd.bind(this);
16355
+ this.updateContainerWidth = this.updateContainerWidth.bind(this);
16317
16356
  }
16357
+ /**
16358
+ * Web component lifecycle method called when element is added to DOM
16359
+ * Initializes state, renders UI, sets up events and starts autoplay
16360
+ */
16318
16361
  connectedCallback() {
16319
- this.initializeOptions();
16320
- this.setupResizeObserver();
16362
+ this.initializeState();
16321
16363
  this.render();
16322
16364
  this.setupCarousel();
16365
+ this.setupTouchEvents();
16366
+ this.startAutoplayIfEnabled();
16367
+ this.updateContainerWidth();
16368
+ window.addEventListener('resize', this.updateContainerWidth.bind(this));
16369
+ this.setupResizeObserver();
16323
16370
  }
16371
+ /**
16372
+ * Web component lifecycle method called when element is removed from DOM
16373
+ * Cleans up all event listeners and intervals
16374
+ */
16324
16375
  disconnectedCallback() {
16325
16376
  var _a;
16326
- this.stopAutoplay();
16377
+ this.cleanupEventListeners();
16327
16378
  (_a = this.resizeObserver) === null || _a === void 0 ? void 0 : _a.disconnect();
16328
16379
  }
16329
- initializeOptions() {
16330
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
16331
- this.useDots = ((_a = this.data) === null || _a === void 0 ? void 0 : _a.useDots) === true || typeof ((_b = this.data) === null || _b === void 0 ? void 0 : _b.useDots) === 'object';
16332
- this.useButtons = ((_c = this.data) === null || _c === void 0 ? void 0 : _c.useButtons) === true || typeof ((_d = this.data) === null || _d === void 0 ? void 0 : _d.useButtons) === 'object';
16333
- this.autoplay = (_f = (_e = this.data) === null || _e === void 0 ? void 0 : _e.autoplay) !== null && _f !== void 0 ? _f : true;
16334
- this.interval = (_h = (_g = this.data) === null || _g === void 0 ? void 0 : _g.interval) !== null && _h !== void 0 ? _h : CustomCarouselElement.defaultInterval;
16335
- this.dotsOptions = {
16336
- position: 'bottom-center',
16337
- color: '#d9d9d9',
16338
- activeColor: '#b5914a',
16339
- size: 'base',
16340
- opacity: 1,
16341
- ...(typeof ((_j = this.data) === null || _j === void 0 ? void 0 : _j.useDots) === 'object' ? this.data.useDots : {}),
16342
- };
16343
- this.buttonsOptions = {
16344
- together: false,
16345
- position: 'middle-sides',
16346
- textColor: '#000000',
16347
- backgroundColor: '#ffffff',
16348
- borderRadius: '50%',
16349
- prev: 'Prev',
16350
- next: 'Next',
16351
- size: 'base',
16352
- opacity: 1,
16353
- ...(typeof ((_k = this.data) === null || _k === void 0 ? void 0 : _k.useButtons) === 'object' ? this.data.useButtons : {}),
16354
- };
16355
- this.validateOptions();
16356
- }
16357
16380
  setupResizeObserver() {
16358
16381
  if (this.data && !this.data.fluid) {
16359
16382
  this.resizeObserver = new ResizeObserverService({
@@ -16368,18 +16391,20 @@ if (typeof window !== 'undefined' && typeof window.customElements !== 'undefined
16368
16391
  }
16369
16392
  }
16370
16393
  handleCarouselSizeChanged(event) {
16371
- const isRBSpot = 'rbHeroadaksda'.startsWith('rb');
16372
- if (!isRBSpot) {
16373
- // Adjust text elements font size based on the scale factor
16374
- this.adjustFontSize(event.detail.scale);
16375
- }
16394
+ // Adjust text elements font size based on the scale factor
16395
+ this.adjustFontSize(event.detail.scale);
16376
16396
  }
16377
16397
  adjustFontSize(elementScale) {
16378
16398
  var _a;
16379
- const scaleFactor = calculateScaleFactor(elementScale);
16380
16399
  // Find all text elements within the shadow root
16381
- const elements = (_a = this.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelectorAll('h1, h2, h3, h4, p, span');
16382
- elements === null || elements === void 0 ? void 0 : elements.forEach((element) => {
16400
+ const selectors = ['h1', 'h2', 'h3', 'h4', 'p', 'span']
16401
+ .map((tag) => `[data-spot="iab"] ${tag}`)
16402
+ .join(', ');
16403
+ const elements = (_a = this.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelectorAll(selectors);
16404
+ if (!elements)
16405
+ return;
16406
+ const scaleFactor = calculateScaleFactor(elementScale);
16407
+ elements.forEach((element) => {
16383
16408
  if (element instanceof HTMLElement) {
16384
16409
  if (!this.originalFontSizes.has(element)) {
16385
16410
  const originalSize = parseFloat(window.getComputedStyle(element).fontSize);
@@ -16391,179 +16416,610 @@ if (typeof window !== 'undefined' && typeof window.customElements !== 'undefined
16391
16416
  }
16392
16417
  });
16393
16418
  }
16419
+ /** State and Configuration Management */
16420
+ /**
16421
+ * Initializes all state values from provided configuration
16422
+ * Sets up dots, buttons, autoplay and other feature flags
16423
+ */
16424
+ initializeState() {
16425
+ var _a, _b, _c, _d, _e, _f, _g, _h;
16426
+ this.state.useDots = Boolean((_a = this.data) === null || _a === void 0 ? void 0 : _a.useDots);
16427
+ this.state.useButtons = Boolean((_b = this.data) === null || _b === void 0 ? void 0 : _b.useButtons);
16428
+ this.state.autoplay = (_d = (_c = this.data) === null || _c === void 0 ? void 0 : _c.autoplay) !== null && _d !== void 0 ? _d : true;
16429
+ this.state.interval = (_f = (_e = this.data) === null || _e === void 0 ? void 0 : _e.interval) !== null && _f !== void 0 ? _f : CustomCarouselElement.defaultConfigs.interval;
16430
+ if (typeof ((_g = this.data) === null || _g === void 0 ? void 0 : _g.useDots) === 'object') {
16431
+ this.state.dots = { ...this.state.dots, ...this.data.useDots };
16432
+ }
16433
+ if (typeof ((_h = this.data) === null || _h === void 0 ? void 0 : _h.useButtons) === 'object') {
16434
+ this.state.buttons = { ...this.state.buttons, ...this.data.useButtons };
16435
+ }
16436
+ this.validateConfiguration();
16437
+ }
16438
+ /**
16439
+ * Validates all configuration options
16440
+ * Ensures positions and other settings are valid
16441
+ */
16442
+ validateConfiguration() {
16443
+ this.validatePosition(this.state.dots.position, 'dotsPosition', 'bottom-center');
16444
+ this.validateButtonsPosition();
16445
+ }
16446
+ /**
16447
+ * Validates navigation element positions
16448
+ * Ensures dots and buttons are placed in valid locations
16449
+ */
16450
+ validatePosition(position, optionName, defaultValue) {
16451
+ if (!CustomCarouselElement.validPositions.has(position)) {
16452
+ console.warn(`Invalid ${optionName}: ${position}. Defaulting to '${defaultValue}'.`);
16453
+ if (optionName === 'dotsPosition') {
16454
+ this.state.dots.position = defaultValue;
16455
+ }
16456
+ else if (optionName === 'buttonsPosition') {
16457
+ this.state.buttons.position = defaultValue;
16458
+ }
16459
+ }
16460
+ }
16461
+ /** Rendering and DOM Management */
16462
+ /**
16463
+ * Main rendering function that builds the carousel structure
16464
+ * Creates slides, dots, and navigation buttons
16465
+ */
16394
16466
  render() {
16395
- var _a;
16396
16467
  if (!this.shadowRoot)
16397
16468
  return;
16398
16469
  const style = document.createElement('style');
16399
16470
  style.textContent = CAROUSEL_COMPONENT_STYLE(this.data);
16400
16471
  this.shadowRoot.appendChild(style);
16401
- const slides = this.renderSlides();
16402
- this.shadowRoot.appendChild(slides);
16403
- this.slidesContainer = (_a = this.shadowRoot.querySelector('.slides')) !== null && _a !== void 0 ? _a : undefined;
16404
- if (this.useDots) {
16405
- const dots = this.renderDots();
16406
- if (dots)
16407
- this.shadowRoot.appendChild(dots);
16472
+ const carouselContainer = document.createElement('div');
16473
+ carouselContainer.className = 'container';
16474
+ const slides = this.createSlides();
16475
+ carouselContainer.appendChild(slides);
16476
+ if (this.state.useDots) {
16477
+ const dots = this.createDots();
16478
+ carouselContainer.appendChild(dots);
16408
16479
  }
16409
- if (this.useButtons) {
16410
- const buttons = this.renderButtons();
16411
- if (buttons)
16412
- this.shadowRoot.appendChild(buttons);
16413
- }
16414
- }
16415
- setupCarousel() {
16416
- this.setupDots();
16417
- this.setupButtons();
16418
- if (this.autoplay) {
16419
- this.startAutoplay();
16480
+ if (this.state.useButtons) {
16481
+ const buttons = this.createButtons();
16482
+ carouselContainer.appendChild(buttons);
16420
16483
  }
16421
- this.updateCarousel();
16484
+ this.shadowRoot.appendChild(carouselContainer);
16485
+ this.cacheElements();
16422
16486
  }
16423
- renderSlides() {
16487
+ /**
16488
+ * Creates the slides container with infinite scroll support
16489
+ * Adds cloned slides at start and end for seamless scrolling
16490
+ */
16491
+ createSlides() {
16424
16492
  const slidesContainer = document.createElement('div');
16425
16493
  slidesContainer.className = 'slides';
16426
- this.slides.forEach((slide) => {
16427
- const slideElement = document.createElement('div');
16428
- slideElement.className = 'slide';
16429
- if (slide instanceof HTMLElement) {
16430
- slideElement.appendChild(slide);
16431
- }
16432
- slidesContainer.appendChild(slideElement);
16494
+ // Add the last slide at the beginning for seamless backward transition
16495
+ this.appendSlide(slidesContainer, this.slides.length - 1);
16496
+ // Add all slides
16497
+ this.slides.forEach((_, index) => {
16498
+ this.appendSlide(slidesContainer, index);
16433
16499
  });
16500
+ // Add the first slide at the end for seamless forward transition
16501
+ this.appendSlide(slidesContainer, 0);
16502
+ // Set initial position to show first real slide
16503
+ slidesContainer.style.transform = 'translateX(-100%)';
16434
16504
  return slidesContainer;
16435
16505
  }
16436
- renderDots() {
16506
+ /**
16507
+ * Appends a single slide to the container
16508
+ * Handles cloning of slide elements
16509
+ */
16510
+ appendSlide(container, index) {
16511
+ const slideElement = document.createElement('div');
16512
+ slideElement.className = 'slide';
16513
+ if (this.slides[index] instanceof HTMLElement) {
16514
+ // Clone the slide
16515
+ const clonedSlide = this.slides[index].cloneNode(true);
16516
+ // Map clone to original for event handling
16517
+ this.cloneToOriginalMap.set(clonedSlide, this.slides[index]);
16518
+ // Add event delegation to the slide container
16519
+ slideElement.addEventListener('click', (e) => {
16520
+ const original = this.slides[index];
16521
+ // Dispatch a new event on the original element
16522
+ const cloneEvent = new Event(e.type, { bubbles: true, cancelable: true });
16523
+ original.dispatchEvent(cloneEvent);
16524
+ });
16525
+ slideElement.appendChild(clonedSlide);
16526
+ }
16527
+ container.appendChild(slideElement);
16528
+ }
16529
+ /**
16530
+ * Creates navigation dots based on number of slides
16531
+ * Sets up initial active state and styling
16532
+ */
16533
+ createDots() {
16534
+ const { position, size, opacity, color } = this.state.dots;
16437
16535
  const dotsContainer = document.createElement('div');
16438
- dotsContainer.className = `dots ${this.dotsOptions.position} ${this.dotsOptions.size}`;
16439
- dotsContainer.style.cssText = `--opacity: ${this.dotsOptions.opacity}`;
16536
+ dotsContainer.className = `dots ${position} ${size}`;
16537
+ dotsContainer.style.setProperty('--opacity', opacity.toString());
16440
16538
  this.slides.forEach((_, index) => {
16441
16539
  const dot = document.createElement('span');
16442
- dot.className = `dot ${index === this.currentSlide ? 'active' : ''}`;
16443
- dot.style.backgroundColor = this.dotsOptions.color;
16540
+ dot.className = `dot ${index === this.state.currentSlide ? 'active' : ''}`;
16541
+ dot.style.backgroundColor = color;
16444
16542
  dotsContainer.appendChild(dot);
16445
16543
  });
16446
16544
  return dotsContainer;
16447
16545
  }
16448
- renderButtons() {
16546
+ /**
16547
+ * Creates navigation buttons (prev/next)
16548
+ * Applies styling and position configuration
16549
+ */
16550
+ createButtons() {
16551
+ // eslint-disable-next-line @typescript-eslint/naming-convention
16552
+ const { together, position, size, opacity } = this.state.buttons;
16449
16553
  const buttonsContainer = document.createElement('div');
16450
- const buttonsClass = this.buttonsOptions.together
16451
- ? `buttons-together ${this.buttonsOptions.position}`
16452
- : 'buttons-separate';
16453
- buttonsContainer.className = `buttons ${buttonsClass} ${this.buttonsOptions.size}`;
16454
- buttonsContainer.style.cssText = `--opacity: ${this.buttonsOptions.opacity}`;
16455
- this.prevButton = this.createButton('prev-button', this.buttonsOptions.prev);
16456
- this.nextButton = this.createButton('next-button', this.buttonsOptions.next);
16457
- buttonsContainer.appendChild(this.prevButton);
16458
- buttonsContainer.appendChild(this.nextButton);
16554
+ const buttonsClass = together ? `buttons-together ${position}` : 'buttons-separate';
16555
+ buttonsContainer.className = `buttons ${buttonsClass} ${size}`;
16556
+ buttonsContainer.style.setProperty('--opacity', opacity.toString());
16557
+ this.elements.prevButton = this.createButton('prev-button', this.state.buttons.prev);
16558
+ this.elements.nextButton = this.createButton('next-button', this.state.buttons.next);
16559
+ buttonsContainer.appendChild(this.elements.prevButton);
16560
+ buttonsContainer.appendChild(this.elements.nextButton);
16459
16561
  return buttonsContainer;
16460
16562
  }
16563
+ /**
16564
+ * Creates a single button element with specified properties
16565
+ * Used for both prev and next buttons
16566
+ */
16461
16567
  createButton(className, text) {
16568
+ const { textColor, backgroundColor, borderRadius } = this.state.buttons;
16462
16569
  const button = document.createElement('button');
16463
16570
  button.className = className;
16464
16571
  button.textContent = text;
16465
- button.style.color = this.buttonsOptions.textColor;
16466
- button.style.backgroundColor = this.buttonsOptions.backgroundColor;
16467
- button.style.borderRadius = this.buttonsOptions.borderRadius;
16572
+ button.style.color = textColor;
16573
+ button.style.backgroundColor = backgroundColor;
16574
+ button.style.borderRadius = borderRadius;
16468
16575
  return button;
16469
16576
  }
16470
- setupDots() {
16471
- if (!this.shadowRoot || !this.useDots)
16577
+ /**
16578
+ * Caches references to important DOM elements
16579
+ * Stores references to slides container, dots, and buttons
16580
+ */
16581
+ cacheElements() {
16582
+ if (!this.shadowRoot)
16472
16583
  return;
16473
- this.dotElements = Array.from(this.shadowRoot.querySelectorAll('.dot'));
16474
- this.dotElements.forEach((dot, index) => {
16475
- dot.addEventListener('click', () => {
16476
- this.goToSlide(index);
16477
- this.resetAutoplay();
16478
- });
16479
- });
16584
+ this.elements.slidesContainer = this.shadowRoot.querySelector('.slides');
16585
+ this.elements.dots = Array.from(this.shadowRoot.querySelectorAll('.dot'));
16586
+ this.elements.prevButton = this.shadowRoot.querySelector('.prev-button');
16587
+ this.elements.nextButton = this.shadowRoot.querySelector('.next-button');
16480
16588
  }
16481
- setupButtons() {
16482
- var _a, _b;
16483
- if (!this.useButtons)
16589
+ /** Event Management */
16590
+ /**
16591
+ * Sets up all carousel event listeners
16592
+ * Includes dots, buttons, and transition events
16593
+ */
16594
+ setupCarousel() {
16595
+ this.setupEventListeners();
16596
+ // Add transition event listener with bound method
16597
+ if (this.elements.slidesContainer) {
16598
+ this.elements.slidesContainer.addEventListener('transitionend', this.handleTransitionEnd);
16599
+ }
16600
+ this.updateCarousel(false);
16601
+ }
16602
+ /**
16603
+ * Sets up touch and drag event listeners
16604
+ * Handles both mobile touch and desktop mouse events
16605
+ */
16606
+ setupTouchEvents() {
16607
+ if (!this.elements.slidesContainer)
16484
16608
  return;
16485
- (_a = this.prevButton) === null || _a === void 0 ? void 0 : _a.addEventListener('click', () => {
16486
- this.prevSlide();
16487
- this.resetAutoplay();
16609
+ // Touch events
16610
+ this.elements.slidesContainer.addEventListener('touchstart', this.handleTouchStart, {
16611
+ passive: true,
16612
+ });
16613
+ this.elements.slidesContainer.addEventListener('touchmove', this.handleTouchMove, {
16614
+ passive: false,
16488
16615
  });
16489
- (_b = this.nextButton) === null || _b === void 0 ? void 0 : _b.addEventListener('click', () => {
16490
- this.nextSlide();
16491
- this.resetAutoplay();
16616
+ this.elements.slidesContainer.addEventListener('touchend', this.handleTouchEnd);
16617
+ // Mouse drag events
16618
+ this.elements.slidesContainer.addEventListener('mousedown', this.handleDragStart);
16619
+ document.addEventListener('mousemove', this.handleDrag);
16620
+ document.addEventListener('mouseup', this.handleDragEnd);
16621
+ }
16622
+ /**
16623
+ * Removes all event listeners
16624
+ * Called during cleanup and before re-adding listeners
16625
+ */
16626
+ removeEventListeners() {
16627
+ this.elements.dots.forEach((dot) => {
16628
+ dot.replaceWith(dot.cloneNode(true));
16492
16629
  });
16630
+ if (this.elements.prevButton) {
16631
+ this.elements.prevButton.replaceWith(this.elements.prevButton.cloneNode(true));
16632
+ }
16633
+ if (this.elements.nextButton) {
16634
+ this.elements.nextButton.replaceWith(this.elements.nextButton.cloneNode(true));
16635
+ }
16636
+ }
16637
+ /**
16638
+ * Removes touch and drag event listeners
16639
+ * Separate from other event listeners for better management
16640
+ */
16641
+ removeTouchEvents() {
16642
+ if (!this.elements.slidesContainer)
16643
+ return;
16644
+ this.elements.slidesContainer.removeEventListener('touchstart', this.handleTouchStart);
16645
+ this.elements.slidesContainer.removeEventListener('touchmove', this.handleTouchMove);
16646
+ this.elements.slidesContainer.removeEventListener('touchend', this.handleTouchEnd);
16647
+ this.elements.slidesContainer.removeEventListener('mousedown', this.handleDragStart);
16648
+ document.removeEventListener('mousemove', this.handleDrag);
16649
+ document.removeEventListener('mouseup', this.handleDragEnd);
16650
+ }
16651
+ /** Touch and Drag Event Handlers */
16652
+ /**
16653
+ * Handles start of touch interaction
16654
+ * Initializes drag tracking and pauses autoplay
16655
+ */
16656
+ handleTouchStart(e) {
16657
+ if (this.state.isTransitioning)
16658
+ return;
16659
+ this.pauseAutoplay();
16660
+ this.state.startX = e.touches[0].clientX;
16661
+ this.state.dragStartTime = Date.now();
16662
+ this.state.isDragging = true;
16663
+ this.state.dragDistance = 0;
16664
+ }
16665
+ /**
16666
+ * Handles touch movement
16667
+ * Calculates drag distance and updates carousel position
16668
+ */
16669
+ handleTouchMove(e) {
16670
+ if (!this.state.isDragging || this.state.isTransitioning)
16671
+ return;
16672
+ e.preventDefault();
16673
+ const currentX = e.touches[0].clientX;
16674
+ this.state.dragDistance = this.state.startX - currentX;
16675
+ this.updateDragPosition();
16676
+ }
16677
+ /**
16678
+ * Handles end of touch interaction
16679
+ * Determines if slide should change based on drag distance
16680
+ */
16681
+ handleTouchEnd() {
16682
+ if (!this.state.isDragging)
16683
+ return;
16684
+ const dragDuration = Date.now() - this.state.dragStartTime;
16685
+ const velocity = Math.abs(this.state.dragDistance) / dragDuration;
16686
+ const isQuickSwipe = velocity > 0.5;
16687
+ this.handleDragComplete(isQuickSwipe);
16688
+ setTimeout(() => this.resumeAutoplay(), 100);
16689
+ }
16690
+ /**
16691
+ * Handles start of mouse drag
16692
+ * Similar to touch start but for desktop interaction
16693
+ */
16694
+ handleDragStart(e) {
16695
+ if (this.state.isTransitioning)
16696
+ return;
16697
+ this.pauseAutoplay();
16698
+ this.state.startX = e.clientX;
16699
+ this.state.dragStartTime = Date.now();
16700
+ this.state.isDragging = true;
16701
+ this.state.dragDistance = 0;
16702
+ e.preventDefault();
16493
16703
  }
16494
- nextSlide() {
16495
- this.goToSlide((this.currentSlide + 1) % this.slides.length);
16704
+ /**
16705
+ * Handles mouse movement during drag
16706
+ * Updates carousel position during drag
16707
+ */
16708
+ handleDrag(e) {
16709
+ if (!this.state.isDragging || this.state.isTransitioning)
16710
+ return;
16711
+ const currentX = e.clientX;
16712
+ this.state.dragDistance = this.state.startX - currentX;
16713
+ this.updateDragPosition();
16714
+ }
16715
+ /**
16716
+ * Handles end of mouse drag
16717
+ * Determines final slide position based on drag
16718
+ */
16719
+ handleDragEnd() {
16720
+ if (!this.state.isDragging)
16721
+ return;
16722
+ const dragDuration = Date.now() - this.state.dragStartTime;
16723
+ const velocity = Math.abs(this.state.dragDistance) / dragDuration;
16724
+ const isQuickSwipe = velocity > 0.5;
16725
+ this.handleDragComplete(isQuickSwipe);
16726
+ setTimeout(() => this.resumeAutoplay(), 100);
16496
16727
  }
16497
- prevSlide() {
16498
- this.goToSlide((this.currentSlide - 1 + this.slides.length) % this.slides.length);
16728
+ /** Navigation and Position Management */
16729
+ /**
16730
+ * Handles navigation button clicks and programmatic navigation
16731
+ * Updates both real and virtual indices
16732
+ */
16733
+ handleNavigation(direction) {
16734
+ if (this.state.isTransitioning)
16735
+ return;
16736
+ const totalSlides = this.slides.length;
16737
+ if (direction === 'next') {
16738
+ this.state.realIndex = (this.state.realIndex + 1) % totalSlides;
16739
+ this.state.virtualIndex++;
16740
+ }
16741
+ else {
16742
+ this.state.realIndex = (this.state.realIndex - 1 + totalSlides) % totalSlides;
16743
+ this.state.virtualIndex--;
16744
+ }
16745
+ this.updateCarousel(true);
16746
+ }
16747
+ /**
16748
+ * Handles dot navigation clicks
16749
+ * Calculates shortest path to target slide
16750
+ */
16751
+ handleDotClick(index) {
16752
+ const currentRealIndex = this.state.realIndex;
16753
+ const totalSlides = this.slides.length;
16754
+ // Calculate the shortest path to the target slide
16755
+ let diff = index - currentRealIndex;
16756
+ // Adjust for shortest path when crossing the boundary
16757
+ if (Math.abs(diff) > totalSlides / 2) {
16758
+ diff = diff > 0 ? diff - totalSlides : diff + totalSlides;
16759
+ }
16760
+ // Update both real and virtual indices
16761
+ this.state.realIndex = index;
16762
+ this.state.virtualIndex += diff;
16763
+ this.updateCarousel(true);
16764
+ this.resetAutoplay();
16765
+ }
16766
+ /**
16767
+ * Completes a drag interaction
16768
+ * Determines whether to change slides based on drag distance
16769
+ */
16770
+ handleDragComplete(isQuickSwipe) {
16771
+ this.state.isDragging = false;
16772
+ if (!this.elements.slidesContainer)
16773
+ return;
16774
+ const dragPercentage = this.state.dragDistance / this.state.containerWidth;
16775
+ const threshold = isQuickSwipe ? 0.1 : CustomCarouselElement.defaultConfigs.dragThreshold;
16776
+ if (Math.abs(dragPercentage) > threshold) {
16777
+ if (this.state.dragDistance > 0) {
16778
+ this.handleNavigation('next');
16779
+ }
16780
+ else {
16781
+ this.handleNavigation('prev');
16782
+ }
16783
+ }
16784
+ else {
16785
+ this.updateCarousel(true);
16786
+ }
16787
+ this.state.dragDistance = 0;
16788
+ }
16789
+ /**
16790
+ * Updates carousel position during drag
16791
+ * Applies transform without transition
16792
+ */
16793
+ updateDragPosition() {
16794
+ if (!this.elements.slidesContainer || this.state.isTransitioning)
16795
+ return;
16796
+ const translateX = -this.state.virtualIndex * 100 -
16797
+ (this.state.dragDistance / this.state.containerWidth) * 100;
16798
+ this.elements.slidesContainer.style.transform = `translateX(${translateX}%)`;
16799
+ this.elements.slidesContainer.style.transition = 'none';
16499
16800
  }
16801
+ /**
16802
+ * Navigates to a specific slide
16803
+ * Used primarily by dot navigation
16804
+ */
16500
16805
  goToSlide(index) {
16501
- this.currentSlide = index;
16502
- this.updateCarousel();
16806
+ if (this.state.isTransitioning)
16807
+ return;
16808
+ this.state.currentSlide = index;
16809
+ this.updateCarousel(true);
16503
16810
  }
16504
- updateCarousel() {
16505
- if (!this.slidesContainer)
16811
+ /** Transition and Animation Management */
16812
+ /**
16813
+ * Updates carousel position and state
16814
+ * Handles both animated and immediate updates
16815
+ */
16816
+ updateCarousel(animated = true) {
16817
+ if (!this.elements.slidesContainer)
16506
16818
  return;
16507
- // Calculate the translation distance based on current slide
16508
- const translateX = -this.currentSlide * 100;
16509
- this.slidesContainer.style.transform = `translateX(${translateX}%)`;
16819
+ const totalSlides = this.slides.length;
16820
+ // Calculate the translation for the current virtual position
16821
+ const translateX = -this.state.virtualIndex * 100;
16822
+ if (animated) {
16823
+ this.state.isTransitioning = true;
16824
+ this.elements.slidesContainer.style.transition = 'transform 0.3s ease-out';
16825
+ }
16826
+ else {
16827
+ this.elements.slidesContainer.style.transition = 'none';
16828
+ }
16829
+ this.elements.slidesContainer.style.transform = `translateX(${translateX}%)`;
16510
16830
  this.updateDots();
16831
+ // Check if we need to reset virtual position
16832
+ if (this.state.virtualIndex <= 0 || this.state.virtualIndex >= totalSlides + 1) {
16833
+ this.state.isVirtualizing = true;
16834
+ }
16835
+ }
16836
+ /**
16837
+ * Handles the end of slide transitions
16838
+ * Manages infinite scroll position reset
16839
+ */
16840
+ handleTransitionEnd(event) {
16841
+ if (event.target !== this.elements.slidesContainer || event.propertyName !== 'transform')
16842
+ return;
16843
+ this.state.isTransitioning = false;
16844
+ if (this.state.isVirtualizing) {
16845
+ this.resetVirtualPosition();
16846
+ }
16847
+ if (this.elements.slidesContainer) {
16848
+ this.elements.slidesContainer.style.transition = 'none';
16849
+ }
16850
+ }
16851
+ /**
16852
+ * Resets virtual position for infinite scroll
16853
+ * Called after hitting first or last clone
16854
+ */
16855
+ resetVirtualPosition() {
16856
+ var _a;
16857
+ const totalSlides = this.slides.length;
16858
+ if (this.state.virtualIndex <= 0) {
16859
+ // If we've moved before the first clone, jump to the last real slide
16860
+ this.state.virtualIndex = totalSlides;
16861
+ }
16862
+ else if (this.state.virtualIndex >= totalSlides + 1) {
16863
+ // If we've moved after the last clone, jump to the first real slide
16864
+ this.state.virtualIndex = 1;
16865
+ }
16866
+ if (this.elements.slidesContainer) {
16867
+ this.elements.slidesContainer.style.transition = 'none';
16868
+ this.elements.slidesContainer.style.transform = `translateX(${-this.state.virtualIndex * 100}%)`;
16869
+ }
16870
+ this.state.isVirtualizing = false;
16871
+ // Force reflow to ensure the style change takes effect immediately
16872
+ void ((_a = this.elements.slidesContainer) === null || _a === void 0 ? void 0 : _a.offsetHeight);
16511
16873
  }
16874
+ /**
16875
+ * Updates dot active states
16876
+ * Reflects current slide position in navigation
16877
+ */
16512
16878
  updateDots() {
16513
- if (!this.useDots)
16879
+ if (!this.state.useDots)
16514
16880
  return;
16515
- this.dotElements.forEach((dot, index) => {
16516
- const isActive = index === this.currentSlide;
16881
+ this.elements.dots.forEach((dot, index) => {
16882
+ const isActive = index === this.state.realIndex;
16517
16883
  dot.classList.toggle('active', isActive);
16518
- dot.style.backgroundColor = isActive
16519
- ? this.dotsOptions.activeColor
16520
- : this.dotsOptions.color;
16884
+ dot.style.backgroundColor = isActive ? this.state.dots.activeColor : this.state.dots.color;
16521
16885
  });
16522
16886
  }
16887
+ /** Autoplay Management */
16888
+ /**
16889
+ * Starts autoplay if enabled
16890
+ * Called during initialization
16891
+ */
16892
+ startAutoplayIfEnabled() {
16893
+ if (this.state.autoplay && !this.state.isAutoplayPaused) {
16894
+ this.startAutoplay();
16895
+ }
16896
+ }
16897
+ /**
16898
+ * Starts the autoplay interval
16899
+ * Ensures only one interval is running
16900
+ */
16523
16901
  startAutoplay() {
16524
- this.autoplayInterval = window.setInterval(() => this.nextSlide(), this.interval);
16902
+ this.stopAutoplay(); // Ensure any existing interval is cleared first
16903
+ if (!this.state.isAutoplayPaused && this.state.autoplay) {
16904
+ this.state.autoplayInterval = window.setInterval(() => this.handleNavigation('next'), this.state.interval);
16905
+ }
16525
16906
  }
16907
+ /**
16908
+ * Pauses autoplay
16909
+ * Used during user interaction
16910
+ */
16911
+ pauseAutoplay() {
16912
+ this.state.isAutoplayPaused = true;
16913
+ this.stopAutoplay();
16914
+ }
16915
+ /**
16916
+ * Resumes autoplay after pause
16917
+ * Used after user interaction ends
16918
+ */
16919
+ resumeAutoplay() {
16920
+ this.state.isAutoplayPaused = false;
16921
+ if (this.state.autoplay) {
16922
+ this.startAutoplay();
16923
+ }
16924
+ }
16925
+ /**
16926
+ * Stops autoplay completely
16927
+ * Cleans up interval
16928
+ */
16526
16929
  stopAutoplay() {
16527
- if (this.autoplayInterval !== null) {
16528
- window.clearInterval(this.autoplayInterval);
16529
- this.autoplayInterval = null;
16930
+ if (this.state.autoplayInterval !== null) {
16931
+ window.clearInterval(this.state.autoplayInterval);
16932
+ this.state.autoplayInterval = null;
16530
16933
  }
16531
16934
  }
16935
+ /**
16936
+ * Resets autoplay interval
16937
+ * Used after user interactions
16938
+ */
16532
16939
  resetAutoplay() {
16533
- if (this.autoplay) {
16940
+ if (!this.state.isDragging && this.state.autoplay) {
16534
16941
  this.stopAutoplay();
16535
16942
  this.startAutoplay();
16536
16943
  }
16537
16944
  }
16538
- validateOptions() {
16539
- this.validatePosition(this.dotsOptions.position, 'dotsPosition', 'bottom-center');
16540
- this.validateButtonsPosition();
16945
+ /** Utility Methods */
16946
+ /**
16947
+ * Updates container width on resize
16948
+ * Used for drag distance calculations
16949
+ */
16950
+ updateContainerWidth() {
16951
+ if (this.elements.slidesContainer) {
16952
+ this.state.containerWidth = this.elements.slidesContainer.offsetWidth;
16953
+ }
16541
16954
  }
16542
- validatePosition(position, optionName, defaultValue) {
16543
- if (!CustomCarouselElement.validPositions.includes(position)) {
16544
- console.warn(`Invalid ${optionName}: ${position}. Defaulting to '${defaultValue}'.`);
16545
- if (optionName === 'dotsPosition') {
16546
- this.dotsOptions.position = defaultValue;
16547
- }
16548
- else if (optionName === 'buttonsPosition') {
16549
- this.buttonsOptions.position = defaultValue;
16550
- }
16955
+ /**
16956
+ * Cleans up all event listeners
16957
+ * Called during component cleanup
16958
+ */
16959
+ cleanupEventListeners() {
16960
+ this.stopAutoplay();
16961
+ this.removeEventListeners();
16962
+ this.removeTouchEvents();
16963
+ // Remove transition listener
16964
+ if (this.elements.slidesContainer) {
16965
+ this.elements.slidesContainer.removeEventListener('transitionend', this.handleTransitionEnd);
16966
+ }
16967
+ window.removeEventListener('resize', this.updateContainerWidth);
16968
+ }
16969
+ /**
16970
+ * Sets up all event listeners
16971
+ * Includes dots, buttons, and transition events
16972
+ */
16973
+ setupEventListeners() {
16974
+ var _a, _b;
16975
+ if (this.state.useDots) {
16976
+ this.elements.dots.forEach((dot, index) => {
16977
+ dot.addEventListener('click', () => this.handleDotClick(index));
16978
+ });
16979
+ }
16980
+ if (this.state.useButtons) {
16981
+ (_a = this.elements.prevButton) === null || _a === void 0 ? void 0 : _a.addEventListener('click', () => this.handleNavigation('prev'));
16982
+ (_b = this.elements.nextButton) === null || _b === void 0 ? void 0 : _b.addEventListener('click', () => this.handleNavigation('next'));
16551
16983
  }
16552
16984
  }
16985
+ /**
16986
+ * Validates button position configuration
16987
+ * Ensures only valid positions are used
16988
+ */
16553
16989
  validateButtonsPosition() {
16554
- if (this.useButtons) {
16555
- if (this.buttonsOptions.together) {
16556
- this.validatePosition(this.buttonsOptions.position, 'buttonsPosition', 'bottom-center');
16557
- }
16558
- else if (this.buttonsOptions.position !== 'middle-sides') {
16559
- console.warn(`Invalid buttonsPosition: ${this.buttonsOptions.position}. When buttons are not together, only 'middle-sides' is allowed. Defaulting to 'middle-sides'.`);
16560
- this.buttonsOptions.position = 'middle-sides';
16561
- }
16990
+ if (this.state.useButtons &&
16991
+ !this.state.buttons.together &&
16992
+ this.state.buttons.position !== 'middle-sides') {
16993
+ console.warn('When buttons are not together, only "middle-sides" is allowed. Defaulting to "middle-sides".');
16994
+ this.state.buttons.position = 'middle-sides';
16562
16995
  }
16563
16996
  }
16564
16997
  }
16565
- CustomCarouselElement.defaultInterval = 5000;
16566
- CustomCarouselElement.validPositions = [
16998
+ /** Core initialization and lifecycle methods */
16999
+ CustomCarouselElement.defaultConfigs = {
17000
+ interval: 5000,
17001
+ touchThreshold: 50, // minimum swipe distance in pixels
17002
+ dragThreshold: 0.2, // minimum drag percentage of carousel width
17003
+ dots: {
17004
+ position: 'bottom-center',
17005
+ color: '#d9d9d9',
17006
+ activeColor: '#b5914a',
17007
+ size: 'base',
17008
+ opacity: 1,
17009
+ },
17010
+ buttons: {
17011
+ together: false,
17012
+ position: 'middle-sides',
17013
+ textColor: '#000000',
17014
+ backgroundColor: '#ffffff',
17015
+ borderRadius: '50%',
17016
+ prev: 'Prev',
17017
+ next: 'Next',
17018
+ size: 'base',
17019
+ opacity: 1,
17020
+ },
17021
+ };
17022
+ CustomCarouselElement.validPositions = new Set([
16567
17023
  'top-left',
16568
17024
  'top-center',
16569
17025
  'top-right',
@@ -16573,7 +17029,7 @@ if (typeof window !== 'undefined' && typeof window.customElements !== 'undefined
16573
17029
  'middle-left',
16574
17030
  'middle-right',
16575
17031
  'middle-sides',
16576
- ];
17032
+ ]);
16577
17033
  CarouselElement = CustomCarouselElement;
16578
17034
  }
16579
17035
 
@@ -16728,19 +17184,20 @@ if (typeof window !== 'undefined' && typeof window.customElements !== 'undefined
16728
17184
  * #########################################################
16729
17185
  */
16730
17186
  handleSpotSizeChanged(event) {
16731
- var _a, _b, _c;
16732
- const isRBSpot = (_c = (_b = (_a = this.data) === null || _a === void 0 ? void 0 : _a.spot) === null || _b === void 0 ? void 0 : _b.startsWith('rb')) !== null && _c !== void 0 ? _c : false;
16733
- if (!isRBSpot) {
16734
- // Adjust text elements font size based on the scale factor
16735
- this.adjustFontSize(event.detail.scale);
16736
- }
17187
+ // Adjust text elements font size based on the scale factor
17188
+ this.adjustFontSize(event.detail.scale);
16737
17189
  }
16738
17190
  adjustFontSize(elementScale) {
16739
17191
  var _a;
16740
- const scaleFactor = calculateScaleFactor(elementScale);
16741
17192
  // Find all text elements within the shadow root
16742
- const elements = (_a = this.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelectorAll('h1, h2, h3, h4, p, span');
16743
- elements === null || elements === void 0 ? void 0 : elements.forEach((element) => {
17193
+ const selectors = ['h1', 'h2', 'h3', 'h4', 'p', 'span']
17194
+ .map((tag) => `[data-spot="iab"] ${tag}`)
17195
+ .join(', ');
17196
+ const elements = (_a = this.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelectorAll(selectors);
17197
+ if (!elements)
17198
+ return;
17199
+ const scaleFactor = calculateScaleFactor(elementScale);
17200
+ elements.forEach((element) => {
16744
17201
  if (element instanceof HTMLElement) {
16745
17202
  if (!this.originalFontSizes.has(element)) {
16746
17203
  const originalSize = parseFloat(window.getComputedStyle(element).fontSize);
@@ -16925,8 +17382,9 @@ function generateGradientColor(overlay, fallback = '') {
16925
17382
  const gradientColor = convertHexToRgba(color, overlayOpacity);
16926
17383
  return `${fullColor} 0%, ${gradientColor} ${goTo}%, ${transparentColor} 100%`;
16927
17384
  }
16928
- function spotHtmlStringToElement(htmlString) {
17385
+ function spotHtmlStringToElement(htmlString, spotType) {
16929
17386
  const spot = document.createElement('div');
17387
+ spot.setAttribute('data-spot', spotType.startsWith('rb') ? 'rb' : 'iab');
16930
17388
  spot.className = 'spot';
16931
17389
  spot.innerHTML = htmlString;
16932
17390
  Object.assign(spot.style, {
@@ -17858,6 +18316,10 @@ const STYLES$6 = ({ textColor = '#ffffff', ctaTextColor = textColor, ctaBorderCo
17858
18316
  .${prefix} {
17859
18317
  background-image: linear-gradient(to top, ${linearGradient}), url("${primaryImage}");
17860
18318
  }
18319
+
18320
+ .${prefix}__text {
18321
+ width: 70%;
18322
+ }
17861
18323
  }
17862
18324
 
17863
18325
  @container (min-width: 768px) {
@@ -18680,7 +19142,7 @@ const SPOT_TEMPLATE_HTML_ELEMENT = (spot, config) => {
18680
19142
  // Generate a highly unique prefix to avoid conflicts with other elements.
18681
19143
  const prefix = 's' + UniqueIdGenerator.generate().toLowerCase();
18682
19144
  const spotHtmlString = variantTemplate(spot, { ...config, prefix });
18683
- return spotHtmlStringToElement(spotHtmlString);
19145
+ return spotHtmlStringToElement(spotHtmlString, spot.spot);
18684
19146
  };
18685
19147
 
18686
19148
  // For the moment, we will only focus on sites that use Google Analytics,
@@ -18875,18 +19337,21 @@ class EventService {
18875
19337
  }
18876
19338
  registerSpot(params) {
18877
19339
  const { placementId, spot, spotElement } = params;
18878
- this.activeSpots.set(placementId, { spotElement });
18879
- // Fire impression event
18880
- this.fireImpressionEvent(placementId, spot);
18881
- // Handle intersection observer
18882
- this.handleIntersectionObserver(placementId, spot, spotElement);
18883
- // Attach click event listener
18884
- spotElement.addEventListener('click', async () => await this.handleClick(params));
19340
+ this.activeSpots.set(spot.id, { placementId, spotElement });
19341
+ // Handle Impression Event
19342
+ this.handleImpressionEvent(placementId, spot);
19343
+ // Handle Click Event
19344
+ spotElement.addEventListener('click', async () => {
19345
+ await this.handleClickEvent(params);
19346
+ });
19347
+ // Handle Intersection Observer
19348
+ this.handleIntersectionObserver(placementId, spotElement);
18885
19349
  }
18886
19350
  unregisterSpot(placementId) {
18887
19351
  const placementIdClean = placementId.replace('#', '');
18888
- const spotData = this.activeSpots.get(placementIdClean);
18889
- if (!spotData) {
19352
+ const spotId = this.getSpotIdByPlacementId(this.activeSpots, placementIdClean);
19353
+ const data = this.activeSpots.get(placementIdClean !== null && placementIdClean !== void 0 ? placementIdClean : '');
19354
+ if (!spotId || !data) {
18890
19355
  this.handleSpotState(placementIdClean, {
18891
19356
  state: {
18892
19357
  error: `Active spot with placementId ${placementIdClean} not found.`,
@@ -18894,7 +19359,7 @@ class EventService {
18894
19359
  });
18895
19360
  return;
18896
19361
  }
18897
- this.intersectionObserver.unobserve(spotData.spotElement);
19362
+ this.intersectionObserver.unobserve(data.spotElement);
18898
19363
  this.handleSpotState(placementIdClean, {
18899
19364
  dom: {
18900
19365
  spotElement: undefined,
@@ -18903,9 +19368,10 @@ class EventService {
18903
19368
  state: {
18904
19369
  unmounted: true,
18905
19370
  mounted: false,
19371
+ loading: false,
18906
19372
  },
18907
19373
  });
18908
- this.activeSpots.delete(placementIdClean);
19374
+ this.activeSpots.delete(spotId);
18909
19375
  const placementElement = document.getElementById(placementIdClean);
18910
19376
  if (!placementElement) {
18911
19377
  this.handleSpotState(placementIdClean, {
@@ -18958,19 +19424,7 @@ class EventService {
18958
19424
  this.pubSubService.publish(exports.RMN_EVENT.LIFECYCLE_STATE, this.spotStates.get(placementId));
18959
19425
  }
18960
19426
  }
18961
- deepMerge(current, updates) {
18962
- return {
18963
- identifier: updates.identifier
18964
- ? { ...current.identifier, ...updates.identifier }
18965
- : current.identifier,
18966
- dom: updates.dom ? { ...current.dom, ...updates.dom } : current.dom,
18967
- state: updates.state ? { ...current.state, ...updates.state } : current.state,
18968
- displayConfig: updates.displayConfig
18969
- ? { ...current.displayConfig, ...updates.displayConfig }
18970
- : current.displayConfig,
18971
- };
18972
- }
18973
- async handleClick({ placementId, spot }) {
19427
+ async handleClickEvent({ placementId, spot }) {
18974
19428
  var _a, _b, _c;
18975
19429
  this.pubSubService.publish(exports.RMN_EVENT.SPOT_EVENT, {
18976
19430
  eventType: exports.RMN_SPOT_EVENT.CLICK,
@@ -18987,22 +19441,10 @@ class EventService {
18987
19441
  spotId: spot.id,
18988
19442
  spotType: spot.spot,
18989
19443
  events: spot.events,
18990
- productIds: (_c = spot.productIds) !== null && _c !== void 0 ? _c : [1, 2, 3],
19444
+ productIds: (_c = spot.productIds) !== null && _c !== void 0 ? _c : [],
18991
19445
  });
18992
19446
  }
18993
- handleIntersectionObserver(placementId, _spot, spotElement) {
18994
- const spotIsVisibleCallback = async () => {
18995
- this.intersectionObserver.unobserve(spotElement);
18996
- this.handleSpotState(placementId, {
18997
- dom: {
18998
- spotElement,
18999
- visibleOnViewport: true,
19000
- },
19001
- });
19002
- };
19003
- this.intersectionObserver.observe(spotElement, spotIsVisibleCallback);
19004
- }
19005
- fireImpressionEvent(placementId, spot) {
19447
+ handleImpressionEvent(placementId, spot) {
19006
19448
  this.pubSubService.publish(exports.RMN_EVENT.SPOT_EVENT, {
19007
19449
  eventType: exports.RMN_SPOT_EVENT.IMPRESSION,
19008
19450
  placementId,
@@ -19016,6 +19458,38 @@ class EventService {
19016
19458
  });
19017
19459
  })();
19018
19460
  }
19461
+ getSpotIdByPlacementId(activeSpots, searchPlacementId) {
19462
+ for (const [spotId, spotData] of activeSpots) {
19463
+ if (spotData.placementId === searchPlacementId) {
19464
+ return spotId;
19465
+ }
19466
+ }
19467
+ return null;
19468
+ }
19469
+ deepMerge(current, updates) {
19470
+ return {
19471
+ identifier: updates.identifier
19472
+ ? { ...current.identifier, ...updates.identifier }
19473
+ : current.identifier,
19474
+ dom: updates.dom ? { ...current.dom, ...updates.dom } : current.dom,
19475
+ state: updates.state ? { ...current.state, ...updates.state } : current.state,
19476
+ displayConfig: updates.displayConfig
19477
+ ? { ...current.displayConfig, ...updates.displayConfig }
19478
+ : current.displayConfig,
19479
+ };
19480
+ }
19481
+ handleIntersectionObserver(placementId, spotElement) {
19482
+ const spotIsVisibleCallback = async () => {
19483
+ this.intersectionObserver.unobserve(spotElement);
19484
+ this.handleSpotState(placementId, {
19485
+ dom: {
19486
+ spotElement,
19487
+ visibleOnViewport: true,
19488
+ },
19489
+ });
19490
+ };
19491
+ this.intersectionObserver.observe(spotElement, spotIsVisibleCallback);
19492
+ }
19019
19493
  }
19020
19494
 
19021
19495
  const SELECTION_API_PATH = '/spots/selection';
@@ -19792,6 +20266,8 @@ class LiquidCommerceRmnClient {
19792
20266
  this.eventService.handleSpotState(item.placementId, {
19793
20267
  state: {
19794
20268
  error: `Placement not found for id "${item.placementId}".`,
20269
+ mounted: false,
20270
+ loading: false,
19795
20271
  },
19796
20272
  });
19797
20273
  continue;