Files
Fake-Social-MediaWriter/0ld/mtdakkomalinkmatch.html.v0
2025-09-07 08:14:54 +02:00

441 lines
13 KiB
Plaintext

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Alcea ↔ Akkoma Match Viewer</title>
<style>
body {
font-family: 'Segoe UI', Tahoma, sans-serif;
padding: 20px;
max-width: 1200px;
margin: 0 auto;
background-color: #f8f9fa;
color: #333;
}
h1 {
color: #2c3e50;
border-bottom: 2px solid #3498db;
padding-bottom: 10px;
}
.stats {
font-weight: bold;
margin: 20px 0;
padding: 15px;
background: #e8f4f8;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.stat-item {
display: inline-block;
margin: 0 15px;
cursor: pointer;
padding: 8px 12px;
border-radius: 4px;
transition: background-color 0.2s;
}
.stat-item:hover {
background-color: #d1e7f5;
}
.stat-count {
color: #2980b9;
font-weight: bold;
font-size: 1.2em;
}
.match {
background: white;
padding: 16px;
margin: 16px 0;
border-left: 5px solid #2ecc71;
border-radius: 4px;
box-shadow: 0 2px 5px rgba(0,0,0,0.05);
}
.url {
font-family: monospace;
font-size: 0.9em;
color: #7f8c8d;
margin: 8px 0;
}
.preview {
font-style: italic;
color: #555;
margin: 10px 0;
padding: 10px;
background-color: #f9f9f9;
border-left: 3px solid #bdc3c7;
}
pre {
background: #f5f5f5;
padding: 12px;
border: 1px solid #ddd;
overflow-x: auto;
border-radius: 4px;
font-size: 0.95em;
}
.match-type {
display: inline-block;
padding: 4px 10px;
border-radius: 4px;
color: white;
font-size: 0.8em;
margin-left: 10px;
background: #7f8c8d;
}
.match-type.raw { background: #27ae60; }
.match-type.normalized { background: #2ecc71; }
.match-type.noBreaks { background: #3498db; }
.match-type.loose { background: #9b59b6; }
.match-type.finalNormalized { background: #e67e22; }
.filter-active {
background-color: #d1e7f5;
box-shadow: 0 0 0 2px #3498db inset;
}
.no-matches {
text-align: center;
padding: 30px;
color: #7f8c8d;
font-style: italic;
}
.loading {
text-align: center;
padding: 30px;
color: #7f8c8d;
}
.error {
color: #c0392b;
background: #fadbd8;
padding: 15px;
border-radius: 5px;
margin: 20px 0;
}
a {
color: #2980b9;
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
.preview-link {
cursor: pointer;
color: #2980b9;
text-decoration: none;
}
.preview-link:hover {
text-decoration: underline;
}
.refresh-button {
background-color: #3498db;
color: white;
border: none;
padding: 8px 16px;
border-radius: 4px;
cursor: pointer;
margin-left: 10px;
font-size: 0.9em;
}
.refresh-button:hover {
background-color: #2980b9;
}
</style>
</head>
<body>
<h1>Compare Alcea with Akkoma</h1>
<div class="stats" id="stats">Loading data...</div>
<div id="matches"></div>
<script>
// Store the original data and matches for filtering
let allData = {
alceaEntries: [],
akkomaEntries: [],
matches: []
};
// Get filter parameters from URL
const urlParams = new URLSearchParams(window.location.search);
const filterType = urlParams.get('filter');
const filterValue = urlParams.get('value');
// Generate cache-busting timestamp
function getCacheBustingTimestamp() {
return new Date().getTime();
}
function normalizeLineBreaks(str) {
return str.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
}
function normalizeText(str) {
return normalizeLineBreaks(str).normalize('NFC').trim();
}
function removeLineBreaks(str) {
return normalizeText(str).replace(/\n/g, '');
}
function normalizeTextCompletely(str) {
return str
.replace(/\r\n|\r/g, '\n')
.replace(/\s+/g, ' ')
.replace(/^\s+|\s+$/g, '')
.normalize('NFC');
}
async function fetchJson(url) {
// Add cache-busting parameter to URL
const cacheBuster = getCacheBustingTimestamp();
const urlWithCacheBusting = url.includes('?')
? `${url}&_=${cacheBuster}`
: `${url}?_=${cacheBuster}`;
const res = await fetch(urlWithCacheBusting);
if (!res.ok) throw new Error(`HTTP ${res.status} fetching ${url}`);
return await res.json();
}
// Extract all entries from the nested Alcea structure
function extractAlceaEntries(alceaData) {
const entries = [];
// Handle both array and object structures
if (Array.isArray(alceaData)) {
// If it's an array, process each item
alceaData.forEach(item => {
if (typeof item === 'object' && item !== null) {
// Extract all values from date keys
Object.values(item).forEach(entry => {
if (entry && typeof entry.value === 'string' && entry.value.includes('#acws')) {
entries.push(entry);
}
});
}
});
} else if (typeof alceaData === 'object' && alceaData !== null) {
// If it's an object, extract all values
Object.values(alceaData).forEach(entry => {
if (entry && typeof entry.value === 'string' && entry.value.includes('#acws')) {
entries.push(entry);
}
});
}
return entries;
}
function filterMatches() {
if (!filterType || !filterValue) return allData.matches;
switch(filterType) {
case 'alcea':
return allData.matches.filter(match =>
match.value.includes(filterValue) ||
match.value === filterValue
);
case 'akkoma':
return allData.matches.filter(match =>
match.preview.includes(filterValue) ||
match.url === filterValue
);
case 'type':
return allData.matches.filter(match => match.type === filterValue);
default:
return allData.matches;
}
}
function updateStats() {
const stats = document.getElementById('stats');
const filteredMatches = filterMatches();
stats.innerHTML = `
<span class="stat-item" id="alcea-stat">
Alcea entries with #acws: <span class="stat-count">${allData.alceaEntries.length}</span>
</span> |
<span class="stat-item" id="akkoma-stat">
Akkoma entries: <span class="stat-count">${allData.akkomaEntries.length}</span>
</span> |
<span class="stat-item" id="matches-stat">
Matches found: <span class="stat-count">${filteredMatches.length}</span>
</span>
${filterType ? `<br><div style="margin-top:10px;font-weight:normal;">Active filter: ${filterType} = ${filterValue} | <a href="${window.location.pathname}">Clear filter</a></div>` : ''}
<br>
<button class="refresh-button" id="refresh-data">Refresh Data</button>
`;
// Add click handlers to stats
document.getElementById('alcea-stat').addEventListener('click', () => {
window.location.href = `${window.location.pathname}?filter=alcea&value=all`;
});
document.getElementById('akkoma-stat').addEventListener('click', () => {
window.location.href = `${window.location.pathname}?filter=akkoma&value=all`;
});
document.getElementById('matches-stat').addEventListener('click', () => {
window.location.href = `${window.location.pathname}?filter=matches&value=all`;
});
// Add click handler to refresh button
document.getElementById('refresh-data').addEventListener('click', () => {
loadData();
});
// Highlight active filter
if (filterType) {
const activeElement = document.getElementById(`${filterType}-stat`);
if (activeElement) activeElement.classList.add('filter-active');
}
}
function displayMatches() {
const matchesDiv = document.getElementById('matches');
const filteredMatches = filterMatches();
if (filteredMatches.length === 0) {
matchesDiv.innerHTML = '<div class="no-matches">No matches found with the current filter.</div>';
return;
}
matchesDiv.innerHTML = '';
filteredMatches.forEach(({ type, preview, url, value }) => {
const div = document.createElement('div');
div.className = 'match';
div.innerHTML = `
<div>
<strong>Match type:</strong> ${type}
<span class="match-type ${type}">${type}</span>
</div>
<div class="url"><strong>Akkoma URL:</strong> <a href="${url}" target="_blank">${url}</a></div>
<div class="preview"><strong>Preview:</strong> <span class="preview-link" data-preview="${encodeURIComponent(preview)}">${preview}</span></div>
<div><strong>Alcea value snippet:</strong>
<pre>${value.slice(0, 500)}${value.length > 500 ? '...' : ''}</pre>
</div>
<div style="margin-top: 10px; font-size: 0.9em;">
<a href="?filter=alcea&value=${encodeURIComponent(value)}">Filter by this Alcea entry</a> |
<a href="?filter=akkoma&value=${encodeURIComponent(url)}">Filter by this Akkoma entry</a> |
<a href="?filter=type&value=${type}">Filter by match type (${type})</a>
</div>
`;
matchesDiv.appendChild(div);
});
const previewLinks = document.querySelectorAll('.preview-link');
previewLinks.forEach(link => {
link.addEventListener('click', function() {
// Get the value of the Alcea entry (instead of previewText)
//const value = this.closest('.match').querySelector('pre').textContent.trim();
const value = this.closest('.match').querySelector('pre').innerHTML.trim(); // Using innerHTML here
// Replace newline characters with CRLF
const formattedText = value.replace(/\n/g, '\r\n');
// Slice to the first 40 characters
const slicedValue = formattedText.slice(0, 40);
// Encode the sliced and formatted value for the URL
const commentLoadUrl = `https://alceawis.de/other/extra/scripts/fakesocialmedia/commentload.html?number=10000&text=${encodeURIComponent(slicedValue)}`;
// Open the comment load URL in a new window
window.open(commentLoadUrl, '_blank');
});
});
}
async function loadData() {
const stats = document.getElementById('stats');
const matchesDiv = document.getElementById('matches');
stats.innerHTML = 'Loading data...';
matchesDiv.innerHTML = '<div class="loading">Loading data...</div>';
try {
const [alceaJson, akkomaJson] = await Promise.all([
fetchJson('/other/extra/scripts/fakesocialmedia/data_alcea.json'),
fetchJson('/other/extra/scripts/fakesocialmedia/0ld/data_akkoma.json')
]);
// Extract Alcea entries from the nested structure
allData.alceaEntries = extractAlceaEntries(alceaJson);
// Akkoma: extract previews
allData.akkomaEntries = Array.isArray(akkomaJson) ? akkomaJson : Object.values(akkomaJson);
allData.matches = [];
for (const alcea of allData.alceaEntries) {
const value = alcea.value;
const rawValue = value;
const normValue = normalizeText(value);
const noBreaksValue = removeLineBreaks(value);
const normValueCompletely = normalizeTextCompletely(value);
let matched = false;
for (const akkoma of allData.akkomaEntries) {
if (!akkoma.preview) continue;
const rawPreview = akkoma.preview;
const normPreview = normalizeText(rawPreview);
const noBreaksPreview = removeLineBreaks(rawPreview);
const normPreviewCompletely = normalizeTextCompletely(rawPreview);
const url = akkoma.url;
if (rawValue.startsWith(rawPreview)) {
allData.matches.push({ type: 'raw', preview: rawPreview, url, value });
matched = true;
break;
}
if (normValue.startsWith(normPreview)) {
allData.matches.push({ type: 'normalized', preview: rawPreview, url, value });
matched = true;
break;
}
if (noBreaksValue.startsWith(noBreaksPreview)) {
allData.matches.push({ type: 'noBreaks', preview: rawPreview, url, value });
matched = true;
break;
}
const indexInNorm = normValue.indexOf(normPreview);
if (indexInNorm >= 0 && indexInNorm <= 30) {
allData.matches.push({ type: 'loose', preview: rawPreview, url, value });
matched = true;
break;
}
const indexInNormCompletely = normValueCompletely.indexOf(normPreviewCompletely);
if (indexInNormCompletely >= 0 && indexInNormCompletely <= 30) {
allData.matches.push({ type: 'finalNormalized', preview: rawPreview, url, value });
matched = true;
break;
}
}
}
updateStats();
displayMatches();
} catch (err) {
stats.textContent = '❌ Error loading or processing data';
stats.className = 'error';
matchesDiv.innerHTML = `
<div class="error">
<p><strong>Error Details:</strong></p>
<p>${err.message}</p>
<p>Check the browser console for more information.</p>
</div>
`;
console.error(err);
}
}
// Start loading data when page loads
loadData();
</script>
</body>
</html>