Skip to content

Commit 4ec4a61

Browse files
committed
perf(cricket-highlighter): reuse and clean SVG patterns safely
- remove owned cricket pattern defs during cleanup - avoid DOUBLE/TRIPLE board targets for tactics compatibility - dedupe owned pattern IDs across connected board SVGs
1 parent e437353 commit 4ec4a61

6 files changed

Lines changed: 453 additions & 46 deletions

File tree

dist/autodarts-xconfig.user.js

Lines changed: 59 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -15655,6 +15655,12 @@
1565515655
var TARGET_SLOT_CLASS_PREFIX = "ad-ext-cricket-slot-";
1565615656
var PRESSURE_SUPPRESSED_CLASS = "ad-ext-cricket-pressure-muted";
1565715657
var STYLE_CONTRACT_VERSION = "cricket-highlighter-style/v1";
15658+
var PRESENTATION_PATTERN_IDS = Object.freeze({
15659+
scoring: "ad-ext-cricket-scoring-pattern",
15660+
pressure: "ad-ext-cricket-pressure-pattern",
15661+
dead: "ad-ext-cricket-dead-pattern",
15662+
inactive: "ad-ext-cricket-inactive-pattern"
15663+
});
1565815664
var PRESENTATION_CLASS = Object.freeze({
1565915665
open: "is-open",
1566015666
closed: "is-dead",
@@ -18052,25 +18058,16 @@
1805218058
...Array.from({ length: 20 }, (_value, index) => String(index + 1)),
1805318059
"BULL"
1805418060
]);
18055-
var SPECIAL_OBJECTIVE_TARGETS = Object.freeze(["DOUBLE", "TRIPLE"]);
18061+
var PRESENTATION_PATTERN_ID_VALUES = Object.freeze(Object.values(PRESENTATION_PATTERN_IDS));
1805618062
function resolvePresentationToken(value) {
1805718063
const token = String(value || "").trim().toLowerCase();
1805818064
if (token === "inactive") {
1805918065
return "inactive";
1806018066
}
1806118067
return normalizeCricketPresentationToken(token);
1806218068
}
18063-
function resolveBoardTargets(renderState) {
18064-
const targets = BASE_BOARD_TARGETS.slice();
18065-
const labelSet = new Set(
18066-
Array.isArray(renderState?.targetOrder) ? renderState.targetOrder.map((entry) => String(entry || "").trim().toUpperCase()) : []
18067-
);
18068-
SPECIAL_OBJECTIVE_TARGETS.forEach((label) => {
18069-
if (labelSet.has(label)) {
18070-
targets.push(label);
18071-
}
18072-
});
18073-
return targets;
18069+
function resolveBoardTargets() {
18070+
return BASE_BOARD_TARGETS.slice();
1807418071
}
1807518072
function polar2(radius, angleDeg) {
1807618073
const radians = (angleDeg - 90) * Math.PI / 180;
@@ -18381,12 +18378,25 @@
1838118378
pattern.setAttribute("patternTransform", "rotate(135)");
1838218379
defs.appendChild(pattern);
1838318380
}
18384-
while (pattern.firstChild) {
18385-
pattern.firstChild.remove();
18386-
}
1838718381
const baseColor = options.color || { r: 0, g: 0, b: 0 };
1838818382
const baseAlpha = clampAlpha(options.baseAlpha, 0.6);
1838918383
const stripeAlpha = clampAlpha(options.stripeAlpha, 0.3);
18384+
const patternSignature = [
18385+
"8",
18386+
"8",
18387+
"rotate(135)",
18388+
Math.round(Number(baseColor.r) || 0),
18389+
Math.round(Number(baseColor.g) || 0),
18390+
Math.round(Number(baseColor.b) || 0),
18391+
baseAlpha.toFixed(4),
18392+
stripeAlpha.toFixed(4)
18393+
].join("|");
18394+
if (String(pattern.getAttribute?.("data-ad-ext-cricket-pattern-signature") || "") === patternSignature && pattern.children?.length === 2) {
18395+
return `url(#${patternId})`;
18396+
}
18397+
while (pattern.firstChild) {
18398+
pattern.firstChild.remove();
18399+
}
1839018400
const baseRect = ownerDocument.createElementNS(SVG_NS2, "rect");
1839118401
baseRect.setAttribute("width", "8");
1839218402
baseRect.setAttribute("height", "8");
@@ -18396,34 +18406,57 @@
1839618406
stripe.setAttribute("d", "M0 0 H8 V4 H0 Z");
1839718407
stripe.setAttribute("fill", rgbaColor(baseColor, stripeAlpha));
1839818408
pattern.appendChild(stripe);
18409+
pattern.setAttribute("data-ad-ext-cricket-pattern-signature", patternSignature);
1839918410
return `url(#${patternId})`;
1840018411
}
18412+
function removePresentationPatternsFromSvg(svgRoot) {
18413+
if (!svgRoot || typeof svgRoot.querySelector !== "function") {
18414+
return;
18415+
}
18416+
PRESENTATION_PATTERN_ID_VALUES.forEach((patternId) => {
18417+
const pattern = svgRoot.querySelector(`#${patternId}`);
18418+
if (pattern && typeof pattern.remove === "function") {
18419+
pattern.remove();
18420+
}
18421+
});
18422+
}
18423+
function removeStalePresentationPatterns(ownerDocument, currentSvg) {
18424+
if (!ownerDocument || typeof ownerDocument.querySelectorAll !== "function" || !currentSvg) {
18425+
return;
18426+
}
18427+
Array.from(ownerDocument.querySelectorAll("svg")).forEach((svgNode) => {
18428+
if (svgNode !== currentSvg) {
18429+
removePresentationPatternsFromSvg(svgNode);
18430+
}
18431+
});
18432+
}
1840118433
function ensurePresentationPatterns(overlay, visualConfig) {
18434+
removeStalePresentationPatterns(overlay?.ownerDocument || null, overlay?.ownerSVGElement || null);
1840218435
const scoringColor = visualConfig?.theme?.scoring || { r: 0, g: 178, b: 135 };
1840318436
const pressureColor = visualConfig?.theme?.pressure || { r: 239, g: 68, b: 68 };
1840418437
const deadColor = visualConfig?.deadColor || { r: 112, g: 118, b: 128 };
1840518438
const inactiveDimStyle = resolveInactiveDimStyle(visualConfig, visualConfig?.intensity || {});
1840618439
return {
1840718440
scoring: ensureStripedPattern(overlay, {
18408-
patternId: "ad-ext-cricket-scoring-pattern",
18441+
patternId: PRESENTATION_PATTERN_IDS.scoring,
1840918442
color: scoringColor,
1841018443
baseAlpha: 0.72,
1841118444
stripeAlpha: 0.32
1841218445
}),
1841318446
pressure: ensureStripedPattern(overlay, {
18414-
patternId: "ad-ext-cricket-pressure-pattern",
18447+
patternId: PRESENTATION_PATTERN_IDS.pressure,
1841518448
color: pressureColor,
1841618449
baseAlpha: 0.64,
1841718450
stripeAlpha: 0.28
1841818451
}),
1841918452
dead: ensureStripedPattern(overlay, {
18420-
patternId: "ad-ext-cricket-dead-pattern",
18453+
patternId: PRESENTATION_PATTERN_IDS.dead,
1842118454
color: deadColor,
1842218455
baseAlpha: 0.46,
1842318456
stripeAlpha: 0.22
1842418457
}),
1842518458
inactive: inactiveDimStyle.usePattern ? ensureStripedPattern(overlay, {
18426-
patternId: "ad-ext-cricket-inactive-pattern",
18459+
patternId: PRESENTATION_PATTERN_IDS.inactive,
1842718460
color: inactiveDimStyle.color,
1842818461
baseAlpha: inactiveDimStyle.patternBaseAlpha,
1842918462
stripeAlpha: inactiveDimStyle.patternStripeAlpha
@@ -18535,7 +18568,7 @@
1853518568
return false;
1853618569
}
1853718570
applyOverlayStyleVars(overlay, visualConfig, board.radius);
18538-
const boardTargets = resolveBoardTargets(renderState);
18571+
const boardTargets = resolveBoardTargets();
1853918572
const patterns = ensurePresentationPatterns(overlay, visualConfig);
1854018573
const overlayShapeState = ensureOverlayShapeCache(
1854118574
overlay,
@@ -18631,6 +18664,11 @@
1863118664
return true;
1863218665
}
1863318666
function clearCricketHighlights(documentRef) {
18667+
if (documentRef && typeof documentRef.querySelectorAll === "function") {
18668+
Array.from(documentRef.querySelectorAll("svg")).forEach((svgNode) => {
18669+
removePresentationPatternsFromSvg(svgNode);
18670+
});
18671+
}
1863418672
const board = findBoardSvgGroup(documentRef);
1863518673
if (!board?.group) {
1863618674
return;
@@ -19878,7 +19916,7 @@
1987819916
}
1987919917
runtime.renderCache.overlayShapeState = null;
1988019918
const managedNodeMatcher = createManagedNodeMatcher({
19881-
ids: [OVERLAY_ID2]
19919+
ids: [OVERLAY_ID2, ...Object.values(PRESENTATION_PATTERN_IDS)]
1988219920
});
1988319921
let lastTransitionSignature = "";
1988419922
let lastStatusSignature = "";

src/features/cricket-highlighter/index.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {
44
} from "./logic.js";
55
import {
66
OVERLAY_ID,
7+
PRESENTATION_PATTERN_IDS,
78
STYLE_CONTRACT_VERSION,
89
STYLE_ID,
910
buildStyleText,
@@ -261,7 +262,7 @@ export function initializeCricketHighlighter(context = {}) {
261262

262263
runtime.renderCache.overlayShapeState = null;
263264
const managedNodeMatcher = createManagedNodeMatcher({
264-
ids: [OVERLAY_ID],
265+
ids: [OVERLAY_ID, ...Object.values(PRESENTATION_PATTERN_IDS)],
265266
});
266267
let lastTransitionSignature = "";
267268
let lastStatusSignature = "";

src/features/cricket-highlighter/logic.js

Lines changed: 65 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
} from "../../shared/dartboard-svg.js";
77
import {
88
OVERLAY_ID,
9+
PRESENTATION_PATTERN_IDS,
910
PRESENTATION_CLASS,
1011
PRESSURE_SUPPRESSED_CLASS,
1112
SVG_NS,
@@ -33,7 +34,7 @@ const BASE_BOARD_TARGETS = Object.freeze([
3334
...Array.from({ length: 20 }, (_value, index) => String(index + 1)),
3435
"BULL",
3536
]);
36-
const SPECIAL_OBJECTIVE_TARGETS = Object.freeze(["DOUBLE", "TRIPLE"]);
37+
const PRESENTATION_PATTERN_ID_VALUES = Object.freeze(Object.values(PRESENTATION_PATTERN_IDS));
3738

3839
function resolvePresentationToken(value) {
3940
const token = String(value || "").trim().toLowerCase();
@@ -43,21 +44,8 @@ function resolvePresentationToken(value) {
4344
return normalizeCricketPresentationToken(token);
4445
}
4546

46-
function resolveBoardTargets(renderState) {
47-
const targets = BASE_BOARD_TARGETS.slice();
48-
const labelSet = new Set(
49-
Array.isArray(renderState?.targetOrder)
50-
? renderState.targetOrder.map((entry) => String(entry || "").trim().toUpperCase())
51-
: []
52-
);
53-
54-
SPECIAL_OBJECTIVE_TARGETS.forEach((label) => {
55-
if (labelSet.has(label)) {
56-
targets.push(label);
57-
}
58-
});
59-
60-
return targets;
47+
function resolveBoardTargets() {
48+
return BASE_BOARD_TARGETS.slice();
6149
}
6250

6351
function polar(radius, angleDeg) {
@@ -413,13 +401,32 @@ function ensureStripedPattern(overlay, options = {}) {
413401
defs.appendChild(pattern);
414402
}
415403

404+
const baseColor = options.color || { r: 0, g: 0, b: 0 };
405+
const baseAlpha = clampAlpha(options.baseAlpha, 0.6);
406+
const stripeAlpha = clampAlpha(options.stripeAlpha, 0.3);
407+
const patternSignature = [
408+
"8",
409+
"8",
410+
"rotate(135)",
411+
Math.round(Number(baseColor.r) || 0),
412+
Math.round(Number(baseColor.g) || 0),
413+
Math.round(Number(baseColor.b) || 0),
414+
baseAlpha.toFixed(4),
415+
stripeAlpha.toFixed(4),
416+
].join("|");
417+
418+
if (
419+
String(pattern.getAttribute?.("data-ad-ext-cricket-pattern-signature") || "") ===
420+
patternSignature &&
421+
pattern.children?.length === 2
422+
) {
423+
return `url(#${patternId})`;
424+
}
425+
416426
while (pattern.firstChild) {
417427
pattern.firstChild.remove();
418428
}
419429

420-
const baseColor = options.color || { r: 0, g: 0, b: 0 };
421-
const baseAlpha = clampAlpha(options.baseAlpha, 0.6);
422-
const stripeAlpha = clampAlpha(options.stripeAlpha, 0.3);
423430
const baseRect = ownerDocument.createElementNS(SVG_NS, "rect");
424431
baseRect.setAttribute("width", "8");
425432
baseRect.setAttribute("height", "8");
@@ -430,37 +437,65 @@ function ensureStripedPattern(overlay, options = {}) {
430437
stripe.setAttribute("d", "M0 0 H8 V4 H0 Z");
431438
stripe.setAttribute("fill", rgbaColor(baseColor, stripeAlpha));
432439
pattern.appendChild(stripe);
440+
pattern.setAttribute("data-ad-ext-cricket-pattern-signature", patternSignature);
433441

434442
return `url(#${patternId})`;
435443
}
436444

445+
function removePresentationPatternsFromSvg(svgRoot) {
446+
if (!svgRoot || typeof svgRoot.querySelector !== "function") {
447+
return;
448+
}
449+
450+
PRESENTATION_PATTERN_ID_VALUES.forEach((patternId) => {
451+
const pattern = svgRoot.querySelector(`#${patternId}`);
452+
if (pattern && typeof pattern.remove === "function") {
453+
pattern.remove();
454+
}
455+
});
456+
}
457+
458+
function removeStalePresentationPatterns(ownerDocument, currentSvg) {
459+
if (!ownerDocument || typeof ownerDocument.querySelectorAll !== "function" || !currentSvg) {
460+
return;
461+
}
462+
463+
Array.from(ownerDocument.querySelectorAll("svg")).forEach((svgNode) => {
464+
if (svgNode !== currentSvg) {
465+
removePresentationPatternsFromSvg(svgNode);
466+
}
467+
});
468+
}
469+
437470
function ensurePresentationPatterns(overlay, visualConfig) {
471+
removeStalePresentationPatterns(overlay?.ownerDocument || null, overlay?.ownerSVGElement || null);
472+
438473
const scoringColor = visualConfig?.theme?.scoring || { r: 0, g: 178, b: 135 };
439474
const pressureColor = visualConfig?.theme?.pressure || { r: 239, g: 68, b: 68 };
440475
const deadColor = visualConfig?.deadColor || { r: 112, g: 118, b: 128 };
441476
const inactiveDimStyle = resolveInactiveDimStyle(visualConfig, visualConfig?.intensity || {});
442477
return {
443478
scoring: ensureStripedPattern(overlay, {
444-
patternId: "ad-ext-cricket-scoring-pattern",
479+
patternId: PRESENTATION_PATTERN_IDS.scoring,
445480
color: scoringColor,
446481
baseAlpha: 0.72,
447482
stripeAlpha: 0.32,
448483
}),
449484
pressure: ensureStripedPattern(overlay, {
450-
patternId: "ad-ext-cricket-pressure-pattern",
485+
patternId: PRESENTATION_PATTERN_IDS.pressure,
451486
color: pressureColor,
452487
baseAlpha: 0.64,
453488
stripeAlpha: 0.28,
454489
}),
455490
dead: ensureStripedPattern(overlay, {
456-
patternId: "ad-ext-cricket-dead-pattern",
491+
patternId: PRESENTATION_PATTERN_IDS.dead,
457492
color: deadColor,
458493
baseAlpha: 0.46,
459494
stripeAlpha: 0.22,
460495
}),
461496
inactive: inactiveDimStyle.usePattern
462497
? ensureStripedPattern(overlay, {
463-
patternId: "ad-ext-cricket-inactive-pattern",
498+
patternId: PRESENTATION_PATTERN_IDS.inactive,
464499
color: inactiveDimStyle.color,
465500
baseAlpha: inactiveDimStyle.patternBaseAlpha,
466501
stripeAlpha: inactiveDimStyle.patternStripeAlpha,
@@ -611,7 +646,7 @@ export function renderCricketHighlights(options = {}) {
611646
}
612647

613648
applyOverlayStyleVars(overlay, visualConfig, board.radius);
614-
const boardTargets = resolveBoardTargets(renderState);
649+
const boardTargets = resolveBoardTargets();
615650
const patterns = ensurePresentationPatterns(overlay, visualConfig);
616651
const overlayShapeState = ensureOverlayShapeCache(
617652
overlay,
@@ -735,6 +770,12 @@ export function renderCricketHighlights(options = {}) {
735770
}
736771

737772
export function clearCricketHighlights(documentRef) {
773+
if (documentRef && typeof documentRef.querySelectorAll === "function") {
774+
Array.from(documentRef.querySelectorAll("svg")).forEach((svgNode) => {
775+
removePresentationPatternsFromSvg(svgNode);
776+
});
777+
}
778+
738779
const board = findBoardSvgGroup(documentRef);
739780
if (!board?.group) {
740781
return;

src/features/cricket-highlighter/style.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@ export const TARGET_CLASS = "ad-ext-cricket-target";
55
export const TARGET_SLOT_CLASS_PREFIX = "ad-ext-cricket-slot-";
66
export const PRESSURE_SUPPRESSED_CLASS = "ad-ext-cricket-pressure-muted";
77
export const STYLE_CONTRACT_VERSION = "cricket-highlighter-style/v1";
8+
export const PRESENTATION_PATTERN_IDS = Object.freeze({
9+
scoring: "ad-ext-cricket-scoring-pattern",
10+
pressure: "ad-ext-cricket-pressure-pattern",
11+
dead: "ad-ext-cricket-dead-pattern",
12+
inactive: "ad-ext-cricket-inactive-pattern",
13+
});
814
export const PRESENTATION_CLASS = Object.freeze({
915
open: "is-open",
1016
closed: "is-dead",

0 commit comments

Comments
 (0)