/
createSVGLoader.js
132 lines (115 loc) · 3.93 KB
/
createSVGLoader.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
import eventify from 'ngraph.events';
import createGraph from 'ngraph.graph';
import {createStreamingSVGParser} from 'streaming-svg-parser';
import {setProgress} from './bus';
import {formatNumber} from './utils';
export default function createSVGLoader(url) {
let disposed = false;
let graph = createGraph();
let parseSVGAsync = createStreamingSVGParser(notifyTagOpen, notifyTagClose, true)
let api = eventify({load, dispose, getGraph() { return graph; }});
return api;
function dispose() {
disposed = true;
}
function load() {
let start = window.performance.now();
fetch(url, {mode: 'cors'}).then(response => {
let fetchReader = response.body.getReader();
let decoder = new TextDecoder();
return fetchReader.read().then(processData);
function processData(progress) {
if (disposed) return;
let chunk = (progress.value !== undefined && decoder.decode(progress.value, {stream: !progress.done})) || '';
return parseSVGAsync(chunk).then(() => {
if(progress.done) {
return;
}
return fetchReader.read().then(processData);
});
}
}).then(() => {
console.log('Elapsed: ', window.performance.now() - start);
setProgress(() => ({
message: 'Loading links...'
}));
// now the svg file is loaded. Let's load secondary information
return fetch(getPathTo('node-ids.txt'), {mode: 'cors'}).then(response => response.text()).then(txt => {
let labels = txt.split('\n');
labels.forEach(label => graph.addNode(label));
return labels;
});
}).then((labels) => {
// now load the edges:
let fromId, fetchReader, prevBuffer, weight;
return fetch(getPathTo('links.bin'), {mode: 'cors'})
.then(response => {
fetchReader = response.body.getReader();
return fetchReader.read().then(processData);
});
function processData(progress) {
if (disposed) return;
let chunk = progress.value;
if (!chunk && prevBuffer) {
throw new Error('Incomplete response');
}
if (!chunk) {
setProgress(() => ({
mapLoaded: true,
}));
return graph;
}
if (prevBuffer) {
let tmp = new Uint8Array(prevBuffer.byteLength + chunk.byteLength);
tmp.set(new Uint8Array(prevBuffer), 0);
tmp.set(new Uint8Array(chunk), prevBuffer.byteLength);
chunk = tmp;
prevBuffer = null;
}
let lastAvailableByte = Math.floor(chunk.byteLength / 4) * 4;
if (chunk.byteLength % 4 !== 0) {
prevBuffer = chunk.slice(lastAvailableByte);
}
const view = new DataView(chunk.buffer);
for (let i = 0; i < lastAvailableByte; i += 4) {
let linkId = view.getInt32(i, /* little endian = */ true);
if (linkId < 0) {
fromId = -linkId - 1;
weight = 1;
graph.addNode(labels[fromId]);
} else {
let toId = linkId - 1;
graph.addLink(labels[fromId], labels[toId], weight)
weight += 1;
}
}
if (progress.done) {
setProgress(() => ({
mapLoaded: true
}));
return graph;
}
setProgress(getLinksCount);
return fetchReader.read().then(processData);
}
});
function getLinksCount() {
return {
message: 'Loaded ' + formatNumber(graph.getLinkCount()) + ' relationship records'
}
}
function getPathTo(name) {
if (url.indexOf('http') < 0) return name;
let baseUrl = new URL(url);
baseUrl.pathname = baseUrl.pathname.substr(0, baseUrl.pathname.lastIndexOf('/') + 1) + name;
return baseUrl.toString();
}
return api;
}
function notifyTagOpen(element) {
api.fire('element-start', element);
}
function notifyTagClose(element) {
api.fire('element-end', element)
}
}