mirror of https://github.com/vladmandic/human
158 lines
5.6 KiB
JavaScript
158 lines
5.6 KiB
JavaScript
let callbackFunction = null;
|
|
|
|
function createElement(type, config) {
|
|
const htmlElement = document.createElement(type);
|
|
if (config === undefined) return htmlElement;
|
|
if (config.className) htmlElement.className = config.className;
|
|
if (config.content) htmlElement.textContent = config.content;
|
|
if (config.style) htmlElement.style = config.style;
|
|
if (config.children) config.children.forEach((el) => !el || htmlElement.appendChild(el));
|
|
return htmlElement;
|
|
}
|
|
|
|
function createExpandedElement(node) {
|
|
const iElem = createElement('i');
|
|
if (node.expanded) { iElem.className = 'fas fa-caret-down'; } else { iElem.className = 'fas fa-caret-right'; }
|
|
const caretElem = createElement('div', { style: 'width: 18px; text-align: center; cursor: pointer', children: [iElem] });
|
|
const handleClick = node.toggle.bind(node);
|
|
caretElem.addEventListener('click', handleClick);
|
|
const indexElem = createElement('div', { className: 'json json-index', content: node.key });
|
|
indexElem.addEventListener('click', handleClick);
|
|
const typeElem = createElement('div', { className: 'json json-type', content: node.type });
|
|
const keyElem = createElement('div', { className: 'json json-key', content: node.key });
|
|
keyElem.addEventListener('click', handleClick);
|
|
const sizeElem = createElement('div', { className: 'json json-size' });
|
|
sizeElem.addEventListener('click', handleClick);
|
|
if (node.type === 'array') {
|
|
sizeElem.innerText = `[${node.children.length} items]`;
|
|
} else if (node.type === 'object') {
|
|
const size = node.children.find((item) => item.key === 'size');
|
|
sizeElem.innerText = size ? `{${size.value.toLocaleString()} bytes}` : `{${node.children.length} properties}`;
|
|
}
|
|
let lineChildren;
|
|
if (node.key === null) lineChildren = [caretElem, typeElem, sizeElem];
|
|
else if (node.parent.type === 'array') lineChildren = [caretElem, indexElem, sizeElem];
|
|
else lineChildren = [caretElem, keyElem, sizeElem];
|
|
const lineElem = createElement('div', { className: 'json-line', children: lineChildren });
|
|
if (node.depth > 0) lineElem.style = `margin-left: ${node.depth * 24}px;`;
|
|
return lineElem;
|
|
}
|
|
|
|
function createNotExpandedElement(node) {
|
|
const caretElem = createElement('div', { style: 'width: 18px' });
|
|
const keyElem = createElement('div', { className: 'json json-key', content: node.key });
|
|
const separatorElement = createElement('div', { className: 'json-separator', content: ':' });
|
|
const valueType = ` json-${typeof node.value}`;
|
|
const valueContent = node.value.toLocaleString();
|
|
const valueElement = createElement('div', { className: `json json-value${valueType}`, content: valueContent });
|
|
const lineElem = createElement('div', { className: 'json-line', children: [caretElem, keyElem, separatorElement, valueElement] });
|
|
if (node.depth > 0) lineElem.style = `margin-left: ${node.depth * 24}px;`;
|
|
return lineElem;
|
|
}
|
|
|
|
function createNode() {
|
|
return {
|
|
key: '',
|
|
parent: {},
|
|
value: null,
|
|
expanded: false,
|
|
type: '',
|
|
children: [],
|
|
elem: {},
|
|
depth: 0,
|
|
|
|
hideChildren() {
|
|
if (Array.isArray(this.children)) {
|
|
this.children.forEach((item) => {
|
|
item['elem']['classList'].add('hide');
|
|
if (item['expanded']) item.hideChildren();
|
|
});
|
|
}
|
|
},
|
|
showChildren() {
|
|
if (Array.isArray(this.children)) {
|
|
this.children.forEach((item) => {
|
|
item['elem']['classList'].remove('hide');
|
|
if (item['expanded']) item.showChildren();
|
|
});
|
|
}
|
|
},
|
|
toggle() {
|
|
if (this.expanded) {
|
|
this.hideChildren();
|
|
const icon = this.elem?.querySelector('.fas');
|
|
icon.classList.replace('fa-caret-down', 'fa-caret-right');
|
|
if (callbackFunction !== null) callbackFunction(null);
|
|
} else {
|
|
this.showChildren();
|
|
const icon = this.elem?.querySelector('.fas');
|
|
icon.classList.replace('fa-caret-right', 'fa-caret-down');
|
|
if (this.type === 'object') {
|
|
if (callbackFunction !== null) callbackFunction(`${this.parent?.key}/${this.key}`);
|
|
}
|
|
}
|
|
this.expanded = !this.expanded;
|
|
},
|
|
};
|
|
}
|
|
|
|
function getType(val) {
|
|
let type
|
|
if (Array.isArray(val)) type = 'array';
|
|
else if (val === null) type = 'null';
|
|
else type = typeof val;
|
|
return type;
|
|
}
|
|
|
|
function traverseObject(obj, parent, filter) {
|
|
for (const key in obj) {
|
|
const child = createNode();
|
|
child.parent = parent;
|
|
child.key = key;
|
|
child.type = getType(obj[key]);
|
|
child.depth = parent.depth + 1;
|
|
child.expanded = false;
|
|
if (Array.isArray(filter)) {
|
|
for (const filtered of filter) {
|
|
if (key === filtered) return;
|
|
}
|
|
}
|
|
if (typeof obj[key] === 'object') {
|
|
child.children = [];
|
|
parent.children.push(child);
|
|
traverseObject(obj[key], child, filter);
|
|
child.elem = createExpandedElement(child);
|
|
} else {
|
|
child.value = obj[key];
|
|
child.elem = createNotExpandedElement(child);
|
|
parent.children.push(child);
|
|
}
|
|
}
|
|
}
|
|
|
|
function createTree(obj, title, filter) {
|
|
const tree = createNode();
|
|
tree.type = title;
|
|
tree.key = title;
|
|
tree.children = [];
|
|
tree.expanded = true;
|
|
traverseObject(obj, tree, filter);
|
|
tree.elem = createExpandedElement(tree);
|
|
return tree;
|
|
}
|
|
|
|
function traverseTree(node, callback) {
|
|
callback(node);
|
|
if (node.children !== null) node.children.forEach((item) => traverseTree(item, callback));
|
|
}
|
|
|
|
async function jsonView(json, element, title = '', filter = []) {
|
|
const tree = createTree(json, title, filter);
|
|
traverseTree(tree, (node) => {
|
|
if (!node.expanded) node.hideChildren();
|
|
element.appendChild(node.elem);
|
|
});
|
|
}
|
|
|
|
export default jsonView;
|