@liquidcommercedev/rmn-sdk 1.5.0-beta.14 → 1.5.0-beta.16

Sign up to get free protection for your applications and to get access to all the features.
package/dist/index.esm.js CHANGED
@@ -78,574 +78,6 @@ var RMN_ENV;
78
78
  RMN_ENV["PRODUCTION"] = "production";
79
79
  })(RMN_ENV || (RMN_ENV = {}));
80
80
 
81
- const SPOT_EVENTS_EXAMPLE = [
82
- {
83
- event: RMN_SPOT_EVENT.CLICK,
84
- url: 'https://dev.rmn.liquidcommerce.cloud/api/spots/event?e=eyJ2IjoiMS4xMiIsImF2IjozMDY1NzgzLCJhdCI6MTYzLCJidCI6MCwiY20iOjQ0MDE5MjQxOCwiY2giOjYzMTg0LCJjayI6e30sImNyIjo0ODE4NTUzNzUsImRpIjoiOWMxNGFhMGI3NWY4NDMxNTllMTAwYWQzNzA1NzQyYzMiLCJkaiI6MCwiaWkiOiIxZjU0MGM5NmQ1N2M0YmZjODFlZjRkNjhkMzFjNDVkOSIsImRtIjozLCJmYyI6NjU2NjgyNTQ5LCJmbCI6NjQzOTMxODIxLCJpcCI6IjM1LjIyMy4xOTguOTUiLCJudyI6MTE1MDAsInBjIjo1MDAwLCJvcCI6NTAwMCwibXAiOjUwMDAsImVjIjowLCJnbSI6MCwiZXAiOm51bGwsInByIjoyNDkzMTYsInJ0IjoxLCJycyI6NTAwLCJzYSI6IjU1Iiwic2IiOiJpLTA0MDI0ODg4ZDlkMWRjZWQ3Iiwic3AiOjI3MjI3Miwic3QiOjEyODcyOTYsInRyIjp0cnVlLCJ1ayI6IjNhZWRhMWMxLTZhYjItNDUwNy04Mzg5LTEwZTJmNDMxYjM5MSIsInRzIjoxNzI5MzU5MDU0OTI3LCJiZiI6dHJ1ZSwicG4iOiJyYlByb2R1Y3RVcGNzIiwiZ2MiOnRydWUsImdDIjp0cnVlLCJncyI6Im5vbmUiLCJkYyI6MSwidHoiOiJBbWVyaWNhL05ld19Zb3JrIiwidXIiOm51bGx9&s=hWz37kbxi_u95EVNn2aoQhc5Aas',
85
- },
86
- {
87
- event: RMN_SPOT_EVENT.IMPRESSION,
88
- url: 'https://dev.rmn.liquidcommerce.cloud/api/spots/event?e=eyJ2IjoiMS4xMiIsImF2IjozMDY1NzgzLCJhdCI6MTYzLCJidCI6MCwiY20iOjQ0MDE5MjQxOCwiY2giOjYzMTg0LCJjayI6e30sImNyIjo0ODE4NTUzNzUsImRpIjoiOWMxNGFhMGI3NWY4NDMxNTllMTAwYWQzNzA1NzQyYzMiLCJkaiI6MCwiaWkiOiIxZjU0MGM5NmQ1N2M0YmZjODFlZjRkNjhkMzFjNDVkOSIsImRtIjozLCJmYyI6NjU2NjgyNTQ5LCJmbCI6NjQzOTMxODIxLCJpcCI6IjM1LjIyMy4xOTguOTUiLCJudyI6MTE1MDAsInBjIjo1MDAwLCJvcCI6NTAwMCwibXAiOjUwMDAsImVjIjowLCJnbSI6MCwiZXAiOm51bGwsInByIjoyNDkzMTYsInJ0IjoxLCJycyI6NTAwLCJzYSI6IjU1Iiwic2IiOiJpLTA0MDI0ODg4ZDlkMWRjZWQ3Iiwic3AiOjI3MjI3Miwic3QiOjEyODcyOTYsInRyIjp0cnVlLCJ1ayI6IjNhZWRhMWMxLTZhYjItNDUwNy04Mzg5LTEwZTJmNDMxYjM5MSIsInRzIjoxNzI5MzU5MDU0OTI3LCJiZiI6dHJ1ZSwicG4iOiJyYlByb2R1Y3RVcGNzIiwiZ2MiOnRydWUsImdDIjp0cnVlLCJncyI6Im5vbmUiLCJkYyI6MSwidHoiOiJBbWVyaWNhL05ld19Zb3JrIiwiYmEiOjEsImZxIjowfQ&s=djoysjCimurf-5T11AlNAwwLSS8',
89
- },
90
- {
91
- event: RMN_SPOT_EVENT.PURCHASE,
92
- url: 'https://dev.rmn.liquidcommerce.cloud/api/spots/event?e=eyJ2IjoiMS4xMiIsImF2IjozMDY1NzgzLCJhdCI6MTYzLCJidCI6MCwiY20iOjQ0MDE5MjQxOCwiY2giOjYzMTg0LCJjayI6e30sImNyIjo0ODE4NTUzNzUsImRpIjoiOWMxNGFhMGI3NWY4NDMxNTllMTAwYWQzNzA1NzQyYzMiLCJkaiI6MCwiaWkiOiIxZjU0MGM5NmQ1N2M0YmZjODFlZjRkNjhkMzFjNDVkOSIsImRtIjozLCJmYyI6NjU2NjgyNTQ5LCJmbCI6NjQzOTMxODIxLCJpcCI6IjM1LjIyMy4xOTguOTUiLCJudyI6MTE1MDAsInBjIjo1MDAwLCJvcCI6NTAwMCwibXAiOjUwMDAsImVjIjowLCJnbSI6MCwiZXAiOm51bGwsInByIjoyNDkzMTYsInJ0IjoxLCJycyI6NTAwLCJzYSI6IjU1Iiwic2IiOiJpLTA0MDI0ODg4ZDlkMWRjZWQ3Iiwic3AiOjI3MjI3Miwic3QiOjEyODcyOTYsInRyIjp0cnVlLCJ1ayI6IjNhZWRhMWMxLTZhYjItNDUwNy04Mzg5LTEwZTJmNDMxYjM5MSIsInRzIjoxNzI5MzU5MDU0OTI3LCJiZiI6dHJ1ZSwicG4iOiJyYlByb2R1Y3RVcGNzIiwiZ2MiOnRydWUsImdDIjp0cnVlLCJncyI6Im5vbmUiLCJkYyI6MSwidHoiOiJBbWVyaWNhL05ld19Zb3JrIiwiZXQiOjU5fQ&s=AAPAw-3SfZ0JMzjEGFSwt9L-2S4',
93
- },
94
- {
95
- event: RMN_SPOT_EVENT.ADD_TO_CART,
96
- url: 'https://dev.rmn.liquidcommerce.cloud/api/spots/event?e=eyJ2IjoiMS4xMiIsImF2IjozMDY1NzgzLCJhdCI6MTYzLCJidCI6MCwiY20iOjQ0MDE5MjQxOCwiY2giOjYzMTg0LCJjayI6e30sImNyIjo0ODE4NTUzNzUsImRpIjoiOWMxNGFhMGI3NWY4NDMxNTllMTAwYWQzNzA1NzQyYzMiLCJkaiI6MCwiaWkiOiIxZjU0MGM5NmQ1N2M0YmZjODFlZjRkNjhkMzFjNDVkOSIsImRtIjozLCJmYyI6NjU2NjgyNTQ5LCJmbCI6NjQzOTMxODIxLCJpcCI6IjM1LjIyMy4xOTguOTUiLCJudyI6MTE1MDAsInBjIjo1MDAwLCJvcCI6NTAwMCwibXAiOjUwMDAsImVjIjowLCJnbSI6MCwiZXAiOm51bGwsInByIjoyNDkzMTYsInJ0IjoxLCJycyI6NTAwLCJzYSI6IjU1Iiwic2IiOiJpLTA0MDI0ODg4ZDlkMWRjZWQ3Iiwic3AiOjI3MjI3Miwic3QiOjEyODcyOTYsInRyIjp0cnVlLCJ1ayI6IjNhZWRhMWMxLTZhYjItNDUwNy04Mzg5LTEwZTJmNDMxYjM5MSIsInRzIjoxNzI5MzU5MDU0OTI3LCJiZiI6dHJ1ZSwicG4iOiJyYlByb2R1Y3RVcGNzIiwiZ2MiOnRydWUsImdDIjp0cnVlLCJncyI6Im5vbmUiLCJkYyI6MSwidHoiOiJBbWVyaWNhL05ld19Zb3JrIiwiZXQiOjYwfQ&s=uzQFcjgL7m9XqUG8FvTPVN5YkZY',
97
- },
98
- {
99
- event: RMN_SPOT_EVENT.ADD_TO_WISHLIST,
100
- url: 'https://dev.rmn.liquidcommerce.cloud/api/spots/event?e=eyJ2IjoiMS4xMiIsImF2IjozMDY1NzgzLCJhdCI6MTYzLCJidCI6MCwiY20iOjQ0MDE5MjQxOCwiY2giOjYzMTg0LCJjayI6e30sImNyIjo0ODE4NTUzNzUsImRpIjoiOWMxNGFhMGI3NWY4NDMxNTllMTAwYWQzNzA1NzQyYzMiLCJkaiI6MCwiaWkiOiIxZjU0MGM5NmQ1N2M0YmZjODFlZjRkNjhkMzFjNDVkOSIsImRtIjozLCJmYyI6NjU2NjgyNTQ5LCJmbCI6NjQzOTMxODIxLCJpcCI6IjM1LjIyMy4xOTguOTUiLCJudyI6MTE1MDAsInBjIjo1MDAwLCJvcCI6NTAwMCwibXAiOjUwMDAsImVjIjowLCJnbSI6MCwiZXAiOm51bGwsInByIjoyNDkzMTYsInJ0IjoxLCJycyI6NTAwLCJzYSI6IjU1Iiwic2IiOiJpLTA0MDI0ODg4ZDlkMWRjZWQ3Iiwic3AiOjI3MjI3Miwic3QiOjEyODcyOTYsInRyIjp0cnVlLCJ1ayI6IjNhZWRhMWMxLTZhYjItNDUwNy04Mzg5LTEwZTJmNDMxYjM5MSIsInRzIjoxNzI5MzU5MDU0OTI3LCJiZiI6dHJ1ZSwicG4iOiJyYlByb2R1Y3RVcGNzIiwiZ2MiOnRydWUsImdDIjp0cnVlLCJncyI6Im5vbmUiLCJkYyI6MSwidHoiOiJBbWVyaWNhL05ld19Zb3JrIiwiZXQiOjYzfQ&s=m3ISU_iIy-OFtXrTKpI6cJAEC0k',
101
- },
102
- {
103
- event: RMN_SPOT_EVENT.BUY_NOW,
104
- url: 'https://dev.rmn.liquidcommerce.cloud/api/spots/event?e=eyJ2IjoiMS4xMiIsImF2IjozMDY1NzgzLCJhdCI6MTYzLCJidCI6MCwiY20iOjQ0MDE5MjQxOCwiY2giOjYzMTg0LCJjayI6e30sImNyIjo0ODE4NTUzNzUsImRpIjoiOWMxNGFhMGI3NWY4NDMxNTllMTAwYWQzNzA1NzQyYzMiLCJkaiI6MCwiaWkiOiIxZjU0MGM5NmQ1N2M0YmZjODFlZjRkNjhkMzFjNDVkOSIsImRtIjozLCJmYyI6NjU2NjgyNTQ5LCJmbCI6NjQzOTMxODIxLCJpcCI6IjM1LjIyMy4xOTguOTUiLCJudyI6MTE1MDAsInBjIjo1MDAwLCJvcCI6NTAwMCwibXAiOjUwMDAsImVjIjowLCJnbSI6MCwiZXAiOm51bGwsInByIjoyNDkzMTYsInJ0IjoxLCJycyI6NTAwLCJzYSI6IjU1Iiwic2IiOiJpLTA0MDI0ODg4ZDlkMWRjZWQ3Iiwic3AiOjI3MjI3Miwic3QiOjEyODcyOTYsInRyIjp0cnVlLCJ1ayI6IjNhZWRhMWMxLTZhYjItNDUwNy04Mzg5LTEwZTJmNDMxYjM5MSIsInRzIjoxNzI5MzU5MDU0OTI3LCJiZiI6dHJ1ZSwicG4iOiJyYlByb2R1Y3RVcGNzIiwiZ2MiOnRydWUsImdDIjp0cnVlLCJncyI6Im5vbmUiLCJkYyI6MSwidHoiOiJBbWVyaWNhL05ld19Zb3JrIiwiZXQiOjY5fQ&s=l6MOscQC-q-FkC2Ksd7w6jjySCQ',
105
- },
106
- ];
107
- const RB_SPOTS_SELECTION_EXAMPLE = {
108
- rbHomepageHero: [
109
- {
110
- id: 'abc123',
111
- spot: RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_FULL_IMAGE,
112
- variant: RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_FULL_IMAGE,
113
- width: 1140,
114
- height: 640,
115
- header: 'Premium Wine Collection',
116
- description: 'Discover our exclusive selection of vintage wines',
117
- ctaText: 'Shop Wines',
118
- textColor: '#ffffff',
119
- ctaTextColor: '#ffffff',
120
- primaryImage: 'https://placehold.co/1140x640/png?text=Wine+Collection',
121
- mobilePrimaryImage: 'https://placehold.co/640x640/png?text=Mobile+Wine',
122
- events: SPOT_EVENTS_EXAMPLE,
123
- productIds: [1, 2, 3],
124
- },
125
- {
126
- id: 'jkl012',
127
- spot: RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_TWO_TILE,
128
- variant: RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_TWO_TILE,
129
- width: 1140,
130
- height: 640,
131
- header: 'Whiskey Collection',
132
- description: 'Premium whiskeys from around the world',
133
- ctaText: 'Shop Whiskeys',
134
- textColor: '#ffffff',
135
- backgroundColor: '#2c1810',
136
- ctaTextColor: '#2c1810',
137
- primaryImage: 'https://placehold.co/1140x640/png?text=Whiskey',
138
- mobilePrimaryImage: 'https://placehold.co/640x640/png?text=Mobile+Whiskey',
139
- events: SPOT_EVENTS_EXAMPLE,
140
- productIds: [10, 11],
141
- },
142
- {
143
- id: 'stu901',
144
- spot: RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_THREE_TILE,
145
- variant: RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_THREE_TILE,
146
- width: 1140,
147
- height: 640,
148
- header: 'Summer Cocktails',
149
- description: 'Essential spirits for summer mixing',
150
- ctaText: 'Mix It Up',
151
- textColor: '#ffffff',
152
- backgroundColor: '#4d3a0a',
153
- ctaTextColor: '#4d3a0a',
154
- primaryImage: 'https://placehold.co/1140x640/png?text=Cocktails',
155
- secondaryImage: 'https://placehold.co/1140x640/png?text=Mixers',
156
- mobilePrimaryImage: 'https://placehold.co/640x640/png?text=Mobile+Cocktails',
157
- mobileSecondaryImage: 'https://placehold.co/640x320/png?text=Mobile+Mixers',
158
- events: SPOT_EVENTS_EXAMPLE,
159
- productIds: [16, 17, 18],
160
- },
161
- {
162
- id: 'def456',
163
- spot: RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_FULL_IMAGE,
164
- variant: RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_FULL_IMAGE,
165
- width: 1140,
166
- height: 640,
167
- header: 'Craft Beer Festival',
168
- description: 'Local breweries and exclusive releases',
169
- ctaText: 'Explore Beers',
170
- textColor: '#ffffff',
171
- ctaTextColor: '#ffffff',
172
- primaryImage: 'https://placehold.co/1140x640/png?text=Beer+Festival',
173
- mobilePrimaryImage: 'https://placehold.co/640x640/png?text=Mobile+Beer',
174
- events: SPOT_EVENTS_EXAMPLE,
175
- productIds: [4, 5, 6],
176
- },
177
- {
178
- id: 'mno345',
179
- spot: RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_TWO_TILE,
180
- variant: RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_TWO_TILE,
181
- width: 1140,
182
- height: 640,
183
- header: 'Champagne Selection',
184
- description: 'Finest champagnes for every occasion',
185
- ctaText: 'View Champagnes',
186
- textColor: '#ffffff',
187
- backgroundColor: '#1a1a1a',
188
- ctaTextColor: '#1a1a1a',
189
- primaryImage: 'https://placehold.co/1140x640/png?text=Champagne',
190
- mobilePrimaryImage: 'https://placehold.co/640x640/png?text=Mobile+Champagne',
191
- events: SPOT_EVENTS_EXAMPLE,
192
- productIds: [12, 13],
193
- },
194
- {
195
- id: 'vwx234',
196
- spot: RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_THREE_TILE,
197
- variant: RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_THREE_TILE,
198
- width: 1140,
199
- height: 640,
200
- header: 'Wine Regions',
201
- description: 'Explore wines from top regions',
202
- ctaText: 'Tour Regions',
203
- textColor: '#ffffff',
204
- backgroundColor: '#4d0a0a',
205
- ctaTextColor: '#4d0a0a',
206
- primaryImage: 'https://placehold.co/1140x640/png?text=Wine+Regions',
207
- secondaryImage: 'https://placehold.co/1140x640/png?text=Vineyards',
208
- mobilePrimaryImage: 'https://placehold.co/640x640/png?text=Mobile+Regions',
209
- mobileSecondaryImage: 'https://placehold.co/640x320/png?text=Mobile+Vineyards',
210
- events: SPOT_EVENTS_EXAMPLE,
211
- productIds: [19, 20, 21],
212
- },
213
- {
214
- id: 'ghi789',
215
- spot: RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_FULL_IMAGE,
216
- variant: RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_FULL_IMAGE,
217
- width: 1140,
218
- height: 640,
219
- header: 'Rare Spirits',
220
- description: 'Limited edition spirits collection',
221
- ctaText: 'View Collection',
222
- textColor: '#ffffff',
223
- ctaTextColor: '#ffffff',
224
- primaryImage: 'https://placehold.co/1140x640/png?text=Rare+Spirits',
225
- mobilePrimaryImage: 'https://placehold.co/640x640/png?text=Mobile+Spirits',
226
- events: SPOT_EVENTS_EXAMPLE,
227
- productIds: [7, 8, 9],
228
- },
229
- {
230
- id: 'pqr678',
231
- spot: RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_TWO_TILE,
232
- variant: RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_TWO_TILE,
233
- width: 1140,
234
- height: 640,
235
- header: 'Gin Collection',
236
- description: 'Artisanal gins and botanicals',
237
- ctaText: 'Explore Gins',
238
- textColor: '#ffffff',
239
- backgroundColor: '#0a4d4d',
240
- ctaTextColor: '#0a4d4d',
241
- primaryImage: 'https://placehold.co/1140x640/png?text=Gin',
242
- mobilePrimaryImage: 'https://placehold.co/640x640/png?text=Mobile+Gin',
243
- events: SPOT_EVENTS_EXAMPLE,
244
- productIds: [14, 15],
245
- },
246
- {
247
- id: 'yz5678',
248
- spot: RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_THREE_TILE,
249
- variant: RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_THREE_TILE,
250
- width: 1140,
251
- height: 640,
252
- header: 'Tequila Collection',
253
- description: 'Premium tequilas and mezcals',
254
- ctaText: 'Shop Tequila',
255
- textColor: '#ffffff',
256
- backgroundColor: '#0a4d2b',
257
- ctaTextColor: '#0a4d2b',
258
- primaryImage: 'https://placehold.co/1140x640/png?text=Tequila',
259
- secondaryImage: 'https://placehold.co/1140x640/png?text=Mezcal',
260
- mobilePrimaryImage: 'https://placehold.co/640x640/png?text=Mobile+Tequila',
261
- mobileSecondaryImage: 'https://placehold.co/640x320/png?text=Mobile+Mezcal',
262
- events: SPOT_EVENTS_EXAMPLE,
263
- productIds: [22, 23, 24],
264
- },
265
- ],
266
- rbHomepageHeroFullImage: [
267
- {
268
- id: 'hjk567',
269
- spot: RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_FULL_IMAGE,
270
- variant: RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_FULL_IMAGE,
271
- width: 1140,
272
- height: 640,
273
- header: 'Holiday Gift Guide',
274
- description: 'Perfect spirits for every occasion',
275
- ctaText: 'Shop Gifts',
276
- textColor: '#ffffff',
277
- ctaTextColor: '#ffffff',
278
- primaryImage: 'https://placehold.co/1140x640/png?text=Gift+Guide',
279
- mobilePrimaryImage: 'https://placehold.co/640x640/png?text=Mobile+Gifts',
280
- events: SPOT_EVENTS_EXAMPLE,
281
- productIds: [25, 26],
282
- },
283
- {
284
- id: 'qwe890',
285
- spot: RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_FULL_IMAGE,
286
- variant: RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_FULL_IMAGE,
287
- width: 1140,
288
- height: 640,
289
- header: 'Summer Wine Festival',
290
- description: 'Refreshing wines for summer',
291
- ctaText: 'Shop Festival',
292
- textColor: '#ffffff',
293
- ctaTextColor: '#ffffff',
294
- primaryImage: 'https://placehold.co/1140x640/png?text=Wine+Festival',
295
- mobilePrimaryImage: 'https://placehold.co/640x640/png?text=Mobile+Festival',
296
- events: SPOT_EVENTS_EXAMPLE,
297
- productIds: [27, 28],
298
- },
299
- ],
300
- rbHomepageHeroTwoTile: [
301
- {
302
- id: 'iop987',
303
- spot: RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_TWO_TILE,
304
- variant: RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_TWO_TILE,
305
- width: 1140,
306
- height: 640,
307
- header: 'Bourbon Selection',
308
- description: "Kentucky's finest bourbons",
309
- ctaText: 'Shop Bourbon',
310
- textColor: '#ffffff',
311
- backgroundColor: '#2c1810',
312
- ctaTextColor: '#2c1810',
313
- primaryImage: 'https://placehold.co/1140x640/png?text=Bourbon',
314
- mobilePrimaryImage: 'https://placehold.co/640x640/png?text=Mobile+Bourbon',
315
- events: SPOT_EVENTS_EXAMPLE,
316
- productIds: [29, 30],
317
- },
318
- {
319
- id: 'lkj012',
320
- spot: RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_TWO_TILE,
321
- variant: RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_TWO_TILE,
322
- width: 1140,
323
- height: 640,
324
- header: 'Vodka Collection',
325
- description: 'Premium vodkas from around the world',
326
- ctaText: 'Shop Vodka',
327
- textColor: '#ffffff',
328
- backgroundColor: '#1a1a1a',
329
- ctaTextColor: '#1a1a1a',
330
- primaryImage: 'https://placehold.co/1140x640/png?text=Vodka',
331
- mobilePrimaryImage: 'https://placehold.co/640x640/png?text=Mobile+Vodka',
332
- events: SPOT_EVENTS_EXAMPLE,
333
- productIds: [31, 32],
334
- },
335
- ],
336
- rbHomepageHeroThreeTile: [
337
- {
338
- id: 'bnm345',
339
- spot: RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_THREE_TILE,
340
- variant: RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_THREE_TILE,
341
- width: 1140,
342
- height: 640,
343
- header: 'Rum Collection',
344
- description: 'Caribbean and craft rums',
345
- ctaText: 'Shop Rum',
346
- textColor: '#ffffff',
347
- backgroundColor: '#4d3a0a',
348
- ctaTextColor: '#4d3a0a',
349
- primaryImage: 'https://placehold.co/1140x640/png?text=Rum',
350
- secondaryImage: 'https://placehold.co/1140x640/png?text=Craft+Rum',
351
- mobilePrimaryImage: 'https://placehold.co/640x640/png?text=Mobile+Rum',
352
- mobileSecondaryImage: 'https://placehold.co/640x320/png?text=Mobile+Craft',
353
- events: SPOT_EVENTS_EXAMPLE,
354
- productIds: [33, 34],
355
- },
356
- {
357
- id: 'fgh678',
358
- spot: RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_THREE_TILE,
359
- variant: RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_THREE_TILE,
360
- width: 1140,
361
- height: 640,
362
- header: 'Scotch Selection',
363
- description: 'Single malts and blends',
364
- ctaText: 'Shop Scotch',
365
- textColor: '#ffffff',
366
- backgroundColor: '#4d0a0a',
367
- ctaTextColor: '#4d0a0a',
368
- primaryImage: 'https://placehold.co/1140x640/png?text=Scotch',
369
- secondaryImage: 'https://placehold.co/1140x640/png?text=Single+Malts',
370
- mobilePrimaryImage: 'https://placehold.co/640x640/png?text=Mobile+Scotch',
371
- mobileSecondaryImage: 'https://placehold.co/640x320/png?text=Mobile+Malts',
372
- events: SPOT_EVENTS_EXAMPLE,
373
- productIds: [35, 36],
374
- },
375
- ],
376
- rbLargeCategoryImageTout: [
377
- {
378
- id: 'cde567',
379
- spot: RMN_SPOT_TYPE.RB_LARGE_CATEGORY_IMAGE_TOUT,
380
- variant: RMN_SPOT_TYPE.RB_LARGE_CATEGORY_IMAGE_TOUT,
381
- width: 468,
382
- height: 410,
383
- header: 'Rare & Limited Edition',
384
- description: 'Discover our collection of hard-to-find and limited release spirits.',
385
- textColor: '#ffffff',
386
- ctaTextColor: '#ffffff',
387
- primaryImage: 'https://placehold.co/468x410/png?text=Rare+Spirits',
388
- mobilePrimaryImage: 'https://placehold.co/468x410/png?text=Mobile+Rare+Spirits',
389
- ctaText: 'Shop Rare Spirits',
390
- events: SPOT_EVENTS_EXAMPLE,
391
- productIds: [37, 38, 39, 40, 41],
392
- },
393
- {
394
- id: 'efg789',
395
- spot: RMN_SPOT_TYPE.RB_LARGE_CATEGORY_IMAGE_TOUT,
396
- variant: RMN_SPOT_TYPE.RB_LARGE_CATEGORY_IMAGE_TOUT,
397
- width: 468,
398
- height: 410,
399
- header: 'Vintage Champagnes',
400
- description: 'Explore our prestigious collection of aged champagnes.',
401
- textColor: '#ffffff',
402
- ctaTextColor: '#ffffff',
403
- primaryImage: 'https://placehold.co/468x410/png?text=Vintage+Champagne',
404
- mobilePrimaryImage: 'https://placehold.co/468x410/png?text=Mobile+Champagne',
405
- ctaText: 'View Collection',
406
- events: SPOT_EVENTS_EXAMPLE,
407
- productIds: [42, 43, 44, 45, 46],
408
- },
409
- {
410
- id: 'ghi012',
411
- spot: RMN_SPOT_TYPE.RB_LARGE_CATEGORY_IMAGE_TOUT,
412
- variant: RMN_SPOT_TYPE.RB_LARGE_CATEGORY_IMAGE_TOUT,
413
- width: 468,
414
- height: 410,
415
- header: 'Small Batch Bourbon',
416
- description: 'Hand-selected premium bourbon from craft distilleries.',
417
- textColor: '#ffffff',
418
- ctaTextColor: '#ffffff',
419
- primaryImage: 'https://placehold.co/468x410/png?text=Craft+Bourbon',
420
- mobilePrimaryImage: 'https://placehold.co/468x410/png?text=Mobile+Bourbon',
421
- ctaText: 'Explore Bourbon',
422
- events: SPOT_EVENTS_EXAMPLE,
423
- productIds: [47, 48, 49, 50, 51],
424
- },
425
- ],
426
- rbSmallDiscoverTout: [
427
- {
428
- id: 'jkl345',
429
- spot: RMN_SPOT_TYPE.RB_SMALL_DISCOVER_TOUT,
430
- variant: RMN_SPOT_TYPE.RB_SMALL_DISCOVER_TOUT,
431
- width: 224,
432
- height: 378,
433
- header: 'Château Margaux 2015 Bordeaux',
434
- textColor: '#ffffff',
435
- primaryImage: 'https://placehold.co/224x378/png?text=Château+Margaux',
436
- mobilePrimaryImage: 'https://placehold.co/224x378/png?text=Mobile+Château+Margaux',
437
- events: SPOT_EVENTS_EXAMPLE,
438
- productIds: [52, 53, 54, 55, 56],
439
- },
440
- {
441
- id: 'lmn678',
442
- spot: RMN_SPOT_TYPE.RB_SMALL_DISCOVER_TOUT,
443
- variant: RMN_SPOT_TYPE.RB_SMALL_DISCOVER_TOUT,
444
- width: 224,
445
- height: 378,
446
- header: 'Macallan 25 Year',
447
- textColor: '#ffffff',
448
- primaryImage: 'https://placehold.co/224x378/png?text=Macallan+25',
449
- mobilePrimaryImage: 'https://placehold.co/224x378/png?text=Mobile+Macallan',
450
- events: SPOT_EVENTS_EXAMPLE,
451
- productIds: [57, 58, 59, 60, 61],
452
- },
453
- {
454
- id: 'nop901',
455
- spot: RMN_SPOT_TYPE.RB_SMALL_DISCOVER_TOUT,
456
- variant: RMN_SPOT_TYPE.RB_SMALL_DISCOVER_TOUT,
457
- width: 224,
458
- height: 378,
459
- header: 'Louis XIII Cognac',
460
- textColor: '#ffffff',
461
- primaryImage: 'https://placehold.co/224x378/png?text=Louis+XIII',
462
- mobilePrimaryImage: 'https://placehold.co/224x378/png?text=Mobile+Louis+XIII',
463
- events: SPOT_EVENTS_EXAMPLE,
464
- productIds: [62, 63, 64, 65, 66],
465
- },
466
- ],
467
- rbSmallCategoryImageTout: [
468
- {
469
- id: 'qrs234',
470
- spot: RMN_SPOT_TYPE.RB_SMALL_CATEGORY_IMAGE_TOUT,
471
- variant: RMN_SPOT_TYPE.RB_SMALL_CATEGORY_IMAGE_TOUT,
472
- width: 224,
473
- height: 410,
474
- header: 'Japanese Sake',
475
- textColor: '#ffffff',
476
- primaryImage: 'https://placehold.co/224x410/png?text=Japanese+Sake',
477
- mobilePrimaryImage: 'https://placehold.co/224x410/png?text=Mobile+Japanese+Sake',
478
- events: SPOT_EVENTS_EXAMPLE,
479
- productIds: [67, 68, 69, 70, 71],
480
- },
481
- {
482
- id: 'stu567',
483
- spot: RMN_SPOT_TYPE.RB_SMALL_CATEGORY_IMAGE_TOUT,
484
- variant: RMN_SPOT_TYPE.RB_SMALL_CATEGORY_IMAGE_TOUT,
485
- width: 224,
486
- height: 410,
487
- header: 'Craft Vermouth',
488
- textColor: '#ffffff',
489
- primaryImage: 'https://placehold.co/224x410/png?text=Craft+Vermouth',
490
- mobilePrimaryImage: 'https://placehold.co/224x410/png?text=Mobile+Vermouth',
491
- events: SPOT_EVENTS_EXAMPLE,
492
- productIds: [72, 73, 74, 75, 76],
493
- },
494
- {
495
- id: 'vwx890',
496
- spot: RMN_SPOT_TYPE.RB_SMALL_CATEGORY_IMAGE_TOUT,
497
- variant: RMN_SPOT_TYPE.RB_SMALL_CATEGORY_IMAGE_TOUT,
498
- width: 224,
499
- height: 410,
500
- header: 'Premium Mezcal',
501
- textColor: '#ffffff',
502
- primaryImage: 'https://placehold.co/224x410/png?text=Premium+Mezcal',
503
- mobilePrimaryImage: 'https://placehold.co/224x410/png?text=Mobile+Mezcal',
504
- events: SPOT_EVENTS_EXAMPLE,
505
- productIds: [77, 78, 79, 80, 81],
506
- },
507
- ],
508
- rbCollectionBannerWithoutTextBlock: [
509
- {
510
- id: 'yz1234',
511
- spot: RMN_SPOT_TYPE.RB_COLLECTION_BANNER_WITHOUT_TEXT_BLOCK,
512
- variant: RMN_SPOT_TYPE.RB_COLLECTION_BANNER_WITHOUT_TEXT_BLOCK,
513
- width: 887,
514
- height: 344,
515
- primaryImage: 'https://placehold.co/887x344/png?text=Summer+Cocktails',
516
- mobilePrimaryImage: 'https://placehold.co/887x344/png?text=Mobile+Summer+Cocktails',
517
- events: SPOT_EVENTS_EXAMPLE,
518
- productIds: [82, 83, 84, 85, 86],
519
- },
520
- {
521
- id: 'abc567',
522
- spot: RMN_SPOT_TYPE.RB_COLLECTION_BANNER_WITHOUT_TEXT_BLOCK,
523
- variant: RMN_SPOT_TYPE.RB_COLLECTION_BANNER_WITHOUT_TEXT_BLOCK,
524
- width: 887,
525
- height: 344,
526
- primaryImage: 'https://placehold.co/887x344/png?text=Holiday+Spirits',
527
- mobilePrimaryImage: 'https://placehold.co/887x344/png?text=Mobile+Holiday+Spirits',
528
- events: SPOT_EVENTS_EXAMPLE,
529
- productIds: [87, 88, 89, 90, 91],
530
- },
531
- {
532
- id: 'def901',
533
- spot: RMN_SPOT_TYPE.RB_COLLECTION_BANNER_WITHOUT_TEXT_BLOCK,
534
- variant: RMN_SPOT_TYPE.RB_COLLECTION_BANNER_WITHOUT_TEXT_BLOCK,
535
- width: 887,
536
- height: 344,
537
- primaryImage: 'https://placehold.co/887x344/png?text=Wine+Essentials',
538
- mobilePrimaryImage: 'https://placehold.co/887x344/png?text=Mobile+Wine+Essentials',
539
- events: SPOT_EVENTS_EXAMPLE,
540
- productIds: [92, 93, 94, 95, 96],
541
- },
542
- ],
543
- rbNavigationBanner: [
544
- {
545
- id: 'ghi234',
546
- spot: RMN_SPOT_TYPE.RB_NAVIGATION_BANNER,
547
- variant: RMN_SPOT_TYPE.RB_NAVIGATION_BANNER,
548
- width: 440,
549
- height: 220,
550
- header: 'Explore Tequilas',
551
- textColor: '#ffffff',
552
- primaryImage: 'https://placehold.co/440x220/png?text=Tequila+Collection',
553
- mobilePrimaryImage: 'https://placehold.co/440x220/png?text=Mobile+Tequila+Collection',
554
- events: SPOT_EVENTS_EXAMPLE,
555
- productIds: [97, 98, 99, 100, 101],
556
- },
557
- {
558
- id: 'jkl678',
559
- spot: RMN_SPOT_TYPE.RB_NAVIGATION_BANNER,
560
- variant: RMN_SPOT_TYPE.RB_NAVIGATION_BANNER,
561
- width: 440,
562
- height: 220,
563
- header: 'Craft Gin Selection',
564
- textColor: '#ffffff',
565
- primaryImage: 'https://placehold.co/440x220/png?text=Gin+Selection',
566
- mobilePrimaryImage: 'https://placehold.co/440x220/png?text=Mobile+Gin+Selection',
567
- events: SPOT_EVENTS_EXAMPLE,
568
- productIds: [102, 103, 104, 105, 106],
569
- },
570
- {
571
- id: 'mno012',
572
- spot: RMN_SPOT_TYPE.RB_NAVIGATION_BANNER,
573
- variant: RMN_SPOT_TYPE.RB_NAVIGATION_BANNER,
574
- width: 440,
575
- height: 220,
576
- header: 'Premium Vodka',
577
- textColor: '#ffffff',
578
- primaryImage: 'https://placehold.co/440x220/png?text=Vodka+Premium',
579
- mobilePrimaryImage: 'https://placehold.co/440x220/png?text=Mobile+Vodka+Premium',
580
- events: SPOT_EVENTS_EXAMPLE,
581
- productIds: [107, 108, 109, 110, 111],
582
- },
583
- ],
584
- };
585
- const IAB_SPOTS_SELECTION_EXAMPLE = {
586
- banner: [],
587
- billboard: [
588
- {
589
- id: 'kol567',
590
- spot: RMN_SPOT_TYPE.BILLBOARD,
591
- variant: `${RMN_SPOT_TYPE.BILLBOARD}V2`,
592
- width: 1140,
593
- height: 640,
594
- header: 'Holiday Gift Guide',
595
- description: 'Perfect spirits for every occasion',
596
- ctaText: 'Shop Gifts',
597
- textColor: '#ffffff',
598
- ctaTextColor: '#ffffff',
599
- primaryImage: 'https://placehold.co/1140x640/png?text=Gift+Guide',
600
- mobilePrimaryImage: 'https://placehold.co/640x640/png?text=Mobile+Gifts',
601
- events: SPOT_EVENTS_EXAMPLE,
602
- productIds: [25, 26],
603
- },
604
- {
605
- id: 'hpm390',
606
- spot: RMN_SPOT_TYPE.BILLBOARD,
607
- variant: `${RMN_SPOT_TYPE.BILLBOARD}V2`,
608
- width: 1140,
609
- height: 640,
610
- header: 'Summer Wine Festival',
611
- description: 'Refreshing wines for summer',
612
- ctaText: 'Shop Festival',
613
- textColor: '#ffffff',
614
- ctaTextColor: '#ffffff',
615
- primaryImage: 'https://placehold.co/1140x640/png?text=Wine+Festival',
616
- mobilePrimaryImage: 'https://placehold.co/640x640/png?text=Mobile+Festival',
617
- events: SPOT_EVENTS_EXAMPLE,
618
- productIds: [27, 28],
619
- },
620
- ],
621
- button2: [],
622
- featurePhoneLargeBanner: [],
623
- featurePhoneMediumBanner: [],
624
- featurePhoneSmallBanner: [],
625
- halfPage: [],
626
- inText: [],
627
- largeLeaderboard: [],
628
- largeRectangle: [],
629
- leaderboard: [],
630
- mediumRectangle: [],
631
- microBar: [],
632
- mobilePhoneInterstitial1: [],
633
- mobilePhoneInterstitial2: [],
634
- mobilePhoneInterstitial3: [],
635
- popUp: [],
636
- portrait: [],
637
- rbProductUpcs: [],
638
- skyscraper: [],
639
- smallRectangle: [],
640
- smallSquare: [],
641
- smartphoneBanner1: [],
642
- smartphoneBanner2: [],
643
- square: [],
644
- verticalBanner: [],
645
- verticalRectangle: [],
646
- wideSkyscraper: [],
647
- };
648
-
649
81
  const REQUEST_CLOUD_PARTNER_SITE = 'X-Liquid-Partner-Site';
