1// Table functionality 2document.addEventListener('DOMContentLoaded', function() { 3 const tableSection = document.getElementById('table-section'); 4 const tableContainer = document.getElementById('table-container'); 5 const tableBtn = document.getElementById('table-btn'); 6 const closeTableBtn = document.getElementById('close-table-btn'); 7 8 if (!tableSection || !tableContainer || !tableBtn || !closeTableBtn) { 9 console.error('Required table elements not found'); 10 return; 11 } 12 13 // Configuration 14 const COLUMNS_PER_PAGE = 7; // Number of columns to show per page (including pipeline name) 15 let currentPage = 0; 16 const filterValues = {}; // Store filter values for each column 17 let currentTableEl = null; // reference to the currently rendered table element 18 19 // Function to reset all filters 20 function resetFilters() { 21 if (!currentTableEl) return; 22 // Clear all filter inputs 23 const filterInputs = currentTableEl.querySelectorAll('.filter-input'); 24 filterInputs.forEach(input => { 25 input.value = ''; 26 const th = input.closest('th'); 27 if (th && th.dataset.columnIndex) { 28 filterValues[th.dataset.columnIndex] = ''; 29 } 30 }); 31 32 // Show all rows 33 const rows = currentTableEl.querySelectorAll('tbody tr'); 34 rows.forEach(row => { 35 row.style.display = ''; 36 }); 37 38 // Reset current page to first page 39 currentPage = 0; 40 updatePageDisplay(); 41 } 42 43 // Initialize back to top button 44 const backToTopBtn = initializeBackToTopButton(tableSection); 45 46 // Function to create table 47 function createTable() { 48 // Clear existing content 49 tableContainer.innerHTML = ''; 50 51 // Create table header 52 const table = document.createElement('table'); 53 table.className = 'pipeline-table'; 54 currentTableEl = table; 55 56 // Create header row 57 const thead = document.createElement('thead'); 58 const headerRow = document.createElement('tr'); 59 60 // Define headers and their corresponding attributes 61 const columnDefinitions = [ 62 { header: 'Number', attribute: 'index', noFilter: true }, 63 { header: 'Pipeline Name', attribute: null }, 64 { header: 'Category', attribute: 'category' }, 65 { header: 'Read Types', attribute: 'readTypes' }, 66 { header: 'Multiple Samples', attribute: 'multiSample' }, 67 { header: 'Co-assembly and/or \nCo-binning', attribute: 'coAssemblyCoBinning' }, 68 { header: 'GUI', attribute: 'GUI' }, 69 { header: 'Adapted to cloud\nenviroments (i.e. AWS, Azure)', attribute: 'Cloud' }, 70 { header: 'Workflow Manager', attribute: 'workflowManager' }, 71 { header: 'Bin Refinement', attribute: 'binRefinement' }, 72 { header: 'External Computational Resources \n(i.e. Galaxy, MGnify)', attribute: 'externalComputationalResources' }, 73 { header: 'Execution Options', attribute: 'executionOptions' }, 74 { header: 'Special Features', attribute: 'specialOptions' }, 75 { header: 'Last update', attribute: 'update' }, 76 { header: 'License', attribute: 'license' }, 77 { header: 'Number of citations', attribute: 'citations' } 78 //{ header: 'Last Commit (GitHub)', attribute: 'lastCommit', custom: true }, 79 //{ header: 'Clones (GitHub)', attribute: 'clones', custom: true } 80 ]; 81 82 // Create navigation controls 83 const navigationControls = document.createElement('div'); 84 navigationControls.className = 'table-navigation'; 85 86 const prevButton = document.createElement('button'); 87 prevButton.className = 'nav-control-btn'; 88 prevButton.textContent = 'â Previous Features'; 89 prevButton.addEventListener('click', () => changePage(-1)); 90 91 const nextButton = document.createElement('button'); 92 nextButton.className = 'nav-control-btn'; 93 nextButton.textContent = 'More Features â'; 94 nextButton.addEventListener('click', () => changePage(1)); 95 96 const pageInfo = document.createElement('span'); 97 pageInfo.className = 'page-info'; 98 99 navigationControls.appendChild(prevButton); 100 navigationControls.appendChild(pageInfo); 101 navigationControls.appendChild(nextButton); 102 103 tableContainer.appendChild(navigationControls); 104 105 // Create header cells with filter inputs 106 columnDefinitions.forEach((col, index) => { 107 const th = document.createElement('th'); 108 th.dataset.columnIndex = index; 109 110 // Create header text 111 const headerText = document.createElement('div'); 112 headerText.textContent = col.header; 113 th.appendChild(headerText); 114
115 // Add filter input for all columns except index 116 if (!col.noFilter) { 117 const filterInput = document.createElement('input'); 118 filterInput.type = 'text'; 119 filterInput.className = 'filter-input'; 120 filterInput.placeholder = 'Filter...'; 121 filterInput.addEventListener('input', filterTable); 122 th.appendChild(filterInput); 123 } 124 125 headerRow.appendChild(th); 126 }); 127 128 thead.appendChild(headerRow); 129 table.appendChild(thead); 130 131 // Create table body 132 const tbody = document.createElement('tbody'); 133 134 // Add rows for each pipeline (sorted alphabetically by name) 135 const sortedPipelines = [...preLoadedObjects].sort((a, b) => a.name.localeCompare(b.name, undefined, { sensitivity: 'base' })); 136 sortedPipelines.forEach((pipeline, index) => { 137 const row = document.createElement('tr'); 138 139 // Add cells for each column 140 columnDefinitions.forEach((col, colIndex) => { 141 const cell = document.createElement('td'); 142 cell.dataset.columnIndex = colIndex; 143 144 if (col.attribute === 'index') { 145 // Index column 146 cell.textContent = index + 1; 147 } else if (col.attribute === null) { 148 // Pipeline name column 149 const nameLink = document.createElement('a'); 150 nameLink.href = '#'; 151 nameLink.addEventListener('click', (e) => { 152 e.preventDefault(); 153 // Hide table section 154 hideTable(); 155 156 // Show workflow section 157 const workflowSection = document.getElementById('workflow-section'); 158 workflowSection.style.display = 'block'; 159 160 // Select the pipeline in the dropdown 161 const pipelineSelect = document.getElementById('pipeline-select'); 162 pipelineSelect.value = pipeline.id; 163 164 // Trigger the change event to display the workflow 165 const event = new Event('change'); 166 pipelineSelect.dispatchEvent(event); 167 }); 168 nameLink.textContent = pipeline.name; 169 cell.appendChild(nameLink); 170 } else if (col.custom) { 171 // GitHub metrics columns 172 const metrics = (window.PIPELINE_GITHUB_METRICS || {})[pipeline.name]; 173 if (col.attribute === 'lastCommit') { 174 cell.textContent = metrics && metrics.lastCommit ? metrics.lastCommit : '-'; 175 } else if (col.attribute === 'clones') { 176 cell.textContent = metrics && typeof metrics.clones !== 'undefined' ? metrics.clones : '-'; 177 } 178 } else if (col.attribute === 'specialOptions' || col.attribute === 'executionOptions' || col.attribute === 'readTypes') { 179 // Special handling for multi-select options 180 const value = pipeline.attributes[col.attribute] && pipeline.attributes[col.attribute].length > 0 ? 181 pipeline.attributes[col.attribute].join(', ') : ''; 182 cell.textContent = value; 183 } else if (col.attribute === 'workflowManager') { 184 // Special handling for workflow manager 185 const value = pipeline.attributes[col.attribute] && pipeline.attributes[col.attribute].length > 0 ? 186 pipeline.attributes[col.attribute].join(', ') : ''; 187 cell.textContent = value; 188 } else { 189 // Regular attribute columns (binary options) 190 const value = pipeline.attributes[col.attribute] && pipeline.attributes[col.attribute].length > 0 ? 191 pipeline.attributes[col.attribute][0] : ''; 192 cell.textContent = value; 193 } 194 195 row.appendChild(cell); 196 }); 197 198 tbody.appendChild(row);
199 }); 200 201 table.appendChild(tbody); 202 tableContainer.appendChild(table); 203 204 // Initialize page display 205 updatePageDisplay(); 206 } 207 208 // Function to change page 209 function changePage(delta) { 210 const totalColumns = currentTableEl ? currentTableEl.querySelectorAll('th').length : 0; 211 const totalPages = Math.ceil((totalColumns - 2) / (COLUMNS_PER_PAGE - 2)); // Subtract 2 for pipeline name and index columns 212 213 currentPage = Math.max(0, Math.min(currentPage + delta, totalPages - 1)); 214 updatePageDisplay(); 215 } 216 217 // Function to update page display 218 function updatePageDisplay() { 219 const totalColumns = currentTableEl ? currentTableEl.querySelectorAll('th').length : 0; 220 const totalPages = Math.ceil((totalColumns - 2) / (COLUMNS_PER_PAGE - 2)); // Subtract 2 for pipeline name and index columns 221 222 // Update page info 223 const pageInfo = document.querySelector('.page-info'); 224 pageInfo.textContent = `Page ${currentPage + 1} of ${totalPages}`; 225 226 // Show/hide columns based on current page 227 const startCol = currentPage * (COLUMNS_PER_PAGE - 2); // Subtract 2 for pipeline name and index columns 228 const endCol = Math.min(startCol + (COLUMNS_PER_PAGE - 2), totalColumns - 1); // Subtract 2 for pipeline name and index columns 229 230 // Always show first two columns (index and pipeline name) 231 if (!currentTableEl) return; 232 currentTableEl.querySelectorAll('th, td').forEach(element => { 233 const colIndex = parseInt(element.dataset.columnIndex); 234 if (colIndex <= 1) { 235 element.style.display = ''; 236 } else if (colIndex > startCol + 1 && colIndex <= endCol + 1) { 237 element.style.display = ''; 238 // Restore filter value if it exists 239 if (element.classList.contains('filter-input')) { 240 element.value = filterValues[colIndex] || ''; 241 } 242 } else { 243 // Save filter value before hiding 244 if (element.classList.contains('filter-input')) { 245 filterValues[colIndex] = element.value; 246 } 247 element.style.display = 'none'; 248 } 249 }); 250 251 // Update navigation buttons 252 const prevButton = tableContainer.querySelector('.nav-control-btn:first-child'); 253 const nextButton = tableContainer.querySelector('.nav-control-btn:last-child'); 254 255 prevButton.disabled = currentPage === 0; 256 nextButton.disabled = currentPage === totalPages - 1; 257 } 258 259 // Function to filter table 260 function filterTable() { 261 if (!currentTableEl) return; 262 const rows = currentTableEl.querySelectorAll('tbody tr'); 263 const allColumns = Array.from(currentTableEl.querySelectorAll('th')) 264 .map(th => parseInt(th.dataset.columnIndex)); 265
266 // Get filter values for all columns, not just visible ones 267 const filters = allColumns.map(colIndex => { 268 const filterInput = currentTableEl.querySelector(`th[data-column-index="${colIndex}"] .filter-input`); 269 if (filterInput) { 270 filterValues[colIndex] = filterInput.value; // Store the filter value 271 return filterInput.value.toLowerCase(); 272 } 273 return filterValues[colIndex] ? filterValues[colIndex].toLowerCase() : ''; 274 }); 275 276 rows.forEach(row => { 277 const cells = Array.from(row.cells); 278 let show = true; 279 280 // Check all columns, not just visible ones 281 allColumns.forEach((colIndex, filterIndex) => { 282 const cell = cells[colIndex]; 283 const cellText = cell.textContent.toLowerCase(); 284 const filterText = filters[filterIndex]; 285 286 if (filterText && !cellText.includes(filterText)) { 287 show = false; 288 } 289 }); 290 291 row.style.display = show ? '' : 'none'; 292 }); 293 } 294 295 // Function to show table 296 function showTable() { 297 tableSection.style.display = 'block'; 298 document.body.style.overflow = 'hidden'; 299 // Reset filters when showing the table 300 resetFilters(); 301 } 302 303 // Function to hide table 304 function hideTable() { 305 tableSection.style.display = 'none'; 306 document.body.style.overflow = 'auto'; 307 // Reset filters when hiding the table 308 resetFilters(); 309 } 310 311 // Event listeners 312 tableBtn.addEventListener('click', showTable); 313 closeTableBtn.addEventListener('click', hideTable); 314 315 // Make functions globally available 316 window.showTable = showTable; 317 window.hideTable = hideTable; 318 319 // Initialize table 320 createTable(); 321});
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.