@streamlayer/feature-advertisement 1.0.0 → 1.1.1-rc.1342
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/classic/package.json +5 -0
- package/lib/cjs/advertisement.d.ts +64 -0
- package/lib/cjs/advertisement.js +307 -0
- package/lib/cjs/index.d.ts +19 -0
- package/lib/cjs/index.js +39 -0
- package/lib/cjs/package.json +1 -0
- package/lib/cjs/units/external-ad.d.ts +58 -0
- package/lib/cjs/units/external-ad.js +390 -0
- package/lib/cjs/units/external-pause-ad.d.ts +50 -0
- package/lib/cjs/units/external-pause-ad.js +253 -0
- package/lib/cjs/units/gam.d.ts +16 -0
- package/lib/cjs/units/gam.js +104 -0
- package/lib/cjs/units/parse-vast-extensions.d.ts +17 -0
- package/lib/cjs/units/parse-vast-extensions.js +52 -0
- package/lib/cjs/units/pause-ad-card.d.ts +49 -0
- package/lib/cjs/units/pause-ad-card.js +136 -0
- package/lib/cjs/units/pause-ad.d.ts +16 -0
- package/lib/cjs/units/pause-ad.js +52 -0
- package/lib/cjs/units/ssai-ad-break.d.ts +21 -0
- package/lib/cjs/units/ssai-ad-break.js +11 -0
- package/lib/cjs/units/ssai-creative.d.ts +89 -0
- package/lib/cjs/units/ssai-creative.js +136 -0
- package/lib/cjs/units/ssai-detector.d.ts +180 -0
- package/lib/cjs/units/ssai-detector.js +1156 -0
- package/lib/cjs/units/ssai-preload.d.ts +20 -0
- package/lib/cjs/units/ssai-preload.js +62 -0
- package/lib/cjs/units/ssai-providers.d.ts +117 -0
- package/lib/cjs/units/ssai-providers.js +263 -0
- package/lib/cjs/units/vast-tracker.d.ts +45 -0
- package/lib/cjs/units/vast-tracker.js +64 -0
- package/lib/classic/_virtual/_rollupPluginBabelHelpers.js +71 -0
- package/lib/classic/advertisement.js +331 -0
- package/lib/classic/cjs/_virtual/_rollupPluginBabelHelpers.js +77 -0
- package/lib/classic/cjs/advertisement.js +333 -0
- package/lib/classic/cjs/index.js +47 -0
- package/lib/classic/cjs/package.json +1 -0
- package/lib/classic/cjs/units/external-ad.js +453 -0
- package/lib/classic/cjs/units/external-pause-ad.js +274 -0
- package/lib/classic/cjs/units/gam.js +110 -0
- package/lib/classic/cjs/units/parse-vast-extensions.js +56 -0
- package/lib/classic/cjs/units/pause-ad-card.js +134 -0
- package/lib/classic/cjs/units/pause-ad.js +72 -0
- package/lib/classic/cjs/units/ssai-ad-break.js +11 -0
- package/lib/classic/cjs/units/ssai-creative.js +142 -0
- package/lib/classic/cjs/units/ssai-detector.js +1170 -0
- package/lib/classic/cjs/units/ssai-preload.js +72 -0
- package/lib/classic/cjs/units/ssai-providers.js +299 -0
- package/lib/classic/cjs/units/vast-tracker.js +81 -0
- package/lib/classic/index.js +28 -0
- package/lib/classic/units/external-ad.js +447 -0
- package/lib/classic/units/external-pause-ad.js +271 -0
- package/lib/classic/units/gam.js +108 -0
- package/lib/classic/units/parse-vast-extensions.js +54 -0
- package/lib/classic/units/pause-ad-card.js +131 -0
- package/lib/classic/units/pause-ad.js +70 -0
- package/lib/classic/units/ssai-ad-break.js +9 -0
- package/lib/classic/units/ssai-creative.js +140 -0
- package/lib/classic/units/ssai-detector.js +1167 -0
- package/lib/classic/units/ssai-preload.js +68 -0
- package/lib/classic/units/ssai-providers.js +289 -0
- package/lib/classic/units/vast-tracker.js +77 -0
- package/lib/index.d.ts +2 -1
- package/lib/index.js +6 -0
- package/lib/units/pause-ad-card.d.ts +49 -0
- package/lib/units/pause-ad-card.js +130 -0
- package/lib/units/pause-ad.d.ts +1 -18
- package/lib/units/pause-ad.js +2 -49
- package/package.json +42 -9
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var _rollupPluginBabelHelpers = require('../_virtual/_rollupPluginBabelHelpers.js');
|
|
4
|
+
require('core-js/modules/es.error.cause.js');
|
|
5
|
+
require('core-js/modules/es.array.iterator.js');
|
|
6
|
+
require('core-js/modules/es.array.sort.js');
|
|
7
|
+
require('core-js/modules/es.promise.js');
|
|
8
|
+
require('core-js/modules/esnext.iterator.constructor.js');
|
|
9
|
+
require('core-js/modules/esnext.iterator.filter.js');
|
|
10
|
+
require('core-js/modules/esnext.iterator.find.js');
|
|
11
|
+
require('core-js/modules/esnext.iterator.some.js');
|
|
12
|
+
require('core-js/modules/web.dom-collections.iterator.js');
|
|
13
|
+
var classic$1 = require('@streamlayer/sdk-web-interfaces/classic');
|
|
14
|
+
var classic = require('@streamlayer/sdk-web-logger/classic');
|
|
15
|
+
require('@streamlayer/sdk-web-api/classic');
|
|
16
|
+
var vastClient$1 = require('@dailymotion/vast-client');
|
|
17
|
+
|
|
18
|
+
const FULLBLEED_VIDEO_TEMPLATES = ['fullbleed-video', 'fullbleed-video-center', 'fullbleed-video-left'];
|
|
19
|
+
/**
|
|
20
|
+
* `indexOf` rather than `Array.prototype.includes`: this package ships to Chrome 32
|
|
21
|
+
* TV webviews and must stay ES5-compatible without a polyfill.
|
|
22
|
+
*/
|
|
23
|
+
const isFullbleedVideoTemplate = template => !!template && FULLBLEED_VIDEO_TEMPLATES.indexOf(template) !== -1;
|
|
24
|
+
const MP4_MIME = 'video/mp4';
|
|
25
|
+
// Programmatic spec (Index Exchange): bitrate window for a linear pause-ad MP4.
|
|
26
|
+
const MIN_BITRATE = 5000;
|
|
27
|
+
const MAX_BITRATE = 40000;
|
|
28
|
+
const TARGET_WIDTH = 1920;
|
|
29
|
+
const TARGET_HEIGHT = 1080;
|
|
30
|
+
const bitrateDistanceToWindow = bitrate => {
|
|
31
|
+
if (bitrate < MIN_BITRATE) return MIN_BITRATE - bitrate;
|
|
32
|
+
if (bitrate > MAX_BITRATE) return bitrate - MAX_BITRATE;
|
|
33
|
+
return 0;
|
|
34
|
+
};
|
|
35
|
+
const resolutionDistanceToTarget = file => {
|
|
36
|
+
var _file$width, _file$height;
|
|
37
|
+
return Math.abs(((_file$width = file.width) !== null && _file$width !== void 0 ? _file$width : 0) - TARGET_WIDTH) + Math.abs(((_file$height = file.height) !== null && _file$height !== void 0 ? _file$height : 0) - TARGET_HEIGHT);
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* Pick the best linear MP4 from a VAST creative's media files: prefer the
|
|
41
|
+
* highest bitrate inside the 5000–40000 kbps window; if none qualify, pick the
|
|
42
|
+
* one closest to the window. Ties break toward 1920×1080. Returns `undefined`
|
|
43
|
+
* when no MP4 is present so image-only VAST keeps working.
|
|
44
|
+
*/
|
|
45
|
+
const selectMp4MediaFile = (mediaFiles = []) => {
|
|
46
|
+
const mp4s = (mediaFiles !== null && mediaFiles !== void 0 ? mediaFiles : []).filter(file => !!(file !== null && file !== void 0 && file.fileURL) && file.mimeType === MP4_MIME);
|
|
47
|
+
if (mp4s.length === 0) {
|
|
48
|
+
return undefined;
|
|
49
|
+
}
|
|
50
|
+
const inWindow = mp4s.filter(file => {
|
|
51
|
+
var _file$bitrate;
|
|
52
|
+
const bitrate = (_file$bitrate = file.bitrate) !== null && _file$bitrate !== void 0 ? _file$bitrate : 0;
|
|
53
|
+
return bitrate >= MIN_BITRATE && bitrate <= MAX_BITRATE;
|
|
54
|
+
});
|
|
55
|
+
if (inWindow.length > 0) {
|
|
56
|
+
return [...inWindow].sort((a, b) => {
|
|
57
|
+
var _b$bitrate, _a$bitrate;
|
|
58
|
+
const byBitrate = ((_b$bitrate = b.bitrate) !== null && _b$bitrate !== void 0 ? _b$bitrate : 0) - ((_a$bitrate = a.bitrate) !== null && _a$bitrate !== void 0 ? _a$bitrate : 0);
|
|
59
|
+
return byBitrate !== 0 ? byBitrate : resolutionDistanceToTarget(a) - resolutionDistanceToTarget(b);
|
|
60
|
+
})[0];
|
|
61
|
+
}
|
|
62
|
+
return [...mp4s].sort((a, b) => {
|
|
63
|
+
var _a$bitrate2, _b$bitrate2;
|
|
64
|
+
const byWindow = bitrateDistanceToWindow((_a$bitrate2 = a.bitrate) !== null && _a$bitrate2 !== void 0 ? _a$bitrate2 : 0) - bitrateDistanceToWindow((_b$bitrate2 = b.bitrate) !== null && _b$bitrate2 !== void 0 ? _b$bitrate2 : 0);
|
|
65
|
+
return byWindow !== 0 ? byWindow : resolutionDistanceToTarget(a) - resolutionDistanceToTarget(b);
|
|
66
|
+
})[0];
|
|
67
|
+
};
|
|
68
|
+
const vastClient = new vastClient$1.VASTClient();
|
|
69
|
+
const log = classic.createLogger('ui:gam-static');
|
|
70
|
+
const preloadImage = src => {
|
|
71
|
+
return new Promise((resolve, reject) => {
|
|
72
|
+
const image = new Image();
|
|
73
|
+
image.onload = () => resolve(src);
|
|
74
|
+
image.onerror = () => reject();
|
|
75
|
+
image.src = src;
|
|
76
|
+
});
|
|
77
|
+
};
|
|
78
|
+
const externalPauseAd = instance => {
|
|
79
|
+
return (gamUrl, $enabled, options) => {
|
|
80
|
+
var _options$template;
|
|
81
|
+
return (
|
|
82
|
+
// `template` MUST be a key part: nanoquery joins parts with `''` (no separator)
|
|
83
|
+
// and its `cache` Map is global per transport, so two stores that produce the
|
|
84
|
+
// same joined key share data AND cross-trigger each other's `revalidate()`.
|
|
85
|
+
// The `tpl:` prefix keeps the url/template boundary unambiguous. Never let this
|
|
86
|
+
// part be `undefined` — an undefined part nulls the WHOLE key and permanently
|
|
87
|
+
// suspends fetching (that is exactly how `$enabled: false` works).
|
|
88
|
+
instance.transport.nanoquery.createFetcherStore([gamUrl, $enabled, `tpl:${(_options$template = options.template) !== null && _options$template !== void 0 ? _options$template : 'default'}`], {
|
|
89
|
+
dedupeTime: options.prefetch ? options.refetchInterval : Infinity,
|
|
90
|
+
revalidateInterval: options.refetchInterval > 0 ? options.refetchInterval : 0,
|
|
91
|
+
// A creative-type mismatch is DETERMINISTIC: this tag returned no usable linear
|
|
92
|
+
// MP4 and retrying it can never change that, so cancel nanoquery's BACKOFF RETRY
|
|
93
|
+
// LOOP (any falsy return does) instead of re-hitting the ad server for the rest of
|
|
94
|
+
// the session — with the default `pauseAdDelay: 0` the store is enabled from mount.
|
|
95
|
+
// This only cancels the retry loop: `revalidateInterval` still re-runs the fetcher
|
|
96
|
+
// on its timer, and `Advertisement.getExternalPauseAd` forces a `revalidate()` on
|
|
97
|
+
// every `$enabled` false → true edge, so with `pauseAdDelay > 0` the tag is still
|
|
98
|
+
// re-requested on every pause.
|
|
99
|
+
// Transient failures (network, CORS, malformed XML, 5xx) still retry; the backoff
|
|
100
|
+
// below mirrors nanoquery's `defaultOnErrorRetry` so the two templates stay on the
|
|
101
|
+
// same curve. The jitter is not decorative — it spreads out retries that would
|
|
102
|
+
// otherwise fire in lockstep when the failure is correlated across stores/clients
|
|
103
|
+
// (ad-server 5xx, CORS), which the `pauseAdDelay: 0` prefetch-at-mount makes more
|
|
104
|
+
// likely.
|
|
105
|
+
// On `default` this is `undefined`, i.e. exactly as if the option were not passed —
|
|
106
|
+
// true as long as the shared nanoquery instance sets no global `onErrorRetry` (an
|
|
107
|
+
// explicit `undefined` DOES shadow a global one, and only then does nanoquery's
|
|
108
|
+
// destructuring default restore `defaultOnErrorRetry`).
|
|
109
|
+
onErrorRetry: isFullbleedVideoTemplate(options.template) ? ({
|
|
110
|
+
error,
|
|
111
|
+
retryCount
|
|
112
|
+
}) => {
|
|
113
|
+
if (error instanceof Error && error.message === 'no_valid_ad') {
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
return ~~((Math.random() + 0.5) * (1 << Math.min(retryCount, 8))) * 2000;
|
|
117
|
+
} : undefined,
|
|
118
|
+
fetcher: function () {
|
|
119
|
+
var _ref = _rollupPluginBabelHelpers.asyncToGenerator(function* (adUrl) {
|
|
120
|
+
if (!adUrl || typeof adUrl !== 'string') {
|
|
121
|
+
throw new Error('ad url is empty');
|
|
122
|
+
}
|
|
123
|
+
classic$1.eventBus.emit('exposedPauseAd', {
|
|
124
|
+
action: 'load',
|
|
125
|
+
payload: {}
|
|
126
|
+
});
|
|
127
|
+
try {
|
|
128
|
+
var _options$template2, _parsedVAST$ads$find;
|
|
129
|
+
// `resolveAll: true` follows every `VASTAdTagURI` wrapper chain and resolves all
|
|
130
|
+
// Ads (the default only resolves the first standalone Ad and stops at the wrapper).
|
|
131
|
+
const parsedVAST = yield vastClient.get(adUrl, {
|
|
132
|
+
resolveAll: true
|
|
133
|
+
});
|
|
134
|
+
const template = (_options$template2 = options.template) !== null && _options$template2 !== void 0 ? _options$template2 : 'default';
|
|
135
|
+
// `fullbleed-video` needs an ad that actually carries a usable linear MP4. With
|
|
136
|
+
// `resolveAll: true` the response can be a pod/wrapper chain, so prefer the first
|
|
137
|
+
// ad that satisfies the template and fall back to the first ad with any creative
|
|
138
|
+
// (which preserves the existing `no_valid_ad` error and event sequencing).
|
|
139
|
+
const validAd = isFullbleedVideoTemplate(template) ? (_parsedVAST$ads$find = parsedVAST.ads.find(ad => ad.creatives.some(creative => {
|
|
140
|
+
var _selectMp4MediaFile;
|
|
141
|
+
return creative.type === 'linear' && (// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
142
|
+
// @ts-ignore vast-client linear creative carries mediaFiles
|
|
143
|
+
(_selectMp4MediaFile = selectMp4MediaFile(creative.mediaFiles)) === null || _selectMp4MediaFile === void 0 ? void 0 : _selectMp4MediaFile.fileURL);
|
|
144
|
+
}))) !== null && _parsedVAST$ads$find !== void 0 ? _parsedVAST$ads$find : parsedVAST.ads.find(ads => ads.creatives.length > 0) : parsedVAST.ads.find(ads => ads.creatives.length > 0);
|
|
145
|
+
if (validAd) {
|
|
146
|
+
var _companion$variations, _variation$staticReso;
|
|
147
|
+
const parentId = validAd.id || '';
|
|
148
|
+
log.debug(validAd, 'validAd');
|
|
149
|
+
// The HOST decides which creative wins (SC-322-2). `fullbleed-video` asks
|
|
150
|
+
// for the linear MP4 explicitly, so it is resolved FIRST and never falls
|
|
151
|
+
// back to a static image — the nonlinear/companion branches below `return`
|
|
152
|
+
// early, so without this a VAST carrying BOTH an image and a linear MP4
|
|
153
|
+
// (e.g. Google `single_ad_samples`) would never reach the linear creative.
|
|
154
|
+
// `default` keeps the pre-SC-322 nonlinear → companion order below and can
|
|
155
|
+
// never produce a video.
|
|
156
|
+
if (isFullbleedVideoTemplate(template)) {
|
|
157
|
+
const linear = validAd.creatives.find(creative => {
|
|
158
|
+
var _selectMp4MediaFile2;
|
|
159
|
+
return creative.type === 'linear' && (// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
160
|
+
// @ts-ignore vast-client linear creative carries mediaFiles
|
|
161
|
+
(_selectMp4MediaFile2 = selectMp4MediaFile(creative.mediaFiles)) === null || _selectMp4MediaFile2 === void 0 ? void 0 : _selectMp4MediaFile2.fileURL);
|
|
162
|
+
});
|
|
163
|
+
if (linear) {
|
|
164
|
+
log.debug(linear, 'linear');
|
|
165
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
166
|
+
// @ts-ignore vast-client linear creative carries mediaFiles
|
|
167
|
+
const mediaFile = selectMp4MediaFile(linear.mediaFiles);
|
|
168
|
+
if (mediaFile !== null && mediaFile !== void 0 && mediaFile.fileURL) {
|
|
169
|
+
const id = linear.adId || linear.id || parentId;
|
|
170
|
+
classic$1.eventBus.emit('exposedPauseAd', {
|
|
171
|
+
action: 'loaded',
|
|
172
|
+
payload: {
|
|
173
|
+
id,
|
|
174
|
+
parentId
|
|
175
|
+
}
|
|
176
|
+
});
|
|
177
|
+
return {
|
|
178
|
+
id,
|
|
179
|
+
parentId,
|
|
180
|
+
url: adUrl,
|
|
181
|
+
backgroundVideoSrc: mediaFile.fileURL,
|
|
182
|
+
ad: validAd,
|
|
183
|
+
creative: linear
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
// Host asked for full-bleed video and this VAST carries no usable linear
|
|
188
|
+
// MP4 → fail closed so `ExposedPauseAd`'s error effect auto-closes the ad,
|
|
189
|
+
// exactly like any other unusable VAST. See §5 of the brief.
|
|
190
|
+
throw new Error('no_valid_ad');
|
|
191
|
+
}
|
|
192
|
+
const nonlinear = validAd.creatives.find(creative => creative.type === 'nonlinear');
|
|
193
|
+
if (nonlinear) {
|
|
194
|
+
var _nonlinear$variations;
|
|
195
|
+
const id = nonlinear.adId || nonlinear.id || parentId;
|
|
196
|
+
log.debug(nonlinear, 'nonlinear');
|
|
197
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
198
|
+
// @ts-ignore
|
|
199
|
+
const variation = (_nonlinear$variations = nonlinear.variations) === null || _nonlinear$variations === void 0 ? void 0 : _nonlinear$variations[0];
|
|
200
|
+
const staticResource = variation === null || variation === void 0 ? void 0 : variation.staticResource;
|
|
201
|
+
const adUrlTemplate = variation === null || variation === void 0 ? void 0 : variation.nonlinearClickThroughURLTemplate;
|
|
202
|
+
if (staticResource) {
|
|
203
|
+
yield preloadImage(staticResource);
|
|
204
|
+
classic$1.eventBus.emit('exposedPauseAd', {
|
|
205
|
+
action: 'loaded',
|
|
206
|
+
payload: {
|
|
207
|
+
id,
|
|
208
|
+
parentId
|
|
209
|
+
}
|
|
210
|
+
});
|
|
211
|
+
return {
|
|
212
|
+
id,
|
|
213
|
+
parentId,
|
|
214
|
+
url: adUrl,
|
|
215
|
+
imageSrc: staticResource,
|
|
216
|
+
adUrl: adUrlTemplate,
|
|
217
|
+
ad: validAd,
|
|
218
|
+
creative: nonlinear,
|
|
219
|
+
variation
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
const companion = validAd.creatives.find(creative => creative.type === 'companion');
|
|
224
|
+
log.debug(companion, 'companion');
|
|
225
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
226
|
+
// @ts-ignore
|
|
227
|
+
const variation = companion === null || companion === void 0 || (_companion$variations = companion.variations) === null || _companion$variations === void 0 ? void 0 : _companion$variations[0];
|
|
228
|
+
const staticResource = variation === null || variation === void 0 || (_variation$staticReso = variation.staticResources) === null || _variation$staticReso === void 0 || (_variation$staticReso = _variation$staticReso[0]) === null || _variation$staticReso === void 0 ? void 0 : _variation$staticReso.url;
|
|
229
|
+
const adUrlTemplate = variation === null || variation === void 0 ? void 0 : variation.companionClickThroughURLTemplate;
|
|
230
|
+
// `&& companion` is for TS narrowing only (companion.adId access below);
|
|
231
|
+
// it is runtime-redundant since staticResource derives from companion.
|
|
232
|
+
if (staticResource && companion) {
|
|
233
|
+
const id = companion.adId || companion.id || parentId;
|
|
234
|
+
yield preloadImage(staticResource);
|
|
235
|
+
classic$1.eventBus.emit('exposedPauseAd', {
|
|
236
|
+
action: 'loaded',
|
|
237
|
+
payload: {
|
|
238
|
+
id,
|
|
239
|
+
parentId
|
|
240
|
+
}
|
|
241
|
+
});
|
|
242
|
+
return {
|
|
243
|
+
id,
|
|
244
|
+
parentId,
|
|
245
|
+
url: adUrl,
|
|
246
|
+
imageSrc: staticResource,
|
|
247
|
+
adUrl: adUrlTemplate,
|
|
248
|
+
ad: validAd,
|
|
249
|
+
creative: companion,
|
|
250
|
+
variation
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
throw new Error('no_valid_ad');
|
|
255
|
+
} catch (err) {
|
|
256
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
257
|
+
// @ts-expect-error
|
|
258
|
+
if (err.message !== 'no_valid_ad' && err.message !== 'ad url is empty') {
|
|
259
|
+
console.error(err);
|
|
260
|
+
}
|
|
261
|
+
throw err;
|
|
262
|
+
}
|
|
263
|
+
});
|
|
264
|
+
return function fetcher(_x) {
|
|
265
|
+
return _ref.apply(this, arguments);
|
|
266
|
+
};
|
|
267
|
+
}()
|
|
268
|
+
})
|
|
269
|
+
);
|
|
270
|
+
};
|
|
271
|
+
};
|
|
272
|
+
|
|
273
|
+
exports.externalPauseAd = externalPauseAd;
|
|
274
|
+
exports.isFullbleedVideoTemplate = isFullbleedVideoTemplate;
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var _rollupPluginBabelHelpers = require('../_virtual/_rollupPluginBabelHelpers.js');
|
|
4
|
+
require('core-js/modules/es.array.iterator.js');
|
|
5
|
+
require('core-js/modules/web.dom-collections.iterator.js');
|
|
6
|
+
require('core-js/modules/web.url.js');
|
|
7
|
+
require('core-js/modules/web.url.to-json.js');
|
|
8
|
+
require('core-js/modules/web.url-search-params.js');
|
|
9
|
+
require('core-js/modules/web.url-search-params.delete.js');
|
|
10
|
+
require('core-js/modules/web.url-search-params.has.js');
|
|
11
|
+
require('core-js/modules/web.url-search-params.size.js');
|
|
12
|
+
var classic = require('@streamlayer/sdk-web-storage/classic');
|
|
13
|
+
var uuid = require('uuid');
|
|
14
|
+
|
|
15
|
+
const urlIsNotGam = url => {
|
|
16
|
+
return url.origin !== 'https://pubads.g.doubleclick.net';
|
|
17
|
+
};
|
|
18
|
+
const gam = (url, adContainer) => {
|
|
19
|
+
const parsedUrl = (() => {
|
|
20
|
+
try {
|
|
21
|
+
const parsedUrl = new URL(url);
|
|
22
|
+
return parsedUrl;
|
|
23
|
+
} catch (_err) {
|
|
24
|
+
return undefined;
|
|
25
|
+
}
|
|
26
|
+
})();
|
|
27
|
+
// Non-GAM VAST/VMAP URLs carry no GAM ad-request params and need no PAL nonce, so
|
|
28
|
+
// resolve them directly. PAL is only required for pubads.g.doubleclick.net requests —
|
|
29
|
+
// raw-VAST consumers (e.g. injected sidebar / l-bar) must not depend on the Google PAL
|
|
30
|
+
// SDK being loaded, so this must run before touching `goog`.
|
|
31
|
+
if (parsedUrl && urlIsNotGam(parsedUrl)) {
|
|
32
|
+
// eslint-disable-next-line require-await
|
|
33
|
+
return /*#__PURE__*/_rollupPluginBabelHelpers.asyncToGenerator(function* () {
|
|
34
|
+
return {
|
|
35
|
+
url: parsedUrl
|
|
36
|
+
};
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
// @ts-expect-error goog is injected by the externally-loaded Google PAL SDK
|
|
40
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
41
|
+
const pal = typeof goog === 'undefined' ? undefined : goog.pal;
|
|
42
|
+
if (!pal || !parsedUrl) {
|
|
43
|
+
// eslint-disable-next-line require-await
|
|
44
|
+
return /*#__PURE__*/_rollupPluginBabelHelpers.asyncToGenerator(function* () {
|
|
45
|
+
return undefined;
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
const consentSettings = new pal.ConsentSettings();
|
|
49
|
+
consentSettings.allowStorage = false;
|
|
50
|
+
const nonceLoader = new pal.NonceLoader(consentSettings);
|
|
51
|
+
const generateUrl = /*#__PURE__*/function () {
|
|
52
|
+
var _ref3 = _rollupPluginBabelHelpers.asyncToGenerator(function* () {
|
|
53
|
+
try {
|
|
54
|
+
var _adContainer$clientHe, _adContainer$clientWi;
|
|
55
|
+
const parsedUrl = new URL(url);
|
|
56
|
+
const request = new pal.NonceRequest();
|
|
57
|
+
let deviceId = classic.safeLocalStorage().getItem('sl-device-id') || uuid.v4();
|
|
58
|
+
let sessionId = classic.safeSessionStorage().getItem('sl-ad-session-id');
|
|
59
|
+
if (!sessionId) {
|
|
60
|
+
sessionId = uuid.v4();
|
|
61
|
+
classic.safeSessionStorage().setItem('sl-ad-session-id', sessionId);
|
|
62
|
+
}
|
|
63
|
+
if (!uuid.validate(deviceId)) {
|
|
64
|
+
deviceId = uuid.v5(`https://${deviceId}`, uuid.v5.URL);
|
|
65
|
+
}
|
|
66
|
+
if (!parsedUrl.searchParams.get('url')) {
|
|
67
|
+
parsedUrl.searchParams.set('url', 'https://streamlayer.io');
|
|
68
|
+
}
|
|
69
|
+
if (!parsedUrl.searchParams.get('description_url')) {
|
|
70
|
+
parsedUrl.searchParams.set('description_url', 'https://streamlayer.io');
|
|
71
|
+
}
|
|
72
|
+
request.adWillAutoPlay = parsedUrl.searchParams.get('vpa') === 'auto';
|
|
73
|
+
request.adWillPlayMuted = parsedUrl.searchParams.get('vpmute') === '1';
|
|
74
|
+
request.continuousPlayback = true;
|
|
75
|
+
request.descriptionUrl = parsedUrl.searchParams.get('description_url');
|
|
76
|
+
request.iconsSupported = true;
|
|
77
|
+
request.sessionId = deviceId;
|
|
78
|
+
// Player support for VPAID 2.0, OMID 1.0, and SIMID 1.1
|
|
79
|
+
request.supportedApiFrameworks = '2,7,9';
|
|
80
|
+
request.videoHeight = (_adContainer$clientHe = adContainer === null || adContainer === void 0 ? void 0 : adContainer.clientHeight) !== null && _adContainer$clientHe !== void 0 ? _adContainer$clientHe : 0;
|
|
81
|
+
request.videoWidth = (_adContainer$clientWi = adContainer === null || adContainer === void 0 ? void 0 : adContainer.clientWidth) !== null && _adContainer$clientWi !== void 0 ? _adContainer$clientWi : 0;
|
|
82
|
+
const nonceManager = yield nonceLoader.loadNonceManager(request);
|
|
83
|
+
if (parsedUrl.searchParams.get('is_lat') === '[placeholder]') {
|
|
84
|
+
parsedUrl.searchParams.set('is_lat', '1');
|
|
85
|
+
}
|
|
86
|
+
if (parsedUrl.searchParams.get('rdid') === '[placeholder]') {
|
|
87
|
+
parsedUrl.searchParams.set('rdid', deviceId);
|
|
88
|
+
}
|
|
89
|
+
if (parsedUrl.searchParams.get('correlator') === '[placeholder]') {
|
|
90
|
+
parsedUrl.searchParams.set('correlator', sessionId);
|
|
91
|
+
}
|
|
92
|
+
parsedUrl.searchParams.set('wta', '1');
|
|
93
|
+
parsedUrl.searchParams.set('givn', nonceManager.getNonce());
|
|
94
|
+
return {
|
|
95
|
+
url: parsedUrl,
|
|
96
|
+
manager: nonceManager
|
|
97
|
+
};
|
|
98
|
+
} catch (adError) {
|
|
99
|
+
console.log('AdsManager could not be started', adError);
|
|
100
|
+
return undefined;
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
return function generateUrl() {
|
|
104
|
+
return _ref3.apply(this, arguments);
|
|
105
|
+
};
|
|
106
|
+
}();
|
|
107
|
+
return generateUrl;
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
exports.gam = gam;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
require('core-js/modules/esnext.iterator.constructor.js');
|
|
4
|
+
require('core-js/modules/esnext.iterator.filter.js');
|
|
5
|
+
require('core-js/modules/esnext.iterator.find.js');
|
|
6
|
+
require('core-js/modules/esnext.iterator.map.js');
|
|
7
|
+
var interactive_common_pb = require('@streamlayer/sl-eslib/interactive/interactive.common_pb');
|
|
8
|
+
|
|
9
|
+
const MAP_EXTENSION_NAME_AD_TYPE = {
|
|
10
|
+
DoubleBox: interactive_common_pb.AdType.SIDE_BY_SIDE
|
|
11
|
+
};
|
|
12
|
+
function parseVastExtension(root) {
|
|
13
|
+
var _child$value2, _child2, _child$value3, _child3, _child$value4, _child4, _backgrounds$find, _backgrounds$find2, _logos$find;
|
|
14
|
+
if (!root) {
|
|
15
|
+
return undefined;
|
|
16
|
+
}
|
|
17
|
+
const child = (node, name) => node.children.find(c => c.name === name);
|
|
18
|
+
const childrenByName = (node, name) => node.children.filter(c => c.name === name);
|
|
19
|
+
const urlOf = node => {
|
|
20
|
+
var _child$value, _child;
|
|
21
|
+
return (_child$value = (_child = child(node, 'Url')) === null || _child === void 0 ? void 0 : _child.value) !== null && _child$value !== void 0 ? _child$value : '';
|
|
22
|
+
};
|
|
23
|
+
const num = v => v == null ? NaN : Number(v);
|
|
24
|
+
const adName = (_child$value2 = (_child2 = child(root, 'AdName')) === null || _child2 === void 0 ? void 0 : _child2.value) !== null && _child$value2 !== void 0 ? _child$value2 : '';
|
|
25
|
+
const backgroundsNode = child(root, 'Backgrounds');
|
|
26
|
+
const backgrounds = backgroundsNode ? childrenByName(backgroundsNode, 'Background').map(bg => ({
|
|
27
|
+
orientation: bg.attributes.orientation,
|
|
28
|
+
width: num(bg.attributes.width),
|
|
29
|
+
height: num(bg.attributes.height),
|
|
30
|
+
type: bg.attributes.type,
|
|
31
|
+
url: urlOf(bg)
|
|
32
|
+
})) : [];
|
|
33
|
+
const logosNode = child(root, 'Logos');
|
|
34
|
+
const logos = logosNode ? childrenByName(logosNode, 'Logo').map(logo => ({
|
|
35
|
+
role: logo.attributes.role,
|
|
36
|
+
width: num(logo.attributes.width),
|
|
37
|
+
height: num(logo.attributes.height),
|
|
38
|
+
type: logo.attributes.type,
|
|
39
|
+
url: urlOf(logo)
|
|
40
|
+
})) : [];
|
|
41
|
+
const ctaNode = child(root, 'CTA');
|
|
42
|
+
const cta = {
|
|
43
|
+
label: ctaNode ? (_child$value3 = (_child3 = child(ctaNode, 'Label')) === null || _child3 === void 0 ? void 0 : _child3.value) !== null && _child$value3 !== void 0 ? _child$value3 : '' : '',
|
|
44
|
+
type: ctaNode ? (_child$value4 = (_child4 = child(ctaNode, 'Type')) === null || _child4 === void 0 ? void 0 : _child4.value) !== null && _child$value4 !== void 0 ? _child$value4 : '' : ''
|
|
45
|
+
};
|
|
46
|
+
return {
|
|
47
|
+
adName,
|
|
48
|
+
adType: MAP_EXTENSION_NAME_AD_TYPE[root.name] || interactive_common_pb.AdType.SIDE_BY_SIDE,
|
|
49
|
+
landscapeBackground: (_backgrounds$find = backgrounds.find(b => b.orientation === 'landscape')) === null || _backgrounds$find === void 0 ? void 0 : _backgrounds$find.url,
|
|
50
|
+
portraitBackground: (_backgrounds$find2 = backgrounds.find(b => b.orientation === 'portrait')) === null || _backgrounds$find2 === void 0 ? void 0 : _backgrounds$find2.url,
|
|
51
|
+
sponsorLogo: (_logos$find = logos.find(l => l.role === 'header')) === null || _logos$find === void 0 ? void 0 : _logos$find.url,
|
|
52
|
+
ctaLabel: cta.label
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
exports.parseVastExtension = parseVastExtension;
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
require('core-js/modules/esnext.iterator.constructor.js');
|
|
4
|
+
require('core-js/modules/esnext.iterator.filter.js');
|
|
5
|
+
require('core-js/modules/esnext.iterator.map.js');
|
|
6
|
+
var interactive_common_pb = require('@streamlayer/sl-eslib/interactive/interactive.common_pb');
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Insight body cap (SC-353). Studio enforces it on the Additional Content field; the
|
|
10
|
+
* SDK clamps as well so a card authored before the limit landed cannot overflow the
|
|
11
|
+
* screen — the format is explicitly designed to fit without scrolling.
|
|
12
|
+
*/
|
|
13
|
+
const INSIGHT_BODY_MAX_LENGTH = 600;
|
|
14
|
+
/**
|
|
15
|
+
* Sponsor logo for a factoid card. Same General-tab opt-in as the promotion path below,
|
|
16
|
+
* but the logo lives on `question.promotion.sponsor` (a factoid card carries no
|
|
17
|
+
* `PromotionOptions`) and Studio writes the uploaded file to `logo`, not `centerLogo`.
|
|
18
|
+
*
|
|
19
|
+
* `logo` therefore WINS over `centerLogo`: the latter is an optional field carried over from the
|
|
20
|
+
* notification/header surfaces, so a card that kept a stale `centerLogo` from an earlier revision
|
|
21
|
+
* would otherwise render it instead of the Insight logo the author just uploaded.
|
|
22
|
+
*/
|
|
23
|
+
const resolveInsightSponsorLogo = question => {
|
|
24
|
+
var _question$promotion, _question$promotion2;
|
|
25
|
+
const sponsor = (_question$promotion = question.promotion) === null || _question$promotion === void 0 ? void 0 : _question$promotion.sponsor;
|
|
26
|
+
if ((sponsor === null || sponsor === void 0 ? void 0 : sponsor.logoPosition) === interactive_common_pb.NotificationSponsorLogoMode.NOTIFICATION_SPONSOR_LOGO_NONE) {
|
|
27
|
+
return undefined;
|
|
28
|
+
}
|
|
29
|
+
return (sponsor === null || sponsor === void 0 ? void 0 : sponsor.useGeneralLogo) === interactive_common_pb.UseGeneralLogo.ENABLED ? ((_question$promotion2 = question.promotion) === null || _question$promotion2 === void 0 || (_question$promotion2 = _question$promotion2.generalTabMetadata) === null || _question$promotion2 === void 0 ? void 0 : _question$promotion2.advertiserTransparentLogo) || undefined : (sponsor === null || sponsor === void 0 ? void 0 : sponsor.logo) || (sponsor === null || sponsor === void 0 ? void 0 : sponsor.centerLogo) || undefined;
|
|
30
|
+
};
|
|
31
|
+
const MEDIA_MODE_BY_PROTO = {
|
|
32
|
+
[interactive_common_pb.FactoidMediaMode.IMAGE]: 'image',
|
|
33
|
+
[interactive_common_pb.FactoidMediaMode.CIRCLE]: 'circle'
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Insight Pause Ad, Full Bleed (SC-353). A factoid card, so none of the promotion
|
|
37
|
+
* plumbing above applies: the heading is the question subject, the body is
|
|
38
|
+
* `FactoidOptions.body`, and the sponsor logo comes off `question.promotion.sponsor`.
|
|
39
|
+
*
|
|
40
|
+
* `FACTOID_MEDIA_MODE_VIDEO` is deliberately treated as "no media": the full-bleed
|
|
41
|
+
* format has no player chrome and no way to start playback, unlike the sidebar variant.
|
|
42
|
+
*/
|
|
43
|
+
const parseInsightPauseAdCard = question => {
|
|
44
|
+
var _question$options, _MEDIA_MODE_BY_PROTO$, _question$backgroundI;
|
|
45
|
+
const factoidOptions = ((_question$options = question.options) === null || _question$options === void 0 ? void 0 : _question$options.options.case) === 'factoid' ? question.options.options.value : undefined;
|
|
46
|
+
if (!factoidOptions) {
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
const mediaMode = (_MEDIA_MODE_BY_PROTO$ = MEDIA_MODE_BY_PROTO[factoidOptions.mode]) !== null && _MEDIA_MODE_BY_PROTO$ !== void 0 ? _MEDIA_MODE_BY_PROTO$ : 'none';
|
|
50
|
+
const body = factoidOptions.body.slice(0, INSIGHT_BODY_MAX_LENGTH);
|
|
51
|
+
return {
|
|
52
|
+
id: question.id,
|
|
53
|
+
parentId: question.eventId || question.id,
|
|
54
|
+
// `landscapeUrl` ONLY. `portraitUrl` keeps stale artwork from earlier revisions of the
|
|
55
|
+
// card that Studio no longer shows in its Background Image slot, so falling back to it
|
|
56
|
+
// paints a background the author has already removed. Unlike the standard card, an
|
|
57
|
+
// Insight one renders fine with no background at all — its copy is the ad.
|
|
58
|
+
backgroundImageSrc: ((_question$backgroundI = question.backgroundImage) === null || _question$backgroundI === void 0 ? void 0 : _question$backgroundI.landscapeUrl) || undefined,
|
|
59
|
+
kind: 'insight',
|
|
60
|
+
layout: 'left',
|
|
61
|
+
isInteractive: false,
|
|
62
|
+
logoSrc: resolveInsightSponsorLogo(question),
|
|
63
|
+
title: question.subject || undefined,
|
|
64
|
+
description: body || undefined,
|
|
65
|
+
mediaSrc: mediaMode === 'none' ? undefined : factoidOptions.image || undefined,
|
|
66
|
+
mediaMode
|
|
67
|
+
};
|
|
68
|
+
};
|
|
69
|
+
const parsePauseAdCard = question => {
|
|
70
|
+
var _question$options2, _question$backgroundI2, _question$options3, _pauseAdOptions$layou, _question$promotion3, _promotionOptions$ban, _promotionOptions$ban2, _pauseAdOptions$qrCod, _pauseAdOptions$qrCod2;
|
|
71
|
+
if (((_question$options2 = question.options) === null || _question$options2 === void 0 ? void 0 : _question$options2.options.case) === 'factoid') {
|
|
72
|
+
return parseInsightPauseAdCard(question);
|
|
73
|
+
}
|
|
74
|
+
const backgroundImageSrc = (_question$backgroundI2 = question.backgroundImage) === null || _question$backgroundI2 === void 0 ? void 0 : _question$backgroundI2.landscapeUrl;
|
|
75
|
+
if (!backgroundImageSrc) {
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
const promotionOptions = ((_question$options3 = question.options) === null || _question$options3 === void 0 ? void 0 : _question$options3.options.case) === 'promotion' ? question.options.options.value : undefined;
|
|
79
|
+
const pauseAdOptions = promotionOptions === null || promotionOptions === void 0 ? void 0 : promotionOptions.pauseAdOptions;
|
|
80
|
+
const optionsSponsor = promotionOptions === null || promotionOptions === void 0 ? void 0 : promotionOptions.sponsor;
|
|
81
|
+
const layoutProto = (_pauseAdOptions$layou = pauseAdOptions === null || pauseAdOptions === void 0 ? void 0 : pauseAdOptions.layout) !== null && _pauseAdOptions$layou !== void 0 ? _pauseAdOptions$layou : interactive_common_pb.PauseAdLayout.UNSET;
|
|
82
|
+
const layout = layoutProto === interactive_common_pb.PauseAdLayout.CENTER ? 'center' : layoutProto === interactive_common_pb.PauseAdLayout.IMAGE ? 'image' : 'left';
|
|
83
|
+
const isInteractive = (promotionOptions === null || promotionOptions === void 0 ? void 0 : promotionOptions.buttonType) === interactive_common_pb.PromotionButtonType.SEND_TO_USER;
|
|
84
|
+
const hasQr = (pauseAdOptions === null || pauseAdOptions === void 0 ? void 0 : pauseAdOptions.qrCode) === interactive_common_pb.QRCode.QR_CODE_ENABLED && !!(pauseAdOptions !== null && pauseAdOptions !== void 0 && pauseAdOptions.qrCodeUrl);
|
|
85
|
+
let ctaLabel;
|
|
86
|
+
let ctaUrl;
|
|
87
|
+
if (isInteractive && (promotionOptions === null || promotionOptions === void 0 ? void 0 : promotionOptions.buttonOptions.case) === 'sendToUserOptions') {
|
|
88
|
+
const accountInfo = promotionOptions.buttonOptions.value.userAccountInfo;
|
|
89
|
+
const isPhone = accountInfo === interactive_common_pb.UserAccountInfo.PHONE;
|
|
90
|
+
ctaUrl = isPhone ? 'sendToPhone:' : 'sendToEmail:';
|
|
91
|
+
ctaLabel = isPhone ? 'Send info to account phone' : 'Send info to account email';
|
|
92
|
+
}
|
|
93
|
+
const logoSrc = (optionsSponsor === null || optionsSponsor === void 0 ? void 0 : optionsSponsor.logoPosition) === interactive_common_pb.NotificationSponsorLogoMode.NOTIFICATION_SPONSOR_LOGO_NONE ? undefined : (optionsSponsor === null || optionsSponsor === void 0 ? void 0 : optionsSponsor.useGeneralLogo) === interactive_common_pb.UseGeneralLogo.ENABLED ? ((_question$promotion3 = question.promotion) === null || _question$promotion3 === void 0 || (_question$promotion3 = _question$promotion3.generalTabMetadata) === null || _question$promotion3 === void 0 ? void 0 : _question$promotion3.advertiserTransparentLogo) || undefined : (optionsSponsor === null || optionsSponsor === void 0 ? void 0 : optionsSponsor.centerLogo) || undefined;
|
|
94
|
+
return {
|
|
95
|
+
id: question.id,
|
|
96
|
+
parentId: question.eventId || question.id,
|
|
97
|
+
backgroundImageSrc,
|
|
98
|
+
layout,
|
|
99
|
+
logoSrc,
|
|
100
|
+
title: (promotionOptions === null || promotionOptions === void 0 || (_promotionOptions$ban = promotionOptions.banner) === null || _promotionOptions$ban === void 0 ? void 0 : _promotionOptions$ban.title) || undefined,
|
|
101
|
+
description: (promotionOptions === null || promotionOptions === void 0 || (_promotionOptions$ban2 = promotionOptions.banner) === null || _promotionOptions$ban2 === void 0 ? void 0 : _promotionOptions$ban2.body) || undefined,
|
|
102
|
+
isInteractive,
|
|
103
|
+
ctaLabel: ctaLabel || undefined,
|
|
104
|
+
ctaUrl: ctaUrl || undefined,
|
|
105
|
+
qrCodeUrl: hasQr ? (_pauseAdOptions$qrCod = pauseAdOptions === null || pauseAdOptions === void 0 ? void 0 : pauseAdOptions.qrCodeUrl) !== null && _pauseAdOptions$qrCod !== void 0 ? _pauseAdOptions$qrCod : undefined : undefined,
|
|
106
|
+
qrCodeLabel: hasQr ? (_pauseAdOptions$qrCod2 = pauseAdOptions === null || pauseAdOptions === void 0 ? void 0 : pauseAdOptions.qrCodeLabel) !== null && _pauseAdOptions$qrCod2 !== void 0 ? _pauseAdOptions$qrCod2 : undefined : undefined
|
|
107
|
+
};
|
|
108
|
+
};
|
|
109
|
+
/**
|
|
110
|
+
* Which cards from `pauseAdList` the SDK can actually draw. Both renderers are full bleed; a
|
|
111
|
+
* Sidebar-format pause ad has no chrome yet and is skipped.
|
|
112
|
+
*/
|
|
113
|
+
const isFullBleedPauseAd = question => {
|
|
114
|
+
var _question$options4, _question$options5;
|
|
115
|
+
if (((_question$options4 = question.options) === null || _question$options4 === void 0 ? void 0 : _question$options4.options.case) === 'factoid') {
|
|
116
|
+
// Insight Pause Ad (SC-353). Studio does not write `CardFormat` on these cards yet
|
|
117
|
+
// (SLW-7062), so an UNSET format is taken as Full Bleed — the only Insight pause-ad chrome
|
|
118
|
+
// the SDK has. An explicit Sidebar stays out. Drop the UNSET arm once Studio sets the field.
|
|
119
|
+
return question.format === interactive_common_pb.CardFormat.FULL_BLEED || question.format === interactive_common_pb.CardFormat.UNSET;
|
|
120
|
+
}
|
|
121
|
+
const promotionOptions = ((_question$options5 = question.options) === null || _question$options5 === void 0 ? void 0 : _question$options5.options.case) === 'promotion' ? question.options.options.value : undefined;
|
|
122
|
+
return (promotionOptions === null || promotionOptions === void 0 ? void 0 : promotionOptions.format) === interactive_common_pb.PromotionFormat.FULL_BLEED || question.format === interactive_common_pb.CardFormat.FULL_BLEED;
|
|
123
|
+
};
|
|
124
|
+
/**
|
|
125
|
+
* The `pauseAdList` response, shaped for the renderers: keep the cards the SDK has chrome for,
|
|
126
|
+
* turn each into a `PauseAdCard`, drop the ones that cannot be drawn.
|
|
127
|
+
*
|
|
128
|
+
* Pure on purpose — `pause-ad.ts` owns the store and the transport, this owns the card shapes,
|
|
129
|
+
* and only this half needs unit tests.
|
|
130
|
+
*/
|
|
131
|
+
const parsePauseAdCards = questions => questions.filter(isFullBleedPauseAd).map(parsePauseAdCard).filter(card => card !== null);
|
|
132
|
+
|
|
133
|
+
exports.INSIGHT_BODY_MAX_LENGTH = INSIGHT_BODY_MAX_LENGTH;
|
|
134
|
+
exports.parsePauseAdCards = parsePauseAdCards;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var _rollupPluginBabelHelpers = require('../_virtual/_rollupPluginBabelHelpers.js');
|
|
4
|
+
var classic = require('@streamlayer/sdk-web-logger/classic');
|
|
5
|
+
var nanostores = require('nanostores');
|
|
6
|
+
require('@streamlayer/sdk-web-api/classic');
|
|
7
|
+
require('@streamlayer/sdk-web-core/classic/store');
|
|
8
|
+
var interactive_feed_pb = require('@streamlayer/sl-eslib/interactive/feed/interactive.feed_pb');
|
|
9
|
+
var pauseAdCard = require('./pause-ad-card.js');
|
|
10
|
+
|
|
11
|
+
const log = classic.createLogger('advertisement:pause-ad');
|
|
12
|
+
const pauseAdListStore = instance => {
|
|
13
|
+
const $slStreamId = instance.stores.slStreamId.getAtomStore();
|
|
14
|
+
return ($enabled, options) => {
|
|
15
|
+
const {
|
|
16
|
+
client,
|
|
17
|
+
queryKey
|
|
18
|
+
} = instance.transport.createPromiseClient(interactive_feed_pb.Feed, {
|
|
19
|
+
method: 'pauseAdList',
|
|
20
|
+
params: [$slStreamId, $enabled]
|
|
21
|
+
});
|
|
22
|
+
const $list = instance.transport.nanoquery.createFetcherStore(queryKey, {
|
|
23
|
+
dedupeTime: 0,
|
|
24
|
+
revalidateInterval: options.refetchInterval > 0 ? options.refetchInterval : 0,
|
|
25
|
+
fetcher: function () {
|
|
26
|
+
var _ref = _rollupPluginBabelHelpers.asyncToGenerator(function* (_, __, eventId, isEnabled) {
|
|
27
|
+
var _res$data;
|
|
28
|
+
if (!eventId || typeof eventId !== 'string' || !isEnabled) {
|
|
29
|
+
return [];
|
|
30
|
+
}
|
|
31
|
+
log.debug({
|
|
32
|
+
eventId
|
|
33
|
+
}, 'fetching pause ad list');
|
|
34
|
+
const res = yield client.pauseAdList({
|
|
35
|
+
eventId
|
|
36
|
+
});
|
|
37
|
+
const cards = pauseAdCard.parsePauseAdCards((_res$data = res.data) !== null && _res$data !== void 0 ? _res$data : []);
|
|
38
|
+
log.debug({
|
|
39
|
+
count: cards.length
|
|
40
|
+
}, 'parsed %d fullbleed pause ad cards');
|
|
41
|
+
return cards;
|
|
42
|
+
});
|
|
43
|
+
return function fetcher(_x, _x2, _x3, _x4) {
|
|
44
|
+
return _ref.apply(this, arguments);
|
|
45
|
+
};
|
|
46
|
+
}()
|
|
47
|
+
});
|
|
48
|
+
const $index = nanostores.atom(0);
|
|
49
|
+
const next = () => {
|
|
50
|
+
const list = $list.get().data;
|
|
51
|
+
if (!(list !== null && list !== void 0 && list.length)) return;
|
|
52
|
+
$index.set(($index.get() + 1) % list.length);
|
|
53
|
+
};
|
|
54
|
+
const reset = () => {
|
|
55
|
+
$index.set(0);
|
|
56
|
+
};
|
|
57
|
+
const getCurrent = () => {
|
|
58
|
+
const list = $list.get().data;
|
|
59
|
+
if (!(list !== null && list !== void 0 && list.length)) return undefined;
|
|
60
|
+
return list[$index.get()];
|
|
61
|
+
};
|
|
62
|
+
return {
|
|
63
|
+
$list,
|
|
64
|
+
$index,
|
|
65
|
+
next,
|
|
66
|
+
reset,
|
|
67
|
+
getCurrent
|
|
68
|
+
};
|
|
69
|
+
};
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
exports.pauseAdListStore = pauseAdListStore;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var classic = require('@streamlayer/sdk-web-interfaces/classic');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Imperative atom driven directly by the detector (no nanoquery — the trigger
|
|
7
|
+
* is push-based, not request/response).
|
|
8
|
+
*/
|
|
9
|
+
const createSsaiAdBreakStore = () => classic.createSingleStore(null);
|
|
10
|
+
|
|
11
|
+
exports.createSsaiAdBreakStore = createSsaiAdBreakStore;
|