@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.cjs CHANGED
@@ -3,6 +3,7 @@
3
3
  exports.RMN_SPOT_TYPE = void 0;
4
4
  (function (RMN_SPOT_TYPE) {
5
5
  // Reserve Bar Spot Types
6
+ RMN_SPOT_TYPE["RB_HOMEPAGE_HERO"] = "rbHomepageHero";
6
7
  RMN_SPOT_TYPE["RB_HOMEPAGE_HERO_THREE_TILE"] = "rbHomepageHeroThreeTile";
7
8
  RMN_SPOT_TYPE["RB_HOMEPAGE_HERO_TWO_TILE"] = "rbHomepageHeroTwoTile";
8
9
  RMN_SPOT_TYPE["RB_HOMEPAGE_HERO_FULL_IMAGE"] = "rbHomepageHeroFullImage";
@@ -54,14 +55,21 @@ exports.RMN_FILTER_PROPERTIES = void 0;
54
55
  RMN_FILTER_PROPERTIES["PUBLISHERS"] = "publishers";
55
56
  RMN_FILTER_PROPERTIES["SECTION"] = "section";
56
57
  })(exports.RMN_FILTER_PROPERTIES || (exports.RMN_FILTER_PROPERTIES = {}));
58
+ exports.RMN_EVENT = void 0;
59
+ (function (RMN_EVENT) {
60
+ RMN_EVENT["LIFECYCLE_STATE"] = "LIFECYCLE_STATE";
61
+ RMN_EVENT["SPOT_EVENT"] = "SPOT_EVENT";
62
+ })(exports.RMN_EVENT || (exports.RMN_EVENT = {}));
57
63
  exports.RMN_SPOT_EVENT = void 0;
58
64
  (function (RMN_SPOT_EVENT) {
59
- RMN_SPOT_EVENT["LIFECYCLE_STATE"] = "LIFECYCLE_STATE";
60
65
  RMN_SPOT_EVENT["IMPRESSION"] = "IMPRESSION";
61
66
  RMN_SPOT_EVENT["CLICK"] = "CLICK";
62
67
  RMN_SPOT_EVENT["PURCHASE"] = "PURCHASE";
63
68
  RMN_SPOT_EVENT["ADD_TO_CART"] = "ADD_TO_CART";
69
+ RMN_SPOT_EVENT["REMOVE_FROM_CART"] = "REMOVE_FROM_CART";
70
+ RMN_SPOT_EVENT["ADD_TO_CART_FROM_DETAILS"] = "ADD_TO_CART_FROM_DETAILS";
64
71
  RMN_SPOT_EVENT["ADD_TO_WISHLIST"] = "ADD_TO_WISHLIST";
72
+ RMN_SPOT_EVENT["EXPAND_PRODUCT"] = "EXPAND_PRODUCT";
65
73
  RMN_SPOT_EVENT["BUY_NOW"] = "BUY_NOW";
66
74
  })(exports.RMN_SPOT_EVENT || (exports.RMN_SPOT_EVENT = {}));
67
75
  exports.RMN_ENV = void 0;
@@ -72,6 +80,511 @@ exports.RMN_ENV = void 0;
72
80
  RMN_ENV["PRODUCTION"] = "production";
73
81
  })(exports.RMN_ENV || (exports.RMN_ENV = {}));
74
82
 
