JS and css cleanup

This commit is contained in:
Anthony Stirling 2023-06-02 20:15:10 +01:00
parent 7bdb2615d4
commit 45b3e0aa6a
14 changed files with 669 additions and 644 deletions

View file

@ -0,0 +1,10 @@
.custom-file-label {
padding-right: 90px;
}
.selected-files {
margin-top: 10px;
max-height: 150px;
overflow-y: auto;
white-space: pre-wrap;
}

View file

@ -0,0 +1,76 @@
var toggleCount = 0;
var lastToggleTime = Date.now();
function toggleDarkMode() {
var currentTime = Date.now();
if (currentTime - lastToggleTime < 1000) {
toggleCount++;
} else {
toggleCount = 1;
}
lastToggleTime = currentTime;
var lightModeStyles = document.getElementById("light-mode-styles");
var darkModeStyles = document.getElementById("dark-mode-styles");
var rainbowModeStyles = document.getElementById("rainbow-mode-styles");
var darkModeIcon = document.getElementById("dark-mode-icon");
if (toggleCount >= 18) {
localStorage.setItem("dark-mode", "rainbow");
lightModeStyles.disabled = true;
darkModeStyles.disabled = true;
rainbowModeStyles.disabled = false;
darkModeIcon.src = "rainbow.svg";
} else if (localStorage.getItem("dark-mode") == "on") {
localStorage.setItem("dark-mode", "off");
lightModeStyles.disabled = false;
darkModeStyles.disabled = true;
rainbowModeStyles.disabled = true;
darkModeIcon.src = "sun.svg";
} else {
localStorage.setItem("dark-mode", "on");
lightModeStyles.disabled = true;
darkModeStyles.disabled = false;
rainbowModeStyles.disabled = true;
darkModeIcon.src = "moon.svg";
}
}
document.addEventListener("DOMContentLoaded", function() {
var lightModeStyles = document.getElementById("light-mode-styles");
var darkModeStyles = document.getElementById("dark-mode-styles");
var rainbowModeStyles = document.getElementById("rainbow-mode-styles");
var darkModeIcon = document.getElementById("dark-mode-icon");
if (localStorage.getItem("dark-mode") == "on") {
lightModeStyles.disabled = true;
darkModeStyles.disabled = false;
rainbowModeStyles.disabled = true;
darkModeIcon.src = "moon.svg";
} else if (localStorage.getItem("dark-mode") == "off") {
lightModeStyles.disabled = false;
darkModeStyles.disabled = true;
rainbowModeStyles.disabled = true;
darkModeIcon.src = "sun.svg";
} else if (localStorage.getItem("dark-mode") == "rainbow") {
lightModeStyles.disabled = true;
darkModeStyles.disabled = true;
rainbowModeStyles.disabled = false;
darkModeIcon.src = "rainbow.svg";
} else {
if (window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches) {
darkModeStyles.disabled = false;
rainbowModeStyles.disabled = true;
darkModeIcon.src = "moon.svg";
} else {
darkModeStyles.disabled = true;
rainbowModeStyles.disabled = true;
darkModeIcon.src = "sun.svg";
}
}
document.getElementById("dark-mode-toggle").addEventListener("click", function(event) {
event.preventDefault();
toggleDarkMode();
});
});

View file

