docs: function sortable(sortableElements, options) {
          // get method string to see if a method is called
          var method = String(options);
          // merge user options with defaultss
          options = Object.assign({
              connectWith: null,
              acceptFrom: null,
              copy: false,
              placeholder: null,
              disableIEFix: null,
              placeholderClass: 'sortable-placeholder',
              draggingClass: 'sortable-dragging',
              hoverClass: false,
              debounce: 0,
              maxItems: 0,
              itemSerializer: undefined,
              containerSerializer: undefined,
              customDragImage: null,
              items: null
          }, (typeof options === 'object') ? options : {});
          // check if the user provided a selector instead of an element
          if (typeof sortableElements === 'string') {
              sortableElements = document.querySelectorAll(sortableElements);
          }
          // if the user provided an element, return it in an array to keep the return value consistant
          if (sortableElements instanceof HTMLElement) {
              sortableElements = [sortableElements];
          }
          sortableElements = Array.prototype.slice.call(sortableElements);
          if (/serialize/.test(method)) {
              return sortableElements.map(function (sortableContainer) {
                  var opts = addData(sortableContainer, 'opts');
                  return _serialize(sortableContainer, opts.itemSerializer, opts.containerSerializer);
              });
          }
          sortableElements.forEach(function (sortableElement) {
              if (/enable|disable|destroy/.test(method)) {
                  return sortable[method](sortableElement);
              }
              // log deprecation
              ['connectWith', 'disableIEFix'].forEach(function (configKey) {
                  if (options.hasOwnProperty(configKey) && options[configKey] !== null) {
                      console.warn("HTML5Sortable: You are using the deprecated configuration \"" + configKey + "\". This will be removed in an upcoming version, make sure to migrate to the new options when updating.");
                  }
              });
              // merge options with default options
              options = Object.assign({}, defaultConfiguration, options);
              // init data store for sortable
              store(sortableElement).config = options;
              // get options & set options on sortable
              options = addData(sortableElement, 'opts') || options;
              addData(sortableElement, 'opts', options);
              // property to define as sortable
              sortableElement.isSortable = true;
              // reset sortable
              _reloadSortable(sortableElement);
              // initialize
              var listItems = _filter(sortableElement.children, options.items);
              // create element if user defined a placeholder element as a string
              var customPlaceholder;
              if (options.placeholder !== null && options.placeholder !== undefined) {
                  var tempContainer = document.createElement(sortableElement.tagName);
                  tempContainer.innerHTML = options.placeholder;
                  customPlaceholder = tempContainer.children[0];
              }
              // add placeholder
              store(sortableElement).placeholder = _makePlaceholder(sortableElement, customPlaceholder, options.placeholderClass);
              addData(sortableElement, 'items', options.items);
              if (options.acceptFrom) {
                  addData(sortableElement, 'acceptFrom', options.acceptFrom);
              }
              else if (options.connectWith) {
                  addData(sortableElement, 'connectWith', options.connectWith);
              }
              _enableSortable(sortableElement);
              addAttribute(listItems, 'role', 'option');
              addAttribute(listItems, 'aria-grabbed', 'false');
              // enable hover class
              enableHoverClass(sortableElement, true);
              /*
               Handle drag events on draggable items
               Handle is set at the sortableElement level as it will bubble up
               from the item
               */
              addEventListener(sortableElement, 'dragstart', function (e) {
                  // ignore dragstart events
                  if (e.target.isSortable === true) {
                      return;
                  }
                  e.stopImmediatePropagation();
                  if ((options.handle && !e.target.matches(options.handle)) || e.target.getAttribute('draggable') === 'false') {
                      return;
                  }
                  var sortableContainer = findSortable(e.target);
                  var dragItem = findDragElement(sortableContainer, e.target);
                  // grab values
                  originItemsBeforeUpdate = _filter(sortableContainer.children, options.items);
                  originIndex = originItemsBeforeUpdate.indexOf(dragItem);
                  originElementIndex = index(dragItem, sortableContainer.children);
                  originContainer = sortableContainer;
                  // add transparent clone or other ghost to cursor
                  setDragImage(e, dragItem, options.customDragImage);
                  // cache selsection & add attr for dragging
                  draggingHeight = _getElementHeight(dragItem);
                  dragItem.classList.add(options.draggingClass);
                  dragging = _getDragging(dragItem, sortableContainer);
                  addAttribute(dragging, 'aria-grabbed', 'true');
                  // dispatch sortstart event on each element in group
                  sortableContainer.dispatchEvent(new CustomEvent('sortstart', {
                      detail: {
                          origin: {
                              elementIndex: originElementIndex,
                              index: originIndex,
                              container: originContainer
                          },
                          item: dragging
                      }
                  }));
              });
              /*
               We are capturing targetSortable before modifications with 'dragenter' event
              */
              addEventListener(sortableElement, 'dragenter', function (e) {
                  if (e.target.isSortable === true) {
                      return;
                  }
                  var sortableContainer = findSortable(e.target);
                  destinationItemsBeforeUpdate = _filter(sortableContainer.children, addData(sortableContainer, 'items'))
                      .filter(function (item) { return item !== store(sortableElement).placeholder; });
              });
              /*
               * Dragend Event - https://developer.mozilla.org/en-US/docs/Web/Events/dragend
               * Fires each time dragEvent end, or ESC pressed
               * We are using it to clean up any draggable elements and placeholders
               */
              addEventListener(sortableElement, 'dragend', function (e) {
                  if (!dragging) {
                      return;
                  }
                  dragging.classList.remove(options.draggingClass);
                  addAttribute(dragging, 'aria-grabbed', 'false');
                  if (dragging.getAttribute('aria-copied') === 'true' && addData(dragging, 'dropped') !== 'true') {
                      dragging.remove();
                  }
                  dragging.style.display = dragging.oldDisplay;
                  delete dragging.oldDisplay;
                  var visiblePlaceholder = Array.from(stores.values()).map(function (data) { return data.placeholder; })
                      .filter(function (placeholder) { return placeholder instanceof HTMLElement; })
                      .filter(isInDom)[0];
                  if (visiblePlaceholder) {
                      visiblePlaceholder.remove();
                  }
                  // dispatch sortstart event on each element in group
                  sortableElement.dispatchEvent(new CustomEvent('sortstop', {
                      detail: {
                          origin: {
                              elementIndex: originElementIndex,
                              index: originIndex,
                              container: originContainer
                          },
                          item: dragging
                      }
                  }));
                  dragging = null;
                  draggingHeight = null;
              });
              /*
               * Drop Event - https://developer.mozilla.org/en-US/docs/Web/Events/drop
               * Fires when valid drop target area is hit
               */
              addEventListener(sortableElement, 'drop', function (e) {
                  if (!_listsConnected(sortableElement, dragging.parentElement)) {
                      return;
                  }
                  e.preventDefault();
                  e.stopPropagation();
                  addData(dragging, 'dropped', 'true');
                  // get the one placeholder that is currently visible
                  var visiblePlaceholder = Array.from(stores.values()).map(function (data) {
                      return data.placeholder;
                  })
                      .filter(function (placeholder) { return placeholder instanceof HTMLElement; })
                      .filter(isInDom)[0];
                  // attach element after placeholder
                  insertAfter(visiblePlaceholder, dragging);
                  // remove placeholder from dom
                  visiblePlaceholder.remove();
                  /*
                   * Fires Custom Event - 'sortstop'
                   */
                  sortableElement.dispatchEvent(new CustomEvent('sortstop', {
                      detail: {
                          origin: {
                              elementIndex: originElementIndex,
                              index: originIndex,
                              container: originContainer
                          },
                          item: dragging
                      }
                  }));
                  var placeholder = store(sortableElement).placeholder;
                  var originItems = _filter(originContainer.children, options.items)
                      .filter(function (item) { return item !== placeholder; });
                  var destinationContainer = this.isSortable === true ? this : this.parentElement;
                  var destinationItems = _filter(destinationContainer.children, addData(destinationContainer, 'items'))
                      .filter(function (item) { return item !== placeholder; });
                  var destinationElementIndex = index(dragging, Array.from(dragging.parentElement.children)
                      .filter(function (item) { return item !== placeholder; }));
                  var destinationIndex = index(dragging, destinationItems);
                  /*
                   * When a list item changed container lists or index within a list
                   * Fires Custom Event - 'sortupdate'
                   */
                  if (originElementIndex !== destinationElementIndex || originContainer !== destinationContainer) {
                      sortableElement.dispatchEvent(new CustomEvent('sortupdate', {
                          detail: {
                              origin: {
                                  elementIndex: originElementIndex,
                                  index: originIndex,
                                  container: originContainer,
                                  itemsBeforeUpdate: originItemsBeforeUpdate,
                                  items: originItems
                              },
                              destination: {
                                  index: destinationIndex,
                                  elementIndex: destinationElementIndex,
                                  container: destinationContainer,
                                  itemsBeforeUpdate: destinationItemsBeforeUpdate,
                                  items: destinationItems
                              },
                              item: dragging
                          }
                      }));
                  }
              });
              var debouncedDragOverEnter = _debounce(function (sortableElement, element, pageY) {
                  if (!dragging) {
                      return;
                  }
                  // set placeholder height if forcePlaceholderSize option is set
                  if (options.forcePlaceholderSize) {
                      store(sortableElement).placeholder.style.height = draggingHeight + 'px';
                  }
                  // if element the draggedItem is dragged onto is within the array of all elements in list
                  // (not only items, but also disabled, etc.)
                  if (Array.from(sortableElement.children).indexOf(element) > -1) {
                      var thisHeight = _getElementHeight(element);
                      var placeholderIndex = index(store(sortableElement).placeholder, element.parentElement.children);
                      var thisIndex = index(element, element.parentElement.children);
                      // Check if `element` is bigger than the draggable. If it is, we have to define a dead zone to prevent flickering
                      if (thisHeight > draggingHeight) {
                          // Dead zone?
                          var deadZone = thisHeight - draggingHeight;
                          var offsetTop = offset(element).top;
                          if (placeholderIndex < thisIndex && pageY < offsetTop) {
                              return;
                          }
                          if (placeholderIndex > thisIndex &&
                              pageY > offsetTop + thisHeight - deadZone) {
                              return;
                          }
                      }
                      if (dragging.oldDisplay === undefined) {
                          dragging.oldDisplay = dragging.style.display;
                      }
                      if (dragging.style.display !== 'none') {
                          dragging.style.display = 'none';
                      }
                      // To avoid flicker, determine where to position the placeholder
                      // based on where the mouse pointer is relative to the elements
                      // vertical center.
                      var placeAfter = false;
                      try {
                          var elementMiddle = offset(element).top + element.offsetHeight / 2;
                          placeAfter = pageY >= elementMiddle;
                      }
                      catch (e) {
                          placeAfter = placeholderIndex < thisIndex;
                      }
                      if (placeAfter) {
                          insertAfter(element, store(sortableElement).placeholder);
                      }
                      else {
                          insertBefore(element, store(sortableElement).placeholder);
                      }
                      // get placeholders from all stores & remove all but current one
                      Array.from(stores.values())
                          .filter(function (data) { return data.placeholder !== undefined; })
                          .forEach(function (data) {
                          if (data.placeholder !== store(sortableElement).placeholder) {
                              data.placeholder.remove();
                          }
                      });
                  }
                  else {
                      // get all placeholders from store
                      var placeholders = Array.from(stores.values())
                          .filter(function (data) { return data.placeholder !== undefined; })
                          .map(function (data) {
                          return data.placeholder;
                      });
                      // check if element is not in placeholders
                      if (placeholders.indexOf(element) === -1 && sortableElement === element && !_filter(element.children, options.items).length) {
                          placeholders.forEach(function (element) { return element.remove(); });
                          element.appendChild(store(sortableElement).placeholder);
                      }
                  }
              }, options.debounce);
              // Handle dragover and dragenter events on draggable items
              var onDragOverEnter = function (e) {
                  var element = e.target;
                  var sortableElement = element.isSortable === true ? element : findSortable(element);
                  element = findDragElement(sortableElement, element);
                  if (!dragging || !_listsConnected(sortableElement, dragging.parentElement) || addData(sortableElement, '_disabled') === 'true') {
                      return;
                  }
                  var options = addData(sortableElement, 'opts');
                  if (parseInt(options.maxItems) && _filter(sortableElement.children, addData(sortableElement, 'items')).length >= parseInt(options.maxItems) && dragging.parentElement !== sortableElement) {
                      return;
                  }
                  e.preventDefault();
                  e.stopPropagation();
                  e.dataTransfer.dropEffect = store(sortableElement).getConfig('copy') === true ? 'copy' : 'move';
                  debouncedDragOverEnter(sortableElement, element, e.pageY);
              };
              addEventListener(listItems.concat(sortableElement), 'dragover', onDragOverEnter);
              addEventListener(listItems.concat(sortableElement), 'dragenter', onDragOverEnter);
          });
          return sortableElements;
      }