@guardian/commercial-core 3.3.0 → 4.0.0
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/cjs/ad-sizes.d.ts +4 -4
- package/dist/cjs/ad-sizes.js +3 -3
- package/dist/cjs/create-ad-slot.d.ts +9 -0
- package/dist/cjs/create-ad-slot.js +229 -0
- package/dist/cjs/event-timer.d.ts +2 -1
- package/dist/cjs/index.d.ts +2 -1
- package/dist/cjs/index.js +9 -3
- package/dist/cjs/send-commercial-metrics.d.ts +2 -3
- package/dist/cjs/send-commercial-metrics.js +30 -13
- package/dist/cjs/track-gpc-signal.d.ts +7 -0
- package/dist/cjs/track-gpc-signal.js +17 -0
- package/dist/cjs/types.d.ts +1 -0
- package/dist/esm/ad-sizes.d.ts +4 -4
- package/dist/esm/ad-sizes.js +2 -2
- package/dist/esm/create-ad-slot.d.ts +9 -0
- package/dist/esm/create-ad-slot.js +225 -0
- package/dist/esm/event-timer.d.ts +2 -1
- package/dist/esm/index.d.ts +2 -1
- package/dist/esm/index.js +2 -1
- package/dist/esm/send-commercial-metrics.d.ts +2 -3
- package/dist/esm/send-commercial-metrics.js +30 -13
- package/dist/esm/track-gpc-signal.d.ts +7 -0
- package/dist/esm/track-gpc-signal.js +14 -0
- package/dist/esm/types.d.ts +1 -0
- package/package.json +9 -9
package/dist/cjs/ad-sizes.d.ts
CHANGED
|
@@ -11,7 +11,7 @@ interface SizeMapping {
|
|
|
11
11
|
phablet?: AdSize[];
|
|
12
12
|
tablet?: AdSize[];
|
|
13
13
|
}
|
|
14
|
-
interface
|
|
14
|
+
interface SlotSizeMappings {
|
|
15
15
|
right: SizeMapping;
|
|
16
16
|
comments: SizeMapping;
|
|
17
17
|
'top-above-nav': SizeMapping;
|
|
@@ -21,10 +21,10 @@ interface SizeMappings {
|
|
|
21
21
|
survey: SizeMapping;
|
|
22
22
|
}
|
|
23
23
|
declare const adSizes: Record<SizeKeys, AdSize>;
|
|
24
|
-
declare const
|
|
24
|
+
declare const slotSizeMappings: SlotSizeMappings;
|
|
25
25
|
declare const getAdSize: (size: SizeKeys) => AdSize;
|
|
26
26
|
export declare const _: {
|
|
27
27
|
createAdSize: (width: number, height: number) => AdSize;
|
|
28
28
|
};
|
|
29
|
-
export type { AdSizeString, AdSize, SizeKeys };
|
|
30
|
-
export { adSizes, getAdSize,
|
|
29
|
+
export type { AdSizeString, AdSize, SizeKeys, SizeMapping };
|
|
30
|
+
export { adSizes, getAdSize, slotSizeMappings };
|
package/dist/cjs/ad-sizes.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.slotSizeMappings = exports.getAdSize = exports.adSizes = exports._ = void 0;
|
|
4
4
|
const createAdSize = (width, height) => {
|
|
5
5
|
const toString = () => width === 0 && height === 0 ? 'fluid' : `${width},${height}`;
|
|
6
6
|
return Object.freeze({
|
|
@@ -43,7 +43,7 @@ const adSizes = {
|
|
|
43
43
|
'160x600': adSizesPartial.skyscraper,
|
|
44
44
|
};
|
|
45
45
|
exports.adSizes = adSizes;
|
|
46
|
-
const
|
|
46
|
+
const slotSizeMappings = {
|
|
47
47
|
right: {
|
|
48
48
|
mobile: [
|
|
49
49
|
adSizes.outOfPage,
|
|
@@ -156,7 +156,7 @@ const sizeMappings = {
|
|
|
156
156
|
desktop: [adSizes.outOfPage],
|
|
157
157
|
},
|
|
158
158
|
};
|
|
159
|
-
exports.
|
|
159
|
+
exports.slotSizeMappings = slotSizeMappings;
|
|
160
160
|
const getAdSize = (size) => adSizes[size];
|
|
161
161
|
exports.getAdSize = getAdSize;
|
|
162
162
|
// Export for testing
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { AdSize } from './ad-sizes';
|
|
2
|
+
declare type SlotName = 'im' | 'high-merch' | 'high-merch-lucky' | 'high-merch-paid' | 'inline' | 'mostpop' | 'comments' | 'top-above-nav' | 'carrot' | 'epic' | 'mobile-sticky';
|
|
3
|
+
declare type CreateSlotOptions = {
|
|
4
|
+
classes?: string;
|
|
5
|
+
name?: string;
|
|
6
|
+
sizes?: Record<string, AdSize[]>;
|
|
7
|
+
};
|
|
8
|
+
export declare const createAdSlot: (name: SlotName, options?: CreateSlotOptions) => HTMLElement;
|
|
9
|
+
export {};
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createAdSlot = void 0;
|
|
4
|
+
const ad_sizes_1 = require("./ad-sizes");
|
|
5
|
+
const adSlotIdPrefix = 'dfp-ad--';
|
|
6
|
+
const commonSizeMappings = {
|
|
7
|
+
mobile: [
|
|
8
|
+
ad_sizes_1.adSizes.outOfPage,
|
|
9
|
+
ad_sizes_1.adSizes.empty,
|
|
10
|
+
ad_sizes_1.adSizes.outstreamMobile,
|
|
11
|
+
ad_sizes_1.adSizes.mpu,
|
|
12
|
+
ad_sizes_1.adSizes.googleCard,
|
|
13
|
+
ad_sizes_1.adSizes.fluid,
|
|
14
|
+
],
|
|
15
|
+
phablet: [
|
|
16
|
+
ad_sizes_1.adSizes.outOfPage,
|
|
17
|
+
ad_sizes_1.adSizes.empty,
|
|
18
|
+
ad_sizes_1.adSizes.outstreamMobile,
|
|
19
|
+
ad_sizes_1.adSizes.mpu,
|
|
20
|
+
ad_sizes_1.adSizes.googleCard,
|
|
21
|
+
ad_sizes_1.adSizes.fluid,
|
|
22
|
+
],
|
|
23
|
+
desktop: [
|
|
24
|
+
ad_sizes_1.adSizes.outOfPage,
|
|
25
|
+
ad_sizes_1.adSizes.empty,
|
|
26
|
+
ad_sizes_1.adSizes.mpu,
|
|
27
|
+
ad_sizes_1.adSizes.googleCard,
|
|
28
|
+
ad_sizes_1.adSizes.fluid,
|
|
29
|
+
],
|
|
30
|
+
};
|
|
31
|
+
/*
|
|
32
|
+
|
|
33
|
+
mark: 432b3a46-90c1-4573-90d3-2400b51af8d0
|
|
34
|
+
|
|
35
|
+
The ad sizes which are hardcoded here are also hardcoded in the source code of
|
|
36
|
+
dotcom-rendering.
|
|
37
|
+
|
|
38
|
+
If/when this file is modified, please make sure that updates, if any, are reported to DCR.
|
|
39
|
+
|
|
40
|
+
TODO use a map type??
|
|
41
|
+
want to assert that values are of type AdSlotConfig
|
|
42
|
+
*/
|
|
43
|
+
const adSlotConfigs = {
|
|
44
|
+
im: {
|
|
45
|
+
label: false,
|
|
46
|
+
refresh: false,
|
|
47
|
+
sizeMappings: {
|
|
48
|
+
mobile: [
|
|
49
|
+
ad_sizes_1.adSizes.outOfPage,
|
|
50
|
+
ad_sizes_1.adSizes.empty,
|
|
51
|
+
ad_sizes_1.adSizes.inlineMerchandising,
|
|
52
|
+
ad_sizes_1.adSizes.fluid,
|
|
53
|
+
],
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
'high-merch': {
|
|
57
|
+
label: false,
|
|
58
|
+
refresh: false,
|
|
59
|
+
name: 'merchandising-high',
|
|
60
|
+
sizeMappings: {
|
|
61
|
+
mobile: [
|
|
62
|
+
ad_sizes_1.adSizes.outOfPage,
|
|
63
|
+
ad_sizes_1.adSizes.empty,
|
|
64
|
+
ad_sizes_1.adSizes.merchandisingHigh,
|
|
65
|
+
ad_sizes_1.adSizes.fluid,
|
|
66
|
+
],
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
'high-merch-lucky': {
|
|
70
|
+
label: false,
|
|
71
|
+
refresh: false,
|
|
72
|
+
name: 'merchandising-high-lucky',
|
|
73
|
+
sizeMappings: {
|
|
74
|
+
mobile: [ad_sizes_1.adSizes.outOfPage, ad_sizes_1.adSizes.empty, ad_sizes_1.adSizes.fluid],
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
'high-merch-paid': {
|
|
78
|
+
label: false,
|
|
79
|
+
refresh: false,
|
|
80
|
+
name: 'merchandising-high',
|
|
81
|
+
sizeMappings: {
|
|
82
|
+
mobile: [
|
|
83
|
+
ad_sizes_1.adSizes.outOfPage,
|
|
84
|
+
ad_sizes_1.adSizes.empty,
|
|
85
|
+
ad_sizes_1.adSizes.merchandisingHighAdFeature,
|
|
86
|
+
ad_sizes_1.adSizes.fluid,
|
|
87
|
+
],
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
inline: {
|
|
91
|
+
sizeMappings: commonSizeMappings,
|
|
92
|
+
},
|
|
93
|
+
mostpop: {
|
|
94
|
+
sizeMappings: commonSizeMappings,
|
|
95
|
+
},
|
|
96
|
+
comments: {
|
|
97
|
+
sizeMappings: commonSizeMappings,
|
|
98
|
+
},
|
|
99
|
+
'top-above-nav': {
|
|
100
|
+
sizeMappings: {
|
|
101
|
+
mobile: [
|
|
102
|
+
ad_sizes_1.adSizes.outOfPage,
|
|
103
|
+
ad_sizes_1.adSizes.empty,
|
|
104
|
+
ad_sizes_1.adSizes.fabric,
|
|
105
|
+
ad_sizes_1.adSizes.outstreamMobile,
|
|
106
|
+
ad_sizes_1.adSizes.mpu,
|
|
107
|
+
ad_sizes_1.adSizes.fluid,
|
|
108
|
+
],
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
carrot: {
|
|
112
|
+
label: false,
|
|
113
|
+
refresh: false,
|
|
114
|
+
name: 'carrot',
|
|
115
|
+
sizeMappings: {
|
|
116
|
+
mobile: [ad_sizes_1.adSizes.fluid],
|
|
117
|
+
},
|
|
118
|
+
},
|
|
119
|
+
epic: {
|
|
120
|
+
label: false,
|
|
121
|
+
refresh: false,
|
|
122
|
+
name: 'epic',
|
|
123
|
+
sizeMappings: {
|
|
124
|
+
mobile: [ad_sizes_1.adSizes.fluid],
|
|
125
|
+
},
|
|
126
|
+
},
|
|
127
|
+
'mobile-sticky': {
|
|
128
|
+
label: true,
|
|
129
|
+
refresh: true,
|
|
130
|
+
name: 'mobile-sticky',
|
|
131
|
+
sizeMappings: {
|
|
132
|
+
mobile: [ad_sizes_1.adSizes.mobilesticky],
|
|
133
|
+
},
|
|
134
|
+
},
|
|
135
|
+
};
|
|
136
|
+
/**
|
|
137
|
+
Returns an adSlot HTMLElement which is the main DFP slot.
|
|
138
|
+
|
|
139
|
+
Insert that element as siblings at the place you want adverts to appear.
|
|
140
|
+
|
|
141
|
+
Note that for the DFP slot to be filled by GTP, you'll have to
|
|
142
|
+
use addSlot from add-slot.js
|
|
143
|
+
*/
|
|
144
|
+
const createAdSlotElement = (name, attrs, classes) => {
|
|
145
|
+
const id = `${adSlotIdPrefix}${name}`;
|
|
146
|
+
// 3562dc07-78e9-4507-b922-78b979d4c5cb
|
|
147
|
+
if (window.guardian.config?.isDotcomRendering && name === 'top-above-nav') {
|
|
148
|
+
// This is to prevent a problem that appeared with DCR.
|
|
149
|
+
// We are simply making sure that if we are about to
|
|
150
|
+
// introduce dfp-ad--top-above-nav then there isn't already one.
|
|
151
|
+
const node = document.getElementById(id);
|
|
152
|
+
if (node?.parentNode) {
|
|
153
|
+
const pnode = node.parentNode;
|
|
154
|
+
console.log(`warning: cleaning up dom node id: dfp-ad--${name}`);
|
|
155
|
+
pnode.removeChild(node);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
// The 'main' adSlot
|
|
159
|
+
const adSlot = document.createElement('div');
|
|
160
|
+
adSlot.id = id;
|
|
161
|
+
adSlot.className = `js-ad-slot ad-slot ${classes.join(' ')}`;
|
|
162
|
+
adSlot.setAttribute('data-link-name', `ad slot ${name}`);
|
|
163
|
+
adSlot.setAttribute('data-name', name);
|
|
164
|
+
adSlot.setAttribute('aria-hidden', 'true');
|
|
165
|
+
Object.entries(attrs).forEach(([k, v]) => adSlot.setAttribute(k, v));
|
|
166
|
+
return adSlot;
|
|
167
|
+
};
|
|
168
|
+
/**
|
|
169
|
+
* Split class names and prefix all with ad-slot--${className}
|
|
170
|
+
*/
|
|
171
|
+
const createClasses = (slotName, classes) => [...(classes?.split(' ') ?? []), slotName].map((className) => `ad-slot--${className}`);
|
|
172
|
+
/**
|
|
173
|
+
* Given default size mappings and additional size mappings from
|
|
174
|
+
* the createAdSlot options parameter.
|
|
175
|
+
*
|
|
176
|
+
* 1. Check that the options size mappings use known device names
|
|
177
|
+
* 2. If so concat the size mappings
|
|
178
|
+
*
|
|
179
|
+
*/
|
|
180
|
+
const concatSizeMappings = (defaultSizeMappings, optionSizeMappings) => {
|
|
181
|
+
if (!optionSizeMappings)
|
|
182
|
+
return defaultSizeMappings;
|
|
183
|
+
const concatenatedSizeMappings = { ...defaultSizeMappings };
|
|
184
|
+
const optionDevices = Object.keys(optionSizeMappings); // ['mobile', 'desktop']
|
|
185
|
+
for (let i = 0; i < optionDevices.length; i++) {
|
|
186
|
+
const device = optionDevices[i];
|
|
187
|
+
if (optionSizeMappings[device] && defaultSizeMappings[device]) {
|
|
188
|
+
const optionSizeMappingsForDevice = optionSizeMappings[device];
|
|
189
|
+
const defaultSizeMappingsForDevice = defaultSizeMappings[device];
|
|
190
|
+
if (defaultSizeMappingsForDevice && optionSizeMappingsForDevice) {
|
|
191
|
+
// TODO can we do concatenatedSizeMappings[device]?.concat ?
|
|
192
|
+
concatenatedSizeMappings[device] = (concatenatedSizeMappings[device] ?? []).concat(optionSizeMappingsForDevice);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
return concatenatedSizeMappings;
|
|
197
|
+
};
|
|
198
|
+
/**
|
|
199
|
+
* Convert size mappings to a string that will be added to the ad slot
|
|
200
|
+
* data attributes.
|
|
201
|
+
*
|
|
202
|
+
* e.g. { desktop: [[320,250], [320, 280]] } => { desktop: '320,250|320,280' }
|
|
203
|
+
*
|
|
204
|
+
*/
|
|
205
|
+
const covertSizeMappingsToStrings = (sizeMappings) => Object.entries(sizeMappings).reduce((result, [device, sizes]) => {
|
|
206
|
+
result[device] = sizes.join('|');
|
|
207
|
+
return result;
|
|
208
|
+
}, {});
|
|
209
|
+
/**
|
|
210
|
+
* Prefix all object keys with data-${key}
|
|
211
|
+
*/
|
|
212
|
+
const createDataAttributes = (attrs) => Object.entries(attrs).reduce((result, [key, value]) => {
|
|
213
|
+
result[`data-${key}`] = value;
|
|
214
|
+
return result;
|
|
215
|
+
}, {});
|
|
216
|
+
const createAdSlot = (name, options = {}) => {
|
|
217
|
+
const adSlotConfig = adSlotConfigs[name];
|
|
218
|
+
const slotName = options.name ?? adSlotConfig.name ?? name;
|
|
219
|
+
const sizeMappings = concatSizeMappings(adSlotConfig.sizeMappings, options.sizes);
|
|
220
|
+
const attributes = covertSizeMappingsToStrings(sizeMappings);
|
|
221
|
+
if (adSlotConfig.label === false) {
|
|
222
|
+
attributes.label = 'false';
|
|
223
|
+
}
|
|
224
|
+
if (adSlotConfig.refresh === false) {
|
|
225
|
+
attributes.refresh = 'false';
|
|
226
|
+
}
|
|
227
|
+
return createAdSlotElement(slotName, createDataAttributes(attributes), createClasses(slotName, options.classes));
|
|
228
|
+
};
|
|
229
|
+
exports.createAdSlot = createAdSlot;
|
|
@@ -30,6 +30,7 @@ interface EventTimerProperties {
|
|
|
30
30
|
adSlotsInline?: number;
|
|
31
31
|
adSlotsTotal?: number;
|
|
32
32
|
pageHeightVH?: number;
|
|
33
|
+
gpcSignal?: number;
|
|
33
34
|
lazyLoadMarginPercent?: number;
|
|
34
35
|
}
|
|
35
36
|
export declare class EventTimer {
|
|
@@ -71,7 +72,7 @@ export declare class EventTimer {
|
|
|
71
72
|
* @param {string} name - the property's name
|
|
72
73
|
* @param {value} number - the property's value
|
|
73
74
|
*/
|
|
74
|
-
setProperty(name: 'adSlotsInline' | 'adSlotsTotal' | 'pageHeightVH' | 'lazyLoadMarginPercent', value: number): void;
|
|
75
|
+
setProperty(name: 'adSlotsInline' | 'adSlotsTotal' | 'pageHeightVH' | 'gpcSignal' | 'lazyLoadMarginPercent', value: number): void;
|
|
75
76
|
/**
|
|
76
77
|
* Creates a new performance mark
|
|
77
78
|
* For slot events also ensures each TYPE of event event is marked only once for 'first'
|
package/dist/cjs/index.d.ts
CHANGED
|
@@ -7,11 +7,12 @@ export { remarketing } from './third-party-tags/remarketing';
|
|
|
7
7
|
export { EventTimer } from './event-timer';
|
|
8
8
|
export { bypassCommercialMetricsSampling, initCommercialMetrics, } from './send-commercial-metrics';
|
|
9
9
|
export type { ThirdPartyTag } from './types';
|
|
10
|
-
export { adSizes, getAdSize,
|
|
10
|
+
export { adSizes, getAdSize, slotSizeMappings } from './ad-sizes';
|
|
11
11
|
export type { SizeKeys, AdSizeString, AdSize } from './ad-sizes';
|
|
12
12
|
export { isAdBlockInUse } from './detect-ad-blocker';
|
|
13
13
|
export { clearPermutiveSegments, getPermutiveSegments, getPermutivePFPSegments, } from './permutive';
|
|
14
14
|
export { initTrackScrollDepth } from './track-scroll-depth';
|
|
15
|
+
export { initTrackGpcSignal } from './track-gpc-signal';
|
|
15
16
|
export { buildAdsConfigWithConsent, disabledAds } from './ad-targeting-youtube';
|
|
16
17
|
export type { AdsConfig, AdsConfigBasic, AdsConfigDisabled, AdTargetingBuilder, CustomParams, } from './types';
|
|
17
18
|
export * as constants from './constants';
|
package/dist/cjs/index.js
CHANGED
|
@@ -2,7 +2,11 @@
|
|
|
2
2
|
/* istanbul ignore file -- there's no point check this for test coverage */
|
|
3
3
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
4
4
|
if (k2 === undefined) k2 = k;
|
|
5
|
-
Object.
|
|
5
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
6
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
7
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
8
|
+
}
|
|
9
|
+
Object.defineProperty(o, k2, desc);
|
|
6
10
|
}) : (function(o, m, k, k2) {
|
|
7
11
|
if (k2 === undefined) k2 = k;
|
|
8
12
|
o[k2] = m[k];
|
|
@@ -20,7 +24,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
20
24
|
return result;
|
|
21
25
|
};
|
|
22
26
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
23
|
-
exports.pickTargetingValues = exports.getViewportTargeting = exports.getSharedTargeting = exports.getSessionTargeting = exports.getPersonalisedTargeting = exports.getContentTargeting = exports.constants = exports.disabledAds = exports.buildAdsConfigWithConsent = exports.initTrackScrollDepth = exports.getPermutivePFPSegments = exports.getPermutiveSegments = exports.clearPermutiveSegments = exports.isAdBlockInUse = exports.
|
|
27
|
+
exports.pickTargetingValues = exports.getViewportTargeting = exports.getSharedTargeting = exports.getSessionTargeting = exports.getPersonalisedTargeting = exports.getContentTargeting = exports.constants = exports.disabledAds = exports.buildAdsConfigWithConsent = exports.initTrackGpcSignal = exports.initTrackScrollDepth = exports.getPermutivePFPSegments = exports.getPermutiveSegments = exports.clearPermutiveSegments = exports.isAdBlockInUse = exports.slotSizeMappings = exports.getAdSize = exports.adSizes = exports.initCommercialMetrics = exports.bypassCommercialMetricsSampling = exports.EventTimer = exports.remarketing = exports.inizio = exports.twitter = exports.fbPixel = exports.permutive = exports.ias = void 0;
|
|
24
28
|
var ias_1 = require("./third-party-tags/ias");
|
|
25
29
|
Object.defineProperty(exports, "ias", { enumerable: true, get: function () { return ias_1.ias; } });
|
|
26
30
|
var permutive_1 = require("./third-party-tags/permutive");
|
|
@@ -41,7 +45,7 @@ Object.defineProperty(exports, "initCommercialMetrics", { enumerable: true, get:
|
|
|
41
45
|
var ad_sizes_1 = require("./ad-sizes");
|
|
42
46
|
Object.defineProperty(exports, "adSizes", { enumerable: true, get: function () { return ad_sizes_1.adSizes; } });
|
|
43
47
|
Object.defineProperty(exports, "getAdSize", { enumerable: true, get: function () { return ad_sizes_1.getAdSize; } });
|
|
44
|
-
Object.defineProperty(exports, "
|
|
48
|
+
Object.defineProperty(exports, "slotSizeMappings", { enumerable: true, get: function () { return ad_sizes_1.slotSizeMappings; } });
|
|
45
49
|
var detect_ad_blocker_1 = require("./detect-ad-blocker");
|
|
46
50
|
Object.defineProperty(exports, "isAdBlockInUse", { enumerable: true, get: function () { return detect_ad_blocker_1.isAdBlockInUse; } });
|
|
47
51
|
var permutive_2 = require("./permutive");
|
|
@@ -50,6 +54,8 @@ Object.defineProperty(exports, "getPermutiveSegments", { enumerable: true, get:
|
|
|
50
54
|
Object.defineProperty(exports, "getPermutivePFPSegments", { enumerable: true, get: function () { return permutive_2.getPermutivePFPSegments; } });
|
|
51
55
|
var track_scroll_depth_1 = require("./track-scroll-depth");
|
|
52
56
|
Object.defineProperty(exports, "initTrackScrollDepth", { enumerable: true, get: function () { return track_scroll_depth_1.initTrackScrollDepth; } });
|
|
57
|
+
var track_gpc_signal_1 = require("./track-gpc-signal");
|
|
58
|
+
Object.defineProperty(exports, "initTrackGpcSignal", { enumerable: true, get: function () { return track_gpc_signal_1.initTrackGpcSignal; } });
|
|
53
59
|
var ad_targeting_youtube_1 = require("./ad-targeting-youtube");
|
|
54
60
|
Object.defineProperty(exports, "buildAdsConfigWithConsent", { enumerable: true, get: function () { return ad_targeting_youtube_1.buildAdsConfigWithConsent; } });
|
|
55
61
|
Object.defineProperty(exports, "disabledAds", { enumerable: true, get: function () { return ad_targeting_youtube_1.disabledAds; } });
|
|
@@ -22,7 +22,7 @@ declare enum Endpoints {
|
|
|
22
22
|
/**
|
|
23
23
|
* A method to asynchronously send metrics after initialization.
|
|
24
24
|
*/
|
|
25
|
-
declare function bypassCommercialMetricsSampling(): void
|
|
25
|
+
declare function bypassCommercialMetricsSampling(): Promise<void>;
|
|
26
26
|
interface InitCommercialMetricsArgs {
|
|
27
27
|
pageViewId: string;
|
|
28
28
|
browserId: string | undefined;
|
|
@@ -38,11 +38,10 @@ interface InitCommercialMetricsArgs {
|
|
|
38
38
|
* @param init.adBlockerInUse - indicates whether or not an adblocker is being used.
|
|
39
39
|
* @param init.sampling - rate at which to sample commercial metrics - the default is to send for 1% of pageviews
|
|
40
40
|
*/
|
|
41
|
-
declare function initCommercialMetrics({ pageViewId, browserId, isDev, adBlockerInUse, sampling, }: InitCommercialMetricsArgs): boolean
|
|
41
|
+
declare function initCommercialMetrics({ pageViewId, browserId, isDev, adBlockerInUse, sampling, }: InitCommercialMetricsArgs): Promise<boolean>;
|
|
42
42
|
export declare const _: {
|
|
43
43
|
Endpoints: typeof Endpoints;
|
|
44
44
|
setEndpoint: (isDev: boolean) => Endpoints;
|
|
45
|
-
filterUndefinedProperties: <T>(transformedProperties: [string, T | undefined][]) => [string, T][];
|
|
46
45
|
mapEventTimerPropertiesToString: (properties: Array<[string, string | number]>) => Property[];
|
|
47
46
|
roundTimeStamp: (events: TimedEvent[]) => Metric[];
|
|
48
47
|
transformToObjectEntries: (eventTimerProperties: EventProperties) => Array<[string, string | number | undefined]>;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.initCommercialMetrics = exports.bypassCommercialMetricsSampling = exports._ = void 0;
|
|
4
|
+
const consent_management_platform_1 = require("@guardian/consent-management-platform");
|
|
4
5
|
const libs_1 = require("@guardian/libs");
|
|
5
6
|
const event_timer_1 = require("./event-timer");
|
|
6
7
|
var Endpoints;
|
|
@@ -38,12 +39,6 @@ const transformToObjectEntries = (eventTimerProperties) => {
|
|
|
38
39
|
// Transforms object {key: value} pairs into an array of [key, value] arrays
|
|
39
40
|
return Object.entries(eventTimerProperties);
|
|
40
41
|
};
|
|
41
|
-
const filterUndefinedProperties = (transformedProperties) => transformedProperties.reduce((acc, [key, value]) => {
|
|
42
|
-
if (typeof value !== 'undefined') {
|
|
43
|
-
acc.push([key, value]);
|
|
44
|
-
}
|
|
45
|
-
return acc;
|
|
46
|
-
}, []);
|
|
47
42
|
const mapEventTimerPropertiesToString = (properties) => {
|
|
48
43
|
return properties.map(([name, value]) => ({
|
|
49
44
|
name: String(name),
|
|
@@ -64,7 +59,7 @@ function gatherMetricsOnPageUnload() {
|
|
|
64
59
|
// Assemble commercial properties and metrics
|
|
65
60
|
const eventTimer = event_timer_1.EventTimer.get();
|
|
66
61
|
const transformedEntries = transformToObjectEntries(eventTimer.properties);
|
|
67
|
-
const filteredEventTimerProperties =
|
|
62
|
+
const filteredEventTimerProperties = transformedEntries.filter((item) => typeof item[1] !== 'undefined');
|
|
68
63
|
const mappedEventTimerProperties = mapEventTimerPropertiesToString(filteredEventTimerProperties);
|
|
69
64
|
const properties = mappedEventTimerProperties
|
|
70
65
|
.concat(devProperties)
|
|
@@ -92,15 +87,32 @@ const addVisibilityListeners = () => {
|
|
|
92
87
|
// Safari does not reliably fire the `visibilitychange` on page unload.
|
|
93
88
|
window.addEventListener('pagehide', listener);
|
|
94
89
|
};
|
|
90
|
+
const checkConsent = async () => {
|
|
91
|
+
const consentState = await (0, consent_management_platform_1.onConsent)();
|
|
92
|
+
if (consentState.tcfv2) {
|
|
93
|
+
// TCFv2 mode - check for consent
|
|
94
|
+
const consents = consentState.tcfv2.consents;
|
|
95
|
+
const REQUIRED_CONSENTS = [7, 8];
|
|
96
|
+
return REQUIRED_CONSENTS.every((consent) => consents[consent]);
|
|
97
|
+
}
|
|
98
|
+
// non-TCFv2 mode - don't check for consent
|
|
99
|
+
return true;
|
|
100
|
+
};
|
|
95
101
|
/**
|
|
96
102
|
* A method to asynchronously send metrics after initialization.
|
|
97
103
|
*/
|
|
98
|
-
function bypassCommercialMetricsSampling() {
|
|
104
|
+
async function bypassCommercialMetricsSampling() {
|
|
99
105
|
if (!initialised) {
|
|
100
106
|
console.warn('initCommercialMetrics not yet initialised');
|
|
101
107
|
return;
|
|
102
108
|
}
|
|
103
|
-
|
|
109
|
+
const consented = await checkConsent();
|
|
110
|
+
if (consented) {
|
|
111
|
+
addVisibilityListeners();
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
(0, libs_1.log)('commercial', "Metrics won't be sent because consent wasn't given");
|
|
115
|
+
}
|
|
104
116
|
}
|
|
105
117
|
exports.bypassCommercialMetricsSampling = bypassCommercialMetricsSampling;
|
|
106
118
|
/**
|
|
@@ -111,7 +123,7 @@ exports.bypassCommercialMetricsSampling = bypassCommercialMetricsSampling;
|
|
|
111
123
|
* @param init.adBlockerInUse - indicates whether or not an adblocker is being used.
|
|
112
124
|
* @param init.sampling - rate at which to sample commercial metrics - the default is to send for 1% of pageviews
|
|
113
125
|
*/
|
|
114
|
-
function initCommercialMetrics({ pageViewId, browserId, isDev, adBlockerInUse, sampling = 1 / 100, }) {
|
|
126
|
+
async function initCommercialMetrics({ pageViewId, browserId, isDev, adBlockerInUse, sampling = 1 / 100, }) {
|
|
115
127
|
commercialMetricsPayload.page_view_id = pageViewId;
|
|
116
128
|
commercialMetricsPayload.browser_id = browserId;
|
|
117
129
|
setEndpoint(isDev);
|
|
@@ -123,8 +135,14 @@ function initCommercialMetrics({ pageViewId, browserId, isDev, adBlockerInUse, s
|
|
|
123
135
|
initialised = true;
|
|
124
136
|
const userIsInSamplingGroup = Math.random() <= sampling;
|
|
125
137
|
if (isDev || userIsInSamplingGroup) {
|
|
126
|
-
|
|
127
|
-
|
|
138
|
+
const consented = await checkConsent();
|
|
139
|
+
if (consented) {
|
|
140
|
+
addVisibilityListeners();
|
|
141
|
+
return true;
|
|
142
|
+
}
|
|
143
|
+
else {
|
|
144
|
+
(0, libs_1.log)('commercial', "Metrics won't be sent because consent wasn't given");
|
|
145
|
+
}
|
|
128
146
|
}
|
|
129
147
|
return false;
|
|
130
148
|
}
|
|
@@ -132,7 +150,6 @@ exports.initCommercialMetrics = initCommercialMetrics;
|
|
|
132
150
|
exports._ = {
|
|
133
151
|
Endpoints,
|
|
134
152
|
setEndpoint,
|
|
135
|
-
filterUndefinedProperties,
|
|
136
153
|
mapEventTimerPropertiesToString,
|
|
137
154
|
roundTimeStamp,
|
|
138
155
|
transformToObjectEntries,
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { ConsentState } from '@guardian/consent-management-platform/dist/types';
|
|
2
|
+
/**
|
|
3
|
+
* Collect metrics on gpcSignal presence and value
|
|
4
|
+
* https://globalprivacycontrol.github.io/gpc-spec/
|
|
5
|
+
*/
|
|
6
|
+
declare const initTrackGpcSignal: (consentState: ConsentState) => void;
|
|
7
|
+
export { initTrackGpcSignal };
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.initTrackGpcSignal = void 0;
|
|
4
|
+
const libs_1 = require("@guardian/libs");
|
|
5
|
+
const event_timer_1 = require("./event-timer");
|
|
6
|
+
/**
|
|
7
|
+
* Collect metrics on gpcSignal presence and value
|
|
8
|
+
* https://globalprivacycontrol.github.io/gpc-spec/
|
|
9
|
+
*/
|
|
10
|
+
const initTrackGpcSignal = (consentState) => {
|
|
11
|
+
// If undefined we set the property value to -1, false is 0, true is 1
|
|
12
|
+
const gpcSignal = consentState.gpcSignal === undefined ? -1 : +consentState.gpcSignal;
|
|
13
|
+
const eventTimer = event_timer_1.EventTimer.get();
|
|
14
|
+
(0, libs_1.log)('commercial', `gpcSignal ${gpcSignal}`);
|
|
15
|
+
eventTimer.setProperty('gpcSignal', gpcSignal);
|
|
16
|
+
};
|
|
17
|
+
exports.initTrackGpcSignal = initTrackGpcSignal;
|
package/dist/cjs/types.d.ts
CHANGED
|
@@ -23,6 +23,7 @@ export declare type GuardianAnalyticsConfig = {
|
|
|
23
23
|
};
|
|
24
24
|
export declare type GuardianWindowConfig = {
|
|
25
25
|
googleAnalytics?: GuardianAnalyticsConfig;
|
|
26
|
+
isDotcomRendering?: boolean;
|
|
26
27
|
};
|
|
27
28
|
export declare type GoogleTagParams = unknown;
|
|
28
29
|
export declare type GoogleTrackConversionObject = {
|
package/dist/esm/ad-sizes.d.ts
CHANGED
|
@@ -11,7 +11,7 @@ interface SizeMapping {
|
|
|
11
11
|
phablet?: AdSize[];
|
|
12
12
|
tablet?: AdSize[];
|
|
13
13
|
}
|
|
14
|
-
interface
|
|
14
|
+
interface SlotSizeMappings {
|
|
15
15
|
right: SizeMapping;
|
|
16
16
|
comments: SizeMapping;
|
|
17
17
|
'top-above-nav': SizeMapping;
|
|
@@ -21,10 +21,10 @@ interface SizeMappings {
|
|
|
21
21
|
survey: SizeMapping;
|
|
22
22
|
}
|
|
23
23
|
declare const adSizes: Record<SizeKeys, AdSize>;
|
|
24
|
-
declare const
|
|
24
|
+
declare const slotSizeMappings: SlotSizeMappings;
|
|
25
25
|
declare const getAdSize: (size: SizeKeys) => AdSize;
|
|
26
26
|
export declare const _: {
|
|
27
27
|
createAdSize: (width: number, height: number) => AdSize;
|
|
28
28
|
};
|
|
29
|
-
export type { AdSizeString, AdSize, SizeKeys };
|
|
30
|
-
export { adSizes, getAdSize,
|
|
29
|
+
export type { AdSizeString, AdSize, SizeKeys, SizeMapping };
|
|
30
|
+
export { adSizes, getAdSize, slotSizeMappings };
|
package/dist/esm/ad-sizes.js
CHANGED
|
@@ -39,7 +39,7 @@ const adSizes = {
|
|
|
39
39
|
'300x1050': adSizesPartial.portrait,
|
|
40
40
|
'160x600': adSizesPartial.skyscraper,
|
|
41
41
|
};
|
|
42
|
-
const
|
|
42
|
+
const slotSizeMappings = {
|
|
43
43
|
right: {
|
|
44
44
|
mobile: [
|
|
45
45
|
adSizes.outOfPage,
|
|
@@ -155,4 +155,4 @@ const sizeMappings = {
|
|
|
155
155
|
const getAdSize = (size) => adSizes[size];
|
|
156
156
|
// Export for testing
|
|
157
157
|
export const _ = { createAdSize };
|
|
158
|
-
export { adSizes, getAdSize,
|
|
158
|
+
export { adSizes, getAdSize, slotSizeMappings };
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { AdSize } from './ad-sizes';
|
|
2
|
+
declare type SlotName = 'im' | 'high-merch' | 'high-merch-lucky' | 'high-merch-paid' | 'inline' | 'mostpop' | 'comments' | 'top-above-nav' | 'carrot' | 'epic' | 'mobile-sticky';
|
|
3
|
+
declare type CreateSlotOptions = {
|
|
4
|
+
classes?: string;
|
|
5
|
+
name?: string;
|
|
6
|
+
sizes?: Record<string, AdSize[]>;
|
|
7
|
+
};
|
|
8
|
+
export declare const createAdSlot: (name: SlotName, options?: CreateSlotOptions) => HTMLElement;
|
|
9
|
+
export {};
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
import { adSizes } from './ad-sizes';
|
|
2
|
+
const adSlotIdPrefix = 'dfp-ad--';
|
|
3
|
+
const commonSizeMappings = {
|
|
4
|
+
mobile: [
|
|
5
|
+
adSizes.outOfPage,
|
|
6
|
+
adSizes.empty,
|
|
7
|
+
adSizes.outstreamMobile,
|
|
8
|
+
adSizes.mpu,
|
|
9
|
+
adSizes.googleCard,
|
|
10
|
+
adSizes.fluid,
|
|
11
|
+
],
|
|
12
|
+
phablet: [
|
|
13
|
+
adSizes.outOfPage,
|
|
14
|
+
adSizes.empty,
|
|
15
|
+
adSizes.outstreamMobile,
|
|
16
|
+
adSizes.mpu,
|
|
17
|
+
adSizes.googleCard,
|
|
18
|
+
adSizes.fluid,
|
|
19
|
+
],
|
|
20
|
+
desktop: [
|
|
21
|
+
adSizes.outOfPage,
|
|
22
|
+
adSizes.empty,
|
|
23
|
+
adSizes.mpu,
|
|
24
|
+
adSizes.googleCard,
|
|
25
|
+
adSizes.fluid,
|
|
26
|
+
],
|
|
27
|
+
};
|
|
28
|
+
/*
|
|
29
|
+
|
|
30
|
+
mark: 432b3a46-90c1-4573-90d3-2400b51af8d0
|
|
31
|
+
|
|
32
|
+
The ad sizes which are hardcoded here are also hardcoded in the source code of
|
|
33
|
+
dotcom-rendering.
|
|
34
|
+
|
|
35
|
+
If/when this file is modified, please make sure that updates, if any, are reported to DCR.
|
|
36
|
+
|
|
37
|
+
TODO use a map type??
|
|
38
|
+
want to assert that values are of type AdSlotConfig
|
|
39
|
+
*/
|
|
40
|
+
const adSlotConfigs = {
|
|
41
|
+
im: {
|
|
42
|
+
label: false,
|
|
43
|
+
refresh: false,
|
|
44
|
+
sizeMappings: {
|
|
45
|
+
mobile: [
|
|
46
|
+
adSizes.outOfPage,
|
|
47
|
+
adSizes.empty,
|
|
48
|
+
adSizes.inlineMerchandising,
|
|
49
|
+
adSizes.fluid,
|
|
50
|
+
],
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
'high-merch': {
|
|
54
|
+
label: false,
|
|
55
|
+
refresh: false,
|
|
56
|
+
name: 'merchandising-high',
|
|
57
|
+
sizeMappings: {
|
|
58
|
+
mobile: [
|
|
59
|
+
adSizes.outOfPage,
|
|
60
|
+
adSizes.empty,
|
|
61
|
+
adSizes.merchandisingHigh,
|
|
62
|
+
adSizes.fluid,
|
|
63
|
+
],
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
'high-merch-lucky': {
|
|
67
|
+
label: false,
|
|
68
|
+
refresh: false,
|
|
69
|
+
name: 'merchandising-high-lucky',
|
|
70
|
+
sizeMappings: {
|
|
71
|
+
mobile: [adSizes.outOfPage, adSizes.empty, adSizes.fluid],
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
'high-merch-paid': {
|
|
75
|
+
label: false,
|
|
76
|
+
refresh: false,
|
|
77
|
+
name: 'merchandising-high',
|
|
78
|
+
sizeMappings: {
|
|
79
|
+
mobile: [
|
|
80
|
+
adSizes.outOfPage,
|
|
81
|
+
adSizes.empty,
|
|
82
|
+
adSizes.merchandisingHighAdFeature,
|
|
83
|
+
adSizes.fluid,
|
|
84
|
+
],
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
inline: {
|
|
88
|
+
sizeMappings: commonSizeMappings,
|
|
89
|
+
},
|
|
90
|
+
mostpop: {
|
|
91
|
+
sizeMappings: commonSizeMappings,
|
|
92
|
+
},
|
|
93
|
+
comments: {
|
|
94
|
+
sizeMappings: commonSizeMappings,
|
|
95
|
+
},
|
|
96
|
+
'top-above-nav': {
|
|
97
|
+
sizeMappings: {
|
|
98
|
+
mobile: [
|
|
99
|
+
adSizes.outOfPage,
|
|
100
|
+
adSizes.empty,
|
|
101
|
+
adSizes.fabric,
|
|
102
|
+
adSizes.outstreamMobile,
|
|
103
|
+
adSizes.mpu,
|
|
104
|
+
adSizes.fluid,
|
|
105
|
+
],
|
|
106
|
+
},
|
|
107
|
+
},
|
|
108
|
+
carrot: {
|
|
109
|
+
label: false,
|
|
110
|
+
refresh: false,
|
|
111
|
+
name: 'carrot',
|
|
112
|
+
sizeMappings: {
|
|
113
|
+
mobile: [adSizes.fluid],
|
|
114
|
+
},
|
|
115
|
+
},
|
|
116
|
+
epic: {
|
|
117
|
+
label: false,
|
|
118
|
+
refresh: false,
|
|
119
|
+
name: 'epic',
|
|
120
|
+
sizeMappings: {
|
|
121
|
+
mobile: [adSizes.fluid],
|
|
122
|
+
},
|
|
123
|
+
},
|
|
124
|
+
'mobile-sticky': {
|
|
125
|
+
label: true,
|
|
126
|
+
refresh: true,
|
|
127
|
+
name: 'mobile-sticky',
|
|
128
|
+
sizeMappings: {
|
|
129
|
+
mobile: [adSizes.mobilesticky],
|
|
130
|
+
},
|
|
131
|
+
},
|
|
132
|
+
};
|
|
133
|
+
/**
|
|
134
|
+
Returns an adSlot HTMLElement which is the main DFP slot.
|
|
135
|
+
|
|
136
|
+
Insert that element as siblings at the place you want adverts to appear.
|
|
137
|
+
|
|
138
|
+
Note that for the DFP slot to be filled by GTP, you'll have to
|
|
139
|
+
use addSlot from add-slot.js
|
|
140
|
+
*/
|
|
141
|
+
const createAdSlotElement = (name, attrs, classes) => {
|
|
142
|
+
const id = `${adSlotIdPrefix}${name}`;
|
|
143
|
+
// 3562dc07-78e9-4507-b922-78b979d4c5cb
|
|
144
|
+
if (window.guardian.config?.isDotcomRendering && name === 'top-above-nav') {
|
|
145
|
+
// This is to prevent a problem that appeared with DCR.
|
|
146
|
+
// We are simply making sure that if we are about to
|
|
147
|
+
// introduce dfp-ad--top-above-nav then there isn't already one.
|
|
148
|
+
const node = document.getElementById(id);
|
|
149
|
+
if (node?.parentNode) {
|
|
150
|
+
const pnode = node.parentNode;
|
|
151
|
+
console.log(`warning: cleaning up dom node id: dfp-ad--${name}`);
|
|
152
|
+
pnode.removeChild(node);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
// The 'main' adSlot
|
|
156
|
+
const adSlot = document.createElement('div');
|
|
157
|
+
adSlot.id = id;
|
|
158
|
+
adSlot.className = `js-ad-slot ad-slot ${classes.join(' ')}`;
|
|
159
|
+
adSlot.setAttribute('data-link-name', `ad slot ${name}`);
|
|
160
|
+
adSlot.setAttribute('data-name', name);
|
|
161
|
+
adSlot.setAttribute('aria-hidden', 'true');
|
|
162
|
+
Object.entries(attrs).forEach(([k, v]) => adSlot.setAttribute(k, v));
|
|
163
|
+
return adSlot;
|
|
164
|
+
};
|
|
165
|
+
/**
|
|
166
|
+
* Split class names and prefix all with ad-slot--${className}
|
|
167
|
+
*/
|
|
168
|
+
const createClasses = (slotName, classes) => [...(classes?.split(' ') ?? []), slotName].map((className) => `ad-slot--${className}`);
|
|
169
|
+
/**
|
|
170
|
+
* Given default size mappings and additional size mappings from
|
|
171
|
+
* the createAdSlot options parameter.
|
|
172
|
+
*
|
|
173
|
+
* 1. Check that the options size mappings use known device names
|
|
174
|
+
* 2. If so concat the size mappings
|
|
175
|
+
*
|
|
176
|
+
*/
|
|
177
|
+
const concatSizeMappings = (defaultSizeMappings, optionSizeMappings) => {
|
|
178
|
+
if (!optionSizeMappings)
|
|
179
|
+
return defaultSizeMappings;
|
|
180
|
+
const concatenatedSizeMappings = { ...defaultSizeMappings };
|
|
181
|
+
const optionDevices = Object.keys(optionSizeMappings); // ['mobile', 'desktop']
|
|
182
|
+
for (let i = 0; i < optionDevices.length; i++) {
|
|
183
|
+
const device = optionDevices[i];
|
|
184
|
+
if (optionSizeMappings[device] && defaultSizeMappings[device]) {
|
|
185
|
+
const optionSizeMappingsForDevice = optionSizeMappings[device];
|
|
186
|
+
const defaultSizeMappingsForDevice = defaultSizeMappings[device];
|
|
187
|
+
if (defaultSizeMappingsForDevice && optionSizeMappingsForDevice) {
|
|
188
|
+
// TODO can we do concatenatedSizeMappings[device]?.concat ?
|
|
189
|
+
concatenatedSizeMappings[device] = (concatenatedSizeMappings[device] ?? []).concat(optionSizeMappingsForDevice);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
return concatenatedSizeMappings;
|
|
194
|
+
};
|
|
195
|
+
/**
|
|
196
|
+
* Convert size mappings to a string that will be added to the ad slot
|
|
197
|
+
* data attributes.
|
|
198
|
+
*
|
|
199
|
+
* e.g. { desktop: [[320,250], [320, 280]] } => { desktop: '320,250|320,280' }
|
|
200
|
+
*
|
|
201
|
+
*/
|
|
202
|
+
const covertSizeMappingsToStrings = (sizeMappings) => Object.entries(sizeMappings).reduce((result, [device, sizes]) => {
|
|
203
|
+
result[device] = sizes.join('|');
|
|
204
|
+
return result;
|
|
205
|
+
}, {});
|
|
206
|
+
/**
|
|
207
|
+
* Prefix all object keys with data-${key}
|
|
208
|
+
*/
|
|
209
|
+
const createDataAttributes = (attrs) => Object.entries(attrs).reduce((result, [key, value]) => {
|
|
210
|
+
result[`data-${key}`] = value;
|
|
211
|
+
return result;
|
|
212
|
+
}, {});
|
|
213
|
+
export const createAdSlot = (name, options = {}) => {
|
|
214
|
+
const adSlotConfig = adSlotConfigs[name];
|
|
215
|
+
const slotName = options.name ?? adSlotConfig.name ?? name;
|
|
216
|
+
const sizeMappings = concatSizeMappings(adSlotConfig.sizeMappings, options.sizes);
|
|
217
|
+
const attributes = covertSizeMappingsToStrings(sizeMappings);
|
|
218
|
+
if (adSlotConfig.label === false) {
|
|
219
|
+
attributes.label = 'false';
|
|
220
|
+
}
|
|
221
|
+
if (adSlotConfig.refresh === false) {
|
|
222
|
+
attributes.refresh = 'false';
|
|
223
|
+
}
|
|
224
|
+
return createAdSlotElement(slotName, createDataAttributes(attributes), createClasses(slotName, options.classes));
|
|
225
|
+
};
|
|
@@ -30,6 +30,7 @@ interface EventTimerProperties {
|
|
|
30
30
|
adSlotsInline?: number;
|
|
31
31
|
adSlotsTotal?: number;
|
|
32
32
|
pageHeightVH?: number;
|
|
33
|
+
gpcSignal?: number;
|
|
33
34
|
lazyLoadMarginPercent?: number;
|
|
34
35
|
}
|
|
35
36
|
export declare class EventTimer {
|
|
@@ -71,7 +72,7 @@ export declare class EventTimer {
|
|
|
71
72
|
* @param {string} name - the property's name
|
|
72
73
|
* @param {value} number - the property's value
|
|
73
74
|
*/
|
|
74
|
-
setProperty(name: 'adSlotsInline' | 'adSlotsTotal' | 'pageHeightVH' | 'lazyLoadMarginPercent', value: number): void;
|
|
75
|
+
setProperty(name: 'adSlotsInline' | 'adSlotsTotal' | 'pageHeightVH' | 'gpcSignal' | 'lazyLoadMarginPercent', value: number): void;
|
|
75
76
|
/**
|
|
76
77
|
* Creates a new performance mark
|
|
77
78
|
* For slot events also ensures each TYPE of event event is marked only once for 'first'
|
package/dist/esm/index.d.ts
CHANGED
|
@@ -7,11 +7,12 @@ export { remarketing } from './third-party-tags/remarketing';
|
|
|
7
7
|
export { EventTimer } from './event-timer';
|
|
8
8
|
export { bypassCommercialMetricsSampling, initCommercialMetrics, } from './send-commercial-metrics';
|
|
9
9
|
export type { ThirdPartyTag } from './types';
|
|
10
|
-
export { adSizes, getAdSize,
|
|
10
|
+
export { adSizes, getAdSize, slotSizeMappings } from './ad-sizes';
|
|
11
11
|
export type { SizeKeys, AdSizeString, AdSize } from './ad-sizes';
|
|
12
12
|
export { isAdBlockInUse } from './detect-ad-blocker';
|
|
13
13
|
export { clearPermutiveSegments, getPermutiveSegments, getPermutivePFPSegments, } from './permutive';
|
|
14
14
|
export { initTrackScrollDepth } from './track-scroll-depth';
|
|
15
|
+
export { initTrackGpcSignal } from './track-gpc-signal';
|
|
15
16
|
export { buildAdsConfigWithConsent, disabledAds } from './ad-targeting-youtube';
|
|
16
17
|
export type { AdsConfig, AdsConfigBasic, AdsConfigDisabled, AdTargetingBuilder, CustomParams, } from './types';
|
|
17
18
|
export * as constants from './constants';
|
package/dist/esm/index.js
CHANGED
|
@@ -7,10 +7,11 @@ export { inizio } from './third-party-tags/inizio';
|
|
|
7
7
|
export { remarketing } from './third-party-tags/remarketing';
|
|
8
8
|
export { EventTimer } from './event-timer';
|
|
9
9
|
export { bypassCommercialMetricsSampling, initCommercialMetrics, } from './send-commercial-metrics';
|
|
10
|
-
export { adSizes, getAdSize,
|
|
10
|
+
export { adSizes, getAdSize, slotSizeMappings } from './ad-sizes';
|
|
11
11
|
export { isAdBlockInUse } from './detect-ad-blocker';
|
|
12
12
|
export { clearPermutiveSegments, getPermutiveSegments, getPermutivePFPSegments, } from './permutive';
|
|
13
13
|
export { initTrackScrollDepth } from './track-scroll-depth';
|
|
14
|
+
export { initTrackGpcSignal } from './track-gpc-signal';
|
|
14
15
|
export { buildAdsConfigWithConsent, disabledAds } from './ad-targeting-youtube';
|
|
15
16
|
import * as constants_1 from './constants';
|
|
16
17
|
export { constants_1 as constants };
|
|
@@ -22,7 +22,7 @@ declare enum Endpoints {
|
|
|
22
22
|
/**
|
|
23
23
|
* A method to asynchronously send metrics after initialization.
|
|
24
24
|
*/
|
|
25
|
-
declare function bypassCommercialMetricsSampling(): void
|
|
25
|
+
declare function bypassCommercialMetricsSampling(): Promise<void>;
|
|
26
26
|
interface InitCommercialMetricsArgs {
|
|
27
27
|
pageViewId: string;
|
|
28
28
|
browserId: string | undefined;
|
|
@@ -38,11 +38,10 @@ interface InitCommercialMetricsArgs {
|
|
|
38
38
|
* @param init.adBlockerInUse - indicates whether or not an adblocker is being used.
|
|
39
39
|
* @param init.sampling - rate at which to sample commercial metrics - the default is to send for 1% of pageviews
|
|
40
40
|
*/
|
|
41
|
-
declare function initCommercialMetrics({ pageViewId, browserId, isDev, adBlockerInUse, sampling, }: InitCommercialMetricsArgs): boolean
|
|
41
|
+
declare function initCommercialMetrics({ pageViewId, browserId, isDev, adBlockerInUse, sampling, }: InitCommercialMetricsArgs): Promise<boolean>;
|
|
42
42
|
export declare const _: {
|
|
43
43
|
Endpoints: typeof Endpoints;
|
|
44
44
|
setEndpoint: (isDev: boolean) => Endpoints;
|
|
45
|
-
filterUndefinedProperties: <T>(transformedProperties: [string, T | undefined][]) => [string, T][];
|
|
46
45
|
mapEventTimerPropertiesToString: (properties: Array<[string, string | number]>) => Property[];
|
|
47
46
|
roundTimeStamp: (events: TimedEvent[]) => Metric[];
|
|
48
47
|
transformToObjectEntries: (eventTimerProperties: EventProperties) => Array<[string, string | number | undefined]>;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { onConsent } from '@guardian/consent-management-platform';
|
|
1
2
|
import { log } from '@guardian/libs';
|
|
2
3
|
import { EventTimer } from './event-timer';
|
|
3
4
|
var Endpoints;
|
|
@@ -35,12 +36,6 @@ const transformToObjectEntries = (eventTimerProperties) => {
|
|
|
35
36
|
// Transforms object {key: value} pairs into an array of [key, value] arrays
|
|
36
37
|
return Object.entries(eventTimerProperties);
|
|
37
38
|
};
|
|
38
|
-
const filterUndefinedProperties = (transformedProperties) => transformedProperties.reduce((acc, [key, value]) => {
|
|
39
|
-
if (typeof value !== 'undefined') {
|
|
40
|
-
acc.push([key, value]);
|
|
41
|
-
}
|
|
42
|
-
return acc;
|
|
43
|
-
}, []);
|
|
44
39
|
const mapEventTimerPropertiesToString = (properties) => {
|
|
45
40
|
return properties.map(([name, value]) => ({
|
|
46
41
|
name: String(name),
|
|
@@ -61,7 +56,7 @@ function gatherMetricsOnPageUnload() {
|
|
|
61
56
|
// Assemble commercial properties and metrics
|
|
62
57
|
const eventTimer = EventTimer.get();
|
|
63
58
|
const transformedEntries = transformToObjectEntries(eventTimer.properties);
|
|
64
|
-
const filteredEventTimerProperties =
|
|
59
|
+
const filteredEventTimerProperties = transformedEntries.filter((item) => typeof item[1] !== 'undefined');
|
|
65
60
|
const mappedEventTimerProperties = mapEventTimerPropertiesToString(filteredEventTimerProperties);
|
|
66
61
|
const properties = mappedEventTimerProperties
|
|
67
62
|
.concat(devProperties)
|
|
@@ -89,15 +84,32 @@ const addVisibilityListeners = () => {
|
|
|
89
84
|
// Safari does not reliably fire the `visibilitychange` on page unload.
|
|
90
85
|
window.addEventListener('pagehide', listener);
|
|
91
86
|
};
|
|
87
|
+
const checkConsent = async () => {
|
|
88
|
+
const consentState = await onConsent();
|
|
89
|
+
if (consentState.tcfv2) {
|
|
90
|
+
// TCFv2 mode - check for consent
|
|
91
|
+
const consents = consentState.tcfv2.consents;
|
|
92
|
+
const REQUIRED_CONSENTS = [7, 8];
|
|
93
|
+
return REQUIRED_CONSENTS.every((consent) => consents[consent]);
|
|
94
|
+
}
|
|
95
|
+
// non-TCFv2 mode - don't check for consent
|
|
96
|
+
return true;
|
|
97
|
+
};
|
|
92
98
|
/**
|
|
93
99
|
* A method to asynchronously send metrics after initialization.
|
|
94
100
|
*/
|
|
95
|
-
function bypassCommercialMetricsSampling() {
|
|
101
|
+
async function bypassCommercialMetricsSampling() {
|
|
96
102
|
if (!initialised) {
|
|
97
103
|
console.warn('initCommercialMetrics not yet initialised');
|
|
98
104
|
return;
|
|
99
105
|
}
|
|
100
|
-
|
|
106
|
+
const consented = await checkConsent();
|
|
107
|
+
if (consented) {
|
|
108
|
+
addVisibilityListeners();
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
log('commercial', "Metrics won't be sent because consent wasn't given");
|
|
112
|
+
}
|
|
101
113
|
}
|
|
102
114
|
/**
|
|
103
115
|
* A method to initialise metrics.
|
|
@@ -107,7 +119,7 @@ function bypassCommercialMetricsSampling() {
|
|
|
107
119
|
* @param init.adBlockerInUse - indicates whether or not an adblocker is being used.
|
|
108
120
|
* @param init.sampling - rate at which to sample commercial metrics - the default is to send for 1% of pageviews
|
|
109
121
|
*/
|
|
110
|
-
function initCommercialMetrics({ pageViewId, browserId, isDev, adBlockerInUse, sampling = 1 / 100, }) {
|
|
122
|
+
async function initCommercialMetrics({ pageViewId, browserId, isDev, adBlockerInUse, sampling = 1 / 100, }) {
|
|
111
123
|
commercialMetricsPayload.page_view_id = pageViewId;
|
|
112
124
|
commercialMetricsPayload.browser_id = browserId;
|
|
113
125
|
setEndpoint(isDev);
|
|
@@ -119,15 +131,20 @@ function initCommercialMetrics({ pageViewId, browserId, isDev, adBlockerInUse, s
|
|
|
119
131
|
initialised = true;
|
|
120
132
|
const userIsInSamplingGroup = Math.random() <= sampling;
|
|
121
133
|
if (isDev || userIsInSamplingGroup) {
|
|
122
|
-
|
|
123
|
-
|
|
134
|
+
const consented = await checkConsent();
|
|
135
|
+
if (consented) {
|
|
136
|
+
addVisibilityListeners();
|
|
137
|
+
return true;
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
140
|
+
log('commercial', "Metrics won't be sent because consent wasn't given");
|
|
141
|
+
}
|
|
124
142
|
}
|
|
125
143
|
return false;
|
|
126
144
|
}
|
|
127
145
|
export const _ = {
|
|
128
146
|
Endpoints,
|
|
129
147
|
setEndpoint,
|
|
130
|
-
filterUndefinedProperties,
|
|
131
148
|
mapEventTimerPropertiesToString,
|
|
132
149
|
roundTimeStamp,
|
|
133
150
|
transformToObjectEntries,
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { ConsentState } from '@guardian/consent-management-platform/dist/types';
|
|
2
|
+
/**
|
|
3
|
+
* Collect metrics on gpcSignal presence and value
|
|
4
|
+
* https://globalprivacycontrol.github.io/gpc-spec/
|
|
5
|
+
*/
|
|
6
|
+
declare const initTrackGpcSignal: (consentState: ConsentState) => void;
|
|
7
|
+
export { initTrackGpcSignal };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { log } from '@guardian/libs';
|
|
2
|
+
import { EventTimer } from './event-timer';
|
|
3
|
+
/**
|
|
4
|
+
* Collect metrics on gpcSignal presence and value
|
|
5
|
+
* https://globalprivacycontrol.github.io/gpc-spec/
|
|
6
|
+
*/
|
|
7
|
+
const initTrackGpcSignal = (consentState) => {
|
|
8
|
+
// If undefined we set the property value to -1, false is 0, true is 1
|
|
9
|
+
const gpcSignal = consentState.gpcSignal === undefined ? -1 : +consentState.gpcSignal;
|
|
10
|
+
const eventTimer = EventTimer.get();
|
|
11
|
+
log('commercial', `gpcSignal ${gpcSignal}`);
|
|
12
|
+
eventTimer.setProperty('gpcSignal', gpcSignal);
|
|
13
|
+
};
|
|
14
|
+
export { initTrackGpcSignal };
|
package/dist/esm/types.d.ts
CHANGED
|
@@ -23,6 +23,7 @@ export declare type GuardianAnalyticsConfig = {
|
|
|
23
23
|
};
|
|
24
24
|
export declare type GuardianWindowConfig = {
|
|
25
25
|
googleAnalytics?: GuardianAnalyticsConfig;
|
|
26
|
+
isDotcomRendering?: boolean;
|
|
26
27
|
};
|
|
27
28
|
export declare type GoogleTagParams = unknown;
|
|
28
29
|
export declare type GoogleTrackConversionObject = {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@guardian/commercial-core",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.0.0",
|
|
4
4
|
"description": "Guardian advertising business logic",
|
|
5
5
|
"homepage": "https://github.com/guardian/commercial-core#readme",
|
|
6
6
|
"bugs": {
|
|
@@ -43,16 +43,16 @@
|
|
|
43
43
|
"prettier": "@guardian/prettier",
|
|
44
44
|
"devDependencies": {
|
|
45
45
|
"@commitlint/cli": "^16.1.0",
|
|
46
|
-
"@commitlint/config-conventional": "^
|
|
46
|
+
"@commitlint/config-conventional": "^17.0.0",
|
|
47
47
|
"@guardian/ab-core": "^2.0.0",
|
|
48
|
-
"@guardian/consent-management-platform": "^10.
|
|
48
|
+
"@guardian/consent-management-platform": "^10.6.0",
|
|
49
49
|
"@guardian/eslint-config-typescript": "^1.0.0",
|
|
50
|
-
"@guardian/libs": "
|
|
51
|
-
"@guardian/prettier": "^0.
|
|
50
|
+
"@guardian/libs": "5.1.0",
|
|
51
|
+
"@guardian/prettier": "^1.0.0",
|
|
52
52
|
"@octokit/core": "^3.5.1",
|
|
53
53
|
"@semantic-release/github": "^8.0.2",
|
|
54
54
|
"@types/google.analytics": "^0.0.42",
|
|
55
|
-
"@types/googletag": "^
|
|
55
|
+
"@types/googletag": "^2.0.0",
|
|
56
56
|
"@types/jest": "^27.0.3",
|
|
57
57
|
"@typescript-eslint/eslint-plugin": "^5.5.0",
|
|
58
58
|
"@typescript-eslint/parser": "^5.5.0",
|
|
@@ -63,15 +63,15 @@
|
|
|
63
63
|
"eslint-config-prettier": "^8.3.0",
|
|
64
64
|
"eslint-plugin-eslint-comments": "^3.2.0",
|
|
65
65
|
"eslint-plugin-import": "^2.25.3",
|
|
66
|
-
"eslint-plugin-jest": "^
|
|
66
|
+
"eslint-plugin-jest": "^26.1.5",
|
|
67
67
|
"eslint-plugin-prettier": "^4.0.0",
|
|
68
|
-
"husky": "^
|
|
68
|
+
"husky": "^8.0.1",
|
|
69
69
|
"jest": "^27.4.1",
|
|
70
70
|
"lint-staged": "^12.1.2",
|
|
71
71
|
"mockdate": "^3.0.5",
|
|
72
72
|
"npm-run-all": "^4.1.5",
|
|
73
73
|
"prettier": "^2.5.0",
|
|
74
|
-
"semantic-release": "^
|
|
74
|
+
"semantic-release": "^19.0.2",
|
|
75
75
|
"ts-jest": "^27.0.7",
|
|
76
76
|
"type-fest": "^2.8.0",
|
|
77
77
|
"typescript": "^4.5.2",
|