@liquidcommercedev/rmn-sdk 1.5.0-beta.1 → 1.5.0-beta.11

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 (28) hide show
  1. package/dist/index.cjs +1414 -602
  2. package/dist/index.esm.js +1415 -603
  3. package/dist/types/common/helpers/event-type.helper.d.ts +8 -0
  4. package/dist/types/common/helpers/index.d.ts +4 -0
  5. package/dist/types/common/helpers/utils.helper.d.ts +37 -0
  6. package/dist/types/enums.d.ts +8 -1
  7. package/dist/types/modules/element/element.interface.d.ts +5 -2
  8. package/dist/types/modules/event/event.interface.d.ts +6 -32
  9. package/dist/types/modules/event/event.service.d.ts +7 -22
  10. package/dist/types/modules/event/index.d.ts +0 -1
  11. package/dist/types/modules/{event/helpers → helper-service}/index.d.ts +1 -0
  12. package/dist/types/modules/{event/helpers → helper-service}/intersection.service.d.ts +1 -1
  13. package/dist/types/modules/helper-service/localstorage.service.d.ts +56 -0
  14. package/dist/types/modules/{event/pubsub.d.ts → helper-service/pubsub.service.d.ts} +8 -8
  15. package/dist/types/modules/{event/helpers → helper-service}/resize.service.d.ts +1 -1
  16. package/dist/types/modules/monitor/index.d.ts +2 -0
  17. package/dist/types/modules/monitor/monitor.enums.d.ts +4 -0
  18. package/dist/types/modules/monitor/monitor.interface.d.ts +11 -0
  19. package/dist/types/modules/monitor/monitor.service.d.ts +12 -0
  20. package/dist/types/modules/monitor/monitors/datalayer.monitor.d.ts +12 -0
  21. package/dist/types/modules/selection/selection.interface.d.ts +3 -1
  22. package/dist/types/rmn-client.d.ts +1 -1
  23. package/dist/types/types.d.ts +5 -4
  24. package/package.json +1 -1
  25. package/umd/liquidcommerce-rmn-sdk.min.js +1 -1
  26. package/dist/types/modules/element/component/utils.d.ts +0 -1
  27. package/dist/types/modules/event/helpers/localstorage.service.d.ts +0 -26
  28. /package/dist/types/{static.constant.d.ts → example.constant.d.ts} +0 -0
package/dist/index.esm.js CHANGED
@@ -1,6 +1,7 @@
1
1
  var RMN_SPOT_TYPE;
2
2
  (function (RMN_SPOT_TYPE) {
3
3
  // Reserve Bar Spot Types
4
+ RMN_SPOT_TYPE["RB_HOMEPAGE_HERO"] = "rbHomepageHero";
4
5
  RMN_SPOT_TYPE["RB_HOMEPAGE_HERO_THREE_TILE"] = "rbHomepageHeroThreeTile";
5
6
  RMN_SPOT_TYPE["RB_HOMEPAGE_HERO_TWO_TILE"] = "rbHomepageHeroTwoTile";
6
7
  RMN_SPOT_TYPE["RB_HOMEPAGE_HERO_FULL_IMAGE"] = "rbHomepageHeroFullImage";
@@ -52,14 +53,21 @@ var RMN_FILTER_PROPERTIES;
52
53
  RMN_FILTER_PROPERTIES["PUBLISHERS"] = "publishers";
53
54
  RMN_FILTER_PROPERTIES["SECTION"] = "section";
54
55
  })(RMN_FILTER_PROPERTIES || (RMN_FILTER_PROPERTIES = {}));
56
+ var RMN_EVENT;
57
+ (function (RMN_EVENT) {
58
+ RMN_EVENT["LIFECYCLE_STATE"] = "LIFECYCLE_STATE";
59
+ RMN_EVENT["SPOT_EVENT"] = "SPOT_EVENT";
60
+ })(RMN_EVENT || (RMN_EVENT = {}));
55
61
  var RMN_SPOT_EVENT;
56
62
  (function (RMN_SPOT_EVENT) {
57
- RMN_SPOT_EVENT["LIFECYCLE_STATE"] = "LIFECYCLE_STATE";
58
63
  RMN_SPOT_EVENT["IMPRESSION"] = "IMPRESSION";
59
64
  RMN_SPOT_EVENT["CLICK"] = "CLICK";
60
65
  RMN_SPOT_EVENT["PURCHASE"] = "PURCHASE";
61
66
  RMN_SPOT_EVENT["ADD_TO_CART"] = "ADD_TO_CART";
67
+ RMN_SPOT_EVENT["REMOVE_FROM_CART"] = "REMOVE_FROM_CART";
68
+ RMN_SPOT_EVENT["ADD_TO_CART_FROM_DETAILS"] = "ADD_TO_CART_FROM_DETAILS";
62
69
  RMN_SPOT_EVENT["ADD_TO_WISHLIST"] = "ADD_TO_WISHLIST";
70
+ RMN_SPOT_EVENT["EXPAND_PRODUCT"] = "EXPAND_PRODUCT";
63
71
  RMN_SPOT_EVENT["BUY_NOW"] = "BUY_NOW";
64
72
  })(RMN_SPOT_EVENT || (RMN_SPOT_EVENT = {}));
65
73
  var RMN_ENV;
@@ -70,6 +78,511 @@ var RMN_ENV;
70
78
  RMN_ENV["PRODUCTION"] = "production";
71
79
  })(RMN_ENV || (RMN_ENV = {}));
72
80
 
