(function () {
  'use strict';

  function _defineProperties(target, props) {
    for (var i = 0; i < props.length; i++) {
      var descriptor = props[i];
      descriptor.enumerable = descriptor.enumerable || false;
      descriptor.configurable = true;
      if ("value" in descriptor) descriptor.writable = true;
      Object.defineProperty(target, descriptor.key, descriptor);
    }
  }

  function _createClass(Constructor, protoProps, staticProps) {
    if (protoProps) _defineProperties(Constructor.prototype, protoProps);
    if (staticProps) _defineProperties(Constructor, staticProps);
    return Constructor;
  }

  function _inheritsLoose(subClass, superClass) {
    subClass.prototype = Object.create(superClass.prototype);
    subClass.prototype.constructor = subClass;

    _setPrototypeOf(subClass, superClass);
  }

  function _getPrototypeOf(o) {
    _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) {
      return o.__proto__ || Object.getPrototypeOf(o);
    };
    return _getPrototypeOf(o);
  }

  function _setPrototypeOf(o, p) {
    _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) {
      o.__proto__ = p;
      return o;
    };

    return _setPrototypeOf(o, p);
  }

  function _isNativeReflectConstruct() {
    if (typeof Reflect === "undefined" || !Reflect.construct) return false;
    if (Reflect.construct.sham) return false;
    if (typeof Proxy === "function") return true;

    try {
      Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {}));
      return true;
    } catch (e) {
      return false;
    }
  }

  function _construct(Parent, args, Class) {
    if (_isNativeReflectConstruct()) {
      _construct = Reflect.construct;
    } else {
      _construct = function _construct(Parent, args, Class) {
        var a = [null];
        a.push.apply(a, args);
        var Constructor = Function.bind.apply(Parent, a);
        var instance = new Constructor();
        if (Class) _setPrototypeOf(instance, Class.prototype);
        return instance;
      };
    }

    return _construct.apply(null, arguments);
  }

  function _isNativeFunction(fn) {
    return Function.toString.call(fn).indexOf("[native code]") !== -1;
  }

  function _wrapNativeSuper(Class) {
    var _cache = typeof Map === "function" ? new Map() : undefined;

    _wrapNativeSuper = function _wrapNativeSuper(Class) {
      if (Class === null || !_isNativeFunction(Class)) return Class;

      if (typeof Class !== "function") {
        throw new TypeError("Super expression must either be null or a function");
      }

      if (typeof _cache !== "undefined") {
        if (_cache.has(Class)) return _cache.get(Class);

        _cache.set(Class, Wrapper);
      }

      function Wrapper() {
        return _construct(Class, arguments, _getPrototypeOf(this).constructor);
      }

      Wrapper.prototype = Object.create(Class.prototype, {
        constructor: {
          value: Wrapper,
          enumerable: false,
          writable: true,
          configurable: true
        }
      });
      return _setPrototypeOf(Wrapper, Class);
    };

    return _wrapNativeSuper(Class);
  }

  function _assertThisInitialized(self) {
    if (self === void 0) {
      throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
    }

    return self;
  }

  /**
   * @copyright  (C) 2019 Open Source Matters, Inc. <https://www.joomla.org>
   * @license    GNU General Public License version 2 or later; see LICENSE.txt
   */
  (function (customElements) {
    var KEYCODE = {
      SPACE: 32,
      ESC: 27,
      ENTER: 13
    };
    /**
     * Helper for testing whether a selection modifier is pressed
     * @param {Event} event
     *
     * @returns {boolean|*}
     */

    function hasModifier(event) {
      return event.ctrlKey || event.metaKey || event.shiftKey;
    }

    var JoomlaFieldSubform = /*#__PURE__*/function (_HTMLElement) {
      _inheritsLoose(JoomlaFieldSubform, _HTMLElement);

      function JoomlaFieldSubform() {
        var _this;

        _this = _HTMLElement.call(this) || this;

        var that = _assertThisInitialized(_this); // Get the rows container


        _this.containerWithRows = _assertThisInitialized(_this);

        if (_this.rowsContainer) {
          var allContainers = _this.querySelectorAll(_this.rowsContainer); // Find closest, and exclude nested


          Array.from(allContainers).forEach(function (container) {
            if (container.closest('joomla-field-subform') === _assertThisInitialized(_this)) {
              _this.containerWithRows = container;
            }
          });
        } // Keep track of row index, this is important to avoid a name duplication
        // Note: php side should reset the indexes each time, eg: $value = array_values($value);


        _this.lastRowIndex = _this.getRows().length - 1; // Template for the repeating group

        _this.template = ''; // Prepare a row template, and find available field names

        _this.prepareTemplate(); // Bind buttons


        if (_this.buttonAdd || _this.buttonRemove) {
          _this.addEventListener('click', function (event) {
            var btnAdd = null;
            var btnRem = null;

            if (that.buttonAdd) {
              btnAdd = event.target.matches(that.buttonAdd) ? event.target : event.target.closest(that.buttonAdd);
            }

            if (that.buttonRemove) {
              btnRem = event.target.matches(that.buttonRemove) ? event.target : event.target.closest(that.buttonRemove);
            } // Check active, with extra check for nested joomla-field-subform


            if (btnAdd && btnAdd.closest('joomla-field-subform') === that) {
              var row = btnAdd.closest(that.repeatableElement);
              row = row && row.closest('joomla-field-subform') === that ? row : null;
              that.addRow(row);
              event.preventDefault();
            } else if (btnRem && btnRem.closest('joomla-field-subform') === that) {
              var _row = btnRem.closest(that.repeatableElement);

              that.removeRow(_row);
              event.preventDefault();
            }
          });

          _this.addEventListener('keydown', function (event) {
            if (event.keyCode !== KEYCODE.SPACE) return;
            var isAdd = that.buttonAdd && event.target.matches(that.buttonAdd);
            var isRem = that.buttonRemove && event.target.matches(that.buttonRemove);

            if ((isAdd || isRem) && event.target.closest('joomla-field-subform') === that) {
              var row = event.target.closest(that.repeatableElement);
              row = row && row.closest('joomla-field-subform') === that ? row : null;

              if (isRem && row) {
                that.removeRow(row);
              } else if (isAdd) {
                that.addRow(row);
              }

              event.preventDefault();
            }
          });
        } // Sorting


        if (_this.buttonMove) {
          _this.setUpDragSort();
        }

        return _this;
      }
      /**
       * Search for existing rows
       * @returns {HTMLElement[]}
       */


      var _proto = JoomlaFieldSubform.prototype;

      _proto.getRows = function getRows() {
        var _this2 = this;

        var rows = Array.from(this.containerWithRows.children);
        var result = []; // Filter out the rows

        rows.forEach(function (row) {
          if (row.matches(_this2.repeatableElement)) {
            result.push(row);
          }
        });
        return result;
      }
      /**
       * Prepare a row template
       */
      ;

      _proto.prepareTemplate = function prepareTemplate() {
        var tmplElement = [].slice.call(this.children).filter(function (el) {
          return el.classList.contains('subform-repeatable-template-section');
        });

        if (tmplElement[0]) {
          this.template = tmplElement[0].innerHTML;
        }

        if (!this.template) {
          throw new Error('The row template is required for the subform element to work');
        }
      }
      /**
       * Add new row
       * @param {HTMLElement} after
       * @returns {HTMLElement}
       */
      ;

      _proto.addRow = function addRow(after) {
        // Count how many we already have
        var count = this.getRows().length;

        if (count >= this.maximum) {
          return null;
        } // Make a new row from the template


        var tmpEl;

        if (this.containerWithRows.nodeName === 'TBODY' || this.containerWithRows.nodeName === 'TABLE') {
          tmpEl = document.createElement('tbody');
        } else {
          tmpEl = document.createElement('div');
        }

        tmpEl.innerHTML = this.template;
        var row = tmpEl.children[0]; // Add to container

        if (after) {
          after.parentNode.insertBefore(row, after.nextSibling);
        } else {
          this.containerWithRows.append(row);
        } // Add draggable attributes


        if (this.buttonMove) {
          row.setAttribute('draggable', 'false');
          row.setAttribute('aria-grabbed', 'false');
          row.setAttribute('tabindex', '0');
        } // Marker that it is new


        row.setAttribute('data-new', '1'); // Fix names and ids, and reset values

        this.fixUniqueAttributes(row, count); // Tell about the new row

        this.dispatchEvent(new CustomEvent('subform-row-add', {
          detail: {
            row: row
          },
          bubbles: true
        }));
        row.dispatchEvent(new CustomEvent('joomla:updated', {
          bubbles: true,
          cancelable: true
        }));
        return row;
      }
      /**
       * Remove the row
       * @param {HTMLElement} row
       */
      ;

      _proto.removeRow = function removeRow(row) {
        // Count how much we have
        var count = this.getRows().length;

        if (count <= this.minimum) {
          return;
        } // Tell about the row will be removed


        this.dispatchEvent(new CustomEvent('subform-row-remove', {
          detail: {
            row: row
          },
          bubbles: true
        }));
        row.dispatchEvent(new CustomEvent('joomla:removed', {
          bubbles: true,
          cancelable: true
        }));
        row.parentNode.removeChild(row);
      }
      /**
       * Fix name and id for fields that are in the row
       * @param {HTMLElement} row
       * @param {Number} count
       */
      ;

      _proto.fixUniqueAttributes = function fixUniqueAttributes(row, count) {
        var _this3 = this;

        var countTmp = count || 0;
        var group = row.getAttribute('data-group'); // current group name

        var basename = row.getAttribute('data-base-name');
        var countnew = Math.max(this.lastRowIndex, countTmp);
        var groupnew = basename + countnew; // new group name

        this.lastRowIndex = countnew + 1;
        row.setAttribute('data-group', groupnew); // Fix inputs that have a "name" attribute

        var haveName = row.querySelectorAll('[name]');
        var ids = {}; // Collect id for fix checkboxes and radio
        // Filter out nested

        haveName = [].slice.call(haveName).filter(function (el) {
          if (el.nodeName === 'JOOMLA-FIELD-SUBFORM') {
            // Skip self in .closest() call
            return el.parentElement.closest('joomla-field-subform') === _this3;
          }

          return el.closest('joomla-field-subform') === _this3;
        });
        haveName.forEach(function (elem) {
          var $el = elem;
          var name = $el.getAttribute('name');
          var aria = $el.getAttribute('aria-describedby');
          var id = name.replace(/(\[\]$)/g, '').replace(/(\]\[)/g, '__').replace(/\[/g, '_').replace(/\]/g, ''); // id from name

          var nameNew = name.replace("[" + group + "][", "[" + groupnew + "]["); // New name

          var idNew = id.replace(group, groupnew).replace(/\W/g, '_'); // Count new id

          var countMulti = 0; // count for multiple radio/checkboxes

          var forOldAttr = id; // Fix "for" in the labels

          if ($el.type === 'checkbox' && name.match(/\[\]$/)) {
            // <input type="checkbox" name="name[]"> fix
            // Recount id
            countMulti = ids[id] ? ids[id].length : 0;

            if (!countMulti) {
              // Set the id for fieldset and group label
              var fieldset = $el.closest('fieldset.checkboxes');
              var elLbl = row.querySelector("label[for=\"" + id + "\"]");

              if (fieldset) {
                fieldset.setAttribute('id', idNew);
              }

              if (elLbl) {
                elLbl.setAttribute('for', idNew);
                elLbl.setAttribute('id', idNew + "-lbl");
              }
            }

            forOldAttr += countMulti;
            idNew += countMulti;
          } else if ($el.type === 'radio') {
            // <input type="radio"> fix
            // Recount id
            countMulti = ids[id] ? ids[id].length : 0;

            if (!countMulti) {
              // Set the id for fieldset and group label
              var _fieldset = $el.closest('fieldset.radio');

              var _elLbl = row.querySelector("label[for=\"" + id + "\"]");

              if (_fieldset) {
                _fieldset.setAttribute('id', idNew);
              }

              if (_elLbl) {
                _elLbl.setAttribute('for', idNew);

                _elLbl.setAttribute('id', idNew + "-lbl");
              }
            }

            forOldAttr += countMulti;
            idNew += countMulti;
          } // Cache already used id


          if (ids[id]) {
            ids[id].push(true);
          } else {
            ids[id] = [true];
          } // Replace the name to new one


          $el.name = nameNew;

          if ($el.id) {
            $el.id = idNew;
          }

          if (aria) {
            $el.setAttribute('aria-describedby', nameNew + "-desc");
          } // Check if there is a label for this input


          var lbl = row.querySelector("label[for=\"" + forOldAttr + "\"]");

          if (lbl) {
            lbl.setAttribute('for', idNew);
            lbl.setAttribute('id', idNew + "-lbl");
          }
        });
      }
      /**
       * Use of HTML Drag and Drop API
       * https://developer.mozilla.org/en-US/docs/Web/API/HTML_Drag_and_Drop_API
       * https://www.sitepoint.com/accessible-drag-drop/
       */
      ;

      _proto.setUpDragSort = function setUpDragSort() {
        var that = this; // Self reference

        var item = null; // Storing the selected item

        var touched = false; // We have a touch events
        // Find all existing rows and add draggable attributes

        var rows = Array.from(this.getRows());
        rows.forEach(function (row) {
          row.setAttribute('draggable', 'false');
          row.setAttribute('aria-grabbed', 'false');
          row.setAttribute('tabindex', '0');
        }); // Helper method to test whether Handler was clicked

        function getMoveHandler(element) {
          return !element.form // This need to test whether the element is :input
          && element.matches(that.buttonMove) ? element : element.closest(that.buttonMove);
        } // Helper method to move row to selected position


        function switchRowPositions(src, dest) {
          var isRowBefore = false;

          if (src.parentNode === dest.parentNode) {
            for (var cur = src; cur; cur = cur.previousSibling) {
              if (cur === dest) {
                isRowBefore = true;
                break;
              }
            }
          }

          if (isRowBefore) {
            dest.parentNode.insertBefore(src, dest);
          } else {
            dest.parentNode.insertBefore(src, dest.nextSibling);
          }
        }
        /**
         *  Touch interaction:
         *
         *  - a touch of "move button" marks a row draggable / "selected",
         *     or deselect previous selected
         *
         *  - a touch of "move button" in the destination row will move
         *     a selected row to a new position
         */


        this.addEventListener('touchstart', function (event) {
          touched = true; // Check for .move button

          var handler = getMoveHandler(event.target);
          var row = handler ? handler.closest(that.repeatableElement) : null;

          if (!row || row.closest('joomla-field-subform') !== that) {
            return;
          } // First selection


          if (!item) {
            row.setAttribute('draggable', 'true');
            row.setAttribute('aria-grabbed', 'true');
            item = row;
          } else {
            // Second selection
            // Move to selected position
            if (row !== item) {
              switchRowPositions(item, row);
            }

            item.setAttribute('draggable', 'false');
            item.setAttribute('aria-grabbed', 'false');
            item = null;
          }

          event.preventDefault();
        }); // Mouse interaction
        // - mouse down, enable "draggable" and allow to drag the row,
        // - mouse up, disable "draggable"

        this.addEventListener('mousedown', function (_ref) {
          var target = _ref.target;
          if (touched) return; // Check for .move button

          var handler = getMoveHandler(target);
          var row = handler ? handler.closest(that.repeatableElement) : null;

          if (!row || row.closest('joomla-field-subform') !== that) {
            return;
          }

          row.setAttribute('draggable', 'true');
          row.setAttribute('aria-grabbed', 'true');
          item = row;
        });
        this.addEventListener('mouseup', function () {
          if (item && !touched) {
            item.setAttribute('draggable', 'false');
            item.setAttribute('aria-grabbed', 'false');
            item = null;
          }
        }); // Keyboard interaction
        // - "tab" to navigate to needed row,
        // - modifier (ctr,alt,shift) + "space" select the row,
        // - "tab" to select destination,
        // - "enter" to place selected row in to destination
        // - "esc" to cancel selection

        this.addEventListener('keydown', function (event) {
          if (event.keyCode !== KEYCODE.ESC && event.keyCode !== KEYCODE.SPACE && event.keyCode !== KEYCODE.ENTER || event.target.form || !event.target.matches(that.repeatableElement)) {
            return;
          }

          var row = event.target; // Make sure we handle correct children

          if (!row || row.closest('joomla-field-subform') !== that) {
            return;
          } // Space is the selection or unselection keystroke


          if (event.keyCode === KEYCODE.SPACE && hasModifier(event)) {
            // Unselect previously selected
            if (row.getAttribute('aria-grabbed') === 'true') {
              row.setAttribute('draggable', 'false');
              row.setAttribute('aria-grabbed', 'false');
              item = null;
            } else {
              // Select new
              // If there was previously selected
              if (item) {
                item.setAttribute('draggable', 'false');
                item.setAttribute('aria-grabbed', 'false');
                item = null;
              } // Mark new selection


              row.setAttribute('draggable', 'true');
              row.setAttribute('aria-grabbed', 'true');
              item = row;
            } // Prevent default to suppress any native actions


            event.preventDefault();
          } // Escape is the abort keystroke (for any target element)


          if (event.keyCode === KEYCODE.ESC && item) {
            item.setAttribute('draggable', 'false');
            item.setAttribute('aria-grabbed', 'false');
            item = null;
          } // Enter, to place selected item in selected position


          if (event.keyCode === KEYCODE.ENTER && item) {
            item.setAttribute('draggable', 'false');
            item.setAttribute('aria-grabbed', 'false'); // Do nothing here

            if (row === item) {
              item = null;
              return;
            } // Move the item to selected position


            switchRowPositions(item, row);
            event.preventDefault();
            item = null;
          }
        }); // dragstart event to initiate mouse dragging

        this.addEventListener('dragstart', function (_ref2) {
          var dataTransfer = _ref2.dataTransfer;

          if (item) {
            // We going to move the row
            dataTransfer.effectAllowed = 'move'; // This need to work in Firefox and IE10+

            dataTransfer.setData('text', '');
          }
        });
        this.addEventListener('dragover', function (event) {
          if (item) {
            event.preventDefault();
          }
        }); // Handle drag action, move element to hovered position

        this.addEventListener('dragenter', function (_ref3) {
          var target = _ref3.target;

          // Make sure the target in the correct container
          if (!item || that.rowsContainer && target.closest(that.rowsContainer) !== that.containerWithRows) {
            return;
          } // Find a hovered row, and replace it


          var row = target.matches(that.repeatableElement) ? target : target.closest(that.repeatableElement);
          if (!row) return;
          switchRowPositions(item, row);
        }); // dragend event to clean-up after drop or abort
        // which fires whether or not the drop target was valid

        this.addEventListener('dragend', function () {
          if (item) {
            item.setAttribute('draggable', 'false');
            item.setAttribute('aria-grabbed', 'false');
            item = null;
          }
        });
      };

      _createClass(JoomlaFieldSubform, [{
        key: "buttonAdd",
        get: // Attribute getters
        function get() {
          return this.getAttribute('button-add');
        }
      }, {
        key: "buttonRemove",
        get: function get() {
          return this.getAttribute('button-remove');
        }
      }, {
        key: "buttonMove",
        get: function get() {
          return this.getAttribute('button-move');
        }
      }, {
        key: "rowsContainer",
        get: function get() {
          return this.getAttribute('rows-container');
        }
      }, {
        key: "repeatableElement",
        get: function get() {
          return this.getAttribute('repeatable-element');
        }
      }, {
        key: "minimum",
        get: function get() {
          return this.getAttribute('minimum');
        }
      }, {
        key: "maximum",
        get: function get() {
          return this.getAttribute('maximum');
        }
      }, {
        key: "name",
        get: function get() {
          return this.getAttribute('name');
        },
        set: function set(value) {
          // Update the template
          this.template = this.template.replace(new RegExp(" name=\"" + this.name.replace(/[[\]]/g, '\\$&'), 'g'), " name=\"" + value);
          return this.setAttribute('name', value);
        }
      }]);

      return JoomlaFieldSubform;
    }( /*#__PURE__*/_wrapNativeSuper(HTMLElement));

    customElements.define('joomla-field-subform', JoomlaFieldSubform);
  })(customElements);

}());
