const { ipcRenderer } = require('electron');
let tables_data;
let current_table;
let selected_cells = [];
let selected_cell_data;
let nav = $("nav");
let nav_scroll = nav.scrollLeft();
let detail_tab_name = $("#detail-tab-name");
let detail_table_name = $("#detail-table-name");
let detail_table_hidden = $("#detail-table-hidden");
let detail_column_name = $("#detail-column-name");
let detail_column_selectable = $("#detail-column-selectable");
let detail_column_sortable = $("#detail-column-sortable");
let detail_cell_text = $("#detail-cell-text");
function openFile(file_name) {
$.ajax({
dataType: "json",
url: file_name,
cache: false,
success: (json, textStatus, jqXHR) => {
tables_data = json;
refreshPage();
}
});
}
function saveFile(create_new_file) {
ipcRenderer.send("save", tables_data, create_new_file);
}
function clearPage() {
$("#tables").empty();
nav.empty();
$("#details input, #details textarea").val("");
}
function refreshPage() {
clearPage();
generateTables();
showTab(tables_data[0]["id"]);
}
function generateTable(table_data) {
const table_id = table_data["id"];
let a = $("")
.attr("id", table_id + "-nav")
.addClass("nav-link")
.attr("href", "#" + table_id)
.text(table_data["name"])
.on("click", () => {
showTab(table_id);
});
let add_table_btn = nav.find("a[id='add-table']");
if (add_table_btn.length !== 0) {
add_table_btn.before(a);
} else {
nav.append(a);
}
let table_div = $("
")
.attr("id", table_id + "-tab")
.addClass("tab-pane table-responsive");
let table = $("")
.attr("id", table_id)
.addClass("table table-bordered")
.append($("")
.append($("
")
.addClass("text-nowrap")))
.append("");
$.each(table_data["col-def"], (i, col) => {
const col_id = col["field"];
let header_id = table_id + "-" + col_id;
table.find("thead > tr")
.append(makeHeader(header_id, col["title"]));
})
$.each(table_data["rows"], (i, row) => {
const row_id = row["uid"];
let tr = $("
")
.attr("id", table_id + "-" + row_id);
$.each(table_data["col-def"], (j, col) => {
const col_id = col["field"];
let cell_id = table_id + "-" + col_id + "-" + row_id;
let td = makeCell(cell_id, row[col_id]);
tr.append(td);
})
table.find("tbody").append(tr);
})
table_div.append(table)
$("#tables").append(table_div);
}
function makeCell(cell_id, cell_text) {
return $(" | ")
.attr("id", cell_id)
.text(cell_text)
.on("click", () => {
selectCell(cell_id);
})
.on("contextmenu" , (event) => {
cellContextMenu(event, cell_id);
})
.on("dblclick", (event) => {
cellInlineEdit(event);
});
}
function makeHeader(header_id, col_title) {
return $("")
.attr("id", header_id)
.text(col_title)
.on("click", () => {
selectCell(header_id);
})
.on("contextmenu", (event) => {
headerContextMenu(event, header_id);
})
.on("dblclick", (event) => {
headerInlineEdit(event);
});
}
function generateTables() {
for (let table_data of tables_data) {
generateTable(table_data);
}
nav.append($("")
.attr("id", "add-table")
.addClass("nav-link btn-success")
.html("")
.on("click", () => {
addTable();
}))
}
function updateDetailsPanel() {
let data = getSelectedCellData();
detail_tab_name.val(data["tab_name"]);
detail_table_name.val(data["table_name"]);
detail_column_name.val(data["col_name"]);
detail_table_hidden.prop("checked", data["table_hidden"]);
detail_column_selectable.prop("checked", data["col_selectable"]);
detail_column_sortable.prop("checked", data["col_sortable"]);
if (data["row_id"]) {
detail_cell_text.attr("disabled", false);
detail_cell_text.val(data["cell_text"]);
} else {
detail_cell_text.attr("disabled", true);
detail_cell_text.val("");
}
}
function showTab(table_name) {
current_table = table_name;
let tab_name = table_name+"-tab";
$("#tables").children().removeClass("show active");
nav.children().removeClass("active");
$("#"+tab_name).addClass("show active");
$("#"+tab_name.replace("-tab", "-nav")).addClass("active");
selectCell($("table:visible > tbody > tr:first > td:first").attr("id"));
}
function selectCell(cell_id) {
$("td, th").removeClass("selected");
$("#"+cell_id).addClass("selected");
selected_cells = [cell_id];
updateDetailsPanel();
}
function getSelectedCellData(force_update=false) {
if (!force_update && selected_cell_data && selected_cells[0] === selected_cell_data["selected_id"]) {
return selected_cell_data;
}
let table_pattern = /(table\d+)/;
let column_pattern = /(column\d+)/;
let row_pattern = /(row\d+)/;
let table_match = table_pattern.exec(selected_cells[0]);
let column_match = column_pattern.exec(selected_cells[0]);
let row_match = row_pattern.exec(selected_cells[0]);
let cell_data = {
"selected_id": selected_cells[0],
"table_id": table_match[0],
"table_nav_id": table_match[0]+"-nav",
"col_id": table_match[0]+"-"+column_match[0],
}
$.each(tables_data, (i, table_data) => {
if (table_data["id"] === table_match[0]) {
cell_data["table_index"] = i;
cell_data["table_name"] = table_data["name"];
cell_data["tab_name"] = table_data["tab_name"];
cell_data["table_hidden"] = table_data["hidden"];
$.each(table_data["col-def"], (j, col) => {
if (col["field"] === column_match[0]) {
cell_data["col_index"] = j;
cell_data["col_name"] = col["title"];
cell_data["col_selectable"] = col["selectable"];
cell_data["col_sortable"] = col["sortable"];
return false;
}
})
if (row_match !== null) {
cell_data["row_id"] = table_match[0]+"-"+row_match[0];
cell_data["cell_id"] = table_match[0]+"-"+column_match[0]+"-"+row_match[0];
$.each(table_data["rows"], (j, row) => {
if (row["uid"] === row_match[0]) {
cell_data["row_index"] = j;
cell_data["cell_text"] = row[column_match[0]];
cell_data["row_key"] = column_match[0];
return false;
}
})
}
return false;
}
})
selected_cell_data = cell_data;
return selected_cell_data;
}
function addTable() {
let table_ids = getTableIDs();
let table_num = 0;
for (let id of table_ids) {
if (table_ids.includes(`table${table_num}`)) {
table_num++;
} else {break;}
}
let table_id = `table${table_num}`;
let table_data = {
"id": table_id,
"name": "New Table",
"filter": false,
"hidden": false,
"tab_name": "",
"description": "",
"col-def": [
{
"field": "column0",
"title": "Column 1",
"sortable": true,
"selectable": false
}
],
"rows": [
{
"uid": "row0",
"column0": "",
}
]
}
tables_data.push(table_data);
generateTable(table_data);
}
function insertColumn(table_id, index) {
if (index < 0){return;}
$.each(tables_data, (i, table_data) => {
if (table_data["id"] === table_id) {
let col_ids = getColumnIDs(table_data["col-def"]);
let col_num = 0;
for (let id of col_ids) {
if (col_ids.includes(`column${col_num}`)) {
col_num++;
} else {break;}
}
let col_id = `column${col_num}`
table_data["col-def"].splice(index, 0,{
"field": col_id,
"title": "New Column",
"sortable": false,
"selectable": false,
})
let num_cols = $(`#${table_id} > thead > tr`).children().length;
if (index >= num_cols) {
index = num_cols-1;
$.fn.insert = function (data) {
this.after(data);
};
} else {
$.fn.insert = function (data) {
this.before(data);
};
}
let header_id = table_id + "-" + col_id;
$(`#${table_id} > thead > tr > th:nth-child(${index + 1})`)
.insert(makeHeader(header_id, "New Column"));
$(`#${table_id} > tbody > tr`).each((j, tr) => {
tr = $(tr);
let cell_id = table_id+"-"+col_id+"-"+table_data["rows"][j]["uid"];
tr.children(`td:nth-child(${index + 1})`)
.insert(makeCell(cell_id, ""))
});
getSelectedCellData(true);
return false;
}
})
}
function insertRow(table_id, index) {
if (index < 0){return;}
$.each(tables_data, (i, table_data) => {
if (table_data["id"] === table_id) {
let row_ids = getRowIDs(table_data["rows"]);
let row_num = 0;
for (let id of row_ids) {
if (row_ids.includes(`row${row_num}`)) {
row_num++;
} else {break;}
}
let row_id = `row${row_num}`
let col_ids = getColumnIDs(table_data["col-def"]);
let row_data = {"uid": row_id};
for (let id of col_ids) {
row_data[id] = "";
}
table_data["rows"].splice(index, 0, row_data);
let num_rows = $(`#${table_id} > tbody > tr`).length;
if (index >= num_rows) {
index = num_rows-1;
$.fn.insert = function (data) {
this.after(data);
};
} else {
$.fn.insert = function (data) {
this.before(data);
};
}
let tr = $(" |
")
.attr("id", table_id+"-"+row_id);
for (let col_id of col_ids) {
let cell_id = table_id+"-"+col_id+"-"+row_id;
tr.append(makeCell(cell_id, ""))
}
$(`#${table_id} > tbody > tr:nth-child(${index+1})`)
.insert(tr);
getSelectedCellData(true);
return false;
}
})
}
function cellContextMenu(event, cell_id) {
selectCell(cell_id);
let menu = $("")
.attr("id", "context-menu-"+cell_id)
.addClass("border context-menu text-nowrap")
.on("mouseleave", () => {
menu.remove();
})
.append($("")
.addClass("btn d-block")
.on("click", () => {
let data = getSelectedCellData();
insertRow(data["table_id"], data["row_index"]);
})
.text("Insert row above"))
.append($("")
.addClass("btn d-block")
.on("click", () => {
let data = getSelectedCellData();
insertRow(data["table_id"], data["row_index"]+1);
})
.text("Insert row below"))
.append($("")
.addClass("btn d-block")
.on("click", () => {
let data = getSelectedCellData();
insertColumn(data["table_id"], data["col_index"]);
})
.text("Insert column left"))
.append($("")
.addClass("btn d-block")
.on("click", () => {
let data = getSelectedCellData();
insertColumn(data["table_id"], data["col_index"]+1);
})
.text("Insert column right"))
let max_height = $(document).height();
let max_width = $(document).width();
let x_pos = event.pageX-5;
let y_pos = event.pageY-5;
$("body").append(menu);
y_pos = Math.min(y_pos, max_height-menu.height());
x_pos = Math.min(x_pos, max_width-menu.width());
menu.css({"top": y_pos, "left": x_pos});
}
function headerContextMenu(event, header_id) {
if (!selected_cells.includes(header_id)) {
selectCell(header_id);
}
let menu = $("")
.attr("id", "context-menu-"+header_id)
.addClass("border context-menu text-nowrap")
.on("mouseleave", () => {
menu.remove();
})
.append($("")
.addClass("btn d-block")
.on("click", () => {
let data = getSelectedCellData();
insertColumn(data["table_id"], data["col_index"]);
})
.text("Insert column left"))
.append($("")
.addClass("btn d-block")
.on("click", () => {
let data = getSelectedCellData();
insertColumn(data["table_id"], data["col_index"]+1);
})
.text("Insert column right"))
let max_height = $(document).height();
let max_width = $(document).width();
let x_pos = event.pageX-5;
let y_pos = event.pageY-5;
$("body").append(menu);
y_pos = Math.min(y_pos, max_height-menu.height());
x_pos = Math.min(x_pos, max_width-menu.width());
menu.css({"top": y_pos, "left": x_pos});
}
function cellInlineEdit(event) {
let data = getSelectedCellData()
let target = $(event.target);
let text_area = $("