1 |
|
2 |
|
3 |
|
4 |
|
5 | import {
|
6 | csvParse as d3CsvParse,
|
7 | tsvParse as d3TsvParse,
|
8 | csvParseRows as d3CsvParseRows,
|
9 | tsvParseRows as d3TsvParseRows,
|
10 | } from "d3-dsv";
|
11 | import {isUndefined, isDefined, isObject, isValue, notEmpty, isArray, capitalize} from "../../module/util";
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 |
|
18 | export default {
|
19 | |
20 |
|
21 |
|
22 |
|
23 |
|
24 |
|
25 |
|
26 | convertData(args, callback: Function): object {
|
27 | let data;
|
28 |
|
29 | if (args.bindto) {
|
30 | data = {};
|
31 |
|
32 | ["url", "mimeType", "headers", "keys", "json", "keys", "rows", "columns"]
|
33 | .forEach(v => {
|
34 | const key = `data_${v}`;
|
35 |
|
36 | if (key in args) {
|
37 | data[v] = args[key];
|
38 | }
|
39 | });
|
40 | } else {
|
41 | data = args;
|
42 | }
|
43 |
|
44 | if (data.url && callback) {
|
45 | this.convertUrlToData(data.url, data.mimeType, data.headers, data.keys, callback);
|
46 | } else if (data.json) {
|
47 | data = this.convertJsonToData(data.json, data.keys);
|
48 | } else if (data.rows) {
|
49 | data = this.convertRowsToData(data.rows);
|
50 | } else if (data.columns) {
|
51 | data = this.convertColumnsToData(data.columns);
|
52 | } else if (args.bindto) {
|
53 | throw Error("url or json or rows or columns is required.");
|
54 | }
|
55 |
|
56 | return isArray(data) && data;
|
57 | },
|
58 |
|
59 | |
60 |
|
61 |
|
62 |
|
63 |
|
64 |
|
65 |
|
66 |
|
67 |
|
68 | convertUrlToData(url: string, mimeType = "csv", headers: object, keys: object, done: Function): void {
|
69 | const req = new XMLHttpRequest();
|
70 |
|
71 | req.open("GET", url);
|
72 |
|
73 | if (headers) {
|
74 | Object.keys(headers).forEach(key => {
|
75 | req.setRequestHeader(key, headers[key]);
|
76 | });
|
77 | }
|
78 |
|
79 | req.onreadystatechange = () => {
|
80 | if (req.readyState === 4) {
|
81 | if (req.status === 200) {
|
82 | const response = req.responseText;
|
83 |
|
84 | response && done.call(this,
|
85 | this[`convert${capitalize(mimeType)}ToData`](
|
86 | mimeType === "json" ? JSON.parse(response) : response,
|
87 | keys
|
88 | ));
|
89 | } else {
|
90 | throw new Error(`${url}: Something went wrong loading!`);
|
91 | }
|
92 | }
|
93 | };
|
94 |
|
95 | req.send();
|
96 | },
|
97 |
|
98 | |
99 |
|
100 |
|
101 |
|
102 |
|
103 |
|
104 |
|
105 | convertCsvTsvToData(parser, xsv) {
|
106 | const rows = parser.rows(xsv);
|
107 | let d;
|
108 |
|
109 | if (rows.length === 1) {
|
110 | d = [{}];
|
111 |
|
112 | rows[0].forEach(id => {
|
113 | d[0][id] = null;
|
114 | });
|
115 | } else {
|
116 | d = parser.parse(xsv);
|
117 | }
|
118 |
|
119 | return d;
|
120 | },
|
121 |
|
122 | convertCsvToData(xsv) {
|
123 | return this.convertCsvTsvToData({
|
124 | rows: d3CsvParseRows,
|
125 | parse: d3CsvParse
|
126 | }, xsv);
|
127 | },
|
128 |
|
129 | convertTsvToData(tsv) {
|
130 | return this.convertCsvTsvToData({
|
131 | rows: d3TsvParseRows,
|
132 | parse: d3TsvParse
|
133 | }, tsv);
|
134 | },
|
135 |
|
136 | convertJsonToData(json, keysParam) {
|
137 | const {config} = this;
|
138 | const newRows: string[][] = [];
|
139 | let targetKeys: string[];
|
140 | let data;
|
141 |
|
142 | if (isArray(json)) {
|
143 | const keys = keysParam || config.data_keys;
|
144 |
|
145 | if (keys.x) {
|
146 | targetKeys = keys.value.concat(keys.x);
|
147 | config.data_x = keys.x;
|
148 | } else {
|
149 | targetKeys = keys.value;
|
150 | }
|
151 |
|
152 | newRows.push(targetKeys);
|
153 |
|
154 | json.forEach(o => {
|
155 | const newRow = targetKeys.map(key => {
|
156 |
|
157 | let v = this.findValueInJson(o, key);
|
158 |
|
159 | if (isUndefined(v)) {
|
160 | v = null;
|
161 | }
|
162 |
|
163 | return v;
|
164 | });
|
165 |
|
166 | newRows.push(newRow);
|
167 | });
|
168 |
|
169 | data = this.convertRowsToData(newRows);
|
170 | } else {
|
171 | Object.keys(json).forEach(key => {
|
172 | const tmp = json[key].concat();
|
173 |
|
174 | tmp.unshift(key);
|
175 | newRows.push(tmp);
|
176 | });
|
177 |
|
178 | data = this.convertColumnsToData(newRows);
|
179 | }
|
180 |
|
181 | return data;
|
182 | },
|
183 |
|
184 | findValueInJson(object, path) {
|
185 | if (object[path] !== undefined) {
|
186 | return object[path];
|
187 | }
|
188 |
|
189 | const convertedPath = path.replace(/\[(\w+)\]/g, ".$1");
|
190 | const pathArray = convertedPath.replace(/^\./, "").split(".");
|
191 | let target = object;
|
192 |
|
193 | pathArray.some(k => !(
|
194 | target = target && k in target ?
|
195 | target[k] : undefined
|
196 | ));
|
197 |
|
198 | return target;
|
199 | },
|
200 |
|
201 | convertRowsToData(rows) {
|
202 | const keys = rows[0];
|
203 | const newRows: any[] = [];
|
204 |
|
205 | rows.forEach((row, i) => {
|
206 | if (i > 0) {
|
207 | const newRow = {};
|
208 |
|
209 | row.forEach((v, j) => {
|
210 | if (isUndefined(v)) {
|
211 | throw new Error(`Source data is missing a component at (${i}, ${j})!`);
|
212 | }
|
213 |
|
214 | newRow[keys[j]] = v;
|
215 | });
|
216 |
|
217 | newRows.push(newRow);
|
218 | }
|
219 | });
|
220 |
|
221 | return newRows;
|
222 | },
|
223 |
|
224 | convertColumnsToData(columns) {
|
225 | const newRows: any[] = [];
|
226 |
|
227 | columns.forEach((col, i) => {
|
228 | const key = col[0];
|
229 |
|
230 | col.forEach((v, j) => {
|
231 | if (j > 0) {
|
232 | if (isUndefined(newRows[j - 1])) {
|
233 | newRows[j - 1] = {};
|
234 | }
|
235 |
|
236 | if (isUndefined(v)) {
|
237 | throw new Error(`Source data is missing a component at (${i}, ${j})!`);
|
238 | }
|
239 |
|
240 | newRows[j - 1][key] = v;
|
241 | }
|
242 | });
|
243 | });
|
244 |
|
245 | return newRows;
|
246 | },
|
247 |
|
248 | convertDataToTargets(data, appendXs) {
|
249 | const $$ = this;
|
250 | const {axis, config, state} = $$;
|
251 | let isCategorized = false;
|
252 | let isTimeSeries = false;
|
253 | let isCustomX = false;
|
254 |
|
255 | if (axis) {
|
256 | isCategorized = axis.isCategorized();
|
257 | isTimeSeries = axis.isTimeSeries();
|
258 | isCustomX = axis.isCustomX();
|
259 | }
|
260 |
|
261 | const dataKeys = Object.keys(data[0] || {});
|
262 | const ids = dataKeys.length ? dataKeys.filter($$.isNotX, $$) : [];
|
263 | const xs = dataKeys.length ? dataKeys.filter($$.isX, $$) : [];
|
264 |
|
265 | let xsData;
|
266 |
|
267 |
|
268 | ids.forEach(id => {
|
269 | const xKey = this.getXKey(id);
|
270 |
|
271 | if (isCustomX || isTimeSeries) {
|
272 |
|
273 | if (xs.indexOf(xKey) >= 0) {
|
274 | xsData = ((appendXs && $$.data.xs[id]) || [])
|
275 | .concat(
|
276 | data.map(d => d[xKey])
|
277 | .filter(isValue)
|
278 | .map((rawX, i) => $$.generateTargetX(rawX, id, i))
|
279 | );
|
280 | } else if (config.data_x) {
|
281 |
|
282 | xsData = this.getOtherTargetXs();
|
283 | } else if (notEmpty(config.data_xs)) {
|
284 |
|
285 | xsData = $$.getXValuesOfXKey(xKey, $$.data.targets);
|
286 | }
|
287 |
|
288 | } else {
|
289 | xsData = data.map((d, i) => i);
|
290 | }
|
291 |
|
292 | xsData && (this.data.xs[id] = xsData);
|
293 | });
|
294 |
|
295 |
|
296 | ids.forEach(id => {
|
297 | if (!this.data.xs[id]) {
|
298 | throw new Error(`x is not defined for id = "${id}".`);
|
299 | }
|
300 | });
|
301 |
|
302 |
|
303 | const targets = ids.map((id, index) => {
|
304 | const convertedId = config.data_idConverter.bind($$.api)(id);
|
305 | const xKey = $$.getXKey(id);
|
306 | const isCategory = isCustomX && isCategorized;
|
307 | const hasCategory = isCategory && data.map(v => v.x)
|
308 | .every(v => config.axis_x_categories.indexOf(v) > -1);
|
309 |
|
310 | return {
|
311 | id: convertedId,
|
312 | id_org: id,
|
313 | values: data.map((d, i) => {
|
314 | const rawX = d[xKey];
|
315 | let value = d[id];
|
316 | let x;
|
317 |
|
318 | value = value !== null && !isNaN(value) && !isObject(value) ?
|
319 | +value : (isArray(value) || isObject(value) ? value : null);
|
320 |
|
321 |
|
322 | if ((isCategory || state.hasRadar) && index === 0 && !isUndefined(rawX)) {
|
323 | if (!hasCategory && index === 0 && i === 0) {
|
324 | config.axis_x_categories = [];
|
325 | }
|
326 |
|
327 | x = config.axis_x_categories.indexOf(rawX);
|
328 |
|
329 | if (x === -1) {
|
330 | x = config.axis_x_categories.length;
|
331 | config.axis_x_categories.push(rawX);
|
332 | }
|
333 | } else {
|
334 | x = $$.generateTargetX(rawX, id, i);
|
335 | }
|
336 |
|
337 |
|
338 | if (isUndefined(value) || $$.data.xs[id].length <= i) {
|
339 | x = undefined;
|
340 | }
|
341 |
|
342 | return {x, value, id: convertedId};
|
343 | }).filter(v => isDefined(v.x))
|
344 | };
|
345 | });
|
346 |
|
347 |
|
348 | targets.forEach(t => {
|
349 |
|
350 | if (config.data_xSort) {
|
351 | t.values = t.values.sort((v1, v2) => {
|
352 | const x1 = v1.x || v1.x === 0 ? v1.x : Infinity;
|
353 | const x2 = v2.x || v2.x === 0 ? v2.x : Infinity;
|
354 |
|
355 | return x1 - x2;
|
356 | });
|
357 | }
|
358 |
|
359 |
|
360 | t.values.forEach((v, i) => (v.index = i));
|
361 |
|
362 |
|
363 | $$.data.xs[t.id].sort((v1, v2) => v1 - v2);
|
364 | });
|
365 |
|
366 |
|
367 | state.hasNegativeValue = $$.hasNegativeValueInTargets(targets);
|
368 | state.hasPositiveValue = $$.hasPositiveValueInTargets(targets);
|
369 |
|
370 |
|
371 | if (config.data_type) {
|
372 | $$.setTargetType($$.mapToIds(targets)
|
373 | .filter(id => !(id in config.data_types)), config.data_type);
|
374 | }
|
375 |
|
376 |
|
377 | targets.forEach(d => $$.cache.add(d.id_org, d, true));
|
378 |
|
379 | return targets;
|
380 | }
|
381 | };
|