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

naver / billboard.js / 14590453034

22 Apr 2025 08:38AM UTC coverage: 87.038% (-7.1%) from 94.132%
14590453034

push

github

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

Co-authored-by: netil <netil@netilui-Mac-Studio.local>

5621 of 6951 branches covered (80.87%)

Branch coverage included in aggregate %.

7446 of 8062 relevant lines covered (92.36%)

12074.19 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
        getPointer,
31
        getRectSegList,
32
        getUnique,
33
        isFunction,
34
        isNumber,
35
        isObjectType,
36
        isUndefined,
37
        isValue,
38
        notEmpty,
39
        parseDate
40
} from "../../module/util";
41
import type {IDataIndice, IDataRow, TIndices} from "../data/IData";
42

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

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

60
        return fn;
180✔
61
}
62

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

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

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

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

85
                        if (
16,248✔
86
                                $$.hasType(v) || $$.hasTypeOf(name) || (
45,134✔
87
                                        v === "line" &&
88
                                        ($$.hasType("bubble") || $$.hasType("scatter"))
89
                                )
90
                        ) {
91
                                const indices = $$.getShapeIndices($$[`is${name}Type`]);
3,514✔
92
                                const drawFn = $$[`generateDraw${name}`];
3,514✔
93

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

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

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

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

117
                return shape;
4,119✔
118
        },
119

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

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

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

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

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

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

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

178
                return indices;
3,694✔
179
        },
180

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

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

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

204
                        return ind;
×
205
                }
206

207
                return notEmpty(xs) ? indices[xs[id]] : indices;
19,569✔
208
        },
209

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

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

233
                // total shapes half width
234
                const halfWidth = isObjectType(offset) && (
11,034✔
235
                        offset._$total.length ? offset._$total.reduce(sum) / 2 : 0
108!
236
                );
237

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

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

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

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

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

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

274
                        return x;
9,798✔
275
                };
276
        },
277

278
        getShapeY(isSub?: boolean): Function {
279
                const $$ = this;
11,034✔
280
                const isStackNormalized = $$.isStackNormalized();
11,034✔
281

282
                return d => {
11,034✔
283
                        let {value} = d;
9,942✔
284

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

298
                        return $$.getYScaleById(d.id, isSub)(value);
9,942✔
299
                };
300
        },
301

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

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

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

331
                const shapeOffsetTargets = targets.map(target => {
11,034✔
332
                        let rowValues = target.values;
19,263✔
333
                        const values = {};
19,263✔
334

335
                        if ($$.isStepType(target)) {
19,263✔
336
                                rowValues = $$.convertValuesToStep(rowValues);
543✔
337
                        }
338

339
                        const rowValueMapByXValue = rowValues.reduce((out, d) => {
19,263✔
340
                                const key = Number(d.x);
103,044✔
341

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

345
                                return out;
103,044✔
346
                        }, {});
347

348
                        return {
19,263✔
349
                                id: target.id,
350
                                rowValues,
351
                                rowValueMapByXValue,
352
                                values
353
                        };
354
                });
355
                const indexMapByTargetId = targets.reduce((out, {id}, index) => {
11,034✔
356
                        out[id] = index;
19,263✔
357
                        return out;
19,263✔
358
                }, {});
359

360
                return {indexMapByTargetId, shapeOffsetTargets};
11,034✔
361
        },
362

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

370
                return (d, idx) => {
11,034✔
371
                        const {id, value, x} = d;
9,798✔
372
                        const ind = $$.getIndices(indices, d);
9,798✔
373
                        const scale = $$.getYScaleById(id, isSub);
9,798✔
374

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

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

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

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

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

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

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

420
                        return offset;
9,768✔
421
                };
422
        },
423

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

436
                if ($$.isGrouped(id)) {
26,130✔
437
                        points = getGroupedDataPointsFn.bind($$)(d);
180✔
438
                }
439

440
                return points ? points(d, i)[0][1] : $$.getYScaleById(id)($$.getBaseValue(d));
26,130✔
441
        },
442

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

455
                let tickInterval = axis.tickInterval(maxDataCount);
1,722✔
456

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

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

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

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

477
                        return max && w > max ? max : w;
1,722!
478
                };
479

480
                let result = getWidth();
1,722✔
481

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

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

493
                return result;
1,722✔
494
        },
495

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

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

525
                return shape;
1,143✔
526
        },
527

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

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

546
                return isWithin;
273✔
547
        },
548

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

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

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

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

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

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

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