@ -0,0 +1,242 @@
function showErrorBanner(message, stackTrace) {
const errorContainer = document.getElementById("errorContainer");
errorContainer.style.display = "block"; // Display the banner
document.querySelector("#errorContainer .alert-heading").textContent = "Error";
document.querySelector("#errorContainer p").textContent = message;
document.querySelector("#traceContent").textContent = stackTrace;
}
$(document).ready(function() {
$('form').submit(async function(event) {
event.preventDefault();
const url = this.action;
const files = $('#fileInput-input')[0].files;
const formData = new FormData(this);
const override = $('#override').val() || '';
$('#submitBtn').text('Processing...');
try {
if (override === 'multi' || files.length > 1 && override !== 'single') {
// Show the progress bar
$('#progressBarContainer').show();
// Initialize the progress bar
//let progressBar = $('#progressBar');
//progressBar.css('width', '0%');
//progressBar.attr('aria-valuenow', 0);
//progressBar.attr('aria-valuemax', files.length);
await submitMultiPdfForm(url, files);
} else {
const downloadDetails = await handleSingleDownload(url, formData);
const downloadOption = localStorage.getItem('downloadOption');
// Handle the download action according to the selected option
//handleDownloadAction(downloadOption, downloadDetails.blob, downloadDetails.filename);
// Update the progress bar
//updateProgressBar(progressBar, 1);
}
$('#submitBtn').text('Submit');
} catch (error) {
handleDownloadError(error);
$('#submitBtn').text('Submit');
console.error(error);
}
});
});
function handleDownloadAction(downloadOption, blob, filename) {
const url = URL.createObjectURL(blob);
switch (downloadOption) {
case 'sameWindow':
// Open the file in the same window
window.location.href = url;
break;
case 'newWindow':
// Open the file in a new window
window.open(url, '_blank');
break;
default:
// Download the file
const link = document.createElement('a');
link.href = url;
link.download = filename;
link.click();
break;
}
}
async function handleSingleDownload(url, formData) {
try {
const response = await fetch(url, { method: 'POST', body: formData });
const contentType = response.headers.get('content-type');
if (!response.ok) {
if (contentType && contentType.includes('application/json')) {
return handleJsonResponse(response);
console.error('Throwing error banner, response was not okay');
}
throw new Error(`HTTP error! status: ${response.status}`);
}
const contentDisposition = response.headers.get('Content-Disposition');
let filename = getFilenameFromContentDisposition(contentDisposition);
const blob = await response.blob();
if (contentType.includes('application/pdf') || contentType.includes('image/')) {
return handleResponse(blob, filename, true);
} else {
return handleResponse(blob, filename);
}
} catch (error) {
console.error('Error in handleSingleDownload:', error);
throw error; // Re-throw the error if you want it to be handled higher up.
}
}
function getFilenameFromContentDisposition(contentDisposition) {
let filename;
if (contentDisposition && contentDisposition.indexOf('attachment') !== -1) {
filename = decodeURIComponent(contentDisposition.split('filename=')[1].replace(/"/g, '')).trim();
} else {
// If the Content-Disposition header is not present or does not contain the filename, use a default filename
filename = 'download';
}
return filename;
}
async function handleJsonResponse(response) {
const json = await response.json();
const errorMessage = JSON.stringify(json, null, 2);
if (errorMessage.toLowerCase().includes('the password is incorrect') || errorMessage.toLowerCase().includes('Password is not provided')) {
alert('[[#{error.pdfPassword}]]');
} else {
showErrorBanner(json.error + ':' + json.message, json.trace);
}
}
async function handleResponse(blob, filename, considerViewOptions = false) {
if (!blob) return;
const downloadOption = localStorage.getItem('downloadOption');
if (considerViewOptions) {
if (downloadOption === 'sameWindow') {
const url = URL.createObjectURL(blob);
window.location.href = url;
return;
} else if (downloadOption === 'newWindow') {
const url = URL.createObjectURL(blob);
window.open(url, '_blank');
return;
}
}
downloadFile(blob, filename);
return { filename, blob };
}
function handleDownloadError(error) {
const errorMessage = error.message;
showErrorBanner(errorMessage);
}
let urls = []; // An array to hold all the URLs
function downloadFile(blob, filename) {
if (!(blob instanceof Blob)) {
console.error('Invalid blob passed to downloadFile function');
return;
}
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = filename;
a.click();
urls.push(url); // Store the URL so it doesn't get garbage collected too soon
return { filename, blob };
}
async function submitMultiPdfForm(url, files) {
const zipThreshold = parseInt(localStorage.getItem('zipThreshold'), 10) || 4;
const zipFiles = files.length > zipThreshold;
let jszip = null;
//let progressBar = $('#progressBar');
//progressBar.css('width', '0%');
//progressBar.attr('aria-valuenow', 0);
//progressBar.attr('aria-valuemax', Array.from(files).length);
if (zipFiles) {
jszip = new JSZip();
}
// Get existing form data
let formData = new FormData($('form')[0]);
formData.delete('fileInput');
const CONCURRENCY_LIMIT = 8;
const chunks = [];
for (let i = 0; i < Array.from(files).length; i += CONCURRENCY_LIMIT) {
chunks.push(Array.from(files).slice(i, i + CONCURRENCY_LIMIT));
}
for (const chunk of chunks) {
const promises = chunk.map(async file => {
let fileFormData = new FormData();
fileFormData.append('fileInput', file);
// Add other form data
for (let pair of formData.entries()) {
fileFormData.append(pair[0], pair[1]);
}
try {
const downloadDetails = await handleSingleDownload(url, fileFormData);
console.log(downloadDetails);
if (zipFiles) {
jszip.file(downloadDetails.filename, downloadDetails.blob);
} else {
downloadFile(downloadDetails.blob, downloadDetails.filename);
}
//updateProgressBar(progressBar, Array.from(files).length);
} catch (error) {
handleDownloadError(error);
console.error(error);
}
});
await Promise.all(promises);
}
if (zipFiles) {
try {
const content = await jszip.generateAsync({ type: "blob" });
downloadFile(content, "files.zip");
} catch (error) {
console.error('Error generating ZIP file: ' + error);
}
}
}
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);
}
window.addEventListener('unload', () => {
for (const url of urls) {
URL.revokeObjectURL(url);
}
});

View file

