Skip to content
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 12 additions & 7 deletions ETL Editor.html
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,12 @@
.mappings-table th { font-size: 0.75rem; color: var(--text-secondary); }
.mappings-table .form-control { font-size: 0.8rem; padding: 0.3rem 0.5rem; }
.mappings-table td.actions { width: 1%; }
.tree-view { font-family: 'Fira Code', monospace; color: var(--text-primary); }
.tree-view ul { list-style: none; padding-left: 1rem; border-left: 1px solid var(--border); margin: 0.25rem 0; }
.tree-node { margin: 0.25rem 0; }
.tree-tag { color: var(--primary); }
.tree-attr { color: var(--secondary); }
.tree-text { color: var(--text-secondary); font-style: italic; }
</style>
</head>
<body>
Expand All @@ -166,7 +172,7 @@
</div>
<div class="editor-container">
<div class="tabs"><button class="tab active" onclick="switchTab('xml', event)">XML Output</button><button class="tab" onclick="switchTab('preview', event)">Tree View</button></div>
<div class="editor"><div id="xmlOutput"></div><div id="treeView" style="display: none;"></div></div>
<div class="editor"><div id="xmlOutput"></div><div id="treeView" class="tree-view" style="display: none;"></div></div>
</div>
</div>

Expand Down Expand Up @@ -214,13 +220,10 @@ <h2 class="modal-title">Edit Load</h2>
</div>

<script>
// No changes to the JavaScript are needed for this layout improvement.
// All existing functions from the previous step remain the same.

let targetData = { name: '', database: '', temporalization: '', loads: [] };
let currentTab = 'xml';

