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

naver / billboard.js / 16137267914

08 Jul 2025 07:51AM UTC coverage: 86.941% (-7.2%) from 94.118%
16137267914

push

github

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

update dependencies to the latest versions

5668 of 7016 branches covered (80.79%)

Branch coverage included in aggregate %.

7487 of 8115 relevant lines covered (92.26%)

11761.7 hits per line

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

84.77
/src/ChartInternal/shape/shape.ts
1
/**
2
 * Copyright (c) 2017 ~ present NAVER Corp.
3
 * billboard.js project is licensed under the MIT license
4
 */
5
import {select as d3Select} from "d3-selection";
6
import {
7
        curveBasis as d3CurveBasis,
8
        curveBasisClosed as d3CurveBasisClosed,
9
        curveBasisOpen as d3CurveBasisOpen,
10
        curveBundle as d3CurveBundle,
11
        curveCardinal as d3CurveCardinal,
12
        curveCardinalClosed as d3CurveCardinalClosed,
13
        curveCardinalOpen as d3CurveCardinalOpen,
14
        curveCatmullRom as d3CurveCatmullRom,
15
        curveCatmullRomClosed as d3CurveCatmullRomClosed,
16
        curveCatmullRomOpen as d3CurveCatmullRomOpen,
17
        curveLinear as d3CurveLinear,
18
        curveLinearClosed as d3CurveLinearClosed,
19
        curveMonotoneX as d3CurveMonotoneX,
20
        curveMonotoneY as d3CurveMonotoneY,
21
        curveNatural as d3CurveNatural,
22
        curveStep as d3CurveStep,
23
        curveStepAfter as d3CurveStepAfter,
24
        curveStepBefore as d3CurveStepBefore
25
} from "d3-shape";
26
import type {d3Selection} from "../../../types/types";
27
import CLASS from "../../config/classes";
28
import {
29
        capitalize,
30
        getBBox,
31
        getPointer,
32
        getRectSegList,
33
        getUnique,
34
        isFunction,
35
        isNumber,
36
        isObjectType,
37
        isUndefined,
38
        isValue,
39
        notEmpty,
40
        parseDate
41
} from "../../module/util";
42
import type {IDataIndice, IDataRow, TIndices} from "../data/IData";
43

44
/**
45
 * Get grouped data point function for y coordinate
46
 * - Note: Grouped(stacking) works only for line and bar types
47
 * @param {object} d data vlaue
48
 * @returns {Function|undefined}
49
 * @private
50
 */
51
function getGroupedDataPointsFn(d) {
52
        const $$ = this;
180✔
53
        let fn;
54

55
        if ($$.isLineType(d)) {
180!
56
                fn = $$.generateGetLinePoints($$.getShapeIndices($$.isLineType));
180✔
57
        } else if ($$.isBarType(d)) {
×
58
                fn = $$.generateGetBarPoints($$.getShapeIndices($$.isBarType));
×
59
        }
60

61
        return fn;
180✔
62
}
63

64
export interface IOffset {
65
        _$width: number;
66
        _$total: number[];
67
}
68

