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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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.