PageSourceSearch

https://www.delingsdorf.de/js/alterspyramide.js?v=1

js delingsdorf.de collected 2026-09-25 10:11:33 UTC 5,126 bytes, 113 lines download raw bytes

1/*
2 * alterspyramide.js — zeichnet die einseitige Alterspyramide für den
3 * CMS-Tag [ALTERSPYRAMIDE]. First-party, kein externes Lib, kein Consent.
4 *
5 * Erwartet je .alterspyramide-Element ein <script type="application/json"
6 * class="alterspyramide-data"> mit { groups: [...], years: [{jahr, values:[...]}] }.
7 * Zeichnet je Altersgruppe einen zentrierten horizontalen Balken (jüngste unten
8 * → älteste oben). Ein Slider blättert durch die Jahre; die Skala (globales
9 * Maximum über alle Jahre) bleibt konstant, sodass das Wachstum sichtbar wird.
10 */
11(function () {
12    'use strict';
13
14    function esc(s) {
15        return String(s).replace(/[&<>"]/g, function (c) {
16            return { '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;' }[c];
17        });
18    }
19
20    function draw(svgEl, groups, values, maxVal, jahr) {
21        var W = 600, rowH = 34, padTop = 10, padBottom = 10;
22        var centerX = W / 2, maxBarW = W * 0.42, labelPad = 8;
23        var n = groups.length;
24        var H = padTop + padBottom + n * rowH;
25
26        var s = '<svg viewBox="0 0 ' + W + ' ' + H + '" preserveAspectRatio="xMidYMid meet" width="100%" xmlns="http://www.w3.org/2000/svg" font-family="inherit">';
27        // Mittelachse
28        s += '<line x1="' + centerX + '" y1="' + padTop + '" x2="' + centerX + '" y2="' + (H - padBottom) + '" stroke="#d5d5d5" stroke-width="1"/>';
29
30        for (var i = 0; i < n; i++) {
31            var val = +values[i] || 0;                       // groups[0] = jüngste
32            var barW = maxVal > 0 ? (val / maxVal) * maxBarW : 0;
33            var yc = H - padBottom - (i + 0.5) * rowH;       // i=0 unten, älteste oben
34            var barH = rowH * 0.60;
35            var x = centerX - barW / 2;
36            var y = yc - barH / 2;
37
38            s += '<rect x="' + x.toFixed(1) + '" y="' + y.toFixed(1) + '" width="' + barW.toFixed(1) + '" height="' + barH.toFixed(1) + '" rx="2" fill="var(--color-primary, #047c3c)">';
39            s += '<title>' + esc(groups[i]) + ' (' + jahr + '): ' + val + '</title></rect>';
40            // Altersgruppe links, Wert rechts
41            s += '<text x="' + (centerX - maxBarW - labelPad) + '" y="' + (yc + 4) + '" text-anchor="end" font-size="12" fill="#444">' + esc(groups[i]) + '</text>';
42            s += '<text x="' + (centerX + maxBarW + labelPad) + '" y="' + (yc + 4) + '" text-anchor="start" font-size="12" fill="#444">' + val + '</text>';
43        }
44        s += '</svg>';
45        svgEl.innerHTML = s;
46    }
47
48    function init(el) {
49        if (el.__apInit) { return; }
50        el.__apInit = true;
51
52        var dataEl = el.querySelector('.alterspyramide-data');
53        if (!dataEl) { return; }
54        var data;
55        try { data = JSON.parse(dataEl.textContent); } catch (e) { return; }
56        var groups = data.groups || [], years = data.years || [];
57        if (!years.length || !groups.length) { return; }
58
59        var svgEl = el.querySelector('.alterspyramide-svg');
60        var slider = el.querySelector('.alterspyramide-slider');
61        var yearLbl = el.querySelector('.alterspyramide-year');
62        var playBtn = el.querySelector('.alterspyramide-play');
63        if (!svgEl) { return; }
64
65        // Globales Maximum über alle Jahre/Gruppen → konstante Skala (Wachstum sichtbar)
66        var maxVal = 0;
67        years.forEach(function (yr) {
68            (yr.values || []).forEach(function (v) { if (+v > maxVal) { maxVal = +v; } });
69        });
70
71        function render(idx) {
72            idx = Math.max(0, Math.min(years.length - 1, idx | 0));
73            var yr = years[idx];
74            draw(svgEl, groups, yr.values || [], maxVal, yr.jahr);
75            if (yearLbl) { yearLbl.textContent = yr.jahr; }
76            if (svgEl) { svgEl.setAttribute('aria-label', 'Alterspyramide ' + yr.jahr); }
77            if (slider && +slider.value !== idx) { slider.value = idx; }
78        }
79
80        var timer = null;
81        function stop() {
82            if (timer) { clearInterval(timer); timer = null; if (playBtn) { playBtn.textContent = '▶'; playBtn.setAttribute('aria-label', 'Jahre abspielen'); } }
83        }
84        function play() {
85            if (timer) { stop(); return; }
86            if (playBtn) { playBtn.textContent = '⏸'; playBtn.setAttribute('aria-label', 'Pause'); }
87            var idx = +slider.value;
88            if (idx >= years.length - 1) { idx = 0; }
89            render(idx);
90            timer = setInterval(function () {
91                idx++;
92                if (idx >= years.length) { stop(); return; }
93                render(idx);
94            }, 750);
95        }
96
97        if (slider) { slider.addEventListener('input', function () { stop(); render(+slider.value); }); }
98        if (playBtn) { playBtn.addEventListener('click', play); }
99
100        render(years.length - 1);   // Start: aktuellstes Jahr
101    }
102
103    function initAll() {
104        var els = document.querySelectorAll('.alterspyramide');
105        for (var i = 0; i < els.length; i++) { init(els[i]); }
106    }
107
108    if (document.readyState === 'loading') {
109        document.addEventListener('DOMContentLoaded', initAll);
110    } else {
111        initAll();
112    }
113})();

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.