• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

naver / billboard.js / 21126911356

19 Jan 2026 05:55AM UTC coverage: 94.082% (-0.08%) from 94.157%
21126911356

push

github

web-flow
chore(deps-dev): update dependency (#4083)

update dependencies to the latest

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>

6624 of 7321 branches covered (90.48%)

Branch coverage included in aggregate %.

8225 of 8462 relevant lines covered (97.2%)

25510.97 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

96.91
/src/ChartInternal/data/convert.ts
1
/**
2
 * Copyright (c) 2017 ~ present NAVER Corp.
3
 * billboard.js project is licensed under the MIT license
4
 */
5
import {
6
        isArray,
7
        isDefined,
8
        isEmpty,
9
        isObject,
10
        isUndefined,
11
        isValue,
12
        notEmpty,
13
        toSet
14
} from "../../module/util";
15
import {runWorker} from "../../module/worker";
16
import type {IData} from "../data/IData";
17
import {columns, json, rows, url} from "./convert.helper";
18

19
/**
20
 * Get data key for JSON
21
 * @param {string|object} keysParam Key params
22
 * @param {object} config Config object
23
 * @returns {string} Data key
24
 * @private
25
 */
26
function _getDataKeyForJson(keysParam, config) {
27
        const keys = keysParam || config?.data_keys;
165✔
28

29
        if (keys?.x) {
165✔
30
                config.data_x = keys.x;
63✔
31
        }
32

33
        return keys;
165✔
34
}
35

36
/**
37
 * Set `xs` for each id
38
 * @param {string[]} ids Ids to set xs
39
 * @param {object[]} data Data to set xs from
40
 * @param {object} params Parameters for setting xs
41
 * @param {boolean} params.appendXs Whether to append xs
42
 * @param {string[]} params.xs X keys to set xs from
43
 * @param {boolean} params.categorized Whether the axis is categorized
44
 * @param {boolean} params.timeSeries Whether the axis is time series
45
 * @param {boolean} params.customX Whether the x is custom
46
 * @private
47
 */
48
function _setXS(
49
        ids: string[],
50
        data: {[key: string]: number | null}[],
51
        params: {appendXs, xs, categorized: boolean, timeSeries: boolean, customX: boolean}
52
): void {
53
        const $$ = this;
6,381✔
54
        const {config} = $$;
6,381✔
55
        let xsData;
56

57
        ids.forEach(id => {
6,381✔
58
                const xKey = $$.getXKey(id);
14,130✔
59

60
                if (params.customX || params.timeSeries) {
14,130✔
61
                        // if included in input data
62
                        if (params.xs.indexOf(xKey) >= 0) {
3,735✔
63
                                xsData = ((params.appendXs && $$.data.xs[id]) || [])
3,726✔
64
                                        .concat(
65
                                                data.map((d, i) => {
66
                                                        const rawX = isValue(d[xKey]);
16,698✔
67
                                                        return rawX ? $$.generateTargetX(rawX, id, i) : false;
16,698✔
68
                                                }).filter(v => v !== false)
16,698✔
69
                                        );
70
                        } else if (config.data_x) {
9✔
71
                                // if not included in input data, find from preloaded data of other id's x
72
                                xsData = this.getOtherTargetXs();
6✔
73
                        } else if (notEmpty(config.data_xs)) {
3!
74
                                // if not included in input data, find from preloaded data
75
                                xsData = $$.getXValuesOfXKey(xKey, $$.data.targets);
3✔
76
                        }
77
                        // MEMO: if no x included, use same x of current will be used
78
                } else {
79
                        xsData = data.map((d, i) => i);
45,351✔
80
                }
81

82
                if (xsData) {
14,130!
83
                        $$.data.xs[id] = xsData;
14,130✔
84
                } else {
85
                        throw new Error(`x is not defined for id = "${id}".`);
×
86
                }
87
        });
88
}
89

90
/**
91
 * Data convert
92
 * @memberof ChartInternal
93
 * @private
94
 */
95
export default {
96
        /**
97
         * Convert data according its type
98
         * @param {object} args data object
99
         * @param {function} [callback] callback for url(XHR) type loading
100
         * @private
101
         */
102
        convertData(args, callback: Function): void {
103
                const {config} = this;
6,414✔
104
                const useWorker = d => d?.length && !isEmpty(d[0]) ? config.boost_useWorker : false;
6,414✔
105
                let data = args;
6,414✔
106

107
                if (args.bindto) {
6,414✔
108
                        data = {};
6,162✔
109

110
                        ["url", "mimeType", "headers", "keys", "json", "keys", "rows", "columns"]
6,162✔
111
                                .forEach(v => {
112
                                        const key = `data_${v}`;
49,296✔
113

114
                                        if (key in args) {
49,296!
115
                                                data[v] = args[key];
49,296✔
116
                                        }
117
                                });
118
                }
119

120
                if (data.url && callback) {
6,414✔
121
                        url(data.url, data.mimeType, data.headers, _getDataKeyForJson(data.keys, config),
12✔
122
                                callback);
123
                } else if (data.json) {
6,402✔
124
                        runWorker(useWorker(data.json), json, callback, [columns, rows])(
153✔
125
                                data.json,
126
                                _getDataKeyForJson(data.keys, config)
127
                        );
128
                } else if (data.rows) {
6,249✔
129
                        runWorker(useWorker(data.rows), rows, callback)(data.rows);
36✔
130
                } else if (data.columns) {
6,213✔
131
                        runWorker(useWorker(data.columns), columns, callback)(data.columns);
6,210✔
132
                } else if (args.bindto) {
3!
133
                        throw Error("url or json or rows or columns is required.");
3✔
134
                }
135
        },
136

137
        /**
138
         * Convert data to targets
139
         * @param {object[]} data Data to convert
140
         * @param {boolean} appendXs Whether to append xs
141
         * @returns {IData[]} Converted targets
142
         * @private
143
         */
144
        convertDataToTargets(data: {[key: string]: number | null}[], appendXs: boolean): IData[] {
145
                const $$ = this;
6,381✔
146
                const {axis, config, state} = $$;
6,381✔
147
                const chartType = config.data_type;
6,381✔
148
                const dataKeys = Object.keys(data[0] || {});
6,381✔
149

150
                // Extract ids and xs from data keys to handle x and non-x values
151
                const {ids, xs} = dataKeys.length ?
6,381✔
152
                        dataKeys.reduce((acc, key) => {
153
                                if ($$.isX.call($$, key)) {
15,633✔
154
                                        acc.xs.push(key);
1,503✔
155
                                } else if ($$.isNotX.call($$, key)) {
14,130!
156
                                        acc.ids.push(key);
14,130✔
157
                                }
158

159
                                return acc;
15,633✔
160
                        }, {ids: [] as string[], xs: [] as string[]}) :
161
                        {ids: [], xs: []};
162

163
                const params = {
6,381✔
164
                        appendXs,
165
                        xs,
166
                        idConverter: config.data_idConverter.bind($$.api),
167
                        categorized: axis?.isCategorized(),
168
                        timeSeries: axis?.isTimeSeries(),
169
                        customX: axis?.isCustomX()
170
                };
171

172
                // save x for update data by load when custom x and bb.x API
173
                _setXS.bind($$)(ids, data, params);
6,381✔
174

175
                // convert to target
176
                const targets = ids.map((id, index) => {
6,381✔
177
                        const convertedId = config.data_idConverter.bind($$.api)(id);
14,130✔
178
                        const xKey = $$.getXKey(id);
14,130✔
179
                        const isCategory = params.customX && params.categorized;
14,130✔
180
                        const hasCategory = isCategory && (() => {
14,130✔
181
                                const categorySet = toSet(config.axis_x_categories);
1,833✔
182
                                return data.every(v => categorySet.has(v.x));
2,514✔
183
                        })();
184

185
                        // when .load() with 'append' option is used for indexed axis
186
                        // @ts-ignore
187
                        const isDataAppend = data.__append__;
14,130✔
188
                        const xIndex = xKey === null && isDataAppend ? $$.api.data.values(id).length : 0;
14,130✔
189

190
                        return {
14,130✔
191
                                id: convertedId,
192
                                id_org: id,
193
                                values: data.map((d, i) => {
194
                                        const rawX = d[xKey];
62,094✔
195
                                        let value = d[id];
62,094✔
196
                                        let x;
197

198
                                        value = value !== null && !isNaN(value) && !isObject(value) ?
62,094✔
199
                                                +value :
200
                                                (isArray(value) || isObject(value) ? value : null);
6,648✔
201

202
                                        // use x as categories if custom x and categorized
203
                                        if ((isCategory || state.hasRadar) && index === 0 && !isUndefined(rawX)) {
62,094✔
204
                                                if (!hasCategory && index === 0 && i === 0 && !isDataAppend) {
2,094✔
205
                                                        config.axis_x_categories = [];
447✔
206
                                                }
207

208
                                                x = config.axis_x_categories.indexOf(rawX);
2,094✔
209

210
                                                if (x === -1) {
2,094✔
211
                                                        x = config.axis_x_categories.length;
2,085✔
212
                                                        config.axis_x_categories.push(rawX);
2,085✔
213
                                                }
214
                                        } else {
215
                                                x = $$.generateTargetX(rawX, id, xIndex + i);
60,000✔
216
                                        }
217

218
                                        // mark as x = undefined if value is undefined and filter to remove after mapped
219
                                        if (isUndefined(value) || $$.data.xs[id].length <= i) {
62,094✔
220
                                                x = undefined;
180✔
221
                                        }
222

223
                                        return {
62,094✔
224
                                                x,
225
                                                value,
226
                                                id: convertedId,
227
                                                index: -1
228
                                        };
229
                                }).filter(v => isDefined(v.x))
62,094✔
230
                        };
231
                });
232

233
                // finish targets
234
                targets.forEach(t => {
6,381✔
235
                        // sort values by its x
236
                        if (config.data_xSort) {
14,130✔
237
                                t.values = t.values.sort((v1, v2) => {
14,031✔
238
                                        const x1 = v1.x || v1.x === 0 ? v1.x : Infinity;
65,595✔
239
                                        const x2 = v2.x || v2.x === 0 ? v2.x : Infinity;
65,595✔
240

241
                                        return x1 - x2;
65,595✔
242
                                });
243
                        }
244

245
                        // indexing each value
246
                        t.values.forEach((v, i) => (v.index = i));
61,914✔
247

248
                        // this needs to be sorted because its index and value.index is identical
249
                        $$.data.xs[t.id]?.sort((v1, v2) => v1 - v2);
65,937✔
250
                });
251

252
                // cache information about values
253
                state.hasNegativeValue = $$.hasNegativeValueInTargets(targets);
6,381✔
254
                state.hasPositiveValue = $$.hasPositiveValueInTargets(targets);
6,381✔
255

256
                // set target types
257
                if (chartType && $$.isValidChartType(chartType)) {
6,381✔
258
                        const targetIds = $$.mapToIds(targets)
3,972✔
259
                                .filter(id =>
260
                                        !(id in config.data_types) || !$$.isValidChartType(config.data_types[id])
9,648✔
261
                                );
262

263
                        $$.setTargetType(targetIds, chartType);
3,972✔
264
                }
265

266
                // cache as original id keyed
267
                targets.forEach(d => $$.cache.add(d.id_org, d, true));
14,130✔
268

269
                return targets as IData[];
6,381✔
270
        }
271
};
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc