@valbuild/react 0.148.2 → 0.148.4
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/CHANGELOG.md +24 -0
- package/analytics/dist/valbuild-react-analytics.browser.esm.js +950 -0
- package/analytics/dist/valbuild-react-analytics.cjs.d.ts +2 -0
- package/analytics/dist/valbuild-react-analytics.cjs.dev.js +962 -0
- package/analytics/dist/valbuild-react-analytics.cjs.js +7 -0
- package/analytics/dist/valbuild-react-analytics.cjs.prod.js +962 -0
- package/analytics/dist/valbuild-react-analytics.esm.js +953 -0
- package/analytics/dist/valbuild-react-analytics.worker.esm.js +232 -0
- package/analytics/package.json +7 -0
- package/dist/createForOfIteratorHelper-69c4a419.esm.js +52 -0
- package/dist/createForOfIteratorHelper-6f9dc6fa.cjs.prod.js +54 -0
- package/dist/createForOfIteratorHelper-73fe9a1a.worker.esm.js +52 -0
- package/dist/createForOfIteratorHelper-8a871dc3.cjs.dev.js +54 -0
- package/dist/createForOfIteratorHelper-c0be3691.browser.esm.js +52 -0
- package/dist/declarations/src/analytics/beacon.d.ts +80 -0
- package/dist/declarations/src/analytics/index.d.ts +3 -0
- package/dist/declarations/src/analytics/splitValPaths.d.ts +12 -0
- package/dist/declarations/src/analytics/tracker.d.ts +64 -0
- package/dist/{raw-eda286dd.cjs.dev.js → raw-c2b82a01.cjs.dev.js} +23 -71
- package/dist/{raw-7aae8bf6.esm.js → raw-cd78aea7.browser.esm.js} +4 -51
- package/dist/{raw-00151a05.worker.esm.js → raw-d156d004.worker.esm.js} +4 -51
- package/dist/{raw-9bb55a5f.browser.esm.js → raw-d43ba7b8.esm.js} +4 -51
- package/dist/{raw-f3f7e399.cjs.prod.js → raw-de29c973.cjs.prod.js} +23 -71
- package/dist/{slicedToArray-e9e8e991.cjs.dev.js → slicedToArray-387314c6.cjs.dev.js} +0 -11
- package/dist/{slicedToArray-0c0a3b4a.esm.js → slicedToArray-3d4c6640.esm.js} +1 -11
- package/dist/{slicedToArray-86004d59.cjs.prod.js → slicedToArray-64c83d56.cjs.prod.js} +0 -11
- package/dist/{slicedToArray-7d0170b6.worker.esm.js → slicedToArray-8b1a55e5.worker.esm.js} +1 -11
- package/dist/{slicedToArray-9cce0d9f.browser.esm.js → slicedToArray-8f9b0d84.browser.esm.js} +1 -11
- package/dist/toConsumableArray-203e636a.cjs.prod.js +21 -0
- package/dist/toConsumableArray-5335b4b0.worker.esm.js +19 -0
- package/dist/toConsumableArray-991f680f.browser.esm.js +19 -0
- package/dist/toConsumableArray-c950e394.esm.js +19 -0
- package/dist/toConsumableArray-d7cf1601.cjs.dev.js +21 -0
- package/dist/typeof-16428c61.cjs.prod.js +13 -0
- package/dist/typeof-9915e135.browser.esm.js +11 -0
- package/dist/typeof-a1531d8f.esm.js +11 -0
- package/dist/typeof-b568f48f.cjs.dev.js +13 -0
- package/dist/typeof-e0c837cc.worker.esm.js +11 -0
- package/internal/dist/valbuild-react-internal.browser.esm.js +5 -18
- package/internal/dist/valbuild-react-internal.cjs.dev.js +6 -19
- package/internal/dist/valbuild-react-internal.cjs.prod.js +6 -19
- package/internal/dist/valbuild-react-internal.esm.js +5 -18
- package/internal/dist/valbuild-react-internal.worker.esm.js +5 -18
- package/jsx-dev-runtime/dist/valbuild-react-jsx-dev-runtime.browser.esm.js +2 -1
- package/jsx-dev-runtime/dist/valbuild-react-jsx-dev-runtime.cjs.dev.js +3 -2
- package/jsx-dev-runtime/dist/valbuild-react-jsx-dev-runtime.cjs.prod.js +3 -2
- package/jsx-dev-runtime/dist/valbuild-react-jsx-dev-runtime.esm.js +2 -1
- package/jsx-dev-runtime/dist/valbuild-react-jsx-dev-runtime.worker.esm.js +2 -1
- package/jsx-runtime/dist/valbuild-react-jsx-runtime.browser.esm.js +2 -1
- package/jsx-runtime/dist/valbuild-react-jsx-runtime.cjs.dev.js +3 -2
- package/jsx-runtime/dist/valbuild-react-jsx-runtime.cjs.prod.js +3 -2
- package/jsx-runtime/dist/valbuild-react-jsx-runtime.esm.js +2 -1
- package/jsx-runtime/dist/valbuild-react-jsx-runtime.worker.esm.js +2 -1
- package/package.json +16 -6
- package/stega/dist/valbuild-react-stega.browser.esm.js +5 -3
- package/stega/dist/valbuild-react-stega.cjs.dev.js +8 -6
- package/stega/dist/valbuild-react-stega.cjs.prod.js +8 -6
- package/stega/dist/valbuild-react-stega.esm.js +5 -3
- package/stega/dist/valbuild-react-stega.worker.esm.js +5 -3
|
@@ -0,0 +1,950 @@
|
|
|
1
|
+
import { _ as _slicedToArray } from '../../dist/slicedToArray-8f9b0d84.browser.esm.js';
|
|
2
|
+
import { _ as _toConsumableArray } from '../../dist/toConsumableArray-991f680f.browser.esm.js';
|
|
3
|
+
import { _ as _createForOfIteratorHelper } from '../../dist/createForOfIteratorHelper-c0be3691.browser.esm.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* The analytics wire format, version 1: what the browser sends to
|
|
7
|
+
* `POST /api/val/analytics`. See `docs/plans/analytics.md`, "Wire format".
|
|
8
|
+
*
|
|
9
|
+
* Nothing in this directory may import `@valbuild/core`, `@valbuild/shared`
|
|
10
|
+
* or zod: this is code every visitor of a site downloads, and those three are
|
|
11
|
+
* most of the weight Val already puts on a page (`client-bundle-weight.md`).
|
|
12
|
+
* `imports.test.ts` keeps it so. The zod schema the server parses this with
|
|
13
|
+
* is `AnalyticsBeacon` in `@valbuild/shared/internal`, and `beacon.test.ts`
|
|
14
|
+
* checks the two agree.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
/** What only the first beacon of a page view carries. Its presence IS the page view. */
|
|
18
|
+
|
|
19
|
+
/** Module file path → module paths (the part of a source path after `?p=`). */
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* The receiver rejects a beacon above these (`AnalyticsBeacon` in
|
|
23
|
+
* `@valbuild/shared/internal` repeats them), so the sender never sends more:
|
|
24
|
+
* it cuts or drops what is longer, and splits what is larger.
|
|
25
|
+
*/
|
|
26
|
+
var MAX_PATHS_PER_BEACON = 200;
|
|
27
|
+
var MAX_ROUTE_LENGTH = 512;
|
|
28
|
+
var MAX_UTM_LENGTH = 100;
|
|
29
|
+
/** A host name is at most 253 characters (RFC 1035). */
|
|
30
|
+
var MAX_REF_LENGTH = 253;
|
|
31
|
+
var MAX_CV_LENGTH = 128;
|
|
32
|
+
var MAX_MODULE_FILE_PATH_LENGTH = 512;
|
|
33
|
+
var MAX_MODULE_PATH_LENGTH = 1024;
|
|
34
|
+
/**
|
|
35
|
+
* A beacon's size in bytes of JSON, below the 16 KB Val's hosting accepts --
|
|
36
|
+
* 200 long router keys are about 18 KB. `sendBeacon` itself takes 64 KB.
|
|
37
|
+
*/
|
|
38
|
+
var MAX_BEACON_BYTES = 12000;
|
|
39
|
+
var MODULE_FILE_PATH_SEP = "?p=";
|
|
40
|
+
|
|
41
|
+
/** Phone below 640 px of viewport width, tablet below 1024, desktop above. */
|
|
42
|
+
function deviceOf(viewportWidth) {
|
|
43
|
+
if (viewportWidth < 640) {
|
|
44
|
+
return "phone";
|
|
45
|
+
}
|
|
46
|
+
if (viewportWidth < 1024) {
|
|
47
|
+
return "tablet";
|
|
48
|
+
}
|
|
49
|
+
return "desktop";
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* The referrer's host, when it is another site -- never its path or query,
|
|
54
|
+
* which can carry anything. A referrer from this site is navigation within it,
|
|
55
|
+
* not a source.
|
|
56
|
+
*/
|
|
57
|
+
function referrerHostOf(referrer, ownHost) {
|
|
58
|
+
if (!referrer) {
|
|
59
|
+
return undefined;
|
|
60
|
+
}
|
|
61
|
+
var host;
|
|
62
|
+
try {
|
|
63
|
+
host = new URL(referrer).host;
|
|
64
|
+
} catch (_unused) {
|
|
65
|
+
return undefined;
|
|
66
|
+
}
|
|
67
|
+
if (!host || host === ownHost) {
|
|
68
|
+
return undefined;
|
|
69
|
+
}
|
|
70
|
+
return host.slice(0, MAX_REF_LENGTH);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** `utm_source`, `utm_medium` and `utm_campaign`, and no other parameter. */
|
|
74
|
+
function utmOf(search) {
|
|
75
|
+
var params = new URLSearchParams(search);
|
|
76
|
+
var utm = {};
|
|
77
|
+
var _iterator = _createForOfIteratorHelper(["source", "medium", "campaign"]),
|
|
78
|
+
_step;
|
|
79
|
+
try {
|
|
80
|
+
for (_iterator.s(); !(_step = _iterator.n()).done;) {
|
|
81
|
+
var key = _step.value;
|
|
82
|
+
var value = params.get("utm_".concat(key));
|
|
83
|
+
if (value) {
|
|
84
|
+
utm[key] = value.slice(0, MAX_UTM_LENGTH);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
} catch (err) {
|
|
88
|
+
_iterator.e(err);
|
|
89
|
+
} finally {
|
|
90
|
+
_iterator.f();
|
|
91
|
+
}
|
|
92
|
+
return Object.keys(utm).length > 0 ? utm : undefined;
|
|
93
|
+
}
|
|
94
|
+
function routeOf(pathname) {
|
|
95
|
+
return pathname.slice(0, MAX_ROUTE_LENGTH);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/** Splits a source path into its module file path and its module path. */
|
|
99
|
+
function splitSourcePath(sourcePath) {
|
|
100
|
+
var at = sourcePath.indexOf(MODULE_FILE_PATH_SEP);
|
|
101
|
+
if (at === -1) {
|
|
102
|
+
return [sourcePath, ""];
|
|
103
|
+
}
|
|
104
|
+
return [sourcePath.slice(0, at), sourcePath.slice(at + MODULE_FILE_PATH_SEP.length)];
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* The beacons that carry `rendered` and `viewed`: at most
|
|
109
|
+
* `MAX_PATHS_PER_BEACON` paths and about `MAX_BEACON_BYTES` each. `view`, when
|
|
110
|
+
* given, goes in the first. Returns none when there is nothing to say.
|
|
111
|
+
*
|
|
112
|
+
* A path longer than the receiver takes is dropped rather than cut: half a
|
|
113
|
+
* path names some other content, or none.
|
|
114
|
+
*/
|
|
115
|
+
function buildBeacons(args) {
|
|
116
|
+
var _args$cv;
|
|
117
|
+
var counted = function counted(kind, sourcePath) {
|
|
118
|
+
var _splitSourcePath = splitSourcePath(sourcePath),
|
|
119
|
+
_splitSourcePath2 = _slicedToArray(_splitSourcePath, 2),
|
|
120
|
+
moduleFilePath = _splitSourcePath2[0],
|
|
121
|
+
modulePath = _splitSourcePath2[1];
|
|
122
|
+
if (moduleFilePath.length > MAX_MODULE_FILE_PATH_LENGTH || modulePath.length > MAX_MODULE_PATH_LENGTH) {
|
|
123
|
+
return [];
|
|
124
|
+
}
|
|
125
|
+
return [{
|
|
126
|
+
kind: kind,
|
|
127
|
+
moduleFilePath: moduleFilePath,
|
|
128
|
+
modulePath: modulePath
|
|
129
|
+
}];
|
|
130
|
+
};
|
|
131
|
+
var all = [].concat(_toConsumableArray(args.rendered.flatMap(function (path) {
|
|
132
|
+
return counted("rendered", path);
|
|
133
|
+
})), _toConsumableArray(args.viewed.flatMap(function (path) {
|
|
134
|
+
return counted("viewed", path);
|
|
135
|
+
})));
|
|
136
|
+
if (all.length === 0 && !args.view) {
|
|
137
|
+
return [];
|
|
138
|
+
}
|
|
139
|
+
var cv = (_args$cv = args.cv) === null || _args$cv === void 0 ? void 0 : _args$cv.slice(0, MAX_CV_LENGTH);
|
|
140
|
+
var empty = function empty(withView) {
|
|
141
|
+
var beacon = {
|
|
142
|
+
v: 1,
|
|
143
|
+
route: args.route,
|
|
144
|
+
rendered: {},
|
|
145
|
+
viewed: {}
|
|
146
|
+
};
|
|
147
|
+
if (cv) {
|
|
148
|
+
beacon.cv = cv;
|
|
149
|
+
}
|
|
150
|
+
if (withView && args.view) {
|
|
151
|
+
beacon.view = args.view;
|
|
152
|
+
}
|
|
153
|
+
return beacon;
|
|
154
|
+
};
|
|
155
|
+
var beacons = [];
|
|
156
|
+
var beacon = empty(true);
|
|
157
|
+
var paths = 0;
|
|
158
|
+
var bytes = byteLength(JSON.stringify(beacon));
|
|
159
|
+
// What adding a path costs: the path and a comma, and a new key when its
|
|
160
|
+
// module is not in this beacon yet. An estimate in UTF-8 bytes, close
|
|
161
|
+
// enough to keep well under the limit.
|
|
162
|
+
var costOf = function costOf(path) {
|
|
163
|
+
return byteLength(JSON.stringify(path.modulePath)) + 1 + (beacon[path.kind][path.moduleFilePath] ? 0 : byteLength(JSON.stringify(path.moduleFilePath)) + 4);
|
|
164
|
+
};
|
|
165
|
+
var _iterator2 = _createForOfIteratorHelper(all),
|
|
166
|
+
_step2;
|
|
167
|
+
try {
|
|
168
|
+
for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
|
|
169
|
+
var _beacon$path$kind, _path$moduleFilePath, _beacon$path$kind$_pa;
|
|
170
|
+
var path = _step2.value;
|
|
171
|
+
var cost = costOf(path);
|
|
172
|
+
if (paths > 0 && (paths === MAX_PATHS_PER_BEACON || bytes + cost > MAX_BEACON_BYTES)) {
|
|
173
|
+
beacons.push(beacon);
|
|
174
|
+
beacon = empty(false);
|
|
175
|
+
paths = 0;
|
|
176
|
+
bytes = byteLength(JSON.stringify(beacon));
|
|
177
|
+
cost = costOf(path);
|
|
178
|
+
}
|
|
179
|
+
((_beacon$path$kind$_pa = (_beacon$path$kind = beacon[path.kind])[_path$moduleFilePath = path.moduleFilePath]) !== null && _beacon$path$kind$_pa !== void 0 ? _beacon$path$kind$_pa : _beacon$path$kind[_path$moduleFilePath] = []).push(path.modulePath);
|
|
180
|
+
paths += 1;
|
|
181
|
+
bytes += cost;
|
|
182
|
+
}
|
|
183
|
+
} catch (err) {
|
|
184
|
+
_iterator2.e(err);
|
|
185
|
+
} finally {
|
|
186
|
+
_iterator2.f();
|
|
187
|
+
}
|
|
188
|
+
beacons.push(beacon);
|
|
189
|
+
return beacons;
|
|
190
|
+
}
|
|
191
|
+
function byteLength(text) {
|
|
192
|
+
return new TextEncoder().encode(text).length;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* The source paths in a `data-val-path` attribute.
|
|
197
|
+
*
|
|
198
|
+
* One element can show several pieces of content, and `attrs()` and
|
|
199
|
+
* `autoTagJSX` join their paths with commas. Core's `splitJoinedSourcePaths`
|
|
200
|
+
* splits on every comma, which cuts a path whose record key holds one
|
|
201
|
+
* (`"/blog/a,b"`). This splits only on commas outside the JSON strings a
|
|
202
|
+
* module path is made of, so it agrees with core wherever core is right and
|
|
203
|
+
* keeps such a path whole. It is here rather than imported because nothing in
|
|
204
|
+
* this directory may import core -- see `beacon.ts`.
|
|
205
|
+
*/
|
|
206
|
+
function splitValPaths(attribute) {
|
|
207
|
+
if (!attribute) {
|
|
208
|
+
return [];
|
|
209
|
+
}
|
|
210
|
+
var paths = [];
|
|
211
|
+
var current = "";
|
|
212
|
+
var inString = false;
|
|
213
|
+
var escaped = false;
|
|
214
|
+
var _iterator = _createForOfIteratorHelper(attribute),
|
|
215
|
+
_step;
|
|
216
|
+
try {
|
|
217
|
+
for (_iterator.s(); !(_step = _iterator.n()).done;) {
|
|
218
|
+
var _char = _step.value;
|
|
219
|
+
if (inString) {
|
|
220
|
+
current += _char;
|
|
221
|
+
if (escaped) {
|
|
222
|
+
escaped = false;
|
|
223
|
+
} else if (_char === "\\") {
|
|
224
|
+
escaped = true;
|
|
225
|
+
} else if (_char === '"') {
|
|
226
|
+
inString = false;
|
|
227
|
+
}
|
|
228
|
+
continue;
|
|
229
|
+
}
|
|
230
|
+
if (_char === '"') {
|
|
231
|
+
inString = true;
|
|
232
|
+
current += _char;
|
|
233
|
+
continue;
|
|
234
|
+
}
|
|
235
|
+
if (_char === ",") {
|
|
236
|
+
if (current) {
|
|
237
|
+
paths.push(current);
|
|
238
|
+
}
|
|
239
|
+
current = "";
|
|
240
|
+
continue;
|
|
241
|
+
}
|
|
242
|
+
current += _char;
|
|
243
|
+
}
|
|
244
|
+
} catch (err) {
|
|
245
|
+
_iterator.e(err);
|
|
246
|
+
} finally {
|
|
247
|
+
_iterator.f();
|
|
248
|
+
}
|
|
249
|
+
if (current) {
|
|
250
|
+
paths.push(current);
|
|
251
|
+
}
|
|
252
|
+
return paths;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
var SELECTOR = "[data-val-path]";
|
|
256
|
+
var ATTRIBUTE = "data-val-path";
|
|
257
|
+
/** Every 5%, so an element far taller than the screen still crosses one. */
|
|
258
|
+
var THRESHOLDS = Array.from({
|
|
259
|
+
length: 21
|
|
260
|
+
}, function (_, i) {
|
|
261
|
+
return i / 20;
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Counts which content a visitor's page shows, and sends it.
|
|
266
|
+
*
|
|
267
|
+
* - **Rendered**: an element with a `data-val-path` is in the document.
|
|
268
|
+
* - **Viewed**: one is at least half visible -- or covers at least half the
|
|
269
|
+
* viewport, for an element taller than it -- for `viewedAfterMs` without a
|
|
270
|
+
* break, while the page is visible.
|
|
271
|
+
*
|
|
272
|
+
* Each path counts at most once of each per page view. A page view starts with
|
|
273
|
+
* the page and with every client-side navigation to another pathname; content
|
|
274
|
+
* still on screen after one (a header, a footer) counts again for the new one,
|
|
275
|
+
* because it was rendered on it.
|
|
276
|
+
*
|
|
277
|
+
* Returns null where it does nothing: on the server, and for an automated
|
|
278
|
+
* browser (`navigator.webdriver`). The caller decides the rest -- draft mode,
|
|
279
|
+
* the Studio's canvas and an editor browsing with Val enabled all count
|
|
280
|
+
* nothing, and the provider does not start it for them.
|
|
281
|
+
*
|
|
282
|
+
* A page the browser PRERENDERS (speculation rules, the address bar's guess
|
|
283
|
+
* at where the visitor is going) is hydrated with nobody looking, and may
|
|
284
|
+
* never be looked at: nothing starts until it is shown, and nothing at all
|
|
285
|
+
* if it never is.
|
|
286
|
+
*/
|
|
287
|
+
function startValAnalytics() {
|
|
288
|
+
var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
|
289
|
+
if (navigator.webdriver) {
|
|
290
|
+
return null;
|
|
291
|
+
}
|
|
292
|
+
if (!isPrerendering()) {
|
|
293
|
+
return startNow(options);
|
|
294
|
+
}
|
|
295
|
+
var tracker = null;
|
|
296
|
+
var stopped = false;
|
|
297
|
+
var onShown = function onShown() {
|
|
298
|
+
if (!stopped) {
|
|
299
|
+
tracker = startNow(options);
|
|
300
|
+
}
|
|
301
|
+
};
|
|
302
|
+
document.addEventListener("prerenderingchange", onShown, {
|
|
303
|
+
once: true
|
|
304
|
+
});
|
|
305
|
+
return {
|
|
306
|
+
flush: function flush() {
|
|
307
|
+
var _tracker;
|
|
308
|
+
return (_tracker = tracker) === null || _tracker === void 0 ? void 0 : _tracker.flush();
|
|
309
|
+
},
|
|
310
|
+
stop: function stop() {
|
|
311
|
+
var _tracker2;
|
|
312
|
+
stopped = true;
|
|
313
|
+
document.removeEventListener("prerenderingchange", onShown);
|
|
314
|
+
(_tracker2 = tracker) === null || _tracker2 === void 0 || _tracker2.stop();
|
|
315
|
+
}
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/** `document.prerendering`, which TypeScript's DOM types do not have yet. */
|
|
320
|
+
function isPrerendering() {
|
|
321
|
+
return "prerendering" in document && document.prerendering === true;
|
|
322
|
+
}
|
|
323
|
+
function startNow(options) {
|
|
324
|
+
var _options$endpoint, _options$firstSendAft, _options$viewedAfterM, _options$sendWhenPend, _options$minSendInter, _options$landingPath;
|
|
325
|
+
var routeFor = function routeFor(pathname) {
|
|
326
|
+
return routeOf(options.routeFor ? options.routeFor(pathname) : pathname);
|
|
327
|
+
};
|
|
328
|
+
var endpoint = (_options$endpoint = options.endpoint) !== null && _options$endpoint !== void 0 ? _options$endpoint : "/api/val/analytics";
|
|
329
|
+
var firstSendAfterMs = (_options$firstSendAft = options.firstSendAfterMs) !== null && _options$firstSendAft !== void 0 ? _options$firstSendAft : 3000;
|
|
330
|
+
var viewedAfterMs = (_options$viewedAfterM = options.viewedAfterMs) !== null && _options$viewedAfterM !== void 0 ? _options$viewedAfterM : 1000;
|
|
331
|
+
var sendWhenPending = (_options$sendWhenPend = options.sendWhenPending) !== null && _options$sendWhenPend !== void 0 ? _options$sendWhenPend : 50;
|
|
332
|
+
var minSendIntervalMs = (_options$minSendInter = options.minSendIntervalMs) !== null && _options$minSendInter !== void 0 ? _options$minSendInter : 15000;
|
|
333
|
+
var stopped = false;
|
|
334
|
+
/**
|
|
335
|
+
* Whether a beacon has been sent with `fetch`, whose answer can be read --
|
|
336
|
+
* one is, the first that is not sent while leaving, so a site with
|
|
337
|
+
* analytics off or paused (410) stops sending.
|
|
338
|
+
*/
|
|
339
|
+
var asked = false;
|
|
340
|
+
|
|
341
|
+
// The page view.
|
|
342
|
+
/** The page view's pathname, which a new page view is a change of. */
|
|
343
|
+
var pathname = location.pathname;
|
|
344
|
+
var route = routeFor(pathname);
|
|
345
|
+
var view = null;
|
|
346
|
+
var viewSent = false;
|
|
347
|
+
var rendered = new Set();
|
|
348
|
+
var viewed = new Set();
|
|
349
|
+
var pendingRendered = [];
|
|
350
|
+
var pendingViewed = [];
|
|
351
|
+
var lastSentAt = 0;
|
|
352
|
+
var firstSendTimer = null;
|
|
353
|
+
var batchTimer = null;
|
|
354
|
+
var viewTimers = new Map();
|
|
355
|
+
function pageView(first, url) {
|
|
356
|
+
var pageView = {
|
|
357
|
+
device: deviceOf(innerWidth)
|
|
358
|
+
};
|
|
359
|
+
// A referrer is where the LOAD came from. A client-side navigation's
|
|
360
|
+
// `document.referrer` is still that, so it is not a source again.
|
|
361
|
+
var ref = first ? referrerHostOf(document.referrer, location.host) : undefined;
|
|
362
|
+
if (ref) {
|
|
363
|
+
pageView.ref = ref;
|
|
364
|
+
}
|
|
365
|
+
var utm = utmOf(url.search);
|
|
366
|
+
if (utm) {
|
|
367
|
+
pageView.utm = utm;
|
|
368
|
+
}
|
|
369
|
+
return pageView;
|
|
370
|
+
}
|
|
371
|
+
function send(beacon, leaving) {
|
|
372
|
+
var body = JSON.stringify(beacon);
|
|
373
|
+
if (!asked && !leaving) {
|
|
374
|
+
asked = true;
|
|
375
|
+
// `sendBeacon` cannot read the answer, so this one asks. Not while
|
|
376
|
+
// leaving, where only `sendBeacon` is sure to get out -- the next one
|
|
377
|
+
// asks instead.
|
|
378
|
+
fetch(endpoint, {
|
|
379
|
+
method: "POST",
|
|
380
|
+
body: body,
|
|
381
|
+
keepalive: true,
|
|
382
|
+
headers: {
|
|
383
|
+
"Content-Type": "text/plain;charset=UTF-8"
|
|
384
|
+
}
|
|
385
|
+
}).then(function (res) {
|
|
386
|
+
if (res.status === 410) {
|
|
387
|
+
stop(false);
|
|
388
|
+
}
|
|
389
|
+
})["catch"](function () {
|
|
390
|
+
// A lost beacon is a rounding error, not something to retry.
|
|
391
|
+
});
|
|
392
|
+
return;
|
|
393
|
+
}
|
|
394
|
+
if (typeof navigator.sendBeacon === "function") {
|
|
395
|
+
// A string body is sent as text/plain, which needs no CORS preflight.
|
|
396
|
+
if (navigator.sendBeacon(endpoint, body)) {
|
|
397
|
+
return;
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
fetch(endpoint, {
|
|
401
|
+
method: "POST",
|
|
402
|
+
body: body,
|
|
403
|
+
keepalive: true,
|
|
404
|
+
headers: {
|
|
405
|
+
"Content-Type": "text/plain;charset=UTF-8"
|
|
406
|
+
}
|
|
407
|
+
})["catch"](function () {
|
|
408
|
+
// As above.
|
|
409
|
+
});
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
/**
|
|
413
|
+
* `confirm`: what was carried over from the previous page and is still
|
|
414
|
+
* here is the new page's. True when a page view's first beacon is due --
|
|
415
|
+
* that is as long as a navigation's old page gets. False when the page view
|
|
416
|
+
* ends first -- leaving, or navigating on again before the new page has
|
|
417
|
+
* changed the screen -- so it is not counted at all, which is better than
|
|
418
|
+
* counted on the wrong route.
|
|
419
|
+
*/
|
|
420
|
+
function flushNow(leaving) {
|
|
421
|
+
var confirm = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : !leaving;
|
|
422
|
+
if (confirm) {
|
|
423
|
+
confirmCarried();
|
|
424
|
+
}
|
|
425
|
+
if (firstSendTimer !== null) {
|
|
426
|
+
clearTimeout(firstSendTimer);
|
|
427
|
+
firstSendTimer = null;
|
|
428
|
+
}
|
|
429
|
+
if (batchTimer !== null) {
|
|
430
|
+
clearTimeout(batchTimer);
|
|
431
|
+
batchTimer = null;
|
|
432
|
+
}
|
|
433
|
+
var beacons = buildBeacons({
|
|
434
|
+
route: route,
|
|
435
|
+
cv: options.contentVersion,
|
|
436
|
+
view: !viewSent && view ? view : undefined,
|
|
437
|
+
rendered: pendingRendered.splice(0),
|
|
438
|
+
viewed: pendingViewed.splice(0)
|
|
439
|
+
});
|
|
440
|
+
if (beacons.length === 0) {
|
|
441
|
+
return;
|
|
442
|
+
}
|
|
443
|
+
viewSent = true;
|
|
444
|
+
lastSentAt = Date.now();
|
|
445
|
+
var _iterator = _createForOfIteratorHelper(beacons),
|
|
446
|
+
_step;
|
|
447
|
+
try {
|
|
448
|
+
for (_iterator.s(); !(_step = _iterator.n()).done;) {
|
|
449
|
+
var beacon = _step.value;
|
|
450
|
+
send(beacon, leaving);
|
|
451
|
+
}
|
|
452
|
+
} catch (err) {
|
|
453
|
+
_iterator.e(err);
|
|
454
|
+
} finally {
|
|
455
|
+
_iterator.f();
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
function maybeSendSoon() {
|
|
459
|
+
if (!viewSent || batchTimer !== null) {
|
|
460
|
+
// Before the first beacon, what is waiting goes with it.
|
|
461
|
+
return;
|
|
462
|
+
}
|
|
463
|
+
if (pendingRendered.length + pendingViewed.length < sendWhenPending) {
|
|
464
|
+
return;
|
|
465
|
+
}
|
|
466
|
+
batchTimer = setTimeout(function () {
|
|
467
|
+
batchTimer = null;
|
|
468
|
+
flushNow(false);
|
|
469
|
+
}, Math.max(0, lastSentAt + minSendIntervalMs - Date.now()));
|
|
470
|
+
}
|
|
471
|
+
function countRendered(element) {
|
|
472
|
+
var _iterator2 = _createForOfIteratorHelper(splitValPaths(element.getAttribute(ATTRIBUTE))),
|
|
473
|
+
_step2;
|
|
474
|
+
try {
|
|
475
|
+
for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
|
|
476
|
+
var path = _step2.value;
|
|
477
|
+
if (!rendered.has(path)) {
|
|
478
|
+
rendered.add(path);
|
|
479
|
+
pendingRendered.push(path);
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
} catch (err) {
|
|
483
|
+
_iterator2.e(err);
|
|
484
|
+
} finally {
|
|
485
|
+
_iterator2.f();
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
function countViewed(element) {
|
|
489
|
+
var _iterator3 = _createForOfIteratorHelper(splitValPaths(element.getAttribute(ATTRIBUTE))),
|
|
490
|
+
_step3;
|
|
491
|
+
try {
|
|
492
|
+
for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
|
|
493
|
+
var path = _step3.value;
|
|
494
|
+
if (!viewed.has(path)) {
|
|
495
|
+
viewed.add(path);
|
|
496
|
+
pendingViewed.push(path);
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
} catch (err) {
|
|
500
|
+
_iterator3.e(err);
|
|
501
|
+
} finally {
|
|
502
|
+
_iterator3.f();
|
|
503
|
+
}
|
|
504
|
+
maybeSendSoon();
|
|
505
|
+
}
|
|
506
|
+
function onScreenEnough(entry) {
|
|
507
|
+
if (!entry.isIntersecting) {
|
|
508
|
+
return false;
|
|
509
|
+
}
|
|
510
|
+
return entry.intersectionRatio >= 0.5 || entry.intersectionRect.height >= 0.5 * innerHeight;
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
/** Elements on screen enough to be viewed, whether or not they have been. */
|
|
514
|
+
var onScreen = new Set();
|
|
515
|
+
function armViewTimer(element) {
|
|
516
|
+
if (viewTimers.has(element) || document.visibilityState !== "visible") {
|
|
517
|
+
return;
|
|
518
|
+
}
|
|
519
|
+
viewTimers.set(element, setTimeout(function () {
|
|
520
|
+
viewTimers["delete"](element);
|
|
521
|
+
if (element.isConnected) {
|
|
522
|
+
countViewed(element);
|
|
523
|
+
}
|
|
524
|
+
}, viewedAfterMs));
|
|
525
|
+
}
|
|
526
|
+
function clearViewTimer(element) {
|
|
527
|
+
var timer = viewTimers.get(element);
|
|
528
|
+
if (timer !== undefined) {
|
|
529
|
+
clearTimeout(timer);
|
|
530
|
+
viewTimers["delete"](element);
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
function clearViewTimers() {
|
|
534
|
+
var _iterator4 = _createForOfIteratorHelper(viewTimers.values()),
|
|
535
|
+
_step4;
|
|
536
|
+
try {
|
|
537
|
+
for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
|
|
538
|
+
var timer = _step4.value;
|
|
539
|
+
clearTimeout(timer);
|
|
540
|
+
}
|
|
541
|
+
} catch (err) {
|
|
542
|
+
_iterator4.e(err);
|
|
543
|
+
} finally {
|
|
544
|
+
_iterator4.f();
|
|
545
|
+
}
|
|
546
|
+
viewTimers.clear();
|
|
547
|
+
}
|
|
548
|
+
var intersections = new IntersectionObserver(function (entries) {
|
|
549
|
+
var _iterator5 = _createForOfIteratorHelper(entries),
|
|
550
|
+
_step5;
|
|
551
|
+
try {
|
|
552
|
+
for (_iterator5.s(); !(_step5 = _iterator5.n()).done;) {
|
|
553
|
+
var entry = _step5.value;
|
|
554
|
+
if (onScreenEnough(entry)) {
|
|
555
|
+
onScreen.add(entry.target);
|
|
556
|
+
armViewTimer(entry.target);
|
|
557
|
+
} else {
|
|
558
|
+
onScreen["delete"](entry.target);
|
|
559
|
+
clearViewTimer(entry.target);
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
} catch (err) {
|
|
563
|
+
_iterator5.e(err);
|
|
564
|
+
} finally {
|
|
565
|
+
_iterator5.f();
|
|
566
|
+
}
|
|
567
|
+
}, {
|
|
568
|
+
threshold: THRESHOLDS
|
|
569
|
+
});
|
|
570
|
+
var observed = new Set();
|
|
571
|
+
function forget(element) {
|
|
572
|
+
observed["delete"](element);
|
|
573
|
+
onScreen["delete"](element);
|
|
574
|
+
clearViewTimer(element);
|
|
575
|
+
intersections.unobserve(element);
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
/*
|
|
579
|
+
* After a client-side navigation the URL changes when it starts, and the
|
|
580
|
+
* old page stays on screen until the new one has loaded -- often longer
|
|
581
|
+
* than a scan takes to find it. So what is on the page when the URL
|
|
582
|
+
* changes is CARRIED, and not counted for the new route until the page has
|
|
583
|
+
* changed under it: some content of the new page is added, or some of the
|
|
584
|
+
* carried content is removed. What is still there then is the layout
|
|
585
|
+
* around both pages (a header, a footer), and it counts.
|
|
586
|
+
*/
|
|
587
|
+
var carried = new Set();
|
|
588
|
+
function confirmCarried() {
|
|
589
|
+
if (carried.size === 0) {
|
|
590
|
+
return;
|
|
591
|
+
}
|
|
592
|
+
var kept = _toConsumableArray(carried);
|
|
593
|
+
carried.clear();
|
|
594
|
+
var _iterator6 = _createForOfIteratorHelper(kept),
|
|
595
|
+
_step6;
|
|
596
|
+
try {
|
|
597
|
+
for (_iterator6.s(); !(_step6 = _iterator6.n()).done;) {
|
|
598
|
+
var element = _step6.value;
|
|
599
|
+
if (element.isConnected) {
|
|
600
|
+
track(element);
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
} catch (err) {
|
|
604
|
+
_iterator6.e(err);
|
|
605
|
+
} finally {
|
|
606
|
+
_iterator6.f();
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
function track(element) {
|
|
610
|
+
if (carried.has(element)) {
|
|
611
|
+
return;
|
|
612
|
+
}
|
|
613
|
+
countRendered(element);
|
|
614
|
+
if (!observed.has(element)) {
|
|
615
|
+
observed.add(element);
|
|
616
|
+
intersections.observe(element);
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
function scanAll() {
|
|
620
|
+
document.querySelectorAll(SELECTOR).forEach(track);
|
|
621
|
+
maybeSendSoon();
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
/*
|
|
625
|
+
* What changed, a little later: a render that adds a hundred tagged
|
|
626
|
+
* elements is one pass, and the pass looks at what was added rather than
|
|
627
|
+
* at the whole document, so a page that changes all the time (a clock, a
|
|
628
|
+
* carousel) costs what it changes.
|
|
629
|
+
*/
|
|
630
|
+
var added = new Set();
|
|
631
|
+
var removedSome = false;
|
|
632
|
+
var processQueued = false;
|
|
633
|
+
function process() {
|
|
634
|
+
processQueued = false;
|
|
635
|
+
if (stopped) {
|
|
636
|
+
return;
|
|
637
|
+
}
|
|
638
|
+
var pageChanged = false;
|
|
639
|
+
var _iterator7 = _createForOfIteratorHelper(added),
|
|
640
|
+
_step7;
|
|
641
|
+
try {
|
|
642
|
+
var _loop = function _loop() {
|
|
643
|
+
var root = _step7.value;
|
|
644
|
+
if (!root.isConnected) {
|
|
645
|
+
return 1; // continue
|
|
646
|
+
}
|
|
647
|
+
var found = root.matches(SELECTOR) ? [root] : [];
|
|
648
|
+
root.querySelectorAll(SELECTOR).forEach(function (element) {
|
|
649
|
+
found.push(element);
|
|
650
|
+
});
|
|
651
|
+
for (var _i = 0, _found = found; _i < _found.length; _i++) {
|
|
652
|
+
var _element2 = _found[_i];
|
|
653
|
+
if (!carried.has(_element2)) {
|
|
654
|
+
pageChanged = true;
|
|
655
|
+
track(_element2);
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
};
|
|
659
|
+
for (_iterator7.s(); !(_step7 = _iterator7.n()).done;) {
|
|
660
|
+
if (_loop()) continue;
|
|
661
|
+
}
|
|
662
|
+
} catch (err) {
|
|
663
|
+
_iterator7.e(err);
|
|
664
|
+
} finally {
|
|
665
|
+
_iterator7.f();
|
|
666
|
+
}
|
|
667
|
+
added.clear();
|
|
668
|
+
if (removedSome) {
|
|
669
|
+
removedSome = false;
|
|
670
|
+
var _iterator8 = _createForOfIteratorHelper(observed),
|
|
671
|
+
_step8;
|
|
672
|
+
try {
|
|
673
|
+
for (_iterator8.s(); !(_step8 = _iterator8.n()).done;) {
|
|
674
|
+
var element = _step8.value;
|
|
675
|
+
if (!element.isConnected) {
|
|
676
|
+
forget(element);
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
} catch (err) {
|
|
680
|
+
_iterator8.e(err);
|
|
681
|
+
} finally {
|
|
682
|
+
_iterator8.f();
|
|
683
|
+
}
|
|
684
|
+
var _iterator9 = _createForOfIteratorHelper(carried),
|
|
685
|
+
_step9;
|
|
686
|
+
try {
|
|
687
|
+
for (_iterator9.s(); !(_step9 = _iterator9.n()).done;) {
|
|
688
|
+
var _element = _step9.value;
|
|
689
|
+
if (!_element.isConnected) {
|
|
690
|
+
carried["delete"](_element);
|
|
691
|
+
pageChanged = true;
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
} catch (err) {
|
|
695
|
+
_iterator9.e(err);
|
|
696
|
+
} finally {
|
|
697
|
+
_iterator9.f();
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
if (pageChanged) {
|
|
701
|
+
confirmCarried();
|
|
702
|
+
}
|
|
703
|
+
maybeSendSoon();
|
|
704
|
+
}
|
|
705
|
+
var mutations = new MutationObserver(function (records) {
|
|
706
|
+
var _iterator0 = _createForOfIteratorHelper(records),
|
|
707
|
+
_step0;
|
|
708
|
+
try {
|
|
709
|
+
for (_iterator0.s(); !(_step0 = _iterator0.n()).done;) {
|
|
710
|
+
var record = _step0.value;
|
|
711
|
+
if (record.type === "attributes") {
|
|
712
|
+
if (record.target instanceof Element) {
|
|
713
|
+
added.add(record.target);
|
|
714
|
+
}
|
|
715
|
+
continue;
|
|
716
|
+
}
|
|
717
|
+
record.addedNodes.forEach(function (node) {
|
|
718
|
+
if (node instanceof Element) {
|
|
719
|
+
added.add(node);
|
|
720
|
+
}
|
|
721
|
+
});
|
|
722
|
+
if (record.removedNodes.length > 0) {
|
|
723
|
+
removedSome = true;
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
} catch (err) {
|
|
727
|
+
_iterator0.e(err);
|
|
728
|
+
} finally {
|
|
729
|
+
_iterator0.f();
|
|
730
|
+
}
|
|
731
|
+
if (!processQueued && (added.size > 0 || removedSome)) {
|
|
732
|
+
processQueued = true;
|
|
733
|
+
setTimeout(process, 50);
|
|
734
|
+
}
|
|
735
|
+
});
|
|
736
|
+
|
|
737
|
+
/**
|
|
738
|
+
* A new page view. `carry` for a client-side navigation, whose old page is
|
|
739
|
+
* probably still on screen; see `carried`.
|
|
740
|
+
*/
|
|
741
|
+
function startPageView(first, url, carry) {
|
|
742
|
+
pathname = location.pathname;
|
|
743
|
+
route = routeFor(pathname);
|
|
744
|
+
view = pageView(first, url);
|
|
745
|
+
viewSent = false;
|
|
746
|
+
rendered.clear();
|
|
747
|
+
viewed.clear();
|
|
748
|
+
clearViewTimers();
|
|
749
|
+
// Observe everything afresh, so what is already on screen reports itself
|
|
750
|
+
// to the new page view.
|
|
751
|
+
for (var _i2 = 0, _arr = _toConsumableArray(observed); _i2 < _arr.length; _i2++) {
|
|
752
|
+
var element = _arr[_i2];
|
|
753
|
+
forget(element);
|
|
754
|
+
}
|
|
755
|
+
carried.clear();
|
|
756
|
+
if (carry) {
|
|
757
|
+
document.querySelectorAll(SELECTOR).forEach(function (element) {
|
|
758
|
+
carried.add(element);
|
|
759
|
+
});
|
|
760
|
+
}
|
|
761
|
+
firstSendTimer = setTimeout(function () {
|
|
762
|
+
firstSendTimer = null;
|
|
763
|
+
flushNow(false);
|
|
764
|
+
}, firstSendAfterMs);
|
|
765
|
+
scanAll();
|
|
766
|
+
}
|
|
767
|
+
function onLocationMaybeChanged() {
|
|
768
|
+
if (stopped || location.pathname === pathname) {
|
|
769
|
+
return;
|
|
770
|
+
}
|
|
771
|
+
flushNow(false, false);
|
|
772
|
+
startPageView(false, location, true);
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
// Client-side navigation: the router calls `pushState`, and back and
|
|
776
|
+
// forward fire `popstate`. A change of query or hash alone is not a new
|
|
777
|
+
// page view.
|
|
778
|
+
//
|
|
779
|
+
// Applied to `history`, never to `this`: a router may call the function
|
|
780
|
+
// it took off `history` unbound (TanStack's does), and the browser's own
|
|
781
|
+
// throws "Illegal invocation" without its object.
|
|
782
|
+
var originalPushState = history.pushState;
|
|
783
|
+
var originalReplaceState = history.replaceState;
|
|
784
|
+
var pushState = function pushState() {
|
|
785
|
+
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
|
|
786
|
+
args[_key] = arguments[_key];
|
|
787
|
+
}
|
|
788
|
+
originalPushState.apply(history, args);
|
|
789
|
+
onLocationMaybeChanged();
|
|
790
|
+
};
|
|
791
|
+
var replaceState = function replaceState() {
|
|
792
|
+
for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
|
|
793
|
+
args[_key2] = arguments[_key2];
|
|
794
|
+
}
|
|
795
|
+
originalReplaceState.apply(history, args);
|
|
796
|
+
onLocationMaybeChanged();
|
|
797
|
+
};
|
|
798
|
+
history.pushState = pushState;
|
|
799
|
+
history.replaceState = replaceState;
|
|
800
|
+
function onVisibilityChange() {
|
|
801
|
+
if (document.visibilityState === "hidden") {
|
|
802
|
+
// Time hidden is not time looked at: what was on its way to viewed
|
|
803
|
+
// starts again when the visitor is back.
|
|
804
|
+
clearViewTimers();
|
|
805
|
+
flushNow(true);
|
|
806
|
+
} else if (document.visibilityState === "visible") {
|
|
807
|
+
// The observer reports nothing new when only the tab changed, so what
|
|
808
|
+
// is on screen is armed again from what it last said.
|
|
809
|
+
var _iterator1 = _createForOfIteratorHelper(onScreen),
|
|
810
|
+
_step1;
|
|
811
|
+
try {
|
|
812
|
+
for (_iterator1.s(); !(_step1 = _iterator1.n()).done;) {
|
|
813
|
+
var element = _step1.value;
|
|
814
|
+
armViewTimer(element);
|
|
815
|
+
}
|
|
816
|
+
} catch (err) {
|
|
817
|
+
_iterator1.e(err);
|
|
818
|
+
} finally {
|
|
819
|
+
_iterator1.f();
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
function onPageHide() {
|
|
824
|
+
flushNow(true);
|
|
825
|
+
}
|
|
826
|
+
function onPageShow(event) {
|
|
827
|
+
// Back from the back/forward cache: the visitor is looking again.
|
|
828
|
+
if (event.persisted) {
|
|
829
|
+
startPageView(false, location, false);
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
addEventListener("popstate", onLocationMaybeChanged);
|
|
833
|
+
addEventListener("pagehide", onPageHide);
|
|
834
|
+
addEventListener("pageshow", onPageShow);
|
|
835
|
+
document.addEventListener("visibilitychange", onVisibilityChange);
|
|
836
|
+
mutations.observe(document.body, {
|
|
837
|
+
childList: true,
|
|
838
|
+
subtree: true,
|
|
839
|
+
attributes: true,
|
|
840
|
+
attributeFilter: [ATTRIBUTE]
|
|
841
|
+
});
|
|
842
|
+
|
|
843
|
+
/*
|
|
844
|
+
* The page the visitor LANDED on, if it is not the one they are on now.
|
|
845
|
+
*
|
|
846
|
+
* This starts after hydration, from a lazily loaded chunk, and a visitor
|
|
847
|
+
* can click through to another page before it does. That first page view
|
|
848
|
+
* still happened, and it is the one with the referrer -- often the most
|
|
849
|
+
* useful thing about a visit -- so it is sent on its own, with nothing
|
|
850
|
+
* counted on it (nothing was being counted yet), and the page they are on
|
|
851
|
+
* now starts as a navigation within the site.
|
|
852
|
+
*/
|
|
853
|
+
var landing = landingUrl();
|
|
854
|
+
var landedAt = (_options$landingPath = options.landingPath) !== null && _options$landingPath !== void 0 ? _options$landingPath : landing === null || landing === void 0 ? void 0 : landing.pathname;
|
|
855
|
+
// The campaign is in the URL the visitor arrived at; a redirect or the
|
|
856
|
+
// router may have dropped the query since.
|
|
857
|
+
var arrivedAt = landing && !utmOf(location.search) ? landing : location;
|
|
858
|
+
if (landedAt !== undefined && landedAt !== location.pathname) {
|
|
859
|
+
var _iterator10 = _createForOfIteratorHelper(buildBeacons({
|
|
860
|
+
route: routeFor(landedAt),
|
|
861
|
+
cv: options.contentVersion,
|
|
862
|
+
view: pageView(true, arrivedAt),
|
|
863
|
+
rendered: [],
|
|
864
|
+
viewed: []
|
|
865
|
+
})),
|
|
866
|
+
_step10;
|
|
867
|
+
try {
|
|
868
|
+
for (_iterator10.s(); !(_step10 = _iterator10.n()).done;) {
|
|
869
|
+
var beacon = _step10.value;
|
|
870
|
+
send(beacon, false);
|
|
871
|
+
}
|
|
872
|
+
} catch (err) {
|
|
873
|
+
_iterator10.e(err);
|
|
874
|
+
} finally {
|
|
875
|
+
_iterator10.f();
|
|
876
|
+
}
|
|
877
|
+
startPageView(false, location, false);
|
|
878
|
+
} else {
|
|
879
|
+
startPageView(true, arrivedAt, false);
|
|
880
|
+
}
|
|
881
|
+
function stop(sendFirst) {
|
|
882
|
+
if (stopped) {
|
|
883
|
+
return;
|
|
884
|
+
}
|
|
885
|
+
if (sendFirst) {
|
|
886
|
+
flushNow(false, false);
|
|
887
|
+
}
|
|
888
|
+
stopped = true;
|
|
889
|
+
if (firstSendTimer !== null) {
|
|
890
|
+
clearTimeout(firstSendTimer);
|
|
891
|
+
}
|
|
892
|
+
if (batchTimer !== null) {
|
|
893
|
+
clearTimeout(batchTimer);
|
|
894
|
+
}
|
|
895
|
+
clearViewTimers();
|
|
896
|
+
mutations.disconnect();
|
|
897
|
+
intersections.disconnect();
|
|
898
|
+
// Only put them back if nothing has wrapped them since: restoring over a
|
|
899
|
+
// later wrapper would take it off, and whatever installed it would stop
|
|
900
|
+
// seeing navigations.
|
|
901
|
+
if (history.pushState === pushState) {
|
|
902
|
+
history.pushState = originalPushState;
|
|
903
|
+
}
|
|
904
|
+
if (history.replaceState === replaceState) {
|
|
905
|
+
history.replaceState = originalReplaceState;
|
|
906
|
+
}
|
|
907
|
+
removeEventListener("popstate", onLocationMaybeChanged);
|
|
908
|
+
removeEventListener("pagehide", onPageHide);
|
|
909
|
+
removeEventListener("pageshow", onPageShow);
|
|
910
|
+
document.removeEventListener("visibilitychange", onVisibilityChange);
|
|
911
|
+
}
|
|
912
|
+
return {
|
|
913
|
+
flush: function flush() {
|
|
914
|
+
if (!stopped) {
|
|
915
|
+
flushNow(false);
|
|
916
|
+
}
|
|
917
|
+
},
|
|
918
|
+
stop: function (_stop) {
|
|
919
|
+
function stop() {
|
|
920
|
+
return _stop.apply(this, arguments);
|
|
921
|
+
}
|
|
922
|
+
stop.toString = function () {
|
|
923
|
+
return _stop.toString();
|
|
924
|
+
};
|
|
925
|
+
return stop;
|
|
926
|
+
}(function () {
|
|
927
|
+
return stop(true);
|
|
928
|
+
})
|
|
929
|
+
};
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
/** The URL the document was loaded at, from the navigation timing entry. */
|
|
933
|
+
function landingUrl() {
|
|
934
|
+
if (typeof performance === "undefined" || !performance.getEntriesByType) {
|
|
935
|
+
return null;
|
|
936
|
+
}
|
|
937
|
+
var _performance$getEntri = performance.getEntriesByType("navigation"),
|
|
938
|
+
_performance$getEntri2 = _slicedToArray(_performance$getEntri, 1),
|
|
939
|
+
entry = _performance$getEntri2[0];
|
|
940
|
+
if (!(entry !== null && entry !== void 0 && entry.name)) {
|
|
941
|
+
return null;
|
|
942
|
+
}
|
|
943
|
+
try {
|
|
944
|
+
return new URL(entry.name);
|
|
945
|
+
} catch (_unused) {
|
|
946
|
+
return null;
|
|
947
|
+
}
|
|
948
|
+
}
|
|
949
|
+
|
|
950
|
+
export { MAX_PATHS_PER_BEACON, MAX_ROUTE_LENGTH, MAX_UTM_LENGTH, buildBeacons, splitValPaths, startValAnalytics };
|