@openreplay/tracker 4.0.1 → 4.1.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/cjs/app/guards.d.ts +1 -0
- package/cjs/app/guards.js +6 -1
- package/cjs/app/index.d.ts +1 -1
- package/cjs/app/index.js +14 -8
- package/cjs/app/messages.gen.js +1 -0
- package/cjs/app/nodes.d.ts +1 -1
- package/cjs/app/nodes.js +3 -5
- package/cjs/app/observer/iframe_observer.js +1 -0
- package/cjs/app/observer/iframe_offsets.d.ts +8 -0
- package/cjs/app/observer/iframe_offsets.js +59 -0
- package/cjs/app/observer/observer.js +4 -4
- package/cjs/app/observer/top_observer.d.ts +2 -4
- package/cjs/app/observer/top_observer.js +11 -21
- package/cjs/app/sanitizer.d.ts +10 -4
- package/cjs/app/sanitizer.js +33 -15
- package/cjs/app/session.js +1 -1
- package/cjs/common/messages.gen.js +1 -0
- package/cjs/index.d.ts +1 -0
- package/cjs/index.js +6 -6
- package/cjs/modules/constructedStyleSheets.d.ts +4 -0
- package/cjs/modules/{adoptedStyleSheets.js → constructedStyleSheets.js} +21 -20
- package/cjs/modules/cssrules.js +65 -18
- package/cjs/modules/img.js +27 -19
- package/cjs/modules/input.js +2 -2
- package/cjs/modules/mouse.js +11 -7
- package/cjs/modules/scroll.js +32 -12
- package/cjs/utils.d.ts +5 -3
- package/cjs/utils.js +18 -13
- package/lib/app/guards.d.ts +1 -0
- package/lib/app/guards.js +4 -0
- package/lib/app/index.d.ts +1 -1
- package/lib/app/index.js +14 -8
- package/lib/app/messages.gen.js +1 -0
- package/lib/app/nodes.d.ts +1 -1
- package/lib/app/nodes.js +3 -5
- package/lib/app/observer/iframe_observer.js +1 -0
- package/lib/app/observer/iframe_offsets.d.ts +8 -0
- package/lib/app/observer/iframe_offsets.js +56 -0
- package/lib/app/observer/observer.js +4 -4
- package/lib/app/observer/top_observer.d.ts +2 -4
- package/lib/app/observer/top_observer.js +11 -21
- package/lib/app/sanitizer.d.ts +10 -4
- package/lib/app/sanitizer.js +32 -15
- package/lib/app/session.js +1 -1
- package/lib/common/messages.gen.js +1 -0
- package/lib/common/tsconfig.tsbuildinfo +1 -1
- package/lib/index.d.ts +1 -0
- package/lib/index.js +4 -5
- package/lib/modules/constructedStyleSheets.d.ts +4 -0
- package/lib/modules/{adoptedStyleSheets.js → constructedStyleSheets.js} +20 -21
- package/lib/modules/cssrules.js +67 -19
- package/lib/modules/img.js +28 -20
- package/lib/modules/input.js +3 -3
- package/lib/modules/mouse.js +11 -7
- package/lib/modules/scroll.js +33 -13
- package/lib/utils.d.ts +5 -3
- package/lib/utils.js +17 -11
- package/package.json +1 -1
- package/cjs/modules/adoptedStyleSheets.d.ts +0 -2
- package/lib/modules/adoptedStyleSheets.d.ts +0 -2
package/lib/modules/cssrules.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { AdoptedSSInsertRuleURLBased, // TODO: rename to common StyleSheet names
|
|
2
|
+
AdoptedSSDeleteRule, AdoptedSSAddOwner, TechnicalInfo, } from '../app/messages.gen.js';
|
|
2
3
|
import { hasTag } from '../app/guards.js';
|
|
4
|
+
import { nextID, styleSheetIDMap } from './constructedStyleSheets.js';
|
|
3
5
|
export default function (app) {
|
|
4
6
|
if (app === null) {
|
|
5
7
|
return;
|
|
@@ -8,42 +10,88 @@ export default function (app) {
|
|
|
8
10
|
app.send(TechnicalInfo('no_stylesheet_prototype_in_window', ''));
|
|
9
11
|
return;
|
|
10
12
|
}
|
|
11
|
-
const
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
}
|
|
13
|
+
const sendInserDeleteRule = app.safe((sheet, index, rule) => {
|
|
14
|
+
const sheetID = styleSheetIDMap.get(sheet);
|
|
15
|
+
if (!sheetID) {
|
|
16
|
+
// OK-case. Sheet haven't been registered yet. Rules will be sent on registration.
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
if (typeof rule === 'string') {
|
|
20
|
+
app.send(AdoptedSSInsertRuleURLBased(sheetID, rule, index, app.getBaseHref()));
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
app.send(AdoptedSSDeleteRule(sheetID, index));
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
// TODO: proper rule insertion/removal (how?)
|
|
27
|
+
const sendReplaceGroupingRule = app.safe((rule) => {
|
|
28
|
+
let topmostRule = rule;
|
|
29
|
+
while (topmostRule.parentRule) {
|
|
30
|
+
topmostRule = topmostRule.parentRule;
|
|
31
|
+
}
|
|
32
|
+
const sheet = topmostRule.parentStyleSheet;
|
|
33
|
+
if (!sheet) {
|
|
34
|
+
app.debug.warn('No parent StyleSheet found for', topmostRule, rule);
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
const sheetID = styleSheetIDMap.get(sheet);
|
|
38
|
+
if (!sheetID) {
|
|
39
|
+
app.debug.warn('No sheedID found for', sheet, styleSheetIDMap);
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
const cssText = topmostRule.cssText;
|
|
43
|
+
const ruleList = sheet.cssRules;
|
|
44
|
+
const idx = Array.from(ruleList).indexOf(topmostRule);
|
|
45
|
+
if (idx >= 0) {
|
|
46
|
+
app.send(AdoptedSSInsertRuleURLBased(sheetID, cssText, idx, app.getBaseHref()));
|
|
47
|
+
app.send(AdoptedSSDeleteRule(sheetID, idx + 1)); // Remove previous clone
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
app.debug.warn('Rule index not found in', sheet, topmostRule);
|
|
51
|
+
}
|
|
23
52
|
});
|
|
24
53
|
const patchContext = (context) => {
|
|
25
54
|
const { insertRule, deleteRule } = context.CSSStyleSheet.prototype;
|
|
55
|
+
const { insertRule: groupInsertRule, deleteRule: groupDeleteRule } = context.CSSGroupingRule.prototype;
|
|
26
56
|
context.CSSStyleSheet.prototype.insertRule = function (rule, index = 0) {
|
|
27
|
-
|
|
57
|
+
sendInserDeleteRule(this, index, rule);
|
|
28
58
|
return insertRule.call(this, rule, index);
|
|
29
59
|
};
|
|
30
60
|
context.CSSStyleSheet.prototype.deleteRule = function (index) {
|
|
31
|
-
|
|
61
|
+
sendInserDeleteRule(this, index);
|
|
32
62
|
return deleteRule.call(this, index);
|
|
33
63
|
};
|
|
64
|
+
context.CSSGroupingRule.prototype.insertRule = function (rule, index = 0) {
|
|
65
|
+
const result = groupInsertRule.call(this, rule, index);
|
|
66
|
+
sendReplaceGroupingRule(this);
|
|
67
|
+
return result;
|
|
68
|
+
};
|
|
69
|
+
context.CSSGroupingRule.prototype.deleteRule = function (index = 0) {
|
|
70
|
+
const result = groupDeleteRule.call(this, index);
|
|
71
|
+
sendReplaceGroupingRule(this);
|
|
72
|
+
return result;
|
|
73
|
+
};
|
|
34
74
|
};
|
|
35
75
|
patchContext(window);
|
|
36
76
|
app.observer.attachContextCallback(patchContext);
|
|
37
77
|
app.nodes.attachNodeCallback((node) => {
|
|
38
|
-
if (!hasTag(node, 'STYLE') || !node.sheet) {
|
|
78
|
+
if (!(hasTag(node, 'STYLE') || hasTag(node, 'style')) || !node.sheet) {
|
|
39
79
|
return;
|
|
40
80
|
}
|
|
41
81
|
if (node.textContent !== null && node.textContent.trim().length > 0) {
|
|
42
|
-
return; //
|
|
82
|
+
return; // Non-virtual styles captured by the observer as a text
|
|
83
|
+
}
|
|
84
|
+
const nodeID = app.nodes.getID(node);
|
|
85
|
+
if (!nodeID) {
|
|
86
|
+
return;
|
|
43
87
|
}
|
|
44
|
-
const
|
|
88
|
+
const sheet = node.sheet;
|
|
89
|
+
const sheetID = nextID();
|
|
90
|
+
styleSheetIDMap.set(sheet, sheetID);
|
|
91
|
+
app.send(AdoptedSSAddOwner(sheetID, nodeID));
|
|
92
|
+
const rules = sheet.cssRules;
|
|
45
93
|
for (let i = 0; i < rules.length; i++) {
|
|
46
|
-
|
|
94
|
+
sendInserDeleteRule(sheet, i, rules[i].cssText);
|
|
47
95
|
}
|
|
48
96
|
});
|
|
49
97
|
}
|
package/lib/modules/img.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { timestamp, isURL } from '../utils.js';
|
|
1
|
+
import { timestamp, isURL, IS_FIREFOX, MAX_STR_LEN } from '../utils.js';
|
|
2
2
|
import { ResourceTiming, SetNodeAttributeURLBased, SetNodeAttribute } from '../app/messages.gen.js';
|
|
3
3
|
import { hasTag } from '../app/guards.js';
|
|
4
4
|
function resolveURL(url, location = document.location) {
|
|
@@ -8,7 +8,7 @@ function resolveURL(url, location = document.location) {
|
|
|
8
8
|
}
|
|
9
9
|
else if (url.startsWith('http://') ||
|
|
10
10
|
url.startsWith('https://') ||
|
|
11
|
-
url.startsWith('data:') // any other possible value here?
|
|
11
|
+
url.startsWith('data:') // any other possible value here? https://bugzilla.mozilla.org/show_bug.cgi?id=1758035
|
|
12
12
|
) {
|
|
13
13
|
return url;
|
|
14
14
|
}
|
|
@@ -16,6 +16,10 @@ function resolveURL(url, location = document.location) {
|
|
|
16
16
|
return location.origin + location.pathname + url;
|
|
17
17
|
}
|
|
18
18
|
}
|
|
19
|
+
// https://bugzilla.mozilla.org/show_bug.cgi?id=1607081
|
|
20
|
+
function isSVGInFireFox(url) {
|
|
21
|
+
return IS_FIREFOX && (url.startsWith('data:image/svg+xml') || url.match(/.svg$|/i));
|
|
22
|
+
}
|
|
19
23
|
const PLACEHOLDER_SRC = 'https://static.openreplay.com/tracker/placeholder.jpeg';
|
|
20
24
|
export default function (app) {
|
|
21
25
|
function sendPlaceholder(id, node) {
|
|
@@ -40,30 +44,34 @@ export default function (app) {
|
|
|
40
44
|
app.send(SetNodeAttribute(id, 'srcset', resolvedSrcset));
|
|
41
45
|
};
|
|
42
46
|
const sendSrc = function (id, img) {
|
|
43
|
-
|
|
44
|
-
|
|
47
|
+
if (img.src.length > MAX_STR_LEN) {
|
|
48
|
+
sendPlaceholder(id, img);
|
|
49
|
+
}
|
|
50
|
+
app.send(SetNodeAttributeURLBased(id, 'src', img.src, app.getBaseHref()));
|
|
45
51
|
};
|
|
46
|
-
const
|
|
47
|
-
const
|
|
52
|
+
const sendImgError = app.safe(function (img) {
|
|
53
|
+
const resolvedSrc = resolveURL(img.src || ''); // Src type is null sometimes. - is it true?
|
|
54
|
+
if (isURL(resolvedSrc)) {
|
|
55
|
+
app.send(ResourceTiming(timestamp(), 0, 0, 0, 0, 0, resolvedSrc, 'img'));
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
const sendImgAttrs = app.safe(function (img) {
|
|
59
|
+
const id = app.nodes.getID(img);
|
|
48
60
|
if (id === undefined) {
|
|
49
61
|
return;
|
|
50
62
|
}
|
|
51
|
-
|
|
52
|
-
if (!complete) {
|
|
63
|
+
if (!img.complete) {
|
|
53
64
|
return;
|
|
54
65
|
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
if (isURL(resolvedSrc)) {
|
|
58
|
-
app.send(ResourceTiming(timestamp(), 0, 0, 0, 0, 0, resolvedSrc, 'img'));
|
|
59
|
-
}
|
|
66
|
+
if (img.naturalHeight === 0 && img.naturalWidth === 0 && !isSVGInFireFox(img.src)) {
|
|
67
|
+
sendImgError(img);
|
|
60
68
|
}
|
|
61
|
-
else if (
|
|
62
|
-
sendPlaceholder(id,
|
|
69
|
+
else if (app.sanitizer.isHidden(id) || app.sanitizer.isObscured(id)) {
|
|
70
|
+
sendPlaceholder(id, img);
|
|
63
71
|
}
|
|
64
72
|
else {
|
|
65
|
-
sendSrc(id,
|
|
66
|
-
sendSrcset(id,
|
|
73
|
+
sendSrc(id, img);
|
|
74
|
+
sendSrcset(id, img);
|
|
67
75
|
}
|
|
68
76
|
});
|
|
69
77
|
const observer = new MutationObserver((mutations) => {
|
|
@@ -90,9 +98,9 @@ export default function (app) {
|
|
|
90
98
|
if (!hasTag(node, 'IMG')) {
|
|
91
99
|
return;
|
|
92
100
|
}
|
|
93
|
-
app.nodes.
|
|
94
|
-
app.nodes.
|
|
95
|
-
sendImgAttrs
|
|
101
|
+
app.nodes.attachNodeListener(node, 'error', () => sendImgError(node));
|
|
102
|
+
app.nodes.attachNodeListener(node, 'load', () => sendImgAttrs(node));
|
|
103
|
+
sendImgAttrs(node);
|
|
96
104
|
observer.observe(node, { attributes: true, attributeFilter: ['src', 'srcset'] });
|
|
97
105
|
});
|
|
98
106
|
}
|
package/lib/modules/input.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { normSpaces, IN_BROWSER, getLabelAttribute
|
|
1
|
+
import { normSpaces, IN_BROWSER, getLabelAttribute } from '../utils.js';
|
|
2
2
|
import { hasTag } from '../app/guards.js';
|
|
3
3
|
import { SetInputTarget, SetInputValue, SetInputChecked } from '../app/messages.gen.js';
|
|
4
4
|
const INPUT_TYPES = ['text', 'password', 'email', 'search', 'number', 'range', 'date'];
|
|
@@ -76,10 +76,10 @@ export default function (app, opts) {
|
|
|
76
76
|
function sendInputValue(id, node) {
|
|
77
77
|
let value = node.value;
|
|
78
78
|
let inputMode = options.defaultInputMode;
|
|
79
|
-
if (node.type === 'password' ||
|
|
79
|
+
if (node.type === 'password' || app.sanitizer.isHidden(id)) {
|
|
80
80
|
inputMode = 2 /* Hidden */;
|
|
81
81
|
}
|
|
82
|
-
else if (
|
|
82
|
+
else if (app.sanitizer.isObscured(id) ||
|
|
83
83
|
(inputMode === 0 /* Plain */ &&
|
|
84
84
|
((options.obscureInputNumbers && node.type !== 'date' && /\d\d\d\d/.test(value)) ||
|
|
85
85
|
(options.obscureInputDates && node.type === 'date') ||
|
package/lib/modules/mouse.js
CHANGED
|
@@ -96,11 +96,13 @@ export default function (app) {
|
|
|
96
96
|
let mousePositionChanged = false;
|
|
97
97
|
let mouseTarget = null;
|
|
98
98
|
let mouseTargetTime = 0;
|
|
99
|
+
let selectorMap = {};
|
|
99
100
|
app.attachStopCallback(() => {
|
|
100
101
|
mousePositionX = -1;
|
|
101
102
|
mousePositionY = -1;
|
|
102
103
|
mousePositionChanged = false;
|
|
103
104
|
mouseTarget = null;
|
|
105
|
+
selectorMap = {};
|
|
104
106
|
});
|
|
105
107
|
const sendMouseMove = () => {
|
|
106
108
|
if (mousePositionChanged) {
|
|
@@ -108,25 +110,27 @@ export default function (app) {
|
|
|
108
110
|
mousePositionChanged = false;
|
|
109
111
|
}
|
|
110
112
|
};
|
|
111
|
-
const patchDocument = (document) => {
|
|
112
|
-
const selectorMap = {};
|
|
113
|
+
const patchDocument = (document, topframe = false) => {
|
|
113
114
|
function getSelector(id, target) {
|
|
114
115
|
return (selectorMap[id] = selectorMap[id] || _getSelector(target, document));
|
|
115
116
|
}
|
|
116
|
-
|
|
117
|
+
const attachListener = topframe
|
|
118
|
+
? app.attachEventListener.bind(app) // attached/removed on start/stop
|
|
119
|
+
: app.nodes.attachNodeListener.bind(app.nodes); // attached/removed on node register/unregister
|
|
120
|
+
attachListener(document.documentElement, 'mouseover', (e) => {
|
|
117
121
|
const target = getTarget(e.target, document);
|
|
118
122
|
if (target !== mouseTarget) {
|
|
119
123
|
mouseTarget = target;
|
|
120
124
|
mouseTargetTime = performance.now();
|
|
121
125
|
}
|
|
122
126
|
});
|
|
123
|
-
|
|
124
|
-
const
|
|
127
|
+
attachListener(document, 'mousemove', (e) => {
|
|
128
|
+
const [left, top] = app.observer.getDocumentOffset(document); // MBTODO?: document-id related message
|
|
125
129
|
mousePositionX = e.clientX + left;
|
|
126
130
|
mousePositionY = e.clientY + top;
|
|
127
131
|
mousePositionChanged = true;
|
|
128
132
|
}, false);
|
|
129
|
-
|
|
133
|
+
attachListener(document, 'click', (e) => {
|
|
130
134
|
const target = getTarget(e.target, document);
|
|
131
135
|
if ((!e.clientX && !e.clientY) || target === null) {
|
|
132
136
|
return;
|
|
@@ -144,6 +148,6 @@ export default function (app) {
|
|
|
144
148
|
patchDocument(node);
|
|
145
149
|
}
|
|
146
150
|
});
|
|
147
|
-
patchDocument(document);
|
|
151
|
+
patchDocument(document, true);
|
|
148
152
|
app.ticker.attach(sendMouseMove, 10);
|
|
149
153
|
}
|
package/lib/modules/scroll.js
CHANGED
|
@@ -1,20 +1,33 @@
|
|
|
1
1
|
import { SetViewportScroll, SetNodeScroll } from '../app/messages.gen.js';
|
|
2
|
-
import { isElementNode, isRootNode } from '../app/guards.js';
|
|
2
|
+
import { isNode, isElementNode, isRootNode, isDocument } from '../app/guards.js';
|
|
3
|
+
function getDocumentScroll(doc) {
|
|
4
|
+
const win = doc.defaultView;
|
|
5
|
+
return [
|
|
6
|
+
(win && win.pageXOffset) ||
|
|
7
|
+
(doc.documentElement && doc.documentElement.scrollLeft) ||
|
|
8
|
+
(doc.body && doc.body.scrollLeft) ||
|
|
9
|
+
0,
|
|
10
|
+
(win && win.pageYOffset) ||
|
|
11
|
+
(doc.documentElement && doc.documentElement.scrollTop) ||
|
|
12
|
+
(doc.body && doc.body.scrollTop) ||
|
|
13
|
+
0,
|
|
14
|
+
];
|
|
15
|
+
}
|
|
3
16
|
export default function (app) {
|
|
4
17
|
let documentScroll = false;
|
|
5
18
|
const nodeScroll = new Map();
|
|
6
19
|
function setNodeScroll(target) {
|
|
7
|
-
if (target
|
|
20
|
+
if (!isNode(target)) {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
if (isElementNode(target)) {
|
|
8
24
|
nodeScroll.set(target, [target.scrollLeft, target.scrollTop]);
|
|
9
25
|
}
|
|
26
|
+
if (isDocument(target)) {
|
|
27
|
+
nodeScroll.set(target, getDocumentScroll(target));
|
|
28
|
+
}
|
|
10
29
|
}
|
|
11
|
-
const sendSetViewportScroll = app.safe(() => app.send(SetViewportScroll(
|
|
12
|
-
(document.documentElement && document.documentElement.scrollLeft) ||
|
|
13
|
-
(document.body && document.body.scrollLeft) ||
|
|
14
|
-
0, window.pageYOffset ||
|
|
15
|
-
(document.documentElement && document.documentElement.scrollTop) ||
|
|
16
|
-
(document.body && document.body.scrollTop) ||
|
|
17
|
-
0)));
|
|
30
|
+
const sendSetViewportScroll = app.safe(() => app.send(SetViewportScroll(...getDocumentScroll(document))));
|
|
18
31
|
const sendSetNodeScroll = app.safe((s, node) => {
|
|
19
32
|
const id = app.nodes.getID(node);
|
|
20
33
|
if (id !== undefined) {
|
|
@@ -27,12 +40,19 @@ export default function (app) {
|
|
|
27
40
|
nodeScroll.clear();
|
|
28
41
|
});
|
|
29
42
|
app.nodes.attachNodeCallback((node, isStart) => {
|
|
30
|
-
|
|
31
|
-
|
|
43
|
+
// MBTODO: iterate over all the nodes on start instead of using isStart hack
|
|
44
|
+
if (isStart) {
|
|
45
|
+
if (isElementNode(node) && node.scrollLeft + node.scrollTop > 0) {
|
|
46
|
+
nodeScroll.set(node, [node.scrollLeft, node.scrollTop]);
|
|
47
|
+
}
|
|
48
|
+
else if (isDocument(node)) {
|
|
49
|
+
// DRY somehow?
|
|
50
|
+
nodeScroll.set(node, getDocumentScroll(node));
|
|
51
|
+
}
|
|
32
52
|
}
|
|
33
|
-
|
|
53
|
+
if (isRootNode(node)) {
|
|
34
54
|
// scroll is not-composed event (https://javascript.info/shadow-dom-events)
|
|
35
|
-
app.
|
|
55
|
+
app.nodes.attachNodeListener(node, 'scroll', (e) => {
|
|
36
56
|
setNodeScroll(e.target);
|
|
37
57
|
});
|
|
38
58
|
}
|
package/lib/utils.d.ts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
export declare
|
|
1
|
+
export declare const IN_BROWSER: boolean;
|
|
2
|
+
export declare const IS_FIREFOX: false | RegExpMatchArray | null;
|
|
3
|
+
export declare const MAX_STR_LEN = 100000;
|
|
4
|
+
export declare const timestamp: () => number;
|
|
2
5
|
export declare const stars: (str: string) => string;
|
|
3
6
|
export declare function normSpaces(str: string): string;
|
|
4
7
|
export declare function isURL(s: string): boolean;
|
|
5
|
-
export declare const IN_BROWSER: boolean;
|
|
6
8
|
export declare const DOCS_HOST = "https://docs.openreplay.com";
|
|
7
9
|
export declare function deprecationWarn(nameOfFeature: string, useInstead: string, docsPath?: string): void;
|
|
8
10
|
export declare function getLabelAttribute(e: Element): string | null;
|
|
9
|
-
export declare function hasOpenreplayAttribute(e: Element,
|
|
11
|
+
export declare function hasOpenreplayAttribute(e: Element, attr: string): boolean;
|
package/lib/utils.js
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
const DEPRECATED_ATTRS = { htmlmasked: 'hidden', masked: 'obscured' };
|
|
2
|
+
export const IN_BROWSER = !(typeof window === 'undefined');
|
|
3
|
+
export const IS_FIREFOX = IN_BROWSER && navigator.userAgent.match(/firefox|fxios/i);
|
|
4
|
+
export const MAX_STR_LEN = 1e5;
|
|
5
|
+
const navigationStart = (IN_BROWSER && performance.timing.navigationStart) || performance.timeOrigin;
|
|
6
|
+
// performance.now() is buggy in some browsers
|
|
7
|
+
export const timestamp = IN_BROWSER && performance.now() && navigationStart
|
|
8
|
+
? () => Math.round(performance.now() + navigationStart)
|
|
9
|
+
: () => Date.now();
|
|
4
10
|
export const stars = 'repeat' in String.prototype
|
|
5
11
|
? (str) => '*'.repeat(str.length)
|
|
6
12
|
: (str) => str.replace(/./g, '*');
|
|
@@ -11,7 +17,6 @@ export function normSpaces(str) {
|
|
|
11
17
|
export function isURL(s) {
|
|
12
18
|
return s.startsWith('https://') || s.startsWith('http://');
|
|
13
19
|
}
|
|
14
|
-
export const IN_BROWSER = !(typeof window === 'undefined');
|
|
15
20
|
// TODO: JOIN IT WITH LOGGER somehow (use logging decorators?); Don't forget about index.js loggin when there is no logger instance.
|
|
16
21
|
export const DOCS_HOST = 'https://docs.openreplay.com';
|
|
17
22
|
const warnedFeatures = {};
|
|
@@ -33,14 +38,15 @@ export function getLabelAttribute(e) {
|
|
|
33
38
|
}
|
|
34
39
|
return value;
|
|
35
40
|
}
|
|
36
|
-
export function hasOpenreplayAttribute(e,
|
|
37
|
-
const newName = `data-openreplay-${
|
|
41
|
+
export function hasOpenreplayAttribute(e, attr) {
|
|
42
|
+
const newName = `data-openreplay-${attr}`;
|
|
38
43
|
if (e.hasAttribute(newName)) {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
+
// @ts-ignore
|
|
45
|
+
if (DEPRECATED_ATTRS[attr]) {
|
|
46
|
+
deprecationWarn(`"${newName}" attribute`,
|
|
47
|
+
// @ts-ignore
|
|
48
|
+
`"${DEPRECATED_ATTRS[attr]}" attribute`, '/installation/sanitize-data');
|
|
49
|
+
}
|
|
44
50
|
return true;
|
|
45
51
|
}
|
|
46
52
|
return false;
|
package/package.json
CHANGED