function switchTab(tabName, event) { currentTab = tabName; document.querySelectorAll('.tab').forEach(t => t.classList.remove('active')); event.target.classList.add('active'); if (tabName === 'xml') { document.getElementById('xmlOutput').style.display = 'block'; document.getElementById('treeView').style.display = 'none'; updateXML(); } else { document.getElementById('xmlOutput').style.display = 'none'; document.getElementById('treeView').style.display = 'block'; document.getElementById('treeView').innerHTML = "Tree view not implemented."; } }
function switchTab(tabName, event) { currentTab = tabName; document.querySelectorAll('.tab').forEach(t => t.classList.remove('active')); event.target.classList.add('active'); if (tabName === 'xml') { document.getElementById('xmlOutput').style.display = 'block'; document.getElementById('treeView').style.display = 'none'; updateXML(); } else { document.getElementById('xmlOutput').style.display = 'none'; document.getElementById('treeView').style.display = 'block'; updateXML(); } }
function newFile() { if (confirm('Create a new file? Current changes will be lost.')) { targetData = { name: '', database: '', temporalization: '', loads: [] }; document.getElementById('targetName').value = ''; document.getElementById('targetDatabase').value = ''; document.getElementById('temporalization').value = ''; updateLoadsList(); updateXML(); } }
function loadFile(event) { const file = event.target.files[0]; if (file) { const reader = new FileReader(); reader.onload = function(e) { parseXML(e.target.result); }; reader.readAsText(file); } }
function parseXML(xmlText) { const parser = new DOMParser(); const xmlDoc = parser.parseFromString(xmlText, "text/xml"); const target = xmlDoc.querySelector("target"); if (target) { targetData.name = target.getAttribute("name") || ""; targetData.database = target.getAttribute("database") || ""; targetData.temporalization = target.getAttribute("temporalization") || ""; document.getElementById("targetName").value = targetData.name; document.getElementById("targetDatabase").value = targetData.database; document.getElementById("temporalization").value = targetData.temporalization; targetData.loads = []; const loads = target.querySelectorAll("load"); loads.forEach(load => { const loadObj = { source: load.getAttribute("source") || "", target: load.getAttribute("target") || "", anchor: load.getAttribute("anchor") || "", type: load.getAttribute("type") || "", pass: load.getAttribute("pass") || "", sqlBefore: "", sqlAfter: "", selectStatement: "", mappings: [] }; const sqlElements = load.querySelectorAll("sql"); sqlElements.forEach(sql => { const position = sql.getAttribute("position"); if (position === "before") { loadObj.sqlBefore = sql.textContent.trim(); } else if (position === "after") { loadObj.sqlAfter = sql.textContent.trim(); } }); const textNodes = Array.from(load.childNodes).filter(node => node.nodeType === 3 && node.textContent.trim()); if (textNodes.length > 0) { loadObj.selectStatement = textNodes.map(n => n.textContent).join("\n").trim(); } const maps = load.querySelectorAll("map"); maps.forEach(map => { loadObj.mappings.push({ source: map.getAttribute("source") || "", target: map.getAttribute("target") || "", as: map.getAttribute("as") || "", attribute: map.getAttribute("attribute") || "", deletable: map.getAttribute("deletable") === "true" }); }); targetData.loads.push(loadObj); }); updateLoadsList(); updateXML(); } }
Expand All @@ -236,11 +239,13 @@ <h2 class="modal-title">Edit Load</h2>
function removeLoad(index) { if (confirm('Are you sure you want to remove this load?')) { targetData.loads.splice(index, 1); updateLoadsList(); updateXML(); } }
function updateLoadsList() { const tbody = document.getElementById('loadsList'); tbody.innerHTML = ''; if (targetData.loads.length === 0) { tbody.innerHTML = `<tr><td colspan="7" style="text-align: center; padding: 2rem; color: var(--text-secondary);">No loads defined. Click 'Add Load' to get started.</td></tr>`; return; } targetData.loads.forEach((load, index) => { const tr = document.createElement('tr'); tr.innerHTML = `<td>${escapeHtml(load.source)}</td><td>${escapeHtml(load.target)}</td><td>${load.type ? `<span style="background:var(--primary);color:white;padding:2px 6px;border-radius:4px;font-size:0.7rem;">${load.type.toUpperCase()}</span>` : 'Default'}</td><td>${escapeHtml(load.anchor || 'N/A')}</td><td>${escapeHtml(load.pass || 'N/A')}</td><td>${load.mappings ? load.mappings.length : 0}</td><td class="actions"><button class="btn-icon" onclick="editLoad(${index})" title="Edit Load">✏️</button><button class="btn-icon btn-danger" onclick="removeLoad(${index})" title="Remove Load">🗑️</button></td>`; tbody.appendChild(tr); }); }
function generateXML() { targetData.name = document.getElementById('targetName').value; targetData.database = document.getElementById('targetDatabase').value; targetData.temporalization = document.getElementById('temporalization').value; let xml = `<target name="${targetData.name}" database="${targetData.database}"`; if (targetData.temporalization) { xml += ` temporalization="${targetData.temporalization}"`; } xml += '>\n'; targetData.loads.forEach(load => { xml += ` <load source="${load.source}" target="${load.target}"`; if (load.anchor) xml += ` anchor="${load.anchor}"`; if (load.type) xml += ` type="${load.type}"`; if (load.pass) xml += ` pass="${load.pass}"`; xml += '>\n'; if (load.sqlBefore) { xml += ` <sql position="before"><![CDATA[\n${load.sqlBefore}\n ]]></sql>\n`; } if (load.mappings && load.mappings.length > 0) { load.mappings.forEach(map => { xml += ` <map source="${map.source}" target="${map.target}"`; if (map.as) xml += ` as="${map.as}"`; if (map.attribute) xml += ` attribute="${map.attribute}"`; xml += '/>\n'; }); } if (load.selectStatement) { xml += ` <![CDATA[\n${load.selectStatement}\n ]]>\n`; } if (load.sqlAfter) { xml += ` <sql position="after"><![CDATA[\n${load.sqlAfter}\n ]]></sql>\n`; } xml += ' </load>\n'; }); xml += '</target>'; return xml; }
function updateXML() { const rawXml = generateXML(); document.getElementById('xmlOutput').innerHTML = syntaxHighlightXML(rawXml); }
function updateXML() { const rawXml = generateXML(); document.getElementById('xmlOutput').innerHTML = syntaxHighlightXML(rawXml); if (currentTab === 'preview') { updateTreeView(rawXml); } }
function downloadXML() { const xml = generateXML(); const blob = new Blob([xml], { type: 'application/xml' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `${targetData.name || 'output'}.xml`; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); }
function escapeHtml(text) { return text ? text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#039;") : ''; }
function syntaxHighlightXML(xml) { let escaped = escapeHtml(xml); return escaped.replace(/(&lt;!\[CDATA\[|\]\]&gt;)/g, '<span style="color:var(--text-secondary); font-style: italic;">$&</span>').replace(/(&lt;\??)(\/?\w+)/g, '<span class="xml-tag">$1$2</span>').replace(/(\w+)=(&quot;.*?&quot;)/g, '<span class="xml-attr-name">$1</span>=<span class="xml-attr-value">$2</span>'); }
function updateTreeView(rawXml) { const container = document.getElementById('treeView'); const parser = new DOMParser(); const doc = parser.parseFromString(rawXml, "application/xml"); if (doc.querySelector('parsererror')) { container.innerHTML = '<div class="tree-text">Unable to render tree view.</div>'; return; } container.innerHTML = ''; const rootList = document.createElement('ul'); rootList.appendChild(createTreeNode(doc.documentElement)); container.appendChild(rootList); }
function createTreeNode(node) { const li = document.createElement('li'); const attrs = Array.from(node.attributes || []).map(attr => `<span class="tree-attr">${escapeHtml(attr.name)}</span>="${escapeHtml(attr.value)}"`).join(' '); const labelParts = [`<span class="tree-tag">${escapeHtml(node.nodeName)}</span>`]; if (attrs) { labelParts.push(attrs); } li.innerHTML = `<div class="tree-node">${labelParts.join(' ')}</div>`; const children = Array.from(node.childNodes).filter(child => child.nodeType === 1 || (child.nodeType === 3 && child.textContent.trim())); if (children.length) { const ul = document.createElement('ul'); children.forEach(child => { if (child.nodeType === 1) { ul.appendChild(createTreeNode(child)); } else { const textLi = document.createElement('li'); textLi.innerHTML = `<div class="tree-text">${escapeHtml(child.textContent.trim())}</div>`; ul.appendChild(textLi); } }); li.appendChild(ul); } return li; }
window.onload = function() { updateXML(); updateLoadsList(); }
</script>
</body>
</html>```
</html>```