HEX
Server: Microsoft-IIS/8.5
System: Windows NT YDAWBH120 6.3 build 9600 (Windows Server 2012 R2 Standard Edition) AMD64
User: tentjecom_web (0)
PHP: 7.4.14
Disabled: NONE
Upload Files
File: D:/HostingSpaces/SBogers10/helder.komma.pro/wwwroot/js/kms/attributes.js
function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _nonIterableSpread(); }

function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance"); }

function _iterableToArray(iter) { if (Symbol.iterator in Object(iter) || Object.prototype.toString.call(iter) === "[object Arguments]") return Array.from(iter); }

function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = new Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } }

function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

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; }

/**
 * HTML 5 file uploads that report progress while uploading
 */
var HTML5Uploader =
/*#__PURE__*/
function () {
  function HTML5Uploader(uploadUrl, maxPostSize, maxUploadSize, translations) {
    _classCallCheck(this, HTML5Uploader);

    this.constructedSuccessfully = false;

    if (!uploadUrl) {
      console.error('Did not get a valid upload url. Not initializing Html5Uploader');
      return;
    }

    this.uploadUrl = uploadUrl;

    if (!maxPostSize) {
      console.error('Did not get the maximum post size. Not initializing Html5Uploader');
      return;
    }

    this.maxPostSize = maxPostSize;

    if (!maxUploadSize) {
      console.error('Did not get the maximum upload size. Not initializing Html5Uploader');
      return;
    }

    this.maxUploadSize = maxUploadSize;

    if (!translations) {
      console.error('Did not get the translations. Not initializing Html5Uploader');
      return;
    }

    this.translations = JSON.parse(translations);
    this.eventMap = {};

    if (!this.isSupported()) {
      console.warn('HTML 5 file upload is not supported. Not initializing Html5Uploader');
      return;
    }

    this.constructedSuccessfully = true;
  }
  /**
   * Returns true if the browser supports HTML 5 uploading. false if not
   * @return {boolean}
   */


  _createClass(HTML5Uploader, [{
    key: "isSupported",
    value: function isSupported() {
      return window.FormData !== undefined;
    }
    /**
     * @param {File} file
     */

  }, {
    key: "receiveFile",
    value: function receiveFile(file) {
      if (!this.isSupported()) {
        console.error('HTML 5 file upload is not supported. Not uploading file.');
        return;
      }
    }
    /**
     * @param {File} file
     * @param {string} extraData
     */

  }, {
    key: "upload",
    value: function upload(file, extraData) {
      if (!this.verifyFileSizeOrShowError(file)) return;
      if (this.constructedSuccessfully === false) return;
      var self = this;
      var formData = new FormData();
      formData.append(file.name, file);
      formData.append('extraData', extraData);
      var request = new XMLHttpRequest();
      var uploadRequest = request.upload;
      uploadRequest.addEventListener("progress", function (file, extraData) {
        return function (event) {
          self.updateProgress(file, event, extraData);
        };
      }(file, extraData));
      request.addEventListener("loadstart", function (file, extraData) {
        return function (event) {
          self.uploadStart(file, extraData);
        };
      }(file, extraData));
      request.addEventListener("error", function (file, extraData) {
        return function (event) {
          self.uploadFailed(file, 'general error', extraData);
        };
      }(file, extraData));
      request.addEventListener("abort", function (file, extraData) {
        return function (event) {
          self.uploadCanceled(file, extraData);
        };
      }(file, extraData));

      request.onreadystatechange = function (file, extraData) {
        return function () {
          if (request.readyState === 4) {
            if (request.status === 200) {
              self.uploadComplete(file, request.responseText, extraData);
            } else {
              self.uploadFailed(file, request.responseText, extraData);
            }
          }
        };
      }(file, extraData);

      request.open('POST', this.uploadUrl);
      request.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
      var csfrTokenContainer = document.querySelector('meta[name="csrf-token"]');

      if (!csfrTokenContainer) {
        console.error('Not uploading file since not csrf-token could be found in a meta tag with name "csrf-token". Please add it');
        return;
      }

      request.setRequestHeader('X-CSRF-TOKEN', csfrTokenContainer.getAttribute('content'));
      request.send(formData);
    }
    /**
     * Returns true if the file size is lower then the maximum allowed server upload size.
     * Or shows an alert and returns false if it is bigger then the allowed server upload size.
     *
     * @param {File} file
     * @return {boolean}
     */

  }, {
    key: "verifyFileSizeOrShowError",
    value: function verifyFileSizeOrShowError(file) {
      if (file.size > this.maxPostSize || file.size > this.maxUploadSize) {
        //Calculate file size and max file size to megabytes
        var fileSizeInMegaBytes = file.size / (1024 * 1024);
        var maxSizeInMegaBytes = (this.maxUploadSize > this.maxPostSize ? this.maxPostSize : this.maxUploadSize) / (1024 * 1024); //Round this sizes to 2 decimals

        fileSizeInMegaBytes = Math.round(fileSizeInMegaBytes * 100) / 100;
        maxSizeInMegaBytes = Math.round(maxSizeInMegaBytes * 100) / 100; //Gather translation texts

        var introText = this.translations.file_to_big;
        var maxFileSizeUploadTranslation = this.translations.max_file_size_upload;
        var fileSizeUploadTranslation = this.translations.file_size_upload; //Assemble alert message

        var alertText = introText + '\n\n' + maxFileSizeUploadTranslation + maxSizeInMegaBytes + ' MB\n' + fileSizeUploadTranslation + fileSizeInMegaBytes + ' MB\n'; //Show alert message and return false

        alert(alertText);
        return false;
      } //File size is fine. Return true


      return true;
    }
  }, {
    key: "updateProgress",
    value: function updateProgress(file, event, extraData) {
      if (event.lengthComputable) {
        var percentComplete = Math.ceil(event.loaded / event.total * 100);
        this.triggerEvent('updateProgress', [file, percentComplete, extraData]);
      } else {
        // Unable to compute progress information since the total size is unknown
        this.triggerEvent('updateProgress', [file, null, extraData]);
      }
    }
  }, {
    key: "uploadStart",
    value: function uploadStart(file, extraData) {
      this.triggerEvent('uploadStart', [file, extraData]);
    }
  }, {
    key: "uploadComplete",
    value: function uploadComplete(file, responseText, extraData) {
      this.triggerEvent('uploadComplete', [file, responseText, extraData]);
    }
  }, {
    key: "uploadFailed",
    value: function uploadFailed(file, extraData) {
      this.triggerEvent('uploadFailed', [file, extraData]);
    }
  }, {
    key: "uploadCanceled",
    value: function uploadCanceled(file, extraData) {
      this.triggerEvent('uploadCanceled', [file, extraData]);
    }
    /**
     * Registers a event to a callback and returns the a reference to the event handler for that specific event
     *
     * @param event (string)
     * @param callback (callable)
     */

  }, {
    key: "on",
    value: function on(event, callback) {
      if (this.constructedSuccessfully === false) return;
      if (!this.eventMap.hasOwnProperty(event)) this.eventMap[event] = [];
      return this.eventMap[event].push(callback);
    }
    /**
     * Call event callbacks
     *
     * @param event (string)
     * @param eventArgs (array) an array of arguments to pass to the callback
     */

  }, {
    key: "triggerEvent",
    value: function triggerEvent(event, eventArgs) {
      if (!this.eventMap.hasOwnProperty(event)) return;
      var nEvents = this.eventMap[event].length;

      for (var index = 0; index < nEvents; index++) {
        var callback = this.eventMap[event][index];

        if (eventArgs && eventArgs.length > 0) {
          callback.apply(this, eventArgs);
        } else {
          callback.call(this);
        }
      }
    }
  }]);

  return HTML5Uploader;
}();
/**
 * The main class to manage components
 */


