Compare commits

...

20 Commits

Author SHA1 Message Date
1a3c95487f Temp search form 2025-11-21 17:54:50 +01:00
b0915d0973 Simplify marker regex + link CSS 2025-11-12 17:16:53 +01:00
ebea10059e Prevent null in captions... 2025-11-10 10:15:11 +01:00
249a31d11a More marker changes... 2025-11-07 11:41:35 +01:00
4d95aa7fce More markers in site sheet... 2025-11-03 10:11:09 +01:00
e7f1ed7a98 Markers in localization 2025-10-30 15:27:25 +01:00
5cf42ff4be Update marker parsing regex 2025-10-30 09:36:53 +01:00
0f16af95e7 Update for internal links 2025-10-30 08:52:16 +01:00
13550078cb Open modal for internal links (WIP) 2025-10-29 18:23:41 +01:00
262540e735 Bloody whitespace... 2025-10-24 15:31:47 +02:00
c8de489ead Parse markers for other record types 2025-10-24 15:24:39 +02:00
952dc3f841 Parse marker shortcodes (WIP) 2025-10-24 10:27:28 +02:00
00cedfeb85 Fix undefined bug... 2025-09-03 15:54:34 +02:00
53c3f6c6b2 More refactoring components (minus Site) 2025-09-02 09:59:02 +02:00
77d42a2c27 Refactor components (WIP) 2025-09-01 17:20:47 +02:00
40cc2ef88b Update main menu 2025-08-25 11:15:22 +02:00
e6578225ec Populate biblio state for reuse data 2025-08-08 16:28:22 +02:00
986c4b0a75 Images for Reuse 2025-08-08 14:17:01 +02:00
f849f885f9 Add Reuse asset type 2025-08-08 12:06:38 +02:00
9196653c0d Another feature popup fix... 2025-07-15 16:11:44 +02:00
15 changed files with 603 additions and 246 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

View File

