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