Erster Commit

This commit is contained in:
zed
2026-03-13 09:53:40 +01:00
commit 5ebcad02ed
3945 changed files with 974582 additions and 0 deletions

345
js/editor.js Normal file
View File

@@ -0,0 +1,345 @@
/**
* EDI Document Editor
* Allows parsing, visualizing, editing and re-exporting EDI/VDA files
*/
window.EDIBridge = window.EDIBridge || {};
class Editor {
constructor(app) {
this.app = app;
this.initElements();
this.bindEvents();
this.currentDoc = null; // Store { type: 'edifact'|'vda', data: ..., raw: ... }
}
initElements() {
this.dropZone = document.getElementById('editorDropZone');
this.fileInput = document.getElementById('editorFileInput');
this.renderArea = document.getElementById('editorDocumentRender');
this.btnDownload = document.getElementById('btnEditorDownload');
this.btnBack = document.getElementById('btnEditorBack');
this.dropText = document.getElementById('editorDropText');
}
bindEvents() {
if (this.dropZone) {
this.dropZone.onclick = (e) => { e.stopPropagation(); if (this.fileInput) this.fileInput.click(); };
this.dropZone.ondragover = (e) => { e.preventDefault(); this.dropZone.classList.add('dragover'); };
this.dropZone.ondragleave = () => this.dropZone.classList.remove('dragover');
this.dropZone.ondrop = (e) => {
e.preventDefault();
this.dropZone.classList.remove('dragover');
this.handleFile(e.dataTransfer.files[0]);
};
}
if (this.fileInput) {
this.fileInput.onchange = (e) => this.handleFile(e.target.files[0]);
}
}
handleFile(file) {
if (!file) return;
this.fileName = file.name;
const reader = new FileReader();
reader.onload = (e) => {
this.processContent(e.target.result);
};
reader.readAsText(file);
}
processContent(content) {
const trimmed = content.trim();
const upper = trimmed.toUpperCase();
try {
if (upper.startsWith('UNA') || upper.startsWith('UNB')) {
this.parseAndRenderEdifact(trimmed);
} else if (upper.startsWith('711') || upper.startsWith('511')) {
this.parseAndRenderVDA(trimmed);
} else {
alert('Unbekanntes Dateiformat (Weder EDIFACT noch VDA).');
}
} catch (error) {
console.error(error);
alert('Fehler beim Laden/Parsen der Datei im Editor: ' + error.message);
}
}
parseAndRenderEdifact(content) {
const parser = window.EDIBridge.DelforParser;
if (!parser) throw new Error('DelforParser nicht geladen.');
const parsed = parser.parse(content);
this.currentDoc = {
type: 'edifact',
meta: parsed.meta,
segments: parsed.segments,
raw: content
};
this.renderEdifactEditor();
}
parseAndRenderVDA(content) {
const lines = content.split(/\r?\n/).filter(l => l.length >= 3);
const segments = lines.map(l => {
return {
tag: l.substring(0, 3),
rawLine: l,
fields: this.splitVdaLine(l)
};
});
this.currentDoc = {
type: 'vda',
segments: segments,
raw: content
};
this.renderVdaEditor();
}
// A helper to roughly split VDA into fields (since VDA is fixed length, this is a bit arbitrary for editing unless we know exact positions. For generic editing, we can let user deal with the raw string or fixed chunks if we define them. Let's do raw string editing for VDA segments initially to prevent destroying structure, or simple chunking)
splitVdaLine(line) {
// As a simple generic VDA editor, we split it into Tag and the rest of the string
return [
{ label: 'Satzart', value: line.substring(0, 3), readonly: true },
{ label: 'Daten', value: line.substring(3), length: line.length - 3 }
];
}
renderEdifactEditor() {
let html = `
<div style="margin-bottom: 20px; font-size: 0.9rem; color: var(--text-dim);">
Datei: <strong>${this.fileName}</strong> | Typ: EDIFACT | Segmente: ${this.currentDoc.segments.length}
</div>
<div class="editor-segments-list">
`;
this.currentDoc.segments.forEach((seg, sIdx) => {
html += `
<div class="editor-segment" data-segment-index="${sIdx}">
<div class="editor-segment-header">
<div>Segment <span class="editor-segment-tag">${seg.tag}</span></div>
</div>
<div class="editor-segment-fields">
`;
seg.elements.forEach((el, elIdx) => {
if (Array.isArray(el)) {
el.forEach((subEl, subIdx) => {
html += `
<div class="editor-field">
<label>E${elIdx + 1}.${subIdx + 1}</label>
<input type="text" class="editor-field-input"
data-seg="${sIdx}" data-el="${elIdx}" data-sub="${subIdx}"
value="${this.escapeHtml(subEl)}"
onchange="app.editor.updateEdifactField(this)">
</div>
`;
});
} else {
html += `
<div class="editor-field">
<label>E${elIdx + 1}</label>
<input type="text" class="editor-field-input"
data-seg="${sIdx}" data-el="${elIdx}" data-sub="-1"
value="${this.escapeHtml(el)}"
onchange="app.editor.updateEdifactField(this)">
</div>
`;
}
});
html += `
</div>
</div>
`;
});
html += `</div>`;
this.showEditor(html);
}
renderVdaEditor() {
let html = `
<div style="margin-bottom: 20px; font-size: 0.9rem; color: var(--text-dim);">
Datei: <strong>${this.fileName}</strong> | Typ: VDA | Sätze: ${this.currentDoc.segments.length}
</div>
<div class="editor-segments-list">
`;
this.currentDoc.segments.forEach((seg, sIdx) => {
html += `
<div class="editor-segment" data-segment-index="${sIdx}">
<div class="editor-segment-header">
<div>Satzart <span class="editor-segment-tag">${seg.tag}</span></div>
<div style="font-size: 0.75rem; color: var(--text-dim); font-weight: normal;">Länge: <span id="vda-len-${sIdx}">${seg.rawLine.length}</span></div>
</div>
<div class="editor-segment-fields" style="flex-direction: column;">
`;
seg.fields.forEach((field, fIdx) => {
if (field.readonly) {
html += `
<div class="editor-field" style="flex-direction: row; align-items: center;">
<label style="width: 60px;">${field.label}</label>
<input type="text" class="editor-field-input" value="${field.value}" readonly disabled style="opacity: 0.6; width: 60px;">
</div>
`;
} else {
html += `
<div class="editor-field">
<label>${field.label} (Original Länge: ${field.length})</label>
<input type="text" class="editor-field-input" style="font-family: 'Fira Code', monospace; width: 100%; min-width: 500px;"
data-seg="${sIdx}" data-field="${fIdx}"
value="${this.escapeHtml(field.value)}"
oninput="app.editor.updateVdaFieldLength(${sIdx}, this.value)"
onchange="app.editor.updateVdaField(this)">
</div>
`;
}
});
html += `
</div>
</div>
`;
});
html += `</div>`;
this.showEditor(html);
}
updateEdifactField(inputElem) {
const sIdx = parseInt(inputElem.getAttribute('data-seg'));
const elIdx = parseInt(inputElem.getAttribute('data-el'));
const subIdx = parseInt(inputElem.getAttribute('data-sub'));
const val = inputElem.value;
if (subIdx === -1) {
this.currentDoc.segments[sIdx].elements[elIdx] = val;
} else {
this.currentDoc.segments[sIdx].elements[elIdx][subIdx] = val;
}
}
updateVdaFieldLength(sIdx, value) {
const lenSpan = document.getElementById(`vda-len-${sIdx}`);
if (lenSpan) {
lenSpan.textContent = value.length + 3; // +3 for tag
// Suggesting length warnings could be added here later
}
}
updateVdaField(inputElem) {
const sIdx = parseInt(inputElem.getAttribute('data-seg'));
const fIdx = parseInt(inputElem.getAttribute('data-field'));
const val = inputElem.value;
this.currentDoc.segments[sIdx].fields[fIdx].value = val;
// Update rawLine
const tag = this.currentDoc.segments[sIdx].fields[0].value;
this.currentDoc.segments[sIdx].rawLine = tag + val;
}
showEditor(html) {
this.renderArea.innerHTML = html;
this.dropZone.style.display = 'none';
this.renderArea.style.display = 'block';
if (this.btnDownload) this.btnDownload.style.display = 'inline-flex';
if (this.btnBack) this.btnBack.style.display = 'inline-flex';
if (window.lucide) {
lucide.createIcons();
}
}
reset() {
this.currentDoc = null;
this.renderArea.innerHTML = '';
this.renderArea.style.display = 'none';
this.dropZone.style.display = 'flex';
if (this.btnDownload) this.btnDownload.style.display = 'none';
if (this.btnBack) this.btnBack.style.display = 'none';
if (this.fileInput) this.fileInput.value = '';
}
downloadDocument() {
if (!this.currentDoc) return;
let output = '';
if (this.currentDoc.type === 'edifact') {
output = this.rebuildEdifact();
} else if (this.currentDoc.type === 'vda') {
output = this.rebuildVda();
}
const blob = new Blob([output], { type: 'text/plain;charset=utf-8' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
let outName = this.fileName;
if (!outName.includes('_edited')) {
const parts = outName.split('.');
if (parts.length > 1) {
const ext = parts.pop();
outName = parts.join('.') + '_edited.' + ext;
} else {
outName += '_edited';
}
}
a.href = url;
a.download = outName;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
}
rebuildEdifact() {
const meta = this.currentDoc.meta;
let out = '';
// Add UNA if it was present
if (this.currentDoc.raw.startsWith('UNA')) {
out += `UNA${meta.subSeparator}${meta.separator}${meta.decimal}${meta.release} ${meta.terminator}`;
}
this.currentDoc.segments.forEach(seg => {
let segStr = seg.tag;
seg.elements.forEach(el => {
segStr += meta.separator;
if (Array.isArray(el)) {
segStr += el.join(meta.subSeparator);
} else {
segStr += el;
}
});
out += segStr + meta.terminator + "\\r\\n";
});
// Very basic rebuilding: standardizing newlines
return out.replace(/\\r\\n/g, '\r\n');
}
rebuildVda() {
return this.currentDoc.segments.map(s => s.rawLine).join('\r\n');
}
escapeHtml(unsafe) {
if (unsafe === undefined || unsafe === null) return '';
return unsafe
.toString()
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;")
.replace(/'/g, "&#039;");
}
}
window.EDIBridge.Editor = Editor;