From 9a1510a4f12fcf224498c3b764b39cd264fb43bb Mon Sep 17 00:00:00 2001 From: jordy Date: Sat, 29 Apr 2023 12:43:12 +0200 Subject: [PATCH] further refactor js Turn the div adapters into injectable files so that each PdfContainer can be customized. And the adapters can be used in different PdfContainers as well. --- .../{dragDrop.js => DragDropManager.js} | 20 +-- .../{pdfActions.js => PdfActionsManager.js} | 146 +++++++++--------- .../static/js/multitool/horizontalScroll.js | 8 +- .../static/js/multitool/imageHighlighter.js | 42 +++-- .../static/js/multitool/pdfContainer.js | 141 ++++++++--------- src/main/resources/templates/multi-tool.html | 39 +++-- 6 files changed, 206 insertions(+), 190 deletions(-) rename src/main/resources/static/js/multitool/{dragDrop.js => DragDropManager.js} (87%) rename src/main/resources/static/js/multitool/{pdfActions.js => PdfActionsManager.js} (66%) diff --git a/src/main/resources/static/js/multitool/dragDrop.js b/src/main/resources/static/js/multitool/DragDropManager.js similarity index 87% rename from src/main/resources/static/js/multitool/dragDrop.js rename to src/main/resources/static/js/multitool/DragDropManager.js index 7a1066fd..90354551 100644 --- a/src/main/resources/static/js/multitool/dragDrop.js +++ b/src/main/resources/static/js/multitool/DragDropManager.js @@ -8,9 +8,8 @@ class DragDropManager { draggedImageEl; hoveredEl; - constructor(id, movePageTo) { + constructor(id) { this.dragContainer = document.getElementById(id); - this.movePageTo = movePageTo; this.pageDragging = false; this.hoveredEl = undefined; this.draggelEl = undefined @@ -19,8 +18,6 @@ class DragDropManager { this.startDraggingPage = this.startDraggingPage.bind(this); this.onDragEl = this.onDragEl.bind(this); this.stopDraggingPage = this.stopDraggingPage.bind(this); - this.attachDragDropCallbacks = this.attachDragDropCallbacks.bind(this); - } startDraggingPage(div, imageSrc) { @@ -34,9 +31,7 @@ class DragDropManager { this.draggedImageEl.style.left = screenX; this.draggedImageEl.style.right = screenY; this.dragContainer.appendChild(imgEl); - window.addEventListener('mouseup', (e) => { - this.stopDraggingPage(); - }) + window.addEventListener('mouseup', this.stopDraggingPage) window.addEventListener('mousemove', this.onDragEl) } @@ -51,6 +46,7 @@ class DragDropManager { stopDraggingPage() { window.removeEventListener('mousemove', this.onDragEl); + window.removeEventListener('mouseup', this.stopDraggingPage) this.draggedImageEl = undefined; this.pageDragging = false; this.draggedEl.classList.remove('dragging'); @@ -61,10 +57,14 @@ class DragDropManager { this.movePageTo(this.draggedEl, this.hoveredEl); } + setActions({ movePageTo }) { + this.movePageTo = movePageTo; + } - attachDragDropCallbacks(div, imageSrc) { + + adapt(div) { const onDragStart = () => { - this.startDraggingPage(div, imageSrc); + this.startDraggingPage(div, div.querySelector('img').src); } const onMouseEnter = () => { @@ -82,6 +82,8 @@ class DragDropManager { div.addEventListener('dragstart', onDragStart); div.addEventListener('mouseenter', onMouseEnter); div.addEventListener('mouseleave', onMouseLeave); + + return div; } } diff --git a/src/main/resources/static/js/multitool/pdfActions.js b/src/main/resources/static/js/multitool/PdfActionsManager.js similarity index 66% rename from src/main/resources/static/js/multitool/pdfActions.js rename to src/main/resources/static/js/multitool/PdfActionsManager.js index 14bfc349..b716f1d1 100644 --- a/src/main/resources/static/js/multitool/pdfActions.js +++ b/src/main/resources/static/js/multitool/PdfActionsManager.js @@ -1,73 +1,75 @@ class PdfActionsManager { - callbacks; - pageDirection; - constructor(id, { movePageTo, addPdfs, rotateElement }) { - this.pageDirection = document.documentElement.getAttribute("lang-direction"); - const moveUpButtonCallback = e => { - var imgContainer = e.target; - while (!imgContainer.classList.contains(id)) { - imgContainer = imgContainer.parentNode; - } - - const sibling = imgContainer.previousSibling; - if (sibling) { - movePageTo(imgContainer, sibling, true); - } - }; - const moveDownButtonCallback = e => { - var imgContainer = e.target; - while (!imgContainer.classList.contains(id)) { - imgContainer = imgContainer.parentNode; - } - const sibling = imgContainer.nextSibling; - if (sibling) { - movePageTo(imgContainer, sibling.nextSibling, true); - } - }; - const rotateCCWButtonCallback = e => { - var imgContainer = e.target; - while (!imgContainer.classList.contains(id)) { - imgContainer = imgContainer.parentNode; - } - const img = imgContainer.querySelector("img"); - - rotateElement(img, -90) - }; - const rotateCWButtonCallback = e => { - var imgContainer = e.target; - while (!imgContainer.classList.contains(id)) { - imgContainer = imgContainer.parentNode; - } - const img = imgContainer.querySelector("img"); - - rotateElement(img, 90) - }; - const deletePageButtonCallback = e => { - var imgContainer = e.target; - while (!imgContainer.classList.contains(id)) { - imgContainer = imgContainer.parentNode; - } - pagesContainer.removeChild(imgContainer); - }; - const insertFileButtonCallback = e => { - var imgContainer = e.target; - while (!imgContainer.classList.contains(id)) { - imgContainer = imgContainer.parentNode; - } - addPdfs(imgContainer) - }; + pageDirection; + pagesContainer; - this.callbacks = { - moveUpButtonCallback, - moveDownButtonCallback, - rotateCCWButtonCallback, - rotateCWButtonCallback, - deletePageButtonCallback, - insertFileButtonCallback + constructor(id) { + this.pagesContainer = document.getElementById(id); + this.pageDirection = document.documentElement.getAttribute("lang-direction"); + } + + getPageContainer(element) { + var container = element + while (!container.classList.contains('page-container')) { + container = container.parentNode; + } + return container; + } + + moveUpButtonCallback(e) { + var imgContainer = this.getPageContainer(e.target); + + const sibling = imgContainer.previousSibling; + if (sibling) { + this.movePageTo(imgContainer, sibling, true); } } - attachPDFActions(div) { + moveDownButtonCallback(e) { + var imgContainer = this.getPageContainer(e.target); + const sibling = imgContainer.nextSibling; + if (sibling) { + this.movePageTo(imgContainer, sibling.nextSibling, true); + } + }; + + rotateCCWButtonCallback(e) { + var imgContainer = this.getPageContainer(e.target); + const img = imgContainer.querySelector("img"); + + this.rotateElement(img, -90) + }; + + rotateCWButtonCallback(e) { + var imgContainer = this.getPageContainer(e.target); + const img = imgContainer.querySelector("img"); + + this.rotateElement(img, 90) + }; + + deletePageButtonCallback(e) { + var imgContainer = this.getPageContainer(e.target); + this.pagesContainer.removeChild(imgContainer); + }; + + insertFileButtonCallback(e) { + var imgContainer = this.getPageContainer(e.target); + this.addPdfs(imgContainer) + }; + + setActions({ movePageTo, addPdfs, rotateElement }) { + this.movePageTo = movePageTo; + this.addPdfs = addPdfs; + this.rotateElement = rotateElement; + + this.moveUpButtonCallback = this.moveUpButtonCallback.bind(this); + this.moveDownButtonCallback = this.moveDownButtonCallback.bind(this); + this.rotateCCWButtonCallback = this.rotateCCWButtonCallback.bind(this); + this.rotateCWButtonCallback = this.rotateCWButtonCallback.bind(this); + this.deletePageButtonCallback = this.deletePageButtonCallback.bind(this); + this.insertFileButtonCallback = this.insertFileButtonCallback.bind(this); + } + + adapt(div) { const leftDirection = this.pageDirection === 'rtl' ? 'right' : 'left' const rightDirection = this.pageDirection === 'rtl' ? 'left' : 'right' const buttonContainer = document.createElement('div'); @@ -77,13 +79,13 @@ class PdfActionsManager { const moveUp = document.createElement('button'); moveUp.classList.add("move-left-button","btn", "btn-secondary"); moveUp.innerHTML = ``; - moveUp.onclick = this.callbacks.moveUpButtonCallback; + moveUp.onclick = this.moveUpButtonCallback; buttonContainer.appendChild(moveUp); const moveDown = document.createElement('button'); moveDown.classList.add("move-right-button","btn", "btn-secondary"); moveDown.innerHTML = ``; - moveDown.onclick = this.callbacks.moveDownButtonCallback; + moveDown.onclick = this.moveDownButtonCallback; buttonContainer.appendChild(moveDown); const rotateCCW = document.createElement('button'); @@ -92,7 +94,7 @@ class PdfActionsManager { `; - rotateCCW.onclick = this.callbacks.rotateCCWButtonCallback; + rotateCCW.onclick = this.rotateCCWButtonCallback; buttonContainer.appendChild(rotateCCW); const rotateCW = document.createElement('button'); @@ -101,7 +103,7 @@ class PdfActionsManager { `; - rotateCW.onclick = this.callbacks.rotateCWButtonCallback; + rotateCW.onclick = this.rotateCWButtonCallback; buttonContainer.appendChild(rotateCW); const deletePage = document.createElement('button'); @@ -110,7 +112,7 @@ class PdfActionsManager { `; - deletePage.onclick = this.callbacks.deletePageButtonCallback; + deletePage.onclick = this.deletePageButtonCallback; buttonContainer.appendChild(deletePage); div.appendChild(buttonContainer); @@ -128,7 +130,7 @@ class PdfActionsManager { `; - insertFileButton.onclick = this.callbacks.insertFileButtonCallback; + insertFileButton.onclick = this.insertFileButtonCallback; insertFileButtonContainer.appendChild(insertFileButton); div.appendChild(insertFileButtonContainer); @@ -150,6 +152,8 @@ class PdfActionsManager { insertFileButtonRightContainer.appendChild(insertFileButtonRight); div.appendChild(insertFileButtonRightContainer); + + return div; } } diff --git a/src/main/resources/static/js/multitool/horizontalScroll.js b/src/main/resources/static/js/multitool/horizontalScroll.js index c1c5ca87..2d20ee79 100644 --- a/src/main/resources/static/js/multitool/horizontalScroll.js +++ b/src/main/resources/static/js/multitool/horizontalScroll.js @@ -1,12 +1,10 @@ - - const scrollDivHorizontally = (id) => { var scrollDelta = 0; // variable to store the accumulated scroll delta var isScrolling = false; // variable to track if scroll is already in progress - const pagesContainerWrapper = document.getElementById(id); + const divToScrollHorizontally = document.getElementById(id) function scrollLoop() { // Scroll the div horizontally by a fraction of the accumulated scroll delta - pagesContainerWrapper.scrollLeft += scrollDelta * 0.1; + divToScrollHorizontally.scrollLeft += scrollDelta * 0.1; // Reduce the accumulated scroll delta by a fraction scrollDelta *= 0.9; @@ -19,7 +17,7 @@ const scrollDivHorizontally = (id) => { } } - const divToScrollHorizontally = document.getElementById(id) + divToScrollHorizontally.addEventListener("wheel", function(e) { e.preventDefault(); // prevent default mousewheel behavior diff --git a/src/main/resources/static/js/multitool/imageHighlighter.js b/src/main/resources/static/js/multitool/imageHighlighter.js index 15f54c98..d20510d3 100644 --- a/src/main/resources/static/js/multitool/imageHighlighter.js +++ b/src/main/resources/static/js/multitool/imageHighlighter.js @@ -1,27 +1,39 @@ -const getImageHighlighterCallback = (id) => { - const imageHighlighter = document.getElementById(id); - imageHighlighter.onclick = () => { - imageHighlighter.childNodes.forEach((child) => { - child.classList.add('remove'); - setTimeout(() => { - imageHighlighter.removeChild(child); - }, 100) - }) +class ImageHiglighter { + imageHighlighter; + constructor(id) { + this.imageHighlighter = document.getElementById(id); + this.imageHighlightCallback = this.imageHighlightCallback.bind(this); + this.imageHighlighter.onclick = () => { + this.imageHighlighter.childNodes.forEach((child) => { + child.classList.add('remove'); + setTimeout(() => { + this.imageHighlighter.removeChild(child); + }, 100) + }) + } } - const imageHighlightCallback = (highlightEvent) => { + imageHighlightCallback(highlightEvent) { var bigImg = document.createElement('img'); bigImg.onclick = (imageClickEvent) => { - // This prevents the highlighter's onClick from closing the image when clicking on the image - // instead of next to it. + // This prevents the highlighter's onClick from closing the image when clicking + // on the image instead of next to it. imageClickEvent.preventDefault(); imageClickEvent.stopPropagation(); }; bigImg.src = highlightEvent.target.src; - imageHighlighter.appendChild(bigImg); + this.imageHighlighter.appendChild(bigImg); }; - return imageHighlightCallback + setActions() { + // not needed in this case + } + + adapt(div) { + const img = div.querySelector('.page-image'); + img.addEventListener('click', this.imageHighlightCallback) + return div; + } } -export default getImageHighlighterCallback; \ No newline at end of file +export default ImageHiglighter; \ No newline at end of file diff --git a/src/main/resources/static/js/multitool/pdfContainer.js b/src/main/resources/static/js/multitool/pdfContainer.js index 64c5efa4..f1cc1061 100644 --- a/src/main/resources/static/js/multitool/pdfContainer.js +++ b/src/main/resources/static/js/multitool/pdfContainer.js @@ -1,23 +1,43 @@ -import DragDropManager from "./dragDrop.js"; -import scrollDivHorizontally from "./horizontalScroll.js"; -import getImageHighlighterCallback from "./imageHighlighter.js"; -import PdfActionsManager from './pdfActions.js'; +class PdfContainer { + fileName; + pagesContainer; + pagesContainerWrapper; + pdfAdapters; -const createPdfContainer = (id, wrapperId, highlighterId, dragElId) => { - var fileName = null; - const pagesContainer = document.getElementById(id); - const pagesContainerWrapper = document.getElementById(wrapperId); + constructor(id, wrapperId, pdfAdapters) { + this.fileName = null; + this.pagesContainer = document.getElementById(id) + this.pagesContainerWrapper = document.getElementById(wrapperId); + this.movePageTo = this.movePageTo.bind(this); + this.addPdfs = this.addPdfs.bind(this); + this.rotateElement = this.rotateElement.bind(this); + this.rotateAll = this.rotateAll.bind(this); + this.exportPdf = this.exportPdf.bind(this); + this.pdfAdapters = pdfAdapters; - const movePageTo = (startElement, endElement, scrollTo = false) => { - const childArray = Array.from(pagesContainer.childNodes); + this.pdfAdapters.forEach(adapter => { + adapter.setActions({ + movePageTo: this.movePageTo, + addPdfs: this.addPdfs, + rotateElement: this.rotateElement, + }) + }) + + window.addPdfs = this.addPdfs; + window.exportPdf = this.exportPdf; + window.rotateAll = this.rotateAll; + } + + movePageTo(startElement, endElement, scrollTo = false) { + const childArray = Array.from(this.pagesContainer.childNodes); const startIndex = childArray.indexOf(startElement); const endIndex = childArray.indexOf(endElement); - pagesContainer.removeChild(startElement); + this.pagesContainer.removeChild(startElement); if(!endElement) { - pagesContainer.append(startElement); + this.pagesContainer.append(startElement); } else { - pagesContainer.insertBefore(startElement, endElement); + this.pagesContainer.insertBefore(startElement, endElement); } if(scrollTo) { @@ -26,13 +46,13 @@ const createPdfContainer = (id, wrapperId, highlighterId, dragElId) => { ? 0-width : width; - pagesContainerWrapper.scroll({ - left: pagesContainerWrapper.scrollLeft + vector, + this.pagesContainerWrapper.scroll({ + left: this.pagesContainerWrapper.scrollLeft + vector, }) } } - function addPdfs(nextSiblingElement) { + addPdfs(nextSiblingElement) { var input = document.createElement('input'); input.type = 'file'; input.multiple = true; @@ -40,9 +60,9 @@ const createPdfContainer = (id, wrapperId, highlighterId, dragElId) => { input.onchange = async(e) => { const files = e.target.files; - fileName = files[0].name; + this.fileName = files[0].name; for (var i=0; i < files.length; i++) { - addPdfFile(files[i], nextSiblingElement); + this.addPdfFile(files[i], nextSiblingElement); } document.querySelectorAll(".enable-on-file").forEach(element => { @@ -53,7 +73,7 @@ const createPdfContainer = (id, wrapperId, highlighterId, dragElId) => { input.click(); } - function rotateElement(element, deg) { + rotateElement(element, deg) { var lastTransform = element.style.rotate; if (!lastTransform) { lastTransform = "0"; @@ -63,22 +83,9 @@ const createPdfContainer = (id, wrapperId, highlighterId, dragElId) => { element.style.rotate = newAngle + "deg"; } - - scrollDivHorizontally(wrapperId); - var imageHighlighterCallback; - if (highlighterId) { - imageHighlighterCallback = getImageHighlighterCallback(highlighterId); - } - var dragDropManager; - if(dragElId) { - dragDropManager = new DragDropManager('drag-container', movePageTo); - } - - var pdfActionManager = new PdfActionsManager('page-container', { movePageTo, addPdfs, rotateElement }); - - async function addPdfFile(file, nextSiblingElement) { - const { renderer, pdfDocument } = await loadFile(file); + async addPdfFile(file, nextSiblingElement) { + const { renderer, pdfDocument } = await this.loadFile(file); for (var i=0; i < renderer.pageCount; i++) { const div = document.createElement('div'); @@ -94,32 +101,25 @@ const createPdfContainer = (id, wrapperId, highlighterId, dragElId) => { img.doc = pdfDocument; div.appendChild(img); - - if(dragDropManager) { - dragDropManager.attachDragDropCallbacks(div, imageSrc); - } - - /** - * Making pages larger when clicking on them - */ - if(imageHighlighterCallback) { - img.addEventListener('click', imageHighlighterCallback) - } - - /** - * Rendering the various buttons to manipulate and move pdf pages - */ - pdfActionManager.attachPDFActions(div); - + this.pdfAdapters.forEach((adapter) => { + adapter.adapt?.(div) + }) if (nextSiblingElement) { - pagesContainer.insertBefore(div, nextSiblingElement); + this.pagesContainer.insertBefore(div, nextSiblingElement); } else { - pagesContainer.appendChild(div); + this.pagesContainer.appendChild(div); } } } - async function toRenderer(objectUrl) { + async loadFile(file) { + var objectUrl = URL.createObjectURL(file); + var pdfDocument = await this.toPdfLib(objectUrl); + var renderer = await this.toRenderer(objectUrl); + return { renderer, pdfDocument }; + } + + async toRenderer(objectUrl) { const pdf = await pdfjsLib.getDocument(objectUrl).promise; return { document: pdf, @@ -150,31 +150,26 @@ const createPdfContainer = (id, wrapperId, highlighterId, dragElId) => { }; } - async function toPdfLib(objectUrl) { + async toPdfLib(objectUrl) { const existingPdfBytes = await fetch(objectUrl).then(res => res.arrayBuffer()); const pdfDoc = await PDFLib.PDFDocument.load(existingPdfBytes); return pdfDoc; } - async function loadFile(file) { - var objectUrl = URL.createObjectURL(file); - var pdfDocument = await toPdfLib(objectUrl); - var renderer = await toRenderer(objectUrl); - return { renderer, pdfDocument }; - } + - function rotateAll(deg) { - for (var i=0; i { // Download the file const downloadLink = document.createElement('a'); downloadLink.href = url; - downloadLink.download = fileName ? fileName : 'managed.pdf'; + downloadLink.download = this.fileName ? this.fileName : 'managed.pdf'; downloadLink.click(); } } - - return { - addPdfs, - rotateAll, - exportPdf, - } } -export default createPdfContainer; +export default PdfContainer; diff --git a/src/main/resources/templates/multi-tool.html b/src/main/resources/templates/multi-tool.html index ef8900c3..6f3ea682 100644 --- a/src/main/resources/templates/multi-tool.html +++ b/src/main/resources/templates/multi-tool.html @@ -52,7 +52,6 @@ - @@ -61,20 +60,32 @@