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

naver / billboard.js / 14591086771

22 Apr 2025 09:07AM UTC coverage: 87.071% (-7.1%) from 94.191%
14591086771

push

github

Jae Sung Park
fix(core): Fix potential security vulnerability

Add prevention for prototype pollution, by enforcing
options objects to not extend nor chaining Object.prototype

Fix #3975

5626 of 6953 branches covered (80.91%)

Branch coverage included in aggregate %.

9 of 9 new or added lines in 3 files covered. (100.0%)

389 existing lines in 27 files now uncovered.

7446 of 8060 relevant lines covered (92.38%)

11838.6 hits per line

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

94.84
/src/ChartInternal/ChartInternal.ts
1
/**
2
 * Copyright (c) 2017 ~ present NAVER Corp.
3
 * billboard.js project is licensed under the MIT license
4
 * @ignore
5
 */
6
import {select as d3Select} from "d3-selection";
7
import {
8
        timeFormat as d3TimeFormat,
9
        timeParse as d3TimeParse,
10
        utcFormat as d3UtcFormat,
11
        utcParse as d3UtcParse
12
} from "d3-time-format";
13
import type {d3Selection, d3Transition} from "../../types/types";
14
import {$CIRCLE, $COMMON, $TEXT} from "../config/classes";
15
import Options from "../config/Options/Options";
16
import Store from "../config/Store/Store";
17
import {document, window} from "../module/browser";
18
import Cache from "../module/Cache";
19
import {checkModuleImport} from "../module/error";
20
import {generateResize} from "../module/generator";
21
import {
22
        callFn,
23
        capitalize,
24
        convertInputType,
25
        extend,
26
        getOption,
27
        getRandom,
28
        hasStyle,
29
        isFunction,
30
        isObject,
31
        isString,
32
        notEmpty,
33
        sortValue
34
} from "../module/util";
35

36
// data
37
import dataConvert from "./data/convert";
38
import data from "./data/data";
39
import dataLoad from "./data/load";
40

41
// interactions
42
import interaction from "./interactions/interaction";
43

44
// internals
45
import category from "./internals/category"; // used to retrieve radar Axis name
46
import classModule from "./internals/class";
47
import color from "./internals/color";
48
import domain from "./internals/domain";
49
import format from "./internals/format";
50
import legend from "./internals/legend";
51
import redraw from "./internals/redraw";
52
import scale from "./internals/scale";
53
import size from "./internals/size";
54
import style from "./internals/style";
55
import text from "./internals/text";
56
import title from "./internals/title";
57
import tooltip from "./internals/tooltip";
58
import transform from "./internals/transform";
59
import typeInternals from "./internals/type";
60
import shape from "./shape/shape";
61

62
/**
63
 * Internal chart class.
64
 * - Note: Instantiated internally, not exposed for public.
65
 * @class ChartInternal
66
 * @ignore
67
 * @private
68
 */
