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

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