@@ -26,7 +26,7 @@
<script src="js/index.js" type="module"></script> <script src="js/index.js" type="module"></script>
<title>WebGIS Isola di Capri</title> <title>WebGIS Isola di Capri</title>
</head> </head>
<body data-controller="menu" <body data-controller="menu modal"
data-action="menu-ready@document->menu#buildMenu menu-ready@document->menu#buildCartographyMenu"> data-action="menu-ready@document->menu#buildMenu menu-ready@document->menu#buildCartographyMenu">
<nav class="navbar mb-0" role="navigation"> <nav class="navbar mb-0" role="navigation">
<div class="navbar-brand"> <div class="navbar-brand">
@@ -63,9 +63,16 @@
</span> </span>
Catasto storico Catasto storico
</button> </button>
<button class="button is-outlined is-rounded is-link mr-4 mt-1" id="howto" title="Istruzioni"> <button class="button is-white mr-2 mt-1" title="Cerca"
<span class="icon is-large has-text-link"> data-id="search"
<i class="fas fa-question fa-lg"></i> data-action="modal#open">
<span class="icon">
<i class="fa fa-search"></i>
</span>
</button>
<button class="button is-outlined is-rounded mr-4 mt-1" id="howto" title="Istruzioni">
<span class="icon has-text-black">
<i class="fas fa-question"></i>
</span> </span>
</button> </button>
</div> </div>
@@ -192,6 +199,30 @@
</span> </span>
</li> </li>
</ul> </ul>
<p class="menu-label is-size-5 is-clickable" data-id="reuse">
<span class="icon pl-1 mr-2 is-small" data-layer-target="reuse" data-action="click->layer#toggle">
<i class="fa fa-xs fa-eye-slash" data-layer-target="icon"></i>
</span>
<span class="icon pr-1">
<img class="image" src="img/icons/reimpiego_menu.png"/>
</span>
<span role="button" data-action="click->menu#toggle" data-id="reuse">
Reimpiego
<span class="icon pl-2">
<i class="fa fa-chevron-right" data-menu-target="icon" data-id="reuse"></i>
</span>
</span>
</p>
<ul class="menu-list is-hidden" id="reuse-list" data-menu-target="list" data-controller="marker">
<li data-list-id="reuse-capri-sub">
<span role="button" class="is-clickable" data-action="click->menu#openSubList" data-list-id="reuse-capri-sub">
Capri
<span class="icon ml-2">
<i class="fa fa-chevron-right" data-menu-target="icon"></i>
</span>
</span>
</li>
</ul>
<p class="menu-label is-size-5 is-clickable" data-id="prehistoric"> <p class="menu-label is-size-5 is-clickable" data-id="prehistoric">
<span class="icon pl-1 mr-2 is-small" title="Nascondi" data-layer-target="prehistoric" data-action="click->layer#toggle"> <span class="icon pl-1 mr-2 is-small" title="Nascondi" data-layer-target="prehistoric" data-action="click->layer#toggle">
<i class="fa fa-xs fa-eye-slash" data-layer-target="icon"></i> <i class="fa fa-xs fa-eye-slash" data-layer-target="icon"></i>
@@ -286,6 +317,84 @@
<button title="Chiudi menu" class="delete is-pulled-right" data-action="menu#closeCartography"></button> <button title="Chiudi menu" class="delete is-pulled-right" data-action="menu#closeCartography"></button>
</aside> </aside>
</div> </div>
<!-- Search modal -->
<div class="modal" id="search" data-modal-target="modal">
<div class="modal-background" data-action="click->modal#close click->tabs#reset"></div>
<div class="modal-content has-background-white pt-4 mr-4 ml-4 pl-4 pr-4" style="min-height: 400px;">
<h1 class="is-size-4 has-text-centered">Ricerca</h1>
<div class="field">
<label class="label">Testo libero</label>
<div class="control is-full-width">
<input class="input" type="text" placeholder="Inserire parole chiave">
</div>
</div>
<div class="columns mt-5 pt-3">
<div class="field column">
<label class="label">Categoria sito</label>
<div class="control">
<div class="select">
<select>
<option default>-- Scegliere la categoria del sito --</option>
<option>Sito conservato</option>
<option>Sito non conservato</option>
</select>
</div>
</div>
</div>
<div class="field column">
<label class="label">Categoria reperto</label>
<div class="control">
<div class="select">
<select>
<option default>-- Scegliere la categoria del reperto --</option>
<option>Scultura</option>
<option>Epigrafe</option>
<option>Elemento architettonico</option>
<option>Decorazione parietale</option>
<option>Pavimentazione</option>
<option>Arredo</option>
<option>Abbigliamento e ornamenti personali</option>
</select>
</div>
</div>
</div>
<div class="field column">
<label class="label">Tecnica muraria</label>
<div class="control">
<div class="select">
<select>
<option default>-- Scegliere tecnica --</option>
<option>Opera poligonale</option>
<option>Opera incerta</option>
<option>Opera reticolata</option>
<option>Opera laterizia</option>
<option>Opera mista</option>
<option>Opera cementizia</option>
</select>
</div>
</div>
</div>
</div>
<div class="field is-grouped mt-5 has-text-right">
<div class="control">
<button class="button is-link">
<span>Cerca</span>
<span class="icon is-small">
<i class="fa fa-search"></i>
</span>
</button>
</div>
<div class="control">
<button class="button is-link is-light">
<span>Cancella filtri</span>
<span class="icon is-small">
<i class="fa fa-times"></i>
</span>
</button>
</div>
</div>
</div>
</div>
</div> </div>
<!-- Bibliography citations template --> <!-- Bibliography citations template -->
<template id="biblio-item-template"> <template id="biblio-item-template">
@@ -438,12 +547,63 @@
<button class="modal-close is-large" aria-label="close" data-action="modal#close tabs#reset"></button> <button class="modal-close is-large" aria-label="close" data-action="modal#close tabs#reset"></button>
</div> </div>
<!-- Underwater modal --> <!-- Underwater modal -->
<div class="modal" id="underwater-data" data-controller="modal biblio marker" data-modal-target="modal"> <div class="modal" id="underwater-data" data-controller="modal tabs marker" data-modal-target="modal">
<div class="modal-background" data-action="click->modal#close"></div> <div class="modal-background" data-action="click->modal#close click->tabs#reset"></div>
<div class="modal-content has-background-white"> <div class="modal-content has-background-white">
<div id="underwater-sheet"></div> <div class="tabs is-centered">
<ul>
<li class="is-active" id="for-underwater-sheet" data-tabs-target="tab active" data-action="click->tabs#activate">
<a>
<span class="icon is-small"><i class="fas fa-info-circle" aria-hidden="true"></i></span>
<span>Scheda</span>
</a>
</li>
<li id="for-photos" data-tabs-target="tab" data-action="click->tabs#activate">
<a>
<span class="icon is-small"><i class="fas fa-image" aria-hidden="true"></i></span>
<span>Immagini</span>
</a>
</li>
<li id="for-documents" data-tabs-target="tab" data-action="click->tabs#activate">
<a>
<span class="icon is-small"><i class="fas fa-book" aria-hidden="true"></i></span>
<span>Documenti</span>
</a>
</li>
</ul>
</div>
<div class="data-tabs" id="underwater-sheet" data-tabs-target="content"></div>
<div class="data-tabs is-hidden" id="photos" data-tabs-target="content"></div>
<div class="data-tabs is-hidden" id="documents" data-tabs-target="content"></div>
</div> </div>
<button class="modal-close is-large" aria-label="close" data-action="modal#close"></button> <button class="modal-close is-large" aria-label="close" data-action="modal#close tabs#reset"></button>
</div>
<!-- Reuse modal -->
<div class="modal" id="reuse-data" data-controller="modal biblio tabs marker" data-modal-target="modal">
<div class="modal-background" data-action="click->modal#close click->tabs#reset"></div>
<div class="modal-content has-background-white">
<div class="tabs is-centered">
<ul>
<li class="is-active" id="for-reuse-sheet" data-tabs-target="tab active" data-action="click->tabs#activate">
<a>
<span class="icon is-small"><i class="fas fa-info-circle" aria-hidden="true"></i></span>
<span>Scheda</span>
</a>
</li>
<li id="for-photos" data-tabs-target="tab" data-action="click->tabs#activate">
<a>
<span class="icon is-small"
><i class="fas fa-image" aria-hidden="true"></i
></span>
<span>Immagini</span>
</a>
</li>
</ul>
</div>
<div class="data-tabs" id="reuse-sheet" data-tabs-target="content"></div>
<div class="data-tabs is-hidden" id="photos" data-tabs-target="content"></div>
</div>
<button class="modal-close is-large" aria-label="close" data-action="modal#close tabs#reset"></button>
</div> </div>
<!-- Spherical photo modal --> <!-- Spherical photo modal -->
<div class="modal" id="spherical-modal"> <div class="modal" id="spherical-modal">

View File