var ComponentAreaManager =
/*#__PURE__*/
function () {
  /**
   * @param {HTMLDivElement} wrapper
   * @param {ComponentManagerApiController} componentManagerApiController
   */
  function ComponentAreaManager(wrapper, componentManagerApiController) {
    _classCallCheck(this, ComponentAreaManager);

    this._componentAreaWrapperElement = wrapper;
    this._componentManagerApiController = componentManagerApiController;

    if (this._componentAreaWrapperElement === undefined || this._componentAreaWrapperElement.tagName !== "DIV") {
      console.error('ComponentAreaManager:constructor Did not get the expected div element that represents the components wrapper.');
      return;
    }

    if (this._componentManagerApiController === undefined) {
      console.error('ComponentAreaManager:constructor Did not get the expected _componentManagerApiController.');
      return;
    }

    this._componentAreaAttributeKey = this._componentAreaWrapperElement.id; //Get the input field for the components attribute that will hold all data for the components attribute values.

    this._masterInput = this._componentAreaWrapperElement.querySelector('input[name="' + this._componentAreaAttributeKey + '"]');

    if (!this._masterInput) {
      console.error('ComponentAreaManager:constructor Did not find an hidden input with name "' + this._componentAreaAttributeKey + '" inside of the wrapper.');
      return;
    }

    this._componentAreaContainer = this._componentAreaWrapperElement.querySelector('.js-components');

    if (!this._componentAreaContainer) {
      console.error('ComponentAreaManager:constructor the wrapper did not contain a div with class components.');
      return;
    }

    if (!'componentAttributeFieldsRegex' in this._componentAreaWrapperElement.dataset) {
      console.error('The wrapper element must, but did not have a componentAttributeFieldsRegex dataset value');
      return;
    }

    this._componentAttributeFieldsRegex = this._componentAreaWrapperElement.dataset.componentAttributeFieldsRegex;
    this._saveVersion = '0.9.1';
    this._addComponentButtonElements = [];
    this.eventMap = {};

    this._setAddComponentButtonElements();

    this._loadComponentsUsingSaveStateData();
  }
  /**
   * Sets up the add component buttons by finding them in the wrapper and adding listeners to them
   */


  _createClass(ComponentAreaManager, [{
    key: "_setAddComponentButtonElements",
    value: function _setAddComponentButtonElements() {
      this._addComponentButtonElements = this._componentAreaWrapperElement.querySelectorAll(".js-add-component");
      var addComponentButtonsElementsLength = this._addComponentButtonElements.length;

      for (var index = 0; index < addComponentButtonsElementsLength; index++) {
        var currentAddComponentButtonElement = this._addComponentButtonElements[index];
        currentAddComponentButtonElement.addEventListener('click', this._addComponentButtonClicked.bind(this));
      }
    }
    /**
     * Click handler for add component buttons
     *
     * @param {MouseEvent} mouseEvent
     */

  }, {
    key: "_addComponentButtonClicked",
    value: function _addComponentButtonClicked(mouseEvent) {
      var self = this;
      mouseEvent.preventDefault();
      var button = mouseEvent.target;

      if (!'component' in button.dataset) {
        console.error('ComponentAreaManager: One of the add component buttons was missing the component dataset attribute. Please set it to the name of a valid component');
        return;
      }

      var componentSaveState = new ComponentSaveState();
      componentSaveState.id = parseInt(self._componentAreaWrapperElement.dataset.timesAddedComponent) * -1;
      componentSaveState.componentTypeId = parseInt(button.dataset.componentType); //The id of the component to resolve

      componentSaveState.sortOrder = 0;

      this._componentManagerApiController.getComponentHtmlElement(componentSaveState, this._componentAreaWrapperElement.id).then(function (data) {
        self._componentAreaWrapperElement.dataset.timesAddedComponent = (parseInt(self._componentAreaWrapperElement.dataset.timesAddedComponent) + 1).toString();
        var componentHtmlElement = data.element;

        self._addComponent(componentHtmlElement);

        self._updateMasterInput();
      });
    }
    /**
     * Adds a component to the component wrapper and add listeners to it to detect changes
     * {HTMLElement} componentHtmlElement
     * {Number} position
     */

  }, {
    key: "_addComponent",
    value: function _addComponent(componentHtmlElement) {
      var self = this; //Add listeners to the attributes to detect changes and trigger a master input update

      var foundComponentInputsModel = this._findAttributeDataInputs(componentHtmlElement);

      var inputsCount = foundComponentInputsModel.inputElements.length;

      for (var index = 0; index < inputsCount; index++) {
        var input = foundComponentInputsModel.inputElements[index];

        var debouncedKeyFunction = self._debounce(self._attributeInputChanged.bind(self), 500);

        input.addEventListener('change', self._attributeInputChanged.bind(self));
        input.addEventListener('blur', self._attributeInputChanged.bind(self));
        input.addEventListener('keydown', debouncedKeyFunction.bind(self));
      } //Add listeners to componentable attributes to detect changes and trigger a master input update


      var componentableComponentInputs = this._findComponentableInputs(componentHtmlElement);

      var componentableInputsCount = foundComponentInputsModel.inputElements.length;

      for (var _index = 0; _index < componentableInputsCount; _index++) {
        var _input = componentableComponentInputs.inputElements[_index];
        if (!_input) continue;

        var _debouncedKeyFunction = self._debounce(self._attributeInputChanged.bind(self), 500);

        _input.addEventListener('change', self._attributeInputChanged.bind(self));

        _input.addEventListener('blur', self._attributeInputChanged.bind(self));

        _input.addEventListener('keydown', _debouncedKeyFunction.bind(self));
      } //Make the delete button work


      var deleteButton = componentHtmlElement.querySelector('.js-component-delete');
      deleteButton.addEventListener('click', function (event) {
        event.preventDefault();
        componentHtmlElement.parentElement.removeChild(componentHtmlElement);

        self._updateMasterInput();
      }); //Make the move down button work

      var downButton = componentHtmlElement.querySelector('.js-component-move-up');
      downButton.addEventListener('click', function (event) {
        event.preventDefault();

        if (componentHtmlElement.previousSibling) {
          componentHtmlElement.parentElement.insertBefore(componentHtmlElement, componentHtmlElement.previousSibling);
        }

        self._updateMasterInput();
      }); //Make the move up button work

      var upButton = componentHtmlElement.querySelector('.js-component-move-down');
      upButton.addEventListener('click', function (event) {
        event.preventDefault();

        if (componentHtmlElement.nextSibling && componentHtmlElement.nextSibling.nextSibling) {
          componentHtmlElement.parentElement.insertBefore(componentHtmlElement, componentHtmlElement.nextSibling.nextSibling);
        } else {
          componentHtmlElement.parentElement.appendChild(componentHtmlElement);
        }

        self._updateMasterInput();
      }); //Put some of the component's attributes in tabs when needed

      this._putComponentAttributesInTabs(componentHtmlElement); //Add the component


      this._componentAreaContainer.appendChild(componentHtmlElement);
    }
    /**
     * Triggered when an attribute input was changed.
     *
     * @param event
     */

  }, {
    key: "_attributeInputChanged",
    value: function _attributeInputChanged(event) {
      this._updateMasterInput();
    }
    /**
     * updates the master input which holds all attribute values
     */

  }, {
    key: "_updateMasterInput",
    value: function _updateMasterInput() {
      var self = this;

      var componentAreaSaveState = this._getDataToSave();

      self._masterInput.value = JSON.stringify(componentAreaSaveState);
    }
  }, {
    key: "_putComponentAttributesInTabs",
    value: function _putComponentAttributesInTabs(componentElement) {
      //Validate that we did get a component element, or show error and return;
      var attributeWrapper = componentElement.querySelector('.attributes');

      if (!attributeWrapper) {
        console.error('Expected to get a "componentElement" but did not. It did not contain a div with class "attributes" in it.');
        return;
      } //Retrieve the attributes in the component and store the length


      var attributes = attributeWrapper.children;
      var attributesCount = attributes.length; //Create tab configuration that we use to build the tabs later on. Each property represents a tab name.
      //each property value is an array of attribute elements that belong to the tab name.

      var tabsConfiguration = {};

      for (var i = 0; i < attributesCount; i++) {
        var attributeElement = attributes[i];

        if ("tab" in attributeElement.dataset) {
          if (!tabsConfiguration.hasOwnProperty(attributeElement.dataset.tab)) tabsConfiguration[attributeElement.dataset.tab] = [];
          tabsConfiguration[attributeElement.dataset.tab].push(attributeElement);
        }
      } //Now build tabs if the tabsConfiguration object has children


      var tabsCount = Object.keys(tabsConfiguration).length;

      if (tabsCount > 0) {
        //First build the tab "framework" html and add it to the attributes container
        var domParser = new DOMParser();

        var _document = domParser.parseFromString('<div class="component-tab">' + //Note check kms.js. It uses the class name to make the tabs work
        '    <ul class="component-tab__list">' + '    </ul>' + '    <div class="component-tab__container">' + '    </div>' + '</div>', 'text/html');

        var componentTab = _document.body.firstChild;
        attributeWrapper.appendChild(componentTab); //Build and add tabs. And put the attributes inside the correct ones

        var index = 0;

        var _loop = function _loop(tabName) {
          if (!tabsConfiguration.hasOwnProperty(tabName)) return "continue"; //Skip "tabName" if it is a property on the objects prototype
          //Create the tab switch button to switch to the first tab

          _document = domParser.parseFromString('<li class="component-tab__list-item">' + '    <a class="component-tab__button">' + tabName + '</a>' + '</li>', 'text/html');
          var tabSwitchButton = _document.body.firstChild; //Add the tabSwitchButton to the tab list

          var tabList = componentTab.getElementsByClassName('component-tab__list')[0];
          tabList.appendChild(tabSwitchButton); //Create the content holder

          _document = domParser.parseFromString('<div class="component-tab__content"></div>', 'text/html');
          var tabContent = _document.body.firstChild; //Add the content holder in the componentTab container

          var container = componentTab.getElementsByClassName('component-tab__container')[0];
          container.appendChild(tabContent); //Put the attributes in the tab content holder

          var attributesCount = tabsConfiguration[tabName].length;

          for (var _i = 0; _i < attributesCount; _i++) {
            tabContent.appendChild(tabsConfiguration[tabName][_i]);
          } //Make the first tab active


          if (index === 0) {
            tabSwitchButton.classList.add('active');
            tabContent.classList.add('active');
          } //Make the button switch to the correct tab by using the is-active classes


          tabSwitchButton.addEventListener('click', function (clickEvent) {
            clickEvent.preventDefault();
            var activeTabIndex = 0;

            for (var tabSwitchButtonIndex = 0; tabSwitchButtonIndex < tabsCount; tabSwitchButtonIndex++) {
              var currentTabSwitchButton = tabList.children[tabSwitchButtonIndex];
              currentTabSwitchButton.classList.remove('active');

              if (currentTabSwitchButton === tabSwitchButton) {
                currentTabSwitchButton.classList.add('active');
                activeTabIndex = tabSwitchButtonIndex;
              }
            }

            for (var tabContentIndex = 0; tabContentIndex < tabsCount; tabContentIndex++) {
              var currentTabContent = container.children[tabContentIndex];
              currentTabContent.classList.remove('active');
              if (tabContentIndex === activeTabIndex) currentTabContent.classList.add('active');
            }
          });
          index++;
        };

        for (var tabName in tabsConfiguration) {
          var _ret = _loop(tabName);

          if (_ret === "continue") continue;
        }
      }
    }
    /**
     * Does search inputs, textarea's and select's in the given component div element or else inside of
     * the componentsWrapper and checks if they match against the regex that identifies them as attribute data fields
     *
     * @param {HTMLDivElement|null} componentElement
     * @param {ComponentSaveState} componentSavestate to also pass when resolved
     * @return {FoundComponentInputsModel}
     */

  }, {
    key: "_findAttributeDataInputs",
    value: function _findAttributeDataInputs(componentElement) {
      var componentSavestate = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : undefined;
      var dataInputs = [];
      var attributesContainer = componentElement.getElementsByClassName('attributes')[0];
      var possibleDataInputs = attributesContainer.querySelectorAll('input, textarea, select');
      var inputsQuantity = possibleDataInputs.length;

      for (var index = 0; index < inputsQuantity; index++) {
        var input = possibleDataInputs[index];

        if (input.name !== undefined && input.name.match(new RegExp(this._componentAttributeFieldsRegex))) {
          dataInputs.push(input);
        }
      }

      var model = new FoundComponentInputsModel();
      model.inputElements = dataInputs;
      model.componentElement = componentElement;
      model.componentSaveState = componentSavestate;
      return model;
    }
    /**
     * Gets the value of the select / autocomplete input for selecting a model you want to link the component to.
     * Notice that the code is pretty much the same as _findAttributeDataInputs.
     *
     * @param {HTMLDivElement|null} componentElement
     * @param {ComponentSaveState} componentSavestate to also pass when resolved
     * @return {FoundComponentInputsModel}
     *
     * @private
     */

  }, {
    key: "_findComponentableInputs",
    value: function _findComponentableInputs(componentElement) {
      var componentSavestate = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : undefined;
      var dataInputs = [];
      var componentableSelectorsContainer = componentElement.querySelector('.componentableSelectors');
      if (!componentableSelectorsContainer) return new FoundComponentInputsModel();
      var possibleDataInputs = componentableSelectorsContainer.querySelectorAll('input, textarea, select');
      var inputsQuantity = possibleDataInputs.length;

      for (var index = 0; index < inputsQuantity; index++) {
        var input = possibleDataInputs[index];

        if (input.name !== undefined && input.name.match(new RegExp(this._componentAttributeFieldsRegex))) {
          dataInputs.push(input);
        }
      }

      var model = new FoundComponentInputsModel();
      model.inputElements = dataInputs;
      model.componentElement = componentElement;
      model.componentSaveState = componentSavestate;
      return model;
    }
    /**
     * @return {NodeListOf<HTMLElementTagNameMap[HTMLDivElement]>}
     */

  }, {
    key: "getComponentElements",
    value: function getComponentElements() {
      return this._componentAreaContainer.querySelectorAll('.c-component');
    }
    /**
     * Return a promise wich resolves to  a ComponentAreaSaveState
     * And contains a save version to allow non breaking updates in the future.
     *
     * @return ComponentAreaSaveState
     */

  }, {
    key: "_getDataToSave",
    value: function _getDataToSave() {
      var saveState = new ComponentAreaSaveState(); //Loop over all components.

      var components = this.getComponentElements();
      var componentsCount = components.length;

      for (var componentNumber = 0; componentNumber < componentsCount; componentNumber++) {
        //Loop over all attributes in the components...
        var currentComponent = components[componentNumber]; // findAttributeDataInputsPromises.push(this._findAttributeDataInputs(currentComponent));

        var foundComponentInputsModel = this._findAttributeDataInputs(currentComponent);

        var foundComponentsInputModelForLinkable = this._findComponentableInputs(currentComponent); //Create an array of objects. The objects properties are field names and the objects values are the values of those field names


        var attributeInputData = {};
        var inputsCounts = foundComponentInputsModel.inputElements.length;

        for (var currentInputNumber = 0; currentInputNumber < inputsCounts; currentInputNumber++) {
          var currentInput = foundComponentInputsModel.inputElements[currentInputNumber];

          if (currentInput.tagName === "INPUT" && currentInput.type === "file") {//Ignore file input
          } else {
            attributeInputData[currentInput.name] = currentInput.value;
          }
        } //Create an array containing one object that has a property name that corresponds to an inputs field name and the value of that property is the value of that input.


        var linkableInputObject = {};
        var linkableInputsCount = foundComponentsInputModelForLinkable.inputElements.length;

        for (var _currentInputNumber = 0; _currentInputNumber < linkableInputsCount; _currentInputNumber++) {
          var _currentInput = foundComponentsInputModelForLinkable.inputElements[_currentInputNumber];

          if (_currentInput.tagName === "INPUT" && _currentInput.type === "file") {//Ignore file input
          } else {
            linkableInputObject[_currentInput.name] = _currentInput.value;
          }
        }

        var componentSaveState = new ComponentSaveState();
        componentSaveState.id = Number(foundComponentInputsModel.componentElement.dataset.id);
        componentSaveState.componentTypeId = Number(foundComponentInputsModel.componentElement.dataset.componentTypeId);
        componentSaveState.data = attributeInputData;
        componentSaveState.version = this._saveVersion;
        componentSaveState.sortOrder = Number(componentNumber);
        componentSaveState.componentableData = linkableInputObject;
        saveState.addComponentSaveState(componentSaveState);
      }

      return saveState;
    }
    /**
     * Uses the master input to rebuild the saved componentarea with components with their data
     */

  }, {
    key: "_loadComponentsUsingSaveStateData",
    value: function _loadComponentsUsingSaveStateData() {
      var self = this; //Retrieve and decode the save state data

      var savedData = this._masterInput.value;
      if (!savedData) return false;
      var saveState = ComponentAreaSaveState.fromJsonString(savedData);
      if (!saveState) return false;
      var getComponentHtmlElementPromises = [];
      var savedComponentsCount = saveState.componentsCount;

      for (var currentComponentNumber = 0; currentComponentNumber < savedComponentsCount; currentComponentNumber++) {
        var currentComponentSaveState = saveState.getComponentSavestateAt(currentComponentNumber);
        getComponentHtmlElementPromises.push(this._componentManagerApiController.getComponentHtmlElement(currentComponentSaveState, this._componentAreaWrapperElement.id));
      }

      Promise.all(getComponentHtmlElementPromises).then(function (componentDatas) {
        var componentDatasCount = componentDatas.length; //Rebuild structure

        for (var componentNumber = 0; componentNumber < componentDatasCount; componentNumber++) {
          var componentData = componentDatas[componentNumber];
          var componentHTMLElement = componentData.element;

          self._addComponent(componentHTMLElement);
        } //Loop over the same componentHTMLElements and fill their attribute inputs


        for (var _currentComponentNumber = 0; _currentComponentNumber < componentDatasCount; _currentComponentNumber++) {
          var currentComponentData = componentDatas[_currentComponentNumber];
          var currentComponentSavestate = currentComponentData.componentSavestate;
          var attributeData = currentComponentSavestate.data;

          for (var attributeKeyName in attributeData) {
            if (!attributeData.hasOwnProperty(attributeKeyName)) continue;

            if (currentComponentSavestate.data.hasOwnProperty(attributeKeyName)) {
              var attributeFields = self._componentAreaWrapperElement.querySelectorAll('[name="' + attributeKeyName + '"]');

              var nAttributeFields = attributeFields.length;

              if (nAttributeFields > 0) {
                for (var index = 0; index < nAttributeFields; index++) {
                  attributeFields[index].value = attributeData[attributeKeyName];
                }
              } else {
                console.error('Could not load data for field: ' + attributeKeyName + ' since it could not be found. Skipping it');
              }
            }
          }

          var componentableData = currentComponentSavestate.componentableData;

          for (var _attributeKeyName in componentableData) {
            if (!componentableData.hasOwnProperty(_attributeKeyName)) continue;

            if (currentComponentSavestate.componentableData.hasOwnProperty(_attributeKeyName)) {
              var _attributeFields = self._componentAreaWrapperElement.querySelectorAll('[name="' + _attributeKeyName + '"]');

              var _nAttributeFields = _attributeFields.length;

              if (_nAttributeFields > 0) {
                for (var _index2 = 0; _index2 < _nAttributeFields; _index2++) {
                  _attributeFields[_index2].value = componentableData[_attributeKeyName];
                }
              } else {
                console.error('Could not load data for field: ' + _attributeKeyName + ' since it could not be found. Skipping it');
              }
            }
          }
        } //sessionStorage.removeItem("componentScrollPosition");


        if (isset(sessionStorage.getItem("componentScrollPosition"))) {
          self._componentAreaWrapperElement.parentElement.parentElement.parentElement.scrollTop = sessionStorage.getItem("componentScrollPosition");
        }
      });
    }
    /**
     * Returns a function, that, as long as it continues to be invoked, will not
     * be triggered. The function will be called after it stops being called for
     * N milliseconds. If `immediate` is passed, trigger the function on the
     * leading edge, instead of the trailing.
     *
     * @param func
     * @param wait
     * @param immediate
     * @return {Function}
     */

  }, {
    key: "_debounce",
    value: function _debounce(func, wait, immediate) {
      var timeout;
      return function () {
        var context = this,
            args = arguments;

        var later = function later() {
          timeout = null;
          if (!immediate) func.apply(context, args);
        };

        var callNow = immediate && !timeout;
        clearTimeout(timeout);
        timeout = setTimeout(later, wait);
        if (callNow) func.apply(context, args);
      };
    }
  }]);

  return ComponentAreaManager;
}();
/**
 * Used by the ComponentAreaManager to talk to the backend
 */