@ -0,0 +1,50 @@
var traceVisible = false;
function toggletrace() {
var traceDiv = document.getElementById("trace");
if (!traceVisible) {
traceDiv.style.maxHeight = "500px";
traceVisible = true;
} else {
traceDiv.style.maxHeight = "0px";
traceVisible = false;
}
adjustContainerHeight();
}
function copytrace() {
var flip = false
if (!traceVisible) {
toggletrace()
flip = true
}
var traceContent = document.getElementById("traceContent");
var range = document.createRange();
range.selectNode(traceContent);
window.getSelection().removeAllRanges();
window.getSelection().addRange(range);
document.execCommand("copy");
window.getSelection().removeAllRanges();
if (flip) {
toggletrace()
}
}
function dismissError() {
var errorContainer = document.getElementById("errorContainer");
errorContainer.style.display = "none";
errorContainer.style.height = "0";
}
function adjustContainerHeight() {
var errorContainer = document.getElementById("errorContainer");
var traceDiv = document.getElementById("trace");
if (traceVisible) {
errorContainer.style.height = errorContainer.scrollHeight - traceDiv.scrollHeight + traceDiv.offsetHeight + "px";
} else {
errorContainer.style.height = "auto";
}
}
function showHelp() {
$('#helpModal').modal('show');
}

View file

@ -0,0 +1,37 @@
function updateFavoritesDropdown() {
var dropdown = document.querySelector('#favoritesDropdown');
dropdown.innerHTML = ''; // Clear the current favorites
var hasFavorites = false;
for (var i = 0; i < localStorage.length; i++) {
var key = localStorage.key(i);
if (localStorage.getItem(key) === 'favorite') {
// Find the corresponding navbar entry
var navbarEntry = document.querySelector(`a[href='${key}']`);
if (navbarEntry) {
// Create a new dropdown entry
var dropdownItem = document.createElement('a');
dropdownItem.className = 'dropdown-item';
dropdownItem.href = navbarEntry.href;
dropdownItem.innerHTML = navbarEntry.innerHTML;
dropdown.appendChild(dropdownItem);
hasFavorites = true;
}
}
}
// Show or hide the default item based on whether there are any favorites
if (!hasFavorites) {
var defaultItem = document.createElement('a');
defaultItem.className = 'dropdown-item';
defaultItem.textContent = noFavourites;
dropdown.appendChild(defaultItem);
}
}
document.addEventListener('DOMContentLoaded', function() {
updateFavoritesDropdown();
});

View file

@ -0,0 +1,45 @@
document.addEventListener('DOMContentLoaded', function() {
const fileInput = document.getElementById(elementID);
// 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)
}
});
$(elementID).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>");
});
console.log("fileNames.length=" + fileNames.length)
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 + " " + filesSelected);
} else {
$(inputElement).siblings(".custom-file-label").addClass("selected").html(pdfPrompt);
}
}

View file

@ -0,0 +1,41 @@
function compareVersions(version1, version2) {
const v1 = version1.split('.');
const v2 = version2.split('.');
for (let i = 0; i < v1.length || i < v2.length; i++) {
const n1 = parseInt(v1[i]) || 0;
const n2 = parseInt(v2[i]) || 0;
if (n1 > n2) {
return 1;
} else if (n1 < n2) {
return -1;
}
}
return 0;
}
async function getLatestReleaseVersion() {
const url = "https://api.github.com/repos/Frooodle/Stirling-PDF/releases/latest";
const response = await fetch(url);
const data = await response.json();
return data.tag_name.substring(1);
}
async function checkForUpdate() {
const latestVersion = await getLatestReleaseVersion();
console.log("latestVersion=" + latestVersion)
console.log("currentVersion=" + currentVersion)
console.log("compareVersions(latestVersion, currentVersion) > 0)=" + compareVersions(latestVersion, currentVersion))
if (latestVersion != null && latestVersion != "" && compareVersions(latestVersion, currentVersion) > 0) {
document.getElementById("update-btn").style.display = "block";
console.log("visible")
} else {
document.getElementById("update-btn").style.display = "none";
console.log("hidden")
}
}
checkForUpdate();

View file

@ -0,0 +1,46 @@
document.addEventListener('DOMContentLoaded', function() {
const defaultLocale = document.documentElement.lang || 'en_GB';
const storedLocale = localStorage.getItem('languageCode') || defaultLocale;
const dropdownItems = document.querySelectorAll('.lang_dropdown-item');
for (let i = 0; i < dropdownItems.length; i++) {
const item = dropdownItems[i];
item.classList.remove('active');
if (item.dataset.languageCode === storedLocale) {
item.classList.add('active');
}
item.addEventListener('click', handleDropdownItemClick);
}
});
function handleDropdownItemClick(event) {
event.preventDefault();
const languageCode = this.dataset.languageCode;
localStorage.setItem('languageCode', languageCode);
const currentUrl = window.location.href;
if (currentUrl.indexOf('?lang=') === -1) {
window.location.href = currentUrl + '?lang=' + languageCode;
} else {
window.location.href = currentUrl.replace(/\?lang=\w{2,}/, '?lang=' + languageCode);
}
}
$(document).ready(function() {
$(".nav-item.dropdown").each(function() {
var $dropdownMenu = $(this).find(".dropdown-menu");
if ($dropdownMenu.children().length <= 2 && $dropdownMenu.children("hr.dropdown-divider").length === $dropdownMenu.children().length) {
$(this).prev('.nav-item.nav-item-separator').remove();
$(this).remove();
}
});
//Sort languages by alphabet
var list = $('.dropdown-menu[aria-labelledby="languageDropdown"]').children("a");
list.sort(function(a, b) {
var A = $(a).text().toUpperCase();
var B = $(b).text().toUpperCase();
return (A < B) ? -1 : (A > B) ? 1 : 0;
})
.appendTo('.dropdown-menu[aria-labelledby="languageDropdown"]');
});

