@liquidcommercedev/rmn-sdk 1.4.6 → 1.5.0-beta.10

Sign up to get free protection for your applications and to get access to all the features.
Files changed (91) hide show
  1. package/dist/index.cjs +3234 -746
  2. package/dist/index.esm.js +3234 -747
  3. package/dist/types/common/helpers/utils.helper.d.ts +50 -0
  4. package/dist/types/common/helpers/uuid.helper.d.ts +49 -0
  5. package/dist/types/enums.d.ts +64 -1
  6. package/dist/types/example.constant.d.ts +4 -0
  7. package/dist/types/index.umd.d.ts +2 -2
  8. package/dist/types/modules/element/component/carousel/carousel.component.d.ts +3 -0
  9. package/dist/types/modules/element/component/carousel/carousel.interface.d.ts +35 -0
  10. package/dist/types/modules/element/component/carousel/carousel.style.d.ts +2 -0
  11. package/dist/types/modules/element/component/carousel/index.d.ts +3 -0
  12. package/dist/types/modules/element/component/spot/index.d.ts +2 -0
  13. package/dist/types/modules/element/component/spot/spot.component.d.ts +3 -0
  14. package/dist/types/modules/element/component/spot/spot.interface.d.ts +10 -0
  15. package/dist/types/modules/{spot/html/constants/html.constant.d.ts → element/element.constant.d.ts} +2 -5
  16. package/dist/types/modules/element/element.interface.d.ts +55 -0
  17. package/dist/types/modules/element/element.service.d.ts +40 -0
  18. package/dist/types/modules/element/index.d.ts +3 -0
  19. package/dist/types/modules/element/template/helper.d.ts +4 -0
  20. package/dist/types/modules/element/template/index.d.ts +1 -0
  21. package/dist/types/modules/element/template/reservebar/collection-banner-without-text-block.template.d.ts +3 -0
  22. package/dist/types/modules/element/template/reservebar/homepage-hero-full-image.template.d.ts +3 -0
  23. package/dist/types/modules/element/template/reservebar/homepage-hero-three-tile.template.d.ts +3 -0
  24. package/dist/types/modules/element/template/reservebar/homepage-hero-two-tile.template.d.ts +3 -0
  25. package/dist/types/modules/element/template/reservebar/large-category-image-tout.template.d.ts +3 -0
  26. package/dist/types/modules/element/template/reservebar/navigation-banner.template.d.ts +3 -0
  27. package/dist/types/modules/element/template/reservebar/small-category-image-tout.template.d.ts +3 -0
  28. package/dist/types/modules/element/template/reservebar/small-discover-tout.template.d.ts +3 -0
  29. package/dist/types/modules/element/template/template.service.d.ts +11 -0
  30. package/dist/types/modules/element/template/template.type.d.ts +11 -0
  31. package/dist/types/modules/event/event.constant.d.ts +1 -0
  32. package/dist/types/modules/event/event.interface.d.ts +46 -0
  33. package/dist/types/modules/event/event.service.d.ts +30 -0
  34. package/dist/types/modules/event/index.d.ts +3 -0
  35. package/dist/types/modules/helper-service/index.d.ts +4 -0
  36. package/dist/types/modules/helper-service/intersection.service.d.ts +8 -0
  37. package/dist/types/modules/helper-service/localstorage.service.d.ts +56 -0
  38. package/dist/types/modules/helper-service/pubsub.service.d.ts +69 -0
  39. package/dist/types/modules/helper-service/resize.service.d.ts +30 -0
  40. package/dist/types/modules/monitor/index.d.ts +2 -0
  41. package/dist/types/modules/monitor/monitor.enums.d.ts +4 -0
  42. package/dist/types/modules/monitor/monitor.interface.d.ts +11 -0
  43. package/dist/types/modules/monitor/monitor.service.d.ts +12 -0
  44. package/dist/types/modules/monitor/monitors/datalayer.monitor.d.ts +12 -0
  45. package/dist/types/modules/selection/index.d.ts +4 -0
  46. package/dist/types/modules/selection/selection.constant.d.ts +1 -0
  47. package/dist/types/modules/{spot/spot.interface.d.ts → selection/selection.interface.d.ts} +13 -14
  48. package/dist/types/modules/selection/selection.service.d.ts +18 -0
  49. package/dist/types/modules/{spot/spot.type.d.ts → selection/selection.type.d.ts} +4 -3
  50. package/dist/types/rmn-client.d.ts +66 -23
  51. package/dist/types/types.d.ts +18 -6
  52. package/package.json +2 -2
  53. package/umd/liquidcommerce-rmn-sdk.min.js +1 -1
  54. package/dist/types/modules/spot/html/constants/index.d.ts +0 -1
  55. package/dist/types/modules/spot/html/index.d.ts +0 -1
  56. package/dist/types/modules/spot/html/spot.element.service.d.ts +0 -7
  57. package/dist/types/modules/spot/html/templates/index.d.ts +0 -1
  58. package/dist/types/modules/spot/html/templates/reservebar/collection-banner-without-text-block.template.d.ts +0 -2
  59. package/dist/types/modules/spot/html/templates/reservebar/homepage-hero-full-image.template.d.ts +0 -2
  60. package/dist/types/modules/spot/html/templates/reservebar/homepage-hero-three-tile.template.d.ts +0 -2
  61. package/dist/types/modules/spot/html/templates/reservebar/homepage-hero-two-tile.template.d.ts +0 -2
  62. package/dist/types/modules/spot/html/templates/reservebar/large-category-image-tout.template.d.ts +0 -2
  63. package/dist/types/modules/spot/html/templates/reservebar/navigation-banner.template.d.ts +0 -2
  64. package/dist/types/modules/spot/html/templates/reservebar/small-category-image-tout.template.d.ts +0 -2
  65. package/dist/types/modules/spot/html/templates/reservebar/small-discover-tout.template.d.ts +0 -2
  66. package/dist/types/modules/spot/html/templates/spot.template.d.ts +0 -21
  67. package/dist/types/modules/spot/index.d.ts +0 -6
  68. package/dist/types/modules/spot/spot.constant.d.ts +0 -4
  69. package/dist/types/modules/spot/spot.enum.d.ts +0 -57
  70. package/dist/types/modules/spot/spot.html.service.d.ts +0 -22
  71. package/dist/types/modules/spot/spot.selection.service.d.ts +0 -15
  72. /package/dist/types/modules/{spot/html/templates → element/template}/iab/billboard/billboard-v1.template.d.ts +0 -0
  73. /package/dist/types/modules/{spot/html/templates → element/template}/iab/billboard/billboard-v2.template.d.ts +0 -0
  74. /package/dist/types/modules/{spot/html/templates → element/template}/iab/billboard/billboard-v3.template.d.ts +0 -0
  75. /package/dist/types/modules/{spot/html/templates → element/template}/iab/billboard/index.d.ts +0 -0
  76. /package/dist/types/modules/{spot/html/templates → element/template}/iab/in-text/in-text-v1.template.d.ts +0 -0
  77. /package/dist/types/modules/{spot/html/templates → element/template}/iab/in-text/index.d.ts +0 -0
  78. /package/dist/types/modules/{spot/html/templates → element/template}/iab/index.d.ts +0 -0
  79. /package/dist/types/modules/{spot/html/templates → element/template}/iab/large-leaderboard/index.d.ts +0 -0
  80. /package/dist/types/modules/{spot/html/templates → element/template}/iab/large-leaderboard/large-leaderboard-v1.template.d.ts +0 -0
  81. /package/dist/types/modules/{spot/html/templates → element/template}/iab/large-leaderboard/large-leaderboard-v2.template.d.ts +0 -0
  82. /package/dist/types/modules/{spot/html/templates → element/template}/iab/large-rectangle/index.d.ts +0 -0
  83. /package/dist/types/modules/{spot/html/templates → element/template}/iab/large-rectangle/large-rectangle-v1.template.d.ts +0 -0
  84. /package/dist/types/modules/{spot/html/templates → element/template}/iab/square/index.d.ts +0 -0
  85. /package/dist/types/modules/{spot/html/templates → element/template}/iab/square/square-v1.template.d.ts +0 -0
  86. /package/dist/types/modules/{spot/html/templates → element/template}/iab/square/square-v2.template.d.ts +0 -0
  87. /package/dist/types/modules/{spot/html/templates → element/template}/iab/vertical-rectangle/index.d.ts +0 -0
  88. /package/dist/types/modules/{spot/html/templates → element/template}/iab/vertical-rectangle/vertical-rectangle-v1.template.d.ts +0 -0
  89. /package/dist/types/modules/{spot/html/templates → element/template}/iab/wide-skyscraper/index.d.ts +0 -0
  90. /package/dist/types/modules/{spot/html/templates → element/template}/iab/wide-skyscraper/wide-skyscraper-v1.template.d.ts +0 -0
  91. /package/dist/types/modules/{spot/html/templates → element/template}/reservebar/index.d.ts +0 -0
package/dist/index.cjs CHANGED
@@ -3,6 +3,7 @@
3
3
  exports.RMN_SPOT_TYPE = void 0;
