-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathindex.html
More file actions
150 lines (139 loc) · 7.02 KB
/
index.html
File metadata and controls
150 lines (139 loc) · 7.02 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>BioEcoOcean Metadata Catalogue</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=DM+Sans:ital,opsz,wght@0,9..40,400;0,9..40,600;1,9..40,400&family=Fragment+Mono:ital@0;1&display=swap" rel="stylesheet">
<link rel="stylesheet" href="styles.css">
</head>
<body>
<div class="page-content">
<header class="header">
<div class="container">
<img src="images/bioecoocean_logo.png" style="max-width: 10vw;">
<h1>BioEcoOcean Metadata Catalogue</h1>
<p>Find documents, deliverables, datasets, and publications generated by the European Union-funded project BioEcoOcean.</p>
</div>
</header>
<main class="container">
<div class="search-wrap">
<input type="search" class="search-box" id="search" placeholder="Search by title, description, keywords…" aria-label="Search outputs">
</div>
<p class="count" id="count" aria-live="polite">Loading catalogue…</p>
<div class="list" id="list" role="list"></div>
<div class="no-results" id="no-results" hidden>No outputs match your search.</div>
</main>
<script>
(function () {
const searchEl = document.getElementById('search');
const countEl = document.getElementById('count');
const listEl = document.getElementById('list');
const noResultsEl = document.getElementById('no-results');
let items = [];
function getSearchableText(record) {
const keywordsRaw = Array.isArray(record.keywords) ? record.keywords : [];
const keywordsText = keywordsRaw
.map(k => (typeof k === 'string') ? k : (k && typeof k === 'object' ? (k.name || '') : ''))
.filter(Boolean)
.join(' ');
const parts = [
record.name || '',
record.description || (typeof record.description === 'object' && record.description['@value']) ? (record.description['@value'] || '') : '',
keywordsText,
Array.isArray(record.creator) ? record.creator.map(c => c.name || '').join(' ') : (record.creator && record.creator.name) || ''
];
return parts.join(' ').toLowerCase();
}
function renderRecord(record) {
const name = record.name || 'Untitled';
const url = record.url || record['@id'] || '#';
const identifier = record.identifier || '';
const doi = typeof identifier === 'string' && identifier.includes('doi.org') ? identifier : null;
let desc = record.description;
if (desc && typeof desc === 'object' && desc['@value']) desc = desc['@value'];
if (typeof desc === 'string') {
// Strip simple HTML tags like <p>, <span>, etc.
desc = desc.replace(/<[^>]+>/g, ' ').replace(/\s+/g, ' ').trim();
}
if (desc && typeof desc === 'string' && desc.length > 280) desc = desc.slice(0, 277) + '…';
const creators = Array.isArray(record.creator) ? record.creator : (record.creator ? [record.creator] : []);
const firstAuthor = (creators[0] && creators[0].name) ? creators[0].name : '';
const date = record.datePublished || '';
const year = (typeof date === 'string' && date.length >= 4) ? date.slice(0, 4) : '';
const keywordsRaw = Array.isArray(record.keywords) ? record.keywords : [];
const keywords = keywordsRaw
.map(k => (typeof k === 'string') ? k : (k && typeof k === 'object' ? (k.name || '') : ''))
.filter(Boolean);
const card = document.createElement('article');
card.className = 'card';
card.setAttribute('role', 'listitem');
card.innerHTML =
'<h2 class="card-title"><a href="' + escapeAttr(url) + '" target="_blank" rel="noopener">' + escapeHtml(name) + '</a></h2>' +
(desc ? '<p class="card-desc">' + escapeHtml(desc) + '</p>' : '') +
'<div class="card-meta">' +
(doi ? '<a href="' + escapeAttr(doi) + '" target="_blank" rel="noopener">' + escapeHtml(shortDoi(doi)) + '</a>' : '') +
(firstAuthor ? '<span>' + escapeHtml(firstAuthor) + '</span>' : '') +
(year ? '<span>' + escapeHtml(year) + '</span>' : (date ? '<span>' + escapeHtml(date) + '</span>' : '')) +
'</div>' +
(keywords.length ? '<div class="card-keywords">' + keywords.slice(0, 8).map(k => '<span class="tag">' + escapeHtml(k) + '</span>').join('') + '</div>' : '');
return card;
}
function escapeAttr(s) {
return s.replace(/&/g, '&').replace(/"/g, '"').replace(/</g, '<');
}
function escapeHtml(s) {
return s.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');
}
function shortDoi(url) {
const m = url.match(/doi\.org\/(.+)$/);
return m ? m[1] : url;
}
function filterAndRender() {
const q = (searchEl.value || '').trim().toLowerCase();
const filtered = q
? items.filter(r => getSearchableText(r).includes(q))
: items;
if (filtered.length === items.length) {
countEl.innerHTML = '<strong>' + items.length + '</strong> output' + (items.length !== 1 ? 's' : '');
} else {
countEl.innerHTML = 'Showing <strong>' + filtered.length + '</strong> of <strong>' + items.length + '</strong> outputs';
}
listEl.innerHTML = '';
noResultsEl.hidden = true;
if (filtered.length === 0) {
noResultsEl.hidden = false;
return;
}
filtered.forEach(r => listEl.appendChild(renderRecord(r)));
}
searchEl.addEventListener('input', filterAndRender);
searchEl.addEventListener('keydown', function (e) {
if (e.key === 'Escape') { searchEl.value = ''; filterAndRender(); searchEl.focus(); }
});
fetch('bioecoocean-catalogue.jsonld')
.then(r => { if (!r.ok) throw new Error(r.statusText); return r.json(); })
.then(data => {
items = (data && data['@graph']) ? data['@graph'] : [];
filterAndRender();
})
.catch(err => {
countEl.textContent = '';
listEl.innerHTML = '<p class="error" id="load-err">Could not load catalogue. Ensure <code>bioecoocean-catalogue.jsonld</code> is in the same folder and the page is served over HTTP (e.g. run <code>python -m http.server 8000</code> in this directory).</p>';
console.error(err);
});
})();
</script>
</div>
</body>
<footer>
<div>
<img src="images/eu_funded.jpg" style="max-width:70%">
</div>
<div>
<p>The BioEcoOcean project (Grant Agreement No. 101136748) is funded by the European Union. Views and opinions expressed are however those of the author(s) only and do not necessarily reflect those of the European Union or the European Research Executive Agency (REA). Neither the European Union nor the granting authority can be held responsible for them.</p>
</div>
</footer>
</html>