Files
GDPR-Content-Blocker/gdpr-content-blocker/assets/admin.js
s4luorth 4f0f8ea170 feat: pfeile statt +/-, shortcodes-tab, funktionen aus darstellung entfernt
- Ein-/Ausklappen wieder mit schwarzem Pfeil (>/v) ohne Box.
- Neuer Tab Shortcodes; Darstellung listet keine Funktionen mehr.
- CSS-Klassen-Referenz in den Darstellung-Tab (unter Custom CSS) verschoben.
- EN-Uebersetzung (121 Strings) neu gebaut.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-07 15:22:20 +02:00

286 lines
9.8 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* Content Blocker admin.js
* Color-picker init + service repeater (add / remove rows).
*/
( function ( $ ) {
'use strict';
/* ── Color pickers ── */
function initColorPickers( ctx ) {
$( '.cb-color-picker', ctx ).wpColorPicker();
}
/* ── Live header update: "interner-name — Anbietername" ── */
function syncTitle( box ) {
const id = ( $( '.cb-input-id', box ).val() || '' ).trim();
const name = ( $( '.cb-input-name', box ).val() || '' ).trim();
const head = ( id || ( cbAdmin.newServiceLbl || 'neu' ) ) + ( name ? ' — ' + name : '' );
$( '.cb-service-title', box ).first().text( head );
}
function bindNameSync( box ) {
$( '.cb-input-name, .cb-input-id', box ).on( 'input', function () {
syncTitle( box );
} );
}
/* ── Expand / collapse a service box ── */
function setExpanded( box, open ) {
$( '.cb-service-grid', box ).toggle( open );
const $btn = $( '.cb-service-toggle', box );
$btn.attr( 'aria-expanded', open ? 'true' : 'false' ).text( open ? '▾' : '▸' );
}
function bindToggle( box ) {
$( '.cb-service-toggle', box ).on( 'click', function () {
const open = $( this ).attr( 'aria-expanded' ) !== 'true';
setExpanded( box, open );
} );
// Clicking the title also expands/collapses.
$( '.cb-service-title', box ).on( 'click', function () {
const open = $( '.cb-service-toggle', box ).attr( 'aria-expanded' ) !== 'true';
setExpanded( box, open );
} );
}
/* ── Re-index a row's field names after a remove ── */
function reindexRows() {
$( '#cb-services-list .cb-service-box' ).each( function ( i ) {
$( this ).attr( 'data-cb-index', i );
$( this ).find( '[name]' ).each( function () {
const n = $( this ).attr( 'name' );
$( this ).attr( 'name', n.replace( /cb_services\[\d+\]/, 'cb_services[' + i + ']' ) );
} );
} );
}
/* ── Add a new (optionally prefilled) service row ── */
function addServiceRow( preset ) {
const index = $( '#cb-services-list .cb-service-box' ).length;
const template = $( '#cb-service-template' ).html();
const newHtml = template.replace( /__INDEX__/g, index );
const $box = $( newHtml );
$( '#cb-services-list' ).append( $box );
bindNameSync( $box );
bindToggle( $box );
$box.find( '.cb-remove-service' ).on( 'click', handleRemove );
if ( preset ) {
fillPreset( $box, preset );
}
setExpanded( $box, true ); // new rows start expanded for editing
syncTitle( $box );
return $box;
}
/* ── Fill a row from a preset object ── */
function fillPreset( $box, p ) {
const setText = function ( field, val ) {
$box.find( '[name$="[' + field + ']"]' ).val( val != null ? val : '' );
};
const setBool = function ( field, val ) {
$box.find( '[name$="[' + field + ']"]' ).prop( 'checked', !! val );
};
setText( 'id', p.id );
setText( 'name', p.name );
setText( 'match_pattern', p.match_pattern );
setText( 'recipient', p.recipient );
setText( 'privacy_url', p.privacy_url );
setText( 'purpose', p.purpose );
setBool( 'third_country', p.third_country );
setBool( 'sets_cookie', p.sets_cookie );
setBool( 'loads_script', p.loads_script );
$box.find( '.cb-service-title' ).text( p.name || cbAdmin.newServiceLbl );
}
/* ── Add empty service ── */
$( '#cb-add-service' ).on( 'click', function () {
addServiceRow( null );
} );
/* ── Insert preset ── */
$( '#cb-preset-select' ).on( 'change', function () {
const key = $( this ).val();
if ( ! key || ! cbAdmin.presets || ! cbAdmin.presets[ key ] ) {
return;
}
addServiceRow( cbAdmin.presets[ key ] );
$( this ).val( '' ); // reset so the same preset can be added again
} );
/* ── Remove service ── */
function handleRemove() {
if ( ! window.confirm( cbAdmin.confirmRemove ) ) {
return;
}
$( this ).closest( '.cb-service-box' ).remove();
reindexRows();
}
/* ── Tab switching ── */
$( '[data-cb-tab]' ).on( 'click', function ( e ) {
e.preventDefault();
activateTab( $( this ).data( 'cb-tab' ) );
} );
/* ── Website scan ── */
$( '#cb-scan-btn' ).on( 'click', function () {
const $btn = $( this ).prop( 'disabled', true );
const i18n = cbAdmin.i18n || {};
$( '#cb-scan-status' ).text( i18n.scanning || 'Scanning…' );
$( '#cb-scan-results' ).empty();
$.post( cbAdmin.ajaxUrl, { action: 'cb_scan', nonce: cbAdmin.scanNonce } )
.done( function ( resp ) {
if ( ! resp || ! resp.success ) {
const msg = resp && resp.data && resp.data.message ? resp.data.message : 'Error';
$( '#cb-scan-status' ).text( ( i18n.scanError || 'Scan failed:' ) + ' ' + msg );
return;
}
$( '#cb-scan-status' ).text( '' );
renderScan( resp.data );
} )
.fail( function ( xhr ) {
$( '#cb-scan-status' ).text( ( i18n.scanError || 'Scan failed:' ) + ' ' + xhr.status );
} )
.always( function () {
$btn.prop( 'disabled', false );
} );
} );
function renderScan( data ) {
const i18n = cbAdmin.i18n || {};
const findings = ( data && data.findings ) || [];
const $out = $( '#cb-scan-results' ).empty();
// Scanned pages summary
if ( data && data.scanned && data.scanned.length ) {
const $p = $( '<p class="description"></p>' ).text( ( i18n.scannedPages || 'Scanned:' ) + ' ' );
data.scanned.forEach( function ( s, i ) {
if ( i > 0 ) $p.append( ', ' );
$p.append( $( '<code></code>' ).text( s.url + ( s.error ? ' (!)' : '' ) ) );
} );
$out.append( $p );
}
if ( ! findings.length ) {
$out.append( $( '<p></p>' ).text( i18n.noFindings || 'No external resources found.' ) );
return;
}
const $table = $( '<table class="widefat striped"></table>' );
const $head = $( '<tr></tr>' );
[ i18n.host || 'Host', i18n.type || 'Type', i18n.count || 'Count', i18n.foundOn || 'Found on', i18n.example || 'Example', i18n.status || 'Status', i18n.action || 'Action' ]
.forEach( function ( h ) { $head.append( $( '<th></th>' ).text( h ) ); } );
$table.append( $( '<thead></thead>' ).append( $head ) );
const $body = $( '<tbody></tbody>' );
findings.forEach( function ( f ) {
const $tr = $( '<tr></tr>' );
// Host (+ party badge)
const $host = $( '<td></td>' );
$host.append( $( '<strong></strong>' ).text( f.host ) );
$host.append( document.createElement( 'br' ) );
$host.append( $( '<span></span>' )
.css( { fontSize: '11px', color: f.third_party ? '#b32d2e' : '#1a7f37' } )
.text( f.third_party ? ( i18n.thirdParty || 'third-party' ) : ( i18n.firstParty || 'first-party' ) ) );
$tr.append( $host );
// Types
$tr.append( $( '<td></td>' ).text( ( f.types || [] ).join( ', ' ) ) );
// Count
$tr.append( $( '<td></td>' ).text( f.count ) );
// Found on which pages
const $pages = $( '<td></td>' ).css( { fontSize: '11px' } );
( f.pages || [] ).forEach( function ( pageUrl, i ) {
if ( i > 0 ) $pages.append( document.createElement( 'br' ) );
let label = pageUrl;
try { label = new URL( pageUrl ).pathname || pageUrl; } catch ( e ) {}
$pages.append( $( '<a></a>' )
.attr( { href: pageUrl, target: '_blank', rel: 'noopener noreferrer', title: pageUrl } )
.text( label ) );
} );
$tr.append( $pages );
// Example URL
const $ex = $( '<td></td>' );
const sample = ( f.sample_urls || [] )[ 0 ] || '';
$ex.append( $( '<code></code>' ).css( { fontSize: '11px', wordBreak: 'break-all' } ).text( sample ) );
$tr.append( $ex );
// Does a ready-made template exist for this finding?
const preset = ( f.preset && cbAdmin.presets && cbAdmin.presets[ f.preset ] )
? cbAdmin.presets[ f.preset ]
: null;
// Status
const $st = $( '<td></td>' );
if ( f.covered ) {
$st.append( $( '<span></span>' ).css( { color: '#1a7f37', fontWeight: '600' } ).text( '✓ ' + ( i18n.covered || 'covered' ) ) );
} else if ( preset ) {
$st.append( $( '<span></span>' ).css( { color: '#2043B7', fontWeight: '600' } ).text( '★ ' + ( i18n.templateAvail || 'Vorlage verfügbar' ) ) );
}
$tr.append( $st );
// Action: take over as a new service (only useful for uncovered third parties)
const $act = $( '<td></td>' );
if ( f.third_party && ! f.covered ) {
// Prefill from the matching preset (full data) if there is one,
// otherwise just seed host + pattern.
const data = preset
? preset
: { name: f.host, match_pattern: f.suggested_pattern || f.host, third_country: true };
$( '<button type="button" class="button button-small"></button>' )
.text( preset ? ( i18n.useTemplate || 'Vorlage übernehmen' ) : ( i18n.addService || 'Add as service' ) )
.on( 'click', function () {
activateTab( 'services' );
const $box = addServiceRow( data );
if ( $box && $box.length ) {
$box[ 0 ].scrollIntoView( { behavior: 'smooth', block: 'center' } );
$box.find( '.cb-input-id' ).trigger( 'focus' );
}
} )
.appendTo( $act );
}
$tr.append( $act );
$body.append( $tr );
} );
$table.append( $body );
$out.append( $table );
}
/* ── Activate a tab by key ── */
function activateTab( tab ) {
$( '.cb-tab-content' ).hide();
$( '#cb-tab-' + tab ).show();
$( '[data-cb-tab]' ).removeClass( 'nav-tab-active' );
$( '[data-cb-tab="' + tab + '"]' ).addClass( 'nav-tab-active' );
}
/* ── Bootstrap ── */
$( document ).ready( function () {
initColorPickers( document );
$( '#cb-services-list .cb-service-box' ).each( function () {
bindNameSync( this );
bindToggle( this );
$( '.cb-remove-service', this ).on( 'click', handleRemove );
} );
// Honor ?cb_tab=… so license redirects land on the right tab.
const params = new URLSearchParams( window.location.search );
const tab = params.get( 'cb_tab' );
if ( tab && $( '#cb-tab-' + tab ).length ) {
activateTab( tab );
}
} );
// Expose strings from wp_localize_script if needed.
window.cbAdmin = window.cbAdmin || { confirmRemove: 'Dienst wirklich entfernen?' };
} )( jQuery );