@guardian/ophan-tracker-js 2.1.1 → 2.1.2-next.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/package.json CHANGED
@@ -1,30 +1,40 @@
1
1
  {
2
2
  "name": "@guardian/ophan-tracker-js",
3
- "version": "2.1.1",
3
+ "version": "2.1.2-next.0",
4
4
  "type": "module",
5
5
  "engines": {
6
6
  "node": ">=16"
7
7
  },
8
8
  "files": [
9
- "assets/*.js",
10
- "assets/vendor/*.js"
9
+ "NPM-dist/**/*.js",
10
+ "NPM-dist/**/*.d.ts"
11
11
  ],
12
- "main": "assets/ng.js",
12
+ "main": "NPM-dist/ng.js",
13
+ "types": "NPM-dist/types/ng.d.ts",
13
14
  "devDependencies": {
14
- "@babel/preset-env": "7.22.10",
15
+ "@babel/preset-env": "^7.22.10",
16
+ "@babel/preset-typescript": "^7.24.7",
15
17
  "@changesets/cli": "^2.26.2",
16
18
  "@guardian/prettier": "^7.0.0",
17
19
  "@rollup/plugin-babel": "6.0.3",
18
20
  "@rollup/plugin-terser": "0.4.3",
21
+ "@rollup/plugin-typescript": "^11.1.6",
22
+ "@types/jest": "^29.5.12",
23
+ "babel-jest": "^29.7.0",
19
24
  "jest": "^29.7.0",
20
25
  "jest-environment-jsdom": "^29.7.0",
21
26
  "rollup": "3.28.0",
22
- "rollup-jest": "^3.1.0"
27
+ "rollup-jest": "^3.1.0",
28
+ "ts-jest": "^29.1.4",
29
+ "typescript": "^5.4.4"
23
30
  },
24
31
  "scripts": {
25
- "build": "NODE_ENV=production npx rollup -c",
26
- "build:test": "NODE_ENV=test npx rollup -c",
32
+ "build-for-cdn": "NODE_ENV=production npx rollup -c",
33
+ "build-for-npm": "tsc",
27
34
  "test": "jest",
28
35
  "format": "npx prettier --write ."
36
+ },
37
+ "dependencies": {
38
+ "@guardian/tsconfig": "^1.0.0"
29
39
  }
30
40
  }
package/readme.md CHANGED
@@ -1,14 +1,105 @@
1
- # Ophan Tracker JS
1
+ # Using Ophan Tracker JS
2
2
 
3
3
  Browser JavaScript client for Ophan.
4
4
 
5
5
  ## Install
6
6
 
7
+ ```bash
8
+ $ npm install @guardian/ophan-tracker-js
7
9
  ```
