|
|
|
@@ -19,7 +19,7 @@ const rrwebPlayer: typeof rrwebPlayerMod.default = rrwebPlayerMod as any;
|
|
|
|
|
* export interface IRecordingEvent extends eventWithTime {}
|
|
|
|
|
*
|
|
|
|
|
* Here, for brevity, we define an empty interface
|
|
|
|
|
* and cast all events to any.
|
|
|
|
|
* and cast all events to any.
|
|
|
|
|
*/
|
|
|
|
|
export interface IRecordingEvent {}
|
|
|
|
|
|
|
|
|
@@ -32,6 +32,11 @@ export class SioRecorder extends DeesElement {
|
|
|
|
|
*/
|
|
|
|
|
private events: IRecordingEvent[] = [];
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* status
|
|
|
|
|
*/
|
|
|
|
|
public status: 'recording' | 'playing' | 'stopped' = 'stopped';
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* A reference to rrweb's stop recording function.
|
|
|
|
|
* We'll store it when we begin a record session so we can call it later.
|
|
|
|
@@ -59,9 +64,7 @@ export class SioRecorder extends DeesElement {
|
|
|
|
|
`;
|
|
|
|
|
|
|
|
|
|
render() {
|
|
|
|
|
return html`
|
|
|
|
|
<div id="playback"></div>
|
|
|
|
|
`;
|
|
|
|
|
return html` <div id="playback"></div> `;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
@@ -101,22 +104,29 @@ export class SioRecorder extends DeesElement {
|
|
|
|
|
* Starts an rrweb recording session that tracks the entire DOM,
|
|
|
|
|
* including canvases and cross-origin iframes (if permissible).
|
|
|
|
|
*/
|
|
|
|
|
private startRecording(): void {
|
|
|
|
|
private async startRecording(): void {
|
|
|
|
|
await this.domtoolsPromise;
|
|
|
|
|
this.status = 'recording';
|
|
|
|
|
this.events = [];
|
|
|
|
|
|
|
|
|
|
// For capturing "everything," enable advanced flags:
|
|
|
|
|
this.stopFn = rrweb.record({
|
|
|
|
|
emit: (event: any) => {
|
|
|
|
|
// If you have a stricter type:
|
|
|
|
|
// this.events.push(event as IRecordingEvent);
|
|
|
|
|
// else store as any:
|
|
|
|
|
this.events.push(event);
|
|
|
|
|
},
|
|
|
|
|
// Some recommended settings to capture the "complete" page:
|
|
|
|
|
recordCanvas: true, // record canvas elements
|
|
|
|
|
recordCrossOriginIframes: true, // attempt capturing cross-origin iframes
|
|
|
|
|
// checkoutEveryNms: 1000, // check every N milliseconds
|
|
|
|
|
});
|
|
|
|
|
while (this.status === 'recording') {
|
|
|
|
|
this.stopFn = rrweb.record({
|
|
|
|
|
emit: (event: any) => {
|
|
|
|
|
// If you have a stricter type:
|
|
|
|
|
// this.events.push(event as IRecordingEvent);
|
|
|
|
|
// else store as any:
|
|
|
|
|
this.events.push(event);
|
|
|
|
|
},
|
|
|
|
|
// Some recommended settings to capture the "complete" page:
|
|
|
|
|
recordCanvas: true, // record canvas elements
|
|
|
|
|
recordCrossOriginIframes: true, // attempt capturing cross-origin iframes
|
|
|
|
|
|
|
|
|
|
// checkoutEveryNms: 1000, // check every N milliseconds
|
|
|
|
|
});
|
|
|
|
|
await this.domtools.convenience.smartdelay.delayFor(1000);
|
|
|
|
|
await this.stopFn();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
console.log('Recording has started...');
|
|
|
|
|
}
|
|
|
|
@@ -138,7 +148,7 @@ export class SioRecorder extends DeesElement {
|
|
|
|
|
private async playRecording(): Promise<void> {
|
|
|
|
|
await this.domtoolsPromise;
|
|
|
|
|
if (!this.playbackDiv) return;
|
|
|
|
|
const replayer =new rrwebPlayer({
|
|
|
|
|
const replayer = new rrwebPlayer({
|
|
|
|
|
target: this.playbackDiv, // customizable root element
|
|
|
|
|
props: {
|
|
|
|
|
events: this.events as any,
|
|
|
|
@@ -146,7 +156,6 @@ export class SioRecorder extends DeesElement {
|
|
|
|
|
showController: false,
|
|
|
|
|
width: this.playbackDiv.offsetWidth,
|
|
|
|
|
height: this.playbackDiv.offsetHeight,
|
|
|
|
|
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
this.domtools.convenience.smartdelay.delayFor(0).then(async () => {
|
|
|
|
@@ -163,10 +172,11 @@ export class SioRecorder extends DeesElement {
|
|
|
|
|
public async fixPosition() {
|
|
|
|
|
await this.domtoolsPromise;
|
|
|
|
|
await this.domtools.convenience.smartdelay.delayFor(0);
|
|
|
|
|
const iframe = this.shadowRoot.querySelector('iframe');
|
|
|
|
|
const playbackDiv = this.shadowRoot.querySelector('#playback') as HTMLElement;
|
|
|
|
|
const replayerWrapper = this.shadowRoot.querySelector('.replayer-wrapper') as HTMLElement;
|
|
|
|
|
const replayerMouse = this.shadowRoot.querySelector('.replayer-mouse') as HTMLElement;
|
|
|
|
|
const replayerMouseTail = this.shadowRoot.querySelector('.replayer-mouse-tail') as HTMLElement;
|
|
|
|
|
const iframe = this.shadowRoot.querySelector('iframe');
|
|
|
|
|
replayerWrapper.style.position = 'absolute';
|
|
|
|
|
replayerWrapper.style.top = '0px';
|
|
|
|
|
replayerWrapper.style.left = '0px';
|
|
|
|
@@ -176,6 +186,21 @@ export class SioRecorder extends DeesElement {
|
|
|
|
|
iframe.style.position = 'absolute';
|
|
|
|
|
iframe.style.top = '0px';
|
|
|
|
|
iframe.style.left = '0px';
|
|
|
|
|
iframe.style.border = 'none';
|
|
|
|
|
|
|
|
|
|
// set z-index
|
|
|
|
|
replayerWrapper.style.zIndex = '1000';
|
|
|
|
|
iframe.style.zIndex = '1000';
|
|
|
|
|
replayerMouse.style.zIndex = '1002';
|
|
|
|
|
replayerMouseTail.style.zIndex = '1001';
|
|
|
|
|
|
|
|
|
|
// lets show a mouse cursor
|
|
|
|
|
replayerMouse.style.width = '10px';
|
|
|
|
|
replayerMouse.style.height = '10px';
|
|
|
|
|
replayerMouse.style.background = 'green';
|
|
|
|
|
replayerMouse.style.transform = 'translate(-50%, -50%)';
|
|
|
|
|
replayerMouse.style.borderRadius = '50%';
|
|
|
|
|
replayerMouse.style.border = '1px solid white';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
@@ -185,4 +210,4 @@ export class SioRecorder extends DeesElement {
|
|
|
|
|
this.stopRecording();
|
|
|
|
|
this.playRecording();
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|