accessify-widget 0.3.82 → 0.3.84
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/{animation-stop-C4OcBKkR.js → animation-stop-DrDe9Q9n.js} +15 -35
- package/dist/animation-stop-DrDe9Q9n.js.map +1 -0
- package/dist/{index-Bdke9nRb.js → index-CJB-hRuq.js} +753 -36
- package/dist/index-CJB-hRuq.js.map +1 -0
- package/dist/{keyboard-nav-AFyqDbnd.js → keyboard-nav-Bxe4jywQ.js} +2 -2
- package/dist/{keyboard-nav-AFyqDbnd.js.map → keyboard-nav-Bxe4jywQ.js.map} +1 -1
- package/dist/{page-structure-BpCqEHJ6.js → page-structure-BOFg6Zbt.js} +3 -7
- package/dist/page-structure-BOFg6Zbt.js.map +1 -0
- package/dist/{text-simplify-7XhQbbXL.js → text-simplify-BIFpqadq.js} +3 -9
- package/dist/{text-simplify-7XhQbbXL.js.map → text-simplify-BIFpqadq.js.map} +1 -1
- package/dist/{tts-64TqRR7s.js → tts-CjszLRnb.js} +11 -37
- package/dist/tts-CjszLRnb.js.map +1 -0
- package/dist/widget.js +1 -1
- package/dist/widget.js.map +1 -1
- package/package.json +1 -1
- package/dist/alt-text-Dg2ted6-.js +0 -747
- package/dist/alt-text-Dg2ted6-.js.map +0 -1
- package/dist/animation-stop-C4OcBKkR.js.map +0 -1
- package/dist/index-Bdke9nRb.js.map +0 -1
- package/dist/page-structure-BpCqEHJ6.js.map +0 -1
- package/dist/tts-64TqRR7s.js.map +0 -1
|
@@ -6620,8 +6620,6 @@ function FeatureGrid($$anchor, $$props) {
|
|
|
6620
6620
|
}
|
|
6621
6621
|
};
|
|
6622
6622
|
const INLINE_CONTROLS = /* @__PURE__ */ new Set(["contrast", "text-size"]);
|
|
6623
|
-
const MOUSE_ONLY_FEATURES = /* @__PURE__ */ new Set(["big-cursor", "reading-guide", "reading-mask"]);
|
|
6624
|
-
const isTouchOnly = typeof window !== "undefined" && navigator.maxTouchPoints > 0 && window.matchMedia("(pointer:coarse)").matches && !window.matchMedia("(pointer:fine)").matches;
|
|
6625
6623
|
const CONTRAST_MODES = [
|
|
6626
6624
|
{
|
|
6627
6625
|
id: "light",
|
|
@@ -6644,17 +6642,17 @@ function FeatureGrid($$anchor, $$props) {
|
|
|
6644
6642
|
const FEATURE_LOADERS = {
|
|
6645
6643
|
contrast: () => import("./contrast-CqsOs6Uo.js"),
|
|
6646
6644
|
"text-size": () => import("./text-size-m_mHNPWo.js"),
|
|
6647
|
-
"keyboard-nav": () => import("./keyboard-nav-
|
|
6645
|
+
"keyboard-nav": () => import("./keyboard-nav-Bxe4jywQ.js"),
|
|
6648
6646
|
"link-highlight": () => import("./link-highlight-D9gxFmiG.js"),
|
|
6649
6647
|
"reading-guide": () => import("./reading-guide-C_jxzorm.js"),
|
|
6650
6648
|
"reading-mask": () => import("./reading-mask-B_NxbhTN.js"),
|
|
6651
|
-
"animation-stop": () => import("./animation-stop-
|
|
6649
|
+
"animation-stop": () => import("./animation-stop-DrDe9Q9n.js"),
|
|
6652
6650
|
"hide-images": () => import("./hide-images-B_LeCBcd.js"),
|
|
6653
6651
|
"big-cursor": () => import("./big-cursor-B2UKu9dQ.js"),
|
|
6654
|
-
"page-structure": () => import("./page-structure-
|
|
6655
|
-
tts: () => import("./tts-
|
|
6656
|
-
"text-simplify": () => import("./text-simplify-
|
|
6657
|
-
"alt-text": () =>
|
|
6652
|
+
"page-structure": () => import("./page-structure-BOFg6Zbt.js"),
|
|
6653
|
+
tts: () => import("./tts-CjszLRnb.js"),
|
|
6654
|
+
"text-simplify": () => import("./text-simplify-BIFpqadq.js"),
|
|
6655
|
+
"alt-text": () => Promise.resolve().then(() => altText)
|
|
6658
6656
|
};
|
|
6659
6657
|
let contrastMode = /* @__PURE__ */ state(proxy(readStoredContrastMode()));
|
|
6660
6658
|
let textSize = /* @__PURE__ */ state(proxy(readStoredTextSize()));
|
|
@@ -6788,7 +6786,7 @@ function FeatureGrid($$anchor, $$props) {
|
|
|
6788
6786
|
$$props.ontoggle("text-size", true);
|
|
6789
6787
|
$$props.ontextsize?.(value);
|
|
6790
6788
|
}
|
|
6791
|
-
let cardFeatures = /* @__PURE__ */ user_derived(() => $$props.config.features.filter((id) => !INLINE_CONTROLS.has(id) && FEATURE_REGISTRY[id]
|
|
6789
|
+
let cardFeatures = /* @__PURE__ */ user_derived(() => $$props.config.features.filter((id) => !INLINE_CONTROLS.has(id) && FEATURE_REGISTRY[id]).map((id) => FEATURE_REGISTRY[id]));
|
|
6792
6790
|
user_effect(() => {
|
|
6793
6791
|
`${$$props.config.features.join("|")}|${get(activeSignature)}`;
|
|
6794
6792
|
void syncActiveModules();
|
|
@@ -6971,8 +6969,8 @@ function FeatureGrid($$anchor, $$props) {
|
|
|
6971
6969
|
pop();
|
|
6972
6970
|
}
|
|
6973
6971
|
delegate(["click"]);
|
|
6974
|
-
var root_1$1 = /* @__PURE__ */ from_html(`<span aria-hidden="true">·</span> <a target="_blank" rel="noopener noreferrer">
|
|
6975
|
-
var root$1 = /* @__PURE__ */ from_html(`<div class="accessify-footer"><div class="accessify-footer-links"><a target="_blank" rel="noopener noreferrer">Accessify Dashboard
|
|
6972
|
+
var root_1$1 = /* @__PURE__ */ from_html(`<span aria-hidden="true">·</span> <a target="_blank" rel="noopener noreferrer"> </a>`, 1);
|
|
6973
|
+
var root$1 = /* @__PURE__ */ from_html(`<div class="accessify-footer"><div class="accessify-footer-links"><a target="_blank" rel="noopener noreferrer">Accessify Dashboard</a> <!></div> <button class="accessify-footer-close"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg> <span> </span></button></div>`);
|
|
6976
6974
|
function PanelFooter($$anchor, $$props) {
|
|
6977
6975
|
push($$props, true);
|
|
6978
6976
|
let lang = prop($$props, "lang", 3, "en");
|
|
@@ -6988,7 +6986,7 @@ function PanelFooter($$anchor, $$props) {
|
|
|
6988
6986
|
template_effect(
|
|
6989
6987
|
($0) => {
|
|
6990
6988
|
set_attribute(a_1, "href", $$props.statementUrl);
|
|
6991
|
-
set_text(text,
|
|
6989
|
+
set_text(text, $0);
|
|
6992
6990
|
},
|
|
6993
6991
|
[() => t("widget.statement", lang())]
|
|
6994
6992
|
);
|
|
@@ -7382,7 +7380,7 @@ function createTextTransformService() {
|
|
|
7382
7380
|
var root_3 = /* @__PURE__ */ from_svg(`<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6L9 17l-5-5"></path></svg>`);
|
|
7383
7381
|
var root_2 = /* @__PURE__ */ from_html(`<button class="accessify-lang-option" role="option"><span> </span> <!></button>`);
|
|
7384
7382
|
var root_1 = /* @__PURE__ */ from_html(`<div id="accessify-lang-list" class="accessify-lang-list" role="listbox"></div>`);
|
|
7385
|
-
var root = /* @__PURE__ */ from_html(`<div class="sr-only" role="status" aria-live="polite" aria-atomic="true"> </div> <!> <div class="accessify-panel" role="dialog"
|
|
7383
|
+
var root = /* @__PURE__ */ from_html(`<div class="sr-only" role="status" aria-live="polite" aria-atomic="true"> </div> <!> <div class="accessify-panel" role="dialog"><!> <div class="accessify-body"><div class="accessify-lang-dropdown"><button class="accessify-lang-toggle" aria-controls="accessify-lang-list"><span class="accessify-lang-toggle-left"><!> <span> </span></span> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="6 9 12 15 18 9"></polyline></svg></button> <!></div> <div class="accessify-section-divider"></div> <!> <div class="accessify-section-divider"></div> <!></div> <!></div>`, 1);
|
|
7386
7384
|
function WidgetApp($$anchor, $$props) {
|
|
7387
7385
|
push($$props, true);
|
|
7388
7386
|
let config2 = prop($$props, "config", 7);
|
|
@@ -8067,11 +8065,7 @@ function createWidgetStyles(config2) {
|
|
|
8067
8065
|
background: var(--surface-dim); color: var(--accent);
|
|
8068
8066
|
}
|
|
8069
8067
|
.accessify-card-icon svg { width: 28px; height: 28px; stroke-width: 2.5; }
|
|
8070
|
-
.accessify-card-label {
|
|
8071
|
-
font-size: 12px; font-weight: 600; line-height: 1.25;
|
|
8072
|
-
display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
|
|
8073
|
-
overflow: hidden; word-break: break-word;
|
|
8074
|
-
}
|
|
8068
|
+
.accessify-card-label { font-size: 12px; font-weight: 600; line-height: 1.25; }
|
|
8075
8069
|
|
|
8076
8070
|
/* Info icon + tooltip */
|
|
8077
8071
|
.accessify-card-info {
|
|
@@ -8079,10 +8073,10 @@ function createWidgetStyles(config2) {
|
|
|
8079
8073
|
display: inline-flex; align-items: center; justify-content: center;
|
|
8080
8074
|
width: 22px; height: 22px; border-radius: 999px;
|
|
8081
8075
|
color: var(--text-dim); cursor: help; background: none; border: none;
|
|
8082
|
-
opacity: 0.
|
|
8076
|
+
opacity: 0.5; transition: opacity var(--fast), color var(--fast);
|
|
8083
8077
|
}
|
|
8084
8078
|
.accessify-card-info svg { width: 14px; height: 14px; }
|
|
8085
|
-
.accessify-card-info:hover, .accessify-card-info:active
|
|
8079
|
+
.accessify-card-info:hover, .accessify-card-info:active { opacity: 1; color: var(--accent); }
|
|
8086
8080
|
|
|
8087
8081
|
/* ─── Section Divider ─── */
|
|
8088
8082
|
.accessify-section-divider {
|
|
@@ -8139,10 +8133,742 @@ function createWidgetStyles(config2) {
|
|
|
8139
8133
|
.accessify-stepper-btn { width: 44px; min-height: 44px; }
|
|
8140
8134
|
.accessify-stepper { height: 44px; }
|
|
8141
8135
|
.accessify-footer-close { min-height: 44px; padding: 8px 16px; }
|
|
8142
|
-
.accessify-drag-handle { display: none; }
|
|
8143
8136
|
}
|
|
8144
8137
|
`;
|
|
8145
8138
|
}
|
|
8139
|
+
const IDB_NAME = "accessify-alt-text-cache";
|
|
8140
|
+
const IDB_STORE = "alt-texts";
|
|
8141
|
+
const IDB_VERSION = 2;
|
|
8142
|
+
function hashSrc(src) {
|
|
8143
|
+
let hash = 5381;
|
|
8144
|
+
const n = src.toLowerCase().trim();
|
|
8145
|
+
for (let i = 0; i < n.length; i++) {
|
|
8146
|
+
hash = (hash << 5) + hash + n.charCodeAt(i);
|
|
8147
|
+
hash |= 0;
|
|
8148
|
+
}
|
|
8149
|
+
return Math.abs(hash).toString(36);
|
|
8150
|
+
}
|
|
8151
|
+
function normalizeImageUrl(url) {
|
|
8152
|
+
try {
|
|
8153
|
+
const u = new URL(url);
|
|
8154
|
+
u.searchParams.delete("width");
|
|
8155
|
+
u.searchParams.delete("height");
|
|
8156
|
+
u.searchParams.delete("scale-down-to");
|
|
8157
|
+
return u.href;
|
|
8158
|
+
} catch {
|
|
8159
|
+
return url;
|
|
8160
|
+
}
|
|
8161
|
+
}
|
|
8162
|
+
function getImageSrc(img) {
|
|
8163
|
+
return img.currentSrc || img.src;
|
|
8164
|
+
}
|
|
8165
|
+
function openCache() {
|
|
8166
|
+
return new Promise((resolve) => {
|
|
8167
|
+
try {
|
|
8168
|
+
const req = indexedDB.open(IDB_NAME, IDB_VERSION);
|
|
8169
|
+
req.onupgradeneeded = () => {
|
|
8170
|
+
const db = req.result;
|
|
8171
|
+
if (!db.objectStoreNames.contains(IDB_STORE)) db.createObjectStore(IDB_STORE, { keyPath: "key" });
|
|
8172
|
+
if (!db.objectStoreNames.contains("alt-text-reports")) db.createObjectStore("alt-text-reports", { keyPath: "key" });
|
|
8173
|
+
};
|
|
8174
|
+
req.onsuccess = () => resolve(req.result);
|
|
8175
|
+
req.onerror = () => resolve(null);
|
|
8176
|
+
} catch {
|
|
8177
|
+
resolve(null);
|
|
8178
|
+
}
|
|
8179
|
+
});
|
|
8180
|
+
}
|
|
8181
|
+
async function getAllCachedAltTexts() {
|
|
8182
|
+
const db = await openCache();
|
|
8183
|
+
const map = /* @__PURE__ */ new Map();
|
|
8184
|
+
if (!db) return map;
|
|
8185
|
+
return new Promise((resolve) => {
|
|
8186
|
+
try {
|
|
8187
|
+
const tx = db.transaction(IDB_STORE, "readonly");
|
|
8188
|
+
const req = tx.objectStore(IDB_STORE).getAll();
|
|
8189
|
+
req.onsuccess = () => {
|
|
8190
|
+
for (const r of req.result) {
|
|
8191
|
+
map.set(r.src, r.altText);
|
|
8192
|
+
}
|
|
8193
|
+
resolve(map);
|
|
8194
|
+
};
|
|
8195
|
+
req.onerror = () => resolve(map);
|
|
8196
|
+
} catch {
|
|
8197
|
+
resolve(map);
|
|
8198
|
+
}
|
|
8199
|
+
});
|
|
8200
|
+
}
|
|
8201
|
+
async function getCachedAltText(src) {
|
|
8202
|
+
const db = await openCache();
|
|
8203
|
+
if (!db) return null;
|
|
8204
|
+
return new Promise((resolve) => {
|
|
8205
|
+
try {
|
|
8206
|
+
const tx = db.transaction(IDB_STORE, "readonly");
|
|
8207
|
+
const store = tx.objectStore(IDB_STORE);
|
|
8208
|
+
const req1 = store.get(hashSrc(src));
|
|
8209
|
+
req1.onsuccess = () => {
|
|
8210
|
+
if (req1.result?.altText) {
|
|
8211
|
+
resolve(req1.result.altText);
|
|
8212
|
+
return;
|
|
8213
|
+
}
|
|
8214
|
+
const norm = normalizeImageUrl(src);
|
|
8215
|
+
if (norm !== src) {
|
|
8216
|
+
const req2 = store.get(hashSrc(norm));
|
|
8217
|
+
req2.onsuccess = () => resolve(req2.result?.altText || null);
|
|
8218
|
+
req2.onerror = () => resolve(null);
|
|
8219
|
+
} else {
|
|
8220
|
+
resolve(null);
|
|
8221
|
+
}
|
|
8222
|
+
};
|
|
8223
|
+
req1.onerror = () => resolve(null);
|
|
8224
|
+
} catch {
|
|
8225
|
+
resolve(null);
|
|
8226
|
+
}
|
|
8227
|
+
});
|
|
8228
|
+
}
|
|
8229
|
+
async function setCachedAltText(src, altText2, langCode) {
|
|
8230
|
+
const db = await openCache();
|
|
8231
|
+
if (!db) return;
|
|
8232
|
+
return new Promise((resolve) => {
|
|
8233
|
+
try {
|
|
8234
|
+
const tx = db.transaction(IDB_STORE, "readwrite");
|
|
8235
|
+
tx.objectStore(IDB_STORE).put({ key: hashSrc(src), src, altText: altText2, lang: langCode, createdAt: Date.now() });
|
|
8236
|
+
tx.oncomplete = () => resolve();
|
|
8237
|
+
tx.onerror = () => resolve();
|
|
8238
|
+
} catch {
|
|
8239
|
+
resolve();
|
|
8240
|
+
}
|
|
8241
|
+
});
|
|
8242
|
+
}
|
|
8243
|
+
const DEFAULT_API_BASE = "https://accessify-api.accessify.workers.dev";
|
|
8244
|
+
let currentAltSiteMode = "manual";
|
|
8245
|
+
async function fetchServerAltTexts(siteKey, proxyUrl, lang) {
|
|
8246
|
+
const map = /* @__PURE__ */ new Map();
|
|
8247
|
+
try {
|
|
8248
|
+
const base = proxyUrl || DEFAULT_API_BASE;
|
|
8249
|
+
const pageUrl = window.location.origin + window.location.pathname;
|
|
8250
|
+
const res = await fetch(`${base}/v1/manifest?siteKey=${encodeURIComponent(siteKey)}&url=${encodeURIComponent(pageUrl)}&feature=alt_text&variant=${encodeURIComponent(lang)}`, {
|
|
8251
|
+
headers: { "Accept": "application/json" },
|
|
8252
|
+
cache: "no-cache"
|
|
8253
|
+
});
|
|
8254
|
+
if (!res.ok) return map;
|
|
8255
|
+
const data = await res.json();
|
|
8256
|
+
currentAltSiteMode = data.siteMode === "auto" ? "auto" : "manual";
|
|
8257
|
+
if (data.blocks) {
|
|
8258
|
+
for (const block2 of data.blocks) {
|
|
8259
|
+
if (block2.selector && block2.result) map.set(block2.selector, block2.result);
|
|
8260
|
+
}
|
|
8261
|
+
}
|
|
8262
|
+
} catch {
|
|
8263
|
+
}
|
|
8264
|
+
return map;
|
|
8265
|
+
}
|
|
8266
|
+
function persistAltTextToServer(siteKey, proxyUrl, imageUrl, altText2, lang) {
|
|
8267
|
+
try {
|
|
8268
|
+
const base = proxyUrl || DEFAULT_API_BASE;
|
|
8269
|
+
const pageUrl = window.location.origin + window.location.pathname;
|
|
8270
|
+
fetch(`${base}/v1/cache/persist-alt-text`, {
|
|
8271
|
+
method: "POST",
|
|
8272
|
+
headers: { "Content-Type": "application/json" },
|
|
8273
|
+
body: JSON.stringify({ siteKey, pageUrl, imageUrl, altText: altText2, lang })
|
|
8274
|
+
}).catch(() => {
|
|
8275
|
+
});
|
|
8276
|
+
} catch {
|
|
8277
|
+
}
|
|
8278
|
+
}
|
|
8279
|
+
let autoApplied = false;
|
|
8280
|
+
async function autoApplyCachedAltTexts(widgetConfig) {
|
|
8281
|
+
if (autoApplied) return 0;
|
|
8282
|
+
autoApplied = true;
|
|
8283
|
+
const cache = /* @__PURE__ */ new Map();
|
|
8284
|
+
const siteKey = widgetConfig?.siteKey;
|
|
8285
|
+
const proxyUrl = widgetConfig?.proxyUrl || "";
|
|
8286
|
+
const pageLang = widgetConfig?.lang?.split("-")[0] || document.documentElement.lang?.split("-")[0] || "en";
|
|
8287
|
+
if (siteKey) {
|
|
8288
|
+
const serverCache = await fetchServerAltTexts(siteKey, proxyUrl, pageLang);
|
|
8289
|
+
for (const [url, alt] of serverCache) {
|
|
8290
|
+
cache.set(url, alt);
|
|
8291
|
+
const norm = normalizeImageUrl(url);
|
|
8292
|
+
if (norm !== url) cache.set(norm, alt);
|
|
8293
|
+
setCachedAltText(url, alt, pageLang).catch(() => {
|
|
8294
|
+
});
|
|
8295
|
+
}
|
|
8296
|
+
}
|
|
8297
|
+
const localCache = await getAllCachedAltTexts();
|
|
8298
|
+
for (const [url, alt] of localCache) {
|
|
8299
|
+
if (!cache.has(url)) cache.set(url, alt);
|
|
8300
|
+
}
|
|
8301
|
+
if (cache.size === 0) return 0;
|
|
8302
|
+
function applyToImg(img) {
|
|
8303
|
+
if (img.closest("#accessify-root") || img.closest("accessify-widget")) return false;
|
|
8304
|
+
const src = getImageSrc(img);
|
|
8305
|
+
const norm = normalizeImageUrl(src);
|
|
8306
|
+
const imgSrcNorm = normalizeImageUrl(img.src);
|
|
8307
|
+
const cached = cache.get(src) || cache.get(norm) || cache.get(img.src) || cache.get(imgSrcNorm);
|
|
8308
|
+
if (!cached) return false;
|
|
8309
|
+
const alt = img.getAttribute("alt");
|
|
8310
|
+
if (alt === null || alt.trim() === "") {
|
|
8311
|
+
img.setAttribute("alt", cached);
|
|
8312
|
+
img.setAttribute("data-accessify-alt", "auto");
|
|
8313
|
+
}
|
|
8314
|
+
img.setAttribute("title", cached);
|
|
8315
|
+
return true;
|
|
8316
|
+
}
|
|
8317
|
+
let applied = 0;
|
|
8318
|
+
document.querySelectorAll("img").forEach((img) => {
|
|
8319
|
+
if (applyToImg(img)) applied++;
|
|
8320
|
+
});
|
|
8321
|
+
const observer2 = new MutationObserver((mutations) => {
|
|
8322
|
+
for (const m of mutations) {
|
|
8323
|
+
for (const node of m.addedNodes) {
|
|
8324
|
+
const imgs = node instanceof HTMLImageElement ? [node] : node instanceof HTMLElement ? Array.from(node.querySelectorAll("img")) : [];
|
|
8325
|
+
for (const img of imgs) applyToImg(img);
|
|
8326
|
+
}
|
|
8327
|
+
}
|
|
8328
|
+
});
|
|
8329
|
+
observer2.observe(document.body, { childList: true, subtree: true });
|
|
8330
|
+
setTimeout(() => observer2.disconnect(), 3e4);
|
|
8331
|
+
return applied;
|
|
8332
|
+
}
|
|
8333
|
+
function createAltTextModule(aiService, initialLang = "de", serverConfig) {
|
|
8334
|
+
const siteKey = serverConfig?.siteKey || "";
|
|
8335
|
+
const proxyUrl = serverConfig?.proxyUrl || "";
|
|
8336
|
+
let enabled = false;
|
|
8337
|
+
let domObserver = null;
|
|
8338
|
+
let autoGenerating = false;
|
|
8339
|
+
let missingAltImages = [];
|
|
8340
|
+
const processedImages = /* @__PURE__ */ new Map();
|
|
8341
|
+
function lang() {
|
|
8342
|
+
return getCurrentWidgetLang() || initialLang;
|
|
8343
|
+
}
|
|
8344
|
+
function isDE() {
|
|
8345
|
+
return lang().startsWith("de");
|
|
8346
|
+
}
|
|
8347
|
+
function isGenericAlt(alt) {
|
|
8348
|
+
if (!alt.trim()) return true;
|
|
8349
|
+
if (/^(image|img|photo|bild|foto|untitled|placeholder)/i.test(alt)) return true;
|
|
8350
|
+
if (/^IMG_\d+/i.test(alt)) return true;
|
|
8351
|
+
if (/\.(jpg|jpeg|png|gif|webp|svg|avif)$/i.test(alt)) return true;
|
|
8352
|
+
return false;
|
|
8353
|
+
}
|
|
8354
|
+
function isDecorativeImage(img) {
|
|
8355
|
+
const role = img.getAttribute("role");
|
|
8356
|
+
if (role === "presentation" || role === "none") return true;
|
|
8357
|
+
if (img.complete && img.naturalWidth > 0 && (img.naturalWidth < 50 || img.naturalHeight < 50)) return true;
|
|
8358
|
+
return false;
|
|
8359
|
+
}
|
|
8360
|
+
function isValidImage(img) {
|
|
8361
|
+
if (img.closest("#accessify-root") || img.closest("accessify-widget")) return false;
|
|
8362
|
+
if (img.complete && img.naturalWidth > 0 && (img.naturalWidth < 50 || img.naturalHeight < 50)) return false;
|
|
8363
|
+
const rect = img.getBoundingClientRect();
|
|
8364
|
+
if (rect.width < 40 || rect.height < 40) return false;
|
|
8365
|
+
const style = getComputedStyle(img);
|
|
8366
|
+
if (style.display === "none" || style.visibility === "hidden" || style.opacity === "0") return false;
|
|
8367
|
+
if (img.getAttribute("role") === "presentation" || img.getAttribute("aria-hidden") === "true") return false;
|
|
8368
|
+
return true;
|
|
8369
|
+
}
|
|
8370
|
+
function needsAltText(img) {
|
|
8371
|
+
if (!isValidImage(img)) return false;
|
|
8372
|
+
const alt = img.getAttribute("alt");
|
|
8373
|
+
if (alt === null) return true;
|
|
8374
|
+
if (isGenericAlt(alt)) return true;
|
|
8375
|
+
if (alt === "" && !isDecorativeImage(img)) return true;
|
|
8376
|
+
return false;
|
|
8377
|
+
}
|
|
8378
|
+
function scanForMissingAlt() {
|
|
8379
|
+
const missing = [];
|
|
8380
|
+
document.querySelectorAll("img").forEach((img) => {
|
|
8381
|
+
if (needsAltText(img)) missing.push(img);
|
|
8382
|
+
});
|
|
8383
|
+
document.querySelectorAll("picture").forEach((picture) => {
|
|
8384
|
+
if (picture.closest("#accessify-root") || picture.closest("accessify-widget")) return;
|
|
8385
|
+
const img = picture.querySelector("img");
|
|
8386
|
+
if (img && !missing.includes(img) && needsAltText(img)) {
|
|
8387
|
+
missing.push(img);
|
|
8388
|
+
}
|
|
8389
|
+
});
|
|
8390
|
+
return missing;
|
|
8391
|
+
}
|
|
8392
|
+
function gatherImageContext(img) {
|
|
8393
|
+
const parts = [];
|
|
8394
|
+
try {
|
|
8395
|
+
const url = new URL(getImageSrc(img), window.location.href);
|
|
8396
|
+
const filename = url.pathname.split("/").pop();
|
|
8397
|
+
if (filename) parts.push(`Filename: ${filename}`);
|
|
8398
|
+
} catch {
|
|
8399
|
+
}
|
|
8400
|
+
const figure = img.closest("figure");
|
|
8401
|
+
if (figure) {
|
|
8402
|
+
const caption = figure.querySelector("figcaption");
|
|
8403
|
+
if (caption?.textContent?.trim()) parts.push(`Caption: ${caption.textContent.trim()}`);
|
|
8404
|
+
}
|
|
8405
|
+
const container = img.parentElement?.parentElement || img.parentElement;
|
|
8406
|
+
if (container) {
|
|
8407
|
+
const heading = container.querySelector("h1, h2, h3, h4, h5, h6");
|
|
8408
|
+
if (heading?.textContent?.trim()) parts.push(`Nearby heading: ${heading.textContent.trim()}`);
|
|
8409
|
+
}
|
|
8410
|
+
return parts.join(". ");
|
|
8411
|
+
}
|
|
8412
|
+
function applyAltText(img, altText2) {
|
|
8413
|
+
img.setAttribute("alt", altText2);
|
|
8414
|
+
img.setAttribute("title", altText2);
|
|
8415
|
+
processedImages.set(img, { generatedAlt: altText2 });
|
|
8416
|
+
if (enabled) addInfoBadge(img, altText2);
|
|
8417
|
+
}
|
|
8418
|
+
function applyTitlesToAllImages() {
|
|
8419
|
+
document.querySelectorAll("img").forEach((img) => {
|
|
8420
|
+
if (!isValidImage(img)) return;
|
|
8421
|
+
if (img.getAttribute("title")) return;
|
|
8422
|
+
const alt = img.getAttribute("alt");
|
|
8423
|
+
if (alt && alt.trim()) {
|
|
8424
|
+
img.setAttribute("title", alt);
|
|
8425
|
+
img.setAttribute("data-accessify-title", "auto");
|
|
8426
|
+
}
|
|
8427
|
+
});
|
|
8428
|
+
}
|
|
8429
|
+
const BADGE_ATTR = "data-accessify-badge";
|
|
8430
|
+
const WRAPPER_ATTR = "data-accessify-badge-wrap";
|
|
8431
|
+
const OVERLAY_ID = "accessify-badge-overlay";
|
|
8432
|
+
const BADGE_STYLE_ID = "accessify-badge-styles";
|
|
8433
|
+
let badgePositionRAF = null;
|
|
8434
|
+
let trackedBadges = [];
|
|
8435
|
+
let outsideClickHandler = null;
|
|
8436
|
+
function getOrCreateOverlay() {
|
|
8437
|
+
let overlay = document.getElementById(OVERLAY_ID);
|
|
8438
|
+
if (!overlay) {
|
|
8439
|
+
overlay = document.createElement("div");
|
|
8440
|
+
overlay.id = OVERLAY_ID;
|
|
8441
|
+
Object.assign(overlay.style, {
|
|
8442
|
+
position: "absolute",
|
|
8443
|
+
top: "0",
|
|
8444
|
+
left: "0",
|
|
8445
|
+
width: "0",
|
|
8446
|
+
height: "0",
|
|
8447
|
+
overflow: "visible",
|
|
8448
|
+
pointerEvents: "none",
|
|
8449
|
+
zIndex: "10000"
|
|
8450
|
+
});
|
|
8451
|
+
document.body.appendChild(overlay);
|
|
8452
|
+
}
|
|
8453
|
+
if (!document.getElementById(BADGE_STYLE_ID)) {
|
|
8454
|
+
const style = document.createElement("style");
|
|
8455
|
+
style.id = BADGE_STYLE_ID;
|
|
8456
|
+
style.textContent = `
|
|
8457
|
+
[${BADGE_ATTR}="badge"]:hover + [${BADGE_ATTR}="tooltip"] { display: block !important; }
|
|
8458
|
+
[${BADGE_ATTR}="badge"]:hover { background: rgba(0,0,0,0.85) !important; }
|
|
8459
|
+
@media (pointer: coarse) {
|
|
8460
|
+
[${BADGE_ATTR}="badge"] { width: 36px !important; height: 36px !important; line-height: 36px !important; font-size: 16px !important; }
|
|
8461
|
+
}
|
|
8462
|
+
`;
|
|
8463
|
+
document.head.appendChild(style);
|
|
8464
|
+
}
|
|
8465
|
+
return overlay;
|
|
8466
|
+
}
|
|
8467
|
+
function positionBadge(target, badge, tooltip) {
|
|
8468
|
+
const rect = target.getBoundingClientRect();
|
|
8469
|
+
if (rect.width < 40 || rect.height < 40 || rect.bottom < 0 || rect.top > window.innerHeight + 200 || rect.right < 0 || rect.left > window.innerWidth + 200) {
|
|
8470
|
+
badge.style.display = "none";
|
|
8471
|
+
tooltip.style.display = "none";
|
|
8472
|
+
return;
|
|
8473
|
+
}
|
|
8474
|
+
const scrollX = window.scrollX;
|
|
8475
|
+
const scrollY = window.scrollY;
|
|
8476
|
+
const top = rect.top + scrollY + 6;
|
|
8477
|
+
const left = rect.left + scrollX + 6;
|
|
8478
|
+
badge.style.display = "";
|
|
8479
|
+
badge.style.top = `${top}px`;
|
|
8480
|
+
badge.style.left = `${left}px`;
|
|
8481
|
+
tooltip.style.top = `${top + 26}px`;
|
|
8482
|
+
tooltip.style.left = `${left}px`;
|
|
8483
|
+
}
|
|
8484
|
+
function updateAllBadgePositions() {
|
|
8485
|
+
for (const { target, badge, tooltip } of trackedBadges) {
|
|
8486
|
+
if (!document.body.contains(target)) continue;
|
|
8487
|
+
positionBadge(target, badge, tooltip);
|
|
8488
|
+
}
|
|
8489
|
+
}
|
|
8490
|
+
function startBadgeTracking() {
|
|
8491
|
+
if (badgePositionRAF !== null) return;
|
|
8492
|
+
const tick = () => {
|
|
8493
|
+
updateAllBadgePositions();
|
|
8494
|
+
badgePositionRAF = requestAnimationFrame(tick);
|
|
8495
|
+
};
|
|
8496
|
+
badgePositionRAF = requestAnimationFrame(tick);
|
|
8497
|
+
if (!outsideClickHandler) {
|
|
8498
|
+
outsideClickHandler = (e) => {
|
|
8499
|
+
const target = e.target;
|
|
8500
|
+
if (target.getAttribute(BADGE_ATTR) === "badge") return;
|
|
8501
|
+
for (const entry of trackedBadges) {
|
|
8502
|
+
entry.tooltip.style.display = "none";
|
|
8503
|
+
entry.badge.style.background = "rgba(0,0,0,0.6)";
|
|
8504
|
+
}
|
|
8505
|
+
};
|
|
8506
|
+
document.addEventListener("click", outsideClickHandler, { passive: true });
|
|
8507
|
+
}
|
|
8508
|
+
}
|
|
8509
|
+
function stopBadgeTracking() {
|
|
8510
|
+
if (badgePositionRAF !== null) {
|
|
8511
|
+
cancelAnimationFrame(badgePositionRAF);
|
|
8512
|
+
badgePositionRAF = null;
|
|
8513
|
+
}
|
|
8514
|
+
if (outsideClickHandler) {
|
|
8515
|
+
document.removeEventListener("click", outsideClickHandler);
|
|
8516
|
+
outsideClickHandler = null;
|
|
8517
|
+
}
|
|
8518
|
+
}
|
|
8519
|
+
function addInfoBadge(target, altText2) {
|
|
8520
|
+
if (target.getAttribute(BADGE_ATTR)) return;
|
|
8521
|
+
target.setAttribute(BADGE_ATTR, "1");
|
|
8522
|
+
const overlay = getOrCreateOverlay();
|
|
8523
|
+
const badge = document.createElement("span");
|
|
8524
|
+
Object.assign(badge.style, {
|
|
8525
|
+
position: "absolute",
|
|
8526
|
+
width: "22px",
|
|
8527
|
+
height: "22px",
|
|
8528
|
+
borderRadius: "50%",
|
|
8529
|
+
background: "rgba(0,0,0,0.6)",
|
|
8530
|
+
color: "#fff",
|
|
8531
|
+
fontFamily: "sans-serif",
|
|
8532
|
+
fontWeight: "bold",
|
|
8533
|
+
fontSize: "13px",
|
|
8534
|
+
lineHeight: "22px",
|
|
8535
|
+
textAlign: "center",
|
|
8536
|
+
cursor: "default",
|
|
8537
|
+
pointerEvents: "auto",
|
|
8538
|
+
boxShadow: "0 1px 4px rgba(0,0,0,0.4)",
|
|
8539
|
+
transition: "background 0.15s"
|
|
8540
|
+
});
|
|
8541
|
+
badge.textContent = "i";
|
|
8542
|
+
badge.setAttribute("aria-hidden", "true");
|
|
8543
|
+
badge.setAttribute("translate", "no");
|
|
8544
|
+
badge.classList.add("notranslate");
|
|
8545
|
+
badge.setAttribute(BADGE_ATTR, "badge");
|
|
8546
|
+
const tooltip = document.createElement("span");
|
|
8547
|
+
Object.assign(tooltip.style, {
|
|
8548
|
+
display: "none",
|
|
8549
|
+
position: "absolute",
|
|
8550
|
+
minWidth: "150px",
|
|
8551
|
+
maxWidth: "280px",
|
|
8552
|
+
padding: "6px 10px",
|
|
8553
|
+
background: "rgba(0,0,0,0.88)",
|
|
8554
|
+
color: "#fff",
|
|
8555
|
+
fontFamily: "sans-serif",
|
|
8556
|
+
fontSize: "12px",
|
|
8557
|
+
lineHeight: "1.4",
|
|
8558
|
+
borderRadius: "6px",
|
|
8559
|
+
pointerEvents: "none",
|
|
8560
|
+
wordWrap: "break-word",
|
|
8561
|
+
boxShadow: "0 2px 8px rgba(0,0,0,0.3)",
|
|
8562
|
+
zIndex: "1"
|
|
8563
|
+
});
|
|
8564
|
+
tooltip.textContent = altText2;
|
|
8565
|
+
tooltip.setAttribute("translate", "no");
|
|
8566
|
+
tooltip.classList.add("notranslate");
|
|
8567
|
+
tooltip.setAttribute(BADGE_ATTR, "tooltip");
|
|
8568
|
+
badge.addEventListener("click", () => {
|
|
8569
|
+
const isOpen = tooltip.style.display === "block";
|
|
8570
|
+
for (const entry of trackedBadges) {
|
|
8571
|
+
entry.tooltip.style.display = "none";
|
|
8572
|
+
entry.badge.style.background = "rgba(0,0,0,0.6)";
|
|
8573
|
+
}
|
|
8574
|
+
if (!isOpen) {
|
|
8575
|
+
badge.style.background = "rgba(0,0,0,0.85)";
|
|
8576
|
+
tooltip.style.display = "block";
|
|
8577
|
+
}
|
|
8578
|
+
});
|
|
8579
|
+
overlay.appendChild(badge);
|
|
8580
|
+
overlay.appendChild(tooltip);
|
|
8581
|
+
positionBadge(target, badge, tooltip);
|
|
8582
|
+
trackedBadges.push({ target, badge, tooltip });
|
|
8583
|
+
}
|
|
8584
|
+
function removeAllBadges() {
|
|
8585
|
+
stopBadgeTracking();
|
|
8586
|
+
trackedBadges = [];
|
|
8587
|
+
document.getElementById(OVERLAY_ID)?.remove();
|
|
8588
|
+
document.getElementById(BADGE_STYLE_ID)?.remove();
|
|
8589
|
+
document.querySelectorAll(`[${BADGE_ATTR}]`).forEach((el) => el.removeAttribute(BADGE_ATTR));
|
|
8590
|
+
document.querySelectorAll(`[${WRAPPER_ATTR}]`).forEach((wrapper) => {
|
|
8591
|
+
const img = wrapper.querySelector("img");
|
|
8592
|
+
if (img && wrapper.parentElement) {
|
|
8593
|
+
wrapper.parentElement.insertBefore(img, wrapper);
|
|
8594
|
+
wrapper.remove();
|
|
8595
|
+
}
|
|
8596
|
+
});
|
|
8597
|
+
}
|
|
8598
|
+
function addBadgesToAllImages() {
|
|
8599
|
+
document.querySelectorAll("img").forEach((img) => {
|
|
8600
|
+
if (!isValidImage(img)) return;
|
|
8601
|
+
const alt = img.getAttribute("alt") || img.getAttribute("title");
|
|
8602
|
+
if (alt && alt.trim()) {
|
|
8603
|
+
addInfoBadge(img, alt.trim());
|
|
8604
|
+
}
|
|
8605
|
+
});
|
|
8606
|
+
document.querySelectorAll("[data-accessify-bg-alt]").forEach((el) => {
|
|
8607
|
+
const alt = el.getAttribute("aria-label");
|
|
8608
|
+
if (alt && alt.trim()) {
|
|
8609
|
+
addInfoBadge(el, alt.trim());
|
|
8610
|
+
}
|
|
8611
|
+
});
|
|
8612
|
+
if (trackedBadges.length > 0) startBadgeTracking();
|
|
8613
|
+
}
|
|
8614
|
+
function removeTitles() {
|
|
8615
|
+
document.querySelectorAll('img[data-accessify-title="auto"]').forEach((img) => {
|
|
8616
|
+
img.removeAttribute("title");
|
|
8617
|
+
img.removeAttribute("data-accessify-title");
|
|
8618
|
+
});
|
|
8619
|
+
document.querySelectorAll("[data-accessify-bg-alt]").forEach((el) => {
|
|
8620
|
+
el.removeAttribute("role");
|
|
8621
|
+
el.removeAttribute("aria-label");
|
|
8622
|
+
el.removeAttribute("title");
|
|
8623
|
+
el.removeAttribute("data-accessify-bg-alt");
|
|
8624
|
+
});
|
|
8625
|
+
removeAllBadges();
|
|
8626
|
+
}
|
|
8627
|
+
function scanForBackgroundImages() {
|
|
8628
|
+
const found = [];
|
|
8629
|
+
const allElements = document.querySelectorAll("*");
|
|
8630
|
+
for (const el of allElements) {
|
|
8631
|
+
if (el.closest("#accessify-root") || el.closest("accessify-widget")) continue;
|
|
8632
|
+
if (el.getAttribute("data-accessify-bg-alt")) continue;
|
|
8633
|
+
if (el.offsetWidth < 200 || el.offsetHeight < 150) continue;
|
|
8634
|
+
const style = getComputedStyle(el);
|
|
8635
|
+
const bg = style.backgroundImage;
|
|
8636
|
+
if (!bg || bg === "none") continue;
|
|
8637
|
+
const urlMatch = bg.match(/url\(["']?([^"')]+)["']?\)/);
|
|
8638
|
+
if (!urlMatch) continue;
|
|
8639
|
+
if (urlMatch[1].startsWith("data:image/svg")) continue;
|
|
8640
|
+
if (el.getAttribute("role") === "img" && el.getAttribute("aria-label")) continue;
|
|
8641
|
+
found.push(el);
|
|
8642
|
+
}
|
|
8643
|
+
return found;
|
|
8644
|
+
}
|
|
8645
|
+
function getBackgroundImageUrl(el) {
|
|
8646
|
+
const bg = getComputedStyle(el).backgroundImage;
|
|
8647
|
+
const match = bg?.match(/url\(["']?([^"')]+)["']?\)/);
|
|
8648
|
+
return match ? match[1] : null;
|
|
8649
|
+
}
|
|
8650
|
+
function applyBgAlt(el, altText2) {
|
|
8651
|
+
el.setAttribute("role", "img");
|
|
8652
|
+
el.setAttribute("aria-label", altText2);
|
|
8653
|
+
el.setAttribute("title", altText2);
|
|
8654
|
+
el.setAttribute("data-accessify-bg-alt", "auto");
|
|
8655
|
+
if (enabled) addInfoBadge(el, altText2);
|
|
8656
|
+
}
|
|
8657
|
+
async function generateBgAlts() {
|
|
8658
|
+
if (!aiService || !enabled) return;
|
|
8659
|
+
if (currentAltSiteMode !== "auto") {
|
|
8660
|
+
if (window.__ACCESSIFY_DEBUG) {
|
|
8661
|
+
console.info("[Accessify] alt-text: siteMode=manual — skipping live generation for background images");
|
|
8662
|
+
}
|
|
8663
|
+
return;
|
|
8664
|
+
}
|
|
8665
|
+
const bgElements = scanForBackgroundImages();
|
|
8666
|
+
for (const el of bgElements) {
|
|
8667
|
+
if (!enabled) break;
|
|
8668
|
+
const src = getBackgroundImageUrl(el);
|
|
8669
|
+
if (!src) continue;
|
|
8670
|
+
const cached = await getCachedAltText(src);
|
|
8671
|
+
if (cached) {
|
|
8672
|
+
applyBgAlt(el, cached);
|
|
8673
|
+
continue;
|
|
8674
|
+
}
|
|
8675
|
+
try {
|
|
8676
|
+
const ctx = gatherBgContext(el);
|
|
8677
|
+
const alt = await aiService.generateAltText(src, ctx, lang());
|
|
8678
|
+
if (alt && enabled) {
|
|
8679
|
+
applyBgAlt(el, alt);
|
|
8680
|
+
setCachedAltText(src, alt, lang()).catch(() => {
|
|
8681
|
+
});
|
|
8682
|
+
if (siteKey) persistAltTextToServer(siteKey, proxyUrl, src, alt, lang());
|
|
8683
|
+
}
|
|
8684
|
+
} catch (err) {
|
|
8685
|
+
console.warn("[Accessify] Bg alt-text generation failed:", src, err);
|
|
8686
|
+
}
|
|
8687
|
+
}
|
|
8688
|
+
}
|
|
8689
|
+
function gatherBgContext(el) {
|
|
8690
|
+
const parts = [];
|
|
8691
|
+
parts.push(`Element: <${el.tagName.toLowerCase()}>`);
|
|
8692
|
+
if (el.className) parts.push(`Class: ${el.className}`);
|
|
8693
|
+
const text = el.textContent?.trim().slice(0, 100);
|
|
8694
|
+
if (text) parts.push(`Overlay text: ${text}`);
|
|
8695
|
+
const heading = el.querySelector("h1, h2, h3");
|
|
8696
|
+
if (heading?.textContent?.trim()) parts.push(`Heading: ${heading.textContent.trim()}`);
|
|
8697
|
+
return parts.join(". ");
|
|
8698
|
+
}
|
|
8699
|
+
async function generateAll() {
|
|
8700
|
+
if (autoGenerating || !enabled || !aiService) return;
|
|
8701
|
+
autoGenerating = true;
|
|
8702
|
+
const CONCURRENCY = 3;
|
|
8703
|
+
const uncached = [];
|
|
8704
|
+
for (const img of missingAltImages) {
|
|
8705
|
+
if (processedImages.has(img)) continue;
|
|
8706
|
+
const src = getImageSrc(img);
|
|
8707
|
+
const cached = await getCachedAltText(src);
|
|
8708
|
+
if (cached) {
|
|
8709
|
+
applyAltText(img, cached);
|
|
8710
|
+
} else {
|
|
8711
|
+
uncached.push(img);
|
|
8712
|
+
}
|
|
8713
|
+
}
|
|
8714
|
+
if (!uncached.length) {
|
|
8715
|
+
autoGenerating = false;
|
|
8716
|
+
return;
|
|
8717
|
+
}
|
|
8718
|
+
if (currentAltSiteMode !== "auto") {
|
|
8719
|
+
if (window.__ACCESSIFY_DEBUG) {
|
|
8720
|
+
console.info(
|
|
8721
|
+
`[Accessify] alt-text: siteMode=manual — ${uncached.length} image(s) left untouched. Trigger a crawl from the dashboard or enable auto mode.`
|
|
8722
|
+
);
|
|
8723
|
+
}
|
|
8724
|
+
autoGenerating = false;
|
|
8725
|
+
return;
|
|
8726
|
+
}
|
|
8727
|
+
for (let i = 0; i < uncached.length; i += CONCURRENCY) {
|
|
8728
|
+
if (!enabled) break;
|
|
8729
|
+
const batch = uncached.slice(i, i + CONCURRENCY);
|
|
8730
|
+
await Promise.all(batch.map(async (img) => {
|
|
8731
|
+
if (!enabled) return;
|
|
8732
|
+
try {
|
|
8733
|
+
const src = getImageSrc(img);
|
|
8734
|
+
const ctx = gatherImageContext(img);
|
|
8735
|
+
const alt = await aiService.generateAltText(src, ctx, lang());
|
|
8736
|
+
if (alt && enabled) {
|
|
8737
|
+
applyAltText(img, alt);
|
|
8738
|
+
setCachedAltText(src, alt, lang()).catch(() => {
|
|
8739
|
+
});
|
|
8740
|
+
if (siteKey) persistAltTextToServer(siteKey, proxyUrl, src, alt, lang());
|
|
8741
|
+
}
|
|
8742
|
+
} catch (err) {
|
|
8743
|
+
console.warn("[Accessify] Alt-text generation failed:", getImageSrc(img), err);
|
|
8744
|
+
}
|
|
8745
|
+
}));
|
|
8746
|
+
}
|
|
8747
|
+
autoGenerating = false;
|
|
8748
|
+
}
|
|
8749
|
+
async function generateSingle(img) {
|
|
8750
|
+
if (!aiService || !enabled) return;
|
|
8751
|
+
const src = getImageSrc(img);
|
|
8752
|
+
const cached = await getCachedAltText(src);
|
|
8753
|
+
if (cached) {
|
|
8754
|
+
applyAltText(img, cached);
|
|
8755
|
+
return;
|
|
8756
|
+
}
|
|
8757
|
+
if (currentAltSiteMode !== "auto") return;
|
|
8758
|
+
try {
|
|
8759
|
+
const ctx = gatherImageContext(img);
|
|
8760
|
+
const alt = await aiService.generateAltText(src, ctx, lang());
|
|
8761
|
+
if (alt && enabled) {
|
|
8762
|
+
applyAltText(img, alt);
|
|
8763
|
+
setCachedAltText(src, alt, lang()).catch(() => {
|
|
8764
|
+
});
|
|
8765
|
+
if (siteKey) persistAltTextToServer(siteKey, proxyUrl, src, alt, lang());
|
|
8766
|
+
}
|
|
8767
|
+
} catch (err) {
|
|
8768
|
+
console.warn("[Accessify] Alt-text generation failed:", src, err);
|
|
8769
|
+
}
|
|
8770
|
+
}
|
|
8771
|
+
function tryRegisterImage(img) {
|
|
8772
|
+
if (!enabled) return;
|
|
8773
|
+
if (!isValidImage(img)) return;
|
|
8774
|
+
if (missingAltImages.includes(img)) return;
|
|
8775
|
+
function addIfValid() {
|
|
8776
|
+
if (!enabled || missingAltImages.includes(img)) return;
|
|
8777
|
+
if (img.naturalWidth < 20 || img.naturalHeight < 20) return;
|
|
8778
|
+
if (needsAltText(img)) {
|
|
8779
|
+
missingAltImages.push(img);
|
|
8780
|
+
generateSingle(img);
|
|
8781
|
+
} else {
|
|
8782
|
+
const alt = img.getAttribute("alt");
|
|
8783
|
+
if (alt && alt.trim()) {
|
|
8784
|
+
if (!img.getAttribute("title")) {
|
|
8785
|
+
img.setAttribute("title", alt);
|
|
8786
|
+
img.setAttribute("data-accessify-title", "auto");
|
|
8787
|
+
}
|
|
8788
|
+
addInfoBadge(img, alt.trim());
|
|
8789
|
+
}
|
|
8790
|
+
}
|
|
8791
|
+
}
|
|
8792
|
+
if (img.complete && img.naturalWidth > 0) addIfValid();
|
|
8793
|
+
else img.addEventListener("load", addIfValid, { once: true });
|
|
8794
|
+
}
|
|
8795
|
+
function activate() {
|
|
8796
|
+
if (enabled) return;
|
|
8797
|
+
enabled = true;
|
|
8798
|
+
document.querySelectorAll('img[data-accessify-alt="auto"]').forEach((img) => {
|
|
8799
|
+
if (img.closest("#accessify-root")) return;
|
|
8800
|
+
if (!processedImages.has(img)) {
|
|
8801
|
+
processedImages.set(img, { generatedAlt: img.getAttribute("alt") || "" });
|
|
8802
|
+
missingAltImages.push(img);
|
|
8803
|
+
}
|
|
8804
|
+
});
|
|
8805
|
+
const missing = scanForMissingAlt();
|
|
8806
|
+
for (const img of missing) {
|
|
8807
|
+
if (!missingAltImages.includes(img)) {
|
|
8808
|
+
missingAltImages.push(img);
|
|
8809
|
+
}
|
|
8810
|
+
}
|
|
8811
|
+
generateAll();
|
|
8812
|
+
generateBgAlts();
|
|
8813
|
+
applyTitlesToAllImages();
|
|
8814
|
+
addBadgesToAllImages();
|
|
8815
|
+
document.querySelectorAll("img").forEach((img) => {
|
|
8816
|
+
if (!img.complete) tryRegisterImage(img);
|
|
8817
|
+
});
|
|
8818
|
+
for (const delay of [2e3, 5e3, 1e4]) {
|
|
8819
|
+
setTimeout(() => {
|
|
8820
|
+
if (enabled) {
|
|
8821
|
+
document.querySelectorAll("img").forEach(tryRegisterImage);
|
|
8822
|
+
applyTitlesToAllImages();
|
|
8823
|
+
addBadgesToAllImages();
|
|
8824
|
+
}
|
|
8825
|
+
}, delay);
|
|
8826
|
+
}
|
|
8827
|
+
domObserver = new MutationObserver((mutations) => {
|
|
8828
|
+
for (const m of mutations) {
|
|
8829
|
+
for (const node of m.addedNodes) {
|
|
8830
|
+
if (node instanceof HTMLImageElement) tryRegisterImage(node);
|
|
8831
|
+
else if (node instanceof HTMLElement) {
|
|
8832
|
+
node.querySelectorAll("img").forEach(tryRegisterImage);
|
|
8833
|
+
}
|
|
8834
|
+
}
|
|
8835
|
+
}
|
|
8836
|
+
});
|
|
8837
|
+
domObserver.observe(document.body, { childList: true, subtree: true });
|
|
8838
|
+
}
|
|
8839
|
+
function deactivate() {
|
|
8840
|
+
enabled = false;
|
|
8841
|
+
autoGenerating = false;
|
|
8842
|
+
domObserver?.disconnect();
|
|
8843
|
+
domObserver = null;
|
|
8844
|
+
removeTitles();
|
|
8845
|
+
missingAltImages = [];
|
|
8846
|
+
}
|
|
8847
|
+
autoApplyCachedAltTexts({ siteKey, proxyUrl, lang: initialLang }).catch(() => {
|
|
8848
|
+
});
|
|
8849
|
+
return {
|
|
8850
|
+
id: "alt-text",
|
|
8851
|
+
name: () => isDE() ? "Bildbeschreibung" : "Image Description",
|
|
8852
|
+
description: isDE() ? "Bildbeschreibungen per Hover anzeigen und fehlende automatisch erzeugen" : "Show image descriptions on hover and auto-generate missing ones",
|
|
8853
|
+
icon: "alt-text",
|
|
8854
|
+
category: "ai",
|
|
8855
|
+
activate,
|
|
8856
|
+
deactivate,
|
|
8857
|
+
getState: () => ({
|
|
8858
|
+
id: "alt-text",
|
|
8859
|
+
enabled,
|
|
8860
|
+
value: {
|
|
8861
|
+
missingCount: missingAltImages.filter((img) => !processedImages.has(img)).length,
|
|
8862
|
+
processedCount: processedImages.size
|
|
8863
|
+
}
|
|
8864
|
+
})
|
|
8865
|
+
};
|
|
8866
|
+
}
|
|
8867
|
+
const altText = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
|
8868
|
+
__proto__: null,
|
|
8869
|
+
autoApplyCachedAltTexts,
|
|
8870
|
+
default: createAltTextModule
|
|
8871
|
+
}, Symbol.toStringTag, { value: "Module" }));
|
|
8146
8872
|
const REMOVED_FEATURES = /* @__PURE__ */ new Set([
|
|
8147
8873
|
"spacing",
|
|
8148
8874
|
"dyslexia-font",
|
|
@@ -8186,7 +8912,6 @@ const DEFAULT_CONFIG = {
|
|
|
8186
8912
|
let widgetInstance = null;
|
|
8187
8913
|
let containerEl = null;
|
|
8188
8914
|
let config = { ...DEFAULT_CONFIG };
|
|
8189
|
-
let cleanupListeners = null;
|
|
8190
8915
|
async function fetchSiteConfig(siteKey, proxyUrl) {
|
|
8191
8916
|
try {
|
|
8192
8917
|
const base = proxyUrl || "https://accessify-api.accessify.workers.dev";
|
|
@@ -8289,14 +9014,13 @@ async function init(userConfig = {}) {
|
|
|
8289
9014
|
if (handled) e.stopImmediatePropagation();
|
|
8290
9015
|
}, true);
|
|
8291
9016
|
}
|
|
8292
|
-
|
|
9017
|
+
window.addEventListener("accessify:reposition", ((e) => {
|
|
8293
9018
|
const newPos = e.detail?.position;
|
|
8294
9019
|
if (newPos && config) {
|
|
8295
9020
|
config.position = newPos;
|
|
8296
9021
|
sheet.replaceSync(createWidgetStyles(config));
|
|
8297
9022
|
}
|
|
8298
|
-
});
|
|
8299
|
-
window.addEventListener("accessify:reposition", handleReposition);
|
|
9023
|
+
}));
|
|
8300
9024
|
const ALLOWED_PREVIEW_ORIGINS = [
|
|
8301
9025
|
"https://accessify-dashboard.pages.dev",
|
|
8302
9026
|
"https://accessify.dev",
|
|
@@ -8317,7 +9041,7 @@ async function init(userConfig = {}) {
|
|
|
8317
9041
|
}
|
|
8318
9042
|
return false;
|
|
8319
9043
|
}
|
|
8320
|
-
|
|
9044
|
+
window.addEventListener("message", (e) => {
|
|
8321
9045
|
if (!isAllowedPreviewOrigin(e.origin)) return;
|
|
8322
9046
|
if (e.data?.type === "accessify:preview-config") {
|
|
8323
9047
|
const preview = e.data.config || {};
|
|
@@ -8330,20 +9054,13 @@ async function init(userConfig = {}) {
|
|
|
8330
9054
|
if (panel) panel.setAttribute("aria-hidden", "false");
|
|
8331
9055
|
if (trigger) trigger.setAttribute("aria-expanded", "true");
|
|
8332
9056
|
}
|
|
8333
|
-
};
|
|
8334
|
-
window.addEventListener("message", handleMessage);
|
|
8335
|
-
cleanupListeners = () => {
|
|
8336
|
-
window.removeEventListener("accessify:reposition", handleReposition);
|
|
8337
|
-
window.removeEventListener("message", handleMessage);
|
|
8338
|
-
};
|
|
9057
|
+
});
|
|
8339
9058
|
document.body.appendChild(containerEl);
|
|
8340
|
-
|
|
9059
|
+
autoApplyCachedAltTexts(config).catch(() => {
|
|
8341
9060
|
});
|
|
8342
9061
|
config.onReady?.();
|
|
8343
9062
|
}
|
|
8344
9063
|
function destroy() {
|
|
8345
|
-
cleanupListeners?.();
|
|
8346
|
-
cleanupListeners = null;
|
|
8347
9064
|
if (widgetInstance) {
|
|
8348
9065
|
unmount(widgetInstance);
|
|
8349
9066
|
widgetInstance = null;
|
|
@@ -8382,4 +9099,4 @@ export {
|
|
|
8382
9099
|
init as i,
|
|
8383
9100
|
t
|
|
8384
9101
|
};
|
|
8385
|
-
//# sourceMappingURL=index-
|
|
9102
|
+
//# sourceMappingURL=index-CJB-hRuq.js.map
|