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