define(['lodash', 'i18n', 'ui/component', 'handlebars', 'lib/handlebars/helpers', 'css!ui/listbox/css/listbox.css'], function (_, __, component, Handlebars, Helpers0, listbox_css) { 'use strict';

    _ = _ && Object.prototype.hasOwnProperty.call(_, 'default') ? _['default'] : _;
    __ = __ && Object.prototype.hasOwnProperty.call(__, 'default') ? __['default'] : __;
    component = component && Object.prototype.hasOwnProperty.call(component, 'default') ? component['default'] : component;
    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, self=this, functionType="function", escapeExpression=this.escapeExpression;

    function program1(depth0,data) {
      
      
      return "class=\"hidden\"";
      }

    function program3(depth0,data) {
      
      
      return " hidden";
      }

      buffer += "<div class=\"listbox\">\n    <h1 ";
      stack1 = helpers.unless.call(depth0, (depth0 && depth0.title), {hash:{},inverse:self.noop,fn:self.program(1, program1, data),data:data});
      if(stack1 || stack1 === 0) { buffer += stack1; }
      buffer += ">";
      if (helper = helpers.title) { stack1 = helper.call(depth0, {hash:{},data:data}); }
      else { helper = (depth0 && depth0.title); stack1 = typeof helper === functionType ? helper.call(depth0, {hash:{},data:data}) : helper; }
      buffer += escapeExpression(stack1)
        + "</h1>\n    <h2>\n        <span class=\"empty-list";
      stack1 = helpers.unless.call(depth0, (depth0 && depth0.textEmpty), {hash:{},inverse:self.noop,fn:self.program(3, program3, data),data:data});
      if(stack1 || stack1 === 0) { buffer += stack1; }
      buffer += "\">";
      if (helper = helpers.textEmpty) { stack1 = helper.call(depth0, {hash:{},data:data}); }
      else { helper = (depth0 && depth0.textEmpty); stack1 = typeof helper === functionType ? helper.call(depth0, {hash:{},data:data}) : helper; }
      buffer += escapeExpression(stack1)
        + "</span>\n        <span class=\"available-list";
      stack1 = helpers.unless.call(depth0, (depth0 && depth0.textNumber), {hash:{},inverse:self.noop,fn:self.program(3, program3, data),data:data});
      if(stack1 || stack1 === 0) { buffer += stack1; }
      buffer += "\"><span class=\"label\">";
      if (helper = helpers.textNumber) { stack1 = helper.call(depth0, {hash:{},data:data}); }
      else { helper = (depth0 && depth0.textNumber); stack1 = typeof helper === functionType ? helper.call(depth0, {hash:{},data:data}) : helper; }
      buffer += escapeExpression(stack1)
        + "</span>: <span class=\"count\"></span></span>\n        <span class=\"loading";
      stack1 = helpers.unless.call(depth0, (depth0 && depth0.textLoading), {hash:{},inverse:self.noop,fn:self.program(3, program3, data),data:data});
      if(stack1 || stack1 === 0) { buffer += stack1; }
      buffer += "\"><span>";
      if (helper = helpers.textLoading) { stack1 = helper.call(depth0, {hash:{},data:data}); }
      else { helper = (depth0 && depth0.textLoading); stack1 = typeof helper === functionType ? helper.call(depth0, {hash:{},data:data}) : helper; }
      buffer += escapeExpression(stack1)
        + "</span>...</span>\n    </h2>\n    <div class=\"list\"></div>\n</div>\n";
      return buffer;
      });
    function mainTpl(data, options, asString) {
      var html = Template(data, options);
      return (asString || true) ? html : $(html);
    }

    if (!Helpers0.__initialized) {
      Helpers0(Handlebars);
      Helpers0.__initialized = true;
    }
    var Template$1 = 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, functionType="function", escapeExpression=this.escapeExpression, self=this;

    function program1(depth0,data,depth1) {
      
      var buffer = "", stack1, helper;
      buffer += "\n    <li class=\"entry flex-col-";
      stack1 = helpers['if'].call(depth0, (depth0 && depth0.width), {hash:{},inverse:self.programWithDepth(4, program4, data, depth1),fn:self.program(2, program2, data),data:data});
      if(stack1 || stack1 === 0) { buffer += stack1; }
      stack1 = helpers['if'].call(depth0, (depth0 && depth0.cls), {hash:{},inverse:self.noop,fn:self.program(9, program9, data),data:data});
      if(stack1 || stack1 === 0) { buffer += stack1; }
      buffer += "\">\n        <a class=\"block box\" href=\"";
      if (helper = helpers.url) { stack1 = helper.call(depth0, {hash:{},data:data}); }
      else { helper = (depth0 && depth0.url); stack1 = typeof helper === functionType ? helper.call(depth0, {hash:{},data:data}) : helper; }
      buffer += escapeExpression(stack1)
        + "\">\n            <h3 class=\"title\">";
      if (helper = helpers.label) { stack1 = helper.call(depth0, {hash:{},data:data}); }
      else { helper = (depth0 && depth0.label); stack1 = typeof helper === functionType ? helper.call(depth0, {hash:{},data:data}) : helper; }
      buffer += escapeExpression(stack1)
        + "</h3>\n            ";
      stack1 = helpers['if'].call(depth0, (depth0 && depth0.content), {hash:{},inverse:self.noop,fn:self.program(11, program11, data),data:data});
      if(stack1 || stack1 === 0) { buffer += stack1; }
      buffer += "\n            <div class=\"bottom clearfix\">\n                ";
      stack1 = helpers['if'].call(depth0, (depth0 && depth0.html), {hash:{},inverse:self.noop,fn:self.program(13, program13, data),data:data});
      if(stack1 || stack1 === 0) { buffer += stack1; }
      buffer += "\n                ";
      stack1 = helpers['if'].call(depth0, (depth0 && depth0.text), {hash:{},inverse:self.noop,fn:self.program(15, program15, data),data:data});
      if(stack1 || stack1 === 0) { buffer += stack1; }
      buffer += "\n            </div>\n        </a>\n    </li>\n    ";
      return buffer;
      }
    function program2(depth0,data) {
      
      var stack1, helper;
      if (helper = helpers.width) { stack1 = helper.call(depth0, {hash:{},data:data}); }
      else { helper = (depth0 && depth0.width); stack1 = typeof helper === functionType ? helper.call(depth0, {hash:{},data:data}) : helper; }
      return escapeExpression(stack1);
      }

    function program4(depth0,data,depth2) {
      
      var stack1;
      stack1 = helpers['if'].call(depth0, (depth2 && depth2.width), {hash:{},inverse:self.program(7, program7, data),fn:self.programWithDepth(5, program5, data, depth2),data:data});
      if(stack1 || stack1 === 0) { return stack1; }
      else { return ''; }
      }
    function program5(depth0,data,depth3) {
      
      var stack1;
      return escapeExpression(((stack1 = (depth3 && depth3.width)),typeof stack1 === functionType ? stack1.apply(depth0) : stack1));
      }

    function program7(depth0,data) {
      
      
      return "12";
      }

    function program9(depth0,data) {
      
      var buffer = "", stack1, helper;
      buffer += " ";
      if (helper = helpers.cls) { stack1 = helper.call(depth0, {hash:{},data:data}); }
      else { helper = (depth0 && depth0.cls); stack1 = typeof helper === functionType ? helper.call(depth0, {hash:{},data:data}) : helper; }
      buffer += escapeExpression(stack1);
      return buffer;
      }

    function program11(depth0,data) {
      
      var buffer = "", stack1, helper;
      buffer += "<div class=\"content clearfix\">";
      if (helper = helpers.content) { stack1 = helper.call(depth0, {hash:{},data:data}); }
      else { helper = (depth0 && depth0.content); stack1 = typeof helper === functionType ? helper.call(depth0, {hash:{},data:data}) : helper; }
      if(stack1 || stack1 === 0) { buffer += stack1; }
      buffer += "</div>";
      return buffer;
      }

    function program13(depth0,data) {
      
      var buffer = "", stack1, helper;
      buffer += "<span class=\"text-html\">";
      if (helper = helpers.html) { stack1 = helper.call(depth0, {hash:{},data:data}); }
      else { helper = (depth0 && depth0.html); stack1 = typeof helper === functionType ? helper.call(depth0, {hash:{},data:data}) : helper; }
      if(stack1 || stack1 === 0) { buffer += stack1; }
      buffer += "</span>";
      return buffer;
      }

    function program15(depth0,data) {
      
      var buffer = "", stack1, helper;
      buffer += "<span class=\"text-link\"><span class=\"icon-play\"></span>";
      if (helper = helpers.text) { stack1 = helper.call(depth0, {hash:{},data:data}); }
      else { helper = (depth0 && depth0.text); stack1 = typeof helper === functionType ? helper.call(depth0, {hash:{},data:data}) : helper; }
      buffer += escapeExpression(stack1)
        + "</span>";
      return buffer;
      }

      buffer += "<ul class=\"list-box flex-grid plain\">\n    ";
      stack1 = helpers.each.call(depth0, (depth0 && depth0.list), {hash:{},inverse:self.noop,fn:self.programWithDepth(1, program1, data, depth0),data:data});
      if(stack1 || stack1 === 0) { buffer += stack1; }
      buffer += "\n</ul>\n";
      return buffer;
      });
    function listTpl(data, options, asString) {
      var html = Template$1(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 (original work) Open Assessment Technologies SA ;
     */

    /**
     * Some default values
     * @type {Object}
     * @private
     */
    var _defaults = {
      title: false,
      textNumber: __('Available'),
      textEmpty: __('There is nothing to list!'),
      textLoading: __('Loading'),
      flex: 12
    };

    /**
     * Defines a list of boxes
     * @type {Object}
     */
    var listBox = {
      /**
       * Updates the list of boxes
       * @param {Array} list
       * @param {String} [list.url] - The URL of the entry point
       * @param {String} [list.label] - The displayed label
       * @param {String} [list.content] - An optional content displayed in the middle
       * @param {String} [list.text] - A bottom text
       * @param {String} [list.html] - A bottom html
       * @param {Number} [list.width] - The width of the entry related to flex-grid (default: 6)
       * @param {String} [list.cls] - An optional CSS class to add
       * @returns {listBox}
       */
      update: function update(list) {
        var $list = this.controls && this.controls.$list;
        var $numberValue = this.controls && this.controls.$numberValue;
        var count;
        this.setLoading(true);
        if ($list) {
          $list.empty();
          if (list && list.length) {
            $list.append(listTpl({
              list: list,
              width: this.config.flex
            }));
            if ($numberValue) {
              count = list.length;
              if (_.isFunction(this.config.countRenderer)) {
                count = this.config.countRenderer(count);
              }
              $numberValue.text(count);
            }
            this.setState('empty', false);
            this.setState('loaded', true);
          } else {
            this.setState('empty', true);
            this.setState('loaded', false);
          }
        }
        this.setLoading(false);
        return this;
      },
      /**
       * Sets the loading state
       * @param {Boolean} flag
       * @returns {listBox}
       */
      setLoading: function setLoading(flag) {
        if (flag) {
          this.setState('loaded', false);
        }
        return this.setState('loading', flag);
      },
      /**
       * Sets the title of the list.
       * @param {String|Boolean} title - The text to set. If the value is false no title is displayed
       * @returns {listBox}
       */
      setTitle: function setTitle(title) {
        var $title = this.controls && this.controls.$title;
        this.config.title = title;
        if ($title) {
          if (false === title) {
            $title.addClass('hidden');
          } else {
            $title.html(title).removeClass('hidden');
          }
        }
        return this;
      },
      /**
       * Sets the label of the number of boxes.
       * @param {String|Boolean} text - The text to set. If the value is false no label is displayed
       * @returns {listBox}
       */
      setTextNumber: function setTextNumber(text) {
        var $numberLabel = this.controls && this.controls.$numberLabel;
        var $textAvailable = this.controls && this.controls.$textAvailable;
        this.config.textNumber = text;
        if ($numberLabel) {
          if (text !== false) {
            $numberLabel.html(text).removeClass('hidden');
          } else if ($textAvailable) {
            $textAvailable.addClass('hidden');
          }
        }
        return this;
      },
      /**
       * Sets the label displayed when there no boxes available.
       * @param {String|Boolean} text - The text to set. If the value is false no label is displayed
       * @returns {listBox}
       */
      setTextEmpty: function setTextEmpty(text) {
        var $textEmpty = this.controls && this.controls.$textEmpty;
        this.config.textEmpty = text;
        if ($textEmpty) {
          if (false === text) {
            $textEmpty.addClass('hidden');
          } else {
            $textEmpty.html(text).removeClass('hidden');
          }
        }
        return this;
      },
      /**
       * Sets the label displayed when the list is loading.
       * @param {String|Boolean} text - The text to set. If the value is false no label is displayed
       * @returns {listBox}
       */
      setTextLoading: function setTextLoading(text) {
        var $textLoading = this.controls && this.controls.$textLoading;
        this.config.textLoading = text;
        if ($textLoading) {
          if (false === text) {
            $textLoading.addClass('hidden');
          } else {
            $textLoading.html(text).removeClass('hidden');
          }
        }
        return this;
      },
      /**
       * Just override the base method to prevent unwanted resizing: the width must be handled by CSS flex box
       * @returns {listBox}
       */
      setSize: function setSize() {
        //eslint-disable-next-line no-console
        console.warn('The setSize() method has been disabled in the listbox component as its size is handled by CSS flex box');
        return this;
      }
    };

    /**
     * Builds an instance of the listBox manager
     * @param {Object} config
     * @param {String|Boolean} [config.title] - Sets the title of the list. If the value is false no title is displayed (default: false)
     * @param {String|Boolean} [config.textNumber] - Sets the label of the number of boxes. If the value is false no label is displayed (default: 'Available')
     * @param {String|Boolean} [config.textEmpty] - Sets the label displayed when there no boxes available. If the value is false no label is displayed (default: 'There is nothing to list!')
     * @param {String|Boolean} [config.textLoading] - Sets the label displayed when the list is loading. If the value is false no label is displayed (default: 'Loading')
     * @param {Number} [config.width] - Sets the default flex width of all boxes, unless they define their own value. (default: 12)
     * @param {Array} [config.list] - The list of boxes to display
     * @param {jQuery|HTMLElement|String} [config.renderTo] - An optional container in which renders the component
     * @param {Boolean} [config.replace] - When the component is appended to its container, clears the place before
     * @param {Function} [config.countRenderer] - An optional callback applied on the list count before display
     * @returns {listBox}
     */
    var listBoxFactory = function listBoxFactory(config) {
      var initConfig = config || {};

      // The width is managed by CSS flex box. So we need to remap the flex width to another attribute,
      // as the width is used to size the base component using pixels units.
      // Please also note the main size handling brought by the base component is not used here, and has been disabled.
      initConfig.flex = initConfig.width;
      return component(listBox, _defaults).setTemplate(mainTpl)

      // uninstalls the component
      .on('destroy', function () {
        this.controls = null;
      })

      // renders the component
      .on('render', function () {
        this.controls = {
          $title: this.$component.find('h1'),
          $textEmpty: this.$component.find('.empty-list'),
          $textAvailable: this.$component.find('.available-list'),
          $textLoading: this.$component.find('.loading span'),
          $numberLabel: this.$component.find('.available-list .label'),
          $numberValue: this.$component.find('.available-list .count'),
          $list: this.$component.find('.list')
        };
        if (this.config.list) {
          this.update(this.config.list);
        } else {
          this.setState('empty', true);
          this.setState('loaded', false);
        }
      }).init(_.omit(initConfig, 'width'));
    };

    return listBoxFactory;

});
