@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,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
Detect whether or not the user has an ad blocking extension enabled.
|
|
3
|
+
A few ad blockers are not detectable with this approach e.g. Safari / Adblock
|
|
4
|
+
Code inspired by just-detect-adblock's:
|
|
5
|
+
https://github.com/wmcmurray/just-detect-adblock/blob/master/src/helpers.js
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Determines whether or not the user has an ad blocking extension enabled.
|
|
9
|
+
* Note: positive results can be considered reliable while negative ones may not be.
|
|
10
|
+
* @returns Promise
|
|
11
|
+
*/
|
|
12
|
+
export declare function isAdBlockInUse(): Promise<boolean>;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
Detect whether or not the user has an ad blocking extension enabled.
|
|
3
|
+
A few ad blockers are not detectable with this approach e.g. Safari / Adblock
|
|
4
|
+
Code inspired by just-detect-adblock's:
|
|
5
|
+
https://github.com/wmcmurray/just-detect-adblock/blob/master/src/helpers.js
|
|
6
|
+
*/
|
|
7
|
+
/*istanbul ignore file -- adElementBlocked can't be tested without patching each of the properties of
|
|
8
|
+
HTMLElement.prototype that it accesses, defeating the purpose of the test! */
|
|
9
|
+
let adBlockInUse = undefined;
|
|
10
|
+
function adElementBlocked(ad) {
|
|
11
|
+
if (ad.offsetParent === null ||
|
|
12
|
+
ad.offsetHeight === 0 ||
|
|
13
|
+
ad.offsetLeft === 0 ||
|
|
14
|
+
ad.offsetTop === 0 ||
|
|
15
|
+
ad.offsetWidth === 0 ||
|
|
16
|
+
ad.clientHeight === 0 ||
|
|
17
|
+
ad.clientWidth === 0) {
|
|
18
|
+
return true;
|
|
19
|
+
}
|
|
20
|
+
const adStyles = window.getComputedStyle(ad);
|
|
21
|
+
if (adStyles.getPropertyValue('display') === 'none')
|
|
22
|
+
return true;
|
|
23
|
+
if (adStyles.getPropertyValue('visibility') === 'hidden')
|
|
24
|
+
return true;
|
|
25
|
+
const mozBindingProp = adStyles.getPropertyValue('-moz-binding');
|
|
26
|
+
if (mozBindingProp.includes('about:'))
|
|
27
|
+
return true;
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Determines whether or not the user has an ad blocking extension enabled.
|
|
32
|
+
* Note: positive results can be considered reliable while negative ones may not be.
|
|
33
|
+
* @returns Promise
|
|
34
|
+
*/
|
|
35
|
+
export function isAdBlockInUse() {
|
|
36
|
+
if (adBlockInUse !== undefined) {
|
|
37
|
+
return Promise.resolve(adBlockInUse);
|
|
38
|
+
}
|
|
39
|
+
if (typeof window.getComputedStyle !== 'function') {
|
|
40
|
+
// Old browsers not supporting getComputedStyle most likely won't have adBlockers
|
|
41
|
+
adBlockInUse = false;
|
|
42
|
+
return Promise.resolve(adBlockInUse);
|
|
43
|
+
}
|
|
44
|
+
return new Promise((resolve) => {
|
|
45
|
+
window.requestAnimationFrame(() => {
|
|
46
|
+
// create a fake ad element and append it to the document
|
|
47
|
+
const ad = document.createElement('div');
|
|
48
|
+
ad.setAttribute('class', 'ad_unit pub_300x250 pub_300x250m pub_728x90 text-ad textAd text_ad text_ads text-ads text-ad-links ad-text adSense adBlock adContent adBanner');
|
|
49
|
+
ad.setAttribute('style', 'width: 1px !important; height: 1px !important; position: absolute !important; left: -10000px !important; top: -1000px !important;');
|
|
50
|
+
document.body.appendChild(ad);
|
|
51
|
+
// avoid a forced layout
|
|
52
|
+
window.requestAnimationFrame(() => {
|
|
53
|
+
// if the ad element has been hidden, an ad blocker is enabled.
|
|
54
|
+
resolve(adElementBlocked(ad));
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import type { ConnectionType } from './types';
|
|
2
|
+
declare const supportsPerformanceAPI: () => boolean;
|
|
3
|
+
interface EventTimerProperties {
|
|
4
|
+
type?: ConnectionType;
|
|
5
|
+
downlink?: number;
|
|
6
|
+
effectiveType?: string;
|
|
7
|
+
adSlotsInline?: number;
|
|
8
|
+
adSlotsTotal?: number;
|
|
9
|
+
/** the height of the page / the viewport height */
|
|
10
|
+
pageHeightVH?: number;
|
|
11
|
+
gpcSignal?: number;
|
|
12
|
+
/** distance in percentage of viewport height at which ads are lazy loaded */
|
|
13
|
+
lazyLoadMarginPercent?: number;
|
|
14
|
+
hasLabsContainer?: boolean;
|
|
15
|
+
labsUrl?: string;
|
|
16
|
+
/** Record whether we've detected an ad blocker. This is intentionally
|
|
17
|
+
* distinct from the property we pass into commercial metrics, and in the
|
|
18
|
+
* future _could_ be removed in favour of this property */
|
|
19
|
+
detectedAdBlocker?: boolean;
|
|
20
|
+
/** Record whether we've shown the adblock as message */
|
|
21
|
+
didDisplayAdBlockAsk?: boolean;
|
|
22
|
+
/** creative ID of a video interscroller for video reporting metrics */
|
|
23
|
+
videoInterscrollerCreativeId?: number | null | undefined;
|
|
24
|
+
/** percentage progress of video interscroller on page unload */
|
|
25
|
+
videoInterscrollerPercentageProgress?: number;
|
|
26
|
+
}
|
|
27
|
+
declare class EventTimer {
|
|
28
|
+
private _marks;
|
|
29
|
+
private _measures;
|
|
30
|
+
properties: EventTimerProperties;
|
|
31
|
+
/**
|
|
32
|
+
* Initialise the EventTimer class on page.
|
|
33
|
+
* Returns the singleton instance of the EventTimer class and binds
|
|
34
|
+
* to window.guardian.commercialTimer. If it's been previously
|
|
35
|
+
* initialised and bound it returns the original instance
|
|
36
|
+
* Note: We save to window.guardian.commercialTimer because
|
|
37
|
+
* different bundles (DCR / DCP) can use commercial, and we want
|
|
38
|
+
* all timer events saved to a single instance per-page
|
|
39
|
+
* @returns {EventTimer} Instance of EventTimer
|
|
40
|
+
*/
|
|
41
|
+
static init(): EventTimer;
|
|
42
|
+
/**
|
|
43
|
+
* Just a helper method to access the singleton instance of EventTimer.
|
|
44
|
+
* Typical use case is EventTimer.get().trigger
|
|
45
|
+
*/
|
|
46
|
+
static get(): EventTimer;
|
|
47
|
+
/**
|
|
48
|
+
* These are marks that are not triggered by commercial but we are interested in
|
|
49
|
+
* tracking their performance. For example, CMP-related events.
|
|
50
|
+
**/
|
|
51
|
+
private get _externalMarks();
|
|
52
|
+
/**
|
|
53
|
+
* Returns all performance marks that should be saved as commercial metrics.
|
|
54
|
+
*/
|
|
55
|
+
get marks(): {
|
|
56
|
+
name: string;
|
|
57
|
+
ts: number;
|
|
58
|
+
}[];
|
|
59
|
+
/**
|
|
60
|
+
* Returns all performance measures that should be saved as commercial metrics.
|
|
61
|
+
*/
|
|
62
|
+
get measures(): {
|
|
63
|
+
name: string;
|
|
64
|
+
duration: number;
|
|
65
|
+
}[];
|
|
66
|
+
private constructor();
|
|
67
|
+
/**
|
|
68
|
+
* Adds a non timer measurement
|
|
69
|
+
*
|
|
70
|
+
* @param {string} name - the property's name
|
|
71
|
+
* @param value - the property's value
|
|
72
|
+
*/
|
|
73
|
+
setProperty<T extends keyof EventTimerProperties>(name: T, value: EventTimerProperties[T]): void;
|
|
74
|
+
/**
|
|
75
|
+
* Creates a new performance mark, and if the mark ends with 'End' it will
|
|
76
|
+
* create a performance measure between the start and end marks.
|
|
77
|
+
*
|
|
78
|
+
* Marks can be triggered multiple times, but we only save the first
|
|
79
|
+
* instance of a mark, as things like ad refreshes can trigger the same mark.
|
|
80
|
+
*
|
|
81
|
+
* More info on the performance API:
|
|
82
|
+
* https://developer.mozilla.org/en-US/docs/Web/API/Performance/mark
|
|
83
|
+
* https://developer.mozilla.org/en-US/docs/Web/API/Performance/measure
|
|
84
|
+
*
|
|
85
|
+
* @todo more strict typing for eventName and origin
|
|
86
|
+
* @param eventName The short name applied to the mark
|
|
87
|
+
* @param origin - Either 'page' (default) or the name of the slot
|
|
88
|
+
*/
|
|
89
|
+
mark(eventName: string, origin?: string): void;
|
|
90
|
+
/**
|
|
91
|
+
* Creates a performance measure given the name of the end marks.
|
|
92
|
+
* The start mark is inferred from the end mark.
|
|
93
|
+
*
|
|
94
|
+
* @param endMark - The name of the mark that ends the measure
|
|
95
|
+
**/
|
|
96
|
+
private measure;
|
|
97
|
+
}
|
|
98
|
+
declare const _: {
|
|
99
|
+
slotMarks: readonly ["slotReady", "adRenderStart", "prebidStart", "adOnPage", "viewable"];
|
|
100
|
+
slotMeasures: readonly ["adRender", "defineSlot", "prepareSlot", "prebid", "fetchAd"];
|
|
101
|
+
trackedSlots: readonly ["top-above-nav", "inline1", "inline2", "fronts-banner-1", "fronts-banner-2"];
|
|
102
|
+
};
|
|
103
|
+
export { EventTimer, _, supportsPerformanceAPI };
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import { log } from '@guardian/libs';
|
|
2
|
+
const supportsPerformanceAPI = () => typeof window !== 'undefined' &&
|
|
3
|
+
typeof window.performance !== 'undefined' &&
|
|
4
|
+
typeof window.performance.mark === 'function';
|
|
5
|
+
// Events will be logged using the performance API for all slots, but only these slots will be tracked as commercial metrics and sent to the data lake
|
|
6
|
+
const trackedSlots = [
|
|
7
|
+
'top-above-nav',
|
|
8
|
+
'inline1',
|
|
9
|
+
'inline2',
|
|
10
|
+
'fronts-banner-1',
|
|
11
|
+
'fronts-banner-2',
|
|
12
|
+
];
|
|
13
|
+
// marks that we want to save as commercial metrics
|
|
14
|
+
const slotMarks = [
|
|
15
|
+
'slotReady',
|
|
16
|
+
'adRenderStart',
|
|
17
|
+
'prebidStart',
|
|
18
|
+
'adOnPage',
|
|
19
|
+
'viewable',
|
|
20
|
+
];
|
|
21
|
+
// measures that we want to save as commercial metrics
|
|
22
|
+
const slotMeasures = [
|
|
23
|
+
'adRender',
|
|
24
|
+
'defineSlot',
|
|
25
|
+
'prepareSlot',
|
|
26
|
+
'prebid',
|
|
27
|
+
'fetchAd',
|
|
28
|
+
];
|
|
29
|
+
const pageMarks = ['commercialStart', 'commercialModulesLoaded'];
|
|
30
|
+
// measures that we want to save as commercial metrics
|
|
31
|
+
const pageMeasures = ['commercialBoot', 'googletagInit'];
|
|
32
|
+
// all marks, including the measure start and end marks
|
|
33
|
+
const allSlotMarks = [
|
|
34
|
+
...slotMarks,
|
|
35
|
+
...slotMeasures.map((measure) => `${measure}Start`),
|
|
36
|
+
...slotMeasures.map((measure) => `${measure}End`),
|
|
37
|
+
];
|
|
38
|
+
const externalMarks = [
|
|
39
|
+
'cmp-init',
|
|
40
|
+
'cmp-ui-displayed',
|
|
41
|
+
'cmp-got-consent',
|
|
42
|
+
];
|
|
43
|
+
const shouldSave = (name) => {
|
|
44
|
+
let [origin, type] = name.split('_');
|
|
45
|
+
if (!type) {
|
|
46
|
+
type = origin;
|
|
47
|
+
origin = 'page';
|
|
48
|
+
}
|
|
49
|
+
const shouldSaveMark = (trackedSlots.includes(origin) &&
|
|
50
|
+
slotMarks.includes(type)) ||
|
|
51
|
+
(origin === 'page' && pageMarks.includes(type));
|
|
52
|
+
const shouldSaveMeasure = (trackedSlots.includes(origin) &&
|
|
53
|
+
slotMeasures.includes(type)) ||
|
|
54
|
+
(origin === 'page' && pageMeasures.includes(type));
|
|
55
|
+
return shouldSaveMark || shouldSaveMeasure;
|
|
56
|
+
};
|
|
57
|
+
class EventTimer {
|
|
58
|
+
_marks;
|
|
59
|
+
_measures;
|
|
60
|
+
properties;
|
|
61
|
+
/**
|
|
62
|
+
* Initialise the EventTimer class on page.
|
|
63
|
+
* Returns the singleton instance of the EventTimer class and binds
|
|
64
|
+
* to window.guardian.commercialTimer. If it's been previously
|
|
65
|
+
* initialised and bound it returns the original instance
|
|
66
|
+
* Note: We save to window.guardian.commercialTimer because
|
|
67
|
+
* different bundles (DCR / DCP) can use commercial, and we want
|
|
68
|
+
* all timer events saved to a single instance per-page
|
|
69
|
+
* @returns {EventTimer} Instance of EventTimer
|
|
70
|
+
*/
|
|
71
|
+
static init() {
|
|
72
|
+
return (window.guardian.commercialTimer ??= new EventTimer());
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Just a helper method to access the singleton instance of EventTimer.
|
|
76
|
+
* Typical use case is EventTimer.get().trigger
|
|
77
|
+
*/
|
|
78
|
+
static get() {
|
|
79
|
+
return this.init();
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* These are marks that are not triggered by commercial but we are interested in
|
|
83
|
+
* tracking their performance. For example, CMP-related events.
|
|
84
|
+
**/
|
|
85
|
+
get _externalMarks() {
|
|
86
|
+
if (!supportsPerformanceAPI()) {
|
|
87
|
+
return new Map();
|
|
88
|
+
}
|
|
89
|
+
return externalMarks.reduce((map, mark) => {
|
|
90
|
+
const entries = window.performance.getEntriesByName(mark);
|
|
91
|
+
if (entries.length && entries[0]) {
|
|
92
|
+
map.set(mark, entries[0]);
|
|
93
|
+
}
|
|
94
|
+
return map;
|
|
95
|
+
}, new Map());
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Returns all performance marks that should be saved as commercial metrics.
|
|
99
|
+
*/
|
|
100
|
+
get marks() {
|
|
101
|
+
return [...this._marks, ...this._externalMarks].map(([name, timer]) => ({
|
|
102
|
+
name,
|
|
103
|
+
ts: timer.startTime,
|
|
104
|
+
}));
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Returns all performance measures that should be saved as commercial metrics.
|
|
108
|
+
*/
|
|
109
|
+
get measures() {
|
|
110
|
+
return [...this._measures].map(([name, measure]) => ({
|
|
111
|
+
name,
|
|
112
|
+
duration: measure.duration,
|
|
113
|
+
}));
|
|
114
|
+
}
|
|
115
|
+
constructor() {
|
|
116
|
+
this._marks = new Map();
|
|
117
|
+
this._measures = new Map();
|
|
118
|
+
this.properties = {};
|
|
119
|
+
if (window.navigator.connection) {
|
|
120
|
+
this.properties.type = window.navigator.connection.type;
|
|
121
|
+
this.properties.downlink = window.navigator.connection.downlink;
|
|
122
|
+
this.properties.effectiveType =
|
|
123
|
+
window.navigator.connection.effectiveType;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Adds a non timer measurement
|
|
128
|
+
*
|
|
129
|
+
* @param {string} name - the property's name
|
|
130
|
+
* @param value - the property's value
|
|
131
|
+
*/
|
|
132
|
+
setProperty(name, value) {
|
|
133
|
+
this.properties[name] = value;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Creates a new performance mark, and if the mark ends with 'End' it will
|
|
137
|
+
* create a performance measure between the start and end marks.
|
|
138
|
+
*
|
|
139
|
+
* Marks can be triggered multiple times, but we only save the first
|
|
140
|
+
* instance of a mark, as things like ad refreshes can trigger the same mark.
|
|
141
|
+
*
|
|
142
|
+
* More info on the performance API:
|
|
143
|
+
* https://developer.mozilla.org/en-US/docs/Web/API/Performance/mark
|
|
144
|
+
* https://developer.mozilla.org/en-US/docs/Web/API/Performance/measure
|
|
145
|
+
*
|
|
146
|
+
* @todo more strict typing for eventName and origin
|
|
147
|
+
* @param eventName The short name applied to the mark
|
|
148
|
+
* @param origin - Either 'page' (default) or the name of the slot
|
|
149
|
+
*/
|
|
150
|
+
mark(eventName, origin = 'page') {
|
|
151
|
+
let name = eventName;
|
|
152
|
+
if (allSlotMarks.includes(eventName) && origin !== 'page') {
|
|
153
|
+
name = `${origin}_${name}`;
|
|
154
|
+
}
|
|
155
|
+
if (!!this._marks.get(name) || !supportsPerformanceAPI()) {
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
const mark = window.performance.mark(name);
|
|
159
|
+
if (
|
|
160
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- browser support is patchy
|
|
161
|
+
typeof mark?.startTime === 'number' &&
|
|
162
|
+
// we only want to save the marks that are related to certain slots or the page
|
|
163
|
+
shouldSave(name)) {
|
|
164
|
+
this._marks.set(name, mark);
|
|
165
|
+
}
|
|
166
|
+
if (name.endsWith('End')) {
|
|
167
|
+
this.measure(name);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Creates a performance measure given the name of the end marks.
|
|
172
|
+
* The start mark is inferred from the end mark.
|
|
173
|
+
*
|
|
174
|
+
* @param endMark - The name of the mark that ends the measure
|
|
175
|
+
**/
|
|
176
|
+
measure(endMark) {
|
|
177
|
+
const startMark = endMark.replace('End', 'Start');
|
|
178
|
+
const measureName = endMark.replace('End', '');
|
|
179
|
+
const startMarkExists = window.performance.getEntriesByName(startMark).length > 0;
|
|
180
|
+
if (startMarkExists) {
|
|
181
|
+
try {
|
|
182
|
+
const measure = window.performance.measure(measureName, startMark, endMark);
|
|
183
|
+
// we only want to save the measures that are related to certain slots or the page
|
|
184
|
+
if (measure && shouldSave(measureName)) {
|
|
185
|
+
this._measures.set(measureName, measure);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
catch (e) {
|
|
189
|
+
log('commercial', `error measuring ${measureName}`, e);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
const _ = {
|
|
195
|
+
slotMarks,
|
|
196
|
+
slotMeasures,
|
|
197
|
+
trackedSlots,
|
|
198
|
+
};
|
|
199
|
+
export { EventTimer, _, supportsPerformanceAPI };
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { getCookie, isString, storage } from '@guardian/libs';
|
|
2
|
+
const editionToCountryCodeMap = {
|
|
3
|
+
UK: 'GB',
|
|
4
|
+
US: 'US',
|
|
5
|
+
AU: 'AU',
|
|
6
|
+
};
|
|
7
|
+
const editionToCountryCode = (editionKey = 'UK') => editionToCountryCodeMap[editionKey];
|
|
8
|
+
const countryCookieName = 'GU_geo_country';
|
|
9
|
+
const countryOverrideName = 'gu.geo.override';
|
|
10
|
+
let locale;
|
|
11
|
+
/*
|
|
12
|
+
This method can be used as a non async way of getting the country code
|
|
13
|
+
after init has been called. Returning locale should cover all/most
|
|
14
|
+
of the cases but if a race condition happen or the cookie is not set,
|
|
15
|
+
we keep fallbacks to cookie or geo from edition.
|
|
16
|
+
*/
|
|
17
|
+
const getCountryCode = () => {
|
|
18
|
+
const pageEdition = window.guardian.config.page.edition;
|
|
19
|
+
const maybeCountryOverride = storage.local.get(countryOverrideName);
|
|
20
|
+
const countryOverride = isString(maybeCountryOverride)
|
|
21
|
+
? maybeCountryOverride
|
|
22
|
+
: null;
|
|
23
|
+
return (locale ??
|
|
24
|
+
countryOverride ??
|
|
25
|
+
getCookie({
|
|
26
|
+
name: countryCookieName,
|
|
27
|
+
shouldMemoize: true,
|
|
28
|
+
}) ??
|
|
29
|
+
editionToCountryCode(pageEdition));
|
|
30
|
+
};
|
|
31
|
+
export { getCountryCode };
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export declare const isInUk: () => boolean;
|
|
2
|
+
export declare const isInUsa: () => boolean;
|
|
3
|
+
export declare const isInCanada: () => boolean;
|
|
4
|
+
export declare const isInAustralia: () => boolean;
|
|
5
|
+
export declare const isInNewZealand: () => boolean;
|
|
6
|
+
export declare const isInUsOrCa: () => boolean;
|
|
7
|
+
export declare const isInAuOrNz: () => boolean;
|
|
8
|
+
export declare const isInRow: () => boolean;
|
|
9
|
+
export declare const _: {
|
|
10
|
+
resetModule: () => void;
|
|
11
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { getCountryCode } from './country-code';
|
|
2
|
+
// cache the users location so we only have to look it up once
|
|
3
|
+
let geo;
|
|
4
|
+
const currentGeoLocation = () => {
|
|
5
|
+
geo = geo ?? getCountryCode();
|
|
6
|
+
return geo;
|
|
7
|
+
};
|
|
8
|
+
export const isInUk = () => currentGeoLocation() === 'GB';
|
|
9
|
+
export const isInUsa = () => currentGeoLocation() === 'US';
|
|
10
|
+
export const isInCanada = () => currentGeoLocation() === 'CA';
|
|
11
|
+
export const isInAustralia = () => currentGeoLocation() === 'AU';
|
|
12
|
+
export const isInNewZealand = () => currentGeoLocation() === 'NZ';
|
|
13
|
+
export const isInUsOrCa = () => isInUsa() || isInCanada();
|
|
14
|
+
export const isInAuOrNz = () => isInAustralia() || isInNewZealand();
|
|
15
|
+
export const isInRow = () => !isInUk() && !isInUsOrCa() && !isInAuOrNz();
|
|
16
|
+
export const _ = {
|
|
17
|
+
resetModule: () => {
|
|
18
|
+
geo = undefined;
|
|
19
|
+
},
|
|
20
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { CountryCode } from '@guardian/libs';
|
|
2
|
+
export declare const __resetCachedValue: () => void;
|
|
3
|
+
/**
|
|
4
|
+
* Fetches the user's current location as an ISO 3166-1 alpha-2 string e.g. 'GB', 'AU' etc
|
|
5
|
+
* Note: This has been copied from guardian-libs and made syncronous by ommiting the call to
|
|
6
|
+
* the geolocation API
|
|
7
|
+
*/
|
|
8
|
+
export declare const getLocale: () => CountryCode;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { getCookie, isString, storage } from '@guardian/libs';
|
|
2
|
+
const KEY = 'GU_geo_country';
|
|
3
|
+
const KEY_OVERRIDE = 'gu.geo.override';
|
|
4
|
+
const COUNTRY_REGEX = /^[A-Z]{2}$/;
|
|
5
|
+
// best guess that we have a valid code, without actually shipping the entire list
|
|
6
|
+
const isValidCountryCode = (country) => isString(country) && COUNTRY_REGEX.test(country);
|
|
7
|
+
// we'll cache any successful lookups so we only have to do this once
|
|
8
|
+
let locale;
|
|
9
|
+
const editionToGeolocationMap = {
|
|
10
|
+
UK: 'GB',
|
|
11
|
+
US: 'US',
|
|
12
|
+
AU: 'AU',
|
|
13
|
+
};
|
|
14
|
+
const editionToGeolocation = (editionKey) => editionToGeolocationMap[editionKey];
|
|
15
|
+
// just used for tests
|
|
16
|
+
export const __resetCachedValue = () => (locale = undefined);
|
|
17
|
+
/**
|
|
18
|
+
* Fetches the user's current location as an ISO 3166-1 alpha-2 string e.g. 'GB', 'AU' etc
|
|
19
|
+
* Note: This has been copied from guardian-libs and made syncronous by ommiting the call to
|
|
20
|
+
* the geolocation API
|
|
21
|
+
*/
|
|
22
|
+
export const getLocale = () => {
|
|
23
|
+
if (locale)
|
|
24
|
+
return locale;
|
|
25
|
+
// return overridden geo from localStorage, used for changing geo only for development purposes
|
|
26
|
+
const geoOverride = storage.local.get(KEY_OVERRIDE);
|
|
27
|
+
if (isValidCountryCode(geoOverride)) {
|
|
28
|
+
return (locale = geoOverride);
|
|
29
|
+
}
|
|
30
|
+
// return locale from cookie if it exists
|
|
31
|
+
const stored = getCookie({ name: KEY });
|
|
32
|
+
if (stored && isValidCountryCode(stored)) {
|
|
33
|
+
return (locale = stored);
|
|
34
|
+
}
|
|
35
|
+
// return locale from edition
|
|
36
|
+
const editionCountryCode = editionToGeolocation(window.guardian.config.page.edition);
|
|
37
|
+
return (locale = editionCountryCode);
|
|
38
|
+
};
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import type { EventTimer } from './event-timer';
|
|
2
|
+
import type { AdBlockers, Apstag, ArticleCounts, ComscoreGlobals, Confiant, Config, DfpEnv, FetchBidResponse, GoogleTagParams, GoogleTrackConversionObject, HeaderNotification, IasPET, NetworkInformation, NSdkInstance, Ophan, OptOutAdSlot, OptOutInitializeOptions, Permutive, SafeFrameAPI, TeadsAnalytics, Trac } from './types';
|
|
3
|
+
declare global {
|
|
4
|
+
interface Navigator {
|
|
5
|
+
readonly connection?: NetworkInformation;
|
|
6
|
+
readonly cookieDeprecationLabel?: {
|
|
7
|
+
getValue: () => Promise<string>;
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
interface Window {
|
|
11
|
+
guardian: {
|
|
12
|
+
ophan?: Ophan;
|
|
13
|
+
config: Config;
|
|
14
|
+
queue: Array<() => Promise<void>>;
|
|
15
|
+
mustardCut?: boolean;
|
|
16
|
+
polyfilled?: boolean;
|
|
17
|
+
adBlockers: AdBlockers;
|
|
18
|
+
css: {
|
|
19
|
+
onLoad: () => void;
|
|
20
|
+
loaded: boolean;
|
|
21
|
+
};
|
|
22
|
+
articleCounts?: ArticleCounts;
|
|
23
|
+
commercial?: {
|
|
24
|
+
dfpEnv?: DfpEnv;
|
|
25
|
+
a9WinningBids?: FetchBidResponse[];
|
|
26
|
+
};
|
|
27
|
+
notificationEventHistory?: HeaderNotification[][];
|
|
28
|
+
commercialTimer?: EventTimer;
|
|
29
|
+
offlineCount?: number;
|
|
30
|
+
modules: {
|
|
31
|
+
sentry?: {
|
|
32
|
+
reportError?: (error: Error, feature: string, tags?: Record<string, string>, extras?: Record<string, unknown>) => void;
|
|
33
|
+
};
|
|
34
|
+
};
|
|
35
|
+
};
|
|
36
|
+
ootag: {
|
|
37
|
+
queue: Array<() => void>;
|
|
38
|
+
initializeOo: (o: OptOutInitializeOptions) => void;
|
|
39
|
+
addParameter: (key: string, value: string | string[]) => void;
|
|
40
|
+
addParameterForSlot: (slotId: string, key: string, value: string | string[]) => void;
|
|
41
|
+
defineSlot: (o: OptOutAdSlot) => void;
|
|
42
|
+
makeRequests: () => void;
|
|
43
|
+
refreshSlot: (slotId: string) => void;
|
|
44
|
+
refreshAllSlots: () => void;
|
|
45
|
+
logger: (...args: unknown[]) => void;
|
|
46
|
+
};
|
|
47
|
+
readonly navigator: Navigator;
|
|
48
|
+
confiant?: Confiant;
|
|
49
|
+
apstag?: Apstag;
|
|
50
|
+
permutive?: Permutive;
|
|
51
|
+
_comscore?: ComscoreGlobals[];
|
|
52
|
+
__iasPET?: IasPET;
|
|
53
|
+
teads_analytics?: TeadsAnalytics;
|
|
54
|
+
$sf: SafeFrameAPI;
|
|
55
|
+
conf: unknown;
|
|
56
|
+
NOLCMB: {
|
|
57
|
+
getInstance: (apid: string) => NSdkInstance;
|
|
58
|
+
};
|
|
59
|
+
nol_t: (pvar: {
|
|
60
|
+
cid: string;
|
|
61
|
+
content: string;
|
|
62
|
+
server: string;
|
|
63
|
+
}) => Trac;
|
|
64
|
+
google_trackConversion?: (arg0: GoogleTrackConversionObject) => void;
|
|
65
|
+
google_tag_params?: GoogleTagParams;
|
|
66
|
+
_brandmetrics?: Array<{
|
|
67
|
+
cmd: string;
|
|
68
|
+
val: Record<string, unknown>;
|
|
69
|
+
}>;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export { isAdBlockInUse } from './detect-ad-blocker';
|
|
2
|
+
export { EventTimer } from './event-timer';
|
|
3
|
+
export { adSizes } from './ad-sizes';
|
|
4
|
+
export * as constants from './constants';
|
|
5
|
+
export { bypassCommercialMetricsSampling, initCommercialMetrics, } from './send-commercial-metrics';
|
|
6
|
+
export { buildPageTargeting } from './targeting/build-page-targeting';
|
|
7
|
+
export { postMessage } from './messenger/post-message';
|
|
8
|
+
export { buildImaAdTagUrl } from './targeting/youtube-ima';
|
|
9
|
+
export { getPermutivePFPSegments } from './permutive';
|
|
10
|
+
export { isEligibleForTeads } from './targeting/teads-eligibility';
|
|
11
|
+
export type { AdSize, SizeMapping, SlotName } from './ad-sizes';
|
|
12
|
+
export type { PageTargeting } from './targeting/build-page-targeting';
|
|
13
|
+
export type { AdsConfigDisabled, AdsConfigUSNATorAus, AdsConfigTCFV2, } from './types';
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export { isAdBlockInUse } from './detect-ad-blocker';
|
|
2
|
+
export { EventTimer } from './event-timer';
|
|
3
|
+
export { adSizes } from './ad-sizes';
|
|
4
|
+
export * as constants from './constants';
|
|
5
|
+
export { bypassCommercialMetricsSampling, initCommercialMetrics, } from './send-commercial-metrics';
|
|
6
|
+
export { buildPageTargeting } from './targeting/build-page-targeting';
|
|
7
|
+
export { postMessage } from './messenger/post-message';
|
|
8
|
+
export { buildImaAdTagUrl } from './targeting/youtube-ima';
|
|
9
|
+
export { getPermutivePFPSegments } from './permutive';
|
|
10
|
+
export { isEligibleForTeads } from './targeting/teads-eligibility';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const postMessage: (message: Record<string, unknown>, target: MessageEventSource, targetOrigin?: string) => void;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
declare const getPermutiveSegments: () => string[];
|
|
2
|
+
declare const getPermutivePFPSegments: () => string[];
|
|
3
|
+
declare const clearPermutiveSegments: () => void;
|
|
4
|
+
export declare const _: {
|
|
5
|
+
PERMUTIVE_KEY: string;
|
|
6
|
+
PERMUTIVE_PFP_KEY: string;
|
|
7
|
+
getSegments: (key: string) => string[];
|
|
8
|
+
};
|
|
9
|
+
export { getPermutiveSegments, getPermutivePFPSegments, clearPermutiveSegments, };
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { storage } from '@guardian/libs';
|
|
2
|
+
const PERMUTIVE_KEY = `_papns`;
|
|
3
|
+
const PERMUTIVE_PFP_KEY = `_pdfps`;
|
|
4
|
+
const getSegments = (key) => {
|
|
5
|
+
try {
|
|
6
|
+
const rawSegments = storage.local.getRaw(key);
|
|
7
|
+
const segments = rawSegments
|
|
8
|
+
? JSON.parse(rawSegments)
|
|
9
|
+
: null;
|
|
10
|
+
if (!Array.isArray(segments))
|
|
11
|
+
return [];
|
|
12
|
+
return segments
|
|
13
|
+
.slice(0, 250)
|
|
14
|
+
.map((s) => Number.parseInt(s, 10))
|
|
15
|
+
.filter((n) => typeof n === 'number' && !Number.isNaN(n))
|
|
16
|
+
.map(String);
|
|
17
|
+
}
|
|
18
|
+
catch (err) {
|
|
19
|
+
return [];
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
const getPermutiveSegments = () => getSegments(PERMUTIVE_KEY);
|
|
23
|
+
const getPermutivePFPSegments = () => getSegments(PERMUTIVE_PFP_KEY);
|
|
24
|
+
const clearPermutiveSegments = () => {
|
|
25
|
+
storage.local.remove(PERMUTIVE_KEY);
|
|
26
|
+
storage.local.remove(PERMUTIVE_PFP_KEY);
|
|
27
|
+
};
|
|
28
|
+
export const _ = {
|
|
29
|
+
PERMUTIVE_KEY,
|
|
30
|
+
PERMUTIVE_PFP_KEY,
|
|
31
|
+
getSegments,
|
|
32
|
+
};
|
|
33
|
+
export { getPermutiveSegments, getPermutivePFPSegments, clearPermutiveSegments, };
|