Updated to Drupal 8.5. Core Media not yet in use.
[yaffs-website] / web / modules / contrib / paragraphs / js / paragraphs.dragdrop.js
diff --git a/web/modules/contrib/paragraphs/js/paragraphs.dragdrop.js b/web/modules/contrib/paragraphs/js/paragraphs.dragdrop.js
new file mode 100644 (file)
index 0000000..8c6a8ee
--- /dev/null
@@ -0,0 +1,228 @@
+/**
+ * @file
+ * Paragraphs drag and drop handling and integration with the Sortable library.
+ */
+
+(function ($, Drupal) {
+
+  'use strict';
+
+  /**
+   * jQuery plugin for Sortable
+   *
+   * Registers Sortable under a custom name to prevent a collision with jQuery
+   * UI.
+   *
+   * @param   {Object|String} options
+   * @param   {..*}           [args]
+   * @returns {jQuery|*}
+   */
+  $.fn.paragraphsSortable = function (options) {
+    var retVal,
+      args = arguments;
+
+    this.each(function () {
+      var $el = $(this),
+        sortable = $el.data('sortable');
+
+      if (!sortable && (options instanceof Object || !options)) {
+        sortable = new Sortable(this, options);
+        $el.data('sortable', sortable);
+      }
+
+      if (sortable) {
+        if (options === 'widget') {
+          return sortable;
+        }
+        else if (options === 'destroy') {
+          sortable.destroy();
+          $el.removeData('sortable');
+        }
+        else if (typeof sortable[options] === 'function') {
+          retVal = sortable[options].apply(sortable, [].slice.call(args, 1));
+        }
+        else if (options in sortable.options) {
+          retVal = sortable.option.apply(sortable, args);
+        }
+      }
+    });
+
+    return (retVal === void 0) ? this : retVal;
+  };
+
+
+  Drupal.behaviors.paragraphsDraggable = {
+    attach: function (context) {
+
+      // Initialize drag and drop.
+      $('ul.paragraphs-dragdrop', context).each(function (i, item) {
+        $(item).paragraphsSortable({
+          group: "paragraphs",
+          sort: true,
+          handle: ".tabledrag-handle",
+          onMove: isAllowed,
+          onEnd: handleReorder
+        });
+      });
+
+      /**
+       * Callback to update weight and path information.
+       *
+       * @param evt
+       *   The Sortable event.
+       */
+      function handleReorder(evt) {
+        var $item = $(evt.item);
+        var $parent = $item.closest('.paragraphs-dragdrop');
+        var $children = $parent.children('li');
+        var $srcParent = $(evt.to);
+        var $srcChildren = $srcParent.children('li');
+
+        // Update both the source and target children.
+        updateWeightsAndPath($srcChildren);
+        updateWeightsAndPath($children);
+      }
+
+
+      /**
+       * Update weight and recursively update path of the provided paragraphs.
+       *
+       * @param $items
+       *   Drag and drop items.
+       */
+      function updateWeightsAndPath($items) {
+        $items.each(function (index, value) {
+
+          // Update the weight in the weight of the current element, avoid
+          // matching child weights by selecting the first.
+          var $currentItem = $(value);
+          var $weight = $currentItem.find('.paragraphs-dragdrop__weight:first');
+          $weight.val(index);
+
+          // Update the path of the current element and then update all nested
+          // elements.
+          updatePaths($currentItem, $currentItem.parent());
+          $currentItem.find('> div > ul').each(function () {
+            updateNestedPath(this, index, $currentItem);
+          });
+        })
+      }
+
+      /**
+       * Update the path field based on the parent.
+       *
+       * @param $item
+       *   A list item.
+       * @param $parent
+       *   The parent of the list item.
+       */
+      function updatePaths($item, $parent) {
+        // Select the first path field which is the one from the current
+        // element.
+        var $pathField = $item.find('.paragraphs-dragdrop__path:first');
+        var newPath = $parent.attr('data-paragraphs-dragdrop-path');
+        $pathField.val(newPath);
+      }
+
+      /**
+       * Update nested paragraphs for a field/list.
+       *
+       * @param childList
+       *   The paragraph field/list, parent of the children to be updated.
+       * @param parentIndex
+       *   The index of the parent list item.
+       * @param $parentListItem
+       *   The parent list item.
+       */
+      function updateNestedPath(childList, parentIndex, $parentListItem) {
+
+        var sortablePath = childList.getAttribute('data-paragraphs-dragdrop-path');
+        var newParent = $parentListItem.parent().attr('data-paragraphs-dragdrop-path');
+
+        // Update the data attribute of the list based on the parent index and
+        // list item.
+        sortablePath = newParent + "][" + parentIndex + sortablePath.substr(sortablePath.lastIndexOf("]"));
+        childList.setAttribute('data-paragraphs-dragdrop-path', sortablePath);
+
+        // Now update the children.
+        $(childList).children().each(function (childIndex) {
+          var $childListItem = $(this);
+          updatePaths($childListItem, $(childList), childIndex);
+          $(this).find('> div > ul').each(function () {
+            var nestedChildList = this;
+            updateNestedPath(nestedChildList, childIndex, $childListItem);
+          });
+        });
+      }
+
+
+      /**
+       * Callback to check if a paragraph item can be dropped into a position.
+       *
+       * @param evt
+       *   The Sortable event.
+       * @param originalEvent
+       *   The original Sortable event.
+       *
+       * @returns {boolean|*}
+       *   True if the type is allowed and there is enough room.
+       */
+      function isAllowed(evt, originalEvent) {
+        var dragee = evt.dragged;
+        var target = evt.to;
+        var drageeType = dragee.dataset.paragraphsDragdropBundle;
+        var allowedTypes = target.dataset.paragraphsDragdropAllowedTypes;
+        var hasSameContainer = evt.to === evt.from;
+        return hasSameContainer || (contains(drageeType, allowedTypes) && hasRoom(target));
+      }
+
+      /**
+       * Checks if the target has room.
+       *
+       * @param target
+       *   The target list/paragraph field.
+       *
+       * @returns {boolean}
+       *   True if the field is unlimited or limit is not reached yet.
+       */
+      function hasRoom(target) {
+
+        var cardinality = target.dataset.paragraphsDragdropCardinality;
+        var occupants = target.childNodes.length;
+        var isLimited = parseInt(cardinality, 10) !== -1;
+        var hasRoom = cardinality > occupants;
+
+        return hasRoom || !isLimited;
+      }
+
+      /**
+       * Checks if the paragraph type is allowed in the target type list.
+       *
+       * @param candidate
+       *   The paragraph type.
+       * @param set
+       *   Comma separated list of target types.
+       *
+       * @returns {boolean}
+       *   TRUE if the target type is allowed.
+       */
+      function contains(candidate, set) {
+        set = set.split(',');
+        var l = set.length;
+
+        for(var i = 0; i < l; i++) {
+          if(set[i] === candidate) {
+            return true;
+          }
+        }
+        return false;
+      }
+
+      // Fix for an iOS 10 bug. Binding empty event handler on the touchmove
+      // event.
+      window.addEventListener('touchmove', function () {
+      })
+    }
+  }
+
+})(jQuery, Drupal);