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/Eurotools/euro-tools.nl/wwwroot/js/kms/attributes.js
var _createClass = 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); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();

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

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

/**
 * Manages file inputs in a wrapper so that files
 *
 * @param {HTMLElement} wrapper
 * @param {HTML5Uploader} HTML5Uploader
 */
var DocumentManager = 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\\KommaApp\\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);
        }

        /**
         * 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 _document = JSON.parse(documentElement.dataset.json);

                if (_document.state === DOCUMENT_STATE_DELETED) continue;

                _document.sort_order = currentSortNumber;
                console.log('Document', _document);
                if (_document.state !== DOCUMENT_STATE_NEW) _document.state = DOCUMENT_STATE_MODIFIED;

                documentElement.dataset.json = JSON.stringify(_document);
                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 = 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 = 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) {
                _iterator.return();
            }
        } finally {
            if (_didIteratorError) {
                throw _iteratorError;
            }
        }
    }

    return dt.files;
};

var FileDragAndDropHandler = 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 _i = 0; _i < dragEvent.dataTransfer.files.length; _i++) {
                    var _file = dragEvent.dataTransfer.files[_i];
                    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;
}();

/**
 * HTML 5 file uploads that report progress while uploading
 */


var HTML5Uploader = 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;
}();

var ImageController = 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;
}();

//also @See onOff.blade.php

var OnOff = function () {
    function OnOff() {
        _classCallCheck(this, OnOff);
    }

    _createClass(OnOff, [{
        key: "toggleOnOff",
        value: function toggleOnOff($id) {
            var onOffBox = document.getElementById($id);
            var onOffSwitch = document.getElementById($id + '-switch');

            if (onOffBox.checked) {
                onOffBox.value = '1';
                onOffSwitch.classList.add('on');
            } else {
                onOffBox.value = '0';
                onOffSwitch.classList.remove('on');
            }
        }
    }, {
        key: "toggleOnOffSwitch",
        value: function toggleOnOffSwitch($id) {
            var onOffBox = document.getElementById($id);
            onOffBox.checked = onOffBox.checked ? onOffBox.checked = false : onOffBox.checked = true;
            this.toggleOnOff($id);
        }
    }]);

    return OnOff;
}();

var PasswordController = 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);
        console.log(this.saveButton);

        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 = void 0;

            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;
}();
//Also See select.blade.php

$(function () {
    $(".select-menu").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, '');
        },
        close: function close(event, ui) {
            var button = document.getElementById(this.id + '-button');
            button.classList.remove('dropdown-open');
        }
    });
});
/**
 * 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 = 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;
}();