@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/lib/index.js
CHANGED
|
@@ -1,716 +1,3 @@
|
|
|
1
|
-
// DEFLATE is a complex format; to read this code, you should probably check the RFC first:
|
|
2
|
-
// https://tools.ietf.org/html/rfc1951
|
|
3
|
-
// You may also wish to take a look at the guide I made about this program:
|
|
4
|
-
// https://gist.github.com/101arrowz/253f31eb5abc3d9275ab943003ffecad
|
|
5
|
-
// Some of the following code is similar to that of UZIP.js:
|
|
6
|
-
// https://github.com/photopea/UZIP.js
|
|
7
|
-
// However, the vast majority of the codebase has diverged from UZIP.js to increase performance and reduce bundle size.
|
|
8
|
-
// Sometimes 0 will appear where -1 would be more appropriate. This is because using a uint
|
|
9
|
-
// is better for memory in most engines (I *think*).
|
|
10
|
-
var ch2 = {};
|
|
11
|
-
var wk = (function (c, id, msg, transfer, cb) {
|
|
12
|
-
var w = new Worker(ch2[id] || (ch2[id] = URL.createObjectURL(new Blob([
|
|
13
|
-
c + ';addEventListener("error",function(e){e=e.error;postMessage({$e$:[e.message,e.code,e.stack]})})'
|
|
14
|
-
], { type: 'text/javascript' }))));
|
|
15
|
-
w.onmessage = function (e) {
|
|
16
|
-
var d = e.data, ed = d.$e$;
|
|
17
|
-
if (ed) {
|
|
18
|
-
var err = new Error(ed[0]);
|
|
19
|
-
err['code'] = ed[1];
|
|
20
|
-
err.stack = ed[2];
|
|
21
|
-
cb(err, null);
|
|
22
|
-
}
|
|
23
|
-
else
|
|
24
|
-
cb(null, d);
|
|
25
|
-
};
|
|
26
|
-
w.postMessage(msg, transfer);
|
|
27
|
-
return w;
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
// aliases for shorter compressed code (most minifers don't do this)
|
|
31
|
-
var u8 = Uint8Array, u16 = Uint16Array, i32 = Int32Array;
|
|
32
|
-
// fixed length extra bits
|
|
33
|
-
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]);
|
|
34
|
-
// fixed distance extra bits
|
|
35
|
-
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]);
|
|
36
|
-
// code length index map
|
|
37
|
-
var clim = new u8([16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15]);
|
|
38
|
-
// get base, reverse index map from extra bits
|
|
39
|
-
var freb = function (eb, start) {
|
|
40
|
-
var b = new u16(31);
|
|
41
|
-
for (var i = 0; i < 31; ++i) {
|
|
42
|
-
b[i] = start += 1 << eb[i - 1];
|
|
43
|
-
}
|
|
44
|
-
// numbers here are at max 18 bits
|
|
45
|
-
var r = new i32(b[30]);
|
|
46
|
-
for (var i = 1; i < 30; ++i) {
|
|
47
|
-
for (var j = b[i]; j < b[i + 1]; ++j) {
|
|
48
|
-
r[j] = ((j - b[i]) << 5) | i;
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
return { b: b, r: r };
|
|
52
|
-
};
|
|
53
|
-
var _a = freb(fleb, 2), fl = _a.b, revfl = _a.r;
|
|
54
|
-
// we can ignore the fact that the other numbers are wrong; they never happen anyway
|
|
55
|
-
fl[28] = 258, revfl[258] = 28;
|
|
56
|
-
var _b = freb(fdeb, 0), revfd = _b.r;
|
|
57
|
-
// map of value to reverse (assuming 16 bits)
|
|
58
|
-
var rev = new u16(32768);
|
|
59
|
-
for (var i$1 = 0; i$1 < 32768; ++i$1) {
|
|
60
|
-
// reverse table algorithm from SO
|
|
61
|
-
var x$1 = ((i$1 & 0xAAAA) >> 1) | ((i$1 & 0x5555) << 1);
|
|
62
|
-
x$1 = ((x$1 & 0xCCCC) >> 2) | ((x$1 & 0x3333) << 2);
|
|
63
|
-
x$1 = ((x$1 & 0xF0F0) >> 4) | ((x$1 & 0x0F0F) << 4);
|
|
64
|
-
rev[i$1] = (((x$1 & 0xFF00) >> 8) | ((x$1 & 0x00FF) << 8)) >> 1;
|
|
65
|
-
}
|
|
66
|
-
// create huffman tree from u8 "map": index -> code length for code index
|
|
67
|
-
// mb (max bits) must be at most 15
|
|
68
|
-
// TODO: optimize/split up?
|
|
69
|
-
var hMap = (function (cd, mb, r) {
|
|
70
|
-
var s = cd.length;
|
|
71
|
-
// index
|
|
72
|
-
var i = 0;
|
|
73
|
-
// u16 "map": index -> # of codes with bit length = index
|
|
74
|
-
var l = new u16(mb);
|
|
75
|
-
// length of cd must be 288 (total # of codes)
|
|
76
|
-
for (; i < s; ++i) {
|
|
77
|
-
if (cd[i])
|
|
78
|
-
++l[cd[i] - 1];
|
|
79
|
-
}
|
|
80
|
-
// u16 "map": index -> minimum code for bit length = index
|
|
81
|
-
var le = new u16(mb);
|
|
82
|
-
for (i = 1; i < mb; ++i) {
|
|
83
|
-
le[i] = (le[i - 1] + l[i - 1]) << 1;
|
|
84
|
-
}
|
|
85
|
-
var co;
|
|
86
|
-
if (r) {
|
|
87
|
-
// u16 "map": index -> number of actual bits, symbol for code
|
|
88
|
-
co = new u16(1 << mb);
|
|
89
|
-
// bits to remove for reverser
|
|
90
|
-
var rvb = 15 - mb;
|
|
91
|
-
for (i = 0; i < s; ++i) {
|
|
92
|
-
// ignore 0 lengths
|
|
93
|
-
if (cd[i]) {
|
|
94
|
-
// num encoding both symbol and bits read
|
|
95
|
-
var sv = (i << 4) | cd[i];
|
|
96
|
-
// free bits
|
|
97
|
-
var r_1 = mb - cd[i];
|
|
98
|
-
// start value
|
|
99
|
-
var v = le[cd[i] - 1]++ << r_1;
|
|
100
|
-
// m is end value
|
|
101
|
-
for (var m = v | ((1 << r_1) - 1); v <= m; ++v) {
|
|
102
|
-
// every 16 bit value starting with the code yields the same result
|
|
103
|
-
co[rev[v] >> rvb] = sv;
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
else {
|
|
109
|
-
co = new u16(s);
|
|
110
|
-
for (i = 0; i < s; ++i) {
|
|
111
|
-
if (cd[i]) {
|
|
112
|
-
co[i] = rev[le[cd[i] - 1]++] >> (15 - cd[i]);
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
return co;
|
|
117
|
-
});
|
|
118
|
-
// fixed length tree
|
|
119
|
-
var flt = new u8(288);
|
|
120
|
-
for (var i$1 = 0; i$1 < 144; ++i$1)
|
|
121
|
-
flt[i$1] = 8;
|
|
122
|
-
for (var i$1 = 144; i$1 < 256; ++i$1)
|
|
123
|
-
flt[i$1] = 9;
|
|
124
|
-
for (var i$1 = 256; i$1 < 280; ++i$1)
|
|
125
|
-
flt[i$1] = 7;
|
|
126
|
-
for (var i$1 = 280; i$1 < 288; ++i$1)
|
|
127
|
-
flt[i$1] = 8;
|
|
128
|
-
// fixed distance tree
|
|
129
|
-
var fdt = new u8(32);
|
|
130
|
-
for (var i$1 = 0; i$1 < 32; ++i$1)
|
|
131
|
-
fdt[i$1] = 5;
|
|
132
|
-
// fixed length map
|
|
133
|
-
var flm = /*#__PURE__*/ hMap(flt, 9, 0);
|
|
134
|
-
// fixed distance map
|
|
135
|
-
var fdm = /*#__PURE__*/ hMap(fdt, 5, 0);
|
|
136
|
-
// get end of byte
|
|
137
|
-
var shft = function (p) { return ((p + 7) / 8) | 0; };
|
|
138
|
-
// typed array slice - allows garbage collector to free original reference,
|
|
139
|
-
// while being more compatible than .slice
|
|
140
|
-
var slc = function (v, s, e) {
|
|
141
|
-
if (s == null || s < 0)
|
|
142
|
-
s = 0;
|
|
143
|
-
if (e == null || e > v.length)
|
|
144
|
-
e = v.length;
|
|
145
|
-
// can't use .constructor in case user-supplied
|
|
146
|
-
return new u8(v.subarray(s, e));
|
|
147
|
-
};
|
|
148
|
-
// error codes
|
|
149
|
-
var ec = [
|
|
150
|
-
'unexpected EOF',
|
|
151
|
-
'invalid block type',
|
|
152
|
-
'invalid length/literal',
|
|
153
|
-
'invalid distance',
|
|
154
|
-
'stream finished',
|
|
155
|
-
'no stream handler',
|
|
156
|
-
,
|
|
157
|
-
'no callback',
|
|
158
|
-
'invalid UTF-8 data',
|
|
159
|
-
'extra field too long',
|
|
160
|
-
'date not in range 1980-2099',
|
|
161
|
-
'filename too long',
|
|
162
|
-
'stream finishing',
|
|
163
|
-
'invalid zip data'
|
|
164
|
-
// determined by unknown compression method
|
|
165
|
-
];
|
|
166
|
-
var err = function (ind, msg, nt) {
|
|
167
|
-
var e = new Error(msg || ec[ind]);
|
|
168
|
-
e.code = ind;
|
|
169
|
-
if (Error.captureStackTrace)
|
|
170
|
-
Error.captureStackTrace(e, err);
|
|
171
|
-
if (!nt)
|
|
172
|
-
throw e;
|
|
173
|
-
return e;
|
|
174
|
-
};
|
|
175
|
-
// starting at p, write the minimum number of bits that can hold v to d
|
|
176
|
-
var wbits = function (d, p, v) {
|
|
177
|
-
v <<= p & 7;
|
|
178
|
-
var o = (p / 8) | 0;
|
|
179
|
-
d[o] |= v;
|
|
180
|
-
d[o + 1] |= v >> 8;
|
|
181
|
-
};
|
|
182
|
-
// starting at p, write the minimum number of bits (>8) that can hold v to d
|
|
183
|
-
var wbits16 = function (d, p, v) {
|
|
184
|
-
v <<= p & 7;
|
|
185
|
-
var o = (p / 8) | 0;
|
|
186
|
-
d[o] |= v;
|
|
187
|
-
d[o + 1] |= v >> 8;
|
|
188
|
-
d[o + 2] |= v >> 16;
|
|
189
|
-
};
|
|
190
|
-
// creates code lengths from a frequency table
|
|
191
|
-
var hTree = function (d, mb) {
|
|
192
|
-
// Need extra info to make a tree
|
|
193
|
-
var t = [];
|
|
194
|
-
for (var i = 0; i < d.length; ++i) {
|
|
195
|
-
if (d[i])
|
|
196
|
-
t.push({ s: i, f: d[i] });
|
|
197
|
-
}
|
|
198
|
-
var s = t.length;
|
|
199
|
-
var t2 = t.slice();
|
|
200
|
-
if (!s)
|
|
201
|
-
return { t: et, l: 0 };
|
|
202
|
-
if (s == 1) {
|
|
203
|
-
var v = new u8(t[0].s + 1);
|
|
204
|
-
v[t[0].s] = 1;
|
|
205
|
-
return { t: v, l: 1 };
|
|
206
|
-
}
|
|
207
|
-
t.sort(function (a, b) { return a.f - b.f; });
|
|
208
|
-
// after i2 reaches last ind, will be stopped
|
|
209
|
-
// freq must be greater than largest possible number of symbols
|
|
210
|
-
t.push({ s: -1, f: 25001 });
|
|
211
|
-
var l = t[0], r = t[1], i0 = 0, i1 = 1, i2 = 2;
|
|
212
|
-
t[0] = { s: -1, f: l.f + r.f, l: l, r: r };
|
|
213
|
-
// efficient algorithm from UZIP.js
|
|
214
|
-
// i0 is lookbehind, i2 is lookahead - after processing two low-freq
|
|
215
|
-
// symbols that combined have high freq, will start processing i2 (high-freq,
|
|
216
|
-
// non-composite) symbols instead
|
|
217
|
-
// see https://reddit.com/r/photopea/comments/ikekht/uzipjs_questions/
|
|
218
|
-
while (i1 != s - 1) {
|
|
219
|
-
l = t[t[i0].f < t[i2].f ? i0++ : i2++];
|
|
220
|
-
r = t[i0 != i1 && t[i0].f < t[i2].f ? i0++ : i2++];
|
|
221
|
-
t[i1++] = { s: -1, f: l.f + r.f, l: l, r: r };
|
|
222
|
-
}
|
|
223
|
-
var maxSym = t2[0].s;
|
|
224
|
-
for (var i = 1; i < s; ++i) {
|
|
225
|
-
if (t2[i].s > maxSym)
|
|
226
|
-
maxSym = t2[i].s;
|
|
227
|
-
}
|
|
228
|
-
// code lengths
|
|
229
|
-
var tr = new u16(maxSym + 1);
|
|
230
|
-
// max bits in tree
|
|
231
|
-
var mbt = ln(t[i1 - 1], tr, 0);
|
|
232
|
-
if (mbt > mb) {
|
|
233
|
-
// more algorithms from UZIP.js
|
|
234
|
-
// TODO: find out how this code works (debt)
|
|
235
|
-
// ind debt
|
|
236
|
-
var i = 0, dt = 0;
|
|
237
|
-
// left cost
|
|
238
|
-
var lft = mbt - mb, cst = 1 << lft;
|
|
239
|
-
t2.sort(function (a, b) { return tr[b.s] - tr[a.s] || a.f - b.f; });
|
|
240
|
-
for (; i < s; ++i) {
|
|
241
|
-
var i2_1 = t2[i].s;
|
|
242
|
-
if (tr[i2_1] > mb) {
|
|
243
|
-
dt += cst - (1 << (mbt - tr[i2_1]));
|
|
244
|
-
tr[i2_1] = mb;
|
|
245
|
-
}
|
|
246
|
-
else
|
|
247
|
-
break;
|
|
248
|
-
}
|
|
249
|
-
dt >>= lft;
|
|
250
|
-
while (dt > 0) {
|
|
251
|
-
var i2_2 = t2[i].s;
|
|
252
|
-
if (tr[i2_2] < mb)
|
|
253
|
-
dt -= 1 << (mb - tr[i2_2]++ - 1);
|
|
254
|
-
else
|
|
255
|
-
++i;
|
|
256
|
-
}
|
|
257
|
-
for (; i >= 0 && dt; --i) {
|
|
258
|
-
var i2_3 = t2[i].s;
|
|
259
|
-
if (tr[i2_3] == mb) {
|
|
260
|
-
--tr[i2_3];
|
|
261
|
-
++dt;
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
mbt = mb;
|
|
265
|
-
}
|
|
266
|
-
return { t: new u8(tr), l: mbt };
|
|
267
|
-
};
|
|
268
|
-
// get the max length and assign length codes
|
|
269
|
-
var ln = function (n, l, d) {
|
|
270
|
-
return n.s == -1
|
|
271
|
-
? Math.max(ln(n.l, l, d + 1), ln(n.r, l, d + 1))
|
|
272
|
-
: (l[n.s] = d);
|
|
273
|
-
};
|
|
274
|
-
// length codes generation
|
|
275
|
-
var lc = function (c) {
|
|
276
|
-
var s = c.length;
|
|
277
|
-
// Note that the semicolon was intentional
|
|
278
|
-
while (s && !c[--s])
|
|
279
|
-
;
|
|
280
|
-
var cl = new u16(++s);
|
|
281
|
-
// ind num streak
|
|
282
|
-
var cli = 0, cln = c[0], cls = 1;
|
|
283
|
-
var w = function (v) { cl[cli++] = v; };
|
|
284
|
-
for (var i = 1; i <= s; ++i) {
|
|
285
|
-
if (c[i] == cln && i != s)
|
|
286
|
-
++cls;
|
|
287
|
-
else {
|
|
288
|
-
if (!cln && cls > 2) {
|
|
289
|
-
for (; cls > 138; cls -= 138)
|
|
290
|
-
w(32754);
|
|
291
|
-
if (cls > 2) {
|
|
292
|
-
w(cls > 10 ? ((cls - 11) << 5) | 28690 : ((cls - 3) << 5) | 12305);
|
|
293
|
-
cls = 0;
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
else if (cls > 3) {
|
|
297
|
-
w(cln), --cls;
|
|
298
|
-
for (; cls > 6; cls -= 6)
|
|
299
|
-
w(8304);
|
|
300
|
-
if (cls > 2)
|
|
301
|
-
w(((cls - 3) << 5) | 8208), cls = 0;
|
|
302
|
-
}
|
|
303
|
-
while (cls--)
|
|
304
|
-
w(cln);
|
|
305
|
-
cls = 1;
|
|
306
|
-
cln = c[i];
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
return { c: cl.subarray(0, cli), n: s };
|
|
310
|
-
};
|
|
311
|
-
// calculate the length of output from tree, code lengths
|
|
312
|
-
var clen = function (cf, cl) {
|
|
313
|
-
var l = 0;
|
|
314
|
-
for (var i = 0; i < cl.length; ++i)
|
|
315
|
-
l += cf[i] * cl[i];
|
|
316
|
-
return l;
|
|
317
|
-
};
|
|
318
|
-
// writes a fixed block
|
|
319
|
-
// returns the new bit pos
|
|
320
|
-
var wfblk = function (out, pos, dat) {
|
|
321
|
-
// no need to write 00 as type: TypedArray defaults to 0
|
|
322
|
-
var s = dat.length;
|
|
323
|
-
var o = shft(pos + 2);
|
|
324
|
-
out[o] = s & 255;
|
|
325
|
-
out[o + 1] = s >> 8;
|
|
326
|
-
out[o + 2] = out[o] ^ 255;
|
|
327
|
-
out[o + 3] = out[o + 1] ^ 255;
|
|
328
|
-
for (var i = 0; i < s; ++i)
|
|
329
|
-
out[o + i + 4] = dat[i];
|
|
330
|
-
return (o + 4 + s) * 8;
|
|
331
|
-
};
|
|
332
|
-
// writes a block
|
|
333
|
-
var wblk = function (dat, out, final, syms, lf, df, eb, li, bs, bl, p) {
|
|
334
|
-
wbits(out, p++, final);
|
|
335
|
-
++lf[256];
|
|
336
|
-
var _a = hTree(lf, 15), dlt = _a.t, mlb = _a.l;
|
|
337
|
-
var _b = hTree(df, 15), ddt = _b.t, mdb = _b.l;
|
|
338
|
-
var _c = lc(dlt), lclt = _c.c, nlc = _c.n;
|
|
339
|
-
var _d = lc(ddt), lcdt = _d.c, ndc = _d.n;
|
|
340
|
-
var lcfreq = new u16(19);
|
|
341
|
-
for (var i = 0; i < lclt.length; ++i)
|
|
342
|
-
++lcfreq[lclt[i] & 31];
|
|
343
|
-
for (var i = 0; i < lcdt.length; ++i)
|
|
344
|
-
++lcfreq[lcdt[i] & 31];
|
|
345
|
-
var _e = hTree(lcfreq, 7), lct = _e.t, mlcb = _e.l;
|
|
346
|
-
var nlcc = 19;
|
|
347
|
-
for (; nlcc > 4 && !lct[clim[nlcc - 1]]; --nlcc)
|
|
348
|
-
;
|
|
349
|
-
var flen = (bl + 5) << 3;
|
|
350
|
-
var ftlen = clen(lf, flt) + clen(df, fdt) + eb;
|
|
351
|
-
var dtlen = clen(lf, dlt) + clen(df, ddt) + eb + 14 + 3 * nlcc + clen(lcfreq, lct) + 2 * lcfreq[16] + 3 * lcfreq[17] + 7 * lcfreq[18];
|
|
352
|
-
if (bs >= 0 && flen <= ftlen && flen <= dtlen)
|
|
353
|
-
return wfblk(out, p, dat.subarray(bs, bs + bl));
|
|
354
|
-
var lm, ll, dm, dl;
|
|
355
|
-
wbits(out, p, 1 + (dtlen < ftlen)), p += 2;
|
|
356
|
-
if (dtlen < ftlen) {
|
|
357
|
-
lm = hMap(dlt, mlb, 0), ll = dlt, dm = hMap(ddt, mdb, 0), dl = ddt;
|
|
358
|
-
var llm = hMap(lct, mlcb, 0);
|
|
359
|
-
wbits(out, p, nlc - 257);
|
|
360
|
-
wbits(out, p + 5, ndc - 1);
|
|
361
|
-
wbits(out, p + 10, nlcc - 4);
|
|
362
|
-
p += 14;
|
|
363
|
-
for (var i = 0; i < nlcc; ++i)
|
|
364
|
-
wbits(out, p + 3 * i, lct[clim[i]]);
|
|
365
|
-
p += 3 * nlcc;
|
|
366
|
-
var lcts = [lclt, lcdt];
|
|
367
|
-
for (var it = 0; it < 2; ++it) {
|
|
368
|
-
var clct = lcts[it];
|
|
369
|
-
for (var i = 0; i < clct.length; ++i) {
|
|
370
|
-
var len = clct[i] & 31;
|
|
371
|
-
wbits(out, p, llm[len]), p += lct[len];
|
|
372
|
-
if (len > 15)
|
|
373
|
-
wbits(out, p, (clct[i] >> 5) & 127), p += clct[i] >> 12;
|
|
374
|
-
}
|
|
375
|
-
}
|
|
376
|
-
}
|
|
377
|
-
else {
|
|
378
|
-
lm = flm, ll = flt, dm = fdm, dl = fdt;
|
|
379
|
-
}
|
|
380
|
-
for (var i = 0; i < li; ++i) {
|
|
381
|
-
var sym = syms[i];
|
|
382
|
-
if (sym > 255) {
|
|
383
|
-
var len = (sym >> 18) & 31;
|
|
384
|
-
wbits16(out, p, lm[len + 257]), p += ll[len + 257];
|
|
385
|
-
if (len > 7)
|
|
386
|
-
wbits(out, p, (sym >> 23) & 31), p += fleb[len];
|
|
387
|
-
var dst = sym & 31;
|
|
388
|
-
wbits16(out, p, dm[dst]), p += dl[dst];
|
|
389
|
-
if (dst > 3)
|
|
390
|
-
wbits16(out, p, (sym >> 5) & 8191), p += fdeb[dst];
|
|
391
|
-
}
|
|
392
|
-
else {
|
|
393
|
-
wbits16(out, p, lm[sym]), p += ll[sym];
|
|
394
|
-
}
|
|
395
|
-
}
|
|
396
|
-
wbits16(out, p, lm[256]);
|
|
397
|
-
return p + ll[256];
|
|
398
|
-
};
|
|
399
|
-
// deflate options (nice << 13) | chain
|
|
400
|
-
var deo = /*#__PURE__*/ new i32([65540, 131080, 131088, 131104, 262176, 1048704, 1048832, 2114560, 2117632]);
|
|
401
|
-
// empty
|
|
402
|
-
var et = /*#__PURE__*/ new u8(0);
|
|
403
|
-
// compresses data into a raw DEFLATE buffer
|
|
404
|
-
var dflt = function (dat, lvl, plvl, pre, post, st) {
|
|
405
|
-
var s = st.z || dat.length;
|
|
406
|
-
var o = new u8(pre + s + 5 * (1 + Math.ceil(s / 7000)) + post);
|
|
407
|
-
// writing to this writes to the output buffer
|
|
408
|
-
var w = o.subarray(pre, o.length - post);
|
|
409
|
-
var lst = st.l;
|
|
410
|
-
var pos = (st.r || 0) & 7;
|
|
411
|
-
if (lvl) {
|
|
412
|
-
if (pos)
|
|
413
|
-
w[0] = st.r >> 3;
|
|
414
|
-
var opt = deo[lvl - 1];
|
|
415
|
-
var n = opt >> 13, c = opt & 8191;
|
|
416
|
-
var msk_1 = (1 << plvl) - 1;
|
|
417
|
-
// prev 2-byte val map curr 2-byte val map
|
|
418
|
-
var prev = st.p || new u16(32768), head = st.h || new u16(msk_1 + 1);
|
|
419
|
-
var bs1_1 = Math.ceil(plvl / 3), bs2_1 = 2 * bs1_1;
|
|
420
|
-
var hsh = function (i) { return (dat[i] ^ (dat[i + 1] << bs1_1) ^ (dat[i + 2] << bs2_1)) & msk_1; };
|
|
421
|
-
// 24576 is an arbitrary number of maximum symbols per block
|
|
422
|
-
// 424 buffer for last block
|
|
423
|
-
var syms = new i32(25000);
|
|
424
|
-
// length/literal freq distance freq
|
|
425
|
-
var lf = new u16(288), df = new u16(32);
|
|
426
|
-
// l/lcnt exbits index l/lind waitdx blkpos
|
|
427
|
-
var lc_1 = 0, eb = 0, i = st.i || 0, li = 0, wi = st.w || 0, bs = 0;
|
|
428
|
-
for (; i + 2 < s; ++i) {
|
|
429
|
-
// hash value
|
|
430
|
-
var hv = hsh(i);
|
|
431
|
-
// index mod 32768 previous index mod
|
|
432
|
-
var imod = i & 32767, pimod = head[hv];
|
|
433
|
-
prev[imod] = pimod;
|
|
434
|
-
head[hv] = imod;
|
|
435
|
-
// We always should modify head and prev, but only add symbols if
|
|
436
|
-
// this data is not yet processed ("wait" for wait index)
|
|
437
|
-
if (wi <= i) {
|
|
438
|
-
// bytes remaining
|
|
439
|
-
var rem = s - i;
|
|
440
|
-
if ((lc_1 > 7000 || li > 24576) && (rem > 423 || !lst)) {
|
|
441
|
-
pos = wblk(dat, w, 0, syms, lf, df, eb, li, bs, i - bs, pos);
|
|
442
|
-
li = lc_1 = eb = 0, bs = i;
|
|
443
|
-
for (var j = 0; j < 286; ++j)
|
|
444
|
-
lf[j] = 0;
|
|
445
|
-
for (var j = 0; j < 30; ++j)
|
|
446
|
-
df[j] = 0;
|
|
447
|
-
}
|
|
448
|
-
// len dist chain
|
|
449
|
-
var l = 2, d = 0, ch_1 = c, dif = imod - pimod & 32767;
|
|
450
|
-
if (rem > 2 && hv == hsh(i - dif)) {
|
|
451
|
-
var maxn = Math.min(n, rem) - 1;
|
|
452
|
-
var maxd = Math.min(32767, i);
|
|
453
|
-
// max possible length
|
|
454
|
-
// not capped at dif because decompressors implement "rolling" index population
|
|
455
|
-
var ml = Math.min(258, rem);
|
|
456
|
-
while (dif <= maxd && --ch_1 && imod != pimod) {
|
|
457
|
-
if (dat[i + l] == dat[i + l - dif]) {
|
|
458
|
-
var nl = 0;
|
|
459
|
-
for (; nl < ml && dat[i + nl] == dat[i + nl - dif]; ++nl)
|
|
460
|
-
;
|
|
461
|
-
if (nl > l) {
|
|
462
|
-
l = nl, d = dif;
|
|
463
|
-
// break out early when we reach "nice" (we are satisfied enough)
|
|
464
|
-
if (nl > maxn)
|
|
465
|
-
break;
|
|
466
|
-
// now, find the rarest 2-byte sequence within this
|
|
467
|
-
// length of literals and search for that instead.
|
|
468
|
-
// Much faster than just using the start
|
|
469
|
-
var mmd = Math.min(dif, nl - 2);
|
|
470
|
-
var md = 0;
|
|
471
|
-
for (var j = 0; j < mmd; ++j) {
|
|
472
|
-
var ti = i - dif + j & 32767;
|
|
473
|
-
var pti = prev[ti];
|
|
474
|
-
var cd = ti - pti & 32767;
|
|
475
|
-
if (cd > md)
|
|
476
|
-
md = cd, pimod = ti;
|
|
477
|
-
}
|
|
478
|
-
}
|
|
479
|
-
}
|
|
480
|
-
// check the previous match
|
|
481
|
-
imod = pimod, pimod = prev[imod];
|
|
482
|
-
dif += imod - pimod & 32767;
|
|
483
|
-
}
|
|
484
|
-
}
|
|
485
|
-
// d will be nonzero only when a match was found
|
|
486
|
-
if (d) {
|
|
487
|
-
// store both dist and len data in one int32
|
|
488
|
-
// Make sure this is recognized as a len/dist with 28th bit (2^28)
|
|
489
|
-
syms[li++] = 268435456 | (revfl[l] << 18) | revfd[d];
|
|
490
|
-
var lin = revfl[l] & 31, din = revfd[d] & 31;
|
|
491
|
-
eb += fleb[lin] + fdeb[din];
|
|
492
|
-
++lf[257 + lin];
|
|
493
|
-
++df[din];
|
|
494
|
-
wi = i + l;
|
|
495
|
-
++lc_1;
|
|
496
|
-
}
|
|
497
|
-
else {
|
|
498
|
-
syms[li++] = dat[i];
|
|
499
|
-
++lf[dat[i]];
|
|
500
|
-
}
|
|
501
|
-
}
|
|
502
|
-
}
|
|
503
|
-
for (i = Math.max(i, wi); i < s; ++i) {
|
|
504
|
-
syms[li++] = dat[i];
|
|
505
|
-
++lf[dat[i]];
|
|
506
|
-
}
|
|
507
|
-
pos = wblk(dat, w, lst, syms, lf, df, eb, li, bs, i - bs, pos);
|
|
508
|
-
if (!lst) {
|
|
509
|
-
st.r = (pos & 7) | w[(pos / 8) | 0] << 3;
|
|
510
|
-
// shft(pos) now 1 less if pos & 7 != 0
|
|
511
|
-
pos -= 7;
|
|
512
|
-
st.h = head, st.p = prev, st.i = i, st.w = wi;
|
|
513
|
-
}
|
|
514
|
-
}
|
|
515
|
-
else {
|
|
516
|
-
for (var i = st.w || 0; i < s + lst; i += 65535) {
|
|
517
|
-
// end
|
|
518
|
-
var e = i + 65535;
|
|
519
|
-
if (e >= s) {
|
|
520
|
-
// write final block
|
|
521
|
-
w[(pos / 8) | 0] = lst;
|
|
522
|
-
e = s;
|
|
523
|
-
}
|
|
524
|
-
pos = wfblk(w, pos + 1, dat.subarray(i, e));
|
|
525
|
-
}
|
|
526
|
-
st.i = s;
|
|
527
|
-
}
|
|
528
|
-
return slc(o, 0, pre + shft(pos) + post);
|
|
529
|
-
};
|
|
530
|
-
// CRC32 table
|
|
531
|
-
var crct = /*#__PURE__*/ (function () {
|
|
532
|
-
var t = new Int32Array(256);
|
|
533
|
-
for (var i = 0; i < 256; ++i) {
|
|
534
|
-
var c = i, k = 9;
|
|
535
|
-
while (--k)
|
|
536
|
-
c = ((c & 1) && -306674912) ^ (c >>> 1);
|
|
537
|
-
t[i] = c;
|
|
538
|
-
}
|
|
539
|
-
return t;
|
|
540
|
-
})();
|
|
541
|
-
// CRC32
|
|
542
|
-
var crc = function () {
|
|
543
|
-
var c = -1;
|
|
544
|
-
return {
|
|
545
|
-
p: function (d) {
|
|
546
|
-
// closures have awful performance
|
|
547
|
-
var cr = c;
|
|
548
|
-
for (var i = 0; i < d.length; ++i)
|
|
549
|
-
cr = crct[(cr & 255) ^ d[i]] ^ (cr >>> 8);
|
|
550
|
-
c = cr;
|
|
551
|
-
},
|
|
552
|
-
d: function () { return ~c; }
|
|
553
|
-
};
|
|
554
|
-
};
|
|
555
|
-
// deflate with opts
|
|
556
|
-
var dopt = function (dat, opt, pre, post, st) {
|
|
557
|
-
if (!st) {
|
|
558
|
-
st = { l: 1 };
|
|
559
|
-
if (opt.dictionary) {
|
|
560
|
-
var dict = opt.dictionary.subarray(-32768);
|
|
561
|
-
var newDat = new u8(dict.length + dat.length);
|
|
562
|
-
newDat.set(dict);
|
|
563
|
-
newDat.set(dat, dict.length);
|
|
564
|
-
dat = newDat;
|
|
565
|
-
st.w = dict.length;
|
|
566
|
-
}
|
|
567
|
-
}
|
|
568
|
-
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);
|
|
569
|
-
};
|
|
570
|
-
// Walmart object spread
|
|
571
|
-
var mrg = function (a, b) {
|
|
572
|
-
var o = {};
|
|
573
|
-
for (var k in a)
|
|
574
|
-
o[k] = a[k];
|
|
575
|
-
for (var k in b)
|
|
576
|
-
o[k] = b[k];
|
|
577
|
-
return o;
|
|
578
|
-
};
|
|
579
|
-
// worker clone
|
|
580
|
-
// This is possibly the craziest part of the entire codebase, despite how simple it may seem.
|
|
581
|
-
// The only parameter to this function is a closure that returns an array of variables outside of the function scope.
|
|
582
|
-
// We're going to try to figure out the variable names used in the closure as strings because that is crucial for workerization.
|
|
583
|
-
// We will return an object mapping of true variable name to value (basically, the current scope as a JS object).
|
|
584
|
-
// The reason we can't just use the original variable names is minifiers mangling the toplevel scope.
|
|
585
|
-
// This took me three weeks to figure out how to do.
|
|
586
|
-
var wcln = function (fn, fnStr, td) {
|
|
587
|
-
var dt = fn();
|
|
588
|
-
var st = fn.toString();
|
|
589
|
-
var ks = st.slice(st.indexOf('[') + 1, st.lastIndexOf(']')).replace(/\s+/g, '').split(',');
|
|
590
|
-
for (var i = 0; i < dt.length; ++i) {
|
|
591
|
-
var v = dt[i], k = ks[i];
|
|
592
|
-
if (typeof v == 'function') {
|
|
593
|
-
fnStr += ';' + k + '=';
|
|
594
|
-
var st_1 = v.toString();
|
|
595
|
-
if (v.prototype) {
|
|
596
|
-
// for global objects
|
|
597
|
-
if (st_1.indexOf('[native code]') != -1) {
|
|
598
|
-
var spInd = st_1.indexOf(' ', 8) + 1;
|
|
599
|
-
fnStr += st_1.slice(spInd, st_1.indexOf('(', spInd));
|
|
600
|
-
}
|
|
601
|
-
else {
|
|
602
|
-
fnStr += st_1;
|
|
603
|
-
for (var t in v.prototype)
|
|
604
|
-
fnStr += ';' + k + '.prototype.' + t + '=' + v.prototype[t].toString();
|
|
605
|
-
}
|
|
606
|
-
}
|
|
607
|
-
else
|
|
608
|
-
fnStr += st_1;
|
|
609
|
-
}
|
|
610
|
-
else
|
|
611
|
-
td[k] = v;
|
|
612
|
-
}
|
|
613
|
-
return fnStr;
|
|
614
|
-
};
|
|
615
|
-
var ch = [];
|
|
616
|
-
// clone bufs
|
|
617
|
-
var cbfs = function (v) {
|
|
618
|
-
var tl = [];
|
|
619
|
-
for (var k in v) {
|
|
620
|
-
if (v[k].buffer) {
|
|
621
|
-
tl.push((v[k] = new v[k].constructor(v[k])).buffer);
|
|
622
|
-
}
|
|
623
|
-
}
|
|
624
|
-
return tl;
|
|
625
|
-
};
|
|
626
|
-
// use a worker to execute code
|
|
627
|
-
var wrkr = function (fns, init, id, cb) {
|
|
628
|
-
if (!ch[id]) {
|
|
629
|
-
var fnStr = '', td_1 = {}, m = fns.length - 1;
|
|
630
|
-
for (var i = 0; i < m; ++i)
|
|
631
|
-
fnStr = wcln(fns[i], fnStr, td_1);
|
|
632
|
-
ch[id] = { c: wcln(fns[m], fnStr, td_1), e: td_1 };
|
|
633
|
-
}
|
|
634
|
-
var td = mrg({}, ch[id].e);
|
|
635
|
-
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);
|
|
636
|
-
};
|
|
637
|
-
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]; };
|
|
638
|
-
// gzip extra
|
|
639
|
-
var gze = function () { return [gzh, gzhl, wbytes, crc, crct]; };
|
|
640
|
-
// post buf
|
|
641
|
-
var pbf = function (msg) { return postMessage(msg, [msg.buffer]); };
|
|
642
|
-
// async helper
|
|
643
|
-
var cbify = function (dat, opts, fns, init, id, cb) {
|
|
644
|
-
var w = wrkr(fns, init, id, function (err, dat) {
|
|
645
|
-
w.terminate();
|
|
646
|
-
cb(err, dat);
|
|
647
|
-
});
|
|
648
|
-
w.postMessage([dat, opts], opts.consume ? [dat.buffer] : []);
|
|
649
|
-
return function () { w.terminate(); };
|
|
650
|
-
};
|
|
651
|
-
// write bytes
|
|
652
|
-
var wbytes = function (d, b, v) {
|
|
653
|
-
for (; v; ++b)
|
|
654
|
-
d[b] = v, v >>>= 8;
|
|
655
|
-
};
|
|
656
|
-
// gzip header
|
|
657
|
-
var gzh = function (c, o) {
|
|
658
|
-
var fn = o.filename;
|
|
659
|
-
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
|
|
660
|
-
if (o.mtime != 0)
|
|
661
|
-
wbytes(c, 4, Math.floor(new Date(o.mtime || Date.now()) / 1000));
|
|
662
|
-
if (fn) {
|
|
663
|
-
c[3] = 8;
|
|
664
|
-
for (var i = 0; i <= fn.length; ++i)
|
|
665
|
-
c[i + 10] = fn.charCodeAt(i);
|
|
666
|
-
}
|
|
667
|
-
};
|
|
668
|
-
// gzip header length
|
|
669
|
-
var gzhl = function (o) { return 10 + (o.filename ? o.filename.length + 1 : 0); };
|
|
670
|
-
/**
|
|
671
|
-
* Compresses data with DEFLATE without any wrapper
|
|
672
|
-
* @param data The data to compress
|
|
673
|
-
* @param opts The compression options
|
|
674
|
-
* @returns The deflated version of the data
|
|
675
|
-
*/
|
|
676
|
-
function deflateSync(data, opts) {
|
|
677
|
-
return dopt(data, opts || {}, 0, 0);
|
|
678
|
-
}
|
|
679
|
-
function gzip(data, opts, cb) {
|
|
680
|
-
if (!cb)
|
|
681
|
-
cb = opts, opts = {};
|
|
682
|
-
if (typeof cb != 'function')
|
|
683
|
-
err(7);
|
|
684
|
-
return cbify(data, opts, [
|
|
685
|
-
bDflt,
|
|
686
|
-
gze,
|
|
687
|
-
function () { return [gzipSync]; }
|
|
688
|
-
], function (ev) { return pbf(gzipSync(ev.data[0], ev.data[1])); }, 2, cb);
|
|
689
|
-
}
|
|
690
|
-
/**
|
|
691
|
-
* Compresses data with GZIP
|
|
692
|
-
* @param data The data to compress
|
|
693
|
-
* @param opts The compression options
|
|
694
|
-
* @returns The gzipped version of the data
|
|
695
|
-
*/
|
|
696
|
-
function gzipSync(data, opts) {
|
|
697
|
-
if (!opts)
|
|
698
|
-
opts = {};
|
|
699
|
-
var c = crc(), l = data.length;
|
|
700
|
-
c.p(data);
|
|
701
|
-
var d = dopt(data, opts, gzhl(opts), 8), s = d.length;
|
|
702
|
-
return gzh(d, opts), wbytes(d, s - 8, c.d()), wbytes(d, s - 4, l), d;
|
|
703
|
-
}
|
|
704
|
-
// text decoder
|
|
705
|
-
var td = typeof TextDecoder != 'undefined' && /*#__PURE__*/ new TextDecoder();
|
|
706
|
-
// text decoder stream
|
|
707
|
-
var tds = 0;
|
|
708
|
-
try {
|
|
709
|
-
td.decode(et, { stream: true });
|
|
710
|
-
tds = 1;
|
|
711
|
-
}
|
|
712
|
-
catch (e) { }
|
|
713
|
-
|
|
714
1
|
class StringDictionary {
|
|
715
2
|
constructor() {
|
|
716
3
|
this.lastTs = 0;
|
|
@@ -1161,6 +448,23 @@ function deprecationWarn(nameOfFeature, useInstead, docsPath = '/') {
|
|
|
1161
448
|
console.warn(`OxvoSessions: ${nameOfFeature} is deprecated. ${useInstead ? `Please, use ${useInstead} instead.` : ''} Visit ${DOCS_HOST}${docsPath} for more information.`);
|
|
1162
449
|
warnedFeatures[nameOfFeature] = true;
|
|
1163
450
|
}
|
|
451
|
+
function getCustomAttributeLabel(e, customAttributes) {
|
|
452
|
+
if (!customAttributes || customAttributes.length === 0)
|
|
453
|
+
return '';
|
|
454
|
+
const parts = [];
|
|
455
|
+
for (const attr of customAttributes) {
|
|
456
|
+
const value = e.getAttribute(attr);
|
|
457
|
+
if (value !== null) {
|
|
458
|
+
parts.push(`[${attr}=${value}]`);
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
return parts.join('');
|
|
462
|
+
}
|
|
463
|
+
function getClassSelector(e) {
|
|
464
|
+
if (!e.classList || e.classList.length === 0)
|
|
465
|
+
return '';
|
|
466
|
+
return '.' + Array.from(e.classList).join('.');
|
|
467
|
+
}
|
|
1164
468
|
function getLabelAttribute(e) {
|
|
1165
469
|
let value = e.getAttribute('data-oxvosessions-label');
|
|
1166
470
|
if (value !== null) {
|
|
@@ -1917,13 +1221,6 @@ function BatchMetadata(version, pageNo, firstIndex, timestamp, location) {
|
|
|
1917
1221
|
location,
|
|
1918
1222
|
];
|
|
1919
1223
|
}
|
|
1920
|
-
function PartitionedMessage(partNo, partTotal) {
|
|
1921
|
-
return [
|
|
1922
|
-
82 /* Messages.Type.PartitionedMessage */,
|
|
1923
|
-
partNo,
|
|
1924
|
-
partTotal,
|
|
1925
|
-
];
|
|
1926
|
-
}
|
|
1927
1224
|
function NetworkRequest(type, method, url, request, response, status, timestamp, duration, transferredBodySize) {
|
|
1928
1225
|
return [
|
|
1929
1226
|
83 /* Messages.Type.NetworkRequest */,
|
|
@@ -2135,7 +1432,6 @@ var _Messages = /*#__PURE__*/Object.freeze({
|
|
|
2135
1432
|
OTable: OTable,
|
|
2136
1433
|
PageLoadTiming: PageLoadTiming,
|
|
2137
1434
|
PageRenderTiming: PageRenderTiming,
|
|
2138
|
-
PartitionedMessage: PartitionedMessage,
|
|
2139
1435
|
PerformanceTrack: PerformanceTrack,
|
|
2140
1436
|
Profiler: Profiler,
|
|
2141
1437
|
Redux: Redux,
|
|
@@ -2230,11 +1526,157 @@ function Performance (app, opts) {
|
|
|
2230
1526
|
}
|
|
2231
1527
|
}
|
|
2232
1528
|
|
|
1529
|
+
/**
|
|
1530
|
+
* Two-tier tag matching:
|
|
1531
|
+
* 1. Fast fingerprint lookup by id, data-attr,
|
|
1532
|
+
* or class from the selector's last segment
|
|
1533
|
+
* 2. Fallback iteration using native element.matches()
|
|
1534
|
+
*/
|
|
1535
|
+
class TagMatcher {
|
|
1536
|
+
constructor() {
|
|
1537
|
+
this.tags = [];
|
|
1538
|
+
this.byId = new Map();
|
|
1539
|
+
this.byDataAttr = new Map();
|
|
1540
|
+
this.byClass = new Map();
|
|
1541
|
+
this.fallback = [];
|
|
1542
|
+
}
|
|
1543
|
+
setTags(tags) {
|
|
1544
|
+
this.tags = tags;
|
|
1545
|
+
this.byId.clear();
|
|
1546
|
+
this.byDataAttr.clear();
|
|
1547
|
+
this.byClass.clear();
|
|
1548
|
+
this.fallback = [];
|
|
1549
|
+
for (const tag of tags) {
|
|
1550
|
+
const last = lastSegment(tag.selector);
|
|
1551
|
+
if (!last) {
|
|
1552
|
+
this.fallback.push(tag);
|
|
1553
|
+
continue;
|
|
1554
|
+
}
|
|
1555
|
+
if (last.startsWith('#')) {
|
|
1556
|
+
this.byId.set(last.slice(1), tag);
|
|
1557
|
+
}
|
|
1558
|
+
else if (last.startsWith('[data-')) {
|
|
1559
|
+
this.byDataAttr.set(last, tag);
|
|
1560
|
+
}
|
|
1561
|
+
else {
|
|
1562
|
+
const cls = extractClass(last);
|
|
1563
|
+
if (cls) {
|
|
1564
|
+
this.byClass.set(cls, tag);
|
|
1565
|
+
}
|
|
1566
|
+
else {
|
|
1567
|
+
this.fallback.push(tag);
|
|
1568
|
+
}
|
|
1569
|
+
}
|
|
1570
|
+
}
|
|
1571
|
+
}
|
|
1572
|
+
getTags() {
|
|
1573
|
+
return this.tags;
|
|
1574
|
+
}
|
|
1575
|
+
/** Match element, its parent, or direct children against known tag selectors */
|
|
1576
|
+
match(el) {
|
|
1577
|
+
const direct = this.matchExact(el);
|
|
1578
|
+
if (direct)
|
|
1579
|
+
return direct;
|
|
1580
|
+
if (el.parentElement) {
|
|
1581
|
+
const parent = this.matchExact(el.parentElement);
|
|
1582
|
+
if (parent)
|
|
1583
|
+
return parent;
|
|
1584
|
+
}
|
|
1585
|
+
const children = el.children;
|
|
1586
|
+
for (let i = 0; i < children.length; i++) {
|
|
1587
|
+
const child = this.matchExact(children[i]);
|
|
1588
|
+
if (child)
|
|
1589
|
+
return child;
|
|
1590
|
+
}
|
|
1591
|
+
return null;
|
|
1592
|
+
}
|
|
1593
|
+
matchExact(el) {
|
|
1594
|
+
if (el.id && this.byId.has(el.id)) {
|
|
1595
|
+
const tag = this.byId.get(el.id);
|
|
1596
|
+
if (safeMatches(el, tag.selector) && matchesLocation(tag))
|
|
1597
|
+
return tag;
|
|
1598
|
+
}
|
|
1599
|
+
if (this.byDataAttr.size > 0) {
|
|
1600
|
+
const attrs = el.attributes;
|
|
1601
|
+
for (let i = 0; i < attrs.length; i++) {
|
|
1602
|
+
const attr = attrs[i];
|
|
1603
|
+
if (attr.name.startsWith('data-')) {
|
|
1604
|
+
const key = `[${attr.name}="${attr.value}"]`;
|
|
1605
|
+
if (this.byDataAttr.has(key)) {
|
|
1606
|
+
const tag = this.byDataAttr.get(key);
|
|
1607
|
+
if (safeMatches(el, tag.selector) && matchesLocation(tag))
|
|
1608
|
+
return tag;
|
|
1609
|
+
}
|
|
1610
|
+
}
|
|
1611
|
+
}
|
|
1612
|
+
}
|
|
1613
|
+
if (this.byClass.size > 0 && el.classList) {
|
|
1614
|
+
for (let i = 0; i < el.classList.length; i++) {
|
|
1615
|
+
const cls = el.classList[i];
|
|
1616
|
+
if (this.byClass.has(cls)) {
|
|
1617
|
+
const tag = this.byClass.get(cls);
|
|
1618
|
+
if (safeMatches(el, tag.selector) && matchesLocation(tag))
|
|
1619
|
+
return tag;
|
|
1620
|
+
}
|
|
1621
|
+
}
|
|
1622
|
+
}
|
|
1623
|
+
for (const tag of this.fallback) {
|
|
1624
|
+
if (safeMatches(el, tag.selector) && matchesLocation(tag))
|
|
1625
|
+
return tag;
|
|
1626
|
+
}
|
|
1627
|
+
return null;
|
|
1628
|
+
}
|
|
1629
|
+
clear() {
|
|
1630
|
+
this.tags = [];
|
|
1631
|
+
this.byId.clear();
|
|
1632
|
+
this.byDataAttr.clear();
|
|
1633
|
+
this.byClass.clear();
|
|
1634
|
+
this.fallback = [];
|
|
1635
|
+
}
|
|
1636
|
+
}
|
|
1637
|
+
/** Last combinator-separated segment of a CSS selector */
|
|
1638
|
+
function lastSegment(selector) {
|
|
1639
|
+
const trimmed = selector.trim();
|
|
1640
|
+
if (!trimmed)
|
|
1641
|
+
return null;
|
|
1642
|
+
const parts = trimmed.split(/\s*[>+~ ]\s*/);
|
|
1643
|
+
const last = parts[parts.length - 1]?.trim();
|
|
1644
|
+
return last || null;
|
|
1645
|
+
}
|
|
1646
|
+
/** First class name from a selector segment, e.g. "div.my-class" -> "my-class" */
|
|
1647
|
+
function extractClass(segment) {
|
|
1648
|
+
const match = segment.match(/\.([a-zA-Z_-][\w-]*)/);
|
|
1649
|
+
return match ? match[1] : null;
|
|
1650
|
+
}
|
|
1651
|
+
function safeMatches(el, selector) {
|
|
1652
|
+
try {
|
|
1653
|
+
return el.matches(selector);
|
|
1654
|
+
}
|
|
1655
|
+
catch {
|
|
1656
|
+
return false;
|
|
1657
|
+
}
|
|
1658
|
+
}
|
|
1659
|
+
function matchesLocation(tag) {
|
|
1660
|
+
if (!tag.location)
|
|
1661
|
+
return true;
|
|
1662
|
+
try {
|
|
1663
|
+
const loc = tag.location;
|
|
1664
|
+
if (loc.startsWith('/')) {
|
|
1665
|
+
return window.location.pathname === loc;
|
|
1666
|
+
}
|
|
1667
|
+
return window.location.href === loc;
|
|
1668
|
+
}
|
|
1669
|
+
catch {
|
|
1670
|
+
return true;
|
|
1671
|
+
}
|
|
1672
|
+
}
|
|
1673
|
+
|
|
2233
1674
|
const WATCHED_MARKERS_KEY = '__or__watched_markers__';
|
|
2234
1675
|
class MarkerWatcher {
|
|
2235
1676
|
constructor(params) {
|
|
2236
1677
|
this.interval = null;
|
|
2237
1678
|
this.tags = [];
|
|
1679
|
+
this.matcher = new TagMatcher();
|
|
2238
1680
|
this.sessionStorage = params.sessionStorage;
|
|
2239
1681
|
this.errLog = params.errLog;
|
|
2240
1682
|
this.onMarkerHit = params.onMarkerHit;
|
|
@@ -2277,12 +1719,15 @@ class MarkerWatcher {
|
|
|
2277
1719
|
}
|
|
2278
1720
|
setTags(tags) {
|
|
2279
1721
|
this.tags = tags;
|
|
1722
|
+
this.matcher.setTags(tags);
|
|
2280
1723
|
if (this.interval) {
|
|
2281
1724
|
clearInterval(this.interval);
|
|
2282
1725
|
this.interval = null;
|
|
2283
1726
|
}
|
|
2284
1727
|
this.interval = setInterval(() => {
|
|
2285
1728
|
this.tags.forEach((tag) => {
|
|
1729
|
+
if (!matchesLocation(tag))
|
|
1730
|
+
return;
|
|
2286
1731
|
const possibleEls = document.querySelectorAll(tag.selector);
|
|
2287
1732
|
if (possibleEls.length > 0) {
|
|
2288
1733
|
const el = possibleEls[0];
|
|
@@ -2294,13 +1739,17 @@ class MarkerWatcher {
|
|
|
2294
1739
|
}, 500);
|
|
2295
1740
|
}
|
|
2296
1741
|
onMarkerRendered(markerId) {
|
|
2297
|
-
|
|
2298
|
-
|
|
1742
|
+
const tag = this.tags.find((t) => t.id === markerId);
|
|
1743
|
+
if (tag) {
|
|
1744
|
+
this.tags = this.tags.filter((t) => t.id !== markerId);
|
|
1745
|
+
if (!matchesLocation(tag))
|
|
1746
|
+
return;
|
|
2299
1747
|
}
|
|
2300
1748
|
this.onMarkerHit(markerId);
|
|
2301
1749
|
}
|
|
2302
1750
|
clear() {
|
|
2303
1751
|
this.tags = [];
|
|
1752
|
+
this.matcher.clear();
|
|
2304
1753
|
if (this.interval) {
|
|
2305
1754
|
clearInterval(this.interval);
|
|
2306
1755
|
this.interval = null;
|
|
@@ -2351,6 +1800,30 @@ class CanvasRecorder {
|
|
|
2351
1800
|
this.MAX_QUEUE_SIZE = 50; // ~500 images max (50 batches × 10 images)
|
|
2352
1801
|
this.pendingBatches = [];
|
|
2353
1802
|
this.isProcessingQueue = false;
|
|
1803
|
+
/**
|
|
1804
|
+
* Reacts to a runtime sanitization change on a canvas: stop capturing if it
|
|
1805
|
+
* just became masked, start if it just became visible. Already-sent frames
|
|
1806
|
+
* cannot be retracted, so escalation only stops future capture.
|
|
1807
|
+
*/
|
|
1808
|
+
this.resanitizeCanvas = (node, id) => {
|
|
1809
|
+
if (!hasTag(node, 'canvas')) {
|
|
1810
|
+
return;
|
|
1811
|
+
}
|
|
1812
|
+
const isIgnored = this.app.sanitizer.isObscured(id) || this.app.sanitizer.isHidden(id);
|
|
1813
|
+
if (isIgnored) {
|
|
1814
|
+
if (this.snapshots[id] || this.observers.has(id)) {
|
|
1815
|
+
const observer = this.observers.get(id);
|
|
1816
|
+
if (observer) {
|
|
1817
|
+
observer.disconnect();
|
|
1818
|
+
this.observers.delete(id);
|
|
1819
|
+
}
|
|
1820
|
+
this.cleanupCanvas(id);
|
|
1821
|
+
}
|
|
1822
|
+
}
|
|
1823
|
+
else if (!this.snapshots[id] && !this.observers.has(id)) {
|
|
1824
|
+
this.captureCanvas(node);
|
|
1825
|
+
}
|
|
1826
|
+
};
|
|
2354
1827
|
this.restartTracking = () => {
|
|
2355
1828
|
this.clear();
|
|
2356
1829
|
this.app.nodes.scanTree(this.captureCanvas);
|
|
@@ -2447,14 +1920,15 @@ class CanvasRecorder {
|
|
|
2447
1920
|
}, this.interval);
|
|
2448
1921
|
this.intervals.set(id, int);
|
|
2449
1922
|
};
|
|
2450
|
-
this.fileExt =
|
|
1923
|
+
this.fileExt = 'webp';
|
|
2451
1924
|
this.interval = 1000 / options.fps;
|
|
2452
1925
|
}
|
|
2453
1926
|
startTracking() {
|
|
2454
1927
|
setTimeout(() => {
|
|
2455
1928
|
this.app.nodes.scanTree(this.captureCanvas);
|
|
2456
1929
|
this.app.nodes.attachNodeCallback(this.captureCanvas);
|
|
2457
|
-
|
|
1930
|
+
this.app.attachResanitizeCallback(this.resanitizeCanvas);
|
|
1931
|
+
}, 125);
|
|
2458
1932
|
}
|
|
2459
1933
|
sendSnaps(images, canvasId, createdAt) {
|
|
2460
1934
|
if (Object.keys(this.snapshots).length === 0) {
|
|
@@ -2483,17 +1957,73 @@ class CanvasRecorder {
|
|
|
2483
1957
|
}
|
|
2484
1958
|
this.isProcessingQueue = false;
|
|
2485
1959
|
}
|
|
2486
|
-
uploadBatch(images, canvasId, createdAt) {
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
1960
|
+
async uploadBatch(images, canvasId, createdAt) {
|
|
1961
|
+
if (this.options.isDebug) {
|
|
1962
|
+
const packed = await packFrames(images);
|
|
1963
|
+
if (packed) {
|
|
1964
|
+
const fileName = `${createdAt}_${canvasId}.${this.fileExt}.frames`;
|
|
1965
|
+
const url = URL.createObjectURL(new Blob([packed]));
|
|
1966
|
+
const link = document.createElement('a');
|
|
1967
|
+
link.href = url;
|
|
1968
|
+
link.download = fileName;
|
|
1969
|
+
link.style.display = 'none';
|
|
1970
|
+
document.body.appendChild(link);
|
|
1971
|
+
link.click();
|
|
1972
|
+
document.body.removeChild(link);
|
|
1973
|
+
URL.revokeObjectURL(url);
|
|
1974
|
+
}
|
|
1975
|
+
// fall through to also send to backend
|
|
1976
|
+
}
|
|
1977
|
+
let formData;
|
|
1978
|
+
if (this.options.framesSupport) {
|
|
1979
|
+
// Pack frames into binary format: [uint64 LE timestamp][uint32 LE size][data] per frame
|
|
1980
|
+
const buffers = [];
|
|
1981
|
+
let totalSize = 0;
|
|
1982
|
+
for (const snapshot of images) {
|
|
1983
|
+
if (!snapshot.data)
|
|
1984
|
+
continue;
|
|
1985
|
+
const ab = await snapshot.data.arrayBuffer();
|
|
1986
|
+
buffers.push(ab);
|
|
1987
|
+
totalSize += 8 + 4 + ab.byteLength; // uint64 ts + uint32 size + data
|
|
2495
1988
|
}
|
|
2496
|
-
|
|
1989
|
+
if (totalSize === 0)
|
|
1990
|
+
return;
|
|
1991
|
+
const packed = new ArrayBuffer(totalSize);
|
|
1992
|
+
const view = new DataView(packed);
|
|
1993
|
+
const bytes = new Uint8Array(packed);
|
|
1994
|
+
let offset = 0;
|
|
1995
|
+
for (let i = 0; i < images.length; i++) {
|
|
1996
|
+
if (!images[i].data)
|
|
1997
|
+
continue;
|
|
1998
|
+
const ab = buffers.shift();
|
|
1999
|
+
const ts = images[i].id;
|
|
2000
|
+
// uint64 LE as two uint32 LE writes -- timestamp
|
|
2001
|
+
view.setUint32(offset, ts % 0x100000000, true);
|
|
2002
|
+
view.setUint32(offset + 4, Math.floor(ts / 0x100000000), true);
|
|
2003
|
+
offset += 8;
|
|
2004
|
+
// uint32 LE -- size
|
|
2005
|
+
view.setUint32(offset, ab.byteLength, true);
|
|
2006
|
+
offset += 4;
|
|
2007
|
+
// image data
|
|
2008
|
+
bytes.set(new Uint8Array(ab), offset);
|
|
2009
|
+
offset += ab.byteLength;
|
|
2010
|
+
}
|
|
2011
|
+
formData = new FormData();
|
|
2012
|
+
formData.append('type', 'frames');
|
|
2013
|
+
const fileName = `${createdAt}_${canvasId}.${this.fileExt}.frames`;
|
|
2014
|
+
formData.append('frames', new Blob([packed]), fileName);
|
|
2015
|
+
}
|
|
2016
|
+
else {
|
|
2017
|
+
// Legacy: send individual image files
|
|
2018
|
+
formData = new FormData();
|
|
2019
|
+
images.forEach((snapshot) => {
|
|
2020
|
+
const blob = snapshot.data;
|
|
2021
|
+
if (!blob)
|
|
2022
|
+
return;
|
|
2023
|
+
const name = `${createdAt}_${canvasId}_${snapshot.id}.${this.fileExt}`;
|
|
2024
|
+
formData.append('snapshot', blob, name);
|
|
2025
|
+
});
|
|
2026
|
+
}
|
|
2497
2027
|
const initRestart = () => {
|
|
2498
2028
|
this.app.debug.log('Restarting tracker; token expired');
|
|
2499
2029
|
this.app.stop(false);
|
|
@@ -2589,15 +2119,36 @@ function captureSnapshot(canvas, quality = 'medium', dummy, fixedScaling = false
|
|
|
2589
2119
|
canvas.toBlob(onBlob, imageFormat, qualityInt[quality]);
|
|
2590
2120
|
}
|
|
2591
2121
|
}
|
|
2592
|
-
function
|
|
2593
|
-
const
|
|
2594
|
-
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
|
|
2599
|
-
|
|
2600
|
-
|
|
2122
|
+
async function packFrames(images) {
|
|
2123
|
+
const buffers = [];
|
|
2124
|
+
let totalSize = 0;
|
|
2125
|
+
for (const snapshot of images) {
|
|
2126
|
+
if (!snapshot.data)
|
|
2127
|
+
continue;
|
|
2128
|
+
const ab = await snapshot.data.arrayBuffer();
|
|
2129
|
+
buffers.push(ab);
|
|
2130
|
+
totalSize += 8 + 4 + ab.byteLength;
|
|
2131
|
+
}
|
|
2132
|
+
if (totalSize === 0)
|
|
2133
|
+
return null;
|
|
2134
|
+
const packed = new ArrayBuffer(totalSize);
|
|
2135
|
+
const view = new DataView(packed);
|
|
2136
|
+
const bytes = new Uint8Array(packed);
|
|
2137
|
+
let offset = 0;
|
|
2138
|
+
for (let i = 0; i < images.length; i++) {
|
|
2139
|
+
if (!images[i].data)
|
|
2140
|
+
continue;
|
|
2141
|
+
const ab = buffers.shift();
|
|
2142
|
+
const ts = images[i].id;
|
|
2143
|
+
view.setUint32(offset, ts % 0x100000000, true);
|
|
2144
|
+
view.setUint32(offset + 4, Math.floor(ts / 0x100000000), true);
|
|
2145
|
+
offset += 8;
|
|
2146
|
+
view.setUint32(offset, ab.byteLength, true);
|
|
2147
|
+
offset += 4;
|
|
2148
|
+
bytes.set(new Uint8Array(ab), offset);
|
|
2149
|
+
offset += ab.byteLength;
|
|
2150
|
+
}
|
|
2151
|
+
return packed;
|
|
2601
2152
|
}
|
|
2602
2153
|
|
|
2603
2154
|
const LogLevel = {
|
|
@@ -2878,8 +2429,44 @@ function inlineRemoteCss(node, id, baseHref, getNextID, insertRule, addOwner, fo
|
|
|
2878
2429
|
})
|
|
2879
2430
|
.catch(error => {
|
|
2880
2431
|
console.error(`OxvoSessions: Failed to fetch CSS from ${node.href}:`, error);
|
|
2432
|
+
// Fetch failed, likely due to CORS. Retry via load event and sheet access.
|
|
2433
|
+
retryViaLoadEvent();
|
|
2881
2434
|
});
|
|
2882
2435
|
}
|
|
2436
|
+
else {
|
|
2437
|
+
retryViaLoadEvent();
|
|
2438
|
+
}
|
|
2439
|
+
function retryViaLoadEvent() {
|
|
2440
|
+
const trySheet = () => {
|
|
2441
|
+
const loadedSheet = node.sheet;
|
|
2442
|
+
if (loadedSheet) {
|
|
2443
|
+
try {
|
|
2444
|
+
const cssText = stringifyStylesheet(loadedSheet);
|
|
2445
|
+
if (cssText) {
|
|
2446
|
+
if (sendPlain && onPlain) {
|
|
2447
|
+
onPlain(cssText, fakeIdHolder++);
|
|
2448
|
+
}
|
|
2449
|
+
else {
|
|
2450
|
+
processCssText(cssText);
|
|
2451
|
+
}
|
|
2452
|
+
return;
|
|
2453
|
+
}
|
|
2454
|
+
}
|
|
2455
|
+
catch (e) {
|
|
2456
|
+
// Cross-origin sheet. Nothing more can be read safely here.
|
|
2457
|
+
}
|
|
2458
|
+
}
|
|
2459
|
+
};
|
|
2460
|
+
if (node.sheet) {
|
|
2461
|
+
trySheet();
|
|
2462
|
+
}
|
|
2463
|
+
else {
|
|
2464
|
+
node.addEventListener('load', function onLoad() {
|
|
2465
|
+
node.removeEventListener('load', onLoad);
|
|
2466
|
+
trySheet();
|
|
2467
|
+
});
|
|
2468
|
+
}
|
|
2469
|
+
}
|
|
2883
2470
|
function processCssText(cssText) {
|
|
2884
2471
|
// Remove comments
|
|
2885
2472
|
cssText = cssText.replace(/\/\*[\s\S]*?\*\//g, '');
|
|
@@ -3101,7 +2688,6 @@ function ConstructedStyleSheets (app) {
|
|
|
3101
2688
|
if (!hasAdoptedSS(document)) {
|
|
3102
2689
|
return;
|
|
3103
2690
|
}
|
|
3104
|
-
const styleSheetIDMap = new Map();
|
|
3105
2691
|
const adoptedStyleSheetsOwnings = new Map();
|
|
3106
2692
|
const sendAdoptedStyleSheetsUpdate = (root) => setTimeout(() => {
|
|
3107
2693
|
let nodeID = app.nodes.getID(root);
|
|
@@ -3212,7 +2798,120 @@ function ConstructedStyleSheets (app) {
|
|
|
3212
2798
|
if (hasAdoptedSS(node)) {
|
|
3213
2799
|
sendAdoptedStyleSheetsUpdate(node);
|
|
3214
2800
|
}
|
|
3215
|
-
});
|
|
2801
|
+
});
|
|
2802
|
+
}
|
|
2803
|
+
|
|
2804
|
+
var SanitizeLevel;
|
|
2805
|
+
(function (SanitizeLevel) {
|
|
2806
|
+
SanitizeLevel[SanitizeLevel["Plain"] = 0] = "Plain";
|
|
2807
|
+
SanitizeLevel[SanitizeLevel["Obscured"] = 1] = "Obscured";
|
|
2808
|
+
SanitizeLevel[SanitizeLevel["Hidden"] = 2] = "Hidden";
|
|
2809
|
+
})(SanitizeLevel || (SanitizeLevel = {}));
|
|
2810
|
+
const stringWiper = (input) => input
|
|
2811
|
+
.trim()
|
|
2812
|
+
.replace(/[^\f\n\r\t\v\u00a0\u1680\u2000-\u200a\u2028\u2029\u202f\u205f\u3000\ufeff\s]/g, '*');
|
|
2813
|
+
class Sanitizer {
|
|
2814
|
+
constructor(params) {
|
|
2815
|
+
// Node id -> level. Plain (0) is never stored; a missing entry means Plain.
|
|
2816
|
+
// A map (not the old grow-only Sets) so levels can be raised and lowered.
|
|
2817
|
+
this.levels = new Map();
|
|
2818
|
+
this.app = params.app;
|
|
2819
|
+
const defaultOptions = {
|
|
2820
|
+
maskTextEmails: true,
|
|
2821
|
+
maskTextNumbers: false,
|
|
2822
|
+
maskAllByDefault: false,
|
|
2823
|
+
domSanitizer: undefined,
|
|
2824
|
+
};
|
|
2825
|
+
this.maskAllByDefault = params.options?.maskAllByDefault ?? false;
|
|
2826
|
+
this.options = Object.assign(defaultOptions, params.options);
|
|
2827
|
+
}
|
|
2828
|
+
// Pure recomputation of a node's level from the live DOM + parent level.
|
|
2829
|
+
// Reading current state on every call lets resanitize() pick up runtime changes.
|
|
2830
|
+
computeLevel(node, parentLevel) {
|
|
2831
|
+
if (this.options.maskAllByDefault) {
|
|
2832
|
+
if (isElementNode(node) && !hasOxvoSessionsAttribute(node, 'unmask')) {
|
|
2833
|
+
return SanitizeLevel.Obscured;
|
|
2834
|
+
}
|
|
2835
|
+
if (isTextNode(node) && !hasOxvoSessionsAttribute(node.parentNode, 'unmask')) {
|
|
2836
|
+
return SanitizeLevel.Obscured;
|
|
2837
|
+
}
|
|
2838
|
+
}
|
|
2839
|
+
let level = SanitizeLevel.Plain;
|
|
2840
|
+
if (parentLevel >= SanitizeLevel.Obscured ||
|
|
2841
|
+
(isElementNode(node) &&
|
|
2842
|
+
(hasOxvoSessionsAttribute(node, 'masked') || hasOxvoSessionsAttribute(node, 'obscured')))) {
|
|
2843
|
+
level = SanitizeLevel.Obscured;
|
|
2844
|
+
}
|
|
2845
|
+
if (parentLevel === SanitizeLevel.Hidden ||
|
|
2846
|
+
(isElementNode(node) &&
|
|
2847
|
+
(hasOxvoSessionsAttribute(node, 'htmlmasked') || hasOxvoSessionsAttribute(node, 'hidden')))) {
|
|
2848
|
+
level = SanitizeLevel.Hidden;
|
|
2849
|
+
}
|
|
2850
|
+
if (this.options.domSanitizer !== undefined && isElementNode(node)) {
|
|
2851
|
+
const sanitizeLevel = this.options.domSanitizer(node);
|
|
2852
|
+
if (sanitizeLevel === SanitizeLevel.Obscured && level < SanitizeLevel.Obscured) {
|
|
2853
|
+
level = SanitizeLevel.Obscured;
|
|
2854
|
+
}
|
|
2855
|
+
if (sanitizeLevel === SanitizeLevel.Hidden) {
|
|
2856
|
+
level = SanitizeLevel.Hidden;
|
|
2857
|
+
}
|
|
2858
|
+
}
|
|
2859
|
+
return level;
|
|
2860
|
+
}
|
|
2861
|
+
getLevel(id) {
|
|
2862
|
+
return this.levels.get(id) ?? SanitizeLevel.Plain;
|
|
2863
|
+
}
|
|
2864
|
+
// Sets a node's level in either direction and returns the previous level.
|
|
2865
|
+
setLevel(id, level) {
|
|
2866
|
+
const prev = this.getLevel(id);
|
|
2867
|
+
if (level === SanitizeLevel.Plain) {
|
|
2868
|
+
this.levels.delete(id);
|
|
2869
|
+
}
|
|
2870
|
+
else {
|
|
2871
|
+
this.levels.set(id, level);
|
|
2872
|
+
}
|
|
2873
|
+
return prev;
|
|
2874
|
+
}
|
|
2875
|
+
handleNode(id, parentID, node) {
|
|
2876
|
+
const level = this.computeLevel(node, this.getLevel(parentID));
|
|
2877
|
+
// Escalate-only: commits never lower a level, only resanitize/setLevel do.
|
|
2878
|
+
if (level > this.getLevel(id)) {
|
|
2879
|
+
this.setLevel(id, level);
|
|
2880
|
+
}
|
|
2881
|
+
}
|
|
2882
|
+
sanitize(id, data) {
|
|
2883
|
+
if (this.getLevel(id) >= SanitizeLevel.Obscured) {
|
|
2884
|
+
// TODO: is it the best place to put trim() ? Might trimmed spaces be considered in layout in certain cases?
|
|
2885
|
+
return stringWiper(data);
|
|
2886
|
+
}
|
|
2887
|
+
if (this.options.maskTextNumbers) {
|
|
2888
|
+
data = data.replace(/\d/g, '0');
|
|
2889
|
+
}
|
|
2890
|
+
if (this.options.maskTextEmails) {
|
|
2891
|
+
data = data.replace(/^\w+([+.-]\w+)*@\w+([.-]\w+)*\.\w{2,3}$/g, (email) => {
|
|
2892
|
+
const [name, domain] = email.split('@');
|
|
2893
|
+
const [domainName, host] = domain.split('.');
|
|
2894
|
+
return `${stars(name)}@${stars(domainName)}.${stars(host)}`;
|
|
2895
|
+
});
|
|
2896
|
+
}
|
|
2897
|
+
return data;
|
|
2898
|
+
}
|
|
2899
|
+
isObscured(id) {
|
|
2900
|
+
return this.getLevel(id) >= SanitizeLevel.Obscured;
|
|
2901
|
+
}
|
|
2902
|
+
isHidden(id) {
|
|
2903
|
+
return this.getLevel(id) === SanitizeLevel.Hidden;
|
|
2904
|
+
}
|
|
2905
|
+
getInnerTextSecure(el) {
|
|
2906
|
+
const id = this.app.nodes.getID(el);
|
|
2907
|
+
if (!id) {
|
|
2908
|
+
return '';
|
|
2909
|
+
}
|
|
2910
|
+
return this.sanitize(id, el.innerText);
|
|
2911
|
+
}
|
|
2912
|
+
clear() {
|
|
2913
|
+
this.levels.clear();
|
|
2914
|
+
}
|
|
3216
2915
|
}
|
|
3217
2916
|
|
|
3218
2917
|
const iconCache = {};
|
|
@@ -3366,6 +3065,12 @@ class Observer {
|
|
|
3366
3065
|
this.inlineRemoteCss = false;
|
|
3367
3066
|
this.inlinerOptions = undefined;
|
|
3368
3067
|
this.domParser = new DOMParser();
|
|
3068
|
+
/**
|
|
3069
|
+
* Bumped on every disconnect(). Stale async CSS-inlining callbacks from a
|
|
3070
|
+
* previous session (e.g. agent reload → tracker restart) compare against
|
|
3071
|
+
* this and bail instead of sending messages that reference vanished node ids.
|
|
3072
|
+
*/
|
|
3073
|
+
this.generation = 0;
|
|
3369
3074
|
this.throttling = true;
|
|
3370
3075
|
this.throttledSetNodeData = throttleWithTrailing((id, parentElement, data) => this.sendNodeData(id, parentElement, data), 30);
|
|
3371
3076
|
this.throttling = !Boolean(options.disableThrottling);
|
|
@@ -3512,18 +3217,23 @@ class Observer {
|
|
|
3512
3217
|
}
|
|
3513
3218
|
if (name === 'style' || (name === 'href' && hasTag(node, 'link'))) {
|
|
3514
3219
|
if ('rel' in node && node.rel === 'stylesheet' && this.inlineRemoteCss) {
|
|
3220
|
+
const gen = this.generation;
|
|
3515
3221
|
setTimeout(() => {
|
|
3516
3222
|
inlineRemoteCss(
|
|
3517
3223
|
// @ts-ignore
|
|
3518
3224
|
node, id, this.app.getBaseHref(), nextID, (id, cssText, index, baseHref) => {
|
|
3225
|
+
if (this.generation !== gen)
|
|
3226
|
+
return;
|
|
3519
3227
|
this.app.send(AdoptedSSInsertRuleURLBased(id, cssText, index, baseHref));
|
|
3520
3228
|
}, (sheetId, ownerId) => {
|
|
3229
|
+
if (this.generation !== gen)
|
|
3230
|
+
return;
|
|
3521
3231
|
this.app.send(AdoptedSSAddOwner(sheetId, ownerId));
|
|
3522
3232
|
}, this.inlinerOptions?.forceFetch, this.inlinerOptions?.forcePlain, (cssText, fakeTextId) => {
|
|
3233
|
+
if (this.generation !== gen)
|
|
3234
|
+
return;
|
|
3523
3235
|
this.app.send(CreateTextNode(fakeTextId, id, 0));
|
|
3524
|
-
|
|
3525
|
-
this.app.send(SetCSSDataURLBased(fakeTextId, cssText, this.app.getBaseHref()));
|
|
3526
|
-
}, 10);
|
|
3236
|
+
this.app.send(SetCSSDataURLBased(fakeTextId, cssText, this.app.getBaseHref()));
|
|
3527
3237
|
});
|
|
3528
3238
|
}, 0);
|
|
3529
3239
|
return;
|
|
@@ -3783,10 +3493,105 @@ class Observer {
|
|
|
3783
3493
|
beforeCommit(this.app.nodes.getID(node));
|
|
3784
3494
|
this.commitNodes(true);
|
|
3785
3495
|
}
|
|
3496
|
+
/**
|
|
3497
|
+
* Re-evaluates sanitization for every tracked node in `root`'s subtree against
|
|
3498
|
+
* the current DOM and re-emits whatever changed. Pass the highest node you
|
|
3499
|
+
* changed (or the document root) so inherited levels propagate correctly.
|
|
3500
|
+
*/
|
|
3501
|
+
resanitizeSubtree(root) {
|
|
3502
|
+
if (!isObservable(root)) {
|
|
3503
|
+
return;
|
|
3504
|
+
}
|
|
3505
|
+
const parent = root.parentNode;
|
|
3506
|
+
const parentId = parent !== null ? this.app.nodes.getID(parent) : undefined;
|
|
3507
|
+
const parentLevel = parentId !== undefined ? this.app.sanitizer.getLevel(parentId) : SanitizeLevel.Plain;
|
|
3508
|
+
this.resanitizeNode(root, parentLevel);
|
|
3509
|
+
}
|
|
3510
|
+
resanitizeNode(node, parentLevel) {
|
|
3511
|
+
if (isIgnored(node)) {
|
|
3512
|
+
return;
|
|
3513
|
+
}
|
|
3514
|
+
const id = this.app.nodes.getID(node);
|
|
3515
|
+
if (id === undefined) {
|
|
3516
|
+
// Untracked (new, or under a hidden ancestor): the live observer handles it.
|
|
3517
|
+
return;
|
|
3518
|
+
}
|
|
3519
|
+
const newLevel = this.app.sanitizer.computeLevel(node, parentLevel);
|
|
3520
|
+
const prevLevel = this.app.sanitizer.getLevel(id);
|
|
3521
|
+
const wasHidden = prevLevel === SanitizeLevel.Hidden;
|
|
3522
|
+
const willHidden = newLevel === SanitizeLevel.Hidden;
|
|
3523
|
+
// Crossing the hidden boundary changes the rendered structure (placeholder vs
|
|
3524
|
+
// real subtree), so rebuild rather than re-emit.
|
|
3525
|
+
if (wasHidden !== willHidden) {
|
|
3526
|
+
this.recreateSubtree(node);
|
|
3527
|
+
return;
|
|
3528
|
+
}
|
|
3529
|
+
if (willHidden) {
|
|
3530
|
+
return;
|
|
3531
|
+
}
|
|
3532
|
+
// Plain <-> Obscured: same structure, only leaf content changes.
|
|
3533
|
+
if (prevLevel !== newLevel) {
|
|
3534
|
+
this.app.sanitizer.setLevel(id, newLevel);
|
|
3535
|
+
this.reemitNode(id, node);
|
|
3536
|
+
}
|
|
3537
|
+
for (let child = node.firstChild; child !== null; child = child.nextSibling) {
|
|
3538
|
+
this.resanitizeNode(child, newLevel);
|
|
3539
|
+
}
|
|
3540
|
+
}
|
|
3541
|
+
// Destroys the node player-side and re-emits its subtree from scratch (new ids)
|
|
3542
|
+
// so it materializes at the freshly-computed level.
|
|
3543
|
+
recreateSubtree(node) {
|
|
3544
|
+
const id = this.app.nodes.getID(node);
|
|
3545
|
+
if (id === undefined) {
|
|
3546
|
+
return;
|
|
3547
|
+
}
|
|
3548
|
+
this.app.send(RemoveNode(id));
|
|
3549
|
+
this.clearSubtreeRegistration(node);
|
|
3550
|
+
this.bindTree(node);
|
|
3551
|
+
this.commitNodes();
|
|
3552
|
+
}
|
|
3553
|
+
clearSubtreeRegistration(node) {
|
|
3554
|
+
const clearOne = (n) => {
|
|
3555
|
+
const oldId = this.app.nodes.getID(n);
|
|
3556
|
+
if (oldId !== undefined) {
|
|
3557
|
+
this.app.sanitizer.setLevel(oldId, SanitizeLevel.Plain);
|
|
3558
|
+
}
|
|
3559
|
+
this.app.nodes.unregisterNode(n);
|
|
3560
|
+
};
|
|
3561
|
+
const walker = document.createTreeWalker(node, NodeFilter.SHOW_ELEMENT + NodeFilter.SHOW_TEXT, {
|
|
3562
|
+
acceptNode: (n) => isIgnored(n) || this.app.nodes.getID(n) === undefined
|
|
3563
|
+
? NodeFilter.FILTER_REJECT
|
|
3564
|
+
: NodeFilter.FILTER_ACCEPT,
|
|
3565
|
+
},
|
|
3566
|
+
// @ts-ignore
|
|
3567
|
+
false);
|
|
3568
|
+
// Collect first, then clear: unregistering mutates the ids the walker reads.
|
|
3569
|
+
const subtree = [];
|
|
3570
|
+
while (walker.nextNode()) {
|
|
3571
|
+
subtree.push(walker.currentNode);
|
|
3572
|
+
}
|
|
3573
|
+
clearOne(node);
|
|
3574
|
+
subtree.forEach(clearOne);
|
|
3575
|
+
}
|
|
3576
|
+
reemitNode(id, node) {
|
|
3577
|
+
if (isTextNode(node)) {
|
|
3578
|
+
const parent = node.parentNode;
|
|
3579
|
+
if (parent !== null && isElementNode(parent)) {
|
|
3580
|
+
// re-runs sanitize() at the level we just set
|
|
3581
|
+
this.sendNodeData(id, parent, node.data);
|
|
3582
|
+
}
|
|
3583
|
+
return;
|
|
3584
|
+
}
|
|
3585
|
+
if (isElementNode(node)) {
|
|
3586
|
+
// inputs/images/canvas re-emit their own payload via registered callbacks
|
|
3587
|
+
this.app.callResanitizeCallbacks(node, id);
|
|
3588
|
+
}
|
|
3589
|
+
}
|
|
3786
3590
|
disconnect() {
|
|
3787
3591
|
this.observer.disconnect();
|
|
3788
3592
|
this.clear();
|
|
3789
3593
|
this.throttledSetNodeData.clear();
|
|
3594
|
+
this.generation++;
|
|
3790
3595
|
}
|
|
3791
3596
|
}
|
|
3792
3597
|
|
|
@@ -4021,7 +3826,10 @@ class TopObserver extends Observer {
|
|
|
4021
3826
|
this.app.debug.info('doc already observed for', id);
|
|
4022
3827
|
return;
|
|
4023
3828
|
}
|
|
4024
|
-
const observer = new IFrameObserver(this.app, false, {
|
|
3829
|
+
const observer = new IFrameObserver(this.app, false, {
|
|
3830
|
+
disableSprites: this.options.disableSprites,
|
|
3831
|
+
...getInlineOptions(this.options.inlineCss, console.warn),
|
|
3832
|
+
});
|
|
4025
3833
|
this.iframeObservers.set(iframe, observer);
|
|
4026
3834
|
this.docObservers.set(currentDoc, observer);
|
|
4027
3835
|
this.iframeObserversArr.push(observer);
|
|
@@ -4043,7 +3851,10 @@ class TopObserver extends Observer {
|
|
|
4043
3851
|
handle();
|
|
4044
3852
|
}
|
|
4045
3853
|
handleShadowRoot(shRoot) {
|
|
4046
|
-
const observer = new ShadowRootObserver(this.app
|
|
3854
|
+
const observer = new ShadowRootObserver(this.app, false, {
|
|
3855
|
+
disableSprites: this.options.disableSprites,
|
|
3856
|
+
...getInlineOptions(this.options.inlineCss, console.warn),
|
|
3857
|
+
});
|
|
4047
3858
|
this.shadowRootObservers.set(shRoot, observer);
|
|
4048
3859
|
observer.observe(shRoot.host);
|
|
4049
3860
|
}
|
|
@@ -4079,7 +3890,10 @@ class TopObserver extends Observer {
|
|
|
4079
3890
|
};
|
|
4080
3891
|
this.app.nodes.clear();
|
|
4081
3892
|
this.app.nodes.crossdomainMode(frameLevel, frameOder);
|
|
4082
|
-
const iframeObserver = new IFrameObserver(this.app
|
|
3893
|
+
const iframeObserver = new IFrameObserver(this.app, false, {
|
|
3894
|
+
disableSprites: this.options.disableSprites,
|
|
3895
|
+
...getInlineOptions(this.options.inlineCss, console.warn),
|
|
3896
|
+
});
|
|
4083
3897
|
this.iframeObservers.set(window.document, iframeObserver);
|
|
4084
3898
|
iframeObserver.syntheticObserve(rootNodeId, window.document);
|
|
4085
3899
|
}
|
|
@@ -4095,94 +3909,6 @@ class TopObserver extends Observer {
|
|
|
4095
3909
|
}
|
|
4096
3910
|
}
|
|
4097
3911
|
|
|
4098
|
-
var SanitizeLevel;
|
|
4099
|
-
(function (SanitizeLevel) {
|
|
4100
|
-
SanitizeLevel[SanitizeLevel["Plain"] = 0] = "Plain";
|
|
4101
|
-
SanitizeLevel[SanitizeLevel["Obscured"] = 1] = "Obscured";
|
|
4102
|
-
SanitizeLevel[SanitizeLevel["Hidden"] = 2] = "Hidden";
|
|
4103
|
-
})(SanitizeLevel || (SanitizeLevel = {}));
|
|
4104
|
-
const stringWiper = (input) => input
|
|
4105
|
-
.trim()
|
|
4106
|
-
.replace(/[^\f\n\r\t\v\u00a0\u1680\u2000-\u200a\u2028\u2029\u202f\u205f\u3000\ufeff\s]/g, '*');
|
|
4107
|
-
class Sanitizer {
|
|
4108
|
-
constructor(params) {
|
|
4109
|
-
this.obscured = new Set();
|
|
4110
|
-
this.hidden = new Set();
|
|
4111
|
-
this.app = params.app;
|
|
4112
|
-
const defaultOptions = {
|
|
4113
|
-
maskTextEmails: true,
|
|
4114
|
-
maskTextNumbers: false,
|
|
4115
|
-
maskAllByDefault: false,
|
|
4116
|
-
domSanitizer: undefined,
|
|
4117
|
-
};
|
|
4118
|
-
this.maskAllByDefault = params.options?.maskAllByDefault ?? false;
|
|
4119
|
-
this.options = Object.assign(defaultOptions, params.options);
|
|
4120
|
-
}
|
|
4121
|
-
handleNode(id, parentID, node) {
|
|
4122
|
-
if (this.options.maskAllByDefault) {
|
|
4123
|
-
if (isElementNode(node) && !hasOxvoSessionsAttribute(node, 'unmask')) {
|
|
4124
|
-
return this.obscured.add(id);
|
|
4125
|
-
}
|
|
4126
|
-
if (isTextNode(node) && !hasOxvoSessionsAttribute(node.parentNode, 'unmask')) {
|
|
4127
|
-
return this.obscured.add(id);
|
|
4128
|
-
}
|
|
4129
|
-
}
|
|
4130
|
-
if (this.obscured.has(parentID) ||
|
|
4131
|
-
(isElementNode(node) &&
|
|
4132
|
-
(hasOxvoSessionsAttribute(node, 'masked') || hasOxvoSessionsAttribute(node, 'obscured')))) {
|
|
4133
|
-
this.obscured.add(id);
|
|
4134
|
-
}
|
|
4135
|
-
if (this.hidden.has(parentID) ||
|
|
4136
|
-
(isElementNode(node) &&
|
|
4137
|
-
(hasOxvoSessionsAttribute(node, 'htmlmasked') || hasOxvoSessionsAttribute(node, 'hidden')))) {
|
|
4138
|
-
this.hidden.add(id);
|
|
4139
|
-
}
|
|
4140
|
-
if (this.options.domSanitizer !== undefined && isElementNode(node)) {
|
|
4141
|
-
const sanitizeLevel = this.options.domSanitizer(node);
|
|
4142
|
-
if (sanitizeLevel === SanitizeLevel.Obscured) {
|
|
4143
|
-
this.obscured.add(id);
|
|
4144
|
-
}
|
|
4145
|
-
if (sanitizeLevel === SanitizeLevel.Hidden) {
|
|
4146
|
-
this.hidden.add(id);
|
|
4147
|
-
}
|
|
4148
|
-
}
|
|
4149
|
-
}
|
|
4150
|
-
sanitize(id, data) {
|
|
4151
|
-
if (this.obscured.has(id)) {
|
|
4152
|
-
// TODO: is it the best place to put trim() ? Might trimmed spaces be considered in layout in certain cases?
|
|
4153
|
-
return stringWiper(data);
|
|
4154
|
-
}
|
|
4155
|
-
if (this.options.maskTextNumbers) {
|
|
4156
|
-
data = data.replace(/\d/g, '0');
|
|
4157
|
-
}
|
|
4158
|
-
if (this.options.maskTextEmails) {
|
|
4159
|
-
data = data.replace(/^\w+([+.-]\w+)*@\w+([.-]\w+)*\.\w{2,3}$/g, (email) => {
|
|
4160
|
-
const [name, domain] = email.split('@');
|
|
4161
|
-
const [domainName, host] = domain.split('.');
|
|
4162
|
-
return `${stars(name)}@${stars(domainName)}.${stars(host)}`;
|
|
4163
|
-
});
|
|
4164
|
-
}
|
|
4165
|
-
return data;
|
|
4166
|
-
}
|
|
4167
|
-
isObscured(id) {
|
|
4168
|
-
return this.obscured.has(id);
|
|
4169
|
-
}
|
|
4170
|
-
isHidden(id) {
|
|
4171
|
-
return this.hidden.has(id);
|
|
4172
|
-
}
|
|
4173
|
-
getInnerTextSecure(el) {
|
|
4174
|
-
const id = this.app.nodes.getID(el);
|
|
4175
|
-
if (!id) {
|
|
4176
|
-
return '';
|
|
4177
|
-
}
|
|
4178
|
-
return this.sanitize(id, el.innerText);
|
|
4179
|
-
}
|
|
4180
|
-
clear() {
|
|
4181
|
-
this.obscured.clear();
|
|
4182
|
-
this.hidden.clear();
|
|
4183
|
-
}
|
|
4184
|
-
}
|
|
4185
|
-
|
|
4186
3912
|
const tokenSeparator = '_$_';
|
|
4187
3913
|
class Session {
|
|
4188
3914
|
constructor(params) {
|
|
@@ -4382,9 +4108,10 @@ class Ticker {
|
|
|
4382
4108
|
* this value is injected during build time via rollup
|
|
4383
4109
|
* */
|
|
4384
4110
|
// @ts-ignore
|
|
4385
|
-
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";
|
|
4111
|
+
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";
|
|
4386
4112
|
const CANCELED = 'canceled';
|
|
4387
4113
|
const bufferStorageKey = 'or_buffer_1';
|
|
4114
|
+
const PROTO_VERSION = '2';
|
|
4388
4115
|
const UnsuccessfulStart = (reason) => ({ reason, success: false });
|
|
4389
4116
|
const SuccessfulStart = (body) => ({ ...body, success: true });
|
|
4390
4117
|
var ActivityState;
|
|
@@ -4428,9 +4155,14 @@ const proto = {
|
|
|
4428
4155
|
reset: 'reset-your-session-please',
|
|
4429
4156
|
};
|
|
4430
4157
|
class App {
|
|
4158
|
+
get tagMatcher() {
|
|
4159
|
+
return this.markerWatcher.matcher;
|
|
4160
|
+
}
|
|
4431
4161
|
constructor(projectKey, sessionToken, options, signalError, insideIframe) {
|
|
4432
4162
|
this.signalError = signalError;
|
|
4433
4163
|
this.insideIframe = insideIframe;
|
|
4164
|
+
// Registered by input/img/canvas to re-emit a node when its level changes.
|
|
4165
|
+
this.resanitizeCallbacks = [];
|
|
4434
4166
|
this.messages = [];
|
|
4435
4167
|
/**
|
|
4436
4168
|
* we need 2 buffers, so we don't lose anything
|
|
@@ -4442,7 +4174,7 @@ class App {
|
|
|
4442
4174
|
this.stopCallbacks = [];
|
|
4443
4175
|
this.commitCallbacks = [];
|
|
4444
4176
|
this.activityState = ActivityState.NotActive;
|
|
4445
|
-
this.version = '7.
|
|
4177
|
+
this.version = '7.3.0'; // TODO: version compatability check inside each plugin.
|
|
4446
4178
|
this.socketMode = false;
|
|
4447
4179
|
this.compressionThreshold = 24 * 1000;
|
|
4448
4180
|
this.bc = null;
|
|
@@ -4464,9 +4196,19 @@ class App {
|
|
|
4464
4196
|
if (!data || event.source === window)
|
|
4465
4197
|
return;
|
|
4466
4198
|
if (data.line === proto.startIframe) {
|
|
4467
|
-
|
|
4199
|
+
// Avoid corrupting an in-flight start; let it complete.
|
|
4200
|
+
if (this.activityState === ActivityState.Starting)
|
|
4468
4201
|
return;
|
|
4469
4202
|
try {
|
|
4203
|
+
if (this.active()) {
|
|
4204
|
+
this.stop();
|
|
4205
|
+
}
|
|
4206
|
+
if (data.token) {
|
|
4207
|
+
this.session.setSessionSecret(data.token, this.projectKey);
|
|
4208
|
+
}
|
|
4209
|
+
if (data.id !== undefined) {
|
|
4210
|
+
this.rootId = data.id;
|
|
4211
|
+
}
|
|
4470
4212
|
this.allowAppStart();
|
|
4471
4213
|
void this.start();
|
|
4472
4214
|
}
|
|
@@ -4492,17 +4234,22 @@ class App {
|
|
|
4492
4234
|
}
|
|
4493
4235
|
}
|
|
4494
4236
|
};
|
|
4495
|
-
/**
|
|
4496
|
-
* context ids for iframes,
|
|
4497
|
-
* order is not so important as long as its consistent
|
|
4498
|
-
* */
|
|
4499
4237
|
this.trackedFrames = [];
|
|
4238
|
+
this.frameLastSeen = new Map();
|
|
4239
|
+
this.FRAME_STALE_MS = 1500;
|
|
4500
4240
|
this.crossDomainIframeListener = (event) => {
|
|
4501
|
-
if (
|
|
4241
|
+
if (event.source === window)
|
|
4502
4242
|
return;
|
|
4503
4243
|
const { data } = event;
|
|
4504
4244
|
if (!data)
|
|
4505
4245
|
return;
|
|
4246
|
+
// Record liveness regardless of our own active state so the queue can prune
|
|
4247
|
+
// stale contexts reliably once we resume dispatching commands after a cycle.
|
|
4248
|
+
if ((data.line === proto.polling || data.line === proto.iframeSignal) && data.context) {
|
|
4249
|
+
this.frameLastSeen.set(data.context, Date.now());
|
|
4250
|
+
}
|
|
4251
|
+
if (!this.active())
|
|
4252
|
+
return;
|
|
4506
4253
|
if (data.line === proto.iframeSignal) {
|
|
4507
4254
|
// @ts-ignore
|
|
4508
4255
|
event.source?.postMessage({ ping: true, line: proto.parentAlive }, '*');
|
|
@@ -4600,16 +4347,41 @@ class App {
|
|
|
4600
4347
|
if (!this.pollingQueue.order.length) {
|
|
4601
4348
|
return;
|
|
4602
4349
|
}
|
|
4603
|
-
|
|
4604
|
-
|
|
4605
|
-
|
|
4350
|
+
this.pruneStaleFrames();
|
|
4351
|
+
const liveSet = new Set(this.trackedFrames);
|
|
4352
|
+
while (this.pollingQueue.order.length > 0) {
|
|
4353
|
+
const head = this.pollingQueue.order[0];
|
|
4354
|
+
this.pollingQueue[head] = this.pollingQueue[head].filter((ctx) => liveSet.has(ctx));
|
|
4355
|
+
if (this.pollingQueue[head].length === 0) {
|
|
4356
|
+
delete this.pollingQueue[head];
|
|
4357
|
+
this.pollingQueue.order.shift();
|
|
4358
|
+
}
|
|
4359
|
+
else {
|
|
4360
|
+
break;
|
|
4361
|
+
}
|
|
4362
|
+
}
|
|
4363
|
+
if (!this.pollingQueue.order.length) {
|
|
4606
4364
|
return;
|
|
4607
4365
|
}
|
|
4366
|
+
const nextCommand = this.pollingQueue.order[0];
|
|
4608
4367
|
if (this.pollingQueue[nextCommand].includes(data.context)) {
|
|
4609
4368
|
this.pollingQueue[nextCommand] = this.pollingQueue[nextCommand].filter((c) => c !== data.context);
|
|
4369
|
+
const message = { line: nextCommand };
|
|
4370
|
+
if (nextCommand === proto.startIframe) {
|
|
4371
|
+
message.token = this.session.getSessionSecret(this.projectKey);
|
|
4372
|
+
const targetFrame = this.pageFrames.find((f) => f.contentWindow === event.source) ||
|
|
4373
|
+
Array.from(document.querySelectorAll('iframe')).find((f) => f.contentWindow === event.source);
|
|
4374
|
+
if (targetFrame) {
|
|
4375
|
+
const nodeId = targetFrame[this.options.node_id];
|
|
4376
|
+
if (nodeId !== undefined) {
|
|
4377
|
+
message.id = nodeId;
|
|
4378
|
+
}
|
|
4379
|
+
}
|
|
4380
|
+
}
|
|
4610
4381
|
// @ts-ignore
|
|
4611
|
-
event.source?.postMessage(
|
|
4382
|
+
event.source?.postMessage(message, '*');
|
|
4612
4383
|
if (this.pollingQueue[nextCommand].length === 0) {
|
|
4384
|
+
delete this.pollingQueue[nextCommand];
|
|
4613
4385
|
this.pollingQueue.order.shift();
|
|
4614
4386
|
}
|
|
4615
4387
|
}
|
|
@@ -4623,6 +4395,11 @@ class App {
|
|
|
4623
4395
|
order: [],
|
|
4624
4396
|
};
|
|
4625
4397
|
this.addCommand = (cmd) => {
|
|
4398
|
+
this.pruneStaleFrames();
|
|
4399
|
+
if (this.pollingQueue[cmd]) {
|
|
4400
|
+
this.pollingQueue[cmd] = Array.from(new Set([...this.pollingQueue[cmd], ...this.trackedFrames]));
|
|
4401
|
+
return;
|
|
4402
|
+
}
|
|
4626
4403
|
this.pollingQueue.order.push(cmd);
|
|
4627
4404
|
this.pollingQueue[cmd] = [...this.trackedFrames];
|
|
4628
4405
|
};
|
|
@@ -4751,6 +4528,26 @@ class App {
|
|
|
4751
4528
|
this.restartCanvasTracking = () => {
|
|
4752
4529
|
this.canvasRecorder?.restartTracking();
|
|
4753
4530
|
};
|
|
4531
|
+
this.attachResanitizeCallback = (cb) => {
|
|
4532
|
+
this.resanitizeCallbacks.push(cb);
|
|
4533
|
+
};
|
|
4534
|
+
this.callResanitizeCallbacks = (node, id) => {
|
|
4535
|
+
this.resanitizeCallbacks.forEach((cb) => cb(node, id));
|
|
4536
|
+
};
|
|
4537
|
+
this.resanitize = (el) => {
|
|
4538
|
+
const root = el ?? (IN_BROWSER ? document.documentElement : undefined);
|
|
4539
|
+
if (!root) {
|
|
4540
|
+
return;
|
|
4541
|
+
}
|
|
4542
|
+
this.observer.resanitizeSubtree(root);
|
|
4543
|
+
};
|
|
4544
|
+
this.checkSanitization = (el) => {
|
|
4545
|
+
const id = this.nodes.getID(el);
|
|
4546
|
+
if (id === undefined) {
|
|
4547
|
+
return undefined;
|
|
4548
|
+
}
|
|
4549
|
+
return this.sanitizer.getLevel(id);
|
|
4550
|
+
};
|
|
4754
4551
|
this.flushBuffer = async (buffer) => {
|
|
4755
4552
|
return new Promise((res, reject) => {
|
|
4756
4553
|
if (buffer.length === 0) {
|
|
@@ -4792,6 +4589,7 @@ class App {
|
|
|
4792
4589
|
__is_snippet: false,
|
|
4793
4590
|
__debug_report_edp: null,
|
|
4794
4591
|
__debug__: LogLevel.Silent,
|
|
4592
|
+
__local_debug: false,
|
|
4795
4593
|
localStorage: null,
|
|
4796
4594
|
sessionStorage: null,
|
|
4797
4595
|
forceSingleTab: false,
|
|
@@ -4850,9 +4648,7 @@ class App {
|
|
|
4850
4648
|
});
|
|
4851
4649
|
this.session.attachUpdateCallback(({ userID, metadata }) => {
|
|
4852
4650
|
if (userID != null) {
|
|
4853
|
-
if (!userID ||
|
|
4854
|
-
typeof userID !== 'string' ||
|
|
4855
|
-
userID.trim().length === 0) {
|
|
4651
|
+
if (!userID || typeof userID !== 'string' || userID.trim().length === 0) {
|
|
4856
4652
|
this.debug.warn('Invalid userID (must be type string), ignoring.');
|
|
4857
4653
|
return;
|
|
4858
4654
|
}
|
|
@@ -4874,9 +4670,6 @@ class App {
|
|
|
4874
4670
|
window.addEventListener('message', this.parentCrossDomainFrameListener);
|
|
4875
4671
|
window.addEventListener('message', this.crossDomainIframeListener);
|
|
4876
4672
|
setInterval(() => {
|
|
4877
|
-
if (document.hidden) {
|
|
4878
|
-
return;
|
|
4879
|
-
}
|
|
4880
4673
|
window.parent.postMessage({
|
|
4881
4674
|
line: proto.polling,
|
|
4882
4675
|
context: this.contextId,
|
|
@@ -4939,6 +4732,16 @@ class App {
|
|
|
4939
4732
|
};
|
|
4940
4733
|
}
|
|
4941
4734
|
}
|
|
4735
|
+
pruneStaleFrames() {
|
|
4736
|
+
const staleAfter = Date.now() - this.FRAME_STALE_MS;
|
|
4737
|
+
this.trackedFrames = this.trackedFrames.filter((ctx) => {
|
|
4738
|
+
const last = this.frameLastSeen.get(ctx);
|
|
4739
|
+
if (last !== undefined && last >= staleAfter)
|
|
4740
|
+
return true;
|
|
4741
|
+
this.frameLastSeen.delete(ctx);
|
|
4742
|
+
return false;
|
|
4743
|
+
});
|
|
4744
|
+
}
|
|
4942
4745
|
allowAppStart() {
|
|
4943
4746
|
this.canStart = true;
|
|
4944
4747
|
if (this.startTimeout) {
|
|
@@ -5038,22 +4841,39 @@ class App {
|
|
|
5038
4841
|
}
|
|
5039
4842
|
else if (data.type === 'compress') {
|
|
5040
4843
|
const batch = data.batch;
|
|
4844
|
+
const dataType = data.dataType;
|
|
5041
4845
|
const batchSize = batch.byteLength;
|
|
5042
|
-
|
|
5043
|
-
|
|
5044
|
-
|
|
5045
|
-
|
|
5046
|
-
|
|
5047
|
-
|
|
5048
|
-
|
|
5049
|
-
|
|
5050
|
-
|
|
4846
|
+
const hasCompressionAPI = 'CompressionStream' in globalThis;
|
|
4847
|
+
if (batchSize > this.compressionThreshold && hasCompressionAPI) {
|
|
4848
|
+
const blob = new Blob([batch]);
|
|
4849
|
+
const stream = blob.stream().pipeThrough(new CompressionStream('gzip'));
|
|
4850
|
+
new Response(stream)
|
|
4851
|
+
.arrayBuffer()
|
|
4852
|
+
.then((compressedBuffer) => {
|
|
4853
|
+
this.worker?.postMessage({
|
|
4854
|
+
type: 'compressed',
|
|
4855
|
+
batch: new Uint8Array(compressedBuffer),
|
|
4856
|
+
dataType,
|
|
4857
|
+
});
|
|
4858
|
+
})
|
|
4859
|
+
.catch((err) => {
|
|
4860
|
+
this.debug.error('OxvoSessions compression error:', err);
|
|
4861
|
+
this.worker?.postMessage({ type: 'uncompressed', batch: batch, dataType });
|
|
5051
4862
|
});
|
|
5052
4863
|
}
|
|
5053
4864
|
else {
|
|
5054
|
-
this.worker?.postMessage({ type: 'uncompressed', batch: batch });
|
|
4865
|
+
this.worker?.postMessage({ type: 'uncompressed', batch: batch, dataType });
|
|
5055
4866
|
}
|
|
5056
4867
|
}
|
|
4868
|
+
else if (data.type === 'local_save') {
|
|
4869
|
+
const blob = new Blob([data.batch], { type: 'application/octet-stream' });
|
|
4870
|
+
const url = URL.createObjectURL(blob);
|
|
4871
|
+
const a = document.createElement('a');
|
|
4872
|
+
a.href = url;
|
|
4873
|
+
a.download = data.name;
|
|
4874
|
+
a.click();
|
|
4875
|
+
URL.revokeObjectURL(url);
|
|
4876
|
+
}
|
|
5057
4877
|
else if (data.type === 'queue_empty') {
|
|
5058
4878
|
this.onSessionSent();
|
|
5059
4879
|
}
|
|
@@ -5092,19 +4912,31 @@ class App {
|
|
|
5092
4912
|
this.messages.length = 0;
|
|
5093
4913
|
return;
|
|
5094
4914
|
}
|
|
5095
|
-
if (this.worker === undefined
|
|
4915
|
+
if (this.worker === undefined) {
|
|
5096
4916
|
return;
|
|
5097
4917
|
}
|
|
5098
4918
|
if (!this.messages.length) {
|
|
5099
|
-
//
|
|
4919
|
+
// Send a keepalive batch every ~30s (1000 * 30ms) to keep the session active
|
|
5100
4920
|
if (this.emptyBatchCounter < 1000) {
|
|
5101
4921
|
this.emptyBatchCounter++;
|
|
5102
4922
|
return;
|
|
5103
4923
|
}
|
|
4924
|
+
// Keepalive: send just Timestamp + TabData so the session stays alive
|
|
4925
|
+
this.emptyBatchCounter = 0;
|
|
4926
|
+
try {
|
|
4927
|
+
this.worker?.postMessage([Timestamp(this.timestamp()), TabData(this.session.getTabId())]);
|
|
4928
|
+
}
|
|
4929
|
+
catch (e) {
|
|
4930
|
+
this._debug('worker_keepalive', e);
|
|
4931
|
+
}
|
|
4932
|
+
return;
|
|
5104
4933
|
}
|
|
5105
4934
|
this.emptyBatchCounter = 0;
|
|
5106
4935
|
try {
|
|
5107
4936
|
requestIdleCb(() => {
|
|
4937
|
+
if (!this.messages.length) {
|
|
4938
|
+
return;
|
|
4939
|
+
}
|
|
5108
4940
|
this.messages.unshift(Timestamp(this.timestamp()), TabData(this.session.getTabId()));
|
|
5109
4941
|
this.worker?.postMessage(this.messages);
|
|
5110
4942
|
this.commitCallbacks.forEach((cb) => cb(this.messages));
|
|
@@ -5298,9 +5130,14 @@ class App {
|
|
|
5298
5130
|
}
|
|
5299
5131
|
}
|
|
5300
5132
|
checkSessionToken(startNewSession) {
|
|
5301
|
-
const
|
|
5302
|
-
|
|
5133
|
+
const needReset = this.sessionStorage.getItem(this.options.session_reset_key) !== null;
|
|
5134
|
+
let needNewSessionID = startNewSession || needReset;
|
|
5303
5135
|
const sessionToken = this.session.getSessionSecret(this.projectKey);
|
|
5136
|
+
const storedVersion = this.sessionStorage.getItem(`${this.options.session_token_key}_version`);
|
|
5137
|
+
if (!storedVersion || storedVersion !== PROTO_VERSION) {
|
|
5138
|
+
needNewSessionID = true;
|
|
5139
|
+
this.sessionStorage.setItem(`${this.options.session_token_key}_version`, PROTO_VERSION);
|
|
5140
|
+
}
|
|
5304
5141
|
return needNewSessionID || !sessionToken;
|
|
5305
5142
|
}
|
|
5306
5143
|
/**
|
|
@@ -5478,6 +5315,7 @@ class App {
|
|
|
5478
5315
|
connAttemptCount: this.options.connAttemptCount,
|
|
5479
5316
|
connAttemptGap: this.options.connAttemptGap,
|
|
5480
5317
|
tabId: this.session.getTabId(),
|
|
5318
|
+
localDebug: this.options.__local_debug,
|
|
5481
5319
|
});
|
|
5482
5320
|
const ingestPoint = normalizeEndpoint(this.options.endpoint);
|
|
5483
5321
|
const r = await fetch(ingestPoint + '/v1/bootstrap', {
|
|
@@ -5492,7 +5330,7 @@ class App {
|
|
|
5492
5330
|
token: '',
|
|
5493
5331
|
})),
|
|
5494
5332
|
});
|
|
5495
|
-
const { session = {}, client = {}, limits = {} } = await r.json();
|
|
5333
|
+
const { session = {}, client = {}, limits = {}, protocolVersion: offlineProtocolVersion } = await r.json();
|
|
5496
5334
|
const { token, projectId } = session;
|
|
5497
5335
|
const { browser: userBrowser, city: userCity, country: userCountry, device: userDevice, os: userOS, state: userState, } = client;
|
|
5498
5336
|
const { beacon: beaconSizeLimit } = limits;
|
|
@@ -5500,6 +5338,7 @@ class App {
|
|
|
5500
5338
|
type: 'auth',
|
|
5501
5339
|
token,
|
|
5502
5340
|
beaconSizeLimit,
|
|
5341
|
+
protocolVersion: offlineProtocolVersion,
|
|
5503
5342
|
});
|
|
5504
5343
|
this.session.assign({ projectID: projectId });
|
|
5505
5344
|
this.session.setUserInfo({
|
|
@@ -5520,6 +5359,9 @@ class App {
|
|
|
5520
5359
|
if (Object.keys(startOpts).length !== 0) {
|
|
5521
5360
|
this.prevOpts = startOpts;
|
|
5522
5361
|
}
|
|
5362
|
+
if (startOpts.startCallback) {
|
|
5363
|
+
this.userStartCallback = startOpts.startCallback;
|
|
5364
|
+
}
|
|
5523
5365
|
const isColdStart = this.activityState === ActivityState.ColdStart;
|
|
5524
5366
|
if (isColdStart && this.coldInterval) {
|
|
5525
5367
|
clearInterval(this.coldInterval);
|
|
@@ -5561,6 +5403,7 @@ class App {
|
|
|
5561
5403
|
connAttemptCount: this.options.connAttemptCount,
|
|
5562
5404
|
connAttemptGap: this.options.connAttemptGap,
|
|
5563
5405
|
tabId: this.session.getTabId(),
|
|
5406
|
+
localDebug: this.options.__local_debug,
|
|
5564
5407
|
});
|
|
5565
5408
|
const sessionToken = this.session.getSessionSecret(this.projectKey);
|
|
5566
5409
|
const isNewSession = this.checkSessionToken(startOpts.startNewSession);
|
|
@@ -5576,7 +5419,7 @@ class App {
|
|
|
5576
5419
|
body: JSON.stringify(this.buildStartPayload({
|
|
5577
5420
|
timestamp,
|
|
5578
5421
|
dry: false,
|
|
5579
|
-
bufferMs: timestamp - this.coldStartTs,
|
|
5422
|
+
bufferMs: this.coldStartTs ? timestamp - this.coldStartTs : 0,
|
|
5580
5423
|
token: isNewSession ? '' : sessionToken,
|
|
5581
5424
|
rule: conditionName,
|
|
5582
5425
|
width: window.screen.width,
|
|
@@ -5593,12 +5436,12 @@ class App {
|
|
|
5593
5436
|
this.signalError(reason, []);
|
|
5594
5437
|
return UnsuccessfulStart(reason);
|
|
5595
5438
|
}
|
|
5596
|
-
const { session = {}, device = {}, client = {}, limits = {}, canvas = {}, flags = {} } = await r.json();
|
|
5439
|
+
const { session = {}, device = {}, client = {}, limits = {}, canvas = {}, flags = {}, protocolVersion, } = await r.json();
|
|
5597
5440
|
const { token, id: sessionID, projectId: projectID, delayMs: delay, startedAt: startTimestamp, } = session;
|
|
5598
5441
|
const { id: userUUID } = device;
|
|
5599
5442
|
const { browser: userBrowser, city: userCity, country: userCountry, device: userDevice, os: userOS, state: userState, } = client;
|
|
5600
5443
|
const { beacon: beaconSizeLimit, compressAt: compressionThreshold } = limits;
|
|
5601
|
-
const { enabled: canvasEnabled, quality: canvasQuality, fps: canvasFPS } = canvas;
|
|
5444
|
+
const { enabled: canvasEnabled, quality: canvasQuality, fps: canvasFPS, framesSupport, } = canvas;
|
|
5602
5445
|
const socketOnly = flags?.socketOnly;
|
|
5603
5446
|
if (typeof token !== 'string' ||
|
|
5604
5447
|
typeof userUUID !== 'string' ||
|
|
@@ -5640,6 +5483,7 @@ class App {
|
|
|
5640
5483
|
type: 'auth',
|
|
5641
5484
|
token,
|
|
5642
5485
|
beaconSizeLimit,
|
|
5486
|
+
protocolVersion,
|
|
5643
5487
|
});
|
|
5644
5488
|
}
|
|
5645
5489
|
if (!isNewSession && token === sessionToken) {
|
|
@@ -5655,10 +5499,9 @@ class App {
|
|
|
5655
5499
|
// TODO: start as early as possible (before receiving the token)
|
|
5656
5500
|
/** after start */
|
|
5657
5501
|
this.startCallbacks.forEach((cb) => cb(onStartInfo)); // MBTODO: callbacks after DOM "mounted" (observed)
|
|
5658
|
-
if (
|
|
5659
|
-
|
|
5502
|
+
if (this.userStartCallback) {
|
|
5503
|
+
this.userStartCallback(SuccessfulStart(onStartInfo));
|
|
5660
5504
|
}
|
|
5661
|
-
await this.markerWatcher.fetchTags(normalizeEndpoint(this.options.endpoint), token);
|
|
5662
5505
|
this.activityState = ActivityState.Active;
|
|
5663
5506
|
if (this.options.crossdomain?.enabled) {
|
|
5664
5507
|
void this.bootChildrenFrames();
|
|
@@ -5672,6 +5515,7 @@ class App {
|
|
|
5672
5515
|
isDebug: this.options.canvas.__save_canvas_locally,
|
|
5673
5516
|
fixedScaling: this.options.canvas.fixedCanvasScaling,
|
|
5674
5517
|
useAnimationFrame: this.options.canvas.useAnimationFrame,
|
|
5518
|
+
framesSupport: !!framesSupport,
|
|
5675
5519
|
});
|
|
5676
5520
|
}
|
|
5677
5521
|
/** --------------- COLD START BUFFER ------------------*/
|
|
@@ -5694,6 +5538,7 @@ class App {
|
|
|
5694
5538
|
}
|
|
5695
5539
|
this.ticker.start();
|
|
5696
5540
|
this.canvasRecorder?.startTracking();
|
|
5541
|
+
void this.markerWatcher.fetchTags(normalizeEndpoint(this.options.endpoint), token);
|
|
5697
5542
|
return SuccessfulStart(onStartInfo);
|
|
5698
5543
|
}
|
|
5699
5544
|
catch (reason) {
|
|
@@ -5818,6 +5663,7 @@ class App {
|
|
|
5818
5663
|
this.canvasRecorder?.clear();
|
|
5819
5664
|
this.messages.length = 0;
|
|
5820
5665
|
this.parentActive = false;
|
|
5666
|
+
this.pageFrames = [];
|
|
5821
5667
|
}
|
|
5822
5668
|
finally {
|
|
5823
5669
|
this.activityState = ActivityState.NotActive;
|
|
@@ -6315,6 +6161,12 @@ function Img (app) {
|
|
|
6315
6161
|
sendImgAttrs(node);
|
|
6316
6162
|
observer.observe(node, { attributes: true, attributeFilter: ['src', 'srcset'] });
|
|
6317
6163
|
});
|
|
6164
|
+
// On a runtime level change, re-evaluate placeholder vs real src for this image.
|
|
6165
|
+
app.attachResanitizeCallback((node) => {
|
|
6166
|
+
if (hasTag(node, 'img')) {
|
|
6167
|
+
sendImgAttrs(node);
|
|
6168
|
+
}
|
|
6169
|
+
});
|
|
6318
6170
|
}
|
|
6319
6171
|
|
|
6320
6172
|
const INPUT_TYPES = [
|
|
@@ -6372,19 +6224,24 @@ const labelElementFor = IN_BROWSER && 'labels' in HTMLInputElement.prototype
|
|
|
6372
6224
|
}
|
|
6373
6225
|
}
|
|
6374
6226
|
};
|
|
6375
|
-
function getInputLabel(node) {
|
|
6376
|
-
|
|
6377
|
-
if (
|
|
6378
|
-
|
|
6379
|
-
|
|
6380
|
-
|
|
6381
|
-
|
|
6382
|
-
|
|
6383
|
-
|
|
6384
|
-
|
|
6385
|
-
|
|
6386
|
-
|
|
6387
|
-
|
|
6227
|
+
function getInputLabel(node, customAttributes) {
|
|
6228
|
+
const oxvosessionsLabel = getLabelAttribute(node);
|
|
6229
|
+
if (oxvosessionsLabel !== null)
|
|
6230
|
+
return normSpaces(oxvosessionsLabel).slice(0, 100);
|
|
6231
|
+
const customAttributeLabel = getCustomAttributeLabel(node, customAttributes);
|
|
6232
|
+
if (customAttributeLabel)
|
|
6233
|
+
return customAttributeLabel;
|
|
6234
|
+
if (node.id)
|
|
6235
|
+
return `#${node.id}`;
|
|
6236
|
+
const classLabel = getClassSelector(node);
|
|
6237
|
+
if (classLabel)
|
|
6238
|
+
return classLabel;
|
|
6239
|
+
const labelElement = labelElementFor(node);
|
|
6240
|
+
const label = node.name ||
|
|
6241
|
+
node.placeholder ||
|
|
6242
|
+
(labelElement && labelElement.innerText) ||
|
|
6243
|
+
node.type;
|
|
6244
|
+
return normSpaces(label || '').slice(0, 100);
|
|
6388
6245
|
}
|
|
6389
6246
|
const InputMode = {
|
|
6390
6247
|
Plain: 0,
|
|
@@ -6406,9 +6263,12 @@ function Input (app, opts) {
|
|
|
6406
6263
|
}
|
|
6407
6264
|
else if (app.sanitizer.isObscured(id) ||
|
|
6408
6265
|
(inputMode === InputMode.Plain &&
|
|
6409
|
-
((options.maskInputNumbers &&
|
|
6266
|
+
((options.maskInputNumbers &&
|
|
6267
|
+
node.type !== 'date' &&
|
|
6268
|
+
/\d\d\d\d/.test(value)) ||
|
|
6410
6269
|
(options.maskInputDates && node.type === 'date') ||
|
|
6411
|
-
(options.maskInputEmails &&
|
|
6270
|
+
(options.maskInputEmails &&
|
|
6271
|
+
(node.type === 'email' || !!~value.indexOf('@')))))) {
|
|
6412
6272
|
inputMode = InputMode.Obscured;
|
|
6413
6273
|
}
|
|
6414
6274
|
let mask = 0;
|
|
@@ -6428,11 +6288,24 @@ function Input (app, opts) {
|
|
|
6428
6288
|
const { value, mask } = getInputValue(id, node);
|
|
6429
6289
|
app.send(SetInputValue(id, value, mask));
|
|
6430
6290
|
}
|
|
6291
|
+
const tagSelectorMap = new Map();
|
|
6431
6292
|
const inputValues = new Map();
|
|
6432
6293
|
const checkboxValues = new Map();
|
|
6294
|
+
function getTagSelector(id, node) {
|
|
6295
|
+
const cached = tagSelectorMap.get(id);
|
|
6296
|
+
if (cached !== undefined)
|
|
6297
|
+
return cached;
|
|
6298
|
+
const tagMatch = app.tagMatcher.match(node);
|
|
6299
|
+
if (tagMatch) {
|
|
6300
|
+
tagSelectorMap.set(id, tagMatch.selector);
|
|
6301
|
+
return tagMatch.selector;
|
|
6302
|
+
}
|
|
6303
|
+
return null;
|
|
6304
|
+
}
|
|
6433
6305
|
app.attachStopCallback(() => {
|
|
6434
6306
|
inputValues.clear();
|
|
6435
6307
|
checkboxValues.clear();
|
|
6308
|
+
tagSelectorMap.clear();
|
|
6436
6309
|
});
|
|
6437
6310
|
function trackInputValue(id, node) {
|
|
6438
6311
|
if (inputValues.get(id) === node.value) {
|
|
@@ -6465,12 +6338,19 @@ function Input (app, opts) {
|
|
|
6465
6338
|
}, 3);
|
|
6466
6339
|
function sendInputChange(id, node, hesitationTime, inputTime) {
|
|
6467
6340
|
const { value, mask } = getInputValue(id, node);
|
|
6468
|
-
let label = getInputLabel(node);
|
|
6341
|
+
let label = getTagSelector(id, node) || getInputLabel(node, options.customAttributes);
|
|
6469
6342
|
if (app.sanitizer.maskAllByDefault) {
|
|
6470
6343
|
label = label.replaceAll(/./g, '*');
|
|
6471
6344
|
}
|
|
6472
6345
|
app.send(InputChange(id, value, mask !== 0, label, hesitationTime, inputTime));
|
|
6473
6346
|
}
|
|
6347
|
+
// Re-emit a field's value when its sanitization level changes at runtime.
|
|
6348
|
+
// getInputValue() reads the current level, so re-sending applies the new mask.
|
|
6349
|
+
app.attachResanitizeCallback((node, id) => {
|
|
6350
|
+
if (isTextFieldElement(node) || hasTag(node, 'select')) {
|
|
6351
|
+
sendInputValue(id, node);
|
|
6352
|
+
}
|
|
6353
|
+
});
|
|
6474
6354
|
app.nodes.attachNodeCallback(app.safe((node) => {
|
|
6475
6355
|
const id = app.nodes.getID(node);
|
|
6476
6356
|
if (id === undefined) {
|
|
@@ -6574,21 +6454,28 @@ function _getTarget(target, document) {
|
|
|
6574
6454
|
return target === document.documentElement ? null : target;
|
|
6575
6455
|
}
|
|
6576
6456
|
function Mouse (app, options) {
|
|
6577
|
-
const { disableClickmaps = false } = options || {};
|
|
6457
|
+
const { disableClickmaps = false, customAttributes } = options || {};
|
|
6578
6458
|
function getTargetLabel(target) {
|
|
6579
6459
|
const dl = getLabelAttribute(target);
|
|
6580
6460
|
if (dl !== null) {
|
|
6581
6461
|
return dl;
|
|
6582
6462
|
}
|
|
6583
6463
|
if (hasTag(target, 'input')) {
|
|
6584
|
-
return getInputLabel(target);
|
|
6585
|
-
}
|
|
6464
|
+
return getInputLabel(target, customAttributes);
|
|
6465
|
+
}
|
|
6466
|
+
const customAttributeLabel = getCustomAttributeLabel(target, customAttributes);
|
|
6467
|
+
if (customAttributeLabel)
|
|
6468
|
+
return customAttributeLabel;
|
|
6469
|
+
if (target.id)
|
|
6470
|
+
return `#${target.id}`;
|
|
6471
|
+
const classLabel = getClassSelector(target);
|
|
6472
|
+
if (classLabel)
|
|
6473
|
+
return classLabel;
|
|
6586
6474
|
if (isClickable(target)) {
|
|
6587
6475
|
let label = '';
|
|
6588
6476
|
if (target instanceof HTMLElement) {
|
|
6589
6477
|
label = app.sanitizer.getInnerTextSecure(target);
|
|
6590
6478
|
}
|
|
6591
|
-
label = label || target.id || target.className;
|
|
6592
6479
|
return normSpaces(label).slice(0, 100);
|
|
6593
6480
|
}
|
|
6594
6481
|
return '';
|
|
@@ -6641,7 +6528,13 @@ function Mouse (app, options) {
|
|
|
6641
6528
|
};
|
|
6642
6529
|
const patchDocument = (document, topframe = false) => {
|
|
6643
6530
|
function getSelector(id, target) {
|
|
6644
|
-
|
|
6531
|
+
if (selectorMap[id])
|
|
6532
|
+
return selectorMap[id];
|
|
6533
|
+
const tagMatch = app.tagMatcher.match(target);
|
|
6534
|
+
if (tagMatch) {
|
|
6535
|
+
return (selectorMap[id] = tagMatch.selector);
|
|
6536
|
+
}
|
|
6537
|
+
return (selectorMap[id] = _getSelector(target));
|
|
6645
6538
|
}
|
|
6646
6539
|
const attachListener = topframe
|
|
6647
6540
|
? app.attachEventListener.bind(app) // attached/removed on start/stop
|
|
@@ -6732,6 +6625,11 @@ function getCSSPath(el) {
|
|
|
6732
6625
|
return false;
|
|
6733
6626
|
if (el.id)
|
|
6734
6627
|
return `#${cssEscape(el.id)}`;
|
|
6628
|
+
// if has data attributes - use them as they are more likely to be stable and unique
|
|
6629
|
+
const dataAttr = Array.from(el.attributes).find(attr => attr.name.startsWith('data-'));
|
|
6630
|
+
if (dataAttr) {
|
|
6631
|
+
return `[${dataAttr.name}="${cssEscape(dataAttr.value)}"]`;
|
|
6632
|
+
}
|
|
6735
6633
|
const parts = [];
|
|
6736
6634
|
while (el && el.nodeType === 1 && el !== el.ownerDocument) {
|
|
6737
6635
|
if (el.id) {
|
|
@@ -7072,11 +6970,27 @@ function Scroll (app, insideIframe) {
|
|
|
7072
6970
|
}, 5, false);
|
|
7073
6971
|
}
|
|
7074
6972
|
|
|
6973
|
+
const NativeURL = URL;
|
|
6974
|
+
const defaultUrlSanitizer = (url) => {
|
|
6975
|
+
const hiddenQueryParams = ['jwt', 'password', 'reset-password', 'invitation', 'secret', 'token'];
|
|
6976
|
+
try {
|
|
6977
|
+
const u = new NativeURL(url);
|
|
6978
|
+
u.searchParams.forEach((value, key) => {
|
|
6979
|
+
if (hiddenQueryParams.includes(key.toLowerCase())) {
|
|
6980
|
+
u.searchParams.set(key, '*'.repeat(value.length));
|
|
6981
|
+
}
|
|
6982
|
+
});
|
|
6983
|
+
return u.toString();
|
|
6984
|
+
}
|
|
6985
|
+
catch {
|
|
6986
|
+
return url;
|
|
6987
|
+
}
|
|
6988
|
+
};
|
|
7075
6989
|
function Viewport (app, options) {
|
|
7076
6990
|
let url, width, height;
|
|
7077
6991
|
let navigationStart;
|
|
7078
6992
|
let referrer = document.referrer;
|
|
7079
|
-
const urlSanitizer = options?.urlSanitizer ||
|
|
6993
|
+
const urlSanitizer = options?.urlSanitizer || defaultUrlSanitizer;
|
|
7080
6994
|
const titleSanitizer = options?.titleSanitizer || ((t) => t);
|
|
7081
6995
|
const sendSetPageLocation = app.safe(() => {
|
|
7082
6996
|
const currURL = document.URL;
|
|
@@ -8902,6 +8816,8 @@ function webAnimations(app, options = {}) {
|
|
|
8902
8816
|
const lastKF = anim.effect.getKeyframes().at(-1);
|
|
8903
8817
|
if (!lastKF)
|
|
8904
8818
|
return;
|
|
8819
|
+
if (!el || !(el instanceof Element) || !el.isConnected)
|
|
8820
|
+
return;
|
|
8905
8821
|
const computedStyle = getComputedStyle(el);
|
|
8906
8822
|
const keys = Object.keys(lastKF).filter((p) => !toIgnore.includes(p));
|
|
8907
8823
|
// @ts-ignore
|
|
@@ -9313,7 +9229,7 @@ class ConstantProperties {
|
|
|
9313
9229
|
user_id: this.user_id,
|
|
9314
9230
|
distinct_id: this.deviceId,
|
|
9315
9231
|
sdk_edition: 'web',
|
|
9316
|
-
sdk_version: '7.
|
|
9232
|
+
sdk_version: '7.3.0',
|
|
9317
9233
|
timezone: getUTCOffsetString(),
|
|
9318
9234
|
search_engine: this.searchEngine,
|
|
9319
9235
|
};
|
|
@@ -9701,10 +9617,11 @@ class People {
|
|
|
9701
9617
|
* Creates batches of events, then sends them at intervals.
|
|
9702
9618
|
*/
|
|
9703
9619
|
class Batcher {
|
|
9704
|
-
constructor(backendUrl, getToken, init) {
|
|
9620
|
+
constructor(backendUrl, getToken, init, standalone) {
|
|
9705
9621
|
this.backendUrl = backendUrl;
|
|
9706
9622
|
this.getToken = getToken;
|
|
9707
9623
|
this.init = init;
|
|
9624
|
+
this.standalone = standalone;
|
|
9708
9625
|
this.autosendInterval = 5 * 1000;
|
|
9709
9626
|
this.retryTimeout = 3 * 1000;
|
|
9710
9627
|
this.retryLimit = 3;
|
|
@@ -9714,10 +9631,20 @@ class Batcher {
|
|
|
9714
9631
|
[categories.events]: [],
|
|
9715
9632
|
};
|
|
9716
9633
|
this.intervalId = null;
|
|
9634
|
+
this.stopped = false;
|
|
9635
|
+
this.paused = false;
|
|
9636
|
+
this.onVisibilityChange = () => {
|
|
9637
|
+
this.paused = document.hidden;
|
|
9638
|
+
};
|
|
9717
9639
|
}
|
|
9718
9640
|
getBatches() {
|
|
9719
9641
|
this.batch[categories.people] = this.dedupePeopleEvents();
|
|
9720
|
-
const finalData = {
|
|
9642
|
+
const finalData = {
|
|
9643
|
+
data: {
|
|
9644
|
+
[categories.people]: [...this.batch[categories.people]],
|
|
9645
|
+
[categories.events]: [...this.batch[categories.events]],
|
|
9646
|
+
},
|
|
9647
|
+
};
|
|
9721
9648
|
return finalData;
|
|
9722
9649
|
}
|
|
9723
9650
|
addEvent(event) {
|
|
@@ -9793,6 +9720,9 @@ class Batcher {
|
|
|
9793
9720
|
return Array.from(uniqueEventsByType.values());
|
|
9794
9721
|
}
|
|
9795
9722
|
sendBatch(batch) {
|
|
9723
|
+
if (this.stopped) {
|
|
9724
|
+
return;
|
|
9725
|
+
}
|
|
9796
9726
|
const sentBatch = batch;
|
|
9797
9727
|
let attempts = 0;
|
|
9798
9728
|
const send = () => {
|
|
@@ -9809,11 +9739,27 @@ class Batcher {
|
|
|
9809
9739
|
},
|
|
9810
9740
|
body: JSON.stringify(sentBatch),
|
|
9811
9741
|
})
|
|
9812
|
-
.then((response) => {
|
|
9742
|
+
.then(async (response) => {
|
|
9743
|
+
if (response.status === 401) {
|
|
9744
|
+
const body = await response.json().catch(() => null);
|
|
9745
|
+
if (!this.standalone && body?.error === 'token expired') {
|
|
9746
|
+
this.stop();
|
|
9747
|
+
return;
|
|
9748
|
+
}
|
|
9749
|
+
if (attempts < this.retryLimit) {
|
|
9750
|
+
return this.init().then(() => {
|
|
9751
|
+
send();
|
|
9752
|
+
});
|
|
9753
|
+
}
|
|
9754
|
+
return;
|
|
9755
|
+
}
|
|
9813
9756
|
if (response.status === 403) {
|
|
9814
|
-
this.
|
|
9815
|
-
|
|
9816
|
-
|
|
9757
|
+
if (attempts < this.retryLimit) {
|
|
9758
|
+
return this.init().then(() => {
|
|
9759
|
+
send();
|
|
9760
|
+
});
|
|
9761
|
+
}
|
|
9762
|
+
return;
|
|
9817
9763
|
}
|
|
9818
9764
|
if (!response.ok) {
|
|
9819
9765
|
throw new Error(`HTTP error! status: ${response.status}`);
|
|
@@ -9828,8 +9774,15 @@ class Batcher {
|
|
|
9828
9774
|
void send();
|
|
9829
9775
|
}
|
|
9830
9776
|
startAutosend() {
|
|
9777
|
+
if (this.intervalId) {
|
|
9778
|
+
clearInterval(this.intervalId);
|
|
9779
|
+
}
|
|
9780
|
+
this.paused = document.hidden;
|
|
9781
|
+
document.addEventListener('visibilitychange', this.onVisibilityChange);
|
|
9831
9782
|
this.intervalId = setInterval(() => {
|
|
9832
|
-
this.
|
|
9783
|
+
if (!this.paused) {
|
|
9784
|
+
this.flush();
|
|
9785
|
+
}
|
|
9833
9786
|
}, this.autosendInterval);
|
|
9834
9787
|
}
|
|
9835
9788
|
flush() {
|
|
@@ -9849,6 +9802,12 @@ class Batcher {
|
|
|
9849
9802
|
clearInterval(this.intervalId);
|
|
9850
9803
|
this.intervalId = null;
|
|
9851
9804
|
}
|
|
9805
|
+
document.removeEventListener('visibilitychange', this.onVisibilityChange);
|
|
9806
|
+
this.stopped = true;
|
|
9807
|
+
}
|
|
9808
|
+
restart() {
|
|
9809
|
+
this.stopped = false;
|
|
9810
|
+
this.startAutosend();
|
|
9852
9811
|
}
|
|
9853
9812
|
}
|
|
9854
9813
|
|
|
@@ -9880,7 +9839,9 @@ class Analytics {
|
|
|
9880
9839
|
if (this.standalone) {
|
|
9881
9840
|
return this.token;
|
|
9882
9841
|
}
|
|
9883
|
-
|
|
9842
|
+
else {
|
|
9843
|
+
return this.getToken();
|
|
9844
|
+
}
|
|
9884
9845
|
};
|
|
9885
9846
|
this._getTimestamp = () => {
|
|
9886
9847
|
if (this.standalone) {
|
|
@@ -9934,6 +9895,19 @@ class Analytics {
|
|
|
9934
9895
|
}
|
|
9935
9896
|
}
|
|
9936
9897
|
};
|
|
9898
|
+
/**
|
|
9899
|
+
* Used by tracker when running in blundled mode
|
|
9900
|
+
*/
|
|
9901
|
+
this.onStart = () => {
|
|
9902
|
+
if (!this.standalone) {
|
|
9903
|
+
this.batcher.restart();
|
|
9904
|
+
}
|
|
9905
|
+
};
|
|
9906
|
+
this.onStop = () => {
|
|
9907
|
+
if (!this.standalone) {
|
|
9908
|
+
this.batcher.stop();
|
|
9909
|
+
}
|
|
9910
|
+
};
|
|
9937
9911
|
this.reset = () => {
|
|
9938
9912
|
this.people.reset(true);
|
|
9939
9913
|
this.events.reset();
|
|
@@ -9972,7 +9946,7 @@ class Analytics {
|
|
|
9972
9946
|
this.standalone = !options.notStandalone;
|
|
9973
9947
|
this.token = this.sessionStorage.getItem(STORAGEKEY);
|
|
9974
9948
|
this.constantProperties = new ConstantProperties(this.localStorage, this.sessionStorage);
|
|
9975
|
-
this.batcher = new Batcher(this.backendUrl, this._getToken, this.init);
|
|
9949
|
+
this.batcher = new Batcher(this.backendUrl, this._getToken, this.init, this.standalone);
|
|
9976
9950
|
this.events = new Events(this.constantProperties, this._getTimestamp, this.batcher);
|
|
9977
9951
|
this.people = new People(this.constantProperties, this._getTimestamp, this.setUserId, this.batcher);
|
|
9978
9952
|
if (options.notStandalone) {
|
|
@@ -10025,7 +9999,7 @@ class API {
|
|
|
10025
9999
|
this.signalStartIssue = (reason, missingApi) => {
|
|
10026
10000
|
const doNotTrack = this.checkDoNotTrack();
|
|
10027
10001
|
console.log("Tracker couldn't start due to:", JSON.stringify({
|
|
10028
|
-
trackerVersion: '7.
|
|
10002
|
+
trackerVersion: '7.3.0',
|
|
10029
10003
|
siteKey: this.options.siteKey,
|
|
10030
10004
|
doNotTrack,
|
|
10031
10005
|
reason: missingApi.length ? `missing api: ${missingApi.join(',')}` : reason,
|
|
@@ -10037,6 +10011,31 @@ class API {
|
|
|
10037
10011
|
}
|
|
10038
10012
|
this.app.restartCanvasTracking();
|
|
10039
10013
|
};
|
|
10014
|
+
/**
|
|
10015
|
+
* Re-evaluates sanitization against the current DOM and re-emits whatever
|
|
10016
|
+
* changed, updating already-recorded nodes mid-session. Call after toggling
|
|
10017
|
+
* `data-oxvosessions-*` attributes or after changing whatever your `domSanitizer`
|
|
10018
|
+
* keys on (class/id/etc).
|
|
10019
|
+
*
|
|
10020
|
+
* @param el - the highest node you changed; omit to re-scan the whole document;
|
|
10021
|
+
* scanning the entire doc is O(dom size)
|
|
10022
|
+
* */
|
|
10023
|
+
this.resanitize = (el) => {
|
|
10024
|
+
if (this.app === null) {
|
|
10025
|
+
return;
|
|
10026
|
+
}
|
|
10027
|
+
this.app.resanitize(el);
|
|
10028
|
+
};
|
|
10029
|
+
/**
|
|
10030
|
+
* Returns the sanitization level the tracker currently has for a node
|
|
10031
|
+
* (0 = Plain, 1 = Obscured, 2 = Hidden), or undefined if it isn't tracked.
|
|
10032
|
+
* */
|
|
10033
|
+
this.checkSanitization = (el) => {
|
|
10034
|
+
if (this.app === null) {
|
|
10035
|
+
return undefined;
|
|
10036
|
+
}
|
|
10037
|
+
return this.app.checkSanitization(el);
|
|
10038
|
+
};
|
|
10040
10039
|
this.getSessionLink = (options) => {
|
|
10041
10040
|
if (this.app === null) {
|
|
10042
10041
|
return undefined;
|
|
@@ -10179,6 +10178,12 @@ class API {
|
|
|
10179
10178
|
: (options.analytics?.endpoint ?? options.endpoint ?? defaultEdp),
|
|
10180
10179
|
projectKey: options.siteKey,
|
|
10181
10180
|
});
|
|
10181
|
+
app.attachStartCallback(() => {
|
|
10182
|
+
this.analytics?.onStart();
|
|
10183
|
+
});
|
|
10184
|
+
app.attachStopCallback(() => {
|
|
10185
|
+
this.analytics?.onStop();
|
|
10186
|
+
});
|
|
10182
10187
|
}
|
|
10183
10188
|
if (!this.crossdomainMode) {
|
|
10184
10189
|
// no need to send iframe viewport data since its a node for us
|
|
@@ -10190,7 +10195,7 @@ class API {
|
|
|
10190
10195
|
// no tabs in iframes yet
|
|
10191
10196
|
Tabs(app);
|
|
10192
10197
|
}
|
|
10193
|
-
Mouse(app, options.mouse);
|
|
10198
|
+
Mouse(app, { ...options.mouse, customAttributes: options.customAttributes });
|
|
10194
10199
|
// inside iframe, we ignore viewport scroll
|
|
10195
10200
|
Scroll(app, this.crossdomainMode);
|
|
10196
10201
|
CSSRules(app, options.css);
|