318 lines
12 KiB
Smarty
318 lines
12 KiB
Smarty
<!-- Modal - Verlag Details mit semantischem Netz -->
|
|
<div class="modal fade" id="DetailsModal" tabindex="-1" aria-labelledby="DetailsModalLabel" aria-hidden="true">
|
|
<div class="modal-dialog modal-xl" style="max-width: 1200px;">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title" id="DetailsModalHeadline">
|
|
<i class="fas fa-book"></i> Detailanzeige Verlag
|
|
</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Schließen"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
|
|
<div class="row">
|
|
<!-- Linke Spalte: Stammdaten -->
|
|
<div class="col-md-4">
|
|
<div class="detail-info-panel">
|
|
<div class="form-group mb-3">
|
|
<label class="form-label fw-bold">Name</label>
|
|
<input type="text" id="display_term" class="form-control" disabled />
|
|
<input type="hidden" id="display_id" />
|
|
</div>
|
|
|
|
<div class="form-group mb-3">
|
|
<label class="form-label fw-bold">Deskriptor</label>
|
|
<input type="text" id="display_descriptor" class="form-control" disabled />
|
|
</div>
|
|
|
|
<div class="row mb-3">
|
|
<div class="col-6">
|
|
<label class="form-label fw-bold">Type</label>
|
|
<input type="text" id="display_type" class="form-control" disabled />
|
|
</div>
|
|
<div class="col-6">
|
|
<label class="form-label fw-bold">ID</label>
|
|
<input type="text" id="display_id_show" class="form-control" disabled />
|
|
</div>
|
|
</div>
|
|
|
|
<div class="form-group mb-3">
|
|
<label class="form-label fw-bold">Scopenote</label>
|
|
<textarea class="form-control" id="display_scopenote" rows="4" disabled></textarea>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Rechte Spalte: Semantisches Netz -->
|
|
<div class="col-md-8">
|
|
<div class="network-panel">
|
|
<div class="d-flex justify-content-between align-items-center mb-2">
|
|
<h6 class="mb-0"><i class="fas fa-project-diagram me-2"></i>Semantisches Netz</h6>
|
|
<div class="network-toolbar">
|
|
<button class="btn btn-sm btn-outline-secondary" onclick="networkFit()" title="Ansicht anpassen">
|
|
<i class="fas fa-expand"></i>
|
|
</button>
|
|
<button class="btn btn-sm btn-outline-secondary" onclick="networkZoomIn()" title="Vergrößern">
|
|
<i class="fas fa-search-plus"></i>
|
|
</button>
|
|
<button class="btn btn-sm btn-outline-secondary" onclick="networkZoomOut()" title="Verkleinern">
|
|
<i class="fas fa-search-minus"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div id="network-container"></div>
|
|
<div class="network-legend">
|
|
<span class="legend-item"><span class="legend-dot" style="background: #1a3a5c;"></span> Zentraler Verlag</span>
|
|
<span class="legend-item"><span class="legend-dot" style="background: #28a745;"></span> Früher verwendet</span>
|
|
<span class="legend-item"><span class="legend-dot" style="background: #dc3545;"></span> Später verwendet</span>
|
|
<span class="legend-item"><span class="legend-dot" style="background: #fd7e14;"></span> Alternativer Name</span>
|
|
<span class="legend-item"><span class="legend-dot" style="background: #6c757d;"></span> USE/USEDFOR</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="modal_content" style="display: none;"></div>
|
|
|
|
</div>
|
|
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">
|
|
<i class="fas fa-times"></i> schliessen
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<style>
|
|
#network-container {
|
|
width: 100%;
|
|
height: 450px;
|
|
border: 1px solid var(--border-color);
|
|
border-radius: var(--radius-md);
|
|
background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
|
|
}
|
|
|
|
.network-panel {
|
|
background: white;
|
|
border-radius: var(--radius-md);
|
|
padding: 15px;
|
|
border: 1px solid var(--border-color);
|
|
}
|
|
|
|
.detail-info-panel {
|
|
background: var(--bg-light);
|
|
border-radius: var(--radius-md);
|
|
padding: 15px;
|
|
border: 1px solid var(--border-color);
|
|
}
|
|
|
|
.network-toolbar {
|
|
display: flex;
|
|
gap: 5px;
|
|
}
|
|
|
|
.network-toolbar .btn {
|
|
padding: 4px 8px;
|
|
}
|
|
|
|
.network-legend {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 15px;
|
|
margin-top: 10px;
|
|
padding-top: 10px;
|
|
border-top: 1px solid var(--border-light);
|
|
font-size: 12px;
|
|
}
|
|
|
|
.legend-item {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 5px;
|
|
}
|
|
|
|
.legend-dot {
|
|
width: 12px;
|
|
height: 12px;
|
|
border-radius: 50%;
|
|
display: inline-block;
|
|
}
|
|
|
|
#DetailsModal .modal-body {
|
|
max-height: 80vh;
|
|
overflow-y: auto;
|
|
}
|
|
</style>
|
|
|
|
<script src="/libs/vis-network/current/vis-network.min.js"></script>
|
|
<script>if (typeof vis === "undefined") { document.write('<script src="https://unpkg.com/vis-network/standalone/umd/vis-network.min.js"><\/script>'); }</script>
|
|
|
|
<script>
|
|
var network = null;
|
|
var networkData = null;
|
|
|
|
function createSemanticNetwork(centralTerm, centralId, relations) {
|
|
console.log('🕸️ Erstelle semantisches Netz für Verlag:', centralTerm);
|
|
|
|
var nodes = [];
|
|
var edges = [];
|
|
|
|
nodes.push({
|
|
id: centralId,
|
|
label: wrapLabel(centralTerm, 20),
|
|
shape: 'ellipse',
|
|
color: {
|
|
background: '#1a3a5c',
|
|
border: '#0d1f30',
|
|
highlight: { background: '#2a5a8c', border: '#1a3a5c' },
|
|
hover: { background: '#2a5a8c', border: '#1a3a5c' }
|
|
},
|
|
font: { color: '#ffffff', size: 14, face: 'Arial', bold: true },
|
|
size: 35,
|
|
borderWidth: 3,
|
|
shadow: true
|
|
});
|
|
|
|
// Farben für Verlags-Relationstypen
|
|
var relationColors = {
|
|
'USEDEARLIER': { node: '#28a745', edge: '#28a745', label: 'Früher verwendet' },
|
|
'USEDLATER': { node: '#dc3545', edge: '#dc3545', label: 'Später verwendet' },
|
|
'ALTERNATIVENAME': { node: '#fd7e14', edge: '#fd7e14', label: 'Alternativer Name' },
|
|
'USE': { node: '#6c757d', edge: '#6c757d', label: 'Benutze' },
|
|
'USEDFOR': { node: '#6c757d', edge: '#6c757d', label: 'Benutzt für' }
|
|
};
|
|
|
|
if (relations && relations.length > 0) {
|
|
relations.forEach(function(rel, index) {
|
|
var relType = rel.Relationtype || 'USE';
|
|
var relColor = relationColors[relType] || { node: '#17a2b8', edge: '#17a2b8' };
|
|
var relId = rel.IDRelation || ('rel_' + index);
|
|
var relText = rel.TextRelation || 'Unbekannt';
|
|
|
|
nodes.push({
|
|
id: relId,
|
|
label: wrapLabel(relText, 18),
|
|
shape: 'ellipse',
|
|
color: {
|
|
background: relColor.node,
|
|
border: shadeColor(relColor.node, -20),
|
|
highlight: { background: shadeColor(relColor.node, 20), border: relColor.node },
|
|
hover: { background: shadeColor(relColor.node, 20), border: relColor.node }
|
|
},
|
|
font: { color: '#ffffff', size: 12, face: 'Arial' },
|
|
size: 25,
|
|
borderWidth: 2,
|
|
shadow: true,
|
|
title: relType + ': ' + relText + ' (ID: ' + relId + ')'
|
|
});
|
|
|
|
var arrowDirection = {};
|
|
if (relType === 'USEDEARLIER') {
|
|
arrowDirection = { from: { enabled: true, scaleFactor: 1 } };
|
|
} else if (relType === 'USEDLATER') {
|
|
arrowDirection = { to: { enabled: true, scaleFactor: 1 } };
|
|
}
|
|
|
|
edges.push({
|
|
from: centralId,
|
|
to: relId,
|
|
label: relType,
|
|
color: { color: relColor.edge, highlight: relColor.edge, hover: relColor.edge },
|
|
width: 2,
|
|
arrows: arrowDirection,
|
|
font: { size: 10, color: '#666', strokeWidth: 3, strokeColor: '#ffffff' },
|
|
smooth: { type: 'curvedCW', roundness: 0.2 }
|
|
});
|
|
});
|
|
}
|
|
|
|
var container = document.getElementById('network-container');
|
|
|
|
networkData = {
|
|
nodes: new vis.DataSet(nodes),
|
|
edges: new vis.DataSet(edges)
|
|
};
|
|
|
|
var options = {
|
|
nodes: { shape: 'ellipse', scaling: { min: 20, max: 40 } },
|
|
edges: { smooth: { type: 'curvedCW', roundness: 0.2 } },
|
|
physics: {
|
|
enabled: true,
|
|
barnesHut: {
|
|
gravitationalConstant: -3000,
|
|
centralGravity: 0.3,
|
|
springLength: 150,
|
|
springConstant: 0.04,
|
|
damping: 0.09
|
|
},
|
|
stabilization: { enabled: true, iterations: 200, updateInterval: 25 }
|
|
},
|
|
interaction: { hover: true, tooltipDelay: 200, zoomView: true, dragView: true },
|
|
layout: { improvedLayout: true }
|
|
};
|
|
|
|
network = new vis.Network(container, networkData, options);
|
|
|
|
network.on('click', function(params) {
|
|
if (params.nodes.length > 0) {
|
|
var clickedNodeId = params.nodes[0];
|
|
if (clickedNodeId != centralId) {
|
|
ShowModalDetails(clickedNodeId);
|
|
}
|
|
}
|
|
});
|
|
|
|
network.once('stabilizationIterationsDone', function() {
|
|
network.fit({ animation: { duration: 500, easingFunction: 'easeInOutQuad' } });
|
|
});
|
|
}
|
|
|
|
function wrapLabel(text, maxLength) {
|
|
if (!text) return '';
|
|
if (text.length <= maxLength) return text;
|
|
var words = text.split(' ');
|
|
var lines = [];
|
|
var currentLine = '';
|
|
words.forEach(function(word) {
|
|
if ((currentLine + ' ' + word).trim().length <= maxLength) {
|
|
currentLine = (currentLine + ' ' + word).trim();
|
|
} else {
|
|
if (currentLine) lines.push(currentLine);
|
|
currentLine = word;
|
|
}
|
|
});
|
|
if (currentLine) lines.push(currentLine);
|
|
return lines.join('\n');
|
|
}
|
|
|
|
function shadeColor(color, percent) {
|
|
var R = parseInt(color.substring(1,3), 16);
|
|
var G = parseInt(color.substring(3,5), 16);
|
|
var B = parseInt(color.substring(5,7), 16);
|
|
R = parseInt(R * (100 + percent) / 100);
|
|
G = parseInt(G * (100 + percent) / 100);
|
|
B = parseInt(B * (100 + percent) / 100);
|
|
R = (R < 255) ? R : 255;
|
|
G = (G < 255) ? G : 255;
|
|
B = (B < 255) ? B : 255;
|
|
var RR = ((R.toString(16).length == 1) ? "0" + R.toString(16) : R.toString(16));
|
|
var GG = ((G.toString(16).length == 1) ? "0" + G.toString(16) : G.toString(16));
|
|
var BB = ((B.toString(16).length == 1) ? "0" + B.toString(16) : B.toString(16));
|
|
return "#" + RR + GG + BB;
|
|
}
|
|
|
|
function networkFit() {
|
|
if (network) network.fit({ animation: { duration: 500, easingFunction: 'easeInOutQuad' } });
|
|
}
|
|
|
|
function networkZoomIn() {
|
|
if (network) network.moveTo({ scale: network.getScale() * 1.3, animation: { duration: 300 } });
|
|
}
|
|
|
|
function networkZoomOut() {
|
|
if (network) network.moveTo({ scale: network.getScale() / 1.3, animation: { duration: 300 } });
|
|
}
|
|
</script>
|