UI:
- Ein-/Ausklappen jetzt mit grossem +/- Icon statt kleinem Pfeil.
- "Entfernen" ist ein Papierkorb-Symbol (dashicon).
- Aktiver Tab klar gekennzeichnet (Akzent-Unterstrich + Farbe).
- 20px Abstand zwischen Tabs und Inhalt.
Funktionen:
- Scan erkennt Anbieter, fuer die es eine Vorlage gibt ("Vorlage verfuegbar"),
und "Vorlage uebernehmen" fuellt die komplette Vorlage statt nur Host/Pattern.
- Platzhalter: Checkbox "Diesen Dienst kuenftig immer laden" (Standard AN).
Abgewaehlt -> Inhalt wird nur einmal geladen, keine dauerhafte Einwilligung.
i18n:
- Sprachumschaltung: Deutsch fuer alle de_* Locales, Englisch fuer alle anderen
(plugin_locale-Filter). Vollstaendige englische Uebersetzung (126 Strings,
inkl. Vorlagentexte/Empfaenger) als gdpr-content-blocker-en_US.po/.mo.
- Helper-Skripte (extract/build) in hilfsdaten/.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
124 lines
3.7 KiB
JavaScript
124 lines
3.7 KiB
JavaScript
/**
|
||
* Content Blocker – frontend.js
|
||
* Click-to-load: the real iframe is CREATED only after the user actively
|
||
* consents per service. Consent is stored in localStorage per service id.
|
||
*/
|
||
|
||
( function () {
|
||
'use strict';
|
||
|
||
const STORAGE_PREFIX = 'cb_consent_';
|
||
|
||
/* ───────────────────────── consent storage ───────────────────────── */
|
||
|
||
function hasConsent( serviceId ) {
|
||
try {
|
||
return localStorage.getItem( STORAGE_PREFIX + serviceId ) === '1';
|
||
} catch ( e ) {
|
||
return false;
|
||
}
|
||
}
|
||
|
||
function grantConsent( serviceId ) {
|
||
try {
|
||
localStorage.setItem( STORAGE_PREFIX + serviceId, '1' );
|
||
} catch ( e ) {
|
||
// localStorage unavailable; allow the load for this session only.
|
||
}
|
||
}
|
||
|
||
/* ───────────────────────── iframe loading ────────────────────────── */
|
||
|
||
/** Replace a .cb-blocker element with the real iframe. src comes from data-src. */
|
||
function loadContent( blockerEl ) {
|
||
const src = blockerEl.dataset.src;
|
||
if ( ! src ) {
|
||
blockerEl.remove();
|
||
return;
|
||
}
|
||
|
||
const iframe = document.createElement( 'iframe' );
|
||
|
||
if ( blockerEl.dataset.width ) iframe.width = blockerEl.dataset.width;
|
||
if ( blockerEl.dataset.height ) iframe.height = blockerEl.dataset.height;
|
||
|
||
iframe.setAttribute( 'loading', 'lazy' );
|
||
iframe.setAttribute( 'allowfullscreen', '' );
|
||
iframe.setAttribute( 'referrerpolicy', 'no-referrer-when-downgrade' );
|
||
|
||
// Set src last — this is the moment the network request is made.
|
||
iframe.src = src;
|
||
|
||
blockerEl.parentNode.replaceChild( iframe, blockerEl );
|
||
}
|
||
|
||
function loadPreConsented() {
|
||
document.querySelectorAll( '.cb-blocker[data-cb-id]' ).forEach( function ( el ) {
|
||
const id = el.dataset.cbId;
|
||
if ( id && hasConsent( id ) ) {
|
||
loadContent( el );
|
||
}
|
||
} );
|
||
}
|
||
|
||
/* ───────────────────────── consent buttons ───────────────────────── */
|
||
|
||
function attachButtons() {
|
||
document.addEventListener( 'click', function ( e ) {
|
||
const btn = e.target.closest( '.cb-blocker__button' );
|
||
if ( ! btn ) return;
|
||
|
||
const serviceId = btn.dataset.cbId;
|
||
if ( ! serviceId ) return;
|
||
|
||
const wrapper = btn.closest( '.cb-blocker' );
|
||
|
||
// "Remember" checkbox (default checked): persist consent only when
|
||
// ticked; otherwise load this embed once without storing consent.
|
||
const remember = wrapper
|
||
? wrapper.querySelector( '.cb-blocker__remember-cb' )
|
||
: null;
|
||
if ( ! remember || remember.checked ) {
|
||
grantConsent( serviceId );
|
||
}
|
||
|
||
if ( wrapper ) {
|
||
loadContent( wrapper );
|
||
}
|
||
} );
|
||
}
|
||
|
||
/* ───────────────────────── revoke (Art. 7 (3)) ───────────────────── */
|
||
|
||
window.cbRevokeAll = function () {
|
||
try {
|
||
const keysToRemove = [];
|
||
for ( let i = 0; i < localStorage.length; i++ ) {
|
||
const key = localStorage.key( i );
|
||
if ( key && key.indexOf( STORAGE_PREFIX ) === 0 ) {
|
||
keysToRemove.push( key );
|
||
}
|
||
}
|
||
keysToRemove.forEach( function ( k ) {
|
||
localStorage.removeItem( k );
|
||
} );
|
||
} catch ( e ) {
|
||
// Silently ignore if localStorage is unavailable.
|
||
}
|
||
window.location.reload();
|
||
};
|
||
|
||
/* ───────────────────────── bootstrap ─────────────────────────────── */
|
||
|
||
function init() {
|
||
loadPreConsented();
|
||
attachButtons();
|
||
}
|
||
|
||
if ( document.readyState === 'loading' ) {
|
||
document.addEventListener( 'DOMContentLoaded', init );
|
||
} else {
|
||
init();
|
||
}
|
||
} )();
|