You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
681 lines
27 KiB
681 lines
27 KiB
// tabs.js
|
|
|
|
let tabCount = 0;
|
|
let activeTabId = null;
|
|
|
|
let tabNodes = {}; // Almacenar los nodos por pestaña
|
|
let tabConnections = {}; // Almacenar las conexiones por pestaña
|
|
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
loadExistingTabs();
|
|
|
|
document.getElementById("createTabButton").addEventListener("click", () => {
|
|
const tabName = prompt('Ingrese el nombre para la nueva pestaña:', `Pestaña ${tabCount + 1}`);
|
|
if (tabName) {
|
|
createNewTab(tabName);
|
|
}
|
|
});
|
|
});
|
|
|
|
// Función para obtener el CSRF token
|
|
function getCSRFToken() {
|
|
const name = 'csrftoken';
|
|
let cookieValue = null;
|
|
if (document.cookie && document.cookie !== '') {
|
|
const cookies = document.cookie.split(';');
|
|
for (let i = 0; i < cookies.length; i++) {
|
|
const cookie = cookies[i].trim();
|
|
if (cookie.substring(0, name.length + 1) === (name + '=')) {
|
|
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return cookieValue;
|
|
}
|
|
|
|
// Función para crear el menú contextual
|
|
function createContextMenu(x, y, options) {
|
|
// Eliminar cualquier menú existente
|
|
removeContextMenu();
|
|
|
|
const menu = document.createElement('div');
|
|
menu.classList.add('context-menu');
|
|
menu.style.top = `${y}px`;
|
|
menu.style.left = `${x}px`;
|
|
|
|
options.forEach(option => {
|
|
const item = document.createElement('div');
|
|
item.classList.add('context-menu-item');
|
|
item.textContent = option.label;
|
|
item.addEventListener('click', option.action);
|
|
menu.appendChild(item);
|
|
});
|
|
|
|
document.body.appendChild(menu);
|
|
menu.classList.add('show');
|
|
|
|
// Cerrar el menú al hacer clic en cualquier parte
|
|
document.addEventListener('click', removeContextMenu);
|
|
}
|
|
|
|
// Función para eliminar el menú contextual
|
|
function removeContextMenu() {
|
|
const existingMenu = document.querySelector('.context-menu');
|
|
if (existingMenu) {
|
|
existingMenu.remove();
|
|
document.removeEventListener('click', removeContextMenu);
|
|
}
|
|
}
|
|
|
|
async function loadExistingTabs() {
|
|
try {
|
|
const response = await fetch('/list-tabs/');
|
|
if (response.ok) {
|
|
const data = await response.json();
|
|
let maxTabId = 0;
|
|
for (const tab of data.tabs) {
|
|
await createNewTab(tab.name, tab.id);
|
|
maxTabId = Math.max(maxTabId, parseInt(tab.id));
|
|
}
|
|
tabCount = maxTabId; // Establecer tabCount al ID máximo existente
|
|
} else {
|
|
console.error('Error al cargar las pestañas existentes');
|
|
}
|
|
} catch (error) {
|
|
console.error('Error de red al cargar las pestañas existentes:', error);
|
|
}
|
|
}
|
|
|
|
async function createNewTab(tabName, existingTabId = null) {
|
|
let tabId;
|
|
if (existingTabId) {
|
|
// Usar el ID existente sin incrementar tabCount
|
|
tabId = existingTabId;
|
|
} else {
|
|
// Incrementar tabCount y asignarlo como nuevo ID
|
|
tabCount++;
|
|
tabId = tabCount;
|
|
}
|
|
|
|
const tabList = document.getElementById("tabsList");
|
|
const tabContent = document.getElementById("tabContent");
|
|
|
|
const newTab = document.createElement("li");
|
|
newTab.textContent = tabName;
|
|
newTab.id = `tab${tabId}`;
|
|
newTab.classList.add("tab");
|
|
newTab.setAttribute('data-tab-name', tabName); // Guardar el nombre de la pestaña
|
|
newTab.addEventListener("click", () => activateTab(newTab));
|
|
newTab.addEventListener("dblclick", () => renameTab(newTab)); // Permitir renombrar
|
|
|
|
// Agregar evento de clic derecho para mostrar el menú contextual
|
|
newTab.addEventListener('contextmenu', (e) => {
|
|
e.preventDefault();
|
|
createContextMenu(e.pageX, e.pageY, [
|
|
{
|
|
label: 'Eliminar Pestaña',
|
|
action: () => deleteTab(newTab)
|
|
}
|
|
]);
|
|
});
|
|
|
|
const newContent = document.createElement("div");
|
|
newContent.id = `content${tabId}`;
|
|
newContent.classList.add("tabContent");
|
|
newContent.style.display = "none";
|
|
|
|
// Crear el sidebar y el canvas dentro de la pestaña
|
|
newContent.innerHTML = `
|
|
<div class="tabNodes" id="tabNodes${tabId}">
|
|
<div class="container_canvas">
|
|
<canvas id="canvas${tabId}" width="800" height="600"></canvas>
|
|
<div class="content" id="nodeContainer${tabId}">
|
|
<!-- Aquí se agregarán los nodos -->
|
|
</div>
|
|
</div>
|
|
<div class="right_sidebar">
|
|
<div class="node" data-node-type="node1">DID o Extensión</div>
|
|
<div class="node" data-node-type="node2">Audio</div>
|
|
<div class="node" data-node-type="node3">Prompt y Condiciones</div>
|
|
<div class="node" data-node-type="node4">Transferencia llamada</div>
|
|
<button class="btnGuardar" onclick="saveTabData(${tabId})">Guardar</button>
|
|
</div>
|
|
</div>`;
|
|
|
|
tabList.appendChild(newTab);
|
|
tabContent.appendChild(newContent);
|
|
|
|
tabNodes[tabId] = [];
|
|
tabConnections[tabId] = [];
|
|
|
|
const rightSidebar = newContent.querySelector('.right_sidebar');
|
|
const nodeElements = rightSidebar.querySelectorAll('.node');
|
|
|
|
nodeElements.forEach(nodeEl => {
|
|
nodeEl.addEventListener("click", (e) => {
|
|
createNode(e, tabId);
|
|
});
|
|
});
|
|
|
|
// Activar la pestaña recién creada
|
|
activateTab(newTab);
|
|
|
|
// Esperar a que el DOM se actualice
|
|
await new Promise(resolve => requestAnimationFrame(resolve));
|
|
|
|
initializeCanvas(tabId);
|
|
|
|
// Solo llamar loadTabData si es una pestaña existente (ya guardada en el servidor)
|
|
if (existingTabId) {
|
|
await loadTabData(tabId);
|
|
}
|
|
}
|
|
|
|
function activateTab(selectedTab) {
|
|
const tabs = document.querySelectorAll("#tabsList li.tab");
|
|
const contents = document.querySelectorAll(".tabContent");
|
|
|
|
tabs.forEach(tab => {
|
|
tab.classList.remove("active");
|
|
});
|
|
|
|
contents.forEach(content => {
|
|
content.style.display = "none";
|
|
});
|
|
|
|
selectedTab.classList.add("active");
|
|
document.getElementById(`content${selectedTab.id.slice(3)}`).style.display = "block";
|
|
|
|
activeTabId = selectedTab.id.slice(3);
|
|
|
|
// Esperamos a que el DOM se actualice antes de inicializar el canvas y actualizar conexiones
|
|
requestAnimationFrame(() => {
|
|
initializeCanvas(activeTabId);
|
|
updateConnectionPoints(activeTabId);
|
|
updateConnections(activeTabId);
|
|
});
|
|
}
|
|
|
|
async function renameTab(tabElement) {
|
|
const oldName = tabElement.getAttribute('data-tab-name');
|
|
const newName = prompt('Ingrese el nuevo nombre para la pestaña:', oldName);
|
|
if (newName && newName !== oldName) {
|
|
const tabId = tabElement.id.slice(3); // Asumiendo que el ID es 'tab<id>'
|
|
try {
|
|
const response = await fetch('/rename_tab/', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'X-CSRFToken': getCSRFToken(),
|
|
},
|
|
body: JSON.stringify({
|
|
tabId: tabId,
|
|
newTabName: newName
|
|
})
|
|
});
|
|
|
|
const result = await response.json();
|
|
|
|
if (result.status === 'success') {
|
|
tabElement.textContent = newName;
|
|
tabElement.setAttribute('data-tab-name', newName);
|
|
alert("Pestaña renombrada exitosamente.");
|
|
} else {
|
|
alert(`Error al renombrar la pestaña: ${result.message}`);
|
|
}
|
|
} catch (error) {
|
|
console.error('Error al renombrar la pestaña:', error);
|
|
alert('Ocurrió un error al renombrar la pestaña.');
|
|
}
|
|
}
|
|
}
|
|
|
|
// Función para eliminar una pestaña
|
|
async function deleteTab(tabElement) {
|
|
const confirmDelete = confirm("¿Estás seguro de que deseas eliminar esta pestaña y todos sus contenidos?");
|
|
if (!confirmDelete) return;
|
|
|
|
const tabId = tabElement.id.slice(3); // Asumiendo que el ID es 'tab<id>'
|
|
const tabName = tabElement.getAttribute('data-tab-name');
|
|
|
|
try {
|
|
const response = await fetch('/delete_tab/', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'X-CSRFToken': getCSRFToken(),
|
|
},
|
|
body: JSON.stringify({
|
|
tabId: tabId,
|
|
tabName: tabName
|
|
})
|
|
});
|
|
|
|
const result = await response.json();
|
|
|
|
if (result.status === 'success') {
|
|
// Eliminar la pestaña y su contenido del DOM
|
|
const contentElement = document.getElementById(`content${tabId}`);
|
|
tabElement.remove();
|
|
contentElement.remove();
|
|
|
|
// Eliminar datos de conexiones y nodos almacenados en JS
|
|
delete window.tabConnections[tabId];
|
|
delete tabNodes[tabId];
|
|
|
|
alert("Pestaña eliminada exitosamente.");
|
|
} else {
|
|
alert(`Error: ${result.message}`);
|
|
}
|
|
} catch (error) {
|
|
console.error('Error al eliminar la pestaña:', error);
|
|
alert('Ocurrió un error al eliminar la pestaña.');
|
|
}
|
|
}
|
|
|
|
async function saveTabData(tabId) {
|
|
const tabElement = document.getElementById(`tab${tabId}`);
|
|
const tabName = tabElement.getAttribute('data-tab-name') || `Pestaña ${tabId}`;
|
|
|
|
// Recopilar datos de los nodos
|
|
const nodes = Array.from(document.querySelectorAll(`#nodeContainer${tabId} .slice_node`)).map(node => {
|
|
const nodeType = node.getAttribute('data-node-type');
|
|
if (nodeType === 'node1') {
|
|
return {
|
|
id: node.id,
|
|
title: node.querySelector('.node-title').textContent,
|
|
extensionNumber: node.extensionNumber || "",
|
|
position: {
|
|
top: node.style.top,
|
|
left: node.style.left
|
|
},
|
|
nodeType: nodeType,
|
|
};
|
|
} else if (nodeType === 'node2') {
|
|
return {
|
|
id: node.id,
|
|
title: node.querySelector('.node-title').textContent,
|
|
audioFile: node.audioFile || "",
|
|
position: {
|
|
top: node.style.top,
|
|
left: node.style.left
|
|
},
|
|
nodeType: nodeType,
|
|
};
|
|
} else if (nodeType === 'node3') {
|
|
return {
|
|
id: node.id,
|
|
title: node.querySelector('.node-title').textContent,
|
|
content: node.content || "",
|
|
position: {
|
|
top: node.style.top,
|
|
left: node.style.left
|
|
},
|
|
nodeType: nodeType,
|
|
textFile: node.textFile || "",
|
|
conditions: node.conditions || [] // Incluir condiciones dinámicas
|
|
};
|
|
} else if (nodeType === 'node4') {
|
|
return {
|
|
id: node.id,
|
|
title: node.querySelector('.node-title').textContent,
|
|
position: {
|
|
top: node.style.top,
|
|
left: node.style.left
|
|
},
|
|
nodeType: nodeType,
|
|
extensionNumber: node.extensionNumber || "" // Incluido extensionNumber
|
|
};
|
|
} else {
|
|
return {
|
|
id: node.id,
|
|
title: node.querySelector('.node-title').textContent,
|
|
extensionNumber: node.extensionNumber || "",
|
|
content: node.content || "",
|
|
position: {
|
|
top: node.style.top,
|
|
left: node.style.left
|
|
},
|
|
nodeType: nodeType,
|
|
audioFiles: node.audioFile ? [node.audioFile] : [],
|
|
textFile: node.textFile ? extractFileName(node.textFile) : ""
|
|
};
|
|
}
|
|
});
|
|
|
|
// Recolectar conexiones
|
|
const connections = window.tabConnections[tabId].map(conn => {
|
|
return {
|
|
startNodeId: conn.startPoint.from.id,
|
|
startPortClass: conn.startPoint.portClass,
|
|
endNodeId: conn.endPoint.to.id,
|
|
endPortClass: conn.endPoint.portClass,
|
|
isActive: conn.isActive || false // Incluir isActive en las conexiones
|
|
};
|
|
});
|
|
|
|
const data = {
|
|
tabId,
|
|
tabName,
|
|
nodes,
|
|
connections
|
|
};
|
|
|
|
try {
|
|
// Primero, enviar cada nodo3 a save_tts para generar los archivos de texto
|
|
for (let node of nodes) {
|
|
// Solo enviar nodos3 que tengan contenido y no hayan sido guardados previamente
|
|
if (node.nodeType === 'node3' && node.content && node.content.trim() !== "" && (!node.textFile)) {
|
|
const response = await fetch('/save_tts/', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'X-CSRFToken': getCSRFToken(),
|
|
},
|
|
body: JSON.stringify({
|
|
tabId: tabId,
|
|
nodeId: node.id, // Incluir nodeId
|
|
nodeType: node.nodeType,
|
|
title: node.title,
|
|
content: node.content
|
|
})
|
|
});
|
|
|
|
const result = await response.json();
|
|
|
|
if (result.status === 'success') {
|
|
// Actualizar el nodo con las URLs de los archivos
|
|
node.textFile = result.textFile;
|
|
} else if (result.status !== 'skipped') {
|
|
console.error(`Error al generar archivos para el nodo ${node.id}: ${result.message}`);
|
|
alert(`Error al generar archivos para el nodo "${node.title}": ${result.message}`);
|
|
// Continuar con otros nodos en lugar de detenerse
|
|
}
|
|
}
|
|
}
|
|
|
|
// Luego, enviar los datos completos de la pestaña a save_tab_data
|
|
const response = await fetch('/save-tab-data/', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'X-CSRFToken': getCSRFToken(),
|
|
},
|
|
body: JSON.stringify(data)
|
|
});
|
|
|
|
if (response.ok) {
|
|
const result = await response.json();
|
|
alert(result.message);
|
|
} else {
|
|
const errorData = await response.json();
|
|
console.error('Error al guardar los datos:', errorData.error);
|
|
alert(`Error al guardar los datos: ${errorData.error}`);
|
|
}
|
|
} catch (error) {
|
|
console.error('Error de red o servidor:', error);
|
|
alert('Error de red o servidor. Por favor, intenta nuevamente.');
|
|
}
|
|
}
|
|
|
|
async function loadTabData(tabId, tabName = null) {
|
|
try {
|
|
const response = await fetch(`/load-tab-data/${tabId}/`);
|
|
if (response.ok) {
|
|
const data = await response.json();
|
|
|
|
const nodesData = Array.isArray(data.nodes) ? data.nodes : [];
|
|
const connectionsData = Array.isArray(data.connections) ? data.connections : [];
|
|
|
|
// Utilizar el tabName proporcionado si data.tabName no está disponible
|
|
const effectiveTabName = data.tabName || tabName || `Pestaña ${tabId}`;
|
|
|
|
// Actualizar el nombre de la pestaña
|
|
const tabElement = document.getElementById(`tab${tabId}`);
|
|
if (tabElement) {
|
|
tabElement.textContent = effectiveTabName;
|
|
tabElement.setAttribute('data-tab-name', effectiveTabName);
|
|
}
|
|
|
|
const contentElement = document.getElementById(`nodeContainer${tabId}`);
|
|
if (contentElement) {
|
|
contentElement.innerHTML = '';
|
|
}
|
|
|
|
const nodeMap = {};
|
|
|
|
nodesData.forEach(nodeData => {
|
|
const node = document.createElement('div');
|
|
node.id = nodeData.id || `node${Date.now()}`;
|
|
node.className = 'slice_node';
|
|
node.style.position = 'absolute';
|
|
node.style.top = nodeData.position.top;
|
|
node.style.left = nodeData.position.left;
|
|
node.setAttribute('data-node-type', nodeData.nodeType);
|
|
|
|
// Asignar las propiedades según el tipo de nodo
|
|
if (nodeData.nodeType === 'node1') {
|
|
node.extensionNumber = nodeData.extensionNumber || "";
|
|
} else if (nodeData.nodeType === 'node3') {
|
|
node.textFile = nodeData.textFile || "";
|
|
node.content = nodeData.content || "";
|
|
node.conditions = nodeData.conditions || []; // Cargar condiciones
|
|
} else if (nodeData.nodeType === 'node4') {
|
|
node.extensionNumber = nodeData.extensionNumber || "";
|
|
} else {
|
|
node.audioFile = nodeData.audioFile || "";
|
|
node.textFile = nodeData.textFile || "";
|
|
node.content = nodeData.content || "";
|
|
}
|
|
|
|
// Crear elemento para el título
|
|
const titleElement = document.createElement('div');
|
|
titleElement.className = 'node-title';
|
|
titleElement.textContent = nodeData.title;
|
|
node.appendChild(titleElement);
|
|
|
|
// Crear elemento para el nombre del archivo .txt (solo para node3)
|
|
if (nodeData.nodeType === 'node3') {
|
|
const txtFileElement = document.createElement('div');
|
|
txtFileElement.className = 'txt-file';
|
|
txtFileElement.style = 'display: none;'
|
|
txtFileElement.textContent = nodeData.textFile ? extractFileName(nodeData.textFile) : "No guardado";
|
|
node.appendChild(txtFileElement);
|
|
}
|
|
|
|
// Crear elemento para el ícono de reproducción (Node2 y Node3)
|
|
if (nodeData.nodeType === "node2") { // Solo para node2
|
|
const playIcon = document.createElement('span');
|
|
playIcon.className = 'play-icon';
|
|
playIcon.innerHTML = '▶'; // Unicode para el triángulo de reproducción
|
|
playIcon.title = "Reproducir Audio";
|
|
playIcon.style.cursor = 'pointer';
|
|
playIcon.style.marginLeft = '10px';
|
|
playIcon.style.color = 'white';
|
|
playIcon.style.fontSize = '16px';
|
|
playIcon.style.verticalAlign = 'middle';
|
|
playIcon.onclick = function() {
|
|
if (node.audioFile) {
|
|
let audio = new Audio(node.audioFile);
|
|
audio.play();
|
|
} else {
|
|
alert("No hay audio asociado a este nodo.");
|
|
}
|
|
};
|
|
node.appendChild(playIcon);
|
|
}
|
|
|
|
// Crear elemento para node4
|
|
if (nodeData.nodeType === 'node4') {
|
|
const displayElement = document.createElement('div');
|
|
displayElement.className = 'display-extension';
|
|
displayElement.textContent = nodeData.extensionNumber || "No recibido"; // Mostrar el número de extensión
|
|
displayElement.style = "display: none;";
|
|
node.appendChild(displayElement);
|
|
}
|
|
|
|
// Crear y agregar puertos según el tipo de nodo
|
|
if (nodeData.nodeType === "node1") {
|
|
const portOut = document.createElement('div');
|
|
portOut.className = "port port-out";
|
|
node.appendChild(portOut);
|
|
node.style.background = "#92a5b9";
|
|
} else if (nodeData.nodeType === "node2") {
|
|
const portIn = document.createElement('div');
|
|
portIn.className = "port port-in";
|
|
node.appendChild(portIn);
|
|
const portOut = document.createElement('div');
|
|
portOut.className = "port port-out";
|
|
node.appendChild(portOut);
|
|
node.style.background = "#00BFA5";
|
|
} else if (nodeData.nodeType === "node3") {
|
|
const portIn1 = document.createElement("div");
|
|
portIn1.className = "port port-in";
|
|
node.appendChild(portIn1);
|
|
const portIn2 = document.createElement("div");
|
|
portIn2.className = "port port-in";
|
|
node.appendChild(portIn2);
|
|
// Añadir puertos de salida para node3
|
|
const portOut1 = document.createElement("div");
|
|
portOut1.className = "port port-out";
|
|
node.appendChild(portOut1);
|
|
const portOut2 = document.createElement("div");
|
|
portOut2.className = "port port-out";
|
|
node.appendChild(portOut2);
|
|
node.style.background = "#DDDDDD";
|
|
} else if (nodeData.nodeType === "node4") {
|
|
const portIn1 = document.createElement("div");
|
|
portIn1.className = "port port-in";
|
|
node.appendChild(portIn1);
|
|
const portIn2 = document.createElement("div");
|
|
portIn2.className = "port port-in";
|
|
node.appendChild(portIn2);
|
|
node.style.background = "#1DE9B6"; // Color actualizado
|
|
}
|
|
|
|
node.addEventListener('mousedown', (event) => {
|
|
if (!event.target.classList.contains('port') && !event.target.classList.contains('play-icon')) {
|
|
mouseDown(event, node, tabId);
|
|
}
|
|
});
|
|
|
|
// Agregar evento de doble clic para abrir el modal
|
|
node.addEventListener('dblclick', (e) => openModal(e, node));
|
|
|
|
// Agregar eventos a los puertos
|
|
const ports = node.querySelectorAll('.port');
|
|
ports.forEach(port => {
|
|
port.addEventListener('mousedown', (e) => {
|
|
startConnection(e, tabId);
|
|
});
|
|
});
|
|
|
|
// Añadir evento de clic derecho para mostrar el menú contextual
|
|
node.addEventListener('contextmenu', (e) => {
|
|
e.preventDefault();
|
|
createContextMenu(e.pageX, e.pageY, [
|
|
{
|
|
label: 'Eliminar Nodo',
|
|
action: () => deleteNode(node, tabId)
|
|
}
|
|
]);
|
|
});
|
|
|
|
if (contentElement) {
|
|
contentElement.appendChild(node);
|
|
}
|
|
nodeMap[node.id] = node;
|
|
});
|
|
|
|
window.tabConnections[tabId] = [];
|
|
|
|
// Esperamos a que el DOM se actualice antes de calcular las posiciones
|
|
await new Promise(resolve => requestAnimationFrame(resolve));
|
|
|
|
connectionsData.forEach(connData => {
|
|
const startNode = nodeMap[connData.startNodeId];
|
|
const endNode = nodeMap[connData.endNodeId];
|
|
|
|
if (!startNode || !endNode) {
|
|
console.warn('No se encontró el nodo para la conexión:', connData);
|
|
return;
|
|
}
|
|
|
|
const startPort = startNode.querySelector(connData.startPortClass === 'out' ? '.port-out' : '.port-in');
|
|
const endPort = endNode.querySelector(connData.endPortClass === 'in' ? '.port-in' : '.port-out');
|
|
|
|
if (!startPort || !endPort) {
|
|
console.warn('No se encontró el puerto para la conexión:', connData);
|
|
return;
|
|
}
|
|
|
|
const canvasRect = document.getElementById(`canvas${tabId}`).getBoundingClientRect();
|
|
const startPortRect = startPort.getBoundingClientRect();
|
|
const endPortRect = endPort.getBoundingClientRect();
|
|
|
|
const startPoint = {
|
|
x: startPortRect.left + startPort.clientWidth / 2 - canvasRect.left,
|
|
y: startPortRect.top + startPort.clientHeight / 2 - canvasRect.top,
|
|
from: startNode,
|
|
portClass: connData.startPortClass,
|
|
};
|
|
|
|
const endPoint = {
|
|
x: endPortRect.left + endPort.clientWidth / 2 - canvasRect.left,
|
|
y: endPortRect.top + endPort.clientHeight / 2 - canvasRect.top,
|
|
to: endNode,
|
|
portClass: connData.endPortClass,
|
|
};
|
|
|
|
window.tabConnections[tabId].push({ startPoint, endPoint, isActive: connData.isActive });
|
|
});
|
|
|
|
updateConnectionPoints(tabId);
|
|
updateConnections(tabId);
|
|
|
|
// Actualizar los números de extensión para node4 basado en las conexiones activas
|
|
nodesData.forEach(nodeData => {
|
|
if (nodeData.nodeType === 'node4') {
|
|
window.tabConnections[tabId].forEach(conn => {
|
|
if (conn.endPoint.to.id === nodeData.id && nodeData.nodeType === 'node4' && conn.isActive) {
|
|
const startNode = conn.startPoint.from;
|
|
const startNodeType = startNode.getAttribute('data-node-type');
|
|
if (startNodeType === 'node3') {
|
|
const node3 = startNode;
|
|
nodeData.extensionNumber = node3.content || "";
|
|
const displayElement = nodeMap[nodeData.id]?.querySelector('.display-extension');
|
|
if (displayElement) {
|
|
displayElement.textContent = nodeData.extensionNumber;
|
|
}
|
|
}
|
|
}
|
|
});
|
|
}
|
|
});
|
|
} else {
|
|
console.error('Error al cargar los datos de la pestaña:', response);
|
|
}
|
|
} catch (error) {
|
|
console.error('Error al cargar los datos de la pestaña:', error);
|
|
}
|
|
}
|
|
|
|
// Atajo para guardar con Ctrl+S
|
|
document.addEventListener('keydown', function(event) {
|
|
if (event.ctrlKey && event.key === "s") {
|
|
event.preventDefault();
|
|
if (activeTabId !== null) {
|
|
saveTabData(activeTabId);
|
|
} else {
|
|
alert("No hay pestaña activa");
|
|
}
|
|
}
|
|
});
|
|
|
|
// Exportar funciones necesarias para conexiones.js
|
|
window.initializeCanvas = initializeCanvas;
|
|
window.updateConnectionPoints = updateConnectionPoints;
|
|
window.updateConnections = updateConnections;
|
|
window.startConnection = startConnection;
|
|
window.deleteTab = deleteTab; // Exportar deleteTab para que pueda ser llamado desde el menú contextual
|