define(['jquery', 'ui/autoscroll', 'ui/component', 'handlebars', 'lib/handlebars/helpers', 'css!ui/itemButtonList/css/item-button-list.css'], function ($$1, autoscroll, componentFactory, Handlebars, Helpers0, itemButtonList_css) { 'use strict';

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

    function program1(depth0,data) {
      
      var buffer = "", stack1, helper;
      buffer += "\n    <li class=\"buttonlist-item ";
      if (helper = helpers.status) { stack1 = helper.call(depth0, {hash:{},data:data}); }
      else { helper = (depth0 && depth0.status); stack1 = typeof helper === functionType ? helper.call(depth0, {hash:{},data:data}) : helper; }
      buffer += escapeExpression(stack1);
      stack1 = helpers['if'].call(depth0, (depth0 && depth0.scoreType), {hash:{},inverse:self.noop,fn:self.program(2, program2, data),data:data});
      if(stack1 || stack1 === 0) { buffer += stack1; }
      stack1 = helpers['if'].call(depth0, (depth0 && depth0.disabled), {hash:{},inverse:self.noop,fn:self.program(4, program4, data),data:data});
      if(stack1 || stack1 === 0) { buffer += stack1; }
      buffer += "\" data-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)
        + "\">\n        <button class=\"buttonlist-btn\"\n                role=\"link\"\n                ";
      stack1 = helpers['if'].call(depth0, (depth0 && depth0.title), {hash:{},inverse:self.noop,fn:self.program(6, program6, data),data:data});
      if(stack1 || stack1 === 0) { buffer += stack1; }
      buffer += "\n                aria-label=\"";
      if (helper = helpers.ariaLabel) { stack1 = helper.call(depth0, {hash:{},data:data}); }
      else { helper = (depth0 && depth0.ariaLabel); stack1 = typeof helper === functionType ? helper.call(depth0, {hash:{},data:data}) : helper; }
      buffer += escapeExpression(stack1)
        + "\"\n                ";
      stack1 = helpers['if'].call(depth0, (depth0 && depth0.disabled), {hash:{},inverse:self.noop,fn:self.program(8, program8, data),data:data});
      if(stack1 || stack1 === 0) { buffer += stack1; }
      buffer += "\n                data-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)
        + "\">\n            <span class=\"icon-indicator indicator\" aria-hidden=\"true\"></span>\n            ";
      stack1 = helpers['if'].call(depth0, (depth0 && depth0.scoreType), {hash:{},inverse:self.noop,fn:self.program(10, program10, data),data:data});
      if(stack1 || stack1 === 0) { buffer += stack1; }
      buffer += "\n            <span class=\"buttonlist-icon";
      stack1 = helpers['if'].call(depth0, (depth0 && depth0.icon), {hash:{},inverse:self.noop,fn:self.program(12, program12, data),data:data});
      if(stack1 || stack1 === 0) { buffer += stack1; }
      buffer += "\" aria-hidden=\"true\"></span>\n            <span class=\"buttonlist-label\" aria-hidden=\"true\">";
      stack1 = helpers['if'].call(depth0, (depth0 && depth0.numericLabel), {hash:{},inverse:self.noop,fn:self.program(14, program14, data),data:data});
      if(stack1 || stack1 === 0) { buffer += stack1; }
      buffer += "</span>\n        </button>\n    </li>\n    ";
      return buffer;
      }
    function program2(depth0,data) {
      
      var buffer = "", stack1, helper;
      buffer += " ";
      if (helper = helpers.scoreType) { stack1 = helper.call(depth0, {hash:{},data:data}); }
      else { helper = (depth0 && depth0.scoreType); stack1 = typeof helper === functionType ? helper.call(depth0, {hash:{},data:data}) : helper; }
      buffer += escapeExpression(stack1);
      return buffer;
      }

    function program4(depth0,data) {
      
      
      return " disabled";
      }

    function program6(depth0,data) {
      
      var buffer = "", stack1, helper;
      buffer += "title=\"";
      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)
        + "\"";
      return buffer;
      }

    function program8(depth0,data) {
      
      
      return "aria-disabled=\"true\"";
      }

    function program10(depth0,data) {
      
      var buffer = "", stack1, helper;
      buffer += "\n                <span class=\"buttonlist-score-badge\">\n                    <span class=\"buttonlist-score-icon icon-";
      if (helper = helpers.scoreType) { stack1 = helper.call(depth0, {hash:{},data:data}); }
      else { helper = (depth0 && depth0.scoreType); stack1 = typeof helper === functionType ? helper.call(depth0, {hash:{},data:data}) : helper; }
      buffer += escapeExpression(stack1)
        + "\" aria-hidden=\"true\"></span>\n                </span>\n            ";
      return buffer;
      }

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

    function program14(depth0,data) {
      
      var stack1, helper;
      if (helper = helpers.numericLabel) { stack1 = helper.call(depth0, {hash:{},data:data}); }
      else { helper = (depth0 && depth0.numericLabel); stack1 = typeof helper === functionType ? helper.call(depth0, {hash:{},data:data}) : helper; }
      return escapeExpression(stack1);
      }

      buffer += "<ol class=\"buttonlist-items\">\n    ";
      stack1 = helpers.each.call(depth0, (depth0 && depth0.items), {hash:{},inverse:self.noop,fn:self.program(1, program1, data),data:data});
      if(stack1 || stack1 === 0) { buffer += stack1; }
      buffer += "\n</ol>\n";
      return buffer;
      });
    function itemButtonListTpl(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) 2022 Open Assessment Technologies SA ;
     */

    /**
     * CSS classes involved in the component
     * @type {Object}
     */
    const cssClasses = {
      active: 'buttonlist-item-active',
      keyfocused: 'buttonlist-btn-focus'
    };

    /**
     * CSS selectors that match some particular elements
     * @type {Object}
     */
    const cssSelectors = {
      active: `.${cssClasses.active}`,
      keyfocused: `.${cssClasses.keyfocused}`,
      navigable: '.buttonlist-btn',
      itemById: id => `.buttonlist-item[data-id="${id}"]`,
      navigableById: id => `.buttonlist-btn[data-id="${id}"]`
    };

    /**
     * @typedef {Object} ItemButton
     * @property {String} id - item id
     * @property {Number} position - 0-based list index
     * @property {String} numericLabel - displayed number
     * @property {String} ariaLabel
     * @property {String} status - 'answered'/'viewed'/'unseen'
     * @property {String} scoreType - 'correct'/'incorrect'/'score-pending'/'score-partial'/null
     * @property {String} icon - 'info'/'flagged'/null
     * @property {Boolean} disabled
     * @property {String} [title] - optional tooltip
     */
    /**
     * Item Button List
     * Ordered list of buttons representing items from a testMap section
     * Mostly presentational component
     *
     * @param {Object} config
     * @param {ItemButton[]} [config.items] - The list of entries to display
     * @param {String|jQuery|HTMLElement} [scrollContainer] - scroll container element, for autoscroll
     * @returns {component}
     * @fires ready - When the component is ready to work
     * @fires click When an item is selected by the user
     */
    function itemButtonListFactory() {
      let config = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
      let component;
      let activeItemId = null;

      //jQuery or HTMLElement!
      /**
       * Get scroll container element
       * @returns {HTMLElement}
       */
      const getScrollContainer = () => {
        return config.scrollContainer || component.getElement();
      };

      /**
       * Selects the active item
       * @param {String|null} itemId
       */
      const selectItem = itemId => {
        // first deactivate already active elements
        component.getElement().find(cssSelectors.active).removeClass(cssClasses.active);
        component.getElement().find(`${cssSelectors.navigable}[aria-current]`).removeAttr('aria-current');

        // activate element
        if (itemId) {
          const $target = component.getElement().find(cssSelectors.itemById(itemId));
          if ($target.length) {
            $target.addClass(cssClasses.active);
            // finally make sure the item is visible
            autoscroll($target, getScrollContainer());
            const $ariaTarget = component.getElement().find(cssSelectors.navigableById(itemId));
            $ariaTarget.attr('aria-current', 'location');
          }
        }
      };

      /**
       * Update single item properties:
       * Only `icon`, `numericLabel`, `ariaLabel` are supported
       * @param {String} itemId
       * @param {Object} itemData
       */
      const updateItemData = (itemId, itemData) => {
        const $target = component.getElement().find(cssSelectors.itemById(itemId));
        if ($target.length) {
          if (typeof itemData.icon !== 'undefined') {
            const iconElem = $target.find('.buttonlist-icon').get(0);
            for (let i = 0; i < iconElem.classList.length; i++) {
              if (iconElem.classList[i].startsWith('icon-')) {
                iconElem.classList.remove(iconElem.classList[i]);
              }
            }
            if (itemData.icon) {
              iconElem.classList.add(`icon-${itemData.icon}`);
            }
          }
          if (typeof itemData.numericLabel !== 'undefined') {
            $target.find('.buttonlist-label').text(itemData.numericLabel !== null ? itemData.numericLabel : '');
          }
          if (typeof itemData.ariaLabel !== 'undefined') {
            $target.find('.buttonlist-btn').attr('aria-label', itemData.ariaLabel);
          }
        }
      };

      /**
       * 'tabfocus' styling, for Safari until :focus-visible supported
       * @param {jQuery|null}  $target
       */
      const setFocusStyle = $target => {
        component.getElement().find(cssSelectors.keyfocused).removeClass(cssClasses.keyfocused);
        if ($target && $target.length) {
          $target.addClass(cssClasses.keyfocused);
        }
      };

      /**
       * Apply a callback on each navigable element
       * @param {*} callback
       */
      const eachNavigable = callback => {
        component.getElement().find(cssSelectors.navigable).each(callback);
      };

      /**
       * Enables the keyboard navigation using 'tab' keys
       */
      const enableKeyboard = () => {
        eachNavigable((index, el) => el.removeAttribute('tabindex'));
      };

      /**
       * Disables the keyboard navigation using 'tab' keys
       */
      const disableKeyboard = () => {
        eachNavigable((index, el) => el.setAttribute('tabindex', -1));
        setFocusStyle(null);
      };

      /**
       * Emits the click event detailing the clicked item
       * The active item change should be handled by the consumer through the API, in case it is conditional or asynchronous
       * @param {String} itemId
       */
      const onClick = itemId => {
        /**
         * @event click
         * @param {String} itemId
         */
        component.trigger('click', {
          id: itemId
        });
      };

      /**
       * Defines the buttonList API
       * @type {buttonList}
       */
      const api = {
        /**
         * Sets the active item
         * @param {String} itemId
         * @returns {buttonList}
         */
        setActiveItem(itemId) {
          activeItemId = itemId;
          if (this.is('rendered')) {
            selectItem(itemId);
          }
          return this;
        },
        /**
         * Update single item properties:
         * Only `icon`, `numericLabel`, `ariaLabel` are supported
         * @param {String} itemId
         * @param {Object} itemData
         * @returns {buttonList}
         */
        updateItem(itemId, itemData) {
          if (this.is('rendered')) {
            updateItemData(itemId, itemData);
          }
          return this;
        }
      };

      /**
       * @typedef {component} buttonList
       */
      component = componentFactory(api, {})
      // set the component's layout
      .setTemplate(itemButtonListTpl)
      // renders the component
      .on('render', function onItemButtonListRender() {
        // 'tabfocus' detection, for Safari until :focus-visible supported
        this.getElement().on('keydown', cssSelectors.navigable, e => {
          if (e.key === 'Tab') {
            setFocusStyle(null);
          }
        });
        this.getElement().on('keyup', cssSelectors.navigable, e => {
          if (e.key === 'Tab') {
            setFocusStyle($$1(e.target));
          }
        });
        component.getElement().on('click', cssSelectors.navigable, e => {
          //does not check `disabled` property of clicked item, should be checked by consumer
          if (!this.is('disabled')) {
            onClick(e.currentTarget.dataset.id);
          }
        });
        selectItem(activeItemId);
        if (!this.is('disabled')) {
          enableKeyboard();
        } else {
          disableKeyboard();
        }

        /**
         * @event ready
         */
        this.setState('ready', true).trigger('ready');
      })

      // reflect enable/disabled state
      .on('enable', () => enableKeyboard).on('disable', () => disableKeyboard);

      // initialize the component with the provided config:
      // config also contains data passed to template when rendering
      component.init(config);
      return component;
    }

    return itemButtonListFactory;

});
