From de26eabac04e993ca77ab9f5eaac1ed1f16a1819 Mon Sep 17 00:00:00 2001 From: doomedraven Date: Fri, 21 Aug 2026 11:04:06 +0200 Subject: [PATCH] feat(web): Standardize tables with DataTables.js for instant search and paging --- web/static/css/dataTables.bootstrap5.min.css | 515 + web/static/js/dataTables.bootstrap5.min.js | 157 + web/static/js/jquery.dataTables.min.js | 13537 +++++++++++++++++ web/templates/analysis/index.html | 556 +- web/templates/analysis/network/_http.html | 86 +- web/templates/analysis/pending.html | 25 +- web/templates/analysis/search.html | 69 +- web/templates/audit/index.html | 560 +- web/templates/base.html | 3 + 9 files changed, 14633 insertions(+), 875 deletions(-) create mode 100644 web/static/css/dataTables.bootstrap5.min.css create mode 100644 web/static/js/dataTables.bootstrap5.min.js create mode 100644 web/static/js/jquery.dataTables.min.js diff --git a/web/static/css/dataTables.bootstrap5.min.css b/web/static/css/dataTables.bootstrap5.min.css new file mode 100644 index 00000000000..9d6b5ab43eb --- /dev/null +++ b/web/static/css/dataTables.bootstrap5.min.css @@ -0,0 +1,515 @@ +@charset "UTF-8"; +:root { + --dt-row-selected: 13, 110, 253; + --dt-row-selected-text: 255, 255, 255; + --dt-row-selected-link: 9, 10, 11; + --dt-row-stripe: 0, 0, 0; + --dt-row-hover: 0, 0, 0; + --dt-column-ordering: 0, 0, 0; + --dt-html-background: white; +} +:root.dark { + --dt-html-background: rgb(33, 37, 41); +} + +table.dataTable td.dt-control { + text-align: center; + cursor: pointer; +} +table.dataTable td.dt-control:before { + display: inline-block; + box-sizing: border-box; + content: ""; + border-top: 5px solid transparent; + border-left: 10px solid rgba(0, 0, 0, 0.5); + border-bottom: 5px solid transparent; + border-right: 0px solid transparent; +} +table.dataTable tr.dt-hasChild td.dt-control:before { + border-top: 10px solid rgba(0, 0, 0, 0.5); + border-left: 5px solid transparent; + border-bottom: 0px solid transparent; + border-right: 5px solid transparent; +} + +html.dark table.dataTable td.dt-control:before, +:root[data-bs-theme=dark] table.dataTable td.dt-control:before, +:root[data-theme=dark] table.dataTable td.dt-control:before { + border-left-color: rgba(255, 255, 255, 0.5); +} +html.dark table.dataTable tr.dt-hasChild td.dt-control:before, +:root[data-bs-theme=dark] table.dataTable tr.dt-hasChild td.dt-control:before, +:root[data-theme=dark] table.dataTable tr.dt-hasChild td.dt-control:before { + border-top-color: rgba(255, 255, 255, 0.5); + border-left-color: transparent; +} + +div.dt-scroll { + width: 100%; +} + +div.dt-scroll-body thead tr, +div.dt-scroll-body tfoot tr { + height: 0; +} +div.dt-scroll-body thead tr th, div.dt-scroll-body thead tr td, +div.dt-scroll-body tfoot tr th, +div.dt-scroll-body tfoot tr td { + height: 0 !important; + padding-top: 0px !important; + padding-bottom: 0px !important; + border-top-width: 0px !important; + border-bottom-width: 0px !important; +} +div.dt-scroll-body thead tr th div.dt-scroll-sizing, div.dt-scroll-body thead tr td div.dt-scroll-sizing, +div.dt-scroll-body tfoot tr th div.dt-scroll-sizing, +div.dt-scroll-body tfoot tr td div.dt-scroll-sizing { + height: 0 !important; + overflow: hidden !important; +} + +table.dataTable thead > tr > th:active, +table.dataTable thead > tr > td:active { + outline: none; +} +table.dataTable thead > tr > th.dt-orderable-asc span.dt-column-order:before, table.dataTable thead > tr > th.dt-ordering-asc span.dt-column-order:before, +table.dataTable thead > tr > td.dt-orderable-asc span.dt-column-order:before, +table.dataTable thead > tr > td.dt-ordering-asc span.dt-column-order:before { + position: absolute; + display: block; + bottom: 50%; + content: "▲"; + content: "▲"/""; +} +table.dataTable thead > tr > th.dt-orderable-desc span.dt-column-order:after, table.dataTable thead > tr > th.dt-ordering-desc span.dt-column-order:after, +table.dataTable thead > tr > td.dt-orderable-desc span.dt-column-order:after, +table.dataTable thead > tr > td.dt-ordering-desc span.dt-column-order:after { + position: absolute; + display: block; + top: 50%; + content: "▼"; + content: "▼"/""; +} +table.dataTable thead > tr > th.dt-orderable-asc, table.dataTable thead > tr > th.dt-orderable-desc, table.dataTable thead > tr > th.dt-ordering-asc, table.dataTable thead > tr > th.dt-ordering-desc, +table.dataTable thead > tr > td.dt-orderable-asc, +table.dataTable thead > tr > td.dt-orderable-desc, +table.dataTable thead > tr > td.dt-ordering-asc, +table.dataTable thead > tr > td.dt-ordering-desc { + position: relative; + padding-right: 30px; +} +table.dataTable thead > tr > th.dt-orderable-asc span.dt-column-order, table.dataTable thead > tr > th.dt-orderable-desc span.dt-column-order, table.dataTable thead > tr > th.dt-ordering-asc span.dt-column-order, table.dataTable thead > tr > th.dt-ordering-desc span.dt-column-order, +table.dataTable thead > tr > td.dt-orderable-asc span.dt-column-order, +table.dataTable thead > tr > td.dt-orderable-desc span.dt-column-order, +table.dataTable thead > tr > td.dt-ordering-asc span.dt-column-order, +table.dataTable thead > tr > td.dt-ordering-desc span.dt-column-order { + position: absolute; + right: 12px; + top: 0; + bottom: 0; + width: 12px; +} +table.dataTable thead > tr > th.dt-orderable-asc span.dt-column-order:before, table.dataTable thead > tr > th.dt-orderable-asc span.dt-column-order:after, table.dataTable thead > tr > th.dt-orderable-desc span.dt-column-order:before, table.dataTable thead > tr > th.dt-orderable-desc span.dt-column-order:after, table.dataTable thead > tr > th.dt-ordering-asc span.dt-column-order:before, table.dataTable thead > tr > th.dt-ordering-asc span.dt-column-order:after, table.dataTable thead > tr > th.dt-ordering-desc span.dt-column-order:before, table.dataTable thead > tr > th.dt-ordering-desc span.dt-column-order:after, +table.dataTable thead > tr > td.dt-orderable-asc span.dt-column-order:before, +table.dataTable thead > tr > td.dt-orderable-asc span.dt-column-order:after, +table.dataTable thead > tr > td.dt-orderable-desc span.dt-column-order:before, +table.dataTable thead > tr > td.dt-orderable-desc span.dt-column-order:after, +table.dataTable thead > tr > td.dt-ordering-asc span.dt-column-order:before, +table.dataTable thead > tr > td.dt-ordering-asc span.dt-column-order:after, +table.dataTable thead > tr > td.dt-ordering-desc span.dt-column-order:before, +table.dataTable thead > tr > td.dt-ordering-desc span.dt-column-order:after { + left: 0; + opacity: 0.125; + line-height: 9px; + font-size: 0.8em; +} +table.dataTable thead > tr > th.dt-orderable-asc, table.dataTable thead > tr > th.dt-orderable-desc, +table.dataTable thead > tr > td.dt-orderable-asc, +table.dataTable thead > tr > td.dt-orderable-desc { + cursor: pointer; +} +table.dataTable thead > tr > th.dt-orderable-asc:hover, table.dataTable thead > tr > th.dt-orderable-desc:hover, +table.dataTable thead > tr > td.dt-orderable-asc:hover, +table.dataTable thead > tr > td.dt-orderable-desc:hover { + outline: 2px solid rgba(0, 0, 0, 0.05); + outline-offset: -2px; +} +table.dataTable thead > tr > th.dt-ordering-asc span.dt-column-order:before, table.dataTable thead > tr > th.dt-ordering-desc span.dt-column-order:after, +table.dataTable thead > tr > td.dt-ordering-asc span.dt-column-order:before, +table.dataTable thead > tr > td.dt-ordering-desc span.dt-column-order:after { + opacity: 0.6; +} +table.dataTable thead > tr > th.sorting_desc_disabled span.dt-column-order:after, table.dataTable thead > tr > th.sorting_asc_disabled span.dt-column-order:before, +table.dataTable thead > tr > td.sorting_desc_disabled span.dt-column-order:after, +table.dataTable thead > tr > td.sorting_asc_disabled span.dt-column-order:before { + display: none; +} +table.dataTable thead > tr > th:active, +table.dataTable thead > tr > td:active { + outline: none; +} + +div.dt-scroll-body > table.dataTable > thead > tr > th, +div.dt-scroll-body > table.dataTable > thead > tr > td { + overflow: hidden; +} + +:root.dark table.dataTable thead > tr > th.dt-orderable-asc:hover, :root.dark table.dataTable thead > tr > th.dt-orderable-desc:hover, +:root.dark table.dataTable thead > tr > td.dt-orderable-asc:hover, +:root.dark table.dataTable thead > tr > td.dt-orderable-desc:hover, +:root[data-bs-theme=dark] table.dataTable thead > tr > th.dt-orderable-asc:hover, +:root[data-bs-theme=dark] table.dataTable thead > tr > th.dt-orderable-desc:hover, +:root[data-bs-theme=dark] table.dataTable thead > tr > td.dt-orderable-asc:hover, +:root[data-bs-theme=dark] table.dataTable thead > tr > td.dt-orderable-desc:hover { + outline: 2px solid rgba(255, 255, 255, 0.05); +} + +div.dt-processing { + position: absolute; + top: 50%; + left: 50%; + width: 200px; + margin-left: -100px; + margin-top: -22px; + text-align: center; + padding: 2px; + z-index: 10; +} +div.dt-processing > div:last-child { + position: relative; + width: 80px; + height: 15px; + margin: 1em auto; +} +div.dt-processing > div:last-child > div { + position: absolute; + top: 0; + width: 13px; + height: 13px; + border-radius: 50%; + background: rgb(13, 110, 253); + background: rgb(var(--dt-row-selected)); + animation-timing-function: cubic-bezier(0, 1, 1, 0); +} +div.dt-processing > div:last-child > div:nth-child(1) { + left: 8px; + animation: datatables-loader-1 0.6s infinite; +} +div.dt-processing > div:last-child > div:nth-child(2) { + left: 8px; + animation: datatables-loader-2 0.6s infinite; +} +div.dt-processing > div:last-child > div:nth-child(3) { + left: 32px; + animation: datatables-loader-2 0.6s infinite; +} +div.dt-processing > div:last-child > div:nth-child(4) { + left: 56px; + animation: datatables-loader-3 0.6s infinite; +} + +@keyframes datatables-loader-1 { + 0% { + transform: scale(0); + } + 100% { + transform: scale(1); + } +} +@keyframes datatables-loader-3 { + 0% { + transform: scale(1); + } + 100% { + transform: scale(0); + } +} +@keyframes datatables-loader-2 { + 0% { + transform: translate(0, 0); + } + 100% { + transform: translate(24px, 0); + } +} +table.dataTable.nowrap th, table.dataTable.nowrap td { + white-space: nowrap; +} +table.dataTable th, +table.dataTable td { + box-sizing: border-box; +} +table.dataTable th.dt-left, +table.dataTable td.dt-left { + text-align: left; +} +table.dataTable th.dt-center, +table.dataTable td.dt-center { + text-align: center; +} +table.dataTable th.dt-right, +table.dataTable td.dt-right { + text-align: right; +} +table.dataTable th.dt-justify, +table.dataTable td.dt-justify { + text-align: justify; +} +table.dataTable th.dt-nowrap, +table.dataTable td.dt-nowrap { + white-space: nowrap; +} +table.dataTable th.dt-empty, +table.dataTable td.dt-empty { + text-align: center; + vertical-align: top; +} +table.dataTable th.dt-type-numeric, table.dataTable th.dt-type-date, +table.dataTable td.dt-type-numeric, +table.dataTable td.dt-type-date { + text-align: right; +} +table.dataTable thead th, +table.dataTable thead td, +table.dataTable tfoot th, +table.dataTable tfoot td { + text-align: left; +} +table.dataTable thead th.dt-head-left, +table.dataTable thead td.dt-head-left, +table.dataTable tfoot th.dt-head-left, +table.dataTable tfoot td.dt-head-left { + text-align: left; +} +table.dataTable thead th.dt-head-center, +table.dataTable thead td.dt-head-center, +table.dataTable tfoot th.dt-head-center, +table.dataTable tfoot td.dt-head-center { + text-align: center; +} +table.dataTable thead th.dt-head-right, +table.dataTable thead td.dt-head-right, +table.dataTable tfoot th.dt-head-right, +table.dataTable tfoot td.dt-head-right { + text-align: right; +} +table.dataTable thead th.dt-head-justify, +table.dataTable thead td.dt-head-justify, +table.dataTable tfoot th.dt-head-justify, +table.dataTable tfoot td.dt-head-justify { + text-align: justify; +} +table.dataTable thead th.dt-head-nowrap, +table.dataTable thead td.dt-head-nowrap, +table.dataTable tfoot th.dt-head-nowrap, +table.dataTable tfoot td.dt-head-nowrap { + white-space: nowrap; +} +table.dataTable tbody th.dt-body-left, +table.dataTable tbody td.dt-body-left { + text-align: left; +} +table.dataTable tbody th.dt-body-center, +table.dataTable tbody td.dt-body-center { + text-align: center; +} +table.dataTable tbody th.dt-body-right, +table.dataTable tbody td.dt-body-right { + text-align: right; +} +table.dataTable tbody th.dt-body-justify, +table.dataTable tbody td.dt-body-justify { + text-align: justify; +} +table.dataTable tbody th.dt-body-nowrap, +table.dataTable tbody td.dt-body-nowrap { + white-space: nowrap; +} + +/*! Bootstrap 5 integration for DataTables + * + * ©2020 SpryMedia Ltd, all rights reserved. + * License: MIT datatables.net/license/mit + */ +table.table.dataTable { + clear: both; + margin-bottom: 0; + max-width: none; + border-spacing: 0; +} +table.table.dataTable.table-striped > tbody > tr:nth-of-type(2n+1) > * { + box-shadow: none; +} +table.table.dataTable > :not(caption) > * > * { + background-color: var(--bs-table-bg); +} +table.table.dataTable > tbody > tr { + background-color: transparent; +} +table.table.dataTable > tbody > tr.selected > * { + box-shadow: inset 0 0 0 9999px rgb(13, 110, 253); + box-shadow: inset 0 0 0 9999px rgb(var(--dt-row-selected)); + color: rgb(255, 255, 255); + color: rgb(var(--dt-row-selected-text)); +} +table.table.dataTable > tbody > tr.selected a { + color: rgb(9, 10, 11); + color: rgb(var(--dt-row-selected-link)); +} +table.table.dataTable.table-striped > tbody > tr:nth-of-type(2n+1) > * { + box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-stripe), 0.05); +} +table.table.dataTable.table-striped > tbody > tr:nth-of-type(2n+1).selected > * { + box-shadow: inset 0 0 0 9999px rgba(13, 110, 253, 0.95); + box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-selected), 0.95); +} +table.table.dataTable.table-hover > tbody > tr:hover > * { + box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-hover), 0.075); +} +table.table.dataTable.table-hover > tbody > tr.selected:hover > * { + box-shadow: inset 0 0 0 9999px rgba(13, 110, 253, 0.975); + box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-selected), 0.975); +} + +div.dt-container div.dt-layout-start > *:not(:last-child) { + margin-right: 1em; +} +div.dt-container div.dt-layout-end > *:not(:first-child) { + margin-left: 1em; +} +div.dt-container div.dt-layout-full { + width: 100%; +} +div.dt-container div.dt-layout-full > *:only-child { + margin-left: auto; + margin-right: auto; +} +div.dt-container div.dt-layout-table > div { + display: block !important; +} + +@media screen and (max-width: 767px) { + div.dt-container div.dt-layout-start > *:not(:last-child) { + margin-right: 0; + } + div.dt-container div.dt-layout-end > *:not(:first-child) { + margin-left: 0; + } +} +div.dt-container div.dt-length label { + font-weight: normal; + text-align: left; + white-space: nowrap; +} +div.dt-container div.dt-length select { + width: auto; + display: inline-block; + margin-right: 0.5em; +} +div.dt-container div.dt-search { + text-align: right; +} +div.dt-container div.dt-search label { + font-weight: normal; + white-space: nowrap; + text-align: left; +} +div.dt-container div.dt-search input { + margin-left: 0.5em; + display: inline-block; + width: auto; +} +div.dt-container div.dt-paging { + margin: 0; +} +div.dt-container div.dt-paging ul.pagination { + margin: 2px 0; + flex-wrap: wrap; +} +div.dt-container div.dt-row { + position: relative; +} + +div.dt-scroll-head table.dataTable { + margin-bottom: 0 !important; +} + +div.dt-scroll-body { + border-bottom-color: var(--bs-border-color); + border-bottom-width: var(--bs-border-width); + border-bottom-style: solid; +} +div.dt-scroll-body > table { + border-top: none; + margin-top: 0 !important; + margin-bottom: 0 !important; +} +div.dt-scroll-body > table > tbody > tr:first-child { + border-top-width: 0; +} +div.dt-scroll-body > table > thead > tr { + border-width: 0 !important; +} +div.dt-scroll-body > table > tbody > tr:last-child > * { + border-bottom: none; +} + +div.dt-scroll-foot > .dt-scroll-footInner { + box-sizing: content-box; +} +div.dt-scroll-foot > .dt-scroll-footInner > table { + margin-top: 0 !important; + border-top: none; +} +div.dt-scroll-foot > .dt-scroll-footInner > table > tfoot > tr:first-child { + border-top-width: 0 !important; +} + +@media screen and (max-width: 767px) { + div.dt-container div.dt-length, + div.dt-container div.dt-search, + div.dt-container div.dt-info, + div.dt-container div.dt-paging { + text-align: center; + } + div.dt-container .row { + --bs-gutter-y: 0.5rem; + } + div.dt-container div.dt-paging ul.pagination { + justify-content: center !important; + } +} +table.dataTable.table-sm > thead > tr th.dt-orderable-asc, table.dataTable.table-sm > thead > tr th.dt-orderable-desc, table.dataTable.table-sm > thead > tr th.dt-ordering-asc, table.dataTable.table-sm > thead > tr th.dt-ordering-desc, +table.dataTable.table-sm > thead > tr td.dt-orderable-asc, +table.dataTable.table-sm > thead > tr td.dt-orderable-desc, +table.dataTable.table-sm > thead > tr td.dt-ordering-asc, +table.dataTable.table-sm > thead > tr td.dt-ordering-desc { + padding-right: 20px; +} +table.dataTable.table-sm > thead > tr th.dt-orderable-asc span.dt-column-order, table.dataTable.table-sm > thead > tr th.dt-orderable-desc span.dt-column-order, table.dataTable.table-sm > thead > tr th.dt-ordering-asc span.dt-column-order, table.dataTable.table-sm > thead > tr th.dt-ordering-desc span.dt-column-order, +table.dataTable.table-sm > thead > tr td.dt-orderable-asc span.dt-column-order, +table.dataTable.table-sm > thead > tr td.dt-orderable-desc span.dt-column-order, +table.dataTable.table-sm > thead > tr td.dt-ordering-asc span.dt-column-order, +table.dataTable.table-sm > thead > tr td.dt-ordering-desc span.dt-column-order { + right: 5px; +} + +div.dt-scroll-head table.table-bordered { + border-bottom-width: 0; +} + +div.table-responsive > div.dt-container > div.row { + margin: 0; +} +div.table-responsive > div.dt-container > div.row > div[class^=col-]:first-child { + padding-left: 0; +} +div.table-responsive > div.dt-container > div.row > div[class^=col-]:last-child { + padding-right: 0; +} + +:root[data-bs-theme=dark] { + --dt-row-hover: 255, 255, 255; + --dt-row-stripe: 255, 255, 255; + --dt-column-ordering: 255, 255, 255; +} diff --git a/web/static/js/dataTables.bootstrap5.min.js b/web/static/js/dataTables.bootstrap5.min.js new file mode 100644 index 00000000000..3b041483823 --- /dev/null +++ b/web/static/js/dataTables.bootstrap5.min.js @@ -0,0 +1,157 @@ +/*! DataTables Bootstrap 5 integration + * © SpryMedia Ltd - datatables.net/license + */ + +(function( factory ){ + if ( typeof define === 'function' && define.amd ) { + // AMD + define( ['jquery', 'datatables.net'], function ( $ ) { + return factory( $, window, document ); + } ); + } + else if ( typeof exports === 'object' ) { + // CommonJS + var jq = require('jquery'); + var cjsRequires = function (root, $) { + if ( ! $.fn.dataTable ) { + require('datatables.net')(root, $); + } + }; + + if (typeof window === 'undefined') { + module.exports = function (root, $) { + if ( ! root ) { + // CommonJS environments without a window global must pass a + // root. This will give an error otherwise + root = window; + } + + if ( ! $ ) { + $ = jq( root ); + } + + cjsRequires( root, $ ); + return factory( $, root, root.document ); + }; + } + else { + cjsRequires( window, jq ); + module.exports = factory( jq, window, window.document ); + } + } + else { + // Browser + factory( jQuery, window, document ); + } +}(function( $, window, document ) { +'use strict'; +var DataTable = $.fn.dataTable; + + + +/** + * DataTables integration for Bootstrap 5. + * + * This file sets the defaults and adds options to DataTables to style its + * controls using Bootstrap. See https://datatables.net/manual/styling/bootstrap + * for further information. + */ + +/* Set the defaults for DataTables initialisation */ +$.extend( true, DataTable.defaults, { + renderer: 'bootstrap' +} ); + + +/* Default class modification */ +$.extend( true, DataTable.ext.classes, { + container: "dt-container dt-bootstrap5", + search: { + input: "form-control form-control-sm" + }, + length: { + select: "form-select form-select-sm" + }, + processing: { + container: "dt-processing card" + }, + layout: { + row: 'row mt-2 justify-content-between', + cell: 'd-md-flex justify-content-between align-items-center', + tableCell: 'col-12', + start: 'dt-layout-start col-md-auto me-auto', + end: 'dt-layout-end col-md-auto ms-auto', + full: 'dt-layout-full col-md' + } +} ); + + +/* Bootstrap paging button renderer */ +DataTable.ext.renderer.pagingButton.bootstrap = function (settings, buttonType, content, active, disabled) { + var btnClasses = ['dt-paging-button', 'page-item']; + + if (active) { + btnClasses.push('active'); + } + + if (disabled) { + btnClasses.push('disabled') + } + + var li = $('
  • ').addClass(btnClasses.join(' ')); + var a = $(' + + {% endfor %} {% endif %} - - -
    -
    +
    +
    -
    +
    +
    - -
    +
    {% if paging.show_session_next == "show" or paging.show_session_prev == "show" %} -