View file

@ -0,0 +1,63 @@
document.getElementById("fileInput-input").addEventListener("change", function() {
var files = this.files;
var list = document.getElementById("selectedFiles");
list.innerHTML = "";
for (var i = 0; i < files.length; i++) {
var item = document.createElement("li");
item.className = "list-group-item";
item.innerHTML = `
<div class="d-flex justify-content-between align-items-center w-100">
<div class="filename">${files[i].name}</div>
<div class="arrows d-flex">
<button class="btn btn-secondary move-up"><span>&uarr;</span></button>
<button class="btn btn-secondary move-down"><span>&darr;</span></button>
</div>
</div>
`;
list.appendChild(item);
}
var moveUpButtons = document.querySelectorAll(".move-up");
for (var i = 0; i < moveUpButtons.length; i++) {
moveUpButtons[i].addEventListener("click", function(event) {
event.preventDefault();
var parent = this.closest(".list-group-item");
var grandParent = parent.parentNode;
if (parent.previousElementSibling) {
grandParent.insertBefore(parent, parent.previousElementSibling);
updateFiles();
}
});
}
var moveDownButtons = document.querySelectorAll(".move-down");
for (var i = 0; i < moveDownButtons.length; i++) {
moveDownButtons[i].addEventListener("click", function(event) {
event.preventDefault();
var parent = this.closest(".list-group-item");
var grandParent = parent.parentNode;
if (parent.nextElementSibling) {
grandParent.insertBefore(parent.nextElementSibling, parent);
updateFiles();
}
});
}
function updateFiles() {
var dataTransfer = new DataTransfer();
var liElements = document.querySelectorAll("#selectedFiles li");
for (var i = 0; i < liElements.length; i++) {
var fileNameFromList = liElements[i].querySelector(".filename").innerText;
var fileFromFiles;
for (var j = 0; j < files.length; j++) {
var file = files[j];
if (file.name === fileNameFromList) {
dataTransfer.items.add(file);
break;
}
}
}
document.getElementById("fileInput-input").files = dataTransfer.files;
}
});

View file

@ -0,0 +1,42 @@
// Get the download option from local storage, or set it to 'sameWindow' if it doesn't exist
var downloadOption = localStorage.getItem('downloadOption')
|| 'sameWindow';
// Set the selected option in the dropdown
document.getElementById('downloadOption').value = downloadOption;
// Save the selected option to local storage when the dropdown value changes
document.getElementById('downloadOption').addEventListener(
'change',
function() {
downloadOption = this.value;
localStorage.setItem('downloadOption',
downloadOption);
});
// Get the zipThreshold value from local storage, or set it to 0 if it doesn't exist
var zipThreshold = parseInt(localStorage.getItem('zipThreshold'), 10) || 4;
// Set the value of the slider and the display span
document.getElementById('zipThreshold').value = zipThreshold;
document.getElementById('zipThresholdValue').textContent = zipThreshold;
// Save the selected value to local storage when the slider value changes
document.getElementById('zipThreshold').addEventListener('input', function() {
zipThreshold = this.value;
document.getElementById('zipThresholdValue').textContent = zipThreshold;
localStorage.setItem('zipThreshold', zipThreshold);
});
var boredWaiting = localStorage.getItem('boredWaiting') || 'disabled';
document.getElementById('boredWaiting').checked = boredWaiting === 'enabled';
document.getElementById('boredWaiting').addEventListener('change', function() {
boredWaiting = this.checked ? 'enabled' : 'disabled';
localStorage.setItem('boredWaiting', boredWaiting);
});

View file

