redoc/src/utils/jsonToHtml.ts

122 lines
3.7 KiB
TypeScript
Raw Normal View History

2017-10-12 00:01:37 +03:00
let level = 1;
export function jsonToHTML(json, maxExpandLevel) {
2017-10-12 00:01:37 +03:00
level = 1;
2018-01-22 21:30:53 +03:00
let output = '';
2017-10-12 00:01:37 +03:00
output += '<div class="redoc-json">';
output += '<code>';
output += valueToHTML(json, maxExpandLevel);
output += '</code>';
2017-10-12 00:01:37 +03:00
output += '</div>';
return output;
}
function htmlEncode(t) {
2018-01-22 21:30:53 +03:00
return t !== undefined
2017-10-12 00:01:37 +03:00
? t
.toString()
.replace(/&/g, '&amp;')
.replace(/"/g, '&quot;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
: '';
}
function stringifyStringLiteral(str: string) {
return JSON.stringify(str).slice(1, -1);
}
function decorateWithSpan(value, className) {
return '<span class="' + className + '">' + htmlEncode(value) + '</span>';
}
function punctuation(val) {
return '<span class="token punctuation">' + val + '</span>';
}
function valueToHTML(value, maxExpandLevel: number) {
2018-01-22 21:30:53 +03:00
const valueType = typeof value;
let output = '';
if (value === undefined || value === null) {
output += decorateWithSpan('null', 'token keyword');
2016-06-13 20:54:24 +03:00
} else if (value && value.constructor === Array) {
level++;
output += arrayToHTML(value, maxExpandLevel);
level--;
} else if (value && value.constructor === Date) {
output += decorateWithSpan('"' + value.toISOString() + '"', 'token string');
2016-06-13 20:54:24 +03:00
} else if (valueType === 'object') {
level++;
output += objectToHTML(value, maxExpandLevel);
level--;
2016-06-13 20:54:24 +03:00
} else if (valueType === 'number') {
output += decorateWithSpan(value, 'token number');
2016-06-13 20:54:24 +03:00
} else if (valueType === 'string') {
if (/^(http|https):\/\/[^\s]+$/.test(value)) {
2017-10-12 00:01:37 +03:00
output +=
decorateWithSpan('"', 'token string') +
2017-10-12 00:01:37 +03:00
'<a href="' +
encodeURI(value) +
2017-10-12 00:01:37 +03:00
'">' +
htmlEncode(stringifyStringLiteral(value)) +
2017-10-12 00:01:37 +03:00
'</a>' +
decorateWithSpan('"', 'token string');
} else {
output += decorateWithSpan('"' + stringifyStringLiteral(value) + '"', 'token string');
}
} else if (valueType === 'boolean') {
output += decorateWithSpan(value, 'token boolean');
}
return output;
}
function arrayToHTML(json, maxExpandLevel: number) {
const collapsed = level > maxExpandLevel ? 'collapsed' : '';
let output = `<button class="collapser" aria-label="${
level > maxExpandLevel + 1 ? 'expand' : 'collapse'
}"></button>${punctuation('[')}<span class="ellipsis"></span><ul class="array collapsible">`;
2018-01-22 21:30:53 +03:00
let hasContents = false;
const length = json.length;
for (let i = 0; i < length; i++) {
hasContents = true;
output += '<li><div class="hoverable ' + collapsed + '">';
output += valueToHTML(json[i], maxExpandLevel);
if (i < length - 1) {
output += ',';
}
output += '</div></li>';
}
output += `</ul>${punctuation(']')}`;
if (!hasContents) {
output = punctuation('[ ]');
}
return output;
}
function objectToHTML(json, maxExpandLevel: number) {
const collapsed = level > maxExpandLevel ? 'collapsed' : '';
2018-01-22 21:30:53 +03:00
const keys = Object.keys(json);
const length = keys.length;
let output = `<button class="collapser" aria-label="${
level > maxExpandLevel + 1 ? 'expand' : 'collapse'
}"></button>${punctuation('{')}<span class="ellipsis"></span><ul class="obj collapsible">`;
2018-01-22 21:30:53 +03:00
let hasContents = false;
for (let i = 0; i < length; i++) {
const key = keys[i];
hasContents = true;
output += '<li><div class="hoverable ' + collapsed + '">';
output += '<span class="property token string">"' + htmlEncode(key) + '"</span>: ';
output += valueToHTML(json[key], maxExpandLevel);
if (i < length - 1) {
output += punctuation(',');
}
output += '</div></li>';
}
output += `</ul>${punctuation('}')}`;
if (!hasContents) {
output = punctuation('{ }');
}
return output;
}