All checks were successful
Deploy Internet for Kids / Build & Push (push) Successful in 13s
Deploy Internet for Kids / Deploy (push) Successful in 6s
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 2s
Deploy Internet for Kids / Rollback (push) Has been skipped
Deploy Internet for Kids / Audit (push) Successful in 2s
CSS background bleeds through Leaflet transform3d layer boundaries on retina Macs causing horizontal lines. Move ocean color into SVG layer so it composites with countries. Add backface-visibility: hidden. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
169 lines
8.2 KiB
HTML
169 lines
8.2 KiB
HTML
{{ $lang := .Page.Language.Lang }}
|
|
{{ $countries := hugo.Data.countries }}
|
|
|
|
<link rel="stylesheet" href="{{ "css/leaflet.min.css" | relURL }}" />
|
|
<style>
|
|
.ifk-map-wrap { max-width: 100%; margin: 2rem 0; }
|
|
.ifk-map-wrap h3 { text-align: center; margin-bottom: 0.5rem; font-size: 1.25rem; }
|
|
.ifk-map-subtitle { text-align: center; color: #666; font-size: 0.85rem; margin-bottom: 1rem; }
|
|
#ifk-world-map { width: 100%; height: 420px; border-radius: 8px; background: #fff; -webkit-backface-visibility: hidden; backface-visibility: hidden; }
|
|
#ifk-world-map .leaflet-map-pane, #ifk-world-map .leaflet-overlay-pane { -webkit-backface-visibility: hidden; backface-visibility: hidden; }
|
|
.ifk-map-legend {
|
|
display: flex; flex-wrap: wrap; gap: 1rem; justify-content: center;
|
|
margin-top: 1rem; font-size: 0.8rem;
|
|
}
|
|
.ifk-map-legend-item { display: flex; align-items: center; gap: 0.35rem; }
|
|
.ifk-map-legend-swatch { width: 14px; height: 14px; border-radius: 3px; border: 1px solid rgba(0,0,0,0.1); }
|
|
.ifk-popup-status { display: inline-block; padding: 0.15rem 0.5rem; border-radius: 4px; font-weight: 600; font-size: 0.8rem; color: white; margin: 0.3rem 0; }
|
|
.ifk-popup-age { display: flex; align-items: center; gap: 0.35rem; margin: 0.3rem 0; font-size: 0.8rem; color: #555; }
|
|
.ifk-popup-detail { margin: 0.4rem 0; font-size: 0.82rem; color: #444; }
|
|
.ifk-popup-link { display: inline-block; margin-top: 0.4rem; font-size: 0.8rem; color: #667eea; text-decoration: none; font-weight: 600; }
|
|
.ifk-popup-link:hover { text-decoration: underline; }
|
|
@media (max-width: 640px) { #ifk-world-map { height: 280px; } }
|
|
</style>
|
|
|
|
<div class="ifk-map-wrap">
|
|
<h3>{{ if eq $lang "de" }}Interaktive Weltkarte: Kinderschutzgesetze{{ else if eq $lang "fr" }}Carte interactive : lois de protection de l'enfance{{ else }}Interactive Map: Child Protection Laws{{ end }}</h3>
|
|
<p class="ifk-map-subtitle">{{ if eq $lang "de" }}Klicken Sie auf ein Land für Details{{ else if eq $lang "fr" }}Cliquez sur un pays pour plus de détails{{ else }}Click a country for details{{ end }}</p>
|
|
<div id="ifk-world-map"></div>
|
|
<div class="ifk-map-legend">
|
|
<div class="ifk-map-legend-item"><div class="ifk-map-legend-swatch" style="background:#667eea"></div>{{ if eq $lang "de" }}In Kraft{{ else if eq $lang "fr" }}En vigueur{{ else }}Enforced{{ end }}</div>
|
|
<div class="ifk-map-legend-item"><div class="ifk-map-legend-swatch" style="background:#764ba2"></div>{{ if eq $lang "de" }}Verabschiedet{{ else if eq $lang "fr" }}Adopté{{ else }}Passed{{ end }}</div>
|
|
<div class="ifk-map-legend-item"><div class="ifk-map-legend-swatch" style="background:#a5b4fc"></div>{{ if eq $lang "de" }}In Bearbeitung{{ else if eq $lang "fr" }}En cours{{ else }}In Progress{{ end }}</div>
|
|
<div class="ifk-map-legend-item"><div class="ifk-map-legend-swatch" style="background:#e0e7ff"></div>{{ if eq $lang "de" }}Richtlinien{{ else if eq $lang "fr" }}Directives{{ else }}Guidelines{{ end }}</div>
|
|
<div class="ifk-map-legend-item"><div class="ifk-map-legend-swatch" style="background:#e2e8f0"></div>{{ if eq $lang "de" }}Keine Daten{{ else if eq $lang "fr" }}Pas de données{{ else }}No Data{{ end }}</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script src="{{ "js/leaflet.min.js" | relURL }}"></script>
|
|
<script src="{{ "js/topojson-client.min.js" | relURL }}"></script>
|
|
<script>
|
|
(function() {
|
|
var LANG = "{{ $lang }}";
|
|
var STATUS_COLORS = { enforced: '#667eea', passed: '#764ba2', progress: '#a5b4fc', guidelines: '#e0e7ff' };
|
|
var STATUS_LABELS = {
|
|
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 AGE_LABELS = { en: 'Min. social media age', de: 'Min. Social-Media-Alter', fr: 'Âge min. réseaux sociaux' };
|
|
var ARTICLE_ANCHORS = {
|
|
AUS: 'australia', DEU: 'germany', USA: 'the-united-states',
|
|
FRA: 'france', BRA: 'brazil'
|
|
};
|
|
if (LANG === 'de') {
|
|
ARTICLE_ANCHORS.AUS = 'australien';
|
|
ARTICLE_ANCHORS.USA = 'die-vereinigten-staaten';
|
|
ARTICLE_ANCHORS.DEU = 'deutschland';
|
|
ARTICLE_ANCHORS.BRA = 'brasilien';
|
|
}
|
|
|
|
var countries = {{ $countries | jsonify | safeJS }};
|
|
var byIsoNum = {};
|
|
countries.forEach(function(c) { byIsoNum[c.isoNum] = c; });
|
|
|
|
var map = L.map('ifk-world-map', {
|
|
center: [20, 20],
|
|
zoom: 2,
|
|
minZoom: 2,
|
|
maxZoom: 6,
|
|
scrollWheelZoom: false,
|
|
attributionControl: false,
|
|
zoomControl: false
|
|
});
|
|
|
|
L.control.zoom({ position: 'topright' }).addTo(map);
|
|
|
|
// Enable scroll zoom in fullscreen
|
|
document.addEventListener('fullscreenchange', function() {
|
|
if (document.fullscreenElement) map.scrollWheelZoom.enable();
|
|
else map.scrollWheelZoom.disable();
|
|
});
|
|
|
|
// Fullscreen button
|
|
var FsControl = L.Control.extend({
|
|
options: { position: 'topright' },
|
|
onAdd: function() {
|
|
var btn = L.DomUtil.create('div', 'leaflet-bar');
|
|
var a = L.DomUtil.create('a', '', btn);
|
|
a.href = '#'; a.title = 'Fullscreen'; a.setAttribute('role', 'button');
|
|
a.innerHTML = '<svg width="14" height="14" viewBox="0 0 14 14"><path d="M2 9H0v5h5v-2H2V9zm0-4h3V3H2V0H0v5zm10 7H9v2h5V9h-2v3zM9 0v2h3v3h2V0H9z" fill="currentColor"/></svg>';
|
|
a.style.cssText = 'display:flex;align-items:center;justify-content:center;width:30px;height:30px;';
|
|
L.DomEvent.on(a, 'click', function(e) {
|
|
L.DomEvent.stop(e);
|
|
var el = document.getElementById('ifk-world-map');
|
|
if (!document.fullscreenElement) el.requestFullscreen();
|
|
else document.exitFullscreen();
|
|
});
|
|
return btn;
|
|
}
|
|
});
|
|
new FsControl().addTo(map);
|
|
|
|
var hoveredLayer = null;
|
|
|
|
function resetStyle(layer) {
|
|
layer.setStyle({ weight: 0.5, color: '#94a3b8' });
|
|
}
|
|
|
|
function highlightStyle(layer) {
|
|
layer.setStyle({ weight: 2.5, color: '#667eea' });
|
|
if (!L.Browser.ie && !L.Browser.opera && !L.Browser.edge) layer.bringToFront();
|
|
}
|
|
|
|
// Ocean as SVG rectangle — same compositing layer as countries, prevents GPU seams
|
|
L.rectangle([[-90, -180], [90, 180]], {
|
|
fillColor: '#c8d3df', fillOpacity: 1, stroke: false, interactive: false
|
|
}).addTo(map);
|
|
|
|
fetch('{{ "data/countries-50m.json" | relURL }}')
|
|
.then(function(r) { return r.json(); })
|
|
.then(function(topo) {
|
|
var geo = topojson.feature(topo, topo.objects.countries);
|
|
|
|
L.geoJSON(geo, {
|
|
style: function(feature) {
|
|
var id = String(feature.id).padStart(3, '0');
|
|
var c = byIsoNum[id];
|
|
return {
|
|
fillColor: c ? STATUS_COLORS[c.status] : '#ffffff',
|
|
fillOpacity: c ? 0.65 : 1,
|
|
weight: 0.5,
|
|
color: '#94a3b8'
|
|
};
|
|
},
|
|
onEachFeature: function(feature, layer) {
|
|
var id = String(feature.id).padStart(3, '0');
|
|
var c = byIsoNum[id];
|
|
if (!c) return;
|
|
|
|
layer.on('mouseover', function() {
|
|
highlightStyle(layer);
|
|
hoveredLayer = layer;
|
|
});
|
|
layer.on('mouseout', function() {
|
|
resetStyle(layer);
|
|
hoveredLayer = null;
|
|
});
|
|
layer.on('click', function(e) {
|
|
var loc = c[LANG] || c.en;
|
|
var labels = STATUS_LABELS[LANG] || STATUS_LABELS.en;
|
|
var ageLabel = AGE_LABELS[LANG] || AGE_LABELS.en;
|
|
var html = '<strong>' + c.flag + ' ' + loc.name + '</strong><br>' +
|
|
'<em>' + loc.law + '</em><br>' +
|
|
'<span class="ifk-popup-status" style="background:' + STATUS_COLORS[c.status] + '">' + labels[c.status] + ' (' + c.year + ')</span>' +
|
|
'<div class="ifk-popup-age">👤 ' + ageLabel + ': <strong>' + c.ageLimitSocial + '+</strong></div>' +
|
|
'<div class="ifk-popup-detail">' + loc.detail + '</div>';
|
|
var anchor = ARTICLE_ANCHORS[c.iso3];
|
|
if (anchor) {
|
|
var readMore = { en: 'Read more ↓', de: 'Weiterlesen ↓', fr: 'En savoir plus ↓' };
|
|
html += '<a class="ifk-popup-link" href="#' + anchor + '">' + (readMore[LANG] || readMore.en) + '</a>';
|
|
}
|
|
L.popup({ maxWidth: 280 }).setLatLng(e.latlng).setContent(html).openOn(map);
|
|
});
|
|
}
|
|
}).addTo(map);
|
|
});
|
|
})();
|
|
</script>
|