mirror of
https://github.com/Stirling-Tools/Stirling-PDF.git
synced 2025-06-06 18:30:57 +00:00
* Implement Command class for Command Pattern Created a base `Command` class to implement the **Command Pattern**. This class provides a skeletal implementation for `execute`, `undo`, and `redo` methods. **Note:** This class is intended to be subclassed and not instantiated directly. * Add undo/redo stacks and operations * Use rotate element command to perform execute/undo/redo operations * Handle commands executed through events - Add "command-execution" event listener to execute commands that are not invoked from the same class while adding the command to the undo stack and clearing the redo stack. * Add and use rotate all command to rotate/redo/undo all elements * Use command pattern to delete pages * Use command pattern for page selection * Use command pattern to move pages up and down * Use command pattern to remove selected pages * Use command pattern to perform the splitting operation * Add undo/redo functionality with filename input exclusion - Implement undo (Ctrl+Z) and redo (Ctrl+Y) functionality. - Prevent undo/redo actions when the filename input field is focused. - Ensures proper handling of undo/redo actions without interfering with text editing. * Introduce UndoManager for managing undo/redo operations - Encapsulate undo/redo stacks and operations within UndoManager. - Simplify handling of undo/redo functionality through a dedicated manager. * Call execute on splitAllCommand - Fix a bug that caused split all functionality to not work as execute() wasn't called on splitAllCommand * Add undo/redo buttons to multi tool - Add undo/redo buttons to multi tool - Dispatch an event upon state change (such as changes in the undo/redo stacks) to update the UI accordingly. * Add undo/redo to translations * Replace hard-coded "Undo"/"Redo" with translation keys in multi tool --------- Co-authored-by: Anthony Stirling <77850077+Frooodle@users.noreply.github.com>
This commit is contained in:
parent
de9c21b3de
commit
61e750646c
@ -958,6 +958,8 @@ multiTool.moveLeft=تحريك إلى اليسار
|
|||||||
multiTool.moveRight=تحريك إلى اليمين
|
multiTool.moveRight=تحريك إلى اليمين
|
||||||
multiTool.delete=حذف
|
multiTool.delete=حذف
|
||||||
multiTool.dragDropMessage=الصفحات المحددة
|
multiTool.dragDropMessage=الصفحات المحددة
|
||||||
|
multiTool.undo=تراجع
|
||||||
|
multiTool.redo=إعادة إجراء
|
||||||
|
|
||||||
#multiTool-advert
|
#multiTool-advert
|
||||||
multiTool-advert.message=هذه الميزة متوفرة في <a href="{0}">صفحة الأدوات المتعددة</a> لدينا. اطلع عليها للحصول على واجهة مستخدم محسّنة لكل صفحة وميزات إضافية!
|
multiTool-advert.message=هذه الميزة متوفرة في <a href="{0}">صفحة الأدوات المتعددة</a> لدينا. اطلع عليها للحصول على واجهة مستخدم محسّنة لكل صفحة وميزات إضافية!
|
||||||
|
@ -958,6 +958,8 @@ multiTool.moveLeft=Move Left
|
|||||||
multiTool.moveRight=Move Right
|
multiTool.moveRight=Move Right
|
||||||
multiTool.delete=Delete
|
multiTool.delete=Delete
|
||||||
multiTool.dragDropMessage=Seçilmiş Səhifə(lər)
|
multiTool.dragDropMessage=Seçilmiş Səhifə(lər)
|
||||||
|
multiTool.undo=Undo
|
||||||
|
multiTool.redo=Redo
|
||||||
|
|
||||||
#multiTool-advert
|
#multiTool-advert
|
||||||
multiTool-advert.message=Bu xüsusiyyət bizim <a href="{0}">multi-alət səhifə</a>mizdə də mövcuddur. Əlavə xüsusiyyətlər və səhifə-səhifə interfeys üçün sınaqdan keçirin!
|
multiTool-advert.message=Bu xüsusiyyət bizim <a href="{0}">multi-alət səhifə</a>mizdə də mövcuddur. Əlavə xüsusiyyətlər və səhifə-səhifə interfeys üçün sınaqdan keçirin!
|
||||||
|
@ -958,6 +958,8 @@ multiTool.moveLeft=Move Left
|
|||||||
multiTool.moveRight=Move Right
|
multiTool.moveRight=Move Right
|
||||||
multiTool.delete=Delete
|
multiTool.delete=Delete
|
||||||
multiTool.dragDropMessage=Page(s) Selected
|
multiTool.dragDropMessage=Page(s) Selected
|
||||||
|
multiTool.undo=Undo
|
||||||
|
multiTool.redo=Redo
|
||||||
|
|
||||||
#multiTool-advert
|
#multiTool-advert
|
||||||
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
||||||
|
@ -958,6 +958,8 @@ multiTool.moveLeft=Move Left
|
|||||||
multiTool.moveRight=Move Right
|
multiTool.moveRight=Move Right
|
||||||
multiTool.delete=Delete
|
multiTool.delete=Delete
|
||||||
multiTool.dragDropMessage=Page(s) Selected
|
multiTool.dragDropMessage=Page(s) Selected
|
||||||
|
multiTool.undo=Undo
|
||||||
|
multiTool.redo=Redo
|
||||||
|
|
||||||
#multiTool-advert
|
#multiTool-advert
|
||||||
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
||||||
|
@ -958,6 +958,8 @@ multiTool.moveLeft=Move Left
|
|||||||
multiTool.moveRight=Move Right
|
multiTool.moveRight=Move Right
|
||||||
multiTool.delete=Delete
|
multiTool.delete=Delete
|
||||||
multiTool.dragDropMessage=Page(s) Selected
|
multiTool.dragDropMessage=Page(s) Selected
|
||||||
|
multiTool.undo=Undo
|
||||||
|
multiTool.redo=Redo
|
||||||
|
|
||||||
#multiTool-advert
|
#multiTool-advert
|
||||||
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
||||||
|
@ -958,6 +958,8 @@ multiTool.moveLeft=Move Left
|
|||||||
multiTool.moveRight=Move Right
|
multiTool.moveRight=Move Right
|
||||||
multiTool.delete=Delete
|
multiTool.delete=Delete
|
||||||
multiTool.dragDropMessage=Page(s) Selected
|
multiTool.dragDropMessage=Page(s) Selected
|
||||||
|
multiTool.undo=Undo
|
||||||
|
multiTool.redo=Redo
|
||||||
|
|
||||||
#multiTool-advert
|
#multiTool-advert
|
||||||
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
||||||
|
@ -958,6 +958,8 @@ multiTool.moveLeft=Move Left
|
|||||||
multiTool.moveRight=Move Right
|
multiTool.moveRight=Move Right
|
||||||
multiTool.delete=Delete
|
multiTool.delete=Delete
|
||||||
multiTool.dragDropMessage=Page(s) Selected
|
multiTool.dragDropMessage=Page(s) Selected
|
||||||
|
multiTool.undo=Undo
|
||||||
|
multiTool.redo=Redo
|
||||||
|
|
||||||
#multiTool-advert
|
#multiTool-advert
|
||||||
multiTool-advert.message=Diese Funktion ist auch auf unserer <a href="{0}">PDF-Multitool-Seite</a> verfügbar. Probieren Sie sie aus, denn sie bietet eine verbesserte Benutzeroberfläche und zusätzliche Funktionen!
|
multiTool-advert.message=Diese Funktion ist auch auf unserer <a href="{0}">PDF-Multitool-Seite</a> verfügbar. Probieren Sie sie aus, denn sie bietet eine verbesserte Benutzeroberfläche und zusätzliche Funktionen!
|
||||||
|
@ -958,6 +958,8 @@ multiTool.moveLeft=Move Left
|
|||||||
multiTool.moveRight=Move Right
|
multiTool.moveRight=Move Right
|
||||||
multiTool.delete=Delete
|
multiTool.delete=Delete
|
||||||
multiTool.dragDropMessage=Page(s) Selected
|
multiTool.dragDropMessage=Page(s) Selected
|
||||||
|
multiTool.undo=Undo
|
||||||
|
multiTool.redo=Redo
|
||||||
|
|
||||||
#multiTool-advert
|
#multiTool-advert
|
||||||
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
||||||
|
@ -958,6 +958,8 @@ multiTool.moveLeft=Move Left
|
|||||||
multiTool.moveRight=Move Right
|
multiTool.moveRight=Move Right
|
||||||
multiTool.delete=Delete
|
multiTool.delete=Delete
|
||||||
multiTool.dragDropMessage=Page(s) Selected
|
multiTool.dragDropMessage=Page(s) Selected
|
||||||
|
multiTool.undo=Undo
|
||||||
|
multiTool.redo=Redo
|
||||||
|
|
||||||
#multiTool-advert
|
#multiTool-advert
|
||||||
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
||||||
|
@ -958,6 +958,8 @@ multiTool.moveLeft=Move Left
|
|||||||
multiTool.moveRight=Move Right
|
multiTool.moveRight=Move Right
|
||||||
multiTool.delete=Delete
|
multiTool.delete=Delete
|
||||||
multiTool.dragDropMessage=Page(s) Selected
|
multiTool.dragDropMessage=Page(s) Selected
|
||||||
|
multiTool.undo=Undo
|
||||||
|
multiTool.redo=Redo
|
||||||
|
|
||||||
#multiTool-advert
|
#multiTool-advert
|
||||||
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
||||||
|
@ -958,6 +958,8 @@ multiTool.moveLeft=Move Left
|
|||||||
multiTool.moveRight=Move Right
|
multiTool.moveRight=Move Right
|
||||||
multiTool.delete=Delete
|
multiTool.delete=Delete
|
||||||
multiTool.dragDropMessage=Page(s) Selected
|
multiTool.dragDropMessage=Page(s) Selected
|
||||||
|
multiTool.undo=Undo
|
||||||
|
multiTool.redo=Redo
|
||||||
|
|
||||||
#multiTool-advert
|
#multiTool-advert
|
||||||
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
||||||
|
@ -958,6 +958,8 @@ multiTool.moveLeft=Move Left
|
|||||||
multiTool.moveRight=Move Right
|
multiTool.moveRight=Move Right
|
||||||
multiTool.delete=Delete
|
multiTool.delete=Delete
|
||||||
multiTool.dragDropMessage=Page(s) Selected
|
multiTool.dragDropMessage=Page(s) Selected
|
||||||
|
multiTool.undo=Undo
|
||||||
|
multiTool.redo=Redo
|
||||||
|
|
||||||
#multiTool-advert
|
#multiTool-advert
|
||||||
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
||||||
|
@ -958,6 +958,8 @@ multiTool.moveLeft=Déplacer vers la gauche
|
|||||||
multiTool.moveRight=Déplacer vers la droite
|
multiTool.moveRight=Déplacer vers la droite
|
||||||
multiTool.delete=Supprimer
|
multiTool.delete=Supprimer
|
||||||
multiTool.dragDropMessage=Page(s) sélectionnées
|
multiTool.dragDropMessage=Page(s) sélectionnées
|
||||||
|
multiTool.undo=Undo
|
||||||
|
multiTool.redo=Redo
|
||||||
|
|
||||||
#multiTool-advert
|
#multiTool-advert
|
||||||
multiTool-advert.message=Cette fonctionnalité est aussi disponible dans la <a href="{0}">page de l'outil multifonction</a>. Allez-y pour une interface page par page améliorée et des fonctionnalités additionnelles !
|
multiTool-advert.message=Cette fonctionnalité est aussi disponible dans la <a href="{0}">page de l'outil multifonction</a>. Allez-y pour une interface page par page améliorée et des fonctionnalités additionnelles !
|
||||||
|
@ -958,6 +958,8 @@ multiTool.moveLeft=Move Left
|
|||||||
multiTool.moveRight=Move Right
|
multiTool.moveRight=Move Right
|
||||||
multiTool.delete=Delete
|
multiTool.delete=Delete
|
||||||
multiTool.dragDropMessage=Page(s) Selected
|
multiTool.dragDropMessage=Page(s) Selected
|
||||||
|
multiTool.undo=Undo
|
||||||
|
multiTool.redo=Redo
|
||||||
|
|
||||||
#multiTool-advert
|
#multiTool-advert
|
||||||
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
||||||
|
@ -958,6 +958,8 @@ multiTool.moveLeft=Move Left
|
|||||||
multiTool.moveRight=Move Right
|
multiTool.moveRight=Move Right
|
||||||
multiTool.delete=Delete
|
multiTool.delete=Delete
|
||||||
multiTool.dragDropMessage=Page(s) Selected
|
multiTool.dragDropMessage=Page(s) Selected
|
||||||
|
multiTool.undo=Undo
|
||||||
|
multiTool.redo=Redo
|
||||||
|
|
||||||
#multiTool-advert
|
#multiTool-advert
|
||||||
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
||||||
|
@ -958,6 +958,8 @@ multiTool.moveLeft=Move Left
|
|||||||
multiTool.moveRight=Move Right
|
multiTool.moveRight=Move Right
|
||||||
multiTool.delete=Delete
|
multiTool.delete=Delete
|
||||||
multiTool.dragDropMessage=Page(s) Selected
|
multiTool.dragDropMessage=Page(s) Selected
|
||||||
|
multiTool.undo=Undo
|
||||||
|
multiTool.redo=Redo
|
||||||
|
|
||||||
#multiTool-advert
|
#multiTool-advert
|
||||||
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
||||||
|
@ -958,6 +958,8 @@ multiTool.moveLeft=Move Left
|
|||||||
multiTool.moveRight=Move Right
|
multiTool.moveRight=Move Right
|
||||||
multiTool.delete=Delete
|
multiTool.delete=Delete
|
||||||
multiTool.dragDropMessage=Page(s) Selected
|
multiTool.dragDropMessage=Page(s) Selected
|
||||||
|
multiTool.undo=Undo
|
||||||
|
multiTool.redo=Redo
|
||||||
|
|
||||||
#multiTool-advert
|
#multiTool-advert
|
||||||
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
||||||
|
@ -958,6 +958,8 @@ multiTool.moveLeft=Move Left
|
|||||||
multiTool.moveRight=Move Right
|
multiTool.moveRight=Move Right
|
||||||
multiTool.delete=Delete
|
multiTool.delete=Delete
|
||||||
multiTool.dragDropMessage=Page(s) Selected
|
multiTool.dragDropMessage=Page(s) Selected
|
||||||
|
multiTool.undo=Undo
|
||||||
|
multiTool.redo=Redo
|
||||||
|
|
||||||
#multiTool-advert
|
#multiTool-advert
|
||||||
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
||||||
|
@ -958,6 +958,8 @@ multiTool.moveLeft=Sposta a sinistra
|
|||||||
multiTool.moveRight=Sposta a destra
|
multiTool.moveRight=Sposta a destra
|
||||||
multiTool.delete=Elimina
|
multiTool.delete=Elimina
|
||||||
multiTool.dragDropMessage=Pagina(e) selezionata(e)
|
multiTool.dragDropMessage=Pagina(e) selezionata(e)
|
||||||
|
multiTool.undo=Undo
|
||||||
|
multiTool.redo=Redo
|
||||||
|
|
||||||
#multiTool-advert
|
#multiTool-advert
|
||||||
multiTool-advert.message=Questa funzione è disponibile anche nella nostra <a href="{0}">pagina multi-strumento</a>. Scoprila per un'interfaccia utente pagina per pagina migliorata e funzionalità aggiuntive!
|
multiTool-advert.message=Questa funzione è disponibile anche nella nostra <a href="{0}">pagina multi-strumento</a>. Scoprila per un'interfaccia utente pagina per pagina migliorata e funzionalità aggiuntive!
|
||||||
|
@ -958,6 +958,8 @@ multiTool.moveLeft=Move Left
|
|||||||
multiTool.moveRight=Move Right
|
multiTool.moveRight=Move Right
|
||||||
multiTool.delete=Delete
|
multiTool.delete=Delete
|
||||||
multiTool.dragDropMessage=Page(s) Selected
|
multiTool.dragDropMessage=Page(s) Selected
|
||||||
|
multiTool.undo=Undo
|
||||||
|
multiTool.redo=Redo
|
||||||
|
|
||||||
#multiTool-advert
|
#multiTool-advert
|
||||||
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
||||||
|
@ -958,6 +958,8 @@ multiTool.moveLeft=Move Left
|
|||||||
multiTool.moveRight=Move Right
|
multiTool.moveRight=Move Right
|
||||||
multiTool.delete=Delete
|
multiTool.delete=Delete
|
||||||
multiTool.dragDropMessage=Page(s) Selected
|
multiTool.dragDropMessage=Page(s) Selected
|
||||||
|
multiTool.undo=Undo
|
||||||
|
multiTool.redo=Redo
|
||||||
|
|
||||||
#multiTool-advert
|
#multiTool-advert
|
||||||
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
||||||
|
@ -958,6 +958,8 @@ multiTool.moveLeft=Move Left
|
|||||||
multiTool.moveRight=Move Right
|
multiTool.moveRight=Move Right
|
||||||
multiTool.delete=Delete
|
multiTool.delete=Delete
|
||||||
multiTool.dragDropMessage=Page(s) Selected
|
multiTool.dragDropMessage=Page(s) Selected
|
||||||
|
multiTool.undo=Undo
|
||||||
|
multiTool.redo=Redo
|
||||||
|
|
||||||
#multiTool-advert
|
#multiTool-advert
|
||||||
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
||||||
|
@ -958,6 +958,8 @@ multiTool.moveLeft=Move Left
|
|||||||
multiTool.moveRight=Move Right
|
multiTool.moveRight=Move Right
|
||||||
multiTool.delete=Delete
|
multiTool.delete=Delete
|
||||||
multiTool.dragDropMessage=Page(s) Selected
|
multiTool.dragDropMessage=Page(s) Selected
|
||||||
|
multiTool.undo=Undo
|
||||||
|
multiTool.redo=Redo
|
||||||
|
|
||||||
#multiTool-advert
|
#multiTool-advert
|
||||||
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
||||||
|
@ -958,6 +958,8 @@ multiTool.moveLeft=Move Left
|
|||||||
multiTool.moveRight=Move Right
|
multiTool.moveRight=Move Right
|
||||||
multiTool.delete=Delete
|
multiTool.delete=Delete
|
||||||
multiTool.dragDropMessage=Page(s) Selected
|
multiTool.dragDropMessage=Page(s) Selected
|
||||||
|
multiTool.undo=Undo
|
||||||
|
multiTool.redo=Redo
|
||||||
|
|
||||||
#multiTool-advert
|
#multiTool-advert
|
||||||
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
||||||
|
@ -958,6 +958,8 @@ multiTool.moveLeft=Move Left
|
|||||||
multiTool.moveRight=Move Right
|
multiTool.moveRight=Move Right
|
||||||
multiTool.delete=Delete
|
multiTool.delete=Delete
|
||||||
multiTool.dragDropMessage=Page(s) Selected
|
multiTool.dragDropMessage=Page(s) Selected
|
||||||
|
multiTool.undo=Undo
|
||||||
|
multiTool.redo=Redo
|
||||||
|
|
||||||
#multiTool-advert
|
#multiTool-advert
|
||||||
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
||||||
|
@ -958,6 +958,8 @@ multiTool.moveLeft=Move Left
|
|||||||
multiTool.moveRight=Move Right
|
multiTool.moveRight=Move Right
|
||||||
multiTool.delete=Delete
|
multiTool.delete=Delete
|
||||||
multiTool.dragDropMessage=Page(s) Selected
|
multiTool.dragDropMessage=Page(s) Selected
|
||||||
|
multiTool.undo=Undo
|
||||||
|
multiTool.redo=Redo
|
||||||
|
|
||||||
#multiTool-advert
|
#multiTool-advert
|
||||||
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
||||||
|
@ -958,6 +958,8 @@ multiTool.moveLeft=Move Left
|
|||||||
multiTool.moveRight=Move Right
|
multiTool.moveRight=Move Right
|
||||||
multiTool.delete=Delete
|
multiTool.delete=Delete
|
||||||
multiTool.dragDropMessage=Page(s) Selected
|
multiTool.dragDropMessage=Page(s) Selected
|
||||||
|
multiTool.undo=Undo
|
||||||
|
multiTool.redo=Redo
|
||||||
|
|
||||||
#multiTool-advert
|
#multiTool-advert
|
||||||
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
||||||
|
@ -958,6 +958,8 @@ multiTool.moveLeft=Move Left
|
|||||||
multiTool.moveRight=Move Right
|
multiTool.moveRight=Move Right
|
||||||
multiTool.delete=Delete
|
multiTool.delete=Delete
|
||||||
multiTool.dragDropMessage=Page(s) Selected
|
multiTool.dragDropMessage=Page(s) Selected
|
||||||
|
multiTool.undo=Undo
|
||||||
|
multiTool.redo=Redo
|
||||||
|
|
||||||
#multiTool-advert
|
#multiTool-advert
|
||||||
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
||||||
|
@ -958,6 +958,8 @@ multiTool.moveLeft=Move Left
|
|||||||
multiTool.moveRight=Move Right
|
multiTool.moveRight=Move Right
|
||||||
multiTool.delete=Delete
|
multiTool.delete=Delete
|
||||||
multiTool.dragDropMessage=Page(s) Selected
|
multiTool.dragDropMessage=Page(s) Selected
|
||||||
|
multiTool.undo=Undo
|
||||||
|
multiTool.redo=Redo
|
||||||
|
|
||||||
#multiTool-advert
|
#multiTool-advert
|
||||||
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
||||||
|
@ -958,6 +958,8 @@ multiTool.moveLeft=Move Left
|
|||||||
multiTool.moveRight=Move Right
|
multiTool.moveRight=Move Right
|
||||||
multiTool.delete=Delete
|
multiTool.delete=Delete
|
||||||
multiTool.dragDropMessage=Page(s) Selected
|
multiTool.dragDropMessage=Page(s) Selected
|
||||||
|
multiTool.undo=Undo
|
||||||
|
multiTool.redo=Redo
|
||||||
|
|
||||||
#multiTool-advert
|
#multiTool-advert
|
||||||
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
||||||
|
@ -958,6 +958,8 @@ multiTool.moveLeft=Move Left
|
|||||||
multiTool.moveRight=Move Right
|
multiTool.moveRight=Move Right
|
||||||
multiTool.delete=Delete
|
multiTool.delete=Delete
|
||||||
multiTool.dragDropMessage=Page(s) Selected
|
multiTool.dragDropMessage=Page(s) Selected
|
||||||
|
multiTool.undo=Undo
|
||||||
|
multiTool.redo=Redo
|
||||||
|
|
||||||
#multiTool-advert
|
#multiTool-advert
|
||||||
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
||||||
|
@ -958,6 +958,8 @@ multiTool.moveLeft=Move Left
|
|||||||
multiTool.moveRight=Move Right
|
multiTool.moveRight=Move Right
|
||||||
multiTool.delete=Delete
|
multiTool.delete=Delete
|
||||||
multiTool.dragDropMessage=Page(s) Selected
|
multiTool.dragDropMessage=Page(s) Selected
|
||||||
|
multiTool.undo=Undo
|
||||||
|
multiTool.redo=Redo
|
||||||
|
|
||||||
#multiTool-advert
|
#multiTool-advert
|
||||||
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
||||||
|
@ -958,6 +958,8 @@ multiTool.moveLeft=Move Left
|
|||||||
multiTool.moveRight=Move Right
|
multiTool.moveRight=Move Right
|
||||||
multiTool.delete=Delete
|
multiTool.delete=Delete
|
||||||
multiTool.dragDropMessage=Page(s) Selected
|
multiTool.dragDropMessage=Page(s) Selected
|
||||||
|
multiTool.undo=Undo
|
||||||
|
multiTool.redo=Redo
|
||||||
|
|
||||||
#multiTool-advert
|
#multiTool-advert
|
||||||
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
||||||
|
@ -958,6 +958,8 @@ multiTool.moveLeft=Move Left
|
|||||||
multiTool.moveRight=Move Right
|
multiTool.moveRight=Move Right
|
||||||
multiTool.delete=Delete
|
multiTool.delete=Delete
|
||||||
multiTool.dragDropMessage=Page(s) Selected
|
multiTool.dragDropMessage=Page(s) Selected
|
||||||
|
multiTool.undo=Undo
|
||||||
|
multiTool.redo=Redo
|
||||||
|
|
||||||
#multiTool-advert
|
#multiTool-advert
|
||||||
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
||||||
|
@ -958,6 +958,8 @@ multiTool.moveLeft=Move Left
|
|||||||
multiTool.moveRight=Move Right
|
multiTool.moveRight=Move Right
|
||||||
multiTool.delete=Delete
|
multiTool.delete=Delete
|
||||||
multiTool.dragDropMessage=Page(s) Selected
|
multiTool.dragDropMessage=Page(s) Selected
|
||||||
|
multiTool.undo=Undo
|
||||||
|
multiTool.redo=Redo
|
||||||
|
|
||||||
#multiTool-advert
|
#multiTool-advert
|
||||||
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
||||||
|
@ -958,6 +958,8 @@ multiTool.moveLeft=Move Left
|
|||||||
multiTool.moveRight=Move Right
|
multiTool.moveRight=Move Right
|
||||||
multiTool.delete=Delete
|
multiTool.delete=Delete
|
||||||
multiTool.dragDropMessage=Page(s) Selected
|
multiTool.dragDropMessage=Page(s) Selected
|
||||||
|
multiTool.undo=Undo
|
||||||
|
multiTool.redo=Redo
|
||||||
|
|
||||||
#multiTool-advert
|
#multiTool-advert
|
||||||
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
||||||
|
@ -958,6 +958,8 @@ multiTool.moveLeft=Move Left
|
|||||||
multiTool.moveRight=Move Right
|
multiTool.moveRight=Move Right
|
||||||
multiTool.delete=Delete
|
multiTool.delete=Delete
|
||||||
multiTool.dragDropMessage=Page(s) Selected
|
multiTool.dragDropMessage=Page(s) Selected
|
||||||
|
multiTool.undo=Undo
|
||||||
|
multiTool.redo=Redo
|
||||||
|
|
||||||
#multiTool-advert
|
#multiTool-advert
|
||||||
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
multiTool-advert.message=This feature is also available in our <a href="{0}">multi-tool page</a>. Check it out for enhanced page-by-page UI and additional features!
|
||||||
|
@ -1,12 +1,20 @@
|
|||||||
|
import { DeletePageCommand } from "./commands/delete-page.js";
|
||||||
|
import { SelectPageCommand } from "./commands/select.js";
|
||||||
|
import { SplitFileCommand } from "./commands/split.js";
|
||||||
|
import { UndoManager } from "./UndoManager.js";
|
||||||
|
|
||||||
class PdfActionsManager {
|
class PdfActionsManager {
|
||||||
pageDirection;
|
pageDirection;
|
||||||
pagesContainer;
|
pagesContainer;
|
||||||
static selectedPages = []; // Static property shared across all instances
|
static selectedPages = []; // Static property shared across all instances
|
||||||
|
undoManager;
|
||||||
|
|
||||||
constructor(id) {
|
constructor(id, undoManager) {
|
||||||
this.pagesContainer = document.getElementById(id);
|
this.pagesContainer = document.getElementById(id);
|
||||||
this.pageDirection = document.documentElement.getAttribute("dir");
|
this.pageDirection = document.documentElement.getAttribute("dir");
|
||||||
|
|
||||||
|
this.undoManager = undoManager || new UndoManager();
|
||||||
|
|
||||||
var styleElement = document.createElement("link");
|
var styleElement = document.createElement("link");
|
||||||
styleElement.rel = "stylesheet";
|
styleElement.rel = "stylesheet";
|
||||||
styleElement.href = "css/pdfActions.css";
|
styleElement.href = "css/pdfActions.css";
|
||||||
@ -27,7 +35,8 @@ class PdfActionsManager {
|
|||||||
|
|
||||||
const sibling = imgContainer.previousSibling;
|
const sibling = imgContainer.previousSibling;
|
||||||
if (sibling) {
|
if (sibling) {
|
||||||
this.movePageTo(imgContainer, sibling, true);
|
let movePageCommand = this.movePageTo(imgContainer, sibling, true, true);
|
||||||
|
this._pushUndoClearRedo(movePageCommand);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -35,7 +44,12 @@ class PdfActionsManager {
|
|||||||
var imgContainer = this.getPageContainer(e.target);
|
var imgContainer = this.getPageContainer(e.target);
|
||||||
const sibling = imgContainer.nextSibling;
|
const sibling = imgContainer.nextSibling;
|
||||||
if (sibling) {
|
if (sibling) {
|
||||||
this.movePageTo(imgContainer, sibling.nextSibling, true);
|
let movePageCommand = this.movePageTo(
|
||||||
|
imgContainer,
|
||||||
|
sibling.nextSibling,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
this._pushUndoClearRedo(movePageCommand);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -43,30 +57,27 @@ class PdfActionsManager {
|
|||||||
var imgContainer = this.getPageContainer(e.target);
|
var imgContainer = this.getPageContainer(e.target);
|
||||||
const img = imgContainer.querySelector("img");
|
const img = imgContainer.querySelector("img");
|
||||||
|
|
||||||
this.rotateElement(img, -90);
|
let rotateCommand = this.rotateElement(img, -90);
|
||||||
|
this._pushUndoClearRedo(rotateCommand);
|
||||||
}
|
}
|
||||||
|
|
||||||
rotateCWButtonCallback(e) {
|
rotateCWButtonCallback(e) {
|
||||||
var imgContainer = this.getPageContainer(e.target);
|
var imgContainer = this.getPageContainer(e.target);
|
||||||
const img = imgContainer.querySelector("img");
|
const img = imgContainer.querySelector("img");
|
||||||
|
|
||||||
this.rotateElement(img, 90);
|
let rotateCommand = this.rotateElement(img, 90);
|
||||||
|
this._pushUndoClearRedo(rotateCommand);
|
||||||
}
|
}
|
||||||
|
|
||||||
deletePageButtonCallback(e) {
|
deletePageButtonCallback(e) {
|
||||||
var imgContainer = this.getPageContainer(e.target);
|
let imgContainer = this.getPageContainer(e.target);
|
||||||
this.pagesContainer.removeChild(imgContainer);
|
let deletePageCommand = new DeletePageCommand(
|
||||||
if (this.pagesContainer.childElementCount === 0) {
|
imgContainer,
|
||||||
const filenameInput = document.getElementById("filename-input");
|
this.pagesContainer
|
||||||
const filenameParagraph = document.getElementById("filename");
|
);
|
||||||
const downloadBtn = document.getElementById("export-button");
|
deletePageCommand.execute();
|
||||||
|
|
||||||
filenameInput.disabled = true;
|
this._pushUndoClearRedo(deletePageCommand);
|
||||||
filenameInput.value = "";
|
|
||||||
filenameParagraph.innerText = "";
|
|
||||||
|
|
||||||
downloadBtn.disabled = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
insertFileButtonCallback(e) {
|
insertFileButtonCallback(e) {
|
||||||
@ -81,7 +92,15 @@ class PdfActionsManager {
|
|||||||
|
|
||||||
splitFileButtonCallback(e) {
|
splitFileButtonCallback(e) {
|
||||||
var imgContainer = this.getPageContainer(e.target);
|
var imgContainer = this.getPageContainer(e.target);
|
||||||
imgContainer.classList.toggle("split-before");
|
|
||||||
|
let splitFileCommand = new SplitFileCommand(imgContainer, "split-before");
|
||||||
|
splitFileCommand.execute();
|
||||||
|
|
||||||
|
this._pushUndoClearRedo(splitFileCommand);
|
||||||
|
}
|
||||||
|
|
||||||
|
_pushUndoClearRedo(command) {
|
||||||
|
this.undoManager.pushUndoClearRedo(command);
|
||||||
}
|
}
|
||||||
|
|
||||||
setActions({ movePageTo, addFiles, rotateElement }) {
|
setActions({ movePageTo, addFiles, rotateElement }) {
|
||||||
@ -159,25 +178,10 @@ class PdfActionsManager {
|
|||||||
|
|
||||||
selectCheckbox.onchange = () => {
|
selectCheckbox.onchange = () => {
|
||||||
const pageNumber = Array.from(div.parentNode.children).indexOf(div) + 1;
|
const pageNumber = Array.from(div.parentNode.children).indexOf(div) + 1;
|
||||||
if (selectCheckbox.checked) {
|
let selectPageCommand = new SelectPageCommand(pageNumber, selectCheckbox);
|
||||||
//adds to array of selected pages
|
selectPageCommand.execute();
|
||||||
window.selectedPages.push(pageNumber);
|
|
||||||
} else {
|
|
||||||
//remove page from selected pages array
|
|
||||||
const index = window.selectedPages.indexOf(pageNumber);
|
|
||||||
if (index !== -1) {
|
|
||||||
window.selectedPages.splice(index, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (window.selectedPages.length > 0 && !window.selectPage) {
|
this._pushUndoClearRedo(selectPageCommand);
|
||||||
window.toggleSelectPageVisibility();
|
|
||||||
}
|
|
||||||
if (window.selectedPages.length == 0 && window.selectPage) {
|
|
||||||
window.toggleSelectPageVisibility();
|
|
||||||
}
|
|
||||||
|
|
||||||
window.updateSelectedPagesDisplay();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const insertFileButtonContainer = document.createElement("div");
|
const insertFileButtonContainer = document.createElement("div");
|
||||||
|
@ -1,11 +1,18 @@
|
|||||||
|
import { MovePageUpCommand, MovePageDownCommand } from "./commands/move-page.js";
|
||||||
|
import { RemoveSelectedCommand } from "./commands/remove.js";
|
||||||
|
import { RotateAllCommand, RotateElementCommand } from "./commands/rotate.js";
|
||||||
|
import { SplitAllCommand } from "./commands/split.js";
|
||||||
|
import { UndoManager } from "./UndoManager.js";
|
||||||
|
|
||||||
class PdfContainer {
|
class PdfContainer {
|
||||||
fileName;
|
fileName;
|
||||||
pagesContainer;
|
pagesContainer;
|
||||||
pagesContainerWrapper;
|
pagesContainerWrapper;
|
||||||
pdfAdapters;
|
pdfAdapters;
|
||||||
downloadLink;
|
downloadLink;
|
||||||
|
undoManager;
|
||||||
|
|
||||||
constructor(id, wrapperId, pdfAdapters) {
|
constructor(id, wrapperId, pdfAdapters, undoManager) {
|
||||||
this.pagesContainer = document.getElementById(id);
|
this.pagesContainer = document.getElementById(id);
|
||||||
this.pagesContainerWrapper = document.getElementById(wrapperId);
|
this.pagesContainerWrapper = document.getElementById(wrapperId);
|
||||||
this.downloadLink = null;
|
this.downloadLink = null;
|
||||||
@ -31,6 +38,8 @@ class PdfContainer {
|
|||||||
this.removeAllElements = this.removeAllElements.bind(this);
|
this.removeAllElements = this.removeAllElements.bind(this);
|
||||||
this.resetPages = this.resetPages.bind(this);
|
this.resetPages = this.resetPages.bind(this);
|
||||||
|
|
||||||
|
this.undoManager = undoManager || new UndoManager();
|
||||||
|
|
||||||
this.pdfAdapters = pdfAdapters;
|
this.pdfAdapters = pdfAdapters;
|
||||||
|
|
||||||
this.pdfAdapters.forEach((adapter) => {
|
this.pdfAdapters.forEach((adapter) => {
|
||||||
@ -58,6 +67,33 @@ class PdfContainer {
|
|||||||
window.removeAllElements = this.removeAllElements;
|
window.removeAllElements = this.removeAllElements;
|
||||||
window.resetPages = this.resetPages;
|
window.resetPages = this.resetPages;
|
||||||
|
|
||||||
|
let undoBtn = document.getElementById('undo-btn');
|
||||||
|
let redoBtn = document.getElementById('redo-btn');
|
||||||
|
|
||||||
|
document.addEventListener('undo-manager-update', (e) => {
|
||||||
|
let canUndo = e.detail.canUndo;
|
||||||
|
let canRedo = e.detail.canRedo;
|
||||||
|
|
||||||
|
undoBtn.disabled = !canUndo;
|
||||||
|
redoBtn.disabled = !canRedo;
|
||||||
|
})
|
||||||
|
|
||||||
|
window.undo = () => {
|
||||||
|
if (undoManager.canUndo()) undoManager.undo();
|
||||||
|
else {
|
||||||
|
undoBtn.disabled = !undoManager.canUndo();
|
||||||
|
redoBtn.disabled = !undoManager.canRedo();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window.redo = () => {
|
||||||
|
if (undoManager.canRedo()) undoManager.redo();
|
||||||
|
else {
|
||||||
|
undoBtn.disabled = !undoManager.canUndo();
|
||||||
|
redoBtn.disabled = !undoManager.canRedo();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const filenameInput = document.getElementById("filename-input");
|
const filenameInput = document.getElementById("filename-input");
|
||||||
const downloadBtn = document.getElementById("export-button");
|
const downloadBtn = document.getElementById("export-button");
|
||||||
|
|
||||||
@ -68,32 +104,28 @@ class PdfContainer {
|
|||||||
downloadBtn.disabled = true;
|
downloadBtn.disabled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
movePageTo(startElement, endElement, scrollTo = false) {
|
movePageTo(startElement, endElement, scrollTo = false, moveUp = false) {
|
||||||
const childArray = Array.from(this.pagesContainer.childNodes);
|
let movePageCommand;
|
||||||
const startIndex = childArray.indexOf(startElement);
|
if (moveUp) {
|
||||||
const endIndex = childArray.indexOf(endElement);
|
movePageCommand = new MovePageUpCommand(
|
||||||
|
startElement,
|
||||||
// Check & remove page number elements here too if they exist because Firefox doesn't fire the relevant event on page move.
|
endElement,
|
||||||
const pageNumberElement = startElement.querySelector(".page-number");
|
this.pagesContainer,
|
||||||
if (pageNumberElement) {
|
this.pagesContainerWrapper,
|
||||||
startElement.removeChild(pageNumberElement);
|
scrollTo
|
||||||
}
|
);
|
||||||
|
|
||||||
this.pagesContainer.removeChild(startElement);
|
|
||||||
if (!endElement) {
|
|
||||||
this.pagesContainer.append(startElement);
|
|
||||||
} else {
|
} else {
|
||||||
this.pagesContainer.insertBefore(startElement, endElement);
|
movePageCommand = new MovePageDownCommand(
|
||||||
|
startElement,
|
||||||
|
endElement,
|
||||||
|
this.pagesContainer,
|
||||||
|
this.pagesContainerWrapper,
|
||||||
|
scrollTo
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (scrollTo) {
|
movePageCommand.execute();
|
||||||
const { width } = startElement.getBoundingClientRect();
|
return movePageCommand;
|
||||||
const vector = endIndex !== -1 && startIndex > endIndex ? 0 - width : width;
|
|
||||||
|
|
||||||
this.pagesContainerWrapper.scroll({
|
|
||||||
left: this.pagesContainerWrapper.scrollLeft + vector,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
addFiles(nextSiblingElement, blank = false) {
|
addFiles(nextSiblingElement, blank = false) {
|
||||||
@ -177,15 +209,10 @@ class PdfContainer {
|
|||||||
|
|
||||||
|
|
||||||
rotateElement(element, deg) {
|
rotateElement(element, deg) {
|
||||||
var lastTransform = element.style.rotate;
|
let rotateCommand = new RotateElementCommand(element, deg);
|
||||||
if (!lastTransform) {
|
rotateCommand.execute();
|
||||||
lastTransform = "0";
|
|
||||||
}
|
|
||||||
const lastAngle = parseInt(lastTransform.replace(/[^\d-]/g, ""));
|
|
||||||
const newAngle = lastAngle + deg;
|
|
||||||
|
|
||||||
element.style.rotate = newAngle + "deg";
|
|
||||||
|
|
||||||
|
return rotateCommand;
|
||||||
}
|
}
|
||||||
|
|
||||||
async addPdfFile(renderer, pdfDocument, nextSiblingElement) {
|
async addPdfFile(renderer, pdfDocument, nextSiblingElement) {
|
||||||
@ -280,6 +307,7 @@ class PdfContainer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
rotateAll(deg) {
|
rotateAll(deg) {
|
||||||
|
let elementsToRotate = [];
|
||||||
for (let i = 0; i < this.pagesContainer.childNodes.length; i++) {
|
for (let i = 0; i < this.pagesContainer.childNodes.length; i++) {
|
||||||
const child = this.pagesContainer.children[i];
|
const child = this.pagesContainer.children[i];
|
||||||
if (!child) continue;
|
if (!child) continue;
|
||||||
@ -291,8 +319,13 @@ class PdfContainer {
|
|||||||
const img = child.querySelector("img");
|
const img = child.querySelector("img");
|
||||||
if (!img) continue;
|
if (!img) continue;
|
||||||
|
|
||||||
this.rotateElement(img, deg);
|
elementsToRotate.push(img);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let rotateAllCommand = new RotateAllCommand(elementsToRotate, deg);
|
||||||
|
rotateAllCommand.execute();
|
||||||
|
|
||||||
|
this.undoManager.pushUndoClearRedo(rotateAllCommand);
|
||||||
}
|
}
|
||||||
|
|
||||||
removeAllElements(){
|
removeAllElements(){
|
||||||
@ -307,34 +340,13 @@ class PdfContainer {
|
|||||||
|
|
||||||
deleteSelected() {
|
deleteSelected() {
|
||||||
window.selectedPages.sort((a, b) => a - b);
|
window.selectedPages.sort((a, b) => a - b);
|
||||||
let deletions = 0;
|
let removeSelectedCommand = new RemoveSelectedCommand(
|
||||||
|
this.pagesContainer,
|
||||||
|
window.selectedPages,
|
||||||
|
this.updatePageNumbersAndCheckboxes
|
||||||
|
);
|
||||||
|
|
||||||
window.selectedPages.forEach((pageIndex) => {
|
this.undoManager.pushUndoClearRedo(removeSelectedCommand);
|
||||||
const adjustedIndex = pageIndex - 1 - deletions;
|
|
||||||
const child = this.pagesContainer.children[adjustedIndex];
|
|
||||||
if (child) {
|
|
||||||
this.pagesContainer.removeChild(child);
|
|
||||||
deletions++;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (this.pagesContainer.childElementCount === 0) {
|
|
||||||
const filenameInput = document.getElementById("filename-input");
|
|
||||||
const filenameParagraph = document.getElementById("filename");
|
|
||||||
const downloadBtn = document.getElementById("export-button");
|
|
||||||
|
|
||||||
if (filenameInput)
|
|
||||||
filenameInput.disabled = true;
|
|
||||||
filenameInput.value = "";
|
|
||||||
if (filenameParagraph)
|
|
||||||
filenameParagraph.innerText = "";
|
|
||||||
|
|
||||||
downloadBtn.disabled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
window.selectedPages = [];
|
|
||||||
this.updatePageNumbersAndCheckboxes();
|
|
||||||
document.dispatchEvent(new Event("selectedPagesUpdated"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleSelectAll() {
|
toggleSelectAll() {
|
||||||
@ -502,33 +514,16 @@ class PdfContainer {
|
|||||||
|
|
||||||
splitAll() {
|
splitAll() {
|
||||||
const allPages = this.pagesContainer.querySelectorAll(".page-container");
|
const allPages = this.pagesContainer.querySelectorAll(".page-container");
|
||||||
|
let splitAllCommand = new SplitAllCommand(
|
||||||
|
allPages,
|
||||||
|
window.selectPage,
|
||||||
|
window.selectedPages,
|
||||||
|
"split-before"
|
||||||
|
);
|
||||||
|
splitAllCommand.execute();
|
||||||
|
|
||||||
if (!window.selectPage) {
|
this.undoManager.pushUndoClearRedo(splitAllCommand);
|
||||||
const hasSplit = this.pagesContainer.querySelectorAll(".split-before").length > 0;
|
|
||||||
if (hasSplit) {
|
|
||||||
allPages.forEach(page => {
|
|
||||||
page.classList.remove("split-before");
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
allPages.forEach(page => {
|
|
||||||
page.classList.add("split-before");
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
allPages.forEach((page, index) => {
|
|
||||||
const pageIndex = index;
|
|
||||||
if (window.selectPage && !window.selectedPages.includes(pageIndex)) return;
|
|
||||||
|
|
||||||
if (page.classList.contains("split-before")) {
|
|
||||||
page.classList.remove("split-before");
|
|
||||||
} else {
|
|
||||||
page.classList.add("split-before");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
async splitPDF(baseDocBytes, splitters) {
|
async splitPDF(baseDocBytes, splitters) {
|
||||||
const baseDocument = await PDFLib.PDFDocument.load(baseDocBytes);
|
const baseDocument = await PDFLib.PDFDocument.load(baseDocBytes);
|
||||||
|
65
src/main/resources/static/js/multitool/UndoManager.js
Normal file
65
src/main/resources/static/js/multitool/UndoManager.js
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
export class UndoManager {
|
||||||
|
_undoStack;
|
||||||
|
_redoStack;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this._undoStack = [];
|
||||||
|
this._redoStack = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
pushUndo(command) {
|
||||||
|
this._undoStack.push(command);
|
||||||
|
this._dispatchStateChange();
|
||||||
|
}
|
||||||
|
|
||||||
|
pushRedo(command) {
|
||||||
|
this._redoStack.push(command);
|
||||||
|
this._dispatchStateChange();
|
||||||
|
}
|
||||||
|
|
||||||
|
pushUndoClearRedo(command) {
|
||||||
|
this._undoStack.push(command);
|
||||||
|
this._redoStack = [];
|
||||||
|
this._dispatchStateChange();
|
||||||
|
}
|
||||||
|
|
||||||
|
undo() {
|
||||||
|
if (!this.canUndo()) return;
|
||||||
|
|
||||||
|
let cmd = this._undoStack.pop();
|
||||||
|
cmd.undo();
|
||||||
|
|
||||||
|
this._redoStack.push(cmd);
|
||||||
|
this._dispatchStateChange();
|
||||||
|
}
|
||||||
|
|
||||||
|
canUndo() {
|
||||||
|
return this._undoStack && this._undoStack.length > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
redo() {
|
||||||
|
if (!this.canRedo()) return;
|
||||||
|
|
||||||
|
let cmd = this._redoStack.pop();
|
||||||
|
cmd.redo();
|
||||||
|
|
||||||
|
this._undoStack.push(cmd);
|
||||||
|
this._dispatchStateChange();
|
||||||
|
}
|
||||||
|
|
||||||
|
canRedo() {
|
||||||
|
return this._redoStack && this._redoStack.length > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
_dispatchStateChange() {
|
||||||
|
document.dispatchEvent(
|
||||||
|
new CustomEvent("undo-manager-update", {
|
||||||
|
bubbles: true,
|
||||||
|
detail: {
|
||||||
|
canUndo: this.canUndo(),
|
||||||
|
canRedo: this.canRedo(),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,5 @@
|
|||||||
|
export class Command {
|
||||||
|
execute() {}
|
||||||
|
undo() {}
|
||||||
|
redo() {}
|
||||||
|
}
|
@ -0,0 +1,74 @@
|
|||||||
|
import { Command } from "./command.js";
|
||||||
|
|
||||||
|
export class DeletePageCommand extends Command {
|
||||||
|
constructor(element, pagesContainer) {
|
||||||
|
super();
|
||||||
|
|
||||||
|
this.element = element;
|
||||||
|
this.pagesContainer = pagesContainer;
|
||||||
|
|
||||||
|
this.filenameInputValue = document.getElementById("filename-input").value;
|
||||||
|
|
||||||
|
const filenameParagraph = document.getElementById("filename");
|
||||||
|
this.filenameParagraphText = filenameParagraph ? filenameParagraph.innerText : "";
|
||||||
|
}
|
||||||
|
|
||||||
|
execute() {
|
||||||
|
this.nextSibling = this.element.nextSibling;
|
||||||
|
|
||||||
|
this.pagesContainer.removeChild(this.element);
|
||||||
|
if (this.pagesContainer.childElementCount === 0) {
|
||||||
|
const filenameInput = document.getElementById("filename-input");
|
||||||
|
const filenameParagraph = document.getElementById("filename");
|
||||||
|
const downloadBtn = document.getElementById("export-button");
|
||||||
|
|
||||||
|
filenameInput.disabled = true;
|
||||||
|
filenameInput.value = "";
|
||||||
|
filenameParagraph.innerText = "";
|
||||||
|
|
||||||
|
downloadBtn.disabled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
undo() {
|
||||||
|
let node = this.nextSibling;
|
||||||
|
if (node) this.pagesContainer.insertBefore(this.element, node);
|
||||||
|
else this.pagesContainer.appendChild(this.element);
|
||||||
|
|
||||||
|
const pageNumberElement = this.element.querySelector(".page-number");
|
||||||
|
if (pageNumberElement) {
|
||||||
|
this.element.removeChild(pageNumberElement);
|
||||||
|
}
|
||||||
|
|
||||||
|
const filenameInput = document.getElementById("filename-input");
|
||||||
|
const filenameParagraph = document.getElementById("filename");
|
||||||
|
const downloadBtn = document.getElementById("export-button");
|
||||||
|
|
||||||
|
filenameInput.disabled = false;
|
||||||
|
filenameInput.value = this.filenameInputValue;
|
||||||
|
if (this.filenameParagraph)
|
||||||
|
filenameParagraph.innerText = this.filenameParagraphText;
|
||||||
|
|
||||||
|
downloadBtn.disabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
redo() {
|
||||||
|
const pageNumberElement = this.element.querySelector(".page-number");
|
||||||
|
if (pageNumberElement) {
|
||||||
|
this.element.removeChild(pageNumberElement);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.pagesContainer.removeChild(this.element);
|
||||||
|
if (this.pagesContainer.childElementCount === 0) {
|
||||||
|
const filenameInput = document.getElementById("filename-input");
|
||||||
|
const filenameParagraph = document.getElementById("filename");
|
||||||
|
const downloadBtn = document.getElementById("export-button");
|
||||||
|
|
||||||
|
filenameInput.disabled = true;
|
||||||
|
filenameInput.value = "";
|
||||||
|
filenameParagraph.innerText = "";
|
||||||
|
|
||||||
|
downloadBtn.disabled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
133
src/main/resources/static/js/multitool/commands/move-page.js
Normal file
133
src/main/resources/static/js/multitool/commands/move-page.js
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
import { Command } from "./command.js";
|
||||||
|
|
||||||
|
export class AbstractMovePageCommand extends Command {
|
||||||
|
constructor(
|
||||||
|
startElement,
|
||||||
|
endElement,
|
||||||
|
pagesContainer,
|
||||||
|
pagesContainerWrapper,
|
||||||
|
scrollTo = false
|
||||||
|
) {
|
||||||
|
super();
|
||||||
|
|
||||||
|
this.pagesContainer = pagesContainer;
|
||||||
|
const childArray = Array.from(this.pagesContainer.childNodes);
|
||||||
|
|
||||||
|
this.startIndex = childArray.indexOf(startElement);
|
||||||
|
this.endIndex = childArray.indexOf(endElement);
|
||||||
|
|
||||||
|
this.startElement = startElement;
|
||||||
|
this.endElement = endElement;
|
||||||
|
|
||||||
|
this.scrollTo = scrollTo;
|
||||||
|
this.pagesContainerWrapper = pagesContainerWrapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
execute() {
|
||||||
|
// Check & remove page number elements here too if they exist because Firefox doesn't fire the relevant event on page move.
|
||||||
|
const pageNumberElement = this.startElement.querySelector(".page-number");
|
||||||
|
if (pageNumberElement) {
|
||||||
|
this.startElement.removeChild(pageNumberElement);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.pagesContainer.removeChild(this.startElement);
|
||||||
|
if (!this.endElement) {
|
||||||
|
this.pagesContainer.append(this.startElement);
|
||||||
|
} else {
|
||||||
|
this.pagesContainer.insertBefore(this.startElement, this.endElement);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.scrollTo) {
|
||||||
|
const { width } = this.startElement.getBoundingClientRect();
|
||||||
|
const vector =
|
||||||
|
this.endIndex !== -1 && this.startIndex > this.endIndex
|
||||||
|
? 0 - width
|
||||||
|
: width;
|
||||||
|
|
||||||
|
this.pagesContainerWrapper.scroll({
|
||||||
|
left: this.pagesContainerWrapper.scrollLeft + vector,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
undo() {
|
||||||
|
// Requires overriding in child classes
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
redo() {
|
||||||
|
this.execute();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class MovePageUpCommand extends AbstractMovePageCommand {
|
||||||
|
constructor(
|
||||||
|
startElement,
|
||||||
|
endElement,
|
||||||
|
pagesContainer,
|
||||||
|
pagesContainerWrapper,
|
||||||
|
scrollTo = false
|
||||||
|
) {
|
||||||
|
super(startElement, endElement, pagesContainer, pagesContainerWrapper, scrollTo);
|
||||||
|
}
|
||||||
|
|
||||||
|
undo() {
|
||||||
|
if (this.endElement) {
|
||||||
|
this.pagesContainer.removeChild(this.endElement);
|
||||||
|
this.startElement.insertAdjacentElement("beforebegin", this.endElement);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.scrollTo) {
|
||||||
|
const { width } = this.startElement.getBoundingClientRect();
|
||||||
|
const vector =
|
||||||
|
this.endIndex === -1 || this.startIndex <= this.endIndex
|
||||||
|
? 0 - width
|
||||||
|
: width;
|
||||||
|
|
||||||
|
this.pagesContainerWrapper.scroll({
|
||||||
|
left: this.pagesContainerWrapper.scrollLeft - vector,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
redo() {
|
||||||
|
this.execute();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class MovePageDownCommand extends AbstractMovePageCommand {
|
||||||
|
constructor(
|
||||||
|
startElement,
|
||||||
|
endElement,
|
||||||
|
pagesContainer,
|
||||||
|
pagesContainerWrapper,
|
||||||
|
scrollTo = false
|
||||||
|
) {
|
||||||
|
super(startElement, endElement, pagesContainer, pagesContainerWrapper, scrollTo);
|
||||||
|
}
|
||||||
|
|
||||||
|
undo() {
|
||||||
|
let previousElement = this.startElement.previousSibling;
|
||||||
|
|
||||||
|
if (this.startElement) {
|
||||||
|
this.pagesContainer.removeChild(this.startElement);
|
||||||
|
previousElement.insertAdjacentElement("beforebegin", this.startElement);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.scrollTo) {
|
||||||
|
const { width } = this.startElement.getBoundingClientRect();
|
||||||
|
const vector =
|
||||||
|
this.endIndex === -1 || this.startIndex <= this.endIndex
|
||||||
|
? 0 - width
|
||||||
|
: width;
|
||||||
|
|
||||||
|
this.pagesContainerWrapper.scroll({
|
||||||
|
left: this.pagesContainerWrapper.scrollLeft - vector,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
redo() {
|
||||||
|
this.execute();
|
||||||
|
}
|
||||||
|
}
|
101
src/main/resources/static/js/multitool/commands/remove.js
Normal file
101
src/main/resources/static/js/multitool/commands/remove.js
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
import { Command } from "./command.js";
|
||||||
|
|
||||||
|
export class RemoveSelectedCommand extends Command {
|
||||||
|
constructor(pagesContainer, selectedPages, updatePageNumbersAndCheckboxes) {
|
||||||
|
super();
|
||||||
|
this.pagesContainer = pagesContainer;
|
||||||
|
this.selectedPages = selectedPages;
|
||||||
|
|
||||||
|
this.deletedChildren = [];
|
||||||
|
|
||||||
|
if (updatePageNumbersAndCheckboxes) {
|
||||||
|
this.updatePageNumbersAndCheckboxes = updatePageNumbersAndCheckboxes;
|
||||||
|
} else {
|
||||||
|
const pageDivs = document.querySelectorAll(".pdf-actions_container");
|
||||||
|
|
||||||
|
pageDivs.forEach((div, index) => {
|
||||||
|
const pageNumber = index + 1;
|
||||||
|
const checkbox = div.querySelector(".pdf-actions_checkbox");
|
||||||
|
checkbox.id = `selectPageCheckbox-${pageNumber}`;
|
||||||
|
checkbox.setAttribute("data-page-number", pageNumber);
|
||||||
|
checkbox.checked = window.selectedPages.includes(pageNumber);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const filenameInput = document.getElementById("filename-input");
|
||||||
|
const filenameParagraph = document.getElementById("filename");
|
||||||
|
|
||||||
|
this.originalFilenameInputValue = filenameInput ? filenameInput.value : "";
|
||||||
|
if (filenameParagraph)
|
||||||
|
this.originalFilenameParagraphText = filenameParagraph.innerText;
|
||||||
|
}
|
||||||
|
|
||||||
|
execute() {
|
||||||
|
let deletions = 0;
|
||||||
|
|
||||||
|
this.selectedPages.forEach((pageIndex) => {
|
||||||
|
const adjustedIndex = pageIndex - 1 - deletions;
|
||||||
|
const child = this.pagesContainer.children[adjustedIndex];
|
||||||
|
if (child) {
|
||||||
|
this.pagesContainer.removeChild(child);
|
||||||
|
deletions++;
|
||||||
|
|
||||||
|
this.deletedChildren.push({
|
||||||
|
idx: adjustedIndex,
|
||||||
|
childNode: child,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (this.pagesContainer.childElementCount === 0) {
|
||||||
|
const filenameInput = document.getElementById("filename-input");
|
||||||
|
const filenameParagraph = document.getElementById("filename");
|
||||||
|
const downloadBtn = document.getElementById("export-button");
|
||||||
|
|
||||||
|
if (filenameInput) filenameInput.disabled = true;
|
||||||
|
filenameInput.value = "";
|
||||||
|
if (filenameParagraph) filenameParagraph.innerText = "";
|
||||||
|
|
||||||
|
downloadBtn.disabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
window.selectedPages = [];
|
||||||
|
this.updatePageNumbersAndCheckboxes();
|
||||||
|
document.dispatchEvent(new Event("selectedPagesUpdated"));
|
||||||
|
}
|
||||||
|
|
||||||
|
undo() {
|
||||||
|
while (this.deletedChildren.length > 0) {
|
||||||
|
let deletedChild = this.deletedChildren.pop();
|
||||||
|
if (this.pagesContainer.children.length <= deletedChild.idx)
|
||||||
|
this.pagesContainer.appendChild(deletedChild.childNode);
|
||||||
|
else {
|
||||||
|
this.pagesContainer.insertBefore(
|
||||||
|
deletedChild.childNode,
|
||||||
|
this.pagesContainer.children[deletedChild.idx]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.pagesContainer.childElementCount > 0) {
|
||||||
|
const filenameInput = document.getElementById("filename-input");
|
||||||
|
const filenameParagraph = document.getElementById("filename");
|
||||||
|
const downloadBtn = document.getElementById("export-button");
|
||||||
|
|
||||||
|
if (filenameInput) filenameInput.disabled = false;
|
||||||
|
filenameInput.value = this.originalFilenameInputValue;
|
||||||
|
if (filenameParagraph)
|
||||||
|
filenameParagraph.innerText = this.originalFilenameParagraphText;
|
||||||
|
|
||||||
|
downloadBtn.disabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
window.selectedPages = this.selectedPages;
|
||||||
|
this.updatePageNumbersAndCheckboxes();
|
||||||
|
document.dispatchEvent(new Event("selectedPagesUpdated"));
|
||||||
|
}
|
||||||
|
|
||||||
|
redo() {
|
||||||
|
this.execute();
|
||||||
|
}
|
||||||
|
}
|
74
src/main/resources/static/js/multitool/commands/rotate.js
Normal file
74
src/main/resources/static/js/multitool/commands/rotate.js
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
import { Command } from "./command.js";
|
||||||
|
|
||||||
|
export class RotateElementCommand extends Command {
|
||||||
|
constructor(element, degree) {
|
||||||
|
super();
|
||||||
|
this.element = element;
|
||||||
|
this.degree = degree;
|
||||||
|
}
|
||||||
|
|
||||||
|
execute() {
|
||||||
|
let lastTransform = this.element.style.rotate;
|
||||||
|
if (!lastTransform) {
|
||||||
|
lastTransform = "0";
|
||||||
|
}
|
||||||
|
const lastAngle = parseInt(lastTransform.replace(/[^\d-]/g, ""));
|
||||||
|
const newAngle = lastAngle + parseInt(this.degree);
|
||||||
|
|
||||||
|
this.element.style.rotate = newAngle + "deg";
|
||||||
|
}
|
||||||
|
|
||||||
|
undo() {
|
||||||
|
let lastTransform = this.element.style.rotate;
|
||||||
|
if (!lastTransform) {
|
||||||
|
lastTransform = "0";
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentAngle = parseInt(lastTransform.replace(/[^\d-]/g, ""));
|
||||||
|
const undoAngle = currentAngle + -parseInt(this.degree);
|
||||||
|
|
||||||
|
this.element.style.rotate = undoAngle + "deg";
|
||||||
|
}
|
||||||
|
|
||||||
|
redo() {
|
||||||
|
this.execute();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class RotateAllCommand extends Command {
|
||||||
|
constructor(elements, degree) {
|
||||||
|
super();
|
||||||
|
this.elements = elements;
|
||||||
|
this.degree = degree;
|
||||||
|
}
|
||||||
|
|
||||||
|
execute() {
|
||||||
|
for (let element of this.elements) {
|
||||||
|
let lastTransform = element.style.rotate;
|
||||||
|
if (!lastTransform) {
|
||||||
|
lastTransform = "0";
|
||||||
|
}
|
||||||
|
const lastAngle = parseInt(lastTransform.replace(/[^\d-]/g, ""));
|
||||||
|
const newAngle = lastAngle + this.degree;
|
||||||
|
|
||||||
|
element.style.rotate = newAngle + "deg";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
undo() {
|
||||||
|
for (let element of this.elements) {
|
||||||
|
let lastTransform = element.style.rotate;
|
||||||
|
if (!lastTransform) {
|
||||||
|
lastTransform = "0";
|
||||||
|
}
|
||||||
|
const currentAngle = parseInt(lastTransform.replace(/[^\d-]/g, ""));
|
||||||
|
const undoAngle = currentAngle + -this.degree;
|
||||||
|
|
||||||
|
element.style.rotate = undoAngle + "deg";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
redo() {
|
||||||
|
this.execute();
|
||||||
|
}
|
||||||
|
}
|
59
src/main/resources/static/js/multitool/commands/select.js
Normal file
59
src/main/resources/static/js/multitool/commands/select.js
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
import { Command } from "./command.js";
|
||||||
|
|
||||||
|
export class SelectPageCommand extends Command {
|
||||||
|
constructor(pageNumber, checkbox) {
|
||||||
|
super();
|
||||||
|
this.pageNumber = pageNumber;
|
||||||
|
this.selectCheckbox = checkbox;
|
||||||
|
}
|
||||||
|
|
||||||
|
execute() {
|
||||||
|
if (this.selectCheckbox.checked) {
|
||||||
|
//adds to array of selected pages
|
||||||
|
window.selectedPages.push(this.pageNumber);
|
||||||
|
} else {
|
||||||
|
//remove page from selected pages array
|
||||||
|
const index = window.selectedPages.indexOf(this.pageNumber);
|
||||||
|
if (index !== -1) {
|
||||||
|
window.selectedPages.splice(index, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (window.selectedPages.length > 0 && !window.selectPage) {
|
||||||
|
window.toggleSelectPageVisibility();
|
||||||
|
}
|
||||||
|
if (window.selectedPages.length == 0 && window.selectPage) {
|
||||||
|
window.toggleSelectPageVisibility();
|
||||||
|
}
|
||||||
|
|
||||||
|
window.updateSelectedPagesDisplay();
|
||||||
|
}
|
||||||
|
|
||||||
|
undo() {
|
||||||
|
this.selectCheckbox.checked = !this.selectCheckbox.checked;
|
||||||
|
if (this.selectCheckbox.checked) {
|
||||||
|
//adds to array of selected pages
|
||||||
|
window.selectedPages.push(this.pageNumber);
|
||||||
|
} else {
|
||||||
|
//remove page from selected pages array
|
||||||
|
const index = window.selectedPages.indexOf(this.pageNumber);
|
||||||
|
if (index !== -1) {
|
||||||
|
window.selectedPages.splice(index, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (window.selectedPages.length > 0 && !window.selectPage) {
|
||||||
|
window.toggleSelectPageVisibility();
|
||||||
|
}
|
||||||
|
if (window.selectedPages.length == 0 && window.selectPage) {
|
||||||
|
window.toggleSelectPageVisibility();
|
||||||
|
}
|
||||||
|
|
||||||
|
window.updateSelectedPagesDisplay();
|
||||||
|
}
|
||||||
|
|
||||||
|
redo() {
|
||||||
|
this.selectCheckbox.checked = !this.selectCheckbox.checked;
|
||||||
|
this.execute();
|
||||||
|
}
|
||||||
|
}
|
101
src/main/resources/static/js/multitool/commands/split.js
Normal file
101
src/main/resources/static/js/multitool/commands/split.js
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
import { Command } from "./command.js";
|
||||||
|
|
||||||
|
export class SplitFileCommand extends Command {
|
||||||
|
constructor(element, splitClass) {
|
||||||
|
super();
|
||||||
|
this.element = element;
|
||||||
|
this.splitClass = splitClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
execute() {
|
||||||
|
this.element.classList.toggle(this.splitClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
undo() {
|
||||||
|
this.element.classList.toggle(this.splitClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
redo() {
|
||||||
|
this.execute();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class SplitAllCommand extends Command {
|
||||||
|
constructor(elements, isSelectedInWindow, selectedPages, splitClass) {
|
||||||
|
super();
|
||||||
|
this.elements = elements;
|
||||||
|
this.isSelectedInWindow = isSelectedInWindow;
|
||||||
|
this.selectedPages = selectedPages;
|
||||||
|
this.splitClass = splitClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
execute() {
|
||||||
|
if (!this.isSelectedInWindow) {
|
||||||
|
const hasSplit = this._hasSplit(this.elements, this.splitClass);
|
||||||
|
if (hasSplit) {
|
||||||
|
this.elements.forEach((page) => {
|
||||||
|
page.classList.remove(this.splitClass);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.elements.forEach((page) => {
|
||||||
|
page.classList.add(this.splitClass);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.elements.forEach((page, index) => {
|
||||||
|
const pageIndex = index;
|
||||||
|
if (this.isSelectedInWindow && !this.selectedPages.includes(pageIndex))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (page.classList.contains(this.splitClass)) {
|
||||||
|
page.classList.remove(this.splitClass);
|
||||||
|
} else {
|
||||||
|
page.classList.add(this.splitClass);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
_hasSplit() {
|
||||||
|
if (!this.elements || this.elements.length == 0) return false;
|
||||||
|
|
||||||
|
for (const node of this.elements) {
|
||||||
|
if (node.classList.contains(this.splitClass)) return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
undo() {
|
||||||
|
if (!this.isSelectedInWindow) {
|
||||||
|
const hasSplit = this._hasSplit(this.elements, this.splitClass);
|
||||||
|
if (hasSplit) {
|
||||||
|
this.elements.forEach((page) => {
|
||||||
|
page.classList.remove(this.splitClass);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.elements.forEach((page) => {
|
||||||
|
page.classList.add(this.splitClass);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.elements.forEach((page, index) => {
|
||||||
|
const pageIndex = index;
|
||||||
|
if (this.isSelectedInWindow && !this.selectedPages.includes(pageIndex))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (page.classList.contains(this.splitClass)) {
|
||||||
|
page.classList.remove(this.splitClass);
|
||||||
|
} else {
|
||||||
|
page.classList.add(this.splitClass);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
redo() {
|
||||||
|
this.execute();
|
||||||
|
}
|
||||||
|
}
|
@ -57,6 +57,27 @@
|
|||||||
</span>
|
</span>
|
||||||
<span class="btn-tooltip" th:text="#{multiTool.insertPageBreak}"></span>
|
<span class="btn-tooltip" th:text="#{multiTool.insertPageBreak}"></span>
|
||||||
</button>
|
</button>
|
||||||
|
<button id="undo-btn" class="btn btn-secondary" onclick="undo()" disabled>
|
||||||
|
<span class="material-symbols-rounded">
|
||||||
|
undo
|
||||||
|
</span>
|
||||||
|
<span class="btn-tooltip">
|
||||||
|
<div th:text="#{multiTool.undo}"></div>
|
||||||
|
<div class="text-uppercase" th:text="'(CTRL + Z)'"></div>
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button id="redo-btn" class="btn btn-secondary" onclick="redo()" disabled>
|
||||||
|
<span class="material-symbols-rounded">
|
||||||
|
redo
|
||||||
|
</span>
|
||||||
|
<span class="btn-tooltip">
|
||||||
|
<div th:text="#{multiTool.redo}"></div>
|
||||||
|
<div class="text-uppercase" th:text="'(CTRL + Y)'"></div>
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
</button>
|
||||||
|
|
||||||
<button id="select-pages-container" class="btn btn-secondary enable-on-file"
|
<button id="select-pages-container" class="btn btn-secondary enable-on-file"
|
||||||
onclick="toggleSelectPageVisibility()" disabled>
|
onclick="toggleSelectPageVisibility()" disabled>
|
||||||
<span id="select-pages-button" class="material-symbols-rounded">
|
<span id="select-pages-button" class="material-symbols-rounded">
|
||||||
@ -139,7 +160,9 @@
|
|||||||
split: '[[#{multiTool.split}]]',
|
split: '[[#{multiTool.split}]]',
|
||||||
addFile: '[[#{multiTool.addFile}]]',
|
addFile: '[[#{multiTool.addFile}]]',
|
||||||
insertPageBreak:'[[#{multiTool.insertPageBreak}]]',
|
insertPageBreak:'[[#{multiTool.insertPageBreak}]]',
|
||||||
dragDropMessage:'[[#{multiTool.dragDropMessage}]]'
|
dragDropMessage:'[[#{multiTool.dragDropMessage}]]',
|
||||||
|
undo: '[[#{multiTool.undo}]]',
|
||||||
|
redo: '[[#{multiTool.redo}]]'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -155,17 +178,20 @@
|
|||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
<script type="module">
|
<script type="module">
|
||||||
|
import { UndoManager } from './js/multitool/UndoManager.js';
|
||||||
import PdfContainer from './js/multitool/PdfContainer.js';
|
import PdfContainer from './js/multitool/PdfContainer.js';
|
||||||
import DragDropManager from "./js/multitool/DragDropManager.js";
|
import DragDropManager from "./js/multitool/DragDropManager.js";
|
||||||
import ImageHighlighter from "./js/multitool/ImageHighlighter.js";
|
import ImageHighlighter from "./js/multitool/ImageHighlighter.js";
|
||||||
import PdfActionsManager from './js/multitool/PdfActionsManager.js';
|
import PdfActionsManager from './js/multitool/PdfActionsManager.js';
|
||||||
import FileDragManager from './js/multitool/fileInput.js';
|
import FileDragManager from './js/multitool/fileInput.js';
|
||||||
// enables drag and drop
|
// enables drag and drop
|
||||||
|
|
||||||
|
var undoManager = new UndoManager();
|
||||||
const dragDropManager = new DragDropManager('drag-container', 'pages-container');
|
const dragDropManager = new DragDropManager('drag-container', 'pages-container');
|
||||||
// enables image highlight on click
|
// enables image highlight on click
|
||||||
const imageHighlighter = new ImageHighlighter('image-highlighter');
|
const imageHighlighter = new ImageHighlighter('image-highlighter');
|
||||||
// enables the default action buttons on each file
|
// enables the default action buttons on each file
|
||||||
const pdfActionsManager = new PdfActionsManager('pages-container');
|
const pdfActionsManager = new PdfActionsManager('pages-container', undoManager);
|
||||||
const fileDragManager = new FileDragManager();
|
const fileDragManager = new FileDragManager();
|
||||||
// Scroll the wrapper horizontally
|
// Scroll the wrapper horizontally
|
||||||
|
|
||||||
@ -178,10 +204,23 @@
|
|||||||
imageHighlighter,
|
imageHighlighter,
|
||||||
pdfActionsManager,
|
pdfActionsManager,
|
||||||
fileDragManager
|
fileDragManager
|
||||||
]
|
],
|
||||||
|
undoManager
|
||||||
)
|
)
|
||||||
|
|
||||||
fileDragManager.setCallback(async (files) => pdfContainer.addFilesFromFiles(files));
|
fileDragManager.setCallback(async (files) => pdfContainer.addFilesFromFiles(files));
|
||||||
|
document.addEventListener('keydown', function(event) {
|
||||||
|
let targetElementId = event.target.id;
|
||||||
|
|
||||||
|
// To avoid undoing/redoing the page when the user is simply undoing/redoing text
|
||||||
|
const isFilenameInputField = (targetElementId === 'filename-input') && (event.target === document.activeElement);
|
||||||
|
|
||||||
|
const isUndo = (event.ctrlKey && event.key === 'z');
|
||||||
|
const isRedo = (event.ctrlKey && event.key == 'y');
|
||||||
|
if (isUndo && !isFilenameInputField)
|
||||||
|
undoManager.undo();
|
||||||
|
else if (isRedo && !isFilenameInputField) undoManager.redo();
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user