diff --git a/src/main/resources/static/js/downloader.js b/src/main/resources/static/js/downloader.js index fc118d28d..bb16dcad9 100644 --- a/src/main/resources/static/js/downloader.js +++ b/src/main/resources/static/js/downloader.js @@ -1,26 +1,36 @@ -(function() { +(function () { + if (window.isDownloadScriptInitialized) return; // Prevent re-execution + window.isDownloadScriptInitialized = true; - const { pdfPasswordPrompt, multipleInputsForSingleRequest, disableMultipleFiles, remoteCall, sessionExpired, refreshPage, error } = window.stirlingPDF; + const { + pdfPasswordPrompt, + multipleInputsForSingleRequest, + disableMultipleFiles, + remoteCall, + sessionExpired, + refreshPage, + error, + } = window.stirlingPDF; function showErrorBanner(message, stackTrace) { - const errorContainer = document.getElementById("errorContainer"); - errorContainer.style.display = "block"; // Display the banner - errorContainer.querySelector(".alert-heading").textContent = error; - errorContainer.querySelector("p").textContent = message; - document.querySelector("#traceContent").textContent = stackTrace; + const errorContainer = document.getElementById('errorContainer'); + errorContainer.style.display = 'block'; // Display the banner + errorContainer.querySelector('.alert-heading').textContent = error; + errorContainer.querySelector('p').textContent = message; + document.querySelector('#traceContent').textContent = stackTrace; } function showSessionExpiredPrompt() { - const errorContainer = document.getElementById("errorContainer"); - errorContainer.style.display = "block"; - errorContainer.querySelector(".alert-heading").textContent = sessionExpired; - errorContainer.querySelector("p").textContent = sessionExpired; - document.querySelector("#traceContent").textContent = ""; + const errorContainer = document.getElementById('errorContainer'); + errorContainer.style.display = 'block'; + errorContainer.querySelector('.alert-heading').textContent = sessionExpired; + errorContainer.querySelector('p').textContent = sessionExpired; + document.querySelector('#traceContent').textContent = ''; // Optional: Add a refresh button - const refreshButton = document.createElement("button"); + const refreshButton = document.createElement('button'); refreshButton.textContent = refreshPage; - refreshButton.className = "btn btn-primary mt-3"; + refreshButton.className = 'btn btn-primary mt-3'; refreshButton.onclick = () => location.reload(); errorContainer.appendChild(refreshButton); } @@ -28,19 +38,19 @@ let firstErrorOccurred = false; $(document).ready(function () { - $("form").submit(async function (event) { + $('form').submit(async function (event) { event.preventDefault(); firstErrorOccurred = false; const url = this.action; - const files = $("#fileInput-input")[0].files; + const files = $('#fileInput-input')[0].files; const formData = new FormData(this); - const submitButton = document.getElementById("submitBtn"); - const showGameBtn = document.getElementById("show-game-btn"); + const submitButton = document.getElementById('submitBtn'); + const showGameBtn = document.getElementById('show-game-btn'); const originalButtonText = submitButton.textContent; - var boredWaiting = localStorage.getItem("boredWaiting") || "disabled"; + var boredWaiting = localStorage.getItem('boredWaiting') || 'disabled'; if (showGameBtn) { - showGameBtn.style.display = "none"; + showGameBtn.style.display = 'none'; } // Remove empty file entries @@ -49,58 +59,60 @@ formData.delete(key); } } - const override = $("#override").val() || ""; + const override = $('#override').val() || ''; console.log(override); // Set a timeout to show the game button if operation takes more than 5 seconds const timeoutId = setTimeout(() => { - if (boredWaiting === "enabled" && showGameBtn) { - showGameBtn.style.display = "block"; + if (boredWaiting === 'enabled' && showGameBtn) { + showGameBtn.style.display = 'block'; showGameBtn.parentNode.insertBefore(document.createElement('br'), showGameBtn.nextSibling); } }, 5000); try { - submitButton.textContent = "Processing..."; + submitButton.textContent = 'Processing...'; submitButton.disabled = true; if (remoteCall === true) { - if (override === "multi" || (!multipleInputsForSingleRequest && files.length > 1 && override !== "single")) { + if (override === 'multi' || (!multipleInputsForSingleRequest && files.length > 1 && override !== 'single')) { await submitMultiPdfForm(url, files); } else { await handleSingleDownload(url, formData); } } - clearFileInput(); clearTimeout(timeoutId); if (showGameBtn) { - showGameBtn.style.display = "none"; - showGameBtn.style.marginTop = ""; + showGameBtn.style.display = 'none'; + showGameBtn.style.marginTop = ''; } submitButton.textContent = originalButtonText; submitButton.disabled = false; // After process finishes, check for boredWaiting and gameDialog open status const gameDialog = document.getElementById('game-container-wrapper'); - if (boredWaiting === "enabled" && gameDialog && gameDialog.open) { + if (boredWaiting === 'enabled' && gameDialog && gameDialog.open) { // Display a green banner at the bottom of the screen saying "Download complete" - let downloadCompleteText = "Download Complete"; - if(window.downloadCompleteText){ + let downloadCompleteText = 'Download Complete'; + if (window.downloadCompleteText) { downloadCompleteText = window.downloadCompleteText; } - $("body").append('
'+ downloadCompleteText + '
'); - setTimeout(function() { - $("#download-complete-banner").fadeOut("slow", function() { + $('body').append( + '
' + + downloadCompleteText + + '
' + ); + setTimeout(function () { + $('#download-complete-banner').fadeOut('slow', function () { $(this).remove(); // Remove the banner after fading out }); }, 5000); // Banner will fade out after 5 seconds } - } catch (error) { clearTimeout(timeoutId); - showGameBtn.style.display = "none"; + showGameBtn.style.display = 'none'; submitButton.textContent = originalButtonText; submitButton.disabled = false; handleDownloadError(error); @@ -112,8 +124,8 @@ async function getPDFPageCount(file) { try { const arrayBuffer = await file.arrayBuffer(); - pdfjsLib.GlobalWorkerOptions.workerSrc = '/pdfjs-legacy/pdf.worker.mjs' - const pdf = await pdfjsLib.getDocument({ data: arrayBuffer }).promise; + pdfjsLib.GlobalWorkerOptions.workerSrc = '/pdfjs-legacy/pdf.worker.mjs'; + const pdf = await pdfjsLib.getDocument({data: arrayBuffer}).promise; return pdf.numPages; } catch (error) { console.error('Error getting PDF page count:', error); @@ -128,8 +140,8 @@ let errorMessage = null; try { - const response = await fetch(url, { method: "POST", body: formData }); - const contentType = response.headers.get("content-type"); + const response = await fetch(url, {method: 'POST', body: formData}); + const contentType = response.headers.get('content-type'); if (!response.ok) { errorMessage = response.status; @@ -137,58 +149,57 @@ showSessionExpiredPrompt(); return; } - if (contentType && contentType.includes("application/json")) { - console.error("Throwing error banner, response was not okay"); + if (contentType && contentType.includes('application/json')) { + console.error('Throwing error banner, response was not okay'); return handleJsonResponse(response); } throw new Error(`HTTP error! status: ${response.status}`); } - const contentDisposition = response.headers.get("Content-Disposition"); + const contentDisposition = response.headers.get('Content-Disposition'); let filename = getFilenameFromContentDisposition(contentDisposition); const blob = await response.blob(); success = true; - if (contentType.includes("application/pdf") || contentType.includes("image/")) { + if (contentType.includes('application/pdf') || contentType.includes('image/')) { clearFileInput(); return handleResponse(blob, filename, !isMulti, isZip); } else { clearFileInput(); return handleResponse(blob, filename, false, isZip); } - } catch (error) { success = false; errorMessage = error.message; - console.error("Error in handleSingleDownload:", error); + console.error('Error in handleSingleDownload:', error); throw error; } finally { const processingTime = performance.now() - startTime; // Capture analytics const pageCount = file && file.type === 'application/pdf' ? await getPDFPageCount(file) : null; - if(analyticsEnabled) { + if (analyticsEnabled) { posthog.capture('file_processing', { success: success, file_type: file ? file.type || 'unknown' : 'unknown', file_size: file ? file.size : 0, processing_time: processingTime, error_message: errorMessage, - pdf_pages: pageCount + pdf_pages: pageCount, }); } } } - function getFilenameFromContentDisposition(contentDisposition) { + function getFilenameFromContentDisposition(contentDisposition) { let filename; - if (contentDisposition && contentDisposition.indexOf("attachment") !== -1) { - filename = decodeURIComponent(contentDisposition.split("filename=")[1].replace(/"/g, "")).trim(); + 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"; + filename = 'download'; } return filename; @@ -198,37 +209,37 @@ 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") || - errorMessage.toLowerCase().includes("PDF contains an encryption dictionary") + errorMessage.toLowerCase().includes('the password is incorrect') || + errorMessage.toLowerCase().includes('Password is not provided') || + errorMessage.toLowerCase().includes('PDF contains an encryption dictionary') ) { if (!firstErrorOccurred) { firstErrorOccurred = true; alert(pdfPasswordPrompt); } } else { - showErrorBanner(json.error + ":" + json.message, json.trace); + showErrorBanner(json.error + ':' + json.message, json.trace); } } async function handleResponse(blob, filename, considerViewOptions = false, isZip = false) { if (!blob) return; - const downloadOption = localStorage.getItem("downloadOption"); + const downloadOption = localStorage.getItem('downloadOption'); if (considerViewOptions) { - if (downloadOption === "sameWindow") { + if (downloadOption === 'sameWindow') { const url = URL.createObjectURL(blob); window.location.href = url; return; - } else if (downloadOption === "newWindow") { + } else if (downloadOption === 'newWindow') { const url = URL.createObjectURL(blob); - window.open(url, "_blank"); + window.open(url, '_blank'); return; } } if (!isZip) { downloadFile(blob, filename); } - return { filename, blob }; + return {filename, blob}; } function handleDownloadError(error) { @@ -240,32 +251,32 @@ function downloadFile(blob, filename) { if (!(blob instanceof Blob)) { - console.error("Invalid blob passed to downloadFile function"); + console.error('Invalid blob passed to downloadFile function'); return; } const url = URL.createObjectURL(blob); - const a = document.createElement("a"); + 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 }; + return {filename, blob}; } async function submitMultiPdfForm(url, files) { - const zipThreshold = parseInt(localStorage.getItem("zipThreshold"), 10) || 4; + const zipThreshold = parseInt(localStorage.getItem('zipThreshold'), 10) || 4; const zipFiles = files.length > zipThreshold; let jszip = null; // Add Space below Progress Bar before Showing $('.progressBarContainer').after($('
')); - $(".progressBarContainer").show(); + $('.progressBarContainer').show(); // Initialize the progress bar - let progressBar = $(".progressBar"); - progressBar.css("width", "0%"); - progressBar.attr("aria-valuenow", 0); - progressBar.attr("aria-valuemax", files.length); + let progressBar = $('.progressBar'); + progressBar.css('width', '0%'); + progressBar.attr('aria-valuenow', 0); + progressBar.attr('aria-valuemax', files.length); if (zipFiles) { jszip = new JSZip(); @@ -279,10 +290,10 @@ if (postForm) { formData = new FormData($(postForm)[0]); // Convert the form to a jQuery object and get the raw DOM element } else { - console.log("No form with POST method found."); + console.log('No form with POST method found.'); } //Remove file to reuse parameters for other runs - formData.delete("fileInput"); + formData.delete('fileInput'); // Remove empty file entries for (let [key, value] of formData.entries()) { if (value instanceof File && !value.name) { @@ -298,12 +309,12 @@ for (const chunk of chunks) { const promises = chunk.map(async (file) => { let fileFormData = new FormData(); - fileFormData.append("fileInput", file); + fileFormData.append('fileInput', file); console.log(fileFormData); // Add other form data for (let pair of formData.entries()) { fileFormData.append(pair[0], pair[1]); - console.log(pair[0] + ", " + pair[1]); + console.log(pair[0] + ', ' + pair[1]); } try { @@ -325,47 +336,47 @@ if (zipFiles) { try { - const content = await jszip.generateAsync({ type: "blob" }); - downloadFile(content, "files.zip"); + const content = await jszip.generateAsync({type: 'blob'}); + downloadFile(content, 'files.zip'); } catch (error) { - console.error("Error generating ZIP file: " + error); + console.error('Error generating ZIP file: ' + error); } } - progressBar.css("width", "100%"); - progressBar.attr("aria-valuenow", Array.from(files).length); + progressBar.css('width', '100%'); + progressBar.attr('aria-valuenow', Array.from(files).length); } 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); + 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", () => { + window.addEventListener('unload', () => { for (const url of urls) { URL.revokeObjectURL(url); } }); // Clear file input after job - function clearFileInput(){ + function clearFileInput() { let pathname = document.location.pathname; - if(pathname != "/merge-pdfs"){ - let formElement = document.querySelector("#fileInput-input"); + if (pathname != '/merge-pdfs') { + let formElement = document.querySelector('#fileInput-input'); formElement.value = ''; - let editSectionElement = document.querySelector("#editSection"); - if(editSectionElement){ - editSectionElement.style.display = "none"; + let editSectionElement = document.querySelector('#editSection'); + if (editSectionElement) { + editSectionElement.style.display = 'none'; } - let cropPdfCanvas = document.querySelector("#cropPdfCanvas"); - let overlayCanvas = document.querySelector("#overlayCanvas"); - if(cropPdfCanvas && overlayCanvas){ + let cropPdfCanvas = document.querySelector('#cropPdfCanvas'); + let overlayCanvas = document.querySelector('#overlayCanvas'); + if (cropPdfCanvas && overlayCanvas) { cropPdfCanvas.width = 0; cropPdfCanvas.height = 0; overlayCanvas.width = 0; overlayCanvas.height = 0; } - } else{ + } else { console.log("Disabled for 'Merge'"); } }