543 lines
18 KiB
HTML
543 lines
18 KiB
HTML
<!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>
|