mirror of
https://github.com/Frooodle/Stirling-PDF.git
synced 2025-09-03 17:52:30 +02:00
footer and header changes
This commit is contained in:
parent
4a2da2b7e0
commit
4d8f4096c3
11
build.gradle
11
build.gradle
@ -21,6 +21,7 @@ dependencies {
|
|||||||
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
|
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
|
||||||
testImplementation 'org.springframework.boot:spring-boot-starter-test'
|
testImplementation 'org.springframework.boot:spring-boot-starter-test'
|
||||||
implementation 'org.apache.pdfbox:pdfbox:2.0.27'
|
implementation 'org.apache.pdfbox:pdfbox:2.0.27'
|
||||||
|
//implementation 'org.apache.pdfbox:preflight:2.0.27'
|
||||||
implementation 'org.apache.logging.log4j:log4j-core:2.20.0'
|
implementation 'org.apache.logging.log4j:log4j-core:2.20.0'
|
||||||
implementation 'e-iceblue:spire.pdf.free:5.1.0'
|
implementation 'e-iceblue:spire.pdf.free:5.1.0'
|
||||||
|
|
||||||
@ -33,6 +34,11 @@ dependencies {
|
|||||||
|
|
||||||
jar {
|
jar {
|
||||||
enabled = false
|
enabled = false
|
||||||
|
manifest {
|
||||||
|
attributes 'Implementation-Title': 'Stirling-PDF',
|
||||||
|
'Implementation-Version': project.version
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.named('test') {
|
tasks.named('test') {
|
||||||
@ -43,3 +49,8 @@ task printVersion {
|
|||||||
println project.version
|
println project.version
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bootRun {
|
||||||
|
systemProperty 'version', project.version
|
||||||
|
systemProperties['version'] = version
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -2,6 +2,9 @@ package stirling.software.SPDF;
|
|||||||
|
|
||||||
import org.springframework.boot.SpringApplication;
|
import org.springframework.boot.SpringApplication;
|
||||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||||
|
|
||||||
|
import stirling.software.SPDF.config.AppConfig;
|
||||||
|
|
||||||
@SpringBootApplication
|
@SpringBootApplication
|
||||||
public class SPdfApplication {
|
public class SPdfApplication {
|
||||||
|
14
src/main/java/stirling/software/SPDF/config/AppConfig.java
Normal file
14
src/main/java/stirling/software/SPDF/config/AppConfig.java
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
package stirling.software.SPDF.config;
|
||||||
|
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
public class AppConfig {
|
||||||
|
@Bean(name = "appVersion")
|
||||||
|
public String appVersion() {
|
||||||
|
String version = getClass().getPackage().getImplementationVersion();
|
||||||
|
return (version != null) ? version : "Develop";
|
||||||
|
}
|
||||||
|
}
|
@ -134,7 +134,7 @@ public class SplitPDFController {
|
|||||||
ByteArrayResource resource = new ByteArrayResource(data);
|
ByteArrayResource resource = new ByteArrayResource(data);
|
||||||
new File("split_documents.zip").delete();
|
new File("split_documents.zip").delete();
|
||||||
// return the Resource in the response
|
// return the Resource in the response
|
||||||
return ResponseEntity.ok().header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=split_documents.zip").contentType(MediaType.APPLICATION_OCTET_STREAM)
|
return ResponseEntity.ok().header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + file.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_split.zip").contentType(MediaType.APPLICATION_OCTET_STREAM)
|
||||||
.contentLength(resource.contentLength()).body(resource);
|
.contentLength(resource.contentLength()).body(resource);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -78,7 +78,7 @@ public class ConvertImgPDFController {
|
|||||||
} else {
|
} else {
|
||||||
ByteArrayResource resource = new ByteArrayResource(result);
|
ByteArrayResource resource = new ByteArrayResource(result);
|
||||||
// return the Resource in the response
|
// return the Resource in the response
|
||||||
return ResponseEntity.ok().header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=converted_documents.zip").contentType(MediaType.APPLICATION_OCTET_STREAM)
|
return ResponseEntity.ok().header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename="+ file.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_convertedToImages.zip").contentType(MediaType.APPLICATION_OCTET_STREAM)
|
||||||
.contentLength(resource.contentLength()).body(resource);
|
.contentLength(resource.contentLength()).body(resource);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
3
src/main/resources/static/images/discord.svg
Normal file
3
src/main/resources/static/images/discord.svg
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="43" height="43" fill="#007bff" class="bi bi-discord" viewBox="0 0 16 16">
|
||||||
|
<path d="M13.545 2.907a13.227 13.227 0 0 0-3.257-1.011.05.05 0 0 0-.052.025c-.141.25-.297.577-.406.833a12.19 12.19 0 0 0-3.658 0 8.258 8.258 0 0 0-.412-.833.051.051 0 0 0-.052-.025c-1.125.194-2.22.534-3.257 1.011a.041.041 0 0 0-.021.018C.356 6.024-.213 9.047.066 12.032c.001.014.01.028.021.037a13.276 13.276 0 0 0 3.995 2.02.05.05 0 0 0 .056-.019c.308-.42.582-.863.818-1.329a.05.05 0 0 0-.01-.059.051.051 0 0 0-.018-.011 8.875 8.875 0 0 1-1.248-.595.05.05 0 0 1-.02-.066.051.051 0 0 1 .015-.019c.084-.063.168-.129.248-.195a.05.05 0 0 1 .051-.007c2.619 1.196 5.454 1.196 8.041 0a.052.052 0 0 1 .053.007c.08.066.164.132.248.195a.051.051 0 0 1-.004.085 8.254 8.254 0 0 1-1.249.594.05.05 0 0 0-.03.03.052.052 0 0 0 .003.041c.24.465.515.909.817 1.329a.05.05 0 0 0 .056.019 13.235 13.235 0 0 0 4.001-2.02.049.049 0 0 0 .021-.037c.334-3.451-.559-6.449-2.366-9.106a.034.034 0 0 0-.02-.019Zm-8.198 7.307c-.789 0-1.438-.724-1.438-1.612 0-.889.637-1.613 1.438-1.613.807 0 1.45.73 1.438 1.613 0 .888-.637 1.612-1.438 1.612Zm5.316 0c-.788 0-1.438-.724-1.438-1.612 0-.889.637-1.613 1.438-1.613.807 0 1.451.73 1.438 1.613 0 .888-.631 1.612-1.438 1.612Z"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.2 KiB |
@ -3,3 +3,4 @@
|
|||||||
<path stroke="#007bff" stroke-width="38" d="M 297.507 242.806 L 339.507 242.806 M 247.507 242.806 L 289.507 242.806 M 198.507 242.806 L 240.507 242.806 M 149.507 242.806 L 190.507 242.806 M 99.507 242.806 L 141.507 242.806 M 149.507 196.806 L 190.507 196.806 M 198.507 196.806 L 240.507 196.806 M 247.507 196.806 L 289.507 196.806 M 247.507 150.806 L 289.507 150.806"/>
|
<path stroke="#007bff" stroke-width="38" d="M 297.507 242.806 L 339.507 242.806 M 247.507 242.806 L 289.507 242.806 M 198.507 242.806 L 240.507 242.806 M 149.507 242.806 L 190.507 242.806 M 99.507 242.806 L 141.507 242.806 M 149.507 196.806 L 190.507 196.806 M 198.507 196.806 L 240.507 196.806 M 247.507 196.806 L 289.507 196.806 M 247.507 150.806 L 289.507 150.806"/>
|
||||||
<path fill="#007bff" d="M 473.507 244.806 C 473.507 244.806 455.507 227.806 418.507 233.806 C 414.507 204.806 383.507 187.806 383.507 187.806 C 383.507 187.806 354.507 222.806 375.507 261.806 C 369.507 264.806 359.507 268.806 344.507 268.806 L 69.507 268.806 C 64.507 287.806 64.507 413.806 202.507 413.806 C 301.507 413.806 375.507 367.806 410.507 283.806 C 462.507 287.806 473.507 244.806 473.507 244.806"/>
|
<path fill="#007bff" d="M 473.507 244.806 C 473.507 244.806 455.507 227.806 418.507 233.806 C 414.507 204.806 383.507 187.806 383.507 187.806 C 383.507 187.806 354.507 222.806 375.507 261.806 C 369.507 264.806 359.507 268.806 344.507 268.806 L 69.507 268.806 C 64.507 287.806 64.507 413.806 202.507 413.806 C 301.507 413.806 375.507 367.806 410.507 283.806 C 462.507 287.806 473.507 244.806 473.507 244.806"/>
|
||||||
</svg>
|
</svg>
|
||||||
|
|
||||||
|
Before Width: | Height: | Size: 919 B After Width: | Height: | Size: 921 B |
@ -15,7 +15,7 @@
|
|||||||
<h2 th:text="#{extractImages.header}"></h2>
|
<h2 th:text="#{extractImages.header}"></h2>
|
||||||
|
|
||||||
<form id="multiPdfForm" th:action="@{extract-images}" method="post" enctype="multipart/form-data">
|
<form id="multiPdfForm" th:action="@{extract-images}" method="post" enctype="multipart/form-data">
|
||||||
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=true)}"></div>
|
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=false)}"></div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label th:text="#{extractImages.selectText}"></label>
|
<label th:text="#{extractImages.selectText}"></label>
|
||||||
<select class="form-control" name="format">
|
<select class="form-control" name="format">
|
||||||
@ -26,102 +26,6 @@
|
|||||||
</div>
|
</div>
|
||||||
<button type="submit" class="btn btn-primary" th:text="#{extractImages.submit}"></button>
|
<button type="submit" class="btn btn-primary" th:text="#{extractImages.submit}"></button>
|
||||||
</form>
|
</form>
|
||||||
<th:block th:insert="~{fragments/common :: filelist}"></th:block>
|
|
||||||
|
|
||||||
<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>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
$('form').submit(function(event) {
|
|
||||||
event.preventDefault(); // Prevent the default form handling behavior
|
|
||||||
submitMultiPdfForm(event);
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
async function submitMultiPdfForm(event) {
|
|
||||||
// Get the selected PDF files
|
|
||||||
var files = $('#fileInput-input')[0].files;
|
|
||||||
|
|
||||||
// Get the existing form data
|
|
||||||
var formData = new FormData($('form')[0]);
|
|
||||||
formData.delete('fileInput');
|
|
||||||
|
|
||||||
// Show the progress bar
|
|
||||||
$('#progressBarContainer').show();
|
|
||||||
|
|
||||||
// Initialize the progress bar
|
|
||||||
var progressBar = $('#progressBar');
|
|
||||||
progressBar.css('width', '0%');
|
|
||||||
progressBar.attr('aria-valuenow', 0);
|
|
||||||
progressBar.attr('aria-valuemax', files.length);
|
|
||||||
|
|
||||||
// Submit each PDF file in parallel
|
|
||||||
var promises = [];
|
|
||||||
for (var i = 0; i < files.length; i++) {
|
|
||||||
var promise = new Promise(function(resolve, reject) {
|
|
||||||
var fileFormData = new FormData();
|
|
||||||
fileFormData.append('fileInput', files[i]);
|
|
||||||
fileFormData.append('format', $('select[name="format"]').val());
|
|
||||||
|
|
||||||
fetch('extract-images', {
|
|
||||||
method: 'POST',
|
|
||||||
body: fileFormData
|
|
||||||
}).then(function(response) {
|
|
||||||
console.log('Received response for file ' + i + ': ' + response);
|
|
||||||
|
|
||||||
var contentDisposition = response.headers.get('content-disposition');
|
|
||||||
var fileName = contentDisposition.split('filename=')[1].replace(/"/g, '');
|
|
||||||
|
|
||||||
response.blob().then(function(blob) {
|
|
||||||
var url = window.URL.createObjectURL(blob);
|
|
||||||
var a = document.createElement('a');
|
|
||||||
a.href = url;
|
|
||||||
a.download = fileName;
|
|
||||||
document.body.appendChild(a);
|
|
||||||
a.click();
|
|
||||||
a.remove();
|
|
||||||
resolve();
|
|
||||||
});
|
|
||||||
}).catch(function(error) {
|
|
||||||
console.error('Error submitting request for file ' + i + ': ' + error);
|
|
||||||
reject();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// Update the progress bar as each request finishes
|
|
||||||
promise.then(function() {
|
|
||||||
var 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);
|
|
||||||
});
|
|
||||||
|
|
||||||
promises.push(promise);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wait for all requests to finish
|
|
||||||
try {
|
|
||||||
await Promise.all(promises);
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error while uploading files: ' + error);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update the progress bar
|
|
||||||
progressBar.css('width', '100%');
|
|
||||||
progressBar.attr('aria-valuenow', files.length);
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</script>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -24,30 +24,41 @@
|
|||||||
<link rel="stylesheet" th:href="@{css/dark-mode.css}" id="dark-mode-styles">
|
<link rel="stylesheet" th:href="@{css/dark-mode.css}" id="dark-mode-styles">
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
function toggleDarkMode() {
|
function toggleDarkMode() {
|
||||||
var checkbox = document.getElementById("toggle-dark-mode");
|
var checkbox = document.getElementById("toggle-dark-mode");
|
||||||
var darkModeStyles = document.getElementById("dark-mode-styles");
|
var darkModeStyles = document.getElementById("dark-mode-styles");
|
||||||
if (checkbox.checked) {
|
if (checkbox.checked) {
|
||||||
localStorage.setItem("dark-mode", "on");
|
localStorage.setItem("dark-mode", "on");
|
||||||
darkModeStyles.disabled = false;
|
darkModeStyles.disabled = false;
|
||||||
} else {
|
} else {
|
||||||
localStorage.setItem("dark-mode", "off");
|
localStorage.setItem("dark-mode", "off");
|
||||||
darkModeStyles.disabled = true;
|
darkModeStyles.disabled = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$(document).ready(function() {
|
|
||||||
var darkModeStyles = document.getElementById("dark-mode-styles");
|
$(document).ready(function() {
|
||||||
var checkbox = document.getElementById("toggle-dark-mode");
|
var darkModeStyles = document.getElementById("dark-mode-styles");
|
||||||
if (localStorage.getItem("dark-mode") == "on") {
|
var checkbox = document.getElementById("toggle-dark-mode");
|
||||||
darkModeStyles.disabled = false;
|
|
||||||
checkbox.checked = true;
|
// Check if the user has already set a preference
|
||||||
}
|
if (localStorage.getItem("dark-mode") == "on") {
|
||||||
if (localStorage.getItem("dark-mode") == "off") {
|
darkModeStyles.disabled = false;
|
||||||
darkModeStyles.disabled = true;
|
checkbox.checked = true;
|
||||||
checkbox.checked = false;
|
} else if (localStorage.getItem("dark-mode") == "off") {
|
||||||
}
|
darkModeStyles.disabled = true;
|
||||||
|
checkbox.checked = false;
|
||||||
|
} else {
|
||||||
|
// Check the OS's default dark mode setting
|
||||||
|
if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
|
||||||
|
darkModeStyles.disabled = false;
|
||||||
|
checkbox.checked = true;
|
||||||
|
} else {
|
||||||
|
darkModeStyles.disabled = true;
|
||||||
|
checkbox.checked = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
@ -112,11 +123,174 @@
|
|||||||
<th:block th:fragment="fileSelector(name, multiple)">
|
<th:block th:fragment="fileSelector(name, multiple)">
|
||||||
<div class="custom-file-chooser">
|
<div class="custom-file-chooser">
|
||||||
<div class="custom-file">
|
<div class="custom-file">
|
||||||
<input type="file" class="custom-file-input" th:name="${name}" th:id="${name}+'-input'" th:multiple="${multiple}">
|
<input type="file" class="custom-file-input" th:name="${name}" th:id="${name}+'-input'" multiple>
|
||||||
<label class="custom-file-label" th:for="${name}+'-input'" th:text="#{pdfPrompt}"></label>
|
<label class="custom-file-label" th:for="${name}+'-input'" th:text="#{pdfPrompt}"></label>
|
||||||
</div>
|
</div>
|
||||||
<div class="selected-files"></div>
|
<div class="selected-files"></div>
|
||||||
</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>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
$('form').submit(function(event) {
|
||||||
|
var files = $('#fileInput-input')[0].files;
|
||||||
|
var url = this.action;
|
||||||
|
/* Check if ${multiple} is false */
|
||||||
|
var multiple = [[${multiple}]] || false;
|
||||||
|
if (!multiple && files.length > 1) {
|
||||||
|
event.preventDefault(); // Prevent the default form handling behavior
|
||||||
|
submitMultiPdfForm(event,url);
|
||||||
|
} else {
|
||||||
|
// Get the selected download option from localStorage
|
||||||
|
const downloadOption = localStorage.getItem('downloadOption');
|
||||||
|
|
||||||
|
// Create an XMLHttpRequest object to send the request to the server
|
||||||
|
const xhr = new XMLHttpRequest();
|
||||||
|
|
||||||
|
// Set up the response type and event listener for the XMLHttpRequest object
|
||||||
|
xhr.responseType = 'blob';
|
||||||
|
xhr.addEventListener('load', function() {
|
||||||
|
const filename = xhr.getResponseHeader('Content-Disposition').match(/filename=(.+)$/)[1];
|
||||||
|
// Check if the response is a PDF or an image
|
||||||
|
if (xhr.response.type.includes('pdf') || xhr.response.type.includes('image')) {
|
||||||
|
// Perform the appropriate action based on the download option
|
||||||
|
if (downloadOption === 'sameWindow') {
|
||||||
|
// Open the file in the same window
|
||||||
|
window.location.href = URL.createObjectURL(xhr.response);
|
||||||
|
} else if (downloadOption === 'newWindow') {
|
||||||
|
// Open the file in a new window
|
||||||
|
window.open(URL.createObjectURL(xhr.response), '_blank');
|
||||||
|
} else {
|
||||||
|
// Download the file
|
||||||
|
const link = document.createElement('a');
|
||||||
|
link.href = URL.createObjectURL(xhr.response);
|
||||||
|
link.download = filename
|
||||||
|
link.click();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// For ZIP files or other file types, just download the file
|
||||||
|
const link = document.createElement('a');
|
||||||
|
link.href = URL.createObjectURL(xhr.response);
|
||||||
|
link.download = filename
|
||||||
|
link.click();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Set up the error listener for the XMLHttpRequest object
|
||||||
|
xhr.addEventListener('error', function() {
|
||||||
|
// Extract the error message and stack trace from the response
|
||||||
|
const errorResponse = JSON.parse(xhr.responseText);
|
||||||
|
const errorMessage = errorResponse.message;
|
||||||
|
const stackTrace = errorResponse.stackTrace.join('\n');
|
||||||
|
|
||||||
|
// Create an error message to display to the user
|
||||||
|
const message = `${errorMessage}\n\n${stackTrace}`;
|
||||||
|
|
||||||
|
|
||||||
|
// Display the error message to the user
|
||||||
|
alert(message);
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function submitMultiPdfForm(event,url) {
|
||||||
|
// Get the selected PDF files
|
||||||
|
var files = $('#fileInput-input')[0].files;
|
||||||
|
|
||||||
|
// Get the existing form data
|
||||||
|
var formData = new FormData($('form')[0]);
|
||||||
|
formData.delete('fileInput');
|
||||||
|
// Show the progress bar
|
||||||
|
$('#progressBarContainer').show();
|
||||||
|
|
||||||
|
// Initialize the progress bar
|
||||||
|
var progressBar = $('#progressBar');
|
||||||
|
progressBar.css('width', '0%');
|
||||||
|
progressBar.attr('aria-valuenow', 0);
|
||||||
|
progressBar.attr('aria-valuemax', files.length);
|
||||||
|
|
||||||
|
// Submit each PDF file in parallel
|
||||||
|
var promises = [];
|
||||||
|
for (var i = 0; i < files.length; i++) {
|
||||||
|
var promise = new Promise(function(resolve, reject) {
|
||||||
|
|
||||||
|
var fileFormData = new FormData();
|
||||||
|
fileFormData.append('fileInput', files[i]);
|
||||||
|
for (var pair of formData.entries()) {
|
||||||
|
fileFormData.append(pair[0], pair[1]);
|
||||||
|
}
|
||||||
|
console.log(fileFormData);
|
||||||
|
|
||||||
|
fetch(url, {
|
||||||
|
method: 'POST',
|
||||||
|
body: fileFormData
|
||||||
|
}).then(function(response) {
|
||||||
|
if (!response) {
|
||||||
|
throw new Error('Received null response for file ' + i);
|
||||||
|
}
|
||||||
|
console.log('Received response for file ' + i + ': ' + response);
|
||||||
|
|
||||||
|
var contentDisposition = response.headers.get('content-disposition');
|
||||||
|
var fileName = contentDisposition.split('filename=')[1].replace(/"/g, '');
|
||||||
|
|
||||||
|
response.blob().then(function(blob) {
|
||||||
|
var url = window.URL.createObjectURL(blob);
|
||||||
|
var a = document.createElement('a');
|
||||||
|
a.href = url;
|
||||||
|
a.download = fileName;
|
||||||
|
document.body.appendChild(a);
|
||||||
|
a.click();
|
||||||
|
a.remove();
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
}).catch(function(error) {
|
||||||
|
console.error('Error submitting request for file ' + i + ': ' + error);
|
||||||
|
/* Set default values or fallbacks for error properties */
|
||||||
|
var status = error && error.status || 500;
|
||||||
|
var statusText = error && error.statusText || 'Internal Server Error';
|
||||||
|
var message = error && error.message || 'An error occurred while processing your request.';
|
||||||
|
|
||||||
|
/* Reject the Promise to signal that the request has failed */
|
||||||
|
reject();
|
||||||
|
/* Redirect to error page with Spring Boot error parameters */
|
||||||
|
var url = '/error?status=' + status + '&error=' + encodeURIComponent(statusText) + '&message=' + encodeURIComponent(message);
|
||||||
|
window.location.href = url;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Update the progress bar as each request finishes
|
||||||
|
promise.then(function() {
|
||||||
|
var 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);
|
||||||
|
});
|
||||||
|
|
||||||
|
promises.push(promise);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for all requests to finish
|
||||||
|
try {
|
||||||
|
await Promise.all(promises);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error while uploading files: ' + error);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the progress bar
|
||||||
|
progressBar.css('width', '100%');
|
||||||
|
progressBar.attr('aria-valuenow', files.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
<script th:inline="javascript">
|
<script th:inline="javascript">
|
||||||
@ -152,4 +326,6 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|
||||||
</th:block>
|
</th:block>
|
@ -2,5 +2,6 @@
|
|||||||
<footer id="footer" class="text-center py-3">
|
<footer id="footer" class="text-center py-3">
|
||||||
<a href="https://github.com/Frooodle/Stirling-PDF" target="_blank" class="mx-1"><img src="images/github.svg"></img></a>
|
<a href="https://github.com/Frooodle/Stirling-PDF" target="_blank" class="mx-1"><img src="images/github.svg"></img></a>
|
||||||
<a href="https://hub.docker.com/r/frooodle/s-pdf" target="_blank" class="mx-1"><img src="images/docker.svg"></img></a>
|
<a href="https://hub.docker.com/r/frooodle/s-pdf" target="_blank" class="mx-1"><img src="images/docker.svg"></img></a>
|
||||||
|
<a href="https://discord.gg/Cn8pWhQRxZ" target="_blank" class="mx-1"><img src="images/discord.svg"></img></a>
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
@ -1,81 +1,4 @@
|
|||||||
<div th:fragment="navbar" class="mx-auto">
|
<div th:fragment="navbar" class="mx-auto">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.toggle-container {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
margin-right: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.toggle {
|
|
||||||
position: relative;
|
|
||||||
display: inline-block;
|
|
||||||
width: 50px;
|
|
||||||
height: 28px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.toggle input {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.slider {
|
|
||||||
position: absolute;
|
|
||||||
cursor: pointer;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
bottom: 0;
|
|
||||||
background-color: #ccc;
|
|
||||||
border-radius: 28px;
|
|
||||||
transition: .4s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.slider:before {
|
|
||||||
position: absolute;
|
|
||||||
content: "";
|
|
||||||
height: 20px;
|
|
||||||
width: 20px;
|
|
||||||
left: 4px;
|
|
||||||
bottom: 4px;
|
|
||||||
background-color: white;
|
|
||||||
border-radius: 50%;
|
|
||||||
transition: .4s;
|
|
||||||
}
|
|
||||||
|
|
||||||
input:checked+.slider {
|
|
||||||
background-color: #2196F3;
|
|
||||||
}
|
|
||||||
|
|
||||||
input:focus+.slider {
|
|
||||||
box-shadow: 0 0 1px #2196F3;
|
|
||||||
}
|
|
||||||
|
|
||||||
input:checked+.slider:before {
|
|
||||||
transform: translateX(22px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.toggle-label {
|
|
||||||
margin-top: 2px;
|
|
||||||
margin-left: 2px;
|
|
||||||
text-align: center;
|
|
||||||
font-size: 14px;
|
|
||||||
color: #333;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
height: 20px;
|
|
||||||
width: 60px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar-nav {
|
|
||||||
width: 100% !important;
|
|
||||||
white-space: nowrap !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
</style>
|
|
||||||
<script>
|
<script>
|
||||||
document
|
document
|
||||||
.addEventListener(
|
.addEventListener(
|
||||||
@ -190,36 +113,30 @@ input:checked+.slider:before {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div class="toggle-container">
|
<input type="checkbox" id="toggle-dark-mode" checked="true" th:onclick="javascript:toggleDarkMode()">
|
||||||
<label class="toggle"> <input type="checkbox" id="toggle-dark-mode" checked="true" th:onclick="javascript:toggleDarkMode()"> <span class="slider round"></span>
|
<a class="nav-link" href="#" for="toggle-dark-mode" th:text="#{navbar.darkmode}"></a>
|
||||||
</label> <label class="toggle-label" for="toggle-dark-mode" th:text="#{navbar.darkmode}"></label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<li class="nav-item dropdown">
|
||||||
|
<a class="nav-link dropdown-toggle" href="#" id="languageDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor" class="bi bi-globe2" viewBox="0 0 20 20">
|
||||||
|
<path d="M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8zm7.5-6.923c-.67.204-1.335.82-1.887 1.855-.143.268-.276.56-.395.872.705.157 1.472.257 2.282.287V1.077zM4.249 3.539c.142-.384.304-.744.481-1.078a6.7 6.7 0 0 1 .597-.933A7.01 7.01 0 0 0 3.051 3.05c.362.184.763.349 1.198.49zM3.509 7.5c.036-1.07.188-2.087.436-3.008a9.124 9.124 0 0 1-1.565-.667A6.964 6.964 0 0 0 1.018 7.5h2.49zm1.4-2.741a12.344 12.344 0 0 0-.4 2.741H7.5V5.091c-.91-.03-1.783-.145-2.591-.332zM8.5 5.09V7.5h2.99a12.342 12.342 0 0 0-.399-2.741c-.808.187-1.681.301-2.591.332zM4.51 8.5c.035.987.176 1.914.399 2.741A13.612 13.612 0 0 1 7.5 10.91V8.5H4.51zm3.99 0v2.409c.91.03 1.783.145 2.591.332.223-.827.364-1.754.4-2.741H8.5zm-3.282 3.696c.12.312.252.604.395.872.552 1.035 1.218 1.65 1.887 1.855V11.91c-.81.03-1.577.13-2.282.287zm.11 2.276a6.696 6.696 0 0 1-.598-.933 8.853 8.853 0 0 1-.481-1.079 8.38 8.38 0 0 0-1.198.49 7.01 7.01 0 0 0 2.276 1.522zm-1.383-2.964A13.36 13.36 0 0 1 3.508 8.5h-2.49a6.963 6.963 0 0 0 1.362 3.675c.47-.258.995-.482 1.565-.667zm6.728 2.964a7.009 7.009 0 0 0 2.275-1.521 8.376 8.376 0 0 0-1.197-.49 8.853 8.853 0 0 1-.481 1.078 6.688 6.688 0 0 1-.597.933zM8.5 11.909v3.014c.67-.204 1.335-.82 1.887-1.855.143-.268.276-.56.395-.872A12.63 12.63 0 0 0 8.5 11.91zm3.555-.401c.57.185 1.095.409 1.565.667A6.963 6.963 0 0 0 14.982 8.5h-2.49a13.36 13.36 0 0 1-.437 3.008zM14.982 7.5a6.963 6.963 0 0 0-1.362-3.675c-.47.258-.995.482-1.565.667.248.92.4 1.938.437 3.008h2.49zM11.27 2.461c.177.334.339.694.482 1.078a8.368 8.368 0 0 0 1.196-.49 7.01 7.01 0 0 0-2.275-1.52c.218.283.418.597.597.932zm-.488 1.343a7.765 7.765 0 0 0-.395-.872C9.835 1.897 9.17 1.282 8.5 1.077V4.09c.81-.03 1.577-.13 2.282-.287z"/>
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<li class="nav-item dropdown"><a class="nav-link dropdown-toggle" href="#" id="languageDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> <i class="bi bi-globe2"></i>
|
|
||||||
</a>
|
</a>
|
||||||
<div class="dropdown-menu" aria-labelledby="languageDropdown">
|
<div class="dropdown-menu" aria-labelledby="languageDropdown">
|
||||||
<a class="dropdown-item lang_dropdown-item" href="" data-language-code="en_US">English (US)</a> <a class="dropdown-item lang_dropdown-item" href="" data-language-code="en_GB">English (UK)</a> <a class="dropdown-item lang_dropdown-item" href="" data-language-code="ar_AR">العربية</a>
|
<a class="dropdown-item lang_dropdown-item" href="" data-language-code="en_US">English (US)</a> <a class="dropdown-item lang_dropdown-item" href="" data-language-code="en_GB">English (UK)</a> <a class="dropdown-item lang_dropdown-item" href="" data-language-code="ar_AR">العربية</a>
|
||||||
<a class="dropdown-item lang_dropdown-item" href="" data-language-code="de_DE">Deutsch</a> <a class="dropdown-item lang_dropdown-item" href="" data-language-code="fr_FR">Français</a>
|
<a class="dropdown-item lang_dropdown-item" href="" data-language-code="de_DE">Deutsch</a> <a class="dropdown-item lang_dropdown-item" href="" data-language-code="fr_FR">Français</a>
|
||||||
</div></li>
|
</div></li>
|
||||||
|
|
||||||
</ul>
|
<li class="nav-item">
|
||||||
|
|
||||||
<ul class="navbar-nav ml-auto">
|
|
||||||
<li class="nav-item">
|
|
||||||
<button type="button" class="btn btn-outline-primary" id="update-btn">Update available</button>
|
<button type="button" class="btn btn-outline-primary" id="update-btn">Update available</button>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
@ -228,7 +145,7 @@ input:checked+.slider:before {
|
|||||||
|
|
||||||
<div class="modal fade" id="settingsModal" tabindex="-1" role="dialog" aria-labelledby="settingsModalLabel" aria-hidden="true">
|
<div class="modal fade" id="settingsModal" tabindex="-1" role="dialog" aria-labelledby="settingsModalLabel" aria-hidden="true">
|
||||||
<div class="modal-dialog modal-dialog-centered" role="document">
|
<div class="modal-dialog modal-dialog-centered" role="document">
|
||||||
<div class="modal-content">
|
<div class="modal-content dark-card">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<h5 class="modal-title" id="settingsModalLabel">Settings</h5>
|
<h5 class="modal-title" id="settingsModalLabel">Settings</h5>
|
||||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||||
@ -236,7 +153,7 @@ input:checked+.slider:before {
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<p th:text="'App Version:' + ${appVersion}"></p>
|
<p th:text="'App Version:' + ${@appVersion}"></p>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="downloadOption">Choose download option (For non ZIP downloads):</label> <select class="form-control" id="downloadOption">
|
<label for="downloadOption">Choose download option (For non ZIP downloads):</label> <select class="form-control" id="downloadOption">
|
||||||
<option value="sameWindow">Open in same window (Single file downloads only)</option>
|
<option value="sameWindow">Open in same window (Single file downloads only)</option>
|
||||||
|
Loading…
Reference in New Issue
Block a user