8
- $ npm install ophan-tracker-js
10
+
11
+ ## Usage
12
+
13
+ Tracker JS contains multiple entry point files tailored to specific platforms. These include:
14
+
15
+ - `ng.js` (short for next-gen) for the theguardian.com
16
+ - `manage-my-account.js` for manage.theguardian.com
17
+ - `support.js` for support.theguardian.com
18
+ - `membership.js` for membership-related sections
19
+
20
+ **Default Behaviour**
21
+
22
+ The [default entry point](https://github.com/guardian/ophan/blob/main/tracker-js/package.json#L12) for the library is `ng.js`. With this entry point, the library automatically initializes itself on the global window object and provides a suite of functionalities to monitor user interactions, visibility, and more.
23
+ The library will send an initial [page view event](./types/event.ts#L15) on page load. You are then in control of sending other events to Ophan.
24
+
25
+ You can see example usage of the library on theguardian.com [here](https://github.com/guardian/dotcom-rendering/blob/main/dotcom-rendering/src/client/ophan/ophan.ts)
26
+
27
+ 1. **Import and start the library:**
28
+
29
+ The library should be imported and executed eagerly (non-lazily) to ensure proper initialization. The exact syntax may vary depending on your build tool or environment.
30
+
31
+ ```javascript
32
+ import ophan from '@guardian/ophan-tracker-js';
33
+ ```
34
+
35
+ 2. **Send Tracking Events with `ophan.record()`:**
36
+
37
+ ```javascript
38
+ ophan.record({
39
+ // Your tracking data in JSON format
40
+ });
41
+ ```
42
+
43
+ ### Usage with Typescript
44
+
45
+ Refer to the type definitions within the library for the structure of valid tracking events. The `ophan.record()` function accepts a type of `EventPayload` found [here]("./types/event.ts#L95").
46
+
47
+ #### Example Usage
48
+
49
+ ```typescript
50
+ import ophan, { Product, ComponentEvent } from '@guardian/ophan-tracker-js';
51
+ import type { AbTest } from '@guardian/ophan-tracker-js';
52
+
53
+ const exampleProduct: TProduct = "APP_PREMIUM_TIER";
54
+ const exampleAbTest: AbTest = {
55
+ name: "myAbTest",
56
+ variant: "variant",
57
+ campaignCodes: ["code1", "code2"]
58
+ };
59
+
60
+ const componentV2 = {
61
+ componentType: "ACQUISITIONS_BUTTON",
62
+ id: "component-id",
63
+ products: new Set([exampleProduct]),
64
+ campaignCode: "campaign-code",
65
+ labels: new Set(["label1", "label2"]),
66
+ };
67
+
68
+ const componentEvent: ComponentEvent = {
69
+ component: componentV2,
70
+ action: "CLICK",
71
+ value: "some-value",
72
+ id: "event-id",
73
+ abTest: exampleAbTest,
74
+ targetingAbTest: exampleAbTest,
75
+ };
76
+
77
+ ophan.record({
78
+ componentEvent: componentEvent
79
+ });
9
80
  ```
10
81
 
11
- ## Contribute
82
+ If you would like to add a new event type or event property which does not exist, please [contact the Ophan team](Ophan.Dev@guardian.co.uk).
83
+
84
+ ### Sending events straight to the backend
85
+
86
+ _Please [consult with the Ophan team](Ophan.Dev@guardian.co.uk) if you wish to do this._
87
+
88
+ The backend of tracker exposes two endpoints, `img/1` which takes in page view events, and `img/2` which takes in all other events. For events sent to the `img/2` endpoint, a page view event MUST also be created via the `img/1`. Further down the Ophan and Datatech pipeline, events with the same page view ID are tied together for ease of querying and data analysis. Therefore, a page view event must be present.
89
+
90
+ # Developing Ophan Tracker JS
91
+
92
+ The library is implemented using both Javascript and TypeScript, with the source code located in the `src` directory and its subdirectories. The project is set up to include both TypeScript (.ts) and JavaScript (.js) files, allowing for a mix of both languages in the library's implementation.
93
+
94
+ Typescript interfaces and types are developed in the `types` folder.
95
+
96
+ The TypeScript compiler is configured to generate compiled JavaScript files and corresponding declaration files (.d.ts) in the ./dist directory. The declaration files provide type information for the library's public API. The compiler also generates source map files for the declaration files.
97
+
98
+ ## Compile
99
+
100
+ ```
101
+ .../ophan/tracker-js $ npm run build-for-npm
102
+ ```
12
103
 
13
104
  #### Proxy tracker-js:
14
105
 
@@ -25,8 +116,8 @@ $ npm install ophan-tracker-js
25
116
  .../ophan/tracker-js $ lighttpd -f lighttpd.conf -D
26
117
  ```
27
118
 
28
- 3. Install FoxyProxy (https://getfoxyproxy.org/downloads/) in your browser and create a rule that forces j.ophan.co.uk
29
- to localhost 8000. (For me FoxyProxy needed a few switches on and off before it decided to start talking to lighttpd.)
119
+ 3. Install FoxyProxy (https://getfoxyproxy.org/downloads/) in your browser and create a rule that forces j.ophan.co.uk
120
+ to localhost 8000. (For me FoxyProxy needed a few switches on and off before it decided to start talking to lighttpd.)
30
121
 
31
122
  4. In another terminal window, start a server that is [running the Guardian frontend](https://github.com/guardian/frontend/blob/main/docs/01-start-here/01-installation-steps.md)
32
123
 
@@ -53,7 +144,7 @@ We use `changesets` for automated publishing of the NPM package:
53
144
  ```
54
145
 
55
146
  2. When you raise a PR, the changeset bot won’t correctly see that you have included a changeset – it only checks for the .changesets folder in the root of the repository, whereas we have it in a subdirectory. Don’t worry.
56
-
147
+
57
148
  3. When the PR is merged, the [`changesets` github action](https://github.com/changesets/action) will create a new PR, [example here](https://github.com/guardian/ophan/pull/5626). On merging this generated PR, the action will publish to NPM. (This checks the subdirectory correctly.)
58
149
 
59
150
  ##### S3 Bucket
@@ -62,8 +153,4 @@ We use `changesets` for automated publishing of the NPM package:
62
153
 
63
154
  ##### Rollup
64
155
 
65
- The generated files from Rollup are only used for publishing to the CDN to use SystemJS / AMD
66
-
67
- ##### Example Usage
68
-
69
- You can see example usage of the library on theguardian.com [here](https://github.com/guardian/dotcom-rendering/blob/main/dotcom-rendering/src/client/ophan/ophan.ts)
156
+ The generated files from Rollup (using the `build-for-cdn` script in `package.json`) are only used for publishing to the CDN to use SystemJS / AMD
@@ -1,19 +0,0 @@
1
- import abd from './vendor/adBlockDetectionLib.js';
2
-
3
- export default {
4
- run() {
5
- return new Promise((resolve, reject) => {
6
- if (abd.init) {
7
- try {
8
- abd.init({
9
- complete: resolve,
10
- });
11
- } catch (error) {
12
- reject(error);
13
- }
14
- } else {
15
- reject(new Error('Failed to initialise adblock detection'));
16
- }
17
- });
18
- },
19
- };
@@ -1,160 +0,0 @@
1
- // Generated by CoffeeScript 2.7.0
2
- import transmit from './transmit.js';
3
-
4
- import components from './components.js';
5
-
6
- // The upworthy definition of attention minutes, from
7
- // http://upworthy.github.io/2014/06/implementing-attention-minutes-part-1/ :
8
-
9
- // "We consider the page to have the user’s attention if
10
- // 1. some activity is happening on the page. For our purposes this means:
11
- // 1. the page currently has focus
12
- // 2. and the user has interacted with the page within a certain timeout
13
- // 2. or a video is playing on the page
14
- // Attention Minutes, then, are the total number of minutes that a user has spent on
15
- // a page where the above conditions have held true."
16
-
17
- // our definition of "within a certain timeout" above:
18
- const ATTENTIONDECAY = 5000;
19
-
20
- // our definition of "the user has interacted with the page"
21
- const EVENTS = [
22
- 'focus',
23
- 'click',
24
- 'scroll',
25
- 'mousemove',
26
- 'touchstart',
27
- 'touchend',
28
- 'touchcancel',
29
- 'touchleave',
30
- 'touchmove',
31
- 'keyup',
32
- 'keydown',
33
- ];
34
-
35
- const REPORTINGINTERVAL = 10000;
36
-
37
- // total attention time so far on this page
38
- let totalAttentionMs = 0;
39
-
40
- // the time elapsed since the time origin (https://developer.mozilla.org/en-US/docs/Web/API/DOMHighResTimeStamp#the_time_origin)
41
- // when the most recent period of attention (that is, attention
42
- // which we haven't yet added to totalAttentionMs) began.
43
- // null if we don't have attention.
44
- let unrecordedAttentionStarted = null;
45
-
46
- // the attention time we last reported to ophan
47
- let reportedTotalAttentionMs = null; // setting null here forces a first report of attention time, even if it's only 0ms
48
-
49
- // the decay timer if we've set one
50
- let decayTimerId = null;
51
-
52
- // is video currently playing?
53
- let videoPlaying = false;
54
-
55
- const cancelDecayTimer = () => {
56
- if (decayTimerId != null) {
57
- window.clearTimeout(decayTimerId);
58
- }
59
- decayTimerId = null;
60
- };
61
-
62
- const makeActive = () => {
63
- if (unrecordedAttentionStarted == null) {
64
- unrecordedAttentionStarted = performance.now();
65
- }
66
- components.startMonitoring();
67
- cancelDecayTimer();
68
- decayTimerId = window.setTimeout(() => {
69
- if (!videoPlaying) {
70
- makeInactive();
71
- }
72
- }, ATTENTIONDECAY);
73
- };
74
-
75
- const incrementTotalAttentionTimeByUnrecordedAmount = () => {
76
- if (unrecordedAttentionStarted != null) {
77
- const now = performance.now();
78
- const unrecordedMs = Math.min(
79
- now - unrecordedAttentionStarted,
80
- REPORTINGINTERVAL,
81
- );
82
- totalAttentionMs += unrecordedMs;
83
- unrecordedAttentionStarted = now;
84
- }
85
- };
86
-
87
- const makeInactive = () => {
88
- cancelDecayTimer();
89
- components.stopMonitoring();
90
- incrementTotalAttentionTimeByUnrecordedAmount();
91
- unrecordedAttentionStarted = null;
92
- };
93
-
94
- const reporter = () => {
95
- incrementTotalAttentionTimeByUnrecordedAmount();
96
- if (totalAttentionMs !== reportedTotalAttentionMs) {
97
- const report = {
98
- attentionMs: Math.round(totalAttentionMs),
99
- };
100
- const componentAttentionTimes = components.getAttentionTimes();
101
- if (Object.keys(componentAttentionTimes).length) {
102
- report.componentAttentionMs = componentAttentionTimes;
103
- }
104
- transmit.sendMore(report);
105
- reportedTotalAttentionMs = totalAttentionMs;
106
- }
107
- };
108
-
109
- const initComponent = (name, el, visibilityThreshold = 0.5) => {
110
- components.registerComponent(
111
- name,
112
- el,
113
- visibilityThreshold,
114
- REPORTINGINTERVAL,
115
- );
116
- };
117
-
118
- const initAttention = (visibility) => {
119
- EVENTS.forEach((event) => {
120
- window.addEventListener(event, makeActive);
121
- });
122
-
123
- document.addEventListener(
124
- visibility.changeEvent,
125
- () => {
126
- if (visibility.state() === 'visible') {
127
- makeActive();
128
- } else {
129
- makeInactive();
130
- }
131
- },
132
- false,
133
- );
134
-
135
- document.addEventListener('videoPlaying', () => {
136
- videoPlaying = true;
137
- makeActive();
138
- });
139
- document.addEventListener('videoEnded', () => {
140
- videoPlaying = false;
141
- makeInactive();
142
- });
143
- document.addEventListener('videoPause', () => {
144
- videoPlaying = false;
145
- makeInactive();
146
- });
147
-
148
- window.setTimeout(reporter, 100);
149
- window.setInterval(reporter, REPORTINGINTERVAL);
150
- };
151
-
152
- const setEventEmitter = (emitter) => {
153
- components.setEventEmitter(emitter);
154
- };
155
-
156
- export default {
157
- init: initAttention,
158
- initComponent: initComponent,
159
- setEventEmitter: setEventEmitter,
160
- };
@@ -1,25 +0,0 @@
1
- // Generated by CoffeeScript 2.7.0
2
- import transmit from './transmit.js';
3
- import perf from './perf.js';
4
-
5
- const start = perf?.timing?.domComplete ?? performance.now();
6
-
7
- // DFP ads
8
- if (typeof googletag !== 'undefined' && googletag !== null) {
9
- googletag.cmd.push(() => {
10
- googletag.pubads().addEventListener('slotRenderEnded', (event) => {
11
- const slotId = event.slot.getSlotId().getDomId();
12
- transmit.sendMore({
13
- ads: [
14
- {
15
- slot: slotId,
16
- campaignId: event.isEmpty ? '__empty__' : event.lineItemId,
17
- creativeId: event.creativeId,
18
- timeToRenderEnded: Math.round(performance.now() - start),
19
- adServer: 'DFP',
20
- },
21
- ],
22
- });
23
- });
24
- });
25
- }
@@ -1,80 +0,0 @@
1
- import ophan from './core.js';
2
-
3
- import transmit from './transmit.js';
4
-
5
- /**
6
- * Finds the closest ancestor anchor (`<a>`) element of the given element.
7
- *
8
- * @param {Element} el - The element to start searching from.
9
- * @returns {Element|null} The closest ancestor anchor element or null if none found.
10
- */
11
- export const validAncestorAnchorElement = (el) => {
12
- if (!el || el.nodeName?.toLowerCase() === 'body') return null;
13
- return el.nodeName.toLowerCase() === 'a'
14
- ? el
15
- : validAncestorAnchorElement(el.parentNode);
16
- };
17
-
18
- /**
19
- * Retrieves the value of the `data-component` attribute from the closest ancestor.
20
- *
21
- * @param {Element} el - The element to start searching from.
22
- * @returns {string|null} The `data-component` attribute value or null if not found.
23
- */
24
- export const getContainingComponent = (el) => {
25
- if (!el || el.nodeName?.toLowerCase() === 'body') return null;
26
- return (
27
- el.getAttribute('data-component') || getContainingComponent(el.parentNode)
28
- );
29
- };
30
-
31
- /**
32
- * Collects all `data-link-name` attribute values from the ancestor elements.
33
- *
34
- * @param {Element} el - The element to start searching from.
35
- * @param {string[]} dataLinkNames - Initial array of data link names (optional).
36
- * @returns {string[]} Array of `data-link-name` values.
37
- */
38
- const getDataLinkNames = (el, dataLinkNames = []) => {
39
- if (!el || el === document.body || el === document) return dataLinkNames;
40
-
41
- const dataLinkName = el.getAttribute('data-link-name');
42
- if (dataLinkName) dataLinkNames.push(dataLinkName);
43
-
44
- return getDataLinkNames(el.parentNode, dataLinkNames);
45
- };
46
-
47
- if (typeof document.addEventListener === 'function') {
48
- document.addEventListener(
49
- 'click',
50
- function (e) {
51
- let anchorTarget, info;
52
- anchorTarget = validAncestorAnchorElement(e.target);
53
- info = {
54
- from: [location.protocol, '//', location.host, location.pathname].join(
55
- '',
56
- ),
57
- to: anchorTarget ? anchorTarget.href : void 0,
58
- referringComponent: getContainingComponent(e.target),
59
- referringDataLinkNames: getDataLinkNames(e.target),
60
- refPlatform: ophan.servingPlatform(),
61
- refViewId: ophan.viewId,
62
- };
63
- if (info.referringDataLinkNames != null) {
64
- transmit.sendMore({
65
- clickComponent: info.referringComponent,
66
- clickLinkNames: info.referringDataLinkNames,
67
- });
68
- }
69
- if (anchorTarget != null) {
70
- // then we may be about to navigate away from this page, so store to send on next request
71
- return ophan.storeDataToSendOnNextEvent(info);
72
- }
73
- },
74
- false,
75
- );
76
- }
77
-
78
- export default {
79
- getDataLinkNames: getDataLinkNames,
80
- };
@@ -1,199 +0,0 @@
1
- /**
2
- * @type {Component[]}
3
- */
4
- export const components = [];
5
-
6
- /**
7
- * theguardian.com uses a custom scroll event for performance reasons.
8
- * We pass in the event emitter so that we can listen to this custom scroll event.
9
- * If not on theguardian.com, we default to the native scroll event
10
- * @type {Object|null}
11
- */
12
- let eventEmitter = null;
13
-
14
- // Tracks attention time of elements on the page with data-component attributes.
15
- // This should always be less than or equal to the page attention time.
16
- // If the page does not have attention, no component has attention.
17
- // If the page does have attention, a given component *may* also have
18
- // attention, if it is within the viewport and not hidden.
19
- class Component {
20
- /**
21
- * Component constructor.
22
- * @param {string} name - The name of the component.
23
- * @param {HTMLElement} element - The DOM element of the component.
24
- * @param {number} visibilityThreshold1 -
25
- * visibilityThreshold represents the fraction of each component that must
26
- * be in the viewport before it is considered visible.
27
- * e.g. 0.5 means that half the height or width must be in the viewport
28
- * 1 means that the entire element must be in the viewport
29
- * @param {number} reportingInterval1 - ??
30
- */
31
- constructor(name, element, visibilityThreshold1, reportingInterval1) {
32
- this.name = name;
33
- this.element = element;
34
- this.visibilityThreshold = visibilityThreshold1;
35
- this.reportingInterval = reportingInterval1;
36
- this.visible = false;
37
- // total attention time so far for this element
38
- this.totalAttentionMs = 0;
39
- // the time elapsed since the time origin (https://developer.mozilla.org/en-US/docs/Web/API/DOMHighResTimeStamp#the_time_origin)
40
- // when the most recent period of attention (that is, attention
41
- // which we haven't yet added to totalAttentionMs) began.
42
- // null if we don't have attention.
43
- this.unrecordedAttentionStarted = null;
44
- // the attention time when getAttentionTime() was last called
45
- this.reportedTotalAttentionMs = 0;
46
- this.usingEmitter = false;
47
- this.visCheck = this.checkVisibility.bind(this);
48
- if (eventEmitter != null) {
49
- this.usingEmitter = true;
50
- eventEmitter.on('window:throttledScroll', this.visCheck);
51
- } else {
52
- window.addEventListener('scroll', this.visCheck);
53
- }
54
- window.addEventListener('resize', this.visCheck);
55
- }
56
-
57
- isVisible(threshold = 1) {
58
- if (!this.element.offsetParent) {
59
- // exclude hidden elements
60
- // will also exclude position: fixed elements but we don't care about these
61
- // (because their attention time would always be the same as the whole page)
62
- // https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/offsetParent
63
- return false;
64
- }
65
-
66
- const box = this.element.getBoundingClientRect();
67
- const width = box.width;
68
- const height = box.height;
69
- const windowHeight =
70
- window.innerHeight || document.documentElement.clientHeight;
71
- const windowWidth =
72
- window.innerWidth || document.documentElement.clientWidth;
73
-
74
- return (
75
- box.left >= -(width * (1 - threshold)) &&
76
- box.top >= -(height * (1 - threshold)) &&
77
- box.right <= windowWidth + width * (1 - threshold) &&
78
- box.bottom <= windowHeight + height * (1 - threshold)
79
- );
80
- }
81
-
82
- visibilityHasChanged() {
83
- const wasVisible = this.visible;
84
- this.visible = this.isVisible(this.visibilityThreshold);
85
- return wasVisible !== this.visible;
86
- }
87
-
88
- rebindToEventEmitter() {
89
- if (!this.usingEmitter && eventEmitter != null) {
90
- window.removeEventListener('scroll', this.visCheck);
91
- this.usingEmitter = true;
92
- return eventEmitter.on('window:throttledScroll', this.visCheck);
93
- }
94
- }
95
-
96
- checkVisibility() {
97
- this.rebindToEventEmitter();
98
- if (this.visibilityHasChanged()) {
99
- return this.visible ? this.makeActive() : this.makeInactive();
100
- }
101
- }
102
-
103
- makeActive() {
104
- // mark when the latest period of attention began,
105
- // if we're not already in an attention period
106
- return this.unrecordedAttentionStarted != null
107
- ? this.unrecordedAttentionStarted
108
- : (this.unrecordedAttentionStarted = performance.now());
109
- }
110
-
111
- makeInactive() {
112
- this.incrementTotalAttentionTimeByUnrecordedAmount();
113
- return (this.unrecordedAttentionStarted = null);
114
- }
115
-
116
- hadAttentionSinceLastGet() {
117
- this.incrementTotalAttentionTimeByUnrecordedAmount();
118
- return this.totalAttentionMs !== this.reportedTotalAttentionMs;
119
- }
120
-
121
- getAttentionTime() {
122
- this.incrementTotalAttentionTimeByUnrecordedAmount();
123
- this.reportedTotalAttentionMs = this.totalAttentionMs;
124
- return this.totalAttentionMs;
125
- }
126
-
127
- incrementTotalAttentionTimeByUnrecordedAmount() {
128
- if (this.unrecordedAttentionStarted != null) {
129
- const now = performance.now();
130
- // the Math.min here is to deal with occasions where we don't get an event indicating that the user
131
- // has become inactive - this can happen e.g. due to users suspending their phone. Never send
132
- // crazy big values!
133
- const unrecordedMs = Math.min(
134
- now - this.unrecordedAttentionStarted,
135
- this.reportingInterval,
136
- );
137
- this.totalAttentionMs += unrecordedMs;
138
- return (this.unrecordedAttentionStarted = now);
139
- }
140
- }
141
- }
142
-
143
- /**
144
- * Begins monitoring the components on the page for attention time.
145
- * @returns {Array} Array of results from makeActive() for each visible component.
146
- */
147
- const startMonitoring = () => {
148
- return components
149
- .filter((component) => component.isVisible())
150
- .map((component) => component.makeActive());
151
- };
152
-
153
- /**
154
- * Stops monitoring the components on the page for attention time.
155
- * @returns {Array} Array of results from makeInactive() for each component.
156
- */
157
- const stopMonitoring = () => {
158
- return components.map((component) => component.makeInactive());
159
- };
160
-
161
- /**
162
- * Retrieves the attention times for all components.
163
- * @returns {Object}
164
- */
165
- const getAttentionTimes = () => {
166
- const obj = {};
167
- for (const component of components) {
168
- if (component.hadAttentionSinceLastGet()) {
169
- // if there are duplicate component names, only the last one survives
170
- obj[component.name] = Math.round(component.getAttentionTime());
171
- }
172
- }
173
- return obj;
174
- };
175
-
176
- export default {
177
- /**
178
- * @param {Object} emitter - The event emitter to set.
179
- * @returns {Object}
180
- */
181
- setEventEmitter: function (emitter) {
182
- return (eventEmitter = emitter);
183
- },
184
- startMonitoring,
185
- stopMonitoring,
186
- getAttentionTimes,
187
-
188
- /** @type {(...args: ConstructorParameters<typeof Component>) => void} */
189
- registerComponent: function (
190
- name,
191
- el,
192
- visibilityThreshold,
193
- reportingInterval,
194
- ) {
195
- return components.push(
196
- new Component(name, el, visibilityThreshold, reportingInterval),
197
- );
198
- },
199
- };
@@ -1,25 +0,0 @@
1
- // Generated by CoffeeScript 2.7.0
2
- import core from './core.js';
3
-
4
- import transmit from './transmit.js';
5
-
6
- import attention from './attention.js';
7
-
8
- import visibility from './visibility.js';
9
-
10
- import './click-path-capture.js';
11
-
12
- import './perf.js';
13
-
14
- import './campaign.js';
15
-
16
- core.init('contribution');
17
-
18
- if (window.addEventListener != null) {
19
- attention.init(visibility);
20
- }
21
-
22
- export default {
23
- record: transmit.sendMore,
24
- viewId: transmit.viewId,
25
- };