-
Notifications
You must be signed in to change notification settings - Fork 4
/
tabble.js
85 lines (78 loc) · 1.94 KB
/
tabble.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
/**
* UI element for a tab-separated text area.
* Automatically ensures tabs are evenly-spaced.
*/
export default class Tabble {
/** @param {!Element} elem */
constructor(elem) {
/** @private @const {!Element} */
this.elem_ = elem;
/** @private @const {!Array<number>} */
this.sizes_ = [];
/** @private @const {!Array<!Array<string>>} */
this.rows_ = [];
this.update_ = rateLimit(this.update_, 500);
}
/**
* Writes a single row of text.
* @param {string} text
*/
write(text) {
const cols = text.split('\t');
const row = [];
while (this.sizes_.length < cols.length) this.sizes_.push(0);
for (let [i, s] of cols.entries()) {
const cs = this.sizes_[i];
if (s.length < cs) {
s = pad(s, cs);
} else if (cs < s.length) {
this.sizes_[i] = s.length;
for (let r of this.rows_) {
if (i < r.length) r[i] = pad(r[i], s.length);
}
}
row.push(s);
}
this.rows_.push(row);
this.update_()
}
update_() {
this.elem_.textContent =
this.rows_.map(row => row.join(' ')).join('\n');
}
}
/**
* @param {string} str
* @param {number} size
* @return {string} Padded string.
*/
function pad(str, size) {
if (str.length < size) str += ' '.repeat(size - str.length);
return str;
}
/**
* Rate limits a function to only be called once every rateMillis.
* @param {function(this: THIS)} func
* @param {number} rateMillis
* @return {function(this: THIS)}
* @template THIS
*/
function rateLimit(func, rateMillis) {
let queued = false;
let lastMs = 0;
return function() {
if (queued) return;
const now = new Date().getTime();
if (now - lastMs < rateMillis) {
queued = true;
setTimeout(() => {
queued = false;
func.call(this);
lastMs = new Date().getTime();
}, rateMillis - (now - lastMs));
} else {
func.call(this);
lastMs = new Date().getTime();
}
}
}