Skip to content

Instantly share code, notes, and snippets.

@pb-uk
Created October 31, 2022 11:28
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save pb-uk/f78b85bcddad0b988cb66eadf0b7e284 to your computer and use it in GitHub Desktop.
Save pb-uk/f78b85bcddad0b988cb66eadf0b7e284 to your computer and use it in GitHub Desktop.
<!DOCTYPE html>
<style>
body {
font-family: system-ui, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif,
'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';
}
</style>
<p><button id="btnFetch">Fetch data</button></p>
<div id="tree"></div>
<script>
TreeStore = (() => {
const baseUrl = 'https://giant-db-connection.onrender.com';
class ApiError extends Error {
constructor(message, { error, response }) {
const e = new Error(message);
e.error = error;
e.response = response;
return e;
}
}
const fetchTree = async () => {
let response;
try {
response = await fetch(`${baseUrl}/opc_ua_tree/`);
// If we return the raw response we can access it if we need to.
return [await response.json(), response];
} catch (error) {
throw new ApiError(`Error fetching tree: ${error.message}`, {
error,
response,
});
}
};
const tree = {
server: null,
treeId: null,
rootNodeId: null,
nodes: {},
};
const loadTree = async () => {
const [data] = await fetchTree();
tree.server = data[0].server;
tree.treeId = data[0].tree_id;
tree.nodes = {};
const { id } = parseNode(data[0].tree_structure[0].Nodes[0], tree.nodes);
tree.rootNodeId = id;
};
const parseNode = (rawNode, nodes) => {
// Recursively parse the child nodes, adding to this nodes children.
const children = [];
(rawNode.Nodes ?? []).forEach((item) => {
const { id } = parseNode(item, nodes);
children.push(id);
});
// Create this node.
const node = {
id: rawNode.Attributes[0].NodeId,
title: rawNode.DisplayName,
children,
attributes: rawNode.Attributes,
};
// Save this node to the dictionary.
nodes[node.id] = node;
// Return the node so the caller can get the id.
return node;
};
const getRootNode = () => {
return tree.nodes[tree.rootNodeId] ?? null;
};
const getNode = (id) => {
return tree.nodes[id] ?? null;
};
const getTree = () => {
return tree;
};
return { loadTree, getNode, getTree, getRootNode };
})(this);
</script>
<script>
const { loadTree, getRootNode, getNode } = TreeStore;
const renderNode = (node) => {
const el = document.createElement('div');
const title = document.createElement('div');
// Add the node's id, making sure it is safe HTML.
const idNode = document.createElement('code');
idNode.innerText = node.id;
// Add the node's title, making sure it is safe HTML.
const titleNode = document.createElement('strong');
titleNode.style.padding = '0 0.5rem';
titleNode.innerText = node.title;
title.append(idNode, titleNode);
// Add the node's value, making sure it is safe HTML.
const value = node.attributes[0].Value;
if (value != null) {
const valueNode = document.createElement('span');
valueNode.innerText =
value.length > 25 ? value.substr(0, 25) + '…' : value;
title.append(valueNode);
}
el.append(title);
const listEl = document.createElement('ul');
node.children.forEach((childNodeId) => {
const itemEl = document.createElement('li');
itemEl.append(renderNode(getNode(childNodeId)));
listEl.append(itemEl);
});
el.append(listEl);
return el;
};
const renderTree = async () => {
const el = document.querySelector('#tree');
el.textContent = '';
await loadTree();
el.append(renderNode(getRootNode()));
};
const handleFetch = async (ev) => {
const btn = event.target;
btn.innerText = 'Loading...';
btn.setAttribute('disabled', '');
try {
await renderTree();
btn.innerText = 'Fetch data again';
} catch (err) {
document.querySelector('#tree').innerText = `Error: ${err.message}`;
btn.innerText = 'Retry fetch data';
console.error(err);
} finally {
btn.removeAttribute('disabled');
}
};
const render = () => {
document.querySelector('#btnFetch').addEventListener('click', handleFetch);
};
render();
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment