updated files with new features and improvements: caching proper
This commit is contained in:
+34
-4
@@ -140,6 +140,7 @@
|
|||||||
const cardBackCache = new Map();
|
const cardBackCache = new Map();
|
||||||
const cardBackThumbnailCache = new Map();
|
const cardBackThumbnailCache = new Map();
|
||||||
const imagePreloadCache = new Map();
|
const imagePreloadCache = new Map();
|
||||||
|
const loadedImageCache = new Map();
|
||||||
const deckImagePreloadCache = new Map();
|
const deckImagePreloadCache = new Map();
|
||||||
const deckPreloadStatus = {
|
const deckPreloadStatus = {
|
||||||
activeDeckId: DEFAULT_DECK_ID,
|
activeDeckId: DEFAULT_DECK_ID,
|
||||||
@@ -574,6 +575,7 @@
|
|||||||
cardBackCache.clear();
|
cardBackCache.clear();
|
||||||
cardBackThumbnailCache.clear();
|
cardBackThumbnailCache.clear();
|
||||||
imagePreloadCache.clear();
|
imagePreloadCache.clear();
|
||||||
|
loadedImageCache.clear();
|
||||||
deckImagePreloadCache.clear();
|
deckImagePreloadCache.clear();
|
||||||
backgroundDeckWarmupPromise = null;
|
backgroundDeckWarmupPromise = null;
|
||||||
setDeckPreloadStatus({
|
setDeckPreloadStatus({
|
||||||
@@ -948,7 +950,11 @@
|
|||||||
function preloadImageUrl(url) {
|
function preloadImageUrl(url) {
|
||||||
const normalizedUrl = String(url || "").trim();
|
const normalizedUrl = String(url || "").trim();
|
||||||
if (!normalizedUrl) {
|
if (!normalizedUrl) {
|
||||||
return Promise.resolve(false);
|
return Promise.resolve(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (loadedImageCache.has(normalizedUrl)) {
|
||||||
|
return Promise.resolve(loadedImageCache.get(normalizedUrl));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (imagePreloadCache.has(normalizedUrl)) {
|
if (imagePreloadCache.has(normalizedUrl)) {
|
||||||
@@ -971,12 +977,20 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
image.decoding = "async";
|
image.decoding = "async";
|
||||||
image.onload = () => finalize(true);
|
image.onload = () => {
|
||||||
image.onerror = () => finalize(false);
|
loadedImageCache.set(normalizedUrl, image);
|
||||||
|
finalize(image);
|
||||||
|
};
|
||||||
|
image.onerror = () => finalize(null);
|
||||||
image.src = normalizedUrl;
|
image.src = normalizedUrl;
|
||||||
|
|
||||||
if (image.complete) {
|
if (image.complete) {
|
||||||
finalize(Boolean(image.naturalWidth));
|
if (image.naturalWidth) {
|
||||||
|
loadedImageCache.set(normalizedUrl, image);
|
||||||
|
finalize(image);
|
||||||
|
} else {
|
||||||
|
finalize(null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -984,6 +998,20 @@
|
|||||||
return preloadPromise;
|
return preloadPromise;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function ensureImageLoaded(url) {
|
||||||
|
return preloadImageUrl(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
function isImageLoaded(url) {
|
||||||
|
const normalizedUrl = String(url || "").trim();
|
||||||
|
if (!normalizedUrl) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const cachedImage = loadedImageCache.get(normalizedUrl);
|
||||||
|
return Boolean(cachedImage?.complete && cachedImage?.naturalWidth);
|
||||||
|
}
|
||||||
|
|
||||||
async function preloadImageUrls(urls, concurrency = 6) {
|
async function preloadImageUrls(urls, concurrency = 6) {
|
||||||
const queue = Array.from(new Set(Array.isArray(urls) ? urls.map((entry) => String(entry || "").trim()).filter(Boolean) : []));
|
const queue = Array.from(new Set(Array.isArray(urls) ? urls.map((entry) => String(entry || "").trim()).filter(Boolean) : []));
|
||||||
if (queue.length === 0) {
|
if (queue.length === 0) {
|
||||||
@@ -1268,6 +1296,8 @@
|
|||||||
resolveTarotCardBackThumbnail,
|
resolveTarotCardBackThumbnail,
|
||||||
preloadDeckImages,
|
preloadDeckImages,
|
||||||
preloadAllDeckImages,
|
preloadAllDeckImages,
|
||||||
|
ensureImageLoaded,
|
||||||
|
isImageLoaded,
|
||||||
getDeckPreloadStatus: () => emitDeckPreloadStatus(),
|
getDeckPreloadStatus: () => emitDeckPreloadStatus(),
|
||||||
getTarotCardDisplayName,
|
getTarotCardDisplayName,
|
||||||
getTarotCardSearchAliases,
|
getTarotCardSearchAliases,
|
||||||
|
|||||||
+68
-10
@@ -54,6 +54,8 @@
|
|||||||
let activePinchGesture = null;
|
let activePinchGesture = null;
|
||||||
let suppressNextCardClick = false;
|
let suppressNextCardClick = false;
|
||||||
let suppressDeckCompareToggleUntil = 0;
|
let suppressDeckCompareToggleUntil = 0;
|
||||||
|
let primaryImageRequestToken = 0;
|
||||||
|
let overlayImageRequestToken = 0;
|
||||||
|
|
||||||
const LIGHTBOX_ZOOM_SCALE = 6.66;
|
const LIGHTBOX_ZOOM_SCALE = 6.66;
|
||||||
const LIGHTBOX_ZOOM_STEP = 0.1;
|
const LIGHTBOX_ZOOM_STEP = 0.1;
|
||||||
@@ -837,6 +839,7 @@
|
|||||||
const label = String(normalized.label || normalized.altText || "Tarot card enlarged image").trim() || "Tarot card enlarged image";
|
const label = String(normalized.label || normalized.altText || "Tarot card enlarged image").trim() || "Tarot card enlarged image";
|
||||||
return {
|
return {
|
||||||
src: String(normalized.src || "").trim(),
|
src: String(normalized.src || "").trim(),
|
||||||
|
previewSrc: String(normalized.previewSrc || "").trim(),
|
||||||
altText: String(normalized.altText || label).trim() || label,
|
altText: String(normalized.altText || label).trim() || label,
|
||||||
label,
|
label,
|
||||||
cardId: String(normalized.cardId || "").trim(),
|
cardId: String(normalized.cardId || "").trim(),
|
||||||
@@ -847,6 +850,66 @@
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getImageRequestToken(layer = "primary") {
|
||||||
|
if (layer === "overlay") {
|
||||||
|
overlayImageRequestToken += 1;
|
||||||
|
return overlayImageRequestToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
primaryImageRequestToken += 1;
|
||||||
|
return primaryImageRequestToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCurrentImageRequestToken(layer = "primary") {
|
||||||
|
return layer === "overlay" ? overlayImageRequestToken : primaryImageRequestToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
function applyCardImageToElement(targetImageEl, cardRequest, layer = "primary") {
|
||||||
|
if (!(targetImageEl instanceof HTMLImageElement)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const normalizedCard = normalizeCardRequest(cardRequest);
|
||||||
|
const fullSrc = String(normalizedCard.src || "").trim();
|
||||||
|
const previewSrc = String(normalizedCard.previewSrc || "").trim();
|
||||||
|
const normalizedPreviewSrc = previewSrc && previewSrc !== fullSrc ? previewSrc : "";
|
||||||
|
const imageCache = window.TarotCardImages || {};
|
||||||
|
const fullImageLoaded = typeof imageCache.isImageLoaded === "function"
|
||||||
|
? imageCache.isImageLoaded(fullSrc)
|
||||||
|
: false;
|
||||||
|
const requestToken = getImageRequestToken(layer);
|
||||||
|
const initialSrc = fullSrc && (!normalizedPreviewSrc || fullImageLoaded)
|
||||||
|
? fullSrc
|
||||||
|
: (normalizedPreviewSrc || fullSrc);
|
||||||
|
|
||||||
|
if (initialSrc) {
|
||||||
|
targetImageEl.src = initialSrc;
|
||||||
|
targetImageEl.alt = normalizedCard.altText;
|
||||||
|
} else {
|
||||||
|
targetImageEl.removeAttribute("src");
|
||||||
|
targetImageEl.alt = normalizedCard.altText;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!fullSrc || initialSrc === fullSrc || typeof imageCache.ensureImageLoaded !== "function") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
imageCache.ensureImageLoaded(fullSrc)
|
||||||
|
.then((loadedImage) => {
|
||||||
|
if (!loadedImage || getCurrentImageRequestToken(layer) !== requestToken || !lightboxState.isOpen) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (targetImageEl.src !== fullSrc) {
|
||||||
|
targetImageEl.src = fullSrc;
|
||||||
|
targetImageEl.alt = normalizedCard.altText;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function normalizeDeckOptions(deckOptions) {
|
function normalizeDeckOptions(deckOptions) {
|
||||||
if (!Array.isArray(deckOptions)) {
|
if (!Array.isArray(deckOptions)) {
|
||||||
return [];
|
return [];
|
||||||
@@ -3198,8 +3261,7 @@
|
|||||||
if (isCompactLightboxLayout()) {
|
if (isCompactLightboxLayout()) {
|
||||||
lightboxState.mobileInfoView = "overlay";
|
lightboxState.mobileInfoView = "overlay";
|
||||||
}
|
}
|
||||||
overlayImageEl.src = normalizedCard.src;
|
applyCardImageToElement(overlayImageEl, normalizedCard, "overlay");
|
||||||
overlayImageEl.alt = normalizedCard.altText;
|
|
||||||
overlayImageEl.style.display = "block";
|
overlayImageEl.style.display = "block";
|
||||||
overlayImageEl.style.opacity = String(lightboxState.overlayOpacity);
|
overlayImageEl.style.opacity = String(lightboxState.overlayOpacity);
|
||||||
if (syncSelection && typeof lightboxState.onSelectCardId === "function") {
|
if (syncSelection && typeof lightboxState.onSelectCardId === "function") {
|
||||||
@@ -3254,8 +3316,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
lightboxState.primaryCard = nextCard;
|
lightboxState.primaryCard = nextCard;
|
||||||
imageEl.src = nextCard.src;
|
applyCardImageToElement(imageEl, nextCard, "primary");
|
||||||
imageEl.alt = nextCard.altText;
|
|
||||||
resetZoom();
|
resetZoom();
|
||||||
if (lightboxState.deckCompareMode) {
|
if (lightboxState.deckCompareMode) {
|
||||||
syncDeckCompareCards();
|
syncDeckCompareCards();
|
||||||
@@ -3279,10 +3340,8 @@
|
|||||||
lightboxState.primaryCard = nextPrimaryCard;
|
lightboxState.primaryCard = nextPrimaryCard;
|
||||||
lightboxState.secondaryCard = nextSecondaryCard;
|
lightboxState.secondaryCard = nextSecondaryCard;
|
||||||
|
|
||||||
imageEl.src = nextPrimaryCard.src;
|
applyCardImageToElement(imageEl, nextPrimaryCard, "primary");
|
||||||
imageEl.alt = nextPrimaryCard.altText;
|
applyCardImageToElement(overlayImageEl, nextSecondaryCard, "overlay");
|
||||||
overlayImageEl.src = nextSecondaryCard.src;
|
|
||||||
overlayImageEl.alt = nextSecondaryCard.altText;
|
|
||||||
overlayImageEl.style.display = "block";
|
overlayImageEl.style.display = "block";
|
||||||
overlayImageEl.style.opacity = String(lightboxState.overlayOpacity);
|
overlayImageEl.style.opacity = String(lightboxState.overlayOpacity);
|
||||||
|
|
||||||
@@ -3723,8 +3782,7 @@
|
|||||||
setInfoPanelOpen(getPersistedInfoPanelVisibility(), { persist: false });
|
setInfoPanelOpen(getPersistedInfoPanelVisibility(), { persist: false });
|
||||||
lightboxState.mobileInfoView = "primary";
|
lightboxState.mobileInfoView = "primary";
|
||||||
|
|
||||||
imageEl.src = normalizedPrimary.src;
|
applyCardImageToElement(imageEl, normalizedPrimary, "primary");
|
||||||
imageEl.alt = normalizedPrimary.altText;
|
|
||||||
clearSecondaryCard();
|
clearSecondaryCard();
|
||||||
resetZoom();
|
resetZoom();
|
||||||
previousFocusedEl = document.activeElement instanceof HTMLElement ? document.activeElement : null;
|
previousFocusedEl = document.activeElement instanceof HTMLElement ? document.activeElement : null;
|
||||||
|
|||||||
@@ -770,6 +770,9 @@
|
|||||||
const src = typeof resolveTarotCardImage === "function"
|
const src = typeof resolveTarotCardImage === "function"
|
||||||
? resolveTarotCardImage(card.name, deckOptions)
|
? resolveTarotCardImage(card.name, deckOptions)
|
||||||
: "";
|
: "";
|
||||||
|
const previewSrc = typeof resolveTarotCardThumbnail === "function"
|
||||||
|
? (resolveTarotCardThumbnail(card.name, deckOptions) || src)
|
||||||
|
: src;
|
||||||
const deckMeta = resolvedDeckId ? getRegisteredDeckOptionMap().get(resolvedDeckId) : null;
|
const deckMeta = resolvedDeckId ? getRegisteredDeckOptionMap().get(resolvedDeckId) : null;
|
||||||
const label = (typeof getTarotCardDisplayName === "function"
|
const label = (typeof getTarotCardDisplayName === "function"
|
||||||
? getTarotCardDisplayName(card.name, deckOptions)
|
? getTarotCardDisplayName(card.name, deckOptions)
|
||||||
@@ -777,6 +780,7 @@
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
src,
|
src,
|
||||||
|
previewSrc,
|
||||||
altText: label,
|
altText: label,
|
||||||
label,
|
label,
|
||||||
cardId: card.id,
|
cardId: card.id,
|
||||||
|
|||||||
Reference in New Issue
Block a user