@@ -1,4 +1,4 @@
import { GisState } from "../state.js"; import Utils from "./utils.js";
/** /**
* @class Finding * @class Finding
*/ */
@@ -23,7 +23,7 @@ export class Finding {
<strong>Misure:</strong> ${this._data.measurements} <strong>Misure:</strong> ${this._data.measurements}
</p> </p>
<p class="p-2"> <p class="p-2">
<strong>Luogo e anno rinvenimento:</strong> ${this._data.place}. ${this._data.year} <strong>Luogo e anno rinvenimento:</strong> ${Utils.parseMarkers(this._data.place)}. ${this._data.year}
</p> </p>
<p class="p-2"> <p class="p-2">
<strong>Datazione:</strong> ${this._data.dating} <strong>Datazione:</strong> ${this._data.dating}
@@ -36,7 +36,7 @@ export class Finding {
</p> </p>
<p class="mt-4 pl-2 pr-5"> <p class="mt-4 pl-2 pr-5">
<strong class="pb-3">Descrizione</strong></br> <strong class="pb-3">Descrizione</strong></br>
${this._data.description} ${Utils.parseMarkers(this._data.description)}
</p> </p>
<p class="mt-4 pl-2 pr-5"> <p class="mt-4 pl-2 pr-5">
<span class="icon has-text-link"> <span class="icon has-text-link">
@@ -50,64 +50,25 @@ export class Finding {
</p> </p>
</div>`; </div>`;
} }
renderImages() {
let content = `<div class="content has-text-centered">
<p class="is-size-5 mt-3">Immagini</p>`;
content += `
<div style="max-width: 70%; margin: 0 auto">
<p class="is-size-6 has-text-centered">Cliccare sull'immagine per aprire la gallery</p>
<figure class="is-relative is-clickable has-text-centered" id="finding-gallery">
<img src="img/${this.images[0].filename}" width="300"/>
<div class="icon overlay is-flex is-justify-content-center is-align-items-center">
<i class="is-flex fa fa-2x fa-play-circle"></i>
</div>
</figure>
</div>
</div>`;
return content;
}
/** /**
* @param {HTMLElement} imageContainer * @param {HTMLElement} imageContainer
* @param {Function} gallery * @param {Function} gallery
*/ */
async setImages(imageContainer, gallery) { async setImages(imageContainer, gallery) {
let record = await this.fetchData(`${GisState.apiUrl}/finding/${this._data.id}`) if (this._data.images?.length) {
imageContainer.innerHTML = Utils.renderImages('finding-gallery', this._data.images);
if (record.images.length) { gallery('finding-gallery', this._data.images);
this.images = record.images; } else
imageContainer.innerHTML = this.renderImages(); imageContainer.innerHTML = '<p class="has-text-centered">Nessuna risorsa visuale disponibile</p>';
gallery('finding-gallery', this.images);
}
} }
/** /**
* @param {number} recordId * @param {number} recordId
*/ */
async biblio(recordId) { async biblio(recordId) {
let finding = await this.fetchData(`${GisState.apiUrl}/finding/${recordId}`); let {citations, biblioElements} = await Utils.buildBibliography('finding', recordId);
this.biblioElements = biblioElements;
let citations = ''; return citations;
if (finding.bibliography.length) {
finding.bibliography.forEach(record => {
citations += `
<span class="is-clickable has-text-link"
data-action="click->biblio#open"
id="cit-${record.id}">
${record.citation}</span>`;
citations += record.pages?.length ? `, ${record.pages};` : ';';
this.biblioElements.push(`
<div class="p-2 mt-2" id="ref-${record.id}">
<p class="p-3">${record.reference}</p>
</div>
`);
});
}
return citations.trim().slice(0, -1);
} }
getReference(id) { getReference(id) {
@@ -116,8 +77,4 @@ export class Finding {
return ref.match(regex); return ref.match(regex);
}); });
} }
async fetchData(url) {
return await fetch(url).then(res => res.json());
}
} }

View File

@@ -1,4 +1,4 @@
import { GisState } from "../state.js"; import Utils from "./utils.js";
/** /**
* Component to render data for not conserved assets * Component to render data for not conserved assets
@@ -32,11 +32,11 @@ export class NotConserved {
<span class="icon has-text-link"> <span class="icon has-text-link">
<i class="fa fa-map"></i> <i class="fa fa-map"></i>
</span> </span>
<strong>Località generica:</strong> ${this._data.genericLocation} <strong>Località generica:</strong> ${Utils.parseMarkers(this._data.genericLocation)}
</p> </p>
<p class="mt-4 pl-2 pr-5"> <p class="mt-4 pl-2 pr-5">
<strong class="pb-3">Descrizione</strong></br> <strong class="pb-3">Descrizione</strong></br>
${this._data.shortDescription} ${Utils.parseMarkers(this._data.shortDescription)}
</p> </p>
<p class="mt-4 pl-2 pr-5"> <p class="mt-4 pl-2 pr-5">
<span class="icon has-text-link"> <span class="icon has-text-link">
@@ -51,116 +51,27 @@ export class NotConserved {
</div>`; </div>`;
} }
renderImages() {
let content = `<div class="content has-text-centered">
<p class="is-size-5 mt-3">Immagini</p>`;
content += `
<div style="max-width: 70%; margin: 0 auto">
<p class="is-size-6 has-text-centered">Cliccare sull'immagine per aprire la gallery</p>
<figure class="is-relative is-clickable has-text-centered" id="not-conserved-gallery">
<img src="img/${this.images[0].filename}" width="300"/>
<div class="icon overlay is-flex is-justify-content-center is-align-items-center">
<i class="is-flex fa fa-2x fa-play-circle"></i>
</div>
</figure>
</div>
</div>`;
return content;
}
/** /**
* @param {HTMLElement} imageContainer * @param {HTMLElement} imageContainer
* @param {Function} gallery * @param {Function} gallery
*/ */
async setImages(imageContainer, gallery) { setImages(imageContainer, gallery) {
if (this._data.images.length) { if (this._data.images?.length) {
this.images = this._data.images; imageContainer.innerHTML = Utils.renderImages('not-conserved-gallery', this._data.images);
imageContainer.innerHTML = this.renderImages(); gallery('not-conserved-gallery', this._data.images);
gallery('not-conserved-gallery', this.images);
} else } else
imageContainer.innerHTML = '<p class="has-text-centered">Nessuna risorsa visuale disponibile</p>'; imageContainer.innerHTML = '<p class="has-text-centered">Nessuna risorsa visuale disponibile</p>';
} }
async renderDocs() { async renderDocs() {
let record = await this.fetchData(`${GisState.apiUrl}/not_conserved/${this._data.id}`); return await Utils.generateDocsTable(this._data, 'not_conserved');
this.documentation = record.documents.filter(d => d.type === 'documentazione')
this.publications = record.documents.filter(d => d.type === 'pubblicazione');
let content = `
<div class="has-bottom-border">
<div class="p-2">
<table class="p-4 table is-fullwidth is-striped">
<thead>
<tr><th colspan=3 class="p-2 has-text-centered is-size-5">Documentazione di archivio</th>
<tr><th>Titolo</th><th>Luogo di conservazione</th><th>Download</th></tr>
</thead>
<tbody>
`;
for (const doc of this.documentation) {
content += `
<tr><td>${doc.title}</td><td>${doc.conservationPlace}</td><td><a class="button is-link has-text-white" href="docs/${doc.filename}">
<i class="fa fa-download mr-2"></i> PDF
</a></td></tr>
`;
}
if (this.publications.length) {
content += `
</tbody>
<thead>
<tr><th colspan=3 class="p-2 has-text-centered is-size-5">Pubblicazioni del progetto Carta Archeologica</th>
<tr><th>Titolo</th><th>Autori</th><th>Download</th></tr>
</thead>
<tbody>
`;
for (const doc of this.publications) {
content += `
<tr><td>${doc.title}</td><td>${doc.authors}</td><td><a class="button is-link has-text-white" href="docs/${doc.filename}">
<i class="fa fa-download mr-2"></i> PDF
</a></td></tr>
`;
}
}
content += `
</tbody>
</table>
</div>
</div>
`;
if (!this.publications.length && !this.documentation.length) {
content = '<p class="has-text-centered">Nessun documento disponibile.</p>';
}
return content;
} }
async biblio(recordId) { async biblio(recordId) {
let record = await this.fetchData(`${GisState.apiUrl}/not_conserved/${recordId}`); let {citations, biblioElements} = await Utils.buildBibliography('not_conserved', recordId);
this.biblioElements = biblioElements;
let citations = ''; return citations;
if (record.bibliography.length) {
record.bibliography.forEach(record => {
citations += `
<span class="is-clickable has-text-link"
data-action="click->biblio#open"
id="cit-${record.id}">
${record.citation.trim()}</span>`;
citations += record.pages?.length ? `, ${record.pages};` : ';';
this.biblioElements.push(`
<div class="p-2 mt-2" id="ref-${record.id}">
<p class="p-3">${record.reference}</p>
</div>
`
);
});
}
return citations.trim().slice(0, -1);
} }
getReference(id) { getReference(id) {
@@ -169,8 +80,4 @@ export class NotConserved {
return ref.match(regex); return ref.match(regex);
}); });
} }
async fetchData(url) {
return await fetch(url).then(res => res.json());
}
} }

