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

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