var ComponentManagerApiController =
/*#__PURE__*/
function () {
  function ComponentManagerApiController() {
    _classCallCheck(this, ComponentManagerApiController);

    this.domParser = new DOMParser();
  }
  /**
   * @param {ComponentSaveState} componentSavestate
   * @param {string} componentAreaAttributeKey
   * @return {Promise} that resolves with a HTMLDivElement that represents the newly requested component with it's attributes in it
   */


  _createClass(ComponentManagerApiController, [{
    key: "getComponentHtmlElement",
    value: function getComponentHtmlElement(componentSavestate, componentAreaAttributeKey) {
      var self = this;
      return new Promise(function (resolve, reject) {
        var postData = {
          componentAreaAttributeKey: componentAreaAttributeKey,
          componentSaveState: JSON.stringify(componentSavestate)
        };
        axios.post('/api/dynamic/component/resolve', postData).then(function (response) {
          var toResolve = {
            element: self._stringToHtmlElement(response.data),
            componentSavestate: componentSavestate
          };
          resolve(toResolve);
        }).catch(function (error) {
          console.error('ComponentManagerApiController:getComponentHtmlElement Could not get HTML of a component because of the following error:', error);
          reject(error);
        });
      });
    }
  }, {
    key: "getComponentAreaRegexForComponent",
    value: function getComponentAreaRegexForComponent(componentKey) {
      if (componentKey === undefined) {
        console.error('ComponentManagerApiController:getComponentAreaRegexForComponent Could not get the regex for an undefined componentKey. Please specify it.');
        return;
      }

      return new Promise(function (resolve, reject) {
        axios.post('/api/dynamic/component/attribute_regex', {
          componentKey: componentKey
        }).then(function (response) {
          resolve(response.data);
        }).catch(function (error) {
          console.error('ComponentManagerApiController:getComponentAreaRegexForComponent Could not get the regex to find inputs for a component because of the following error:', error);
          reject(error);
        });
      });
    }
    /**
     * @return {Promise} that resolves with an array of component type names. Or rejects with the already console logged error.
     */

  }, {
    key: "getAllComponentTypeNames",
    value: function getAllComponentTypeNames() {
      return new Promise(function (resolve, reject) {
        axios.get('/api/dynamic/component').then(function (response) {
          return response.data; //Must be an array full of component type names
        }).catch(function (error) {
          console.error('ComponentManagerApiController:getAllComponentTypeNames Could not the names of all available componentTypes because of an error:', error);
          reject(error);
        });
      });
    }
    /**
     * Converts a string to an HTMLElement
     *
     * @param {string} data
     * @return {HTMLElement}
     */

  }, {
    key: "_stringToHtmlElement",
    value: function _stringToHtmlElement(data) {
      var document = this.domParser.parseFromString(data, "text/html");
      return document.body.firstElementChild;
    }
  }]);

  return ComponentManagerApiController;
}();
/**
 * Holds information about a component HtmlElement, its input elements
 * and the save state for that component.
 */


var FoundComponentInputsModel =
/*#__PURE__*/
function () {
  function FoundComponentInputsModel() {
    _classCallCheck(this, FoundComponentInputsModel);

    this._inputElements = [];
    this._componentElement = null;
    this._componentSaveState = null;
  }
  /**
   * @return {null|Array}
   */


  _createClass(FoundComponentInputsModel, [{
    key: "inputElements",
    get: function get() {
      return this._inputElements;
    }
    /**
     * @param {NodeList} value
     */
    ,
    set: function set(value) {
      if (_typeof(value) !== 'object') {
        //Pssst...array primitives don't exist. Don't tell anyone :)
        console.error('ComponentSaveState: Did not set inputElements since the parameter was not an array. Actual: ', value);
        return;
      }

      this._inputElements = value;
    }
    /**
     * @return {null|HTMLDivElement}
     */

  }, {
    key: "componentElement",
    get: function get() {
      return this._componentElement;
    }
    /**
     * @param {HTMLDivElement} value
     */
    ,
    set: function set(value) {
      if (!value instanceof HTMLDivElement) {
        console.error('ComponentSaveState: Did not set componentElement since the parameter was not an HTMLDivElement.');
        return;
      }

      this._componentElement = value;
    }
    /**
     * @return {null|ComponentSaveState}
     */

  }, {
    key: "componentSaveState",
    get: function get() {
      return this._componentSaveState;
    }
    /**
     * @param {null|ComponentSaveState} value
     */
    ,
    set: function set(value) {
      if (!value instanceof ComponentSaveState) {
        console.error('ComponentSaveState: Did not set componentSaveState since the parameter was not ComponentSaveState.');
        return;
      }

      this._componentSaveState = value;
    }
  }]);

  return FoundComponentInputsModel;
}();
/**
 * Represents data that can be saved and loaded for a Component.
 * Always used in combination with ComponentAreaSaveState
 */


var ComponentSaveState =
/*#__PURE__*/
function () {
  function ComponentSaveState() {
    _classCallCheck(this, ComponentSaveState);

    this._version = '0.9.1';
    this._id = null;
    this._componentTypeId = null;
    this._data = {};
    this._componentableData = {};
    this._sortOrder = null;
    this.toJSON = this._toJson.bind(this);
  }

  _createClass(ComponentSaveState, [{
    key: "_toJson",
    value: function _toJson() {
      return {
        id: this._id,
        version: this._version,
        componentTypeId: this._componentTypeId,
        data: this._data,
        componentableData: this._componentableData,
        sortOrder: this._sortOrder
      };
    }
  }, {
    key: "version",
    get: function get() {
      return this._version;
    },
    set: function set(value) {
      if (typeof value !== 'string') {
        console.error('ComponentSaveState: Did not set version since the parameter was not a string. Actual: ', value);
        return;
      }

      this._version = value;
    }
  }, {
    key: "id",
    get: function get() {
      return this._id;
    },
    set: function set(value) {
      if (value === "" || typeof value !== 'number') {
        console.error('ComponentSaveState: Did not set the id since the parameter was not a number. Actual: ', _typeof(value), 1);
        return;
      }

      this._id = value;
    }
  }, {
    key: "componentTypeId",
    get: function get() {
      return this._componentTypeId;
    },
    set: function set(value) {
      if (value === "" || typeof value !== 'number') {
        console.error('ComponentSaveState: Did not set componentTypeId since the parameter was not a number. Actual: ', _typeof(value), 1);
        return;
      }

      this._componentTypeId = value;
    }
  }, {
    key: "data",
    get: function get() {
      return this._data;
    },
    set: function set(value) {
      if (_typeof(value) !== 'object') {
        console.error('ComponentSaveState: Did not set data since the parameter was not an object. Actual: ', value);
        return;
      }

      this._data = value;
    }
  }, {
    key: "sortOrder",
    get: function get() {
      return this._sortOrder;
    },
    set: function set(value) {
      if (value === "" || typeof value !== 'number') {
        console.error('ComponentSaveState: Did not set sortOrder since the parameter was not a number. Actual: ', value);
      }

      this._sortOrder = value;
    }
  }, {
    key: "componentableData",
    get: function get() {
      return this._componentableData;
    },
    set: function set(value) {
      if (_typeof(value) !== 'object') {
        console.error('ComponentSaveState: Did not set componentableData since the parameter was not an object. Actual: ', value);
        return;
      }

      this._componentableData = value;
    }
  }], [{
    key: "fromObject",
    value: function fromObject(jsonObject) {
      //Validate the object
      if (!jsonObject.hasOwnProperty('id') || typeof jsonObject.id !== 'number' || !jsonObject.hasOwnProperty('componentTypeId') || typeof jsonObject.componentTypeId !== 'number' || !jsonObject.hasOwnProperty('version') || typeof jsonObject.version !== 'string' || !jsonObject.hasOwnProperty('sortOrder') || typeof jsonObject.sortOrder !== 'number' || !jsonObject.hasOwnProperty('data') || _typeof(jsonObject.data) !== 'object' || !jsonObject.hasOwnProperty('componentableData') || _typeof(jsonObject.data) !== 'object') {
        console.error('Could not create a ComponentSaveState instance from an object since that object did not contain all of the following properties of the correct types: id (number), componentTypeId (number), version (string), sortOrder (number), data (array object). Actual: ', jsonObject);
        return;
      } //Create a new instance of the current class that we return later on.


      var componentSaveState = new this();
      componentSaveState.id = jsonObject.id;
      componentSaveState.componentTypeId = jsonObject.componentTypeId;
      componentSaveState.version = jsonObject.version;
      componentSaveState.sortOrder = jsonObject.sortOrder;
      componentSaveState.data = jsonObject.data;
      componentSaveState.componentableData = jsonObject.componentableData;
      return componentSaveState;
    }
  }]);

  return ComponentSaveState;
}();
/**
 * Represents data that can be saved and loaded for a ComponentArea.
 * This is the actual data container for the backend.
 */


var ComponentAreaSaveState =
/*#__PURE__*/
function () {
  function ComponentAreaSaveState() {
    _classCallCheck(this, ComponentAreaSaveState);

    this.toJSON = this._toJson.bind(this);
    this._componentSaveStates = [];
  }
  /**
   * @param {ComponentSaveState} value
   */


  _createClass(ComponentAreaSaveState, [{
    key: "addComponentSaveState",
    value: function addComponentSaveState(value) {
      if (!value instanceof ComponentSaveState) {
        console.error('ComponentAreaSaveState: The passed "componentSaveState" was not an instance of ComponentSaveState. Actual: ', value);
        return;
      }

      this._componentSaveStates.push(value);
    }
  }, {
    key: "getComponentSavestateAt",
    value: function getComponentSavestateAt(index) {
      return this._componentSaveStates[index];
    }
    /**
     *
     * @return {Array}
     * @private
     */

  }, {
    key: "_toJson",
    value: function _toJson() {
      return this._componentSaveStates;
    }
    /**
     *
     * @param {string} json
     * @return {ComponentAreaSaveState}
     */

  }, {
    key: "componentsCount",

    /**
     * Returns the amount of components
     *
     * @return {number}
     */
    get: function get() {
      return this._componentSaveStates.length;
    }
  }], [{
    key: "fromJsonString",
    value: function fromJsonString(json) {
      var jsonObject = null;

      try {
        jsonObject = JSON.parse(json);
      } catch (e) {
        console.error('ComponentAreaSaveState: The given jsonString does not represent ComponentAreaSaveState since the json string was not a valid json');
        return;
      } //Create a new instance of the current class that we return later on.


      var componentAreaSaveState = new this(); //Create components from the saved data and put it in the new ComponentAreaSaveState instance

      var componentsCount = jsonObject.length;

      for (var index = 0; index < componentsCount; index++) {
        var currentComponentSaveStateObject = jsonObject[index];
        var currentComponentSaveState = ComponentSaveState.fromObject(currentComponentSaveStateObject);
        componentAreaSaveState.addComponentSaveState(currentComponentSaveState);
      }

      return componentAreaSaveState;
    }
  }]);

  return ComponentAreaSaveState;
}();
/**
 * Give this class an htmlElement with a data-to-copy attribute,
 * and it will copy the text in the value of the attribute to your clipboard
 * when you click on it.
 */


var CopyText =
/*#__PURE__*/
function () {
  function CopyText(element) {
    _classCallCheck(this, CopyText);

    this.element = element;

    if (!'toCopy' in this.element.dataset) {
      console.error('CopyText: The given element must have a data-to-copy attribute with the value that you want to copy. But did not have that attribute.', element);
    }

    this.addEventListeners();
  }

  _createClass(CopyText, [{
    key: "addEventListeners",
    value: function addEventListeners() {
      this.element.addEventListener('click', this.clickHandler.bind(this));
    }
  }, {
    key: "clickHandler",
    value: function clickHandler(mouseEvent) {
      //Prevent the event from following a button if element is a button
      mouseEvent.preventDefault(); //Get the text to copy

      var text = this.element.dataset.toCopy; //Temporary create an input element

      var input = document.createElement('input');
      document.body.appendChild(input);
      input.value = text;
      input.select();
      document.execCommand("copy");
      document.body.removeChild(input);
      input = null;
    }
  }]);

  return CopyText;
}();
/**
 * Manages file inputs in a wrapper so that files
 *
 * @param {HTMLElement} wrapper
 * @param {HTML5Uploader} HTML5Uploader
 */


