define(['jquery', 'lodash', 'i18n', 'ui/component', 'ui/feedback', 'util/url', 'handlebars', 'lib/handlebars/helpers'], function ($$1, _, __, component, feedback, urlUtil, Handlebars, Helpers0) { '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'] : __;
    component = component && Object.prototype.hasOwnProperty.call(component, 'default') ? component['default'] : component;
    feedback = feedback && Object.prototype.hasOwnProperty.call(feedback, 'default') ? feedback['default'] : feedback;
    urlUtil = urlUtil && Object.prototype.hasOwnProperty.call(urlUtil, 'default') ? urlUtil['default'] : urlUtil;
    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) {
      
      var buffer = "", stack1;
      buffer += "\n            <div class=\"xhtml_form_error\">"
        + escapeExpression(((stack1 = ((stack1 = (depth0 && depth0.message)),stack1 == null || stack1 === false ? stack1 : stack1.error)),typeof stack1 === functionType ? stack1.apply(depth0) : stack1))
        + "</div>\n        ";
      return buffer;
      }

    function program3(depth0,data) {
      
      
      return "autofocus=\"autofocus\"";
      }

    function program5(depth0,data) {
      
      
      return " autocomplete=\"off\"";
      }

    function program7(depth0,data) {
      
      var buffer = "", stack1;
      buffer += "\n                <div class=\"form-error\">\n                    "
        + escapeExpression(((stack1 = ((stack1 = (depth0 && depth0.fieldMessages)),stack1 == null || stack1 === false ? stack1 : stack1.login)),typeof stack1 === functionType ? stack1.apply(depth0) : stack1))
        + "\n                </div>\n            ";
      return buffer;
      }

      buffer += "<div class=\"xhtml_form login-component\">\n    <form action=\"";
      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)
        + "\" method=\"post\" id=\"";
      if (helper = helpers.name) { stack1 = helper.call(depth0, {hash:{},data:data}); }
      else { helper = (depth0 && depth0.name); stack1 = typeof helper === functionType ? helper.call(depth0, {hash:{},data:data}) : helper; }
      buffer += escapeExpression(stack1)
        + "\" name=\"";
      if (helper = helpers.name) { stack1 = helper.call(depth0, {hash:{},data:data}); }
      else { helper = (depth0 && depth0.name); stack1 = typeof helper === functionType ? helper.call(depth0, {hash:{},data:data}) : helper; }
      buffer += escapeExpression(stack1)
        + "\">\n        <input type=\"hidden\" class=\"global\" name=\"";
      if (helper = helpers.name) { stack1 = helper.call(depth0, {hash:{},data:data}); }
      else { helper = (depth0 && depth0.name); stack1 = typeof helper === functionType ? helper.call(depth0, {hash:{},data:data}) : helper; }
      buffer += escapeExpression(stack1)
        + "_sent\" value=\"1\">\n        ";
      stack1 = helpers['if'].call(depth0, ((stack1 = (depth0 && depth0.message)),stack1 == null || stack1 === false ? stack1 : stack1.error), {hash:{},inverse:self.noop,fn:self.program(1, program1, data),data:data});
      if(stack1 || stack1 === 0) { buffer += stack1; }
      buffer += "\n        <div>\n            <label class=\"form_desc\" for=\"login\">\n                "
        + escapeExpression((helper = helpers.__ || (depth0 && depth0.__),options={hash:{},data:data},helper ? helper.call(depth0, "Login", options) : helperMissing.call(depth0, "__", "Login", options)))
        + "\n            </label><input type=\"text\" name=\"login\" id=\"login\" ";
      stack1 = helpers.unless.call(depth0, (depth0 && depth0.disableAutofocus), {hash:{},inverse:self.noop,fn:self.program(3, program3, data),data:data});
      if(stack1 || stack1 === 0) { buffer += stack1; }
      buffer += " ";
      stack1 = helpers['if'].call(depth0, (depth0 && depth0.disableAutocomplete), {hash:{},inverse:self.noop,fn:self.program(5, program5, data),data:data});
      if(stack1 || stack1 === 0) { buffer += stack1; }
      buffer += " aria-required=\"true\">\n            ";
      stack1 = helpers['if'].call(depth0, ((stack1 = (depth0 && depth0.fieldMessages)),stack1 == null || stack1 === false ? stack1 : stack1.login), {hash:{},inverse:self.noop,fn:self.program(7, program7, data),data:data});
      if(stack1 || stack1 === 0) { buffer += stack1; }
      buffer += "\n        </div>\n        <div>\n            <label class=\"form_desc\" for=\"password\">\n                "
        + escapeExpression((helper = helpers.__ || (depth0 && depth0.__),options={hash:{},data:data},helper ? helper.call(depth0, "Password", options) : helperMissing.call(depth0, "__", "Password", options)))
        + "\n            </label><input type=\"password\" name=\"password\" id=\"password\"";
      stack1 = helpers['if'].call(depth0, (depth0 && depth0.disableAutocomplete), {hash:{},inverse:self.noop,fn:self.program(5, program5, data),data:data});
      if(stack1 || stack1 === 0) { buffer += stack1; }
      buffer += " aria-required=\"true\">\n            ";
      stack1 = helpers['if'].call(depth0, ((stack1 = (depth0 && depth0.fieldMessages)),stack1 == null || stack1 === false ? stack1 : stack1.password), {hash:{},inverse:self.noop,fn:self.program(7, program7, data),data:data});
      if(stack1 || stack1 === 0) { buffer += stack1; }
      buffer += "\n        </div>\n        <div class=\"form-toolbar\"><input type=\"submit\" id=\"connect\" name=\"connect\" disabled=\"disabled\" class=\"disabled\" value=\""
        + escapeExpression((helper = helpers.__ || (depth0 && depth0.__),options={hash:{},data:data},helper ? helper.call(depth0, "Log in", options) : helperMissing.call(depth0, "__", "Log in", options)))
        + "\"></div>\n    </form>\n</div>\n";
      return buffer;
      });
    function loginTpl(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, helper, functionType="function";


      buffer += "<div class=\"form loginForm fakeForm\">";
      if (helper = helpers.form) { stack1 = helper.call(depth0, {hash:{},data:data}); }
      else { helper = (depth0 && depth0.form); stack1 = typeof helper === functionType ? helper.call(depth0, {hash:{},data:data}) : helper; }
      if(stack1 || stack1 === 0) { buffer += stack1; }
      buffer += "</div>";
      return buffer;
      });
    function fakeFormTpl(data, options, asString) {
      var html = Template$1(data, options);
      return (asString || true) ? html : $(html);
    }

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


      buffer += "<span class=\"viewable-hiddenbox\">\n    ";
      if (helper = helpers.elements) { stack1 = helper.call(depth0, {hash:{},data:data}); }
      else { helper = (depth0 && depth0.elements); stack1 = typeof helper === functionType ? helper.call(depth0, {hash:{},data:data}) : helper; }
      if(stack1 || stack1 === 0) { buffer += stack1; }
      buffer += "\n    <span class=\"viewable-hiddenbox-toggle\">\n        <span aria-label=\""
        + escapeExpression((helper = helpers.__ || (depth0 && depth0.__),options={hash:{},data:data},helper ? helper.call(depth0, "Show password", options) : helperMissing.call(depth0, "__", "Show password", options)))
        + "\" class=\"icon-preview\" role=\"button\" tabindex=\"0\"></span>\n        <span aria-label=\""
        + escapeExpression((helper = helpers.__ || (depth0 && depth0.__),options={hash:{},data:data},helper ? helper.call(depth0, "Hide password", options) : helperMissing.call(depth0, "__", "Hide password", options)))
        + "\" class=\"icon-eye-slash\" style=\"display: none;\" role=\"button\" tabindex=\"0\"></span>\n    </span>\n</span>\n";
      return buffer;
      });
    function pwdRevealTpl(data, options, asString) {
      var html = Template$2(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) 2019 (original work) Open Assessment Technologies SA (under the project TAO-PRODUCT);
     */
    var _defaultConfig = {
      disableAutocomplete: false,
      enablePasswordReveal: false,
      disableAutofocus: false,
      message: {
        error: '',
        info: null
      },
      fieldMessages: {},
      name: 'loginForm',
      url: urlUtil.route('login', 'Main', 'tao')
    };

    /**
     * The factory that creates a login component
     *
     * @param {jQueryElement} $container - where to append the component
     * @param {Object} config - the component config
     * @param {Object} [config.disableAutocomplete] - depending on this setting autocomplete would be disabled or enabled (and fakeForm rendered)
     * @param {Object} [config.enablePasswordReveal] - depending on this setting password reveal would be disabled or enabled for the password field
     * @param {Object} [config.disableAutofocus] - depending on this setting autofocus attribute will be added to login filed
     * @param {Object} [config.fieldMessages] - field validation messages
     * @param {String} [config.name] - the component name (used by the element)
     * @param {String} [config.url] - the url to send login form to.
     * @param {String} [config.message] - the form wide error|info messages
     * @returns {loginComponent} the component
     */
    function loginFactory($container, config) {
      /**
       * The component API
       */
      var api = {
        /**
         * Returns whether autocomplete is disabled or not
         * @returns {boolean}
         */
        isAutocompleteDisabled: function isAutocompleteDisabled() {
          return this.config.disableAutocomplete;
        },
        /**
         * Returns whether password reveal is enabled or not
         * @returns {boolean}
         */
        isPasswordRevealEnabled: function isPasswordRevealEnabled() {
          return this.config.enablePasswordReveal;
        },
        /**
         * Get messages from config
         * @returns {Object} Object containing passed messages
         */
        getMessages: function getMessages() {
          return this.config.message;
        },
        /**
         * Returns form fields validation messages, if any
         * @returns {Object} fieldMessages
         */
        getFieldMessages: function getFieldMessages() {
          return this.config.fieldMessages;
        },
        /**
         * Creates fakeForm from the real form
         * @returns {jQuery} jQuery element
         */
        createFakeForm: function createFakeForm() {
          const $element = this.getElement();
          const $fakeFormDom = $element.clone();
          $element.find('label').remove();
          return $fakeFormDom.html(fakeFormTpl({
            form: $fakeFormDom.find('form').html()
          }));
        },
        /**
         * Gets real form jQuery element
         * @returns {jQuery} jQuery element
         */
        getRealForm: function getRealForm() {
          return this.getElement().find('form');
        },
        /**
         * Gets fake form jQuery element
         * @returns {jQuery} jQuery element
         */
        getFakeForm: function getFakeForm() {
          return this.getContainer().find('div.fakeForm');
        },
        /**
         * Gets real or fake form jQuery element, depending on the disableAutocomplete setting
         * @returns {jQuery} jQuery element
         */
        getForm: function getForm() {
          return this.isAutocompleteDisabled() ? this.getFakeForm() : this.getRealForm();
        },
        /**
         * Manipulates form dom (adds password reveal elements)
         */
        manipulateFormDom: function manipulateFormDom() {
          var $form, $pwdInput, $pwdLabel;
          $form = this.getForm();
          $pwdInput = $form.find('input[type=password]');
          $pwdLabel = $form.find('label[for=' + $pwdInput.attr('name') + ']');
          $pwdInput.replaceWith(pwdRevealTpl({
            elements: $pwdLabel[0].outerHTML + $pwdInput[0].outerHTML
          }));
          $pwdLabel.remove();
        },
        /**
         * Attaches events to password reveal options
         */
        attachPasswordRevealEvents: function attachPasswordRevealEvents() {
          var $form, $pwdInput, $inputToggle, $viewIcon, $hideIcon;
          const self = this;
          const autoHide = function autoHide(event) {
            if (!event.target.isSameNode($pwdInput) && !event.target.isSameNode($hideIcon[0]) && !event.target.isSameNode($inputToggle[0])) {
              hide();
            }
          };
          function show() {
            $viewIcon.hide();
            $hideIcon.show();
            $pwdInput.type = 'text';
            $pwdInput.autocomplete = 'off';
            window.addEventListener('mousedown', autoHide);
            $pwdInput.focus();
          }
          function hide(moveFocus) {
            $hideIcon.hide();
            $viewIcon.show();
            $pwdInput.type = 'password';
            $pwdInput.autocomplete = self.isAutocompleteDisabled() ? 'off' : 'on';
            window.removeEventListener('mousedown', autoHide);
            if (moveFocus) {
              $pwdInput.focus();
            }
          }
          const togglePassword = function togglePassword() {
            if ($pwdInput.type === 'password') {
              show();
            } else {
              hide(true);
            }
          };
          $form = this.getForm();
          $pwdInput = $form.find('input[type=password]')[0];
          $inputToggle = $form.find('.viewable-hiddenbox-toggle');
          $viewIcon = $form.find('span.icon-preview');
          $hideIcon = $form.find('span.icon-eye-slash');
          hide();
          $inputToggle.on('keyup', function (e) {
            if (e.key === ' ') {
              togglePassword();
            }
          });
          $inputToggle.on('keypress', function (e) {
            if (e.key === 'Enter') {
              togglePassword();
              e.stopPropagation();
            }
          });
          $inputToggle.on('click', togglePassword);
        },
        /**
         * Displays the error/info messages
         */
        displayMessages: function displayMessages(messages) {
          var $fields = this.getForm().find(':input');
          if (!messages.error && this.getForm().find('.form-error').length) {
            messages.error = __('All fields are required');
          }
          _.forEach(messages, function (message, level) {
            if (message) {
              feedback().message(level, message).open();
              $fields.addClass(level);
            }
          });
        }
      };
      var loginComponent = component(api, _defaultConfig).setTemplate(loginTpl).on('init', function () {
        this.render($container);
      }).on('render', function () {
        var $fakeForm, $loginBtn;
        var $loginForm = this.getRealForm();
        var self = this;

        /**
         * Submits the form after a copy of all the inputs the user has made in the fake form
         * @fires submit event when real form gots submitted
         */
        var submitForm = function submitForm() {
          // if the fake form exists, copy all fields values into the real form
          $fakeForm.find(':input').each(function () {
            var $field = $$1(this);
            $loginForm.find('input[name="' + $field.attr('name') + '"]').val($field.val());
          });

          // just submit the real form as if the user did it
          self.trigger('submit');
          $loginForm.submit();
        };

        /**
         * Create fake form and attach event handlers if autocomplete is disabled
         */
        if (this.isAutocompleteDisabled()) {
          $fakeForm = this.createFakeForm();
          this.hide();
          this.getElement().find('form').attr('id', 'loginForm').attr('aria-hidden', 'true');
          this.getContainer().prepend($fakeForm);

          // submit the form when the user hit the submit button inside the fake form
          $fakeForm.find('input[type="submit"], button[type="submit"]').off('click').on('click', function (e) {
            e.preventDefault();
            submitForm();
          });

          // submit the form when the user hit the ENTER key inside the fake form
          $fakeForm.on('keypress', function (e) {
            if (e.key === 'Enter') {
              e.preventDefault();
              submitForm();
            }
          });
        }

        /**
         * Attach elements for password revealing and attach event handlers
         */
        if (this.isPasswordRevealEnabled()) {
          this.manipulateFormDom();
          this.attachPasswordRevealEvents();
        }
        $loginBtn = this.getForm().find('[name=connect]');
        $loginBtn.removeAttr('disabled').removeClass('disabled');
        this.displayMessages(this.getMessages());
      });
      _.defer(function () {
        loginComponent.init(config);
      });
      return loginComponent;
    }

    return loginFactory;

});