81
+ const SPOT_EVENTS_EXAMPLE = [
82
+ {
83
+ event: RMN_SPOT_EVENT.CLICK,
84
+ url: 'https://dev.rmn.liquidcommerce.cloud/api/spots/event?e=eyJ2IjoiMS4xMiIsImF2IjozMDY1NzgzLCJhdCI6MTYzLCJidCI6MCwiY20iOjQ0MDE5MjQxOCwiY2giOjYzMTg0LCJjayI6e30sImNyIjo0ODE4NTUzNzUsImRpIjoiOWMxNGFhMGI3NWY4NDMxNTllMTAwYWQzNzA1NzQyYzMiLCJkaiI6MCwiaWkiOiIxZjU0MGM5NmQ1N2M0YmZjODFlZjRkNjhkMzFjNDVkOSIsImRtIjozLCJmYyI6NjU2NjgyNTQ5LCJmbCI6NjQzOTMxODIxLCJpcCI6IjM1LjIyMy4xOTguOTUiLCJudyI6MTE1MDAsInBjIjo1MDAwLCJvcCI6NTAwMCwibXAiOjUwMDAsImVjIjowLCJnbSI6MCwiZXAiOm51bGwsInByIjoyNDkzMTYsInJ0IjoxLCJycyI6NTAwLCJzYSI6IjU1Iiwic2IiOiJpLTA0MDI0ODg4ZDlkMWRjZWQ3Iiwic3AiOjI3MjI3Miwic3QiOjEyODcyOTYsInRyIjp0cnVlLCJ1ayI6IjNhZWRhMWMxLTZhYjItNDUwNy04Mzg5LTEwZTJmNDMxYjM5MSIsInRzIjoxNzI5MzU5MDU0OTI3LCJiZiI6dHJ1ZSwicG4iOiJyYlByb2R1Y3RVcGNzIiwiZ2MiOnRydWUsImdDIjp0cnVlLCJncyI6Im5vbmUiLCJkYyI6MSwidHoiOiJBbWVyaWNhL05ld19Zb3JrIiwidXIiOm51bGx9&s=hWz37kbxi_u95EVNn2aoQhc5Aas',
85
+ },
86
+ {
87
+ event: RMN_SPOT_EVENT.IMPRESSION,
88
+ url: 'https://dev.rmn.liquidcommerce.cloud/api/spots/event?e=eyJ2IjoiMS4xMiIsImF2IjozMDY1NzgzLCJhdCI6MTYzLCJidCI6MCwiY20iOjQ0MDE5MjQxOCwiY2giOjYzMTg0LCJjayI6e30sImNyIjo0ODE4NTUzNzUsImRpIjoiOWMxNGFhMGI3NWY4NDMxNTllMTAwYWQzNzA1NzQyYzMiLCJkaiI6MCwiaWkiOiIxZjU0MGM5NmQ1N2M0YmZjODFlZjRkNjhkMzFjNDVkOSIsImRtIjozLCJmYyI6NjU2NjgyNTQ5LCJmbCI6NjQzOTMxODIxLCJpcCI6IjM1LjIyMy4xOTguOTUiLCJudyI6MTE1MDAsInBjIjo1MDAwLCJvcCI6NTAwMCwibXAiOjUwMDAsImVjIjowLCJnbSI6MCwiZXAiOm51bGwsInByIjoyNDkzMTYsInJ0IjoxLCJycyI6NTAwLCJzYSI6IjU1Iiwic2IiOiJpLTA0MDI0ODg4ZDlkMWRjZWQ3Iiwic3AiOjI3MjI3Miwic3QiOjEyODcyOTYsInRyIjp0cnVlLCJ1ayI6IjNhZWRhMWMxLTZhYjItNDUwNy04Mzg5LTEwZTJmNDMxYjM5MSIsInRzIjoxNzI5MzU5MDU0OTI3LCJiZiI6dHJ1ZSwicG4iOiJyYlByb2R1Y3RVcGNzIiwiZ2MiOnRydWUsImdDIjp0cnVlLCJncyI6Im5vbmUiLCJkYyI6MSwidHoiOiJBbWVyaWNhL05ld19Zb3JrIiwiYmEiOjEsImZxIjowfQ&s=djoysjCimurf-5T11AlNAwwLSS8',
89
+ },
90
+ {
91
+ event: RMN_SPOT_EVENT.PURCHASE,
92
+ url: 'https://dev.rmn.liquidcommerce.cloud/api/spots/event?e=eyJ2IjoiMS4xMiIsImF2IjozMDY1NzgzLCJhdCI6MTYzLCJidCI6MCwiY20iOjQ0MDE5MjQxOCwiY2giOjYzMTg0LCJjayI6e30sImNyIjo0ODE4NTUzNzUsImRpIjoiOWMxNGFhMGI3NWY4NDMxNTllMTAwYWQzNzA1NzQyYzMiLCJkaiI6MCwiaWkiOiIxZjU0MGM5NmQ1N2M0YmZjODFlZjRkNjhkMzFjNDVkOSIsImRtIjozLCJmYyI6NjU2NjgyNTQ5LCJmbCI6NjQzOTMxODIxLCJpcCI6IjM1LjIyMy4xOTguOTUiLCJudyI6MTE1MDAsInBjIjo1MDAwLCJvcCI6NTAwMCwibXAiOjUwMDAsImVjIjowLCJnbSI6MCwiZXAiOm51bGwsInByIjoyNDkzMTYsInJ0IjoxLCJycyI6NTAwLCJzYSI6IjU1Iiwic2IiOiJpLTA0MDI0ODg4ZDlkMWRjZWQ3Iiwic3AiOjI3MjI3Miwic3QiOjEyODcyOTYsInRyIjp0cnVlLCJ1ayI6IjNhZWRhMWMxLTZhYjItNDUwNy04Mzg5LTEwZTJmNDMxYjM5MSIsInRzIjoxNzI5MzU5MDU0OTI3LCJiZiI6dHJ1ZSwicG4iOiJyYlByb2R1Y3RVcGNzIiwiZ2MiOnRydWUsImdDIjp0cnVlLCJncyI6Im5vbmUiLCJkYyI6MSwidHoiOiJBbWVyaWNhL05ld19Zb3JrIiwiZXQiOjU5fQ&s=AAPAw-3SfZ0JMzjEGFSwt9L-2S4',
93
+ },
94
+ {
95
+ event: RMN_SPOT_EVENT.ADD_TO_CART,
96
+ url: 'https://dev.rmn.liquidcommerce.cloud/api/spots/event?e=eyJ2IjoiMS4xMiIsImF2IjozMDY1NzgzLCJhdCI6MTYzLCJidCI6MCwiY20iOjQ0MDE5MjQxOCwiY2giOjYzMTg0LCJjayI6e30sImNyIjo0ODE4NTUzNzUsImRpIjoiOWMxNGFhMGI3NWY4NDMxNTllMTAwYWQzNzA1NzQyYzMiLCJkaiI6MCwiaWkiOiIxZjU0MGM5NmQ1N2M0YmZjODFlZjRkNjhkMzFjNDVkOSIsImRtIjozLCJmYyI6NjU2NjgyNTQ5LCJmbCI6NjQzOTMxODIxLCJpcCI6IjM1LjIyMy4xOTguOTUiLCJudyI6MTE1MDAsInBjIjo1MDAwLCJvcCI6NTAwMCwibXAiOjUwMDAsImVjIjowLCJnbSI6MCwiZXAiOm51bGwsInByIjoyNDkzMTYsInJ0IjoxLCJycyI6NTAwLCJzYSI6IjU1Iiwic2IiOiJpLTA0MDI0ODg4ZDlkMWRjZWQ3Iiwic3AiOjI3MjI3Miwic3QiOjEyODcyOTYsInRyIjp0cnVlLCJ1ayI6IjNhZWRhMWMxLTZhYjItNDUwNy04Mzg5LTEwZTJmNDMxYjM5MSIsInRzIjoxNzI5MzU5MDU0OTI3LCJiZiI6dHJ1ZSwicG4iOiJyYlByb2R1Y3RVcGNzIiwiZ2MiOnRydWUsImdDIjp0cnVlLCJncyI6Im5vbmUiLCJkYyI6MSwidHoiOiJBbWVyaWNhL05ld19Zb3JrIiwiZXQiOjYwfQ&s=uzQFcjgL7m9XqUG8FvTPVN5YkZY',
97
+ },
98
+ {
99
+ event: 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: 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: RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_FULL_IMAGE,
112
+ variant: 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: RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_TWO_TILE,
128
+ variant: 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: RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_THREE_TILE,
145
+ variant: 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: RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_FULL_IMAGE,
164
+ variant: 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: RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_TWO_TILE,
180
+ variant: 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: RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_THREE_TILE,
197
+ variant: 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: RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_FULL_IMAGE,
216
+ variant: 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: RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_TWO_TILE,
232
+ variant: 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: RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_THREE_TILE,
249
+ variant: 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: RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_FULL_IMAGE,
270
+ variant: 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: RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_FULL_IMAGE,
286
+ variant: 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: RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_TWO_TILE,
304
+ variant: 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: RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_TWO_TILE,
321
+ variant: 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: RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_THREE_TILE,
340
+ variant: 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: RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_THREE_TILE,
359
+ variant: 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: RMN_SPOT_TYPE.RB_LARGE_CATEGORY_IMAGE_TOUT,
380
+ variant: 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: RMN_SPOT_TYPE.RB_LARGE_CATEGORY_IMAGE_TOUT,
396
+ variant: 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: RMN_SPOT_TYPE.RB_LARGE_CATEGORY_IMAGE_TOUT,
412
+ variant: 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: RMN_SPOT_TYPE.RB_SMALL_DISCOVER_TOUT,
430
+ variant: 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: RMN_SPOT_TYPE.RB_SMALL_DISCOVER_TOUT,
443
+ variant: 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: RMN_SPOT_TYPE.RB_SMALL_DISCOVER_TOUT,
456
+ variant: 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: RMN_SPOT_TYPE.RB_SMALL_CATEGORY_IMAGE_TOUT,
471
+ variant: 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: RMN_SPOT_TYPE.RB_SMALL_CATEGORY_IMAGE_TOUT,
484
+ variant: 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: RMN_SPOT_TYPE.RB_SMALL_CATEGORY_IMAGE_TOUT,
497
+ variant: 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: RMN_SPOT_TYPE.RB_COLLECTION_BANNER_WITHOUT_TEXT_BLOCK,
512
+ variant: 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: RMN_SPOT_TYPE.RB_COLLECTION_BANNER_WITHOUT_TEXT_BLOCK,
523
+ variant: 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: RMN_SPOT_TYPE.RB_COLLECTION_BANNER_WITHOUT_TEXT_BLOCK,
534
+ variant: 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: RMN_SPOT_TYPE.RB_NAVIGATION_BANNER,
547
+ variant: 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: RMN_SPOT_TYPE.RB_NAVIGATION_BANNER,
560
+ variant: 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: RMN_SPOT_TYPE.RB_NAVIGATION_BANNER,
573
+ variant: 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
+
73
586
  const REQUEST_CLOUD_PARTNER_SITE = 'X-Liquid-Partner-Site';
74
587
  const REQUEST_CLOUD_PROTECTED_KEY = 'X-Liquid-Protected';
75
588
  const REQUEST_CLOUD_PROTECTED_TIMESTAMP = 'X-Liquid-Timestamp';
@@ -6032,6 +6545,94 @@ axios.HttpStatusCode = HttpStatusCode;
6032
6545
 
6033
6546
  axios.default = axios;
6034
6547
 
6548
+ /**
6549
+ * Keyword patterns for each RMN_SPOT_EVENT.
6550
+ * Each event type has required keywords that must be present and optional ones.
6551
+ *
6552
+ * Note: The order of checks matters - more specific patterns must be checked before general ones
6553
+ * to avoid incorrect matches.
6554
+ */
6555
+ const EVENT_KEYWORDS = {
6556
+ [RMN_SPOT_EVENT.ADD_TO_CART_FROM_DETAILS]: {
6557
+ required: ['add', 'cart'],
6558
+ optional: ['details', 'detail', 'single', 'profile', 'page', 'pdp'],
6559
+ },
6560
+ [RMN_SPOT_EVENT.BUY_NOW]: {
6561
+ required: ['buy', 'now'],
6562
+ },
6563
+ [RMN_SPOT_EVENT.EXPAND_PRODUCT]: {
6564
+ required: ['product'],
6565
+ optional: ['details', 'expand', 'modal', 'popup', 'quickview', 'view'],
6566
+ },
6567
+ [RMN_SPOT_EVENT.ADD_TO_WISHLIST]: {
6568
+ required: ['add', 'wishlist'],
6569
+ },
6570
+ [RMN_SPOT_EVENT.REMOVE_FROM_CART]: {
6571
+ required: ['remove', 'cart'],
6572
+ },
6573
+ [RMN_SPOT_EVENT.PURCHASE]: {
6574
+ required: ['purchase'],
6575
+ },
6576
+ [RMN_SPOT_EVENT.ADD_TO_CART]: {
6577
+ required: ['add', 'cart'],
6578
+ },
6579
+ };
6580
+ /**
6581
+ * Normalizes an event name by converting to lowercase, removing special characters,
6582
+ * and splitting into words.
6583
+ */
6584
+ function normalizeEventName(event) {
6585
+ return event
6586
+ .toLowerCase()
6587
+ .replace(/[^a-z0-9\s]+/g, ' ') // Replace special chars with spaces
6588
+ .split(/\s+/) // Split on whitespace
6589
+ .filter(Boolean); // Remove empty strings
6590
+ }
6591
+ /**
6592
+ * Checks if a word matches a keyword, considering word boundaries.
6593
+ * This prevents partial word matches (e.g., "card" shouldn't match "cart").
6594
+ */
6595
+ function wordMatchesKeyword(word, keyword) {
6596
+ // Create a RegExp that matches the keyword as a whole word
6597
+ const keywordRegex = new RegExp(`^${keyword}s?$|${keyword}s?\\W|\\W${keyword}s?$|\\W${keyword}s?\\W`);
6598
+ return keywordRegex.test(` ${word} `); // Add spaces to handle word boundaries
6599
+ }
6600
+ /**
6601
+ * Checks if all required keywords and at least one optional keyword (if specified) are present.
6602
+ */
6603
+ function matchesKeywordPattern(words, required, optional) {
6604
+ // Check if all required keywords are present as whole words
6605
+ const hasAllRequired = required.every((keyword) => words.some((word) => wordMatchesKeyword(word, keyword)));
6606
+ if (!hasAllRequired) {
6607
+ return false;
6608
+ }
6609
+ // If no optional keywords are specified, return true
6610
+ if (!(optional === null || optional === void 0 ? void 0 : optional.length)) {
6611
+ return true;
6612
+ }
6613
+ // If optional keywords exist, check if at least one matches as a whole word
6614
+ return optional.some((keyword) => words.some((word) => wordMatchesKeyword(word, keyword)));
6615
+ }
6616
+ /**
6617
+ * Determines the event type from a raw event string by checking for keyword patterns.
6618
+ *
6619
+ * @param {string} [event] - The raw event string to evaluate
6620
+ * @returns {RMN_SPOT_EVENT | null} - The corresponding RMN_SPOT_EVENT or null if no match
6621
+ */
6622
+ function getEventTypeFromRawEvent(event) {
6623
+ if (!(event === null || event === void 0 ? void 0 : event.trim())) {
6624
+ return null;
6625
+ }
6626
+ const words = normalizeEventName(event);
6627
+ // Use Object.entries to maintain the exact order defined in EVENT_KEYWORDS
6628
+ for (const [eventType, { required, optional }] of Object.entries(EVENT_KEYWORDS)) {
6629
+ if (matchesKeywordPattern(words, required, optional)) {
6630
+ return eventType;
6631
+ }
6632
+ }
6633
+ return null;
6634
+ }
6635
+
6035
6636
  class SingletonManager {
6036
6637
  /**
6037
6638
  * Retrieves an instance of the specified class using the provided instance creator function.
@@ -6088,114 +6689,421 @@ class ObjectHelper {
6088
6689
  return Object.values(source);
6089
6690
  }
6090
6691
  /**
6091
- * Checks if a given key is present in a source object.
6092
- *
6093
- * @param {Record<string, any>} source - The object to search within.
6094
- * @param {string} key - The key to check for.
6095
- * @return {boolean} - True if the key is present in the source, false otherwise.
6692
+ * Checks if a given key is present in a source object.
6693
+ *
6694
+ * @param {Record<string, any>} source - The object to search within.
6695
+ * @param {string} key - The key to check for.
6696
+ * @return {boolean} - True if the key is present in the source, false otherwise.
6697
+ */
6698
+ includes(source, key) {
6699
+ return key in source;
6700
+ }
6701
+ /**
6702
+ * Checks if the given value is included in the values of a source object.
6703
+ *
6704
+ * @param {Record<string, any>} source - The source object to check the values from.
6705
+ * @param {string} value - The value to search for in the source object values.
6706
+ * @returns {boolean} - True if the value is found in the source object values, false otherwise.
6707
+ */
6708
+ valuesIncludes(source, value) {
6709
+ const sourceValues = Object.values(source);
6710
+ return sourceValues.includes(value);
6711
+ }
6712
+ /**
6713
+ * Checks if all keys from the given 'keys' array are present in the 'source' object.
6714
+ *
6715
+ * @param {Record<string, any>} source - The object to be checked.
6716
+ * @param {string[]} keys - The array of keys to be checked in the 'source' object.
6717
+ * @return {boolean} - Returns true if all keys are present in the 'source' object,
6718
+ * false otherwise.
6719
+ */
6720
+ hasAllValues(source, keys) {
6721
+ const sourceKeys = Object.keys(source);
6722
+ return keys.every((key) => sourceKeys.includes(key));
6723
+ }
6724
+ /**
6725
+ * Checks if the given object is an array.
6726
+ *
6727
+ * @param {any} obj - The object to check.
6728
+ * @return {boolean} - Returns true if the object is an array, otherwise returns false.
6729
+ */
6730
+ isArray(obj) {
6731
+ return Object.prototype.toString.call(obj) === '[object Array]';
6732
+ }
6733
+ /**
6734
+ * Merges the value with the target value.
6735
+ *
6736
+ * @param {any[] | Record<string, T>} targetValue - The target value to merge with.
6737
+ * @param {Record<string, any>} value - The value to merge.
6738
+ * @returns {Record<string, any>} - The merged value.
6739
+ */
6740
+ mergeValue(targetValue, value) {
6741
+ var _a;
6742
+ if (this.isArray(value)) {
6743
+ return this.mergeArrayValues(targetValue, value);
6744
+ }
6745
+ else if (typeof value === 'object') {
6746
+ return this.mergeObjectValues(targetValue, value);
6747
+ }
6748
+ return (_a = value !== null && value !== void 0 ? value : targetValue) !== null && _a !== void 0 ? _a : undefined;
6749
+ }
6750
+ /**
6751
+ * Merges the values of an array or object with a given array of objects and returns the merged result as an object.
6752
+ * If the targetValue is not an array, it returns a copy of the value array.
6753
+ *
6754
+ * @param targetValue - The target array or object to merge values into.
6755
+ * @param value - The array of objects containing values to merge into the targetValue.
6756
+ * @returns The merged result as an object.
6757
+ */
6758
+ mergeArrayValues(targetValue, value) {
6759
+ if (!this.isArray(targetValue)) {
6760
+ return [...value];
6761
+ }
6762
+ // make TypeScript understand that targetValue is an array
6763
+ const targetArray = targetValue;
6764
+ for (let i = 0, l = value.length; i < l; i++) {
6765
+ targetArray[i] = this.mergeValue(targetArray[i], value[i]);
6766
+ }
6767
+ return targetArray;
6768
+ }
6769
+ /**
6770
+ * Merges the values of two objects or arrays into a single object.
6771
+ * If the target value is an array or a record, the method merges the values into the target value.
6772
+ * If the target value is not an array or a record, it returns the new value.
6773
+ *
6774
+ * @template T - The type of the values in the target object.
6775
+ * @param {object[]|Record<string, T>} targetValue - The target object or array to merge the values into.
6776
+ * @param {Record<string, any>} value - The new values to merge into the target object or array.
6777
+ * @returns {Record<string, any>} - The merged object or array.
6778
+ */
6779
+ mergeObjectValues(targetValue, value) {
6780
+ if (targetValue && typeof targetValue === 'object') {
6781
+ this.innerMerge(targetValue, value);
6782
+ return targetValue;
6783
+ }
6784
+ return value ? { ...value } : value;
6785
+ }
6786
+ /**
6787
+ * Merges the properties of the source object into the target object.
6788
+ *
6789
+ * @param {Record<string, any>} target - The target object to merge the properties into.
6790
+ * @param {Record<string, any>} source - The source object containing the properties to merge.
6791
+ *
6792
+ * @return {void} - This method does not return any value.
6793
+ */
6794
+ innerMerge(target, source) {
6795
+ for (const key of Object.keys(source)) {
6796
+ target[key] = this.mergeValue(target[key], source[key]);
6797
+ }
6798
+ }
6799
+ }
6800
+
6801
+ /**
6802
+ * Recursively extracts ID values from a nested data structure.
6803
+ * Searches for specified property names and collects their primitive values (strings/numbers).
6804
+ *
6805
+ * @param data - The data structure to search through (can be nested objects/arrays)
6806
+ * @param propertyNames - Array of property names to look for
6807
+ * @returns Array of extracted ID values (strings/numbers only)
6808
+ *
6809
+ * @example
6810
+ * const data = {
6811
+ * id: [1, 2, 3],
6812
+ * nested: { id: 'abc' },
6813
+ * items: [{ id: 456 }]
6814
+ * };
6815
+ * extractDeepIds(data); // Returns [1, 2, 3, 'abc', 456]
6816
+ */
6817
+ function extractDeepIds(data, propertyNames) {
6818
+ const ids = [];
6819
+ const defaulPropertyNames = [
6820
+ 'id',
6821
+ 'upc',
6822
+ 'groupingId',
6823
+ 'sku',
6824
+ 'productId',
6825
+ 'item_id',
6826
+ 'isbn',
6827
+ 'asin',
6828
+ 'mpn',
6829
+ 'model_number',
6830
+ 'article_number',
6831
+ 'variant_id',
6832
+ 'item_number',
6833
+ 'catalog_id',
6834
+ 'reference_id',
6835
+ ];
6836
+ // Set for faster property name lookups
6837
+ const propertySet = new Set(defaulPropertyNames);
6838
+ /**
6839
+ * Processes a value and extracts IDs if it matches criteria
6840
+ * @param value - The value to process
6841
+ * @param currentKey - The property name of the current value
6842
+ */
6843
+ const processValue = (value, currentKey) => {
6844
+ // Early exit for null/undefined values
6845
+ if (value == null)
6846
+ return;
6847
+ // If current key matches our target properties
6848
+ if (currentKey && propertySet.has(currentKey)) {
6849
+ if (Array.isArray(value)) {
6850
+ // Filter and push valid array values in one pass
6851
+ ids.push(...value.filter((item) => typeof item === 'string' || typeof item === 'number'));
6852
+ }
6853
+ else if (typeof value === 'string' || typeof value === 'number') {
6854
+ ids.push(value);
6855
+ }
6856
+ return; // Stop processing this branch after handling the ID
6857
+ }
6858
+ // Recursively process nested structures
6859
+ if (Array.isArray(value)) {
6860
+ value.forEach((item) => processValue(item));
6861
+ }
6862
+ else if (typeof value === 'object') {
6863
+ // Process all enumerable properties
6864
+ for (const [key, val] of Object.entries(value)) {
6865
+ processValue(val, key);
6866
+ }
6867
+ }
6868
+ };
6869
+ processValue(data);
6870
+ return ids; // No need to filter nulls as we handle that during collection
6871
+ }
6872
+ // Fallback method using fetch if sendBeacon isn't available
6873
+ async function fallbackEventFire(url) {
6874
+ try {
6875
+ const racePromise = Promise.race([
6876
+ // Promise #1: The fetch request
6877
+ fetch(url, {
6878
+ method: 'POST',
6879
+ keepalive: true,
6880
+ }),
6881
+ // Promise #2: The timeout
6882
+ new Promise((_, reject) => setTimeout(() => reject(new Error('Request timeout')), 2000)),
6883
+ ]);
6884
+ /**
6885
+ * Prevent requests from hanging indefinitely
6886
+ * Improve user experience by failing fast
6887
+ * Handle slow network conditions gracefully
6888
+ * Ensure resources are freed up in a timely manner
6889
+ */
6890
+ const response = await racePromise;
6891
+ return response.ok;
6892
+ }
6893
+ catch (_a) {
6894
+ return false;
6895
+ }
6896
+ }
6897
+ /**
6898
+ * Extracts and decodes a URL from a base64-encoded query parameter.
6899
+ *
6900
+ * @param {string} url - The URL containing the base64-encoded query parameter.
6901
+ * @returns {string | null} - The decoded URL or null if not found or invalid.
6902
+ */
6903
+ function getRedirectUrlFromPayload(url) {
6904
+ try {
6905
+ const base64String = new URL(url).searchParams.get('e');
6906
+ if (!base64String) {
6907
+ return null;
6908
+ }
6909
+ const data = JSON.parse(atob(base64String));
6910
+ return data.ur || null;
6911
+ }
6912
+ catch (_a) {
6913
+ return null;
6914
+ }
6915
+ }
6916
+ /**
6917
+ * Fires an event using the navigator.sendBeacon method or a fallback method if sendBeacon is not available.
6918
+ * If the event is a click event and a redirect URL is found, it redirects the user to that URL.
6919
+ *
6920
+ * @param {IFireEventParams} params - The parameters for firing the event.
6921
+ * @param {RMN_SPOT_EVENT} params.event - The event type.
6922
+ * @param {string} params.eventUrl - The URL to which the event is sent.
6923
+ * @returns {Promise<void>} - A promise that resolves when the event is fired.
6924
+ */
6925
+ async function fireEvent({ event, eventUrl }) {
6926
+ var _a;
6927
+ try {
6928
+ 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));
6929
+ if (!didFireEvent) {
6930
+ return;
6931
+ }
6932
+ if (event === RMN_SPOT_EVENT.CLICK) {
6933
+ const redirectUrl = getRedirectUrlFromPayload(eventUrl);
6934
+ if (redirectUrl) {
6935
+ window.location.href = redirectUrl;
6936
+ }
6937
+ }
6938
+ }
6939
+ catch (_b) {
6940
+ // Handle errors silently
6941
+ }
6942
+ }
6943
+ function calculateScaleFactor(elementScale) {
6944
+ // Step 1: Apply square root for non-linear scaling
6945
+ // This creates a more gradual scaling effect, especially for larger changes
6946
+ // For example:
6947
+ // - elementScale of 0.25 (1/4 size) becomes 0.5
6948
+ // - elementScale of 1 (unchanged) remains 1
6949
+ // - elementScale of 4 (4x size) becomes 2
6950
+ const baseFactor = Math.sqrt(elementScale);
6951
+ // Step 2: Apply additional dampening to further soften the scaling effect
6952
+ // The dampening factor (0.5) can be adjusted:
6953
+ // - Lower values (closer to 0) make scaling more subtle
6954
+ // - Higher values (closer to 1) make scaling more pronounced
6955
+ const dampening = 0.35;
6956
+ // Calculate the scaleFactor:
6957
+ // 1. (baseFactor - 1) represents the change from the original size
6958
+ // 2. Multiply by dampening to reduce the effect
6959
+ // 3. Add 1 to center the scaling around the original size
6960
+ // For example, if baseFactor is 2:
6961
+ // scaleFactor = 1 + (2 - 1) * 0.5 = 1.5
6962
+ const scaleFactor = 1 + (baseFactor - 1) * dampening;
6963
+ // Step 3: Define the allowed range for the scale factor
6964
+ // This ensures that the font size never changes too drastically
6965
+ const minScale = 0.35; // Font will never be smaller than 50% of original
6966
+ const maxScale = 1.5; // Font will never be larger than 150% of original
6967
+ // Step 4: Clamp the scale factor to the defined range
6968
+ // Math.min ensures the value doesn't exceed maxScale
6969
+ // Math.max ensures the value isn't less than minScale
6970
+ return Math.max(minScale, Math.min(maxScale, scaleFactor));
6971
+ }
6972
+
6973
+ class UniqueIdGenerator {
6974
+ /**
6975
+ * Initialize the generator with a node ID
6976
+ * @param nodeId Number between 0-1023 to identify this instance
6096
6977
  */
6097
- includes(source, key) {
6098
- return key in source;
6978
+ static initialize(nodeId = Math.floor(Math.random() * 1024)) {
6979
+ if (nodeId < 0 || nodeId >= 1 << this.nodeBits) {
6980
+ throw new Error(`Node ID must be between 0 and ${(1 << this.nodeBits) - 1}`);
6981
+ }
6982
+ this.nodeId = nodeId;
6099
6983
  }
6100
6984
  /**
6101
- * Checks if the given value is included in the values of a source object.
6102
- *
6103
- * @param {Record<string, any>} source - The source object to check the values from.
6104
- * @param {string} value - The value to search for in the source object values.
6105
- * @returns {boolean} - True if the value is found in the source object values, false otherwise.
6985
+ * Convert a number to base32 string with specified length
6106
6986
  */
6107
- valuesIncludes(source, value) {
6108
- const sourceValues = Object.values(source);
6109
- return sourceValues.includes(value);
6987
+ static toBase32(num, length) {
6988
+ let result = '';
6989
+ while (num > 0) {
6990
+ result = this.base32Chars[Number(num % BigInt(32))] + result;
6991
+ // @ts-expect-error - TS doesn't support bigint division
6992
+ num = num / 32n;
6993
+ }
6994
+ return result.padStart(length, '0');
6110
6995
  }
6111
6996
  /**
6112
- * Checks if all keys from the given 'keys' array are present in the 'source' object.
6113
- *
6114
- * @param {Record<string, any>} source - The object to be checked.
6115
- * @param {string[]} keys - The array of keys to be checked in the 'source' object.
6116
- * @return {boolean} - Returns true if all keys are present in the 'source' object,
6117
- * false otherwise.
6997
+ * Generate a cryptographically secure random number
6118
6998
  */
6119
- hasAllValues(source, keys) {
6120
- const sourceKeys = Object.keys(source);
6121
- return keys.every((key) => sourceKeys.includes(key));
6999
+ static getSecureRandom() {
7000
+ if (typeof crypto !== 'undefined') {
7001
+ const buffer = new Uint32Array(1);
7002
+ crypto.getRandomValues(buffer);
7003
+ return buffer[0];
7004
+ }
7005
+ return Math.floor(Math.random() * 0xffffffff);
6122
7006
  }
6123
7007
  /**
6124
- * Checks if the given object is an array.
6125
- *
6126
- * @param {any} obj - The object to check.
6127
- * @return {boolean} - Returns true if the object is an array, otherwise returns false.
7008
+ * Wait until next millisecond
6128
7009
  */
6129
- isArray(obj) {
6130
- return Object.prototype.toString.call(obj) === '[object Array]';
7010
+ static waitNextMillis(lastTimestamp) {
7011
+ let timestamp = Date.now();
7012
+ while (timestamp <= lastTimestamp) {
7013
+ timestamp = Date.now();
7014
+ }
7015
+ return timestamp;
6131
7016
  }
6132
7017
  /**
6133
- * Merges the value with the target value.
7018
+ * Generates a highly unique ID with the following format:
7019
+ * TTTTTTTTTTCCCCNNNNNRRRR
7020
+ * T: Timestamp (10 chars)
7021
+ * C: Counter (4 chars)
7022
+ * N: Node ID (5 chars)
7023
+ * R: Random (4 chars)
6134
7024
  *
6135
- * @param {any[] | Record<string, T>} targetValue - The target value to merge with.
6136
- * @param {Record<string, any>} value - The value to merge.
6137
- * @returns {Record<string, any>} - The merged value.
7025
+ * Total length: 23 characters, always uppercase alphanumeric
6138
7026
  */
6139
- mergeValue(targetValue, value) {
6140
- var _a;
6141
- if (this.isArray(value)) {
6142
- return this.mergeArrayValues(targetValue, value);
7027
+ static generate() {
7028
+ if (this.nodeId === undefined) {
7029
+ this.initialize();
6143
7030
  }
6144
- else if (typeof value === 'object') {
6145
- return this.mergeObjectValues(targetValue, value);
7031
+ let timestamp = Date.now() - this.epoch;
7032
+ // Handle clock moving backwards or same millisecond
7033
+ if (timestamp < this.lastTimestamp) {
7034
+ throw new Error('Clock moved backwards. Refusing to generate ID.');
6146
7035
  }
6147
- return (_a = value !== null && value !== void 0 ? value : targetValue) !== null && _a !== void 0 ? _a : undefined;
7036
+ if (timestamp === this.lastTimestamp) {
7037
+ this.sequence = (this.sequence + 1) & ((1 << this.sequenceBits) - 1);
7038
+ if (this.sequence === 0) {
7039
+ timestamp = this.waitNextMillis(this.lastTimestamp);
7040
+ }
7041
+ }
7042
+ else {
7043
+ this.sequence = 0;
7044
+ }
7045
+ this.lastTimestamp = timestamp;
7046
+ // Generate random component
7047
+ const random = this.getSecureRandom() & 0xffff; // 16 bits of randomness
7048
+ // Combine all components into a BigInt
7049
+ // const id =
7050
+ // (BigInt(timestamp) << BigInt(this.nodeBits + this.sequenceBits + 16)) |
7051
+ // (BigInt(this.nodeId) << BigInt(this.sequenceBits + 16)) |
7052
+ // (BigInt(this.sequence) << BigInt(16)) |
7053
+ // BigInt(random);
7054
+ // Convert to base32 representation
7055
+ const timeComponent = this.toBase32(BigInt(timestamp), 10);
7056
+ const counterComponent = this.toBase32(BigInt(this.sequence), 4);
7057
+ const nodeComponent = this.toBase32(BigInt(this.nodeId), 5);
7058
+ const randomComponent = this.toBase32(BigInt(random), 4);
7059
+ return `${timeComponent}${counterComponent}${nodeComponent}${randomComponent}`;
6148
7060
  }
6149
7061
  /**
6150
- * Merges the values of an array or object with a given array of objects and returns the merged result as an object.
6151
- * If the targetValue is not an array, it returns a copy of the value array.
6152
- *
6153
- * @param targetValue - The target array or object to merge values into.
6154
- * @param value - The array of objects containing values to merge into the targetValue.
6155
- * @returns The merged result as an object.
7062
+ * Validates if a string matches the expected ID format
6156
7063
  */
6157
- mergeArrayValues(targetValue, value) {
6158
- if (!this.isArray(targetValue)) {
6159
- return [...value];
7064
+ static isValid(id) {
7065
+ if (!/^[0-9A-HJ-NP-Z]{23}$/.test(id))
7066
+ return false;
7067
+ try {
7068
+ const timeComponent = id.slice(0, 10);
7069
+ const timestamp = this.decodeBase32(timeComponent);
7070
+ const now = Date.now() - this.epoch;
7071
+ return timestamp >= 0 && timestamp <= now;
6160
7072
  }
6161
- // make TypeScript understand that targetValue is an array
6162
- const targetArray = targetValue;
6163
- for (let i = 0, l = value.length; i < l; i++) {
6164
- targetArray[i] = this.mergeValue(targetArray[i], value[i]);
7073
+ catch (_a) {
7074
+ return false;
6165
7075
  }
6166
- return targetArray;
6167
7076
  }
6168
7077
  /**
6169
- * Merges the values of two objects or arrays into a single object.
6170
- * If the target value is an array or a record, the method merges the values into the target value.
6171
- * If the target value is not an array or a record, it returns the new value.
6172
- *
6173
- * @template T - The type of the values in the target object.
6174
- * @param {object[]|Record<string, T>} targetValue - The target object or array to merge the values into.
6175
- * @param {Record<string, any>} value - The new values to merge into the target object or array.
6176
- * @returns {Record<string, any>} - The merged object or array.
7078
+ * Decode base32 string to number
6177
7079
  */
6178
- mergeObjectValues(targetValue, value) {
6179
- if (targetValue && typeof targetValue === 'object') {
6180
- this.innerMerge(targetValue, value);
6181
- return targetValue;
7080
+ static decodeBase32(str) {
7081
+ let result = 0;
7082
+ for (const char of str) {
7083
+ result = result * 32 + this.base32Chars.indexOf(char);
6182
7084
  }
6183
- return value ? { ...value } : value;
7085
+ return result;
6184
7086
  }
6185
7087
  /**
6186
- * Merges the properties of the source object into the target object.
6187
- *
6188
- * @param {Record<string, any>} target - The target object to merge the properties into.
6189
- * @param {Record<string, any>} source - The source object containing the properties to merge.
6190
- *
6191
- * @return {void} - This method does not return any value.
7088
+ * Extract timestamp from ID
6192
7089
  */
6193
- innerMerge(target, source) {
6194
- for (const key of Object.keys(source)) {
6195
- target[key] = this.mergeValue(target[key], source[key]);
6196
- }
7090
+ static getTimestamp(id) {
7091
+ if (!this.isValid(id))
7092
+ throw new Error('Invalid ID format');
7093
+ const timeComponent = id.slice(0, 10);
7094
+ const timestamp = this.decodeBase32(timeComponent);
7095
+ return new Date(timestamp + this.epoch);
6197
7096
  }
6198
7097
  }
7098
+ // Constants for bit manipulation
7099
+ UniqueIdGenerator.epoch = 1577836800000; // 2020-01-01 as epoch
7100
+ UniqueIdGenerator.nodeBits = 10;
7101
+ UniqueIdGenerator.sequenceBits = 12;
7102
+ // Instance variables
7103
+ UniqueIdGenerator.lastTimestamp = -1;
7104
+ UniqueIdGenerator.sequence = 0;
7105
+ // Character set for base32 encoding (excluding similar looking characters)
7106
+ UniqueIdGenerator.base32Chars = '0123456789ABCDEFGHJKMNPQRSTVWXYZ';
6199
7107
 
6200
7108
  var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
6201
7109
 
@@ -15203,8 +16111,21 @@ class IntersectionObserverService {
15203
16111
  }
15204
16112
  }
15205
16113
 
15206
- class LocalStorage {
16114
+ var ENUM_LOCAL_STORAGE_SPOT_ARRAY_INDEX;
16115
+ (function (ENUM_LOCAL_STORAGE_SPOT_ARRAY_INDEX) {
16116
+ ENUM_LOCAL_STORAGE_SPOT_ARRAY_INDEX[ENUM_LOCAL_STORAGE_SPOT_ARRAY_INDEX["PLACEMENT_ID"] = 0] = "PLACEMENT_ID";
16117
+ ENUM_LOCAL_STORAGE_SPOT_ARRAY_INDEX[ENUM_LOCAL_STORAGE_SPOT_ARRAY_INDEX["SPOT_ID"] = 1] = "SPOT_ID";
16118
+ ENUM_LOCAL_STORAGE_SPOT_ARRAY_INDEX[ENUM_LOCAL_STORAGE_SPOT_ARRAY_INDEX["SPOT_TYPE"] = 2] = "SPOT_TYPE";
16119
+ ENUM_LOCAL_STORAGE_SPOT_ARRAY_INDEX[ENUM_LOCAL_STORAGE_SPOT_ARRAY_INDEX["EVENTS"] = 3] = "EVENTS";
16120
+ ENUM_LOCAL_STORAGE_SPOT_ARRAY_INDEX[ENUM_LOCAL_STORAGE_SPOT_ARRAY_INDEX["PRODUCT_IDS"] = 4] = "PRODUCT_IDS";
16121
+ ENUM_LOCAL_STORAGE_SPOT_ARRAY_INDEX[ENUM_LOCAL_STORAGE_SPOT_ARRAY_INDEX["CREATED_AT"] = 5] = "CREATED_AT";
16122
+ })(ENUM_LOCAL_STORAGE_SPOT_ARRAY_INDEX || (ENUM_LOCAL_STORAGE_SPOT_ARRAY_INDEX = {}));
16123
+ class LocalStorageService {
15207
16124
  constructor() {
16125
+ if (typeof window.localStorage === 'undefined') {
16126
+ console.warn('Local storage is not supported in this environment');
16127
+ return;
16128
+ }
15208
16129
  this.spots = new Map();
15209
16130
  // Sync local storage with the current state
15210
16131
  this.syncLocalStorage();
@@ -15212,19 +16133,23 @@ class LocalStorage {
15212
16133
  this.removeExpiredSpots();
15213
16134
  }
15214
16135
  static getInstance() {
15215
- if (!LocalStorage.instance) {
15216
- LocalStorage.instance = new LocalStorage();
16136
+ if (!LocalStorageService.instance) {
16137
+ LocalStorageService.instance = new LocalStorageService();
15217
16138
  }
15218
- return LocalStorage.instance;
16139
+ return LocalStorageService.instance;
15219
16140
  }
15220
16141
  syncLocalStorage() {
15221
- const localStorageData = localStorage.getItem(LocalStorage.localStorageKey);
15222
- // TODO: Encrypt the data before storing it in the local storage
16142
+ const localStorageData = window.localStorage.getItem(LocalStorageService.localStorageKey);
15223
16143
  if (localStorageData) {
15224
16144
  try {
15225
- const parsedData = JSON.parse(localStorageData);
16145
+ const decryptedData = this.decryptData(localStorageData);
16146
+ const parsedData = JSON.parse(decryptedData);
15226
16147
  if (parsedData && typeof parsedData === 'object') {
15227
- this.spots = this.objToMap(parsedData);
16148
+ const data = {};
16149
+ for (const [key, value] of Object.entries(parsedData)) {
16150
+ data[key] = this.arrayToObject(value);
16151
+ }
16152
+ this.spots = this.objectToMap(data);
15228
16153
  }
15229
16154
  else {
15230
16155
  this.clearLocalStorage();
@@ -15237,43 +16162,176 @@ class LocalStorage {
15237
16162
  }
15238
16163
  }
15239
16164
  setSpot(spotId, data) {
16165
+ var _a;
15240
16166
  data.createdAt = Date.now();
15241
- this.spots.set(spotId, data);
16167
+ (_a = this.spots) === null || _a === void 0 ? void 0 : _a.set(spotId, data);
15242
16168
  this.updateLocalStorage();
15243
16169
  }
15244
- getSpot(spotId) {
15245
- return this.spots.get(spotId);
15246
- }
15247
16170
  removeSpot(spotId) {
15248
- this.spots.delete(spotId);
16171
+ var _a;
16172
+ (_a = this.spots) === null || _a === void 0 ? void 0 : _a.delete(spotId);
15249
16173
  this.updateLocalStorage();
15250
16174
  }
16175
+ getSpot(spotId) {
16176
+ var _a;
16177
+ return (_a = this.spots) === null || _a === void 0 ? void 0 : _a.get(spotId);
16178
+ }
16179
+ getSpots() {
16180
+ if (!this.spots)
16181
+ return undefined;
16182
+ return this.mapToObject(this.spots);
16183
+ }
15251
16184
  updateLocalStorage() {
15252
- const data = this.mapToObj(this.spots);
15253
- localStorage.setItem(LocalStorage.localStorageKey, JSON.stringify(data));
16185
+ if (!this.spots)
16186
+ return undefined;
16187
+ const data = this.mapToObject(this.spots);
16188
+ const dataArray = {};
16189
+ for (const [key, value] of Object.entries(data)) {
16190
+ dataArray[key] = this.objectToArray(value);
16191
+ }
16192
+ try {
16193
+ const encryptedData = this.encryptData(JSON.stringify(dataArray));
16194
+ window.localStorage.setItem(LocalStorageService.localStorageKey, encryptedData);
16195
+ }
16196
+ catch (_a) {
16197
+ // If there is an error parsing the data, clear the local storage to prevent any issues
16198
+ this.clearLocalStorage();
16199
+ }
15254
16200
  }
15255
16201
  clearLocalStorage() {
15256
- localStorage.removeItem(LocalStorage.localStorageKey);
16202
+ window.localStorage.removeItem(LocalStorageService.localStorageKey);
15257
16203
  }
15258
16204
  removeExpiredSpots() {
16205
+ var _a;
15259
16206
  const currentTime = Date.now();
15260
- this.spots.forEach((spot, spotId) => {
15261
- var _a;
15262
- if (currentTime - ((_a = spot.createdAt) !== null && _a !== void 0 ? _a : 0) > LocalStorage.spotExpirationTime) {
15263
- this.spots.delete(spotId);
16207
+ (_a = this.spots) === null || _a === void 0 ? void 0 : _a.forEach((spot, spotId) => {
16208
+ var _a, _b;
16209
+ if (currentTime - ((_a = spot.createdAt) !== null && _a !== void 0 ? _a : 0) > LocalStorageService.spotExpirationTime) {
16210
+ (_b = this.spots) === null || _b === void 0 ? void 0 : _b.delete(spotId);
15264
16211
  }
15265
16212
  });
15266
16213
  this.updateLocalStorage();
15267
16214
  }
15268
- mapToObj(map) {
16215
+ mapToObject(map) {
15269
16216
  return Object.fromEntries(map);
15270
16217
  }
15271
- objToMap(obj) {
15272
- return new Map(Object.entries(obj));
16218
+ objectToMap(obj) {
16219
+ return new Map(Object.entries(obj));
16220
+ }
16221
+ objectToArray(obj) {
16222
+ return [obj.placementId, obj.spotId, obj.spotType, obj.events, obj.productIds, obj.createdAt];
16223
+ }
16224
+ arrayToObject(arr) {
16225
+ return {
16226
+ placementId: arr[ENUM_LOCAL_STORAGE_SPOT_ARRAY_INDEX.PLACEMENT_ID],
16227
+ spotId: arr[ENUM_LOCAL_STORAGE_SPOT_ARRAY_INDEX.SPOT_ID],
16228
+ spotType: arr[ENUM_LOCAL_STORAGE_SPOT_ARRAY_INDEX.SPOT_TYPE],
16229
+ events: arr[ENUM_LOCAL_STORAGE_SPOT_ARRAY_INDEX.EVENTS],
16230
+ productIds: arr[ENUM_LOCAL_STORAGE_SPOT_ARRAY_INDEX.PRODUCT_IDS],
16231
+ createdAt: arr[ENUM_LOCAL_STORAGE_SPOT_ARRAY_INDEX.CREATED_AT],
16232
+ };
16233
+ }
16234
+ encryptData(data) {
16235
+ if (!LocalStorageService.encryptData)
16236
+ return data;
16237
+ // For now, we are using base64 encoding to encrypt the data
16238
+ // Later we will use Jose encryption
16239
+ const encryptedData = btoa(data);
16240
+ return encryptedData;
16241
+ }
16242
+ decryptData(data) {
16243
+ if (!LocalStorageService.encryptData)
16244
+ return data;
16245
+ // For now, we are using base64 encoding to encrypt
16246
+ // Later we will use Jose encryption
16247
+ const decryptedData = atob(data);
16248
+ return decryptedData;
16249
+ }
16250
+ }
16251
+ LocalStorageService.localStorageKey = 'lc_rmn';
16252
+ LocalStorageService.spotExpirationTime = 1000 * 60 * 60 * 24 * 7; // 7 days
16253
+ LocalStorageService.encryptData = true;
16254
+
16255
+ /**
16256
+ * PubsubService class
16257
+ * Manages event subscriptions and publications
16258
+ * @template IRmnEventMap A record type defining the structure of events and their data
16259
+ */
16260
+ class PubsubService {
16261
+ constructor() {
16262
+ /**
16263
+ * Object to store subscribers for each event type
16264
+ */
16265
+ this.subscribers = {};
16266
+ }
16267
+ static getInstance() {
16268
+ if (!PubsubService.instance) {
16269
+ PubsubService.instance = new PubsubService();
16270
+ }
16271
+ return PubsubService.instance;
16272
+ }
16273
+ /**
16274
+ * Subscribe to an event
16275
+ * @param eventType - The type of event to subscribe to
16276
+ * @param callback - The function to be called when the event is published
16277
+ * @returns A function to unsubscribe from the event
16278
+ *
16279
+ * @Example:
16280
+ * const unsubscribe = pubSub.subscribe('userLogin', (data) => {
16281
+ * console.log(`User ${data.username} logged in`);
16282
+ * });
16283
+ */
16284
+ subscribe(eventType, callback) {
16285
+ if (!this.subscribers[eventType]) {
16286
+ this.subscribers[eventType] = [];
16287
+ }
16288
+ this.subscribers[eventType].push(callback);
16289
+ // Return an unsubscribe function
16290
+ return () => {
16291
+ this.subscribers[eventType] = this.subscribers[eventType].filter((cb) => cb !== callback);
16292
+ };
16293
+ }
16294
+ /**
16295
+ * Publish an event
16296
+ * @param eventType - The type of event to publish
16297
+ * @param data - The data to be passed to the event subscribers
16298
+ *
16299
+ * @Example:
16300
+ * pubSub.publish('userLogin', { username: 'john_doe', timestamp: Date.now() });
16301
+ */
16302
+ publish(eventType, data) {
16303
+ if (!this.subscribers[eventType]) {
16304
+ return;
16305
+ }
16306
+ this.subscribers[eventType].forEach((callback) => callback(data));
15273
16307
  }
15274
16308
  }
15275
- LocalStorage.localStorageKey = 'lc_rmn';
15276
- LocalStorage.spotExpirationTime = 1000 * 60 * 60 * 24 * 7; // 7 days
16309
+ /**
16310
+ * Usage Example:
16311
+ *
16312
+ * interface IRmnEventMap {
16313
+ * userLogin: { username: string; timestamp: number };
16314
+ * pageView: { url: string; timestamp: number };
16315
+ * }
16316
+ *
16317
+ * const pubSub = new PubsubService<IRmnEventMap>();
16318
+ *
16319
+ * // Subscribe to events
16320
+ * const unsubscribeLogin = pubSub.subscribe('userLogin', (data) => {
16321
+ * console.log(`User ${data.username} logged in at ${new Date(data.timestamp)}`);
16322
+ * });
16323
+ *
16324
+ * pubSub.subscribe('pageView', (data) => {
16325
+ * console.log(`Page ${data.url} viewed at ${new Date(data.timestamp)}`);
16326
+ * });
16327
+ *
16328
+ * // Publish events
16329
+ * pubSub.publish('userLogin', { username: 'john_doe', timestamp: Date.now() });
16330
+ * pubSub.publish('pageView', { url: '/home', timestamp: Date.now() });
16331
+ *
16332
+ * // Unsubscribe from an event
16333
+ * unsubscribeLogin();
16334
+ */
15277
16335
 
15278
16336
  class ResizeObserverService {
15279
16337
  constructor({ element, maxSize, minScale }) {
@@ -15343,36 +16401,6 @@ class ResizeObserverService {
15343
16401
  }
15344
16402
  }
15345
16403
 
15346
- function calculateScaleFactor(elementScale) {
15347
- // Step 1: Apply square root for non-linear scaling
15348
- // This creates a more gradual scaling effect, especially for larger changes
15349
- // For example:
15350
- // - elementScale of 0.25 (1/4 size) becomes 0.5
15351
- // - elementScale of 1 (unchanged) remains 1
15352
- // - elementScale of 4 (4x size) becomes 2
15353
- const baseFactor = Math.sqrt(elementScale);
15354
- // Step 2: Apply additional dampening to further soften the scaling effect
15355
- // The dampening factor (0.5) can be adjusted:
15356
- // - Lower values (closer to 0) make scaling more subtle
15357
- // - Higher values (closer to 1) make scaling more pronounced
15358
- const dampening = 0.35;
15359
- // Calculate the scaleFactor:
15360
- // 1. (baseFactor - 1) represents the change from the original size
15361
- // 2. Multiply by dampening to reduce the effect
15362
- // 3. Add 1 to center the scaling around the original size
15363
- // For example, if baseFactor is 2:
15364
- // scaleFactor = 1 + (2 - 1) * 0.5 = 1.5
15365
- const scaleFactor = 1 + (baseFactor - 1) * dampening;
15366
- // Step 3: Define the allowed range for the scale factor
15367
- // This ensures that the font size never changes too drastically
15368
- const minScale = 0.35; // Font will never be smaller than 50% of original
15369
- const maxScale = 1.5; // Font will never be larger than 150% of original
15370
- // Step 4: Clamp the scale factor to the defined range
15371
- // Math.min ensures the value doesn't exceed maxScale
15372
- // Math.max ensures the value isn't less than minScale
15373
- return Math.max(minScale, Math.min(maxScale, scaleFactor));
15374
- }
15375
-
15376
16404
  const CAROUSEL_COMPONENT_STYLE = ({ width, height, fluid }) => `
15377
16405
  :host {
15378
16406
  position: relative;
@@ -15387,11 +16415,13 @@ const CAROUSEL_COMPONENT_STYLE = ({ width, height, fluid }) => `
15387
16415
  position: relative;
15388
16416
  height: 100%;
15389
16417
  width: 100%;
16418
+ display: flex;
16419
+ transition: transform 0.5s ease-in-out;
15390
16420
  }
15391
16421
 
15392
16422
  .slide {
15393
- display: none;
15394
-
16423
+ flex: 0 0 100%;
16424
+ display: flex;
15395
16425
  justify-content: center;
15396
16426
  align-items: center;
15397
16427
  height: 100%;
@@ -15696,9 +16726,9 @@ if (typeof window !== 'undefined' && typeof window.customElements !== 'undefined
15696
16726
  renderSlides() {
15697
16727
  const slidesContainer = document.createElement('div');
15698
16728
  slidesContainer.className = 'slides';
15699
- this.slides.forEach((slide, index) => {
16729
+ this.slides.forEach((slide) => {
15700
16730
  const slideElement = document.createElement('div');
15701
- slideElement.className = `slide ${index === this.currentSlide ? 'active' : ''}`;
16731
+ slideElement.className = 'slide';
15702
16732
  if (slide instanceof HTMLElement) {
15703
16733
  slideElement.appendChild(slide);
15704
16734
  }
@@ -15777,10 +16807,9 @@ if (typeof window !== 'undefined' && typeof window.customElements !== 'undefined
15777
16807
  updateCarousel() {
15778
16808
  if (!this.slidesContainer)
15779
16809
  return;
15780
- const slides = Array.from(this.slidesContainer.children);
15781
- slides.forEach((slide, index) => {
15782
- slide.classList.toggle('active', index === this.currentSlide);
15783
- });
16810
+ // Calculate the translation distance based on current slide
16811
+ const translateX = -this.currentSlide * 100;
16812
+ this.slidesContainer.style.transform = `translateX(${translateX}%)`;
15784
16813
  this.updateDots();
15785
16814
  }
15786
16815
  updateDots() {
@@ -16032,141 +17061,6 @@ class ElementService {
16032
17061
  }
16033
17062
  }
16034
17063
 
16035
- class UniqueIdGenerator {
16036
- /**
16037
- * Initialize the generator with a node ID
16038
- * @param nodeId Number between 0-1023 to identify this instance
16039
- */
16040
- static initialize(nodeId = Math.floor(Math.random() * 1024)) {
16041
- if (nodeId < 0 || nodeId >= 1 << this.nodeBits) {
16042
- throw new Error(`Node ID must be between 0 and ${(1 << this.nodeBits) - 1}`);
16043
- }
16044
- this.nodeId = nodeId;
16045
- }
16046
- /**
16047
- * Convert a number to base32 string with specified length
16048
- */
16049
- static toBase32(num, length) {
16050
- let result = '';
16051
- while (num > 0) {
16052
- result = this.base32Chars[Number(num % BigInt(32))] + result;
16053
- // @ts-expect-error - TS doesn't support bigint division
16054
- num = num / 32n;
16055
- }
16056
- return result.padStart(length, '0');
16057
- }
16058
- /**
16059
- * Generate a cryptographically secure random number
16060
- */
16061
- static getSecureRandom() {
16062
- if (typeof crypto !== 'undefined') {
16063
- const buffer = new Uint32Array(1);
16064
- crypto.getRandomValues(buffer);
16065
- return buffer[0];
16066
- }
16067
- return Math.floor(Math.random() * 0xffffffff);
16068
- }
16069
- /**
16070
- * Wait until next millisecond
16071
- */
16072
- static waitNextMillis(lastTimestamp) {
16073
- let timestamp = Date.now();
16074
- while (timestamp <= lastTimestamp) {
16075
- timestamp = Date.now();
16076
- }
16077
- return timestamp;
16078
- }
16079
- /**
16080
- * Generates a highly unique ID with the following format:
16081
- * TTTTTTTTTTCCCCNNNNNRRRR
16082
- * T: Timestamp (10 chars)
16083
- * C: Counter (4 chars)
16084
- * N: Node ID (5 chars)
16085
- * R: Random (4 chars)
16086
- *
16087
- * Total length: 23 characters, always uppercase alphanumeric
16088
- */
16089
- static generate() {
16090
- if (this.nodeId === undefined) {
16091
- this.initialize();
16092
- }
16093
- let timestamp = Date.now() - this.epoch;
16094
- // Handle clock moving backwards or same millisecond
16095
- if (timestamp < this.lastTimestamp) {
16096
- throw new Error('Clock moved backwards. Refusing to generate ID.');
16097
- }
16098
- if (timestamp === this.lastTimestamp) {
16099
- this.sequence = (this.sequence + 1) & ((1 << this.sequenceBits) - 1);
16100
- if (this.sequence === 0) {
16101
- timestamp = this.waitNextMillis(this.lastTimestamp);
16102
- }
16103
- }
16104
- else {
16105
- this.sequence = 0;
16106
- }
16107
- this.lastTimestamp = timestamp;
16108
- // Generate random component
16109
- const random = this.getSecureRandom() & 0xffff; // 16 bits of randomness
16110
- // Combine all components into a BigInt
16111
- // const id =
16112
- // (BigInt(timestamp) << BigInt(this.nodeBits + this.sequenceBits + 16)) |
16113
- // (BigInt(this.nodeId) << BigInt(this.sequenceBits + 16)) |
16114
- // (BigInt(this.sequence) << BigInt(16)) |
16115
- // BigInt(random);
16116
- // Convert to base32 representation
16117
- const timeComponent = this.toBase32(BigInt(timestamp), 10);
16118
- const counterComponent = this.toBase32(BigInt(this.sequence), 4);
16119
- const nodeComponent = this.toBase32(BigInt(this.nodeId), 5);
16120
- const randomComponent = this.toBase32(BigInt(random), 4);
16121
- return `${timeComponent}${counterComponent}${nodeComponent}${randomComponent}`;
16122
- }
16123
- /**
16124
- * Validates if a string matches the expected ID format
16125
- */
16126
- static isValid(id) {
16127
- if (!/^[0-9A-HJ-NP-Z]{23}$/.test(id))
16128
- return false;
16129
- try {
16130
- const timeComponent = id.slice(0, 10);
16131
- const timestamp = this.decodeBase32(timeComponent);
16132
- const now = Date.now() - this.epoch;
16133
- return timestamp >= 0 && timestamp <= now;
16134
- }
16135
- catch (_a) {
16136
- return false;
16137
- }
16138
- }
16139
- /**
16140
- * Decode base32 string to number
16141
- */
16142
- static decodeBase32(str) {
16143
- let result = 0;
16144
- for (const char of str) {
16145
- result = result * 32 + this.base32Chars.indexOf(char);
16146
- }
16147
- return result;
16148
- }
16149
- /**
16150
- * Extract timestamp from ID
16151
- */
16152
- static getTimestamp(id) {
16153
- if (!this.isValid(id))
16154
- throw new Error('Invalid ID format');
16155
- const timeComponent = id.slice(0, 10);
16156
- const timestamp = this.decodeBase32(timeComponent);
16157
- return new Date(timestamp + this.epoch);
16158
- }
16159
- }
16160
- // Constants for bit manipulation
16161
- UniqueIdGenerator.epoch = 1577836800000; // 2020-01-01 as epoch
16162
- UniqueIdGenerator.nodeBits = 10;
16163
- UniqueIdGenerator.sequenceBits = 12;
16164
- // Instance variables
16165
- UniqueIdGenerator.lastTimestamp = -1;
16166
- UniqueIdGenerator.sequence = 0;
16167
- // Character set for base32 encoding (excluding similar looking characters)
16168
- UniqueIdGenerator.base32Chars = '0123456789ABCDEFGHJKMNPQRSTVWXYZ';
16169
-
16170
17064
  function convertHexToRgba(hex, opacity = 1) {
16171
17065
  // Remove # if present
16172
17066
  const cleanHex = hex.replace('#', '');
@@ -16204,12 +17098,13 @@ function spotHtmlStringToElement(htmlString) {
16204
17098
  spot.className = 'spot';
16205
17099
  spot.innerHTML = htmlString;
16206
17100
  Object.assign(spot.style, {
16207
- position: 'relative',
16208
17101
  display: 'block',
16209
17102
  width: '100%',
16210
17103
  height: '100%',
16211
17104
  margin: '0',
16212
17105
  padding: '0',
17106
+ containerType: 'inline-size',
17107
+ position: 'relative',
16213
17108
  });
16214
17109
  return spot;
16215
17110
  }
@@ -17079,7 +17974,6 @@ const STYLES$6 = ({ textColor = '#ffffff', ctaTextColor = textColor, ctaBorderCo
17079
17974
  box-sizing: border-box;
17080
17975
  color: ${textColor};
17081
17976
  cursor: pointer;
17082
- container-type: inline-size;
17083
17977
  }
17084
17978
 
17085
17979
  .${prefix}__text {
@@ -17095,7 +17989,7 @@ const STYLES$6 = ({ textColor = '#ffffff', ctaTextColor = textColor, ctaBorderCo
17095
17989
  .${prefix}__header {
17096
17990
  font-size: 24px;
17097
17991
  margin: 0;
17098
- font-family: "Cormorant";
17992
+ font-family: "Cormorant", system-ui;
17099
17993
  font-style: normal;
17100
17994
  font-weight: 300;
17101
17995
  line-height: normal;
@@ -17214,7 +18108,6 @@ const STYLES$5 = ({ textColor = '#212121', backgroundColor = '#e8e6de', ctaTextC
17214
18108
  height: 100%;
17215
18109
  display: block;
17216
18110
  position: relative;
17217
- container-type: inline-size;
17218
18111
  }
17219
18112
 
17220
18113
  .${prefix}__content {
@@ -17405,15 +18298,20 @@ function rbHomepageHeroThreeTileTemplate(spot, config) {
17405
18298
  const STYLES$4 = ({ textColor = '#212121', backgroundColor = '#e8e6de', ctaTextColor = textColor, primaryImage, mobilePrimaryImage = primaryImage, }, { prefix }) => `
17406
18299
  <style>
17407
18300
  .${prefix} {
18301
+ width: 100%;
18302
+ height: 100%;
18303
+ background-color: transparent;
18304
+ cursor: pointer;
18305
+ position: relative;
18306
+ }
18307
+
18308
+ .${prefix}__content {
17408
18309
  width: 100%;
17409
18310
  height: 100%;
17410
18311
  display: flex;
17411
18312
  flex-direction: column-reverse;
17412
18313
  background-color: transparent;
17413
18314
  gap: 6px;
17414
- cursor: pointer;
17415
- container-type: inline-size;
17416
- position: relative;
17417
18315
  }
17418
18316
 
17419
18317
  .${prefix}__image {
@@ -17444,7 +18342,7 @@ const STYLES$4 = ({ textColor = '#212121', backgroundColor = '#e8e6de', ctaTextC
17444
18342
  font-size: 18px;
17445
18343
  margin: 0;
17446
18344
  color: inherit;
17447
- font-family: "Cormorant";
18345
+ font-family: "Cormorant", system-ui;
17448
18346
  font-style: normal;
17449
18347
  font-weight: 700;
17450
18348
  line-height: normal;
@@ -17486,7 +18384,7 @@ const STYLES$4 = ({ textColor = '#212121', backgroundColor = '#e8e6de', ctaTextC
17486
18384
  }
17487
18385
 
17488
18386
  @container (min-width: 768px) {
17489
- .${prefix} {
18387
+ .${prefix}__content {
17490
18388
  flex-direction: row;
17491
18389
  }
17492
18390
  .${prefix}__image {
@@ -17541,12 +18439,14 @@ function rbHomepageHeroTwoTileTemplate(spot, config) {
17541
18439
  ${GFONT_CORMORANT}
17542
18440
  ${STYLES$4(spot, config)}
17543
18441
  <div class="${prefix}">
17544
- <div class="${prefix}__text">
17545
- ${spot.header ? `<h2 class="${prefix}__header">${spot.header}</h2>` : ''}
17546
- ${spot.description ? `<p class="${prefix}__description">${spot.description}</p>` : ''}
17547
- ${spot.ctaText ? `<span class="${prefix}__cta-button">${spot.ctaText}</span>` : ''}
17548
- </div>
17549
- <div class="${prefix}__image"></div>
18442
+ <div class="${prefix}__content">
18443
+ <div class="${prefix}__text">
18444
+ ${spot.header ? `<h2 class="${prefix}__header">${spot.header}</h2>` : ''}
18445
+ ${spot.description ? `<p class="${prefix}__description">${spot.description}</p>` : ''}
18446
+ ${spot.ctaText ? `<span class="${prefix}__cta-button">${spot.ctaText}</span>` : ''}
18447
+ </div>
18448
+ <div class="${prefix}__image"></div>
18449
+ </div>
17550
18450
  </div>
17551
18451
  `;
17552
18452
  }
@@ -17569,12 +18469,10 @@ const STYLES$3 = ({ textColor = '#ffffff', ctaTextColor = textColor, ctaBorderCo
17569
18469
  overflow: hidden;
17570
18470
  cursor: pointer;
17571
18471
  color: ${textColor};
17572
- container-type: inline-size;
17573
18472
  }
17574
18473
 
17575
18474
  .${prefix}__text {
17576
18475
  padding: 20px;
17577
- width: 70%;
17578
18476
  display: flex;
17579
18477
  flex-direction: column;
17580
18478
  justify-content: center;
@@ -17586,7 +18484,7 @@ const STYLES$3 = ({ textColor = '#ffffff', ctaTextColor = textColor, ctaBorderCo
17586
18484
  color: inherit;
17587
18485
  margin: 0;
17588
18486
  font-size: 20px;
17589
- font-family: "Cormorant";
18487
+ font-family: "Cormorant", system-ui;
17590
18488
  font-style: normal;
17591
18489
  font-weight: 300;
17592
18490
  line-height: normal;
@@ -17705,7 +18603,6 @@ const STYLES$2 = ({ textColor = '#ffffff', primaryImage, mobilePrimaryImage = pr
17705
18603
  background-size: cover;
17706
18604
  background-position: center;
17707
18605
  background-repeat: no-repeat;
17708
- container-type: inline-size;
17709
18606
  position: relative;
17710
18607
  }
17711
18608
 
@@ -17775,12 +18672,7 @@ const STYLES$1 = ({ textColor = '#ffffff', primaryImage, mobilePrimaryImage = pr
17775
18672
  border-radius: 5px;
17776
18673
  overflow: hidden;
17777
18674
  cursor: pointer;
17778
- container-type: inline-size;
17779
- }
17780
-
17781
- .${prefix}__text {
17782
- padding: 10px;
17783
- width: 70%;
18675
+ position: relative;
17784
18676
  }
17785
18677
 
17786
18678
  .${prefix}__header {
@@ -17791,6 +18683,7 @@ const STYLES$1 = ({ textColor = '#ffffff', primaryImage, mobilePrimaryImage = pr
17791
18683
  font-style: normal;
17792
18684
  font-weight: 400;
17793
18685
  margin: 0;
18686
+ padding: 10px;
17794
18687
  }
17795
18688
 
17796
18689
  @container (min-width: 640px) {
@@ -17808,9 +18701,7 @@ function rbSmallCategoryImageToutTemplate(spot, config) {
17808
18701
  ${GFONT_CORMORANT}
17809
18702
  ${STYLES$1(spot, config)}
17810
18703
  <div class="${prefix}">
17811
- <div class="${prefix}__text">
17812
- ${spot.header ? `<h2 class="${prefix}__header">${spot.header}</h2>` : ''}
17813
- </div>
18704
+ ${spot.header ? `<h2 class="${prefix}__header">${spot.header}</h2>` : ''}
17814
18705
  </div>
17815
18706
  `;
17816
18707
  }
@@ -17825,7 +18716,6 @@ const STYLES = ({ textColor = '#000000', backgroundColor = 'transparent', primar
17825
18716
  display: flex;
17826
18717
  flex-direction: column;
17827
18718
  border-radius: 5px;
17828
- container-type: inline-size;
17829
18719
  }
17830
18720
 
17831
18721
  .${prefix}__image {
@@ -17961,94 +18851,164 @@ const SPOT_TEMPLATE_HTML_ELEMENT = (spot, config) => {
17961
18851
  return spotHtmlStringToElement(spotHtmlString);
17962
18852
  };
17963
18853
 
17964
- /**
17965
- * PubSub class
17966
- * Manages event subscriptions and publications
17967
- * @template IEventMap A record type defining the structure of events and their data
17968
- */
17969
- class PubSub {
18854
+ // For the moment, we will only focus on sites that use Google Analytics,
18855
+ // but we will add support for other analytics tools in the future.
18856
+ var AnalyticsTool;
18857
+ (function (AnalyticsTool) {
18858
+ AnalyticsTool["GoogleAnalytics"] = "google-analytics";
18859
+ AnalyticsTool["Other"] = "Other";
18860
+ })(AnalyticsTool || (AnalyticsTool = {}));
18861
+
18862
+ class DataLayerMonitor {
18863
+ constructor() {
18864
+ if (!window.dataLayer) {
18865
+ return;
18866
+ }
18867
+ this.originalPush = window.dataLayer.push;
18868
+ }
18869
+ static getInstance() {
18870
+ if (!DataLayerMonitor.instance) {
18871
+ DataLayerMonitor.instance = new DataLayerMonitor();
18872
+ }
18873
+ return DataLayerMonitor.instance;
18874
+ }
18875
+ setListener(listener) {
18876
+ this.listener = listener;
18877
+ }
18878
+ start() {
18879
+ window.dataLayer.push = (...args) => {
18880
+ const result = this.originalPush.apply(window.dataLayer, args);
18881
+ const pushedEvent = args[0];
18882
+ if (this.listener) {
18883
+ const normalizedData = this.cleanEventData(pushedEvent);
18884
+ if (normalizedData) {
18885
+ this.listener(normalizedData);
18886
+ }
18887
+ }
18888
+ return result;
18889
+ };
18890
+ }
18891
+ cleanEventData(data) {
18892
+ const eventName = getEventTypeFromRawEvent(data.event);
18893
+ if (!eventName) {
18894
+ return null;
18895
+ }
18896
+ const productIds = extractDeepIds(data.value);
18897
+ return {
18898
+ event: eventName,
18899
+ productIds,
18900
+ };
18901
+ }
18902
+ stop() {
18903
+ if (this.originalPush) {
18904
+ window.dataLayer.push = this.originalPush;
18905
+ }
18906
+ this.listener = undefined;
18907
+ }
18908
+ }
18909
+
18910
+ // @TODO: Add support for user to push events to our own data layer, if they don't use any analytics tool.
18911
+ // window.rmnDataLayer = window.rmnDataLayer || [];
18912
+ class MonitorService {
17970
18913
  constructor() {
17971
- /**
17972
- * Object to store subscribers for each event type
17973
- */
17974
- this.subscribers = {};
18914
+ const analyticsTool = this.detectAnalyticsTool();
18915
+ switch (analyticsTool) {
18916
+ case AnalyticsTool.GoogleAnalytics:
18917
+ this.implementedMonitor = DataLayerMonitor.getInstance();
18918
+ break;
18919
+ case AnalyticsTool.Other:
18920
+ default:
18921
+ console.warn('This site uses an unsupported analytics tool.');
18922
+ break;
18923
+ }
18924
+ if (analyticsTool === AnalyticsTool.Other) {
18925
+ return;
18926
+ }
18927
+ this.pubSubService = PubsubService.getInstance();
18928
+ this.localStorageService = LocalStorageService.getInstance();
17975
18929
  }
17976
18930
  static getInstance() {
17977
- if (!PubSub.instance) {
17978
- PubSub.instance = new PubSub();
18931
+ if (!MonitorService.instance) {
18932
+ MonitorService.instance = new MonitorService();
17979
18933
  }
17980
- return PubSub.instance;
18934
+ return MonitorService.instance;
17981
18935
  }
17982
- /**
17983
- * Subscribe to an event
17984
- * @param eventType - The type of event to subscribe to
17985
- * @param callback - The function to be called when the event is published
17986
- * @returns A function to unsubscribe from the event
17987
- *
17988
- * @Example:
17989
- * const unsubscribe = pubSub.subscribe('userLogin', (data) => {
17990
- * console.log(`User ${data.username} logged in`);
17991
- * });
17992
- */
17993
- subscribe(eventType, callback) {
17994
- if (!this.subscribers[eventType]) {
17995
- this.subscribers[eventType] = [];
18936
+ start() {
18937
+ if (!this.implementedMonitor)
18938
+ return;
18939
+ this.implementedMonitor.setListener(async (eventData) => {
18940
+ var _a;
18941
+ await this.matchAndFireEvent(eventData, (_a = this.localStorageService) === null || _a === void 0 ? void 0 : _a.getSpots());
18942
+ });
18943
+ this.implementedMonitor.start();
18944
+ }
18945
+ async matchAndFireEvent(eventData, spots) {
18946
+ var _a, _b;
18947
+ if (!spots)
18948
+ return;
18949
+ const eventProductIds = new Set(eventData.productIds);
18950
+ for (const spot of Object.values(spots)) {
18951
+ if (!spot.productIds.length)
18952
+ continue;
18953
+ const hasCommonProductIds = spot.productIds.find((productId) => eventProductIds.has(productId));
18954
+ if (hasCommonProductIds) {
18955
+ if (Object.values(RMN_SPOT_EVENT).includes(eventData.event)) {
18956
+ await this.fireAndPublishSpotEvent({
18957
+ spotEvent: eventData.event,
18958
+ eventUrl: (_b = (_a = spot.events.find((event) => event.event === eventData.event)) === null || _a === void 0 ? void 0 : _a.url) !== null && _b !== void 0 ? _b : '',
18959
+ placementId: spot.placementId,
18960
+ spotId: spot.spotId,
18961
+ });
18962
+ }
18963
+ }
17996
18964
  }
17997
- this.subscribers[eventType].push(callback);
17998
- // Return an unsubscribe function
17999
- return () => {
18000
- this.subscribers[eventType] = this.subscribers[eventType].filter((cb) => cb !== callback);
18001
- };
18002
18965
  }
18003
- /**
18004
- * Publish an event
18005
- * @param eventType - The type of event to publish
18006
- * @param data - The data to be passed to the event subscribers
18007
- *
18008
- * @Example:
18009
- * pubSub.publish('userLogin', { username: 'john_doe', timestamp: Date.now() });
18010
- */
18011
- publish(eventType, data) {
18012
- if (!this.subscribers[eventType]) {
18966
+ async fireAndPublishSpotEvent({ spotEvent, eventUrl, placementId, spotId, }) {
18967
+ await fireEvent({
18968
+ event: spotEvent,
18969
+ eventUrl,
18970
+ });
18971
+ if (!this.pubSubService)
18013
18972
  return;
18973
+ this.pubSubService.publish(RMN_EVENT.SPOT_EVENT, {
18974
+ eventType: spotEvent,
18975
+ placementId,
18976
+ spotId,
18977
+ });
18978
+ }
18979
+ detectAnalyticsTool() {
18980
+ let analyticsTool = AnalyticsTool.Other;
18981
+ // Check for Google Analytics
18982
+ if (typeof window.ga !== 'undefined') {
18983
+ analyticsTool = AnalyticsTool.GoogleAnalytics;
18014
18984
  }
18015
- this.subscribers[eventType].forEach((callback) => callback(data));
18985
+ // Check for Google Analytics 4
18986
+ if (typeof window.gtag !== 'undefined') {
18987
+ analyticsTool = AnalyticsTool.GoogleAnalytics;
18988
+ }
18989
+ // Check for Google Tag Manager
18990
+ if (typeof window.google_tag_manager !== 'undefined') {
18991
+ analyticsTool = AnalyticsTool.GoogleAnalytics;
18992
+ }
18993
+ // @TODO: Add support for other analytics tools
18994
+ // Check for Heap Analytics
18995
+ // Check for Mixpanel
18996
+ // Check for Woopra
18997
+ // Check for Segment
18998
+ // Check for Amplitude
18999
+ return analyticsTool;
18016
19000
  }
18017
19001
  }
18018
- /**
18019
- * Usage Example:
18020
- *
18021
- * interface IEventMap {
18022
- * userLogin: { username: string; timestamp: number };
18023
- * pageView: { url: string; timestamp: number };
18024
- * }
18025
- *
18026
- * const pubSub = new PubSub<IEventMap>();
18027
- *
18028
- * // Subscribe to events
18029
- * const unsubscribeLogin = pubSub.subscribe('userLogin', (data) => {
18030
- * console.log(`User ${data.username} logged in at ${new Date(data.timestamp)}`);
18031
- * });
18032
- *
18033
- * pubSub.subscribe('pageView', (data) => {
18034
- * console.log(`Page ${data.url} viewed at ${new Date(data.timestamp)}`);
18035
- * });
18036
- *
18037
- * // Publish events
18038
- * pubSub.publish('userLogin', { username: 'john_doe', timestamp: Date.now() });
18039
- * pubSub.publish('pageView', { url: '/home', timestamp: Date.now() });
18040
- *
18041
- * // Unsubscribe from an event
18042
- * unsubscribeLogin();
18043
- */
18044
19002
 
18045
19003
  class EventService {
18046
19004
  constructor() {
18047
- this.pubSub = PubSub.getInstance();
18048
- this.localStorage = LocalStorage.getInstance();
19005
+ this.pubSubService = PubsubService.getInstance();
19006
+ this.localStorageService = LocalStorageService.getInstance();
18049
19007
  this.activeSpots = new Map();
18050
19008
  this.spotStates = new Map();
18051
19009
  this.intersectionObserver = new IntersectionObserverService();
19010
+ // Start the user monitor, which will track and check user interactions
19011
+ MonitorService.getInstance().start();
18052
19012
  }
18053
19013
  static getInstance() {
18054
19014
  if (!EventService.instance) {
@@ -18057,16 +19017,16 @@ class EventService {
18057
19017
  return EventService.instance;
18058
19018
  }
18059
19019
  subscribe(eventType, callback) {
18060
- return this.pubSub.subscribe(eventType, callback);
19020
+ return this.pubSubService.subscribe(eventType, callback);
18061
19021
  }
18062
19022
  publish(eventType, data) {
18063
- this.pubSub.publish(eventType, data);
19023
+ this.pubSubService.publish(eventType, data);
18064
19024
  }
18065
19025
  registerSpot(params) {
18066
19026
  const { placementId, spot, spotElement } = params;
18067
19027
  this.activeSpots.set(placementId, { spotElement });
18068
19028
  // Fire impression event
18069
- this.fireImpressionEvent(placementId, spot, spotElement);
19029
+ this.fireImpressionEvent(placementId, spot);
18070
19030
  // Handle intersection observer
18071
19031
  this.handleIntersectionObserver(placementId, spot, spotElement);
18072
19032
  // Attach click event listener
@@ -18141,30 +19101,42 @@ class EventService {
18141
19101
  },
18142
19102
  };
18143
19103
  }
18144
- this.spotStates.set(placementId, { ...currentState, ...updates });
19104
+ const merged = this.deepMerge(currentState, updates);
19105
+ this.spotStates.set(placementId, merged);
18145
19106
  if (publish) {
18146
- this.pubSub.publish(RMN_SPOT_EVENT.LIFECYCLE_STATE, this.spotStates.get(placementId));
19107
+ this.pubSubService.publish(RMN_EVENT.LIFECYCLE_STATE, this.spotStates.get(placementId));
18147
19108
  }
18148
19109
  }
18149
- async handleClick({ placementId, spot, spotElement, }) {
19110
+ deepMerge(current, updates) {
19111
+ return {
19112
+ identifier: updates.identifier
19113
+ ? { ...current.identifier, ...updates.identifier }
19114
+ : current.identifier,
19115
+ dom: updates.dom ? { ...current.dom, ...updates.dom } : current.dom,
19116
+ state: updates.state ? { ...current.state, ...updates.state } : current.state,
19117
+ displayConfig: updates.displayConfig
19118
+ ? { ...current.displayConfig, ...updates.displayConfig }
19119
+ : current.displayConfig,
19120
+ };
19121
+ }
19122
+ async handleClick({ placementId, spot }) {
18150
19123
  var _a, _b, _c;
18151
- // Publish click event
18152
- this.pubSub.publish(RMN_SPOT_EVENT.CLICK, {
19124
+ this.pubSubService.publish(RMN_EVENT.SPOT_EVENT, {
19125
+ eventType: RMN_SPOT_EVENT.CLICK,
18153
19126
  placementId,
18154
19127
  spotId: spot.id,
18155
- spotElement,
18156
19128
  });
18157
- // Fire click event
18158
- await this.fireEvent({
19129
+ await fireEvent({
18159
19130
  event: RMN_SPOT_EVENT.CLICK,
18160
19131
  eventUrl: (_b = (_a = spot.events.find((event) => event.event === RMN_SPOT_EVENT.CLICK)) === null || _a === void 0 ? void 0 : _a.url) !== null && _b !== void 0 ? _b : '',
18161
19132
  });
18162
19133
  // Save spot to local storage for event tracking
18163
- this.localStorage.setSpot(spot.id, {
19134
+ this.localStorageService.setSpot(spot.id, {
19135
+ placementId,
18164
19136
  spotId: spot.id,
18165
19137
  spotType: spot.spot,
18166
19138
  events: spot.events,
18167
- productIds: (_c = spot.productIds) !== null && _c !== void 0 ? _c : [1, 'GROUPING-12345', 'DAN-12345', 131398103],
19139
+ productIds: (_c = spot.productIds) !== null && _c !== void 0 ? _c : [1, 2, 3],
18168
19140
  });
18169
19141
  }
18170
19142
  handleIntersectionObserver(placementId, _spot, spotElement) {
@@ -18179,51 +19151,20 @@ class EventService {
18179
19151
  };
18180
19152
  this.intersectionObserver.observe(spotElement, spotIsVisibleCallback);
18181
19153
  }
18182
- fireImpressionEvent(placementId, spot, spotElement) {
18183
- this.pubSub.publish(RMN_SPOT_EVENT.IMPRESSION, {
19154
+ fireImpressionEvent(placementId, spot) {
19155
+ this.pubSubService.publish(RMN_EVENT.SPOT_EVENT, {
19156
+ eventType: RMN_SPOT_EVENT.IMPRESSION,
18184
19157
  placementId,
18185
19158
  spotId: spot.id,
18186
- spotElement,
18187
19159
  });
18188
19160
  (async () => {
18189
19161
  var _a, _b;
18190
- await this.fireEvent({
19162
+ await fireEvent({
18191
19163
  event: RMN_SPOT_EVENT.IMPRESSION,
18192
19164
  eventUrl: (_b = (_a = spot.events.find((event) => event.event === RMN_SPOT_EVENT.IMPRESSION)) === null || _a === void 0 ? void 0 : _a.url) !== null && _b !== void 0 ? _b : '',
18193
19165
  });
18194
19166
  })();
18195
19167
  }
18196
- /**
18197
- * Fires an event using the navigator.sendBeacon method and redirects the user if the event is a click event.
18198
- *
18199
- * @param {IFireEventParams} params - The parameters for firing the event.
18200
- * @param {RMN_SPOT_EVENT} params.event - The event type.
18201
- * @param {string} params.eventUrl - The URL to which the event is sent.
18202
- * @returns {Promise<void>} - A promise that resolves when the event is fired.
18203
- */
18204
- async fireEvent({ event, eventUrl }) {
18205
- const didFireEvent = navigator.sendBeacon(eventUrl);
18206
- if (didFireEvent && event === RMN_SPOT_EVENT.CLICK) {
18207
- window.location.href = this.getRedirectUrlFromPayload(eventUrl);
18208
- }
18209
- }
18210
- /**
18211
- * Extracts and decodes a URL from a base64-encoded query parameter.
18212
- *
18213
- * @param {string} url - The URL containing the base64-encoded query parameter.
18214
- * @returns {string} - The decoded URL or an empty string if decoding fails.
18215
- */
18216
- getRedirectUrlFromPayload(url) {
18217
- var _a, _b;
18218
- const base64String = (_a = new URL(url).searchParams.get('e')) !== null && _a !== void 0 ? _a : '';
18219
- try {
18220
- const data = JSON.parse(atob(base64String));
18221
- return (_b = data.ur) !== null && _b !== void 0 ? _b : '';
18222
- }
18223
- catch (_c) {
18224
- return '';
18225
- }
18226
- }
18227
19168
  }
18228
19169
 
18229
19170
  const SELECTION_API_PATH = '/spots/selection';
@@ -18256,176 +19197,6 @@ class SelectionService extends BaseApi {
18256
19197
  }
18257
19198
  }
18258
19199
 
18259
- const SPOT_EVENTS_EXAMPLE = [
18260
- {
18261
- event: RMN_SPOT_EVENT.CLICK,
18262
- url: 'https://dev.rmn.liquidcommerce.cloud/api/spots/event?e=eyJ2IjoiMS4xMiIsImF2IjozMDY1NzgzLCJhdCI6MTYzLCJidCI6MCwiY20iOjQ0MDE5MjQxOCwiY2giOjYzMTg0LCJjayI6e30sImNyIjo0ODE4NTUzNzUsImRpIjoiOWMxNGFhMGI3NWY4NDMxNTllMTAwYWQzNzA1NzQyYzMiLCJkaiI6MCwiaWkiOiIxZjU0MGM5NmQ1N2M0YmZjODFlZjRkNjhkMzFjNDVkOSIsImRtIjozLCJmYyI6NjU2NjgyNTQ5LCJmbCI6NjQzOTMxODIxLCJpcCI6IjM1LjIyMy4xOTguOTUiLCJudyI6MTE1MDAsInBjIjo1MDAwLCJvcCI6NTAwMCwibXAiOjUwMDAsImVjIjowLCJnbSI6MCwiZXAiOm51bGwsInByIjoyNDkzMTYsInJ0IjoxLCJycyI6NTAwLCJzYSI6IjU1Iiwic2IiOiJpLTA0MDI0ODg4ZDlkMWRjZWQ3Iiwic3AiOjI3MjI3Miwic3QiOjEyODcyOTYsInRyIjp0cnVlLCJ1ayI6IjNhZWRhMWMxLTZhYjItNDUwNy04Mzg5LTEwZTJmNDMxYjM5MSIsInRzIjoxNzI5MzU5MDU0OTI3LCJiZiI6dHJ1ZSwicG4iOiJyYlByb2R1Y3RVcGNzIiwiZ2MiOnRydWUsImdDIjp0cnVlLCJncyI6Im5vbmUiLCJkYyI6MSwidHoiOiJBbWVyaWNhL05ld19Zb3JrIiwidXIiOm51bGx9&s=hWz37kbxi_u95EVNn2aoQhc5Aas',
18263
- },
18264
- {
18265
- event: RMN_SPOT_EVENT.IMPRESSION,
18266
- url: 'https://dev.rmn.liquidcommerce.cloud/api/spots/event?e=eyJ2IjoiMS4xMiIsImF2IjozMDY1NzgzLCJhdCI6MTYzLCJidCI6MCwiY20iOjQ0MDE5MjQxOCwiY2giOjYzMTg0LCJjayI6e30sImNyIjo0ODE4NTUzNzUsImRpIjoiOWMxNGFhMGI3NWY4NDMxNTllMTAwYWQzNzA1NzQyYzMiLCJkaiI6MCwiaWkiOiIxZjU0MGM5NmQ1N2M0YmZjODFlZjRkNjhkMzFjNDVkOSIsImRtIjozLCJmYyI6NjU2NjgyNTQ5LCJmbCI6NjQzOTMxODIxLCJpcCI6IjM1LjIyMy4xOTguOTUiLCJudyI6MTE1MDAsInBjIjo1MDAwLCJvcCI6NTAwMCwibXAiOjUwMDAsImVjIjowLCJnbSI6MCwiZXAiOm51bGwsInByIjoyNDkzMTYsInJ0IjoxLCJycyI6NTAwLCJzYSI6IjU1Iiwic2IiOiJpLTA0MDI0ODg4ZDlkMWRjZWQ3Iiwic3AiOjI3MjI3Miwic3QiOjEyODcyOTYsInRyIjp0cnVlLCJ1ayI6IjNhZWRhMWMxLTZhYjItNDUwNy04Mzg5LTEwZTJmNDMxYjM5MSIsInRzIjoxNzI5MzU5MDU0OTI3LCJiZiI6dHJ1ZSwicG4iOiJyYlByb2R1Y3RVcGNzIiwiZ2MiOnRydWUsImdDIjp0cnVlLCJncyI6Im5vbmUiLCJkYyI6MSwidHoiOiJBbWVyaWNhL05ld19Zb3JrIiwiYmEiOjEsImZxIjowfQ&s=djoysjCimurf-5T11AlNAwwLSS8',
18267
- },
18268
- {
18269
- event: RMN_SPOT_EVENT.PURCHASE,
18270
- url: 'https://dev.rmn.liquidcommerce.cloud/api/spots/event?e=eyJ2IjoiMS4xMiIsImF2IjozMDY1NzgzLCJhdCI6MTYzLCJidCI6MCwiY20iOjQ0MDE5MjQxOCwiY2giOjYzMTg0LCJjayI6e30sImNyIjo0ODE4NTUzNzUsImRpIjoiOWMxNGFhMGI3NWY4NDMxNTllMTAwYWQzNzA1NzQyYzMiLCJkaiI6MCwiaWkiOiIxZjU0MGM5NmQ1N2M0YmZjODFlZjRkNjhkMzFjNDVkOSIsImRtIjozLCJmYyI6NjU2NjgyNTQ5LCJmbCI6NjQzOTMxODIxLCJpcCI6IjM1LjIyMy4xOTguOTUiLCJudyI6MTE1MDAsInBjIjo1MDAwLCJvcCI6NTAwMCwibXAiOjUwMDAsImVjIjowLCJnbSI6MCwiZXAiOm51bGwsInByIjoyNDkzMTYsInJ0IjoxLCJycyI6NTAwLCJzYSI6IjU1Iiwic2IiOiJpLTA0MDI0ODg4ZDlkMWRjZWQ3Iiwic3AiOjI3MjI3Miwic3QiOjEyODcyOTYsInRyIjp0cnVlLCJ1ayI6IjNhZWRhMWMxLTZhYjItNDUwNy04Mzg5LTEwZTJmNDMxYjM5MSIsInRzIjoxNzI5MzU5MDU0OTI3LCJiZiI6dHJ1ZSwicG4iOiJyYlByb2R1Y3RVcGNzIiwiZ2MiOnRydWUsImdDIjp0cnVlLCJncyI6Im5vbmUiLCJkYyI6MSwidHoiOiJBbWVyaWNhL05ld19Zb3JrIiwiZXQiOjU5fQ&s=AAPAw-3SfZ0JMzjEGFSwt9L-2S4',
18271
- },
18272
- {
18273
- event: RMN_SPOT_EVENT.ADD_TO_CART,
18274
- url: 'https://dev.rmn.liquidcommerce.cloud/api/spots/event?e=eyJ2IjoiMS4xMiIsImF2IjozMDY1NzgzLCJhdCI6MTYzLCJidCI6MCwiY20iOjQ0MDE5MjQxOCwiY2giOjYzMTg0LCJjayI6e30sImNyIjo0ODE4NTUzNzUsImRpIjoiOWMxNGFhMGI3NWY4NDMxNTllMTAwYWQzNzA1NzQyYzMiLCJkaiI6MCwiaWkiOiIxZjU0MGM5NmQ1N2M0YmZjODFlZjRkNjhkMzFjNDVkOSIsImRtIjozLCJmYyI6NjU2NjgyNTQ5LCJmbCI6NjQzOTMxODIxLCJpcCI6IjM1LjIyMy4xOTguOTUiLCJudyI6MTE1MDAsInBjIjo1MDAwLCJvcCI6NTAwMCwibXAiOjUwMDAsImVjIjowLCJnbSI6MCwiZXAiOm51bGwsInByIjoyNDkzMTYsInJ0IjoxLCJycyI6NTAwLCJzYSI6IjU1Iiwic2IiOiJpLTA0MDI0ODg4ZDlkMWRjZWQ3Iiwic3AiOjI3MjI3Miwic3QiOjEyODcyOTYsInRyIjp0cnVlLCJ1ayI6IjNhZWRhMWMxLTZhYjItNDUwNy04Mzg5LTEwZTJmNDMxYjM5MSIsInRzIjoxNzI5MzU5MDU0OTI3LCJiZiI6dHJ1ZSwicG4iOiJyYlByb2R1Y3RVcGNzIiwiZ2MiOnRydWUsImdDIjp0cnVlLCJncyI6Im5vbmUiLCJkYyI6MSwidHoiOiJBbWVyaWNhL05ld19Zb3JrIiwiZXQiOjYwfQ&s=uzQFcjgL7m9XqUG8FvTPVN5YkZY',
18275
- },
18276
- {
18277
- event: RMN_SPOT_EVENT.ADD_TO_WISHLIST,
18278
- url: 'https://dev.rmn.liquidcommerce.cloud/api/spots/event?e=eyJ2IjoiMS4xMiIsImF2IjozMDY1NzgzLCJhdCI6MTYzLCJidCI6MCwiY20iOjQ0MDE5MjQxOCwiY2giOjYzMTg0LCJjayI6e30sImNyIjo0ODE4NTUzNzUsImRpIjoiOWMxNGFhMGI3NWY4NDMxNTllMTAwYWQzNzA1NzQyYzMiLCJkaiI6MCwiaWkiOiIxZjU0MGM5NmQ1N2M0YmZjODFlZjRkNjhkMzFjNDVkOSIsImRtIjozLCJmYyI6NjU2NjgyNTQ5LCJmbCI6NjQzOTMxODIxLCJpcCI6IjM1LjIyMy4xOTguOTUiLCJudyI6MTE1MDAsInBjIjo1MDAwLCJvcCI6NTAwMCwibXAiOjUwMDAsImVjIjowLCJnbSI6MCwiZXAiOm51bGwsInByIjoyNDkzMTYsInJ0IjoxLCJycyI6NTAwLCJzYSI6IjU1Iiwic2IiOiJpLTA0MDI0ODg4ZDlkMWRjZWQ3Iiwic3AiOjI3MjI3Miwic3QiOjEyODcyOTYsInRyIjp0cnVlLCJ1ayI6IjNhZWRhMWMxLTZhYjItNDUwNy04Mzg5LTEwZTJmNDMxYjM5MSIsInRzIjoxNzI5MzU5MDU0OTI3LCJiZiI6dHJ1ZSwicG4iOiJyYlByb2R1Y3RVcGNzIiwiZ2MiOnRydWUsImdDIjp0cnVlLCJncyI6Im5vbmUiLCJkYyI6MSwidHoiOiJBbWVyaWNhL05ld19Zb3JrIiwiZXQiOjYzfQ&s=m3ISU_iIy-OFtXrTKpI6cJAEC0k',
18279
- },
18280
- {
18281
- event: RMN_SPOT_EVENT.BUY_NOW,
18282
- url: 'https://dev.rmn.liquidcommerce.cloud/api/spots/event?e=eyJ2IjoiMS4xMiIsImF2IjozMDY1NzgzLCJhdCI6MTYzLCJidCI6MCwiY20iOjQ0MDE5MjQxOCwiY2giOjYzMTg0LCJjayI6e30sImNyIjo0ODE4NTUzNzUsImRpIjoiOWMxNGFhMGI3NWY4NDMxNTllMTAwYWQzNzA1NzQyYzMiLCJkaiI6MCwiaWkiOiIxZjU0MGM5NmQ1N2M0YmZjODFlZjRkNjhkMzFjNDVkOSIsImRtIjozLCJmYyI6NjU2NjgyNTQ5LCJmbCI6NjQzOTMxODIxLCJpcCI6IjM1LjIyMy4xOTguOTUiLCJudyI6MTE1MDAsInBjIjo1MDAwLCJvcCI6NTAwMCwibXAiOjUwMDAsImVjIjowLCJnbSI6MCwiZXAiOm51bGwsInByIjoyNDkzMTYsInJ0IjoxLCJycyI6NTAwLCJzYSI6IjU1Iiwic2IiOiJpLTA0MDI0ODg4ZDlkMWRjZWQ3Iiwic3AiOjI3MjI3Miwic3QiOjEyODcyOTYsInRyIjp0cnVlLCJ1ayI6IjNhZWRhMWMxLTZhYjItNDUwNy04Mzg5LTEwZTJmNDMxYjM5MSIsInRzIjoxNzI5MzU5MDU0OTI3LCJiZiI6dHJ1ZSwicG4iOiJyYlByb2R1Y3RVcGNzIiwiZ2MiOnRydWUsImdDIjp0cnVlLCJncyI6Im5vbmUiLCJkYyI6MSwidHoiOiJBbWVyaWNhL05ld19Zb3JrIiwiZXQiOjY5fQ&s=l6MOscQC-q-FkC2Ksd7w6jjySCQ',
18283
- },
18284
- ];
18285
- const RB_SPOTS_SELECTION_EXAMPLE = {
18286
- rbHomepageHeroFullImage: [
18287
- {
18288
- id: '111111_111111',
18289
- spot: RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_FULL_IMAGE,
18290
- variant: RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_FULL_IMAGE,
18291
- width: 1140,
18292
- height: 640,
18293
- header: 'Artisanal Craft Beer Collection',
18294
- description: 'Discover our curated selection of small-batch, flavor-packed craft beers.',
18295
- ctaText: 'Explore the Collection',
18296
- textColor: '#ffffff',
18297
- ctaTextColor: '#ffffff',
18298
- primaryImage: 'https://placehold.co/1140x640/png?text=Craft+Beer+Collection',
18299
- mobilePrimaryImage: 'https://placehold.co/640x640/png?text=Mobile+Craft+Beer',
18300
- events: SPOT_EVENTS_EXAMPLE,
18301
- },
18302
- {
18303
- id: '222222_222222',
18304
- spot: RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_FULL_IMAGE,
18305
- variant: RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_FULL_IMAGE,
18306
- width: 1140,
18307
- height: 640,
18308
- header: 'Summer Wine Spectacular',
18309
- description: 'Refresh your palate with our handpicked selection of crisp, summer wines.',
18310
- ctaText: 'Shop Summer Wines',
18311
- textColor: '#000000',
18312
- ctaTextColor: '#ffffff',
18313
- primaryImage: 'https://placehold.co/1140x640/png?text=Summer+Wines',
18314
- mobilePrimaryImage: 'https://placehold.co/640x640/png?text=Mobile+Summer+Wines',
18315
- events: SPOT_EVENTS_EXAMPLE,
18316
- },
18317
- ],
18318
- rbHomepageHeroTwoTile: [
18319
- {
18320
- id: '333333_333333',
18321
- spot: RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_TWO_TILE,
18322
- variant: RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_TWO_TILE,
18323
- width: 1140,
18324
- height: 640,
18325
- header: 'Whiskey Wonderland',
18326
- description: 'Embark on a journey through our premium whiskey selection.',
18327
- ctaText: 'Discover Whiskeys',
18328
- textColor: '#ffffff',
18329
- backgroundColor: '#2c1a05',
18330
- ctaTextColor: '#2c1a05',
18331
- primaryImage: 'https://placehold.co/1140x640/png?text=Whiskey+Collection',
18332
- mobilePrimaryImage: 'https://placehold.co/640x640/png?text=Mobile+Whiskey',
18333
- events: SPOT_EVENTS_EXAMPLE,
18334
- },
18335
- ],
18336
- rbHomepageHeroThreeTile: [
18337
- {
18338
- id: '444444_444444',
18339
- spot: RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_THREE_TILE,
18340
- variant: RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_THREE_TILE,
18341
- width: 1140,
18342
- height: 640,
18343
- header: 'Cocktail Essentials',
18344
- description: 'Stock your bar with premium spirits and mixers for the perfect cocktail.',
18345
- ctaText: 'Build Your Bar',
18346
- textColor: '#ffffff',
18347
- backgroundColor: '#1a3c4d',
18348
- ctaTextColor: '#1a3c4d',
18349
- primaryImage: 'https://placehold.co/1140x640/png?text=Cocktail+Spirits',
18350
- secondaryImage: 'https://placehold.co/1140x640/png?text=Cocktail+Mixers',
18351
- mobilePrimaryImage: 'https://placehold.co/640x640/png?text=Mobile+Cocktail+Kit',
18352
- mobileSecondaryImage: 'https://placehold.co/640x320/png?text=Mobile+Cocktail+Mixers',
18353
- events: SPOT_EVENTS_EXAMPLE,
18354
- },
18355
- ],
18356
- rbLargeCategoryImageTout: [
18357
- {
18358
- id: '555555_555555',
18359
- spot: RMN_SPOT_TYPE.RB_LARGE_CATEGORY_IMAGE_TOUT,
18360
- variant: RMN_SPOT_TYPE.RB_LARGE_CATEGORY_IMAGE_TOUT,
18361
- width: 468,
18362
- height: 410,
18363
- header: 'Rare & Limited Edition',
18364
- description: 'Discover our collection of hard-to-find and limited release spirits.',
18365
- textColor: '#ffffff',
18366
- ctaTextColor: '#ffffff',
18367
- primaryImage: 'https://placehold.co/468x410/png?text=Rare+Spirits',
18368
- mobilePrimaryImage: 'https://placehold.co/468x410/png?text=Mobile+Rare+Spirits',
18369
- ctaText: 'Shop Rare Spirits',
18370
- events: SPOT_EVENTS_EXAMPLE,
18371
- },
18372
- ],
18373
- rbSmallDiscoverTout: [
18374
- {
18375
- id: '666666_666666',
18376
- spot: RMN_SPOT_TYPE.RB_SMALL_DISCOVER_TOUT,
18377
- variant: RMN_SPOT_TYPE.RB_SMALL_DISCOVER_TOUT,
18378
- width: 224,
18379
- height: 378,
18380
- header: 'Château Margaux 2015 Bordeaux',
18381
- textColor: '#ffffff',
18382
- primaryImage: 'https://placehold.co/224x378/png?text=Château+Margaux',
18383
- mobilePrimaryImage: 'https://placehold.co/224x378/png?text=Mobile+Château+Margaux',
18384
- events: SPOT_EVENTS_EXAMPLE,
18385
- },
18386
- ],
18387
- rbSmallCategoryImageTout: [
18388
- {
18389
- id: '777777_777777',
18390
- spot: RMN_SPOT_TYPE.RB_SMALL_CATEGORY_IMAGE_TOUT,
18391
- variant: RMN_SPOT_TYPE.RB_SMALL_CATEGORY_IMAGE_TOUT,
18392
- width: 224,
18393
- height: 410,
18394
- header: 'Japanese Sake',
18395
- textColor: '#ffffff',
18396
- primaryImage: 'https://placehold.co/224x410/png?text=Japanese+Sake',
18397
- mobilePrimaryImage: 'https://placehold.co/224x410/png?text=Mobile+Japanese+Sake',
18398
- events: SPOT_EVENTS_EXAMPLE,
18399
- },
18400
- ],
18401
- rbCollectionBannerWithoutTextBlock: [
18402
- {
18403
- id: '888888_888888',
18404
- spot: RMN_SPOT_TYPE.RB_COLLECTION_BANNER_WITHOUT_TEXT_BLOCK,
18405
- variant: RMN_SPOT_TYPE.RB_COLLECTION_BANNER_WITHOUT_TEXT_BLOCK,
18406
- width: 887,
18407
- height: 344,
18408
- primaryImage: 'https://placehold.co/887x344/png?text=Summer+Cocktails',
18409
- mobilePrimaryImage: 'https://placehold.co/887x344/png?text=Mobile+Summer+Cocktails',
18410
- events: SPOT_EVENTS_EXAMPLE,
18411
- },
18412
- ],
18413
- rbNavigationBanner: [
18414
- {
18415
- id: '999999_999999',
18416
- spot: RMN_SPOT_TYPE.RB_NAVIGATION_BANNER,
18417
- variant: RMN_SPOT_TYPE.RB_NAVIGATION_BANNER,
18418
- width: 440,
18419
- height: 220,
18420
- header: 'Explore Tequilas',
18421
- textColor: '#ffffff',
18422
- primaryImage: 'https://placehold.co/440x220/png?text=Tequila+Collection',
18423
- mobilePrimaryImage: 'https://placehold.co/440x220/png?text=Mobile+Tequila+Collection',
18424
- events: SPOT_EVENTS_EXAMPLE,
18425
- },
18426
- ],
18427
- };
18428
-
18429
19200
  class LiquidCommerceRmnClient {
18430
19201
  constructor(auth) {
18431
19202
  this.selectionService = SelectionService.getInstance(auth);
@@ -18439,7 +19210,7 @@ class LiquidCommerceRmnClient {
18439
19210
  *
18440
19211
  * @param {ISpotSelectionParams} params - Spots selection parameters.
18441
19212
  *
18442
- * @return {Promise<ISpots | {error : string}>} - The spots response object.
19213
+ * @return {Promise<ISpots | { error : string }>} - The spots response object.
18443
19214
  */
18444
19215
  async spotSelection(params) {
18445
19216
  return this.selectionService.spotSelection(params);
@@ -18453,13 +19224,19 @@ class LiquidCommerceRmnClient {
18453
19224
  */
18454
19225
  async injectSpotElement(params) {
18455
19226
  var _a;
19227
+ if (typeof window === 'undefined' || typeof document === 'undefined') {
19228
+ console.warn('LiquidCommerce Rmn Sdk: Methods which create elements are only available in browser environments.');
19229
+ return;
19230
+ }
18456
19231
  const config = params.config;
18457
19232
  let inject = params.inject;
18458
19233
  if (!inject.length) {
19234
+ // Handle no spots error state
18459
19235
  this.eventService.handleSpotState('all', {
18460
19236
  state: {
18461
19237
  error: 'No spot elements provided for injection.',
18462
19238
  loading: false,
19239
+ mounted: false,
18463
19240
  },
18464
19241
  });
18465
19242
  return;
@@ -18478,9 +19255,12 @@ class LiquidCommerceRmnClient {
18478
19255
  // const response = await this.useSpotSelectionExample(inject);
18479
19256
  // Handle the response
18480
19257
  if (typeof response === 'object' && 'error' in response) {
19258
+ // Handle request error state
18481
19259
  this.eventService.handleSpotState('all', {
18482
19260
  state: {
18483
19261
  error: response.error,
19262
+ mounted: false,
19263
+ loading: false,
18484
19264
  },
18485
19265
  });
18486
19266
  return;
@@ -18489,9 +19269,11 @@ class LiquidCommerceRmnClient {
18489
19269
  const itemConfig = (_a = item.config) !== null && _a !== void 0 ? _a : config;
18490
19270
  const spots = response[item.placementId];
18491
19271
  if (!(spots === null || spots === void 0 ? void 0 : spots.length)) {
19272
+ // Handle no spots found error state
18492
19273
  this.eventService.handleSpotState(item.placementId, {
18493
19274
  state: {
18494
19275
  error: `No spots found for type "${item.spotType}".`,
19276
+ mounted: false,
18495
19277
  loading: false,
18496
19278
  },
18497
19279
  });
@@ -18500,9 +19282,11 @@ class LiquidCommerceRmnClient {
18500
19282
  const placementId = item.placementId.replace('#', '');
18501
19283
  const placement = document.getElementById(placementId);
18502
19284
  if (!placement) {
19285
+ // Handle placement not found error state
18503
19286
  this.eventService.handleSpotState(item.placementId, {
18504
19287
  state: {
18505
19288
  error: `Placement not found for id "${placementId}".`,
19289
+ mounted: false,
18506
19290
  loading: false,
18507
19291
  },
18508
19292
  });
@@ -18513,16 +19297,18 @@ class LiquidCommerceRmnClient {
18513
19297
  placement.removeAttribute('class');
18514
19298
  Object.assign(placement.style, {
18515
19299
  width: '100%',
18516
- height: 'auto',
19300
+ height: '100%',
18517
19301
  display: 'flex',
18518
19302
  justifyContent: 'center',
18519
19303
  });
19304
+ // Handle single spot
18520
19305
  if (spots.length === 1) {
18521
19306
  const isInjected = this.injectOneSpotElement(item, placement, spots[0], itemConfig);
18522
19307
  if (!isInjected) {
18523
19308
  continue;
18524
19309
  }
18525
19310
  }
19311
+ // Handle multiple spots (carousel)
18526
19312
  if (spots.length > 1) {
18527
19313
  const isInjected = this.injectCarouselSpotElement(placement, spots, itemConfig);
18528
19314
  if (!isInjected) {
@@ -18566,18 +19352,24 @@ class LiquidCommerceRmnClient {
18566
19352
  const carouselSlides = [];
18567
19353
  for (const spotItem of spots) {
18568
19354
  this.eventService.handleSpotState(placement.id, {
19355
+ identifier: {
19356
+ placementId: placement.id,
19357
+ spotType: spotItem.spot,
19358
+ spotId: spotItem.id,
19359
+ },
18569
19360
  displayConfig: {
19361
+ isSingleItem: false,
18570
19362
  isCarousel: true,
18571
19363
  isCarouselItem: true,
18572
- isSingleItem: false,
18573
19364
  },
18574
- }, false);
19365
+ });
18575
19366
  const spot = this.elementService.overrideSpotColors(spotItem, config === null || config === void 0 ? void 0 : config.colors);
18576
19367
  const content = SPOT_TEMPLATE_HTML_ELEMENT(spot, { overlay: config === null || config === void 0 ? void 0 : config.overlay });
18577
19368
  if (!content) {
18578
19369
  this.eventService.handleSpotState(placement.id, {
18579
19370
  state: {
18580
19371
  error: `Failed to inject carousel spot item element. Could not create element for type "${spot.spot}".`,
19372
+ mounted: false,
18581
19373
  loading: false,
18582
19374
  },
18583
19375
  });
@@ -18610,6 +19402,7 @@ class LiquidCommerceRmnClient {
18610
19402
  this.eventService.handleSpotState(placement.id, {
18611
19403
  state: {
18612
19404
  error: `Failed to inject spot carousel element. Could not create spot carousel element.`,
19405
+ mounted: false,
18613
19406
  loading: false,
18614
19407
  },
18615
19408
  });
@@ -18619,10 +19412,12 @@ class LiquidCommerceRmnClient {
18619
19412
  this.eventService.handleSpotState(placement.id, {
18620
19413
  dom: {
18621
19414
  spotElement: carouselElement,
19415
+ visibleOnViewport: false,
18622
19416
  },
18623
19417
  state: {
18624
19418
  mounted: true,
18625
19419
  loading: false,
19420
+ error: undefined,
18626
19421
  },
18627
19422
  });
18628
19423
  return true;
@@ -18639,18 +19434,25 @@ class LiquidCommerceRmnClient {
18639
19434
  */
18640
19435
  injectOneSpotElement(injectItem, placement, spot, config) {
18641
19436
  var _a;
18642
- const spotData = this.elementService.overrideSpotColors(spot, config === null || config === void 0 ? void 0 : config.colors);
18643
19437
  this.eventService.handleSpotState(injectItem.placementId, {
19438
+ identifier: {
19439
+ placementId: injectItem.placementId,
19440
+ spotType: injectItem.spotType,
19441
+ spotId: spot.id,
19442
+ },
18644
19443
  displayConfig: {
18645
19444
  isSingleItem: true,
19445
+ isCarousel: false,
19446
+ isCarouselItem: false,
18646
19447
  },
18647
- }, false);
18648
- // Create the spot template element
19448
+ });
19449
+ const spotData = this.elementService.overrideSpotColors(spot, config === null || config === void 0 ? void 0 : config.colors);
18649
19450
  const content = SPOT_TEMPLATE_HTML_ELEMENT(spotData, { overlay: config === null || config === void 0 ? void 0 : config.overlay });
18650
19451
  if (!content) {
18651
19452
  this.eventService.handleSpotState(injectItem.placementId, {
18652
19453
  state: {
18653
19454
  error: `Failed to inject spot element. Could not create element for type "${injectItem.spotType}".`,
19455
+ mounted: false,
18654
19456
  loading: false,
18655
19457
  },
18656
19458
  });
@@ -18671,6 +19473,7 @@ class LiquidCommerceRmnClient {
18671
19473
  this.eventService.handleSpotState(injectItem.placementId, {
18672
19474
  state: {
18673
19475
  error: `Failed to inject spot element. Could not create element for type "${injectItem.spotType}".`,
19476
+ mounted: false,
18674
19477
  loading: false,
18675
19478
  },
18676
19479
  });
@@ -18685,10 +19488,12 @@ class LiquidCommerceRmnClient {
18685
19488
  this.eventService.handleSpotState(injectItem.placementId, {
18686
19489
  dom: {
18687
19490
  spotElement,
19491
+ visibleOnViewport: false,
18688
19492
  },
18689
19493
  state: {
18690
19494
  mounted: true,
18691
19495
  loading: false,
19496
+ error: undefined,
18692
19497
  },
18693
19498
  });
18694
19499
  return true;
@@ -18709,6 +19514,8 @@ class LiquidCommerceRmnClient {
18709
19514
  this.eventService.handleSpotState(item.placementId, {
18710
19515
  state: {
18711
19516
  error: `Duplicate placement id (${item.placementId}) found. Please provide a unique placement id for each spot element.`,
19517
+ mounted: false,
19518
+ loading: false,
18712
19519
  },
18713
19520
  });
18714
19521
  return false;
@@ -18724,6 +19531,8 @@ class LiquidCommerceRmnClient {
18724
19531
  this.eventService.handleSpotState(item.placementId, {
18725
19532
  state: {
18726
19533
  error: `Invalid spot type (${item.spotType}) found. Please provide a valid spot type for each spot element.`,
19534
+ mounted: false,
19535
+ loading: false,
18727
19536
  },
18728
19537
  });
18729
19538
  continue;
@@ -18732,6 +19541,7 @@ class LiquidCommerceRmnClient {
18732
19541
  }
18733
19542
  return newInject;
18734
19543
  }
19544
+ // Initialize spots with loading state and identifiers
18735
19545
  updateSpotsState(inject) {
18736
19546
  for (const item of inject) {
18737
19547
  this.eventService.handleSpotState(item.placementId, {
@@ -18741,6 +19551,8 @@ class LiquidCommerceRmnClient {
18741
19551
  },
18742
19552
  state: {
18743
19553
  loading: true,
19554
+ mounted: false,
19555
+ error: undefined,
18744
19556
  },
18745
19557
  });
18746
19558
  }
@@ -18832,4 +19644,4 @@ function RmnCreateSpotElement(spot, config) {
18832
19644
  });
18833
19645
  }
18834
19646
 
18835
- export { LiquidCommerceRmnClient, RMN_ENV, RMN_FILTER_PROPERTIES, RMN_SPOT_EVENT, RMN_SPOT_TYPE, RmnClient, RmnCreateSpotElement, RmnEventManager };
19647
+ export { LiquidCommerceRmnClient, RMN_ENV, RMN_EVENT, RMN_FILTER_PROPERTIES, RMN_SPOT_EVENT, RMN_SPOT_TYPE, RmnClient, RmnCreateSpotElement, RmnEventManager };