View File

@@ -1,4 +1,4 @@
import { GisState } from "../state.js"; import Utils from "./utils.js";
/** /**
* @class Prehistoric * @class Prehistoric
*/ */
@@ -32,11 +32,11 @@ export class Prehistoric {
<span class="icon has-text-link"> <span class="icon has-text-link">
<i class="fa fa-map"></i> <i class="fa fa-map"></i>
</span> </span>
<strong>Località generica:</strong> ${this._data.genericPlace} <strong>Località generica:</strong> ${Utils.parseMarkers(this._data.genericPlace)}
</p> </p>
<p class="mt-4 pl-2 pr-5"> <p class="mt-4 pl-2 pr-5">
<strong class="pb-3">Descrizione breve</strong></br> <strong class="pb-3">Descrizione breve</strong></br>
${this._data.description} ${Utils.parseMarkers(this._data.description)}
</p> </p>
<p class="p-2"> <p class="p-2">
<strong>Conservazione:</strong> ${this._data.conservation} <strong>Conservazione:</strong> ${this._data.conservation}
@@ -47,7 +47,6 @@ export class Prehistoric {
</div>`; </div>`;
/* /*
<p class="mt-4 pl-2 pr-5"> <p class="mt-4 pl-2 pr-5">
<span class="icon has-text-link"> <span class="icon has-text-link">
<i class="fa fa-book"></i> <i class="fa fa-book"></i>
@@ -58,32 +57,14 @@ export class Prehistoric {
*/ */
} }
renderImages() {
let content = `<div class="content has-text-centered">
<p class="is-size-5 mt-3">Immagini</p>`;
content += `
<div style="max-width: 70%; margin: 0 auto">
<p class="is-size-6 has-text-centered">Cliccare sull'immagine per aprire la gallery</p>
<figure class="is-relative is-clickable has-text-centered" id="prehist-gallery">
<img src="img/${this.images[0].filename}" width="300"/>
<div class="icon overlay is-flex is-justify-content-center is-align-items-center">
<i class="is-flex fa fa-2x fa-play-circle"></i>
</div>
</figure>
</div>
</div>`;
return content;
}
/** /**
* @param {HTMLElement} imageContainer * @param {HTMLElement} imageContainer
* @param {Function} gallery * @param {Function} gallery
*/ */
async setImages(imageContainer, gallery) { async setImages(imageContainer, gallery) {
if (this._data.images.length) { if (this._data.images?.length) {
this.images = this._data.images; imageContainer.innerHTML = Utils.renderImages('prehist-gallery', this._data.images);
imageContainer.innerHTML = this.renderImages(); gallery('prehist-gallery', this._data.images);
gallery('prehist-gallery', this.images);
} else } else
imageContainer.innerHTML = '<p class="has-text-centered">Nessuna risorsa visuale disponibile</p>'; imageContainer.innerHTML = '<p class="has-text-centered">Nessuna risorsa visuale disponibile</p>';
} }
@@ -91,29 +72,10 @@ export class Prehistoric {
* @param {number} recordId * @param {number} recordId
*/ */
async biblio(recordId) { async biblio(recordId) {
let finding = await this.fetchData(`${GisState.apiUrl}/prehistoric/${recordId}`); let {citations, biblioElements} = await Utils.buildBibliography('prehistoric', recordId);
this.biblioElements = biblioElements;
let citations = ''; return citations;
if (finding.bibliography.length) {
finding.bibliography.forEach(record => {
citations += `
<span class="is-clickable has-text-link"
data-action="click->biblio#open"
id="cit-${record.id}">
${record.citation}</span>`;
citations += record.pages?.length ? `, ${record.pages};` : ';';
this.biblioElements.push(`
<div class="p-2 mt-2" id="ref-${record.id}">
<p class="p-3">${record.reference}</p>
</div>
`);
});
}
return citations.trim().slice(0, -1);
} }
getReference(id) { getReference(id) {
@@ -122,8 +84,4 @@ export class Prehistoric {
return ref.match(regex); return ref.match(regex);
}); });
} }
async fetchData(url) {
return await fetch(url).then(res => res.json());
}
} }

View File

