(function (trim, esc) {
var xml = new ActiveXObject("Msxml2.DOMDocument.6.0");
xml.async = false;
// Helper Function: returns the subjects cast type
function getType(subject) {
return Object.prototype.toString.call(subject).match(/^\[object (\S+)\]$/)[1].toLowerCase();
}
// Helper Function: formats text to be valid YAML
function yamlTextEscape(text) {
// Empty string
if (text === '') {
return '""';
}
// String needs to be quoted:
// Starts with: ! & * - ? # | > @ ` " '
// Contains: { } [ ] ( ) , : \ / tab carriage-return line-feed
// Ends with space
// is: void null true false
if (/(?:^[!&*-?#|>@`"' ])|[{}\[\]\(\),:\\\/\t\r\n]|(?: $)|(?:^(?:void|null|true|false)$)/g.test(text)) {
return '"' + text.replace(
/[\\"\u0000-\u001F\u2028\u2029]/g,
function (chr) {
return {'"': '\\"', '\\': '\\\\', '\b': '\\b', '\f': '\\f', '\n': '\\n', '\r': '\\r', '\t': '\\t'}[chr] || '\\u' + (chr.charCodeAt(0) + 0x10000).toString(16).substr(1);
}
) + '"';
}
// String can be left as-is
return text;
}
// Formats input into valid YAML
yaml = function (data, indent) {
// undefined: do nothing
if (data === undefined) {
return;
}
var type = getType(data),
key,
value,
hasChild,
prefix,
result = [];
// Null, Boolean, Number: convert to string
if (data === null || data === !!data || (type === 'number' && isFinite(data))) {
return '' + data;
}
// String: escape text
if (type === 'string') {
return yamlTextEscape(data);
}
// Array
if (type === 'array') {
// Generate prefix indent
prefix = Array(indent + 1).join(' ') + '-';
// Loop over each item in array
for (key = 0; key < data.length; key += 1) {
// Invalid
if (data[key] === undefined) {
continue;
}
// Get item and item type
value = data[key];
type = getType(value);
// String
if (type === 'string') {
result.push(prefix + ' ' + yamlTextEscape(value));
// Null, Boolean, Number: convert to string
} else if (value === null || value === !!value || (type === 'number' && isFinite(value))) {
result.push(prefix + ' ' + value);
// Array
} else if (type === 'array') {
value = yaml(value, indent + 2);
// value is empty array
if (value === '[]') {
result.push(prefix + ' []');
// Value is populated collection
} else {
result.push(prefix, value);
}
// Collection
} else if (type === 'object') {
value = yaml(value, indent + 2);
// value is empty collection
if (value === '{}') {
result.push(prefix + ' {}');
// Value is populated collection
} else {
result.push(prefix, value);
}
// Unkown type
} else {
continue;
}
// Subject array has atleast one valid child
hasChild = true;
}
if (hasChild) {
return result.join('\n');
}
// no valid items in the array
return '[]';
}
// Collection
if (type === 'object') {
// Loop over each item in the collection
for (key in data) {
// Invalid item
if (!Object.prototype.hasOwnProperty.call(data, key) || data[key] === undefined) {
continue;
}
// Get item and item type
value = data[key];
type = getType(value);
// Prepare indent prefix
prefix = Array(indent + 1).join(' ') + yamlTextEscape(key) + ':';
// String
if (type === 'string') {
result.push(prefix + ' ' + yamlTextEscape(value));
// Null, Boolean, Number: convert to string
} else if (value === null || value === !!value || (type === 'number' && isFinite(value))) {
result.push(prefix + ' ' + value);
// Array
} else if (type === 'array') {
value = yaml(value, indent + 2);
// value is empty array
if (value === '[]') {
result.push(prefix + ' []');
// Value is populated collection
} else {
result.push(prefix, value);
}
// Collection
} else if (type === 'object') {
value = yaml(value, indent + 2);
// value is empty collection
if (value === '{}') {
result.push(prefix + ' {}');
// Value is populated collection
} else {
result.push(prefix, value);
}
// Unkown type
} else {
continue;
}
// Subject collection has atleast one valid child
hasChild = true;
}
// Collection has valid items
if (hasChild) {
return result.join('\n');
}
// empty collection
return '{}';
}
}
function xmlWalk(node, parent) {
var text = (node.text === undefined || node.text === null || node.text === '') ? true : node.text,
result,
index,
attr,
attributes = {};
// No attributes or children
if (node.attributes.length === 0 && node.childNodes.length === 0) {
result = text;
} else {
// prep result object
result = {
'@value': text
};
// Add attributes to result object
for (
index = 0;
index < node.attributes.length;
index += 1
) {
attr = node.attributes[index];
result['@@' + attr.nodeName] = attr.text === undefined ? true : attr.text;
}
// Add children to result object
for (
index = 0;
index < node.childNodes.length;
index += 1
) {
child = node.childNodes[index];
if (child.nodeType === 1) {
xmlWalk(child, result);
}
}
}
// Parent has entry for node
if (Object.prototype.hasOwnProperty.call(parent, node.nodeName)) {
// Entry for node is array
if (getType(parent[node.nodeName]) === 'array') {
parent[node.nodeName].push(result);
// Entry for node is not an array
} else {
parent[node.nodeName] = [parent[node.nodeName], result];
}
// Parent does not have entry for node
} else {
parent[node.nodeName] = result;
}
return parent;
}
// Exposed Function: converts xml into valid yaml
xml2yaml = function (data) {
try {
var loaded = xml.loadXML(data);
} catch (e) {
throw new Error('INVALID_XML');
}
if (!loaded || xml.documentElement == null) {
throw new Error('INVALID_XML');
}
return yaml(xmlWalk(xml.documentElement, {}), 0);
};
// Exposed Function: converts json into valid yaml
json2yaml = function (data) {
data = String(data).replace(/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, function(chr) {
return '\\u' + ('0000' + chr.charCodeAt(0).toString(16)).slice(-4);
});
if (!/^[\],:{}\s]*$/.test(data.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@').replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
throw new Error("INVALID_JSON");
}
try {
data = eval('(' + data + ')');
} catch (e) {
throw new Error("INVALID_JSON");
}
return yaml(data, 0);
};
}());
/* Uncomment for testing - run with cscript file.js from cmd.exe
WScript.echo(json2yaml('{"key": "value"}'));
WScript.echo(xml2yaml('<node />'));
*/