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