@@ -0,0 +1,81 @@
import Utils from "./utils.js";
/**
* @class Reuse
*/
export class Reuse {
biblioElements = [];
images = null;
set data(data) {
this._data = data;
}
async render() {
return `
<div class="container px-4 pt-4">
<p class="p-2">
<strong>Denominazione:</strong> ${this._data.denomination}
</p>
<p class="p-2">
<strong>Materia:</strong> ${this._data.material}
</p>
<p class="p-2">
<strong>Misure:</strong> ${this._data.measurements}
</p>
<p class="p-2">
<strong>Luogo di conservazione:</strong> ${this._data.conservationPlace}
</p>
<p class="p-2">
<strong>Stato di conservazione:</strong> ${this._data.conservationState}
</p>
<p class="p-2">
<strong>Luogo e anno rinvenimento:</strong> ${Utils.parseMarkers(this._data.finding)}
</p>
<p class="p-2">
<strong>Datazione:</strong> ${this._data.dating}
</p>
<p class="mt-4 pl-2 pr-5">
<strong class="pb-3">Descrizione</strong></br>
${Utils.parseMarkers(this._data.description)}
</p>
<p class="mt-4 pl-2 pr-5">
<span class="icon has-text-link">
<i class="fa fa-book"></i>
</span>
<strong>Bibliografia:</strong> ${await this.biblio(this._data.id)}
</p>
<div class="notification is-light mx-3 mt-4 mb-0 p-2 is-hidden" data-biblio-target="biblio"></div>
<p class="p-2 mb-4">
<strong>Autore scheda:</strong> ${this._data.author}
</p>
</div>`;
}
/**
* @param {HTMLElement} imageContainer
* @param {Function} gallery
*/
async setImages(imageContainer, gallery) {
if (this._data.images?.length) {
imageContainer.innerHTML = Utils.renderImages('reuse-gallery', this._data.images);
gallery('reuse-gallery', this._data.images);
} else
imageContainer.innerHTML = '<p class="has-text-centered">Nessuna risorsa visuale disponibile</p>';
}
/**
* @param {Number} recordId
*/
async biblio(recordId) {
let {citations, biblioElements} = await Utils.buildBibliography('reuse', recordId);
this.biblioElements = biblioElements;
return citations;
}
getReference(id) {
return this.biblioElements.find(ref => {
let regex = new RegExp('ref-'+id+'"');
return ref.match(regex);
});
}
}

View File