69
export default class ChartInternal {
70
        public api; // API interface
71
        public config; // config object
72
        public cache; // cache instance
73
        public $el; // elements
74
        public state; // state variables
75
        public charts; // all Chart instances array within page (equivalent of 'bb.instances')
76

77
        // data object
78
        public data = {
2,895✔
79
                xs: {},
80
                targets: []
81
        };
82

83
        // Axis
84
        public axis; // Axis
85

86
        // scales
87
        public scale = {
2,895✔
88
                x: null,
89
                y: null,
90
                y2: null,
91
                subX: null,
92
                subY: null,
93
                subY2: null,
94
                zoom: null
95
        };
96

97
        // original values
98
        public org = {
2,895✔
99
                xScale: null,
100
                xDomain: null
101
        };
102

103
        // formatter function
104
        public color;
105
        public patterns;
106
        public levelColor;
107
        public point;
108
        public brush;
109

110
        // format function
111
        public format = {
2,895✔
112
                extraLineClasses: null,
113
                xAxisTick: null,
114
                dataTime: null, // dataTimeFormat
115
                defaultAxisTime: null, // defaultAxisTimeFormat
116
                axisTime: null // axisTimeFormat
117
        };
118

119
        constructor(api) {
120
                const $$ = this;
2,895✔
121

122
                $$.api = api; // Chart class instance alias
2,895✔
123
                $$.config = new Options();
2,895✔
124
                $$.cache = new Cache();
2,895✔
125

126
                const store = new Store();
2,895✔
127

128
                $$.$el = store.getStore("element");
2,895✔
129
                $$.state = store.getStore("state");
2,895✔
130

131
                $$.$T = $$.$T.bind($$);
2,895✔
132
        }
133

134
        /**
135
         * Get the selection based on transition config
136
         * @param {SVGElement|d3Selection} selection Target selection
137
         * @param {boolean} force Force transition
138
         * @param {string} name Transition name
139
         * @returns {d3Selection}
140
         * @private
141
         */
142
        $T(selection: SVGElement | d3Selection | d3Transition, force?: boolean,
143
                name?: string): d3Selection {
144
                const {config, state} = this;
138,155✔
145
                const duration = config.transition_duration;
138,155✔
146
                const subchart = config.subchart_show;
138,155✔
147
                let t = selection;
138,155✔
148

149
                if (t) {
138,155✔
150
                        // in case of non d3 selection, wrap with d3 selection
151
                        if ("tagName" in t) {
132,720✔
152
                                t = d3Select(t);
1,695✔
153
                        }
154

155
                        // do not transit on:
156
                        // - wheel zoom (state.zooming = true)
157
                        // - when has no subchart
158
                        // - initialization
159
                        // - resizing
160
                        const transit = ((force !== false && duration) || force) &&
132,720✔
161
                                (!state.zooming || state.dragging) &&
162
                                !state.resizing &&
163
                                state.rendered &&
164
                                !subchart;
165

166
                        // @ts-ignore
167
                        t = (transit ? t.transition(name).duration(duration) : t) as d3Selection;
132,720✔
168
                }
169

170
                return t;
138,155✔
171
        }
172

173
        beforeInit(): void {
174
                const $$ = this;
2,895✔
175

176
                $$.callPluginHook("$beforeInit");
2,895✔
177

178
                // can do something
179
                callFn($$.config.onbeforeinit, $$.api);
2,889✔
180
        }
181

182
        afterInit(): void {
183
                const $$ = this;
2,817✔
184

185
                $$.callPluginHook("$afterInit");
2,817✔
186

187
                // can do something
188
                callFn($$.config.onafterinit, $$.api);
2,817✔
189
        }
190

191
        init(): void {
192
                const $$ = <any>this;
2,889✔
193
                const {config, state, $el} = $$;
2,889✔
194
                const useCssRule = config.boost_useCssRule;
2,889✔
195

196
                checkModuleImport($$);
2,889✔
197

198
                state.hasRadar = !state.hasAxis && $$.hasType("radar");
2,826✔
199
                state.hasFunnel = !state.hasAxis && $$.hasType("funnel");
2,826✔
200
                state.hasTreemap = !state.hasAxis && $$.hasType("treemap");
2,826✔
201
                state.hasAxis = !$$.hasArcType() && !state.hasFunnel && !state.hasTreemap;
2,826✔
202

203
                // datetime to be used for uniqueness
204
                state.datetimeId = `bb-${+new Date() * (getRandom() as number)}`;
2,826✔
205

206
                if (useCssRule) {
2,826✔
207
                        // append style element
208
                        const styleEl = document.createElement("style");
12✔
209

210
                        // styleEl.id = styleId;
211
                        styleEl.type = "text/css";
12✔
212
                        document.head.appendChild(styleEl);
12✔
213

214
                        state.style = {
12✔
215
                                rootSelctor: `.${state.datetimeId}`,
216
                                sheet: styleEl.sheet
217
                        };
218

219
                        // used on .destroy()
220
                        $el.style = styleEl;
12✔
221
                }
222

223
                const bindto = {
2,826✔
224
                        element: config.bindto,
225
                        classname: "bb"
226
                };
227

228
                if (isObject(config.bindto)) {
2,826✔
229
                        bindto.element = config.bindto.element || "#chart";
3!
230
                        bindto.classname = config.bindto.classname || bindto.classname;
3!
231
                }
232

233
                // select bind element
234
                $el.chart = isFunction(bindto.element.node) ?
2,826!
235
                        config.bindto.element :
236
                        d3Select(bindto.element || []);
2,826!
237

238
                if ($el.chart.empty()) {
2,826✔
239
                        $el.chart = d3Select(document.body.appendChild(document.createElement("div")));
99✔
240
                }
241

242
                $el.chart.html("")
2,826✔
243
                        .classed(bindto.classname, true)
244
                        .classed(state.datetimeId, useCssRule)
245
                        .style("position", "relative");
246

247
                $$.initParams();
2,826✔
248
                $$.initToRender();
2,826✔
249
        }
250

251
        /**
252
         * Initialize the rendering process
253
         * @param {boolean} forced Force to render process
254
         * @private
255
         */
256
        initToRender(forced?: boolean): void {
257
                const $$ = <any>this;
2,838✔
258
                const {config, state, $el: {chart}} = $$;
2,838✔
259
                const isHidden = () => hasStyle(chart, {display: "none", visibility: "hidden"});
2,838✔
260

261
                const isLazy = config.render.lazy === false ? false : config.render.lazy || isHidden();
2,838✔
262
                const MutationObserver = window.MutationObserver;
2,838✔
263

264
                if (isLazy && MutationObserver && config.render.observe !== false && !forced) {
2,838✔
265
                        new MutationObserver((mutation, observer) => {
9✔
266
                                if (!isHidden()) {
9!
267
                                        observer.disconnect();
9✔
268
                                        !state.rendered && $$.initToRender(true);
9✔
269
                                }
270
                        }).observe(chart.node(), {
271
                                attributes: true,
272
                                attributeFilter: ["class", "style"]
273
                        });
274
                }
275

276
                if (!isLazy || forced) {
2,838✔
277
                        $$.convertData(config, res => {
2,826✔
278
                                $$.initWithData(res);
2,817✔
279
                                $$.afterInit();
2,817✔
280
                        });
281
                }
282
        }
283

284
        initParams(): void {
285
                const $$ = <any>this;
2,826✔
286
                const {config, format, state} = $$;
2,826✔
287
                const isRotated = config.axis_rotated;
2,826✔
288

289
                // color settings
290
                $$.color = $$.generateColor();
2,826✔
291
                $$.levelColor = $$.generateLevelColor();
2,826✔
292

293
                // when 'padding=false' is set, disable axes and subchart. Because they are useless.
294
                if (config.padding === false) {
2,826✔
295
                        config.axis_x_show = false;
6✔
296
                        config.axis_y_show = false;
6✔
297
                        config.axis_y2_show = false;
6✔
298
                        config.subchart_show = false;
6✔
299
                }
300

301
                if ($$.hasPointType() || $$.hasLegendDefsPoint?.()) {
2,826✔
302
                        $$.point = $$.generatePoint();
750✔
303
                }
304

305
                if (state.hasAxis) {
2,826✔
306
                        $$.initClip();
2,064✔
307

308
                        format.extraLineClasses = $$.generateExtraLineClass();
2,064✔
309
                        format.dataTime = config.data_xLocaltime ? d3TimeParse : d3UtcParse;
2,064!
310
                        format.axisTime = config.axis_x_localtime ? d3TimeFormat : d3UtcFormat;
2,064!
311

312
                        const isDragZoom = $$.config.zoom_enabled && $$.config.zoom_type === "drag";
2,064✔
313

314
                        format.defaultAxisTime = d => {
2,064✔
315
                                const {x, zoom} = $$.scale;
1,275✔
316
                                const isZoomed = isDragZoom ?
1,275✔
317
                                        zoom :
318
                                        zoom && x.orgDomain().toString() !== zoom.domain().toString();
1,401✔
319

320
                                const specifier: string = (d.getMilliseconds() && ".%L") ||
1,275!
321
                                        (d.getSeconds() && ".:%S") ||
322
                                        (d.getMinutes() && "%I:%M") ||
323
                                        (d.getHours() && "%I %p") ||
324
                                        (d.getDate() !== 1 && "%b %d") ||
325
                                        (isZoomed && d.getDate() === 1 && "%b'%y") ||
326
                                        (d.getMonth() && "%-m/%-d") || "%Y";
327

328
                                return format.axisTime(specifier)(d);
1,275✔
329
                        };
330
                }
331

332
                state.isLegendRight = config.legend_position === "right";
2,826✔
333
                state.isLegendInset = config.legend_position === "inset";
2,826✔
334

335
                state.isLegendTop = config.legend_inset_anchor === "top-left" ||
2,826✔
336
                        config.legend_inset_anchor === "top-right";
337

338
                state.isLegendLeft = config.legend_inset_anchor === "top-left" ||
2,826✔
339
                        config.legend_inset_anchor === "bottom-left";
340

341
                state.rotatedPadding.top = $$.getResettedPadding(state.rotatedPadding.top);
2,826✔
342
                state.rotatedPadding.right = isRotated && !config.axis_x_show ? 0 : 30;
2,826!
343

344
                state.inputType = convertInputType(
2,826✔
345
                        config.interaction_inputType_mouse,
346
                        config.interaction_inputType_touch
347
                );
348
        }
349

350
        initWithData(data): void {
351
                const $$ = <any>this;
2,817✔
352
                const {config, scale, state, $el, org} = $$;
2,817✔
353
                const {hasAxis, hasFunnel, hasTreemap} = state;
2,817✔
354
                const hasInteraction = config.interaction_enabled;
2,817✔
355
                const hasPolar = $$.hasType("polar");
2,817✔
356
                const labelsBGColor = config.data_labels_backgroundColors;
2,817✔
357

358
                // for arc type, set axes to not be shown
359
                // $$.hasArcType() && ["x", "y", "y2"].forEach(id => (config[`axis_${id}_show`] = false));
360

361
                if (hasAxis) {
2,817✔
362
                        $$.axis = $$.getAxisInstance();
2,055✔
363
                        config.zoom_enabled && $$.initZoom();
2,055✔
364
                }
365

366
                // Init data as targets
367
                $$.data.xs = {};
2,817✔
368
                $$.data.targets = $$.convertDataToTargets(data);
2,817✔
369

370
                if (config.data_filter) {
2,817!
371
                        $$.data.targets = $$.data.targets.filter(config.data_filter.bind($$.api));
×
372
                }
373

374
                // Set targets to hide if needed
375
                if (config.data_hide) {
2,817✔
376
                        $$.addHiddenTargetIds(
6✔
377
                                config.data_hide === true ? $$.mapToIds($$.data.targets) : config.data_hide
6!
378
                        );
379
                }
380

381
                if (config.legend_hide) {
2,817✔
382
                        $$.addHiddenLegendIds(
9✔
383
                                config.legend_hide === true ? $$.mapToIds($$.data.targets) : config.legend_hide
9✔
384
                        );
385
                }
386

387
                // Init sizes and scales
388
                $$.updateSizes();
2,817✔
389
                $$.updateScales(true);
2,817✔
390

391
                // retrieve scale after the 'updateScales()' is called
392
                if (hasAxis) {
2,817✔
393
                        const {x, y, y2, subX, subY, subY2} = scale;
2,055✔
394

395
                        // Set domains for each scale
396
                        if (x) {
2,055!
397
                                x.domain(sortValue($$.getXDomain($$.data.targets), !config.axis_x_inverted));
2,055✔
398
                                subX.domain(x.domain());
2,055✔
399

400
                                // Save original x domain for zoom update
401
                                org.xDomain = x.domain();
2,055✔
402
                        }
403

404
                        if (y) {
2,055!
405
                                y.domain($$.getYDomain($$.data.targets, "y"));
2,055✔
406
                                subY.domain(y.domain());
2,055✔
407
                        }
408

409
                        if (y2) {
2,055✔
410
                                y2.domain($$.getYDomain($$.data.targets, "y2"));
243✔
411
                                subY2 && subY2.domain(y2.domain());
243✔
412
                        }
413
                }
414

415
                // -- Basic Elements --
416
                $el.svg = $el.chart.append("svg")
2,817✔
417
                        .style("overflow", "hidden")
418
                        .style("display", "block");
419

420
                if (hasInteraction && state.inputType) {
2,817✔
421
                        const isTouch = state.inputType === "touch";
2,814✔
422
                        const {onclick, onover, onout} = config;
2,814✔
423

424
                        $el.svg
2,814✔
425
                                .on("click", onclick?.bind($$.api) || null)
5,625✔
426
                                .on(isTouch ? "touchstart" : "mouseenter", onover?.bind($$.api) || null)
8,439✔
427
                                .on(isTouch ? "touchend" : "mouseleave", onout?.bind($$.api) || null);
8,439✔
428
                }
429

430
                config.svg_classname && $el.svg.attr("class", config.svg_classname);
2,817✔
431

432
                // Define defs
433
                const hasColorPatterns = isFunction(config.color_tiles) && $$.patterns;
2,817✔
434

435
                if (
2,817✔
436
                        hasAxis || hasColorPatterns || hasPolar || hasTreemap ||
6,327✔
437
                        labelsBGColor || $$.hasLegendDefsPoint?.()
438
                ) {
439
                        $el.defs = $el.svg.append("defs");
2,181✔
440

441
                        if (hasAxis) {
2,181✔
442
                                ["id", "idXAxis", "idYAxis", "idGrid"].forEach(v => {
2,055✔
443
                                        $$.appendClip($el.defs, state.clip[v]);
8,220✔
444
                                });
445
                        }
446

447
                        // Append data background color filter definition
448
                        $$.generateTextBGColorFilter(labelsBGColor);
2,181✔
449

450
                        // set color patterns
451
                        if (hasColorPatterns) {
2,181✔
452
                                $$.patterns.forEach(p => $el.defs.append(() => p.node));
153✔
453
                        }
454
                }
455

456
                $$.updateSvgSize();
2,817✔
457

458
                // Bind resize event
459
                $$.bindResize();
2,817✔
460

461
                // Define regions
462
                const main = $el.svg.append("g")
2,817✔
463
                        .classed($COMMON.main, true)
464
                        .attr("transform", hasFunnel || hasTreemap ? null : $$.getTranslate("main"));
8,397✔
465

466
                $el.main = main;
2,817✔
467

468
                // initialize subchart when subchart show option is set
469
                config.subchart_show && $$.initSubchart();
2,817✔
470

471
                config.tooltip_show && $$.initTooltip();
2,817✔
472

473
                config.title_text && $$.initTitle();
2,817✔
474
                !hasTreemap && config.legend_show && $$.initLegend();
2,817✔
475

476
                // -- Main Region --
477

478
                // text when empty
479
                if (config.data_empty_label_text) {
2,817!
UNCOV
480
                        main.append("text")
×
481
                                .attr("class", `${$TEXT.text} ${$COMMON.empty}`)
482
                                .attr("text-anchor", "middle") // horizontal centering of text at x position in all browsers.
483
                                .attr("dominant-baseline", "middle"); // vertical centering of text at y position in all browsers, except IE.
484
                }
485

486
                if (hasAxis) {
2,817✔
487
                        // Regions
488
                        config.regions.length && $$.initRegion();
2,055✔
489

490
                        // Add Axis here, when clipPath is 'false'
491
                        !config.clipPath && $$.axis.init();
2,055✔
492
                }
493

494
                // Define g for chart area
495
                main.append("g")
2,817✔
496
                        .classed($COMMON.chart, true)
497
                        .attr("clip-path", hasAxis ? state.clip.path : null);
2,817✔
498

499
                $$.callPluginHook("$init");
2,817✔
500

501
                $$.initChartElements();
2,817✔
502

503
                if (hasAxis) {
2,817✔
504
                        // Cover whole with rects for events
505
                        hasInteraction && $$.initEventRect?.();
2,055✔
506

507
                        // Grids
508
                        $$.initGrid();
2,055✔
509

510
                        // Add Axis here, when clipPath is 'true'
511
                        config.clipPath && $$.axis?.init();
2,055✔
512
                }
513

514
                // Set targets
515
                $$.updateTargets($$.data.targets);
2,817✔
516

517
                // Draw with targets
518
                $$.updateDimension();
2,817✔
519

520
                // oninit callback
521
                callFn(config.oninit, $$.api);
2,817✔
522

523
                // Set background
524
                $$.setBackground();
2,817✔
525

526
                $$.redraw({
2,817✔
527
                        withTransition: false,
528
                        withTransform: true,
529
                        withUpdateXDomain: true,
530
                        withUpdateOrgXDomain: true,
531
                        withTransitionForAxis: false,
532
                        initializing: true
533
                });
534

535
                // data.onmin/max callback
536
                if (config.data_onmin || config.data_onmax) {
2,817!
UNCOV
537
                        const minMax = $$.getMinMaxData();
×
538

UNCOV
539
                        callFn(config.data_onmin, $$.api, minMax.min);
×
UNCOV
540
                        callFn(config.data_onmax, $$.api, minMax.max);
×
541
                }
542

543
                config.tooltip_show && $$.initShowTooltip();
2,817✔
544
                state.rendered = true;
2,817✔
545
        }
546

547
        /**
548
         * Initialize chart elements
549
         * @private
550
         */
551
        initChartElements(): void {
552
                const $$ = <any>this;
2,817✔
553
                const {hasAxis, hasRadar, hasTreemap} = $$.state;
2,817✔
554
                const types: string[] = [];
2,817✔
555

556
                if (hasAxis) {
2,817✔
557
                        const shapes = ["bar", "bubble", "candlestick", "line"];
2,055✔
558

559
                        if ($$.config.bar_front) {
2,055!
UNCOV
560
                                shapes.push(shapes.shift() as string);
×
561
                        }
562

563
                        shapes.forEach(v => {
2,055✔
564
                                const name = capitalize(v);
8,220✔
565

566
                                if ((v === "line" && $$.hasTypeOf(name)) || $$.hasType(v)) {
8,220✔
567
                                        types.push(name);
2,058✔
568
                                }
569
                        });
570
                } else if (hasTreemap) {
762✔
571
                        types.push("Treemap");
51✔
572
                } else if ($$.hasType("funnel")) {
711✔
573
                        types.push("Funnel");
54✔
574
                } else {
575
                        const hasPolar = $$.hasType("polar");
657✔
576

577
                        if (!hasRadar) {
657✔
578
                                types.push("Arc", "Pie");
567✔
579
                        }
580

581
                        if ($$.hasType("gauge")) {
657✔
582
                                types.push("Gauge");
234✔
583
                        } else if (hasRadar) {
423✔
584
                                types.push("Radar");
90✔
585
                        } else if (hasPolar) {
333✔
586
                                types.push("Polar");
66✔
587
                        }
588
                }
589

590
                types.forEach(v => {
2,817✔
591
                        $$[`init${v}`]();
3,687✔
592
                });
593

594
                notEmpty($$.config.data_labels) && !$$.hasArcType(null, ["radar"]) && $$.initText();
2,817✔
595
        }
596

597
        /**
598
         * Set chart elements
599
         * @private
600
         */
601
        setChartElements(): void {
602
                const $$ = this;
3,051✔
603
                const {
604
                        $el: {
605
                                chart,
606
                                svg,
607
                                defs,
608
                                main,
609
                                tooltip,
610
                                legend,
611
                                title,
612
                                grid,
613
                                needle,
614
                                arcs: arc,
615
                                circle: circles,
616
                                bar: bars,
617
                                candlestick,
618
                                line: lines,
619
                                area: areas,
620
                                text: texts
621
                        }
622
                } = $$;
3,051✔
623

624
                // public
625
                $$.api.$ = {
3,051✔
626
                        chart,
627
                        svg,
628
                        defs,
629
                        main,
630
                        tooltip,
631
                        legend,
632
                        title,
633
                        grid,
634
                        arc,
635
                        circles,
636
                        bar: {bars},
637
                        candlestick,
638
                        line: {lines, areas},
639
                        needle,
640
                        text: {texts}
641
                };
642
        }
643

644
        /**
645
         * Set background element/image
646
         * @private
647
         */
648
        setBackground(): void {
649
                const $$ = this;
2,817✔
650
                const {config: {background: bg}, state, $el: {svg}} = $$;
2,817✔
651

652
                if (notEmpty(bg)) {
2,817✔
653
                        const element = svg.select("g")
15✔
654
                                .insert(bg.imgUrl ? "image" : "rect", ":first-child");
15✔
655

656
                        if (bg.imgUrl) {
15✔
657
                                element.attr("href", bg.imgUrl);
6✔
658
                        } else if (bg.color) {
9!
659
                                element
9✔
660
                                        .style("fill", bg.color)
661
                                        .attr("clip-path", state.clip.path);
662
                        }
663

664
                        element
15✔
665
                                .attr("class", bg.class || null)
18✔
666
                                .attr("width", "100%")
667
                                .attr("height", "100%");
668
                }
669
        }
670

671
        /**
672
         * Update targeted element with given data
673
         * @param {object} targets Data object formatted as 'target'
674
         * @private
675
         */
676
        updateTargets(targets): void {
677
                const $$ = <any>this;
3,027✔
678
                const {hasAxis, hasFunnel, hasRadar, hasTreemap} = $$.state;
3,027✔
679
                const helper = type =>
3,027✔
680
                        $$[`updateTargetsFor${type}`](
2,973✔
681
                                targets.filter($$[`is${type}Type`].bind($$))
682
                        );
683

684
                // Text
685
                $$.updateTargetsForText(targets);
3,027✔
686

687
                if (hasAxis) {
3,027✔
688
                        ["bar", "candlestick", "line"].forEach(v => {
2,238✔
689
                                const name = capitalize(v);
6,714✔
690

691
                                if ((v === "line" && $$.hasTypeOf(name)) || $$.hasType(v)) {
6,714✔
692
                                        helper(name);
2,184✔
693
                                }
694
                        });
695

696
                        // Sub Chart
697
                        $$.updateTargetsForSubchart &&
2,238✔
698
                                $$.updateTargetsForSubchart(targets);
699

700
                        // Arc, Polar, Radar
701
                } else if ($$.hasArcType(targets)) {
789✔
702
                        let type = "Arc";
672✔
703

704
                        if (hasRadar) {
672✔
705
                                type = "Radar";
93✔
706
                        } else if ($$.hasType("polar")) {
579✔
707
                                type = "Polar";
66✔
708
                        }
709

710
                        helper(type);
672✔
711
                } else if (hasFunnel) {
117✔
712
                        helper("Funnel");
60✔
713
                } else if (hasTreemap) {
57!
714
                        helper("Treemap");
57✔
715
                }
716

717
                // Point types
718
                const hasPointType = $$.hasType("bubble") || $$.hasType("scatter");
3,027✔
719

720
                if (hasPointType) {
3,027✔
721
                        $$.updateTargetForCircle?.();
141✔
722
                }
723

724
                // Fade-in each chart
725
                $$.filterTargetsToShowAtInit(hasPointType);
3,027✔
726
        }
727

728
        /**
729
         * Display targeted elements at initialization
730
         * @param {boolean} hasPointType whether has point type(bubble, scatter) or not
731
         * @private
732
         */
733
        filterTargetsToShowAtInit(hasPointType: boolean = false): void {
×
734
                const $$ = <any>this;
3,027✔
735
                const {$el: {svg}, $T} = $$;
3,027✔
736
                let selector = `.${$COMMON.target}`;
3,027✔
737

738
                if (hasPointType) {
3,027✔
739
                        selector += `, .${$CIRCLE.chartCircles} > .${$CIRCLE.circles}`;
141✔
740
                }
741

742
                $T(svg.selectAll(selector)
3,027✔
743
                        .filter(d => $$.isTargetToShow(d.id))).style("opacity", null);
7,377✔
744
        }
745

746
        getWithOption(options) {
747
                const withOptions = {
4,001✔
748
                        Dimension: true,
749
                        EventRect: true,
750
                        Legend: false,
751
                        Subchart: true,
752
                        Transform: false,
753
                        Transition: true,
754
                        TrimXDomain: true,
755
                        UpdateXAxis: "UpdateXDomain",
756
                        UpdateXDomain: false,
757
                        UpdateOrgXDomain: false,
758
                        TransitionForExit: "Transition",
759
                        TransitionForAxis: "Transition",
760
                        Y: true
761
                };
762

763
                Object.keys(withOptions).forEach(key => {
4,001✔
764
                        let defVal = withOptions[key];
52,013✔
765

766
                        if (isString(defVal)) {
52,013✔
767
                                defVal = withOptions[defVal];
12,003✔
768
                        }
769

770
                        withOptions[key] = getOption(options, `with${key}`, defVal);
52,013✔
771
                });
772

773
                return withOptions;
4,001✔
774
        }
775

776
        initialOpacity(d): null | "0" {
777
                const $$ = <any>this;
11,703✔
778
                const {withoutFadeIn} = $$.state;
11,703✔
779

780
                const r = $$.getBaseValue(d) !== null &&
11,703✔
781
                                withoutFadeIn[d.id] ?
782
                        null :
783
                        "0";
784

785
                return r;
11,703✔
786
        }
787

788
        bindResize(): void {
789
                const $$ = <any>this;
2,817✔
790
                const {$el, config, state} = $$;
2,817✔
791
                const resizeFunction = generateResize(config.resize_timer);
2,817✔
792
                const list: Function[] = [];
2,817✔
793

794
                list.push(() => callFn(config.onresize, $$.api));
2,817✔
795

796
                if (/^(true|parent)$/.test(config.resize_auto)) {
2,817✔
797
                        list.push(() => {
2,769✔
798
                                state.resizing = true;
71✔
799

800
                                // https://github.com/naver/billboard.js/issues/2650
801
                                if (config.legend_show) {
71!
802
                                        $$.updateSizes();
71✔
803
                                        $$.updateLegend();
71✔
804
                                }
805

806
                                $$.api.flush(false);
71✔
807
                        });
808
                }
809

810
                list.push(() => {
2,817✔
811
                        callFn(config.onresized, $$.api);
74✔
812
                        state.resizing = false;
74✔
813
                });
814

815
                // add resize functions
816
                list.forEach(v => resizeFunction.add(v));
8,403✔
817

818
                $$.resizeFunction = resizeFunction;
2,817✔
819

820
                // attach resize event
821
                if (config.resize_auto === "parent") {
2,817✔
822
                        ($$.resizeFunction.resizeObserver = new ResizeObserver($$.resizeFunction.bind($$)))
3✔
823
                                .observe($el.chart.node().parentNode);
824
                } else {
825
                        window.addEventListener("resize", $$.resizeFunction);
2,814✔
826
                }
827
        }
828

829
        /**
830
         * Call plugin hook
831
         * @param {string} phase The lifecycle phase
832
         * @param {Array} args Arguments
833
         * @private
834
         */
835
        callPluginHook(phase, ...args): void {
836
                this.config.plugins.forEach(v => {
12,827✔
837
                        if (phase === "$beforeInit") {
678✔
838
                                v.$$ = this;
171✔
839
                                this.api.plugins.push(v);
171✔
840
                        }
841

842
                        v[phase](...args);
678✔
843
                });
844
        }
845
}
846

847
extend(ChartInternal.prototype, [
228✔
848
        // common
849
        dataConvert,
850
        data,
851
        dataLoad,
852
        category,
853
        classModule,
854
        color,
855
        domain,
856
        interaction,
857
        format,
858
        legend,
859
        redraw,
860
        scale,
861
        shape,
862
        size,
863
        style,
864
        text,
865
        title,
866
        tooltip,
867
        transform,
868
        typeInternals
869
]);
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