emojis = Generators.observe(notify => {
const bisect = d3.bisector(([, value]) => value);
const entries = [];
const counts = new Map;
const source = new EventSource("https://stream.emojitracker.com/subscribe/eps");
let head = null;
let tail = null;
function process(data) {
for (const [key, value] of data) {
if (counts.has(key)) {
const value0 = counts.get(key);
const value1 = value0 + value;
counts.set(key, value1);
if (value > 0) {
let i0 = bisect.left(entries, value0);
let i1 = bisect.left(entries, value1) - 1;
if (i0 <= i1) {
while (entries[i0][0] !== key) ++i0;
while (i0 < i1) entries[i0] = entries[i0 + 1], ++i0;
entries[i1] = [key, value1];
}
} else {
let i0 = bisect.right(entries, value0) - 1;
let i1 = bisect.right(entries, value1);
if (i0 >= i1) {
while (entries[i0][0] !== key) --i0;
if (value1) {
while (i0 > i1) entries[i0] = entries[i0 - 1], --i0;
entries[i1] = [key, value1];
} else {
counts.delete(key);
entries.splice(i0, 1);
}
}
}
} else {
counts.set(key, value);
entries.splice(bisect.right(entries, value), 0, [key, value]);
}
}
}
source.onmessage = ({data}) => {
data = Object.entries(JSON.parse(data));
process(data);
const node = {time: Date.now(), data, next: null};
if (tail === null) tail = head = node;
else tail = tail.next = node;
};
const timer = d3.interval(() => {
const expires = Date.now() - duration;
while (head !== null && head.time < expires) {
process(head.data.map(([key, value]) => [key, -value]));
head = head.next;
}
if (head === null) tail = null;
notify(entries);
}, delay);
return () => {
timer.stop();
source.close();
};
})