83
+ const SPOT_EVENTS_EXAMPLE = [
84
+ {
85
+ event: exports.RMN_SPOT_EVENT.CLICK,
86
+ url: 'https://dev.rmn.liquidcommerce.cloud/api/spots/event?e=eyJ2IjoiMS4xMiIsImF2IjozMDY1NzgzLCJhdCI6MTYzLCJidCI6MCwiY20iOjQ0MDE5MjQxOCwiY2giOjYzMTg0LCJjayI6e30sImNyIjo0ODE4NTUzNzUsImRpIjoiOWMxNGFhMGI3NWY4NDMxNTllMTAwYWQzNzA1NzQyYzMiLCJkaiI6MCwiaWkiOiIxZjU0MGM5NmQ1N2M0YmZjODFlZjRkNjhkMzFjNDVkOSIsImRtIjozLCJmYyI6NjU2NjgyNTQ5LCJmbCI6NjQzOTMxODIxLCJpcCI6IjM1LjIyMy4xOTguOTUiLCJudyI6MTE1MDAsInBjIjo1MDAwLCJvcCI6NTAwMCwibXAiOjUwMDAsImVjIjowLCJnbSI6MCwiZXAiOm51bGwsInByIjoyNDkzMTYsInJ0IjoxLCJycyI6NTAwLCJzYSI6IjU1Iiwic2IiOiJpLTA0MDI0ODg4ZDlkMWRjZWQ3Iiwic3AiOjI3MjI3Miwic3QiOjEyODcyOTYsInRyIjp0cnVlLCJ1ayI6IjNhZWRhMWMxLTZhYjItNDUwNy04Mzg5LTEwZTJmNDMxYjM5MSIsInRzIjoxNzI5MzU5MDU0OTI3LCJiZiI6dHJ1ZSwicG4iOiJyYlByb2R1Y3RVcGNzIiwiZ2MiOnRydWUsImdDIjp0cnVlLCJncyI6Im5vbmUiLCJkYyI6MSwidHoiOiJBbWVyaWNhL05ld19Zb3JrIiwidXIiOm51bGx9&s=hWz37kbxi_u95EVNn2aoQhc5Aas',
87
+ },
88
+ {
89
+ event: exports.RMN_SPOT_EVENT.IMPRESSION,
90
+ url: 'https://dev.rmn.liquidcommerce.cloud/api/spots/event?e=eyJ2IjoiMS4xMiIsImF2IjozMDY1NzgzLCJhdCI6MTYzLCJidCI6MCwiY20iOjQ0MDE5MjQxOCwiY2giOjYzMTg0LCJjayI6e30sImNyIjo0ODE4NTUzNzUsImRpIjoiOWMxNGFhMGI3NWY4NDMxNTllMTAwYWQzNzA1NzQyYzMiLCJkaiI6MCwiaWkiOiIxZjU0MGM5NmQ1N2M0YmZjODFlZjRkNjhkMzFjNDVkOSIsImRtIjozLCJmYyI6NjU2NjgyNTQ5LCJmbCI6NjQzOTMxODIxLCJpcCI6IjM1LjIyMy4xOTguOTUiLCJudyI6MTE1MDAsInBjIjo1MDAwLCJvcCI6NTAwMCwibXAiOjUwMDAsImVjIjowLCJnbSI6MCwiZXAiOm51bGwsInByIjoyNDkzMTYsInJ0IjoxLCJycyI6NTAwLCJzYSI6IjU1Iiwic2IiOiJpLTA0MDI0ODg4ZDlkMWRjZWQ3Iiwic3AiOjI3MjI3Miwic3QiOjEyODcyOTYsInRyIjp0cnVlLCJ1ayI6IjNhZWRhMWMxLTZhYjItNDUwNy04Mzg5LTEwZTJmNDMxYjM5MSIsInRzIjoxNzI5MzU5MDU0OTI3LCJiZiI6dHJ1ZSwicG4iOiJyYlByb2R1Y3RVcGNzIiwiZ2MiOnRydWUsImdDIjp0cnVlLCJncyI6Im5vbmUiLCJkYyI6MSwidHoiOiJBbWVyaWNhL05ld19Zb3JrIiwiYmEiOjEsImZxIjowfQ&s=djoysjCimurf-5T11AlNAwwLSS8',
91
+ },
92
+ {
93
+ event: exports.RMN_SPOT_EVENT.PURCHASE,
94
+ url: 'https://dev.rmn.liquidcommerce.cloud/api/spots/event?e=eyJ2IjoiMS4xMiIsImF2IjozMDY1NzgzLCJhdCI6MTYzLCJidCI6MCwiY20iOjQ0MDE5MjQxOCwiY2giOjYzMTg0LCJjayI6e30sImNyIjo0ODE4NTUzNzUsImRpIjoiOWMxNGFhMGI3NWY4NDMxNTllMTAwYWQzNzA1NzQyYzMiLCJkaiI6MCwiaWkiOiIxZjU0MGM5NmQ1N2M0YmZjODFlZjRkNjhkMzFjNDVkOSIsImRtIjozLCJmYyI6NjU2NjgyNTQ5LCJmbCI6NjQzOTMxODIxLCJpcCI6IjM1LjIyMy4xOTguOTUiLCJudyI6MTE1MDAsInBjIjo1MDAwLCJvcCI6NTAwMCwibXAiOjUwMDAsImVjIjowLCJnbSI6MCwiZXAiOm51bGwsInByIjoyNDkzMTYsInJ0IjoxLCJycyI6NTAwLCJzYSI6IjU1Iiwic2IiOiJpLTA0MDI0ODg4ZDlkMWRjZWQ3Iiwic3AiOjI3MjI3Miwic3QiOjEyODcyOTYsInRyIjp0cnVlLCJ1ayI6IjNhZWRhMWMxLTZhYjItNDUwNy04Mzg5LTEwZTJmNDMxYjM5MSIsInRzIjoxNzI5MzU5MDU0OTI3LCJiZiI6dHJ1ZSwicG4iOiJyYlByb2R1Y3RVcGNzIiwiZ2MiOnRydWUsImdDIjp0cnVlLCJncyI6Im5vbmUiLCJkYyI6MSwidHoiOiJBbWVyaWNhL05ld19Zb3JrIiwiZXQiOjU5fQ&s=AAPAw-3SfZ0JMzjEGFSwt9L-2S4',
95
+ },
96
+ {
97
+ event: exports.RMN_SPOT_EVENT.ADD_TO_CART,
98
+ url: 'https://dev.rmn.liquidcommerce.cloud/api/spots/event?e=eyJ2IjoiMS4xMiIsImF2IjozMDY1NzgzLCJhdCI6MTYzLCJidCI6MCwiY20iOjQ0MDE5MjQxOCwiY2giOjYzMTg0LCJjayI6e30sImNyIjo0ODE4NTUzNzUsImRpIjoiOWMxNGFhMGI3NWY4NDMxNTllMTAwYWQzNzA1NzQyYzMiLCJkaiI6MCwiaWkiOiIxZjU0MGM5NmQ1N2M0YmZjODFlZjRkNjhkMzFjNDVkOSIsImRtIjozLCJmYyI6NjU2NjgyNTQ5LCJmbCI6NjQzOTMxODIxLCJpcCI6IjM1LjIyMy4xOTguOTUiLCJudyI6MTE1MDAsInBjIjo1MDAwLCJvcCI6NTAwMCwibXAiOjUwMDAsImVjIjowLCJnbSI6MCwiZXAiOm51bGwsInByIjoyNDkzMTYsInJ0IjoxLCJycyI6NTAwLCJzYSI6IjU1Iiwic2IiOiJpLTA0MDI0ODg4ZDlkMWRjZWQ3Iiwic3AiOjI3MjI3Miwic3QiOjEyODcyOTYsInRyIjp0cnVlLCJ1ayI6IjNhZWRhMWMxLTZhYjItNDUwNy04Mzg5LTEwZTJmNDMxYjM5MSIsInRzIjoxNzI5MzU5MDU0OTI3LCJiZiI6dHJ1ZSwicG4iOiJyYlByb2R1Y3RVcGNzIiwiZ2MiOnRydWUsImdDIjp0cnVlLCJncyI6Im5vbmUiLCJkYyI6MSwidHoiOiJBbWVyaWNhL05ld19Zb3JrIiwiZXQiOjYwfQ&s=uzQFcjgL7m9XqUG8FvTPVN5YkZY',
99
+ },
100
+ {
101
+ event: exports.RMN_SPOT_EVENT.ADD_TO_WISHLIST,
102
+ url: 'https://dev.rmn.liquidcommerce.cloud/api/spots/event?e=eyJ2IjoiMS4xMiIsImF2IjozMDY1NzgzLCJhdCI6MTYzLCJidCI6MCwiY20iOjQ0MDE5MjQxOCwiY2giOjYzMTg0LCJjayI6e30sImNyIjo0ODE4NTUzNzUsImRpIjoiOWMxNGFhMGI3NWY4NDMxNTllMTAwYWQzNzA1NzQyYzMiLCJkaiI6MCwiaWkiOiIxZjU0MGM5NmQ1N2M0YmZjODFlZjRkNjhkMzFjNDVkOSIsImRtIjozLCJmYyI6NjU2NjgyNTQ5LCJmbCI6NjQzOTMxODIxLCJpcCI6IjM1LjIyMy4xOTguOTUiLCJudyI6MTE1MDAsInBjIjo1MDAwLCJvcCI6NTAwMCwibXAiOjUwMDAsImVjIjowLCJnbSI6MCwiZXAiOm51bGwsInByIjoyNDkzMTYsInJ0IjoxLCJycyI6NTAwLCJzYSI6IjU1Iiwic2IiOiJpLTA0MDI0ODg4ZDlkMWRjZWQ3Iiwic3AiOjI3MjI3Miwic3QiOjEyODcyOTYsInRyIjp0cnVlLCJ1ayI6IjNhZWRhMWMxLTZhYjItNDUwNy04Mzg5LTEwZTJmNDMxYjM5MSIsInRzIjoxNzI5MzU5MDU0OTI3LCJiZiI6dHJ1ZSwicG4iOiJyYlByb2R1Y3RVcGNzIiwiZ2MiOnRydWUsImdDIjp0cnVlLCJncyI6Im5vbmUiLCJkYyI6MSwidHoiOiJBbWVyaWNhL05ld19Zb3JrIiwiZXQiOjYzfQ&s=m3ISU_iIy-OFtXrTKpI6cJAEC0k',
103
+ },
104
+ {
105
+ event: exports.RMN_SPOT_EVENT.BUY_NOW,
106
+ url: 'https://dev.rmn.liquidcommerce.cloud/api/spots/event?e=eyJ2IjoiMS4xMiIsImF2IjozMDY1NzgzLCJhdCI6MTYzLCJidCI6MCwiY20iOjQ0MDE5MjQxOCwiY2giOjYzMTg0LCJjayI6e30sImNyIjo0ODE4NTUzNzUsImRpIjoiOWMxNGFhMGI3NWY4NDMxNTllMTAwYWQzNzA1NzQyYzMiLCJkaiI6MCwiaWkiOiIxZjU0MGM5NmQ1N2M0YmZjODFlZjRkNjhkMzFjNDVkOSIsImRtIjozLCJmYyI6NjU2NjgyNTQ5LCJmbCI6NjQzOTMxODIxLCJpcCI6IjM1LjIyMy4xOTguOTUiLCJudyI6MTE1MDAsInBjIjo1MDAwLCJvcCI6NTAwMCwibXAiOjUwMDAsImVjIjowLCJnbSI6MCwiZXAiOm51bGwsInByIjoyNDkzMTYsInJ0IjoxLCJycyI6NTAwLCJzYSI6IjU1Iiwic2IiOiJpLTA0MDI0ODg4ZDlkMWRjZWQ3Iiwic3AiOjI3MjI3Miwic3QiOjEyODcyOTYsInRyIjp0cnVlLCJ1ayI6IjNhZWRhMWMxLTZhYjItNDUwNy04Mzg5LTEwZTJmNDMxYjM5MSIsInRzIjoxNzI5MzU5MDU0OTI3LCJiZiI6dHJ1ZSwicG4iOiJyYlByb2R1Y3RVcGNzIiwiZ2MiOnRydWUsImdDIjp0cnVlLCJncyI6Im5vbmUiLCJkYyI6MSwidHoiOiJBbWVyaWNhL05ld19Zb3JrIiwiZXQiOjY5fQ&s=l6MOscQC-q-FkC2Ksd7w6jjySCQ',
107
+ },
108
+ ];
109
+ const RB_SPOTS_SELECTION_EXAMPLE = {
110
+ rbHomepageHero: [
111
+ {
112
+ id: 'abc123',
113
+ spot: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_FULL_IMAGE,
114
+ variant: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_FULL_IMAGE,
115
+ width: 1140,
116
+ height: 640,
117
+ header: 'Premium Wine Collection',
118
+ description: 'Discover our exclusive selection of vintage wines',
119
+ ctaText: 'Shop Wines',
120
+ textColor: '#ffffff',
121
+ ctaTextColor: '#ffffff',
122
+ primaryImage: 'https://placehold.co/1140x640/png?text=Wine+Collection',
123
+ mobilePrimaryImage: 'https://placehold.co/640x640/png?text=Mobile+Wine',
124
+ events: SPOT_EVENTS_EXAMPLE,
125
+ productIds: [1, 2, 3],
126
+ },
127
+ {
128
+ id: 'jkl012',
129
+ spot: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_TWO_TILE,
130
+ variant: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_TWO_TILE,
131
+ width: 1140,
132
+ height: 640,
133
+ header: 'Whiskey Collection',
134
+ description: 'Premium whiskeys from around the world',
135
+ ctaText: 'Shop Whiskeys',
136
+ textColor: '#ffffff',
137
+ backgroundColor: '#2c1810',
138
+ ctaTextColor: '#2c1810',
139
+ primaryImage: 'https://placehold.co/1140x640/png?text=Whiskey',
140
+ mobilePrimaryImage: 'https://placehold.co/640x640/png?text=Mobile+Whiskey',
141
+ events: SPOT_EVENTS_EXAMPLE,
142
+ productIds: [10, 11],
143
+ },
144
+ {
145
+ id: 'stu901',
146
+ spot: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_THREE_TILE,
147
+ variant: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_THREE_TILE,
148
+ width: 1140,
149
+ height: 640,
150
+ header: 'Summer Cocktails',
151
+ description: 'Essential spirits for summer mixing',
152
+ ctaText: 'Mix It Up',
153
+ textColor: '#ffffff',
154
+ backgroundColor: '#4d3a0a',
155
+ ctaTextColor: '#4d3a0a',
156
+ primaryImage: 'https://placehold.co/1140x640/png?text=Cocktails',
157
+ secondaryImage: 'https://placehold.co/1140x640/png?text=Mixers',
158
+ mobilePrimaryImage: 'https://placehold.co/640x640/png?text=Mobile+Cocktails',
159
+ mobileSecondaryImage: 'https://placehold.co/640x320/png?text=Mobile+Mixers',
160
+ events: SPOT_EVENTS_EXAMPLE,
161
+ productIds: [16, 17, 18],
162
+ },
163
+ {
164
+ id: 'def456',
165
+ spot: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_FULL_IMAGE,
166
+ variant: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_FULL_IMAGE,
167
+ width: 1140,
168
+ height: 640,
169
+ header: 'Craft Beer Festival',
170
+ description: 'Local breweries and exclusive releases',
171
+ ctaText: 'Explore Beers',
172
+ textColor: '#ffffff',
173
+ ctaTextColor: '#ffffff',
174
+ primaryImage: 'https://placehold.co/1140x640/png?text=Beer+Festival',
175
+ mobilePrimaryImage: 'https://placehold.co/640x640/png?text=Mobile+Beer',
176
+ events: SPOT_EVENTS_EXAMPLE,
177
+ productIds: [4, 5, 6],
178
+ },
179
+ {
180
+ id: 'mno345',
181
+ spot: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_TWO_TILE,
182
+ variant: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_TWO_TILE,
183
+ width: 1140,
184
+ height: 640,
185
+ header: 'Champagne Selection',
186
+ description: 'Finest champagnes for every occasion',
187
+ ctaText: 'View Champagnes',
188
+ textColor: '#ffffff',
189
+ backgroundColor: '#1a1a1a',
190
+ ctaTextColor: '#1a1a1a',
191
+ primaryImage: 'https://placehold.co/1140x640/png?text=Champagne',
192
+ mobilePrimaryImage: 'https://placehold.co/640x640/png?text=Mobile+Champagne',
193
+ events: SPOT_EVENTS_EXAMPLE,
194
+ productIds: [12, 13],
195
+ },
196
+ {
197
+ id: 'vwx234',
198
+ spot: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_THREE_TILE,
199
+ variant: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_THREE_TILE,
200
+ width: 1140,
201
+ height: 640,
202
+ header: 'Wine Regions',
203
+ description: 'Explore wines from top regions',
204
+ ctaText: 'Tour Regions',
205
+ textColor: '#ffffff',
206
+ backgroundColor: '#4d0a0a',
207
+ ctaTextColor: '#4d0a0a',
208
+ primaryImage: 'https://placehold.co/1140x640/png?text=Wine+Regions',
209
+ secondaryImage: 'https://placehold.co/1140x640/png?text=Vineyards',
210
+ mobilePrimaryImage: 'https://placehold.co/640x640/png?text=Mobile+Regions',
211
+ mobileSecondaryImage: 'https://placehold.co/640x320/png?text=Mobile+Vineyards',
212
+ events: SPOT_EVENTS_EXAMPLE,
213
+ productIds: [19, 20, 21],
214
+ },
215
+ {
216
+ id: 'ghi789',
217
+ spot: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_FULL_IMAGE,
218
+ variant: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_FULL_IMAGE,
219
+ width: 1140,
220
+ height: 640,
221
+ header: 'Rare Spirits',
222
+ description: 'Limited edition spirits collection',
223
+ ctaText: 'View Collection',
224
+ textColor: '#ffffff',
225
+ ctaTextColor: '#ffffff',
226
+ primaryImage: 'https://placehold.co/1140x640/png?text=Rare+Spirits',
227
+ mobilePrimaryImage: 'https://placehold.co/640x640/png?text=Mobile+Spirits',
228
+ events: SPOT_EVENTS_EXAMPLE,
229
+ productIds: [7, 8, 9],
230
+ },
231
+ {
232
+ id: 'pqr678',
233
+ spot: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_TWO_TILE,
234
+ variant: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_TWO_TILE,
235
+ width: 1140,
236
+ height: 640,
237
+ header: 'Gin Collection',
238
+ description: 'Artisanal gins and botanicals',
239
+ ctaText: 'Explore Gins',
240
+ textColor: '#ffffff',
241
+ backgroundColor: '#0a4d4d',
242
+ ctaTextColor: '#0a4d4d',
243
+ primaryImage: 'https://placehold.co/1140x640/png?text=Gin',
244
+ mobilePrimaryImage: 'https://placehold.co/640x640/png?text=Mobile+Gin',
245
+ events: SPOT_EVENTS_EXAMPLE,
246
+ productIds: [14, 15],
247
+ },
248
+ {
249
+ id: 'yz5678',
250
+ spot: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_THREE_TILE,
251
+ variant: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_THREE_TILE,
252
+ width: 1140,
253
+ height: 640,
254
+ header: 'Tequila Collection',
255
+ description: 'Premium tequilas and mezcals',
256
+ ctaText: 'Shop Tequila',
257
+ textColor: '#ffffff',
258
+ backgroundColor: '#0a4d2b',
259
+ ctaTextColor: '#0a4d2b',
260
+ primaryImage: 'https://placehold.co/1140x640/png?text=Tequila',
261
+ secondaryImage: 'https://placehold.co/1140x640/png?text=Mezcal',
262
+ mobilePrimaryImage: 'https://placehold.co/640x640/png?text=Mobile+Tequila',
263
+ mobileSecondaryImage: 'https://placehold.co/640x320/png?text=Mobile+Mezcal',
264
+ events: SPOT_EVENTS_EXAMPLE,
265
+ productIds: [22, 23, 24],
266
+ },
267
+ ],
268
+ rbHomepageHeroFullImage: [
269
+ {
270
+ id: 'hjk567',
271
+ spot: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_FULL_IMAGE,
272
+ variant: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_FULL_IMAGE,
273
+ width: 1140,
274
+ height: 640,
275
+ header: 'Holiday Gift Guide',
276
+ description: 'Perfect spirits for every occasion',
277
+ ctaText: 'Shop Gifts',
278
+ textColor: '#ffffff',
279
+ ctaTextColor: '#ffffff',
280
+ primaryImage: 'https://placehold.co/1140x640/png?text=Gift+Guide',
281
+ mobilePrimaryImage: 'https://placehold.co/640x640/png?text=Mobile+Gifts',
282
+ events: SPOT_EVENTS_EXAMPLE,
283
+ productIds: [25, 26],
284
+ },
285
+ {
286
+ id: 'qwe890',
287
+ spot: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_FULL_IMAGE,
288
+ variant: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_FULL_IMAGE,
289
+ width: 1140,
290
+ height: 640,
291
+ header: 'Summer Wine Festival',
292
+ description: 'Refreshing wines for summer',
293
+ ctaText: 'Shop Festival',
294
+ textColor: '#ffffff',
295
+ ctaTextColor: '#ffffff',
296
+ primaryImage: 'https://placehold.co/1140x640/png?text=Wine+Festival',
297
+ mobilePrimaryImage: 'https://placehold.co/640x640/png?text=Mobile+Festival',
298
+ events: SPOT_EVENTS_EXAMPLE,
299
+ productIds: [27, 28],
300
+ },
301
+ ],
302
+ rbHomepageHeroTwoTile: [
303
+ {
304
+ id: 'iop987',
305
+ spot: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_TWO_TILE,
306
+ variant: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_TWO_TILE,
307
+ width: 1140,
308
+ height: 640,
309
+ header: 'Bourbon Selection',
310
+ description: "Kentucky's finest bourbons",
311
+ ctaText: 'Shop Bourbon',
312
+ textColor: '#ffffff',
313
+ backgroundColor: '#2c1810',
314
+ ctaTextColor: '#2c1810',
315
+ primaryImage: 'https://placehold.co/1140x640/png?text=Bourbon',
316
+ mobilePrimaryImage: 'https://placehold.co/640x640/png?text=Mobile+Bourbon',
317
+ events: SPOT_EVENTS_EXAMPLE,
318
+ productIds: [29, 30],
319
+ },
320
+ {
321
+ id: 'lkj012',
322
+ spot: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_TWO_TILE,
323
+ variant: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_TWO_TILE,
324
+ width: 1140,
325
+ height: 640,
326
+ header: 'Vodka Collection',
327
+ description: 'Premium vodkas from around the world',
328
+ ctaText: 'Shop Vodka',
329
+ textColor: '#ffffff',
330
+ backgroundColor: '#1a1a1a',
331
+ ctaTextColor: '#1a1a1a',
332
+ primaryImage: 'https://placehold.co/1140x640/png?text=Vodka',
333
+ mobilePrimaryImage: 'https://placehold.co/640x640/png?text=Mobile+Vodka',
334
+ events: SPOT_EVENTS_EXAMPLE,
335
+ productIds: [31, 32],
336
+ },
337
+ ],
338
+ rbHomepageHeroThreeTile: [
339
+ {
340
+ id: 'bnm345',
341
+ spot: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_THREE_TILE,
342
+ variant: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_THREE_TILE,
343
+ width: 1140,
344
+ height: 640,
345
+ header: 'Rum Collection',
346
+ description: 'Caribbean and craft rums',
347
+ ctaText: 'Shop Rum',
348
+ textColor: '#ffffff',
349
+ backgroundColor: '#4d3a0a',
350
+ ctaTextColor: '#4d3a0a',
351
+ primaryImage: 'https://placehold.co/1140x640/png?text=Rum',
352
+ secondaryImage: 'https://placehold.co/1140x640/png?text=Craft+Rum',
353
+ mobilePrimaryImage: 'https://placehold.co/640x640/png?text=Mobile+Rum',
354
+ mobileSecondaryImage: 'https://placehold.co/640x320/png?text=Mobile+Craft',
355
+ events: SPOT_EVENTS_EXAMPLE,
356
+ productIds: [33, 34],
357
+ },
358
+ {
359
+ id: 'fgh678',
360
+ spot: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_THREE_TILE,
361
+ variant: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_THREE_TILE,
362
+ width: 1140,
363
+ height: 640,
364
+ header: 'Scotch Selection',
365
+ description: 'Single malts and blends',
366
+ ctaText: 'Shop Scotch',
367
+ textColor: '#ffffff',
368
+ backgroundColor: '#4d0a0a',
369
+ ctaTextColor: '#4d0a0a',
370
+ primaryImage: 'https://placehold.co/1140x640/png?text=Scotch',
371
+ secondaryImage: 'https://placehold.co/1140x640/png?text=Single+Malts',
372
+ mobilePrimaryImage: 'https://placehold.co/640x640/png?text=Mobile+Scotch',
373
+ mobileSecondaryImage: 'https://placehold.co/640x320/png?text=Mobile+Malts',
374
+ events: SPOT_EVENTS_EXAMPLE,
375
+ productIds: [35, 36],
376
+ },
377
+ ],
378
+ rbLargeCategoryImageTout: [
379
+ {
380
+ id: 'cde567',
381
+ spot: exports.RMN_SPOT_TYPE.RB_LARGE_CATEGORY_IMAGE_TOUT,
382
+ variant: exports.RMN_SPOT_TYPE.RB_LARGE_CATEGORY_IMAGE_TOUT,
383
+ width: 468,
384
+ height: 410,
385
+ header: 'Rare & Limited Edition',
386
+ description: 'Discover our collection of hard-to-find and limited release spirits.',
387
+ textColor: '#ffffff',
388
+ ctaTextColor: '#ffffff',
389
+ primaryImage: 'https://placehold.co/468x410/png?text=Rare+Spirits',
390
+ mobilePrimaryImage: 'https://placehold.co/468x410/png?text=Mobile+Rare+Spirits',
391
+ ctaText: 'Shop Rare Spirits',
392
+ events: SPOT_EVENTS_EXAMPLE,
393
+ productIds: [37, 38, 39, 40, 41],
394
+ },
395
+ {
396
+ id: 'efg789',
397
+ spot: exports.RMN_SPOT_TYPE.RB_LARGE_CATEGORY_IMAGE_TOUT,
398
+ variant: exports.RMN_SPOT_TYPE.RB_LARGE_CATEGORY_IMAGE_TOUT,
399
+ width: 468,
400
+ height: 410,
401
+ header: 'Vintage Champagnes',
402
+ description: 'Explore our prestigious collection of aged champagnes.',
403
+ textColor: '#ffffff',
404
+ ctaTextColor: '#ffffff',
405
+ primaryImage: 'https://placehold.co/468x410/png?text=Vintage+Champagne',
406
+ mobilePrimaryImage: 'https://placehold.co/468x410/png?text=Mobile+Champagne',
407
+ ctaText: 'View Collection',
408
+ events: SPOT_EVENTS_EXAMPLE,
409
+ productIds: [42, 43, 44, 45, 46],
410
+ },
411
+ {
412
+ id: 'ghi012',
413
+ spot: exports.RMN_SPOT_TYPE.RB_LARGE_CATEGORY_IMAGE_TOUT,
414
+ variant: exports.RMN_SPOT_TYPE.RB_LARGE_CATEGORY_IMAGE_TOUT,
415
+ width: 468,
416
+ height: 410,
417
+ header: 'Small Batch Bourbon',
418
+ description: 'Hand-selected premium bourbon from craft distilleries.',
419
+ textColor: '#ffffff',
420
+ ctaTextColor: '#ffffff',
421
+ primaryImage: 'https://placehold.co/468x410/png?text=Craft+Bourbon',
422
+ mobilePrimaryImage: 'https://placehold.co/468x410/png?text=Mobile+Bourbon',
423
+ ctaText: 'Explore Bourbon',
424
+ events: SPOT_EVENTS_EXAMPLE,
425
+ productIds: [47, 48, 49, 50, 51],
426
+ },
427
+ ],
428
+ rbSmallDiscoverTout: [
429
+ {
430
+ id: 'jkl345',
431
+ spot: exports.RMN_SPOT_TYPE.RB_SMALL_DISCOVER_TOUT,
432
+ variant: exports.RMN_SPOT_TYPE.RB_SMALL_DISCOVER_TOUT,
433
+ width: 224,
434
+ height: 378,
435
+ header: 'Château Margaux 2015 Bordeaux',
436
+ textColor: '#ffffff',
437
+ primaryImage: 'https://placehold.co/224x378/png?text=Château+Margaux',
438
+ mobilePrimaryImage: 'https://placehold.co/224x378/png?text=Mobile+Château+Margaux',
439
+ events: SPOT_EVENTS_EXAMPLE,
440
+ productIds: [52, 53, 54, 55, 56],
441
+ },
442
+ {
443
+ id: 'lmn678',
444
+ spot: exports.RMN_SPOT_TYPE.RB_SMALL_DISCOVER_TOUT,
445
+ variant: exports.RMN_SPOT_TYPE.RB_SMALL_DISCOVER_TOUT,
446
+ width: 224,
447
+ height: 378,
448
+ header: 'Macallan 25 Year',
449
+ textColor: '#ffffff',
450
+ primaryImage: 'https://placehold.co/224x378/png?text=Macallan+25',
451
+ mobilePrimaryImage: 'https://placehold.co/224x378/png?text=Mobile+Macallan',
452
+ events: SPOT_EVENTS_EXAMPLE,
453
+ productIds: [57, 58, 59, 60, 61],
454
+ },
455
+ {
456
+ id: 'nop901',
457
+ spot: exports.RMN_SPOT_TYPE.RB_SMALL_DISCOVER_TOUT,
458
+ variant: exports.RMN_SPOT_TYPE.RB_SMALL_DISCOVER_TOUT,
459
+ width: 224,
460
+ height: 378,
461
+ header: 'Louis XIII Cognac',
462
+ textColor: '#ffffff',
463
+ primaryImage: 'https://placehold.co/224x378/png?text=Louis+XIII',
464
+ mobilePrimaryImage: 'https://placehold.co/224x378/png?text=Mobile+Louis+XIII',
465
+ events: SPOT_EVENTS_EXAMPLE,
466
+ productIds: [62, 63, 64, 65, 66],
467
+ },
468
+ ],
469
+ rbSmallCategoryImageTout: [
470
+ {
471
+ id: 'qrs234',
472
+ spot: exports.RMN_SPOT_TYPE.RB_SMALL_CATEGORY_IMAGE_TOUT,
473
+ variant: exports.RMN_SPOT_TYPE.RB_SMALL_CATEGORY_IMAGE_TOUT,
474
+ width: 224,
475
+ height: 410,
476
+ header: 'Japanese Sake',
477
+ textColor: '#ffffff',
478
+ primaryImage: 'https://placehold.co/224x410/png?text=Japanese+Sake',
479
+ mobilePrimaryImage: 'https://placehold.co/224x410/png?text=Mobile+Japanese+Sake',
480
+ events: SPOT_EVENTS_EXAMPLE,
481
+ productIds: [67, 68, 69, 70, 71],
482
+ },
483
+ {
484
+ id: 'stu567',
485
+ spot: exports.RMN_SPOT_TYPE.RB_SMALL_CATEGORY_IMAGE_TOUT,
486
+ variant: exports.RMN_SPOT_TYPE.RB_SMALL_CATEGORY_IMAGE_TOUT,
487
+ width: 224,
488
+ height: 410,
489
+ header: 'Craft Vermouth',
490
+ textColor: '#ffffff',
491
+ primaryImage: 'https://placehold.co/224x410/png?text=Craft+Vermouth',
492
+ mobilePrimaryImage: 'https://placehold.co/224x410/png?text=Mobile+Vermouth',
493
+ events: SPOT_EVENTS_EXAMPLE,
494
+ productIds: [72, 73, 74, 75, 76],
495
+ },
496
+ {
497
+ id: 'vwx890',
498
+ spot: exports.RMN_SPOT_TYPE.RB_SMALL_CATEGORY_IMAGE_TOUT,
499
+ variant: exports.RMN_SPOT_TYPE.RB_SMALL_CATEGORY_IMAGE_TOUT,
500
+ width: 224,
501
+ height: 410,
502
+ header: 'Premium Mezcal',
503
+ textColor: '#ffffff',
504
+ primaryImage: 'https://placehold.co/224x410/png?text=Premium+Mezcal',
505
+ mobilePrimaryImage: 'https://placehold.co/224x410/png?text=Mobile+Mezcal',
506
+ events: SPOT_EVENTS_EXAMPLE,
507
+ productIds: [77, 78, 79, 80, 81],
508
+ },
509
+ ],
510
+ rbCollectionBannerWithoutTextBlock: [
511
+ {
512
+ id: 'yz1234',
513
+ spot: exports.RMN_SPOT_TYPE.RB_COLLECTION_BANNER_WITHOUT_TEXT_BLOCK,
514
+ variant: exports.RMN_SPOT_TYPE.RB_COLLECTION_BANNER_WITHOUT_TEXT_BLOCK,
515
+ width: 887,
516
+ height: 344,
517
+ primaryImage: 'https://placehold.co/887x344/png?text=Summer+Cocktails',
518
+ mobilePrimaryImage: 'https://placehold.co/887x344/png?text=Mobile+Summer+Cocktails',
519
+ events: SPOT_EVENTS_EXAMPLE,
520
+ productIds: [82, 83, 84, 85, 86],
521
+ },
522
+ {
523
+ id: 'abc567',
524
+ spot: exports.RMN_SPOT_TYPE.RB_COLLECTION_BANNER_WITHOUT_TEXT_BLOCK,
525
+ variant: exports.RMN_SPOT_TYPE.RB_COLLECTION_BANNER_WITHOUT_TEXT_BLOCK,
526
+ width: 887,
527
+ height: 344,
528
+ primaryImage: 'https://placehold.co/887x344/png?text=Holiday+Spirits',
529
+ mobilePrimaryImage: 'https://placehold.co/887x344/png?text=Mobile+Holiday+Spirits',
530
+ events: SPOT_EVENTS_EXAMPLE,
531
+ productIds: [87, 88, 89, 90, 91],
532
+ },
533
+ {
534
+ id: 'def901',
535
+ spot: exports.RMN_SPOT_TYPE.RB_COLLECTION_BANNER_WITHOUT_TEXT_BLOCK,
536
+ variant: exports.RMN_SPOT_TYPE.RB_COLLECTION_BANNER_WITHOUT_TEXT_BLOCK,
537
+ width: 887,
538
+ height: 344,
539
+ primaryImage: 'https://placehold.co/887x344/png?text=Wine+Essentials',
540
+ mobilePrimaryImage: 'https://placehold.co/887x344/png?text=Mobile+Wine+Essentials',
541
+ events: SPOT_EVENTS_EXAMPLE,
542
+ productIds: [92, 93, 94, 95, 96],
543
+ },
544
+ ],
545
+ rbNavigationBanner: [
546
+ {
547
+ id: 'ghi234',
548
+ spot: exports.RMN_SPOT_TYPE.RB_NAVIGATION_BANNER,
549
+ variant: exports.RMN_SPOT_TYPE.RB_NAVIGATION_BANNER,
550
+ width: 440,
551
+ height: 220,
552
+ header: 'Explore Tequilas',
553
+ textColor: '#ffffff',
554
+ primaryImage: 'https://placehold.co/440x220/png?text=Tequila+Collection',
555
+ mobilePrimaryImage: 'https://placehold.co/440x220/png?text=Mobile+Tequila+Collection',
556
+ events: SPOT_EVENTS_EXAMPLE,
557
+ productIds: [97, 98, 99, 100, 101],
558
+ },
559
+ {
560
+ id: 'jkl678',
561
+ spot: exports.RMN_SPOT_TYPE.RB_NAVIGATION_BANNER,
562
+ variant: exports.RMN_SPOT_TYPE.RB_NAVIGATION_BANNER,
563
+ width: 440,
564
+ height: 220,
565
+ header: 'Craft Gin Selection',
566
+ textColor: '#ffffff',
567
+ primaryImage: 'https://placehold.co/440x220/png?text=Gin+Selection',
568
+ mobilePrimaryImage: 'https://placehold.co/440x220/png?text=Mobile+Gin+Selection',
569
+ events: SPOT_EVENTS_EXAMPLE,
570
+ productIds: [102, 103, 104, 105, 106],
571
+ },
572
+ {
573
+ id: 'mno012',
574
+ spot: exports.RMN_SPOT_TYPE.RB_NAVIGATION_BANNER,
575
+ variant: exports.RMN_SPOT_TYPE.RB_NAVIGATION_BANNER,
576
+ width: 440,
577
+ height: 220,
578
+ header: 'Premium Vodka',
579
+ textColor: '#ffffff',
580
+ primaryImage: 'https://placehold.co/440x220/png?text=Vodka+Premium',
581
+ mobilePrimaryImage: 'https://placehold.co/440x220/png?text=Mobile+Vodka+Premium',
582
+ events: SPOT_EVENTS_EXAMPLE,
583
+ productIds: [107, 108, 109, 110, 111],
584
+ },
585
+ ],
586
+ };
587
+
75
588
  const REQUEST_CLOUD_PARTNER_SITE = 'X-Liquid-Partner-Site';
