Skip to content

Instantly share code, notes, and snippets.

@watermusic
Last active October 27, 2025 13:10
Show Gist options
  • Select an option

  • Save watermusic/9232d4fe21fcee14219f9892542aea93 to your computer and use it in GitHub Desktop.

Select an option

Save watermusic/9232d4fe21fcee14219f9892542aea93 to your computer and use it in GitHub Desktop.
JSON Syntax Validator
// ==UserScript==
// @name JSON Syntax Validator
// @namespace http://tampermonkey.net/
// @version 1.0.2
// @description Validiert JSON-Syntax in spezifischen PIM Textarea-/Input Feldern
// @match https://pim.ppapi.de/*
// @updateURL https://gist.githubusercontent.com/watermusic/9232d4fe21fcee14219f9892542aea93/raw/ff457d4107e4a0f2b254663a65864409c51f8d6b/json-validator.pim.js
// @downloadURL https://gist.githubusercontent.com/watermusic/9232d4fe21fcee14219f9892542aea93/raw/ff457d4107e4a0f2b254663a65864409c51f8d6b/json-validator.pim.js
// @grant none
// ==/UserScript==
(function() {
'use strict';
// Konfiguration: Liste der Selektoren für Textareas die validiert werden sollen
const TEXTAREA_SELECTORS = [
'div[data-attribute="graduated_price"] textarea',
'div[data-attribute="design_picker_attributes"] input',
'div[data-attribute="print_placements"] textarea',
'div[data-attribute="printess_start_design"] input',
'div[data-attribute="cpp_start_design_id"] textarea',
'div[data-attribute="printess_layout_snippet"] input',
'div[data-attribute="printess_group_snippets"] input',
'div[data-attribute="mockup_form_fields"] textarea',
'div[data-attribute="supplier_production_parameters"] textarea',
];
// CSS Styles hinzufügen
const style = document.createElement('style');
style.textContent = `
.json-valid {
border: 3px solid #28a745 !important;
box-shadow: 0 0 5px rgba(40, 167, 69, 0.5) !important;
}
.json-invalid {
border: 3px solid #dc3545 !important;
box-shadow: 0 0 5px rgba(220, 53, 69, 0.5) !important;
}
.json-error-tooltip {
position: absolute;
background: #dc3545;
color: white;
padding: 8px 12px;
border-radius: 4px;
font-size: 12px;
z-index: 10000;
max-width: 300px;
box-shadow: 0 2px 8px rgba(0,0,0,0.3);
}
.json-error-tooltip::before {
content: '';
position: absolute;
top: -5px;
left: 10px;
border-left: 5px solid transparent;
border-right: 5px solid transparent;
border-bottom: 5px solid #dc3545;
}
`;
document.head.appendChild(style);
function validateJSON(textarea) {
// Vorherige Klassen entfernen
textarea.classList.remove('json-valid', 'json-invalid');
// Bestehende Tooltips entfernen
const existingTooltip = document.querySelector('.json-error-tooltip');
if (existingTooltip) {
existingTooltip.remove();
}
const content = textarea.value.trim();
if (!content) {
return; // Leere Felder nicht validieren
}
try {
// JSON parsen - das reicht für Syntax-Validierung
const jsonData = JSON.parse(content);
textarea.classList.add('json-valid');
console.log('✅ JSON Syntax ist korrekt:', jsonData);
} catch (parseError) {
// JSON Parse Fehler
textarea.classList.add('json-invalid');
// Detaillierte Fehlermeldung erstellen
let errorMessage = parseError.message;
// Häufige Fehler benutzerfreundlicher machen
if (errorMessage.includes('Unexpected token')) {
if (errorMessage.includes('Unexpected token ,')) {
errorMessage = 'Überflüssiges Komma gefunden';
} else if (errorMessage.includes('Unexpected token }')) {
errorMessage = 'Fehlendes Komma vor der schließenden Klammer';
} else if (errorMessage.includes('Unexpected token ]')) {
errorMessage = 'Fehlendes Komma vor der schließenden eckigen Klammer';
} else if (errorMessage.includes('Unexpected end')) {
errorMessage = 'JSON unvollständig - fehlende schließende Klammer';
}
} else if (errorMessage.includes('Unterminated string')) {
errorMessage = 'Unvollständiger String - fehlende Anführungszeichen';
} else if (errorMessage.includes('Expected property name')) {
errorMessage = 'Property-Name erwartet (in Anführungszeichen)';
}
showErrorTooltip(textarea, [{message: errorMessage}]);
console.log('❌ JSON Syntax Fehler:', parseError.message);
}
}
function showErrorTooltip(textarea, errors) {
const tooltip = document.createElement('div');
tooltip.className = 'json-error-tooltip';
// Einfach die erste Fehlermeldung anzeigen
tooltip.textContent = errors[0].message;
// Position relativ zur Textarea
const rect = textarea.getBoundingClientRect();
tooltip.style.position = 'fixed';
tooltip.style.left = `${rect.left}px`;
tooltip.style.top = `${rect.top - 40}px`;
document.body.appendChild(tooltip);
// Tooltip nach 5 Sekunden automatisch entfernen
setTimeout(() => {
if (tooltip.parentNode) {
tooltip.remove();
}
}, 5000);
}
function setupValidation() {
let textareas = [];
// Textareas anhand der definierten Selektoren finden
TEXTAREA_SELECTORS.forEach(selector => {
const found = document.querySelectorAll(selector);
found.forEach(textarea => {
if (!textareas.includes(textarea)) {
textareas.push(textarea);
}
});
});
textareas.forEach(textarea => {
// Event Listener für Echtzeit-Validierung
textarea.addEventListener('input', debounce(() => {
validateJSON(textarea);
}, 500));
// Event Listener für Blur (wenn Feld verlassen wird)
textarea.addEventListener('blur', () => {
validateJSON(textarea);
});
// Initial validieren falls schon Inhalt vorhanden
if (textarea.value.trim()) {
validateJSON(textarea);
}
});
console.log(`🔍 JSON Syntax Validator aktiv für ${textareas.length} spezifische Textarea(s)`);
if (textareas.length === 0) {
console.warn('⚠️ Keine Textareas mit den angegebenen Selektoren gefunden!');
console.log('Gesuchte Selektoren:', TEXTAREA_SELECTORS);
}
}
function setupListener() {
// Event-Listener für alle Klicks auf der Seite
document.addEventListener('click', function(e) {
// Hier kannst du spezifische Elemente filtern
// z.B. nur <p>, <span>, <div> mit einer bestimmten Klasse
const target = e.target;
// Beispiel: Nur Elemente mit Klasse 'copyable' kopieren
if (!target.classList.contains('breadcrumb-item')) {
return;
}
const pimClass = "fouxYE";
target.classList.remove(pimClass);
const text = target.innerText || target.textContent;
target.classList.add(pimClass);
if (text) {
// Moderne Clipboard API
navigator.clipboard.writeText(text).then(function() {
console.log('Text kopiert:', text);
// Optionales visuelles Feedback
const originalBg = target.style.backgroundColor;
target.style.backgroundColor = '#90EE90';
setTimeout(() => {
target.style.backgroundColor = originalBg;
}, 200);
}).catch(function(err) {
console.error('Fehler beim Kopieren:', err);
});
}
});
}
// Debounce Funktion um zu häufige Validierung zu vermeiden
function debounce(func, wait) {
let timeout;
return function executedFunction(...args) {
const later = () => {
clearTimeout(timeout);
func(...args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
}
// Auf dynamisch hinzugefügte Textareas reagieren
function observeDOM() {
const observer = new MutationObserver(mutations => {
mutations.forEach(mutation => {
mutation.addedNodes.forEach(node => {
if (node.nodeType === Node.ELEMENT_NODE) {
// Prüfen ob hinzugefügte Nodes unsere Selektoren matchen
TEXTAREA_SELECTORS.forEach(selector => {
let matchingElements = [];
if (node.matches && node.matches(selector)) {
matchingElements.push(node);
}
if (node.querySelectorAll) {
const found = node.querySelectorAll(selector);
matchingElements.push(...found);
}
matchingElements.forEach(setupValidationForElement);
});
}
});
});
});
observer.observe(document.body, {
childList: true,
subtree: true
});
}
function setupValidationForElement(textarea) {
// Verhindere doppelte Event Listener
if (textarea.dataset.jsonValidatorActive) return;
textarea.dataset.jsonValidatorActive = 'true';
textarea.addEventListener('input', debounce(() => {
validateJSON(textarea);
}, 500));
textarea.addEventListener('blur', () => {
validateJSON(textarea);
});
if (textarea.value.trim()) {
validateJSON(textarea);
}
}
// Warten bis DOM geladen ist
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', () => {
setupValidation();
setupListener();
observeDOM();
});
} else {
setupValidation();
setupListener();
observeDOM();
}
// Konsolen-Befehle für manuelle Kontrolle
window.validateAllTextareas = () => {
document.querySelectorAll('textarea').forEach(validateJSON);
};
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment