@oxvo/browser 7.1.0 → 7.3.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/dist/cjs/common/interaction.d.ts +12 -1
- package/dist/cjs/common/messages.gen.d.ts +4 -7
- package/dist/cjs/entry.js +991 -945
- package/dist/cjs/entry.js.map +1 -1
- package/dist/cjs/index.js +918 -913
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/main/app/canvas.d.ts +8 -0
- package/dist/cjs/main/app/index.d.ts +13 -5
- package/dist/cjs/main/app/messages.gen.d.ts +0 -1
- package/dist/cjs/main/app/observer/observer.d.ts +16 -0
- package/dist/cjs/main/app/sanitizer.d.ts +5 -3
- package/dist/cjs/main/index.d.ts +16 -0
- package/dist/cjs/main/modules/analytics/batcher.d.ts +6 -1
- package/dist/cjs/main/modules/analytics/index.d.ts +5 -0
- package/dist/cjs/main/modules/input.d.ts +2 -1
- package/dist/cjs/main/modules/markerWatcher.d.ts +4 -8
- package/dist/cjs/main/modules/mouse.d.ts +1 -0
- package/dist/cjs/main/modules/tagMatcher.d.ts +28 -0
- package/dist/cjs/main/singleton.d.ts +24 -0
- package/dist/cjs/main/utils.d.ts +2 -0
- package/dist/lib/common/interaction.d.ts +12 -1
- package/dist/lib/common/messages.gen.d.ts +4 -7
- package/dist/lib/entry.js +991 -945
- package/dist/lib/entry.js.map +1 -1
- package/dist/lib/index.js +918 -913
- package/dist/lib/index.js.map +1 -1
- package/dist/lib/main/app/canvas.d.ts +8 -0
- package/dist/lib/main/app/index.d.ts +13 -5
- package/dist/lib/main/app/messages.gen.d.ts +0 -1
- package/dist/lib/main/app/observer/observer.d.ts +16 -0
- package/dist/lib/main/app/sanitizer.d.ts +5 -3
- package/dist/lib/main/index.d.ts +16 -0
- package/dist/lib/main/modules/analytics/batcher.d.ts +6 -1
- package/dist/lib/main/modules/analytics/index.d.ts +5 -0
- package/dist/lib/main/modules/input.d.ts +2 -1
- package/dist/lib/main/modules/markerWatcher.d.ts +4 -8
- package/dist/lib/main/modules/mouse.d.ts +1 -0
- package/dist/lib/main/modules/tagMatcher.d.ts +28 -0
- package/dist/lib/main/singleton.d.ts +24 -0
- package/dist/lib/main/utils.d.ts +2 -0
- package/dist/types/common/interaction.d.ts +12 -1
- package/dist/types/common/messages.gen.d.ts +4 -7
- package/dist/types/main/app/canvas.d.ts +8 -0
- package/dist/types/main/app/index.d.ts +13 -5
- package/dist/types/main/app/messages.gen.d.ts +0 -1
- package/dist/types/main/app/observer/observer.d.ts +16 -0
- package/dist/types/main/app/sanitizer.d.ts +5 -3
- package/dist/types/main/index.d.ts +16 -0
- package/dist/types/main/modules/analytics/batcher.d.ts +6 -1
- package/dist/types/main/modules/analytics/index.d.ts +5 -0
- package/dist/types/main/modules/input.d.ts +2 -1
- package/dist/types/main/modules/markerWatcher.d.ts +4 -8
- package/dist/types/main/modules/mouse.d.ts +1 -0
- package/dist/types/main/modules/tagMatcher.d.ts +28 -0
- package/dist/types/main/singleton.d.ts +24 -0
- package/dist/types/main/utils.d.ts +2 -0
- package/package.json +11 -12
package/dist/cjs/index.js
CHANGED
|
@@ -2,719 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
4
|
|
|
5
|
-
// DEFLATE is a complex format; to read this code, you should probably check the RFC first:
|
|
6
|
-
// https://tools.ietf.org/html/rfc1951
|
|
7
|
-
// You may also wish to take a look at the guide I made about this program:
|
|
8
|
-
// https://gist.github.com/101arrowz/253f31eb5abc3d9275ab943003ffecad
|
|
9
|
-
// Some of the following code is similar to that of UZIP.js:
|
|
10
|
-
// https://github.com/photopea/UZIP.js
|
|
11
|
-
// However, the vast majority of the codebase has diverged from UZIP.js to increase performance and reduce bundle size.
|
|
12
|
-
// Sometimes 0 will appear where -1 would be more appropriate. This is because using a uint
|
|
13
|
-
// is better for memory in most engines (I *think*).
|
|
14
|
-
var ch2 = {};
|
|
15
|
-
var wk = (function (c, id, msg, transfer, cb) {
|
|
16
|
-
var w = new Worker(ch2[id] || (ch2[id] = URL.createObjectURL(new Blob([
|
|
17
|
-
c + ';addEventListener("error",function(e){e=e.error;postMessage({$e$:[e.message,e.code,e.stack]})})'
|
|
18
|
-
], { type: 'text/javascript' }))));
|
|
19
|
-
w.onmessage = function (e) {
|
|
20
|
-
var d = e.data, ed = d.$e$;
|
|
21
|
-
if (ed) {
|
|
22
|
-
var err = new Error(ed[0]);
|
|
23
|
-
err['code'] = ed[1];
|
|
24
|
-
err.stack = ed[2];
|
|
25
|
-
cb(err, null);
|
|
26
|
-
}
|
|
27
|
-
else
|
|
28
|
-
cb(null, d);
|
|
29
|
-
};
|
|
30
|
-
w.postMessage(msg, transfer);
|
|
31
|
-
return w;
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
// aliases for shorter compressed code (most minifers don't do this)
|
|
35
|
-
var u8 = Uint8Array, u16 = Uint16Array, i32 = Int32Array;
|
|
36
|
-
// fixed length extra bits
|
|
37
|
-
var fleb = new u8([0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, /* unused */ 0, 0, /* impossible */ 0]);
|
|
38
|
-
// fixed distance extra bits
|
|
39
|
-
var fdeb = new u8([0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, /* unused */ 0, 0]);
|
|
40
|
-
// code length index map
|
|
41
|
-
var clim = new u8([16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15]);
|
|
42
|
-
// get base, reverse index map from extra bits
|
|
43
|
-
var freb = function (eb, start) {
|
|
44
|
-
var b = new u16(31);
|
|
45
|
-
for (var i = 0; i < 31; ++i) {
|
|
46
|
-
b[i] = start += 1 << eb[i - 1];
|
|
47
|
-
}
|
|
48
|
-
// numbers here are at max 18 bits
|
|
49
|
-
var r = new i32(b[30]);
|
|
50
|
-
for (var i = 1; i < 30; ++i) {
|
|
51
|
-
for (var j = b[i]; j < b[i + 1]; ++j) {
|
|
52
|
-
r[j] = ((j - b[i]) << 5) | i;
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
return { b: b, r: r };
|
|
56
|
-
};
|
|
57
|
-
var _a = freb(fleb, 2), fl = _a.b, revfl = _a.r;
|
|
58
|
-
// we can ignore the fact that the other numbers are wrong; they never happen anyway
|
|
59
|
-
fl[28] = 258, revfl[258] = 28;
|
|
60
|
-
var _b = freb(fdeb, 0), revfd = _b.r;
|
|
61
|
-
// map of value to reverse (assuming 16 bits)
|
|
62
|
-
var rev = new u16(32768);
|
|
63
|
-
for (var i$1 = 0; i$1 < 32768; ++i$1) {
|
|
64
|
-
// reverse table algorithm from SO
|
|
65
|
-
var x$1 = ((i$1 & 0xAAAA) >> 1) | ((i$1 & 0x5555) << 1);
|
|
66
|
-
x$1 = ((x$1 & 0xCCCC) >> 2) | ((x$1 & 0x3333) << 2);
|
|
67
|
-
x$1 = ((x$1 & 0xF0F0) >> 4) | ((x$1 & 0x0F0F) << 4);
|
|
68
|
-
rev[i$1] = (((x$1 & 0xFF00) >> 8) | ((x$1 & 0x00FF) << 8)) >> 1;
|
|
69
|
-
}
|
|
70
|
-
// create huffman tree from u8 "map": index -> code length for code index
|
|
71
|
-
// mb (max bits) must be at most 15
|
|
72
|
-
// TODO: optimize/split up?
|
|
73
|
-
var hMap = (function (cd, mb, r) {
|
|
74
|
-
var s = cd.length;
|
|
75
|
-
// index
|
|
76
|
-
var i = 0;
|
|
77
|
-
// u16 "map": index -> # of codes with bit length = index
|
|
78
|
-
var l = new u16(mb);
|
|
79
|
-
// length of cd must be 288 (total # of codes)
|
|
80
|
-
for (; i < s; ++i) {
|
|
81
|
-
if (cd[i])
|
|
82
|
-
++l[cd[i] - 1];
|
|
83
|
-
}
|
|
84
|
-
// u16 "map": index -> minimum code for bit length = index
|
|
85
|
-
var le = new u16(mb);
|
|
86
|
-
for (i = 1; i < mb; ++i) {
|
|
87
|
-
le[i] = (le[i - 1] + l[i - 1]) << 1;
|
|
88
|
-
}
|
|
89
|
-
var co;
|
|
90
|
-
if (r) {
|
|
91
|
-
// u16 "map": index -> number of actual bits, symbol for code
|
|
92
|
-
co = new u16(1 << mb);
|
|
93
|
-
// bits to remove for reverser
|
|
94
|
-
var rvb = 15 - mb;
|
|
95
|
-
for (i = 0; i < s; ++i) {
|
|
96
|
-
// ignore 0 lengths
|
|
97
|
-
if (cd[i]) {
|
|
98
|
-
// num encoding both symbol and bits read
|
|
99
|
-
var sv = (i << 4) | cd[i];
|
|
100
|
-
// free bits
|
|
101
|
-
var r_1 = mb - cd[i];
|
|
102
|
-
// start value
|
|
103
|
-
var v = le[cd[i] - 1]++ << r_1;
|
|
104
|
-
// m is end value
|
|
105
|
-
for (var m = v | ((1 << r_1) - 1); v <= m; ++v) {
|
|
106
|
-
// every 16 bit value starting with the code yields the same result
|
|
107
|
-
co[rev[v] >> rvb] = sv;
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
else {
|
|
113
|
-
co = new u16(s);
|
|
114
|
-
for (i = 0; i < s; ++i) {
|
|
115
|
-
if (cd[i]) {
|
|
116
|
-
co[i] = rev[le[cd[i] - 1]++] >> (15 - cd[i]);
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
return co;
|
|
121
|
-
});
|
|
122
|
-
// fixed length tree
|
|
123
|
-
var flt = new u8(288);
|
|
124
|
-
for (var i$1 = 0; i$1 < 144; ++i$1)
|
|
125
|
-
flt[i$1] = 8;
|
|
126
|
-
for (var i$1 = 144; i$1 < 256; ++i$1)
|
|
127
|
-
flt[i$1] = 9;
|
|
128
|
-
for (var i$1 = 256; i$1 < 280; ++i$1)
|
|
129
|
-
flt[i$1] = 7;
|
|
130
|
-
for (var i$1 = 280; i$1 < 288; ++i$1)
|
|
131
|
-
flt[i$1] = 8;
|
|
132
|
-
// fixed distance tree
|
|
133
|
-
var fdt = new u8(32);
|
|
134
|
-
for (var i$1 = 0; i$1 < 32; ++i$1)
|
|
135
|
-
fdt[i$1] = 5;
|
|
136
|
-
// fixed length map
|
|
137
|
-
var flm = /*#__PURE__*/ hMap(flt, 9, 0);
|
|
138
|
-
// fixed distance map
|
|
139
|
-
var fdm = /*#__PURE__*/ hMap(fdt, 5, 0);
|
|
140
|
-
// get end of byte
|
|
141
|
-
var shft = function (p) { return ((p + 7) / 8) | 0; };
|
|
142
|
-
// typed array slice - allows garbage collector to free original reference,
|
|
143
|
-
// while being more compatible than .slice
|
|
144
|
-
var slc = function (v, s, e) {
|
|
145
|
-
if (s == null || s < 0)
|
|
146
|
-
s = 0;
|
|
147
|
-
if (e == null || e > v.length)
|
|
148
|
-
e = v.length;
|
|
149
|
-
// can't use .constructor in case user-supplied
|
|
150
|
-
return new u8(v.subarray(s, e));
|
|
151
|
-
};
|
|
152
|
-
// error codes
|
|
153
|
-
var ec = [
|
|
154
|
-
'unexpected EOF',
|
|
155
|
-
'invalid block type',
|
|
156
|
-
'invalid length/literal',
|
|
157
|
-
'invalid distance',
|
|
158
|
-
'stream finished',
|
|
159
|
-
'no stream handler',
|
|
160
|
-
,
|
|
161
|
-
'no callback',
|
|
162
|
-
'invalid UTF-8 data',
|
|
163
|
-
'extra field too long',
|
|
164
|
-
'date not in range 1980-2099',
|
|
165
|
-
'filename too long',
|
|
166
|
-
'stream finishing',
|
|
167
|
-
'invalid zip data'
|
|
168
|
-
// determined by unknown compression method
|
|
169
|
-
];
|
|
170
|
-
var err = function (ind, msg, nt) {
|
|
171
|
-
var e = new Error(msg || ec[ind]);
|
|
172
|
-
e.code = ind;
|
|
173
|
-
if (Error.captureStackTrace)
|
|
174
|
-
Error.captureStackTrace(e, err);
|
|
175
|
-
if (!nt)
|
|
176
|
-
throw e;
|
|
177
|
-
return e;
|
|
178
|
-
};
|
|
179
|
-
// starting at p, write the minimum number of bits that can hold v to d
|
|
180
|
-
var wbits = function (d, p, v) {
|
|
181
|
-
v <<= p & 7;
|
|
182
|
-
var o = (p / 8) | 0;
|
|
183
|
-
d[o] |= v;
|
|
184
|
-
d[o + 1] |= v >> 8;
|
|
185
|
-
};
|
|
186
|
-
// starting at p, write the minimum number of bits (>8) that can hold v to d
|
|
187
|
-
var wbits16 = function (d, p, v) {
|
|
188
|
-
v <<= p & 7;
|
|
189
|
-
var o = (p / 8) | 0;
|
|
190
|
-
d[o] |= v;
|
|
191
|
-
d[o + 1] |= v >> 8;
|
|
192
|
-
d[o + 2] |= v >> 16;
|
|
193
|
-
};
|
|
194
|
-
// creates code lengths from a frequency table
|
|
195
|
-
var hTree = function (d, mb) {
|
|
196
|
-
// Need extra info to make a tree
|
|
197
|
-
var t = [];
|
|
198
|
-
for (var i = 0; i < d.length; ++i) {
|
|
199
|
-
if (d[i])
|
|
200
|
-
t.push({ s: i, f: d[i] });
|
|
201
|
-
}
|
|
202
|
-
var s = t.length;
|
|
203
|
-
var t2 = t.slice();
|
|
204
|
-
if (!s)
|
|
205
|
-
return { t: et, l: 0 };
|
|
206
|
-
if (s == 1) {
|
|
207
|
-
var v = new u8(t[0].s + 1);
|
|
208
|
-
v[t[0].s] = 1;
|
|
209
|
-
return { t: v, l: 1 };
|
|
210
|
-
}
|
|
211
|
-
t.sort(function (a, b) { return a.f - b.f; });
|
|
212
|
-
// after i2 reaches last ind, will be stopped
|
|
213
|
-
// freq must be greater than largest possible number of symbols
|
|
214
|
-
t.push({ s: -1, f: 25001 });
|
|
215
|
-
var l = t[0], r = t[1], i0 = 0, i1 = 1, i2 = 2;
|
|
216
|
-
t[0] = { s: -1, f: l.f + r.f, l: l, r: r };
|
|
217
|
-
// efficient algorithm from UZIP.js
|
|
218
|
-
// i0 is lookbehind, i2 is lookahead - after processing two low-freq
|
|
219
|
-
// symbols that combined have high freq, will start processing i2 (high-freq,
|
|
220
|
-
// non-composite) symbols instead
|
|
221
|
-
// see https://reddit.com/r/photopea/comments/ikekht/uzipjs_questions/
|
|
222
|
-
while (i1 != s - 1) {
|
|
223
|
-
l = t[t[i0].f < t[i2].f ? i0++ : i2++];
|
|
224
|
-
r = t[i0 != i1 && t[i0].f < t[i2].f ? i0++ : i2++];
|
|
225
|
-
t[i1++] = { s: -1, f: l.f + r.f, l: l, r: r };
|
|
226
|
-
}
|
|
227
|
-
var maxSym = t2[0].s;
|
|
228
|
-
for (var i = 1; i < s; ++i) {
|
|
229
|
-
if (t2[i].s > maxSym)
|
|
230
|
-
maxSym = t2[i].s;
|
|
231
|
-
}
|
|
232
|
-
// code lengths
|
|
233
|
-
var tr = new u16(maxSym + 1);
|
|
234
|
-
// max bits in tree
|
|
235
|
-
var mbt = ln(t[i1 - 1], tr, 0);
|
|
236
|
-
if (mbt > mb) {
|
|
237
|
-
// more algorithms from UZIP.js
|
|
238
|
-
// TODO: find out how this code works (debt)
|
|
239
|
-
// ind debt
|
|
240
|
-
var i = 0, dt = 0;
|
|
241
|
-
// left cost
|
|
242
|
-
var lft = mbt - mb, cst = 1 << lft;
|
|
243
|
-
t2.sort(function (a, b) { return tr[b.s] - tr[a.s] || a.f - b.f; });
|
|
244
|
-
for (; i < s; ++i) {
|
|
245
|
-
var i2_1 = t2[i].s;
|
|
246
|
-
if (tr[i2_1] > mb) {
|
|
247
|
-
dt += cst - (1 << (mbt - tr[i2_1]));
|
|
248
|
-
tr[i2_1] = mb;
|
|
249
|
-
}
|
|
250
|
-
else
|
|
251
|
-
break;
|
|
252
|
-
}
|
|
253
|
-
dt >>= lft;
|
|
254
|
-
while (dt > 0) {
|
|
255
|
-
var i2_2 = t2[i].s;
|
|
256
|
-
if (tr[i2_2] < mb)
|
|
257
|
-
dt -= 1 << (mb - tr[i2_2]++ - 1);
|
|
258
|
-
else
|
|
259
|
-
++i;
|
|
260
|
-
}
|
|
261
|
-
for (; i >= 0 && dt; --i) {
|
|
262
|
-
var i2_3 = t2[i].s;
|
|
263
|
-
if (tr[i2_3] == mb) {
|
|
264
|
-
--tr[i2_3];
|
|
265
|
-
++dt;
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
mbt = mb;
|
|
269
|
-
}
|
|
270
|
-
return { t: new u8(tr), l: mbt };
|
|
271
|
-
};
|
|
272
|
-
// get the max length and assign length codes
|
|
273
|
-
var ln = function (n, l, d) {
|
|
274
|
-
return n.s == -1
|
|
275
|
-
? Math.max(ln(n.l, l, d + 1), ln(n.r, l, d + 1))
|
|
276
|
-
: (l[n.s] = d);
|
|
277
|
-
};
|
|
278
|
-
// length codes generation
|
|
279
|
-
var lc = function (c) {
|
|
280
|
-
var s = c.length;
|
|
281
|
-
// Note that the semicolon was intentional
|
|
282
|
-
while (s && !c[--s])
|
|
283
|
-
;
|
|
284
|
-
var cl = new u16(++s);
|
|
285
|
-
// ind num streak
|
|
286
|
-
var cli = 0, cln = c[0], cls = 1;
|
|
287
|
-
var w = function (v) { cl[cli++] = v; };
|
|
288
|
-
for (var i = 1; i <= s; ++i) {
|
|
289
|
-
if (c[i] == cln && i != s)
|
|
290
|
-
++cls;
|
|
291
|
-
else {
|
|
292
|
-
if (!cln && cls > 2) {
|
|
293
|
-
for (; cls > 138; cls -= 138)
|
|
294
|
-
w(32754);
|
|
295
|
-
if (cls > 2) {
|
|
296
|
-
w(cls > 10 ? ((cls - 11) << 5) | 28690 : ((cls - 3) << 5) | 12305);
|
|
297
|
-
cls = 0;
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
else if (cls > 3) {
|
|
301
|
-
w(cln), --cls;
|
|
302
|
-
for (; cls > 6; cls -= 6)
|
|
303
|
-
w(8304);
|
|
304
|
-
if (cls > 2)
|
|
305
|
-
w(((cls - 3) << 5) | 8208), cls = 0;
|
|
306
|
-
}
|
|
307
|
-
while (cls--)
|
|
308
|
-
w(cln);
|
|
309
|
-
cls = 1;
|
|
310
|
-
cln = c[i];
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
return { c: cl.subarray(0, cli), n: s };
|
|
314
|
-
};
|
|
315
|
-
// calculate the length of output from tree, code lengths
|
|
316
|
-
var clen = function (cf, cl) {
|
|
317
|
-
var l = 0;
|
|
318
|
-
for (var i = 0; i < cl.length; ++i)
|
|
319
|
-
l += cf[i] * cl[i];
|
|
320
|
-
return l;
|
|
321
|
-
};
|
|
322
|
-
// writes a fixed block
|
|
323
|
-
// returns the new bit pos
|
|
324
|
-
var wfblk = function (out, pos, dat) {
|
|
325
|
-
// no need to write 00 as type: TypedArray defaults to 0
|
|
326
|
-
var s = dat.length;
|
|
327
|
-
var o = shft(pos + 2);
|
|
328
|
-
out[o] = s & 255;
|
|
329
|
-
out[o + 1] = s >> 8;
|
|
330
|
-
out[o + 2] = out[o] ^ 255;
|
|
331
|
-
out[o + 3] = out[o + 1] ^ 255;
|
|
332
|
-
for (var i = 0; i < s; ++i)
|
|
333
|
-
out[o + i + 4] = dat[i];
|
|
334
|
-
return (o + 4 + s) * 8;
|
|
335
|
-
};
|
|
336
|
-
// writes a block
|
|
337
|
-
var wblk = function (dat, out, final, syms, lf, df, eb, li, bs, bl, p) {
|
|
338
|
-
wbits(out, p++, final);
|
|
339
|
-
++lf[256];
|
|
340
|
-
var _a = hTree(lf, 15), dlt = _a.t, mlb = _a.l;
|
|
341
|
-
var _b = hTree(df, 15), ddt = _b.t, mdb = _b.l;
|
|
342
|
-
var _c = lc(dlt), lclt = _c.c, nlc = _c.n;
|
|
343
|
-
var _d = lc(ddt), lcdt = _d.c, ndc = _d.n;
|
|
344
|
-
var lcfreq = new u16(19);
|
|
345
|
-
for (var i = 0; i < lclt.length; ++i)
|
|
346
|
-
++lcfreq[lclt[i] & 31];
|
|
347
|
-
for (var i = 0; i < lcdt.length; ++i)
|
|
348
|
-
++lcfreq[lcdt[i] & 31];
|
|
349
|
-
var _e = hTree(lcfreq, 7), lct = _e.t, mlcb = _e.l;
|
|
350
|
-
var nlcc = 19;
|
|
351
|
-
for (; nlcc > 4 && !lct[clim[nlcc - 1]]; --nlcc)
|
|
352
|
-
;
|
|
353
|
-
var flen = (bl + 5) << 3;
|
|
354
|
-
var ftlen = clen(lf, flt) + clen(df, fdt) + eb;
|
|
355
|
-
var dtlen = clen(lf, dlt) + clen(df, ddt) + eb + 14 + 3 * nlcc + clen(lcfreq, lct) + 2 * lcfreq[16] + 3 * lcfreq[17] + 7 * lcfreq[18];
|
|
356
|
-
if (bs >= 0 && flen <= ftlen && flen <= dtlen)
|
|
357
|
-
return wfblk(out, p, dat.subarray(bs, bs + bl));
|
|
358
|
-
var lm, ll, dm, dl;
|
|
359
|
-
wbits(out, p, 1 + (dtlen < ftlen)), p += 2;
|
|
360
|
-
if (dtlen < ftlen) {
|
|
361
|
-
lm = hMap(dlt, mlb, 0), ll = dlt, dm = hMap(ddt, mdb, 0), dl = ddt;
|
|
362
|
-
var llm = hMap(lct, mlcb, 0);
|
|
363
|
-
wbits(out, p, nlc - 257);
|
|
364
|
-
wbits(out, p + 5, ndc - 1);
|
|
365
|
-
wbits(out, p + 10, nlcc - 4);
|
|
366
|
-
p += 14;
|
|
367
|
-
for (var i = 0; i < nlcc; ++i)
|
|
368
|
-
wbits(out, p + 3 * i, lct[clim[i]]);
|
|
369
|
-
p += 3 * nlcc;
|
|
370
|
-
var lcts = [lclt, lcdt];
|
|
371
|
-
for (var it = 0; it < 2; ++it) {
|
|
372
|
-
var clct = lcts[it];
|
|
373
|
-
for (var i = 0; i < clct.length; ++i) {
|
|
374
|
-
var len = clct[i] & 31;
|
|
375
|
-
wbits(out, p, llm[len]), p += lct[len];
|
|
376
|
-
if (len > 15)
|
|
377
|
-
wbits(out, p, (clct[i] >> 5) & 127), p += clct[i] >> 12;
|
|
378
|
-
}
|
|
379
|
-
}
|
|
380
|
-
}
|
|
381
|
-
else {
|
|
382
|
-
lm = flm, ll = flt, dm = fdm, dl = fdt;
|
|
383
|
-
}
|
|
384
|
-
for (var i = 0; i < li; ++i) {
|
|
385
|
-
var sym = syms[i];
|
|
386
|
-
if (sym > 255) {
|
|
387
|
-
var len = (sym >> 18) & 31;
|
|
388
|
-
wbits16(out, p, lm[len + 257]), p += ll[len + 257];
|
|
389
|
-
if (len > 7)
|
|
390
|
-
wbits(out, p, (sym >> 23) & 31), p += fleb[len];
|
|
391
|
-
var dst = sym & 31;
|
|
392
|
-
wbits16(out, p, dm[dst]), p += dl[dst];
|
|
393
|
-
if (dst > 3)
|
|
394
|
-
wbits16(out, p, (sym >> 5) & 8191), p += fdeb[dst];
|
|
395
|
-
}
|
|
396
|
-
else {
|
|
397
|
-
wbits16(out, p, lm[sym]), p += ll[sym];
|
|
398
|
-
}
|
|
399
|
-
}
|
|
400
|
-
wbits16(out, p, lm[256]);
|
|
401
|
-
return p + ll[256];
|
|
402
|
-
};
|
|
403
|
-
// deflate options (nice << 13) | chain
|
|
404
|
-
var deo = /*#__PURE__*/ new i32([65540, 131080, 131088, 131104, 262176, 1048704, 1048832, 2114560, 2117632]);
|
|
405
|
-
// empty
|
|
406
|
-
var et = /*#__PURE__*/ new u8(0);
|
|
407
|
-
// compresses data into a raw DEFLATE buffer
|
|
408
|
-
var dflt = function (dat, lvl, plvl, pre, post, st) {
|
|
409
|
-
var s = st.z || dat.length;
|
|
410
|
-
var o = new u8(pre + s + 5 * (1 + Math.ceil(s / 7000)) + post);
|
|
411
|
-
// writing to this writes to the output buffer
|
|
412
|
-
var w = o.subarray(pre, o.length - post);
|
|
413
|
-
var lst = st.l;
|
|
414
|
-
var pos = (st.r || 0) & 7;
|
|
415
|
-
if (lvl) {
|
|
416
|
-
if (pos)
|
|
417
|
-
w[0] = st.r >> 3;
|
|
418
|
-
var opt = deo[lvl - 1];
|
|
419
|
-
var n = opt >> 13, c = opt & 8191;
|
|
420
|
-
var msk_1 = (1 << plvl) - 1;
|
|
421
|
-
// prev 2-byte val map curr 2-byte val map
|
|
422
|
-
var prev = st.p || new u16(32768), head = st.h || new u16(msk_1 + 1);
|
|
423
|
-
var bs1_1 = Math.ceil(plvl / 3), bs2_1 = 2 * bs1_1;
|
|
424
|
-
var hsh = function (i) { return (dat[i] ^ (dat[i + 1] << bs1_1) ^ (dat[i + 2] << bs2_1)) & msk_1; };
|
|
425
|
-
// 24576 is an arbitrary number of maximum symbols per block
|
|
426
|
-
// 424 buffer for last block
|
|
427
|
-
var syms = new i32(25000);
|
|
428
|
-
// length/literal freq distance freq
|
|
429
|
-
var lf = new u16(288), df = new u16(32);
|
|
430
|
-
// l/lcnt exbits index l/lind waitdx blkpos
|
|
431
|
-
var lc_1 = 0, eb = 0, i = st.i || 0, li = 0, wi = st.w || 0, bs = 0;
|
|
432
|
-
for (; i + 2 < s; ++i) {
|
|
433
|
-
// hash value
|
|
434
|
-
var hv = hsh(i);
|
|
435
|
-
// index mod 32768 previous index mod
|
|
436
|
-
var imod = i & 32767, pimod = head[hv];
|
|
437
|
-
prev[imod] = pimod;
|
|
438
|
-
head[hv] = imod;
|
|
439
|
-
// We always should modify head and prev, but only add symbols if
|
|
440
|
-
// this data is not yet processed ("wait" for wait index)
|
|
441
|
-
if (wi <= i) {
|
|
442
|
-
// bytes remaining
|
|
443
|
-
var rem = s - i;
|
|
444
|
-
if ((lc_1 > 7000 || li > 24576) && (rem > 423 || !lst)) {
|
|
445
|
-
pos = wblk(dat, w, 0, syms, lf, df, eb, li, bs, i - bs, pos);
|
|
446
|
-
li = lc_1 = eb = 0, bs = i;
|
|
447
|
-
for (var j = 0; j < 286; ++j)
|
|
448
|
-
lf[j] = 0;
|
|
449
|
-
for (var j = 0; j < 30; ++j)
|
|
450
|
-
df[j] = 0;
|
|
451
|
-
}
|
|
452
|
-
// len dist chain
|
|
453
|
-
var l = 2, d = 0, ch_1 = c, dif = imod - pimod & 32767;
|
|
454
|
-
if (rem > 2 && hv == hsh(i - dif)) {
|
|
455
|
-
var maxn = Math.min(n, rem) - 1;
|
|
456
|
-
var maxd = Math.min(32767, i);
|
|
457
|
-
// max possible length
|
|
458
|
-
// not capped at dif because decompressors implement "rolling" index population
|
|
459
|
-
var ml = Math.min(258, rem);
|
|
460
|
-
while (dif <= maxd && --ch_1 && imod != pimod) {
|
|
461
|
-
if (dat[i + l] == dat[i + l - dif]) {
|
|
462
|
-
var nl = 0;
|
|
463
|
-
for (; nl < ml && dat[i + nl] == dat[i + nl - dif]; ++nl)
|
|
464
|
-
;
|
|
465
|
-
if (nl > l) {
|
|
466
|
-
l = nl, d = dif;
|
|
467
|
-
// break out early when we reach "nice" (we are satisfied enough)
|
|
468
|
-
if (nl > maxn)
|
|
469
|
-
break;
|
|
470
|
-
// now, find the rarest 2-byte sequence within this
|
|
471
|
-
// length of literals and search for that instead.
|
|
472
|
-
// Much faster than just using the start
|
|
473
|
-
var mmd = Math.min(dif, nl - 2);
|
|
474
|
-
var md = 0;
|
|
475
|
-
for (var j = 0; j < mmd; ++j) {
|
|
476
|
-
var ti = i - dif + j & 32767;
|
|
477
|
-
var pti = prev[ti];
|
|
478
|
-
var cd = ti - pti & 32767;
|
|
479
|
-
if (cd > md)
|
|
480
|
-
md = cd, pimod = ti;
|
|
481
|
-
}
|
|
482
|
-
}
|
|
483
|
-
}
|
|
484
|
-
// check the previous match
|
|
485
|
-
imod = pimod, pimod = prev[imod];
|
|
486
|
-
dif += imod - pimod & 32767;
|
|
487
|
-
}
|
|
488
|
-
}
|
|
489
|
-
// d will be nonzero only when a match was found
|
|
490
|
-
if (d) {
|
|
491
|
-
// store both dist and len data in one int32
|
|
492
|
-
// Make sure this is recognized as a len/dist with 28th bit (2^28)
|
|
493
|
-
syms[li++] = 268435456 | (revfl[l] << 18) | revfd[d];
|
|
494
|
-
var lin = revfl[l] & 31, din = revfd[d] & 31;
|
|
495
|
-
eb += fleb[lin] + fdeb[din];
|
|
496
|
-
++lf[257 + lin];
|
|
497
|
-
++df[din];
|
|
498
|
-
wi = i + l;
|
|
499
|
-
++lc_1;
|
|
500
|
-
}
|
|
501
|
-
else {
|
|
502
|
-
syms[li++] = dat[i];
|
|
503
|
-
++lf[dat[i]];
|
|
504
|
-
}
|
|
505
|
-
}
|
|
506
|
-
}
|
|
507
|
-
for (i = Math.max(i, wi); i < s; ++i) {
|
|
508
|
-
syms[li++] = dat[i];
|
|
509
|
-
++lf[dat[i]];
|
|
510
|
-
}
|
|
511
|
-
pos = wblk(dat, w, lst, syms, lf, df, eb, li, bs, i - bs, pos);
|
|
512
|
-
if (!lst) {
|
|
513
|
-
st.r = (pos & 7) | w[(pos / 8) | 0] << 3;
|
|
514
|
-
// shft(pos) now 1 less if pos & 7 != 0
|
|
515
|
-
pos -= 7;
|
|
516
|
-
st.h = head, st.p = prev, st.i = i, st.w = wi;
|
|
517
|
-
}
|
|
518
|
-
}
|
|
519
|
-
else {
|
|
520
|
-
for (var i = st.w || 0; i < s + lst; i += 65535) {
|
|
521
|
-
// end
|
|
522
|
-
var e = i + 65535;
|
|
523
|
-
if (e >= s) {
|
|
524
|
-
// write final block
|
|
525
|
-
w[(pos / 8) | 0] = lst;
|
|
526
|
-
e = s;
|
|
527
|
-
}
|
|
528
|
-
pos = wfblk(w, pos + 1, dat.subarray(i, e));
|
|
529
|
-
}
|
|
530
|
-
st.i = s;
|
|
531
|
-
}
|
|
532
|
-
return slc(o, 0, pre + shft(pos) + post);
|
|
533
|
-
};
|
|
534
|
-
// CRC32 table
|
|
535
|
-
var crct = /*#__PURE__*/ (function () {
|
|
536
|
-
var t = new Int32Array(256);
|
|
537
|
-
for (var i = 0; i < 256; ++i) {
|
|
538
|
-
var c = i, k = 9;
|
|
539
|
-
while (--k)
|
|
540
|
-
c = ((c & 1) && -306674912) ^ (c >>> 1);
|
|
541
|
-
t[i] = c;
|
|
542
|
-
}
|
|
543
|
-
return t;
|
|
544
|
-
})();
|
|
545
|
-
// CRC32
|
|
546
|
-
var crc = function () {
|
|
547
|
-
var c = -1;
|
|
548
|
-
return {
|
|
549
|
-
p: function (d) {
|
|
550
|
-
// closures have awful performance
|
|
551
|
-
var cr = c;
|
|
552
|
-
for (var i = 0; i < d.length; ++i)
|
|
553
|
-
cr = crct[(cr & 255) ^ d[i]] ^ (cr >>> 8);
|
|
554
|
-
c = cr;
|
|
555
|
-
},
|
|
556
|
-
d: function () { return ~c; }
|
|
557
|
-
};
|
|
558
|
-
};
|
|
559
|
-
// deflate with opts
|
|
560
|
-
var dopt = function (dat, opt, pre, post, st) {
|
|
561
|
-
if (!st) {
|
|
562
|
-
st = { l: 1 };
|
|
563
|
-
if (opt.dictionary) {
|
|
564
|
-
var dict = opt.dictionary.subarray(-32768);
|
|
565
|
-
var newDat = new u8(dict.length + dat.length);
|
|
566
|
-
newDat.set(dict);
|
|
567
|
-
newDat.set(dat, dict.length);
|
|
568
|
-
dat = newDat;
|
|
569
|
-
st.w = dict.length;
|
|
570
|
-
}
|
|
571
|
-
}
|
|
572
|
-
return dflt(dat, opt.level == null ? 6 : opt.level, opt.mem == null ? (st.l ? Math.ceil(Math.max(8, Math.min(13, Math.log(dat.length))) * 1.5) : 20) : (12 + opt.mem), pre, post, st);
|
|
573
|
-
};
|
|
574
|
-
// Walmart object spread
|
|
575
|
-
var mrg = function (a, b) {
|
|
576
|
-
var o = {};
|
|
577
|
-
for (var k in a)
|
|
578
|
-
o[k] = a[k];
|
|
579
|
-
for (var k in b)
|
|
580
|
-
o[k] = b[k];
|
|
581
|
-
return o;
|
|
582
|
-
};
|
|
583
|
-
// worker clone
|
|
584
|
-
// This is possibly the craziest part of the entire codebase, despite how simple it may seem.
|
|
585
|
-
// The only parameter to this function is a closure that returns an array of variables outside of the function scope.
|
|
586
|
-
// We're going to try to figure out the variable names used in the closure as strings because that is crucial for workerization.
|
|
587
|
-
// We will return an object mapping of true variable name to value (basically, the current scope as a JS object).
|
|
588
|
-
// The reason we can't just use the original variable names is minifiers mangling the toplevel scope.
|
|
589
|
-
// This took me three weeks to figure out how to do.
|
|
590
|
-
var wcln = function (fn, fnStr, td) {
|
|
591
|
-
var dt = fn();
|
|
592
|
-
var st = fn.toString();
|
|
593
|
-
var ks = st.slice(st.indexOf('[') + 1, st.lastIndexOf(']')).replace(/\s+/g, '').split(',');
|
|
594
|
-
for (var i = 0; i < dt.length; ++i) {
|
|
595
|
-
var v = dt[i], k = ks[i];
|
|
596
|
-
if (typeof v == 'function') {
|
|
597
|
-
fnStr += ';' + k + '=';
|
|
598
|
-
var st_1 = v.toString();
|
|
599
|
-
if (v.prototype) {
|
|
600
|
-
// for global objects
|
|
601
|
-
if (st_1.indexOf('[native code]') != -1) {
|
|
602
|
-
var spInd = st_1.indexOf(' ', 8) + 1;
|
|
603
|
-
fnStr += st_1.slice(spInd, st_1.indexOf('(', spInd));
|
|
604
|
-
}
|
|
605
|
-
else {
|
|
606
|
-
fnStr += st_1;
|
|
607
|
-
for (var t in v.prototype)
|
|
608
|
-
fnStr += ';' + k + '.prototype.' + t + '=' + v.prototype[t].toString();
|
|
609
|
-
}
|
|
610
|
-
}
|
|
611
|
-
else
|
|
612
|
-
fnStr += st_1;
|
|
613
|
-
}
|
|
614
|
-
else
|
|
615
|
-
td[k] = v;
|
|
616
|
-
}
|
|
617
|
-
return fnStr;
|
|
618
|
-
};
|
|
619
|
-
var ch = [];
|
|
620
|
-
// clone bufs
|
|
621
|
-
var cbfs = function (v) {
|
|
622
|
-
var tl = [];
|
|
623
|
-
for (var k in v) {
|
|
624
|
-
if (v[k].buffer) {
|
|
625
|
-
tl.push((v[k] = new v[k].constructor(v[k])).buffer);
|
|
626
|
-
}
|
|
627
|
-
}
|
|
628
|
-
return tl;
|
|
629
|
-
};
|
|
630
|
-
// use a worker to execute code
|
|
631
|
-
var wrkr = function (fns, init, id, cb) {
|
|
632
|
-
if (!ch[id]) {
|
|
633
|
-
var fnStr = '', td_1 = {}, m = fns.length - 1;
|
|
634
|
-
for (var i = 0; i < m; ++i)
|
|
635
|
-
fnStr = wcln(fns[i], fnStr, td_1);
|
|
636
|
-
ch[id] = { c: wcln(fns[m], fnStr, td_1), e: td_1 };
|
|
637
|
-
}
|
|
638
|
-
var td = mrg({}, ch[id].e);
|
|
639
|
-
return wk(ch[id].c + ';onmessage=function(e){for(var k in e.data)self[k]=e.data[k];onmessage=' + init.toString() + '}', id, td, cbfs(td), cb);
|
|
640
|
-
};
|
|
641
|
-
var bDflt = function () { return [u8, u16, i32, fleb, fdeb, clim, revfl, revfd, flm, flt, fdm, fdt, rev, deo, et, hMap, wbits, wbits16, hTree, ln, lc, clen, wfblk, wblk, shft, slc, dflt, dopt, deflateSync, pbf]; };
|
|
642
|
-
// gzip extra
|
|
643
|
-
var gze = function () { return [gzh, gzhl, wbytes, crc, crct]; };
|
|
644
|
-
// post buf
|
|
645
|
-
var pbf = function (msg) { return postMessage(msg, [msg.buffer]); };
|
|
646
|
-
// async helper
|
|
647
|
-
var cbify = function (dat, opts, fns, init, id, cb) {
|
|
648
|
-
var w = wrkr(fns, init, id, function (err, dat) {
|
|
649
|
-
w.terminate();
|
|
650
|
-
cb(err, dat);
|
|
651
|
-
});
|
|
652
|
-
w.postMessage([dat, opts], opts.consume ? [dat.buffer] : []);
|
|
653
|
-
return function () { w.terminate(); };
|
|
654
|
-
};
|
|
655
|
-
// write bytes
|
|
656
|
-
var wbytes = function (d, b, v) {
|
|
657
|
-
for (; v; ++b)
|
|
658
|
-
d[b] = v, v >>>= 8;
|
|
659
|
-
};
|
|
660
|
-
// gzip header
|
|
661
|
-
var gzh = function (c, o) {
|
|
662
|
-
var fn = o.filename;
|
|
663
|
-
c[0] = 31, c[1] = 139, c[2] = 8, c[8] = o.level < 2 ? 4 : o.level == 9 ? 2 : 0, c[9] = 3; // assume Unix
|
|
664
|
-
if (o.mtime != 0)
|
|
665
|
-
wbytes(c, 4, Math.floor(new Date(o.mtime || Date.now()) / 1000));
|
|
666
|
-
if (fn) {
|
|
667
|
-
c[3] = 8;
|
|
668
|
-
for (var i = 0; i <= fn.length; ++i)
|
|
669
|
-
c[i + 10] = fn.charCodeAt(i);
|
|
670
|
-
}
|
|
671
|
-
};
|
|
672
|
-
// gzip header length
|
|
673
|
-
var gzhl = function (o) { return 10 + (o.filename ? o.filename.length + 1 : 0); };
|
|
674
|
-
/**
|
|
675
|
-
* Compresses data with DEFLATE without any wrapper
|
|
676
|
-
* @param data The data to compress
|
|
677
|
-
* @param opts The compression options
|
|
678
|
-
* @returns The deflated version of the data
|
|
679
|
-
*/
|
|
680
|
-
function deflateSync(data, opts) {
|
|
681
|
-
return dopt(data, opts || {}, 0, 0);
|
|
682
|
-
}
|
|
683
|
-
function gzip(data, opts, cb) {
|
|
684
|
-
if (!cb)
|
|
685
|
-
cb = opts, opts = {};
|
|
686
|
-
if (typeof cb != 'function')
|
|
687
|
-
err(7);
|
|
688
|
-
return cbify(data, opts, [
|
|
689
|
-
bDflt,
|
|
690
|
-
gze,
|
|
691
|
-
function () { return [gzipSync]; }
|
|
692
|
-
], function (ev) { return pbf(gzipSync(ev.data[0], ev.data[1])); }, 2, cb);
|
|
693
|
-
}
|
|
694
|
-
/**
|
|
695
|
-
* Compresses data with GZIP
|
|
696
|
-
* @param data The data to compress
|
|
697
|
-
* @param opts The compression options
|
|
698
|
-
* @returns The gzipped version of the data
|
|
699
|
-
*/
|
|
700
|
-
function gzipSync(data, opts) {
|
|
701
|
-
if (!opts)
|
|
702
|
-
opts = {};
|
|
703
|
-
var c = crc(), l = data.length;
|
|
704
|
-
c.p(data);
|
|
705
|
-
var d = dopt(data, opts, gzhl(opts), 8), s = d.length;
|
|
706
|
-
return gzh(d, opts), wbytes(d, s - 8, c.d()), wbytes(d, s - 4, l), d;
|
|
707
|
-
}
|
|
708
|
-
// text decoder
|
|
709
|
-
var td = typeof TextDecoder != 'undefined' && /*#__PURE__*/ new TextDecoder();
|
|
710
|
-
// text decoder stream
|
|
711
|
-
var tds = 0;
|
|
712
|
-
try {
|
|
713
|
-
td.decode(et, { stream: true });
|
|
714
|
-
tds = 1;
|
|
715
|
-
}
|
|
716
|
-
catch (e) { }
|
|
717
|
-
|
|
718
5
|
class StringDictionary {
|
|
719
6
|
constructor() {
|
|
720
7
|
this.lastTs = 0;
|
|
@@ -1165,6 +452,23 @@ function deprecationWarn(nameOfFeature, useInstead, docsPath = '/') {
|
|
|
1165
452
|
console.warn(`OxvoSessions: ${nameOfFeature} is deprecated. ${useInstead ? `Please, use ${useInstead} instead.` : ''} Visit ${DOCS_HOST}${docsPath} for more information.`);
|
|
1166
453
|
warnedFeatures[nameOfFeature] = true;
|
|
1167
454
|
}
|
|
455
|
+
function getCustomAttributeLabel(e, customAttributes) {
|
|
456
|
+
if (!customAttributes || customAttributes.length === 0)
|
|
457
|
+
return '';
|
|
458
|
+
const parts = [];
|
|
459
|
+
for (const attr of customAttributes) {
|
|
460
|
+
const value = e.getAttribute(attr);
|
|
461
|
+
if (value !== null) {
|
|
462
|
+
parts.push(`[${attr}=${value}]`);
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
return parts.join('');
|
|
466
|
+
}
|
|
467
|
+
function getClassSelector(e) {
|
|
468
|
+
if (!e.classList || e.classList.length === 0)
|
|
469
|
+
return '';
|
|
470
|
+
return '.' + Array.from(e.classList).join('.');
|
|
471
|
+
}
|
|
1168
472
|
function getLabelAttribute(e) {
|
|
1169
473
|
let value = e.getAttribute('data-oxvosessions-label');
|
|
1170
474
|
if (value !== null) {
|
|
@@ -1921,13 +1225,6 @@ function BatchMetadata(version, pageNo, firstIndex, timestamp, location) {
|
|
|
1921
1225
|
location,
|
|
1922
1226
|
];
|
|
1923
1227
|
}
|
|
1924
|
-
function PartitionedMessage(partNo, partTotal) {
|
|
1925
|
-
return [
|
|
1926
|
-
82 /* Messages.Type.PartitionedMessage */,
|
|
1927
|
-
partNo,
|
|
1928
|
-
partTotal,
|
|
1929
|
-
];
|
|
1930
|
-
}
|
|
1931
1228
|
function NetworkRequest(type, method, url, request, response, status, timestamp, duration, transferredBodySize) {
|
|
1932
1229
|
return [
|
|
1933
1230
|
83 /* Messages.Type.NetworkRequest */,
|
|
@@ -2139,7 +1436,6 @@ var _Messages = /*#__PURE__*/Object.freeze({
|
|
|
2139
1436
|
OTable: OTable,
|
|
2140
1437
|
PageLoadTiming: PageLoadTiming,
|
|
2141
1438
|
PageRenderTiming: PageRenderTiming,
|
|
2142
|
-
PartitionedMessage: PartitionedMessage,
|
|
2143
1439
|
PerformanceTrack: PerformanceTrack,
|
|
2144
1440
|
Profiler: Profiler,
|
|
2145
1441
|
Redux: Redux,
|
|
@@ -2234,11 +1530,157 @@ function Performance (app, opts) {
|
|
|
2234
1530
|
}
|
|
2235
1531
|
}
|
|
2236
1532
|
|
|
1533
|
+
/**
|
|
1534
|
+
* Two-tier tag matching:
|
|
1535
|
+
* 1. Fast fingerprint lookup by id, data-attr,
|
|
1536
|
+
* or class from the selector's last segment
|
|
1537
|
+
* 2. Fallback iteration using native element.matches()
|
|
1538
|
+
*/
|
|
1539
|
+
class TagMatcher {
|
|
1540
|
+
constructor() {
|
|
1541
|
+
this.tags = [];
|
|
1542
|
+
this.byId = new Map();
|
|
1543
|
+
this.byDataAttr = new Map();
|
|
1544
|
+
this.byClass = new Map();
|
|
1545
|
+
this.fallback = [];
|
|
1546
|
+
}
|
|
1547
|
+
setTags(tags) {
|
|
1548
|
+
this.tags = tags;
|
|
1549
|
+
this.byId.clear();
|
|
1550
|
+
this.byDataAttr.clear();
|
|
1551
|
+
this.byClass.clear();
|
|
1552
|
+
this.fallback = [];
|
|
1553
|
+
for (const tag of tags) {
|
|
1554
|
+
const last = lastSegment(tag.selector);
|
|
1555
|
+
if (!last) {
|
|
1556
|
+
this.fallback.push(tag);
|
|
1557
|
+
continue;
|
|
1558
|
+
}
|
|
1559
|
+
if (last.startsWith('#')) {
|
|
1560
|
+
this.byId.set(last.slice(1), tag);
|
|
1561
|
+
}
|
|
1562
|
+
else if (last.startsWith('[data-')) {
|
|
1563
|
+
this.byDataAttr.set(last, tag);
|
|
1564
|
+
}
|
|
1565
|
+
else {
|
|
1566
|
+
const cls = extractClass(last);
|
|
1567
|
+
if (cls) {
|
|
1568
|
+
this.byClass.set(cls, tag);
|
|
1569
|
+
}
|
|
1570
|
+
else {
|
|
1571
|
+
this.fallback.push(tag);
|
|
1572
|
+
}
|
|
1573
|
+
}
|
|
1574
|
+
}
|
|
1575
|
+
}
|
|
1576
|
+
getTags() {
|
|
1577
|
+
return this.tags;
|
|
1578
|
+
}
|
|
1579
|
+
/** Match element, its parent, or direct children against known tag selectors */
|
|
1580
|
+
match(el) {
|
|
1581
|
+
const direct = this.matchExact(el);
|
|
1582
|
+
if (direct)
|
|
1583
|
+
return direct;
|
|
1584
|
+
if (el.parentElement) {
|
|
1585
|
+
const parent = this.matchExact(el.parentElement);
|
|
1586
|
+
if (parent)
|
|
1587
|
+
return parent;
|
|
1588
|
+
}
|
|
1589
|
+
const children = el.children;
|
|
1590
|
+
for (let i = 0; i < children.length; i++) {
|
|
1591
|
+
const child = this.matchExact(children[i]);
|
|
1592
|
+
if (child)
|
|
1593
|
+
return child;
|
|
1594
|
+
}
|
|
1595
|
+
return null;
|
|
1596
|
+
}
|
|
1597
|
+
matchExact(el) {
|
|
1598
|
+
if (el.id && this.byId.has(el.id)) {
|
|
1599
|
+
const tag = this.byId.get(el.id);
|
|
1600
|
+
if (safeMatches(el, tag.selector) && matchesLocation(tag))
|
|
1601
|
+
return tag;
|
|
1602
|
+
}
|
|
1603
|
+
if (this.byDataAttr.size > 0) {
|
|
1604
|
+
const attrs = el.attributes;
|
|
1605
|
+
for (let i = 0; i < attrs.length; i++) {
|
|
1606
|
+
const attr = attrs[i];
|
|
1607
|
+
if (attr.name.startsWith('data-')) {
|
|
1608
|
+
const key = `[${attr.name}="${attr.value}"]`;
|
|
1609
|
+
if (this.byDataAttr.has(key)) {
|
|
1610
|
+
const tag = this.byDataAttr.get(key);
|
|
1611
|
+
if (safeMatches(el, tag.selector) && matchesLocation(tag))
|
|
1612
|
+
return tag;
|
|
1613
|
+
}
|
|
1614
|
+
}
|
|
1615
|
+
}
|
|
1616
|
+
}
|
|
1617
|
+
if (this.byClass.size > 0 && el.classList) {
|
|
1618
|
+
for (let i = 0; i < el.classList.length; i++) {
|
|
1619
|
+
const cls = el.classList[i];
|
|
1620
|
+
if (this.byClass.has(cls)) {
|
|
1621
|
+
const tag = this.byClass.get(cls);
|
|
1622
|
+
if (safeMatches(el, tag.selector) && matchesLocation(tag))
|
|
1623
|
+
return tag;
|
|
1624
|
+
}
|
|
1625
|
+
}
|
|
1626
|
+
}
|
|
1627
|
+
for (const tag of this.fallback) {
|
|
1628
|
+
if (safeMatches(el, tag.selector) && matchesLocation(tag))
|
|
1629
|
+
return tag;
|
|
1630
|
+
}
|
|
1631
|
+
return null;
|
|
1632
|
+
}
|
|
1633
|
+
clear() {
|
|
1634
|
+
this.tags = [];
|
|
1635
|
+
this.byId.clear();
|
|
1636
|
+
this.byDataAttr.clear();
|
|
1637
|
+
this.byClass.clear();
|
|
1638
|
+
this.fallback = [];
|
|
1639
|
+
}
|
|
1640
|
+
}
|
|
1641
|
+
/** Last combinator-separated segment of a CSS selector */
|
|
1642
|
+
function lastSegment(selector) {
|
|
1643
|
+
const trimmed = selector.trim();
|
|
1644
|
+
if (!trimmed)
|
|
1645
|
+
return null;
|
|
1646
|
+
const parts = trimmed.split(/\s*[>+~ ]\s*/);
|
|
1647
|
+
const last = parts[parts.length - 1]?.trim();
|
|
1648
|
+
return last || null;
|
|
1649
|
+
}
|
|
1650
|
+
/** First class name from a selector segment, e.g. "div.my-class" -> "my-class" */
|
|
1651
|
+
function extractClass(segment) {
|
|
1652
|
+
const match = segment.match(/\.([a-zA-Z_-][\w-]*)/);
|
|
1653
|
+
return match ? match[1] : null;
|
|
1654
|
+
}
|
|
1655
|
+
function safeMatches(el, selector) {
|
|
1656
|
+
try {
|
|
1657
|
+
return el.matches(selector);
|
|
1658
|
+
}
|
|
1659
|
+
catch {
|
|
1660
|
+
return false;
|
|
1661
|
+
}
|
|
1662
|
+
}
|
|
1663
|
+
function matchesLocation(tag) {
|
|
1664
|
+
if (!tag.location)
|
|
1665
|
+
return true;
|
|
1666
|
+
try {
|
|
1667
|
+
const loc = tag.location;
|
|
1668
|
+
if (loc.startsWith('/')) {
|
|
1669
|
+
return window.location.pathname === loc;
|
|
1670
|
+
}
|
|
1671
|
+
return window.location.href === loc;
|
|
1672
|
+
}
|
|
1673
|
+
catch {
|
|
1674
|
+
return true;
|
|
1675
|
+
}
|
|
1676
|
+
}
|
|
1677
|
+
|
|
2237
1678
|
const WATCHED_MARKERS_KEY = '__or__watched_markers__';
|
|
2238
1679
|
class MarkerWatcher {
|
|
2239
1680
|
constructor(params) {
|
|
2240
1681
|
this.interval = null;
|
|
2241
1682
|
this.tags = [];
|
|
1683
|
+
this.matcher = new TagMatcher();
|
|
2242
1684
|
this.sessionStorage = params.sessionStorage;
|
|
2243
1685
|
this.errLog = params.errLog;
|
|
2244
1686
|
this.onMarkerHit = params.onMarkerHit;
|
|
@@ -2281,12 +1723,15 @@ class MarkerWatcher {
|
|
|
2281
1723
|
}
|
|
2282
1724
|
setTags(tags) {
|
|
2283
1725
|
this.tags = tags;
|
|
1726
|
+
this.matcher.setTags(tags);
|
|
2284
1727
|
if (this.interval) {
|
|
2285
1728
|
clearInterval(this.interval);
|
|
2286
1729
|
this.interval = null;
|
|
2287
1730
|
}
|
|
2288
1731
|
this.interval = setInterval(() => {
|
|
2289
1732
|
this.tags.forEach((tag) => {
|
|
1733
|
+
if (!matchesLocation(tag))
|
|
1734
|
+
return;
|
|
2290
1735
|
const possibleEls = document.querySelectorAll(tag.selector);
|
|
2291
1736
|
if (possibleEls.length > 0) {
|
|
2292
1737
|
const el = possibleEls[0];
|
|
@@ -2298,13 +1743,17 @@ class MarkerWatcher {
|
|
|
2298
1743
|
}, 500);
|
|
2299
1744
|
}
|
|
2300
1745
|
onMarkerRendered(markerId) {
|
|
2301
|
-
|
|
2302
|
-
|
|
1746
|
+
const tag = this.tags.find((t) => t.id === markerId);
|
|
1747
|
+
if (tag) {
|
|
1748
|
+
this.tags = this.tags.filter((t) => t.id !== markerId);
|
|
1749
|
+
if (!matchesLocation(tag))
|
|
1750
|
+
return;
|
|
2303
1751
|
}
|
|
2304
1752
|
this.onMarkerHit(markerId);
|
|
2305
1753
|
}
|
|
2306
1754
|
clear() {
|
|
2307
1755
|
this.tags = [];
|
|
1756
|
+
this.matcher.clear();
|
|
2308
1757
|
if (this.interval) {
|
|
2309
1758
|
clearInterval(this.interval);
|
|
2310
1759
|
this.interval = null;
|
|
@@ -2355,6 +1804,30 @@ class CanvasRecorder {
|
|
|
2355
1804
|
this.MAX_QUEUE_SIZE = 50; // ~500 images max (50 batches × 10 images)
|
|
2356
1805
|
this.pendingBatches = [];
|
|
2357
1806
|
this.isProcessingQueue = false;
|
|
1807
|
+
/**
|
|
1808
|
+
* Reacts to a runtime sanitization change on a canvas: stop capturing if it
|
|
1809
|
+
* just became masked, start if it just became visible. Already-sent frames
|
|
1810
|
+
* cannot be retracted, so escalation only stops future capture.
|
|
1811
|
+
*/
|
|
1812
|
+
this.resanitizeCanvas = (node, id) => {
|
|
1813
|
+
if (!hasTag(node, 'canvas')) {
|
|
1814
|
+
return;
|
|
1815
|
+
}
|
|
1816
|
+
const isIgnored = this.app.sanitizer.isObscured(id) || this.app.sanitizer.isHidden(id);
|
|
1817
|
+
if (isIgnored) {
|
|
1818
|
+
if (this.snapshots[id] || this.observers.has(id)) {
|
|
1819
|
+
const observer = this.observers.get(id);
|
|
1820
|
+
if (observer) {
|
|
1821
|
+
observer.disconnect();
|
|
1822
|
+
this.observers.delete(id);
|
|
1823
|
+
}
|
|
1824
|
+
this.cleanupCanvas(id);
|
|
1825
|
+
}
|
|
1826
|
+
}
|
|
1827
|
+
else if (!this.snapshots[id] && !this.observers.has(id)) {
|
|
1828
|
+
this.captureCanvas(node);
|
|
1829
|
+
}
|
|
1830
|
+
};
|
|
2358
1831
|
this.restartTracking = () => {
|
|
2359
1832
|
this.clear();
|
|
2360
1833
|
this.app.nodes.scanTree(this.captureCanvas);
|
|
@@ -2451,14 +1924,15 @@ class CanvasRecorder {
|
|
|
2451
1924
|
}, this.interval);
|
|
2452
1925
|
this.intervals.set(id, int);
|
|
2453
1926
|
};
|
|
2454
|
-
this.fileExt =
|
|
1927
|
+
this.fileExt = 'webp';
|
|
2455
1928
|
this.interval = 1000 / options.fps;
|
|
2456
1929
|
}
|
|
2457
1930
|
startTracking() {
|
|
2458
1931
|
setTimeout(() => {
|
|
2459
1932
|
this.app.nodes.scanTree(this.captureCanvas);
|
|
2460
1933
|
this.app.nodes.attachNodeCallback(this.captureCanvas);
|
|
2461
|
-
|
|
1934
|
+
this.app.attachResanitizeCallback(this.resanitizeCanvas);
|
|
1935
|
+
}, 125);
|
|
2462
1936
|
}
|
|
2463
1937
|
sendSnaps(images, canvasId, createdAt) {
|
|
2464
1938
|
if (Object.keys(this.snapshots).length === 0) {
|
|
@@ -2487,17 +1961,73 @@ class CanvasRecorder {
|
|
|
2487
1961
|
}
|
|
2488
1962
|
this.isProcessingQueue = false;
|
|
2489
1963
|
}
|
|
2490
|
-
uploadBatch(images, canvasId, createdAt) {
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
1964
|
+
async uploadBatch(images, canvasId, createdAt) {
|
|
1965
|
+
if (this.options.isDebug) {
|
|
1966
|
+
const packed = await packFrames(images);
|
|
1967
|
+
if (packed) {
|
|
1968
|
+
const fileName = `${createdAt}_${canvasId}.${this.fileExt}.frames`;
|
|
1969
|
+
const url = URL.createObjectURL(new Blob([packed]));
|
|
1970
|
+
const link = document.createElement('a');
|
|
1971
|
+
link.href = url;
|
|
1972
|
+
link.download = fileName;
|
|
1973
|
+
link.style.display = 'none';
|
|
1974
|
+
document.body.appendChild(link);
|
|
1975
|
+
link.click();
|
|
1976
|
+
document.body.removeChild(link);
|
|
1977
|
+
URL.revokeObjectURL(url);
|
|
1978
|
+
}
|
|
1979
|
+
// fall through to also send to backend
|
|
1980
|
+
}
|
|
1981
|
+
let formData;
|
|
1982
|
+
if (this.options.framesSupport) {
|
|
1983
|
+
// Pack frames into binary format: [uint64 LE timestamp][uint32 LE size][data] per frame
|
|
1984
|
+
const buffers = [];
|
|
1985
|
+
let totalSize = 0;
|
|
1986
|
+
for (const snapshot of images) {
|
|
1987
|
+
if (!snapshot.data)
|
|
1988
|
+
continue;
|
|
1989
|
+
const ab = await snapshot.data.arrayBuffer();
|
|
1990
|
+
buffers.push(ab);
|
|
1991
|
+
totalSize += 8 + 4 + ab.byteLength; // uint64 ts + uint32 size + data
|
|
2499
1992
|
}
|
|
2500
|
-
|
|
1993
|
+
if (totalSize === 0)
|
|
1994
|
+
return;
|
|
1995
|
+
const packed = new ArrayBuffer(totalSize);
|
|
1996
|
+
const view = new DataView(packed);
|
|
1997
|
+
const bytes = new Uint8Array(packed);
|
|
1998
|
+
let offset = 0;
|
|
1999
|
+
for (let i = 0; i < images.length; i++) {
|
|
2000
|
+
if (!images[i].data)
|
|
2001
|
+
continue;
|
|
2002
|
+
const ab = buffers.shift();
|
|
2003
|
+
const ts = images[i].id;
|
|
2004
|
+
// uint64 LE as two uint32 LE writes -- timestamp
|
|
2005
|
+
view.setUint32(offset, ts % 0x100000000, true);
|
|
2006
|
+
view.setUint32(offset + 4, Math.floor(ts / 0x100000000), true);
|
|
2007
|
+
offset += 8;
|
|
2008
|
+
// uint32 LE -- size
|
|
2009
|
+
view.setUint32(offset, ab.byteLength, true);
|
|
2010
|
+
offset += 4;
|
|
2011
|
+
// image data
|
|
2012
|
+
bytes.set(new Uint8Array(ab), offset);
|
|
2013
|
+
offset += ab.byteLength;
|
|
2014
|
+
}
|
|
2015
|
+
formData = new FormData();
|
|
2016
|
+
formData.append('type', 'frames');
|
|
2017
|
+
const fileName = `${createdAt}_${canvasId}.${this.fileExt}.frames`;
|
|
2018
|
+
formData.append('frames', new Blob([packed]), fileName);
|
|
2019
|
+
}
|
|
2020
|
+
else {
|
|
2021
|
+
// Legacy: send individual image files
|
|
2022
|
+
formData = new FormData();
|
|
2023
|
+
images.forEach((snapshot) => {
|
|
2024
|
+
const blob = snapshot.data;
|
|
2025
|
+
if (!blob)
|
|
2026
|
+
return;
|
|
2027
|
+
const name = `${createdAt}_${canvasId}_${snapshot.id}.${this.fileExt}`;
|
|
2028
|
+
formData.append('snapshot', blob, name);
|
|
2029
|
+
});
|
|
2030
|
+
}
|
|
2501
2031
|
const initRestart = () => {
|
|
2502
2032
|
this.app.debug.log('Restarting tracker; token expired');
|
|
2503
2033
|
this.app.stop(false);
|
|
@@ -2593,15 +2123,36 @@ function captureSnapshot(canvas, quality = 'medium', dummy, fixedScaling = false
|
|
|
2593
2123
|
canvas.toBlob(onBlob, imageFormat, qualityInt[quality]);
|
|
2594
2124
|
}
|
|
2595
2125
|
}
|
|
2596
|
-
function
|
|
2597
|
-
const
|
|
2598
|
-
|
|
2599
|
-
|
|
2600
|
-
|
|
2601
|
-
|
|
2602
|
-
|
|
2603
|
-
|
|
2604
|
-
|
|
2126
|
+
async function packFrames(images) {
|
|
2127
|
+
const buffers = [];
|
|
2128
|
+
let totalSize = 0;
|
|
2129
|
+
for (const snapshot of images) {
|
|
2130
|
+
if (!snapshot.data)
|
|
2131
|
+
continue;
|
|
2132
|
+
const ab = await snapshot.data.arrayBuffer();
|
|
2133
|
+
buffers.push(ab);
|
|
2134
|
+
totalSize += 8 + 4 + ab.byteLength;
|
|
2135
|
+
}
|
|
2136
|
+
if (totalSize === 0)
|
|
2137
|
+
return null;
|
|
2138
|
+
const packed = new ArrayBuffer(totalSize);
|
|
2139
|
+
const view = new DataView(packed);
|
|
2140
|
+
const bytes = new Uint8Array(packed);
|
|
2141
|
+
let offset = 0;
|
|
2142
|
+
for (let i = 0; i < images.length; i++) {
|
|
2143
|
+
if (!images[i].data)
|
|
2144
|
+
continue;
|
|
2145
|
+
const ab = buffers.shift();
|
|
2146
|
+
const ts = images[i].id;
|
|
2147
|
+
view.setUint32(offset, ts % 0x100000000, true);
|
|
2148
|
+
view.setUint32(offset + 4, Math.floor(ts / 0x100000000), true);
|
|
2149
|
+
offset += 8;
|
|
2150
|
+
view.setUint32(offset, ab.byteLength, true);
|
|
2151
|
+
offset += 4;
|
|
2152
|
+
bytes.set(new Uint8Array(ab), offset);
|
|
2153
|
+
offset += ab.byteLength;
|
|
2154
|
+
}
|
|
2155
|
+
return packed;
|
|
2605
2156
|
}
|
|
2606
2157
|
|
|
2607
2158
|
const LogLevel = {
|
|
@@ -2882,8 +2433,44 @@ function inlineRemoteCss(node, id, baseHref, getNextID, insertRule, addOwner, fo
|
|
|
2882
2433
|
})
|
|
2883
2434
|
.catch(error => {
|
|
2884
2435
|
console.error(`OxvoSessions: Failed to fetch CSS from ${node.href}:`, error);
|
|
2436
|
+
// Fetch failed, likely due to CORS. Retry via load event and sheet access.
|
|
2437
|
+
retryViaLoadEvent();
|
|
2885
2438
|
});
|
|
2886
2439
|
}
|
|
2440
|
+
else {
|
|
2441
|
+
retryViaLoadEvent();
|
|
2442
|
+
}
|
|
2443
|
+
function retryViaLoadEvent() {
|
|
2444
|
+
const trySheet = () => {
|
|
2445
|
+
const loadedSheet = node.sheet;
|
|
2446
|
+
if (loadedSheet) {
|
|
2447
|
+
try {
|
|
2448
|
+
const cssText = stringifyStylesheet(loadedSheet);
|
|
2449
|
+
if (cssText) {
|
|
2450
|
+
if (sendPlain && onPlain) {
|
|
2451
|
+
onPlain(cssText, fakeIdHolder++);
|
|
2452
|
+
}
|
|
2453
|
+
else {
|
|
2454
|
+
processCssText(cssText);
|
|
2455
|
+
}
|
|
2456
|
+
return;
|
|
2457
|
+
}
|
|
2458
|
+
}
|
|
2459
|
+
catch (e) {
|
|
2460
|
+
// Cross-origin sheet. Nothing more can be read safely here.
|
|
2461
|
+
}
|
|
2462
|
+
}
|
|
2463
|
+
};
|
|
2464
|
+
if (node.sheet) {
|
|
2465
|
+
trySheet();
|
|
2466
|
+
}
|
|
2467
|
+
else {
|
|
2468
|
+
node.addEventListener('load', function onLoad() {
|
|
2469
|
+
node.removeEventListener('load', onLoad);
|
|
2470
|
+
trySheet();
|
|
2471
|
+
});
|
|
2472
|
+
}
|
|
2473
|
+
}
|
|
2887
2474
|
function processCssText(cssText) {
|
|
2888
2475
|
// Remove comments
|
|
2889
2476
|
cssText = cssText.replace(/\/\*[\s\S]*?\*\//g, '');
|
|
@@ -3105,7 +2692,6 @@ function ConstructedStyleSheets (app) {
|
|
|
3105
2692
|
if (!hasAdoptedSS(document)) {
|
|
3106
2693
|
return;
|
|
3107
2694
|
}
|
|
3108
|
-
const styleSheetIDMap = new Map();
|
|
3109
2695
|
const adoptedStyleSheetsOwnings = new Map();
|
|
3110
2696
|
const sendAdoptedStyleSheetsUpdate = (root) => setTimeout(() => {
|
|
3111
2697
|
let nodeID = app.nodes.getID(root);
|
|
@@ -3216,7 +2802,120 @@ function ConstructedStyleSheets (app) {
|
|
|
3216
2802
|
if (hasAdoptedSS(node)) {
|
|
3217
2803
|
sendAdoptedStyleSheetsUpdate(node);
|
|
3218
2804
|
}
|
|
3219
|
-
});
|
|
2805
|
+
});
|
|
2806
|
+
}
|
|
2807
|
+
|
|
2808
|
+
exports.SanitizeLevel = void 0;
|
|
2809
|
+
(function (SanitizeLevel) {
|
|
2810
|
+
SanitizeLevel[SanitizeLevel["Plain"] = 0] = "Plain";
|
|
2811
|
+
SanitizeLevel[SanitizeLevel["Obscured"] = 1] = "Obscured";
|
|
2812
|
+
SanitizeLevel[SanitizeLevel["Hidden"] = 2] = "Hidden";
|
|
2813
|
+
})(exports.SanitizeLevel || (exports.SanitizeLevel = {}));
|
|
2814
|
+
const stringWiper = (input) => input
|
|
2815
|
+
.trim()
|
|
2816
|
+
.replace(/[^\f\n\r\t\v\u00a0\u1680\u2000-\u200a\u2028\u2029\u202f\u205f\u3000\ufeff\s]/g, '*');
|
|
2817
|
+
class Sanitizer {
|
|
2818
|
+
constructor(params) {
|
|
2819
|
+
// Node id -> level. Plain (0) is never stored; a missing entry means Plain.
|
|
2820
|
+
// A map (not the old grow-only Sets) so levels can be raised and lowered.
|
|
2821
|
+
this.levels = new Map();
|
|
2822
|
+
this.app = params.app;
|
|
2823
|
+
const defaultOptions = {
|
|
2824
|
+
maskTextEmails: true,
|
|
2825
|
+
maskTextNumbers: false,
|
|
2826
|
+
maskAllByDefault: false,
|
|
2827
|
+
domSanitizer: undefined,
|
|
2828
|
+
};
|
|
2829
|
+
this.maskAllByDefault = params.options?.maskAllByDefault ?? false;
|
|
2830
|
+
this.options = Object.assign(defaultOptions, params.options);
|
|
2831
|
+
}
|
|
2832
|
+
// Pure recomputation of a node's level from the live DOM + parent level.
|
|
2833
|
+
// Reading current state on every call lets resanitize() pick up runtime changes.
|
|
2834
|
+
computeLevel(node, parentLevel) {
|
|
2835
|
+
if (this.options.maskAllByDefault) {
|
|
2836
|
+
if (isElementNode(node) && !hasOxvoSessionsAttribute(node, 'unmask')) {
|
|
2837
|
+
return exports.SanitizeLevel.Obscured;
|
|
2838
|
+
}
|
|
2839
|
+
if (isTextNode(node) && !hasOxvoSessionsAttribute(node.parentNode, 'unmask')) {
|
|
2840
|
+
return exports.SanitizeLevel.Obscured;
|
|
2841
|
+
}
|
|
2842
|
+
}
|
|
2843
|
+
let level = exports.SanitizeLevel.Plain;
|
|
2844
|
+
if (parentLevel >= exports.SanitizeLevel.Obscured ||
|
|
2845
|
+
(isElementNode(node) &&
|
|
2846
|
+
(hasOxvoSessionsAttribute(node, 'masked') || hasOxvoSessionsAttribute(node, 'obscured')))) {
|
|
2847
|
+
level = exports.SanitizeLevel.Obscured;
|
|
2848
|
+
}
|
|
2849
|
+
if (parentLevel === exports.SanitizeLevel.Hidden ||
|
|
2850
|
+
(isElementNode(node) &&
|
|
2851
|
+
(hasOxvoSessionsAttribute(node, 'htmlmasked') || hasOxvoSessionsAttribute(node, 'hidden')))) {
|
|
2852
|
+
level = exports.SanitizeLevel.Hidden;
|
|
2853
|
+
}
|
|
2854
|
+
if (this.options.domSanitizer !== undefined && isElementNode(node)) {
|
|
2855
|
+
const sanitizeLevel = this.options.domSanitizer(node);
|
|
2856
|
+
if (sanitizeLevel === exports.SanitizeLevel.Obscured && level < exports.SanitizeLevel.Obscured) {
|
|
2857
|
+
level = exports.SanitizeLevel.Obscured;
|
|
2858
|
+
}
|
|
2859
|
+
if (sanitizeLevel === exports.SanitizeLevel.Hidden) {
|
|
2860
|
+
level = exports.SanitizeLevel.Hidden;
|
|
2861
|
+
}
|
|
2862
|
+
}
|
|
2863
|
+
return level;
|
|
2864
|
+
}
|
|
2865
|
+
getLevel(id) {
|
|
2866
|
+
return this.levels.get(id) ?? exports.SanitizeLevel.Plain;
|
|
2867
|
+
}
|
|
2868
|
+
// Sets a node's level in either direction and returns the previous level.
|
|
2869
|
+
setLevel(id, level) {
|
|
2870
|
+
const prev = this.getLevel(id);
|
|
2871
|
+
if (level === exports.SanitizeLevel.Plain) {
|
|
2872
|
+
this.levels.delete(id);
|
|
2873
|
+
}
|
|
2874
|
+
else {
|
|
2875
|
+
this.levels.set(id, level);
|
|
2876
|
+
}
|
|
2877
|
+
return prev;
|
|
2878
|
+
}
|
|
2879
|
+
handleNode(id, parentID, node) {
|
|
2880
|
+
const level = this.computeLevel(node, this.getLevel(parentID));
|
|
2881
|
+
// Escalate-only: commits never lower a level, only resanitize/setLevel do.
|
|
2882
|
+
if (level > this.getLevel(id)) {
|
|
2883
|
+
this.setLevel(id, level);
|
|
2884
|
+
}
|
|
2885
|
+
}
|
|
2886
|
+
sanitize(id, data) {
|
|
2887
|
+
if (this.getLevel(id) >= exports.SanitizeLevel.Obscured) {
|
|
2888
|
+
// TODO: is it the best place to put trim() ? Might trimmed spaces be considered in layout in certain cases?
|
|
2889
|
+
return stringWiper(data);
|
|
2890
|
+
}
|
|
2891
|
+
if (this.options.maskTextNumbers) {
|
|
2892
|
+
data = data.replace(/\d/g, '0');
|
|
2893
|
+
}
|
|
2894
|
+
if (this.options.maskTextEmails) {
|
|
2895
|
+
data = data.replace(/^\w+([+.-]\w+)*@\w+([.-]\w+)*\.\w{2,3}$/g, (email) => {
|
|
2896
|
+
const [name, domain] = email.split('@');
|
|
2897
|
+
const [domainName, host] = domain.split('.');
|
|
2898
|
+
return `${stars(name)}@${stars(domainName)}.${stars(host)}`;
|
|
2899
|
+
});
|
|
2900
|
+
}
|
|
2901
|
+
return data;
|
|
2902
|
+
}
|
|
2903
|
+
isObscured(id) {
|
|
2904
|
+
return this.getLevel(id) >= exports.SanitizeLevel.Obscured;
|
|
2905
|
+
}
|
|
2906
|
+
isHidden(id) {
|
|
2907
|
+
return this.getLevel(id) === exports.SanitizeLevel.Hidden;
|
|
2908
|
+
}
|
|
2909
|
+
getInnerTextSecure(el) {
|
|
2910
|
+
const id = this.app.nodes.getID(el);
|
|
2911
|
+
if (!id) {
|
|
2912
|
+
return '';
|
|
2913
|
+
}
|
|
2914
|
+
return this.sanitize(id, el.innerText);
|
|
2915
|
+
}
|
|
2916
|
+
clear() {
|
|
2917
|
+
this.levels.clear();
|
|
2918
|
+
}
|
|
3220
2919
|
}
|
|
3221
2920
|
|
|
3222
2921
|
const iconCache = {};
|
|
@@ -3370,6 +3069,12 @@ class Observer {
|
|
|
3370
3069
|
this.inlineRemoteCss = false;
|
|
3371
3070
|
this.inlinerOptions = undefined;
|
|
3372
3071
|
this.domParser = new DOMParser();
|
|
3072
|
+
/**
|
|
3073
|
+
* Bumped on every disconnect(). Stale async CSS-inlining callbacks from a
|
|
3074
|
+
* previous session (e.g. agent reload → tracker restart) compare against
|
|
3075
|
+
* this and bail instead of sending messages that reference vanished node ids.
|
|
3076
|
+
*/
|
|
3077
|
+
this.generation = 0;
|
|
3373
3078
|
this.throttling = true;
|
|
3374
3079
|
this.throttledSetNodeData = throttleWithTrailing((id, parentElement, data) => this.sendNodeData(id, parentElement, data), 30);
|
|
3375
3080
|
this.throttling = !Boolean(options.disableThrottling);
|
|
@@ -3516,18 +3221,23 @@ class Observer {
|
|
|
3516
3221
|
}
|
|
3517
3222
|
if (name === 'style' || (name === 'href' && hasTag(node, 'link'))) {
|
|
3518
3223
|
if ('rel' in node && node.rel === 'stylesheet' && this.inlineRemoteCss) {
|
|
3224
|
+
const gen = this.generation;
|
|
3519
3225
|
setTimeout(() => {
|
|
3520
3226
|
inlineRemoteCss(
|
|
3521
3227
|
// @ts-ignore
|
|
3522
3228
|
node, id, this.app.getBaseHref(), nextID, (id, cssText, index, baseHref) => {
|
|
3229
|
+
if (this.generation !== gen)
|
|
3230
|
+
return;
|
|
3523
3231
|
this.app.send(AdoptedSSInsertRuleURLBased(id, cssText, index, baseHref));
|
|
3524
3232
|
}, (sheetId, ownerId) => {
|
|
3233
|
+
if (this.generation !== gen)
|
|
3234
|
+
return;
|
|
3525
3235
|
this.app.send(AdoptedSSAddOwner(sheetId, ownerId));
|
|
3526
3236
|
}, this.inlinerOptions?.forceFetch, this.inlinerOptions?.forcePlain, (cssText, fakeTextId) => {
|
|
3237
|
+
if (this.generation !== gen)
|
|
3238
|
+
return;
|
|
3527
3239
|
this.app.send(CreateTextNode(fakeTextId, id, 0));
|
|
3528
|
-
|
|
3529
|
-
this.app.send(SetCSSDataURLBased(fakeTextId, cssText, this.app.getBaseHref()));
|
|
3530
|
-
}, 10);
|
|
3240
|
+
this.app.send(SetCSSDataURLBased(fakeTextId, cssText, this.app.getBaseHref()));
|
|
3531
3241
|
});
|
|
3532
3242
|
}, 0);
|
|
3533
3243
|
return;
|
|
@@ -3787,10 +3497,105 @@ class Observer {
|
|
|
3787
3497
|
beforeCommit(this.app.nodes.getID(node));
|
|
3788
3498
|
this.commitNodes(true);
|
|
3789
3499
|
}
|
|
3500
|
+
/**
|
|
3501
|
+
* Re-evaluates sanitization for every tracked node in `root`'s subtree against
|
|
3502
|
+
* the current DOM and re-emits whatever changed. Pass the highest node you
|
|
3503
|
+
* changed (or the document root) so inherited levels propagate correctly.
|
|
3504
|
+
*/
|
|
3505
|
+
resanitizeSubtree(root) {
|
|
3506
|
+
if (!isObservable(root)) {
|
|
3507
|
+
return;
|
|
3508
|
+
}
|
|
3509
|
+
const parent = root.parentNode;
|
|
3510
|
+
const parentId = parent !== null ? this.app.nodes.getID(parent) : undefined;
|
|
3511
|
+
const parentLevel = parentId !== undefined ? this.app.sanitizer.getLevel(parentId) : exports.SanitizeLevel.Plain;
|
|
3512
|
+
this.resanitizeNode(root, parentLevel);
|
|
3513
|
+
}
|
|
3514
|
+
resanitizeNode(node, parentLevel) {
|
|
3515
|
+
if (isIgnored(node)) {
|
|
3516
|
+
return;
|
|
3517
|
+
}
|
|
3518
|
+
const id = this.app.nodes.getID(node);
|
|
3519
|
+
if (id === undefined) {
|
|
3520
|
+
// Untracked (new, or under a hidden ancestor): the live observer handles it.
|
|
3521
|
+
return;
|
|
3522
|
+
}
|
|
3523
|
+
const newLevel = this.app.sanitizer.computeLevel(node, parentLevel);
|
|
3524
|
+
const prevLevel = this.app.sanitizer.getLevel(id);
|
|
3525
|
+
const wasHidden = prevLevel === exports.SanitizeLevel.Hidden;
|
|
3526
|
+
const willHidden = newLevel === exports.SanitizeLevel.Hidden;
|
|
3527
|
+
// Crossing the hidden boundary changes the rendered structure (placeholder vs
|
|
3528
|
+
// real subtree), so rebuild rather than re-emit.
|
|
3529
|
+
if (wasHidden !== willHidden) {
|
|
3530
|
+
this.recreateSubtree(node);
|
|
3531
|
+
return;
|
|
3532
|
+
}
|
|
3533
|
+
if (willHidden) {
|
|
3534
|
+
return;
|
|
3535
|
+
}
|
|
3536
|
+
// Plain <-> Obscured: same structure, only leaf content changes.
|
|
3537
|
+
if (prevLevel !== newLevel) {
|
|
3538
|
+
this.app.sanitizer.setLevel(id, newLevel);
|
|
3539
|
+
this.reemitNode(id, node);
|
|
3540
|
+
}
|
|
3541
|
+
for (let child = node.firstChild; child !== null; child = child.nextSibling) {
|
|
3542
|
+
this.resanitizeNode(child, newLevel);
|
|
3543
|
+
}
|
|
3544
|
+
}
|
|
3545
|
+
// Destroys the node player-side and re-emits its subtree from scratch (new ids)
|
|
3546
|
+
// so it materializes at the freshly-computed level.
|
|
3547
|
+
recreateSubtree(node) {
|
|
3548
|
+
const id = this.app.nodes.getID(node);
|
|
3549
|
+
if (id === undefined) {
|
|
3550
|
+
return;
|
|
3551
|
+
}
|
|
3552
|
+
this.app.send(RemoveNode(id));
|
|
3553
|
+
this.clearSubtreeRegistration(node);
|
|
3554
|
+
this.bindTree(node);
|
|
3555
|
+
this.commitNodes();
|
|
3556
|
+
}
|
|
3557
|
+
clearSubtreeRegistration(node) {
|
|
3558
|
+
const clearOne = (n) => {
|
|
3559
|
+
const oldId = this.app.nodes.getID(n);
|
|
3560
|
+
if (oldId !== undefined) {
|
|
3561
|
+
this.app.sanitizer.setLevel(oldId, exports.SanitizeLevel.Plain);
|
|
3562
|
+
}
|
|
3563
|
+
this.app.nodes.unregisterNode(n);
|
|
3564
|
+
};
|
|
3565
|
+
const walker = document.createTreeWalker(node, NodeFilter.SHOW_ELEMENT + NodeFilter.SHOW_TEXT, {
|
|
3566
|
+
acceptNode: (n) => isIgnored(n) || this.app.nodes.getID(n) === undefined
|
|
3567
|
+
? NodeFilter.FILTER_REJECT
|
|
3568
|
+
: NodeFilter.FILTER_ACCEPT,
|
|
3569
|
+
},
|
|
3570
|
+
// @ts-ignore
|
|
3571
|
+
false);
|
|
3572
|
+
// Collect first, then clear: unregistering mutates the ids the walker reads.
|
|
3573
|
+
const subtree = [];
|
|
3574
|
+
while (walker.nextNode()) {
|
|
3575
|
+
subtree.push(walker.currentNode);
|
|
3576
|
+
}
|
|
3577
|
+
clearOne(node);
|
|
3578
|
+
subtree.forEach(clearOne);
|
|
3579
|
+
}
|
|
3580
|
+
reemitNode(id, node) {
|
|
3581
|
+
if (isTextNode(node)) {
|
|
3582
|
+
const parent = node.parentNode;
|
|
3583
|
+
if (parent !== null && isElementNode(parent)) {
|
|
3584
|
+
// re-runs sanitize() at the level we just set
|
|
3585
|
+
this.sendNodeData(id, parent, node.data);
|
|
3586
|
+
}
|
|
3587
|
+
return;
|
|
3588
|
+
}
|
|
3589
|
+
if (isElementNode(node)) {
|
|
3590
|
+
// inputs/images/canvas re-emit their own payload via registered callbacks
|
|
3591
|
+
this.app.callResanitizeCallbacks(node, id);
|
|
3592
|
+
}
|
|
3593
|
+
}
|
|
3790
3594
|
disconnect() {
|
|
3791
3595
|
this.observer.disconnect();
|
|
3792
3596
|
this.clear();
|
|
3793
3597
|
this.throttledSetNodeData.clear();
|
|
3598
|
+
this.generation++;
|
|
3794
3599
|
}
|
|
3795
3600
|
}
|
|
3796
3601
|
|
|
@@ -4025,7 +3830,10 @@ class TopObserver extends Observer {
|
|
|
4025
3830
|
this.app.debug.info('doc already observed for', id);
|
|
4026
3831
|
return;
|
|
4027
3832
|
}
|
|
4028
|
-
const observer = new IFrameObserver(this.app, false, {
|
|
3833
|
+
const observer = new IFrameObserver(this.app, false, {
|
|
3834
|
+
disableSprites: this.options.disableSprites,
|
|
3835
|
+
...getInlineOptions(this.options.inlineCss, console.warn),
|
|
3836
|
+
});
|
|
4029
3837
|
this.iframeObservers.set(iframe, observer);
|
|
4030
3838
|
this.docObservers.set(currentDoc, observer);
|
|
4031
3839
|
this.iframeObserversArr.push(observer);
|
|
@@ -4047,7 +3855,10 @@ class TopObserver extends Observer {
|
|
|
4047
3855
|
handle();
|
|
4048
3856
|
}
|
|
4049
3857
|
handleShadowRoot(shRoot) {
|
|
4050
|
-
const observer = new ShadowRootObserver(this.app
|
|
3858
|
+
const observer = new ShadowRootObserver(this.app, false, {
|
|
3859
|
+
disableSprites: this.options.disableSprites,
|
|
3860
|
+
...getInlineOptions(this.options.inlineCss, console.warn),
|
|
3861
|
+
});
|
|
4051
3862
|
this.shadowRootObservers.set(shRoot, observer);
|
|
4052
3863
|
observer.observe(shRoot.host);
|
|
4053
3864
|
}
|
|
@@ -4083,7 +3894,10 @@ class TopObserver extends Observer {
|
|
|
4083
3894
|
};
|
|
4084
3895
|
this.app.nodes.clear();
|
|
4085
3896
|
this.app.nodes.crossdomainMode(frameLevel, frameOder);
|
|
4086
|
-
const iframeObserver = new IFrameObserver(this.app
|
|
3897
|
+
const iframeObserver = new IFrameObserver(this.app, false, {
|
|
3898
|
+
disableSprites: this.options.disableSprites,
|
|
3899
|
+
...getInlineOptions(this.options.inlineCss, console.warn),
|
|
3900
|
+
});
|
|
4087
3901
|
this.iframeObservers.set(window.document, iframeObserver);
|
|
4088
3902
|
iframeObserver.syntheticObserve(rootNodeId, window.document);
|
|
4089
3903
|
}
|
|
@@ -4099,94 +3913,6 @@ class TopObserver extends Observer {
|
|
|
4099
3913
|
}
|
|
4100
3914
|
}
|
|
4101
3915
|
|
|
4102
|
-
exports.SanitizeLevel = void 0;
|
|
4103
|
-
(function (SanitizeLevel) {
|
|
4104
|
-
SanitizeLevel[SanitizeLevel["Plain"] = 0] = "Plain";
|
|
4105
|
-
SanitizeLevel[SanitizeLevel["Obscured"] = 1] = "Obscured";
|
|
4106
|
-
SanitizeLevel[SanitizeLevel["Hidden"] = 2] = "Hidden";
|
|
4107
|
-
})(exports.SanitizeLevel || (exports.SanitizeLevel = {}));
|
|
4108
|
-
const stringWiper = (input) => input
|
|
4109
|
-
.trim()
|
|
4110
|
-
.replace(/[^\f\n\r\t\v\u00a0\u1680\u2000-\u200a\u2028\u2029\u202f\u205f\u3000\ufeff\s]/g, '*');
|
|
4111
|
-
class Sanitizer {
|
|
4112
|
-
constructor(params) {
|
|
4113
|
-
this.obscured = new Set();
|
|
4114
|
-
this.hidden = new Set();
|
|
4115
|
-
this.app = params.app;
|
|
4116
|
-
const defaultOptions = {
|
|
4117
|
-
maskTextEmails: true,
|
|
4118
|
-
maskTextNumbers: false,
|
|
4119
|
-
maskAllByDefault: false,
|
|
4120
|
-
domSanitizer: undefined,
|
|
4121
|
-
};
|
|
4122
|
-
this.maskAllByDefault = params.options?.maskAllByDefault ?? false;
|
|
4123
|
-
this.options = Object.assign(defaultOptions, params.options);
|
|
4124
|
-
}
|
|
4125
|
-
handleNode(id, parentID, node) {
|
|
4126
|
-
if (this.options.maskAllByDefault) {
|
|
4127
|
-
if (isElementNode(node) && !hasOxvoSessionsAttribute(node, 'unmask')) {
|
|
4128
|
-
return this.obscured.add(id);
|
|
4129
|
-
}
|
|
4130
|
-
if (isTextNode(node) && !hasOxvoSessionsAttribute(node.parentNode, 'unmask')) {
|
|
4131
|
-
return this.obscured.add(id);
|
|
4132
|
-
}
|
|
4133
|
-
}
|
|
4134
|
-
if (this.obscured.has(parentID) ||
|
|
4135
|
-
(isElementNode(node) &&
|
|
4136
|
-
(hasOxvoSessionsAttribute(node, 'masked') || hasOxvoSessionsAttribute(node, 'obscured')))) {
|
|
4137
|
-
this.obscured.add(id);
|
|
4138
|
-
}
|
|
4139
|
-
if (this.hidden.has(parentID) ||
|
|
4140
|
-
(isElementNode(node) &&
|
|
4141
|
-
(hasOxvoSessionsAttribute(node, 'htmlmasked') || hasOxvoSessionsAttribute(node, 'hidden')))) {
|
|
4142
|
-
this.hidden.add(id);
|
|
4143
|
-
}
|
|
4144
|
-
if (this.options.domSanitizer !== undefined && isElementNode(node)) {
|
|
4145
|
-
const sanitizeLevel = this.options.domSanitizer(node);
|
|
4146
|
-
if (sanitizeLevel === exports.SanitizeLevel.Obscured) {
|
|
4147
|
-
this.obscured.add(id);
|
|
4148
|
-
}
|
|
4149
|
-
if (sanitizeLevel === exports.SanitizeLevel.Hidden) {
|
|
4150
|
-
this.hidden.add(id);
|
|
4151
|
-
}
|
|
4152
|
-
}
|
|
4153
|
-
}
|
|
4154
|
-
sanitize(id, data) {
|
|
4155
|
-
if (this.obscured.has(id)) {
|
|
4156
|
-
// TODO: is it the best place to put trim() ? Might trimmed spaces be considered in layout in certain cases?
|
|
4157
|
-
return stringWiper(data);
|
|
4158
|
-
}
|
|
4159
|
-
if (this.options.maskTextNumbers) {
|
|
4160
|
-
data = data.replace(/\d/g, '0');
|
|
4161
|
-
}
|
|
4162
|
-
if (this.options.maskTextEmails) {
|
|
4163
|
-
data = data.replace(/^\w+([+.-]\w+)*@\w+([.-]\w+)*\.\w{2,3}$/g, (email) => {
|
|
4164
|
-
const [name, domain] = email.split('@');
|
|
4165
|
-
const [domainName, host] = domain.split('.');
|
|
4166
|
-
return `${stars(name)}@${stars(domainName)}.${stars(host)}`;
|
|
4167
|
-
});
|
|
4168
|
-
}
|
|
4169
|
-
return data;
|
|
4170
|
-
}
|
|
4171
|
-
isObscured(id) {
|
|
4172
|
-
return this.obscured.has(id);
|
|
4173
|
-
}
|
|
4174
|
-
isHidden(id) {
|
|
4175
|
-
return this.hidden.has(id);
|
|
4176
|
-
}
|
|
4177
|
-
getInnerTextSecure(el) {
|
|
4178
|
-
const id = this.app.nodes.getID(el);
|
|
4179
|
-
if (!id) {
|
|
4180
|
-
return '';
|
|
4181
|
-
}
|
|
4182
|
-
return this.sanitize(id, el.innerText);
|
|
4183
|
-
}
|
|
4184
|
-
clear() {
|
|
4185
|
-
this.obscured.clear();
|
|
4186
|
-
this.hidden.clear();
|
|
4187
|
-
}
|
|
4188
|
-
}
|
|
4189
|
-
|
|
4190
3916
|
const tokenSeparator = '_$_';
|
|
4191
3917
|
class Session {
|
|
4192
3918
|
constructor(params) {
|
|
@@ -4386,9 +4112,10 @@ class Ticker {
|
|
|
4386
4112
|
* this value is injected during build time via rollup
|
|
4387
4113
|
* */
|
|
4388
4114
|
// @ts-ignore
|
|
4389
|
-
const workerBodyFn = "!function(){\"use strict\";class t{constructor(t,s,i,e=10,n=250,h,r){this.onUnauthorised=s,this.onFailure=i,this.MAX_ATTEMPTS_COUNT=e,this.ATTEMPT_TIMEOUT=n,this.onCompress=h,this.pageNo=r,this.attemptsCount=0,this.busy=!1,this.queue=[],this.token=null,this.lastBatchNum=0,this.ingestURL=t+\"/v1/events\",this.isCompressing=void 0!==h}getQueueStatus(){return 0===this.queue.length&&!this.busy}authorise(t){this.token=t,this.busy||this.sendNext()}push(t){if(this.busy||!this.token)this.queue.push(t);else if(this.busy=!0,this.isCompressing&&this.onCompress)this.onCompress(t);else{const s=++this.lastBatchNum;this.sendBatch(t,!1,s)}}sendNext(){const t=this.queue.shift();if(t)if(this.busy=!0,this.isCompressing&&this.onCompress)this.onCompress(t);else{const s=++this.lastBatchNum;this.sendBatch(t,!1,s)}else this.busy=!1}retry(t,s,i){this.attemptsCount>=this.MAX_ATTEMPTS_COUNT?this.onFailure(`Failed to send batch after ${this.attemptsCount} attempts.`):(this.attemptsCount++,setTimeout((()=>this.sendBatch(t,s,i)),this.ATTEMPT_TIMEOUT*this.attemptsCount))}sendBatch(t,s,i){var e;const n=null==i?void 0:i.toString().replace(/^([^_]+)_([^_]+).*/,\"$1_$2_$3\");this.busy=!0;const h={Authorization:`Bearer ${this.token}`};if(s&&(h[\"Content-Encoding\"]=\"gzip\"),null===this.token)return void setTimeout((()=>{this.sendBatch(t,s,`${null!=i?i:\"noBatchNum\"}_newToken`)}),500);const r=`${null!==(e=this.pageNo)&&void 0!==e?e:\"noPageNum\"}_${null!=n?n:\"noBatchNum\"}`;fetch(`${this.ingestURL}?seq=${r}`,{body:t,method:\"POST\",headers:h,keepalive:t.length<65536}).then((e=>{if(401===e.status)return this.busy=!1,void this.onUnauthorised();e.status>=400?this.retry(t,s,`${null!=i?i:\"noBatchNum\"}_network:${e.status}`):(this.attemptsCount=0,this.sendNext())})).catch((e=>{console.warn(\"OxvoSessions:\",e),this.retry(t,s,`${null!=i?i:\"noBatchNum\"}_reject:${e.message}`)}))}sendCompressed(t){const s=++this.lastBatchNum;this.sendBatch(t,!0,s)}sendUncompressed(t){const s=++this.lastBatchNum;this.sendBatch(t,!1,s)}clean(){this.sendNext(),setTimeout((()=>{this.token=null,this.queue.length=0}),10)}}const s=\"function\"==typeof TextEncoder?new TextEncoder:{encode(t){const s=t.length,i=new Uint8Array(3*s);let e=-1;for(let n=0,h=0,r=0;r!==s;){if(n=t.charCodeAt(r),r+=1,n>=55296&&n<=56319){if(r===s){i[e+=1]=239,i[e+=1]=191,i[e+=1]=189;break}if(h=t.charCodeAt(r),!(h>=56320&&h<=57343)){i[e+=1]=239,i[e+=1]=191,i[e+=1]=189;continue}if(n=1024*(n-55296)+h-56320+65536,r+=1,n>65535){i[e+=1]=240|n>>>18,i[e+=1]=128|n>>>12&63,i[e+=1]=128|n>>>6&63,i[e+=1]=128|63&n;continue}}n<=127?i[e+=1]=0|n:n<=2047?(i[e+=1]=192|n>>>6,i[e+=1]=128|63&n):(i[e+=1]=224|n>>>12,i[e+=1]=128|n>>>6&63,i[e+=1]=128|63&n)}return i.subarray(0,e+1)}};class i{constructor(t){this.size=t,this.offset=0,this.checkpointOffset=0,this.data=new Uint8Array(t)}getCurrentOffset(){return this.offset}checkpoint(){this.checkpointOffset=this.offset}get isEmpty(){return 0===this.offset}skip(t){return this.offset+=t,this.offset<=this.size}set(t,s){this.data.set(t,s)}boolean(t){return this.data[this.offset++]=+t,this.offset<=this.size}uint(t){for((t<0||t>Number.MAX_SAFE_INTEGER)&&(t=0);t>=128;)this.data[this.offset++]=t%256|128,t=Math.floor(t/128);return this.data[this.offset++]=t,this.offset<=this.size}int(t){return t=Math.round(t),this.uint(t>=0?2*t:-2*t-1)}string(t){const i=s.encode(t),e=i.byteLength;return!(!this.uint(e)||this.offset+e>this.size)&&(this.data.set(i,this.offset),this.offset+=e,!0)}reset(){this.offset=0,this.checkpointOffset=0}flush(){const t=this.data.slice(0,this.checkpointOffset);return this.reset(),t}}class e extends i{encode(t){switch(t[0]){case 0:case 11:case 114:case 115:return this.uint(t[1]);case 4:case 44:case 47:return this.string(t[1])&&this.string(t[2])&&this.uint(t[3]);case 5:case 20:case 65:case 70:case 75:case 76:case 77:case 82:return this.uint(t[1])&&this.uint(t[2]);case 6:return this.int(t[1])&&this.int(t[2]);case 7:return!0;case 8:return this.uint(t[1])&&this.uint(t[2])&&this.uint(t[3])&&this.string(t[4])&&this.boolean(t[5]);case 9:case 10:case 24:case 35:case 51:return this.uint(t[1])&&this.uint(t[2])&&this.uint(t[3]);case 12:case 52:case 61:case 71:return this.uint(t[1])&&this.string(t[2])&&this.string(t[3]);case 13:case 14:case 17:case 34:case 36:case 50:case 54:return this.uint(t[1])&&this.string(t[2]);case 16:return this.uint(t[1])&&this.int(t[2])&&this.int(t[3]);case 18:return this.uint(t[1])&&this.string(t[2])&&this.int(t[3]);case 19:return this.uint(t[1])&&this.boolean(t[2]);case 21:return this.string(t[1])&&this.string(t[2])&&this.string(t[3])&&this.string(t[4])&&this.string(t[5])&&this.uint(t[6])&&this.uint(t[7])&&this.uint(t[8]);case 22:case 27:case 30:case 41:case 45:case 46:case 43:case 63:case 64:case 79:case 124:return this.string(t[1])&&this.string(t[2]);case 23:return this.uint(t[1])&&this.uint(t[2])&&this.uint(t[3])&&this.uint(t[4])&&this.uint(t[5])&&this.uint(t[6])&&this.uint(t[7])&&this.uint(t[8])&&this.uint(t[9]);case 28:case 29:case 42:case 117:case 118:return this.string(t[1]);case 40:return this.string(t[1])&&this.uint(t[2])&&this.string(t[3])&&this.string(t[4]);case 48:return this.string(t[1])&&this.string(t[2])&&this.string(t[3])&&this.string(t[4])&&this.int(t[5]);case 49:return this.int(t[1])&&this.int(t[2])&&this.uint(t[3])&&this.uint(t[4]);case 53:return this.uint(t[1])&&this.uint(t[2])&&this.uint(t[3])&&this.uint(t[4])&&this.uint(t[5])&&this.uint(t[6])&&this.string(t[7])&&this.string(t[8]);case 55:return this.boolean(t[1]);case 57:case 60:return this.uint(t[1])&&this.string(t[2])&&this.string(t[3])&&this.string(t[4]);case 58:case 120:return this.int(t[1]);case 68:return this.uint(t[1])&&this.uint(t[2])&&this.string(t[3])&&this.string(t[4])&&this.uint(t[5])&&this.uint(t[6]);case 69:return this.uint(t[1])&&this.uint(t[2])&&this.string(t[3])&&this.string(t[4]);case 73:return this.uint(t[1])&&this.string(t[2])&&this.uint(t[3])&&this.string(t[4]);case 78:return this.string(t[1])&&this.string(t[2])&&this.string(t[3])&&this.string(t[4]);case 81:return this.uint(t[1])&&this.uint(t[2])&&this.uint(t[3])&&this.int(t[4])&&this.string(t[5]);case 83:return this.string(t[1])&&this.string(t[2])&&this.string(t[3])&&this.string(t[4])&&this.string(t[5])&&this.uint(t[6])&&this.uint(t[7])&&this.uint(t[8])&&this.uint(t[9]);case 84:return this.string(t[1])&&this.string(t[2])&&this.string(t[3])&&this.uint(t[4])&&this.string(t[5])&&this.string(t[6]);case 85:return this.uint(t[1])&&this.uint(t[2])&&this.uint(t[3])&&this.uint(t[4])&&this.uint(t[5])&&this.uint(t[6])&&this.string(t[7])&&this.string(t[8])&&this.uint(t[9])&&this.boolean(t[10])&&this.uint(t[11])&&this.uint(t[12])&&this.uint(t[13])&&this.uint(t[14])&&this.uint(t[15])&&this.uint(t[16])&&this.uint(t[17]);case 87:return this.string(t[1])&&this.int(t[2])&&this.int(t[3]);case 89:return this.string(t[1])&&this.int(t[2])&&this.int(t[3])&&this.int(t[4])&&this.int(t[5])&&this.string(t[6]);case 112:return this.uint(t[1])&&this.string(t[2])&&this.boolean(t[3])&&this.string(t[4])&&this.int(t[5])&&this.int(t[6]);case 113:return this.uint(t[1])&&this.uint(t[2])&&this.string(t[3]);case 116:return this.uint(t[1])&&this.uint(t[2])&&this.uint(t[3])&&this.uint(t[4])&&this.uint(t[5])&&this.uint(t[6])&&this.string(t[7])&&this.string(t[8])&&this.uint(t[9])&&this.boolean(t[10]);case 119:return this.string(t[1])&&this.uint(t[2]);case 121:return this.string(t[1])&&this.string(t[2])&&this.uint(t[3])&&this.uint(t[4]);case 122:return this.string(t[1])&&this.string(t[2])&&this.uint(t[3])&&this.string(t[4]);case 123:return this.string(t[1])&&this.string(t[2])&&this.string(t[3])&&this.string(t[4])&&this.uint(t[5])}}}class n{constructor(t,s,i,n,h,r){this.pageNo=t,this.timestamp=s,this.url=i,this.onBatch=n,this.tabId=h,this.onOfflineEnd=r,this.nextIndex=0,this.beaconSize=2e5,this.encoder=new e(this.beaconSize),this.sizeBuffer=new Uint8Array(3),this.isEmpty=!0,this.checkpoints=[],this.beaconSizeLimit=1e6,this.prepare()}writeType(t){return this.encoder.uint(t[0])}writeFields(t){return this.encoder.encode(t)}writeSizeAt(t,s){for(let s=0;s<3;s++)this.sizeBuffer[s]=t>>8*s;this.encoder.set(this.sizeBuffer,s)}prepare(){if(!this.encoder.isEmpty)return;this.checkpoints.length=0;const t=[81,1,this.pageNo,this.nextIndex,this.timestamp,this.url],s=[0,this.timestamp],i=[118,this.tabId];this.writeType(t),this.writeFields(t),this.writeWithSize(s),this.writeWithSize(i),this.isEmpty=!0}writeWithSize(t){const s=this.encoder;if(!this.writeType(t)||!s.skip(3))return!1;const i=s.getCurrentOffset(),e=this.writeFields(t);if(e){const e=s.getCurrentOffset()-i;if(e>16777215)return console.warn(\"OxvoSessions: max message size overflow.\"),!1;this.writeSizeAt(e,i-3),s.checkpoint(),this.checkpoints.push(s.getCurrentOffset()),this.isEmpty=this.isEmpty&&0===t[0],this.nextIndex++}return e}setBeaconSizeLimit(t){this.beaconSizeLimit=t}writeMessage(t){if(-1===t[0])return this.finaliseBatch(),this.onOfflineEnd();0===t[0]&&(this.timestamp=t[1]),122===t[0]&&(this.url=t[1]),this.writeWithSize(t)||(this.finaliseBatch(),this.writeWithSize(t)||(this.encoder=new e(this.beaconSizeLimit),this.prepare(),this.writeWithSize(t)?this.finaliseBatch():console.warn(\"OxvoSessions: beacon size overflow. Skipping large message.\",t,this),this.encoder=new e(this.beaconSize),this.prepare()))}finaliseBatch(t=!1){if(this.isEmpty)return;const s=this.encoder.flush();this.onBatch(s,t),this.prepare()}clean(){this.encoder.reset(),this.checkpoints.length=0}}var h;!function(t){t[t.NotActive=0]=\"NotActive\",t[t.Starting=1]=\"Starting\",t[t.Stopping=2]=\"Stopping\",t[t.Active=3]=\"Active\",t[t.Stopped=4]=\"Stopped\"}(h||(h={}));let r=null,u=null,a=h.NotActive;function o(t){u&&u.finaliseBatch(t)}function c(){return new Promise((t=>{a=h.Stopping,null!==p&&(clearInterval(p),p=null),u&&(u.clean(),u=null),r&&(r.clean(),setTimeout((()=>{r=null}),20)),setTimeout((()=>{a=h.NotActive,t(null)}),100)}))}function g(){[h.Stopped,h.Stopping].includes(a)||(postMessage(\"a_stop\"),c().then((()=>{postMessage(\"a_start\")})))}let l,p=null;self.onmessage=({data:s})=>{if(\"stop\"===s)return o(),void c().then((()=>{a=h.Stopped}));if(\"forceFlushBatch\"!==s)if(\"closing\"!==s){if(!Array.isArray(s)){if(\"compressed\"===s.type){if(!r)return console.debug(\"OR WebWorker: sender not initialised. Compressed batch.\"),void g();s.batch&&r.sendCompressed(s.batch)}if(\"uncompressed\"===s.type){if(!r)return console.debug(\"OR WebWorker: sender not initialised. Uncompressed batch.\"),void g();s.batch&&r.sendUncompressed(s.batch)}return\"start\"===s.type?(a=h.Starting,r=new t(s.ingestPoint,(()=>{g()}),(t=>{!function(t){postMessage({type:\"failure\",reason:t}),c()}(t)}),s.connAttemptCount,s.connAttemptGap,(t=>{postMessage({type:\"compress\",batch:t},[t.buffer])}),s.pageNo),u=new n(s.pageNo,s.timestamp,s.url,((t,s)=>{r&&(s?r.sendUncompressed(t):r.push(t))}),s.tabId,(()=>postMessage({type:\"queue_empty\"}))),null===p&&(p=setInterval(o,3e4)),a=h.Active):\"auth\"===s.type?r?u?(r.authorise(s.token),void(s.beaconSizeLimit&&u.setBeaconSizeLimit(s.beaconSizeLimit))):(console.debug(\"OR WebWorker: writer not initialised. Received auth.\"),void g()):(console.debug(\"OR WebWorker: sender not initialised. Received auth.\"),void g()):void 0}if(u){const t=u;s.forEach((s=>{55===s[0]&&(s[1]?l=setTimeout((()=>g()),18e5):clearTimeout(l)),t.writeMessage(s)}))}else postMessage(\"not_init\"),g()}else o(!0);else o()}}();\n";
|
|
4115
|
+
const workerBodyFn = "!function(){\"use strict\";class t{constructor(t,s,i,e=10,n=250,h,r){this.onUnauthorised=s,this.onFailure=i,this.MAX_ATTEMPTS_COUNT=e,this.ATTEMPT_TIMEOUT=n,this.onCompress=h,this.pageNo=r,this.attemptsCount=0,this.busy=!1,this.queue=[],this.token=null,this.lastBatchNum=0,this.inflightKeepaliveBytes=0,this.ingestURL=t+\"/v1/events\",this.isCompressing=void 0!==h}getQueueStatus(){return 0===this.queue.length&&!this.busy}authorise(t){this.token=t,this.busy||this.sendNext()}push(t,s=\"player\"){if(this.busy||!this.token)this.queue.push({batch:t,dataType:s});else if(this.busy=!0,this.isCompressing&&this.onCompress)this.onCompress(t,s);else{const i=++this.lastBatchNum;this.sendBatch(t,!1,i,s)}}sendNext(){const t=this.queue.shift();if(t)if(this.busy=!0,this.isCompressing&&this.onCompress)this.onCompress(t.batch,t.dataType);else{const s=++this.lastBatchNum;this.sendBatch(t.batch,!1,s,t.dataType)}else this.busy=!1}retry(t,s,i,e=\"player\"){if(this.attemptsCount>=this.MAX_ATTEMPTS_COUNT)return void this.onFailure(`Failed to send batch after ${this.attemptsCount} attempts.`);this.attemptsCount++;const n=new Uint8Array(t);setTimeout((()=>this.sendBatch(n,s,i,e)),this.ATTEMPT_TIMEOUT*this.attemptsCount)}sendBatch(t,s,i,e=\"player\"){var n;if(0===t.length)return console.error(\"OxvoSessions: refusing to send 0-byte batch.\",{batchNum:i,dataType:e,isCompressed:s,batch:t}),this.attemptsCount=0,void this.sendNext();const h=null==i?void 0:i.toString().replace(/^([^_]+)_([^_]+).*/,\"$1_$2_$3\");this.busy=!0;const r={Authorization:`Bearer ${this.token}`,DataType:e};if(s&&(r[\"Content-Encoding\"]=\"gzip\"),null===this.token)return void setTimeout((()=>{this.sendBatch(t,s,`${null!=i?i:\"noBatchNum\"}_newToken`,e)}),500);const a=t.length<65536&&this.inflightKeepaliveBytes+t.length<=65536;a&&(this.inflightKeepaliveBytes+=t.length);const o=()=>{a&&(this.inflightKeepaliveBytes-=t.length)},u=`${null!==(n=this.pageNo)&&void 0!==n?n:\"noPageNum\"}_${null!=h?h:\"noBatchNum\"}`;fetch(`${this.ingestURL}?seq=${u}&keepalive=${a?\"yes\":\"no\"}`,{body:t,method:\"POST\",headers:r,keepalive:a}).then((n=>{var h;if(o(),null===(h=n.body)||void 0===h||h.cancel().catch((()=>{})),401===n.status)return this.busy=!1,void this.onUnauthorised();n.status>=400?this.retry(t,s,`${null!=i?i:\"noBatchNum\"}_network:${n.status}`,e):(this.attemptsCount=0,this.sendNext())})).catch((n=>{o(),console.warn(\"OxvoSessions:\",n),this.retry(t,s,`${null!=i?i:\"noBatchNum\"}_reject:${n.message}`,e)}))}sendCompressed(t,s=\"player\"){const i=++this.lastBatchNum;this.sendBatch(t,!0,i,s)}sendUncompressed(t,s=\"player\"){const i=++this.lastBatchNum;this.sendBatch(t,!1,i,s)}clean(){this.sendNext(),setTimeout((()=>{this.token=null,this.queue.length=0}),10)}}const s=new Set([60,61,71,73]),i=new Set([21,22,40,41,44,45,46,47,48,79,83,84,85,87,89,116,120,121,123]),e=new Set([17,23,24,27,28,29,30,42,63,64,78,112,115,124]),n=\"function\"==typeof TextEncoder?new TextEncoder:{encode(t){const s=t.length,i=new Uint8Array(3*s);let e=-1;for(let n=0,h=0,r=0;r!==s;){if(n=t.charCodeAt(r),r+=1,n>=55296&&n<=56319){if(r===s){i[e+=1]=239,i[e+=1]=191,i[e+=1]=189;break}if(h=t.charCodeAt(r),!(h>=56320&&h<=57343)){i[e+=1]=239,i[e+=1]=191,i[e+=1]=189;continue}if(n=1024*(n-55296)+h-56320+65536,r+=1,n>65535){i[e+=1]=240|n>>>18,i[e+=1]=128|n>>>12&63,i[e+=1]=128|n>>>6&63,i[e+=1]=128|63&n;continue}}n<=127?i[e+=1]=0|n:n<=2047?(i[e+=1]=192|n>>>6,i[e+=1]=128|63&n):(i[e+=1]=224|n>>>12,i[e+=1]=128|n>>>6&63,i[e+=1]=128|63&n)}return i.subarray(0,e+1)}};class h{constructor(t){this.size=t,this.offset=0,this.checkpointOffset=0,this.data=new Uint8Array(t)}getCurrentOffset(){return this.offset}getCurrentCheckpoint(){return this.checkpointOffset}checkpoint(){this.checkpointOffset=this.offset}get isEmpty(){return 0===this.offset}skip(t){return this.offset+=t,this.offset<=this.size}set(t,s){this.data.set(t,s)}boolean(t){return this.data[this.offset++]=+t,this.offset<=this.size}uint(t){for((t<0||t>Number.MAX_SAFE_INTEGER)&&(t=0);t>=128;)this.data[this.offset++]=t%256|128,t=Math.floor(t/128);return this.data[this.offset++]=t,this.offset<=this.size}int(t){return t=Math.round(t),this.uint(t>=0?2*t:-2*t-1)}string(t){const s=n.encode(t),i=s.byteLength;return!(!this.uint(i)||this.offset+i>this.size)&&(this.data.set(s,this.offset),this.offset+=i,!0)}reset(){this.offset=0,this.checkpointOffset=0}rewind(t,s){t>this.offset||s>this.checkpointOffset||(this.offset=t,this.checkpointOffset=s)}flush(){const t=this.data.slice(0,this.checkpointOffset);return this.reset(),t}}class r extends h{encode(t){switch(t[0]){case 0:case 11:case 114:case 115:return this.uint(t[1]);case 4:case 44:case 47:return this.string(t[1])&&this.string(t[2])&&this.uint(t[3]);case 5:case 20:case 65:case 70:case 75:case 76:case 77:return this.uint(t[1])&&this.uint(t[2]);case 6:return this.int(t[1])&&this.int(t[2]);case 7:return!0;case 8:return this.uint(t[1])&&this.uint(t[2])&&this.uint(t[3])&&this.string(t[4])&&this.boolean(t[5]);case 9:case 10:case 24:case 35:case 51:return this.uint(t[1])&&this.uint(t[2])&&this.uint(t[3]);case 12:case 52:case 61:case 71:return this.uint(t[1])&&this.string(t[2])&&this.string(t[3]);case 13:case 14:case 17:case 34:case 36:case 50:case 54:return this.uint(t[1])&&this.string(t[2]);case 16:return this.uint(t[1])&&this.int(t[2])&&this.int(t[3]);case 18:return this.uint(t[1])&&this.string(t[2])&&this.int(t[3]);case 19:return this.uint(t[1])&&this.boolean(t[2]);case 21:return this.string(t[1])&&this.string(t[2])&&this.string(t[3])&&this.string(t[4])&&this.string(t[5])&&this.uint(t[6])&&this.uint(t[7])&&this.uint(t[8]);case 22:case 27:case 30:case 41:case 45:case 46:case 43:case 63:case 64:case 79:case 124:return this.string(t[1])&&this.string(t[2]);case 23:return this.uint(t[1])&&this.uint(t[2])&&this.uint(t[3])&&this.uint(t[4])&&this.uint(t[5])&&this.uint(t[6])&&this.uint(t[7])&&this.uint(t[8])&&this.uint(t[9]);case 28:case 29:case 42:case 117:case 118:return this.string(t[1]);case 40:return this.string(t[1])&&this.uint(t[2])&&this.string(t[3])&&this.string(t[4]);case 48:return this.string(t[1])&&this.string(t[2])&&this.string(t[3])&&this.string(t[4])&&this.int(t[5]);case 49:return this.int(t[1])&&this.int(t[2])&&this.uint(t[3])&&this.uint(t[4]);case 53:return this.uint(t[1])&&this.uint(t[2])&&this.uint(t[3])&&this.uint(t[4])&&this.uint(t[5])&&this.uint(t[6])&&this.string(t[7])&&this.string(t[8]);case 55:return this.boolean(t[1]);case 57:case 60:return this.uint(t[1])&&this.string(t[2])&&this.string(t[3])&&this.string(t[4]);case 58:case 120:return this.int(t[1]);case 68:return this.uint(t[1])&&this.uint(t[2])&&this.string(t[3])&&this.string(t[4])&&this.uint(t[5])&&this.uint(t[6]);case 69:return this.uint(t[1])&&this.uint(t[2])&&this.string(t[3])&&this.string(t[4]);case 73:return this.uint(t[1])&&this.string(t[2])&&this.uint(t[3])&&this.string(t[4]);case 78:return this.string(t[1])&&this.string(t[2])&&this.string(t[3])&&this.string(t[4]);case 81:return this.uint(t[1])&&this.uint(t[2])&&this.uint(t[3])&&this.int(t[4])&&this.string(t[5]);case 83:return this.string(t[1])&&this.string(t[2])&&this.string(t[3])&&this.string(t[4])&&this.string(t[5])&&this.uint(t[6])&&this.uint(t[7])&&this.uint(t[8])&&this.uint(t[9]);case 84:return this.string(t[1])&&this.string(t[2])&&this.string(t[3])&&this.uint(t[4])&&this.string(t[5])&&this.string(t[6]);case 85:return this.uint(t[1])&&this.uint(t[2])&&this.uint(t[3])&&this.uint(t[4])&&this.uint(t[5])&&this.uint(t[6])&&this.string(t[7])&&this.string(t[8])&&this.uint(t[9])&&this.boolean(t[10])&&this.uint(t[11])&&this.uint(t[12])&&this.uint(t[13])&&this.uint(t[14])&&this.uint(t[15])&&this.uint(t[16])&&this.uint(t[17]);case 87:return this.string(t[1])&&this.int(t[2])&&this.int(t[3]);case 89:return this.string(t[1])&&this.int(t[2])&&this.int(t[3])&&this.int(t[4])&&this.int(t[5])&&this.string(t[6]);case 112:return this.uint(t[1])&&this.string(t[2])&&this.boolean(t[3])&&this.string(t[4])&&this.int(t[5])&&this.int(t[6]);case 113:return this.uint(t[1])&&this.uint(t[2])&&this.string(t[3]);case 116:return this.uint(t[1])&&this.uint(t[2])&&this.uint(t[3])&&this.uint(t[4])&&this.uint(t[5])&&this.uint(t[6])&&this.string(t[7])&&this.string(t[8])&&this.uint(t[9])&&this.boolean(t[10]);case 119:return this.string(t[1])&&this.uint(t[2]);case 121:return this.string(t[1])&&this.string(t[2])&&this.uint(t[3])&&this.uint(t[4]);case 122:return this.string(t[1])&&this.string(t[2])&&this.uint(t[3])&&this.string(t[4]);case 123:return this.string(t[1])&&this.string(t[2])&&this.string(t[3])&&this.string(t[4])&&this.uint(t[5])}}}class a{constructor(t,s,i){this.bufferSize=t,this.version=s,this.dataType=i,this.sizeBuffer=new Uint8Array(3),this.snap=null,this.hasNonTimestamp=!1,this.lastPushedTs=0,this.encoder=new r(t)}push(t,s){const i=this.encoder,e=null===this.snap,n=i.getCurrentOffset(),h=i.getCurrentCheckpoint();if(e){const t={pageNo:s.pageNo,firstIndex:s.index,timestamp:s.timestamp,url:s.url,tabId:s.tabId};if(!this.writeHeader(t))return i.rewind(n,h),!1;this.snap=t,this.lastPushedTs=s.timestamp}return 0===t[0]||s.timestamp===this.lastPushedTs||this.writeMessageWithSize([0,s.timestamp])?this.writeMessageWithSize(t)?(this.lastPushedTs=s.timestamp,0!==t[0]&&(this.hasNonTimestamp=!0),!0):(i.rewind(n,h),e&&(this.snap=null),!1):(i.rewind(n,h),!1)}hasContent(){return null!==this.snap&&this.hasNonTimestamp}flush(){if(!this.hasContent())return this.reset(),null;const t=this.encoder.flush();return this.snap=null,this.hasNonTimestamp=!1,this.lastPushedTs=0,t}reset(){this.encoder.reset(),this.snap=null,this.hasNonTimestamp=!1,this.lastPushedTs=0}writeHeader(t){const s=this.encoder,i=[81,this.version,t.pageNo,t.firstIndex,t.timestamp,t.url];return!!s.uint(i[0])&&(!!s.encode(i)&&(!(s.getCurrentOffset()>this.bufferSize)&&(s.checkpoint(),!!this.writeMessageWithSize([0,t.timestamp])&&!!this.writeMessageWithSize([118,t.tabId]))))}writeMessageWithSize(t){const s=this.encoder;if(!s.uint(t[0])||!s.skip(3))return!1;const i=s.getCurrentOffset();if(!s.encode(t))return!1;const e=s.getCurrentOffset(),n=e-i;return n>16777215?(console.warn(\"OxvoSessions: max message size overflow.\"),!1):!(e>this.bufferSize)&&(this.writeSizeAt(n,i-3),s.checkpoint(),!0)}writeSizeAt(t,s){for(let s=0;s<3;s++)this.sizeBuffer[s]=t>>8*s;this.encoder.set(this.sizeBuffer,s)}}class o{constructor(t,s,i,e,n,h,r=!1,o){this.pageNo=t,this.timestamp=s,this.url=i,this.onBatch=e,this.tabId=n,this.onOfflineEnd=h,this.localDebug=r,this.onLocalSave=o,this.nextIndex=0,this.beaconSize=2e5,this.beaconSizeLimit=1e6,this.protocolVersion=1,this.playerBuilder=new a(this.beaconSize,this.playerVersion(),\"player\"),this.assetBuilder=new a(this.beaconSize,3,\"assets\"),this.devtoolsBuilder=new a(this.beaconSize,4,\"devtools\"),this.analyticsBuilder=new a(this.beaconSize,5,\"analytics\")}playerVersion(){return 2===this.protocolVersion?2:1}currentCtx(){return{pageNo:this.pageNo,index:this.nextIndex,timestamp:this.timestamp,url:this.url,tabId:this.tabId}}setBeaconSizeLimit(t){this.beaconSizeLimit=t}setProtocolVersion(t){this.protocolVersion!==t&&(this.protocolVersion=t,this.playerBuilder.reset(),this.playerBuilder=new a(this.beaconSize,this.playerVersion(),\"player\"))}writeMessage(t){if(-1===t[0])return this.finaliseBatch(),this.onOfflineEnd();0===t[0]&&(this.timestamp=t[1]),122===t[0]&&(this.url=t[1]);const s=this.routeMessage(t);this.pushTo(s,t)}routeMessage(t){if(2===this.protocolVersion){const n=t[0];if(s.has(n))return this.assetBuilder;if(i.has(n))return this.devtoolsBuilder;if(e.has(n))return this.analyticsBuilder}return this.playerBuilder}pushTo(t,s){const i=this.currentCtx();if(t.push(s,i))return void this.nextIndex++;if(this.flushBuilder(t),t.push(s,i))return void this.nextIndex++;const e=new a(this.beaconSizeLimit,t.version,t.dataType);if(!e.push(s,i))return void console.warn(\"OxvoSessions: beacon size overflow. Skipping large message.\",s);this.nextIndex++;const n=e.flush();n&&this.emitBatch(n,t.dataType,!1)}flushBuilder(t,s=!1){const i=t.flush();return!!i&&(this.emitBatch(i,t.dataType,s),!0)}emitBatch(t,s,i){this.localDebug&&this.onLocalSave&&this.onLocalSave(`${s}-${Date.now()}`,t.slice()),this.onBatch(t,i,s)}finaliseBatch(t=!1){this.flushBuilder(this.playerBuilder,t),this.flushBuilder(this.assetBuilder,t),this.flushBuilder(this.devtoolsBuilder,t),this.flushBuilder(this.analyticsBuilder,t)}clean(){this.playerBuilder.reset(),this.assetBuilder.reset(),this.devtoolsBuilder.reset(),this.analyticsBuilder.reset()}}var u;!function(t){t[t.NotActive=0]=\"NotActive\",t[t.Starting=1]=\"Starting\",t[t.Stopping=2]=\"Stopping\",t[t.Active=3]=\"Active\",t[t.Stopped=4]=\"Stopped\"}(u||(u={}));let c=null,l=null,d=u.NotActive;function p(t){l&&l.finaliseBatch(t)}function f(){return new Promise((t=>{d=u.Stopping,null!==b&&(clearInterval(b),b=null),l&&(l.clean(),l=null),c&&(c.clean(),setTimeout((()=>{c=null}),20)),setTimeout((()=>{d=u.NotActive,t(null)}),100)}))}function g(){[u.Stopped,u.Stopping].includes(d)||(postMessage(\"a_stop\"),f().then((()=>{postMessage(\"a_start\")})))}let m,b=null;self.onmessage=({data:s})=>{var i;if(\"stop\"===s)return p(),void f().then((()=>{d=u.Stopped}));if(\"forceFlushBatch\"!==s)if(\"closing\"!==s){if(!Array.isArray(s)){if(\"compressed\"===s.type){if(!c)return console.debug(\"OR WebWorker: sender not initialised. Compressed batch.\"),void g();s.batch&&c.sendCompressed(s.batch,s.dataType)}if(\"uncompressed\"===s.type){if(!c)return console.debug(\"OR WebWorker: sender not initialised. Uncompressed batch.\"),void g();s.batch&&c.sendUncompressed(s.batch,s.dataType)}return\"start\"===s.type?(d=u.Starting,c=new t(s.ingestPoint,(()=>{g()}),(t=>{!function(t){postMessage({type:\"failure\",reason:t}),f()}(t)}),s.connAttemptCount,s.connAttemptGap,((t,s)=>{postMessage({type:\"compress\",batch:t,dataType:s},[t.buffer])}),s.pageNo),l=new o(s.pageNo,s.timestamp,s.url,((t,s,i=\"player\")=>{c&&(s?c.sendUncompressed(t,i):c.push(t,i))}),s.tabId,(()=>postMessage({type:\"queue_empty\"})),null!==(i=s.localDebug)&&void 0!==i&&i,((t,s)=>{postMessage({type:\"local_save\",name:t,batch:s},[s.buffer])})),null===b&&(b=setInterval(p,3e4)),d=u.Active):\"auth\"===s.type?c?l?(c.authorise(s.token),s.beaconSizeLimit&&l.setBeaconSizeLimit(s.beaconSizeLimit),void(s.protocolVersion&&l.setProtocolVersion(s.protocolVersion))):(console.debug(\"OR WebWorker: writer not initialised. Received auth.\"),void g()):(console.debug(\"OR WebWorker: sender not initialised. Received auth.\"),void g()):void 0}if(l){const t=l;s.forEach((s=>{55===s[0]&&(s[1]?m=setTimeout((()=>g()),18e5):clearTimeout(m)),t.writeMessage(s)}))}else postMessage(\"not_init\"),g()}else p(!0);else p()}}();\n";
|
|
4390
4116
|
const CANCELED = 'canceled';
|
|
4391
4117
|
const bufferStorageKey = 'or_buffer_1';
|
|
4118
|
+
const PROTO_VERSION = '2';
|
|
4392
4119
|
const UnsuccessfulStart = (reason) => ({ reason, success: false });
|
|
4393
4120
|
const SuccessfulStart = (body) => ({ ...body, success: true });
|
|
4394
4121
|
var ActivityState;
|
|
@@ -4432,9 +4159,14 @@ const proto = {
|
|
|
4432
4159
|
reset: 'reset-your-session-please',
|
|
4433
4160
|
};
|
|
4434
4161
|
class App {
|
|
4162
|
+
get tagMatcher() {
|
|
4163
|
+
return this.markerWatcher.matcher;
|
|
4164
|
+
}
|
|
4435
4165
|
constructor(projectKey, sessionToken, options, signalError, insideIframe) {
|
|
4436
4166
|
this.signalError = signalError;
|
|
4437
4167
|
this.insideIframe = insideIframe;
|
|
4168
|
+
// Registered by input/img/canvas to re-emit a node when its level changes.
|
|
4169
|
+
this.resanitizeCallbacks = [];
|
|
4438
4170
|
this.messages = [];
|
|
4439
4171
|
/**
|
|
4440
4172
|
* we need 2 buffers, so we don't lose anything
|
|
@@ -4446,7 +4178,7 @@ class App {
|
|
|
4446
4178
|
this.stopCallbacks = [];
|
|
4447
4179
|
this.commitCallbacks = [];
|
|
4448
4180
|
this.activityState = ActivityState.NotActive;
|
|
4449
|
-
this.version = '7.
|
|
4181
|
+
this.version = '7.3.0'; // TODO: version compatability check inside each plugin.
|
|
4450
4182
|
this.socketMode = false;
|
|
4451
4183
|
this.compressionThreshold = 24 * 1000;
|
|
4452
4184
|
this.bc = null;
|
|
@@ -4468,9 +4200,19 @@ class App {
|
|
|
4468
4200
|
if (!data || event.source === window)
|
|
4469
4201
|
return;
|
|
4470
4202
|
if (data.line === proto.startIframe) {
|
|
4471
|
-
|
|
4203
|
+
// Avoid corrupting an in-flight start; let it complete.
|
|
4204
|
+
if (this.activityState === ActivityState.Starting)
|
|
4472
4205
|
return;
|
|
4473
4206
|
try {
|
|
4207
|
+
if (this.active()) {
|
|
4208
|
+
this.stop();
|
|
4209
|
+
}
|
|
4210
|
+
if (data.token) {
|
|
4211
|
+
this.session.setSessionSecret(data.token, this.projectKey);
|
|
4212
|
+
}
|
|
4213
|
+
if (data.id !== undefined) {
|
|
4214
|
+
this.rootId = data.id;
|
|
4215
|
+
}
|
|
4474
4216
|
this.allowAppStart();
|
|
4475
4217
|
void this.start();
|
|
4476
4218
|
}
|
|
@@ -4496,17 +4238,22 @@ class App {
|
|
|
4496
4238
|
}
|
|
4497
4239
|
}
|
|
4498
4240
|
};
|
|
4499
|
-
/**
|
|
4500
|
-
* context ids for iframes,
|
|
4501
|
-
* order is not so important as long as its consistent
|
|
4502
|
-
* */
|
|
4503
4241
|
this.trackedFrames = [];
|
|
4242
|
+
this.frameLastSeen = new Map();
|
|
4243
|
+
this.FRAME_STALE_MS = 1500;
|
|
4504
4244
|
this.crossDomainIframeListener = (event) => {
|
|
4505
|
-
if (
|
|
4245
|
+
if (event.source === window)
|
|
4506
4246
|
return;
|
|
4507
4247
|
const { data } = event;
|
|
4508
4248
|
if (!data)
|
|
4509
4249
|
return;
|
|
4250
|
+
// Record liveness regardless of our own active state so the queue can prune
|
|
4251
|
+
// stale contexts reliably once we resume dispatching commands after a cycle.
|
|
4252
|
+
if ((data.line === proto.polling || data.line === proto.iframeSignal) && data.context) {
|
|
4253
|
+
this.frameLastSeen.set(data.context, Date.now());
|
|
4254
|
+
}
|
|
4255
|
+
if (!this.active())
|
|
4256
|
+
return;
|
|
4510
4257
|
if (data.line === proto.iframeSignal) {
|
|
4511
4258
|
// @ts-ignore
|
|
4512
4259
|
event.source?.postMessage({ ping: true, line: proto.parentAlive }, '*');
|
|
@@ -4604,16 +4351,41 @@ class App {
|
|
|
4604
4351
|
if (!this.pollingQueue.order.length) {
|
|
4605
4352
|
return;
|
|
4606
4353
|
}
|
|
4607
|
-
|
|
4608
|
-
|
|
4609
|
-
|
|
4354
|
+
this.pruneStaleFrames();
|
|
4355
|
+
const liveSet = new Set(this.trackedFrames);
|
|
4356
|
+
while (this.pollingQueue.order.length > 0) {
|
|
4357
|
+
const head = this.pollingQueue.order[0];
|
|
4358
|
+
this.pollingQueue[head] = this.pollingQueue[head].filter((ctx) => liveSet.has(ctx));
|
|
4359
|
+
if (this.pollingQueue[head].length === 0) {
|
|
4360
|
+
delete this.pollingQueue[head];
|
|
4361
|
+
this.pollingQueue.order.shift();
|
|
4362
|
+
}
|
|
4363
|
+
else {
|
|
4364
|
+
break;
|
|
4365
|
+
}
|
|
4366
|
+
}
|
|
4367
|
+
if (!this.pollingQueue.order.length) {
|
|
4610
4368
|
return;
|
|
4611
4369
|
}
|
|
4370
|
+
const nextCommand = this.pollingQueue.order[0];
|
|
4612
4371
|
if (this.pollingQueue[nextCommand].includes(data.context)) {
|
|
4613
4372
|
this.pollingQueue[nextCommand] = this.pollingQueue[nextCommand].filter((c) => c !== data.context);
|
|
4373
|
+
const message = { line: nextCommand };
|
|
4374
|
+
if (nextCommand === proto.startIframe) {
|
|
4375
|
+
message.token = this.session.getSessionSecret(this.projectKey);
|
|
4376
|
+
const targetFrame = this.pageFrames.find((f) => f.contentWindow === event.source) ||
|
|
4377
|
+
Array.from(document.querySelectorAll('iframe')).find((f) => f.contentWindow === event.source);
|
|
4378
|
+
if (targetFrame) {
|
|
4379
|
+
const nodeId = targetFrame[this.options.node_id];
|
|
4380
|
+
if (nodeId !== undefined) {
|
|
4381
|
+
message.id = nodeId;
|
|
4382
|
+
}
|
|
4383
|
+
}
|
|
4384
|
+
}
|
|
4614
4385
|
// @ts-ignore
|
|
4615
|
-
event.source?.postMessage(
|
|
4386
|
+
event.source?.postMessage(message, '*');
|
|
4616
4387
|
if (this.pollingQueue[nextCommand].length === 0) {
|
|
4388
|
+
delete this.pollingQueue[nextCommand];
|
|
4617
4389
|
this.pollingQueue.order.shift();
|
|
4618
4390
|
}
|
|
4619
4391
|
}
|
|
@@ -4627,6 +4399,11 @@ class App {
|
|
|
4627
4399
|
order: [],
|
|
4628
4400
|
};
|
|
4629
4401
|
this.addCommand = (cmd) => {
|
|
4402
|
+
this.pruneStaleFrames();
|
|
4403
|
+
if (this.pollingQueue[cmd]) {
|
|
4404
|
+
this.pollingQueue[cmd] = Array.from(new Set([...this.pollingQueue[cmd], ...this.trackedFrames]));
|
|
4405
|
+
return;
|
|
4406
|
+
}
|
|
4630
4407
|
this.pollingQueue.order.push(cmd);
|
|
4631
4408
|
this.pollingQueue[cmd] = [...this.trackedFrames];
|
|
4632
4409
|
};
|
|
@@ -4755,6 +4532,26 @@ class App {
|
|
|
4755
4532
|
this.restartCanvasTracking = () => {
|
|
4756
4533
|
this.canvasRecorder?.restartTracking();
|
|
4757
4534
|
};
|
|
4535
|
+
this.attachResanitizeCallback = (cb) => {
|
|
4536
|
+
this.resanitizeCallbacks.push(cb);
|
|
4537
|
+
};
|
|
4538
|
+
this.callResanitizeCallbacks = (node, id) => {
|
|
4539
|
+
this.resanitizeCallbacks.forEach((cb) => cb(node, id));
|
|
4540
|
+
};
|
|
4541
|
+
this.resanitize = (el) => {
|
|
4542
|
+
const root = el ?? (IN_BROWSER ? document.documentElement : undefined);
|
|
4543
|
+
if (!root) {
|
|
4544
|
+
return;
|
|
4545
|
+
}
|
|
4546
|
+
this.observer.resanitizeSubtree(root);
|
|
4547
|
+
};
|
|
4548
|
+
this.checkSanitization = (el) => {
|
|
4549
|
+
const id = this.nodes.getID(el);
|
|
4550
|
+
if (id === undefined) {
|
|
4551
|
+
return undefined;
|
|
4552
|
+
}
|
|
4553
|
+
return this.sanitizer.getLevel(id);
|
|
4554
|
+
};
|
|
4758
4555
|
this.flushBuffer = async (buffer) => {
|
|
4759
4556
|
return new Promise((res, reject) => {
|
|
4760
4557
|
if (buffer.length === 0) {
|
|
@@ -4796,6 +4593,7 @@ class App {
|
|
|
4796
4593
|
__is_snippet: false,
|
|
4797
4594
|
__debug_report_edp: null,
|
|
4798
4595
|
__debug__: LogLevel.Silent,
|
|
4596
|
+
__local_debug: false,
|
|
4799
4597
|
localStorage: null,
|
|
4800
4598
|
sessionStorage: null,
|
|
4801
4599
|
forceSingleTab: false,
|
|
@@ -4854,9 +4652,7 @@ class App {
|
|
|
4854
4652
|
});
|
|
4855
4653
|
this.session.attachUpdateCallback(({ userID, metadata }) => {
|
|
4856
4654
|
if (userID != null) {
|
|
4857
|
-
if (!userID ||
|
|
4858
|
-
typeof userID !== 'string' ||
|
|
4859
|
-
userID.trim().length === 0) {
|
|
4655
|
+
if (!userID || typeof userID !== 'string' || userID.trim().length === 0) {
|
|
4860
4656
|
this.debug.warn('Invalid userID (must be type string), ignoring.');
|
|
4861
4657
|
return;
|
|
4862
4658
|
}
|
|
@@ -4878,9 +4674,6 @@ class App {
|
|
|
4878
4674
|
window.addEventListener('message', this.parentCrossDomainFrameListener);
|
|
4879
4675
|
window.addEventListener('message', this.crossDomainIframeListener);
|
|
4880
4676
|
setInterval(() => {
|
|
4881
|
-
if (document.hidden) {
|
|
4882
|
-
return;
|
|
4883
|
-
}
|
|
4884
4677
|
window.parent.postMessage({
|
|
4885
4678
|
line: proto.polling,
|
|
4886
4679
|
context: this.contextId,
|
|
@@ -4943,6 +4736,16 @@ class App {
|
|
|
4943
4736
|
};
|
|
4944
4737
|
}
|
|
4945
4738
|
}
|
|
4739
|
+
pruneStaleFrames() {
|
|
4740
|
+
const staleAfter = Date.now() - this.FRAME_STALE_MS;
|
|
4741
|
+
this.trackedFrames = this.trackedFrames.filter((ctx) => {
|
|
4742
|
+
const last = this.frameLastSeen.get(ctx);
|
|
4743
|
+
if (last !== undefined && last >= staleAfter)
|
|
4744
|
+
return true;
|
|
4745
|
+
this.frameLastSeen.delete(ctx);
|
|
4746
|
+
return false;
|
|
4747
|
+
});
|
|
4748
|
+
}
|
|
4946
4749
|
allowAppStart() {
|
|
4947
4750
|
this.canStart = true;
|
|
4948
4751
|
if (this.startTimeout) {
|
|
@@ -5042,22 +4845,39 @@ class App {
|
|
|
5042
4845
|
}
|
|
5043
4846
|
else if (data.type === 'compress') {
|
|
5044
4847
|
const batch = data.batch;
|
|
4848
|
+
const dataType = data.dataType;
|
|
5045
4849
|
const batchSize = batch.byteLength;
|
|
5046
|
-
|
|
5047
|
-
|
|
5048
|
-
|
|
5049
|
-
|
|
5050
|
-
|
|
5051
|
-
|
|
5052
|
-
|
|
5053
|
-
|
|
5054
|
-
|
|
4850
|
+
const hasCompressionAPI = 'CompressionStream' in globalThis;
|
|
4851
|
+
if (batchSize > this.compressionThreshold && hasCompressionAPI) {
|
|
4852
|
+
const blob = new Blob([batch]);
|
|
4853
|
+
const stream = blob.stream().pipeThrough(new CompressionStream('gzip'));
|
|
4854
|
+
new Response(stream)
|
|
4855
|
+
.arrayBuffer()
|
|
4856
|
+
.then((compressedBuffer) => {
|
|
4857
|
+
this.worker?.postMessage({
|
|
4858
|
+
type: 'compressed',
|
|
4859
|
+
batch: new Uint8Array(compressedBuffer),
|
|
4860
|
+
dataType,
|
|
4861
|
+
});
|
|
4862
|
+
})
|
|
4863
|
+
.catch((err) => {
|
|
4864
|
+
this.debug.error('OxvoSessions compression error:', err);
|
|
4865
|
+
this.worker?.postMessage({ type: 'uncompressed', batch: batch, dataType });
|
|
5055
4866
|
});
|
|
5056
4867
|
}
|
|
5057
4868
|
else {
|
|
5058
|
-
this.worker?.postMessage({ type: 'uncompressed', batch: batch });
|
|
4869
|
+
this.worker?.postMessage({ type: 'uncompressed', batch: batch, dataType });
|
|
5059
4870
|
}
|
|
5060
4871
|
}
|
|
4872
|
+
else if (data.type === 'local_save') {
|
|
4873
|
+
const blob = new Blob([data.batch], { type: 'application/octet-stream' });
|
|
4874
|
+
const url = URL.createObjectURL(blob);
|
|
4875
|
+
const a = document.createElement('a');
|
|
4876
|
+
a.href = url;
|
|
4877
|
+
a.download = data.name;
|
|
4878
|
+
a.click();
|
|
4879
|
+
URL.revokeObjectURL(url);
|
|
4880
|
+
}
|
|
5061
4881
|
else if (data.type === 'queue_empty') {
|
|
5062
4882
|
this.onSessionSent();
|
|
5063
4883
|
}
|
|
@@ -5096,19 +4916,31 @@ class App {
|
|
|
5096
4916
|
this.messages.length = 0;
|
|
5097
4917
|
return;
|
|
5098
4918
|
}
|
|
5099
|
-
if (this.worker === undefined
|
|
4919
|
+
if (this.worker === undefined) {
|
|
5100
4920
|
return;
|
|
5101
4921
|
}
|
|
5102
4922
|
if (!this.messages.length) {
|
|
5103
|
-
//
|
|
4923
|
+
// Send a keepalive batch every ~30s (1000 * 30ms) to keep the session active
|
|
5104
4924
|
if (this.emptyBatchCounter < 1000) {
|
|
5105
4925
|
this.emptyBatchCounter++;
|
|
5106
4926
|
return;
|
|
5107
4927
|
}
|
|
4928
|
+
// Keepalive: send just Timestamp + TabData so the session stays alive
|
|
4929
|
+
this.emptyBatchCounter = 0;
|
|
4930
|
+
try {
|
|
4931
|
+
this.worker?.postMessage([Timestamp(this.timestamp()), TabData(this.session.getTabId())]);
|
|
4932
|
+
}
|
|
4933
|
+
catch (e) {
|
|
4934
|
+
this._debug('worker_keepalive', e);
|
|
4935
|
+
}
|
|
4936
|
+
return;
|
|
5108
4937
|
}
|
|
5109
4938
|
this.emptyBatchCounter = 0;
|
|
5110
4939
|
try {
|
|
5111
4940
|
requestIdleCb(() => {
|
|
4941
|
+
if (!this.messages.length) {
|
|
4942
|
+
return;
|
|
4943
|
+
}
|
|
5112
4944
|
this.messages.unshift(Timestamp(this.timestamp()), TabData(this.session.getTabId()));
|
|
5113
4945
|
this.worker?.postMessage(this.messages);
|
|
5114
4946
|
this.commitCallbacks.forEach((cb) => cb(this.messages));
|
|
@@ -5302,9 +5134,14 @@ class App {
|
|
|
5302
5134
|
}
|
|
5303
5135
|
}
|
|
5304
5136
|
checkSessionToken(startNewSession) {
|
|
5305
|
-
const
|
|
5306
|
-
|
|
5137
|
+
const needReset = this.sessionStorage.getItem(this.options.session_reset_key) !== null;
|
|
5138
|
+
let needNewSessionID = startNewSession || needReset;
|
|
5307
5139
|
const sessionToken = this.session.getSessionSecret(this.projectKey);
|
|
5140
|
+
const storedVersion = this.sessionStorage.getItem(`${this.options.session_token_key}_version`);
|
|
5141
|
+
if (!storedVersion || storedVersion !== PROTO_VERSION) {
|
|
5142
|
+
needNewSessionID = true;
|
|
5143
|
+
this.sessionStorage.setItem(`${this.options.session_token_key}_version`, PROTO_VERSION);
|
|
5144
|
+
}
|
|
5308
5145
|
return needNewSessionID || !sessionToken;
|
|
5309
5146
|
}
|
|
5310
5147
|
/**
|
|
@@ -5482,6 +5319,7 @@ class App {
|
|
|
5482
5319
|
connAttemptCount: this.options.connAttemptCount,
|
|
5483
5320
|
connAttemptGap: this.options.connAttemptGap,
|
|
5484
5321
|
tabId: this.session.getTabId(),
|
|
5322
|
+
localDebug: this.options.__local_debug,
|
|
5485
5323
|
});
|
|
5486
5324
|
const ingestPoint = normalizeEndpoint(this.options.endpoint);
|
|
5487
5325
|
const r = await fetch(ingestPoint + '/v1/bootstrap', {
|
|
@@ -5496,7 +5334,7 @@ class App {
|
|
|
5496
5334
|
token: '',
|
|
5497
5335
|
})),
|
|
5498
5336
|
});
|
|
5499
|
-
const { session = {}, client = {}, limits = {} } = await r.json();
|
|
5337
|
+
const { session = {}, client = {}, limits = {}, protocolVersion: offlineProtocolVersion } = await r.json();
|
|
5500
5338
|
const { token, projectId } = session;
|
|
5501
5339
|
const { browser: userBrowser, city: userCity, country: userCountry, device: userDevice, os: userOS, state: userState, } = client;
|
|
5502
5340
|
const { beacon: beaconSizeLimit } = limits;
|
|
@@ -5504,6 +5342,7 @@ class App {
|
|
|
5504
5342
|
type: 'auth',
|
|
5505
5343
|
token,
|
|
5506
5344
|
beaconSizeLimit,
|
|
5345
|
+
protocolVersion: offlineProtocolVersion,
|
|
5507
5346
|
});
|
|
5508
5347
|
this.session.assign({ projectID: projectId });
|
|
5509
5348
|
this.session.setUserInfo({
|
|
@@ -5524,6 +5363,9 @@ class App {
|
|
|
5524
5363
|
if (Object.keys(startOpts).length !== 0) {
|
|
5525
5364
|
this.prevOpts = startOpts;
|
|
5526
5365
|
}
|
|
5366
|
+
if (startOpts.startCallback) {
|
|
5367
|
+
this.userStartCallback = startOpts.startCallback;
|
|
5368
|
+
}
|
|
5527
5369
|
const isColdStart = this.activityState === ActivityState.ColdStart;
|
|
5528
5370
|
if (isColdStart && this.coldInterval) {
|
|
5529
5371
|
clearInterval(this.coldInterval);
|
|
@@ -5565,6 +5407,7 @@ class App {
|
|
|
5565
5407
|
connAttemptCount: this.options.connAttemptCount,
|
|
5566
5408
|
connAttemptGap: this.options.connAttemptGap,
|
|
5567
5409
|
tabId: this.session.getTabId(),
|
|
5410
|
+
localDebug: this.options.__local_debug,
|
|
5568
5411
|
});
|
|
5569
5412
|
const sessionToken = this.session.getSessionSecret(this.projectKey);
|
|
5570
5413
|
const isNewSession = this.checkSessionToken(startOpts.startNewSession);
|
|
@@ -5580,7 +5423,7 @@ class App {
|
|
|
5580
5423
|
body: JSON.stringify(this.buildStartPayload({
|
|
5581
5424
|
timestamp,
|
|
5582
5425
|
dry: false,
|
|
5583
|
-
bufferMs: timestamp - this.coldStartTs,
|
|
5426
|
+
bufferMs: this.coldStartTs ? timestamp - this.coldStartTs : 0,
|
|
5584
5427
|
token: isNewSession ? '' : sessionToken,
|
|
5585
5428
|
rule: conditionName,
|
|
5586
5429
|
width: window.screen.width,
|
|
@@ -5597,12 +5440,12 @@ class App {
|
|
|
5597
5440
|
this.signalError(reason, []);
|
|
5598
5441
|
return UnsuccessfulStart(reason);
|
|
5599
5442
|
}
|
|
5600
|
-
const { session = {}, device = {}, client = {}, limits = {}, canvas = {}, flags = {} } = await r.json();
|
|
5443
|
+
const { session = {}, device = {}, client = {}, limits = {}, canvas = {}, flags = {}, protocolVersion, } = await r.json();
|
|
5601
5444
|
const { token, id: sessionID, projectId: projectID, delayMs: delay, startedAt: startTimestamp, } = session;
|
|
5602
5445
|
const { id: userUUID } = device;
|
|
5603
5446
|
const { browser: userBrowser, city: userCity, country: userCountry, device: userDevice, os: userOS, state: userState, } = client;
|
|
5604
5447
|
const { beacon: beaconSizeLimit, compressAt: compressionThreshold } = limits;
|
|
5605
|
-
const { enabled: canvasEnabled, quality: canvasQuality, fps: canvasFPS } = canvas;
|
|
5448
|
+
const { enabled: canvasEnabled, quality: canvasQuality, fps: canvasFPS, framesSupport, } = canvas;
|
|
5606
5449
|
const socketOnly = flags?.socketOnly;
|
|
5607
5450
|
if (typeof token !== 'string' ||
|
|
5608
5451
|
typeof userUUID !== 'string' ||
|
|
@@ -5644,6 +5487,7 @@ class App {
|
|
|
5644
5487
|
type: 'auth',
|
|
5645
5488
|
token,
|
|
5646
5489
|
beaconSizeLimit,
|
|
5490
|
+
protocolVersion,
|
|
5647
5491
|
});
|
|
5648
5492
|
}
|
|
5649
5493
|
if (!isNewSession && token === sessionToken) {
|
|
@@ -5659,10 +5503,9 @@ class App {
|
|
|
5659
5503
|
// TODO: start as early as possible (before receiving the token)
|
|
5660
5504
|
/** after start */
|
|
5661
5505
|
this.startCallbacks.forEach((cb) => cb(onStartInfo)); // MBTODO: callbacks after DOM "mounted" (observed)
|
|
5662
|
-
if (
|
|
5663
|
-
|
|
5506
|
+
if (this.userStartCallback) {
|
|
5507
|
+
this.userStartCallback(SuccessfulStart(onStartInfo));
|
|
5664
5508
|
}
|
|
5665
|
-
await this.markerWatcher.fetchTags(normalizeEndpoint(this.options.endpoint), token);
|
|
5666
5509
|
this.activityState = ActivityState.Active;
|
|
5667
5510
|
if (this.options.crossdomain?.enabled) {
|
|
5668
5511
|
void this.bootChildrenFrames();
|
|
@@ -5676,6 +5519,7 @@ class App {
|
|
|
5676
5519
|
isDebug: this.options.canvas.__save_canvas_locally,
|
|
5677
5520
|
fixedScaling: this.options.canvas.fixedCanvasScaling,
|
|
5678
5521
|
useAnimationFrame: this.options.canvas.useAnimationFrame,
|
|
5522
|
+
framesSupport: !!framesSupport,
|
|
5679
5523
|
});
|
|
5680
5524
|
}
|
|
5681
5525
|
/** --------------- COLD START BUFFER ------------------*/
|
|
@@ -5698,6 +5542,7 @@ class App {
|
|
|
5698
5542
|
}
|
|
5699
5543
|
this.ticker.start();
|
|
5700
5544
|
this.canvasRecorder?.startTracking();
|
|
5545
|
+
void this.markerWatcher.fetchTags(normalizeEndpoint(this.options.endpoint), token);
|
|
5701
5546
|
return SuccessfulStart(onStartInfo);
|
|
5702
5547
|
}
|
|
5703
5548
|
catch (reason) {
|
|
@@ -5822,6 +5667,7 @@ class App {
|
|
|
5822
5667
|
this.canvasRecorder?.clear();
|
|
5823
5668
|
this.messages.length = 0;
|
|
5824
5669
|
this.parentActive = false;
|
|
5670
|
+
this.pageFrames = [];
|
|
5825
5671
|
}
|
|
5826
5672
|
finally {
|
|
5827
5673
|
this.activityState = ActivityState.NotActive;
|
|
@@ -6319,6 +6165,12 @@ function Img (app) {
|
|
|
6319
6165
|
sendImgAttrs(node);
|
|
6320
6166
|
observer.observe(node, { attributes: true, attributeFilter: ['src', 'srcset'] });
|
|
6321
6167
|
});
|
|
6168
|
+
// On a runtime level change, re-evaluate placeholder vs real src for this image.
|
|
6169
|
+
app.attachResanitizeCallback((node) => {
|
|
6170
|
+
if (hasTag(node, 'img')) {
|
|
6171
|
+
sendImgAttrs(node);
|
|
6172
|
+
}
|
|
6173
|
+
});
|
|
6322
6174
|
}
|
|
6323
6175
|
|
|
6324
6176
|
const INPUT_TYPES = [
|
|
@@ -6376,19 +6228,24 @@ const labelElementFor = IN_BROWSER && 'labels' in HTMLInputElement.prototype
|
|
|
6376
6228
|
}
|
|
6377
6229
|
}
|
|
6378
6230
|
};
|
|
6379
|
-
function getInputLabel(node) {
|
|
6380
|
-
|
|
6381
|
-
if (
|
|
6382
|
-
|
|
6383
|
-
|
|
6384
|
-
|
|
6385
|
-
|
|
6386
|
-
|
|
6387
|
-
|
|
6388
|
-
|
|
6389
|
-
|
|
6390
|
-
|
|
6391
|
-
|
|
6231
|
+
function getInputLabel(node, customAttributes) {
|
|
6232
|
+
const oxvosessionsLabel = getLabelAttribute(node);
|
|
6233
|
+
if (oxvosessionsLabel !== null)
|
|
6234
|
+
return normSpaces(oxvosessionsLabel).slice(0, 100);
|
|
6235
|
+
const customAttributeLabel = getCustomAttributeLabel(node, customAttributes);
|
|
6236
|
+
if (customAttributeLabel)
|
|
6237
|
+
return customAttributeLabel;
|
|
6238
|
+
if (node.id)
|
|
6239
|
+
return `#${node.id}`;
|
|
6240
|
+
const classLabel = getClassSelector(node);
|
|
6241
|
+
if (classLabel)
|
|
6242
|
+
return classLabel;
|
|
6243
|
+
const labelElement = labelElementFor(node);
|
|
6244
|
+
const label = node.name ||
|
|
6245
|
+
node.placeholder ||
|
|
6246
|
+
(labelElement && labelElement.innerText) ||
|
|
6247
|
+
node.type;
|
|
6248
|
+
return normSpaces(label || '').slice(0, 100);
|
|
6392
6249
|
}
|
|
6393
6250
|
const InputMode = {
|
|
6394
6251
|
Plain: 0,
|
|
@@ -6410,9 +6267,12 @@ function Input (app, opts) {
|
|
|
6410
6267
|
}
|
|
6411
6268
|
else if (app.sanitizer.isObscured(id) ||
|
|
6412
6269
|
(inputMode === InputMode.Plain &&
|
|
6413
|
-
((options.maskInputNumbers &&
|
|
6270
|
+
((options.maskInputNumbers &&
|
|
6271
|
+
node.type !== 'date' &&
|
|
6272
|
+
/\d\d\d\d/.test(value)) ||
|
|
6414
6273
|
(options.maskInputDates && node.type === 'date') ||
|
|
6415
|
-
(options.maskInputEmails &&
|
|
6274
|
+
(options.maskInputEmails &&
|
|
6275
|
+
(node.type === 'email' || !!~value.indexOf('@')))))) {
|
|
6416
6276
|
inputMode = InputMode.Obscured;
|
|
6417
6277
|
}
|
|
6418
6278
|
let mask = 0;
|
|
@@ -6432,11 +6292,24 @@ function Input (app, opts) {
|
|
|
6432
6292
|
const { value, mask } = getInputValue(id, node);
|
|
6433
6293
|
app.send(SetInputValue(id, value, mask));
|
|
6434
6294
|
}
|
|
6295
|
+
const tagSelectorMap = new Map();
|
|
6435
6296
|
const inputValues = new Map();
|
|
6436
6297
|
const checkboxValues = new Map();
|
|
6298
|
+
function getTagSelector(id, node) {
|
|
6299
|
+
const cached = tagSelectorMap.get(id);
|
|
6300
|
+
if (cached !== undefined)
|
|
6301
|
+
return cached;
|
|
6302
|
+
const tagMatch = app.tagMatcher.match(node);
|
|
6303
|
+
if (tagMatch) {
|
|
6304
|
+
tagSelectorMap.set(id, tagMatch.selector);
|
|
6305
|
+
return tagMatch.selector;
|
|
6306
|
+
}
|
|
6307
|
+
return null;
|
|
6308
|
+
}
|
|
6437
6309
|
app.attachStopCallback(() => {
|
|
6438
6310
|
inputValues.clear();
|
|
6439
6311
|
checkboxValues.clear();
|
|
6312
|
+
tagSelectorMap.clear();
|
|
6440
6313
|
});
|
|
6441
6314
|
function trackInputValue(id, node) {
|
|
6442
6315
|
if (inputValues.get(id) === node.value) {
|
|
@@ -6469,12 +6342,19 @@ function Input (app, opts) {
|
|
|
6469
6342
|
}, 3);
|
|
6470
6343
|
function sendInputChange(id, node, hesitationTime, inputTime) {
|
|
6471
6344
|
const { value, mask } = getInputValue(id, node);
|
|
6472
|
-
let label = getInputLabel(node);
|
|
6345
|
+
let label = getTagSelector(id, node) || getInputLabel(node, options.customAttributes);
|
|
6473
6346
|
if (app.sanitizer.maskAllByDefault) {
|
|
6474
6347
|
label = label.replaceAll(/./g, '*');
|
|
6475
6348
|
}
|
|
6476
6349
|
app.send(InputChange(id, value, mask !== 0, label, hesitationTime, inputTime));
|
|
6477
6350
|
}
|
|
6351
|
+
// Re-emit a field's value when its sanitization level changes at runtime.
|
|
6352
|
+
// getInputValue() reads the current level, so re-sending applies the new mask.
|
|
6353
|
+
app.attachResanitizeCallback((node, id) => {
|
|
6354
|
+
if (isTextFieldElement(node) || hasTag(node, 'select')) {
|
|
6355
|
+
sendInputValue(id, node);
|
|
6356
|
+
}
|
|
6357
|
+
});
|
|
6478
6358
|
app.nodes.attachNodeCallback(app.safe((node) => {
|
|
6479
6359
|
const id = app.nodes.getID(node);
|
|
6480
6360
|
if (id === undefined) {
|
|
@@ -6578,21 +6458,28 @@ function _getTarget(target, document) {
|
|
|
6578
6458
|
return target === document.documentElement ? null : target;
|
|
6579
6459
|
}
|
|
6580
6460
|
function Mouse (app, options) {
|
|
6581
|
-
const { disableClickmaps = false } = options || {};
|
|
6461
|
+
const { disableClickmaps = false, customAttributes } = options || {};
|
|
6582
6462
|
function getTargetLabel(target) {
|
|
6583
6463
|
const dl = getLabelAttribute(target);
|
|
6584
6464
|
if (dl !== null) {
|
|
6585
6465
|
return dl;
|
|
6586
6466
|
}
|
|
6587
6467
|
if (hasTag(target, 'input')) {
|
|
6588
|
-
return getInputLabel(target);
|
|
6589
|
-
}
|
|
6468
|
+
return getInputLabel(target, customAttributes);
|
|
6469
|
+
}
|
|
6470
|
+
const customAttributeLabel = getCustomAttributeLabel(target, customAttributes);
|
|
6471
|
+
if (customAttributeLabel)
|
|
6472
|
+
return customAttributeLabel;
|
|
6473
|
+
if (target.id)
|
|
6474
|
+
return `#${target.id}`;
|
|
6475
|
+
const classLabel = getClassSelector(target);
|
|
6476
|
+
if (classLabel)
|
|
6477
|
+
return classLabel;
|
|
6590
6478
|
if (isClickable(target)) {
|
|
6591
6479
|
let label = '';
|
|
6592
6480
|
if (target instanceof HTMLElement) {
|
|
6593
6481
|
label = app.sanitizer.getInnerTextSecure(target);
|
|
6594
6482
|
}
|
|
6595
|
-
label = label || target.id || target.className;
|
|
6596
6483
|
return normSpaces(label).slice(0, 100);
|
|
6597
6484
|
}
|
|
6598
6485
|
return '';
|
|
@@ -6645,7 +6532,13 @@ function Mouse (app, options) {
|
|
|
6645
6532
|
};
|
|
6646
6533
|
const patchDocument = (document, topframe = false) => {
|
|
6647
6534
|
function getSelector(id, target) {
|
|
6648
|
-
|
|
6535
|
+
if (selectorMap[id])
|
|
6536
|
+
return selectorMap[id];
|
|
6537
|
+
const tagMatch = app.tagMatcher.match(target);
|
|
6538
|
+
if (tagMatch) {
|
|
6539
|
+
return (selectorMap[id] = tagMatch.selector);
|
|
6540
|
+
}
|
|
6541
|
+
return (selectorMap[id] = _getSelector(target));
|
|
6649
6542
|
}
|
|
6650
6543
|
const attachListener = topframe
|
|
6651
6544
|
? app.attachEventListener.bind(app) // attached/removed on start/stop
|
|
@@ -6736,6 +6629,11 @@ function getCSSPath(el) {
|
|
|
6736
6629
|
return false;
|
|
6737
6630
|
if (el.id)
|
|
6738
6631
|
return `#${cssEscape(el.id)}`;
|
|
6632
|
+
// if has data attributes - use them as they are more likely to be stable and unique
|
|
6633
|
+
const dataAttr = Array.from(el.attributes).find(attr => attr.name.startsWith('data-'));
|
|
6634
|
+
if (dataAttr) {
|
|
6635
|
+
return `[${dataAttr.name}="${cssEscape(dataAttr.value)}"]`;
|
|
6636
|
+
}
|
|
6739
6637
|
const parts = [];
|
|
6740
6638
|
while (el && el.nodeType === 1 && el !== el.ownerDocument) {
|
|
6741
6639
|
if (el.id) {
|
|
@@ -7076,11 +6974,27 @@ function Scroll (app, insideIframe) {
|
|
|
7076
6974
|
}, 5, false);
|
|
7077
6975
|
}
|
|
7078
6976
|
|
|
6977
|
+
const NativeURL = URL;
|
|
6978
|
+
const defaultUrlSanitizer = (url) => {
|
|
6979
|
+
const hiddenQueryParams = ['jwt', 'password', 'reset-password', 'invitation', 'secret', 'token'];
|
|
6980
|
+
try {
|
|
6981
|
+
const u = new NativeURL(url);
|
|
6982
|
+
u.searchParams.forEach((value, key) => {
|
|
6983
|
+
if (hiddenQueryParams.includes(key.toLowerCase())) {
|
|
6984
|
+
u.searchParams.set(key, '*'.repeat(value.length));
|
|
6985
|
+
}
|
|
6986
|
+
});
|
|
6987
|
+
return u.toString();
|
|
6988
|
+
}
|
|
6989
|
+
catch {
|
|
6990
|
+
return url;
|
|
6991
|
+
}
|
|
6992
|
+
};
|
|
7079
6993
|
function Viewport (app, options) {
|
|
7080
6994
|
let url, width, height;
|
|
7081
6995
|
let navigationStart;
|
|
7082
6996
|
let referrer = document.referrer;
|
|
7083
|
-
const urlSanitizer = options?.urlSanitizer ||
|
|
6997
|
+
const urlSanitizer = options?.urlSanitizer || defaultUrlSanitizer;
|
|
7084
6998
|
const titleSanitizer = options?.titleSanitizer || ((t) => t);
|
|
7085
6999
|
const sendSetPageLocation = app.safe(() => {
|
|
7086
7000
|
const currURL = document.URL;
|
|
@@ -8906,6 +8820,8 @@ function webAnimations(app, options = {}) {
|
|
|
8906
8820
|
const lastKF = anim.effect.getKeyframes().at(-1);
|
|
8907
8821
|
if (!lastKF)
|
|
8908
8822
|
return;
|
|
8823
|
+
if (!el || !(el instanceof Element) || !el.isConnected)
|
|
8824
|
+
return;
|
|
8909
8825
|
const computedStyle = getComputedStyle(el);
|
|
8910
8826
|
const keys = Object.keys(lastKF).filter((p) => !toIgnore.includes(p));
|
|
8911
8827
|
// @ts-ignore
|
|
@@ -9317,7 +9233,7 @@ class ConstantProperties {
|
|
|
9317
9233
|
user_id: this.user_id,
|
|
9318
9234
|
distinct_id: this.deviceId,
|
|
9319
9235
|
sdk_edition: 'web',
|
|
9320
|
-
sdk_version: '7.
|
|
9236
|
+
sdk_version: '7.3.0',
|
|
9321
9237
|
timezone: getUTCOffsetString(),
|
|
9322
9238
|
search_engine: this.searchEngine,
|
|
9323
9239
|
};
|
|
@@ -9705,10 +9621,11 @@ class People {
|
|
|
9705
9621
|
* Creates batches of events, then sends them at intervals.
|
|
9706
9622
|
*/
|
|
9707
9623
|
class Batcher {
|
|
9708
|
-
constructor(backendUrl, getToken, init) {
|
|
9624
|
+
constructor(backendUrl, getToken, init, standalone) {
|
|
9709
9625
|
this.backendUrl = backendUrl;
|
|
9710
9626
|
this.getToken = getToken;
|
|
9711
9627
|
this.init = init;
|
|
9628
|
+
this.standalone = standalone;
|
|
9712
9629
|
this.autosendInterval = 5 * 1000;
|
|
9713
9630
|
this.retryTimeout = 3 * 1000;
|
|
9714
9631
|
this.retryLimit = 3;
|
|
@@ -9718,10 +9635,20 @@ class Batcher {
|
|
|
9718
9635
|
[categories.events]: [],
|
|
9719
9636
|
};
|
|
9720
9637
|
this.intervalId = null;
|
|
9638
|
+
this.stopped = false;
|
|
9639
|
+
this.paused = false;
|
|
9640
|
+
this.onVisibilityChange = () => {
|
|
9641
|
+
this.paused = document.hidden;
|
|
9642
|
+
};
|
|
9721
9643
|
}
|
|
9722
9644
|
getBatches() {
|
|
9723
9645
|
this.batch[categories.people] = this.dedupePeopleEvents();
|
|
9724
|
-
const finalData = {
|
|
9646
|
+
const finalData = {
|
|
9647
|
+
data: {
|
|
9648
|
+
[categories.people]: [...this.batch[categories.people]],
|
|
9649
|
+
[categories.events]: [...this.batch[categories.events]],
|
|
9650
|
+
},
|
|
9651
|
+
};
|
|
9725
9652
|
return finalData;
|
|
9726
9653
|
}
|
|
9727
9654
|
addEvent(event) {
|
|
@@ -9797,6 +9724,9 @@ class Batcher {
|
|
|
9797
9724
|
return Array.from(uniqueEventsByType.values());
|
|
9798
9725
|
}
|
|
9799
9726
|
sendBatch(batch) {
|
|
9727
|
+
if (this.stopped) {
|
|
9728
|
+
return;
|
|
9729
|
+
}
|
|
9800
9730
|
const sentBatch = batch;
|
|
9801
9731
|
let attempts = 0;
|
|
9802
9732
|
const send = () => {
|
|
@@ -9813,11 +9743,27 @@ class Batcher {
|
|
|
9813
9743
|
},
|
|
9814
9744
|
body: JSON.stringify(sentBatch),
|
|
9815
9745
|
})
|
|
9816
|
-
.then((response) => {
|
|
9746
|
+
.then(async (response) => {
|
|
9747
|
+
if (response.status === 401) {
|
|
9748
|
+
const body = await response.json().catch(() => null);
|
|
9749
|
+
if (!this.standalone && body?.error === 'token expired') {
|
|
9750
|
+
this.stop();
|
|
9751
|
+
return;
|
|
9752
|
+
}
|
|
9753
|
+
if (attempts < this.retryLimit) {
|
|
9754
|
+
return this.init().then(() => {
|
|
9755
|
+
send();
|
|
9756
|
+
});
|
|
9757
|
+
}
|
|
9758
|
+
return;
|
|
9759
|
+
}
|
|
9817
9760
|
if (response.status === 403) {
|
|
9818
|
-
this.
|
|
9819
|
-
|
|
9820
|
-
|
|
9761
|
+
if (attempts < this.retryLimit) {
|
|
9762
|
+
return this.init().then(() => {
|
|
9763
|
+
send();
|
|
9764
|
+
});
|
|
9765
|
+
}
|
|
9766
|
+
return;
|
|
9821
9767
|
}
|
|
9822
9768
|
if (!response.ok) {
|
|
9823
9769
|
throw new Error(`HTTP error! status: ${response.status}`);
|
|
@@ -9832,8 +9778,15 @@ class Batcher {
|
|
|
9832
9778
|
void send();
|
|
9833
9779
|
}
|
|
9834
9780
|
startAutosend() {
|
|
9781
|
+
if (this.intervalId) {
|
|
9782
|
+
clearInterval(this.intervalId);
|
|
9783
|
+
}
|
|
9784
|
+
this.paused = document.hidden;
|
|
9785
|
+
document.addEventListener('visibilitychange', this.onVisibilityChange);
|
|
9835
9786
|
this.intervalId = setInterval(() => {
|
|
9836
|
-
this.
|
|
9787
|
+
if (!this.paused) {
|
|
9788
|
+
this.flush();
|
|
9789
|
+
}
|
|
9837
9790
|
}, this.autosendInterval);
|
|
9838
9791
|
}
|
|
9839
9792
|
flush() {
|
|
@@ -9853,6 +9806,12 @@ class Batcher {
|
|
|
9853
9806
|
clearInterval(this.intervalId);
|
|
9854
9807
|
this.intervalId = null;
|
|
9855
9808
|
}
|
|
9809
|
+
document.removeEventListener('visibilitychange', this.onVisibilityChange);
|
|
9810
|
+
this.stopped = true;
|
|
9811
|
+
}
|
|
9812
|
+
restart() {
|
|
9813
|
+
this.stopped = false;
|
|
9814
|
+
this.startAutosend();
|
|
9856
9815
|
}
|
|
9857
9816
|
}
|
|
9858
9817
|
|
|
@@ -9884,7 +9843,9 @@ class Analytics {
|
|
|
9884
9843
|
if (this.standalone) {
|
|
9885
9844
|
return this.token;
|
|
9886
9845
|
}
|
|
9887
|
-
|
|
9846
|
+
else {
|
|
9847
|
+
return this.getToken();
|
|
9848
|
+
}
|
|
9888
9849
|
};
|
|
9889
9850
|
this._getTimestamp = () => {
|
|
9890
9851
|
if (this.standalone) {
|
|
@@ -9938,6 +9899,19 @@ class Analytics {
|
|
|
9938
9899
|
}
|
|
9939
9900
|
}
|
|
9940
9901
|
};
|
|
9902
|
+
/**
|
|
9903
|
+
* Used by tracker when running in blundled mode
|
|
9904
|
+
*/
|
|
9905
|
+
this.onStart = () => {
|
|
9906
|
+
if (!this.standalone) {
|
|
9907
|
+
this.batcher.restart();
|
|
9908
|
+
}
|
|
9909
|
+
};
|
|
9910
|
+
this.onStop = () => {
|
|
9911
|
+
if (!this.standalone) {
|
|
9912
|
+
this.batcher.stop();
|
|
9913
|
+
}
|
|
9914
|
+
};
|
|
9941
9915
|
this.reset = () => {
|
|
9942
9916
|
this.people.reset(true);
|
|
9943
9917
|
this.events.reset();
|
|
@@ -9976,7 +9950,7 @@ class Analytics {
|
|
|
9976
9950
|
this.standalone = !options.notStandalone;
|
|
9977
9951
|
this.token = this.sessionStorage.getItem(STORAGEKEY);
|
|
9978
9952
|
this.constantProperties = new ConstantProperties(this.localStorage, this.sessionStorage);
|
|
9979
|
-
this.batcher = new Batcher(this.backendUrl, this._getToken, this.init);
|
|
9953
|
+
this.batcher = new Batcher(this.backendUrl, this._getToken, this.init, this.standalone);
|
|
9980
9954
|
this.events = new Events(this.constantProperties, this._getTimestamp, this.batcher);
|
|
9981
9955
|
this.people = new People(this.constantProperties, this._getTimestamp, this.setUserId, this.batcher);
|
|
9982
9956
|
if (options.notStandalone) {
|
|
@@ -10029,7 +10003,7 @@ class API {
|
|
|
10029
10003
|
this.signalStartIssue = (reason, missingApi) => {
|
|
10030
10004
|
const doNotTrack = this.checkDoNotTrack();
|
|
10031
10005
|
console.log("Tracker couldn't start due to:", JSON.stringify({
|
|
10032
|
-
trackerVersion: '7.
|
|
10006
|
+
trackerVersion: '7.3.0',
|
|
10033
10007
|
siteKey: this.options.siteKey,
|
|
10034
10008
|
doNotTrack,
|
|
10035
10009
|
reason: missingApi.length ? `missing api: ${missingApi.join(',')}` : reason,
|
|
@@ -10041,6 +10015,31 @@ class API {
|
|
|
10041
10015
|
}
|
|
10042
10016
|
this.app.restartCanvasTracking();
|
|
10043
10017
|
};
|
|
10018
|
+
/**
|
|
10019
|
+
* Re-evaluates sanitization against the current DOM and re-emits whatever
|
|
10020
|
+
* changed, updating already-recorded nodes mid-session. Call after toggling
|
|
10021
|
+
* `data-oxvosessions-*` attributes or after changing whatever your `domSanitizer`
|
|
10022
|
+
* keys on (class/id/etc).
|
|
10023
|
+
*
|
|
10024
|
+
* @param el - the highest node you changed; omit to re-scan the whole document;
|
|
10025
|
+
* scanning the entire doc is O(dom size)
|
|
10026
|
+
* */
|
|
10027
|
+
this.resanitize = (el) => {
|
|
10028
|
+
if (this.app === null) {
|
|
10029
|
+
return;
|
|
10030
|
+
}
|
|
10031
|
+
this.app.resanitize(el);
|
|
10032
|
+
};
|
|
10033
|
+
/**
|
|
10034
|
+
* Returns the sanitization level the tracker currently has for a node
|
|
10035
|
+
* (0 = Plain, 1 = Obscured, 2 = Hidden), or undefined if it isn't tracked.
|
|
10036
|
+
* */
|
|
10037
|
+
this.checkSanitization = (el) => {
|
|
10038
|
+
if (this.app === null) {
|
|
10039
|
+
return undefined;
|
|
10040
|
+
}
|
|
10041
|
+
return this.app.checkSanitization(el);
|
|
10042
|
+
};
|
|
10044
10043
|
this.getSessionLink = (options) => {
|
|
10045
10044
|
if (this.app === null) {
|
|
10046
10045
|
return undefined;
|
|
@@ -10183,6 +10182,12 @@ class API {
|
|
|
10183
10182
|
: (options.analytics?.endpoint ?? options.endpoint ?? defaultEdp),
|
|
10184
10183
|
projectKey: options.siteKey,
|
|
10185
10184
|
});
|
|
10185
|
+
app.attachStartCallback(() => {
|
|
10186
|
+
this.analytics?.onStart();
|
|
10187
|
+
});
|
|
10188
|
+
app.attachStopCallback(() => {
|
|
10189
|
+
this.analytics?.onStop();
|
|
10190
|
+
});
|
|
10186
10191
|
}
|
|
10187
10192
|
if (!this.crossdomainMode) {
|
|
10188
10193
|
// no need to send iframe viewport data since its a node for us
|
|
@@ -10194,7 +10199,7 @@ class API {
|
|
|
10194
10199
|
// no tabs in iframes yet
|
|
10195
10200
|
Tabs(app);
|
|
10196
10201
|
}
|
|
10197
|
-
Mouse(app, options.mouse);
|
|
10202
|
+
Mouse(app, { ...options.mouse, customAttributes: options.customAttributes });
|
|
10198
10203
|
// inside iframe, we ignore viewport scroll
|
|
10199
10204
|
Scroll(app, this.crossdomainMode);
|
|
10200
10205
|
CSSRules(app, options.css);
|