// ==UserScript==
// @name wahlrecht.de Time Series
// @namespace saf12490ßioj
// @match https://www.wahlrecht.de/umfragen/*.htm
// @match https://www.wahlrecht.de/umfragen/*/*.htm
// @exclude https://www.wahlrecht.de/umfragen/index.htm
// @grant none
// @version 0.74
// @author tdhg
// @description Adds a table of checkboxes for summing party data with average color blending for summed results.
// ==/UserScript==
window.addEventListener('load', function () {
var script = document.createElement('script');
script.src = 'https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.9.1/chart.min.js';
document.head.appendChild(script);
script.onload = function () {
let table = document.querySelector('.wilko');
if (!table) return;
let headers = table.querySelectorAll('thead th.part');
let rows = table.querySelectorAll('tbody tr');
let partyNames = [];
let dataByParty = {};
let dates = [];
const partyColors = {
"CDU/CSU": "#000000", // Black
"CDU": "#000000", // Black
"CSU": "#000000", // Black
"SPD": "#ff0000", // Red
"GRÜNE": "#008000", // Green
"FDP": "#ffff00", // Yellow
"LINKE": "#800080", // Purple
"AfD": "#0000ff", // Blue
"FW": "#ffa500", // Orange
"PIRATEN": "#ffa500", // Orange
"BSW": "#d11754", //
"Sonstige": "#a52a2a" // Brown
};
headers.forEach((header, index) => {
//if (index > 1 && index < 11) { // CDU/CSU to BSW
let partyName = header.textContent.trim();
partyNames.push(partyName);
dataByParty[partyName] = [];
//}
});
rows.forEach(row => {
let cells = row.querySelectorAll('td');
let date = cells[0].textContent.trim();
if (cells[1]) {
dates.push(date);
partyNames.forEach((party, index) => {
let voteCell = cells[index + 2].textContent.trim();
let voteValue = voteCell.includes('–') ? 0 : parseFloat(voteCell.replace('%', '').replace(',', '.'));
dataByParty[party].push(voteValue);
});
}
});
dates.reverse();
function rgbToHex(rgb) {
let [r, g, b] = rgb;
return `#${Math.round(r).toString(16).padStart(2, '0')}${Math.round(g).toString(16).padStart(2, '0')}${Math.round(b).toString(16).padStart(2, '0')}`;
}
function averageColors(colors) {
let totalR = 0, totalG = 0, totalB = 0;
colors.forEach(color => {
let [r, g, b] = color.match(/\w\w/g).map(x => parseInt(x, 16));
totalR += r;
totalG += g;
totalB += b;
});
let count = colors.length;
return rgbToHex([totalR / count, totalG / count, totalB / count]);
}
function createChart(datasets) {
chartContainer.parentNode.insertBefore(ctx, chartContainer);
window.chart = new Chart(ctx, {
type: 'line',
data: {
labels: dates,
datasets: datasets
},
options: {
responsive: true,
plugins: {
legend: {
position: 'top',
},
title: {
display: true,
text: 'Wahlrecht.de Umfragen - Zeitreihe'
}
}, interaction: {
mode: 'nearest',
axis: 'x',
intersect: false
},
scales: {
x: {
display: true,
title: {
display: true,
text: 'Datum'
}
},
y: {
min: 0,
max: 100,
stacked: true,
title: {
display: true,
text: 'Prozent'
}
}
}
}
});
}
function updateChart() {
let datasets = [];
let selectedParties = new Set();
let partieGroups = new Set();
// Determine which parties are selected
partyNames.forEach(party => {
let checkboxes = document.querySelectorAll(`input[data-party="${party}"]`);
let selectedPartiesForRow = Array.from(checkboxes)
.filter(cb => cb.checked)
.map(cb => cb.value);
let combinedData = new Array(dates.length).fill(0);
let colors = [];
selectedPartiesForRow.forEach(party => {
dataByParty[party].forEach((value, index) => {
combinedData[index] += value;
});
colors.push(partyColors[party] ? partyColors[party] : '#000000');
});
if (selectedPartiesForRow.length > 0) {
let blendedColor = averageColors(colors);
let partiesArray = Array.from(selectedPartiesForRow);
datasets.push({
label: `${partiesArray.join(', ')}`,
data: combinedData.reverse(),
fill: true,
backgroundColor: blendedColor,
borderColor: blendedColor,
tension: 0.1
});
}
});
if (window.chart) {
window.chart.destroy();
}
createChart(datasets);
}
// Create checkbox table
let ctx = document.createElement('canvas');
let tableContainer = document.createElement('div');
let checkboxTable = document.createElement('table');
let thead = document.createElement('thead');
let tbody = document.createElement('tbody');
// Header row
let headerRow = document.createElement('tr');
let headerCell = document.createElement('th');
headerCell.textContent = 'Partys';
headerRow.appendChild(headerCell);
partyNames.forEach(party => {
let th = document.createElement('th');
th.textContent = party;
th.style.width = '4em';
headerRow.appendChild(th);
});
thead.appendChild(headerRow);
// Data rows
partyNames.forEach(party => {
let row = document.createElement('tr');
let labelCell = document.createElement('td');
labelCell.textContent = party;
row.appendChild(labelCell);
partyNames.forEach(otherParty => {
let cell = document.createElement('td');
let checkbox = document.createElement('input');
checkbox.type = 'checkbox';
checkbox.id = `${party}-${otherParty}`;
checkbox.value = otherParty;
checkbox.dataset.party = party;
if (party === otherParty) {
checkbox.checked = true; // Default selection for individual party
}
checkbox.addEventListener('change', updateChart);
cell.appendChild(checkbox);
row.appendChild(cell);
});
tbody.appendChild(row);
});
checkboxTable.appendChild(thead);
checkboxTable.appendChild(tbody);
tableContainer.appendChild(checkboxTable);
let chartContainer = document.createElement('div');
chartContainer.style.marginTop = '20px';
table.parentNode.insertBefore(tableContainer, table);
table.parentNode.insertBefore(chartContainer, table);
// Initial chart render
updateChart();
};
}, false);