@guardian/commercial-core 3.5.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.
@@ -11,7 +11,7 @@ interface SizeMapping {
11
11
  phablet?: AdSize[];
12
12
  tablet?: AdSize[];
13
13
  }
14
- interface SizeMappings {
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 sizeMappings: SizeMappings;
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, sizeMappings };
29
+ export type { AdSizeString, AdSize, SizeKeys, SizeMapping };
30
+ export { adSizes, getAdSize, slotSizeMappings };
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.sizeMappings = exports.getAdSize = exports.adSizes = exports._ = void 0;
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 sizeMappings = {
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.sizeMappings = sizeMappings;
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;
@@ -7,7 +7,7 @@ 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, sizeMappings } from './ad-sizes';
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';
package/dist/cjs/index.js CHANGED
@@ -24,7 +24,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
24
24
  return result;
25
25
  };
26
26
  Object.defineProperty(exports, "__esModule", { value: true });
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.sizeMappings = exports.getAdSize = exports.adSizes = exports.initCommercialMetrics = exports.bypassCommercialMetricsSampling = exports.EventTimer = exports.remarketing = exports.inizio = exports.twitter = exports.fbPixel = exports.permutive = exports.ias = void 0;
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;
28
28
  var ias_1 = require("./third-party-tags/ias");
29
29
  Object.defineProperty(exports, "ias", { enumerable: true, get: function () { return ias_1.ias; } });
30
30
  var permutive_1 = require("./third-party-tags/permutive");
@@ -45,7 +45,7 @@ Object.defineProperty(exports, "initCommercialMetrics", { enumerable: true, get:
45
45
  var ad_sizes_1 = require("./ad-sizes");
46
46
  Object.defineProperty(exports, "adSizes", { enumerable: true, get: function () { return ad_sizes_1.adSizes; } });
47
47
  Object.defineProperty(exports, "getAdSize", { enumerable: true, get: function () { return ad_sizes_1.getAdSize; } });
48
- Object.defineProperty(exports, "sizeMappings", { enumerable: true, get: function () { return ad_sizes_1.sizeMappings; } });
48
+ Object.defineProperty(exports, "slotSizeMappings", { enumerable: true, get: function () { return ad_sizes_1.slotSizeMappings; } });
49
49
  var detect_ad_blocker_1 = require("./detect-ad-blocker");
50
50
  Object.defineProperty(exports, "isAdBlockInUse", { enumerable: true, get: function () { return detect_ad_blocker_1.isAdBlockInUse; } });
51
51
  var permutive_2 = require("./permutive");
@@ -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 = {
@@ -11,7 +11,7 @@ interface SizeMapping {
11
11
  phablet?: AdSize[];
12
12
  tablet?: AdSize[];
13
13
  }
14
- interface SizeMappings {
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 sizeMappings: SizeMappings;
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, sizeMappings };
29
+ export type { AdSizeString, AdSize, SizeKeys, SizeMapping };
30
+ export { adSizes, getAdSize, slotSizeMappings };
@@ -39,7 +39,7 @@ const adSizes = {
39
39
  '300x1050': adSizesPartial.portrait,
40
40
  '160x600': adSizesPartial.skyscraper,
41
41
  };
42
- const sizeMappings = {
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, sizeMappings };
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
+ };
@@ -7,7 +7,7 @@ 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, sizeMappings } from './ad-sizes';
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';
package/dist/esm/index.js CHANGED
@@ -7,7 +7,7 @@ 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, sizeMappings } from './ad-sizes';
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';
@@ -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.5.0",
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": {