define(['jquery', 'lodash', 'core/format', 'ui/component', 'util/wrapLongWords', 'util/encode', 'handlebars', 'lib/handlebars/helpers'], function ($$1, _, format, component, wrapLongWords, encode, Handlebars, Helpers0) { 'use strict';

    $$1 = $$1 && Object.prototype.hasOwnProperty.call($$1, 'default') ? $$1['default'] : $$1;
    _ = _ && Object.prototype.hasOwnProperty.call(_, 'default') ? _['default'] : _;
    format = format && Object.prototype.hasOwnProperty.call(format, 'default') ? format['default'] : format;
    component = component && Object.prototype.hasOwnProperty.call(component, 'default') ? component['default'] : component;
    wrapLongWords = wrapLongWords && Object.prototype.hasOwnProperty.call(wrapLongWords, 'default') ? wrapLongWords['default'] : wrapLongWords;
    encode = encode && Object.prototype.hasOwnProperty.call(encode, 'default') ? encode['default'] : encode;
    Handlebars = Handlebars && Object.prototype.hasOwnProperty.call(Handlebars, 'default') ? Handlebars['default'] : Handlebars;
    Helpers0 = Helpers0 && Object.prototype.hasOwnProperty.call(Helpers0, 'default') ? Helpers0['default'] : Helpers0;

    if (!Helpers0.__initialized) {
      Helpers0(Handlebars);
      Helpers0.__initialized = true;
    }
    var Template = Handlebars.template(function (Handlebars,depth0,helpers,partials,data) {
      this.compilerInfo = [4,'>= 1.0.0'];
    helpers = this.merge(helpers, Handlebars.helpers); data = data || {};
      var buffer = "", stack1, helper, options, functionType="function", escapeExpression=this.escapeExpression, self=this, helperMissing=helpers.helperMissing;

    function program1(depth0,data) {
      
      
      return "popup";
      }

      buffer += "<div id=\"";
      if (helper = helpers.id) { stack1 = helper.call(depth0, {hash:{},data:data}); }
      else { helper = (depth0 && depth0.id); stack1 = typeof helper === functionType ? helper.call(depth0, {hash:{},data:data}) : helper; }
      buffer += escapeExpression(stack1)
        + "\" class=\"feedback feedback-";
      if (helper = helpers.level) { stack1 = helper.call(depth0, {hash:{},data:data}); }
      else { helper = (depth0 && depth0.level); stack1 = typeof helper === functionType ? helper.call(depth0, {hash:{},data:data}) : helper; }
      buffer += escapeExpression(stack1)
        + " ";
      stack1 = helpers['if'].call(depth0, (depth0 && depth0.popup), {hash:{},inverse:self.noop,fn:self.program(1, program1, data),data:data});
      if(stack1 || stack1 === 0) { buffer += stack1; }
      buffer += "\" role=\"alert\" >\n    <span class=\"icon-";
      if (helper = helpers.level) { stack1 = helper.call(depth0, {hash:{},data:data}); }
      else { helper = (depth0 && depth0.level); stack1 = typeof helper === functionType ? helper.call(depth0, {hash:{},data:data}) : helper; }
      buffer += escapeExpression(stack1)
        + "\"></span>\n    <div>\n        ";
      stack1 = (helper = helpers.dompurify || (depth0 && depth0.dompurify),options={hash:{},data:data},helper ? helper.call(depth0, (depth0 && depth0.msg), options) : helperMissing.call(depth0, "dompurify", (depth0 && depth0.msg), options));
      if(stack1 || stack1 === 0) { buffer += stack1; }
      buffer += "\n    </div>\n    <span title=\""
        + escapeExpression((helper = helpers.__ || (depth0 && depth0.__),options={hash:{},data:data},helper ? helper.call(depth0, "Close message", options) : helperMissing.call(depth0, "__", "Close message", options)))
        + "\" class=\"icon-close\" data-close=\":parent .feedback\" role=\"button\" tabindex=\"0\"></span>\n</div>\n";
      return buffer;
      });
    function tpl(data, options, asString) {
      var html = Template(data, options);
      return (asString || true) ? html : $(html);
    }

    /*
     * This program is free software; you can redistribute it and/or
     * modify it under the terms of the GNU General Public License
     * as published by the Free Software Foundation; under version 2
     * of the License (non-upgradable).
     *
     * This program is distributed in the hope that it will be useful,
     * but WITHOUT ANY WARRANTY; without even the implied warranty of
     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     * GNU General Public License for more details.
     *
     * You should have received a copy of the GNU General Public License
     * along with this program; if not, write to the Free Software
     * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
     *
     * Copyright (c) 2015-2020 (original work) Open Assessment Technologies SA ;
     *
     */

    //The default container of the feedbacks
    var defaultContainerSelector = '#feedback-box';

    //keep a reference to ALL alive feedback
    var currents = [];

    //available levels
    var levels = ['info', 'success', 'warning', 'danger', 'error'];
    var defaultOptions = {
      timeout: {
        info: 2000,
        success: 2000,
        warning: 4000,
        danger: 4000,
        error: 8000
      },
      // Note: value depends on font, font-weight and such.
      // 40 is pretty good in the current setup but will
      // never be exact with a non-proportional font.
      wrapLongWordsAfter: 40,
      //by default HTML content is encoded
      encodeHtml: true,
      //change the display (absolute or in the flow)
      popup: true
    };

    /**
     * Creates a feedback object.
     *
     * @exports ui/feedback
     * @param {jQUeryElement} [$container] - only to specify another container
     * @param {Object} [config] - change the config
     * @param {Object|Number} [config.timeout] - either one for every level or per level timeout in ms
     * @param {Number} [config.wrapLongWordsAfter] - add a space in the middle of very long word to enable wrap lines
     * @param {Boolean} [config.encodeHtml] - weither the message is html encoded
     * @param {Boolean} [config.popup] - displays the message as a popup or in the flow
     * @returns {feedback} the feedback object
     * @throws {TypeError} without a container
     */
    var feedbackFactory = function feedbackFactory($container, config) {
      var feedback;
      const codeEnter = 13;
      const codeSpace = 32;
      if (!$container || !$container.length) {
        $container = $$1(defaultContainerSelector);
      }
      if (!$container.length) {
        throw new TypeError('The feedback needs to belong to an existing container');
      }

      /**
       * @typedef {Object} feedback - the feedback component
       */
      feedback = component({
        /**
         * Creates a message, not displayed.
         * @param {String} [level = 'info'] - the message level
         * @param {String} msg - the message to display
         * @param {Array} [params] - parameters for the message format (%s,%d,%j)
         * @param {Object} [options] - specify the config
         * @returns {feedback} chains
         */
        message: function message(level, msg, params, options) {
          var displayedMessage = msg;
          if (!level || !_.includes(levels, level)) {
            level = 'info';
          }

          //parameterized messages
          if (_.isPlainObject(params)) {
            options = params;
            params = [];
          }
          this.config = _.defaults(options || {}, this.config);
          this.config.level = level;

          // encode plain text string to html
          if (this.config.encodeHtml) {
            displayedMessage = encode.html(displayedMessage);
          }

          // wrap long words
          if (this.config.wrapLongWordsAfter) {
            displayedMessage = wrapLongWords(displayedMessage, this.config.wrapLongWordsAfter);
          }

          //apply strf like format parameters
          if (_.isArray(params) && params.length) {
            displayedMessage = format.apply(format, [displayedMessage].concat(params));
          }
          this.config.msg = displayedMessage;
          return this;
        },
        /**
         * Opens an info message
         * @param {String} msg - the message to display
         * @param {Array} [params] - parameters for the message format (%s,%d,%j)
         * @param {Object} [options] - specify the config
         * @returns {feedback} chains
         */
        info: function info(msg, params, options) {
          return this.message('info', msg, params, options).open();
        },
        /**
         * Opens an success message
         * @param {String} msg - the message to display
         * @param {Array} [params] - parameters for the message format (%s,%d,%j)
         * @param {Object} [options] - specify the config
         * @returns {feedback} chains
         */
        success: function success(msg, params, options) {
          return this.message('success', msg, params, options).open();
        },
        /**
         * Opens an warning message
         * @param {String} msg - the message to display
         * @param {Array} [params] - parameters for the message format (%s,%d,%j)
         * @param {Object} [options] - specify the config
         * @returns {feedback} chains
         */
        warning: function warning(msg, params, options) {
          return this.message('warning', msg, params, options).open();
        },
        /**
         * Opens an danger message
         * @param {String} msg - the message to display
         * @param {Array} [params] - parameters for the message format (%s,%d,%j)
         * @param {Object} [options] - specify the config
         * @returns {feedback} chains
         */
        danger: function danger(msg, params, options) {
          return this.message('danger', msg, params, options).open();
        },
        /**
         * Opens an error message
         * @param {String} msg - the message to display
         * @param {Array} [params] - parameters for the message format (%s,%d,%j)
         * @param {Object} [options] - specify the config
         * @returns {feedback} chains
         */
        error: function error(msg, params, options) {
          return this.message('error', msg, params, options).open();
        },
        /**
         * Opens the message (and close previous one)
         * ! Method kept for backward compat with previous version !
         * @returns {feedback} chains
         */
        open: function open() {
          //close others
          _(currents).reject(this).forEach(function (fb) {
            fb.close();
          });

          //and display
          return this.display();
        },
        /**
         * Closes the message
         * ! Method kept for backward compat with previous version !
         * @returns {feedback} chains
         */
        close: function close() {
          if (this.is('rendered')) {
            this.destroy();
          }
        },
        /**
         * Displays the message (does the render)
         * ! Method kept for backward compat with previous version !
         * @returns {feedback} chains
         */
        display: function display() {
          if (this.is('rendered')) {
            this.show();
          } else {
            this.render($container);
          }
          return this;
        }
      }, defaultOptions);
      return feedback.setTemplate(tpl).on('init', function () {
        this.config.id = 'feedback-' + (currents.length + 1);
        currents.push(this);

        //for backward compat
        $container.trigger('create.feedback');
      }).on('render', function () {
        var self = this;
        var $component = this.getElement();
        var $closer = $$1('.icon-close', $component);
        var timeout = _.isPlainObject(this.config.timeout) ? this.config.timeout[this.config.level] : this.config.timeout;
        $closer.off('click').on('click', e => {
          e.preventDefault();
          this.destroy();
        }).off('keyup').on('keyup', e => {
          if ([codeEnter, codeSpace].indexOf(e.which) !== -1) {
            this.destroy();
          }
        });
        if (_.isNumber(timeout) && timeout > 0) {
          _.delay(function () {
            self.close();
          }, timeout);
        }

        //for backward compat
        $container.trigger('display.feedback');
      }).on('destroy', function () {
        //for backward compat
        $container.trigger('close.feedback');
        _.pull(currents, this);
      }).init(config);
    };

    return feedbackFactory;

});
