accessify-widget 0.3.3 → 0.3.5
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/dist/accessify.min.js +1 -1
- package/dist/accessify.min.js.map +1 -1
- package/dist/accessify.mjs +1 -1
- package/dist/{index-B8sAE1Po.js → index-BTkeEVoS.js} +166 -283
- package/dist/{index-B8sAE1Po.js.map → index-BTkeEVoS.js.map} +1 -1
- package/dist/{keyboard-nav-SGx_nXud.js → keyboard-nav-BZ4ei-Lt.js} +2 -2
- package/dist/{keyboard-nav-SGx_nXud.js.map → keyboard-nav-BZ4ei-Lt.js.map} +1 -1
- package/dist/{page-structure-BfD6Mw28.js → page-structure-JY54Sj14.js} +2 -2
- package/dist/{page-structure-BfD6Mw28.js.map → page-structure-JY54Sj14.js.map} +1 -1
- package/dist/widget.js +1 -1
- package/dist/widget.js.map +1 -1
- package/package.json +1 -1
package/dist/accessify.mjs
CHANGED
|
@@ -4779,7 +4779,7 @@ const deJson = {
|
|
|
4779
4779
|
"feature.textSimplify": "Text vereinfachen",
|
|
4780
4780
|
"feature.textSimplify.desc": "Text in Einfache Sprache umwandeln",
|
|
4781
4781
|
"feature.altText": "Bildbeschreibung",
|
|
4782
|
-
"feature.altText.desc": "
|
|
4782
|
+
"feature.altText.desc": "Bildbeschreibungen per Hover anzeigen und fehlende automatisch erzeugen",
|
|
4783
4783
|
"feature.autoScan": "WCAG-Prüfung",
|
|
4784
4784
|
"feature.autoScan.desc": "Seite auf Barrierefreiheit prüfen",
|
|
4785
4785
|
"feature.saturation": "Sättigung",
|
|
@@ -4868,7 +4868,7 @@ const enJson = {
|
|
|
4868
4868
|
"feature.textSimplify": "Simplify Text",
|
|
4869
4869
|
"feature.textSimplify.desc": "Simplify text for easier understanding",
|
|
4870
4870
|
"feature.altText": "Image Description",
|
|
4871
|
-
"feature.altText.desc": "
|
|
4871
|
+
"feature.altText.desc": "Show image descriptions on hover and auto-generate missing ones",
|
|
4872
4872
|
"feature.autoScan": "WCAG Scan",
|
|
4873
4873
|
"feature.autoScan.desc": "Scan page for accessibility issues",
|
|
4874
4874
|
"feature.saturation": "Saturation",
|
|
@@ -6433,14 +6433,14 @@ function FeatureGrid($$anchor, $$props) {
|
|
|
6433
6433
|
const FEATURE_LOADERS = {
|
|
6434
6434
|
contrast: () => import("./contrast-CqsICAkU.js"),
|
|
6435
6435
|
"text-size": () => import("./text-size-C6OFhCGi.js"),
|
|
6436
|
-
"keyboard-nav": () => import("./keyboard-nav-
|
|
6436
|
+
"keyboard-nav": () => import("./keyboard-nav-BZ4ei-Lt.js"),
|
|
6437
6437
|
"link-highlight": () => import("./link-highlight-DBGm067Y.js"),
|
|
6438
6438
|
"reading-guide": () => import("./reading-guide-VT8NciIL.js"),
|
|
6439
6439
|
"reading-mask": () => import("./reading-mask-BABChuCz.js"),
|
|
6440
6440
|
"animation-stop": () => import("./animation-stop-C0MwseK0.js"),
|
|
6441
6441
|
"hide-images": () => import("./hide-images-B_LeCBcd.js"),
|
|
6442
6442
|
"big-cursor": () => import("./big-cursor-B2UKu9dQ.js"),
|
|
6443
|
-
"page-structure": () => import("./page-structure-
|
|
6443
|
+
"page-structure": () => import("./page-structure-JY54Sj14.js"),
|
|
6444
6444
|
tts: () => import("./tts-CjszLRnb.js"),
|
|
6445
6445
|
"text-simplify": () => import("./text-simplify-Cvhpio7g.js"),
|
|
6446
6446
|
"alt-text": () => Promise.resolve().then(() => altText)
|
|
@@ -6866,6 +6866,38 @@ function getTranslatableAttributes(root2 = document.body) {
|
|
|
6866
6866
|
}
|
|
6867
6867
|
return attrs;
|
|
6868
6868
|
}
|
|
6869
|
+
const BLOCK_TAGS = /* @__PURE__ */ new Set(["H1", "H2", "H3", "H4", "H5", "H6", "P", "LI", "TD", "TH", "CAPTION", "FIGCAPTION", "BLOCKQUOTE", "DT", "DD"]);
|
|
6870
|
+
function groupTextNodes(textNodes) {
|
|
6871
|
+
const groups = [];
|
|
6872
|
+
const assigned = /* @__PURE__ */ new Set();
|
|
6873
|
+
for (const node of textNodes) {
|
|
6874
|
+
if (assigned.has(node)) continue;
|
|
6875
|
+
const block2 = findBlockAncestor(node);
|
|
6876
|
+
if (block2 && BLOCK_TAGS.has(block2.tagName)) {
|
|
6877
|
+
const siblings = textNodes.filter((n) => !assigned.has(n) && findBlockAncestor(n) === block2);
|
|
6878
|
+
if (siblings.length > 1) {
|
|
6879
|
+
for (const s of siblings) assigned.add(s);
|
|
6880
|
+
groups.push({
|
|
6881
|
+
nodes: siblings,
|
|
6882
|
+
fullText: siblings.map((n) => n.data.trim()).filter(Boolean).join(" "),
|
|
6883
|
+
singleNode: false
|
|
6884
|
+
});
|
|
6885
|
+
continue;
|
|
6886
|
+
}
|
|
6887
|
+
}
|
|
6888
|
+
assigned.add(node);
|
|
6889
|
+
groups.push({ nodes: [node], fullText: node.data.trim(), singleNode: true });
|
|
6890
|
+
}
|
|
6891
|
+
return groups;
|
|
6892
|
+
}
|
|
6893
|
+
function findBlockAncestor(node) {
|
|
6894
|
+
let el = node.parentElement;
|
|
6895
|
+
while (el && el !== document.body) {
|
|
6896
|
+
if (BLOCK_TAGS.has(el.tagName)) return el;
|
|
6897
|
+
el = el.parentElement;
|
|
6898
|
+
}
|
|
6899
|
+
return null;
|
|
6900
|
+
}
|
|
6869
6901
|
async function translatePage(targetLang) {
|
|
6870
6902
|
const pageLang = document.documentElement.lang?.split("-")[0] || "auto";
|
|
6871
6903
|
const textNodes = getTranslatableTextNodes();
|
|
@@ -6883,8 +6915,9 @@ async function translatePage(targetLang) {
|
|
|
6883
6915
|
savedAttrs.push(entry);
|
|
6884
6916
|
}
|
|
6885
6917
|
}
|
|
6918
|
+
const nodeGroups = groupTextNodes(textNodes);
|
|
6886
6919
|
const allTexts = [
|
|
6887
|
-
...
|
|
6920
|
+
...nodeGroups.map((g) => g.fullText),
|
|
6888
6921
|
...attrEntries.map((a) => a.original.trim())
|
|
6889
6922
|
].filter(Boolean);
|
|
6890
6923
|
const uniqueTexts = [...new Set(allTexts)];
|
|
@@ -6897,13 +6930,25 @@ async function translatePage(targetLang) {
|
|
|
6897
6930
|
}
|
|
6898
6931
|
});
|
|
6899
6932
|
observerPaused = true;
|
|
6900
|
-
for (const
|
|
6901
|
-
|
|
6902
|
-
|
|
6903
|
-
|
|
6904
|
-
|
|
6905
|
-
|
|
6906
|
-
|
|
6933
|
+
for (const group of nodeGroups) {
|
|
6934
|
+
if (group.singleNode) {
|
|
6935
|
+
const node = group.nodes[0];
|
|
6936
|
+
const replacement = lookup.get(group.fullText);
|
|
6937
|
+
if (replacement) {
|
|
6938
|
+
const leading = node.data.match(/^\s*/)?.[0] || "";
|
|
6939
|
+
const trailing = node.data.match(/\s*$/)?.[0] || "";
|
|
6940
|
+
node.data = leading + replacement + trailing;
|
|
6941
|
+
}
|
|
6942
|
+
} else {
|
|
6943
|
+
const replacement = lookup.get(group.fullText);
|
|
6944
|
+
if (replacement) {
|
|
6945
|
+
const firstNode = group.nodes[0];
|
|
6946
|
+
const leading = firstNode.data.match(/^\s*/)?.[0] || "";
|
|
6947
|
+
firstNode.data = leading + replacement;
|
|
6948
|
+
for (let i = 1; i < group.nodes.length; i++) {
|
|
6949
|
+
group.nodes[i].data = "";
|
|
6950
|
+
}
|
|
6951
|
+
}
|
|
6907
6952
|
}
|
|
6908
6953
|
}
|
|
6909
6954
|
for (const entry of attrEntries) {
|
|
@@ -7984,258 +8029,16 @@ function createAltTextModule(aiService, initialLang = "de", serverConfig) {
|
|
|
7984
8029
|
const siteKey = serverConfig?.siteKey || "";
|
|
7985
8030
|
const proxyUrl = serverConfig?.proxyUrl || "";
|
|
7986
8031
|
let enabled = false;
|
|
7987
|
-
let styleEl = null;
|
|
7988
|
-
let tooltipEl = null;
|
|
7989
8032
|
let domObserver = null;
|
|
7990
8033
|
let autoGenerating = false;
|
|
7991
8034
|
let missingAltImages = [];
|
|
7992
8035
|
const processedImages = /* @__PURE__ */ new Map();
|
|
7993
|
-
let describeClickHandler = null;
|
|
7994
|
-
let describeEscHandler = null;
|
|
7995
|
-
const describeButtons = /* @__PURE__ */ new Map();
|
|
7996
8036
|
function lang() {
|
|
7997
8037
|
return getCurrentWidgetLang() || initialLang;
|
|
7998
8038
|
}
|
|
7999
8039
|
function isDE() {
|
|
8000
8040
|
return lang().startsWith("de");
|
|
8001
8041
|
}
|
|
8002
|
-
const STYLE_ID = "accessify-alt-text-styles";
|
|
8003
|
-
function getStyles() {
|
|
8004
|
-
return `
|
|
8005
|
-
img[data-a11y-describable="true"]:not([data-a11y-is-link="true"]) {
|
|
8006
|
-
cursor: help !important;
|
|
8007
|
-
}
|
|
8008
|
-
|
|
8009
|
-
.accessify-describe-btn {
|
|
8010
|
-
position: absolute;
|
|
8011
|
-
top: 6px;
|
|
8012
|
-
right: 6px;
|
|
8013
|
-
z-index: 10;
|
|
8014
|
-
width: 26px;
|
|
8015
|
-
height: 26px;
|
|
8016
|
-
padding: 0;
|
|
8017
|
-
border: none;
|
|
8018
|
-
border-radius: 50%;
|
|
8019
|
-
background: rgba(0, 0, 0, 0.55);
|
|
8020
|
-
color: #fff;
|
|
8021
|
-
font-family: system-ui, -apple-system, sans-serif;
|
|
8022
|
-
font-size: 14px;
|
|
8023
|
-
font-weight: 700;
|
|
8024
|
-
line-height: 26px;
|
|
8025
|
-
text-align: center;
|
|
8026
|
-
cursor: help;
|
|
8027
|
-
opacity: 0;
|
|
8028
|
-
transition: opacity 0.15s ease;
|
|
8029
|
-
backdrop-filter: blur(4px);
|
|
8030
|
-
}
|
|
8031
|
-
img[data-a11y-is-link="true"]:hover ~ .accessify-describe-btn,
|
|
8032
|
-
.accessify-describe-btn:hover {
|
|
8033
|
-
opacity: 1;
|
|
8034
|
-
}
|
|
8035
|
-
.accessify-describe-btn:hover {
|
|
8036
|
-
background: rgba(0, 0, 0, 0.75);
|
|
8037
|
-
}
|
|
8038
|
-
|
|
8039
|
-
.accessify-describe-tooltip {
|
|
8040
|
-
position: fixed;
|
|
8041
|
-
z-index: 2147483647;
|
|
8042
|
-
max-width: 320px;
|
|
8043
|
-
padding: 10px 14px;
|
|
8044
|
-
background: rgba(0, 0, 0, 0.75);
|
|
8045
|
-
color: #fff;
|
|
8046
|
-
border-radius: 8px;
|
|
8047
|
-
backdrop-filter: blur(8px);
|
|
8048
|
-
font-family: system-ui, -apple-system, sans-serif;
|
|
8049
|
-
font-size: 13px;
|
|
8050
|
-
line-height: 1.5;
|
|
8051
|
-
word-break: break-word;
|
|
8052
|
-
animation: accessify-fade-in 0.12s ease;
|
|
8053
|
-
pointer-events: auto;
|
|
8054
|
-
}
|
|
8055
|
-
.accessify-describe-loading {
|
|
8056
|
-
display: flex;
|
|
8057
|
-
align-items: center;
|
|
8058
|
-
gap: 8px;
|
|
8059
|
-
color: #ddd;
|
|
8060
|
-
font-style: italic;
|
|
8061
|
-
}
|
|
8062
|
-
.accessify-describe-loading::before {
|
|
8063
|
-
content: '';
|
|
8064
|
-
width: 12px;
|
|
8065
|
-
height: 12px;
|
|
8066
|
-
border: 2px solid rgba(255,255,255,0.3);
|
|
8067
|
-
border-top-color: #fff;
|
|
8068
|
-
border-radius: 50%;
|
|
8069
|
-
animation: accessify-spin 0.8s linear infinite;
|
|
8070
|
-
}
|
|
8071
|
-
@keyframes accessify-spin {
|
|
8072
|
-
to { transform: rotate(360deg); }
|
|
8073
|
-
}
|
|
8074
|
-
@keyframes accessify-fade-in {
|
|
8075
|
-
from { opacity: 0; transform: translateY(4px); }
|
|
8076
|
-
to { opacity: 1; transform: translateY(0); }
|
|
8077
|
-
}
|
|
8078
|
-
`;
|
|
8079
|
-
}
|
|
8080
|
-
function injectStyles() {
|
|
8081
|
-
if (document.getElementById(STYLE_ID)) return;
|
|
8082
|
-
styleEl = document.createElement("style");
|
|
8083
|
-
styleEl.id = STYLE_ID;
|
|
8084
|
-
styleEl.textContent = getStyles();
|
|
8085
|
-
document.head.appendChild(styleEl);
|
|
8086
|
-
}
|
|
8087
|
-
function removeStyles() {
|
|
8088
|
-
styleEl?.remove();
|
|
8089
|
-
styleEl = null;
|
|
8090
|
-
document.getElementById(STYLE_ID)?.remove();
|
|
8091
|
-
}
|
|
8092
|
-
function showDescribeTooltip(text, rect, loading = false) {
|
|
8093
|
-
hideDescribeTooltip();
|
|
8094
|
-
const tip = document.createElement("div");
|
|
8095
|
-
tip.className = "accessify-describe-tooltip";
|
|
8096
|
-
if (loading) {
|
|
8097
|
-
const loadingEl = document.createElement("div");
|
|
8098
|
-
loadingEl.className = "accessify-describe-loading";
|
|
8099
|
-
loadingEl.textContent = isDE() ? "Bild wird analysiert…" : "Analyzing image…";
|
|
8100
|
-
tip.appendChild(loadingEl);
|
|
8101
|
-
} else {
|
|
8102
|
-
const textEl = document.createElement("div");
|
|
8103
|
-
textEl.textContent = text;
|
|
8104
|
-
tip.appendChild(textEl);
|
|
8105
|
-
}
|
|
8106
|
-
document.body.appendChild(tip);
|
|
8107
|
-
tooltipEl = tip;
|
|
8108
|
-
const tipRect = tip.getBoundingClientRect();
|
|
8109
|
-
let top = rect.bottom + 8;
|
|
8110
|
-
if (top + tipRect.height > window.innerHeight - 8) {
|
|
8111
|
-
top = rect.top - tipRect.height - 8;
|
|
8112
|
-
}
|
|
8113
|
-
if (top < 4) top = 4;
|
|
8114
|
-
let left = rect.left + (rect.width - tipRect.width) / 2;
|
|
8115
|
-
left = Math.max(8, Math.min(left, window.innerWidth - tipRect.width - 8));
|
|
8116
|
-
tip.style.top = `${top}px`;
|
|
8117
|
-
tip.style.left = `${left}px`;
|
|
8118
|
-
}
|
|
8119
|
-
function hideDescribeTooltip() {
|
|
8120
|
-
tooltipEl?.remove();
|
|
8121
|
-
tooltipEl = null;
|
|
8122
|
-
}
|
|
8123
|
-
async function handleImageClick(img) {
|
|
8124
|
-
const rect = img.getBoundingClientRect();
|
|
8125
|
-
const cached = img.dataset.accessifyDescription;
|
|
8126
|
-
if (cached) {
|
|
8127
|
-
showDescribeTooltip(cached, rect);
|
|
8128
|
-
return;
|
|
8129
|
-
}
|
|
8130
|
-
const existingAlt = img.getAttribute("alt");
|
|
8131
|
-
showDescribeTooltip("", rect, true);
|
|
8132
|
-
try {
|
|
8133
|
-
if (!aiService) {
|
|
8134
|
-
showDescribeTooltip(
|
|
8135
|
-
existingAlt || (isDE() ? "Kein API-Key konfiguriert" : "No API key configured"),
|
|
8136
|
-
rect
|
|
8137
|
-
);
|
|
8138
|
-
return;
|
|
8139
|
-
}
|
|
8140
|
-
const src = getImageSrc(img);
|
|
8141
|
-
const description = await aiService.describeImage(src, lang());
|
|
8142
|
-
if (!enabled) return;
|
|
8143
|
-
img.dataset.accessifyDescription = description;
|
|
8144
|
-
showDescribeTooltip(description, rect);
|
|
8145
|
-
const currentAlt = img.getAttribute("alt");
|
|
8146
|
-
if (!currentAlt || !currentAlt.trim()) {
|
|
8147
|
-
try {
|
|
8148
|
-
const ctx = gatherImageContext(img);
|
|
8149
|
-
const altText2 = await aiService.generateAltText(src, ctx, lang());
|
|
8150
|
-
if (altText2) {
|
|
8151
|
-
img.setAttribute("alt", altText2);
|
|
8152
|
-
img.setAttribute("title", altText2);
|
|
8153
|
-
setCachedAltText(src, altText2, lang()).catch(() => {
|
|
8154
|
-
});
|
|
8155
|
-
if (siteKey) persistAltTextToServer(siteKey, proxyUrl, src, altText2, lang());
|
|
8156
|
-
}
|
|
8157
|
-
} catch {
|
|
8158
|
-
}
|
|
8159
|
-
}
|
|
8160
|
-
} catch (err) {
|
|
8161
|
-
console.warn("[Accessify] Image description failed:", err);
|
|
8162
|
-
showDescribeTooltip(
|
|
8163
|
-
existingAlt || (isDE() ? "Beschreibung konnte nicht geladen werden" : "Could not load description"),
|
|
8164
|
-
rect
|
|
8165
|
-
);
|
|
8166
|
-
}
|
|
8167
|
-
}
|
|
8168
|
-
function isLinkedImage(img) {
|
|
8169
|
-
return !!img.closest("a[href]");
|
|
8170
|
-
}
|
|
8171
|
-
function addDescribeButton(img) {
|
|
8172
|
-
if (describeButtons.has(img)) return;
|
|
8173
|
-
const parent = img.parentElement;
|
|
8174
|
-
if (!parent) return;
|
|
8175
|
-
const pos = getComputedStyle(parent).position;
|
|
8176
|
-
if (pos === "static") parent.style.position = "relative";
|
|
8177
|
-
const btn = document.createElement("button");
|
|
8178
|
-
btn.className = "accessify-describe-btn";
|
|
8179
|
-
btn.textContent = "i";
|
|
8180
|
-
btn.setAttribute("aria-label", isDE() ? "Bildbeschreibung anzeigen" : "Show image description");
|
|
8181
|
-
btn.addEventListener("click", (e) => {
|
|
8182
|
-
e.preventDefault();
|
|
8183
|
-
e.stopPropagation();
|
|
8184
|
-
handleImageClick(img);
|
|
8185
|
-
});
|
|
8186
|
-
img.insertAdjacentElement("afterend", btn);
|
|
8187
|
-
describeButtons.set(img, btn);
|
|
8188
|
-
}
|
|
8189
|
-
function removeDescribeButtons() {
|
|
8190
|
-
describeButtons.forEach((btn) => btn.remove());
|
|
8191
|
-
describeButtons.clear();
|
|
8192
|
-
}
|
|
8193
|
-
function markImageDescribable(img) {
|
|
8194
|
-
if (img.closest("#accessify-root") || img.closest("accessify-widget")) return;
|
|
8195
|
-
if (img.complete && img.naturalWidth > 0 && (img.naturalWidth < 20 || img.naturalHeight < 20)) return;
|
|
8196
|
-
img.dataset.a11yDescribable = "true";
|
|
8197
|
-
if (isLinkedImage(img)) {
|
|
8198
|
-
img.dataset.a11yIsLink = "true";
|
|
8199
|
-
addDescribeButton(img);
|
|
8200
|
-
}
|
|
8201
|
-
}
|
|
8202
|
-
function enableClickToDescribe() {
|
|
8203
|
-
document.querySelectorAll("img").forEach(markImageDescribable);
|
|
8204
|
-
describeClickHandler = (e) => {
|
|
8205
|
-
if (e.target.closest(".accessify-describe-tooltip")) return;
|
|
8206
|
-
if (e.target.closest(".accessify-describe-btn")) return;
|
|
8207
|
-
const img = e.target.closest("img[data-a11y-describable]");
|
|
8208
|
-
if (!img) {
|
|
8209
|
-
if (tooltipEl) hideDescribeTooltip();
|
|
8210
|
-
return;
|
|
8211
|
-
}
|
|
8212
|
-
if (isLinkedImage(img)) return;
|
|
8213
|
-
e.preventDefault();
|
|
8214
|
-
e.stopPropagation();
|
|
8215
|
-
handleImageClick(img);
|
|
8216
|
-
};
|
|
8217
|
-
document.addEventListener("click", describeClickHandler, true);
|
|
8218
|
-
describeEscHandler = (e) => {
|
|
8219
|
-
if (e.key === "Escape") hideDescribeTooltip();
|
|
8220
|
-
};
|
|
8221
|
-
document.addEventListener("keydown", describeEscHandler);
|
|
8222
|
-
}
|
|
8223
|
-
function disableClickToDescribe() {
|
|
8224
|
-
if (describeClickHandler) {
|
|
8225
|
-
document.removeEventListener("click", describeClickHandler, true);
|
|
8226
|
-
describeClickHandler = null;
|
|
8227
|
-
}
|
|
8228
|
-
if (describeEscHandler) {
|
|
8229
|
-
document.removeEventListener("keydown", describeEscHandler);
|
|
8230
|
-
describeEscHandler = null;
|
|
8231
|
-
}
|
|
8232
|
-
hideDescribeTooltip();
|
|
8233
|
-
removeDescribeButtons();
|
|
8234
|
-
document.querySelectorAll("img[data-a11y-describable]").forEach((img) => {
|
|
8235
|
-
delete img.dataset.a11yDescribable;
|
|
8236
|
-
delete img.dataset.a11yIsLink;
|
|
8237
|
-
});
|
|
8238
|
-
}
|
|
8239
8042
|
function isGenericAlt(alt) {
|
|
8240
8043
|
if (!alt.trim()) return true;
|
|
8241
8044
|
if (/^(image|img|photo|bild|foto|untitled|placeholder)/i.test(alt)) return true;
|
|
@@ -8249,9 +8052,13 @@ function createAltTextModule(aiService, initialLang = "de", serverConfig) {
|
|
|
8249
8052
|
if (img.complete && img.naturalWidth > 0 && (img.naturalWidth < 50 || img.naturalHeight < 50)) return true;
|
|
8250
8053
|
return false;
|
|
8251
8054
|
}
|
|
8252
|
-
function
|
|
8055
|
+
function isValidImage(img) {
|
|
8253
8056
|
if (img.closest("#accessify-root") || img.closest("accessify-widget")) return false;
|
|
8254
8057
|
if (img.complete && img.naturalWidth > 0 && (img.naturalWidth < 20 || img.naturalHeight < 20)) return false;
|
|
8058
|
+
return true;
|
|
8059
|
+
}
|
|
8060
|
+
function needsAltText(img) {
|
|
8061
|
+
if (!isValidImage(img)) return false;
|
|
8255
8062
|
const alt = img.getAttribute("alt");
|
|
8256
8063
|
if (alt === null) return true;
|
|
8257
8064
|
if (isGenericAlt(alt)) return true;
|
|
@@ -8297,6 +8104,96 @@ function createAltTextModule(aiService, initialLang = "de", serverConfig) {
|
|
|
8297
8104
|
img.setAttribute("title", altText2);
|
|
8298
8105
|
processedImages.set(img, { generatedAlt: altText2 });
|
|
8299
8106
|
}
|
|
8107
|
+
function applyTitlesToAllImages() {
|
|
8108
|
+
document.querySelectorAll("img").forEach((img) => {
|
|
8109
|
+
if (!isValidImage(img)) return;
|
|
8110
|
+
if (img.getAttribute("title")) return;
|
|
8111
|
+
const alt = img.getAttribute("alt");
|
|
8112
|
+
if (alt && alt.trim()) {
|
|
8113
|
+
img.setAttribute("title", alt);
|
|
8114
|
+
img.setAttribute("data-accessify-title", "auto");
|
|
8115
|
+
}
|
|
8116
|
+
});
|
|
8117
|
+
}
|
|
8118
|
+
function removeTitles() {
|
|
8119
|
+
document.querySelectorAll('img[data-accessify-title="auto"]').forEach((img) => {
|
|
8120
|
+
img.removeAttribute("title");
|
|
8121
|
+
img.removeAttribute("data-accessify-title");
|
|
8122
|
+
});
|
|
8123
|
+
document.querySelectorAll("[data-accessify-bg-alt]").forEach((el) => {
|
|
8124
|
+
el.removeAttribute("role");
|
|
8125
|
+
el.removeAttribute("aria-label");
|
|
8126
|
+
el.removeAttribute("title");
|
|
8127
|
+
el.removeAttribute("data-accessify-bg-alt");
|
|
8128
|
+
});
|
|
8129
|
+
}
|
|
8130
|
+
function scanForBackgroundImages() {
|
|
8131
|
+
const found = [];
|
|
8132
|
+
const candidates = document.querySelectorAll(
|
|
8133
|
+
'header, [class*="hero"], [class*="banner"], [class*="header"], [class*="bg-"], [class*="background"], [style*="background"]'
|
|
8134
|
+
);
|
|
8135
|
+
for (const el of candidates) {
|
|
8136
|
+
if (el.closest("#accessify-root") || el.closest("accessify-widget")) continue;
|
|
8137
|
+
if (el.getAttribute("data-accessify-bg-alt")) continue;
|
|
8138
|
+
const style = getComputedStyle(el);
|
|
8139
|
+
const bg = style.backgroundImage;
|
|
8140
|
+
if (!bg || bg === "none") continue;
|
|
8141
|
+
const urlMatch = bg.match(/url\(["']?([^"')]+)["']?\)/);
|
|
8142
|
+
if (!urlMatch) continue;
|
|
8143
|
+
const rect = el.getBoundingClientRect();
|
|
8144
|
+
if (rect.width < 100 || rect.height < 100) continue;
|
|
8145
|
+
if (el.getAttribute("role") === "img" && el.getAttribute("aria-label")) continue;
|
|
8146
|
+
found.push(el);
|
|
8147
|
+
}
|
|
8148
|
+
return found;
|
|
8149
|
+
}
|
|
8150
|
+
function getBackgroundImageUrl(el) {
|
|
8151
|
+
const bg = getComputedStyle(el).backgroundImage;
|
|
8152
|
+
const match = bg?.match(/url\(["']?([^"')]+)["']?\)/);
|
|
8153
|
+
return match ? match[1] : null;
|
|
8154
|
+
}
|
|
8155
|
+
function applyBgAlt(el, altText2) {
|
|
8156
|
+
el.setAttribute("role", "img");
|
|
8157
|
+
el.setAttribute("aria-label", altText2);
|
|
8158
|
+
el.setAttribute("title", altText2);
|
|
8159
|
+
el.setAttribute("data-accessify-bg-alt", "auto");
|
|
8160
|
+
}
|
|
8161
|
+
async function generateBgAlts() {
|
|
8162
|
+
if (!aiService || !enabled) return;
|
|
8163
|
+
const bgElements = scanForBackgroundImages();
|
|
8164
|
+
for (const el of bgElements) {
|
|
8165
|
+
if (!enabled) break;
|
|
8166
|
+
const src = getBackgroundImageUrl(el);
|
|
8167
|
+
if (!src) continue;
|
|
8168
|
+
const cached = await getCachedAltText(src);
|
|
8169
|
+
if (cached) {
|
|
8170
|
+
applyBgAlt(el, cached);
|
|
8171
|
+
continue;
|
|
8172
|
+
}
|
|
8173
|
+
try {
|
|
8174
|
+
const ctx = gatherBgContext(el);
|
|
8175
|
+
const alt = await aiService.generateAltText(src, ctx, lang());
|
|
8176
|
+
if (alt && enabled) {
|
|
8177
|
+
applyBgAlt(el, alt);
|
|
8178
|
+
setCachedAltText(src, alt, lang()).catch(() => {
|
|
8179
|
+
});
|
|
8180
|
+
if (siteKey) persistAltTextToServer(siteKey, proxyUrl, src, alt, lang());
|
|
8181
|
+
}
|
|
8182
|
+
} catch (err) {
|
|
8183
|
+
console.warn("[Accessify] Bg alt-text generation failed:", src, err);
|
|
8184
|
+
}
|
|
8185
|
+
}
|
|
8186
|
+
}
|
|
8187
|
+
function gatherBgContext(el) {
|
|
8188
|
+
const parts = [];
|
|
8189
|
+
parts.push(`Element: <${el.tagName.toLowerCase()}>`);
|
|
8190
|
+
if (el.className) parts.push(`Class: ${el.className}`);
|
|
8191
|
+
const text = el.textContent?.trim().slice(0, 100);
|
|
8192
|
+
if (text) parts.push(`Overlay text: ${text}`);
|
|
8193
|
+
const heading = el.querySelector("h1, h2, h3");
|
|
8194
|
+
if (heading?.textContent?.trim()) parts.push(`Heading: ${heading.textContent.trim()}`);
|
|
8195
|
+
return parts.join(". ");
|
|
8196
|
+
}
|
|
8300
8197
|
async function generateAll() {
|
|
8301
8198
|
if (autoGenerating || !enabled || !aiService) return;
|
|
8302
8199
|
autoGenerating = true;
|
|
@@ -8344,7 +8241,6 @@ function createAltTextModule(aiService, initialLang = "de", serverConfig) {
|
|
|
8344
8241
|
const cached = await getCachedAltText(src);
|
|
8345
8242
|
if (cached) {
|
|
8346
8243
|
applyAltText(img, cached);
|
|
8347
|
-
updateBadge();
|
|
8348
8244
|
return;
|
|
8349
8245
|
}
|
|
8350
8246
|
try {
|
|
@@ -8362,15 +8258,20 @@ function createAltTextModule(aiService, initialLang = "de", serverConfig) {
|
|
|
8362
8258
|
}
|
|
8363
8259
|
function tryRegisterImage(img) {
|
|
8364
8260
|
if (!enabled) return;
|
|
8365
|
-
if (img
|
|
8261
|
+
if (!isValidImage(img)) return;
|
|
8366
8262
|
if (missingAltImages.includes(img)) return;
|
|
8367
8263
|
function addIfValid() {
|
|
8368
8264
|
if (!enabled || missingAltImages.includes(img)) return;
|
|
8369
8265
|
if (img.naturalWidth < 20 || img.naturalHeight < 20) return;
|
|
8370
|
-
markImageDescribable(img);
|
|
8371
8266
|
if (needsAltText(img)) {
|
|
8372
8267
|
missingAltImages.push(img);
|
|
8373
8268
|
generateSingle(img);
|
|
8269
|
+
} else {
|
|
8270
|
+
const alt = img.getAttribute("alt");
|
|
8271
|
+
if (alt && alt.trim() && !img.getAttribute("title")) {
|
|
8272
|
+
img.setAttribute("title", alt);
|
|
8273
|
+
img.setAttribute("data-accessify-title", "auto");
|
|
8274
|
+
}
|
|
8374
8275
|
}
|
|
8375
8276
|
}
|
|
8376
8277
|
if (img.complete && img.naturalWidth > 0) addIfValid();
|
|
@@ -8379,9 +8280,7 @@ function createAltTextModule(aiService, initialLang = "de", serverConfig) {
|
|
|
8379
8280
|
function activate() {
|
|
8380
8281
|
if (enabled) return;
|
|
8381
8282
|
enabled = true;
|
|
8382
|
-
|
|
8383
|
-
const alreadyApplied = document.querySelectorAll('img[data-accessify-alt="auto"]');
|
|
8384
|
-
alreadyApplied.forEach((img) => {
|
|
8283
|
+
document.querySelectorAll('img[data-accessify-alt="auto"]').forEach((img) => {
|
|
8385
8284
|
if (img.closest("#accessify-root")) return;
|
|
8386
8285
|
if (!processedImages.has(img)) {
|
|
8387
8286
|
processedImages.set(img, { generatedAlt: img.getAttribute("alt") || "" });
|
|
@@ -8395,21 +8294,15 @@ function createAltTextModule(aiService, initialLang = "de", serverConfig) {
|
|
|
8395
8294
|
}
|
|
8396
8295
|
}
|
|
8397
8296
|
generateAll();
|
|
8398
|
-
|
|
8399
|
-
|
|
8400
|
-
const alt = img.getAttribute("alt");
|
|
8401
|
-
if (alt && alt.trim() && !img.getAttribute("title")) {
|
|
8402
|
-
img.setAttribute("title", alt);
|
|
8403
|
-
img.setAttribute("data-accessify-title", "auto");
|
|
8404
|
-
}
|
|
8405
|
-
});
|
|
8406
|
-
enableClickToDescribe();
|
|
8297
|
+
generateBgAlts();
|
|
8298
|
+
applyTitlesToAllImages();
|
|
8407
8299
|
document.querySelectorAll("img").forEach((img) => {
|
|
8408
8300
|
if (!img.complete) tryRegisterImage(img);
|
|
8409
8301
|
});
|
|
8410
8302
|
setTimeout(() => {
|
|
8411
8303
|
if (enabled) {
|
|
8412
8304
|
document.querySelectorAll("img").forEach(tryRegisterImage);
|
|
8305
|
+
applyTitlesToAllImages();
|
|
8413
8306
|
}
|
|
8414
8307
|
}, 2e3);
|
|
8415
8308
|
domObserver = new MutationObserver((mutations) => {
|
|
@@ -8418,11 +8311,6 @@ function createAltTextModule(aiService, initialLang = "de", serverConfig) {
|
|
|
8418
8311
|
if (node instanceof HTMLImageElement) tryRegisterImage(node);
|
|
8419
8312
|
else if (node instanceof HTMLElement) {
|
|
8420
8313
|
node.querySelectorAll("img").forEach(tryRegisterImage);
|
|
8421
|
-
node.querySelectorAll("img").forEach((img) => {
|
|
8422
|
-
if (!img.closest("#accessify-root") && !img.closest("accessify-widget")) {
|
|
8423
|
-
img.dataset.a11yDescribable = "true";
|
|
8424
|
-
}
|
|
8425
|
-
});
|
|
8426
8314
|
}
|
|
8427
8315
|
}
|
|
8428
8316
|
}
|
|
@@ -8434,20 +8322,15 @@ function createAltTextModule(aiService, initialLang = "de", serverConfig) {
|
|
|
8434
8322
|
autoGenerating = false;
|
|
8435
8323
|
domObserver?.disconnect();
|
|
8436
8324
|
domObserver = null;
|
|
8437
|
-
|
|
8438
|
-
document.querySelectorAll('img[data-accessify-title="auto"]').forEach((img) => {
|
|
8439
|
-
img.removeAttribute("title");
|
|
8440
|
-
img.removeAttribute("data-accessify-title");
|
|
8441
|
-
});
|
|
8325
|
+
removeTitles();
|
|
8442
8326
|
missingAltImages = [];
|
|
8443
|
-
removeStyles();
|
|
8444
8327
|
}
|
|
8445
8328
|
autoApplyCachedAltTexts({ siteKey, proxyUrl, lang: initialLang }).catch(() => {
|
|
8446
8329
|
});
|
|
8447
8330
|
return {
|
|
8448
8331
|
id: "alt-text",
|
|
8449
8332
|
name: () => isDE() ? "Bildbeschreibung" : "Image Description",
|
|
8450
|
-
description: isDE() ? "
|
|
8333
|
+
description: isDE() ? "Bildbeschreibungen per Hover anzeigen und fehlende automatisch erzeugen" : "Show image descriptions on hover and auto-generate missing ones",
|
|
8451
8334
|
icon: "alt-text",
|
|
8452
8335
|
category: "ai",
|
|
8453
8336
|
activate,
|
|
@@ -8653,4 +8536,4 @@ export {
|
|
|
8653
8536
|
init as i,
|
|
8654
8537
|
t
|
|
8655
8538
|
};
|
|
8656
|
-
//# sourceMappingURL=index-
|
|
8539
|
+
//# sourceMappingURL=index-BTkeEVoS.js.map
|