Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enhanced Codebase Through Structured Optimizations: Improving Structure, Efficiency, Readability, and Documentation #21

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Update Method_2_Script.js
To organize the listed optimizations into thematic groupings and stack-ranked based on their anticipated impact, we can categorize them under headings such as **Code Structure**, **Efficiency and Performance**, **Readability and Maintainability**, and **Documentation and Cleanliness**. Within each category, the optimizations are ordered from most to least impactful based on their potential to improve the codebase.

### Code Structure

1. **Decomposed Functions**: Separating `processAndGeneratePDF`, `addImageToPDF`, `processValidImages`, and `handleAutoScrollAndGeneratePDF` into specific tasks significantly enhances modularity and clarity. This decomposition allows each function to be independently developed, tested, and reused, which is fundamental in software engineering.
2. **Avoid global variables**: Encapsulating variables like `i`, `doc`, `pdfDocumentName`, and `validImgTagCounter` within their respective functions prevents potential bugs caused by unintended modifications, crucial for maintaining a clean scope and preventing side effects.

### Efficiency and Performance

1. **Efficient DOM Access**: Minimizing DOM access by caching elements and using efficient selectors and methods directly impacts the performance, especially in a browser environment where DOM manipulation is costly.
2. **Use Array.prototype.forEach instead of for loops**: Leveraging `Array.from` and `forEach` not only improves readability but also efficiency by eliminating manual counter management and leveraging modern JavaScript optimizations.

### Readability and Maintainability

1. **Use const and let appropriately**: Using `const` for unchangeable variables makes the code easier to understand and debug by clearly indicating which variables are meant to remain constant, thereby preventing accidental reassignments.
2. **Use meaningful variable names**: Descriptive names improve the code's self-documentation, making it easier for new developers to understand what each variable represents without needing to decipher the code's logic.
3. **Improved Loop and Conditional Logic**: Streamlining logic for better readability and maintainability, crucial for complex conditions and loops.
4. **Use a single doc.addImage call**: Refactoring to avoid duplicate code improves maintainability by having a single point of update for future changes.
5. **Using arrow functions for setTimeout callbacks**: Enhances readability and succinctness, making asynchronous code more straightforward.

### Documentation and Cleanliness

1. **Dynamic Loading of jsPDF**: Adding comments to explain dynamic loading increases understandability, especially for developers unfamiliar with asynchronous script loading.
2. **Added comments explaining key operations**: Comprehensive comments describing the purpose and functionality of code sections, variables, and complex operations serve as an invaluable guide for new developers, enhancing the codebase's overall readability and maintainability.

### Additional Notes

- Direct PDF Saving: This is implicitly covered under efficiency but is more of a specific improvement to streamline the file handling process.
- Using template literals and removing the else clause are specific examples of improvements in **Readability and Maintainability** but are subsumed under the broader optimizations already listed.

This thematic grouping and stack-ranking help highlight the importance of clear code structure, efficiency, readability, and cleanliness in developing maintainable and performant web applications.
___

Explanation of the Commenting Approach
• Overall Structure: The code is broken down into functions, each with a specific role, enhancing modularity and readability.
• Purpose of Each Function: Comments describe what each function does, the logic behind its implementation, and how it contributes to the overall goal of generating a PDF document.
• Variable Descriptions: Comments explain the purpose of key variables, especially those whose roles might not be immediately clear to someone unfamiliar with the code.
• Process Flow: The sequence of operations is explained, particularly how the asynchronous loading of the jsPDF library triggers the subsequent steps of the PDF generation process.
• Decision Making: The rationale behind certain decisions, such as the calculation of scroll distances and the selection of elements for scrolling, is made clear.
This commented version aims to make the codebase more accessible and understandable to new developers, facilitating easier modifications, debugging, and enhancements.
  • Loading branch information
