/** * Main application JavaScript for the Power BI Measure Call Graph */ // DOM elements const serverInput = document.getElementById('server'); const databaseInput = document.getElementById('database'); const connectBtn = document.getElementById('connect-btn'); const connectionStatus = document.getElementById('connection-status'); const resetBtn = document.getElementById('reset-btn'); const resetDetailBtn = document.getElementById('reset-detail-btn'); const searchInput = document.getElementById('search-input'); const searchResults = document.getElementById('search-results'); const measureName = document.getElementById('measure-name'); const measureExpression = document.getElementById('measure-expression'); // Graph instance let graph = null; let searchTimeout = null; // Event listeners document.addEventListener('DOMContentLoaded', () => { // Initialize the graph graph = new Graph('#graph-container'); // Connect button connectBtn.addEventListener('click', connectToModel); // Reset button for main graph resetBtn.addEventListener('click', () => { if (graph) { graph.resetView(); } }); // Reset button for detail graph resetDetailBtn.addEventListener('click', () => { if (graph) { graph.resetDetailView(); } }); // Search input searchInput.addEventListener('input', handleSearch); // 点击其他地方时关闭搜索结果 document.addEventListener('click', (e) => { if (!searchInput.contains(e.target) && !searchResults.contains(e.target)) { searchResults.classList.remove('active'); } }); }); /** * Connect to the Analysis Services model */ async function connectToModel() { const server = serverInput.value.trim() || 'localhost'; const database = databaseInput.value.trim() || 'null'; if (!databaseInput.value.trim()) { showConnectionStatus('No database name provided, using null', 'warning'); } try { showConnectionStatus('Connecting...', ''); // Connect to the model const connectResponse = await fetch('/connect', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ server, database }) }); const connectData = await connectResponse.json(); if (!connectData.success) { showConnectionStatus(`Connection failed: ${connectData.message}`, 'error'); return; } showConnectionStatus('Connected successfully. Loading measures...', 'success'); // Get the call graph data const graphResponse = await fetch('/get_call_graph'); const graphData = await graphResponse.json(); if (!graphData.success) { showConnectionStatus(`Failed to load graph: ${graphData.message}`, 'error'); return; } // Initialize the graph with the data graph.setData(graphData.graph); graph.render(); showConnectionStatus('Graph loaded successfully', 'success'); } catch (error) { showConnectionStatus(`Error: ${error.message}`, 'error'); } } /** * Show connection status message * @param {string} message - The message to display * @param {string} type - The message type (success, error, or empty for neutral) */ function showConnectionStatus(message, type) { connectionStatus.textContent = message; connectionStatus.className = type; } /** * Display measure details * @param {Object} measure - The measure object */ function showMeasureDetails(measure) { if (!measure) { measureName.textContent = ''; measureExpression.textContent = ''; return; } measureName.textContent = measure.id; measureExpression.textContent = measure.expression || 'No expression available'; } // 处理搜索输入 function handleSearch(e) { const query = e.target.value.trim(); // 清除之前的定时器 if (searchTimeout) { clearTimeout(searchTimeout); } // 设置新的定时器,避免频繁搜索 searchTimeout = setTimeout(() => { if (graph) { const results = graph.searchNodes(query); updateSearchResults(results); } }, 300); } // 更新搜索结果下拉菜单 function updateSearchResults(results) { searchResults.innerHTML = ''; if (!results || results.length === 0) { searchResults.classList.remove('active'); return; } results.forEach((node, index) => { const div = document.createElement('div'); div.className = 'search-result-item'; div.textContent = node.id; // 添加点击事件 div.addEventListener('click', () => { searchInput.value = node.id; searchResults.classList.remove('active'); graph.selectNode(node); graph.centerViewOnNode(node); }); // 添加键盘导航 div.addEventListener('keydown', (e) => { if (e.key === 'Enter') { div.click(); } }); searchResults.appendChild(div); }); searchResults.classList.add('active'); } // Export functions for use in graph.js window.appFunctions = { showMeasureDetails };