diff --git a/Kitodo/src/main/java/org/kitodo/production/forms/dataeditor/GalleryPanel.java b/Kitodo/src/main/java/org/kitodo/production/forms/dataeditor/GalleryPanel.java index fa4252b148e..e7259e0afb0 100644 --- a/Kitodo/src/main/java/org/kitodo/production/forms/dataeditor/GalleryPanel.java +++ b/Kitodo/src/main/java/org/kitodo/production/forms/dataeditor/GalleryPanel.java @@ -51,7 +51,6 @@ import org.kitodo.production.services.ServiceManager; import org.kitodo.production.services.file.FileService; import org.primefaces.PrimeFaces; -import org.primefaces.event.DragDropEvent; import org.primefaces.model.DefaultStreamedContent; import org.primefaces.model.StreamedContent; @@ -210,11 +209,15 @@ public List getStripes() { * @param event * JSF drag'n'drop event description object */ - public void onPageDrop(DragDropEvent event) { - int toStripeIndex = getDropStripeIndex(event); - - if (toStripeIndex == -1 || !dragStripeIndexMatches(event)) { - logger.error("Unsupported drag'n'drop event from {} to {}", event.getDragId(), event.getDropId()); + public void onPageDrop() { + FacesContext context = FacesContext.getCurrentInstance(); + Map params = context.getExternalContext().getRequestParameterMap(); + String dragId = params.get("dragId"); + String dropId = params.get("dropId"); + + int toStripeIndex = getDropStripeIndex(dropId); + if (toStripeIndex == -1 || !dragStripeIndexMatches(dragId)) { + logger.error("Unsupported drag'n'drop event from {} to {}", dragId, dropId); return; } @@ -232,7 +235,7 @@ public void onPageDrop(DragDropEvent event) { viewsToBeMoved.sort(IMAGE_ORDER_COMPARATOR); - int toMediaIndex = getMediaIndex(event); + int toMediaIndex = getMediaIndex(dropId); try { updateData(toStripe, viewsToBeMoved, toMediaIndex); } catch (Exception e) { @@ -244,25 +247,25 @@ public void onPageDrop(DragDropEvent event) { updateAffectedStripes(toStripe, viewsToBeMoved); } - private boolean dragStripeIndexMatches(DragDropEvent event) { - Matcher dragStripeImageMatcher = DRAG_STRIPE_IMAGE.matcher(event.getDragId()); - Matcher dragUnstructuredMediaMatcher = DRAG_UNSTRUCTURED_MEDIA.matcher(event.getDragId()); + private boolean dragStripeIndexMatches(String dragId) { + Matcher dragStripeImageMatcher = DRAG_STRIPE_IMAGE.matcher(dragId); + Matcher dragUnstructuredMediaMatcher = DRAG_UNSTRUCTURED_MEDIA.matcher(dragId); return dragUnstructuredMediaMatcher.matches() || dragStripeImageMatcher.matches(); } - private int getDropStripeIndex(DragDropEvent event) { + private int getDropStripeIndex(String dropId) { // empty stripe of structure element - Matcher dropStripeMatcher = DROP_STRIPE.matcher(event.getDropId()); + Matcher dropStripeMatcher = DROP_STRIPE.matcher(dropId); // between two pages of structure element - Matcher dropMediaAreaMatcher = DROP_MEDIA_AREA.matcher(event.getDropId()); + Matcher dropMediaAreaMatcher = DROP_MEDIA_AREA.matcher(dropId); // after last page of structure element - Matcher dropMediaLastAreaMatcher = DROP_MEDIA_LAST_AREA.matcher(event.getDropId()); + Matcher dropMediaLastAreaMatcher = DROP_MEDIA_LAST_AREA.matcher(dropId); // empty unstructured media stripe - Matcher dropUnstructuredMediaStripeMatcher = DROP_UNSTRUCTURED_STRIPE.matcher(event.getDropId()); + Matcher dropUnstructuredMediaStripeMatcher = DROP_UNSTRUCTURED_STRIPE.matcher(dropId); // between two pages of unstructured media stripe - Matcher dropUnstructuredMediaAreaMatcher = DROP_UNSTRUCTURED_MEDIA_AREA.matcher(event.getDropId()); + Matcher dropUnstructuredMediaAreaMatcher = DROP_UNSTRUCTURED_MEDIA_AREA.matcher(dropId); // after last page of unstructured media stripe - Matcher dropUnstructuredMediaLastAreaMatcher = DROP_UNSTRUCTURED_MEDIA_LAST_AREA.matcher(event.getDropId()); + Matcher dropUnstructuredMediaLastAreaMatcher = DROP_UNSTRUCTURED_MEDIA_LAST_AREA.matcher(dropId); if (dropStripeMatcher.matches()) { return Integer.parseInt(dropStripeMatcher.group(1)); } else if (dropMediaAreaMatcher.matches()) { @@ -279,11 +282,11 @@ private int getDropStripeIndex(DragDropEvent event) { } } - private int getMediaIndex(DragDropEvent event) { - Matcher dropMediaAreaMatcher = DROP_MEDIA_AREA.matcher(event.getDropId()); - Matcher dropMediaLastAreaMatcher = DROP_MEDIA_LAST_AREA.matcher(event.getDropId()); - Matcher dropUnstructuredMediaAreaMatcher = DROP_UNSTRUCTURED_MEDIA_AREA.matcher(event.getDropId()); - Matcher dropUnstructuredMediaLastAreaMatcher = DROP_UNSTRUCTURED_MEDIA_LAST_AREA.matcher(event.getDropId()); + private int getMediaIndex(String dropId) { + Matcher dropMediaAreaMatcher = DROP_MEDIA_AREA.matcher(dropId); + Matcher dropMediaLastAreaMatcher = DROP_MEDIA_LAST_AREA.matcher(dropId); + Matcher dropUnstructuredMediaAreaMatcher = DROP_UNSTRUCTURED_MEDIA_AREA.matcher(dropId); + Matcher dropUnstructuredMediaLastAreaMatcher = DROP_UNSTRUCTURED_MEDIA_LAST_AREA.matcher(dropId); if (dropMediaAreaMatcher.matches()) { return Integer.parseInt(dropMediaAreaMatcher.group(2)); } else if (dropMediaLastAreaMatcher.matches()) { diff --git a/Kitodo/src/main/webapp/WEB-INF/resources/js/metadataeditor_gallery_drag_drop.js b/Kitodo/src/main/webapp/WEB-INF/resources/js/metadataeditor_gallery_drag_drop.js new file mode 100644 index 00000000000..3fe83fb7e7f --- /dev/null +++ b/Kitodo/src/main/webapp/WEB-INF/resources/js/metadataeditor_gallery_drag_drop.js @@ -0,0 +1,95 @@ +/** + * (c) Kitodo. Key to digital objects e. V. + * + * This file is part of the Kitodo project. + * + * It is licensed under GNU General Public License version 3 or later. + * + * For the full copyright and license information, please read the + * GPL3-License.txt file that was distributed with this source code. + */ + +/* global triggerOnPageDrop, PrimeFaces */ + +/** + * Registers event handler that makes relevant components of gallery + * draggable and droppable via jquery-ui. + * + * This is much faster than adding individual and + * primefaces components, which would initialize drag and + * drop capabilities individually for each component. + * Additionally, this reduces the form complexity of the gallery view, which + * results in faster form updates for very large galleries. + */ +function registerMakeDragAndDroppable() { + + // define debouce function that will allow to make components draggable in one go + // after all of them have been updated via Primefaces when reloading the gallery + function makeDebounced(func, timeout = 100) { + let timer = null; + return function () { + clearTimeout(timer); + timer = setTimeout(func, timeout); + }; + } + + function onDrop(event, ui) { + let dragId = ui.draggable.attr('id'); + let dropId = $(event.target).attr('id'); + // trigger remote command, see gallery.xhtml and GalleryPanel.onPageDrop + triggerOnPageDrop([{"name": "dragId", "value": dragId}, {"name": "dropId", "value": dropId}]); + } + + // apply jquery draggable and droppable to the relevant dom elements + let makeDragAndDroppable = makeDebounced(function () { + // make individual pages draggable + $("#imagePreviewForm\\:structuredPages .draggableStructurePagePanel").draggable({ + scope: "assignedPagesDroppable", + stack: ".ui-panel", + revert: true, + }); + $("#imagePreviewForm\\:unstructuredMediaList .draggableUnstructuredMediaPanel").draggable({ + scope: "assignedPagesDroppable", + stack: ".ui-panel", + revert: true, + }); + + // make droppable containers before and after each page + $("#imagePreviewForm\\:structuredPages .page-drop-area").droppable({ + scope: "assignedPagesDroppable", + activeClass: "media-stripe-index-active", + drop: onDrop, + }); + $("#imagePreviewForm\\:unstructuredMediaList .page-drop-area").droppable({ + scope: "assignedPagesDroppable", + activeClass: "media-stripe-index-active", + drop: onDrop, + }); + + // make droppable container for empty stripes + $("#imagePreviewForm\\:structuredPages .structureElementDataList").has(".ui-datalist-empty-message").droppable({ + scope: "assignedPagesDroppable", + activeClass: "media-stripe-active", + drop: onDrop, + }); + $("#imagePreviewForm\\:unstructuredMediaList").has(".ui-datalist-empty-message").droppable({ + scope: "assignedPagesDroppable", + activeClass: "media-stripe-active", + drop: onDrop, + }); + }); + + // update components after they have been updated via PrimeFaces + let backupFunc = PrimeFaces.ajax.Utils.updateElement; + PrimeFaces.ajax.Utils.updateElement = function() { + backupFunc.apply(this, arguments); + makeDragAndDroppable(); + }; + + // make components draggable the very first time the gallery loads + makeDragAndDroppable(); +} + +$(function() { + registerMakeDragAndDroppable(); +}); diff --git a/Kitodo/src/main/webapp/WEB-INF/templates/includes/metadataEditor/gallery.xhtml b/Kitodo/src/main/webapp/WEB-INF/templates/includes/metadataEditor/gallery.xhtml index de3b45df375..7ed70ef555c 100644 --- a/Kitodo/src/main/webapp/WEB-INF/templates/includes/metadataEditor/gallery.xhtml +++ b/Kitodo/src/main/webapp/WEB-INF/templates/includes/metadataEditor/gallery.xhtml @@ -25,6 +25,14 @@ + + + +