-
Notifications
You must be signed in to change notification settings - Fork 14
/
Copy pathreplay.js
177 lines (143 loc) · 4.61 KB
/
replay.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
// create a xterm for replay
function createReplayTerminal() {
// vscode-snazzy https://github.com/Tyriar/vscode-snazzy
// copied from xterm.js website
var baseTheme = {
foreground: '#eff0eb',
background: '#282a36',
selection: '#97979b33',
black: '#282a36',
brightBlack: '#686868',
red: '#ff5c57',
brightRed: '#ff5c57',
green: '#5af78e',
brightGreen: '#5af78e',
yellow: '#f3f99d',
brightYellow: '#f3f99d',
blue: '#57c7ff',
brightBlue: '#57c7ff',
magenta: '#ff6ac1',
brightMagenta: '#ff6ac1',
cyan: '#9aedfe',
brightCyan: '#9aedfe',
white: '#f1f1f0',
brightWhite: '#eff0eb'
};
const term = new Terminal({
fontFamily: `'Fira Code', ui-monospace,SFMono-Regular,'SF Mono',Menlo,Consolas,'Liberation Mono',monospace`,
fontSize: 12,
theme: baseTheme,
convertEol: true,
cursorBlink: true,
});
term.open(document.getElementById('terminal_view'));
term.resize(120, 36);
const weblinksAddon = new WebLinksAddon.WebLinksAddon();
term.loadAddon(weblinksAddon);
// fit the xterm viewpoint to parent element
const fitAddon = new FitAddon.FitAddon();
term.loadAddon(fitAddon);
fitAddon.fit();
return term;
}
// sleep for ms seconds
function _sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
// we could sleep for a long time
// periodically check if we need to end replay.
// This is pretty ugly but the callback mess otherwise
async function sleep(ms, paused) {
var loop_cnt = parseInt(ms / 20) + 1
for (i = 0; i < loop_cnt; i++) {
if (paused()) {
return paused()
}
await _sleep(20)
}
return paused()
}
// convert data to uint8array, we cannot convert it to string as
// it will mess up special characters
function base64ToUint8array(base64) {
var raw = window.atob(base64);
var rawLength = raw.length;
var array = new Uint8Array(new ArrayBuffer(rawLength));
for (i = 0; i < rawLength; i++) {
array[i] = raw.charCodeAt(i);
}
return array;
}
// replay session
// term: xterm, path: session file to replay,
// start: start position to replay in percentile, range 0-100
// paused: callback whether to stop play
// prog: callback to update the progress bar
// shift: callback whether we should change position
// end: callback when playback is finished
async function replay_session(term, records, max_wait, total_dur, start, paused, prog, end) {
var cur = 0
start = parseInt(total_dur * start / 100)
term.reset()
for (const item of records) {
// we will blast through the beginning of the session
if (cur >= start) {
// we are cheating a little bit here, we do not want to wait for too long
exit = await sleep(Math.min(item.Duration, max_wait), paused)
if (exit) {
return
}
}
term.write(base64ToUint8array(item.Data))
cur += item.Duration
if (cur > start) {
prog(parseInt(cur * 100 / total_dur))
}
}
end()
}
function forwardScreen(term, records, end) {
var cur = 0
end = parseInt(total_dur * end / 100)
term.reset()
for (const item of records) {
// we will blast through the beginning of the session
if (cur >= end) {
return
}
term.write(base64ToUint8array(item.Data))
cur += item.Duration
}
}
async function fetchAndParse(path, update) {
var records
var total_dur = 0
// read file from server, we need to await twice.
var res = await fetch(path)
if (!res.ok) {
return
}
records = await res.json()
//calculate the total duration
for (const item of records) {
item.Duration = parseInt(item.Duration / 1000000)
total_dur += item.Duration
}
console.log("total_dur", total_dur)
update(records, total_dur)
}
function Init() {
let term = createReplayTerminal();
var str = [
' ┌────────────────────────────────────────────────────────────────────────────┐\n',
' │ \u001b[32;1mhttps://github.com/syssecfsu/witty\x1b[0m <- click it! │\n',
' └────────────────────────────────────────────────────────────────────────────┘\n',
''
].join('');
term.writeln(str);
// adjust the progress bar size to that of terminal
var view = document.querySelector("#terminal")
var pbar = document.querySelector("#replay-control")
pbar.setAttribute("style", "width:" + (view.offsetWidth - 32) + "px");
return term
}