$(() => {
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 editButton = document.createElement("button");
let img = document.createElement("img");
let container = document.createElement("li");
let loadingImg = document.createElement("div");
checkbox.type = "checkbox";
editButton.type = "button";
if (!mediaItem.writeAccess)
editButton.classList.add("hidden");
let editButtonSpan = document.createElement("span");
editButtonSpan.className = "bi bi-pen";
editButton.appendChild(editButtonSpan);
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);
container.appendChild(editButton);
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;
}
editButton.addEventListener("click", e => {
e.stopPropagation();
if (!checkbox.checked) {
checkbox.checked = true;
setSelectionCheckboxValue(mediaItem, true);
}
let sel = selectedThumbnails.map(x => MediaStorage.Instance.getMediaLocal(x)).filter(x => x.writeAccess);
if (sel.length === 1 && sel[0].md5sum === mediaItem.md5sum) {
checkbox.checked = false;
setSelectionCheckboxValue(mediaItem, false);
document.location.hash = mediaItem.md5sum;
return;
}
console.log(sel);
});
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)
if (window.FilterManager.match(i))
redraw(newContainer, i);
});
MediaStorage.Instance.addEventListener("newMedia", (evt) => {
let container = document.getElementById('pch-mediaList');
if (window.FilterManager.match(evt.detail))
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, isRo) {
let li = document.createElement("li");
let type = value?.type || null;
let val = (value?.value !== undefined ? value.value : value);
if (!val && 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);
if (key == 'fNumber')
val = `f/ ${val}`;
let keyTranslate = {
fileSize: "File Size",
photochamberImport: "Photochamber Imported",
lensModel: "Lens Model",
exposureTimeStr: "Exposure Time"
};
let keySpan = document.createElement('span');
let valSpan = document.createElement('div');
li.classList.add("row");
keySpan.className = "metaKey col-xl-12 col-4";
valSpan.className = "metaVal col-xl-12 col-8";
let inputGroup = document.createElement('form');
valSpan.appendChild(inputGroup);
inputGroup.classList.add('input-group');
keySpan.innerText = (keyTranslate[key] || (key[0].toUpperCase()+key.substr(1))) + ':';
let valInput = document.createElement("input");
valInput.classList.add("form-control");
valInput.value = val;
valInput.disabled = isRo;
valInput.addEventListener('keyup', evt => evt.stopPropagation());
valInput.addEventListener('keydown', evt => evt.stopPropagation());
inputGroup.appendChild(valInput);
if (!isRo) {
let bt = document.createElement('button');
bt.className = 'btn btn-outline-secondary';
bt.type = 'button';
bt.innerHTML = '';
inputGroup.appendChild(bt);
bt.addEventListener('click', () => MediaStorage.Instance.setMetaValue(fullPageMediaDisplayed.md5sum, key, valInput.value));
inputGroup.addEventListener('submit', evt => {
evt.preventDefault();
MediaStorage.Instance.setMetaValue(fullPageMediaDisplayed.md5sum, key, valInput.value);
});
}
li.appendChild(keySpan);
li.appendChild(valSpan);
return li;
}
function displayMetas(metaData, isRo) {
let metaList = document.createElement("ul");
metaData.libraryPath = null;
metaData.tags = metaData.fixedTags = 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], true);
metaItem && metaList.appendChild(metaItem);
metaData[i] = null;
}
for (let i of Object.keys(metaData).sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase()))) {
let metaItem = displayMeta(i, metaData[i], isRo || !MediaStorage.Instance.allMetaTypes[i]?.canWrite);
metaItem && metaList.appendChild(metaItem);
}
if (!isRo)
for (let i of ['geoCountry']) {
if (!metaData[i]) {
let metaItem = displayMeta(i, "", false);
metaItem && metaList.appendChild(metaItem);
}
}
return metaList;
}
function displayTags(fixedTagList, tagList, writeAccess) {
let tagListUi = document.createElement("ul");
tagListUi.className = "taglist";
let createTagUiItem = (i, roTag) => {
let uiItem = document.createElement("li");
uiItem.className = "badge text-bg-light";
let textItem = document.createElement("span");
textItem.textContent = i;
uiItem.appendChild(textItem);
if (!roTag) {
let btItem = document.createElement("a");
btItem.className = "border-start bi bi-x removeBt";
btItem.href = '#';
btItem.addEventListener('click', e => {
e.preventDefault();
});
uiItem.appendChild(btItem);
}
tagListUi.appendChild(uiItem);
};
for (let i of fixedTagList)
createTagUiItem(i, true);
for (let i of tagList)
createTagUiItem(i, !writeAccess);
return tagListUi;
}
function _displayMediaFullPage(fileName, imgUrl, metaData, downloadLink, writeAccess) {
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.assign({}, metaData || {}), !writeAccess));
document.getElementById("pch-fullPageDetail").appendChild(displayTags(metaData?.fixedTags || [], metaData?.tags || [], writeAccess));
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);
}
function CloseFullpageMedia() {
document.getElementById("pch-fullPageMedia").classList.add("hidden");
fullPageMediaDisplayed = false;
history.pushState({}, '', '#');
}
window.displayMediaFullPage = function(mediaItem) {
document.getElementById("pch-fullPageMedia").classList.remove("hidden");
fullPageMediaDisplayed = mediaItem;
if (!mediaItem)
return _displayMediaFullPage("Error", null, {}, [], null, false);
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,
fixedTags: mediaItem.fixedTags,
tags: mediaItem.tags
};
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, mediaItem.writeAccess);
}
document.getElementById("pch-fullPageMedia-closeBt")
.addEventListener("click", () => CloseFullpageMedia());
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 if (evt.keyCode === 27)
CloseFullpageMedia();
else {
console.log("Unregistered key event", evt.key, evt.keyCode);
return;
}
evt.preventDefault();
});
});