@openreplay/tracker 16.4.11-beta.0 → 16.4.11-beta.1

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.
Files changed (102) hide show
  1. package/dist/cjs/common/messages.gen.d.ts +1 -44
  2. package/dist/cjs/entry.js +1584 -2177
  3. package/dist/cjs/entry.js.map +1 -1
  4. package/dist/cjs/index.js +1566 -2135
  5. package/dist/cjs/index.js.map +1 -1
  6. package/dist/cjs/main/app/canvas.d.ts +11 -0
  7. package/dist/cjs/main/app/index.d.ts +2 -7
  8. package/dist/cjs/main/app/messages.gen.d.ts +0 -5
  9. package/dist/cjs/main/app/nodes/idSeq.d.ts +14 -0
  10. package/dist/cjs/main/app/nodes/index.d.ts +2 -1
  11. package/dist/cjs/main/app/observer/top_observer.d.ts +2 -1
  12. package/dist/cjs/main/app/session.d.ts +1 -0
  13. package/dist/cjs/main/entry.d.ts +5 -4
  14. package/dist/cjs/main/index.d.ts +22 -17
  15. package/dist/cjs/main/modules/analytics/batcher.d.ts +45 -0
  16. package/dist/cjs/main/modules/analytics/constantProperties.d.ts +53 -0
  17. package/dist/cjs/main/modules/analytics/demo.d.ts +0 -0
  18. package/dist/cjs/main/modules/analytics/events.d.ts +37 -0
  19. package/dist/cjs/main/modules/analytics/index.d.ts +73 -0
  20. package/dist/cjs/main/modules/analytics/people.d.ts +51 -0
  21. package/dist/cjs/main/modules/analytics/types.d.ts +32 -0
  22. package/dist/cjs/main/modules/analytics/utils.d.ts +19 -0
  23. package/dist/cjs/main/modules/conditionsManager.d.ts +6 -1
  24. package/dist/cjs/main/modules/input.d.ts +2 -1
  25. package/dist/cjs/main/modules/mouse.d.ts +1 -0
  26. package/dist/cjs/main/modules/viewport.d.ts +7 -0
  27. package/dist/cjs/main/singleton.d.ts +5 -7
  28. package/dist/cjs/main/utils.d.ts +2 -0
  29. package/dist/lib/common/messages.gen.d.ts +1 -44
  30. package/dist/lib/entry.js +1583 -2178
  31. package/dist/lib/entry.js.map +1 -1
  32. package/dist/lib/index.js +1566 -2135
  33. package/dist/lib/index.js.map +1 -1
  34. package/dist/lib/main/app/canvas.d.ts +11 -0
  35. package/dist/lib/main/app/index.d.ts +2 -7
  36. package/dist/lib/main/app/messages.gen.d.ts +0 -5
  37. package/dist/lib/main/app/nodes/idSeq.d.ts +14 -0
  38. package/dist/lib/main/app/nodes/index.d.ts +2 -1
  39. package/dist/lib/main/app/observer/top_observer.d.ts +2 -1
  40. package/dist/lib/main/app/session.d.ts +1 -0
  41. package/dist/lib/main/entry.d.ts +5 -4
  42. package/dist/lib/main/index.d.ts +22 -17
  43. package/dist/lib/main/modules/analytics/batcher.d.ts +45 -0
  44. package/dist/lib/main/modules/analytics/constantProperties.d.ts +53 -0
  45. package/dist/lib/main/modules/analytics/demo.d.ts +0 -0
  46. package/dist/lib/main/modules/analytics/events.d.ts +37 -0
  47. package/dist/lib/main/modules/analytics/index.d.ts +73 -0
  48. package/dist/lib/main/modules/analytics/people.d.ts +51 -0
  49. package/dist/lib/main/modules/analytics/types.d.ts +32 -0
  50. package/dist/lib/main/modules/analytics/utils.d.ts +19 -0
  51. package/dist/lib/main/modules/conditionsManager.d.ts +6 -1
  52. package/dist/lib/main/modules/input.d.ts +2 -1
  53. package/dist/lib/main/modules/mouse.d.ts +1 -0
  54. package/dist/lib/main/modules/viewport.d.ts +7 -0
  55. package/dist/lib/main/singleton.d.ts +5 -7
  56. package/dist/lib/main/utils.d.ts +2 -0
  57. package/dist/types/common/messages.gen.d.ts +1 -44
  58. package/dist/types/main/app/canvas.d.ts +11 -0
  59. package/dist/types/main/app/index.d.ts +2 -7
  60. package/dist/types/main/app/messages.gen.d.ts +0 -5
  61. package/dist/types/main/app/nodes/idSeq.d.ts +14 -0
  62. package/dist/types/main/app/nodes/index.d.ts +2 -1
  63. package/dist/types/main/app/observer/top_observer.d.ts +2 -1
  64. package/dist/types/main/app/session.d.ts +1 -0
  65. package/dist/types/main/entry.d.ts +5 -4
  66. package/dist/types/main/index.d.ts +22 -17
  67. package/dist/types/main/modules/analytics/batcher.d.ts +45 -0
  68. package/dist/types/main/modules/analytics/constantProperties.d.ts +53 -0
  69. package/dist/types/main/modules/analytics/demo.d.ts +0 -0
  70. package/dist/types/main/modules/analytics/events.d.ts +37 -0
  71. package/dist/types/main/modules/analytics/index.d.ts +73 -0
  72. package/dist/types/main/modules/analytics/people.d.ts +51 -0
  73. package/dist/types/main/modules/analytics/types.d.ts +32 -0
  74. package/dist/types/main/modules/analytics/utils.d.ts +19 -0
  75. package/dist/types/main/modules/conditionsManager.d.ts +6 -1
  76. package/dist/types/main/modules/input.d.ts +2 -1
  77. package/dist/types/main/modules/mouse.d.ts +1 -0
  78. package/dist/types/main/modules/viewport.d.ts +7 -0
  79. package/dist/types/main/singleton.d.ts +5 -7
  80. package/dist/types/main/utils.d.ts +2 -0
  81. package/package.json +1 -1
  82. package/dist/cjs/main/modules/featureFlags.d.ts +0 -25
  83. package/dist/cjs/main/modules/userTesting/SignalManager.d.ts +0 -29
  84. package/dist/cjs/main/modules/userTesting/dnd.d.ts +0 -1
  85. package/dist/cjs/main/modules/userTesting/index.d.ts +0 -45
  86. package/dist/cjs/main/modules/userTesting/recorder.d.ts +0 -24
  87. package/dist/cjs/main/modules/userTesting/styles.d.ts +0 -277
  88. package/dist/cjs/main/modules/userTesting/utils.d.ts +0 -9
  89. package/dist/lib/main/modules/featureFlags.d.ts +0 -25
  90. package/dist/lib/main/modules/userTesting/SignalManager.d.ts +0 -29
  91. package/dist/lib/main/modules/userTesting/dnd.d.ts +0 -1
  92. package/dist/lib/main/modules/userTesting/index.d.ts +0 -45
  93. package/dist/lib/main/modules/userTesting/recorder.d.ts +0 -24
  94. package/dist/lib/main/modules/userTesting/styles.d.ts +0 -277
  95. package/dist/lib/main/modules/userTesting/utils.d.ts +0 -9
  96. package/dist/types/main/modules/featureFlags.d.ts +0 -25
  97. package/dist/types/main/modules/userTesting/SignalManager.d.ts +0 -29
  98. package/dist/types/main/modules/userTesting/dnd.d.ts +0 -1
  99. package/dist/types/main/modules/userTesting/index.d.ts +0 -45
  100. package/dist/types/main/modules/userTesting/recorder.d.ts +0 -24
  101. package/dist/types/main/modules/userTesting/styles.d.ts +0 -277
  102. package/dist/types/main/modules/userTesting/utils.d.ts +0 -9