69
export default {
70
        /**
71
         * Get the shape draw function
72
         * @returns {object}
73
         * @private
74
         */
75
        getDrawShape() {
76
                type TShape = {area?: any, bar?: any, line?: any};
77

78
                const $$ = this;
4,044✔
79
                const isRotated = $$.config.axis_rotated;
4,044✔
80
                const {hasRadar, hasTreemap} = $$.state;
4,044✔
81
                const shape = {type: <TShape>{}, indices: <TShape>{}, pos: {}};
4,044✔
82

83
                !hasTreemap && ["bar", "candlestick", "line", "area"].forEach(v => {
4,044✔
84
                        const name = capitalize(/^(bubble|scatter)$/.test(v) ? "line" : v);
15,948!
85

86
                        if (
15,948✔
87
                                $$.hasType(v) || $$.hasTypeOf(name) || (
44,358✔
88
                                        v === "line" &&
89
                                        ($$.hasType("bubble") || $$.hasType("scatter"))
90
                                )
91
                        ) {
92
                                const indices = $$.getShapeIndices($$[`is${name}Type`]);
3,432✔
93
                                const drawFn = $$[`generateDraw${name}`];
3,432✔
94

95
                                shape.indices[v] = indices;
3,432✔
96
                                shape.type[v] = drawFn ? drawFn.bind($$)(indices, false) : undefined;
3,432!
97
                        }
98
                });
99

100
                if (!$$.hasArcType() || hasRadar || hasTreemap) {
4,044✔
101
                        let cx;
102
                        let cy;
103

104
                        // generate circle x/y functions depending on updated params
105
                        if (!hasTreemap) {
3,384✔
106
                                cx = hasRadar ? $$.radarCircleX : (isRotated ? $$.circleY : $$.circleX);
3,327✔
107
                                cy = hasRadar ? $$.radarCircleY : (isRotated ? $$.circleX : $$.circleY);
3,327✔
108
                        }
109

110
                        shape.pos = {
3,384✔
111
                                xForText: $$.generateXYForText(shape.indices, true),
112
                                yForText: $$.generateXYForText(shape.indices, false),
113
                                cx: (cx || function() {}).bind($$),
3,441✔
114
                                cy: (cy || function() {}).bind($$)
3,441✔
115
                        };
116
                }
117

118
                return shape;
4,044✔
119
        },
120

121
        /**
122
         * Get shape's indices according it's position within each axis tick.
123
         *
124
         * From the below example, indices will be:
125
         * ==> {data1: 0, data2: 0, data3: 1, data4: 1, __max__: 1}
126
         *
127
         *         data1 data3   data1 data3
128
         *         data2 data4   data2 data4
129
         *         -------------------------
130
         *                  0             1
131
         * @param {Function} typeFilter Chart type filter function
132
         * @returns {object} Indices object with its position
133
         */
134
        getShapeIndices(typeFilter): TIndices {
135
                const $$ = this;
3,612✔
136
                const {config} = $$;
3,612✔
137
                const xs = config.data_xs;
3,612✔
138
                const hasXs = notEmpty(xs);
3,612✔
139
                const indices: TIndices = {};
3,612✔
140
                let i: any = hasXs ? {} : 0;
3,612✔
141

142
                if (hasXs) {
3,612✔
143
                        getUnique(Object.keys(xs).map(v => xs[v]))
138✔
144
                                .forEach(v => {
145
                                        i[v] = 0;
135✔
146
                                        indices[v] = {};
135✔
147
                                });
148
                }
149

150
                $$.filterTargetsToShow($$.data.targets.filter(typeFilter, $$))
3,612✔
151
                        .forEach(d => {
152
                                const xKey = d.id in xs ? xs[d.id] : "";
6,591✔
153
                                const ind = xKey ? indices[xKey] : indices;
6,591✔
154

155
                                for (let j = 0, groups; (groups = config.data_groups[j]); j++) {
6,591✔
156
                                        if (groups.indexOf(d.id) < 0) {
1,872✔
157
                                                continue;
609✔
158
                                        }
159

160
                                        for (let k = 0, key; (key = groups[k]); k++) {
1,263✔
161
                                                if (key in ind) {
1,968✔
162
                                                        ind[d.id] = ind[key];
699✔
163
                                                        break;
699✔
164
                                                }
165

166
                                                // for same grouped data, add other data to same indices
167
                                                if (d.id !== key && xKey) {
1,269✔
168
                                                        ind[key] = ind[d.id] ?? i[xKey];
6✔
169
                                                }
170
                                        }
171
                                }
172

173
                                if (isUndefined(ind[d.id])) {
6,591✔
174
                                        ind[d.id] = xKey ? i[xKey]++ : i++;
5,892✔
175
                                        ind.__max__ = (xKey ? i[xKey] : i) - 1;
5,892✔
176
                                }
177
                        });
178

179
                return indices;
3,612✔
180
        },
181

182
        /**
183
         * Get indices value based on data ID value
184
         * @param {object} indices Indices object
185
         * @param {object} d Data row
186
         * @param {string} caller Caller function name (Used only for 'sparkline' plugin)
187
         * @returns {object} Indices object
188
         * @private
189
         */
190
        getIndices(indices: TIndices, d: IDataRow, caller?: string): IDataIndice { // eslint-disable-line
191
                const $$ = this;
19,713✔
192
                const {data_xs: xs, bar_indices_removeNull: removeNull} = $$.config;
19,713✔
193
                const {id, index} = d;
19,713✔
194

195
                if ($$.isBarType(id) && removeNull) {
19,713!
196
                        const ind = {} as IDataIndice;
×
197

198
                        // redefine bar indices order
199
                        $$.getAllValuesOnIndex(index, true)
×
200
                                .forEach((v, i) => {
201
                                        ind[v.id] = i;
×
202
                                        ind.__max__ = i;
×
203
                                });
204

205
                        return ind;
×
206
                }
207

208
                return notEmpty(xs) ? indices[xs[id]] : indices as IDataIndice;
19,713✔
209
        },
210

211
        /**
212
         * Get indices max number
213
         * @param {object} indices Indices object
214
         * @returns {number} Max number
215
         * @private
216
         */
217
        getIndicesMax(indices: TIndices | IDataIndice): number {
218
                return notEmpty(this.config.data_xs) ?
1,704✔
219
                        // if is multiple xs, return total sum of xs' __max__ value
220
                        Object.keys(indices)
221
                                .map(v => indices[v].__max__ || 0)
36✔
222
                                .reduce((acc, curr) => acc + curr) :
9✔
223
                        (indices as IDataIndice).__max__;
224
        },
225

226
        getShapeX(offset: IOffset, indices, isSub?: boolean): (d) => number {
227
                const $$ = this;
10,785✔
228
                const {config, scale} = $$;
10,785✔
229
                const currScale = isSub ? scale.subX : (scale.zoom || scale.x);
10,785✔
230
                const barOverlap = config.bar_overlap;
10,785✔
231
                const barPadding = config.bar_padding;
10,785✔
232
                const sum = (p, c) => p + c;
10,785✔
233

234
                // total shapes half width
235
                const halfWidth = isObjectType(offset) && (
10,785✔
236
                        offset._$total.length ? offset._$total.reduce(sum) / 2 : 0
117!
237
                );
238

239
                return d => {
10,785✔
240
                        const ind = $$.getIndices(indices, d, "getShapeX");
9,870✔
241
                        const index = d.id in ind ? ind[d.id] : 0;
9,870✔
242
                        const targetsNum = (ind.__max__ || 0) + 1;
9,870✔
243
                        let x = 0;
9,870✔
244

245
                        if (notEmpty(d.x)) {
9,870!
246
                                const xPos = currScale(d.x, true);
9,870✔
247

248
                                if (halfWidth) {
9,870!
249
                                        const offsetWidth = offset[d.id] || offset._$width;
×
250

251
                                        x = barOverlap ? xPos - offsetWidth / 2 : xPos - offsetWidth +
×
252
                                                offset._$total.slice(0, index + 1).reduce(sum) -
253
                                                halfWidth;
254
                                } else {
255
                                        x = xPos - (isNumber(offset) ? offset : offset._$width) *
9,870✔
256
                                                        (targetsNum / 2 - (
257
                                                                barOverlap ? 1 : index
9,870!
258
                                                        ));
259
                                }
260
                        }
261

262
                        // adjust x position for bar.padding option
263
                        if (offset && x && targetsNum > 1 && barPadding) {
9,870!
264
                                if (index) {
×
265
                                        x += barPadding * index;
×
266
                                }
267

268
                                if (targetsNum > 2) {
×
269
                                        x -= (targetsNum - 1) * barPadding / 2;
×
270
                                } else if (targetsNum === 2) {
×
271
                                        x -= barPadding / 2;
×
272
                                }
273
                        }
274

275
                        return x;
9,870✔
276
                };
277
        },
278

279
        getShapeY(isSub?: boolean): Function {
280
                const $$ = this;
10,785✔
281
                const isStackNormalized = $$.isStackNormalized();
10,785✔
282

283
                return d => {
10,785✔
284
                        let {value} = d;
10,014✔
285

286
                        if (isNumber(d)) {
10,014✔
287
                                value = d;
192✔
288
                        } else if ($$.isAreaRangeType(d)) {
9,822!
289
                                value = $$.getBaseValue(d, "mid");
×
290
                        } else if (isStackNormalized) {
9,822!
291
                                value = $$.getRatio("index", d, true);
×
292
                        } else if ($$.isBubbleZType(d)) {
9,822!
293
                                value = $$.getBubbleZData(d.value, "y");
×
294
                        } else if ($$.isBarRangeType(d)) {
9,822✔
295
                                // TODO use range.getEnd() like method
296
                                value = value[1];
30✔
297
                        }
298

299
                        return $$.getYScaleById(d.id, isSub)(value);
10,014✔
300
                };
301
        },
302

303
        /**
304
         * Get shape based y Axis min value
305
         * @param {string} id Data id
306
         * @returns {number}
307
         * @private
308
         */
309
        getShapeYMin(id: string): number {
310
                const $$ = this;
21,510✔
311
                const axisId = $$.axis.getId(id);
21,510✔
312
                const scale = $$.scale[axisId];
21,510✔
313
                const [yMin] = scale.domain();
21,510✔
314
                const inverted = $$.config[`axis_${axisId}_inverted`];
21,510✔
315

316
                return !$$.isGrouped(id) && !inverted && yMin > 0 ? yMin : 0;
21,510✔
317
        },
318

319
        /**
320
         * Get Shape's offset data
321
         * @param {Function} typeFilter Type filter function
322
         * @returns {object}
323
         * @private
324
         */
325
        getShapeOffsetData(typeFilter) {
326
                const $$ = this;
10,785✔
327
                const targets = $$.orderTargets(
10,785✔
328
                        $$.filterTargetsToShow($$.data.targets.filter(typeFilter, $$))
329
                );
330
                const isStackNormalized = $$.isStackNormalized();
10,785✔
331

332
                const shapeOffsetTargets = targets.map(target => {
10,785✔
333
                        let rowValues = target.values;
18,951✔
334
                        const values = {};
18,951✔
335

336
                        if ($$.isStepType(target)) {
18,951✔
337
                                rowValues = $$.convertValuesToStep(rowValues);
543✔
338
                        }
339

340
                        const rowValueMapByXValue = rowValues.reduce((out, d) => {
18,951✔
341
                                const key = Number(d.x);
102,090✔
342

343
                                out[key] = d;
102,090✔
344
                                values[key] = isStackNormalized ? $$.getRatio("index", d, true) : d.value;
102,090!
345

346
                                return out;
102,090✔
347
                        }, {});
348

349
                        return {
18,951✔
350
                                id: target.id,
351
                                rowValues,
352
                                rowValueMapByXValue,
353
                                values
354
                        };
355
                });
356
                const indexMapByTargetId = targets.reduce((out, {id}, index) => {
10,785✔
357
                        out[id] = index;
18,951✔
358
                        return out;
18,951✔
359
                }, {});
360

361
                return {indexMapByTargetId, shapeOffsetTargets};
10,785✔
362
        },
363

364
        getShapeOffset(typeFilter, indices, isSub?: boolean): Function {
365
                const $$ = this;
10,785✔
366
                const {shapeOffsetTargets, indexMapByTargetId} = $$.getShapeOffsetData(
10,785✔
367
                        typeFilter
368
                );
369
                const groupsZeroAs = $$.config.data_groupsZeroAs;
10,785✔
370

371
                return (d, idx) => {
10,785✔
372
                        const {id, value, x} = d;
9,870✔
373
                        const ind = $$.getIndices(indices, d);
9,870✔
374
                        const scale = $$.getYScaleById(id, isSub);
9,870✔
375

376
                        if ($$.isBarRangeType(d)) {
9,870✔
377
                                // TODO use range.getStart()
378
                                return scale(value[0]);
30✔
379
                        }
380

381
                        const dataXAsNumber = Number(x);
9,840✔
382
                        const y0 = scale(groupsZeroAs === "zero" ? 0 : $$.getShapeYMin(id));
9,840!
383
                        let offset = y0;
9,840✔
384

385
                        shapeOffsetTargets
9,840✔
386
                                .filter(t => t.id !== id && ind[t.id] === ind[id])
22,815✔
387
                                .forEach(t => {
388
                                        const {
389
                                                id: tid,
390
                                                rowValueMapByXValue,
391
                                                rowValues,
392
                                                values: tvalues
393
                                        } = t;
7,596✔
394

395
                                        // for same stacked group (ind[tid] === ind[id])
396
                                        if (indexMapByTargetId[tid] < indexMapByTargetId[id]) {
7,596✔
397
                                                const rValue = tvalues[dataXAsNumber];
3,798✔
398
                                                let row = rowValues[idx];
3,798✔
399

400
                                                // check if the x values line up
401
                                                if (!row || Number(row.x) !== dataXAsNumber) {
3,798✔
402
                                                        row = rowValueMapByXValue[dataXAsNumber];
102✔
403
                                                }
404

405
                                                if (row?.value * value >= 0 && isNumber(rValue)) {
3,798✔
406
                                                        const addOffset = value === 0 ?
3,687!
407
                                                                (
408
                                                                        (groupsZeroAs === "positive" &&
×
409
                                                                                rValue > 0) ||
410
                                                                        (groupsZeroAs === "negative" && rValue < 0)
411
                                                                ) :
412
                                                                true;
413

414
                                                        if (addOffset) {
3,687!
415
                                                                offset += scale(rValue) - y0;
3,687✔
416
                                                        }
417
                                                }
418
                                        }
419
                                });
420

421
                        return offset;
9,840✔
422
                };
423
        },
424

425
        /**
426
         * Get data's y coordinate
427
         * @param {object} d Target data
428
         * @param {number} i Index number
429
         * @returns {number} y coordinate
430
         * @private
431
         */
432
        circleY(d: IDataRow, i: number): number {
433
                const $$ = this;
25,890✔
434
                const id = d.id;
25,890✔
435
                let points;
436

437
                if ($$.isGrouped(id)) {
25,890✔
438
                        points = getGroupedDataPointsFn.bind($$)(d);
180✔
439
                }
440

441
                return points ? points(d, i)[0][1] : $$.getYScaleById(id)($$.getBaseValue(d));
25,890✔
442
        },
443

444
        getBarW(type, axis, targetsNum: number): number | IOffset {
445
                const $$ = this;
1,704✔
446
                const {config, org, scale, state} = $$;
1,704✔
447
                const maxDataCount = $$.getMaxDataCount();
1,704✔
448
                const isGrouped = type === "bar" && config.data_groups?.length;
1,704✔
449
                const configName = `${type}_width`;
1,704✔
450
                const {k} = $$.getZoomTransform?.() ?? {k: 1};
1,704✔
451
                const xMinMax = <[number, number]>[
1,704✔
452
                        config.axis_x_min ?? org.xDomain[0],
3,345✔
453
                        config.axis_x_max ?? org.xDomain[1]
3,345✔
454
                ].map($$.axis.isTimeSeries() ? parseDate.bind($$) : Number);
1,704✔
455

456
                let tickInterval = axis.tickInterval(maxDataCount);
1,704✔
457

458
                if (scale.zoom && !$$.axis.isCategorized() && k > 1) {
1,704✔
459
                        const isSameMinMax = xMinMax.every((v, i) => v === org.xDomain[i]);
108✔
460

461
                        tickInterval = org.xDomain.map((v, i) => {
63✔
462
                                const value = isSameMinMax ? v : v - Math.abs(xMinMax[i]);
126✔
463

464
                                return scale.zoom(value);
126✔
465
                        }).reduce((a, c) => Math.abs(a) + c) / maxDataCount;
63✔
466
                }
467

468
                const getWidth = (id?: string) => {
1,704✔
469
                        const width = id ? config[configName][id] : config[configName];
1,704!
470
                        const ratio = id ? width.ratio : config[`${configName}_ratio`];
1,704!
471
                        const max = id ? width.max : config[`${configName}_max`];
1,704!
472
                        const w = isNumber(width) ? width : (
1,704!
473
                                isFunction(width) ?
1,704!
474
                                        width.call($$, state.width, targetsNum, maxDataCount) :
475
                                        (targetsNum ? (tickInterval * ratio) / targetsNum : 0)
1,704✔
476
                        );
477

478
                        return max && w > max ? max : w;
1,704!
479
                };
480

481
                let result = getWidth();
1,704✔
482

483
                if (!isGrouped && isObjectType(config[configName])) {
1,704✔
484
                        result = {_$width: result, _$total: []};
117✔
485

486
                        $$.filterTargetsToShow($$.data.targets).forEach(v => {
117✔
487
                                if (config[configName][v.id]) {
126!
488
                                        result[v.id] = getWidth(v.id);
×
489
                                        result._$total.push(result[v.id] || result._$width);
×
490
                                }
491
                        });
492
                }
493

494
                return result;
1,704✔
495
        },
496

497
        /**
498
         * Get shape element
499
         * @param {string} shapeName Shape string
500
         * @param {number} i Index number
501
         * @param {string} id Data series id
502
         * @returns {d3Selection}
503
         * @private
504
         */
505
        getShapeByIndex(shapeName: string, i: number, id?: string): d3Selection {
506
                const $$ = this;
1,161✔
507
                const {$el} = $$;
1,161✔
508
                const suffix = isValue(i) ? `-${i}` : ``;
1,161✔
509
                let shape = $el[shapeName];
1,161✔
510

511
                // filter from shape reference if has
512
                if (shape && !shape.empty()) {
1,161✔
513
                        shape = shape
1,113✔
514
                                .filter(d => (id ? d.id === id : true))
11,085✔
515
                                .filter(d => (isValue(i) ? d.index === i : true));
10,518✔
516
                } else {
517
                        shape = (id ?
48!
518
                                $el.main
519
                                        .selectAll(
520
                                                `.${CLASS[`${shapeName}s`]}${$$.getTargetSelectorSuffix(id)}`
521
                                        ) :
522
                                $el.main)
523
                                .selectAll(`.${CLASS[shapeName]}${suffix}`);
524
                }
525

526
                return shape;
1,161✔
527
        },
528

529
        isWithinShape(that, d): boolean {
530
                const $$ = this;
273✔
531
                const shape = d3Select(that);
273✔
532
                let isWithin;
533

534
                if (!$$.isTargetToShow(d.id)) {
273!
535
                        isWithin = false;
×
536
                } else if ($$.hasValidPointType?.(that.nodeName)) {
273✔
537
                        isWithin = $$.isStepType(d) ?
192!
538
                                $$.isWithinStep(that, $$.getYScaleById(d.id)($$.getBaseValue(d))) :
539
                                $$.isWithinCircle(
540
                                        that,
541
                                        $$.isBubbleType(d) ? $$.pointSelectR(d) * 1.5 : 0
192!
542
                                );
543
                } else if (that.nodeName === "path") {
81!
544
                        isWithin = shape.classed(CLASS.bar) ? $$.isWithinBar(that) : true;
81!
545
                }
546

547
                return isWithin;
273✔
548
        },
549

550
        getInterpolate(d) {
551
                const $$ = this;
5,364✔
552
                const interpolation = $$.getInterpolateType(d);
5,364✔
553

554
                return {
5,364✔
555
                        basis: d3CurveBasis,
556
                        "basis-closed": d3CurveBasisClosed,
557
                        "basis-open": d3CurveBasisOpen,
558
                        bundle: d3CurveBundle,
559
                        cardinal: d3CurveCardinal,
560
                        "cardinal-closed": d3CurveCardinalClosed,
561
                        "cardinal-open": d3CurveCardinalOpen,
562
                        "catmull-rom": d3CurveCatmullRom,
563
                        "catmull-rom-closed": d3CurveCatmullRomClosed,
564
                        "catmull-rom-open": d3CurveCatmullRomOpen,
565
                        "monotone-x": d3CurveMonotoneX,
566
                        "monotone-y": d3CurveMonotoneY,
567
                        natural: d3CurveNatural,
568
                        "linear-closed": d3CurveLinearClosed,
569
                        linear: d3CurveLinear,
570
                        step: d3CurveStep,
571
                        "step-after": d3CurveStepAfter,
572
                        "step-before": d3CurveStepBefore
573
                }[interpolation];
574
        },
575

576
        getInterpolateType(d) {
577
                const $$ = this;
5,364✔
578
                const {config} = $$;
5,364✔
579
                const type = config.spline_interpolation_type;
5,364✔
580
                const interpolation = $$.isInterpolationType(type) ? type : "cardinal";
5,364✔
581

582
                return $$.isSplineType(d) ? interpolation : (
5,364✔
583
                        $$.isStepType(d) ? config.line_step_type : "linear"
4,908✔
584
                );
585
        },
586

587
        isWithinBar(that): boolean {
588
                const mouse = getPointer(this.state.event, that);
87✔
589
                const list = getRectSegList(that);
87✔
590
                const [seg0, seg1] = list;
87✔
591
                const x = Math.min(seg0.x, seg1.x);
87✔
592
                const y = Math.min(seg0.y, seg1.y);
87✔
593
                const offset = this.config.bar_sensitivity;
87✔
594
                const {width, height} = getBBox(that, true);
87✔
595
                const sx = x - offset;
87✔
596
                const ex = x + width + offset;
87✔
597
                const sy = y + height + offset;
87✔
598
                const ey = y - offset;
87✔
599

600
                const isWithin = sx < mouse[0] &&
87✔
601
                        mouse[0] < ex &&
602
                        ey < mouse[1] &&
603
                        mouse[1] < sy;
604

605
                return isWithin;
87✔
606
        }
607
};
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