-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathmbg-format-scd.js
130 lines (113 loc) · 4.01 KB
/
mbg-format-scd.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
/** Helper function to identify escape XML entities in a string */
function escapeXML(str) {
return str
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''');
}
/* Helper function to normalize CDATA content */
function normalizeCDATA(content) {
// Replace CRLF (\r\n) and CR (\r) with LF (\n)
return content.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
}
/** Helper function to format attributes */
function formatAttributes(element) {
return Array.from(element.attributes)
.map(attr => `${attr.name}="${escapeXML(attr.value)}"`)
.join(' ');
}
/** Helper function to recursively format tags */
function formatNode(node, indentLevel = 0) {
const INDENT = ' ';
const indent = INDENT.repeat(indentLevel);
if (node.nodeType === Node.TEXT_NODE) {
const textContent = node.textContent.trim();
return textContent ? escapeXML(textContent) : null;
}
if (node.nodeType === Node.CDATA_SECTION_NODE) {
const content = normalizeCDATA(node.nodeValue);
return `${indent}<![CDATA[${content}]]>`;
}
if (node.nodeType === Node.ELEMENT_NODE) {
const { tagName } = node;
const attributes = formatAttributes(node);
const children = Array.from(node.childNodes);
// Special handling for <Private> tags with CDATA content
if (
tagName === 'Private' &&
children.some(child => child.nodeType === Node.CDATA_SECTION_NODE)
) {
const openingTag = attributes
? `${indent}<${tagName} ${attributes}>`
: `${indent}<${tagName}>`;
const cdataNode = children.find(
child => child.nodeType === Node.CDATA_SECTION_NODE,
);
const cdataContent = formatNode(cdataNode, indentLevel + 1);
return `${openingTag}\n${cdataContent}\n${indent}</${tagName}>`;
}
// Handle self-closing tags
if (children.length === 0) {
return attributes
? `${indent}<${tagName} ${attributes}/>`
: `${indent}<${tagName}/>`;
}
// Check if element has a single text node with a value
if (
children.length === 1 &&
children[0].nodeType === Node.TEXT_NODE &&
children[0].textContent.trim()
) {
const textContent = escapeXML(children[0].textContent.trim());
return attributes
? `${indent}<${tagName} ${attributes}>${textContent}</${tagName}>`
: `${indent}<${tagName}>${textContent}</${tagName}>`;
}
// Otherwise, format opening tag, children, and closing tag
const openingTag = attributes
? `${indent}<${tagName} ${attributes}>`
: `${indent}<${tagName}>`;
const closingTag = `${indent}</${tagName}>`;
const formattedChildren = children
.map(child => formatNode(child, indentLevel + 1))
.filter(child => child !== null)
.join('\n');
return `${openingTag}\n${formattedChildren}\n${closingTag}`;
}
return null;
}
/** Helper function to convert input to a Document if it is a string */
function getDocument(input) {
let doc;
if (typeof input === 'string') {
// If it is a string, parse and check for errors
const parser = new DOMParser();
doc = parser.parseFromString(input, 'application/xml');
if (doc.getElementsByTagName('parsererror').length > 0) {
throw new Error('Invalid XML string provided.');
}
} else if (input instanceof Document) {
doc = input;
} else {
throw new Error('Input must be a valid XML string or Document.');
}
return doc;
}
/**
* Public helper function to fix formatting of a new SCD/CID/ICD document
* @param {string|Document} input The XML string or Document to be formatted
*/
export function formatNewSCD(input) {
const doc = getDocument(input);
// Start formatting from the document's root
const root = doc.documentElement;
const formatted = `${formatNode(root)}\n`;
// Prepend the XML declaration if it is missing
const xmlDeclaration = '<?xml version="1.0" encoding="utf-8"?>\n';
if (!formatted.startsWith(xmlDeclaration)) {
return xmlDeclaration + formatted;
}
return formatted;
}