part of rrweb #80, support configure mask input types
This commit is contained in:
@@ -5,10 +5,11 @@ import {
|
|||||||
attributes,
|
attributes,
|
||||||
INode,
|
INode,
|
||||||
idNodeMap,
|
idNodeMap,
|
||||||
|
MaskInputOptions,
|
||||||
} from './types';
|
} from './types';
|
||||||
|
|
||||||
let _id = 1;
|
let _id = 1;
|
||||||
const symbolAndNumberRegex = RegExp('[^a-z1-6\-]');
|
const symbolAndNumberRegex = RegExp('[^a-z1-6-]');
|
||||||
|
|
||||||
function genId(): number {
|
function genId(): number {
|
||||||
return _id++;
|
return _id++;
|
||||||
@@ -32,9 +33,9 @@ function getCssRulesString(s: CSSStyleSheet): string | null {
|
|||||||
const rules = s.rules || s.cssRules;
|
const rules = s.rules || s.cssRules;
|
||||||
return rules
|
return rules
|
||||||
? Array.from(rules).reduce(
|
? Array.from(rules).reduce(
|
||||||
(prev, cur) => prev + getCssRuleString(cur),
|
(prev, cur) => prev + getCssRuleString(cur),
|
||||||
'',
|
'',
|
||||||
)
|
)
|
||||||
: null;
|
: null;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return null;
|
return null;
|
||||||
@@ -54,10 +55,7 @@ function isCSSImportRule(rule: CSSRule): rule is CSSImportRule {
|
|||||||
function extractOrigin(url: string): string {
|
function extractOrigin(url: string): string {
|
||||||
let origin;
|
let origin;
|
||||||
if (url.indexOf('//') > -1) {
|
if (url.indexOf('//') > -1) {
|
||||||
origin = url
|
origin = url.split('/').slice(0, 3).join('/');
|
||||||
.split('/')
|
|
||||||
.slice(0, 3)
|
|
||||||
.join('/');
|
|
||||||
} else {
|
} else {
|
||||||
origin = url.split('/')[0];
|
origin = url.split('/')[0];
|
||||||
}
|
}
|
||||||
@@ -114,7 +112,7 @@ function getAbsoluteSrcsetString(doc: Document, attributeValue: string) {
|
|||||||
// srcset attributes is defined as such:
|
// srcset attributes is defined as such:
|
||||||
// srcset = "url size,url1 size1"
|
// srcset = "url size,url1 size1"
|
||||||
const resultingSrcsetString = srcsetValues
|
const resultingSrcsetString = srcsetValues
|
||||||
.map(srcItem => {
|
.map((srcItem) => {
|
||||||
// removing all but middle spaces
|
// removing all but middle spaces
|
||||||
const trimmedSrcItem = srcItem.trimLeft().trimRight();
|
const trimmedSrcItem = srcItem.trimLeft().trimRight();
|
||||||
const urlAndSize = trimmedSrcItem.split(' ');
|
const urlAndSize = trimmedSrcItem.split(' ');
|
||||||
@@ -168,7 +166,7 @@ function serializeNode(
|
|||||||
doc: Document,
|
doc: Document,
|
||||||
blockClass: string | RegExp,
|
blockClass: string | RegExp,
|
||||||
inlineStylesheet: boolean,
|
inlineStylesheet: boolean,
|
||||||
maskAllInputs: boolean,
|
maskInputOptions: MaskInputOptions = {},
|
||||||
): serializedNode | false {
|
): serializedNode | false {
|
||||||
switch (n.nodeType) {
|
switch (n.nodeType) {
|
||||||
case n.DOCUMENT_NODE:
|
case n.DOCUMENT_NODE:
|
||||||
@@ -188,7 +186,7 @@ function serializeNode(
|
|||||||
if (typeof blockClass === 'string') {
|
if (typeof blockClass === 'string') {
|
||||||
needBlock = (n as HTMLElement).classList.contains(blockClass);
|
needBlock = (n as HTMLElement).classList.contains(blockClass);
|
||||||
} else {
|
} else {
|
||||||
(n as HTMLElement).classList.forEach(className => {
|
(n as HTMLElement).classList.forEach((className) => {
|
||||||
if (blockClass.test(className)) {
|
if (blockClass.test(className)) {
|
||||||
needBlock = true;
|
needBlock = true;
|
||||||
}
|
}
|
||||||
@@ -201,7 +199,7 @@ function serializeNode(
|
|||||||
}
|
}
|
||||||
// remote css
|
// remote css
|
||||||
if (tagName === 'link' && inlineStylesheet) {
|
if (tagName === 'link' && inlineStylesheet) {
|
||||||
const stylesheet = Array.from(doc.styleSheets).find(s => {
|
const stylesheet = Array.from(doc.styleSheets).find((s) => {
|
||||||
return s.href === (n as HTMLLinkElement).href;
|
return s.href === (n as HTMLLinkElement).href;
|
||||||
});
|
});
|
||||||
const cssText = getCssRulesString(stylesheet as CSSStyleSheet);
|
const cssText = getCssRulesString(stylesheet as CSSStyleSheet);
|
||||||
@@ -246,7 +244,11 @@ function serializeNode(
|
|||||||
attributes.type !== 'button' &&
|
attributes.type !== 'button' &&
|
||||||
value
|
value
|
||||||
) {
|
) {
|
||||||
attributes.value = maskAllInputs ? '*'.repeat(value.length) : value;
|
attributes.value = maskInputOptions[
|
||||||
|
attributes.type as keyof MaskInputOptions
|
||||||
|
]
|
||||||
|
? '*'.repeat(value.length)
|
||||||
|
: value;
|
||||||
} else if ((n as HTMLInputElement).checked) {
|
} else if ((n as HTMLInputElement).checked) {
|
||||||
attributes.checked = (n as HTMLInputElement).checked;
|
attributes.checked = (n as HTMLInputElement).checked;
|
||||||
}
|
}
|
||||||
@@ -320,14 +322,14 @@ export function serializeNodeWithId(
|
|||||||
blockClass: string | RegExp,
|
blockClass: string | RegExp,
|
||||||
skipChild = false,
|
skipChild = false,
|
||||||
inlineStylesheet = true,
|
inlineStylesheet = true,
|
||||||
maskAllInputs = false,
|
maskInputOptions?: MaskInputOptions,
|
||||||
): serializedNodeWithId | null {
|
): serializedNodeWithId | null {
|
||||||
const _serializedNode = serializeNode(
|
const _serializedNode = serializeNode(
|
||||||
n,
|
n,
|
||||||
doc,
|
doc,
|
||||||
blockClass,
|
blockClass,
|
||||||
inlineStylesheet,
|
inlineStylesheet,
|
||||||
maskAllInputs,
|
maskInputOptions,
|
||||||
);
|
);
|
||||||
if (!_serializedNode) {
|
if (!_serializedNode) {
|
||||||
// TODO: dev only
|
// TODO: dev only
|
||||||
@@ -363,7 +365,7 @@ export function serializeNodeWithId(
|
|||||||
blockClass,
|
blockClass,
|
||||||
skipChild,
|
skipChild,
|
||||||
inlineStylesheet,
|
inlineStylesheet,
|
||||||
maskAllInputs,
|
maskInputOptions,
|
||||||
);
|
);
|
||||||
if (serializedChildNode) {
|
if (serializedChildNode) {
|
||||||
serializedNode.childNodes.push(serializedChildNode);
|
serializedNode.childNodes.push(serializedChildNode);
|
||||||
@@ -377,9 +379,29 @@ function snapshot(
|
|||||||
n: Document,
|
n: Document,
|
||||||
blockClass: string | RegExp = 'rr-block',
|
blockClass: string | RegExp = 'rr-block',
|
||||||
inlineStylesheet = true,
|
inlineStylesheet = true,
|
||||||
maskAllInputs = false,
|
maskAllInputsOrOptions: boolean | MaskInputOptions,
|
||||||
): [serializedNodeWithId | null, idNodeMap] {
|
): [serializedNodeWithId | null, idNodeMap] {
|
||||||
const idNodeMap: idNodeMap = {};
|
const idNodeMap: idNodeMap = {};
|
||||||
|
const maskInputOptions: MaskInputOptions =
|
||||||
|
maskAllInputsOrOptions === true
|
||||||
|
? {
|
||||||
|
color: true,
|
||||||
|
date: true,
|
||||||
|
'datetime-local': true,
|
||||||
|
email: true,
|
||||||
|
month: true,
|
||||||
|
number: true,
|
||||||
|
range: true,
|
||||||
|
search: true,
|
||||||
|
tel: true,
|
||||||
|
text: true,
|
||||||
|
time: true,
|
||||||
|
url: true,
|
||||||
|
week: true,
|
||||||
|
}
|
||||||
|
: maskAllInputsOrOptions === false
|
||||||
|
? {}
|
||||||
|
: maskAllInputsOrOptions;
|
||||||
return [
|
return [
|
||||||
serializeNodeWithId(
|
serializeNodeWithId(
|
||||||
n,
|
n,
|
||||||
@@ -388,7 +410,7 @@ function snapshot(
|
|||||||
blockClass,
|
blockClass,
|
||||||
false,
|
false,
|
||||||
inlineStylesheet,
|
inlineStylesheet,
|
||||||
maskAllInputs,
|
maskInputOptions,
|
||||||
),
|
),
|
||||||
idNodeMap,
|
idNodeMap,
|
||||||
];
|
];
|
||||||
|
|||||||
16
src/types.ts
16
src/types.ts
@@ -68,3 +68,19 @@ export interface INode extends Node {
|
|||||||
export type idNodeMap = {
|
export type idNodeMap = {
|
||||||
[key: number]: INode;
|
[key: number]: INode;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type MaskInputOptions = Partial<{
|
||||||
|
color: boolean;
|
||||||
|
date: boolean;
|
||||||
|
'datetime-local': boolean;
|
||||||
|
email: boolean;
|
||||||
|
month: boolean;
|
||||||
|
number: boolean;
|
||||||
|
range: boolean;
|
||||||
|
search: boolean;
|
||||||
|
tel: boolean;
|
||||||
|
text: boolean;
|
||||||
|
time: boolean;
|
||||||
|
url: boolean;
|
||||||
|
week: boolean;
|
||||||
|
}>;
|
||||||
|
|||||||
Reference in New Issue
Block a user