define(['jquery', 'lodash', 'i18n', 'handlebars', 'lib/handlebars/helpers', 'ui/component', 'ui/keyNavigation/navigator', 'ui/keyNavigation/navigableDomElement', 'util/shortcut/registry', 'util/shortcut', 'util/namespace', 'ui/modal', 'select2', 'css!ui/bulkActionPopup/css/bulkActionPopup.css'], function ($$1, _, __, Handlebars, Helpers0, component, keyNavigator, navigableDomElement, shortcutRegistry, globalShortcut, namespaceHelper, modal, select2, bulkActionPopup_css) { 'use strict';

    $$1 = $$1 && Object.prototype.hasOwnProperty.call($$1, 'default') ? $$1['default'] : $$1;
    _ = _ && Object.prototype.hasOwnProperty.call(_, 'default') ? _['default'] : _;
    __ = __ && Object.prototype.hasOwnProperty.call(__, 'default') ? __['default'] : __;
    Handlebars = Handlebars && Object.prototype.hasOwnProperty.call(Handlebars, 'default') ? Handlebars['default'] : Handlebars;
    Helpers0 = Helpers0 && Object.prototype.hasOwnProperty.call(Helpers0, 'default') ? Helpers0['default'] : Helpers0;
    component = component && Object.prototype.hasOwnProperty.call(component, 'default') ? component['default'] : component;
    keyNavigator = keyNavigator && Object.prototype.hasOwnProperty.call(keyNavigator, 'default') ? keyNavigator['default'] : keyNavigator;
    navigableDomElement = navigableDomElement && Object.prototype.hasOwnProperty.call(navigableDomElement, 'default') ? navigableDomElement['default'] : navigableDomElement;
    shortcutRegistry = shortcutRegistry && Object.prototype.hasOwnProperty.call(shortcutRegistry, 'default') ? shortcutRegistry['default'] : shortcutRegistry;
    globalShortcut = globalShortcut && Object.prototype.hasOwnProperty.call(globalShortcut, 'default') ? globalShortcut['default'] : globalShortcut;
    namespaceHelper = namespaceHelper && Object.prototype.hasOwnProperty.call(namespaceHelper, 'default') ? namespaceHelper['default'] : namespaceHelper;

    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, helperMissing=helpers.helperMissing, self=this;

    function program1(depth0,data) {
      
      var buffer = "", stack1;
      buffer += "\n        ";
      stack1 = helpers['if'].call(depth0, (depth0 && depth0.single), {hash:{},inverse:self.program(4, program4, data),fn:self.program(2, program2, data),data:data});
      if(stack1 || stack1 === 0) { buffer += stack1; }
      buffer += "\n    ";
      return buffer;
      }
    function program2(depth0,data) {
      
      var buffer = "", stack1, helper, options;
      buffer += "\n    <div class=\"single\" data-resource=\""
        + escapeExpression(((stack1 = ((stack1 = ((stack1 = (depth0 && depth0.allowedResources)),stack1 == null || stack1 === false ? stack1 : stack1[0])),stack1 == null || stack1 === false ? stack1 : stack1.id)),typeof stack1 === functionType ? stack1.apply(depth0) : stack1))
        + "\">\n        <p>\n            "
        + escapeExpression((helper = helpers.__ || (depth0 && depth0.__),options={hash:{},data:data},helper ? helper.call(depth0, "The action will be applied to", options) : helperMissing.call(depth0, "__", "The action will be applied to", options)))
        + " ";
      if (helper = helpers.resourceType) { stack1 = helper.call(depth0, {hash:{},data:data}); }
      else { helper = (depth0 && depth0.resourceType); stack1 = typeof helper === functionType ? helper.call(depth0, {hash:{},data:data}) : helper; }
      buffer += escapeExpression(stack1)
        + " <span class=\"resource-label\">"
        + escapeExpression(((stack1 = ((stack1 = ((stack1 = (depth0 && depth0.allowedResources)),stack1 == null || stack1 === false ? stack1 : stack1[0])),stack1 == null || stack1 === false ? stack1 : stack1.label)),typeof stack1 === functionType ? stack1.apply(depth0) : stack1))
        + "</span>\n        </p>\n    </div>\n        ";
      return buffer;
      }

    function program4(depth0,data) {
      
      var buffer = "", stack1, helper, options;
      buffer += "\n    <div class=\"multiple\">\n        <p>\n            "
        + escapeExpression((helper = helpers.__ || (depth0 && depth0.__),options={hash:{},data:data},helper ? helper.call(depth0, "The action will be applied to the following", options) : helperMissing.call(depth0, "__", "The action will be applied to the following", options)))
        + " <span class=\"resource-count\">";
      if (helper = helpers.resourceCount) { stack1 = helper.call(depth0, {hash:{},data:data}); }
      else { helper = (depth0 && depth0.resourceCount); stack1 = typeof helper === functionType ? helper.call(depth0, {hash:{},data:data}) : helper; }
      buffer += escapeExpression(stack1)
        + "</span> ";
      if (helper = helpers.resourceTypes) { stack1 = helper.call(depth0, {hash:{},data:data}); }
      else { helper = (depth0 && depth0.resourceTypes); stack1 = typeof helper === functionType ? helper.call(depth0, {hash:{},data:data}) : helper; }
      buffer += escapeExpression(stack1)
        + ":\n        </p>\n        <ul class=\"plain applicables\">\n            ";
      stack1 = helpers.each.call(depth0, (depth0 && depth0.allowedResources), {hash:{},inverse:self.noop,fn:self.program(5, program5, data),data:data});
      if(stack1 || stack1 === 0) { buffer += stack1; }
      buffer += "\n        </ul>\n    </div>\n        ";
      return buffer;
      }
    function program5(depth0,data) {
      
      var buffer = "", stack1, helper;
      buffer += "\n            <li data-resource=\"";
      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=\"resource-label\">";
      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)
        + "</span>\n            </li>\n            ";
      return buffer;
      }

    function program7(depth0,data) {
      
      var buffer = "", stack1;
      buffer += "\n        ";
      stack1 = helpers['if'].call(depth0, (depth0 && depth0.singleDenied), {hash:{},inverse:self.program(10, program10, data),fn:self.program(8, program8, data),data:data});
      if(stack1 || stack1 === 0) { buffer += stack1; }
      buffer += "\n    ";
      return buffer;
      }
    function program8(depth0,data) {
      
      var buffer = "", stack1, helper, options;
      buffer += "\n    <div class=\"single\" data-resource=\""
        + escapeExpression(((stack1 = ((stack1 = ((stack1 = (depth0 && depth0.deniedResources)),stack1 == null || stack1 === false ? stack1 : stack1[0])),stack1 == null || stack1 === false ? stack1 : stack1.id)),typeof stack1 === functionType ? stack1.apply(depth0) : stack1))
        + "\">\n        <p>\n            "
        + escapeExpression((helper = helpers.__ || (depth0 && depth0.__),options={hash:{},data:data},helper ? helper.call(depth0, "The action will not be applied to ", options) : helperMissing.call(depth0, "__", "The action will not be applied to ", options)))
        + " ";
      if (helper = helpers.resourceType) { stack1 = helper.call(depth0, {hash:{},data:data}); }
      else { helper = (depth0 && depth0.resourceType); stack1 = typeof helper === functionType ? helper.call(depth0, {hash:{},data:data}) : helper; }
      buffer += escapeExpression(stack1)
        + "\n            <span class=\"resource-label\">"
        + escapeExpression(((stack1 = ((stack1 = ((stack1 = (depth0 && depth0.deniedResources)),stack1 == null || stack1 === false ? stack1 : stack1[0])),stack1 == null || stack1 === false ? stack1 : stack1.label)),typeof stack1 === functionType ? stack1.apply(depth0) : stack1))
        + "</span>\n            <span class=\"reason\">("
        + escapeExpression(((stack1 = ((stack1 = ((stack1 = (depth0 && depth0.deniedResources)),stack1 == null || stack1 === false ? stack1 : stack1[0])),stack1 == null || stack1 === false ? stack1 : stack1.reason)),typeof stack1 === functionType ? stack1.apply(depth0) : stack1))
        + ")</span>\n        </p>\n    </div>\n        ";
      return buffer;
      }

    function program10(depth0,data) {
      
      var buffer = "", stack1, helper, options;
      buffer += "\n    <p>\n        "
        + escapeExpression((helper = helpers.__ || (depth0 && depth0.__),options={hash:{},data:data},helper ? helper.call(depth0, "The action will not be applied to the following", options) : helperMissing.call(depth0, "__", "The action will not be applied to the following", options)))
        + " ";
      if (helper = helpers.resourceTypes) { stack1 = helper.call(depth0, {hash:{},data:data}); }
      else { helper = (depth0 && depth0.resourceTypes); stack1 = typeof helper === functionType ? helper.call(depth0, {hash:{},data:data}) : helper; }
      buffer += escapeExpression(stack1)
        + ":\n    </p>\n    <ul class=\"plain no-applicables\">\n            ";
      stack1 = helpers.each.call(depth0, (depth0 && depth0.deniedResources), {hash:{},inverse:self.noop,fn:self.program(11, program11, data),data:data});
      if(stack1 || stack1 === 0) { buffer += stack1; }
      buffer += "\n    </ul>\n        ";
      return buffer;
      }
    function program11(depth0,data) {
      
      var buffer = "", stack1, helper;
      buffer += "\n        <li data-resource=\"";
      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=\"resource-label\">";
      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)
        + "</span>\n            <span class=\"reason\">(";
      if (helper = helpers.reason) { stack1 = helper.call(depth0, {hash:{},data:data}); }
      else { helper = (depth0 && depth0.reason); stack1 = typeof helper === functionType ? helper.call(depth0, {hash:{},data:data}) : helper; }
      buffer += escapeExpression(stack1)
        + ")</span>\n        </li>\n            ";
      return buffer;
      }

    function program13(depth0,data) {
      
      var buffer = "", helper, options;
      buffer += "\n    <div class=\"reason\">\n        <p>\n            "
        + escapeExpression((helper = helpers.__ || (depth0 && depth0.__),options={hash:{},data:data},helper ? helper.call(depth0, "Please provide a reason", options) : helperMissing.call(depth0, "__", "Please provide a reason", options)))
        + ":\n        </p>\n        <div class=\"categories\"></div>\n        <div class=\"comment\">\n            <textarea placeholder=\""
        + escapeExpression((helper = helpers.__ || (depth0 && depth0.__),options={hash:{},data:data},helper ? helper.call(depth0, "comment...", options) : helperMissing.call(depth0, "__", "comment...", options)))
        + "\"></textarea>\n        </div>\n    </div>\n    ";
      return buffer;
      }

    function program15(depth0,data) {
      
      var buffer = "", stack1, helper;
      buffer += "\n        <div class=\"message\">\n            ";
      stack1 = helpers['if'].call(depth0, (depth0 && depth0.icon), {hash:{},inverse:self.noop,fn:self.program(16, program16, data),data:data});
      if(stack1 || stack1 === 0) { buffer += stack1; }
      buffer += "\n            ";
      if (helper = helpers.message) { stack1 = helper.call(depth0, {hash:{},data:data}); }
      else { helper = (depth0 && depth0.message); stack1 = typeof helper === functionType ? helper.call(depth0, {hash:{},data:data}) : helper; }
      buffer += escapeExpression(stack1)
        + "\n        </div>\n    ";
      return buffer;
      }
    function program16(depth0,data) {
      
      var buffer = "", stack1, helper;
      buffer += "\n                <span class=\"icon 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)
        + "\"></span>\n            ";
      return buffer;
      }

      buffer += "<div class=\"bulk-action-popup\">\n    <h2 class=\"title\">"
        + escapeExpression((helper = helpers.__ || (depth0 && depth0.__),options={hash:{},data:data},helper ? helper.call(depth0, "Action", options) : helperMissing.call(depth0, "__", "Action", options)))
        + ": ";
      if (helper = helpers.actionName) { stack1 = helper.call(depth0, {hash:{},data:data}); }
      else { helper = (depth0 && depth0.actionName); stack1 = typeof helper === functionType ? helper.call(depth0, {hash:{},data:data}) : helper; }
      buffer += escapeExpression(stack1)
        + "</h2>\n\n    ";
      stack1 = helpers['if'].call(depth0, (depth0 && depth0.resourceCount), {hash:{},inverse:self.noop,fn:self.program(1, program1, data),data:data});
      if(stack1 || stack1 === 0) { buffer += stack1; }
      buffer += "\n\n    ";
      stack1 = helpers['if'].call(depth0, ((stack1 = (depth0 && depth0.deniedResources)),stack1 == null || stack1 === false ? stack1 : stack1.length), {hash:{},inverse:self.noop,fn:self.program(7, program7, data),data:data});
      if(stack1 || stack1 === 0) { buffer += stack1; }
      buffer += "\n\n    ";
      stack1 = helpers['if'].call(depth0, (depth0 && depth0.reason), {hash:{},inverse:self.noop,fn:self.program(13, program13, data),data:data});
      if(stack1 || stack1 === 0) { buffer += stack1; }
      buffer += "\n\n    ";
      stack1 = helpers['if'].call(depth0, (depth0 && depth0.message), {hash:{},inverse:self.noop,fn:self.program(15, program15, data),data:data});
      if(stack1 || stack1 === 0) { buffer += stack1; }
      buffer += "\n\n    <div class=\"actions\">\n        <button class=\"btn btn-info small done\">"
        + escapeExpression((helper = helpers.__ || (depth0 && depth0.__),options={hash:{},data:data},helper ? helper.call(depth0, "OK", options) : helperMissing.call(depth0, "__", "OK", options)))
        + "</button>\n        <a href=\"#\" class=\"btn cancel\" title=\""
        + escapeExpression((helper = helpers.__ || (depth0 && depth0.__),options={hash:{},data:data},helper ? helper.call(depth0, "cancel the action", options) : helperMissing.call(depth0, "__", "cancel the action", options)))
        + "\">"
        + escapeExpression((helper = helpers.__ || (depth0 && depth0.__),options={hash:{},data:data},helper ? helper.call(depth0, "cancel", options) : helperMissing.call(depth0, "__", "cancel", options)))
        + "</a>\n    </div>\n\n</div>";
      return buffer;
      });
    function layoutTpl(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-2019 Open Assessment Technologies SA;
    */

    /**
     * Namespace used in events and shortcuts
     * @type {String}
     * @private
     */
    const _ns = 'bulk-action-popup';

    /**
     * Builds an instance of the bulkActionPopup component
     *
     * @param {Object} config
     * @param {jQuery} config.renderTo - the jQuery container it should be rendered to
     * @param {String} config.actionName - the action name (use in the title text)
     * @param {String} config.resourceType - the name of the resource type (use in the text)
     * @param {Boolean} [config.allowShortcuts] - allow keyboard shortcuts (Esc to cancel, Enter to validate)
     * @param {String} [config.resourceTypes] - the name of the resource type in plural (use in the text)
     * @param {Boolean} [config.reason] - defines if the reason section should be displayed or not
     * @param {Function} [config.categoriesSelector] - callback renderer for categories
     * @param {Array} config.allowedResources - list of allowed resources to be displayed
     * @param {Array} [config.deniedResources] - list of denied resources to be displayed
     * @param {String} config.message - message or warning (will be shown at the bottom of the popup)
     * @param {String} config.icon - icon from the TAO font (will be shown before the message)
     * @returns {bulkActionPopup}
     */
    function bulkActionPopupFactory(config) {
      //private object to hold the state of edition
      const state = {
        reasons: null,
        comment: ''
      };
      const instance = component({
        /**
         * Validates the dialog, and closes it (action performed when hitting the Ok button)
         * @returns {Boolean} Returns `true` if the dialog has been successively validated (and closed)
         */
        validate() {
          const $element = this.getElement();
          if ($element) {
            $$1('.feedback-error', $element).remove();
            if (!checkRequiredFields($element)) {
              const $error = $$1('<div class="feedback-error small"></div>').text(__('All fields are required'));
              $element.find('.actions').prepend($error);
              return false;
            }
          }
          this.trigger('ok', state);
          this.destroy();
          return true;
        },
        /**
         * Cancels and closes the dialog
         */
        cancel() {
          this.trigger('cancel');
          this.destroy();
        }
      }).setTemplate(layoutTpl)

      // uninstalls the component
      .on('destroy', function () {
        // allows all registered shortcuts to be triggered and disables the dialog shortcuts
        globalShortcut.enable();
        if (this.dialogShortcut) {
          this.dialogShortcut.disable();
          this.dialogShortcut.clear();
          this.dialogShortcut = null;
        }
        if (this.navigator) {
          this.navigator.destroy();
          this.navigator = null;
        }
        this.getElement().removeClass('modal').modal('destroy');
      })

      // event triggered when the OK button has been clicked or the related shortcut has been used
      .on('action-ok', function () {
        this.validate();
      })

      // event triggered when the Cancel button has been clicked or the related shortcut has been used
      .on('action-cancel', function () {
        this.cancel();
      })

      // renders the component
      .on('render', function () {
        const $element = this.getElement();
        initModal({
          disableEscape: true,
          width: this.config.single && !this.config.deniedResources.length && !this.config.reason ? 600 : 800
        });
        if (_.isObject(this.config.categoriesSelector)) {
          const $reason = $element.find('.reason').children('.categories');
          this.config.categoriesSelector.render($reason);
        }
        $element.on(namespaceHelper.namespaceAll('selected.cascading-combobox', _ns), (e, reasons) => {
          state.reasons = reasons;
          if (this.config.allowShortcuts) {
            // ensure the keyboard navigation is taking care of the possible new fields
            initNavigator();
          }
          this.trigger('change', state);
        }).on(namespaceHelper.namespaceAll('change', _ns), 'textarea', e => {
          state.comment = $$1(e.currentTarget).val();
          this.trigger('change', state);
        }).on(namespaceHelper.namespaceAll('click', _ns), '.actions .done', e => {
          e.preventDefault();
          this.trigger('action-ok');
        }).on(namespaceHelper.namespaceAll('click', _ns), '.actions .cancel', e => {
          e.preventDefault();
          this.trigger('action-cancel');
        });
        if (this.config.allowShortcuts) {
          // install the keyboard navigation
          initNavigator();

          // prevents all registered shortcuts to be triggered and activate the dialog shortcuts
          globalShortcut.disable();
          this.dialogShortcut = shortcutRegistry($$1('body'), {
            avoidInput: true,
            propagate: false,
            prevent: true
          })
          // prevents the TAB key to be used to move outside the dialog box, but handles navigation
          .add(namespaceHelper.namespaceAll('Tab', _ns, true), () => {
            this.navigator.next();
          }, {
            avoidInput: false
          }).add(namespaceHelper.namespaceAll('Shift+Tab', _ns, true), () => {
            this.navigator.previous();
          }, {
            avoidInput: false
          })

          // handles the dialog's shortcuts: just fire the action using the event loop
          .add(namespaceHelper.namespaceAll('esc', _ns, true), function (e, shortcut) {
            instance.trigger('action-cancel', shortcut);
          }).add(namespaceHelper.namespaceAll('enter', _ns, true), function (e, shortcut) {
            instance.trigger('action-ok', shortcut);
          });
        }
      });

      /**
       * Validates that all required fields have been filled
       * @param {jQuery} $container
       * @returns {Boolean}
       */
      function checkRequiredFields($container) {
        return $$1('select, textarea', $container).filter(function () {
          return $$1.trim($$1(this).val()).length === 0;
        }).length === 0;
      }

      /**
       * Adds the form into a popup and displays it
       * @param {Object} modalConfig
       */
      function initModal(modalConfig) {
        instance.getElement().addClass('modal').on('closed.modal', function () {
          // always destroy the widget when closing
          instance.destroy();
        }).modal(modalConfig).focus();
      }

      /**
       * Sets a keyboard navigator on the dialog to take care of TAB navigation
       */
      function initNavigator() {
        const $element = instance.getElement();
        instance.navigator = keyNavigator({
          id: _ns,
          loop: true,
          group: $element,
          // the dialog is always set as first component, so ensure to start on the first field
          defaultPosition: 1,
          // take all input fields and the dialog as navigable components
          // but ignore buttons and select2 hidden fields
          elements: navigableDomElement.createFromDoms($element.find(':input:not(button,select.select2-offscreen)').add($element))
        }).on('activate', function (cursor, target) {
          if ($$1(target).is($element)) {
            instance.validate();
          }
        })

        // put the focus on the keyboard navigation, to ensure the first field is selected
        // otherwise the user will have to hit twice the tab key before selecting it
        .focus();
      }

      //compute extra config data (essentially for the template)
      return instance.init(_.defaults(config, {
        deniedResources: [],
        reason: false,
        allowShortcuts: true,
        reasonRequired: false,
        resourceCount: config.allowedResources.length,
        single: config.allowedResources.length === 1,
        singleDenied: config.deniedResources && config.deniedResources.length === 1,
        resourceTypes: `${config.resourceType}s`
      }));
    }

    return bulkActionPopupFactory;

});