650
82
  const REQUEST_CLOUD_PROTECTED_KEY = 'X-Liquid-Protected';
651
83
  const REQUEST_CLOUD_PROTECTED_TIMESTAMP = 'X-Liquid-Timestamp';
@@ -16260,6 +15692,7 @@ class LocalStorageService {
16260
15692
  console.warn('Local storage is not supported in this environment');
16261
15693
  return;
16262
15694
  }
15695
+ this.setUserId();
16263
15696
  this.spots = new Map();
16264
15697
  // Sync local storage with the current state
16265
15698
  this.syncLocalStorage();
@@ -16346,6 +15779,45 @@ class LocalStorageService {
16346
15779
  });
16347
15780
  this.updateLocalStorage();
16348
15781
  }
15782
+ // ======================== Utility functions ======================== //
15783
+ getUserId() {
15784
+ return LocalStorageService.localStorageKey.replace(`${LocalStorageService.localStorageKeyPrefix}_`, '');
15785
+ }
15786
+ /**
15787
+ * Sets the user ID in the local storage.
15788
+ * If no existing key is found,
15789
+ * it generates a new unique ID and sets it as the local storage key.
15790
+ */
15791
+ setUserId() {
15792
+ const prefix = LocalStorageService.localStorageKeyPrefix;
15793
+ const existingKeys = Object.keys(window.localStorage).filter((key) => key.startsWith(prefix));
15794
+ const setNewKey = () => {
15795
+ const uniqueId = UniqueIdGenerator.generate();
15796
+ const newLocalStorageKey = `${prefix}_${uniqueId}`;
15797
+ window.localStorage.setItem(newLocalStorageKey, '');
15798
+ LocalStorageService.localStorageKey = newLocalStorageKey;
15799
+ };
15800
+ if (existingKeys.length === 0) {
15801
+ setNewKey();
15802
+ }
15803
+ else {
15804
+ const validKey = existingKeys.find((key) => UniqueIdGenerator.isValid(key.replace(`${prefix}_`, '')));
15805
+ // Delete all other keys except the valid one
15806
+ existingKeys.forEach((key) => {
15807
+ if (key !== validKey) {
15808
+ window.localStorage.removeItem(key);
15809
+ }
15810
+ });
15811
+ if (validKey) {
15812
+ // Found a valid key, assign it to localStorageKey
15813
+ LocalStorageService.localStorageKey = validKey;
15814
+ }
15815
+ else {
15816
+ // No valid key found, generate a new key
15817
+ setNewKey();
15818
+ }
15819
+ }
15820
+ }
16349
15821
  mapToObject(map) {
16350
15822
  return Object.fromEntries(map);
16351
15823
  }
