Renormalize line endings in js files

This commit is contained in:
Andrej730
2026-09-15 12:25:59 +05:00
parent cd11d82c03
commit 28a1ebdf89
4 changed files with 1600 additions and 1600 deletions
+146 -146
View File
@@ -1,146 +1,146 @@
// A global variable to hold the WebSocket connection // A global variable to hold the WebSocket connection
let socket; let socket;
// Document ready function // Document ready function
// This function is triggered once the DOM is fully loaded. // This function is triggered once the DOM is fully loaded.
// It sets the theme for the UI and // It sets the theme for the UI and
// establishes the WebSocket connection by calling connectSocket(). // establishes the WebSocket connection by calling connectSocket().
$(document).ready(function () { $(document).ready(function () {
var defaultTheme = "blender"; // Default theme to be applied var defaultTheme = "blender"; // Default theme to be applied
var theme = localStorage.getItem("theme") || defaultTheme; // Retrieve the stored theme or use the default var theme = localStorage.getItem("theme") || defaultTheme; // Retrieve the stored theme or use the default
setTheme(theme); // Apply the theme setTheme(theme); // Apply the theme
connectSocket(); connectSocket();
}); });
// Function to establish WebSocket connection // Function to establish WebSocket connection
// This function constructs the WebSocket connection URL using the SOCKET_PORT variable // This function constructs the WebSocket connection URL using the SOCKET_PORT variable
// and initializes the socket connection. It also registers event handlers to listen // and initializes the socket connection. It also registers event handlers to listen
// for various events emitted by the server, such as 'blender_connect', 'blender_disconnect', // for various events emitted by the server, such as 'blender_connect', 'blender_disconnect',
// 'theme_data', and 'demo_data'. // 'theme_data', and 'demo_data'.
function connectSocket() { function connectSocket() {
const url = "ws://localhost:" + SOCKET_PORT + "/web"; const url = "ws://localhost:" + SOCKET_PORT + "/web";
socket = io(url); socket = io(url);
console.log("socket: ", socket); console.log("socket: ", socket);
// Register socket event handlers // Register socket event handlers
socket.on("blender_connect", handleBlenderConnect); socket.on("blender_connect", handleBlenderConnect);
socket.on("blender_disconnect", handleBlenderDisconnect); socket.on("blender_disconnect", handleBlenderDisconnect);
socket.on("theme_data", handleThemeData); socket.on("theme_data", handleThemeData);
socket.on("demo_data", handleDemoData); socket.on("demo_data", handleDemoData);
} }
// Function to handle 'blender_connect' event // Function to handle 'blender_connect' event
// This function is triggered when a new Bonsai instance connects to the server. // This function is triggered when a new Bonsai instance connects to the server.
// The 'blenderId' is a unique identifier assigned by the server to each Bonsai instance. // The 'blenderId' is a unique identifier assigned by the server to each Bonsai instance.
// It helps distinguish between different instances of Bonsai connected to the server. // It helps distinguish between different instances of Bonsai connected to the server.
// The 'blenderId' is used in various parts of the code to ensure that messages and data // The 'blenderId' is used in various parts of the code to ensure that messages and data
// are correctly associated with the appropriate Bonsai instance. // are correctly associated with the appropriate Bonsai instance.
function handleBlenderConnect(blenderId) { function handleBlenderConnect(blenderId) {
console.log("blender connected: ", blenderId); console.log("blender connected: ", blenderId);
} }
// Function to handle 'blender_disconnect' event // Function to handle 'blender_disconnect' event
// This function is triggered when a Bonsai instance disconnects from the server. // This function is triggered when a Bonsai instance disconnects from the server.
// The 'blenderId' is used here to log which specific Bonsai instance has disconnected. // The 'blenderId' is used here to log which specific Bonsai instance has disconnected.
function handleBlenderDisconnect(blenderId) { function handleBlenderDisconnect(blenderId) {
console.log("blender disconnected: ", blenderId); console.log("blender disconnected: ", blenderId);
} }
// Function to handle 'demo_data' event // Function to handle 'demo_data' event
// This function processes the demo data received from the server. // This function processes the demo data received from the server.
// It extracts the message and the Bonsai instance ID ('blenderId'), // It extracts the message and the Bonsai instance ID ('blenderId'),
// and updates the DOM to display the message. The 'blenderId' is used // and updates the DOM to display the message. The 'blenderId' is used
// to differentiate between messages from different Bonsai instances. // to differentiate between messages from different Bonsai instances.
// If the message for the specific Bonsai instance is already displayed, it updates the content; // If the message for the specific Bonsai instance is already displayed, it updates the content;
// otherwise, it creates a new element to display the message. // otherwise, it creates a new element to display the message.
function handleDemoData(demoData) { function handleDemoData(demoData) {
console.log(demoData); console.log(demoData);
const message = demoData["data"]["demo_message"]; const message = demoData["data"]["demo_message"];
const blenderId = demoData["blenderId"]; const blenderId = demoData["blenderId"];
const id = "message-" + blenderId; const id = "message-" + blenderId;
const messageHeader = $("#" + id); const messageHeader = $("#" + id);
const messageText = `Bonsai instance with ID: ${blenderId} sent the message: ${message}`; const messageText = `Bonsai instance with ID: ${blenderId} sent the message: ${message}`;
if (messageHeader[0] === undefined) { if (messageHeader[0] === undefined) {
const newMessageElement = $("<h3>", { id: id }).text(messageText); const newMessageElement = $("<h3>", { id: id }).text(messageText);
$("#message-container").prepend(newMessageElement); $("#message-container").prepend(newMessageElement);
} else { } else {
messageHeader.text(messageText); messageHeader.text(messageText);
messageHeader.prependTo("#message-container"); messageHeader.prependTo("#message-container");
} }
} }
// Function to send a message to the server // Function to send a message to the server
// This function is triggered when the user submits a message. // This function is triggered when the user submits a message.
// It collects the message from the input field, constructs a message object, // It collects the message from the input field, constructs a message object,
// and emits the 'web_operator' event to the server with the message data. // and emits the 'web_operator' event to the server with the message data.
// If 'blenderId' is specified, the message is sent to a specific Bonsai instance. // If 'blenderId' is specified, the message is sent to a specific Bonsai instance.
function sendMessage() { function sendMessage() {
const inputMessage = $("#input-message").val(); const inputMessage = $("#input-message").val();
const msg = { const msg = {
sourcePage: "demo", sourcePage: "demo",
// blenderId: BlenderId, // specify the target Bonsai instance // blenderId: BlenderId, // specify the target Bonsai instance
operator: { operator: {
type: "message", type: "message",
message: inputMessage, message: inputMessage,
}, },
}; };
socket.emit("web_operator", msg); socket.emit("web_operator", msg);
} }
function handleThemeData(themeData) { function handleThemeData(themeData) {
// console.log(themeData); // console.log(themeData);
function arrayToRgbString(arr) { function arrayToRgbString(arr) {
const [r, g, b, a] = arr.map((num) => Math.round(num * 255)); const [r, g, b, a] = arr.map((num) => Math.round(num * 255));
if (a !== undefined) { if (a !== undefined) {
return `rgba(${r}, ${g}, ${b}, ${a})`; return `rgba(${r}, ${g}, ${b}, ${a})`;
} }
return `rgb(${r}, ${g}, ${b})`; return `rgb(${r}, ${g}, ${b})`;
} }
function generateCssVariableRule(theme) { function generateCssVariableRule(theme) {
let cssVariables = ":root.blender {\n"; let cssVariables = ":root.blender {\n";
for (const key in theme) { for (const key in theme) {
const cssVariableName = `--blender-${key.replace(/_/g, "-")}`; const cssVariableName = `--blender-${key.replace(/_/g, "-")}`;
const cssVariableValue = arrayToRgbString(theme[key]); const cssVariableValue = arrayToRgbString(theme[key]);
cssVariables += ` ${cssVariableName}: ${cssVariableValue};\n`; cssVariables += ` ${cssVariableName}: ${cssVariableValue};\n`;
} }
cssVariables += "}"; cssVariables += "}";
return cssVariables; return cssVariables;
} }
const cssRule = generateCssVariableRule(themeData.theme); const cssRule = generateCssVariableRule(themeData.theme);
console.log(cssRule); console.log(cssRule);
var styleElement = $("#demo-stylesheet")[0]; var styleElement = $("#demo-stylesheet")[0];
if (styleElement) { if (styleElement) {
var sheet = styleElement.sheet || styleElement.styleSheet; var sheet = styleElement.sheet || styleElement.styleSheet;
sheet.insertRule(cssRule, sheet.cssRules.length); sheet.insertRule(cssRule, sheet.cssRules.length);
} }
} }
function setTheme(theme) { function setTheme(theme) {
$("html").removeClass("light dark blender").addClass(theme); $("html").removeClass("light dark blender").addClass(theme);
$(":root").css("color-scheme", theme); $(":root").css("color-scheme", theme);
if (theme === "light") { if (theme === "light") {
$("#toggle-theme").html('<i class="fas fa-sun"></i>'); $("#toggle-theme").html('<i class="fas fa-sun"></i>');
} else if (theme === "dark") { } else if (theme === "dark") {
$("#toggle-theme").html('<i class="fas fa-moon"></i>'); $("#toggle-theme").html('<i class="fas fa-moon"></i>');
} else if (theme === "blender") { } else if (theme === "blender") {
$("#toggle-theme").html('<i class="fas fa-adjust"></i>'); $("#toggle-theme").html('<i class="fas fa-adjust"></i>');
} }
localStorage.setItem("theme", theme); localStorage.setItem("theme", theme);
} }
function toggleTheme() { function toggleTheme() {
if ($("html").hasClass("light")) { if ($("html").hasClass("light")) {
setTheme("dark"); setTheme("dark");
} else if ($("html").hasClass("dark")) { } else if ($("html").hasClass("dark")) {
setTheme("blender"); setTheme("blender");
} else { } else {
setTheme("light"); setTheme("light");
} }
} }
@@ -1,367 +1,367 @@
// keeps track of blenders connected // keeps track of blenders connected
const connectedClients = {}; const connectedClients = {};
const allDrawings = {}; const allDrawings = {};
let socket; let socket;
// Document ready function // Document ready function
$(document).ready(function () { $(document).ready(function () {
var defaultTheme = "blender"; var defaultTheme = "blender";
var theme = localStorage.getItem("theme") || defaultTheme; var theme = localStorage.getItem("theme") || defaultTheme;
setTheme(theme); setTheme(theme);
$("#dropdown-menu").change(function () { $("#dropdown-menu").change(function () {
const blenderId = $(this).attr("id"); const blenderId = $(this).attr("id");
const ifcFile = $(this).val(); const ifcFile = $(this).val();
displayDrawingsNames(blenderId, ifcFile); displayDrawingsNames(blenderId, ifcFile);
}); });
connectSocket(); connectSocket();
}); });
// Function to connect to Socket.IO server // Function to connect to Socket.IO server
function connectSocket() { function connectSocket() {
const url = "ws://localhost:" + SOCKET_PORT + "/web"; const url = "ws://localhost:" + SOCKET_PORT + "/web";
socket = io(url); socket = io(url);
console.log("socket: ", socket); console.log("socket: ", socket);
// Register socket event handlers // Register socket event handlers
socket.on("blender_connect", handleBlenderConnect); socket.on("blender_connect", handleBlenderConnect);
socket.on("blender_disconnect", handleBlenderDisconnect); socket.on("blender_disconnect", handleBlenderDisconnect);
socket.on("connect", handleWebConnect); socket.on("connect", handleWebConnect);
socket.on("connected_clients", handleConnectedClients); socket.on("connected_clients", handleConnectedClients);
//socket.on("default_data", handleDefaultData); //socket.on("default_data", handleDefaultData);
socket.on("theme_data", handleThemeData); socket.on("theme_data", handleThemeData);
socket.on("drawings_data", handleDrawingsData); socket.on("drawings_data", handleDrawingsData);
socket.on("svg_data", handleSvgData); socket.on("svg_data", handleSvgData);
} }
// function used to get drawings data from Bonsai // function used to get drawings data from Bonsai
function handleWebConnect() { function handleWebConnect() {
console.log("Handle Web Connect") console.log("Handle Web Connect")
getDrawingsData(); getDrawingsData();
} }
// Function to handle 'blender_connect' event // Function to handle 'blender_connect' event
function handleBlenderConnect(blenderId) { function handleBlenderConnect(blenderId) {
console.log("blender connected: ", blenderId); console.log("blender connected: ", blenderId);
if (!connectedClients.hasOwnProperty(blenderId)) { if (!connectedClients.hasOwnProperty(blenderId)) {
connectedClients[blenderId] = { connectedClients[blenderId] = {
shown: false, shown: false,
ifc_file: "", ifc_file: "",
has_drawings: false, has_drawings: false,
}; };
} }
$("#blender-count").text(function (i, text) { $("#blender-count").text(function (i, text) {
return parseInt(text, 10) + 1; return parseInt(text, 10) + 1;
}); });
const msg = { const msg = {
blenderId: blenderId, blenderId: blenderId,
sourcePage: "drawings", sourcePage: "drawings",
operator: { operator: {
type: "getDrawings", type: "getDrawings",
}, },
}; };
socket.emit("web_operator", msg); socket.emit("web_operator", msg);
} }
// Function to handle 'default_data' event // Function to handle 'default_data' event
function handleDefaultData(data) { function handleDefaultData(data) {
const blenderId = data["blenderId"]; const blenderId = data["blenderId"];
getDrawingsData(blenderId); getDrawingsData(blenderId);
} }
// Function to handle 'blender_disconnect' event // Function to handle 'blender_disconnect' event
function handleBlenderDisconnect(blenderId) { function handleBlenderDisconnect(blenderId) {
if (connectedClients.hasOwnProperty(blenderId)) { if (connectedClients.hasOwnProperty(blenderId)) {
delete connectedClients[blenderId]; delete connectedClients[blenderId];
removeSelectOption(blenderId); removeSelectOption(blenderId);
} }
$("#blender-count").text(function (i, text) { $("#blender-count").text(function (i, text) {
return parseInt(text, 10) - 1; return parseInt(text, 10) - 1;
}); });
} }
function handleConnectedClients(data) { function handleConnectedClients(data) {
$("#blender-count").text(data.length); $("#blender-count").text(data.length);
// console.log(data); // console.log(data);
data.forEach(function (id) { data.forEach(function (id) {
connectedClients[id] = { connectedClients[id] = {
shown: false, shown: false,
ifc_file: "", ifc_file: "",
has_drawings: false, has_drawings: false,
}; };
}); });
} }
function handleThemeData(themeData) { function handleThemeData(themeData) {
function arrayToRgbString(arr) { function arrayToRgbString(arr) {
const [r, g, b, a] = arr.map((num) => Math.round(num * 255)); const [r, g, b, a] = arr.map((num) => Math.round(num * 255));
if (a !== undefined) { if (a !== undefined) {
return `rgba(${r}, ${g}, ${b}, ${a})`; return `rgba(${r}, ${g}, ${b}, ${a})`;
} }
return `rgb(${r}, ${g}, ${b})`; return `rgb(${r}, ${g}, ${b})`;
} }
function generateCssVariableRule(theme) { function generateCssVariableRule(theme) {
let cssVariables = ":root.blender {\n"; let cssVariables = ":root.blender {\n";
for (const key in theme) { for (const key in theme) {
const cssVariableName = `--blender-${key.replace(/_/g, "-")}`; const cssVariableName = `--blender-${key.replace(/_/g, "-")}`;
const cssVariableValue = arrayToRgbString(theme[key]); const cssVariableValue = arrayToRgbString(theme[key]);
cssVariables += ` ${cssVariableName}: ${cssVariableValue};\n`; cssVariables += ` ${cssVariableName}: ${cssVariableValue};\n`;
} }
cssVariables += "}"; cssVariables += "}";
return cssVariables; return cssVariables;
} }
const cssRule = generateCssVariableRule(themeData.theme); const cssRule = generateCssVariableRule(themeData.theme);
console.log(cssRule); console.log(cssRule);
var styleElement = $("#drawings-stylesheet")[0]; var styleElement = $("#drawings-stylesheet")[0];
if (styleElement) { if (styleElement) {
var sheet = styleElement.sheet || styleElement.styleSheet; var sheet = styleElement.sheet || styleElement.styleSheet;
sheet.insertRule(cssRule, sheet.cssRules.length); sheet.insertRule(cssRule, sheet.cssRules.length);
} }
} }
function handleDrawingsData(data) { function handleDrawingsData(data) {
console.log("Running handleDrawingsData"); console.log("Running handleDrawingsData");
const blenderId = data["blenderId"]; const blenderId = data["blenderId"];
console.log(data); console.log(data);
const filename = data["data"]["ifc_file"]; const filename = data["data"]["ifc_file"];
const drawings = data["data"]["drawings_data"]["drawings"]; const drawings = data["data"]["drawings_data"]["drawings"];
const sheets = data["data"]["drawings_data"]["sheets"]; const sheets = data["data"]["drawings_data"]["sheets"];
allDrawings[filename] = { drawings: drawings, sheets: sheets }; allDrawings[filename] = { drawings: drawings, sheets: sheets };
const hasDrawings = drawings.length > 0 || sheets.length > 0; const hasDrawings = drawings.length > 0 || sheets.length > 0;
// console.log(connectedClients); // console.log(connectedClients);
if (connectedClients.hasOwnProperty(blenderId)) { if (connectedClients.hasOwnProperty(blenderId)) {
if (!connectedClients[blenderId].shown) { if (!connectedClients[blenderId].shown) {
connectedClients[blenderId] = { connectedClients[blenderId] = {
shown: true, shown: true,
ifc_file: filename, ifc_file: filename,
has_drawings: hasDrawings, has_drawings: hasDrawings,
}; };
addSelectOption(blenderId, filename); addSelectOption(blenderId, filename);
} else { } else {
updateSelectOption(blenderId, filename); updateSelectOption(blenderId, filename);
} }
} else { } else {
connectedClients[blenderId] = { connectedClients[blenderId] = {
shown: true, shown: true,
ifc_file: filename, ifc_file: filename,
has_drawings: hasDrawings, has_drawings: hasDrawings,
}; };
addSelectOption(blenderId, filename); addSelectOption(blenderId, filename);
} }
} }
function handleSvgData(data) { function handleSvgData(data) {
const svgContainer = document.getElementById("svg-container"); const svgContainer = document.getElementById("svg-container");
svgContainer.innerHTML = ""; svgContainer.innerHTML = "";
svgContainer.style.backgroundColor = "white"; svgContainer.style.backgroundColor = "white";
const dom = SVG(svgContainer).svg(data); const dom = SVG(svgContainer).svg(data);
const svgElement = dom.node.children[0]; const svgElement = dom.node.children[0];
// to make the svg not grow outside the svg container // to make the svg not grow outside the svg container
svgElement.setAttribute("width", "100%"); svgElement.setAttribute("width", "100%");
svgElement.setAttribute("height", "100%"); svgElement.setAttribute("height", "100%");
svgElement.style.maxWidth = "100%"; svgElement.style.maxWidth = "100%";
svgElement.style.maxHeight = "100%"; svgElement.style.maxHeight = "100%";
svgElement.style.width = "100%"; svgElement.style.width = "100%";
svgElement.style.height = "100%"; svgElement.style.height = "100%";
svgElement.style.boxSizing = "border-box"; // Ensure padding and borders are included in the element's total width and height svgElement.style.boxSizing = "border-box"; // Ensure padding and borders are included in the element's total width and height
const panZoomControls = svgPanZoom(svgElement, { const panZoomControls = svgPanZoom(svgElement, {
zoomEnabled: true, zoomEnabled: true,
controlIconsEnabled: true, controlIconsEnabled: true,
fit: true, fit: true,
center: true, center: true,
}); });
} }
function addSelectOption(blenderId, filename) { function addSelectOption(blenderId, filename) {
const filenameOption = $("<option></option>") const filenameOption = $("<option></option>")
.attr("id", "blender-" + blenderId) .attr("id", "blender-" + blenderId)
.val(filename) .val(filename)
.text(filename); .text(filename);
$("#dropdown-menu").append(filenameOption); $("#dropdown-menu").append(filenameOption);
} }
function updateSelectOption(blenderId, filename) { function updateSelectOption(blenderId, filename) {
$("#blender-" + blenderId).text(filename); $("#blender-" + blenderId).text(filename);
// console.log(blenderId, filename); // console.log(blenderId, filename);
console.log($("#dropdown-menu").val()); console.log($("#dropdown-menu").val());
if ($("#dropdown-menu").val() === filename) { if ($("#dropdown-menu").val() === filename) {
displayDrawingsNames(blenderId, filename); displayDrawingsNames(blenderId, filename);
} }
} }
function removeSelectOption(blenderId) { function removeSelectOption(blenderId) {
const filename = $("#blender-" + blenderId).val(); const filename = $("#blender-" + blenderId).val();
const selectedOption = $("#dropdown-menu").val(); const selectedOption = $("#dropdown-menu").val();
$("#blender-" + blenderId).remove(); $("#blender-" + blenderId).remove();
delete allDrawings[filename]; delete allDrawings[filename];
if (selectedOption == filename) { if (selectedOption == filename) {
$("#dropdown-menu").val("0"); $("#dropdown-menu").val("0");
$("#drawings-sub-panel").empty(); $("#drawings-sub-panel").empty();
$("#sheets-sub-panel").empty(); $("#sheets-sub-panel").empty();
} }
} }
function displayDrawingsNames(blenderId, ifcFile) { function displayDrawingsNames(blenderId, ifcFile) {
drawings = allDrawings[ifcFile].drawings; drawings = allDrawings[ifcFile].drawings;
sheets = allDrawings[ifcFile].sheets; sheets = allDrawings[ifcFile].sheets;
function createSvgNames(text, index, type) { function createSvgNames(text, index, type) {
var label = $("<li></lio>") var label = $("<li></lio>")
.text(text) .text(text)
.attr("id", type + "-" + index) .attr("id", type + "-" + index)
.addClass("svg-name") .addClass("svg-name")
.click(function () { .click(function () {
const id = $(this).attr("id").split("-"); const id = $(this).attr("id").split("-");
const index = parseInt(id[1]); const index = parseInt(id[1]);
const type = id[0]; const type = id[0];
const ifcFile = $("#dropdown-menu").val(); const ifcFile = $("#dropdown-menu").val();
var path = ""; var path = "";
if (type === "drawing") { if (type === "drawing") {
path = allDrawings[ifcFile].drawings[index].path; path = allDrawings[ifcFile].drawings[index].path;
} else if (type === "sheet") { } else if (type === "sheet") {
path = allDrawings[ifcFile].sheets[index].path; path = allDrawings[ifcFile].sheets[index].path;
} }
const msg = { const msg = {
path: path, path: path,
}; };
// console.log(msg); // console.log(msg);
socket.emit("get_svg", msg); socket.emit("get_svg", msg);
$("li").removeClass("selected-svg"); $("li").removeClass("selected-svg");
$(this).addClass("selected-svg"); $(this).addClass("selected-svg");
}); });
if (type === "drawing") $("#drawings-sub-panel").append(label); if (type === "drawing") $("#drawings-sub-panel").append(label);
else if (type === "sheet") $("#sheets-sub-panel").append(label); else if (type === "sheet") $("#sheets-sub-panel").append(label);
} }
$("#drawings-sub-panel").empty(); $("#drawings-sub-panel").empty();
drawings.forEach(function (drawing, index) { drawings.forEach(function (drawing, index) {
createSvgNames(drawing.name, index, "drawing"); createSvgNames(drawing.name, index, "drawing");
}); });
$("#sheets-sub-panel").empty(); $("#sheets-sub-panel").empty();
sheets.forEach(function (sheet, index) { sheets.forEach(function (sheet, index) {
createSvgNames(sheet.name, index, "sheet"); createSvgNames(sheet.name, index, "sheet");
}); });
} }
function setTheme(theme) { function setTheme(theme) {
$("html").removeClass("light dark blender").addClass(theme); $("html").removeClass("light dark blender").addClass(theme);
$(":root").css("color-scheme", theme); $(":root").css("color-scheme", theme);
if (theme === "light") { if (theme === "light") {
$("#toggle-theme").html('<i class="fas fa-sun"></i>'); $("#toggle-theme").html('<i class="fas fa-sun"></i>');
} else if (theme === "dark") { } else if (theme === "dark") {
$("#toggle-theme").html('<i class="fas fa-moon"></i>'); $("#toggle-theme").html('<i class="fas fa-moon"></i>');
} else if (theme === "blender") { } else if (theme === "blender") {
$("#toggle-theme").html('<i class="fas fa-adjust"></i>'); $("#toggle-theme").html('<i class="fas fa-adjust"></i>');
} }
localStorage.setItem("theme", theme); localStorage.setItem("theme", theme);
} }
function toggleTheme() { function toggleTheme() {
if ($("html").hasClass("light")) { if ($("html").hasClass("light")) {
setTheme("dark"); setTheme("dark");
} else if ($("html").hasClass("dark")) { } else if ($("html").hasClass("dark")) {
setTheme("blender"); setTheme("blender");
} else { } else {
setTheme("light"); setTheme("light");
} }
} }
function toggleClientList() { function toggleClientList() {
var clientList = $("#client-list"); var clientList = $("#client-list");
if (clientList.hasClass("show")) { if (clientList.hasClass("show")) {
clientList.removeClass("show"); clientList.removeClass("show");
return; return;
} }
clientList.empty(); clientList.empty();
var counter = 0; var counter = 0;
$.each(connectedClients, function (id, client) { $.each(connectedClients, function (id, client) {
counter++; counter++;
const dropdownIcon = $("<i>") const dropdownIcon = $("<i>")
.addClass("fas fa-chevron-down") .addClass("fas fa-chevron-down")
.css("margin-left", "0.5rem"); .css("margin-left", "0.5rem");
const clientDiv = $("<div>") const clientDiv = $("<div>")
.addClass("client") .addClass("client")
.text(client.ifc_file || "Blender " + counter); .text(client.ifc_file || "Blender " + counter);
clientDiv.append(dropdownIcon); clientDiv.append(dropdownIcon);
const clientDetailsDiv = $("<div>").addClass("client-details"); const clientDetailsDiv = $("<div>").addClass("client-details");
if (id) { if (id) {
const clientId = $("<div>") const clientId = $("<div>")
.addClass("client-detail") .addClass("client-detail")
.text(`Blender ID: ${id}`); .text(`Blender ID: ${id}`);
clientDetailsDiv.append(clientId); clientDetailsDiv.append(clientId);
} }
if (!client.has_drawings) { if (!client.has_drawings) {
const clientShown = $("<div>") const clientShown = $("<div>")
.addClass("client-detail") .addClass("client-detail")
.text("No drawings exist for this IFC File yet"); .text("No drawings exist for this IFC File yet");
clientDetailsDiv.append(clientShown); clientDetailsDiv.append(clientShown);
} else { } else {
const clientNumbers = $("<div>") const clientNumbers = $("<div>")
.addClass("client-detail") .addClass("client-detail")
.text( .text(
`${allDrawings[client.ifc_file].drawings.length} Drawing(s), `${allDrawings[client.ifc_file].drawings.length} Drawing(s),
${allDrawings[client.ifc_file].sheets.length} Sheet(s)` ${allDrawings[client.ifc_file].sheets.length} Sheet(s)`
); );
clientDetailsDiv.append(clientNumbers); clientDetailsDiv.append(clientNumbers);
} }
clientDiv.append(clientDetailsDiv); clientDiv.append(clientDetailsDiv);
clientDiv.on("click", function () { clientDiv.on("click", function () {
clientDetailsDiv.toggleClass("show"); clientDetailsDiv.toggleClass("show");
}); });
clientList.append(clientDiv); clientList.append(clientDiv);
}); });
clientList.addClass("show"); clientList.addClass("show");
} }
function getDrawingsData(blenderId) { function getDrawingsData(blenderId) {
const msg = { const msg = {
sourcePage: "drawings", sourcePage: "drawings",
operator: { operator: {
type: "getDrawings", type: "getDrawings",
}, },
}; };
if (blenderId !== undefined) { if (blenderId !== undefined) {
msg.BlenderId = blenderId; msg.BlenderId = blenderId;
} }
socket.emit("web_operator", msg); socket.emit("web_operator", msg);
} }
File diff suppressed because it is too large Load Diff
@@ -1,487 +1,487 @@
// keeps track of blenders connected in form of // keeps track of blenders connected in form of
// {shown:bool, filename:string, headers:array, warning: false} // {shown:bool, filename:string, headers:array, warning: false}
const connectedClients = {}; const connectedClients = {};
let socket; let socket;
// Document ready function // Document ready function
$(document).ready(function () { $(document).ready(function () {
var defaultTheme = "blender"; var defaultTheme = "blender";
var theme = localStorage.getItem("theme") || defaultTheme; var theme = localStorage.getItem("theme") || defaultTheme;
setTheme(theme); setTheme(theme);
connectSocket(); connectSocket();
}); });
// Function to connect to Socket.IO server // Function to connect to Socket.IO server
function connectSocket() { function connectSocket() {
const url = "ws://localhost:" + SOCKET_PORT + "/web"; const url = "ws://localhost:" + SOCKET_PORT + "/web";
socket = io(url); socket = io(url);
console.log("socket: ", socket); console.log("socket: ", socket);
// Register socket event handlers // Register socket event handlers
socket.on("blender_connect", handleBlenderConnect); socket.on("blender_connect", handleBlenderConnect);
socket.on("blender_disconnect", handleBlenderDisconnect); socket.on("blender_disconnect", handleBlenderDisconnect);
socket.on("connected_clients", handleConnectedClients); socket.on("connected_clients", handleConnectedClients);
socket.on("theme_data", handleThemeData); socket.on("theme_data", handleThemeData);
socket.on("csv_data", handleCsvData); socket.on("csv_data", handleCsvData);
socket.on("default_data", handleDefaultData); socket.on("default_data", handleDefaultData);
} }
// Function to handle 'blender_connect' event // Function to handle 'blender_connect' event
function handleBlenderConnect(blenderId) { function handleBlenderConnect(blenderId) {
console.log("blender connected: ", blenderId); console.log("blender connected: ", blenderId);
if (!connectedClients.hasOwnProperty(blenderId)) { if (!connectedClients.hasOwnProperty(blenderId)) {
connectedClients[blenderId] = { shown: false, ifc_file: "" }; connectedClients[blenderId] = { shown: false, ifc_file: "" };
} }
$("#blender-count").text(function (i, text) { $("#blender-count").text(function (i, text) {
return parseInt(text, 10) + 1; return parseInt(text, 10) + 1;
}); });
} }
// Function to handle 'blender_disconnect' event // Function to handle 'blender_disconnect' event
function handleBlenderDisconnect(blenderId) { function handleBlenderDisconnect(blenderId) {
console.log("blender disconnected: ", blenderId); console.log("blender disconnected: ", blenderId);
if (connectedClients.hasOwnProperty(blenderId)) { if (connectedClients.hasOwnProperty(blenderId)) {
delete connectedClients[blenderId]; delete connectedClients[blenderId];
removeTableElement(blenderId); removeTableElement(blenderId);
} }
$("#blender-count").text(function (i, text) { $("#blender-count").text(function (i, text) {
return parseInt(text, 10) - 1; return parseInt(text, 10) - 1;
}); });
} }
function handleConnectedClients(data) { function handleConnectedClients(data) {
$("#blender-count").text(data.length); $("#blender-count").text(data.length);
// console.log(data); // console.log(data);
data.forEach(function (id) { data.forEach(function (id) {
connectedClients[id] = { shown: false, ifc_file: "" }; connectedClients[id] = { shown: false, ifc_file: "" };
}); });
} }
function handleThemeData(themeData) { function handleThemeData(themeData) {
// console.log(themeData); // console.log(themeData);
function arrayToRgbString(arr) { function arrayToRgbString(arr) {
const [r, g, b, a] = arr.map((num) => Math.round(num * 255)); const [r, g, b, a] = arr.map((num) => Math.round(num * 255));
if (a !== undefined) { if (a !== undefined) {
return `rgba(${r}, ${g}, ${b}, ${a})`; return `rgba(${r}, ${g}, ${b}, ${a})`;
} }
return `rgb(${r}, ${g}, ${b})`; return `rgb(${r}, ${g}, ${b})`;
} }
function generateCssVariableRule(theme) { function generateCssVariableRule(theme) {
let cssVariables = ":root.blender {\n"; let cssVariables = ":root.blender {\n";
for (const key in theme) { for (const key in theme) {
const cssVariableName = `--blender-${key.replace(/_/g, "-")}`; const cssVariableName = `--blender-${key.replace(/_/g, "-")}`;
const cssVariableValue = arrayToRgbString(theme[key]); const cssVariableValue = arrayToRgbString(theme[key]);
cssVariables += ` ${cssVariableName}: ${cssVariableValue};\n`; cssVariables += ` ${cssVariableName}: ${cssVariableValue};\n`;
} }
cssVariables += "}"; cssVariables += "}";
return cssVariables; return cssVariables;
} }
const cssRule = generateCssVariableRule(themeData.theme); const cssRule = generateCssVariableRule(themeData.theme);
console.log(cssRule); console.log(cssRule);
var styleElement = $("#index-stylesheet")[0]; var styleElement = $("#index-stylesheet")[0];
if (styleElement) { if (styleElement) {
var sheet = styleElement.sheet || styleElement.styleSheet; var sheet = styleElement.sheet || styleElement.styleSheet;
sheet.insertRule(cssRule, sheet.cssRules.length); sheet.insertRule(cssRule, sheet.cssRules.length);
} }
} }
// Function to handle 'csv_data' event // Function to handle 'csv_data' event
function handleCsvData(data) { function handleCsvData(data) {
const blenderId = data["blenderId"]; const blenderId = data["blenderId"];
const csvData = data["data"]["csv_data"]; const csvData = data["data"]["csv_data"];
const filename = data["data"]["ifc_file"]; const filename = data["data"]["ifc_file"];
console.log(data); console.log(data);
if (connectedClients.hasOwnProperty(blenderId)) { if (connectedClients.hasOwnProperty(blenderId)) {
if (!connectedClients[blenderId].shown) { if (!connectedClients[blenderId].shown) {
connectedClients[blenderId] = { connectedClients[blenderId] = {
shown: true, shown: true,
ifc_file: filename, ifc_file: filename,
headers: [], headers: [],
warning: false, warning: false,
}; };
addTableElement(blenderId, csvData, filename); addTableElement(blenderId, csvData, filename);
} else { } else {
updateTableElement(blenderId, csvData, filename); updateTableElement(blenderId, csvData, filename);
connectedClients[blenderId].warning = false; connectedClients[blenderId].warning = false;
} }
} else { } else {
connectedClients[blenderId] = { connectedClients[blenderId] = {
shown: true, shown: true,
ifc_file: filename, ifc_file: filename,
headers: [], headers: [],
warning: false, warning: false,
}; };
addTableElement(blenderId, csvData, filename); addTableElement(blenderId, csvData, filename);
} }
} }
function handleDefaultData(data) { function handleDefaultData(data) {
const blenderId = data["blenderId"]; const blenderId = data["blenderId"];
const isDirty = data["data"]["is_dirty"]; const isDirty = data["data"]["is_dirty"];
showWarning(blenderId, isDirty); showWarning(blenderId, isDirty);
console.log(data); console.log(data);
} }
// Function to add a new table with data and filename // Function to add a new table with data and filename
function addTableElement(blenderId, csvData, filename) { function addTableElement(blenderId, csvData, filename) {
// store headers of the csv data // store headers of the csv data
const firstLine = csvData.indexOf("\n"); const firstLine = csvData.indexOf("\n");
const csvHeaders = csvData.substring(0, firstLine).split(","); const csvHeaders = csvData.substring(0, firstLine).split(",");
const hasRows = csvData.substring(firstLine + 1).trim().length > 0; const hasRows = csvData.substring(firstLine + 1).trim().length > 0;
if (hasRows) { if (hasRows) {
connectedClients[blenderId].headers = csvHeaders; connectedClients[blenderId].headers = csvHeaders;
} else { } else {
connectedClients[blenderId].headers = ""; connectedClients[blenderId].headers = "";
} }
// console.log(connectedClients[blenderId]); // console.log(connectedClients[blenderId]);
const tableContainer = $("<div></div>") const tableContainer = $("<div></div>")
.addClass("table-container") .addClass("table-container")
.attr("id", "container-" + blenderId); .attr("id", "container-" + blenderId);
const tableTitle = $("<h3></h3>") const tableTitle = $("<h3></h3>")
.attr("id", "title-" + blenderId) .attr("id", "title-" + blenderId)
.text(filename) .text(filename)
.css("margin-bottom", "10px"); .css("margin-bottom", "10px");
const tableDiv = $("<div></div>") const tableDiv = $("<div></div>")
.addClass("csv-table") .addClass("csv-table")
.attr("id", "table-" + blenderId); .attr("id", "table-" + blenderId);
tableContainer.append(tableTitle); tableContainer.append(tableTitle);
tableContainer.append(tableDiv); tableContainer.append(tableDiv);
$("#container").append(tableContainer); $("#container").append(tableContainer);
function createHeaderMenu(definitions) { function createHeaderMenu(definitions) {
var menu = []; var menu = [];
for (let column of definitions) { for (let column of definitions) {
// Create text element for checkbox // Create text element for checkbox
if (!column.hasOwnProperty("key")) { if (!column.hasOwnProperty("key")) {
column.visible = true; column.visible = true;
} }
let checkbox = document.createElement("span"); let checkbox = document.createElement("span");
checkbox.textContent = column.visible ? "\u2611" : "\u2610"; // ☑ or ☐ checkbox.textContent = column.visible ? "\u2611" : "\u2610"; // ☑ or ☐
// Build label // Build label
let label = document.createElement("span"); let label = document.createElement("span");
let title = document.createElement("span"); let title = document.createElement("span");
title.textContent = " " + column.title; title.textContent = " " + column.title;
label.appendChild(checkbox); label.appendChild(checkbox);
label.appendChild(title); label.appendChild(title);
// Create menu item // Create menu item
menu.push({ menu.push({
label: label, label: label,
action: function (e) { action: function (e) {
// Prevent menu closing // Prevent menu closing
e.stopPropagation(); e.stopPropagation();
// Toggle current column visibility // Toggle current column visibility
column.visible = !column.visible; column.visible = !column.visible;
// console.log(column.title, column.visible); // console.log(column.title, column.visible);
if (column.visible) { if (column.visible) {
table.showColumn(column.title); table.showColumn(column.title);
checkbox.textContent = "\u2611"; // ☑ checkbox.textContent = "\u2611"; // ☑
} else { } else {
table.hideColumn(column.title); table.hideColumn(column.title);
checkbox.textContent = "\u2610"; // ☐ checkbox.textContent = "\u2610"; // ☐
} }
}, },
}); });
} }
return menu; return menu;
} }
// create right click context menu for choosing column top calculation // create right click context menu for choosing column top calculation
function createColumnContextMenu(column) { function createColumnContextMenu(column) {
const field = column.field; const field = column.field;
const calculations = ["sum", "avg", "max", "min"]; const calculations = ["sum", "avg", "max", "min"];
// label is the item label in the menu // label is the item label in the menu
// update column defination on clicking an item // update column defination on clicking an item
// specifically the topCalc and pass calc type to the topCalcFormatter function // specifically the topCalc and pass calc type to the topCalcFormatter function
// uisng the topCalcFormatterParams field // uisng the topCalcFormatterParams field
return calculations.map((calc) => ({ return calculations.map((calc) => ({
label: `Show ${ label: `Show ${
calc.charAt(0).toUpperCase() + calc.slice(1) calc.charAt(0).toUpperCase() + calc.slice(1)
} for ${field}`, } for ${field}`,
action: (e, cell) => action: (e, cell) =>
cell cell
.getColumn() .getColumn()
.updateDefinition({ topCalc: calc, topCalcFormatterParams: calc }), .updateDefinition({ topCalc: calc, topCalcFormatterParams: calc }),
})); }));
} }
function calcFormatter(cell, formatterParams, onRendered) { function calcFormatter(cell, formatterParams, onRendered) {
// formatterParams is anything in the topCalcFormatterParams field in the column definition // formatterParams is anything in the topCalcFormatterParams field in the column definition
// so we pass the calculation type to display what the calculation type is // so we pass the calculation type to display what the calculation type is
return `${formatterParams}: ${cell.getValue()}`; return `${formatterParams}: ${cell.getValue()}`;
} }
var containerHeight = $(".table-container").height(); var containerHeight = $(".table-container").height();
var h3Height = $(".table-container>h3").height(); var h3Height = $(".table-container>h3").height();
var table = new Tabulator("#table-" + blenderId, { var table = new Tabulator("#table-" + blenderId, {
placeholder: "No data to display", placeholder: "No data to display",
resizableColumnGuide: true, resizableColumnGuide: true,
index: "GlobalId", index: "GlobalId",
data: csvData, data: csvData,
importFormat: "csv", importFormat: "csv",
autoColumns: true, autoColumns: true,
selectableRows: 1, selectableRows: 1,
layout: "fitDataFill", layout: "fitDataFill",
height: containerHeight - h3Height - 20 + "px", height: containerHeight - h3Height - 20 + "px",
movableColumns: true, movableColumns: true,
// Our fields are never nested, and we use the "." character in queries // Our fields are never nested, and we use the "." character in queries
nestedFieldSeparator: false, nestedFieldSeparator: false,
// defines column defualts that are applied to all columns // defines column defualts that are applied to all columns
columnDefaults: { columnDefaults: {
visible: true, visible: true,
headerFilter: true, headerFilter: true,
topCalc: "sum", topCalc: "sum",
topCalcFormatter: calcFormatter, topCalcFormatter: calcFormatter,
topCalcFormatterParams: "sum", topCalcFormatterParams: "sum",
}, },
// defines a callback to edit the column definition generated by autoColumns // defines a callback to edit the column definition generated by autoColumns
// that cannot be set as default // that cannot be set as default
autoColumnsDefinitions: function (definitions) { autoColumnsDefinitions: function (definitions) {
headerMenu = createHeaderMenu(definitions, table); headerMenu = createHeaderMenu(definitions, table);
definitions.forEach((column) => { definitions.forEach((column) => {
column.headerMenu = headerMenu; column.headerMenu = headerMenu;
column.contextMenu = createColumnContextMenu(column); column.contextMenu = createColumnContextMenu(column);
}); });
return definitions; return definitions;
}, },
}); });
createDownloadIcon(blenderId); createDownloadIcon(blenderId);
table.on("rowSelected", function (row) { table.on("rowSelected", function (row) {
var index = row.getIndex(); // the guid of the object var index = row.getIndex(); // the guid of the object
if (!index) { if (!index) {
console.log("No Global ID found"); console.log("No Global ID found");
return; return;
} }
// table id is usually "table-BlenderId" so blender id is always // table id is usually "table-BlenderId" so blender id is always
// after the first 6 characters // after the first 6 characters
var tableId = row.getTable().element.id.substr(6); var tableId = row.getTable().element.id.substr(6);
const msg = { const msg = {
sourcePage: "csv", sourcePage: "csv",
blenderId: tableId, blenderId: tableId,
operator: { operator: {
type: "selection", type: "selection",
globalId: index, globalId: index,
}, },
}; };
socket.emit("web_operator", msg); socket.emit("web_operator", msg);
}); });
} }
function createDownloadIcon(blenderId) { function createDownloadIcon(blenderId) {
const table = Tabulator.findTable("#table-" + blenderId)[0]; const table = Tabulator.findTable("#table-" + blenderId)[0];
const tableTitle = $("#title-" + blenderId); const tableTitle = $("#title-" + blenderId);
var downloadCsv = $('<a><i class="fa-solid fa-file-csv"></i></a>') var downloadCsv = $('<a><i class="fa-solid fa-file-csv"></i></a>')
.css("margin-left", "10px") .css("margin-left", "10px")
.css("cursor", "pointer"); .css("cursor", "pointer");
tableTitle.append(downloadCsv); tableTitle.append(downloadCsv);
downloadCsv.on("click", function () { downloadCsv.on("click", function () {
table.download("csv", "data.csv"); table.download("csv", "data.csv");
}); });
} }
// Function to update table and filename // Function to update table and filename
function updateTableElement(blenderId, csvData, filename) { function updateTableElement(blenderId, csvData, filename) {
// check if the headers are the same // check if the headers are the same
// if yes, just replace the data // if yes, just replace the data
// else, replace the whole data and column definations // else, replace the whole data and column definations
const firstLine = csvData.indexOf("\n"); const firstLine = csvData.indexOf("\n");
const newHeaders = csvData.substring(0, firstLine).split(","); const newHeaders = csvData.substring(0, firstLine).split(",");
const hasRows = csvData.substring(firstLine + 1).trim().length > 0; const hasRows = csvData.substring(firstLine + 1).trim().length > 0;
const sameHeaders = compareHeaders( const sameHeaders = compareHeaders(
connectedClients[blenderId].headers, connectedClients[blenderId].headers,
newHeaders newHeaders
); );
const table = Tabulator.findTable("#table-" + blenderId)[0]; const table = Tabulator.findTable("#table-" + blenderId)[0];
if (table) { if (table) {
if (sameHeaders) { if (sameHeaders) {
table.replaceData(csvData, { importFormat: "csv" }); table.replaceData(csvData, { importFormat: "csv" });
} else { } else {
table.setData(csvData); table.setData(csvData);
if (hasRows) { if (hasRows) {
connectedClients[blenderId].headers = newHeaders; connectedClients[blenderId].headers = newHeaders;
} else { } else {
connectedClients[blenderId].headers = ""; connectedClients[blenderId].headers = "";
} }
} }
$("#title-" + blenderId).text(filename); $("#title-" + blenderId).text(filename);
createDownloadIcon(blenderId); createDownloadIcon(blenderId);
} }
} }
// Function to remove table element // Function to remove table element
function removeTableElement(blenderId) { function removeTableElement(blenderId) {
$("#container-" + blenderId).remove(); $("#container-" + blenderId).remove();
} }
function showWarning(blenderId, isDirty) { function showWarning(blenderId, isDirty) {
connectedClients[blenderId].warning = true; connectedClients[blenderId].warning = true;
} }
// Utility function to compare two csv header // Utility function to compare two csv header
function compareHeaders(headers1, headers2) { function compareHeaders(headers1, headers2) {
if ( if (
headers1.length !== headers2.length || headers1.length !== headers2.length ||
headers1.length == 0 || headers1.length == 0 ||
headers2.length == 0 headers2.length == 0
) { ) {
return false; return false;
} }
for (let i = 0; i < headers1.length; i++) { for (let i = 0; i < headers1.length; i++) {
if (headers1[i] !== headers2[i]) { if (headers1[i] !== headers2[i]) {
return false; return false;
} }
} }
return true; return true;
} }
function setTheme(theme) { function setTheme(theme) {
$("html").removeClass("light dark blender").addClass(theme); $("html").removeClass("light dark blender").addClass(theme);
$(":root").css("color-scheme", theme); $(":root").css("color-scheme", theme);
var stylesheet = $("#tabulator-stylesheet"); var stylesheet = $("#tabulator-stylesheet");
if (theme === "light") { if (theme === "light") {
stylesheet.attr( stylesheet.attr(
"href", "href",
"https://unpkg.com/tabulator-tables/dist/css/tabulator_site.min.css" "https://unpkg.com/tabulator-tables/dist/css/tabulator_site.min.css"
); );
$("#toggle-theme").html('<i class="fas fa-sun"></i>'); $("#toggle-theme").html('<i class="fas fa-sun"></i>');
} else if (theme === "dark") { } else if (theme === "dark") {
stylesheet.attr( stylesheet.attr(
"href", "href",
"https://unpkg.com/tabulator-tables/dist/css/tabulator_site_dark.min.css" "https://unpkg.com/tabulator-tables/dist/css/tabulator_site_dark.min.css"
); );
$("#toggle-theme").html('<i class="fas fa-moon"></i>'); $("#toggle-theme").html('<i class="fas fa-moon"></i>');
} else if (theme === "blender") { } else if (theme === "blender") {
$("#toggle-theme").html('<i class="fas fa-adjust"></i>'); $("#toggle-theme").html('<i class="fas fa-adjust"></i>');
} }
localStorage.setItem("theme", theme); localStorage.setItem("theme", theme);
} }
function toggleTheme() { function toggleTheme() {
if ($("html").hasClass("light")) { if ($("html").hasClass("light")) {
setTheme("dark"); setTheme("dark");
} else if ($("html").hasClass("dark")) { } else if ($("html").hasClass("dark")) {
setTheme("blender"); setTheme("blender");
} else { } else {
setTheme("light"); setTheme("light");
} }
} }
function toggleClientList() { function toggleClientList() {
var clientList = $("#client-list"); var clientList = $("#client-list");
if (clientList.hasClass("show")) { if (clientList.hasClass("show")) {
clientList.removeClass("show"); clientList.removeClass("show");
return; return;
} }
clientList.empty(); clientList.empty();
var counter = 0; var counter = 0;
$.each(connectedClients, function (id, client) { $.each(connectedClients, function (id, client) {
counter++; counter++;
const dropdownIcon = $("<i>") const dropdownIcon = $("<i>")
.addClass("fas fa-chevron-down") .addClass("fas fa-chevron-down")
.css("margin-left", "0.5rem"); .css("margin-left", "0.5rem");
const clientDiv = $("<div>") const clientDiv = $("<div>")
.addClass("client") .addClass("client")
.addClass(`client-${id}`) .addClass(`client-${id}`)
.text(client.ifc_file || "Blender " + counter); .text(client.ifc_file || "Blender " + counter);
if (client.warning) { if (client.warning) {
warningIcon = $("<i>") warningIcon = $("<i>")
.addClass("fas fa-triangle-exclamation warning") .addClass("fas fa-triangle-exclamation warning")
.css("margin-left", "0.5rem"); .css("margin-left", "0.5rem");
clientDiv.append(warningIcon); clientDiv.append(warningIcon);
} }
clientDiv.append(dropdownIcon); clientDiv.append(dropdownIcon);
const clientDetailsDiv = $("<div>").addClass("client-details"); const clientDetailsDiv = $("<div>").addClass("client-details");
if (client.warning) { if (client.warning) {
warningIcon = $("<i>").addClass("fa-solid fa-triangle-exclamation"); warningIcon = $("<i>").addClass("fa-solid fa-triangle-exclamation");
const clientWarning = $("<div>") const clientWarning = $("<div>")
.addClass("client-detail warning") .addClass("client-detail warning")
.text(" Might have outdated data due to recent changes in Blender."); .text(" Might have outdated data due to recent changes in Blender.");
clientWarning.prepend(warningIcon); clientWarning.prepend(warningIcon);
clientDetailsDiv.append(clientWarning); clientDetailsDiv.append(clientWarning);
} }
if (id) { if (id) {
const clientId = $("<div>") const clientId = $("<div>")
.addClass("client-detail") .addClass("client-detail")
.text(`Blender ID: ${id}`); .text(`Blender ID: ${id}`);
clientDetailsDiv.append(clientId); clientDetailsDiv.append(clientId);
} }
if (!client.shown) { if (!client.shown) {
const clientShown = $("<div>") const clientShown = $("<div>")
.addClass("client-detail") .addClass("client-detail")
.text("No CSV exported for this Blender yet"); .text("No CSV exported for this Blender yet");
clientDetailsDiv.append(clientShown); clientDetailsDiv.append(clientShown);
} }
if (client.headers && client.headers.length) { if (client.headers && client.headers.length) {
const clientHeaders = $("<div>") const clientHeaders = $("<div>")
.addClass("client-detail") .addClass("client-detail")
.text(`Table Headers: ${client.headers.join(", ")}`); .text(`Table Headers: ${client.headers.join(", ")}`);
const scrollButton = $("<button>") const scrollButton = $("<button>")
.addClass("scroll-button") .addClass("scroll-button")
.text("Scroll to Table") .text("Scroll to Table")
.on("click", function () { .on("click", function () {
$("html, body").animate( $("html, body").animate(
{ scrollTop: $("#table-" + id).offset().top }, { scrollTop: $("#table-" + id).offset().top },
600 600
); );
clientList.removeClass("show"); clientList.removeClass("show");
}); });
clientDetailsDiv.append(clientHeaders); clientDetailsDiv.append(clientHeaders);
clientDetailsDiv.append(scrollButton); clientDetailsDiv.append(scrollButton);
} }
clientDiv.append(clientDetailsDiv); clientDiv.append(clientDetailsDiv);
clientDiv.on("click", function () { clientDiv.on("click", function () {
clientDetailsDiv.toggleClass("show"); clientDetailsDiv.toggleClass("show");
}); });
clientList.append(clientDiv); clientList.append(clientDiv);
}); });
clientList.addClass("show"); clientList.addClass("show");
} }