impl replay the mutations and mouse interactions
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -6,8 +6,10 @@ import {
|
||||
IncrementalSource,
|
||||
fullSnapshotEvent,
|
||||
eventWithTime,
|
||||
MouseInteractions,
|
||||
} from '../types';
|
||||
import eventsStr from './events';
|
||||
import { mirror, getIdNodeMap } from '../utils';
|
||||
|
||||
const _events: eventWithTime[] = JSON.parse(eventsStr);
|
||||
|
||||
@@ -94,13 +96,57 @@ class Replayer {
|
||||
.replace(/>/g, '>'),
|
||||
);
|
||||
this.iframe.contentDocument!.close();
|
||||
mirror.map = getIdNodeMap(this.iframe.contentDocument!);
|
||||
// avoid form submit to refresh the iframe
|
||||
this.iframe.contentDocument!.querySelectorAll('form').forEach(form => {
|
||||
form.addEventListener('submit', evt => evt.preventDefault());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private applyIncremental(d: incrementalData) {
|
||||
switch (d.source) {
|
||||
case IncrementalSource.Mutation:
|
||||
case IncrementalSource.Mutation: {
|
||||
d.texts.forEach(mutation => {
|
||||
const target = (mirror.getNode(mutation.id) as Node) as Text;
|
||||
target.textContent = mutation.value;
|
||||
});
|
||||
d.attributes.forEach(mutation => {
|
||||
const target = (mirror.getNode(mutation.id) as Node) as Element;
|
||||
for (const attributeName in mutation.attributes) {
|
||||
if (typeof attributeName === 'string') {
|
||||
const value = mutation.attributes[attributeName];
|
||||
if (value) {
|
||||
target.setAttribute(attributeName, value);
|
||||
} else {
|
||||
target.removeAttribute(attributeName);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
// TODO: update id node map
|
||||
d.removes.forEach(mutation => {
|
||||
const target = (mirror.getNode(mutation.id) as Node) as Element;
|
||||
const parent = (mirror.getNode(mutation.parentId) as Node) as Element;
|
||||
parent.removeChild(target);
|
||||
});
|
||||
d.adds.forEach(mutation => {
|
||||
const target = (mirror.getNode(mutation.id) as Node) as Element;
|
||||
const parent = (mirror.getNode(mutation.parentId) as Node) as Element;
|
||||
if (mutation.nextId) {
|
||||
const next = (mirror.getNode(mutation.nextId) as Node) as Element;
|
||||
parent.insertBefore(target, next);
|
||||
} else if (mutation.previousId) {
|
||||
const previous = (mirror.getNode(
|
||||
mutation.previousId,
|
||||
) as Node) as Element;
|
||||
parent.insertBefore(target, previous.nextSibling);
|
||||
} else {
|
||||
parent.appendChild(target);
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
case IncrementalSource.MouseMove:
|
||||
d.positions.forEach(p => {
|
||||
later(() => {
|
||||
@@ -109,8 +155,19 @@ class Replayer {
|
||||
}, p.timeOffset);
|
||||
});
|
||||
break;
|
||||
case IncrementalSource.MouseInteraction:
|
||||
case IncrementalSource.MouseInteraction: {
|
||||
const event = new Event(MouseInteractions[d.type].toLowerCase());
|
||||
const target = (mirror.getNode(d.id) as Node) as HTMLElement;
|
||||
target.dispatchEvent(event);
|
||||
if (d.type === MouseInteractions.Blur) {
|
||||
target.blur();
|
||||
} else if (d.type === MouseInteractions.Click) {
|
||||
target.click();
|
||||
} else if (d.type === MouseInteractions.Focus) {
|
||||
target.focus();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case IncrementalSource.Scroll:
|
||||
// TODO: maybe element
|
||||
this.iframe.contentWindow!.scrollTo({
|
||||
@@ -124,13 +181,11 @@ class Replayer {
|
||||
this.iframe.height = `${d.height}px`;
|
||||
break;
|
||||
case IncrementalSource.Input: {
|
||||
const target: HTMLInputElement | null = this.iframe.contentDocument!.querySelector(
|
||||
`[data-rrid="${d.id}"]`,
|
||||
);
|
||||
if (target) {
|
||||
const target: HTMLInputElement = (mirror.getNode(
|
||||
d.id,
|
||||
) as Node) as HTMLInputElement;
|
||||
target.checked = d.isChecked;
|
||||
target.value = d.text;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
||||
21
src/utils.ts
21
src/utils.ts
@@ -1,3 +1,4 @@
|
||||
import { idNodeMap, NodeType, serializeNodeWithId } from 'rrweb-snapshot';
|
||||
import {
|
||||
Mirror,
|
||||
throttleOptions,
|
||||
@@ -29,6 +30,26 @@ export const mirror: Mirror = {
|
||||
},
|
||||
};
|
||||
|
||||
// TODO: transform this into the snapshot repo
|
||||
export function getIdNodeMap(doc: Document) {
|
||||
const map: idNodeMap = {};
|
||||
|
||||
function walk(n: Node) {
|
||||
const node = serializeNodeWithId(n, doc, map);
|
||||
if (!node) {
|
||||
return null;
|
||||
}
|
||||
if (node.type === NodeType.Document || node.type === NodeType.Element) {
|
||||
for (const _n of Array.from(n.childNodes)) {
|
||||
walk(_n);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
walk(doc);
|
||||
return map;
|
||||
}
|
||||
|
||||
// copy from underscore and modified
|
||||
export function throttle<T>(
|
||||
func: (arg: T) => void,
|
||||
|
||||
Reference in New Issue
Block a user