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