var DocumentManager =
/*#__PURE__*/
function () {
  function DocumentManager(wrapper, HTML5Uploader) {
    _classCallCheck(this, DocumentManager);

    //Initialize variables
    this.constructedSuccessFully = false;
    this.wrapper = wrapper;
    this.HTML5Uploader = HTML5Uploader;
    this.accept = "documentsAccept" in wrapper.dataset ? wrapper.dataset.documentsAccept : undefined;

    if ("documentsKey" in wrapper.dataset) {
      this.key = wrapper.dataset.documentsKey;
    } else {
      console.error('Make sure that the wrapper contains the attributes key');
      return;
    }

    this.isSortable = "documentsIsSortable" in wrapper.dataset ? wrapper.dataset.documentsIsSortable === "1" : false;
    this.extensionThumbsFolder = "documentsExtensionThumbsFolder" in wrapper.dataset ? wrapper.dataset.documentsExtensionThumbsFolder : '/img/kms/extension_thumbs/';
    this.availableExtensionThumbs = "documentsAvailableExtensionThumbs" in wrapper.dataset ? wrapper.dataset.documentsAvailableExtensionThumbs : ['svg', 'pdf', 'zip', 'rar', 'csv', 'xlsx', 'mp3', 'mp4', 'docx', 'doc', 'png', 'jpg', 'jpeg', 'gif'], this.enablePreviewsIfPossible = "documentsEnablePreviewsIfPossible" in wrapper.dataset ? wrapper.dataset.documentsEnablePreviewsIfPossible === "1" : false;
    this.maxDocuments = "documentsMaxDocuments" in wrapper.dataset ? wrapper.dataset.documentsMaxDocuments : undefined;
    this.imageProperties = "imageProperties" in wrapper.dataset ? wrapper.dataset.imageProperties : null;
    this.subFolder = "subFolder" in wrapper.dataset ? wrapper.dataset.subFolder : 'documents';
    this.eventMap = {};
    var form = document.getElementById('entity-form');

    if (form) {
      var maxUploadSizeInBytes = form.dataset.maxUploadSize;
      if (maxUploadSizeInBytes) this.maxUploadSizeInBytes = maxUploadSizeInBytes;
    }

    this.uploadSizeExceededMessage = 'You cannot upload more files right now. Please save these first before continuing. The limit is:';

    if (wrapper == null) {
      console.error('The wrapper was not a valid html element. Stopping DocumentManager construction');
      return;
    }

    var documentList = wrapper.getElementsByClassName('files')[0];
    var dataInput = wrapper.querySelector('input[name="' + this.key + '-data"]');

    if (!documentList) {
      console.error('The document uploader needs an ul element with class "files" in the given wrapper');
      return;
    }

    this.documentList = documentList;

    if (!dataInput) {
      console.error('The document uploader needs an input element with name "' + this.key + '-data" in the given wrapper. It is used to keep track of the states of all documents. Stopping DocumentManager construction');
      return;
    }

    this.dataInput = dataInput;

    if (!"uploadedDocuments" in wrapper.dataset) {
      console.error('The wrapper must have a uploadedDocuments dataset attribute containing a json string representing the uploaded documents as an array of documents. Stopping DocumentManager construction');
    }

    var documents = JSON.parse(this.dataInput.value);
    this.constructedSuccessFully = true;
    this.initialize(documents);
  }
  /**
   * Initializes the setup of the document uploader.
   * @param documents string Example structure:
   *  '[{
   *          "id": 3,
   *           "path": "uploads/products/Before 2018-01-25 at 14,48,23_1522928384.png",
   *          "documentable_id": 1,
   *          "documentable_type": "App\\Komma\\Shop\\Products\\Product\\Product",
   *          "created_at": "2018-04-05 11:39:44",
   *          "updated_at": "2018-04-05 11:39:44"
   *  }]'
   *
   */


  _createClass(DocumentManager, [{
    key: "initialize",
    value: function initialize(documents) {
      if (!this.constructedSuccessFully) return;

      if (documents) {
        var length = documents.length;

        for (var index = 0; index < length; index++) {
          var initDocument = documents[index];
          var documentModel = DocumentModel.fromJson(initDocument);

          if (documentModel) {
            this.addDocumentElement(documentModel);
          } else {
            console.error("DocumentManager stumbled upon a document that is not valid upon initializing: ");
            console.error(initDocument);
          }
        }

        this.sortDocuments();
      }

      this.updateDataInput();
      this.setupHtml5Uploader();
    }
    /**
     * Set up the html 5 uploader. Hook to its events
     */

  }, {
    key: "setupHtml5Uploader",
    value: function setupHtml5Uploader() {
      var self = this;
      this.HTML5Uploader.on('uploadStart', this.HTML5UploadStarted.bind(this));
      this.HTML5Uploader.on('updateProgress', this.HTML5UploadProgress.bind(this));
      this.HTML5Uploader.on('uploadComplete', this.HTML5UploadedFile.bind(this));
      this.HTML5Uploader.on('uploadFailed', this.HTML5UploadFailedOrCanceled.bind(this));
      this.HTML5Uploader.on('uploadCanceled', this.HTML5UploadFailedOrCanceled.bind(this));
    }
    /**
     * Triggered when a HTML file upload failed to upload or was canceled.
     *
     * @constructor
     */

  }, {
    key: "HTML5UploadFailedOrCanceled",
    value: function HTML5UploadFailedOrCanceled(file, responseText, extraData) {
      var documentElement = file.documentElement;
      if (!documentElement) return;
      this.documentList.removeChild(documentElement);
      this.updateDataInput();
    }
    /**
     * Triggered when a file was uploaded successfully
     *
     * @param file
     * @param responseText
     * @constructor
     */

  }, {
    key: "HTML5UploadedFile",
    value: function HTML5UploadedFile(file, responseText) {
      var documentElement = file.documentElement;
      if (!documentElement) return;
      var backendDocumentModel = JSON.parse(responseText);
      var documentModel = DocumentModel.fromJson(backendDocumentModel);
      this.attachDocumentModelToDocumentElement(documentElement, documentModel);
      this.updateDocumentElementWithDocumentData(documentElement, documentModel);
      this.updateSortOrder(); //Does also update the data input

      documentElement.querySelector('.drag-icon').classList.remove('is-hidden');
      documentElement.querySelector('.thumb').classList.remove('is-uploading');
      documentElement.querySelector('.percentage').remove();
    }
    /**
     * Triggered when HTML5 Uploader made progress in uploading a file
     *
     * @param file
     * @param percentCompleteOrNull
     * @constructor
     */

  }, {
    key: "HTML5UploadProgress",
    value: function HTML5UploadProgress(file, percentCompleteOrNull) {
      var documentElement = file.documentElement;
      if (!documentElement) return;
      var progress = documentElement.querySelector('.percentage'); //There is still some backend processing time needed. So we don't show 100%. Only when the backend responds with a complete

      if (percentCompleteOrNull && percentCompleteOrNull === 100) percentCompleteOrNull = 99;else if (!percentCompleteOrNull) percentCompleteOrNull = 1; // Update the progress value

      progress.setAttribute("aria-valuenow", percentCompleteOrNull);
    }
    /**
     * Triggered when a HTML 5 upload was started.
     *
     * @param file
     * @constructor
     */

  }, {
    key: "HTML5UploadStarted",
    value: function HTML5UploadStarted(file) {
      var documentElement = this.createDocumentElement();
      this.attachDocumentModelToDocumentElement(documentElement);
      var documentModel = DocumentModel.fromJson(JSON.parse(documentElement.dataset.json));
      documentModel.state = DOCUMENT_STATE_NEW;
      documentModel.name = file.name;
      documentModel.path = file.name;
      documentElement = this.updateDocumentElementWithDocumentData(documentElement, documentModel);
      this.attachDocumentModelToDocumentElement(documentElement, documentModel); // Add the progress element to the thumb

      var progressPercentageString = '<span class="percentage" role="progressbar" aria-valuemin="0" aria-valuemax="100"></span>'; // let progressPercentage = document.createElement('span');
      // progressPercentage.setAttribute('class', 'percentage');
      // progressPercentage.setAttribute("role", "progressbar");
      // progressPercentage.setAttribute("aria-valuemin", 0);
      // progressPercentage.setAttribute("aria-valuemax", 100);

      documentElement.querySelector('.thumb').insertAdjacentHTML('afterbegin', progressPercentageString);
      this.documentList.appendChild(documentElement);
      documentElement.querySelector('.drag-icon').classList.add('is-hidden');
      documentElement.querySelector('.thumb').classList.add('is-uploading'); // documentElement.querySelector('.thumb').classList.add('has-image');

      file.documentElement = documentElement;
    }
    /**
     * Occurs when the user clicked the delete document button
     *
     * @param mouseEvent A javascript mouse event
     */

  }, {
    key: "deleteDocumentButtonClicked",
    value: function deleteDocumentButtonClicked(mouseEvent) {
      var documentElement = mouseEvent.target.parentElement;
      var document = JSON.parse(documentElement.dataset.json);
      if (document.state === DOCUMENT_STATE_DELETED) return;

      if (document.state !== DOCUMENT_STATE_NEW) {
        document.state = DOCUMENT_STATE_DELETED;
        documentElement.classList.add(DOCUMENT_STATE_DELETED);
        documentElement.dataset.json = JSON.stringify(document);
      } else {
        this.documentList.removeChild(documentElement);
      }

      this.updateDataInput();
      this.updateSortOrder();
    }
    /**
     * Occurs when the user modified the document
     *
     * @param event A javascript event
     */

  }, {
    key: "modifiedDocument",
    value: function modifiedDocument(event) {
      var wrapper = event.target.parentElement.parentElement; //Document exists. Updates its json state and hide it.

      var document = JSON.parse(wrapper.dataset.json);
      if (document.state === DOCUMENT_STATE_DELETED) return;
      if (document.state !== DOCUMENT_STATE_NEW) document.state = DOCUMENT_STATE_MODIFIED; //Check what is modified and update the document accordingly

      var nameInput = wrapper.getElementsByClassName('name')[0];
      document.name = nameInput.value;
      wrapper.dataset.json = JSON.stringify(document);
      wrapper.classList.add(DOCUMENT_STATE_MODIFIED);
      this.updateDataInput();
    }
    /**
     * Adds a new document element. When documentModel isn't specified, the document element wil give the user the
     * ability to upload a new document by transforming that document element to a document uploader (fancy fileinput)
     *
     * @param documentModel DocumentModel. You only need to specify it when it is an existing document.
     */

  }, {
    key: "addDocumentElement",
    value: function addDocumentElement() {
      var documentModel = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : undefined;
      //Create an fileInput wrapper in which we put the fileInput and delete / remove button
      var documentElement = this.createDocumentElement();
      if (documentModel !== undefined) this.updateDocumentElementWithDocumentData(documentElement, documentModel);
      this.attachDocumentModelToDocumentElement(documentElement, documentModel); // if(documentModel === undefined) this.transformDocumentElementIntoADocumentUpload(documentElement);
      //Make it dragable if needed. With this it can be sorted

      if (documentModel) {
        this.makeElementRespondToDragging(documentElement, this.isSortable);
        this.makeElementRespondToDragOverAndLeave(documentElement, this.isSortable);
        this.makeElementRespondToDrop(documentElement, this.isSortable);
      } //Add the newly created HTMLElement to the document list


      this.documentList.appendChild(documentElement);
    }
    /**
     * @param documentElement {HTMLElement}
     * @param documentModel {DocumentModel|undefined}
     * @returns {HTMLElement} The document element
     */

  }, {
    key: "attachDocumentModelToDocumentElement",
    value: function attachDocumentModelToDocumentElement(documentElement) {
      var documentModel = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : undefined;

      if (documentModel !== undefined) {
        documentElement.dataset.json = JSON.stringify(documentModel);
      } else {
        documentModel = new DocumentModel();
        documentModel.sort_order = this.getCurrentDocumentsCount() + 1;
        documentElement.dataset.json = JSON.stringify(documentModel);
      }

      return documentElement;
    }
    /**
     *
     * @param documentElement {HTMLElement}
     * @param documentModel {DocumentModel}
     * @return {HTMLElement}
     */

  }, {
    key: "updateDocumentElementWithDocumentData",
    value: function updateDocumentElementWithDocumentData(documentElement, documentModel) {
      var nameInput = documentElement.querySelector('.name');
      var pathParagraph = documentElement.querySelector('.path');
      var thumbImageElement = documentElement.querySelector('.thumb'); //Set the path of the document

      if (pathParagraph) {
        pathParagraph.innerText = documentModel.path;
      } //Set the name of the document


      if (nameInput) {
        nameInput.setAttribute('value', documentModel.name);
      } //Set the thumb image


      if (thumbImageElement && documentModel.path) {
        var thumbUrl = this.getThumbUrlUsingDocumentModel(documentModel);
        var documentExtension = this.getExtensionFromFileName(documentModel.path);
        thumbImageElement.setAttribute('data-filetype', documentExtension);
        var thumbImage = thumbImageElement.querySelector('.thumb__image');

        if (thumbImageElement) {
          if (thumbUrl && documentModel.state !== 'new') {
            thumbImage.style.backgroundImage = "url('" + thumbUrl + "')";
            thumbImageElement.classList.add('has-image');
          } else if (this.availableExtensionThumbs.indexOf(documentExtension) !== -1) {
            thumbImage.style.backgroundImage = "url('" + this.extensionThumbsFolder + documentExtension + ".svg')";
            thumbImageElement.classList.add('has-icon');
          }
        }
      }

      return documentElement;
    }
    /**
     * Creates a li item with a name input, path text paragraph, delete button and a thumb image.
     *
     * @returns {HTMLElement}
     */

  }, {
    key: "createDocumentElement",
    value: function createDocumentElement() {
      var documentElement = document.createElement('li');
      documentElement.className = 'document';
      var nameInput = document.createElement('input');
      nameInput.setAttribute('class', 'name');
      nameInput.setAttribute('type', 'text');
      nameInput.addEventListener('change', this.modifiedDocument.bind(this));
      var pathText = document.createElement('p');
      pathText.setAttribute('class', 'path');
      var contentWrapper = document.createElement('div');
      contentWrapper.setAttribute('class', 'content-wrapper');
      contentWrapper.appendChild(nameInput);
      contentWrapper.appendChild(pathText);
      var deleteButton = document.createElement('span');
      deleteButton.className = 'delete'; // deleteButton.innerText = 'x';

      deleteButton.addEventListener('click', this.deleteDocumentButtonClicked.bind(this));
      var dragIcon = document.createElement('span');
      dragIcon.className = 'drag-icon';
      var thumbContainer = document.createElement('div');
      thumbContainer.className = 'thumb';
      thumbContainer.setAttribute('draggable', 'false'); //Prevent image from being dragged and interfering with what the makeElementDraggableIfNeeded method does

      var thumbImage = document.createElement('div');
      thumbImage.className = 'thumb__image';
      thumbContainer.appendChild(thumbImage);
      var fileInput = document.createElement('input');
      fileInput.setAttribute('type', 'file');
      fileInput.setAttribute('name', this.key + '-' + (this.getCurrentDocumentsCount() + 1));
      if (this.accept) fileInput.setAttribute('accept', this.accept);
      documentElement.appendChild(fileInput);
      fileInput.style.display = "none";
      documentElement.appendChild(dragIcon);
      documentElement.appendChild(thumbContainer);
      documentElement.appendChild(contentWrapper);
      documentElement.appendChild(deleteButton);
      return documentElement;
    }
    /**
     * Retrieves a javascript file instance and creates a documentElement for it.
     * Usually used by outside classes to inject a document from a file to upload.
     *
     * @param fileInstance {File}
     */

  }, {
    key: "receiveFile",
    value: function receiveFile(fileInstance) {
      if (!fileInstance) {
        console.warn('DocumentManager:receiveFile: Expected to get a file but did not get one');
        return;
      } // console.log('this.getCurrentDocumentsCount() < this.maxDocuments: ', this.getCurrentDocumentsCount(), this.maxDocuments, 'File', fileInstance);


      if (this.getCurrentDocumentsCount() < this.maxDocuments) {
        var documentElement = this.createDocumentElement();
        this.attachDocumentModelToDocumentElement(documentElement);

        if (this.HTML5Uploader.isSupported() === false) {
          documentElement = this.giveDocumentElementAFile(documentElement, fileInstance); //Add the newly created HTMLElement to the document list

          this.documentList.appendChild(documentElement);

          if (this.formSizeExceeded()) {
            documentElement.parentElement.removeChild(documentElement); //Remove the documentElement
          }

          this.updateSortOrder();
        } else {
          // console.log('Uploading via HTML5');
          var extraData = {
            'imageProperties': this.imageProperties,
            'subFolder': this.subFolder
          };
          this.HTML5Uploader.upload(fileInstance, JSON.stringify(extraData));
        }
      }
    }
    /**
     * Returns the amount of documents
     */

  }, {
    key: "getCurrentDocumentsCount",
    value: function getCurrentDocumentsCount() {
      return this.documentList.getElementsByClassName('document').length;
    }
    /**
     * Sets the file of an document element to the given one.
     * Makes the drag icon invisible since it was not uploaded yet
     * and cannot be positioned. Sets the file input to the files name and
     * loads the thumb with the appropiate extension
     *
     * @param {HTMLElement} documentElement
     * @param {File} file
     * @return {HTMLElement} the documentElement
     */

  }, {
    key: "giveDocumentElementAFile",
    value: function giveDocumentElementAFile(documentElement, file) {
      var filename = file.name;
      var extension = this.getExtensionFromFileName(filename);
      var thumbUrl = this.previewThumbUrlIsImage(filename); //Get the document element and some parts in it

      var nameInputElement = documentElement.querySelector('.name');
      var thumbImageElement = documentElement.querySelector('.thumb');
      var contentWrapper = documentElement.querySelector('.content-wrapper');
      var deleteButton = documentElement.querySelector('.delete');
      var dragIcon = documentElement.querySelector('.drag-icon');
      var fileInputElement = documentElement.querySelector('input[type="file"]'); //Hide the drag icon. Dragging is only supported after file upload

      dragIcon.style.display = 'none'; //Update file input with a file if the input does not have a file.

      if (fileInputElement && !fileInputElement.files[0]) {
        fileInputElement.files = new FileList(file);
      } //Make thumb visible and load extension image


      thumbImageElement.setAttribute('data-filetype', extension);

      if (this.availableExtensionThumbs.indexOf(extension) !== -1) {
        var thumbImage = thumbImageElement.querySelector('thumb__image');
        thumbImage.style.backgroundImage = "url('" + this.extensionThumbsFolder + extension + ".svg')";
        thumbImage.classList.add('has-icon');
      }

      nameInputElement.value = filename;
      return documentElement;
    }
    /**
     * Returns the thumbnail for the document or false if it isn't available
     */

  }, {
    key: "getThumbUrlUsingDocumentModel",
    value: function getThumbUrlUsingDocumentModel(documentModel) {
      if (documentModel) {
        var filename = documentModel.path;
        if (documentModel.thumb_image_url !== '') filename = documentModel.thumb_image_url;
        if (this.previewThumbUrlIsImage(filename)) return filename;
      }

      return false;
    }
    /**
     * Returns the given preview thumb url for an extension if that extension is an image or false if it is not an image
     */

  }, {
    key: "previewThumbUrlIsImage",
    value: function previewThumbUrlIsImage(url) {
      if (!this.enablePreviewsIfPossible) return false;
      var extension = this.getExtensionFromFileName(url);

      switch (extension) {
        case 'png':
        case 'jpg':
        case 'jpeg':
        case 'gif':
          return url;

        case 'pdf':
        default:
          return false;
      }
    }
    /**
     * Returns the extension without leading dot from a file name or '' when it did not have an extension.
     */

  }, {
    key: "getExtensionFromFileName",
    value: function getExtensionFromFileName(filename) {
      var extension = filename.split(/[.]+/).pop();
      if (extension === filename) return '';
      return extension.toLowerCase();
    }
    /**
     * Check if the total form size isn't bigger then the maximum allowed upload size.
     * Warning: when maxUploadSizeInBytes isn't set this function wil always return false.
     */

  }, {
    key: "formSizeExceeded",
    value: function formSizeExceeded() {
      if (this.maxUploadSizeInBytes) {
        var exceeded = false;
        var currentTotalInBytes = 0; //validate total

        this.documentList.querySelectorAll('input[type="file"]').forEach(function (fileInput) {
          if (fileInput.files.length !== 1) return;
          var document = fileInput.files[0];
          currentTotalInBytes += document.size;
        });
        console.log('max upload size in bytes: ' + this.maxUploadSizeInBytes + ' current upload size in bytes: ' + currentTotalInBytes);

        if (currentTotalInBytes > this.maxUploadSizeInBytes) {
          var maxUploadSizeInMegaBytes = this.maxUploadSizeInBytes / 1048576;
          exceeded = true;
          alert(this.uploadSizeExceededMessage + ' ' + maxUploadSizeInMegaBytes + ' MegaBytes');
        }

        return exceeded;
      } else {
        return false;
      }
    }
    /**
     * Update the data input with an array of json objects that are the data-json attribues of each "document" li.
     * This is used for the backend to determine what has changed and what did not.
     */

  }, {
    key: "updateDataInput",
    value: function updateDataInput() {
      var documentsDataArray = []; //Please notice that the first document isnt a document but is the document add button. That's why we check for the json dataset

      var documents = this.documentList.children;
      var nDocuments = documents.length;

      for (var documentsIndex = 0; documentsIndex < nDocuments; documentsIndex++) {
        var documentElement = documents[documentsIndex];

        if ("json" in documentElement.dataset) {
          documentsDataArray.push(JSON.parse(documentElement.dataset.json));
        }
      }

      if (nDocuments >= this.maxDocuments) {
        this.wrapper.querySelector('.drag-and-drop-area').classList.add('is-hidden');
      } else {
        this.wrapper.querySelector('.drag-and-drop-area').classList.remove('is-hidden');
      }

      this.dataInput.value = JSON.stringify(documentsDataArray);
      var changeEvent = createNewEvent('change');
      dispatchEventForElement(this.dataInput, changeEvent);
    }
    /**
     * Updates the sort order of each document when needed and then updates the data input field
     */

  }, {
    key: "updateSortOrder",
    value: function updateSortOrder() {
      //Please notice that the first document isnt a document but is the document add button. That's why we check for the json dataset
      var documents = this.documentList.children;
      var nDocuments = documents.length;
      var currentSortNumber = 1;

      for (var documentsIndex = 0; documentsIndex < nDocuments; documentsIndex++) {
        var documentElement = documents[documentsIndex];
        if ("json" in documentElement.dataset === false) continue; //used to skip the add new document button

        var _document2 = JSON.parse(documentElement.dataset.json);

        if (_document2.state === DOCUMENT_STATE_DELETED) continue;
        _document2.sort_order = currentSortNumber;
        console.log('Document', _document2);
        if (_document2.state !== DOCUMENT_STATE_NEW) _document2.state = DOCUMENT_STATE_MODIFIED;
        documentElement.dataset.json = JSON.stringify(_document2);
        documentElement.classList.add(DOCUMENT_STATE_MODIFIED);
        currentSortNumber++;
      }

      this.updateDataInput();
    }
    /**
     * Does have a look at the documentElements and sorts them by using their sort order values
     */

  }, {
    key: "sortDocuments",
    value: function sortDocuments() {
      var documentElements = this.documentList.children;
      documentElements = Array.prototype.slice.call(documentElements); //Converts the htmlCollection of HTMLElement documentElements to an Array of documentElements

      documentElements.sort(function (itemA, itemB) {
        var itemADocument = JSON.parse(itemA.dataset.json);
        var itemBDocument = JSON.parse(itemB.dataset.json); // console.log(itemADocument.sort_order, itemBDocument.sort_order);

        if (itemADocument.sort_order < itemBDocument.sort_order) return -1;
        return 1;
      });
      var sortedDocumentsLength = documentElements.length;

      for (var index = 1; index < sortedDocumentsLength; index++) {
        this.documentList.removeChild(documentElements[index]);
        if (this.documentList.length > 1) this.documentList.insertBefore(documentElements[index], this.documentList.firstChild);else this.documentList.appendChild(documentElements[index]);
      }
    }
    /**
     * @param element HTMLElement that needs to be draggable or not
     * @param respondOrNotBoolean
     * @return {DocumentManager}
     */

  }, {
    key: "makeElementRespondToDragging",
    value: function makeElementRespondToDragging(element) {
      var respondOrNotBoolean = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
      element.setAttribute('draggable', respondOrNotBoolean ? 'true' : 'false');
      element.id = this.key + '_' + this.getCurrentDocumentsCount();

      if (respondOrNotBoolean) {
        element.removeEventListener('dragstart', this.drag.bind(this));
        element.addEventListener('dragstart', this.drag.bind(this));
      } else {
        element.removeEventListener('dragstart', this.drag.bind(this));
      }

      return this;
    }
    /**
     * @param element HTMLElement that needs to be draggable or not
     * @param respondOrNotBoolean
     * @return {DocumentManager}
     */

  }, {
    key: "makeElementRespondToDrop",
    value: function makeElementRespondToDrop(element) {
      var respondOrNotBoolean = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;

      if (respondOrNotBoolean) {
        element.removeEventListener('drop', this.drop.bind(this));
        element.addEventListener('drop', this.drop.bind(this));
      } else {
        element.removeEventListener('drop', this.drop.bind(this));
      }

      return this;
    }
    /**
     * Prepares the element so that it can receive items that are dropped onto it
     *
     * @param element HTMLElement that needs can be a drop target or not
     * @param respondOrNotBoolean wheter or not it should be a drop target
     */

  }, {
    key: "makeElementRespondToDragOverAndLeave",
    value: function makeElementRespondToDragOverAndLeave(element) {
      var respondOrNotBoolean = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;

      if (respondOrNotBoolean) {
        element.removeEventListener('dragover', this.dragOver.bind(this));
        element.removeEventListener('dragleave', this.dragLeave.bind(this));
        element.addEventListener('dragover', this.dragOver.bind(this));
        element.addEventListener('dragleave', this.dragLeave.bind(this));
      } else {
        element.removeEventListener('dragover', this.dragOver.bind(this));
        element.removeEventListener('dragleave', this.dragLeave.bind(this));
      }
    }
    /**
     * Occurs when a document (li) HTMLElement is being dragged
     *
     * @param dragEvent
     */

  }, {
    key: "drag",
    value: function drag(dragEvent) {
      if (!dragEvent.target.id) return;
      dragEvent.stopPropagation();
      dragEvent.dataTransfer.setData("text", dragEvent.target.id); //Set the id of the thing that is being dragged in the event.

      var draggedElement = document.getElementById(dragEvent.target.id);
      this.triggerEvent('drag', draggedElement);
    }
    /**
     * Occurs when a document (li) HTMLElement is being dragged over the target element.
     * So the dragEvent target is not the element that you drag, but the place / HTMLElement where it may be dropped.
     *
     * @param dragEvent
     */

  }, {
    key: "dragOver",
    value: function dragOver(dragEvent) {
      dragEvent.preventDefault(); //Sets target HTMLElement to allow a drop

      dragEvent.stopPropagation();
      if (!dragEvent.target.id) return;
      var draggedElementId = dragEvent.dataTransfer.getData("text");
      var draggedElement = document.getElementById(draggedElementId);
      this.enableOrDisablePointerEventsOnChildrenOfElement(dragEvent.target, false);
      dragEvent.target.classList.add('isDropTarget');
      this.triggerEvent('dragLeave', [draggedElement, dragEvent.target]);
    }
    /**
     * Occurs when a document (li) HTMLElement is NOT being dragged anymore over the target element.
     *
     * @param dragEvent
     */

  }, {
    key: "dragLeave",
    value: function dragLeave(dragEvent) {
      if (!dragEvent.target.id) return;
      dragEvent.stopPropagation();
      var draggedElementId = dragEvent.dataTransfer.getData("text");
      var draggedElement = document.getElementById(draggedElementId);
      this.enableOrDisablePointerEventsOnChildrenOfElement(dragEvent.target, true);
      dragEvent.target.classList.remove('isDropTarget');
      this.triggerEvent('dragLeave', [draggedElement, dragEvent.target]);
    }
    /**
     * Occurs when a document is being dropped
     *
     * @param dragEvent
     */

  }, {
    key: "drop",
    value: function drop(dragEvent) {
      dragEvent.preventDefault(); //Prevent browser from activating links and buttons

      if (!dragEvent.target.id) return;
      var draggedElementId = dragEvent.dataTransfer.getData("text");
      var draggedElement = document.getElementById(draggedElementId);
      var targetElement = dragEvent.target;
      targetElement.classList.remove('isDropTarget'); //Create a shim element that keeps track of where the dragged element was and where the target element must be inserted

      var shimElement = document.createElement('div');
      draggedElement.parentElement.insertBefore(shimElement, draggedElement); //Move the dragged element in just before the target element

      draggedElement.parentElement.insertBefore(draggedElement, targetElement); //Move the target element before the shim and then remove the shim since we only needed that to mark the dragged elements original position

      draggedElement.parentElement.insertBefore(targetElement, shimElement);
      shimElement.parentElement.removeChild(shimElement);
      this.triggerEvent('drop', [draggedElement, targetElement]);
      this.enableOrDisablePointerEventsOnChildrenOfElement(targetElement, true);
      this.updateSortOrder();
    }
    /**
     * Enables / disables the listening to pointer (example: mouse) events on an element
     * and all of it's children. This prevents for example the dragover event from beeing canceled
     * when dragging over an input that resides in an element which listens to the dragover event
     * because the input captures the mouse.
     */

  }, {
    key: "enableOrDisablePointerEventsOnChildrenOfElement",
    value: function enableOrDisablePointerEventsOnChildrenOfElement(element, enable) {
      var length = element.children.length;

      for (var index = 1; index < length; index++) {
        if (enable === false) {
          element.children[index].style.pointerEvents = 'none';
        } else {
          element.children[index].style.pointerEvents = null;
        }

        var childrenLength = element.children.children;

        for (var childrenIndex = 1; childrenIndex < childrenLength; childrenIndex++) {
          this.enableOrDisablePointerEventsOnChildrenOfElement(element.children.children[index], enable);
        }
      }
    }
    /**
     * Registers a event to a callback and returns the a reference to the event handler for that specific event
     *
     * @param event (string)
     * @param callback (callable)
     */

  }, {
    key: "on",
    value: function on(event, callback) {
      if (!this.eventMap.hasOwnProperty(event)) this.eventMap[event] = [];
      return this.eventMap[event].push(callback);
    }
    /**
     * Call event callbacks
     *
     * @param event (string)
     * @param eventArgs (array) an array of arguments to pass to the callback
     */

  }, {
    key: "triggerEvent",
    value: function triggerEvent(event, eventArgs) {
      if (!this.eventMap.hasOwnProperty(event)) return;
      var nEvents = this.eventMap[event].length;

      for (var index = 0; index < nEvents; index++) {
        var callback = this.eventMap[event][index];

        if (eventArgs && eventArgs.length > 0) {
          callback.apply(this, eventArgs);
        } else {
          callback.call(this);
        }
      }
    }
  }]);

  return DocumentManager;
}();
/**
 * Represents a Document. It the equivalent of the laravel php version
 */


