2024-11-03 08:24:16 +01:00
|
|
|
(function() {
|
2024-02-16 22:49:06 +01:00
|
|
|
|
2024-11-03 08:24:16 +01:00
|
|
|
const { pdfPasswordPrompt, multipleInputsForSingleRequest, disableMultipleFiles, remoteCall, sessionExpired, refreshPage, error } = window.stirlingPDF;
|
2024-02-17 23:23:07 +00:00
|
|
|
|
2024-11-03 08:24:16 +01:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
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 = "";
|
|
|
|
|
|
|
|
// Optional: Add a refresh button
|
|
|
|
const refreshButton = document.createElement("button");
|
|
|
|
refreshButton.textContent = refreshPage;
|
|
|
|
refreshButton.className = "btn btn-primary mt-3";
|
|
|
|
refreshButton.onclick = () => location.reload();
|
|
|
|
errorContainer.appendChild(refreshButton);
|
|
|
|
}
|
|
|
|
|
|
|
|
let firstErrorOccurred = false;
|
|
|
|
|
|
|
|
$(document).ready(function () {
|
|
|
|
$("form").submit(async function (event) {
|
|
|
|
event.preventDefault();
|
|
|
|
firstErrorOccurred = false;
|
|
|
|
const url = this.action;
|
|
|
|
const files = $("#fileInput-input")[0].files;
|
|
|
|
const formData = new FormData(this);
|
2024-11-22 03:14:26 -06:00
|
|
|
const submitButton = document.getElementById("submitBtn");
|
|
|
|
const showGameBtn = document.getElementById("show-game-btn");
|
|
|
|
const originalButtonText = submitButton.textContent;
|
|
|
|
var boredWaiting = localStorage.getItem("boredWaiting") || "disabled";
|
|
|
|
|
|
|
|
showGameBtn.style.display = "none";
|
2024-11-03 08:24:16 +01:00
|
|
|
|
|
|
|
// Remove empty file entries
|
|
|
|
for (let [key, value] of formData.entries()) {
|
|
|
|
if (value instanceof File && !value.name) {
|
|
|
|
formData.delete(key);
|
2023-07-30 11:31:46 +01:00
|
|
|
}
|
2024-02-16 22:49:06 +01:00
|
|
|
}
|
2024-11-03 08:24:16 +01:00
|
|
|
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";
|
|
|
|
showGameBtn.parentNode.insertBefore(document.createElement('br'), showGameBtn.nextSibling);
|
|
|
|
}
|
|
|
|
}, 5000);
|
|
|
|
|
|
|
|
try {
|
2024-11-22 03:14:26 -06:00
|
|
|
submitButton.textContent = "Processing...";
|
|
|
|
submitButton.disabled = true;
|
|
|
|
|
2024-11-03 08:24:16 +01:00
|
|
|
if (remoteCall === true) {
|
|
|
|
if (override === "multi" || (!multipleInputsForSingleRequest && files.length > 1 && override !== "single")) {
|
|
|
|
await submitMultiPdfForm(url, files);
|
|
|
|
} else {
|
|
|
|
await handleSingleDownload(url, formData);
|
|
|
|
}
|
|
|
|
}
|
2024-11-15 13:21:23 -07:00
|
|
|
|
2024-11-22 03:14:26 -06:00
|
|
|
|
2024-11-15 13:21:23 -07:00
|
|
|
clearFileInput();
|
2024-11-03 08:24:16 +01:00
|
|
|
clearTimeout(timeoutId);
|
2024-11-22 03:14:26 -06:00
|
|
|
showGameBtn.style.display = "none";
|
|
|
|
showGameBtn.style.marginTop = "";
|
|
|
|
submitButton.textContent = originalButtonText;
|
|
|
|
submitButton.disabled = false;
|
2024-11-03 08:24:16 +01:00
|
|
|
|
|
|
|
// After process finishes, check for boredWaiting and gameDialog open status
|
|
|
|
const gameDialog = document.getElementById('game-container-wrapper');
|
|
|
|
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){
|
|
|
|
downloadCompleteText = window.downloadCompleteText;
|
|
|
|
}
|
|
|
|
$("body").append('<div id="download-complete-banner" style="position:fixed;bottom:0;left:0;width:100%;background-color:green;color:white;text-align:center;padding:10px;font-size:16px;z-index:1000;">'+ downloadCompleteText + '</div>');
|
|
|
|
setTimeout(function() {
|
|
|
|
$("#download-complete-banner").fadeOut("slow", function() {
|
|
|
|
$(this).remove(); // Remove the banner after fading out
|
|
|
|
});
|
|
|
|
}, 5000); // Banner will fade out after 5 seconds
|
2024-03-21 21:58:01 +01:00
|
|
|
}
|
2024-11-03 08:24:16 +01:00
|
|
|
|
|
|
|
} catch (error) {
|
2024-11-15 13:21:23 -07:00
|
|
|
clearFileInput();
|
2024-11-03 08:24:16 +01:00
|
|
|
clearTimeout(timeoutId);
|
2024-11-22 03:14:26 -06:00
|
|
|
showGameBtn.style.display = "none";
|
|
|
|
submitButton.textContent = originalButtonText;
|
|
|
|
submitButton.disabled = false;
|
2024-11-03 08:24:16 +01:00
|
|
|
handleDownloadError(error);
|
|
|
|
console.error(error);
|
2024-02-18 13:30:56 +00:00
|
|
|
}
|
2024-11-03 08:24:16 +01:00
|
|
|
});
|
2024-02-16 22:49:06 +01:00
|
|
|
});
|
2023-06-02 20:15:10 +01:00
|
|
|
|
2024-11-20 10:32:44 +00:00
|
|
|
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;
|
|
|
|
return pdf.numPages;
|
|
|
|
} catch (error) {
|
|
|
|
console.error('Error getting PDF page count:', error);
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-11-03 08:24:16 +01:00
|
|
|
async function handleSingleDownload(url, formData, isMulti = false, isZip = false) {
|
2024-11-20 10:32:44 +00:00
|
|
|
const startTime = performance.now();
|
|
|
|
const file = formData.get('fileInput');
|
|
|
|
let success = false;
|
|
|
|
let errorMessage = null;
|
|
|
|
|
2024-11-03 08:24:16 +01:00
|
|
|
try {
|
|
|
|
const response = await fetch(url, { method: "POST", body: formData });
|
|
|
|
const contentType = response.headers.get("content-type");
|
2024-02-16 22:49:06 +01:00
|
|
|
|
2024-11-03 08:24:16 +01:00
|
|
|
if (!response.ok) {
|
2024-11-20 10:32:44 +00:00
|
|
|
errorMessage = response.status;
|
2024-11-03 08:24:16 +01:00
|
|
|
if (response.status === 401) {
|
|
|
|
showSessionExpiredPrompt();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
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}`);
|
2024-10-14 22:34:41 +01:00
|
|
|
}
|
2024-11-03 08:24:16 +01:00
|
|
|
|
|
|
|
const contentDisposition = response.headers.get("Content-Disposition");
|
|
|
|
let filename = getFilenameFromContentDisposition(contentDisposition);
|
|
|
|
|
|
|
|
const blob = await response.blob();
|
2024-11-20 10:32:44 +00:00
|
|
|
success = true;
|
|
|
|
|
2024-11-03 08:24:16 +01:00
|
|
|
if (contentType.includes("application/pdf") || contentType.includes("image/")) {
|
2024-11-15 13:21:23 -07:00
|
|
|
clearFileInput();
|
2024-11-03 08:24:16 +01:00
|
|
|
return handleResponse(blob, filename, !isMulti, isZip);
|
|
|
|
} else {
|
2024-11-15 13:21:23 -07:00
|
|
|
clearFileInput();
|
2024-11-03 08:24:16 +01:00
|
|
|
return handleResponse(blob, filename, false, isZip);
|
2024-02-16 22:49:06 +01:00
|
|
|
}
|
2024-11-15 13:21:23 -07:00
|
|
|
|
2024-11-03 08:24:16 +01:00
|
|
|
} catch (error) {
|
2024-11-20 10:32:44 +00:00
|
|
|
success = false;
|
|
|
|
errorMessage = error.message;
|
2024-11-15 13:21:23 -07:00
|
|
|
clearFileInput();
|
2024-11-03 08:24:16 +01:00
|
|
|
console.error("Error in handleSingleDownload:", error);
|
|
|
|
throw error;
|
2024-11-20 10:32:44 +00:00
|
|
|
} finally {
|
|
|
|
const processingTime = performance.now() - startTime;
|
|
|
|
|
|
|
|
// Capture analytics
|
|
|
|
const pageCount = file && file.type === 'application/pdf' ? await getPDFPageCount(file) : null;
|
2024-11-21 11:47:47 +00:00
|
|
|
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
|
|
|
|
});
|
|
|
|
}
|
2024-02-16 22:49:06 +01:00
|
|
|
}
|
2024-11-03 08:24:16 +01:00
|
|
|
}
|
2023-06-02 20:15:10 +01:00
|
|
|
|
2024-11-20 10:32:44 +00:00
|
|
|
function getFilenameFromContentDisposition(contentDisposition) {
|
2024-11-03 08:24:16 +01:00
|
|
|
let filename;
|
2023-06-02 20:15:10 +01:00
|
|
|
|
2024-11-03 08:24:16 +01:00
|
|
|
if (contentDisposition && contentDisposition.indexOf("attachment") !== -1) {
|
|
|
|
filename = decodeURIComponent(contentDisposition.split("filename=")[1].replace(/"/g, "")).trim();
|
2024-02-16 22:49:06 +01:00
|
|
|
} else {
|
2024-11-03 08:24:16 +01:00
|
|
|
// If the Content-Disposition header is not present or does not contain the filename, use a default filename
|
|
|
|
filename = "download";
|
2024-02-16 22:49:06 +01:00
|
|
|
}
|
2023-06-02 20:15:10 +01:00
|
|
|
|
2024-11-03 08:24:16 +01:00
|
|
|
return filename;
|
2024-02-16 22:49:06 +01:00
|
|
|
}
|
2024-11-20 10:32:44 +00:00
|
|
|
|
2024-11-03 08:24:16 +01:00
|
|
|
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") ||
|
|
|
|
errorMessage.toLowerCase().includes("PDF contains an encryption dictionary")
|
|
|
|
) {
|
|
|
|
if (!firstErrorOccurred) {
|
|
|
|
firstErrorOccurred = true;
|
|
|
|
alert(pdfPasswordPrompt);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
showErrorBanner(json.error + ":" + json.message, json.trace);
|
2024-02-16 22:49:06 +01:00
|
|
|
}
|
|
|
|
}
|
2024-11-03 08:24:16 +01:00
|
|
|
|
|
|
|
async function handleResponse(blob, filename, considerViewOptions = false, isZip = 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;
|
|
|
|
}
|
2024-02-16 22:49:06 +01:00
|
|
|
}
|
2024-11-03 08:24:16 +01:00
|
|
|
if (!isZip) {
|
|
|
|
downloadFile(blob, filename);
|
|
|
|
}
|
|
|
|
return { filename, blob };
|
2024-02-16 22:49:06 +01:00
|
|
|
}
|
2024-11-03 08:24:16 +01:00
|
|
|
|
|
|
|
function handleDownloadError(error) {
|
|
|
|
const errorMessage = error.message;
|
|
|
|
showErrorBanner(errorMessage);
|
2024-02-16 22:49:06 +01:00
|
|
|
}
|
2023-06-02 20:15:10 +01:00
|
|
|
|
2024-11-03 08:24:16 +01:00
|
|
|
let urls = []; // An array to hold all the URLs
|
2023-06-02 20:15:10 +01:00
|
|
|
|
2024-11-03 08:24:16 +01:00
|
|
|
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
|
2023-06-02 20:15:10 +01:00
|
|
|
|
2024-11-03 08:24:16 +01:00
|
|
|
return { filename, blob };
|
2024-02-16 22:49:06 +01:00
|
|
|
}
|
|
|
|
|
2024-11-03 08:24:16 +01:00
|
|
|
async function submitMultiPdfForm(url, files) {
|
|
|
|
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($('<br>'));
|
|
|
|
$(".progressBarContainer").show();
|
|
|
|
// Initialize the progress bar
|
2024-02-16 22:49:06 +01:00
|
|
|
|
2024-11-03 08:24:16 +01:00
|
|
|
let progressBar = $(".progressBar");
|
|
|
|
progressBar.css("width", "0%");
|
|
|
|
progressBar.attr("aria-valuenow", 0);
|
|
|
|
progressBar.attr("aria-valuemax", files.length);
|
|
|
|
|
|
|
|
if (zipFiles) {
|
|
|
|
jszip = new JSZip();
|
2024-02-16 22:49:06 +01:00
|
|
|
}
|
|
|
|
|
2024-11-03 08:24:16 +01:00
|
|
|
// Get the form with the method attribute set to POST
|
|
|
|
let postForm = document.querySelector('form[method="POST"]');
|
|
|
|
|
|
|
|
// Get existing form data
|
|
|
|
let formData;
|
|
|
|
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.");
|
|
|
|
}
|
|
|
|
//Remove file to reuse parameters for other runs
|
|
|
|
formData.delete("fileInput");
|
|
|
|
// Remove empty file entries
|
|
|
|
for (let [key, value] of formData.entries()) {
|
|
|
|
if (value instanceof File && !value.name) {
|
|
|
|
formData.delete(key);
|
2024-02-16 22:49:06 +01:00
|
|
|
}
|
2024-11-03 08:24:16 +01:00
|
|
|
}
|
|
|
|
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));
|
|
|
|
}
|
2024-02-16 22:49:06 +01:00
|
|
|
|
2024-11-03 08:24:16 +01:00
|
|
|
for (const chunk of chunks) {
|
|
|
|
const promises = chunk.map(async (file) => {
|
|
|
|
let fileFormData = new FormData();
|
|
|
|
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]);
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
|
|
const downloadDetails = await handleSingleDownload(url, fileFormData, true, zipFiles);
|
|
|
|
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);
|
2023-07-30 11:31:46 +01:00
|
|
|
}
|
2024-11-03 08:24:16 +01:00
|
|
|
});
|
|
|
|
await Promise.all(promises);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (zipFiles) {
|
|
|
|
try {
|
|
|
|
const content = await jszip.generateAsync({ type: "blob" });
|
|
|
|
downloadFile(content, "files.zip");
|
2024-02-16 22:49:06 +01:00
|
|
|
} catch (error) {
|
2024-11-03 08:24:16 +01:00
|
|
|
console.error("Error generating ZIP file: " + error);
|
2024-02-16 22:49:06 +01:00
|
|
|
}
|
2023-07-30 11:39:29 +01:00
|
|
|
}
|
2024-11-03 08:24:16 +01:00
|
|
|
progressBar.css("width", "100%");
|
|
|
|
progressBar.attr("aria-valuenow", Array.from(files).length);
|
2024-02-16 22:49:06 +01:00
|
|
|
}
|
2024-11-03 08:24:16 +01: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);
|
2024-02-16 22:49:06 +01:00
|
|
|
}
|
2024-11-03 08:24:16 +01:00
|
|
|
window.addEventListener("unload", () => {
|
|
|
|
for (const url of urls) {
|
|
|
|
URL.revokeObjectURL(url);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2024-11-15 13:21:23 -07:00
|
|
|
// Clear file input after job
|
|
|
|
function clearFileInput(){
|
|
|
|
let pathname = document.location.pathname;
|
|
|
|
if(pathname != "/merge-pdfs"){
|
|
|
|
let formElement = document.querySelector("#fileInput-input");
|
|
|
|
formElement.value = '';
|
|
|
|
let editSectionElement = document.querySelector("#editSection");
|
|
|
|
if(editSectionElement){
|
|
|
|
editSectionElement.style.display = "none";
|
|
|
|
}
|
2024-11-20 00:53:14 -07:00
|
|
|
let cropPdfCanvas = document.querySelector("#cropPdfCanvas");
|
2024-11-15 13:21:23 -07:00
|
|
|
let overlayCanvas = document.querySelector("#overlayCanvas");
|
|
|
|
if(cropPdfCanvas && overlayCanvas){
|
|
|
|
cropPdfCanvas.width = 0;
|
2024-11-20 00:53:14 -07:00
|
|
|
cropPdfCanvas.height = 0;
|
2024-11-15 13:21:23 -07:00
|
|
|
|
|
|
|
overlayCanvas.width = 0;
|
2024-11-20 00:53:14 -07:00
|
|
|
overlayCanvas.height = 0;
|
2024-11-15 13:21:23 -07:00
|
|
|
}
|
|
|
|
} else{
|
|
|
|
console.log("Disabled for 'Merge'");
|
|
|
|
}
|
|
|
|
}
|
2024-11-03 08:24:16 +01:00
|
|
|
})();
|