76
589
  const REQUEST_CLOUD_PROTECTED_KEY = 'X-Liquid-Protected';
77
590
  const REQUEST_CLOUD_PROTECTED_TIMESTAMP = 'X-Liquid-Timestamp';
@@ -6034,6 +6547,94 @@ axios.HttpStatusCode = HttpStatusCode;
6034
6547
 
6035
6548
  axios.default = axios;
6036
6549
 
6550
+ /**
6551
+ * Keyword patterns for each RMN_SPOT_EVENT.
6552
+ * Each event type has required keywords that must be present and optional ones.
6553
+ *
6554
+ * Note: The order of checks matters - more specific patterns must be checked before general ones
6555
+ * to avoid incorrect matches.
6556
+ */
6557
+ const EVENT_KEYWORDS = {
6558
+ [exports.RMN_SPOT_EVENT.ADD_TO_CART_FROM_DETAILS]: {
6559
+ required: ['add', 'cart'],
6560
+ optional: ['details', 'detail', 'single', 'profile', 'page', 'pdp'],
6561
+ },
6562
+ [exports.RMN_SPOT_EVENT.BUY_NOW]: {
6563
+ required: ['buy', 'now'],
6564
+ },
6565
+ [exports.RMN_SPOT_EVENT.EXPAND_PRODUCT]: {
6566
+ required: ['product'],
6567
+ optional: ['details', 'expand', 'modal', 'popup', 'quickview', 'view'],
6568
+ },
6569
+ [exports.RMN_SPOT_EVENT.ADD_TO_WISHLIST]: {
6570
+ required: ['add', 'wishlist'],
6571
+ },
6572
+ [exports.RMN_SPOT_EVENT.REMOVE_FROM_CART]: {
6573
+ required: ['remove', 'cart'],
6574
+ },
6575
+ [exports.RMN_SPOT_EVENT.PURCHASE]: {
6576
+ required: ['purchase'],
6577
+ },
6578
+ [exports.RMN_SPOT_EVENT.ADD_TO_CART]: {
6579
+ required: ['add', 'cart'],
6580
+ },
6581
+ };
6582
+ /**
6583
+ * Normalizes an event name by converting to lowercase, removing special characters,
6584
+ * and splitting into words.
6585
+ */
6586
+ function normalizeEventName(event) {
6587
+ return event
6588
+ .toLowerCase()
6589
+ .replace(/[^a-z0-9\s]+/g, ' ') // Replace special chars with spaces
6590
+ .split(/\s+/) // Split on whitespace
6591
+ .filter(Boolean); // Remove empty strings
6592
+ }
6593
+ /**
6594
+ * Checks if a word matches a keyword, considering word boundaries.
6595
+ * This prevents partial word matches (e.g., "card" shouldn't match "cart").
6596
+ */
6597
+ function wordMatchesKeyword(word, keyword) {
6598
+ // Create a RegExp that matches the keyword as a whole word
6599
+ const keywordRegex = new RegExp(`^${keyword}s?$|${keyword}s?\\W|\\W${keyword}s?$|\\W${keyword}s?\\W`);
6600
+ return keywordRegex.test(` ${word} `); // Add spaces to handle word boundaries
6601
+ }
6602
+ /**
6603
+ * Checks if all required keywords and at least one optional keyword (if specified) are present.
6604
+ */
6605
+ function matchesKeywordPattern(words, required, optional) {
6606
+ // Check if all required keywords are present as whole words
6607
+ const hasAllRequired = required.every((keyword) => words.some((word) => wordMatchesKeyword(word, keyword)));
6608
+ if (!hasAllRequired) {
6609
+ return false;
6610
+ }
6611
+ // If no optional keywords are specified, return true
6612
+ if (!(optional === null || optional === void 0 ? void 0 : optional.length)) {
6613
+ return true;
6614
+ }
6615
+ // If optional keywords exist, check if at least one matches as a whole word
6616
+ return optional.some((keyword) => words.some((word) => wordMatchesKeyword(word, keyword)));
6617
+ }
6618
+ /**
6619
+ * Determines the event type from a raw event string by checking for keyword patterns.
6620
+ *
6621
+ * @param {string} [event] - The raw event string to evaluate
6622
+ * @returns {RMN_SPOT_EVENT | null} - The corresponding RMN_SPOT_EVENT or null if no match
6623
+ */
6624
+ function getEventTypeFromRawEvent(event) {
6625
+ if (!(event === null || event === void 0 ? void 0 : event.trim())) {
6626
+ return null;
6627
+ }
6628
+ const words = normalizeEventName(event);
6629
+ // Use Object.entries to maintain the exact order defined in EVENT_KEYWORDS
6630
+ for (const [eventType, { required, optional }] of Object.entries(EVENT_KEYWORDS)) {
6631
+ if (matchesKeywordPattern(words, required, optional)) {
6632
+ return eventType;
6633
+ }
6634
+ }
6635
+ return null;
6636
+ }
6637
+
6037
6638
  class SingletonManager {
6038
6639
  /**
6039
6640
  * Retrieves an instance of the specified class using the provided instance creator function.
@@ -6090,114 +6691,421 @@ class ObjectHelper {
6090
6691
  return Object.values(source);
6091
6692
  }
6092
6693
  /**
6093
- * Checks if a given key is present in a source object.
6094
- *
6095
- * @param {Record<string, any>} source - The object to search within.
6096
- * @param {string} key - The key to check for.
6097
- * @return {boolean} - True if the key is present in the source, false otherwise.
6694
+ * Checks if a given key is present in a source object.
6695
+ *
6696
+ * @param {Record<string, any>} source - The object to search within.
6697
+ * @param {string} key - The key to check for.
6698
+ * @return {boolean} - True if the key is present in the source, false otherwise.
6699
+ */
6700
+ includes(source, key) {
6701
+ return key in source;
6702
+ }
6703
+ /**
6704
+ * Checks if the given value is included in the values of a source object.
6705
+ *
6706
+ * @param {Record<string, any>} source - The source object to check the values from.
6707
+ * @param {string} value - The value to search for in the source object values.
6708
+ * @returns {boolean} - True if the value is found in the source object values, false otherwise.
6709
+ */
6710
+ valuesIncludes(source, value) {
6711
+ const sourceValues = Object.values(source);
6712
+ return sourceValues.includes(value);
6713
+ }
6714
+ /**
6715
+ * Checks if all keys from the given 'keys' array are present in the 'source' object.
6716
+ *
6717
+ * @param {Record<string, any>} source - The object to be checked.
6718
+ * @param {string[]} keys - The array of keys to be checked in the 'source' object.
6719
+ * @return {boolean} - Returns true if all keys are present in the 'source' object,
6720
+ * false otherwise.
6721
+ */
6722
+ hasAllValues(source, keys) {
6723
+ const sourceKeys = Object.keys(source);
6724
+ return keys.every((key) => sourceKeys.includes(key));
6725
+ }
6726
+ /**
6727
+ * Checks if the given object is an array.
6728
+ *
6729
+ * @param {any} obj - The object to check.
6730
+ * @return {boolean} - Returns true if the object is an array, otherwise returns false.
6731
+ */
6732
+ isArray(obj) {
6733
+ return Object.prototype.toString.call(obj) === '[object Array]';
6734
+ }
6735
+ /**
6736
+ * Merges the value with the target value.
6737
+ *
6738
+ * @param {any[] | Record<string, T>} targetValue - The target value to merge with.
6739
+ * @param {Record<string, any>} value - The value to merge.
6740
+ * @returns {Record<string, any>} - The merged value.
6741
+ */
6742
+ mergeValue(targetValue, value) {
6743
+ var _a;
6744
+ if (this.isArray(value)) {
6745
+ return this.mergeArrayValues(targetValue, value);
6746
+ }
6747
+ else if (typeof value === 'object') {
6748
+ return this.mergeObjectValues(targetValue, value);
6749
+ }
6750
+ return (_a = value !== null && value !== void 0 ? value : targetValue) !== null && _a !== void 0 ? _a : undefined;
6751
+ }
6752
+ /**
6753
+ * Merges the values of an array or object with a given array of objects and returns the merged result as an object.
6754
+ * If the targetValue is not an array, it returns a copy of the value array.
6755
+ *
6756
+ * @param targetValue - The target array or object to merge values into.
6757
+ * @param value - The array of objects containing values to merge into the targetValue.
6758
+ * @returns The merged result as an object.
6759
+ */
6760
+ mergeArrayValues(targetValue, value) {
6761
+ if (!this.isArray(targetValue)) {
6762
+ return [...value];
6763
+ }
6764
+ // make TypeScript understand that targetValue is an array
6765
+ const targetArray = targetValue;
6766
+ for (let i = 0, l = value.length; i < l; i++) {
6767
+ targetArray[i] = this.mergeValue(targetArray[i], value[i]);
6768
+ }
6769
+ return targetArray;
6770
+ }
6771
+ /**
6772
+ * Merges the values of two objects or arrays into a single object.
6773
+ * If the target value is an array or a record, the method merges the values into the target value.
6774
+ * If the target value is not an array or a record, it returns the new value.
6775
+ *
6776
+ * @template T - The type of the values in the target object.
6777
+ * @param {object[]|Record<string, T>} targetValue - The target object or array to merge the values into.
6778
+ * @param {Record<string, any>} value - The new values to merge into the target object or array.
6779
+ * @returns {Record<string, any>} - The merged object or array.
6780
+ */
6781
+ mergeObjectValues(targetValue, value) {
6782
+ if (targetValue && typeof targetValue === 'object') {
6783
+ this.innerMerge(targetValue, value);
6784
+ return targetValue;
6785
+ }
6786
+ return value ? { ...value } : value;
6787
+ }
6788
+ /**
6789
+ * Merges the properties of the source object into the target object.
6790
+ *
6791
+ * @param {Record<string, any>} target - The target object to merge the properties into.
6792
+ * @param {Record<string, any>} source - The source object containing the properties to merge.
6793
+ *
6794
+ * @return {void} - This method does not return any value.
6795
+ */
6796
+ innerMerge(target, source) {
6797
+ for (const key of Object.keys(source)) {
6798
+ target[key] = this.mergeValue(target[key], source[key]);
6799
+ }
6800
+ }
6801
+ }
6802
+
6803
+ /**
6804
+ * Recursively extracts ID values from a nested data structure.
6805
+ * Searches for specified property names and collects their primitive values (strings/numbers).
6806
+ *
6807
+ * @param data - The data structure to search through (can be nested objects/arrays)
6808
+ * @param propertyNames - Array of property names to look for
6809
+ * @returns Array of extracted ID values (strings/numbers only)
6810
+ *
6811
+ * @example
6812
+ * const data = {
6813
+ * id: [1, 2, 3],
6814
+ * nested: { id: 'abc' },
6815
+ * items: [{ id: 456 }]
6816
+ * };
6817
+ * extractDeepIds(data); // Returns [1, 2, 3, 'abc', 456]
6818
+ */
6819
+ function extractDeepIds(data, propertyNames) {
6820
+ const ids = [];
6821
+ const defaulPropertyNames = [
6822
+ 'id',
6823
+ 'upc',
6824
+ 'groupingId',
6825
+ 'sku',
6826
+ 'productId',
6827
+ 'item_id',
6828
+ 'isbn',
6829
+ 'asin',
6830
+ 'mpn',
6831
+ 'model_number',
6832
+ 'article_number',
6833
+ 'variant_id',
6834
+ 'item_number',
6835
+ 'catalog_id',
6836
+ 'reference_id',
6837
+ ];
6838
+ // Set for faster property name lookups
6839
+ const propertySet = new Set(defaulPropertyNames);
6840
+ /**
6841
+ * Processes a value and extracts IDs if it matches criteria
6842
+ * @param value - The value to process
6843
+ * @param currentKey - The property name of the current value
6844
+ */
6845
+ const processValue = (value, currentKey) => {
6846
+ // Early exit for null/undefined values
6847
+ if (value == null)
6848
+ return;
6849
+ // If current key matches our target properties
6850
+ if (currentKey && propertySet.has(currentKey)) {
6851
+ if (Array.isArray(value)) {
6852
+ // Filter and push valid array values in one pass
6853
+ ids.push(...value.filter((item) => typeof item === 'string' || typeof item === 'number'));
6854
+ }
6855
+ else if (typeof value === 'string' || typeof value === 'number') {
6856
+ ids.push(value);
6857
+ }
6858
+ return; // Stop processing this branch after handling the ID
6859
+ }
6860
+ // Recursively process nested structures
6861
+ if (Array.isArray(value)) {
6862
+ value.forEach((item) => processValue(item));
6863
+ }
6864
+ else if (typeof value === 'object') {
6865
+ // Process all enumerable properties
6866
+ for (const [key, val] of Object.entries(value)) {
6867
+ processValue(val, key);
6868
+ }
6869
+ }
6870
+ };
6871
+ processValue(data);
6872
+ return ids; // No need to filter nulls as we handle that during collection
6873
+ }
6874
+ // Fallback method using fetch if sendBeacon isn't available
6875
+ async function fallbackEventFire(url) {
6876
+ try {
6877
+ const racePromise = Promise.race([
6878
+ // Promise #1: The fetch request
6879
+ fetch(url, {
6880
+ method: 'POST',
6881
+ keepalive: true,
6882
+ }),
6883
+ // Promise #2: The timeout
6884
+ new Promise((_, reject) => setTimeout(() => reject(new Error('Request timeout')), 2000)),
6885
+ ]);
6886
+ /**
6887
+ * Prevent requests from hanging indefinitely
6888
+ * Improve user experience by failing fast
6889
+ * Handle slow network conditions gracefully
6890
+ * Ensure resources are freed up in a timely manner
6891
+ */
6892
+ const response = await racePromise;
6893
+ return response.ok;
6894
+ }
6895
+ catch (_a) {
6896
+ return false;
6897
+ }
6898
+ }
6899
+ /**
6900
+ * Extracts and decodes a URL from a base64-encoded query parameter.
6901
+ *
6902
+ * @param {string} url - The URL containing the base64-encoded query parameter.
6903
+ * @returns {string | null} - The decoded URL or null if not found or invalid.
6904
+ */
6905
+ function getRedirectUrlFromPayload(url) {
6906
+ try {
6907
+ const base64String = new URL(url).searchParams.get('e');
6908
+ if (!base64String) {
6909
+ return null;
6910
+ }
6911
+ const data = JSON.parse(atob(base64String));
6912
+ return data.ur || null;
6913
+ }
6914
+ catch (_a) {
6915
+ return null;
6916
+ }
6917
+ }
6918
+ /**
6919
+ * Fires an event using the navigator.sendBeacon method or a fallback method if sendBeacon is not available.
6920
+ * If the event is a click event and a redirect URL is found, it redirects the user to that URL.
6921
+ *
6922
+ * @param {IFireEventParams} params - The parameters for firing the event.
6923
+ * @param {RMN_SPOT_EVENT} params.event - The event type.
6924
+ * @param {string} params.eventUrl - The URL to which the event is sent.
6925
+ * @returns {Promise<void>} - A promise that resolves when the event is fired.
6926
+ */
6927
+ async function fireEvent({ event, eventUrl }) {
6928
+ var _a;
6929
+ try {
6930
+ 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));
6931
+ if (!didFireEvent) {
6932
+ return;
6933
+ }
6934
+ if (event === exports.RMN_SPOT_EVENT.CLICK) {
6935
+ const redirectUrl = getRedirectUrlFromPayload(eventUrl);
6936
+ if (redirectUrl) {
6937
+ window.location.href = redirectUrl;
6938
+ }
6939
+ }
6940
+ }
6941
+ catch (_b) {
6942
+ // Handle errors silently
6943
+ }
6944
+ }
6945
+ function calculateScaleFactor(elementScale) {
6946
+ // Step 1: Apply square root for non-linear scaling
6947
+ // This creates a more gradual scaling effect, especially for larger changes
6948
+ // For example:
6949
+ // - elementScale of 0.25 (1/4 size) becomes 0.5
6950
+ // - elementScale of 1 (unchanged) remains 1
6951
+ // - elementScale of 4 (4x size) becomes 2
6952
+ const baseFactor = Math.sqrt(elementScale);
6953
+ // Step 2: Apply additional dampening to further soften the scaling effect
6954
+ // The dampening factor (0.5) can be adjusted:
6955
+ // - Lower values (closer to 0) make scaling more subtle
6956
+ // - Higher values (closer to 1) make scaling more pronounced
6957
+ const dampening = 0.35;
6958
+ // Calculate the scaleFactor:
6959
+ // 1. (baseFactor - 1) represents the change from the original size
6960
+ // 2. Multiply by dampening to reduce the effect
6961
+ // 3. Add 1 to center the scaling around the original size
6962
+ // For example, if baseFactor is 2:
6963
+ // scaleFactor = 1 + (2 - 1) * 0.5 = 1.5
6964
+ const scaleFactor = 1 + (baseFactor - 1) * dampening;
6965
+ // Step 3: Define the allowed range for the scale factor
6966
+ // This ensures that the font size never changes too drastically
6967
+ const minScale = 0.35; // Font will never be smaller than 50% of original
6968
+ const maxScale = 1.5; // Font will never be larger than 150% of original
6969
+ // Step 4: Clamp the scale factor to the defined range
6970
+ // Math.min ensures the value doesn't exceed maxScale
6971
+ // Math.max ensures the value isn't less than minScale
6972
+ return Math.max(minScale, Math.min(maxScale, scaleFactor));
6973
+ }
6974
+
6975
+ class UniqueIdGenerator {
6976
+ /**
6977
+ * Initialize the generator with a node ID
6978
+ * @param nodeId Number between 0-1023 to identify this instance
6098
6979
  */
6099
- includes(source, key) {
6100
- return key in source;
6980
+ static initialize(nodeId = Math.floor(Math.random() * 1024)) {
6981
+ if (nodeId < 0 || nodeId >= 1 << this.nodeBits) {
6982
+ throw new Error(`Node ID must be between 0 and ${(1 << this.nodeBits) - 1}`);
6983
+ }
6984
+ this.nodeId = nodeId;
6101
6985
  }
6102
6986
  /**
6103
- * Checks if the given value is included in the values of a source object.
6104
- *
6105
- * @param {Record<string, any>} source - The source object to check the values from.
6106
- * @param {string} value - The value to search for in the source object values.
6107
- * @returns {boolean} - True if the value is found in the source object values, false otherwise.
6987
+ * Convert a number to base32 string with specified length
6108
6988
  */
6109
- valuesIncludes(source, value) {
6110
- const sourceValues = Object.values(source);
6111
- return sourceValues.includes(value);
6989
+ static toBase32(num, length) {
6990
+ let result = '';
6991
+ while (num > 0) {
6992
+ result = this.base32Chars[Number(num % BigInt(32))] + result;
6993
+ // @ts-expect-error - TS doesn't support bigint division
6994
+ num = num / 32n;
6995
+ }
6996
+ return result.padStart(length, '0');
6112
6997
  }
6113
6998
  /**
6114
- * Checks if all keys from the given 'keys' array are present in the 'source' object.
6115
- *
6116
- * @param {Record<string, any>} source - The object to be checked.
6117
- * @param {string[]} keys - The array of keys to be checked in the 'source' object.
6118
- * @return {boolean} - Returns true if all keys are present in the 'source' object,
6119
- * false otherwise.
6999
+ * Generate a cryptographically secure random number
6120
7000
  */
6121
- hasAllValues(source, keys) {
6122
- const sourceKeys = Object.keys(source);
6123
- return keys.every((key) => sourceKeys.includes(key));
7001
+ static getSecureRandom() {
7002
+ if (typeof crypto !== 'undefined') {
7003
+ const buffer = new Uint32Array(1);
7004
+ crypto.getRandomValues(buffer);
7005
+ return buffer[0];
7006
+ }
7007
+ return Math.floor(Math.random() * 0xffffffff);
6124
7008
  }
6125
7009
  /**
6126
- * Checks if the given object is an array.
6127
- *
6128
- * @param {any} obj - The object to check.
6129
- * @return {boolean} - Returns true if the object is an array, otherwise returns false.
7010
+ * Wait until next millisecond
6130
7011
  */
6131
- isArray(obj) {
6132
- return Object.prototype.toString.call(obj) === '[object Array]';
7012
+ static waitNextMillis(lastTimestamp) {
7013
+ let timestamp = Date.now();
7014
+ while (timestamp <= lastTimestamp) {
7015
+ timestamp = Date.now();
7016
+ }
7017
+ return timestamp;
6133
7018
  }
6134
7019
  /**
6135
- * Merges the value with the target value.
7020
+ * Generates a highly unique ID with the following format:
7021
+ * TTTTTTTTTTCCCCNNNNNRRRR
7022
+ * T: Timestamp (10 chars)
7023
+ * C: Counter (4 chars)
7024
+ * N: Node ID (5 chars)
7025
+ * R: Random (4 chars)
6136
7026
  *
6137
- * @param {any[] | Record<string, T>} targetValue - The target value to merge with.
6138
- * @param {Record<string, any>} value - The value to merge.
6139
- * @returns {Record<string, any>} - The merged value.
7027
+ * Total length: 23 characters, always uppercase alphanumeric
6140
7028
  */
6141
- mergeValue(targetValue, value) {
6142
- var _a;
6143
- if (this.isArray(value)) {
6144
- return this.mergeArrayValues(targetValue, value);
7029
+ static generate() {
7030
+ if (this.nodeId === undefined) {
7031
+ this.initialize();
6145
7032
  }
6146
- else if (typeof value === 'object') {
6147
- return this.mergeObjectValues(targetValue, value);
7033
+ let timestamp = Date.now() - this.epoch;
7034
+ // Handle clock moving backwards or same millisecond
7035
+ if (timestamp < this.lastTimestamp) {
7036
+ throw new Error('Clock moved backwards. Refusing to generate ID.');
6148
7037
  }
6149
- return (_a = value !== null && value !== void 0 ? value : targetValue) !== null && _a !== void 0 ? _a : undefined;
7038
+ if (timestamp === this.lastTimestamp) {
7039
+ this.sequence = (this.sequence + 1) & ((1 << this.sequenceBits) - 1);
7040
+ if (this.sequence === 0) {
7041
+ timestamp = this.waitNextMillis(this.lastTimestamp);
7042
+ }
7043
+ }
7044
+ else {
7045
+ this.sequence = 0;
7046
+ }
7047
+ this.lastTimestamp = timestamp;
7048
+ // Generate random component
7049
+ const random = this.getSecureRandom() & 0xffff; // 16 bits of randomness
7050
+ // Combine all components into a BigInt
7051
+ // const id =
7052
+ // (BigInt(timestamp) << BigInt(this.nodeBits + this.sequenceBits + 16)) |
7053
+ // (BigInt(this.nodeId) << BigInt(this.sequenceBits + 16)) |
7054
+ // (BigInt(this.sequence) << BigInt(16)) |
7055
+ // BigInt(random);
7056
+ // Convert to base32 representation
7057
+ const timeComponent = this.toBase32(BigInt(timestamp), 10);
7058
+ const counterComponent = this.toBase32(BigInt(this.sequence), 4);
7059
+ const nodeComponent = this.toBase32(BigInt(this.nodeId), 5);
7060
+ const randomComponent = this.toBase32(BigInt(random), 4);
7061
+ return `${timeComponent}${counterComponent}${nodeComponent}${randomComponent}`;
6150
7062
  }
6151
7063
  /**
6152
- * Merges the values of an array or object with a given array of objects and returns the merged result as an object.
6153
- * If the targetValue is not an array, it returns a copy of the value array.
6154
- *
6155
- * @param targetValue - The target array or object to merge values into.
6156
- * @param value - The array of objects containing values to merge into the targetValue.
6157
- * @returns The merged result as an object.
7064
+ * Validates if a string matches the expected ID format
6158
7065
  */
6159
- mergeArrayValues(targetValue, value) {
6160
- if (!this.isArray(targetValue)) {
6161
- return [...value];
7066
+ static isValid(id) {
7067
+ if (!/^[0-9A-HJ-NP-Z]{23}$/.test(id))
7068
+ return false;
7069
+ try {
7070
+ const timeComponent = id.slice(0, 10);
7071
+ const timestamp = this.decodeBase32(timeComponent);
7072
+ const now = Date.now() - this.epoch;
7073
+ return timestamp >= 0 && timestamp <= now;
6162
7074
  }
6163
- // make TypeScript understand that targetValue is an array
6164
- const targetArray = targetValue;
6165
- for (let i = 0, l = value.length; i < l; i++) {
6166
- targetArray[i] = this.mergeValue(targetArray[i], value[i]);
7075
+ catch (_a) {
7076
+ return false;
6167
7077
  }
6168
- return targetArray;
6169
7078
  }
6170
7079
  /**
6171
- * Merges the values of two objects or arrays into a single object.
6172
- * If the target value is an array or a record, the method merges the values into the target value.
6173
- * If the target value is not an array or a record, it returns the new value.
6174
- *
6175
- * @template T - The type of the values in the target object.
6176
- * @param {object[]|Record<string, T>} targetValue - The target object or array to merge the values into.
6177
- * @param {Record<string, any>} value - The new values to merge into the target object or array.
6178
- * @returns {Record<string, any>} - The merged object or array.
7080
+ * Decode base32 string to number
6179
7081
  */
6180
- mergeObjectValues(targetValue, value) {
6181
- if (targetValue && typeof targetValue === 'object') {
6182
- this.innerMerge(targetValue, value);
6183
- return targetValue;
7082
+ static decodeBase32(str) {
7083
+ let result = 0;
7084
+ for (const char of str) {
7085
+ result = result * 32 + this.base32Chars.indexOf(char);
6184
7086
  }
6185
- return value ? { ...value } : value;
7087
+ return result;
6186
7088
  }
6187
7089
  /**
6188
- * Merges the properties of the source object into the target object.
6189
- *
6190
- * @param {Record<string, any>} target - The target object to merge the properties into.
6191
- * @param {Record<string, any>} source - The source object containing the properties to merge.
6192
- *
6193
- * @return {void} - This method does not return any value.
7090
+ * Extract timestamp from ID
6194
7091
  */
6195
- innerMerge(target, source) {
6196
- for (const key of Object.keys(source)) {
6197
- target[key] = this.mergeValue(target[key], source[key]);
6198
- }
7092
+ static getTimestamp(id) {
7093
+ if (!this.isValid(id))
7094
+ throw new Error('Invalid ID format');
7095
+ const timeComponent = id.slice(0, 10);
7096
+ const timestamp = this.decodeBase32(timeComponent);
7097
+ return new Date(timestamp + this.epoch);
6199
7098
  }
6200
7099
  }
7100
+ // Constants for bit manipulation
7101
+ UniqueIdGenerator.epoch = 1577836800000; // 2020-01-01 as epoch
7102
+ UniqueIdGenerator.nodeBits = 10;
7103
+ UniqueIdGenerator.sequenceBits = 12;
7104
+ // Instance variables
7105
+ UniqueIdGenerator.lastTimestamp = -1;
7106
+ UniqueIdGenerator.sequence = 0;
7107
+ // Character set for base32 encoding (excluding similar looking characters)
7108
+ UniqueIdGenerator.base32Chars = '0123456789ABCDEFGHJKMNPQRSTVWXYZ';
6201
7109
 
6202
7110
  var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
6203
7111
 
@@ -15205,8 +16113,21 @@ class IntersectionObserverService {
15205
16113
  }
15206
16114
  }
15207
16115
 
15208
- class LocalStorage {
16116
+ var ENUM_LOCAL_STORAGE_SPOT_ARRAY_INDEX;
16117
+ (function (ENUM_LOCAL_STORAGE_SPOT_ARRAY_INDEX) {
16118
+ ENUM_LOCAL_STORAGE_SPOT_ARRAY_INDEX[ENUM_LOCAL_STORAGE_SPOT_ARRAY_INDEX["PLACEMENT_ID"] = 0] = "PLACEMENT_ID";
16119
+ ENUM_LOCAL_STORAGE_SPOT_ARRAY_INDEX[ENUM_LOCAL_STORAGE_SPOT_ARRAY_INDEX["SPOT_ID"] = 1] = "SPOT_ID";
16120
+ ENUM_LOCAL_STORAGE_SPOT_ARRAY_INDEX[ENUM_LOCAL_STORAGE_SPOT_ARRAY_INDEX["SPOT_TYPE"] = 2] = "SPOT_TYPE";
16121
+ ENUM_LOCAL_STORAGE_SPOT_ARRAY_INDEX[ENUM_LOCAL_STORAGE_SPOT_ARRAY_INDEX["EVENTS"] = 3] = "EVENTS";
16122
+ ENUM_LOCAL_STORAGE_SPOT_ARRAY_INDEX[ENUM_LOCAL_STORAGE_SPOT_ARRAY_INDEX["PRODUCT_IDS"] = 4] = "PRODUCT_IDS";
16123
+ ENUM_LOCAL_STORAGE_SPOT_ARRAY_INDEX[ENUM_LOCAL_STORAGE_SPOT_ARRAY_INDEX["CREATED_AT"] = 5] = "CREATED_AT";
16124
+ })(ENUM_LOCAL_STORAGE_SPOT_ARRAY_INDEX || (ENUM_LOCAL_STORAGE_SPOT_ARRAY_INDEX = {}));
16125
+ class LocalStorageService {
15209
16126
  constructor() {
16127
+ if (typeof window.localStorage === 'undefined') {
16128
+ console.warn('Local storage is not supported in this environment');
16129
+ return;
16130
+ }
15210
16131
  this.spots = new Map();
15211
16132
  // Sync local storage with the current state
15212
16133
  this.syncLocalStorage();
@@ -15214,19 +16135,23 @@ class LocalStorage {
15214
16135
  this.removeExpiredSpots();
15215
16136
  }
15216
16137
  static getInstance() {
15217
- if (!LocalStorage.instance) {
15218
- LocalStorage.instance = new LocalStorage();
16138
+ if (!LocalStorageService.instance) {
16139
+ LocalStorageService.instance = new LocalStorageService();
15219
16140
  }
15220
- return LocalStorage.instance;
16141
+ return LocalStorageService.instance;
15221
16142
  }
15222
16143
  syncLocalStorage() {
15223
- const localStorageData = localStorage.getItem(LocalStorage.localStorageKey);
15224
- // TODO: Encrypt the data before storing it in the local storage
16144
+ const localStorageData = window.localStorage.getItem(LocalStorageService.localStorageKey);
15225
16145
  if (localStorageData) {
15226
16146
  try {
15227
- const parsedData = JSON.parse(localStorageData);
16147
+ const decryptedData = this.decryptData(localStorageData);
16148
+ const parsedData = JSON.parse(decryptedData);
15228
16149
  if (parsedData && typeof parsedData === 'object') {
15229
- this.spots = this.objToMap(parsedData);
16150
+ const data = {};
16151
+ for (const [key, value] of Object.entries(parsedData)) {
16152
+ data[key] = this.arrayToObject(value);
16153
+ }
16154
+ this.spots = this.objectToMap(data);
15230
16155
  }
15231
16156
  else {
15232
16157
  this.clearLocalStorage();
@@ -15239,43 +16164,176 @@ class LocalStorage {
15239
16164
  }
15240
16165
  }
15241
16166
  setSpot(spotId, data) {
16167
+ var _a;
15242
16168
  data.createdAt = Date.now();
15243
- this.spots.set(spotId, data);
16169
+ (_a = this.spots) === null || _a === void 0 ? void 0 : _a.set(spotId, data);
15244
16170
  this.updateLocalStorage();
15245
16171
  }
15246
- getSpot(spotId) {
15247
- return this.spots.get(spotId);
15248
- }
15249
16172
  removeSpot(spotId) {
15250
- this.spots.delete(spotId);
16173
+ var _a;
16174
+ (_a = this.spots) === null || _a === void 0 ? void 0 : _a.delete(spotId);
15251
16175
  this.updateLocalStorage();
15252
16176
  }
16177
+ getSpot(spotId) {
16178
+ var _a;
16179
+ return (_a = this.spots) === null || _a === void 0 ? void 0 : _a.get(spotId);
16180
+ }
16181
+ getSpots() {
16182
+ if (!this.spots)
16183
+ return undefined;
16184
+ return this.mapToObject(this.spots);
16185
+ }
15253
16186
  updateLocalStorage() {
15254
- const data = this.mapToObj(this.spots);
15255
- localStorage.setItem(LocalStorage.localStorageKey, JSON.stringify(data));
16187
+ if (!this.spots)
16188
+ return undefined;
16189
+ const data = this.mapToObject(this.spots);
16190
+ const dataArray = {};
16191
+ for (const [key, value] of Object.entries(data)) {
16192
+ dataArray[key] = this.objectToArray(value);
16193
+ }
16194
+ try {
16195
+ const encryptedData = this.encryptData(JSON.stringify(dataArray));
16196
+ window.localStorage.setItem(LocalStorageService.localStorageKey, encryptedData);
16197
+ }
16198
+ catch (_a) {
16199
+ // If there is an error parsing the data, clear the local storage to prevent any issues
16200
+ this.clearLocalStorage();
16201
+ }
15256
16202
  }
15257
16203
  clearLocalStorage() {
15258
- localStorage.removeItem(LocalStorage.localStorageKey);
16204
+ window.localStorage.removeItem(LocalStorageService.localStorageKey);
15259
16205
  }
15260
16206
  removeExpiredSpots() {
16207
+ var _a;
15261
16208
  const currentTime = Date.now();
15262
- this.spots.forEach((spot, spotId) => {
15263
- var _a;
15264
- if (currentTime - ((_a = spot.createdAt) !== null && _a !== void 0 ? _a : 0) > LocalStorage.spotExpirationTime) {
15265
- this.spots.delete(spotId);
16209
+ (_a = this.spots) === null || _a === void 0 ? void 0 : _a.forEach((spot, spotId) => {
16210
+ var _a, _b;
16211
+ if (currentTime - ((_a = spot.createdAt) !== null && _a !== void 0 ? _a : 0) > LocalStorageService.spotExpirationTime) {
16212
+ (_b = this.spots) === null || _b === void 0 ? void 0 : _b.delete(spotId);
15266
16213
  }
15267
16214
  });
15268
16215
  this.updateLocalStorage();
15269
16216
  }
15270
- mapToObj(map) {
16217
+ mapToObject(map) {
15271
16218
  return Object.fromEntries(map);
15272
16219
  }
15273
- objToMap(obj) {
15274
- return new Map(Object.entries(obj));
16220
+ objectToMap(obj) {
16221
+ return new Map(Object.entries(obj));
16222
+ }
16223
+ objectToArray(obj) {
16224
+ return [obj.placementId, obj.spotId, obj.spotType, obj.events, obj.productIds, obj.createdAt];
16225
+ }
16226
+ arrayToObject(arr) {
16227
+ return {
16228
+ placementId: arr[ENUM_LOCAL_STORAGE_SPOT_ARRAY_INDEX.PLACEMENT_ID],
16229
+ spotId: arr[ENUM_LOCAL_STORAGE_SPOT_ARRAY_INDEX.SPOT_ID],
16230
+ spotType: arr[ENUM_LOCAL_STORAGE_SPOT_ARRAY_INDEX.SPOT_TYPE],
16231
+ events: arr[ENUM_LOCAL_STORAGE_SPOT_ARRAY_INDEX.EVENTS],
16232
+ productIds: arr[ENUM_LOCAL_STORAGE_SPOT_ARRAY_INDEX.PRODUCT_IDS],
16233
+ createdAt: arr[ENUM_LOCAL_STORAGE_SPOT_ARRAY_INDEX.CREATED_AT],
16234
+ };
16235
+ }
16236
+ encryptData(data) {
16237
+ if (!LocalStorageService.encryptData)
16238
+ return data;
16239
+ // For now, we are using base64 encoding to encrypt the data
16240
+ // Later we will use Jose encryption
16241
+ const encryptedData = btoa(data);
16242
+ return encryptedData;
16243
+ }
16244
+ decryptData(data) {
16245
+ if (!LocalStorageService.encryptData)
16246
+ return data;
16247
+ // For now, we are using base64 encoding to encrypt
16248
+ // Later we will use Jose encryption
16249
+ const decryptedData = atob(data);
16250
+ return decryptedData;
16251
+ }
16252
+ }
16253
+ LocalStorageService.localStorageKey = 'lc_rmn';
16254
+ LocalStorageService.spotExpirationTime = 1000 * 60 * 60 * 24 * 7; // 7 days
16255
+ LocalStorageService.encryptData = true;
16256
+
16257
+ /**
16258
+ * PubsubService class
16259
+ * Manages event subscriptions and publications
16260
+ * @template IRmnEventMap A record type defining the structure of events and their data
16261
+ */
16262
+ class PubsubService {
16263
+ constructor() {
16264
+ /**
16265
+ * Object to store subscribers for each event type
16266
+ */
16267
+ this.subscribers = {};
16268
+ }
16269
+ static getInstance() {
16270
+ if (!PubsubService.instance) {
16271
+ PubsubService.instance = new PubsubService();
16272
+ }
16273
+ return PubsubService.instance;
16274
+ }
16275
+ /**
16276
+ * Subscribe to an event
16277
+ * @param eventType - The type of event to subscribe to
16278
+ * @param callback - The function to be called when the event is published
16279
+ * @returns A function to unsubscribe from the event
16280
+ *
16281
+ * @Example:
16282
+ * const unsubscribe = pubSub.subscribe('userLogin', (data) => {
16283
+ * console.log(`User ${data.username} logged in`);
16284
+ * });
16285
+ */
16286
+ subscribe(eventType, callback) {
16287
+ if (!this.subscribers[eventType]) {
16288
+ this.subscribers[eventType] = [];
16289
+ }
16290
+ this.subscribers[eventType].push(callback);
16291
+ // Return an unsubscribe function
16292
+ return () => {
16293
+ this.subscribers[eventType] = this.subscribers[eventType].filter((cb) => cb !== callback);
16294
+ };
16295
+ }
16296
+ /**
16297
+ * Publish an event
16298
+ * @param eventType - The type of event to publish
16299
+ * @param data - The data to be passed to the event subscribers
16300
+ *
16301
+ * @Example:
16302
+ * pubSub.publish('userLogin', { username: 'john_doe', timestamp: Date.now() });
16303
+ */
16304
+ publish(eventType, data) {
16305
+ if (!this.subscribers[eventType]) {
16306
+ return;
16307
+ }
16308
+ this.subscribers[eventType].forEach((callback) => callback(data));
15275
16309
  }
15276
16310
  }
15277
- LocalStorage.localStorageKey = 'lc_rmn';
15278
- LocalStorage.spotExpirationTime = 1000 * 60 * 60 * 24 * 7; // 7 days
16311
+ /**
16312
+ * Usage Example:
16313
+ *
16314
+ * interface IRmnEventMap {
16315
+ * userLogin: { username: string; timestamp: number };
16316
+ * pageView: { url: string; timestamp: number };
16317
+ * }
16318
+ *
16319
+ * const pubSub = new PubsubService<IRmnEventMap>();
16320
+ *
16321
+ * // Subscribe to events
16322
+ * const unsubscribeLogin = pubSub.subscribe('userLogin', (data) => {
16323
+ * console.log(`User ${data.username} logged in at ${new Date(data.timestamp)}`);
16324
+ * });
16325
+ *
16326
+ * pubSub.subscribe('pageView', (data) => {
16327
+ * console.log(`Page ${data.url} viewed at ${new Date(data.timestamp)}`);
16328
+ * });
16329
+ *
16330
+ * // Publish events
16331
+ * pubSub.publish('userLogin', { username: 'john_doe', timestamp: Date.now() });
16332
+ * pubSub.publish('pageView', { url: '/home', timestamp: Date.now() });
16333
+ *
16334
+ * // Unsubscribe from an event
16335
+ * unsubscribeLogin();
16336
+ */
15279
16337
 
15280
16338
  class ResizeObserverService {
15281
16339
  constructor({ element, maxSize, minScale }) {
@@ -15345,36 +16403,6 @@ class ResizeObserverService {
15345
16403
  }
15346
16404
  }
15347
16405
 
15348
- function calculateScaleFactor(elementScale) {
15349
- // Step 1: Apply square root for non-linear scaling
15350
- // This creates a more gradual scaling effect, especially for larger changes
15351
- // For example:
15352
- // - elementScale of 0.25 (1/4 size) becomes 0.5
15353
- // - elementScale of 1 (unchanged) remains 1
15354
- // - elementScale of 4 (4x size) becomes 2
15355
- const baseFactor = Math.sqrt(elementScale);
15356
- // Step 2: Apply additional dampening to further soften the scaling effect
15357
- // The dampening factor (0.5) can be adjusted:
15358
- // - Lower values (closer to 0) make scaling more subtle
15359
- // - Higher values (closer to 1) make scaling more pronounced
15360
- const dampening = 0.35;
15361
- // Calculate the scaleFactor:
15362
- // 1. (baseFactor - 1) represents the change from the original size
15363
- // 2. Multiply by dampening to reduce the effect
15364
- // 3. Add 1 to center the scaling around the original size
15365
- // For example, if baseFactor is 2:
15366
- // scaleFactor = 1 + (2 - 1) * 0.5 = 1.5
15367
- const scaleFactor = 1 + (baseFactor - 1) * dampening;
15368
- // Step 3: Define the allowed range for the scale factor
15369
- // This ensures that the font size never changes too drastically
15370
- const minScale = 0.35; // Font will never be smaller than 50% of original
15371
- const maxScale = 1.5; // Font will never be larger than 150% of original
15372
- // Step 4: Clamp the scale factor to the defined range
15373
- // Math.min ensures the value doesn't exceed maxScale
15374
- // Math.max ensures the value isn't less than minScale
15375
- return Math.max(minScale, Math.min(maxScale, scaleFactor));
15376
- }
15377
-
15378
16406
  const CAROUSEL_COMPONENT_STYLE = ({ width, height, fluid }) => `
15379
16407
  :host {
15380
16408
  position: relative;
@@ -15389,11 +16417,13 @@ const CAROUSEL_COMPONENT_STYLE = ({ width, height, fluid }) => `
15389
16417
  position: relative;
15390
16418
  height: 100%;
15391
16419
  width: 100%;
16420
+ display: flex;
16421
+ transition: transform 0.5s ease-in-out;
15392
16422
  }
15393
16423
 
15394
16424
  .slide {
15395
- display: none;
15396
-
16425
+ flex: 0 0 100%;
16426
+ display: flex;
15397
16427
  justify-content: center;
15398
16428
  align-items: center;
15399
16429
  height: 100%;
@@ -15698,9 +16728,9 @@ if (typeof window !== 'undefined' && typeof window.customElements !== 'undefined
15698
16728
  renderSlides() {
15699
16729
  const slidesContainer = document.createElement('div');
15700
16730
  slidesContainer.className = 'slides';
15701
- this.slides.forEach((slide, index) => {
16731
+ this.slides.forEach((slide) => {
15702
16732
  const slideElement = document.createElement('div');
15703
- slideElement.className = `slide ${index === this.currentSlide ? 'active' : ''}`;
16733
+ slideElement.className = 'slide';
15704
16734
  if (slide instanceof HTMLElement) {
15705
16735
  slideElement.appendChild(slide);
15706
16736
  }
@@ -15779,10 +16809,9 @@ if (typeof window !== 'undefined' && typeof window.customElements !== 'undefined
15779
16809
  updateCarousel() {
15780
16810
  if (!this.slidesContainer)
15781
16811
  return;
15782
- const slides = Array.from(this.slidesContainer.children);
15783
- slides.forEach((slide, index) => {
15784
- slide.classList.toggle('active', index === this.currentSlide);
15785
- });
16812
+ // Calculate the translation distance based on current slide
16813
+ const translateX = -this.currentSlide * 100;
16814
+ this.slidesContainer.style.transform = `translateX(${translateX}%)`;
15786
16815
  this.updateDots();
15787
16816
  }
15788
16817
  updateDots() {
@@ -16034,141 +17063,6 @@ class ElementService {
16034
17063
  }
16035
17064
  }
16036
17065
 
16037
- class UniqueIdGenerator {
16038
- /**
16039
- * Initialize the generator with a node ID
16040
- * @param nodeId Number between 0-1023 to identify this instance
16041
- */
16042
- static initialize(nodeId = Math.floor(Math.random() * 1024)) {
16043
- if (nodeId < 0 || nodeId >= 1 << this.nodeBits) {
16044
- throw new Error(`Node ID must be between 0 and ${(1 << this.nodeBits) - 1}`);
16045
- }
16046
- this.nodeId = nodeId;
16047
- }
16048
- /**
16049
- * Convert a number to base32 string with specified length
16050
- */
16051
- static toBase32(num, length) {
16052
- let result = '';
16053
- while (num > 0) {
16054
- result = this.base32Chars[Number(num % BigInt(32))] + result;
16055
- // @ts-expect-error - TS doesn't support bigint division
16056
- num = num / 32n;
16057
- }
16058
- return result.padStart(length, '0');
16059
- }
16060
- /**
16061
- * Generate a cryptographically secure random number
16062
- */
16063
- static getSecureRandom() {
16064
- if (typeof crypto !== 'undefined') {
16065
- const buffer = new Uint32Array(1);
16066
- crypto.getRandomValues(buffer);
16067
- return buffer[0];
16068
- }
16069
- return Math.floor(Math.random() * 0xffffffff);
16070
- }
16071
- /**
16072
- * Wait until next millisecond
16073
- */
16074
- static waitNextMillis(lastTimestamp) {
16075
- let timestamp = Date.now();
16076
- while (timestamp <= lastTimestamp) {
16077
- timestamp = Date.now();
16078
- }
16079
- return timestamp;
16080
- }
16081
- /**
16082
- * Generates a highly unique ID with the following format:
16083
- * TTTTTTTTTTCCCCNNNNNRRRR
16084
- * T: Timestamp (10 chars)
16085
- * C: Counter (4 chars)
16086
- * N: Node ID (5 chars)
16087
- * R: Random (4 chars)
16088
- *
16089
- * Total length: 23 characters, always uppercase alphanumeric
16090
- */
16091
- static generate() {
16092
- if (this.nodeId === undefined) {
16093
- this.initialize();
16094
- }
16095
- let timestamp = Date.now() - this.epoch;
16096
- // Handle clock moving backwards or same millisecond
16097
- if (timestamp < this.lastTimestamp) {
16098
- throw new Error('Clock moved backwards. Refusing to generate ID.');
16099
- }
16100
- if (timestamp === this.lastTimestamp) {
16101
- this.sequence = (this.sequence + 1) & ((1 << this.sequenceBits) - 1);
16102
- if (this.sequence === 0) {
16103
- timestamp = this.waitNextMillis(this.lastTimestamp);
16104
- }
16105
- }
16106
- else {
16107
- this.sequence = 0;
16108
- }
16109
- this.lastTimestamp = timestamp;
16110
- // Generate random component
16111
- const random = this.getSecureRandom() & 0xffff; // 16 bits of randomness
16112
- // Combine all components into a BigInt
16113
- // const id =
16114
- // (BigInt(timestamp) << BigInt(this.nodeBits + this.sequenceBits + 16)) |
16115
- // (BigInt(this.nodeId) << BigInt(this.sequenceBits + 16)) |
16116
- // (BigInt(this.sequence) << BigInt(16)) |
16117
- // BigInt(random);
16118
- // Convert to base32 representation
16119
- const timeComponent = this.toBase32(BigInt(timestamp), 10);
16120
- const counterComponent = this.toBase32(BigInt(this.sequence), 4);
16121
- const nodeComponent = this.toBase32(BigInt(this.nodeId), 5);
16122
- const randomComponent = this.toBase32(BigInt(random), 4);
16123
- return `${timeComponent}${counterComponent}${nodeComponent}${randomComponent}`;
16124
- }
16125
- /**
16126
- * Validates if a string matches the expected ID format
16127
- */
16128
- static isValid(id) {
16129
- if (!/^[0-9A-HJ-NP-Z]{23}$/.test(id))
16130
- return false;
16131
- try {
16132
- const timeComponent = id.slice(0, 10);
16133
- const timestamp = this.decodeBase32(timeComponent);
16134
- const now = Date.now() - this.epoch;
16135
- return timestamp >= 0 && timestamp <= now;
16136
- }
16137
- catch (_a) {
16138
- return false;
16139
- }
16140
- }
16141
- /**
16142
- * Decode base32 string to number
16143
- */
16144
- static decodeBase32(str) {
16145
- let result = 0;
16146
- for (const char of str) {
16147
- result = result * 32 + this.base32Chars.indexOf(char);
16148
- }
16149
- return result;
16150
- }
16151
- /**
16152
- * Extract timestamp from ID
16153
- */
16154
- static getTimestamp(id) {
16155
- if (!this.isValid(id))
16156
- throw new Error('Invalid ID format');
16157
- const timeComponent = id.slice(0, 10);
16158
- const timestamp = this.decodeBase32(timeComponent);
16159
- return new Date(timestamp + this.epoch);
16160
- }
16161
- }
16162
- // Constants for bit manipulation
16163
- UniqueIdGenerator.epoch = 1577836800000; // 2020-01-01 as epoch
16164
- UniqueIdGenerator.nodeBits = 10;
16165
- UniqueIdGenerator.sequenceBits = 12;
16166
- // Instance variables
16167
- UniqueIdGenerator.lastTimestamp = -1;
16168
- UniqueIdGenerator.sequence = 0;
16169
- // Character set for base32 encoding (excluding similar looking characters)
16170
- UniqueIdGenerator.base32Chars = '0123456789ABCDEFGHJKMNPQRSTVWXYZ';
16171
-
16172
17066
  function convertHexToRgba(hex, opacity = 1) {
16173
17067
  // Remove # if present
16174
17068
  const cleanHex = hex.replace('#', '');
@@ -16206,12 +17100,13 @@ function spotHtmlStringToElement(htmlString) {
16206
17100
  spot.className = 'spot';
16207
17101
  spot.innerHTML = htmlString;
16208
17102
  Object.assign(spot.style, {
16209
- position: 'relative',
16210
17103
  display: 'block',
16211
17104
  width: '100%',
16212
17105
  height: '100%',
16213
17106
  margin: '0',
16214
17107
  padding: '0',
17108
+ containerType: 'inline-size',
17109
+ position: 'relative',
16215
17110
  });
16216
17111
  return spot;
16217
17112
  }
@@ -17081,7 +17976,6 @@ const STYLES$6 = ({ textColor = '#ffffff', ctaTextColor = textColor, ctaBorderCo
17081
17976
  box-sizing: border-box;
17082
17977
  color: ${textColor};
17083
17978
  cursor: pointer;
17084
- container-type: inline-size;
17085
17979
  }
17086
17980
 
17087
17981
  .${prefix}__text {
@@ -17097,7 +17991,7 @@ const STYLES$6 = ({ textColor = '#ffffff', ctaTextColor = textColor, ctaBorderCo
17097
17991
  .${prefix}__header {
17098
17992
  font-size: 24px;
17099
17993
  margin: 0;
17100
- font-family: "Cormorant";
17994
+ font-family: "Cormorant", system-ui;
17101
17995
  font-style: normal;
17102
17996
  font-weight: 300;
17103
17997
  line-height: normal;
@@ -17216,7 +18110,6 @@ const STYLES$5 = ({ textColor = '#212121', backgroundColor = '#e8e6de', ctaTextC
17216
18110
  height: 100%;
17217
18111
  display: block;
17218
18112
  position: relative;
17219
- container-type: inline-size;
17220
18113
  }
17221
18114
 
17222
18115
  .${prefix}__content {
@@ -17407,15 +18300,20 @@ function rbHomepageHeroThreeTileTemplate(spot, config) {
17407
18300
  const STYLES$4 = ({ textColor = '#212121', backgroundColor = '#e8e6de', ctaTextColor = textColor, primaryImage, mobilePrimaryImage = primaryImage, }, { prefix }) => `
17408
18301
  <style>
17409
18302
  .${prefix} {
18303
+ width: 100%;
18304
+ height: 100%;
18305
+ background-color: transparent;
18306
+ cursor: pointer;
18307
+ position: relative;
18308
+ }
18309
+
18310
+ .${prefix}__content {
17410
18311
  width: 100%;
17411
18312
  height: 100%;
17412
18313
  display: flex;
17413
18314
  flex-direction: column-reverse;
17414
18315
  background-color: transparent;
17415
18316
  gap: 6px;
17416
- cursor: pointer;
17417
- container-type: inline-size;
17418
- position: relative;
17419
18317
  }
17420
18318
 
17421
18319
  .${prefix}__image {
@@ -17446,7 +18344,7 @@ const STYLES$4 = ({ textColor = '#212121', backgroundColor = '#e8e6de', ctaTextC
17446
18344
  font-size: 18px;
17447
18345
  margin: 0;
17448
18346
  color: inherit;
17449
- font-family: "Cormorant";
18347
+ font-family: "Cormorant", system-ui;
17450
18348
  font-style: normal;
17451
18349
  font-weight: 700;
17452
18350
  line-height: normal;
@@ -17488,7 +18386,7 @@ const STYLES$4 = ({ textColor = '#212121', backgroundColor = '#e8e6de', ctaTextC
17488
18386
  }
17489
18387
 
17490
18388
  @container (min-width: 768px) {
17491
- .${prefix} {
18389
+ .${prefix}__content {
17492
18390
  flex-direction: row;
17493
18391
  }
17494
18392
  .${prefix}__image {
@@ -17543,12 +18441,14 @@ function rbHomepageHeroTwoTileTemplate(spot, config) {
17543
18441
  ${GFONT_CORMORANT}
17544
18442
  ${STYLES$4(spot, config)}
17545
18443
  <div class="${prefix}">
17546
- <div class="${prefix}__text">
17547
- ${spot.header ? `<h2 class="${prefix}__header">${spot.header}</h2>` : ''}
17548
- ${spot.description ? `<p class="${prefix}__description">${spot.description}</p>` : ''}
17549
- ${spot.ctaText ? `<span class="${prefix}__cta-button">${spot.ctaText}</span>` : ''}
17550
- </div>
17551
- <div class="${prefix}__image"></div>
18444
+ <div class="${prefix}__content">
18445
+ <div class="${prefix}__text">
18446
+ ${spot.header ? `<h2 class="${prefix}__header">${spot.header}</h2>` : ''}
18447
+ ${spot.description ? `<p class="${prefix}__description">${spot.description}</p>` : ''}
18448
+ ${spot.ctaText ? `<span class="${prefix}__cta-button">${spot.ctaText}</span>` : ''}
18449
+ </div>
18450
+ <div class="${prefix}__image"></div>
18451
+ </div>
17552
18452
  </div>
17553
18453
  `;
17554
18454
  }
@@ -17571,12 +18471,10 @@ const STYLES$3 = ({ textColor = '#ffffff', ctaTextColor = textColor, ctaBorderCo
17571
18471
  overflow: hidden;
17572
18472
  cursor: pointer;
17573
18473
  color: ${textColor};
17574
- container-type: inline-size;
17575
18474
  }
17576
18475
 
17577
18476
  .${prefix}__text {
17578
18477
  padding: 20px;
17579
- width: 70%;
17580
18478
  display: flex;
17581
18479
  flex-direction: column;
17582
18480
  justify-content: center;
@@ -17588,7 +18486,7 @@ const STYLES$3 = ({ textColor = '#ffffff', ctaTextColor = textColor, ctaBorderCo
17588
18486
  color: inherit;
17589
18487
  margin: 0;
17590
18488
  font-size: 20px;
17591
- font-family: "Cormorant";
18489
+ font-family: "Cormorant", system-ui;
17592
18490
  font-style: normal;
17593
18491
  font-weight: 300;
17594
18492
  line-height: normal;
@@ -17707,7 +18605,6 @@ const STYLES$2 = ({ textColor = '#ffffff', primaryImage, mobilePrimaryImage = pr
17707
18605
  background-size: cover;
17708
18606
  background-position: center;
17709
18607
  background-repeat: no-repeat;
17710
- container-type: inline-size;
17711
18608
  position: relative;
17712
18609
  }
17713
18610
 
@@ -17777,12 +18674,7 @@ const STYLES$1 = ({ textColor = '#ffffff', primaryImage, mobilePrimaryImage = pr
17777
18674
  border-radius: 5px;
17778
18675
  overflow: hidden;
17779
18676
  cursor: pointer;
17780
- container-type: inline-size;
17781
- }
17782
-
17783
- .${prefix}__text {
17784
- padding: 10px;
17785
- width: 70%;
18677
+ position: relative;
17786
18678
  }
17787
18679
 
17788
18680
  .${prefix}__header {
@@ -17793,6 +18685,7 @@ const STYLES$1 = ({ textColor = '#ffffff', primaryImage, mobilePrimaryImage = pr
17793
18685
  font-style: normal;
17794
18686
  font-weight: 400;
17795
18687
  margin: 0;
18688
+ padding: 10px;
17796
18689
  }
17797
18690
 
17798
18691
  @container (min-width: 640px) {
@@ -17810,9 +18703,7 @@ function rbSmallCategoryImageToutTemplate(spot, config) {
17810
18703
  ${GFONT_CORMORANT}
17811
18704
  ${STYLES$1(spot, config)}
17812
18705
  <div class="${prefix}">
17813
- <div class="${prefix}__text">
17814
- ${spot.header ? `<h2 class="${prefix}__header">${spot.header}</h2>` : ''}
17815
- </div>
18706
+ ${spot.header ? `<h2 class="${prefix}__header">${spot.header}</h2>` : ''}
17816
18707
  </div>
17817
18708
  `;
17818
18709
  }
@@ -17827,7 +18718,6 @@ const STYLES = ({ textColor = '#000000', backgroundColor = 'transparent', primar
17827
18718
  display: flex;
17828
18719
  flex-direction: column;
17829
18720
  border-radius: 5px;
17830
- container-type: inline-size;
17831
18721
  }
17832
18722
 
17833
18723
  .${prefix}__image {
@@ -17963,94 +18853,164 @@ const SPOT_TEMPLATE_HTML_ELEMENT = (spot, config) => {
17963
18853
  return spotHtmlStringToElement(spotHtmlString);
17964
18854
  };
17965
18855
 
17966
- /**
17967
- * PubSub class
17968
- * Manages event subscriptions and publications
17969
- * @template IEventMap A record type defining the structure of events and their data
17970
- */
17971
- class PubSub {
18856
+ // For the moment, we will only focus on sites that use Google Analytics,
18857
+ // but we will add support for other analytics tools in the future.
18858
+ var AnalyticsTool;
18859
+ (function (AnalyticsTool) {
18860
+ AnalyticsTool["GoogleAnalytics"] = "google-analytics";
18861
+ AnalyticsTool["Other"] = "Other";
18862
+ })(AnalyticsTool || (AnalyticsTool = {}));
18863
+
18864
+ class DataLayerMonitor {
18865
+ constructor() {
18866
+ if (!window.dataLayer) {
18867
+ return;
18868
+ }
18869
+ this.originalPush = window.dataLayer.push;
18870
+ }
18871
+ static getInstance() {
18872
+ if (!DataLayerMonitor.instance) {
18873
+ DataLayerMonitor.instance = new DataLayerMonitor();
18874
+ }
18875
+ return DataLayerMonitor.instance;
18876
+ }
18877
+ setListener(listener) {
18878
+ this.listener = listener;
18879
+ }
18880
+ start() {
18881
+ window.dataLayer.push = (...args) => {
18882
+ const result = this.originalPush.apply(window.dataLayer, args);
18883
+ const pushedEvent = args[0];
18884
+ if (this.listener) {
18885
+ const normalizedData = this.cleanEventData(pushedEvent);
18886
+ if (normalizedData) {
18887
+ this.listener(normalizedData);
18888
+ }
18889
+ }
18890
+ return result;
18891
+ };
18892
+ }
18893
+ cleanEventData(data) {
18894
+ const eventName = getEventTypeFromRawEvent(data.event);
18895
+ if (!eventName) {
18896
+ return null;
18897
+ }
18898
+ const productIds = extractDeepIds(data.value);
18899
+ return {
18900
+ event: eventName,
18901
+ productIds,
18902
+ };
18903
+ }
18904
+ stop() {
18905
+ if (this.originalPush) {
18906
+ window.dataLayer.push = this.originalPush;
18907
+ }
18908
+ this.listener = undefined;
18909
+ }
18910
+ }
18911
+
18912
+ // @TODO: Add support for user to push events to our own data layer, if they don't use any analytics tool.
18913
+ // window.rmnDataLayer = window.rmnDataLayer || [];
18914
+ class MonitorService {
17972
18915
  constructor() {
17973
- /**
17974
- * Object to store subscribers for each event type
17975
- */
17976
- this.subscribers = {};
18916
+ const analyticsTool = this.detectAnalyticsTool();
18917
+ switch (analyticsTool) {
18918
+ case AnalyticsTool.GoogleAnalytics:
18919
+ this.implementedMonitor = DataLayerMonitor.getInstance();
18920
+ break;
18921
+ case AnalyticsTool.Other:
18922
+ default:
18923
+ console.warn('This site uses an unsupported analytics tool.');
18924
+ break;
18925
+ }
18926
+ if (analyticsTool === AnalyticsTool.Other) {
18927
+ return;
18928
+ }
18929
+ this.pubSubService = PubsubService.getInstance();
18930
+ this.localStorageService = LocalStorageService.getInstance();
17977
18931
  }
17978
18932
  static getInstance() {
17979
- if (!PubSub.instance) {
17980
- PubSub.instance = new PubSub();
18933
+ if (!MonitorService.instance) {
18934
+ MonitorService.instance = new MonitorService();
17981
18935
  }
17982
- return PubSub.instance;
18936
+ return MonitorService.instance;
17983
18937
  }
17984
- /**
17985
- * Subscribe to an event
17986
- * @param eventType - The type of event to subscribe to
17987
- * @param callback - The function to be called when the event is published
17988
- * @returns A function to unsubscribe from the event
17989
- *
17990
- * @Example:
17991
- * const unsubscribe = pubSub.subscribe('userLogin', (data) => {
17992
- * console.log(`User ${data.username} logged in`);
17993
- * });
17994
- */
17995
- subscribe(eventType, callback) {
17996
- if (!this.subscribers[eventType]) {
17997
- this.subscribers[eventType] = [];
18938
+ start() {
18939
+ if (!this.implementedMonitor)
18940
+ return;
18941
+ this.implementedMonitor.setListener(async (eventData) => {
18942
+ var _a;
18943
+ await this.matchAndFireEvent(eventData, (_a = this.localStorageService) === null || _a === void 0 ? void 0 : _a.getSpots());
18944
+ });
18945
+ this.implementedMonitor.start();
18946
+ }
18947
+ async matchAndFireEvent(eventData, spots) {
18948
+ var _a, _b;
18949
+ if (!spots)
18950
+ return;
18951
+ const eventProductIds = new Set(eventData.productIds);
18952
+ for (const spot of Object.values(spots)) {
18953
+ if (!spot.productIds.length)
18954
+ continue;
18955
+ const hasCommonProductIds = spot.productIds.find((productId) => eventProductIds.has(productId));
18956
+ if (hasCommonProductIds) {
18957
+ if (Object.values(exports.RMN_SPOT_EVENT).includes(eventData.event)) {
18958
+ await this.fireAndPublishSpotEvent({
18959
+ spotEvent: eventData.event,
18960
+ eventUrl: (_b = (_a = spot.events.find((event) => event.event === eventData.event)) === null || _a === void 0 ? void 0 : _a.url) !== null && _b !== void 0 ? _b : '',
18961
+ placementId: spot.placementId,
18962
+ spotId: spot.spotId,
18963
+ });
18964
+ }
18965
+ }
17998
18966
  }
17999
- this.subscribers[eventType].push(callback);
18000
- // Return an unsubscribe function
18001
- return () => {
18002
- this.subscribers[eventType] = this.subscribers[eventType].filter((cb) => cb !== callback);
18003
- };
18004
18967
  }
18005
- /**
18006
- * Publish an event
18007
- * @param eventType - The type of event to publish
18008
- * @param data - The data to be passed to the event subscribers
18009
- *
18010
- * @Example:
18011
- * pubSub.publish('userLogin', { username: 'john_doe', timestamp: Date.now() });
18012
- */
18013
- publish(eventType, data) {
18014
- if (!this.subscribers[eventType]) {
18968
+ async fireAndPublishSpotEvent({ spotEvent, eventUrl, placementId, spotId, }) {
18969
+ await fireEvent({
18970
+ event: spotEvent,
18971
+ eventUrl,
18972
+ });
18973
+ if (!this.pubSubService)
18015
18974
  return;
18975
+ this.pubSubService.publish(exports.RMN_EVENT.SPOT_EVENT, {
18976
+ eventType: spotEvent,
18977
+ placementId,
18978
+ spotId,
18979
+ });
18980
+ }
18981
+ detectAnalyticsTool() {
18982
+ let analyticsTool = AnalyticsTool.Other;
18983
+ // Check for Google Analytics
18984
+ if (typeof window.ga !== 'undefined') {
18985
+ analyticsTool = AnalyticsTool.GoogleAnalytics;
18016
18986
  }
18017
- this.subscribers[eventType].forEach((callback) => callback(data));
18987
+ // Check for Google Analytics 4
18988
+ if (typeof window.gtag !== 'undefined') {
18989
+ analyticsTool = AnalyticsTool.GoogleAnalytics;
18990
+ }
18991
+ // Check for Google Tag Manager
18992
+ if (typeof window.google_tag_manager !== 'undefined') {
18993
+ analyticsTool = AnalyticsTool.GoogleAnalytics;
18994
+ }
18995
+ // @TODO: Add support for other analytics tools
18996
+ // Check for Heap Analytics
18997
+ // Check for Mixpanel
18998
+ // Check for Woopra
18999
+ // Check for Segment
19000
+ // Check for Amplitude
19001
+ return analyticsTool;
18018
19002
  }
18019
19003
  }
18020
- /**
18021
- * Usage Example:
18022
- *
18023
- * interface IEventMap {
18024
- * userLogin: { username: string; timestamp: number };
18025
- * pageView: { url: string; timestamp: number };
18026
- * }
18027
- *
18028
- * const pubSub = new PubSub<IEventMap>();
18029
- *
18030
- * // Subscribe to events
18031
- * const unsubscribeLogin = pubSub.subscribe('userLogin', (data) => {
18032
- * console.log(`User ${data.username} logged in at ${new Date(data.timestamp)}`);
18033
- * });
18034
- *
18035
- * pubSub.subscribe('pageView', (data) => {
18036
- * console.log(`Page ${data.url} viewed at ${new Date(data.timestamp)}`);
18037
- * });
18038
- *
18039
- * // Publish events
18040
- * pubSub.publish('userLogin', { username: 'john_doe', timestamp: Date.now() });
18041
- * pubSub.publish('pageView', { url: '/home', timestamp: Date.now() });
18042
- *
18043
- * // Unsubscribe from an event
18044
- * unsubscribeLogin();
18045
- */
18046
19004
 
18047
19005
  class EventService {
18048
19006
  constructor() {
18049
- this.pubSub = PubSub.getInstance();
18050
- this.localStorage = LocalStorage.getInstance();
19007
+ this.pubSubService = PubsubService.getInstance();
19008
+ this.localStorageService = LocalStorageService.getInstance();
18051
19009
  this.activeSpots = new Map();
18052
19010
  this.spotStates = new Map();
18053
19011
  this.intersectionObserver = new IntersectionObserverService();
19012
+ // Start the user monitor, which will track and check user interactions
19013
+ MonitorService.getInstance().start();
18054
19014
  }
18055
19015
  static getInstance() {
18056
19016
  if (!EventService.instance) {
@@ -18059,16 +19019,16 @@ class EventService {
18059
19019
  return EventService.instance;
18060
19020
  }
18061
19021
  subscribe(eventType, callback) {
18062
- return this.pubSub.subscribe(eventType, callback);
19022
+ return this.pubSubService.subscribe(eventType, callback);
18063
19023
  }
18064
19024
  publish(eventType, data) {
18065
- this.pubSub.publish(eventType, data);
19025
+ this.pubSubService.publish(eventType, data);
18066
19026
  }
18067
19027
  registerSpot(params) {
18068
19028
  const { placementId, spot, spotElement } = params;
18069
19029
  this.activeSpots.set(placementId, { spotElement });
18070
19030
  // Fire impression event
18071
- this.fireImpressionEvent(placementId, spot, spotElement);
19031
+ this.fireImpressionEvent(placementId, spot);
18072
19032
  // Handle intersection observer
18073
19033
  this.handleIntersectionObserver(placementId, spot, spotElement);
18074
19034
  // Attach click event listener
@@ -18143,30 +19103,42 @@ class EventService {
18143
19103
  },
18144
19104
  };
18145
19105
  }
18146
- this.spotStates.set(placementId, { ...currentState, ...updates });
19106
+ const merged = this.deepMerge(currentState, updates);
19107
+ this.spotStates.set(placementId, merged);
18147
19108
  if (publish) {
18148
- this.pubSub.publish(exports.RMN_SPOT_EVENT.LIFECYCLE_STATE, this.spotStates.get(placementId));
19109
+ this.pubSubService.publish(exports.RMN_EVENT.LIFECYCLE_STATE, this.spotStates.get(placementId));
18149
19110
  }
18150
19111
  }
18151
- async handleClick({ placementId, spot, spotElement, }) {
19112
+ deepMerge(current, updates) {
19113
+ return {
19114
+ identifier: updates.identifier
19115
+ ? { ...current.identifier, ...updates.identifier }
19116
+ : current.identifier,
19117
+ dom: updates.dom ? { ...current.dom, ...updates.dom } : current.dom,
19118
+ state: updates.state ? { ...current.state, ...updates.state } : current.state,
19119
+ displayConfig: updates.displayConfig
19120
+ ? { ...current.displayConfig, ...updates.displayConfig }
19121
+ : current.displayConfig,
19122
+ };
19123
+ }
19124
+ async handleClick({ placementId, spot }) {
18152
19125
  var _a, _b, _c;
18153
- // Publish click event
18154
- this.pubSub.publish(exports.RMN_SPOT_EVENT.CLICK, {
19126
+ this.pubSubService.publish(exports.RMN_EVENT.SPOT_EVENT, {
19127
+ eventType: exports.RMN_SPOT_EVENT.CLICK,
18155
19128
  placementId,
18156
19129
  spotId: spot.id,
18157
- spotElement,
18158
19130
  });
18159
- // Fire click event
18160
- await this.fireEvent({
19131
+ await fireEvent({
18161
19132
  event: exports.RMN_SPOT_EVENT.CLICK,
18162
19133
  eventUrl: (_b = (_a = spot.events.find((event) => event.event === exports.RMN_SPOT_EVENT.CLICK)) === null || _a === void 0 ? void 0 : _a.url) !== null && _b !== void 0 ? _b : '',
18163
19134
  });
18164
19135
  // Save spot to local storage for event tracking
18165
- this.localStorage.setSpot(spot.id, {
19136
+ this.localStorageService.setSpot(spot.id, {
19137
+ placementId,
18166
19138
  spotId: spot.id,
18167
19139
  spotType: spot.spot,
18168
19140
  events: spot.events,
18169
- productIds: (_c = spot.productIds) !== null && _c !== void 0 ? _c : [1, 'GROUPING-12345', 'DAN-12345', 131398103],
19141
+ productIds: (_c = spot.productIds) !== null && _c !== void 0 ? _c : [1, 2, 3],
18170
19142
  });
18171
19143
  }
18172
19144
  handleIntersectionObserver(placementId, _spot, spotElement) {
@@ -18181,51 +19153,20 @@ class EventService {
18181
19153
  };
18182
19154
  this.intersectionObserver.observe(spotElement, spotIsVisibleCallback);
18183
19155
  }
18184
- fireImpressionEvent(placementId, spot, spotElement) {
18185
- this.pubSub.publish(exports.RMN_SPOT_EVENT.IMPRESSION, {
19156
+ fireImpressionEvent(placementId, spot) {
19157
+ this.pubSubService.publish(exports.RMN_EVENT.SPOT_EVENT, {
19158
+ eventType: exports.RMN_SPOT_EVENT.IMPRESSION,
18186
19159
  placementId,
18187
19160
  spotId: spot.id,
18188
- spotElement,
18189
19161
  });
18190
19162
  (async () => {
18191
19163
  var _a, _b;
18192
- await this.fireEvent({
19164
+ await fireEvent({
18193
19165
  event: exports.RMN_SPOT_EVENT.IMPRESSION,
18194
19166
  eventUrl: (_b = (_a = spot.events.find((event) => event.event === exports.RMN_SPOT_EVENT.IMPRESSION)) === null || _a === void 0 ? void 0 : _a.url) !== null && _b !== void 0 ? _b : '',
18195
19167
  });
18196
19168
  })();
18197
19169
  }
18198
- /**
18199
- * Fires an event using the navigator.sendBeacon method and redirects the user if the event is a click event.
18200
- *
18201
- * @param {IFireEventParams} params - The parameters for firing the event.
18202
- * @param {RMN_SPOT_EVENT} params.event - The event type.
18203
- * @param {string} params.eventUrl - The URL to which the event is sent.
18204
- * @returns {Promise<void>} - A promise that resolves when the event is fired.
18205
- */
18206
- async fireEvent({ event, eventUrl }) {
18207
- const didFireEvent = navigator.sendBeacon(eventUrl);
18208
- if (didFireEvent && event === exports.RMN_SPOT_EVENT.CLICK) {
18209
- window.location.href = this.getRedirectUrlFromPayload(eventUrl);
18210
- }
18211
- }
18212
- /**
18213
- * Extracts and decodes a URL from a base64-encoded query parameter.
18214
- *
18215
- * @param {string} url - The URL containing the base64-encoded query parameter.
18216
- * @returns {string} - The decoded URL or an empty string if decoding fails.
18217
- */
18218
- getRedirectUrlFromPayload(url) {
18219
- var _a, _b;
18220
- const base64String = (_a = new URL(url).searchParams.get('e')) !== null && _a !== void 0 ? _a : '';
18221
- try {
18222
- const data = JSON.parse(atob(base64String));
18223
- return (_b = data.ur) !== null && _b !== void 0 ? _b : '';
18224
- }
18225
- catch (_c) {
18226
- return '';
18227
- }
18228
- }
18229
19170
  }
18230
19171
 
18231
19172
  const SELECTION_API_PATH = '/spots/selection';
@@ -18258,176 +19199,6 @@ class SelectionService extends BaseApi {
18258
19199
  }
18259
19200
  }
18260
19201
 
18261
- const SPOT_EVENTS_EXAMPLE = [
18262
- {
18263
- event: exports.RMN_SPOT_EVENT.CLICK,
18264
- url: 'https://dev.rmn.liquidcommerce.cloud/api/spots/event?e=eyJ2IjoiMS4xMiIsImF2IjozMDY1NzgzLCJhdCI6MTYzLCJidCI6MCwiY20iOjQ0MDE5MjQxOCwiY2giOjYzMTg0LCJjayI6e30sImNyIjo0ODE4NTUzNzUsImRpIjoiOWMxNGFhMGI3NWY4NDMxNTllMTAwYWQzNzA1NzQyYzMiLCJkaiI6MCwiaWkiOiIxZjU0MGM5NmQ1N2M0YmZjODFlZjRkNjhkMzFjNDVkOSIsImRtIjozLCJmYyI6NjU2NjgyNTQ5LCJmbCI6NjQzOTMxODIxLCJpcCI6IjM1LjIyMy4xOTguOTUiLCJudyI6MTE1MDAsInBjIjo1MDAwLCJvcCI6NTAwMCwibXAiOjUwMDAsImVjIjowLCJnbSI6MCwiZXAiOm51bGwsInByIjoyNDkzMTYsInJ0IjoxLCJycyI6NTAwLCJzYSI6IjU1Iiwic2IiOiJpLTA0MDI0ODg4ZDlkMWRjZWQ3Iiwic3AiOjI3MjI3Miwic3QiOjEyODcyOTYsInRyIjp0cnVlLCJ1ayI6IjNhZWRhMWMxLTZhYjItNDUwNy04Mzg5LTEwZTJmNDMxYjM5MSIsInRzIjoxNzI5MzU5MDU0OTI3LCJiZiI6dHJ1ZSwicG4iOiJyYlByb2R1Y3RVcGNzIiwiZ2MiOnRydWUsImdDIjp0cnVlLCJncyI6Im5vbmUiLCJkYyI6MSwidHoiOiJBbWVyaWNhL05ld19Zb3JrIiwidXIiOm51bGx9&s=hWz37kbxi_u95EVNn2aoQhc5Aas',
18265
- },
18266
- {
18267
- event: exports.RMN_SPOT_EVENT.IMPRESSION,
18268
- url: 'https://dev.rmn.liquidcommerce.cloud/api/spots/event?e=eyJ2IjoiMS4xMiIsImF2IjozMDY1NzgzLCJhdCI6MTYzLCJidCI6MCwiY20iOjQ0MDE5MjQxOCwiY2giOjYzMTg0LCJjayI6e30sImNyIjo0ODE4NTUzNzUsImRpIjoiOWMxNGFhMGI3NWY4NDMxNTllMTAwYWQzNzA1NzQyYzMiLCJkaiI6MCwiaWkiOiIxZjU0MGM5NmQ1N2M0YmZjODFlZjRkNjhkMzFjNDVkOSIsImRtIjozLCJmYyI6NjU2NjgyNTQ5LCJmbCI6NjQzOTMxODIxLCJpcCI6IjM1LjIyMy4xOTguOTUiLCJudyI6MTE1MDAsInBjIjo1MDAwLCJvcCI6NTAwMCwibXAiOjUwMDAsImVjIjowLCJnbSI6MCwiZXAiOm51bGwsInByIjoyNDkzMTYsInJ0IjoxLCJycyI6NTAwLCJzYSI6IjU1Iiwic2IiOiJpLTA0MDI0ODg4ZDlkMWRjZWQ3Iiwic3AiOjI3MjI3Miwic3QiOjEyODcyOTYsInRyIjp0cnVlLCJ1ayI6IjNhZWRhMWMxLTZhYjItNDUwNy04Mzg5LTEwZTJmNDMxYjM5MSIsInRzIjoxNzI5MzU5MDU0OTI3LCJiZiI6dHJ1ZSwicG4iOiJyYlByb2R1Y3RVcGNzIiwiZ2MiOnRydWUsImdDIjp0cnVlLCJncyI6Im5vbmUiLCJkYyI6MSwidHoiOiJBbWVyaWNhL05ld19Zb3JrIiwiYmEiOjEsImZxIjowfQ&s=djoysjCimurf-5T11AlNAwwLSS8',
18269
- },
18270
- {
18271
- event: exports.RMN_SPOT_EVENT.PURCHASE,
18272
- url: 'https://dev.rmn.liquidcommerce.cloud/api/spots/event?e=eyJ2IjoiMS4xMiIsImF2IjozMDY1NzgzLCJhdCI6MTYzLCJidCI6MCwiY20iOjQ0MDE5MjQxOCwiY2giOjYzMTg0LCJjayI6e30sImNyIjo0ODE4NTUzNzUsImRpIjoiOWMxNGFhMGI3NWY4NDMxNTllMTAwYWQzNzA1NzQyYzMiLCJkaiI6MCwiaWkiOiIxZjU0MGM5NmQ1N2M0YmZjODFlZjRkNjhkMzFjNDVkOSIsImRtIjozLCJmYyI6NjU2NjgyNTQ5LCJmbCI6NjQzOTMxODIxLCJpcCI6IjM1LjIyMy4xOTguOTUiLCJudyI6MTE1MDAsInBjIjo1MDAwLCJvcCI6NTAwMCwibXAiOjUwMDAsImVjIjowLCJnbSI6MCwiZXAiOm51bGwsInByIjoyNDkzMTYsInJ0IjoxLCJycyI6NTAwLCJzYSI6IjU1Iiwic2IiOiJpLTA0MDI0ODg4ZDlkMWRjZWQ3Iiwic3AiOjI3MjI3Miwic3QiOjEyODcyOTYsInRyIjp0cnVlLCJ1ayI6IjNhZWRhMWMxLTZhYjItNDUwNy04Mzg5LTEwZTJmNDMxYjM5MSIsInRzIjoxNzI5MzU5MDU0OTI3LCJiZiI6dHJ1ZSwicG4iOiJyYlByb2R1Y3RVcGNzIiwiZ2MiOnRydWUsImdDIjp0cnVlLCJncyI6Im5vbmUiLCJkYyI6MSwidHoiOiJBbWVyaWNhL05ld19Zb3JrIiwiZXQiOjU5fQ&s=AAPAw-3SfZ0JMzjEGFSwt9L-2S4',
18273
- },
18274
- {
18275
- event: exports.RMN_SPOT_EVENT.ADD_TO_CART,
18276
- url: 'https://dev.rmn.liquidcommerce.cloud/api/spots/event?e=eyJ2IjoiMS4xMiIsImF2IjozMDY1NzgzLCJhdCI6MTYzLCJidCI6MCwiY20iOjQ0MDE5MjQxOCwiY2giOjYzMTg0LCJjayI6e30sImNyIjo0ODE4NTUzNzUsImRpIjoiOWMxNGFhMGI3NWY4NDMxNTllMTAwYWQzNzA1NzQyYzMiLCJkaiI6MCwiaWkiOiIxZjU0MGM5NmQ1N2M0YmZjODFlZjRkNjhkMzFjNDVkOSIsImRtIjozLCJmYyI6NjU2NjgyNTQ5LCJmbCI6NjQzOTMxODIxLCJpcCI6IjM1LjIyMy4xOTguOTUiLCJudyI6MTE1MDAsInBjIjo1MDAwLCJvcCI6NTAwMCwibXAiOjUwMDAsImVjIjowLCJnbSI6MCwiZXAiOm51bGwsInByIjoyNDkzMTYsInJ0IjoxLCJycyI6NTAwLCJzYSI6IjU1Iiwic2IiOiJpLTA0MDI0ODg4ZDlkMWRjZWQ3Iiwic3AiOjI3MjI3Miwic3QiOjEyODcyOTYsInRyIjp0cnVlLCJ1ayI6IjNhZWRhMWMxLTZhYjItNDUwNy04Mzg5LTEwZTJmNDMxYjM5MSIsInRzIjoxNzI5MzU5MDU0OTI3LCJiZiI6dHJ1ZSwicG4iOiJyYlByb2R1Y3RVcGNzIiwiZ2MiOnRydWUsImdDIjp0cnVlLCJncyI6Im5vbmUiLCJkYyI6MSwidHoiOiJBbWVyaWNhL05ld19Zb3JrIiwiZXQiOjYwfQ&s=uzQFcjgL7m9XqUG8FvTPVN5YkZY',
18277
- },
18278
- {
18279
- event: exports.RMN_SPOT_EVENT.ADD_TO_WISHLIST,
18280
- url: 'https://dev.rmn.liquidcommerce.cloud/api/spots/event?e=eyJ2IjoiMS4xMiIsImF2IjozMDY1NzgzLCJhdCI6MTYzLCJidCI6MCwiY20iOjQ0MDE5MjQxOCwiY2giOjYzMTg0LCJjayI6e30sImNyIjo0ODE4NTUzNzUsImRpIjoiOWMxNGFhMGI3NWY4NDMxNTllMTAwYWQzNzA1NzQyYzMiLCJkaiI6MCwiaWkiOiIxZjU0MGM5NmQ1N2M0YmZjODFlZjRkNjhkMzFjNDVkOSIsImRtIjozLCJmYyI6NjU2NjgyNTQ5LCJmbCI6NjQzOTMxODIxLCJpcCI6IjM1LjIyMy4xOTguOTUiLCJudyI6MTE1MDAsInBjIjo1MDAwLCJvcCI6NTAwMCwibXAiOjUwMDAsImVjIjowLCJnbSI6MCwiZXAiOm51bGwsInByIjoyNDkzMTYsInJ0IjoxLCJycyI6NTAwLCJzYSI6IjU1Iiwic2IiOiJpLTA0MDI0ODg4ZDlkMWRjZWQ3Iiwic3AiOjI3MjI3Miwic3QiOjEyODcyOTYsInRyIjp0cnVlLCJ1ayI6IjNhZWRhMWMxLTZhYjItNDUwNy04Mzg5LTEwZTJmNDMxYjM5MSIsInRzIjoxNzI5MzU5MDU0OTI3LCJiZiI6dHJ1ZSwicG4iOiJyYlByb2R1Y3RVcGNzIiwiZ2MiOnRydWUsImdDIjp0cnVlLCJncyI6Im5vbmUiLCJkYyI6MSwidHoiOiJBbWVyaWNhL05ld19Zb3JrIiwiZXQiOjYzfQ&s=m3ISU_iIy-OFtXrTKpI6cJAEC0k',
18281
- },
18282
- {
18283
- event: exports.RMN_SPOT_EVENT.BUY_NOW,
18284
- url: 'https://dev.rmn.liquidcommerce.cloud/api/spots/event?e=eyJ2IjoiMS4xMiIsImF2IjozMDY1NzgzLCJhdCI6MTYzLCJidCI6MCwiY20iOjQ0MDE5MjQxOCwiY2giOjYzMTg0LCJjayI6e30sImNyIjo0ODE4NTUzNzUsImRpIjoiOWMxNGFhMGI3NWY4NDMxNTllMTAwYWQzNzA1NzQyYzMiLCJkaiI6MCwiaWkiOiIxZjU0MGM5NmQ1N2M0YmZjODFlZjRkNjhkMzFjNDVkOSIsImRtIjozLCJmYyI6NjU2NjgyNTQ5LCJmbCI6NjQzOTMxODIxLCJpcCI6IjM1LjIyMy4xOTguOTUiLCJudyI6MTE1MDAsInBjIjo1MDAwLCJvcCI6NTAwMCwibXAiOjUwMDAsImVjIjowLCJnbSI6MCwiZXAiOm51bGwsInByIjoyNDkzMTYsInJ0IjoxLCJycyI6NTAwLCJzYSI6IjU1Iiwic2IiOiJpLTA0MDI0ODg4ZDlkMWRjZWQ3Iiwic3AiOjI3MjI3Miwic3QiOjEyODcyOTYsInRyIjp0cnVlLCJ1ayI6IjNhZWRhMWMxLTZhYjItNDUwNy04Mzg5LTEwZTJmNDMxYjM5MSIsInRzIjoxNzI5MzU5MDU0OTI3LCJiZiI6dHJ1ZSwicG4iOiJyYlByb2R1Y3RVcGNzIiwiZ2MiOnRydWUsImdDIjp0cnVlLCJncyI6Im5vbmUiLCJkYyI6MSwidHoiOiJBbWVyaWNhL05ld19Zb3JrIiwiZXQiOjY5fQ&s=l6MOscQC-q-FkC2Ksd7w6jjySCQ',
18285
- },
18286
- ];
18287
- const RB_SPOTS_SELECTION_EXAMPLE = {
18288
- rbHomepageHeroFullImage: [
18289
- {
18290
- id: '111111_111111',
18291
- spot: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_FULL_IMAGE,
18292
- variant: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_FULL_IMAGE,
18293
- width: 1140,
18294
- height: 640,
18295
- header: 'Artisanal Craft Beer Collection',
18296
- description: 'Discover our curated selection of small-batch, flavor-packed craft beers.',
18297
- ctaText: 'Explore the Collection',
18298
- textColor: '#ffffff',
18299
- ctaTextColor: '#ffffff',
18300
- primaryImage: 'https://placehold.co/1140x640/png?text=Craft+Beer+Collection',
18301
- mobilePrimaryImage: 'https://placehold.co/640x640/png?text=Mobile+Craft+Beer',
18302
- events: SPOT_EVENTS_EXAMPLE,
18303
- },
18304
- {
18305
- id: '222222_222222',
18306
- spot: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_FULL_IMAGE,
18307
- variant: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_FULL_IMAGE,
18308
- width: 1140,
18309
- height: 640,
18310
- header: 'Summer Wine Spectacular',
18311
- description: 'Refresh your palate with our handpicked selection of crisp, summer wines.',
18312
- ctaText: 'Shop Summer Wines',
18313
- textColor: '#000000',
18314
- ctaTextColor: '#ffffff',
18315
- primaryImage: 'https://placehold.co/1140x640/png?text=Summer+Wines',
18316
- mobilePrimaryImage: 'https://placehold.co/640x640/png?text=Mobile+Summer+Wines',
18317
- events: SPOT_EVENTS_EXAMPLE,
18318
- },
18319
- ],
18320
- rbHomepageHeroTwoTile: [
18321
- {
18322
- id: '333333_333333',
18323
- spot: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_TWO_TILE,
18324
- variant: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_TWO_TILE,
18325
- width: 1140,
18326
- height: 640,
18327
- header: 'Whiskey Wonderland',
18328
- description: 'Embark on a journey through our premium whiskey selection.',
18329
- ctaText: 'Discover Whiskeys',
18330
- textColor: '#ffffff',
18331
- backgroundColor: '#2c1a05',
18332
- ctaTextColor: '#2c1a05',
18333
- primaryImage: 'https://placehold.co/1140x640/png?text=Whiskey+Collection',
18334
- mobilePrimaryImage: 'https://placehold.co/640x640/png?text=Mobile+Whiskey',
18335
- events: SPOT_EVENTS_EXAMPLE,
18336
- },
18337
- ],
18338
- rbHomepageHeroThreeTile: [
18339
- {
18340
- id: '444444_444444',
18341
- spot: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_THREE_TILE,
18342
- variant: exports.RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_THREE_TILE,
18343
- width: 1140,
18344
- height: 640,
18345
- header: 'Cocktail Essentials',
18346
- description: 'Stock your bar with premium spirits and mixers for the perfect cocktail.',
18347
- ctaText: 'Build Your Bar',
18348
- textColor: '#ffffff',
18349
- backgroundColor: '#1a3c4d',
18350
- ctaTextColor: '#1a3c4d',
18351
- primaryImage: 'https://placehold.co/1140x640/png?text=Cocktail+Spirits',
18352
- secondaryImage: 'https://placehold.co/1140x640/png?text=Cocktail+Mixers',
18353
- mobilePrimaryImage: 'https://placehold.co/640x640/png?text=Mobile+Cocktail+Kit',
18354
- mobileSecondaryImage: 'https://placehold.co/640x320/png?text=Mobile+Cocktail+Mixers',
18355
- events: SPOT_EVENTS_EXAMPLE,
18356
- },
18357
- ],
18358
- rbLargeCategoryImageTout: [
18359
- {
18360
- id: '555555_555555',
18361
- spot: exports.RMN_SPOT_TYPE.RB_LARGE_CATEGORY_IMAGE_TOUT,
18362
- variant: exports.RMN_SPOT_TYPE.RB_LARGE_CATEGORY_IMAGE_TOUT,
18363
- width: 468,
18364
- height: 410,
18365
- header: 'Rare & Limited Edition',
18366
- description: 'Discover our collection of hard-to-find and limited release spirits.',
18367
- textColor: '#ffffff',
18368
- ctaTextColor: '#ffffff',
18369
- primaryImage: 'https://placehold.co/468x410/png?text=Rare+Spirits',
18370
- mobilePrimaryImage: 'https://placehold.co/468x410/png?text=Mobile+Rare+Spirits',
18371
- ctaText: 'Shop Rare Spirits',
18372
- events: SPOT_EVENTS_EXAMPLE,
18373
- },
18374
- ],
18375
- rbSmallDiscoverTout: [
18376
- {
18377
- id: '666666_666666',
18378
- spot: exports.RMN_SPOT_TYPE.RB_SMALL_DISCOVER_TOUT,
18379
- variant: exports.RMN_SPOT_TYPE.RB_SMALL_DISCOVER_TOUT,
18380
- width: 224,
18381
- height: 378,
18382
- header: 'Château Margaux 2015 Bordeaux',
18383
- textColor: '#ffffff',
18384
- primaryImage: 'https://placehold.co/224x378/png?text=Château+Margaux',
18385
- mobilePrimaryImage: 'https://placehold.co/224x378/png?text=Mobile+Château+Margaux',
18386
- events: SPOT_EVENTS_EXAMPLE,
18387
- },
18388
- ],
18389
- rbSmallCategoryImageTout: [
18390
- {
18391
- id: '777777_777777',
18392
- spot: exports.RMN_SPOT_TYPE.RB_SMALL_CATEGORY_IMAGE_TOUT,
18393
- variant: exports.RMN_SPOT_TYPE.RB_SMALL_CATEGORY_IMAGE_TOUT,
18394
- width: 224,
18395
- height: 410,
18396
- header: 'Japanese Sake',
18397
- textColor: '#ffffff',
18398
- primaryImage: 'https://placehold.co/224x410/png?text=Japanese+Sake',
18399
- mobilePrimaryImage: 'https://placehold.co/224x410/png?text=Mobile+Japanese+Sake',
18400
- events: SPOT_EVENTS_EXAMPLE,
18401
- },
18402
- ],
18403
- rbCollectionBannerWithoutTextBlock: [
18404
- {
18405
- id: '888888_888888',
18406
- spot: exports.RMN_SPOT_TYPE.RB_COLLECTION_BANNER_WITHOUT_TEXT_BLOCK,
18407
- variant: exports.RMN_SPOT_TYPE.RB_COLLECTION_BANNER_WITHOUT_TEXT_BLOCK,
18408
- width: 887,
18409
- height: 344,
18410
- primaryImage: 'https://placehold.co/887x344/png?text=Summer+Cocktails',
18411
- mobilePrimaryImage: 'https://placehold.co/887x344/png?text=Mobile+Summer+Cocktails',
18412
- events: SPOT_EVENTS_EXAMPLE,
18413
- },
18414
- ],
18415
- rbNavigationBanner: [
18416
- {
18417
- id: '999999_999999',
18418
- spot: exports.RMN_SPOT_TYPE.RB_NAVIGATION_BANNER,
18419
- variant: exports.RMN_SPOT_TYPE.RB_NAVIGATION_BANNER,
18420
- width: 440,
18421
- height: 220,
18422
- header: 'Explore Tequilas',
18423
- textColor: '#ffffff',
18424
- primaryImage: 'https://placehold.co/440x220/png?text=Tequila+Collection',
18425
- mobilePrimaryImage: 'https://placehold.co/440x220/png?text=Mobile+Tequila+Collection',
18426
- events: SPOT_EVENTS_EXAMPLE,
18427
- },
18428
- ],
18429
- };
18430
-
18431
19202
  class LiquidCommerceRmnClient {
18432
19203
  constructor(auth) {
18433
19204
  this.selectionService = SelectionService.getInstance(auth);
@@ -18441,7 +19212,7 @@ class LiquidCommerceRmnClient {
18441
19212
  *
18442
19213
  * @param {ISpotSelectionParams} params - Spots selection parameters.
18443
19214
  *
18444
- * @return {Promise<ISpots | {error : string}>} - The spots response object.
19215
+ * @return {Promise<ISpots | { error : string }>} - The spots response object.
18445
19216
  */
18446
19217
  async spotSelection(params) {
18447
19218
  return this.selectionService.spotSelection(params);
@@ -18455,13 +19226,19 @@ class LiquidCommerceRmnClient {
18455
19226
  */
18456
19227
  async injectSpotElement(params) {
18457
19228
  var _a;
19229
+ if (typeof window === 'undefined' || typeof document === 'undefined') {
19230
+ console.warn('LiquidCommerce Rmn Sdk: Methods which create elements are only available in browser environments.');
19231
+ return;
19232
+ }
18458
19233
  const config = params.config;
18459
19234
  let inject = params.inject;
18460
19235
  if (!inject.length) {
19236
+ // Handle no spots error state
18461
19237
  this.eventService.handleSpotState('all', {
18462
19238
  state: {
18463
19239
  error: 'No spot elements provided for injection.',
18464
19240
  loading: false,
19241
+ mounted: false,
18465
19242
  },
18466
19243
  });
18467
19244
  return;
@@ -18480,9 +19257,12 @@ class LiquidCommerceRmnClient {
18480
19257
  // const response = await this.useSpotSelectionExample(inject);
18481
19258
  // Handle the response
18482
19259
  if (typeof response === 'object' && 'error' in response) {
19260
+ // Handle request error state
18483
19261
  this.eventService.handleSpotState('all', {
18484
19262
  state: {
18485
19263
  error: response.error,
19264
+ mounted: false,
19265
+ loading: false,
18486
19266
  },
18487
19267
  });
18488
19268
  return;
@@ -18491,9 +19271,11 @@ class LiquidCommerceRmnClient {
18491
19271
  const itemConfig = (_a = item.config) !== null && _a !== void 0 ? _a : config;
18492
19272
  const spots = response[item.placementId];
18493
19273
  if (!(spots === null || spots === void 0 ? void 0 : spots.length)) {
19274
+ // Handle no spots found error state
18494
19275
  this.eventService.handleSpotState(item.placementId, {
18495
19276
  state: {
18496
19277
  error: `No spots found for type "${item.spotType}".`,
19278
+ mounted: false,
18497
19279
  loading: false,
18498
19280
  },
18499
19281
  });
@@ -18502,9 +19284,11 @@ class LiquidCommerceRmnClient {
18502
19284
  const placementId = item.placementId.replace('#', '');
18503
19285
  const placement = document.getElementById(placementId);
18504
19286
  if (!placement) {
19287
+ // Handle placement not found error state
18505
19288
  this.eventService.handleSpotState(item.placementId, {
18506
19289
  state: {
18507
19290
  error: `Placement not found for id "${placementId}".`,
19291
+ mounted: false,
18508
19292
  loading: false,
18509
19293
  },
18510
19294
  });
@@ -18515,16 +19299,18 @@ class LiquidCommerceRmnClient {
18515
19299
  placement.removeAttribute('class');
18516
19300
  Object.assign(placement.style, {
18517
19301
  width: '100%',
18518
- height: 'auto',
19302
+ height: '100%',
18519
19303
  display: 'flex',
18520
19304
  justifyContent: 'center',
18521
19305
  });
19306
+ // Handle single spot
18522
19307
  if (spots.length === 1) {
18523
19308
  const isInjected = this.injectOneSpotElement(item, placement, spots[0], itemConfig);
18524
19309
  if (!isInjected) {
18525
19310
  continue;
18526
19311
  }
18527
19312
  }
19313
+ // Handle multiple spots (carousel)
18528
19314
  if (spots.length > 1) {
18529
19315
  const isInjected = this.injectCarouselSpotElement(placement, spots, itemConfig);
18530
19316
  if (!isInjected) {
@@ -18568,18 +19354,24 @@ class LiquidCommerceRmnClient {
18568
19354
  const carouselSlides = [];
18569
19355
  for (const spotItem of spots) {
18570
19356
  this.eventService.handleSpotState(placement.id, {
19357
+ identifier: {
19358
+ placementId: placement.id,
19359
+ spotType: spotItem.spot,
19360
+ spotId: spotItem.id,
19361
+ },
18571
19362
  displayConfig: {
19363
+ isSingleItem: false,
18572
19364
  isCarousel: true,
18573
19365
  isCarouselItem: true,
18574
- isSingleItem: false,
18575
19366
  },
18576
- }, false);
19367
+ });
18577
19368
  const spot = this.elementService.overrideSpotColors(spotItem, config === null || config === void 0 ? void 0 : config.colors);
18578
19369
  const content = SPOT_TEMPLATE_HTML_ELEMENT(spot, { overlay: config === null || config === void 0 ? void 0 : config.overlay });
18579
19370
  if (!content) {
18580
19371
  this.eventService.handleSpotState(placement.id, {
18581
19372
  state: {
18582
19373
  error: `Failed to inject carousel spot item element. Could not create element for type "${spot.spot}".`,
19374
+ mounted: false,
18583
19375
  loading: false,
18584
19376
  },
18585
19377
  });
@@ -18612,6 +19404,7 @@ class LiquidCommerceRmnClient {
18612
19404
  this.eventService.handleSpotState(placement.id, {
18613
19405
  state: {
18614
19406
  error: `Failed to inject spot carousel element. Could not create spot carousel element.`,
19407
+ mounted: false,
18615
19408
  loading: false,
18616
19409
  },
18617
19410
  });
@@ -18621,10 +19414,12 @@ class LiquidCommerceRmnClient {
18621
19414
  this.eventService.handleSpotState(placement.id, {
18622
19415
  dom: {
18623
19416
  spotElement: carouselElement,
19417
+ visibleOnViewport: false,
18624
19418
  },
18625
19419
  state: {
18626
19420
  mounted: true,
18627
19421
  loading: false,
19422
+ error: undefined,
18628
19423
  },
18629
19424
  });
18630
19425
  return true;
@@ -18641,18 +19436,25 @@ class LiquidCommerceRmnClient {
18641
19436
  */
18642
19437
  injectOneSpotElement(injectItem, placement, spot, config) {
18643
19438
  var _a;
18644
- const spotData = this.elementService.overrideSpotColors(spot, config === null || config === void 0 ? void 0 : config.colors);
18645
19439
  this.eventService.handleSpotState(injectItem.placementId, {
19440
+ identifier: {
19441
+ placementId: injectItem.placementId,
19442
+ spotType: injectItem.spotType,
19443
+ spotId: spot.id,
19444
+ },
18646
19445
  displayConfig: {
18647
19446
  isSingleItem: true,
19447
+ isCarousel: false,
19448
+ isCarouselItem: false,
18648
19449
  },
18649
- }, false);
18650
- // Create the spot template element
19450
+ });
19451
+ const spotData = this.elementService.overrideSpotColors(spot, config === null || config === void 0 ? void 0 : config.colors);
18651
19452
  const content = SPOT_TEMPLATE_HTML_ELEMENT(spotData, { overlay: config === null || config === void 0 ? void 0 : config.overlay });
18652
19453
  if (!content) {
18653
19454
  this.eventService.handleSpotState(injectItem.placementId, {
18654
19455
  state: {
18655
19456
  error: `Failed to inject spot element. Could not create element for type "${injectItem.spotType}".`,
19457
+ mounted: false,
18656
19458
  loading: false,
18657
19459
  },
18658
19460
  });
@@ -18673,6 +19475,7 @@ class LiquidCommerceRmnClient {
18673
19475
  this.eventService.handleSpotState(injectItem.placementId, {
18674
19476
  state: {
18675
19477
  error: `Failed to inject spot element. Could not create element for type "${injectItem.spotType}".`,
19478
+ mounted: false,
18676
19479
  loading: false,
18677
19480
  },
18678
19481
  });
@@ -18687,10 +19490,12 @@ class LiquidCommerceRmnClient {
18687
19490
  this.eventService.handleSpotState(injectItem.placementId, {
18688
19491
  dom: {
18689
19492
  spotElement,
19493
+ visibleOnViewport: false,
18690
19494
  },
18691
19495
  state: {
18692
19496
  mounted: true,
18693
19497
  loading: false,
19498
+ error: undefined,
18694
19499
  },
18695
19500
  });
18696
19501
  return true;
@@ -18711,6 +19516,8 @@ class LiquidCommerceRmnClient {
18711
19516
  this.eventService.handleSpotState(item.placementId, {
18712
19517
  state: {
18713
19518
  error: `Duplicate placement id (${item.placementId}) found. Please provide a unique placement id for each spot element.`,
19519
+ mounted: false,
19520
+ loading: false,
18714
19521
  },
18715
19522
  });
18716
19523
  return false;
@@ -18726,6 +19533,8 @@ class LiquidCommerceRmnClient {
18726
19533
  this.eventService.handleSpotState(item.placementId, {
18727
19534
  state: {
18728
19535
  error: `Invalid spot type (${item.spotType}) found. Please provide a valid spot type for each spot element.`,
19536
+ mounted: false,
19537
+ loading: false,
18729
19538
  },
18730
19539
  });
18731
19540
  continue;
@@ -18734,6 +19543,7 @@ class LiquidCommerceRmnClient {
18734
19543
  }
18735
19544
  return newInject;
18736
19545
  }
19546
+ // Initialize spots with loading state and identifiers
18737
19547
  updateSpotsState(inject) {
18738
19548
  for (const item of inject) {
18739
19549
  this.eventService.handleSpotState(item.placementId, {
@@ -18743,6 +19553,8 @@ class LiquidCommerceRmnClient {
18743
19553
  },
18744
19554
  state: {
18745
19555
  loading: true,
19556
+ mounted: false,
19557
+ error: undefined,
18746
19558
  },
18747
19559
  });
18748
19560
  }