main.js
Version: 0.1
JavaScript:
"use strict";
// data input
// list of nodes (add new objects here)
let nodes = [
{id:0, name:"Celestial Objects", parentId:null},
{id:1, name:"Milky Way (Sagittarius A)", parentId:0},
{id:2, name:"Solar System (Sun)", parentId:1},
{id:3, name:"Planets", parentId:2},
{id:4, name:"Mercury", parentId:3},
{id:5, name:"Venus", parentId:3},
{id:6, name:"Earth", parentId:3},
{id:7, name:"Moon", parentId:6},
{id:8, name:"Mars", parentId:3},
{id:9, name:"Phobos", parentId:8},
{id:10, name:"Deimos", parentId:8},
{id:11, name:"Jupiter", parentId:3},
{id:12, name:"Io", parentId:11},
{id:13, name:"Europa", parentId:11},
{id:14, name:"Ganymede", parentId:11},
{id:15, name:"Callisto", parentId:11},
{id:16, name:"etc...", parentId:11},
{id:17, name:"etc...", parentId:3},
{id:18, name:"Dwarf Planets", parentId:2},
{id:19, name:"Pluto", parentId:18},
{id:20, name:"Charon", parentId:19},
{id:21, name:"Nix", parentId:19},
{id:22, name:"Hydra", parentId:19},
{id:23, name:"etc...", parentId:19},
{id:24, name:"Ceres", parentId:18},
{id:25, name:"Eris", parentId:18},
{id:26, name:"Haumea", parentId:18},
{id:27, name:"etc...", parentId:18},
{id:28, name:"Asteroids", parentId:2},
{id:29, name:"10 Hygiea", parentId:28},
{id:30, name:"101955 Bennu", parentId:28},
{id:31, name:"107 Camilla", parentId:28},
{id:32, name:"etc...", parentId:28},
{id:33, name:"Comets", parentId:2},
{id:48, name:"67P/Churyumov–Gerasimenko", parentId:33},
{id:34, name:"C/1995 O1 (Hale–Bopp)", parentId:33},
{id:35, name:"19P/Borelly", parentId:33},
{id:36, name:"1P/Halley", parentId:33},
{id:37, name:"etc...", parentId:33},
{id:38, name:"etc...", parentId:2},
{id:39, name:"Alpha Centauri (A)", parentId:1},
{id:40, name:"Alpha Centauri B", parentId:39},
{id:41, name:"Proxima Centauri", parentId:39},
{id:42, name:"Proxima b", parentId:41},
{id:43, name:"Proxima c", parentId:41},
{id:44, name:"1SWASP J1407", parentId:1},
{id:45, name:"1SWASP J1407 b", parentId:44},
{id:46, name:"Day/Night Cycle", parentId:null},
{id:47, name:"Enhanced Textures", parentId:null}
];
// debug
/*{
let properties = "";
for (let i = 0; i < nodes.length; i++) {
let node = nodes.find(item => item.id === i);
properties += ">id: " + node.id
+ " | parent id: " + node.parentId
+ " | name: " + node.name + "<\n"
;
}
console.log(properties);
}*/
// attributes initialization
for (let i = 0; i < nodes.length; i++) {
if (typeof (nodes[i].id) === "string" || (nodes[i].id) instanceof String) {
nodes[i].id = parseInt(nodes[i].id);
}
if (typeof (nodes[i].parentId) === "string" || (nodes[i].parentId) instanceof String) {
nodes[i].parentId = parseInt(nodes[i].parentId);
}
nodes[i].index = i; //loging purposes
nodes[i].nestedIndex = i; // further purposes
nodes[i].state = 0; // default state (global)
// special states (specify the default state of certain nodes here)
// 0 = unchecked, 1 = checked, 2 = greyed out
if (nodes[i].id == 2) nodes[i].state = 2;
if (nodes[i].id == 3) nodes[i].state = 1;
if (nodes[i].id == 4) nodes[i].state = 1;
if (nodes[i].id == 5) nodes[i].state = 1;
if (nodes[i].id == 6) nodes[i].state = 2;
if (nodes[i].id == 7) nodes[i].state = 1;
if (nodes[i].id == 8) nodes[i].state = 1;
if (nodes[i].id == 9) nodes[i].state = 1;
if (nodes[i].id == 10) nodes[i].state = 1;
if (nodes[i].id == 11) nodes[i].state = 1;
if (nodes[i].parentId == 11 && nodes[i].name !== "etc...") nodes[i].state = 1; // example
}
// attributes validity check
for (let i = 0; i < nodes.length; i++) {
// IDs
if (!Number.isInteger(nodes[i].id) || nodes[i].id < 0) {
logNode(nodes[i], "invalid ID for: " + nodes[i].name, 1, false, true);
} else {
for (let j = 0; j < nodes.length; j++) {
if (i == j) continue;
if (nodes[i].id == nodes[j].id) {
logNode(nodes[i], "duplicate ID for: " + nodes[i].name, 1);
break;
}
}
}
// parent IDs
if (nodes[i].parentId != null) {
if (!Number.isInteger(nodes[i].parentId) || nodes[i].parentId < 0) {
logNode(nodes[i], "invalid parent ID for: ", 2, true);
nodes[i].parentId = null;
} else if (nodes[i].parentId == nodes[i].id) {
logNode(nodes[i], "same ID and parent ID for: ", 2, true);
nodes[i].parentId = null;
} else if (!nodes.some(item => item.id === nodes[i].parentId)) {
logNode(nodes[i], "parent ID does not exists for: ", 2, true);
nodes[i].parentId = null;
} else if (nodes[i].id == nodes.find(item => item.id === nodes[i].parentId).parentId) {
logNode(nodes[i], "node nested in his own child...", 2, true);
nodes[i].parentId = null;
}
}
// states
if (!Number.isInteger(nodes[i].state) || nodes[i].state < 0 || nodes[i].state > 2) {
logNode(nodes[i], "invalid state value for: ", 1, true);
nodes[i].state = 0;
}
}
// display
// nodes nesting (recursive)
let nestedNodes = [];
let node;
function nest(children, parentId) {
for (let i = 0; i < nodes.length; i++) {
node = nodes[i];
if (node.parentId == parentId) {
children.push(node);
node.children = [];
nest(node.children, node.id);
}
}
}
nest(nestedNodes, null);
// indexing nested objects (recursive)
let nestedIndex = 0;
function indexNestedObjects(children) {
for (let i = 0; i < children.length; i++) {
children[i].nestedIndex = nestedIndex++;
indexNestedObjects(children[i].children);
}
}
indexNestedObjects(nestedNodes);
// nodes HTML printing (recursive)
function printNodes(nodesToPrint, parentList) {
for (let i = 0; i < nodesToPrint.length; i++) {
let li = document.createElement("li");
switch (nodesToPrint[i].state) {
case 0:
li.innerHTML = (nodesToPrint[i].children.length > 0 ? "<span class=\"caret\"></span>" : "")
+ (nodesToPrint[i].children.length > 0 ? "<span class=\"unchecked-box\">" : "<span class=\"unchecked-box padded\">")
+ nodesToPrint[i].name + "</span>";
break;
case 1:
li.innerHTML = (nodesToPrint[i].children.length > 0 ? "<span class=\"caret\"></span>" : "")
+ (nodesToPrint[i].children.length > 0 ? "<span class=\"checked-box\">" : "<span class=\"checked-box padded\">")
+ nodesToPrint[i].name + "</span>";
break;
case 2:
li.innerHTML = (nodesToPrint[i].children.length > 0 ? "<span class=\"caret\"></span>" : "")
+ (nodesToPrint[i].children.length > 0 ? "<span class=\"greyed-out-box\">" : "<span class=\"greyed-out-box padded\">")
+ nodesToPrint[i].name + "</span>";
break;
default: logNode(nodesToPrint[i], "invalid state value for: " + nodesToPrint[i].name + " (in function: printNodes())", 2, false, true);
}
if (nodesToPrint[i].children.length > 0) {
let ul = document.createElement("ul");
ul.classList.add("nested");
li.appendChild(ul);
printNodes(nodesToPrint[i].children, ul);
}
parentList.appendChild(li);
}
}
printNodes(nestedNodes, document.getElementById("list"));
// nodes event handling
let carets = document.getElementsByClassName("caret");
let boxes = document.querySelectorAll(".unchecked-box, .checked-box, .greyed-out-box");
// carets events
for (let i = 0; i < carets.length; i++) {
carets[i].addEventListener("click", function() {
this.classList.toggle("caret-down");
this.parentElement.querySelector(".nested").classList.toggle("active");
});
}
// boxes events
for (let i = 0; i < boxes.length; i++) {
boxes[i].targetIndex = nodes.findIndex(item => item.nestedIndex === i);
boxes[i].addEventListener("click", function() {
if (nodes[this.targetIndex].state == 0) {
nodes[this.targetIndex].state = 1;
this.classList.replace("unchecked-box", "checked-box");
} else if (nodes[this.targetIndex].state == 1) {
nodes[this.targetIndex].state = 0;
this.classList.replace("checked-box", "unchecked-box");
}
//debug
logNode(nodes[this.targetIndex], "clicked object:", 0, true, true, "Event target index: " + this.targetIndex);
});
}
// loging
function logNode(node, message, errorLevel, verbose, includeObjectForm, additionalMessage) {
if (additionalMessage == undefined) {
additionalMessage = "";
} else {
additionalMessage = "\n" + additionalMessage;
}
let prefix;
switch(errorLevel) {
case 0: prefix = "EVENT - "; break;
case 1: prefix = "WARNING - "; break;
case 2: prefix = "ERROR - "; break;
default: prefix = "INFO - ";
}
let string = verbose ? (message
+ "\n\tname: " + node.name
+ "\n\tid: " + node.id
+ "\n\tparent id: " + node.parentId
+ "\n\tindex: " + node.index
+ "\n\tnested index: " + node.nestedIndex
+ "\n\tstate: " + node.state
+ additionalMessage
) : message;
if (errorLevel > 0) {
if (includeObjectForm) {
console.error(prefix + string, node);
} else {
console.error(prefix + string);
}
alert("An error occured in the box tree! (" + errorLevel + ")\n\nDetails: " + string);
} else {
if (includeObjectForm) {
console.log(prefix + string, node);
} else {
console.log(prefix + string);
}
}
}