UNPKG

reldens

Version:
614 lines (584 loc) 27.4 kB
/** * * Reldens - Admin Client JS * */ let trustedTypesPolicy = null; if(window.trustedTypes && window.trustedTypes.createPolicy){ trustedTypesPolicy = window.trustedTypes.createPolicy('default', { createHTML: (s) => s, createScriptURL: (s) => s }); } window.trustedTypesPolicy = trustedTypesPolicy; window.addEventListener('DOMContentLoaded', () => { // helpers: let location = window.location; let currentPath = location.pathname; let queryString = location.search; let urlParams = new URLSearchParams(queryString); // error codes messages map: let errorMessages = { saveBadPatchData: 'Bad patch data on update.', saveEntityStorageError: 'Entity storage error.', saveEntityError: 'Entity could not be saved.', shutdownError: 'Server could not be shutdown, missing "shutdownTime".', errorView: 'Could not render view page.', errorEdit: 'Could not render edit page.', errorId: 'Missing entity ID on POST.', // Reldens custom messages: mapsWizardImportDataError: 'Map could not be imported, missing generated map data.', mapsWizardImportError: 'Map could not be imported.', objectsImportMissingDataError: 'Object could not be imported, missing JSON files.', objectsImportDataError: 'Object could not be imported, missing data in JSON files.', objectsImportError: 'Object could not be imported.', skillsImportMissingDataError: 'Skills could not be imported, missing JSON files.', skillsImportDataError: 'Skills could not be imported, missing data in JSON files.', skillsImportError: 'Skills could not be imported.', errorMissingTileIndex: 'Missing tile index to create change point.', errorMissingNextRoom: 'Missing next room selection.', errorMissingRoomX: 'Missing return point X.', errorMissingRoomY: 'Missing return point Y.', errorSaveChangePoint: 'Error saving change point.', errorSaveReturnPoint: 'Error saving return point.', themeManagerMissingTheme: 'Please select a theme.', themeManagerMissingCommand: 'Please select a command.', themeManagerExecutionError: 'Theme command execution failed.', }; activateExpandCollapse(); activateModalElements(); // login errors: if('true' === urlParams.get('login-error')){ let loginErrorBox = document.querySelector('form.login-form .response-error'); if(loginErrorBox){ loginErrorBox.innerHTML = 'Login error, please try again.'; } } // entity search functionality: let entityFilterTerm = document.querySelector('#entityFilterTerm'); let filterForm = document.querySelector('#filter-form'); let allFilters = document.querySelectorAll('.filters-toggle-content .filter input'); if(entityFilterTerm && filterForm){ entityFilterTerm.addEventListener('input', () => { if(entityFilterTerm.value){ for(let filterInput of allFilters){ filterInput.value = ''; } } }); entityFilterTerm.addEventListener('keypress', (event) => { if(13 === event.keyCode){ event.preventDefault(); filterForm.submit(); } }); for(let filterInput of allFilters){ filterInput.addEventListener('input', () => { if(filterInput.value){ entityFilterTerm.value = ''; } }); } filterForm.addEventListener('submit', () => { if(entityFilterTerm.value && allFilters.some(input => input.value)){ for(let filterInput of allFilters){ filterInput.value = ''; } } }); } // forms behavior: let forms = document.querySelectorAll('form'); if(forms){ for(let form of forms){ form.addEventListener('submit', (event) => { let submitButton = form.querySelector('input[type="submit"], button[type="submit"]'); submitButton.disabled = true; let loadingImage = form.querySelector('.loading'); if(form.classList.contains('form-delete') || form.classList.contains('confirmation-required')){ event.preventDefault(); showConfirmDialog((confirmed) => { if(confirmed){ if(loadingImage){ loadingImage.classList.remove('hidden'); } form.submit(); } if(!confirmed){ submitButton.disabled = false; } }); return; } if(loadingImage){ loadingImage.classList.remove('hidden'); } }); } } // sidebar headers click behavior: let sideBarHeaders = document.querySelectorAll('.with-sub-items h3'); if(sideBarHeaders){ for(let header of sideBarHeaders){ header.addEventListener('click', (event) => { event.currentTarget.parentNode.classList.toggle('active'); }); } } // expand menu on load: let subItemContainers = document.querySelectorAll('.with-sub-items'); if(subItemContainers){ let done = false; for(let container of subItemContainers){ let links = container.querySelectorAll('.side-bar-item a'); for(let link of links){ let linkWithoutHost = link.href.replace(location.host, '').replace(location.protocol+'//', ''); if(currentPath === linkWithoutHost || 0 === currentPath.indexOf(linkWithoutHost+'/')){ link.parentNode.classList.add('active'); container.classList.add('active'); done = true; break; } } if(done){ break; } } } // filters toggle visibility: let filtersToggle = document.querySelector('.filters-toggle'); let filtersToggleContent = document.querySelector('.filters-toggle-content'); if(filtersToggle && filtersToggleContent){ filtersToggle.addEventListener('click', () => { filtersToggle.classList.toggle('active'); filtersToggleContent.classList.toggle('hidden'); }); let allFilters = document.querySelectorAll('.filters-toggle-content .filter input'); let entitySearchInput = document.querySelector('#entityFilterTerm'); let hasEntitySearch = entitySearchInput && '' !== entitySearchInput.value; let activeFilters = Array.from(allFilters).filter(input => '' !== input.value); if(0 < activeFilters.length || hasEntitySearch){ filtersToggleContent.classList.remove('hidden'); } let paginationLinks = document.querySelectorAll('.pagination a'); if(paginationLinks && filterForm){ for(let link of paginationLinks){ link.addEventListener('click', (event) => { event.stopPropagation(); event.preventDefault(); let url = new URL(link.href); let params = new URLSearchParams(url.search); if(entitySearchInput && entitySearchInput.value){ params.set('entityFilterTerm', entitySearchInput.value); } for(let filterInput of allFilters){ if(filterInput.value){ let filterName = filterInput.name; params.set(filterName, filterInput.value); } } let sortedHeader = document.querySelector('th.sorted'); if(sortedHeader){ let columnName = sortedHeader.getAttribute('data-column'); let sortDirection = sortedHeader.classList.contains('sorted-asc') ? 'asc' : 'desc'; params.set('sortBy', columnName); params.set('sortDirection', sortDirection); } window.location.href = url.pathname+'?'+params; return false; }); } } } // column sorting functionality: let sortableHeaders = document.querySelectorAll('th.sortable'); if(sortableHeaders){ for(let header of sortableHeaders){ header.addEventListener('click', () => { let sortForm = header.querySelector('.sort-form'); if(!sortForm){ return; } let columnName = header.getAttribute('data-column'); let currentSortDirection = header.classList.contains('sorted-asc') ? 'asc' : header.classList.contains('sorted-desc') ? 'desc' : ''; let newSortDirection = 'asc'; if('asc' === currentSortDirection){ newSortDirection = 'desc'; } let sortByInput = sortForm.querySelector('input[name="sortBy"]'); let sortDirectionInput = sortForm.querySelector('input[name="sortDirection"]'); sortByInput.value = columnName; sortDirectionInput.value = newSortDirection; let entitySearchInput = document.querySelector('#entityFilterTerm'); let entityFilterTermInput = sortForm.querySelector('input[name="entityFilterTerm"]'); if(entityFilterTermInput){ entityFilterTermInput.value = entitySearchInput?.value || ''; } let allFilters = document.querySelectorAll('.filters-toggle-content .filter input'); for(let filterInput of allFilters){ let filterName = filterInput.name.replace(/^filters\[/, '').replace(/\]$/, ''); let sortFormFilterInput = sortForm.querySelector('input[data-filter-key="'+filterName+'"]'); if(sortFormFilterInput){ sortFormFilterInput.value = filterInput.value; } } sortForm.submit(); }); } } // list "select all" option: let listSelect = document.querySelector('.list-select'); if(listSelect){ listSelect.addEventListener('click', (event) => { let checkboxes = document.querySelectorAll('.ids-checkbox'); for(let checkbox of checkboxes){ checkbox.checked = 1 === Number(event.currentTarget.dataset.checked); } event.currentTarget.dataset.checked = 1 === Number(event.currentTarget.dataset.checked) ? 0 : 1; }); } // list delete selection: let listDeleteSelection = document.querySelector('.list-delete-selection'); let deleteSelectionForm = document.getElementById('delete-selection-form'); let hiddenInput = document.querySelector('.hidden-ids-input'); if(listDeleteSelection && deleteSelectionForm && hiddenInput){ listDeleteSelection.addEventListener('click', (event) => { event.preventDefault(); showConfirmDialog((confirmed) => { if(confirmed){ let checkboxes = document.querySelectorAll('.ids-checkbox'); let ids = []; for(let checkbox of checkboxes){ if(checkbox.checked){ ids.push(checkbox.value); } } if(0 === ids.length){ return; } deleteSelectionForm.innerHTML = ''; for(let id of ids){ let input = document.createElement('input'); input.type = 'hidden'; input.name = 'ids[]'; input.value = id; deleteSelectionForm.appendChild(input); } deleteSelectionForm.submit(); } }); }); } // display notifications from query params: let notificationElement = document.querySelector('.notification'); if(notificationElement){ let closeNotificationElement = document.querySelector('.notification .close'); closeNotificationElement?.addEventListener('click', () => { notificationElement.classList.remove('success', 'error'); }); let queryParams = new URLSearchParams(location.search); let result = queryParams.get('result'); if(!result){ result = getCookie('result'); } let notificationMessageElement = document.querySelector('.notification .message'); if(result && notificationMessageElement){ let notificationClass = 'success' === result ? 'success' : 'error'; notificationMessageElement.innerHTML = ''; notificationElement.classList.add(notificationClass); notificationMessageElement.innerHTML = 'success' === result ? 'Success!' : 'There was an error: '+escapeHTML(errorMessages[result] || result); deleteCookie('result'); queryParams.delete('result'); let newUrl = location.pathname + (queryParams.toString() ? '?' + queryParams.toString() : ''); window.history.replaceState({}, '', newUrl); } } // shutdown timer: let shuttingDownTimeElement = document.querySelector('.shutting-down .shutting-down-time'); if(shuttingDownTimeElement){ let shuttingDownTime = shuttingDownTimeElement.getAttribute('data-shutting-down-time'); if(shuttingDownTime){ shuttingDownTimeElement.innerHTML = escapeHTML(String(shuttingDownTime))+'s'; shuttingDownTime = Number(shuttingDownTime); let shuttingDownTimer = setInterval( () => { shuttingDownTimeElement.innerHTML = escapeHTML(String(shuttingDownTime))+'s'; shuttingDownTime--; if(0 === Number(shuttingDownTime)){ clearInterval(shuttingDownTimer); } }, 1000 ); } } // cache clear all functionality: let cacheClearAllButton = document.querySelector('.cache-clear-all-button'); let cacheClearForm = document.querySelector('.cache-clear-form'); if(cacheClearAllButton){ cacheClearAllButton.addEventListener('click', () => { showConfirmDialog((confirmed) => { if(confirmed && cacheClearForm){ let submitButton = cacheClearForm.querySelector('button[type="submit"]'); if(submitButton){ submitButton.disabled = true; } cacheClearForm.submit(); } }); }); } // remove upload button functionality: let removeUploadButtons = document.querySelectorAll('.remove-upload-btn'); if(removeUploadButtons){ for(let button of removeUploadButtons){ button.addEventListener('click', (event) => { event.preventDefault(); let fieldName = button.getAttribute('data-field'); let fileName = button.getAttribute('data-filename'); let fileInput = document.getElementById(fieldName); let form = fileInput?.closest('form'); if(!fileInput){ return; } if(!form){ return; } let currentFileDisplay = button.closest('.upload-current-file'); let container = button.closest('.upload-files-container'); let isRequired = container && 'true' === container.dataset.required; if(isRequired){ let remainingFiles = container.querySelectorAll('.upload-current-file'); if(2 === remainingFiles.length){ let allRemoveButtons = container.querySelectorAll('.remove-upload-btn'); for(let removeBtn of allRemoveButtons){ removeBtn.remove(); } } } if(currentFileDisplay){ currentFileDisplay.remove(); } if(fileName){ let hiddenFieldName = 'removed_'+fieldName; let existingHiddenInput = form.querySelector('input[name="'+hiddenFieldName+'"]'); if(existingHiddenInput){ let currentValue = existingHiddenInput.value; let filesArray = currentValue ? currentValue.split(',') : []; if(-1 === filesArray.indexOf(fileName)){ filesArray.push(fileName); } existingHiddenInput.value = filesArray.join(','); return; } let hiddenInput = document.createElement('input'); hiddenInput.type = 'hidden'; hiddenInput.name = hiddenFieldName; hiddenInput.value = fileName; form.appendChild(hiddenInput); return; } fileInput.value = ''; let clearFieldName = 'clear_'+fieldName; let existingClearInput = form.querySelector('input[name="'+clearFieldName+'"]'); if(existingClearInput){ return; } let clearInput = document.createElement('input'); clearInput.type = 'hidden'; clearInput.name = clearFieldName; clearInput.value = '1'; form.appendChild(clearInput); }); } } // tileset alert icon toggle: let tilesetAlertIcons = document.querySelectorAll('.tileset-alert-icon'); if(tilesetAlertIcons){ for(let icon of tilesetAlertIcons){ icon.addEventListener('click', () => { let message = icon.nextElementSibling; if(message && message.classList.contains('tileset-info-message')){ message.classList.toggle('hidden'); } }); } } // Reldens custom functions: // create rooms link function: let entityDataElement = document.querySelector('[data-entity-serialized-data]'); let mapLoadElement = document.querySelector('[data-map-loader]'); if(entityDataElement){ let entityData = entityDataElement?.dataset.entitySerializedData ? JSON.parse(entityDataElement.dataset.entitySerializedData) : false; let elementCurrentRoomChangePointTileIndex = document.querySelector('#currentRoomChangePointTileIndex'); let roomsSelector = document.querySelector('.nextRoomSelector'); let elementNextRoomPositionX = document.querySelector('#nextRoomPositionX'); let elementNextRoomPositionY = document.querySelector('#nextRoomPositionY'); let nextRoomMapContainer = document.querySelector('.next-room-return-position-container'); let roomsList = entityData?.extraData?.roomsList; if(roomsList && nextRoomMapContainer && roomsSelector instanceof HTMLSelectElement){ let roomListKey = Object.keys(roomsList); for(let key of roomListKey){ let roomListData = roomsList[key]; let option = document.createElement('option'); option.text = roomListData.name; option.value = roomListData.id; option.dataset.mapFile = roomListData.mapFile; option.dataset.mapImages = roomListData.mapImages; roomsSelector.add(option); } roomsSelector.addEventListener('change', (event) => { let selectedOption = event.target.options[event.target.selectedIndex]; nextRoomMapContainer.innerHTML = ''; elementNextRoomPositionX.value = ''; elementNextRoomPositionY.value = ''; loadAndCreateMap( selectedOption.dataset.mapFile, selectedOption.dataset.mapImages, nextRoomMapContainer, (event, data) => { let tileData = calculateTileData(event, data); elementNextRoomPositionX.value = tileData.positionTileX; elementNextRoomPositionY.value = tileData.positionTileY; } ); }); } if(mapLoadElement){ loadAndCreateMap( entityData.map_filename, entityData.scene_images, mapLoadElement, (event, data) => { let tileData = calculateTileData(event, data); if(elementCurrentRoomChangePointTileIndex){ elementCurrentRoomChangePointTileIndex.value = tileData.tileIndex; } } ); } } // maps wizard functions: let mapsWizardsOptions = document.querySelectorAll('.maps-wizard-form .map-wizard-option.with-state'); for(let option of mapsWizardsOptions){ option.addEventListener('click', (event) => { let wizardOptionsContainer = document.querySelectorAll('.wizard-option-container'); for(let container of wizardOptionsContainer){ container.classList.remove('active'); } event.currentTarget.parentNode.parentNode.classList.add('active'); }); } let mapCanvasElements = document.querySelectorAll('.mapCanvas'); for(let mapCanvas of mapCanvasElements){ if(!mapCanvas.dataset?.mapJson){ continue; } let tileset = new Image(); // for now, we will only handle 1 image cases: tileset.src = mapCanvas.dataset.imageKey; tileset.onload = () => { fetchMapFileAndDraw(mapCanvas.dataset.mapJson, tileset, mapCanvas); }; tileset.onerror = () => { console.error('Error loading tileset image'); }; } // theme manager functionality: let themeSelector = document.querySelector('#selected-theme'); let executeCommandButtons = document.querySelectorAll('.execute-command'); let showCommandInfoButtons = document.querySelectorAll('.show-command-info'); let commandDescriptionsData = document.querySelector('#command-descriptions-data'); let commandDescriptions = {}; if(commandDescriptionsData){ try { commandDescriptions = JSON.parse(commandDescriptionsData.textContent); } catch(error){ console.error('Failed to parse command descriptions', error); } } if(executeCommandButtons){ for(let button of executeCommandButtons){ button.addEventListener('click', (event) => { event.preventDefault(); if(!themeSelector || !themeSelector.value){ return; } let commandName = button.dataset.command; if(!commandName){ return; } let form = button.closest('form'); if(!form){ return; } let formThemeInput = form.querySelector('input[name="selected-theme"]'); let formCommandInput = form.querySelector('input[name="command"]'); if(!formThemeInput || !formCommandInput){ return; } formThemeInput.value = themeSelector.value; formCommandInput.value = commandName; let commandItem = button.closest('.command-item'); let loadingImage = commandItem ? commandItem.querySelector('.command-loading') : null; showConfirmDialog((confirmed) => { if(confirmed){ if(loadingImage){ loadingImage.classList.remove('hidden'); } form.submit(); } }); }); } } if(showCommandInfoButtons){ let allTooltips = document.querySelectorAll('.command-info-tooltip'); for(let button of showCommandInfoButtons){ let commandName = button.dataset.command; if(!commandName){ continue; } let commandItem = button.closest('.command-item'); if(!commandItem){ continue; } let tooltip = commandItem.querySelector('.command-info-tooltip[data-command="'+commandName+'"]'); if(!tooltip){ continue; } let commandInfo = commandDescriptions[commandName]; if(commandInfo){ let descriptionElement = tooltip.querySelector('.tooltip-description'); let detailsElement = tooltip.querySelector('.tooltip-details'); if(descriptionElement){ descriptionElement.textContent = commandInfo.description || ''; } if(detailsElement){ detailsElement.textContent = commandInfo.details || ''; } } button.addEventListener('click', (event) => { event.preventDefault(); event.stopPropagation(); for(let otherTooltip of allTooltips){ if(otherTooltip !== tooltip){ otherTooltip.classList.remove('visible'); } } tooltip.classList.toggle('visible'); }); } document.addEventListener('click', () => { for(let tooltip of allTooltips){ tooltip.classList.remove('visible'); } }); } });