$(() => { var fullPageMediaDisplayed = false; var selectedThumbnails = []; var lastKeyboardEvent = null; var lastSelection = null; function onItemSelected(mediaItem) { document.getElementById("pch-mediaList").classList.add("selection"); } function onItemDeselected(mediaItem) { if (!selectedThumbnails.length) document.getElementById("pch-mediaList").classList.remove("selection"); } function buildThumbnail(mediaItem) { if (mediaItem.ui) return mediaItem.ui; let checkbox = document.createElement("input"); let img = document.createElement("img"); let container = document.createElement("li"); let loadingImg = document.createElement("div"); checkbox.type = "checkbox"; container.classList.add("pch-image"); container.classList.add("loading"); loadingImg.classList.add("spinner"); loadingImg.innerHTML = ""; container.dataset.md5sum = mediaItem.md5sum; img.loading = "lazy"; let requestSize = mediaItem.resize(450, 450); img.src = `${mediaItem.thumbnail}?w=${requestSize.width}&h=${requestSize.height}&q=4`; img.classList.add("img-fluid"); img.classList.add("img-thumbnail"); img.addEventListener("load", () => { container.classList.remove("loading"); container.classList.remove("spinner-grow"); }); container.style.width = `${requestSize.width}px`; container.appendChild(loadingImg); container.appendChild(img); container.appendChild(checkbox); let setSelectionCheckboxValue = function(media, value) { let checked = media.ui.checkbox.checked; let indexInSelection = selectedThumbnails.indexOf(media.md5sum); if (checked && indexInSelection < 0) { selectedThumbnails.push(media.md5sum); onItemSelected(media); return true; } else if (!checked && indexInSelection >= 0) { selectedThumbnails.splice(indexInSelection, 1); onItemDeselected(media); return true; } return false; } let cascadeSetSelectionCheckboxValue = function(value) { if (!setSelectionCheckboxValue(mediaItem, value)) return; if (lastKeyboardEvent?.shiftKey && lastSelection) { let _lastKeyboardEvent = lastKeyboardEvent; lastKeyboardEvent = null; for (let i of MediaStorage.Instance.getMediaBetween(lastSelection, mediaItem)) { if (i === mediaItem) continue; i.ui.checkbox.setAttribute("checked", value); i.ui.checkbox.checked = value; setSelectionCheckboxValue(i, value); } lastKeyboardEvent = _lastKeyboardEvent; } lastSelection = mediaItem; console.log(mediaItem); } container.addEventListener("click", () => { if (selectedThumbnails.length || lastKeyboardEvent?.ctrlKey) { let value = !checkbox.checked; checkbox.setAttribute("checked", value); checkbox.checked = value; cascadeSetSelectionCheckboxValue(value); return; } document.location.hash = mediaItem.md5sum; }); checkbox.addEventListener("click", evt => { evt.stopPropagation(); }); checkbox.addEventListener("change", evt => { cascadeSetSelectionCheckboxValue(checkbox.checked); }); return mediaItem.ui = { root: container, img: img, checkbox: checkbox }; } function buildYear(date) { let result = document.createElement('h3'); result.textContent = date.getUTCFullYear(); return result; } function buildMonth(date) { let result = document.createElement('h4'); result.textContent = date.toLocaleString('default', { month: 'long' }); return result; } function redraw(container, media) { buildThumbnail(media); if (!media.ui) return; let yearUpdated = !container.dataset.lastItemYear || container.dataset.lastItemYear != media.date.getUTCFullYear(); if (yearUpdated) { container.appendChild(buildYear(media.date)); container.dataset.lastItemYear = media.date.getUTCFullYear(); } if (yearUpdated || container.dataset.lastItemMonth === undefined || container.dataset.lastItemMonth != media.date.getUTCMonth()) { container.appendChild(buildMonth(media.date)); container.dataset.lastItemMonth = media.date.getUTCMonth(); } container.appendChild(media.ui.root); } MediaStorage.Instance.addEventListener("rebuildMedia", (evt) => { let newContainer = document.getElementById('pch-mediaList'); newContainer.textContent = ''; newContainer.dataset.lastItemYear = null; newContainer.dataset.lastItemMonth = null; for (let i of MediaStorage.Instance.medias) redraw(newContainer, i); }); MediaStorage.Instance.addEventListener("newMedia", (evt) => { let container = document.getElementById('pch-mediaList'); redraw(container, evt.detail); }); function serializeFileSize(size) { let units = [ 'o', 'Ko', 'Mo', 'Go', 'To' ]; let idx = 0; while (size >= 800 && idx < units.length) { ++idx; size /= 1024; } size = Math.floor(size * 100) / 100; return `${size} ${units[idx]}`; } function displayMeta(key, value) { let li = document.createElement("li"); let type = value?.type || null; let val = (value?.value ? value.value : value) || null; if (!val) return null; if (type === 'date') val = val.toLocaleString(); if (["dimension", "height", "width"].indexOf(key) >= 0) val += ' px'; if (["fileSize"].indexOf(key) >= 0) val = serializeFileSize(val); let keyTranslate = { fileSize: "File Size", photochamberImport: "Photochamber Imported", lensModel: "Lens Model", exposureTimeStr: "Exposure Time" }; let keyStr = keyTranslate[key] || (key[0].toUpperCase()+key.substr(1)); li.innerText = `${keyStr}: ${val}`; return li; } function displayMetas(metaData) { let metaList = document.createElement("ul"); metaData.libraryPath = null; if (metaData.exposureTime && metaData.exposureTimeStr) metaData.exposureTime = null; if (metaData.date && metaData.dateTime) metaData.dateTime = null; if (metaData.height && metaData.width) { metaData.dimension = { type: "string", value: `${metaData.width.value} x ${metaData.height.value}` } metaData.height = metaData.width = null; } for (let i of [ "date", "dimension", "height", "width", "fileSize" ]) { let metaItem = displayMeta(i, metaData[i]); metaItem && metaList.appendChild(metaItem); metaData[i] = null; } // FIXME sort and filter for (let i in metaData) { let metaItem = displayMeta(i, metaData[i]); metaItem && metaList.appendChild(metaItem); } return metaList; } function _displayMediaFullPage(fileName, imgUrl, metaData, downloadLink) { return new Promise(ok => { document.getElementById("pch-fullPagePreviewContainer").classList.add("loading"); document.getElementById("pch-fullPageMedia-title").innerText = fileName; document.getElementById("pch-fullPagePreview").onceLoaded = ok; document.getElementById("pch-fullPagePreview").src = imgUrl; document.getElementById("pch-fullPageDetail").innerText = ""; document.getElementById("pch-fullPageDetail").appendChild(displayMetas(Object.create(metaData || {}))); if (downloadLink) { document.getElementById("pch-fullPageDetail-dlButton").classList.remove("hidden"); document.getElementById("pch-fullPageDetail-dlButton").dataset["link"] = downloadLink; } else { document.getElementById("pch-fullPageDetail-dlButton").classList.add("hidden"); } }); } function LoadPreviousMedia() { let media = MediaStorage.Instance.previousMedia(fullPageMediaDisplayed); if (media) window.displayMediaFullPage(media); } function LoadNextMedia() { let media = MediaStorage.Instance.nextMedia(fullPageMediaDisplayed); if (media) window.displayMediaFullPage(media); } window.displayMediaFullPage = function(mediaItem) { document.getElementById("pch-fullPageMedia").classList.remove("hidden"); fullPageMediaDisplayed = mediaItem; if (!mediaItem) return _displayMediaFullPage("Error", null, {}, null); let containerSize = document.getElementById("pch-fullPageMedia").getBoundingClientRect(); let requestSize = mediaItem.resize(containerSize.width, containerSize.height); document.getElementById("pch-fullPagePreview").parentNode.style.maxWidth = "100%"; document.getElementById("pch-fullPagePreview").parentNode.style.maxHeight = "100%"; let meta = { ...mediaItem.meta, date: { type: 'date', value: mediaItem.date } || undefined, filename: mediaItem.filename ? { type: 'string', value: mediaItem.fileName } : undefined }; if (document.location.hash != `#${mediaItem.md5sum}`) history.pushState({}, '', `#${mediaItem.md5sum}`); return _displayMediaFullPage(mediaItem.fileName, `${mediaItem.thumbnail}?w=${requestSize.width}&h=${requestSize.height}&q=6`, meta, mediaItem.original); } document.getElementById("pch-fullPageMedia-closeBt") .addEventListener("click", () => { document.getElementById("pch-fullPageMedia").classList.add("hidden"); fullPageMediaDisplayed = false; history.pushState({}, '', '#'); }); document.getElementById("pch-fullPagePreview").addEventListener("load", () => { document.getElementById("pch-fullPagePreviewContainer").classList.remove("loading"); let domItem = document.getElementById("pch-fullPagePreview"); domItem.onceLoaded && domItem.onceLoaded(); domItem.onceLoaded = null; }); document.getElementById('pch-fullPageDetail-dlButton').addEventListener("click", (evt) => { if (!evt.target?.dataset?.link) return; let link = document.createElement('a'); link.target='_blank'; link.setAttribute("download", ""); link.href = evt.target.dataset.link; link.click(); }); document.addEventListener("keyup", evt => { lastKeyboardEvent = evt; }); document.addEventListener("keydown", evt => { lastKeyboardEvent = evt; if (!fullPageMediaDisplayed) return; if (evt.keyCode === 37 || evt.keyCode === 38) LoadPreviousMedia(); else if (evt.keyCode === 39 || evt.keyCode === 40) LoadNextMedia(); else { console.log("Unregistered key event", evt.key, evt.keyCode); return; } evt.preventDefault(); }); });