dashboard.dashboard.js Maven / Gradle / Ivy
The newest version!
let sheetsMap = new Map();
async function DecompressBlob(blob) {
const ds = new DecompressionStream("gzip");
const decompressedStream = blob.stream().pipeThrough(ds);
return await new Response(decompressedStream).blob();
}
/**
* https://stackoverflow.com/questions/16245767/creating-a-blob-from-a-base64-string-in-javascript
* Convert a base64 string to a Blob object.
* @param base64Data {string}
* @param contentType {string}
* @returns {Blob}
*/
function base64toBlob(base64Data, contentType = '') {
contentType = contentType || '';
const sliceSize = 1024;
const byteCharacters = atob(base64Data);
const bytesLength = byteCharacters.length;
const slicesCount = Math.ceil(bytesLength / sliceSize);
const byteArrays = new Array(slicesCount);
for (let sliceIndex = 0; sliceIndex < slicesCount; ++sliceIndex) {
const begin = sliceIndex * sliceSize;
const end = Math.min(begin + sliceSize, bytesLength);
const bytes = new Array(end - begin);
for (let offset = begin, i = 0; offset < end; ++i, ++offset) {
bytes[i] = byteCharacters[offset].charCodeAt(0);
}
byteArrays[sliceIndex] = new Uint8Array(bytes);
}
return new Blob(byteArrays, {type: contentType});
}
Element.prototype.isVisible = function (percentX, percentY) {
let tolerance = 0.01; /* needed because the rects returned by getBoundingClientRect provides the position up to 10 decimals */
/*if (percentX == null) {
percentX = 100;
}*/
if (percentY == null) {
percentY = 100;
}
let elementRect = this.getBoundingClientRect();
let parentRects = [];
let element = this;
while (element.parentElement != null) {
parentRects.push(element.parentElement.getBoundingClientRect());
element = element.parentElement;
}
return parentRects.every(function (parentRect) {
/*let visiblePixelX = Math.min(elementRect.right, parentRect.right) - Math.max(elementRect.left, parentRect.left);
let visiblePercentageX = visiblePixelX / elementRect.width * 100;*/
let visiblePixelY = Math.min(elementRect.bottom, parentRect.bottom) - Math.max(elementRect.top, parentRect.top);
let visiblePercentageY = visiblePixelY / elementRect.height * 100;
/* this would check if both x and y-wise the component is visible, but checking only y is enough for this case
* return visiblePercentageX + tolerance > percentX && visiblePercentageY + tolerance > percentY; */
return visiblePercentageY + tolerance > percentY;
});
};
let doNotAddToHistory = false;
let isFirstSheet = false;
let lastSheet = '';
let sheetRecursionCheck = '';
/* sheet id --> { decompressedSheet: string, decompressionTime: number } */
let decompressedSheetsCache = new Map();
function showSheet(sheetID) {
if (sheetID == null) return;
if (!sheetsMap.has(sheetID)) {
if (sheetRecursionCheck === sheetID) {
sheetRecursionCheck = null;
return;
}
sheetRecursionCheck = defaultSheet;
showSheet(defaultSheet);
return;
}
if (isFirstSheet && lastSheet.length > 0) {
isFirstSheet = false;
return;
}
if (!doNotAddToHistory && lastSheet.length > 0) {
addToHistory(lastSheet);
}
if (lastSheet.length > 0) {
document.getElementById("nav-row-" + lastSheet).classList.remove("active");
document.getElementById("navigation-tile-" + lastSheet).classList.remove("active");
document.getElementById("nav-row-" + sheetID).classList.add("active");
document.getElementById("navigation-tile-" + sheetID).classList.add("active");
}
lastSheet = sheetID;
accessContentSheet(sheetID)
.then(decompressedSheetData => {
let sheetContainer = document.getElementById('sheet-container');
sheetContainer.innerHTML = '';
let element = document.createElement('span');
const decompressedSheet = decompressedSheetData.decompressedSheet;
if (decompressedSheet == null) {
console.error('Failed to access and decompress sheet data: ', e);
return;
}
element.innerHTML = decompressedSheet;
sheetContainer.appendChild(element.firstChild);
hideRequiredSheetParagraphs(false);
nodeScriptReplace(sheetContainer);
if (!document.getElementById("navigation-tile-" + sheetID).isVisible()) {
let headerOffset = 200;
let elementPosition = document.getElementById('navigation-tile-' + sheetID).getBoundingClientRect().top;
let offsetPosition = elementPosition - headerOffset + document.getElementById('navigation-table').scrollTop;
document.getElementById('navigation-table').scrollTo({
top: offsetPosition,
behavior: "smooth"
});
}
doNotAddToHistory = false;
updateGoBackArrow();
document.title = sheetID + ' | ' + document.title.replace(/^.* \| /, '');
updateURL();
if (currentDisplayMode === 0) {
setDisplayMode(1);
}
setContentSheetWidth();
let keys = Array.from(sheetsMap.keys());
let currentIndex = keys.indexOf(sheetID);
let beforeIndex = currentIndex - 1;
let nextIndex = currentIndex + 1;
if (beforeIndex >= 0) {
accessContentSheet(keys[beforeIndex]);
}
if (nextIndex < keys.length) {
accessContentSheet(keys[nextIndex]);
}
})
.catch(e => {
console.error('Failed to access and decompress sheet data: ', e);
});
}
function accessContentSheet(sheetID) {
return new Promise((resolve, reject) => {
if (decompressedSheetsCache.has(sheetID)) {
// console.log('sheet cache hit:', sheetID);
resolve(decompressedSheetsCache.get(sheetID));
} else {
const sheetCompressedContent = sheetsMap.get(sheetID);
const startTimestamp = performance.now();
DecompressBlob(base64toBlob(sheetCompressedContent))
.then(e => {
e.text().then(decompressedSheetData => {
decompressedSheetsCache.set(sheetID, {
decompressedSheet: decompressedSheetData,
decompressionTime: performance.now()
});
console.log('sheet cache miss:', sheetID, 'in', performance.now() - startTimestamp, 'ms');
cleanupContentSheetCache();
resolve(decompressedSheetsCache.get(sheetID));
});
})
.catch(e => {
console.error('Failed to access and decompress sheet data: ', e);
reject(e);
});
}
});
}
function cleanupContentSheetCache() {
const maxCacheSize = 20;
while (decompressedSheetsCache.size > maxCacheSize) {
let oldestSheet = '';
let oldestDecompressionTime = Number.MAX_VALUE;
decompressedSheetsCache.forEach((value, key) => {
if (value.decompressionTime < oldestDecompressionTime) {
oldestSheet = key;
oldestDecompressionTime = value.decompressionTime;
}
});
decompressedSheetsCache.delete(oldestSheet);
}
}
function nodeScriptReplace(node) {
if (node != null) {
if (node.tagName === 'SCRIPT') {
node.parentNode.replaceChild(nodeScriptClone(node), node);
} else {
let i = -1, children = node.childNodes;
while (++i < children.length) {
nodeScriptReplace(children[i]);
}
}
}
return node;
}
function nodeScriptClone(node) {
let script = document.createElement("script");
script.text = node.innerHTML;
let i = -1, attrs = node.attributes, attr;
while (++i < attrs.length) {
script.setAttribute((attr = attrs[i]).name, attr.value);
}
return script;
}
let historyStack = [];
function addToHistory(sheet) {
if (historyStack.length === 0 || historyStack[historyStack.length - 1] !== sheet) {
historyStack.push(sheet);
adjustTopRightOverflowItems();
}
if (historyStack.length > 30) {
historyStack.shift();
}
}
function historyBackwards() {
if (historyStack.length > 0) {
doNotAddToHistory = true;
showSheet(historyStack.pop());
adjustTopRightOverflowItems();
}
}
let goBackArrowIsVisible = false;
function updateGoBackArrow() {
if (document.getElementsByClassName('previous-sheet-arrow').length > 0) {
/* only show the go-back-arrow if there are entries to go back to and if there is only one make sure it is not the currently displayed one */
if (historyStack.length > 0 && !(historyStack.length === 1 && historyStack[0] === lastSheet)) {
document.getElementsByClassName('previous-sheet-arrow')[0].classList.remove('hidden');
goBackArrowIsVisible = true;
} else {
document.getElementsByClassName('previous-sheet-arrow')[0].classList.add('hidden');
goBackArrowIsVisible = false;
}
}
}
function hideRequiredSheetParagraphs(changeSliders) {
let hideSheetDataGET = findGetParameter('hide');
if (hideSheetDataGET != null) {
let hideData = hideSheetDataGET.split("_");
for (let i = 0; i < hideData.length; i++) {
if (hideData[i].length > 0) {
toggleDataSheetContentVisibility(hideData[i], changeSliders);
}
}
}
}
function toggleDataSheetContentVisibility(contentType, toggleSliders) {
let allContentTypeElements = document.getElementsByClassName('data-sheet-' + contentType);
let dataSheetContentToggle = document.getElementById('data-sheet-content-toggle-' + contentType);
for (let i = 0, max = allContentTypeElements.length; i < max; i++) {
if (allContentTypeElements[i].classList.contains('hidden'))
allContentTypeElements[i].classList.remove('hidden');
else allContentTypeElements[i].classList.add('hidden');
}
if (toggleSliders && dataSheetContentToggle != null) {
if (dataSheetContentToggle.hasAttribute('checked'))
dataSheetContentToggle.removeAttribute('checked');
else dataSheetContentToggle.setAttribute('checked', '');
updateURL();
}
}
let updateUrl = false;
function updateURL() {
if (updateUrl) {
let queryParams = new URLSearchParams(window.location.search);
let hiddenData = '';
for (let i = 0; i < visibilityToggleDataIdentifiers.length; i++) {
if (document.getElementById(visibilityToggleDataIdentifiers[i]) == null) continue;
if (!document.getElementById(visibilityToggleDataIdentifiers[i]).hasAttribute('checked')) {
if (hiddenData.length > 0) hiddenData = hiddenData + '_';
hiddenData = hiddenData + visibilityToggleDataIdentifiers[i].replace('data-sheet-content-toggle-', '');
}
}
if (hiddenData.length > 0) queryParams.set('hide', trimCharacterFromString(hiddenData, '_'));
else queryParams.delete('hide');
if (lastSheet.length > 0) {
queryParams.set('sheet', lastSheet.replaceAll('-', '_'));
}
let filters = [];
for (let i = 0; i < navigationFilters.length; i++) {
let filter = navigationFilters[i];
filters.push(filter.column + ';' + filter.operation + ';' + filter.value.replace(',', 'REP00'));
}
if (filters.length > 0) {
queryParams.set('f', filters.join(',').replaceAll('#', 'REP01').replaceAll(' ', 'REP02'));
} else {
queryParams.delete('f');
}
if (bookmarkedDataSheets.length > 0) {
queryParams.set('bkm', bookmarkedDataSheets.join(','));
} else {
queryParams.delete('bkm');
}
if (tableIsSortedBy !== -1 && tableIsSortedBy != null) {
queryParams.set('sort', tableIsSortedBy);
queryParams.set('sortDir', tableIsSortedDirection);
}
history.replaceState(null, null, '?' + queryParams.toString());
}
}
function findGetParameter(parameterName) {
let result = null, tmp = [];
let items = location.search.substr(1).split('&');
for (let index = 0; index < items.length; index++) {
tmp = items[index].split('=');
if (tmp[0] === parameterName) result = decodeURIComponent(tmp[1]).replaceAll('REP01', '#').replaceAll('REP02', ' ');
}
return result;
}
let tableIsSortedBy = -1;
let tableIsSortedDirection = true;
function checkForTableRowsSortedInit() {
tableIsSortedBy = findGetParameter('sort');
if (tableIsSortedBy != null && findGetParameter('sortDir') != null) {
tableIsSortedDirection = findGetParameter('sortDir') === 'true';
let tableElement = document.getElementById('navigation-table-table');
if (tableElement != null) {
sortTableRowsByColumn(tableElement, tableIsSortedBy, tableIsSortedDirection);
document.getElementById('header-table-id-' + tableIsSortedBy).dataset['sort'] = tableIsSortedDirection ? 'asc' : 'desc';
}
}
}
function sortTableRowsByColumn(table, columnIndex, ascending) {
if (table == null) return;
const rows = Array.from(table.getElementsByTagName('tbody')[0].getElementsByTagName('tr'));
rows.sort((x, y) => {
let xValue = x.cells[columnIndex].textContent;
let yValue = y.cells[columnIndex].textContent;
if (!isNaN(xValue) && !isNaN(yValue)) {
xValue = parseFloat(xValue);
yValue = parseFloat(yValue);
} else if (xValue != null && yValue != null) {
let xValueTmp = xValue.toString().replace(/^ *(\d+).*$/, '$1');
let yValueTmp = yValue.toString().replace(/^ *(\d+).*$/, '$1');
if (!isNaN(xValueTmp) && !isNaN(yValueTmp)) {
xValue = parseFloat(xValueTmp);
yValue = parseFloat(yValueTmp);
}
}
if (xValue === yValue) {
const identifierX = x.cells[0].textContent;
const identifierY = y.cells[0].textContent;
if (ascending) {
return identifierX > identifierY ? 1 : -1;
} else {
return identifierX < identifierY ? 1 : -1;
}
}
if (ascending) {
if (xValue === "N/A")
return -1;
if (yValue === "N/A")
return 1;
return xValue > yValue ? 1 : -1;
} else {
if (xValue === "N/A")
return 1;
if (yValue === "N/A")
return -1;
return xValue < yValue ? 1 : -1;
}
});
for (let row of rows) {
table.tBodies[0].appendChild(row);
}
let sortedIndicatorElements = document.getElementsByClassName('header-table-sort');
for (let i = 0, max = sortedIndicatorElements.length; i < max; i++) {
sortedIndicatorElements[i].classList.remove('btn-primary');
sortedIndicatorElements[i].classList.remove('btn-success');
sortedIndicatorElements[i].classList.add('btn-secondary');
}
sortedIndicatorElements = document.getElementsByClassName('navigation-header-tile');
for (let i = 0, max = sortedIndicatorElements.length; i < max; i++) {
sortedIndicatorElements[i].classList.remove('descending');
sortedIndicatorElements[i].classList.remove('ascending');
}
let sortArrow = document.getElementById('navigation-table-sort-dir-arrow');
if (sortArrow !== null) {
sortArrow.remove();
}
let sortedIndicatorElement = document.getElementById('header-table-sort-' + columnIndex);
let sortedIndicatorTableElement = document.getElementById('header-table-id-' + columnIndex);
sortedIndicatorElement.classList.remove('btn-secondary');
if (ascending) {
sortedIndicatorElement.classList.add('btn-success');
sortedIndicatorTableElement.classList.add('ascending');
sortArrow = document.createElement('span');
sortArrow.innerHTML = getSvg('arrow-down', 14, 'var(--strong-dark-green)');
} else {
sortedIndicatorElement.classList.add('btn-primary');
sortedIndicatorTableElement.classList.add('descending');
sortArrow = document.createElement('span');
sortArrow.innerHTML = getSvg('arrow-up', 14, 'var(--strong-dark-blue)');
}
sortArrow.id = 'navigation-table-sort-dir-arrow';
sortArrow.style.marginLeft = '3px';
sortedIndicatorTableElement.appendChild(sortArrow);
tableIsSortedBy = columnIndex;
tableIsSortedDirection = ascending;
updateURL();
}
function onColumnHeaderClicked(th) {
const table = th.parentElement.parentElement.parentElement;
const thIndex = Array.from(th.parentElement.children).indexOf(th);
const descending = !('sort' in th.dataset) || th.dataset.sort !== 'desc';
const start = performance.now();
sortTableRowsByColumn(table, thIndex, !descending);
const end = performance.now();
console.log("Sorted table rows in %d ms.", end - start);
const allTh = table.querySelectorAll(':scope > thead > tr > th');
for (let th2 of allTh) {
delete th2.dataset['sort'];
}
th.dataset['sort'] = descending ? 'desc' : 'asc';
}
function onColumnHeaderRightClicked(th) {
addFilter(th.innerText, '', '');
openModal('settingsModal');
}
function setNavigationVisible(visible) {
let navItem = document.getElementById('navigation-table');
if (visible) navItem.classList.remove('hidden');
else navItem.classList.add('hidden');
}
function setSheetsVisible(visible) {
let sheet = document.getElementsByClassName('right-sheet');
for (let i = 0, max = sheet.length; i < max; i++) {
if (visible) sheet[i].classList.remove('hidden');
else sheet[i].classList.add('hidden');
}
}
let navigationFullScreenStoredWidth = '0px';
function setNavigationFullScreen(active) {
let navItem = document.getElementById('navigation-table');
if (active) {
navItem.classList.add('full-screen');
navItem.classList.remove('horizontal-resize');
navigationFullScreenStoredWidth = navItem.style.width;
navItem.style.removeProperty('width');
} else {
navItem.classList.remove('full-screen');
navItem.classList.add('horizontal-resize');
let widthDiff = window.innerWidth - navigationFullScreenStoredWidth.replace('px', '');
if (navigationFullScreenStoredWidth !== '0px' && widthDiff > 130) {
navItem.style.width = navigationFullScreenStoredWidth;
}
}
}
let currentDisplayMode = 0;
function nextDisplayMode() {
currentDisplayMode = Math.max(0, currentDisplayMode - 1);
setDisplayMode(currentDisplayMode);
}
function previousDisplayMode(allowOverflow) {
if (allowOverflow) currentDisplayMode = (currentDisplayMode + 1) % 3;
else currentDisplayMode = Math.min(2, currentDisplayMode + 1);
setDisplayMode(currentDisplayMode);
}
function setDisplayMode(displayMode) {
currentDisplayMode = displayMode;
console.log('Setting display mode to ' + currentDisplayMode);
if (displayMode === 0) {
setNavigationVisible(true);
setSheetsVisible(false);
setNavigationFullScreen(true);
} else if (displayMode === 1) {
setNavigationVisible(true);
setSheetsVisible(true);
setNavigationFullScreen(false);
if (lastSheet === '') {
showSheet(defaultSheet);
} else {
showSheet(lastSheet);
}
} else if (displayMode === 2) {
setNavigationVisible(false);
setSheetsVisible(true);
setNavigationFullScreen(false);
showSheet(lastSheet);
}
}
function isAnyModalVisible() {
let modals = document.getElementsByClassName('modal');
if (modals == null) return false;
for (let i = 0; i < modals.length; i++) {
if (modals[i].style.display !== 'none') return true;
}
return false;
}
function isModalVisible(modalId) {
let current = document.getElementById(modalId).style.display;
return current !== 'none' && current !== '';
}
function toggleModal(modalId) {
if (isModalVisible(modalId)) closeModal(modalId);
else openModal(modalId);
}
function closeAllModals() {
let modals = document.getElementsByClassName('modal');
if (modals == null) return;
for (let i = 0; i < modals.length; i++) {
modals[i].style.display = 'none';
}
overwriteFilterUid = -1;
}
function openModal(modalId) {
closeAllModals();
let modal = document.getElementById(modalId);
if (modal == null) return;
modal.style.display = 'block';
if (onOpenModalScripts && onOpenModalScripts[modalId]) {
onOpenModalScripts[modalId]();
}
}
function closeModal(modalId) {
let modal = document.getElementById(modalId);
if (modal == null) return;
modal.style.display = 'none';
overwriteFilterUid = -1;
}
/* new search and filter */
function getAllColumnsNames() {
let columns = [];
let navigationHeaders = document.getElementsByClassName('navigation-header-tile');
for (let k = 0; k < navigationHeaders.length; k++) {
columns.push(normalizeHeader(navigationHeaders[k].innerText));
}
return columns;
}
function capitalizeWords(str) {
return str.replace(/\w\S*/g, function (txt) {
return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();
});
}
function updateFiltersInSettings() {
let displayFilterListing = document.getElementById('display-filter-listing');
let displayFilterClearAll = document.getElementById('display-filter-clear-filters');
displayFilterListing.innerHTML = '';
if (navigationFilters.length === 0) {
displayFilterListing.classList.add('hidden');
displayFilterClearAll.classList.add('hidden');
} else {
displayFilterListing.classList.remove('hidden');
displayFilterClearAll.classList.remove('hidden');
for (let i = 0; i < navigationFilters.length; i++) {
let filter = navigationFilters[i];
displayFilterListing.appendChild(createFilterListingEntry(filter));
}
}
let filterIndicator = document.getElementsByClassName('navigation-table-filter-indicator');
for (let i = filterIndicator.length - 1; i >= 0; i--) {
filterIndicator[i].parentNode.removeChild(filterIndicator[i]);
}
let columnsWithIndicator = [];
for (let i = 0; i < navigationFilters.length; i++) {
filterIndicator = document.createElement('span');
filterIndicator.innerHTML = getSvg('filter', 16, 'var(--strong-yellow)');
filterIndicator.classList.add('navigation-table-filter-indicator');
filterIndicator.style.marginLeft = '3px';
let filter = navigationFilters[i];
let columnIndex = findNavigationTableColumnIndex(filter.column);
if (columnIndex !== -1 && columnsWithIndicator.indexOf(columnIndex) === -1) {
let tableElement = document.getElementById('header-table-id-' + columnIndex);
tableElement.appendChild(filterIndicator);
columnsWithIndicator.push(columnIndex);
}
}
}
function createFilterListingEntry(filter) {
let container = document.createElement('span');
let remove = document.createElement('span');
remove.classList.add('badge');
remove.classList.add('badge-danger');
remove.innerHTML = getSvg('trash3', 10);
remove.style.cursor = 'pointer';
remove.onclick = function () {
removeFilterByUID(filter.uid);
}
let filterElement = document.createElement('span');
filterElement.style.marginLeft = '4px';
let selectColumnElement = document.createElement('select');
selectColumnElement.classList.add('select-input-field');
let columns = getAllColumnsNames();
columns.push('sheet content');
for (let i = 0; i < columns.length; i++) {
let option = document.createElement('option');
option.value = columns[i];
option.innerText = capitalizeWords(columns[i]);
selectColumnElement.appendChild(option);
if (compareNormalizedString(filter.column, columns[i])) {
selectColumnElement.value = columns[i];
}
}
selectColumnElement.onchange = function () {
filter.column = selectColumnElement.value;
updateFiltersInSettings();
applyFilters();
}
filterElement.appendChild(selectColumnElement);
let selectOperationElement = document.createElement('select');
selectOperationElement.classList.add('select-input-field');
let operations = FILTER_OPERATIONS;
for (let i = 0; i < operations.length; i++) {
let option = document.createElement('option');
option.value = operations[i];
option.innerText = capitalizeWords(operations[i]);
selectOperationElement.appendChild(option);
if (filter.operation === operations[i]) {
selectOperationElement.value = operations[i];
}
}
selectOperationElement.onchange = function () {
filter.operation = selectOperationElement.value;
updateFiltersInSettings();
applyFilters();
}
filterElement.appendChild(selectOperationElement);
let inputValueElement = document.createElement('input');
inputValueElement.classList.add('text-input-field');
inputValueElement.type = 'text';
inputValueElement.value = filter.value;
inputValueElement.onchange = function () {
filter.value = inputValueElement.value;
updateFiltersInSettings();
applyFilters();
}
filterElement.appendChild(inputValueElement);
container.appendChild(remove);
container.appendChild(filterElement);
container.appendChild(document.createElement('br'));
return container;
}
function addFilter(column, operation, value) {
if (column === undefined || column === null || column === '') column = getAllColumnsNames()[0];
if (operation === undefined || operation === null || operation === '') operation = FILTER_OPERATIONS[0];
if (value === undefined || value === null) value = '';
let columns = getAllColumnsNames();
for (let i = 0; i < columns.length; i++) {
if (compareNormalizedString(column, columns[i])) {
column = columns[i];
break;
}
}
console.log('Created filter for ' + column);
navigationFilters.push(new Filter(column, operation, value));
updateFiltersInSettings();
applyFilters();
}
function normalizeHeader(header) {
return header
.replaceAll('\n', ' ')
.replaceAll('- ', '')
.replaceAll('-', '')
.toLowerCase();
}
function compareNormalizedString(c1, c2) {
return normalizeHeader(c1) === normalizeHeader(c2);
}
const navigationFilters = [];
let filteredElementCount = sheetsMap.size;
class Filter {
constructor(column, operation, value) {
this.column = column;
this.operation = operation;
this.value = value;
this.uid = Math.floor(Math.random() * 100000);
}
}
Filter.prototype.toString = function () {
let middle;
switch (this.operation) {
case 'not contains':
middle = 'does not contain';
break;
case 'equal':
case 'not equal':
middle = 'is ' + this.operation + ' to';
break;
case 'larger':
case 'smaller':
middle = 'is ' + this.operation + ' than';
break;
case 'larger/equal':
case 'smaller/equal':
middle = 'is ' + this.operation.replace('/', ' or ') + ' to';
break;
default:
middle = this.operation;
}
return this.column + ' ' + middle + ' ' + this.value;
}
function clearAllFilters() {
navigationFilters.length = 0;
updateFiltersInSettings();
applyFilters();
}
function removeFilterByUID(uid) {
for (let i = 0; i < navigationFilters.length; i++) {
if (navigationFilters[i].uid === uid) {
navigationFilters.splice(i, 1);
}
}
updateFiltersInSettings();
applyFilters();
}
function applyFilters() {
filteredElementCount = 0;
let navigationListElements = document.getElementsByClassName('navigation-entry');
for (let i = 0, max = navigationListElements.length; i < max; i++) {
let shouldBeVisible = true;
for (let j = 0; j < navigationFilters.length; j++) {
let filter = navigationFilters[j];
let checkContent = '';
if (compareNormalizedString(filter.column, 'sheet content')) {
checkContent = sheetsMap.get(navigationListElements[i].id.replace('nav-row-', ''));
} else {
let headerIndex = findNavigationTableColumnIndex(filter.column);
if (headerIndex !== -1) {
checkContent = navigationListElements[i].childNodes[headerIndex].innerText;
}
}
checkContent = checkContent.toLowerCase();
let compareValue = filter.value.toLowerCase();
switch (filter.operation) {
case 'contains':
shouldBeVisible &= checkContent.indexOf(compareValue) !== -1;
break;
case 'not contains':
shouldBeVisible &= checkContent.indexOf(compareValue) === -1;
break;
case 'equal':
shouldBeVisible &= checkContent === compareValue;
break;
case 'not equal':
shouldBeVisible &= checkContent !== compareValue;
break;
case 'larger':
shouldBeVisible &= getNumberFromString(checkContent) > getNumberFromString(compareValue);
break;
case 'larger/equal':
shouldBeVisible &= getNumberFromString(checkContent) >= getNumberFromString(compareValue);
break;
case 'smaller/equal':
shouldBeVisible &= getNumberFromString(checkContent) <= getNumberFromString(compareValue);
break;
case 'smaller':
shouldBeVisible &= getNumberFromString(checkContent) < getNumberFromString(compareValue);
break;
default:
break;
}
if (!shouldBeVisible) break;
}
if (shouldBeVisible) {
navigationListElements[i].classList.remove('hidden');
filteredElementCount++;
} else {
navigationListElements[i].classList.add('hidden');
}
}
updateURL();
try {
onFilterApplied();
} catch (e) {
}
}
function getNumberFromString(str) {
let extracted = str.replace(/(?:[\s\S]*?)(-?\d+\.?\d*)(?:[\s\S]*)/gi, '$1');
if (extracted.length > 0) {
return parseFloat(extracted);
}
return NaN;
}
function findNavigationTableColumnIndex(columnName) {
let navigationHeaders = document.getElementsByClassName('navigation-header-tile');
columnName = columnName.replaceAll('-', '').replaceAll('\n', '').replaceAll(' ', '').toLowerCase();
for (let k = 0; k < navigationHeaders.length; k++) {
let compareHeader = navigationHeaders[k].innerText.replaceAll('-', '').replaceAll('\n', '').replaceAll(' ', '').toLowerCase();
if (compareHeader === columnName) {
return k;
}
}
return -1;
}
function loadFiltersFromGetValues() {
let filters = findGetParameter('f');
if (filters !== undefined && filters !== null && filters.length > 0) {
createFilterFromGETValue(filters);
}
}
function createFilterFromGETValue(getValue) {
let split = getValue.split(',');
for (let i = 0; i < split.length; i++) {
let filterAttributes = split[i].split(';');
if (filterAttributes.length === 3) {
let column = filterAttributes[0];
let operation = filterAttributes[1];
let value = filterAttributes[2].replace('REP00', ',');
navigationFilters.push(new Filter(column, operation, value));
}
}
updateFiltersInSettings();
applyFilters();
}
/* search and filter over */
function getSvg(id, size, color = 'currentColor') {
switch (id) {
case "pencil":
return '';
case "trash3":
return '';
case "arrow-up":
return '';
case "arrow-down":
return '';
case "filter":
return '';
}
}
function nextVulnerability() {
if (!canUseNavigationBarShortcuts()) return;
let current = getCurrentNavigationElement();
let tableBody = current.parentElement;
let index = Array.prototype.indexOf.call(tableBody.children, current);
for (let i = 0; i < 300; i++) {
index = (index + 1) % tableBody.children.length;
if (!tableBody.children[index].classList.contains('hidden')) {
break;
}
}
eventFire(tableBody.children[index], 'click');
}
function previousVulnerability() {
if (!canUseNavigationBarShortcuts()) return;
let current = getCurrentNavigationElement();
let tableBody = current.parentElement;
let index = Array.prototype.indexOf.call(tableBody.children, current);
for (let i = 0; i < 300; i++) {
index -= 1;
if (index < 0) index = tableBody.children.length - 1;
if (!tableBody.children[index].classList.contains('hidden')) {
break;
}
}
eventFire(tableBody.children[index], 'click');
}
let timeoutForNavigationStart = Date.now();
function canUseNavigationBarShortcuts() {
if (Date.now() - timeoutForNavigationStart > 140) {
timeoutForNavigationStart = Date.now();
return true;
}
return false;
}
function getCurrentNavigationElement() {
return document.getElementById('navigation-tile-' + lastSheet).parentElement.parentElement;
}
function eventFire(el, etype) {
if (el.fireEvent) {
el.fireEvent('on' + etype);
} else {
let evObj = document.createEvent('Events');
evObj.initEvent(etype, true, false);
el.dispatchEvent(evObj);
}
}
document.addEventListener('keyup', (e) => {
if (isAnyModalVisible()) {
if (e.code === 'Escape') {
closeAllModals();
} else if (e.code === 'Enter') {
if (doesUserNotSelectInputElement()) {
closeAllModals();
}
}
}
});
document.addEventListener('keydown', (e) => {
if (doesUserNotSelectInputElement()) {
let actionFound = false;
if (e.altKey && e.code === 'ArrowLeft') {
previousDisplayMode(false);
actionFound = true;
} else if (e.altKey && e.code === 'ArrowRight') {
nextDisplayMode();
actionFound = true;
} else if (e.altKey && e.code === 'ArrowDown') {
nextVulnerability(false);
actionFound = true;
} else if (e.altKey && e.code === 'ArrowUp') {
previousVulnerability();
actionFound = true;
} else if (e.altKey && e.code === 'KeyS') {
openModal('settingsModal');
findOrCreateSheetContentContainsFilter();
actionFound = true;
} else if (e.code === 'KeyS') {
toggleModal('settingsModal');
actionFound = true;
} else if (e.code === 'Backspace') {
historyBackwards();
actionFound = true;
}
if (actionFound) {
e.preventDefault();
}
}
});
function doesUserNotSelectInputElement() {
return document.activeElement === undefined || document.activeElement === null || (document.activeElement.tagName.toLowerCase() !== 'button' && document.activeElement.tagName.toLowerCase() !== 'input' && document.activeElement.tagName.toLowerCase() !== 'textarea');
}
function findOrCreateSheetContentContainsFilter() {
if (!navigationFilters
.filter(filter => filter.column === 'sheet content' && filter.operation === 'contains')
.length > 0) {
addFilter('sheet content', 'contains', '');
}
let filterContainer = document.getElementById('display-filter-listing');
for (let i = 0; i < filterContainer.children.length; i++) {
let filter = filterContainer.children[i];
let filterColumn = filter.getElementsByClassName('select-input-field')[0];
let filterOperation = filter.getElementsByClassName('select-input-field')[1];
let filterValue = filter.getElementsByClassName('text-input-field')[0];
if (filterColumn.value === 'sheet content' && filterOperation.value === 'contains') {
filterValue.focus();
}
}
}
/* saving data-sheet entries */
let bookmarkedDataSheets = [];
function loadDataSheetBookmark() {
let saved = findGetParameter('bkm');
if (saved !== undefined && saved !== null && saved.length > 0) {
let split = saved.split(',');
for (let i = 0; i < split.length; i++) {
bookmarkedDataSheets.push(split[i]);
}
}
updateBookmarkedDataSheets(null);
}
function toggleDataSheetBookmark(sheetID) {
if (!sheetsMap.has(sheetID)) {
return;
}
let index = bookmarkedDataSheets.indexOf(sheetID);
if (index === -1) {
bookmarkedDataSheets.push(sheetID);
} else {
bookmarkedDataSheets.splice(index, 1);
}
updateBookmarkedDataSheets(sheetID);
updateURL();
}
function updateBookmarkedDataSheets(currentSheetID) {
if (currentSheetID !== undefined && currentSheetID !== null) {
document.getElementById("navigation-tile-" + currentSheetID).classList.remove("special-highlighted");
}
for (let i = 0; i < bookmarkedDataSheets.length; i++) {
document.getElementById("navigation-tile-" + bookmarkedDataSheets[i]).classList.add("special-highlighted");
}
}
/* saving data-sheet entries over*/
function getDashboardTitleWidth() {
/* (title width + subtitle width) + margin left + gap */
return document.getElementById('dashboard-full-title').offsetWidth + 30 + 8;
}
function topRightBadgesOverhangToggle() {
let overhangElement = document.getElementById("top-right-badges-overhang");
if (overhangElement) {
overhangElement.classList.toggle('hidden');
}
}
function setTopRightBadgesOverhangVisible(visible) {
if (isMobile()) return;
let overhangElement = document.getElementById("top-right-badges-overhang");
if (overhangElement) {
if (visible && overhangElement.classList.contains('hidden')) {
overhangElement.classList.remove('hidden');
} else if (!visible && !overhangElement.classList.contains('hidden')) {
overhangElement.classList.add('hidden');
}
}
}
function isMobile() {
return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
}
let topRightIconSize = 22;
function adjustTopRightOverflowItems() {
let width = window.innerWidth;
let mainContainer = document.getElementById('top-right-badges');
let overflowContainer = document.getElementById('top-right-badges-overhang');
let storageContainer = document.getElementById('top-right-badges-storage');
if (mainContainer && overflowContainer && storageContainer) {
mainContainer.innerHTML = '';
mainContainer.classList.add('hidden');
overflowContainer.innerHTML = '';
overflowContainer.classList.add('hidden');
let storedItems = storageContainer.childNodes;
let amountItems = storedItems.length - 1;
let elementWidth = 14 + topRightIconSize; /* flex-gap + own width */
let requiredWidth = amountItems * elementWidth + 30 + 20 + elementWidth * 2; /* items * width + container box margin right + container box padding + two at least item widths space */
let availableSpace = width - getDashboardTitleWidth(); /* width - title width */
let overlappingSpace = availableSpace - requiredWidth;
let overlapItems = Math.abs(Math.min(0, Math.ceil(overlappingSpace / elementWidth))); /* figure out how many items overlap the reserved space */
let amountItemsAdded = 0;
let goBackArrowIsInOverflow = false;
for (let i = 0; i < storedItems.length; i++) {
let storedItem = storedItems[i];
if (storedItem.getAttribute('id') === 'top-right-items-show-more-storage') {
continue;
}
if (amountItemsAdded >= overlapItems) {
mainContainer.appendChild(storedItem.cloneNode(true));
} else {
overflowContainer.appendChild(storedItem.cloneNode(true));
if (!goBackArrowIsInOverflow && storedItem.classList.contains('previous-sheet-arrow')) {
goBackArrowIsInOverflow = true;
}
}
amountItemsAdded++;
}
updateGoBackArrow();
if (overflowContainer.childNodes.length > 0 && !(!goBackArrowIsVisible && goBackArrowIsInOverflow && overflowContainer.childNodes.length === 1)) {
let threeDots = document.getElementById('top-right-items-show-more-storage');
if (threeDots) {
threeDots = threeDots.cloneNode(true);
threeDots.setAttribute('id', 'top-right-items-show-more');
mainContainer.appendChild(threeDots);
}
}
mainContainer.classList.remove('hidden');
} else if (mainContainer) {
mainContainer.classList.add('hidden');
}
}
function trimCharacterFromString(s, c) {
return s.replace(new RegExp(
'^[' + c + ']+|[' + c + ']+$', 'g'
), '');
}
function copyToClipboard(text) {
try {
navigator.clipboard.writeText(text);
return true;
} catch (e) {
window.prompt("Copy to clipboard: Ctrl+C, Enter", text);
}
}
/* checks up to three layers upwards whether there is a node that has the tooltip attribute */
function getTooltipAttributeFromParent(e) {
let tooltipText = null;
try {
tooltipText = e.target.getAttribute('tooltip');
if (!tooltipText && e.target.parentNode !== undefined) {
tooltipText = e.target.parentNode.getAttribute('tooltip');
if (!tooltipText && e.target.parentNode.parentNode !== undefined) {
tooltipText = e.target.parentNode.parentNode.getAttribute('tooltip');
if (!tooltipText && e.target.parentNode.parentNode.parentNode !== undefined) {
tooltipText = e.target.parentNode.parentNode.parentNode.getAttribute('tooltip');
}
}
}
} catch (e) {
}
return tooltipText;
}
/* a listener whether the screen size has been adjusted */
window.addEventListener('resize', function (e) {
adjustTopRightOverflowItems();
setContentSheetWidth();
}, true);
function setContentSheetWidth() {
let contentSheets = document.getElementsByClassName("content-sheet");
if (contentSheets.length > 0) {
let contentSheet = contentSheets[0];
let sheetContainerRect = contentSheet.getBoundingClientRect();
let left = sheetContainerRect.left;
let screenWidth = window.innerWidth;
contentSheet.style.width = (screenWidth - left - 31) + "px";
}
}
const TOOLTIP_OFFSET = 7;
/* a global hover listener for all elements */
document.addEventListener('mousemove', function (e) {
handleTooltipHover(e);
if (e.target === undefined || e.target === null || e.target.getAttribute === undefined) {
return;
}
const topRightBadgesOverhang = document.getElementById('top-right-badges-overhang');
/* check if user hovers over the show more icon in the top right menu bar */
if (e.target.getAttribute('id') === 'top-right-items-show-more'
|| (isDomElement(e.target.parentNode) && e.target.parentNode.getAttribute('id') === 'top-right-items-show-more')) {
setTopRightBadgesOverhangVisible(true);
} else if (topRightBadgesOverhang && !topRightBadgesOverhang.classList.contains('hidden')) {
let threeDots = document.getElementById('top-right-items-show-more');
if (threeDots && distanceFromNodeToPosition(threeDots, e.clientX, e.clientY) > 300) {
setTopRightBadgesOverhangVisible(false);
}
}
});
function handleTooltipHover(e) {
let tooltip = document.getElementById('tooltip');
/* do not change the tooltip if the alt key is pressed */
if (tooltip && e.altKey) {
return;
}
/* check if user hovers an element with a tooltip attribute */
let tooltipContent = getTooltipAttributeFromParent(e);
if (tooltipContent) {
let tooltipHasToBeAdded = !tooltip;
if (tooltipHasToBeAdded) {
tooltip = document.createElement('div');
tooltip.setAttribute('id', 'tooltip');
tooltip.style.visibility = 'hidden';
}
tooltip.innerHTML = tooltipContent;
/* respect the bounding box of the tooltip and the screen
give it an offset of 10px */
let tooltipWidth = tooltip.offsetWidth + TOOLTIP_OFFSET;
let tooltipHeight = tooltip.offsetHeight + TOOLTIP_OFFSET;
let tooltipX = e.clientX + TOOLTIP_OFFSET;
let tooltipY = e.clientY + TOOLTIP_OFFSET;
/* if the tooltip would be off the screen, move it to the left */
if (tooltipX + tooltipWidth > window.innerWidth) {
tooltipX = e.clientX - tooltipWidth - TOOLTIP_OFFSET + 3;
}
/* if the tooltip would be off the screen, move it to the top */
if (tooltipY + tooltipHeight > window.innerHeight) {
/* if this would lead to the tooltip being out of the top, do not perform this action
but rather make the tooltip wider */
if (e.clientY - tooltipHeight - TOOLTIP_OFFSET < 0) {
tooltip.style.maxWidth = '900px';
} else {
tooltipY = e.clientY - tooltipHeight - TOOLTIP_OFFSET;
}
}
tooltip.style.left = tooltipX + 'px';
tooltip.style.top = tooltipY + 'px';
if (tooltipHasToBeAdded) {
document.body.appendChild(tooltip);
handleTooltipHover(e);
} else {
tooltip.style.visibility = 'visible';
}
} else if (tooltip) {
tooltip.remove();
}
}
function isDomElement(obj) {
try {
return obj instanceof HTMLElement;
} catch (e) {
return (typeof obj === "object") &&
(obj.nodeType === 1) && (typeof obj.style === "object") &&
(typeof obj.ownerDocument === "object");
}
}
function distanceFromNodeToPosition(node, x, y) {
let viewportOffset = node.getBoundingClientRect();
return Math.sqrt(Math.pow(x - viewportOffset.left, 2) + Math.pow(y - viewportOffset.top, 2));
}
/* observe the width of the navigation table */
function addNavigationResizeListener() {
let navigation = document.getElementById('navigation-table');
let lastWidth = navigation.style.width.replace('px', '');
setInterval(function () {
let newWidth = navigation.style.width.replace('px', '');
if (lastWidth !== newWidth) {
navigationResized(newWidth);
lastWidth = newWidth;
}
}, 500);
}
function navigationResized(newWidth) {
let widthDiff = window.innerWidth - newWidth;
/* Check if the navigation is full screen, change to full screen navigation layout for this */
if (widthDiff < 120 && currentDisplayMode !== 0) {
setDisplayMode(0);
}
setContentSheetWidth();
}
/* DARK MODE START */
let manualDarkModeState = localStorage.getItem('ae-vad-manual-dark-mode'); // null, true, false
let currentDarkModeState = null;
let darkModeSwitchTimeout = false;
if (manualDarkModeState === null) {
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', event => {
const newColorScheme = event.matches ? "dark" : "light";
setDarkMode(newColorScheme === "dark");
});
} else {
setDarkMode(manualDarkModeState === "true" || manualDarkModeState === true);
}
function setDarkMode(darkMode) {
darkModeSwitchTimeout = setTimeout(function () {
console.log("Dark mode:", darkMode);
if (darkMode) {
try {
Chart.defaults.backgroundColor = '#00000000';
Chart.defaults.borderColor = 'rgba(170, 170, 170, 0.2)';
Chart.defaults.color = '#FFF';
Chart.defaults.scale.ticks.backdropColor = "#00000000";
} catch (e) {
}
} else {
try {
Chart.defaults.backgroundColor = '#00000000';
Chart.defaults.borderColor = 'rgba(0, 0, 0, 0.2)';
Chart.defaults.color = '#000';
Chart.defaults.scale.ticks.backdropColor = "rgba(255, 255, 255, 0.75)";
} catch (e) {
}
}
const switchElement = document.getElementById("dark-mode-switch");
if (switchElement) {
let key = darkMode ? 'svgDarkMode' : 'svgLightMode';
switchElement.innerHTML = switchElement.dataset[key];
if (currentDarkModeState === null && darkMode) {
setDarkMode(darkMode);
}
}
if (darkMode) {
document.documentElement.classList.remove("ae-light");
document.documentElement.classList.add("ae-dark");
} else if (!darkMode) {
document.documentElement.classList.remove("ae-dark");
document.documentElement.classList.add("ae-light");
}
if (lastSheet !== null && lastSheet !== "") {
showSheet(lastSheet);
}
currentDarkModeState = darkMode;
try {
onThemeChanged(darkMode);
} catch (e) {
}
}, 100);
}
function toggleManualDarkMode() {
currentDarkModeState = !currentDarkModeState;
manualDarkModeState = currentDarkModeState;
localStorage.setItem('ae-vad-manual-dark-mode', manualDarkModeState);
setDarkMode(manualDarkModeState);
}
/* DARK MODE END */
/* COPY DATA START */
function copyContentSheetNames() {
const navigationListElements = document.getElementsByClassName('navigation-entry');
const names = Array.from(navigationListElements)
.filter(e => !e.classList.contains('hidden'))
.map(e => e.childNodes[0])
.map(e => e.innerText);
const csv = names.join(', ');
copyTextToClipboard(csv);
}
function copyTextToClipboard(text) {
if (navigator.clipboard && window.isSecureContext) {
navigator.clipboard.writeText(text).then(() => {
}).catch(err => {
console.error("Could not copy text: ", err);
fallbackCopyTextToClipboard(text);
});
} else {
fallbackCopyTextToClipboard(text);
}
}
function fallbackCopyTextToClipboard(text) {
prompt('Copy to clipboard: Ctrl+C, Enter', text);
}
/* COPY DATA END */
function checkForIdExistsValidation(id, elementName) {
if (document.getElementById(id) === null) {
console.error(elementName, ' (id: ' + id + ') not found');
}
}
function onLoad() {
console.log('Starting loading process for VAD');
console.log('Running validation checks');
checkForIdExistsValidation('navigation-table', 'navigation table');
checkForIdExistsValidation('dashboard-full-title', 'dashboard title');
checkForIdExistsValidation('top-right-badges', 'top right badges');
checkForIdExistsValidation('top-right-badges-overhang', 'top right badges overhang');
checkForIdExistsValidation('top-right-badges-storage', 'top right badges storage');
try {
if (defaultSheet === undefined) {
console.error('defaultSheet is undefined');
}
} catch (e) {
console.error('defaultSheet is not defined', e);
}
addNavigationResizeListener();
console.log('1. Added navigation resize listener');
try {
adjustTopRightOverflowItems();
console.log('2. Adjusted top right overflow items');
} catch (e) {
console.error('2. Failed to adjust top right overflow items', e);
}
setContentSheetWidth();
console.log('3. Set content sheet width');
loadFiltersFromGetValues();
console.log('4. Loaded filters from get values');
loadDataSheetBookmark();
console.log('5. Loaded data sheet bookmark');
applyFilters();
console.log('6. Applied filters');
try {
if (manualDarkModeState == null) {
if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
setDarkMode(true);
} else {
setDarkMode(false);
}
}
console.log('7. Set dark mode');
} catch (e) {
console.error('7. Failed to set dark mode', e);
}
console.log('Finished loading process for VAD');
}
window.onload = onLoad;
© 2015 - 2025 Weber Informatics LLC | Privacy Policy