@@ -1,14 +1,10 @@
import Utils from "./utils.js";
/** /**
* Component to render data for site sheet * Component to render data for site sheet
* @class SiteSheet * @class SiteSheet
*/ */
export class SiteSheet { export class SiteSheet {
biblioElements = []; biblioElements = [];
/*
constructor(data) {
this._siteData = data;
}
*/
/** /**
* @param {object} data * @param {object} data
*/ */
@@ -16,9 +12,13 @@ export class SiteSheet {
this._siteData = data; this._siteData = data;
} }
/** /**
* @todo Refactor!
* @returns {string} HTML * @returns {string} HTML
*/ */
render() { render() {
const description = Utils.parseMarkers(this._siteData.description);
const localization = Utils.parseMarkers(this._siteData.localization);
return `<div class="container has-bottom-border"> return `<div class="container has-bottom-border">
<table class="table is-fullwidth is-striped"> <table class="table is-fullwidth is-striped">
<tr class="is-link"><th class="is-size-5 has-text-centered" colspan=2>Identificazione</th></tr> <tr class="is-link"><th class="is-size-5 has-text-centered" colspan=2>Identificazione</th></tr>
@@ -28,7 +28,7 @@ export class SiteSheet {
<tr><th>Comune</th><td>${this._siteData.municipality}</td></tr> <tr><th>Comune</th><td>${this._siteData.municipality}</td></tr>
<tr><th>Indirizzo</th><td>${this._siteData.address}</td></tr> <tr><th>Indirizzo</th><td>${this._siteData.address}</td></tr>
<tr><th>Località</th><td>${this._siteData.place}</td></tr> <tr><th>Località</th><td>${this._siteData.place}</td></tr>
<tr><th>Localizzazione</th><td>${this._siteData.localization}</td></tr> <tr><th>Localizzazione</th><td>${localization}</td></tr>
<tr class="is-link"><th class="is-size-5 has-text-centered" colspan=2>Cronologia</th></tr> <tr class="is-link"><th class="is-size-5 has-text-centered" colspan=2>Cronologia</th></tr>
<tr><th>Periodo</th><td>${this._siteData.period}</td></tr> <tr><th>Periodo</th><td>${this._siteData.period}</td></tr>
<tr><th>Fase</th><td>${this._siteData.phase}</td></tr> <tr><th>Fase</th><td>${this._siteData.phase}</td></tr>
@@ -38,13 +38,13 @@ export class SiteSheet {
<tr><th>Stato di conservazione</th><td>${this._siteData.conservationState}</td></tr> <tr><th>Stato di conservazione</th><td>${this._siteData.conservationState}</td></tr>
<tr><th>Tecniche edilizie impiegate:</th><td>${this._siteData.techniques}</td></tr> <tr><th>Tecniche edilizie impiegate:</th><td>${this._siteData.techniques}</td></tr>
<tr class="is-link"><th class="is-size-5 has-text-centered" colspan=2>Ritrovamento e materiali</th></tr> <tr class="is-link"><th class="is-size-5 has-text-centered" colspan=2>Ritrovamento e materiali</th></tr>
<tr><th>Anno di ritrovamento</th><td>${this._siteData.finding}</td></tr> <tr><th>Modalità e anno di ritrovamento</th><td>${Utils.parseMarkers(this._siteData.finding)}</td></tr>
<tr><th>Materiali rinvenuti</th><td>${this._siteData.materials}</td></tr> <tr><th>Materiali rinvenuti</th><td>${this._siteData.materials}</td></tr>
<tr><th>Luogo custodia materiali</th><td>${this._siteData.conservationPlace}</td></tr> <tr><th>Luogo custodia materiali</th><td>${Utils.parseMarkers(this._siteData.conservationPlace)}</td></tr>
<tr class="is-link"><th class="is-size-5 has-text-centered" colspan=2>Rilievi</th></tr> <tr class="is-link"><th class="is-size-5 has-text-centered" colspan=2>Rilievi</th></tr>
<tr><td colspan=2>${this._siteData.surveys ?? 'Nessun rilievo'}</td></tr> <tr><td colspan=2>${this._siteData.surveys ?? 'Nessun rilievo'}</td></tr>
<tr class="is-link"><th class="is-size-5 has-text-centered" colspan=2>Descrizione</th></tr> <tr class="is-link"><th class="is-size-5 has-text-centered" colspan=2>Descrizione</th></tr>
<tr><td class="pr-6 pl-6 pt-3" colspan="2">${this._siteData.description}</td></tr> <tr><td class="pr-6 pl-6 pt-3" colspan="2">${description}</td></tr>
<tr class="is-link"><th class="is-size-5 has-text-centered" colspan=2>Bibliografia</th></tr> <tr class="is-link"><th class="is-size-5 has-text-centered" colspan=2>Bibliografia</th></tr>
<tr> <tr>
<td colspan=2> <td colspan=2>
@@ -60,6 +60,8 @@ export class SiteSheet {
} }
renderShort() { renderShort() {
const shortDesc = Utils.parseMarkers(this._siteData.shortDescription);
return ` return `
<div class="container p-3"> <div class="container p-3">
<p class="p-2"> <p class="p-2">
@@ -81,7 +83,7 @@ export class SiteSheet {
<strong>Località generica:</strong> ${this._siteData.genericPlace} <strong>Località generica:</strong> ${this._siteData.genericPlace}
</p> </p>
<p class="mt-4 pl-2 pr-5"> <p class="mt-4 pl-2 pr-5">
${this._siteData.shortDescription} ${shortDesc}
</p> </p>
<p class="mt-4 pl-2 pr-5"> <p class="mt-4 pl-2 pr-5">
<span class="icon has-text-link"> <span class="icon has-text-link">

View File

@@ -1,3 +1,4 @@
import Utils from "./utils.js";
/** /**
* @class Underwater * @class Underwater
*/ */
@@ -27,15 +28,26 @@ export class Underwater {
</p> </p>
<p class="mt-4 pl-2 pr-5"> <p class="mt-4 pl-2 pr-5">
<strong class="pb-3">Descrizione breve</strong></br> <strong class="pb-3">Descrizione breve</strong></br>
${this._data.shortDescription} ${Utils.parseMarkers(this._data.shortDescription)}
</p> </p>
<p class="p-2 mb-4"> <p class="p-2 mb-4">
<strong>Autore scheda:</strong> ${this._data.author} <strong>Autore scheda:</strong> ${this._data.author}
</p> </p>
</div>`; </div>`;
} }
/**
* @param {HTMLElement} imageContainer
* @param {Function} gallery
*/
setImages(imageContainer, gallery) {
if (this._data.images?.length) {
imageContainer.innerHTML = Utils.renderImages('underwater-gallery', this._data.images);
gallery('underwater-gallery', this._data.images);
} else
imageContainer.innerHTML = '<p class="has-text-centered">Nessuna risorsa visuale disponibile</p>';
}
async fetchData(url) { async renderDocs() {
return await fetch(url).then(res => res.json()); return await Utils.generateDocsTable(this._data, 'underwater');
} }
} }

View File

@@ -0,0 +1,175 @@
import { GisState } from "../state.js";
/**
* @namespace Utils
*/
const Utils = {};
/**
*
* @param {string} galleryId The image gallery's id
* @param {Object} imagesData
* @returns {string}
*/
Utils.renderImages = function (galleryId, imagesData) {
let content = `<div class="content has-text-centered">
<p class="is-size-5 mt-3">Immagini</p>`;
content += `
<div style="max-width: 70%; margin: 0 auto">
<p class="is-size-6 has-text-centered">Cliccare sull'immagine per aprire la gallery</p>
<figure class="is-relative is-clickable has-text-centered" id="${galleryId}">
<img src="img/${imagesData[0].filename}" width="300"/>
<div class="icon overlay is-flex is-justify-content-center is-align-items-center">
<i class="is-flex fa fa-2x fa-play-circle"></i>
</div>
</figure>
</div>
</div>`;
return content;
}
/**
* @param {Object} imagesData
* @param {HTMLElement} imageContainer
* @param {Function} galleryGenerator The function that creates the image gallery
* @param {string} galleryId The image gallery's id
*/
Utils.setImages = function(imagesData, imageContainer, galleryGenerator, galleryId) {
imageContainer.innerHTML = Utils.renderImages(galleryId);
galleryGenerator(galleryId, imagesData);
}
/**
*
* @param {Object} data The component's data
* @param {String} resourceUri The resource URI to be used for API calls
* @returns {String} The table HTML
*/
Utils.generateDocsTable = async function(data, resourceUri) {
let record = await Utils.fetchData(`${GisState.apiUrl}/${resourceUri}/${data.id}`);
// TODO Horrible??
if (record instanceof Error) return '<p class="has-text-centered">Nessun documento disponibile.</p>';
const documentation = record.documents.filter(d => d.type === 'documentazione')
const publications = record.documents.filter(d => d.type === 'pubblicazione');
let content = `
<div class="has-bottom-border">
<div class="p-2">
<table class="p-4 table is-fullwidth is-striped">
<thead>
<tr><th colspan=3 class="p-2 has-text-centered is-size-5">Documentazione di archivio</th>
<tr><th>Titolo</th><th>Luogo di conservazione</th><th>Download</th></tr>
</thead>
<tbody>
`;
for (const doc of documentation) {
content += `
<tr><td>${doc.title}</td><td>${doc.conservationPlace}</td><td><a class="button is-link has-text-white" href="docs/${doc.filename}">
<i class="fa fa-download mr-2"></i> PDF
</a></td></tr>
`;
}
if (publications.length) {
content += `
</tbody>
<thead>
<tr><th colspan=3 class="p-2 has-text-centered is-size-5">Pubblicazioni del progetto Carta Archeologica</th>
<tr><th>Titolo</th><th>Autori</th><th>Download</th></tr>
</thead>
<tbody>
`;
for (const doc of publications) {
content += `
<tr><td>${doc.title}</td><td>${doc.authors}</td><td><a class="button is-link has-text-white" href="docs/${doc.filename}">
<i class="fa fa-download mr-2"></i> PDF
</a></td></tr>
`;
}
}
content += `
</tbody>
</table>
</div>
</div>
`;
if (publications.length === 0 && documentation.length === 0) {
content = '<p class="has-text-centered">Nessun documento disponibile.</p>';
}
return content;
}
/**
*
* @param {String} recordUri The record URI used for API calls
* @param {Number} recordId This record's ID
* @returns {{citations:String,biblioElements:String[]}}
*/
Utils.buildBibliography = async function(recordUri, recordId) {
let record = await Utils.fetchData(`${GisState.apiUrl}/${recordUri}/${recordId}`);
let biblioElements = [];
let citations = '';
if (record.bibliography.length) {
record.bibliography.forEach(record => {
citations += `
<span class="is-clickable has-text-link"
data-action="click->biblio#open"
id="cit-${record.id}">
${record.citation.trim()}
</span>
`;
citations += record.pages?.length ? `, ${record.pages};` : ';';
biblioElements.push(`
<div class="p-2 mt-2" id="ref-${record.id}">
<p class="p-3">${record.reference}</p>
</div>
`
);
});
}
const bibliography = {
citations: citations.trim().slice(0, -1),
biblioElements
}
return bibliography;
}
/**
* Parse marker strings (pseudo-shortcodes) and convert them
* to Stimulus links
* @param {String} text - The content text from database
*/
Utils.parseMarkers = function(text) {
const regex = /(?<marker>\[marker coords=\"(?<coords>[\d\s\.]+)\"\ ?(group=\"(?<group>\w+)\")?](?<content>[\s\S]+?)\[\/marker\])/mig;
if (!text) return text;
let matches = [...text.matchAll(regex)];
if (matches.length) {
matches.forEach(match => {
const replacement = `<a class="has-text-link" data-action="marker#go modal#close tabs#reset marker#goAndOpen" data-controller="marker"
data-marker-coords-value="${match.groups.coords}"
data-marker-group-value="${match.groups.group}">${match.groups.content.trim()}</a>`;
text = text.replace(match.groups.marker, replacement.trim());
});
}
return text;
}
Utils.fetchData = async function(url) {
return await fetch(url).then(res => res.ok ? res.json() : new Error())
.catch(err => console.log(err));
}
export default Utils;

View File

@@ -1,5 +1,6 @@
import { Controller } from "@hotwired/stimulus"; import { Controller } from "@hotwired/stimulus";
import { GisState, getMarkerByCoords } from "../state.js"; import { GisState, getMarkerByCoords } from "../state.js";
import UI from "../ui.js";
export default class extends Controller { export default class extends Controller {
static values = { static values = {
@@ -8,17 +9,16 @@ export default class extends Controller {
'id': String, 'id': String,
}; };
END_ZOOM = 19; uiModals = {
// Animation breaks automatic tooltip opening... sites: '#site-data',
mapAnimate = { notConserved: '#not-conser-data',
animate: true, finding: '#finding-data',
duration: 1, prehist: '#prehist-data',
easeLinearity: 0.25 reuse: '#reuse-data',
}; };
/** END_ZOOM = 19;
* @param {Event} event
*/
go() { go() {
let map = GisState.map; let map = GisState.map;
const coords = this.coordsValue.split(' '); const coords = this.coordsValue.split(' ');
@@ -31,8 +31,40 @@ export default class extends Controller {
let marker = this.getMarker(map, coords); let marker = this.getMarker(map, coords);
// DEBUG for sites // DEBUG for sites
if (this.groupValue) marker = getMarkerByCoords(coords, this.groupValue); //if (this.groupValue) marker = getMarkerByCoords(coords, this.groupValue);
marker?.openTooltip(); marker?.openTooltip();
return marker;
}
/**
* Go to a marker location on the map
* and open its modal
*/
goAndOpen() {
const marker = this.go();
const selector = this.uiModals[this.groupValue];
const data = marker.options.data;
switch(this.groupValue) {
case 'sites':
UI.openSiteModal(data, selector);
break;
case 'notConserved':
UI.openNotConserModal(data, selector);
break;
case 'finding':
UI.openFindingModal(data, selector);
break;
case 'prehist':
UI.openPrehistModal(data, selector);
break;
case 'reuse':
UI.openReuseModal(data, selector);
break;
default:
console.log('Cannot open modal...');
break;
}
} }
/** /**
* @param {L.Map} map * @param {L.Map} map

View File

@@ -158,6 +158,7 @@ GIS.addLayerGroups = async function (map) {
this.findings(), this.findings(),
this.prehistoric(), this.prehistoric(),
this.underwater(), this.underwater(),
this.reuse(),
] ]
); );
groups.forEach(group => group.addTo(map)); groups.forEach(group => group.addTo(map));
@@ -199,6 +200,7 @@ GIS.sites = async function () {
// Populate app state for reuse and avoid window.Sites etc. // Populate app state for reuse and avoid window.Sites etc.
// Municipality (Capri, Anacapri) added for reuse in dynamic menu // Municipality (Capri, Anacapri) added for reuse in dynamic menu
marker.options.municipality = record.municipality; marker.options.municipality = record.municipality;
marker.options.data = record;
const markerIndex = `${record.coordinates[0]} ${record.coordinates[1]}`; const markerIndex = `${record.coordinates[0]} ${record.coordinates[1]}`;
GisState.markers.sites[markerIndex] = marker; GisState.markers.sites[markerIndex] = marker;
} }
@@ -229,6 +231,7 @@ GIS.notConserved = async function () {
// Populate app state for reuse and avoid window.Sites etc. // Populate app state for reuse and avoid window.Sites etc.
const markerLabel = `${record.coordinates[0]} ${record.coordinates[1]}`; const markerLabel = `${record.coordinates[0]} ${record.coordinates[1]}`;
marker.options.municipality = record.municipality; marker.options.municipality = record.municipality;
marker.options.data = record;
GisState.markers.notConserved[markerLabel] = marker; GisState.markers.notConserved[markerLabel] = marker;
} }
@@ -261,6 +264,7 @@ GIS.findings = async function () {
findings.addLayer(marker); findings.addLayer(marker);
const markerLabel = `${record.coordinates[0]} ${record.coordinates[1]}`; const markerLabel = `${record.coordinates[0]} ${record.coordinates[1]}`;
marker.options.municipality = record.municipality; marker.options.municipality = record.municipality;
marker.options.data = record;
GisState.markers.findings[markerLabel] = marker; GisState.markers.findings[markerLabel] = marker;
} }
@@ -292,6 +296,7 @@ GIS.prehistoric = async function () {
const markerLabel = `${record.coordinates[0]} ${record.coordinates[1]}`; const markerLabel = `${record.coordinates[0]} ${record.coordinates[1]}`;
marker.options.municipality = record.municipality; marker.options.municipality = record.municipality;
marker.options.data = record;
GisState.markers.prehistoric[markerLabel] = marker; GisState.markers.prehistoric[markerLabel] = marker;
prehistoric.addLayer(marker); prehistoric.addLayer(marker);
@@ -322,7 +327,8 @@ GIS.underwater = async function () {
); );
const markerLabel = `${record.coordinates[0]} ${record.coordinates[1]}`; const markerLabel = `${record.coordinates[0]} ${record.coordinates[1]}`;
GisState.markers.prehistoric[markerLabel] = marker; marker.options.data = record;
GisState.markers.underwater[markerLabel] = marker;
underwater.addLayer(marker); underwater.addLayer(marker);
} }
@@ -331,6 +337,39 @@ GIS.underwater = async function () {
return underwater; return underwater;
} }
/**
* Create reused sites group ('reimpiego')
* @returns {L.MarkerClusterGroup}
*/
GIS.reuse = async function () {
let reuseData = await fetch(`${API_URL}/reuse`)
.then(data => data.json());
let reuse = L.markerClusterGroup(Options.cluster);
for (let record of reuseData.records) {
const marker = L.marker(
record.coordinates,
{icon: Icons.reuse}
).bindTooltip(record.label)
.on(
'click',
() => UI.openReuseModal(record, '#reuse-data')
);
const markerLabel = `${record.coordinates[0]} ${record.coordinates[1]}`;
marker.options.municipality = record.municipality;
marker.options.label = record.label;
marker.options.data = record;
GisState.markers.reuse[markerLabel] = marker;
reuse.addLayer(marker);
}
GisState.layers.reuse = reuse;
return reuse;
}
/** /**
* Adds layers to map and returns an object * Adds layers to map and returns an object
* with {baseMap, archeoLayers, sitesLayerGroup} * with {baseMap, archeoLayers, sitesLayerGroup}
@@ -527,9 +566,9 @@ GIS.featurePopup = function (layerName, feature) {
<tr><th>Anno</th><td>${feature.properties.ANNO}</td></tr> <tr><th>Anno</th><td>${feature.properties.ANNO}</td></tr>
<tr><th>Comune</th><td>${capitalize(feature.properties.COMUNE)}</td></tr> <tr><th>Comune</th><td>${capitalize(feature.properties.COMUNE)}</td></tr>
<tr><th>Località</th><td>${capitalize(feature.properties.LOCALITA) ?? 'n.d.'}</td></tr> <tr><th>Località</th><td>${capitalize(feature.properties.LOCALITA) ?? 'n.d.'}</td></tr>
<tr><th>Proprietà</th><td>${capitalize(feature.properties.PROPRIETA)}</td></tr> <tr><th>Proprietà</th><td>${capitalize(feature.properties.PROPRIETA) ?? 'n.d.'}</td></tr>
<tr><th>Foglio</th><td>${feature.properties.FOGLIO}</td></tr> <tr><th>Foglio</th><td>${feature.properties.FOGLIO}</td></tr>
<tr><th>Particella</th><td>${feature.properties.PART_BIS}</td></tr> <tr><th>Particella</th><td>${feature.properties.PART_BIS ?? 'n.d.'}</td></tr>
</table> </table>
`; `;
const content = { const content = {

View File

@@ -48,6 +48,15 @@ Icons.underwater = L.icon(
} }
); );
Icons.reuse = L.icon(
{
iconUrl: 'img/icons/reimpiego.png',
iconSize: [18, 27],
iconAnchor: [10, 24],
tooltipAnchor: [0, -22],
}
);
Icons.camera = L.divIcon({className: 'fa fa-camera'}); Icons.camera = L.divIcon({className: 'fa fa-camera'});
export default Icons; export default Icons;

View File

@@ -27,6 +27,7 @@ export const GisState = {
findings: {}, findings: {},
prehistoric: {}, prehistoric: {},
underwater: {}, underwater: {},
reuse: {},
}, },
layers: { layers: {
sites: {}, sites: {},
@@ -34,6 +35,7 @@ export const GisState = {
findings: {}, findings: {},
prehistoric: {}, prehistoric: {},
underwater: {}, underwater: {},
reuse: {},
}, },
bibliography: null, bibliography: null,
apiUrl : null, apiUrl : null,

View File

@@ -10,6 +10,7 @@ import { Finding } from './components/Finding.js';
import { Prehistoric } from './components/Prehistoric.js'; import { Prehistoric } from './components/Prehistoric.js';
import { Underwater } from './components/Underwater.js'; import { Underwater } from './components/Underwater.js';
import { GisState } from "./state.js"; import { GisState } from "./state.js";
import { Reuse } from './components/Reuse.js';
/** /**
* @namespace UI * @namespace UI
@@ -211,6 +212,27 @@ UI.openUnderwaterModal = function (data, selector) {
// For Stimulus biblio_controller // For Stimulus biblio_controller
//GisState.bibliography = underwater; //GisState.bibliography = underwater;
underwater.render().then(html => modal.querySelector('#underwater-sheet').innerHTML = html); underwater.render().then(html => modal.querySelector('#underwater-sheet').innerHTML = html);
underwater.renderDocs().then(html => modal.querySelector('#documents').innerHTML = html);
underwater.setImages(modal.querySelector('#photos'), this.imageGallery);
modal.classList.add('is-active');
}
/**
* @todo Biblio?
* @param {object} data The data retrieved from the DB to display as modal content
* @param {string} selector The modal selector
*/
UI.openReuseModal = function (data, selector) {
const modal = document.querySelector(selector);
let reuse = new Reuse();
reuse.data = data;
GisState.bibliography = reuse;
// For Stimulus biblio_controller
//GisState.bibliography = underwater;
reuse.render().then(html => modal.querySelector('#reuse-sheet').innerHTML = html);
reuse.setImages(modal.querySelector('#photos'), this.imageGallery);
modal.classList.add('is-active'); modal.classList.add('is-active');
} }
/** /**
@@ -226,8 +248,9 @@ UI.imageGallery = function (galleryId, items, video = false) {
let gallery = []; let gallery = [];
for (let media of items) { for (let media of items) {
let author = media.author ? ` (${media.author})` : ''; let author = media.author ? ` (${media.author})` : '';
let caption = media.caption ?? '';
let mediaObj = { let mediaObj = {
description: media.caption + author description: caption + author
}; };
if (video) { if (video) {