@guardian/commercial-core 0.0.0-beta-20250716121613
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/LICENSE +201 -0
- package/README.md +45 -0
- package/dist/cjs/ad-sizes.d.ts +202 -0
- package/dist/cjs/ad-sizes.js +400 -0
- package/dist/cjs/breakpoint.d.ts +8 -0
- package/dist/cjs/breakpoint.js +10 -0
- package/dist/cjs/constants/ad-label-height.d.ts +4 -0
- package/dist/cjs/constants/ad-label-height.js +7 -0
- package/dist/cjs/constants/index.d.ts +3 -0
- package/dist/cjs/constants/index.js +9 -0
- package/dist/cjs/constants/prebid-timeout.d.ts +4 -0
- package/dist/cjs/constants/prebid-timeout.js +7 -0
- package/dist/cjs/constants/top-above-nav-height.d.ts +10 -0
- package/dist/cjs/constants/top-above-nav-height.js +48 -0
- package/dist/cjs/detect-ad-blocker.d.ts +12 -0
- package/dist/cjs/detect-ad-blocker.js +61 -0
- package/dist/cjs/event-timer.d.ts +103 -0
- package/dist/cjs/event-timer.js +204 -0
- package/dist/cjs/geo/country-code.d.ts +3 -0
- package/dist/cjs/geo/country-code.js +34 -0
- package/dist/cjs/geo/geo-utils.d.ts +11 -0
- package/dist/cjs/geo/geo-utils.js +31 -0
- package/dist/cjs/geo/get-locale.d.ts +8 -0
- package/dist/cjs/geo/get-locale.js +43 -0
- package/dist/cjs/global.d.ts +71 -0
- package/dist/cjs/global.js +2 -0
- package/dist/cjs/index.d.ts +13 -0
- package/dist/cjs/index.js +46 -0
- package/dist/cjs/messenger/post-message.d.ts +1 -0
- package/dist/cjs/messenger/post-message.js +7 -0
- package/dist/cjs/permutive.d.ts +9 -0
- package/dist/cjs/permutive.js +38 -0
- package/dist/cjs/send-commercial-metrics.d.ts +58 -0
- package/dist/cjs/send-commercial-metrics.js +209 -0
- package/dist/cjs/targeting/build-page-targeting.d.ts +47 -0
- package/dist/cjs/targeting/build-page-targeting.js +112 -0
- package/dist/cjs/targeting/content.d.ts +87 -0
- package/dist/cjs/targeting/content.js +76 -0
- package/dist/cjs/targeting/personalised.d.ts +83 -0
- package/dist/cjs/targeting/personalised.js +140 -0
- package/dist/cjs/targeting/pick-targeting-values.d.ts +25 -0
- package/dist/cjs/targeting/pick-targeting-values.js +47 -0
- package/dist/cjs/targeting/session.d.ts +111 -0
- package/dist/cjs/targeting/session.js +61 -0
- package/dist/cjs/targeting/shared.d.ts +156 -0
- package/dist/cjs/targeting/shared.js +28 -0
- package/dist/cjs/targeting/teads-eligibility.d.ts +2 -0
- package/dist/cjs/targeting/teads-eligibility.js +20 -0
- package/dist/cjs/targeting/types.d.ts +6 -0
- package/dist/cjs/targeting/types.js +2 -0
- package/dist/cjs/targeting/viewport.d.ts +48 -0
- package/dist/cjs/targeting/viewport.js +22 -0
- package/dist/cjs/targeting/youtube-ima.d.ts +12 -0
- package/dist/cjs/targeting/youtube-ima.js +76 -0
- package/dist/cjs/types.d.ts +426 -0
- package/dist/cjs/types.js +12 -0
- package/dist/esm/ad-sizes.d.ts +202 -0
- package/dist/esm/ad-sizes.js +390 -0
- package/dist/esm/breakpoint.d.ts +8 -0
- package/dist/esm/breakpoint.js +6 -0
- package/dist/esm/constants/ad-label-height.d.ts +4 -0
- package/dist/esm/constants/ad-label-height.js +4 -0
- package/dist/esm/constants/index.d.ts +3 -0
- package/dist/esm/constants/index.js +3 -0
- package/dist/esm/constants/prebid-timeout.d.ts +4 -0
- package/dist/esm/constants/prebid-timeout.js +4 -0
- package/dist/esm/constants/top-above-nav-height.d.ts +10 -0
- package/dist/esm/constants/top-above-nav-height.js +45 -0
- package/dist/esm/detect-ad-blocker.d.ts +12 -0
- package/dist/esm/detect-ad-blocker.js +58 -0
- package/dist/esm/event-timer.d.ts +103 -0
- package/dist/esm/event-timer.js +199 -0
- package/dist/esm/geo/country-code.d.ts +3 -0
- package/dist/esm/geo/country-code.js +31 -0
- package/dist/esm/geo/geo-utils.d.ts +11 -0
- package/dist/esm/geo/geo-utils.js +20 -0
- package/dist/esm/geo/get-locale.d.ts +8 -0
- package/dist/esm/geo/get-locale.js +38 -0
- package/dist/esm/global.d.ts +71 -0
- package/dist/esm/global.js +0 -0
- package/dist/esm/index.d.ts +13 -0
- package/dist/esm/index.js +10 -0
- package/dist/esm/messenger/post-message.d.ts +1 -0
- package/dist/esm/messenger/post-message.js +3 -0
- package/dist/esm/permutive.d.ts +9 -0
- package/dist/esm/permutive.js +33 -0
- package/dist/esm/send-commercial-metrics.d.ts +58 -0
- package/dist/esm/send-commercial-metrics.js +204 -0
- package/dist/esm/targeting/build-page-targeting.d.ts +47 -0
- package/dist/esm/targeting/build-page-targeting.js +108 -0
- package/dist/esm/targeting/content.d.ts +87 -0
- package/dist/esm/targeting/content.js +73 -0
- package/dist/esm/targeting/personalised.d.ts +83 -0
- package/dist/esm/targeting/personalised.js +137 -0
- package/dist/esm/targeting/pick-targeting-values.d.ts +25 -0
- package/dist/esm/targeting/pick-targeting-values.js +43 -0
- package/dist/esm/targeting/session.d.ts +111 -0
- package/dist/esm/targeting/session.js +57 -0
- package/dist/esm/targeting/shared.d.ts +156 -0
- package/dist/esm/targeting/shared.js +25 -0
- package/dist/esm/targeting/teads-eligibility.d.ts +2 -0
- package/dist/esm/targeting/teads-eligibility.js +17 -0
- package/dist/esm/targeting/types.d.ts +6 -0
- package/dist/esm/targeting/types.js +0 -0
- package/dist/esm/targeting/viewport.d.ts +48 -0
- package/dist/esm/targeting/viewport.js +19 -0
- package/dist/esm/targeting/youtube-ima.d.ts +12 -0
- package/dist/esm/targeting/youtube-ima.js +73 -0
- package/dist/esm/types.d.ts +426 -0
- package/dist/esm/types.js +10 -0
- package/package.json +65 -0
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import type { Breakpoint } from './breakpoint';
|
|
2
|
+
type AdSizeString = 'fluid' | `${number},${number}`;
|
|
3
|
+
/**
|
|
4
|
+
* Store ad sizes in a way that is compatible with google-tag but also accessible via
|
|
5
|
+
* more semantic `width`/`height` properties and keep things readonly.
|
|
6
|
+
*
|
|
7
|
+
* example:
|
|
8
|
+
* const size = new AdSize([300, 250]);
|
|
9
|
+
*
|
|
10
|
+
* size.width === 300; // true
|
|
11
|
+
* size[0] === 300; // true
|
|
12
|
+
*
|
|
13
|
+
* size.height === 250; // true
|
|
14
|
+
* size[1] === 250; // true
|
|
15
|
+
*
|
|
16
|
+
* size[0] = 200; // throws error
|
|
17
|
+
* size.width = 200; // throws error
|
|
18
|
+
*
|
|
19
|
+
*/
|
|
20
|
+
declare class AdSize extends Array<number> {
|
|
21
|
+
readonly [0]: number;
|
|
22
|
+
readonly [1]: number;
|
|
23
|
+
constructor([width, height]: [number, number]);
|
|
24
|
+
toString(): AdSizeString;
|
|
25
|
+
toArray(): number[];
|
|
26
|
+
isProxy(): boolean;
|
|
27
|
+
get width(): number;
|
|
28
|
+
get height(): number;
|
|
29
|
+
}
|
|
30
|
+
type SizeKeys = '160x600' | '300x1050' | '300x250' | '300x600' | '728x90' | '970x250' | 'billboard' | 'cascade' | 'empty' | 'fabric' | 'fluid' | 'googleCard' | 'halfPage' | 'leaderboard' | 'merchandising' | 'merchandisingHigh' | 'merchandisingHighAdFeature' | 'mobilesticky' | 'mpu' | 'outOfPage' | 'outstreamDesktop' | 'outstreamGoogleDesktop' | 'outstreamMobile' | 'portrait' | 'portraitInterstitial' | 'pubmaticInterscroller' | 'skyscraper' | 'sponsorLogo';
|
|
31
|
+
type SlotName = 'article-end' | 'carrot' | 'comments-expanded' | 'comments' | 'crossword-banner-mobile' | 'exclusion' | 'external' | 'fronts-banner' | 'inline' | 'liveblog-top' | 'merchandising-high' | 'merchandising' | 'mobile-sticky' | 'football-right' | 'mostpop' | 'right' | 'sponsor-logo' | 'survey' | 'top-above-nav' | 'interactive';
|
|
32
|
+
type SizeMapping = Partial<Record<Breakpoint, readonly AdSize[]>>;
|
|
33
|
+
type SlotSizeMappings = Record<SlotName, SizeMapping>;
|
|
34
|
+
declare const createAdSize: (width: number, height: number) => AdSize;
|
|
35
|
+
declare const standardAdSizes: {
|
|
36
|
+
'970x250': AdSize;
|
|
37
|
+
'300x600': AdSize;
|
|
38
|
+
'728x90': AdSize;
|
|
39
|
+
'300x250': AdSize;
|
|
40
|
+
'300x1050': AdSize;
|
|
41
|
+
'160x600': AdSize;
|
|
42
|
+
};
|
|
43
|
+
declare const outstreamSizes: {
|
|
44
|
+
outstreamDesktop: AdSize;
|
|
45
|
+
outstreamGoogleDesktop: AdSize;
|
|
46
|
+
outstreamMobile: AdSize;
|
|
47
|
+
};
|
|
48
|
+
declare const adSizes: {
|
|
49
|
+
readonly empty: AdSize;
|
|
50
|
+
readonly fabric: AdSize;
|
|
51
|
+
readonly merchandising: AdSize;
|
|
52
|
+
readonly merchandisingHigh: AdSize;
|
|
53
|
+
readonly merchandisingHighAdFeature: AdSize;
|
|
54
|
+
readonly sponsorLogo: AdSize;
|
|
55
|
+
readonly fluid: AdSize;
|
|
56
|
+
readonly googleCard: AdSize;
|
|
57
|
+
readonly outOfPage: AdSize;
|
|
58
|
+
readonly pubmaticInterscroller: AdSize;
|
|
59
|
+
readonly outstreamDesktop: AdSize;
|
|
60
|
+
readonly outstreamGoogleDesktop: AdSize;
|
|
61
|
+
readonly outstreamMobile: AdSize;
|
|
62
|
+
readonly '970x250': AdSize;
|
|
63
|
+
readonly '300x600': AdSize;
|
|
64
|
+
readonly '728x90': AdSize;
|
|
65
|
+
readonly '300x250': AdSize;
|
|
66
|
+
readonly '300x1050': AdSize;
|
|
67
|
+
readonly '160x600': AdSize;
|
|
68
|
+
readonly billboard: AdSize;
|
|
69
|
+
readonly halfPage: AdSize;
|
|
70
|
+
readonly leaderboard: AdSize;
|
|
71
|
+
readonly mobilesticky: AdSize;
|
|
72
|
+
readonly mpu: AdSize;
|
|
73
|
+
readonly portrait: AdSize;
|
|
74
|
+
readonly skyscraper: AdSize;
|
|
75
|
+
readonly cascade: AdSize;
|
|
76
|
+
readonly portraitInterstitial: AdSize;
|
|
77
|
+
};
|
|
78
|
+
/**
|
|
79
|
+
* mark: 432b3a46-90c1-4573-90d3-2400b51af8d0
|
|
80
|
+
* Some of these may or may not need to be synced for with the sizes in ./create-ad-slot.ts
|
|
81
|
+
* these were originally from DCR, create-ad-slot.ts ones were in frontend.
|
|
82
|
+
*
|
|
83
|
+
* Note:
|
|
84
|
+
* If a breakpoint is not defined in a size mapping for a slot, that breakpoint will use the sizes
|
|
85
|
+
* of the next breakpoint down that has a size mapping. For example, if only "mobile" and "phablet" sizes
|
|
86
|
+
* are defined for a slot, all breakpoints larger than "phablet" will use the mapping for "phablet".
|
|
87
|
+
*
|
|
88
|
+
* In another example, if a slot has only "tablet" as a size mapping defined,
|
|
89
|
+
* then "desktop" will use the size mapping for "tablet". "mobile" and "phablet"
|
|
90
|
+
* will have no size mapping. This type of example may be used in cases where
|
|
91
|
+
* we only want the slot to appear on the "tablet" size or greater.
|
|
92
|
+
**/
|
|
93
|
+
declare const slotSizeMappings: {
|
|
94
|
+
readonly inline: {
|
|
95
|
+
readonly mobile: readonly [AdSize, AdSize, AdSize, AdSize, AdSize, AdSize];
|
|
96
|
+
readonly desktop: readonly [AdSize, AdSize, AdSize, AdSize, AdSize];
|
|
97
|
+
};
|
|
98
|
+
readonly right: {
|
|
99
|
+
readonly mobile: readonly [AdSize, AdSize, AdSize, AdSize, AdSize, AdSize];
|
|
100
|
+
};
|
|
101
|
+
readonly comments: {
|
|
102
|
+
readonly mobile: readonly [AdSize, AdSize, AdSize, AdSize, AdSize, AdSize];
|
|
103
|
+
readonly desktop: readonly [AdSize, AdSize, AdSize, AdSize, AdSize];
|
|
104
|
+
};
|
|
105
|
+
readonly 'comments-expanded': {
|
|
106
|
+
readonly mobile: readonly [AdSize, AdSize];
|
|
107
|
+
readonly desktop: readonly [AdSize, AdSize, AdSize, AdSize, AdSize, AdSize, AdSize];
|
|
108
|
+
};
|
|
109
|
+
readonly 'top-above-nav': {
|
|
110
|
+
readonly mobile: readonly [AdSize, AdSize, AdSize, AdSize, AdSize, AdSize];
|
|
111
|
+
readonly tablet: readonly [AdSize, AdSize, AdSize, AdSize, AdSize];
|
|
112
|
+
readonly desktop: readonly [AdSize, AdSize, AdSize, AdSize, AdSize, AdSize, AdSize, AdSize];
|
|
113
|
+
};
|
|
114
|
+
readonly 'fronts-banner': {
|
|
115
|
+
readonly tablet: readonly [AdSize, AdSize];
|
|
116
|
+
readonly desktop: readonly [AdSize, AdSize, AdSize, AdSize, AdSize];
|
|
117
|
+
};
|
|
118
|
+
readonly mostpop: {
|
|
119
|
+
readonly mobile: readonly [AdSize, AdSize, AdSize, AdSize, AdSize];
|
|
120
|
+
readonly phablet: readonly [AdSize, AdSize, AdSize, AdSize, AdSize, AdSize, AdSize];
|
|
121
|
+
readonly tablet: readonly [AdSize, AdSize, AdSize, AdSize, AdSize, AdSize];
|
|
122
|
+
readonly desktop: readonly [AdSize, AdSize, AdSize, AdSize, AdSize, AdSize];
|
|
123
|
+
};
|
|
124
|
+
readonly 'liveblog-top': {
|
|
125
|
+
readonly mobile: readonly [AdSize, AdSize, AdSize, AdSize];
|
|
126
|
+
readonly tablet: readonly [];
|
|
127
|
+
readonly desktop: readonly [];
|
|
128
|
+
};
|
|
129
|
+
readonly 'merchandising-high': {
|
|
130
|
+
readonly mobile: readonly [AdSize, AdSize, AdSize, AdSize, AdSize];
|
|
131
|
+
readonly tablet: readonly [AdSize, AdSize, AdSize, AdSize];
|
|
132
|
+
readonly desktop: readonly [AdSize, AdSize, AdSize, AdSize, AdSize];
|
|
133
|
+
};
|
|
134
|
+
readonly merchandising: {
|
|
135
|
+
readonly mobile: readonly [AdSize, AdSize, AdSize, AdSize, AdSize];
|
|
136
|
+
readonly tablet: readonly [AdSize, AdSize, AdSize, AdSize];
|
|
137
|
+
readonly desktop: readonly [AdSize, AdSize, AdSize, AdSize, AdSize];
|
|
138
|
+
};
|
|
139
|
+
readonly survey: {
|
|
140
|
+
readonly desktop: readonly [AdSize];
|
|
141
|
+
};
|
|
142
|
+
readonly carrot: {
|
|
143
|
+
readonly mobile: readonly [AdSize];
|
|
144
|
+
};
|
|
145
|
+
readonly 'mobile-sticky': {
|
|
146
|
+
readonly mobile: readonly [AdSize, AdSize, AdSize];
|
|
147
|
+
};
|
|
148
|
+
readonly 'crossword-banner-mobile': {
|
|
149
|
+
readonly mobile: readonly [AdSize];
|
|
150
|
+
};
|
|
151
|
+
readonly 'football-right': {
|
|
152
|
+
readonly desktop: readonly [AdSize, AdSize, AdSize, AdSize];
|
|
153
|
+
};
|
|
154
|
+
readonly 'article-end': {
|
|
155
|
+
readonly mobile: readonly [];
|
|
156
|
+
};
|
|
157
|
+
readonly exclusion: {
|
|
158
|
+
readonly mobile: readonly [AdSize];
|
|
159
|
+
};
|
|
160
|
+
/**
|
|
161
|
+
* @deprecated Use `slotSizeMappings['sponsor-logo']` instead
|
|
162
|
+
*/
|
|
163
|
+
readonly external: {
|
|
164
|
+
readonly mobile: readonly [AdSize, AdSize, AdSize, AdSize];
|
|
165
|
+
};
|
|
166
|
+
readonly 'sponsor-logo': {
|
|
167
|
+
readonly mobile: readonly [AdSize, AdSize, AdSize, AdSize];
|
|
168
|
+
};
|
|
169
|
+
readonly interactive: {
|
|
170
|
+
readonly mobile: readonly [AdSize, AdSize];
|
|
171
|
+
readonly tablet: readonly [AdSize, AdSize];
|
|
172
|
+
readonly desktop: readonly [AdSize, AdSize];
|
|
173
|
+
};
|
|
174
|
+
};
|
|
175
|
+
declare const getAdSize: (size: SizeKeys) => AdSize;
|
|
176
|
+
/**
|
|
177
|
+
* Finds the ad sizes that will be used for a breakpoint given a size mapping
|
|
178
|
+
*
|
|
179
|
+
* If ad sizes are defined in the size mapping for the specified breakpoint, we use that.
|
|
180
|
+
* If no sizes are defined for the breakpoint, use the next smallest breakpoint with defined ad sizes.
|
|
181
|
+
* If no smaller breakpoints have defined ad sizes, return an empty array
|
|
182
|
+
*
|
|
183
|
+
* Example:
|
|
184
|
+
* For the following slotSizeMappings:
|
|
185
|
+
* inline: {
|
|
186
|
+
* phablet: [adSizes.mpu],
|
|
187
|
+
* desktop: [adSizes.billboard]
|
|
188
|
+
* }
|
|
189
|
+
* the applied sizes for each breakpoint for the "inline" slot will be:
|
|
190
|
+
* mobile: []
|
|
191
|
+
* phablet: [adSizes.mpu]
|
|
192
|
+
* tablet: [adSizes.mpu]
|
|
193
|
+
* desktop: [adSizes.billboard]
|
|
194
|
+
*
|
|
195
|
+
* See ad-sizes.test.ts for more examples
|
|
196
|
+
*/
|
|
197
|
+
declare const findAppliedSizesForBreakpoint: (sizeMappings: SizeMapping, breakpoint: Breakpoint) => readonly AdSize[];
|
|
198
|
+
export declare const _: {
|
|
199
|
+
createAdSize: (width: number, height: number) => AdSize;
|
|
200
|
+
};
|
|
201
|
+
export type { AdSizeString, SizeKeys, SizeMapping, SlotSizeMappings, SlotName };
|
|
202
|
+
export { AdSize, adSizes, standardAdSizes, outstreamSizes, getAdSize, slotSizeMappings, createAdSize, findAppliedSizesForBreakpoint, };
|
|
@@ -0,0 +1,390 @@
|
|
|
1
|
+
import { breakpoints, isBreakpoint } from './breakpoint';
|
|
2
|
+
/**
|
|
3
|
+
* Store ad sizes in a way that is compatible with google-tag but also accessible via
|
|
4
|
+
* more semantic `width`/`height` properties and keep things readonly.
|
|
5
|
+
*
|
|
6
|
+
* example:
|
|
7
|
+
* const size = new AdSize([300, 250]);
|
|
8
|
+
*
|
|
9
|
+
* size.width === 300; // true
|
|
10
|
+
* size[0] === 300; // true
|
|
11
|
+
*
|
|
12
|
+
* size.height === 250; // true
|
|
13
|
+
* size[1] === 250; // true
|
|
14
|
+
*
|
|
15
|
+
* size[0] = 200; // throws error
|
|
16
|
+
* size.width = 200; // throws error
|
|
17
|
+
*
|
|
18
|
+
*/
|
|
19
|
+
class AdSize extends Array {
|
|
20
|
+
[0];
|
|
21
|
+
[1];
|
|
22
|
+
constructor([width, height]) {
|
|
23
|
+
super();
|
|
24
|
+
this[0] = width;
|
|
25
|
+
this[1] = height;
|
|
26
|
+
}
|
|
27
|
+
toString() {
|
|
28
|
+
return this.width === 0 && this.height === 0
|
|
29
|
+
? 'fluid'
|
|
30
|
+
: `${this.width},${this.height}`;
|
|
31
|
+
}
|
|
32
|
+
toArray() {
|
|
33
|
+
return [this[0], this[1]];
|
|
34
|
+
}
|
|
35
|
+
// The advert size is not reflective of the actual size of the advert.
|
|
36
|
+
// For example, fluid ads and Guardian merch ads are larger than the dimensions
|
|
37
|
+
isProxy() {
|
|
38
|
+
const isOutOfPage = this.width === 1 && this.height === 1;
|
|
39
|
+
const isEmpty = this.width === 2 && this.height === 2;
|
|
40
|
+
const isFluid = this.toString() === 'fluid';
|
|
41
|
+
const isMerch = this.width === 88;
|
|
42
|
+
const isSponsorLogo = this.width === 3 && this.height === 3;
|
|
43
|
+
return isOutOfPage || isEmpty || isFluid || isMerch || isSponsorLogo;
|
|
44
|
+
}
|
|
45
|
+
get width() {
|
|
46
|
+
return this[0];
|
|
47
|
+
}
|
|
48
|
+
get height() {
|
|
49
|
+
return this[1];
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
const createAdSize = (width, height) => {
|
|
53
|
+
return new AdSize([width, height]);
|
|
54
|
+
};
|
|
55
|
+
const namedStandardAdSizes = {
|
|
56
|
+
billboard: createAdSize(970, 250),
|
|
57
|
+
halfPage: createAdSize(300, 600),
|
|
58
|
+
leaderboard: createAdSize(728, 90),
|
|
59
|
+
mobilesticky: createAdSize(320, 50),
|
|
60
|
+
mpu: createAdSize(300, 250),
|
|
61
|
+
portrait: createAdSize(300, 1050),
|
|
62
|
+
skyscraper: createAdSize(160, 600),
|
|
63
|
+
cascade: createAdSize(940, 230),
|
|
64
|
+
portraitInterstitial: createAdSize(320, 480),
|
|
65
|
+
};
|
|
66
|
+
const standardAdSizes = {
|
|
67
|
+
'970x250': namedStandardAdSizes.billboard,
|
|
68
|
+
'300x600': namedStandardAdSizes.halfPage,
|
|
69
|
+
'728x90': namedStandardAdSizes.leaderboard,
|
|
70
|
+
'300x250': namedStandardAdSizes.mpu,
|
|
71
|
+
'300x1050': namedStandardAdSizes.portrait,
|
|
72
|
+
'160x600': namedStandardAdSizes.skyscraper,
|
|
73
|
+
};
|
|
74
|
+
const outstreamSizes = {
|
|
75
|
+
outstreamDesktop: createAdSize(620, 350),
|
|
76
|
+
outstreamGoogleDesktop: createAdSize(550, 310),
|
|
77
|
+
outstreamMobile: createAdSize(300, 197),
|
|
78
|
+
};
|
|
79
|
+
/**
|
|
80
|
+
* Ad sizes commonly associated with third parties
|
|
81
|
+
*/
|
|
82
|
+
const proprietaryAdSizes = {
|
|
83
|
+
fluid: createAdSize(0, 0),
|
|
84
|
+
googleCard: createAdSize(300, 274),
|
|
85
|
+
outOfPage: createAdSize(1, 1),
|
|
86
|
+
pubmaticInterscroller: createAdSize(371, 660), // pubmatic only mobile size
|
|
87
|
+
};
|
|
88
|
+
/**
|
|
89
|
+
* Ad sizes associated with in-house formats
|
|
90
|
+
*/
|
|
91
|
+
const guardianProprietaryAdSizes = {
|
|
92
|
+
empty: createAdSize(2, 2),
|
|
93
|
+
fabric: createAdSize(88, 71),
|
|
94
|
+
merchandising: createAdSize(88, 88),
|
|
95
|
+
merchandisingHigh: createAdSize(88, 87),
|
|
96
|
+
merchandisingHighAdFeature: createAdSize(88, 89),
|
|
97
|
+
/**
|
|
98
|
+
* This is a proxy size (not the true size of the rendered creative)
|
|
99
|
+
* that can be used to ensure that no other high priority line items
|
|
100
|
+
* fill a certain slot.
|
|
101
|
+
*/
|
|
102
|
+
sponsorLogo: createAdSize(3, 3),
|
|
103
|
+
};
|
|
104
|
+
const adSizes = {
|
|
105
|
+
...namedStandardAdSizes,
|
|
106
|
+
...standardAdSizes,
|
|
107
|
+
...outstreamSizes,
|
|
108
|
+
...proprietaryAdSizes,
|
|
109
|
+
...guardianProprietaryAdSizes,
|
|
110
|
+
};
|
|
111
|
+
/**
|
|
112
|
+
* mark: 432b3a46-90c1-4573-90d3-2400b51af8d0
|
|
113
|
+
* Some of these may or may not need to be synced for with the sizes in ./create-ad-slot.ts
|
|
114
|
+
* these were originally from DCR, create-ad-slot.ts ones were in frontend.
|
|
115
|
+
*
|
|
116
|
+
* Note:
|
|
117
|
+
* If a breakpoint is not defined in a size mapping for a slot, that breakpoint will use the sizes
|
|
118
|
+
* of the next breakpoint down that has a size mapping. For example, if only "mobile" and "phablet" sizes
|
|
119
|
+
* are defined for a slot, all breakpoints larger than "phablet" will use the mapping for "phablet".
|
|
120
|
+
*
|
|
121
|
+
* In another example, if a slot has only "tablet" as a size mapping defined,
|
|
122
|
+
* then "desktop" will use the size mapping for "tablet". "mobile" and "phablet"
|
|
123
|
+
* will have no size mapping. This type of example may be used in cases where
|
|
124
|
+
* we only want the slot to appear on the "tablet" size or greater.
|
|
125
|
+
**/
|
|
126
|
+
const slotSizeMappings = {
|
|
127
|
+
inline: {
|
|
128
|
+
mobile: [
|
|
129
|
+
adSizes.outOfPage,
|
|
130
|
+
adSizes.empty,
|
|
131
|
+
adSizes.outstreamMobile,
|
|
132
|
+
adSizes.mpu,
|
|
133
|
+
adSizes.googleCard,
|
|
134
|
+
adSizes.fluid,
|
|
135
|
+
],
|
|
136
|
+
desktop: [
|
|
137
|
+
adSizes.outOfPage,
|
|
138
|
+
adSizes.empty,
|
|
139
|
+
adSizes.mpu,
|
|
140
|
+
adSizes.googleCard,
|
|
141
|
+
adSizes.fluid,
|
|
142
|
+
],
|
|
143
|
+
},
|
|
144
|
+
right: {
|
|
145
|
+
mobile: [
|
|
146
|
+
adSizes.outOfPage,
|
|
147
|
+
adSizes.empty,
|
|
148
|
+
adSizes.mpu,
|
|
149
|
+
adSizes.googleCard,
|
|
150
|
+
adSizes.halfPage,
|
|
151
|
+
adSizes.fluid,
|
|
152
|
+
],
|
|
153
|
+
},
|
|
154
|
+
comments: {
|
|
155
|
+
mobile: [
|
|
156
|
+
adSizes.outOfPage,
|
|
157
|
+
adSizes.empty,
|
|
158
|
+
adSizes.outstreamMobile,
|
|
159
|
+
adSizes.mpu,
|
|
160
|
+
adSizes.googleCard,
|
|
161
|
+
adSizes.fluid,
|
|
162
|
+
],
|
|
163
|
+
desktop: [
|
|
164
|
+
adSizes.outOfPage,
|
|
165
|
+
adSizes.empty,
|
|
166
|
+
adSizes.mpu,
|
|
167
|
+
adSizes.googleCard,
|
|
168
|
+
adSizes.fluid,
|
|
169
|
+
],
|
|
170
|
+
},
|
|
171
|
+
'comments-expanded': {
|
|
172
|
+
mobile: [adSizes.mpu, adSizes.empty],
|
|
173
|
+
desktop: [
|
|
174
|
+
adSizes.outOfPage,
|
|
175
|
+
adSizes.empty,
|
|
176
|
+
adSizes.mpu,
|
|
177
|
+
adSizes.googleCard,
|
|
178
|
+
adSizes.fluid,
|
|
179
|
+
adSizes.skyscraper,
|
|
180
|
+
adSizes.halfPage,
|
|
181
|
+
],
|
|
182
|
+
},
|
|
183
|
+
'top-above-nav': {
|
|
184
|
+
mobile: [
|
|
185
|
+
adSizes.outOfPage,
|
|
186
|
+
adSizes.empty,
|
|
187
|
+
adSizes.fabric,
|
|
188
|
+
adSizes.outstreamMobile,
|
|
189
|
+
adSizes.mpu,
|
|
190
|
+
adSizes.fluid,
|
|
191
|
+
],
|
|
192
|
+
tablet: [
|
|
193
|
+
adSizes.outOfPage,
|
|
194
|
+
adSizes.empty,
|
|
195
|
+
adSizes.fabric,
|
|
196
|
+
adSizes.fluid,
|
|
197
|
+
adSizes.leaderboard,
|
|
198
|
+
],
|
|
199
|
+
desktop: [
|
|
200
|
+
adSizes.outOfPage,
|
|
201
|
+
adSizes.empty,
|
|
202
|
+
adSizes.leaderboard,
|
|
203
|
+
createAdSize(940, 230),
|
|
204
|
+
createAdSize(900, 250),
|
|
205
|
+
adSizes.billboard,
|
|
206
|
+
adSizes.fabric,
|
|
207
|
+
adSizes.fluid,
|
|
208
|
+
],
|
|
209
|
+
},
|
|
210
|
+
'fronts-banner': {
|
|
211
|
+
tablet: [adSizes.empty, adSizes.leaderboard],
|
|
212
|
+
desktop: [
|
|
213
|
+
adSizes.outOfPage,
|
|
214
|
+
adSizes.empty,
|
|
215
|
+
adSizes.billboard,
|
|
216
|
+
adSizes.merchandisingHigh,
|
|
217
|
+
adSizes.fluid,
|
|
218
|
+
],
|
|
219
|
+
},
|
|
220
|
+
mostpop: {
|
|
221
|
+
mobile: [
|
|
222
|
+
adSizes.outOfPage,
|
|
223
|
+
adSizes.empty,
|
|
224
|
+
adSizes.mpu,
|
|
225
|
+
adSizes.googleCard,
|
|
226
|
+
adSizes.fluid,
|
|
227
|
+
],
|
|
228
|
+
phablet: [
|
|
229
|
+
adSizes.outOfPage,
|
|
230
|
+
adSizes.empty,
|
|
231
|
+
adSizes.outstreamMobile,
|
|
232
|
+
adSizes.mpu,
|
|
233
|
+
adSizes.googleCard,
|
|
234
|
+
adSizes.halfPage,
|
|
235
|
+
adSizes.fluid,
|
|
236
|
+
],
|
|
237
|
+
tablet: [
|
|
238
|
+
adSizes.outOfPage,
|
|
239
|
+
adSizes.empty,
|
|
240
|
+
adSizes.mpu,
|
|
241
|
+
adSizes.googleCard,
|
|
242
|
+
adSizes.halfPage,
|
|
243
|
+
adSizes.fluid,
|
|
244
|
+
],
|
|
245
|
+
desktop: [
|
|
246
|
+
adSizes.outOfPage,
|
|
247
|
+
adSizes.empty,
|
|
248
|
+
adSizes.mpu,
|
|
249
|
+
adSizes.googleCard,
|
|
250
|
+
adSizes.halfPage,
|
|
251
|
+
adSizes.fluid,
|
|
252
|
+
],
|
|
253
|
+
},
|
|
254
|
+
'liveblog-top': {
|
|
255
|
+
mobile: [adSizes.outOfPage, adSizes.empty, adSizes.mpu, adSizes.fluid],
|
|
256
|
+
tablet: [],
|
|
257
|
+
desktop: [],
|
|
258
|
+
},
|
|
259
|
+
'merchandising-high': {
|
|
260
|
+
mobile: [
|
|
261
|
+
adSizes.outOfPage,
|
|
262
|
+
adSizes.empty,
|
|
263
|
+
adSizes.merchandisingHigh,
|
|
264
|
+
adSizes.fluid,
|
|
265
|
+
adSizes.mpu,
|
|
266
|
+
],
|
|
267
|
+
tablet: [
|
|
268
|
+
adSizes.outOfPage,
|
|
269
|
+
adSizes.empty,
|
|
270
|
+
adSizes.merchandisingHigh,
|
|
271
|
+
adSizes.fluid,
|
|
272
|
+
],
|
|
273
|
+
desktop: [
|
|
274
|
+
adSizes.outOfPage,
|
|
275
|
+
adSizes.empty,
|
|
276
|
+
adSizes.merchandisingHigh,
|
|
277
|
+
adSizes.fluid,
|
|
278
|
+
adSizes.billboard,
|
|
279
|
+
],
|
|
280
|
+
},
|
|
281
|
+
merchandising: {
|
|
282
|
+
mobile: [
|
|
283
|
+
adSizes.outOfPage,
|
|
284
|
+
adSizes.empty,
|
|
285
|
+
adSizes.merchandising,
|
|
286
|
+
adSizes.fluid,
|
|
287
|
+
adSizes.mpu,
|
|
288
|
+
],
|
|
289
|
+
tablet: [
|
|
290
|
+
adSizes.outOfPage,
|
|
291
|
+
adSizes.empty,
|
|
292
|
+
adSizes.merchandising,
|
|
293
|
+
adSizes.fluid,
|
|
294
|
+
],
|
|
295
|
+
desktop: [
|
|
296
|
+
adSizes.outOfPage,
|
|
297
|
+
adSizes.empty,
|
|
298
|
+
adSizes.merchandising,
|
|
299
|
+
adSizes.fluid,
|
|
300
|
+
adSizes.billboard,
|
|
301
|
+
],
|
|
302
|
+
},
|
|
303
|
+
survey: {
|
|
304
|
+
desktop: [adSizes.outOfPage],
|
|
305
|
+
},
|
|
306
|
+
carrot: {
|
|
307
|
+
mobile: [adSizes.fluid],
|
|
308
|
+
},
|
|
309
|
+
'mobile-sticky': {
|
|
310
|
+
mobile: [adSizes.mobilesticky, adSizes.empty, createAdSize(300, 50)],
|
|
311
|
+
},
|
|
312
|
+
'crossword-banner-mobile': {
|
|
313
|
+
mobile: [adSizes.mobilesticky],
|
|
314
|
+
},
|
|
315
|
+
'football-right': {
|
|
316
|
+
desktop: [
|
|
317
|
+
adSizes.empty,
|
|
318
|
+
adSizes.mpu,
|
|
319
|
+
adSizes.skyscraper,
|
|
320
|
+
adSizes.halfPage,
|
|
321
|
+
],
|
|
322
|
+
},
|
|
323
|
+
'article-end': {
|
|
324
|
+
mobile: [], // Mappings are dynamically added for this slot using additionalSizes
|
|
325
|
+
},
|
|
326
|
+
exclusion: {
|
|
327
|
+
mobile: [adSizes.empty],
|
|
328
|
+
},
|
|
329
|
+
/**
|
|
330
|
+
* @deprecated Use `slotSizeMappings['sponsor-logo']` instead
|
|
331
|
+
*/
|
|
332
|
+
external: {
|
|
333
|
+
mobile: [adSizes.outOfPage, adSizes.empty, adSizes.fluid, adSizes.mpu],
|
|
334
|
+
},
|
|
335
|
+
'sponsor-logo': {
|
|
336
|
+
mobile: [
|
|
337
|
+
adSizes.outOfPage,
|
|
338
|
+
adSizes.empty,
|
|
339
|
+
adSizes.fluid,
|
|
340
|
+
adSizes.sponsorLogo,
|
|
341
|
+
],
|
|
342
|
+
},
|
|
343
|
+
interactive: {
|
|
344
|
+
// Mappings are dynamically added for this slot using data attributes
|
|
345
|
+
mobile: [adSizes.outOfPage, adSizes.empty],
|
|
346
|
+
tablet: [adSizes.outOfPage, adSizes.empty],
|
|
347
|
+
desktop: [adSizes.outOfPage, adSizes.empty],
|
|
348
|
+
},
|
|
349
|
+
};
|
|
350
|
+
const getAdSize = (size) => adSizes[size];
|
|
351
|
+
/**
|
|
352
|
+
* Finds the ad sizes that will be used for a breakpoint given a size mapping
|
|
353
|
+
*
|
|
354
|
+
* If ad sizes are defined in the size mapping for the specified breakpoint, we use that.
|
|
355
|
+
* If no sizes are defined for the breakpoint, use the next smallest breakpoint with defined ad sizes.
|
|
356
|
+
* If no smaller breakpoints have defined ad sizes, return an empty array
|
|
357
|
+
*
|
|
358
|
+
* Example:
|
|
359
|
+
* For the following slotSizeMappings:
|
|
360
|
+
* inline: {
|
|
361
|
+
* phablet: [adSizes.mpu],
|
|
362
|
+
* desktop: [adSizes.billboard]
|
|
363
|
+
* }
|
|
364
|
+
* the applied sizes for each breakpoint for the "inline" slot will be:
|
|
365
|
+
* mobile: []
|
|
366
|
+
* phablet: [adSizes.mpu]
|
|
367
|
+
* tablet: [adSizes.mpu]
|
|
368
|
+
* desktop: [adSizes.billboard]
|
|
369
|
+
*
|
|
370
|
+
* See ad-sizes.test.ts for more examples
|
|
371
|
+
*/
|
|
372
|
+
const findAppliedSizesForBreakpoint = (sizeMappings, breakpoint) => {
|
|
373
|
+
if (!isBreakpoint(breakpoint)) {
|
|
374
|
+
return [];
|
|
375
|
+
}
|
|
376
|
+
let breakpointIndex = breakpoints.findIndex((b) => b === breakpoint);
|
|
377
|
+
while (breakpointIndex >= 0) {
|
|
378
|
+
const breakpointToTry = breakpoints[breakpointIndex];
|
|
379
|
+
const sizeMapping = sizeMappings[breakpointToTry];
|
|
380
|
+
if (sizeMapping?.length) {
|
|
381
|
+
return sizeMapping;
|
|
382
|
+
}
|
|
383
|
+
breakpointIndex--;
|
|
384
|
+
}
|
|
385
|
+
// There are no size mappings defined for any size smaller than the breakpoint
|
|
386
|
+
return [];
|
|
387
|
+
};
|
|
388
|
+
// Export for testing
|
|
389
|
+
export const _ = { createAdSize };
|
|
390
|
+
export { AdSize, adSizes, standardAdSizes, outstreamSizes, getAdSize, slotSizeMappings, createAdSize, findAppliedSizesForBreakpoint, };
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Breakpoints ordered from smallest to largest
|
|
3
|
+
*/
|
|
4
|
+
declare const breakpoints: readonly ["mobile", "phablet", "tablet", "desktop", "wide"];
|
|
5
|
+
type Breakpoint = (typeof breakpoints)[number];
|
|
6
|
+
declare const isBreakpoint: (s: string) => s is Breakpoint;
|
|
7
|
+
export type { Breakpoint };
|
|
8
|
+
export { breakpoints, isBreakpoint };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit: pixels.
|
|
3
|
+
*
|
|
4
|
+
* The majority of ads in the top banner are 250px high. We ran an experiment
|
|
5
|
+
* in October 2021 to set the minimum height to 250, and let smaller ads be
|
|
6
|
+
* centred in the space. We did not process with this option, as it had a
|
|
7
|
+
* negative impact on viewability and revenue.
|
|
8
|
+
*
|
|
9
|
+
*/
|
|
10
|
+
export declare const TOP_ABOVE_NAV_HEIGHT = 90;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit: pixels.
|
|
3
|
+
*
|
|
4
|
+
* The majority of ads in the top banner are 250px high. We ran an experiment
|
|
5
|
+
* in October 2021 to set the minimum height to 250, and let smaller ads be
|
|
6
|
+
* centred in the space. We did not process with this option, as it had a
|
|
7
|
+
* negative impact on viewability and revenue.
|
|
8
|
+
*
|
|
9
|
+
*/
|
|
10
|
+
export const TOP_ABOVE_NAV_HEIGHT = 90;
|
|
11
|
+
/*
|
|
12
|
+
Further Notes
|
|
13
|
+
=============
|
|
14
|
+
|
|
15
|
+
There was a very positive impact on CLS (Cumulative Layout Shift), which is good
|
|
16
|
+
for UX. However, the negative commercial impact meant we kept a height of 90px.
|
|
17
|
+
|
|
18
|
+
We ran a 1% server-side experiment to measure CLS when dedicating 250px for the
|
|
19
|
+
topAboveNav. The experiment showed this change has a significant positive impact
|
|
20
|
+
on CLS, and moves average CLS for the page from 0.09 to 0.07 (a 26% improvement
|
|
21
|
+
from this one change). The other way we sliced the data was to look at the
|
|
22
|
+
percent of pages that Google categorised as having 'good', 'needs improvement'
|
|
23
|
+
or 'poor' CLS scores. The viewability for the page dropped by about 1%, and for
|
|
24
|
+
that specific slot, by 4-6%.
|
|
25
|
+
|
|
26
|
+
When the experiment ran, the breakdown was as follows:
|
|
27
|
+
|
|
28
|
+
- 74% of our pages have a “good” CLS score
|
|
29
|
+
- 12% have a “poor” CLS score.
|
|
30
|
+
- 70% viewability for top-above-nav
|
|
31
|
+
|
|
32
|
+
This change resulted in:
|
|
33
|
+
|
|
34
|
+
- 84% “good”
|
|
35
|
+
- 4% “poor”
|
|
36
|
+
- 64% viewability for top-above-nav
|
|
37
|
+
|
|
38
|
+
Relevant Pull Requests
|
|
39
|
+
----------------------
|
|
40
|
+
|
|
41
|
+
- https://github.com/guardian/frontend/pull/24095
|
|
42
|
+
- https://github.com/guardian/dotcom-rendering/pull/3497
|
|
43
|
+
- https://github.com/guardian/dotcom-rendering/pull/3340
|
|
44
|
+
|
|
45
|
+
*/
|