2023-02-03 21:26:35 +01:00
|
|
|
<head th:fragment="head">
|
2023-02-07 21:14:03 +01:00
|
|
|
|
2023-02-11 15:27:15 +01:00
|
|
|
<!-- Metadata -->
|
|
|
|
<meta charset="UTF-8">
|
2023-03-25 23:16:26 +01:00
|
|
|
|
2023-04-01 22:02:54 +02:00
|
|
|
<title th:text="${@appName} + (${title} != null and ${title} != '' ? ' - ' + ${title} : '')"></title>
|
2023-02-11 15:27:15 +01:00
|
|
|
<link rel="shortcut icon" href="favicon.svg">
|
|
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
|
|
|
|
|
|
<!-- jQuery -->
|
|
|
|
<script src="js/jquery.min.js"></script>
|
|
|
|
|
2023-03-20 22:55:11 +01:00
|
|
|
<!-- jQuery -->
|
|
|
|
<script src="js/jszip.min.js"></script>
|
|
|
|
|
2023-02-11 15:27:15 +01:00
|
|
|
<!-- Bootstrap -->
|
|
|
|
<script src="js/popper.min.js"></script>
|
|
|
|
<script src="js/bootstrap.min.js"></script>
|
|
|
|
<link rel="stylesheet" href="css/bootstrap.min.css">
|
|
|
|
<link rel="stylesheet" href="css/bootstrap-icons.css">
|
|
|
|
|
|
|
|
<!-- PDF.js -->
|
2023-03-20 22:55:11 +01:00
|
|
|
<script src="pdfjs/pdf.js"></script>
|
2023-02-11 15:27:15 +01:00
|
|
|
|
2023-04-18 04:08:38 +02:00
|
|
|
<!-- PDF-Lib -->
|
2023-04-20 18:34:58 +02:00
|
|
|
<script src="js/pdf-lib.min.js"></script>
|
2023-04-18 04:08:38 +02:00
|
|
|
|
2023-02-11 15:27:15 +01:00
|
|
|
<!-- Custom -->
|
|
|
|
<link rel="stylesheet" href="css/general.css">
|
|
|
|
<link rel="stylesheet" th:href="@{css/dark-mode.css}" id="dark-mode-styles">
|
|
|
|
|
|
|
|
<script>
|
2023-03-20 22:55:11 +01:00
|
|
|
function toggleDarkMode() {
|
|
|
|
var darkModeStyles = document.getElementById("dark-mode-styles");
|
2023-04-21 18:50:59 +02:00
|
|
|
var darkModeIcon = document.getElementById("dark-mode-icon");
|
|
|
|
|
|
|
|
if (localStorage.getItem("dark-mode") == "on") {
|
2023-03-20 22:55:11 +01:00
|
|
|
localStorage.setItem("dark-mode", "off");
|
|
|
|
darkModeStyles.disabled = true;
|
2023-04-21 18:50:59 +02:00
|
|
|
darkModeIcon.src = "sun.svg";
|
|
|
|
} else {
|
|
|
|
localStorage.setItem("dark-mode", "on");
|
|
|
|
darkModeStyles.disabled = false;
|
|
|
|
darkModeIcon.src = "moon.svg";
|
2023-03-20 22:55:11 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-04-21 18:50:59 +02:00
|
|
|
document.addEventListener("DOMContentLoaded", function () {
|
2023-03-20 22:55:11 +01:00
|
|
|
var darkModeStyles = document.getElementById("dark-mode-styles");
|
2023-04-21 18:50:59 +02:00
|
|
|
var darkModeIcon = document.getElementById("dark-mode-icon");
|
2023-03-20 22:55:11 +01:00
|
|
|
|
|
|
|
// Check if the user has already set a preference
|
|
|
|
if (localStorage.getItem("dark-mode") == "on") {
|
|
|
|
darkModeStyles.disabled = false;
|
2023-04-21 18:50:59 +02:00
|
|
|
darkModeIcon.src = "moon.svg";
|
2023-03-20 22:55:11 +01:00
|
|
|
} else if (localStorage.getItem("dark-mode") == "off") {
|
|
|
|
darkModeStyles.disabled = true;
|
2023-04-21 18:50:59 +02:00
|
|
|
darkModeIcon.src = "sun.svg";
|
2023-03-20 22:55:11 +01:00
|
|
|
} else {
|
|
|
|
// Check the OS's default dark mode setting
|
2023-04-21 18:50:59 +02:00
|
|
|
if (window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches) {
|
2023-03-20 22:55:11 +01:00
|
|
|
darkModeStyles.disabled = false;
|
2023-04-21 18:50:59 +02:00
|
|
|
darkModeIcon.src = "moon.svg";
|
2023-03-20 22:55:11 +01:00
|
|
|
} else {
|
|
|
|
darkModeStyles.disabled = true;
|
2023-04-21 18:50:59 +02:00
|
|
|
darkModeIcon.src = "sun.svg";
|
2023-03-20 22:55:11 +01:00
|
|
|
}
|
|
|
|
}
|
2023-04-21 18:50:59 +02:00
|
|
|
|
|
|
|
// Attach the toggleDarkMode function to the click event of the dark mode toggle
|
|
|
|
document.getElementById("dark-mode-toggle").addEventListener("click", function (event) {
|
|
|
|
event.preventDefault();
|
|
|
|
toggleDarkMode();
|
|
|
|
});
|
2023-03-20 22:55:11 +01:00
|
|
|
});
|
|
|
|
|
2023-02-07 21:14:03 +01:00
|
|
|
</script>
|
2023-02-03 21:26:35 +01:00
|
|
|
</head>
|
2023-03-26 00:05:30 +01:00
|
|
|
<th:block th:fragment="fileSelector(name, multiple)" th:with="accept=${accept} ?: '*/*', inputText=${inputText} ?: #{pdfPrompt}">
|
2023-03-20 22:55:11 +01:00
|
|
|
<div class="custom-file-chooser">
|
|
|
|
<div class="custom-file">
|
2023-03-25 23:16:26 +01:00
|
|
|
<input type="file" class="custom-file-input" th:name="${name}" th:id="${name}+'-input'" th:accept="${accept}" multiple>
|
2023-03-26 00:05:30 +01:00
|
|
|
<label class="custom-file-label" th:for="${name}+'-input'" th:text="${inputText}"></label>
|
2023-03-20 22:55:11 +01:00
|
|
|
</div>
|
|
|
|
<div class="selected-files"></div>
|
|
|
|
</div>
|
|
|
|
<br>
|
|
|
|
<div id="progressBarContainer" style="display: none; position: relative;">
|
|
|
|
<div class="progress" style="height: 1rem;">
|
|
|
|
<div id="progressBar" class="progress-bar progress-bar-striped progress-bar-animated bg-success" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" style="width: 0%;">
|
|
|
|
<span class="sr-only">Loading...</span>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
2023-02-03 21:26:35 +01:00
|
|
|
|
2023-03-20 22:55:11 +01:00
|
|
|
<script>
|
|
|
|
|
|
|
|
$('form').submit(function(event) {
|
2023-04-02 12:51:07 +02:00
|
|
|
var processing = "Processing..."
|
|
|
|
var submitButtonText = $('#submitBtn').text()
|
|
|
|
|
|
|
|
$('#submitBtn').text('Processing...');
|
2023-03-20 22:55:11 +01:00
|
|
|
console.log("start download code")
|
|
|
|
var files = $('#fileInput-input')[0].files;
|
|
|
|
var url = this.action;
|
|
|
|
console.log(url)
|
|
|
|
event.preventDefault(); // Prevent the default form handling behavior
|
|
|
|
/* Check if ${multiple} is false */
|
|
|
|
var multiple = [[${multiple}]] || false;
|
2023-03-25 23:16:26 +01:00
|
|
|
var override = $('#override').val() || '';
|
|
|
|
console.log("override=" + override)
|
|
|
|
if (override === 'multi' || (!multiple && files.length > 1) && override !== 'single' ) {
|
2023-03-20 22:55:11 +01:00
|
|
|
console.log("multi parallel download")
|
|
|
|
submitMultiPdfForm(event,url);
|
|
|
|
} else {
|
|
|
|
console.log("start single download")
|
2023-02-03 21:26:35 +01:00
|
|
|
|
2023-03-20 22:55:11 +01:00
|
|
|
// Get the selected download option from localStorage
|
|
|
|
const downloadOption = localStorage.getItem('downloadOption');
|
2023-02-03 21:26:35 +01:00
|
|
|
|
2023-03-20 22:55:11 +01:00
|
|
|
var formData = new FormData($('form')[0]);
|
|
|
|
|
|
|
|
// Send the request to the server using the fetch() API
|
|
|
|
fetch(url, {
|
|
|
|
method: 'POST',
|
|
|
|
body: formData
|
|
|
|
}).then(response => {
|
|
|
|
if (!response) {
|
|
|
|
throw new Error('Received null response for file ' + i);
|
|
|
|
}
|
|
|
|
console.log("load single download")
|
2023-02-03 21:26:35 +01:00
|
|
|
|
2023-03-20 22:55:11 +01:00
|
|
|
|
|
|
|
// Extract the filename from the Content-Disposition header, if present
|
|
|
|
let filename = null;
|
|
|
|
const contentDispositionHeader = response.headers.get('Content-Disposition');
|
|
|
|
console.log(contentDispositionHeader)
|
|
|
|
if (contentDispositionHeader && contentDispositionHeader.indexOf('attachment') !== -1) {
|
|
|
|
filename = contentDispositionHeader.split('filename=')[1].replace(/"/g, '');
|
|
|
|
} else {
|
|
|
|
// If the Content-Disposition header is not present or does not contain the filename, use a default filename
|
|
|
|
filename = 'download';
|
|
|
|
}
|
|
|
|
console.log("filename=" + filename)
|
2023-02-03 21:26:35 +01:00
|
|
|
|
2023-03-20 22:55:11 +01:00
|
|
|
|
|
|
|
const contentType = response.headers.get('Content-Type');
|
|
|
|
console.log("contentType=" + contentType)
|
|
|
|
// Check if the response is a PDF or an image
|
|
|
|
if (contentType.includes('pdf') || contentType.includes('image')) {
|
|
|
|
response.blob().then(blob => {
|
|
|
|
console.log("pdf/image")
|
|
|
|
|
|
|
|
// Perform the appropriate action based on the download option
|
|
|
|
if (downloadOption === 'sameWindow') {
|
|
|
|
console.log("same window")
|
|
|
|
|
|
|
|
// Open the file in the same window
|
|
|
|
window.location.href = URL.createObjectURL(blob);
|
|
|
|
} else if (downloadOption === 'newWindow') {
|
|
|
|
console.log("new window")
|
|
|
|
|
|
|
|
// Open the file in a new window
|
|
|
|
window.open(URL.createObjectURL(blob), '_blank');
|
|
|
|
} else {
|
|
|
|
console.log("else save")
|
|
|
|
|
|
|
|
// Download the file
|
|
|
|
const link = document.createElement('a');
|
|
|
|
link.href = URL.createObjectURL(blob);
|
|
|
|
link.download = filename;
|
|
|
|
link.click();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
} else if (contentType.includes('json')) {
|
|
|
|
// Handle the JSON response
|
|
|
|
response.json().then(data => {
|
|
|
|
// Format the error message
|
|
|
|
const errorMessage = JSON.stringify(data, null, 2);
|
|
|
|
|
|
|
|
// Display the error message in an alert
|
|
|
|
alert(`An error occurred: ${errorMessage}`);
|
|
|
|
});
|
|
|
|
|
|
|
|
} else {
|
|
|
|
response.blob().then(blob => {
|
|
|
|
console.log("else save 2 zip")
|
|
|
|
|
|
|
|
// For ZIP files or other file types, just download the file
|
|
|
|
const link = document.createElement('a');
|
|
|
|
link.href = URL.createObjectURL(blob);
|
|
|
|
link.download = filename;
|
|
|
|
link.click();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.catch(error => {
|
|
|
|
console.log("error listener")
|
2023-02-03 21:26:35 +01:00
|
|
|
|
2023-03-20 22:55:11 +01:00
|
|
|
// Extract the error message and stack trace from the response
|
|
|
|
const errorMessage = error.message;
|
|
|
|
const stackTrace = error.stack;
|
2023-02-03 21:26:35 +01:00
|
|
|
|
2023-03-20 22:55:11 +01:00
|
|
|
// Create an error message to display to the user
|
|
|
|
const message = `${errorMessage}\n\n${stackTrace}`;
|
2023-04-02 12:51:07 +02:00
|
|
|
|
|
|
|
$('#submitBtn').text(submitButtonText);
|
|
|
|
|
2023-03-20 22:55:11 +01:00
|
|
|
// Display the error message to the user
|
|
|
|
alert(message);
|
2023-04-02 12:51:07 +02:00
|
|
|
|
2023-03-20 22:55:11 +01:00
|
|
|
});
|
2023-04-02 12:51:07 +02:00
|
|
|
|
2023-03-20 22:55:11 +01:00
|
|
|
}
|
2023-04-02 12:51:07 +02:00
|
|
|
$('#submitBtn').text(submitButtonText);
|
2023-03-20 22:55:11 +01:00
|
|
|
});
|
2023-02-03 21:26:35 +01:00
|
|
|
|
2023-04-01 22:02:54 +02:00
|
|
|
async function submitMultiPdfForm(event, url) {
|
2023-03-20 22:55:11 +01:00
|
|
|
// Get the selected PDF files
|
2023-04-01 22:02:54 +02:00
|
|
|
let files = $('#fileInput-input')[0].files;
|
2023-02-05 00:45:33 +01:00
|
|
|
|
2023-03-20 22:55:11 +01:00
|
|
|
// Get the existing form data
|
2023-04-01 22:02:54 +02:00
|
|
|
let formData = new FormData($('form')[0]);
|
2023-03-20 22:55:11 +01:00
|
|
|
formData.delete('fileInput');
|
2023-04-01 22:02:54 +02:00
|
|
|
|
2023-03-20 22:55:11 +01:00
|
|
|
// Show the progress bar
|
|
|
|
$('#progressBarContainer').show();
|
|
|
|
|
|
|
|
// Initialize the progress bar
|
2023-04-01 22:02:54 +02:00
|
|
|
let progressBar = $('#progressBar');
|
2023-03-20 22:55:11 +01:00
|
|
|
progressBar.css('width', '0%');
|
|
|
|
progressBar.attr('aria-valuenow', 0);
|
|
|
|
progressBar.attr('aria-valuemax', files.length);
|
2023-04-01 22:02:54 +02:00
|
|
|
|
|
|
|
// Check the flag in localStorage, default to 4
|
2023-03-25 23:16:26 +01:00
|
|
|
const zipThreshold = parseInt(localStorage.getItem('zipThreshold'), 10) || 4;
|
|
|
|
const zipFiles = files.length > zipThreshold;
|
2023-03-20 22:55:11 +01:00
|
|
|
|
|
|
|
// Initialize JSZip instance if needed
|
|
|
|
let jszip = null;
|
|
|
|
if (zipFiles) {
|
2023-04-01 22:02:54 +02:00
|
|
|
jszip = new JSZip();
|
2023-03-20 22:55:11 +01:00
|
|
|
}
|
2023-04-01 22:02:54 +02:00
|
|
|
|
2023-03-20 22:55:11 +01:00
|
|
|
// Submit each PDF file in parallel
|
2023-04-01 22:02:54 +02:00
|
|
|
let promises = [];
|
|
|
|
for (let i = 0; i < files.length; i++) {
|
|
|
|
let promise = new Promise(async function(resolve, reject) {
|
|
|
|
let fileFormData = new FormData();
|
2023-03-20 22:55:11 +01:00
|
|
|
fileFormData.append('fileInput', files[i]);
|
2023-04-01 22:02:54 +02:00
|
|
|
for (let pair of formData.entries()) {
|
2023-03-20 22:55:11 +01:00
|
|
|
fileFormData.append(pair[0], pair[1]);
|
|
|
|
}
|
|
|
|
console.log(fileFormData);
|
2023-02-11 15:27:15 +01:00
|
|
|
|
2023-04-01 22:02:54 +02:00
|
|
|
try {
|
|
|
|
let response = await fetch(url, {
|
|
|
|
method: 'POST',
|
|
|
|
body: fileFormData
|
|
|
|
});
|
|
|
|
|
2023-03-20 22:55:11 +01:00
|
|
|
if (!response) {
|
|
|
|
throw new Error('Received null response for file ' + i);
|
|
|
|
}
|
2023-04-01 22:02:54 +02:00
|
|
|
|
|
|
|
if (!response.ok) {
|
|
|
|
throw new Error(`Error submitting request for file ${i}: ${response.status} ${response.statusText}`);
|
|
|
|
}
|
|
|
|
|
|
|
|
let contentDisposition = response.headers.get('content-disposition');
|
|
|
|
let fileName = "file.pdf"
|
|
|
|
if (!contentDisposition) {
|
|
|
|
//throw new Error('Content-Disposition header not found for file ' + i);
|
|
|
|
} else {
|
|
|
|
fileName = contentDisposition.split('filename=')[1].replace(/"/g, '');
|
|
|
|
}
|
2023-03-20 22:55:11 +01:00
|
|
|
console.log('Received response for file ' + i + ': ' + response);
|
|
|
|
|
2023-04-01 22:02:54 +02:00
|
|
|
|
|
|
|
|
|
|
|
let blob = await response.blob();
|
|
|
|
if (zipFiles) {
|
|
|
|
// Add the file to the ZIP archive
|
|
|
|
jszip.file(fileName, blob);
|
|
|
|
resolve();
|
|
|
|
} else {
|
|
|
|
// Download the file directly
|
|
|
|
let url = window.URL.createObjectURL(blob);
|
|
|
|
let a = document.createElement('a');
|
|
|
|
a.href = url;
|
|
|
|
a.download = fileName;
|
|
|
|
document.body.appendChild(a);
|
|
|
|
a.click();
|
|
|
|
a.remove();
|
|
|
|
resolve();
|
|
|
|
}
|
|
|
|
} catch (error) {
|
2023-03-20 22:55:11 +01:00
|
|
|
console.error('Error submitting request for file ' + i + ': ' + error);
|
2023-04-01 22:02:54 +02:00
|
|
|
|
2023-03-20 22:55:11 +01:00
|
|
|
// Set default values or fallbacks for error properties
|
2023-04-01 22:02:54 +02:00
|
|
|
let status = error && error.status || 500;
|
|
|
|
let statusText = error && error.statusText || 'Internal Server Error';
|
|
|
|
let message = error && error.message || 'An error occurred while processing your request.';
|
|
|
|
|
|
|
|
// Reject the Promise to signal that the request has failed
|
2023-03-20 22:55:11 +01:00
|
|
|
reject();
|
2023-04-01 22:02:54 +02:00
|
|
|
// Redirect to error page with Spring Boot error parameters
|
|
|
|
let url = '/error?status=' + status + '&error=' + encodeURIComponent(statusText) + '&message=' + encodeURIComponent(message);
|
2023-03-20 22:55:11 +01:00
|
|
|
window.location.href = url;
|
2023-04-01 22:02:54 +02:00
|
|
|
}
|
2023-03-20 22:55:11 +01:00
|
|
|
});
|
2023-04-01 22:02:54 +02:00
|
|
|
|
2023-03-20 22:55:11 +01:00
|
|
|
// Update the progress bar as each request finishes
|
|
|
|
promise.then(function() {
|
2023-04-01 22:02:54 +02:00
|
|
|
updateProgressBar(progressBar, files);
|
2023-03-20 22:55:11 +01:00
|
|
|
});
|
2023-04-01 22:02:54 +02:00
|
|
|
|
2023-03-20 22:55:11 +01:00
|
|
|
promises.push(promise);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Wait for all requests to finish
|
|
|
|
try {
|
|
|
|
await Promise.all(promises);
|
|
|
|
} catch (error) {
|
|
|
|
console.error('Error while uploading files: ' + error);
|
|
|
|
}
|
2023-04-01 22:02:54 +02:00
|
|
|
|
2023-03-20 22:55:11 +01:00
|
|
|
// Update the progress bar
|
|
|
|
progressBar.css('width', '100%');
|
|
|
|
progressBar.attr('aria-valuenow', files.length);
|
2023-04-01 22:02:54 +02:00
|
|
|
|
|
|
|
// After all requests are finished, download the ZIP file if needed
|
2023-03-20 22:55:11 +01:00
|
|
|
if (zipFiles) {
|
2023-04-01 22:02:54 +02:00
|
|
|
try {
|
|
|
|
let content = await jszip.generateAsync({ type: "blob" });
|
|
|
|
let url = window.URL.createObjectURL(content);
|
|
|
|
let a = document.createElement('a');
|
|
|
|
a.href = url;
|
|
|
|
a.download = "files.zip";
|
|
|
|
document.body.appendChild(a);
|
|
|
|
a.click();
|
|
|
|
a.remove();
|
|
|
|
} catch (error) {
|
|
|
|
console.error('Error generating ZIP file: ' + error);
|
|
|
|
}
|
2023-03-20 22:55:11 +01:00
|
|
|
}
|
|
|
|
}
|
2023-04-01 22:02:54 +02:00
|
|
|
function updateProgressBar(progressBar, files) {
|
|
|
|
let progress = ((progressBar.attr('aria-valuenow') / files.length) * 100) + (100 / files.length);
|
|
|
|
progressBar.css('width', progress + '%');
|
|
|
|
progressBar.attr('aria-valuenow', parseInt(progressBar.attr('aria-valuenow')) + 1);
|
|
|
|
}
|
|
|
|
|
2023-03-20 22:55:11 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
2023-02-11 15:27:15 +01:00
|
|
|
<script th:inline="javascript">
|
2023-03-20 22:55:11 +01:00
|
|
|
|
|
|
|
document.addEventListener('DOMContentLoaded', function () {
|
|
|
|
const fileInput = document.getElementById('fileInput-input');
|
|
|
|
|
|
|
|
// Prevent default behavior for drag events
|
|
|
|
['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
|
|
|
|
fileInput.addEventListener(eventName, preventDefaults, false);
|
|
|
|
});
|
|
|
|
|
|
|
|
function preventDefaults(e) {
|
|
|
|
e.preventDefault();
|
|
|
|
e.stopPropagation();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add drop event listener
|
|
|
|
fileInput.addEventListener('drop', handleDrop, false);
|
|
|
|
|
|
|
|
function handleDrop(e) {
|
|
|
|
const dt = e.dataTransfer;
|
|
|
|
const files = dt.files;
|
|
|
|
fileInput.files = files;
|
|
|
|
handleFileInputChange(fileInput)
|
|
|
|
}
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
$([[${"#"+name+"-input"}]]).on("change", function() {
|
|
|
|
handleFileInputChange(this);
|
|
|
|
});
|
|
|
|
|
|
|
|
function handleFileInputChange(inputElement) {
|
|
|
|
const files = $(inputElement).get(0).files;
|
|
|
|
const fileNames = Array.from(files).map(f => f.name);
|
|
|
|
const selectedFilesContainer = $(inputElement).siblings(".selected-files");
|
|
|
|
selectedFilesContainer.empty();
|
|
|
|
fileNames.forEach(fileName => {
|
|
|
|
selectedFilesContainer.append("<div>" + fileName + "</div>");
|
|
|
|
});
|
2023-03-25 23:16:26 +01:00
|
|
|
console.log("fileNames.length=" + fileNames.length)
|
2023-03-20 22:55:11 +01:00
|
|
|
if (fileNames.length === 1) {
|
|
|
|
$(inputElement).siblings(".custom-file-label").addClass("selected").html(fileNames[0]);
|
|
|
|
} else if (fileNames.length > 1) {
|
|
|
|
$(inputElement).siblings(".custom-file-label").addClass("selected").html(fileNames.length + " files selected");
|
|
|
|
} else {
|
|
|
|
$(inputElement).siblings(".custom-file-label").addClass("selected").html([[#{pdfPrompt}]]);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2023-02-05 00:45:33 +01:00
|
|
|
</script>
|
2023-02-06 12:26:20 +01:00
|
|
|
|
2023-02-11 15:27:15 +01:00
|
|
|
<style>
|
|
|
|
.custom-file-label {
|
2023-03-20 22:55:11 +01:00
|
|
|
padding-right: 90px;
|
|
|
|
}
|
|
|
|
|
|
|
|
.selected-files {
|
|
|
|
margin-top: 10px;
|
|
|
|
max-height: 150px;
|
|
|
|
overflow-y: auto;
|
|
|
|
white-space: pre-wrap;
|
|
|
|
}
|
|
|
|
|
2023-02-11 15:27:15 +01:00
|
|
|
</style>
|
2023-03-20 22:55:11 +01:00
|
|
|
|
|
|
|
|
2023-02-03 21:26:35 +01:00
|
|
|
</th:block>
|