@ -34,84 +34,7 @@
<script src="js/tab-container.js"></script> <script src="js/tab-container.js"></script>
<script> <script src="js/darkmode.js"></script>
var toggleCount = 0;
var lastToggleTime = Date.now();
function toggleDarkMode() {
var currentTime = Date.now();
if (currentTime - lastToggleTime < 1000) {
toggleCount++;
} else {
toggleCount = 1;
}
lastToggleTime = currentTime;
var lightModeStyles = document.getElementById("light-mode-styles");
var darkModeStyles = document.getElementById("dark-mode-styles");
var rainbowModeStyles = document.getElementById("rainbow-mode-styles");
var darkModeIcon = document.getElementById("dark-mode-icon");
if (toggleCount >= 18) {
localStorage.setItem("dark-mode", "rainbow");
lightModeStyles.disabled = true;
darkModeStyles.disabled = true;
rainbowModeStyles.disabled = false;
darkModeIcon.src = "rainbow.svg";
} else if (localStorage.getItem("dark-mode") == "on") {
localStorage.setItem("dark-mode", "off");
lightModeStyles.disabled = false;
darkModeStyles.disabled = true;
rainbowModeStyles.disabled = true;
darkModeIcon.src = "sun.svg";
} else {
localStorage.setItem("dark-mode", "on");
lightModeStyles.disabled = true;
darkModeStyles.disabled = false;
rainbowModeStyles.disabled = true;
darkModeIcon.src = "moon.svg";
}
}
document.addEventListener("DOMContentLoaded", function () {
var lightModeStyles = document.getElementById("light-mode-styles");
var darkModeStyles = document.getElementById("dark-mode-styles");
var rainbowModeStyles = document.getElementById("rainbow-mode-styles");
var darkModeIcon = document.getElementById("dark-mode-icon");
if (localStorage.getItem("dark-mode") == "on") {
lightModeStyles.disabled = true;
darkModeStyles.disabled = false;
rainbowModeStyles.disabled = true;
darkModeIcon.src = "moon.svg";
} else if (localStorage.getItem("dark-mode") == "off") {
lightModeStyles.disabled = false;
darkModeStyles.disabled = true;
rainbowModeStyles.disabled = true;
darkModeIcon.src = "sun.svg";
} else if (localStorage.getItem("dark-mode") == "rainbow") {
lightModeStyles.disabled = true;
darkModeStyles.disabled = true;
rainbowModeStyles.disabled = false;
darkModeIcon.src = "rainbow.svg";
} else {
if (window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches) {
darkModeStyles.disabled = false;
rainbowModeStyles.disabled = true;
darkModeIcon.src = "moon.svg";
} else {
darkModeStyles.disabled = true;
rainbowModeStyles.disabled = true;
darkModeIcon.src = "sun.svg";
}
}
document.getElementById("dark-mode-toggle").addEventListener("click", function (event) {
event.preventDefault();
toggleDarkMode();
});
});
</script>
</head> </head>
@ -168,214 +91,7 @@ document.addEventListener("DOMContentLoaded", function () {
</th:block> </th:block>
<th:block th:fragment="fileSelector(name, multiple)" th:with="accept=${accept} ?: '*/*', inputText=${inputText} ?: #{pdfPrompt}, remoteCall=${remoteCall} ?: 'true', notRequired=${notRequired} ?: false"> <th:block th:fragment="fileSelector(name, multiple)" th:with="accept=${accept} ?: '*/*', inputText=${inputText} ?: #{pdfPrompt}, remoteCall=${remoteCall} ?: 'true', notRequired=${notRequired} ?: false">
<script> <script src="js/downloader.js"></script>
function showErrorBanner(message, stackTrace) {
const errorContainer = document.getElementById("errorContainer");
errorContainer.style.display = "block"; // Display the banner
document.querySelector("#errorContainer .alert-heading").textContent = "Error";
document.querySelector("#errorContainer p").textContent = message;
document.querySelector("#traceContent").textContent = stackTrace;
}
$(document).ready(function () {
$('form').submit(async function (event) {
event.preventDefault();
const url = this.action;
const files = $('#fileInput-input')[0].files;
const formData = new FormData(this);
const override = $('#override').val() || '';
$('#submitBtn').text('Processing...');
try {
if (override === 'multi' || files.length > 1 && override !== 'single') {
await submitMultiPdfForm(url, files);
} else {
const downloadDetails = await handleSingleDownload(url, formData);
// Determine the download option from localStorage
const downloadOption = localStorage.getItem('downloadOption');
// Handle the download action according to the selected option
handleDownloadAction(downloadOption, downloadDetails.blob, downloadDetails.filename);
}
$('#submitBtn').text('Submit');
} catch (error) {
handleDownloadError(error);
$('#submitBtn').text('Submit');
}
});
});
function handleDownloadAction(downloadOption, blob, filename) {
const url = URL.createObjectURL(blob);
switch (downloadOption) {
case 'sameWindow':
// Open the file in the same window
window.location.href = url;
break;
case 'newWindow':
// Open the file in a new window
window.open(url, '_blank');
break;
default:
// Download the file
const link = document.createElement('a');
link.href = url;
link.download = filename;
link.click();
break;
}
}
async function handleSingleDownload(url, formData) {
const response = await fetch(url, {
method: 'POST',
body: formData
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
} else {
const blob = await response.blob();
const filename = getFilenameFromContentDisposition(response.headers.get('Content-Disposition'));
return { blob, filename };
}
}
function getFilenameFromContentDisposition(contentDisposition) {
let filename;
if (contentDisposition && contentDisposition.indexOf('attachment') !== -1) {
filename = decodeURIComponent(contentDisposition.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';
}
return filename;
}
async function handlePdfOrImageResponse(response, filename) {
const downloadOption = localStorage.getItem('downloadOption');
const blob = await response.blob();
const url = URL.createObjectURL(blob);
if (downloadOption === 'sameWindow') {
window.location.href = url;
} else if (downloadOption === 'newWindow') {
window.open(url, '_blank');
} else {
downloadFile(url, filename);
}
}
async function handleJsonResponse(response) {
const json = await response.json();
const errorMessage = JSON.stringify(json, null, 2);
if(errorMessage.toLowerCase().includes('the password is incorrect') || errorMessage.toLowerCase().includes('Password is not provided')){
alert('[[#{error.pdfPassword}]]');
} else {
showErrorBanner(json.error + ':' + json.message, json.trace);
}
}
async function handleOtherResponse(response, filename) {
const blob = await response.blob();
const url = URL.createObjectURL(blob);
downloadFile(url, filename);
}
function handleDownloadError(error) {
const errorMessage = error.message;
showErrorBanner(errorMessage);
}
let urls = []; // An array to hold all the URLs
function downloadFile(blob, filename) {
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = filename;
a.click();
urls.push(url); // Store the URL so it doesn't get garbage collected too soon
}
async function submitMultiPdfForm(url, files) {
const zipThreshold = parseInt(localStorage.getItem('zipThreshold'), 10) || 4;
const zipFiles = files.length > zipThreshold;
let jszip = null;
let progressBar = $('#progressBar');
progressBar.css('width', '0%');
progressBar.attr('aria-valuenow', 0);
progressBar.attr('aria-valuemax', Array.from(files).length);
if (zipFiles) {
jszip = new JSZip();
}
// Get existing form data
let formData = new FormData($('form')[0]);
formData.delete('fileInput');
const CONCURRENCY_LIMIT = 8;
const chunks = [];
for (let i = 0; i < Array.from(files).length; i += CONCURRENCY_LIMIT) {
chunks.push(Array.from(files).slice(i, i + CONCURRENCY_LIMIT));
}
for (const chunk of chunks) {
const promises = chunk.map(async file => {
let fileFormData = new FormData();
fileFormData.append('fileInput', file);
// Add other form data
for (let pair of formData.entries()) {
fileFormData.append(pair[0], pair[1]);
}
try {
const downloadDetails = await handleSingleDownload(url, fileFormData);
if (zipFiles) {
jszip.file(downloadDetails.filename, downloadDetails.blob);
} else {
downloadFile(downloadDetails.blob, downloadDetails.filename);
}
updateProgressBar(progressBar, Array.from(files).length);
} catch (error) {
handleDownloadError(error);
}
});
await Promise.all(promises);
}
if (zipFiles) {
try {
const content = await jszip.generateAsync({ type: "blob" });
downloadFile(content, "files.zip");
} catch (error) {
console.error('Error generating ZIP file: ' + error);
}
}
}
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);
}
window.addEventListener('unload', () => {
for (const url of urls) {
URL.revokeObjectURL(url);
}
});
</script>
<div class="custom-file-chooser"> <div class="custom-file-chooser">
<div class="custom-file"> <div class="custom-file">
@ -394,65 +110,12 @@ document.addEventListener("DOMContentLoaded", function () {
<button type="button" class="btn btn-primary" id="show-game-btn" style="display:none;">Bored waiting?</button> <button type="button" class="btn btn-primary" id="show-game-btn" style="display:none;">Bored waiting?</button>
<script th:inline="javascript">
<script th:inline="javascript"> const elementID = /*[[${name+"-input"}]]*/ '';
document.addEventListener('DOMContentLoaded', function () { const filesSelected = /*[[#{filesSelected}]]*/ '';
const fileInput = document.getElementById([[${name+"-input"}]]); const pdfPrompt = /*[[#{pdfPrompt}]]*/ '';
// 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>");
});
console.log("fileNames.length=" + fileNames.length)
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 + " " + [[#{filesSelected}]]);
} else {
$(inputElement).siblings(".custom-file-label").addClass("selected").html([[#{pdfPrompt}]]);
}
}
</script> </script>
<style> <script src="js/fileInput.js"></script>
.custom-file-label { <link rel="stylesheet" href="css/fileSelect.css">
padding-right: 90px;
}
.selected-files {
margin-top: 10px;
max-height: 150px;
overflow-y: auto;
white-space: pre-wrap;
}
</style>
</th:block> </th:block>

View file

@ -54,56 +54,5 @@
</div> </div>
</div> </div>
</div> </div>
<script> <script src="js/errorBanner.js"></script>
var traceVisible = false;
function toggletrace() {
var traceDiv = document.getElementById("trace");
if (!traceVisible) {
traceDiv.style.maxHeight = "500px";
traceVisible = true;
} else {
traceDiv.style.maxHeight = "0px";
traceVisible = false;
}
adjustContainerHeight();
}
function copytrace() {
var flip = false
if(!traceVisible) {
toggletrace()
flip = true
}
var traceContent = document.getElementById("traceContent");
var range = document.createRange();
range.selectNode(traceContent);
window.getSelection().removeAllRanges();
window.getSelection().addRange(range);
document.execCommand("copy");
window.getSelection().removeAllRanges();
if(flip){
toggletrace()
}
}
function dismissError() {
var errorContainer = document.getElementById("errorContainer");
errorContainer.style.display = "none";
errorContainer.style.height ="0";
}
function adjustContainerHeight() {
var errorContainer = document.getElementById("errorContainer");
var traceDiv = document.getElementById("trace");
if (traceVisible) {
errorContainer.style.height = errorContainer.scrollHeight - traceDiv.scrollHeight + traceDiv.offsetHeight + "px";
} else {
errorContainer.style.height = "auto";
}
}
function showHelp() {
$('#helpModal').modal('show');
}
</script>
</th:block> </th:block>

View file

@ -1,78 +1,11 @@
<div th:fragment="navbar" class="mx-auto"> <div th:fragment="navbar" class="mx-auto">
<script> <script src="js/languageSelection.js"></script>
document.addEventListener('DOMContentLoaded', function () {
const defaultLocale = document.documentElement.lang || 'en_GB';
const storedLocale = localStorage.getItem('languageCode') || defaultLocale;
const dropdownItems = document.querySelectorAll('.lang_dropdown-item');
for (let i = 0; i < dropdownItems.length; i++) {
const item = dropdownItems[i];
item.classList.remove('active');
if (item.dataset.languageCode === storedLocale) {
item.classList.add('active');
}
item.addEventListener('click', handleDropdownItemClick);
}
});
function handleDropdownItemClick(event) {
event.preventDefault();
const languageCode = this.dataset.languageCode;
localStorage.setItem('languageCode', languageCode);
const currentUrl = window.location.href;
if (currentUrl.indexOf('?lang=') === -1) {
window.location.href = currentUrl + '?lang=' + languageCode;
} else {
window.location.href = currentUrl.replace(/\?lang=\w{2,}/, '?lang=' + languageCode);
}
}
</script>
<script th:inline="javascript"> <script th:inline="javascript">
function compareVersions(version1, version2) { const currentVersion = /*[[${@appVersion}]]*/ '';
const v1 = version1.split('.'); const noFavourites = /*[[#{noFavourites}]]*/ '';
const v2 = version2.split('.');
for (let i = 0; i < v1.length || i < v2.length; i++) {
const n1 = parseInt(v1[i]) || 0;
const n2 = parseInt(v2[i]) || 0;
if (n1 > n2) {
return 1;
} else if (n1 < n2) {
return -1;
}
}
return 0;
}
async function getLatestReleaseVersion() {
const url = "https://api.github.com/repos/Frooodle/Stirling-PDF/releases/latest";
const response = await fetch(url);
const data = await response.json();
return data.tag_name.substring(1);
}
const currentVersion = /*[[${@appVersion}]]*/ ''; // Replace with your current version number
async function checkForUpdate() {
const latestVersion = await getLatestReleaseVersion();
console.log("latestVersion=" + latestVersion)
console.log("currentVersion=" + currentVersion)
console.log("compareVersions(latestVersion, currentVersion) > 0)=" + compareVersions(latestVersion, currentVersion))
if (latestVersion != null && latestVersion != "" && compareVersions(latestVersion, currentVersion) > 0) {
document.getElementById("update-btn").style.display = "block";
console.log("visible")
} else {
document.getElementById("update-btn").style.display = "none";
console.log("hidden")
}
}
checkForUpdate();
</script> </script>
<script th:src="@{/js/githubVersion.js}"></script>
<link rel="stylesheet" href="css/navbar.css"> <link rel="stylesheet" href="css/navbar.css">
@ -263,48 +196,7 @@ function compareVersions(version1, version2) {
</div> </div>
</div> </div>
<script th:inline="javascript"> <script src="js/favourites.js"></script>
function updateFavoritesDropdown() {
var dropdown = document.querySelector('#favoritesDropdown');
dropdown.innerHTML = ''; // Clear the current favorites
var hasFavorites = false;
for (var i = 0; i < localStorage.length; i++) {
var key = localStorage.key(i);
if (localStorage.getItem(key) === 'favorite') {
// Find the corresponding navbar entry
var navbarEntry = document.querySelector(`a[href='${key}']`);
if (navbarEntry) {
// Create a new dropdown entry
var dropdownItem = document.createElement('a');
dropdownItem.className = 'dropdown-item';
dropdownItem.href = navbarEntry.href;
dropdownItem.innerHTML = navbarEntry.innerHTML;
dropdown.appendChild(dropdownItem);
hasFavorites = true;
}
}
}
// Show or hide the default item based on whether there are any favorites
if(!hasFavorites){
var defaultItem = document.createElement('a');
defaultItem.className = 'dropdown-item';
defaultItem.textContent = [[#{noFavourites}]]
dropdown.appendChild(defaultItem);
}
}
document.addEventListener('DOMContentLoaded', function () {
updateFavoritesDropdown();
});
</script>
</nav> </nav>
<div th:insert="~{fragments/errorBannerPerPage.html :: errorBannerPerPage}"></div> <div th:insert="~{fragments/errorBannerPerPage.html :: errorBannerPerPage}"></div>
@ -363,73 +255,7 @@ function compareVersions(version1, version2) {
</div> </div>
<script> <script src="js/settings.js"></script>
$(document).ready(function() {
$(".nav-item.dropdown").each(function() {
var $dropdownMenu = $(this).find(".dropdown-menu");
if ($dropdownMenu.children().length <= 2 && $dropdownMenu.children("hr.dropdown-divider").length === $dropdownMenu.children().length) {
$(this).prev('.nav-item.nav-item-separator').remove();
$(this).remove();
}
});
//Sort languages by alphabet
var list = $('.dropdown-menu[aria-labelledby="languageDropdown"]').children("a");
list.sort(function(a, b) {
var A = $(a).text().toUpperCase();
var B = $(b).text().toUpperCase();
return (A < B) ? -1 : (A > B) ? 1 : 0;
})
.appendTo('.dropdown-menu[aria-labelledby="languageDropdown"]');
});
// Get the download option from local storage, or set it to 'sameWindow' if it doesn't exist
var downloadOption = localStorage.getItem('downloadOption')
|| 'sameWindow';
// Set the selected option in the dropdown
document.getElementById('downloadOption').value = downloadOption;
// Save the selected option to local storage when the dropdown value changes
document.getElementById('downloadOption').addEventListener(
'change',
function() {
downloadOption = this.value;
localStorage.setItem('downloadOption',
downloadOption);
});
// Get the zipThreshold value from local storage, or set it to 0 if it doesn't exist
var zipThreshold = parseInt(localStorage.getItem('zipThreshold'), 10) || 4;
// Set the value of the slider and the display span
document.getElementById('zipThreshold').value = zipThreshold;
document.getElementById('zipThresholdValue').textContent = zipThreshold;
// Save the selected value to local storage when the slider value changes
document.getElementById('zipThreshold').addEventListener('input', function () {
zipThreshold = this.value;
document.getElementById('zipThresholdValue').textContent = zipThreshold;
localStorage.setItem('zipThreshold', zipThreshold);
});
var boredWaiting = localStorage.getItem('boredWaiting') || 'disabled';
document.getElementById('boredWaiting').checked = boredWaiting === 'enabled';
document.getElementById('boredWaiting').addEventListener('change', function() {
boredWaiting = this.checked ? 'enabled' : 'disabled';
localStorage.setItem('boredWaiting', boredWaiting);
});
</script>

View file

@ -27,72 +27,7 @@
</div> </div>
</form> </form>
<link rel="stylesheet" href="css/merge.css"> <link rel="stylesheet" href="css/merge.css">
<script> <script src="js/merge.js"></script>
document.getElementById("fileInput-input").addEventListener("change", function() {
var files = this.files;
var list = document.getElementById("selectedFiles");
list.innerHTML = "";
for (var i = 0; i < files.length; i++) {
var item = document.createElement("li");
item.className = "list-group-item";
item.innerHTML = `
<div class="d-flex justify-content-between align-items-center w-100">
<div class="filename">${files[i].name}</div>
<div class="arrows d-flex">
<button class="btn btn-secondary move-up"><span>&uarr;</span></button>
<button class="btn btn-secondary move-down"><span>&darr;</span></button>
</div>
</div>
`;
list.appendChild(item);
}
var moveUpButtons = document.querySelectorAll(".move-up");
for (var i = 0; i < moveUpButtons.length; i++) {
moveUpButtons[i].addEventListener("click", function(event) {
event.preventDefault();
var parent = this.closest(".list-group-item");
var grandParent = parent.parentNode;
if (parent.previousElementSibling) {
grandParent.insertBefore(parent, parent.previousElementSibling);
updateFiles();
}
});
}
var moveDownButtons = document.querySelectorAll(".move-down");
for (var i = 0; i < moveDownButtons.length; i++) {
moveDownButtons[i].addEventListener("click", function(event) {
event.preventDefault();
var parent = this.closest(".list-group-item");
var grandParent = parent.parentNode;
if (parent.nextElementSibling) {
grandParent.insertBefore(parent.nextElementSibling, parent);
updateFiles();
}
});
}
function updateFiles() {
var dataTransfer = new DataTransfer();
var liElements = document.querySelectorAll("#selectedFiles li");
for (var i = 0; i < liElements.length; i++) {
var fileNameFromList = liElements[i].querySelector(".filename").innerText;
var fileFromFiles;
for (var j = 0; j < files.length; j++) {
var file = files[j];
if (file.name === fileNameFromList) {
dataTransfer.items.add(file);
break;
}
}
}
document.getElementById("fileInput-input").files = dataTransfer.files;
}
});
</script>
</div> </div>
</div> </div>
</div> </div>