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