1 2//Data tables default settings 3$.extend( true, $.fn.dataTable.defaults, { 4 processing: true, 5 "language": { 6 "processing": `<div style="padding:30px 50px;background:#F9F9F9EC;border:1px solid #0003;position:absolute;z-index:999;"> 7 Processing ... 8 </div>`, 9 } 10} ); 11 12// Options pre-processor 13function dt_getOptions(options, dt_serverCall, server, filters) { 14 // Common settings 15 options["orderCellsTop"] = true; 16 17 // Check for server-loading 18 if(server || 'sAjaxSource' in options) { 19 // console.log("DataTable"+(filters?"Filters":"")+": server-side load") 20 options["bProcessing"] = true; 21 options["bServerSide"] = true; 22 options["fnServerData"] = dt_serverCall; 23 } 24 else { 25 // console.log("DataTable"+(filters?"Filters":"")+": client-side load") 26 options["bProcessing"] = false; 27 options["bServerSide"] = false; 28 options["fnServerData"] = null; 29 } 30 31 if(options["bProcessing"] && filters) { 32 // add flag for datatablefilters 33 options["sAjaxSource"] += "&dataTableFilters=true"; 34 } 35 36 // Return processed options 37 return options; 38} 39/* 40* Initiates dataTable for Server-Loading 41*/ 42$.fn.DataTableServer = function(options = {}) { 43 function dt_serverCall(sSource, aoData, fnCallback) { 44 // console.log("sSource: ", sSource, " aoData: ", aoData) 45 $.ajax({ 46 url: sSource, 47 type: 'GET', 48 data: aoData, // Data to send to server 49 dataType: 'json', 50 success: function(response) { 51 // console.log("response: ", response) 52 if(response.success) { 53 fnCallback(response.success) // Pass the server data back to DataTables 54 } else { 55 console.error(response) 56 } 57 }, 58 error: function(response) { 59 console.error(response) 60 } 61 }); 62 } 63 64 var _this = this, 65 _options = dt_getOptions(options, dt_serverCall, true, false) 66 67 // Create DataTables 68 var _table = new DataTable(_this, _options); 69} 70/* 71* Creates the DataTable with filters 72*/ 73$.fn.DataTableFilters = function(options = {}) { 74 function dt_serverCall(sSource, aoData, fnCallback) { 75 // Add selected filters to ajax 76 var selectedFilters = {}; 77 $('.column_filter').each(function( i ) { 78 const columnIdx = $(this).data("column"); 79 $(`select[data-column='${columnIdx}'] :selected`).each(function( i ) { 80 const vals = $(this).data("values"); 81 if(!selectedFilters[columnIdx]) selectedFilters[columnIdx] = [] 82 selectedFilters = {...selectedFilters, [columnIdx]: [...selectedFilters[columnIdx], vals]}; 83 }) 84 }) 85 aoData.push({ 86 "name": "selectedFilters", 87 "value": JSON.stringify(selectedFilters) 88 }); 89 90 $.ajax({ 91 url: sSource, 92 type: 'GET', 93 data: aoData, // Data to send to server 94 dataType: 'json', 95 success: function(response) { 96 if(response.success) { 97 fnCallback(response.success) // Pass the server data back to DataTables 98 loadColumns(response.success.columns); 99 } else { 100 console.error(response) 101 } 102 }, 103 error: function(response) { 104 console.error(response) 105 } 106 }); 107 } 108 109 // Init 110 var _this = this, 111 _options = dt_getOptions(options, dt_serverCall, null, true), 112 _isServerSideLoad = _options["bServerSide"], 113 _hasInitColumns = false; 114 115 // Create DataTables 116 var _table = new DataTable(_this, _options); 117 118 // Create filter dropdowns 119 function initColumns() { 120 if(!_hasInitColumns) { 121 _hasInitColumns = true; 122 123 // Create filter row 124 var row = `<tr class="dataTable-filterRow">`; 125 $("#" + _this.attr('id') + ' thead tr th').each(function (i) { 126 var thCol = $(this); 127 // append column filter to row 128 row += 129 (`<th class="sorting_disabled">` + 130 (thCol.hasClass("sorting_disabled") 131 ? `` 132 : `<select class="column_filter select2" data-column="${i}" multiple> 133 <option value="">All</option> 134 </select>` 135 ) + 136 `</th>`); 137 }) 138 row += "</tr>"; 139 // append row to thead 140 _this.find('thead').append($(row)); 141 142 // Initialize Select2 on the filter dropdowns 143 $('.select2').select2({ 144 placeholder: "Select values", 145 allowClear: true, 146 width: 'resolve' 147 }); 148 } 149 }
150 initColumns(); 151 152 153 // Dynamic column loading 154 function loadColumns(serverColumnData = null) { 155 if(!_hasInitColumns) initColumns(); 156 157 // If client-side loading 158 if(!_isServerSideLoad) { 159 // Populate the dropdowns dynamically with distinct plain text column values 160 _table.columns().every(function() { 161 var column = this; 162 var columnIndex = column.index(); 163 164 var uniqueValues = {}; // Object to track unique values and avoid duplicates 165 166 // Iterate through each cell and extract the plain text content 167 column.nodes().each(function(node) { 168 var plainText = $(node).text().trim(); // Extract text without HTML tags 169 170 // Add to select only if it is not already added 171 if (plainText && !uniqueValues[plainText]) { 172 $('select[data-column="' + columnIndex + '"]').append('<option value="' + plainText + '">' + plainText + '</option>'); 173 uniqueValues[plainText] = true; // Mark value as added 174 } 175 }); 176 }); 177 } 178 // Else server-loading, if column data is included 179 else if(serverColumnData) { 180 Object.entries(serverColumnData).forEach(([columnIndex, columnData]) => { 181 Object.entries(columnData).forEach(([optionIndex, {result, values}]) => { 182 $(`select[data-column='${columnIndex}']`).append( 183 $(`<option></option>`).data("values", values).val(result).html(result) 184 ); 185 }) 186 }) 187 } 188 }; 189 loadColumns(); 190 191 192 // Apply individual column filters using Select2 193 $('.column_filter').on('change', function() { 194 var column_filter = $(this); 195 var columnIndex = column_filter.attr('data-column'); 196 var selectedValues = column_filter.val(); // Get selected values 197 198 // If there are selected values, filter the table 199 if (selectedValues && selectedValues.length > 0) { 200 // escape any special regex characters 201 selectedValues = selectedValues.map(val => val.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')) 202 203 /// create regex for literal search terms 204 var regexLiteral = '(((?<!.)(' + selectedValues.join(')(?!.))|((?<!.)(') + ')(?!.)))'; 205 // create regex for search term wrapped in html tags 206 var regexHTML = '((' + selectedValues.join(')|(') + ')(\s)*[^\w]+)'; 207 // create final regex 208 var searchRegex = regexLiteral + "|" + regexHTML; 209 210 // search and re-draw 211 _table 212 .column(columnIndex) 213 .search(searchRegex, true, false) 214 .draw(); 215 } else { 216 _table.column(columnIndex).search('').draw(); // Clear the filter if nothing is selected 217 } 218 }); 219};
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.