richardcmckinney authored Feb 11, 2024

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
commit 325e9d4c21117d8ff5e039af346e6485f25c49b2
213 changes: 106 additions & 107 deletions Method_2_Script.js
Original file line number Diff line number Diff line change
@@ -1,120 +1,119 @@
let jspdf = document.createElement("script");
jspdf.onload = function () {
let pdfDocumentName = "Document";
let doc;

function generatePDF (){
let imgTags = document.getElementsByTagName("img");
let checkURLString = "blob:https://drive.google.com/";
let validImgTagCounter = 0;
for (i = 0; i < imgTags.length; i++) {

if (imgTags[i].src.substring(0, checkURLString.length) === checkURLString){
validImgTagCounter = validImgTagCounter + 1;
//console.log(imgTags[i].src);
let img = imgTags[i];

let canvas = document.createElement('canvas');
let context = canvas.getContext("2d");
canvas.width = img.naturalWidth;
canvas.height = img.naturalHeight;
//console.log("Width: " + img.naturalWidth + ", Height: " + img.naturalHeight);
context.drawImage(img, 0, 0, img.naturalWidth, img.naturalHeight);
let imgDataURL = canvas.toDataURL();
// console.log(imgDataURL);

//let ratio;
let orientation;
if (img.naturalWidth > img.naturalHeight){
//console.log("Landscape");
orientation = "l";
//ratio = img.naturalWidth/img.naturalHeight
}else {
//console.log("Portrait");
orientation = "p";
//ratio = img.naturalWidth/img.naturalHeight
}

let scalefactor = 1.335;
let pageWidth = img.naturalWidth * scalefactor;
let pageHeight = img.naturalHeight * scalefactor;
//let imagexLeft = (pageWidth - img.naturalWidth)/2;
//let imagexTop = (pageHeight - img.naturalHeight)/2;
if (validImgTagCounter === 1){
doc = new jsPDF({
orientation: orientation,
unit: "px",
format: [pageWidth, pageHeight],
});
doc.addImage(imgDataURL, "PNG", 0, 0, img.naturalWidth, img.naturalHeight);
}else{
doc.addPage([pageWidth, pageHeight] , orientation);
doc.addImage(imgDataURL, "PNG", 0, 0, img.naturalWidth, img.naturalHeight);
}
}
// Function to dynamically load the jsPDF library and execute a callback function once it's loaded
function loadJsPDF(callback) {
// Create a new script element
let script = document.createElement("script");
// Assign a callback function to be called once the script is fully loaded
script.onload = callback;
// Set the source URL of the script to the jsPDF library
script.src = 'https://cdnjs.cloudflare.com/ajax/libs/jspdf/1.5.3/jspdf.debug.js';
// Append the script element to the body of the document, triggering the download and execution of the jsPDF script
document.body.appendChild(script);
}

// Main function to process the page content and generate a PDF
function processAndGeneratePDF() {
// Initialize the document name with the desired output filename
const pdfDocumentName = "Document.pdf";
// This variable will hold our jsPDF instance once it's created
let doc = null;

// Function to add an individual image to the PDF document
function addImageToPDF(img) {
// Create a canvas element to draw the image on
const canvas = document.createElement('canvas');
const ctx = canvas.getContext("2d");
// Set the canvas dimensions to match the image dimensions
canvas.width = img.naturalWidth;
canvas.height = img.naturalHeight;
// Draw the image onto the canvas
ctx.drawImage(img, 0, 0);

// Convert the canvas content to a data URL that jsPDF can use
const imgData = canvas.toDataURL();
// Determine the orientation of the image for the PDF layout
const orientation = img.naturalWidth > img.naturalHeight ? "l" : "p";
// Set a scale factor to adjust the image size in the PDF
const scaleFactor = 1.335;
// Calculate the page dimensions based on the image size and scale factor
const pageWidth = img.naturalWidth * scaleFactor;
const pageHeight = img.naturalHeight * scaleFactor;

// Check if the jsPDF instance exists, create it if not, or add a new page to it if it does
if (!doc) {
doc = new jsPDF({
orientation,
unit: "px",
format: [pageWidth, pageHeight],
});
} else {
doc.addPage([pageWidth, pageHeight], orientation);
}

pdfDocumentName = pdfDocumentName + ".pdf";
doc.save(pdfDocumentName);
// Add the image to the PDF document
doc.addImage(imgData, "PNG", 0, 0, img.naturalWidth, img.naturalHeight);
}

let allElements = document.querySelectorAll("*");
let chosenElement;
let heightOfScrollableElement = 0;

for (i = 0; i < allElements.length; i++) {
if ( allElements[i].scrollHeight>=allElements[i].clientHeight){
if (heightOfScrollableElement < allElements[i].scrollHeight){
//console.log(allElements[i]);
//console.log(allElements[i].scrollHeight);
heightOfScrollableElement = allElements[i].scrollHeight;
chosenElement = allElements[i];
// Function to identify valid images and process them for inclusion in the PDF
function processValidImages() {
// Retrieve all image elements from the document
const imgTags = document.getElementsByTagName("img");
// Define the URL pattern to filter images that should be included in the PDF
const checkURLString = "blob:https://drive.google.com/";
// Iterate over each image and add it to the PDF if it matches the pattern
Array.from(imgTags).forEach(img => {
if (img.src.startsWith(checkURLString)) {
addImageToPDF(img);
}
});

// Once all valid images are processed, save the PDF document
if (doc) {
doc.save(pdfDocumentName);
}
}

if (chosenElement.scrollHeight > chosenElement.clientHeight){
console.log("Auto Scroll");

let scrollDistance = Math.round(chosenElement.clientHeight/2);
//console.log("scrollHeight: " + chosenElement.scrollHeight);
//console.log("scrollDistance: " + scrollDistance);

let loopCounter = 0;
function myLoop(remainingHeightToScroll, scrollToLocation) {
loopCounter = loopCounter+1;
console.log(loopCounter);

setTimeout(function() {
if (remainingHeightToScroll === 0){
scrollToLocation = scrollDistance;
chosenElement.scrollTo(0, scrollToLocation);
remainingHeightToScroll = chosenElement.scrollHeight - scrollDistance;
}else{
scrollToLocation = scrollToLocation + scrollDistance ;
chosenElement.scrollTo(0, scrollToLocation);
remainingHeightToScroll = remainingHeightToScroll - scrollDistance;
}

if (remainingHeightToScroll >= chosenElement.clientHeight){
myLoop(remainingHeightToScroll, scrollToLocation)
}else{
setTimeout(function() {
generatePDF();
}, 1500)
// Function to handle scrolling for elements with significant scrollable content before generating the PDF
function handleAutoScrollAndGeneratePDF() {
// Identify all elements that have scrollable content
const elementsWithScroll = Array.from(document.querySelectorAll("*")).filter(el => el.scrollHeight > el.clientHeight);
// Select the element with the maximum scroll height to focus on
const chosenElement = elementsWithScroll.reduce((maxEl, currentEl) => currentEl.scrollHeight > (maxEl.scrollHeight || 0) ? currentEl : maxEl, {});

// Check if the chosen element requires scrolling
if (chosenElement.scrollHeight > chosenElement.clientHeight) {
console.log("Auto Scroll");
// Initialize the remaining height to scroll through
let remainingHeight = chosenElement.scrollHeight;
// Calculate the distance to scroll each iteration, based on half the client height of the element
const scrollDistance = Math.round(chosenElement.clientHeight / 2);

// Recursive function to scroll through the element and generate the PDF afterwards
function scrollAndGenerate() {
if (remainingHeight > 0) {
// Scroll the element by the calculated distance
chosenElement.scrollBy(0, scrollDistance);
// Subtract the scrolled distance from the remaining height
remainingHeight -= scrollDistance;
// Continue scrolling after a brief delay
setTimeout(scrollAndGenerate, 500);
} else {
// Once scrolling is complete, wait briefly before generating the PDF
setTimeout(processValidImages, 1500);
}
}

}, 500)
// Start the scrolling process
scrollAndGenerate();
} else {
// If no scrolling is needed, wait briefly before generating the PDF directly
console.log("No Scroll");
setTimeout(processValidImages, 1500);
}
myLoop(0, 0);

}else{
console.log("No Scroll");
setTimeout(function() {
generatePDF();
}, 1500)
}

};
jspdf.src = 'https://cdnjs.cloudflare.com/ajax/libs/jspdf/1.5.3/jspdf.debug.js';
document.body.appendChild(jspdf);
// Initiate the process of handling scrollable content and generating the PDF
handleAutoScrollAndGeneratePDF();
}

// Start the entire process by loading the jsPDF library and then executing the main function once it's loaded
loadJsPDF(processAndGeneratePDF);