@@ -16399,7 +15871,8 @@ class LocalStorageService {
16399
15871
  return atob(data);
16400
15872
  }
16401
15873
  }
16402
- LocalStorageService.localStorageKey = 'lc_rmn';
15874
+ LocalStorageService.localStorageKeyPrefix = 'lc_rmn';
15875
+ LocalStorageService.localStorageKey = '';
16403
15876
  LocalStorageService.spotExpirationTime = 1000 * 60 * 60 * 24 * 7; // 7 days
16404
15877
  LocalStorageService.encryptData = true;
16405
15878
 
@@ -18838,651 +18311,1319 @@ const STYLES$3 = ({ textColor = '#ffffff', ctaTextColor = textColor, ctaBorderCo
18838
18311
  font-size: 28px;
18839
18312
  }
18840
18313
 
18841
- .${prefix}__description {
18842
- font-size: 14px;
18843
- }
18314
+ .${prefix}__description {
18315
+ font-size: 14px;
18316
+ }
18317
+
18318
+ .${prefix}__cta-button {
18319
+ font-size: 13px;
18320
+ }
18321
+ }
18322
+
18323
+ @container (min-width: 1280px) {
18324
+ .${prefix}__cta-button {
18325
+ font-size: 14px;
18326
+ }
18327
+ }
18328
+ </style>
18329
+ `;
18330
+ };
18331
+ function rbLargeCategoryImageToutTemplate(spot, config) {
18332
+ const { prefix = '' } = config;
18333
+ return `
18334
+ ${GFONT_PRECONNECT}
18335
+ ${GFONT_SOURCE_SANS_3}
18336
+ ${GFONT_CORMORANT}
18337
+ ${STYLES$3(spot, config)}
18338
+ <div class="${prefix}">
18339
+ <div class="${prefix}__text">
18340
+ ${spot.header ? `<h2 class="${prefix}__header">${spot.header}</h2>` : ''}
18341
+ ${spot.description ? `<p class="${prefix}__description">${spot.description}</p>` : ''}
18342
+ ${spot.ctaText ? `<span class="${prefix}__cta-button">${spot.ctaText}</span>` : ''}
18343
+ </div>
18344
+ </div>
18345
+ `;
18346
+ }
18347
+
18348
+ const STYLES$2 = ({ textColor = '#ffffff', primaryImage, mobilePrimaryImage = primaryImage }, { prefix, overlay }) => {
18349
+ const linearGradient = generateGradientColor(overlay, 'rgba(0, 0, 0, 1) 0%, rgba(0, 0, 0, 0.5) 0%, rgba(0, 0, 0, 0) 30%');
18350
+ return `
18351
+ <style>
18352
+ .${prefix} {
18353
+ width: 100%;
18354
+ height: 100%;
18355
+ display: flex;
18356
+ flex-direction: column;
18357
+ justify-content: flex-end;
18358
+ border-radius: 5px;
18359
+ overflow: hidden;
18360
+ cursor: pointer;
18361
+ background-image: linear-gradient(to top, ${linearGradient}), url("${mobilePrimaryImage}");
18362
+ background-size: cover;
18363
+ background-position: center;
18364
+ background-repeat: no-repeat;
18365
+ position: relative;
18366
+ }
18367
+
18368
+ .${prefix}__header {
18369
+ font-size: 16px;
18370
+ color: ${textColor};
18371
+ line-height: 20px;
18372
+ font-weight: 400;
18373
+ font-family: "Source Sans 3", system-ui;
18374
+ font-style: normal;
18375
+ margin: 0;
18376
+ padding: 15px 10%;
18377
+ }
18378
+
18379
+ @container (min-width: 640px) {
18380
+ .${prefix} {
18381
+ background-image: linear-gradient(to top, ${linearGradient}), url("${primaryImage}");
18382
+ }
18383
+ }
18384
+
18385
+ @container (min-width: 768px) {
18386
+ .${prefix}__header {
18387
+ font-size: 22px;
18388
+ }
18389
+ }
18390
+
18391
+ @container (min-width: 1024px) {
18392
+ .${prefix}__header {
18393
+ font-size: 24px;
18394
+ }
18395
+ }
18396
+
18397
+ @container (min-width: 1280px) {
18398
+ .${prefix}__header {
18399
+ font-size: 28px;
18400
+ }
18401
+ }
18402
+ </style>
18403
+ `;
18404
+ };
18405
+ function rbNavigationBannerTemplate(spot, config) {
18406
+ const { prefix = '' } = config;
18407
+ return `
18408
+ ${GFONT_PRECONNECT}
18409
+ ${GFONT_SOURCE_SANS_3}
18410
+ ${STYLES$2(spot, config)}
18411
+ <div class="${prefix}">
18412
+ ${spot.header ? `<h2 class="${prefix}__header">${spot.header}</h2>` : ''}
18413
+ </div>
18414
+ `;
18415
+ }
18416
+
18417
+ const STYLES$1 = ({ textColor = '#ffffff', primaryImage, mobilePrimaryImage = primaryImage }, { prefix, overlay }) => {
18418
+ const linearGradient = generateGradientColor(overlay, 'rgba(0, 0, 0, 1) 0%, rgba(0, 0, 0, 0.5) 0%, rgba(0, 0, 0, 0) 30%');
18419
+ return `
18420
+ <style>
18421
+ .${prefix} {
18422
+ width: 100%;
18423
+ height: 100%;
18424
+ display: flex;
18425
+ flex-direction: column;
18426
+ justify-content: flex-end;
18427
+ background-image: linear-gradient(to top, ${linearGradient}), url("${mobilePrimaryImage}");
18428
+ background-size: cover;
18429
+ background-repeat: no-repeat;
18430
+ background-position: center;
18431
+ border-radius: 5px;
18432
+ overflow: hidden;
18433
+ cursor: pointer;
18434
+ position: relative;
18435
+ }
18436
+
18437
+ .${prefix}__header {
18438
+ font-size: 12px;
18439
+ color: ${textColor};
18440
+ line-height: 16px;
18441
+ font-family: "Source Sans 3", system-ui;
18442
+ font-style: normal;
18443
+ font-weight: 400;
18444
+ margin: 0;
18445
+ padding: 10px;
18446
+ }
18447
+
18448
+ @container (min-width: 640px) {
18449
+ .${prefix} {
18450
+ background-image: linear-gradient(to top, ${linearGradient}), url("${primaryImage}");
18451
+ }
18452
+ }
18453
+ </style>
18454
+ `;
18455
+ };
18456
+ function rbSmallCategoryImageToutTemplate(spot, config) {
18457
+ const { prefix = '' } = config;
18458
+ return `
18459
+ ${GFONT_PRECONNECT}
18460
+ ${GFONT_CORMORANT}
18461
+ ${STYLES$1(spot, config)}
18462
+ <div class="${prefix}">
18463
+ ${spot.header ? `<h2 class="${prefix}__header">${spot.header}</h2>` : ''}
18464
+ </div>
18465
+ `;
18466
+ }
18467
+
18468
+ const STYLES = ({ textColor = '#000000', backgroundColor = 'transparent', primaryImage, mobilePrimaryImage = primaryImage, }, { prefix }) => `
18469
+ <style>
18470
+ .${prefix} {
18471
+ width: 100%;
18472
+ height: 100%;
18473
+ background-color: ${backgroundColor};
18474
+ cursor: pointer;
18475
+ display: flex;
18476
+ flex-direction: column;
18477
+ border-radius: 5px;
18478
+ }
18479
+
18480
+ .${prefix}__image {
18481
+ width: 100%;
18482
+ height: 100%;
18483
+ background-image: url("${mobilePrimaryImage}");
18484
+ background-size: cover;
18485
+ background-repeat: no-repeat;
18486
+ background-position: center;
18487
+ border-radius: 5px;
18488
+ }
18489
+
18490
+ .${prefix}__text {
18491
+ text-align: left;
18492
+ display: flex;
18493
+ flex-direction: row;
18494
+ align-items: flex-start;
18495
+ justify-content: flex-start;
18496
+ width: 100%;
18497
+ height: fit-content;
18498
+ position: relative;
18499
+ }
18844
18500
 
18845
- .${prefix}__cta-button {
18846
- font-size: 13px;
18847
- }
18848
- }
18501
+ .${prefix}__header {
18502
+ font-size: 12px;
18503
+ color: ${textColor};
18504
+ padding-top: 5px;
18505
+ line-height: 16px;
18506
+ font-family: "Source Sans 3", system-ui;
18507
+ font-style: normal;
18508
+ font-weight: 400;
18509
+ margin: 0;
18510
+ }
18849
18511
 