var DocumentModel =
/*#__PURE__*/
function () {
  function DocumentModel() {
    _classCallCheck(this, DocumentModel);

    this.id = -1;
    this.path = '';
    this.state = DOCUMENT_STATE_NEW;
    this.name = '';
    this.sort_order = 1;
    this.thumb_image_url = '';
    this.small_image_url = '';
    this.medium_image_url = '';
    this.large_image_url = '';
    this.documentable_id = -1;
    this.documentable_type = '';
    this.upload_tracker_id = -1;
    this.created_at = '';
    this.updated_at = '';
  }

  _createClass(DocumentModel, null, [{
    key: "isValidDocumentJson",
    value: function isValidDocumentJson(jsonData) {
      return jsonData.hasOwnProperty('id') && jsonData.hasOwnProperty('path') && jsonData.hasOwnProperty('state') && jsonData.hasOwnProperty('name') && jsonData.hasOwnProperty('sort_order') && jsonData.hasOwnProperty('thumb_image_url') && jsonData.hasOwnProperty('small_image_url') && jsonData.hasOwnProperty('medium_image_url') && jsonData.hasOwnProperty('large_image_url') && jsonData.hasOwnProperty('documentable_id') && jsonData.hasOwnProperty('documentable_type') && jsonData.hasOwnProperty('created_at') && jsonData.hasOwnProperty('updated_at');
    }
  }, {
    key: "fromJson",
    value: function fromJson(jsonData) {
      if (!DocumentModel.isValidDocumentJson(jsonData)) return false;
      var documentModel = new DocumentModel();
      documentModel.id = jsonData.id;
      documentModel.path = jsonData.path;
      documentModel.state = jsonData.state;
      documentModel.name = jsonData.name;
      documentModel.sort_order = jsonData.sort_order;
      documentModel.thumb_image_url = jsonData.thumb_image_url;
      documentModel.small_image_url = jsonData.small_image_url;
      documentModel.medium_image_url = jsonData.medium_image_url;
      documentModel.large_image_url = jsonData.large_image_url;
      documentModel.documentable_id = jsonData.id;
      documentModel.documentable_type = jsonData.documentable_type;
      documentModel.upload_tracker_id = jsonData.upload_tracker_id;
      documentModel.created_at = jsonData.created_at;
      documentModel.updated_at = jsonData.updated_at;
      return documentModel;
    }
  }]);

  return DocumentModel;
}();

