Files
jbergner 6e152a5121
Some checks failed
release-tag / release-image (push) Failing after 1m38s
2.0.2 Update und Anpassungen
2026-07-24 10:08:19 +02:00

259 lines
10 KiB
JavaScript

(() => {
const cfg = window.BULK_CONFIG;
if (!cfg) return;
const $ = id => document.getElementById(id);
const t = (key, fallback = key) => (cfg.texts && cfg.texts[key]) || fallback;
let lastResult = null;
const apiKeyInput = $('bulk-api-key');
if (apiKeyInput) {
apiKeyInput.value = sessionStorage.getItem('aiDisclosureBulkApiKey') || '';
apiKeyInput.addEventListener('input', () => sessionStorage.setItem('aiDisclosureBulkApiKey', apiKeyInput.value));
}
function parseLines() {
const raw = ($('bulk-items')?.value || '').split(/\r?\n/).map(v => v.trim()).filter(Boolean);
return raw.map((line, index) => {
let id = '';
let subject = '';
if (line.includes('|')) {
const [left, ...right] = line.split('|');
id = left.trim();
subject = right.join('|').trim();
} else if (line.includes('\t')) {
const [left, ...right] = line.split('\t');
id = left.trim();
subject = right.join('\t').trim();
} else {
subject = line;
}
if (!id) id = `item-${index + 1}`;
return {id, subject};
});
}
function updateCount() {
const count = parseLines().length;
const el = $('bulk-item-count');
if (el) {
el.textContent = `${count} / ${cfg.maxItems}`;
el.classList.toggle('over-limit', count > cfg.maxItems);
}
}
function normalizeReview(extentId, reviewId) {
const extent = $(extentId);
const review = $(reviewId);
if (!extent || !review) return;
if (extent.value === 'none') review.value = 'none';
else if (review.value === 'none') review.value = 'editorial';
}
const componentPairs = [
['bulk-text-extent', 'bulk-text-review'],
['bulk-cover-extent', 'bulk-cover-review'],
['bulk-image-extent', 'bulk-image-review'],
['bulk-research-extent', 'bulk-research-review'],
['bulk-translation-extent', 'bulk-translation-review'],
['bulk-code-extent', 'bulk-code-review']
];
componentPairs.forEach(([extentId, reviewId]) => {
$(extentId)?.addEventListener('change', () => normalizeReview(extentId, reviewId));
});
function commonParameters(subject) {
const params = {
mode: 'article',
lang: $('bulk-output-language').value,
assurance: $('bulk-assurance').value,
textExtent: $('bulk-text-extent').value,
textReview: $('bulk-text-review').value,
coverImageExtent: $('bulk-cover-extent').value,
coverImageReview: $('bulk-cover-review').value,
imageExtent: $('bulk-image-extent').value,
imageReview: $('bulk-image-review').value,
researchExtent: $('bulk-research-extent').value,
researchReview: $('bulk-research-review').value,
translationExtent: $('bulk-translation-extent').value,
translationReview: $('bulk-translation-review').value,
codeExtent: $('bulk-code-extent').value,
codeReview: $('bulk-code-review').value
};
if (subject) params.subject = subject;
const bools = {
'bulk-public-interest': 'publicInterestText',
'bulk-deepfake': 'deepfake',
'bulk-substantial-review': 'substantialHumanReview',
'bulk-editorial-responsibility': 'editorialResponsibilityConfirmed',
'bulk-first-exposure': 'firstExposureDisclosure',
'bulk-accessibility': 'accessibilityConsidered'
};
Object.entries(bools).forEach(([id, key]) => { if ($(id)?.checked) params[key] = 'true'; });
const responsible = $('bulk-responsible')?.value.trim();
const responsibleURL = $('bulk-responsible-url')?.value.trim();
if (responsible) params.responsible = responsible;
if (responsibleURL) params.responsibleUrl = responsibleURL;
return params;
}
function buildVisualRequest() {
const items = parseLines();
if (!items.length) throw new Error(t('error_no_items', 'Add at least one item.'));
if (items.length > cfg.maxItems) throw new Error(t('error_too_many_items', 'The configured item limit has been exceeded.'));
return {items: items.map(item => ({id: item.id, parameters: commonParameters(item.subject)}))};
}
function headers() {
const h = {'Content-Type': 'application/json'};
const key = apiKeyInput?.value.trim();
if (cfg.requireAPIKey) {
if (!key) throw new Error(t('error_api_key', 'A bulk API key is required.'));
h.Authorization = `Bearer ${key}`;
} else if (key) {
h.Authorization = `Bearer ${key}`;
}
return h;
}
async function run(request) {
const status = $('bulk-run-status');
if (status) status.textContent = t('status_running', 'Processing…');
try {
const response = await fetch(cfg.endpoint, {method: 'POST', headers: headers(), body: JSON.stringify(request)});
const data = await response.json().catch(() => ({code: 'invalid_response', detail: response.statusText}));
if (!response.ok) throw new Error(data.detail || data.title || `${response.status} ${response.statusText}`);
lastResult = data;
renderResults(data);
if (status) status.textContent = t('status_done', 'Completed.');
} catch (err) {
if (status) status.textContent = `${t('status_error', 'Error')}: ${err.message}`;
throw err;
}
}
function renderResults(data) {
const section = $('results');
const body = $('bulk-result-body');
const raw = $('bulk-result-json');
if (!section || !body || !raw) return;
section.hidden = false;
body.textContent = '';
const items = Array.isArray(data.items) ? data.items : [];
const valid = items.filter(item => item.valid).length;
const failed = items.length - valid;
$('bulk-result-summary').textContent = `${t('summary_processed', 'Processed')}: ${items.length} · ${t('summary_successful', 'Successful')}: ${valid} · ${t('summary_failed', 'Failed')}: ${failed}`;
items.forEach(item => {
const tr = document.createElement('tr');
const id = document.createElement('td');
id.textContent = item.id || '—';
const status = document.createElement('td');
const pill = document.createElement('span');
pill.className = `bulk-result-pill ${item.valid ? 'ok' : 'error'}`;
pill.textContent = item.valid ? t('result_ok', 'OK') : t('result_error', 'Error');
status.appendChild(pill);
if (!item.valid && item.error?.detail) {
const detail = document.createElement('small');
detail.className = 'bulk-error-detail';
detail.textContent = item.error.detail;
status.appendChild(detail);
}
const assessment = document.createElement('td');
assessment.textContent = item.article50Assessment?.code || '—';
const links = document.createElement('td');
if (item.valid) {
const linkData = [
[t('link_declaration', 'Declaration'), item.declarationUrl],
[t('link_manifest', 'Manifest'), item.manifestUrl],
[t('link_badge', 'Badge'), item.badgeUrl]
];
linkData.forEach(([label, href], index) => {
if (!href) return;
if (index) links.appendChild(document.createTextNode(' · '));
const a = document.createElement('a');
a.href = href;
a.target = '_blank';
a.rel = 'noopener noreferrer';
a.textContent = label;
links.appendChild(a);
});
} else links.textContent = '—';
tr.append(id, status, assessment, links);
body.appendChild(tr);
});
raw.textContent = JSON.stringify(data, null, 2);
section.scrollIntoView({behavior: 'smooth', block: 'start'});
}
function download(filename, content, type) {
const blob = new Blob([content], {type});
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = filename;
document.body.appendChild(a);
a.click();
a.remove();
URL.revokeObjectURL(url);
}
function csvEscape(value) {
const s = String(value ?? '');
return /[",\n\r;]/.test(s) ? `"${s.replaceAll('"', '""')}"` : s;
}
function resultCSV() {
const rows = [['id', 'valid', 'assessment', 'declaration_url', 'manifest_url', 'badge_url', 'error']];
(lastResult?.items || []).forEach(item => rows.push([
item.id || '', item.valid ? 'true' : 'false', item.article50Assessment?.code || '', item.declarationUrl || '', item.manifestUrl || '', item.badgeUrl || '', item.error?.detail || ''
]));
return rows.map(row => row.map(csvEscape).join(';')).join('\n');
}
$('bulk-items')?.addEventListener('input', updateCount);
$('bulk-sample-items')?.addEventListener('click', () => {
$('bulk-items').value = 'article-1001 | https://example.org/articles/1001\narticle-1002 | https://example.org/articles/1002\narticle-1003 | https://example.org/articles/1003';
updateCount();
});
$('bulk-run')?.addEventListener('click', async () => {
try { await run(buildVisualRequest()); } catch (_) { /* status already rendered */ }
});
$('bulk-json-sample')?.addEventListener('click', () => {
try {
if (!parseLines().length) {
$('bulk-items').value = 'article-1001 | https://example.org/articles/1001';
updateCount();
}
$('bulk-json').value = JSON.stringify(buildVisualRequest(), null, 2);
} catch (err) {
$('bulk-run-status').textContent = `${t('status_error', 'Error')}: ${err.message}`;
}
});
$('bulk-json-run')?.addEventListener('click', async () => {
try {
const request = JSON.parse($('bulk-json').value);
await run(request);
} catch (err) {
$('bulk-run-status').textContent = `${t('status_error', 'Error')}: ${err.message}`;
}
});
$('bulk-copy-json')?.addEventListener('click', async () => {
if (!lastResult) return;
await navigator.clipboard.writeText(JSON.stringify(lastResult, null, 2));
});
$('bulk-download-json')?.addEventListener('click', () => {
if (lastResult) download('ai-disclosure-bulk-results.json', JSON.stringify(lastResult, null, 2), 'application/json');
});
$('bulk-download-csv')?.addEventListener('click', () => {
if (lastResult) download('ai-disclosure-bulk-results.csv', resultCSV(), 'text/csv;charset=utf-8');
});
$('bulk-language')?.addEventListener('change', event => {
const url = new URL(window.location.href);
url.searchParams.set('lang', event.target.value);
window.location.href = url.toString();
});
updateCount();
componentPairs.forEach(([extentId, reviewId]) => normalizeReview(extentId, reviewId));
})();