@openreplay/tracker 16.4.10 → 17.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/common/messages.gen.d.ts +57 -7
- package/dist/cjs/entry.js +564 -1453
- package/dist/cjs/entry.js.map +1 -1
- package/dist/cjs/index.js +526 -1373
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/main/app/guards.d.ts +1 -0
- package/dist/cjs/main/app/index.d.ts +5 -12
- package/dist/cjs/main/app/messages.gen.d.ts +7 -2
- package/dist/cjs/main/app/observer/observer.d.ts +4 -0
- package/dist/cjs/main/app/observer/top_observer.d.ts +3 -1
- package/dist/cjs/main/index.d.ts +9 -13
- package/dist/cjs/main/modules/conditionsManager.d.ts +6 -1
- package/dist/cjs/main/modules/longAnimationTask.d.ts +25 -0
- package/dist/cjs/main/modules/tagWatcher.d.ts +1 -1
- package/dist/cjs/main/modules/webAnimations.d.ts +9 -0
- package/dist/cjs/main/singleton.d.ts +0 -7
- package/dist/cjs/main/utils.d.ts +3 -0
- package/dist/lib/common/messages.gen.d.ts +57 -7
- package/dist/lib/entry.js +564 -1453
- package/dist/lib/entry.js.map +1 -1
- package/dist/lib/index.js +526 -1373
- package/dist/lib/index.js.map +1 -1
- package/dist/lib/main/app/guards.d.ts +1 -0
- package/dist/lib/main/app/index.d.ts +5 -12
- package/dist/lib/main/app/messages.gen.d.ts +7 -2
- package/dist/lib/main/app/observer/observer.d.ts +4 -0
- package/dist/lib/main/app/observer/top_observer.d.ts +3 -1
- package/dist/lib/main/index.d.ts +9 -13
- package/dist/lib/main/modules/conditionsManager.d.ts +6 -1
- package/dist/lib/main/modules/longAnimationTask.d.ts +25 -0
- package/dist/lib/main/modules/tagWatcher.d.ts +1 -1
- package/dist/lib/main/modules/webAnimations.d.ts +9 -0
- package/dist/lib/main/singleton.d.ts +0 -7
- package/dist/lib/main/utils.d.ts +3 -0
- package/dist/types/common/messages.gen.d.ts +57 -7
- package/dist/types/main/app/guards.d.ts +1 -0
- package/dist/types/main/app/index.d.ts +5 -12
- package/dist/types/main/app/messages.gen.d.ts +7 -2
- package/dist/types/main/app/observer/observer.d.ts +4 -0
- package/dist/types/main/app/observer/top_observer.d.ts +3 -1
- package/dist/types/main/index.d.ts +9 -13
- package/dist/types/main/modules/conditionsManager.d.ts +6 -1
- package/dist/types/main/modules/longAnimationTask.d.ts +25 -0
- package/dist/types/main/modules/tagWatcher.d.ts +1 -1
- package/dist/types/main/modules/webAnimations.d.ts +9 -0
- package/dist/types/main/singleton.d.ts +0 -7
- package/dist/types/main/utils.d.ts +3 -0
- package/package.json +13 -13
- package/dist/cjs/main/modules/featureFlags.d.ts +0 -25
- package/dist/cjs/main/modules/userTesting/SignalManager.d.ts +0 -29
- package/dist/cjs/main/modules/userTesting/dnd.d.ts +0 -1
- package/dist/cjs/main/modules/userTesting/index.d.ts +0 -45
- package/dist/cjs/main/modules/userTesting/recorder.d.ts +0 -24
- package/dist/cjs/main/modules/userTesting/styles.d.ts +0 -277
- package/dist/cjs/main/modules/userTesting/utils.d.ts +0 -9
- package/dist/lib/main/modules/featureFlags.d.ts +0 -25
- package/dist/lib/main/modules/userTesting/SignalManager.d.ts +0 -29
- package/dist/lib/main/modules/userTesting/dnd.d.ts +0 -1
- package/dist/lib/main/modules/userTesting/index.d.ts +0 -45
- package/dist/lib/main/modules/userTesting/recorder.d.ts +0 -24
- package/dist/lib/main/modules/userTesting/styles.d.ts +0 -277
- package/dist/lib/main/modules/userTesting/utils.d.ts +0 -9
- package/dist/types/main/modules/featureFlags.d.ts +0 -25
- package/dist/types/main/modules/userTesting/SignalManager.d.ts +0 -29
- package/dist/types/main/modules/userTesting/dnd.d.ts +0 -1
- package/dist/types/main/modules/userTesting/index.d.ts +0 -45
- package/dist/types/main/modules/userTesting/recorder.d.ts +0 -24
- package/dist/types/main/modules/userTesting/styles.d.ts +0 -277
- package/dist/types/main/modules/userTesting/utils.d.ts +0 -9
package/dist/cjs/entry.js
CHANGED
|
@@ -1114,93 +1114,6 @@ const mapCondition = (condition) => {
|
|
|
1114
1114
|
return con;
|
|
1115
1115
|
};
|
|
1116
1116
|
|
|
1117
|
-
class FeatureFlags {
|
|
1118
|
-
constructor(app) {
|
|
1119
|
-
this.app = app;
|
|
1120
|
-
this.flags = [];
|
|
1121
|
-
this.storageKey = '__openreplay_flags';
|
|
1122
|
-
const persistFlags = this.app.sessionStorage.getItem(this.storageKey);
|
|
1123
|
-
if (persistFlags) {
|
|
1124
|
-
const persistFlagsStrArr = persistFlags.split(';').filter(Boolean);
|
|
1125
|
-
this.flags = persistFlagsStrArr.map((flag) => JSON.parse(flag));
|
|
1126
|
-
}
|
|
1127
|
-
}
|
|
1128
|
-
getFeatureFlag(flagName) {
|
|
1129
|
-
return this.flags.find((flag) => flag.key === flagName);
|
|
1130
|
-
}
|
|
1131
|
-
isFlagEnabled(flagName) {
|
|
1132
|
-
return this.flags.findIndex((flag) => flag.key === flagName) !== -1;
|
|
1133
|
-
}
|
|
1134
|
-
onFlagsLoad(cb) {
|
|
1135
|
-
this.onFlagsCb = cb;
|
|
1136
|
-
}
|
|
1137
|
-
async reloadFlags(token) {
|
|
1138
|
-
const persistFlagsStr = this.app.sessionStorage.getItem(this.storageKey);
|
|
1139
|
-
const persistFlags = {};
|
|
1140
|
-
if (persistFlagsStr) {
|
|
1141
|
-
const persistArray = persistFlagsStr.split(';').filter(Boolean);
|
|
1142
|
-
persistArray.forEach((flag) => {
|
|
1143
|
-
const flagObj = JSON.parse(flag);
|
|
1144
|
-
persistFlags[flagObj.key] = { key: flagObj.key, value: flagObj.value };
|
|
1145
|
-
});
|
|
1146
|
-
}
|
|
1147
|
-
const sessionInfo = this.app.session.getInfo();
|
|
1148
|
-
const userInfo = this.app.session.userInfo;
|
|
1149
|
-
const requestObject = {
|
|
1150
|
-
projectID: sessionInfo.projectID,
|
|
1151
|
-
userID: sessionInfo.userID,
|
|
1152
|
-
metadata: sessionInfo.metadata,
|
|
1153
|
-
referrer: document.referrer,
|
|
1154
|
-
os: userInfo.userOS,
|
|
1155
|
-
device: userInfo.userDevice,
|
|
1156
|
-
country: userInfo.userCountry,
|
|
1157
|
-
state: userInfo.userState,
|
|
1158
|
-
city: userInfo.userCity,
|
|
1159
|
-
browser: userInfo.userBrowser,
|
|
1160
|
-
persistFlags: persistFlags,
|
|
1161
|
-
};
|
|
1162
|
-
const authToken = token ?? this.app.session.getSessionToken();
|
|
1163
|
-
const resp = await fetch(this.app.options.ingestPoint + '/v1/web/feature-flags', {
|
|
1164
|
-
method: 'POST',
|
|
1165
|
-
headers: {
|
|
1166
|
-
'Content-Type': 'application/json',
|
|
1167
|
-
Authorization: `Bearer ${authToken}`,
|
|
1168
|
-
},
|
|
1169
|
-
body: JSON.stringify(requestObject),
|
|
1170
|
-
});
|
|
1171
|
-
if (resp.status === 200) {
|
|
1172
|
-
const data = await resp.json();
|
|
1173
|
-
return this.handleFlags(data.flags);
|
|
1174
|
-
}
|
|
1175
|
-
}
|
|
1176
|
-
handleFlags(flags) {
|
|
1177
|
-
const persistFlags = [];
|
|
1178
|
-
flags.forEach((flag) => {
|
|
1179
|
-
if (flag.is_persist)
|
|
1180
|
-
persistFlags.push(flag);
|
|
1181
|
-
});
|
|
1182
|
-
let str = '';
|
|
1183
|
-
const uniquePersistFlags = this.diffPersist(persistFlags);
|
|
1184
|
-
uniquePersistFlags.forEach((flag) => {
|
|
1185
|
-
str += `${JSON.stringify(flag)};`;
|
|
1186
|
-
});
|
|
1187
|
-
this.app.sessionStorage.setItem(this.storageKey, str);
|
|
1188
|
-
this.flags = flags;
|
|
1189
|
-
return this.onFlagsCb?.(flags);
|
|
1190
|
-
}
|
|
1191
|
-
clearPersistFlags() {
|
|
1192
|
-
this.app.sessionStorage.removeItem(this.storageKey);
|
|
1193
|
-
}
|
|
1194
|
-
diffPersist(flags) {
|
|
1195
|
-
const persistFlags = this.app.sessionStorage.getItem(this.storageKey);
|
|
1196
|
-
if (!persistFlags)
|
|
1197
|
-
return flags;
|
|
1198
|
-
const persistFlagsStrArr = persistFlags.split(';').filter(Boolean);
|
|
1199
|
-
const persistFlagsArr = persistFlagsStrArr.map((flag) => JSON.parse(flag));
|
|
1200
|
-
return flags.filter((flag) => persistFlagsArr.findIndex((pf) => pf.key === flag.key) === -1);
|
|
1201
|
-
}
|
|
1202
|
-
}
|
|
1203
|
-
|
|
1204
1117
|
const DEPRECATED_ATTRS = { htmlmasked: 'hidden', masked: 'obscured' };
|
|
1205
1118
|
const IN_BROWSER = !(typeof window === 'undefined');
|
|
1206
1119
|
const IS_FIREFOX = IN_BROWSER && navigator.userAgent.match(/firefox|fxios/i);
|
|
@@ -1469,6 +1382,43 @@ function simpleMerge(defaultObj, givenObj) {
|
|
|
1469
1382
|
}
|
|
1470
1383
|
return result;
|
|
1471
1384
|
}
|
|
1385
|
+
function throttleWithTrailing(fn, interval) {
|
|
1386
|
+
const lastCalls = new Map();
|
|
1387
|
+
const timeouts = new Map();
|
|
1388
|
+
const lastArgs = new Map();
|
|
1389
|
+
const throttled = function (key, ...args) {
|
|
1390
|
+
const now = Date.now();
|
|
1391
|
+
const lastCall = lastCalls.get(key) ?? 0;
|
|
1392
|
+
const remaining = interval - (now - lastCall);
|
|
1393
|
+
lastArgs.set(key, args);
|
|
1394
|
+
if (remaining <= 0) {
|
|
1395
|
+
if (timeouts.has(key)) {
|
|
1396
|
+
clearTimeout(timeouts.get(key));
|
|
1397
|
+
timeouts.delete(key);
|
|
1398
|
+
}
|
|
1399
|
+
lastCalls.set(key, now);
|
|
1400
|
+
fn(key, ...args);
|
|
1401
|
+
}
|
|
1402
|
+
else if (!timeouts.has(key)) {
|
|
1403
|
+
const timeoutId = setTimeout(() => {
|
|
1404
|
+
lastCalls.set(key, Date.now());
|
|
1405
|
+
timeouts.delete(key);
|
|
1406
|
+
const finalArgs = lastArgs.get(key);
|
|
1407
|
+
fn(key, ...finalArgs);
|
|
1408
|
+
}, remaining);
|
|
1409
|
+
timeouts.set(key, timeoutId);
|
|
1410
|
+
}
|
|
1411
|
+
};
|
|
1412
|
+
throttled.clear = () => {
|
|
1413
|
+
for (const timeout of timeouts.values()) {
|
|
1414
|
+
clearTimeout(timeout);
|
|
1415
|
+
}
|
|
1416
|
+
timeouts.clear();
|
|
1417
|
+
lastArgs.clear();
|
|
1418
|
+
lastCalls.clear();
|
|
1419
|
+
};
|
|
1420
|
+
return throttled;
|
|
1421
|
+
}
|
|
1472
1422
|
|
|
1473
1423
|
// Auto-generated, do not edit
|
|
1474
1424
|
/* eslint-disable */
|
|
@@ -1679,6 +1629,13 @@ function SetNodeAttributeDictGlobal(id, name, value) {
|
|
|
1679
1629
|
value,
|
|
1680
1630
|
];
|
|
1681
1631
|
}
|
|
1632
|
+
function NodeAnimationResult(id, styles) {
|
|
1633
|
+
return [
|
|
1634
|
+
36 /* Messages.Type.NodeAnimationResult */,
|
|
1635
|
+
id,
|
|
1636
|
+
styles,
|
|
1637
|
+
];
|
|
1638
|
+
}
|
|
1682
1639
|
function CSSInsertRule(id, rule, index) {
|
|
1683
1640
|
return [
|
|
1684
1641
|
37 /* Messages.Type.CSSInsertRule */,
|
|
@@ -1807,9 +1764,9 @@ function SetNodeAttributeDict(id, name, value) {
|
|
|
1807
1764
|
value,
|
|
1808
1765
|
];
|
|
1809
1766
|
}
|
|
1810
|
-
function
|
|
1767
|
+
function ResourceTimingDeprecatedDeprecated(timestamp, duration, ttfb, headerSize, encodedBodySize, decodedBodySize, url, initiator) {
|
|
1811
1768
|
return [
|
|
1812
|
-
53 /* Messages.Type.
|
|
1769
|
+
53 /* Messages.Type.ResourceTimingDeprecatedDeprecated */,
|
|
1813
1770
|
timestamp,
|
|
1814
1771
|
duration,
|
|
1815
1772
|
ttfb,
|
|
@@ -1891,6 +1848,13 @@ function CustomIssue(name, payload) {
|
|
|
1891
1848
|
payload,
|
|
1892
1849
|
];
|
|
1893
1850
|
}
|
|
1851
|
+
function SetNodeSlot(id, slotID) {
|
|
1852
|
+
return [
|
|
1853
|
+
65 /* Messages.Type.SetNodeSlot */,
|
|
1854
|
+
id,
|
|
1855
|
+
slotID,
|
|
1856
|
+
];
|
|
1857
|
+
}
|
|
1894
1858
|
function CSSInsertRuleURLBased(id, rule, index, baseURL) {
|
|
1895
1859
|
return [
|
|
1896
1860
|
67 /* Messages.Type.CSSInsertRuleURLBased */,
|
|
@@ -2023,6 +1987,47 @@ function WSChannel(chType, channelName, data, timestamp, dir, messageType) {
|
|
|
2023
1987
|
messageType,
|
|
2024
1988
|
];
|
|
2025
1989
|
}
|
|
1990
|
+
function ResourceTiming(timestamp, duration, ttfb, headerSize, encodedBodySize, decodedBodySize, url, initiator, transferredSize, cached, queueing, dnsLookup, initialConnection, ssl, contentDownload, total, stalled) {
|
|
1991
|
+
return [
|
|
1992
|
+
85 /* Messages.Type.ResourceTiming */,
|
|
1993
|
+
timestamp,
|
|
1994
|
+
duration,
|
|
1995
|
+
ttfb,
|
|
1996
|
+
headerSize,
|
|
1997
|
+
encodedBodySize,
|
|
1998
|
+
decodedBodySize,
|
|
1999
|
+
url,
|
|
2000
|
+
initiator,
|
|
2001
|
+
transferredSize,
|
|
2002
|
+
cached,
|
|
2003
|
+
queueing,
|
|
2004
|
+
dnsLookup,
|
|
2005
|
+
initialConnection,
|
|
2006
|
+
ssl,
|
|
2007
|
+
contentDownload,
|
|
2008
|
+
total,
|
|
2009
|
+
stalled,
|
|
2010
|
+
];
|
|
2011
|
+
}
|
|
2012
|
+
function Incident(label, startTime, endTime) {
|
|
2013
|
+
return [
|
|
2014
|
+
87 /* Messages.Type.Incident */,
|
|
2015
|
+
label,
|
|
2016
|
+
startTime,
|
|
2017
|
+
endTime,
|
|
2018
|
+
];
|
|
2019
|
+
}
|
|
2020
|
+
function LongAnimationTask$1(name, duration, blockingDuration, firstUIEventTimestamp, startTime, scripts) {
|
|
2021
|
+
return [
|
|
2022
|
+
89 /* Messages.Type.LongAnimationTask */,
|
|
2023
|
+
name,
|
|
2024
|
+
duration,
|
|
2025
|
+
blockingDuration,
|
|
2026
|
+
firstUIEventTimestamp,
|
|
2027
|
+
startTime,
|
|
2028
|
+
scripts,
|
|
2029
|
+
];
|
|
2030
|
+
}
|
|
2026
2031
|
function InputChange(id, value, valueMasked, label, hesitationTime, inputDuration) {
|
|
2027
2032
|
return [
|
|
2028
2033
|
112 /* Messages.Type.InputChange */,
|
|
@@ -2054,9 +2059,9 @@ function UnbindNodes(totalRemovedPercent) {
|
|
|
2054
2059
|
totalRemovedPercent,
|
|
2055
2060
|
];
|
|
2056
2061
|
}
|
|
2057
|
-
function
|
|
2062
|
+
function ResourceTimingDeprecated(timestamp, duration, ttfb, headerSize, encodedBodySize, decodedBodySize, url, initiator, transferredSize, cached) {
|
|
2058
2063
|
return [
|
|
2059
|
-
116 /* Messages.Type.
|
|
2064
|
+
116 /* Messages.Type.ResourceTimingDeprecated */,
|
|
2060
2065
|
timestamp,
|
|
2061
2066
|
duration,
|
|
2062
2067
|
ttfb,
|
|
@@ -2153,9 +2158,11 @@ var _Messages = /*#__PURE__*/Object.freeze({
|
|
|
2153
2158
|
Fetch: Fetch,
|
|
2154
2159
|
GraphQL: GraphQL,
|
|
2155
2160
|
GraphQLDeprecated: GraphQLDeprecated,
|
|
2161
|
+
Incident: Incident,
|
|
2156
2162
|
InputChange: InputChange,
|
|
2157
2163
|
JSException: JSException,
|
|
2158
2164
|
LoadFontFace: LoadFontFace,
|
|
2165
|
+
LongAnimationTask: LongAnimationTask$1,
|
|
2159
2166
|
LongTask: LongTask,
|
|
2160
2167
|
Metadata: Metadata,
|
|
2161
2168
|
MobX: MobX,
|
|
@@ -2167,6 +2174,7 @@ var _Messages = /*#__PURE__*/Object.freeze({
|
|
|
2167
2174
|
NetworkRequest: NetworkRequest,
|
|
2168
2175
|
NetworkRequestDeprecated: NetworkRequestDeprecated,
|
|
2169
2176
|
NgRx: NgRx,
|
|
2177
|
+
NodeAnimationResult: NodeAnimationResult,
|
|
2170
2178
|
OTable: OTable,
|
|
2171
2179
|
PageLoadTiming: PageLoadTiming,
|
|
2172
2180
|
PageRenderTiming: PageRenderTiming,
|
|
@@ -2179,6 +2187,7 @@ var _Messages = /*#__PURE__*/Object.freeze({
|
|
|
2179
2187
|
RemoveNodeAttribute: RemoveNodeAttribute,
|
|
2180
2188
|
ResourceTiming: ResourceTiming,
|
|
2181
2189
|
ResourceTimingDeprecated: ResourceTimingDeprecated,
|
|
2190
|
+
ResourceTimingDeprecatedDeprecated: ResourceTimingDeprecatedDeprecated,
|
|
2182
2191
|
SelectionChange: SelectionChange,
|
|
2183
2192
|
SetCSSDataURLBased: SetCSSDataURLBased,
|
|
2184
2193
|
SetInputChecked: SetInputChecked,
|
|
@@ -2192,6 +2201,7 @@ var _Messages = /*#__PURE__*/Object.freeze({
|
|
|
2192
2201
|
SetNodeData: SetNodeData,
|
|
2193
2202
|
SetNodeFocus: SetNodeFocus,
|
|
2194
2203
|
SetNodeScroll: SetNodeScroll,
|
|
2204
|
+
SetNodeSlot: SetNodeSlot,
|
|
2195
2205
|
SetPageLocation: SetPageLocation,
|
|
2196
2206
|
SetPageLocationDeprecated: SetPageLocationDeprecated,
|
|
2197
2207
|
SetPageVisibility: SetPageVisibility,
|
|
@@ -2266,7 +2276,7 @@ function Performance (app, opts) {
|
|
|
2266
2276
|
const WATCHED_TAGS_KEY = '__or__watched_tags__';
|
|
2267
2277
|
class TagWatcher {
|
|
2268
2278
|
constructor(params) {
|
|
2269
|
-
this.
|
|
2279
|
+
this.interval = null;
|
|
2270
2280
|
this.tags = [];
|
|
2271
2281
|
this.sessionStorage = params.sessionStorage;
|
|
2272
2282
|
this.errLog = params.errLog;
|
|
@@ -2308,9 +2318,12 @@ class TagWatcher {
|
|
|
2308
2318
|
}
|
|
2309
2319
|
setTags(tags) {
|
|
2310
2320
|
this.tags = tags;
|
|
2311
|
-
this.
|
|
2312
|
-
|
|
2313
|
-
this.
|
|
2321
|
+
if (this.interval) {
|
|
2322
|
+
clearInterval(this.interval);
|
|
2323
|
+
this.interval = null;
|
|
2324
|
+
}
|
|
2325
|
+
this.interval = setInterval(() => {
|
|
2326
|
+
this.tags.forEach((tag) => {
|
|
2314
2327
|
const possibleEls = document.querySelectorAll(tag.selector);
|
|
2315
2328
|
if (possibleEls.length > 0) {
|
|
2316
2329
|
const el = possibleEls[0];
|
|
@@ -2318,1002 +2331,22 @@ class TagWatcher {
|
|
|
2318
2331
|
el.__or_watcher_tagname = tag.id;
|
|
2319
2332
|
this.observer.observe(el);
|
|
2320
2333
|
}
|
|
2321
|
-
}
|
|
2322
|
-
});
|
|
2334
|
+
});
|
|
2335
|
+
}, 500);
|
|
2323
2336
|
}
|
|
2324
2337
|
onTagRendered(tagId) {
|
|
2325
|
-
if (this.
|
|
2326
|
-
|
|
2338
|
+
if (this.tags.findIndex(t => t.id === tagId)) {
|
|
2339
|
+
this.tags = this.tags.filter((tag) => tag.id !== tagId);
|
|
2327
2340
|
}
|
|
2328
2341
|
this.onTag(tagId);
|
|
2329
2342
|
}
|
|
2330
2343
|
clear() {
|
|
2331
|
-
this.tags.forEach((tag) => {
|
|
2332
|
-
clearInterval(this.intervals[tag.id]);
|
|
2333
|
-
});
|
|
2334
2344
|
this.tags = [];
|
|
2335
|
-
this.
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
}
|
|
2339
|
-
|
|
2340
|
-
const bgStyle = {
|
|
2341
|
-
position: 'fixed',
|
|
2342
|
-
top: 0,
|
|
2343
|
-
left: 0,
|
|
2344
|
-
width: '100vw',
|
|
2345
|
-
height: '100vh',
|
|
2346
|
-
background: 'rgba(0, 0, 0, 0.40)',
|
|
2347
|
-
display: 'flex',
|
|
2348
|
-
alignItems: 'center',
|
|
2349
|
-
justifyContent: 'center',
|
|
2350
|
-
zIndex: 999999,
|
|
2351
|
-
fontFamily: `-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"`,
|
|
2352
|
-
};
|
|
2353
|
-
const containerStyle = {
|
|
2354
|
-
display: 'flex',
|
|
2355
|
-
flexDirection: 'column',
|
|
2356
|
-
gap: '2rem',
|
|
2357
|
-
alignItems: 'center',
|
|
2358
|
-
padding: '1.5rem',
|
|
2359
|
-
borderRadius: '2px',
|
|
2360
|
-
border: '1px solid rgb(255 255 255 / var(--tw-bg-opacity, 1))',
|
|
2361
|
-
background: '#FFF',
|
|
2362
|
-
width: '22rem',
|
|
2363
|
-
};
|
|
2364
|
-
const containerWidgetStyle = {
|
|
2365
|
-
display: 'flex',
|
|
2366
|
-
'flex-direction': 'column',
|
|
2367
|
-
gap: 'unset',
|
|
2368
|
-
'align-items': 'center',
|
|
2369
|
-
padding: 'unset',
|
|
2370
|
-
fontFamily: `-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"`,
|
|
2371
|
-
'border-radius': '2px',
|
|
2372
|
-
border: '1px solid rgb(255 255 255 / var(--tw-bg-opacity, 1))',
|
|
2373
|
-
background: 'rgba(255, 255, 255, 0.75)',
|
|
2374
|
-
width: '22rem',
|
|
2375
|
-
};
|
|
2376
|
-
const titleStyle = {
|
|
2377
|
-
fontFamily: 'Verdana, sans-serif',
|
|
2378
|
-
fontSize: '1.25rem',
|
|
2379
|
-
fontStyle: 'normal',
|
|
2380
|
-
fontWeight: '500',
|
|
2381
|
-
lineHeight: '1.75rem',
|
|
2382
|
-
color: 'rgba(0, 0, 0, 0.85)',
|
|
2383
|
-
};
|
|
2384
|
-
const descriptionStyle = {
|
|
2385
|
-
borderTop: '1px solid rgba(0, 0, 0, 0.06)',
|
|
2386
|
-
borderBottom: '1px solid rgba(0, 0, 0, 0.06)',
|
|
2387
|
-
padding: '1.25rem 0rem',
|
|
2388
|
-
color: 'rgba(0, 0, 0, 0.85)',
|
|
2389
|
-
fontFamily: 'Verdana, sans-serif',
|
|
2390
|
-
fontSize: '13px',
|
|
2391
|
-
fontStyle: 'normal',
|
|
2392
|
-
fontWeight: '400',
|
|
2393
|
-
lineHeight: 'auto',
|
|
2394
|
-
whiteSpace: 'pre-wrap',
|
|
2395
|
-
};
|
|
2396
|
-
const buttonStyle = {
|
|
2397
|
-
display: 'flex',
|
|
2398
|
-
padding: '0.4rem 0.9375rem',
|
|
2399
|
-
justifyContent: 'center',
|
|
2400
|
-
alignItems: 'center',
|
|
2401
|
-
gap: '0.625rem',
|
|
2402
|
-
borderRadius: '0.25rem',
|
|
2403
|
-
border: '1px solid #394EFF',
|
|
2404
|
-
background: '#394EFF',
|
|
2405
|
-
boxShadow: '0px 2px 0px 0px rgba(0, 0, 0, 0.04)',
|
|
2406
|
-
color: '#FFF',
|
|
2407
|
-
textAlign: 'center',
|
|
2408
|
-
fontFamily: 'Verdana, sans-serif',
|
|
2409
|
-
fontSize: '1rem',
|
|
2410
|
-
fontStyle: 'normal',
|
|
2411
|
-
fontWeight: '500',
|
|
2412
|
-
lineHeight: '1.5rem',
|
|
2413
|
-
cursor: 'pointer',
|
|
2414
|
-
};
|
|
2415
|
-
const sectionTitleStyle = {
|
|
2416
|
-
fontFamily: 'Verdana, sans-serif',
|
|
2417
|
-
fontSize: '13px',
|
|
2418
|
-
fontWeight: '500',
|
|
2419
|
-
lineHeight: 'auto',
|
|
2420
|
-
display: 'flex',
|
|
2421
|
-
justifyContent: 'space-between',
|
|
2422
|
-
width: '100%',
|
|
2423
|
-
cursor: 'pointer',
|
|
2424
|
-
};
|
|
2425
|
-
const contentStyle = {
|
|
2426
|
-
display: 'flex',
|
|
2427
|
-
flexDirection: 'column',
|
|
2428
|
-
alignItems: 'flex-start',
|
|
2429
|
-
gap: '0.625rem',
|
|
2430
|
-
fontSize: '13px',
|
|
2431
|
-
lineHeight: 'auto',
|
|
2432
|
-
};
|
|
2433
|
-
// New widget styles
|
|
2434
|
-
const titleWidgetStyle = {
|
|
2435
|
-
padding: '0.5rem',
|
|
2436
|
-
gap: '0.5rem',
|
|
2437
|
-
fontFamily: 'Verdana, sans-serif',
|
|
2438
|
-
fontSize: '16px',
|
|
2439
|
-
fontStyle: 'normal',
|
|
2440
|
-
fontWeight: '500',
|
|
2441
|
-
lineHeight: 'auto',
|
|
2442
|
-
color: 'white',
|
|
2443
|
-
display: 'flex',
|
|
2444
|
-
alignItems: 'center',
|
|
2445
|
-
width: '100%',
|
|
2446
|
-
borderRadius: '2px',
|
|
2447
|
-
background: 'rgba(0, 0, 0, 0.75)',
|
|
2448
|
-
boxSizing: 'border-box',
|
|
2449
|
-
};
|
|
2450
|
-
const descriptionWidgetStyle = {
|
|
2451
|
-
boxSizing: 'border-box',
|
|
2452
|
-
display: 'block',
|
|
2453
|
-
width: '100%',
|
|
2454
|
-
borderBottom: '1px solid rgb(255 255 255 / var(--tw-bg-opacity, 1))',
|
|
2455
|
-
background: '#FFF',
|
|
2456
|
-
padding: '0.65rem',
|
|
2457
|
-
alignSelf: 'stretch',
|
|
2458
|
-
color: '#000',
|
|
2459
|
-
fontFamily: 'Verdana, sans-serif',
|
|
2460
|
-
// fontSize: '0.875rem',
|
|
2461
|
-
fontStyle: 'normal',
|
|
2462
|
-
fontWeight: '400',
|
|
2463
|
-
// lineHeight: '1.375rem',
|
|
2464
|
-
};
|
|
2465
|
-
const endSectionStyle = {
|
|
2466
|
-
...descriptionWidgetStyle,
|
|
2467
|
-
display: 'flex',
|
|
2468
|
-
flexDirection: 'column',
|
|
2469
|
-
alignItems: 'center',
|
|
2470
|
-
gap: '0.625rem',
|
|
2471
|
-
};
|
|
2472
|
-
const symbolIcon = {
|
|
2473
|
-
fontSize: '1.25rem',
|
|
2474
|
-
fontWeight: '500',
|
|
2475
|
-
cursor: 'pointer',
|
|
2476
|
-
color: '#394EFF',
|
|
2477
|
-
};
|
|
2478
|
-
const buttonWidgetStyle = {
|
|
2479
|
-
display: 'flex',
|
|
2480
|
-
padding: '0.4rem 0.9375rem',
|
|
2481
|
-
justifyContent: 'center',
|
|
2482
|
-
alignItems: 'center',
|
|
2483
|
-
gap: '0.625rem',
|
|
2484
|
-
borderRadius: '0.25rem',
|
|
2485
|
-
border: '1px solid #394EFF',
|
|
2486
|
-
background: '#394EFF',
|
|
2487
|
-
boxShadow: '0px 2px 0px 0px rgba(0, 0, 0, 0.04)',
|
|
2488
|
-
color: '#FFF',
|
|
2489
|
-
textAlign: 'center',
|
|
2490
|
-
fontFamily: 'Verdana, sans-serif',
|
|
2491
|
-
fontSize: '1rem',
|
|
2492
|
-
fontStyle: 'normal',
|
|
2493
|
-
fontWeight: '500',
|
|
2494
|
-
lineHeight: '1.5rem',
|
|
2495
|
-
width: '100%',
|
|
2496
|
-
boxSizing: 'border-box',
|
|
2497
|
-
cursor: 'pointer',
|
|
2498
|
-
};
|
|
2499
|
-
const stopWidgetStyle = {
|
|
2500
|
-
marginTop: '1rem',
|
|
2501
|
-
marginBottom: '1rem',
|
|
2502
|
-
cursor: 'pointer',
|
|
2503
|
-
display: 'block',
|
|
2504
|
-
fontWeight: '500',
|
|
2505
|
-
fontSize: '13px!important',
|
|
2506
|
-
lineHeight: 'auto',
|
|
2507
|
-
};
|
|
2508
|
-
const paginationStyle = {
|
|
2509
|
-
display: 'flex',
|
|
2510
|
-
justifyContent: 'space-between',
|
|
2511
|
-
alignItems: 'center',
|
|
2512
|
-
gap: '1rem',
|
|
2513
|
-
padding: '0.5rem',
|
|
2514
|
-
width: '100%',
|
|
2515
|
-
boxSizing: 'border-box',
|
|
2516
|
-
};
|
|
2517
|
-
const taskNumberActive = {
|
|
2518
|
-
display: 'flex',
|
|
2519
|
-
flexDirection: 'column',
|
|
2520
|
-
alignItems: 'center',
|
|
2521
|
-
justifyContent: 'center',
|
|
2522
|
-
borderRadius: '6.25em',
|
|
2523
|
-
outline: '1px solid #394EFF',
|
|
2524
|
-
fontSize: '13px',
|
|
2525
|
-
height: '24px',
|
|
2526
|
-
width: '24px',
|
|
2527
|
-
};
|
|
2528
|
-
const taskNumberDone = {
|
|
2529
|
-
display: 'flex',
|
|
2530
|
-
flexDirection: 'column',
|
|
2531
|
-
alignItems: 'center',
|
|
2532
|
-
justifyContent: 'center',
|
|
2533
|
-
borderRadius: '6.25em',
|
|
2534
|
-
outline: '1px solid #D2DFFF',
|
|
2535
|
-
boxShadow: '0px 2px 0px 0px rgba(0, 0, 0, 0.04)',
|
|
2536
|
-
background: '#D2DFFF',
|
|
2537
|
-
fontSize: '13px',
|
|
2538
|
-
height: '24px',
|
|
2539
|
-
width: '24px',
|
|
2540
|
-
};
|
|
2541
|
-
const taskDescriptionCard = {
|
|
2542
|
-
borderRadius: '0.375rem',
|
|
2543
|
-
border: '1px solid rgba(0, 0, 0, 0.06)',
|
|
2544
|
-
background: '#F5F7FF',
|
|
2545
|
-
boxShadow: '0px 2px 0px 0px rgba(0, 0, 0, 0.04)',
|
|
2546
|
-
display: 'flex',
|
|
2547
|
-
flexDirection: 'column',
|
|
2548
|
-
padding: '0.625rem 0.9375rem',
|
|
2549
|
-
gap: '0.5rem',
|
|
2550
|
-
alignSelf: 'stretch',
|
|
2551
|
-
};
|
|
2552
|
-
const taskTextStyle = {
|
|
2553
|
-
fontWeight: 'bold',
|
|
2554
|
-
};
|
|
2555
|
-
const taskDescriptionStyle = {
|
|
2556
|
-
fontSize: '13px',
|
|
2557
|
-
lineHeight: 'auto',
|
|
2558
|
-
};
|
|
2559
|
-
const taskButtonStyle = {
|
|
2560
|
-
marginRight: '0.5rem',
|
|
2561
|
-
cursor: 'pointer',
|
|
2562
|
-
color: '#394EFF',
|
|
2563
|
-
textAlign: 'center',
|
|
2564
|
-
fontFamily: 'Verdana, sans-serif',
|
|
2565
|
-
fontSize: '13px',
|
|
2566
|
-
fontStyle: 'normal',
|
|
2567
|
-
fontWeight: '500',
|
|
2568
|
-
lineHeight: 'auto',
|
|
2569
|
-
};
|
|
2570
|
-
const taskButtonBorderedStyle = {
|
|
2571
|
-
...taskButtonStyle,
|
|
2572
|
-
display: 'flex',
|
|
2573
|
-
padding: '0.25rem 0.9375rem',
|
|
2574
|
-
justifyContent: 'center',
|
|
2575
|
-
alignItems: 'center',
|
|
2576
|
-
gap: '0.5rem',
|
|
2577
|
-
borderRadius: '0.25rem',
|
|
2578
|
-
border: '1px solid #394EFF',
|
|
2579
|
-
};
|
|
2580
|
-
const taskButtonsRow = {
|
|
2581
|
-
display: 'flex',
|
|
2582
|
-
justifyContent: 'space-between',
|
|
2583
|
-
alignItems: 'center',
|
|
2584
|
-
width: '100%',
|
|
2585
|
-
boxSizing: 'border-box',
|
|
2586
|
-
};
|
|
2587
|
-
const spinnerStyles = {
|
|
2588
|
-
border: '4px solid rgba(255, 255, 255, 0.4)',
|
|
2589
|
-
width: '16px',
|
|
2590
|
-
height: '16px',
|
|
2591
|
-
borderRadius: '50%',
|
|
2592
|
-
borderLeftColor: '#fff',
|
|
2593
|
-
animation: 'spin 0.5s linear infinite',
|
|
2594
|
-
};
|
|
2595
|
-
|
|
2596
|
-
const Quality = {
|
|
2597
|
-
Standard: { width: 1280, height: 720 }};
|
|
2598
|
-
class Recorder {
|
|
2599
|
-
constructor(app) {
|
|
2600
|
-
this.app = app;
|
|
2601
|
-
this.mediaRecorder = null;
|
|
2602
|
-
this.recordedChunks = [];
|
|
2603
|
-
this.stream = null;
|
|
2604
|
-
this.recStartTs = null;
|
|
2605
|
-
}
|
|
2606
|
-
async startRecording(fps, quality, micReq, camReq) {
|
|
2607
|
-
this.recStartTs = this.app.timestamp();
|
|
2608
|
-
const videoConstraints = quality;
|
|
2609
|
-
try {
|
|
2610
|
-
this.stream = await navigator.mediaDevices.getUserMedia({
|
|
2611
|
-
video: camReq ? { ...videoConstraints, frameRate: { ideal: fps } } : false,
|
|
2612
|
-
audio: micReq,
|
|
2613
|
-
});
|
|
2614
|
-
this.mediaRecorder = new MediaRecorder(this.stream, {
|
|
2615
|
-
mimeType: 'video/webm;codecs=vp9',
|
|
2616
|
-
});
|
|
2617
|
-
this.recordedChunks = [];
|
|
2618
|
-
this.mediaRecorder.ondataavailable = (event) => {
|
|
2619
|
-
if (event.data.size > 0) {
|
|
2620
|
-
this.recordedChunks.push(event.data);
|
|
2621
|
-
}
|
|
2622
|
-
};
|
|
2623
|
-
this.mediaRecorder.start();
|
|
2624
|
-
}
|
|
2625
|
-
catch (error) {
|
|
2626
|
-
console.error(error);
|
|
2627
|
-
}
|
|
2628
|
-
}
|
|
2629
|
-
async stopRecording() {
|
|
2630
|
-
return new Promise((resolve) => {
|
|
2631
|
-
if (!this.mediaRecorder)
|
|
2632
|
-
return;
|
|
2633
|
-
this.mediaRecorder.onstop = () => {
|
|
2634
|
-
const blob = new Blob(this.recordedChunks, {
|
|
2635
|
-
type: 'video/webm',
|
|
2636
|
-
});
|
|
2637
|
-
resolve(blob);
|
|
2638
|
-
};
|
|
2639
|
-
this.mediaRecorder.stop();
|
|
2640
|
-
});
|
|
2641
|
-
}
|
|
2642
|
-
async sendToAPI() {
|
|
2643
|
-
const blob = await this.stopRecording();
|
|
2644
|
-
// const formData = new FormData()
|
|
2645
|
-
// formData.append('file', blob, 'record.webm')
|
|
2646
|
-
// formData.append('start', this.recStartTs?.toString() ?? '')
|
|
2647
|
-
return fetch(`${this.app.options.ingestPoint}/v1/web/uxt/upload-url`, {
|
|
2648
|
-
headers: {
|
|
2649
|
-
Authorization: `Bearer ${this.app.session.getSessionToken()}`,
|
|
2650
|
-
},
|
|
2651
|
-
})
|
|
2652
|
-
.then((r) => {
|
|
2653
|
-
if (r.ok) {
|
|
2654
|
-
return r.json();
|
|
2655
|
-
}
|
|
2656
|
-
else {
|
|
2657
|
-
throw new Error('Failed to get upload url');
|
|
2658
|
-
}
|
|
2659
|
-
})
|
|
2660
|
-
.then(({ url }) => {
|
|
2661
|
-
return fetch(url, {
|
|
2662
|
-
method: 'PUT',
|
|
2663
|
-
headers: {
|
|
2664
|
-
'Content-Type': 'video/webm',
|
|
2665
|
-
},
|
|
2666
|
-
body: blob,
|
|
2667
|
-
});
|
|
2668
|
-
})
|
|
2669
|
-
.catch(console.error)
|
|
2670
|
-
.finally(() => {
|
|
2671
|
-
this.discard();
|
|
2672
|
-
});
|
|
2673
|
-
}
|
|
2674
|
-
async saveToFile(fileName = 'recorded-video.webm') {
|
|
2675
|
-
const blob = await this.stopRecording();
|
|
2676
|
-
const url = URL.createObjectURL(blob);
|
|
2677
|
-
const a = document.createElement('a');
|
|
2678
|
-
a.style.display = 'none';
|
|
2679
|
-
a.href = url;
|
|
2680
|
-
a.download = fileName;
|
|
2681
|
-
document.body.appendChild(a);
|
|
2682
|
-
a.click();
|
|
2683
|
-
window.URL.revokeObjectURL(url);
|
|
2684
|
-
document.body.removeChild(a);
|
|
2685
|
-
}
|
|
2686
|
-
discard() {
|
|
2687
|
-
this.mediaRecorder?.stop();
|
|
2688
|
-
this.stream?.getTracks().forEach((track) => track.stop());
|
|
2689
|
-
}
|
|
2690
|
-
}
|
|
2691
|
-
|
|
2692
|
-
// @ts-nocheck
|
|
2693
|
-
function attachDND(element, dragTarget) {
|
|
2694
|
-
dragTarget.onmousedown = function (event) {
|
|
2695
|
-
const clientRect = element.getBoundingClientRect();
|
|
2696
|
-
const shiftX = event.clientX - clientRect.left;
|
|
2697
|
-
const shiftY = event.clientY - clientRect.top;
|
|
2698
|
-
element.style.position = 'fixed';
|
|
2699
|
-
element.style.zIndex = 99999999999999;
|
|
2700
|
-
moveAt(event.pageX, event.pageY);
|
|
2701
|
-
function moveAt(pageX, pageY) {
|
|
2702
|
-
let leftC = pageX - shiftX;
|
|
2703
|
-
let topC = pageY - shiftY;
|
|
2704
|
-
if (leftC <= 5)
|
|
2705
|
-
leftC = 5;
|
|
2706
|
-
if (topC <= 5)
|
|
2707
|
-
topC = 5;
|
|
2708
|
-
if (leftC >= window.innerWidth - clientRect.width)
|
|
2709
|
-
leftC = window.innerWidth - clientRect.width;
|
|
2710
|
-
if (topC >= window.innerHeight - clientRect.height)
|
|
2711
|
-
topC = window.innerHeight - clientRect.height;
|
|
2712
|
-
element.style.left = `${leftC}px`;
|
|
2713
|
-
element.style.top = `${topC}px`;
|
|
2714
|
-
}
|
|
2715
|
-
function onMouseMove(event) {
|
|
2716
|
-
moveAt(event.pageX, event.pageY);
|
|
2717
|
-
}
|
|
2718
|
-
document.addEventListener('mousemove', onMouseMove);
|
|
2719
|
-
const clearAll = () => {
|
|
2720
|
-
document.removeEventListener('mousemove', onMouseMove);
|
|
2721
|
-
document.removeEventListener('mouseup', clearAll);
|
|
2722
|
-
};
|
|
2723
|
-
document.addEventListener('mouseup', clearAll);
|
|
2724
|
-
};
|
|
2725
|
-
dragTarget.ondragstart = function () {
|
|
2726
|
-
return false;
|
|
2727
|
-
};
|
|
2728
|
-
}
|
|
2729
|
-
|
|
2730
|
-
function generateGrid() {
|
|
2731
|
-
const grid = document.createElement('div');
|
|
2732
|
-
grid.className = 'grid';
|
|
2733
|
-
for (let i = 0; i < 16; i++) {
|
|
2734
|
-
const cell = document.createElement('div');
|
|
2735
|
-
Object.assign(cell.style, {
|
|
2736
|
-
width: '2px',
|
|
2737
|
-
height: '2px',
|
|
2738
|
-
borderRadius: '10px',
|
|
2739
|
-
background: 'white',
|
|
2740
|
-
});
|
|
2741
|
-
cell.className = 'cell';
|
|
2742
|
-
grid.appendChild(cell);
|
|
2743
|
-
}
|
|
2744
|
-
Object.assign(grid.style, {
|
|
2745
|
-
display: 'grid',
|
|
2746
|
-
gridTemplateColumns: 'repeat(4, 1fr)',
|
|
2747
|
-
gridTemplateRows: 'repeat(4, 1fr)',
|
|
2748
|
-
gap: '2px',
|
|
2749
|
-
cursor: 'grab',
|
|
2750
|
-
});
|
|
2751
|
-
return grid;
|
|
2752
|
-
}
|
|
2753
|
-
function generateChevron() {
|
|
2754
|
-
const triangle = document.createElement('div');
|
|
2755
|
-
Object.assign(triangle.style, {
|
|
2756
|
-
width: '0',
|
|
2757
|
-
height: '0',
|
|
2758
|
-
borderLeft: '7px solid transparent',
|
|
2759
|
-
borderRight: '7px solid transparent',
|
|
2760
|
-
borderBottom: '7px solid white',
|
|
2761
|
-
});
|
|
2762
|
-
const container = document.createElement('div');
|
|
2763
|
-
container.appendChild(triangle);
|
|
2764
|
-
Object.assign(container.style, {
|
|
2765
|
-
display: 'flex',
|
|
2766
|
-
alignItems: 'center',
|
|
2767
|
-
justifyContent: 'center',
|
|
2768
|
-
width: '16px',
|
|
2769
|
-
height: '16px',
|
|
2770
|
-
cursor: 'pointer',
|
|
2771
|
-
marginLeft: 'auto',
|
|
2772
|
-
transform: 'rotate(180deg)',
|
|
2773
|
-
});
|
|
2774
|
-
return container;
|
|
2775
|
-
}
|
|
2776
|
-
function addKeyframes() {
|
|
2777
|
-
const styleSheet = document.createElement('style');
|
|
2778
|
-
styleSheet.type = 'text/css';
|
|
2779
|
-
styleSheet.innerText = `@keyframes spin {
|
|
2780
|
-
0% { transform: rotate(0deg); }
|
|
2781
|
-
100% { transform: rotate(360deg); }
|
|
2782
|
-
}`;
|
|
2783
|
-
document.head.appendChild(styleSheet);
|
|
2784
|
-
}
|
|
2785
|
-
function createSpinner() {
|
|
2786
|
-
addKeyframes();
|
|
2787
|
-
const spinner = document.createElement('div');
|
|
2788
|
-
spinner.classList.add('spinner');
|
|
2789
|
-
Object.assign(spinner.style, spinnerStyles);
|
|
2790
|
-
return spinner;
|
|
2791
|
-
}
|
|
2792
|
-
function createElement(tag, className, styles, textContent, id) {
|
|
2793
|
-
const element = document.createElement(tag);
|
|
2794
|
-
element.className = className;
|
|
2795
|
-
Object.assign(element.style, styles);
|
|
2796
|
-
if (textContent) {
|
|
2797
|
-
element.textContent = textContent;
|
|
2798
|
-
}
|
|
2799
|
-
if (id) {
|
|
2800
|
-
element.id = id;
|
|
2801
|
-
}
|
|
2802
|
-
return element;
|
|
2803
|
-
}
|
|
2804
|
-
const TEST_START = 'or_uxt_test_start';
|
|
2805
|
-
const TASK_IND = 'or_uxt_task_index';
|
|
2806
|
-
const SESSION_ID = 'or_uxt_session_id';
|
|
2807
|
-
const TEST_ID = 'or_uxt_test_id';
|
|
2808
|
-
|
|
2809
|
-
class SignalManager {
|
|
2810
|
-
constructor(ingestPoint, getTimestamp, token, testId, storageKey, setStorageKey, removeStorageKey, getStorageKey, getSessionId) {
|
|
2811
|
-
this.ingestPoint = ingestPoint;
|
|
2812
|
-
this.getTimestamp = getTimestamp;
|
|
2813
|
-
this.token = token;
|
|
2814
|
-
this.testId = testId;
|
|
2815
|
-
this.storageKey = storageKey;
|
|
2816
|
-
this.setStorageKey = setStorageKey;
|
|
2817
|
-
this.removeStorageKey = removeStorageKey;
|
|
2818
|
-
this.getStorageKey = getStorageKey;
|
|
2819
|
-
this.getSessionId = getSessionId;
|
|
2820
|
-
this.durations = {
|
|
2821
|
-
testStart: 0,
|
|
2822
|
-
tasks: [],
|
|
2823
|
-
};
|
|
2824
|
-
this.getDurations = () => {
|
|
2825
|
-
return this.durations;
|
|
2826
|
-
};
|
|
2827
|
-
this.setDurations = (durations) => {
|
|
2828
|
-
this.durations.testStart = durations.testStart;
|
|
2829
|
-
this.durations.tasks = durations.tasks;
|
|
2830
|
-
};
|
|
2831
|
-
this.signalTask = (taskId, status, taskAnswer) => {
|
|
2832
|
-
if (!taskId)
|
|
2833
|
-
return console.error('User Testing: No Task ID Given');
|
|
2834
|
-
const taskStart = this.durations.tasks.find((t) => t.taskId === taskId);
|
|
2835
|
-
const timestamp = this.getTimestamp();
|
|
2836
|
-
const duration = taskStart ? timestamp - taskStart.started : 0;
|
|
2837
|
-
return fetch(`${this.ingestPoint}/v1/web/uxt/signals/task`, {
|
|
2838
|
-
method: 'POST',
|
|
2839
|
-
headers: {
|
|
2840
|
-
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
|
|
2841
|
-
Authorization: `Bearer ${this.token}`,
|
|
2842
|
-
},
|
|
2843
|
-
body: JSON.stringify({
|
|
2844
|
-
testId: this.testId,
|
|
2845
|
-
taskId,
|
|
2846
|
-
status,
|
|
2847
|
-
duration,
|
|
2848
|
-
timestamp,
|
|
2849
|
-
taskAnswer,
|
|
2850
|
-
}),
|
|
2851
|
-
});
|
|
2852
|
-
};
|
|
2853
|
-
this.signalTest = (status) => {
|
|
2854
|
-
const timestamp = this.getTimestamp();
|
|
2855
|
-
if (status === 'begin' && this.testId) {
|
|
2856
|
-
const sessionId = this.getSessionId();
|
|
2857
|
-
this.setStorageKey(SESSION_ID, sessionId);
|
|
2858
|
-
this.setStorageKey(this.storageKey, this.testId.toString());
|
|
2859
|
-
this.setStorageKey(TEST_START, timestamp.toString());
|
|
2860
|
-
}
|
|
2861
|
-
else {
|
|
2862
|
-
this.removeStorageKey(this.storageKey);
|
|
2863
|
-
this.removeStorageKey(TASK_IND);
|
|
2864
|
-
this.removeStorageKey(TEST_START);
|
|
2865
|
-
}
|
|
2866
|
-
const start = this.durations.testStart || timestamp;
|
|
2867
|
-
const duration = timestamp - start;
|
|
2868
|
-
return fetch(`${this.ingestPoint}/v1/web/uxt/signals/test`, {
|
|
2869
|
-
method: 'POST',
|
|
2870
|
-
headers: {
|
|
2871
|
-
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
|
|
2872
|
-
Authorization: `Bearer ${this.token}`,
|
|
2873
|
-
},
|
|
2874
|
-
body: JSON.stringify({
|
|
2875
|
-
testId: this.testId,
|
|
2876
|
-
status,
|
|
2877
|
-
duration,
|
|
2878
|
-
timestamp,
|
|
2879
|
-
}),
|
|
2880
|
-
});
|
|
2881
|
-
};
|
|
2882
|
-
const possibleStart = this.getStorageKey(TEST_START);
|
|
2883
|
-
if (possibleStart) {
|
|
2884
|
-
this.durations.testStart = parseInt(possibleStart, 10);
|
|
2885
|
-
}
|
|
2886
|
-
}
|
|
2887
|
-
}
|
|
2888
|
-
|
|
2889
|
-
class UserTestManager {
|
|
2890
|
-
constructor(app, storageKey) {
|
|
2891
|
-
this.app = app;
|
|
2892
|
-
this.storageKey = storageKey;
|
|
2893
|
-
this.bg = createElement('div', 'bg', bgStyle, undefined, '__or_ut_bg');
|
|
2894
|
-
this.container = createElement('div', 'container', containerStyle, undefined, '__or_ut_ct');
|
|
2895
|
-
this.widgetGuidelinesVisible = true;
|
|
2896
|
-
this.widgetTasksVisible = false;
|
|
2897
|
-
this.widgetVisible = true;
|
|
2898
|
-
this.isActive = false;
|
|
2899
|
-
this.descriptionSection = null;
|
|
2900
|
-
this.taskSection = null;
|
|
2901
|
-
this.endSection = null;
|
|
2902
|
-
this.stopButton = null;
|
|
2903
|
-
this.stopButtonContainer = null;
|
|
2904
|
-
this.test = null;
|
|
2905
|
-
this.testId = null;
|
|
2906
|
-
this.signalManager = null;
|
|
2907
|
-
this.getTest = (id, token, inProgress) => {
|
|
2908
|
-
this.testId = id;
|
|
2909
|
-
const ingest = this.app.options.ingestPoint;
|
|
2910
|
-
return fetch(`${ingest}/v1/web/uxt/test/${id}`, {
|
|
2911
|
-
headers: {
|
|
2912
|
-
Authorization: `Bearer ${token}`,
|
|
2913
|
-
},
|
|
2914
|
-
})
|
|
2915
|
-
.then((res) => res.json())
|
|
2916
|
-
.then(({ test }) => {
|
|
2917
|
-
this.isActive = true;
|
|
2918
|
-
this.test = test;
|
|
2919
|
-
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());
|
|
2920
|
-
this.createGreeting(test.title, test.reqMic, test.reqCamera);
|
|
2921
|
-
if (inProgress) {
|
|
2922
|
-
if (test.reqMic || test.reqCamera) {
|
|
2923
|
-
void this.userRecorder.startRecording(30, Quality.Standard, test.reqMic, test.reqCamera);
|
|
2924
|
-
}
|
|
2925
|
-
this.showWidget(test.description, test.tasks, true);
|
|
2926
|
-
this.showTaskSection();
|
|
2927
|
-
}
|
|
2928
|
-
})
|
|
2929
|
-
.then(() => id)
|
|
2930
|
-
.catch((err) => {
|
|
2931
|
-
console.log('OR: Error fetching test', err);
|
|
2932
|
-
});
|
|
2933
|
-
};
|
|
2934
|
-
this.hideTaskSection = () => false;
|
|
2935
|
-
this.showTaskSection = () => true;
|
|
2936
|
-
this.collapseWidget = () => false;
|
|
2937
|
-
this.removeGreeting = () => false;
|
|
2938
|
-
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
2939
|
-
this.toggleDescriptionVisibility = () => { };
|
|
2940
|
-
this.currentTaskIndex = 0;
|
|
2941
|
-
this.userRecorder = new Recorder(app);
|
|
2942
|
-
const sessionId = this.app.getSessionID();
|
|
2943
|
-
const savedSessionId = this.app.localStorage.getItem(SESSION_ID);
|
|
2944
|
-
if (sessionId !== savedSessionId) {
|
|
2945
|
-
this.app.localStorage.removeItem(this.storageKey);
|
|
2946
|
-
this.app.localStorage.removeItem(SESSION_ID);
|
|
2947
|
-
this.app.localStorage.removeItem(TEST_ID);
|
|
2948
|
-
this.app.localStorage.removeItem(TASK_IND);
|
|
2949
|
-
this.app.localStorage.removeItem(TEST_START);
|
|
2950
|
-
}
|
|
2951
|
-
const taskIndex = this.app.localStorage.getItem(TASK_IND);
|
|
2952
|
-
if (taskIndex) {
|
|
2953
|
-
this.currentTaskIndex = parseInt(taskIndex, 10);
|
|
2954
|
-
}
|
|
2955
|
-
}
|
|
2956
|
-
getTestId() {
|
|
2957
|
-
return this.testId;
|
|
2958
|
-
}
|
|
2959
|
-
createGreeting(title, micRequired, cameraRequired) {
|
|
2960
|
-
const titleElement = createElement('div', 'title', titleStyle, title);
|
|
2961
|
-
const descriptionElement = createElement('div', 'description', descriptionStyle, `Welcome, you're here to help us improve, not to be judged. Your insights matter!\n
|
|
2962
|
-
📹 We're recording this browser tab to learn from your experience.
|
|
2963
|
-
🎤 Please enable mic and camera if asked, to give us a complete picture.`);
|
|
2964
|
-
const buttonElement = createElement('div', 'button', buttonStyle, 'Read guidelines to begin');
|
|
2965
|
-
this.removeGreeting = () => {
|
|
2966
|
-
// this.container.innerHTML = ''
|
|
2967
|
-
if (micRequired || cameraRequired) {
|
|
2968
|
-
void this.userRecorder.startRecording(30, Quality.Standard, micRequired, cameraRequired);
|
|
2969
|
-
}
|
|
2970
|
-
this.container.removeChild(buttonElement);
|
|
2971
|
-
this.container.removeChild(descriptionElement);
|
|
2972
|
-
this.container.removeChild(titleElement);
|
|
2973
|
-
return false;
|
|
2974
|
-
};
|
|
2975
|
-
buttonElement.onclick = () => {
|
|
2976
|
-
this.removeGreeting();
|
|
2977
|
-
const durations = this.signalManager?.getDurations();
|
|
2978
|
-
if (durations && this.signalManager) {
|
|
2979
|
-
durations.testStart = this.app.timestamp();
|
|
2980
|
-
this.signalManager.setDurations(durations);
|
|
2981
|
-
}
|
|
2982
|
-
void this.signalManager?.signalTest('begin');
|
|
2983
|
-
this.container.style.fontFamily = `-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"`;
|
|
2984
|
-
Object.assign(this.container.style, containerWidgetStyle);
|
|
2985
|
-
this.showWidget(this.test?.guidelines || '', this.test?.tasks || []);
|
|
2986
|
-
};
|
|
2987
|
-
this.container.append(titleElement, descriptionElement, buttonElement);
|
|
2988
|
-
this.bg.appendChild(this.container);
|
|
2989
|
-
document.body.appendChild(this.bg);
|
|
2990
|
-
}
|
|
2991
|
-
showWidget(guidelines, tasks, inProgress) {
|
|
2992
|
-
this.container.innerHTML = '';
|
|
2993
|
-
Object.assign(this.bg.style, {
|
|
2994
|
-
position: 'fixed',
|
|
2995
|
-
zIndex: 99999999999999,
|
|
2996
|
-
right: '8px',
|
|
2997
|
-
left: 'unset',
|
|
2998
|
-
width: 'fit-content',
|
|
2999
|
-
top: '8px',
|
|
3000
|
-
height: 'fit-content',
|
|
3001
|
-
background: 'unset',
|
|
3002
|
-
display: 'unset',
|
|
3003
|
-
alignItems: 'unset',
|
|
3004
|
-
justifyContent: 'unset',
|
|
3005
|
-
});
|
|
3006
|
-
// Create title section
|
|
3007
|
-
const titleSection = this.createTitleSection();
|
|
3008
|
-
this.container.style.fontFamily = `-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"`;
|
|
3009
|
-
Object.assign(this.container.style, containerWidgetStyle);
|
|
3010
|
-
const descriptionSection = this.createDescriptionSection(guidelines);
|
|
3011
|
-
const tasksSection = this.createTasksSection(tasks);
|
|
3012
|
-
const stopButton = createElement('div', 'stop_bn_or', stopWidgetStyle, 'Abort Session');
|
|
3013
|
-
const stopContainer = createElement('div', 'stop_ct_or', { fontSize: '13px!important' });
|
|
3014
|
-
stopContainer.style.fontSize = '13px';
|
|
3015
|
-
stopContainer.append(stopButton);
|
|
3016
|
-
this.container.append(titleSection, descriptionSection, tasksSection, stopContainer);
|
|
3017
|
-
this.taskSection = tasksSection;
|
|
3018
|
-
this.descriptionSection = descriptionSection;
|
|
3019
|
-
this.stopButton = stopButton;
|
|
3020
|
-
this.stopButtonContainer = stopContainer;
|
|
3021
|
-
stopButton.onclick = () => {
|
|
3022
|
-
this.userRecorder.discard();
|
|
3023
|
-
void this.signalManager?.signalTest('skipped');
|
|
3024
|
-
document.body.removeChild(this.bg);
|
|
3025
|
-
window.close();
|
|
3026
|
-
};
|
|
3027
|
-
if (!inProgress) {
|
|
3028
|
-
this.hideTaskSection();
|
|
3029
|
-
}
|
|
3030
|
-
else {
|
|
3031
|
-
this.toggleDescriptionVisibility();
|
|
3032
|
-
}
|
|
3033
|
-
}
|
|
3034
|
-
createTitleSection() {
|
|
3035
|
-
const title = createElement('div', 'title', titleWidgetStyle);
|
|
3036
|
-
const leftIcon = generateGrid();
|
|
3037
|
-
const titleText = createElement('div', 'title_text', {
|
|
3038
|
-
maxWidth: '19rem',
|
|
3039
|
-
overflow: 'hidden',
|
|
3040
|
-
textOverflow: 'ellipsis',
|
|
3041
|
-
width: '100%',
|
|
3042
|
-
fontSize: 16,
|
|
3043
|
-
lineHeight: 'auto',
|
|
3044
|
-
cursor: 'pointer',
|
|
3045
|
-
}, this.test?.title);
|
|
3046
|
-
const rightIcon = generateChevron();
|
|
3047
|
-
title.append(leftIcon, titleText, rightIcon);
|
|
3048
|
-
const toggleWidget = (isVisible) => {
|
|
3049
|
-
this.widgetVisible = isVisible;
|
|
3050
|
-
this.container.style.fontFamily = `-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"`;
|
|
3051
|
-
Object.assign(this.container.style, this.widgetVisible
|
|
3052
|
-
? containerWidgetStyle
|
|
3053
|
-
: { border: 'none', background: 'none', padding: 0 });
|
|
3054
|
-
if (this.taskSection) {
|
|
3055
|
-
Object.assign(this.taskSection.style, this.widgetVisible ? descriptionWidgetStyle : { display: 'none' });
|
|
3056
|
-
}
|
|
3057
|
-
if (this.descriptionSection) {
|
|
3058
|
-
Object.assign(this.descriptionSection.style, this.widgetVisible ? descriptionWidgetStyle : { display: 'none' });
|
|
3059
|
-
}
|
|
3060
|
-
if (this.endSection) {
|
|
3061
|
-
Object.assign(this.endSection.style, this.widgetVisible ? descriptionWidgetStyle : { display: 'none' });
|
|
3062
|
-
}
|
|
3063
|
-
if (this.stopButton) {
|
|
3064
|
-
Object.assign(this.stopButton.style, this.widgetVisible ? stopWidgetStyle : { display: 'none' });
|
|
3065
|
-
}
|
|
3066
|
-
return isVisible;
|
|
3067
|
-
};
|
|
3068
|
-
const collapseWidget = () => {
|
|
3069
|
-
Object.assign(rightIcon.style, {
|
|
3070
|
-
transform: this.widgetVisible ? 'rotate(0deg)' : 'rotate(180deg)',
|
|
3071
|
-
});
|
|
3072
|
-
toggleWidget(!this.widgetVisible);
|
|
3073
|
-
};
|
|
3074
|
-
titleText.onclick = collapseWidget;
|
|
3075
|
-
rightIcon.onclick = collapseWidget;
|
|
3076
|
-
attachDND(this.bg, leftIcon);
|
|
3077
|
-
this.collapseWidget = () => toggleWidget(false);
|
|
3078
|
-
return title;
|
|
3079
|
-
}
|
|
3080
|
-
createDescriptionSection(guidelines) {
|
|
3081
|
-
const section = createElement('div', 'description_section_or', descriptionWidgetStyle);
|
|
3082
|
-
const titleContainer = createElement('div', 'description_s_title_or', sectionTitleStyle);
|
|
3083
|
-
const title = createElement('div', 'title', {
|
|
3084
|
-
fontSize: 13,
|
|
3085
|
-
fontWeight: 500,
|
|
3086
|
-
lineHeight: 'auto',
|
|
3087
|
-
}, 'Introduction & Guidelines');
|
|
3088
|
-
const icon = createElement('div', 'icon', symbolIcon, '-');
|
|
3089
|
-
const content = createElement('div', 'content', contentStyle);
|
|
3090
|
-
const descriptionC = createElement('div', 'text_description', {
|
|
3091
|
-
maxHeight: '250px',
|
|
3092
|
-
overflowY: 'auto',
|
|
3093
|
-
whiteSpace: 'pre-wrap',
|
|
3094
|
-
fontSize: 13,
|
|
3095
|
-
color: '#454545',
|
|
3096
|
-
lineHeight: 'auto',
|
|
3097
|
-
});
|
|
3098
|
-
descriptionC.innerHTML = guidelines;
|
|
3099
|
-
const button = createElement('div', 'button_begin_or', buttonWidgetStyle, 'Begin Test');
|
|
3100
|
-
titleContainer.append(title, icon);
|
|
3101
|
-
content.append(descriptionC, button);
|
|
3102
|
-
section.append(titleContainer, content);
|
|
3103
|
-
const toggleDescriptionVisibility = () => {
|
|
3104
|
-
this.widgetGuidelinesVisible = !this.widgetGuidelinesVisible;
|
|
3105
|
-
icon.textContent = this.widgetGuidelinesVisible ? '-' : '+';
|
|
3106
|
-
Object.assign(content.style, this.widgetGuidelinesVisible ? contentStyle : { display: 'none' });
|
|
3107
|
-
};
|
|
3108
|
-
titleContainer.onclick = toggleDescriptionVisibility;
|
|
3109
|
-
this.toggleDescriptionVisibility = () => {
|
|
3110
|
-
this.widgetGuidelinesVisible = false;
|
|
3111
|
-
icon.textContent = this.widgetGuidelinesVisible ? '-' : '+';
|
|
3112
|
-
Object.assign(content.style, this.widgetGuidelinesVisible ? contentStyle : { display: 'none' });
|
|
3113
|
-
content.removeChild(button);
|
|
3114
|
-
};
|
|
3115
|
-
button.onclick = () => {
|
|
3116
|
-
toggleDescriptionVisibility();
|
|
3117
|
-
if (this.test) {
|
|
3118
|
-
const durations = this.signalManager?.getDurations();
|
|
3119
|
-
const taskDurationInd = durations
|
|
3120
|
-
? durations.tasks.findIndex((t) => this.test && t.taskId === this.test.tasks[0].task_id)
|
|
3121
|
-
: null;
|
|
3122
|
-
if (durations && taskDurationInd === -1) {
|
|
3123
|
-
durations.tasks.push({
|
|
3124
|
-
taskId: this.test.tasks[0].task_id,
|
|
3125
|
-
started: this.app.timestamp(),
|
|
3126
|
-
});
|
|
3127
|
-
this.signalManager?.setDurations(durations);
|
|
3128
|
-
}
|
|
3129
|
-
void this.signalManager?.signalTask(this.test.tasks[0].task_id, 'begin');
|
|
3130
|
-
}
|
|
3131
|
-
this.showTaskSection();
|
|
3132
|
-
content.removeChild(button);
|
|
3133
|
-
};
|
|
3134
|
-
return section;
|
|
3135
|
-
}
|
|
3136
|
-
createTasksSection(tasks) {
|
|
3137
|
-
this.container.style.fontFamily = `-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"`;
|
|
3138
|
-
Object.assign(this.container.style, containerWidgetStyle);
|
|
3139
|
-
const section = createElement('div', 'task_section_or', descriptionWidgetStyle);
|
|
3140
|
-
const titleContainer = createElement('div', 'description_t_title_or', sectionTitleStyle);
|
|
3141
|
-
const title = createElement('div', 'title', {
|
|
3142
|
-
fontSize: '13px',
|
|
3143
|
-
fontWeight: '500',
|
|
3144
|
-
lineHeight: 'auto',
|
|
3145
|
-
}, 'Tasks');
|
|
3146
|
-
const icon = createElement('div', 'icon', symbolIcon, '-');
|
|
3147
|
-
const content = createElement('div', 'content', contentStyle);
|
|
3148
|
-
const pagination = createElement('div', 'pagination', paginationStyle);
|
|
3149
|
-
// const leftArrow = createElement('span', 'leftArrow', {}, '<')
|
|
3150
|
-
// const rightArrow = createElement('span', 'rightArrow', {}, '>')
|
|
3151
|
-
const taskCard = createElement('div', 'taskCard', taskDescriptionCard);
|
|
3152
|
-
const taskText = createElement('div', 'taskText', taskTextStyle);
|
|
3153
|
-
const taskDescription = createElement('div', 'taskDescription', taskDescriptionStyle);
|
|
3154
|
-
const taskButtons = createElement('div', 'taskButtons', taskButtonsRow);
|
|
3155
|
-
const inputTitle = createElement('div', 'taskText', taskTextStyle);
|
|
3156
|
-
inputTitle.textContent = 'Your answer';
|
|
3157
|
-
const inputArea = createElement('textarea', 'taskDescription', {
|
|
3158
|
-
resize: 'vertical',
|
|
3159
|
-
});
|
|
3160
|
-
const inputContainer = createElement('div', 'inputArea', taskDescriptionCard);
|
|
3161
|
-
inputContainer.append(inputTitle, inputArea);
|
|
3162
|
-
const closePanelButton = createElement('div', 'closePanelButton', taskButtonStyle, 'Collapse Panel');
|
|
3163
|
-
const nextButton = createElement('div', 'nextButton', taskButtonBorderedStyle, 'Done, Next');
|
|
3164
|
-
titleContainer.append(title, icon);
|
|
3165
|
-
taskCard.append(taskText, taskDescription);
|
|
3166
|
-
taskButtons.append(closePanelButton, nextButton);
|
|
3167
|
-
content.append(pagination, taskCard, inputContainer, taskButtons);
|
|
3168
|
-
section.append(titleContainer, content);
|
|
3169
|
-
const updateTaskContent = () => {
|
|
3170
|
-
const task = tasks[this.currentTaskIndex];
|
|
3171
|
-
taskText.textContent = task.title;
|
|
3172
|
-
taskDescription.textContent = task.description;
|
|
3173
|
-
if (task.allow_typing) {
|
|
3174
|
-
inputContainer.style.display = 'flex';
|
|
3175
|
-
}
|
|
3176
|
-
else {
|
|
3177
|
-
inputContainer.style.display = 'none';
|
|
3178
|
-
}
|
|
3179
|
-
};
|
|
3180
|
-
// pagination.appendChild(leftArrow)
|
|
3181
|
-
tasks.forEach((_, index) => {
|
|
3182
|
-
const pageNumber = createElement('span', `or_task_${index}`, {
|
|
3183
|
-
outline: '1px solid #efefef',
|
|
3184
|
-
fontSize: '13px',
|
|
3185
|
-
height: '24px',
|
|
3186
|
-
width: '24px',
|
|
3187
|
-
display: 'flex',
|
|
3188
|
-
flexDirection: 'column',
|
|
3189
|
-
alignItems: 'center',
|
|
3190
|
-
justifyContent: 'center',
|
|
3191
|
-
borderRadius: '6.25em',
|
|
3192
|
-
}, (index + 1).toString());
|
|
3193
|
-
pageNumber.id = `or_task_${index}`;
|
|
3194
|
-
pagination.append(pageNumber);
|
|
3195
|
-
});
|
|
3196
|
-
// pagination.appendChild(rightArrow)
|
|
3197
|
-
const toggleTasksVisibility = () => {
|
|
3198
|
-
this.widgetTasksVisible = !this.widgetTasksVisible;
|
|
3199
|
-
icon.textContent = this.widgetTasksVisible ? '-' : '+';
|
|
3200
|
-
Object.assign(content.style, this.widgetTasksVisible ? contentStyle : { display: 'none' });
|
|
3201
|
-
};
|
|
3202
|
-
this.hideTaskSection = () => {
|
|
3203
|
-
icon.textContent = '+';
|
|
3204
|
-
Object.assign(content.style, {
|
|
3205
|
-
display: 'none',
|
|
3206
|
-
});
|
|
3207
|
-
this.widgetTasksVisible = false;
|
|
3208
|
-
return false;
|
|
3209
|
-
};
|
|
3210
|
-
this.showTaskSection = () => {
|
|
3211
|
-
icon.textContent = '-';
|
|
3212
|
-
Object.assign(content.style, contentStyle);
|
|
3213
|
-
this.widgetTasksVisible = true;
|
|
3214
|
-
return true;
|
|
3215
|
-
};
|
|
3216
|
-
const highlightActive = () => {
|
|
3217
|
-
const activeTaskEl = document.getElementById(`or_task_${this.currentTaskIndex}`);
|
|
3218
|
-
if (activeTaskEl) {
|
|
3219
|
-
Object.assign(activeTaskEl.style, taskNumberActive);
|
|
3220
|
-
}
|
|
3221
|
-
for (let i = 0; i < this.currentTaskIndex; i++) {
|
|
3222
|
-
const taskEl = document.getElementById(`or_task_${i}`);
|
|
3223
|
-
if (taskEl) {
|
|
3224
|
-
Object.assign(taskEl.style, taskNumberDone);
|
|
3225
|
-
}
|
|
3226
|
-
}
|
|
3227
|
-
};
|
|
3228
|
-
titleContainer.onclick = toggleTasksVisibility;
|
|
3229
|
-
closePanelButton.onclick = this.collapseWidget;
|
|
3230
|
-
nextButton.onclick = () => {
|
|
3231
|
-
const textAnswer = tasks[this.currentTaskIndex].allow_typing ? inputArea.value : undefined;
|
|
3232
|
-
inputArea.value = '';
|
|
3233
|
-
void this.signalManager?.signalTask(tasks[this.currentTaskIndex].task_id, 'done', textAnswer);
|
|
3234
|
-
if (this.currentTaskIndex < tasks.length - 1) {
|
|
3235
|
-
this.currentTaskIndex++;
|
|
3236
|
-
updateTaskContent();
|
|
3237
|
-
const durations = this.signalManager?.getDurations();
|
|
3238
|
-
if (durations &&
|
|
3239
|
-
durations.tasks.findIndex((t) => t.taskId === tasks[this.currentTaskIndex].task_id) === -1) {
|
|
3240
|
-
durations.tasks.push({
|
|
3241
|
-
taskId: tasks[this.currentTaskIndex].task_id,
|
|
3242
|
-
started: this.app.timestamp(),
|
|
3243
|
-
});
|
|
3244
|
-
this.signalManager?.setDurations(durations);
|
|
3245
|
-
}
|
|
3246
|
-
void this.signalManager?.signalTask(tasks[this.currentTaskIndex].task_id, 'begin');
|
|
3247
|
-
highlightActive();
|
|
3248
|
-
}
|
|
3249
|
-
else {
|
|
3250
|
-
this.showEndSection();
|
|
3251
|
-
}
|
|
3252
|
-
this.app.localStorage.setItem('or_uxt_task_index', this.currentTaskIndex.toString());
|
|
3253
|
-
};
|
|
3254
|
-
setTimeout(() => {
|
|
3255
|
-
const firstTaskEl = document.getElementById('or_task_0');
|
|
3256
|
-
if (firstTaskEl) {
|
|
3257
|
-
Object.assign(firstTaskEl.style, taskNumberActive);
|
|
3258
|
-
}
|
|
3259
|
-
updateTaskContent();
|
|
3260
|
-
highlightActive();
|
|
3261
|
-
}, 1);
|
|
3262
|
-
return section;
|
|
3263
|
-
}
|
|
3264
|
-
showEndSection() {
|
|
3265
|
-
let isLoading = true;
|
|
3266
|
-
void this.signalManager?.signalTest('done');
|
|
3267
|
-
const section = createElement('div', 'end_section_or', endSectionStyle);
|
|
3268
|
-
const title = createElement('div', 'end_title_or', {
|
|
3269
|
-
fontSize: '1.25rem',
|
|
3270
|
-
fontWeight: '500',
|
|
3271
|
-
}, 'Thank you! 👍');
|
|
3272
|
-
const description = createElement('div', 'end_description_or', {}, this.test?.conclusion ??
|
|
3273
|
-
'Thank you for participating in our usability test. Your feedback has been captured and will be used to enhance our website. \n' +
|
|
3274
|
-
'\n' +
|
|
3275
|
-
'We appreciate your time and valuable input.');
|
|
3276
|
-
const button = createElement('div', 'end_button_or', buttonWidgetStyle, 'Submitting Feedback');
|
|
3277
|
-
const spinner = createSpinner();
|
|
3278
|
-
button.appendChild(spinner);
|
|
3279
|
-
if (this.test?.reqMic || this.test?.reqCamera) {
|
|
3280
|
-
void this.userRecorder
|
|
3281
|
-
.sendToAPI()
|
|
3282
|
-
.then(() => {
|
|
3283
|
-
button.removeChild(spinner);
|
|
3284
|
-
button.textContent = 'End Session';
|
|
3285
|
-
isLoading = false;
|
|
3286
|
-
})
|
|
3287
|
-
.catch((err) => {
|
|
3288
|
-
console.error(err);
|
|
3289
|
-
button.removeChild(spinner);
|
|
3290
|
-
button.textContent = 'End Session';
|
|
3291
|
-
isLoading = false;
|
|
3292
|
-
});
|
|
3293
|
-
}
|
|
3294
|
-
else {
|
|
3295
|
-
button.removeChild(spinner);
|
|
3296
|
-
button.textContent = 'End Session';
|
|
3297
|
-
isLoading = false;
|
|
3298
|
-
}
|
|
3299
|
-
if (this.taskSection) {
|
|
3300
|
-
this.container.removeChild(this.taskSection);
|
|
3301
|
-
}
|
|
3302
|
-
if (this.descriptionSection) {
|
|
3303
|
-
this.container.removeChild(this.descriptionSection);
|
|
2345
|
+
if (this.interval) {
|
|
2346
|
+
clearInterval(this.interval);
|
|
2347
|
+
this.interval = null;
|
|
3304
2348
|
}
|
|
3305
|
-
|
|
3306
|
-
this.container.removeChild(this.stopButtonContainer);
|
|
3307
|
-
}
|
|
3308
|
-
button.onclick = () => {
|
|
3309
|
-
if (isLoading)
|
|
3310
|
-
return;
|
|
3311
|
-
window.close();
|
|
3312
|
-
document.body.removeChild(this.bg);
|
|
3313
|
-
};
|
|
3314
|
-
section.append(title, description, button);
|
|
3315
|
-
this.endSection = section;
|
|
3316
|
-
this.container.append(section);
|
|
2349
|
+
this.observer.disconnect();
|
|
3317
2350
|
}
|
|
3318
2351
|
}
|
|
3319
2352
|
|
|
@@ -4156,6 +3189,13 @@ async function parseUseEl(useElement, mode, domParser) {
|
|
|
4156
3189
|
return;
|
|
4157
3190
|
}
|
|
4158
3191
|
let [url, symbolId] = href.split('#');
|
|
3192
|
+
if (!url && !symbolId) {
|
|
3193
|
+
console.warn('Openreplay: Invalid xlink:href or href found on <use>.');
|
|
3194
|
+
return;
|
|
3195
|
+
}
|
|
3196
|
+
if (iconCache[symbolId]) {
|
|
3197
|
+
return iconCache[symbolId];
|
|
3198
|
+
}
|
|
4159
3199
|
// happens if svg spritemap is local, fastest case for us
|
|
4160
3200
|
if (!url && symbolId) {
|
|
4161
3201
|
const hasHashtag = href.startsWith('#');
|
|
@@ -4181,13 +3221,6 @@ async function parseUseEl(useElement, mode, domParser) {
|
|
|
4181
3221
|
return;
|
|
4182
3222
|
}
|
|
4183
3223
|
}
|
|
4184
|
-
if (!url && !symbolId) {
|
|
4185
|
-
console.warn('Openreplay: Invalid xlink:href or href found on <use>.');
|
|
4186
|
-
return;
|
|
4187
|
-
}
|
|
4188
|
-
if (iconCache[symbolId]) {
|
|
4189
|
-
return iconCache[symbolId];
|
|
4190
|
-
}
|
|
4191
3224
|
let svgDoc;
|
|
4192
3225
|
if (svgUrlCache[url]) {
|
|
4193
3226
|
if (svgUrlCache[url] === 1) {
|
|
@@ -4287,6 +3320,7 @@ class Observer {
|
|
|
4287
3320
|
this.indexes = [];
|
|
4288
3321
|
this.attributesMap = new Map();
|
|
4289
3322
|
this.textSet = new Set();
|
|
3323
|
+
this.slotMap = new Map();
|
|
4290
3324
|
this.disableSprites = false;
|
|
4291
3325
|
/**
|
|
4292
3326
|
* this option means that, instead of using link element with href to load css,
|
|
@@ -4296,6 +3330,9 @@ class Observer {
|
|
|
4296
3330
|
this.inlineRemoteCss = false;
|
|
4297
3331
|
this.inlinerOptions = undefined;
|
|
4298
3332
|
this.domParser = new DOMParser();
|
|
3333
|
+
this.throttling = true;
|
|
3334
|
+
this.throttledSetNodeData = throttleWithTrailing((id, parentElement, data) => this.sendNodeData(id, parentElement, data), 30);
|
|
3335
|
+
this.throttling = !Boolean(options.disableThrottling);
|
|
4299
3336
|
this.disableSprites = Boolean(options.disableSprites);
|
|
4300
3337
|
this.inlineRemoteCss = Boolean(options.inlineRemoteCss);
|
|
4301
3338
|
this.inlinerOptions = options.inlinerOptions;
|
|
@@ -4476,6 +3513,18 @@ class Observer {
|
|
|
4476
3513
|
}
|
|
4477
3514
|
bindNode(node) {
|
|
4478
3515
|
const [id, isNew] = this.app.nodes.registerNode(node);
|
|
3516
|
+
if (isElementNode(node) && hasTag(node, 'slot')) {
|
|
3517
|
+
this.app.nodes.attachNodeListener(node, 'slotchange', () => {
|
|
3518
|
+
const sl = node;
|
|
3519
|
+
sl.assignedNodes({ flatten: true }).forEach((n) => {
|
|
3520
|
+
const nid = this.app.nodes.getID(n);
|
|
3521
|
+
if (nid !== undefined) {
|
|
3522
|
+
this.recents.set(nid, RecentsType.Removed);
|
|
3523
|
+
this.commitNode(nid);
|
|
3524
|
+
}
|
|
3525
|
+
});
|
|
3526
|
+
});
|
|
3527
|
+
}
|
|
4479
3528
|
if (isNew) {
|
|
4480
3529
|
this.recents.set(id, RecentsType.New);
|
|
4481
3530
|
}
|
|
@@ -4506,6 +3555,9 @@ class Observer {
|
|
|
4506
3555
|
}
|
|
4507
3556
|
unbindTree(node) {
|
|
4508
3557
|
const id = this.app.nodes.unregisterNode(node);
|
|
3558
|
+
if (id !== undefined) {
|
|
3559
|
+
this.slotMap.delete(id);
|
|
3560
|
+
}
|
|
4509
3561
|
if (id !== undefined && this.recents.get(id) === RecentsType.Removed) {
|
|
4510
3562
|
// Sending RemoveNode only for parent to maintain
|
|
4511
3563
|
this.app.send(RemoveNode(id));
|
|
@@ -4534,8 +3586,15 @@ class Observer {
|
|
|
4534
3586
|
if (isRootNode(node)) {
|
|
4535
3587
|
return true;
|
|
4536
3588
|
}
|
|
4537
|
-
|
|
4538
|
-
|
|
3589
|
+
let slot = node.assignedSlot;
|
|
3590
|
+
let isLightDom = false;
|
|
3591
|
+
if (slot) {
|
|
3592
|
+
// Check if the node is in light DOM (not in shadow DOM)
|
|
3593
|
+
// This is a workaround for the issue with shadow DOM and slots
|
|
3594
|
+
// where the slot is not assigned to the node in shadow DOM.
|
|
3595
|
+
isLightDom = node.getRootNode() instanceof ShadowRoot;
|
|
3596
|
+
}
|
|
3597
|
+
const parent = node.parentNode;
|
|
4539
3598
|
let parentID;
|
|
4540
3599
|
// Disable parent check for the upper context HTMLHtmlElement, because it is root there... (before)
|
|
4541
3600
|
// TODO: get rid of "special" cases (there is an issue with CreateDocument altered behaviour though)
|
|
@@ -4547,7 +3606,15 @@ class Observer {
|
|
|
4547
3606
|
this.unbindTree(node);
|
|
4548
3607
|
return false;
|
|
4549
3608
|
}
|
|
4550
|
-
|
|
3609
|
+
if (isLightDom && slot) {
|
|
3610
|
+
parentID = this.app.nodes.getID(slot);
|
|
3611
|
+
// in light dom, we don't "slot" the node,
|
|
3612
|
+
// but rather use the slot as a parent
|
|
3613
|
+
slot = null;
|
|
3614
|
+
}
|
|
3615
|
+
else {
|
|
3616
|
+
parentID = this.app.nodes.getID(parent);
|
|
3617
|
+
}
|
|
4551
3618
|
if (parentID === undefined) {
|
|
4552
3619
|
this.unbindTree(node);
|
|
4553
3620
|
return false;
|
|
@@ -4607,12 +3674,35 @@ class Observer {
|
|
|
4607
3674
|
else if (isTextNode(node)) {
|
|
4608
3675
|
// for text node id != 0, hence parentID !== undefined and parent is Element
|
|
4609
3676
|
this.app.send(CreateTextNode(id, parentID, index));
|
|
4610
|
-
this.
|
|
3677
|
+
if (this.throttling) {
|
|
3678
|
+
this.throttledSetNodeData(id, parent, node.data);
|
|
3679
|
+
}
|
|
3680
|
+
else {
|
|
3681
|
+
this.sendNodeData(id, parent, node.data);
|
|
3682
|
+
}
|
|
3683
|
+
}
|
|
3684
|
+
if (slot) {
|
|
3685
|
+
const slotID = this.app.nodes.getID(slot);
|
|
3686
|
+
if (slotID !== undefined) {
|
|
3687
|
+
this.slotMap.set(id, slotID);
|
|
3688
|
+
this.app.send(SetNodeSlot(id, slotID));
|
|
3689
|
+
}
|
|
4611
3690
|
}
|
|
4612
3691
|
return true;
|
|
4613
3692
|
}
|
|
4614
3693
|
if (recentsType === RecentsType.Removed && parentID !== undefined) {
|
|
4615
3694
|
this.app.send(MoveNode(id, parentID, index));
|
|
3695
|
+
if (slot) {
|
|
3696
|
+
const slotID = this.app.nodes.getID(slot);
|
|
3697
|
+
if (slotID !== undefined && this.slotMap.get(id) !== slotID) {
|
|
3698
|
+
this.slotMap.set(id, slotID);
|
|
3699
|
+
this.app.send(SetNodeSlot(id, slotID));
|
|
3700
|
+
}
|
|
3701
|
+
}
|
|
3702
|
+
else if (this.slotMap.has(id)) {
|
|
3703
|
+
this.slotMap.delete(id);
|
|
3704
|
+
this.app.send(SetNodeSlot(id, 0));
|
|
3705
|
+
}
|
|
4616
3706
|
}
|
|
4617
3707
|
const attr = this.attributesMap.get(id);
|
|
4618
3708
|
if (attr !== undefined) {
|
|
@@ -4628,7 +3718,12 @@ class Observer {
|
|
|
4628
3718
|
throw 'commitNode: node is not a text';
|
|
4629
3719
|
}
|
|
4630
3720
|
// for text node id != 0, hence parent is Element
|
|
4631
|
-
this.
|
|
3721
|
+
if (this.throttling) {
|
|
3722
|
+
this.throttledSetNodeData(id, parent, node.data);
|
|
3723
|
+
}
|
|
3724
|
+
else {
|
|
3725
|
+
this.sendNodeData(id, parent, node.data);
|
|
3726
|
+
}
|
|
4632
3727
|
}
|
|
4633
3728
|
return true;
|
|
4634
3729
|
}
|
|
@@ -4671,6 +3766,7 @@ class Observer {
|
|
|
4671
3766
|
disconnect() {
|
|
4672
3767
|
this.observer.disconnect();
|
|
4673
3768
|
this.clear();
|
|
3769
|
+
this.throttledSetNodeData.clear();
|
|
4674
3770
|
}
|
|
4675
3771
|
}
|
|
4676
3772
|
|
|
@@ -4774,16 +3870,18 @@ class IFrameOffsets {
|
|
|
4774
3870
|
|
|
4775
3871
|
var InlineCssMode;
|
|
4776
3872
|
(function (InlineCssMode) {
|
|
3873
|
+
InlineCssMode[InlineCssMode["Unset"] = -1] = "Unset";
|
|
4777
3874
|
/** default behavior -- will parse and cache the css file on backend */
|
|
4778
3875
|
InlineCssMode[InlineCssMode["Disabled"] = 0] = "Disabled";
|
|
4779
3876
|
/** will attempt to record the linked css file as AdoptedStyleSheet object */
|
|
4780
3877
|
InlineCssMode[InlineCssMode["Inline"] = 1] = "Inline";
|
|
4781
|
-
/** will fetch the file, then
|
|
3878
|
+
/** will fetch the file, then simulate AdoptedStyleSheets behavior programmaticaly for the replay */
|
|
4782
3879
|
InlineCssMode[InlineCssMode["InlineFetched"] = 2] = "InlineFetched";
|
|
4783
3880
|
/** will fetch the file, then save it as plain css inside <style> node */
|
|
4784
3881
|
InlineCssMode[InlineCssMode["PlainFetched"] = 3] = "PlainFetched";
|
|
4785
3882
|
})(InlineCssMode || (InlineCssMode = {}));
|
|
4786
|
-
|
|
3883
|
+
const localhostStylesDoc = 'https://docs.openreplay.com/en/troubleshooting/localhost/';
|
|
3884
|
+
function getInlineOptions(mode, logger) {
|
|
4787
3885
|
switch (mode) {
|
|
4788
3886
|
case InlineCssMode.Inline:
|
|
4789
3887
|
return {
|
|
@@ -4809,6 +3907,27 @@ function getInlineOptions(mode) {
|
|
|
4809
3907
|
forcePlain: true,
|
|
4810
3908
|
},
|
|
4811
3909
|
};
|
|
3910
|
+
case InlineCssMode.Unset:
|
|
3911
|
+
const isLocalhost = /^https?:\/\/(localhost|127\.0\.0\.1)(:\d+)?\/?/.test(window.location.href);
|
|
3912
|
+
if (isLocalhost) {
|
|
3913
|
+
logger(`Enabling InlineCssMode by default on localhost to preserve css styles, refer to ${localhostStylesDoc} for details, set InlineCssMode to 0 to skip this behavior`);
|
|
3914
|
+
return {
|
|
3915
|
+
inlineRemoteCss: true,
|
|
3916
|
+
inlinerOptions: {
|
|
3917
|
+
forceFetch: false,
|
|
3918
|
+
forcePlain: false,
|
|
3919
|
+
},
|
|
3920
|
+
};
|
|
3921
|
+
}
|
|
3922
|
+
else {
|
|
3923
|
+
return {
|
|
3924
|
+
inlineRemoteCss: false,
|
|
3925
|
+
inlinerOptions: {
|
|
3926
|
+
forceFetch: false,
|
|
3927
|
+
forcePlain: false,
|
|
3928
|
+
},
|
|
3929
|
+
};
|
|
3930
|
+
}
|
|
4812
3931
|
case InlineCssMode.Disabled:
|
|
4813
3932
|
default:
|
|
4814
3933
|
return {
|
|
@@ -4830,7 +3949,8 @@ class TopObserver extends Observer {
|
|
|
4830
3949
|
}, params.options);
|
|
4831
3950
|
const observerOptions = {
|
|
4832
3951
|
disableSprites: opts.disableSprites,
|
|
4833
|
-
|
|
3952
|
+
disableThrottling: opts.disableThrottling,
|
|
3953
|
+
...getInlineOptions(opts.inlineCss, console.warn),
|
|
4834
3954
|
};
|
|
4835
3955
|
super(params.app, true, observerOptions);
|
|
4836
3956
|
this.iframeOffsets = new IFrameOffsets();
|
|
@@ -5238,9 +4358,8 @@ class Ticker {
|
|
|
5238
4358
|
* this value is injected during build time via rollup
|
|
5239
4359
|
* */
|
|
5240
4360
|
// @ts-ignore
|
|
5241
|
-
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 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 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 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(\"q_end\"===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,a=null,u=h.NotActive;function o(t){a&&a.finaliseBatch(t)}function c(){return new Promise((t=>{u=h.Stopping,null!==p&&(clearInterval(p),p=null),a&&(a.clean(),a=null),r&&(r.clean(),setTimeout((()=>{r=null}),20)),setTimeout((()=>{u=h.NotActive,t(null)}),100)}))}function g(){[h.Stopped,h.Stopping].includes(u)||(postMessage(\"a_stop\"),c().then((()=>{postMessage(\"a_start\")})))}let l,p=null;self.onmessage=({data:s})=>{if(\"stop\"===s)return o(),void c().then((()=>{u=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?(u=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),a=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)),u=h.Active):\"auth\"===s.type?r?a?(r.authorise(s.token),void(s.beaconSizeLimit&&a.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(a){const t=a;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";
|
|
4361
|
+
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";
|
|
5242
4362
|
const CANCELED = 'canceled';
|
|
5243
|
-
const uxtStorageKey = 'or_uxt_active';
|
|
5244
4363
|
const bufferStorageKey = 'or_buffer_1';
|
|
5245
4364
|
const UnsuccessfulStart = (reason) => ({ reason, success: false });
|
|
5246
4365
|
const SuccessfulStart = (body) => ({ ...body, success: true });
|
|
@@ -5292,7 +4411,7 @@ class App {
|
|
|
5292
4411
|
this.stopCallbacks = [];
|
|
5293
4412
|
this.commitCallbacks = [];
|
|
5294
4413
|
this.activityState = ActivityState.NotActive;
|
|
5295
|
-
this.version = '
|
|
4414
|
+
this.version = '17.0.0'; // TODO: version compatability check inside each plugin.
|
|
5296
4415
|
this.socketMode = false;
|
|
5297
4416
|
this.compressionThreshold = 24 * 1000;
|
|
5298
4417
|
this.bc = null;
|
|
@@ -5303,7 +4422,6 @@ class App {
|
|
|
5303
4422
|
this.pageFrames = [];
|
|
5304
4423
|
this.frameOderNumber = 0;
|
|
5305
4424
|
this.frameLevel = 0;
|
|
5306
|
-
this.features = {};
|
|
5307
4425
|
this.emptyBatchCounter = 0;
|
|
5308
4426
|
/** used by child iframes for crossdomain only */
|
|
5309
4427
|
this.parentActive = false;
|
|
@@ -5395,13 +4513,6 @@ class App {
|
|
|
5395
4513
|
}
|
|
5396
4514
|
};
|
|
5397
4515
|
void signalId();
|
|
5398
|
-
if (this.active()) {
|
|
5399
|
-
// @ts-ignore
|
|
5400
|
-
event.source?.postMessage({ line: proto.startIframe }, '*');
|
|
5401
|
-
}
|
|
5402
|
-
else {
|
|
5403
|
-
this.addCommand(proto.startIframe);
|
|
5404
|
-
}
|
|
5405
4516
|
}
|
|
5406
4517
|
/**
|
|
5407
4518
|
* proxying messages from iframe to main body, so they can be in one batch (same indexes, etc)
|
|
@@ -5409,7 +4520,8 @@ class App {
|
|
|
5409
4520
|
* */
|
|
5410
4521
|
if (data.line === proto.iframeBatch) {
|
|
5411
4522
|
const msgBatch = data.messages;
|
|
5412
|
-
const mappedMessages =
|
|
4523
|
+
const mappedMessages = [];
|
|
4524
|
+
msgBatch.forEach((msg) => {
|
|
5413
4525
|
if (msg[0] === 20 /* MType.MouseMove */) {
|
|
5414
4526
|
let fixedMessage = msg;
|
|
5415
4527
|
this.pageFrames.forEach((frame) => {
|
|
@@ -5419,7 +4531,7 @@ class App {
|
|
|
5419
4531
|
fixedMessage = [type, x + left, y + top];
|
|
5420
4532
|
}
|
|
5421
4533
|
});
|
|
5422
|
-
|
|
4534
|
+
mappedMessages.push(fixedMessage);
|
|
5423
4535
|
}
|
|
5424
4536
|
if (msg[0] === 68 /* MType.MouseClick */) {
|
|
5425
4537
|
let fixedMessage = msg;
|
|
@@ -5445,9 +4557,11 @@ class App {
|
|
|
5445
4557
|
];
|
|
5446
4558
|
}
|
|
5447
4559
|
});
|
|
5448
|
-
|
|
4560
|
+
mappedMessages.push(fixedMessage);
|
|
4561
|
+
}
|
|
4562
|
+
if (![28 /* MType.UserID */, 29 /* MType.UserAnonymousID */, 30 /* MType.Metadata */].includes(msg[0])) {
|
|
4563
|
+
mappedMessages.push(msg);
|
|
5449
4564
|
}
|
|
5450
|
-
return msg;
|
|
5451
4565
|
});
|
|
5452
4566
|
this.messages.push(...mappedMessages);
|
|
5453
4567
|
}
|
|
@@ -5544,211 +4658,50 @@ class App {
|
|
|
5544
4658
|
this.conditionsManager?.processMessage(message);
|
|
5545
4659
|
}
|
|
5546
4660
|
else {
|
|
5547
|
-
this.messages.push(message);
|
|
5548
|
-
}
|
|
5549
|
-
// TODO: commit on start if there were `urgent` sends;
|
|
5550
|
-
// Clarify where urgent can be used for;
|
|
5551
|
-
// Clarify workflow for each type of message in case it was sent before start
|
|
5552
|
-
// (like Fetch before start; maybe add an option "preCapture: boolean" or sth alike)
|
|
5553
|
-
// Careful: `this.delay` is equal to zero before start so all Timestamp-s will have to be updated on start
|
|
5554
|
-
if (this.activityState === ActivityState.Active && urgent) {
|
|
5555
|
-
this.commit();
|
|
5556
|
-
}
|
|
5557
|
-
};
|
|
5558
|
-
this.coldStartCommitN = 0;
|
|
5559
|
-
this.delay = 0;
|
|
5560
|
-
this.attachStartCallback = (cb, useSafe = false) => {
|
|
5561
|
-
if (useSafe) {
|
|
5562
|
-
cb = this.safe(cb);
|
|
5563
|
-
}
|
|
5564
|
-
this.startCallbacks.push(cb);
|
|
5565
|
-
};
|
|
5566
|
-
this.attachStopCallback = (cb, useSafe = false) => {
|
|
5567
|
-
if (useSafe) {
|
|
5568
|
-
cb = this.safe(cb);
|
|
5569
|
-
}
|
|
5570
|
-
this.stopCallbacks.push(cb);
|
|
5571
|
-
};
|
|
5572
|
-
this.attachEventListener = (target, type, listener, useSafe = true, useCapture = true) => {
|
|
5573
|
-
if (useSafe) {
|
|
5574
|
-
listener = this.safe(listener);
|
|
5575
|
-
}
|
|
5576
|
-
const createListener = () => target
|
|
5577
|
-
? createEventListener(target, type, listener, useCapture, this.options.forceNgOff)
|
|
5578
|
-
: null;
|
|
5579
|
-
const deleteListener = () => target
|
|
5580
|
-
? deleteEventListener(target, type, listener, useCapture, this.options.forceNgOff)
|
|
5581
|
-
: null;
|
|
5582
|
-
this.attachStartCallback(createListener, useSafe);
|
|
5583
|
-
this.attachStopCallback(deleteListener, useSafe);
|
|
5584
|
-
};
|
|
5585
|
-
this.coldInterval = null;
|
|
5586
|
-
this.orderNumber = 0;
|
|
5587
|
-
this.coldStartTs = 0;
|
|
5588
|
-
this.singleBuffer = false;
|
|
5589
|
-
/**
|
|
5590
|
-
* start buffering messages without starting the actual session, which gives
|
|
5591
|
-
* user 30 seconds to "activate" and record session by calling `start()` on conditional trigger,
|
|
5592
|
-
* and we will then send buffered batch, so it won't get lost
|
|
5593
|
-
* */
|
|
5594
|
-
this.coldStart = async (startOpts = {}, conditional) => {
|
|
5595
|
-
this.singleBuffer = false;
|
|
5596
|
-
const second = 1000;
|
|
5597
|
-
const isNewSession = this.checkSessionToken(startOpts.forceNew);
|
|
5598
|
-
if (conditional) {
|
|
5599
|
-
await this.setupConditionalStart(startOpts);
|
|
5600
|
-
}
|
|
5601
|
-
const cycle = () => {
|
|
5602
|
-
this.orderNumber += 1;
|
|
5603
|
-
adjustTimeOrigin();
|
|
5604
|
-
this.coldStartTs = now();
|
|
5605
|
-
if (this.orderNumber % 2 === 0) {
|
|
5606
|
-
this.bufferedMessages1.length = 0;
|
|
5607
|
-
this.bufferedMessages1.push(Timestamp(this.timestamp()));
|
|
5608
|
-
this.bufferedMessages1.push(TabData(this.session.getTabId()));
|
|
5609
|
-
}
|
|
5610
|
-
else {
|
|
5611
|
-
this.bufferedMessages2.length = 0;
|
|
5612
|
-
this.bufferedMessages2.push(Timestamp(this.timestamp()));
|
|
5613
|
-
this.bufferedMessages2.push(TabData(this.session.getTabId()));
|
|
5614
|
-
}
|
|
5615
|
-
this.stop(false);
|
|
5616
|
-
this.activityState = ActivityState.ColdStart;
|
|
5617
|
-
if (startOpts.sessionHash) {
|
|
5618
|
-
this.session.applySessionHash(startOpts.sessionHash);
|
|
5619
|
-
}
|
|
5620
|
-
if (startOpts.forceNew) {
|
|
5621
|
-
this.session.reset();
|
|
5622
|
-
}
|
|
5623
|
-
this.session.assign({
|
|
5624
|
-
userID: startOpts.userID,
|
|
5625
|
-
metadata: startOpts.metadata,
|
|
5626
|
-
});
|
|
5627
|
-
if (!isNewSession) {
|
|
5628
|
-
this.debug.log('continuing session on new tab', this.session.getTabId());
|
|
5629
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
5630
|
-
this.send(TabChange(this.session.getTabId()));
|
|
5631
|
-
}
|
|
5632
|
-
this.observer.observe();
|
|
5633
|
-
this.ticker.start();
|
|
5634
|
-
};
|
|
5635
|
-
this.coldInterval = setInterval(() => {
|
|
5636
|
-
cycle();
|
|
5637
|
-
}, 30 * second);
|
|
5638
|
-
cycle();
|
|
5639
|
-
};
|
|
5640
|
-
this.setupConditionalStart = async (startOpts) => {
|
|
5641
|
-
this.conditionsManager = new ConditionsManager(this, startOpts);
|
|
5642
|
-
const r = await fetch(this.options.ingestPoint + '/v1/web/start', {
|
|
5643
|
-
method: 'POST',
|
|
5644
|
-
headers: {
|
|
5645
|
-
'Content-Type': 'application/json',
|
|
5646
|
-
},
|
|
5647
|
-
body: JSON.stringify({
|
|
5648
|
-
...this.getTrackerInfo(),
|
|
5649
|
-
timestamp: now(),
|
|
5650
|
-
doNotRecord: true,
|
|
5651
|
-
bufferDiff: 0,
|
|
5652
|
-
userID: this.session.getInfo().userID,
|
|
5653
|
-
token: undefined,
|
|
5654
|
-
deviceMemory,
|
|
5655
|
-
jsHeapSizeLimit,
|
|
5656
|
-
timezone: getTimezone(),
|
|
5657
|
-
width: window.screen.width,
|
|
5658
|
-
height: window.screen.height,
|
|
5659
|
-
}),
|
|
5660
|
-
});
|
|
5661
|
-
const {
|
|
5662
|
-
// this token is needed to fetch conditions and flags,
|
|
5663
|
-
// but it can't be used to record a session
|
|
5664
|
-
token, userBrowser, userCity, userCountry, userDevice, userOS, userState, projectID, features, } = await r.json();
|
|
5665
|
-
this.features = features ? features : this.features;
|
|
5666
|
-
this.session.assign({ projectID });
|
|
5667
|
-
this.session.setUserInfo({
|
|
5668
|
-
userBrowser,
|
|
5669
|
-
userCity,
|
|
5670
|
-
userCountry,
|
|
5671
|
-
userDevice,
|
|
5672
|
-
userOS,
|
|
5673
|
-
userState,
|
|
5674
|
-
});
|
|
5675
|
-
const onStartInfo = { sessionToken: token, userUUID: '', sessionID: '' };
|
|
5676
|
-
this.startCallbacks.forEach((cb) => cb(onStartInfo));
|
|
5677
|
-
await this.conditionsManager?.fetchConditions(projectID, token);
|
|
5678
|
-
if (this.features['feature-flags']) {
|
|
5679
|
-
await this.featureFlags.reloadFlags(token);
|
|
5680
|
-
this.conditionsManager?.processFlags(this.featureFlags.flags);
|
|
5681
|
-
}
|
|
5682
|
-
await this.tagWatcher.fetchTags(this.options.ingestPoint, token);
|
|
5683
|
-
};
|
|
5684
|
-
this.onSessionSent = () => {
|
|
5685
|
-
return;
|
|
5686
|
-
};
|
|
5687
|
-
/**
|
|
5688
|
-
* Starts offline session recording
|
|
5689
|
-
* @param {Object} startOpts - options for session start, same as .start()
|
|
5690
|
-
* @param {Function} onSessionSent - callback that will be called once session is fully sent
|
|
5691
|
-
* */
|
|
5692
|
-
this.offlineRecording = (startOpts = {}, onSessionSent) => {
|
|
5693
|
-
this.onSessionSent = onSessionSent;
|
|
5694
|
-
this.singleBuffer = true;
|
|
5695
|
-
const isNewSession = this.checkSessionToken(startOpts.forceNew);
|
|
5696
|
-
adjustTimeOrigin();
|
|
5697
|
-
this.coldStartTs = now();
|
|
5698
|
-
const saverBuffer = this.localStorage.getItem(bufferStorageKey);
|
|
5699
|
-
if (saverBuffer) {
|
|
5700
|
-
const data = JSON.parse(saverBuffer);
|
|
5701
|
-
this.bufferedMessages1 = Array.isArray(data) ? data : this.bufferedMessages1;
|
|
5702
|
-
this.localStorage.removeItem(bufferStorageKey);
|
|
5703
|
-
}
|
|
5704
|
-
this.bufferedMessages1.push(Timestamp(this.timestamp()));
|
|
5705
|
-
this.bufferedMessages1.push(TabData(this.session.getTabId()));
|
|
5706
|
-
this.activityState = ActivityState.ColdStart;
|
|
5707
|
-
if (startOpts.sessionHash) {
|
|
5708
|
-
this.session.applySessionHash(startOpts.sessionHash);
|
|
4661
|
+
this.messages.push(message);
|
|
5709
4662
|
}
|
|
5710
|
-
if
|
|
5711
|
-
|
|
4663
|
+
// TODO: commit on start if there were `urgent` sends;
|
|
4664
|
+
// Clarify where urgent can be used for;
|
|
4665
|
+
// Clarify workflow for each type of message in case it was sent before start
|
|
4666
|
+
// (like Fetch before start; maybe add an option "preCapture: boolean" or sth alike)
|
|
4667
|
+
// Careful: `this.delay` is equal to zero before start so all Timestamp-s will have to be updated on start
|
|
4668
|
+
if (this.activityState === ActivityState.Active && urgent) {
|
|
4669
|
+
this.commit();
|
|
5712
4670
|
}
|
|
5713
|
-
|
|
5714
|
-
|
|
5715
|
-
|
|
5716
|
-
|
|
5717
|
-
|
|
5718
|
-
|
|
5719
|
-
if (!isNewSession) {
|
|
5720
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
5721
|
-
this.send(TabChange(this.session.getTabId()));
|
|
4671
|
+
};
|
|
4672
|
+
this.coldStartCommitN = 0;
|
|
4673
|
+
this.delay = 0;
|
|
4674
|
+
this.attachStartCallback = (cb, useSafe = false) => {
|
|
4675
|
+
if (useSafe) {
|
|
4676
|
+
cb = this.safe(cb);
|
|
5722
4677
|
}
|
|
5723
|
-
this.
|
|
5724
|
-
this.ticker.start();
|
|
5725
|
-
return {
|
|
5726
|
-
saveBuffer: this.saveBuffer,
|
|
5727
|
-
getBuffer: this.getBuffer,
|
|
5728
|
-
setBuffer: this.setBuffer,
|
|
5729
|
-
};
|
|
4678
|
+
this.startCallbacks.push(cb);
|
|
5730
4679
|
};
|
|
5731
|
-
|
|
5732
|
-
|
|
5733
|
-
|
|
5734
|
-
|
|
5735
|
-
|
|
5736
|
-
* Keeping the size of local storage reasonable is up to the end users of this library
|
|
5737
|
-
* */
|
|
5738
|
-
this.saveBuffer = () => {
|
|
5739
|
-
this.localStorage.setItem(bufferStorageKey, JSON.stringify(this.bufferedMessages1));
|
|
4680
|
+
this.attachStopCallback = (cb, useSafe = false) => {
|
|
4681
|
+
if (useSafe) {
|
|
4682
|
+
cb = this.safe(cb);
|
|
4683
|
+
}
|
|
4684
|
+
this.stopCallbacks.push(cb);
|
|
5740
4685
|
};
|
|
5741
|
-
|
|
5742
|
-
|
|
5743
|
-
|
|
5744
|
-
|
|
5745
|
-
|
|
4686
|
+
this.attachEventListener = (target, type, listener, useSafe = true, useCapture = true) => {
|
|
4687
|
+
if (useSafe) {
|
|
4688
|
+
listener = this.safe(listener);
|
|
4689
|
+
}
|
|
4690
|
+
const createListener = () => target
|
|
4691
|
+
? createEventListener(target, type, listener, useCapture, this.options.forceNgOff)
|
|
4692
|
+
: null;
|
|
4693
|
+
const deleteListener = () => target
|
|
4694
|
+
? deleteEventListener(target, type, listener, useCapture, this.options.forceNgOff)
|
|
4695
|
+
: null;
|
|
4696
|
+
this.attachStartCallback(createListener, useSafe);
|
|
4697
|
+
this.attachStopCallback(deleteListener, useSafe);
|
|
5746
4698
|
};
|
|
5747
|
-
|
|
5748
|
-
|
|
5749
|
-
|
|
5750
|
-
this.
|
|
5751
|
-
|
|
4699
|
+
this.coldInterval = null;
|
|
4700
|
+
this.orderNumber = 0;
|
|
4701
|
+
this.coldStartTs = 0;
|
|
4702
|
+
this.singleBuffer = false;
|
|
4703
|
+
this.onSessionSent = () => {
|
|
4704
|
+
return;
|
|
5752
4705
|
};
|
|
5753
4706
|
this.prevOpts = {};
|
|
5754
4707
|
this.restartCanvasTracking = () => {
|
|
@@ -5779,7 +4732,6 @@ class App {
|
|
|
5779
4732
|
});
|
|
5780
4733
|
});
|
|
5781
4734
|
};
|
|
5782
|
-
this.onUxtCb = [];
|
|
5783
4735
|
this.contextId = Math.random().toString(36).slice(2);
|
|
5784
4736
|
this.projectKey = projectKey;
|
|
5785
4737
|
this.networkOptions = options.network;
|
|
@@ -5814,8 +4766,9 @@ class App {
|
|
|
5814
4766
|
useAnimationFrame: false,
|
|
5815
4767
|
},
|
|
5816
4768
|
forceNgOff: false,
|
|
5817
|
-
inlineCss:
|
|
4769
|
+
inlineCss: InlineCssMode.Unset,
|
|
5818
4770
|
disableSprites: false,
|
|
4771
|
+
disableThrottling: false,
|
|
5819
4772
|
};
|
|
5820
4773
|
this.options = simpleMerge(defaultOptions, options);
|
|
5821
4774
|
if (!this.insideIframe &&
|
|
@@ -5846,7 +4799,6 @@ class App {
|
|
|
5846
4799
|
app: this,
|
|
5847
4800
|
isDictDisabled: Boolean(this.options.disableStringDict || this.options.crossdomain?.enabled),
|
|
5848
4801
|
});
|
|
5849
|
-
this.featureFlags = new FeatureFlags(this);
|
|
5850
4802
|
this.tagWatcher = new TagWatcher({
|
|
5851
4803
|
sessionStorage: this.sessionStorage,
|
|
5852
4804
|
errLog: this.debug.error,
|
|
@@ -6258,6 +5210,162 @@ class App {
|
|
|
6258
5210
|
const sessionToken = this.session.getSessionToken(this.projectKey);
|
|
6259
5211
|
return needNewSessionID || !sessionToken;
|
|
6260
5212
|
}
|
|
5213
|
+
/**
|
|
5214
|
+
* start buffering messages without starting the actual session, which gives
|
|
5215
|
+
* user 30 seconds to "activate" and record session by calling `start()` on conditional trigger,
|
|
5216
|
+
* and we will then send buffered batch, so it won't get lost
|
|
5217
|
+
* */
|
|
5218
|
+
async coldStart(startOpts = {}, conditional) {
|
|
5219
|
+
this.singleBuffer = false;
|
|
5220
|
+
const second = 1000;
|
|
5221
|
+
const isNewSession = this.checkSessionToken(startOpts.forceNew);
|
|
5222
|
+
if (conditional) {
|
|
5223
|
+
await this.setupConditionalStart(startOpts);
|
|
5224
|
+
}
|
|
5225
|
+
const cycle = () => {
|
|
5226
|
+
this.orderNumber += 1;
|
|
5227
|
+
adjustTimeOrigin();
|
|
5228
|
+
this.coldStartTs = now();
|
|
5229
|
+
if (this.orderNumber % 2 === 0) {
|
|
5230
|
+
this.bufferedMessages1.length = 0;
|
|
5231
|
+
this.bufferedMessages1.push(Timestamp(this.timestamp()));
|
|
5232
|
+
this.bufferedMessages1.push(TabData(this.session.getTabId()));
|
|
5233
|
+
}
|
|
5234
|
+
else {
|
|
5235
|
+
this.bufferedMessages2.length = 0;
|
|
5236
|
+
this.bufferedMessages2.push(Timestamp(this.timestamp()));
|
|
5237
|
+
this.bufferedMessages2.push(TabData(this.session.getTabId()));
|
|
5238
|
+
}
|
|
5239
|
+
this.stop(false);
|
|
5240
|
+
this.activityState = ActivityState.ColdStart;
|
|
5241
|
+
if (startOpts.sessionHash) {
|
|
5242
|
+
this.session.applySessionHash(startOpts.sessionHash);
|
|
5243
|
+
}
|
|
5244
|
+
if (startOpts.forceNew) {
|
|
5245
|
+
this.session.reset();
|
|
5246
|
+
}
|
|
5247
|
+
this.session.assign({
|
|
5248
|
+
userID: startOpts.userID,
|
|
5249
|
+
metadata: startOpts.metadata,
|
|
5250
|
+
});
|
|
5251
|
+
if (!isNewSession) {
|
|
5252
|
+
this.debug.log('continuing session on new tab', this.session.getTabId());
|
|
5253
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
5254
|
+
this.send(TabChange(this.session.getTabId()));
|
|
5255
|
+
}
|
|
5256
|
+
this.observer.observe();
|
|
5257
|
+
this.ticker.start();
|
|
5258
|
+
};
|
|
5259
|
+
this.coldInterval = setInterval(() => {
|
|
5260
|
+
cycle();
|
|
5261
|
+
}, 30 * second);
|
|
5262
|
+
cycle();
|
|
5263
|
+
}
|
|
5264
|
+
async setupConditionalStart(startOpts) {
|
|
5265
|
+
this.conditionsManager = new ConditionsManager(this, startOpts);
|
|
5266
|
+
const r = await fetch(this.options.ingestPoint + '/v1/web/start', {
|
|
5267
|
+
method: 'POST',
|
|
5268
|
+
headers: {
|
|
5269
|
+
'Content-Type': 'application/json',
|
|
5270
|
+
},
|
|
5271
|
+
body: JSON.stringify({
|
|
5272
|
+
...this.getTrackerInfo(),
|
|
5273
|
+
timestamp: now(),
|
|
5274
|
+
doNotRecord: true,
|
|
5275
|
+
bufferDiff: 0,
|
|
5276
|
+
userID: this.session.getInfo().userID,
|
|
5277
|
+
token: undefined,
|
|
5278
|
+
deviceMemory,
|
|
5279
|
+
jsHeapSizeLimit,
|
|
5280
|
+
timezone: getTimezone(),
|
|
5281
|
+
width: window.screen.width,
|
|
5282
|
+
height: window.screen.height,
|
|
5283
|
+
}),
|
|
5284
|
+
});
|
|
5285
|
+
const {
|
|
5286
|
+
// this token is needed to fetch conditions and flags,
|
|
5287
|
+
// but it can't be used to record a session
|
|
5288
|
+
token, userBrowser, userCity, userCountry, userDevice, userOS, userState, projectID, } = await r.json();
|
|
5289
|
+
this.session.assign({ projectID });
|
|
5290
|
+
this.session.setUserInfo({
|
|
5291
|
+
userBrowser,
|
|
5292
|
+
userCity,
|
|
5293
|
+
userCountry,
|
|
5294
|
+
userDevice,
|
|
5295
|
+
userOS,
|
|
5296
|
+
userState,
|
|
5297
|
+
});
|
|
5298
|
+
const onStartInfo = { sessionToken: token, userUUID: '', sessionID: '' };
|
|
5299
|
+
this.startCallbacks.forEach((cb) => cb(onStartInfo));
|
|
5300
|
+
await this.conditionsManager?.fetchConditions(projectID, token);
|
|
5301
|
+
await this.tagWatcher.fetchTags(this.options.ingestPoint, token);
|
|
5302
|
+
}
|
|
5303
|
+
/**
|
|
5304
|
+
* Starts offline session recording
|
|
5305
|
+
* @param {Object} startOpts - options for session start, same as .start()
|
|
5306
|
+
* @param {Function} onSessionSent - callback that will be called once session is fully sent
|
|
5307
|
+
* */
|
|
5308
|
+
offlineRecording(startOpts = {}, onSessionSent) {
|
|
5309
|
+
this.onSessionSent = onSessionSent;
|
|
5310
|
+
this.singleBuffer = true;
|
|
5311
|
+
const isNewSession = this.checkSessionToken(startOpts.forceNew);
|
|
5312
|
+
adjustTimeOrigin();
|
|
5313
|
+
this.coldStartTs = now();
|
|
5314
|
+
const saverBuffer = this.localStorage.getItem(bufferStorageKey);
|
|
5315
|
+
if (saverBuffer) {
|
|
5316
|
+
const data = JSON.parse(saverBuffer);
|
|
5317
|
+
this.bufferedMessages1 = Array.isArray(data) ? data : this.bufferedMessages1;
|
|
5318
|
+
this.localStorage.removeItem(bufferStorageKey);
|
|
5319
|
+
}
|
|
5320
|
+
this.bufferedMessages1.push(Timestamp(this.timestamp()));
|
|
5321
|
+
this.bufferedMessages1.push(TabData(this.session.getTabId()));
|
|
5322
|
+
this.activityState = ActivityState.ColdStart;
|
|
5323
|
+
if (startOpts.sessionHash) {
|
|
5324
|
+
this.session.applySessionHash(startOpts.sessionHash);
|
|
5325
|
+
}
|
|
5326
|
+
if (startOpts.forceNew) {
|
|
5327
|
+
this.session.reset();
|
|
5328
|
+
}
|
|
5329
|
+
this.session.assign({
|
|
5330
|
+
userID: startOpts.userID,
|
|
5331
|
+
metadata: startOpts.metadata,
|
|
5332
|
+
});
|
|
5333
|
+
const onStartInfo = { sessionToken: '', userUUID: '', sessionID: '' };
|
|
5334
|
+
this.startCallbacks.forEach((cb) => cb(onStartInfo));
|
|
5335
|
+
if (!isNewSession) {
|
|
5336
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
5337
|
+
this.send(TabChange(this.session.getTabId()));
|
|
5338
|
+
}
|
|
5339
|
+
this.observer.observe();
|
|
5340
|
+
this.ticker.start();
|
|
5341
|
+
return {
|
|
5342
|
+
saveBuffer: this.saveBuffer,
|
|
5343
|
+
getBuffer: this.getBuffer,
|
|
5344
|
+
setBuffer: this.setBuffer,
|
|
5345
|
+
};
|
|
5346
|
+
}
|
|
5347
|
+
/**
|
|
5348
|
+
* Saves the captured messages in localStorage (or whatever is used in its place)
|
|
5349
|
+
*
|
|
5350
|
+
* Then, when this.offlineRecording is called, it will preload this messages and clear the storage item
|
|
5351
|
+
*
|
|
5352
|
+
* Keeping the size of local storage reasonable is up to the end users of this library
|
|
5353
|
+
* */
|
|
5354
|
+
saveBuffer() {
|
|
5355
|
+
this.localStorage.setItem(bufferStorageKey, JSON.stringify(this.bufferedMessages1));
|
|
5356
|
+
}
|
|
5357
|
+
/**
|
|
5358
|
+
* @returns buffer with stored messages for offline recording
|
|
5359
|
+
* */
|
|
5360
|
+
getBuffer() {
|
|
5361
|
+
return this.bufferedMessages1;
|
|
5362
|
+
}
|
|
5363
|
+
/**
|
|
5364
|
+
* Used to set a buffer with messages array
|
|
5365
|
+
* */
|
|
5366
|
+
setBuffer(buffer) {
|
|
5367
|
+
this.bufferedMessages1 = buffer;
|
|
5368
|
+
}
|
|
6261
5369
|
/**
|
|
6262
5370
|
* Uploads the stored session buffer to backend
|
|
6263
5371
|
* @returns promise that resolves once messages are loaded, it has to be awaited
|
|
@@ -6313,7 +5421,7 @@ class App {
|
|
|
6313
5421
|
while (this.bufferedMessages1.length > 0) {
|
|
6314
5422
|
await this.flushBuffer(this.bufferedMessages1);
|
|
6315
5423
|
}
|
|
6316
|
-
this.postToWorker([[
|
|
5424
|
+
this.postToWorker([[-1]]);
|
|
6317
5425
|
this.clearBuffers();
|
|
6318
5426
|
}
|
|
6319
5427
|
async _start(startOpts = {}, resetByWorker = false, conditionName) {
|
|
@@ -6401,8 +5509,7 @@ class App {
|
|
|
6401
5509
|
delay, // derived from token
|
|
6402
5510
|
sessionID, // derived from token
|
|
6403
5511
|
startTimestamp, // real startTS (server time), derived from sessionID
|
|
6404
|
-
userBrowser, userCity, userCountry, userDevice, userOS, userState, canvasEnabled, canvasQuality, canvasFPS, assistOnly: socketOnly,
|
|
6405
|
-
this.features = features ? features : this.features;
|
|
5512
|
+
userBrowser, userCity, userCountry, userDevice, userOS, userState, canvasEnabled, canvasQuality, canvasFPS, assistOnly: socketOnly, } = await r.json();
|
|
6406
5513
|
if (typeof token !== 'string' ||
|
|
6407
5514
|
typeof userUUID !== 'string' ||
|
|
6408
5515
|
(typeof startTimestamp !== 'number' && typeof startTimestamp !== 'undefined') ||
|
|
@@ -6455,14 +5562,6 @@ class App {
|
|
|
6455
5562
|
if (startOpts.startCallback) {
|
|
6456
5563
|
startOpts.startCallback(SuccessfulStart(onStartInfo));
|
|
6457
5564
|
}
|
|
6458
|
-
if (this.features['feature-flags']) {
|
|
6459
|
-
try {
|
|
6460
|
-
void this.featureFlags.reloadFlags();
|
|
6461
|
-
}
|
|
6462
|
-
catch (e) {
|
|
6463
|
-
this.debug.log("Error getting feature flags", e);
|
|
6464
|
-
}
|
|
6465
|
-
}
|
|
6466
5565
|
await this.tagWatcher.fetchTags(this.options.ingestPoint, token);
|
|
6467
5566
|
this.activityState = ActivityState.Active;
|
|
6468
5567
|
if (this.options.crossdomain?.enabled) {
|
|
@@ -6491,47 +5590,14 @@ class App {
|
|
|
6491
5590
|
this.commit();
|
|
6492
5591
|
/** --------------- COLD START BUFFER ------------------*/
|
|
6493
5592
|
}
|
|
5593
|
+
if (this.insideIframe && this.rootId) {
|
|
5594
|
+
this.observer.crossdomainObserve(this.rootId, this.frameOderNumber, this.frameLevel);
|
|
5595
|
+
}
|
|
6494
5596
|
else {
|
|
6495
|
-
|
|
6496
|
-
this.observer.crossdomainObserve(this.rootId, this.frameOderNumber, this.frameLevel);
|
|
6497
|
-
}
|
|
6498
|
-
else {
|
|
6499
|
-
this.observer.observe();
|
|
6500
|
-
}
|
|
6501
|
-
this.ticker.start();
|
|
5597
|
+
this.observer.observe();
|
|
6502
5598
|
}
|
|
5599
|
+
this.ticker.start();
|
|
6503
5600
|
this.canvasRecorder?.startTracking();
|
|
6504
|
-
if (this.features['usability-test'] && !this.insideIframe) {
|
|
6505
|
-
this.uxtManager = this.uxtManager
|
|
6506
|
-
? this.uxtManager
|
|
6507
|
-
: new UserTestManager(this, uxtStorageKey);
|
|
6508
|
-
let uxtId;
|
|
6509
|
-
const savedUxtTag = this.localStorage.getItem(uxtStorageKey);
|
|
6510
|
-
if (savedUxtTag) {
|
|
6511
|
-
uxtId = parseInt(savedUxtTag, 10);
|
|
6512
|
-
}
|
|
6513
|
-
if (location?.search) {
|
|
6514
|
-
const query = new URLSearchParams(location.search);
|
|
6515
|
-
if (query.has('oruxt')) {
|
|
6516
|
-
const qId = query.get('oruxt');
|
|
6517
|
-
uxtId = qId ? parseInt(qId, 10) : undefined;
|
|
6518
|
-
}
|
|
6519
|
-
}
|
|
6520
|
-
if (uxtId) {
|
|
6521
|
-
if (!this.uxtManager.isActive) {
|
|
6522
|
-
// eslint-disable-next-line
|
|
6523
|
-
this.uxtManager.getTest(uxtId, token, Boolean(savedUxtTag)).then((id) => {
|
|
6524
|
-
if (id) {
|
|
6525
|
-
this.onUxtCb.forEach((cb) => cb(id));
|
|
6526
|
-
}
|
|
6527
|
-
});
|
|
6528
|
-
}
|
|
6529
|
-
else {
|
|
6530
|
-
// @ts-ignore
|
|
6531
|
-
this.onUxtCb.forEach((cb) => cb(uxtId));
|
|
6532
|
-
}
|
|
6533
|
-
}
|
|
6534
|
-
}
|
|
6535
5601
|
return SuccessfulStart(onStartInfo);
|
|
6536
5602
|
}
|
|
6537
5603
|
catch (reason) {
|
|
@@ -6552,13 +5618,6 @@ class App {
|
|
|
6552
5618
|
return UnsuccessfulStart(errorMessage);
|
|
6553
5619
|
}
|
|
6554
5620
|
}
|
|
6555
|
-
addOnUxtCb(cb) {
|
|
6556
|
-
// @ts-ignore
|
|
6557
|
-
this.onUxtCb.push(cb);
|
|
6558
|
-
}
|
|
6559
|
-
getUxtId() {
|
|
6560
|
-
return this.uxtManager?.getTestId();
|
|
6561
|
-
}
|
|
6562
5621
|
async waitStart() {
|
|
6563
5622
|
return new Promise((resolve) => {
|
|
6564
5623
|
const int = setInterval(() => {
|
|
@@ -6679,9 +5738,16 @@ function Connection (app) {
|
|
|
6679
5738
|
if (connection === undefined) {
|
|
6680
5739
|
return;
|
|
6681
5740
|
}
|
|
6682
|
-
const sendConnectionInformation = () =>
|
|
6683
|
-
|
|
6684
|
-
|
|
5741
|
+
const sendConnectionInformation = () => {
|
|
5742
|
+
app.send(ConnectionInformation(Math.round(connection.downlink * 1000), connection.effectiveType || 'unknown'));
|
|
5743
|
+
};
|
|
5744
|
+
app.attachStartCallback(() => {
|
|
5745
|
+
sendConnectionInformation();
|
|
5746
|
+
connection.addEventListener('change', sendConnectionInformation);
|
|
5747
|
+
});
|
|
5748
|
+
app.attachStopCallback(() => {
|
|
5749
|
+
connection.removeEventListener('change', sendConnectionInformation);
|
|
5750
|
+
});
|
|
6685
5751
|
}
|
|
6686
5752
|
|
|
6687
5753
|
const printError = IN_BROWSER && 'InstallTrigger' in window // detect Firefox
|
|
@@ -7102,7 +6168,7 @@ function Img (app) {
|
|
|
7102
6168
|
const sendImgError = app.safe(function (img) {
|
|
7103
6169
|
const resolvedSrc = resolveURL(img.src || ''); // Src type is null sometimes. - is it true?
|
|
7104
6170
|
if (isURL(resolvedSrc)) {
|
|
7105
|
-
app.send(ResourceTiming(app.timestamp(), 0, 0, 0, 0, 0, resolvedSrc, 'img', 0, false));
|
|
6171
|
+
app.send(ResourceTiming(app.timestamp(), 0, 0, 0, 0, 0, resolvedSrc, 'img', 0, false, 0, 0, 0, 0, 0, 0, 0));
|
|
7106
6172
|
}
|
|
7107
6173
|
});
|
|
7108
6174
|
const sendImgAttrs = app.safe(function (img) {
|
|
@@ -7706,18 +6772,38 @@ function Timing (app, opts) {
|
|
|
7706
6772
|
if (shouldSkip) {
|
|
7707
6773
|
return;
|
|
7708
6774
|
}
|
|
6775
|
+
// will probably require custom header added to responses for tracked requests:
|
|
6776
|
+
// Timing-Allow-Origin: *
|
|
6777
|
+
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Timing-Allow-Origin
|
|
6778
|
+
let stalled = 0;
|
|
6779
|
+
if (entry.connectEnd && entry.connectEnd > entry.domainLookupEnd) {
|
|
6780
|
+
// Usual case stalled is time between connection establishment and request start
|
|
6781
|
+
stalled = Math.max(0, entry.requestStart - entry.connectEnd);
|
|
6782
|
+
}
|
|
6783
|
+
else {
|
|
6784
|
+
// Connection reuse case - stalled is time between domain lookup and request start
|
|
6785
|
+
stalled = Math.max(0, entry.requestStart - entry.domainLookupEnd);
|
|
6786
|
+
}
|
|
6787
|
+
const timings = {
|
|
6788
|
+
queueing: entry.requestStart - entry.fetchStart,
|
|
6789
|
+
dnsLookup: entry.domainLookupEnd - entry.domainLookupStart,
|
|
6790
|
+
initialConnection: entry.connectEnd - entry.connectStart,
|
|
6791
|
+
ssl: entry.secureConnectionStart > 0 ? entry.connectEnd - entry.secureConnectionStart : 0,
|
|
6792
|
+
ttfb: entry.responseStart - entry.requestStart,
|
|
6793
|
+
contentDownload: entry.responseEnd - entry.responseStart,
|
|
6794
|
+
total: entry.duration ?? entry.responseEnd - entry.startTime,
|
|
6795
|
+
stalled,
|
|
6796
|
+
};
|
|
7709
6797
|
const entryName = options.resourceNameSanitizer
|
|
7710
6798
|
? options.resourceNameSanitizer(entry.name)
|
|
7711
6799
|
: entry.name;
|
|
7712
|
-
const cached =
|
|
7713
|
-
// @ts-ignore
|
|
7714
|
-
(entry.responseStatus && entry.responseStatus === 304) ||
|
|
6800
|
+
const cached = (entry.responseStatus && entry.responseStatus === 304) ||
|
|
7715
6801
|
// @ts-ignore
|
|
7716
6802
|
(entry.deliveryType && entry.deliveryType === 'cache') ||
|
|
7717
6803
|
(entry.transferSize === 0 && entry.decodedBodySize > 0);
|
|
7718
6804
|
const requestFailed = entry.responseStatus && entry.responseStatus >= 400;
|
|
7719
6805
|
const decodedBodySize = requestFailed ? -111 : entry.decodedBodySize || 0;
|
|
7720
|
-
app.send(ResourceTiming(entry.startTime + getTimeOrigin(), entry.duration,
|
|
6806
|
+
app.send(ResourceTiming(entry.startTime + getTimeOrigin(), entry.duration, timings.ttfb, entry.transferSize > entry.encodedBodySize ? entry.transferSize - entry.encodedBodySize : 0, entry.encodedBodySize || 0, decodedBodySize, app.sanitizer.privateMode ? entry.name.replaceAll(/./g, '*') : entryName, entry.initiatorType, entry.transferSize, cached, timings.queueing, timings.dnsLookup, timings.initialConnection, timings.ssl, timings.contentDownload, timings.total, timings.stalled));
|
|
7721
6807
|
}
|
|
7722
6808
|
const observer = new PerformanceObserver((list) => list.getEntries().forEach(resourceTiming));
|
|
7723
6809
|
function onVitalsSignal(msg) {
|
|
@@ -9664,8 +8750,86 @@ function Tabs (app) {
|
|
|
9664
8750
|
app.attachEventListener(window, 'focus', changeTab, false, false);
|
|
9665
8751
|
}
|
|
9666
8752
|
|
|
8753
|
+
function LongAnimationTask (app, opts) {
|
|
8754
|
+
if (!opts.longTasks || !('PerformanceObserver' in window)) {
|
|
8755
|
+
return;
|
|
8756
|
+
}
|
|
8757
|
+
const onEntry = (entry) => {
|
|
8758
|
+
app.send(LongAnimationTask$1(entry.name, entry.duration, entry.blockingDuration, entry.firstUIEventTimestamp, entry.startTime, JSON.stringify(entry.scripts ?? [])));
|
|
8759
|
+
};
|
|
8760
|
+
const observer = new PerformanceObserver((entryList) => {
|
|
8761
|
+
entryList.getEntries().forEach((entry) => {
|
|
8762
|
+
if (entry.entryType === 'long-animation-frame') {
|
|
8763
|
+
onEntry(entry);
|
|
8764
|
+
}
|
|
8765
|
+
});
|
|
8766
|
+
});
|
|
8767
|
+
app.attachStartCallback(() => {
|
|
8768
|
+
performance.getEntriesByType('long-animation-frame').forEach((lat) => {
|
|
8769
|
+
onEntry(lat);
|
|
8770
|
+
});
|
|
8771
|
+
observer.observe({
|
|
8772
|
+
entryTypes: ['long-animation-frame'],
|
|
8773
|
+
});
|
|
8774
|
+
});
|
|
8775
|
+
app.attachStopCallback(() => {
|
|
8776
|
+
observer.disconnect();
|
|
8777
|
+
});
|
|
8778
|
+
}
|
|
8779
|
+
|
|
8780
|
+
const toIgnore = ["composite", "computedOffset", "easing", "offset"];
|
|
8781
|
+
function webAnimations(app, options = {}) {
|
|
8782
|
+
const { allElements = false } = options;
|
|
8783
|
+
let listening = new WeakSet();
|
|
8784
|
+
let handled = new WeakSet();
|
|
8785
|
+
function wire(anim, el, nodeId) {
|
|
8786
|
+
if (handled.has(anim))
|
|
8787
|
+
return;
|
|
8788
|
+
handled.add(anim);
|
|
8789
|
+
anim.addEventListener('finish', () => {
|
|
8790
|
+
const lastKF = anim.effect.getKeyframes().at(-1);
|
|
8791
|
+
const computedStyle = getComputedStyle(el);
|
|
8792
|
+
const keys = Object.keys(lastKF).filter((p) => !toIgnore.includes(p));
|
|
8793
|
+
// @ts-ignore
|
|
8794
|
+
const finalStyle = {};
|
|
8795
|
+
keys.forEach((key) => {
|
|
8796
|
+
finalStyle[key] = computedStyle[key];
|
|
8797
|
+
});
|
|
8798
|
+
app.send(NodeAnimationResult(nodeId, JSON.stringify(finalStyle)));
|
|
8799
|
+
}, { once: true });
|
|
8800
|
+
}
|
|
8801
|
+
function scanElement(el, nodeId) {
|
|
8802
|
+
el.getAnimations({ subtree: false }).forEach((anim) => wire(anim, el, nodeId));
|
|
8803
|
+
}
|
|
8804
|
+
app.nodes.attachNodeCallback((node) => {
|
|
8805
|
+
if ((allElements || node.nodeName.includes('-')) && 'getAnimations' in node) {
|
|
8806
|
+
const animations = node.getAnimations({ subtree: false });
|
|
8807
|
+
const id = app.nodes.getID(node);
|
|
8808
|
+
if (animations.length > 0 && !listening.has(node) && id) {
|
|
8809
|
+
listening.add(node);
|
|
8810
|
+
scanElement(node, id);
|
|
8811
|
+
node.addEventListener('animationstart', () => scanElement(node, id));
|
|
8812
|
+
}
|
|
8813
|
+
}
|
|
8814
|
+
});
|
|
8815
|
+
const origAnimate = Element.prototype.animate;
|
|
8816
|
+
Element.prototype.animate = function (...args) {
|
|
8817
|
+
const anim = origAnimate.apply(this, args);
|
|
8818
|
+
const id = app.nodes.getID(this);
|
|
8819
|
+
if (!id)
|
|
8820
|
+
return anim;
|
|
8821
|
+
wire(anim, this, id);
|
|
8822
|
+
return anim;
|
|
8823
|
+
};
|
|
8824
|
+
app.attachStopCallback(() => {
|
|
8825
|
+
Element.prototype.animate = origAnimate; // Restore original animate method
|
|
8826
|
+
listening = new WeakSet();
|
|
8827
|
+
handled = new WeakSet();
|
|
8828
|
+
});
|
|
8829
|
+
}
|
|
8830
|
+
|
|
9667
8831
|
const Messages = _Messages;
|
|
9668
|
-
const DOCS_SETUP = '/en/sdk
|
|
8832
|
+
const DOCS_SETUP = '/en/sdk';
|
|
9669
8833
|
function processOptions(obj) {
|
|
9670
8834
|
if (obj == null) {
|
|
9671
8835
|
console.error(`OpenReplay: invalid options argument type. Please, check documentation on ${DOCS_HOST}${DOCS_SETUP}`);
|
|
@@ -9715,7 +8879,7 @@ class API {
|
|
|
9715
8879
|
this.signalStartIssue = (reason, missingApi) => {
|
|
9716
8880
|
const doNotTrack = this.checkDoNotTrack();
|
|
9717
8881
|
console.log("Tracker couldn't start due to:", JSON.stringify({
|
|
9718
|
-
trackerVersion: '
|
|
8882
|
+
trackerVersion: '17.0.0',
|
|
9719
8883
|
projectKey: this.options.projectKey,
|
|
9720
8884
|
doNotTrack,
|
|
9721
8885
|
reason: missingApi.length ? `missing api: ${missingApi.join(',')}` : reason,
|
|
@@ -9743,6 +8907,12 @@ class API {
|
|
|
9743
8907
|
}
|
|
9744
8908
|
}
|
|
9745
8909
|
};
|
|
8910
|
+
this.incident = (options) => {
|
|
8911
|
+
if (this.app === null) {
|
|
8912
|
+
return;
|
|
8913
|
+
}
|
|
8914
|
+
this.app.send(Incident(options.label ?? '', options.startTime, options.endTime ?? options.startTime));
|
|
8915
|
+
};
|
|
9746
8916
|
this.crossdomainMode = Boolean(inIframe() && options.crossdomain?.enabled);
|
|
9747
8917
|
if (!IN_BROWSER || !processOptions(options)) {
|
|
9748
8918
|
return;
|
|
@@ -9821,6 +8991,7 @@ class API {
|
|
|
9821
8991
|
Img(app);
|
|
9822
8992
|
Input(app, options);
|
|
9823
8993
|
Timing(app, options);
|
|
8994
|
+
LongAnimationTask(app, options);
|
|
9824
8995
|
Focus(app);
|
|
9825
8996
|
Fonts(app);
|
|
9826
8997
|
const skipNetwork = options.network?.disabled;
|
|
@@ -9828,10 +8999,8 @@ class API {
|
|
|
9828
8999
|
Network(app, options.network);
|
|
9829
9000
|
}
|
|
9830
9001
|
selection(app);
|
|
9002
|
+
webAnimations(app, options.webAnimations);
|
|
9831
9003
|
window.__OPENREPLAY__ = this;
|
|
9832
|
-
if (options.flags && options.flags.onFlagsLoad) {
|
|
9833
|
-
this.onFlagsLoad(options.flags.onFlagsLoad);
|
|
9834
|
-
}
|
|
9835
9004
|
const wOpen = window.open;
|
|
9836
9005
|
if (options.autoResetOnWindowOpen || options.resetTabOnWindowOpen) {
|
|
9837
9006
|
app.attachStartCallback(() => {
|
|
@@ -9854,24 +9023,6 @@ class API {
|
|
|
9854
9023
|
});
|
|
9855
9024
|
}
|
|
9856
9025
|
}
|
|
9857
|
-
isFlagEnabled(flagName) {
|
|
9858
|
-
return this.featureFlags.isFlagEnabled(flagName);
|
|
9859
|
-
}
|
|
9860
|
-
onFlagsLoad(callback) {
|
|
9861
|
-
this.app?.featureFlags.onFlagsLoad(callback);
|
|
9862
|
-
}
|
|
9863
|
-
clearPersistFlags() {
|
|
9864
|
-
this.app?.featureFlags.clearPersistFlags();
|
|
9865
|
-
}
|
|
9866
|
-
reloadFlags() {
|
|
9867
|
-
return this.app?.featureFlags.reloadFlags();
|
|
9868
|
-
}
|
|
9869
|
-
getFeatureFlag(flagName) {
|
|
9870
|
-
return this.app?.featureFlags.getFeatureFlag(flagName);
|
|
9871
|
-
}
|
|
9872
|
-
getAllFeatureFlags() {
|
|
9873
|
-
return this.app?.featureFlags.flags;
|
|
9874
|
-
}
|
|
9875
9026
|
use(fn) {
|
|
9876
9027
|
return fn(this.app, this.options);
|
|
9877
9028
|
}
|
|
@@ -10002,12 +9153,6 @@ class API {
|
|
|
10002
9153
|
}
|
|
10003
9154
|
return this.app.getTabId();
|
|
10004
9155
|
}
|
|
10005
|
-
getUxId() {
|
|
10006
|
-
if (this.app === null) {
|
|
10007
|
-
return null;
|
|
10008
|
-
}
|
|
10009
|
-
return this.app.getUxtId();
|
|
10010
|
-
}
|
|
10011
9156
|
sessionID() {
|
|
10012
9157
|
deprecationWarn("'sessionID' method", "'getSessionID' method", '/');
|
|
10013
9158
|
return this.getSessionID();
|
|
@@ -10051,12 +9196,20 @@ class API {
|
|
|
10051
9196
|
return this.issue(key, payload);
|
|
10052
9197
|
}
|
|
10053
9198
|
else {
|
|
9199
|
+
if (!payload || typeof payload === 'string') {
|
|
9200
|
+
return this.app.send(CustomEvent(key, payload));
|
|
9201
|
+
}
|
|
10054
9202
|
try {
|
|
9203
|
+
if ('or_timestamp' in payload) {
|
|
9204
|
+
const startTs = this.getSessionInfo()?.timestamp ?? 0;
|
|
9205
|
+
const diff = payload.or_timestamp - startTs;
|
|
9206
|
+
if (diff < 0) {
|
|
9207
|
+
console.error(`OpenReplay: event ${key} has or_timestamp (${payload.or_timestamp}) before session start (${startTs}). It will be ignored.`);
|
|
9208
|
+
}
|
|
9209
|
+
}
|
|
10055
9210
|
payload = JSON.stringify(payload);
|
|
10056
9211
|
}
|
|
10057
|
-
catch (
|
|
10058
|
-
return;
|
|
10059
|
-
}
|
|
9212
|
+
catch (_) { }
|
|
10060
9213
|
this.app.send(CustomEvent(key, payload));
|
|
10061
9214
|
}
|
|
10062
9215
|
}
|
|
@@ -10180,42 +9333,6 @@ class TrackerSingleton {
|
|
|
10180
9333
|
}
|
|
10181
9334
|
this.instance.handleError(e, metadata);
|
|
10182
9335
|
}
|
|
10183
|
-
isFlagEnabled(flagName) {
|
|
10184
|
-
if (!IN_BROWSER || !this.ensureConfigured() || !this.instance) {
|
|
10185
|
-
return false;
|
|
10186
|
-
}
|
|
10187
|
-
return this.instance.isFlagEnabled(flagName);
|
|
10188
|
-
}
|
|
10189
|
-
onFlagsLoad(...args) {
|
|
10190
|
-
if (!IN_BROWSER || !this.ensureConfigured() || !this.instance) {
|
|
10191
|
-
return;
|
|
10192
|
-
}
|
|
10193
|
-
this.instance.onFlagsLoad(...args);
|
|
10194
|
-
}
|
|
10195
|
-
clearPersistFlags() {
|
|
10196
|
-
if (!IN_BROWSER || !this.ensureConfigured() || !this.instance) {
|
|
10197
|
-
return;
|
|
10198
|
-
}
|
|
10199
|
-
this.instance.clearPersistFlags();
|
|
10200
|
-
}
|
|
10201
|
-
reloadFlags() {
|
|
10202
|
-
if (!IN_BROWSER || !this.ensureConfigured() || !this.instance) {
|
|
10203
|
-
return;
|
|
10204
|
-
}
|
|
10205
|
-
return this.instance.reloadFlags();
|
|
10206
|
-
}
|
|
10207
|
-
getFeatureFlag(flagName) {
|
|
10208
|
-
if (!IN_BROWSER || !this.ensureConfigured() || !this.instance) {
|
|
10209
|
-
return;
|
|
10210
|
-
}
|
|
10211
|
-
return this.instance.getFeatureFlag(flagName);
|
|
10212
|
-
}
|
|
10213
|
-
getAllFeatureFlags() {
|
|
10214
|
-
if (!IN_BROWSER || !this.ensureConfigured() || !this.instance) {
|
|
10215
|
-
return;
|
|
10216
|
-
}
|
|
10217
|
-
return this.instance.getAllFeatureFlags();
|
|
10218
|
-
}
|
|
10219
9336
|
restartCanvasTracking() {
|
|
10220
9337
|
if (!IN_BROWSER || !this.ensureConfigured() || !this.instance) {
|
|
10221
9338
|
return;
|
|
@@ -10338,12 +9455,6 @@ class TrackerSingleton {
|
|
|
10338
9455
|
}
|
|
10339
9456
|
return this.instance.getTabId();
|
|
10340
9457
|
}
|
|
10341
|
-
getUxId() {
|
|
10342
|
-
if (!IN_BROWSER || !this.ensureConfigured() || !this.instance) {
|
|
10343
|
-
return null;
|
|
10344
|
-
}
|
|
10345
|
-
return this.instance.getUxId();
|
|
10346
|
-
}
|
|
10347
9458
|
}
|
|
10348
9459
|
const tracker = new TrackerSingleton();
|
|
10349
9460
|
|