    /** @module Messages */

    /**
     * a generic message that can be displayed in the {@link Messages} box
     */
    class Message {
        /**
         * @param {string} text               text of the message
         * @param {Function} [onHide] a function that will be called when the `hide()` method is called
         */
        constructor(text, onHide) {
            this.$el = $('<div>')
                .addClass('message')
                .text(text);

            /**
             * callback function that will be called when the `hide()` method is called
             * @type {Function}
             */
            this.onHide = onHide;
        }

        /**
         * hide the message (and call the onHide callback if there is any)
         */
        hide() {
            this.$el.remove();

            if (this.onHide) {
                this.onHide();
            }
        }
    }

    /**
     * a loading message that can be displayed in the {@link Messages} box
     * @extends Message
     */
    class LoadingMessage extends Message {
        constructor(text, onHide = undefined) {
            super(text, onHide);

            this.$el.addClass('loading');
        }
    }

    /**
     * a message that has a close button and that can be displayed in the {@link Messages} box
     * @extends Message
     */
    class ClosableMessage extends Message {
        constructor(text, onHide = undefined) {
            super(text, onHide);

            this.$el.append(
                $('<span>')
                    .addClass('close')
                    .click(() => {
                        this.hide();
                    })
            );
        }
    }

    /**
     * an error message that can be displayed in the {@link Messages} box
     * @extends ClosableMessage
     */
    class ErrorMessage extends ClosableMessage {
        constructor(text, onHide = undefined) {
            super(text, onHide);

            this.$el.addClass('error');
        }
    }

    /**
     * a warning message that can be displayed in the {@link Messages} box
     * @extends ClosableMessage
     */
    class WarningMessage extends ClosableMessage {
        constructor(text, onHide = undefined) {
            super(text, onHide);

            this.$el.addClass('warning');
        }
    }

    /**
     * display messages to the user in a nice UI
     */
    export default class Messages {
        constructor() {
            /**
             * jQuery element that represents the message interface. This element contains all the currently displayed messages.
             * @type {jQuery.element}
             */
            this.$el = $('<div>').addClass('messages');

            /**
             * number of currently displayed messages, has a specified setter and getter
             * @type {number}
             */
            this.count = 0;

            // place the progress info element
            $('body').append(this.$el);
        }

        /**
         * get the number of currently displayed messages
         * @return {number} [description]
         */
        get count() {
            return this.messageCount;
        }

        /**
         * Set the number of currently displayed messages. Should be called only through functions that add messages.
         *
         * If the message count is >= 1, the jQuery element for the UI is displayed, if the message count is <1, the UI is hidden.
         * @param  {number} value [description]
         * @return {number}       [description]
         */
        set count(value) {
            this.messageCount = value;

            if (this.messageCount < 1) {
                this.hide();
            } else {
                this.display();
            }
        }

        /**
         * hide the message box by adding a `hidden` class to the element
         */
        hide() {
            this.$el.addClass('hidden');
        }

        /**
         * display the message box by removing the `hidden` class to the element
         */
        display() {
            this.$el.removeClass('hidden');
        }

        /**
         * add a new message to the message box
         * @param  {string} text             text of the message
         * @param  {Message} [constr=Message] constructor of the message, must be a derivate of the {@link Message} class
         * @return {Message}                  the newly constructed message (instance made by the specified constructor)
         */
        newMessage(text, constr = Message) {
            // Create the message by calling the constructor,
            // provide it with text and a callback function that will be called when hiding the message.
            // This callback subtracts 1 from the
            let message = new constr(text, () => {
                this.count--;
            });

            this.$el.append(message.$el);
            this.count++;

            return message;
        }

        /**
         * create a new loading message and add it to the message box
         * @param  {string} text text of the message
         * @return {LoadingMessage} the newly constructed {@link LoadingMessage}
         */
        newLoadingMessage(text) {
            return this.newMessage(text, LoadingMessage);
        }

        /**
         * create a new error message and add it to the message box
         * @param  {string} text text of the message
         * @return {ErrorMessage} the newly constructed {@link ErrorMessage}
         */
        newErrorMessage(text) {
            return this.newMessage(text, ErrorMessage);
        }

        /**
         * create a new warning message and add it to the message box
         * @param  {string} text text of the message
         * @return {WarningMessage} the newly constructed {@link WarningMessage}
         */
        newWarningMessage(text) {
            return this.newMessage(text, WarningMessage);
        }
    }