| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447 |
- $(() => {
- let fullPageMediaDisplayed = false;
- let fullPageMediaList = false;
- 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 = '<i class="bi bi-pen"></i>';
- inputGroup.appendChild(bt);
- bt.addEventListener('click', async () => {
- await MediaStorage.Instance.setMetaValue(fullPageMediaDisplayed?.fixedSum || fullPageMediaList, key, valInput.value)
- reloadCurrentMedia();
- });
- inputGroup.addEventListener('submit', async evt => {
- evt.preventDefault();
- await MediaStorage.Instance.setMetaValue(fullPageMediaDisplayed?.fixedSum || fullPageMediaList, key, valInput.value);
- reloadCurrentMedia();
- });
- }
- li.appendChild(keySpan);
- li.appendChild(valSpan);
- return li;
- }
- let latLngTimeo = null;
- function setLatLngWithDelay(medias, lat, lng) {
- if (latLngTimeo)
- clearTimeout(latLngTimeo);
- latLngTimeo = setTimeout(async () => {
- await MediaStorage.Instance.setMetaValue(medias.map(x => x.fixedSum), 'gpsLocation', JSON.stringify([lat, lng]));
- latLngTimeo = null;
- reloadCurrentMedia();
- }, 500);
- }
- function displayMap(media, geo, isRo) {
- let jsonGeo = geo;
- let marker;
- let searchMarker;
- let searchPopup;
- let createMarker = (latLng) => {
- if (marker)
- return marker;
- marker = L.marker(latLng, { draggable: true, autoPan: true });
- if (!isRo)
- marker.addEventListener('dragend', e => {
- let latLng = e.target?.getLatLng();
- if (!latLng || !latLng.lat || !latLng.lng)
- return;
- setLatLngWithDelay(media, latLng.lat, latLng.lng);
- });
- return marker;
- };
- try {
- geo = geo && JSON.parse(geo);
- }
- catch(err) { return null; }
- let outerHTML = document.createElement("li");
- outerHTML.className = "row";
- let container = document.createElement("div");
- container.className = "leaflet-container container";
- outerHTML.appendChild(container);
- let innerHTML = document.createElement("div");
- container.appendChild(innerHTML);
- let map = L.map(innerHTML, { scrollWheelZoom: false });
- L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
- attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
- }).addTo(map);
- if (!isRo) {
- L.Control.geocoder({defaultMarkGeocode: false}).on('markgeocode', e => {
- let pos = e?.geocode?.center;
- if (!pos)
- return;
- if (!searchMarker)
- searchMarker = L.marker(pos).addTo(map);
- else
- searchMarker.setLatLng(pos);
- searchMarker.bindPopup("<div class='container'><div class='row'>" +(e.geocode.html || e.geocode.name) + "</div><div class='row'><button class='btn btn-primary'>Set</button></div></div>").openPopup();
- searchMarker._popup._container.querySelector("button").addEventListener('click', () => {
- setLatLngWithDelay(media, pos.lat, pos.lng);
- });
- map.setView(pos, 13);
- }).addTo(map);
- map.addEventListener("contextmenu", evt => {
- if (!marker)
- createMarker(evt.latlng).addTo(map);
- else
- marker.setLatLng(evt.latlng);
- setLatLngWithDelay(media, evt.latlng.lat, evt.latlng.lng);
- });
- }
- if (geo) {
- map.setView(geo, 13);
- createMarker(geo).addTo(map);
- }
- else
- map.setView(L.latLng(45, 2), 5);
- setTimeout(function () {
- map.invalidateSize();
- }, 0);
- if (jsonGeo) {
- let a = document.createElement("a");
- a.href = `https://www.openstreetmap.org/?mlat=${geo[0]}&mlon=${geo[1]}`;
- a.target = "_blank";
- a.innerHTML = jsonGeo;
- container.appendChild(a);
- }
- return outerHTML;
- }
- function displayMetas(media, 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()))) {
- if (i === 'gpsLocation') {
- let dom = displayMap(Array.isArray(media) ? media : [media], metaData[i].value, isRo);
- dom && metaList.appendChild(dom);
- continue;
- }
- let metaItem = displayMeta(i, metaData[i], isRo || !MediaStorage.Instance.allMetaTypes[i]?.canWrite);
- metaItem && metaList.appendChild(metaItem);
- }
- if (!isRo && media) {
- for (let i of ['geoCountry', 'geoCity', 'geoAdmin']) {
- if (!metaData[i]) {
- let metaItem = displayMeta(i, "", false);
- metaItem && metaList.appendChild(metaItem);
- }
- }
- if (!metaData['gpsLocation']) {
- let dom = displayMap(Array.isArray(media) ? media : [media], null, isRo);
- dom && metaList.appendChild(dom);
- }
- }
- return metaList;
- }
- function reloadCurrentMedia() {
- fullPageMediaDisplayed && window.displayMediaFullPage(MediaStorage.Instance.getMediaLocal(fullPageMediaDisplayed.fixedSum));
- }
- 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', async e => {
- e.preventDefault();
- await MediaStorage.Instance.removeTag(fullPageMediaDisplayed?.fixedSum || fullPageMediaList, i);
- reloadCurrentMedia();
- });
- uiItem.appendChild(btItem);
- }
- tagListUi.appendChild(uiItem);
- };
- for (let i of fixedTagList)
- createTagUiItem(i, true);
- for (let i of tagList)
- createTagUiItem(i, !writeAccess);
- if (writeAccess) {
- let inputGroup = document.createElement('form');
- tagListUi.appendChild(inputGroup);
- inputGroup.classList.add('input-group');
- let valInput = document.createElement("input");
- valInput.classList.add("form-control");
- valInput.addEventListener('keyup', evt => evt.stopPropagation());
- valInput.addEventListener('keydown', evt => evt.stopPropagation());
- inputGroup.appendChild(valInput);
- let bt = document.createElement('button');
- bt.className = 'btn btn-outline-secondary';
- bt.type = 'button';
- bt.innerHTML = '<i class="bi bi-tags"></i>';
- inputGroup.appendChild(bt);
- bt.addEventListener('click', async () => { await MediaStorage.Instance.addTag(fullPageMediaDisplayed?.fixedSum || fullPageMediaList, valInput.value); reloadCurrentMedia(); });
- inputGroup.addEventListener('submit', async evt => {
- evt.preventDefault();
- await MediaStorage.Instance.addTag(fullPageMediaDisplayed?.fixedSum || fullPageMediaList, valInput.value);
- reloadCurrentMedia();
- });
- }
- return tagListUi;
- }
- function displayDownloadBt(downloadLink) {
- let bt = document.createElement("button");
- bt.type = "button";
- bt.className = "btn btn-primary";
- bt.textContent = "Download";
- bt.addEventListener("click", (evt) => {
- let link = document.createElement('a');
- link.target='_blank';
- link.setAttribute("download", "");
- link.href = downloadLink;
- link.click();
- });
- return bt;
- }
- function displayRemoveButton(ids) {
- let bt = document.createElement("button");
- bt.type = "button";
- bt.className = "btn btn-danger";
- bt.textContent = ids.length > 1 ? "Remove files" : "Remove file";
- bt.addEventListener("click", async evt => {
- if (window.confirm("File will be removed from the server. Are you sure ?")) {
- await MediaStorage.Instance.remoteRemove(ids);
- CloseFullpageMedia();
- }
- });
- return bt;
- }
- function displayImg(medias) {
- if (!Array.isArray(medias))
- medias = [ medias ];
- if (medias.length > 1) {
- $("#pch-fullPagePreview > a").removeClass("hidden");
- $("#pch-fullPagePreview > .carousel-indicators").removeClass("hidden");
- } else {
- $("#pch-fullPagePreview > a").addClass("hidden");
- $("#pch-fullPagePreview > .carousel-indicators").addClass("hidden");
- }
- const containerSize = document.getElementById("pch-fullPageMedia").getBoundingClientRect();
- let container = document.querySelector("#pch-fullPagePreview .carousel-inner");
- container.textContent = "";
- let carouselIndicators = document.querySelector("#pch-fullPagePreview .carousel-indicators");
- carouselIndicators.textContent = "";
- return Promise.allSettled(medias.map((media, index) => new Promise(ok => {
- let item = document.createElement("div");
- item.className = "carousel-item";
- let img = document.createElement("img");
- let requestSize = media.resize(containerSize.width, containerSize.height);
- const requestSizeQuery = requestSize ? `&w=${requestSize.width}&h=${requestSize.height}` : "";
- img.src = `${media.thumbnail}?q=6${requestSizeQuery}`;
- img.addEventListener("load", () => ok());
- item.appendChild(img);
- container.appendChild(item);
- let carouselIndicator = document.createElement("button");
- carouselIndicator.type = "button";
- carouselIndicator.dataset.bsTarget = "#pch-fullPagePreview";
- carouselIndicator.dataset.bsSlideTo = index;
- if (!index) {
- item.classList.add("active");
- carouselIndicator.classList.add("active");
- }
- carouselIndicators.appendChild(carouselIndicator);
- ++index;
- }))).finally(() => {
- document.getElementById("pch-fullPagePreviewContainer").classList.remove("loading");
- if (medias.length > 1)
- $(container.parentElement).carousel();
- });
- }
- function _displayMediaFullPage(media, fileName, imgUrl, metaData, downloadLink, fileIds, writeAccess) {
- document.getElementById("pch-fullPagePreviewContainer").classList.add("loading");
- document.getElementById("pch-fullPageMedia-title").innerText = fileName;
- document.getElementById("pch-fullPageDetail").innerText = "";
- if (downloadLink)
- document.getElementById("pch-fullPageDetail").appendChild(displayDownloadBt(downloadLink));
- if (fileIds && writeAccess)
- document.getElementById("pch-fullPageDetail").appendChild(displayRemoveButton(fileIds));
- document.getElementById("pch-fullPageDetail").appendChild(displayTags(metaData?.fixedTags || [], metaData?.tags || [], writeAccess));
- document.getElementById("pch-fullPageDetail").appendChild(displayMetas(media, Object.assign({}, metaData || {}), !writeAccess));
- document.querySelector("#pch-fullPagePreview .carousel-inner").textContent = "";
- $("#pch-fullPagePreview.carousel").carousel("dispose");
- return displayImg(media, imgUrl);
- }
- function LoadPreviousMedia() {
- let i = fullPageMediaDisplayed;
- if (!i) return;
- while (i = MediaStorage.Instance.previousMedia(i)) {
- if (window.FilterManager.match(i)) {
- window.displayMediaFullPage(i);
- break;
- }
- }
- }
- function LoadNextMedia() {
- let i = fullPageMediaDisplayed;
- if (!i) return;
- while (i = MediaStorage.Instance.nextMedia(i)) {
- if (window.FilterManager.match(i)) {
- window.displayMediaFullPage(i);
- break;
- }
- }
- }
- function CloseFullpageMedia() {
- if (fullPageMediaDisplayed !== false || fullPageMediaList)
- document.body.classList.remove("overlay-visible");
- document.getElementById("pch-fullPageMedia").classList.add("hidden");
- fullPageMediaDisplayed = false;
- history.pushState({}, '', '#');
- document.Title.pop();
- }
- window.displayMediaFullPage = function(mediaItem) {
- document.getElementById("pch-fullPageMedia").classList.remove("hidden");
- document.getElementById("pch-fullPageMedia").classList.remove("multiple");
- if (fullPageMediaDisplayed)
- document.Title.replaceTitle(mediaItem.fileName);
- else
- document.Title.pushTitle(mediaItem.fileName);
- fullPageMediaDisplayed = mediaItem ?? null;
- document.body.classList.add("overlay-visible");
- if (!mediaItem)
- return _displayMediaFullPage(null, "Error", null, {}, null, null, false);
- let meta = {
- ...mediaItem.meta,
- date: { type: 'date', value: mediaItem.getDate() } || undefined,
- filename: mediaItem.filename ? { type: 'string', value: mediaItem.fileName } : undefined,
- fixedTags: mediaItem.fixedTags,
- tags: mediaItem.tags
- };
- if (document.location.hash != `#${mediaItem.fixedSum}`)
- history.pushState({}, '', `#${mediaItem.fixedSum}`);
- return _displayMediaFullPage(mediaItem, mediaItem.fileName, "", meta, `${mediaItem.original}?trim`, [mediaItem.fixedSum], mediaItem.writeAccess);
- }
- function aggregateMetas(medias) {
- let meta = medias.reduce((acc, x) => {
- for (let key in x.meta) {
- acc[key] = acc[key] || x.meta[key];
- acc[key] = acc[key].value != x.meta[key].value ? "(multiple)" : x.meta[key];
- }
- return acc;
- }, {});
- delete meta.dateTime;
- if (!Number.isInteger(meta.height) || !Number.isInteger(meta.width)) {
- delete meta.height;
- delete meta.width;
- }
- return meta;
- }
- window.displayMultipleMediaFullPage = function(medias) {
- const title = "Multiple edit"; // FIXME lang ?
- fullPageMediaList = Array.from(new Set(medias.map(x => x.fixedSum)));
- document.getElementById("pch-fullPageMedia").classList.remove("hidden");
- document.getElementById("pch-fullPageMedia").classList.add("multiple");
- document.Title.pushTitle(title);
- document.body.classList.add("overlay-visible");
- let meta = {
- ...aggregateMetas(medias),
- fixedTags: medias.reduce((acc, x) => { x.fixedTags.forEach(tag => acc.add(tag)) ; return acc; }, new Set()),
- tags: medias.reduce((acc, x) => { x.tags.forEach(tag => acc.add(tag)) ; return acc; }, new Set()),
- };
- return _displayMediaFullPage(medias, title, "", meta, null, medias.map(x => x.fixedSum), true);
- }
- document.getElementById("pch-fullPageMedia-closeBt")
- .addEventListener("click", () => CloseFullpageMedia());
- document.addEventListener("keydown", evt => {
- if (!fullPageMediaDisplayed)
- return;
- if (evt.keyCode === 37 || evt.keyCode === 38)
- LoadPreviousMedia();
- else if (evt.keyCode === 39 || evt.keyCode === 40)
- LoadNextMedia();
- });
- document.onClosePopinRequested(() => { CloseFullpageMedia(); });
- });
|