|
921 | 921 |
|
922 | 922 | # CSS style used in HTML dump format
|
923 | 923 | HTML_DUMP_CSS_STYLE = """<style>
|
924 |
| -table{ |
925 |
| - margin:10; |
926 |
| - background-color:#FFFFFF; |
927 |
| - font-family:verdana; |
928 |
| - font-size:12px; |
929 |
| - align:center; |
| 924 | +table { |
| 925 | + margin: 10px; |
| 926 | + background: #fff; |
| 927 | + font: 12px verdana; |
| 928 | + text-align: center; |
930 | 929 | }
|
931 | 930 | thead{
|
932 | 931 | font-weight:bold;
|
933 | 932 | background-color:#4F81BD;
|
934 |
| - color:#FFFFFF; |
| 933 | + color: #fff; |
935 | 934 | }
|
936 | 935 | tr:nth-child(even) {
|
937 |
| - background-color: #D3DFEE |
| 936 | + background-color: #D3DFEE; |
938 | 937 | }
|
939 |
| -td{ |
940 |
| - font-size:12px; |
| 938 | +</style>""" |
| 939 | + |
| 940 | +HTML_DUMP_CSS_SORTABLE_STYLE = """ |
| 941 | +<style> |
| 942 | +table thead th { |
| 943 | + cursor: pointer; |
| 944 | + white-space: nowrap; |
| 945 | + position: sticky; |
| 946 | + top: 0; |
| 947 | + z-index: 1; |
941 | 948 | }
|
942 |
| -th{ |
943 |
| - font-size:12px; |
| 949 | +
|
| 950 | +table thead th::after, |
| 951 | +table thead th::before { |
| 952 | + color: transparent; |
| 953 | +} |
| 954 | +
|
| 955 | +table thead th::after { |
| 956 | + margin-left: 3px; |
| 957 | + content: "▸"; |
| 958 | +} |
| 959 | +
|
| 960 | +table thead th:hover::after, |
| 961 | +table thead th[aria-sort]::after { |
| 962 | + color: inherit; |
| 963 | +} |
| 964 | +
|
| 965 | +table thead th[aria-sort=descending]::after { |
| 966 | + content: "▾"; |
944 | 967 | }
|
945 |
| -</style>""" |
946 | 968 |
|
| 969 | +table thead th[aria-sort=ascending]::after { |
| 970 | + content: "▴"; |
| 971 | +} |
| 972 | +
|
| 973 | +table thead th.indicator-left::before { |
| 974 | + margin-right: 3px; |
| 975 | + content: "▸"; |
| 976 | +} |
| 977 | +
|
| 978 | +table thead th.indicator-left[aria-sort=descending]::before { |
| 979 | + color: inherit; |
| 980 | + content: "▾"; |
| 981 | +} |
| 982 | +
|
| 983 | +table thead th.indicator-left[aria-sort=ascending]::before { |
| 984 | + color: inherit; |
| 985 | + content: "▴"; |
| 986 | +} |
| 987 | +</style> |
| 988 | +""" |
| 989 | +HTML_DUMP_SORTABLE_JAVASCRIPT = """<script> |
| 990 | +window.addEventListener('DOMContentLoaded', () => { |
| 991 | + document.addEventListener('click', event => { |
| 992 | + try { |
| 993 | + const isAltSort = event.shiftKey || event.altKey; |
| 994 | +
|
| 995 | + // Find the clicked table header |
| 996 | + const findParentElement = (element, nodeName) => |
| 997 | + element.nodeName === nodeName ? element : findParentElement(element.parentNode, nodeName); |
| 998 | + |
| 999 | + const headerCell = findParentElement(event.target, 'TH'); |
| 1000 | + const headerRow = headerCell.parentNode; |
| 1001 | + const thead = headerRow.parentNode; |
| 1002 | + const table = thead.parentNode; |
| 1003 | +
|
| 1004 | + if (thead.nodeName !== 'THEAD') return; |
| 1005 | +
|
| 1006 | + // Reset sort indicators on other headers |
| 1007 | + Array.from(headerRow.cells).forEach(cell => { |
| 1008 | + if (cell !== headerCell) cell.removeAttribute('aria-sort'); |
| 1009 | + }); |
| 1010 | +
|
| 1011 | + // Toggle sort direction |
| 1012 | + const currentSort = headerCell.getAttribute('aria-sort'); |
| 1013 | + const isAscending = table.classList.contains('asc') && currentSort !== 'ascending'; |
| 1014 | + const sortDirection = (currentSort === 'descending' || isAscending) ? 'ascending' : 'descending'; |
| 1015 | + headerCell.setAttribute('aria-sort', sortDirection); |
| 1016 | +
|
| 1017 | + // Debounce sort operation |
| 1018 | + if (table.dataset.timer) clearTimeout(Number(table.dataset.timer)); |
| 1019 | + |
| 1020 | + table.dataset.timer = setTimeout(() => { |
| 1021 | + sortTable(table, isAltSort); |
| 1022 | + }, 1).toString(); |
| 1023 | + } catch (error) { |
| 1024 | + console.error('Sorting error:', error); |
| 1025 | + } |
| 1026 | + }); |
| 1027 | +}); |
| 1028 | +
|
| 1029 | +function sortTable(table, useAltSort) { |
| 1030 | + table.dispatchEvent(new CustomEvent('sort-start', { bubbles: true })); |
| 1031 | +
|
| 1032 | + const sortHeader = table.tHead.querySelector('th[aria-sort]'); |
| 1033 | + const headerRow = table.tHead.children[0]; |
| 1034 | + const isAscending = sortHeader.getAttribute('aria-sort') === 'ascending'; |
| 1035 | + const shouldPushEmpty = table.classList.contains('n-last'); |
| 1036 | + const sortColumnIndex = Number(sortHeader.dataset.sortCol ?? sortHeader.cellIndex); |
| 1037 | +
|
| 1038 | + const getCellValue = cell => { |
| 1039 | + if (useAltSort) return cell.dataset.sortAlt; |
| 1040 | + return cell.dataset.sort ?? cell.textContent; |
| 1041 | + }; |
| 1042 | +
|
| 1043 | + const compareRows = (row1, row2) => { |
| 1044 | + const value1 = getCellValue(row1.cells[sortColumnIndex]); |
| 1045 | + const value2 = getCellValue(row2.cells[sortColumnIndex]); |
| 1046 | +
|
| 1047 | + // Handle empty values |
| 1048 | + if (shouldPushEmpty) { |
| 1049 | + if (value1 === '' && value2 !== '') return -1; |
| 1050 | + if (value2 === '' && value1 !== '') return 1; |
| 1051 | + } |
| 1052 | +
|
| 1053 | + // Compare numerically if possible, otherwise use string comparison |
| 1054 | + const numericDiff = Number(value1) - Number(value2); |
| 1055 | + const comparison = isNaN(numericDiff) ? |
| 1056 | + value1.localeCompare(value2, undefined, { numeric: true }) : |
| 1057 | + numericDiff; |
| 1058 | +
|
| 1059 | + // Handle tiebreaker |
| 1060 | + if (comparison === 0 && headerRow.cells[sortColumnIndex]?.dataset.sortTbr) { |
| 1061 | + const tiebreakIndex = Number(headerRow.cells[sortColumnIndex].dataset.sortTbr); |
| 1062 | + return compareRows(row1, row2, tiebreakIndex); |
| 1063 | + } |
| 1064 | +
|
| 1065 | + return isAscending ? -comparison : comparison; |
| 1066 | + }; |
| 1067 | +
|
| 1068 | + // Sort each tbody |
| 1069 | + Array.from(table.tBodies).forEach(tbody => { |
| 1070 | + const rows = Array.from(tbody.rows); |
| 1071 | + const sortedRows = rows.sort(compareRows); |
| 1072 | + |
| 1073 | + const newTbody = tbody.cloneNode(); |
| 1074 | + newTbody.append(...sortedRows); |
| 1075 | + tbody.replaceWith(newTbody); |
| 1076 | + }); |
| 1077 | +
|
| 1078 | + table.dispatchEvent(new CustomEvent('sort-end', { bubbles: true })); |
| 1079 | +} |
| 1080 | +</script>""" |
947 | 1081 | # Leaving (dirty) possibility to change values from here (e.g. `export SQLMAP__MAX_NUMBER_OF_THREADS=20`)
|
948 | 1082 | for key, value in os.environ.items():
|
949 | 1083 | if key.upper().startswith("%s_" % SQLMAP_ENVIRONMENT_PREFIX):
|
|
0 commit comments