@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.
- package/dist/cjs/common/messages.gen.d.ts +1 -44
- package/dist/cjs/entry.js +1584 -2177
- package/dist/cjs/entry.js.map +1 -1
- package/dist/cjs/index.js +1566 -2135
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/main/app/canvas.d.ts +11 -0
- package/dist/cjs/main/app/index.d.ts +2 -7
- package/dist/cjs/main/app/messages.gen.d.ts +0 -5
- package/dist/cjs/main/app/nodes/idSeq.d.ts +14 -0
- package/dist/cjs/main/app/nodes/index.d.ts +2 -1
- package/dist/cjs/main/app/observer/top_observer.d.ts +2 -1
- package/dist/cjs/main/app/session.d.ts +1 -0
- package/dist/cjs/main/entry.d.ts +5 -4
- package/dist/cjs/main/index.d.ts +22 -17
- package/dist/cjs/main/modules/analytics/batcher.d.ts +45 -0
- package/dist/cjs/main/modules/analytics/constantProperties.d.ts +53 -0
- package/dist/cjs/main/modules/analytics/demo.d.ts +0 -0
- package/dist/cjs/main/modules/analytics/events.d.ts +37 -0
- package/dist/cjs/main/modules/analytics/index.d.ts +73 -0
- package/dist/cjs/main/modules/analytics/people.d.ts +51 -0
- package/dist/cjs/main/modules/analytics/types.d.ts +32 -0
- package/dist/cjs/main/modules/analytics/utils.d.ts +19 -0
- package/dist/cjs/main/modules/conditionsManager.d.ts +6 -1
- package/dist/cjs/main/modules/input.d.ts +2 -1
- package/dist/cjs/main/modules/mouse.d.ts +1 -0
- package/dist/cjs/main/modules/viewport.d.ts +7 -0
- package/dist/cjs/main/singleton.d.ts +5 -7
- package/dist/cjs/main/utils.d.ts +2 -0
- package/dist/lib/common/messages.gen.d.ts +1 -44
- package/dist/lib/entry.js +1583 -2178
- package/dist/lib/entry.js.map +1 -1
- package/dist/lib/index.js +1566 -2135
- package/dist/lib/index.js.map +1 -1
- package/dist/lib/main/app/canvas.d.ts +11 -0
- package/dist/lib/main/app/index.d.ts +2 -7
- package/dist/lib/main/app/messages.gen.d.ts +0 -5
- package/dist/lib/main/app/nodes/idSeq.d.ts +14 -0
- package/dist/lib/main/app/nodes/index.d.ts +2 -1
- package/dist/lib/main/app/observer/top_observer.d.ts +2 -1
- package/dist/lib/main/app/session.d.ts +1 -0
- package/dist/lib/main/entry.d.ts +5 -4
- package/dist/lib/main/index.d.ts +22 -17
- package/dist/lib/main/modules/analytics/batcher.d.ts +45 -0
- package/dist/lib/main/modules/analytics/constantProperties.d.ts +53 -0
- package/dist/lib/main/modules/analytics/demo.d.ts +0 -0
- package/dist/lib/main/modules/analytics/events.d.ts +37 -0
- package/dist/lib/main/modules/analytics/index.d.ts +73 -0
- package/dist/lib/main/modules/analytics/people.d.ts +51 -0
- package/dist/lib/main/modules/analytics/types.d.ts +32 -0
- package/dist/lib/main/modules/analytics/utils.d.ts +19 -0
- package/dist/lib/main/modules/conditionsManager.d.ts +6 -1
- package/dist/lib/main/modules/input.d.ts +2 -1
- package/dist/lib/main/modules/mouse.d.ts +1 -0
- package/dist/lib/main/modules/viewport.d.ts +7 -0
- package/dist/lib/main/singleton.d.ts +5 -7
- package/dist/lib/main/utils.d.ts +2 -0
- package/dist/types/common/messages.gen.d.ts +1 -44
- package/dist/types/main/app/canvas.d.ts +11 -0
- package/dist/types/main/app/index.d.ts +2 -7
- package/dist/types/main/app/messages.gen.d.ts +0 -5
- package/dist/types/main/app/nodes/idSeq.d.ts +14 -0
- package/dist/types/main/app/nodes/index.d.ts +2 -1
- package/dist/types/main/app/observer/top_observer.d.ts +2 -1
- package/dist/types/main/app/session.d.ts +1 -0
- package/dist/types/main/entry.d.ts +5 -4
- package/dist/types/main/index.d.ts +22 -17
- package/dist/types/main/modules/analytics/batcher.d.ts +45 -0
- package/dist/types/main/modules/analytics/constantProperties.d.ts +53 -0
- package/dist/types/main/modules/analytics/demo.d.ts +0 -0
- package/dist/types/main/modules/analytics/events.d.ts +37 -0
- package/dist/types/main/modules/analytics/index.d.ts +73 -0
- package/dist/types/main/modules/analytics/people.d.ts +51 -0
- package/dist/types/main/modules/analytics/types.d.ts +32 -0
- package/dist/types/main/modules/analytics/utils.d.ts +19 -0
- package/dist/types/main/modules/conditionsManager.d.ts +6 -1
- package/dist/types/main/modules/input.d.ts +2 -1
- package/dist/types/main/modules/mouse.d.ts +1 -0
- package/dist/types/main/modules/viewport.d.ts +7 -0
- package/dist/types/main/singleton.d.ts +5 -7
- package/dist/types/main/utils.d.ts +2 -0
- package/package.json +1 -1
- package/dist/cjs/main/modules/featureFlags.d.ts +0 -25
- package/dist/cjs/main/modules/userTesting/SignalManager.d.ts +0 -29
- package/dist/cjs/main/modules/userTesting/dnd.d.ts +0 -1
- package/dist/cjs/main/modules/userTesting/index.d.ts +0 -45
- package/dist/cjs/main/modules/userTesting/recorder.d.ts +0 -24
- package/dist/cjs/main/modules/userTesting/styles.d.ts +0 -277
- package/dist/cjs/main/modules/userTesting/utils.d.ts +0 -9
- package/dist/lib/main/modules/featureFlags.d.ts +0 -25
- package/dist/lib/main/modules/userTesting/SignalManager.d.ts +0 -29
- package/dist/lib/main/modules/userTesting/dnd.d.ts +0 -1
- package/dist/lib/main/modules/userTesting/index.d.ts +0 -45
- package/dist/lib/main/modules/userTesting/recorder.d.ts +0 -24
- package/dist/lib/main/modules/userTesting/styles.d.ts +0 -277
- package/dist/lib/main/modules/userTesting/utils.d.ts +0 -9
- package/dist/types/main/modules/featureFlags.d.ts +0 -25
- package/dist/types/main/modules/userTesting/SignalManager.d.ts +0 -29
- package/dist/types/main/modules/userTesting/dnd.d.ts +0 -1
- package/dist/types/main/modules/userTesting/index.d.ts +0 -45
- package/dist/types/main/modules/userTesting/recorder.d.ts +0 -24
- package/dist/types/main/modules/userTesting/styles.d.ts +0 -277
- 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
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
}
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
|
|
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
|
-
|
|
2697
|
-
|
|
2698
|
-
class Recorder {
|
|
2699
|
-
constructor(app) {
|
|
1634
|
+
class CanvasRecorder {
|
|
1635
|
+
constructor(app, options) {
|
|
2700
1636
|
this.app = app;
|
|
2701
|
-
this.
|
|
2702
|
-
this.
|
|
2703
|
-
this.
|
|
2704
|
-
this.
|
|
2705
|
-
|
|
2706
|
-
|
|
2707
|
-
this.
|
|
2708
|
-
|
|
2709
|
-
|
|
2710
|
-
|
|
2711
|
-
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
|
|
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
|
-
|
|
2757
|
-
|
|
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
|
-
|
|
2761
|
-
|
|
2762
|
-
|
|
2763
|
-
|
|
2764
|
-
|
|
2765
|
-
|
|
2766
|
-
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
|
|
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 (
|
|
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
|
|
3522
|
-
|
|
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
|
-
|
|
1721
|
+
this.cleanupCanvas(id);
|
|
3526
1722
|
return;
|
|
3527
1723
|
}
|
|
3528
|
-
if (!
|
|
3529
|
-
this.app.debug.log('Canvas element not in sync',
|
|
3530
|
-
|
|
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
|
-
|
|
3534
|
-
if (
|
|
3535
|
-
|
|
3536
|
-
|
|
3537
|
-
|
|
3538
|
-
|
|
3539
|
-
|
|
3540
|
-
|
|
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.
|
|
1740
|
+
this.intervals.set(id, int);
|
|
3547
1741
|
};
|
|
3548
|
-
this.fileExt =
|
|
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
|
-
|
|
3562
|
-
|
|
3563
|
-
|
|
3564
|
-
|
|
3565
|
-
|
|
3566
|
-
|
|
3567
|
-
|
|
3568
|
-
|
|
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
|
-
|
|
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
|
|
3624
|
-
const
|
|
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 =
|
|
3627
|
-
link.download =
|
|
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
|
-
|
|
3800
|
-
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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(
|
|
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(
|
|
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}
|
|
5233
|
-
this.app.sessionStorage.setItem(this.options.session_token_key, `${token}
|
|
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.
|
|
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 = '
|
|
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.
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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:
|
|
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
|
-
|
|
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
|
-
|
|
6014
|
-
|
|
6015
|
-
|
|
6016
|
-
|
|
6017
|
-
|
|
6018
|
-
|
|
6019
|
-
|
|
6020
|
-
|
|
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,
|
|
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:
|
|
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,
|
|
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
|
|
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 (
|
|
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 = () =>
|
|
6820
|
-
|
|
6821
|
-
|
|
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
|
-
|
|
7352
|
-
if (
|
|
7353
|
-
|
|
7354
|
-
|
|
7355
|
-
|
|
7356
|
-
|
|
7357
|
-
|
|
7358
|
-
|
|
7359
|
-
|
|
7360
|
-
|
|
7361
|
-
|
|
7362
|
-
|
|
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
|
|
8058
|
-
if (
|
|
8059
|
-
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
|
|
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: '
|
|
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 (
|
|
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
|