- Added symbols/ folder (previously separate repo) - Removed symbols/ from .gitignore - Updated CLAUDE.md documentation - Deleted separate Symbols repo on Gitea 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1155 lines
44 KiB
JavaScript
1155 lines
44 KiB
JavaScript
|
|
// ========== TEXT-GENERATOR v5 ==========
|
|
// Features: Separate Padding pro Kante, Reset-Button, Einzelner Pfeil-Export, Pfeilspitzenlaenge
|
|
|
|
var textGenDefaults = {
|
|
text: '',
|
|
fontSize: 16,
|
|
textColor: '#000000',
|
|
frameColor: '#000000',
|
|
shape: 'none',
|
|
frameScale: 100,
|
|
paddingTop: 10,
|
|
paddingRight: 10,
|
|
paddingBottom: 10,
|
|
paddingLeft: 10,
|
|
lineStyle: 'solid',
|
|
lineWeight: 2,
|
|
arrow: 'none',
|
|
arrowLength: 40,
|
|
arrowAngle: 0,
|
|
arrowBend: 50,
|
|
arrowSize: 10,
|
|
arrowTipLength: 15
|
|
};
|
|
|
|
var textGenState = JSON.parse(JSON.stringify(textGenDefaults));
|
|
|
|
// Custom Symbols aus localStorage
|
|
var customSymbols = [];
|
|
|
|
function initTextGenerator() {
|
|
loadCustomSymbols();
|
|
|
|
var textInput = document.getElementById('customText');
|
|
var fontSizeInput = document.getElementById('fontSize');
|
|
var fontSizeValue = document.getElementById('fontSizeValue');
|
|
var textColorInput = document.getElementById('textColor');
|
|
var textColorValue = document.getElementById('textColorValue');
|
|
var frameColorInput = document.getElementById('frameColor');
|
|
var frameColorValue = document.getElementById('frameColorValue');
|
|
var frameScaleInput = document.getElementById('frameScale');
|
|
var frameScaleValue = document.getElementById('frameScaleValue');
|
|
|
|
// Gesamt-Padding Input
|
|
var paddingAllInput = document.getElementById("paddingAll");
|
|
var paddingAllValueEl = document.getElementById("paddingAllValue");
|
|
|
|
// Separate Padding Inputs
|
|
var paddingTopInput = document.getElementById('paddingTop');
|
|
var paddingTopValue = document.getElementById('paddingTopValue');
|
|
var paddingRightInput = document.getElementById('paddingRight');
|
|
var paddingRightValue = document.getElementById('paddingRightValue');
|
|
var paddingBottomInput = document.getElementById('paddingBottom');
|
|
var paddingBottomValue = document.getElementById('paddingBottomValue');
|
|
var paddingLeftInput = document.getElementById('paddingLeft');
|
|
var paddingLeftValue = document.getElementById('paddingLeftValue');
|
|
|
|
var arrowLengthInput = document.getElementById('arrowLength');
|
|
var arrowLengthValue = document.getElementById('arrowLengthValue');
|
|
var arrowAngleInput = document.getElementById('arrowAngle');
|
|
var arrowAngleValue = document.getElementById('arrowAngleValue');
|
|
var arrowBendInput = document.getElementById('arrowBend');
|
|
var arrowBendValue = document.getElementById('arrowBendValue');
|
|
var arrowSizeInput = document.getElementById('arrowSize');
|
|
var arrowSizeValue = document.getElementById('arrowSizeValue');
|
|
var arrowTipLengthInput = document.getElementById('arrowTipLength');
|
|
var arrowTipLengthValue = document.getElementById('arrowTipLengthValue');
|
|
|
|
// Text input
|
|
if (textInput) {
|
|
textInput.addEventListener('input', function(e) {
|
|
textGenState.text = e.target.value;
|
|
updateTextPreview();
|
|
});
|
|
}
|
|
|
|
// Font size
|
|
if (fontSizeInput) {
|
|
fontSizeInput.addEventListener('input', function(e) {
|
|
textGenState.fontSize = parseInt(e.target.value);
|
|
fontSizeValue.textContent = e.target.value + 'px';
|
|
updateTextPreview();
|
|
});
|
|
}
|
|
|
|
// Text color
|
|
if (textColorInput) {
|
|
textColorInput.addEventListener('input', function(e) {
|
|
textGenState.textColor = e.target.value;
|
|
textColorValue.textContent = e.target.value;
|
|
updateTextPreview();
|
|
});
|
|
}
|
|
|
|
// Frame color
|
|
if (frameColorInput) {
|
|
frameColorInput.addEventListener('input', function(e) {
|
|
textGenState.frameColor = e.target.value;
|
|
frameColorValue.textContent = e.target.value;
|
|
updateTextPreview();
|
|
});
|
|
}
|
|
|
|
// Frame scale
|
|
if (frameScaleInput) {
|
|
frameScaleInput.addEventListener('input', function(e) {
|
|
textGenState.frameScale = parseInt(e.target.value);
|
|
frameScaleValue.textContent = e.target.value + '%';
|
|
updateTextPreview();
|
|
});
|
|
}
|
|
|
|
// Gesamt-Padding handler (setzt alle 4 Seiten)
|
|
if (paddingAllInput) {
|
|
paddingAllInput.addEventListener("input", function(e) {
|
|
var val = parseInt(e.target.value);
|
|
textGenState.paddingTop = val;
|
|
textGenState.paddingRight = val;
|
|
textGenState.paddingBottom = val;
|
|
textGenState.paddingLeft = val;
|
|
paddingAllValueEl.textContent = val + "px";
|
|
// Update einzelne Slider
|
|
if (paddingTopInput) { paddingTopInput.value = val; paddingTopValue.textContent = val + "px"; }
|
|
if (paddingRightInput) { paddingRightInput.value = val; paddingRightValue.textContent = val + "px"; }
|
|
if (paddingBottomInput) { paddingBottomInput.value = val; paddingBottomValue.textContent = val + "px"; }
|
|
if (paddingLeftInput) { paddingLeftInput.value = val; paddingLeftValue.textContent = val + "px"; }
|
|
updateTextPreview();
|
|
});
|
|
}
|
|
|
|
// Separate Padding handlers
|
|
if (paddingTopInput) {
|
|
paddingTopInput.addEventListener('input', function(e) {
|
|
textGenState.paddingTop = parseInt(e.target.value);
|
|
paddingTopValue.textContent = e.target.value + 'px';
|
|
updateTextPreview();
|
|
});
|
|
}
|
|
if (paddingRightInput) {
|
|
paddingRightInput.addEventListener('input', function(e) {
|
|
textGenState.paddingRight = parseInt(e.target.value);
|
|
paddingRightValue.textContent = e.target.value + 'px';
|
|
updateTextPreview();
|
|
});
|
|
}
|
|
if (paddingBottomInput) {
|
|
paddingBottomInput.addEventListener('input', function(e) {
|
|
textGenState.paddingBottom = parseInt(e.target.value);
|
|
paddingBottomValue.textContent = e.target.value + 'px';
|
|
updateTextPreview();
|
|
});
|
|
}
|
|
if (paddingLeftInput) {
|
|
paddingLeftInput.addEventListener('input', function(e) {
|
|
textGenState.paddingLeft = parseInt(e.target.value);
|
|
paddingLeftValue.textContent = e.target.value + 'px';
|
|
updateTextPreview();
|
|
});
|
|
}
|
|
|
|
// Arrow length
|
|
if (arrowLengthInput) {
|
|
arrowLengthInput.addEventListener('input', function(e) {
|
|
textGenState.arrowLength = parseInt(e.target.value);
|
|
arrowLengthValue.textContent = e.target.value + 'px';
|
|
updateTextPreview();
|
|
try { updateStandaloneArrowPreview(); } catch(e) { console.log("Standalone arrow preview error:", e); }
|
|
});
|
|
}
|
|
|
|
// Arrow angle
|
|
if (arrowAngleInput) {
|
|
arrowAngleInput.addEventListener('input', function(e) {
|
|
textGenState.arrowAngle = parseInt(e.target.value);
|
|
arrowAngleValue.textContent = e.target.value + '\u00B0';
|
|
updateTextPreview();
|
|
try { updateStandaloneArrowPreview(); } catch(e) { console.log("Standalone arrow preview error:", e); }
|
|
});
|
|
}
|
|
|
|
// Arrow bend position
|
|
if (arrowBendInput) {
|
|
arrowBendInput.addEventListener('input', function(e) {
|
|
textGenState.arrowBend = parseInt(e.target.value);
|
|
arrowBendValue.textContent = e.target.value + '%';
|
|
updateTextPreview();
|
|
try { updateStandaloneArrowPreview(); } catch(e) { console.log("Standalone arrow preview error:", e); }
|
|
});
|
|
}
|
|
|
|
// Arrow size (width)
|
|
if (arrowSizeInput) {
|
|
arrowSizeInput.addEventListener('input', function(e) {
|
|
textGenState.arrowSize = parseInt(e.target.value);
|
|
arrowSizeValue.textContent = e.target.value + 'px';
|
|
updateTextPreview();
|
|
try { updateStandaloneArrowPreview(); } catch(e) { console.log("Standalone arrow preview error:", e); }
|
|
});
|
|
}
|
|
|
|
// Arrow tip length (laenger = schmaler)
|
|
if (arrowTipLengthInput) {
|
|
arrowTipLengthInput.addEventListener('input', function(e) {
|
|
textGenState.arrowTipLength = parseInt(e.target.value);
|
|
arrowTipLengthValue.textContent = e.target.value + 'px';
|
|
updateTextPreview();
|
|
try { updateStandaloneArrowPreview(); } catch(e) { console.log("Standalone arrow preview error:", e); }
|
|
});
|
|
}
|
|
|
|
// Shape buttons
|
|
document.querySelectorAll('.shape-btn').forEach(function(btn) {
|
|
btn.addEventListener('click', function() {
|
|
document.querySelectorAll('.shape-btn').forEach(function(b) { b.classList.remove('active'); });
|
|
btn.classList.add('active');
|
|
textGenState.shape = btn.dataset.shape;
|
|
updateFrameScaleVisibility();
|
|
updateTextPreview();
|
|
});
|
|
});
|
|
|
|
// Line style buttons
|
|
document.querySelectorAll('.line-btn').forEach(function(btn) {
|
|
btn.addEventListener('click', function() {
|
|
document.querySelectorAll('.line-btn').forEach(function(b) { b.classList.remove('active'); });
|
|
btn.classList.add('active');
|
|
textGenState.lineStyle = btn.dataset.style;
|
|
updateTextPreview();
|
|
try { updateStandaloneArrowPreview(); } catch(e) { console.log("Standalone arrow preview error:", e); }
|
|
});
|
|
});
|
|
|
|
// Line weight buttons
|
|
document.querySelectorAll('.weight-btn').forEach(function(btn) {
|
|
btn.addEventListener('click', function() {
|
|
document.querySelectorAll('.weight-btn').forEach(function(b) { b.classList.remove('active'); });
|
|
btn.classList.add('active');
|
|
textGenState.lineWeight = parseInt(btn.dataset.weight);
|
|
updateTextPreview();
|
|
try { updateStandaloneArrowPreview(); } catch(e) { console.log("Standalone arrow preview error:", e); }
|
|
});
|
|
});
|
|
|
|
// Arrow buttons
|
|
document.querySelectorAll('.arrow-btn').forEach(function(btn) {
|
|
btn.addEventListener('click', function() {
|
|
document.querySelectorAll('.arrow-btn').forEach(function(b) { b.classList.remove('active'); });
|
|
btn.classList.add('active');
|
|
textGenState.arrow = btn.dataset.arrow;
|
|
updateArrowDetailsVisibility();
|
|
updateTextPreview();
|
|
});
|
|
});
|
|
|
|
updateFrameScaleVisibility();
|
|
updateArrowDetailsVisibility();
|
|
updateTextPreview();
|
|
try { updateStandaloneArrowPreview(); } catch(e) { console.log("Standalone arrow preview error:", e); }
|
|
}
|
|
|
|
// ========== RESET FUNCTION ==========
|
|
function resetToDefaults() {
|
|
textGenState = JSON.parse(JSON.stringify(textGenDefaults));
|
|
|
|
// Update all UI elements
|
|
var textInput = document.getElementById('customText');
|
|
if (textInput) textInput.value = '';
|
|
|
|
var fontSizeInput = document.getElementById('fontSize');
|
|
var fontSizeValue = document.getElementById('fontSizeValue');
|
|
if (fontSizeInput) fontSizeInput.value = textGenDefaults.fontSize;
|
|
if (fontSizeValue) fontSizeValue.textContent = textGenDefaults.fontSize + 'px';
|
|
|
|
var textColorInput = document.getElementById('textColor');
|
|
var textColorValue = document.getElementById('textColorValue');
|
|
if (textColorInput) textColorInput.value = textGenDefaults.textColor;
|
|
if (textColorValue) textColorValue.textContent = textGenDefaults.textColor;
|
|
|
|
var frameColorInput = document.getElementById('frameColor');
|
|
var frameColorValue = document.getElementById('frameColorValue');
|
|
if (frameColorInput) frameColorInput.value = textGenDefaults.frameColor;
|
|
if (frameColorValue) frameColorValue.textContent = textGenDefaults.frameColor;
|
|
|
|
var frameScaleInput = document.getElementById('frameScale');
|
|
var frameScaleValue = document.getElementById('frameScaleValue');
|
|
if (frameScaleInput) frameScaleInput.value = textGenDefaults.frameScale;
|
|
if (frameScaleValue) frameScaleValue.textContent = textGenDefaults.frameScale + '%';
|
|
|
|
// Reset Gesamt-Padding
|
|
var paddingAllInput = document.getElementById('paddingAll');
|
|
var paddingAllValue = document.getElementById('paddingAllValue');
|
|
if (paddingAllInput) paddingAllInput.value = textGenDefaults.paddingTop;
|
|
if (paddingAllValue) paddingAllValue.textContent = textGenDefaults.paddingTop + 'px';
|
|
|
|
// Reset separate padding
|
|
['Top', 'Right', 'Bottom', 'Left'].forEach(function(side) {
|
|
var input = document.getElementById('padding' + side);
|
|
var value = document.getElementById('padding' + side + 'Value');
|
|
var defaultVal = textGenDefaults['padding' + side];
|
|
if (input) input.value = defaultVal;
|
|
if (value) value.textContent = defaultVal + 'px';
|
|
});
|
|
|
|
var arrowLengthInput = document.getElementById('arrowLength');
|
|
var arrowLengthValue = document.getElementById('arrowLengthValue');
|
|
if (arrowLengthInput) arrowLengthInput.value = textGenDefaults.arrowLength;
|
|
if (arrowLengthValue) arrowLengthValue.textContent = textGenDefaults.arrowLength + 'px';
|
|
|
|
var arrowAngleInput = document.getElementById('arrowAngle');
|
|
var arrowAngleValue = document.getElementById('arrowAngleValue');
|
|
if (arrowAngleInput) arrowAngleInput.value = textGenDefaults.arrowAngle;
|
|
if (arrowAngleValue) arrowAngleValue.textContent = textGenDefaults.arrowAngle + '\u00B0';
|
|
|
|
var arrowBendInput = document.getElementById('arrowBend');
|
|
var arrowBendValue = document.getElementById('arrowBendValue');
|
|
if (arrowBendInput) arrowBendInput.value = textGenDefaults.arrowBend;
|
|
if (arrowBendValue) arrowBendValue.textContent = textGenDefaults.arrowBend + '%';
|
|
|
|
var arrowSizeInput = document.getElementById('arrowSize');
|
|
var arrowSizeValue = document.getElementById('arrowSizeValue');
|
|
if (arrowSizeInput) arrowSizeInput.value = textGenDefaults.arrowSize;
|
|
if (arrowSizeValue) arrowSizeValue.textContent = textGenDefaults.arrowSize + 'px';
|
|
|
|
var arrowTipLengthInput = document.getElementById('arrowTipLength');
|
|
var arrowTipLengthValue = document.getElementById('arrowTipLengthValue');
|
|
if (arrowTipLengthInput) arrowTipLengthInput.value = textGenDefaults.arrowTipLength;
|
|
if (arrowTipLengthValue) arrowTipLengthValue.textContent = textGenDefaults.arrowTipLength + 'px';
|
|
|
|
// Reset buttons
|
|
document.querySelectorAll('.shape-btn').forEach(function(btn) {
|
|
btn.classList.toggle('active', btn.dataset.shape === 'none');
|
|
});
|
|
document.querySelectorAll('.line-btn').forEach(function(btn) {
|
|
btn.classList.toggle('active', btn.dataset.style === 'solid');
|
|
});
|
|
document.querySelectorAll('.weight-btn').forEach(function(btn) {
|
|
btn.classList.toggle('active', btn.dataset.weight === '2');
|
|
});
|
|
document.querySelectorAll('.arrow-btn').forEach(function(btn) {
|
|
btn.classList.toggle('active', btn.dataset.arrow === 'none');
|
|
});
|
|
|
|
updateFrameScaleVisibility();
|
|
updateArrowDetailsVisibility();
|
|
updateTextPreview();
|
|
try { updateStandaloneArrowPreview(); } catch(e) { console.log("Standalone arrow preview error:", e); }
|
|
|
|
showNotification('Auf Standard zurueckgesetzt');
|
|
}
|
|
|
|
function updateFrameScaleVisibility() {
|
|
console.log("updateFrameScaleVisibility called, shape:", textGenState.shape);
|
|
var row = document.getElementById('frameScaleRow');
|
|
var paddingAllRow = document.getElementById('framePaddingAllRow');
|
|
var paddingRow = document.getElementById('framePaddingRow');
|
|
if (row) {
|
|
row.style.display = textGenState.shape === 'none' ? 'none' : 'flex';
|
|
}
|
|
if (paddingAllRow) {
|
|
paddingAllRow.style.display = textGenState.shape === 'none' ? 'none' : 'flex';
|
|
}
|
|
if (paddingRow) {
|
|
paddingRow.style.display = textGenState.shape === 'none' ? 'none' : 'flex';
|
|
}
|
|
}
|
|
|
|
function updateArrowDetailsVisibility() {
|
|
var row = document.getElementById('arrowDetailsRow');
|
|
var row2 = document.getElementById('arrowDetailsRow2');
|
|
if (row) {
|
|
row.style.display = textGenState.arrow === 'none' ? 'none' : 'flex';
|
|
}
|
|
if (row2) {
|
|
row2.style.display = textGenState.arrow === 'none' ? 'none' : 'flex';
|
|
}
|
|
}
|
|
|
|
function toggleTextGenerator() {
|
|
var generator = document.querySelector('.text-generator');
|
|
generator.classList.toggle('collapsed');
|
|
}
|
|
|
|
function getStrokeDasharray(style) {
|
|
switch(style) {
|
|
case 'dashed': return '8,4';
|
|
case 'dotted': return '2,3';
|
|
default: return 'none';
|
|
}
|
|
}
|
|
|
|
function generateTextSVG() {
|
|
var text = textGenState.text || 'Text';
|
|
var fontSize = textGenState.fontSize;
|
|
var textColor = textGenState.textColor;
|
|
var frameColor = textGenState.frameColor;
|
|
var shape = textGenState.shape;
|
|
var frameScale = textGenState.frameScale / 100;
|
|
var paddingTop = textGenState.paddingTop;
|
|
var paddingRight = textGenState.paddingRight;
|
|
var paddingBottom = textGenState.paddingBottom;
|
|
var paddingLeft = textGenState.paddingLeft;
|
|
var lineStyle = textGenState.lineStyle;
|
|
var lineWeight = textGenState.lineWeight;
|
|
var arrow = textGenState.arrow;
|
|
var arrowLength = textGenState.arrowLength;
|
|
var arrowAngle = textGenState.arrowAngle;
|
|
var arrowBend = textGenState.arrowBend / 100;
|
|
var arrowSize = textGenState.arrowSize;
|
|
var arrowTipLength = textGenState.arrowTipLength;
|
|
|
|
var lines = text.split('\n');
|
|
if (lines.length === 0) lines = ['Text'];
|
|
|
|
var maxLineLength = 0;
|
|
for (var i = 0; i < lines.length; i++) {
|
|
if (lines[i].length > maxLineLength) maxLineLength = lines[i].length;
|
|
}
|
|
if (maxLineLength === 0) maxLineLength = 4;
|
|
|
|
var charWidth = fontSize * 0.6;
|
|
var lineHeight = fontSize * 1.3;
|
|
var textWidth = maxLineLength * charWidth;
|
|
var textHeight = lines.length * lineHeight;
|
|
|
|
var frameWidth = (textWidth + paddingLeft + paddingRight) * frameScale;
|
|
var frameHeight = (textHeight + paddingTop + paddingBottom) * frameScale;
|
|
if (frameWidth < 40) frameWidth = 40;
|
|
if (frameHeight < 30) frameHeight = 30;
|
|
|
|
var arrowSpace = (arrow !== 'none') ? arrowLength + arrowTipLength + 5 : 0;
|
|
var svgWidth = frameWidth;
|
|
var svgHeight = frameHeight;
|
|
var offsetX = 0, offsetY = 0;
|
|
|
|
if (arrow === 'left') { svgWidth += arrowSpace; offsetX = arrowSpace; }
|
|
else if (arrow === 'right') { svgWidth += arrowSpace; }
|
|
else if (arrow === 'top') { svgHeight += arrowSpace; offsetY = arrowSpace; }
|
|
else if (arrow === 'bottom') { svgHeight += arrowSpace; }
|
|
|
|
var frameCx = offsetX + frameWidth / 2;
|
|
var frameCy = offsetY + frameHeight / 2;
|
|
|
|
// Text-Verschiebung durch asymmetrisches Padding
|
|
var textOffsetX = ((paddingLeft - paddingRight) / 2) * frameScale;
|
|
var textOffsetY = ((paddingTop - paddingBottom) / 2) * frameScale;
|
|
var textCx = frameCx + textOffsetX;
|
|
var textCy = frameCy + textOffsetY;
|
|
|
|
var dashArray = getStrokeDasharray(lineStyle);
|
|
var dashAttr = dashArray !== 'none' ? ' stroke-dasharray="' + dashArray + '"' : '';
|
|
|
|
var shapeSvg = '';
|
|
var shapeX = offsetX + lineWeight / 2;
|
|
var shapeY = offsetY + lineWeight / 2;
|
|
var shapeW = frameWidth - lineWeight;
|
|
var shapeH = frameHeight - lineWeight;
|
|
|
|
switch(shape) {
|
|
case 'rect':
|
|
shapeSvg = '<rect x="' + shapeX + '" y="' + shapeY + '" width="' + shapeW + '" height="' + shapeH + '" fill="none" stroke="' + frameColor + '" stroke-width="' + lineWeight + '"' + dashAttr + '/>';
|
|
break;
|
|
case 'square':
|
|
var squareSize = Math.max(shapeW, shapeH);
|
|
shapeSvg = '<rect x="' + (frameCx - squareSize/2) + '" y="' + (frameCy - squareSize/2) + '" width="' + squareSize + '" height="' + squareSize + '" fill="none" stroke="' + frameColor + '" stroke-width="' + lineWeight + '"' + dashAttr + '/>';
|
|
break;
|
|
case 'circle':
|
|
var radius = Math.max(shapeW, shapeH) / 2;
|
|
shapeSvg = '<circle cx="' + frameCx + '" cy="' + frameCy + '" r="' + (radius - lineWeight/2) + '" fill="none" stroke="' + frameColor + '" stroke-width="' + lineWeight + '"' + dashAttr + '/>';
|
|
break;
|
|
case 'oval':
|
|
shapeSvg = '<ellipse cx="' + frameCx + '" cy="' + frameCy + '" rx="' + (shapeW/2 - lineWeight/2) + '" ry="' + (shapeH/2 - lineWeight/2) + '" fill="none" stroke="' + frameColor + '" stroke-width="' + lineWeight + '"' + dashAttr + '/>';
|
|
break;
|
|
case 'diamond':
|
|
var halfW = shapeW / 2, halfH = shapeH / 2;
|
|
shapeSvg = '<polygon points="' + frameCx + ',' + (frameCy - halfH) + ' ' + (frameCx + halfW) + ',' + frameCy + ' ' + frameCx + ',' + (frameCy + halfH) + ' ' + (frameCx - halfW) + ',' + frameCy + '" fill="none" stroke="' + frameColor + '" stroke-width="' + lineWeight + '"' + dashAttr + '/>';
|
|
break;
|
|
}
|
|
|
|
var arrowSvg = '';
|
|
if (arrow !== 'none') {
|
|
arrowSvg = generateArrowPath(arrow, frameCx, frameCy, frameWidth, frameHeight, offsetX, offsetY, arrowLength, arrowAngle, arrowBend, frameColor, lineWeight, dashAttr, arrowSize, arrowTipLength);
|
|
}
|
|
|
|
var textSvg = '<text x="' + textCx + '" font-family="Arial, sans-serif" font-size="' + fontSize + '" fill="' + textColor + '" text-anchor="middle">';
|
|
var totalTextHeight = lines.length * lineHeight;
|
|
var startY = textCy - (totalTextHeight / 2) + (fontSize * 0.8);
|
|
|
|
for (var j = 0; j < lines.length; j++) {
|
|
var lineY = startY + (j * lineHeight);
|
|
var lineText = lines[j] || ' ';
|
|
lineText = lineText.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
|
textSvg += '<tspan x="' + textCx + '" y="' + lineY + '">' + lineText + '</tspan>';
|
|
}
|
|
textSvg += '</text>';
|
|
|
|
return '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ' + svgWidth + ' ' + svgHeight + '" width="' + svgWidth + '" height="' + svgHeight + '">' + shapeSvg + arrowSvg + textSvg + '</svg>';
|
|
}
|
|
|
|
|
|
function generateArrowPath(direction, cx, cy, frameWidth, frameHeight, offsetX, offsetY, length, angle, bendPos, color, weight, dashAttr, arrowSize, tipLength) {
|
|
var startX, startY, midX, midY, endX, endY;
|
|
var angleRad = angle * Math.PI / 180;
|
|
|
|
// Startpunkt am Rahmen
|
|
switch(direction) {
|
|
case 'top':
|
|
startX = cx;
|
|
startY = offsetY;
|
|
midX = startX;
|
|
midY = startY - length * bendPos;
|
|
endX = midX + Math.sin(angleRad) * (length * (1 - bendPos));
|
|
endY = midY - Math.cos(angleRad) * (length * (1 - bendPos));
|
|
break;
|
|
case 'bottom':
|
|
startX = cx;
|
|
startY = offsetY + frameHeight;
|
|
midX = startX;
|
|
midY = startY + length * bendPos;
|
|
endX = midX + Math.sin(angleRad) * (length * (1 - bendPos));
|
|
endY = midY + Math.cos(angleRad) * (length * (1 - bendPos));
|
|
break;
|
|
case 'left':
|
|
startX = offsetX;
|
|
startY = cy;
|
|
midX = startX - length * bendPos;
|
|
midY = startY;
|
|
endX = midX - Math.cos(angleRad) * (length * (1 - bendPos));
|
|
endY = midY + Math.sin(angleRad) * (length * (1 - bendPos));
|
|
break;
|
|
case 'right':
|
|
startX = offsetX + frameWidth;
|
|
startY = cy;
|
|
midX = startX + length * bendPos;
|
|
midY = startY;
|
|
endX = midX + Math.cos(angleRad) * (length * (1 - bendPos));
|
|
endY = midY + Math.sin(angleRad) * (length * (1 - bendPos));
|
|
break;
|
|
}
|
|
|
|
// Pfad erstellen
|
|
var pathD;
|
|
if (angle !== 0 && bendPos > 0 && bendPos < 1) {
|
|
pathD = 'M ' + startX + ' ' + startY + ' L ' + midX + ' ' + midY + ' L ' + endX + ' ' + endY;
|
|
} else {
|
|
pathD = 'M ' + startX + ' ' + startY + ' L ' + endX + ' ' + endY;
|
|
}
|
|
|
|
// Pfeilspitze: Richtung basiert auf dem LETZTEN Segment
|
|
var lastSegmentStartX, lastSegmentStartY;
|
|
if (angle !== 0 && bendPos > 0 && bendPos < 1) {
|
|
lastSegmentStartX = midX;
|
|
lastSegmentStartY = midY;
|
|
} else {
|
|
lastSegmentStartX = startX;
|
|
lastSegmentStartY = startY;
|
|
}
|
|
|
|
// Winkel des letzten Segments berechnen
|
|
var arrowHeadAngle = Math.atan2(endY - lastSegmentStartY, endX - lastSegmentStartX);
|
|
|
|
// Pfeilspitze Punkte - tipLength bestimmt wie lang (und damit schmaler) die Spitze ist
|
|
// arrowSize bestimmt die Breite an der Basis
|
|
// Je laenger tipLength, desto spitzer/schmaler wird der Pfeil
|
|
var tipHalfWidth = arrowSize / 2;
|
|
|
|
// Basis der Pfeilspitze (zurueck von der Spitze)
|
|
var baseX = endX - tipLength * Math.cos(arrowHeadAngle);
|
|
var baseY = endY - tipLength * Math.sin(arrowHeadAngle);
|
|
|
|
// Seitenpunkte der Pfeilspitze
|
|
var perpAngle = arrowHeadAngle + Math.PI / 2;
|
|
var ah1x = baseX + tipHalfWidth * Math.cos(perpAngle);
|
|
var ah1y = baseY + tipHalfWidth * Math.sin(perpAngle);
|
|
var ah2x = baseX - tipHalfWidth * Math.cos(perpAngle);
|
|
var ah2y = baseY - tipHalfWidth * Math.sin(perpAngle);
|
|
|
|
var arrowHead = '<polygon points="' + endX + ',' + endY + ' ' + ah1x + ',' + ah1y + ' ' + ah2x + ',' + ah2y + '" fill="' + color + '"/>';
|
|
|
|
return '<path d="' + pathD + '" fill="none" stroke="' + color + '" stroke-width="' + weight + '"' + dashAttr + '/>' + arrowHead;
|
|
}
|
|
|
|
// ========== STANDALONE ARROW (Einzelner loser Pfeil) ==========
|
|
|
|
function generateStandaloneArrowSVG() {
|
|
var length = textGenState.arrowLength;
|
|
var angle = textGenState.arrowAngle;
|
|
var bendPos = textGenState.arrowBend / 100;
|
|
var color = textGenState.frameColor;
|
|
var weight = textGenState.lineWeight;
|
|
var lineStyle = textGenState.lineStyle;
|
|
var arrowSize = textGenState.arrowSize;
|
|
var tipLength = textGenState.arrowTipLength;
|
|
|
|
var dashArray = getStrokeDasharray(lineStyle);
|
|
var dashAttr = dashArray !== 'none' ? ' stroke-dasharray="' + dashArray + '"' : '';
|
|
|
|
// Padding fuer SVG
|
|
var padding = Math.max(arrowSize, tipLength) + 5;
|
|
var angleRad = angle * Math.PI / 180;
|
|
|
|
// Pfeil geht von links nach rechts
|
|
var startX = padding;
|
|
var startY = padding + length / 2;
|
|
|
|
// Knickpunkt
|
|
var midX = startX + length * bendPos;
|
|
var midY = startY;
|
|
|
|
// Endpunkt mit Winkel
|
|
var endX, endY;
|
|
if (angle !== 0 && bendPos > 0 && bendPos < 1) {
|
|
endX = midX + Math.cos(angleRad) * (length * (1 - bendPos));
|
|
endY = midY - Math.sin(angleRad) * (length * (1 - bendPos));
|
|
} else {
|
|
endX = startX + length;
|
|
endY = startY;
|
|
}
|
|
|
|
// SVG Dimensionen berechnen
|
|
var minX = Math.min(startX, midX, endX) - padding;
|
|
var maxX = Math.max(startX, midX, endX) + padding + tipLength;
|
|
var minY = Math.min(startY, midY, endY) - padding - arrowSize;
|
|
var maxY = Math.max(startY, midY, endY) + padding + arrowSize;
|
|
|
|
var svgWidth = maxX - minX;
|
|
var svgHeight = maxY - minY;
|
|
|
|
// Offset korrigieren
|
|
var offsetX = -minX;
|
|
var offsetY = -minY;
|
|
|
|
startX += offsetX;
|
|
startY += offsetY;
|
|
midX += offsetX;
|
|
midY += offsetY;
|
|
endX += offsetX;
|
|
endY += offsetY;
|
|
|
|
// Pfad erstellen
|
|
var pathD;
|
|
if (angle !== 0 && bendPos > 0 && bendPos < 1) {
|
|
pathD = 'M ' + startX + ' ' + startY + ' L ' + midX + ' ' + midY + ' L ' + endX + ' ' + endY;
|
|
} else {
|
|
pathD = 'M ' + startX + ' ' + startY + ' L ' + endX + ' ' + endY;
|
|
}
|
|
|
|
// Pfeilspitze
|
|
var lastSegmentStartX, lastSegmentStartY;
|
|
if (angle !== 0 && bendPos > 0 && bendPos < 1) {
|
|
lastSegmentStartX = midX;
|
|
lastSegmentStartY = midY;
|
|
} else {
|
|
lastSegmentStartX = startX;
|
|
lastSegmentStartY = startY;
|
|
}
|
|
|
|
var arrowHeadAngle = Math.atan2(endY - lastSegmentStartY, endX - lastSegmentStartX);
|
|
|
|
var tipHalfWidth = arrowSize / 2;
|
|
var baseX = endX - tipLength * Math.cos(arrowHeadAngle);
|
|
var baseY = endY - tipLength * Math.sin(arrowHeadAngle);
|
|
|
|
var perpAngle = arrowHeadAngle + Math.PI / 2;
|
|
var ah1x = baseX + tipHalfWidth * Math.cos(perpAngle);
|
|
var ah1y = baseY + tipHalfWidth * Math.sin(perpAngle);
|
|
var ah2x = baseX - tipHalfWidth * Math.cos(perpAngle);
|
|
var ah2y = baseY - tipHalfWidth * Math.sin(perpAngle);
|
|
|
|
var arrowHead = '<polygon points="' + endX + ',' + endY + ' ' + ah1x + ',' + ah1y + ' ' + ah2x + ',' + ah2y + '" fill="' + color + '"/>';
|
|
var pathSvg = '<path d="' + pathD + '" fill="none" stroke="' + color + '" stroke-width="' + weight + '"' + dashAttr + '/>';
|
|
|
|
return '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ' + svgWidth + ' ' + svgHeight + '" width="' + svgWidth + '" height="' + svgHeight + '">' + pathSvg + arrowHead + '</svg>';
|
|
}
|
|
|
|
function updateStandaloneArrowPreview() {
|
|
// Nur anzeigen wenn Pfeil ausgewaehlt
|
|
if (textGenState.arrow === "none") {
|
|
var preview = document.getElementById("standaloneArrowPreview");
|
|
if (preview) preview.innerHTML = '<p style="color:#999;font-size:12px;text-align:center;">Waehle einen Pfeil aus</p>';
|
|
return;
|
|
}
|
|
var preview = document.getElementById('standaloneArrowPreview');
|
|
if (preview) {
|
|
preview.innerHTML = generateStandaloneArrowSVG();
|
|
}
|
|
}
|
|
|
|
async function copyStandaloneArrow() {
|
|
var svg = generateStandaloneArrowSVG();
|
|
try {
|
|
var canvas = await svgToCanvas(svg, 2);
|
|
canvas.toBlob(async function(blob) {
|
|
try {
|
|
await navigator.clipboard.write([
|
|
new ClipboardItem({ 'image/png': blob })
|
|
]);
|
|
showNotification('Pfeil in Zwischenablage kopiert!');
|
|
} catch (err) {
|
|
downloadStandaloneArrowPNG();
|
|
}
|
|
}, 'image/png');
|
|
} catch (err) {
|
|
showNotification('Fehler beim Kopieren', 'error');
|
|
}
|
|
}
|
|
|
|
function downloadStandaloneArrowSVG() {
|
|
var svg = generateStandaloneArrowSVG();
|
|
var blob = new Blob([svg], { type: 'image/svg+xml' });
|
|
var url = URL.createObjectURL(blob);
|
|
var link = document.createElement('a');
|
|
link.href = url;
|
|
link.download = 'pfeil.svg';
|
|
link.click();
|
|
URL.revokeObjectURL(url);
|
|
showNotification('Pfeil SVG heruntergeladen!');
|
|
}
|
|
|
|
async function downloadStandaloneArrowPNG() {
|
|
var svg = generateStandaloneArrowSVG();
|
|
try {
|
|
var canvas = await svgToCanvas(svg, 2);
|
|
canvas.toBlob(function(blob) {
|
|
var link = document.createElement('a');
|
|
link.href = URL.createObjectURL(blob);
|
|
link.download = 'pfeil.png';
|
|
link.click();
|
|
URL.revokeObjectURL(link.href);
|
|
showNotification('Pfeil PNG heruntergeladen!');
|
|
}, 'image/png');
|
|
} catch (err) {
|
|
showNotification('Fehler beim PNG-Export', 'error');
|
|
}
|
|
}
|
|
|
|
async function downloadStandaloneArrowJPG() {
|
|
var svg = generateStandaloneArrowSVG();
|
|
try {
|
|
var canvas = await svgToCanvas(svg, 2);
|
|
var tempCanvas = document.createElement('canvas');
|
|
tempCanvas.width = canvas.width;
|
|
tempCanvas.height = canvas.height;
|
|
var tempCtx = tempCanvas.getContext('2d');
|
|
tempCtx.fillStyle = '#FFFFFF';
|
|
tempCtx.fillRect(0, 0, tempCanvas.width, tempCanvas.height);
|
|
tempCtx.drawImage(canvas, 0, 0);
|
|
|
|
tempCanvas.toBlob(function(blob) {
|
|
var link = document.createElement('a');
|
|
link.href = URL.createObjectURL(blob);
|
|
link.download = 'pfeil.jpg';
|
|
link.click();
|
|
URL.revokeObjectURL(link.href);
|
|
showNotification('Pfeil JPG heruntergeladen!');
|
|
}, 'image/jpeg', 0.95);
|
|
} catch (err) {
|
|
showNotification('Fehler beim JPG-Export', 'error');
|
|
}
|
|
}
|
|
|
|
function downloadStandaloneArrowDXF() {
|
|
var svg = generateStandaloneArrowSVG();
|
|
var dxf = svgToDxf(svg, 1);
|
|
var blob = new Blob([dxf], { type: 'application/dxf' });
|
|
var url = URL.createObjectURL(blob);
|
|
var link = document.createElement('a');
|
|
link.href = url;
|
|
link.download = 'pfeil.dxf';
|
|
link.click();
|
|
URL.revokeObjectURL(url);
|
|
showNotification('Pfeil DXF heruntergeladen!');
|
|
}
|
|
|
|
function addStandaloneArrowToLegend() {
|
|
var svg = generateStandaloneArrowSVG();
|
|
legendItems.push({
|
|
id: 'arrow_' + Date.now(),
|
|
name: 'Pfeil',
|
|
svg: svg,
|
|
description: ''
|
|
});
|
|
updateLegendCount();
|
|
saveLegendToStorage();
|
|
showNotification('Pfeil zur Legende hinzugefuegt!');
|
|
}
|
|
|
|
function updateTextPreview() {
|
|
console.log("updateTextPreview called");
|
|
var preview = document.getElementById('textPreview');
|
|
if (preview) {
|
|
var svg = generateTextSVG(); console.log("Generated SVG length:", svg.length); preview.innerHTML = svg;
|
|
}
|
|
}
|
|
|
|
// Hilfsfunktion: SVG zu Canvas rendern
|
|
async function svgToCanvas(svg, scale) {
|
|
var parser = new DOMParser();
|
|
var svgDoc = parser.parseFromString(svg, 'image/svg+xml');
|
|
var svgEl = svgDoc.documentElement;
|
|
var svgWidth = parseFloat(svgEl.getAttribute('width')) || 100;
|
|
var svgHeight = parseFloat(svgEl.getAttribute('height')) || 100;
|
|
|
|
if (!scale) {
|
|
scale = Math.max(200 / Math.min(svgWidth, svgHeight), 1);
|
|
scale = Math.min(scale, 512 / Math.max(svgWidth, svgHeight));
|
|
}
|
|
|
|
var canvasWidth = Math.round(svgWidth * scale);
|
|
var canvasHeight = Math.round(svgHeight * scale);
|
|
|
|
var canvas = document.createElement('canvas');
|
|
var ctx = canvas.getContext('2d');
|
|
canvas.width = canvasWidth;
|
|
canvas.height = canvasHeight;
|
|
|
|
var img = new Image();
|
|
var svgBlob = new Blob([svg], { type: 'image/svg+xml;charset=utf-8' });
|
|
var url = URL.createObjectURL(svgBlob);
|
|
|
|
await new Promise(function(resolve, reject) {
|
|
img.onload = resolve;
|
|
img.onerror = reject;
|
|
img.src = url;
|
|
});
|
|
|
|
ctx.drawImage(img, 0, 0, canvasWidth, canvasHeight);
|
|
URL.revokeObjectURL(url);
|
|
|
|
return canvas;
|
|
}
|
|
|
|
async function copyTextAsImage() {
|
|
var svg = generateTextSVG();
|
|
|
|
try {
|
|
var canvas = await svgToCanvas(svg);
|
|
|
|
canvas.toBlob(async function(blob) {
|
|
try {
|
|
await navigator.clipboard.write([
|
|
new ClipboardItem({ 'image/png': blob })
|
|
]);
|
|
showNotification('In Zwischenablage kopiert!');
|
|
} catch (err) {
|
|
var link = document.createElement('a');
|
|
link.href = URL.createObjectURL(blob);
|
|
link.download = 'text-symbol.png';
|
|
link.click();
|
|
showNotification('PNG heruntergeladen');
|
|
}
|
|
}, 'image/png');
|
|
} catch (err) {
|
|
console.error('Fehler:', err);
|
|
showNotification('Fehler beim Kopieren', 'error');
|
|
}
|
|
}
|
|
|
|
function downloadTextSVG() {
|
|
var svg = generateTextSVG();
|
|
var blob = new Blob([svg], { type: 'image/svg+xml' });
|
|
var url = URL.createObjectURL(blob);
|
|
var link = document.createElement('a');
|
|
link.href = url;
|
|
var filename = textGenState.text ? textGenState.text.replace(/\n/g, '_').replace(/[^a-zA-Z0-9_-]/g, '').substring(0, 20) : 'symbol';
|
|
link.download = 'text-' + filename + '.svg';
|
|
link.click();
|
|
URL.revokeObjectURL(url);
|
|
showNotification('SVG heruntergeladen!');
|
|
}
|
|
|
|
async function downloadTextPNG() {
|
|
var svg = generateTextSVG();
|
|
try {
|
|
var canvas = await svgToCanvas(svg, 2);
|
|
|
|
canvas.toBlob(function(blob) {
|
|
var link = document.createElement('a');
|
|
link.href = URL.createObjectURL(blob);
|
|
var filename = textGenState.text ? textGenState.text.replace(/\n/g, '_').replace(/[^a-zA-Z0-9_-]/g, '').substring(0, 20) : 'symbol';
|
|
link.download = 'text-' + filename + '.png';
|
|
link.click();
|
|
URL.revokeObjectURL(link.href);
|
|
showNotification('PNG heruntergeladen!');
|
|
}, 'image/png');
|
|
} catch (err) {
|
|
console.error('Fehler:', err);
|
|
showNotification('Fehler beim PNG-Export', 'error');
|
|
}
|
|
}
|
|
|
|
async function downloadTextJPG() {
|
|
var svg = generateTextSVG();
|
|
try {
|
|
var canvas = await svgToCanvas(svg, 2);
|
|
var ctx = canvas.getContext('2d');
|
|
|
|
var tempCanvas = document.createElement('canvas');
|
|
tempCanvas.width = canvas.width;
|
|
tempCanvas.height = canvas.height;
|
|
var tempCtx = tempCanvas.getContext('2d');
|
|
tempCtx.fillStyle = '#FFFFFF';
|
|
tempCtx.fillRect(0, 0, tempCanvas.width, tempCanvas.height);
|
|
tempCtx.drawImage(canvas, 0, 0);
|
|
|
|
tempCanvas.toBlob(function(blob) {
|
|
var link = document.createElement('a');
|
|
link.href = URL.createObjectURL(blob);
|
|
var filename = textGenState.text ? textGenState.text.replace(/\n/g, '_').replace(/[^a-zA-Z0-9_-]/g, '').substring(0, 20) : 'symbol';
|
|
link.download = 'text-' + filename + '.jpg';
|
|
link.click();
|
|
URL.revokeObjectURL(link.href);
|
|
showNotification('JPG heruntergeladen!');
|
|
}, 'image/jpeg', 0.95);
|
|
} catch (err) {
|
|
console.error('Fehler:', err);
|
|
showNotification('Fehler beim JPG-Export', 'error');
|
|
}
|
|
}
|
|
|
|
function downloadTextDXF() {
|
|
var svg = generateTextSVG();
|
|
var dxf = svgToDxf(svg, 1);
|
|
var blob = new Blob([dxf], { type: 'application/dxf' });
|
|
var url = URL.createObjectURL(blob);
|
|
var link = document.createElement('a');
|
|
link.href = url;
|
|
var filename = textGenState.text ? textGenState.text.replace(/\n/g, '_').replace(/[^a-zA-Z0-9_-]/g, '').substring(0, 20) : 'symbol';
|
|
link.download = 'text-' + filename + '.dxf';
|
|
link.click();
|
|
URL.revokeObjectURL(url);
|
|
showNotification('DXF heruntergeladen!');
|
|
}
|
|
|
|
function addTextToLegend() {
|
|
var svg = generateTextSVG();
|
|
var text = textGenState.text || 'Text';
|
|
var displayName = text.replace(/\n/g, ' ').substring(0, 30);
|
|
|
|
legendItems.push({
|
|
id: 'custom_text_' + Date.now(),
|
|
name: displayName,
|
|
svg: svg,
|
|
description: ''
|
|
});
|
|
|
|
updateLegendCount();
|
|
saveLegendToStorage();
|
|
showNotification('Zur Legende hinzugefuegt!');
|
|
}
|
|
|
|
// ========== CUSTOM SYMBOLS STORAGE ==========
|
|
|
|
function loadCustomSymbols() {
|
|
try {
|
|
var stored = localStorage.getItem('customSymbols');
|
|
if (stored) {
|
|
customSymbols = JSON.parse(stored);
|
|
}
|
|
} catch (e) {
|
|
console.error('Fehler beim Laden der eigenen Symbole:', e);
|
|
customSymbols = [];
|
|
}
|
|
}
|
|
|
|
function saveCustomSymbols() {
|
|
try {
|
|
localStorage.setItem('customSymbols', JSON.stringify(customSymbols));
|
|
} catch (e) {
|
|
console.error('Fehler beim Speichern der eigenen Symbole:', e);
|
|
}
|
|
}
|
|
|
|
function openSaveModal() {
|
|
var modal = document.getElementById('saveModal');
|
|
var nameInput = document.getElementById('symbolName');
|
|
var descInput = document.getElementById('symbolDescription');
|
|
|
|
var suggestedName = textGenState.text ? textGenState.text.replace(/\n/g, ' ').substring(0, 30) : '';
|
|
nameInput.value = suggestedName;
|
|
descInput.value = '';
|
|
|
|
modal.style.display = 'flex';
|
|
nameInput.focus();
|
|
}
|
|
|
|
function closeSaveModal() {
|
|
var modal = document.getElementById('saveModal');
|
|
modal.style.display = 'none';
|
|
}
|
|
|
|
function saveCustomSymbol() {
|
|
var nameInput = document.getElementById('symbolName');
|
|
var descInput = document.getElementById('symbolDescription');
|
|
|
|
var name = nameInput.value.trim();
|
|
if (!name) {
|
|
showNotification('Bitte einen Namen eingeben!', 'error');
|
|
return;
|
|
}
|
|
|
|
var svg = generateTextSVG();
|
|
|
|
var newSymbol = {
|
|
id: 'custom_' + Date.now(),
|
|
name: name,
|
|
description: descInput.value.trim(),
|
|
svg: svg,
|
|
category: 'custom',
|
|
tags: ['eigene', 'custom', name.toLowerCase()],
|
|
createdAt: new Date().toISOString(),
|
|
state: JSON.parse(JSON.stringify(textGenState))
|
|
};
|
|
|
|
customSymbols.push(newSymbol);
|
|
saveCustomSymbols();
|
|
|
|
closeSaveModal();
|
|
showNotification('Symbol "' + name + '" gespeichert!');
|
|
|
|
var activeFilter = document.querySelector('.filter-pill.active');
|
|
if (activeFilter && activeFilter.dataset.filter === 'custom') {
|
|
renderSymbolGrid();
|
|
}
|
|
}
|
|
|
|
function deleteCustomSymbol(id) {
|
|
if (!confirm('Symbol wirklich loeschen?')) return;
|
|
|
|
customSymbols = customSymbols.filter(function(s) { return s.id !== id; });
|
|
saveCustomSymbols();
|
|
renderSymbolGrid();
|
|
showNotification('Symbol geloescht');
|
|
}
|
|
|
|
var originalRenderSymbolGrid = typeof renderSymbolGrid === 'function' ? renderSymbolGrid : null;
|
|
|
|
function renderSymbolGridWithCustom() {
|
|
var activeFilter = document.querySelector('.filter-pill.active');
|
|
var filter = activeFilter ? activeFilter.dataset.filter : 'all';
|
|
|
|
if (filter === 'custom') {
|
|
renderCustomSymbolsOnly();
|
|
} else if (originalRenderSymbolGrid) {
|
|
originalRenderSymbolGrid();
|
|
if (filter === 'all') {
|
|
appendCustomSymbols();
|
|
}
|
|
}
|
|
}
|
|
|
|
function renderCustomSymbolsOnly() {
|
|
var grid = document.getElementById('symbolGrid');
|
|
if (!grid) return;
|
|
|
|
grid.innerHTML = '';
|
|
|
|
if (customSymbols.length === 0) {
|
|
grid.innerHTML = '<div class="no-results">Noch keine eigenen Symbole gespeichert.<br>Erstelle ein Text-Symbol und klicke auf "Speichern".</div>';
|
|
return;
|
|
}
|
|
|
|
customSymbols.forEach(function(symbol) {
|
|
var card = createCustomSymbolCard(symbol);
|
|
grid.appendChild(card);
|
|
});
|
|
}
|
|
|
|
function appendCustomSymbols() {
|
|
var grid = document.getElementById('symbolGrid');
|
|
if (!grid || customSymbols.length === 0) return;
|
|
|
|
customSymbols.forEach(function(symbol) {
|
|
var card = createCustomSymbolCard(symbol);
|
|
grid.appendChild(card);
|
|
});
|
|
}
|
|
|
|
function createCustomSymbolCard(symbol) {
|
|
var card = document.createElement('div');
|
|
card.className = 'symbol-card';
|
|
card.innerHTML =
|
|
'<button class="btn-delete" onclick="event.stopPropagation(); deleteCustomSymbol(\'' + symbol.id + '\')" title="Loeschen">X</button>' +
|
|
'<div class="symbol-preview">' + symbol.svg + '</div>' +
|
|
'<div class="symbol-name">' + escapeHtml(symbol.name) + '</div>' +
|
|
'<div class="symbol-actions">' +
|
|
'<button class="btn-action btn-copy" onclick="copySymbolAsImage(\'' + symbol.id + '\', true)" title="Kopieren">Kopieren</button>' +
|
|
'<button class="btn-action btn-svg" onclick="downloadSymbolSVG(\'' + symbol.id + '\', true)" title="SVG">SVG</button>' +
|
|
'<button class="btn-action btn-dxf" onclick="downloadSymbolDXF(\'' + symbol.id + '\', true)" title="DXF">DXF</button>' +
|
|
'<button class="btn-action btn-legend" onclick="addSymbolToLegend(\'' + symbol.id + '\', true)" title="Legende">+</button>' +
|
|
'</div>';
|
|
return card;
|
|
}
|
|
|
|
function escapeHtml(text) {
|
|
var div = document.createElement('div');
|
|
div.textContent = text;
|
|
return div.innerHTML;
|
|
}
|
|
|
|
function getSymbolById(id, isCustom) {
|
|
if (isCustom) {
|
|
return customSymbols.find(function(s) { return s.id === id; });
|
|
}
|
|
return allSymbols.find(function(s) { return s.id === id; });
|
|
}
|
|
|
|
// ========== IMPRESSUM & DATENSCHUTZ ==========
|
|
function openImpressum() {
|
|
var modal = document.getElementById('impressumModal');
|
|
if (modal) modal.style.display = 'flex';
|
|
}
|
|
|
|
function closeImpressum() {
|
|
var modal = document.getElementById('impressumModal');
|
|
if (modal) modal.style.display = 'none';
|
|
}
|
|
|
|
function openDatenschutz() {
|
|
var modal = document.getElementById('datenschutzModal');
|
|
if (modal) modal.style.display = 'flex';
|
|
}
|
|
|
|
function closeDatenschutz() {
|
|
var modal = document.getElementById('datenschutzModal');
|
|
if (modal) modal.style.display = 'none';
|
|
}
|
|
|
|
// Init beim Laden
|
|
if (document.readyState === 'loading') {
|
|
document.addEventListener('DOMContentLoaded', initTextGenerator);
|
|
} else {
|
|
initTextGenerator();
|
|
}
|
|
|
|
// ========== GLOBAL EXPORTS fuer onclick Handler ==========
|
|
window.resetToDefaults = resetToDefaults;
|
|
window.copyStandaloneArrow = copyStandaloneArrow;
|
|
window.downloadStandaloneArrowSVG = downloadStandaloneArrowSVG;
|
|
window.downloadStandaloneArrowPNG = downloadStandaloneArrowPNG;
|
|
window.downloadStandaloneArrowJPG = downloadStandaloneArrowJPG;
|
|
window.downloadStandaloneArrowDXF = downloadStandaloneArrowDXF;
|
|
window.addStandaloneArrowToLegend = addStandaloneArrowToLegend;
|
|
window.generateTextSVG = generateTextSVG;
|
|
window.copyTextAsImage = copyTextAsImage;
|
|
window.downloadTextSVG = downloadTextSVG;
|
|
window.downloadTextPNG = downloadTextPNG;
|
|
window.downloadTextJPG = downloadTextJPG;
|
|
window.downloadTextDXF = downloadTextDXF;
|
|
window.addTextToLegend = addTextToLegend;
|
|
window.toggleTextGenerator = toggleTextGenerator;
|
|
window.openImpressum = openImpressum;
|
|
window.closeImpressum = closeImpressum;
|
|
window.openDatenschutz = openDatenschutz;
|
|
window.closeDatenschutz = closeDatenschutz;
|