package/dist/cjs/entry.js CHANGED
@@ -2,719 +2,6 @@
2
2
 
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
- // DEFLATE is a complex format; to read this code, you should probably check the RFC first:
6
- // https://tools.ietf.org/html/rfc1951
7
- // You may also wish to take a look at the guide I made about this program:
8
- // https://gist.github.com/101arrowz/253f31eb5abc3d9275ab943003ffecad
9
- // Some of the following code is similar to that of UZIP.js:
10
- // https://github.com/photopea/UZIP.js
11
- // However, the vast majority of the codebase has diverged from UZIP.js to increase performance and reduce bundle size.
12
- // Sometimes 0 will appear where -1 would be more appropriate. This is because using a uint
13
- // is better for memory in most engines (I *think*).
14
- var ch2 = {};
15
- var wk = (function (c, id, msg, transfer, cb) {
16
- var w = new Worker(ch2[id] || (ch2[id] = URL.createObjectURL(new Blob([
17
- c + ';addEventListener("error",function(e){e=e.error;postMessage({$e$:[e.message,e.code,e.stack]})})'
18
- ], { type: 'text/javascript' }))));
19
- w.onmessage = function (e) {
20
- var d = e.data, ed = d.$e$;
21
- if (ed) {
22
- var err = new Error(ed[0]);
23
- err['code'] = ed[1];
24
- err.stack = ed[2];
25
- cb(err, null);
26
- }
27
- else
28
- cb(null, d);
29
- };
30
- w.postMessage(msg, transfer);
31
- return w;
32
- });
33
-
34
- // aliases for shorter compressed code (most minifers don't do this)
35
- var u8 = Uint8Array, u16 = Uint16Array, i32 = Int32Array;
36
- // fixed length extra bits
37
- var fleb = new u8([0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, /* unused */ 0, 0, /* impossible */ 0]);
38
- // fixed distance extra bits
39
- var fdeb = new u8([0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, /* unused */ 0, 0]);
40
- // code length index map
41
- var clim = new u8([16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15]);
42
- // get base, reverse index map from extra bits
43
- var freb = function (eb, start) {
44
- var b = new u16(31);
45
- for (var i = 0; i < 31; ++i) {
46
- b[i] = start += 1 << eb[i - 1];
47
- }
48
- // numbers here are at max 18 bits
49
- var r = new i32(b[30]);
50
- for (var i = 1; i < 30; ++i) {
51
- for (var j = b[i]; j < b[i + 1]; ++j) {
52
- r[j] = ((j - b[i]) << 5) | i;
53
- }
54
- }
55
- return { b: b, r: r };
56
- };
57
- var _a = freb(fleb, 2), fl = _a.b, revfl = _a.r;
58
- // we can ignore the fact that the other numbers are wrong; they never happen anyway
59
- fl[28] = 258, revfl[258] = 28;
60
- var _b = freb(fdeb, 0), revfd = _b.r;
61
- // map of value to reverse (assuming 16 bits)
62
- var rev = new u16(32768);
63
- for (var i$1 = 0; i$1 < 32768; ++i$1) {
64
- // reverse table algorithm from SO
65
- var x$1 = ((i$1 & 0xAAAA) >> 1) | ((i$1 & 0x5555) << 1);
66
- x$1 = ((x$1 & 0xCCCC) >> 2) | ((x$1 & 0x3333) << 2);
67
- x$1 = ((x$1 & 0xF0F0) >> 4) | ((x$1 & 0x0F0F) << 4);
68
- rev[i$1] = (((x$1 & 0xFF00) >> 8) | ((x$1 & 0x00FF) << 8)) >> 1;
69
- }
70
- // create huffman tree from u8 "map": index -> code length for code index
71
- // mb (max bits) must be at most 15
72
- // TODO: optimize/split up?
73
- var hMap = (function (cd, mb, r) {
74
- var s = cd.length;
75
- // index
76
- var i = 0;
77
- // u16 "map": index -> # of codes with bit length = index
78
- var l = new u16(mb);
79
- // length of cd must be 288 (total # of codes)
80
- for (; i < s; ++i) {
81
- if (cd[i])
82
- ++l[cd[i] - 1];
83
- }
84
- // u16 "map": index -> minimum code for bit length = index
85
- var le = new u16(mb);
86
- for (i = 1; i < mb; ++i) {
87
- le[i] = (le[i - 1] + l[i - 1]) << 1;
88
- }
89
- var co;
90
- if (r) {
91
- // u16 "map": index -> number of actual bits, symbol for code
92
- co = new u16(1 << mb);
93
- // bits to remove for reverser
94
- var rvb = 15 - mb;
95
- for (i = 0; i < s; ++i) {
96
- // ignore 0 lengths
97
- if (cd[i]) {
98
- // num encoding both symbol and bits read
99
- var sv = (i << 4) | cd[i];
100
- // free bits
101
- var r_1 = mb - cd[i];
102
- // start value
103
- var v = le[cd[i] - 1]++ << r_1;
104
- // m is end value
105
- for (var m = v | ((1 << r_1) - 1); v <= m; ++v) {
106
- // every 16 bit value starting with the code yields the same result
107
- co[rev[v] >> rvb] = sv;
108
- }
109
- }
110
- }
111
- }
112
- else {
113
- co = new u16(s);
114
- for (i = 0; i < s; ++i) {
115
- if (cd[i]) {
116
- co[i] = rev[le[cd[i] - 1]++] >> (15 - cd[i]);
117
- }
118
- }
119
- }
120
- return co;
121
- });
122
- // fixed length tree
123
- var flt = new u8(288);
124
- for (var i$1 = 0; i$1 < 144; ++i$1)
125
- flt[i$1] = 8;
126
- for (var i$1 = 144; i$1 < 256; ++i$1)
127
- flt[i$1] = 9;
128
- for (var i$1 = 256; i$1 < 280; ++i$1)
129
- flt[i$1] = 7;
130
- for (var i$1 = 280; i$1 < 288; ++i$1)
131
- flt[i$1] = 8;
132
- // fixed distance tree
133
- var fdt = new u8(32);
134
- for (var i$1 = 0; i$1 < 32; ++i$1)
135
- fdt[i$1] = 5;
136
- // fixed length map
137
- var flm = /*#__PURE__*/ hMap(flt, 9, 0);
138
- // fixed distance map
139
- var fdm = /*#__PURE__*/ hMap(fdt, 5, 0);
140
- // get end of byte
141
- var shft = function (p) { return ((p + 7) / 8) | 0; };
142
- // typed array slice - allows garbage collector to free original reference,
143
- // while being more compatible than .slice
144
- var slc = function (v, s, e) {
145
- if (s == null || s < 0)
146
- s = 0;
147
- if (e == null || e > v.length)
148
- e = v.length;
149
- // can't use .constructor in case user-supplied
150
- return new u8(v.subarray(s, e));
151
- };
152
- // error codes
153
- var ec = [
154
- 'unexpected EOF',
155
- 'invalid block type',
156
- 'invalid length/literal',
157
- 'invalid distance',
158
- 'stream finished',
159
- 'no stream handler',
160
- ,
161
- 'no callback',
162
- 'invalid UTF-8 data',
163
- 'extra field too long',
164
- 'date not in range 1980-2099',
165
- 'filename too long',
166
- 'stream finishing',
167
- 'invalid zip data'
168
- // determined by unknown compression method
169
- ];
170
- var err = function (ind, msg, nt) {
171
- var e = new Error(msg || ec[ind]);
172
- e.code = ind;
173
- if (Error.captureStackTrace)
174
- Error.captureStackTrace(e, err);
175
- if (!nt)
176
- throw e;
177
- return e;
178
- };
179
- // starting at p, write the minimum number of bits that can hold v to d
180
- var wbits = function (d, p, v) {
181
- v <<= p & 7;
182
- var o = (p / 8) | 0;
183
- d[o] |= v;
184
- d[o + 1] |= v >> 8;
185
- };
186
- // starting at p, write the minimum number of bits (>8) that can hold v to d
187
- var wbits16 = function (d, p, v) {
188
- v <<= p & 7;
189
- var o = (p / 8) | 0;
190
- d[o] |= v;
191
- d[o + 1] |= v >> 8;
192
- d[o + 2] |= v >> 16;
193
- };
194
- // creates code lengths from a frequency table
195
- var hTree = function (d, mb) {
196
- // Need extra info to make a tree
197
- var t = [];
198
- for (var i = 0; i < d.length; ++i) {
199
- if (d[i])
200
- t.push({ s: i, f: d[i] });
201
- }
202
- var s = t.length;
203
- var t2 = t.slice();
204
- if (!s)
205
- return { t: et, l: 0 };
206
- if (s == 1) {
207
- var v = new u8(t[0].s + 1);
208
- v[t[0].s] = 1;
209
- return { t: v, l: 1 };
210
- }
211
- t.sort(function (a, b) { return a.f - b.f; });
212
- // after i2 reaches last ind, will be stopped
213
- // freq must be greater than largest possible number of symbols
214
- t.push({ s: -1, f: 25001 });
215
- var l = t[0], r = t[1], i0 = 0, i1 = 1, i2 = 2;
216
- t[0] = { s: -1, f: l.f + r.f, l: l, r: r };
217
- // efficient algorithm from UZIP.js
218
- // i0 is lookbehind, i2 is lookahead - after processing two low-freq
219
- // symbols that combined have high freq, will start processing i2 (high-freq,
220
- // non-composite) symbols instead
221
- // see https://reddit.com/r/photopea/comments/ikekht/uzipjs_questions/
222
- while (i1 != s - 1) {
223
- l = t[t[i0].f < t[i2].f ? i0++ : i2++];
224
- r = t[i0 != i1 && t[i0].f < t[i2].f ? i0++ : i2++];
225
- t[i1++] = { s: -1, f: l.f + r.f, l: l, r: r };
226
- }
227
- var maxSym = t2[0].s;
228
- for (var i = 1; i < s; ++i) {
229
- if (t2[i].s > maxSym)
230
- maxSym = t2[i].s;
231
- }
232
- // code lengths
233
- var tr = new u16(maxSym + 1);
234
- // max bits in tree
235
- var mbt = ln(t[i1 - 1], tr, 0);
236
- if (mbt > mb) {
237
- // more algorithms from UZIP.js
238
- // TODO: find out how this code works (debt)
239
- // ind debt
240
- var i = 0, dt = 0;
241
- // left cost
242
- var lft = mbt - mb, cst = 1 << lft;
243
- t2.sort(function (a, b) { return tr[b.s] - tr[a.s] || a.f - b.f; });
244
- for (; i < s; ++i) {
245
- var i2_1 = t2[i].s;
246
- if (tr[i2_1] > mb) {
247
- dt += cst - (1 << (mbt - tr[i2_1]));
248
- tr[i2_1] = mb;
249
- }
250
- else
251
- break;
252
- }
253
- dt >>= lft;
254
- while (dt > 0) {
255
- var i2_2 = t2[i].s;
256
- if (tr[i2_2] < mb)
257
- dt -= 1 << (mb - tr[i2_2]++ - 1);
258
- else
259
- ++i;
260
- }
261
- for (; i >= 0 && dt; --i) {
262
- var i2_3 = t2[i].s;
263
- if (tr[i2_3] == mb) {
264
- --tr[i2_3];
265
- ++dt;
266
- }
267
- }
268
- mbt = mb;
269
- }
270
- return { t: new u8(tr), l: mbt };
271
- };
272
- // get the max length and assign length codes
273
- var ln = function (n, l, d) {
274
- return n.s == -1
275
- ? Math.max(ln(n.l, l, d + 1), ln(n.r, l, d + 1))
276
- : (l[n.s] = d);
277
- };
278
- // length codes generation
279
- var lc = function (c) {
280
- var s = c.length;
281
- // Note that the semicolon was intentional
282
- while (s && !c[--s])
283
- ;
284
- var cl = new u16(++s);
285
- // ind num streak
286
- var cli = 0, cln = c[0], cls = 1;
287
- var w = function (v) { cl[cli++] = v; };
288
- for (var i = 1; i <= s; ++i) {
289
- if (c[i] == cln && i != s)
290
- ++cls;
291
- else {
292
- if (!cln && cls > 2) {
293
- for (; cls > 138; cls -= 138)
294
- w(32754);
295
- if (cls > 2) {
296
- w(cls > 10 ? ((cls - 11) << 5) | 28690 : ((cls - 3) << 5) | 12305);
297
- cls = 0;
298
- }
299
- }
300
- else if (cls > 3) {
301
- w(cln), --cls;
302
- for (; cls > 6; cls -= 6)
303
- w(8304);
304
- if (cls > 2)
305
- w(((cls - 3) << 5) | 8208), cls = 0;
306
- }
307
- while (cls--)
308
- w(cln);
309
- cls = 1;
310
- cln = c[i];
311
- }
312
- }
313
- return { c: cl.subarray(0, cli), n: s };
314
- };
315
- // calculate the length of output from tree, code lengths
316
- var clen = function (cf, cl) {
317
- var l = 0;
318
- for (var i = 0; i < cl.length; ++i)
319
- l += cf[i] * cl[i];
320
- return l;
321
- };
322
- // writes a fixed block
323
- // returns the new bit pos
324
- var wfblk = function (out, pos, dat) {
325
- // no need to write 00 as type: TypedArray defaults to 0
326
- var s = dat.length;
327
- var o = shft(pos + 2);
328
- out[o] = s & 255;
329
- out[o + 1] = s >> 8;
330
- out[o + 2] = out[o] ^ 255;
331
- out[o + 3] = out[o + 1] ^ 255;
332
- for (var i = 0; i < s; ++i)
333
- out[o + i + 4] = dat[i];
334
- return (o + 4 + s) * 8;
335
- };
336
- // writes a block
337
- var wblk = function (dat, out, final, syms, lf, df, eb, li, bs, bl, p) {
338
- wbits(out, p++, final);
339
- ++lf[256];
340
- var _a = hTree(lf, 15), dlt = _a.t, mlb = _a.l;
341
- var _b = hTree(df, 15), ddt = _b.t, mdb = _b.l;
342
- var _c = lc(dlt), lclt = _c.c, nlc = _c.n;
343
- var _d = lc(ddt), lcdt = _d.c, ndc = _d.n;
344
- var lcfreq = new u16(19);
345
- for (var i = 0; i < lclt.length; ++i)
346
- ++lcfreq[lclt[i] & 31];
347
- for (var i = 0; i < lcdt.length; ++i)
348
- ++lcfreq[lcdt[i] & 31];
349
- var _e = hTree(lcfreq, 7), lct = _e.t, mlcb = _e.l;
350
- var nlcc = 19;
351
- for (; nlcc > 4 && !lct[clim[nlcc - 1]]; --nlcc)
352
- ;
353
- var flen = (bl + 5) << 3;
354
- var ftlen = clen(lf, flt) + clen(df, fdt) + eb;
355
- var dtlen = clen(lf, dlt) + clen(df, ddt) + eb + 14 + 3 * nlcc + clen(lcfreq, lct) + 2 * lcfreq[16] + 3 * lcfreq[17] + 7 * lcfreq[18];
356
- if (bs >= 0 && flen <= ftlen && flen <= dtlen)
357
- return wfblk(out, p, dat.subarray(bs, bs + bl));
358
- var lm, ll, dm, dl;
359
- wbits(out, p, 1 + (dtlen < ftlen)), p += 2;
360
- if (dtlen < ftlen) {
361
- lm = hMap(dlt, mlb, 0), ll = dlt, dm = hMap(ddt, mdb, 0), dl = ddt;
362
- var llm = hMap(lct, mlcb, 0);
363
- wbits(out, p, nlc - 257);
364
- wbits(out, p + 5, ndc - 1);
365
- wbits(out, p + 10, nlcc - 4);
366
- p += 14;
367
- for (var i = 0; i < nlcc; ++i)
368
- wbits(out, p + 3 * i, lct[clim[i]]);
369
- p += 3 * nlcc;
370
- var lcts = [lclt, lcdt];
371
- for (var it = 0; it < 2; ++it) {
372
- var clct = lcts[it];
373
- for (var i = 0; i < clct.length; ++i) {
374
- var len = clct[i] & 31;
375
- wbits(out, p, llm[len]), p += lct[len];
376
- if (len > 15)
377
- wbits(out, p, (clct[i] >> 5) & 127), p += clct[i] >> 12;
378
- }
379
- }
380
- }
381
- else {
382
- lm = flm, ll = flt, dm = fdm, dl = fdt;
383
- }
384
- for (var i = 0; i < li; ++i) {
385
- var sym = syms[i];
386
- if (sym > 255) {
387
- var len = (sym >> 18) & 31;
388
- wbits16(out, p, lm[len + 257]), p += ll[len + 257];
389
- if (len > 7)
390
- wbits(out, p, (sym >> 23) & 31), p += fleb[len];
391
- var dst = sym & 31;
392
- wbits16(out, p, dm[dst]), p += dl[dst];
393
- if (dst > 3)
394
- wbits16(out, p, (sym >> 5) & 8191), p += fdeb[dst];
395
- }
396
- else {
397
- wbits16(out, p, lm[sym]), p += ll[sym];
398
- }
399
- }
400
- wbits16(out, p, lm[256]);
401
- return p + ll[256];
402
- };
403
- // deflate options (nice << 13) | chain
404
- var deo = /*#__PURE__*/ new i32([65540, 131080, 131088, 131104, 262176, 1048704, 1048832, 2114560, 2117632]);
405
- // empty
406
- var et = /*#__PURE__*/ new u8(0);
407
- // compresses data into a raw DEFLATE buffer
408
- var dflt = function (dat, lvl, plvl, pre, post, st) {
409
- var s = st.z || dat.length;
410
- var o = new u8(pre + s + 5 * (1 + Math.ceil(s / 7000)) + post);
411
- // writing to this writes to the output buffer
412
- var w = o.subarray(pre, o.length - post);
413
- var lst = st.l;
414
- var pos = (st.r || 0) & 7;
415
- if (lvl) {
416
- if (pos)
417
- w[0] = st.r >> 3;
418
- var opt = deo[lvl - 1];
419
- var n = opt >> 13, c = opt & 8191;
420
- var msk_1 = (1 << plvl) - 1;
421
- // prev 2-byte val map curr 2-byte val map
422
- var prev = st.p || new u16(32768), head = st.h || new u16(msk_1 + 1);
423
- var bs1_1 = Math.ceil(plvl / 3), bs2_1 = 2 * bs1_1;
424
- var hsh = function (i) { return (dat[i] ^ (dat[i + 1] << bs1_1) ^ (dat[i + 2] << bs2_1)) & msk_1; };
425
- // 24576 is an arbitrary number of maximum symbols per block
426
- // 424 buffer for last block
427
- var syms = new i32(25000);
428
- // length/literal freq distance freq
429
- var lf = new u16(288), df = new u16(32);
430
- // l/lcnt exbits index l/lind waitdx blkpos
431
- var lc_1 = 0, eb = 0, i = st.i || 0, li = 0, wi = st.w || 0, bs = 0;
432
- for (; i + 2 < s; ++i) {
433
- // hash value
434
- var hv = hsh(i);
435
- // index mod 32768 previous index mod
436
- var imod = i & 32767, pimod = head[hv];
437
- prev[imod] = pimod;
438
- head[hv] = imod;
439
- // We always should modify head and prev, but only add symbols if
440
- // this data is not yet processed ("wait" for wait index)
441
- if (wi <= i) {
442
- // bytes remaining
443
- var rem = s - i;
444
- if ((lc_1 > 7000 || li > 24576) && (rem > 423 || !lst)) {
445
- pos = wblk(dat, w, 0, syms, lf, df, eb, li, bs, i - bs, pos);
446
- li = lc_1 = eb = 0, bs = i;
447
- for (var j = 0; j < 286; ++j)
448
- lf[j] = 0;
449
- for (var j = 0; j < 30; ++j)
450
- df[j] = 0;
451
- }
452
- // len dist chain
453
- var l = 2, d = 0, ch_1 = c, dif = imod - pimod & 32767;
454
- if (rem > 2 && hv == hsh(i - dif)) {
455
- var maxn = Math.min(n, rem) - 1;
456
- var maxd = Math.min(32767, i);
457
- // max possible length
458
- // not capped at dif because decompressors implement "rolling" index population
459
- var ml = Math.min(258, rem);
460
- while (dif <= maxd && --ch_1 && imod != pimod) {
461
- if (dat[i + l] == dat[i + l - dif]) {
462
- var nl = 0;
463
- for (; nl < ml && dat[i + nl] == dat[i + nl - dif]; ++nl)
464
- ;
465
- if (nl > l) {
466
- l = nl, d = dif;
467
- // break out early when we reach "nice" (we are satisfied enough)
468
- if (nl > maxn)
469
- break;
470
- // now, find the rarest 2-byte sequence within this
471
- // length of literals and search for that instead.
472
- // Much faster than just using the start
473
- var mmd = Math.min(dif, nl - 2);
474
- var md = 0;
475
- for (var j = 0; j < mmd; ++j) {
476
- var ti = i - dif + j & 32767;
477
- var pti = prev[ti];
478
- var cd = ti - pti & 32767;
479
- if (cd > md)
480
- md = cd, pimod = ti;
481
- }
482
- }
483
- }
484
- // check the previous match
485
- imod = pimod, pimod = prev[imod];
486
- dif += imod - pimod & 32767;
487
- }
488
- }
489
- // d will be nonzero only when a match was found
490
- if (d) {
491
- // store both dist and len data in one int32
492
- // Make sure this is recognized as a len/dist with 28th bit (2^28)
493
- syms[li++] = 268435456 | (revfl[l] << 18) | revfd[d];
494
- var lin = revfl[l] & 31, din = revfd[d] & 31;
495
- eb += fleb[lin] + fdeb[din];
496
- ++lf[257 + lin];
497
- ++df[din];
498
- wi = i + l;
499
- ++lc_1;
500
- }
501
- else {
502
- syms[li++] = dat[i];
503
- ++lf[dat[i]];
504
- }
505
- }
506
- }
507
- for (i = Math.max(i, wi); i < s; ++i) {
508
- syms[li++] = dat[i];
509
- ++lf[dat[i]];
510
- }
511
- pos = wblk(dat, w, lst, syms, lf, df, eb, li, bs, i - bs, pos);
512
- if (!lst) {
513
- st.r = (pos & 7) | w[(pos / 8) | 0] << 3;
514
- // shft(pos) now 1 less if pos & 7 != 0
515
- pos -= 7;
516
- st.h = head, st.p = prev, st.i = i, st.w = wi;
517
- }
518
- }
519
- else {
520
- for (var i = st.w || 0; i < s + lst; i += 65535) {
521
- // end
522
- var e = i + 65535;
523
- if (e >= s) {
524
- // write final block
525
- w[(pos / 8) | 0] = lst;
526
- e = s;
527
- }
528
- pos = wfblk(w, pos + 1, dat.subarray(i, e));
529
- }
530
- st.i = s;
531
- }
532
- return slc(o, 0, pre + shft(pos) + post);
533
- };
534
- // CRC32 table
535
- var crct = /*#__PURE__*/ (function () {
536
- var t = new Int32Array(256);
537
- for (var i = 0; i < 256; ++i) {
538
- var c = i, k = 9;
539
- while (--k)
540
- c = ((c & 1) && -306674912) ^ (c >>> 1);
541
- t[i] = c;
542
- }
543
- return t;
544
- })();
545
- // CRC32
546
- var crc = function () {
547
- var c = -1;
548
- return {
549
- p: function (d) {
550
- // closures have awful performance
551
- var cr = c;
552
- for (var i = 0; i < d.length; ++i)
553
- cr = crct[(cr & 255) ^ d[i]] ^ (cr >>> 8);
554
- c = cr;
555
- },
556
- d: function () { return ~c; }
557
- };
558
- };
559
- // deflate with opts
560
- var dopt = function (dat, opt, pre, post, st) {
561
- if (!st) {
562
- st = { l: 1 };
563
- if (opt.dictionary) {
564
- var dict = opt.dictionary.subarray(-32768);
565
- var newDat = new u8(dict.length + dat.length);
566
- newDat.set(dict);
567
- newDat.set(dat, dict.length);
568
- dat = newDat;
569
- st.w = dict.length;
570
- }
571
- }
572
- return dflt(dat, opt.level == null ? 6 : opt.level, opt.mem == null ? (st.l ? Math.ceil(Math.max(8, Math.min(13, Math.log(dat.length))) * 1.5) : 20) : (12 + opt.mem), pre, post, st);
573
- };
574
- // Walmart object spread
575
- var mrg = function (a, b) {
576
- var o = {};
577
- for (var k in a)
578
- o[k] = a[k];
579
- for (var k in b)
580
- o[k] = b[k];
581
- return o;
582
- };
583
- // worker clone
584
- // This is possibly the craziest part of the entire codebase, despite how simple it may seem.
585
- // The only parameter to this function is a closure that returns an array of variables outside of the function scope.
586
- // We're going to try to figure out the variable names used in the closure as strings because that is crucial for workerization.
587
- // We will return an object mapping of true variable name to value (basically, the current scope as a JS object).
588
- // The reason we can't just use the original variable names is minifiers mangling the toplevel scope.
589
- // This took me three weeks to figure out how to do.
590
- var wcln = function (fn, fnStr, td) {
591
- var dt = fn();
592
- var st = fn.toString();
593
- var ks = st.slice(st.indexOf('[') + 1, st.lastIndexOf(']')).replace(/\s+/g, '').split(',');
594
- for (var i = 0; i < dt.length; ++i) {
595
- var v = dt[i], k = ks[i];
596
- if (typeof v == 'function') {
597
- fnStr += ';' + k + '=';
598
- var st_1 = v.toString();
599
- if (v.prototype) {
600
- // for global objects
601
- if (st_1.indexOf('[native code]') != -1) {
602
- var spInd = st_1.indexOf(' ', 8) + 1;
603
- fnStr += st_1.slice(spInd, st_1.indexOf('(', spInd));
604
- }
605
- else {
606
- fnStr += st_1;
607
- for (var t in v.prototype)
608
- fnStr += ';' + k + '.prototype.' + t + '=' + v.prototype[t].toString();
609
- }
610
- }
611
- else
612
- fnStr += st_1;
613
- }
614
- else
615
- td[k] = v;
616
- }
617
- return fnStr;
618
- };
619
- var ch = [];
620
- // clone bufs
621
- var cbfs = function (v) {
622
- var tl = [];
623
- for (var k in v) {
624
- if (v[k].buffer) {
625
- tl.push((v[k] = new v[k].constructor(v[k])).buffer);
626
- }
627
- }
628
- return tl;
629
- };
630
- // use a worker to execute code
631
- var wrkr = function (fns, init, id, cb) {
632
- if (!ch[id]) {
633
- var fnStr = '', td_1 = {}, m = fns.length - 1;
634
- for (var i = 0; i < m; ++i)
635
- fnStr = wcln(fns[i], fnStr, td_1);
636
- ch[id] = { c: wcln(fns[m], fnStr, td_1), e: td_1 };
637
- }
638
- var td = mrg({}, ch[id].e);
639
- return wk(ch[id].c + ';onmessage=function(e){for(var k in e.data)self[k]=e.data[k];onmessage=' + init.toString() + '}', id, td, cbfs(td), cb);
640
- };
641
- var bDflt = function () { return [u8, u16, i32, fleb, fdeb, clim, revfl, revfd, flm, flt, fdm, fdt, rev, deo, et, hMap, wbits, wbits16, hTree, ln, lc, clen, wfblk, wblk, shft, slc, dflt, dopt, deflateSync, pbf]; };
642
- // gzip extra
643
- var gze = function () { return [gzh, gzhl, wbytes, crc, crct]; };
644
- // post buf
645
- var pbf = function (msg) { return postMessage(msg, [msg.buffer]); };
646
- // async helper
647
- var cbify = function (dat, opts, fns, init, id, cb) {
648
- var w = wrkr(fns, init, id, function (err, dat) {
649
- w.terminate();
650
- cb(err, dat);
651
- });
652
- w.postMessage([dat, opts], opts.consume ? [dat.buffer] : []);
653
- return function () { w.terminate(); };
654
- };
655
- // write bytes
656
- var wbytes = function (d, b, v) {
657
- for (; v; ++b)
658
- d[b] = v, v >>>= 8;
659
- };
660
- // gzip header
661
- var gzh = function (c, o) {
662
- var fn = o.filename;
663
- c[0] = 31, c[1] = 139, c[2] = 8, c[8] = o.level < 2 ? 4 : o.level == 9 ? 2 : 0, c[9] = 3; // assume Unix
664
- if (o.mtime != 0)
665
- wbytes(c, 4, Math.floor(new Date(o.mtime || Date.now()) / 1000));
666
- if (fn) {
667
- c[3] = 8;
668
- for (var i = 0; i <= fn.length; ++i)
669
- c[i + 10] = fn.charCodeAt(i);
670
- }
671
- };
672
- // gzip header length
673
- var gzhl = function (o) { return 10 + (o.filename ? o.filename.length + 1 : 0); };
674
- /**
675
- * Compresses data with DEFLATE without any wrapper
676
- * @param data The data to compress
677
- * @param opts The compression options
678
- * @returns The deflated version of the data
679
- */
680
- function deflateSync(data, opts) {
681
- return dopt(data, opts || {}, 0, 0);
682
- }
683
- function gzip(data, opts, cb) {
684
- if (!cb)
685
- cb = opts, opts = {};
686
- if (typeof cb != 'function')
687
- err(7);
688
- return cbify(data, opts, [
689
- bDflt,
690
- gze,
691
- function () { return [gzipSync]; }
692
- ], function (ev) { return pbf(gzipSync(ev.data[0], ev.data[1])); }, 2, cb);
693
- }
694
- /**
695
- * Compresses data with GZIP
696
- * @param data The data to compress
697
- * @param opts The compression options
698
- * @returns The gzipped version of the data
699
- */
700
- function gzipSync(data, opts) {
701
- if (!opts)
702
- opts = {};
703
- var c = crc(), l = data.length;
704
- c.p(data);
705
- var d = dopt(data, opts, gzhl(opts), 8), s = d.length;
706
- return gzh(d, opts), wbytes(d, s - 8, c.d()), wbytes(d, s - 4, l), d;
707
- }
708
- // text decoder
709
- var td = typeof TextDecoder != 'undefined' && /*#__PURE__*/ new TextDecoder();
710
- // text decoder stream
711
- var tds = 0;
712
- try {
713
- td.decode(et, { stream: true });
714
- tds = 1;
715
- }
716
- catch (e) { }
717
-
718
5
  class StringDictionary {
719
6
  constructor() {
720
7
  this.lastTs = 0;
@@ -1114,93 +401,6 @@ const mapCondition = (condition) => {
1114
401
  return con;
1115
402
  };
1116
403
 
1117
- class FeatureFlags {
1118
- constructor(app) {
1119
- this.app = app;
1120
- this.flags = [];
1121
- this.storageKey = '__openreplay_flags';
1122
- const persistFlags = this.app.sessionStorage.getItem(this.storageKey);
1123
- if (persistFlags) {
1124
- const persistFlagsStrArr = persistFlags.split(';').filter(Boolean);
1125
- this.flags = persistFlagsStrArr.map((flag) => JSON.parse(flag));
1126
- }
1127
- }
1128
- getFeatureFlag(flagName) {
1129
- return this.flags.find((flag) => flag.key === flagName);
1130
- }
1131
- isFlagEnabled(flagName) {
1132
- return this.flags.findIndex((flag) => flag.key === flagName) !== -1;
1133
- }
1134
- onFlagsLoad(cb) {
1135
- this.onFlagsCb = cb;
1136
- }
1137
- async reloadFlags(token) {
1138
- const persistFlagsStr = this.app.sessionStorage.getItem(this.storageKey);
1139
- const persistFlags = {};
1140
- if (persistFlagsStr) {
1141
- const persistArray = persistFlagsStr.split(';').filter(Boolean);
1142
- persistArray.forEach((flag) => {
1143
- const flagObj = JSON.parse(flag);
1144
- persistFlags[flagObj.key] = { key: flagObj.key, value: flagObj.value };
1145
- });
1146
- }
1147
- const sessionInfo = this.app.session.getInfo();
1148
- const userInfo = this.app.session.userInfo;
1149
- const requestObject = {
1150
- projectID: sessionInfo.projectID,
1151
- userID: sessionInfo.userID,
1152
- metadata: sessionInfo.metadata,
1153
- referrer: document.referrer,
1154
- os: userInfo.userOS,
1155
- device: userInfo.userDevice,
1156
- country: userInfo.userCountry,
1157
- state: userInfo.userState,
1158
- city: userInfo.userCity,
1159
- browser: userInfo.userBrowser,
1160
- persistFlags: persistFlags,
1161
- };
1162
- const authToken = token ?? this.app.session.getSessionToken();
1163
- const resp = await fetch(this.app.options.ingestPoint + '/v1/web/feature-flags', {
1164
- method: 'POST',
1165
- headers: {
1166
- 'Content-Type': 'application/json',
1167
- Authorization: `Bearer ${authToken}`,
1168
- },
1169
- body: JSON.stringify(requestObject),
1170
- });
1171
- if (resp.status === 200) {
1172
- const data = await resp.json();
1173
- return this.handleFlags(data.flags);
1174
- }
1175
- }
1176
- handleFlags(flags) {
1177
- const persistFlags = [];
1178
- flags.forEach((flag) => {
1179
- if (flag.is_persist)
1180
- persistFlags.push(flag);
1181
- });
1182
- let str = '';
1183
- const uniquePersistFlags = this.diffPersist(persistFlags);
1184
- uniquePersistFlags.forEach((flag) => {
1185
- str += `${JSON.stringify(flag)};`;
1186
- });
1187
- this.app.sessionStorage.setItem(this.storageKey, str);
1188
- this.flags = flags;
1189
- return this.onFlagsCb?.(flags);
1190
- }
1191
- clearPersistFlags() {
1192
- this.app.sessionStorage.removeItem(this.storageKey);
1193
- }
1194
- diffPersist(flags) {
1195
- const persistFlags = this.app.sessionStorage.getItem(this.storageKey);
1196
- if (!persistFlags)
1197
- return flags;
1198
- const persistFlagsStrArr = persistFlags.split(';').filter(Boolean);
1199
- const persistFlagsArr = persistFlagsStrArr.map((flag) => JSON.parse(flag));
1200
- return flags.filter((flag) => persistFlagsArr.findIndex((pf) => pf.key === flag.key) === -1);
1201
- }
1202
- }
1203
-
1204
404
  const DEPRECATED_ATTRS = { htmlmasked: 'hidden', masked: 'obscured' };
1205
405
  const IN_BROWSER = !(typeof window === 'undefined');
1206
406
  const IS_FIREFOX = IN_BROWSER && navigator.userAgent.match(/firefox|fxios/i);
@@ -1222,7 +422,7 @@ const stars = 'repeat' in String.prototype
1222
422
  ? (str) => '*'.repeat(str.length)
1223
423
  : (str) => str.replace(/./g, '*');
1224
424
  function normSpaces(str) {
1225
- return str.trim().replace(/\s+/g, ' ');
425
+ return str ? str.trim().replace(/\s+/g, ' ') : '';
1226
426
  }
1227
427
  // isAbsoluteUrl regexp: /^([a-z][a-z\d\+\-\.]*:)?\/\//i.test(url)
1228
428
  function isURL(s) {
@@ -1238,6 +438,23 @@ function deprecationWarn(nameOfFeature, useInstead, docsPath = '/') {
1238
438
  console.warn(`OpenReplay: ${nameOfFeature} is deprecated. ${useInstead ? `Please, use ${useInstead} instead.` : ''} Visit ${DOCS_HOST}${docsPath} for more information.`);
1239
439
  warnedFeatures[nameOfFeature] = true;
1240
440
  }
441
+ function getCustomAttributeLabel(e, customAttributes) {
442
+ if (!customAttributes || customAttributes.length === 0)
443
+ return '';
444
+ const parts = [];
445
+ for (const attr of customAttributes) {
446
+ const value = e.getAttribute(attr);
447
+ if (value !== null) {
448
+ parts.push(`[${attr}=${value}]`);
449
+ }
450
+ }
451
+ return parts.join('');
452
+ }
453
+ function getClassSelector(e) {
454
+ if (!e.classList || e.classList.length === 0)
455
+ return '';
456
+ return '.' + Array.from(e.classList).join('.');
457
+ }
1241
458
  function getLabelAttribute(e) {
1242
459
  let value = e.getAttribute('data-openreplay-label');
1243
460
  if (value !== null) {
@@ -1723,33 +940,6 @@ function NodeAnimationResult(id, styles) {
1723
940
  styles,
1724
941
  ];
1725
942
  }
1726
- function CSSInsertRule(id, rule, index) {
1727
- return [
1728
- 37 /* Messages.Type.CSSInsertRule */,
1729
- id,
1730
- rule,
1731
- index,
1732
- ];
1733
- }
1734
- function CSSDeleteRule(id, index) {
1735
- return [
1736
- 38 /* Messages.Type.CSSDeleteRule */,
1737
- id,
1738
- index,
1739
- ];
1740
- }
1741
- function Fetch(method, url, request, response, status, timestamp, duration) {
1742
- return [
1743
- 39 /* Messages.Type.Fetch */,
1744
- method,
1745
- url,
1746
- request,
1747
- response,
1748
- status,
1749
- timestamp,
1750
- duration,
1751
- ];
1752
- }
1753
943
  function Profiler(name, duration, args, result) {
1754
944
  return [
1755
945
  40 /* Messages.Type.Profiler */,
@@ -1892,18 +1082,6 @@ function SetNodeFocus(id) {
1892
1082
  id,
1893
1083
  ];
1894
1084
  }
1895
- function LongTask(timestamp, duration, context, containerType, containerSrc, containerId, containerName) {
1896
- return [
1897
- 59 /* Messages.Type.LongTask */,
1898
- timestamp,
1899
- duration,
1900
- context,
1901
- containerType,
1902
- containerSrc,
1903
- containerId,
1904
- containerName,
1905
- ];
1906
- }
1907
1085
  function SetNodeAttributeURLBased(id, name, value, baseURL) {
1908
1086
  return [
1909
1087
  60 /* Messages.Type.SetNodeAttributeURLBased */,
@@ -1942,15 +1120,6 @@ function SetNodeSlot(id, slotID) {
1942
1120
  slotID,
1943
1121
  ];
1944
1122
  }
1945
- function CSSInsertRuleURLBased(id, rule, index, baseURL) {
1946
- return [
1947
- 67 /* Messages.Type.CSSInsertRuleURLBased */,
1948
- id,
1949
- rule,
1950
- index,
1951
- baseURL,
1952
- ];
1953
- }
1954
1123
  function MouseClick(id, hesitationTime, label, selector, normalizedX, normalizedY) {
1955
1124
  return [
1956
1125
  68 /* Messages.Type.MouseClick */,
@@ -2230,9 +1399,6 @@ var _Messages = /*#__PURE__*/Object.freeze({
2230
1399
  AdoptedSSRemoveOwner: AdoptedSSRemoveOwner,
2231
1400
  AdoptedSSReplaceURLBased: AdoptedSSReplaceURLBased,
2232
1401
  BatchMetadata: BatchMetadata,
2233
- CSSDeleteRule: CSSDeleteRule,
2234
- CSSInsertRule: CSSInsertRule,
2235
- CSSInsertRuleURLBased: CSSInsertRuleURLBased,
2236
1402
  CanvasNode: CanvasNode,
2237
1403
  ConnectionInformation: ConnectionInformation,
2238
1404
  ConsoleLog: ConsoleLog,
@@ -2242,7 +1408,6 @@ var _Messages = /*#__PURE__*/Object.freeze({
2242
1408
  CreateTextNode: CreateTextNode,
2243
1409
  CustomEvent: CustomEvent,
2244
1410
  CustomIssue: CustomIssue,
2245
- Fetch: Fetch,
2246
1411
  GraphQL: GraphQL,
2247
1412
  GraphQLDeprecated: GraphQLDeprecated,
2248
1413
  Incident: Incident,
@@ -2250,7 +1415,6 @@ var _Messages = /*#__PURE__*/Object.freeze({
2250
1415
  JSException: JSException,
2251
1416
  LoadFontFace: LoadFontFace,
2252
1417
  LongAnimationTask: LongAnimationTask$1,
2253
- LongTask: LongTask,
2254
1418
  Metadata: Metadata,
2255
1419
  MobX: MobX,
2256
1420
  MouseClick: MouseClick,
@@ -2437,1060 +1601,85 @@ class TagWatcher {
2437
1601
  }
2438
1602
  }
2439
1603
 
2440
- const bgStyle = {
2441
- position: 'fixed',
2442
- top: 0,
2443
- left: 0,
2444
- width: '100vw',
2445
- height: '100vh',
2446
- background: 'rgba(0, 0, 0, 0.40)',
2447
- display: 'flex',
2448
- alignItems: 'center',
2449
- justifyContent: 'center',
2450
- zIndex: 999999,
2451
- fontFamily: `-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"`,
2452
- };
2453
- const containerStyle = {
2454
- display: 'flex',
2455
- flexDirection: 'column',
2456
- gap: '2rem',
2457
- alignItems: 'center',
2458
- padding: '1.5rem',
2459
- borderRadius: '2px',
2460
- border: '1px solid rgb(255 255 255 / var(--tw-bg-opacity, 1))',
2461
- background: '#FFF',
2462
- width: '22rem',
2463
- };
2464
- const containerWidgetStyle = {
2465
- display: 'flex',
2466
- 'flex-direction': 'column',
2467
- gap: 'unset',
2468
- 'align-items': 'center',
2469
- padding: 'unset',
2470
- fontFamily: `-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"`,
2471
- 'border-radius': '2px',
2472
- border: '1px solid rgb(255 255 255 / var(--tw-bg-opacity, 1))',
2473
- background: 'rgba(255, 255, 255, 0.75)',
2474
- width: '22rem',
2475
- };
2476
- const titleStyle = {
2477
- fontFamily: 'Verdana, sans-serif',
2478
- fontSize: '1.25rem',
2479
- fontStyle: 'normal',
2480
- fontWeight: '500',
2481
- lineHeight: '1.75rem',
2482
- color: 'rgba(0, 0, 0, 0.85)',
2483
- };
2484
- const descriptionStyle = {
2485
- borderTop: '1px solid rgba(0, 0, 0, 0.06)',
2486
- borderBottom: '1px solid rgba(0, 0, 0, 0.06)',
2487
- padding: '1.25rem 0rem',
2488
- color: 'rgba(0, 0, 0, 0.85)',
2489
- fontFamily: 'Verdana, sans-serif',
2490
- fontSize: '13px',
2491
- fontStyle: 'normal',
2492
- fontWeight: '400',
2493
- lineHeight: 'auto',
2494
- whiteSpace: 'pre-wrap',
2495
- };
2496
- const buttonStyle = {
2497
- display: 'flex',
2498
- padding: '0.4rem 0.9375rem',
2499
- justifyContent: 'center',
2500
- alignItems: 'center',
2501
- gap: '0.625rem',
2502
- borderRadius: '0.25rem',
2503
- border: '1px solid #394EFF',
2504
- background: '#394EFF',
2505
- boxShadow: '0px 2px 0px 0px rgba(0, 0, 0, 0.04)',
2506
- color: '#FFF',
2507
- textAlign: 'center',
2508
- fontFamily: 'Verdana, sans-serif',
2509
- fontSize: '1rem',
2510
- fontStyle: 'normal',
2511
- fontWeight: '500',
2512
- lineHeight: '1.5rem',
2513
- cursor: 'pointer',
2514
- };
2515
- const sectionTitleStyle = {
2516
- fontFamily: 'Verdana, sans-serif',
2517
- fontSize: '13px',
2518
- fontWeight: '500',
2519
- lineHeight: 'auto',
2520
- display: 'flex',
2521
- justifyContent: 'space-between',
2522
- width: '100%',
2523
- cursor: 'pointer',
2524
- };
2525
- const contentStyle = {
2526
- display: 'flex',
2527
- flexDirection: 'column',
2528
- alignItems: 'flex-start',
2529
- gap: '0.625rem',
2530
- fontSize: '13px',
2531
- lineHeight: 'auto',
2532
- };
2533
- // New widget styles
2534
- const titleWidgetStyle = {
2535
- padding: '0.5rem',
2536
- gap: '0.5rem',
2537
- fontFamily: 'Verdana, sans-serif',
2538
- fontSize: '16px',
2539
- fontStyle: 'normal',
2540
- fontWeight: '500',
2541
- lineHeight: 'auto',
2542
- color: 'white',
2543
- display: 'flex',
2544
- alignItems: 'center',
2545
- width: '100%',
2546
- borderRadius: '2px',
2547
- background: 'rgba(0, 0, 0, 0.75)',
2548
- boxSizing: 'border-box',
2549
- };
2550
- const descriptionWidgetStyle = {
2551
- boxSizing: 'border-box',
2552
- display: 'block',
2553
- width: '100%',
2554
- borderBottom: '1px solid rgb(255 255 255 / var(--tw-bg-opacity, 1))',
2555
- background: '#FFF',
2556
- padding: '0.65rem',
2557
- alignSelf: 'stretch',
2558
- color: '#000',
2559
- fontFamily: 'Verdana, sans-serif',
2560
- // fontSize: '0.875rem',
2561
- fontStyle: 'normal',
2562
- fontWeight: '400',
2563
- // lineHeight: '1.375rem',
2564
- };
2565
- const endSectionStyle = {
2566
- ...descriptionWidgetStyle,
2567
- display: 'flex',
2568
- flexDirection: 'column',
2569
- alignItems: 'center',
2570
- gap: '0.625rem',
2571
- };
2572
- const symbolIcon = {
2573
- fontSize: '1.25rem',
2574
- fontWeight: '500',
2575
- cursor: 'pointer',
2576
- color: '#394EFF',
2577
- };
2578
- const buttonWidgetStyle = {
2579
- display: 'flex',
2580
- padding: '0.4rem 0.9375rem',
2581
- justifyContent: 'center',
2582
- alignItems: 'center',
2583
- gap: '0.625rem',
2584
- borderRadius: '0.25rem',
2585
- border: '1px solid #394EFF',
2586
- background: '#394EFF',
2587
- boxShadow: '0px 2px 0px 0px rgba(0, 0, 0, 0.04)',
2588
- color: '#FFF',
2589
- textAlign: 'center',
2590
- fontFamily: 'Verdana, sans-serif',
2591
- fontSize: '1rem',
2592
- fontStyle: 'normal',
2593
- fontWeight: '500',
2594
- lineHeight: '1.5rem',
2595
- width: '100%',
2596
- boxSizing: 'border-box',
2597
- cursor: 'pointer',
2598
- };
2599
- const stopWidgetStyle = {
2600
- marginTop: '1rem',
2601
- marginBottom: '1rem',
2602
- cursor: 'pointer',
2603
- display: 'block',
2604
- fontWeight: '500',
2605
- fontSize: '13px!important',
2606
- lineHeight: 'auto',
2607
- };
2608
- const paginationStyle = {
2609
- display: 'flex',
2610
- justifyContent: 'space-between',
2611
- alignItems: 'center',
2612
- gap: '1rem',
2613
- padding: '0.5rem',
2614
- width: '100%',
2615
- boxSizing: 'border-box',
2616
- };
2617
- const taskNumberActive = {
2618
- display: 'flex',
2619
- flexDirection: 'column',
2620
- alignItems: 'center',
2621
- justifyContent: 'center',
2622
- borderRadius: '6.25em',
2623
- outline: '1px solid #394EFF',
2624
- fontSize: '13px',
2625
- height: '24px',
2626
- width: '24px',
2627
- };
2628
- const taskNumberDone = {
2629
- display: 'flex',
2630
- flexDirection: 'column',
2631
- alignItems: 'center',
2632
- justifyContent: 'center',
2633
- borderRadius: '6.25em',
2634
- outline: '1px solid #D2DFFF',
2635
- boxShadow: '0px 2px 0px 0px rgba(0, 0, 0, 0.04)',
2636
- background: '#D2DFFF',
2637
- fontSize: '13px',
2638
- height: '24px',
2639
- width: '24px',
2640
- };
2641
- const taskDescriptionCard = {
2642
- borderRadius: '0.375rem',
2643
- border: '1px solid rgba(0, 0, 0, 0.06)',
2644
- background: '#F5F7FF',
2645
- boxShadow: '0px 2px 0px 0px rgba(0, 0, 0, 0.04)',
2646
- display: 'flex',
2647
- flexDirection: 'column',
2648
- padding: '0.625rem 0.9375rem',
2649
- gap: '0.5rem',
2650
- alignSelf: 'stretch',
2651
- };
2652
- const taskTextStyle = {
2653
- fontWeight: 'bold',
2654
- };
2655
- const taskDescriptionStyle = {
2656
- fontSize: '13px',
2657
- lineHeight: 'auto',
2658
- };
2659
- const taskButtonStyle = {
2660
- marginRight: '0.5rem',
2661
- cursor: 'pointer',
2662
- color: '#394EFF',
2663
- textAlign: 'center',
2664
- fontFamily: 'Verdana, sans-serif',
2665
- fontSize: '13px',
2666
- fontStyle: 'normal',
2667
- fontWeight: '500',
2668
- lineHeight: 'auto',
2669
- };
2670
- const taskButtonBorderedStyle = {
2671
- ...taskButtonStyle,
2672
- display: 'flex',
2673
- padding: '0.25rem 0.9375rem',
2674
- justifyContent: 'center',
2675
- alignItems: 'center',
2676
- gap: '0.5rem',
2677
- borderRadius: '0.25rem',
2678
- border: '1px solid #394EFF',
2679
- };
2680
- const taskButtonsRow = {
2681
- display: 'flex',
2682
- justifyContent: 'space-between',
2683
- alignItems: 'center',
2684
- width: '100%',
2685
- boxSizing: 'border-box',
2686
- };
2687
- const spinnerStyles = {
2688
- border: '4px solid rgba(255, 255, 255, 0.4)',
2689
- width: '16px',
2690
- height: '16px',
2691
- borderRadius: '50%',
2692
- borderLeftColor: '#fff',
2693
- animation: 'spin 0.5s linear infinite',
2694
- };
1604
+ //@ts-ignore
1605
+ function isNode(sth) {
1606
+ return !!sth && sth.nodeType != null;
1607
+ }
1608
+ function isSVGElement(node) {
1609
+ return (node.namespaceURI === 'http://www.w3.org/2000/svg' || node.localName === 'svg');
1610
+ }
1611
+ function isUseElement(node) {
1612
+ return node.localName === 'use';
1613
+ }
1614
+ function isElementNode(node) {
1615
+ return node.nodeType === Node.ELEMENT_NODE;
1616
+ }
1617
+ function isCommentNode(node) {
1618
+ return node.nodeType === Node.COMMENT_NODE;
1619
+ }
1620
+ function isTextNode(node) {
1621
+ return node.nodeType === Node.TEXT_NODE;
1622
+ }
1623
+ function isDocument(node) {
1624
+ return node.nodeType === Node.DOCUMENT_NODE;
1625
+ }
1626
+ function isRootNode(node) {
1627
+ return node.nodeType === Node.DOCUMENT_NODE || node.nodeType === Node.DOCUMENT_FRAGMENT_NODE;
1628
+ }
1629
+ function hasTag(el, tagName) {
1630
+ // @ts-ignore
1631
+ return el.localName === tagName;
1632
+ }
2695
1633
 
2696
- const Quality = {
2697
- Standard: { width: 1280, height: 720 }};
2698
- class Recorder {
2699
- constructor(app) {
1634
+ class CanvasRecorder {
1635
+ constructor(app, options) {
2700
1636
  this.app = app;
2701
- this.mediaRecorder = null;
2702
- this.recordedChunks = [];
2703
- this.stream = null;
2704
- this.recStartTs = null;
2705
- }
2706
- async startRecording(fps, quality, micReq, camReq) {
2707
- this.recStartTs = this.app.timestamp();
2708
- const videoConstraints = quality;
2709
- try {
2710
- this.stream = await navigator.mediaDevices.getUserMedia({
2711
- video: camReq ? { ...videoConstraints, frameRate: { ideal: fps } } : false,
2712
- audio: micReq,
2713
- });
2714
- this.mediaRecorder = new MediaRecorder(this.stream, {
2715
- mimeType: 'video/webm;codecs=vp9',
2716
- });
2717
- this.recordedChunks = [];
2718
- this.mediaRecorder.ondataavailable = (event) => {
2719
- if (event.data.size > 0) {
2720
- this.recordedChunks.push(event.data);
2721
- }
2722
- };
2723
- this.mediaRecorder.start();
2724
- }
2725
- catch (error) {
2726
- console.error(error);
2727
- }
2728
- }
2729
- async stopRecording() {
2730
- return new Promise((resolve) => {
2731
- if (!this.mediaRecorder)
1637
+ this.options = options;
1638
+ this.snapshots = {};
1639
+ this.intervals = new Map();
1640
+ this.observers = new Map();
1641
+ this.uploadQueue = 0;
1642
+ this.MAX_CONCURRENT_UPLOADS = 2;
1643
+ this.MAX_QUEUE_SIZE = 50; // ~500 images max (50 batches × 10 images)
1644
+ this.pendingBatches = [];
1645
+ this.isProcessingQueue = false;
1646
+ this.restartTracking = () => {
1647
+ this.clear();
1648
+ this.app.nodes.scanTree(this.captureCanvas);
1649
+ };
1650
+ this.captureCanvas = (node) => {
1651
+ const id = this.app.nodes.getID(node);
1652
+ if (!id || !hasTag(node, 'canvas')) {
2732
1653
  return;
2733
- this.mediaRecorder.onstop = () => {
2734
- const blob = new Blob(this.recordedChunks, {
2735
- type: 'video/webm',
2736
- });
2737
- resolve(blob);
2738
- };
2739
- this.mediaRecorder.stop();
2740
- });
2741
- }
2742
- async sendToAPI() {
2743
- const blob = await this.stopRecording();
2744
- // const formData = new FormData()
2745
- // formData.append('file', blob, 'record.webm')
2746
- // formData.append('start', this.recStartTs?.toString() ?? '')
2747
- return fetch(`${this.app.options.ingestPoint}/v1/web/uxt/upload-url`, {
2748
- headers: {
2749
- Authorization: `Bearer ${this.app.session.getSessionToken()}`,
2750
- },
2751
- })
2752
- .then((r) => {
2753
- if (r.ok) {
2754
- return r.json();
2755
1654
  }
2756
- else {
2757
- throw new Error('Failed to get upload url');
1655
+ const isIgnored = this.app.sanitizer.isObscured(id) || this.app.sanitizer.isHidden(id);
1656
+ if (isIgnored || this.snapshots[id]) {
1657
+ return;
2758
1658
  }
2759
- })
2760
- .then(({ url }) => {
2761
- return fetch(url, {
2762
- method: 'PUT',
2763
- headers: {
2764
- 'Content-Type': 'video/webm',
2765
- },
2766
- body: blob,
2767
- });
2768
- })
2769
- .catch(console.error)
2770
- .finally(() => {
2771
- this.discard();
2772
- });
2773
- }
2774
- async saveToFile(fileName = 'recorded-video.webm') {
2775
- const blob = await this.stopRecording();
2776
- const url = URL.createObjectURL(blob);
2777
- const a = document.createElement('a');
2778
- a.style.display = 'none';
2779
- a.href = url;
2780
- a.download = fileName;
2781
- document.body.appendChild(a);
2782
- a.click();
2783
- window.URL.revokeObjectURL(url);
2784
- document.body.removeChild(a);
2785
- }
2786
- discard() {
2787
- this.mediaRecorder?.stop();
2788
- this.stream?.getTracks().forEach((track) => track.stop());
2789
- }
2790
- }
2791
-
2792
- // @ts-nocheck
2793
- function attachDND(element, dragTarget) {
2794
- dragTarget.onmousedown = function (event) {
2795
- const clientRect = element.getBoundingClientRect();
2796
- const shiftX = event.clientX - clientRect.left;
2797
- const shiftY = event.clientY - clientRect.top;
2798
- element.style.position = 'fixed';
2799
- element.style.zIndex = 99999999999999;
2800
- moveAt(event.pageX, event.pageY);
2801
- function moveAt(pageX, pageY) {
2802
- let leftC = pageX - shiftX;
2803
- let topC = pageY - shiftY;
2804
- if (leftC <= 5)
2805
- leftC = 5;
2806
- if (topC <= 5)
2807
- topC = 5;
2808
- if (leftC >= window.innerWidth - clientRect.width)
2809
- leftC = window.innerWidth - clientRect.width;
2810
- if (topC >= window.innerHeight - clientRect.height)
2811
- topC = window.innerHeight - clientRect.height;
2812
- element.style.left = `${leftC}px`;
2813
- element.style.top = `${topC}px`;
2814
- }
2815
- function onMouseMove(event) {
2816
- moveAt(event.pageX, event.pageY);
2817
- }
2818
- document.addEventListener('mousemove', onMouseMove);
2819
- const clearAll = () => {
2820
- document.removeEventListener('mousemove', onMouseMove);
2821
- document.removeEventListener('mouseup', clearAll);
2822
- };
2823
- document.addEventListener('mouseup', clearAll);
2824
- };
2825
- dragTarget.ondragstart = function () {
2826
- return false;
2827
- };
2828
- }
2829
-
2830
- function generateGrid() {
2831
- const grid = document.createElement('div');
2832
- grid.className = 'grid';
2833
- for (let i = 0; i < 16; i++) {
2834
- const cell = document.createElement('div');
2835
- Object.assign(cell.style, {
2836
- width: '2px',
2837
- height: '2px',
2838
- borderRadius: '10px',
2839
- background: 'white',
2840
- });
2841
- cell.className = 'cell';
2842
- grid.appendChild(cell);
2843
- }
2844
- Object.assign(grid.style, {
2845
- display: 'grid',
2846
- gridTemplateColumns: 'repeat(4, 1fr)',
2847
- gridTemplateRows: 'repeat(4, 1fr)',
2848
- gap: '2px',
2849
- cursor: 'grab',
2850
- });
2851
- return grid;
2852
- }
2853
- function generateChevron() {
2854
- const triangle = document.createElement('div');
2855
- Object.assign(triangle.style, {
2856
- width: '0',
2857
- height: '0',
2858
- borderLeft: '7px solid transparent',
2859
- borderRight: '7px solid transparent',
2860
- borderBottom: '7px solid white',
2861
- });
2862
- const container = document.createElement('div');
2863
- container.appendChild(triangle);
2864
- Object.assign(container.style, {
2865
- display: 'flex',
2866
- alignItems: 'center',
2867
- justifyContent: 'center',
2868
- width: '16px',
2869
- height: '16px',
2870
- cursor: 'pointer',
2871
- marginLeft: 'auto',
2872
- transform: 'rotate(180deg)',
2873
- });
2874
- return container;
2875
- }
2876
- function addKeyframes() {
2877
- const styleSheet = document.createElement('style');
2878
- styleSheet.type = 'text/css';
2879
- styleSheet.innerText = `@keyframes spin {
2880
- 0% { transform: rotate(0deg); }
2881
- 100% { transform: rotate(360deg); }
2882
- }`;
2883
- document.head.appendChild(styleSheet);
2884
- }
2885
- function createSpinner() {
2886
- addKeyframes();
2887
- const spinner = document.createElement('div');
2888
- spinner.classList.add('spinner');
2889
- Object.assign(spinner.style, spinnerStyles);
2890
- return spinner;
2891
- }
2892
- function createElement(tag, className, styles, textContent, id) {
2893
- const element = document.createElement(tag);
2894
- element.className = className;
2895
- Object.assign(element.style, styles);
2896
- if (textContent) {
2897
- element.textContent = textContent;
2898
- }
2899
- if (id) {
2900
- element.id = id;
2901
- }
2902
- return element;
2903
- }
2904
- const TEST_START = 'or_uxt_test_start';
2905
- const TASK_IND = 'or_uxt_task_index';
2906
- const SESSION_ID = 'or_uxt_session_id';
2907
- const TEST_ID = 'or_uxt_test_id';
2908
-
2909
- class SignalManager {
2910
- constructor(ingestPoint, getTimestamp, token, testId, storageKey, setStorageKey, removeStorageKey, getStorageKey, getSessionId) {
2911
- this.ingestPoint = ingestPoint;
2912
- this.getTimestamp = getTimestamp;
2913
- this.token = token;
2914
- this.testId = testId;
2915
- this.storageKey = storageKey;
2916
- this.setStorageKey = setStorageKey;
2917
- this.removeStorageKey = removeStorageKey;
2918
- this.getStorageKey = getStorageKey;
2919
- this.getSessionId = getSessionId;
2920
- this.durations = {
2921
- testStart: 0,
2922
- tasks: [],
2923
- };
2924
- this.getDurations = () => {
2925
- return this.durations;
2926
- };
2927
- this.setDurations = (durations) => {
2928
- this.durations.testStart = durations.testStart;
2929
- this.durations.tasks = durations.tasks;
2930
- };
2931
- this.signalTask = (taskId, status, taskAnswer) => {
2932
- if (!taskId)
2933
- return console.error('User Testing: No Task ID Given');
2934
- const taskStart = this.durations.tasks.find((t) => t.taskId === taskId);
2935
- const timestamp = this.getTimestamp();
2936
- const duration = taskStart ? timestamp - taskStart.started : 0;
2937
- return fetch(`${this.ingestPoint}/v1/web/uxt/signals/task`, {
2938
- method: 'POST',
2939
- headers: {
2940
- // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
2941
- Authorization: `Bearer ${this.token}`,
2942
- },
2943
- body: JSON.stringify({
2944
- testId: this.testId,
2945
- taskId,
2946
- status,
2947
- duration,
2948
- timestamp,
2949
- taskAnswer,
2950
- }),
2951
- });
2952
- };
2953
- this.signalTest = (status) => {
2954
- const timestamp = this.getTimestamp();
2955
- if (status === 'begin' && this.testId) {
2956
- const sessionId = this.getSessionId();
2957
- this.setStorageKey(SESSION_ID, sessionId);
2958
- this.setStorageKey(this.storageKey, this.testId.toString());
2959
- this.setStorageKey(TEST_START, timestamp.toString());
2960
- }
2961
- else {
2962
- this.removeStorageKey(this.storageKey);
2963
- this.removeStorageKey(TASK_IND);
2964
- this.removeStorageKey(TEST_START);
2965
- }
2966
- const start = this.durations.testStart || timestamp;
2967
- const duration = timestamp - start;
2968
- return fetch(`${this.ingestPoint}/v1/web/uxt/signals/test`, {
2969
- method: 'POST',
2970
- headers: {
2971
- // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
2972
- Authorization: `Bearer ${this.token}`,
2973
- },
2974
- body: JSON.stringify({
2975
- testId: this.testId,
2976
- status,
2977
- duration,
2978
- timestamp,
2979
- }),
2980
- });
2981
- };
2982
- const possibleStart = this.getStorageKey(TEST_START);
2983
- if (possibleStart) {
2984
- this.durations.testStart = parseInt(possibleStart, 10);
2985
- }
2986
- }
2987
- }
2988
-
2989
- class UserTestManager {
2990
- constructor(app, storageKey) {
2991
- this.app = app;
2992
- this.storageKey = storageKey;
2993
- this.bg = createElement('div', 'bg', bgStyle, undefined, '__or_ut_bg');
2994
- this.container = createElement('div', 'container', containerStyle, undefined, '__or_ut_ct');
2995
- this.widgetGuidelinesVisible = true;
2996
- this.widgetTasksVisible = false;
2997
- this.widgetVisible = true;
2998
- this.isActive = false;
2999
- this.descriptionSection = null;
3000
- this.taskSection = null;
3001
- this.endSection = null;
3002
- this.stopButton = null;
3003
- this.stopButtonContainer = null;
3004
- this.test = null;
3005
- this.testId = null;
3006
- this.signalManager = null;
3007
- this.getTest = (id, token, inProgress) => {
3008
- this.testId = id;
3009
- const ingest = this.app.options.ingestPoint;
3010
- return fetch(`${ingest}/v1/web/uxt/test/${id}`, {
3011
- headers: {
3012
- Authorization: `Bearer ${token}`,
3013
- },
3014
- })
3015
- .then((res) => res.json())
3016
- .then(({ test }) => {
3017
- this.isActive = true;
3018
- this.test = test;
3019
- this.signalManager = new SignalManager(this.app.options.ingestPoint, () => this.app.timestamp(), token, id, this.storageKey, (k, v) => this.app.localStorage.setItem(k, v), (k) => this.app.localStorage.removeItem(k), (k) => this.app.localStorage.getItem(k), () => this.app.getSessionID());
3020
- this.createGreeting(test.title, test.reqMic, test.reqCamera);
3021
- if (inProgress) {
3022
- if (test.reqMic || test.reqCamera) {
3023
- void this.userRecorder.startRecording(30, Quality.Standard, test.reqMic, test.reqCamera);
3024
- }
3025
- this.showWidget(test.description, test.tasks, true);
3026
- this.showTaskSection();
3027
- }
3028
- })
3029
- .then(() => id)
3030
- .catch((err) => {
3031
- console.log('OR: Error fetching test', err);
3032
- });
3033
- };
3034
- this.hideTaskSection = () => false;
3035
- this.showTaskSection = () => true;
3036
- this.collapseWidget = () => false;
3037
- this.removeGreeting = () => false;
3038
- // eslint-disable-next-line @typescript-eslint/no-empty-function
3039
- this.toggleDescriptionVisibility = () => { };
3040
- this.currentTaskIndex = 0;
3041
- this.userRecorder = new Recorder(app);
3042
- const sessionId = this.app.getSessionID();
3043
- const savedSessionId = this.app.localStorage.getItem(SESSION_ID);
3044
- if (sessionId !== savedSessionId) {
3045
- this.app.localStorage.removeItem(this.storageKey);
3046
- this.app.localStorage.removeItem(SESSION_ID);
3047
- this.app.localStorage.removeItem(TEST_ID);
3048
- this.app.localStorage.removeItem(TASK_IND);
3049
- this.app.localStorage.removeItem(TEST_START);
3050
- }
3051
- const taskIndex = this.app.localStorage.getItem(TASK_IND);
3052
- if (taskIndex) {
3053
- this.currentTaskIndex = parseInt(taskIndex, 10);
3054
- }
3055
- }
3056
- getTestId() {
3057
- return this.testId;
3058
- }
3059
- createGreeting(title, micRequired, cameraRequired) {
3060
- const titleElement = createElement('div', 'title', titleStyle, title);
3061
- const descriptionElement = createElement('div', 'description', descriptionStyle, `Welcome, you're here to help us improve, not to be judged. Your insights matter!\n
3062
- 📹 We're recording this browser tab to learn from your experience.
3063
- 🎤 Please enable mic and camera if asked, to give us a complete picture.`);
3064
- const buttonElement = createElement('div', 'button', buttonStyle, 'Read guidelines to begin');
3065
- this.removeGreeting = () => {
3066
- // this.container.innerHTML = ''
3067
- if (micRequired || cameraRequired) {
3068
- void this.userRecorder.startRecording(30, Quality.Standard, micRequired, cameraRequired);
3069
- }
3070
- this.container.removeChild(buttonElement);
3071
- this.container.removeChild(descriptionElement);
3072
- this.container.removeChild(titleElement);
3073
- return false;
3074
- };
3075
- buttonElement.onclick = () => {
3076
- this.removeGreeting();
3077
- const durations = this.signalManager?.getDurations();
3078
- if (durations && this.signalManager) {
3079
- durations.testStart = this.app.timestamp();
3080
- this.signalManager.setDurations(durations);
3081
- }
3082
- void this.signalManager?.signalTest('begin');
3083
- this.container.style.fontFamily = `-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"`;
3084
- Object.assign(this.container.style, containerWidgetStyle);
3085
- this.showWidget(this.test?.guidelines || '', this.test?.tasks || []);
3086
- };
3087
- this.container.append(titleElement, descriptionElement, buttonElement);
3088
- this.bg.appendChild(this.container);
3089
- document.body.appendChild(this.bg);
3090
- }
3091
- showWidget(guidelines, tasks, inProgress) {
3092
- this.container.innerHTML = '';
3093
- Object.assign(this.bg.style, {
3094
- position: 'fixed',
3095
- zIndex: 99999999999999,
3096
- right: '8px',
3097
- left: 'unset',
3098
- width: 'fit-content',
3099
- top: '8px',
3100
- height: 'fit-content',
3101
- background: 'unset',
3102
- display: 'unset',
3103
- alignItems: 'unset',
3104
- justifyContent: 'unset',
3105
- });
3106
- // Create title section
3107
- const titleSection = this.createTitleSection();
3108
- this.container.style.fontFamily = `-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"`;
3109
- Object.assign(this.container.style, containerWidgetStyle);
3110
- const descriptionSection = this.createDescriptionSection(guidelines);
3111
- const tasksSection = this.createTasksSection(tasks);
3112
- const stopButton = createElement('div', 'stop_bn_or', stopWidgetStyle, 'Abort Session');
3113
- const stopContainer = createElement('div', 'stop_ct_or', { fontSize: '13px!important' });
3114
- stopContainer.style.fontSize = '13px';
3115
- stopContainer.append(stopButton);
3116
- this.container.append(titleSection, descriptionSection, tasksSection, stopContainer);
3117
- this.taskSection = tasksSection;
3118
- this.descriptionSection = descriptionSection;
3119
- this.stopButton = stopButton;
3120
- this.stopButtonContainer = stopContainer;
3121
- stopButton.onclick = () => {
3122
- this.userRecorder.discard();
3123
- void this.signalManager?.signalTest('skipped');
3124
- document.body.removeChild(this.bg);
3125
- window.close();
3126
- };
3127
- if (!inProgress) {
3128
- this.hideTaskSection();
3129
- }
3130
- else {
3131
- this.toggleDescriptionVisibility();
3132
- }
3133
- }
3134
- createTitleSection() {
3135
- const title = createElement('div', 'title', titleWidgetStyle);
3136
- const leftIcon = generateGrid();
3137
- const titleText = createElement('div', 'title_text', {
3138
- maxWidth: '19rem',
3139
- overflow: 'hidden',
3140
- textOverflow: 'ellipsis',
3141
- width: '100%',
3142
- fontSize: 16,
3143
- lineHeight: 'auto',
3144
- cursor: 'pointer',
3145
- }, this.test?.title);
3146
- const rightIcon = generateChevron();
3147
- title.append(leftIcon, titleText, rightIcon);
3148
- const toggleWidget = (isVisible) => {
3149
- this.widgetVisible = isVisible;
3150
- this.container.style.fontFamily = `-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"`;
3151
- Object.assign(this.container.style, this.widgetVisible
3152
- ? containerWidgetStyle
3153
- : { border: 'none', background: 'none', padding: 0 });
3154
- if (this.taskSection) {
3155
- Object.assign(this.taskSection.style, this.widgetVisible ? descriptionWidgetStyle : { display: 'none' });
3156
- }
3157
- if (this.descriptionSection) {
3158
- Object.assign(this.descriptionSection.style, this.widgetVisible ? descriptionWidgetStyle : { display: 'none' });
3159
- }
3160
- if (this.endSection) {
3161
- Object.assign(this.endSection.style, this.widgetVisible ? descriptionWidgetStyle : { display: 'none' });
3162
- }
3163
- if (this.stopButton) {
3164
- Object.assign(this.stopButton.style, this.widgetVisible ? stopWidgetStyle : { display: 'none' });
3165
- }
3166
- return isVisible;
3167
- };
3168
- const collapseWidget = () => {
3169
- Object.assign(rightIcon.style, {
3170
- transform: this.widgetVisible ? 'rotate(0deg)' : 'rotate(180deg)',
3171
- });
3172
- toggleWidget(!this.widgetVisible);
3173
- };
3174
- titleText.onclick = collapseWidget;
3175
- rightIcon.onclick = collapseWidget;
3176
- attachDND(this.bg, leftIcon);
3177
- this.collapseWidget = () => toggleWidget(false);
3178
- return title;
3179
- }
3180
- createDescriptionSection(guidelines) {
3181
- const section = createElement('div', 'description_section_or', descriptionWidgetStyle);
3182
- const titleContainer = createElement('div', 'description_s_title_or', sectionTitleStyle);
3183
- const title = createElement('div', 'title', {
3184
- fontSize: 13,
3185
- fontWeight: 500,
3186
- lineHeight: 'auto',
3187
- }, 'Introduction & Guidelines');
3188
- const icon = createElement('div', 'icon', symbolIcon, '-');
3189
- const content = createElement('div', 'content', contentStyle);
3190
- const descriptionC = createElement('div', 'text_description', {
3191
- maxHeight: '250px',
3192
- overflowY: 'auto',
3193
- whiteSpace: 'pre-wrap',
3194
- fontSize: 13,
3195
- color: '#454545',
3196
- lineHeight: 'auto',
3197
- });
3198
- descriptionC.innerHTML = guidelines;
3199
- const button = createElement('div', 'button_begin_or', buttonWidgetStyle, 'Begin Test');
3200
- titleContainer.append(title, icon);
3201
- content.append(descriptionC, button);
3202
- section.append(titleContainer, content);
3203
- const toggleDescriptionVisibility = () => {
3204
- this.widgetGuidelinesVisible = !this.widgetGuidelinesVisible;
3205
- icon.textContent = this.widgetGuidelinesVisible ? '-' : '+';
3206
- Object.assign(content.style, this.widgetGuidelinesVisible ? contentStyle : { display: 'none' });
3207
- };
3208
- titleContainer.onclick = toggleDescriptionVisibility;
3209
- this.toggleDescriptionVisibility = () => {
3210
- this.widgetGuidelinesVisible = false;
3211
- icon.textContent = this.widgetGuidelinesVisible ? '-' : '+';
3212
- Object.assign(content.style, this.widgetGuidelinesVisible ? contentStyle : { display: 'none' });
3213
- content.removeChild(button);
3214
- };
3215
- button.onclick = () => {
3216
- toggleDescriptionVisibility();
3217
- if (this.test) {
3218
- const durations = this.signalManager?.getDurations();
3219
- const taskDurationInd = durations
3220
- ? durations.tasks.findIndex((t) => this.test && t.taskId === this.test.tasks[0].task_id)
3221
- : null;
3222
- if (durations && taskDurationInd === -1) {
3223
- durations.tasks.push({
3224
- taskId: this.test.tasks[0].task_id,
3225
- started: this.app.timestamp(),
3226
- });
3227
- this.signalManager?.setDurations(durations);
3228
- }
3229
- void this.signalManager?.signalTask(this.test.tasks[0].task_id, 'begin');
3230
- }
3231
- this.showTaskSection();
3232
- content.removeChild(button);
3233
- };
3234
- return section;
3235
- }
3236
- createTasksSection(tasks) {
3237
- this.container.style.fontFamily = `-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"`;
3238
- Object.assign(this.container.style, containerWidgetStyle);
3239
- const section = createElement('div', 'task_section_or', descriptionWidgetStyle);
3240
- const titleContainer = createElement('div', 'description_t_title_or', sectionTitleStyle);
3241
- const title = createElement('div', 'title', {
3242
- fontSize: '13px',
3243
- fontWeight: '500',
3244
- lineHeight: 'auto',
3245
- }, 'Tasks');
3246
- const icon = createElement('div', 'icon', symbolIcon, '-');
3247
- const content = createElement('div', 'content', contentStyle);
3248
- const pagination = createElement('div', 'pagination', paginationStyle);
3249
- // const leftArrow = createElement('span', 'leftArrow', {}, '<')
3250
- // const rightArrow = createElement('span', 'rightArrow', {}, '>')
3251
- const taskCard = createElement('div', 'taskCard', taskDescriptionCard);
3252
- const taskText = createElement('div', 'taskText', taskTextStyle);
3253
- const taskDescription = createElement('div', 'taskDescription', taskDescriptionStyle);
3254
- const taskButtons = createElement('div', 'taskButtons', taskButtonsRow);
3255
- const inputTitle = createElement('div', 'taskText', taskTextStyle);
3256
- inputTitle.textContent = 'Your answer';
3257
- const inputArea = createElement('textarea', 'taskDescription', {
3258
- resize: 'vertical',
3259
- });
3260
- const inputContainer = createElement('div', 'inputArea', taskDescriptionCard);
3261
- inputContainer.append(inputTitle, inputArea);
3262
- const closePanelButton = createElement('div', 'closePanelButton', taskButtonStyle, 'Collapse Panel');
3263
- const nextButton = createElement('div', 'nextButton', taskButtonBorderedStyle, 'Done, Next');
3264
- titleContainer.append(title, icon);
3265
- taskCard.append(taskText, taskDescription);
3266
- taskButtons.append(closePanelButton, nextButton);
3267
- content.append(pagination, taskCard, inputContainer, taskButtons);
3268
- section.append(titleContainer, content);
3269
- const updateTaskContent = () => {
3270
- const task = tasks[this.currentTaskIndex];
3271
- taskText.textContent = task.title;
3272
- taskDescription.textContent = task.description;
3273
- if (task.allow_typing) {
3274
- inputContainer.style.display = 'flex';
3275
- }
3276
- else {
3277
- inputContainer.style.display = 'none';
3278
- }
3279
- };
3280
- // pagination.appendChild(leftArrow)
3281
- tasks.forEach((_, index) => {
3282
- const pageNumber = createElement('span', `or_task_${index}`, {
3283
- outline: '1px solid #efefef',
3284
- fontSize: '13px',
3285
- height: '24px',
3286
- width: '24px',
3287
- display: 'flex',
3288
- flexDirection: 'column',
3289
- alignItems: 'center',
3290
- justifyContent: 'center',
3291
- borderRadius: '6.25em',
3292
- }, (index + 1).toString());
3293
- pageNumber.id = `or_task_${index}`;
3294
- pagination.append(pageNumber);
3295
- });
3296
- // pagination.appendChild(rightArrow)
3297
- const toggleTasksVisibility = () => {
3298
- this.widgetTasksVisible = !this.widgetTasksVisible;
3299
- icon.textContent = this.widgetTasksVisible ? '-' : '+';
3300
- Object.assign(content.style, this.widgetTasksVisible ? contentStyle : { display: 'none' });
3301
- };
3302
- this.hideTaskSection = () => {
3303
- icon.textContent = '+';
3304
- Object.assign(content.style, {
3305
- display: 'none',
3306
- });
3307
- this.widgetTasksVisible = false;
3308
- return false;
3309
- };
3310
- this.showTaskSection = () => {
3311
- icon.textContent = '-';
3312
- Object.assign(content.style, contentStyle);
3313
- this.widgetTasksVisible = true;
3314
- return true;
3315
- };
3316
- const highlightActive = () => {
3317
- const activeTaskEl = document.getElementById(`or_task_${this.currentTaskIndex}`);
3318
- if (activeTaskEl) {
3319
- Object.assign(activeTaskEl.style, taskNumberActive);
3320
- }
3321
- for (let i = 0; i < this.currentTaskIndex; i++) {
3322
- const taskEl = document.getElementById(`or_task_${i}`);
3323
- if (taskEl) {
3324
- Object.assign(taskEl.style, taskNumberDone);
3325
- }
3326
- }
3327
- };
3328
- titleContainer.onclick = toggleTasksVisibility;
3329
- closePanelButton.onclick = this.collapseWidget;
3330
- nextButton.onclick = () => {
3331
- const textAnswer = tasks[this.currentTaskIndex].allow_typing ? inputArea.value : undefined;
3332
- inputArea.value = '';
3333
- void this.signalManager?.signalTask(tasks[this.currentTaskIndex].task_id, 'done', textAnswer);
3334
- if (this.currentTaskIndex < tasks.length - 1) {
3335
- this.currentTaskIndex++;
3336
- updateTaskContent();
3337
- const durations = this.signalManager?.getDurations();
3338
- if (durations &&
3339
- durations.tasks.findIndex((t) => t.taskId === tasks[this.currentTaskIndex].task_id) === -1) {
3340
- durations.tasks.push({
3341
- taskId: tasks[this.currentTaskIndex].task_id,
3342
- started: this.app.timestamp(),
3343
- });
3344
- this.signalManager?.setDurations(durations);
3345
- }
3346
- void this.signalManager?.signalTask(tasks[this.currentTaskIndex].task_id, 'begin');
3347
- highlightActive();
3348
- }
3349
- else {
3350
- this.showEndSection();
3351
- }
3352
- this.app.localStorage.setItem('or_uxt_task_index', this.currentTaskIndex.toString());
3353
- };
3354
- setTimeout(() => {
3355
- const firstTaskEl = document.getElementById('or_task_0');
3356
- if (firstTaskEl) {
3357
- Object.assign(firstTaskEl.style, taskNumberActive);
3358
- }
3359
- updateTaskContent();
3360
- highlightActive();
3361
- }, 1);
3362
- return section;
3363
- }
3364
- showEndSection() {
3365
- let isLoading = true;
3366
- void this.signalManager?.signalTest('done');
3367
- const section = createElement('div', 'end_section_or', endSectionStyle);
3368
- const title = createElement('div', 'end_title_or', {
3369
- fontSize: '1.25rem',
3370
- fontWeight: '500',
3371
- }, 'Thank you! 👍');
3372
- const description = createElement('div', 'end_description_or', {}, this.test?.conclusion ??
3373
- 'Thank you for participating in our usability test. Your feedback has been captured and will be used to enhance our website. \n' +
3374
- '\n' +
3375
- 'We appreciate your time and valuable input.');
3376
- const button = createElement('div', 'end_button_or', buttonWidgetStyle, 'Submitting Feedback');
3377
- const spinner = createSpinner();
3378
- button.appendChild(spinner);
3379
- if (this.test?.reqMic || this.test?.reqCamera) {
3380
- void this.userRecorder
3381
- .sendToAPI()
3382
- .then(() => {
3383
- button.removeChild(spinner);
3384
- button.textContent = 'End Session';
3385
- isLoading = false;
3386
- })
3387
- .catch((err) => {
3388
- console.error(err);
3389
- button.removeChild(spinner);
3390
- button.textContent = 'End Session';
3391
- isLoading = false;
3392
- });
3393
- }
3394
- else {
3395
- button.removeChild(spinner);
3396
- button.textContent = 'End Session';
3397
- isLoading = false;
3398
- }
3399
- if (this.taskSection) {
3400
- this.container.removeChild(this.taskSection);
3401
- }
3402
- if (this.descriptionSection) {
3403
- this.container.removeChild(this.descriptionSection);
3404
- }
3405
- if (this.stopButton && this.stopButtonContainer) {
3406
- this.container.removeChild(this.stopButtonContainer);
3407
- }
3408
- button.onclick = () => {
3409
- if (isLoading)
3410
- return;
3411
- window.close();
3412
- document.body.removeChild(this.bg);
3413
- };
3414
- section.append(title, description, button);
3415
- this.endSection = section;
3416
- this.container.append(section);
3417
- }
3418
- }
3419
-
3420
- //@ts-ignore
3421
- function isNode(sth) {
3422
- return !!sth && sth.nodeType != null;
3423
- }
3424
- function isSVGElement(node) {
3425
- return (node.namespaceURI === 'http://www.w3.org/2000/svg' || node.localName === 'svg');
3426
- }
3427
- function isUseElement(node) {
3428
- return node.localName === 'use';
3429
- }
3430
- function isElementNode(node) {
3431
- return node.nodeType === Node.ELEMENT_NODE;
3432
- }
3433
- function isCommentNode(node) {
3434
- return node.nodeType === Node.COMMENT_NODE;
3435
- }
3436
- function isTextNode(node) {
3437
- return node.nodeType === Node.TEXT_NODE;
3438
- }
3439
- function isDocument(node) {
3440
- return node.nodeType === Node.DOCUMENT_NODE;
3441
- }
3442
- function isRootNode(node) {
3443
- return node.nodeType === Node.DOCUMENT_NODE || node.nodeType === Node.DOCUMENT_FRAGMENT_NODE;
3444
- }
3445
- function hasTag(el, tagName) {
3446
- // @ts-ignore
3447
- return el.localName === tagName;
3448
- }
3449
-
3450
- class CanvasRecorder {
3451
- constructor(app, options) {
3452
- this.app = app;
3453
- this.options = options;
3454
- this.snapshots = {};
3455
- this.intervals = [];
3456
- this.restartTracking = () => {
3457
- this.clear();
3458
- this.app.nodes.scanTree(this.captureCanvas);
3459
- };
3460
- this.captureCanvas = (node) => {
3461
- const id = this.app.nodes.getID(node);
3462
- if (!id || !hasTag(node, 'canvas')) {
3463
- return;
3464
- }
3465
- const isIgnored = this.app.sanitizer.isObscured(id) || this.app.sanitizer.isHidden(id);
3466
- if (isIgnored || !hasTag(node, 'canvas') || this.snapshots[id]) {
3467
- return;
3468
- }
3469
- const observer = new IntersectionObserver((entries) => {
3470
- entries.forEach((entry) => {
3471
- if (entry.isIntersecting) {
3472
- if (entry.target) {
3473
- if (this.snapshots[id] && this.snapshots[id].createdAt) {
3474
- this.snapshots[id].paused = false;
3475
- }
3476
- else {
3477
- this.recordCanvas(entry.target, id);
3478
- }
3479
- /**
3480
- * We can switch this to start observing when element is in the view
3481
- * but otherwise right now we're just pausing when it's not
3482
- * just to save some bandwidth and space on backend
3483
- * */
3484
- // observer.unobserve(entry.target)
3485
- }
3486
- else {
3487
- if (this.snapshots[id]) {
3488
- this.snapshots[id].paused = true;
3489
- }
3490
- }
3491
- }
3492
- });
1659
+ const observer = new IntersectionObserver((entries) => {
1660
+ entries.forEach((entry) => {
1661
+ if (entry.isIntersecting) {
1662
+ if (this.snapshots[id] && this.snapshots[id].createdAt) {
1663
+ this.snapshots[id].paused = false;
1664
+ }
1665
+ else {
1666
+ this.recordCanvas(entry.target, id);
1667
+ }
1668
+ /**
1669
+ * We can switch this to start observing when element is in the view
1670
+ * but otherwise right now we're just pausing when it's not
1671
+ * just to save some bandwidth and space on backend
1672
+ * */
1673
+ // observer.unobserve(entry.target)
1674
+ }
1675
+ else {
1676
+ if (this.snapshots[id]) {
1677
+ this.snapshots[id].paused = true;
1678
+ }
1679
+ }
1680
+ });
3493
1681
  });
1682
+ this.observers.set(id, observer);
3494
1683
  observer.observe(node);
3495
1684
  };
3496
1685
  this.recordCanvas = (node, id) => {
@@ -3500,15 +1689,23 @@ class CanvasRecorder {
3500
1689
  createdAt: ts,
3501
1690
  paused: false,
3502
1691
  dummy: document.createElement('canvas'),
1692
+ isCapturing: false,
1693
+ isStopped: false,
3503
1694
  };
3504
1695
  const canvasMsg = CanvasNode(id.toString(), ts);
3505
1696
  this.app.send(canvasMsg);
1697
+ const cachedCanvas = node;
3506
1698
  const captureFn = (canvas) => {
1699
+ if (!this.snapshots[id] || this.snapshots[id].isCapturing || this.snapshots[id].isStopped) {
1700
+ return;
1701
+ }
1702
+ this.snapshots[id].isCapturing = true;
3507
1703
  captureSnapshot(canvas, this.options.quality, this.snapshots[id].dummy, this.options.fixedScaling, this.fileExt, (blob) => {
3508
- if (!blob)
1704
+ if (this.snapshots[id]) {
1705
+ this.snapshots[id].isCapturing = false;
1706
+ }
1707
+ if (!blob || !this.snapshots[id] || this.snapshots[id].isStopped) {
3509
1708
  return;
3510
- if (!this.snapshots[id]) {
3511
- return this.app.debug.warn('Canvas not present in snapshots after capture:', this.snapshots, id);
3512
1709
  }
3513
1710
  this.snapshots[id].images.push({ id: this.app.timestamp(), data: blob });
3514
1711
  if (this.snapshots[id].images.length > 9) {
@@ -3518,34 +1715,31 @@ class CanvasRecorder {
3518
1715
  });
3519
1716
  };
3520
1717
  const int = setInterval(() => {
3521
- const cid = this.app.nodes.getID(node);
3522
- const canvas = cid ? this.app.nodes.getNode(cid) : undefined;
3523
- if (!this.snapshots[id]) {
1718
+ const snapshot = this.snapshots[id];
1719
+ if (!snapshot || snapshot.isStopped) {
3524
1720
  this.app.debug.log('Canvas is not present in {snapshots}');
3525
- clearInterval(int);
1721
+ this.cleanupCanvas(id);
3526
1722
  return;
3527
1723
  }
3528
- if (!canvas || !hasTag(canvas, 'canvas') || canvas !== node) {
3529
- this.app.debug.log('Canvas element not in sync', canvas, node);
3530
- clearInterval(int);
1724
+ if (!document.contains(cachedCanvas)) {
1725
+ this.app.debug.log('Canvas element not in sync', cachedCanvas, node);
1726
+ this.cleanupCanvas(id);
3531
1727
  return;
3532
1728
  }
3533
- else {
3534
- if (!this.snapshots[id].paused) {
3535
- if (this.options.useAnimationFrame) {
3536
- requestAnimationFrame(() => {
3537
- captureFn(canvas);
3538
- });
3539
- }
3540
- else {
3541
- captureFn(canvas);
3542
- }
1729
+ if (!snapshot.paused) {
1730
+ if (this.options.useAnimationFrame) {
1731
+ requestAnimationFrame(() => {
1732
+ captureFn(cachedCanvas);
1733
+ });
1734
+ }
1735
+ else {
1736
+ captureFn(cachedCanvas);
3543
1737
  }
3544
1738
  }
3545
1739
  }, this.interval);
3546
- this.intervals.push(int);
1740
+ this.intervals.set(id, int);
3547
1741
  };
3548
- this.fileExt = options.fileExt ?? 'webp';
1742
+ this.fileExt = 'webp';
3549
1743
  this.interval = 1000 / options.fps;
3550
1744
  }
3551
1745
  startTracking() {
@@ -3558,16 +1752,90 @@ class CanvasRecorder {
3558
1752
  if (Object.keys(this.snapshots).length === 0) {
3559
1753
  return;
3560
1754
  }
3561
- const formData = new FormData();
3562
- images.forEach((snapshot) => {
3563
- const blob = snapshot.data;
3564
- if (!blob)
3565
- return;
3566
- formData.append('snapshot', blob, `${createdAt}_${canvasId}_${snapshot.id}.${this.fileExt}`);
3567
- if (this.options.isDebug) {
3568
- saveImageData(blob, `${createdAt}_${canvasId}_${snapshot.id}.${this.fileExt}`);
1755
+ if (this.pendingBatches.length >= this.MAX_QUEUE_SIZE) {
1756
+ this.app.debug.warn('Upload queue full, dropping canvas batch');
1757
+ return;
1758
+ }
1759
+ this.pendingBatches.push({ images, canvasId, createdAt });
1760
+ if (!this.isProcessingQueue) {
1761
+ this.processUploadQueue();
1762
+ }
1763
+ }
1764
+ async processUploadQueue() {
1765
+ this.isProcessingQueue = true;
1766
+ while (this.pendingBatches.length > 0) {
1767
+ if (this.uploadQueue >= this.MAX_CONCURRENT_UPLOADS) {
1768
+ await new Promise((resolve) => setTimeout(resolve, 100));
1769
+ continue;
3569
1770
  }
3570
- });
1771
+ const batch = this.pendingBatches.shift();
1772
+ if (!batch)
1773
+ break;
1774
+ this.uploadBatch(batch.images, batch.canvasId, batch.createdAt);
1775
+ }
1776
+ this.isProcessingQueue = false;
1777
+ }
1778
+ async uploadBatch(images, canvasId, createdAt) {
1779
+ if (this.options.isDebug) {
1780
+ const fileEntries = [];
1781
+ images.forEach((snapshot) => {
1782
+ if (!snapshot.data)
1783
+ return;
1784
+ fileEntries.push({ data: snapshot.data, name: `${createdAt}_${canvasId}_${snapshot.id}.${this.fileExt}` });
1785
+ });
1786
+ void saveArchive(fileEntries, `canvas_${canvasId}_${createdAt}`);
1787
+ return;
1788
+ }
1789
+ let formData;
1790
+ if (this.options.framesSupport) {
1791
+ // Pack frames into binary format: [uint64 LE timestamp][uint32 LE size][data] per frame
1792
+ const buffers = [];
1793
+ let totalSize = 0;
1794
+ for (const snapshot of images) {
1795
+ if (!snapshot.data)
1796
+ continue;
1797
+ const ab = await snapshot.data.arrayBuffer();
1798
+ buffers.push(ab);
1799
+ totalSize += 8 + 4 + ab.byteLength; // uint64 ts + uint32 size + data
1800
+ }
1801
+ if (totalSize === 0)
1802
+ return;
1803
+ const packed = new ArrayBuffer(totalSize);
1804
+ const view = new DataView(packed);
1805
+ const bytes = new Uint8Array(packed);
1806
+ let offset = 0;
1807
+ for (let i = 0; i < images.length; i++) {
1808
+ if (!images[i].data)
1809
+ continue;
1810
+ const ab = buffers.shift();
1811
+ const ts = images[i].id;
1812
+ // uint64 LE as two uint32 LE writes -- timestamp
1813
+ view.setUint32(offset, ts % 0x100000000, true);
1814
+ view.setUint32(offset + 4, Math.floor(ts / 0x100000000), true);
1815
+ offset += 8;
1816
+ // uint32 LE -- size
1817
+ view.setUint32(offset, ab.byteLength, true);
1818
+ offset += 4;
1819
+ // image data
1820
+ bytes.set(new Uint8Array(ab), offset);
1821
+ offset += ab.byteLength;
1822
+ }
1823
+ formData = new FormData();
1824
+ formData.append('type', 'frames');
1825
+ const fileName = `${createdAt}_${canvasId}.${this.fileExt}.frames`;
1826
+ formData.append('frames', new Blob([packed]), fileName);
1827
+ }
1828
+ else {
1829
+ // Legacy: send individual image files
1830
+ formData = new FormData();
1831
+ images.forEach((snapshot) => {
1832
+ const blob = snapshot.data;
1833
+ if (!blob)
1834
+ return;
1835
+ const name = `${createdAt}_${canvasId}_${snapshot.id}.${this.fileExt}`;
1836
+ formData.append('snapshot', blob, name);
1837
+ });
1838
+ }
3571
1839
  const initRestart = () => {
3572
1840
  this.app.debug.log('Restarting tracker; token expired');
3573
1841
  this.app.stop(false);
@@ -3575,6 +1843,7 @@ class CanvasRecorder {
3575
1843
  void this.app.start({}, true);
3576
1844
  }, 250);
3577
1845
  };
1846
+ this.uploadQueue++;
3578
1847
  fetch(this.app.options.ingestPoint + '/v1/web/images', {
3579
1848
  method: 'POST',
3580
1849
  headers: {
@@ -3590,10 +1859,50 @@ class CanvasRecorder {
3590
1859
  })
3591
1860
  .catch((e) => {
3592
1861
  this.app.debug.error('error saving canvas', e);
1862
+ })
1863
+ .finally(() => {
1864
+ this.uploadQueue--;
3593
1865
  });
3594
1866
  }
1867
+ cleanupCanvas(id) {
1868
+ if (this.snapshots[id]) {
1869
+ this.snapshots[id].isStopped = true;
1870
+ }
1871
+ const interval = this.intervals.get(id);
1872
+ if (interval) {
1873
+ clearInterval(interval);
1874
+ this.intervals.delete(id);
1875
+ }
1876
+ const observer = this.observers.get(id);
1877
+ if (observer) {
1878
+ observer.disconnect();
1879
+ this.observers.delete(id);
1880
+ }
1881
+ if (this.snapshots[id]?.dummy) {
1882
+ const dummy = this.snapshots[id].dummy;
1883
+ dummy.width = 0;
1884
+ dummy.height = 0;
1885
+ }
1886
+ delete this.snapshots[id];
1887
+ }
3595
1888
  clear() {
3596
- this.intervals.forEach((int) => clearInterval(int));
1889
+ // Flush remaining images before cleanup
1890
+ Object.keys(this.snapshots).forEach((idStr) => {
1891
+ const id = parseInt(idStr, 10);
1892
+ const snapshot = this.snapshots[id];
1893
+ if (snapshot && snapshot.images.length > 0) {
1894
+ this.sendSnaps(snapshot.images, id, snapshot.createdAt);
1895
+ snapshot.images = [];
1896
+ }
1897
+ });
1898
+ Object.keys(this.snapshots).forEach((idStr) => {
1899
+ const id = parseInt(idStr, 10);
1900
+ this.cleanupCanvas(id);
1901
+ });
1902
+ // don't clear pendingBatches or stop queue processing
1903
+ // to allow flushed images to finish uploading in the background
1904
+ this.intervals.clear();
1905
+ this.observers.clear();
3597
1906
  this.snapshots = {};
3598
1907
  }
3599
1908
  }
@@ -3620,15 +1929,80 @@ function captureSnapshot(canvas, quality = 'medium', dummy, fixedScaling = false
3620
1929
  canvas.toBlob(onBlob, imageFormat, qualityInt[quality]);
3621
1930
  }
3622
1931
  }
3623
- function saveImageData(imageDataBlob, name) {
3624
- const imageDataUrl = URL.createObjectURL(imageDataBlob);
1932
+ async function saveArchive(files, archiveName) {
1933
+ const zipBlob = await createZipBlob(files);
1934
+ const url = URL.createObjectURL(zipBlob);
3625
1935
  const link = document.createElement('a');
3626
- link.href = imageDataUrl;
3627
- link.download = name;
1936
+ link.href = url;
1937
+ link.download = `${archiveName}.zip`;
3628
1938
  link.style.display = 'none';
3629
1939
  document.body.appendChild(link);
3630
1940
  link.click();
3631
1941
  document.body.removeChild(link);
1942
+ URL.revokeObjectURL(url);
1943
+ }
1944
+ async function createZipBlob(files) {
1945
+ const parts = [];
1946
+ const centralDir = [];
1947
+ let offset = 0;
1948
+ for (const file of files) {
1949
+ const nameBytes = new TextEncoder().encode(file.name);
1950
+ const dataBytes = new Uint8Array(await file.data.arrayBuffer());
1951
+ const crc = crc32(dataBytes);
1952
+ // Local file header (30 bytes + filename)
1953
+ const local = new Uint8Array(30 + nameBytes.length);
1954
+ const lv = new DataView(local.buffer);
1955
+ lv.setUint32(0, 0x04034b50, true);
1956
+ lv.setUint16(4, 20, true);
1957
+ lv.setUint16(8, 0, true);
1958
+ lv.setUint32(14, crc, true);
1959
+ lv.setUint32(18, dataBytes.length, true);
1960
+ lv.setUint32(22, dataBytes.length, true);
1961
+ lv.setUint16(26, nameBytes.length, true);
1962
+ local.set(nameBytes, 30);
1963
+ // Central directory entry (46 bytes + filename)
1964
+ const cd = new Uint8Array(46 + nameBytes.length);
1965
+ const cv = new DataView(cd.buffer);
1966
+ cv.setUint32(0, 0x02014b50, true);
1967
+ cv.setUint16(4, 20, true);
1968
+ cv.setUint16(6, 20, true);
1969
+ cv.setUint32(16, crc, true);
1970
+ cv.setUint32(20, dataBytes.length, true);
1971
+ cv.setUint32(24, dataBytes.length, true);
1972
+ cv.setUint16(28, nameBytes.length, true);
1973
+ cv.setUint32(42, offset, true);
1974
+ cd.set(nameBytes, 46);
1975
+ parts.push(local);
1976
+ parts.push(dataBytes);
1977
+ centralDir.push(cd);
1978
+ offset += local.length + dataBytes.length;
1979
+ }
1980
+ const cdOffset = offset;
1981
+ let cdSize = 0;
1982
+ for (const entry of centralDir) {
1983
+ parts.push(entry);
1984
+ cdSize += entry.length;
1985
+ }
1986
+ // End of central directory (22 bytes)
1987
+ const eocd = new Uint8Array(22);
1988
+ const ev = new DataView(eocd.buffer);
1989
+ ev.setUint32(0, 0x06054b50, true);
1990
+ ev.setUint16(8, files.length, true);
1991
+ ev.setUint16(10, files.length, true);
1992
+ ev.setUint32(12, cdSize, true);
1993
+ ev.setUint32(16, cdOffset, true);
1994
+ parts.push(eocd);
1995
+ return new Blob(parts, { type: 'application/zip' });
1996
+ }
1997
+ function crc32(data) {
1998
+ let crc = 0xffffffff;
1999
+ for (let i = 0; i < data.length; i++) {
2000
+ crc ^= data[i];
2001
+ for (let j = 0; j < 8; j++) {
2002
+ crc = (crc >>> 1) ^ (crc & 1 ? 0xedb88320 : 0);
2003
+ }
2004
+ }
2005
+ return (crc ^ 0xffffffff) >>> 0;
3632
2006
  }
3633
2007
 
3634
2008
  const LogLevel = {
@@ -3749,6 +2123,27 @@ class Maintainer {
3749
2123
  }
3750
2124
  }
3751
2125
 
2126
+ // 4 levels, 128 frames between each level, 8_388_608 nodes per page
2127
+ // lets hope no one will need more :D
2128
+ const BITS_LEVEL = 2; // 4
2129
+ const BITS_ORDER = 7; // 128
2130
+ const BITS_NODE = 22; // 8_388_608
2131
+ const SHIFT_ORDER = BITS_NODE;
2132
+ const SHIFT_LEVEL = BITS_NODE + BITS_ORDER;
2133
+ const MASK_NODE = (1 << BITS_NODE) - 1;
2134
+ const MASK_ORDER = (1 << BITS_ORDER) - 1;
2135
+ const MASK_LEVEL = (1 << BITS_LEVEL) - 1;
2136
+ function pack(level, order, nodeId) {
2137
+ if (level < 0 || level > MASK_LEVEL)
2138
+ throw new RangeError('OR: nesting level overflow, max 4');
2139
+ if (order < 0 || order > MASK_ORDER)
2140
+ throw new RangeError('OR: frame order overflow, max 128');
2141
+ const v = ((level & MASK_LEVEL) << SHIFT_LEVEL) |
2142
+ ((order & MASK_ORDER) << SHIFT_ORDER) |
2143
+ (nodeId & MASK_NODE);
2144
+ return v >>> 0;
2145
+ }
2146
+
3752
2147
  class Nodes {
3753
2148
  constructor(params) {
3754
2149
  this.nodes = new Map();
@@ -3776,6 +2171,9 @@ class Nodes {
3776
2171
  }
3777
2172
  listeners.push([type, listener, useCapture]);
3778
2173
  };
2174
+ this.createFrameId = (level, frameOrder) => {
2175
+ return pack(level, frameOrder, 0);
2176
+ };
3779
2177
  this.unregisterNode = (node) => {
3780
2178
  const id = node[this.node_id];
3781
2179
  if (id !== undefined) {
@@ -3796,16 +2194,8 @@ class Nodes {
3796
2194
  this.maintainer = new Maintainer(this.nodes, this.unregisterNode, params.maintainer);
3797
2195
  this.maintainer.start();
3798
2196
  }
3799
- syntheticMode(frameOrder) {
3800
- const maxSafeNumber = Number.MAX_SAFE_INTEGER;
3801
- const placeholderSize = 99999999;
3802
- const nextFrameId = placeholderSize * frameOrder;
3803
- // I highly doubt that this will ever happen,
3804
- // but it will be easier to debug if it does
3805
- if (nextFrameId > maxSafeNumber) {
3806
- throw new Error('Placeholder id overflow');
3807
- }
3808
- this.nextNodeId = nextFrameId;
2197
+ crossdomainMode(level, frameOrder) {
2198
+ this.nextNodeId = this.createFrameId(level, frameOrder);
3809
2199
  }
3810
2200
  registerNode(node) {
3811
2201
  let id = node[this.node_id];
@@ -4570,8 +2960,7 @@ class Observer {
4570
2960
  sl.assignedNodes({ flatten: true }).forEach((n) => {
4571
2961
  const nid = this.app.nodes.getID(n);
4572
2962
  if (nid !== undefined) {
4573
- this.recents.set(nid, RecentsType.Removed);
4574
- this.commitNode(nid);
2963
+ this.recents.set(nid, RecentsType.Changed);
4575
2964
  }
4576
2965
  });
4577
2966
  });
@@ -4638,13 +3027,6 @@ class Observer {
4638
3027
  return true;
4639
3028
  }
4640
3029
  let slot = node.assignedSlot;
4641
- let isLightDom = false;
4642
- if (slot) {
4643
- // Check if the node is in light DOM (not in shadow DOM)
4644
- // This is a workaround for the issue with shadow DOM and slots
4645
- // where the slot is not assigned to the node in shadow DOM.
4646
- isLightDom = node.getRootNode() instanceof ShadowRoot;
4647
- }
4648
3030
  const parent = node.parentNode;
4649
3031
  let parentID;
4650
3032
  // Disable parent check for the upper context HTMLHtmlElement, because it is root there... (before)
@@ -4657,15 +3039,7 @@ class Observer {
4657
3039
  this.unbindTree(node);
4658
3040
  return false;
4659
3041
  }
4660
- if (isLightDom && slot) {
4661
- parentID = this.app.nodes.getID(slot);
4662
- // in light dom, we don't "slot" the node,
4663
- // but rather use the slot as a parent
4664
- slot = null;
4665
- }
4666
- else {
4667
- parentID = this.app.nodes.getID(parent);
4668
- }
3042
+ parentID = this.app.nodes.getID(parent);
4669
3043
  if (parentID === undefined) {
4670
3044
  this.unbindTree(node);
4671
3045
  return false;
@@ -4921,6 +3295,7 @@ class IFrameOffsets {
4921
3295
 
4922
3296
  var InlineCssMode;
4923
3297
  (function (InlineCssMode) {
3298
+ InlineCssMode[InlineCssMode["Unset"] = -1] = "Unset";
4924
3299
  /** default behavior -- will parse and cache the css file on backend */
4925
3300
  InlineCssMode[InlineCssMode["Disabled"] = 0] = "Disabled";
4926
3301
  /** will attempt to record the linked css file as AdoptedStyleSheet object */
@@ -4930,7 +3305,8 @@ var InlineCssMode;
4930
3305
  /** will fetch the file, then save it as plain css inside <style> node */
4931
3306
  InlineCssMode[InlineCssMode["PlainFetched"] = 3] = "PlainFetched";
4932
3307
  })(InlineCssMode || (InlineCssMode = {}));
4933
- function getInlineOptions(mode) {
3308
+ const localhostStylesDoc = 'https://docs.openreplay.com/en/troubleshooting/localhost/';
3309
+ function getInlineOptions(mode, logger) {
4934
3310
  switch (mode) {
4935
3311
  case InlineCssMode.Inline:
4936
3312
  return {
@@ -4956,6 +3332,27 @@ function getInlineOptions(mode) {
4956
3332
  forcePlain: true,
4957
3333
  },
4958
3334
  };
3335
+ case InlineCssMode.Unset:
3336
+ const isLocalhost = /^https?:\/\/(localhost|127\.0\.0\.1)(:\d+)?\/?/.test(window.location.href);
3337
+ if (isLocalhost) {
3338
+ logger(`Enabling InlineCssMode.PlainFetched by default on localhost to preserve css styles, refer to ${localhostStylesDoc} for details, set InlineCssMode to 0 to skip this behavior`);
3339
+ return {
3340
+ inlineRemoteCss: true,
3341
+ inlinerOptions: {
3342
+ forceFetch: true,
3343
+ forcePlain: true,
3344
+ },
3345
+ };
3346
+ }
3347
+ else {
3348
+ return {
3349
+ inlineRemoteCss: false,
3350
+ inlinerOptions: {
3351
+ forceFetch: false,
3352
+ forcePlain: false,
3353
+ },
3354
+ };
3355
+ }
4959
3356
  case InlineCssMode.Disabled:
4960
3357
  default:
4961
3358
  return {
@@ -4978,7 +3375,7 @@ class TopObserver extends Observer {
4978
3375
  const observerOptions = {
4979
3376
  disableSprites: opts.disableSprites,
4980
3377
  disableThrottling: opts.disableThrottling,
4981
- ...getInlineOptions(opts.inlineCss)
3378
+ ...getInlineOptions(opts.inlineCss, console.warn),
4982
3379
  };
4983
3380
  super(params.app, true, observerOptions);
4984
3381
  this.iframeOffsets = new IFrameOffsets();
@@ -5029,7 +3426,10 @@ class TopObserver extends Observer {
5029
3426
  this.app.debug.info('doc already observed for', id);
5030
3427
  return;
5031
3428
  }
5032
- const observer = new IFrameObserver(this.app, false, {});
3429
+ const observer = new IFrameObserver(this.app, false, {
3430
+ disableSprites: this.options.disableSprites,
3431
+ ...getInlineOptions(this.options.inlineCss, console.warn),
3432
+ });
5033
3433
  this.iframeObservers.set(iframe, observer);
5034
3434
  this.docObservers.set(currentDoc, observer);
5035
3435
  this.iframeObserversArr.push(observer);
@@ -5051,7 +3451,10 @@ class TopObserver extends Observer {
5051
3451
  handle();
5052
3452
  }
5053
3453
  handleShadowRoot(shRoot) {
5054
- const observer = new ShadowRootObserver(this.app);
3454
+ const observer = new ShadowRootObserver(this.app, false, {
3455
+ disableSprites: this.options.disableSprites,
3456
+ ...getInlineOptions(this.options.inlineCss, console.warn),
3457
+ });
5055
3458
  this.shadowRootObservers.set(shRoot, observer);
5056
3459
  observer.observe(shRoot.host);
5057
3460
  }
@@ -5077,7 +3480,7 @@ class TopObserver extends Observer {
5077
3480
  this.app.nodes.callNodeCallbacks(document, true);
5078
3481
  }, window.document.documentElement);
5079
3482
  }
5080
- crossdomainObserve(rootNodeId, frameOder) {
3483
+ crossdomainObserve(rootNodeId, frameOder, frameLevel) {
5081
3484
  const observer = this;
5082
3485
  Element.prototype.attachShadow = function () {
5083
3486
  // eslint-disable-next-line
@@ -5086,8 +3489,11 @@ class TopObserver extends Observer {
5086
3489
  return shadow;
5087
3490
  };
5088
3491
  this.app.nodes.clear();
5089
- this.app.nodes.syntheticMode(frameOder);
5090
- const iframeObserver = new IFrameObserver(this.app);
3492
+ this.app.nodes.crossdomainMode(frameLevel, frameOder);
3493
+ const iframeObserver = new IFrameObserver(this.app, false, {
3494
+ disableSprites: this.options.disableSprites,
3495
+ ...getInlineOptions(this.options.inlineCss, console.warn),
3496
+ });
5091
3497
  this.iframeObservers.set(window.document, iframeObserver);
5092
3498
  iframeObserver.syntheticObserve(rootNodeId, window.document);
5093
3499
  }
@@ -5191,6 +3597,7 @@ class Sanitizer {
5191
3597
  }
5192
3598
  }
5193
3599
 
3600
+ const tokenSeparator = '_$_';
5194
3601
  class Session {
5195
3602
  constructor(params) {
5196
3603
  this.metadata = {};
@@ -5218,19 +3625,22 @@ class Session {
5218
3625
  this.getSessionToken = (projectKey) => {
5219
3626
  const tokenWithProject = this.token || this.app.sessionStorage.getItem(this.options.session_token_key);
5220
3627
  if (projectKey && tokenWithProject) {
5221
- const savedProject = tokenWithProject.split('_&_')[1];
3628
+ const savedProject = tokenWithProject.split(tokenSeparator)[1];
5222
3629
  if (!savedProject || savedProject !== projectKey) {
5223
3630
  this.app.sessionStorage.removeItem(this.options.session_token_key);
5224
3631
  this.token = undefined;
5225
3632
  return undefined;
5226
3633
  }
5227
3634
  }
5228
- const token = tokenWithProject ? tokenWithProject.split('_&_')[0] : null;
3635
+ const token = tokenWithProject ? tokenWithProject.split(tokenSeparator)[0] : null;
5229
3636
  return token || undefined;
5230
3637
  };
3638
+ this.getRawTokenWithProject = () => {
3639
+ return this.token || this.app.sessionStorage.getItem(this.options.session_token_key);
3640
+ };
5231
3641
  this.setSessionToken = (token, projectKey) => {
5232
- this.token = `${token}_&_${projectKey}`;
5233
- this.app.sessionStorage.setItem(this.options.session_token_key, `${token}_&_${projectKey}`);
3642
+ this.token = `${token}${tokenSeparator}${projectKey}`;
3643
+ this.app.sessionStorage.setItem(this.options.session_token_key, `${token}${tokenSeparator}${projectKey}`);
5234
3644
  };
5235
3645
  this.app = params.app;
5236
3646
  this.options = params.options;
@@ -5293,7 +3703,7 @@ class Session {
5293
3703
  }
5294
3704
  getSessionHash() {
5295
3705
  const pageNo = this.getPageNumber();
5296
- const token = this.getSessionToken();
3706
+ const token = this.getRawTokenWithProject();
5297
3707
  if (pageNo === undefined || token === undefined) {
5298
3708
  return;
5299
3709
  }
@@ -5386,9 +3796,8 @@ class Ticker {
5386
3796
  * this value is injected during build time via rollup
5387
3797
  * */
5388
3798
  // @ts-ignore
5389
- 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/web/i\",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}`};s&&(h[\"Content-Encoding\"]=\"gzip\"),null!==this.token?fetch(`${this.ingestURL}?batch=${null!==(e=this.pageNo)&&void 0!==e?e:\"noPageNum\"}_${null!=n?n:\"noBatchNum\"}`,{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(\"OpenReplay:\",e),this.retry(t,s,`${null!=i?i:\"noBatchNum\"}_reject:${e.message}`)})):setTimeout((()=>{this.sendBatch(t,s,`${null!=i?i:\"noBatchNum\"}_newToken`)}),500)}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 38: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 37:return this.uint(t[1])&&this.string(t[2])&&this.uint(t[3]);case 39:return this.string(t[1])&&this.string(t[2])&&this.string(t[3])&&this.string(t[4])&&this.uint(t[5])&&this.uint(t[6])&&this.uint(t[7]);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 59:return this.uint(t[1])&&this.uint(t[2])&&this.uint(t[3])&&this.uint(t[4])&&this.string(t[5])&&this.string(t[6])&&this.string(t[7]);case 67:case 73:return this.uint(t[1])&&this.string(t[2])&&this.uint(t[3])&&this.string(t[4]);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 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(\"OpenReplay: 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(\"OpenReplay: 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";
3799
+ 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/web/i\",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}`};s&&(h[\"Content-Encoding\"]=\"gzip\"),null!==this.token?fetch(`${this.ingestURL}?batch=${null!==(e=this.pageNo)&&void 0!==e?e:\"noPageNum\"}_${null!=n?n:\"noBatchNum\"}`,{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(\"OpenReplay:\",e),this.retry(t,s,`${null!=i?i:\"noBatchNum\"}_reject:${e.message}`)})):setTimeout((()=>{this.sendBatch(t,s,`${null!=i?i:\"noBatchNum\"}_newToken`)}),500)}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(\"OpenReplay: 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(\"OpenReplay: 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";
5390
3800
  const CANCELED = 'canceled';
5391
- const uxtStorageKey = 'or_uxt_active';
5392
3801
  const bufferStorageKey = 'or_buffer_1';
5393
3802
  const UnsuccessfulStart = (reason) => ({ reason, success: false });
5394
3803
  const SuccessfulStart = (body) => ({ ...body, success: true });
@@ -5424,6 +3833,9 @@ const proto = {
5424
3833
  startIframe: 'start tracker inside frame',
5425
3834
  // checking updates
5426
3835
  polling: 'hello-how-are-you-im-under-the-water-please-help-me',
3836
+ // happens if tab is old and has outdated token but
3837
+ // not communicating with backend to update it (for whatever reason)
3838
+ reset: 'reset-your-session-please',
5427
3839
  };
5428
3840
  class App {
5429
3841
  constructor(projectKey, sessionToken, options, signalError, insideIframe) {
@@ -5440,7 +3852,7 @@ class App {
5440
3852
  this.stopCallbacks = [];
5441
3853
  this.commitCallbacks = [];
5442
3854
  this.activityState = ActivityState.NotActive;
5443
- this.version = '16.4.11-beta.0'; // TODO: version compatability check inside each plugin.
3855
+ this.version = '17.2.3'; // TODO: version compatability check inside each plugin.
5444
3856
  this.socketMode = false;
5445
3857
  this.compressionThreshold = 24 * 1000;
5446
3858
  this.bc = null;
@@ -5450,10 +3862,7 @@ class App {
5450
3862
  this.rootId = null;
5451
3863
  this.pageFrames = [];
5452
3864
  this.frameOderNumber = 0;
5453
- this.features = {
5454
- 'feature-flags': true,
5455
- 'usability-test': true,
5456
- };
3865
+ this.frameLevel = 0;
5457
3866
  this.emptyBatchCounter = 0;
5458
3867
  /** used by child iframes for crossdomain only */
5459
3868
  this.parentActive = false;
@@ -5483,6 +3892,7 @@ class App {
5483
3892
  this.rootId = data.id;
5484
3893
  this.session.setSessionToken(data.token, this.projectKey);
5485
3894
  this.frameOderNumber = data.frameOrderNumber;
3895
+ this.frameLevel = data.frameLevel;
5486
3896
  this.debug.log('starting iframe tracking', data);
5487
3897
  this.allowAppStart();
5488
3898
  }
@@ -5532,8 +3942,8 @@ class App {
5532
3942
  line: proto.iframeId,
5533
3943
  id,
5534
3944
  token,
5535
- // since indexes go from 0 we +1
5536
3945
  frameOrderNumber: order,
3946
+ frameLevel: this.frameLevel + 1,
5537
3947
  };
5538
3948
  this.debug.log('Got child frame signal; nodeId', id, event.source, iframeData);
5539
3949
  // @ts-ignore
@@ -5551,7 +3961,8 @@ class App {
5551
3961
  * */
5552
3962
  if (data.line === proto.iframeBatch) {
5553
3963
  const msgBatch = data.messages;
5554
- const mappedMessages = msgBatch.map((msg) => {
3964
+ const mappedMessages = [];
3965
+ msgBatch.forEach((msg) => {
5555
3966
  if (msg[0] === 20 /* MType.MouseMove */) {
5556
3967
  let fixedMessage = msg;
5557
3968
  this.pageFrames.forEach((frame) => {
@@ -5561,7 +3972,7 @@ class App {
5561
3972
  fixedMessage = [type, x + left, y + top];
5562
3973
  }
5563
3974
  });
5564
- return fixedMessage;
3975
+ mappedMessages.push(fixedMessage);
5565
3976
  }
5566
3977
  if (msg[0] === 68 /* MType.MouseClick */) {
5567
3978
  let fixedMessage = msg;
@@ -5587,9 +3998,11 @@ class App {
5587
3998
  ];
5588
3999
  }
5589
4000
  });
5590
- return fixedMessage;
4001
+ mappedMessages.push(fixedMessage);
4002
+ }
4003
+ if (![28 /* MType.UserID */, 29 /* MType.UserAnonymousID */, 30 /* MType.Metadata */].includes(msg[0])) {
4004
+ mappedMessages.push(msg);
5591
4005
  }
5592
- return msg;
5593
4006
  });
5594
4007
  this.messages.push(...mappedMessages);
5595
4008
  }
@@ -5673,6 +4086,19 @@ class App {
5673
4086
  }
5674
4087
  };
5675
4088
  this.startTimeout = null;
4089
+ this.restart = () => {
4090
+ this.stop(false);
4091
+ this.waitStatus(ActivityState.NotActive).then(() => {
4092
+ this.allowAppStart();
4093
+ this.start(this.prevOpts, true)
4094
+ .then((r) => {
4095
+ this.debug.info('Session restart', r);
4096
+ })
4097
+ .catch((e) => {
4098
+ this.debug.error('Session restart failed', e);
4099
+ });
4100
+ });
4101
+ };
5676
4102
  this.send = (message, urgent = false) => {
5677
4103
  if (this.activityState === ActivityState.NotActive) {
5678
4104
  return;
@@ -5760,7 +4186,6 @@ class App {
5760
4186
  });
5761
4187
  });
5762
4188
  };
5763
- this.onUxtCb = [];
5764
4189
  this.contextId = Math.random().toString(36).slice(2);
5765
4190
  this.projectKey = projectKey;
5766
4191
  this.networkOptions = options.network;
@@ -5795,7 +4220,7 @@ class App {
5795
4220
  useAnimationFrame: false,
5796
4221
  },
5797
4222
  forceNgOff: false,
5798
- inlineCss: 0,
4223
+ inlineCss: InlineCssMode.Unset,
5799
4224
  disableSprites: false,
5800
4225
  disableThrottling: false,
5801
4226
  };
@@ -5828,7 +4253,6 @@ class App {
5828
4253
  app: this,
5829
4254
  isDictDisabled: Boolean(this.options.disableStringDict || this.options.crossdomain?.enabled),
5830
4255
  });
5831
- this.featureFlags = new FeatureFlags(this);
5832
4256
  this.tagWatcher = new TagWatcher({
5833
4257
  sessionStorage: this.sessionStorage,
5834
4258
  errLog: this.debug.error,
@@ -5836,7 +4260,10 @@ class App {
5836
4260
  });
5837
4261
  this.session.attachUpdateCallback(({ userID, metadata }) => {
5838
4262
  if (userID != null) {
5839
- // TODO: nullable userID
4263
+ if (!userID || typeof userID !== 'string' || userID.trim().length === 0) {
4264
+ this.debug.warn('Invalid userID (must be type string), ignoring.');
4265
+ return;
4266
+ }
5840
4267
  this.send(UserID(userID));
5841
4268
  }
5842
4269
  if (metadata != null) {
@@ -5853,6 +4280,7 @@ class App {
5853
4280
  * listen for messages from parent window, so we can signal that we're alive
5854
4281
  * */
5855
4282
  window.addEventListener('message', this.parentCrossDomainFrameListener);
4283
+ window.addEventListener('message', this.crossDomainIframeListener);
5856
4284
  setInterval(() => {
5857
4285
  window.parent.postMessage({
5858
4286
  line: proto.polling,
@@ -5907,6 +4335,12 @@ class App {
5907
4335
  });
5908
4336
  }
5909
4337
  }
4338
+ if (ev.data.line === proto.reset) {
4339
+ const newToken = ev.data.token;
4340
+ this.debug.log('Received reset signal from another tab');
4341
+ this.session.setSessionToken(newToken, this.projectKey);
4342
+ this.restart();
4343
+ }
5910
4344
  };
5911
4345
  }
5912
4346
  }
@@ -6010,15 +4444,21 @@ class App {
6010
4444
  else if (data.type === 'compress') {
6011
4445
  const batch = data.batch;
6012
4446
  const batchSize = batch.byteLength;
6013
- if (batchSize > this.compressionThreshold) {
6014
- gzip(data.batch, { mtime: 0 }, (err, result) => {
6015
- if (err) {
6016
- this.debug.error('Openreplay compression error:', err);
6017
- this.worker?.postMessage({ type: 'uncompressed', batch: batch });
6018
- }
6019
- else {
6020
- this.worker?.postMessage({ type: 'compressed', batch: result });
6021
- }
4447
+ const hasCompressionAPI = 'CompressionStream' in globalThis;
4448
+ if (batchSize > this.compressionThreshold && hasCompressionAPI) {
4449
+ const blob = new Blob([batch]);
4450
+ const stream = blob.stream().pipeThrough(new CompressionStream('gzip'));
4451
+ new Response(stream)
4452
+ .arrayBuffer()
4453
+ .then((compressedBuffer) => {
4454
+ this.worker?.postMessage({
4455
+ type: 'compressed',
4456
+ batch: new Uint8Array(compressedBuffer),
4457
+ });
4458
+ })
4459
+ .catch((err) => {
4460
+ this.debug.error('Openreplay compression error:', err);
4461
+ this.worker?.postMessage({ type: 'uncompressed', batch: batch });
6022
4462
  });
6023
4463
  }
6024
4464
  else {
@@ -6311,8 +4751,7 @@ class App {
6311
4751
  const {
6312
4752
  // this token is needed to fetch conditions and flags,
6313
4753
  // but it can't be used to record a session
6314
- token, userBrowser, userCity, userCountry, userDevice, userOS, userState, projectID, features, } = await r.json();
6315
- this.features = features ? features : this.features;
4754
+ token, userBrowser, userCity, userCountry, userDevice, userOS, userState, projectID, } = await r.json();
6316
4755
  this.session.assign({ projectID });
6317
4756
  this.session.setUserInfo({
6318
4757
  userBrowser,
@@ -6325,10 +4764,6 @@ class App {
6325
4764
  const onStartInfo = { sessionToken: token, userUUID: '', sessionID: '' };
6326
4765
  this.startCallbacks.forEach((cb) => cb(onStartInfo));
6327
4766
  await this.conditionsManager?.fetchConditions(projectID, token);
6328
- if (this.features['feature-flags']) {
6329
- await this.featureFlags.reloadFlags(token);
6330
- this.conditionsManager?.processFlags(this.featureFlags.flags);
6331
- }
6332
4767
  await this.tagWatcher.fetchTags(this.options.ingestPoint, token);
6333
4768
  }
6334
4769
  /**
@@ -6484,9 +4919,10 @@ class App {
6484
4919
  // Reset session metadata only if requested directly
6485
4920
  this.session.reset();
6486
4921
  }
4922
+ const userId = startOpts.userID ? startOpts.userID.trim() : undefined;
6487
4923
  this.session.assign({
6488
4924
  // MBTODO: maybe it would make sense to `forceNew` if the `userID` was changed
6489
- userID: startOpts.userID,
4925
+ userID: userId || undefined,
6490
4926
  metadata: startOpts.metadata,
6491
4927
  });
6492
4928
  const timestamp = now();
@@ -6524,6 +4960,7 @@ class App {
6524
4960
  assistOnly: startOpts.assistOnly ?? this.socketMode,
6525
4961
  width: window.screen.width,
6526
4962
  height: window.screen.height,
4963
+ referrer: document.referrer,
6527
4964
  }),
6528
4965
  });
6529
4966
  if (r.status !== 200) {
@@ -6540,8 +4977,7 @@ class App {
6540
4977
  delay, // derived from token
6541
4978
  sessionID, // derived from token
6542
4979
  startTimestamp, // real startTS (server time), derived from sessionID
6543
- userBrowser, userCity, userCountry, userDevice, userOS, userState, canvasEnabled, canvasQuality, canvasFPS, assistOnly: socketOnly, features, } = await r.json();
6544
- this.features = features ? features : this.features;
4980
+ userBrowser, userCity, userCountry, userDevice, userOS, userState, canvasEnabled, canvasQuality, canvasFPS, framesSupport, assistOnly: socketOnly, } = await r.json();
6545
4981
  if (typeof token !== 'string' ||
6546
4982
  typeof userUUID !== 'string' ||
6547
4983
  (typeof startTimestamp !== 'number' && typeof startTimestamp !== 'undefined') ||
@@ -6554,6 +4990,12 @@ class App {
6554
4990
  }
6555
4991
  this.delay = delay;
6556
4992
  this.session.setSessionToken(token, this.projectKey);
4993
+ if (sessionToken && sessionToken !== token) {
4994
+ this.bc?.postMessage({
4995
+ type: proto.reset,
4996
+ token: token,
4997
+ });
4998
+ }
6557
4999
  this.session.setUserInfo({
6558
5000
  userBrowser,
6559
5001
  userCity,
@@ -6594,17 +5036,9 @@ class App {
6594
5036
  if (startOpts.startCallback) {
6595
5037
  startOpts.startCallback(SuccessfulStart(onStartInfo));
6596
5038
  }
6597
- if (this.features['feature-flags']) {
6598
- try {
6599
- void this.featureFlags.reloadFlags();
6600
- }
6601
- catch (e) {
6602
- this.debug.log("Error getting feature flags", e);
6603
- }
6604
- }
6605
5039
  await this.tagWatcher.fetchTags(this.options.ingestPoint, token);
6606
5040
  this.activityState = ActivityState.Active;
6607
- if (this.options.crossdomain?.enabled && !this.insideIframe) {
5041
+ if (this.options.crossdomain?.enabled) {
6608
5042
  void this.bootChildrenFrames();
6609
5043
  }
6610
5044
  if (canvasEnabled && !this.options.canvas.disableCanvas) {
@@ -6616,6 +5050,7 @@ class App {
6616
5050
  isDebug: this.options.canvas.__save_canvas_locally,
6617
5051
  fixedScaling: this.options.canvas.fixedCanvasScaling,
6618
5052
  useAnimationFrame: this.options.canvas.useAnimationFrame,
5053
+ framesSupport: !!framesSupport,
6619
5054
  });
6620
5055
  }
6621
5056
  /** --------------- COLD START BUFFER ------------------*/
@@ -6631,44 +5066,13 @@ class App {
6631
5066
  /** --------------- COLD START BUFFER ------------------*/
6632
5067
  }
6633
5068
  if (this.insideIframe && this.rootId) {
6634
- this.observer.crossdomainObserve(this.rootId, this.frameOderNumber);
5069
+ this.observer.crossdomainObserve(this.rootId, this.frameOderNumber, this.frameLevel);
6635
5070
  }
6636
5071
  else {
6637
5072
  this.observer.observe();
6638
5073
  }
6639
5074
  this.ticker.start();
6640
5075
  this.canvasRecorder?.startTracking();
6641
- if (this.features['usability-test'] && !this.insideIframe) {
6642
- this.uxtManager = this.uxtManager
6643
- ? this.uxtManager
6644
- : new UserTestManager(this, uxtStorageKey);
6645
- let uxtId;
6646
- const savedUxtTag = this.localStorage.getItem(uxtStorageKey);
6647
- if (savedUxtTag) {
6648
- uxtId = parseInt(savedUxtTag, 10);
6649
- }
6650
- if (location?.search) {
6651
- const query = new URLSearchParams(location.search);
6652
- if (query.has('oruxt')) {
6653
- const qId = query.get('oruxt');
6654
- uxtId = qId ? parseInt(qId, 10) : undefined;
6655
- }
6656
- }
6657
- if (uxtId) {
6658
- if (!this.uxtManager.isActive) {
6659
- // eslint-disable-next-line
6660
- this.uxtManager.getTest(uxtId, token, Boolean(savedUxtTag)).then((id) => {
6661
- if (id) {
6662
- this.onUxtCb.forEach((cb) => cb(id));
6663
- }
6664
- });
6665
- }
6666
- else {
6667
- // @ts-ignore
6668
- this.onUxtCb.forEach((cb) => cb(uxtId));
6669
- }
6670
- }
6671
- }
6672
5076
  return SuccessfulStart(onStartInfo);
6673
5077
  }
6674
5078
  catch (reason) {
@@ -6689,13 +5093,6 @@ class App {
6689
5093
  return UnsuccessfulStart(errorMessage);
6690
5094
  }
6691
5095
  }
6692
- addOnUxtCb(cb) {
6693
- // @ts-ignore
6694
- this.onUxtCb.push(cb);
6695
- }
6696
- getUxtId() {
6697
- return this.uxtManager?.getTestId();
6698
- }
6699
5096
  async waitStart() {
6700
5097
  return new Promise((resolve) => {
6701
5098
  const int = setInterval(() => {
@@ -6784,7 +5181,7 @@ class App {
6784
5181
  stop(stopWorker = true) {
6785
5182
  if (this.activityState !== ActivityState.NotActive) {
6786
5183
  try {
6787
- if (!this.insideIframe && this.options.crossdomain?.enabled) {
5184
+ if (this.options.crossdomain?.enabled) {
6788
5185
  this.killChildrenFrames();
6789
5186
  }
6790
5187
  this.attributeSender.clear();
@@ -6816,9 +5213,16 @@ function Connection (app) {
6816
5213
  if (connection === undefined) {
6817
5214
  return;
6818
5215
  }
6819
- const sendConnectionInformation = () => app.send(ConnectionInformation(Math.round(connection.downlink * 1000), connection.type || 'unknown'));
6820
- sendConnectionInformation();
6821
- connection.addEventListener('change', sendConnectionInformation);
5216
+ const sendConnectionInformation = () => {
5217
+ app.send(ConnectionInformation(Math.round(connection.downlink * 1000), connection.effectiveType || 'unknown'));
5218
+ };
5219
+ app.attachStartCallback(() => {
5220
+ sendConnectionInformation();
5221
+ connection.addEventListener('change', sendConnectionInformation);
5222
+ });
5223
+ app.attachStopCallback(() => {
5224
+ connection.removeEventListener('change', sendConnectionInformation);
5225
+ });
6822
5226
  }
6823
5227
 
6824
5228
  const printError = IN_BROWSER && 'InstallTrigger' in window // detect Firefox
@@ -7347,19 +5751,21 @@ const labelElementFor = IN_BROWSER && 'labels' in HTMLInputElement.prototype
7347
5751
  }
7348
5752
  }
7349
5753
  };
7350
- function getInputLabel(node) {
7351
- let label = getLabelAttribute(node);
7352
- if (label === null) {
7353
- const labelElement = labelElementFor(node);
7354
- label =
7355
- (labelElement && labelElement.innerText) ||
7356
- node.placeholder ||
7357
- node.name ||
7358
- node.id ||
7359
- node.className ||
7360
- node.type;
7361
- }
7362
- return normSpaces(label).slice(0, 100);
5754
+ function getInputLabel(node, customAttributes) {
5755
+ const openreplayLabel = getLabelAttribute(node);
5756
+ if (openreplayLabel !== null)
5757
+ return normSpaces(openreplayLabel).slice(0, 100);
5758
+ const customAttributeLabel = getCustomAttributeLabel(node, customAttributes);
5759
+ if (customAttributeLabel)
5760
+ return customAttributeLabel;
5761
+ if (node.id)
5762
+ return `#${node.id}`;
5763
+ const classLabel = getClassSelector(node);
5764
+ if (classLabel)
5765
+ return classLabel;
5766
+ const labelElement = labelElementFor(node);
5767
+ const label = node.name || node.placeholder || (labelElement && labelElement.innerText) || node.type;
5768
+ return normSpaces(label || '').slice(0, 100);
7363
5769
  }
7364
5770
  const InputMode = {
7365
5771
  Plain: 0,
@@ -7440,7 +5846,7 @@ function Input (app, opts) {
7440
5846
  }, 3);
7441
5847
  function sendInputChange(id, node, hesitationTime, inputTime) {
7442
5848
  const { value, mask } = getInputValue(id, node);
7443
- let label = getInputLabel(node);
5849
+ let label = getInputLabel(node, options.customAttributes);
7444
5850
  if (app.sanitizer.privateMode) {
7445
5851
  label = label.replaceAll(/./g, '*');
7446
5852
  }
@@ -7549,21 +5955,28 @@ function _getTarget(target, document) {
7549
5955
  return target === document.documentElement ? null : target;
7550
5956
  }
7551
5957
  function Mouse (app, options) {
7552
- const { disableClickmaps = false } = options || {};
5958
+ const { disableClickmaps = false, customAttributes } = options || {};
7553
5959
  function getTargetLabel(target) {
7554
5960
  const dl = getLabelAttribute(target);
7555
5961
  if (dl !== null) {
7556
5962
  return dl;
7557
5963
  }
7558
5964
  if (hasTag(target, 'input')) {
7559
- return getInputLabel(target);
7560
- }
5965
+ return getInputLabel(target, customAttributes);
5966
+ }
5967
+ const customAttributeLabel = getCustomAttributeLabel(target, customAttributes);
5968
+ if (customAttributeLabel)
5969
+ return customAttributeLabel;
5970
+ if (target.id)
5971
+ return `#${target.id}`;
5972
+ const classLabel = getClassSelector(target);
5973
+ if (classLabel)
5974
+ return classLabel;
7561
5975
  if (isClickable(target)) {
7562
5976
  let label = '';
7563
5977
  if (target instanceof HTMLElement) {
7564
5978
  label = app.sanitizer.getInnerTextSecure(target);
7565
5979
  }
7566
- label = label || target.id || target.className;
7567
5980
  return normSpaces(label).slice(0, 100);
7568
5981
  }
7569
5982
  return '';
@@ -7707,6 +6120,11 @@ function getCSSPath(el) {
7707
6120
  return false;
7708
6121
  if (el.id)
7709
6122
  return `#${cssEscape(el.id)}`;
6123
+ // if has data attributes - use them as they are more likely to be stable and unique
6124
+ const dataAttr = Array.from(el.attributes).find(attr => attr.name.startsWith('data-'));
6125
+ if (dataAttr) {
6126
+ return `[${dataAttr.name}="${cssEscape(dataAttr.value)}"]`;
6127
+ }
7710
6128
  const parts = [];
7711
6129
  while (el && el.nodeType === 1 && el !== el.ownerDocument) {
7712
6130
  if (el.id) {
@@ -8054,11 +6472,21 @@ function Viewport (app, options) {
8054
6472
  const urlSanitizer = options?.urlSanitizer || ((u) => u);
8055
6473
  const titleSanitizer = options?.titleSanitizer || ((t) => t);
8056
6474
  const sendSetPageLocation = app.safe(() => {
8057
- const { URL } = document;
8058
- if (URL !== url) {
8059
- url = URL;
6475
+ const currURL = document.URL;
6476
+ if (currURL !== url) {
6477
+ url = currURL;
6478
+ if (options?.replaceHashSymbol) {
6479
+ // replace hash router symbol if needed without affecting pathname of the url
6480
+ const u = new URL(currURL);
6481
+ const hashRoute = u.hash.startsWith('#/') ? u.hash.slice(2) : "";
6482
+ const routePath = hashRoute ? "/" + hashRoute.replace(/^\/+/, "") : "";
6483
+ const cleaned = u.origin + u.pathname.replace(/\/$/, "") + routePath + u.search;
6484
+ url = cleaned;
6485
+ }
8060
6486
  const sanitized = urlSanitizer(url);
8061
- const safeTitle = app.sanitizer.privateMode ? stringWiper(document.title) : titleSanitizer(document.title);
6487
+ const safeTitle = app.sanitizer.privateMode
6488
+ ? stringWiper(document.title)
6489
+ : titleSanitizer(document.title);
8062
6490
  const safeUrl = app.sanitizer.privateMode ? stringWiper(sanitized) : sanitized;
8063
6491
  const safeReferrer = app.sanitizer.privateMode ? stringWiper(referrer) : referrer;
8064
6492
  app.send(SetPageLocation(safeUrl, safeReferrer, navigationStart, safeTitle));
@@ -8483,9 +6911,9 @@ function axiosSpy (app, instance, opts, sanitize, stringify) {
8483
6911
  });
8484
6912
  }
8485
6913
  function isAxiosError(payload) {
8486
- return isObject(payload) && payload.isAxiosError === true;
6914
+ return isObject$1(payload) && payload.isAxiosError === true;
8487
6915
  }
8488
- function isObject(thing) {
6916
+ function isObject$1(thing) {
8489
6917
  return thing !== null && typeof thing === 'object';
8490
6918
  }
8491
6919
 
@@ -9818,6 +8246,11 @@ function Tabs (app) {
9818
8246
  app.send(TabChange(app.session.getTabId()));
9819
8247
  }
9820
8248
  }
8249
+ app.attachStartCallback(() => {
8250
+ // just for the good measure in case of restarts caused by assist plugin,
8251
+ // to keep latest active tab in state for live player
8252
+ changeTab();
8253
+ });
9821
8254
  app.attachEventListener(window, 'focus', changeTab, false, false);
9822
8255
  }
9823
8256
 
@@ -9859,6 +8292,8 @@ function webAnimations(app, options = {}) {
9859
8292
  handled.add(anim);
9860
8293
  anim.addEventListener('finish', () => {
9861
8294
  const lastKF = anim.effect.getKeyframes().at(-1);
8295
+ if (!lastKF)
8296
+ return;
9862
8297
  const computedStyle = getComputedStyle(el);
9863
8298
  const keys = Object.keys(lastKF).filter((p) => !toIgnore.includes(p));
9864
8299
  // @ts-ignore
@@ -9899,6 +8334,977 @@ function webAnimations(app, options = {}) {
9899
8334
  });
9900
8335
  }
9901
8336
 
8337
+ /**
8338
+ * Detects client browser, OS, and device information
8339
+ */
8340
+ function uaParse(sWindow) {
8341
+ const unknown = '-';
8342
+ // Screen detection
8343
+ let width = 0;
8344
+ let height = 0;
8345
+ let screenSize = '';
8346
+ if (sWindow.screen.width) {
8347
+ width = sWindow.screen.width;
8348
+ height = sWindow.screen.height;
8349
+ screenSize = `${width} x ${height}`;
8350
+ }
8351
+ // Browser detection
8352
+ const nVer = sWindow.navigator.appVersion ?? '0';
8353
+ const nAgt = sWindow.navigator.userAgent ?? 'unknown';
8354
+ let browser = sWindow.navigator.appName ?? "unknown";
8355
+ let version = String(parseFloat(nVer));
8356
+ let nameOffset;
8357
+ let verOffset;
8358
+ let ix;
8359
+ // Browser detection logic
8360
+ if ((verOffset = nAgt.indexOf('YaBrowser')) !== -1) {
8361
+ browser = 'Yandex';
8362
+ version = nAgt.substring(verOffset + 10);
8363
+ }
8364
+ else if ((verOffset = nAgt.indexOf('SamsungBrowser')) !== -1) {
8365
+ browser = 'Samsung';
8366
+ version = nAgt.substring(verOffset + 15);
8367
+ }
8368
+ else if ((verOffset = nAgt.indexOf('UCBrowser')) !== -1) {
8369
+ browser = 'UC Browser';
8370
+ version = nAgt.substring(verOffset + 10);
8371
+ }
8372
+ else if ((verOffset = nAgt.indexOf('OPR')) !== -1) {
8373
+ browser = 'Opera';
8374
+ version = nAgt.substring(verOffset + 4);
8375
+ }
8376
+ else if ((verOffset = nAgt.indexOf('Opera')) !== -1) {
8377
+ browser = 'Opera';
8378
+ version = nAgt.substring(verOffset + 6);
8379
+ if ((verOffset = nAgt.indexOf('Version')) !== -1) {
8380
+ version = nAgt.substring(verOffset + 8);
8381
+ }
8382
+ }
8383
+ else if ((verOffset = nAgt.indexOf('Edge')) !== -1) {
8384
+ browser = 'Microsoft Legacy Edge';
8385
+ version = nAgt.substring(verOffset + 5);
8386
+ }
8387
+ else if ((verOffset = nAgt.indexOf('Edg')) !== -1) {
8388
+ browser = 'Microsoft Edge';
8389
+ version = nAgt.substring(verOffset + 4);
8390
+ }
8391
+ else if ((verOffset = nAgt.indexOf('MSIE')) !== -1) {
8392
+ browser = 'Microsoft Internet Explorer';
8393
+ version = nAgt.substring(verOffset + 5);
8394
+ }
8395
+ else if ((verOffset = nAgt.indexOf('Chrome')) !== -1) {
8396
+ browser = 'Chrome';
8397
+ version = nAgt.substring(verOffset + 7);
8398
+ }
8399
+ else if ((verOffset = nAgt.indexOf('Safari')) !== -1) {
8400
+ browser = 'Safari';
8401
+ version = nAgt.substring(verOffset + 7);
8402
+ if ((verOffset = nAgt.indexOf('Version')) !== -1) {
8403
+ version = nAgt.substring(verOffset + 8);
8404
+ }
8405
+ }
8406
+ else if ((verOffset = nAgt.indexOf('Firefox')) !== -1) {
8407
+ browser = 'Firefox';
8408
+ version = nAgt.substring(verOffset + 8);
8409
+ }
8410
+ else if (nAgt.indexOf('Trident/') !== -1) {
8411
+ browser = 'Microsoft Internet Explorer';
8412
+ version = nAgt.substring(nAgt.indexOf('rv:') + 3);
8413
+ }
8414
+ else if ((nameOffset = nAgt.lastIndexOf(' ') + 1) < (verOffset = nAgt.lastIndexOf('/'))) {
8415
+ browser = nAgt.substring(nameOffset, verOffset);
8416
+ version = nAgt.substring(verOffset + 1);
8417
+ if (browser.toLowerCase() === browser.toUpperCase()) {
8418
+ browser = sWindow.navigator.appName;
8419
+ }
8420
+ }
8421
+ // Trim the version string
8422
+ if ((ix = version.indexOf(';')) !== -1) {
8423
+ version = version.substring(0, ix);
8424
+ }
8425
+ if ((ix = version.indexOf(' ')) !== -1) {
8426
+ version = version.substring(0, ix);
8427
+ }
8428
+ if ((ix = version.indexOf(')')) !== -1) {
8429
+ version = version.substring(0, ix);
8430
+ }
8431
+ let majorVersion = parseInt(version, 10);
8432
+ if (isNaN(majorVersion)) {
8433
+ version = String(parseFloat(nVer));
8434
+ majorVersion = parseInt(nVer, 10);
8435
+ }
8436
+ // Mobile detection
8437
+ const mobile = /Mobile|mini|Fennec|Android|iP(ad|od|hone)/.test(nVer);
8438
+ // Cookie detection
8439
+ let cookieEnabled = sWindow.navigator.cookieEnabled || false;
8440
+ if (typeof navigator.cookieEnabled === 'undefined' && !cookieEnabled) {
8441
+ sWindow.document.cookie = 'testcookie';
8442
+ cookieEnabled = sWindow.document.cookie.indexOf('testcookie') !== -1;
8443
+ }
8444
+ // OS detection
8445
+ let os = unknown;
8446
+ const clientStrings = [
8447
+ { s: 'Windows 10', r: /(Windows 10.0|Windows NT 10.0)/ },
8448
+ { s: 'Windows 8.1', r: /(Windows 8.1|Windows NT 6.3)/ },
8449
+ { s: 'Windows 8', r: /(Windows 8|Windows NT 6.2)/ },
8450
+ { s: 'Windows 7', r: /(Windows 7|Windows NT 6.1)/ },
8451
+ { s: 'Windows Vista', r: /Windows NT 6.0/ },
8452
+ { s: 'Windows Server 2003', r: /Windows NT 5.2/ },
8453
+ { s: 'Windows XP', r: /(Windows NT 5.1|Windows XP)/ },
8454
+ { s: 'Windows 2000', r: /(Windows NT 5.0|Windows 2000)/ },
8455
+ { s: 'Windows ME', r: /(Win 9x 4.90|Windows ME)/ },
8456
+ { s: 'Windows 98', r: /(Windows 98|Win98)/ },
8457
+ { s: 'Windows 95', r: /(Windows 95|Win95|Windows_95)/ },
8458
+ { s: 'Windows NT 4.0', r: /(Windows NT 4.0|WinNT4.0|WinNT|Windows NT)/ },
8459
+ { s: 'Windows CE', r: /Windows CE/ },
8460
+ { s: 'Windows 3.11', r: /Win16/ },
8461
+ { s: 'Android', r: /Android/ },
8462
+ { s: 'Open BSD', r: /OpenBSD/ },
8463
+ { s: 'Sun OS', r: /SunOS/ },
8464
+ { s: 'Chrome OS', r: /CrOS/ },
8465
+ { s: 'Linux', r: /(Linux|X11(?!.*CrOS))/ },
8466
+ { s: 'iOS', r: /(iPhone|iPad|iPod)/ },
8467
+ { s: 'Mac OS X', r: /Mac OS X/ },
8468
+ { s: 'Mac OS', r: /(Mac OS|MacPPC|MacIntel|Mac_PowerPC|Macintosh)/ },
8469
+ { s: 'QNX', r: /QNX/ },
8470
+ { s: 'UNIX', r: /UNIX/ },
8471
+ { s: 'BeOS', r: /BeOS/ },
8472
+ { s: 'OS/2', r: /OS\/2/ },
8473
+ {
8474
+ s: 'Search Bot',
8475
+ r: /(nuhk|Googlebot|Yammybot|Openbot|Slurp|MSNBot|Ask Jeeves\/Teoma|ia_archiver)/,
8476
+ },
8477
+ ];
8478
+ // Find matching OS
8479
+ for (const client of clientStrings) {
8480
+ if (client.r.test(nAgt)) {
8481
+ os = client.s;
8482
+ break;
8483
+ }
8484
+ }
8485
+ // OS Version detection
8486
+ let osVersion = unknown;
8487
+ if (/Windows/.test(os)) {
8488
+ const matches = /Windows (.*)/.exec(os);
8489
+ if (matches && matches[1]) {
8490
+ osVersion = matches[1];
8491
+ // Handle Windows 10/11 detection with newer API if available
8492
+ if (osVersion === '10' && 'userAgentData' in sWindow.navigator) {
8493
+ const nav = navigator;
8494
+ if (nav.userAgentData) {
8495
+ nav.userAgentData
8496
+ .getHighEntropyValues(['platformVersion'])
8497
+ .then((ua) => {
8498
+ const version = parseInt(ua.platformVersion.split('.')[0], 10);
8499
+ osVersion = version < 13 ? '10' : '11';
8500
+ })
8501
+ .catch(() => {
8502
+ // ignore errors and keep osVersion as is
8503
+ });
8504
+ }
8505
+ }
8506
+ }
8507
+ os = 'Windows';
8508
+ }
8509
+ // OS version detection for Mac/Android/iOS
8510
+ switch (os) {
8511
+ case 'Mac OS':
8512
+ case 'Mac OS X':
8513
+ case 'Android': {
8514
+ const matches = /(?:Android|Mac OS|Mac OS X|MacPPC|MacIntel|Mac_PowerPC|Macintosh) ([\.\_\d]+)/.exec(nAgt);
8515
+ osVersion = matches && matches[1] ? matches[1] : unknown;
8516
+ break;
8517
+ }
8518
+ case 'iOS': {
8519
+ const matches = /OS (\d+)_(\d+)_?(\d+)?/.exec(nVer);
8520
+ if (matches && matches[1]) {
8521
+ osVersion = `${matches[1]}.${matches[2]}.${parseInt(matches[3] || '0', 10)}`;
8522
+ }
8523
+ break;
8524
+ }
8525
+ }
8526
+ // Return client data
8527
+ return {
8528
+ screen: screenSize,
8529
+ width,
8530
+ height,
8531
+ browser,
8532
+ browserVersion: version,
8533
+ browserMajorVersion: majorVersion,
8534
+ mobile,
8535
+ os,
8536
+ osVersion,
8537
+ cookies: cookieEnabled,
8538
+ };
8539
+ }
8540
+ function isObject(item) {
8541
+ const isNull = item === null;
8542
+ return Boolean(item && typeof item === 'object' && !Array.isArray(item) && !isNull);
8543
+ }
8544
+ function getUTCOffsetString() {
8545
+ const date = new Date();
8546
+ const offsetMinutes = date.getTimezoneOffset();
8547
+ const hours = Math.abs(Math.floor(offsetMinutes / 60));
8548
+ const minutes = Math.abs(offsetMinutes % 60);
8549
+ const sign = offsetMinutes <= 0 ? '+' : '-';
8550
+ const hoursStr = hours.toString().padStart(2, '0');
8551
+ const minutesStr = minutes.toString().padStart(2, '0');
8552
+ return `UTC${sign}${hoursStr}:${minutesStr}`;
8553
+ }
8554
+
8555
+ const refKey = '$__or__initial_ref__$';
8556
+ const distinctIdKey = '$__or__distinct_device_id__$';
8557
+ const utmParamsKey = '$__or__utm_params__$';
8558
+ const superPropKey = '$__or__super_properties__$';
8559
+ const userIdKey = '$__or__user_id__$';
8560
+ const win = 'window' in globalThis
8561
+ ? window
8562
+ : {
8563
+ navigator: { userAgent: '' },
8564
+ screen: {},
8565
+ document: {
8566
+ cookie: '',
8567
+ },
8568
+ location: { search: '' },
8569
+ };
8570
+ const doc = 'document' in globalThis ? document : { referrer: '' };
8571
+ const searchEngineList = [
8572
+ 'google',
8573
+ 'bing',
8574
+ 'yahoo',
8575
+ 'baidu',
8576
+ 'yandex',
8577
+ 'duckduckgo',
8578
+ 'ecosia',
8579
+ 'ask',
8580
+ 'aol',
8581
+ 'wolframalpha',
8582
+ 'startpage',
8583
+ 'swisscows',
8584
+ 'qwant',
8585
+ 'lycos',
8586
+ 'dogpile',
8587
+ 'info',
8588
+ 'teoma',
8589
+ 'webcrawler',
8590
+ 'naver',
8591
+ 'seznam',
8592
+ 'perplexity',
8593
+ ];
8594
+ class ConstantProperties {
8595
+ constructor(localStorage, sessionStorage) {
8596
+ this.localStorage = localStorage;
8597
+ this.sessionStorage = sessionStorage;
8598
+ this.user_id = null;
8599
+ this.setUserId = (user_id) => {
8600
+ this.user_id = user_id;
8601
+ this.sessionStorage.setItem(userIdKey, user_id ?? '');
8602
+ };
8603
+ this.resetUserId = (hard) => {
8604
+ this.user_id = null;
8605
+ if (hard) {
8606
+ this.deviceId = this.getDistinctDeviceId(true);
8607
+ }
8608
+ };
8609
+ this.getDistinctDeviceId = (force) => {
8610
+ const potentialStored = this.localStorage.getItem(distinctIdKey);
8611
+ if (potentialStored && !force) {
8612
+ return potentialStored;
8613
+ }
8614
+ else {
8615
+ const distinctId = `${Math.random().toString(36).slice(2)}-${Math.random().toString(36).slice(2)}-${Math.random().toString(36).slice(2)}`;
8616
+ this.localStorage.setItem(distinctIdKey, distinctId);
8617
+ return distinctId;
8618
+ }
8619
+ };
8620
+ this.getReferrer = () => {
8621
+ const potentialStored = this.sessionStorage.getItem(refKey);
8622
+ if (potentialStored) {
8623
+ return potentialStored;
8624
+ }
8625
+ else {
8626
+ const ref = doc.referrer;
8627
+ this.sessionStorage.setItem(refKey, ref);
8628
+ return ref;
8629
+ }
8630
+ };
8631
+ this.parseUTM = () => {
8632
+ const potentialStored = this.sessionStorage.getItem(utmParamsKey);
8633
+ if (potentialStored) {
8634
+ const obj = JSON.parse(potentialStored);
8635
+ this.utmSource = obj.utm_source;
8636
+ this.utmMedium = obj.utm_medium;
8637
+ this.utmCampaign = obj.utm_campaign;
8638
+ }
8639
+ else {
8640
+ const searchParams = new URLSearchParams(win.location.search);
8641
+ this.utmSource = searchParams.get('utm_source') || null;
8642
+ this.utmMedium = searchParams.get('utm_medium') || null;
8643
+ this.utmCampaign = searchParams.get('utm_campaign') || null;
8644
+ const obj = {
8645
+ utm_source: this.utmSource,
8646
+ utm_medium: this.utmMedium,
8647
+ utm_campaign: this.utmCampaign,
8648
+ };
8649
+ this.sessionStorage.setItem(utmParamsKey, JSON.stringify(obj));
8650
+ }
8651
+ };
8652
+ this.getSearchEngine = (ref) => {
8653
+ for (const searchEngine of searchEngineList) {
8654
+ if (ref.includes(searchEngine)) {
8655
+ return searchEngine;
8656
+ }
8657
+ }
8658
+ return null;
8659
+ };
8660
+ this.getSuperProperties = () => {
8661
+ const potentialStored = this.localStorage.getItem(superPropKey);
8662
+ if (potentialStored) {
8663
+ return JSON.parse(potentialStored);
8664
+ }
8665
+ else {
8666
+ return {};
8667
+ }
8668
+ };
8669
+ this.saveSuperProperties = (props) => {
8670
+ this.localStorage.setItem(superPropKey, JSON.stringify(props));
8671
+ };
8672
+ this.clearSuperProperties = () => {
8673
+ this.localStorage.setItem(superPropKey, JSON.stringify({}));
8674
+ };
8675
+ const { width, height, browser, browserVersion, browserMajorVersion, os, osVersion, mobile } = uaParse(win);
8676
+ const storedUserId = this.sessionStorage.getItem(userIdKey);
8677
+ if (storedUserId) {
8678
+ this.user_id = storedUserId;
8679
+ }
8680
+ this.os = os;
8681
+ this.osVersion = osVersion;
8682
+ this.browser = `${browser}`;
8683
+ this.browserVersion = `${browserVersion} (${browserMajorVersion})`;
8684
+ this.platform = mobile ? 'mobile' : 'desktop';
8685
+ this.screenHeight = height;
8686
+ this.screenWidth = width;
8687
+ this.initialReferrer = this.getReferrer();
8688
+ this.deviceId = this.getDistinctDeviceId();
8689
+ this.searchEngine = this.getSearchEngine(this.initialReferrer);
8690
+ this.parseUTM();
8691
+ }
8692
+ get all() {
8693
+ return {
8694
+ os: this.os,
8695
+ os_version: this.osVersion,
8696
+ browser: this.browser,
8697
+ browser_version: this.browserVersion,
8698
+ platform: this.platform,
8699
+ screen_height: this.screenHeight,
8700
+ screen_width: this.screenWidth,
8701
+ initial_referrer: this.initialReferrer,
8702
+ utm_source: this.utmSource,
8703
+ utm_medium: this.utmMedium,
8704
+ utm_campaign: this.utmCampaign,
8705
+ user_id: this.user_id,
8706
+ distinct_id: this.deviceId,
8707
+ sdk_edition: 'web',
8708
+ sdk_version: '17.2.3',
8709
+ timezone: getUTCOffsetString(),
8710
+ search_engine: this.searchEngine,
8711
+ };
8712
+ }
8713
+ get defaultPropertyKeys() {
8714
+ return Object.keys(this.all);
8715
+ }
8716
+ get distinctId() {
8717
+ return this.deviceId;
8718
+ }
8719
+ }
8720
+
8721
+ const mutationTypes = {
8722
+ identity: 'identity',
8723
+ deleteUser: 'delete_user',
8724
+ setProperty: 'set_property',
8725
+ setPropertyOnce: 'set_property_once',
8726
+ appendProperty: 'append_property',
8727
+ appendUniqueProperty: 'append_unique_property',
8728
+ incrementProperty: 'increment_property',
8729
+ };
8730
+ const categories = {
8731
+ people: 'user_actions',
8732
+ events: 'events',
8733
+ };
8734
+ const createEvent = (category, type, timestamp, payload) => {
8735
+ if (category === categories.people) {
8736
+ return {
8737
+ category,
8738
+ data: {
8739
+ type,
8740
+ user_id: payload.user_id,
8741
+ payload: payload.properties,
8742
+ timestamp,
8743
+ },
8744
+ };
8745
+ }
8746
+ else {
8747
+ if (!payload) {
8748
+ throw new Error('Payload is required for event creation');
8749
+ }
8750
+ return {
8751
+ category,
8752
+ data: {
8753
+ name: payload.name,
8754
+ payload: payload.properties,
8755
+ timestamp,
8756
+ },
8757
+ };
8758
+ }
8759
+ };
8760
+
8761
+ const reservedProps = ['properties', 'token', 'timestamp'];
8762
+ class Events {
8763
+ constructor(constantProperties, getTimestamp, batcher) {
8764
+ this.constantProperties = constantProperties;
8765
+ this.getTimestamp = getTimestamp;
8766
+ this.batcher = batcher;
8767
+ this.ownProperties = {};
8768
+ /**
8769
+ * Add event to batch with option to send it immediately,
8770
+ * properties are optional and will not be saved as super prop
8771
+ * */
8772
+ this.sendEvent = (eventName, properties, options) => {
8773
+ // add properties
8774
+ const eventProps = {};
8775
+ if (properties) {
8776
+ if (!isObject(properties)) {
8777
+ throw new Error('Properties must be an object');
8778
+ }
8779
+ Object.entries(properties).forEach(([key, value]) => {
8780
+ if (!this.constantProperties.defaultPropertyKeys.includes(key)) {
8781
+ eventProps[key] = value;
8782
+ }
8783
+ });
8784
+ }
8785
+ const eventPayload = {
8786
+ name: eventName,
8787
+ properties: { ...this.ownProperties, ...eventProps },
8788
+ };
8789
+ const event = createEvent(categories.events, undefined, this.getTimestamp(), eventPayload);
8790
+ if (options?.send_immediately) {
8791
+ void this.batcher.sendImmediately(event);
8792
+ }
8793
+ else {
8794
+ this.batcher.addEvent(event);
8795
+ }
8796
+ };
8797
+ /**
8798
+ * creates super property for all events
8799
+ * */
8800
+ this.setProperty = (nameOrProperties, value) => {
8801
+ let changed = false;
8802
+ if (isObject(nameOrProperties)) {
8803
+ Object.entries(nameOrProperties).forEach(([key, val]) => {
8804
+ if (!this.constantProperties.defaultPropertyKeys.includes(key)) {
8805
+ this.ownProperties[key] = val;
8806
+ changed = true;
8807
+ }
8808
+ });
8809
+ }
8810
+ if (typeof nameOrProperties === 'string' && value !== undefined) {
8811
+ if (!this.constantProperties.defaultPropertyKeys.includes(nameOrProperties)) {
8812
+ this.ownProperties[nameOrProperties] = value;
8813
+ changed = true;
8814
+ }
8815
+ }
8816
+ if (changed) {
8817
+ this.constantProperties.saveSuperProperties(this.ownProperties);
8818
+ }
8819
+ };
8820
+ /**
8821
+ * set super property only if it doesn't exist yet
8822
+ * */
8823
+ this.setPropertiesOnce = (nameOrProperties, value) => {
8824
+ let changed = false;
8825
+ if (isObject(nameOrProperties)) {
8826
+ Object.entries(nameOrProperties).forEach(([key, val]) => {
8827
+ if (!this.ownProperties[key] && !reservedProps.includes(key)) {
8828
+ this.ownProperties[key] = val;
8829
+ changed = true;
8830
+ }
8831
+ });
8832
+ }
8833
+ if (typeof nameOrProperties === 'string' && value !== undefined) {
8834
+ if (!this.ownProperties[nameOrProperties] && !reservedProps.includes(nameOrProperties)) {
8835
+ this.ownProperties[nameOrProperties] = value;
8836
+ changed = true;
8837
+ }
8838
+ }
8839
+ if (changed) {
8840
+ this.constantProperties.saveSuperProperties(this.ownProperties);
8841
+ }
8842
+ };
8843
+ /**
8844
+ * removes properties from list of super properties
8845
+ * */
8846
+ this.unsetProperties = (properties) => {
8847
+ let changed = false;
8848
+ if (Array.isArray(properties)) {
8849
+ properties.forEach((key) => {
8850
+ if (this.ownProperties[key] && !reservedProps.includes(key)) {
8851
+ delete this.ownProperties[key];
8852
+ changed = true;
8853
+ }
8854
+ });
8855
+ }
8856
+ else if (this.ownProperties[properties] && !reservedProps.includes(properties)) {
8857
+ delete this.ownProperties[properties];
8858
+ changed = true;
8859
+ }
8860
+ if (changed) {
8861
+ this.constantProperties.saveSuperProperties(this.ownProperties);
8862
+ }
8863
+ };
8864
+ /** clears all super properties */
8865
+ this.reset = () => {
8866
+ this.ownProperties = {};
8867
+ this.constantProperties.clearSuperProperties();
8868
+ };
8869
+ /** mixpanel compatibility */
8870
+ this.register = this.setProperty;
8871
+ this.register_once = this.setPropertiesOnce;
8872
+ this.unregister = this.unsetProperties;
8873
+ this.track = this.sendEvent;
8874
+ this.ownProperties = this.constantProperties.getSuperProperties();
8875
+ }
8876
+ }
8877
+
8878
+ class People {
8879
+ constructor(constantProperties, getTimestamp, onId, batcher) {
8880
+ this.constantProperties = constantProperties;
8881
+ this.getTimestamp = getTimestamp;
8882
+ this.onId = onId;
8883
+ this.batcher = batcher;
8884
+ this.ownProperties = {};
8885
+ this.identify = (user_id, options) => {
8886
+ if (!user_id || typeof user_id !== 'string') {
8887
+ throw new Error('OR SDK: user_id (string) is required for .identify()');
8888
+ }
8889
+ // if user exists already, reset properties
8890
+ if (this.constantProperties.user_id && this.constantProperties.user_id !== user_id) {
8891
+ this.reset();
8892
+ }
8893
+ this.constantProperties.setUserId(user_id);
8894
+ if (!options?.fromTracker) {
8895
+ this.onId(user_id);
8896
+ }
8897
+ const identityEvent = createEvent(categories.people, mutationTypes.identity, this.getTimestamp(), { user_id });
8898
+ this.batcher.addEvent(identityEvent);
8899
+ };
8900
+ /** Resets user id and own properties
8901
+ *
8902
+ * !hard reset will destroy persistent device id!
8903
+ * */
8904
+ this.reset = (hard) => {
8905
+ this.constantProperties.resetUserId(hard);
8906
+ this.ownProperties = {};
8907
+ };
8908
+ /**
8909
+ * Will delete user and its data from backend, then reset all local properties
8910
+ */
8911
+ this.deleteUser = () => {
8912
+ const removedUser = this.constantProperties.user_id;
8913
+ if (!removedUser)
8914
+ return;
8915
+ this.constantProperties.setUserId(null);
8916
+ this.ownProperties = {};
8917
+ const deleteEvent = createEvent(categories.people, mutationTypes.deleteUser, undefined, {
8918
+ user_id: removedUser,
8919
+ });
8920
+ this.batcher.addEvent(deleteEvent);
8921
+ this.reset();
8922
+ };
8923
+ /**
8924
+ * set user properties, overwriting existing ones
8925
+ * */
8926
+ this.setProperties = (propertyOrObj, value) => {
8927
+ if (!propertyOrObj) {
8928
+ throw new Error('OR SDK: no user properties provided to set');
8929
+ }
8930
+ const properties = {};
8931
+ if (typeof propertyOrObj === 'string' && propertyOrObj && value) {
8932
+ properties[propertyOrObj] = value;
8933
+ }
8934
+ else if (isObject(propertyOrObj)) {
8935
+ Object.assign(properties, propertyOrObj);
8936
+ }
8937
+ else {
8938
+ throw new Error('OR SDK: invalid user properties provided to set');
8939
+ }
8940
+ Object.entries(properties).forEach(([key, value]) => {
8941
+ if (!this.constantProperties.defaultPropertyKeys.includes(key)) {
8942
+ this.ownProperties[key] = value;
8943
+ }
8944
+ });
8945
+ const setEvent = createEvent(categories.people, mutationTypes.setProperty, undefined, {
8946
+ user_id: this.user_id,
8947
+ properties,
8948
+ });
8949
+ this.batcher.addEvent(setEvent);
8950
+ };
8951
+ /**
8952
+ * Set property if it doesn't exist yet
8953
+ * */
8954
+ this.setPropertiesOnce = (properties) => {
8955
+ if (!isObject(properties)) {
8956
+ throw new Error('Properties must be an object');
8957
+ }
8958
+ Object.entries(properties).forEach(([key, value]) => {
8959
+ if (!this.constantProperties.defaultPropertyKeys.includes(key) && !this.ownProperties[key]) {
8960
+ this.ownProperties[key] = value;
8961
+ }
8962
+ });
8963
+ const setEvent = createEvent(categories.people, mutationTypes.setPropertyOnce, undefined, {
8964
+ user_id: this.user_id,
8965
+ properties,
8966
+ });
8967
+ this.batcher.addEvent(setEvent);
8968
+ };
8969
+ /**
8970
+ * Add value to property (will turn string prop into array)
8971
+ * */
8972
+ this.appendValues = (key, value) => {
8973
+ if (!this.constantProperties.defaultPropertyKeys.includes(key) && this.ownProperties[key]) {
8974
+ if (Array.isArray(this.ownProperties[key])) {
8975
+ this.ownProperties[key].push(value);
8976
+ }
8977
+ else {
8978
+ this.ownProperties[key] = [this.ownProperties[key], value];
8979
+ }
8980
+ }
8981
+ const appendEvent = createEvent(categories.people, mutationTypes.appendProperty, undefined, {
8982
+ properties: { [key]: value },
8983
+ user_id: this.user_id,
8984
+ });
8985
+ this.batcher.addEvent(appendEvent);
8986
+ };
8987
+ /**
8988
+ * Add unique values to property (will turn string prop into array)
8989
+ * */
8990
+ this.appendUniqueValues = (key, value) => {
8991
+ if (!this.ownProperties[key])
8992
+ return;
8993
+ if (Array.isArray(this.ownProperties[key])) {
8994
+ if (!this.ownProperties[key].includes(value)) {
8995
+ this.appendValues(key, value);
8996
+ }
8997
+ }
8998
+ else if (this.ownProperties[key] !== value) {
8999
+ this.appendValues(key, value);
9000
+ }
9001
+ const unionEvent = createEvent(categories.people, mutationTypes.appendUniqueProperty, undefined, {
9002
+ properties: { [key]: value },
9003
+ user_id: this.user_id,
9004
+ });
9005
+ this.batcher.addEvent(unionEvent);
9006
+ };
9007
+ /**
9008
+ * Adds value (incl. negative) to existing numerical property
9009
+ * */
9010
+ this.increment = (key, value) => {
9011
+ if (!this.ownProperties[key]) {
9012
+ this.ownProperties[key] = 0;
9013
+ }
9014
+ if (this.ownProperties[key] && typeof this.ownProperties[key] !== 'number') {
9015
+ throw new Error('OR SDK: Property must be a number to increment');
9016
+ }
9017
+ // @ts-ignore
9018
+ this.ownProperties[key] += value;
9019
+ const incrementEvent = createEvent(categories.people, mutationTypes.incrementProperty, undefined, {
9020
+ user_id: this.user_id,
9021
+ properties: { [key]: value },
9022
+ });
9023
+ this.batcher.addEvent(incrementEvent);
9024
+ };
9025
+ /** mixpanel compatibility */
9026
+ this.union = this.appendUniqueValues;
9027
+ this.set = this.setProperties;
9028
+ this.set_once = this.setPropertiesOnce;
9029
+ this.append = this.appendValues;
9030
+ this.incrementBy = this.increment;
9031
+ }
9032
+ get user_id() {
9033
+ return this.constantProperties.user_id;
9034
+ }
9035
+ }
9036
+
9037
+ /**
9038
+ * Creates batches of events, then sends them at intervals.
9039
+ */
9040
+ class Batcher {
9041
+ constructor(backendUrl, getToken, init) {
9042
+ this.backendUrl = backendUrl;
9043
+ this.getToken = getToken;
9044
+ this.init = init;
9045
+ this.autosendInterval = 5 * 1000;
9046
+ this.retryTimeout = 3 * 1000;
9047
+ this.retryLimit = 3;
9048
+ this.apiEdp = '/v1/sdk/i';
9049
+ this.batch = {
9050
+ [categories.people]: [],
9051
+ [categories.events]: [],
9052
+ };
9053
+ this.intervalId = null;
9054
+ }
9055
+ getBatches() {
9056
+ this.batch[categories.people] = this.dedupePeopleEvents();
9057
+ const finalData = { data: this.batch };
9058
+ return finalData;
9059
+ }
9060
+ addEvent(event) {
9061
+ this.batch[event.category].push(event.data);
9062
+ }
9063
+ sendImmediately(event) {
9064
+ this.sendBatch({ [event.category]: [event.data] });
9065
+ }
9066
+ /**
9067
+ *
9068
+ * Essentially we're dividing the batch by identify events and squash all same category events into one in each part,
9069
+ * taking priority to the last one
9070
+ */
9071
+ dedupePeopleEvents() {
9072
+ const peopleEvents = this.batch[categories.people];
9073
+ const finalEvents = [];
9074
+ const currentPart = [];
9075
+ for (let event of peopleEvents) {
9076
+ if (event.type === 'identity') {
9077
+ if (currentPart.length > 0) {
9078
+ finalEvents.push(...this.squashPeopleEvents(currentPart), event);
9079
+ currentPart.length = 0;
9080
+ }
9081
+ else {
9082
+ finalEvents.push(event);
9083
+ }
9084
+ }
9085
+ else {
9086
+ currentPart.push(event);
9087
+ }
9088
+ }
9089
+ if (currentPart.length > 0) {
9090
+ finalEvents.push(...this.squashPeopleEvents(currentPart));
9091
+ }
9092
+ return finalEvents;
9093
+ }
9094
+ squashPeopleEvents(events) {
9095
+ if (!events || events.length === 0) {
9096
+ return [];
9097
+ }
9098
+ const uniqueEventsByType = new Map();
9099
+ for (let event of events) {
9100
+ const prev = uniqueEventsByType.get(event.type);
9101
+ if (prev) {
9102
+ if (event.type === 'increment_property') {
9103
+ const previousValues = Object.entries(prev.payload);
9104
+ const currentValues = Object.entries(event.payload);
9105
+ const uniqueKeys = new Set([...previousValues.map(([key]) => key), ...currentValues.map(([key]) => key)]);
9106
+ const mergedPayload = {};
9107
+ uniqueKeys.forEach((key) => {
9108
+ const prevValue = typeof prev.payload[key] === 'number' ? prev.payload[key] : 0;
9109
+ const currValue = typeof event.payload[key] === 'number' ? event.payload[key] : 0;
9110
+ mergedPayload[key] = prevValue + currValue;
9111
+ });
9112
+ uniqueEventsByType.set(event.type, {
9113
+ type: event.type,
9114
+ timestamp: event.timestamp,
9115
+ payload: mergedPayload,
9116
+ });
9117
+ continue;
9118
+ }
9119
+ // merge payloads, taking priority to the latest one
9120
+ uniqueEventsByType.set(event.type, {
9121
+ type: event.type,
9122
+ timestamp: event.timestamp,
9123
+ payload: { ...(prev.payload ?? {}), ...(event.payload ?? {}) },
9124
+ });
9125
+ }
9126
+ else {
9127
+ uniqueEventsByType.set(event.type, event);
9128
+ }
9129
+ }
9130
+ return Array.from(uniqueEventsByType.values());
9131
+ }
9132
+ sendBatch(batch) {
9133
+ const sentBatch = batch;
9134
+ let attempts = 0;
9135
+ const send = () => {
9136
+ const token = this.getToken();
9137
+ if (!token) {
9138
+ return;
9139
+ }
9140
+ attempts++;
9141
+ return fetch(`${this.backendUrl}${this.apiEdp}`, {
9142
+ method: 'POST',
9143
+ headers: {
9144
+ 'Content-Type': 'application/json',
9145
+ Authorization: `Bearer ${token}`,
9146
+ },
9147
+ body: JSON.stringify(sentBatch),
9148
+ })
9149
+ .then((response) => {
9150
+ if ([403, 401].includes(response.status)) {
9151
+ if (attempts < this.retryLimit) {
9152
+ return this.init().then(() => {
9153
+ send();
9154
+ });
9155
+ }
9156
+ return;
9157
+ }
9158
+ if (!response.ok) {
9159
+ throw new Error(`HTTP error! status: ${response.status}`);
9160
+ }
9161
+ })
9162
+ .catch(() => {
9163
+ if (attempts < this.retryLimit) {
9164
+ setTimeout(() => void send(), this.retryTimeout);
9165
+ }
9166
+ });
9167
+ };
9168
+ void send();
9169
+ }
9170
+ startAutosend() {
9171
+ if (this.intervalId) {
9172
+ clearInterval(this.intervalId);
9173
+ }
9174
+ this.intervalId = setInterval(() => {
9175
+ this.flush();
9176
+ }, this.autosendInterval);
9177
+ }
9178
+ flush() {
9179
+ const categories = Object.keys(this.batch);
9180
+ const isEmpty = categories.every((category) => this.batch[category].length === 0);
9181
+ if (isEmpty) {
9182
+ return;
9183
+ }
9184
+ this.sendBatch(this.getBatches());
9185
+ categories.forEach((key) => {
9186
+ this.batch[key] = [];
9187
+ });
9188
+ }
9189
+ stop() {
9190
+ this.flush();
9191
+ if (this.intervalId) {
9192
+ clearInterval(this.intervalId);
9193
+ this.intervalId = null;
9194
+ }
9195
+ }
9196
+ }
9197
+
9198
+ const STORAGEKEY = '__or_sdk_analytics_token';
9199
+ class Analytics {
9200
+ /**
9201
+ * @param localStorage Class or Object that implements Storage-like interface that stores
9202
+ * values persistently like window.localStorage or any other file-based storage
9203
+ *
9204
+ * @param sessionStorage Class or Object that implements Storage-like interface that stores values
9205
+ * on per-session basis like window.sessionStorage or any other in-memory storage
9206
+ *
9207
+ * @param getToken Function that returns token to bind events to a session
9208
+ *
9209
+ * @param getTimestamp returns current timestamp
9210
+ *
9211
+ * @param setUserId callback for people.identify
9212
+ *
9213
+ * @param standalone if true, analytics will manage its own token (instead of using with openreplay tracker session)
9214
+ * */
9215
+ constructor(options) {
9216
+ this.token = null;
9217
+ this.standalone = false;
9218
+ this._getToken = () => {
9219
+ if (this.standalone) {
9220
+ return this.token;
9221
+ }
9222
+ else {
9223
+ return this.getToken();
9224
+ }
9225
+ };
9226
+ this._getTimestamp = () => {
9227
+ if (this.standalone) {
9228
+ return Date.now();
9229
+ }
9230
+ return this.getTimestamp();
9231
+ };
9232
+ this.init = async () => {
9233
+ if (!this.standalone) {
9234
+ this.batcher.startAutosend();
9235
+ return;
9236
+ }
9237
+ else {
9238
+ const defaultFields = this.constantProperties.all;
9239
+ const apiEdp = '/v1/sdk/start';
9240
+ const data = {
9241
+ projectKey: this.projectKey,
9242
+ defaultFields,
9243
+ };
9244
+ const resp = await fetch(apiEdp, {
9245
+ method: 'POST',
9246
+ body: JSON.stringify(data),
9247
+ });
9248
+ if (!resp.ok) {
9249
+ throw new Error(`HTTP error! status: ${resp.status}`);
9250
+ }
9251
+ const result = await resp.json();
9252
+ if (result.token) {
9253
+ this.token = result.token;
9254
+ this.sessionStorage.setItem(STORAGEKEY, result.token);
9255
+ }
9256
+ else {
9257
+ throw new Error('No token received from server');
9258
+ }
9259
+ }
9260
+ };
9261
+ this.reset = () => {
9262
+ this.people.reset(true);
9263
+ this.events.reset();
9264
+ this.batcher.stop();
9265
+ if (this.standalone) {
9266
+ this.token = null;
9267
+ this.sessionStorage.setItem(STORAGEKEY, '');
9268
+ }
9269
+ };
9270
+ /**
9271
+ * COMPATIBILITY LAYER
9272
+ * */
9273
+ /**
9274
+ * Identify a user with an id (e.g. email, username, etc.)
9275
+ * will bind all events and properties (including device_id) to this user
9276
+ *
9277
+ * you will need to manually call people.reset() to clear the id on logout event
9278
+ * */
9279
+ this.identify = (user_id) => {
9280
+ return this.people.identify(user_id);
9281
+ };
9282
+ /**
9283
+ * Add event to batch with option to send it immediately,
9284
+ * properties are optional and will not be saved as super prop
9285
+ * */
9286
+ this.track = (eventName, properties, options) => {
9287
+ return this.events.track(eventName, properties, options);
9288
+ };
9289
+ this.sessionStorage = options.sessionStorage || sessionStorage;
9290
+ this.localStorage = options.localStorage || localStorage;
9291
+ this.backendUrl = options.ingestPoint;
9292
+ this.projectKey = options.projectKey;
9293
+ this.getToken = options.getToken || (() => '');
9294
+ this.getTimestamp = options.getTimestamp || (() => Date.now());
9295
+ this.setUserId = options.setUserId || (() => { });
9296
+ this.standalone = !options.notStandalone;
9297
+ this.token = this.sessionStorage.getItem(STORAGEKEY);
9298
+ this.constantProperties = new ConstantProperties(this.localStorage, this.sessionStorage);
9299
+ this.batcher = new Batcher(this.backendUrl, this._getToken, this.init);
9300
+ this.events = new Events(this.constantProperties, this._getTimestamp, this.batcher);
9301
+ this.people = new People(this.constantProperties, this._getTimestamp, this.setUserId, this.batcher);
9302
+ if (options.notStandalone) {
9303
+ this.init();
9304
+ }
9305
+ }
9306
+ }
9307
+
9902
9308
  const Messages = _Messages;
9903
9309
  const DOCS_SETUP = '/en/sdk';
9904
9310
  function processOptions(obj) {
@@ -9940,6 +9346,7 @@ class API {
9940
9346
  constructor(options) {
9941
9347
  this.options = options;
9942
9348
  this.app = null;
9349
+ this.analytics = null;
9943
9350
  this.crossdomainMode = false;
9944
9351
  this.checkDoNotTrack = () => {
9945
9352
  return (this.options.respectDoNotTrack &&
@@ -9950,7 +9357,7 @@ class API {
9950
9357
  this.signalStartIssue = (reason, missingApi) => {
9951
9358
  const doNotTrack = this.checkDoNotTrack();
9952
9359
  console.log("Tracker couldn't start due to:", JSON.stringify({
9953
- trackerVersion: '16.4.11-beta.0',
9360
+ trackerVersion: '17.2.3',
9954
9361
  projectKey: this.options.projectKey,
9955
9362
  doNotTrack,
9956
9363
  reason: missingApi.length ? `missing api: ${missingApi.join(',')}` : reason,
@@ -9962,6 +9369,24 @@ class API {
9962
9369
  }
9963
9370
  this.app.restartCanvasTracking();
9964
9371
  };
9372
+ this.getSessionURL = (options) => {
9373
+ if (this.app === null) {
9374
+ return undefined;
9375
+ }
9376
+ return this.app.getSessionURL(options);
9377
+ };
9378
+ this.setUserID = (id) => {
9379
+ if (typeof id === 'string' && this.app !== null) {
9380
+ this.app.session.setUserID(id);
9381
+ this.analytics?.people.identify(id, { fromTracker: true });
9382
+ }
9383
+ };
9384
+ this.identify = this.setUserID;
9385
+ this.track = this.analytics?.track;
9386
+ this.userID = (id) => {
9387
+ deprecationWarn("'userID' method", "'setUserID' method", '/');
9388
+ this.setUserID(id);
9389
+ };
9965
9390
  this.handleError = (e, metadata = {}) => {
9966
9391
  if (this.app === null) {
9967
9392
  return;
@@ -9984,6 +9409,21 @@ class API {
9984
9409
  }
9985
9410
  this.app.send(Incident(options.label ?? '', options.startTime, options.endTime ?? options.startTime));
9986
9411
  };
9412
+ this.analyticsToken = null;
9413
+ /**
9414
+ * Use custom token for analytics events without session recording
9415
+ * */
9416
+ this.setAnalyticsToken = (token) => {
9417
+ this.analyticsToken = token;
9418
+ };
9419
+ this.getAnalyticsToken = () => {
9420
+ if (this.analyticsToken) {
9421
+ return this.analyticsToken;
9422
+ }
9423
+ else {
9424
+ return this.app?.session.getSessionToken() ?? '';
9425
+ }
9426
+ };
9987
9427
  this.crossdomainMode = Boolean(inIframe() && options.crossdomain?.enabled);
9988
9428
  if (!IN_BROWSER || !processOptions(options)) {
9989
9429
  return;
@@ -10042,6 +9482,34 @@ class API {
10042
9482
  }
10043
9483
  const app = new App(options.projectKey, options.sessionToken, options, this.signalStartIssue, this.crossdomainMode);
10044
9484
  this.app = app;
9485
+ if (options.projectKey && options.analytics?.active) {
9486
+ let isSaas = false;
9487
+ if (!options.ingestPoint) {
9488
+ isSaas = true;
9489
+ }
9490
+ if (options.ingestPoint) {
9491
+ const saasHost = 'api.openreplay.com';
9492
+ const urlObj = new URL(options.ingestPoint);
9493
+ if (urlObj.hostname === saasHost) {
9494
+ isSaas = true;
9495
+ }
9496
+ }
9497
+ const defaultEdp = 'https://api.openreplay.com/ingest';
9498
+ this.analytics = new Analytics({
9499
+ localStorage: options.localStorage ?? localStorage,
9500
+ sessionStorage: options.sessionStorage ?? sessionStorage,
9501
+ getToken: () => this.getAnalyticsToken(),
9502
+ getTimestamp: () => this.app?.timestamp() ?? Date.now(),
9503
+ setUserId: (id) => {
9504
+ this.app?.session.setUserID(id);
9505
+ },
9506
+ notStandalone: true,
9507
+ ingestPoint: isSaas
9508
+ ? defaultEdp
9509
+ : (options.analytics?.ingestPoint ?? options.ingestPoint ?? defaultEdp),
9510
+ projectKey: options.projectKey,
9511
+ });
9512
+ }
10045
9513
  if (!this.crossdomainMode) {
10046
9514
  // no need to send iframe viewport data since its a node for us
10047
9515
  Viewport(app, options.urls);
@@ -10052,7 +9520,7 @@ class API {
10052
9520
  // no tabs in iframes yet
10053
9521
  Tabs(app);
10054
9522
  }
10055
- Mouse(app, options.mouse);
9523
+ Mouse(app, { ...options.mouse, customAttributes: options.customAttributes });
10056
9524
  // inside iframe, we ignore viewport scroll
10057
9525
  Scroll(app, this.crossdomainMode);
10058
9526
  CSSRules(app, options.css);
@@ -10072,9 +9540,6 @@ class API {
10072
9540
  selection(app);
10073
9541
  webAnimations(app, options.webAnimations);
10074
9542
  window.__OPENREPLAY__ = this;
10075
- if (options.flags && options.flags.onFlagsLoad) {
10076
- this.onFlagsLoad(options.flags.onFlagsLoad);
10077
- }
10078
9543
  const wOpen = window.open;
10079
9544
  if (options.autoResetOnWindowOpen || options.resetTabOnWindowOpen) {
10080
9545
  app.attachStartCallback(() => {
@@ -10097,24 +9562,6 @@ class API {
10097
9562
  });
10098
9563
  }
10099
9564
  }
10100
- isFlagEnabled(flagName) {
10101
- return this.featureFlags.isFlagEnabled(flagName);
10102
- }
10103
- onFlagsLoad(callback) {
10104
- this.app?.featureFlags.onFlagsLoad(callback);
10105
- }
10106
- clearPersistFlags() {
10107
- this.app?.featureFlags.clearPersistFlags();
10108
- }
10109
- reloadFlags() {
10110
- return this.app?.featureFlags.reloadFlags();
10111
- }
10112
- getFeatureFlag(flagName) {
10113
- return this.app?.featureFlags.getFeatureFlag(flagName);
10114
- }
10115
- getAllFeatureFlags() {
10116
- return this.app?.featureFlags.flags;
10117
- }
10118
9565
  use(fn) {
10119
9566
  return fn(this.app, this.options);
10120
9567
  }
@@ -10142,6 +9589,10 @@ class API {
10142
9589
  if (this.app === null) {
10143
9590
  return Promise.reject("Browser doesn't support required api, or doNotTrack is active.");
10144
9591
  }
9592
+ if (startOpts?.userID) {
9593
+ this.app.session.setUserID(startOpts.userID);
9594
+ this.analytics?.people.identify(startOpts.userID, { fromTracker: true });
9595
+ }
10145
9596
  return this.app.start(startOpts);
10146
9597
  }
10147
9598
  else {
@@ -10245,31 +9696,10 @@ class API {
10245
9696
  }
10246
9697
  return this.app.getTabId();
10247
9698
  }
10248
- getUxId() {
10249
- if (this.app === null) {
10250
- return null;
10251
- }
10252
- return this.app.getUxtId();
10253
- }
10254
9699
  sessionID() {
10255
9700
  deprecationWarn("'sessionID' method", "'getSessionID' method", '/');
10256
9701
  return this.getSessionID();
10257
9702
  }
10258
- getSessionURL(options) {
10259
- if (this.app === null) {
10260
- return undefined;
10261
- }
10262
- return this.app.getSessionURL(options);
10263
- }
10264
- setUserID(id) {
10265
- if (typeof id === 'string' && this.app !== null) {
10266
- this.app.session.setUserID(id);
10267
- }
10268
- }
10269
- userID(id) {
10270
- deprecationWarn("'userID' method", "'setUserID' method", '/');
10271
- this.setUserID(id);
10272
- }
10273
9703
  setUserAnonymousID(id) {
10274
9704
  if (typeof id === 'string' && this.app !== null) {
10275
9705
  this.app.send(UserAnonymousID(id));
@@ -10294,6 +9724,9 @@ class API {
10294
9724
  return this.issue(key, payload);
10295
9725
  }
10296
9726
  else {
9727
+ if (!payload || typeof payload === 'string') {
9728
+ return this.app.send(CustomEvent(key, payload));
9729
+ }
10297
9730
  try {
10298
9731
  if ('or_timestamp' in payload) {
10299
9732
  const startTs = this.getSessionInfo()?.timestamp ?? 0;
@@ -10304,9 +9737,7 @@ class API {
10304
9737
  }
10305
9738
  payload = JSON.stringify(payload);
10306
9739
  }
10307
- catch (e) {
10308
- return;
10309
- }
9740
+ catch (_) { }
10310
9741
  this.app.send(CustomEvent(key, payload));
10311
9742
  }
10312
9743
  }
@@ -10328,6 +9759,13 @@ class TrackerSingleton {
10328
9759
  constructor() {
10329
9760
  this.instance = null;
10330
9761
  this.isConfigured = false;
9762
+ this.identify = this.setUserID;
9763
+ this.track = (eventName, properties, options) => {
9764
+ if (!IN_BROWSER || !this.ensureConfigured() || !this.instance) {
9765
+ return;
9766
+ }
9767
+ this.instance.track?.(eventName, properties);
9768
+ };
10331
9769
  }
10332
9770
  /**
10333
9771
  * Call this method once to create tracker configuration
@@ -10378,6 +9816,15 @@ class TrackerSingleton {
10378
9816
  }
10379
9817
  this.instance.setUserID(id);
10380
9818
  }
9819
+ get analytics() {
9820
+ if (this.instance?.analytics) {
9821
+ return this.instance.analytics;
9822
+ }
9823
+ else {
9824
+ return null;
9825
+ }
9826
+ }
9827
+ ;
10381
9828
  /**
10382
9829
  * Set metadata for the current session
10383
9830
  *
@@ -10430,42 +9877,6 @@ class TrackerSingleton {
10430
9877
  }
10431
9878
  this.instance.handleError(e, metadata);
10432
9879
  }
10433
- isFlagEnabled(flagName) {
10434
- if (!IN_BROWSER || !this.ensureConfigured() || !this.instance) {
10435
- return false;
10436
- }
10437
- return this.instance.isFlagEnabled(flagName);
10438
- }
10439
- onFlagsLoad(...args) {
10440
- if (!IN_BROWSER || !this.ensureConfigured() || !this.instance) {
10441
- return;
10442
- }
10443
- this.instance.onFlagsLoad(...args);
10444
- }
10445
- clearPersistFlags() {
10446
- if (!IN_BROWSER || !this.ensureConfigured() || !this.instance) {
10447
- return;
10448
- }
10449
- this.instance.clearPersistFlags();
10450
- }
10451
- reloadFlags() {
10452
- if (!IN_BROWSER || !this.ensureConfigured() || !this.instance) {
10453
- return;
10454
- }
10455
- return this.instance.reloadFlags();
10456
- }
10457
- getFeatureFlag(flagName) {
10458
- if (!IN_BROWSER || !this.ensureConfigured() || !this.instance) {
10459
- return;
10460
- }
10461
- return this.instance.getFeatureFlag(flagName);
10462
- }
10463
- getAllFeatureFlags() {
10464
- if (!IN_BROWSER || !this.ensureConfigured() || !this.instance) {
10465
- return;
10466
- }
10467
- return this.instance.getAllFeatureFlags();
10468
- }
10469
9880
  restartCanvasTracking() {
10470
9881
  if (!IN_BROWSER || !this.ensureConfigured() || !this.instance) {
10471
9882
  return;
@@ -10588,17 +9999,13 @@ class TrackerSingleton {
10588
9999
  }
10589
10000
  return this.instance.getTabId();
10590
10001
  }
10591
- getUxId() {
10592
- if (!IN_BROWSER || !this.ensureConfigured() || !this.instance) {
10593
- return null;
10594
- }
10595
- return this.instance.getUxId();
10596
- }
10597
10002
  }
10598
10003
  const tracker = new TrackerSingleton();
10599
10004
 
10005
+ exports.Analytics = Analytics;
10600
10006
  exports.App = App;
10601
10007
  exports.Messages = Messages;
10602
10008
  exports.default = API;
10009
+ exports.openReplay = tracker;
10603
10010
  exports.tracker = tracker;
10604
10011
  //# sourceMappingURL=entry.js.map