var DOCUMENT_STATE_NEW = 'new';
var DOCUMENT_STATE_PRISTINE = 'pristine';
var DOCUMENT_STATE_MODIFIED = 'modified';
var DOCUMENT_STATE_DELETED = 'deleted';
/**
 * Used for setting files
 */

var FileList = function FileList() {
  var _ref;

  for (var _len = arguments.length, items = new Array(_len), _key = 0; _key < _len; _key++) {
    items[_key] = arguments[_key];
  }

  _classCallCheck(this, FileList);

  // flatten rest parameter
  items = (_ref = []).concat.apply(_ref, _toConsumableArray(items)); // check if every element of array is an instance of `File`

  if (items.length && !items.every(function (file) {
    return file instanceof File;
  })) {
    throw new TypeError("expected argument to FileList is File or array of File objects");
  } // use `ClipboardEvent("").clipboardData` for Firefox, which returns `null` at Chromium
  // we just need the `DataTransfer` instance referenced by `.clipboardData`


  var dt = new ClipboardEvent("").clipboardData || new DataTransfer(); // add `File` objects to `DataTransfer` `.items`

  var _iteratorNormalCompletion = true;
  var _didIteratorError = false;
  var _iteratorError = undefined;

  try {
    for (var _iterator = items[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
      var file = _step.value;
      dt.items.add(file);
    }
  } catch (err) {
    _didIteratorError = true;
    _iteratorError = err;
  } finally {
    try {
      if (!_iteratorNormalCompletion && _iterator.return != null) {
        _iterator.return();
      }
    } finally {
      if (_didIteratorError) {
        throw _iteratorError;
      }
    }
  }

  return dt.files;
};

var FileDragAndDropHandler =
/*#__PURE__*/
function () {
  /**
   *
   *
   * @param areaElement {HTMLElement}
   */
  function FileDragAndDropHandler(areaElement) {
    _classCallCheck(this, FileDragAndDropHandler);

    this.hooks = [];
    this.selectButton = areaElement.querySelector('button.select');

    if (!this.selectButton) {
      console.log('FileDragAndDropHandler:constructor The document area must have a button in it with class select. But did not have one. Not responding to drag and drop');
      return;
    }

    this.fileCatcherInput = areaElement.querySelector('input[type="file"]');

    if (!this.fileCatcherInput) {
      console.log('FileDragAndDropHandler:constructor The document area must have file input. But did not have one. Not responding to drag and drop');
      return;
    }

    this.makeElementRespondToDragOverAndLeave(areaElement);
    this.makeElementRespondToDrop(areaElement);
    this.makeElementRespondToClick(areaElement);
    this.makeFileInputPassFilesToHooks();
  }
  /**
   * Adds an event listener to the file input and makes it pass its files to the document manager
   */


  _createClass(FileDragAndDropHandler, [{
    key: "makeFileInputPassFilesToHooks",
    value: function makeFileInputPassFilesToHooks() {
      var self = this;
      this.fileCatcherInput.addEventListener('change', function (event) {
        var nFiles = event.target.files.length;

        for (var index = 0; index < nFiles; index++) {
          var file = event.target.files[index]; // console.log(file);

          self.createFileListForFileAndPassToHooks(file);
        }

        event.target.value = '';
      });
    }
    /**
     * Catches click on the element and passes them trough to the file input
     */

  }, {
    key: "makeElementRespondToClick",
    value: function makeElementRespondToClick(element) {
      var self = this;
      element.addEventListener('click', function (event) {
        self.fileCatcherInput.click();
      });
    }
    /**
     * Hooks a hookable (an object that has a addFile method / function) to the drag and drop handler so that the drag and drop handler passes inputs to the
     * hookable
     * @param hookable {Class}
     */

  }, {
    key: "hookTo",
    value: function hookTo(hookable) {
      if (typeof hookable.receiveFile !== 'function') {
        console.error('The given hookable does not have a required receiveFile method / function. Not hooking the given "hookable".');
        return;
      }

      this.hooks.push(hookable);
    }
    /**
     * @param element HTMLElement that needs to be draggable or not
     * @param respondOrNotBoolean
     * @return {FileDragAndDropHandler}
     */

  }, {
    key: "makeElementRespondToDrop",
    value: function makeElementRespondToDrop(element) {
      var respondOrNotBoolean = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;

      if (respondOrNotBoolean) {
        element.removeEventListener('drop', this.drop.bind(this));
        element.addEventListener('drop', this.drop.bind(this));
      } else {
        element.removeEventListener('drop', this.drop.bind(this));
      }

      return this;
    }
    /**
     * Prepares the element so that it can receive items that are dropped onto it
     *
     * @param element HTMLElement that needs can be a drop target or not
     * @param respondOrNotBoolean wheter or not it should be a drop target
     */

  }, {
    key: "makeElementRespondToDragOverAndLeave",
    value: function makeElementRespondToDragOverAndLeave(element) {
      var respondOrNotBoolean = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;

      if (respondOrNotBoolean) {
        element.removeEventListener('dragover', this.dragOver.bind(this));
        element.removeEventListener('dragleave', this.dragLeave.bind(this));
        element.addEventListener('dragover', this.dragOver.bind(this));
        element.addEventListener('dragleave', this.dragLeave.bind(this));
      } else {
        element.removeEventListener('dragover', this.dragOver.bind(this));
        element.removeEventListener('dragleave', this.dragLeave.bind(this));
      }
    }
    /**
     * Occurs when a document (li) HTMLElement is being dragged over the target element.
     * So the dragEvent target is not the element that you drag, but the place / HTMLElement where it may be dropped.
     *
     * @param dragEvent
     */

  }, {
    key: "dragOver",
    value: function dragOver(dragEvent) {
      dragEvent.preventDefault(); //Sets target HTMLElement to allow a drop

      dragEvent.stopPropagation();
      if (!dragEvent.target.id) return;
      this.enableOrDisablePointerEventsOnChildrenOfElement(dragEvent.target, false);
      dragEvent.target.classList.add('isDropTarget');
    }
    /**
     * Occurs when a document (li) HTMLElement is NOT being dragged anymore over the target element.
     *
     * @param dragEvent
     */

  }, {
    key: "dragLeave",
    value: function dragLeave(dragEvent) {
      if (!dragEvent.target.id) return;
      dragEvent.stopPropagation();
      this.enableOrDisablePointerEventsOnChildrenOfElement(dragEvent.target, true);
      dragEvent.target.classList.remove('isDropTarget');
    }
    /**
     * Occurs when a document is being dropped
     *
     * @param dragEvent
     */

  }, {
    key: "drop",
    value: function drop(dragEvent) {
      dragEvent.preventDefault(); //Prevent browser from activating links and buttons

      var targetElement = dragEvent.target;
      targetElement.classList.remove('isDropTarget');
      this.enableOrDisablePointerEventsOnChildrenOfElement(targetElement, true); // Prevent default behavior (Prevent file from being opened)

      dragEvent.preventDefault();

      if (dragEvent.dataTransfer.items) {
        // Use DataTransferItemList interface to access the file(s)
        for (var i = 0; i < dragEvent.dataTransfer.items.length; i++) {
          // If dropped items aren't files, reject them
          if (dragEvent.dataTransfer.items[i].kind === 'file') {
            var file = dragEvent.dataTransfer.items[i].getAsFile();
            this.createFileListForFileAndPassToHooks(file);
          }
        }
      } else {
        // Use DataTransfer interface to access the file(s)
        for (var _i2 = 0; _i2 < dragEvent.dataTransfer.files.length; _i2++) {
          var _file = dragEvent.dataTransfer.files[_i2];
          this.createFileListForFileAndPassToHooks(_file);
        }
      }
    }
  }, {
    key: "createFileListForFileAndPassToHooks",
    value: function createFileListForFileAndPassToHooks(file) {
      var length = this.hooks.length;

      for (var index = 0; index < length; index++) {
        this.hooks[index].receiveFile(file);
      }
    }
    /**
     * Enables / disables the listening to pointer (example: mouse) events on an element
     * and all of it's children. This prevents for example the dragover event from beeing canceled
     * when dragging over an input that resides in an element which listens to the dragover event
     * because the input captures the mouse.
     */

  }, {
    key: "enableOrDisablePointerEventsOnChildrenOfElement",
    value: function enableOrDisablePointerEventsOnChildrenOfElement(element, enable) {
      var length = element.children.length;

      for (var index = 1; index < length; index++) {
        if (enable === false) {
          element.children[index].style.pointerEvents = 'none';
        } else {
          element.children[index].style.pointerEvents = null;
        }

        var childrenLength = element.children.children;

        for (var childrenIndex = 1; childrenIndex < childrenLength; childrenIndex++) {
          this.enableOrDisablePointerEventsOnChildrenOfElement(element.children.children[index], enable);
        }
      }
    }
  }]);

  return FileDragAndDropHandler;
}();

var ImageController =
/*#__PURE__*/
function () {
  function ImageController(key, images, maxImages) {
    _classCallCheck(this, ImageController);

    //console.log('Image controller got images: ');
    //console.log(images);
    //console.log('ImageController: key: '+key);
    //console.log('ImageController: maxImages: '+maxImages);
    //
    this.key = key;
    this.currentImageListId = "#image-list-" + key;
    this.fileCounter = 0;
    this.maxImages = maxImages;
    this.images = images; //

    this.initialize();
    this.checkNewButtonState();
  }

  _createClass(ImageController, [{
    key: "initialize",
    value: function initialize() {
      var self = this; // Click on an existing image to remove it

      $(self.currentImageListId + ' .image-thumb-li').click(function () {
        $(this).remove();
        self.removeFromImages($('img', this).attr('data-image-id'));
      }); // When a file is selected for uploading

      $(document).on('click', self.currentImageListId + ' .new-image input', function (e) {
        //console.log(self.countActiveImages());
        if (self.maxImages != '' && self.countActiveImages() >= self.maxImages) {
          e.preventDefault();
          return;
        }
      });
      $(document).on('change', self.currentImageListId + ' .new-image input', function () {
        //console.log("images.blade.php: Change event triggered");
        if (self.maxImages != '' && self.countActiveImages() >= self.maxImages) return; //console.log('Checking old input element: #'+self.key+'-'+self.fileCounter);

        var $oldInput = $('#' + self.key + '-' + self.fileCounter); //console.log("old input: ");
        //console.log($oldInput);
        //console.log("current image list element: "+self.currentImageListId + ' .uploads :');
        //console.log($(self.currentImageListId + ' .uploads'));

        $(self.currentImageListId + ' .uploads').append($oldInput);
        self.images.push({
          id: null,
          name: $oldInput.attr('id'),
          delete: false
        });
        self.updateImages();
        $oldInput.unbind('change'); //console.log("unbinded change event from the old input element");

        var files = $oldInput.prop('files'); //console.log("files from property from the old input: ");
        //console.log(files);
        // Removing multiple files doesn't work yet, so it is disabled

        for (var i = 0; i < files.length; i++) {
          self.insertImage(files[i], self.fileCounter, i);
        }

        self.fileCounter++;
        $(self.currentImageListId + ' .new-image').append('<input type="file" id="' + self.key + '-' + self.fileCounter + '" name="' + self.key + '-' + self.fileCounter + '" accept="image/*" />');
      });
      this.checkNewButtonState();
    }
  }, {
    key: "insertImage",
    value: function insertImage(file, fileCounter, imageCounter) {
      var self = this; //console.log('insertImage');

      var reader = new FileReader();

      reader.onload = function (e) {
        var dataURL = e.target.result;
        $(self.currentImageListId + ' .thumbs').append('' + '<li id="image-thumb-' + this.key + '-' + fileCounter + '-' + imageCounter + '">' + '<img src="' + dataURL + '" />' + '<div class="deleteImage">тип</div>' + '</li>');
        var thumb = $('#image-thumb-' + self.key + '-' + fileCounter + '-' + imageCounter); // Remove image for uploading when clicked

        thumb.click(function () {
          self.removeFromImages('' + self.key + '-' + fileCounter);
          thumb.remove();
          var element = $('#' + self.key + '-' + fileCounter);
          element.val('');
          element.remove();
        });
      };

      this.checkNewButtonState();
      reader.readAsDataURL(file);
    }
  }, {
    key: "checkNewButtonState",
    value: function checkNewButtonState() {
      var button = $(self.currentImageListId + ' .new-image');

      if (this.maxImages !== undefined && this.countActiveImages() >= this.maxImages) {
        //hide the button
        button.hide(); // //console.log("hide add new image button");
      } else {
        //show the button
        button.show(); // //console.log("show add new image button");
      }
    }
  }, {
    key: "updateImages",
    value: function updateImages() {
      var images = JSON.stringify(this.images, null, 2); //console.log('Update images: '+images);

      $('#' + this.key).val(images); //console.log("Updated images: id:"+'#'+this.key+" with json data:"+JSON.stringify(this.images, null, 2));
      //console.log($('#'+this.key));

      this.checkNewButtonState();
    }
  }, {
    key: "countActiveImages",
    value: function countActiveImages() {
      //console.log("countActiveImages: ");
      //console.log(this.images);
      var count = 0;

      for (var i = 0; i < this.images.length; i++) {
        if (!this.images[i].delete) count++;
      } //console.log("countActiveImages thats: "+count+" length: "+this.images.length);


      return count;
    }
  }, {
    key: "removeFromImages",
    value: function removeFromImages(idOrFileInputName) {
      //console.log('Removing image: '+idOrFileInputName);
      //console.log('this.images: ');
      //console.log(this.images);
      for (var i = 0; i < this.images.length; i++) {
        if (this.images[i].name == idOrFileInputName) {
          this.images.splice(i, 1);
          this.updateImages();
          return;
        }

        if (this.images[i].id == idOrFileInputName) {
          this.images[i].delete = true;
          this.updateImages();
          return;
        }
      }
    }
  }]);

  return ImageController;
}();

var MultiSelect =
/*#__PURE__*/
function () {
  function MultiSelect(attributeWrapper) {
    _classCallCheck(this, MultiSelect);

    this.attributeKey = attributeWrapper.dataset.key;
    this.value = attributeWrapper.querySelector('[name="' + this.attributeKey + '"]').value;
    /**Select all elements for easy access later on**/

    this.input = document.getElementById(this.attributeKey);
    this.openbutton = document.getElementById(this.attributeKey + "-open");
    this.autoCompleteSelectField = document.getElementById(this.attributeKey + "-fake");
    this.itemsWrapper = document.getElementById(this.attributeKey + "_items");
    this.autoSaveUrl = attributeWrapper.dataset.autosaveUrl;
    this.maxItemsToSelect = attributeWrapper.dataset.maxItemsToSelect;
    this.selectOptions = MultiSelect._buildSelectOptionsFromObject(JSON.parse(attributeWrapper.dataset.items));
    this.dataSet = MultiSelect._buildDataSetFromSelectOptions(this.selectOptions);
    this.sortable = attributeWrapper.dataset.sortable;

    if (this.sortable) {
      var _self = this;

      this.dragElement = null; // Sorting starts

      this.itemsWrapper.addEventListener('dragstart', function (evt) {
        _self.dragElement = evt.target; // Remembering an element that will be moved
        // Limiting the movement type

        evt.dataTransfer.effectAllowed = 'move';
        evt.dataTransfer.setData('Text', _self.dragElement.textContent); // Subscribing to the events at dnd

        _self.itemsWrapper.addEventListener('dragover', _self._onDragOver.bind(_self), false);

        _self.itemsWrapper.addEventListener('dragend', _self._onDragEnd.bind(_self), false);

        setTimeout(function () {
          // If this action is performed without setTimeout, then
          // the moved object will be of this class.
          _self.dragElement.classList.add('ghost');
        }, 0);
      }, false);
    }

    this._makeMultiSelect();

    this._addItems();

    this._updateRealInput();

    this._enableListeners(true);

    this._enableOpenButton();
  }

  _createClass(MultiSelect, [{
    key: "_enableListeners",
    value: function _enableListeners(enable) {
      this.listenersEnabled = enable;
      var length = this.itemsWrapper.children.length; //Add / remove listeners to items

      for (var i = 0; i < length; i++) {
        var item = this.itemsWrapper.children[i];
        var id = item.dataset.id;
        item.classList.remove('readonly');
        this.autoCompleteSelectField.classList.remove('hidden');

        if (!enable) {
          item.classList.add('readonly');
          this.autoCompleteSelectField.classList.add('hidden');
          this.openbutton.classList.add('hidden');
        }
      }
    }
  }, {
    key: "_addItems",
    value: function _addItems() {
      var valuesFromAttribute = this.value.split(',');
      var datasetLength = this.dataSet.length;

      for (var valuesIndex = 0; valuesIndex < valuesFromAttribute.length; valuesIndex++) {
        var idFromAttribute = parseInt(valuesFromAttribute[valuesIndex]);

        for (var itemIndex = 0; itemIndex < datasetLength; itemIndex++) {
          var item = this.dataSet[itemIndex];

          if (item.id === idFromAttribute) {
            this._addItem(item.id, item.value);

            break;
          }
        }
      }
    }
  }, {
    key: "_makeMultiSelect",

    /**
     * Makes the fake input field and autocomplete input field.
     * Focus method is triggered when you hover over items in the menu
     * select method is triggered when you click an item in the list.
     */
    value: function _makeMultiSelect() {
      var self = this;
      var element = document.getElementById(this.attributeKey + "-fake");
      $(element).autocomplete({
        source: this.dataSet,
        minLength: 0,
        focus: function focus(event, ui) {
          return false;
        },
        select: function select(event, ui) {
          $(this.autoCompleteSelectField).val("");

          self._addItem(ui.item.id, ui.item.value);

          self._updateRealInput();

          self._autosaveIfNeeded();

          self._enableListeners(true);

          return false;
        }
      });
    }
  }, {
    key: "_addItem",

    /**
     * Adds a span tag containing the value given and the data-id property set to id given to
     * the p tag with class items.
     */
    value: function _addItem(id, value) {
      var self = this;
      if (this._itemsCount() > self.maxItemsToSelect) return;
      var itemElement = document.createElement('p');
      itemElement.classList.add('item');

      if (this.sortable) {
        itemElement.draggable = true;
        value = '<span class="drag-icon"></span><span class="text">' + value + '</span>';
      }

      itemElement.innerHTML = value;
      var removeButton = document.createElement('span');
      removeButton.classList.add('remove');
      itemElement.dataset.id = id;
      itemElement.dataset.sort_order = this._itemsCount();
      removeButton.addEventListener('click', function (itemToRemove) {
        return function () {
          if (!self.listenersEnabled) return;

          self._removeItem(itemToRemove);
        };
      }(id));
      this.itemsWrapper.appendChild(itemElement);
      itemElement.appendChild(removeButton);
    }
  }, {
    key: "_enableOpenButton",

    /**
     * Make sure we can open the autocomplete field dropdown with the open button
     */
    value: function _enableOpenButton() {
      var self = this;
      this.openbutton.addEventListener('click', function (event) {
        $(self.autoCompleteSelectField).autocomplete("search", "");
      });
    }
  }, {
    key: "_itemsCount",
    value: function _itemsCount() {
      return this.itemsWrapper.childNodes.length;
    }
  }, {
    key: "_removeItem",

    /**
     * Iterates over the child elements of the p element with class items and removes the element that has
     * data-id set to the specified id. Then calls _updateRealInput to update the hidden input
     */
    value: function _removeItem(id) {
      var items = this.itemsWrapper.children;

      for (var index = 0; index < items.length; index++) {
        var item = items[index];

        if (parseInt(item.dataset.id) === id) {
          this.itemsWrapper.removeChild(item);
          break;
        }
      }

      this._updateRealInput();

      this._autosaveIfNeeded();
    }
  }, {
    key: "_updateRealInput",

    /**
     * Iterates over the child elements of the p element with class items and concatenates their data-id
     * values into a string. After that it sets the hidden input with the id string
     */
    value: function _updateRealInput() {
      var ids = [];
      var items = this.itemsWrapper.children;
      var length = this.itemsWrapper.children.length;

      for (var i = 0; i < length; i++) {
        var item = items[i];
        ids[ids.length] = item.dataset.id;
      }

      var idString = ids.join(',');
      this.input.setAttribute('value', idString);
      var changeEvent = document.createEvent("Event");
      changeEvent.initEvent("change", false, true); // args: string type, boolean bubbles, boolean cancelable

      this.input.dispatchEvent(changeEvent);
    }
  }, {
    key: "_autosaveIfNeeded",
    value: function _autosaveIfNeeded() {
      if (!this.autoSaveUrl) return;
      var itemIds = this.input.getAttribute('value');
      var self = this;
      $.ajax({
        type: "POST",
        url: this.autoSaveUrl,
        data: {
          'itemIds': itemIds
        },
        success: function success(response, textStatus, xhr) {
          if (xhr.status === 200) {
            console.log('autosave successfull to url: ' + self.autoSaveUrl);
          } else {
            console.error('autosave failure to url (no code 200): ' + self.autoSaveUrl + '');
            console.error(response);
          }
        },
        error: function error(message) {
          console.error('autosave failure to url: ' + self.autoSaveUrl + '');
          console.error(message);
        },
        headers: {
          'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
        },
        dataType: 'json'
      });
    }
  }, {
    key: "_onDragOver",
    value: function _onDragOver(evt) {
      evt.preventDefault();
      evt.dataTransfer.dropEffect = 'move';
      var target = evt.target;

      if (target && target !== this.dragElement && target.nodeName === 'P') {
        // Sorting
        this.itemsWrapper.insertBefore(this.dragElement, target.nextSibling || target);
      }
    }
  }, {
    key: "_onDragEnd",
    value: function _onDragEnd(evt) {
      evt.preventDefault();
      this.dragElement.classList.remove('ghost');
      this.itemsWrapper.removeEventListener('dragover', this.onDragOver, false);
      this.itemsWrapper.removeEventListener('dragend', this.onDragEnd, false);
      console.log("dragEnd");

      this._updateRealInput();
    }
  }], [{
    key: "_buildSelectOptionsFromObject",
    value: function _buildSelectOptionsFromObject(data) {
      var selectOptions = [];
      var itemsCount = data.length;

      for (var index = 0; index < itemsCount; index++) {
        var selectOption = SelectOption.fromObject(data[index]);
        if (selectOption) selectOptions.push(selectOption);
      }

      return selectOptions;
    }
    /**
     * @param {SelectOption[]}selectOptions
     * @private
     */

  }, {
    key: "_buildDataSetFromSelectOptions",
    value: function _buildDataSetFromSelectOptions(selectOptions) {
      var dataset = [];
      var selectoptionsCount = selectOptions.length;

      for (var index = 0; index < selectoptionsCount; index++) {
        /** @var {SelectOption} selectOption **/
        dataset.push({
          id: selectOptions[index].value,
          value: selectOptions[index].htmlContent.replace(/&nbsp;/gi, '.')
        });
      }

      return dataset;
    }
  }]);

  return MultiSelect;
}();
/**
 * Matches it's php twin functionality
 */


var SelectOption =
/*#__PURE__*/
function () {
  function SelectOption() {
    _classCallCheck(this, SelectOption);

    this._value = '';
    this._content = '';
    this._htmlContent = '';
    this.toJSON = this._toJson.bind(this);
  }

  _createClass(SelectOption, [{
    key: "_toJson",
    value: function _toJson() {
      return {
        value: this._value,
        content: this._content,
        htmlContent: this._htmlContent
      };
    }
  }, {
    key: "value",
    get: function get() {
      return this._value;
    },
    set: function set(value) {
      if (typeof value !== 'string') {
        console.error('SelectOption::Value must be a string');
        return;
      }

      this._value = value;
    }
  }, {
    key: "content",
    get: function get() {
      return this._content;
    },
    set: function set(value) {
      if (typeof value !== 'string') {
        console.error('SelectOption::Content must be a string');
        return;
      }

      this._content = value;
    }
  }, {
    key: "htmlContent",
    get: function get() {
      return this._htmlContent;
    },
    set: function set(value) {
      if (typeof value !== 'string') {
        console.error('SelectOption::htmlContent must be a string');
        return;
      }

      this._htmlContent = value;
    }
  }], [{
    key: "fromObject",
    value: function fromObject(data) {
      if (!this._objectRepresentsSelectOption(data)) return;
      var instance = new this();
      instance._value = data.value;
      instance._content = data.content;
      instance._htmlContent = data.htmlContent;
      return instance;
    }
  }, {
    key: "_objectRepresentsSelectOption",
    value: function _objectRepresentsSelectOption(data) {
      if (_typeof(data) !== 'object' || !data.hasOwnProperty('value') || !data.hasOwnProperty('content') || !data.hasOwnProperty('htmlContent')) {
        console.error('SelectOption::_objectRepresentsSelectOption: This does not represent an select option. It must be an object containing properties: value, content and htmlContent', data);
        return false;
      }

      return true;
    }
  }]);

  return SelectOption;
}(); //also @See onOff.blade.php


var OnOff =
/*#__PURE__*/
function () {
  function OnOff(onOffElementWrapper) {
    _classCallCheck(this, OnOff);

    if (onOffElementWrapper === undefined || onOffElementWrapper.tagName !== "DIV") {
      console.error('OnOff:constructor Missing the expected div element that represents the OnOff attribute wrapper.');
      return;
    }

    this._onOffElementWrapper = onOffElementWrapper;

    if (!('key' in onOffElementWrapper.dataset)) {
      console.error('OnOff:constructor Missing the key dataset property on the onOffElementWrapper');
      return;
    }

    var realCheckbox = onOffElementWrapper.querySelector('input[type="checkbox"]');

    if (!realCheckbox) {
      console.error('OnOff:constructor Did not find a required input of type checkbox in the OnOff attribute wrapper.');
      return;
    }

    this._realCheckbox = realCheckbox;
    var visibleCheckbox = onOffElementWrapper.querySelector('.on-off-switch');

    if (!visibleCheckbox) {
      console.error('OnOff:constructor Did not find a required div with class on-off-switch in the OnOff attribute wrapper.');
      return;
    }

    this._visibleCheckbox = visibleCheckbox;

    this._visibleCheckbox.addEventListener('click', this.toggleOnOffSwitch.bind(this));

    this.initialize();
  }

  _createClass(OnOff, [{
    key: "initialize",
    value: function initialize() {
      this.updateOnOffState();
    }
  }, {
    key: "updateOnOffState",
    value: function updateOnOffState() {
      if (this._realCheckbox.value === '1') {
        this._realCheckbox.value = '1';

        this._visibleCheckbox.classList.add('on');

        this._realCheckbox.setAttribute('checked', '');
      } else {
        this._realCheckbox.value = '0';

        this._visibleCheckbox.classList.remove('on');

        this._realCheckbox.removeAttribute('checked');
      }
    }
  }, {
    key: "toggleOnOffSwitch",
    value: function toggleOnOffSwitch() {
      this._realCheckbox.value = this._realCheckbox.value === "0" ? "1" : "0";
      this.updateOnOffState();
      var changeEvent = document.createEvent("Event");
      changeEvent.initEvent("change", false, true); // args: string type, boolean bubbles, boolean cancelable

      this._realCheckbox.dispatchEvent(changeEvent);
    }
  }]);

  return OnOff;
}();

var PasswordController =
/*#__PURE__*/
function () {
  /**
   * Validates password fields.
   * The password fields values wil be imploded with a pipe symbol
   *
   * @param passwordInputSelector The css selector for selecting the password input fields
   * @param wrapperSelector The css selector for the wrapper div / element that wraps the first and second input fields along the validation helper
   * @param passwordDontMatchErrorText string
   * @param saveButtonId The id of a save button that needs to be disabled when the passwords are not valid
   * @param minPasswordLength int
   * @param wrapperHasTitleAttributeAndErrorClass Must be set to true if the wrapper (selected with the wrapper selector) has the ability to have an error class and title attribute for displaying an error
   */
  function PasswordController(passwordInputSelector, wrapperSelector, passwordDontMatchErrorText, saveButtonId) {
    var minPasswordLength = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : 6;
    var wrapperHasTitleAttributeAndErrorClass = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : false;

    _classCallCheck(this, PasswordController);

    this.wrapperHasTitleAttributeAndErrorClass = wrapperHasTitleAttributeAndErrorClass;
    this.wrapper = document.querySelector(wrapperSelector);
    this.saveButton = document.querySelector("#" + saveButtonId);
    this.firstPasswordInput = this.wrapper.querySelector('input[name=' + passwordInputSelector + '-1]');
    this.secondPasswordInput = this.wrapper.querySelector('input[name=' + passwordInputSelector + '-2]');
    this.realPasswordInput = this.wrapper.querySelector('input[name=' + passwordInputSelector + ']');
    this.validationMessageWrapperSelector = '.validationHelper';
    this.validationMessageWrapper = this.wrapper.querySelector(this.validationMessageWrapperSelector);
    this.minPasswordLength = minPasswordLength; //Used for validation

    this.letter = this.validationMessageWrapper.querySelector(".letter");
    this.capital = this.validationMessageWrapper.querySelector(".capital");
    this.number = this.validationMessageWrapper.querySelector(".number");
    this.length = this.validationMessageWrapper.querySelector(".length");
    this.match = this.validationMessageWrapper.querySelector(".match"); // this.noPipe = document.querySelector(this.validationMessageWrapperSelector + " .noPipe");

    this.activateListeners(true);
  }
  /**
   * Activates listening for keyup events on the password fields to that the passwordChanged method is triggered
   *
   * @param state
   */


  _createClass(PasswordController, [{
    key: "activateListeners",
    value: function activateListeners(state) {
      var self = this;
      var validationHelper = this.validationMessageWrapper;

      if (state) {
        this.firstPasswordInput.addEventListener('keyup', self.debounce(function () {
          self.passwordChanged();
        }, 100));
        this.secondPasswordInput.addEventListener('keyup', self.debounce(function () {
          self.passwordChanged();
        }, 100));
        this.firstPasswordInput.addEventListener('focus', function () {
          if (!validationHelper.classList.contains('active')) validationHelper.classList.add('active');
        });
        this.secondPasswordInput.addEventListener('focus', function () {
          if (!validationHelper.classList.contains('active')) validationHelper.classList.add('active');
        });
        this.firstPasswordInput.addEventListener('blur', function () {
          if (validationHelper.classList.contains('active')) validationHelper.classList.remove('active');
        });
        this.secondPasswordInput.addEventListener('blur', function () {
          if (validationHelper.classList.contains('active')) validationHelper.classList.remove('active');
        });
      } else {
        this.firstPasswordInput.removeEventListener('keyup', self.debounce);
        this.secondPasswordInput.removeEventListener('keyup', self.debounce);
        this.firstPasswordInput.removeEventListener('focus');
        this.secondPasswordInput.removeEventListener('focus');
        this.firstPasswordInput.removeEventListener('blur');
        this.secondPasswordInput.removeEventListener('blur');
      }
    }
    /**
     * Triggered when one of the password fields are changed and if the listeners for those fields are active
     */

  }, {
    key: "passwordChanged",
    value: function passwordChanged() {
      console.log('password changed');
      var value1 = this.firstPasswordInput.value;
      var value2 = this.secondPasswordInput.value;
      var valid = this.validate(value1, value2);
      console.log(valid);
      if (valid) this.realPasswordInput.value = value2;else this.realPasswordInput.value = '';
      this.enableValidMessage(valid);
      this.enableSaveButton(valid);
      this.removeWrapperError();
    }
  }, {
    key: "enableValidMessage",
    value: function enableValidMessage(enable) {
      var validationHelper = this.validationMessageWrapper;

      if (enable) {
        if (!validationHelper.classList.contains('valid')) validationHelper.classList.add('valid');
      } else {
        if (validationHelper.classList.contains('valid')) validationHelper.classList.remove('valid');
      }
    }
  }, {
    key: "enableSaveButton",
    value: function enableSaveButton(enable) {
      console.log(enable);

      if (enable) {
        if (this.saveButton.classList.contains('disabled')) this.saveButton.classList.remove('disabled');
      } else {
        if (!this.saveButton.classList.contains('disabled')) this.saveButton.classList.add('disabled');
      }
    }
    /**
     * Removes the error that may be set on the wrapper
     */

  }, {
    key: "removeWrapperError",
    value: function removeWrapperError() {
      if (!this.wrapperHasTitleAttributeAndErrorClass) return;
      if (this.wrapper.hasAttribute('title')) this.wrapper.setAttribute('title', '');
      if (this.wrapper.classList.contains('error')) this.wrapper.classList.remove('error');
    }
    /**
     * Validate two values and return true or false if respectively valid or not
     *
     * @param value1
     * @param value2
     * @returns {boolean}
     */

  }, {
    key: "validate",
    value: function validate(value1, value2) {
      var valid = true; // Validate lowercase letters

      var lowerCaseLetters = /[a-z]/g;

      if (value1.match(lowerCaseLetters)) {
        this.letter.classList.remove("invalid");
        this.letter.classList.add("valid");
      } else {
        this.letter.classList.remove("valid");
        this.letter.classList.add("invalid");
        valid = false;
      } // Validate capital letters


      var upperCaseLetters = /[A-Z]/g;

      if (value1.match(upperCaseLetters)) {
        this.capital.classList.remove("invalid");
        this.capital.classList.add("valid");
      } else {
        this.capital.classList.remove("valid");
        this.capital.classList.add("invalid");
        valid = false;
      } // Validate numbers


      var numbers = /[0-9]/g;

      if (value1.match(numbers)) {
        this.number.classList.remove("invalid");
        this.number.classList.add("valid");
      } else {
        this.number.classList.remove("valid");
        this.number.classList.add("invalid");
        valid = false;
      } // Validate numbers
      // let pipe = /\|/g;
      // if(value1.match(pipe)) {
      //     this.noPipe.classList.remove("valid");
      //     this.noPipe.classList.add("invalid");
      //     valid = false;
      // } else {
      //     this.noPipe.classList.remove("invalid");
      //     this.noPipe.classList.add("valid");
      // }
      // Validate length


      if (value1.length >= this.minPasswordLength) {
        this.length.classList.remove("invalid");
        this.length.classList.add("valid");
      } else {
        this.length.classList.remove("valid");
        this.length.classList.add("invalid");
        valid = false;
      } // Validate password match


      if (value1 === value2 && (value1 !== '' || value2 !== '')) {
        this.match.classList.remove("invalid");
        this.match.classList.add("valid");
      } else {
        this.match.classList.remove("valid");
        this.match.classList.add("invalid");
        valid = false;
      }

      return valid;
    }
    /**
     * Triggers the function after the wait time (milliseconds) has expired and only
     * if the function is not triggered again before the wait time expired.
     *
     * @param func
     * @param wait
     * @param immediate
     * @returns {Function}
     */

  }, {
    key: "debounce",
    value: function debounce(func, wait, immediate) {
      var timeout;
      return function () {
        var context = this,
            args = arguments;

        var later = function later() {
          timeout = null;
          if (!immediate) func.apply(context, args);
        };

        var callNow = immediate && !timeout;
        clearTimeout(timeout);
        timeout = setTimeout(later, wait);
        if (callNow) func.apply(context, args);
      };
    }
  }]);

  return PasswordController;
}();

var Select =
/*#__PURE__*/
function () {
  function Select(selectElementWrapper) {
    _classCallCheck(this, Select);

    if (selectElementWrapper === undefined || selectElementWrapper.tagName !== "DIV") {
      console.error('SelectElement:constructor Missing the expected div element that represents the selectElementWrapper.');
      return;
    }

    this._selectElementWrapper = selectElementWrapper;

    if (!('key' in selectElementWrapper.dataset)) {
      console.error('SelectElement:constructor Missing the key dataset property on the selectElementWrapper');
      return;
    }

    var key = selectElementWrapper.dataset.key;
    var realInput = selectElementWrapper.querySelector('select[name="' + key + '"]');

    if (!realInput) {
      console.error('SelectElement:constructor Did not find a required hidden select with name "' + key + '" in the VideoElementWrapper.');
      return;
    }

    this._realInput = realInput;
    this.initialize();
  }

  _createClass(Select, [{
    key: "initialize",
    value: function initialize() {
      var selectHandler = this;

      var selectMenu = this._selectElementWrapper.querySelector('.select-menu');

      $(selectMenu).selectmenu({
        width: '100%',
        create: function create(event, ui) {
          buttonId = this.id + '-button';
          menuId = this.id + '-menu';
        },
        open: function open(event, ui) {
          var button = document.getElementById(this.id + '-button');
          var menu = document.getElementById(this.id + '-menu');
          menu.style.width = button.offsetWidth + 'px';
          menu.parentNode.style.width = button.offsetWidth + 'px';
          button.classList.add('dropdown-open');
        },
        change: function change(event, ui) {
          var button = document.getElementById(this.id + '-button');
          var buttonText = button.querySelector('.ui-selectmenu-text').innerHTML;
          button.querySelector('.ui-selectmenu-text').innerHTML = buttonText.replace(/&nbsp;/gi, '');
          var changeEvent = document.createEvent("Event");
          changeEvent.initEvent("change", false, true); // args: string type, boolean bubbles, boolean cancelable

          selectHandler._realInput.dispatchEvent(changeEvent);
        },
        close: function close(event, ui) {
          var button = document.getElementById(this.id + '-button');
          button.classList.remove('dropdown-open');
        }
      });
    }
  }]);

  return Select;
}();
/**
 * Helps managing classes on a button.
 * You can request to add a class and for each time you request to add the class, it will increment a counter.
 * You can also request to remove a class and for each time you do that, it will decrement a counter.
 * When that counter hits 0 it will remove the class.
 * It will keep counter for each unique class you request to add.
 */


var styleClassController =
/*#__PURE__*/
function () {
  /**
   * Constructor
   *
   * @param {HTMLElement} htmlElement
   */
  function styleClassController(htmlElement) {
    _classCallCheck(this, styleClassController);

    this.constructedSuccessFully = false;

    if (!htmlElement instanceof HTMLElement) {
      console.error('styleClassController: The given htmlElement must be, but was not a HTMLElement');
      return;
    }

    this.htmlElement = htmlElement;
    this.classCounts = {};
    this.constructedSuccessFully = true;
  }
  /**
   * @param {string} styleClass
   */


  _createClass(styleClassController, [{
    key: "requestAddClass",
    value: function requestAddClass(styleClass) {
      if (!this.htmlElement.classList.contains(styleClass)) this.htmlElement.classList.add(styleClass);
      if (!this.classCounts.hasOwnProperty(styleClass)) this.classCounts[styleClass] = 0;
      this.classCounts[styleClass]++;
      return true;
    }
  }, {
    key: "requestRemoveClass",
    value: function requestRemoveClass(styleClass) {
      if (!this.htmlElement.classList.contains(styleClass) || !this.classCounts.hasOwnProperty(styleClass)) return false;
      this.classCounts[styleClass]--;

      if (this.classCounts[styleClass] === 0) {
        delete this.classCounts[styleClass];
        this.htmlElement.classList.remove(styleClass);
      }
    }
  }]);

  return styleClassController;
}(); //also @See video.blade.php


var Video =
/*#__PURE__*/
function () {
  function Video(videoElementWrapper) {
    _classCallCheck(this, Video);

    if (videoElementWrapper === undefined || videoElementWrapper.tagName !== "DIV") {
      console.error('VideoElement:constructor Missing the expected div element that represents the VideoElementWrapper.');
      return;
    }

    this._videoElementWrapper = videoElementWrapper;

    if (!('key' in videoElementWrapper.dataset)) {
      console.error('VideoElement:constructor Missing the key dataset property on the videoElementWrapper');
      return;
    }

    var key = videoElementWrapper.dataset.key;
    var realInput = videoElementWrapper.querySelector('input[name="' + key + '"]');

    if (!realInput) {
      console.error('VideoElement:constructor Did not find a required hidden input with name "' + key + '" in the VideoElementWrapper.');
      return;
    }

    this._realInput = realInput;
    var realCheckbox = videoElementWrapper.querySelector('input[name="' + key + '_autoplay"]');

    if (!realCheckbox) {
      console.error('VideoElement:constructor Did not find a required checkbox input of type checkbox with name "' + key + '_autoplay" in the VideoElementWrapper.');
      return;
    }

    this._realCheckbox = realCheckbox;
    var videoIdInput = videoElementWrapper.querySelector('input[name="' + key + '_video_id"]');

    if (!videoIdInput) {
      console.error('VideoElement:constructor Did not find a required input of type text with name "' + key + '_video_id" in the VideoElementWrapper.');
      return;
    }

    this._videoIdInput = videoIdInput;
    this._videoWatchPath = 'https://www.youtube.com/watch?v={videoInputValue}';
    this._videoImgPath = 'https://img.youtube.com/vi/{videoInputValue}/0.jpg';
    this.initialize();
    this.enableEventListeners();
  }

  _createClass(Video, [{
    key: "enableEventListeners",
    value: function enableEventListeners() {
      this._videoIdInput.addEventListener('keyup', this.updateRealInput.bind(this));

      this._realCheckbox.addEventListener('change', this.updateRealInput.bind(this));
    }
  }, {
    key: "updateRealInput",
    value: function updateRealInput(event) {
      var autoPlayCheckboxValue = this._realCheckbox.checked === true ? '1' : '0';
      var videoInputValue = this._videoIdInput.value;
      this._realInput.value = autoPlayCheckboxValue + ',' + videoInputValue;
      this.updateThumbnail(videoInputValue);
    }
    /**
     * Generate a thumbnail with link to the video
     *
     * @param videoInputValue
     */

  }, {
    key: "updateThumbnail",
    value: function updateThumbnail(videoInputValue) {
      // Get the attribute that will change
      var videoLink = this._videoElementWrapper.querySelector('.js-video-link');

      var videoThumb = this._videoElementWrapper.querySelector('.js-video-thumb'); // Remove the attribute if the input is empty


      if (!isset(videoInputValue) || videoInputValue === '') {
        videoThumb.removeAttribute('src');
        videoLink.removeAttribute('href');
        return;
      } // Get the videoThumbPath


      var videoThumbPath = this._getYoutubePath(videoInputValue); // TODO: Create something like this to preven the ugly video not found image of youtube
      // this._ifVideoThumbnailExists(videoThumbPath)
      // Set the attribute to preview the thumbnail with a link to the video


      videoThumb.setAttribute('src', videoThumbPath);
      videoLink.setAttribute('href', this._getYoutubePath(videoInputValue, 'watch'));
    }
    /**
     * Grab the desired path for a youtube link or thumbnail
     *
     * @param {string} videoInputValue
     * @param {string} type
     * @returns {string}
     * @private
     */

  }, {
    key: "_getYoutubePath",
    value: function _getYoutubePath(videoInputValue) {
      var type = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'thumbnail';
      var path = ''; // Grab the desired path and replace the placeholder with the input

      switch (type) {
        case 'thumbnail':
          path = this._videoImgPath;
          return path.replace("{videoInputValue}", videoInputValue);

        case 'watch':
          path = this._videoWatchPath;
          return path.replace("{videoInputValue}", videoInputValue);

        default:
          console.alert('VideoElement:_getPath Type rule not defined');
          return '';
      }
    } // _ifVideoThumbnailExists(videoThumbPath){
    //
    //     Ajax.get(videoThumbPath, function (response) {
    //        console.log(response);
    //     });
    //
    // }

  }, {
    key: "initialize",
    value: function initialize() {
      //Get autoplay value and video id from the hidden input
      if (this._realInput.value === "") return;

      var splittedValue = this._realInput.value.split(',');

      if (splittedValue.length < 2) console.error('VideoElement:initialize Could not initialize a video element because it received an invalid value: ', this._realInput.value);
      var autoplayCheckboxValue = splittedValue[0];
      var videoIdInputValue = splittedValue[1]; //Set the video id input with the videoIdInputValue

      this._videoIdInput.value = videoIdInputValue;
      this.updateThumbnail(videoIdInputValue); //Set the checkbox using the autoplayCheckbox value

      this._realCheckbox.checked = autoplayCheckboxValue === '1';
    }
  }]);

  return Video;
}();