4
4
  (function (RMN_SPOT_TYPE) {
5
5
  // Reserve Bar Spot Types
6
+ RMN_SPOT_TYPE["RB_HOMEPAGE_HERO"] = "rbHomepageHero";
6
7
  RMN_SPOT_TYPE["RB_HOMEPAGE_HERO_THREE_TILE"] = "rbHomepageHeroThreeTile";
7
8
  RMN_SPOT_TYPE["RB_HOMEPAGE_HERO_TWO_TILE"] = "rbHomepageHeroTwoTile";
8
9
  RMN_SPOT_TYPE["RB_HOMEPAGE_HERO_FULL_IMAGE"] = "rbHomepageHeroFullImage";
@@ -44,6 +45,7 @@ exports.RMN_SPOT_TYPE = void 0;
44
45
  exports.RMN_FILTER_PROPERTIES = void 0;
45
46
  (function (RMN_FILTER_PROPERTIES) {
46
47
  RMN_FILTER_PROPERTIES["KEYWORDS"] = "keywords";
48
+ RMN_FILTER_PROPERTIES["PAGE_LOCATION"] = "pageLocation";
47
49
  RMN_FILTER_PROPERTIES["PARENTCO"] = "parentCo";
48
50
  RMN_FILTER_PROPERTIES["BRAND"] = "brand";
49
51
  RMN_FILTER_PROPERTIES["CATEGORY"] = "category";
@@ -53,16 +55,21 @@ exports.RMN_FILTER_PROPERTIES = void 0;
53
55
  RMN_FILTER_PROPERTIES["PUBLISHERS"] = "publishers";
54
56
  RMN_FILTER_PROPERTIES["SECTION"] = "section";
55
57
  })(exports.RMN_FILTER_PROPERTIES || (exports.RMN_FILTER_PROPERTIES = {}));
58
+ exports.RMN_EVENT = void 0;
59
+ (function (RMN_EVENT) {
60
+ RMN_EVENT["LIFECYCLE_STATE"] = "LIFECYCLE_STATE";
61
+ RMN_EVENT["SPOT_EVENT"] = "SPOT_EVENT";
62
+ })(exports.RMN_EVENT || (exports.RMN_EVENT = {}));
56
63
  exports.RMN_SPOT_EVENT = void 0;
57
64
  (function (RMN_SPOT_EVENT) {
58
65
  RMN_SPOT_EVENT["IMPRESSION"] = "IMPRESSION";
59
66
  RMN_SPOT_EVENT["CLICK"] = "CLICK";
60
67
  RMN_SPOT_EVENT["PURCHASE"] = "PURCHASE";
61
68
  RMN_SPOT_EVENT["ADD_TO_CART"] = "ADD_TO_CART";
69
+ RMN_SPOT_EVENT["REMOVE_FROM_CART"] = "REMOVE_FROM_CART";
62
70
  RMN_SPOT_EVENT["ADD_TO_WISHLIST"] = "ADD_TO_WISHLIST";
63
71
  RMN_SPOT_EVENT["BUY_NOW"] = "BUY_NOW";
64
72
  })(exports.RMN_SPOT_EVENT || (exports.RMN_SPOT_EVENT = {}));
65
-
66
73
  exports.RMN_ENV = void 0;
67
74
  (function (RMN_ENV) {
68
75
  RMN_ENV["LOCAL"] = "local";
@@ -71,6 +78,511 @@ exports.RMN_ENV = void 0;
71
78
  RMN_ENV["PRODUCTION"] = "production";
72
79
  })(exports.RMN_ENV || (exports.RMN_ENV = {}));
73
80
 
81
+ const SPOT_EVENTS_EXAMPLE = [
82
+ {
83
+ event: exports.RMN_SPOT_EVENT.CLICK,
84
+ url: 'https://dev.rmn.liquidcommerce.cloud/api/spots/event?e=eyJ2IjoiMS4xMiIsImF2IjozMDY1NzgzLCJhdCI6MTYzLCJidCI6MCwiY20iOjQ0MDE5MjQxOCwiY2giOjYzMTg0LCJjayI6e30sImNyIjo0ODE4NTUzNzUsImRpIjoiOWMxNGFhMGI3NWY4NDMxNTllMTAwYWQzNzA1NzQyYzMiLCJkaiI6MCwiaWkiOiIxZjU0MGM5NmQ1N2M0YmZjODFlZjRkNjhkMzFjNDVkOSIsImRtIjozLCJmYyI6NjU2NjgyNTQ5LCJmbCI6NjQzOTMxODIxLCJpcCI6IjM1LjIyMy4xOTguOTUiLCJudyI6MTE1MDAsInBjIjo1MDAwLCJvcCI6NTAwMCwibXAiOjUwMDAsImVjIjowLCJnbSI6MCwiZXAiOm51bGwsInByIjoyNDkzMTYsInJ0IjoxLCJycyI6NTAwLCJzYSI6IjU1Iiwic2IiOiJpLTA0MDI0ODg4ZDlkMWRjZWQ3Iiwic3AiOjI3MjI3Miwic3QiOjEyODcyOTYsInRyIjp0cnVlLCJ1ayI6IjNhZWRhMWMxLTZhYjItNDUwNy04Mzg5LTEwZTJmNDMxYjM5MSIsInRzIjoxNzI5MzU5MDU0OTI3LCJiZiI6dHJ1ZSwicG4iOiJyYlByb2R1Y3RVcGNzIiwiZ2MiOnRydWUsImdDIjp0cnVlLCJncyI6Im5vbmUiLCJkYyI6MSwidHoiOiJBbWVyaWNhL05ld19Zb3JrIiwidXIiOm51bGx9&s=hWz37kbxi_u95EVNn2aoQhc5Aas',
85
+ },
86
+ {
87
+ event: exports.RMN_SPOT_EVENT.IMPRESSION,
88
+ url: 'https://dev.rmn.liquidcommerce.cloud/api/spots/event?e=eyJ2IjoiMS4xMiIsImF2IjozMDY1NzgzLCJhdCI6MTYzLCJidCI6MCwiY20iOjQ0MDE5MjQxOCwiY2giOjYzMTg0LCJjayI6e30sImNyIjo0ODE4NTUzNzUsImRpIjoiOWMxNGFhMGI3NWY4NDMxNTllMTAwYWQzNzA1NzQyYzMiLCJkaiI6MCwiaWkiOiIxZjU0MGM5NmQ1N2M0YmZjODFlZjRkNjhkMzFjNDVkOSIsImRtIjozLCJmYyI6NjU2NjgyNTQ5LCJmbCI6NjQzOTMxODIxLCJpcCI6IjM1LjIyMy4xOTguOTUiLCJudyI6MTE1MDAsInBjIjo1MDAwLCJvcCI6NTAwMCwibXAiOjUwMDAsImVjIjowLCJnbSI6MCwiZXAiOm51bGwsInByIjoyNDkzMTYsInJ0IjoxLCJycyI6NTAwLCJzYSI6IjU1Iiwic2IiOiJpLTA0MDI0ODg4ZDlkMWRjZWQ3Iiwic3AiOjI3MjI3Miwic3QiOjEyODcyOTYsInRyIjp0cnVlLCJ1ayI6IjNhZWRhMWMxLTZhYjItNDUwNy04Mzg5LTEwZTJmNDMxYjM5MSIsInRzIjoxNzI5MzU5MDU0OTI3LCJiZiI6dHJ1ZSwicG4iOiJyYlByb2R1Y3RVcGNzIiwiZ2MiOnRydWUsImdDIjp0cnVlLCJncyI6Im5vbmUiLCJkYyI6MSwidHoiOiJBbWVyaWNhL05ld19Zb3JrIiwiYmEiOjEsImZxIjowfQ&s=djoysjCimurf-5T11AlNAwwLSS8',
89
+ },
90
+ {
91
+ event: exports.RMN_SPOT_EVENT.PURCHASE,
92
+ url: 'https://dev.rmn.liquidcommerce.cloud/api/spots/event?e=eyJ2IjoiMS4xMiIsImF2IjozMDY1NzgzLCJhdCI6MTYzLCJidCI6MCwiY20iOjQ0MDE5MjQxOCwiY2giOjYzMTg0LCJjayI6e30sImNyIjo0ODE4NTUzNzUsImRpIjoiOWMxNGFhMGI3NWY4NDMxNTllMTAwYWQzNzA1NzQyYzMiLCJkaiI6MCwiaWkiOiIxZjU0MGM5NmQ1N2M0YmZjODFlZjRkNjhkMzFjNDVkOSIsImRtIjozLCJmYyI6NjU2NjgyNTQ5LCJmbCI6NjQzOTMxODIxLCJpcCI6IjM1LjIyMy4xOTguOTUiLCJudyI6MTE1MDAsInBjIjo1MDAwLCJvcCI6NTAwMCwibXAiOjUwMDAsImVjIjowLCJnbSI6MCwiZXAiOm51bGwsInByIjoyNDkzMTYsInJ0IjoxLCJycyI6NTAwLCJzYSI6IjU1Iiwic2IiOiJpLTA0MDI0ODg4ZDlkMWRjZWQ3Iiwic3AiOjI3MjI3Miwic3QiOjEyODcyOTYsInRyIjp0cnVlLCJ1ayI6IjNhZWRhMWMxLTZhYjItNDUwNy04Mzg5LTEwZTJmNDMxYjM5MSIsInRzIjoxNzI5MzU5MDU0OTI3LCJiZiI6dHJ1ZSwicG4iOiJyYlByb2R1Y3RVcGNzIiwiZ2MiOnRydWUsImdDIjp0cnVlLCJncyI6Im5vbmUiLCJkYyI6MSwidHoiOiJBbWVyaWNhL05ld19Zb3JrIiwiZXQiOjU5fQ&s=AAPAw-3SfZ0JMzjEGFSwt9L-2S4',
93
+ },
94
+ {
95
+ event: exports.RMN_SPOT_EVENT.ADD_TO_CART,
96
+ url: 'https://dev.rmn.liquidcommerce.cloud/api/spots/event?e=eyJ2IjoiMS4xMiIsImF2IjozMDY1NzgzLCJhdCI6MTYzLCJidCI6MCwiY20iOjQ0MDE5MjQxOCwiY2giOjYzMTg0LCJjayI6e30sImNyIjo0ODE4NTUzNzUsImRpIjoiOWMxNGFhMGI3NWY4NDMxNTllMTAwYWQzNzA1NzQyYzMiLCJkaiI6MCwiaWkiOiIxZjU0MGM5NmQ1N2M0YmZjODFlZjRkNjhkMzFjNDVkOSIsImRtIjozLCJmYyI6NjU2NjgyNTQ5LCJmbCI6NjQzOTMxODIxLCJpcCI6IjM1LjIyMy4xOTguOTUiLCJudyI6MTE1MDAsInBjIjo1MDAwLCJvcCI6NTAwMCwibXAiOjUwMDAsImVjIjowLCJnbSI6MCwiZXAiOm51bGwsInByIjoyNDkzMTYsInJ0IjoxLCJycyI6NTAwLCJzYSI6IjU1Iiwic2IiOiJpLTA0MDI0ODg4ZDlkMWRjZWQ3Iiwic3AiOjI3MjI3Miwic3QiOjEyODcyOTYsInRyIjp0cnVlLCJ1ayI6IjNhZWRhMWMxLTZhYjItNDUwNy04Mzg5LTEwZTJmNDMxYjM5MSIsInRzIjoxNzI5MzU5MDU0OTI3LCJiZiI6dHJ1ZSwicG4iOiJyYlByb2R1Y3RVcGNzIiwiZ2MiOnRydWUsImdDIjp0cnVlLCJncyI6Im5vbmUiLCJkYyI6MSwidHoiOiJBbWVyaWNhL05ld19Zb3JrIiwiZXQiOjYwfQ&s=uzQFcjgL7m9XqUG8FvTPVN5YkZY',
97
+ },
98
+ {
99
+ event: exports.RMN_SPOT_EVENT.ADD_TO_WISHLIST,
100
+ url: 'https://dev.rmn.liquidcommerce.cloud/api/spots/event?e=eyJ2IjoiMS4xMiIsImF2IjozMDY1NzgzLCJhdCI6MTYzLCJidCI6MCwiY20iOjQ0MDE5MjQxOCwiY2giOjYzMTg0LCJjayI6e30sImNyIjo0ODE4NTUzNzUsImRpIjoiOWMxNGFhMGI3NWY4NDMxNTllMTAwYWQzNzA1NzQyYzMiLCJkaiI6MCwiaWkiOiIxZjU0MGM5NmQ1N2M0YmZjODFlZjRkNjhkMzFjNDVkOSIsImRtIjozLCJmYyI6NjU2NjgyNTQ5LCJmbCI6NjQzOTMxODIxLCJpcCI6IjM1LjIyMy4xOTguOTUiLCJudyI6MTE1MDAsInBjIjo1MDAwLCJvcCI6NTAwMCwibXAiOjUwMDAsImVjIjowLCJnbSI6MCwiZXAiOm51bGwsInByIjoyNDkzMTYsInJ0IjoxLCJycyI6NTAwLCJzYSI6IjU1Iiwic2IiOiJpLTA0MDI0ODg4ZDlkMWRjZWQ3Iiwic3AiOjI3MjI3Miwic3QiOjEyODcyOTYsInRyIjp0cnVlLCJ1ayI6IjNhZWRhMWMxLTZhYjItNDUwNy04Mzg5LTEwZTJmNDMxYjM5MSIsInRzIjoxNzI5MzU5MDU0OTI3LCJiZiI6dHJ1ZSwicG4iOiJyYlByb2R1Y3RVcGNzIiwiZ2MiOnRydWUsImdDIjp0cnVlLCJncyI6Im5vbmUiLCJkYyI6MSwidHoiOiJBbWVyaWNhL05ld19Zb3JrIiwiZXQiOjYzfQ&s=m3ISU_iIy-OFtXrTKpI6cJAEC0k',
101
+ },
102
+ {
103
+ event: exports.RMN_SPOT_EVENT.BUY_NOW,
104
+ url: 'https://dev.rmn.liquidcommerce.cloud/api/spots/event?e=eyJ2IjoiMS4xMiIsImF2IjozMDY1NzgzLCJhdCI6MTYzLCJidCI6MCwiY20iOjQ0MDE5MjQxOCwiY2giOjYzMTg0LCJjayI6e30sImNyIjo0ODE4NTUzNzUsImRpIjoiOWMxNGFhMGI3NWY4NDMxNTllMTAwYWQzNzA1NzQyYzMiLCJkaiI6MCwiaWkiOiIxZjU0MGM5NmQ1N2M0YmZjODFlZjRkNjhkMzFjNDVkOSIsImRtIjozLCJmYyI6NjU2NjgyNTQ5LCJmbCI6NjQzOTMxODIxLCJpcCI6IjM1LjIyMy4xOTguOTUiLCJudyI6MTE1MDAsInBjIjo1MDAwLCJvcCI6NTAwMCwibXAiOjUwMDAsImVjIjowLCJnbSI6MCwiZXAiOm51bGwsInByIjoyNDkzMTYsInJ0IjoxLCJycyI6NTAwLCJzYSI6IjU1Iiwic2IiOiJpLTA0MDI0ODg4ZDlkMWRjZWQ3Iiwic3AiOjI3MjI3Miwic3QiOjEyODcyOTYsInRyIjp0cnVlLCJ1ayI6IjNhZWRhMWMxLTZhYjItNDUwNy04Mzg5LTEwZTJmNDMxYjM5MSIsInRzIjoxNzI5MzU5MDU0OTI3LCJiZiI6dHJ1ZSwicG4iOiJyYlByb2R1Y3RVcGNzIiwiZ2MiOnRydWUsImdDIjp0cnVlLCJncyI6Im5vbmUiLCJkYyI6MSwidHoiOiJBbWVyaWNhL05ld19Zb3JrIiwiZXQiOjY5fQ&s=l6MOscQC-q-FkC2Ksd7w6jjySCQ',
105
+ },
106
+ ];
107
+ const RB_SPOTS_SELECTION_EXAMPLE = {
108
+ rbHomepageHero: [
109
+ {
110
+ id: 'abc123',
111
+ spot: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_FULL_IMAGE,
112
+ variant: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_FULL_IMAGE,
113
+ width: 1140,
114
+ height: 640,
115
+ header: 'Premium Wine Collection',
116
+ description: 'Discover our exclusive selection of vintage wines',
117
+ ctaText: 'Shop Wines',
118
+ textColor: '#ffffff',
119
+ ctaTextColor: '#ffffff',
120
+ primaryImage: 'https://placehold.co/1140x640/png?text=Wine+Collection',
121
+ mobilePrimaryImage: 'https://placehold.co/640x640/png?text=Mobile+Wine',
122
+ events: SPOT_EVENTS_EXAMPLE,
123
+ productIds: [1, 2, 3],
124
+ },
125
+ {
126
+ id: 'jkl012',
127
+ spot: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_TWO_TILE,
128
+ variant: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_TWO_TILE,
129
+ width: 1140,
130
+ height: 640,
131
+ header: 'Whiskey Collection',
132
+ description: 'Premium whiskeys from around the world',
133
+ ctaText: 'Shop Whiskeys',
134
+ textColor: '#ffffff',
135
+ backgroundColor: '#2c1810',
136
+ ctaTextColor: '#2c1810',
137
+ primaryImage: 'https://placehold.co/1140x640/png?text=Whiskey',
138
+ mobilePrimaryImage: 'https://placehold.co/640x640/png?text=Mobile+Whiskey',
139
+ events: SPOT_EVENTS_EXAMPLE,
140
+ productIds: [10, 11],
141
+ },
142
+ {
143
+ id: 'stu901',
144
+ spot: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_THREE_TILE,
145
+ variant: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_THREE_TILE,
146
+ width: 1140,
147
+ height: 640,
148
+ header: 'Summer Cocktails',
149
+ description: 'Essential spirits for summer mixing',
150
+ ctaText: 'Mix It Up',
151
+ textColor: '#ffffff',
152
+ backgroundColor: '#4d3a0a',
153
+ ctaTextColor: '#4d3a0a',
154
+ primaryImage: 'https://placehold.co/1140x640/png?text=Cocktails',
155
+ secondaryImage: 'https://placehold.co/1140x640/png?text=Mixers',
156
+ mobilePrimaryImage: 'https://placehold.co/640x640/png?text=Mobile+Cocktails',
157
+ mobileSecondaryImage: 'https://placehold.co/640x320/png?text=Mobile+Mixers',
158
+ events: SPOT_EVENTS_EXAMPLE,
159
+ productIds: [16, 17, 18],
160
+ },
161
+ {
162
+ id: 'def456',
163
+ spot: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_FULL_IMAGE,
164
+ variant: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_FULL_IMAGE,
165
+ width: 1140,
166
+ height: 640,
167
+ header: 'Craft Beer Festival',
168
+ description: 'Local breweries and exclusive releases',
169
+ ctaText: 'Explore Beers',
170
+ textColor: '#ffffff',
171
+ ctaTextColor: '#ffffff',
172
+ primaryImage: 'https://placehold.co/1140x640/png?text=Beer+Festival',
173
+ mobilePrimaryImage: 'https://placehold.co/640x640/png?text=Mobile+Beer',
174
+ events: SPOT_EVENTS_EXAMPLE,
175
+ productIds: [4, 5, 6],
176
+ },
177
+ {
178
+ id: 'mno345',
179
+ spot: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_TWO_TILE,
180
+ variant: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_TWO_TILE,
181
+ width: 1140,
182
+ height: 640,
183
+ header: 'Champagne Selection',
184
+ description: 'Finest champagnes for every occasion',
185
+ ctaText: 'View Champagnes',
186
+ textColor: '#ffffff',
187
+ backgroundColor: '#1a1a1a',
188
+ ctaTextColor: '#1a1a1a',
189
+ primaryImage: 'https://placehold.co/1140x640/png?text=Champagne',
190
+ mobilePrimaryImage: 'https://placehold.co/640x640/png?text=Mobile+Champagne',
191
+ events: SPOT_EVENTS_EXAMPLE,
192
+ productIds: [12, 13],
193
+ },
194
+ {
195
+ id: 'vwx234',
196
+ spot: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_THREE_TILE,
197
+ variant: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_THREE_TILE,
198
+ width: 1140,
199
+ height: 640,
200
+ header: 'Wine Regions',
201
+ description: 'Explore wines from top regions',
202
+ ctaText: 'Tour Regions',
203
+ textColor: '#ffffff',
204
+ backgroundColor: '#4d0a0a',
205
+ ctaTextColor: '#4d0a0a',
206
+ primaryImage: 'https://placehold.co/1140x640/png?text=Wine+Regions',
207
+ secondaryImage: 'https://placehold.co/1140x640/png?text=Vineyards',
208
+ mobilePrimaryImage: 'https://placehold.co/640x640/png?text=Mobile+Regions',
209
+ mobileSecondaryImage: 'https://placehold.co/640x320/png?text=Mobile+Vineyards',
210
+ events: SPOT_EVENTS_EXAMPLE,
211
+ productIds: [19, 20, 21],
212
+ },
213
+ {
214
+ id: 'ghi789',
215
+ spot: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_FULL_IMAGE,
216
+ variant: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_FULL_IMAGE,
217
+ width: 1140,
218
+ height: 640,
219
+ header: 'Rare Spirits',
220
+ description: 'Limited edition spirits collection',
221
+ ctaText: 'View Collection',
222
+ textColor: '#ffffff',
223
+ ctaTextColor: '#ffffff',
224
+ primaryImage: 'https://placehold.co/1140x640/png?text=Rare+Spirits',
225
+ mobilePrimaryImage: 'https://placehold.co/640x640/png?text=Mobile+Spirits',
226
+ events: SPOT_EVENTS_EXAMPLE,
227
+ productIds: [7, 8, 9],
228
+ },
229
+ {
230
+ id: 'pqr678',
231
+ spot: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_TWO_TILE,
232
+ variant: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_TWO_TILE,
233
+ width: 1140,
234
+ height: 640,
235
+ header: 'Gin Collection',
236
+ description: 'Artisanal gins and botanicals',
237
+ ctaText: 'Explore Gins',
238
+ textColor: '#ffffff',
239
+ backgroundColor: '#0a4d4d',
240
+ ctaTextColor: '#0a4d4d',
241
+ primaryImage: 'https://placehold.co/1140x640/png?text=Gin',
242
+ mobilePrimaryImage: 'https://placehold.co/640x640/png?text=Mobile+Gin',
243
+ events: SPOT_EVENTS_EXAMPLE,
244
+ productIds: [14, 15],
245
+ },
246
+ {
247
+ id: 'yz5678',
248
+ spot: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_THREE_TILE,
249
+ variant: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_THREE_TILE,
250
+ width: 1140,
251
+ height: 640,
252
+ header: 'Tequila Collection',
253
+ description: 'Premium tequilas and mezcals',
254
+ ctaText: 'Shop Tequila',
255
+ textColor: '#ffffff',
256
+ backgroundColor: '#0a4d2b',
257
+ ctaTextColor: '#0a4d2b',
258
+ primaryImage: 'https://placehold.co/1140x640/png?text=Tequila',
259
+ secondaryImage: 'https://placehold.co/1140x640/png?text=Mezcal',
260
+ mobilePrimaryImage: 'https://placehold.co/640x640/png?text=Mobile+Tequila',
261
+ mobileSecondaryImage: 'https://placehold.co/640x320/png?text=Mobile+Mezcal',
262
+ events: SPOT_EVENTS_EXAMPLE,
263
+ productIds: [22, 23, 24],
264
+ },
265
+ ],
266
+ rbHomepageHeroFullImage: [
267
+ {
268
+ id: 'hjk567',
269
+ spot: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_FULL_IMAGE,
270
+ variant: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_FULL_IMAGE,
271
+ width: 1140,
272
+ height: 640,
273
+ header: 'Holiday Gift Guide',
274
+ description: 'Perfect spirits for every occasion',
275
+ ctaText: 'Shop Gifts',
276
+ textColor: '#ffffff',
277
+ ctaTextColor: '#ffffff',
278
+ primaryImage: 'https://placehold.co/1140x640/png?text=Gift+Guide',
279
+ mobilePrimaryImage: 'https://placehold.co/640x640/png?text=Mobile+Gifts',
280
+ events: SPOT_EVENTS_EXAMPLE,
281
+ productIds: [25, 26],
282
+ },
283
+ {
284
+ id: 'qwe890',
285
+ spot: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_FULL_IMAGE,
286
+ variant: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_FULL_IMAGE,
287
+ width: 1140,
288
+ height: 640,
289
+ header: 'Summer Wine Festival',
290
+ description: 'Refreshing wines for summer',
291
+ ctaText: 'Shop Festival',
292
+ textColor: '#ffffff',
293
+ ctaTextColor: '#ffffff',
294
+ primaryImage: 'https://placehold.co/1140x640/png?text=Wine+Festival',
295
+ mobilePrimaryImage: 'https://placehold.co/640x640/png?text=Mobile+Festival',
296
+ events: SPOT_EVENTS_EXAMPLE,
297
+ productIds: [27, 28],
298
+ },
299
+ ],
300
+ rbHomepageHeroTwoTile: [
301
+ {
302
+ id: 'iop987',
303
+ spot: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_TWO_TILE,
304
+ variant: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_TWO_TILE,
305
+ width: 1140,
306
+ height: 640,
307
+ header: 'Bourbon Selection',
308
+ description: "Kentucky's finest bourbons",
309
+ ctaText: 'Shop Bourbon',
310
+ textColor: '#ffffff',
311
+ backgroundColor: '#2c1810',
312
+ ctaTextColor: '#2c1810',
313
+ primaryImage: 'https://placehold.co/1140x640/png?text=Bourbon',
314
+ mobilePrimaryImage: 'https://placehold.co/640x640/png?text=Mobile+Bourbon',
315
+ events: SPOT_EVENTS_EXAMPLE,
316
+ productIds: [29, 30],
317
+ },
318
+ {
319
+ id: 'lkj012',
320
+ spot: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_TWO_TILE,
321
+ variant: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_TWO_TILE,
322
+ width: 1140,
323
+ height: 640,
324
+ header: 'Vodka Collection',
325
+ description: 'Premium vodkas from around the world',
326
+ ctaText: 'Shop Vodka',
327
+ textColor: '#ffffff',
328
+ backgroundColor: '#1a1a1a',
329
+ ctaTextColor: '#1a1a1a',
330
+ primaryImage: 'https://placehold.co/1140x640/png?text=Vodka',
331
+ mobilePrimaryImage: 'https://placehold.co/640x640/png?text=Mobile+Vodka',
332
+ events: SPOT_EVENTS_EXAMPLE,
333
+ productIds: [31, 32],
334
+ },
335
+ ],
336
+ rbHomepageHeroThreeTile: [
337
+ {
338
+ id: 'bnm345',
339
+ spot: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_THREE_TILE,
340
+ variant: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_THREE_TILE,
341
+ width: 1140,
342
+ height: 640,
343
+ header: 'Rum Collection',
344
+ description: 'Caribbean and craft rums',
345
+ ctaText: 'Shop Rum',
346
+ textColor: '#ffffff',
347
+ backgroundColor: '#4d3a0a',
348
+ ctaTextColor: '#4d3a0a',
349
+ primaryImage: 'https://placehold.co/1140x640/png?text=Rum',
350
+ secondaryImage: 'https://placehold.co/1140x640/png?text=Craft+Rum',
351
+ mobilePrimaryImage: 'https://placehold.co/640x640/png?text=Mobile+Rum',
352
+ mobileSecondaryImage: 'https://placehold.co/640x320/png?text=Mobile+Craft',
353
+ events: SPOT_EVENTS_EXAMPLE,
354
+ productIds: [33, 34],
355
+ },
356
+ {
357
+ id: 'fgh678',
358
+ spot: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_THREE_TILE,
359
+ variant: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_THREE_TILE,
360
+ width: 1140,
361
+ height: 640,
362
+ header: 'Scotch Selection',
363
+ description: 'Single malts and blends',
364
+ ctaText: 'Shop Scotch',
365
+ textColor: '#ffffff',
366
+ backgroundColor: '#4d0a0a',
367
+ ctaTextColor: '#4d0a0a',
368
+ primaryImage: 'https://placehold.co/1140x640/png?text=Scotch',
369
+ secondaryImage: 'https://placehold.co/1140x640/png?text=Single+Malts',
370
+ mobilePrimaryImage: 'https://placehold.co/640x640/png?text=Mobile+Scotch',
371
+ mobileSecondaryImage: 'https://placehold.co/640x320/png?text=Mobile+Malts',
372
+ events: SPOT_EVENTS_EXAMPLE,
373
+ productIds: [35, 36],
374
+ },
375
+ ],
376
+ rbLargeCategoryImageTout: [
377
+ {
378
+ id: 'cde567',
379
+ spot: exports.RMN_SPOT_TYPE.RB_LARGE_CATEGORY_IMAGE_TOUT,
380
+ variant: exports.RMN_SPOT_TYPE.RB_LARGE_CATEGORY_IMAGE_TOUT,
381
+ width: 468,
382
+ height: 410,
383
+ header: 'Rare & Limited Edition',
384
+ description: 'Discover our collection of hard-to-find and limited release spirits.',
385
+ textColor: '#ffffff',
386
+ ctaTextColor: '#ffffff',
387
+ primaryImage: 'https://placehold.co/468x410/png?text=Rare+Spirits',
388
+ mobilePrimaryImage: 'https://placehold.co/468x410/png?text=Mobile+Rare+Spirits',
389
+ ctaText: 'Shop Rare Spirits',
390
+ events: SPOT_EVENTS_EXAMPLE,
391
+ productIds: [37, 38, 39, 40, 41],
392
+ },
393
+ {
394
+ id: 'efg789',
395
+ spot: exports.RMN_SPOT_TYPE.RB_LARGE_CATEGORY_IMAGE_TOUT,
396
+ variant: exports.RMN_SPOT_TYPE.RB_LARGE_CATEGORY_IMAGE_TOUT,
397
+ width: 468,
398
+ height: 410,
399
+ header: 'Vintage Champagnes',
400
+ description: 'Explore our prestigious collection of aged champagnes.',
401
+ textColor: '#ffffff',
402
+ ctaTextColor: '#ffffff',
403
+ primaryImage: 'https://placehold.co/468x410/png?text=Vintage+Champagne',
404
+ mobilePrimaryImage: 'https://placehold.co/468x410/png?text=Mobile+Champagne',
405
+ ctaText: 'View Collection',
406
+ events: SPOT_EVENTS_EXAMPLE,
407
+ productIds: [42, 43, 44, 45, 46],
408
+ },
409
+ {
410
+ id: 'ghi012',
411
+ spot: exports.RMN_SPOT_TYPE.RB_LARGE_CATEGORY_IMAGE_TOUT,
412
+ variant: exports.RMN_SPOT_TYPE.RB_LARGE_CATEGORY_IMAGE_TOUT,
413
+ width: 468,
414
+ height: 410,
415
+ header: 'Small Batch Bourbon',
416
+ description: 'Hand-selected premium bourbon from craft distilleries.',
417
+ textColor: '#ffffff',
418
+ ctaTextColor: '#ffffff',
419
+ primaryImage: 'https://placehold.co/468x410/png?text=Craft+Bourbon',
420
+ mobilePrimaryImage: 'https://placehold.co/468x410/png?text=Mobile+Bourbon',
421
+ ctaText: 'Explore Bourbon',
422
+ events: SPOT_EVENTS_EXAMPLE,
423
+ productIds: [47, 48, 49, 50, 51],
424
+ },
425
+ ],
426
+ rbSmallDiscoverTout: [
427
+ {
428
+ id: 'jkl345',
429
+ spot: exports.RMN_SPOT_TYPE.RB_SMALL_DISCOVER_TOUT,
430
+ variant: exports.RMN_SPOT_TYPE.RB_SMALL_DISCOVER_TOUT,
431
+ width: 224,
432
+ height: 378,
433
+ header: 'Château Margaux 2015 Bordeaux',
434
+ textColor: '#ffffff',
435
+ primaryImage: 'https://placehold.co/224x378/png?text=Château+Margaux',
436
+ mobilePrimaryImage: 'https://placehold.co/224x378/png?text=Mobile+Château+Margaux',
437
+ events: SPOT_EVENTS_EXAMPLE,
438
+ productIds: [52, 53, 54, 55, 56],
439
+ },
440
+ {
441
+ id: 'lmn678',
442
+ spot: exports.RMN_SPOT_TYPE.RB_SMALL_DISCOVER_TOUT,
443
+ variant: exports.RMN_SPOT_TYPE.RB_SMALL_DISCOVER_TOUT,
444
+ width: 224,
445
+ height: 378,
446
+ header: 'Macallan 25 Year',
447
+ textColor: '#ffffff',
448
+ primaryImage: 'https://placehold.co/224x378/png?text=Macallan+25',
449
+ mobilePrimaryImage: 'https://placehold.co/224x378/png?text=Mobile+Macallan',
450
+ events: SPOT_EVENTS_EXAMPLE,
451
+ productIds: [57, 58, 59, 60, 61],
452
+ },
453
+ {
454
+ id: 'nop901',
455
+ spot: exports.RMN_SPOT_TYPE.RB_SMALL_DISCOVER_TOUT,
456
+ variant: exports.RMN_SPOT_TYPE.RB_SMALL_DISCOVER_TOUT,
457
+ width: 224,
458
+ height: 378,
459
+ header: 'Louis XIII Cognac',
460
+ textColor: '#ffffff',
461
+ primaryImage: 'https://placehold.co/224x378/png?text=Louis+XIII',
462
+ mobilePrimaryImage: 'https://placehold.co/224x378/png?text=Mobile+Louis+XIII',
463
+ events: SPOT_EVENTS_EXAMPLE,
464
+ productIds: [62, 63, 64, 65, 66],
465
+ },
466
+ ],
467
+ rbSmallCategoryImageTout: [
468
+ {
469
+ id: 'qrs234',
470
+ spot: exports.RMN_SPOT_TYPE.RB_SMALL_CATEGORY_IMAGE_TOUT,
471
+ variant: exports.RMN_SPOT_TYPE.RB_SMALL_CATEGORY_IMAGE_TOUT,
472
+ width: 224,
473
+ height: 410,
474
+ header: 'Japanese Sake',
475
+ textColor: '#ffffff',
476
+ primaryImage: 'https://placehold.co/224x410/png?text=Japanese+Sake',
477
+ mobilePrimaryImage: 'https://placehold.co/224x410/png?text=Mobile+Japanese+Sake',
478
+ events: SPOT_EVENTS_EXAMPLE,
479
+ productIds: [67, 68, 69, 70, 71],
480
+ },
481
+ {
482
+ id: 'stu567',
483
+ spot: exports.RMN_SPOT_TYPE.RB_SMALL_CATEGORY_IMAGE_TOUT,
484
+ variant: exports.RMN_SPOT_TYPE.RB_SMALL_CATEGORY_IMAGE_TOUT,
485
+ width: 224,
486
+ height: 410,
487
+ header: 'Craft Vermouth',
488
+ textColor: '#ffffff',
489
+ primaryImage: 'https://placehold.co/224x410/png?text=Craft+Vermouth',
490
+ mobilePrimaryImage: 'https://placehold.co/224x410/png?text=Mobile+Vermouth',
491
+ events: SPOT_EVENTS_EXAMPLE,
492
+ productIds: [72, 73, 74, 75, 76],
493
+ },
494
+ {
495
+ id: 'vwx890',
496
+ spot: exports.RMN_SPOT_TYPE.RB_SMALL_CATEGORY_IMAGE_TOUT,
497
+ variant: exports.RMN_SPOT_TYPE.RB_SMALL_CATEGORY_IMAGE_TOUT,
498
+ width: 224,
499
+ height: 410,
500
+ header: 'Premium Mezcal',
501
+ textColor: '#ffffff',
502
+ primaryImage: 'https://placehold.co/224x410/png?text=Premium+Mezcal',
503
+ mobilePrimaryImage: 'https://placehold.co/224x410/png?text=Mobile+Mezcal',
504
+ events: SPOT_EVENTS_EXAMPLE,
505
+ productIds: [77, 78, 79, 80, 81],
506
+ },
507
+ ],
508
+ rbCollectionBannerWithoutTextBlock: [
509
+ {
510
+ id: 'yz1234',
511
+ spot: exports.RMN_SPOT_TYPE.RB_COLLECTION_BANNER_WITHOUT_TEXT_BLOCK,
512
+ variant: exports.RMN_SPOT_TYPE.RB_COLLECTION_BANNER_WITHOUT_TEXT_BLOCK,
513
+ width: 887,
514
+ height: 344,
515
+ primaryImage: 'https://placehold.co/887x344/png?text=Summer+Cocktails',
516
+ mobilePrimaryImage: 'https://placehold.co/887x344/png?text=Mobile+Summer+Cocktails',
517
+ events: SPOT_EVENTS_EXAMPLE,
518
+ productIds: [82, 83, 84, 85, 86],
519
+ },
520
+ {
521
+ id: 'abc567',
522
+ spot: exports.RMN_SPOT_TYPE.RB_COLLECTION_BANNER_WITHOUT_TEXT_BLOCK,
523
+ variant: exports.RMN_SPOT_TYPE.RB_COLLECTION_BANNER_WITHOUT_TEXT_BLOCK,
524
+ width: 887,
525
+ height: 344,
526
+ primaryImage: 'https://placehold.co/887x344/png?text=Holiday+Spirits',
527
+ mobilePrimaryImage: 'https://placehold.co/887x344/png?text=Mobile+Holiday+Spirits',
528
+ events: SPOT_EVENTS_EXAMPLE,
529
+ productIds: [87, 88, 89, 90, 91],
530
+ },
531
+ {
532
+ id: 'def901',
533
+ spot: exports.RMN_SPOT_TYPE.RB_COLLECTION_BANNER_WITHOUT_TEXT_BLOCK,
534
+ variant: exports.RMN_SPOT_TYPE.RB_COLLECTION_BANNER_WITHOUT_TEXT_BLOCK,
535
+ width: 887,
536
+ height: 344,
537
+ primaryImage: 'https://placehold.co/887x344/png?text=Wine+Essentials',
538
+ mobilePrimaryImage: 'https://placehold.co/887x344/png?text=Mobile+Wine+Essentials',
539
+ events: SPOT_EVENTS_EXAMPLE,
540
+ productIds: [92, 93, 94, 95, 96],
541
+ },
542
+ ],
543
+ rbNavigationBanner: [
544
+ {
545
+ id: 'ghi234',
546
+ spot: exports.RMN_SPOT_TYPE.RB_NAVIGATION_BANNER,
547
+ variant: exports.RMN_SPOT_TYPE.RB_NAVIGATION_BANNER,
548
+ width: 440,
549
+ height: 220,
550
+ header: 'Explore Tequilas',
551
+ textColor: '#ffffff',
552
+ primaryImage: 'https://placehold.co/440x220/png?text=Tequila+Collection',
553
+ mobilePrimaryImage: 'https://placehold.co/440x220/png?text=Mobile+Tequila+Collection',
554
+ events: SPOT_EVENTS_EXAMPLE,
555
+ productIds: [97, 98, 99, 100, 101],
556
+ },
557
+ {
558
+ id: 'jkl678',
559
+ spot: exports.RMN_SPOT_TYPE.RB_NAVIGATION_BANNER,
560
+ variant: exports.RMN_SPOT_TYPE.RB_NAVIGATION_BANNER,
561
+ width: 440,
562
+ height: 220,
563
+ header: 'Craft Gin Selection',
564
+ textColor: '#ffffff',
565
+ primaryImage: 'https://placehold.co/440x220/png?text=Gin+Selection',
566
+ mobilePrimaryImage: 'https://placehold.co/440x220/png?text=Mobile+Gin+Selection',
567
+ events: SPOT_EVENTS_EXAMPLE,
568
+ productIds: [102, 103, 104, 105, 106],
569
+ },
570
+ {
571
+ id: 'mno012',
572
+ spot: exports.RMN_SPOT_TYPE.RB_NAVIGATION_BANNER,
573
+ variant: exports.RMN_SPOT_TYPE.RB_NAVIGATION_BANNER,
574
+ width: 440,
575
+ height: 220,
576
+ header: 'Premium Vodka',
577
+ textColor: '#ffffff',
578
+ primaryImage: 'https://placehold.co/440x220/png?text=Vodka+Premium',
579
+ mobilePrimaryImage: 'https://placehold.co/440x220/png?text=Mobile+Vodka+Premium',
580
+ events: SPOT_EVENTS_EXAMPLE,
581
+ productIds: [107, 108, 109, 110, 111],
582
+ },
583
+ ],
584
+ };
585
+
74
586
  const REQUEST_CLOUD_PARTNER_SITE = 'X-Liquid-Partner-Site';
75
587
  const REQUEST_CLOUD_PROTECTED_KEY = 'X-Liquid-Protected';
76
588
  const REQUEST_CLOUD_PROTECTED_TIMESTAMP = 'X-Liquid-Timestamp';
@@ -14999,7 +15511,7 @@ class BaseApi extends BaseApiAbstract {
14999
15511
  */
15000
15512
  async post(path, data, configOverrides) {
15001
15513
  let requestData = data;
15002
- if (this.authInfo.env !== exports.RMN_ENV.DEVELOPMENT) {
15514
+ if (![exports.RMN_ENV.LOCAL, exports.RMN_ENV.DEVELOPMENT].includes(this.authInfo.env)) {
15003
15515
  const timestamp = new Date().getTime();
15004
15516
  configOverrides = {
15005
15517
  ...configOverrides,
@@ -15152,9 +15664,7 @@ class AuthService extends BaseApi {
15152
15664
  }
15153
15665
 
15154
15666
  const SPOT_ELEMENT_TAG = 'spot-element';
15155
- const SPOT_ELEMENT_SLOT_NAME = `${SPOT_ELEMENT_TAG}-custom-content`;
15156
- const SPOTS_SELECTION_API_PATH = '/spots/selection';
15157
-
15667
+ const CAROUSEL_ELEMENT_TAG = 'spot-carousel-element';
15158
15668
  const GFONT_PRECONNECT = `
15159
15669
  <link rel="preconnect" href="https://fonts.googleapis.com">
15160
15670
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
@@ -15165,163 +15675,1501 @@ const GFONT_SOURCE_SANS_3 = `
15165
15675
  const GFONT_CORMORANT = `
15166
15676
  <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Cormorant:ital,wght@0,300..700;1,300..700&family=Source+Sans+3:ital,wght@0,200..900;1,200..900&display=swap">
15167
15677
  `;
15168
- const SPOT_ELEMENT_TEMPLATE = (width, height, hasCustomContent) => {
15169
- const style = document.createElement('style');
15170
- style.textContent = `
15171
- :host {
15172
- display: block;
15173
- position: relative;
15174
- container-type: inline-size;
15175
- overflow: hidden;
15678
+
15679
+ /**
15680
+ * Determines the event type from a raw event string.
15681
+ *
15682
+ * @param {string} [event] - The raw event string to evaluate.
15683
+ * @returns {RMN_SPOT_EVENT | null} - The corresponding RMN_SPOT_EVENT or null if no match is found.
15684
+ */
15685
+ function getEventTypeFromRawEvent(event) {
15686
+ if (!event) {
15687
+ return null;
15176
15688
  }
15177
- :host([fluid="true"]) {
15178
- width: 100%;
15179
- height: 100%;
15689
+ if (event.includes('cart')) {
15690
+ if (event.includes('add')) {
15691
+ return exports.RMN_SPOT_EVENT.ADD_TO_CART;
15692
+ }
15693
+ if (event.includes('remove')) {
15694
+ return exports.RMN_SPOT_EVENT.REMOVE_FROM_CART;
15695
+ }
15180
15696
  }
15181
- :host([fluid="false"]) {
15182
- width: ${width}px;
15183
- height: ${height}px;
15697
+ if (event.includes('purchase')) {
15698
+ return exports.RMN_SPOT_EVENT.PURCHASE;
15184
15699
  }
15185
- `;
15186
- const wrapper = document.createElement('div');
15187
- wrapper.className = 'wrapper';
15188
- const slot = document.createElement('slot');
15189
- slot.name = SPOT_ELEMENT_SLOT_NAME;
15190
- if (!hasCustomContent) {
15191
- style.textContent += `
15192
- :host .wrapper {
15193
- position: absolute;
15194
- top: 0;
15195
- left: 0;
15196
- width: 100%;
15197
- height: 100%;
15198
- overflow: hidden;
15199
- }
15200
- `;
15700
+ // if(event.includes('refund')) {
15701
+ // return RMN_SPOT_EVENT.REFUND;
15702
+ // }
15703
+ if (event.includes('wishlist') && event.includes('add')) {
15704
+ return exports.RMN_SPOT_EVENT.ADD_TO_WISHLIST;
15201
15705
  }
15202
- return { style, wrapper, slot };
15203
- };
15204
-
15205
- const STYLES$i = ({ primaryImage, secondaryImage }) => `
15206
- <style>
15207
- .content {
15208
- width: 100%;
15209
- height: 100%;
15210
- display: flex;
15211
- flex-direction: row;
15212
- background-color: #FFFFFF;
15213
- gap: 0.5cqw;
15214
- cursor: pointer;
15215
- color: inherit;
15706
+ return null;
15707
+ }
15708
+ /**
15709
+ * Recursively extracts ID values from a nested data structure.
15710
+ * Searches for specified property names and collects their primitive values (strings/numbers).
15711
+ *
15712
+ * @param data - The data structure to search through (can be nested objects/arrays)
15713
+ * @param propertyNames - Array of property names to look for
15714
+ * @returns Array of extracted ID values (strings/numbers only)
15715
+ *
15716
+ * @example
15717
+ * const data = {
15718
+ * id: [1, 2, 3],
15719
+ * nested: { id: 'abc' },
15720
+ * items: [{ id: 456 }]
15721
+ * };
15722
+ * extractDeepIds(data); // Returns [1, 2, 3, 'abc', 456]
15723
+ */
15724
+ function extractDeepIds(data, propertyNames) {
15725
+ const ids = [];
15726
+ const defaulPropertyNames = [
15727
+ 'id',
15728
+ 'upc',
15729
+ 'groupingId',
15730
+ 'sku',
15731
+ 'productId',
15732
+ 'item_id',
15733
+ 'isbn',
15734
+ 'asin',
15735
+ 'mpn',
15736
+ 'model_number',
15737
+ 'article_number',
15738
+ 'variant_id',
15739
+ 'item_number',
15740
+ 'catalog_id',
15741
+ 'reference_id',
15742
+ ];
15743
+ // Set for faster property name lookups
15744
+ const propertySet = new Set(defaulPropertyNames);
15745
+ /**
15746
+ * Processes a value and extracts IDs if it matches criteria
15747
+ * @param value - The value to process
15748
+ * @param currentKey - The property name of the current value
15749
+ */
15750
+ const processValue = (value, currentKey) => {
15751
+ // Early exit for null/undefined values
15752
+ if (value == null)
15753
+ return;
15754
+ // If current key matches our target properties
15755
+ if (currentKey && propertySet.has(currentKey)) {
15756
+ if (Array.isArray(value)) {
15757
+ // Filter and push valid array values in one pass
15758
+ ids.push(...value.filter((item) => typeof item === 'string' || typeof item === 'number'));
15759
+ }
15760
+ else if (typeof value === 'string' || typeof value === 'number') {
15761
+ ids.push(value);
15762
+ }
15763
+ return; // Stop processing this branch after handling the ID
15216
15764
  }
15217
- .big-image {
15218
- width: 65%;
15219
- height: 100%;
15220
- background-image: url("${primaryImage}");
15221
- background-position: center;
15222
- background-repeat: no-repeat;
15223
- background-size: cover;
15765
+ // Recursively process nested structures
15766
+ if (Array.isArray(value)) {
15767
+ value.forEach((item) => processValue(item));
15224
15768
  }
15225
- .main {
15226
- width: 35%;
15227
- height: 100%;
15228
- display: flex;
15229
- flex-direction: column;
15230
- gap: 0.5cqw;
15769
+ else if (typeof value === 'object') {
15770
+ // Process all enumerable properties
15771
+ for (const [key, val] of Object.entries(value)) {
15772
+ processValue(val, key);
15773
+ }
15231
15774
  }
15232
- .small-image {
15233
- width: 100%;
15234
- height: 50%;
15235
- background-image: url("${secondaryImage}");
15236
- background-position: center;
15237
- background-repeat: no-repeat;
15238
- background-size: cover;
15775
+ };
15776
+ processValue(data);
15777
+ return ids; // No need to filter nulls as we handle that during collection
15778
+ }
15779
+ // Fallback method using fetch if sendBeacon isn't available
15780
+ async function fallbackEventFire(url) {
15781
+ try {
15782
+ const racePromise = Promise.race([
15783
+ // Promise #1: The fetch request
15784
+ fetch(url, {
15785
+ method: 'POST',
15786
+ keepalive: true,
15787
+ }),
15788
+ // Promise #2: The timeout
15789
+ new Promise((_, reject) => setTimeout(() => reject(new Error('Request timeout')), 2000)),
15790
+ ]);
15791
+ /**
15792
+ * Prevent requests from hanging indefinitely
15793
+ * Improve user experience by failing fast
15794
+ * Handle slow network conditions gracefully
15795
+ * Ensure resources are freed up in a timely manner
15796
+ */
15797
+ const response = await racePromise;
15798
+ return response.ok;
15799
+ }
15800
+ catch (_a) {
15801
+ return false;
15802
+ }
15803
+ }
15804
+ /**
15805
+ * Extracts and decodes a URL from a base64-encoded query parameter.
15806
+ *
15807
+ * @param {string} url - The URL containing the base64-encoded query parameter.
15808
+ * @returns {string | null} - The decoded URL or null if not found or invalid.
15809
+ */
15810
+ function getRedirectUrlFromPayload(url) {
15811
+ try {
15812
+ const base64String = new URL(url).searchParams.get('e');
15813
+ if (!base64String) {
15814
+ return null;
15239
15815
  }
15240
- .text {
15241
- background: #E8E6DE;
15242
- text-align: center;
15243
- display: flex;
15244
- flex-direction: column;
15245
- justify-content: center;
15246
- align-items: center;
15247
- height: 50%;
15248
- width: 100%;
15249
- padding: 5% 10%;
15250
- box-sizing: border-box;
15251
- font-family: "Source Sans 3", sans-serif;
15816
+ const data = JSON.parse(atob(base64String));
15817
+ return data.ur || null;
15818
+ }
15819
+ catch (_a) {
15820
+ return null;
15821
+ }
15822
+ }
15823
+ /**
15824
+ * Fires an event using the navigator.sendBeacon method or a fallback method if sendBeacon is not available.
15825
+ * If the event is a click event and a redirect URL is found, it redirects the user to that URL.
15826
+ *
15827
+ * @param {IFireEventParams} params - The parameters for firing the event.
15828
+ * @param {RMN_SPOT_EVENT} params.event - The event type.
15829
+ * @param {string} params.eventUrl - The URL to which the event is sent.
15830
+ * @returns {Promise<void>} - A promise that resolves when the event is fired.
15831
+ */
15832
+ async function fireEvent({ event, eventUrl }) {
15833
+ var _a;
15834
+ try {
15835
+ const didFireEvent = ((_a = navigator === null || navigator === void 0 ? void 0 : navigator.sendBeacon) === null || _a === void 0 ? void 0 : _a.call(navigator, eventUrl)) || (await fallbackEventFire(eventUrl));
15836
+ if (!didFireEvent) {
15837
+ return;
15252
15838
  }
15253
- .header {
15254
- font-size: 2.2cqw;
15255
- color: #000000;
15256
- font-style: normal;
15257
- font-weight: 400;
15258
- font-family: "Cormorant", serif;
15259
- margin: 0;
15839
+ if (event === exports.RMN_SPOT_EVENT.CLICK) {
15840
+ const redirectUrl = getRedirectUrlFromPayload(eventUrl);
15841
+ if (redirectUrl) {
15842
+ window.location.href = redirectUrl;
15843
+ }
15260
15844
  }
15261
- .description {
15262
- font-size: 1.2cqw;
15263
- color: #000000;
15264
- font-style: normal;
15265
- font-weight: 400;
15266
- margin: 1cqh 0;
15845
+ }
15846
+ catch (_b) {
15847
+ // Handle errors silently
15848
+ }
15849
+ }
15850
+ function calculateScaleFactor(elementScale) {
15851
+ // Step 1: Apply square root for non-linear scaling
15852
+ // This creates a more gradual scaling effect, especially for larger changes
15853
+ // For example:
15854
+ // - elementScale of 0.25 (1/4 size) becomes 0.5
15855
+ // - elementScale of 1 (unchanged) remains 1
15856
+ // - elementScale of 4 (4x size) becomes 2
15857
+ const baseFactor = Math.sqrt(elementScale);
15858
+ // Step 2: Apply additional dampening to further soften the scaling effect
15859
+ // The dampening factor (0.5) can be adjusted:
15860
+ // - Lower values (closer to 0) make scaling more subtle
15861
+ // - Higher values (closer to 1) make scaling more pronounced
15862
+ const dampening = 0.35;
15863
+ // Calculate the scaleFactor:
15864
+ // 1. (baseFactor - 1) represents the change from the original size
15865
+ // 2. Multiply by dampening to reduce the effect
15866
+ // 3. Add 1 to center the scaling around the original size
15867
+ // For example, if baseFactor is 2:
15868
+ // scaleFactor = 1 + (2 - 1) * 0.5 = 1.5
15869
+ const scaleFactor = 1 + (baseFactor - 1) * dampening;
15870
+ // Step 3: Define the allowed range for the scale factor
15871
+ // This ensures that the font size never changes too drastically
15872
+ const minScale = 0.35; // Font will never be smaller than 50% of original
15873
+ const maxScale = 1.5; // Font will never be larger than 150% of original
15874
+ // Step 4: Clamp the scale factor to the defined range
15875
+ // Math.min ensures the value doesn't exceed maxScale
15876
+ // Math.max ensures the value isn't less than minScale
15877
+ return Math.max(minScale, Math.min(maxScale, scaleFactor));
15878
+ }
15879
+
15880
+ class IntersectionObserverService {
15881
+ constructor(defaultOptions = {}) {
15882
+ this.observers = new Map();
15883
+ this.defaultOptions = {
15884
+ root: null,
15885
+ rootMargin: '0px',
15886
+ threshold: 0.5,
15887
+ ...defaultOptions,
15888
+ };
15889
+ }
15890
+ observe(element, callback, options = {}) {
15891
+ const mergedOptions = { ...this.defaultOptions, ...options };
15892
+ const ioCallback = (entries) => {
15893
+ entries.forEach((entry) => {
15894
+ if (entry.isIntersecting) {
15895
+ callback(entry);
15896
+ }
15897
+ });
15898
+ };
15899
+ const observer = new IntersectionObserver(ioCallback, mergedOptions);
15900
+ this.observers.set(element, observer);
15901
+ observer.observe(element);
15902
+ }
15903
+ unobserve(element) {
15904
+ const observer = this.observers.get(element);
15905
+ if (observer) {
15906
+ observer.unobserve(element);
15907
+ observer.disconnect();
15908
+ this.observers.delete(element);
15267
15909
  }
15268
- .button {
15269
- font-size: 1cqw;
15270
- color: #000000;
15271
- background-color: transparent;
15272
- font-style: normal;
15273
- font-weight: 700;
15274
- text-decoration: underline;
15275
- text-transform: uppercase;
15276
- border: none;
15277
- cursor: pointer;
15278
- padding: 0;
15279
- transition: opacity 0.3s ease;
15910
+ }
15911
+ unobserveAll() {
15912
+ this.observers.forEach((observer, element) => {
15913
+ observer.unobserve(element);
15914
+ observer.disconnect();
15915
+ });
15916
+ this.observers.clear();
15917
+ }
15918
+ }
15919
+
15920
+ var ENUM_LOCAL_STORAGE_SPOT_ARRAY_INDEX;
15921
+ (function (ENUM_LOCAL_STORAGE_SPOT_ARRAY_INDEX) {
15922
+ ENUM_LOCAL_STORAGE_SPOT_ARRAY_INDEX[ENUM_LOCAL_STORAGE_SPOT_ARRAY_INDEX["PLACEMENT_ID"] = 0] = "PLACEMENT_ID";
15923
+ ENUM_LOCAL_STORAGE_SPOT_ARRAY_INDEX[ENUM_LOCAL_STORAGE_SPOT_ARRAY_INDEX["SPOT_ID"] = 1] = "SPOT_ID";
15924
+ ENUM_LOCAL_STORAGE_SPOT_ARRAY_INDEX[ENUM_LOCAL_STORAGE_SPOT_ARRAY_INDEX["SPOT_TYPE"] = 2] = "SPOT_TYPE";
15925
+ ENUM_LOCAL_STORAGE_SPOT_ARRAY_INDEX[ENUM_LOCAL_STORAGE_SPOT_ARRAY_INDEX["EVENTS"] = 3] = "EVENTS";
15926
+ ENUM_LOCAL_STORAGE_SPOT_ARRAY_INDEX[ENUM_LOCAL_STORAGE_SPOT_ARRAY_INDEX["PRODUCT_IDS"] = 4] = "PRODUCT_IDS";
15927
+ ENUM_LOCAL_STORAGE_SPOT_ARRAY_INDEX[ENUM_LOCAL_STORAGE_SPOT_ARRAY_INDEX["CREATED_AT"] = 5] = "CREATED_AT";
15928
+ })(ENUM_LOCAL_STORAGE_SPOT_ARRAY_INDEX || (ENUM_LOCAL_STORAGE_SPOT_ARRAY_INDEX = {}));
15929
+ class LocalStorageService {
15930
+ constructor() {
15931
+ if (typeof window.localStorage === 'undefined') {
15932
+ console.warn('Local storage is not supported in this environment');
15933
+ return;
15280
15934
  }
15281
- .content:hover .button {
15282
- opacity: 0.7;
15935
+ this.spots = new Map();
15936
+ // Sync local storage with the current state
15937
+ this.syncLocalStorage();
15938
+ // Remove expired spots
15939
+ this.removeExpiredSpots();
15940
+ }
15941
+ static getInstance() {
15942
+ if (!LocalStorageService.instance) {
15943
+ LocalStorageService.instance = new LocalStorageService();
15283
15944
  }
15284
- @container (max-width: 600px) {
15285
- .header {
15286
- font-size: 3cqw;
15287
- }
15288
- .description {
15289
- font-size: 1.6cqw;
15290
- margin: 0;
15945
+ return LocalStorageService.instance;
15946
+ }
15947
+ syncLocalStorage() {
15948
+ const localStorageData = window.localStorage.getItem(LocalStorageService.localStorageKey);
15949
+ if (localStorageData) {
15950
+ try {
15951
+ const decryptedData = this.decryptData(localStorageData);
15952
+ const parsedData = JSON.parse(decryptedData);
15953
+ if (parsedData && typeof parsedData === 'object') {
15954
+ const data = {};
15955
+ for (const [key, value] of Object.entries(parsedData)) {
15956
+ data[key] = this.arrayToObject(value);
15957
+ }
15958
+ this.spots = this.objectToMap(data);
15959
+ }
15960
+ else {
15961
+ this.clearLocalStorage();
15962
+ }
15291
15963
  }
15292
- .button {
15293
- font-size: 1.4cqw;
15964
+ catch (_a) {
15965
+ // If there is an error parsing the data, clear the local storage to prevent any issues
15966
+ this.clearLocalStorage();
15294
15967
  }
15295
15968
  }
15296
- </style>
15297
- `;
15298
- function billboardV1Template(spot) {
15299
- return `
15300
- ${GFONT_PRECONNECT}
15301
- ${GFONT_SOURCE_SANS_3}
15302
- ${GFONT_CORMORANT}
15303
- ${STYLES$i(spot)}
15304
- <div class="content">
15305
- <div class="big-image"></div>
15306
- <div class="main">
15307
- <div class="small-image"></div>
15308
- <div class="text">
15309
- <h2 class="header">${spot.header}</h2>
15310
- <p class="description">${spot.description}</p>
15311
- <span class="button">${spot.ctaText}</span>
15312
- </div>
15313
- </div>
15314
- </div>
15315
- `;
15316
- }
15317
-
15318
- const STYLES$h = ({ primaryImage }) => `
15319
- <style>
15320
- .content {
15321
- display: flex;
15322
- flex-direction: column;
15323
- justify-content: flex-end;
15324
- align-items: flex-start;
15969
+ }
15970
+ setSpot(spotId, data) {
15971
+ var _a;
15972
+ data.createdAt = Date.now();
15973
+ (_a = this.spots) === null || _a === void 0 ? void 0 : _a.set(spotId, data);
15974
+ this.updateLocalStorage();
15975
+ }
15976
+ removeSpot(spotId) {
15977
+ var _a;
15978
+ (_a = this.spots) === null || _a === void 0 ? void 0 : _a.delete(spotId);
15979
+ this.updateLocalStorage();
15980
+ }
15981
+ getSpot(spotId) {
15982
+ var _a;
15983
+ return (_a = this.spots) === null || _a === void 0 ? void 0 : _a.get(spotId);
15984
+ }
15985
+ getSpots() {
15986
+ if (!this.spots)
15987
+ return undefined;
15988
+ return this.mapToObject(this.spots);
15989
+ }
15990
+ updateLocalStorage() {
15991
+ if (!this.spots)
15992
+ return undefined;
15993
+ const data = this.mapToObject(this.spots);
15994
+ const dataArray = {};
15995
+ for (const [key, value] of Object.entries(data)) {
15996
+ dataArray[key] = this.objectToArray(value);
15997
+ }
15998
+ try {
15999
+ const encryptedData = this.encryptData(JSON.stringify(dataArray));
16000
+ window.localStorage.setItem(LocalStorageService.localStorageKey, encryptedData);
16001
+ }
16002
+ catch (_a) {
16003
+ // If there is an error parsing the data, clear the local storage to prevent any issues
16004
+ this.clearLocalStorage();
16005
+ }
16006
+ }
16007
+ clearLocalStorage() {
16008
+ window.localStorage.removeItem(LocalStorageService.localStorageKey);
16009
+ }
16010
+ removeExpiredSpots() {
16011
+ var _a;
16012
+ const currentTime = Date.now();
16013
+ (_a = this.spots) === null || _a === void 0 ? void 0 : _a.forEach((spot, spotId) => {
16014
+ var _a, _b;
16015
+ if (currentTime - ((_a = spot.createdAt) !== null && _a !== void 0 ? _a : 0) > LocalStorageService.spotExpirationTime) {
16016
+ (_b = this.spots) === null || _b === void 0 ? void 0 : _b.delete(spotId);
16017
+ }
16018
+ });
16019
+ this.updateLocalStorage();
16020
+ }
16021
+ mapToObject(map) {
16022
+ return Object.fromEntries(map);
16023
+ }
16024
+ objectToMap(obj) {
16025
+ return new Map(Object.entries(obj));
16026
+ }
16027
+ objectToArray(obj) {
16028
+ return [obj.placementId, obj.spotId, obj.spotType, obj.events, obj.productIds, obj.createdAt];
16029
+ }
16030
+ arrayToObject(arr) {
16031
+ return {
16032
+ placementId: arr[ENUM_LOCAL_STORAGE_SPOT_ARRAY_INDEX.PLACEMENT_ID],
16033
+ spotId: arr[ENUM_LOCAL_STORAGE_SPOT_ARRAY_INDEX.SPOT_ID],
16034
+ spotType: arr[ENUM_LOCAL_STORAGE_SPOT_ARRAY_INDEX.SPOT_TYPE],
16035
+ events: arr[ENUM_LOCAL_STORAGE_SPOT_ARRAY_INDEX.EVENTS],
16036
+ productIds: arr[ENUM_LOCAL_STORAGE_SPOT_ARRAY_INDEX.PRODUCT_IDS],
16037
+ createdAt: arr[ENUM_LOCAL_STORAGE_SPOT_ARRAY_INDEX.CREATED_AT],
16038
+ };
16039
+ }
16040
+ encryptData(data) {
16041
+ if (!LocalStorageService.encryptData)
16042
+ return data;
16043
+ // For now, we are using base64 encoding to encrypt the data
16044
+ // Later we will use Jose encryption
16045
+ const encryptedData = btoa(data);
16046
+ return encryptedData;
16047
+ }
16048
+ decryptData(data) {
16049
+ if (!LocalStorageService.encryptData)
16050
+ return data;
16051
+ // For now, we are using base64 encoding to encrypt
16052
+ // Later we will use Jose encryption
16053
+ const decryptedData = atob(data);
16054
+ return decryptedData;
16055
+ }
16056
+ }
16057
+ LocalStorageService.localStorageKey = 'lc_rmn';
16058
+ LocalStorageService.spotExpirationTime = 1000 * 60 * 60 * 24 * 7; // 7 days
16059
+ LocalStorageService.encryptData = true;
16060
+
16061
+ /**
16062
+ * PubsubService class
16063
+ * Manages event subscriptions and publications
16064
+ * @template IRmnEventMap A record type defining the structure of events and their data
16065
+ */
16066
+ class PubsubService {
16067
+ constructor() {
16068
+ /**
16069
+ * Object to store subscribers for each event type
16070
+ */
16071
+ this.subscribers = {};
16072
+ }
16073
+ static getInstance() {
16074
+ if (!PubsubService.instance) {
16075
+ PubsubService.instance = new PubsubService();
16076
+ }
16077
+ return PubsubService.instance;
16078
+ }
16079
+ /**
16080
+ * Subscribe to an event
16081
+ * @param eventType - The type of event to subscribe to
16082
+ * @param callback - The function to be called when the event is published
16083
+ * @returns A function to unsubscribe from the event
16084
+ *
16085
+ * @Example:
16086
+ * const unsubscribe = pubSub.subscribe('userLogin', (data) => {
16087
+ * console.log(`User ${data.username} logged in`);
16088
+ * });
16089
+ */
16090
+ subscribe(eventType, callback) {
16091
+ if (!this.subscribers[eventType]) {
16092
+ this.subscribers[eventType] = [];
16093
+ }
16094
+ this.subscribers[eventType].push(callback);
16095
+ // Return an unsubscribe function
16096
+ return () => {
16097
+ this.subscribers[eventType] = this.subscribers[eventType].filter((cb) => cb !== callback);
16098
+ };
16099
+ }
16100
+ /**
16101
+ * Publish an event
16102
+ * @param eventType - The type of event to publish
16103
+ * @param data - The data to be passed to the event subscribers
16104
+ *
16105
+ * @Example:
16106
+ * pubSub.publish('userLogin', { username: 'john_doe', timestamp: Date.now() });
16107
+ */
16108
+ publish(eventType, data) {
16109
+ if (!this.subscribers[eventType]) {
16110
+ return;
16111
+ }
16112
+ this.subscribers[eventType].forEach((callback) => callback(data));
16113
+ }
16114
+ }
16115
+ /**
16116
+ * Usage Example:
16117
+ *
16118
+ * interface IRmnEventMap {
16119
+ * userLogin: { username: string; timestamp: number };
16120
+ * pageView: { url: string; timestamp: number };
16121
+ * }
16122
+ *
16123
+ * const pubSub = new PubsubService<IRmnEventMap>();
16124
+ *
16125
+ * // Subscribe to events
16126
+ * const unsubscribeLogin = pubSub.subscribe('userLogin', (data) => {
16127
+ * console.log(`User ${data.username} logged in at ${new Date(data.timestamp)}`);
16128
+ * });
16129
+ *
16130
+ * pubSub.subscribe('pageView', (data) => {
16131
+ * console.log(`Page ${data.url} viewed at ${new Date(data.timestamp)}`);
16132
+ * });
16133
+ *
16134
+ * // Publish events
16135
+ * pubSub.publish('userLogin', { username: 'john_doe', timestamp: Date.now() });
16136
+ * pubSub.publish('pageView', { url: '/home', timestamp: Date.now() });
16137
+ *
16138
+ * // Unsubscribe from an event
16139
+ * unsubscribeLogin();
16140
+ */
16141
+
16142
+ class ResizeObserverService {
16143
+ constructor({ element, maxSize, minScale }) {
16144
+ this.element = element;
16145
+ if (!element.parentElement) {
16146
+ throw new Error('RmnSdk: Spot element must have a parent container.');
16147
+ }
16148
+ this.container = element.parentElement;
16149
+ this.setDimensions(maxSize, minScale);
16150
+ this.resizeObserver = new ResizeObserver(() => this.updateElementSize());
16151
+ this.resizeObserver.observe(this.container);
16152
+ // Initial size update
16153
+ this.updateElementSize();
16154
+ }
16155
+ setDimensions(maxSize, minScale) {
16156
+ if (minScale <= 0 || minScale > 1) {
16157
+ throw new Error('RmnSdk: Invalid minScale value');
16158
+ }
16159
+ const minSize = {
16160
+ width: maxSize.width * minScale,
16161
+ height: maxSize.height * minScale,
16162
+ };
16163
+ if (maxSize.width <= 0 || maxSize.height <= 0 || minSize.width <= 0 || minSize.height <= 0) {
16164
+ throw new Error('RmnSdk: Invalid dimensions');
16165
+ }
16166
+ if (minSize.width > maxSize.width || minSize.height > maxSize.height) {
16167
+ throw new Error('RmnSdk: Minimum size cannot be greater than maximum size');
16168
+ }
16169
+ this.maxSize = maxSize;
16170
+ this.minSize = minSize;
16171
+ this.aspectRatio = this.maxSize.width / this.maxSize.height;
16172
+ }
16173
+ updateElementSize() {
16174
+ const { clientWidth: containerWidth, clientHeight: containerHeight } = this.container;
16175
+ let newWidth;
16176
+ let newHeight;
16177
+ // First, try to fit the maximum width
16178
+ newWidth = Math.min(containerWidth, this.maxSize.width);
16179
+ newHeight = newWidth / this.aspectRatio;
16180
+ // If the height exceeds the container, adjust based on height
16181
+ if (newWidth > containerWidth) {
16182
+ newWidth = containerWidth;
16183
+ newHeight = containerHeight * this.aspectRatio;
16184
+ }
16185
+ // Ensure we're not going below minimum dimensions
16186
+ newWidth = Math.max(newWidth, this.minSize.width);
16187
+ newHeight = Math.max(newHeight, this.minSize.height);
16188
+ this.element.style.width = `${newWidth}px`;
16189
+ this.element.style.height = `${newHeight}px`;
16190
+ // Calculate the scale percentage
16191
+ const scaleWidth = newWidth / this.maxSize.width;
16192
+ const scaleHeight = newHeight / this.maxSize.height;
16193
+ const scale = Math.min(scaleWidth, scaleHeight);
16194
+ // Dispatch a custom event
16195
+ this.element.dispatchEvent(new CustomEvent('spotSizeChanged', {
16196
+ detail: {
16197
+ width: this.maxSize.width,
16198
+ height: this.maxSize.height,
16199
+ newWidth,
16200
+ newHeight,
16201
+ scale,
16202
+ },
16203
+ }));
16204
+ }
16205
+ disconnect() {
16206
+ this.resizeObserver.disconnect();
16207
+ }
16208
+ }
16209
+
16210
+ const CAROUSEL_COMPONENT_STYLE = ({ width, height, fluid }) => `
16211
+ :host {
16212
+ position: relative;
16213
+ display: inline-block;
16214
+ margin: 0;
16215
+ overflow: hidden;
16216
+ width: ${fluid ? '100%' : `${width}px`};
16217
+ height: ${fluid ? '100%' : `${height}px`};
16218
+ }
16219
+
16220
+ .slides {
16221
+ position: relative;
16222
+ height: 100%;
16223
+ width: 100%;
16224
+ display: flex;
16225
+ transition: transform 0.5s ease-in-out;
16226
+ }
16227
+
16228
+ .slide {
16229
+ flex: 0 0 100%;
16230
+ display: flex;
16231
+ justify-content: center;
16232
+ align-items: center;
16233
+ height: 100%;
16234
+ width: 100%;
16235
+ }
16236
+
16237
+ .slide.active {
16238
+ display: flex;
16239
+ }
16240
+
16241
+ .dots {
16242
+ position: absolute;
16243
+ display: flex;
16244
+ align-items: center;
16245
+ gap: 8px;
16246
+ opacity: var(--opacity, 1);
16247
+ }
16248
+
16249
+ .dots.small .dot {
16250
+ width: 8px;
16251
+ height: 8px;
16252
+ }
16253
+
16254
+ .dots.base .dot {
16255
+ width: 12px;
16256
+ height: 12px;
16257
+ }
16258
+
16259
+ .dots.large .dot {
16260
+ width: 16px;
16261
+ height: 16px;
16262
+ }
16263
+
16264
+ .dots .dot {
16265
+ border-radius: 50%;
16266
+ cursor: pointer;
16267
+ transition: all 0.3s ease;
16268
+ }
16269
+
16270
+ .dots.top-left,
16271
+ .dots.bottom-left {
16272
+ left: 10px;
16273
+ }
16274
+
16275
+ .dots.top-center,
16276
+ .dots.bottom-center {
16277
+ left: 50%;
16278
+ transform: translateX(-50%);
16279
+ }
16280
+
16281
+ .dots.top-right,
16282
+ .dots.bottom-right {
16283
+ right: 10px;
16284
+ }
16285
+
16286
+ .dots.top-left,
16287
+ .dots.top-center,
16288
+ .dots.top-right {
16289
+ top: 10px;
16290
+ }
16291
+
16292
+ .dots.bottom-left,
16293
+ .dots.bottom-center,
16294
+ .dots.bottom-right {
16295
+ bottom: 10px;
16296
+ }
16297
+
16298
+ .dots.middle-left {
16299
+ left: 10px;
16300
+ top: 50%;
16301
+ transform: translateY(-50%);
16302
+ flex-direction: column;
16303
+ }
16304
+
16305
+ .dots.middle-right {
16306
+ right: 10px;
16307
+ top: 50%;
16308
+ transform: translateY(-50%);
16309
+ flex-direction: column;
16310
+ }
16311
+
16312
+ .buttons {
16313
+ opacity: var(--opacity, 1);
16314
+ }
16315
+
16316
+ .buttons button {
16317
+ background-color: #00000080;
16318
+ color: #fff;
16319
+ border: none;
16320
+ padding: 10px;
16321
+ cursor: pointer;
16322
+ transition: background-color 0.3s ease;
16323
+ }
16324
+
16325
+ .buttons.small button {
16326
+ padding: 6px;
16327
+ }
16328
+
16329
+ .buttons.base button {
16330
+ padding: 10px;
16331
+ }
16332
+
16333
+ .buttons.large button {
16334
+ padding: 14px;
16335
+ }
16336
+
16337
+ .buttons button:hover {
16338
+ background-color: #000000b3;
16339
+ }
16340
+
16341
+ .buttons.buttons-separate button {
16342
+ position: absolute;
16343
+ top: 50%;
16344
+ transform: translateY(-50%);
16345
+ }
16346
+
16347
+ .buttons.buttons-separate .prev-button {
16348
+ left: 10px;
16349
+ }
16350
+
16351
+ .buttons.buttons-separate .next-button {
16352
+ right: 10px;
16353
+ }
16354
+
16355
+ .buttons.buttons-together {
16356
+ position: absolute;
16357
+ display: flex;
16358
+ gap: 10px;
16359
+ }
16360
+
16361
+ .buttons.buttons-together.top-left,
16362
+ .buttons.buttons-together.bottom-left {
16363
+ left: 10px;
16364
+ }
16365
+
16366
+ .buttons.buttons-together.top-center,
16367
+ .buttons.buttons-together.bottom-center {
16368
+ left: 50%;
16369
+ transform: translateX(-50%);
16370
+ }
16371
+
16372
+ .buttons.buttons-together.top-right,
16373
+ .buttons.buttons-together.bottom-right {
16374
+ right: 10px;
16375
+ }
16376
+
16377
+ .buttons.buttons-together.top-left,
16378
+ .buttons.buttons-together.top-center,
16379
+ .buttons.buttons-together.top-right {
16380
+ top: 10px;
16381
+ }
16382
+
16383
+ .buttons.buttons-together.bottom-left,
16384
+ .buttons.buttons-together.bottom-center,
16385
+ .buttons.buttons-together.bottom-right {
16386
+ bottom: 10px;
16387
+ }
16388
+
16389
+ .buttons.buttons-together.middle-left,
16390
+ .buttons.buttons-together.middle-right {
16391
+ top: 50%;
16392
+ transform: translateY(-50%);
16393
+ flex-direction: column;
16394
+ }
16395
+
16396
+ .buttons.buttons-together.middle-left {
16397
+ left: 10px;
16398
+ }
16399
+
16400
+ .buttons.buttons-together.middle-right {
16401
+ right: 10px;
16402
+ }
16403
+
16404
+ @media (max-width: 768px) {
16405
+ .buttons button {
16406
+ padding: 8px 12px;
16407
+ font-size: 14px;
16408
+ }
16409
+ }
16410
+ `;
16411
+
16412
+ let CarouselElement;
16413
+ if (typeof window !== 'undefined' && typeof window.customElements !== 'undefined') {
16414
+ class CustomCarouselElement extends HTMLElement {
16415
+ constructor() {
16416
+ super();
16417
+ this.currentSlide = 0;
16418
+ this.dotElements = [];
16419
+ this.prevButton = null;
16420
+ this.nextButton = null;
16421
+ this.autoplayInterval = null;
16422
+ this.useDots = false;
16423
+ this.useButtons = false;
16424
+ this.originalFontSizes = new Map();
16425
+ this.attachShadow({ mode: 'open' });
16426
+ }
16427
+ connectedCallback() {
16428
+ this.initializeOptions();
16429
+ this.setupResizeObserver();
16430
+ this.render();
16431
+ this.setupCarousel();
16432
+ }
16433
+ disconnectedCallback() {
16434
+ var _a;
16435
+ this.stopAutoplay();
16436
+ (_a = this.resizeObserver) === null || _a === void 0 ? void 0 : _a.disconnect();
16437
+ }
16438
+ initializeOptions() {
16439
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
16440
+ 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';
16441
+ 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';
16442
+ this.autoplay = (_f = (_e = this.data) === null || _e === void 0 ? void 0 : _e.autoplay) !== null && _f !== void 0 ? _f : true;
16443
+ this.interval = (_h = (_g = this.data) === null || _g === void 0 ? void 0 : _g.interval) !== null && _h !== void 0 ? _h : CustomCarouselElement.defaultInterval;
16444
+ this.dotsOptions = {
16445
+ position: 'bottom-center',
16446
+ color: '#d9d9d9',
16447
+ activeColor: '#b5914a',
16448
+ size: 'base',
16449
+ opacity: 1,
16450
+ ...(typeof ((_j = this.data) === null || _j === void 0 ? void 0 : _j.useDots) === 'object' ? this.data.useDots : {}),
16451
+ };
16452
+ this.buttonsOptions = {
16453
+ together: false,
16454
+ position: 'middle-sides',
16455
+ textColor: '#000000',
16456
+ backgroundColor: '#ffffff',
16457
+ borderRadius: '50%',
16458
+ prev: 'Prev',
16459
+ next: 'Next',
16460
+ size: 'base',
16461
+ opacity: 1,
16462
+ ...(typeof ((_k = this.data) === null || _k === void 0 ? void 0 : _k.useButtons) === 'object' ? this.data.useButtons : {}),
16463
+ };
16464
+ this.validateOptions();
16465
+ }
16466
+ setupResizeObserver() {
16467
+ if (this.data && !this.data.fluid) {
16468
+ this.resizeObserver = new ResizeObserverService({
16469
+ element: this,
16470
+ maxSize: {
16471
+ width: this.data.width,
16472
+ height: this.data.height,
16473
+ },
16474
+ minScale: this.data.minScale,
16475
+ });
16476
+ this.addEventListener('spotSizeChanged', this.handleCarouselSizeChanged.bind(this));
16477
+ }
16478
+ }
16479
+ handleCarouselSizeChanged(event) {
16480
+ const isRBSpot = 'rbHeroadaksda'.startsWith('rb');
16481
+ if (!isRBSpot) {
16482
+ // Adjust text elements font size based on the scale factor
16483
+ this.adjustFontSize(event.detail.scale);
16484
+ }
16485
+ }
16486
+ adjustFontSize(elementScale) {
16487
+ var _a;
16488
+ const scaleFactor = calculateScaleFactor(elementScale);
16489
+ // Find all text elements within the shadow root
16490
+ const elements = (_a = this.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelectorAll('h1, h2, h3, h4, p, span');
16491
+ elements === null || elements === void 0 ? void 0 : elements.forEach((element) => {
16492
+ if (element instanceof HTMLElement) {
16493
+ if (!this.originalFontSizes.has(element)) {
16494
+ const originalSize = parseFloat(window.getComputedStyle(element).fontSize);
16495
+ this.originalFontSizes.set(element, originalSize);
16496
+ }
16497
+ const originalSize = this.originalFontSizes.get(element);
16498
+ const newFontSize = originalSize * scaleFactor;
16499
+ element.style.fontSize = `${newFontSize}px`;
16500
+ }
16501
+ });
16502
+ }
16503
+ render() {
16504
+ var _a;
16505
+ if (!this.shadowRoot)
16506
+ return;
16507
+ const style = document.createElement('style');
16508
+ style.textContent = CAROUSEL_COMPONENT_STYLE(this.data);
16509
+ this.shadowRoot.appendChild(style);
16510
+ const slides = this.renderSlides();
16511
+ this.shadowRoot.appendChild(slides);
16512
+ this.slidesContainer = (_a = this.shadowRoot.querySelector('.slides')) !== null && _a !== void 0 ? _a : undefined;
16513
+ if (this.useDots) {
16514
+ const dots = this.renderDots();
16515
+ if (dots)
16516
+ this.shadowRoot.appendChild(dots);
16517
+ }
16518
+ if (this.useButtons) {
16519
+ const buttons = this.renderButtons();
16520
+ if (buttons)
16521
+ this.shadowRoot.appendChild(buttons);
16522
+ }
16523
+ }
16524
+ setupCarousel() {
16525
+ this.setupDots();
16526
+ this.setupButtons();
16527
+ if (this.autoplay) {
16528
+ this.startAutoplay();
16529
+ }
16530
+ this.updateCarousel();
16531
+ }
16532
+ renderSlides() {
16533
+ const slidesContainer = document.createElement('div');
16534
+ slidesContainer.className = 'slides';
16535
+ this.slides.forEach((slide) => {
16536
+ const slideElement = document.createElement('div');
16537
+ slideElement.className = 'slide';
16538
+ if (slide instanceof HTMLElement) {
16539
+ slideElement.appendChild(slide);
16540
+ }
16541
+ slidesContainer.appendChild(slideElement);
16542
+ });
16543
+ return slidesContainer;
16544
+ }
16545
+ renderDots() {
16546
+ const dotsContainer = document.createElement('div');
16547
+ dotsContainer.className = `dots ${this.dotsOptions.position} ${this.dotsOptions.size}`;
16548
+ dotsContainer.style.cssText = `--opacity: ${this.dotsOptions.opacity}`;
16549
+ this.slides.forEach((_, index) => {
16550
+ const dot = document.createElement('span');
16551
+ dot.className = `dot ${index === this.currentSlide ? 'active' : ''}`;
16552
+ dot.style.backgroundColor = this.dotsOptions.color;
16553
+ dotsContainer.appendChild(dot);
16554
+ });
16555
+ return dotsContainer;
16556
+ }
16557
+ renderButtons() {
16558
+ const buttonsContainer = document.createElement('div');
16559
+ const buttonsClass = this.buttonsOptions.together
16560
+ ? `buttons-together ${this.buttonsOptions.position}`
16561
+ : 'buttons-separate';
16562
+ buttonsContainer.className = `buttons ${buttonsClass} ${this.buttonsOptions.size}`;
16563
+ buttonsContainer.style.cssText = `--opacity: ${this.buttonsOptions.opacity}`;
16564
+ this.prevButton = this.createButton('prev-button', this.buttonsOptions.prev);
16565
+ this.nextButton = this.createButton('next-button', this.buttonsOptions.next);
16566
+ buttonsContainer.appendChild(this.prevButton);
16567
+ buttonsContainer.appendChild(this.nextButton);
16568
+ return buttonsContainer;
16569
+ }
16570
+ createButton(className, text) {
16571
+ const button = document.createElement('button');
16572
+ button.className = className;
16573
+ button.textContent = text;
16574
+ button.style.color = this.buttonsOptions.textColor;
16575
+ button.style.backgroundColor = this.buttonsOptions.backgroundColor;
16576
+ button.style.borderRadius = this.buttonsOptions.borderRadius;
16577
+ return button;
16578
+ }
16579
+ setupDots() {
16580
+ if (!this.shadowRoot || !this.useDots)
16581
+ return;
16582
+ this.dotElements = Array.from(this.shadowRoot.querySelectorAll('.dot'));
16583
+ this.dotElements.forEach((dot, index) => {
16584
+ dot.addEventListener('click', () => {
16585
+ this.goToSlide(index);
16586
+ this.resetAutoplay();
16587
+ });
16588
+ });
16589
+ }
16590
+ setupButtons() {
16591
+ var _a, _b;
16592
+ if (!this.useButtons)
16593
+ return;
16594
+ (_a = this.prevButton) === null || _a === void 0 ? void 0 : _a.addEventListener('click', () => {
16595
+ this.prevSlide();
16596
+ this.resetAutoplay();
16597
+ });
16598
+ (_b = this.nextButton) === null || _b === void 0 ? void 0 : _b.addEventListener('click', () => {
16599
+ this.nextSlide();
16600
+ this.resetAutoplay();
16601
+ });
16602
+ }
16603
+ nextSlide() {
16604
+ this.goToSlide((this.currentSlide + 1) % this.slides.length);
16605
+ }
16606
+ prevSlide() {
16607
+ this.goToSlide((this.currentSlide - 1 + this.slides.length) % this.slides.length);
16608
+ }
16609
+ goToSlide(index) {
16610
+ this.currentSlide = index;
16611
+ this.updateCarousel();
16612
+ }
16613
+ updateCarousel() {
16614
+ if (!this.slidesContainer)
16615
+ return;
16616
+ // Calculate the translation distance based on current slide
16617
+ const translateX = -this.currentSlide * 100;
16618
+ this.slidesContainer.style.transform = `translateX(${translateX}%)`;
16619
+ this.updateDots();
16620
+ }
16621
+ updateDots() {
16622
+ if (!this.useDots)
16623
+ return;
16624
+ this.dotElements.forEach((dot, index) => {
16625
+ const isActive = index === this.currentSlide;
16626
+ dot.classList.toggle('active', isActive);
16627
+ dot.style.backgroundColor = isActive
16628
+ ? this.dotsOptions.activeColor
16629
+ : this.dotsOptions.color;
16630
+ });
16631
+ }
16632
+ startAutoplay() {
16633
+ this.autoplayInterval = window.setInterval(() => this.nextSlide(), this.interval);
16634
+ }
16635
+ stopAutoplay() {
16636
+ if (this.autoplayInterval !== null) {
16637
+ window.clearInterval(this.autoplayInterval);
16638
+ this.autoplayInterval = null;
16639
+ }
16640
+ }
16641
+ resetAutoplay() {
16642
+ if (this.autoplay) {
16643
+ this.stopAutoplay();
16644
+ this.startAutoplay();
16645
+ }
16646
+ }
16647
+ validateOptions() {
16648
+ this.validatePosition(this.dotsOptions.position, 'dotsPosition', 'bottom-center');
16649
+ this.validateButtonsPosition();
16650
+ }
16651
+ validatePosition(position, optionName, defaultValue) {
16652
+ if (!CustomCarouselElement.validPositions.includes(position)) {
16653
+ console.warn(`Invalid ${optionName}: ${position}. Defaulting to '${defaultValue}'.`);
16654
+ if (optionName === 'dotsPosition') {
16655
+ this.dotsOptions.position = defaultValue;
16656
+ }
16657
+ else if (optionName === 'buttonsPosition') {
16658
+ this.buttonsOptions.position = defaultValue;
16659
+ }
16660
+ }
16661
+ }
16662
+ validateButtonsPosition() {
16663
+ if (this.useButtons) {
16664
+ if (this.buttonsOptions.together) {
16665
+ this.validatePosition(this.buttonsOptions.position, 'buttonsPosition', 'bottom-center');
16666
+ }
16667
+ else if (this.buttonsOptions.position !== 'middle-sides') {
16668
+ console.warn(`Invalid buttonsPosition: ${this.buttonsOptions.position}. When buttons are not together, only 'middle-sides' is allowed. Defaulting to 'middle-sides'.`);
16669
+ this.buttonsOptions.position = 'middle-sides';
16670
+ }
16671
+ }
16672
+ }
16673
+ }
16674
+ CustomCarouselElement.defaultInterval = 5000;
16675
+ CustomCarouselElement.validPositions = [
16676
+ 'top-left',
16677
+ 'top-center',
16678
+ 'top-right',
16679
+ 'bottom-left',
16680
+ 'bottom-center',
16681
+ 'bottom-right',
16682
+ 'middle-left',
16683
+ 'middle-right',
16684
+ 'middle-sides',
16685
+ ];
16686
+ CarouselElement = CustomCarouselElement;
16687
+ }
16688
+
16689
+ let SpotElement;
16690
+ if (typeof window !== 'undefined' && typeof window.customElements !== 'undefined') {
16691
+ class CustomSpotElement extends HTMLElement {
16692
+ constructor() {
16693
+ super();
16694
+ this.originalFontSizes = new Map();
16695
+ this.attachShadow({ mode: 'open' });
16696
+ }
16697
+ connectedCallback() {
16698
+ this.setupResizeObserver();
16699
+ this.render();
16700
+ }
16701
+ disconnectedCallback() {
16702
+ var _a;
16703
+ this.removeEventListener('spotSizeChanged', this.handleSpotSizeChanged);
16704
+ (_a = this.resizeObserver) === null || _a === void 0 ? void 0 : _a.disconnect();
16705
+ }
16706
+ /**
16707
+ * Setup observers for the spot element
16708
+ * #########################################################
16709
+ */
16710
+ setupResizeObserver() {
16711
+ if (this.data && !this.data.fluid) {
16712
+ this.resizeObserver = new ResizeObserverService({
16713
+ element: this,
16714
+ maxSize: {
16715
+ width: this.data.width,
16716
+ height: this.data.height,
16717
+ },
16718
+ minScale: this.data.minScale,
16719
+ });
16720
+ this.addEventListener('spotSizeChanged', this.handleSpotSizeChanged.bind(this));
16721
+ }
16722
+ }
16723
+ /**
16724
+ * Observer additional event handlers
16725
+ * #########################################################
16726
+ */
16727
+ handleSpotSizeChanged(event) {
16728
+ var _a, _b, _c;
16729
+ 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;
16730
+ if (!isRBSpot) {
16731
+ // Adjust text elements font size based on the scale factor
16732
+ this.adjustFontSize(event.detail.scale);
16733
+ }
16734
+ }
16735
+ adjustFontSize(elementScale) {
16736
+ var _a;
16737
+ const scaleFactor = calculateScaleFactor(elementScale);
16738
+ // Find all text elements within the shadow root
16739
+ const elements = (_a = this.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelectorAll('h1, h2, h3, h4, p, span');
16740
+ elements === null || elements === void 0 ? void 0 : elements.forEach((element) => {
16741
+ if (element instanceof HTMLElement) {
16742
+ if (!this.originalFontSizes.has(element)) {
16743
+ const originalSize = parseFloat(window.getComputedStyle(element).fontSize);
16744
+ this.originalFontSizes.set(element, originalSize);
16745
+ }
16746
+ const originalSize = this.originalFontSizes.get(element);
16747
+ const newFontSize = originalSize * scaleFactor;
16748
+ element.style.fontSize = `${newFontSize}px`;
16749
+ }
16750
+ });
16751
+ }
16752
+ /**
16753
+ * Spot element rendering
16754
+ * #########################################################
16755
+ */
16756
+ render() {
16757
+ if (!this.shadowRoot || !this.data || !this.content)
16758
+ return;
16759
+ const style = this.getTemplateStyle(this.data.width, this.data.height);
16760
+ if (this.content instanceof HTMLElement) {
16761
+ this.shadowRoot.replaceChildren(style, this.content);
16762
+ }
16763
+ }
16764
+ getTemplateStyle(width, height) {
16765
+ const style = document.createElement('style');
16766
+ style.textContent = `
16767
+ :host {
16768
+ display: block;
16769
+ position: relative;
16770
+ box-sizing: border-box;
16771
+ overflow: hidden;
16772
+ width: ${this.data.fluid ? '100%' : `${width}px`};
16773
+ height: ${this.data.fluid ? '100%' : `${height}px`};
16774
+ }
16775
+ `;
16776
+ return style;
16777
+ }
16778
+ }
16779
+ SpotElement = CustomSpotElement;
16780
+ }
16781
+
16782
+ class ElementService {
16783
+ static getInstance() {
16784
+ return SingletonManager.getInstance('ElementService', () => new ElementService());
16785
+ }
16786
+ /**
16787
+ * Creates the html element based on the provided data, content and configs using shadow dom.
16788
+ *
16789
+ * This method is only available in browser environments.
16790
+ *
16791
+ * @param {ICreateSpotElementParams} params - The parameters to create the final element.
16792
+ *
16793
+ * @return {HTMLElement | null} - The html element or null if the browser environment is not available.
16794
+ */
16795
+ createSpotElement({ content, config }) {
16796
+ var _a, _b;
16797
+ if (!this.ensureBrowserEnvironmentAndDefineElement()) {
16798
+ return null;
16799
+ }
16800
+ const spot = document.createElement(SPOT_ELEMENT_TAG);
16801
+ spot.setAttribute('type', (_a = config === null || config === void 0 ? void 0 : config.spot) !== null && _a !== void 0 ? _a : '');
16802
+ spot.data = {
16803
+ spot: config === null || config === void 0 ? void 0 : config.spot,
16804
+ fluid: (_b = config === null || config === void 0 ? void 0 : config.fluid) !== null && _b !== void 0 ? _b : false,
16805
+ ...config,
16806
+ };
16807
+ spot.content = content;
16808
+ return spot;
16809
+ }
16810
+ /**
16811
+ * Creates the carousel html element based on the provided slides and configs using shadow dom.
16812
+ *
16813
+ * This method is only available in browser environments.
16814
+ *
16815
+ * @param {ICreateCarouselElementParams} params - The parameters to create the final element.
16816
+ *
16817
+ * @return {HTMLElement | null} - The html element or null if the browser environment is not available.
16818
+ */
16819
+ createCarouselElement({ slides, config, }) {
16820
+ if (!this.ensureBrowserEnvironmentAndDefineElement()) {
16821
+ return null;
16822
+ }
16823
+ const carousel = document.createElement(CAROUSEL_ELEMENT_TAG);
16824
+ carousel.data = {
16825
+ spot: config === null || config === void 0 ? void 0 : config.spot,
16826
+ fluid: false,
16827
+ ...config,
16828
+ };
16829
+ carousel.slides = slides;
16830
+ return carousel;
16831
+ }
16832
+ /**
16833
+ * Overrides the spot colors with the provided colors.
16834
+ *
16835
+ * @param {ISpot} spot - The spot data.
16836
+ * @param {ISpotColors} colors - The colors to override.
16837
+ *
16838
+ * @return {ISpot} - The spot data with the colors overridden.
16839
+ */
16840
+ overrideSpotColors(spot, colors) {
16841
+ if (!colors)
16842
+ return spot;
16843
+ const { textColor, backgroundColor, ctaTextColor, ctaBorderColor } = colors;
16844
+ return {
16845
+ ...spot,
16846
+ textColor: textColor !== null && textColor !== void 0 ? textColor : spot.textColor,
16847
+ backgroundColor: backgroundColor !== null && backgroundColor !== void 0 ? backgroundColor : spot.backgroundColor,
16848
+ ctaTextColor: ctaTextColor !== null && ctaTextColor !== void 0 ? ctaTextColor : spot.ctaTextColor,
16849
+ ctaBorderColor: ctaBorderColor !== null && ctaBorderColor !== void 0 ? ctaBorderColor : spot.ctaBorderColor,
16850
+ };
16851
+ }
16852
+ /**
16853
+ * @returns {boolean} - True if the browser environment is available and the element is defined.
16854
+ */
16855
+ ensureBrowserEnvironmentAndDefineElement() {
16856
+ if (typeof window === 'undefined' || typeof document === 'undefined') {
16857
+ console.warn('LiquidCommerce Rmn Sdk: Methods which create elements are only available in browser environments.');
16858
+ return false;
16859
+ }
16860
+ if (!window.customElements.get(SPOT_ELEMENT_TAG)) {
16861
+ window.customElements.define(SPOT_ELEMENT_TAG, SpotElement);
16862
+ }
16863
+ if (!window.customElements.get(CAROUSEL_ELEMENT_TAG)) {
16864
+ window.customElements.define(CAROUSEL_ELEMENT_TAG, CarouselElement);
16865
+ }
16866
+ return true;
16867
+ }
16868
+ }
16869
+
16870
+ class UniqueIdGenerator {
16871
+ /**
16872
+ * Initialize the generator with a node ID
16873
+ * @param nodeId Number between 0-1023 to identify this instance
16874
+ */
16875
+ static initialize(nodeId = Math.floor(Math.random() * 1024)) {
16876
+ if (nodeId < 0 || nodeId >= 1 << this.nodeBits) {
16877
+ throw new Error(`Node ID must be between 0 and ${(1 << this.nodeBits) - 1}`);
16878
+ }
16879
+ this.nodeId = nodeId;
16880
+ }
16881
+ /**
16882
+ * Convert a number to base32 string with specified length
16883
+ */
16884
+ static toBase32(num, length) {
16885
+ let result = '';
16886
+ while (num > 0) {
16887
+ result = this.base32Chars[Number(num % BigInt(32))] + result;
16888
+ // @ts-expect-error - TS doesn't support bigint division
16889
+ num = num / 32n;
16890
+ }
16891
+ return result.padStart(length, '0');
16892
+ }
16893
+ /**
16894
+ * Generate a cryptographically secure random number
16895
+ */
16896
+ static getSecureRandom() {
16897
+ if (typeof crypto !== 'undefined') {
16898
+ const buffer = new Uint32Array(1);
16899
+ crypto.getRandomValues(buffer);
16900
+ return buffer[0];
16901
+ }
16902
+ return Math.floor(Math.random() * 0xffffffff);
16903
+ }
16904
+ /**
16905
+ * Wait until next millisecond
16906
+ */
16907
+ static waitNextMillis(lastTimestamp) {
16908
+ let timestamp = Date.now();
16909
+ while (timestamp <= lastTimestamp) {
16910
+ timestamp = Date.now();
16911
+ }
16912
+ return timestamp;
16913
+ }
16914
+ /**
16915
+ * Generates a highly unique ID with the following format:
16916
+ * TTTTTTTTTTCCCCNNNNNRRRR
16917
+ * T: Timestamp (10 chars)
16918
+ * C: Counter (4 chars)
16919
+ * N: Node ID (5 chars)
16920
+ * R: Random (4 chars)
16921
+ *
16922
+ * Total length: 23 characters, always uppercase alphanumeric
16923
+ */
16924
+ static generate() {
16925
+ if (this.nodeId === undefined) {
16926
+ this.initialize();
16927
+ }
16928
+ let timestamp = Date.now() - this.epoch;
16929
+ // Handle clock moving backwards or same millisecond
16930
+ if (timestamp < this.lastTimestamp) {
16931
+ throw new Error('Clock moved backwards. Refusing to generate ID.');
16932
+ }
16933
+ if (timestamp === this.lastTimestamp) {
16934
+ this.sequence = (this.sequence + 1) & ((1 << this.sequenceBits) - 1);
16935
+ if (this.sequence === 0) {
16936
+ timestamp = this.waitNextMillis(this.lastTimestamp);
16937
+ }
16938
+ }
16939
+ else {
16940
+ this.sequence = 0;
16941
+ }
16942
+ this.lastTimestamp = timestamp;
16943
+ // Generate random component
16944
+ const random = this.getSecureRandom() & 0xffff; // 16 bits of randomness
16945
+ // Combine all components into a BigInt
16946
+ // const id =
16947
+ // (BigInt(timestamp) << BigInt(this.nodeBits + this.sequenceBits + 16)) |
16948
+ // (BigInt(this.nodeId) << BigInt(this.sequenceBits + 16)) |
16949
+ // (BigInt(this.sequence) << BigInt(16)) |
16950
+ // BigInt(random);
16951
+ // Convert to base32 representation
16952
+ const timeComponent = this.toBase32(BigInt(timestamp), 10);
16953
+ const counterComponent = this.toBase32(BigInt(this.sequence), 4);
16954
+ const nodeComponent = this.toBase32(BigInt(this.nodeId), 5);
16955
+ const randomComponent = this.toBase32(BigInt(random), 4);
16956
+ return `${timeComponent}${counterComponent}${nodeComponent}${randomComponent}`;
16957
+ }
16958
+ /**
16959
+ * Validates if a string matches the expected ID format
16960
+ */
16961
+ static isValid(id) {
16962
+ if (!/^[0-9A-HJ-NP-Z]{23}$/.test(id))
16963
+ return false;
16964
+ try {
16965
+ const timeComponent = id.slice(0, 10);
16966
+ const timestamp = this.decodeBase32(timeComponent);
16967
+ const now = Date.now() - this.epoch;
16968
+ return timestamp >= 0 && timestamp <= now;
16969
+ }
16970
+ catch (_a) {
16971
+ return false;
16972
+ }
16973
+ }
16974
+ /**
16975
+ * Decode base32 string to number
16976
+ */
16977
+ static decodeBase32(str) {
16978
+ let result = 0;
16979
+ for (const char of str) {
16980
+ result = result * 32 + this.base32Chars.indexOf(char);
16981
+ }
16982
+ return result;
16983
+ }
16984
+ /**
16985
+ * Extract timestamp from ID
16986
+ */
16987
+ static getTimestamp(id) {
16988
+ if (!this.isValid(id))
16989
+ throw new Error('Invalid ID format');
16990
+ const timeComponent = id.slice(0, 10);
16991
+ const timestamp = this.decodeBase32(timeComponent);
16992
+ return new Date(timestamp + this.epoch);
16993
+ }
16994
+ }
16995
+ // Constants for bit manipulation
16996
+ UniqueIdGenerator.epoch = 1577836800000; // 2020-01-01 as epoch
16997
+ UniqueIdGenerator.nodeBits = 10;
16998
+ UniqueIdGenerator.sequenceBits = 12;
16999
+ // Instance variables
17000
+ UniqueIdGenerator.lastTimestamp = -1;
17001
+ UniqueIdGenerator.sequence = 0;
17002
+ // Character set for base32 encoding (excluding similar looking characters)
17003
+ UniqueIdGenerator.base32Chars = '0123456789ABCDEFGHJKMNPQRSTVWXYZ';
17004
+
17005
+ function convertHexToRgba(hex, opacity = 1) {
17006
+ // Remove # if present
17007
+ const cleanHex = hex.replace('#', '');
17008
+ // Convert hex to RGB
17009
+ const r = parseInt(cleanHex.substring(0, 2), 16);
17010
+ const g = parseInt(cleanHex.substring(2, 4), 16);
17011
+ const b = parseInt(cleanHex.substring(4, 6), 16);
17012
+ // Return rgba string
17013
+ return `rgba(${r}, ${g}, ${b}, ${opacity})`;
17014
+ }
17015
+ function generateGradientColor(overlay, fallback = '') {
17016
+ if (!overlay) {
17017
+ return fallback;
17018
+ }
17019
+ const OVERLAY_SIZE = {
17020
+ small: 10,
17021
+ base: 30,
17022
+ large: 50,
17023
+ };
17024
+ const OVERLAY_OPACITY = {
17025
+ light: 0.1,
17026
+ medium: 0.4,
17027
+ dark: 0.6,
17028
+ };
17029
+ const { size, opacity, color } = overlay;
17030
+ const goTo = OVERLAY_SIZE[size];
17031
+ const overlayOpacity = OVERLAY_OPACITY[opacity];
17032
+ const fullColor = convertHexToRgba(color, 1);
17033
+ const transparentColor = convertHexToRgba(color, 0);
17034
+ const gradientColor = convertHexToRgba(color, overlayOpacity);
17035
+ return `${fullColor} 0%, ${gradientColor} ${goTo}%, ${transparentColor} 100%`;
17036
+ }
17037
+ function spotHtmlStringToElement(htmlString) {
17038
+ const spot = document.createElement('div');
17039
+ spot.className = 'spot';
17040
+ spot.innerHTML = htmlString;
17041
+ Object.assign(spot.style, {
17042
+ display: 'block',
17043
+ width: '100%',
17044
+ height: '100%',
17045
+ margin: '0',
17046
+ padding: '0',
17047
+ containerType: 'inline-size',
17048
+ position: 'relative',
17049
+ });
17050
+ return spot;
17051
+ }
17052
+
17053
+ const STYLES$i = ({ primaryImage, secondaryImage }) => `
17054
+ <style>
17055
+ .content {
17056
+ width: 100%;
17057
+ height: 100%;
17058
+ display: flex;
17059
+ flex-direction: row;
17060
+ background-color: #FFFFFF;
17061
+ gap: 0.5cqw;
17062
+ cursor: pointer;
17063
+ color: inherit;
17064
+ }
17065
+ .big-image {
17066
+ width: 65%;
17067
+ height: 100%;
17068
+ background-image: url("${primaryImage}");
17069
+ background-position: center;
17070
+ background-repeat: no-repeat;
17071
+ background-size: cover;
17072
+ }
17073
+ .main {
17074
+ width: 35%;
17075
+ height: 100%;
17076
+ display: flex;
17077
+ flex-direction: column;
17078
+ gap: 0.5cqw;
17079
+ }
17080
+ .small-image {
17081
+ width: 100%;
17082
+ height: 50%;
17083
+ background-image: url("${secondaryImage}");
17084
+ background-position: center;
17085
+ background-repeat: no-repeat;
17086
+ background-size: cover;
17087
+ }
17088
+ .text {
17089
+ background: #E8E6DE;
17090
+ text-align: center;
17091
+ display: flex;
17092
+ flex-direction: column;
17093
+ justify-content: center;
17094
+ align-items: center;
17095
+ height: 50%;
17096
+ width: 100%;
17097
+ padding: 5% 10%;
17098
+ box-sizing: border-box;
17099
+ font-family: "Source Sans 3", sans-serif;
17100
+ }
17101
+ .header {
17102
+ font-size: 2.2cqw;
17103
+ color: #000000;
17104
+ font-style: normal;
17105
+ font-weight: 400;
17106
+ font-family: "Cormorant", serif;
17107
+ margin: 0;
17108
+ }
17109
+ .description {
17110
+ font-size: 1.2cqw;
17111
+ color: #000000;
17112
+ font-style: normal;
17113
+ font-weight: 400;
17114
+ margin: 1cqh 0;
17115
+ }
17116
+ .button {
17117
+ font-size: 1cqw;
17118
+ color: #000000;
17119
+ background-color: transparent;
17120
+ font-style: normal;
17121
+ font-weight: 700;
17122
+ text-decoration: underline;
17123
+ text-transform: uppercase;
17124
+ border: none;
17125
+ cursor: pointer;
17126
+ padding: 0;
17127
+ transition: opacity 0.3s ease;
17128
+ }
17129
+ .content:hover .button {
17130
+ opacity: 0.7;
17131
+ }
17132
+ @container (max-width: 600px) {
17133
+ .header {
17134
+ font-size: 3cqw;
17135
+ }
17136
+ .description {
17137
+ font-size: 1.6cqw;
17138
+ margin: 0;
17139
+ }
17140
+ .button {
17141
+ font-size: 1.4cqw;
17142
+ }
17143
+ }
17144
+ </style>
17145
+ `;
17146
+ function billboardV1Template(spot) {
17147
+ return `
17148
+ ${GFONT_PRECONNECT}
17149
+ ${GFONT_SOURCE_SANS_3}
17150
+ ${GFONT_CORMORANT}
17151
+ ${STYLES$i(spot)}
17152
+ <div class="content">
17153
+ <div class="big-image"></div>
17154
+ <div class="main">
17155
+ <div class="small-image"></div>
17156
+ <div class="text">
17157
+ <h2 class="header">${spot.header}</h2>
17158
+ <p class="description">${spot.description}</p>
17159
+ <span class="button">${spot.ctaText}</span>
17160
+ </div>
17161
+ </div>
17162
+ </div>
17163
+ `;
17164
+ }
17165
+
17166
+ const STYLES$h = ({ primaryImage }) => `
17167
+ <style>
17168
+ .content {
17169
+ display: flex;
17170
+ flex-direction: column;
17171
+ justify-content: flex-end;
17172
+ align-items: flex-start;
15325
17173
  width: 100%;
15326
17174
  height: 100%;
15327
17175
  font-family: "Source Sans 3", sans-serif;
@@ -16019,185 +17867,202 @@ function wideSkyscraperV1Template(spot) {
16019
17867
  `;
16020
17868
  }
16021
17869
 
16022
- const STYLES$7 = ({ primaryImage, mobilePrimaryImage = primaryImage }) => `
16023
- <style>
16024
- :host {
16025
- min-width: 320px;
16026
- min-height: 223px;
16027
- }
16028
- .content {
16029
- display: block;
16030
- width: 100%;
16031
- height: 100%;
16032
- background-image: url("${mobilePrimaryImage}");
16033
- background-size: cover;
16034
- background-repeat: no-repeat;
16035
- background-position: center;
16036
- cursor: pointer;
16037
- }
16038
- @media (min-width: 640px) {
16039
- .content {
16040
- background-image: url("${primaryImage}");
16041
- }
17870
+ const STYLES$7 = ({ primaryImage, mobilePrimaryImage = primaryImage }, { prefix }) => `
17871
+ <style>
17872
+ .${prefix} {
17873
+ display: block;
17874
+ width: 100%;
17875
+ height: 100%;
17876
+ background-image: url("${mobilePrimaryImage}");
17877
+ background-size: cover;
17878
+ background-repeat: no-repeat;
17879
+ background-position: center;
17880
+ cursor: pointer;
17881
+ container-type: inline-size;
17882
+ }
17883
+
17884
+ @container (min-width: 640px) {
17885
+ .${prefix} {
17886
+ background-image: url("${primaryImage}");
16042
17887
  }
16043
- </style>
17888
+ }
17889
+ </style>
16044
17890
  `;
16045
- function rbCollectionBannerWithoutTextBlockTemplate(spot) {
17891
+ function rbCollectionBannerWithoutTextBlockTemplate(spot, config) {
17892
+ const { prefix = '' } = config;
16046
17893
  return `
16047
- ${STYLES$7(spot)}
16048
- <div class="content"></div>
17894
+ ${STYLES$7(spot, config)}
17895
+ <div class="${prefix}"></div>
16049
17896
  `;
16050
17897
  }
16051
17898
 
16052
- const STYLES$6 = ({ textColor = '#ffffff', ctaTextColor = textColor, ctaBorderColor = ctaTextColor, primaryImage, mobilePrimaryImage = primaryImage, }) => `
16053
- <style>
16054
- :host {
16055
- min-width: 320px;
16056
- min-height: 388px;
16057
- }
16058
- .content {
16059
- display: flex;
16060
- flex-direction: column;
16061
- justify-content: flex-end;
16062
- align-items: flex-start;
16063
- width: 100%;
16064
- height: 100%;
16065
- background-image: url("${mobilePrimaryImage}");
16066
- background-size: cover;
16067
- background-repeat: no-repeat;
16068
- background-position: center;
16069
- padding: 3%;
16070
- box-sizing: border-box;
16071
- color: ${textColor};
16072
- cursor: pointer;
16073
- }
16074
- .text {
16075
- display: flex;
16076
- flex-direction: column;
16077
- justify-content: flex-start;
16078
- align-items: flex-start;
16079
- width: 100%;
16080
- height: fit-content;
16081
- gap: 5px;
16082
- }
16083
- .header {
16084
- font-size: 18px;
16085
- margin: 0;
16086
- font-family: "Cormorant";
16087
- font-style: normal;
16088
- font-weight: 300;
16089
- line-height: normal;
16090
- }
16091
- .description {
16092
- font-size: 10px;
16093
- font-family: "Source Sans 3", system-ui;
16094
- font-style: normal;
16095
- font-weight: 400;
16096
- line-height: 20px;
16097
- margin: 0;
16098
- }
16099
- .cta-button {
16100
- font-size: 8px;
16101
- font-family: "Source Sans 3", system-ui;
16102
- font-style: normal;
16103
- font-weight: 400;
16104
- line-height: 18px;
16105
- border-radius: 5px;
16106
- border: 0.5px solid ${ctaBorderColor};
16107
- display: inline-block;
16108
- padding: 7px 20px;
16109
- color: ${ctaTextColor};
16110
- transition: background-color 0.3s ease;
16111
- }
16112
- .content:hover .cta-button {
16113
- background-color: ${ctaBorderColor.length === 7 ? `${ctaBorderColor}15` : `${ctaBorderColor}`};
16114
- }
16115
- @media (min-width: 640px) {
16116
- .content {
16117
- background-image: url("${primaryImage}");
16118
- }
16119
- }
16120
- @media (min-width: 768px) {
16121
- .primary-image {
16122
- width: 66.66666%;
16123
- height: 100%;
16124
- }
16125
- .main {
17899
+ const STYLES$6 = ({ textColor = '#ffffff', ctaTextColor = textColor, ctaBorderColor = ctaTextColor, primaryImage, mobilePrimaryImage = primaryImage, }, { prefix, overlay }) => {
17900
+ const linearGradient = generateGradientColor(overlay, 'rgba(0, 0, 0, 1) 0%, rgba(0, 0, 0, 0.5) 0%, rgba(0, 0, 0, 0) 40%');
17901
+ return `
17902
+ <style>
17903
+ .${prefix} {
17904
+ display: flex;
16126
17905
  flex-direction: column;
17906
+ justify-content: flex-end;
17907
+ align-items: flex-start;
17908
+ width: 100%;
16127
17909
  height: 100%;
16128
- width: 33.33333%;
17910
+ background-image: linear-gradient(to top, ${linearGradient}), url("${mobilePrimaryImage}");
17911
+ background-size: cover;
17912
+ background-repeat: no-repeat;
17913
+ background-position: center;
17914
+ padding: 3%;
17915
+ box-sizing: border-box;
17916
+ color: ${textColor};
17917
+ cursor: pointer;
16129
17918
  }
16130
- .secondary-image {
17919
+
17920
+ .${prefix}__text {
17921
+ display: flex;
17922
+ flex-direction: column;
17923
+ justify-content: flex-start;
17924
+ align-items: flex-start;
16131
17925
  width: 100%;
16132
- height: 50%;
17926
+ height: fit-content;
17927
+ gap: 8px;
16133
17928
  }
16134
- .header {
16135
- font-size: 22px;
16136
- }
16137
- .description {
16138
- font-size: 12px;
17929
+
17930
+ .${prefix}__header {
17931
+ font-size: 24px;
17932
+ margin: 0;
17933
+ font-family: "Cormorant", system-ui;
17934
+ font-style: normal;
17935
+ font-weight: 300;
17936
+ line-height: normal;
16139
17937
  }
16140
- .cta-button {
16141
- font-size: 12px;
17938
+
17939
+ .${prefix}__description {
17940
+ font-size: 10px;
17941
+ font-family: "Source Sans 3", system-ui;
17942
+ font-style: normal;
17943
+ font-weight: 400;
17944
+ line-height: 20px;
17945
+ margin: 0;
16142
17946
  }
16143
- }
16144
- @media (min-width: 1024px) {
16145
- .header {
16146
- font-size: 24px;
17947
+
17948
+ .${prefix}__cta-button {
17949
+ font-size: 8px;
17950
+ font-family: "Source Sans 3", system-ui;
17951
+ font-style: normal;
17952
+ font-weight: 400;
17953
+ line-height: 18px;
17954
+ border-radius: 5px;
17955
+ border: 0.5px solid ${ctaBorderColor};
17956
+ display: inline-block;
17957
+ padding: 7px 20px;
17958
+ color: ${ctaTextColor};
17959
+ transition: background-color 0.3s ease;
16147
17960
  }
16148
- .description {
16149
- font-size: 13px;
17961
+
17962
+ .${prefix}:hover .cta-button {
17963
+ background-color: ${ctaBorderColor.length === 7 ? `${ctaBorderColor}15` : `${ctaBorderColor}`};
16150
17964
  }
16151
- .cta-button {
16152
- font-size: 13px;
17965
+
17966
+ @container (min-width: 640px) {
17967
+ .${prefix} {
17968
+ background-image: linear-gradient(to top, ${linearGradient}), url("${primaryImage}");
17969
+ }
16153
17970
  }
16154
- }
16155
- @media (min-width: 1280px) {
16156
- .header {
16157
- font-size: 28px;
17971
+
17972
+ @container (min-width: 768px) {
17973
+ .${prefix}__primary-image {
17974
+ width: 66.66666%;
17975
+ height: 100%;
17976
+ }
17977
+
17978
+ .${prefix}__main {
17979
+ flex-direction: column;
17980
+ height: 100%;
17981
+ width: 33.33333%;
17982
+ }
17983
+
17984
+ .${prefix}__secondary-image {
17985
+ width: 100%;
17986
+ height: 50%;
17987
+ }
17988
+
17989
+ .${prefix}__description {
17990
+ font-size: 12px;
17991
+ }
17992
+
17993
+ .${prefix}__cta-button {
17994
+ font-size: 12px;
17995
+ }
16158
17996
  }
16159
- .description {
16160
- font-size: 14px;
17997
+
17998
+ @container (min-width: 1024px) {
17999
+ .${prefix}__header {
18000
+ font-size: 26px;
18001
+ }
18002
+
18003
+ .${prefix}__description {
18004
+ font-size: 13px;
18005
+ }
18006
+
18007
+ .${prefix}__cta-button {
18008
+ font-size: 13px;
18009
+ }
16161
18010
  }
16162
- .cta-button {
16163
- font-size: 14px;
18011
+
18012
+ @container (min-width: 1280px) {
18013
+ .${prefix}__header {
18014
+ font-size: 28px;
18015
+ }
18016
+
18017
+ .${prefix}__description {
18018
+ font-size: 14px;
18019
+ }
18020
+
18021
+ .${prefix}__cta-button {
18022
+ font-size: 14px;
18023
+ }
16164
18024
  }
16165
- }
16166
- </style>
16167
- `;
16168
- function rbHomepageHeroFullImageTemplate(spot) {
18025
+ </style>
18026
+ `;
18027
+ };
18028
+ function rbHomepageHeroFullImageTemplate(spot, config) {
18029
+ const { prefix = '' } = config;
16169
18030
  return `
16170
18031
  ${GFONT_PRECONNECT}
16171
18032
  ${GFONT_SOURCE_SANS_3}
16172
18033
  ${GFONT_CORMORANT}
16173
- ${STYLES$6(spot)}
16174
- <div class="content">
16175
- <div class="text">
16176
- ${spot.header ? `<h2 class="header">${spot.header}</h2>` : ''}
16177
- ${spot.description ? `<p class="description">${spot.description}</p>` : ''}
16178
- ${spot.ctaText ? `<span class="cta-button">${spot.ctaText}</span>` : ''}
16179
- </div>
18034
+ ${STYLES$6(spot, config)}
18035
+ <div class="${prefix}">
18036
+ <div class="${prefix}__text">
18037
+ ${spot.header ? `<h2 class="${prefix}__header">${spot.header}</h2>` : ''}
18038
+ ${spot.description ? `<p class="${prefix}__description">${spot.description}</p>` : ''}
18039
+ ${spot.ctaText ? `<span class="${prefix}__cta-button">${spot.ctaText}</span>` : ''}
18040
+ </div>
16180
18041
  </div>
16181
18042
  `;
16182
18043
  }
16183
18044
 
16184
- const STYLES$5 = ({ textColor = '#212121', backgroundColor = '#e8e6de', ctaTextColor = textColor, primaryImage, mobilePrimaryImage = primaryImage, secondaryImage, mobileSecondaryImage = secondaryImage, }) => `
16185
- <style>
16186
- :host {
16187
- min-width: 320px;
16188
- min-height: 388px;
18045
+ const STYLES$5 = ({ textColor = '#212121', backgroundColor = '#e8e6de', ctaTextColor = textColor, primaryImage, mobilePrimaryImage = primaryImage, secondaryImage, mobileSecondaryImage = secondaryImage, }, { prefix }) => `
18046
+ <style>
18047
+ .${prefix} {
18048
+ width: 100%;
18049
+ height: 100%;
18050
+ display: block;
18051
+ position: relative;
16189
18052
  }
16190
- .content {
18053
+
18054
+ .${prefix}__content {
16191
18055
  width: 100%;
16192
18056
  height: 100%;
16193
18057
  display: flex;
16194
- flex-direction: column;
18058
+ flex-direction: column;
16195
18059
  background-color: transparent;
16196
- gap: 5px;
18060
+ gap: 6px;
16197
18061
  color: inherit;
16198
18062
  cursor: pointer;
16199
18063
  }
16200
- .primary-image {
18064
+
18065
+ .${prefix}__primary-image {
16201
18066
  width: 100%;
16202
18067
  height: 60%;
16203
18068
  background-image: url("${mobilePrimaryImage}");
@@ -16205,14 +18070,16 @@ const STYLES$5 = ({ textColor = '#212121', backgroundColor = '#e8e6de', ctaTextC
16205
18070
  background-repeat: no-repeat;
16206
18071
  background-size: cover;
16207
18072
  }
16208
- .main {
18073
+
18074
+ .${prefix}__main {
16209
18075
  width: 100%;
16210
18076
  height: 40%;
16211
18077
  display: flex;
16212
18078
  flex-direction: row;
16213
- gap: 5px;
18079
+ gap: 6px;
16214
18080
  }
16215
- .secondary-image {
18081
+
18082
+ .${prefix}__secondary-image {
16216
18083
  width: 50%;
16217
18084
  height: 100%;
16218
18085
  background-image: url("${mobileSecondaryImage}");
@@ -16220,7 +18087,8 @@ const STYLES$5 = ({ textColor = '#212121', backgroundColor = '#e8e6de', ctaTextC
16220
18087
  background-repeat: no-repeat;
16221
18088
  background-size: cover;
16222
18089
  }
16223
- .text {
18090
+
18091
+ .${prefix}__text {
16224
18092
  color: ${textColor};
16225
18093
  background-color: ${backgroundColor};
16226
18094
  text-align: center;
@@ -16230,11 +18098,12 @@ const STYLES$5 = ({ textColor = '#212121', backgroundColor = '#e8e6de', ctaTextC
16230
18098
  align-items: center;
16231
18099
  width: 50%;
16232
18100
  height: 100%;
16233
- gap: 5px;
18101
+ gap: 10px;
16234
18102
  padding: 0 10px;
16235
18103
  box-sizing: border-box;
16236
18104
  }
16237
- .header {
18105
+
18106
+ .${prefix}__header {
16238
18107
  color: inherit;
16239
18108
  margin: 0;
16240
18109
  font-size: 18px;
@@ -16243,7 +18112,8 @@ const STYLES$5 = ({ textColor = '#212121', backgroundColor = '#e8e6de', ctaTextC
16243
18112
  font-weight: 700;
16244
18113
  line-height: normal;
16245
18114
  }
16246
- .description {
18115
+
18116
+ .${prefix}__description {
16247
18117
  color: inherit;
16248
18118
  margin: 0;
16249
18119
  font-size: 10px;
@@ -16251,7 +18121,8 @@ const STYLES$5 = ({ textColor = '#212121', backgroundColor = '#e8e6de', ctaTextC
16251
18121
  font-style: normal;
16252
18122
  font-weight: 400;
16253
18123
  }
16254
- .cta-button {
18124
+
18125
+ .${prefix}__cta-button {
16255
18126
  color: ${ctaTextColor};
16256
18127
  background-color: transparent;
16257
18128
  font-size: 8px;
@@ -16266,108 +18137,125 @@ const STYLES$5 = ({ textColor = '#212121', backgroundColor = '#e8e6de', ctaTextC
16266
18137
  padding: 0;
16267
18138
  transition: opacity 0.3s ease;
16268
18139
  }
16269
- .content:hover .cta-button {
18140
+
18141
+ .${prefix}__content:hover .${prefix}__cta-button {
16270
18142
  opacity: 0.8;
16271
18143
  }
16272
- @media (min-width: 640px) {
16273
- .primary-image {
18144
+
18145
+ @container (min-width: 640px) {
18146
+ .${prefix}__primary-image {
16274
18147
  background-image: url("${primaryImage}");
16275
18148
  }
16276
- .secondary-image {
18149
+
18150
+ .${prefix}__secondary-image {
16277
18151
  background-image: url("${secondaryImage}");
16278
18152
  }
16279
18153
  }
16280
- @media (min-width: 768px) {
16281
- .content {
18154
+
18155
+ @container (min-width: 768px) {
18156
+ .${prefix}__content {
16282
18157
  flex-direction: row;
16283
18158
  }
16284
- .primary-image {
18159
+
18160
+ .${prefix}__primary-image {
16285
18161
  width: 66.66666%;
16286
18162
  height: 100%;
16287
18163
  }
16288
- .main {
18164
+
18165
+ .${prefix}__main {
16289
18166
  flex-direction: column;
16290
18167
  height: 100%;
16291
18168
  width: 33.33333%;
16292
18169
  }
16293
- .secondary-image {
18170
+
18171
+ .${prefix}__secondary-image {
16294
18172
  width: 100%;
16295
18173
  height: 50%;
16296
18174
  }
16297
- .text {
18175
+
18176
+ .${prefix}__text {
16298
18177
  width: 100%;
16299
18178
  height: 50%;
16300
18179
  }
16301
- .header {
18180
+ .${prefix}__header {
16302
18181
  font-size: 22px;
16303
18182
  }
16304
- .description {
18183
+ .${prefix}__description {
16305
18184
  font-size: 12px;
16306
18185
  }
16307
- .cta-button {
18186
+ .${prefix}__cta-button {
16308
18187
  font-size: 12px;
16309
18188
  }
16310
18189
  }
16311
- @media (min-width: 1024px) {
16312
- .header {
18190
+
18191
+ @container (min-width: 1024px) {
18192
+ .${prefix}__header {
16313
18193
  font-size: 24px;
16314
18194
  }
16315
- .description {
18195
+ .${prefix}__description {
16316
18196
  font-size: 13px;
16317
- }
16318
- .cta-button {
16319
- font-size: 13px;
16320
- }
18197
+ }
18198
+ .${prefix}__cta-button {
18199
+ font-size: 13px;
18200
+ }
16321
18201
  }
16322
- @media (min-width: 1280px) {
16323
- .header {
18202
+
18203
+ @container (min-width: 1280px) {
18204
+ .${prefix}__header {
16324
18205
  font-size: 28px;
16325
18206
  }
16326
- .description {
18207
+ .${prefix}__description {
16327
18208
  font-size: 14px;
16328
18209
  }
16329
- .cta-button {
18210
+ .${prefix}__cta-button {
16330
18211
  font-size: 14px;
16331
18212
  }
16332
18213
  }
16333
- </style>
18214
+ </style>
16334
18215
  `;
16335
- function rbHomepageHeroThreeTileTemplate(spot) {
18216
+ function rbHomepageHeroThreeTileTemplate(spot, config) {
18217
+ const { prefix = '' } = config;
16336
18218
  return `
16337
18219
  ${GFONT_PRECONNECT}
16338
18220
  ${GFONT_SOURCE_SANS_3}
16339
18221
  ${GFONT_CORMORANT}
16340
- ${STYLES$5(spot)}
16341
- <div class="content">
16342
- <div class="primary-image"></div>
16343
- <div class="main">
16344
- <div class="secondary-image"></div>
16345
- <div class="text">
16346
- ${spot.header ? `<h2 class="header">${spot.header}</h2>` : ''}
16347
- ${spot.description ? `<p class="description">${spot.description}</p>` : ''}
16348
- ${spot.ctaText ? `<span class="cta-button">${spot.ctaText}</span>` : ''}
18222
+ ${STYLES$5(spot, config)}
18223
+ <div class="${prefix}">
18224
+ <div class="${prefix}__content">
18225
+ <div class="${prefix}__primary-image"></div>
18226
+ <div class="${prefix}__main">
18227
+ <div class="${prefix}__secondary-image"></div>
18228
+ <div class="${prefix}__text">
18229
+ ${spot.header ? `<h2 class="${prefix}__header">${spot.header}</h2>` : ''}
18230
+ ${spot.description ? `<p class="${prefix}__description">${spot.description}</p>` : ''}
18231
+ ${spot.ctaText ? `<span class="${prefix}__cta-button">${spot.ctaText}</span>` : ''}
18232
+ </div>
16349
18233
  </div>
16350
18234
  </div>
16351
18235
  </div>
16352
18236
  `;
16353
18237
  }
16354
18238
 
16355
- const STYLES$4 = ({ textColor = '#212121', backgroundColor = '#e8e6de', ctaTextColor = textColor, primaryImage, mobilePrimaryImage = primaryImage, }) => `
18239
+ const STYLES$4 = ({ textColor = '#212121', backgroundColor = '#e8e6de', ctaTextColor = textColor, primaryImage, mobilePrimaryImage = primaryImage, }, { prefix }) => `
16356
18240
  <style>
16357
- :host {
16358
- min-width: 320px;
16359
- min-height: 388px;
18241
+ .${prefix} {
18242
+ width: 100%;
18243
+ height: 100%;
18244
+ background-color: transparent;
18245
+ cursor: pointer;
18246
+ position: relative;
16360
18247
  }
16361
- .content {
18248
+
18249
+ .${prefix}__content {
16362
18250
  width: 100%;
16363
18251
  height: 100%;
16364
18252
  display: flex;
16365
- flex-direction: column-reverse;
18253
+ flex-direction: column-reverse;
16366
18254
  background-color: transparent;
16367
- gap: 5px;
16368
- cursor: pointer;
18255
+ gap: 6px;
16369
18256
  }
16370
- .image {
18257
+
18258
+ .${prefix}__image {
16371
18259
  width: 100%;
16372
18260
  height: 100%;
16373
18261
  background-image: url("${mobilePrimaryImage}");
@@ -16375,7 +18263,8 @@ const STYLES$4 = ({ textColor = '#212121', backgroundColor = '#e8e6de', ctaTextC
16375
18263
  background-repeat: no-repeat;
16376
18264
  background-size: cover;
16377
18265
  }
16378
- .text {
18266
+
18267
+ .${prefix}__text {
16379
18268
  color: ${textColor};
16380
18269
  background-color: ${backgroundColor};
16381
18270
  text-align: center;
@@ -16385,20 +18274,22 @@ const STYLES$4 = ({ textColor = '#212121', backgroundColor = '#e8e6de', ctaTextC
16385
18274
  align-items: center;
16386
18275
  width: 100%;
16387
18276
  height: 100%;
16388
- gap: 5px;
18277
+ gap: 10px;
16389
18278
  padding: 0 10px;
16390
18279
  box-sizing: border-box;
16391
18280
  }
16392
- .header {
18281
+
18282
+ .${prefix}__header {
16393
18283
  font-size: 18px;
16394
18284
  margin: 0;
16395
18285
  color: inherit;
16396
- font-family: "Cormorant";
18286
+ font-family: "Cormorant", system-ui;
16397
18287
  font-style: normal;
16398
18288
  font-weight: 700;
16399
18289
  line-height: normal;
16400
18290
  }
16401
- .description {
18291
+
18292
+ .${prefix}__description {
16402
18293
  color: inherit;
16403
18294
  margin: 0;
16404
18295
  font-size: 10px;
@@ -16406,7 +18297,8 @@ const STYLES$4 = ({ textColor = '#212121', backgroundColor = '#e8e6de', ctaTextC
16406
18297
  font-style: normal;
16407
18298
  font-weight: 400;
16408
18299
  }
16409
- .cta-button {
18300
+
18301
+ .${prefix}__cta-button {
16410
18302
  color: ${ctaTextColor};
16411
18303
  background-color: transparent;
16412
18304
  font-size: 8px;
@@ -16421,205 +18313,225 @@ const STYLES$4 = ({ textColor = '#212121', backgroundColor = '#e8e6de', ctaTextC
16421
18313
  padding: 0;
16422
18314
  transition: opacity 0.3s ease;
16423
18315
  }
16424
- .content:hover .cta-button {
18316
+
18317
+ .${prefix}__content:hover .${prefix}__cta-button {
16425
18318
  opacity: 0.8;
16426
18319
  }
16427
- @media (min-width: 640px) {
16428
- .image {
18320
+
18321
+ @container (min-width: 640px) {
18322
+ .${prefix}__image {
16429
18323
  background-image: url("${primaryImage}");
16430
18324
  }
16431
18325
  }
16432
- @media (min-width: 768px) {
16433
- .content {
18326
+
18327
+ @container (min-width: 768px) {
18328
+ .${prefix}__content {
16434
18329
  flex-direction: row;
16435
18330
  }
16436
- .image {
18331
+ .${prefix}__image {
16437
18332
  width: 66.66666%;
16438
18333
  height: 100%;
16439
18334
  }
16440
- .text {
16441
- width: 33.333%;
18335
+ .${prefix}__text {
18336
+ width: 33.33333%;
16442
18337
  height: 100%;
16443
18338
  }
16444
- .header {
18339
+ .${prefix}__header {
16445
18340
  font-size: 22px;
16446
18341
  }
16447
- .description {
18342
+ .${prefix}__description {
16448
18343
  font-size: 12px;
16449
18344
  }
16450
- .cta-button {
18345
+ .${prefix}__cta-button {
16451
18346
  font-size: 12px;
16452
18347
  }
16453
18348
  }
16454
- @media (min-width: 1024px) {
16455
- .header {
18349
+
18350
+ @container (min-width: 1024px) {
18351
+ .${prefix}__header {
16456
18352
  font-size: 24px;
16457
18353
  }
16458
- .description {
18354
+ .${prefix}__description {
16459
18355
  font-size: 13px;
16460
- }
16461
- .cta-button {
16462
- font-size: 13px;
16463
- }
18356
+ }
18357
+ .${prefix}__cta-button {
18358
+ font-size: 13px;
18359
+ }
16464
18360
  }
16465
- @media (min-width: 1280px) {
16466
- .header {
18361
+
18362
+ @container (min-width: 1280px) {
18363
+ .${prefix}__header {
16467
18364
  font-size: 28px;
16468
18365
  }
16469
- .description {
18366
+ .${prefix}__description {
16470
18367
  font-size: 14px;
16471
18368
  }
16472
- .cta-button {
18369
+ .${prefix}__cta-button {
16473
18370
  font-size: 14px;
16474
18371
  }
16475
18372
  }
16476
18373
  </style>
16477
18374
  `;
16478
- function rbHomepageHeroTwoTileTemplate(spot) {
18375
+ function rbHomepageHeroTwoTileTemplate(spot, config) {
18376
+ const { prefix = '' } = config;
16479
18377
  return `
16480
18378
  ${GFONT_PRECONNECT}
16481
18379
  ${GFONT_SOURCE_SANS_3}
16482
18380
  ${GFONT_CORMORANT}
16483
- ${STYLES$4(spot)}
16484
- <div class="content">
16485
- <div class="text">
16486
- ${spot.header ? `<h2 class="header">${spot.header}</h2>` : ''}
16487
- ${spot.description ? `<p class="description">${spot.description}</p>` : ''}
16488
- ${spot.ctaText ? `<span class="cta-button">${spot.ctaText}</span>` : ''}
16489
- </div>
16490
- <div class="image"></div>
18381
+ ${STYLES$4(spot, config)}
18382
+ <div class="${prefix}">
18383
+ <div class="${prefix}__content">
18384
+ <div class="${prefix}__text">
18385
+ ${spot.header ? `<h2 class="${prefix}__header">${spot.header}</h2>` : ''}
18386
+ ${spot.description ? `<p class="${prefix}__description">${spot.description}</p>` : ''}
18387
+ ${spot.ctaText ? `<span class="${prefix}__cta-button">${spot.ctaText}</span>` : ''}
18388
+ </div>
18389
+ <div class="${prefix}__image"></div>
18390
+ </div>
16491
18391
  </div>
16492
18392
  `;
16493
18393
  }
16494
18394
 
16495
- const STYLES$3 = ({ textColor = '#ffffff', ctaTextColor = textColor, ctaBorderColor = ctaTextColor, primaryImage, mobilePrimaryImage = primaryImage, }) => `
16496
- <style>
16497
- :host {
16498
- min-width: 320px;
16499
- min-height: 334px;
16500
- }
16501
- .content {
16502
- width: 100%;
16503
- height: 100%;
16504
- display: flex;
16505
- flex-direction: column;
16506
- justify-content: flex-end;
16507
- background-image: url("${mobilePrimaryImage}");
16508
- background-size: cover;
16509
- background-repeat: no-repeat;
16510
- background-position: center;
16511
- border-radius: 5px;
16512
- overflow: hidden;
16513
- cursor: pointer;
16514
- color: ${textColor};
16515
- }
16516
- .text {
16517
- padding: 20px;
16518
- width: 70%;
16519
- display: flex;
16520
- flex-direction: column;
16521
- justify-content: center;
16522
- align-items: flex-start;
16523
- gap: 10px;
16524
- }
16525
- .header {
16526
- color: inherit;
16527
- margin: 0;
16528
- font-size: 20px;
16529
- font-family: "Cormorant";
16530
- font-style: normal;
16531
- font-weight: 300;
16532
- line-height: normal;
16533
- }
16534
- .description {
16535
- color: inherit;
16536
- font-size: 12px;
16537
- line-height: 16px;
16538
- font-family: "Source Sans 3", system-ui;
16539
- font-style: normal;
16540
- font-weight: 400;
16541
- margin: 0;
16542
- }
16543
- .cta-button {
16544
- width: fit-content;
16545
- display: inline-block;
16546
- padding: 7px 20px;
16547
- border: 0.5px solid ${ctaBorderColor};
16548
- border-radius: 5px;
16549
- color: ${ctaTextColor};
16550
- font-size: 8px;
16551
- transition: background-color 0.3s ease;
16552
- font-family: "Source Sans 3", system-ui;
16553
- font-style: normal;
16554
- font-weight: 400;
16555
- }
16556
- .content:hover .cta-button {
16557
- background-color: ${ctaBorderColor.length === 7 ? `${ctaBorderColor}15` : `${ctaBorderColor}`};
16558
- }
16559
- @media (min-width: 640px) {
16560
- .content {
16561
- background-image: url("${primaryImage}");
16562
- }
16563
- }
16564
- @media (min-width: 768px) {
16565
- .text {
16566
- padding: 25px;
18395
+ const STYLES$3 = ({ textColor = '#ffffff', ctaTextColor = textColor, ctaBorderColor = ctaTextColor, primaryImage, mobilePrimaryImage = primaryImage, }, { prefix, overlay }) => {
18396
+ const linearGradient = generateGradientColor(overlay, 'rgba(0, 0, 0, 1) 0%, rgba(0, 0, 0, 0.5) 0%, rgba(0, 0, 0, 0) 30%');
18397
+ return `
18398
+ <style>
18399
+ .${prefix} {
18400
+ width: 100%;
18401
+ height: 100%;
18402
+ display: flex;
18403
+ flex-direction: column;
18404
+ justify-content: flex-end;
18405
+ background-image: linear-gradient(to top, ${linearGradient}), url("${mobilePrimaryImage}");
18406
+ background-size: cover;
18407
+ background-repeat: no-repeat;
18408
+ background-position: center;
18409
+ border-radius: 5px;
18410
+ overflow: hidden;
18411
+ cursor: pointer;
18412
+ color: ${textColor};
16567
18413
  }
16568
- .header {
16569
- font-size: 24px;
18414
+
18415
+ .${prefix}__text {
18416
+ padding: 20px;
18417
+ display: flex;
18418
+ flex-direction: column;
18419
+ justify-content: center;
18420
+ align-items: flex-start;
18421
+ gap: 10px;
16570
18422
  }
16571
- .description {
16572
- font-size: 13px;
16573
- line-height: 18px;
18423
+
18424
+ .${prefix}__header {
18425
+ color: inherit;
18426
+ margin: 0;
18427
+ font-size: 20px;
18428
+ font-family: "Cormorant", system-ui;
18429
+ font-style: normal;
18430
+ font-weight: 300;
18431
+ line-height: normal;
16574
18432
  }
16575
- .cta-button {
18433
+
18434
+ .${prefix}__description {
18435
+ color: inherit;
16576
18436
  font-size: 12px;
18437
+ line-height: 16px;
18438
+ font-family: "Source Sans 3", system-ui;
18439
+ font-style: normal;
18440
+ font-weight: 400;
18441
+ margin: 0;
16577
18442
  }
16578
- }
16579
- @media (min-width: 1024px) {
16580
- .text {
16581
- padding: 30px;
18443
+
18444
+ .${prefix}__cta-button {
18445
+ width: fit-content;
18446
+ display: inline-block;
18447
+ padding: 7px 20px;
18448
+ border: 0.5px solid ${ctaBorderColor};
18449
+ border-radius: 5px;
18450
+ color: ${ctaTextColor};
18451
+ font-size: 8px;
18452
+ transition: background-color 0.3s ease;
18453
+ font-family: "Source Sans 3", system-ui;
18454
+ font-style: normal;
18455
+ font-weight: 400;
16582
18456
  }
16583
- .header {
16584
- font-size: 28px;
18457
+
18458
+ .${prefix}:hover .${prefix}__cta-button {
18459
+ background-color: ${ctaBorderColor.length === 7 ? `${ctaBorderColor}15` : `${ctaBorderColor}`};
18460
+ }
18461
+
18462
+ @container (min-width: 640px) {
18463
+ .${prefix} {
18464
+ background-image: linear-gradient(to top, ${linearGradient}), url("${primaryImage}");
18465
+ }
16585
18466
  }
16586
- .description {
16587
- font-size: 14px;
18467
+
18468
+ @container (min-width: 768px) {
18469
+ .${prefix}__text {
18470
+ padding: 25px;
18471
+ }
18472
+
18473
+ .${prefix}__header {
18474
+ font-size: 24px;
18475
+ }
18476
+
18477
+ .${prefix}__description {
18478
+ font-size: 13px;
18479
+ line-height: 18px;
18480
+ }
18481
+
18482
+ .${prefix}__cta-button {
18483
+ font-size: 12px;
18484
+ }
16588
18485
  }
16589
- .cta-button {
16590
- font-size: 13px;
18486
+
18487
+ @container (min-width: 1024px) {
18488
+ .${prefix}__text {
18489
+ padding: 30px;
18490
+ }
18491
+
18492
+ .${prefix}__header {
18493
+ font-size: 28px;
18494
+ }
18495
+
18496
+ .${prefix}__description {
18497
+ font-size: 14px;
18498
+ }
18499
+
18500
+ .${prefix}__cta-button {
18501
+ font-size: 13px;
18502
+ }
16591
18503
  }
16592
- }
16593
- @media (min-width: 1280px) {
16594
- .cta-button {
16595
- font-size: 14px;
18504
+
18505
+ @container (min-width: 1280px) {
18506
+ .${prefix}__cta-button {
18507
+ font-size: 14px;
18508
+ }
16596
18509
  }
16597
- }
16598
- </style>
16599
- `;
16600
- function rbLargeCategoryImageToutTemplate(spot) {
18510
+ </style>
18511
+ `;
18512
+ };
18513
+ function rbLargeCategoryImageToutTemplate(spot, config) {
18514
+ const { prefix = '' } = config;
16601
18515
  return `
16602
18516
  ${GFONT_PRECONNECT}
16603
18517
  ${GFONT_SOURCE_SANS_3}
16604
18518
  ${GFONT_CORMORANT}
16605
- ${STYLES$3(spot)}
16606
- <div class="content">
16607
- <div class="text">
16608
- ${spot.header ? `<h2 class="header">${spot.header}</h2>` : ''}
16609
- ${spot.description ? `<p class="description">${spot.description}</p>` : ''}
16610
- ${spot.ctaText ? `<span class="cta-button">${spot.ctaText}</span>` : ''}
18519
+ ${STYLES$3(spot, config)}
18520
+ <div class="${prefix}">
18521
+ <div class="${prefix}__text">
18522
+ ${spot.header ? `<h2 class="${prefix}__header">${spot.header}</h2>` : ''}
18523
+ ${spot.description ? `<p class="${prefix}__description">${spot.description}</p>` : ''}
18524
+ ${spot.ctaText ? `<span class="${prefix}__cta-button">${spot.ctaText}</span>` : ''}
16611
18525
  </div>
16612
18526
  </div>
16613
18527
  `;
16614
18528
  }
16615
18529
 
16616
- const STYLES$2 = ({ textColor = '#ffffff', primaryImage, mobilePrimaryImage = primaryImage, }) => `
18530
+ const STYLES$2 = ({ textColor = '#ffffff', primaryImage, mobilePrimaryImage = primaryImage }, { prefix, overlay }) => {
18531
+ const linearGradient = generateGradientColor(overlay, 'rgba(0, 0, 0, 1) 0%, rgba(0, 0, 0, 0.5) 0%, rgba(0, 0, 0, 0) 30%');
18532
+ return `
16617
18533
  <style>
16618
- :host {
16619
- min-width: 320px;
16620
- min-height: 150px;
16621
- }
16622
- .content {
18534
+ .${prefix} {
16623
18535
  width: 100%;
16624
18536
  height: 100%;
16625
18537
  display: flex;
@@ -16628,17 +18540,14 @@ const STYLES$2 = ({ textColor = '#ffffff', primaryImage, mobilePrimaryImage = pr
16628
18540
  border-radius: 5px;
16629
18541
  overflow: hidden;
16630
18542
  cursor: pointer;
16631
- background-image: url("${mobilePrimaryImage}");
18543
+ background-image: linear-gradient(to top, ${linearGradient}), url("${mobilePrimaryImage}");
16632
18544
  background-size: cover;
16633
18545
  background-position: center;
16634
- background-blend-mode: overlay;
16635
18546
  background-repeat: no-repeat;
18547
+ position: relative;
16636
18548
  }
16637
- .text {
16638
- padding: 15px 10%;
16639
- width: fit-content;
16640
- }
16641
- .header {
18549
+
18550
+ .${prefix}__header {
16642
18551
  font-size: 16px;
16643
18552
  color: ${textColor};
16644
18553
  line-height: 20px;
@@ -16646,103 +18555,101 @@ const STYLES$2 = ({ textColor = '#ffffff', primaryImage, mobilePrimaryImage = pr
16646
18555
  font-family: "Source Sans 3", system-ui;
16647
18556
  font-style: normal;
16648
18557
  margin: 0;
18558
+ padding: 15px 10%;
16649
18559
  }
16650
- @media (min-width: 640px) {
16651
- .content {
16652
- background-image: url("${primaryImage}");
18560
+
18561
+ @container (min-width: 640px) {
18562
+ .${prefix} {
18563
+ background-image: linear-gradient(to top, ${linearGradient}), url("${primaryImage}");
16653
18564
  }
16654
18565
  }
16655
- @media (min-width: 768px) {
16656
- .header {
18566
+
18567
+ @container (min-width: 768px) {
18568
+ .${prefix}__header {
16657
18569
  font-size: 22px;
16658
18570
  }
16659
18571
  }
16660
- @media (min-width: 1024px) {
16661
- .header {
18572
+
18573
+ @container (min-width: 1024px) {
18574
+ .${prefix}__header {
16662
18575
  font-size: 24px;
16663
18576
  }
16664
18577
  }
16665
- @media (min-width: 1280px) {
16666
- .header {
18578
+
18579
+ @container (min-width: 1280px) {
18580
+ .${prefix}__header {
16667
18581
  font-size: 28px;
16668
18582
  }
16669
18583
  }
16670
18584
  </style>
16671
18585
  `;
16672
- function rbNavigationBannerTemplate(spot) {
18586
+ };
18587
+ function rbNavigationBannerTemplate(spot, config) {
18588
+ const { prefix = '' } = config;
16673
18589
  return `
16674
18590
  ${GFONT_PRECONNECT}
16675
18591
  ${GFONT_SOURCE_SANS_3}
16676
- ${STYLES$2(spot)}
16677
- <div class="content">
16678
- <div class="text">
16679
- ${spot.header ? `<h2 class="header">${spot.header}</h2>` : ''}
16680
- </div>
18592
+ ${STYLES$2(spot, config)}
18593
+ <div class="${prefix}">
18594
+ ${spot.header ? `<h2 class="${prefix}__header">${spot.header}</h2>` : ''}
16681
18595
  </div>
16682
18596
  `;
16683
18597
  }
16684
18598
 
16685
- const STYLES$1 = ({ textColor = '#ffffff', primaryImage, mobilePrimaryImage = primaryImage, }) => `
16686
- <style>
16687
- :host {
16688
- min-width: 165px;
16689
- min-height: 300px;
16690
- }
16691
- .content {
16692
- width: 100%;
16693
- height: 100%;
16694
- display: flex;
16695
- flex-direction: column;
16696
- justify-content: flex-end;
16697
- background-image: url("${mobilePrimaryImage}");
16698
- background-size: cover;
16699
- background-repeat: no-repeat;
16700
- background-position: center;
16701
- border-radius: 5px;
16702
- overflow: hidden;
16703
- cursor: pointer;
16704
- }
16705
- .text {
16706
- padding: 10px;
16707
- width: 70%;
16708
- }
16709
- .header {
16710
- font-size: 12px;
16711
- color: ${textColor};
16712
- line-height: 16px;
16713
- font-family: "Source Sans 3", system-ui;
16714
- font-style: normal;
16715
- font-weight: 400;
16716
- line-height: normal;
16717
- margin: 0;
16718
- }
16719
- @media (min-width: 640px) {
16720
- .content {
16721
- background-image: url("${primaryImage}");
18599
+ const STYLES$1 = ({ textColor = '#ffffff', primaryImage, mobilePrimaryImage = primaryImage }, { prefix, overlay }) => {
18600
+ const linearGradient = generateGradientColor(overlay, 'rgba(0, 0, 0, 1) 0%, rgba(0, 0, 0, 0.5) 0%, rgba(0, 0, 0, 0) 30%');
18601
+ return `
18602
+ <style>
18603
+ .${prefix} {
18604
+ width: 100%;
18605
+ height: 100%;
18606
+ display: flex;
18607
+ flex-direction: column;
18608
+ justify-content: flex-end;
18609
+ background-image: linear-gradient(to top, ${linearGradient}), url("${mobilePrimaryImage}");
18610
+ background-size: cover;
18611
+ background-repeat: no-repeat;
18612
+ background-position: center;
18613
+ border-radius: 5px;
18614
+ overflow: hidden;
18615
+ cursor: pointer;
18616
+ position: relative;
16722
18617
  }
16723
- }
16724
- </style>
16725
- `;
16726
- function rbSmallCategoryImageToutTemplate(spot) {
18618
+
18619
+ .${prefix}__header {
18620
+ font-size: 12px;
18621
+ color: ${textColor};
18622
+ line-height: 16px;
18623
+ font-family: "Source Sans 3", system-ui;
18624
+ font-style: normal;
18625
+ font-weight: 400;
18626
+ margin: 0;
18627
+ padding: 10px;
18628
+ }
18629
+
18630
+ @container (min-width: 640px) {
18631
+ .${prefix} {
18632
+ background-image: linear-gradient(to top, ${linearGradient}), url("${primaryImage}");
18633
+ }
18634
+ }
18635
+ </style>
18636
+ `;
18637
+ };
18638
+ function rbSmallCategoryImageToutTemplate(spot, config) {
18639
+ const { prefix = '' } = config;
16727
18640
  return `
16728
18641
  ${GFONT_PRECONNECT}
16729
18642
  ${GFONT_CORMORANT}
16730
- ${STYLES$1(spot)}
16731
- <div class="content">
16732
- <div class="text">
16733
- ${spot.header ? `<h2 class="header">${spot.header}</h2>` : ''}
16734
- </div>
18643
+ ${STYLES$1(spot, config)}
18644
+ <div class="${prefix}">
18645
+ ${spot.header ? `<h2 class="${prefix}__header">${spot.header}</h2>` : ''}
16735
18646
  </div>
16736
18647
  `;
16737
18648
  }
16738
18649
 
16739
- const STYLES = ({ textColor = '#000000', backgroundColor = 'transparent', primaryImage, mobilePrimaryImage = primaryImage, }) => `
18650
+ const STYLES = ({ textColor = '#000000', backgroundColor = 'transparent', primaryImage, mobilePrimaryImage = primaryImage, }, { prefix }) => `
16740
18651
  <style>
16741
- :host {
16742
- min-width: 165px;
16743
- min-height: 250px;
16744
- }
16745
- .content {
18652
+ .${prefix} {
16746
18653
  width: 100%;
16747
18654
  height: 100%;
16748
18655
  background-color: ${backgroundColor};
@@ -16751,7 +18658,8 @@ const STYLES = ({ textColor = '#000000', backgroundColor = 'transparent', primar
16751
18658
  flex-direction: column;
16752
18659
  border-radius: 5px;
16753
18660
  }
16754
- .image {
18661
+
18662
+ .${prefix}__image {
16755
18663
  width: 100%;
16756
18664
  height: 100%;
16757
18665
  background-image: url("${mobilePrimaryImage}");
@@ -16760,7 +18668,8 @@ const STYLES = ({ textColor = '#000000', backgroundColor = 'transparent', primar
16760
18668
  background-position: center;
16761
18669
  border-radius: 5px;
16762
18670
  }
16763
- .text {
18671
+
18672
+ .${prefix}__text {
16764
18673
  text-align: left;
16765
18674
  display: flex;
16766
18675
  flex-direction: row;
@@ -16770,7 +18679,8 @@ const STYLES = ({ textColor = '#000000', backgroundColor = 'transparent', primar
16770
18679
  height: fit-content;
16771
18680
  position: relative;
16772
18681
  }
16773
- .header {
18682
+
18683
+ .${prefix}__header {
16774
18684
  font-size: 12px;
16775
18685
  color: ${textColor};
16776
18686
  padding-top: 5px;
@@ -16778,46 +18688,42 @@ const STYLES = ({ textColor = '#000000', backgroundColor = 'transparent', primar
16778
18688
  font-family: "Source Sans 3", system-ui;
16779
18689
  font-style: normal;
16780
18690
  font-weight: 400;
16781
- line-height: normal;
16782
18691
  margin: 0;
16783
18692
  }
16784
- @media (min-width: 640px) {
16785
- .image {
18693
+
18694
+ @container (min-width: 640px) {
18695
+ .${prefix}__image {
16786
18696
  background-image: url("${primaryImage}");
16787
18697
  }
16788
18698
  }
16789
18699
  </style>
16790
18700
  `;
16791
- function rbSmallDiscoverToutTemplate(spot) {
18701
+ function rbSmallDiscoverToutTemplate(spot, config) {
18702
+ const { prefix = '' } = config;
16792
18703
  return `
16793
18704
  ${GFONT_PRECONNECT}
16794
18705
  ${GFONT_CORMORANT}
16795
- ${STYLES(spot)}
16796
- <div class="content">
16797
- <div class="image"></div>
16798
- <div class="text">
16799
- ${spot.header ? `<h2 class="header">${spot.header}</h2>` : ''}
18706
+ ${STYLES(spot, config)}
18707
+ <div class="${prefix}">
18708
+ <div class="${prefix}__image"></div>
18709
+ <div class="${prefix}__text">
18710
+ ${spot.header ? `<h2 class="${prefix}__header">${spot.header}</h2>` : ''}
16800
18711
  </div>
16801
18712
  </div>
16802
18713
  `;
16803
18714
  }
16804
18715
 
16805
- var ENUM_SPOT_ELEMENT_ATTRIBUTE;
16806
- (function (ENUM_SPOT_ELEMENT_ATTRIBUTE) {
16807
- ENUM_SPOT_ELEMENT_ATTRIBUTE["WIDTH"] = "width";
16808
- ENUM_SPOT_ELEMENT_ATTRIBUTE["HEIGHT"] = "height";
16809
- ENUM_SPOT_ELEMENT_ATTRIBUTE["FLUID"] = "fluid";
16810
- ENUM_SPOT_ELEMENT_ATTRIBUTE["REDIRECT_ON_CLICK"] = "redirect-on-click";
16811
- })(ENUM_SPOT_ELEMENT_ATTRIBUTE || (ENUM_SPOT_ELEMENT_ATTRIBUTE = {}));
16812
18716
  /**
16813
- * Creates the spot html string based on the provided spot data.
18717
+ * Returns the HTML element for the given spot.
16814
18718
  *
16815
- * @param {ISpot} data - The spot data.
18719
+ * @param {ISpot} spot - The spot object.
18720
+ * @param {ISpotTemplateConfig} config - The spot template configuration.
16816
18721
  *
16817
- * @return {string} - The spot html string.
18722
+ * @return {HTMLElement | null} - The HTML element for the given spot or null if the spot template is not found.
16818
18723
  */
16819
- const GET_SPOT_TEMPLATE_HTML_STRING = (data) => {
18724
+ const SPOT_TEMPLATE_HTML_ELEMENT = (spot, config) => {
16820
18725
  const templates = {
18726
+ // Reserve Bar Spot Templates
16821
18727
  [exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_THREE_TILE]: {
16822
18728
  rbHomepageHeroThreeTile: rbHomepageHeroThreeTileTemplate,
16823
18729
  },
@@ -16845,6 +18751,7 @@ const GET_SPOT_TEMPLATE_HTML_STRING = (data) => {
16845
18751
  [exports.RMN_SPOT_TYPE.RB_PRODUCT_UPCS]: {
16846
18752
  rbProductUpcs: () => '', // No template for this spot type, it will be handled by ReserveBar App.
16847
18753
  },
18754
+ // IAB Standard Spot Templates
16848
18755
  [exports.RMN_SPOT_TYPE.BILLBOARD]: {
16849
18756
  billboardV1: billboardV1Template,
16850
18757
  billboardV2: billboardV2Template,
@@ -16871,232 +18778,770 @@ const GET_SPOT_TEMPLATE_HTML_STRING = (data) => {
16871
18778
  inTextV1: inTextV1Template,
16872
18779
  },
16873
18780
  };
16874
- const spotVariants = templates[data.spot];
18781
+ const spotVariants = templates[spot.spot];
16875
18782
  if (!spotVariants) {
16876
- return '';
18783
+ return null;
16877
18784
  }
16878
- const variantTemplate = spotVariants[data.variant];
18785
+ const variantTemplate = spotVariants[spot.variant];
16879
18786
  if (!variantTemplate) {
16880
- return '';
18787
+ return null;
16881
18788
  }
16882
- return variantTemplate(data);
18789
+ // Generate a highly unique prefix to avoid conflicts with other elements.
18790
+ const prefix = 's' + UniqueIdGenerator.generate().toLowerCase();
18791
+ const spotHtmlString = variantTemplate(spot, { ...config, prefix });
18792
+ return spotHtmlStringToElement(spotHtmlString);
16883
18793
  };
16884
18794
 
16885
- let SpotElement;
16886
- if (typeof window !== 'undefined' && typeof window.customElements !== 'undefined') {
16887
- class CustomSpotElement extends HTMLElement {
16888
- constructor() {
16889
- super();
16890
- this.hasCustomContent = false;
16891
- this.handleClick = this.handleClick.bind(this);
16892
- this.attachShadow({ mode: 'open' });
16893
- }
16894
- connectedCallback() {
16895
- this.hasCustomContent = Boolean(this.customContent);
16896
- this.addEventListener('click', this.handleClick);
16897
- this.setupIntersectionObserver();
16898
- this.render();
18795
+ // For the moment, we will only focus on sites that use Google Analytics,
18796
+ // but we will add support for other analytics tools in the future.
18797
+ var AnalyticsTool;
18798
+ (function (AnalyticsTool) {
18799
+ AnalyticsTool["GoogleAnalytics"] = "google-analytics";
18800
+ AnalyticsTool["Other"] = "Other";
18801
+ })(AnalyticsTool || (AnalyticsTool = {}));
18802
+
18803
+ class DataLayerMonitor {
18804
+ constructor() {
18805
+ if (!window.dataLayer) {
18806
+ return;
16899
18807
  }
16900
- disconnectedCallback() {
16901
- var _a;
16902
- (_a = this.observer) === null || _a === void 0 ? void 0 : _a.disconnect();
16903
- this.removeEventListener('click', this.handleClick);
18808
+ this.originalPush = window.dataLayer.push;
18809
+ }
18810
+ static getInstance() {
18811
+ if (!DataLayerMonitor.instance) {
18812
+ DataLayerMonitor.instance = new DataLayerMonitor();
16904
18813
  }
16905
- attributeChangedCallback(_name, oldValue, newValue) {
16906
- if (oldValue !== newValue) {
16907
- this.render();
18814
+ return DataLayerMonitor.instance;
18815
+ }
18816
+ setListener(listener) {
18817
+ this.listener = listener;
18818
+ }
18819
+ start() {
18820
+ window.dataLayer.push = (...args) => {
18821
+ const result = this.originalPush.apply(window.dataLayer, args);
18822
+ const pushedEvent = args[0];
18823
+ if (this.listener) {
18824
+ const normalizedData = this.cleanEventData(pushedEvent);
18825
+ if (normalizedData) {
18826
+ this.listener(normalizedData);
18827
+ }
16908
18828
  }
18829
+ return result;
18830
+ };
18831
+ }
18832
+ cleanEventData(data) {
18833
+ const eventName = getEventTypeFromRawEvent(data.event);
18834
+ if (!eventName) {
18835
+ return null;
16909
18836
  }
16910
- render() {
16911
- if (!this.shadowRoot || !this.data)
16912
- return;
16913
- const { style, wrapper, slot } = SPOT_ELEMENT_TEMPLATE(this.data.width, this.data.height, this.hasCustomContent);
16914
- this.shadowRoot.replaceChildren(style, slot);
16915
- if (this.hasCustomContent) {
16916
- this.setCustomContent();
16917
- }
16918
- if (!this.hasCustomContent) {
16919
- wrapper.innerHTML = GET_SPOT_TEMPLATE_HTML_STRING(this.data);
16920
- this.shadowRoot.appendChild(wrapper);
16921
- }
18837
+ const productIds = extractDeepIds(data.value);
18838
+ return {
18839
+ event: eventName,
18840
+ productIds,
18841
+ };
18842
+ }
18843
+ stop() {
18844
+ if (this.originalPush) {
18845
+ window.dataLayer.push = this.originalPush;
16922
18846
  }
16923
- setCustomContent() {
16924
- const wrapper = document.createElement('div');
16925
- wrapper.setAttribute('slot', SPOT_ELEMENT_SLOT_NAME);
16926
- if (typeof this.customContent === 'string') {
16927
- wrapper.innerHTML = this.customContent;
16928
- }
16929
- if (this.customContent instanceof HTMLElement) {
16930
- wrapper.appendChild(this.customContent);
16931
- }
16932
- this.appendChild(wrapper);
18847
+ this.listener = undefined;
18848
+ }
18849
+ }
18850
+
18851
+ // @TODO: Add support for user to push events to our own data layer, if they don't use any analytics tool.
18852
+ // window.rmnDataLayer = window.rmnDataLayer || [];
18853
+ class MonitorService {
18854
+ constructor() {
18855
+ const analyticsTool = this.detectAnalyticsTool();
18856
+ switch (analyticsTool) {
18857
+ case AnalyticsTool.GoogleAnalytics:
18858
+ this.implementedMonitor = DataLayerMonitor.getInstance();
18859
+ break;
18860
+ case AnalyticsTool.Other:
18861
+ default:
18862
+ console.warn('This site uses an unsupported analytics tool.');
18863
+ break;
16933
18864
  }
16934
- setupIntersectionObserver() {
16935
- const options = {
16936
- root: null,
16937
- rootMargin: '0px',
16938
- threshold: 0.5, // The element is considered visible when 50% of it is visible
16939
- };
16940
- this.observer = new IntersectionObserver((entries) => {
16941
- var _a;
16942
- if (entries[0].isIntersecting) {
16943
- this.registerEvent(exports.RMN_SPOT_EVENT.IMPRESSION);
16944
- (_a = this.observer) === null || _a === void 0 ? void 0 : _a.disconnect();
16945
- }
16946
- }, options);
16947
- this.observer.observe(this);
18865
+ if (analyticsTool === AnalyticsTool.Other) {
18866
+ return;
16948
18867
  }
16949
- handleClick() {
16950
- this.registerEvent(exports.RMN_SPOT_EVENT.CLICK);
18868
+ this.pubSubService = PubsubService.getInstance();
18869
+ this.localStorageService = LocalStorageService.getInstance();
18870
+ }
18871
+ static getInstance() {
18872
+ if (!MonitorService.instance) {
18873
+ MonitorService.instance = new MonitorService();
16951
18874
  }
16952
- async registerEvent(event) {
16953
- var _a, _b;
16954
- if (!this.data)
16955
- return;
16956
- const shouldRedirectOnClick = this.getAttribute(ENUM_SPOT_ELEMENT_ATTRIBUTE.REDIRECT_ON_CLICK) === 'true';
16957
- const eventUrl = (_b = (_a = this.data.events.find((e) => e.event === event)) === null || _a === void 0 ? void 0 : _a.url) !== null && _b !== void 0 ? _b : '';
16958
- try {
16959
- const options = {
16960
- method: 'POST',
16961
- redirect: event === exports.RMN_SPOT_EVENT.CLICK && shouldRedirectOnClick ? 'follow' : 'manual',
16962
- };
16963
- const response = await fetch(eventUrl, options);
16964
- if (response.ok && event === exports.RMN_SPOT_EVENT.CLICK && shouldRedirectOnClick) {
16965
- window.location.href = this.getRedirectUrlFromPayload(eventUrl);
18875
+ return MonitorService.instance;
18876
+ }
18877
+ start() {
18878
+ if (!this.implementedMonitor)
18879
+ return;
18880
+ this.implementedMonitor.setListener(async (eventData) => {
18881
+ var _a;
18882
+ await this.matchAndFireEvent(eventData, (_a = this.localStorageService) === null || _a === void 0 ? void 0 : _a.getSpots());
18883
+ });
18884
+ this.implementedMonitor.start();
18885
+ }
18886
+ async matchAndFireEvent(eventData, spots) {
18887
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
18888
+ if (!spots)
18889
+ return;
18890
+ const eventProductIds = new Set(eventData.productIds);
18891
+ for (const spot of Object.values(spots)) {
18892
+ if (!spot.productIds.length)
18893
+ continue;
18894
+ const hasCommonProductIds = spot.productIds.find((productId) => eventProductIds.has(productId));
18895
+ if (hasCommonProductIds) {
18896
+ switch (eventData.event) {
18897
+ case exports.RMN_SPOT_EVENT.ADD_TO_CART:
18898
+ await this.fireAndPublishSpotEvent({
18899
+ spotEvent: exports.RMN_SPOT_EVENT.ADD_TO_CART,
18900
+ eventUrl: (_b = (_a = spot.events.find((event) => event.event === exports.RMN_SPOT_EVENT.ADD_TO_CART)) === null || _a === void 0 ? void 0 : _a.url) !== null && _b !== void 0 ? _b : '',
18901
+ placementId: spot.placementId,
18902
+ spotId: spot.spotId,
18903
+ });
18904
+ break;
18905
+ case exports.RMN_SPOT_EVENT.REMOVE_FROM_CART:
18906
+ await this.fireAndPublishSpotEvent({
18907
+ spotEvent: exports.RMN_SPOT_EVENT.REMOVE_FROM_CART,
18908
+ eventUrl: (_d = (_c = spot.events.find((event) => event.event === exports.RMN_SPOT_EVENT.REMOVE_FROM_CART)) === null || _c === void 0 ? void 0 : _c.url) !== null && _d !== void 0 ? _d : '',
18909
+ placementId: spot.placementId,
18910
+ spotId: spot.spotId,
18911
+ });
18912
+ break;
18913
+ case exports.RMN_SPOT_EVENT.PURCHASE:
18914
+ await this.fireAndPublishSpotEvent({
18915
+ spotEvent: exports.RMN_SPOT_EVENT.PURCHASE,
18916
+ eventUrl: (_f = (_e = spot.events.find((event) => event.event === exports.RMN_SPOT_EVENT.PURCHASE)) === null || _e === void 0 ? void 0 : _e.url) !== null && _f !== void 0 ? _f : '',
18917
+ placementId: spot.placementId,
18918
+ spotId: spot.spotId,
18919
+ });
18920
+ break;
18921
+ case exports.RMN_SPOT_EVENT.ADD_TO_WISHLIST:
18922
+ await this.fireAndPublishSpotEvent({
18923
+ spotEvent: exports.RMN_SPOT_EVENT.ADD_TO_WISHLIST,
18924
+ eventUrl: (_h = (_g = spot.events.find((event) => event.event === exports.RMN_SPOT_EVENT.ADD_TO_WISHLIST)) === null || _g === void 0 ? void 0 : _g.url) !== null && _h !== void 0 ? _h : '',
18925
+ placementId: spot.placementId,
18926
+ spotId: spot.spotId,
18927
+ });
18928
+ break;
18929
+ case exports.RMN_SPOT_EVENT.BUY_NOW:
18930
+ await this.fireAndPublishSpotEvent({
18931
+ spotEvent: exports.RMN_SPOT_EVENT.BUY_NOW,
18932
+ eventUrl: (_k = (_j = spot.events.find((event) => event.event === exports.RMN_SPOT_EVENT.BUY_NOW)) === null || _j === void 0 ? void 0 : _j.url) !== null && _k !== void 0 ? _k : '',
18933
+ placementId: spot.placementId,
18934
+ spotId: spot.spotId,
18935
+ });
18936
+ break;
16966
18937
  }
16967
18938
  }
16968
- catch (error) {
16969
- console.error(`Rmn error sending ${event} event:`, error);
16970
- }
16971
18939
  }
16972
- getRedirectUrlFromPayload(url) {
16973
- var _a, _b;
16974
- const base64String = (_a = new URL(url).searchParams.get('e')) !== null && _a !== void 0 ? _a : '';
16975
- try {
16976
- const data = JSON.parse(atob(base64String));
16977
- return (_b = data.ur) !== null && _b !== void 0 ? _b : '';
16978
- }
16979
- catch (_c) {
16980
- return '';
16981
- }
18940
+ }
18941
+ async fireAndPublishSpotEvent({ spotEvent, eventUrl, placementId, spotId, }) {
18942
+ await fireEvent({
18943
+ event: spotEvent,
18944
+ eventUrl,
18945
+ });
18946
+ if (!this.pubSubService)
18947
+ return;
18948
+ this.pubSubService.publish(exports.RMN_EVENT.SPOT_EVENT, {
18949
+ eventType: spotEvent,
18950
+ placementId,
18951
+ spotId,
18952
+ });
18953
+ }
18954
+ detectAnalyticsTool() {
18955
+ let analyticsTool = AnalyticsTool.Other;
18956
+ // Check for Google Analytics
18957
+ if (typeof window.ga !== 'undefined') {
18958
+ analyticsTool = AnalyticsTool.GoogleAnalytics;
18959
+ }
18960
+ // Check for Google Analytics 4
18961
+ if (typeof window.gtag !== 'undefined') {
18962
+ analyticsTool = AnalyticsTool.GoogleAnalytics;
18963
+ }
18964
+ // Check for Google Tag Manager
18965
+ if (typeof window.google_tag_manager !== 'undefined') {
18966
+ analyticsTool = AnalyticsTool.GoogleAnalytics;
16982
18967
  }
18968
+ // @TODO: Add support for other analytics tools
18969
+ // Check for Heap Analytics
18970
+ // Check for Mixpanel
18971
+ // Check for Woopra
18972
+ // Check for Segment
18973
+ // Check for Amplitude
18974
+ return analyticsTool;
16983
18975
  }
16984
- CustomSpotElement.observedAttributes = Object.values(ENUM_SPOT_ELEMENT_ATTRIBUTE);
16985
- SpotElement = CustomSpotElement;
16986
18976
  }
16987
18977
 
16988
- class SpotHtmlService {
18978
+ class EventService {
18979
+ constructor() {
18980
+ this.pubSubService = PubsubService.getInstance();
18981
+ this.localStorageService = LocalStorageService.getInstance();
18982
+ this.activeSpots = new Map();
18983
+ this.spotStates = new Map();
18984
+ this.intersectionObserver = new IntersectionObserverService();
18985
+ // Start the user monitor, which will track and check user interactions
18986
+ MonitorService.getInstance().start();
18987
+ }
16989
18988
  static getInstance() {
16990
- return SingletonManager.getInstance('SpotHtmlService', () => new SpotHtmlService());
18989
+ if (!EventService.instance) {
18990
+ EventService.instance = new EventService();
18991
+ }
18992
+ return EventService.instance;
18993
+ }
18994
+ subscribe(eventType, callback) {
18995
+ return this.pubSubService.subscribe(eventType, callback);
18996
+ }
18997
+ publish(eventType, data) {
18998
+ this.pubSubService.publish(eventType, data);
18999
+ }
19000
+ registerSpot(params) {
19001
+ const { placementId, spot, spotElement } = params;
19002
+ this.activeSpots.set(placementId, { spotElement });
19003
+ // Fire impression event
19004
+ this.fireImpressionEvent(placementId, spot);
19005
+ // Handle intersection observer
19006
+ this.handleIntersectionObserver(placementId, spot, spotElement);
19007
+ // Attach click event listener
19008
+ spotElement.addEventListener('click', async () => await this.handleClick(params));
19009
+ }
19010
+ unregisterSpot(placementId) {
19011
+ const placementIdClean = placementId.replace('#', '');
19012
+ const spotData = this.activeSpots.get(placementIdClean);
19013
+ if (!spotData) {
19014
+ this.handleSpotState(placementIdClean, {
19015
+ state: {
19016
+ error: `Active spot with placementId ${placementIdClean} not found.`,
19017
+ },
19018
+ });
19019
+ return;
19020
+ }
19021
+ this.intersectionObserver.unobserve(spotData.spotElement);
19022
+ this.handleSpotState(placementIdClean, {
19023
+ dom: {
19024
+ spotElement: undefined,
19025
+ visibleOnViewport: false,
19026
+ },
19027
+ state: {
19028
+ unmounted: true,
19029
+ mounted: false,
19030
+ },
19031
+ });
19032
+ this.activeSpots.delete(placementIdClean);
19033
+ const placementElement = document.getElementById(placementIdClean);
19034
+ if (!placementElement) {
19035
+ this.handleSpotState(placementIdClean, {
19036
+ state: {
19037
+ error: `Placement element with id ${placementIdClean} not found.`,
19038
+ },
19039
+ });
19040
+ return;
19041
+ }
19042
+ placementElement.innerHTML = '';
16991
19043
  }
16992
19044
  /**
16993
- * Creates the html element based on the provided spot data using shadow dom.
16994
- *
16995
- * This method is only available in browser environments.
19045
+ * Updates the state of a spot.
16996
19046
  *
16997
- * @param {ISpot} spot - The spot data.
16998
- * @param {ICreateSpotElementConfig} config - The configuration object.
16999
- * @param {ICreateSpotElementConfig.fluid} config.fluid - If the spot should be fluid or not.
17000
- * @param {ICreateSpotElementConfig.customContent} config.customContent - Use a custom html element/string.
17001
- * @param {ICreateSpotElementConfig.redirectOnClick} config.redirectOnClick - If the spot should redirect on click.
19047
+ * @param {string} placementId - The placement ID of the spot.
19048
+ * @param {Partial<ILifecycleState>} updates - The updates to apply to the spot state.
19049
+ * @param {boolean} publish - Whether to publish the updated state.
17002
19050
  *
17003
- * @return {HTMLElement | null} - The spot html element or null if the browser environment is not available.
17004
- */
17005
- createSpotHtmlElement(spot, config) {
17006
- var _a, _b;
17007
- if (!this.ensureBrowserEnvironmentAndDefineElement()) {
17008
- return null;
17009
- }
17010
- const isFluid = (_a = config === null || config === void 0 ? void 0 : config.fluid) !== null && _a !== void 0 ? _a : false;
17011
- const shouldRedirectOnClick = (_b = config === null || config === void 0 ? void 0 : config.redirectOnClick) !== null && _b !== void 0 ? _b : true;
17012
- const element = document.createElement(SPOT_ELEMENT_TAG);
17013
- element.setAttribute(ENUM_SPOT_ELEMENT_ATTRIBUTE.WIDTH, spot.width.toString());
17014
- element.setAttribute(ENUM_SPOT_ELEMENT_ATTRIBUTE.HEIGHT, spot.height.toString());
17015
- element.setAttribute(ENUM_SPOT_ELEMENT_ATTRIBUTE.FLUID, isFluid.toString());
17016
- element.setAttribute(ENUM_SPOT_ELEMENT_ATTRIBUTE.REDIRECT_ON_CLICK, shouldRedirectOnClick.toString());
17017
- // Share the spot data with the element
17018
- element.data = spot;
17019
- // Set custom content
17020
- if (config === null || config === void 0 ? void 0 : config.customContent) {
17021
- element.customContent = config.customContent;
17022
- }
17023
- return element;
17024
- }
17025
- /**
17026
- * @returns {boolean} - True if the browser environment is available and the element is defined.
19051
+ * @returns {void}
17027
19052
  */
17028
- ensureBrowserEnvironmentAndDefineElement() {
17029
- if (typeof window === 'undefined' || typeof document === 'undefined') {
17030
- console.warn('LiquidCommerce Rmn Sdk: createSpotElement is only available in browser environments!!!');
17031
- return false;
19053
+ handleSpotState(placementId, updates, publish = true) {
19054
+ let currentState = this.spotStates.get(placementId);
19055
+ if (!currentState) {
19056
+ currentState = {
19057
+ identifier: {
19058
+ placementId,
19059
+ spotId: '',
19060
+ spotType: '',
19061
+ },
19062
+ dom: {
19063
+ spotElement: undefined,
19064
+ visibleOnViewport: false,
19065
+ },
19066
+ state: {
19067
+ mounted: false,
19068
+ unmounted: false,
19069
+ loading: false,
19070
+ error: undefined,
19071
+ },
19072
+ displayConfig: {
19073
+ isCarousel: false,
19074
+ isCarouselItem: false,
19075
+ isSingleItem: false,
19076
+ },
19077
+ };
17032
19078
  }
17033
- if (!customElements.get(SPOT_ELEMENT_TAG)) {
17034
- customElements.define(SPOT_ELEMENT_TAG, SpotElement);
19079
+ const merged = this.deepMerge(currentState, updates);
19080
+ this.spotStates.set(placementId, merged);
19081
+ if (publish) {
19082
+ this.pubSubService.publish(exports.RMN_EVENT.LIFECYCLE_STATE, this.spotStates.get(placementId));
17035
19083
  }
17036
- return true;
19084
+ }
19085
+ deepMerge(current, updates) {
19086
+ return {
19087
+ identifier: updates.identifier
19088
+ ? { ...current.identifier, ...updates.identifier }
19089
+ : current.identifier,
19090
+ dom: updates.dom ? { ...current.dom, ...updates.dom } : current.dom,
19091
+ state: updates.state ? { ...current.state, ...updates.state } : current.state,
19092
+ displayConfig: updates.displayConfig
19093
+ ? { ...current.displayConfig, ...updates.displayConfig }
19094
+ : current.displayConfig,
19095
+ };
19096
+ }
19097
+ async handleClick({ placementId, spot }) {
19098
+ var _a, _b, _c;
19099
+ this.pubSubService.publish(exports.RMN_EVENT.SPOT_EVENT, {
19100
+ eventType: exports.RMN_SPOT_EVENT.CLICK,
19101
+ placementId,
19102
+ spotId: spot.id,
19103
+ });
19104
+ await fireEvent({
19105
+ event: exports.RMN_SPOT_EVENT.CLICK,
19106
+ eventUrl: (_b = (_a = spot.events.find((event) => event.event === exports.RMN_SPOT_EVENT.CLICK)) === null || _a === void 0 ? void 0 : _a.url) !== null && _b !== void 0 ? _b : '',
19107
+ });
19108
+ // Save spot to local storage for event tracking
19109
+ this.localStorageService.setSpot(spot.id, {
19110
+ placementId,
19111
+ spotId: spot.id,
19112
+ spotType: spot.spot,
19113
+ events: spot.events,
19114
+ productIds: (_c = spot.productIds) !== null && _c !== void 0 ? _c : [],
19115
+ });
19116
+ }
19117
+ handleIntersectionObserver(placementId, _spot, spotElement) {
19118
+ const spotIsVisibleCallback = async () => {
19119
+ this.intersectionObserver.unobserve(spotElement);
19120
+ this.handleSpotState(placementId, {
19121
+ dom: {
19122
+ spotElement,
19123
+ visibleOnViewport: true,
19124
+ },
19125
+ });
19126
+ };
19127
+ this.intersectionObserver.observe(spotElement, spotIsVisibleCallback);
19128
+ }
19129
+ fireImpressionEvent(placementId, spot) {
19130
+ this.pubSubService.publish(exports.RMN_EVENT.SPOT_EVENT, {
19131
+ eventType: exports.RMN_SPOT_EVENT.IMPRESSION,
19132
+ placementId,
19133
+ spotId: spot.id,
19134
+ });
19135
+ (async () => {
19136
+ var _a, _b;
19137
+ await fireEvent({
19138
+ event: exports.RMN_SPOT_EVENT.IMPRESSION,
19139
+ eventUrl: (_b = (_a = spot.events.find((event) => event.event === exports.RMN_SPOT_EVENT.IMPRESSION)) === null || _a === void 0 ? void 0 : _a.url) !== null && _b !== void 0 ? _b : '',
19140
+ });
19141
+ })();
17037
19142
  }
17038
19143
  }
17039
19144
 
17040
- class SpotSelectionService extends BaseApi {
19145
+ const SELECTION_API_PATH = '/spots/selection';
19146
+
19147
+ class SelectionService extends BaseApi {
17041
19148
  constructor(auth) {
17042
19149
  super(auth);
17043
19150
  }
17044
19151
  static getInstance(auth) {
17045
- return SingletonManager.getInstance('SpotSelectionService', () => new SpotSelectionService(auth));
19152
+ return SingletonManager.getInstance('SelectionService', () => new SelectionService(auth));
17046
19153
  }
17047
19154
  /**
17048
19155
  * Makes a selection request on our server based on the provided data.
17049
19156
  *
17050
19157
  * @param {ISpotSelectionParams} data - Spots selection parameters.
17051
19158
  *
17052
- * @return {Promise<ISpots>} - The spots response object.
19159
+ * @return {Promise<ISpots | { error: string }>} - The spots response object.
17053
19160
  */
17054
19161
  async spotSelection(data) {
17055
- const { isOk, val, isErr } = await this.post(SPOTS_SELECTION_API_PATH, data, {});
19162
+ const { isOk, val, isErr } = await this.post(SELECTION_API_PATH, data, {});
17056
19163
  if (isErr) {
17057
- throw new Error(`There was an error during spot selection: (${isErr === null || isErr === void 0 ? void 0 : isErr.errorMessage})`);
19164
+ return { error: `There was an error during spot selection: (${isErr === null || isErr === void 0 ? void 0 : isErr.errorMessage})` };
17058
19165
  }
17059
19166
  if (isOk && val && val.data && (val === null || val === void 0 ? void 0 : val.refresh.token)) {
17060
19167
  this.authInfo.authenticated = true;
17061
19168
  this.authInfo.token = val.refresh.token;
17062
19169
  return val.data.spots;
17063
19170
  }
17064
- throw new Error('Spot selection response was not successful');
19171
+ return { error: 'Spot selection response was not successful' };
17065
19172
  }
17066
19173
  }
17067
19174
 
17068
19175
  class LiquidCommerceRmnClient {
17069
19176
  constructor(auth) {
17070
- this.spotSelectionService = SpotSelectionService.getInstance(auth);
17071
- this.spotHtmlService = SpotHtmlService.getInstance();
19177
+ this.selectionService = SelectionService.getInstance(auth);
19178
+ this.elementService = ElementService.getInstance();
19179
+ this.eventService = EventService.getInstance();
17072
19180
  }
17073
19181
  /**
17074
19182
  * Makes a selection request on our server based on the provided data.
17075
19183
  *
17076
19184
  * To create a spot html element, use the RmnCreateSpotElement function.
17077
19185
  *
17078
- * @param {ISpotSelectionParams} data - Spots selection parameters.
19186
+ * @param {ISpotSelectionParams} params - Spots selection parameters.
17079
19187
  *
17080
- * @return {Promise<ISpots>} - The spots response object.
19188
+ * @return {Promise<ISpots | { error : string }>} - The spots response object.
17081
19189
  */
17082
- async spotSelection(data) {
17083
- return this.spotSelectionService.spotSelection(data);
19190
+ async spotSelection(params) {
19191
+ return this.selectionService.spotSelection(params);
19192
+ }
19193
+ /**
19194
+ * Injects the spot elements into their provided placement.
19195
+ *
19196
+ * @param {IInjectSpotElementParams} params - Parameters for injecting spot elements.
19197
+ *
19198
+ * @return {Promise<void>} - A promise that resolves when the spot elements are injected.
19199
+ */
19200
+ async injectSpotElement(params) {
19201
+ var _a;
19202
+ if (typeof window === 'undefined' || typeof document === 'undefined') {
19203
+ console.warn('LiquidCommerce Rmn Sdk: Methods which create elements are only available in browser environments.');
19204
+ return;
19205
+ }
19206
+ const config = params.config;
19207
+ let inject = params.inject;
19208
+ if (!inject.length) {
19209
+ // Handle no spots error state
19210
+ this.eventService.handleSpotState('all', {
19211
+ state: {
19212
+ error: 'No spot elements provided for injection.',
19213
+ loading: false,
19214
+ mounted: false,
19215
+ },
19216
+ });
19217
+ return;
19218
+ }
19219
+ // Update the state of the spots to loading
19220
+ this.updateSpotsState(inject);
19221
+ // Prevent duplicate placement ids
19222
+ const hasDuplicatePlacementIds = this.preventDuplicateSpotPlacementIds(inject);
19223
+ if (!hasDuplicatePlacementIds) {
19224
+ return;
19225
+ }
19226
+ // Prevent non-existent spot types
19227
+ inject = this.preventNonExistentSpotTypes(inject);
19228
+ // Make the spot selection request
19229
+ const response = await this.spotSelectionRequest({ ...params, inject });
19230
+ // const response = await this.useSpotSelectionExample(inject);
19231
+ // Handle the response
19232
+ if (typeof response === 'object' && 'error' in response) {
19233
+ // Handle request error state
19234
+ this.eventService.handleSpotState('all', {
19235
+ state: {
19236
+ error: response.error,
19237
+ mounted: false,
19238
+ loading: false,
19239
+ },
19240
+ });
19241
+ return;
19242
+ }
19243
+ for (const item of inject) {
19244
+ const itemConfig = (_a = item.config) !== null && _a !== void 0 ? _a : config;
19245
+ const spots = response[item.placementId];
19246
+ if (!(spots === null || spots === void 0 ? void 0 : spots.length)) {
19247
+ // Handle no spots found error state
19248
+ this.eventService.handleSpotState(item.placementId, {
19249
+ state: {
19250
+ error: `No spots found for type "${item.spotType}".`,
19251
+ mounted: false,
19252
+ loading: false,
19253
+ },
19254
+ });
19255
+ continue;
19256
+ }
19257
+ const placementId = item.placementId.replace('#', '');
19258
+ const placement = document.getElementById(placementId);
19259
+ if (!placement) {
19260
+ // Handle placement not found error state
19261
+ this.eventService.handleSpotState(item.placementId, {
19262
+ state: {
19263
+ error: `Placement not found for id "${placementId}".`,
19264
+ mounted: false,
19265
+ loading: false,
19266
+ },
19267
+ });
19268
+ continue;
19269
+ }
19270
+ // Take over placement styles
19271
+ placement.removeAttribute('style');
19272
+ placement.removeAttribute('class');
19273
+ Object.assign(placement.style, {
19274
+ width: '100%',
19275
+ height: '100%',
19276
+ display: 'flex',
19277
+ justifyContent: 'center',
19278
+ });
19279
+ // Handle single spot
19280
+ if (spots.length === 1) {
19281
+ const isInjected = this.injectOneSpotElement(item, placement, spots[0], itemConfig);
19282
+ if (!isInjected) {
19283
+ continue;
19284
+ }
19285
+ }
19286
+ // Handle multiple spots (carousel)
19287
+ if (spots.length > 1) {
19288
+ const isInjected = this.injectCarouselSpotElement(placement, spots, itemConfig);
19289
+ if (!isInjected) {
19290
+ continue;
19291
+ }
19292
+ }
19293
+ }
17084
19294
  }
17085
19295
  /**
17086
- * Creates the spot html element based on the provided data using shadow dom.
19296
+ * Makes a selection request on our server based on the provided data.
19297
+ *
19298
+ * @param {IInjectSpotElementParams} params - Parameters for injecting spot elements.
19299
+ *
19300
+ * @return {Promise<ISpots | {error: string}>} - The spots response object.
19301
+ */
19302
+ async spotSelectionRequest(params) {
19303
+ const { inject, filter, config } = params;
19304
+ const request = {
19305
+ url: config === null || config === void 0 ? void 0 : config.url,
19306
+ filter,
19307
+ spots: inject.map((item) => ({
19308
+ placementId: item.placementId,
19309
+ spot: item.spotType,
19310
+ count: item === null || item === void 0 ? void 0 : item.count,
19311
+ ...item === null || item === void 0 ? void 0 : item.filter,
19312
+ })),
19313
+ };
19314
+ return this.spotSelection(request);
19315
+ }
19316
+ /**
19317
+ * Injects a carousel element with the provided spots into the placement.
19318
+ *
19319
+ * @param {HTMLElement} placement - The placement element.
19320
+ * @param {ISpot[]} spots - The spot data.
19321
+ * @param {IInjectSpotElementConfig} config - The configuration object.
17087
19322
  *
17088
- * This method is useful when you are initializing the client in a browser environment, so you can create the spot html element directly from the RmnClient instance.
19323
+ * @return {void}
19324
+ */
19325
+ injectCarouselSpotElement(placement, spots, config) {
19326
+ var _a;
19327
+ const carouselSlides = [];
19328
+ for (const spotItem of spots) {
19329
+ this.eventService.handleSpotState(placement.id, {
19330
+ identifier: {
19331
+ placementId: placement.id,
19332
+ spotType: spotItem.spot,
19333
+ spotId: spotItem.id,
19334
+ },
19335
+ displayConfig: {
19336
+ isSingleItem: false,
19337
+ isCarousel: true,
19338
+ isCarouselItem: true,
19339
+ },
19340
+ });
19341
+ const spot = this.elementService.overrideSpotColors(spotItem, config === null || config === void 0 ? void 0 : config.colors);
19342
+ const content = SPOT_TEMPLATE_HTML_ELEMENT(spot, { overlay: config === null || config === void 0 ? void 0 : config.overlay });
19343
+ if (!content) {
19344
+ this.eventService.handleSpotState(placement.id, {
19345
+ state: {
19346
+ error: `Failed to inject carousel spot item element. Could not create element for type "${spot.spot}".`,
19347
+ mounted: false,
19348
+ loading: false,
19349
+ },
19350
+ });
19351
+ continue;
19352
+ }
19353
+ this.eventService.registerSpot({
19354
+ spot,
19355
+ placementId: placement.id,
19356
+ spotElement: content,
19357
+ });
19358
+ carouselSlides.push(content);
19359
+ }
19360
+ // Get the max width and height of the spots
19361
+ const { maxWidth, maxHeight } = spots.reduce((max, spot) => ({
19362
+ maxWidth: Math.max(max.maxWidth, spot.width),
19363
+ maxHeight: Math.max(max.maxHeight, spot.height),
19364
+ }), { maxWidth: 0, maxHeight: 0 });
19365
+ // Create the carousel element
19366
+ const carouselElement = this.elementService.createCarouselElement({
19367
+ slides: carouselSlides,
19368
+ config: {
19369
+ fluid: config === null || config === void 0 ? void 0 : config.fluid,
19370
+ width: maxWidth,
19371
+ height: maxHeight,
19372
+ minScale: (_a = config === null || config === void 0 ? void 0 : config.minScale) !== null && _a !== void 0 ? _a : 0.25, // Scale down to 25% of the original size
19373
+ ...config === null || config === void 0 ? void 0 : config.carousel,
19374
+ },
19375
+ });
19376
+ if (!carouselElement) {
19377
+ this.eventService.handleSpotState(placement.id, {
19378
+ state: {
19379
+ error: `Failed to inject spot carousel element. Could not create spot carousel element.`,
19380
+ mounted: false,
19381
+ loading: false,
19382
+ },
19383
+ });
19384
+ return false;
19385
+ }
19386
+ placement.replaceChildren(carouselElement);
19387
+ this.eventService.handleSpotState(placement.id, {
19388
+ dom: {
19389
+ spotElement: carouselElement,
19390
+ visibleOnViewport: false,
19391
+ },
19392
+ state: {
19393
+ mounted: true,
19394
+ loading: false,
19395
+ error: undefined,
19396
+ },
19397
+ });
19398
+ return true;
19399
+ }
19400
+ /**
19401
+ * Injects a single spot element into the provided placement.
17089
19402
  *
19403
+ * @param {IInjectSpotElement} injectItem - The inject item data.
19404
+ * @param {HTMLElement} placement - The placement element.
17090
19405
  * @param {ISpot} spot - The spot data.
17091
- * @param {ICreateSpotElementConfig} config - The configuration object.
17092
- * @param {ICreateSpotElementConfig.fluid} config.fluid - If the spot should be fluid or not.
17093
- * @param {ICreateSpotElementConfig.customContent} config.customContent - Use a custom html element/string.
17094
- * @param {ICreateSpotElementConfig.redirectOnClick} config.redirectOnClick - If the spot should redirect on click.
19406
+ * @param {IInjectSpotElementConfig} config - The configuration object.
19407
+ *
19408
+ * @return {void}
19409
+ */
19410
+ injectOneSpotElement(injectItem, placement, spot, config) {
19411
+ var _a;
19412
+ this.eventService.handleSpotState(injectItem.placementId, {
19413
+ identifier: {
19414
+ placementId: injectItem.placementId,
19415
+ spotType: injectItem.spotType,
19416
+ spotId: spot.id,
19417
+ },
19418
+ displayConfig: {
19419
+ isSingleItem: true,
19420
+ isCarousel: false,
19421
+ isCarouselItem: false,
19422
+ },
19423
+ });
19424
+ const spotData = this.elementService.overrideSpotColors(spot, config === null || config === void 0 ? void 0 : config.colors);
19425
+ const content = SPOT_TEMPLATE_HTML_ELEMENT(spotData, { overlay: config === null || config === void 0 ? void 0 : config.overlay });
19426
+ if (!content) {
19427
+ this.eventService.handleSpotState(injectItem.placementId, {
19428
+ state: {
19429
+ error: `Failed to inject spot element. Could not create element for type "${injectItem.spotType}".`,
19430
+ mounted: false,
19431
+ loading: false,
19432
+ },
19433
+ });
19434
+ return false;
19435
+ }
19436
+ // Create the spot element
19437
+ const spotElement = this.elementService.createSpotElement({
19438
+ content,
19439
+ config: {
19440
+ fluid: config === null || config === void 0 ? void 0 : config.fluid,
19441
+ spot: spot.spot,
19442
+ width: spot.width,
19443
+ height: spot.height,
19444
+ minScale: (_a = config === null || config === void 0 ? void 0 : config.minScale) !== null && _a !== void 0 ? _a : 0.25, // Scale down to 25% of the original size
19445
+ },
19446
+ });
19447
+ if (!spotElement) {
19448
+ this.eventService.handleSpotState(injectItem.placementId, {
19449
+ state: {
19450
+ error: `Failed to inject spot element. Could not create element for type "${injectItem.spotType}".`,
19451
+ mounted: false,
19452
+ loading: false,
19453
+ },
19454
+ });
19455
+ return false;
19456
+ }
19457
+ this.eventService.registerSpot({
19458
+ spot: spotData,
19459
+ placementId: injectItem.placementId,
19460
+ spotElement,
19461
+ });
19462
+ placement.replaceChildren(spotElement);
19463
+ this.eventService.handleSpotState(injectItem.placementId, {
19464
+ dom: {
19465
+ spotElement,
19466
+ visibleOnViewport: false,
19467
+ },
19468
+ state: {
19469
+ mounted: true,
19470
+ loading: false,
19471
+ error: undefined,
19472
+ },
19473
+ });
19474
+ return true;
19475
+ }
19476
+ /**
19477
+ * Prevents duplicate placement ids in the inject data.
19478
+ *
19479
+ * @param {IInjectSpotElement[]} inject - The inject data.
17095
19480
  *
17096
- * @return {HTMLElement | null} - The spot html element or null if the browser environment is not available.
19481
+ * @throws {Error} - If a duplicate placement id is found.
19482
+ *
19483
+ * @return {void}
17097
19484
  */
17098
- createSpotElement(spot, config) {
17099
- return this.spotHtmlService.createSpotHtmlElement(spot, config);
19485
+ preventDuplicateSpotPlacementIds(inject) {
19486
+ const placementIds = new Set();
19487
+ for (const item of inject) {
19488
+ if (placementIds.has(item.placementId)) {
19489
+ this.eventService.handleSpotState(item.placementId, {
19490
+ state: {
19491
+ error: `Duplicate placement id (${item.placementId}) found. Please provide a unique placement id for each spot element.`,
19492
+ mounted: false,
19493
+ loading: false,
19494
+ },
19495
+ });
19496
+ return false;
19497
+ }
19498
+ placementIds.add(item.placementId);
19499
+ }
19500
+ return true;
19501
+ }
19502
+ preventNonExistentSpotTypes(inject) {
19503
+ const newInject = [];
19504
+ for (const item of inject) {
19505
+ if (!Object.values(exports.RMN_SPOT_TYPE).includes(item.spotType)) {
19506
+ this.eventService.handleSpotState(item.placementId, {
19507
+ state: {
19508
+ error: `Invalid spot type (${item.spotType}) found. Please provide a valid spot type for each spot element.`,
19509
+ mounted: false,
19510
+ loading: false,
19511
+ },
19512
+ });
19513
+ continue;
19514
+ }
19515
+ newInject.push(item);
19516
+ }
19517
+ return newInject;
19518
+ }
19519
+ // Initialize spots with loading state and identifiers
19520
+ updateSpotsState(inject) {
19521
+ for (const item of inject) {
19522
+ this.eventService.handleSpotState(item.placementId, {
19523
+ identifier: {
19524
+ placementId: item.placementId,
19525
+ spotType: item.spotType,
19526
+ },
19527
+ state: {
19528
+ loading: true,
19529
+ mounted: false,
19530
+ error: undefined,
19531
+ },
19532
+ });
19533
+ }
19534
+ }
19535
+ useSpotSelectionExample(inject) {
19536
+ const examples = RB_SPOTS_SELECTION_EXAMPLE;
19537
+ const data = {};
19538
+ inject.map((item) => {
19539
+ var _a, _b, _c;
19540
+ data[item.placementId] = (_c = (_a = examples[item.spotType]) === null || _a === void 0 ? void 0 : _a.slice(0, (_b = item.count) !== null && _b !== void 0 ? _b : 1)) !== null && _c !== void 0 ? _c : [];
19541
+ });
19542
+ return new Promise((resolve) => {
19543
+ resolve(data);
19544
+ });
17100
19545
  }
17101
19546
  }
17102
19547
  /**
@@ -17112,26 +19557,69 @@ async function RmnClient(apiKey, config) {
17112
19557
  const credentials = await authService.initialize();
17113
19558
  return new LiquidCommerceRmnClient(credentials);
17114
19559
  }
19560
+ /**
19561
+ * Creates a new instance of the RmnEventManager.
19562
+ *
19563
+ * @return {IRmnEventManager} - The RmnEventManager instance.
19564
+ */
19565
+ function RmnEventManager() {
19566
+ const eventService = EventService.getInstance();
19567
+ return {
19568
+ /**
19569
+ * Subscribes to an event type.
19570
+ */
19571
+ subscribe: (eventType, callback
19572
+ /* eslint-disable arrow-body-style */
19573
+ ) => {
19574
+ return eventService.subscribe(eventType, callback);
19575
+ },
19576
+ /**
19577
+ * Publishes an event type.
19578
+ */
19579
+ publish: (eventType, data) => {
19580
+ eventService.publish(eventType, data);
19581
+ },
19582
+ /**
19583
+ * Destroys a spot element
19584
+ */
19585
+ destroySpot: (placementId) => {
19586
+ eventService.unregisterSpot(placementId);
19587
+ },
19588
+ };
19589
+ }
17115
19590
  /**
17116
19591
  * Creates the spot html element based on the provided data using shadow dom.
17117
19592
  *
17118
19593
  * This method is useful when you are initializing the client in a non-browser environment.
17119
- * When you request a spot selection, you will receive the spot data in server-side and return them back to the client.
19594
+ * When you request a spot selection, you will receive the spot data in server-side and return them to the client.
17120
19595
  * Then you can use this function to create the spot html element based on the provided data without the need of the RmnClient instance.
17121
19596
  *
17122
19597
  * @param {ISpot} spot - The spot data.
17123
- * @param {ICreateSpotElementConfig} config - The configuration object.
17124
- * @param {ICreateSpotElementConfig.fluid} config.fluid - If the spot should be fluid or not.
17125
- * @param {ICreateSpotElementConfig.customContent} config.customContent - Use a custom html element/string.
17126
- * @param {ICreateSpotElementConfig.redirectOnClick} config.redirectOnClick - If the spot should redirect on click.
19598
+ * @param {IRmnCreateSpotElementConfig} config - The configuration object.
17127
19599
  *
17128
19600
  * @return {HTMLElement | null} - The spot html element or null if the browser environment is not available.
17129
19601
  */
17130
19602
  function RmnCreateSpotElement(spot, config) {
17131
- const spotHtmlService = SpotHtmlService.getInstance();
17132
- return spotHtmlService.createSpotHtmlElement(spot, config);
19603
+ var _a;
19604
+ const elementService = ElementService.getInstance();
19605
+ const spotData = elementService.overrideSpotColors(spot, config === null || config === void 0 ? void 0 : config.colors);
19606
+ const content = SPOT_TEMPLATE_HTML_ELEMENT(spotData, { overlay: config === null || config === void 0 ? void 0 : config.overlay });
19607
+ if (!content) {
19608
+ console.warn(`RmnSdk: Failed to create spot element for type "${spotData.spot}".`);
19609
+ return null;
19610
+ }
19611
+ return elementService.createSpotElement({
19612
+ content,
19613
+ config: {
19614
+ fluid: true,
19615
+ width: spot.width,
19616
+ height: spot.height,
19617
+ minScale: (_a = config === null || config === void 0 ? void 0 : config.minScale) !== null && _a !== void 0 ? _a : 0.25, // Scale down to 25% of the original size
19618
+ },
19619
+ });
17133
19620
  }
17134
19621
 
17135
19622
  exports.LiquidCommerceRmnClient = LiquidCommerceRmnClient;
17136
19623
  exports.RmnClient = RmnClient;
17137
19624
  exports.RmnCreateSpotElement = RmnCreateSpotElement;
19625
+ exports.RmnEventManager = RmnEventManager;