pptx-react-viewer 1.18.0 → 1.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/dist/{Model3DScene-GVWQ6SOM.js → Model3DScene-CQP7JLKK.js} +3 -3
- package/dist/{Model3DScene-GVWQ6SOM.js.br → Model3DScene-CQP7JLKK.js.br} +0 -0
- package/dist/Model3DScene-CQP7JLKK.js.gz +0 -0
- package/dist/{Model3DScene-JGVLIDLD.mjs → Model3DScene-J2KBVMEZ.mjs} +2 -2
- package/dist/Model3DScene-J2KBVMEZ.mjs.br +0 -0
- package/dist/Model3DScene-J2KBVMEZ.mjs.gz +0 -0
- package/dist/PowerPointViewer-C4d7SKZf.d.ts.map +1 -1
- package/dist/{SurfaceChart3DScene-DBWSDSKI.mjs → SurfaceChart3DScene-52VUVZSC.mjs} +2 -2
- package/dist/SurfaceChart3DScene-52VUVZSC.mjs.br +0 -0
- package/dist/SurfaceChart3DScene-52VUVZSC.mjs.gz +0 -0
- package/dist/{SurfaceChart3DScene-ZOTOHOIE.js → SurfaceChart3DScene-U6LVKSKR.js} +3 -3
- package/dist/SurfaceChart3DScene-U6LVKSKR.js.br +0 -0
- package/dist/SurfaceChart3DScene-U6LVKSKR.js.gz +0 -0
- package/dist/{chunk-SBNTEQUA.mjs → chunk-3ZGCM6RC.mjs} +20269 -12232
- package/dist/chunk-3ZGCM6RC.mjs.br +0 -0
- package/dist/chunk-3ZGCM6RC.mjs.gz +0 -0
- package/dist/{chunk-EXDUEXTO.js → chunk-AD3EAEY6.js} +20282 -12231
- package/dist/chunk-AD3EAEY6.js.br +0 -0
- package/dist/chunk-AD3EAEY6.js.gz +0 -0
- package/dist/{chunk-73ST7AC6.js → chunk-DBZLZMED.js} +6795 -6485
- package/dist/chunk-DBZLZMED.js.br +0 -0
- package/dist/chunk-DBZLZMED.js.gz +0 -0
- package/dist/{chunk-HBXPK572.js → chunk-P3T4E6JQ.js} +2969 -743
- package/dist/chunk-P3T4E6JQ.js.br +0 -0
- package/dist/chunk-P3T4E6JQ.js.gz +0 -0
- package/dist/{chunk-HHTZQHKZ.mjs → chunk-R7FTNSSP.mjs} +2922 -719
- package/dist/chunk-R7FTNSSP.mjs.br +0 -0
- package/dist/chunk-R7FTNSSP.mjs.gz +0 -0
- package/dist/{chunk-KMLIWCNL.mjs → chunk-SLJXGFZS.mjs} +5783 -5473
- package/dist/chunk-SLJXGFZS.mjs.br +0 -0
- package/dist/chunk-SLJXGFZS.mjs.gz +0 -0
- package/dist/{chunk-Z4MDCPAY.mjs → chunk-XGTMHQR7.mjs} +26 -731
- package/dist/chunk-XGTMHQR7.mjs.br +0 -0
- package/dist/chunk-XGTMHQR7.mjs.gz +0 -0
- package/dist/{chunk-JDRC3PSY.js → chunk-YF6JD4DO.js} +470 -1186
- package/dist/chunk-YF6JD4DO.js.br +0 -0
- package/dist/chunk-YF6JD4DO.js.gz +0 -0
- package/dist/dist-3YRZ26TL.mjs +2 -0
- package/dist/dist-3YRZ26TL.mjs.br +0 -0
- package/dist/dist-3YRZ26TL.mjs.gz +0 -0
- package/dist/{dist-KRRXYUGI.js → dist-M2P6HBG4.js} +602 -546
- package/dist/dist-M2P6HBG4.js.br +0 -0
- package/dist/dist-M2P6HBG4.js.gz +0 -0
- package/dist/hooks-unstable.d.ts +523 -49
- package/dist/hooks-unstable.d.ts.map +1 -1
- package/dist/hooks-unstable.js +77 -77
- package/dist/hooks-unstable.js.br +0 -0
- package/dist/hooks-unstable.js.gz +0 -0
- package/dist/hooks-unstable.mjs +3 -3
- package/dist/hooks-unstable.mjs.br +0 -0
- package/dist/hooks-unstable.mjs.gz +0 -0
- package/dist/index.d.ts +417 -21
- package/dist/index.js +18 -18
- package/dist/index.js.br +3 -3
- package/dist/index.js.gz +0 -0
- package/dist/index.mjs +4 -4
- package/dist/index.mjs.br +0 -0
- package/dist/index.mjs.gz +0 -0
- package/dist/pptx-viewer.css +1 -1
- package/dist/pptx-viewer.css.br +0 -0
- package/dist/pptx-viewer.css.gz +0 -0
- package/dist/viewer/index.d.ts +509 -27
- package/dist/viewer/index.js +20 -20
- package/dist/viewer/index.js.br +0 -0
- package/dist/viewer/index.js.gz +0 -0
- package/dist/viewer/index.mjs +4 -4
- package/dist/viewer/index.mjs.br +0 -0
- package/dist/viewer/index.mjs.gz +0 -0
- package/package.json +1 -1
- package/dist/Model3DScene-GVWQ6SOM.js.gz +0 -0
- package/dist/Model3DScene-JGVLIDLD.mjs.br +0 -0
- package/dist/Model3DScene-JGVLIDLD.mjs.gz +0 -0
- package/dist/SurfaceChart3DScene-DBWSDSKI.mjs.br +0 -0
- package/dist/SurfaceChart3DScene-DBWSDSKI.mjs.gz +0 -0
- package/dist/SurfaceChart3DScene-ZOTOHOIE.js.br +0 -0
- package/dist/SurfaceChart3DScene-ZOTOHOIE.js.gz +0 -0
- package/dist/chunk-73ST7AC6.js.br +0 -0
- package/dist/chunk-73ST7AC6.js.gz +0 -0
- package/dist/chunk-EXDUEXTO.js.br +0 -0
- package/dist/chunk-EXDUEXTO.js.gz +0 -0
- package/dist/chunk-HBXPK572.js.br +0 -0
- package/dist/chunk-HBXPK572.js.gz +0 -0
- package/dist/chunk-HHTZQHKZ.mjs.br +0 -0
- package/dist/chunk-HHTZQHKZ.mjs.gz +0 -0
- package/dist/chunk-JDRC3PSY.js.br +0 -0
- package/dist/chunk-JDRC3PSY.js.gz +0 -0
- package/dist/chunk-KMLIWCNL.mjs.br +0 -0
- package/dist/chunk-KMLIWCNL.mjs.gz +0 -0
- package/dist/chunk-SBNTEQUA.mjs.br +0 -0
- package/dist/chunk-SBNTEQUA.mjs.gz +0 -0
- package/dist/chunk-Z4MDCPAY.mjs.br +0 -0
- package/dist/chunk-Z4MDCPAY.mjs.gz +0 -0
- package/dist/dist-KRRXYUGI.js.br +0 -0
- package/dist/dist-KRRXYUGI.js.gz +0 -0
- package/dist/dist-N6PAPXK6.mjs +0 -2
- package/dist/dist-N6PAPXK6.mjs.br +0 -0
- package/dist/dist-N6PAPXK6.mjs.gz +0 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { MIN_ELEMENT_SIZE, guideEmuToPx, THEME_COLOR_SCHEME_KEYS, hasShapeProperties, hasTextProperties, createChartElement, getShapeClipPath, getAdjustmentAwareShapeClipPath, getShapeClipPathFromPreset, getCloudPathForRendering, hslToRgb, PRESET_COLOR_MAP, isImageLikeElement, applyDrawingColorTransforms } from './chunk-
|
|
1
|
+
import { MIN_ELEMENT_SIZE, guideEmuToPx, THEME_COLOR_SCHEME_KEYS, hasShapeProperties, hasTextProperties, createChartElement, getShapeClipPath, getAdjustmentAwareShapeClipPath, getShapeClipPathFromPreset, getCloudPathForRendering, hslToRgb, PRESET_COLOR_MAP, isImageLikeElement, applyDrawingColorTransforms } from './chunk-3ZGCM6RC.mjs';
|
|
2
2
|
import DOMPurify from 'dompurify';
|
|
3
3
|
|
|
4
4
|
var defaultThemeColors = {
|
|
@@ -196,6 +196,18 @@ function collectImagePaths(slides) {
|
|
|
196
196
|
});
|
|
197
197
|
}
|
|
198
198
|
}
|
|
199
|
+
if (el.type === "model3d") {
|
|
200
|
+
const model = el;
|
|
201
|
+
if (model.modelPath && !model.modelData && !isExternalUrl(model.modelPath)) {
|
|
202
|
+
paths.add(model.modelPath);
|
|
203
|
+
refs.push({ element: el, field: "modelData", path: model.modelPath });
|
|
204
|
+
}
|
|
205
|
+
const posterNeedsResolution = model.posterImage ? !isExternalUrl(model.posterImage) : !model.imageData;
|
|
206
|
+
if (model.imagePath && posterNeedsResolution && !isExternalUrl(model.imagePath)) {
|
|
207
|
+
paths.add(model.imagePath);
|
|
208
|
+
refs.push({ element: el, field: "posterImage", path: model.imagePath });
|
|
209
|
+
}
|
|
210
|
+
}
|
|
199
211
|
if (el.type === "group" && el.children?.length) {
|
|
200
212
|
walkElements(el.children);
|
|
201
213
|
}
|
|
@@ -447,7 +459,7 @@ function getPatternSvg(preset, fg, bg) {
|
|
|
447
459
|
const svg = (w, h, inner) => `<svg xmlns="${xmlns}" width="${w}" height="${h}">${inner}</svg>`;
|
|
448
460
|
const rect = (x, y, w, h, fill) => `<rect x="${x}" y="${y}" width="${w}" height="${h}" fill="${fill}"/>`;
|
|
449
461
|
const bgRect = (w, h) => rect(0, 0, w, h, bg);
|
|
450
|
-
const
|
|
462
|
+
const line2 = (x1, y1, x2, y2, stroke, sw, extra = "") => `<line x1="${x1}" y1="${y1}" x2="${x2}" y2="${y2}" stroke="${stroke}" stroke-width="${sw}"${extra}/>`;
|
|
451
463
|
const circle = (cx, cy, r, fill) => `<circle cx="${cx}" cy="${cy}" r="${r}" fill="${fill}"/>`;
|
|
452
464
|
switch (preset) {
|
|
453
465
|
// ── Percentage fills ──────────────────────────────────────────────
|
|
@@ -526,61 +538,61 @@ function getPatternSvg(preset, fg, bg) {
|
|
|
526
538
|
return svg(
|
|
527
539
|
s,
|
|
528
540
|
s,
|
|
529
|
-
bgRect(s, s) +
|
|
541
|
+
bgRect(s, s) + line2(0, 0, s, s, fg, 2) + line2(-s, 0, 0, s, fg, 2) + line2(s, 0, s * 2, s, fg, 2)
|
|
530
542
|
);
|
|
531
543
|
case "ltDnDiag":
|
|
532
544
|
return svg(
|
|
533
545
|
s,
|
|
534
546
|
s,
|
|
535
|
-
bgRect(s, s) +
|
|
547
|
+
bgRect(s, s) + line2(0, 0, s, s, fg, 1) + line2(-s, 0, 0, s, fg, 1) + line2(s, 0, s * 2, s, fg, 1)
|
|
536
548
|
);
|
|
537
549
|
case "dkDnDiag":
|
|
538
550
|
return svg(
|
|
539
551
|
s,
|
|
540
552
|
s,
|
|
541
|
-
bgRect(s, s) +
|
|
553
|
+
bgRect(s, s) + line2(0, 0, s, s, fg, 3) + line2(-s, 0, 0, s, fg, 3) + line2(s, 0, s * 2, s, fg, 3)
|
|
542
554
|
);
|
|
543
555
|
case "wdDnDiag":
|
|
544
556
|
return svg(
|
|
545
557
|
12,
|
|
546
558
|
12,
|
|
547
|
-
bgRect(12, 12) +
|
|
559
|
+
bgRect(12, 12) + line2(0, 0, 12, 12, fg, 4) + line2(-12, 0, 0, 12, fg, 4) + line2(12, 0, 24, 12, fg, 4)
|
|
548
560
|
);
|
|
549
561
|
case "dashDnDiag":
|
|
550
|
-
return svg(s, s, bgRect(s, s) +
|
|
562
|
+
return svg(s, s, bgRect(s, s) + line2(0, 0, s, s, fg, 1, ' stroke-dasharray="4,4"'));
|
|
551
563
|
// ── Diagonal lines (up) ──────────────────────────────────────────
|
|
552
564
|
case "upDiag":
|
|
553
565
|
return svg(
|
|
554
566
|
s,
|
|
555
567
|
s,
|
|
556
|
-
bgRect(s, s) +
|
|
568
|
+
bgRect(s, s) + line2(0, s, s, 0, fg, 2) + line2(-s, s, 0, 0, fg, 2) + line2(s, s, s * 2, 0, fg, 2)
|
|
557
569
|
);
|
|
558
570
|
case "ltUpDiag":
|
|
559
571
|
return svg(
|
|
560
572
|
s,
|
|
561
573
|
s,
|
|
562
|
-
bgRect(s, s) +
|
|
574
|
+
bgRect(s, s) + line2(0, s, s, 0, fg, 1) + line2(-s, s, 0, 0, fg, 1) + line2(s, s, s * 2, 0, fg, 1)
|
|
563
575
|
);
|
|
564
576
|
case "dkUpDiag":
|
|
565
577
|
return svg(
|
|
566
578
|
s,
|
|
567
579
|
s,
|
|
568
|
-
bgRect(s, s) +
|
|
580
|
+
bgRect(s, s) + line2(0, s, s, 0, fg, 3) + line2(-s, s, 0, 0, fg, 3) + line2(s, s, s * 2, 0, fg, 3)
|
|
569
581
|
);
|
|
570
582
|
case "wdUpDiag":
|
|
571
583
|
return svg(
|
|
572
584
|
12,
|
|
573
585
|
12,
|
|
574
|
-
bgRect(12, 12) +
|
|
586
|
+
bgRect(12, 12) + line2(0, 12, 12, 0, fg, 4) + line2(-12, 12, 0, 0, fg, 4) + line2(12, 12, 24, 0, fg, 4)
|
|
575
587
|
);
|
|
576
588
|
case "dashUpDiag":
|
|
577
|
-
return svg(s, s, bgRect(s, s) +
|
|
589
|
+
return svg(s, s, bgRect(s, s) + line2(0, s, s, 0, fg, 1, ' stroke-dasharray="4,4"'));
|
|
578
590
|
// ── Diagonal cross ───────────────────────────────────────────────
|
|
579
591
|
case "diagCross":
|
|
580
592
|
return svg(
|
|
581
593
|
s,
|
|
582
594
|
s,
|
|
583
|
-
bgRect(s, s) +
|
|
595
|
+
bgRect(s, s) + line2(0, 0, s, s, fg, 1) + line2(-s, 0, 0, s, fg, 1) + line2(s, 0, s * 2, s, fg, 1) + line2(0, s, s, 0, fg, 1) + line2(-s, s, 0, 0, fg, 1) + line2(s, s, s * 2, 0, fg, 1)
|
|
584
596
|
);
|
|
585
597
|
// ── Checkerboard ─────────────────────────────────────────────────
|
|
586
598
|
case "smCheck":
|
|
@@ -622,7 +634,7 @@ function getPatternSvg(preset, fg, bg) {
|
|
|
622
634
|
return svg(
|
|
623
635
|
s,
|
|
624
636
|
s,
|
|
625
|
-
bgRect(s, s) +
|
|
637
|
+
bgRect(s, s) + line2(0, 4, 4, 0, fg, 1) + line2(4, s, s, 4, fg, 1) + line2(0, s, s, 0, fg, 1)
|
|
626
638
|
);
|
|
627
639
|
// ── Diamond ──────────────────────────────────────────────────────
|
|
628
640
|
case "solidDmnd":
|
|
@@ -631,7 +643,7 @@ function getPatternSvg(preset, fg, bg) {
|
|
|
631
643
|
return svg(
|
|
632
644
|
s,
|
|
633
645
|
s,
|
|
634
|
-
bgRect(s, s) +
|
|
646
|
+
bgRect(s, s) + line2(4, 0, s, 4, fg, 1) + line2(s, 4, 4, s, fg, 1) + line2(4, s, 0, 4, fg, 1) + line2(0, 4, 4, 0, fg, 1)
|
|
635
647
|
);
|
|
636
648
|
case "dotDmnd":
|
|
637
649
|
return svg(
|
|
@@ -656,21 +668,21 @@ function getPatternSvg(preset, fg, bg) {
|
|
|
656
668
|
return svg(
|
|
657
669
|
s,
|
|
658
670
|
s,
|
|
659
|
-
bgRect(s, s) +
|
|
671
|
+
bgRect(s, s) + line2(0, 0, 4, 4, fg, 1.5) + line2(4, 4, 0, s, fg, 1.5) + line2(s, 0, 4, 4, fg, 1.5) + line2(4, 4, s, s, fg, 1.5) + line2(4, 0, s, 4, fg, 1.5) + line2(0, 4, 4, s, fg, 1.5)
|
|
660
672
|
);
|
|
661
673
|
// ── Divot ────────────────────────────────────────────────────────
|
|
662
674
|
case "divot":
|
|
663
675
|
return svg(
|
|
664
676
|
s,
|
|
665
677
|
s,
|
|
666
|
-
bgRect(s, s) +
|
|
678
|
+
bgRect(s, s) + line2(2, 1, 2, 3, fg, 1) + line2(1, 2, 3, 2, fg, 1) + line2(6, 5, 6, 7, fg, 1) + line2(5, 6, 7, 6, fg, 1)
|
|
667
679
|
);
|
|
668
680
|
// ── Shingle ──────────────────────────────────────────────────────
|
|
669
681
|
case "shingle":
|
|
670
682
|
return svg(
|
|
671
683
|
s,
|
|
672
684
|
s,
|
|
673
|
-
bgRect(s, s) +
|
|
685
|
+
bgRect(s, s) + line2(0, 0, s, s, fg, 1) + line2(0, s, 4, 4, fg, 1) + rect(0, 7, s, 1, fg)
|
|
674
686
|
);
|
|
675
687
|
// ── Wave ─────────────────────────────────────────────────────────
|
|
676
688
|
case "wave":
|
|
@@ -1357,11 +1369,33 @@ function buildReflectionCssValue(distance, startOpacity, endOpacity, fadeLength,
|
|
|
1357
1369
|
function getDuotoneFilterId(elementId) {
|
|
1358
1370
|
return `dag-duotone-${elementId}`;
|
|
1359
1371
|
}
|
|
1360
|
-
function clamp(
|
|
1372
|
+
function clamp(value, minimum, maximum) {
|
|
1373
|
+
return Math.min(maximum, Math.max(minimum, value));
|
|
1374
|
+
}
|
|
1375
|
+
function buildImageBiLevelTable(threshold) {
|
|
1376
|
+
const cutoff = clamp(threshold, 0, 100);
|
|
1377
|
+
return Array.from({ length: 101 }, (_, index) => index >= cutoff ? "1" : "0").join(" ");
|
|
1378
|
+
}
|
|
1379
|
+
function buildImageLuminanceTransfer(amount) {
|
|
1380
|
+
const normalized = clamp(amount / 100, -1, 1);
|
|
1381
|
+
return normalized >= 0 ? { slope: 1 - normalized, intercept: normalized } : { slope: 1 + normalized, intercept: 0 };
|
|
1382
|
+
}
|
|
1383
|
+
function clamp2(v, lo, hi) {
|
|
1361
1384
|
return v < lo ? lo : v > hi ? hi : v;
|
|
1362
1385
|
}
|
|
1363
1386
|
function normalizeRadius01(radius) {
|
|
1364
|
-
return
|
|
1387
|
+
return clamp2(radius / 100, 0, 1);
|
|
1388
|
+
}
|
|
1389
|
+
function hexToRgbUnit2(hex) {
|
|
1390
|
+
const clean = hex.replace(/^#/u, "");
|
|
1391
|
+
const r = parseInt(clean.substring(0, 2), 16) / 255;
|
|
1392
|
+
const g = parseInt(clean.substring(2, 4), 16) / 255;
|
|
1393
|
+
const b = parseInt(clean.substring(4, 6), 16) / 255;
|
|
1394
|
+
return {
|
|
1395
|
+
r: Number.isFinite(r) ? r : 0,
|
|
1396
|
+
g: Number.isFinite(g) ? g : 0,
|
|
1397
|
+
b: Number.isFinite(b) ? b : 0
|
|
1398
|
+
};
|
|
1365
1399
|
}
|
|
1366
1400
|
function getEffects(element) {
|
|
1367
1401
|
if (!isImageLikeElement(element)) {
|
|
@@ -1452,10 +1486,6 @@ function getImageFilterCss(element, options) {
|
|
|
1452
1486
|
filters.push(buildSimpleArtisticCss(effects.artisticEffect, radius));
|
|
1453
1487
|
}
|
|
1454
1488
|
}
|
|
1455
|
-
if (typeof effects.biLevel === "number") {
|
|
1456
|
-
const thresh = clamp(effects.biLevel, 0, 100);
|
|
1457
|
-
filters.push(`grayscale(100%) contrast(1000%) brightness(${thresh}%)`);
|
|
1458
|
-
}
|
|
1459
1489
|
return filters.length > 0 ? filters.join(" ") : void 0;
|
|
1460
1490
|
}
|
|
1461
1491
|
var getImageEffectsFilter = getImageFilterCss;
|
|
@@ -1526,10 +1556,257 @@ function getImageEffectsOpacity(element) {
|
|
|
1526
1556
|
return void 0;
|
|
1527
1557
|
}
|
|
1528
1558
|
if (typeof effects.alphaModFix === "number") {
|
|
1529
|
-
return
|
|
1559
|
+
return clamp2(effects.alphaModFix / 100, 0, 1);
|
|
1530
1560
|
}
|
|
1531
1561
|
return void 0;
|
|
1532
1562
|
}
|
|
1563
|
+
var GRAYSCALE_LUMINANCE_MATRIX = [
|
|
1564
|
+
0.2126,
|
|
1565
|
+
0.7152,
|
|
1566
|
+
0.0722,
|
|
1567
|
+
0,
|
|
1568
|
+
0,
|
|
1569
|
+
0.2126,
|
|
1570
|
+
0.7152,
|
|
1571
|
+
0.0722,
|
|
1572
|
+
0,
|
|
1573
|
+
0,
|
|
1574
|
+
0.2126,
|
|
1575
|
+
0.7152,
|
|
1576
|
+
0.0722,
|
|
1577
|
+
0,
|
|
1578
|
+
0,
|
|
1579
|
+
0,
|
|
1580
|
+
0,
|
|
1581
|
+
0,
|
|
1582
|
+
1,
|
|
1583
|
+
0
|
|
1584
|
+
].join(" ");
|
|
1585
|
+
function buildDuotoneFilterMarkup(color1, color2) {
|
|
1586
|
+
const c1 = hexToRgbUnit2(color1);
|
|
1587
|
+
const c2 = hexToRgbUnit2(color2);
|
|
1588
|
+
return `<feColorMatrix type="matrix" values="${GRAYSCALE_LUMINANCE_MATRIX}"/><feComponentTransfer><feFuncR type="linear" slope="${c2.r - c1.r}" intercept="${c1.r}"/><feFuncG type="linear" slope="${c2.g - c1.g}" intercept="${c1.g}"/><feFuncB type="linear" slope="${c2.b - c1.b}" intercept="${c1.b}"/></feComponentTransfer>`;
|
|
1589
|
+
}
|
|
1590
|
+
function getDuotoneImageFilter(element, elementId = isImageLikeElement(element) ? element.id : "") {
|
|
1591
|
+
const effects = getEffects(element);
|
|
1592
|
+
if (!effects?.duotone) {
|
|
1593
|
+
return void 0;
|
|
1594
|
+
}
|
|
1595
|
+
const id = getImageDuotoneFilterId(elementId);
|
|
1596
|
+
return {
|
|
1597
|
+
id,
|
|
1598
|
+
cssReference: `url(#${id})`,
|
|
1599
|
+
filterMarkup: buildDuotoneFilterMarkup(effects.duotone.color1, effects.duotone.color2)
|
|
1600
|
+
};
|
|
1601
|
+
}
|
|
1602
|
+
function buildImageAlphaFilterMarkup(effects) {
|
|
1603
|
+
const parts = [];
|
|
1604
|
+
let resultIdx = 0;
|
|
1605
|
+
let inputRef = "SourceGraphic";
|
|
1606
|
+
const next = (build) => {
|
|
1607
|
+
const output = `r${resultIdx++}`;
|
|
1608
|
+
parts.push(build(inputRef, output));
|
|
1609
|
+
inputRef = output;
|
|
1610
|
+
};
|
|
1611
|
+
if (typeof effects.alphaModFix === "number") {
|
|
1612
|
+
const mul = clamp2(effects.alphaModFix / 100, 0, 1);
|
|
1613
|
+
next(
|
|
1614
|
+
(inp, out) => `<feColorMatrix in="${inp}" result="${out}" type="matrix" values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 ${mul} 0"/>`
|
|
1615
|
+
);
|
|
1616
|
+
}
|
|
1617
|
+
if (effects.alphaInv) {
|
|
1618
|
+
next(
|
|
1619
|
+
(inp, out) => `<feComponentTransfer in="${inp}" result="${out}"><feFuncA type="linear" slope="-1" intercept="1"/></feComponentTransfer>`
|
|
1620
|
+
);
|
|
1621
|
+
}
|
|
1622
|
+
if (effects.alphaCeiling) {
|
|
1623
|
+
next(
|
|
1624
|
+
(inp, out) => `<feComponentTransfer in="${inp}" result="${out}"><feFuncA type="discrete" tableValues="0 1 1 1 1 1 1 1 1 1"/></feComponentTransfer>`
|
|
1625
|
+
);
|
|
1626
|
+
}
|
|
1627
|
+
if (effects.alphaFloor) {
|
|
1628
|
+
next(
|
|
1629
|
+
(inp, out) => `<feComponentTransfer in="${inp}" result="${out}"><feFuncA type="discrete" tableValues="0 0 0 0 0 0 0 0 0 1"/></feComponentTransfer>`
|
|
1630
|
+
);
|
|
1631
|
+
}
|
|
1632
|
+
if (typeof effects.alphaRepl === "number") {
|
|
1633
|
+
const a = clamp2(effects.alphaRepl / 100, 0, 1);
|
|
1634
|
+
next(
|
|
1635
|
+
(inp, out) => `<feComponentTransfer in="${inp}" result="${out}"><feFuncA type="linear" slope="0" intercept="${a}"/></feComponentTransfer>`
|
|
1636
|
+
);
|
|
1637
|
+
}
|
|
1638
|
+
if (typeof effects.alphaBiLevel === "number") {
|
|
1639
|
+
const t = clamp2(effects.alphaBiLevel / 100, 0, 1);
|
|
1640
|
+
const table = Array.from({ length: 10 }, (_, i) => i / 10 >= t ? "1" : "0").join(" ");
|
|
1641
|
+
next(
|
|
1642
|
+
(inp, out) => `<feComponentTransfer in="${inp}" result="${out}"><feFuncA type="discrete" tableValues="${table}"/></feComponentTransfer>`
|
|
1643
|
+
);
|
|
1644
|
+
}
|
|
1645
|
+
if (typeof effects.biLevel === "number") {
|
|
1646
|
+
next((inp, out) => `<feColorMatrix in="${inp}" result="${out}" type="saturate" values="0"/>`);
|
|
1647
|
+
const tbl = buildImageBiLevelTable(effects.biLevel);
|
|
1648
|
+
next(
|
|
1649
|
+
(inp, out) => `<feComponentTransfer in="${inp}" result="${out}"><feFuncR type="discrete" tableValues="${tbl}"/><feFuncG type="discrete" tableValues="${tbl}"/><feFuncB type="discrete" tableValues="${tbl}"/></feComponentTransfer>`
|
|
1650
|
+
);
|
|
1651
|
+
}
|
|
1652
|
+
if (effects.lum && (typeof effects.lum.bright === "number" || typeof effects.lum.contrast === "number")) {
|
|
1653
|
+
const b = (effects.lum.bright ?? 0) / 100;
|
|
1654
|
+
const c = 1 + (effects.lum.contrast ?? 0) / 100;
|
|
1655
|
+
const slope = c;
|
|
1656
|
+
const intercept = b + (1 - c) / 2;
|
|
1657
|
+
next(
|
|
1658
|
+
(inp, out) => `<feComponentTransfer in="${inp}" result="${out}"><feFuncR type="linear" slope="${slope}" intercept="${intercept}"/><feFuncG type="linear" slope="${slope}" intercept="${intercept}"/><feFuncB type="linear" slope="${slope}" intercept="${intercept}"/></feComponentTransfer>`
|
|
1659
|
+
);
|
|
1660
|
+
}
|
|
1661
|
+
if (effects.hsl && typeof effects.hsl.sat === "number") {
|
|
1662
|
+
const v = clamp2(1 + effects.hsl.sat / 100, 0, 2);
|
|
1663
|
+
next(
|
|
1664
|
+
(inp, out) => `<feColorMatrix in="${inp}" result="${out}" type="saturate" values="${v}"/>`
|
|
1665
|
+
);
|
|
1666
|
+
}
|
|
1667
|
+
if (effects.hsl && typeof effects.hsl.lum === "number") {
|
|
1668
|
+
const { slope, intercept } = buildImageLuminanceTransfer(effects.hsl.lum);
|
|
1669
|
+
next(
|
|
1670
|
+
(inp, out) => `<feComponentTransfer in="${inp}" result="${out}"><feFuncR type="linear" slope="${slope}" intercept="${intercept}"/><feFuncG type="linear" slope="${slope}" intercept="${intercept}"/><feFuncB type="linear" slope="${slope}" intercept="${intercept}"/></feComponentTransfer>`
|
|
1671
|
+
);
|
|
1672
|
+
}
|
|
1673
|
+
if (effects.tint && typeof effects.tint.hue === "number") {
|
|
1674
|
+
next(
|
|
1675
|
+
(inp, out) => `<feColorMatrix in="${inp}" result="${out}" type="hueRotate" values="${effects.tint.hue}"/>`
|
|
1676
|
+
);
|
|
1677
|
+
}
|
|
1678
|
+
if (effects.tint && typeof effects.tint.amt === "number") {
|
|
1679
|
+
const { slope, intercept } = buildImageLuminanceTransfer(effects.tint.amt);
|
|
1680
|
+
next(
|
|
1681
|
+
(inp, out) => `<feComponentTransfer in="${inp}" result="${out}"><feFuncR type="linear" slope="${slope}" intercept="${intercept}"/><feFuncG type="linear" slope="${slope}" intercept="${intercept}"/><feFuncB type="linear" slope="${slope}" intercept="${intercept}"/></feComponentTransfer>`
|
|
1682
|
+
);
|
|
1683
|
+
}
|
|
1684
|
+
if (effects.clrRepl) {
|
|
1685
|
+
const c = hexToRgbUnit2(effects.clrRepl.color);
|
|
1686
|
+
next(
|
|
1687
|
+
(inp, out) => `<feColorMatrix in="${inp}" result="${out}" type="matrix" values="0 0 0 0 ${c.r} 0 0 0 0 ${c.g} 0 0 0 0 ${c.b} 0 0 0 1 0"/>`
|
|
1688
|
+
);
|
|
1689
|
+
}
|
|
1690
|
+
return parts.length > 0 ? parts.join("") : void 0;
|
|
1691
|
+
}
|
|
1692
|
+
function getImageAlphaFilter(element, elementId = isImageLikeElement(element) ? element.id : "") {
|
|
1693
|
+
const effects = getEffects(element);
|
|
1694
|
+
if (!effects || !hasAdvancedImageAlphaEffects(element)) {
|
|
1695
|
+
return void 0;
|
|
1696
|
+
}
|
|
1697
|
+
const markup = buildImageAlphaFilterMarkup(effects);
|
|
1698
|
+
if (!markup) {
|
|
1699
|
+
return void 0;
|
|
1700
|
+
}
|
|
1701
|
+
const id = getImageAlphaFilterId(elementId);
|
|
1702
|
+
return { id, cssReference: `url(#${id})`, filterMarkup: markup };
|
|
1703
|
+
}
|
|
1704
|
+
function buildDiscreteTable(steps) {
|
|
1705
|
+
const values = [];
|
|
1706
|
+
for (let i = 0; i < steps; i++) {
|
|
1707
|
+
const v = Math.round(i / (steps - 1) * 100) / 100;
|
|
1708
|
+
const count = Math.ceil(256 / steps);
|
|
1709
|
+
for (let j = 0; j < count; j++) {
|
|
1710
|
+
values.push(v);
|
|
1711
|
+
}
|
|
1712
|
+
}
|
|
1713
|
+
return values.slice(0, 256).join(" ");
|
|
1714
|
+
}
|
|
1715
|
+
function buildArtisticFilterMarkup(effectName, radius) {
|
|
1716
|
+
const normalizedRadius = clamp2(radius, 0, 100) / 100;
|
|
1717
|
+
switch (effectName) {
|
|
1718
|
+
case "artisticFilmGrain":
|
|
1719
|
+
case "filmGrain": {
|
|
1720
|
+
const freq = 0.5 + normalizedRadius * 0.5;
|
|
1721
|
+
const opacity = 0.15 + normalizedRadius * 0.25;
|
|
1722
|
+
return `<feTurbulence type="fractalNoise" baseFrequency="${freq}" numOctaves="4" seed="1" stitchTiles="stitch" result="grain"/><feColorMatrix in="grain" type="saturate" values="0" result="grainGray"/><feBlend in="SourceGraphic" in2="grainGray" mode="overlay"/><feComponentTransfer><feFuncR type="linear" slope="${1 + opacity * 0.3}" intercept="${opacity * 0.02}"/><feFuncG type="linear" slope="${1 + opacity * 0.3}" intercept="${opacity * 0.02}"/><feFuncB type="linear" slope="${1 + opacity * 0.3}" intercept="${opacity * 0.02}"/></feComponentTransfer>`;
|
|
1723
|
+
}
|
|
1724
|
+
case "artisticCutout":
|
|
1725
|
+
case "cutout": {
|
|
1726
|
+
const steps = Math.max(2, Math.round(4 + normalizedRadius * 4));
|
|
1727
|
+
const tableValues = buildDiscreteTable(steps);
|
|
1728
|
+
return `<feComponentTransfer><feFuncR type="discrete" tableValues="${tableValues}"/><feFuncG type="discrete" tableValues="${tableValues}"/><feFuncB type="discrete" tableValues="${tableValues}"/></feComponentTransfer>`;
|
|
1729
|
+
}
|
|
1730
|
+
case "artisticCement":
|
|
1731
|
+
case "cement": {
|
|
1732
|
+
const freq = 1.5 + normalizedRadius * 2.5;
|
|
1733
|
+
const desat = Math.max(0, 0.4 - normalizedRadius * 0.3);
|
|
1734
|
+
return `<feColorMatrix type="saturate" values="${desat}" result="desat"/><feTurbulence type="fractalNoise" baseFrequency="${freq}" numOctaves="5" seed="2" stitchTiles="stitch" result="cementNoise"/><feColorMatrix in="cementNoise" type="saturate" values="0" result="cementGray"/><feBlend in="desat" in2="cementGray" mode="multiply"/><feComponentTransfer><feFuncR type="linear" slope="1.2" intercept="0"/><feFuncG type="linear" slope="1.2" intercept="0"/><feFuncB type="linear" slope="1.2" intercept="0"/></feComponentTransfer>`;
|
|
1735
|
+
}
|
|
1736
|
+
case "artisticTexturizer":
|
|
1737
|
+
case "texturizer": {
|
|
1738
|
+
const freq = 2 + normalizedRadius * 3;
|
|
1739
|
+
return `<feTurbulence type="fractalNoise" baseFrequency="${freq}" numOctaves="3" seed="3" stitchTiles="stitch" result="texNoise"/><feColorMatrix in="texNoise" type="saturate" values="0" result="texGray"/><feBlend in="SourceGraphic" in2="texGray" mode="overlay"/><feComponentTransfer><feFuncR type="linear" slope="1.1" intercept="0.02"/><feFuncG type="linear" slope="1.1" intercept="0.02"/><feFuncB type="linear" slope="1.1" intercept="0.02"/></feComponentTransfer>`;
|
|
1740
|
+
}
|
|
1741
|
+
case "artisticCrisscrossEtching":
|
|
1742
|
+
case "crisscrossEtching": {
|
|
1743
|
+
const freq = 0.08 + normalizedRadius * 0.12;
|
|
1744
|
+
return `<feColorMatrix type="saturate" values="0" result="gray"/><feTurbulence type="turbulence" baseFrequency="${freq} ${freq * 3}" numOctaves="1" seed="7" result="lines"/><feColorMatrix in="lines" type="saturate" values="0" result="linesGray"/><feBlend in="gray" in2="linesGray" mode="multiply"/><feComponentTransfer><feFuncR type="linear" slope="1.3" intercept="-0.05"/><feFuncG type="linear" slope="1.3" intercept="-0.05"/><feFuncB type="linear" slope="1.3" intercept="-0.05"/></feComponentTransfer>`;
|
|
1745
|
+
}
|
|
1746
|
+
case "artisticMosaic":
|
|
1747
|
+
case "artisticMosaicBubbles":
|
|
1748
|
+
case "mosaicBubbles":
|
|
1749
|
+
case "mosaic": {
|
|
1750
|
+
const blurAmount = Math.max(2, Math.round(radius * 0.8));
|
|
1751
|
+
const freq = 0.02 + normalizedRadius * 0.03;
|
|
1752
|
+
return `<feGaussianBlur in="SourceGraphic" stdDeviation="${blurAmount}" result="blurred"/><feTurbulence type="turbulence" baseFrequency="${freq}" numOctaves="1" seed="4" result="cells"/><feDisplacementMap in="blurred" in2="cells" scale="${blurAmount * 0.5}" xChannelSelector="R" yChannelSelector="G" result="mosaic"/><feComponentTransfer in="mosaic"><feFuncR type="linear" slope="1.05" intercept="0"/><feFuncG type="linear" slope="1.05" intercept="0"/><feFuncB type="linear" slope="1.05" intercept="0"/></feComponentTransfer>`;
|
|
1753
|
+
}
|
|
1754
|
+
case "artisticGlowEdges":
|
|
1755
|
+
case "glowEdges":
|
|
1756
|
+
case "glow_edges": {
|
|
1757
|
+
const edgeBlur = Math.max(1, Math.round(normalizedRadius * 3));
|
|
1758
|
+
return `<feColorMatrix type="matrix" values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0" result="inverted"/><feGaussianBlur in="inverted" stdDeviation="${edgeBlur}" result="blurredInv"/><feBlend in="SourceGraphic" in2="blurredInv" mode="screen"/><feComponentTransfer><feFuncR type="linear" slope="2" intercept="-0.3"/><feFuncG type="linear" slope="2" intercept="-0.3"/><feFuncB type="linear" slope="2" intercept="-0.3"/></feComponentTransfer>`;
|
|
1759
|
+
}
|
|
1760
|
+
case "artisticChalkSketch":
|
|
1761
|
+
case "chalkSketch":
|
|
1762
|
+
case "chalk": {
|
|
1763
|
+
const freq = 1 + normalizedRadius * 2;
|
|
1764
|
+
const desat = Math.max(0, 0.2 - normalizedRadius * 0.2);
|
|
1765
|
+
return `<feColorMatrix type="saturate" values="${desat}" result="gray"/><feTurbulence type="fractalNoise" baseFrequency="${freq}" numOctaves="4" seed="5" stitchTiles="stitch" result="chalkTex"/><feColorMatrix in="chalkTex" type="saturate" values="0" result="chalkGray"/><feBlend in="gray" in2="chalkGray" mode="overlay"/><feComponentTransfer><feFuncR type="linear" slope="1.5" intercept="0.05"/><feFuncG type="linear" slope="1.5" intercept="0.05"/><feFuncB type="linear" slope="1.5" intercept="0.05"/></feComponentTransfer>`;
|
|
1766
|
+
}
|
|
1767
|
+
case "artisticPencilSketch":
|
|
1768
|
+
case "pencilSketch": {
|
|
1769
|
+
const blurAmt = Math.max(3, Math.round(4 + normalizedRadius * 8));
|
|
1770
|
+
return `<feColorMatrix type="saturate" values="0" result="gray"/><feColorMatrix in="gray" type="matrix" values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0" result="invGray"/><feGaussianBlur in="invGray" stdDeviation="${blurAmt}" result="blurInv"/><feBlend in="blurInv" in2="gray" mode="screen" result="sketch"/><feComponentTransfer in="sketch"><feFuncR type="linear" slope="1.6" intercept="-0.15"/><feFuncG type="linear" slope="1.6" intercept="-0.15"/><feFuncB type="linear" slope="1.6" intercept="-0.15"/></feComponentTransfer>`;
|
|
1771
|
+
}
|
|
1772
|
+
case "artisticPencilGrayscale":
|
|
1773
|
+
case "pencilGrayscale":
|
|
1774
|
+
case "grayPencil": {
|
|
1775
|
+
const sharpenBlur = Math.max(0.5, 1 + normalizedRadius * 2);
|
|
1776
|
+
return `<feColorMatrix type="saturate" values="0" result="gray"/><feGaussianBlur in="gray" stdDeviation="${sharpenBlur}" result="grayBlur"/><feComposite in="gray" in2="grayBlur" operator="arithmetic" k1="0" k2="1.5" k3="-0.5" k4="0"/>`;
|
|
1777
|
+
}
|
|
1778
|
+
default:
|
|
1779
|
+
return void 0;
|
|
1780
|
+
}
|
|
1781
|
+
}
|
|
1782
|
+
function getArtisticImageFilter(element, elementId = isImageLikeElement(element) ? element.id : "") {
|
|
1783
|
+
const effects = getEffects(element);
|
|
1784
|
+
if (!effects?.artisticEffect || !needsSvgArtisticFilter(effects.artisticEffect)) {
|
|
1785
|
+
return void 0;
|
|
1786
|
+
}
|
|
1787
|
+
const markup = buildArtisticFilterMarkup(effects.artisticEffect, effects.artisticRadius ?? 5);
|
|
1788
|
+
if (!markup) {
|
|
1789
|
+
return void 0;
|
|
1790
|
+
}
|
|
1791
|
+
const id = getArtisticFilterId(elementId);
|
|
1792
|
+
return { id, cssReference: `url(#${id})`, filterMarkup: markup };
|
|
1793
|
+
}
|
|
1794
|
+
function getImageSvgFilters(element) {
|
|
1795
|
+
const defs = [];
|
|
1796
|
+
const duotone = getDuotoneImageFilter(element);
|
|
1797
|
+
if (duotone) {
|
|
1798
|
+
defs.push({ id: duotone.id, markup: duotone.filterMarkup });
|
|
1799
|
+
}
|
|
1800
|
+
const alpha = getImageAlphaFilter(element);
|
|
1801
|
+
if (alpha) {
|
|
1802
|
+
defs.push({ id: alpha.id, markup: alpha.filterMarkup });
|
|
1803
|
+
}
|
|
1804
|
+
const artistic = getArtisticImageFilter(element);
|
|
1805
|
+
if (artistic) {
|
|
1806
|
+
defs.push({ id: artistic.id, markup: artistic.filterMarkup });
|
|
1807
|
+
}
|
|
1808
|
+
return defs;
|
|
1809
|
+
}
|
|
1533
1810
|
var NONE_PRESETS = /* @__PURE__ */ new Set(["textNoShape", "textPlain"]);
|
|
1534
1811
|
var PATH_PRESETS = /* @__PURE__ */ new Set([
|
|
1535
1812
|
"textArchUp",
|
|
@@ -3284,8 +3561,8 @@ function hexToRgb(hex) {
|
|
|
3284
3561
|
];
|
|
3285
3562
|
}
|
|
3286
3563
|
function rgbToHex(r, g, b) {
|
|
3287
|
-
const
|
|
3288
|
-
return `#${
|
|
3564
|
+
const clamp7 = (v) => Math.max(0, Math.min(255, Math.round(v)));
|
|
3565
|
+
return `#${clamp7(r).toString(16).padStart(2, "0")}${clamp7(g).toString(16).padStart(2, "0")}${clamp7(b).toString(16).padStart(2, "0")}`;
|
|
3289
3566
|
}
|
|
3290
3567
|
function tint(hex, amount) {
|
|
3291
3568
|
const [r, g, b] = hexToRgb(hex);
|
|
@@ -3397,14 +3674,17 @@ function computeValueRange(series) {
|
|
|
3397
3674
|
}
|
|
3398
3675
|
function valueToY(val2, range, topY, bottomY) {
|
|
3399
3676
|
const usable = bottomY - topY;
|
|
3677
|
+
let ratio;
|
|
3400
3678
|
if (range.logScale && range.logBase) {
|
|
3401
3679
|
const base = range.logBase;
|
|
3402
3680
|
const clampedVal = Math.max(val2, range.min);
|
|
3403
3681
|
const logVal = Math.log(clampedVal) / Math.log(base);
|
|
3404
3682
|
const logMin = Math.log(range.min) / Math.log(base);
|
|
3405
|
-
|
|
3683
|
+
ratio = (logVal - logMin) / range.span;
|
|
3684
|
+
} else {
|
|
3685
|
+
ratio = (val2 - range.min) / range.span;
|
|
3406
3686
|
}
|
|
3407
|
-
return
|
|
3687
|
+
return range.reverseOrder ? topY + ratio * usable : bottomY - ratio * usable;
|
|
3408
3688
|
}
|
|
3409
3689
|
function formatAxisValue(val2) {
|
|
3410
3690
|
if (Math.abs(val2) >= 1e6) {
|
|
@@ -3462,8 +3742,9 @@ function computeLayout(elementWidth, elementHeight, style, hasAxes, legendPos, o
|
|
|
3462
3742
|
svgHeight
|
|
3463
3743
|
};
|
|
3464
3744
|
}
|
|
3745
|
+
var LOG_EXPONENT_TOLERANCE = 1e-12;
|
|
3465
3746
|
function computeLogValueRange(series, logBase) {
|
|
3466
|
-
const allValues = series.flatMap((
|
|
3747
|
+
const allValues = series.flatMap((item) => item.values).filter((value) => value > 0);
|
|
3467
3748
|
if (allValues.length === 0) {
|
|
3468
3749
|
return { min: 1, max: logBase, span: 1, logScale: true, logBase };
|
|
3469
3750
|
}
|
|
@@ -3473,15 +3754,34 @@ function computeLogValueRange(series, logBase) {
|
|
|
3473
3754
|
const logMax = Math.ceil(Math.log(dataMax) / Math.log(logBase));
|
|
3474
3755
|
const min = logBase ** logMin;
|
|
3475
3756
|
const max = logBase ** Math.max(logMax, logMin + 1);
|
|
3476
|
-
const logSpan = Math.log(max) / Math.log(logBase) - Math.log(min) / Math.log(logBase);
|
|
3477
3757
|
return {
|
|
3478
3758
|
min,
|
|
3479
3759
|
max,
|
|
3480
|
-
span: Math.max(
|
|
3760
|
+
span: Math.max(Math.log(max) / Math.log(logBase) - Math.log(min) / Math.log(logBase), 1),
|
|
3481
3761
|
logScale: true,
|
|
3482
3762
|
logBase
|
|
3483
3763
|
};
|
|
3484
3764
|
}
|
|
3765
|
+
function computeValueRangeForAxis(series, axis) {
|
|
3766
|
+
const requestedLogBase = axis?.logBase ?? 10;
|
|
3767
|
+
const logBase = axis?.logScale && Number.isFinite(requestedLogBase) && requestedLogBase > 1 ? requestedLogBase : axis?.logScale ? 10 : void 0;
|
|
3768
|
+
const automatic = logBase ? computeLogValueRange(series, logBase) : computeValueRange(series);
|
|
3769
|
+
const validMin = typeof axis?.min === "number" && Number.isFinite(axis.min) && (!logBase || axis.min > 0) ? axis.min : void 0;
|
|
3770
|
+
const validMax = typeof axis?.max === "number" && Number.isFinite(axis.max) && (!logBase || axis.max > 0) ? axis.max : void 0;
|
|
3771
|
+
const min = validMin ?? automatic.min;
|
|
3772
|
+
let max = validMax ?? automatic.max;
|
|
3773
|
+
if (max <= min) {
|
|
3774
|
+
max = logBase ? min * logBase : min + 1;
|
|
3775
|
+
}
|
|
3776
|
+
const span = logBase ? Math.log(max) / Math.log(logBase) - Math.log(min) / Math.log(logBase) : max - min;
|
|
3777
|
+
return {
|
|
3778
|
+
min,
|
|
3779
|
+
max,
|
|
3780
|
+
span: Math.max(span, Number.EPSILON),
|
|
3781
|
+
...logBase ? { logScale: true, logBase } : {},
|
|
3782
|
+
...axis?.orientation === "maxMin" ? { reverseOrder: true } : {}
|
|
3783
|
+
};
|
|
3784
|
+
}
|
|
3485
3785
|
function generateLogTicks(range) {
|
|
3486
3786
|
if (!range.logScale || !range.logBase) {
|
|
3487
3787
|
return [];
|
|
@@ -3489,21 +3789,56 @@ function generateLogTicks(range) {
|
|
|
3489
3789
|
const base = range.logBase;
|
|
3490
3790
|
const logMin = Math.log(range.min) / Math.log(base);
|
|
3491
3791
|
const logMax = Math.log(range.max) / Math.log(base);
|
|
3492
|
-
const
|
|
3493
|
-
|
|
3494
|
-
|
|
3792
|
+
const firstExponent = Math.ceil(logMin - LOG_EXPONENT_TOLERANCE);
|
|
3793
|
+
const lastExponent = Math.floor(logMax + LOG_EXPONENT_TOLERANCE);
|
|
3794
|
+
if (lastExponent < firstExponent) {
|
|
3795
|
+
return range.min === range.max ? [range.min] : [range.min, range.max];
|
|
3495
3796
|
}
|
|
3496
|
-
return
|
|
3797
|
+
return Array.from(
|
|
3798
|
+
{ length: lastExponent - firstExponent + 1 },
|
|
3799
|
+
(_unused, index) => base ** (firstExponent + index)
|
|
3800
|
+
);
|
|
3497
3801
|
}
|
|
3498
|
-
function
|
|
3499
|
-
|
|
3802
|
+
function generateIntervalTicks(range, unit) {
|
|
3803
|
+
if (!Number.isFinite(unit) || unit <= 0 || range.logScale) {
|
|
3804
|
+
return [];
|
|
3805
|
+
}
|
|
3806
|
+
const tolerance = Math.max(1, Math.abs(range.min), Math.abs(range.max)) * 1e-12;
|
|
3807
|
+
const first = Math.ceil((range.min - tolerance) / unit);
|
|
3808
|
+
const last = Math.floor((range.max + tolerance) / unit);
|
|
3809
|
+
const count = Math.min(Math.max(last - first + 1, 0), 2e3);
|
|
3810
|
+
return Array.from({ length: count }, (_, index) => (first + index) * unit);
|
|
3500
3811
|
}
|
|
3501
|
-
function
|
|
3502
|
-
|
|
3503
|
-
|
|
3504
|
-
|
|
3812
|
+
function generateAxisTicks(range, axis, defaultIntervals) {
|
|
3813
|
+
if (range.logScale && range.logBase) {
|
|
3814
|
+
return generateLogTicks(range);
|
|
3815
|
+
}
|
|
3816
|
+
const explicit = generateIntervalTicks(range, axis?.majorUnit ?? 0);
|
|
3817
|
+
if (explicit.length > 0) {
|
|
3818
|
+
return explicit;
|
|
3819
|
+
}
|
|
3820
|
+
return Array.from(
|
|
3821
|
+
{ length: defaultIntervals + 1 },
|
|
3822
|
+
(_, index) => range.min + range.span / defaultIntervals * index
|
|
3823
|
+
);
|
|
3824
|
+
}
|
|
3825
|
+
function generateMinorAxisTicks(range, axis) {
|
|
3826
|
+
const minor = generateIntervalTicks(range, axis?.minorUnit ?? 0);
|
|
3827
|
+
if (minor.length === 0) {
|
|
3828
|
+
return [];
|
|
3505
3829
|
}
|
|
3506
|
-
|
|
3830
|
+
const majorUnit = axis?.majorUnit;
|
|
3831
|
+
if (!majorUnit || !Number.isFinite(majorUnit) || majorUnit <= 0) {
|
|
3832
|
+
return minor;
|
|
3833
|
+
}
|
|
3834
|
+
const tolerance = Math.max(1, Math.abs(range.min), Math.abs(range.max)) * 1e-10;
|
|
3835
|
+
return minor.filter(
|
|
3836
|
+
(value) => Math.abs(value / majorUnit - Math.round(value / majorUnit)) > tolerance
|
|
3837
|
+
);
|
|
3838
|
+
}
|
|
3839
|
+
function computeValueRangeForChart(series, axes) {
|
|
3840
|
+
const primaryAxis = axes?.find((axis) => axis.axisType === "valAx" && axis.axPos === "l") ?? axes?.find((axis) => axis.axisType === "valAx" && axis.axPos !== "r");
|
|
3841
|
+
return computeValueRangeForAxis(series, primaryAxis);
|
|
3507
3842
|
}
|
|
3508
3843
|
var DISPLAY_UNIT_DIVISORS = {
|
|
3509
3844
|
hundreds: 100,
|
|
@@ -3589,6 +3924,13 @@ function getSecondaryValueAxisId(axes) {
|
|
|
3589
3924
|
const ax = getSecondaryValueAxis(axes);
|
|
3590
3925
|
return ax?.axisId;
|
|
3591
3926
|
}
|
|
3927
|
+
function getPrimaryValueAxisId(axes) {
|
|
3928
|
+
if (!axes) {
|
|
3929
|
+
return void 0;
|
|
3930
|
+
}
|
|
3931
|
+
const primary = axes.find((a) => a.axisType === "valAx" && a.axPos === "l");
|
|
3932
|
+
return primary?.axisId ?? axes.find((a) => a.axisType === "valAx")?.axisId;
|
|
3933
|
+
}
|
|
3592
3934
|
function isSeriesOnSecondaryAxis(series, axes) {
|
|
3593
3935
|
if (!axes) {
|
|
3594
3936
|
return false;
|
|
@@ -3614,41 +3956,199 @@ function splitSeriesByAxis(series, axes) {
|
|
|
3614
3956
|
}
|
|
3615
3957
|
return { primary, secondary };
|
|
3616
3958
|
}
|
|
3959
|
+
function clamp3(value, min, max) {
|
|
3960
|
+
return Math.min(Math.max(value, min), max);
|
|
3961
|
+
}
|
|
3962
|
+
function horizontalAxisY(valueAxis, range, layout, fallback) {
|
|
3963
|
+
if (valueAxis?.crosses === "max") {
|
|
3964
|
+
return layout.plotTop;
|
|
3965
|
+
}
|
|
3966
|
+
if (valueAxis?.crosses === "min") {
|
|
3967
|
+
return layout.plotBottom;
|
|
3968
|
+
}
|
|
3969
|
+
const crossingValue = valueAxis?.crossesAt ?? 0;
|
|
3970
|
+
if (valueAxis?.crossesAt !== void 0 || valueAxis?.crosses === "autoZero") {
|
|
3971
|
+
return valueToY2(
|
|
3972
|
+
clamp3(crossingValue, range.min, range.max),
|
|
3973
|
+
range,
|
|
3974
|
+
layout.plotTop,
|
|
3975
|
+
layout.plotBottom
|
|
3976
|
+
);
|
|
3977
|
+
}
|
|
3978
|
+
return fallback === "top" ? layout.plotTop : layout.plotBottom;
|
|
3979
|
+
}
|
|
3980
|
+
function verticalAxisX(categoryAxis, categoryCount, layout, fallback, dateValues) {
|
|
3981
|
+
if (categoryAxis?.crosses === "max") {
|
|
3982
|
+
return layout.plotRight;
|
|
3983
|
+
}
|
|
3984
|
+
if (categoryAxis?.crosses === "min") {
|
|
3985
|
+
return layout.plotLeft;
|
|
3986
|
+
}
|
|
3987
|
+
if (categoryAxis?.crossesAt !== void 0) {
|
|
3988
|
+
if (categoryAxis.axisType === "dateAx" && dateValues?.length) {
|
|
3989
|
+
const min = categoryAxis.min ?? Math.min(...dateValues);
|
|
3990
|
+
const max = categoryAxis.max ?? Math.max(...dateValues);
|
|
3991
|
+
const ratio2 = max === min ? 0.5 : (clamp3(categoryAxis.crossesAt, min, max) - min) / (max - min);
|
|
3992
|
+
return layout.plotLeft + ratio2 * layout.plotWidth;
|
|
3993
|
+
}
|
|
3994
|
+
const index = clamp3(categoryAxis.crossesAt - 1, 0, Math.max(categoryCount - 1, 0));
|
|
3995
|
+
const ratio = categoryCount <= 1 ? 0.5 : index / (categoryCount - 1);
|
|
3996
|
+
return layout.plotLeft + ratio * layout.plotWidth;
|
|
3997
|
+
}
|
|
3998
|
+
return fallback === "right" ? layout.plotRight : layout.plotLeft;
|
|
3999
|
+
}
|
|
4000
|
+
var DEFAULT_COLOR2 = "#64748b";
|
|
4001
|
+
function dashArray(style, width) {
|
|
4002
|
+
if (!style || style === "solid") {
|
|
4003
|
+
return void 0;
|
|
4004
|
+
}
|
|
4005
|
+
const unit = Math.max(width, 1);
|
|
4006
|
+
if (style === "dot" || style === "sysDot") {
|
|
4007
|
+
return `${unit} ${unit * 2}`;
|
|
4008
|
+
}
|
|
4009
|
+
if (style === "lgDash") {
|
|
4010
|
+
return `${unit * 6} ${unit * 3}`;
|
|
4011
|
+
}
|
|
4012
|
+
return `${unit * 3} ${unit * 2}`;
|
|
4013
|
+
}
|
|
4014
|
+
function chartAxisTextStyle(axis, defaultFontSize = 8) {
|
|
4015
|
+
return {
|
|
4016
|
+
fontSize: axis?.fontSize ?? defaultFontSize,
|
|
4017
|
+
fill: axis?.fontColor ?? DEFAULT_COLOR2,
|
|
4018
|
+
...axis?.fontBold !== void 0 ? { fontWeight: axis.fontBold ? "bold" : "normal" } : {},
|
|
4019
|
+
...axis?.fontFamily ? { fontFamily: axis.fontFamily } : {}
|
|
4020
|
+
};
|
|
4021
|
+
}
|
|
4022
|
+
function chartLineStyle(shape, fallbackColor = DEFAULT_COLOR2, fallbackWidth = 1) {
|
|
4023
|
+
const strokeWidth = shape?.strokeWidth ?? fallbackWidth;
|
|
4024
|
+
return {
|
|
4025
|
+
stroke: shape?.strokeColor ?? fallbackColor,
|
|
4026
|
+
strokeWidth,
|
|
4027
|
+
...dashArray(shape?.strokeDashStyle, strokeWidth) ? { dashArray: dashArray(shape?.strokeDashStyle, strokeWidth) } : {}
|
|
4028
|
+
};
|
|
4029
|
+
}
|
|
4030
|
+
function buildVerticalTickMark(axisX, y, placement, side, length, shape) {
|
|
4031
|
+
if (!placement || placement === "none") {
|
|
4032
|
+
return void 0;
|
|
4033
|
+
}
|
|
4034
|
+
const inward = side === "left" ? 1 : -1;
|
|
4035
|
+
const startOffset = placement === "cross" ? -inward * length : 0;
|
|
4036
|
+
const endOffset = placement === "out" ? -inward * length : inward * length;
|
|
4037
|
+
return {
|
|
4038
|
+
kind: "line",
|
|
4039
|
+
x1: axisX + startOffset,
|
|
4040
|
+
y1: y,
|
|
4041
|
+
x2: axisX + endOffset,
|
|
4042
|
+
y2: y,
|
|
4043
|
+
...chartLineStyle(shape)
|
|
4044
|
+
};
|
|
4045
|
+
}
|
|
4046
|
+
function buildVerticalAxisLine(axis, axisX, layout) {
|
|
4047
|
+
return axis?.spPr ? {
|
|
4048
|
+
kind: "line",
|
|
4049
|
+
x1: axisX,
|
|
4050
|
+
y1: layout.plotTop,
|
|
4051
|
+
x2: axisX,
|
|
4052
|
+
y2: layout.plotBottom,
|
|
4053
|
+
...chartLineStyle(axis.spPr)
|
|
4054
|
+
} : void 0;
|
|
4055
|
+
}
|
|
4056
|
+
function buildStyledGridline(y, layout, shape, fallbackColor, fallbackWidth, fallbackDash, opacity) {
|
|
4057
|
+
const style = chartLineStyle(shape, fallbackColor, fallbackWidth);
|
|
4058
|
+
return {
|
|
4059
|
+
kind: "line",
|
|
4060
|
+
x1: layout.plotLeft,
|
|
4061
|
+
y1: y,
|
|
4062
|
+
x2: layout.plotRight,
|
|
4063
|
+
y2: y,
|
|
4064
|
+
...style,
|
|
4065
|
+
...(shape ? style.dashArray : fallbackDash) ? { dashArray: shape ? style.dashArray : fallbackDash } : {},
|
|
4066
|
+
...opacity !== void 0 ? { opacity } : {}
|
|
4067
|
+
};
|
|
4068
|
+
}
|
|
3617
4069
|
var GRIDLINE_COLOR = "#e2e8f0";
|
|
3618
|
-
var AXIS_LABEL_COLOR = "#64748b";
|
|
3619
4070
|
var SECONDARY_GRID_COLOR = "#e2e8f0";
|
|
3620
4071
|
var TICK_COUNT = 5;
|
|
4072
|
+
var MAJOR_TICK_LENGTH = 4;
|
|
4073
|
+
var MINOR_TICK_LENGTH = 2.5;
|
|
4074
|
+
function valueAxisLabelPlacement(layout, position, defaultSide, axisX) {
|
|
4075
|
+
if (!position || position === "nextTo") {
|
|
4076
|
+
return defaultSide === "left" ? { x: axisX - 4, textAnchor: "end" } : { x: axisX + 4, textAnchor: "start" };
|
|
4077
|
+
}
|
|
4078
|
+
const side = position === "high" ? "right" : position === "low" ? "left" : defaultSide;
|
|
4079
|
+
return side === "left" ? { x: layout.plotLeft - 4, textAnchor: "end" } : { x: layout.plotRight + 4, textAnchor: "start" };
|
|
4080
|
+
}
|
|
3621
4081
|
function formatTick(val2, axis) {
|
|
3622
4082
|
if (axis?.displayUnits) {
|
|
3623
4083
|
return formatAxisValueWithUnits(val2, axis);
|
|
3624
4084
|
}
|
|
3625
4085
|
return formatAxisValue2(val2);
|
|
3626
4086
|
}
|
|
3627
|
-
function buildPrimaryAxis(range, layout, axis) {
|
|
4087
|
+
function buildPrimaryAxis(range, layout, axis, axisX = layout.plotLeft) {
|
|
3628
4088
|
const gridlines = [];
|
|
3629
4089
|
const axisLabels = [];
|
|
3630
|
-
const tickVals =
|
|
4090
|
+
const tickVals = generateAxisTicks(range, axis, TICK_COUNT);
|
|
4091
|
+
const minorTickVals = generateMinorAxisTicks(range, axis);
|
|
4092
|
+
const axisLine = buildVerticalAxisLine(axis, axisX, layout);
|
|
4093
|
+
if (axisLine) {
|
|
4094
|
+
gridlines.push(axisLine);
|
|
4095
|
+
}
|
|
4096
|
+
if (axis?.minorGridlines) {
|
|
4097
|
+
for (const val2 of minorTickVals) {
|
|
4098
|
+
const y = valueToY2(val2, range, layout.plotTop, layout.plotBottom);
|
|
4099
|
+
gridlines.push(
|
|
4100
|
+
buildStyledGridline(y, layout, axis.minorGridlinesSpPr, GRIDLINE_COLOR, 0.5, "1 2", 0.5)
|
|
4101
|
+
);
|
|
4102
|
+
}
|
|
4103
|
+
}
|
|
4104
|
+
for (const val2 of minorTickVals) {
|
|
4105
|
+
const y = valueToY2(val2, range, layout.plotTop, layout.plotBottom);
|
|
4106
|
+
const tick = buildVerticalTickMark(
|
|
4107
|
+
axisX,
|
|
4108
|
+
y,
|
|
4109
|
+
axis?.minorTickMark,
|
|
4110
|
+
"left",
|
|
4111
|
+
MINOR_TICK_LENGTH,
|
|
4112
|
+
axis?.spPr
|
|
4113
|
+
);
|
|
4114
|
+
if (tick) {
|
|
4115
|
+
gridlines.push(tick);
|
|
4116
|
+
}
|
|
4117
|
+
}
|
|
3631
4118
|
for (const val2 of tickVals) {
|
|
3632
4119
|
const y = valueToY2(val2, range, layout.plotTop, layout.plotBottom);
|
|
3633
|
-
gridlines.push(
|
|
3634
|
-
|
|
3635
|
-
|
|
3636
|
-
|
|
3637
|
-
|
|
3638
|
-
|
|
3639
|
-
|
|
3640
|
-
|
|
3641
|
-
|
|
3642
|
-
|
|
3643
|
-
|
|
3644
|
-
|
|
4120
|
+
gridlines.push(
|
|
4121
|
+
buildStyledGridline(
|
|
4122
|
+
y,
|
|
4123
|
+
layout,
|
|
4124
|
+
axis?.majorGridlinesSpPr,
|
|
4125
|
+
GRIDLINE_COLOR,
|
|
4126
|
+
1,
|
|
4127
|
+
void 0,
|
|
4128
|
+
void 0
|
|
4129
|
+
)
|
|
4130
|
+
);
|
|
4131
|
+
const tick = buildVerticalTickMark(
|
|
4132
|
+
axisX,
|
|
3645
4133
|
y,
|
|
3646
|
-
|
|
3647
|
-
|
|
3648
|
-
|
|
3649
|
-
|
|
3650
|
-
|
|
3651
|
-
|
|
4134
|
+
axis?.majorTickMark,
|
|
4135
|
+
"left",
|
|
4136
|
+
MAJOR_TICK_LENGTH,
|
|
4137
|
+
axis?.spPr
|
|
4138
|
+
);
|
|
4139
|
+
if (tick) {
|
|
4140
|
+
gridlines.push(tick);
|
|
4141
|
+
}
|
|
4142
|
+
if (axis?.tickLblPos !== "none") {
|
|
4143
|
+
axisLabels.push({
|
|
4144
|
+
kind: "text",
|
|
4145
|
+
...valueAxisLabelPlacement(layout, axis?.tickLblPos, "left", axisX),
|
|
4146
|
+
y,
|
|
4147
|
+
text: formatTick(val2, axis),
|
|
4148
|
+
...chartAxisTextStyle(axis),
|
|
4149
|
+
dominantBaseline: "central"
|
|
4150
|
+
});
|
|
4151
|
+
}
|
|
3652
4152
|
}
|
|
3653
4153
|
if (axis?.displayUnits) {
|
|
3654
4154
|
const unitLabel = getDisplayUnitLabel(axis.displayUnits, axis.displayUnitsLabel);
|
|
@@ -3660,8 +4160,7 @@ function buildPrimaryAxis(range, layout, axis) {
|
|
|
3660
4160
|
x: labelX,
|
|
3661
4161
|
y: midY,
|
|
3662
4162
|
text: unitLabel,
|
|
3663
|
-
|
|
3664
|
-
fill: AXIS_LABEL_COLOR,
|
|
4163
|
+
...chartAxisTextStyle(axis, 9),
|
|
3665
4164
|
textAnchor: "middle",
|
|
3666
4165
|
transform: `rotate(-90, ${labelX}, ${midY})`
|
|
3667
4166
|
});
|
|
@@ -3669,37 +4168,81 @@ function buildPrimaryAxis(range, layout, axis) {
|
|
|
3669
4168
|
}
|
|
3670
4169
|
return { gridlines, axisLabels };
|
|
3671
4170
|
}
|
|
3672
|
-
function buildSecondaryAxis(range, layout, axis) {
|
|
4171
|
+
function buildSecondaryAxis(range, layout, axis, axisX = layout.plotRight) {
|
|
3673
4172
|
const gridlines = [];
|
|
3674
4173
|
const axisLabels = [];
|
|
3675
|
-
const
|
|
3676
|
-
const
|
|
3677
|
-
const
|
|
3678
|
-
|
|
3679
|
-
|
|
4174
|
+
const textStyle = chartAxisTextStyle(axis);
|
|
4175
|
+
const captionStyle = chartAxisTextStyle(axis, 9);
|
|
4176
|
+
const axisLine = buildVerticalAxisLine(axis, axisX, layout);
|
|
4177
|
+
if (axisLine) {
|
|
4178
|
+
gridlines.push(axisLine);
|
|
4179
|
+
}
|
|
4180
|
+
const tickValues = generateAxisTicks(range, axis, TICK_COUNT - 1);
|
|
4181
|
+
const minorTickValues = generateMinorAxisTicks(range, axis);
|
|
4182
|
+
if (axis?.minorGridlines) {
|
|
4183
|
+
for (const val2 of minorTickValues) {
|
|
4184
|
+
const y = valueToY2(val2, range, layout.plotTop, layout.plotBottom);
|
|
4185
|
+
gridlines.push(
|
|
4186
|
+
buildStyledGridline(
|
|
4187
|
+
y,
|
|
4188
|
+
layout,
|
|
4189
|
+
axis.minorGridlinesSpPr,
|
|
4190
|
+
SECONDARY_GRID_COLOR,
|
|
4191
|
+
0.5,
|
|
4192
|
+
"1 2",
|
|
4193
|
+
0.35
|
|
4194
|
+
)
|
|
4195
|
+
);
|
|
4196
|
+
}
|
|
4197
|
+
}
|
|
4198
|
+
for (const val2 of minorTickValues) {
|
|
3680
4199
|
const y = valueToY2(val2, range, layout.plotTop, layout.plotBottom);
|
|
3681
|
-
|
|
3682
|
-
|
|
3683
|
-
x1: layout.plotLeft,
|
|
3684
|
-
y1: y,
|
|
3685
|
-
x2: layout.plotRight,
|
|
3686
|
-
y2: y,
|
|
3687
|
-
stroke: SECONDARY_GRID_COLOR,
|
|
3688
|
-
strokeWidth: 0.5,
|
|
3689
|
-
dashArray: "2 3",
|
|
3690
|
-
opacity: 0.5
|
|
3691
|
-
});
|
|
3692
|
-
axisLabels.push({
|
|
3693
|
-
kind: "text",
|
|
3694
|
-
x: layout.plotRight + 4,
|
|
4200
|
+
const tick = buildVerticalTickMark(
|
|
4201
|
+
axisX,
|
|
3695
4202
|
y,
|
|
3696
|
-
|
|
3697
|
-
|
|
3698
|
-
|
|
3699
|
-
|
|
3700
|
-
|
|
3701
|
-
|
|
3702
|
-
|
|
4203
|
+
axis?.minorTickMark,
|
|
4204
|
+
"right",
|
|
4205
|
+
MINOR_TICK_LENGTH,
|
|
4206
|
+
axis?.spPr
|
|
4207
|
+
);
|
|
4208
|
+
if (tick) {
|
|
4209
|
+
gridlines.push(tick);
|
|
4210
|
+
}
|
|
4211
|
+
}
|
|
4212
|
+
for (const val2 of tickValues) {
|
|
4213
|
+
const y = valueToY2(val2, range, layout.plotTop, layout.plotBottom);
|
|
4214
|
+
gridlines.push(
|
|
4215
|
+
buildStyledGridline(
|
|
4216
|
+
y,
|
|
4217
|
+
layout,
|
|
4218
|
+
axis?.majorGridlinesSpPr,
|
|
4219
|
+
SECONDARY_GRID_COLOR,
|
|
4220
|
+
0.5,
|
|
4221
|
+
"2 3",
|
|
4222
|
+
0.5
|
|
4223
|
+
)
|
|
4224
|
+
);
|
|
4225
|
+
const tick = buildVerticalTickMark(
|
|
4226
|
+
axisX,
|
|
4227
|
+
y,
|
|
4228
|
+
axis?.majorTickMark,
|
|
4229
|
+
"right",
|
|
4230
|
+
MAJOR_TICK_LENGTH,
|
|
4231
|
+
axis?.spPr
|
|
4232
|
+
);
|
|
4233
|
+
if (tick) {
|
|
4234
|
+
gridlines.push(tick);
|
|
4235
|
+
}
|
|
4236
|
+
if (axis?.tickLblPos !== "none") {
|
|
4237
|
+
axisLabels.push({
|
|
4238
|
+
kind: "text",
|
|
4239
|
+
...valueAxisLabelPlacement(layout, axis?.tickLblPos, "right", axisX),
|
|
4240
|
+
y,
|
|
4241
|
+
text: formatTick(val2, axis),
|
|
4242
|
+
...textStyle,
|
|
4243
|
+
dominantBaseline: "central"
|
|
4244
|
+
});
|
|
4245
|
+
}
|
|
3703
4246
|
}
|
|
3704
4247
|
if (axis?.titleText) {
|
|
3705
4248
|
const titleX = layout.plotRight + 36;
|
|
@@ -3709,8 +4252,7 @@ function buildSecondaryAxis(range, layout, axis) {
|
|
|
3709
4252
|
x: titleX,
|
|
3710
4253
|
y: midY,
|
|
3711
4254
|
text: axis.titleText,
|
|
3712
|
-
|
|
3713
|
-
fill: fontColor,
|
|
4255
|
+
...captionStyle,
|
|
3714
4256
|
textAnchor: "middle",
|
|
3715
4257
|
transform: `rotate(-90, ${titleX}, ${midY})`
|
|
3716
4258
|
});
|
|
@@ -3725,8 +4267,7 @@ function buildSecondaryAxis(range, layout, axis) {
|
|
|
3725
4267
|
x: labelX,
|
|
3726
4268
|
y: midY,
|
|
3727
4269
|
text: unitLabel,
|
|
3728
|
-
|
|
3729
|
-
fill: fontColor,
|
|
4270
|
+
...captionStyle,
|
|
3730
4271
|
textAnchor: "middle",
|
|
3731
4272
|
transform: `rotate(-90, ${labelX}, ${midY})`
|
|
3732
4273
|
});
|
|
@@ -3734,13 +4275,60 @@ function buildSecondaryAxis(range, layout, axis) {
|
|
|
3734
4275
|
}
|
|
3735
4276
|
return { gridlines, axisLabels };
|
|
3736
4277
|
}
|
|
4278
|
+
function hasRicherAxisFeatures(chartData) {
|
|
4279
|
+
return Boolean(
|
|
4280
|
+
chartData.dataTable || chartData.axes?.some(
|
|
4281
|
+
(axis) => axis.crosses !== void 0 || axis.crossesAt !== void 0 || axis.crossBetween !== void 0 || axis.axisType === "valAx" && Boolean(
|
|
4282
|
+
axis.logScale || axis.displayUnits || axis.axPos === "r" || axis.orientation === "maxMin" || axis.majorUnit !== void 0 || axis.minorUnit !== void 0 || axis.minorGridlines || axis.majorTickMark !== void 0 || axis.minorTickMark !== void 0 || axis.tickLblPos !== void 0
|
|
4283
|
+
)
|
|
4284
|
+
)
|
|
4285
|
+
);
|
|
4286
|
+
}
|
|
4287
|
+
function buildCartesianAxes(chartData, layout, primaryRange, secondaryRange, categoryCount) {
|
|
4288
|
+
if (!hasRicherAxisFeatures(chartData)) {
|
|
4289
|
+
const { gridlines, axisLabels } = buildGridlinesAndLabels(primaryRange, layout);
|
|
4290
|
+
return { gridlines, axisLabels, secondaryGridlines: void 0, secondaryAxisLabels: void 0 };
|
|
4291
|
+
}
|
|
4292
|
+
const primaryAxis = chartData.axes?.find((axis) => axis.axisType === "valAx" && axis.axPos !== "r") ?? chartData.axes?.find((axis) => axis.axisType === "valAx");
|
|
4293
|
+
const categoryAxisFor = (axisId) => chartData.axes?.find(
|
|
4294
|
+
(axis) => (axis.axisType === "catAx" || axis.axisType === "dateAx") && axis.axisId === axisId
|
|
4295
|
+
);
|
|
4296
|
+
const axisX = (axisId, fallback) => verticalAxisX(
|
|
4297
|
+
categoryAxisFor(axisId),
|
|
4298
|
+
categoryCount,
|
|
4299
|
+
layout,
|
|
4300
|
+
fallback,
|
|
4301
|
+
chartData.dateCategories?.values
|
|
4302
|
+
);
|
|
4303
|
+
const primary = buildPrimaryAxis(
|
|
4304
|
+
primaryRange,
|
|
4305
|
+
layout,
|
|
4306
|
+
primaryAxis,
|
|
4307
|
+
axisX(primaryAxis?.crossAxisId, "left")
|
|
4308
|
+
);
|
|
4309
|
+
if (!secondaryRange) {
|
|
4310
|
+
return { ...primary, secondaryGridlines: void 0, secondaryAxisLabels: void 0 };
|
|
4311
|
+
}
|
|
4312
|
+
const secondaryAxis = getSecondaryValueAxis(chartData.axes);
|
|
4313
|
+
const secondary = buildSecondaryAxis(
|
|
4314
|
+
secondaryRange,
|
|
4315
|
+
layout,
|
|
4316
|
+
secondaryAxis,
|
|
4317
|
+
axisX(secondaryAxis?.crossAxisId, "right")
|
|
4318
|
+
);
|
|
4319
|
+
return {
|
|
4320
|
+
...primary,
|
|
4321
|
+
secondaryGridlines: secondary.gridlines,
|
|
4322
|
+
secondaryAxisLabels: secondary.axisLabels
|
|
4323
|
+
};
|
|
4324
|
+
}
|
|
3737
4325
|
function categoryTotals(series, catCount) {
|
|
3738
4326
|
return Array.from(
|
|
3739
4327
|
{ length: catCount },
|
|
3740
4328
|
(_, ci) => series.reduce((sum, s) => sum + Math.abs(s.values[ci] ?? 0), 0)
|
|
3741
4329
|
);
|
|
3742
4330
|
}
|
|
3743
|
-
function buildBars(chartData, catCount, layout, primaryRange, secondaryRange, secondaryIdx, grouping) {
|
|
4331
|
+
function buildBars(chartData, catCount, layout, primaryRange, secondaryRange, secondaryIdx, grouping, sourceIndices2) {
|
|
3744
4332
|
const primitives = [];
|
|
3745
4333
|
const dataLabels = [];
|
|
3746
4334
|
const series = chartData.series;
|
|
@@ -3751,10 +4339,11 @@ function buildBars(chartData, catCount, layout, primaryRange, secondaryRange, se
|
|
|
3751
4339
|
const barGroupWidth2 = layout.plotWidth / Math.max(catCount, 1);
|
|
3752
4340
|
const singleBarWidth = barGroupWidth2 * 0.7 / seriesCount;
|
|
3753
4341
|
const groupOffset = (barGroupWidth2 - singleBarWidth * seriesCount) / 2;
|
|
3754
|
-
for (let
|
|
4342
|
+
for (let displayIndex = 0; displayIndex < catCount; displayIndex++) {
|
|
4343
|
+
const sourceIndex = sourceIndices2[displayIndex] ?? displayIndex;
|
|
3755
4344
|
for (let si = 0; si < series.length; si++) {
|
|
3756
|
-
const val2 = series[si].values[
|
|
3757
|
-
const x = layout.plotLeft + barGroupWidth2 *
|
|
4345
|
+
const val2 = series[si].values[sourceIndex] ?? 0;
|
|
4346
|
+
const x = layout.plotLeft + barGroupWidth2 * displayIndex + groupOffset + singleBarWidth * si;
|
|
3758
4347
|
const activeRange = secondaryIdx.has(si) && secondaryRange ? secondaryRange : primaryRange;
|
|
3759
4348
|
const zeroY = valueToY2(0, activeRange, layout.plotTop, layout.plotBottom);
|
|
3760
4349
|
const valY = valueToY2(val2, activeRange, layout.plotTop, layout.plotBottom);
|
|
@@ -3768,7 +4357,7 @@ function buildBars(chartData, catCount, layout, primaryRange, secondaryRange, se
|
|
|
3768
4357
|
h,
|
|
3769
4358
|
fill: seriesColor2(series[si], si, palette),
|
|
3770
4359
|
rx: 1,
|
|
3771
|
-
part: { role: "dataPoint", seriesIndex: si, pointIndex:
|
|
4360
|
+
part: { role: "dataPoint", seriesIndex: si, pointIndex: sourceIndex }
|
|
3772
4361
|
});
|
|
3773
4362
|
if (showLabels) {
|
|
3774
4363
|
dataLabels.push({
|
|
@@ -3786,7 +4375,11 @@ function buildBars(chartData, catCount, layout, primaryRange, secondaryRange, se
|
|
|
3786
4375
|
return { primitives, dataLabels };
|
|
3787
4376
|
}
|
|
3788
4377
|
if (grouping === "stacked") {
|
|
3789
|
-
const
|
|
4378
|
+
const displaySeries2 = series.map((entry) => ({
|
|
4379
|
+
...entry,
|
|
4380
|
+
values: sourceIndices2.map((sourceIndex) => entry.values[sourceIndex] ?? 0)
|
|
4381
|
+
}));
|
|
4382
|
+
const rects = computeStackedBarRects(displaySeries2, catCount, layout, primaryRange, palette);
|
|
3790
4383
|
for (const r of rects) {
|
|
3791
4384
|
primitives.push({
|
|
3792
4385
|
kind: "rect",
|
|
@@ -3796,24 +4389,33 @@ function buildBars(chartData, catCount, layout, primaryRange, secondaryRange, se
|
|
|
3796
4389
|
h: r.h,
|
|
3797
4390
|
fill: r.fill,
|
|
3798
4391
|
rx: 1,
|
|
3799
|
-
part: r.seriesIndex !== void 0 && r.pointIndex !== void 0 ? {
|
|
4392
|
+
part: r.seriesIndex !== void 0 && r.pointIndex !== void 0 ? {
|
|
4393
|
+
role: "dataPoint",
|
|
4394
|
+
seriesIndex: r.seriesIndex,
|
|
4395
|
+
pointIndex: sourceIndices2[r.pointIndex] ?? r.pointIndex
|
|
4396
|
+
} : void 0
|
|
3800
4397
|
});
|
|
3801
4398
|
}
|
|
3802
4399
|
if (showLabels) {
|
|
3803
|
-
pushClusteredStackedLabels(series, catCount, layout, primaryRange, dataLabels);
|
|
4400
|
+
pushClusteredStackedLabels(series, sourceIndices2, catCount, layout, primaryRange, dataLabels);
|
|
3804
4401
|
}
|
|
3805
4402
|
return { primitives, dataLabels };
|
|
3806
4403
|
}
|
|
3807
4404
|
const barGroupWidth = layout.plotWidth / Math.max(catCount, 1);
|
|
3808
4405
|
const barW = barGroupWidth * 0.6;
|
|
3809
4406
|
const barOffset = (barGroupWidth - barW) / 2;
|
|
3810
|
-
const
|
|
4407
|
+
const displaySeries = series.map((entry) => ({
|
|
4408
|
+
...entry,
|
|
4409
|
+
values: sourceIndices2.map((sourceIndex) => entry.values[sourceIndex] ?? 0)
|
|
4410
|
+
}));
|
|
4411
|
+
const totals = categoryTotals(displaySeries, catCount);
|
|
3811
4412
|
for (let ci = 0; ci < catCount; ci++) {
|
|
3812
4413
|
let posRunning = 0;
|
|
3813
4414
|
let negRunning = 0;
|
|
3814
4415
|
const catTotal = totals[ci] || 1;
|
|
3815
4416
|
for (let si = 0; si < series.length; si++) {
|
|
3816
|
-
const
|
|
4417
|
+
const sourceIndex = sourceIndices2[ci] ?? ci;
|
|
4418
|
+
const rawVal = series[si].values[sourceIndex] ?? 0;
|
|
3817
4419
|
const val2 = catTotal > 0 ? rawVal / catTotal * 100 : 0;
|
|
3818
4420
|
const isNeg = val2 < 0;
|
|
3819
4421
|
const base = isNeg ? negRunning : posRunning;
|
|
@@ -3830,7 +4432,7 @@ function buildBars(chartData, catCount, layout, primaryRange, secondaryRange, se
|
|
|
3830
4432
|
w: barW,
|
|
3831
4433
|
h,
|
|
3832
4434
|
fill: seriesColor2(series[si], si, palette),
|
|
3833
|
-
part: { role: "dataPoint", seriesIndex: si, pointIndex:
|
|
4435
|
+
part: { role: "dataPoint", seriesIndex: si, pointIndex: sourceIndex }
|
|
3834
4436
|
});
|
|
3835
4437
|
if (showLabels && Math.abs(val2) > 0) {
|
|
3836
4438
|
dataLabels.push({
|
|
@@ -3853,14 +4455,15 @@ function buildBars(chartData, catCount, layout, primaryRange, secondaryRange, se
|
|
|
3853
4455
|
}
|
|
3854
4456
|
return { primitives, dataLabels };
|
|
3855
4457
|
}
|
|
3856
|
-
function pushClusteredStackedLabels(series, catCount, layout, range, dataLabels) {
|
|
4458
|
+
function pushClusteredStackedLabels(series, sourceIndices2, catCount, layout, range, dataLabels) {
|
|
3857
4459
|
const barGroupWidth = layout.plotWidth / catCount;
|
|
3858
4460
|
const seriesCount = Math.max(series.length, 1);
|
|
3859
4461
|
const singleBarWidth = barGroupWidth * 0.7 / seriesCount;
|
|
3860
4462
|
const groupOffset = (barGroupWidth - singleBarWidth * seriesCount) / 2;
|
|
3861
4463
|
for (let ci = 0; ci < catCount; ci++) {
|
|
4464
|
+
const sourceIndex = sourceIndices2[ci] ?? ci;
|
|
3862
4465
|
for (let si = 0; si < series.length; si++) {
|
|
3863
|
-
const val2 = series[si].values[
|
|
4466
|
+
const val2 = series[si].values[sourceIndex] ?? 0;
|
|
3864
4467
|
const x = layout.plotLeft + barGroupWidth * ci + groupOffset + singleBarWidth * si + singleBarWidth / 2;
|
|
3865
4468
|
const zeroY = valueToY2(0, range, layout.plotTop, layout.plotBottom);
|
|
3866
4469
|
const valY = valueToY2(val2, range, layout.plotTop, layout.plotBottom);
|
|
@@ -3877,7 +4480,7 @@ function pushClusteredStackedLabels(series, catCount, layout, range, dataLabels)
|
|
|
3877
4480
|
}
|
|
3878
4481
|
}
|
|
3879
4482
|
}
|
|
3880
|
-
function buildLines(chartData, catCount, layout, primaryRange, secondaryRange, secondaryIdx) {
|
|
4483
|
+
function buildLines(chartData, catCount, layout, primaryRange, secondaryRange, secondaryIdx, sourceIndices2, xPositions) {
|
|
3881
4484
|
const primitives = [];
|
|
3882
4485
|
const dataLabels = [];
|
|
3883
4486
|
const showLabels = chartData.style?.hasDataLabels;
|
|
@@ -3887,7 +4490,13 @@ function buildLines(chartData, catCount, layout, primaryRange, secondaryRange, s
|
|
|
3887
4490
|
continue;
|
|
3888
4491
|
}
|
|
3889
4492
|
const activeRange = secondaryIdx.has(si) && secondaryRange ? secondaryRange : primaryRange;
|
|
3890
|
-
const
|
|
4493
|
+
const displayValues = sourceIndices2.map((sourceIndex) => series.values[sourceIndex] ?? 0);
|
|
4494
|
+
const pts = computeLinePoints(displayValues, catCount, layout, activeRange).map(
|
|
4495
|
+
(point, index) => ({
|
|
4496
|
+
...point,
|
|
4497
|
+
x: xPositions?.[index] ?? point.x
|
|
4498
|
+
})
|
|
4499
|
+
);
|
|
3891
4500
|
const c = seriesColor2(series, si, chartData.colorPalette);
|
|
3892
4501
|
primitives.push({
|
|
3893
4502
|
kind: "polyline",
|
|
@@ -3897,19 +4506,20 @@ function buildLines(chartData, catCount, layout, primaryRange, secondaryRange, s
|
|
|
3897
4506
|
fill: "none",
|
|
3898
4507
|
part: { role: "series", seriesIndex: si }
|
|
3899
4508
|
});
|
|
3900
|
-
pts.forEach((pt,
|
|
4509
|
+
pts.forEach((pt, displayIndex) => {
|
|
4510
|
+
const sourceIndex = sourceIndices2[displayIndex] ?? displayIndex;
|
|
3901
4511
|
primitives.push({
|
|
3902
4512
|
kind: "circle",
|
|
3903
4513
|
cx: pt.x,
|
|
3904
4514
|
cy: pt.y,
|
|
3905
4515
|
r: 2.5,
|
|
3906
4516
|
fill: c,
|
|
3907
|
-
part: { role: "dataPoint", seriesIndex: si, pointIndex:
|
|
4517
|
+
part: { role: "dataPoint", seriesIndex: si, pointIndex: sourceIndex }
|
|
3908
4518
|
});
|
|
3909
4519
|
});
|
|
3910
4520
|
if (showLabels) {
|
|
3911
|
-
|
|
3912
|
-
const pt = pts[
|
|
4521
|
+
displayValues.forEach((val2, displayIndex) => {
|
|
4522
|
+
const pt = pts[displayIndex];
|
|
3913
4523
|
if (!pt) {
|
|
3914
4524
|
return;
|
|
3915
4525
|
}
|
|
@@ -3927,7 +4537,7 @@ function buildLines(chartData, catCount, layout, primaryRange, secondaryRange, s
|
|
|
3927
4537
|
}
|
|
3928
4538
|
return { primitives, dataLabels };
|
|
3929
4539
|
}
|
|
3930
|
-
function buildAreas(chartData, catCount, layout, range) {
|
|
4540
|
+
function buildAreas(chartData, catCount, layout, range, sourceIndices2, xPositions) {
|
|
3931
4541
|
const primitives = [];
|
|
3932
4542
|
const dataLabels = [];
|
|
3933
4543
|
const showLabels = chartData.style?.hasDataLabels;
|
|
@@ -3937,7 +4547,11 @@ function buildAreas(chartData, catCount, layout, range) {
|
|
|
3937
4547
|
if (series.values.length === 0) {
|
|
3938
4548
|
continue;
|
|
3939
4549
|
}
|
|
3940
|
-
const
|
|
4550
|
+
const displayValues = sourceIndices2.map((sourceIndex) => series.values[sourceIndex] ?? 0);
|
|
4551
|
+
const pts = computeLinePoints(displayValues, catCount, layout, range).map((point, index) => ({
|
|
4552
|
+
...point,
|
|
4553
|
+
x: xPositions?.[index] ?? point.x
|
|
4554
|
+
}));
|
|
3941
4555
|
const c = seriesColor2(series, si, chartData.colorPalette);
|
|
3942
4556
|
const lineStr = linePointsToSvgString(pts);
|
|
3943
4557
|
const firstPt = pts[0];
|
|
@@ -3945,7 +4559,7 @@ function buildAreas(chartData, catCount, layout, range) {
|
|
|
3945
4559
|
if (firstPt && lastPt) {
|
|
3946
4560
|
primitives.push({
|
|
3947
4561
|
kind: "polyline",
|
|
3948
|
-
points: `${
|
|
4562
|
+
points: `${firstPt.x.toFixed(2)},${baselineY.toFixed(2)} ${lineStr} ${lastPt.x.toFixed(2)},${baselineY.toFixed(2)}`,
|
|
3949
4563
|
stroke: "none",
|
|
3950
4564
|
strokeWidth: 0,
|
|
3951
4565
|
fill: c,
|
|
@@ -3961,9 +4575,23 @@ function buildAreas(chartData, catCount, layout, range) {
|
|
|
3961
4575
|
fill: "none",
|
|
3962
4576
|
part: { role: "series", seriesIndex: si }
|
|
3963
4577
|
});
|
|
4578
|
+
pts.forEach((pt, displayIndex) => {
|
|
4579
|
+
primitives.push({
|
|
4580
|
+
kind: "circle",
|
|
4581
|
+
cx: pt.x,
|
|
4582
|
+
cy: pt.y,
|
|
4583
|
+
r: 2,
|
|
4584
|
+
fill: c,
|
|
4585
|
+
part: {
|
|
4586
|
+
role: "dataPoint",
|
|
4587
|
+
seriesIndex: si,
|
|
4588
|
+
pointIndex: sourceIndices2[displayIndex] ?? displayIndex
|
|
4589
|
+
}
|
|
4590
|
+
});
|
|
4591
|
+
});
|
|
3964
4592
|
if (showLabels) {
|
|
3965
|
-
|
|
3966
|
-
const pt = pts[
|
|
4593
|
+
displayValues.forEach((val2, displayIndex) => {
|
|
4594
|
+
const pt = pts[displayIndex];
|
|
3967
4595
|
if (!pt) {
|
|
3968
4596
|
return;
|
|
3969
4597
|
}
|
|
@@ -3987,10 +4615,11 @@ function buildScatter(chartData, layout, range) {
|
|
|
3987
4615
|
const showLabels = chartData.style?.hasDataLabels;
|
|
3988
4616
|
const allIndices = chartData.series.flatMap((s) => s.values.map((_, i) => i));
|
|
3989
4617
|
const maxXIndex = Math.max(1, ...allIndices);
|
|
4618
|
+
const xValues = chartData.categories.map(Number);
|
|
3990
4619
|
for (let si = 0; si < chartData.series.length; si++) {
|
|
3991
4620
|
const series = chartData.series[si];
|
|
3992
4621
|
const c = seriesColor2(series, si, chartData.colorPalette);
|
|
3993
|
-
const dots = computeScatterDots(series.values, maxXIndex, layout, range);
|
|
4622
|
+
const dots = computeScatterDots(series.values, maxXIndex, layout, range, xValues);
|
|
3994
4623
|
dots.forEach((dot, vi) => {
|
|
3995
4624
|
primitives.push({
|
|
3996
4625
|
kind: "circle",
|
|
@@ -4028,6 +4657,7 @@ function buildBubbles(chartData, layout, range) {
|
|
|
4028
4657
|
const showLabels = chartData.style?.hasDataLabels;
|
|
4029
4658
|
const allIndices = chartData.series.flatMap((s) => s.values.map((_, i) => i));
|
|
4030
4659
|
const maxXIndex = Math.max(1, ...allIndices);
|
|
4660
|
+
const xValues = chartData.categories.map(Number);
|
|
4031
4661
|
const sizeSeries = chartData.series.length >= 3 ? chartData.series[2] : void 0;
|
|
4032
4662
|
const maxBubble = sizeSeries ? Math.max(1, ...sizeSeries.values.map((v) => Math.abs(v))) : 1;
|
|
4033
4663
|
const medianRadius = Math.min(layout.plotWidth, layout.plotHeight) * 0.04;
|
|
@@ -4035,7 +4665,7 @@ function buildBubbles(chartData, layout, range) {
|
|
|
4035
4665
|
for (let si = 0; si < pointSeries.length; si++) {
|
|
4036
4666
|
const series = pointSeries[si];
|
|
4037
4667
|
const c = seriesColor2(series, si, chartData.colorPalette);
|
|
4038
|
-
const dots = computeScatterDots(series.values, maxXIndex, layout, range);
|
|
4668
|
+
const dots = computeScatterDots(series.values, maxXIndex, layout, range, xValues);
|
|
4039
4669
|
dots.forEach((dot, vi) => {
|
|
4040
4670
|
const r = computeBubbleRadius(sizeSeries?.values[vi], maxBubble, medianRadius);
|
|
4041
4671
|
primitives.push({
|
|
@@ -4068,12 +4698,527 @@ function buildBubbles(chartData, layout, range) {
|
|
|
4068
4698
|
}
|
|
4069
4699
|
return { primitives, dataLabels };
|
|
4070
4700
|
}
|
|
4071
|
-
function
|
|
4072
|
-
if (
|
|
4073
|
-
|
|
4074
|
-
return layout.plotLeft + slotWidth * xVal + slotWidth / 2;
|
|
4701
|
+
function categoryX(displayIndex, count, layout, spacing) {
|
|
4702
|
+
if (spacing === "bar") {
|
|
4703
|
+
return layout.plotLeft + layout.plotWidth / Math.max(count, 1) * (displayIndex + 0.5);
|
|
4075
4704
|
}
|
|
4076
|
-
|
|
4705
|
+
return count > 1 ? layout.plotLeft + layout.plotWidth / (count - 1) * displayIndex : layout.plotLeft + layout.plotWidth / 2;
|
|
4706
|
+
}
|
|
4707
|
+
function groupedLabels(values) {
|
|
4708
|
+
const groups = [];
|
|
4709
|
+
values.forEach((text, index) => {
|
|
4710
|
+
const previous = groups[groups.length - 1];
|
|
4711
|
+
if (previous?.text === text) {
|
|
4712
|
+
previous.end = index;
|
|
4713
|
+
} else {
|
|
4714
|
+
groups.push({ text, start: index, end: index });
|
|
4715
|
+
}
|
|
4716
|
+
});
|
|
4717
|
+
return groups;
|
|
4718
|
+
}
|
|
4719
|
+
function buildMultiLevelCategoryLabels(categoryLabels, categoryLevels, sourceIndices2, layout, spacing, axis, labelY, labelsAbove, offset) {
|
|
4720
|
+
const sourceLevels = categoryLevels?.length ? categoryLevels : [categoryLabels];
|
|
4721
|
+
const levels = axis?.noMultiLevelLabels ? sourceLevels.slice(0, 1) : sourceLevels;
|
|
4722
|
+
const skip = Math.max(1, axis?.tickLabelSkip ?? 1);
|
|
4723
|
+
const textAnchor = axis?.labelAlignment === "l" ? "start" : axis?.labelAlignment === "r" ? "end" : "middle";
|
|
4724
|
+
const direction = labelsAbove ? -1 : 1;
|
|
4725
|
+
const bandHeight = Math.max(axis?.fontSize ?? 8, 8) + 4;
|
|
4726
|
+
return levels.flatMap((level, levelIndex) => {
|
|
4727
|
+
const values = sourceIndices2.map((sourceIndex) => level[sourceIndex] ?? "");
|
|
4728
|
+
return groupedLabels(values).flatMap((group) => {
|
|
4729
|
+
if (!group.text || group.start % skip !== 0) {
|
|
4730
|
+
return [];
|
|
4731
|
+
}
|
|
4732
|
+
const startX = categoryX(group.start, sourceIndices2.length, layout, spacing);
|
|
4733
|
+
const endX = categoryX(group.end, sourceIndices2.length, layout, spacing);
|
|
4734
|
+
return [
|
|
4735
|
+
{
|
|
4736
|
+
kind: "text",
|
|
4737
|
+
x: (startX + endX) / 2,
|
|
4738
|
+
y: labelY + direction * (offset + levelIndex * bandHeight),
|
|
4739
|
+
text: group.text,
|
|
4740
|
+
textAnchor,
|
|
4741
|
+
...chartAxisTextStyle(axis)
|
|
4742
|
+
}
|
|
4743
|
+
];
|
|
4744
|
+
});
|
|
4745
|
+
});
|
|
4746
|
+
}
|
|
4747
|
+
function primaryCategoryAxis(axes) {
|
|
4748
|
+
const categoryAxes = axes?.filter(
|
|
4749
|
+
(axis) => axis.axisType === "catAx" || axis.axisType === "dateAx"
|
|
4750
|
+
);
|
|
4751
|
+
return categoryAxes?.find((axis) => axis.axPos !== "t") ?? categoryAxes?.[0];
|
|
4752
|
+
}
|
|
4753
|
+
function tickLine(x, y, placement, topAxis, length, axis) {
|
|
4754
|
+
if (!placement || placement === "none") {
|
|
4755
|
+
return void 0;
|
|
4756
|
+
}
|
|
4757
|
+
const inward = topAxis ? length : -length;
|
|
4758
|
+
const outward = -inward;
|
|
4759
|
+
const [start, end] = placement === "cross" ? [-length, length] : placement === "in" ? [0, inward] : [0, outward];
|
|
4760
|
+
return {
|
|
4761
|
+
kind: "line",
|
|
4762
|
+
x1: x,
|
|
4763
|
+
y1: y + start,
|
|
4764
|
+
x2: x,
|
|
4765
|
+
y2: y + end,
|
|
4766
|
+
...chartLineStyle(axis.spPr)
|
|
4767
|
+
};
|
|
4768
|
+
}
|
|
4769
|
+
function buildTickMarks(axis, sourceCount, layout, spacing, axisY) {
|
|
4770
|
+
if (!axis || axis.deleted) {
|
|
4771
|
+
return [];
|
|
4772
|
+
}
|
|
4773
|
+
const result = [];
|
|
4774
|
+
const topAxis = axis.axPos === "t";
|
|
4775
|
+
const y = axisY ?? (topAxis ? layout.plotTop : layout.plotBottom);
|
|
4776
|
+
if (axis.spPr) {
|
|
4777
|
+
result.push({
|
|
4778
|
+
kind: "line",
|
|
4779
|
+
x1: layout.plotLeft,
|
|
4780
|
+
y1: y,
|
|
4781
|
+
x2: layout.plotRight,
|
|
4782
|
+
y2: y,
|
|
4783
|
+
...chartLineStyle(axis.spPr)
|
|
4784
|
+
});
|
|
4785
|
+
}
|
|
4786
|
+
const majorSkip = Math.max(1, axis.tickMarkSkip ?? 1);
|
|
4787
|
+
for (let displayIndex = 0; displayIndex < sourceCount; displayIndex += majorSkip) {
|
|
4788
|
+
const x = categoryX(displayIndex, sourceCount, layout, spacing);
|
|
4789
|
+
const major = tickLine(x, y, axis.majorTickMark, topAxis, 4, axis);
|
|
4790
|
+
if (major) {
|
|
4791
|
+
result.push(major);
|
|
4792
|
+
}
|
|
4793
|
+
if (axis.minorTickMark !== void 0 && displayIndex + majorSkip < sourceCount) {
|
|
4794
|
+
const nextX = categoryX(displayIndex + majorSkip, sourceCount, layout, spacing);
|
|
4795
|
+
const minor = tickLine((x + nextX) / 2, y, axis.minorTickMark, topAxis, 2.5, axis);
|
|
4796
|
+
if (minor) {
|
|
4797
|
+
result.push(minor);
|
|
4798
|
+
}
|
|
4799
|
+
}
|
|
4800
|
+
}
|
|
4801
|
+
return result;
|
|
4802
|
+
}
|
|
4803
|
+
function buildCategoryAxisPlan(categoryLabels, layout, spacing, axes, selectedAxis, axisY, categoryLevels) {
|
|
4804
|
+
const axis = selectedAxis ?? primaryCategoryAxis(axes);
|
|
4805
|
+
const sourceIndices2 = categoryLabels.map((_label, index) => index);
|
|
4806
|
+
if (axis?.orientation === "maxMin") {
|
|
4807
|
+
sourceIndices2.reverse();
|
|
4808
|
+
}
|
|
4809
|
+
if (axis?.deleted || axis?.tickLblPos === "none") {
|
|
4810
|
+
return {
|
|
4811
|
+
axis,
|
|
4812
|
+
sourceIndices: sourceIndices2,
|
|
4813
|
+
labels: [],
|
|
4814
|
+
tickMarks: buildTickMarks(axis, sourceIndices2.length, layout, spacing, axisY)
|
|
4815
|
+
};
|
|
4816
|
+
}
|
|
4817
|
+
const topAxis = axis?.axPos === "t";
|
|
4818
|
+
const high = axis?.tickLblPos === "high";
|
|
4819
|
+
const low = axis?.tickLblPos === "low";
|
|
4820
|
+
const labelsAbove = high || !low && topAxis;
|
|
4821
|
+
const offset = 4 + 8 * ((axis?.labelOffset ?? 100) / 100);
|
|
4822
|
+
const labelY = high ? layout.plotTop : low ? layout.plotBottom : axisY ?? (topAxis ? layout.plotTop : layout.plotBottom);
|
|
4823
|
+
const labels = buildMultiLevelCategoryLabels(
|
|
4824
|
+
categoryLabels,
|
|
4825
|
+
categoryLevels,
|
|
4826
|
+
sourceIndices2,
|
|
4827
|
+
layout,
|
|
4828
|
+
spacing,
|
|
4829
|
+
axis,
|
|
4830
|
+
labelY,
|
|
4831
|
+
labelsAbove,
|
|
4832
|
+
offset
|
|
4833
|
+
);
|
|
4834
|
+
return {
|
|
4835
|
+
axis,
|
|
4836
|
+
sourceIndices: sourceIndices2,
|
|
4837
|
+
labels,
|
|
4838
|
+
tickMarks: buildTickMarks(axis, sourceIndices2.length, layout, spacing, axisY)
|
|
4839
|
+
};
|
|
4840
|
+
}
|
|
4841
|
+
function chartDataInCategoryOrder(chartData, sourceIndices2) {
|
|
4842
|
+
if (sourceIndices2.every((sourceIndex, displayIndex) => sourceIndex === displayIndex)) {
|
|
4843
|
+
return chartData;
|
|
4844
|
+
}
|
|
4845
|
+
return {
|
|
4846
|
+
...chartData,
|
|
4847
|
+
categories: sourceIndices2.map((sourceIndex) => chartData.categories[sourceIndex] ?? ""),
|
|
4848
|
+
categoryLevels: chartData.categoryLevels?.map(
|
|
4849
|
+
(level) => sourceIndices2.map((sourceIndex) => level[sourceIndex] ?? "")
|
|
4850
|
+
),
|
|
4851
|
+
series: chartData.series.map((series) => ({
|
|
4852
|
+
...series,
|
|
4853
|
+
values: sourceIndices2.map((sourceIndex) => series.values[sourceIndex] ?? 0),
|
|
4854
|
+
errBars: series.errBars?.map((errorBars) => ({
|
|
4855
|
+
...errorBars,
|
|
4856
|
+
customPlus: errorBars.customPlus ? sourceIndices2.map((sourceIndex) => errorBars.customPlus?.[sourceIndex] ?? 0) : void 0,
|
|
4857
|
+
customMinus: errorBars.customMinus ? sourceIndices2.map((sourceIndex) => errorBars.customMinus?.[sourceIndex] ?? 0) : void 0
|
|
4858
|
+
}))
|
|
4859
|
+
}))
|
|
4860
|
+
};
|
|
4861
|
+
}
|
|
4862
|
+
var DAY_MS = 864e5;
|
|
4863
|
+
function excelSerialToDate(serial, date1904 = false) {
|
|
4864
|
+
const epoch = date1904 ? Date.UTC(1904, 0, 1) : Date.UTC(1899, 11, serial < 60 ? 31 : 30);
|
|
4865
|
+
return new Date(epoch + serial * DAY_MS);
|
|
4866
|
+
}
|
|
4867
|
+
function primaryDateAxis(chartData) {
|
|
4868
|
+
return chartData.axes?.find((axis) => axis.axisType === "dateAx" && axis.axPos !== "t") ?? chartData.axes?.find((axis) => axis.axisType === "dateAx");
|
|
4869
|
+
}
|
|
4870
|
+
function serialToX(serial, min, max, layout) {
|
|
4871
|
+
const ratio = max === min ? 0.5 : (serial - min) / (max - min);
|
|
4872
|
+
return layout.plotLeft + ratio * layout.plotWidth;
|
|
4873
|
+
}
|
|
4874
|
+
function addUnit(date, amount, unit) {
|
|
4875
|
+
const next = new Date(date);
|
|
4876
|
+
if (unit === "days") {
|
|
4877
|
+
next.setUTCDate(next.getUTCDate() + amount);
|
|
4878
|
+
} else if (unit === "months") {
|
|
4879
|
+
next.setUTCMonth(next.getUTCMonth() + amount);
|
|
4880
|
+
} else {
|
|
4881
|
+
next.setUTCFullYear(next.getUTCFullYear() + amount);
|
|
4882
|
+
}
|
|
4883
|
+
return next;
|
|
4884
|
+
}
|
|
4885
|
+
function formatDate(date, unit, format) {
|
|
4886
|
+
const month = date.toLocaleString("en-US", { month: "short", timeZone: "UTC" });
|
|
4887
|
+
const year = String(date.getUTCFullYear());
|
|
4888
|
+
const day = String(date.getUTCDate());
|
|
4889
|
+
if (format?.toLowerCase().includes("yyyy")) {
|
|
4890
|
+
return format.toLowerCase().includes("d") ? `${day} ${month} ${year}` : `${month} ${year}`;
|
|
4891
|
+
}
|
|
4892
|
+
if (unit === "years") {
|
|
4893
|
+
return year;
|
|
4894
|
+
}
|
|
4895
|
+
if (unit === "months") {
|
|
4896
|
+
return `${month} ${year}`;
|
|
4897
|
+
}
|
|
4898
|
+
return `${day} ${month}`;
|
|
4899
|
+
}
|
|
4900
|
+
function tickLine2(x, y, axis, minor) {
|
|
4901
|
+
const placement = minor ? axis.minorTickMark : axis.majorTickMark;
|
|
4902
|
+
if (!placement || placement === "none") {
|
|
4903
|
+
return void 0;
|
|
4904
|
+
}
|
|
4905
|
+
const length = minor ? 2.5 : 4;
|
|
4906
|
+
const direction = axis.axPos === "t" ? 1 : -1;
|
|
4907
|
+
const inward = direction * length;
|
|
4908
|
+
const outward = -inward;
|
|
4909
|
+
const [start, end] = placement === "cross" ? [-length, length] : placement === "in" ? [0, inward] : [0, outward];
|
|
4910
|
+
return {
|
|
4911
|
+
kind: "line",
|
|
4912
|
+
x1: x,
|
|
4913
|
+
y1: y + start,
|
|
4914
|
+
x2: x,
|
|
4915
|
+
y2: y + end,
|
|
4916
|
+
...chartLineStyle(axis.spPr)
|
|
4917
|
+
};
|
|
4918
|
+
}
|
|
4919
|
+
function calendarTicks(min, max, date1904, unit, step) {
|
|
4920
|
+
const result = [];
|
|
4921
|
+
let date = excelSerialToDate(min, date1904);
|
|
4922
|
+
for (let guard = 0; guard < 1e3 && date.getTime() <= excelSerialToDate(max, date1904).getTime(); guard++) {
|
|
4923
|
+
result.push(min + (date.getTime() - excelSerialToDate(min, date1904).getTime()) / DAY_MS);
|
|
4924
|
+
date = addUnit(date, step, unit);
|
|
4925
|
+
}
|
|
4926
|
+
return result;
|
|
4927
|
+
}
|
|
4928
|
+
function buildDateAxisPlan(chartData, layout, selectedAxis, axisY) {
|
|
4929
|
+
const dates = chartData.dateCategories;
|
|
4930
|
+
const axis = selectedAxis ?? primaryDateAxis(chartData);
|
|
4931
|
+
if (!dates || !axis || dates.values.length === 0) {
|
|
4932
|
+
return void 0;
|
|
4933
|
+
}
|
|
4934
|
+
const indexed = dates.values.map((value, index) => ({ value, index })).sort((a, b) => a.value - b.value);
|
|
4935
|
+
const dataMin = indexed[0]?.value ?? 0;
|
|
4936
|
+
const dataMax = indexed[indexed.length - 1]?.value ?? dataMin;
|
|
4937
|
+
const min = axis.min ?? dataMin;
|
|
4938
|
+
const max = axis.max ?? dataMax;
|
|
4939
|
+
const visible = indexed.filter(({ value }) => value >= min && value <= max);
|
|
4940
|
+
if (axis.orientation === "maxMin") {
|
|
4941
|
+
visible.reverse();
|
|
4942
|
+
}
|
|
4943
|
+
const sourceIndices2 = visible.map(({ index }) => index);
|
|
4944
|
+
const xPositions = visible.map(({ value }) => {
|
|
4945
|
+
const x = serialToX(value, min, max, layout);
|
|
4946
|
+
return axis.orientation === "maxMin" ? layout.plotLeft + layout.plotWidth - (x - layout.plotLeft) : x;
|
|
4947
|
+
});
|
|
4948
|
+
const unit = axis.majorTimeUnit ?? axis.baseTimeUnit ?? "days";
|
|
4949
|
+
const ticks = calendarTicks(
|
|
4950
|
+
min,
|
|
4951
|
+
max,
|
|
4952
|
+
dates.date1904 ?? false,
|
|
4953
|
+
unit,
|
|
4954
|
+
Math.max(1, axis.majorUnit ?? 1)
|
|
4955
|
+
);
|
|
4956
|
+
const y = axisY ?? (axis.axPos === "t" ? layout.plotTop : layout.plotBottom);
|
|
4957
|
+
const offset = 4 + 8 * ((axis.labelOffset ?? 100) / 100);
|
|
4958
|
+
const labelsAbove = axis.tickLblPos === "high" || axis.tickLblPos !== "low" && axis.axPos === "t";
|
|
4959
|
+
const labelY = axis.tickLblPos === "high" ? layout.plotTop : axis.tickLblPos === "low" ? layout.plotBottom : y;
|
|
4960
|
+
const labelSkip = Math.max(1, axis.tickLabelSkip ?? 1);
|
|
4961
|
+
const labels = axis.deleted || axis.tickLblPos === "none" ? [] : ticks.flatMap((serial, index) => {
|
|
4962
|
+
if (index % labelSkip !== 0) {
|
|
4963
|
+
return [];
|
|
4964
|
+
}
|
|
4965
|
+
let x = serialToX(serial, min, max, layout);
|
|
4966
|
+
if (axis.orientation === "maxMin") {
|
|
4967
|
+
x = layout.plotLeft + layout.plotWidth - (x - layout.plotLeft);
|
|
4968
|
+
}
|
|
4969
|
+
return [
|
|
4970
|
+
{
|
|
4971
|
+
kind: "text",
|
|
4972
|
+
x,
|
|
4973
|
+
y: labelsAbove ? labelY - offset : labelY + offset,
|
|
4974
|
+
text: formatDate(
|
|
4975
|
+
excelSerialToDate(serial, dates.date1904),
|
|
4976
|
+
unit,
|
|
4977
|
+
axis.numFmt?.formatCode ?? dates.formatCode
|
|
4978
|
+
),
|
|
4979
|
+
...chartAxisTextStyle(axis),
|
|
4980
|
+
textAnchor: "middle"
|
|
4981
|
+
}
|
|
4982
|
+
];
|
|
4983
|
+
});
|
|
4984
|
+
const tickMarks = axis.deleted ? [] : ticks.flatMap((serial, index) => {
|
|
4985
|
+
if (index % Math.max(1, axis.tickMarkSkip ?? 1) !== 0) {
|
|
4986
|
+
return [];
|
|
4987
|
+
}
|
|
4988
|
+
let x = serialToX(serial, min, max, layout);
|
|
4989
|
+
if (axis.orientation === "maxMin") {
|
|
4990
|
+
x = layout.plotLeft + layout.plotWidth - (x - layout.plotLeft);
|
|
4991
|
+
}
|
|
4992
|
+
const line2 = tickLine2(x, y, axis, false);
|
|
4993
|
+
return line2 ? [line2] : [];
|
|
4994
|
+
});
|
|
4995
|
+
if (!axis.deleted && axis.spPr) {
|
|
4996
|
+
tickMarks.unshift({
|
|
4997
|
+
kind: "line",
|
|
4998
|
+
x1: layout.plotLeft,
|
|
4999
|
+
y1: y,
|
|
5000
|
+
x2: layout.plotRight,
|
|
5001
|
+
y2: y,
|
|
5002
|
+
...chartLineStyle(axis.spPr)
|
|
5003
|
+
});
|
|
5004
|
+
}
|
|
5005
|
+
if (!axis.deleted && axis.minorTickMark && axis.minorTickMark !== "none") {
|
|
5006
|
+
const minorUnit = axis.minorTimeUnit ?? axis.baseTimeUnit ?? unit;
|
|
5007
|
+
for (const serial of calendarTicks(
|
|
5008
|
+
min,
|
|
5009
|
+
max,
|
|
5010
|
+
dates.date1904 ?? false,
|
|
5011
|
+
minorUnit,
|
|
5012
|
+
Math.max(1, axis.minorUnit ?? 1)
|
|
5013
|
+
)) {
|
|
5014
|
+
let x = serialToX(serial, min, max, layout);
|
|
5015
|
+
if (axis.orientation === "maxMin") {
|
|
5016
|
+
x = layout.plotLeft + layout.plotWidth - (x - layout.plotLeft);
|
|
5017
|
+
}
|
|
5018
|
+
const line2 = tickLine2(x, y, axis, true);
|
|
5019
|
+
if (line2) {
|
|
5020
|
+
tickMarks.push(line2);
|
|
5021
|
+
}
|
|
5022
|
+
}
|
|
5023
|
+
}
|
|
5024
|
+
return { sourceIndices: sourceIndices2, xPositions, labels, tickMarks };
|
|
5025
|
+
}
|
|
5026
|
+
function buildCartesianHorizontalAxis(chartData, categoryLabels, layout, kind, primaryRange, secondaryRange) {
|
|
5027
|
+
let catAxisStyle = kind === "line" || kind === "area" || kind === "scatter" || kind === "bubble" ? "line" : "bar";
|
|
5028
|
+
const horizontalAxes = chartData.axes?.filter(
|
|
5029
|
+
(axis) => axis.axisType === "catAx" || axis.axisType === "dateAx"
|
|
5030
|
+
);
|
|
5031
|
+
const primaryAxis = horizontalAxes?.find((axis) => axis.axPos !== "t") ?? horizontalAxes?.[0];
|
|
5032
|
+
const secondaryAxis = horizontalAxes?.find((axis) => axis.axPos === "t" && axis !== primaryAxis);
|
|
5033
|
+
const linkedValueAxis = (axis) => chartData.axes?.find(
|
|
5034
|
+
(candidate) => candidate.axisType === "valAx" && candidate.axisId === axis?.crossAxisId
|
|
5035
|
+
);
|
|
5036
|
+
const primaryValueAxis = linkedValueAxis(primaryAxis);
|
|
5037
|
+
if (primaryValueAxis?.crossBetween === "between") {
|
|
5038
|
+
catAxisStyle = "bar";
|
|
5039
|
+
} else if (primaryValueAxis?.crossBetween === "midCat") {
|
|
5040
|
+
catAxisStyle = "line";
|
|
5041
|
+
}
|
|
5042
|
+
const rangeAndY = (axis) => {
|
|
5043
|
+
const valueAxis = linkedValueAxis(axis);
|
|
5044
|
+
const range = valueAxis?.axPos === "r" && secondaryRange ? secondaryRange : primaryRange;
|
|
5045
|
+
return horizontalAxisY(valueAxis, range, layout, axis?.axPos === "t" ? "top" : "bottom");
|
|
5046
|
+
};
|
|
5047
|
+
const supportsDateScale = kind === "line" || kind === "area" || kind === "combo" || kind === "stock";
|
|
5048
|
+
const datePlan = supportsDateScale && primaryAxis?.axisType === "dateAx" ? buildDateAxisPlan(chartData, layout, primaryAxis, rangeAndY(primaryAxis)) : void 0;
|
|
5049
|
+
const categoryPlan = kind === "scatter" || kind === "bubble" || datePlan ? void 0 : buildCategoryAxisPlan(
|
|
5050
|
+
categoryLabels,
|
|
5051
|
+
layout,
|
|
5052
|
+
catAxisStyle,
|
|
5053
|
+
chartData.axes,
|
|
5054
|
+
primaryAxis,
|
|
5055
|
+
rangeAndY(primaryAxis),
|
|
5056
|
+
chartData.categoryLevels
|
|
5057
|
+
);
|
|
5058
|
+
const secondaryPlan = secondaryAxis ? secondaryAxis.axisType === "dateAx" ? buildDateAxisPlan(chartData, layout, secondaryAxis, rangeAndY(secondaryAxis)) : buildCategoryAxisPlan(
|
|
5059
|
+
categoryLabels,
|
|
5060
|
+
layout,
|
|
5061
|
+
linkedValueAxis(secondaryAxis)?.crossBetween === "between" ? "bar" : "line",
|
|
5062
|
+
chartData.axes,
|
|
5063
|
+
secondaryAxis,
|
|
5064
|
+
rangeAndY(secondaryAxis),
|
|
5065
|
+
chartData.categoryLevels
|
|
5066
|
+
) : void 0;
|
|
5067
|
+
const sourceIndices2 = datePlan?.sourceIndices ?? categoryPlan?.sourceIndices ?? categoryLabels.map((_label, index) => index);
|
|
5068
|
+
return {
|
|
5069
|
+
catAxisStyle,
|
|
5070
|
+
sourceIndices: sourceIndices2,
|
|
5071
|
+
xPositions: datePlan?.xPositions ?? categoryPlan?.sourceIndices.map(
|
|
5072
|
+
(_sourceIndex, displayIndex) => categoryX(displayIndex, categoryPlan.sourceIndices.length, layout, catAxisStyle)
|
|
5073
|
+
),
|
|
5074
|
+
labels: [
|
|
5075
|
+
...datePlan?.labels ?? categoryPlan?.labels ?? buildCategoryLabels(categoryLabels, layout, catAxisStyle),
|
|
5076
|
+
...secondaryPlan?.labels ?? []
|
|
5077
|
+
],
|
|
5078
|
+
tickMarks: [
|
|
5079
|
+
...datePlan?.tickMarks ?? categoryPlan?.tickMarks ?? [],
|
|
5080
|
+
...secondaryPlan?.tickMarks ?? []
|
|
5081
|
+
],
|
|
5082
|
+
displayChartData: datePlan || categoryPlan ? chartDataInCategoryOrder(chartData, sourceIndices2) : chartData
|
|
5083
|
+
};
|
|
5084
|
+
}
|
|
5085
|
+
var DEFAULT_COLOR3 = "#334155";
|
|
5086
|
+
var CAP_HALF_WIDTH = 4;
|
|
5087
|
+
function categoryX2(value, count, layout, mode) {
|
|
5088
|
+
if (mode === "bar") {
|
|
5089
|
+
const slot = layout.plotWidth / Math.max(count, 1);
|
|
5090
|
+
return layout.plotLeft + slot * value + slot / 2;
|
|
5091
|
+
}
|
|
5092
|
+
return layout.plotLeft + value / Math.max(count - 1, 1) * layout.plotWidth;
|
|
5093
|
+
}
|
|
5094
|
+
function positionedCategoryX(value, positions, fallback) {
|
|
5095
|
+
if (!positions || positions.length === 0) {
|
|
5096
|
+
return fallback(value);
|
|
5097
|
+
}
|
|
5098
|
+
const lower = Math.floor(value);
|
|
5099
|
+
const upper = Math.ceil(value);
|
|
5100
|
+
const at = (index) => {
|
|
5101
|
+
if (positions[index] !== void 0) {
|
|
5102
|
+
return positions[index];
|
|
5103
|
+
}
|
|
5104
|
+
const lastIndex = positions.length - 1;
|
|
5105
|
+
const last = positions[lastIndex] ?? 0;
|
|
5106
|
+
const beforeLast = lastIndex > 0 ? positions[lastIndex - 1] ?? positions[0] ?? 0 : positions[0] ?? 0;
|
|
5107
|
+
const gap = last - beforeLast;
|
|
5108
|
+
return last + gap * (index - positions.length + 1);
|
|
5109
|
+
};
|
|
5110
|
+
return at(lower) + (at(upper) - at(lower)) * (value - lower);
|
|
5111
|
+
}
|
|
5112
|
+
function numericX(value, range, layout) {
|
|
5113
|
+
return layout.plotLeft + (value - range.min) / Math.max(range.span, 1e-9) * layout.plotWidth;
|
|
5114
|
+
}
|
|
5115
|
+
function xValuesForSeries(chartData, series, indexes) {
|
|
5116
|
+
const seriesType = series.seriesChartType ?? chartData.chartType;
|
|
5117
|
+
const numeric = seriesType === "scatter" || seriesType === "bubble";
|
|
5118
|
+
if (!numeric) {
|
|
5119
|
+
return { values: indexes.map((_index, displayIndex) => displayIndex), numeric: false };
|
|
5120
|
+
}
|
|
5121
|
+
return {
|
|
5122
|
+
values: indexes.map((index) => {
|
|
5123
|
+
const parsed = Number(chartData.categories[index]);
|
|
5124
|
+
return Number.isFinite(parsed) ? parsed : index;
|
|
5125
|
+
}),
|
|
5126
|
+
numeric: true
|
|
5127
|
+
};
|
|
5128
|
+
}
|
|
5129
|
+
function numericXRange(values) {
|
|
5130
|
+
const min = Math.min(...values);
|
|
5131
|
+
const max = Math.max(...values);
|
|
5132
|
+
return { min, max, span: Math.max(max - min, 1) };
|
|
5133
|
+
}
|
|
5134
|
+
function errorValue(errBars, values, displayIndex, sourceIndex, direction) {
|
|
5135
|
+
switch (errBars.valType) {
|
|
5136
|
+
case "fixedVal":
|
|
5137
|
+
return errBars.val ?? 0;
|
|
5138
|
+
case "percentage":
|
|
5139
|
+
return Math.abs(values[displayIndex] ?? 0) * ((errBars.val ?? 0) / 100);
|
|
5140
|
+
case "stdDev": {
|
|
5141
|
+
const mean = values.reduce((sum, value) => sum + value, 0) / Math.max(values.length, 1);
|
|
5142
|
+
const variance = values.reduce((sum, value) => sum + (value - mean) ** 2, 0) / Math.max(values.length, 1);
|
|
5143
|
+
return Math.sqrt(variance) * (errBars.val ?? 1);
|
|
5144
|
+
}
|
|
5145
|
+
case "stdErr": {
|
|
5146
|
+
const count = Math.max(values.length, 1);
|
|
5147
|
+
const mean = values.reduce((sum, value) => sum + value, 0) / count;
|
|
5148
|
+
const variance = values.reduce((sum, value) => sum + (value - mean) ** 2, 0) / count;
|
|
5149
|
+
return Math.sqrt(variance / count);
|
|
5150
|
+
}
|
|
5151
|
+
case "cust":
|
|
5152
|
+
return direction === "plus" ? errBars.customPlus?.[sourceIndex] ?? 0 : errBars.customMinus?.[sourceIndex] ?? 0;
|
|
5153
|
+
}
|
|
5154
|
+
}
|
|
5155
|
+
function line(x1, y1, x2, y2, color) {
|
|
5156
|
+
return { kind: "line", x1, y1, x2, y2, stroke: color, strokeWidth: 1 };
|
|
5157
|
+
}
|
|
5158
|
+
function appendYBar(out, errBars, x, baseValue, error, direction, range, layout) {
|
|
5159
|
+
const color = errBars.color ?? DEFAULT_COLOR3;
|
|
5160
|
+
const baseY = valueToY2(baseValue, range, layout.plotTop, layout.plotBottom);
|
|
5161
|
+
const endValue = direction === "plus" ? baseValue + error : baseValue - error;
|
|
5162
|
+
const endY = valueToY2(endValue, range, layout.plotTop, layout.plotBottom);
|
|
5163
|
+
out.push(line(x, baseY, x, endY, color));
|
|
5164
|
+
if (!errBars.noEndCap) {
|
|
5165
|
+
out.push(line(x - CAP_HALF_WIDTH, endY, x + CAP_HALF_WIDTH, endY, color));
|
|
5166
|
+
}
|
|
5167
|
+
}
|
|
5168
|
+
function appendXBar(out, errBars, baseXValue, error, direction, y, toPixel) {
|
|
5169
|
+
const color = errBars.color ?? DEFAULT_COLOR3;
|
|
5170
|
+
const baseX = toPixel(baseXValue);
|
|
5171
|
+
const endValue = direction === "plus" ? baseXValue + error : baseXValue - error;
|
|
5172
|
+
const endX = toPixel(endValue);
|
|
5173
|
+
out.push(line(baseX, y, endX, y, color));
|
|
5174
|
+
if (!errBars.noEndCap) {
|
|
5175
|
+
out.push(line(endX, y - CAP_HALF_WIDTH, endX, y + CAP_HALF_WIDTH, color));
|
|
5176
|
+
}
|
|
5177
|
+
}
|
|
5178
|
+
function computeErrorBarPrimitives(chartData, catCount, layout, range, mode = "line", options = {}) {
|
|
5179
|
+
const out = [];
|
|
5180
|
+
chartData.series.forEach((series, seriesIndex) => {
|
|
5181
|
+
const indexes = options.sourceIndices ?? series.values.map((_value, index) => index);
|
|
5182
|
+
const yValues = indexes.map((index) => series.values[index] ?? 0);
|
|
5183
|
+
const xValues = xValuesForSeries(chartData, series, indexes);
|
|
5184
|
+
const xRange = xValues.numeric ? numericXRange(xValues.values) : void 0;
|
|
5185
|
+
const seriesMode = options.seriesModes?.[seriesIndex] ?? mode;
|
|
5186
|
+
const yRange = options.seriesRanges?.[seriesIndex] ?? range;
|
|
5187
|
+
series.errBars?.forEach((errBars) => {
|
|
5188
|
+
indexes.forEach((sourceIndex, displayIndex) => {
|
|
5189
|
+
const yValue = yValues[displayIndex] ?? 0;
|
|
5190
|
+
const baseXValue = xValues.values[displayIndex] ?? displayIndex;
|
|
5191
|
+
const categoryMapper = (value) => positionedCategoryX(
|
|
5192
|
+
value,
|
|
5193
|
+
options.xPositions,
|
|
5194
|
+
(index) => categoryX2(index, indexes.length || catCount, layout, seriesMode)
|
|
5195
|
+
);
|
|
5196
|
+
const toPixel = xValues.numeric ? (value) => numericX(value, xRange, layout) : categoryMapper;
|
|
5197
|
+
const x = toPixel(baseXValue);
|
|
5198
|
+
const y = valueToY2(yValue, yRange, layout.plotTop, layout.plotBottom);
|
|
5199
|
+
for (const direction of ["plus", "minus"]) {
|
|
5200
|
+
if (errBars.barType !== direction && errBars.barType !== "both") {
|
|
5201
|
+
continue;
|
|
5202
|
+
}
|
|
5203
|
+
const values = errBars.direction === "x" ? xValues.values : yValues;
|
|
5204
|
+
const error = errorValue(errBars, values, displayIndex, sourceIndex, direction);
|
|
5205
|
+
if (errBars.direction === "x") {
|
|
5206
|
+
appendXBar(out, errBars, baseXValue, error, direction, y, toPixel);
|
|
5207
|
+
} else {
|
|
5208
|
+
appendYBar(out, errBars, x, yValue, error, direction, yRange, layout);
|
|
5209
|
+
}
|
|
5210
|
+
}
|
|
5211
|
+
});
|
|
5212
|
+
});
|
|
5213
|
+
});
|
|
5214
|
+
return out;
|
|
5215
|
+
}
|
|
5216
|
+
function xToPixel2(xVal, catCount, layout, mode) {
|
|
5217
|
+
if (mode === "bar") {
|
|
5218
|
+
const slotWidth = layout.plotWidth / Math.max(catCount, 1);
|
|
5219
|
+
return layout.plotLeft + slotWidth * xVal + slotWidth / 2;
|
|
5220
|
+
}
|
|
5221
|
+
const maxIdx = Math.max(catCount - 1, 1);
|
|
4077
5222
|
return layout.plotLeft + xVal / maxIdx * layout.plotWidth;
|
|
4078
5223
|
}
|
|
4079
5224
|
function computeLinearRegression2(xVals, yVals) {
|
|
@@ -4322,102 +5467,6 @@ function computeTrendlinePrimitives(chartData, catCount, layout, range, mode = "
|
|
|
4322
5467
|
});
|
|
4323
5468
|
return out;
|
|
4324
5469
|
}
|
|
4325
|
-
function computeErrorValue(errBars, values, pointIndex, direction) {
|
|
4326
|
-
switch (errBars.valType) {
|
|
4327
|
-
case "fixedVal":
|
|
4328
|
-
return errBars.val ?? 0;
|
|
4329
|
-
case "percentage":
|
|
4330
|
-
return Math.abs(values[pointIndex]) * ((errBars.val ?? 0) / 100);
|
|
4331
|
-
case "stdDev": {
|
|
4332
|
-
const n = values.length;
|
|
4333
|
-
if (n === 0) {
|
|
4334
|
-
return 0;
|
|
4335
|
-
}
|
|
4336
|
-
const mean = values.reduce((s, v) => s + v, 0) / n;
|
|
4337
|
-
const variance = values.reduce((s, v) => s + (v - mean) ** 2, 0) / n;
|
|
4338
|
-
return Math.sqrt(variance) * (errBars.val ?? 1);
|
|
4339
|
-
}
|
|
4340
|
-
case "stdErr": {
|
|
4341
|
-
const n2 = values.length;
|
|
4342
|
-
if (n2 === 0) {
|
|
4343
|
-
return 0;
|
|
4344
|
-
}
|
|
4345
|
-
const mean2 = values.reduce((s, v) => s + v, 0) / n2;
|
|
4346
|
-
const variance2 = values.reduce((s, v) => s + (v - mean2) ** 2, 0) / n2;
|
|
4347
|
-
return Math.sqrt(variance2 / n2);
|
|
4348
|
-
}
|
|
4349
|
-
case "cust":
|
|
4350
|
-
return direction === "plus" ? errBars.customPlus?.[pointIndex] ?? 0 : errBars.customMinus?.[pointIndex] ?? 0;
|
|
4351
|
-
default:
|
|
4352
|
-
return 0;
|
|
4353
|
-
}
|
|
4354
|
-
}
|
|
4355
|
-
var ERROR_BAR_COLOR = "#334155";
|
|
4356
|
-
function computeErrorBarPrimitives(chartData, catCount, layout, range, mode = "line") {
|
|
4357
|
-
const out = [];
|
|
4358
|
-
const capW = 4;
|
|
4359
|
-
chartData.series.forEach((series) => {
|
|
4360
|
-
if (!series.errBars || series.errBars.length === 0) {
|
|
4361
|
-
return;
|
|
4362
|
-
}
|
|
4363
|
-
series.errBars.forEach((eb) => {
|
|
4364
|
-
if (eb.direction !== "y") {
|
|
4365
|
-
return;
|
|
4366
|
-
}
|
|
4367
|
-
series.values.forEach((val2, vi) => {
|
|
4368
|
-
const cx = xToPixel2(vi, catCount, layout, mode);
|
|
4369
|
-
const baseY = valueToY2(val2, range, layout.plotTop, layout.plotBottom);
|
|
4370
|
-
if (eb.barType === "plus" || eb.barType === "both") {
|
|
4371
|
-
const plusErr = computeErrorValue(eb, series.values, vi, "plus");
|
|
4372
|
-
const topY = valueToY2(val2 + plusErr, range, layout.plotTop, layout.plotBottom);
|
|
4373
|
-
const stem = {
|
|
4374
|
-
kind: "line",
|
|
4375
|
-
x1: cx,
|
|
4376
|
-
y1: baseY,
|
|
4377
|
-
x2: cx,
|
|
4378
|
-
y2: topY,
|
|
4379
|
-
stroke: ERROR_BAR_COLOR,
|
|
4380
|
-
strokeWidth: 1
|
|
4381
|
-
};
|
|
4382
|
-
const cap = {
|
|
4383
|
-
kind: "line",
|
|
4384
|
-
x1: cx - capW,
|
|
4385
|
-
y1: topY,
|
|
4386
|
-
x2: cx + capW,
|
|
4387
|
-
y2: topY,
|
|
4388
|
-
stroke: ERROR_BAR_COLOR,
|
|
4389
|
-
strokeWidth: 1
|
|
4390
|
-
};
|
|
4391
|
-
out.push(stem, cap);
|
|
4392
|
-
}
|
|
4393
|
-
if (eb.barType === "minus" || eb.barType === "both") {
|
|
4394
|
-
const minusErr = computeErrorValue(eb, series.values, vi, "minus");
|
|
4395
|
-
const botY = valueToY2(val2 - minusErr, range, layout.plotTop, layout.plotBottom);
|
|
4396
|
-
const stem = {
|
|
4397
|
-
kind: "line",
|
|
4398
|
-
x1: cx,
|
|
4399
|
-
y1: baseY,
|
|
4400
|
-
x2: cx,
|
|
4401
|
-
y2: botY,
|
|
4402
|
-
stroke: ERROR_BAR_COLOR,
|
|
4403
|
-
strokeWidth: 1
|
|
4404
|
-
};
|
|
4405
|
-
const cap = {
|
|
4406
|
-
kind: "line",
|
|
4407
|
-
x1: cx - capW,
|
|
4408
|
-
y1: botY,
|
|
4409
|
-
x2: cx + capW,
|
|
4410
|
-
y2: botY,
|
|
4411
|
-
stroke: ERROR_BAR_COLOR,
|
|
4412
|
-
strokeWidth: 1
|
|
4413
|
-
};
|
|
4414
|
-
out.push(stem, cap);
|
|
4415
|
-
}
|
|
4416
|
-
});
|
|
4417
|
-
});
|
|
4418
|
-
});
|
|
4419
|
-
return out;
|
|
4420
|
-
}
|
|
4421
5470
|
var AXIS_TITLE_COLOR = "#475569";
|
|
4422
5471
|
function computeAxisTitlePrimitives(chartData, layout) {
|
|
4423
5472
|
const out = [];
|
|
@@ -4627,42 +5676,6 @@ function computeDataTablePrimitives(chartData, layout, colorPalette) {
|
|
|
4627
5676
|
});
|
|
4628
5677
|
return out;
|
|
4629
5678
|
}
|
|
4630
|
-
function hasRicherAxisFeatures(chartData) {
|
|
4631
|
-
const axes = chartData.axes;
|
|
4632
|
-
if (axes && axes.length > 0) {
|
|
4633
|
-
for (const a of axes) {
|
|
4634
|
-
if (a.axisType === "valAx" && (a.logScale || a.displayUnits || a.axPos === "r")) {
|
|
4635
|
-
return true;
|
|
4636
|
-
}
|
|
4637
|
-
}
|
|
4638
|
-
}
|
|
4639
|
-
return Boolean(chartData.dataTable);
|
|
4640
|
-
}
|
|
4641
|
-
function findPrimaryValueAxis(chartData) {
|
|
4642
|
-
const axes = chartData.axes;
|
|
4643
|
-
if (!axes) {
|
|
4644
|
-
return void 0;
|
|
4645
|
-
}
|
|
4646
|
-
return axes.find((a) => a.axisType === "valAx" && a.axPos !== "r") ?? axes.find((a) => a.axisType === "valAx");
|
|
4647
|
-
}
|
|
4648
|
-
function buildAxes(chartData, layout, primaryRange, secondaryRange) {
|
|
4649
|
-
const richer = hasRicherAxisFeatures(chartData);
|
|
4650
|
-
if (!richer) {
|
|
4651
|
-
const { gridlines: gridlines2, axisLabels: axisLabels2 } = buildGridlinesAndLabels(primaryRange, layout);
|
|
4652
|
-
return { gridlines: gridlines2, axisLabels: axisLabels2, secondaryGridlines: void 0, secondaryAxisLabels: void 0 };
|
|
4653
|
-
}
|
|
4654
|
-
const primaryAxis = findPrimaryValueAxis(chartData);
|
|
4655
|
-
const { gridlines, axisLabels } = buildPrimaryAxis(primaryRange, layout, primaryAxis);
|
|
4656
|
-
let secondaryGridlines;
|
|
4657
|
-
let secondaryAxisLabels;
|
|
4658
|
-
if (secondaryRange) {
|
|
4659
|
-
const secAxis = getSecondaryValueAxis(chartData.axes);
|
|
4660
|
-
const sec = buildSecondaryAxis(secondaryRange, layout, secAxis);
|
|
4661
|
-
secondaryGridlines = sec.gridlines;
|
|
4662
|
-
secondaryAxisLabels = sec.axisLabels;
|
|
4663
|
-
}
|
|
4664
|
-
return { gridlines, axisLabels, secondaryGridlines, secondaryAxisLabels };
|
|
4665
|
-
}
|
|
4666
5679
|
function stackedRange(chartData, catCount, isPercent) {
|
|
4667
5680
|
if (isPercent) {
|
|
4668
5681
|
return { min: 0, max: 100, span: 100 };
|
|
@@ -4681,15 +5694,31 @@ function buildCartesianViewModel(element, chartData, categoryLabels, kind) {
|
|
|
4681
5694
|
const useSecondary = !isStacked && secondaryIdx.size > 0;
|
|
4682
5695
|
const primaryPlotSeries = useSecondary ? chartData.series.filter((_s, i) => !secondaryIdx.has(i)) : chartData.series;
|
|
4683
5696
|
const secondaryPlotSeries = useSecondary ? chartData.series.filter((_s, i) => secondaryIdx.has(i)) : [];
|
|
4684
|
-
const
|
|
5697
|
+
const primaryAxis = chartData.axes?.find(
|
|
5698
|
+
(axis) => axis.axisType === "valAx" && axis.axPos !== "r"
|
|
5699
|
+
);
|
|
5700
|
+
const primaryRange = isStacked ? {
|
|
5701
|
+
...stackedRange(chartData, catCount, isPercent),
|
|
5702
|
+
...primaryAxis?.orientation === "maxMin" ? { reverseOrder: true } : {}
|
|
5703
|
+
} : computeValueRangeForChart(
|
|
4685
5704
|
primaryPlotSeries.length > 0 ? primaryPlotSeries : chartData.series,
|
|
4686
5705
|
chartData.axes
|
|
4687
5706
|
);
|
|
4688
|
-
const secondaryRange = useSecondary && secondaryPlotSeries.length > 0 ?
|
|
4689
|
-
|
|
5707
|
+
const secondaryRange = useSecondary && secondaryPlotSeries.length > 0 ? computeValueRangeForAxis(
|
|
5708
|
+
secondaryPlotSeries,
|
|
5709
|
+
chartData.axes?.find((axis) => axis.axisType === "valAx" && axis.axPos === "r")
|
|
5710
|
+
) : void 0;
|
|
5711
|
+
const axisRes = buildCartesianAxes(chartData, layout, primaryRange, secondaryRange, catCount);
|
|
4690
5712
|
const zeroLine = primaryRange.logScale ? void 0 : buildZeroLine(primaryRange, layout);
|
|
4691
|
-
const
|
|
4692
|
-
|
|
5713
|
+
const horizontalAxis = buildCartesianHorizontalAxis(
|
|
5714
|
+
chartData,
|
|
5715
|
+
categoryLabels,
|
|
5716
|
+
layout,
|
|
5717
|
+
kind,
|
|
5718
|
+
primaryRange,
|
|
5719
|
+
secondaryRange
|
|
5720
|
+
);
|
|
5721
|
+
const { catAxisStyle, sourceIndices: sourceIndices2, displayChartData } = horizontalAxis;
|
|
4693
5722
|
const legendPos = chartData.style?.legendPosition ?? "b";
|
|
4694
5723
|
const { legend, legendX, legendY, legendAnchor } = buildLegend(
|
|
4695
5724
|
chartData.series,
|
|
@@ -4708,31 +5737,52 @@ function buildCartesianViewModel(element, chartData, categoryLabels, kind) {
|
|
|
4708
5737
|
primaryRange,
|
|
4709
5738
|
secondaryRange,
|
|
4710
5739
|
secondaryIdx,
|
|
4711
|
-
isStacked ? isPercent ? "percentStacked" : "stacked" : "clustered"
|
|
5740
|
+
isStacked ? isPercent ? "percentStacked" : "stacked" : "clustered",
|
|
5741
|
+
sourceIndices2
|
|
4712
5742
|
);
|
|
4713
5743
|
} else if (kind === "line") {
|
|
4714
|
-
plot = buildLines(
|
|
5744
|
+
plot = buildLines(
|
|
5745
|
+
chartData,
|
|
5746
|
+
catCount,
|
|
5747
|
+
layout,
|
|
5748
|
+
primaryRange,
|
|
5749
|
+
secondaryRange,
|
|
5750
|
+
secondaryIdx,
|
|
5751
|
+
sourceIndices2,
|
|
5752
|
+
horizontalAxis.xPositions
|
|
5753
|
+
);
|
|
4715
5754
|
} else if (kind === "area") {
|
|
4716
|
-
plot = buildAreas(
|
|
5755
|
+
plot = buildAreas(
|
|
5756
|
+
chartData,
|
|
5757
|
+
catCount,
|
|
5758
|
+
layout,
|
|
5759
|
+
primaryRange,
|
|
5760
|
+
sourceIndices2,
|
|
5761
|
+
horizontalAxis.xPositions
|
|
5762
|
+
);
|
|
4717
5763
|
} else if (kind === "scatter") {
|
|
4718
5764
|
plot = buildScatter(chartData, layout, primaryRange);
|
|
4719
5765
|
} else {
|
|
4720
5766
|
plot = buildBubbles(chartData, layout, primaryRange);
|
|
4721
5767
|
}
|
|
4722
|
-
const primitives = [...plot.primitives];
|
|
5768
|
+
const primitives = [...plot.primitives, ...horizontalAxis.tickMarks];
|
|
4723
5769
|
const overlays = [
|
|
4724
5770
|
...computeTrendlinePrimitives(
|
|
4725
|
-
|
|
5771
|
+
displayChartData,
|
|
4726
5772
|
catCount,
|
|
4727
5773
|
layout,
|
|
4728
5774
|
primaryRange,
|
|
4729
5775
|
catAxisStyle,
|
|
4730
5776
|
chartData.colorPalette
|
|
4731
5777
|
),
|
|
4732
|
-
...computeErrorBarPrimitives(
|
|
5778
|
+
...computeErrorBarPrimitives(displayChartData, catCount, layout, primaryRange, catAxisStyle),
|
|
4733
5779
|
...computeAxisTitlePrimitives(chartData, layout)
|
|
4734
5780
|
];
|
|
4735
|
-
const dataTablePrims = computeDataTablePrimitives(
|
|
5781
|
+
const dataTablePrims = computeDataTablePrimitives(
|
|
5782
|
+
displayChartData,
|
|
5783
|
+
layout,
|
|
5784
|
+
chartData.colorPalette
|
|
5785
|
+
);
|
|
4736
5786
|
primitives.push(...overlays, ...dataTablePrims);
|
|
4737
5787
|
const title = chartData.style?.hasTitle && chartData.title ? chartData.title : void 0;
|
|
4738
5788
|
const valueDrag = isStacked ? void 0 : {
|
|
@@ -4751,7 +5801,7 @@ function buildCartesianViewModel(element, chartData, categoryLabels, kind) {
|
|
|
4751
5801
|
gridlines: axisRes.gridlines,
|
|
4752
5802
|
axisLabels: axisRes.axisLabels,
|
|
4753
5803
|
zeroLine,
|
|
4754
|
-
categoryLabels:
|
|
5804
|
+
categoryLabels: horizontalAxis.labels,
|
|
4755
5805
|
primitives,
|
|
4756
5806
|
dataLabels: plot.dataLabels,
|
|
4757
5807
|
legend: chartData.style?.hasLegend ? legend : [],
|
|
@@ -4765,135 +5815,255 @@ function buildCartesianViewModel(element, chartData, categoryLabels, kind) {
|
|
|
4765
5815
|
valueDrag
|
|
4766
5816
|
};
|
|
4767
5817
|
}
|
|
4768
|
-
|
|
4769
|
-
|
|
4770
|
-
|
|
5818
|
+
function rangeForSeries(index, primaryRange, secondaryRange, secondaryIndexes) {
|
|
5819
|
+
return secondaryRange && secondaryIndexes.has(index) ? secondaryRange : primaryRange;
|
|
5820
|
+
}
|
|
4771
5821
|
function buildComboViewModel(element, chartData, categoryLabels) {
|
|
4772
|
-
const
|
|
4773
|
-
|
|
4774
|
-
|
|
4775
|
-
|
|
4776
|
-
const zeroLine = buildZeroLine(range, layout);
|
|
4777
|
-
const catLabels = buildCategoryLabels(categoryLabels, layout, "bar");
|
|
4778
|
-
const legendPos = chartData.style?.legendPosition ?? "b";
|
|
4779
|
-
const { legend, legendX, legendY, legendAnchor } = buildLegend(
|
|
4780
|
-
chartData.series,
|
|
4781
|
-
chartData.colorPalette,
|
|
4782
|
-
layout.svgWidth,
|
|
4783
|
-
legendPos,
|
|
4784
|
-
layout.svgHeight,
|
|
4785
|
-
layout.plotTop
|
|
5822
|
+
const layoutOptions = computeLayoutOptions(
|
|
5823
|
+
chartData.axes,
|
|
5824
|
+
chartData.dataTable,
|
|
5825
|
+
chartData.series.length
|
|
4786
5826
|
);
|
|
4787
|
-
const
|
|
4788
|
-
|
|
4789
|
-
|
|
4790
|
-
|
|
4791
|
-
|
|
4792
|
-
|
|
4793
|
-
|
|
4794
|
-
|
|
4795
|
-
|
|
4796
|
-
|
|
4797
|
-
|
|
4798
|
-
|
|
4799
|
-
|
|
4800
|
-
|
|
4801
|
-
|
|
4802
|
-
|
|
4803
|
-
|
|
4804
|
-
|
|
4805
|
-
|
|
4806
|
-
|
|
4807
|
-
|
|
4808
|
-
|
|
4809
|
-
|
|
4810
|
-
|
|
4811
|
-
|
|
4812
|
-
|
|
4813
|
-
|
|
4814
|
-
|
|
4815
|
-
|
|
4816
|
-
|
|
4817
|
-
|
|
4818
|
-
|
|
4819
|
-
|
|
4820
|
-
|
|
4821
|
-
|
|
4822
|
-
|
|
5827
|
+
const layout = computePlotLayout(
|
|
5828
|
+
element.width,
|
|
5829
|
+
element.height,
|
|
5830
|
+
chartData,
|
|
5831
|
+
true,
|
|
5832
|
+
layoutOptions
|
|
5833
|
+
);
|
|
5834
|
+
const catCount = Math.max(categoryLabels.length, 1);
|
|
5835
|
+
const { primary, secondary } = splitSeriesByAxis(chartData.series, chartData.axes);
|
|
5836
|
+
const secondaryIndexes = new Set(secondary.map((entry) => entry.index));
|
|
5837
|
+
const primarySeries = primary.length > 0 ? primary.map((entry) => entry.series) : chartData.series;
|
|
5838
|
+
const primaryAxisId = getPrimaryValueAxisId(chartData.axes);
|
|
5839
|
+
const primaryAxis = chartData.axes?.find((axis) => axis.axisId === primaryAxisId);
|
|
5840
|
+
const secondaryAxisFormatting = getSecondaryValueAxis(chartData.axes);
|
|
5841
|
+
const primaryRange = computeValueRangeForAxis(primarySeries, primaryAxis);
|
|
5842
|
+
const secondaryRange = secondary.length > 0 ? computeValueRangeForAxis(
|
|
5843
|
+
secondary.map((entry) => entry.series),
|
|
5844
|
+
secondaryAxisFormatting
|
|
5845
|
+
) : void 0;
|
|
5846
|
+
const primaryCategoryAxis2 = chartData.axes?.find(
|
|
5847
|
+
(axis) => (axis.axisType === "catAx" || axis.axisType === "dateAx") && axis.axisId === primaryAxis?.crossAxisId
|
|
5848
|
+
);
|
|
5849
|
+
const primaryAxisX = verticalAxisX(
|
|
5850
|
+
primaryCategoryAxis2,
|
|
5851
|
+
catCount,
|
|
5852
|
+
layout,
|
|
5853
|
+
"left",
|
|
5854
|
+
chartData.dateCategories?.values
|
|
5855
|
+
);
|
|
5856
|
+
const primaryRendered = primaryCategoryAxis2?.crosses !== void 0 || primaryCategoryAxis2?.crossesAt !== void 0 ? buildPrimaryAxis(primaryRange, layout, primaryAxis, primaryAxisX) : buildGridlinesAndLabels(primaryRange, layout);
|
|
5857
|
+
const { gridlines, axisLabels } = primaryRendered;
|
|
5858
|
+
const secondaryCategoryAxis = chartData.axes?.find(
|
|
5859
|
+
(axis) => (axis.axisType === "catAx" || axis.axisType === "dateAx") && axis.axisId === secondaryAxisFormatting?.crossAxisId
|
|
5860
|
+
);
|
|
5861
|
+
const secondaryAxis = secondaryRange ? buildSecondaryAxis(
|
|
5862
|
+
secondaryRange,
|
|
5863
|
+
layout,
|
|
5864
|
+
secondaryAxisFormatting,
|
|
5865
|
+
verticalAxisX(
|
|
5866
|
+
secondaryCategoryAxis,
|
|
5867
|
+
catCount,
|
|
5868
|
+
layout,
|
|
5869
|
+
"right",
|
|
5870
|
+
chartData.dateCategories?.values
|
|
5871
|
+
)
|
|
5872
|
+
) : void 0;
|
|
5873
|
+
const zeroLine = primaryRange.logScale ? void 0 : buildZeroLine(primaryRange, layout);
|
|
5874
|
+
const horizontalAxis = buildCartesianHorizontalAxis(
|
|
5875
|
+
chartData,
|
|
5876
|
+
categoryLabels,
|
|
5877
|
+
layout,
|
|
5878
|
+
"combo",
|
|
5879
|
+
primaryRange,
|
|
5880
|
+
secondaryRange
|
|
5881
|
+
);
|
|
5882
|
+
const sourceIndices2 = horizontalAxis.sourceIndices;
|
|
5883
|
+
const legendPos = chartData.style?.legendPosition ?? "b";
|
|
5884
|
+
const { legend, legendX, legendY, legendAnchor } = buildLegend(
|
|
5885
|
+
chartData.series,
|
|
5886
|
+
chartData.colorPalette,
|
|
5887
|
+
layout.svgWidth,
|
|
5888
|
+
legendPos,
|
|
5889
|
+
layout.svgHeight,
|
|
5890
|
+
layout.plotTop
|
|
5891
|
+
);
|
|
5892
|
+
const primitives = [];
|
|
5893
|
+
const dataLabels = [];
|
|
5894
|
+
const barSeries = chartData.series.slice(0, 1);
|
|
5895
|
+
if (barSeries[0]) {
|
|
5896
|
+
const barRange = rangeForSeries(0, primaryRange, secondaryRange, secondaryIndexes);
|
|
5897
|
+
const displayBarSeries = [
|
|
5898
|
+
{ ...barSeries[0], values: sourceIndices2.map((index) => barSeries[0].values[index] ?? 0) }
|
|
5899
|
+
];
|
|
5900
|
+
primitives.push(
|
|
5901
|
+
...computeBarRects(displayBarSeries, catCount, layout, barRange, chartData.colorPalette).map(
|
|
5902
|
+
(rect, displayIndex) => ({
|
|
5903
|
+
kind: "rect",
|
|
5904
|
+
x: horizontalAxis.xPositions ? (horizontalAxis.xPositions[displayIndex] ?? rect.x) - rect.w / 2 : rect.x,
|
|
5905
|
+
y: rect.y,
|
|
5906
|
+
w: rect.w,
|
|
5907
|
+
h: rect.h,
|
|
5908
|
+
fill: rect.fill,
|
|
5909
|
+
rx: 1,
|
|
5910
|
+
part: {
|
|
5911
|
+
role: "dataPoint",
|
|
5912
|
+
seriesIndex: 0,
|
|
5913
|
+
pointIndex: sourceIndices2[displayIndex] ?? displayIndex
|
|
5914
|
+
}
|
|
5915
|
+
})
|
|
5916
|
+
)
|
|
5917
|
+
);
|
|
5918
|
+
appendBarLabels(
|
|
5919
|
+
barSeries[0],
|
|
5920
|
+
chartData,
|
|
5921
|
+
layout,
|
|
5922
|
+
catCount,
|
|
5923
|
+
barRange,
|
|
5924
|
+
sourceIndices2,
|
|
5925
|
+
dataLabels,
|
|
5926
|
+
horizontalAxis.xPositions
|
|
5927
|
+
);
|
|
4823
5928
|
}
|
|
4824
5929
|
const barGroupWidth = layout.plotWidth / catCount;
|
|
4825
|
-
|
|
4826
|
-
|
|
5930
|
+
chartData.series.slice(1).forEach((series, offset) => {
|
|
5931
|
+
const seriesIndex = offset + 1;
|
|
4827
5932
|
if (series.values.length === 0) {
|
|
4828
5933
|
return;
|
|
4829
5934
|
}
|
|
4830
|
-
const
|
|
4831
|
-
const
|
|
4832
|
-
const
|
|
4833
|
-
|
|
4834
|
-
|
|
4835
|
-
|
|
4836
|
-
|
|
5935
|
+
const range = rangeForSeries(seriesIndex, primaryRange, secondaryRange, secondaryIndexes);
|
|
5936
|
+
const fill = seriesColor2(series, seriesIndex, chartData.colorPalette);
|
|
5937
|
+
const points = sourceIndices2.map((sourceIndex, displayIndex) => {
|
|
5938
|
+
const value = series.values[sourceIndex] ?? 0;
|
|
5939
|
+
return {
|
|
5940
|
+
x: horizontalAxis.xPositions?.[displayIndex] ?? layout.plotLeft + barGroupWidth * displayIndex + barGroupWidth / 2,
|
|
5941
|
+
y: valueToY2(value, range, layout.plotTop, layout.plotBottom),
|
|
5942
|
+
sourceIndex,
|
|
5943
|
+
value
|
|
5944
|
+
};
|
|
5945
|
+
});
|
|
4837
5946
|
primitives.push({
|
|
4838
5947
|
kind: "polyline",
|
|
4839
|
-
points:
|
|
4840
|
-
stroke:
|
|
5948
|
+
points: points.map((point) => `${point.x.toFixed(2)},${point.y.toFixed(2)}`).join(" "),
|
|
5949
|
+
stroke: fill,
|
|
4841
5950
|
strokeWidth: 2.4,
|
|
4842
5951
|
fill: "none"
|
|
4843
5952
|
});
|
|
4844
|
-
|
|
4845
|
-
|
|
4846
|
-
|
|
4847
|
-
|
|
4848
|
-
|
|
4849
|
-
|
|
4850
|
-
|
|
4851
|
-
|
|
4852
|
-
|
|
5953
|
+
primitives.push(
|
|
5954
|
+
...points.map(
|
|
5955
|
+
(point) => ({
|
|
5956
|
+
kind: "circle",
|
|
5957
|
+
cx: point.x,
|
|
5958
|
+
cy: point.y,
|
|
5959
|
+
r: 2.5,
|
|
5960
|
+
fill,
|
|
5961
|
+
part: { role: "dataPoint", seriesIndex, pointIndex: point.sourceIndex }
|
|
5962
|
+
})
|
|
5963
|
+
)
|
|
5964
|
+
);
|
|
4853
5965
|
if (chartData.style?.hasDataLabels) {
|
|
4854
|
-
|
|
4855
|
-
|
|
4856
|
-
|
|
4857
|
-
|
|
5966
|
+
points.forEach((point) => {
|
|
5967
|
+
if (point) {
|
|
5968
|
+
dataLabels.push({
|
|
5969
|
+
kind: "text",
|
|
5970
|
+
x: point.x,
|
|
5971
|
+
y: point.y - 7,
|
|
5972
|
+
text: formatAxisValue2(point.value),
|
|
5973
|
+
fontSize: 7,
|
|
5974
|
+
fill: "#334155",
|
|
5975
|
+
textAnchor: "middle"
|
|
5976
|
+
});
|
|
4858
5977
|
}
|
|
4859
|
-
dataLabels.push({
|
|
4860
|
-
kind: "text",
|
|
4861
|
-
x: pt.x,
|
|
4862
|
-
y: pt.y - 7,
|
|
4863
|
-
text: formatAxisValue2(val2),
|
|
4864
|
-
fontSize: 7,
|
|
4865
|
-
fill: "#334155",
|
|
4866
|
-
textAnchor: "middle"
|
|
4867
|
-
});
|
|
4868
5978
|
});
|
|
4869
5979
|
}
|
|
4870
5980
|
});
|
|
4871
|
-
|
|
5981
|
+
primitives.push(...horizontalAxis.tickMarks);
|
|
5982
|
+
const displayChartData = horizontalAxis.displayChartData;
|
|
5983
|
+
primitives.push(
|
|
5984
|
+
...computeErrorBarPrimitives(displayChartData, catCount, layout, primaryRange, "line", {
|
|
5985
|
+
xPositions: horizontalAxis.xPositions,
|
|
5986
|
+
seriesRanges: chartData.series.map(
|
|
5987
|
+
(_series, index) => rangeForSeries(index, primaryRange, secondaryRange, secondaryIndexes)
|
|
5988
|
+
),
|
|
5989
|
+
seriesModes: chartData.series.map((_series, index) => index === 0 ? "bar" : "line")
|
|
5990
|
+
})
|
|
5991
|
+
);
|
|
4872
5992
|
return {
|
|
4873
5993
|
svgWidth: layout.svgWidth,
|
|
4874
5994
|
svgHeight: layout.svgHeight,
|
|
4875
|
-
title,
|
|
5995
|
+
title: chartData.style?.hasTitle && chartData.title ? chartData.title : void 0,
|
|
4876
5996
|
titleX: layout.svgWidth / 2,
|
|
4877
5997
|
titleY: 12,
|
|
4878
5998
|
gridlines,
|
|
4879
5999
|
axisLabels,
|
|
4880
6000
|
zeroLine,
|
|
4881
|
-
categoryLabels:
|
|
6001
|
+
categoryLabels: horizontalAxis.labels,
|
|
4882
6002
|
primitives,
|
|
4883
6003
|
dataLabels,
|
|
4884
6004
|
legend: chartData.style?.hasLegend ? legend : [],
|
|
4885
6005
|
legendX,
|
|
4886
6006
|
legendY,
|
|
4887
|
-
legendAnchor
|
|
6007
|
+
legendAnchor,
|
|
6008
|
+
secondaryGridlines: secondaryAxis?.gridlines,
|
|
6009
|
+
secondaryAxisLabels: secondaryAxis?.axisLabels
|
|
4888
6010
|
};
|
|
4889
6011
|
}
|
|
6012
|
+
function appendBarLabels(series, chartData, layout, catCount, range, sourceIndices2, labels, xPositions) {
|
|
6013
|
+
if (!chartData.style?.hasDataLabels) {
|
|
6014
|
+
return;
|
|
6015
|
+
}
|
|
6016
|
+
const groupWidth = layout.plotWidth / catCount;
|
|
6017
|
+
const barWidth = groupWidth * 0.7;
|
|
6018
|
+
const offset = (groupWidth - barWidth) / 2;
|
|
6019
|
+
sourceIndices2.forEach((sourceIndex, displayIndex) => {
|
|
6020
|
+
const value = series.values[sourceIndex] ?? 0;
|
|
6021
|
+
const zeroY = valueToY2(0, range, layout.plotTop, layout.plotBottom);
|
|
6022
|
+
const valueY = valueToY2(value, range, layout.plotTop, layout.plotBottom);
|
|
6023
|
+
labels.push({
|
|
6024
|
+
kind: "text",
|
|
6025
|
+
x: xPositions?.[displayIndex] ?? layout.plotLeft + groupWidth * displayIndex + offset + barWidth / 2,
|
|
6026
|
+
y: value >= 0 ? Math.min(zeroY, valueY) - 4 : Math.max(zeroY, valueY) + 10,
|
|
6027
|
+
text: formatAxisValue2(value),
|
|
6028
|
+
fontSize: 7,
|
|
6029
|
+
fill: "#334155",
|
|
6030
|
+
textAnchor: "middle"
|
|
6031
|
+
});
|
|
6032
|
+
});
|
|
6033
|
+
}
|
|
6034
|
+
var CANDLE_UP_FILL = "#22c55e";
|
|
6035
|
+
var CANDLE_DOWN_FILL = "#ef4444";
|
|
6036
|
+
var CANDLE_WICK_COLOR = "#334155";
|
|
4890
6037
|
function buildStockViewModel(element, chartData, categoryLabels) {
|
|
4891
|
-
const layout = computePlotLayout(
|
|
6038
|
+
const layout = computePlotLayout(
|
|
6039
|
+
element.width,
|
|
6040
|
+
element.height,
|
|
6041
|
+
chartData,
|
|
6042
|
+
true,
|
|
6043
|
+
computeLayoutOptions(chartData.axes, chartData.dataTable, chartData.series.length)
|
|
6044
|
+
);
|
|
4892
6045
|
const catCount = Math.max(categoryLabels.length, 1);
|
|
4893
6046
|
const range = computeValueRange2(chartData.series);
|
|
4894
|
-
const
|
|
6047
|
+
const valueAxis = chartData.axes?.find((axis) => axis.axisType === "valAx" && axis.axPos !== "r");
|
|
6048
|
+
const categoryAxis = chartData.axes?.find(
|
|
6049
|
+
(axis) => (axis.axisType === "catAx" || axis.axisType === "dateAx") && axis.axisId === valueAxis?.crossAxisId
|
|
6050
|
+
);
|
|
6051
|
+
const renderedAxis = categoryAxis?.crosses !== void 0 || categoryAxis?.crossesAt !== void 0 ? buildPrimaryAxis(
|
|
6052
|
+
range,
|
|
6053
|
+
layout,
|
|
6054
|
+
valueAxis,
|
|
6055
|
+
verticalAxisX(categoryAxis, catCount, layout, "left", chartData.dateCategories?.values)
|
|
6056
|
+
) : buildGridlinesAndLabels(range, layout);
|
|
6057
|
+
const { gridlines, axisLabels } = renderedAxis;
|
|
4895
6058
|
const zeroLine = buildZeroLine(range, layout);
|
|
4896
|
-
const
|
|
6059
|
+
const horizontalAxis = buildCartesianHorizontalAxis(
|
|
6060
|
+
chartData,
|
|
6061
|
+
categoryLabels,
|
|
6062
|
+
layout,
|
|
6063
|
+
"stock",
|
|
6064
|
+
range
|
|
6065
|
+
);
|
|
6066
|
+
const sourceIndices2 = horizontalAxis.sourceIndices;
|
|
4897
6067
|
const legendPos = chartData.style?.legendPosition ?? "b";
|
|
4898
6068
|
const { legend, legendX, legendY, legendAnchor } = buildLegend(
|
|
4899
6069
|
chartData.series,
|
|
@@ -4913,13 +6083,14 @@ function buildStockViewModel(element, chartData, categoryLabels) {
|
|
|
4913
6083
|
if (highSeries && lowSeries && closeSeries) {
|
|
4914
6084
|
const barGroupWidth = layout.plotWidth / catCount;
|
|
4915
6085
|
const candleWidth = barGroupWidth * 0.5;
|
|
4916
|
-
for (let
|
|
4917
|
-
const
|
|
4918
|
-
const
|
|
4919
|
-
const
|
|
4920
|
-
const
|
|
6086
|
+
for (let displayIndex = 0; displayIndex < catCount; displayIndex++) {
|
|
6087
|
+
const sourceIndex = sourceIndices2[displayIndex] ?? displayIndex;
|
|
6088
|
+
const high = highSeries.values[sourceIndex] ?? 0;
|
|
6089
|
+
const low = lowSeries.values[sourceIndex] ?? 0;
|
|
6090
|
+
const open = openSeries ? openSeries.values[sourceIndex] ?? low : low;
|
|
6091
|
+
const close = closeSeries.values[sourceIndex] ?? high;
|
|
4921
6092
|
const isUp = close >= open;
|
|
4922
|
-
const cx = layout.plotLeft + barGroupWidth *
|
|
6093
|
+
const cx = horizontalAxis.xPositions?.[displayIndex] ?? layout.plotLeft + barGroupWidth * displayIndex + barGroupWidth / 2;
|
|
4923
6094
|
const highY = valueToY2(high, range, layout.plotTop, layout.plotBottom);
|
|
4924
6095
|
const lowY = valueToY2(low, range, layout.plotTop, layout.plotBottom);
|
|
4925
6096
|
const openY = valueToY2(open, range, layout.plotTop, layout.plotBottom);
|
|
@@ -4942,7 +6113,12 @@ function buildStockViewModel(element, chartData, categoryLabels) {
|
|
|
4942
6113
|
w: candleWidth,
|
|
4943
6114
|
h: bodyHeight,
|
|
4944
6115
|
fill: isUp ? CANDLE_UP_FILL : CANDLE_DOWN_FILL,
|
|
4945
|
-
rx: 1
|
|
6116
|
+
rx: 1,
|
|
6117
|
+
part: {
|
|
6118
|
+
role: "dataPoint",
|
|
6119
|
+
seriesIndex: hasFour ? 3 : 2,
|
|
6120
|
+
pointIndex: sourceIndex
|
|
6121
|
+
}
|
|
4946
6122
|
});
|
|
4947
6123
|
if (chartData.style?.hasDataLabels) {
|
|
4948
6124
|
dataLabels.push({
|
|
@@ -4957,6 +6133,7 @@ function buildStockViewModel(element, chartData, categoryLabels) {
|
|
|
4957
6133
|
}
|
|
4958
6134
|
}
|
|
4959
6135
|
}
|
|
6136
|
+
primitives.push(...horizontalAxis.tickMarks);
|
|
4960
6137
|
const title = chartData.style?.hasTitle && chartData.title ? chartData.title : void 0;
|
|
4961
6138
|
return {
|
|
4962
6139
|
svgWidth: layout.svgWidth,
|
|
@@ -4967,7 +6144,7 @@ function buildStockViewModel(element, chartData, categoryLabels) {
|
|
|
4967
6144
|
gridlines,
|
|
4968
6145
|
axisLabels,
|
|
4969
6146
|
zeroLine,
|
|
4970
|
-
categoryLabels:
|
|
6147
|
+
categoryLabels: horizontalAxis.labels,
|
|
4971
6148
|
primitives,
|
|
4972
6149
|
dataLabels,
|
|
4973
6150
|
legend: chartData.style?.hasLegend ? legend : [],
|
|
@@ -4976,255 +6153,544 @@ function buildStockViewModel(element, chartData, categoryLabels) {
|
|
|
4976
6153
|
legendAnchor
|
|
4977
6154
|
};
|
|
4978
6155
|
}
|
|
4979
|
-
|
|
4980
|
-
|
|
4981
|
-
|
|
6156
|
+
function percentile(sorted, p, method) {
|
|
6157
|
+
const rank = method === "inclusive" ? (sorted.length - 1) * p : (sorted.length + 1) * p - 1;
|
|
6158
|
+
const clamped = Math.max(0, Math.min(rank, sorted.length - 1));
|
|
6159
|
+
const lower = Math.floor(clamped);
|
|
6160
|
+
const upper = Math.ceil(clamped);
|
|
6161
|
+
const fraction = clamped - lower;
|
|
6162
|
+
return sorted[lower] + (sorted[upper] - sorted[lower]) * fraction;
|
|
6163
|
+
}
|
|
6164
|
+
function computeBoxStats(values, method) {
|
|
6165
|
+
if (values.length < 2) {
|
|
6166
|
+
return void 0;
|
|
6167
|
+
}
|
|
6168
|
+
const sorted = [...values].sort((a, b) => a - b);
|
|
6169
|
+
if (!method) {
|
|
6170
|
+
return {
|
|
6171
|
+
min: sorted[0],
|
|
6172
|
+
q1: sorted[Math.floor(sorted.length * 0.25)],
|
|
6173
|
+
median: sorted[Math.floor(sorted.length * 0.5)],
|
|
6174
|
+
q3: sorted[Math.floor(sorted.length * 0.75)],
|
|
6175
|
+
max: sorted[sorted.length - 1]
|
|
6176
|
+
};
|
|
6177
|
+
}
|
|
6178
|
+
return {
|
|
6179
|
+
min: sorted[0],
|
|
6180
|
+
q1: percentile(sorted, 0.25, method),
|
|
6181
|
+
median: percentile(sorted, 0.5, method),
|
|
6182
|
+
q3: percentile(sorted, 0.75, method),
|
|
6183
|
+
max: sorted[sorted.length - 1]
|
|
6184
|
+
};
|
|
6185
|
+
}
|
|
4982
6186
|
function distributionRange(series) {
|
|
4983
|
-
const all = series.flatMap((
|
|
6187
|
+
const all = series.flatMap((item) => item.values);
|
|
4984
6188
|
const min = Math.min(...all, 0);
|
|
4985
6189
|
const max = Math.max(...all, 1);
|
|
4986
6190
|
return { min, max, span: Math.max(max - min, 1) };
|
|
4987
6191
|
}
|
|
6192
|
+
var WHISKER_COLOR = "#64748b";
|
|
6193
|
+
var MEDIAN_COLOR = "#1e293b";
|
|
6194
|
+
function computeBoxWhiskerGeometry(chartData, catCount, layout, range, colorPalette) {
|
|
6195
|
+
const groupWidth = layout.plotWidth / catCount;
|
|
6196
|
+
const boxW = groupWidth * 0.5;
|
|
6197
|
+
const options = chartData.series.find((series) => series.boxWhiskerOptions)?.boxWhiskerOptions;
|
|
6198
|
+
const method = options ? options.quartileMethod ?? "exclusive" : void 0;
|
|
6199
|
+
const output = [];
|
|
6200
|
+
for (let categoryIndex = 0; categoryIndex < catCount; categoryIndex++) {
|
|
6201
|
+
const observations = chartData.series.map((series, seriesIndex) => ({ value: series.values[categoryIndex], seriesIndex })).filter((item) => item.value !== void 0);
|
|
6202
|
+
const stats = computeBoxStats(
|
|
6203
|
+
observations.map((item) => item.value),
|
|
6204
|
+
method
|
|
6205
|
+
);
|
|
6206
|
+
if (!stats) {
|
|
6207
|
+
continue;
|
|
6208
|
+
}
|
|
6209
|
+
const iqr = stats.q3 - stats.q1;
|
|
6210
|
+
const lowerFence = stats.q1 - 1.5 * iqr;
|
|
6211
|
+
const upperFence = stats.q3 + 1.5 * iqr;
|
|
6212
|
+
const inliers = observations.filter(
|
|
6213
|
+
(item) => !options || item.value >= lowerFence && item.value <= upperFence
|
|
6214
|
+
);
|
|
6215
|
+
const whiskerMin = Math.min(...inliers.map((item) => item.value));
|
|
6216
|
+
const whiskerMax = Math.max(...inliers.map((item) => item.value));
|
|
6217
|
+
const boxX = layout.plotLeft + groupWidth * categoryIndex + (groupWidth - boxW) / 2;
|
|
6218
|
+
const mean = observations.reduce((sum, item) => sum + item.value, 0) / observations.length;
|
|
6219
|
+
output.push({
|
|
6220
|
+
stats,
|
|
6221
|
+
boxX,
|
|
6222
|
+
boxW,
|
|
6223
|
+
xMid: boxX + boxW / 2,
|
|
6224
|
+
yMin: valueToY2(whiskerMin, range, layout.plotTop, layout.plotBottom),
|
|
6225
|
+
yMax: valueToY2(whiskerMax, range, layout.plotTop, layout.plotBottom),
|
|
6226
|
+
yQ1: valueToY2(stats.q1, range, layout.plotTop, layout.plotBottom),
|
|
6227
|
+
yQ3: valueToY2(stats.q3, range, layout.plotTop, layout.plotBottom),
|
|
6228
|
+
yMed: valueToY2(stats.median, range, layout.plotTop, layout.plotBottom),
|
|
6229
|
+
yMean: valueToY2(mean, range, layout.plotTop, layout.plotBottom),
|
|
6230
|
+
fill: paletteColor2(categoryIndex, colorPalette),
|
|
6231
|
+
points: observations.map((item, index) => ({
|
|
6232
|
+
x: boxX + boxW * (0.2 + 0.6 * (index + 1) / (observations.length + 1)),
|
|
6233
|
+
y: valueToY2(item.value, range, layout.plotTop, layout.plotBottom),
|
|
6234
|
+
seriesIndex: item.seriesIndex,
|
|
6235
|
+
outlier: item.value < whiskerMin || item.value > whiskerMax
|
|
6236
|
+
}))
|
|
6237
|
+
});
|
|
6238
|
+
}
|
|
6239
|
+
return output;
|
|
6240
|
+
}
|
|
6241
|
+
function whiskerPrimitives(geometry) {
|
|
6242
|
+
const g = geometry;
|
|
6243
|
+
return [
|
|
6244
|
+
{
|
|
6245
|
+
kind: "line",
|
|
6246
|
+
x1: g.xMid,
|
|
6247
|
+
y1: g.yMax,
|
|
6248
|
+
x2: g.xMid,
|
|
6249
|
+
y2: g.yQ3,
|
|
6250
|
+
stroke: WHISKER_COLOR,
|
|
6251
|
+
strokeWidth: 1
|
|
6252
|
+
},
|
|
6253
|
+
{
|
|
6254
|
+
kind: "line",
|
|
6255
|
+
x1: g.xMid,
|
|
6256
|
+
y1: g.yQ1,
|
|
6257
|
+
x2: g.xMid,
|
|
6258
|
+
y2: g.yMin,
|
|
6259
|
+
stroke: WHISKER_COLOR,
|
|
6260
|
+
strokeWidth: 1
|
|
6261
|
+
},
|
|
6262
|
+
{
|
|
6263
|
+
kind: "line",
|
|
6264
|
+
x1: g.boxX + g.boxW * 0.25,
|
|
6265
|
+
y1: g.yMax,
|
|
6266
|
+
x2: g.boxX + g.boxW * 0.75,
|
|
6267
|
+
y2: g.yMax,
|
|
6268
|
+
stroke: WHISKER_COLOR,
|
|
6269
|
+
strokeWidth: 1
|
|
6270
|
+
},
|
|
6271
|
+
{
|
|
6272
|
+
kind: "line",
|
|
6273
|
+
x1: g.boxX + g.boxW * 0.25,
|
|
6274
|
+
y1: g.yMin,
|
|
6275
|
+
x2: g.boxX + g.boxW * 0.75,
|
|
6276
|
+
y2: g.yMin,
|
|
6277
|
+
stroke: WHISKER_COLOR,
|
|
6278
|
+
strokeWidth: 1
|
|
6279
|
+
},
|
|
6280
|
+
{
|
|
6281
|
+
kind: "rect",
|
|
6282
|
+
x: g.boxX,
|
|
6283
|
+
y: Math.min(g.yQ1, g.yQ3),
|
|
6284
|
+
w: g.boxW,
|
|
6285
|
+
h: Math.abs(g.yQ1 - g.yQ3),
|
|
6286
|
+
fill: g.fill,
|
|
6287
|
+
rx: 1,
|
|
6288
|
+
opacity: 0.8
|
|
6289
|
+
},
|
|
6290
|
+
{
|
|
6291
|
+
kind: "line",
|
|
6292
|
+
x1: g.boxX,
|
|
6293
|
+
y1: g.yMed,
|
|
6294
|
+
x2: g.boxX + g.boxW,
|
|
6295
|
+
y2: g.yMed,
|
|
6296
|
+
stroke: MEDIAN_COLOR,
|
|
6297
|
+
strokeWidth: 2
|
|
6298
|
+
}
|
|
6299
|
+
];
|
|
6300
|
+
}
|
|
6301
|
+
function optionPrimitives(geometry, options, categoryIndex) {
|
|
6302
|
+
if (!options) {
|
|
6303
|
+
return [];
|
|
6304
|
+
}
|
|
6305
|
+
const output = [];
|
|
6306
|
+
if (options.showMeanLine) {
|
|
6307
|
+
output.push({
|
|
6308
|
+
kind: "line",
|
|
6309
|
+
x1: geometry.boxX,
|
|
6310
|
+
y1: geometry.yMean,
|
|
6311
|
+
x2: geometry.boxX + geometry.boxW,
|
|
6312
|
+
y2: geometry.yMean,
|
|
6313
|
+
stroke: "#0f766e",
|
|
6314
|
+
strokeWidth: 1.5
|
|
6315
|
+
});
|
|
6316
|
+
}
|
|
6317
|
+
if (options.showMeanMarker) {
|
|
6318
|
+
output.push({
|
|
6319
|
+
kind: "circle",
|
|
6320
|
+
cx: geometry.xMid,
|
|
6321
|
+
cy: geometry.yMean,
|
|
6322
|
+
r: 3,
|
|
6323
|
+
fill: "#0f766e"
|
|
6324
|
+
});
|
|
6325
|
+
}
|
|
6326
|
+
for (const point of geometry.points) {
|
|
6327
|
+
if (point.outlier && !options.showOutlierPoints || !point.outlier && !options.showInnerPoints) {
|
|
6328
|
+
continue;
|
|
6329
|
+
}
|
|
6330
|
+
output.push({
|
|
6331
|
+
kind: "circle",
|
|
6332
|
+
cx: point.x,
|
|
6333
|
+
cy: point.y,
|
|
6334
|
+
r: 2.25,
|
|
6335
|
+
fill: point.outlier ? "#dc2626" : MEDIAN_COLOR,
|
|
6336
|
+
part: { role: "dataPoint", seriesIndex: point.seriesIndex, pointIndex: categoryIndex }
|
|
6337
|
+
});
|
|
6338
|
+
}
|
|
6339
|
+
return output;
|
|
6340
|
+
}
|
|
6341
|
+
function buildBoxWhiskerViewModel(element, chartData, categoryLabels) {
|
|
6342
|
+
const layout = computePlotLayout(element.width, element.height, chartData, true);
|
|
6343
|
+
const range = distributionRange(chartData.series);
|
|
6344
|
+
const options = chartData.series.find((series) => series.boxWhiskerOptions)?.boxWhiskerOptions;
|
|
6345
|
+
const geometries = computeBoxWhiskerGeometry(
|
|
6346
|
+
chartData,
|
|
6347
|
+
Math.max(categoryLabels.length, 1),
|
|
6348
|
+
layout,
|
|
6349
|
+
range,
|
|
6350
|
+
chartData.colorPalette
|
|
6351
|
+
);
|
|
6352
|
+
const primitives = geometries.flatMap((geometry, index) => [
|
|
6353
|
+
...whiskerPrimitives(geometry),
|
|
6354
|
+
...optionPrimitives(geometry, options, index)
|
|
6355
|
+
]);
|
|
6356
|
+
const { gridlines, axisLabels } = buildGridlinesAndLabels(range, layout);
|
|
6357
|
+
const { legend, legendX, legendY, legendAnchor } = buildLegend(
|
|
6358
|
+
chartData.series,
|
|
6359
|
+
chartData.colorPalette,
|
|
6360
|
+
layout.svgWidth,
|
|
6361
|
+
chartData.style?.legendPosition ?? "b",
|
|
6362
|
+
layout.svgHeight,
|
|
6363
|
+
layout.plotTop
|
|
6364
|
+
);
|
|
6365
|
+
const categoryLegend = categoryLabels.map((label, index) => ({
|
|
6366
|
+
color: paletteColor2(index, chartData.colorPalette),
|
|
6367
|
+
label
|
|
6368
|
+
}));
|
|
6369
|
+
return {
|
|
6370
|
+
svgWidth: layout.svgWidth,
|
|
6371
|
+
svgHeight: layout.svgHeight,
|
|
6372
|
+
title: chartData.style?.hasTitle ? chartData.title : void 0,
|
|
6373
|
+
titleX: layout.svgWidth / 2,
|
|
6374
|
+
titleY: 12,
|
|
6375
|
+
gridlines,
|
|
6376
|
+
axisLabels,
|
|
6377
|
+
zeroLine: buildZeroLine(range, layout),
|
|
6378
|
+
categoryLabels: buildCategoryLabels(categoryLabels, layout, "bar"),
|
|
6379
|
+
primitives,
|
|
6380
|
+
dataLabels: [],
|
|
6381
|
+
legend: chartData.style?.hasLegend ? categoryLegend.length ? categoryLegend : legend : [],
|
|
6382
|
+
legendX,
|
|
6383
|
+
legendY,
|
|
6384
|
+
legendAnchor
|
|
6385
|
+
};
|
|
6386
|
+
}
|
|
6387
|
+
function orderParetoEntries(entries) {
|
|
6388
|
+
return [...entries].sort(
|
|
6389
|
+
(left, right) => Math.max(right.value, 0) - Math.max(left.value, 0) || left.sourcePointIndex - right.sourcePointIndex
|
|
6390
|
+
);
|
|
6391
|
+
}
|
|
6392
|
+
function buildParetoPrimitives(entries, layout, series, seriesIndex) {
|
|
6393
|
+
const total = entries.reduce((sum, entry) => sum + Math.max(entry.value, 0), 0);
|
|
6394
|
+
if (total <= 0) {
|
|
6395
|
+
return [];
|
|
6396
|
+
}
|
|
6397
|
+
let cumulative = 0;
|
|
6398
|
+
const points = entries.map((entry, displayIndex) => {
|
|
6399
|
+
cumulative += Math.max(entry.value, 0);
|
|
6400
|
+
const percentage = displayIndex === entries.length - 1 ? 100 : cumulative / total * 100;
|
|
6401
|
+
return {
|
|
6402
|
+
x: layout.plotLeft + layout.plotWidth * (displayIndex + 0.5) / entries.length,
|
|
6403
|
+
y: layout.plotBottom - layout.plotHeight * percentage / 100,
|
|
6404
|
+
pointIndex: entry.sourcePointIndex
|
|
6405
|
+
};
|
|
6406
|
+
});
|
|
6407
|
+
const color = series.color ?? "#ED7D31";
|
|
6408
|
+
return [
|
|
6409
|
+
{
|
|
6410
|
+
kind: "polyline",
|
|
6411
|
+
points: points.map((point) => `${point.x},${point.y}`).join(" "),
|
|
6412
|
+
stroke: color,
|
|
6413
|
+
strokeWidth: 2,
|
|
6414
|
+
fill: "none",
|
|
6415
|
+
part: { role: "series", seriesIndex }
|
|
6416
|
+
},
|
|
6417
|
+
...points.map(
|
|
6418
|
+
(point) => ({
|
|
6419
|
+
kind: "circle",
|
|
6420
|
+
cx: point.x,
|
|
6421
|
+
cy: point.y,
|
|
6422
|
+
r: 2.5,
|
|
6423
|
+
fill: color,
|
|
6424
|
+
part: { role: "dataPoint", seriesIndex, pointIndex: point.pointIndex }
|
|
6425
|
+
})
|
|
6426
|
+
)
|
|
6427
|
+
];
|
|
6428
|
+
}
|
|
6429
|
+
function buildParetoAxis(layout) {
|
|
6430
|
+
const axis = buildSecondaryAxis({ min: 0, max: 100, span: 100 }, layout, {
|
|
6431
|
+
majorUnit: 20,
|
|
6432
|
+
majorTickMark: "out"
|
|
6433
|
+
});
|
|
6434
|
+
return {
|
|
6435
|
+
secondaryGridlines: axis.gridlines,
|
|
6436
|
+
secondaryAxisLabels: axis.axisLabels.map((label) => ({
|
|
6437
|
+
...label,
|
|
6438
|
+
text: `${label.text}%`
|
|
6439
|
+
}))
|
|
6440
|
+
};
|
|
6441
|
+
}
|
|
6442
|
+
var DATA_LABEL_COLOR = "#334155";
|
|
6443
|
+
function binLabel(lower, upper, closed) {
|
|
6444
|
+
return closed === "r" ? `(${formatAxisValue2(lower)}, ${formatAxisValue2(upper)}]` : `[${formatAxisValue2(lower)}, ${formatAxisValue2(upper)})`;
|
|
6445
|
+
}
|
|
6446
|
+
function computeHistogramBins(values, options) {
|
|
6447
|
+
const closed = options.intervalClosed ?? "l";
|
|
6448
|
+
const finite = values.map((value, sourceIndex) => ({ value, sourceIndex })).filter((item) => Number.isFinite(item.value));
|
|
6449
|
+
if (finite.length === 0) {
|
|
6450
|
+
return [];
|
|
6451
|
+
}
|
|
6452
|
+
const underflow = typeof options.underflow === "number" ? options.underflow : void 0;
|
|
6453
|
+
const overflow = typeof options.overflow === "number" ? options.overflow : void 0;
|
|
6454
|
+
const under = underflow === void 0 ? [] : finite.filter(
|
|
6455
|
+
(item) => closed === "r" ? item.value <= underflow : item.value < underflow
|
|
6456
|
+
);
|
|
6457
|
+
const over = overflow === void 0 ? [] : finite.filter((item) => closed === "l" ? item.value >= overflow : item.value > overflow);
|
|
6458
|
+
const regular = finite.filter((item) => !under.includes(item) && !over.includes(item));
|
|
6459
|
+
const result = [];
|
|
6460
|
+
if (underflow !== void 0) {
|
|
6461
|
+
result.push({
|
|
6462
|
+
value: under.length,
|
|
6463
|
+
label: `${closed === "r" ? "\u2264" : "<"} ${formatAxisValue2(underflow)}`,
|
|
6464
|
+
sourceIndices: under.map((item) => item.sourceIndex)
|
|
6465
|
+
});
|
|
6466
|
+
}
|
|
6467
|
+
if (regular.length > 0) {
|
|
6468
|
+
const min = Math.min(...regular.map((item) => item.value));
|
|
6469
|
+
const max = Math.max(...regular.map((item) => item.value));
|
|
6470
|
+
const requestedSize = options.binSize && options.binSize > 0 ? options.binSize : void 0;
|
|
6471
|
+
const requestedCount = options.binCount && options.binCount > 0 ? options.binCount : void 0;
|
|
6472
|
+
const start = requestedSize ? Math.floor(min / requestedSize) * requestedSize : min;
|
|
6473
|
+
const count = requestedSize ? Math.max(
|
|
6474
|
+
closed === "l" ? Math.floor((max - start) / requestedSize) + 1 : Math.ceil((max - start) / requestedSize),
|
|
6475
|
+
1
|
|
6476
|
+
) : Math.max(requestedCount ?? Math.ceil(Math.sqrt(regular.length)), 1);
|
|
6477
|
+
const width = requestedSize ?? Math.max((max - start) / count, 1);
|
|
6478
|
+
const bins = Array.from({ length: count }, (_, index) => ({
|
|
6479
|
+
value: 0,
|
|
6480
|
+
label: binLabel(start + index * width, start + (index + 1) * width, closed),
|
|
6481
|
+
sourceIndices: []
|
|
6482
|
+
}));
|
|
6483
|
+
for (const item of regular) {
|
|
6484
|
+
const rawIndex = closed === "r" ? Math.ceil((item.value - start) / width) - 1 : Math.floor((item.value - start) / width);
|
|
6485
|
+
const index = Math.max(0, Math.min(rawIndex, bins.length - 1));
|
|
6486
|
+
bins[index].value += 1;
|
|
6487
|
+
bins[index].sourceIndices.push(item.sourceIndex);
|
|
6488
|
+
}
|
|
6489
|
+
result.push(...bins);
|
|
6490
|
+
}
|
|
6491
|
+
if (overflow !== void 0) {
|
|
6492
|
+
result.push({
|
|
6493
|
+
value: over.length,
|
|
6494
|
+
label: `${closed === "l" ? "\u2265" : ">"} ${formatAxisValue2(overflow)}`,
|
|
6495
|
+
sourceIndices: over.map((item) => item.sourceIndex)
|
|
6496
|
+
});
|
|
6497
|
+
}
|
|
6498
|
+
return result;
|
|
6499
|
+
}
|
|
4988
6500
|
function computeHistogramBars(values, catCount, layout, range, seriesColorOverride, colorPalette) {
|
|
4989
6501
|
const count = Math.max(catCount, values.length, 1);
|
|
4990
6502
|
const barWidth = layout.plotWidth / count;
|
|
4991
|
-
return values.map((val2,
|
|
4992
|
-
const x = layout.plotLeft + barWidth * i;
|
|
6503
|
+
return values.map((val2, pointIndex) => {
|
|
4993
6504
|
const zeroY = valueToY2(0, range, layout.plotTop, layout.plotBottom);
|
|
4994
6505
|
const valY = valueToY2(val2, range, layout.plotTop, layout.plotBottom);
|
|
4995
|
-
const y = Math.min(zeroY, valY);
|
|
4996
|
-
const h = Math.max(Math.abs(zeroY - valY), 1);
|
|
4997
6506
|
return {
|
|
4998
|
-
x,
|
|
4999
|
-
y,
|
|
6507
|
+
x: layout.plotLeft + barWidth * pointIndex,
|
|
6508
|
+
y: Math.min(zeroY, valY),
|
|
5000
6509
|
w: Math.max(barWidth - 0.5, 1),
|
|
5001
|
-
h,
|
|
5002
|
-
fill: seriesColorOverride ?? paletteColor2(
|
|
6510
|
+
h: Math.max(Math.abs(zeroY - valY), 1),
|
|
6511
|
+
fill: seriesColorOverride ?? paletteColor2(pointIndex, colorPalette),
|
|
6512
|
+
pointIndex
|
|
5003
6513
|
};
|
|
5004
6514
|
});
|
|
5005
6515
|
}
|
|
5006
6516
|
function buildHistogramViewModel(element, chartData, categoryLabels) {
|
|
5007
|
-
const
|
|
5008
|
-
|
|
5009
|
-
|
|
5010
|
-
const
|
|
5011
|
-
|
|
6517
|
+
const paretoIndex = chartData.series.findIndex(
|
|
6518
|
+
(item) => item.histogramOptions?.layout === "pareto"
|
|
6519
|
+
);
|
|
6520
|
+
const layout = computePlotLayout(element.width, element.height, chartData, true, {
|
|
6521
|
+
hasSecondaryValueAxis: paretoIndex >= 0
|
|
6522
|
+
});
|
|
6523
|
+
const histogramIndex = Math.max(
|
|
6524
|
+
chartData.series.findIndex((series2) => series2.histogramOptions?.layout !== "pareto"),
|
|
6525
|
+
0
|
|
6526
|
+
);
|
|
6527
|
+
const series = chartData.series[histogramIndex];
|
|
6528
|
+
const options = series?.histogramOptions;
|
|
6529
|
+
const bins = options?.layout === "histogram" ? computeHistogramBins(series?.values ?? [], options) : void 0;
|
|
6530
|
+
const baseEntries = (bins?.map((bin) => ({ value: bin.value, label: bin.label })) ?? (series?.values ?? []).map((value, index) => ({
|
|
6531
|
+
value,
|
|
6532
|
+
label: categoryLabels[index] ?? ""
|
|
6533
|
+
}))).map((entry, sourcePointIndex) => ({ ...entry, sourcePointIndex }));
|
|
6534
|
+
const entries = paretoIndex >= 0 ? orderParetoEntries(baseEntries) : baseEntries;
|
|
6535
|
+
const values = entries.map((entry) => entry.value);
|
|
6536
|
+
const labels = entries.map((entry) => entry.label);
|
|
6537
|
+
const range = distributionRange([{ name: series?.name ?? "", values }]);
|
|
5012
6538
|
const bars = computeHistogramBars(
|
|
5013
6539
|
values,
|
|
5014
|
-
|
|
6540
|
+
labels.length,
|
|
5015
6541
|
layout,
|
|
5016
6542
|
range,
|
|
5017
|
-
|
|
6543
|
+
series?.color,
|
|
5018
6544
|
chartData.colorPalette
|
|
5019
6545
|
);
|
|
5020
6546
|
const primitives = bars.map(
|
|
5021
|
-
(
|
|
6547
|
+
(bar, displayIndex) => ({
|
|
5022
6548
|
kind: "rect",
|
|
5023
|
-
x:
|
|
5024
|
-
y:
|
|
5025
|
-
w:
|
|
5026
|
-
h:
|
|
5027
|
-
fill:
|
|
5028
|
-
opacity: 0.85
|
|
6549
|
+
x: bar.x,
|
|
6550
|
+
y: bar.y,
|
|
6551
|
+
w: bar.w,
|
|
6552
|
+
h: bar.h,
|
|
6553
|
+
fill: bar.fill,
|
|
6554
|
+
opacity: 0.85,
|
|
6555
|
+
...bins ? {} : {
|
|
6556
|
+
part: {
|
|
6557
|
+
role: "dataPoint",
|
|
6558
|
+
seriesIndex: histogramIndex,
|
|
6559
|
+
pointIndex: entries[displayIndex]?.sourcePointIndex ?? displayIndex
|
|
6560
|
+
}
|
|
6561
|
+
}
|
|
5029
6562
|
})
|
|
5030
6563
|
);
|
|
5031
|
-
|
|
5032
|
-
|
|
5033
|
-
|
|
5034
|
-
|
|
5035
|
-
if (val2 === void 0) {
|
|
5036
|
-
return;
|
|
5037
|
-
}
|
|
5038
|
-
dataLabels.push({
|
|
5039
|
-
kind: "text",
|
|
5040
|
-
x: b.x + barWidth / 2,
|
|
5041
|
-
y: b.y - 4,
|
|
5042
|
-
text: formatAxisValue2(val2),
|
|
5043
|
-
fontSize: 7,
|
|
5044
|
-
fill: DATA_LABEL_COLOR,
|
|
5045
|
-
textAnchor: "middle"
|
|
5046
|
-
});
|
|
5047
|
-
});
|
|
6564
|
+
if (paretoIndex >= 0) {
|
|
6565
|
+
primitives.push(
|
|
6566
|
+
...buildParetoPrimitives(entries, layout, chartData.series[paretoIndex], paretoIndex)
|
|
6567
|
+
);
|
|
5048
6568
|
}
|
|
6569
|
+
const dataLabels = chartData.style?.hasDataLabels ? bars.map((bar, index) => ({
|
|
6570
|
+
kind: "text",
|
|
6571
|
+
x: bar.x + bar.w / 2,
|
|
6572
|
+
y: bar.y - 4,
|
|
6573
|
+
text: formatAxisValue2(values[index]),
|
|
6574
|
+
fontSize: 7,
|
|
6575
|
+
fill: DATA_LABEL_COLOR,
|
|
6576
|
+
textAnchor: "middle"
|
|
6577
|
+
})) : [];
|
|
5049
6578
|
const { gridlines, axisLabels } = buildGridlinesAndLabels(range, layout);
|
|
5050
|
-
const
|
|
5051
|
-
const catLabels = buildCategoryLabels(categoryLabels, layout, "bar");
|
|
5052
|
-
const legendPos = chartData.style?.legendPosition ?? "b";
|
|
6579
|
+
const paretoAxis = paretoIndex >= 0 ? buildParetoAxis(layout) : void 0;
|
|
5053
6580
|
const { legend, legendX, legendY, legendAnchor } = buildLegend(
|
|
5054
6581
|
chartData.series,
|
|
5055
6582
|
chartData.colorPalette,
|
|
5056
6583
|
layout.svgWidth,
|
|
5057
|
-
|
|
6584
|
+
chartData.style?.legendPosition ?? "b",
|
|
5058
6585
|
layout.svgHeight,
|
|
5059
6586
|
layout.plotTop
|
|
5060
6587
|
);
|
|
5061
|
-
const title = chartData.style?.hasTitle && chartData.title ? chartData.title : void 0;
|
|
5062
6588
|
return {
|
|
5063
6589
|
svgWidth: layout.svgWidth,
|
|
5064
6590
|
svgHeight: layout.svgHeight,
|
|
5065
|
-
title,
|
|
6591
|
+
title: chartData.style?.hasTitle ? chartData.title : void 0,
|
|
5066
6592
|
titleX: layout.svgWidth / 2,
|
|
5067
6593
|
titleY: 12,
|
|
5068
6594
|
gridlines,
|
|
5069
6595
|
axisLabels,
|
|
5070
|
-
zeroLine,
|
|
5071
|
-
categoryLabels:
|
|
6596
|
+
zeroLine: buildZeroLine(range, layout),
|
|
6597
|
+
categoryLabels: buildCategoryLabels(labels, layout, "bar"),
|
|
5072
6598
|
primitives,
|
|
5073
6599
|
dataLabels,
|
|
5074
6600
|
legend: chartData.style?.hasLegend ? legend : [],
|
|
5075
6601
|
legendX,
|
|
5076
6602
|
legendY,
|
|
5077
|
-
legendAnchor
|
|
6603
|
+
legendAnchor,
|
|
6604
|
+
secondaryGridlines: paretoAxis?.secondaryGridlines,
|
|
6605
|
+
secondaryAxisLabels: paretoAxis?.secondaryAxisLabels
|
|
5078
6606
|
};
|
|
5079
6607
|
}
|
|
5080
|
-
function
|
|
5081
|
-
|
|
5082
|
-
|
|
5083
|
-
|
|
5084
|
-
const
|
|
5085
|
-
const
|
|
5086
|
-
|
|
5087
|
-
|
|
5088
|
-
|
|
5089
|
-
|
|
5090
|
-
|
|
5091
|
-
|
|
5092
|
-
};
|
|
6608
|
+
function arcPath(cx, cy, innerRadius, outerRadius, startAngle, endAngle) {
|
|
6609
|
+
const sweep = endAngle - startAngle;
|
|
6610
|
+
const largeArc = sweep > Math.PI ? 1 : 0;
|
|
6611
|
+
const x1 = cx + outerRadius * Math.cos(startAngle);
|
|
6612
|
+
const y1 = cy + outerRadius * Math.sin(startAngle);
|
|
6613
|
+
const x2 = cx + outerRadius * Math.cos(endAngle);
|
|
6614
|
+
const y2 = cy + outerRadius * Math.sin(endAngle);
|
|
6615
|
+
const x3 = cx + innerRadius * Math.cos(endAngle);
|
|
6616
|
+
const y3 = cy + innerRadius * Math.sin(endAngle);
|
|
6617
|
+
const x4 = cx + innerRadius * Math.cos(startAngle);
|
|
6618
|
+
const y4 = cy + innerRadius * Math.sin(startAngle);
|
|
6619
|
+
return `M ${x1} ${y1} A ${outerRadius} ${outerRadius} 0 ${largeArc} 1 ${x2} ${y2} L ${x3} ${y3} A ${innerRadius} ${innerRadius} 0 ${largeArc} 0 ${x4} ${y4} Z`;
|
|
5093
6620
|
}
|
|
5094
|
-
function
|
|
5095
|
-
const
|
|
5096
|
-
const
|
|
5097
|
-
const boxOffset = (boxGroupW - boxW) / 2;
|
|
6621
|
+
function computeSunburstArcs(series, cx, cy, maxR, colorPalette) {
|
|
6622
|
+
const seriesCount = Math.max(series.length, 1);
|
|
6623
|
+
const ringWidth = maxR / (seriesCount + 0.5);
|
|
5098
6624
|
const out = [];
|
|
5099
|
-
for (let
|
|
5100
|
-
const
|
|
5101
|
-
const
|
|
5102
|
-
|
|
5103
|
-
|
|
6625
|
+
for (let si = 0; si < series.length; si++) {
|
|
6626
|
+
const values = series[si].values;
|
|
6627
|
+
const innerRadius = ringWidth * (si + 0.5);
|
|
6628
|
+
const outerRadius = ringWidth * (si + 1.5);
|
|
6629
|
+
const total = values.reduce((acc, value) => acc + Math.abs(value), 0) || 1;
|
|
6630
|
+
let startAngle = -Math.PI / 2;
|
|
6631
|
+
for (let pointIndex = 0; pointIndex < values.length; pointIndex++) {
|
|
6632
|
+
const endAngle = startAngle + Math.abs(values[pointIndex]) / total * Math.PI * 2;
|
|
6633
|
+
out.push({
|
|
6634
|
+
d: arcPath(cx, cy, innerRadius, outerRadius, startAngle, endAngle),
|
|
6635
|
+
fill: paletteColor2(pointIndex, colorPalette),
|
|
6636
|
+
opacity: Math.max(0.1, 0.9 - si * 0.1),
|
|
6637
|
+
pointIndex,
|
|
6638
|
+
level: si
|
|
6639
|
+
});
|
|
6640
|
+
startAngle = endAngle;
|
|
5104
6641
|
}
|
|
5105
|
-
const boxX = layout.plotLeft + boxGroupW * ci + boxOffset;
|
|
5106
|
-
out.push({
|
|
5107
|
-
stats,
|
|
5108
|
-
boxX,
|
|
5109
|
-
boxW,
|
|
5110
|
-
xMid: boxX + boxW / 2,
|
|
5111
|
-
yMin: valueToY2(stats.min, range, layout.plotTop, layout.plotBottom),
|
|
5112
|
-
yMax: valueToY2(stats.max, range, layout.plotTop, layout.plotBottom),
|
|
5113
|
-
yQ1: valueToY2(stats.q1, range, layout.plotTop, layout.plotBottom),
|
|
5114
|
-
yQ3: valueToY2(stats.q3, range, layout.plotTop, layout.plotBottom),
|
|
5115
|
-
yMed: valueToY2(stats.median, range, layout.plotTop, layout.plotBottom),
|
|
5116
|
-
fill: paletteColor2(ci, colorPalette)
|
|
5117
|
-
});
|
|
5118
6642
|
}
|
|
5119
6643
|
return out;
|
|
5120
6644
|
}
|
|
5121
|
-
function
|
|
5122
|
-
|
|
5123
|
-
|
|
5124
|
-
|
|
5125
|
-
const
|
|
5126
|
-
|
|
5127
|
-
|
|
5128
|
-
|
|
5129
|
-
|
|
5130
|
-
|
|
5131
|
-
);
|
|
5132
|
-
const
|
|
5133
|
-
|
|
5134
|
-
|
|
5135
|
-
|
|
5136
|
-
|
|
5137
|
-
|
|
5138
|
-
|
|
5139
|
-
|
|
5140
|
-
|
|
5141
|
-
|
|
5142
|
-
|
|
5143
|
-
|
|
5144
|
-
|
|
5145
|
-
|
|
5146
|
-
|
|
5147
|
-
|
|
5148
|
-
|
|
5149
|
-
|
|
5150
|
-
|
|
5151
|
-
strokeWidth: 1
|
|
6645
|
+
function hierarchyKey(levels, level, index) {
|
|
6646
|
+
return levels.slice(level).map((labels) => labels[index] ?? "").join("\0");
|
|
6647
|
+
}
|
|
6648
|
+
function computeHierarchicalSunburstArcs(categoryLevels, values, cx, cy, maxR, colorPalette) {
|
|
6649
|
+
const levels = categoryLevels.filter((level) => level.length > 0);
|
|
6650
|
+
const leafCount = Math.max(values.length, ...levels.map((level) => level.length), 0);
|
|
6651
|
+
if (levels.length === 0 || leafCount === 0) {
|
|
6652
|
+
return [];
|
|
6653
|
+
}
|
|
6654
|
+
const rawWeights = Array.from({ length: leafCount }, (_, index) => Math.abs(values[index] ?? 0));
|
|
6655
|
+
const weights = rawWeights.some((value) => value !== 0) ? rawWeights : rawWeights.map(() => 1);
|
|
6656
|
+
const total = weights.reduce((sum, value) => sum + value, 0);
|
|
6657
|
+
const prefix = [0];
|
|
6658
|
+
for (const weight of weights) {
|
|
6659
|
+
prefix.push((prefix[prefix.length - 1] ?? 0) + weight);
|
|
6660
|
+
}
|
|
6661
|
+
const ringWidth = maxR / (levels.length + 0.5);
|
|
6662
|
+
const rootLabels = levels[levels.length - 1] ?? [];
|
|
6663
|
+
const rootColors = /* @__PURE__ */ new Map();
|
|
6664
|
+
const out = [];
|
|
6665
|
+
for (let level = levels.length - 1; level >= 0; level--) {
|
|
6666
|
+
const ringIndex = levels.length - 1 - level;
|
|
6667
|
+
const innerRadius = ringWidth * (ringIndex + 0.5);
|
|
6668
|
+
const outerRadius = ringWidth * (ringIndex + 1.5);
|
|
6669
|
+
let start = 0;
|
|
6670
|
+
while (start < leafCount) {
|
|
6671
|
+
const key = hierarchyKey(levels, level, start);
|
|
6672
|
+
let end = start + 1;
|
|
6673
|
+
while (end < leafCount && hierarchyKey(levels, level, end) === key) {
|
|
6674
|
+
end++;
|
|
5152
6675
|
}
|
|
5153
|
-
|
|
5154
|
-
|
|
5155
|
-
|
|
5156
|
-
|
|
5157
|
-
|
|
5158
|
-
y1: g.yMax,
|
|
5159
|
-
x2: g.boxX + g.boxW * 0.75,
|
|
5160
|
-
y2: g.yMax,
|
|
5161
|
-
stroke: WHISKER_COLOR,
|
|
5162
|
-
strokeWidth: 1
|
|
5163
|
-
},
|
|
5164
|
-
{
|
|
5165
|
-
kind: "line",
|
|
5166
|
-
x1: g.boxX + g.boxW * 0.25,
|
|
5167
|
-
y1: g.yMin,
|
|
5168
|
-
x2: g.boxX + g.boxW * 0.75,
|
|
5169
|
-
y2: g.yMin,
|
|
5170
|
-
stroke: WHISKER_COLOR,
|
|
5171
|
-
strokeWidth: 1
|
|
6676
|
+
const startAngle = -Math.PI / 2 + prefix[start] / total * Math.PI * 2;
|
|
6677
|
+
const endAngle = -Math.PI / 2 + prefix[end] / total * Math.PI * 2;
|
|
6678
|
+
const rootLabel = rootLabels[start] ?? "";
|
|
6679
|
+
if (!rootColors.has(rootLabel)) {
|
|
6680
|
+
rootColors.set(rootLabel, rootColors.size);
|
|
5172
6681
|
}
|
|
5173
|
-
|
|
5174
|
-
|
|
5175
|
-
|
|
5176
|
-
|
|
5177
|
-
|
|
5178
|
-
|
|
5179
|
-
|
|
5180
|
-
|
|
5181
|
-
|
|
5182
|
-
|
|
5183
|
-
});
|
|
5184
|
-
primitives.push({
|
|
5185
|
-
kind: "line",
|
|
5186
|
-
x1: g.boxX,
|
|
5187
|
-
y1: g.yMed,
|
|
5188
|
-
x2: g.boxX + g.boxW,
|
|
5189
|
-
y2: g.yMed,
|
|
5190
|
-
stroke: MEDIAN_COLOR,
|
|
5191
|
-
strokeWidth: 2
|
|
5192
|
-
});
|
|
6682
|
+
out.push({
|
|
6683
|
+
d: arcPath(cx, cy, innerRadius, outerRadius, startAngle, endAngle),
|
|
6684
|
+
fill: paletteColor2(rootColors.get(rootLabel) ?? start, colorPalette),
|
|
6685
|
+
opacity: Math.max(0.1, 0.9 - ringIndex * 0.1),
|
|
6686
|
+
level: ringIndex,
|
|
6687
|
+
label: levels[level][start] ?? "",
|
|
6688
|
+
...level === 0 && end === start + 1 ? { pointIndex: start } : {}
|
|
6689
|
+
});
|
|
6690
|
+
start = end;
|
|
6691
|
+
}
|
|
5193
6692
|
}
|
|
5194
|
-
|
|
5195
|
-
const zeroLine = buildZeroLine(range, layout);
|
|
5196
|
-
const catLabels = buildCategoryLabels(categoryLabels, layout, "bar");
|
|
5197
|
-
const legendPos = chartData.style?.legendPosition ?? "b";
|
|
5198
|
-
const { legend, legendX, legendY, legendAnchor } = buildLegend(
|
|
5199
|
-
chartData.series,
|
|
5200
|
-
chartData.colorPalette,
|
|
5201
|
-
layout.svgWidth,
|
|
5202
|
-
legendPos,
|
|
5203
|
-
layout.svgHeight,
|
|
5204
|
-
layout.plotTop
|
|
5205
|
-
);
|
|
5206
|
-
const catLegend = categoryLabels.map((label, i) => ({
|
|
5207
|
-
color: paletteColor2(i, chartData.colorPalette),
|
|
5208
|
-
label
|
|
5209
|
-
}));
|
|
5210
|
-
const title = chartData.style?.hasTitle && chartData.title ? chartData.title : void 0;
|
|
5211
|
-
return {
|
|
5212
|
-
svgWidth: layout.svgWidth,
|
|
5213
|
-
svgHeight: layout.svgHeight,
|
|
5214
|
-
title,
|
|
5215
|
-
titleX: layout.svgWidth / 2,
|
|
5216
|
-
titleY: 12,
|
|
5217
|
-
gridlines,
|
|
5218
|
-
axisLabels,
|
|
5219
|
-
zeroLine,
|
|
5220
|
-
categoryLabels: catLabels,
|
|
5221
|
-
primitives,
|
|
5222
|
-
dataLabels: [],
|
|
5223
|
-
legend: chartData.style?.hasLegend ? catLegend.length > 0 ? catLegend : legend : [],
|
|
5224
|
-
legendX,
|
|
5225
|
-
legendY,
|
|
5226
|
-
legendAnchor
|
|
5227
|
-
};
|
|
6693
|
+
return out;
|
|
5228
6694
|
}
|
|
5229
6695
|
function emptyChrome() {
|
|
5230
6696
|
return {
|
|
@@ -5321,45 +6787,20 @@ function buildFunnelViewModel(element, chartData, categoryLabels) {
|
|
|
5321
6787
|
legendAnchor: "middle"
|
|
5322
6788
|
};
|
|
5323
6789
|
}
|
|
5324
|
-
function computeSunburstArcs(series, cx, cy, maxR, colorPalette) {
|
|
5325
|
-
const seriesCount = Math.max(series.length, 1);
|
|
5326
|
-
const ringWidth = maxR / (seriesCount + 0.5);
|
|
5327
|
-
const out = [];
|
|
5328
|
-
for (let si = 0; si < series.length; si++) {
|
|
5329
|
-
const s = series[si];
|
|
5330
|
-
const iR = ringWidth * (si + 0.5);
|
|
5331
|
-
const oR = ringWidth * (si + 1.5);
|
|
5332
|
-
const total = s.values.reduce((acc, v) => acc + Math.abs(v), 0) || 1;
|
|
5333
|
-
let startAngle = -Math.PI / 2;
|
|
5334
|
-
for (let vi = 0; vi < s.values.length; vi++) {
|
|
5335
|
-
const val2 = s.values[vi];
|
|
5336
|
-
const sweep = Math.abs(val2) / total * Math.PI * 2;
|
|
5337
|
-
const endAngle = startAngle + sweep;
|
|
5338
|
-
const largeArc = sweep > Math.PI ? 1 : 0;
|
|
5339
|
-
const x1 = cx + oR * Math.cos(startAngle);
|
|
5340
|
-
const y1 = cy + oR * Math.sin(startAngle);
|
|
5341
|
-
const x2 = cx + oR * Math.cos(endAngle);
|
|
5342
|
-
const y2 = cy + oR * Math.sin(endAngle);
|
|
5343
|
-
const x3 = cx + iR * Math.cos(endAngle);
|
|
5344
|
-
const y3 = cy + iR * Math.sin(endAngle);
|
|
5345
|
-
const x4 = cx + iR * Math.cos(startAngle);
|
|
5346
|
-
const y4 = cy + iR * Math.sin(startAngle);
|
|
5347
|
-
out.push({
|
|
5348
|
-
d: `M ${x1} ${y1} A ${oR} ${oR} 0 ${largeArc} 1 ${x2} ${y2} L ${x3} ${y3} A ${iR} ${iR} 0 ${largeArc} 0 ${x4} ${y4} Z`,
|
|
5349
|
-
fill: paletteColor2(vi, colorPalette),
|
|
5350
|
-
opacity: Math.max(0.1, 0.9 - si * 0.1)
|
|
5351
|
-
});
|
|
5352
|
-
startAngle = endAngle;
|
|
5353
|
-
}
|
|
5354
|
-
}
|
|
5355
|
-
return out;
|
|
5356
|
-
}
|
|
5357
6790
|
function buildSunburstViewModel(element, chartData, categoryLabels) {
|
|
5358
6791
|
const layout = computePlotLayout(element.width, element.height, chartData, false);
|
|
5359
6792
|
const cx = layout.plotLeft + layout.plotWidth / 2;
|
|
5360
6793
|
const cy = layout.plotTop + layout.plotHeight / 2;
|
|
5361
6794
|
const maxR = Math.min(layout.plotWidth, layout.plotHeight) / 2 - 4;
|
|
5362
|
-
const
|
|
6795
|
+
const categoryLevels = chartData.categoryLevels;
|
|
6796
|
+
const arcs = categoryLevels?.length ? computeHierarchicalSunburstArcs(
|
|
6797
|
+
categoryLevels,
|
|
6798
|
+
chartData.series[0]?.values ?? [],
|
|
6799
|
+
cx,
|
|
6800
|
+
cy,
|
|
6801
|
+
maxR,
|
|
6802
|
+
chartData.colorPalette
|
|
6803
|
+
) : computeSunburstArcs(chartData.series, cx, cy, maxR, chartData.colorPalette);
|
|
5363
6804
|
const primitives = arcs.map(
|
|
5364
6805
|
(arc) => ({
|
|
5365
6806
|
kind: "path",
|
|
@@ -5367,10 +6808,12 @@ function buildSunburstViewModel(element, chartData, categoryLabels) {
|
|
|
5367
6808
|
fill: arc.fill,
|
|
5368
6809
|
stroke: "#ffffff",
|
|
5369
6810
|
strokeWidth: 1,
|
|
5370
|
-
opacity: arc.opacity
|
|
6811
|
+
opacity: arc.opacity,
|
|
6812
|
+
part: arc.pointIndex === void 0 ? void 0 : { role: "dataPoint", seriesIndex: 0, pointIndex: arc.pointIndex }
|
|
5371
6813
|
})
|
|
5372
6814
|
);
|
|
5373
|
-
const
|
|
6815
|
+
const legendLabels = categoryLevels?.length ? [...new Set(categoryLevels[categoryLevels.length - 1]?.filter(Boolean) ?? categoryLabels)] : categoryLabels;
|
|
6816
|
+
const legend = chartData.style?.hasLegend ? legendLabels.map((label, i) => ({ color: paletteColor2(i, chartData.colorPalette), label })) : [];
|
|
5374
6817
|
const title = chartData.style?.hasTitle && chartData.title ? chartData.title : void 0;
|
|
5375
6818
|
return {
|
|
5376
6819
|
svgWidth: layout.svgWidth,
|
|
@@ -5387,6 +6830,176 @@ function buildSunburstViewModel(element, chartData, categoryLabels) {
|
|
|
5387
6830
|
legendAnchor: "middle"
|
|
5388
6831
|
};
|
|
5389
6832
|
}
|
|
6833
|
+
function normalizedLevels(levels, pointCount) {
|
|
6834
|
+
return levels.map((level) => {
|
|
6835
|
+
let previous = "";
|
|
6836
|
+
return Array.from({ length: pointCount }, (_, index) => {
|
|
6837
|
+
const label = level[index]?.trim() ?? "";
|
|
6838
|
+
if (label) {
|
|
6839
|
+
previous = label;
|
|
6840
|
+
}
|
|
6841
|
+
return label || previous;
|
|
6842
|
+
});
|
|
6843
|
+
});
|
|
6844
|
+
}
|
|
6845
|
+
function findOrAddParent(children, label, colorIndex, parentLabelLayout) {
|
|
6846
|
+
const existing = children.find((child2) => child2.children && child2.label === label);
|
|
6847
|
+
if (existing) {
|
|
6848
|
+
return existing;
|
|
6849
|
+
}
|
|
6850
|
+
const parent = {
|
|
6851
|
+
label,
|
|
6852
|
+
weight: 0,
|
|
6853
|
+
children: [],
|
|
6854
|
+
colorIndex,
|
|
6855
|
+
parentLabelLayout
|
|
6856
|
+
};
|
|
6857
|
+
children.push(parent);
|
|
6858
|
+
return parent;
|
|
6859
|
+
}
|
|
6860
|
+
function buildSeriesHierarchy(series, seriesIndex, categoryLabels, categoryLevels, colorStride) {
|
|
6861
|
+
const pointCount = series.values.length;
|
|
6862
|
+
const levels = normalizedLevels(categoryLevels ?? [], pointCount);
|
|
6863
|
+
const layout = series.treemapOptions?.parentLabelLayout ?? "banner";
|
|
6864
|
+
const roots = [];
|
|
6865
|
+
for (let pointIndex = 0; pointIndex < pointCount; pointIndex++) {
|
|
6866
|
+
let children = roots;
|
|
6867
|
+
for (let levelIndex = levels.length - 1; levelIndex >= 1; levelIndex--) {
|
|
6868
|
+
const label = levels[levelIndex][pointIndex] || `Group ${levelIndex}`;
|
|
6869
|
+
children = findOrAddParent(children, label, pointIndex, layout).children;
|
|
6870
|
+
}
|
|
6871
|
+
children.push({
|
|
6872
|
+
label: levels[0]?.[pointIndex] || categoryLabels[pointIndex] || String(pointIndex + 1),
|
|
6873
|
+
weight: Math.abs(series.values[pointIndex] ?? 0),
|
|
6874
|
+
seriesIndex,
|
|
6875
|
+
pointIndex,
|
|
6876
|
+
colorIndex: seriesIndex * colorStride + pointIndex,
|
|
6877
|
+
parentLabelLayout: layout
|
|
6878
|
+
});
|
|
6879
|
+
}
|
|
6880
|
+
return roots;
|
|
6881
|
+
}
|
|
6882
|
+
function aggregate(node) {
|
|
6883
|
+
if (node.children) {
|
|
6884
|
+
node.weight = node.children.reduce((sum, child2) => sum + aggregate(child2), 0);
|
|
6885
|
+
}
|
|
6886
|
+
return node.weight;
|
|
6887
|
+
}
|
|
6888
|
+
function splitNodes(nodes, box) {
|
|
6889
|
+
const sorted = [...nodes].sort((a, b) => b.weight - a.weight);
|
|
6890
|
+
const total = sorted.reduce((sum, node) => sum + node.weight, 0);
|
|
6891
|
+
const fallback = total === 0 ? sorted.length : total;
|
|
6892
|
+
let remaining = fallback;
|
|
6893
|
+
let current = { ...box };
|
|
6894
|
+
return sorted.map((node) => {
|
|
6895
|
+
const weight = total === 0 ? 1 : node.weight;
|
|
6896
|
+
const fraction = remaining > 0 ? weight / remaining : 0;
|
|
6897
|
+
const splitWidth = current.w >= current.h;
|
|
6898
|
+
const allocated = splitWidth ? { ...current, w: current.w * fraction } : { ...current, h: current.h * fraction };
|
|
6899
|
+
if (splitWidth) {
|
|
6900
|
+
current = { ...current, x: current.x + allocated.w, w: current.w - allocated.w };
|
|
6901
|
+
} else {
|
|
6902
|
+
current = { ...current, y: current.y + allocated.h, h: current.h - allocated.h };
|
|
6903
|
+
}
|
|
6904
|
+
remaining -= weight;
|
|
6905
|
+
return [node, allocated];
|
|
6906
|
+
});
|
|
6907
|
+
}
|
|
6908
|
+
function renderNodes(nodes, box, colorPalette, primitives) {
|
|
6909
|
+
for (const [node, allocation] of splitNodes(nodes, box)) {
|
|
6910
|
+
const cell = {
|
|
6911
|
+
x: allocation.x + 1,
|
|
6912
|
+
y: allocation.y + 1,
|
|
6913
|
+
w: Math.max(allocation.w - 2, 1),
|
|
6914
|
+
h: Math.max(allocation.h - 2, 1)
|
|
6915
|
+
};
|
|
6916
|
+
if (node.children) {
|
|
6917
|
+
const showParent = node.parentLabelLayout !== "none" && cell.w > 28 && cell.h > 16;
|
|
6918
|
+
if (showParent) {
|
|
6919
|
+
primitives.push({
|
|
6920
|
+
kind: "text",
|
|
6921
|
+
x: cell.x + 4,
|
|
6922
|
+
y: cell.y + 11,
|
|
6923
|
+
text: node.label,
|
|
6924
|
+
fontSize: 10,
|
|
6925
|
+
fill: "#303030",
|
|
6926
|
+
textAnchor: "start",
|
|
6927
|
+
fontWeight: "bold"
|
|
6928
|
+
});
|
|
6929
|
+
}
|
|
6930
|
+
const bannerHeight = showParent && node.parentLabelLayout === "banner" ? 16 : 0;
|
|
6931
|
+
renderNodes(
|
|
6932
|
+
node.children,
|
|
6933
|
+
{ ...cell, y: cell.y + bannerHeight, h: Math.max(cell.h - bannerHeight, 1) },
|
|
6934
|
+
colorPalette,
|
|
6935
|
+
primitives
|
|
6936
|
+
);
|
|
6937
|
+
continue;
|
|
6938
|
+
}
|
|
6939
|
+
primitives.push({
|
|
6940
|
+
kind: "rect",
|
|
6941
|
+
...cell,
|
|
6942
|
+
fill: paletteColor2(node.colorIndex, colorPalette),
|
|
6943
|
+
rx: 2,
|
|
6944
|
+
opacity: 0.85,
|
|
6945
|
+
part: {
|
|
6946
|
+
role: "dataPoint",
|
|
6947
|
+
seriesIndex: node.seriesIndex,
|
|
6948
|
+
pointIndex: node.pointIndex
|
|
6949
|
+
}
|
|
6950
|
+
});
|
|
6951
|
+
if (cell.w > 30 && cell.h > 14) {
|
|
6952
|
+
primitives.push({
|
|
6953
|
+
kind: "text",
|
|
6954
|
+
x: cell.x + cell.w / 2,
|
|
6955
|
+
y: cell.y + cell.h / 2,
|
|
6956
|
+
text: node.label,
|
|
6957
|
+
fontSize: Math.min(10, cell.h * 0.3),
|
|
6958
|
+
fill: "#ffffff",
|
|
6959
|
+
textAnchor: "middle",
|
|
6960
|
+
fontWeight: "bold",
|
|
6961
|
+
dominantBaseline: "central"
|
|
6962
|
+
});
|
|
6963
|
+
}
|
|
6964
|
+
}
|
|
6965
|
+
}
|
|
6966
|
+
function buildHierarchicalTreemapPrimitives(chartData, categoryLabels, box) {
|
|
6967
|
+
const colorStride = Math.max(
|
|
6968
|
+
categoryLabels.length,
|
|
6969
|
+
...chartData.series.map((s) => s.values.length),
|
|
6970
|
+
1
|
|
6971
|
+
);
|
|
6972
|
+
let roots = chartData.series.flatMap(
|
|
6973
|
+
(series, seriesIndex) => buildSeriesHierarchy(
|
|
6974
|
+
series,
|
|
6975
|
+
seriesIndex,
|
|
6976
|
+
categoryLabels,
|
|
6977
|
+
chartData.categoryLevels,
|
|
6978
|
+
colorStride
|
|
6979
|
+
)
|
|
6980
|
+
);
|
|
6981
|
+
if (chartData.series.length > 1) {
|
|
6982
|
+
roots = chartData.series.map((series, seriesIndex) => ({
|
|
6983
|
+
label: series.name,
|
|
6984
|
+
weight: 0,
|
|
6985
|
+
children: buildSeriesHierarchy(
|
|
6986
|
+
series,
|
|
6987
|
+
seriesIndex,
|
|
6988
|
+
categoryLabels,
|
|
6989
|
+
chartData.categoryLevels,
|
|
6990
|
+
colorStride
|
|
6991
|
+
),
|
|
6992
|
+
colorIndex: seriesIndex * colorStride,
|
|
6993
|
+
parentLabelLayout: series.treemapOptions?.parentLabelLayout ?? "banner"
|
|
6994
|
+
}));
|
|
6995
|
+
}
|
|
6996
|
+
for (const root of roots) {
|
|
6997
|
+
aggregate(root);
|
|
6998
|
+
}
|
|
6999
|
+
const primitives = [];
|
|
7000
|
+
renderNodes(roots, box, chartData.colorPalette, primitives);
|
|
7001
|
+
return primitives;
|
|
7002
|
+
}
|
|
5390
7003
|
var ISO_COS30 = Math.cos(Math.PI / 6);
|
|
5391
7004
|
var ISO_SIN30 = Math.sin(Math.PI / 6);
|
|
5392
7005
|
function isoProject(x, y, z) {
|
|
@@ -5554,69 +7167,26 @@ function buildFlatSurfaceViewModel(element, chartData, categoryLabels) {
|
|
|
5554
7167
|
primitives,
|
|
5555
7168
|
legend: chartData.style?.hasLegend ? legend : [],
|
|
5556
7169
|
legendX,
|
|
5557
|
-
legendY,
|
|
5558
|
-
legendAnchor
|
|
5559
|
-
};
|
|
5560
|
-
}
|
|
5561
|
-
function buildSurfaceViewModel(element, chartData, categoryLabels) {
|
|
5562
|
-
const catCount = Math.max(categoryLabels.length, 1);
|
|
5563
|
-
const seriesCount = chartData.series.length;
|
|
5564
|
-
if (seriesCount >= 2 && catCount >= 2) {
|
|
5565
|
-
return buildIsometricSurfaceViewModel(element, chartData, categoryLabels);
|
|
5566
|
-
}
|
|
5567
|
-
return buildFlatSurfaceViewModel(element, chartData, categoryLabels);
|
|
5568
|
-
}
|
|
5569
|
-
function buildTreemapViewModel(element, chartData, categoryLabels) {
|
|
5570
|
-
const layout = computePlotLayout(element.width, element.height, chartData, false);
|
|
5571
|
-
const allValues = chartData.series.flatMap((s) => s.values);
|
|
5572
|
-
const totalAbs = allValues.reduce((sum, v) => sum + Math.abs(v), 0) || 1;
|
|
5573
|
-
const primitives = [];
|
|
5574
|
-
let curX = layout.plotLeft;
|
|
5575
|
-
let curY = layout.plotTop;
|
|
5576
|
-
let remainW = layout.plotWidth;
|
|
5577
|
-
let remainH = layout.plotHeight;
|
|
5578
|
-
let remainTotal = totalAbs;
|
|
5579
|
-
const items = allValues.map((v, i) => ({ value: Math.abs(v), index: i })).sort((a, b) => b.value - a.value);
|
|
5580
|
-
for (const item of items) {
|
|
5581
|
-
const fraction = remainTotal > 0 ? item.value / remainTotal : 0;
|
|
5582
|
-
const useWidth = remainW >= remainH;
|
|
5583
|
-
const w = useWidth ? remainW * fraction : remainW;
|
|
5584
|
-
const h = useWidth ? remainH : remainH * fraction;
|
|
5585
|
-
const rw = Math.max(w - 1, 1);
|
|
5586
|
-
const rh = Math.max(h - 1, 1);
|
|
5587
|
-
primitives.push({
|
|
5588
|
-
kind: "rect",
|
|
5589
|
-
x: curX,
|
|
5590
|
-
y: curY,
|
|
5591
|
-
w: rw,
|
|
5592
|
-
h: rh,
|
|
5593
|
-
fill: paletteColor2(item.index, chartData.colorPalette),
|
|
5594
|
-
rx: 2,
|
|
5595
|
-
opacity: 0.85
|
|
5596
|
-
});
|
|
5597
|
-
const label = categoryLabels[item.index] ?? String(item.index + 1);
|
|
5598
|
-
if (rw > 30 && rh > 14) {
|
|
5599
|
-
primitives.push({
|
|
5600
|
-
kind: "text",
|
|
5601
|
-
x: curX + rw / 2,
|
|
5602
|
-
y: curY + rh / 2,
|
|
5603
|
-
text: label,
|
|
5604
|
-
fontSize: Math.min(10, rh * 0.3),
|
|
5605
|
-
fill: "#ffffff",
|
|
5606
|
-
textAnchor: "middle",
|
|
5607
|
-
fontWeight: "bold",
|
|
5608
|
-
dominantBaseline: "central"
|
|
5609
|
-
});
|
|
5610
|
-
}
|
|
5611
|
-
if (useWidth) {
|
|
5612
|
-
curX += w;
|
|
5613
|
-
remainW -= w;
|
|
5614
|
-
} else {
|
|
5615
|
-
curY += h;
|
|
5616
|
-
remainH -= h;
|
|
5617
|
-
}
|
|
5618
|
-
remainTotal -= item.value;
|
|
7170
|
+
legendY,
|
|
7171
|
+
legendAnchor
|
|
7172
|
+
};
|
|
7173
|
+
}
|
|
7174
|
+
function buildSurfaceViewModel(element, chartData, categoryLabels) {
|
|
7175
|
+
const catCount = Math.max(categoryLabels.length, 1);
|
|
7176
|
+
const seriesCount = chartData.series.length;
|
|
7177
|
+
if (seriesCount >= 2 && catCount >= 2) {
|
|
7178
|
+
return buildIsometricSurfaceViewModel(element, chartData, categoryLabels);
|
|
5619
7179
|
}
|
|
7180
|
+
return buildFlatSurfaceViewModel(element, chartData, categoryLabels);
|
|
7181
|
+
}
|
|
7182
|
+
function buildTreemapViewModel(element, chartData, categoryLabels) {
|
|
7183
|
+
const layout = computePlotLayout(element.width, element.height, chartData, false);
|
|
7184
|
+
const primitives = buildHierarchicalTreemapPrimitives(chartData, categoryLabels, {
|
|
7185
|
+
x: layout.plotLeft,
|
|
7186
|
+
y: layout.plotTop,
|
|
7187
|
+
w: layout.plotWidth,
|
|
7188
|
+
h: layout.plotHeight
|
|
7189
|
+
});
|
|
5620
7190
|
const legendPos = chartData.style?.legendPosition ?? "b";
|
|
5621
7191
|
const { legend, legendX, legendY, legendAnchor } = buildLegend(
|
|
5622
7192
|
chartData.series,
|
|
@@ -5646,31 +7216,144 @@ function buildTreemapViewModel(element, chartData, categoryLabels) {
|
|
|
5646
7216
|
legendAnchor
|
|
5647
7217
|
};
|
|
5648
7218
|
}
|
|
7219
|
+
function sourceIndices(indices, length) {
|
|
7220
|
+
return Array.from({ length }, (_, position) => indices?.[position] ?? position);
|
|
7221
|
+
}
|
|
7222
|
+
function valueAtSource(values, indices, sourceIndex) {
|
|
7223
|
+
const position = indices.indexOf(sourceIndex);
|
|
7224
|
+
return position >= 0 ? values[position] : void 0;
|
|
7225
|
+
}
|
|
7226
|
+
function cachedEntityName(cache, entityId) {
|
|
7227
|
+
if (!cache || typeof cache !== "object") {
|
|
7228
|
+
return void 0;
|
|
7229
|
+
}
|
|
7230
|
+
const record = cache;
|
|
7231
|
+
if (String(record["@_entityId"] ?? "") === entityId && record["@_entityName"] !== void 0) {
|
|
7232
|
+
return String(record["@_entityName"]);
|
|
7233
|
+
}
|
|
7234
|
+
for (const value of Object.values(record)) {
|
|
7235
|
+
if (Array.isArray(value)) {
|
|
7236
|
+
for (const item of value) {
|
|
7237
|
+
const name = cachedEntityName(item, entityId);
|
|
7238
|
+
if (name) {
|
|
7239
|
+
return name;
|
|
7240
|
+
}
|
|
7241
|
+
}
|
|
7242
|
+
} else {
|
|
7243
|
+
const name = cachedEntityName(value, entityId);
|
|
7244
|
+
if (name) {
|
|
7245
|
+
return name;
|
|
7246
|
+
}
|
|
7247
|
+
}
|
|
7248
|
+
}
|
|
7249
|
+
return void 0;
|
|
7250
|
+
}
|
|
7251
|
+
function resolveRegionEntityCode(entityId, options, resolveCode) {
|
|
7252
|
+
if (!entityId) {
|
|
7253
|
+
return void 0;
|
|
7254
|
+
}
|
|
7255
|
+
const direct = resolveCode(entityId);
|
|
7256
|
+
if (direct) {
|
|
7257
|
+
return direct;
|
|
7258
|
+
}
|
|
7259
|
+
const tokens = entityId.split(/[:/|._-]+/u).filter(Boolean);
|
|
7260
|
+
for (let index = tokens.length - 1; index >= 0; index--) {
|
|
7261
|
+
const resolved = resolveCode(tokens[index] ?? "");
|
|
7262
|
+
if (resolved) {
|
|
7263
|
+
return resolved;
|
|
7264
|
+
}
|
|
7265
|
+
}
|
|
7266
|
+
const cachedName = cachedEntityName(options?.geographyCache, entityId);
|
|
7267
|
+
return cachedName ? resolveCode(cachedName) : void 0;
|
|
7268
|
+
}
|
|
7269
|
+
function buildRegionMapEntries(categories, values, options, resolveCode) {
|
|
7270
|
+
const entityIds = options?.entityIds ?? [];
|
|
7271
|
+
const categoryIndices = sourceIndices(options?.categorySourceIndices, categories.length);
|
|
7272
|
+
const valueIndices = sourceIndices(options?.valueSourceIndices, values.length);
|
|
7273
|
+
const entityIndices = sourceIndices(options?.entityIdSourceIndices, entityIds.length);
|
|
7274
|
+
const indices = [.../* @__PURE__ */ new Set([...categoryIndices, ...valueIndices, ...entityIndices])].sort(
|
|
7275
|
+
(a, b) => a - b
|
|
7276
|
+
);
|
|
7277
|
+
return indices.map((sourceIndex) => {
|
|
7278
|
+
const category = valueAtSource(categories, categoryIndices, sourceIndex) ?? "";
|
|
7279
|
+
const value = valueAtSource(values, valueIndices, sourceIndex) ?? 0;
|
|
7280
|
+
const entityId = valueAtSource(entityIds, entityIndices, sourceIndex);
|
|
7281
|
+
return {
|
|
7282
|
+
sourceIndex,
|
|
7283
|
+
label: category || entityId || `Region ${sourceIndex + 1}`,
|
|
7284
|
+
value,
|
|
7285
|
+
...entityId ? { entityId } : {},
|
|
7286
|
+
code: resolveRegionEntityCode(entityId, options, resolveCode) ?? resolveCode(category)
|
|
7287
|
+
};
|
|
7288
|
+
});
|
|
7289
|
+
}
|
|
7290
|
+
function shouldRenderRegionLabel(layout, projectedWidth, projectedHeight) {
|
|
7291
|
+
if (layout === "none") {
|
|
7292
|
+
return false;
|
|
7293
|
+
}
|
|
7294
|
+
if (layout === "bestFitOnly") {
|
|
7295
|
+
return projectedWidth >= 18 && projectedHeight >= 10;
|
|
7296
|
+
}
|
|
7297
|
+
return true;
|
|
7298
|
+
}
|
|
7299
|
+
function formatRegionMapValue(value, cultureLanguage) {
|
|
7300
|
+
if (!cultureLanguage) {
|
|
7301
|
+
return String(value);
|
|
7302
|
+
}
|
|
7303
|
+
try {
|
|
7304
|
+
return new Intl.NumberFormat(cultureLanguage, { maximumFractionDigits: 2 }).format(value);
|
|
7305
|
+
} catch {
|
|
7306
|
+
return String(value);
|
|
7307
|
+
}
|
|
7308
|
+
}
|
|
7309
|
+
function buildWaterfallSteps(values, options) {
|
|
7310
|
+
const subtotalIndices = new Set(
|
|
7311
|
+
options?.subtotalIndices ?? (values.length > 0 ? [values.length - 1] : [])
|
|
7312
|
+
);
|
|
7313
|
+
const steps = [];
|
|
7314
|
+
let runningTotal = 0;
|
|
7315
|
+
for (let sourceIndex = 0; sourceIndex < values.length; sourceIndex++) {
|
|
7316
|
+
const value = values[sourceIndex] ?? 0;
|
|
7317
|
+
const isSubtotal = subtotalIndices.has(sourceIndex);
|
|
7318
|
+
const startValue = isSubtotal ? 0 : runningTotal;
|
|
7319
|
+
const endValue = isSubtotal ? value : runningTotal + value;
|
|
7320
|
+
steps.push({ sourceIndex, value, startValue, endValue, isSubtotal });
|
|
7321
|
+
runningTotal = endValue;
|
|
7322
|
+
}
|
|
7323
|
+
return steps;
|
|
7324
|
+
}
|
|
7325
|
+
function computeWaterfallRange(steps) {
|
|
7326
|
+
return computeValueRange2([
|
|
7327
|
+
{
|
|
7328
|
+
name: "Waterfall range",
|
|
7329
|
+
values: steps.flatMap((step) => [step.startValue, step.endValue])
|
|
7330
|
+
}
|
|
7331
|
+
]);
|
|
7332
|
+
}
|
|
5649
7333
|
var WF_COLOR_POSITIVE = "#22c55e";
|
|
5650
7334
|
var WF_COLOR_NEGATIVE = "#ef4444";
|
|
5651
7335
|
var WF_COLOR_TOTAL = "#6366f1";
|
|
5652
7336
|
var WF_CONNECTOR_COLOR = "#94a3b8";
|
|
5653
7337
|
function buildWaterfallViewModel(element, chartData, categoryLabels) {
|
|
5654
7338
|
const layout = computePlotLayout(element.width, element.height, chartData, true);
|
|
5655
|
-
const
|
|
5656
|
-
const
|
|
7339
|
+
const series = chartData.series[0];
|
|
7340
|
+
const values = series?.values ?? [];
|
|
7341
|
+
const steps = buildWaterfallSteps(values, series?.waterfallOptions);
|
|
7342
|
+
const range = computeWaterfallRange(steps);
|
|
5657
7343
|
const catCount = Math.max(categoryLabels.length, values.length, 1);
|
|
5658
7344
|
const barWidth = layout.plotWidth / catCount * 0.6;
|
|
5659
7345
|
const gap = layout.plotWidth / catCount * 0.2;
|
|
5660
7346
|
const primitives = [];
|
|
5661
7347
|
const dataLabels = [];
|
|
5662
|
-
|
|
5663
|
-
|
|
5664
|
-
const
|
|
5665
|
-
const
|
|
5666
|
-
const
|
|
5667
|
-
const endVal = runningTotal + val2;
|
|
5668
|
-
const barStartY = valueToY2(startVal, range, layout.plotTop, layout.plotBottom);
|
|
5669
|
-
const barEndY = valueToY2(endVal, range, layout.plotTop, layout.plotBottom);
|
|
7348
|
+
for (const step of steps) {
|
|
7349
|
+
const { sourceIndex, value, startValue, endValue, isSubtotal } = step;
|
|
7350
|
+
const barStartY = valueToY2(startValue, range, layout.plotTop, layout.plotBottom);
|
|
7351
|
+
const barEndY = valueToY2(endValue, range, layout.plotTop, layout.plotBottom);
|
|
7352
|
+
const i = sourceIndex;
|
|
5670
7353
|
const x = layout.plotLeft + layout.plotWidth / catCount * i + gap;
|
|
5671
7354
|
const y = Math.min(barStartY, barEndY);
|
|
5672
7355
|
const h = Math.max(Math.abs(barEndY - barStartY), 1);
|
|
5673
|
-
const barColor =
|
|
7356
|
+
const barColor = isSubtotal ? WF_COLOR_TOTAL : value >= 0 ? WF_COLOR_POSITIVE : WF_COLOR_NEGATIVE;
|
|
5674
7357
|
primitives.push({
|
|
5675
7358
|
kind: "rect",
|
|
5676
7359
|
x,
|
|
@@ -5678,20 +7361,21 @@ function buildWaterfallViewModel(element, chartData, categoryLabels) {
|
|
|
5678
7361
|
w: barWidth,
|
|
5679
7362
|
h,
|
|
5680
7363
|
fill: barColor,
|
|
5681
|
-
rx: 1
|
|
7364
|
+
rx: 1,
|
|
7365
|
+
part: { role: "dataPoint", seriesIndex: 0, pointIndex: sourceIndex }
|
|
5682
7366
|
});
|
|
5683
7367
|
if (chartData.style?.hasDataLabels) {
|
|
5684
7368
|
dataLabels.push({
|
|
5685
7369
|
kind: "text",
|
|
5686
7370
|
x: x + barWidth / 2,
|
|
5687
7371
|
y: y - 4,
|
|
5688
|
-
text: formatAxisValue2(
|
|
7372
|
+
text: formatAxisValue2(value),
|
|
5689
7373
|
fontSize: 7,
|
|
5690
7374
|
fill: "#334155",
|
|
5691
7375
|
textAnchor: "middle"
|
|
5692
7376
|
});
|
|
5693
7377
|
}
|
|
5694
|
-
if (
|
|
7378
|
+
if (series?.waterfallOptions?.connectorLines !== false && i < values.length - 1) {
|
|
5695
7379
|
const nextX = layout.plotLeft + layout.plotWidth / catCount * (i + 1) + gap;
|
|
5696
7380
|
primitives.push({
|
|
5697
7381
|
kind: "line",
|
|
@@ -5704,9 +7388,6 @@ function buildWaterfallViewModel(element, chartData, categoryLabels) {
|
|
|
5704
7388
|
dashArray: "3 2"
|
|
5705
7389
|
});
|
|
5706
7390
|
}
|
|
5707
|
-
if (!isLast) {
|
|
5708
|
-
runningTotal += val2;
|
|
5709
|
-
}
|
|
5710
7391
|
}
|
|
5711
7392
|
const { gridlines, axisLabels } = buildGridlinesAndLabels(range, layout);
|
|
5712
7393
|
const zeroLine = buildZeroLine(range, layout);
|
|
@@ -5815,7 +7496,7 @@ function resolveRegionCode(label) {
|
|
|
5815
7496
|
return REGION_ALIAS_MAP[normalized];
|
|
5816
7497
|
}
|
|
5817
7498
|
function lerpColor(a, b, t) {
|
|
5818
|
-
const
|
|
7499
|
+
const clamp7 = (v) => Math.max(0, Math.min(255, Math.round(v)));
|
|
5819
7500
|
const ha = a.replace("#", "");
|
|
5820
7501
|
const hb = b.replace("#", "");
|
|
5821
7502
|
const r1 = parseInt(ha.substring(0, 2), 16);
|
|
@@ -5824,9 +7505,9 @@ function lerpColor(a, b, t) {
|
|
|
5824
7505
|
const r2 = parseInt(hb.substring(0, 2), 16);
|
|
5825
7506
|
const g2 = parseInt(hb.substring(2, 4), 16);
|
|
5826
7507
|
const b2 = parseInt(hb.substring(4, 6), 16);
|
|
5827
|
-
const r =
|
|
5828
|
-
const g =
|
|
5829
|
-
const bl =
|
|
7508
|
+
const r = clamp7(r1 + (r2 - r1) * t);
|
|
7509
|
+
const g = clamp7(g1 + (g2 - g1) * t);
|
|
7510
|
+
const bl = clamp7(b1 + (b2 - b1) * t);
|
|
5830
7511
|
const toHex = (n) => n.toString(16).padStart(2, "0");
|
|
5831
7512
|
return `#${toHex(r)}${toHex(g)}${toHex(bl)}`;
|
|
5832
7513
|
}
|
|
@@ -5977,24 +7658,61 @@ var WORLD_REGIONS = [
|
|
|
5977
7658
|
labelXY: [838, 378]
|
|
5978
7659
|
}
|
|
5979
7660
|
];
|
|
7661
|
+
function regionBounds(region) {
|
|
7662
|
+
const coordinates = region.path.match(/-?\d+(?:\.\d+)?/gu)?.map(Number) ?? [];
|
|
7663
|
+
const xs = [];
|
|
7664
|
+
const ys = [];
|
|
7665
|
+
for (let index = 0; index < coordinates.length; index += 2) {
|
|
7666
|
+
xs.push(coordinates[index] ?? 0);
|
|
7667
|
+
ys.push(coordinates[index + 1] ?? 0);
|
|
7668
|
+
}
|
|
7669
|
+
return {
|
|
7670
|
+
minX: Math.min(...xs),
|
|
7671
|
+
minY: Math.min(...ys),
|
|
7672
|
+
maxX: Math.max(...xs),
|
|
7673
|
+
maxY: Math.max(...ys)
|
|
7674
|
+
};
|
|
7675
|
+
}
|
|
7676
|
+
function regionViewBounds(viewedRegionType, regionValues) {
|
|
7677
|
+
if (!viewedRegionType || viewedRegionType === "world" || regionValues.size === 0) {
|
|
7678
|
+
return { minX: 0, minY: 0, maxX: 1e3, maxY: 500 };
|
|
7679
|
+
}
|
|
7680
|
+
const matched = WORLD_REGIONS.filter((region) => regionValues.has(region.code));
|
|
7681
|
+
const targets = viewedRegionType === "countryRegion" ? matched.slice(0, 1) : matched;
|
|
7682
|
+
if (targets.length === 0) {
|
|
7683
|
+
return { minX: 0, minY: 0, maxX: 1e3, maxY: 500 };
|
|
7684
|
+
}
|
|
7685
|
+
const bounds = targets.map(regionBounds);
|
|
7686
|
+
const padding = 10;
|
|
7687
|
+
return {
|
|
7688
|
+
minX: Math.max(0, Math.min(...bounds.map((item) => item.minX)) - padding),
|
|
7689
|
+
minY: Math.max(0, Math.min(...bounds.map((item) => item.minY)) - padding),
|
|
7690
|
+
maxX: Math.min(1e3, Math.max(...bounds.map((item) => item.maxX)) + padding),
|
|
7691
|
+
maxY: Math.min(500, Math.max(...bounds.map((item) => item.maxY)) + padding)
|
|
7692
|
+
};
|
|
7693
|
+
}
|
|
5980
7694
|
function buildRegionMapViewModel(element, chartData, categoryLabels) {
|
|
5981
7695
|
const svgWidth = Math.max(element.width, 320);
|
|
5982
7696
|
const svgHeight = Math.max(element.height, 200);
|
|
5983
7697
|
const categories = categoryLabels.length > 0 ? categoryLabels : chartData.categories;
|
|
5984
|
-
const
|
|
7698
|
+
const series = chartData.series[0];
|
|
7699
|
+
const options = series?.regionMapOptions;
|
|
7700
|
+
const entries = buildRegionMapEntries(categories, series?.values ?? [], options, resolveRegionCode);
|
|
7701
|
+
const values = entries.map((entry) => entry.value);
|
|
5985
7702
|
const finiteVals = values.filter((v) => Number.isFinite(v));
|
|
5986
7703
|
const minVal = finiteVals.length > 0 ? Math.min(...finiteVals) : 0;
|
|
5987
7704
|
const maxVal = finiteVals.length > 0 ? Math.max(...finiteVals) : 1;
|
|
5988
7705
|
const regionValueMap = /* @__PURE__ */ new Map();
|
|
5989
7706
|
const unmatchedRows = [];
|
|
5990
|
-
for (
|
|
5991
|
-
|
|
5992
|
-
|
|
5993
|
-
|
|
5994
|
-
|
|
5995
|
-
|
|
7707
|
+
for (const entry of entries) {
|
|
7708
|
+
if (entry.code !== void 0) {
|
|
7709
|
+
regionValueMap.set(entry.code, {
|
|
7710
|
+
value: entry.value,
|
|
7711
|
+
label: entry.label,
|
|
7712
|
+
sourceIndex: entry.sourceIndex
|
|
7713
|
+
});
|
|
5996
7714
|
} else {
|
|
5997
|
-
unmatchedRows.push({ label:
|
|
7715
|
+
unmatchedRows.push({ label: entry.label, value: entry.value });
|
|
5998
7716
|
}
|
|
5999
7717
|
}
|
|
6000
7718
|
const legendHeight = 30;
|
|
@@ -6003,9 +7721,12 @@ function buildRegionMapViewModel(element, chartData, categoryLabels) {
|
|
|
6003
7721
|
const fallbackTableH = unmatchedRows.length > 0 ? (maxFallbackRows + 1) * fallbackRowH + 8 : 0;
|
|
6004
7722
|
const titleH = chartData.title ? 22 : 0;
|
|
6005
7723
|
const mapAreaH = Math.max(svgHeight - titleH - legendHeight - fallbackTableH - 8, 80);
|
|
6006
|
-
const
|
|
6007
|
-
const
|
|
6008
|
-
const
|
|
7724
|
+
const viewBounds = regionViewBounds(options?.viewedRegionType, regionValueMap);
|
|
7725
|
+
const viewWidth = Math.max(viewBounds.maxX - viewBounds.minX, 1);
|
|
7726
|
+
const viewHeight = Math.max(viewBounds.maxY - viewBounds.minY, 1);
|
|
7727
|
+
const mapScale = Math.min((svgWidth - 20) / viewWidth, mapAreaH / viewHeight);
|
|
7728
|
+
const mapOffsetX = (svgWidth - viewWidth * mapScale) / 2 - viewBounds.minX * mapScale;
|
|
7729
|
+
const mapOffsetY = titleH + 4 - viewBounds.minY * mapScale;
|
|
6009
7730
|
const primitives = [];
|
|
6010
7731
|
primitives.push({
|
|
6011
7732
|
kind: "rect",
|
|
@@ -6040,16 +7761,28 @@ function buildRegionMapViewModel(element, chartData, categoryLabels) {
|
|
|
6040
7761
|
d: scaledPath,
|
|
6041
7762
|
fill,
|
|
6042
7763
|
stroke: "#94a3b8",
|
|
6043
|
-
strokeWidth: Math.max(0.5 / mapScale, 0.3)
|
|
7764
|
+
strokeWidth: Math.max(0.5 / mapScale, 0.3),
|
|
7765
|
+
...entry ? {
|
|
7766
|
+
part: {
|
|
7767
|
+
role: "dataPoint",
|
|
7768
|
+
seriesIndex: 0,
|
|
7769
|
+
pointIndex: entry.sourceIndex
|
|
7770
|
+
}
|
|
7771
|
+
} : {}
|
|
6044
7772
|
});
|
|
6045
|
-
|
|
7773
|
+
const bounds = regionBounds(region);
|
|
7774
|
+
if (entry !== void 0 && shouldRenderRegionLabel(
|
|
7775
|
+
options?.regionLabelLayout,
|
|
7776
|
+
(bounds.maxX - bounds.minX) * mapScale,
|
|
7777
|
+
(bounds.maxY - bounds.minY) * mapScale
|
|
7778
|
+
)) {
|
|
6046
7779
|
const lx = region.labelXY[0] * mapScale + mapOffsetX;
|
|
6047
7780
|
const ly = region.labelXY[1] * mapScale + mapOffsetY + 4;
|
|
6048
7781
|
primitives.push({
|
|
6049
7782
|
kind: "text",
|
|
6050
7783
|
x: lx,
|
|
6051
7784
|
y: ly,
|
|
6052
|
-
text:
|
|
7785
|
+
text: formatRegionMapValue(entry.value, options?.cultureLanguage),
|
|
6053
7786
|
fontSize: Math.max(6, 7 * mapScale),
|
|
6054
7787
|
fill: "#1e293b",
|
|
6055
7788
|
textAnchor: "middle",
|
|
@@ -6104,6 +7837,17 @@ function buildRegionMapViewModel(element, chartData, categoryLabels) {
|
|
|
6104
7837
|
textAnchor: "middle"
|
|
6105
7838
|
}
|
|
6106
7839
|
);
|
|
7840
|
+
if (options?.attribution) {
|
|
7841
|
+
primitives.push({
|
|
7842
|
+
kind: "text",
|
|
7843
|
+
x: svgWidth - 4,
|
|
7844
|
+
y: svgHeight - 4,
|
|
7845
|
+
text: options.attribution,
|
|
7846
|
+
fontSize: 5,
|
|
7847
|
+
fill: "#64748b",
|
|
7848
|
+
textAnchor: "end"
|
|
7849
|
+
});
|
|
7850
|
+
}
|
|
6107
7851
|
if (unmatchedRows.length > 0) {
|
|
6108
7852
|
const tableY = legendY + 26;
|
|
6109
7853
|
const fontSize = Math.min(8, fallbackRowH * 0.7);
|
|
@@ -6281,14 +8025,17 @@ function computeStackedValueRange(series, catCount) {
|
|
|
6281
8025
|
}
|
|
6282
8026
|
function valueToY2(val2, range, topY, bottomY) {
|
|
6283
8027
|
const usable = bottomY - topY;
|
|
8028
|
+
let ratio;
|
|
6284
8029
|
if (range.logScale && range.logBase) {
|
|
6285
8030
|
const base = range.logBase;
|
|
6286
8031
|
const clampedVal = Math.max(val2, range.min);
|
|
6287
8032
|
const logVal = Math.log(clampedVal) / Math.log(base);
|
|
6288
8033
|
const logMin = Math.log(range.min) / Math.log(base);
|
|
6289
|
-
|
|
8034
|
+
ratio = (logVal - logMin) / range.span;
|
|
8035
|
+
} else {
|
|
8036
|
+
ratio = (val2 - range.min) / range.span;
|
|
6290
8037
|
}
|
|
6291
|
-
return
|
|
8038
|
+
return range.reverseOrder ? topY + ratio * usable : bottomY - ratio * usable;
|
|
6292
8039
|
}
|
|
6293
8040
|
function formatAxisValue2(val2) {
|
|
6294
8041
|
if (Math.abs(val2) >= 1e6) {
|
|
@@ -6349,7 +8096,7 @@ function computePlotLayout(elementWidth, elementHeight, chartData, hasAxes, opti
|
|
|
6349
8096
|
};
|
|
6350
8097
|
}
|
|
6351
8098
|
var GRIDLINE_COLOR2 = "#e2e8f0";
|
|
6352
|
-
var
|
|
8099
|
+
var AXIS_LABEL_COLOR = "#64748b";
|
|
6353
8100
|
var ZERO_LINE_COLOR = "#94a3b8";
|
|
6354
8101
|
var TICK_COUNT2 = 5;
|
|
6355
8102
|
function buildGridlinesAndLabels(range, layout) {
|
|
@@ -6373,7 +8120,7 @@ function buildGridlinesAndLabels(range, layout) {
|
|
|
6373
8120
|
y,
|
|
6374
8121
|
text: formatAxisValue2(val2),
|
|
6375
8122
|
fontSize: 8,
|
|
6376
|
-
fill:
|
|
8123
|
+
fill: AXIS_LABEL_COLOR,
|
|
6377
8124
|
textAnchor: "end",
|
|
6378
8125
|
dominantBaseline: "central"
|
|
6379
8126
|
});
|
|
@@ -6405,7 +8152,7 @@ function buildCategoryLabels(categoryLabels, layout, catSpacing) {
|
|
|
6405
8152
|
y: layout.plotBottom + 12,
|
|
6406
8153
|
text: label,
|
|
6407
8154
|
fontSize: 8,
|
|
6408
|
-
fill:
|
|
8155
|
+
fill: AXIS_LABEL_COLOR,
|
|
6409
8156
|
textAnchor: "middle"
|
|
6410
8157
|
};
|
|
6411
8158
|
});
|
|
@@ -6559,9 +8306,12 @@ function computePieSlices(values, cx, cy, outerR, innerR) {
|
|
|
6559
8306
|
return computePieSlicePath(cx, cy, outerR, innerR, startAngle, cumAngle);
|
|
6560
8307
|
});
|
|
6561
8308
|
}
|
|
6562
|
-
function computeScatterDots(values, maxXIndex, layout, range) {
|
|
8309
|
+
function computeScatterDots(values, maxXIndex, layout, range, xValues) {
|
|
8310
|
+
const finiteX = xValues?.slice(0, values.length).filter(Number.isFinite);
|
|
8311
|
+
const minX = finiteX?.length ? Math.min(...finiteX) : 0;
|
|
8312
|
+
const spanX = finiteX?.length ? Math.max(Math.max(...finiteX) - minX, 1) : maxXIndex;
|
|
6563
8313
|
return values.map((val2, i) => ({
|
|
6564
|
-
cx: layout.plotLeft + (
|
|
8314
|
+
cx: layout.plotLeft + (spanX > 0 ? (Number.isFinite(xValues?.[i]) ? xValues[i] - minX : i) / spanX : 0) * layout.plotWidth,
|
|
6565
8315
|
cy: valueToY2(val2, range, layout.plotTop, layout.plotBottom)
|
|
6566
8316
|
}));
|
|
6567
8317
|
}
|
|
@@ -6977,13 +8727,14 @@ function valueFromY(y, range, topY, bottomY) {
|
|
|
6977
8727
|
if (usable === 0) {
|
|
6978
8728
|
return range.min;
|
|
6979
8729
|
}
|
|
8730
|
+
const ratio = range.reverseOrder ? (y - topY) / usable : (bottomY - y) / usable;
|
|
6980
8731
|
if (range.logScale && range.logBase) {
|
|
6981
8732
|
const base = range.logBase;
|
|
6982
8733
|
const logMin = Math.log(range.min) / Math.log(base);
|
|
6983
|
-
const logVal = logMin +
|
|
8734
|
+
const logVal = logMin + ratio * range.span;
|
|
6984
8735
|
return base ** logVal;
|
|
6985
8736
|
}
|
|
6986
|
-
return range.min +
|
|
8737
|
+
return range.min + ratio * range.span;
|
|
6987
8738
|
}
|
|
6988
8739
|
function roundDragValue(value, range) {
|
|
6989
8740
|
if (!Number.isFinite(value) || range.span <= 0) {
|
|
@@ -6993,16 +8744,16 @@ function roundDragValue(value, range) {
|
|
|
6993
8744
|
const rounded = Math.round(value / step) * step;
|
|
6994
8745
|
return Number.parseFloat(rounded.toPrecision(12));
|
|
6995
8746
|
}
|
|
6996
|
-
function
|
|
8747
|
+
function rangeForSeries2(drag, seriesIndex) {
|
|
6997
8748
|
const useSecondary = drag.secondaryRange !== void 0 && (drag.secondarySeriesIndexes?.includes(seriesIndex) ?? false);
|
|
6998
8749
|
return useSecondary && drag.secondaryRange ? drag.secondaryRange : drag.range;
|
|
6999
8750
|
}
|
|
7000
8751
|
function dragValueForPart(viewY, drag, seriesIndex) {
|
|
7001
|
-
const range =
|
|
8752
|
+
const range = rangeForSeries2(drag, seriesIndex);
|
|
7002
8753
|
return roundDragValue(valueFromY(viewY, range, drag.plotTop, drag.plotBottom), range);
|
|
7003
8754
|
}
|
|
7004
8755
|
function dragAnchorViewY(value, drag, seriesIndex) {
|
|
7005
|
-
return valueToY2(value,
|
|
8756
|
+
return valueToY2(value, rangeForSeries2(drag, seriesIndex), drag.plotTop, drag.plotBottom);
|
|
7006
8757
|
}
|
|
7007
8758
|
function withChartPointValue(chartData, seriesIndex, pointIndex, value) {
|
|
7008
8759
|
const series = chartData.series[seriesIndex];
|
|
@@ -7305,8 +9056,8 @@ function hexToRgb3(hex) {
|
|
|
7305
9056
|
};
|
|
7306
9057
|
}
|
|
7307
9058
|
function rgbToHex3(r, g, b) {
|
|
7308
|
-
const
|
|
7309
|
-
return `#${
|
|
9059
|
+
const clamp7 = (v) => Math.max(0, Math.min(255, Math.round(v)));
|
|
9060
|
+
return `#${clamp7(r).toString(16).padStart(2, "0")}${clamp7(g).toString(16).padStart(2, "0")}${clamp7(b).toString(16).padStart(2, "0")}`;
|
|
7310
9061
|
}
|
|
7311
9062
|
function rgbToHsl(r, g, b) {
|
|
7312
9063
|
const rn = r / 255;
|
|
@@ -10360,6 +12111,16 @@ function getSlideBackgroundStyle(slide) {
|
|
|
10360
12111
|
style["background-repeat"] = "no-repeat";
|
|
10361
12112
|
} else if (slide?.backgroundGradient) {
|
|
10362
12113
|
style["background-image"] = slide.backgroundGradient;
|
|
12114
|
+
} else if (pattern) {
|
|
12115
|
+
const svg = getPatternSvg(
|
|
12116
|
+
pattern.preset,
|
|
12117
|
+
pattern.fgColor ?? solid ?? "#000000",
|
|
12118
|
+
pattern.bgColor ?? DEFAULT_SLIDE_BACKGROUND
|
|
12119
|
+
);
|
|
12120
|
+
if (svg) {
|
|
12121
|
+
style["background-image"] = `url("data:image/svg+xml,${encodeURIComponent(svg)}")`;
|
|
12122
|
+
style["background-repeat"] = "repeat";
|
|
12123
|
+
}
|
|
10363
12124
|
}
|
|
10364
12125
|
return style;
|
|
10365
12126
|
}
|
|
@@ -10565,6 +12326,37 @@ function resolveSlideId(slide, index) {
|
|
|
10565
12326
|
const cSld = rawXml?.["p:sld"];
|
|
10566
12327
|
return String(cSld?.["@_id"] || slide?.slideNumber || index + 1);
|
|
10567
12328
|
}
|
|
12329
|
+
function groupSlidesBySection(sections, slides) {
|
|
12330
|
+
if (slides.length === 0) {
|
|
12331
|
+
return [];
|
|
12332
|
+
}
|
|
12333
|
+
if (sections.length === 0) {
|
|
12334
|
+
return [
|
|
12335
|
+
{ section: void 0, slides: [...slides], slideIndexes: slides.map((_slide, i) => i) }
|
|
12336
|
+
];
|
|
12337
|
+
}
|
|
12338
|
+
const sectionById = new Map(sections.map((section) => [section.id, section]));
|
|
12339
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
12340
|
+
const ungrouped = {
|
|
12341
|
+
section: void 0,
|
|
12342
|
+
slides: [],
|
|
12343
|
+
slideIndexes: []
|
|
12344
|
+
};
|
|
12345
|
+
for (const section of sections) {
|
|
12346
|
+
grouped.set(section.id, { section, slides: [], slideIndexes: [] });
|
|
12347
|
+
}
|
|
12348
|
+
slides.forEach((slide, index) => {
|
|
12349
|
+
const group = slide.sectionId ? grouped.get(slide.sectionId) : void 0;
|
|
12350
|
+
const target = group && sectionById.has(slide.sectionId ?? "") ? group : ungrouped;
|
|
12351
|
+
target.slides.push(slide);
|
|
12352
|
+
target.slideIndexes.push(index);
|
|
12353
|
+
});
|
|
12354
|
+
const result = sections.map((section) => grouped.get(section.id)).filter((group) => group.slides.length > 0);
|
|
12355
|
+
if (ungrouped.slides.length > 0) {
|
|
12356
|
+
result.push(ungrouped);
|
|
12357
|
+
}
|
|
12358
|
+
return result;
|
|
12359
|
+
}
|
|
10568
12360
|
function addSection(sections, slides, name, afterSlideIndex, idGenerator = generateSectionId) {
|
|
10569
12361
|
const newId = idGenerator();
|
|
10570
12362
|
const slideAtIndex = slides[afterSlideIndex];
|
|
@@ -10893,6 +12685,19 @@ function generateTicks(slideLengthPx, scale, unit) {
|
|
|
10893
12685
|
}
|
|
10894
12686
|
return ticks;
|
|
10895
12687
|
}
|
|
12688
|
+
function resolvePictureBullet(info, baseFontSize) {
|
|
12689
|
+
if (!info.imageDataUrl && !info.imageRelId && !info.imageBlipFillXml) {
|
|
12690
|
+
return void 0;
|
|
12691
|
+
}
|
|
12692
|
+
const sizePx = typeof info.sizePts === "number" ? info.sizePts : typeof info.sizePercent === "number" ? baseFontSize * (info.sizePercent / 100) : baseFontSize;
|
|
12693
|
+
return {
|
|
12694
|
+
...info.imageDataUrl ? { src: info.imageDataUrl } : {},
|
|
12695
|
+
sizePx,
|
|
12696
|
+
fallbackMarker: "\u2022",
|
|
12697
|
+
accessibleLabel: "Bullet",
|
|
12698
|
+
...info.imageRelId ? { imageRelId: info.imageRelId } : {}
|
|
12699
|
+
};
|
|
12700
|
+
}
|
|
10896
12701
|
var BLOCKED_PROTOCOLS = /* @__PURE__ */ new Set([
|
|
10897
12702
|
`${"javascript"}:`,
|
|
10898
12703
|
"data:",
|
|
@@ -11723,8 +13528,8 @@ function hexToRgb6(hex) {
|
|
|
11723
13528
|
};
|
|
11724
13529
|
}
|
|
11725
13530
|
function rgbToHex5(r, g, b) {
|
|
11726
|
-
const
|
|
11727
|
-
return `#${
|
|
13531
|
+
const clamp7 = (v) => Math.max(0, Math.min(255, Math.round(v)));
|
|
13532
|
+
return `#${clamp7(r).toString(16).padStart(2, "0").toUpperCase()}${clamp7(g).toString(16).padStart(2, "0").toUpperCase()}${clamp7(b).toString(16).padStart(2, "0").toUpperCase()}`;
|
|
11728
13533
|
}
|
|
11729
13534
|
function tintColor2(hex, tintFactor) {
|
|
11730
13535
|
const { r, g, b } = hexToRgb6(hex);
|
|
@@ -12912,6 +14717,7 @@ var SCALAR_ELEMENT_KEYS = /* @__PURE__ */ new Set([
|
|
|
12912
14717
|
"mediaMimeType",
|
|
12913
14718
|
"mediaReferenceKind",
|
|
12914
14719
|
"mediaReferenceName",
|
|
14720
|
+
"mediaReferenceContentType",
|
|
12915
14721
|
"trimStartMs",
|
|
12916
14722
|
"trimEndMs",
|
|
12917
14723
|
"posterFramePath",
|
|
@@ -12946,9 +14752,11 @@ var SCALAR_ELEMENT_KEYS = /* @__PURE__ */ new Set([
|
|
|
12946
14752
|
"inkWidths",
|
|
12947
14753
|
"inkOpacities",
|
|
12948
14754
|
"inkTool",
|
|
14755
|
+
"inkPartPath",
|
|
12949
14756
|
"zoomType",
|
|
12950
14757
|
"targetSlideIndex",
|
|
12951
14758
|
"targetSectionId",
|
|
14759
|
+
"summaryLayout",
|
|
12952
14760
|
"modelPath",
|
|
12953
14761
|
"modelMimeType",
|
|
12954
14762
|
"posterImage",
|
|
@@ -12982,6 +14790,8 @@ var COMPLEX_ELEMENT_FIELDS = {
|
|
|
12982
14790
|
groupFill: "_gf",
|
|
12983
14791
|
inkPointPressures: "_ipp",
|
|
12984
14792
|
inkStrokes: "_cis",
|
|
14793
|
+
inkPartRawXml: "_cirx",
|
|
14794
|
+
summaryTargets: "_zst",
|
|
12985
14795
|
extensionXml: "_ext",
|
|
12986
14796
|
customGeometryPaths: "_cgp",
|
|
12987
14797
|
customGeometryRawData: "_cgr",
|
|
@@ -13623,6 +15433,38 @@ function compareSlides(base, compare) {
|
|
|
13623
15433
|
function comparePresentation(base, compare) {
|
|
13624
15434
|
return compareSlides(base.slides, compare.slides);
|
|
13625
15435
|
}
|
|
15436
|
+
function applyAcceptSlide(slides, diff) {
|
|
15437
|
+
const next = [...slides];
|
|
15438
|
+
if (diff.status === "added" && diff.compareSlide) {
|
|
15439
|
+
next.splice(Math.min(diff.compareIndex, next.length), 0, { ...diff.compareSlide });
|
|
15440
|
+
} else if (diff.status === "changed" && diff.compareSlide && diff.baseIndex >= 0) {
|
|
15441
|
+
next[diff.baseIndex] = { ...diff.compareSlide };
|
|
15442
|
+
} else if (diff.status === "removed" && diff.baseIndex >= 0) {
|
|
15443
|
+
next.splice(diff.baseIndex, 1);
|
|
15444
|
+
}
|
|
15445
|
+
return next;
|
|
15446
|
+
}
|
|
15447
|
+
function applyAcceptAllSlides(slides, compareResult) {
|
|
15448
|
+
const next = [...slides];
|
|
15449
|
+
const diffs = [...compareResult.diffs];
|
|
15450
|
+
for (const diff of diffs) {
|
|
15451
|
+
if (diff.status === "changed" && diff.compareSlide && diff.baseIndex >= 0 && diff.baseIndex < next.length) {
|
|
15452
|
+
next[diff.baseIndex] = { ...diff.compareSlide };
|
|
15453
|
+
}
|
|
15454
|
+
}
|
|
15455
|
+
for (let index = diffs.length - 1; index >= 0; index--) {
|
|
15456
|
+
const diff = diffs[index];
|
|
15457
|
+
if (diff.status === "removed" && diff.baseIndex >= 0 && diff.baseIndex < next.length) {
|
|
15458
|
+
next.splice(diff.baseIndex, 1);
|
|
15459
|
+
}
|
|
15460
|
+
}
|
|
15461
|
+
for (const diff of diffs) {
|
|
15462
|
+
if (diff.status === "added" && diff.compareSlide) {
|
|
15463
|
+
next.splice(Math.min(diff.compareIndex, next.length), 0, { ...diff.compareSlide });
|
|
15464
|
+
}
|
|
15465
|
+
}
|
|
15466
|
+
return next;
|
|
15467
|
+
}
|
|
13626
15468
|
function resolveDirection(direction, defaultDir) {
|
|
13627
15469
|
switch (direction) {
|
|
13628
15470
|
case "l":
|
|
@@ -16844,6 +18686,15 @@ function setCachedResult(key, dataUrl) {
|
|
|
16844
18686
|
}
|
|
16845
18687
|
colorChangeCache.set(key, dataUrl);
|
|
16846
18688
|
}
|
|
18689
|
+
function getImageColorWashStyle(wash) {
|
|
18690
|
+
if (!wash?.color) {
|
|
18691
|
+
return void 0;
|
|
18692
|
+
}
|
|
18693
|
+
return {
|
|
18694
|
+
backgroundColor: wash.color,
|
|
18695
|
+
opacity: Math.max(0, Math.min(100, wash.opacity)) / 100
|
|
18696
|
+
};
|
|
18697
|
+
}
|
|
16847
18698
|
function mapDuotonePixels(data, shadow, highlight) {
|
|
16848
18699
|
const len = data.length;
|
|
16849
18700
|
for (let i = 0; i < len; i += 4) {
|
|
@@ -17530,6 +19381,55 @@ function rebuildDrawingShapesIfCleared(smartArtData, layout, palette, style, ele
|
|
|
17530
19381
|
drawingShapes: reflowToDrawingShapes(layoutResult, smartArtData.nodes)
|
|
17531
19382
|
};
|
|
17532
19383
|
}
|
|
19384
|
+
function buildSummaryZoomView(element, lookup) {
|
|
19385
|
+
if (element.zoomType !== "summary" || !element.summaryTargets?.length) {
|
|
19386
|
+
return void 0;
|
|
19387
|
+
}
|
|
19388
|
+
const layout = element.summaryLayout ?? "grid";
|
|
19389
|
+
const columns = Math.max(1, Math.ceil(Math.sqrt(element.summaryTargets.length)));
|
|
19390
|
+
const tiles = element.summaryTargets.map((target, index) => {
|
|
19391
|
+
const targetInfo = lookup?.(target.targetSlideIndex);
|
|
19392
|
+
const label = target.title || targetInfo?.sectionName || target.sectionId;
|
|
19393
|
+
const slideNumber = targetInfo?.slideNumber ?? target.targetSlideIndex + 1;
|
|
19394
|
+
return {
|
|
19395
|
+
key: `${target.sectionId}:${index}`,
|
|
19396
|
+
sectionId: target.sectionId,
|
|
19397
|
+
targetSlideIndex: target.targetSlideIndex,
|
|
19398
|
+
label,
|
|
19399
|
+
slideLabel: `Slide ${slideNumber}`,
|
|
19400
|
+
imageSrc: target.imageData,
|
|
19401
|
+
backgroundColor: targetInfo?.backgroundColor ?? "#f0f0f0",
|
|
19402
|
+
style: layout === "fixed" ? fixedTileStyle(element, target.x, target.y, target.width, target.height) : { position: "relative", minWidth: "0", minHeight: "0" },
|
|
19403
|
+
ariaLabel: `Zoom to section ${label}, slide ${slideNumber}`
|
|
19404
|
+
};
|
|
19405
|
+
});
|
|
19406
|
+
return {
|
|
19407
|
+
layout,
|
|
19408
|
+
containerStyle: layout === "fixed" ? { position: "relative", width: "100%", height: "100%" } : {
|
|
19409
|
+
display: "grid",
|
|
19410
|
+
gridTemplateColumns: `repeat(${columns}, minmax(0, 1fr))`,
|
|
19411
|
+
gap: "4px",
|
|
19412
|
+
width: "100%",
|
|
19413
|
+
height: "100%"
|
|
19414
|
+
},
|
|
19415
|
+
tiles,
|
|
19416
|
+
ariaLabel: `Summary Zoom with ${tiles.length} sections`
|
|
19417
|
+
};
|
|
19418
|
+
}
|
|
19419
|
+
function fixedTileStyle(element, x, y, width, height) {
|
|
19420
|
+
const safeWidth = Math.max(element.width, 1);
|
|
19421
|
+
const safeHeight = Math.max(element.height, 1);
|
|
19422
|
+
return {
|
|
19423
|
+
position: "absolute",
|
|
19424
|
+
left: percent((x - element.x) / safeWidth),
|
|
19425
|
+
top: percent((y - element.y) / safeHeight),
|
|
19426
|
+
width: percent(width / safeWidth),
|
|
19427
|
+
height: percent(height / safeHeight)
|
|
19428
|
+
};
|
|
19429
|
+
}
|
|
19430
|
+
function percent(value) {
|
|
19431
|
+
return `${Math.round(value * 1e5) / 1e3}%`;
|
|
19432
|
+
}
|
|
17533
19433
|
var ARTISTIC_EFFECTS = [
|
|
17534
19434
|
["none", "pptx.image.effectNone", ""],
|
|
17535
19435
|
["blur", "pptx.image.effectBlur", "blur(4px)"],
|
|
@@ -18126,6 +20026,55 @@ function putAutosaveRecord(filePath, data) {
|
|
|
18126
20026
|
})
|
|
18127
20027
|
);
|
|
18128
20028
|
}
|
|
20029
|
+
async function getAutosaveSnapshot(filePath) {
|
|
20030
|
+
const db = await openAutosaveDb();
|
|
20031
|
+
return new Promise((resolve) => {
|
|
20032
|
+
const tx = db.transaction(AUTOSAVE_STORE_NAME, "readonly");
|
|
20033
|
+
const store = tx.objectStore(AUTOSAVE_STORE_NAME);
|
|
20034
|
+
const req = store.get(filePath);
|
|
20035
|
+
req.onsuccess = () => {
|
|
20036
|
+
db.close();
|
|
20037
|
+
resolve(req.result);
|
|
20038
|
+
};
|
|
20039
|
+
req.onerror = () => {
|
|
20040
|
+
db.close();
|
|
20041
|
+
resolve(void 0);
|
|
20042
|
+
};
|
|
20043
|
+
});
|
|
20044
|
+
}
|
|
20045
|
+
async function listAutosaveSnapshots() {
|
|
20046
|
+
const db = await openAutosaveDb();
|
|
20047
|
+
return new Promise((resolve) => {
|
|
20048
|
+
const results = [];
|
|
20049
|
+
try {
|
|
20050
|
+
const tx = db.transaction(AUTOSAVE_STORE_NAME, "readonly");
|
|
20051
|
+
const store = tx.objectStore(AUTOSAVE_STORE_NAME);
|
|
20052
|
+
const cursorReq = store.openCursor();
|
|
20053
|
+
cursorReq.onsuccess = () => {
|
|
20054
|
+
const cursor = cursorReq.result;
|
|
20055
|
+
if (cursor) {
|
|
20056
|
+
const val2 = cursor.value;
|
|
20057
|
+
results.push({ key: val2.key, timestamp: val2.timestamp, size: val2.size });
|
|
20058
|
+
cursor.continue();
|
|
20059
|
+
}
|
|
20060
|
+
};
|
|
20061
|
+
tx.oncomplete = () => {
|
|
20062
|
+
db.close();
|
|
20063
|
+
resolve(results);
|
|
20064
|
+
};
|
|
20065
|
+
tx.onerror = () => {
|
|
20066
|
+
db.close();
|
|
20067
|
+
resolve([]);
|
|
20068
|
+
};
|
|
20069
|
+
} catch {
|
|
20070
|
+
try {
|
|
20071
|
+
db.close();
|
|
20072
|
+
} catch {
|
|
20073
|
+
}
|
|
20074
|
+
resolve([]);
|
|
20075
|
+
}
|
|
20076
|
+
});
|
|
20077
|
+
}
|
|
18129
20078
|
async function saveAutosaveSnapshot(filePath, data) {
|
|
18130
20079
|
try {
|
|
18131
20080
|
return await putAutosaveRecord(filePath, data);
|
|
@@ -18141,6 +20090,232 @@ async function saveAutosaveSnapshot(filePath, data) {
|
|
|
18141
20090
|
return putAutosaveRecord(filePath, data);
|
|
18142
20091
|
}
|
|
18143
20092
|
}
|
|
20093
|
+
var BACKSTAGE_NAV = [
|
|
20094
|
+
{ id: "home", label: "Home" },
|
|
20095
|
+
{ id: "new", label: "New" },
|
|
20096
|
+
{ id: "open", label: "Open" },
|
|
20097
|
+
{ id: "info", label: "Info" },
|
|
20098
|
+
{ id: "save", label: "Save" },
|
|
20099
|
+
{ id: "saveAs", label: "Save As" },
|
|
20100
|
+
{ id: "print", label: "Print" },
|
|
20101
|
+
{ id: "share", label: "Share" },
|
|
20102
|
+
{ id: "export", label: "Export" },
|
|
20103
|
+
{ id: "close", label: "Close" },
|
|
20104
|
+
{ id: "account", label: "Account", group: "footer" },
|
|
20105
|
+
{ id: "options", label: "Options", group: "footer" }
|
|
20106
|
+
];
|
|
20107
|
+
var BACKSTAGE_TEMPLATES = [
|
|
20108
|
+
{
|
|
20109
|
+
id: "blank",
|
|
20110
|
+
name: "Blank Presentation",
|
|
20111
|
+
description: "Start with a clean canvas",
|
|
20112
|
+
preview: "linear-gradient(145deg, #fff 0 88%, #d34a1f 88%)"
|
|
20113
|
+
},
|
|
20114
|
+
{
|
|
20115
|
+
id: "warm",
|
|
20116
|
+
name: "Warm Welcome",
|
|
20117
|
+
description: "Bold editorial title slides",
|
|
20118
|
+
preview: "linear-gradient(135deg, #d94b20 0 62%, #f4b183 62%)"
|
|
20119
|
+
},
|
|
20120
|
+
{
|
|
20121
|
+
id: "geometry",
|
|
20122
|
+
name: "Geometric",
|
|
20123
|
+
description: "Modern shapes and strong contrast",
|
|
20124
|
+
preview: "conic-gradient(from 220deg, #173b8f, #dce6ff, #efb7bd, #173b8f)"
|
|
20125
|
+
},
|
|
20126
|
+
{
|
|
20127
|
+
id: "mono",
|
|
20128
|
+
name: "Urban Monochrome",
|
|
20129
|
+
description: "Architectural black and white",
|
|
20130
|
+
preview: "linear-gradient(125deg, #171717, #eee 49%, #777 50%, #fafafa)"
|
|
20131
|
+
},
|
|
20132
|
+
{
|
|
20133
|
+
id: "earth",
|
|
20134
|
+
name: "Earthy Inspiration",
|
|
20135
|
+
description: "Natural, calm presentation system",
|
|
20136
|
+
preview: "radial-gradient(circle at 70% 30%, #be9473, #34271f 38%, #111 70%)"
|
|
20137
|
+
},
|
|
20138
|
+
{
|
|
20139
|
+
id: "future",
|
|
20140
|
+
name: "Future Forward",
|
|
20141
|
+
description: "Clean technology storytelling",
|
|
20142
|
+
preview: "repeating-radial-gradient(ellipse at bottom, #111 0 3px, #fff 4px 13px)"
|
|
20143
|
+
}
|
|
20144
|
+
];
|
|
20145
|
+
var TEMPLATE_BACKGROUNDS = {
|
|
20146
|
+
blank: { backgroundColor: "#ffffff" },
|
|
20147
|
+
warm: {
|
|
20148
|
+
backgroundColor: "#d94b20",
|
|
20149
|
+
backgroundGradient: "linear-gradient(135deg, #d94b20 0 62%, #f4b183 62%)"
|
|
20150
|
+
},
|
|
20151
|
+
geometry: {
|
|
20152
|
+
backgroundColor: "#dce6ff",
|
|
20153
|
+
backgroundGradient: "linear-gradient(135deg, #173b8f, #dce6ff 54%, #efb7bd)"
|
|
20154
|
+
},
|
|
20155
|
+
mono: {
|
|
20156
|
+
backgroundColor: "#f4f4f4",
|
|
20157
|
+
backgroundGradient: "linear-gradient(125deg, #171717, #eeeeee 49%, #777777 50%, #fafafa)"
|
|
20158
|
+
},
|
|
20159
|
+
earth: {
|
|
20160
|
+
backgroundColor: "#34271f",
|
|
20161
|
+
backgroundGradient: "radial-gradient(circle at 70% 30%, #be9473, #34271f 38%, #111111 70%)"
|
|
20162
|
+
},
|
|
20163
|
+
future: {
|
|
20164
|
+
backgroundColor: "#ffffff",
|
|
20165
|
+
backgroundGradient: "repeating-radial-gradient(ellipse at bottom, #111111 0 3px, #ffffff 4px 13px)"
|
|
20166
|
+
}
|
|
20167
|
+
};
|
|
20168
|
+
function createBackstagePresentation(templateId) {
|
|
20169
|
+
const slide = createBlankSlide(1);
|
|
20170
|
+
return [{ ...slide, ...TEMPLATE_BACKGROUNDS[templateId] ?? TEMPLATE_BACKGROUNDS.blank }];
|
|
20171
|
+
}
|
|
20172
|
+
function splitFilePath(path) {
|
|
20173
|
+
const normalized = path.replace(/\\/g, "/");
|
|
20174
|
+
const parts = normalized.split("/").filter(Boolean);
|
|
20175
|
+
return {
|
|
20176
|
+
name: parts[parts.length - 1] || "Untitled Presentation.pptx",
|
|
20177
|
+
location: parts.slice(0, -1).join("/") || "Browser storage"
|
|
20178
|
+
};
|
|
20179
|
+
}
|
|
20180
|
+
async function listBackstageRecentFiles() {
|
|
20181
|
+
if (typeof indexedDB === "undefined") {
|
|
20182
|
+
return [];
|
|
20183
|
+
}
|
|
20184
|
+
try {
|
|
20185
|
+
const snapshots = await listAutosaveSnapshots();
|
|
20186
|
+
return snapshots.map((snapshot) => ({ ...snapshot, ...splitFilePath(snapshot.key) })).sort((a, b) => b.timestamp - a.timestamp);
|
|
20187
|
+
} catch {
|
|
20188
|
+
return [];
|
|
20189
|
+
}
|
|
20190
|
+
}
|
|
20191
|
+
async function readBackstageRecentFile(key) {
|
|
20192
|
+
if (typeof indexedDB === "undefined") {
|
|
20193
|
+
return void 0;
|
|
20194
|
+
}
|
|
20195
|
+
return (await getAutosaveSnapshot(key))?.data;
|
|
20196
|
+
}
|
|
20197
|
+
function formatBackstageDate(timestamp, now = Date.now()) {
|
|
20198
|
+
const elapsed = Math.max(0, now - timestamp);
|
|
20199
|
+
if (elapsed < 6e4) {
|
|
20200
|
+
return "Just now";
|
|
20201
|
+
}
|
|
20202
|
+
if (elapsed < 36e5) {
|
|
20203
|
+
return `${Math.floor(elapsed / 6e4)} min ago`;
|
|
20204
|
+
}
|
|
20205
|
+
if (elapsed < 864e5) {
|
|
20206
|
+
return `${Math.floor(elapsed / 36e5)} hr ago`;
|
|
20207
|
+
}
|
|
20208
|
+
return new Intl.DateTimeFormat(void 0, { month: "short", day: "numeric" }).format(timestamp);
|
|
20209
|
+
}
|
|
20210
|
+
function formatBackstageSize(bytes) {
|
|
20211
|
+
if (bytes < 1024 * 1024) {
|
|
20212
|
+
return `${Math.max(1, Math.round(bytes / 1024))} KB`;
|
|
20213
|
+
}
|
|
20214
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
20215
|
+
}
|
|
20216
|
+
var DEFAULT_VIRTUAL_OVERSCAN = 5;
|
|
20217
|
+
var SLIDE_VIRTUALIZATION_THRESHOLD = 50;
|
|
20218
|
+
function computeVirtualRange(totalItems, itemHeight, scrollTop, viewportHeight, overscan = DEFAULT_VIRTUAL_OVERSCAN) {
|
|
20219
|
+
const safeCount = Math.max(0, totalItems);
|
|
20220
|
+
const safeItemHeight = Math.max(itemHeight, 1);
|
|
20221
|
+
const totalHeight = safeCount * safeItemHeight;
|
|
20222
|
+
if (safeCount === 0) {
|
|
20223
|
+
return {
|
|
20224
|
+
startIndex: 0,
|
|
20225
|
+
endIndex: -1,
|
|
20226
|
+
totalHeight: 0,
|
|
20227
|
+
offsetY: 0,
|
|
20228
|
+
visibleRange: { start: 0, end: -1 }
|
|
20229
|
+
};
|
|
20230
|
+
}
|
|
20231
|
+
const visibleStart = Math.min(safeCount - 1, Math.max(0, Math.floor(scrollTop / safeItemHeight)));
|
|
20232
|
+
const visibleEnd = Math.min(
|
|
20233
|
+
safeCount - 1,
|
|
20234
|
+
Math.max(visibleStart, Math.floor((scrollTop + Math.max(0, viewportHeight)) / safeItemHeight))
|
|
20235
|
+
);
|
|
20236
|
+
const safeOverscan = Math.max(0, overscan);
|
|
20237
|
+
const startIndex = Math.max(0, visibleStart - safeOverscan);
|
|
20238
|
+
const endIndex = Math.min(safeCount - 1, visibleEnd + safeOverscan);
|
|
20239
|
+
return {
|
|
20240
|
+
startIndex,
|
|
20241
|
+
endIndex,
|
|
20242
|
+
totalHeight,
|
|
20243
|
+
offsetY: startIndex * safeItemHeight,
|
|
20244
|
+
visibleRange: { start: visibleStart, end: visibleEnd }
|
|
20245
|
+
};
|
|
20246
|
+
}
|
|
20247
|
+
function collectFontsFromElement(element, fonts) {
|
|
20248
|
+
if (hasTextProperties(element)) {
|
|
20249
|
+
if (element.textStyle?.fontFamily) {
|
|
20250
|
+
fonts.add(element.textStyle.fontFamily);
|
|
20251
|
+
}
|
|
20252
|
+
for (const segment of element.textSegments ?? []) {
|
|
20253
|
+
if (segment.style?.fontFamily) {
|
|
20254
|
+
fonts.add(segment.style.fontFamily);
|
|
20255
|
+
}
|
|
20256
|
+
}
|
|
20257
|
+
}
|
|
20258
|
+
if (element.type === "group") {
|
|
20259
|
+
for (const child2 of element.children ?? []) {
|
|
20260
|
+
collectFontsFromElement(child2, fonts);
|
|
20261
|
+
}
|
|
20262
|
+
}
|
|
20263
|
+
}
|
|
20264
|
+
function collectUsedFonts(slides) {
|
|
20265
|
+
const fonts = /* @__PURE__ */ new Set();
|
|
20266
|
+
for (const slide of slides) {
|
|
20267
|
+
for (const element of slide.elements ?? []) {
|
|
20268
|
+
collectFontsFromElement(element, fonts);
|
|
20269
|
+
}
|
|
20270
|
+
}
|
|
20271
|
+
return [...fonts].sort();
|
|
20272
|
+
}
|
|
20273
|
+
function browserFontSource() {
|
|
20274
|
+
return typeof document === "undefined" ? void 0 : document.fonts;
|
|
20275
|
+
}
|
|
20276
|
+
function isFontFamilyAvailable(family, source = browserFontSource()) {
|
|
20277
|
+
if (!source) {
|
|
20278
|
+
return false;
|
|
20279
|
+
}
|
|
20280
|
+
try {
|
|
20281
|
+
return source.check(`12px "${family.replace(/"/g, '\\"')}"`);
|
|
20282
|
+
} catch {
|
|
20283
|
+
return false;
|
|
20284
|
+
}
|
|
20285
|
+
}
|
|
20286
|
+
async function scanAvailableFontFamilies(families, source = browserFontSource()) {
|
|
20287
|
+
if (!source) {
|
|
20288
|
+
return /* @__PURE__ */ new Set();
|
|
20289
|
+
}
|
|
20290
|
+
try {
|
|
20291
|
+
await source.ready;
|
|
20292
|
+
return new Set(families.filter((family) => isFontFamilyAvailable(family, source)));
|
|
20293
|
+
} catch {
|
|
20294
|
+
return /* @__PURE__ */ new Set();
|
|
20295
|
+
}
|
|
20296
|
+
}
|
|
20297
|
+
function shouldLoopContinuously(input) {
|
|
20298
|
+
return Boolean(input.loopContinuously) || input.showType === "kiosk";
|
|
20299
|
+
}
|
|
20300
|
+
function applyRehearsalTimings(slides, timings) {
|
|
20301
|
+
return slides.map((slide, index) => {
|
|
20302
|
+
const advanceAfterMs = timings[index];
|
|
20303
|
+
if (typeof advanceAfterMs !== "number") {
|
|
20304
|
+
return slide;
|
|
20305
|
+
}
|
|
20306
|
+
return {
|
|
20307
|
+
...slide,
|
|
20308
|
+
transition: {
|
|
20309
|
+
...slide.transition,
|
|
20310
|
+
type: slide.transition?.type ?? "none",
|
|
20311
|
+
advanceAfterMs
|
|
20312
|
+
}
|
|
20313
|
+
};
|
|
20314
|
+
});
|
|
20315
|
+
}
|
|
20316
|
+
function computeEntranceAnimationDelay(delayMs, animationIndex) {
|
|
20317
|
+
return Math.max(0, delayMs || 0) + animationIndex * 60;
|
|
20318
|
+
}
|
|
18144
20319
|
var FALLBACK_DOWNLOAD_NAME = "presentation.pptx";
|
|
18145
20320
|
var REVOKE_DELAY_MS = 200;
|
|
18146
20321
|
var OPEN_REVOKE_DELAY_MS = 6e4;
|
|
@@ -18233,6 +20408,22 @@ function canvasToJpegData(canvas, quality = 0.92) {
|
|
|
18233
20408
|
}
|
|
18234
20409
|
return { bytes, w: canvas.width, h: canvas.height };
|
|
18235
20410
|
}
|
|
20411
|
+
function generatePackageReadme(presentationFilename) {
|
|
20412
|
+
return [
|
|
20413
|
+
"Presentation Package",
|
|
20414
|
+
"====================",
|
|
20415
|
+
"",
|
|
20416
|
+
`This folder contains the presentation "${presentationFilename}" along with`,
|
|
20417
|
+
"all linked media files (images, audio, video) in the /media subfolder.",
|
|
20418
|
+
"",
|
|
20419
|
+
"To view this presentation:",
|
|
20420
|
+
"1. Open the presentation file with any compatible presentation software",
|
|
20421
|
+
"2. Ensure the /media folder remains alongside the presentation file",
|
|
20422
|
+
"",
|
|
20423
|
+
`Packaged on ${(/* @__PURE__ */ new Date()).toLocaleDateString()}`,
|
|
20424
|
+
""
|
|
20425
|
+
].join("\n");
|
|
20426
|
+
}
|
|
18236
20427
|
function encodeGif(frames, delay = {}) {
|
|
18237
20428
|
if (frames.length === 0) {
|
|
18238
20429
|
throw new Error("[gif-encoder] encodeGif: frames array must not be empty");
|
|
@@ -18693,11 +20884,11 @@ function buildNotesTextStream(lines, startY) {
|
|
|
18693
20884
|
let content = `BT /F1 ${NOTES_FONT_SIZE} Tf 0 0 0 rg `;
|
|
18694
20885
|
content += `${NOTES_MARGIN} ${startY.toFixed(2)} Td `;
|
|
18695
20886
|
for (let li = 0; li < lines.length; li++) {
|
|
18696
|
-
const
|
|
20887
|
+
const line2 = lines[li];
|
|
18697
20888
|
if (li === 0) {
|
|
18698
|
-
content += `(${escapePdfText(
|
|
20889
|
+
content += `(${escapePdfText(line2)}) Tj `;
|
|
18699
20890
|
} else {
|
|
18700
|
-
content += `0 ${(-15.399999999999999).toFixed(2)} Td (${escapePdfText(
|
|
20891
|
+
content += `0 ${(-15.399999999999999).toFixed(2)} Td (${escapePdfText(line2)}) Tj `;
|
|
18701
20892
|
}
|
|
18702
20893
|
}
|
|
18703
20894
|
content += "ET\n";
|
|
@@ -18742,12 +20933,24 @@ function isSafeSvgMarkup(svg) {
|
|
|
18742
20933
|
}
|
|
18743
20934
|
return !EVENT_HANDLER_ATTR_RE.test(trimmed);
|
|
18744
20935
|
}
|
|
20936
|
+
function sanitizeSlideSvg(svg, width, height) {
|
|
20937
|
+
if (!isSafeSvgMarkup(svg)) {
|
|
20938
|
+
return "";
|
|
20939
|
+
}
|
|
20940
|
+
const sanitized = sanitizeMarkupOrEmpty(svg, { USE_PROFILES: { svg: true } }).trim();
|
|
20941
|
+
if (!sanitized || sanitized.toLowerCase().startsWith("<svg")) {
|
|
20942
|
+
return sanitized;
|
|
20943
|
+
}
|
|
20944
|
+
const safeWidth = Number.isFinite(width) && width > 0 ? width : 1;
|
|
20945
|
+
const safeHeight = Number.isFinite(height) && height > 0 ? height : 1;
|
|
20946
|
+
return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${safeWidth} ${safeHeight}" width="${safeWidth}" height="${safeHeight}">${sanitized}</svg>`;
|
|
20947
|
+
}
|
|
18745
20948
|
function buildPrintDocument(svgs, width, height, options = {}) {
|
|
18746
20949
|
const { title = "Print", orientation = "landscape", colorFilter = "" } = options;
|
|
18747
20950
|
const safeOrientation = sanitizeOrientation(orientation);
|
|
18748
20951
|
const safeColorFilter = sanitizeCssDeclaration(colorFilter);
|
|
18749
20952
|
const slidePages = svgs.map((svg, i) => {
|
|
18750
|
-
const safeSvg =
|
|
20953
|
+
const safeSvg = sanitizeSlideSvg(svg, width, height);
|
|
18751
20954
|
return `<section class="print-slide-page" aria-label="Slide ${i + 1}">
|
|
18752
20955
|
${safeSvg}
|
|
18753
20956
|
</section>`;
|
|
@@ -19741,4 +21944,4 @@ function segmentFrameCount(durationMs, fps) {
|
|
|
19741
21944
|
return Math.max(1, Math.ceil(durationMs / (1e3 / fps)));
|
|
19742
21945
|
}
|
|
19743
21946
|
|
|
19744
|
-
export { ACTION_BUTTON_PRESETS, ARTISTIC_EFFECTS, BROADCAST_THROTTLE_MS, CATEGORIES, CONNECTION_TIMEOUT_MS, DEFAULT_BROADCAST_SERVER_URL, DEFAULT_COLOR_CHANGE_TOLERANCE, DEFAULT_INSERT_CHART_TYPE, DEFAULT_ROW_HEIGHT, DIRECTIONAL_PRESETS, DUOTONE_PRESETS, EXPORT_ASSEMBLING_PERCENT, EXPORT_DONE_PERCENT, FILL_MODE_OPTIONS, GRADIENT_TYPE_OPTIONS, HANDOUT_OPTIONS, INK_REPLAY_KEYFRAMES, INSERT_CHART_TYPES, LBL, LOCAL_SYNC_ORIGIN, MOBILE_BREAKPOINT, NOTES_FONT_SIZE_DEFAULT, NOTES_FONT_SIZE_MAX, NOTES_FONT_SIZE_MIN, NOTES_FONT_SIZE_STEP, NUM, PATTERN_OPTIONS, PRESENTATION_CHANNEL_NAME, PRESENTATION_HASH, PRESENTATION_MESSAGE_ORIGIN, PRESENTATION_NONCE_KEY, PRESETS, RULER_FONT_SIZE, RULER_THICKNESS, SECTION_HEADING, SEL, SHAPE_PRESET_DEFS, SLIDE_TRANSITION_KEYFRAMES, TABLET_BREAKPOINT, TABLE_STYLE_PRESETS, TAB_ROW_ACTION_CLASSES, TEXT_BUILD_ID_SEP, THEME_COLOR_LABELS, TITLE_BAR_CLASSES, TITLE_BAR_DEFAULT_FILE_KEY, TimelineEngine, WEBM_MIME_CANDIDATES, activateModalFocus, addSection, alignElements, appendPresentationInkPoint, apply3dEffects, applyCaseTransformToSegments, applyColorChange, applyDuotone, applyFormatToElement, applyReroutedConnectors, applyResize, applyStyleToSelectedSegments, bringForward, bringToFront, build3DExtrusionData, buildBroadcastViewerUrl, buildCacheKey, buildChartViewModel, buildCollaborationShareUrl, buildCreateCollaborationConfig, buildCssGradientFromShapeStyle, buildDuotoneCacheKey, buildElementClipboardPayload, buildGlowBoxShadow, buildHandoutsHtml, buildInitialGuides, buildInnerShadowCssFromShapeStyle, buildJoinCollaborationConfig, buildMultiLayerShadowCss, buildNotesHtml, buildNotesPdfBytes, buildOutlineHtml, buildPatternFillCss, buildPresentationAudienceUrl, buildPreviewAnimation, buildPrintDocument, buildPrintHtmlDocument, buildReflectionCss, buildShadowCssFromShapeStyle, buildSlidesHtml, buildSlidesPdfBytes, buildSmartArt3DModel, buildSmartArtA11y, buildSmartArtPresetData, buildTextBody3DSceneStyle, buildTextFillCss, buildTextReflectionCss, buildTextRunFilterChain, buildTextShadowCss, buildThemeColorGrid, buildUserFontFaceStyles, canvasToJpegData, chartPartToAttrs, clampCropValue, clampNotesFontSize, clampPercent, clampUnitInterval, clearPresentationDeck, cloneElementForPaste, cloneHistorySnapshot, cloneSlide, cloneTemplateElementsBySlideId, collectImagePaths, collectMediaElements, colorWithOpacity, comparePresentation, computeAllNotesPages, computeAutoFitTextStyle, computeColumnBoundaries, computeHandoutLayout, computeInlineEditorRect, computeKeyboardInset, computeLayout, computeLayoutOptions, computeMarqueeHitIds, computeMergeCellDown, computeMergeCellRight, computeResizedColumnWidths, computeResizedRowHeight, computeScrollDelta, computeSelectionRect, computeSmartArtLayout, computeSnapToShape, computeSplitCell, computeValueRange, computeValueRangeForChart, convertLatexToOmml, convertOmmlToLatex, convertOmmlToMathMl, copyFormatFromElement, countTextSegments, createBlankSlide, createDefaultChartElement, createInitialPresentationSnapshot, createPresentationSessionId, createPresenterTimer, createSyncGate, createTouchGestureRecognizer, defaultCssVars, defaultRadius, defaultThemeColors, deleteSection, deleteTableColumn, deleteTableRow, derivePresenceList, detectOrientation, detectTouchDevice, distributeElements, downloadBlob, downloadDataUrl, dragAnchorViewY, dragValueForPart, encodeGif, erasePresentationInkAt, expandTextBuildAnimations, extractPathPoints, filterCommands, findChartPartTarget, findSmartArtNodeText, formatAxisValue, formatAxisValueWithUnits, formatBytes, formatCommentTimestamp, formatElapsed, formatMobileElapsed, formatRelativeTime, formatSlideCounter, formatTime, formatVersionTimestamp, fpsToFrameIntervalMs, generateBroadcastRoomId, generateElementId, generateLogTicks, generateNoteLineCount, generatePressureCircles, generateTicks, getAnimationInitialStyle, getAriaLabel, getAriaRole, getAriaRoleDescription, getCachedResult, getChartStylePalette, getCommentMarkerPosition, getCompoundLineBorderWidth, getCompoundLineBoxShadow, getCompoundLineOffsets, getCompoundLineWidths, getConnectionSites, getContentPartReplayStyles, getCssBorderDashStyle, getDisplayUnitLabel, getDuotoneCachedResult, getEffectDagFilter, getElementLabel, getElementTransform, getElementTransformWithoutRotation, getImageEffectsFilter, getImageEffectsOpacity, getInkReplayStyles, getInlineEditorSelection, getKinsokuLineBreakStyles, getNotesPrintableArea, getOleBadgeLabel, getOleTypeColor, getOleTypeLabel, getPatternSvg, getPendingSelectionRestore, getPrintableArea, getResolvedShapeClipPath, getRoundRectRadiusPx, getSecondaryValueAxis, getShapeAdjustmentHandleDescriptor, getSlideBackgroundStyle, getSlideTransitionAnimations, getSvgStrokeDasharray, getTextAlphaOpacity, getTextCompensationTransform, getWarpPath, groupElements, groupIntoParagraphs, hasCopyableFormat, hasDistinctScriptFonts, hasPressureVariation, insertTableColumn, insertTableRow, isBrowserOpenableMime, isCellInRect, isEditableTextElement, isExportAbortError, isFirstSlide, isKeyboardOpen, isLastSlide, isMixedContentBlocked, isMobileViewport, isPpactionUrl, isPresentationSessionMessage, isTemplateElement, isTemplateElementId, isUrlSafe, loadPresentationDeck, makeCloneId, makeSlideId, mergeAdditiveSelection, mergeCells, mergePresentationSnapshot, mobileElapsedSince, mobileSlideCounter, mountModel3D, mountSurfaceChart3D, movePresenterPointer, moveSectionDown, moveSectionUp, moveSlidesToSection, newTableElement, normalizeColorsForCapture, normalizeHexColor, normalizeStrokeDashType, normalizeValue, notesSegmentsToSpans, observeYDocSlides, ooxmlDashToCssBorderStyle, openPptxFile, openUrlInNewTab, paletteColor, parseDrawingColor, parseDrawingColorOpacity, parsePpactionUrl, pickSupportedMimeType, placeAudienceWindow, preprocessCssForCapture, presentationInkPath, presenterElapsed, pressuresToWidths, readSlidesFromYDoc, readViewportMetrics, rebuildDrawingShapesIfCleared, reconcileSlidesInYDoc, recordProgressPercent, rectToCells, remapTextToSegments, renameSection, rerouteConnectorsForMovedElements, resetPresenterTimer, resolveAudienceScreenPlacement, resolveCssTextAlign, resolveDrawingShapeNodeId, resolveFontForScript, resolveInkColor, resolveInkOpacity, resolveInkWidth, resolveLineHeight, resolveOleType, resolveParagraphAlign, resolveParagraphRtl, resolveRegionCode, resolveTitleBarStatusKey, resolveTransitionDurationMs, resolveTransportForServerUrl, resolveUnderlineDecorationStyle, restoreSegmentSelection, safeOpenUrl, sanitizeColor, sanitizeDownloadFilename, sanitizeMathMl, saveAutosaveSnapshot, segmentByScript, segmentFrameCount, sendBackward, sendToBack, sequentialColorScale, seriesColor, setCachedResult, setDuotoneCachedResult, setPendingSelectionRestore, shadeColor2, shouldCommitSmartArtNodeText, shouldUseSvgWarp, slideProgressPercent, slideStatusLabel, snapBoxToGrid, splitCell, splitSeriesByAxis, stepPresenterZoom, storePresentationDeck, substituteFieldText, svgLineCap, swapPresentationWindows, themeToCssVars, tintColor2, toCssTextOrientation, toCssVerticalDirection, toCssWritingMode, togglePresenterTimer, transformTextCase, validateRoomId, valueToY, vermilionDarkColors, vermilionDarkTheme, vermilionLightColors, vermilionLightTheme, vermilionRadius, withChartPointValue, withChartTitle };
|
|
21947
|
+
export { ACTION_BUTTON_PRESETS, ARTISTIC_EFFECTS, BACKSTAGE_NAV, BACKSTAGE_TEMPLATES, BROADCAST_THROTTLE_MS, CATEGORIES, CONNECTION_TIMEOUT_MS, DEFAULT_BROADCAST_SERVER_URL, DEFAULT_COLOR_CHANGE_TOLERANCE, DEFAULT_INSERT_CHART_TYPE, DEFAULT_ROW_HEIGHT, DEFAULT_VIRTUAL_OVERSCAN, DIRECTIONAL_PRESETS, DUOTONE_PRESETS, EXPORT_ASSEMBLING_PERCENT, EXPORT_DONE_PERCENT, FILL_MODE_OPTIONS, GRADIENT_TYPE_OPTIONS, HANDOUT_OPTIONS, INK_REPLAY_KEYFRAMES, INSERT_CHART_TYPES, LBL, LOCAL_SYNC_ORIGIN, MOBILE_BREAKPOINT, NOTES_FONT_SIZE_DEFAULT, NOTES_FONT_SIZE_MAX, NOTES_FONT_SIZE_MIN, NOTES_FONT_SIZE_STEP, NUM, PATTERN_OPTIONS, PRESENTATION_CHANNEL_NAME, PRESENTATION_HASH, PRESENTATION_MESSAGE_ORIGIN, PRESENTATION_NONCE_KEY, PRESETS, RULER_FONT_SIZE, RULER_THICKNESS, SECTION_HEADING, SEL, SHAPE_PRESET_DEFS, SLIDE_TRANSITION_KEYFRAMES, SLIDE_VIRTUALIZATION_THRESHOLD, TABLET_BREAKPOINT, TABLE_STYLE_PRESETS, TAB_ROW_ACTION_CLASSES, TEXT_BUILD_ID_SEP, THEME_COLOR_LABELS, TITLE_BAR_CLASSES, TITLE_BAR_DEFAULT_FILE_KEY, TimelineEngine, WEBM_MIME_CANDIDATES, activateModalFocus, addSection, alignElements, appendPresentationInkPoint, apply3dEffects, applyAcceptAllSlides, applyAcceptSlide, applyCaseTransformToSegments, applyColorChange, applyDuotone, applyFormatToElement, applyRehearsalTimings, applyReroutedConnectors, applyResize, applyStyleToSelectedSegments, bringForward, bringToFront, build3DExtrusionData, buildBroadcastViewerUrl, buildCacheKey, buildChartViewModel, buildCollaborationShareUrl, buildCreateCollaborationConfig, buildCssGradientFromShapeStyle, buildDuotoneCacheKey, buildElementClipboardPayload, buildGlowBoxShadow, buildHandoutsHtml, buildInitialGuides, buildInnerShadowCssFromShapeStyle, buildJoinCollaborationConfig, buildMultiLayerShadowCss, buildNotesHtml, buildNotesPdfBytes, buildOutlineHtml, buildPatternFillCss, buildPresentationAudienceUrl, buildPreviewAnimation, buildPrintDocument, buildPrintHtmlDocument, buildReflectionCss, buildShadowCssFromShapeStyle, buildSlidesHtml, buildSlidesPdfBytes, buildSmartArt3DModel, buildSmartArtA11y, buildSmartArtPresetData, buildSummaryZoomView, buildTextBody3DSceneStyle, buildTextFillCss, buildTextReflectionCss, buildTextRunFilterChain, buildTextShadowCss, buildThemeColorGrid, buildUserFontFaceStyles, canvasToJpegData, chartPartToAttrs, clampCropValue, clampNotesFontSize, clampPercent, clampUnitInterval, clearPresentationDeck, cloneElementForPaste, cloneHistorySnapshot, cloneSlide, cloneTemplateElementsBySlideId, collectImagePaths, collectMediaElements, collectUsedFonts, colorWithOpacity, comparePresentation, computeAllNotesPages, computeAutoFitTextStyle, computeColumnBoundaries, computeEntranceAnimationDelay, computeHandoutLayout, computeInlineEditorRect, computeKeyboardInset, computeLayout, computeLayoutOptions, computeMarqueeHitIds, computeMergeCellDown, computeMergeCellRight, computeResizedColumnWidths, computeResizedRowHeight, computeScrollDelta, computeSelectionRect, computeSmartArtLayout, computeSnapToShape, computeSplitCell, computeValueRange, computeValueRangeForChart, computeVirtualRange, convertLatexToOmml, convertOmmlToLatex, convertOmmlToMathMl, copyFormatFromElement, countTextSegments, createBackstagePresentation, createBlankSlide, createDefaultChartElement, createInitialPresentationSnapshot, createPresentationSessionId, createPresenterTimer, createSyncGate, createTouchGestureRecognizer, defaultCssVars, defaultRadius, defaultThemeColors, deleteSection, deleteTableColumn, deleteTableRow, derivePresenceList, detectOrientation, detectTouchDevice, distributeElements, downloadBlob, downloadDataUrl, dragAnchorViewY, dragValueForPart, encodeGif, erasePresentationInkAt, expandTextBuildAnimations, extractPathPoints, filterCommands, findChartPartTarget, findSmartArtNodeText, formatAxisValue, formatAxisValueWithUnits, formatBackstageDate, formatBackstageSize, formatBytes, formatCommentTimestamp, formatElapsed, formatMobileElapsed, formatRelativeTime, formatSlideCounter, formatTime, formatVersionTimestamp, fpsToFrameIntervalMs, generateBroadcastRoomId, generateElementId, generateLogTicks, generateNoteLineCount, generatePackageReadme, generatePressureCircles, generateTicks, getAnimationInitialStyle, getAriaLabel, getAriaRole, getAriaRoleDescription, getCachedResult, getChartStylePalette, getCommentMarkerPosition, getCompoundLineBorderWidth, getCompoundLineBoxShadow, getCompoundLineOffsets, getCompoundLineWidths, getConnectionSites, getContentPartReplayStyles, getCssBorderDashStyle, getDisplayUnitLabel, getDuotoneCachedResult, getEffectDagFilter, getElementLabel, getElementTransform, getElementTransformWithoutRotation, getImageColorWashStyle, getImageEffectsFilter, getImageEffectsOpacity, getImageSvgFilters, getInkReplayStyles, getInlineEditorSelection, getKinsokuLineBreakStyles, getNotesPrintableArea, getOleBadgeLabel, getOleTypeColor, getOleTypeLabel, getPatternSvg, getPendingSelectionRestore, getPrintableArea, getResolvedShapeClipPath, getRoundRectRadiusPx, getSecondaryValueAxis, getShapeAdjustmentHandleDescriptor, getSlideBackgroundStyle, getSlideTransitionAnimations, getSvgStrokeDasharray, getTextAlphaOpacity, getTextCompensationTransform, getWarpPath, groupElements, groupIntoParagraphs, groupSlidesBySection, hasCopyableFormat, hasDistinctScriptFonts, hasPressureVariation, insertTableColumn, insertTableRow, isBrowserOpenableMime, isCellInRect, isEditableTextElement, isExportAbortError, isFirstSlide, isKeyboardOpen, isLastSlide, isMixedContentBlocked, isMobileViewport, isPpactionUrl, isPresentationSessionMessage, isTemplateElement, isTemplateElementId, isUrlSafe, listBackstageRecentFiles, loadPresentationDeck, makeCloneId, makeSlideId, mergeAdditiveSelection, mergeCells, mergePresentationSnapshot, mobileElapsedSince, mobileSlideCounter, mountModel3D, mountSurfaceChart3D, movePresenterPointer, moveSectionDown, moveSectionUp, moveSlidesToSection, newTableElement, normalizeColorsForCapture, normalizeHexColor, normalizeStrokeDashType, normalizeValue, notesSegmentsToSpans, observeYDocSlides, ooxmlDashToCssBorderStyle, openPptxFile, openUrlInNewTab, paletteColor, parseDrawingColor, parseDrawingColorOpacity, parsePpactionUrl, pickSupportedMimeType, placeAudienceWindow, preprocessCssForCapture, presentationInkPath, presenterElapsed, pressuresToWidths, readBackstageRecentFile, readSlidesFromYDoc, readViewportMetrics, rebuildDrawingShapesIfCleared, reconcileSlidesInYDoc, recordProgressPercent, rectToCells, remapTextToSegments, renameSection, rerouteConnectorsForMovedElements, resetPresenterTimer, resolveAudienceScreenPlacement, resolveCssTextAlign, resolveDrawingShapeNodeId, resolveFontForScript, resolveInkColor, resolveInkOpacity, resolveInkWidth, resolveLineHeight, resolveOleType, resolveParagraphAlign, resolveParagraphRtl, resolvePictureBullet, resolveRegionCode, resolveTitleBarStatusKey, resolveTransitionDurationMs, resolveTransportForServerUrl, resolveUnderlineDecorationStyle, restoreSegmentSelection, safeOpenUrl, sanitizeColor, sanitizeDownloadFilename, sanitizeMathMl, saveAutosaveSnapshot, scanAvailableFontFamilies, segmentByScript, segmentFrameCount, sendBackward, sendToBack, sequentialColorScale, seriesColor, setCachedResult, setDuotoneCachedResult, setPendingSelectionRestore, shadeColor2, shouldCommitSmartArtNodeText, shouldLoopContinuously, shouldUseSvgWarp, slideProgressPercent, slideStatusLabel, snapBoxToGrid, splitCell, splitSeriesByAxis, stepPresenterZoom, storePresentationDeck, substituteFieldText, svgLineCap, swapPresentationWindows, themeToCssVars, tintColor2, toCssTextOrientation, toCssVerticalDirection, toCssWritingMode, togglePresenterTimer, transformTextCase, validateRoomId, valueToY, vermilionDarkColors, vermilionDarkTheme, vermilionLightColors, vermilionLightTheme, vermilionRadius, withChartPointValue, withChartTitle };
|