// ==UserScript==
// @name Wörter-Verwischen-Skript
// @namespace http://tampermonkey.net/
// @version 3.25
// @description Blurt Wörter auf Webseiten. Features: Mehrere Treffer, IP-Erkennung und Vollbild
// @author Sky95
// @match *://*/*
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_registerMenuCommand
// @grant GM_addStyle
// @grant GM_xmlhttpRequest
// @connect api.ipify.org
// @run-at document-start
// @noframes
// ==/UserScript==
(function() {
'use strict';
GM_addStyle(`
.word-blur {
filter: blur(7.5px);
transition: filter 1.5s ease;
}
.word-blur:hover {
filter: blur(0px);
transition-delay: 1s;
}
.full-page-blur {
filter: blur(10px) !important;
}
.full-page-unblur {
filter: blur(0px) !important;
transition: filter 0.5s ease !important;
}
.full-page-blur * {
pointer-events: none !important;
}
body {
max-width: 100% !important;
overflow-x: hidden !important;
}
#blur-settings-panel {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 500px;
background: #2b2b2b !important;
color: #fff !important;
border-radius: 8px !important;
box-shadow: 0 4px 20px rgba(0,0,0,0.8) !important;
z-index: 10000;
display: none;
font-family: 'Courier New', monospace !important;
pointer-events: auto !important;
opacity: 0;
transition: opacity 0.3s ease-in-out;
}
#blur-settings-panel.visible {
opacity: 0.98;
}
#blur-section-container {
max-height: 70vh !important;
overflow-y: auto !important;
scrollbar-width: thin !important;
scrollbar-color: #666 #2b2b2b !important;
}
#blur-settings-header {
padding: 15px 20px !important;
background: #363636 !important;
border-radius: 8px 8px 0 0;
cursor: move;
user-select: none;
display: flex;
justify-content: space-between;
align-items: center;
}
#blur-settings-header h3 {
color: #fff !important;
font-size: 18px !important;
font-weight: normal !important;
font-family: Arial, sans-serif !important;
margin: 0 !important;
padding: 0 !important;
}
#blur-text-info {
padding: 5px 20px !important;
background: #262626 !important;
border: 1px solid #406040 !important;
font-size: 12px !important;
color: #afa !important;
line-height: 1.4 !important;
}
#blur-ip-info {
padding: 5px 20px !important;
background: #262626 !important;
font-size: 14px !important;
color: #aaf !important;
display: flex !important;
justify-content: space-between !important;
align-items: center !important;
pointer-events: auto !important;
filter: blur(0) !important;
margin-top: 5px !important;
}
#blur-ip-display {
pointer-events: auto !important;
filter: blur(0) !important;
}
#blur-ip-button {
background: #4CAF50 !important;
color: white !important;
border: none !important;
border-radius: 4px !important;
padding: 5px 10px !important;
cursor: pointer !important;
font-size: 12px !important;
pointer-events: auto !important;
filter: blur(0) !important;
}
#blur-ip-button:hover {
background: #388E3C !important;
}
#blur-close-button {
background: none !important;
border: none !important;
color: #fff !important;
cursor: pointer !important;
font-family: Arial, sans-serif !important;
font-size: 24px !important;
padding: 0 !important;
width: 24px !important;
height: 24px !important;
display: flex !important;
align-items: center !important;
justify-content: center !important;
line-height: 1 !important;
text-decoration: none !important;
outline: none !important;
}
#blur-close-button:hover {
background: rgba(255,255,255,0.1) !important;
border-radius: 4px !important;
}
.blur-section-content {
padding: 15px 20px !important;
display: none;
background: #2b2b2b !important;
}
.blur-button-container {
padding: 15px 20px !important;
background: #363636 !important;
border-top: 1px solid #404040 !important;
display: flex !important;
justify-content: space-between !important;
border-radius: 0 0 8px 8px !important;
gap: 10px !important;
}
.blur-button {
padding: 8px 16px !important;
border: none !important;
border-radius: 4px !important;
cursor: pointer !important;
font-family: 'Courier New', monospace !important;
font-size: 14px !important;
outline: none !important;
width: auto !important;
}
.blur-save-button {
background: #2196F3 !important;
color: white !important;
}
.blur-save-button:hover {
background: #1976D2 !important;
}
.blur-cancel-button {
background: #666 !important;
color: white !important;
}
.blur-cancel-button:hover {
background: #555 !important;
}
.blur-section {
margin: 5px 0 !important;
border-bottom: 1px solid #404040 !important;
background: #2b2b2b !important;
}
.blur-section-header {
padding: 10px 20px !important;
background: #363636 !important;
cursor: pointer !important;
user-select: none !important;
display: flex !important;
justify-content: space-between !important;
align-items: center !important;
color: #fff !important;
font-size: 14px !important;
font-weight: bold !important;
}
.blur-section-content {
padding: 15px 20px !important;
display: none;
background: #2b2b2b !important;
}
.blur-section.expanded .blur-section-content {
display: block;
}
.blur-section-arrow {
transition: transform 0.3s ease;
color: #fff !important;
font-size: 12px !important;
}
.blur-section.expanded .blur-section-arrow {
transform: rotate(180deg);
}
.blur-slider-container {
margin-top: 10px !important;
display: flex !important;
align-items: center !important;
gap: 5px !important;
}
.blur-slider-label {
flex: 1 !important;
color: #fff !important;
font-size: 14px !important;
}
.blur-slider {
flex: 1 !important;
height: 4px !important;
max-width: 125px;
background: #666 !important;
outline: none !important;
-webkit-appearance: none !important;
appearance: none !important;
border-radius: 2px !important;
}
.blur-slider::-webkit-slider-thumb {
-webkit-appearance: none !important;
appearance: none !important;
width: 16px !important;
height: 16px !important;
background: #2196F3 !important;
border-radius: 50% !important;
cursor: pointer !important;
}
.blur-slider::-moz-range-thumb {
width: 16px !important;
height: 16px !important;
background: #2196F3 !important;
border-radius: 50% !important;
cursor: pointer !important;
border: none !important;
}
.blur-slider-value {
min-width: 45px !important;
text-align: right !important;
color: #2196F3 !important;
font-size: 12px !important;
}
#blur-toggle-button {
position: fixed !important;
top: 0 !important;
left: 0 !important;
width: 14px !important;
height: 14px !important;
max-width: 14px !important;
max-height: 14px !important;
min-width: 14px !important;
min-height: 14px !important;
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAACXBIWXMAAAsTAAALEwEAmpwYAAACY0lEQVQ4jY3VP4gVVxQG8N+dN+tz1dVVWRMwiQkSEkTBSgurpEqXQkwsrSWQKo2NhYE06dJIugRRk0qIovgHBcHEpAmYgCYQ/LO7havo+nZ1n/v23hT3Ljs8H7t+cGcuw3zfnHPu+c6Ek5bFQRzFG+ji38S5IX6Z5b9DdEN+Ly0S6uX1HMaHZT+DscDWBUbX0j5P+plqHXGG3mfElQTfaexXY2Nga+KtyJOnpMTqx7xoM3WVZ639S4Tv8VWJ+gH24MuGYCjpLVTMY1NiJ3ZXvBl52aETSg0/x+kGea5E1ETCAqYxiQ7aGMJ44HLiSl2IP/SR+8XgOVoYCWxPxEAVUTEa6bR4WONY+dJK+Auj2JJYhzoh5DquD7wd+KDC/RWEvsGOwElcTNwJOd0oC86X9B9FntX4Tj6En7CqT+xbHCnEhcD2mOu4AWtKlE/xd+J64GZViGewb0B0Xy9uphnHP7ideIxeYD4whT9rfm9zp2qQ4wDBNODZQPTKfbEPP8UFrzqnxiUY5r0e7wf2BnZhs9wyQb50Mdfazxf4cYAYuQyrMIFPsCfktU2uYStk0bVop2K9d1fI5khZv4VsvbHECCpIWXAksDmyIZxguMon1X/C/ZiVG7sKuWTRUmM/ivwaOFvjBQ7JfbaIrlebfY1ivcRkoJNoB4YiExU3e9yqq/ypUxUfY4dswzOy8a80FUP2+L3EtcRdDMfskoeJP7o8CCcahGYPFdyzNMJ6cv/dwOmKW7GMr6Gc5dRYccpyaArO4Yk8WcYrJtczN9MYsB+9xoA9ji1ldYsrJhLTs3QP8LL/F/A/amq/lc3UweoAAAAASUVORK5CYII=') !important;
background-size: cover !important;
background-color: transparent !important;
background-repeat: no-repeat !important;
border: none !important;
cursor: pointer !important;
z-index: 9999 !important;
padding: 0 !important;
margin: 0 !important;
outline: none !important;
pointer-events: auto !important;
filter: blur(0) !important;
}
#blur-toggle-button:hover {
opacity: 0.8 !important;
}
#blur-word-list {
display: flex !important;
flex-direction: column !important;
margin: 0 !important;
padding: 0 !important;
}
.blur-word-entry {
display: flex !important;
gap: 5px !important;
margin-top: 5px !important;
align-items: center !important;
}
.blur-word-input {
flex-grow: 1 !important;
background: #1e1e1e !important;
border: 1px solid #404040 !important;
color: #ffffff !important;
padding: 8px !important;
border-radius: 4px !important;
font-family: 'Courier New', monospace !important;
font-size: 14px !important;
line-height: 1.4 !important;
height: auto !important;
width: auto !important;
margin: 0 !important;
outline: none !important;
box-shadow: none !important;
-webkit-appearance: none !important;
-moz-appearance: none !important;
appearance: none !important;
transition: border-color 0.2s ease !important;
filter: blur(2px) !important;
}
.blur-word-input:focus {
border-color: #2196F3 !important;
filter: blur(0) !important;
}
.blur-delete-button {
background: #ff4444 !important;
color: white !important;
border: none !important;
border-radius: 4px !important;
width: 30px !important;
height: 30px !important;
cursor: pointer !important;
display: flex !important;
align-items: center !important;
justify-content: center !important;
font-family: Arial, sans-serif !important;
font-size: 20px !important;
line-height: 1 !important;
padding: 0 !important;
margin: 0 !important;
outline: none !important;
transition: background-color 0.2s ease !important;
}
.blur-delete-button:hover {
background: #cc0000 !important;
}
.blur-add-button {
background: #4CAF50 !important;
color: white !important;
border: none !important;
border-radius: 4px !important;
padding: 8px !important;
height: 36px !important;
cursor: pointer !important;
width: 100% !important;
margin-top: 5px !important;
font-family: 'Courier New', monospace !important;
font-size: 14px !important;
text-align: center !important;
outline: none !important;
transition: background-color 0.2s ease !important;
}
.blur-add-button:hover {
background: #388E3C !important;
}
.blur-option-container {
padding: 10px !important;
background: #1e1e1e !important;
border-radius: 4px !important;
margin-top: 5px !important;
}
.blur-checkbox-label {
display: flex !important;
align-items: center !important;
gap: 10px !important;
color: #fff !important;
font-size: 14px !important;
cursor: pointer !important;
user-select: none !important;
}
.blur-checkbox {
width: 16px !important;
height: 16px !important;
cursor: pointer !important;
margin: 0 !important;
}
`);
let blurWords = GM_getValue('blurWords', []);
let userIP = GM_getValue('userIP', '');
let isFullPageBlurred = GM_getValue('isFullPageBlurred', false);
let defaultFullPageBlur = GM_getValue('defaultFullPageBlur', false);
let wordBlurStrength = GM_getValue('wordBlurStrength', 7.5);
let hoverDelay = GM_getValue('hoverDelay', 1.0);
let pageBlurStrength = GM_getValue('pageBlurStrength', 10);
let wordunblurTransitionSpeed = GM_getValue('wordunblurTransitionSpeed', 1.5);
let isIPBlurEnabled = GM_getValue('isIPBlurEnabled', false);
let clickTimeout = null;
const processedNodes = new WeakSet();
let isProcessingComplete = false;
function removeFullPageBlur() {
setTimeout(() => {
document.documentElement.classList.add('full-page-unblur');
document.documentElement.classList.remove('full-page-blur');
}, 100);
}
function checkAndRemoveInitialBlur() {
if (isProcessingComplete && isFullPageBlurred) {
removeFullPageBlur();
isFullPageBlurred = false;
}
}
function toggleFullPageBlur() {
if (isFullPageBlurred) {
removeFullPageBlur();
isFullPageBlurred = false;
} else {
document.documentElement.classList.remove('full-page-unblur');
document.documentElement.classList.add('full-page-blur');
isFullPageBlurred = true;
}
}
function updateBlurStyles() {
GM_addStyle(`
.word-blur {
filter: blur(${wordBlurStrength}px) !important;
transition: filter ${wordunblurTransitionSpeed}s ease !important;
}
.word-blur:hover {
filter: blur(0px) !important;
transition-delay: ${hoverDelay}s !important;
}
.full-page-blur {
filter: blur(${pageBlurStrength}px) !important;
}
`);
}
function fetchIP() {
const ipDisplay = document.getElementById('blur-ip-display');
if (ipDisplay) {
ipDisplay.style.filter = 'blur(0)';
ipDisplay.style.opacity = '0.5';
}
GM_xmlhttpRequest({
method: 'GET',
url: 'https://api.ipify.org?format=json',
onload: function(response) {
try {
const data = JSON.parse(response.responseText);
if (data.ip) {
userIP = data.ip;
GM_setValue('userIP', userIP);
if (ipDisplay) {
ipDisplay.textContent = `Aktuelle IP: ${userIP}`;
ipDisplay.style.opacity = '1';
}
}
} catch (e) {
if (ipDisplay) {
ipDisplay.style.opacity = '1';
}
}
},
onerror: function() {
if (ipDisplay) {
ipDisplay.style.opacity = '1';
}
}
});
}
function createWordEntry(word = '') {
const entry = document.createElement('div');
entry.className = 'blur-word-entry';
const input = document.createElement('input');
input.type = 'text';
input.className = 'blur-word-input';
input.value = word;
const deleteBtn = document.createElement('button');
deleteBtn.className = 'blur-delete-button';
deleteBtn.innerHTML = '×';
deleteBtn.onclick = () => entry.remove();
entry.appendChild(input);
entry.appendChild(deleteBtn);
return entry;
}
function createSettings() {
const panel = document.createElement('div');
panel.id = 'blur-settings-panel';
const version = GM_info.script.version;
panel.innerHTML = `
<div id="blur-settings-header">
<h3 style="margin:0">Wörter zum Ausblenden (v${version})</h3>
<button id="blur-close-button">×</button>
</div>
<div id="blur-section-container">
<div id="blur-ip-info">
Tipp: Mache ein Doppelkick auf das Einstellungs-Symbol um die Seite zu Bluren. Mit Escape kannst du das Bluren der Seite und das UI wieder Schließen.
</div>
<div class="blur-section">
<div class="blur-section-header">
<span>Allgemeine Einstellungen</span>
<span class="blur-section-arrow">▼</span>
</div>
<div class="blur-section-content">
<div id="blur-text-info">
Passe hier deine Blurstärke und Animationszeiten ein.
</div>
<div class="blur-slider-container">
<label class="blur-slider-label">Wort Blur-Stärke (5px - 10px)</label>
<input type="range" min="5" max="10" step="0.5" value="${wordBlurStrength}"
class="blur-slider" id="word-blur-slider">
<span class="blur-slider-value">${wordBlurStrength}px</span>
</div>
<div class="blur-slider-container">
<label class="blur-slider-label">Hover verzögerung (0.5s - 1.5s)</label>
<input type="range" min="0.5" max="1.5" step="0.1" value="${hoverDelay}"
class="blur-slider" id="hover-delay-slider">
<span class="blur-slider-value">${hoverDelay}s</span>
</div>
<div class="blur-slider-container">
<label class="blur-slider-label">Wort unblur Übergangszeit (0.5s - 2.5s)</label>
<input type="range" min="0.5" max="2.5" step="0.1" value="${wordunblurTransitionSpeed}"
class="blur-slider" id="transition-slider">
<span class="blur-slider-value">${wordunblurTransitionSpeed}s</span>
</div>
<div class="blur-option-container">
<label class="blur-checkbox-label">
<input type="checkbox" class="blur-checkbox" id="blur-default-setting"
${defaultFullPageBlur ? 'checked' : ''}>
Seite standardmäßig beim Start blurren
</label>
</div>
<div class="blur-slider-container">
<label class="blur-slider-label">Seiten Blur-Stärke (5px - 15px)</label>
<input type="range" min="5" max="15" step="0.5" value="${pageBlurStrength}"
class="blur-slider" id="page-blur-slider">
<span class="blur-slider-value">${pageBlurStrength}px</span>
</div>
</div>
</div>
<div class="blur-section">
<div class="blur-section-header">
<span>IP-Einstellungen</span>
<span class="blur-section-arrow">▼</span>
</div>
<div class="blur-section-content">
<div id="blur-text-info">
Ermittel erst deine IP und Aktiviere die Option wenn du deine IP Bluren möchtest.<br>Das Ermitteln funktuiniert nicht automatisch!
</div>
<div id="blur-ip-info">
<span id="blur-ip-display">Aktuelle IP: ${userIP || 'Nicht ermittelt'}</span>
<button id="blur-ip-button">IP ermitteln</button>
</div>
<div class="blur-option-container">
<label class="blur-checkbox-label">
<input type="checkbox" class="blur-checkbox" id="ip-blur-setting"
${isIPBlurEnabled ? 'checked' : ''}>
IP-Blur aktivieren
</label>
</div>
</div>
</div>
<div class="blur-section">
<div class="blur-section-header">
<span>Wortliste</span>
<span class="blur-section-arrow">▼</span>
</div>
<div class="blur-section-content">
<div id="blur-text-info">
Füge hier deine Wörter hinzu die du Bluren möchtest.<br>Drücke auf "+ Neues Wort hinzufügen" um ein neues Feld zu erstellen.<br>Nur ein Wort pro Feld!
</div>
<div id="blur-word-list"></div>
<button class="blur-add-button">+ Neues Wort hinzufügen</button>
</div>
</div>
</div>
<div class="blur-button-container">
<button class="blur-button blur-cancel-button" id="blur-cancel">Abbrechen</button>
<button class="blur-button blur-save-button" id="blur-save">Speichern</button>
</div>
`;
document.body.appendChild(panel);
const wordList = panel.querySelector('#blur-word-list');
const addButton = panel.querySelector('.blur-add-button');
const ipButton = panel.querySelector('#blur-ip-button');
const sections = panel.querySelectorAll('.blur-section');
sections.forEach(section => {
const header = section.querySelector('.blur-section-header');
header.addEventListener('click', () => {
section.classList.toggle('expanded');
});
});
const sliders = panel.querySelectorAll('.blur-slider');
sliders.forEach(slider => {
const valueDisplay = slider.nextElementSibling;
slider.addEventListener('input', () => {
const unit = slider.id.includes('delay') || slider.id.includes('transition') ? 's' : 'px';
valueDisplay.textContent = `${slider.value}${unit}`;
});
});
addButton.onclick = () => {
wordList.appendChild(createWordEntry());
};
ipButton.onclick = fetchIP;
const toggleBtn = document.createElement('button');
toggleBtn.id = 'blur-toggle-button';
document.body.appendChild(toggleBtn);
let isDragging = false;
let currentX;
let currentY;
let initialX;
let initialY;
let xOffset = 0;
let yOffset = 0;
const dragStart = (e) => {
initialX = e.clientX - xOffset;
initialY = e.clientY - yOffset;
if (e.target.closest('#blur-settings-header')) {
isDragging = true;
}
};
function adjustPanelPosition() {
const panelRect = panel.getBoundingClientRect();
const viewportWidth = window.innerWidth;
const viewportHeight = window.innerHeight;
let needsAdjustment = false;
let newX = xOffset;
let newY = yOffset;
const panelCenterX = (panelRect.left + panelRect.right) / 2;
const panelCenterY = (panelRect.top + panelRect.bottom) / 2;
if (panelRect.right > viewportWidth) {
newX = viewportWidth / 2 - panelRect.width / 2 - 20;
needsAdjustment = true;
} else if (panelRect.left < 0) {
newX = -viewportWidth / 2 + panelRect.width / 2 + 20;
needsAdjustment = true;
}
if (panelRect.bottom > viewportHeight) {
newY = viewportHeight / 2 - panelRect.height / 2 - 20;
needsAdjustment = true;
} else if (panelRect.top < 0) {
newY = -viewportHeight / 2 + panelRect.height / 2 + 20;
needsAdjustment = true;
}
if (needsAdjustment) {
panel.style.transition = 'transform 0.3s ease-out';
xOffset = newX;
yOffset = newY;
panel.style.transform = `translate(calc(-50% + ${newX}px), calc(-50% + ${newY}px))`;
setTimeout(() => {
panel.style.transition = '';
}, 300);
}
}
const resizeObserver = new ResizeObserver(() => {
adjustPanelPosition();
});
resizeObserver.observe(panel);
window.addEventListener('resize', adjustPanelPosition);
const drag = (e) => {
if (isDragging) {
e.preventDefault();
currentX = e.clientX - initialX;
currentY = e.clientY - initialY;
xOffset = currentX;
yOffset = currentY;
panel.style.transform = `translate(calc(-50% + ${currentX}px), calc(-50% + ${currentY}px))`;
}
};
const dragEnd = () => {
isDragging = false;
adjustPanelPosition();
};
document.addEventListener('mousedown', dragStart);
document.addEventListener('mousemove', drag);
document.addEventListener('mouseup', dragEnd);
function showPanel() {
panel.style.display = 'block';
setTimeout(() => {
panel.classList.add('visible');
}, 10);
panel.style.transform = 'translate(-50%, -50%)';
xOffset = 0;
yOffset = 0;
wordList.innerHTML = '';
blurWords.forEach(word => {
wordList.appendChild(createWordEntry(word));
});
}
function hidePanel() {
panel.classList.remove('visible');
setTimeout(() => {
panel.style.display = 'none';
}, 300);
}
function handleClick() {
if (clickTimeout !== null) {
clearTimeout(clickTimeout);
clickTimeout = null;
toggleFullPageBlur();
} else {
clickTimeout = setTimeout(() => {
clickTimeout = null;
showPanel();
}, 250);
}
}
function saveSettings() {
const inputs = panel.querySelectorAll('.blur-word-input');
const newWords = [...new Set([...inputs].map(input => input.value.trim()).filter(w => w))];
const pageBlurSlider = document.getElementById('page-blur-slider');
const wordBlurSlider = document.getElementById('word-blur-slider');
const hoverDelaySlider = document.getElementById('hover-delay-slider');
const transitionSlider = document.getElementById('transition-slider');
const defaultBlurCheckbox = document.getElementById('blur-default-setting');
const ipBlurCheckbox = document.getElementById('ip-blur-setting');
pageBlurStrength = parseFloat(pageBlurSlider.value);
wordBlurStrength = parseFloat(wordBlurSlider.value);
hoverDelay = parseFloat(hoverDelaySlider.value);
wordunblurTransitionSpeed = parseFloat(transitionSlider.value);
defaultFullPageBlur = defaultBlurCheckbox.checked;
isIPBlurEnabled = ipBlurCheckbox.checked;
GM_setValue('blurWords', newWords);
GM_setValue('hoverDelay', hoverDelay);
GM_setValue('pageBlurStrength', pageBlurStrength);
GM_setValue('wordBlurStrength', wordBlurStrength);
GM_setValue('wordunblurTransitionSpeed', wordunblurTransitionSpeed);
GM_setValue('defaultFullPageBlur', defaultFullPageBlur);
GM_setValue('isIPBlurEnabled', isIPBlurEnabled);
blurWords = newWords;
updateBlurStyles();
hidePanel();
location.reload();
}
function handleKeyPress(e) {
if (e.key === 'Escape') {
if (panel.style.display === 'block') {
hidePanel();
} else if (isFullPageBlurred) {
removeFullPageBlur();
isFullPageBlurred = false;
}
}
}
toggleBtn.addEventListener('click', handleClick);
document.getElementById('blur-close-button').addEventListener('click', hidePanel);
document.getElementById('blur-cancel').addEventListener('click', hidePanel);
document.getElementById('blur-save').addEventListener('click', saveSettings);
document.addEventListener('keydown', handleKeyPress);
}
function findBlurPositions(text) {
const positions = [];
blurWords.forEach(word => {
const escapedWord = word.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
const regex = new RegExp(escapedWord, 'gi');
let match;
while ((match = regex.exec(text)) !== null) {
positions.push({
start: match.index,
end: match.index + match[0].length,
word: match[0]
});
}
});
if (isIPBlurEnabled && userIP) {
const ipRegex = new RegExp(userIP.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'g');
let match;
while ((match = ipRegex.exec(text)) !== null) {
positions.push({
start: match.index,
end: match.index + match[0].length,
word: match[0]
});
}
}
positions.sort((a, b) => a.start - b.start);
const mergedPositions = [];
let current = null;
for (const pos of positions) {
if (!current) {
current = {...pos};
} else if (pos.start <= current.end) {
current.end = Math.max(current.end, pos.end);
current.word = text.slice(current.start, current.end);
} else {
mergedPositions.push(current);
current = {...pos};
}
}
if (current) {
mergedPositions.push(current);
}
return mergedPositions;
}
function isProcessed(node) {
if (!node || !node.parentElement) return true;
const isInput = node.parentElement.closest('input, textarea, [contenteditable="true"]');
if (isInput) return true;
const isBlurSettings = node.parentElement.closest('#blur-settings-panel, #blur-ip-display');
if (isBlurSettings) return true;
return processedNodes.has(node) ||
node.parentElement.classList.contains('word-blur') ||
node.parentElement.hasAttribute('data-blurred');
}
function blurText(node) {
if (isProcessed(node)) return;
const text = node.textContent;
if (!text.trim()) {
processedNodes.add(node);
return;
}
const positions = findBlurPositions(text);
if (positions.length === 0) {
processedNodes.add(node);
return;
}
const container = document.createElement('span');
container.setAttribute('data-blurred', 'true');
let lastIndex = 0;
positions.forEach(pos => {
if (pos.start > lastIndex) {
container.appendChild(document.createTextNode(
text.slice(lastIndex, pos.start)
));
}
const blurSpan = document.createElement('span');
blurSpan.className = 'word-blur';
blurSpan.textContent = pos.word;
container.appendChild(blurSpan);
lastIndex = pos.end;
});
if (lastIndex < text.length) {
container.appendChild(document.createTextNode(
text.slice(lastIndex)
));
}
node.parentNode.replaceChild(container, node);
processedNodes.add(container);
}
function processNodes(root) {
const walker = document.createTreeWalker(
root,
NodeFilter.SHOW_TEXT,
{
acceptNode: (node) => {
if (node.parentElement.closest('#blur-settings-panel')) return NodeFilter.FILTER_REJECT;
if (node.parentElement.closest('script')) return NodeFilter.FILTER_REJECT;
if (node.parentElement.closest('style')) return NodeFilter.FILTER_REJECT;
if (node.parentElement.closest('input, textarea, [contenteditable="true"]')) return NodeFilter.FILTER_REJECT;
if (isProcessed(node)) return NodeFilter.FILTER_REJECT;
return NodeFilter.FILTER_ACCEPT;
}
}
);
const nodes = [];
while (walker.nextNode()) nodes.push(walker.currentNode);
let processedCount = 0;
const totalNodes = nodes.length;
function processBatch(startIndex) {
const endIndex = Math.min(startIndex + 100, nodes.length);
const batch = nodes.slice(startIndex, endIndex);
batch.forEach(blurText);
processedCount += batch.length;
if (endIndex < nodes.length) {
requestAnimationFrame(() => processBatch(endIndex));
} else {
isProcessingComplete = true;
checkAndRemoveInitialBlur();
}
}
if (nodes.length > 0) {
processBatch(0);
} else {
isProcessingComplete = true;
checkAndRemoveInitialBlur();
}
}
const observer = new MutationObserver((mutations) => {
for (const mutation of mutations) {
if (mutation.type === 'childList') {
mutation.addedNodes.forEach(node => {
if (node.nodeType === 1) processNodes(node);
else if (node.nodeType === 3) blurText(node);
});
}
}
});
const initScript = document.createElement('script');
initScript.textContent = `
if (${defaultFullPageBlur}) {
document.documentElement.classList.add('full-page-blur');
}
`;
document.documentElement.appendChild(initScript);
function init() {
createSettings();
processNodes(document.body);
observer.observe(document.body, {
childList: true,
subtree: true
});
updateBlurStyles();
}
if (defaultFullPageBlur) {
document.documentElement.classList.add('full-page-blur');
isFullPageBlurred = true;
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
GM_registerMenuCommand('Wortliste bearbeiten', () => {
document.getElementById('blur-settings-panel').style.display = 'block';
const wordList = document.getElementById('blur-word-list');
wordList.innerHTML = '';
blurWords.forEach(word => {
wordList.appendChild(createWordEntry(word));
});
});
})();