Files
internetforkids/layouts/shortcodes/law-charts.html
Christian Gick f4b2511333
All checks were successful
Deploy Internet for Kids / Build & Push (push) Successful in 14s
Deploy Internet for Kids / Deploy (push) Successful in 5s
Deploy Internet for Kids / Health Check (push) Successful in 2s
Deploy Internet for Kids / Smoke Tests (push) Successful in 3s
Deploy Internet for Kids / IndexNow Ping (push) Successful in 7s
Deploy Internet for Kids / Promote to Latest (push) Successful in 1s
Deploy Internet for Kids / Rollback (push) Has been skipped
Deploy Internet for Kids / Audit (push) Successful in 2s
feat: self-host all JS/CSS (GDPR) + clicksports map tiles
- Leaflet, TopJSON, Chart.js, Mermaid served from /js/
- Leaflet CSS from /css/leaflet.min.css
- World atlas GeoJSON from /data/countries-110m.json
- Zero external CDN requests (GDPR compliant)
- Map tiles from maps.clicksports.de (fixed missing Noto Sans font)
- Attribution: clicksports maps + OpenMapTiles + OpenStreetMap
- window.load for script initialization (load order fix)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 14:35:17 +03:00

125 lines
5.1 KiB
HTML

{{ $lang := .Page.Language.Lang }}
{{ $countries := hugo.Data.countries }}
<style>
.ifk-charts { margin: 2rem 0; }
.ifk-charts h3 { text-align: center; margin-bottom: 1.5rem; font-size: 1.25rem; }
.ifk-chart-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 2rem; }
.ifk-chart-card {
background: #fafbfc; border: 1px solid #e2e8f0; border-radius: 10px;
padding: 1.25rem; text-align: center;
}
.ifk-chart-card h4 { margin: 0 0 1rem; font-size: 0.95rem; color: #334155; }
.ifk-chart-card canvas { max-height: 280px; }
@media (max-width: 640px) {
.ifk-chart-grid { grid-template-columns: 1fr; }
}
</style>
<div class="ifk-charts">
<h3>{{ if eq $lang "de" }}Analyse der Kinderschutzgesetze{{ else if eq $lang "fr" }}Analyse des lois de protection{{ else }}Child Protection Law Analysis{{ end }}</h3>
<div class="ifk-chart-grid">
<div class="ifk-chart-card">
<h4>{{ if eq $lang "de" }}Gesetzgebungsstatus{{ else if eq $lang "fr" }}Statut législatif{{ else }}Legislative Status{{ end }}</h4>
<canvas id="ifk-chart-status"></canvas>
</div>
<div class="ifk-chart-card">
<h4>{{ if eq $lang "de" }}Mindestalter für soziale Medien{{ else if eq $lang "fr" }}Âge minimum réseaux sociaux{{ else }}Social Media Age Limits{{ end }}</h4>
<canvas id="ifk-chart-age"></canvas>
</div>
<div class="ifk-chart-card" style="grid-column: 1 / -1;">
<h4>{{ if eq $lang "de" }}Zeitachse der Gesetzgebung{{ else if eq $lang "fr" }}Chronologie législative{{ else }}Legislation Timeline{{ end }}</h4>
<canvas id="ifk-chart-timeline"></canvas>
</div>
</div>
</div>
<script src="{{ "js/chart.umd.min.js" | relURL }}"></script>
<script>
window.addEventListener('load', function() {
var LANG = "{{ $lang }}";
var countries = {{ $countries | jsonify }};
var statusLabels = {
en: { enforced: 'Enforced', passed: 'Passed', progress: 'In Progress', guidelines: 'Guidelines' },
de: { enforced: 'In Kraft', passed: 'Verabschiedet', progress: 'In Bearbeitung', guidelines: 'Richtlinien' },
fr: { enforced: 'En vigueur', passed: 'Adopté', progress: 'En cours', guidelines: 'Directives' }
};
var labels = statusLabels[LANG] || statusLabels.en;
/* Pie: status distribution */
var counts = { enforced: 0, passed: 0, progress: 0, guidelines: 0 };
countries.forEach(function(c) { counts[c.status]++; });
new Chart(document.getElementById('ifk-chart-status'), {
type: 'doughnut',
data: {
labels: [labels.enforced, labels.passed, labels.progress, labels.guidelines],
datasets: [{
data: [counts.enforced, counts.passed, counts.progress, counts.guidelines],
backgroundColor: ['#1e40af', '#3b82f6', '#93c5fd', '#dbeafe'],
borderWidth: 2, borderColor: '#fff'
}]
},
options: {
responsive: true,
plugins: {
legend: { position: 'bottom', labels: { padding: 16, usePointStyle: true } }
}
}
});
/* Horizontal bar: age limits */
var sorted = countries.slice().sort(function(a, b) { return b.ageLimitSocial - a.ageLimitSocial; });
var colors = { enforced: '#1e40af', passed: '#3b82f6', progress: '#93c5fd', guidelines: '#dbeafe' };
new Chart(document.getElementById('ifk-chart-age'), {
type: 'bar',
data: {
labels: sorted.map(function(c) { return c.flag + ' ' + (c[LANG] || c.en).name; }),
datasets: [{
label: LANG === 'de' ? 'Mindestalter' : LANG === 'fr' ? 'Âge minimum' : 'Min. Age',
data: sorted.map(function(c) { return c.ageLimitSocial; }),
backgroundColor: sorted.map(function(c) { return colors[c.status]; }),
borderRadius: 4
}]
},
options: {
indexAxis: 'y', responsive: true,
scales: {
x: { min: 0, max: 20, title: { display: true, text: LANG === 'de' ? 'Jahre' : LANG === 'fr' ? 'Ans' : 'Years' } }
},
plugins: { legend: { display: false } }
}
});
/* Bar: timeline by year */
var yearCounts = {};
countries.forEach(function(c) {
if (!yearCounts[c.year]) yearCounts[c.year] = { enforced: 0, passed: 0, progress: 0, guidelines: 0 };
yearCounts[c.year][c.status]++;
});
var years = Object.keys(yearCounts).sort();
new Chart(document.getElementById('ifk-chart-timeline'), {
type: 'bar',
data: {
labels: years,
datasets: [
{ label: labels.enforced, data: years.map(function(y) { return yearCounts[y].enforced; }), backgroundColor: '#1e40af', borderRadius: 4 },
{ label: labels.passed, data: years.map(function(y) { return yearCounts[y].passed; }), backgroundColor: '#3b82f6', borderRadius: 4 },
{ label: labels.progress, data: years.map(function(y) { return yearCounts[y].progress; }), backgroundColor: '#93c5fd', borderRadius: 4 },
{ label: labels.guidelines, data: years.map(function(y) { return yearCounts[y].guidelines; }), backgroundColor: '#dbeafe', borderRadius: 4 }
]
},
options: {
responsive: true,
scales: {
x: { stacked: true }, y: { stacked: true, beginAtZero: true, ticks: { stepSize: 1 } }
},
plugins: { legend: { position: 'bottom', labels: { padding: 16, usePointStyle: true } } }
}
});
});
</script>