gallery_server_client_s190js/public/index.html
2026-10-26 11:00:14 +01:00

543 lines
18 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!doctype html>
<html lang="it">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Galleria</title>
<!-- Configurazione iniziale -->
<script>
window.BASE_URL = "";
window.AppConfig = window.AppConfig || {
ready: false,
};
</script>
<!-- CSS applicazione -->
<link rel="stylesheet" href="/css/base.css" />
<link rel="stylesheet" href="/css/operationBanner.css" />
<link rel="stylesheet" href="/css/header.css" />
<link rel="stylesheet" href="/css/gallery.css" />
<link rel="stylesheet" href="/css/workerStatus.css" />
<link rel="stylesheet" href="/css/map-base.css" />
<link rel="stylesheet" href="/css/map-photos.css" />
<link rel="stylesheet" href="/css/map-activities.css" />
<link rel="stylesheet" href="/css/modal.css" />
<link rel="stylesheet" href="/css/activityModal.css" />
<link rel="stylesheet" href="/css/infoPanel.css" />
<link rel="stylesheet" href="/css/bottomSheet.css" />
<link rel="stylesheet" href="/css/optionsSheet.css" />
<link rel="stylesheet" href="/css/utils.css" />
<!-- Leaflet e MarkerCluster -->
<link rel="stylesheet" href="https://unpkg.com/leaflet/dist/leaflet.css" />
<link
rel="stylesheet"
href="https://unpkg.com/leaflet.markercluster/dist/MarkerCluster.css"
/>
<link
rel="stylesheet"
href="https://unpkg.com/leaflet.markercluster/dist/MarkerCluster.Default.css"
/>
<!-- Mapbox GL JS per Terrain 3D -->
<link
href="https://api.mapbox.com/mapbox-gl-js/v3.15.0/mapbox-gl.css"
rel="stylesheet"
/>
<style>
#authLoader {
position: fixed;
inset: 0;
z-index: 9999;
display: flex;
align-items: center;
justify-content: center;
background: #fff;
font-size: 1.4rem;
}
#app[hidden] {
display: none !important;
}
body:not(.authenticated) #app {
display: none !important;
}
</style>
</head>
<body class="photos">
<div id="authLoader">Caricamento…</div>
<!-- Indicatori compatti delle code dei worker -->
<div id="workerStatus" class="worker-status" hidden
aria-label="Stato delle elaborazioni in background">
<div class="worker-status-item"
title="Acquisizione delle foto: job in attesa o in elaborazione"
aria-label="Acquisizione delle foto">
<svg class="worker-status-icon" viewBox="0 0 24 24" aria-hidden="true">
<path d="M4 7h4l2-2h4l2 2h4a1 1 0 0 1 1 1v11H3V8a1 1 0 0 1 1-1Z"
fill="none" stroke="currentColor" stroke-width="1.8"
stroke-linejoin="round"/>
<circle cx="12" cy="13" r="3.5" fill="none"
stroke="currentColor" stroke-width="1.8"/>
</svg>
<span id="workerPhotosCount" class="worker-status-count">0</span>
<span id="workerPhotosErrors" class="worker-status-errors" hidden>0 err.</span>
</div>
<div class="worker-status-item"
title="Riconoscimento facciale: job in attesa o in elaborazione"
aria-label="Riconoscimento facciale">
<svg class="worker-status-icon" viewBox="0 0 24 24"
aria-hidden="true">
<path d="M8 3H5a2 2 0 0 0-2 2v3M16 3h3a2 2 0 0 1 2 2v3M3 16v3a2 2 0 0 0 2 2h3M21 16v3a2 2 0 0 1-2 2h-3"
fill="none" stroke="currentColor" stroke-width="1.8"
stroke-linecap="round"/>
<path d="M8 10a1 1 0 1 1 2 0m4 0a1 1 0 1 1 2 0M9 15c1.7 1.5 4.3 1.5 6 0"
fill="none" stroke="currentColor" stroke-width="1.8"
stroke-linecap="round"/>
</svg>
<span id="workerFacesCount" class="worker-status-count">0</span>
<span id="workerFacesErrors" class="worker-status-errors" hidden>0 err.</span>
</div>
<div class="worker-status-item"
title="Attività: job in attesa o in elaborazione"
aria-label="Elaborazione attività">
<span class="worker-status-icon worker-status-emoji"
aria-hidden="true">🎬</span>
<span id="workerActivitiesCount" class="worker-status-count">0</span>
<span id="workerActivitiesErrors" class="worker-status-errors"
hidden>0 err.</span>
</div>
<div class="worker-status-item"
title="Geocoding: job in attesa o in elaborazione"
aria-label="Calcolo delle posizioni">
<svg class="worker-status-icon" viewBox="0 0 24 24"
aria-hidden="true">
<path d="M20 10c0 5-8 11-8 11S4 15 4 10a8 8 0 1 1 16 0Z"
fill="none" stroke="currentColor" stroke-width="1.8"
stroke-linejoin="round"/>
<circle cx="12" cy="10" r="2.5" fill="none"
stroke="currentColor" stroke-width="1.8"/>
</svg>
<span id="workerLocationsCount" class="worker-status-count">0</span>
<span id="workerLocationsErrors" class="worker-status-errors" hidden>0 err.</span>
</div>
</div>
<div
id="app"
hidden
aria-hidden="true"
>
<!-- HEADER -->
<header class="topbar">
<h1>Galleria</h1>
<div id="selectionBar" class="selection-bar" hidden>
<button id="selectionCancel" class="icon-btn selection-cancel"
type="button" aria-label="Annulla selezione" title="Annulla selezione">✕</button>
<span id="selectionCount" aria-live="polite">0 selezionati</span>
<div class="selection-actions-wrap">
<button id="selectionActionsBtn" class="icon-btn"
type="button" aria-label="Azioni sugli elementi selezionati"
title="Azioni">⋮</button>
<div id="selectionActionsMenu" class="selection-actions-menu"
role="menu" aria-label="Azioni sulle foto selezionate" hidden>
<button id="selectionDownloadBtn" class="selection-menu-item"
type="button" role="menuitem">
<span class="selection-menu-icon" aria-hidden="true">⬇️</span>
<span class="selection-menu-label">Download</span>
</button>
<button id="selectionToggleSoftBtn" class="selection-menu-item"
type="button" role="menuitem">
<span class="selection-menu-icon" aria-hidden="true">🗑️</span>
<span class="selection-menu-label">Soft delete</span>
</button>
<button id="selectionOtherBtn" class="selection-menu-item"
type="button" role="menuitem">
<span class="selection-menu-icon" aria-hidden="true">⋯</span>
<span class="selection-menu-label">Altro</span>
<span class="selection-menu-hint">Prossimamente</span>
</button>
</div>
</div>
</div>
<!-- Pulsante singolo: mostra la modalità corrente e passa all'altra -->
<button
id="modeToggle"
class="mode-toggle"
type="button"
aria-label="Passa alle attività"
title="Passa alle attività"
>
📷
</button>
<div class="top-buttons">
<button
id="uploadPhotoBtn"
class="icon-btn"
type="button"
aria-label="Carica una foto"
title="Carica una foto"
>
📤
</button>
<div class="deleted-toggle">
<button
id="showDeletedBtn"
class="icon-btn deleted-toggle-btn is-hidden"
type="button"
aria-label="Mostra foto cancellate"
aria-pressed="false"
title="Mostra foto cancellate"
>
<span class="eye-icon" aria-hidden="true">👁️</span>
</button>
</div>
<button
id="openMapBtn"
class="icon-btn"
type="button"
aria-label="Apri mappa"
title="Mappa"
>
🗺️
</button>
<button
id="optionsBtn"
class="icon-btn"
type="button"
aria-label="Apri opzioni"
title="Opzioni"
>
⋮
</button>
<button
id="configBtn"
class="icon-btn"
type="button"
aria-label="Apri configurazione"
title="Configurazione"
>
⚙️
</button>
<button
id="logoutBtn"
class="icon-btn logout-btn"
type="button"
data-logout
data-redirect="/login"
aria-label="Logout"
title="Logout"
>
<img src="/logout.png" class="logout-icon" alt="" />
</button>
</div>
</header>
<!-- GALLERIA -->
<main id="gallery"></main>
<!-- MAPPA -->
<div id="globalMap" class="global-map"></div>
<div id="terrainMap" class="global-map terrain-map"></div>
<!-- MODAL FOTO E VIDEO -->
<div id="modal" class="modal" aria-hidden="true">
<button
id="modalClose"
class="modal-close"
type="button"
aria-label="Chiudi"
>
×
</button>
<div id="modalMediaContainer" class="modal-media"></div>
<button
id="modalPrev"
class="modal-nav-btn prev hidden"
type="button"
aria-label="Elemento precedente"
>
‹
</button>
<button
id="modalNext"
class="modal-nav-btn next hidden"
type="button"
aria-label="Elemento successivo"
>
›
</button>
</div>
<!-- MODAL ATTIVITÀ -->
<div id="activityModal" aria-hidden="true" inert>
<button
id="activityModalClose"
class="modal-close"
type="button"
aria-label="Chiudi attività"
>
×
</button>
<div id="activityModalContent"></div>
</div>
<!-- INFO PANEL -->
<aside id="infoPanel" class="info-panel" aria-hidden="true"></aside>
<!-- BOTTOM SHEET MAPPA -->
<div id="bottomSheet" class="bottom-sheet">
<div class="sheet-header"></div>
<div id="sheetGallery" class="sheet-gallery"></div>
</div>
<!-- OPTIONS SHEET -->
<div id="optionsSheet" class="options-sheet">
<div class="sheet-header"></div>
<div class="sheet-content">
<h3>Ordinamento</h3>
<button class="sheet-btn" type="button" data-sort="asc">
Data ↑
</button>
<button class="sheet-btn" type="button" data-sort="desc">
Data ↓
</button>
<h3>Raggruppamento</h3>
<button class="sheet-btn" type="button" data-group="auto">
Automatico
</button>
<button class="sheet-btn" type="button" data-group="day">
Giorno
</button>
<button class="sheet-btn" type="button" data-group="month">
Mese
</button>
<button class="sheet-btn" type="button" data-group="year">
Anno
</button>
<button class="sheet-btn" type="button" data-group="folder" data-photo-filter>
Cartella
</button>
<h3>Filtri</h3>
<button class="sheet-btn" type="button" data-filter="">Tutto</button>
<button class="sheet-btn" type="button" data-filter="folder">
Con cartella
</button>
<button class="sheet-btn" type="button" data-filter="location">
Con GPS
</button>
<button class="sheet-btn" type="button" data-filter="images" data-photo-filter>
Solo foto
</button>
<button class="sheet-btn" type="button" data-filter="videos" data-photo-filter>
Solo video
</button>
<h3>Cartella specifica</h3>
<select id="galleryFolder" data-photo-filter>
<option value="">Tutte le cartelle</option>
</select>
<h3>Ricerca</h3>
<input
id="gallerySearch"
type="search"
data-photo-filter
placeholder="Nome file o cartella..."
autocomplete="off"
/>
<h3>Periodo</h3>
<select id="galleryYear" data-photo-filter>
<option value="">Tutti gli anni</option>
</select>
<select id="galleryMonth" data-photo-filter>
<option value="">Tutti i mesi</option>
<option value="1">Gennaio</option>
<option value="2">Febbraio</option>
<option value="3">Marzo</option>
<option value="4">Aprile</option>
<option value="5">Maggio</option>
<option value="6">Giugno</option>
<option value="7">Luglio</option>
<option value="8">Agosto</option>
<option value="9">Settembre</option>
<option value="10">Ottobre</option>
<option value="11">Novembre</option>
<option value="12">Dicembre</option>
</select>
<button class="sheet-btn" type="button" data-reset-filters data-photo-filter>
Azzera filtri
</button>
</div>
</div>
<div id="sheetOverlay" class="sheet-overlay"></div>
</div>
<!-- ========================================= -->
<!-- LIBRERIE ESTERNE -->
<!-- ========================================= -->
<!-- Leaflet -->
<script src="https://unpkg.com/leaflet/dist/leaflet.js"></script>
<!-- Leaflet MarkerCluster -->
<script src="https://unpkg.com/leaflet.markercluster/dist/leaflet.markercluster.js"></script>
<!-- ========================================= -->
<!-- VENDOR -->
<!-- ========================================= -->
<script src="/vendor/rbush.min.js"></script>
<!-- Bridge librerie -->
<script src="/js/lib/vendor.js"></script>
<!-- ========================================= -->
<!-- CONFIG / AUTENTICAZIONE -->
<!-- ========================================= -->
<script src="/js/config.js"></script>
<script src="/js/auth.js"></script>
<script src="/js/pageControls.js"></script>
<script src="/js/logout.js"></script>
<!-- ========================================= -->
<!-- CORE APPLICAZIONE -->
<!-- ========================================= -->
<script src="/js/api.js"></script>
<script src="/js/state.js"></script>
<script src="/js/sync.js"></script>
<script src="/js/galleryUtils.js"></script>
<script src="/js/activityThumbs.js"></script>
<script src="/js/galleryZoom.js"></script>
<script src="/js/galleryRenderer.js"></script>
<script src="/js/operationBanner.js"></script>
<script src="/js/gallerySelection.js"></script>
<script src="/js/gallery.js"></script>
<script src="/js/photoUpload.js"></script>
<!-- ========================================= -->
<!-- MAPPA CORE -->
<!-- ========================================= -->
<script src="/js/map/spatialIndex.js"></script>
<script src="/js/map/clusterIcons.js"></script>
<script src="/js/map/markerFactory.js"></script>
<script src="/js/map/layerManager.js"></script>
<script src="/js/map/photosLayer.js"></script>
<script src="/js/map/activitiesLayer.js"></script>
<script src="/js/map/baseLayers.js"></script>
<script src="/js/map/engine.js"></script>
<script src="/js/map/globalMap.js"></script>
<!-- ========================================= -->
<!-- MAPBOX GL JS -->
<!-- ========================================= -->
<script src="https://api.mapbox.com/mapbox-gl-js/v3.15.0/mapbox-gl.js"></script>
<!-- ========================================= -->
<!-- MAPBOX PHOTOS -->
<!-- ========================================= -->
<!-- 1. Source -->
<script src="/js/map/photos/photoSource.js"></script>
<!-- 2. Cluster -->
<script src="/js/map/photos/photoClusters.js"></script>
<!-- 3. Marker -->
<script src="/js/map/photos/photoMarkers.js"></script>
<!-- 4. Spiderfy -->
<script src="/js/map/photos/photoSpiderfy.js"></script>
<!-- ========================================= -->
<!-- MAPBOX ACTIVITIES -->
<!-- ========================================= -->
<!-- 1. Source -->
<script src="/js/map/activities/activitySource.js"></script>
<!-- 2. Cluster -->
<script src="/js/map/activities/activityClusters.js"></script>
<!-- 3. Marker -->
<script src="/js/map/activities/activityMarkers.js"></script>
<!-- 4. Spiderfy -->
<script src="/js/map/activities/activitySpiderfy.js"></script>
<!-- ========================================= -->
<!-- MAPBOX CONTROLLER -->
<!-- ========================================= -->
<!-- Deve essere DOPO tutti i moduli sopra -->
<script src="/js/map/mapboxMarkers.js"></script>
<!-- ========================================= -->
<!-- TERRAIN 3D -->
<!-- ========================================= -->
<script src="/js/map/terrain3d.js"></script>
<!-- ========================================= -->
<!-- MODALI -->
<!-- ========================================= -->
<script src="/js/modal.js"></script>
<script src="/js/activityModal.js"></script>
<script src="/js/infoPanel.js"></script>
<script src="/js/bottomSheet.js"></script>
<script src="/js/optionsSheet.js"></script>
<!-- ========================================= -->
<!-- DEBUG -->
<!-- ========================================= -->
<script src="https://cdn.jsdelivr.net/npm/eruda"></script>
<script>
if (window.eruda) {
window.eruda.init();
}
</script>
<script src="/js/workerStatus.js"></script>
</body>
</html>