18850
- @container (min-width: 1280px) {
18851
- .${prefix}__cta-button {
18852
- font-size: 14px;
18853
- }
18512
+ @container (min-width: 640px) {
18513
+ .${prefix}__image {
18514
+ background-image: url("${primaryImage}");
18854
18515
  }
18855
- </style>
18856
- `;
18857
- };
18858
- function rbLargeCategoryImageToutTemplate(spot, config) {
18516
+ }
18517
+ </style>
18518
+ `;
18519
+ function rbSmallDiscoverToutTemplate(spot, config) {
18859
18520
  const { prefix = '' } = config;
18860
18521
  return `
18861
18522
  ${GFONT_PRECONNECT}
18862
- ${GFONT_SOURCE_SANS_3}
18863
18523
  ${GFONT_CORMORANT}
18864
- ${STYLES$3(spot, config)}
18524
+ ${STYLES(spot, config)}
18865
18525
  <div class="${prefix}">
18526
+ <div class="${prefix}__image"></div>
18866
18527
  <div class="${prefix}__text">
18867
18528
  ${spot.header ? `<h2 class="${prefix}__header">${spot.header}</h2>` : ''}
18868
- ${spot.description ? `<p class="${prefix}__description">${spot.description}</p>` : ''}
18869
- ${spot.ctaText ? `<span class="${prefix}__cta-button">${spot.ctaText}</span>` : ''}
18870
18529
  </div>
18871
18530
  </div>
18872
18531
  `;
18873
18532
  }
18874
18533
 
18875
- const STYLES$2 = ({ textColor = '#ffffff', primaryImage, mobilePrimaryImage = primaryImage }, { prefix, overlay }) => {
18876
- const linearGradient = generateGradientColor(overlay, 'rgba(0, 0, 0, 1) 0%, rgba(0, 0, 0, 0.5) 0%, rgba(0, 0, 0, 0) 30%');
18877
- return `
18878
- <style>
18879
- .${prefix} {
18880
- width: 100%;
18881
- height: 100%;
18882
- display: flex;
18883
- flex-direction: column;
18884
- justify-content: flex-end;
18885
- border-radius: 5px;
18886
- overflow: hidden;
18887
- cursor: pointer;
18888
- background-image: linear-gradient(to top, ${linearGradient}), url("${mobilePrimaryImage}");
18889
- background-size: cover;
18890
- background-position: center;
18891
- background-repeat: no-repeat;
18892
- position: relative;
18534
+ /**
18535
+ * Returns the HTML element for the given spot.
18536
+ *
18537
+ * @param {ISpot} spot - The spot object.
18538
+ * @param {ISpotTemplateConfig} config - The spot template configuration.
18539
+ *
18540
+ * @return {HTMLElement | null} - The HTML element for the given spot or null if the spot template is not found.
18541
+ */
18542
+ const SPOT_TEMPLATE_HTML_ELEMENT = (spot, config) => {
18543
+ const templates = {
18544
+ // Reserve Bar Spot Templates
18545
+ [RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_THREE_TILE]: {
18546
+ rbHomepageHeroThreeTile: rbHomepageHeroThreeTileTemplate,
18547
+ },
18548
+ [RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_TWO_TILE]: {
18549
+ rbHomepageHeroTwoTile: rbHomepageHeroTwoTileTemplate,
18550
+ },
18551
+ [RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_FULL_IMAGE]: {
18552
+ rbHomepageHeroFullImage: rbHomepageHeroFullImageTemplate,
18553
+ },
18554
+ [RMN_SPOT_TYPE.RB_LARGE_CATEGORY_IMAGE_TOUT]: {
18555
+ rbLargeCategoryImageTout: rbLargeCategoryImageToutTemplate,
18556
+ },
18557
+ [RMN_SPOT_TYPE.RB_SMALL_DISCOVER_TOUT]: {
18558
+ rbSmallDiscoverTout: rbSmallDiscoverToutTemplate,
18559
+ },
18560
+ [RMN_SPOT_TYPE.RB_SMALL_CATEGORY_IMAGE_TOUT]: {
18561
+ rbSmallCategoryImageTout: rbSmallCategoryImageToutTemplate,
18562
+ },
18563
+ [RMN_SPOT_TYPE.RB_COLLECTION_BANNER_WITHOUT_TEXT_BLOCK]: {
18564
+ rbCollectionBannerWithoutTextBlock: rbCollectionBannerWithoutTextBlockTemplate,
18565
+ },
18566
+ [RMN_SPOT_TYPE.RB_NAVIGATION_BANNER]: {
18567
+ rbNavigationBanner: rbNavigationBannerTemplate,
18568
+ },
18569
+ [RMN_SPOT_TYPE.RB_PRODUCT_UPCS]: {
18570
+ rbProductUpcs: () => '', // No template for this spot type, it will be handled by ReserveBar App.
18571
+ },
18572
+ // IAB Standard Spot Templates
18573
+ [RMN_SPOT_TYPE.BILLBOARD]: {
18574
+ billboardV1: billboardV1Template,
18575
+ billboardV2: billboardV2Template,
18576
+ billboardV3: billboardV3Template,
18577
+ },
18578
+ [RMN_SPOT_TYPE.LARGE_RECTANGLE]: {
18579
+ largeRectangleV1: largeRectangleV1Template,
18580
+ },
18581
+ [RMN_SPOT_TYPE.VERTICAL_RECTANGLE]: {
18582
+ verticalRectangleV1: verticalRectangleV1Template,
18583
+ },
18584
+ [RMN_SPOT_TYPE.SQUARE]: {
18585
+ squareV1: squareV1Template,
18586
+ squareV2: squareV2Template,
18587
+ },
18588
+ [RMN_SPOT_TYPE.LARGE_LEADERBOARD]: {
18589
+ largeLeaderboardV1: largeLeaderboardV1Template,
18590
+ largeLeaderboardV2: largeLeaderboardV2Template,
18591
+ },
18592
+ [RMN_SPOT_TYPE.WIDE_SKYSCRAPER]: {
18593
+ wideSkyscraperV1: wideSkyscraperV1Template,
18594
+ },
18595
+ [RMN_SPOT_TYPE.IN_TEXT]: {
18596
+ inTextV1: inTextV1Template,
18597
+ },
18598
+ };
18599
+ const spotVariants = templates[spot.spot];
18600
+ if (!spotVariants) {
18601
+ return null;
18602
+ }
18603
+ const variantTemplate = spotVariants[spot.variant];
18604
+ if (!variantTemplate) {
18605
+ return null;
18606
+ }
18607
+ // Generate a highly unique prefix to avoid conflicts with other elements.
18608
+ const prefix = 's' + UniqueIdGenerator.generate().toLowerCase();
18609
+ const spotHtmlString = variantTemplate(spot, { ...config, prefix });
18610
+ return spotHtmlStringToElement(spotHtmlString);
18611
+ };
18612
+
18613
+ // For the moment, we will only focus on sites that use Google Analytics,
18614
+ // but we will add support for other analytics tools in the future.
18615
+ var AnalyticsTool;
18616
+ (function (AnalyticsTool) {
18617
+ AnalyticsTool["GoogleAnalytics"] = "google-analytics";
18618
+ AnalyticsTool["Other"] = "Other";
18619
+ })(AnalyticsTool || (AnalyticsTool = {}));
18620
+
18621
+ class DataLayerMonitor {
18622
+ constructor() {
18623
+ if (!window.dataLayer) {
18624
+ return;
18625
+ }
18626
+ this.originalPush = window.dataLayer.push;
18627
+ }
18628
+ static getInstance() {
18629
+ if (!DataLayerMonitor.instance) {
18630
+ DataLayerMonitor.instance = new DataLayerMonitor();
18631
+ }
18632
+ return DataLayerMonitor.instance;
18633
+ }
18634
+ setListener(listener) {
18635
+ this.listener = listener;
18636
+ }
18637
+ start() {
18638
+ window.dataLayer.push = (...args) => {
18639
+ const result = this.originalPush.apply(window.dataLayer, args);
18640
+ const pushedEvent = args[0];
18641
+ if (this.listener) {
18642
+ const normalizedData = this.cleanEventData(pushedEvent);
18643
+ if (normalizedData) {
18644
+ this.listener(normalizedData);
18645
+ }
18646
+ }
18647
+ return result;
18648
+ };
18649
+ }
18650
+ cleanEventData(data) {
18651
+ const eventName = getEventTypeFromRawEvent(data.event);
18652
+ if (!eventName) {
18653
+ return null;
18654
+ }
18655
+ const productIds = extractDeepIds(data);
18656
+ return {
18657
+ event: eventName,
18658
+ productIds,
18659
+ };
18660
+ }
18661
+ stop() {
18662
+ if (this.originalPush) {
18663
+ window.dataLayer.push = this.originalPush;
18664
+ }
18665
+ this.listener = undefined;
18666
+ }
18667
+ }
18668
+
18669
+ // @TODO: Add support for user to push events to our own data layer, if they don't use any analytics tool.
18670
+ // window.rmnDataLayer = window.rmnDataLayer || [];
18671
+ class MonitorService {
18672
+ constructor() {
18673
+ const analyticsTool = this.detectAnalyticsTool();
18674
+ switch (analyticsTool) {
18675
+ case AnalyticsTool.GoogleAnalytics:
18676
+ this.implementedMonitor = DataLayerMonitor.getInstance();
18677
+ break;
18678
+ case AnalyticsTool.Other:
18679
+ default:
18680
+ console.warn('This site uses an unsupported analytics tool.');
18681
+ break;
18682
+ }
18683
+ if (analyticsTool === AnalyticsTool.Other) {
18684
+ return;
18685
+ }
18686
+ this.pubSubService = PubsubService.getInstance();
18687
+ this.localStorageService = LocalStorageService.getInstance();
18688
+ }
18689
+ static getInstance() {
18690
+ if (!MonitorService.instance) {
18691
+ MonitorService.instance = new MonitorService();
18692
+ }
18693
+ return MonitorService.instance;
18694
+ }
18695
+ start() {
18696
+ if (!this.implementedMonitor)
18697
+ return;
18698
+ this.implementedMonitor.setListener(async (eventData) => {
18699
+ var _a;
18700
+ await this.matchAndFireEvent(eventData, (_a = this.localStorageService) === null || _a === void 0 ? void 0 : _a.getSpots());
18701
+ });
18702
+ this.implementedMonitor.start();
18703
+ }
18704
+ async matchAndFireEvent(eventData, spots) {
18705
+ var _a, _b;
18706
+ if (!spots)
18707
+ return;
18708
+ const eventProductIds = new Set(eventData.productIds.map((productId) => String(productId)));
18709
+ for (const spot of Object.values(spots)) {
18710
+ if (!spot.productIds.length)
18711
+ continue;
18712
+ const hasCommonProductIds = spot.productIds.find((productId) => eventProductIds.has(String(productId)));
18713
+ if (hasCommonProductIds) {
18714
+ if (Object.values(RMN_SPOT_EVENT).includes(eventData.event)) {
18715
+ await this.fireAndPublishSpotEvent({
18716
+ spotEvent: eventData.event,
18717
+ eventUrl: (_b = (_a = spot.events.find((event) => event.event === eventData.event)) === null || _a === void 0 ? void 0 : _a.url) !== null && _b !== void 0 ? _b : '',
18718
+ placementId: spot.placementId,
18719
+ spotId: spot.spotId,
18720
+ });
18721
+ }
18722
+ }
18723
+ }
18724
+ }
18725
+ async fireAndPublishSpotEvent({ spotEvent, eventUrl, placementId, spotId, }) {
18726
+ await fireEvent({
18727
+ event: spotEvent,
18728
+ eventUrl,
18729
+ });
18730
+ if (!this.pubSubService)
18731
+ return;
18732
+ this.pubSubService.publish(RMN_EVENT.SPOT_EVENT, {
18733
+ eventType: spotEvent,
18734
+ placementId,
18735
+ spotId,
18736
+ });
18737
+ }
18738
+ detectAnalyticsTool() {
18739
+ let analyticsTool = AnalyticsTool.Other;
18740
+ // Check for Google Analytics
18741
+ if (typeof window.ga !== 'undefined') {
18742
+ analyticsTool = AnalyticsTool.GoogleAnalytics;
18743
+ }
18744
+ // Check for Google Analytics 4
18745
+ if (typeof window.gtag !== 'undefined') {
18746
+ analyticsTool = AnalyticsTool.GoogleAnalytics;
18747
+ }
18748
+ // Check for Google Tag Manager
18749
+ if (typeof window.google_tag_manager !== 'undefined') {
18750
+ analyticsTool = AnalyticsTool.GoogleAnalytics;
18751
+ }
18752
+ // @TODO: Add support for other analytics tools
18753
+ // Check for Heap Analytics
18754
+ // Check for Mixpanel
18755
+ // Check for Woopra
18756
+ // Check for Segment
18757
+ // Check for Amplitude
18758
+ return analyticsTool;
18759
+ }
18760
+ }
18761
+
18762
+ class EventService {
18763
+ constructor() {
18764
+ this.pubSubService = PubsubService.getInstance();
18765
+ this.localStorageService = LocalStorageService.getInstance();
18766
+ this.activeSpots = new Map();
18767
+ this.spotStates = new Map();
18768
+ this.intersectionObserver = new IntersectionObserverService();
18769
+ // Start the user monitor, which will track and check user interactions
18770
+ MonitorService.getInstance().start();
18893
18771
  }
18894
-
18895
- .${prefix}__header {
18896
- font-size: 16px;
18897
- color: ${textColor};
18898
- line-height: 20px;
18899
- font-weight: 400;
18900
- font-family: "Source Sans 3", system-ui;
18901
- font-style: normal;
18902
- margin: 0;
18903
- padding: 15px 10%;
18772
+ static getInstance() {
18773
+ if (!EventService.instance) {
18774
+ EventService.instance = new EventService();
18775
+ }
18776
+ return EventService.instance;
18904
18777
  }
18905
-
18906
- @container (min-width: 640px) {
18907
- .${prefix} {
18908
- background-image: linear-gradient(to top, ${linearGradient}), url("${primaryImage}");
18909
- }
18778
+ subscribe(eventType, callback) {
18779
+ return this.pubSubService.subscribe(eventType, callback);
18910
18780
  }
18911
-
18912
- @container (min-width: 768px) {
18913
- .${prefix}__header {
18914
- font-size: 22px;
18915
- }
18781
+ publish(eventType, data) {
18782
+ this.pubSubService.publish(eventType, data);
18916
18783
  }
18917
-
18918
- @container (min-width: 1024px) {
18919
- .${prefix}__header {
18920
- font-size: 24px;
18921
- }
18784
+ registerSpot(params) {
18785
+ const { placementId, spot, spotElement } = params;
18786
+ this.activeSpots.set(placementId, { spotElement });
18787
+ // Fire impression event
18788
+ this.fireImpressionEvent(placementId, spot);
18789
+ // Handle intersection observer
18790
+ this.handleIntersectionObserver(placementId, spot, spotElement);
18791
+ // Attach click event listener
18792
+ spotElement.addEventListener('click', async () => await this.handleClick(params));
18922
18793
  }
18923
-
18924
- @container (min-width: 1280px) {
18925
- .${prefix}__header {
18926
- font-size: 28px;
18927
- }
18794
+ unregisterSpot(placementId) {
18795
+ const placementIdClean = placementId.replace('#', '');
18796
+ const spotData = this.activeSpots.get(placementIdClean);
18797
+ if (!spotData) {
18798
+ this.handleSpotState(placementIdClean, {
18799
+ state: {
18800
+ error: `Active spot with placementId ${placementIdClean} not found.`,
18801
+ },
18802
+ });
18803
+ return;
18804
+ }
18805
+ this.intersectionObserver.unobserve(spotData.spotElement);
18806
+ this.handleSpotState(placementIdClean, {
18807
+ dom: {
18808
+ spotElement: undefined,
18809
+ visibleOnViewport: false,
18810
+ },
18811
+ state: {
18812
+ unmounted: true,
18813
+ mounted: false,
18814
+ },
18815
+ });
18816
+ this.activeSpots.delete(placementIdClean);
18817
+ const placementElement = document.getElementById(placementIdClean);
18818
+ if (!placementElement) {
18819
+ this.handleSpotState(placementIdClean, {
18820
+ state: {
18821
+ error: `Placement element with id ${placementIdClean} not found.`,
18822
+ },
18823
+ });
18824
+ return;
18825
+ }
18826
+ placementElement.innerHTML = '';
18928
18827
  }
18929
- </style>
18930
- `;
18931
- };
18932
- function rbNavigationBannerTemplate(spot, config) {
18933
- const { prefix = '' } = config;
18934
- return `
18935
- ${GFONT_PRECONNECT}
18936
- ${GFONT_SOURCE_SANS_3}
18937
- ${STYLES$2(spot, config)}
18938
- <div class="${prefix}">
18939
- ${spot.header ? `<h2 class="${prefix}__header">${spot.header}</h2>` : ''}
18940
- </div>
18941
- `;
18942
- }
18943
-
18944
- const STYLES$1 = ({ textColor = '#ffffff', primaryImage, mobilePrimaryImage = primaryImage }, { prefix, overlay }) => {
18945
- const linearGradient = generateGradientColor(overlay, 'rgba(0, 0, 0, 1) 0%, rgba(0, 0, 0, 0.5) 0%, rgba(0, 0, 0, 0) 30%');
18946
- return `
18947
- <style>
18948
- .${prefix} {
18949
- width: 100%;
18950
- height: 100%;
18951
- display: flex;
18952
- flex-direction: column;
18953
- justify-content: flex-end;
18954
- background-image: linear-gradient(to top, ${linearGradient}), url("${mobilePrimaryImage}");
18955
- background-size: cover;
18956
- background-repeat: no-repeat;
18957
- background-position: center;
18958
- border-radius: 5px;
18959
- overflow: hidden;
18960
- cursor: pointer;
18961
- position: relative;
18962
- }
18963
-
18964
- .${prefix}__header {
18965
- font-size: 12px;
18966
- color: ${textColor};
18967
- line-height: 16px;
18968
- font-family: "Source Sans 3", system-ui;
18969
- font-style: normal;
18970
- font-weight: 400;
18971
- margin: 0;
18972
- padding: 10px;
18973
- }
18974
-
18975
- @container (min-width: 640px) {
18976
- .${prefix} {
18977
- background-image: linear-gradient(to top, ${linearGradient}), url("${primaryImage}");
18828
+ /**
18829
+ * Updates the state of a spot.
18830
+ *
18831
+ * @param {string} placementId - The placement ID of the spot.
18832
+ * @param {Partial<ILifecycleState>} updates - The updates to apply to the spot state.
18833
+ * @param {boolean} publish - Whether to publish the updated state.
18834
+ *
18835
+ * @returns {void}
18836
+ */
18837
+ handleSpotState(placementId, updates, publish = true) {
18838
+ let currentState = this.spotStates.get(placementId);
18839
+ if (!currentState) {
18840
+ currentState = {
18841
+ identifier: {
18842
+ placementId,
18843
+ spotId: '',
18844
+ spotType: '',
18845
+ },
18846
+ dom: {
18847
+ spotElement: undefined,
18848
+ visibleOnViewport: false,
18849
+ },
18850
+ state: {
18851
+ mounted: false,
18852
+ unmounted: false,
18853
+ loading: false,
18854
+ error: undefined,
18855
+ },
18856
+ displayConfig: {
18857
+ isCarousel: false,
18858
+ isCarouselItem: false,
18859
+ isSingleItem: false,
18860
+ },
18861
+ };
18978
18862
  }
18979
- }
18980
- </style>
18981
- `;
18982
- };
18983
- function rbSmallCategoryImageToutTemplate(spot, config) {
18984
- const { prefix = '' } = config;
18985
- return `
18986
- ${GFONT_PRECONNECT}
18987
- ${GFONT_CORMORANT}
18988
- ${STYLES$1(spot, config)}
18989
- <div class="${prefix}">
18990
- ${spot.header ? `<h2 class="${prefix}__header">${spot.header}</h2>` : ''}
18991
- </div>
18992
- `;
18993
- }
18994
-
18995
- const STYLES = ({ textColor = '#000000', backgroundColor = 'transparent', primaryImage, mobilePrimaryImage = primaryImage, }, { prefix }) => `
18996
- <style>
18997
- .${prefix} {
18998
- width: 100%;
18999
- height: 100%;
19000
- background-color: ${backgroundColor};
19001
- cursor: pointer;
19002
- display: flex;
19003
- flex-direction: column;
19004
- border-radius: 5px;
18863
+ const merged = this.deepMerge(currentState, updates);
18864
+ this.spotStates.set(placementId, merged);
18865
+ if (publish) {
18866
+ this.pubSubService.publish(RMN_EVENT.LIFECYCLE_STATE, this.spotStates.get(placementId));
18867
+ }
18868
+ }
18869
+ deepMerge(current, updates) {
18870
+ return {
18871
+ identifier: updates.identifier
18872
+ ? { ...current.identifier, ...updates.identifier }
18873
+ : current.identifier,
18874
+ dom: updates.dom ? { ...current.dom, ...updates.dom } : current.dom,
18875
+ state: updates.state ? { ...current.state, ...updates.state } : current.state,
18876
+ displayConfig: updates.displayConfig
18877
+ ? { ...current.displayConfig, ...updates.displayConfig }
18878
+ : current.displayConfig,
18879
+ };
18880
+ }
18881
+ async handleClick({ placementId, spot }) {
18882
+ var _a, _b, _c;
18883
+ this.pubSubService.publish(RMN_EVENT.SPOT_EVENT, {
18884
+ eventType: RMN_SPOT_EVENT.CLICK,
18885
+ placementId,
18886
+ spotId: spot.id,
18887
+ });
18888
+ await fireEvent({
18889
+ event: RMN_SPOT_EVENT.CLICK,
18890
+ eventUrl: (_b = (_a = spot.events.find((event) => event.event === RMN_SPOT_EVENT.CLICK)) === null || _a === void 0 ? void 0 : _a.url) !== null && _b !== void 0 ? _b : '',
18891
+ });
18892
+ // Save spot to local storage for event tracking
18893
+ this.localStorageService.setSpot(spot.id, {
18894
+ placementId,
18895
+ spotId: spot.id,
18896
+ spotType: spot.spot,
18897
+ events: spot.events,
18898
+ productIds: (_c = spot.productIds) !== null && _c !== void 0 ? _c : [1, 2, 3],
18899
+ });
19005
18900
  }
19006
-
19007
- .${prefix}__image {
19008
- width: 100%;
19009
- height: 100%;
19010
- background-image: url("${mobilePrimaryImage}");
19011
- background-size: cover;
19012
- background-repeat: no-repeat;
19013
- background-position: center;
19014
- border-radius: 5px;
18901
+ handleIntersectionObserver(placementId, _spot, spotElement) {
18902
+ const spotIsVisibleCallback = async () => {
18903
+ this.intersectionObserver.unobserve(spotElement);
18904
+ this.handleSpotState(placementId, {
18905
+ dom: {
18906
+ spotElement,
18907
+ visibleOnViewport: true,
18908
+ },
18909
+ });
18910
+ };
18911
+ this.intersectionObserver.observe(spotElement, spotIsVisibleCallback);
19015
18912
  }
19016
-
19017
- .${prefix}__text {
19018
- text-align: left;
19019
- display: flex;
19020
- flex-direction: row;
19021
- align-items: flex-start;
19022
- justify-content: flex-start;
19023
- width: 100%;
19024
- height: fit-content;
19025
- position: relative;
18913
+ fireImpressionEvent(placementId, spot) {
18914
+ this.pubSubService.publish(RMN_EVENT.SPOT_EVENT, {
18915
+ eventType: RMN_SPOT_EVENT.IMPRESSION,
18916
+ placementId,
18917
+ spotId: spot.id,
18918
+ });
18919
+ (async () => {
18920
+ var _a, _b;
18921
+ await fireEvent({
18922
+ event: RMN_SPOT_EVENT.IMPRESSION,
18923
+ eventUrl: (_b = (_a = spot.events.find((event) => event.event === RMN_SPOT_EVENT.IMPRESSION)) === null || _a === void 0 ? void 0 : _a.url) !== null && _b !== void 0 ? _b : '',
18924
+ });
18925
+ })();
19026
18926
  }
18927
+ }
19027
18928
 
19028
- .${prefix}__header {
19029
- font-size: 12px;
19030
- color: ${textColor};
19031
- padding-top: 5px;
19032
- line-height: 16px;
19033
- font-family: "Source Sans 3", system-ui;
19034
- font-style: normal;
19035
- font-weight: 400;
19036
- margin: 0;
19037
- }
18929
+ const SELECTION_API_PATH = '/spots/selection';
19038
18930
 
19039
- @container (min-width: 640px) {
19040
- .${prefix}__image {
19041
- background-image: url("${primaryImage}");
19042
- }
18931
+ class SelectionService extends BaseApi {
18932
+ constructor(auth) {
18933
+ super(auth);
18934
+ }
18935
+ static getInstance(auth) {
18936
+ return SingletonManager.getInstance('SelectionService', () => new SelectionService(auth));
18937
+ }
18938
+ /**
18939
+ * Makes a selection request on our server based on the provided data.
18940
+ *
18941
+ * @param {ISpotSelectionParams} data - Spots selection parameters.
18942
+ *
18943
+ * @return {Promise<ISpots | { error: string }>} - The spots response object.
18944
+ */
18945
+ async spotSelection(data) {
18946
+ const { isOk, val, isErr } = await this.post(SELECTION_API_PATH, data, {});
18947
+ if (isErr) {
18948
+ return { error: `There was an error during spot selection: (${isErr === null || isErr === void 0 ? void 0 : isErr.errorMessage})` };
18949
+ }
18950
+ if (isOk && val && val.data && (val === null || val === void 0 ? void 0 : val.refresh.token)) {
18951
+ this.authInfo.authenticated = true;
18952
+ this.authInfo.token = val.refresh.token;
18953
+ return val.data.spots;
18954
+ }
18955
+ return { error: 'Spot selection response was not successful' };
19043
18956
  }
19044
- </style>
19045
- `;
19046
- function rbSmallDiscoverToutTemplate(spot, config) {
19047
- const { prefix = '' } = config;
19048
- return `
19049
- ${GFONT_PRECONNECT}
19050
- ${GFONT_CORMORANT}
19051
- ${STYLES(spot, config)}
19052
- <div class="${prefix}">
19053
- <div class="${prefix}__image"></div>
19054
- <div class="${prefix}__text">
19055
- ${spot.header ? `<h2 class="${prefix}__header">${spot.header}</h2>` : ''}
19056
- </div>
19057
- </div>
19058
- `;
19059
18957
  }
19060
18958
 
19061
- /**
19062
- * Returns the HTML element for the given spot.
19063
- *
19064
- * @param {ISpot} spot - The spot object.
19065
- * @param {ISpotTemplateConfig} config - The spot template configuration.
19066
- *
19067
- * @return {HTMLElement | null} - The HTML element for the given spot or null if the spot template is not found.
19068
- */
19069
- const SPOT_TEMPLATE_HTML_ELEMENT = (spot, config) => {
19070
- const templates = {
19071
- // Reserve Bar Spot Templates
19072
- [RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_THREE_TILE]: {
19073
- rbHomepageHeroThreeTile: rbHomepageHeroThreeTileTemplate,
18959
+ const SPOT_EVENTS_EXAMPLE = [
18960
+ {
18961
+ event: RMN_SPOT_EVENT.CLICK,
18962
+ url: 'https://dev.rmn.liquidcommerce.cloud/api/spots/event?e=eyJ2IjoiMS4xMiIsImF2IjozMDY1NzgzLCJhdCI6MTYzLCJidCI6MCwiY20iOjQ0MDE5MjQxOCwiY2giOjYzMTg0LCJjayI6e30sImNyIjo0ODE4NTUzNzUsImRpIjoiOWMxNGFhMGI3NWY4NDMxNTllMTAwYWQzNzA1NzQyYzMiLCJkaiI6MCwiaWkiOiIxZjU0MGM5NmQ1N2M0YmZjODFlZjRkNjhkMzFjNDVkOSIsImRtIjozLCJmYyI6NjU2NjgyNTQ5LCJmbCI6NjQzOTMxODIxLCJpcCI6IjM1LjIyMy4xOTguOTUiLCJudyI6MTE1MDAsInBjIjo1MDAwLCJvcCI6NTAwMCwibXAiOjUwMDAsImVjIjowLCJnbSI6MCwiZXAiOm51bGwsInByIjoyNDkzMTYsInJ0IjoxLCJycyI6NTAwLCJzYSI6IjU1Iiwic2IiOiJpLTA0MDI0ODg4ZDlkMWRjZWQ3Iiwic3AiOjI3MjI3Miwic3QiOjEyODcyOTYsInRyIjp0cnVlLCJ1ayI6IjNhZWRhMWMxLTZhYjItNDUwNy04Mzg5LTEwZTJmNDMxYjM5MSIsInRzIjoxNzI5MzU5MDU0OTI3LCJiZiI6dHJ1ZSwicG4iOiJyYlByb2R1Y3RVcGNzIiwiZ2MiOnRydWUsImdDIjp0cnVlLCJncyI6Im5vbmUiLCJkYyI6MSwidHoiOiJBbWVyaWNhL05ld19Zb3JrIiwidXIiOm51bGx9&s=hWz37kbxi_u95EVNn2aoQhc5Aas',
18963
+ },
18964
+ {
18965
+ event: RMN_SPOT_EVENT.IMPRESSION,
18966
+ url: 'https://dev.rmn.liquidcommerce.cloud/api/spots/event?e=eyJ2IjoiMS4xMiIsImF2IjozMDY1NzgzLCJhdCI6MTYzLCJidCI6MCwiY20iOjQ0MDE5MjQxOCwiY2giOjYzMTg0LCJjayI6e30sImNyIjo0ODE4NTUzNzUsImRpIjoiOWMxNGFhMGI3NWY4NDMxNTllMTAwYWQzNzA1NzQyYzMiLCJkaiI6MCwiaWkiOiIxZjU0MGM5NmQ1N2M0YmZjODFlZjRkNjhkMzFjNDVkOSIsImRtIjozLCJmYyI6NjU2NjgyNTQ5LCJmbCI6NjQzOTMxODIxLCJpcCI6IjM1LjIyMy4xOTguOTUiLCJudyI6MTE1MDAsInBjIjo1MDAwLCJvcCI6NTAwMCwibXAiOjUwMDAsImVjIjowLCJnbSI6MCwiZXAiOm51bGwsInByIjoyNDkzMTYsInJ0IjoxLCJycyI6NTAwLCJzYSI6IjU1Iiwic2IiOiJpLTA0MDI0ODg4ZDlkMWRjZWQ3Iiwic3AiOjI3MjI3Miwic3QiOjEyODcyOTYsInRyIjp0cnVlLCJ1ayI6IjNhZWRhMWMxLTZhYjItNDUwNy04Mzg5LTEwZTJmNDMxYjM5MSIsInRzIjoxNzI5MzU5MDU0OTI3LCJiZiI6dHJ1ZSwicG4iOiJyYlByb2R1Y3RVcGNzIiwiZ2MiOnRydWUsImdDIjp0cnVlLCJncyI6Im5vbmUiLCJkYyI6MSwidHoiOiJBbWVyaWNhL05ld19Zb3JrIiwiYmEiOjEsImZxIjowfQ&s=djoysjCimurf-5T11AlNAwwLSS8',
18967
+ },
18968
+ {
18969
+ event: RMN_SPOT_EVENT.PURCHASE,
18970
+ url: 'https://dev.rmn.liquidcommerce.cloud/api/spots/event?e=eyJ2IjoiMS4xMiIsImF2IjozMDY1NzgzLCJhdCI6MTYzLCJidCI6MCwiY20iOjQ0MDE5MjQxOCwiY2giOjYzMTg0LCJjayI6e30sImNyIjo0ODE4NTUzNzUsImRpIjoiOWMxNGFhMGI3NWY4NDMxNTllMTAwYWQzNzA1NzQyYzMiLCJkaiI6MCwiaWkiOiIxZjU0MGM5NmQ1N2M0YmZjODFlZjRkNjhkMzFjNDVkOSIsImRtIjozLCJmYyI6NjU2NjgyNTQ5LCJmbCI6NjQzOTMxODIxLCJpcCI6IjM1LjIyMy4xOTguOTUiLCJudyI6MTE1MDAsInBjIjo1MDAwLCJvcCI6NTAwMCwibXAiOjUwMDAsImVjIjowLCJnbSI6MCwiZXAiOm51bGwsInByIjoyNDkzMTYsInJ0IjoxLCJycyI6NTAwLCJzYSI6IjU1Iiwic2IiOiJpLTA0MDI0ODg4ZDlkMWRjZWQ3Iiwic3AiOjI3MjI3Miwic3QiOjEyODcyOTYsInRyIjp0cnVlLCJ1ayI6IjNhZWRhMWMxLTZhYjItNDUwNy04Mzg5LTEwZTJmNDMxYjM5MSIsInRzIjoxNzI5MzU5MDU0OTI3LCJiZiI6dHJ1ZSwicG4iOiJyYlByb2R1Y3RVcGNzIiwiZ2MiOnRydWUsImdDIjp0cnVlLCJncyI6Im5vbmUiLCJkYyI6MSwidHoiOiJBbWVyaWNhL05ld19Zb3JrIiwiZXQiOjU5fQ&s=AAPAw-3SfZ0JMzjEGFSwt9L-2S4',
18971
+ },
18972
+ {
18973
+ event: RMN_SPOT_EVENT.ADD_TO_CART,
18974
+ url: 'https://dev.rmn.liquidcommerce.cloud/api/spots/event?e=eyJ2IjoiMS4xMiIsImF2IjozMDY1NzgzLCJhdCI6MTYzLCJidCI6MCwiY20iOjQ0MDE5MjQxOCwiY2giOjYzMTg0LCJjayI6e30sImNyIjo0ODE4NTUzNzUsImRpIjoiOWMxNGFhMGI3NWY4NDMxNTllMTAwYWQzNzA1NzQyYzMiLCJkaiI6MCwiaWkiOiIxZjU0MGM5NmQ1N2M0YmZjODFlZjRkNjhkMzFjNDVkOSIsImRtIjozLCJmYyI6NjU2NjgyNTQ5LCJmbCI6NjQzOTMxODIxLCJpcCI6IjM1LjIyMy4xOTguOTUiLCJudyI6MTE1MDAsInBjIjo1MDAwLCJvcCI6NTAwMCwibXAiOjUwMDAsImVjIjowLCJnbSI6MCwiZXAiOm51bGwsInByIjoyNDkzMTYsInJ0IjoxLCJycyI6NTAwLCJzYSI6IjU1Iiwic2IiOiJpLTA0MDI0ODg4ZDlkMWRjZWQ3Iiwic3AiOjI3MjI3Miwic3QiOjEyODcyOTYsInRyIjp0cnVlLCJ1ayI6IjNhZWRhMWMxLTZhYjItNDUwNy04Mzg5LTEwZTJmNDMxYjM5MSIsInRzIjoxNzI5MzU5MDU0OTI3LCJiZiI6dHJ1ZSwicG4iOiJyYlByb2R1Y3RVcGNzIiwiZ2MiOnRydWUsImdDIjp0cnVlLCJncyI6Im5vbmUiLCJkYyI6MSwidHoiOiJBbWVyaWNhL05ld19Zb3JrIiwiZXQiOjYwfQ&s=uzQFcjgL7m9XqUG8FvTPVN5YkZY',
18975
+ },
18976
+ {
18977
+ event: RMN_SPOT_EVENT.ADD_TO_WISHLIST,
18978
+ url: 'https://dev.rmn.liquidcommerce.cloud/api/spots/event?e=eyJ2IjoiMS4xMiIsImF2IjozMDY1NzgzLCJhdCI6MTYzLCJidCI6MCwiY20iOjQ0MDE5MjQxOCwiY2giOjYzMTg0LCJjayI6e30sImNyIjo0ODE4NTUzNzUsImRpIjoiOWMxNGFhMGI3NWY4NDMxNTllMTAwYWQzNzA1NzQyYzMiLCJkaiI6MCwiaWkiOiIxZjU0MGM5NmQ1N2M0YmZjODFlZjRkNjhkMzFjNDVkOSIsImRtIjozLCJmYyI6NjU2NjgyNTQ5LCJmbCI6NjQzOTMxODIxLCJpcCI6IjM1LjIyMy4xOTguOTUiLCJudyI6MTE1MDAsInBjIjo1MDAwLCJvcCI6NTAwMCwibXAiOjUwMDAsImVjIjowLCJnbSI6MCwiZXAiOm51bGwsInByIjoyNDkzMTYsInJ0IjoxLCJycyI6NTAwLCJzYSI6IjU1Iiwic2IiOiJpLTA0MDI0ODg4ZDlkMWRjZWQ3Iiwic3AiOjI3MjI3Miwic3QiOjEyODcyOTYsInRyIjp0cnVlLCJ1ayI6IjNhZWRhMWMxLTZhYjItNDUwNy04Mzg5LTEwZTJmNDMxYjM5MSIsInRzIjoxNzI5MzU5MDU0OTI3LCJiZiI6dHJ1ZSwicG4iOiJyYlByb2R1Y3RVcGNzIiwiZ2MiOnRydWUsImdDIjp0cnVlLCJncyI6Im5vbmUiLCJkYyI6MSwidHoiOiJBbWVyaWNhL05ld19Zb3JrIiwiZXQiOjYzfQ&s=m3ISU_iIy-OFtXrTKpI6cJAEC0k',
18979
+ },
18980
+ {
18981
+ event: RMN_SPOT_EVENT.BUY_NOW,
18982
+ url: 'https://dev.rmn.liquidcommerce.cloud/api/spots/event?e=eyJ2IjoiMS4xMiIsImF2IjozMDY1NzgzLCJhdCI6MTYzLCJidCI6MCwiY20iOjQ0MDE5MjQxOCwiY2giOjYzMTg0LCJjayI6e30sImNyIjo0ODE4NTUzNzUsImRpIjoiOWMxNGFhMGI3NWY4NDMxNTllMTAwYWQzNzA1NzQyYzMiLCJkaiI6MCwiaWkiOiIxZjU0MGM5NmQ1N2M0YmZjODFlZjRkNjhkMzFjNDVkOSIsImRtIjozLCJmYyI6NjU2NjgyNTQ5LCJmbCI6NjQzOTMxODIxLCJpcCI6IjM1LjIyMy4xOTguOTUiLCJudyI6MTE1MDAsInBjIjo1MDAwLCJvcCI6NTAwMCwibXAiOjUwMDAsImVjIjowLCJnbSI6MCwiZXAiOm51bGwsInByIjoyNDkzMTYsInJ0IjoxLCJycyI6NTAwLCJzYSI6IjU1Iiwic2IiOiJpLTA0MDI0ODg4ZDlkMWRjZWQ3Iiwic3AiOjI3MjI3Miwic3QiOjEyODcyOTYsInRyIjp0cnVlLCJ1ayI6IjNhZWRhMWMxLTZhYjItNDUwNy04Mzg5LTEwZTJmNDMxYjM5MSIsInRzIjoxNzI5MzU5MDU0OTI3LCJiZiI6dHJ1ZSwicG4iOiJyYlByb2R1Y3RVcGNzIiwiZ2MiOnRydWUsImdDIjp0cnVlLCJncyI6Im5vbmUiLCJkYyI6MSwidHoiOiJBbWVyaWNhL05ld19Zb3JrIiwiZXQiOjY5fQ&s=l6MOscQC-q-FkC2Ksd7w6jjySCQ',
18983
+ },
18984
+ ];
18985
+ ({
18986
+ rbHomepageHero: [
18987
+ {
18988
+ id: 'abc123',
18989
+ spot: RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_FULL_IMAGE,
18990
+ variant: RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_FULL_IMAGE,
18991
+ width: 1140,
18992
+ height: 640,
18993
+ header: 'Premium Wine Collection',
18994
+ description: 'Discover our exclusive selection of vintage wines',
18995
+ ctaText: 'Shop Wines',
18996
+ textColor: '#ffffff',
18997
+ ctaTextColor: '#ffffff',
18998
+ primaryImage: 'https://placehold.co/1140x640/png?text=Wine+Collection',
18999
+ mobilePrimaryImage: 'https://placehold.co/640x640/png?text=Mobile+Wine',
19000
+ events: SPOT_EVENTS_EXAMPLE,
19001
+ productIds: [1, 2, 3],
19002
+ },
19003
+ {
19004
+ id: 'jkl012',
19005
+ spot: RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_TWO_TILE,
19006
+ variant: RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_TWO_TILE,
19007
+ width: 1140,
19008
+ height: 640,
19009
+ header: 'Whiskey Collection',
19010
+ description: 'Premium whiskeys from around the world',
19011
+ ctaText: 'Shop Whiskeys',
19012
+ textColor: '#ffffff',
19013
+ backgroundColor: '#2c1810',
19014
+ ctaTextColor: '#2c1810',
19015
+ primaryImage: 'https://placehold.co/1140x640/png?text=Whiskey',
19016
+ mobilePrimaryImage: 'https://placehold.co/640x640/png?text=Mobile+Whiskey',
19017
+ events: SPOT_EVENTS_EXAMPLE,
19018
+ productIds: [10, 11],
19019
+ },
19020
+ {
19021
+ id: 'stu901',
19022
+ spot: RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_THREE_TILE,
19023
+ variant: RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_THREE_TILE,
19024
+ width: 1140,
19025
+ height: 640,
19026
+ header: 'Summer Cocktails',
19027
+ description: 'Essential spirits for summer mixing',
19028
+ ctaText: 'Mix It Up',
19029
+ textColor: '#ffffff',
19030
+ backgroundColor: '#4d3a0a',
19031
+ ctaTextColor: '#4d3a0a',
19032
+ primaryImage: 'https://placehold.co/1140x640/png?text=Cocktails',
19033
+ secondaryImage: 'https://placehold.co/1140x640/png?text=Mixers',
19034
+ mobilePrimaryImage: 'https://placehold.co/640x640/png?text=Mobile+Cocktails',
19035
+ mobileSecondaryImage: 'https://placehold.co/640x320/png?text=Mobile+Mixers',
19036
+ events: SPOT_EVENTS_EXAMPLE,
19037
+ productIds: [16, 17, 18],
19038
+ },
19039
+ {
19040
+ id: 'def456',
19041
+ spot: RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_FULL_IMAGE,
19042
+ variant: RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_FULL_IMAGE,
19043
+ width: 1140,
19044
+ height: 640,
19045
+ header: 'Craft Beer Festival',
19046
+ description: 'Local breweries and exclusive releases',
19047
+ ctaText: 'Explore Beers',
19048
+ textColor: '#ffffff',
19049
+ ctaTextColor: '#ffffff',
19050
+ primaryImage: 'https://placehold.co/1140x640/png?text=Beer+Festival',
19051
+ mobilePrimaryImage: 'https://placehold.co/640x640/png?text=Mobile+Beer',
19052
+ events: SPOT_EVENTS_EXAMPLE,
19053
+ productIds: [4, 5, 6],
19054
+ },
19055
+ {
19056
+ id: 'mno345',
19057
+ spot: RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_TWO_TILE,
19058
+ variant: RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_TWO_TILE,
19059
+ width: 1140,
19060
+ height: 640,
19061
+ header: 'Champagne Selection',
19062
+ description: 'Finest champagnes for every occasion',
19063
+ ctaText: 'View Champagnes',
19064
+ textColor: '#ffffff',
19065
+ backgroundColor: '#1a1a1a',
19066
+ ctaTextColor: '#1a1a1a',
19067
+ primaryImage: 'https://placehold.co/1140x640/png?text=Champagne',
19068
+ mobilePrimaryImage: 'https://placehold.co/640x640/png?text=Mobile+Champagne',
19069
+ events: SPOT_EVENTS_EXAMPLE,
19070
+ productIds: [12, 13],
19071
+ },
19072
+ {
19073
+ id: 'vwx234',
19074
+ spot: RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_THREE_TILE,
19075
+ variant: RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_THREE_TILE,
19076
+ width: 1140,
19077
+ height: 640,
19078
+ header: 'Wine Regions',
19079
+ description: 'Explore wines from top regions',
19080
+ ctaText: 'Tour Regions',
19081
+ textColor: '#ffffff',
19082
+ backgroundColor: '#4d0a0a',
19083
+ ctaTextColor: '#4d0a0a',
19084
+ primaryImage: 'https://placehold.co/1140x640/png?text=Wine+Regions',
19085
+ secondaryImage: 'https://placehold.co/1140x640/png?text=Vineyards',
19086
+ mobilePrimaryImage: 'https://placehold.co/640x640/png?text=Mobile+Regions',
19087
+ mobileSecondaryImage: 'https://placehold.co/640x320/png?text=Mobile+Vineyards',
19088
+ events: SPOT_EVENTS_EXAMPLE,
19089
+ productIds: [19, 20, 21],
19074
19090
  },
19075
- [RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_TWO_TILE]: {
19076
- rbHomepageHeroTwoTile: rbHomepageHeroTwoTileTemplate,
19091
+ {
19092
+ id: 'ghi789',
19093
+ spot: RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_FULL_IMAGE,
19094
+ variant: RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_FULL_IMAGE,
19095
+ width: 1140,
19096
+ height: 640,
19097
+ header: 'Rare Spirits',
19098
+ description: 'Limited edition spirits collection',
19099
+ ctaText: 'View Collection',
19100
+ textColor: '#ffffff',
19101
+ ctaTextColor: '#ffffff',
19102
+ primaryImage: 'https://placehold.co/1140x640/png?text=Rare+Spirits',
19103
+ mobilePrimaryImage: 'https://placehold.co/640x640/png?text=Mobile+Spirits',
19104
+ events: SPOT_EVENTS_EXAMPLE,
19105
+ productIds: [7, 8, 9],
19077
19106
  },
19078
- [RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_FULL_IMAGE]: {
19079
- rbHomepageHeroFullImage: rbHomepageHeroFullImageTemplate,
19107
+ {
19108
+ id: 'pqr678',
19109
+ spot: RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_TWO_TILE,
19110
+ variant: RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_TWO_TILE,
19111
+ width: 1140,
19112
+ height: 640,
19113
+ header: 'Gin Collection',
19114
+ description: 'Artisanal gins and botanicals',
19115
+ ctaText: 'Explore Gins',
19116
+ textColor: '#ffffff',
19117
+ backgroundColor: '#0a4d4d',
19118
+ ctaTextColor: '#0a4d4d',
19119
+ primaryImage: 'https://placehold.co/1140x640/png?text=Gin',
19120
+ mobilePrimaryImage: 'https://placehold.co/640x640/png?text=Mobile+Gin',
19121
+ events: SPOT_EVENTS_EXAMPLE,
19122
+ productIds: [14, 15],
19080
19123
  },
19081
- [RMN_SPOT_TYPE.RB_LARGE_CATEGORY_IMAGE_TOUT]: {
19082
- rbLargeCategoryImageTout: rbLargeCategoryImageToutTemplate,
19124
+ {
19125
+ id: 'yz5678',
19126
+ spot: RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_THREE_TILE,
19127
+ variant: RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_THREE_TILE,
19128
+ width: 1140,
19129
+ height: 640,
19130
+ header: 'Tequila Collection',
19131
+ description: 'Premium tequilas and mezcals',
19132
+ ctaText: 'Shop Tequila',
19133
+ textColor: '#ffffff',
19134
+ backgroundColor: '#0a4d2b',
19135
+ ctaTextColor: '#0a4d2b',
19136
+ primaryImage: 'https://placehold.co/1140x640/png?text=Tequila',
19137
+ secondaryImage: 'https://placehold.co/1140x640/png?text=Mezcal',
19138
+ mobilePrimaryImage: 'https://placehold.co/640x640/png?text=Mobile+Tequila',
19139
+ mobileSecondaryImage: 'https://placehold.co/640x320/png?text=Mobile+Mezcal',
19140
+ events: SPOT_EVENTS_EXAMPLE,
19141
+ productIds: [22, 23, 24],
19083
19142
  },
19084
- [RMN_SPOT_TYPE.RB_SMALL_DISCOVER_TOUT]: {
19085
- rbSmallDiscoverTout: rbSmallDiscoverToutTemplate,
19143
+ ],
19144
+ rbHomepageHeroFullImage: [
19145
+ {
19146
+ id: 'hjk567',
19147
+ spot: RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_FULL_IMAGE,
19148
+ variant: RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_FULL_IMAGE,
19149
+ width: 1140,
19150
+ height: 640,
19151
+ header: 'Holiday Gift Guide',
19152
+ description: 'Perfect spirits for every occasion',
19153
+ ctaText: 'Shop Gifts',
19154
+ textColor: '#ffffff',
19155
+ ctaTextColor: '#ffffff',
19156
+ primaryImage: 'https://placehold.co/1140x640/png?text=Gift+Guide',
19157
+ mobilePrimaryImage: 'https://placehold.co/640x640/png?text=Mobile+Gifts',
19158
+ events: SPOT_EVENTS_EXAMPLE,
19159
+ productIds: [25, 26],
19160
+ },
19161
+ {
19162
+ id: 'qwe890',
19163
+ spot: RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_FULL_IMAGE,
19164
+ variant: RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_FULL_IMAGE,
19165
+ width: 1140,
19166
+ height: 640,
19167
+ header: 'Summer Wine Festival',
19168
+ description: 'Refreshing wines for summer',
19169
+ ctaText: 'Shop Festival',
19170
+ textColor: '#ffffff',
19171
+ ctaTextColor: '#ffffff',
19172
+ primaryImage: 'https://placehold.co/1140x640/png?text=Wine+Festival',
19173
+ mobilePrimaryImage: 'https://placehold.co/640x640/png?text=Mobile+Festival',
19174
+ events: SPOT_EVENTS_EXAMPLE,
19175
+ productIds: [27, 28],
19176
+ },
19177
+ ],
19178
+ rbHomepageHeroTwoTile: [
19179
+ {
19180
+ id: 'iop987',
19181
+ spot: RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_TWO_TILE,
19182
+ variant: RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_TWO_TILE,
19183
+ width: 1140,
19184
+ height: 640,
19185
+ header: 'Bourbon Selection',
19186
+ description: "Kentucky's finest bourbons",
19187
+ ctaText: 'Shop Bourbon',
19188
+ textColor: '#ffffff',
19189
+ backgroundColor: '#2c1810',
19190
+ ctaTextColor: '#2c1810',
19191
+ primaryImage: 'https://placehold.co/1140x640/png?text=Bourbon',
19192
+ mobilePrimaryImage: 'https://placehold.co/640x640/png?text=Mobile+Bourbon',
19193
+ events: SPOT_EVENTS_EXAMPLE,
19194
+ productIds: [29, 30],
19195
+ },
19196
+ {
19197
+ id: 'lkj012',
19198
+ spot: RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_TWO_TILE,
19199
+ variant: RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_TWO_TILE,
19200
+ width: 1140,
19201
+ height: 640,
19202
+ header: 'Vodka Collection',
19203
+ description: 'Premium vodkas from around the world',
19204
+ ctaText: 'Shop Vodka',
19205
+ textColor: '#ffffff',
19206
+ backgroundColor: '#1a1a1a',
19207
+ ctaTextColor: '#1a1a1a',
19208
+ primaryImage: 'https://placehold.co/1140x640/png?text=Vodka',
19209
+ mobilePrimaryImage: 'https://placehold.co/640x640/png?text=Mobile+Vodka',
19210
+ events: SPOT_EVENTS_EXAMPLE,
19211
+ productIds: [31, 32],
19212
+ },
19213
+ ],
19214
+ rbHomepageHeroThreeTile: [
19215
+ {
19216
+ id: 'bnm345',
19217
+ spot: RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_THREE_TILE,
19218
+ variant: RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_THREE_TILE,
19219
+ width: 1140,
19220
+ height: 640,
19221
+ header: 'Rum Collection',
19222
+ description: 'Caribbean and craft rums',
19223
+ ctaText: 'Shop Rum',
19224
+ textColor: '#ffffff',
19225
+ backgroundColor: '#4d3a0a',
19226
+ ctaTextColor: '#4d3a0a',
19227
+ primaryImage: 'https://placehold.co/1140x640/png?text=Rum',
19228
+ secondaryImage: 'https://placehold.co/1140x640/png?text=Craft+Rum',
19229
+ mobilePrimaryImage: 'https://placehold.co/640x640/png?text=Mobile+Rum',
19230
+ mobileSecondaryImage: 'https://placehold.co/640x320/png?text=Mobile+Craft',
19231
+ events: SPOT_EVENTS_EXAMPLE,
19232
+ productIds: [33, 34],
19233
+ },
19234
+ {
19235
+ id: 'fgh678',
19236
+ spot: RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_THREE_TILE,
19237
+ variant: RMN_SPOT_TYPE.RB_HOMEPAGE_HERO_THREE_TILE,
19238
+ width: 1140,
19239
+ height: 640,
19240
+ header: 'Scotch Selection',
19241
+ description: 'Single malts and blends',
19242
+ ctaText: 'Shop Scotch',
19243
+ textColor: '#ffffff',
19244
+ backgroundColor: '#4d0a0a',
19245
+ ctaTextColor: '#4d0a0a',
19246
+ primaryImage: 'https://placehold.co/1140x640/png?text=Scotch',
19247
+ secondaryImage: 'https://placehold.co/1140x640/png?text=Single+Malts',
19248
+ mobilePrimaryImage: 'https://placehold.co/640x640/png?text=Mobile+Scotch',
19249
+ mobileSecondaryImage: 'https://placehold.co/640x320/png?text=Mobile+Malts',
19250
+ events: SPOT_EVENTS_EXAMPLE,
19251
+ productIds: [35, 36],
19252
+ },
19253
+ ],
19254
+ rbLargeCategoryImageTout: [
19255
+ {
19256
+ id: 'cde567',
19257
+ spot: RMN_SPOT_TYPE.RB_LARGE_CATEGORY_IMAGE_TOUT,
19258
+ variant: RMN_SPOT_TYPE.RB_LARGE_CATEGORY_IMAGE_TOUT,
19259
+ width: 468,
19260
+ height: 410,
19261
+ header: 'Rare & Limited Edition',
19262
+ description: 'Discover our collection of hard-to-find and limited release spirits.',
19263
+ textColor: '#ffffff',
19264
+ ctaTextColor: '#ffffff',
19265
+ primaryImage: 'https://placehold.co/468x410/png?text=Rare+Spirits',
19266
+ mobilePrimaryImage: 'https://placehold.co/468x410/png?text=Mobile+Rare+Spirits',
19267
+ ctaText: 'Shop Rare Spirits',
19268
+ events: SPOT_EVENTS_EXAMPLE,
19269
+ productIds: [37, 38, 39, 40, 41],
19270
+ },
19271
+ {
19272
+ id: 'efg789',
19273
+ spot: RMN_SPOT_TYPE.RB_LARGE_CATEGORY_IMAGE_TOUT,
19274
+ variant: RMN_SPOT_TYPE.RB_LARGE_CATEGORY_IMAGE_TOUT,
19275
+ width: 468,
19276
+ height: 410,
19277
+ header: 'Vintage Champagnes',
19278
+ description: 'Explore our prestigious collection of aged champagnes.',
19279
+ textColor: '#ffffff',
19280
+ ctaTextColor: '#ffffff',
19281
+ primaryImage: 'https://placehold.co/468x410/png?text=Vintage+Champagne',
19282
+ mobilePrimaryImage: 'https://placehold.co/468x410/png?text=Mobile+Champagne',
19283
+ ctaText: 'View Collection',
19284
+ events: SPOT_EVENTS_EXAMPLE,
19285
+ productIds: [42, 43, 44, 45, 46],
19286
+ },
19287
+ {
19288
+ id: 'ghi012',
19289
+ spot: RMN_SPOT_TYPE.RB_LARGE_CATEGORY_IMAGE_TOUT,
19290
+ variant: RMN_SPOT_TYPE.RB_LARGE_CATEGORY_IMAGE_TOUT,
19291
+ width: 468,
19292
+ height: 410,
19293
+ header: 'Small Batch Bourbon',
19294
+ description: 'Hand-selected premium bourbon from craft distilleries.',
19295
+ textColor: '#ffffff',
19296
+ ctaTextColor: '#ffffff',
19297
+ primaryImage: 'https://placehold.co/468x410/png?text=Craft+Bourbon',
19298
+ mobilePrimaryImage: 'https://placehold.co/468x410/png?text=Mobile+Bourbon',
19299
+ ctaText: 'Explore Bourbon',
19300
+ events: SPOT_EVENTS_EXAMPLE,
19301
+ productIds: [47, 48, 49, 50, 51],
19302
+ },
19303
+ ],
19304
+ rbSmallDiscoverTout: [
19305
+ {
19306
+ id: 'jkl345',
19307
+ spot: RMN_SPOT_TYPE.RB_SMALL_DISCOVER_TOUT,
19308
+ variant: RMN_SPOT_TYPE.RB_SMALL_DISCOVER_TOUT,
19309
+ width: 224,
19310
+ height: 378,
19311
+ header: 'Château Margaux 2015 Bordeaux',
19312
+ textColor: '#ffffff',
19313
+ primaryImage: 'https://placehold.co/224x378/png?text=Château+Margaux',
19314
+ mobilePrimaryImage: 'https://placehold.co/224x378/png?text=Mobile+Château+Margaux',
19315
+ events: SPOT_EVENTS_EXAMPLE,
19316
+ productIds: [52, 53, 54, 55, 56],
19317
+ },
19318
+ {
19319
+ id: 'lmn678',
19320
+ spot: RMN_SPOT_TYPE.RB_SMALL_DISCOVER_TOUT,
19321
+ variant: RMN_SPOT_TYPE.RB_SMALL_DISCOVER_TOUT,
19322
+ width: 224,
19323
+ height: 378,
19324
+ header: 'Macallan 25 Year',
19325
+ textColor: '#ffffff',
19326
+ primaryImage: 'https://placehold.co/224x378/png?text=Macallan+25',
19327
+ mobilePrimaryImage: 'https://placehold.co/224x378/png?text=Mobile+Macallan',
19328
+ events: SPOT_EVENTS_EXAMPLE,
19329
+ productIds: [57, 58, 59, 60, 61],
19330
+ },
19331
+ {
19332
+ id: 'nop901',
19333
+ spot: RMN_SPOT_TYPE.RB_SMALL_DISCOVER_TOUT,
19334
+ variant: RMN_SPOT_TYPE.RB_SMALL_DISCOVER_TOUT,
19335
+ width: 224,
19336
+ height: 378,
19337
+ header: 'Louis XIII Cognac',
19338
+ textColor: '#ffffff',
19339
+ primaryImage: 'https://placehold.co/224x378/png?text=Louis+XIII',
19340
+ mobilePrimaryImage: 'https://placehold.co/224x378/png?text=Mobile+Louis+XIII',
19341
+ events: SPOT_EVENTS_EXAMPLE,
19342
+ productIds: [62, 63, 64, 65, 66],
19086
19343
  },
19087
- [RMN_SPOT_TYPE.RB_SMALL_CATEGORY_IMAGE_TOUT]: {
19088
- rbSmallCategoryImageTout: rbSmallCategoryImageToutTemplate,
19344
+ ],
19345
+ rbSmallCategoryImageTout: [
19346
+ {
19347
+ id: 'qrs234',
19348
+ spot: RMN_SPOT_TYPE.RB_SMALL_CATEGORY_IMAGE_TOUT,
19349
+ variant: RMN_SPOT_TYPE.RB_SMALL_CATEGORY_IMAGE_TOUT,
19350
+ width: 224,
19351
+ height: 410,
19352
+ header: 'Japanese Sake',
19353
+ textColor: '#ffffff',
19354
+ primaryImage: 'https://placehold.co/224x410/png?text=Japanese+Sake',
19355
+ mobilePrimaryImage: 'https://placehold.co/224x410/png?text=Mobile+Japanese+Sake',
19356
+ events: SPOT_EVENTS_EXAMPLE,
19357
+ productIds: [67, 68, 69, 70, 71],
19089
19358
  },
19090
- [RMN_SPOT_TYPE.RB_COLLECTION_BANNER_WITHOUT_TEXT_BLOCK]: {
19091
- rbCollectionBannerWithoutTextBlock: rbCollectionBannerWithoutTextBlockTemplate,
19359
+ {
19360
+ id: 'stu567',
19361
+ spot: RMN_SPOT_TYPE.RB_SMALL_CATEGORY_IMAGE_TOUT,
19362
+ variant: RMN_SPOT_TYPE.RB_SMALL_CATEGORY_IMAGE_TOUT,
19363
+ width: 224,
19364
+ height: 410,
19365
+ header: 'Craft Vermouth',
19366
+ textColor: '#ffffff',
19367
+ primaryImage: 'https://placehold.co/224x410/png?text=Craft+Vermouth',
19368
+ mobilePrimaryImage: 'https://placehold.co/224x410/png?text=Mobile+Vermouth',
19369
+ events: SPOT_EVENTS_EXAMPLE,
19370
+ productIds: [72, 73, 74, 75, 76],
19092
19371
  },
19093
- [RMN_SPOT_TYPE.RB_NAVIGATION_BANNER]: {
19094
- rbNavigationBanner: rbNavigationBannerTemplate,
19372
+ {
19373
+ id: 'vwx890',
19374
+ spot: RMN_SPOT_TYPE.RB_SMALL_CATEGORY_IMAGE_TOUT,
19375
+ variant: RMN_SPOT_TYPE.RB_SMALL_CATEGORY_IMAGE_TOUT,
19376
+ width: 224,
19377
+ height: 410,
19378
+ header: 'Premium Mezcal',
19379
+ textColor: '#ffffff',
19380
+ primaryImage: 'https://placehold.co/224x410/png?text=Premium+Mezcal',
19381
+ mobilePrimaryImage: 'https://placehold.co/224x410/png?text=Mobile+Mezcal',
19382
+ events: SPOT_EVENTS_EXAMPLE,
19383
+ productIds: [77, 78, 79, 80, 81],
19095
19384
  },
19096
- [RMN_SPOT_TYPE.RB_PRODUCT_UPCS]: {
19097
- rbProductUpcs: () => '', // No template for this spot type, it will be handled by ReserveBar App.
19385
+ ],
19386
+ rbCollectionBannerWithoutTextBlock: [
19387
+ {
19388
+ id: 'yz1234',
19389
+ spot: RMN_SPOT_TYPE.RB_COLLECTION_BANNER_WITHOUT_TEXT_BLOCK,
19390
+ variant: RMN_SPOT_TYPE.RB_COLLECTION_BANNER_WITHOUT_TEXT_BLOCK,
19391
+ width: 887,
19392
+ height: 344,
19393
+ primaryImage: 'https://placehold.co/887x344/png?text=Summer+Cocktails',
19394
+ mobilePrimaryImage: 'https://placehold.co/887x344/png?text=Mobile+Summer+Cocktails',
19395
+ events: SPOT_EVENTS_EXAMPLE,
19396
+ productIds: [82, 83, 84, 85, 86],
19098
19397
  },
19099
- // IAB Standard Spot Templates
19100
- [RMN_SPOT_TYPE.BILLBOARD]: {
19101
- billboardV1: billboardV1Template,
19102
- billboardV2: billboardV2Template,
19103
- billboardV3: billboardV3Template,
19398
+ {
19399
+ id: 'abc567',
19400
+ spot: RMN_SPOT_TYPE.RB_COLLECTION_BANNER_WITHOUT_TEXT_BLOCK,
19401
+ variant: RMN_SPOT_TYPE.RB_COLLECTION_BANNER_WITHOUT_TEXT_BLOCK,
19402
+ width: 887,
19403
+ height: 344,
19404
+ primaryImage: 'https://placehold.co/887x344/png?text=Holiday+Spirits',
19405
+ mobilePrimaryImage: 'https://placehold.co/887x344/png?text=Mobile+Holiday+Spirits',
19406
+ events: SPOT_EVENTS_EXAMPLE,
19407
+ productIds: [87, 88, 89, 90, 91],
19104
19408
  },
19105
- [RMN_SPOT_TYPE.LARGE_RECTANGLE]: {
19106
- largeRectangleV1: largeRectangleV1Template,
19409
+ {
19410
+ id: 'def901',
19411
+ spot: RMN_SPOT_TYPE.RB_COLLECTION_BANNER_WITHOUT_TEXT_BLOCK,
19412
+ variant: RMN_SPOT_TYPE.RB_COLLECTION_BANNER_WITHOUT_TEXT_BLOCK,
19413
+ width: 887,
19414
+ height: 344,
19415
+ primaryImage: 'https://placehold.co/887x344/png?text=Wine+Essentials',
19416
+ mobilePrimaryImage: 'https://placehold.co/887x344/png?text=Mobile+Wine+Essentials',
19417
+ events: SPOT_EVENTS_EXAMPLE,
19418
+ productIds: [92, 93, 94, 95, 96],
19107
19419
  },
19108
- [RMN_SPOT_TYPE.VERTICAL_RECTANGLE]: {
19109
- verticalRectangleV1: verticalRectangleV1Template,
19420
+ ],
19421
+ rbNavigationBanner: [
19422
+ {
19423
+ id: 'ghi234',
19424
+ spot: RMN_SPOT_TYPE.RB_NAVIGATION_BANNER,
19425
+ variant: RMN_SPOT_TYPE.RB_NAVIGATION_BANNER,
19426
+ width: 440,
19427
+ height: 220,
19428
+ header: 'Explore Tequilas',
19429
+ textColor: '#ffffff',
19430
+ primaryImage: 'https://placehold.co/440x220/png?text=Tequila+Collection',
19431
+ mobilePrimaryImage: 'https://placehold.co/440x220/png?text=Mobile+Tequila+Collection',
19432
+ events: SPOT_EVENTS_EXAMPLE,
19433
+ productIds: [97, 98, 99, 100, 101],
19110
19434
  },
19111
- [RMN_SPOT_TYPE.SQUARE]: {
19112
- squareV1: squareV1Template,
19113
- squareV2: squareV2Template,
19435
+ {
19436
+ id: 'jkl678',
19437
+ spot: RMN_SPOT_TYPE.RB_NAVIGATION_BANNER,
19438
+ variant: RMN_SPOT_TYPE.RB_NAVIGATION_BANNER,
19439
+ width: 440,
19440
+ height: 220,
19441
+ header: 'Craft Gin Selection',
19442
+ textColor: '#ffffff',
19443
+ primaryImage: 'https://placehold.co/440x220/png?text=Gin+Selection',
19444
+ mobilePrimaryImage: 'https://placehold.co/440x220/png?text=Mobile+Gin+Selection',
19445
+ events: SPOT_EVENTS_EXAMPLE,
19446
+ productIds: [102, 103, 104, 105, 106],
19114
19447
  },
19115
- [RMN_SPOT_TYPE.LARGE_LEADERBOARD]: {
19116
- largeLeaderboardV1: largeLeaderboardV1Template,
19117
- largeLeaderboardV2: largeLeaderboardV2Template,
19448
+ {
19449
+ id: 'mno012',
19450
+ spot: RMN_SPOT_TYPE.RB_NAVIGATION_BANNER,
19451
+ variant: RMN_SPOT_TYPE.RB_NAVIGATION_BANNER,
19452
+ width: 440,
19453
+ height: 220,
19454
+ header: 'Premium Vodka',
19455
+ textColor: '#ffffff',
19456
+ primaryImage: 'https://placehold.co/440x220/png?text=Vodka+Premium',
19457
+ mobilePrimaryImage: 'https://placehold.co/440x220/png?text=Mobile+Vodka+Premium',
19458
+ events: SPOT_EVENTS_EXAMPLE,
19459
+ productIds: [107, 108, 109, 110, 111],
19118
19460
  },
19119
- [RMN_SPOT_TYPE.WIDE_SKYSCRAPER]: {
19120
- wideSkyscraperV1: wideSkyscraperV1Template,
19461
+ ],
19462
+ });
19463
+ ({
19464
+ banner: [],
19465
+ billboard: [
19466
+ {
19467
+ id: 'kol567',
19468
+ spot: RMN_SPOT_TYPE.BILLBOARD,
19469
+ variant: `${RMN_SPOT_TYPE.BILLBOARD}V2`,
19470
+ width: 1140,
19471
+ height: 640,
19472
+ header: 'Holiday Gift Guide',
19473
+ description: 'Perfect spirits for every occasion',
19474
+ ctaText: 'Shop Gifts',
19475
+ textColor: '#ffffff',
19476
+ ctaTextColor: '#ffffff',
19477
+ primaryImage: 'https://placehold.co/1140x640/png?text=Gift+Guide',
19478
+ mobilePrimaryImage: 'https://placehold.co/640x640/png?text=Mobile+Gifts',
19479
+ events: SPOT_EVENTS_EXAMPLE,
19480
+ productIds: [25, 26],
19121
19481
  },
19122
- [RMN_SPOT_TYPE.IN_TEXT]: {
19123
- inTextV1: inTextV1Template,
19482
+ {
19483
+ id: 'hpm390',
19484
+ spot: RMN_SPOT_TYPE.BILLBOARD,
19485
+ variant: `${RMN_SPOT_TYPE.BILLBOARD}V2`,
19486
+ width: 1140,
19487
+ height: 640,
19488
+ header: 'Summer Wine Festival',
19489
+ description: 'Refreshing wines for summer',
19490
+ ctaText: 'Shop Festival',
19491
+ textColor: '#ffffff',
19492
+ ctaTextColor: '#ffffff',
19493
+ primaryImage: 'https://placehold.co/1140x640/png?text=Wine+Festival',
19494
+ mobilePrimaryImage: 'https://placehold.co/640x640/png?text=Mobile+Festival',
19495
+ events: SPOT_EVENTS_EXAMPLE,
19496
+ productIds: [27, 28],
19124
19497
  },
19125
- };
19126
- const spotVariants = templates[spot.spot];
19127
- if (!spotVariants) {
19128
- return null;
19129
- }
19130
- const variantTemplate = spotVariants[spot.variant];
19131
- if (!variantTemplate) {
19132
- return null;
19133
- }
19134
- // Generate a highly unique prefix to avoid conflicts with other elements.
19135
- const prefix = 's' + UniqueIdGenerator.generate().toLowerCase();
19136
- const spotHtmlString = variantTemplate(spot, { ...config, prefix });
19137
- return spotHtmlStringToElement(spotHtmlString);
19138
- };
19139
-
19140
- // For the moment, we will only focus on sites that use Google Analytics,
19141
- // but we will add support for other analytics tools in the future.
19142
- var AnalyticsTool;
19143
- (function (AnalyticsTool) {
19144
- AnalyticsTool["GoogleAnalytics"] = "google-analytics";
19145
- AnalyticsTool["Other"] = "Other";
19146
- })(AnalyticsTool || (AnalyticsTool = {}));
19147
-
19148
- class DataLayerMonitor {
19149
- constructor() {
19150
- if (!window.dataLayer) {
19151
- return;
19152
- }
19153
- this.originalPush = window.dataLayer.push;
19154
- }
19155
- static getInstance() {
19156
- if (!DataLayerMonitor.instance) {
19157
- DataLayerMonitor.instance = new DataLayerMonitor();
19158
- }
19159
- return DataLayerMonitor.instance;
19160
- }
19161
- setListener(listener) {
19162
- this.listener = listener;
19163
- }
19164
- start() {
19165
- window.dataLayer.push = (...args) => {
19166
- const result = this.originalPush.apply(window.dataLayer, args);
19167
- const pushedEvent = args[0];
19168
- if (this.listener) {
19169
- const normalizedData = this.cleanEventData(pushedEvent);
19170
- if (normalizedData) {
19171
- this.listener(normalizedData);
19172
- }
19173
- }
19174
- return result;
19175
- };
19176
- }
19177
- cleanEventData(data) {
19178
- const eventName = getEventTypeFromRawEvent(data.event);
19179
- if (!eventName) {
19180
- return null;
19181
- }
19182
- const productIds = extractDeepIds(data);
19183
- return {
19184
- event: eventName,
19185
- productIds,
19186
- };
19187
- }
19188
- stop() {
19189
- if (this.originalPush) {
19190
- window.dataLayer.push = this.originalPush;
19191
- }
19192
- this.listener = undefined;
19193
- }
19194
- }
19498
+ ],
19499
+ button2: [],
19500
+ featurePhoneLargeBanner: [],
19501
+ featurePhoneMediumBanner: [],
19502
+ featurePhoneSmallBanner: [],
19503
+ halfPage: [],
19504
+ inText: [],
19505
+ largeLeaderboard: [],
19506
+ largeRectangle: [],
19507
+ leaderboard: [],
19508
+ mediumRectangle: [],
19509
+ microBar: [],
19510
+ mobilePhoneInterstitial1: [],
19511
+ mobilePhoneInterstitial2: [],
19512
+ mobilePhoneInterstitial3: [],
19513
+ popUp: [],
19514
+ portrait: [],
19515
+ rbProductUpcs: [],
19516
+ skyscraper: [],
19517
+ smallRectangle: [],
19518
+ smallSquare: [],
19519
+ smartphoneBanner1: [],
19520
+ smartphoneBanner2: [],
19521
+ square: [],
19522
+ verticalBanner: [],
19523
+ verticalRectangle: [],
19524
+ wideSkyscraper: [],
19525
+ });
19195
19526
 
19196
- // @TODO: Add support for user to push events to our own data layer, if they don't use any analytics tool.
19197
- // window.rmnDataLayer = window.rmnDataLayer || [];
19198
- class MonitorService {
19199
- constructor() {
19200
- const analyticsTool = this.detectAnalyticsTool();
19201
- switch (analyticsTool) {
19202
- case AnalyticsTool.GoogleAnalytics:
19203
- this.implementedMonitor = DataLayerMonitor.getInstance();
19204
- break;
19205
- case AnalyticsTool.Other:
19206
- default:
19207
- console.warn('This site uses an unsupported analytics tool.');
19208
- break;
19209
- }
19210
- if (analyticsTool === AnalyticsTool.Other) {
19211
- return;
19212
- }
19213
- this.pubSubService = PubsubService.getInstance();
19214
- this.localStorageService = LocalStorageService.getInstance();
19215
- }
19216
- static getInstance() {
19217
- if (!MonitorService.instance) {
19218
- MonitorService.instance = new MonitorService();
19219
- }
19220
- return MonitorService.instance;
19221
- }
19222
- start() {
19223
- if (!this.implementedMonitor)
19224
- return;
19225
- this.implementedMonitor.setListener(async (eventData) => {
19226
- var _a;
19227
- await this.matchAndFireEvent(eventData, (_a = this.localStorageService) === null || _a === void 0 ? void 0 : _a.getSpots());
19228
- });
19229
- this.implementedMonitor.start();
19230
- }
19231
- async matchAndFireEvent(eventData, spots) {
19232
- var _a, _b;
19233
- if (!spots)
19234
- return;
19235
- const eventProductIds = new Set(eventData.productIds);
19236
- for (const spot of Object.values(spots)) {
19237
- if (!spot.productIds.length)
19238
- continue;
19239
- const hasCommonProductIds = spot.productIds.find((productId) => eventProductIds.has(productId));
19240
- if (hasCommonProductIds) {
19241
- if (Object.values(RMN_SPOT_EVENT).includes(eventData.event)) {
19242
- await this.fireAndPublishSpotEvent({
19243
- spotEvent: eventData.event,
19244
- eventUrl: (_b = (_a = spot.events.find((event) => event.event === eventData.event)) === null || _a === void 0 ? void 0 : _a.url) !== null && _b !== void 0 ? _b : '',
19245
- placementId: spot.placementId,
19246
- spotId: spot.spotId,
19247
- });
19248
- }
19249
- }
19250
- }
19251
- }
19252
- async fireAndPublishSpotEvent({ spotEvent, eventUrl, placementId, spotId, }) {
19253
- await fireEvent({
19254
- event: spotEvent,
19255
- eventUrl,
19256
- });
19257
- if (!this.pubSubService)
19258
- return;
19259
- this.pubSubService.publish(RMN_EVENT.SPOT_EVENT, {
19260
- eventType: spotEvent,
19261
- placementId,
19262
- spotId,
19263
- });
19264
- }
19265
- detectAnalyticsTool() {
19266
- let analyticsTool = AnalyticsTool.Other;
19267
- // Check for Google Analytics
19268
- if (typeof window.ga !== 'undefined') {
19269
- analyticsTool = AnalyticsTool.GoogleAnalytics;
19270
- }
19271
- // Check for Google Analytics 4
19272
- if (typeof window.gtag !== 'undefined') {
19273
- analyticsTool = AnalyticsTool.GoogleAnalytics;
19274
- }
19275
- // Check for Google Tag Manager
19276
- if (typeof window.google_tag_manager !== 'undefined') {
19277
- analyticsTool = AnalyticsTool.GoogleAnalytics;
19278
- }
19279
- // @TODO: Add support for other analytics tools
19280
- // Check for Heap Analytics
19281
- // Check for Mixpanel
19282
- // Check for Woopra
19283
- // Check for Segment
19284
- // Check for Amplitude
19285
- return analyticsTool;
19527
+ /**
19528
+ * Checks if the current environment is a browser environment.
19529
+ *
19530
+ * @return {boolean} - Whether the current environment is a browser environment.
19531
+ */
19532
+ function isBrowserEnvironment() {
19533
+ if (typeof window === 'undefined' || typeof document === 'undefined') {
19534
+ console.warn('LiquidCommerce Rmn Sdk: Methods which create elements are only available in browser environments.');
19535
+ return false;
19286
19536
  }
19537
+ return true;
19287
19538
  }
19288
-
19289
- class EventService {
19290
- constructor() {
19291
- this.pubSubService = PubsubService.getInstance();
19292
- this.localStorageService = LocalStorageService.getInstance();
19293
- this.activeSpots = new Map();
19294
- this.spotStates = new Map();
19295
- this.intersectionObserver = new IntersectionObserverService();
19296
- // Start the user monitor, which will track and check user interactions
19297
- MonitorService.getInstance().start();
19298
- }
19299
- static getInstance() {
19300
- if (!EventService.instance) {
19301
- EventService.instance = new EventService();
19302
- }
19303
- return EventService.instance;
19304
- }
19305
- subscribe(eventType, callback) {
19306
- return this.pubSubService.subscribe(eventType, callback);
19307
- }
19308
- publish(eventType, data) {
19309
- this.pubSubService.publish(eventType, data);
19310
- }
19311
- registerSpot(params) {
19312
- const { placementId, spot, spotElement } = params;
19313
- this.activeSpots.set(placementId, { spotElement });
19314
- // Fire impression event
19315
- this.fireImpressionEvent(placementId, spot);
19316
- // Handle intersection observer
19317
- this.handleIntersectionObserver(placementId, spot, spotElement);
19318
- // Attach click event listener
19319
- spotElement.addEventListener('click', async () => await this.handleClick(params));
19320
- }
19321
- unregisterSpot(placementId) {
19322
- const placementIdClean = placementId.replace('#', '');
19323
- const spotData = this.activeSpots.get(placementIdClean);
19324
- if (!spotData) {
19325
- this.handleSpotState(placementIdClean, {
19326
- state: {
19327
- error: `Active spot with placementId ${placementIdClean} not found.`,
19328
- },
19329
- });
19330
- return;
19331
- }
19332
- this.intersectionObserver.unobserve(spotData.spotElement);
19333
- this.handleSpotState(placementIdClean, {
19334
- dom: {
19335
- spotElement: undefined,
19336
- visibleOnViewport: false,
19337
- },
19338
- state: {
19339
- unmounted: true,
19340
- mounted: false,
19341
- },
19342
- });
19343
- this.activeSpots.delete(placementIdClean);
19344
- const placementElement = document.getElementById(placementIdClean);
19345
- if (!placementElement) {
19346
- this.handleSpotState(placementIdClean, {
19539
+ /**
19540
+ * Validates the inject data by preventing duplicate placement ids and non-existent spot types.
19541
+ *
19542
+ * @param {IInjectSpotElement[]} inject - The inject data.
19543
+ * @return {IInjectSpotElement[]} - The validated inject data.
19544
+ */
19545
+ function validateInjectData(inject) {
19546
+ const eventService = EventService.getInstance();
19547
+ const placementIds = new Set();
19548
+ const validSpotTypes = new Set(Object.values(RMN_SPOT_TYPE));
19549
+ const validatedInject = [];
19550
+ for (const item of inject) {
19551
+ const placementId = item.placementId.replace('#', '');
19552
+ // Check for duplicate placement ids
19553
+ if (placementIds.has(placementId)) {
19554
+ eventService.handleSpotState(placementId, {
19347
19555
  state: {
19348
- error: `Placement element with id ${placementIdClean} not found.`,
19556
+ error: `Duplicate placement id (${placementId}) found. Please provide a unique placement id for each spot element.`,
19349
19557
  },
19350
19558
  });
19351
- return;
19352
- }
19353
- placementElement.innerHTML = '';
19354
- }
19355
- /**
19356
- * Updates the state of a spot.
19357
- *
19358
- * @param {string} placementId - The placement ID of the spot.
19359
- * @param {Partial<ILifecycleState>} updates - The updates to apply to the spot state.
19360
- * @param {boolean} publish - Whether to publish the updated state.
19361
- *
19362
- * @returns {void}
19363
- */
19364
- handleSpotState(placementId, updates, publish = true) {
19365
- let currentState = this.spotStates.get(placementId);
19366
- if (!currentState) {
19367
- currentState = {
19368
- identifier: {
19369
- placementId,
19370
- spotId: '',
19371
- spotType: '',
19372
- },
19373
- dom: {
19374
- spotElement: undefined,
19375
- visibleOnViewport: false,
19376
- },
19377
- state: {
19378
- mounted: false,
19379
- unmounted: false,
19380
- loading: false,
19381
- error: undefined,
19382
- },
19383
- displayConfig: {
19384
- isCarousel: false,
19385
- isCarouselItem: false,
19386
- isSingleItem: false,
19387
- },
19388
- };
19389
- }
19390
- const merged = this.deepMerge(currentState, updates);
19391
- this.spotStates.set(placementId, merged);
19392
- if (publish) {
19393
- this.pubSubService.publish(RMN_EVENT.LIFECYCLE_STATE, this.spotStates.get(placementId));
19394
- }
19395
- }
19396
- deepMerge(current, updates) {
19397
- return {
19398
- identifier: updates.identifier
19399
- ? { ...current.identifier, ...updates.identifier }
19400
- : current.identifier,
19401
- dom: updates.dom ? { ...current.dom, ...updates.dom } : current.dom,
19402
- state: updates.state ? { ...current.state, ...updates.state } : current.state,
19403
- displayConfig: updates.displayConfig
19404
- ? { ...current.displayConfig, ...updates.displayConfig }
19405
- : current.displayConfig,
19406
- };
19407
- }
19408
- async handleClick({ placementId, spot }) {
19409
- var _a, _b, _c;
19410
- this.pubSubService.publish(RMN_EVENT.SPOT_EVENT, {
19411
- eventType: RMN_SPOT_EVENT.CLICK,
19412
- placementId,
19413
- spotId: spot.id,
19414
- });
19415
- await fireEvent({
19416
- event: RMN_SPOT_EVENT.CLICK,
19417
- eventUrl: (_b = (_a = spot.events.find((event) => event.event === RMN_SPOT_EVENT.CLICK)) === null || _a === void 0 ? void 0 : _a.url) !== null && _b !== void 0 ? _b : '',
19418
- });
19419
- // Save spot to local storage for event tracking
19420
- this.localStorageService.setSpot(spot.id, {
19421
- placementId,
19422
- spotId: spot.id,
19423
- spotType: spot.spot,
19424
- events: spot.events,
19425
- productIds: (_c = spot.productIds) !== null && _c !== void 0 ? _c : [1, 2, 3],
19426
- });
19427
- }
19428
- handleIntersectionObserver(placementId, _spot, spotElement) {
19429
- const spotIsVisibleCallback = async () => {
19430
- this.intersectionObserver.unobserve(spotElement);
19431
- this.handleSpotState(placementId, {
19432
- dom: {
19433
- spotElement,
19434
- visibleOnViewport: true,
19559
+ continue;
19560
+ }
19561
+ // Check for non-existent spot types
19562
+ if (!validSpotTypes.has(item.spotType)) {
19563
+ eventService.handleSpotState(placementId, {
19564
+ state: {
19565
+ error: `Invalid spot type (${item.spotType}) found. Please provide a valid spot type for each spot element.`,
19435
19566
  },
19436
19567
  });
19437
- };
19438
- this.intersectionObserver.observe(spotElement, spotIsVisibleCallback);
19439
- }
19440
- fireImpressionEvent(placementId, spot) {
19441
- this.pubSubService.publish(RMN_EVENT.SPOT_EVENT, {
19442
- eventType: RMN_SPOT_EVENT.IMPRESSION,
19443
- placementId,
19444
- spotId: spot.id,
19445
- });
19446
- (async () => {
19447
- var _a, _b;
19448
- await fireEvent({
19449
- event: RMN_SPOT_EVENT.IMPRESSION,
19450
- eventUrl: (_b = (_a = spot.events.find((event) => event.event === RMN_SPOT_EVENT.IMPRESSION)) === null || _a === void 0 ? void 0 : _a.url) !== null && _b !== void 0 ? _b : '',
19451
- });
19452
- })();
19568
+ continue;
19569
+ }
19570
+ placementIds.add(placementId);
19571
+ validatedInject.push(item);
19453
19572
  }
19573
+ return validatedInject;
19454
19574
  }
19455
-
19456
- const SELECTION_API_PATH = '/spots/selection';
19457
-
19458
- class SelectionService extends BaseApi {
19459
- constructor(auth) {
19460
- super(auth);
19461
- }
19462
- static getInstance(auth) {
19463
- return SingletonManager.getInstance('SelectionService', () => new SelectionService(auth));
19575
+ /**
19576
+ * Clears the placement element by removing all its children.
19577
+ *
19578
+ * @param {string} placementId - The placement id.
19579
+ *
19580
+ * @return {void}
19581
+ */
19582
+ function clearPlacement(placementId) {
19583
+ var _a;
19584
+ (_a = document.getElementById(placementId)) === null || _a === void 0 ? void 0 : _a.replaceChildren();
19585
+ }
19586
+ /**
19587
+ * Prepares the spot placement for rendering by taking care of its styling.
19588
+ *
19589
+ * @param {HTMLElement} placement - The placement element.
19590
+ *
19591
+ * @return {void}
19592
+ */
19593
+ function prepareSpotPlacement(placement) {
19594
+ placement.removeAttribute('style');
19595
+ placement.removeAttribute('class');
19596
+ const styles = {
19597
+ width: '100%',
19598
+ height: '100%',
19599
+ display: 'flex',
19600
+ justifyContent: 'center',
19601
+ };
19602
+ Object.assign(placement.style, styles);
19603
+ }
19604
+ /**
19605
+ * Waits for the DOM to be ready before continuing.
19606
+ *
19607
+ * @return {Promise<void>} - A promise that resolves when the DOM is ready.
19608
+ */
19609
+ async function waitForDOM() {
19610
+ if (!isBrowserEnvironment()) {
19611
+ return;
19464
19612
  }
19465
- /**
19466
- * Makes a selection request on our server based on the provided data.
19467
- *
19468
- * @param {ISpotSelectionParams} data - Spots selection parameters.
19469
- *
19470
- * @return {Promise<ISpots | { error: string }>} - The spots response object.
19471
- */
19472
- async spotSelection(data) {
19473
- const { isOk, val, isErr } = await this.post(SELECTION_API_PATH, data, {});
19474
- if (isErr) {
19475
- return { error: `There was an error during spot selection: (${isErr === null || isErr === void 0 ? void 0 : isErr.errorMessage})` };
19476
- }
19477
- if (isOk && val && val.data && (val === null || val === void 0 ? void 0 : val.refresh.token)) {
19478
- this.authInfo.authenticated = true;
19479
- this.authInfo.token = val.refresh.token;
19480
- return val.data.spots;
19481
- }
19482
- return { error: 'Spot selection response was not successful' };
19613
+ if (document.readyState === 'complete' || document.readyState === 'interactive') {
19614
+ return;
19483
19615
  }
19616
+ return new Promise((resolve) => {
19617
+ document.addEventListener('DOMContentLoaded', () => {
19618
+ resolve();
19619
+ });
19620
+ });
19484
19621
  }
19485
19622
 
19623
+ /**
19624
+ * LiquidCommerce Rmn Client
19625
+ * @class
19626
+ */
19486
19627
  class LiquidCommerceRmnClient {
19487
19628
  constructor(auth) {
19488
19629
  this.selectionService = SelectionService.getInstance(auth);
@@ -19511,14 +19652,11 @@ class LiquidCommerceRmnClient {
19511
19652
  */
19512
19653
  async injectSpotElement(params) {
19513
19654
  var _a;
19514
- if (typeof window === 'undefined' || typeof document === 'undefined') {
19515
- console.warn('LiquidCommerce Rmn Sdk: injectSpotElement method is only available in the browser environment.');
19516
- return;
19517
- }
19655
+ // Wait for the DOM to be ready before continuing, to avoid issues with the spot placements
19656
+ await waitForDOM();
19518
19657
  const config = params.config;
19519
- let inject = params.inject;
19520
19658
  // Handle no spots error state
19521
- if (!inject.length) {
19659
+ if (!params.inject.length) {
19522
19660
  this.eventService.handleSpotState('all', {
19523
19661
  state: {
19524
19662
  error: 'No spot elements provided for injection.',
@@ -19526,49 +19664,32 @@ class LiquidCommerceRmnClient {
19526
19664
  });
19527
19665
  return;
19528
19666
  }
19529
- // Identify the spot elements
19667
+ // Validate inject data
19668
+ const inject = validateInjectData(params.inject);
19530
19669
  for (const item of inject) {
19670
+ // Identify the spot element
19531
19671
  this.eventService.handleSpotState(item.placementId, {
19532
19672
  identifier: {
19533
19673
  placementId: item.placementId,
19534
19674
  spotType: item.spotType,
19535
19675
  },
19536
19676
  }, false);
19537
- }
19538
- // Prevent duplicate placement ids
19539
- const hasDuplicatePlacementIds = this.preventDuplicateSpotPlacementIds(inject);
19540
- if (!hasDuplicatePlacementIds) {
19541
- return;
19542
- }
19543
- // Prevent non-existent spot types
19544
- inject = this.preventNonExistentSpotTypes(inject);
19545
- // Add Intersection Observer to the spot elements to track visibility
19546
- // This is useful for lazy loading the spot elements
19547
- for (const item of inject) {
19548
- const placementId = item.placementId.replace('#', '');
19549
- const placement = document.getElementById(placementId);
19677
+ const placement = document.getElementById(item.placementId);
19678
+ // Handle placement not found error state
19550
19679
  if (!placement) {
19551
- // Handle placement not found error state
19552
19680
  this.eventService.handleSpotState(item.placementId, {
19553
19681
  state: {
19554
- error: `Placement not found for id "${placementId}".`,
19682
+ error: `Placement not found for id "${item.placementId}".`,
19555
19683
  },
19556
19684
  });
19557
19685
  continue;
19558
19686
  }
19559
- // Take over placement styles
19560
- placement.removeAttribute('style');
19561
- placement.removeAttribute('class');
19562
- Object.assign(placement.style, {
19563
- width: '100%',
19564
- height: '100%',
19565
- display: 'flex',
19566
- justifyContent: 'center',
19567
- });
19687
+ prepareSpotPlacement(placement);
19568
19688
  const skeletonElement = this.elementService.createSkeletonElement({
19569
19689
  fluid: (_a = config === null || config === void 0 ? void 0 : config.fluid) !== null && _a !== void 0 ? _a : false,
19570
19690
  spotType: item.spotType,
19571
19691
  });
19692
+ // Handle skeleton loader error state
19572
19693
  if (!skeletonElement) {
19573
19694
  this.eventService.handleSpotState(item.placementId, {
19574
19695
  state: {
@@ -19580,19 +19701,15 @@ class LiquidCommerceRmnClient {
19580
19701
  continue;
19581
19702
  }
19582
19703
  placement.replaceChildren(skeletonElement);
19583
- const spotPlacementIsNear = async () => {
19704
+ const spotPlacementIsNearCallback = async () => {
19584
19705
  var _a;
19585
- // Set the spot element to loading state
19586
- this.eventService.handleSpotState(item.placementId, {
19587
- state: {
19588
- loading: true,
19589
- },
19590
- });
19591
- // Stop observing the placement
19706
+ // Stop observing the placement, as we only need to do this once
19592
19707
  this.intersectionObserver.unobserve(placement);
19708
+ // Set the spot element to loading state
19709
+ this.eventService.handleSpotState(item.placementId, { state: { loading: true } });
19593
19710
  // Make the spot selection request
19594
- const response = await this.spotSelectionRequest({ ...params, inject: [item] });
19595
- // const response = await this.useSpotSelectionExample(inject);
19711
+ const response = await this.injectSpotSelectionRequest({ ...params, inject: [item] });
19712
+ // const response = await useSpotSelectionExample(inject);
19596
19713
  // Handle request error state
19597
19714
  if (typeof response === 'object' && 'error' in response) {
19598
19715
  this.eventService.handleSpotState(item.placementId, {
@@ -19602,13 +19719,13 @@ class LiquidCommerceRmnClient {
19602
19719
  loading: false,
19603
19720
  },
19604
19721
  });
19605
- this.clearPlacement(item.placementId);
19722
+ clearPlacement(item.placementId);
19606
19723
  return;
19607
19724
  }
19608
19725
  const itemConfig = (_a = item.config) !== null && _a !== void 0 ? _a : config;
19609
19726
  const spots = response[item.placementId];
19727
+ // Handle no spots found error state
19610
19728
  if (!(spots === null || spots === void 0 ? void 0 : spots.length)) {
19611
- // Handle no spots found error state
19612
19729
  this.eventService.handleSpotState(item.placementId, {
19613
19730
  state: {
19614
19731
  error: `No spots found for type "${item.spotType}".`,
@@ -19616,22 +19733,105 @@ class LiquidCommerceRmnClient {
19616
19733
  loading: false,
19617
19734
  },
19618
19735
  });
19619
- this.clearPlacement(item.placementId);
19736
+ clearPlacement(item.placementId);
19620
19737
  return;
19621
19738
  }
19622
19739
  // Handle single spot
19623
19740
  if (spots.length === 1) {
19624
- this.injectOneSpotElement(item, placement, spots[0], itemConfig);
19741
+ this.injectOneSpotElement(placement, spots[0], itemConfig);
19625
19742
  }
19626
19743
  // Handle multiple spots (carousel)
19627
19744
  if (spots.length > 1) {
19628
19745
  this.injectCarouselSpotElement(placement, spots, itemConfig);
19629
19746
  }
19630
19747
  };
19631
- this.intersectionObserver.observe(placement, spotPlacementIsNear, { rootMargin: '500px' });
19748
+ /**
19749
+ * Observe the placement element to check if it is near the viewport.
19750
+ * If it is near, make the spot selection request.
19751
+ */
19752
+ this.intersectionObserver.observe(placement, spotPlacementIsNearCallback, {
19753
+ rootMargin: '500px',
19754
+ });
19755
+ }
19756
+ }
19757
+ /**
19758
+ * Injects a single spot element into the provided placement.
19759
+ *
19760
+ * @param {HTMLElement} placement - The placement element.
19761
+ * @param {ISpot} spot - The spot data.
19762
+ * @param {IInjectSpotElementConfig} config - The configuration object.
19763
+ *
19764
+ * @return {void}
19765
+ */
19766
+ injectOneSpotElement(placement, spot, config) {
19767
+ var _a;
19768
+ const placementId = placement.id;
19769
+ const spotType = spot.spot;
19770
+ this.eventService.handleSpotState(placementId, {
19771
+ identifier: {
19772
+ placementId,
19773
+ spotType,
19774
+ spotId: spot.id,
19775
+ },
19776
+ displayConfig: {
19777
+ isSingleItem: true,
19778
+ isCarousel: false,
19779
+ isCarouselItem: false,
19780
+ },
19781
+ });
19782
+ const spotData = this.elementService.overrideSpotColors(spot, config === null || config === void 0 ? void 0 : config.colors);
19783
+ const content = SPOT_TEMPLATE_HTML_ELEMENT(spotData, { overlay: config === null || config === void 0 ? void 0 : config.overlay });
19784
+ if (!content) {
19785
+ this.eventService.handleSpotState(placementId, {
19786
+ state: {
19787
+ error: `Failed to inject spot element. Could not create element for type "${spotType}".`,
19788
+ mounted: false,
19789
+ loading: false,
19790
+ },
19791
+ });
19792
+ clearPlacement(placementId);
19793
+ return;
19794
+ }
19795
+ // Create the spot element
19796
+ const spotElement = this.elementService.createSpotElement({
19797
+ content,
19798
+ config: {
19799
+ fluid: config === null || config === void 0 ? void 0 : config.fluid,
19800
+ spot: spot.spot,
19801
+ width: spot.width,
19802
+ height: spot.height,
19803
+ minScale: (_a = config === null || config === void 0 ? void 0 : config.minScale) !== null && _a !== void 0 ? _a : 0.25, // Scale down to 25% of the original size
19804
+ },
19805
+ });
19806
+ if (!spotElement) {
19807
+ this.eventService.handleSpotState(placementId, {
19808
+ state: {
19809
+ error: `Failed to inject spot element. Could not create element for type "${spotType}".`,
19810
+ mounted: false,
19811
+ loading: false,
19812
+ },
19813
+ });
19814
+ clearPlacement(placementId);
19815
+ return;
19632
19816
  }
19817
+ this.eventService.registerSpot({
19818
+ spot: spotData,
19819
+ placementId,
19820
+ spotElement,
19821
+ });
19822
+ placement.replaceChildren(spotElement);
19823
+ this.eventService.handleSpotState(placementId, {
19824
+ dom: {
19825
+ spotElement,
19826
+ visibleOnViewport: false,
19827
+ },
19828
+ state: {
19829
+ mounted: true,
19830
+ loading: false,
19831
+ error: undefined,
19832
+ },
19833
+ });
19633
19834
  }
19634
- /** ========================= HELPER METHODS ========================= **/
19635
19835
  /**
19636
19836
  * Injects a carousel element with the provided spots into the placement.
19637
19837
  *
@@ -19643,11 +19843,12 @@ class LiquidCommerceRmnClient {
19643
19843
  */
19644
19844
  injectCarouselSpotElement(placement, spots, config) {
19645
19845
  var _a;
19846
+ const placementId = placement.id;
19646
19847
  const carouselSlides = [];
19647
19848
  for (const spotItem of spots) {
19648
- this.eventService.handleSpotState(placement.id, {
19849
+ this.eventService.handleSpotState(placementId, {
19649
19850
  identifier: {
19650
- placementId: placement.id,
19851
+ placementId,
19651
19852
  spotType: spotItem.spot,
19652
19853
  spotId: spotItem.id,
19653
19854
  },
@@ -19660,7 +19861,7 @@ class LiquidCommerceRmnClient {
19660
19861
  const spot = this.elementService.overrideSpotColors(spotItem, config === null || config === void 0 ? void 0 : config.colors);
19661
19862
  const content = SPOT_TEMPLATE_HTML_ELEMENT(spot, { overlay: config === null || config === void 0 ? void 0 : config.overlay });
19662
19863
  if (!content) {
19663
- this.eventService.handleSpotState(placement.id, {
19864
+ this.eventService.handleSpotState(placementId, {
19664
19865
  state: {
19665
19866
  error: `Failed to inject carousel spot item element. Could not create element for type "${spot.spot}".`,
19666
19867
  mounted: false,
@@ -19671,7 +19872,7 @@ class LiquidCommerceRmnClient {
19671
19872
  }
19672
19873
  this.eventService.registerSpot({
19673
19874
  spot,
19674
- placementId: placement.id,
19875
+ placementId,
19675
19876
  spotElement: content,
19676
19877
  });
19677
19878
  carouselSlides.push(content);
@@ -19693,18 +19894,18 @@ class LiquidCommerceRmnClient {
19693
19894
  },
19694
19895
  });
19695
19896
  if (!carouselElement) {
19696
- this.eventService.handleSpotState(placement.id, {
19897
+ this.eventService.handleSpotState(placementId, {
19697
19898
  state: {
19698
19899
  error: `Failed to inject spot carousel element. Could not create spot carousel element.`,
19699
19900
  mounted: false,
19700
19901
  loading: false,
19701
19902
  },
19702
19903
  });
19703
- this.clearPlacement(placement.id);
19904
+ clearPlacement(placementId);
19704
19905
  return;
19705
19906
  }
19706
19907
  placement.replaceChildren(carouselElement);
19707
- this.eventService.handleSpotState(placement.id, {
19908
+ this.eventService.handleSpotState(placementId, {
19708
19909
  dom: {
19709
19910
  spotElement: carouselElement,
19710
19911
  visibleOnViewport: false,
@@ -19716,94 +19917,6 @@ class LiquidCommerceRmnClient {
19716
19917
  },
19717
19918
  });
19718
19919
  }
19719
- /**
19720
- * Injects a single spot element into the provided placement.
19721
- *
19722
- * @param {IInjectSpotElement} injectItem - The inject item data.
19723
- * @param {HTMLElement} placement - The placement element.
19724
- * @param {ISpot} spot - The spot data.
19725
- * @param {IInjectSpotElementConfig} config - The configuration object.
19726
- *
19727
- * @return {void}
19728
- */
19729
- injectOneSpotElement(injectItem, placement, spot, config) {
19730
- var _a;
19731
- this.eventService.handleSpotState(injectItem.placementId, {
19732
- identifier: {
19733
- placementId: injectItem.placementId,
19734
- spotType: injectItem.spotType,
19735
- spotId: spot.id,
19736
- },
19737
- displayConfig: {
19738
- isSingleItem: true,
19739
- isCarousel: false,
19740
- isCarouselItem: false,
19741
- },
19742
- });
19743
- const spotData = this.elementService.overrideSpotColors(spot, config === null || config === void 0 ? void 0 : config.colors);
19744
- const content = SPOT_TEMPLATE_HTML_ELEMENT(spotData, { overlay: config === null || config === void 0 ? void 0 : config.overlay });
19745
- if (!content) {
19746
- this.eventService.handleSpotState(injectItem.placementId, {
19747
- state: {
19748
- error: `Failed to inject spot element. Could not create element for type "${injectItem.spotType}".`,
19749
- mounted: false,
19750
- loading: false,
19751
- },
19752
- });
19753
- this.clearPlacement(injectItem.placementId);
19754
- return;
19755
- }
19756
- // Create the spot element
19757
- const spotElement = this.elementService.createSpotElement({
19758
- content,
19759
- config: {
19760
- fluid: config === null || config === void 0 ? void 0 : config.fluid,
19761
- spot: spot.spot,
19762
- width: spot.width,
19763
- height: spot.height,
19764
- minScale: (_a = config === null || config === void 0 ? void 0 : config.minScale) !== null && _a !== void 0 ? _a : 0.25, // Scale down to 25% of the original size
19765
- },
19766
- });
19767
- if (!spotElement) {
19768
- this.eventService.handleSpotState(injectItem.placementId, {
19769
- state: {
19770
- error: `Failed to inject spot element. Could not create element for type "${injectItem.spotType}".`,
19771
- mounted: false,
19772
- loading: false,
19773
- },
19774
- });
19775
- this.clearPlacement(injectItem.placementId);
19776
- return;
19777
- }
19778
- this.eventService.registerSpot({
19779
- spot: spotData,
19780
- placementId: injectItem.placementId,
19781
- spotElement,
19782
- });
19783
- placement.replaceChildren(spotElement);
19784
- this.eventService.handleSpotState(injectItem.placementId, {
19785
- dom: {
19786
- spotElement,
19787
- visibleOnViewport: false,
19788
- },
19789
- state: {
19790
- mounted: true,
19791
- loading: false,
19792
- error: undefined,
19793
- },
19794
- });
19795
- }
19796
- /**
19797
- * Clears the placement element by removing all its children.
19798
- *
19799
- * @param {string} placementId - The placement id.
19800
- *
19801
- * @return {void}
19802
- */
19803
- clearPlacement(placementId) {
19804
- var _a;
19805
- (_a = document.getElementById(placementId)) === null || _a === void 0 ? void 0 : _a.replaceChildren();
19806
- }
19807
19920
  /**
19808
19921
  * Makes a selection request on our server based on the provided data.
19809
19922
  *
@@ -19811,77 +19924,23 @@ class LiquidCommerceRmnClient {
19811
19924
  *
19812
19925
  * @return {Promise<ISpots | {error: string}>} - The spots response object.
19813
19926
  */
19814
- async spotSelectionRequest(params) {
19927
+ async injectSpotSelectionRequest(params) {
19815
19928
  const { inject, filter, config } = params;
19929
+ const localStorageService = LocalStorageService.getInstance();
19930
+ const spots = inject.map((item) => ({
19931
+ placementId: item.placementId,
19932
+ spot: item.spotType,
19933
+ count: item === null || item === void 0 ? void 0 : item.count,
19934
+ ...item === null || item === void 0 ? void 0 : item.filter,
19935
+ }));
19816
19936
  const request = {
19937
+ userId: localStorageService.getUserId(),
19817
19938
  url: config === null || config === void 0 ? void 0 : config.url,
19818
19939
  filter,
19819
- spots: inject.map((item) => ({
19820
- placementId: item.placementId,
19821
- spot: item.spotType,
19822
- count: item === null || item === void 0 ? void 0 : item.count,
19823
- ...item === null || item === void 0 ? void 0 : item.filter,
19824
- })),
19940
+ spots,
19825
19941
  };
19826
19942
  return this.spotSelection(request);
19827
19943
  }
19828
- /**
19829
- * Prevents duplicate placement ids in the inject data.
19830
- *
19831
- * @param {IInjectSpotElement[]} inject - The inject data.
19832
- *
19833
- * @throws {Error} - If a duplicate placement id is found.
19834
- *
19835
- * @return {void}
19836
- */
19837
- preventDuplicateSpotPlacementIds(inject) {
19838
- const placementIds = new Set();
19839
- for (const item of inject) {
19840
- if (placementIds.has(item.placementId)) {
19841
- this.eventService.handleSpotState(item.placementId, {
19842
- state: {
19843
- error: `Duplicate placement id (${item.placementId}) found. Please provide a unique placement id for each spot element.`,
19844
- },
19845
- });
19846
- return false;
19847
- }
19848
- placementIds.add(item.placementId);
19849
- }
19850
- return true;
19851
- }
19852
- /**
19853
- * Prevents non-existent spot types in the inject data.
19854
- *
19855
- * @param {IInjectSpotElement[]} inject - The inject data.
19856
- * @return {IInjectSpotElement[]} - The filtered inject data.
19857
- */
19858
- preventNonExistentSpotTypes(inject) {
19859
- const newInject = [];
19860
- for (const item of inject) {
19861
- if (!Object.values(RMN_SPOT_TYPE).includes(item.spotType)) {
19862
- this.eventService.handleSpotState(item.placementId, {
19863
- state: {
19864
- error: `Invalid spot type (${item.spotType}) found. Please provide a valid spot type for each spot element.`,
19865
- },
19866
- });
19867
- continue;
19868
- }
19869
- newInject.push(item);
19870
- }
19871
- return newInject;
19872
- }
19873
- // Use spot selection example data for private testing
19874
- useSpotSelectionExample(inject) {
19875
- const examples = { ...RB_SPOTS_SELECTION_EXAMPLE, ...IAB_SPOTS_SELECTION_EXAMPLE };
19876
- const data = {};
19877
- inject.map((item) => {
19878
- var _a, _b, _c;
19879
- data[item.placementId] = (_c = (_a = examples[item.spotType]) === null || _a === void 0 ? void 0 : _a.slice(0, (_b = item.count) !== null && _b !== void 0 ? _b : 1)) !== null && _c !== void 0 ? _c : [];
19880
- });
19881
- return new Promise((resolve) => {
19882
- resolve(data);
19883
- });
19884
- }
19885
19944
  }
19886
19945
  /**
19887
19946
  * Creates a new instance of the RmnClient.