(function () {
  'use strict';

  function _defineProperties(target, props) {
    for (var i = 0; i < props.length; i++) {
      var descriptor = props[i];
      descriptor.enumerable = descriptor.enumerable || false;
      descriptor.configurable = true;
      if ("value" in descriptor) descriptor.writable = true;
      Object.defineProperty(target, descriptor.key, descriptor);
    }
  }

  function _createClass(Constructor, protoProps, staticProps) {
    if (protoProps) _defineProperties(Constructor.prototype, protoProps);
    if (staticProps) _defineProperties(Constructor, staticProps);
    Object.defineProperty(Constructor, "prototype", {
      writable: false
    });
    return Constructor;
  }

  function _inheritsLoose(subClass, superClass) {
    subClass.prototype = Object.create(superClass.prototype);
    subClass.prototype.constructor = subClass;

    _setPrototypeOf(subClass, superClass);
  }

  function _getPrototypeOf(o) {
    _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) {
      return o.__proto__ || Object.getPrototypeOf(o);
    };
    return _getPrototypeOf(o);
  }

  function _setPrototypeOf(o, p) {
    _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) {
      o.__proto__ = p;
      return o;
    };

    return _setPrototypeOf(o, p);
  }

  function _isNativeReflectConstruct() {
    if (typeof Reflect === "undefined" || !Reflect.construct) return false;
    if (Reflect.construct.sham) return false;
    if (typeof Proxy === "function") return true;

    try {
      Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {}));
      return true;
    } catch (e) {
      return false;
    }
  }

  function _construct(Parent, args, Class) {
    if (_isNativeReflectConstruct()) {
      _construct = Reflect.construct;
    } else {
      _construct = function _construct(Parent, args, Class) {
        var a = [null];
        a.push.apply(a, args);
        var Constructor = Function.bind.apply(Parent, a);
        var instance = new Constructor();
        if (Class) _setPrototypeOf(instance, Class.prototype);
        return instance;
      };
    }

    return _construct.apply(null, arguments);
  }

  function _isNativeFunction(fn) {
    return Function.toString.call(fn).indexOf("[native code]") !== -1;
  }

  function _wrapNativeSuper(Class) {
    var _cache = typeof Map === "function" ? new Map() : undefined;

    _wrapNativeSuper = function _wrapNativeSuper(Class) {
      if (Class === null || !_isNativeFunction(Class)) return Class;

      if (typeof Class !== "function") {
        throw new TypeError("Super expression must either be null or a function");
      }

      if (typeof _cache !== "undefined") {
        if (_cache.has(Class)) return _cache.get(Class);

        _cache.set(Class, Wrapper);
      }

      function Wrapper() {
        return _construct(Class, arguments, _getPrototypeOf(this).constructor);
      }

      Wrapper.prototype = Object.create(Class.prototype, {
        constructor: {
          value: Wrapper,
          enumerable: false,
          writable: true,
          configurable: true
        }
      });
      return _setPrototypeOf(Wrapper, Class);
    };

    return _wrapNativeSuper(Class);
  }

  /**
   * @copyright  (C) 2018 Open Source Matters, Inc. <https://www.joomla.org>
   * @license    GNU General Public License version 2 or later; see LICENSE.txt
   */

  /**
   * Fancy select field, which use Choices.js
   *
   * Example:
   * <joomla-field-fancy-select ...attributes>
   *   <select>...</select>
   * </joomla-field-fancy-select>
   *
   * Possible attributes:
   *
   * allow-custom          Whether allow User to dynamically add a new value.
   * new-item-prefix=""    Prefix for a dynamically added value.
   *
   * remote-search         Enable remote search.
   * url=""                Url for remote search.
   * term-key="term"       Variable key name for searched term, will be appended to Url.
   *
   * min-term-length="1"   The minimum length a search value should be before choices are searched.
   * placeholder=""        The value of the inputs placeholder.
   * search-placeholder="" The value of the search inputs placeholder.
   *
   * data-max-results="30" The maximum amount of search results to be displayed.
   * data-max-render="30"  The maximum amount of items to be rendered, critical for large lists.
   */
  window.customElements.define('joomla-field-fancy-select', /*#__PURE__*/function (_HTMLElement) {
    _inheritsLoose(_class, _HTMLElement);

    /**
     * Lifecycle
     */
    function _class() {
      var _this;

      _this = _HTMLElement.call(this) || this; // Keycodes

      _this.keyCode = {
        ENTER: 13
      };

      if (!Joomla) {
        throw new Error('Joomla API is not properly initiated');
      }

      if (!window.Choices) {
        throw new Error('JoomlaFieldFancySelect requires Choices.js to work');
      }

      _this.choicesCache = {};
      _this.activeXHR = null;
      _this.choicesInstance = null;
      _this.isDisconnected = false;
      return _this;
    }
    /**
     * Lifecycle
     */


    var _proto = _class.prototype;

    _proto.connectedCallback = function connectedCallback() {
      var _this2 = this;

      // Make sure Choices are loaded
      if (window.Choices || document.readyState === 'complete') {
        this.doConnect();
      } else {
        var callback = function callback() {
          _this2.doConnect();

          window.removeEventListener('load', callback);
        };

        window.addEventListener('load', callback);
      }
    };

    _proto.doConnect = function doConnect() {
      var _this3 = this;

      // Get a <select> element
      this.select = this.querySelector('select');

      if (!this.select) {
        throw new Error('JoomlaFieldFancySelect requires <select> element to work');
      } // The element was already initialised previously and perhaps was detached from DOM


      if (this.choicesInstance) {
        if (this.isDisconnected) {
          // Re init previous instance
          this.choicesInstance.init();
          this.isDisconnected = false;
        }

        return;
      }

      this.isDisconnected = false; // Add placeholder option for multiple mode,
      // Because it not supported as parameter by Choices for <select> https://github.com/jshjohnson/Choices#placeholder

      if (this.select.multiple && this.placeholder) {
        var option = document.createElement('option');
        option.setAttribute('placeholder', '');
        option.textContent = this.placeholder;
        this.select.appendChild(option);
      } // Init Choices
      // eslint-disable-next-line no-undef


      this.choicesInstance = new Choices(this.select, {
        placeholderValue: this.placeholder,
        searchPlaceholderValue: this.searchPlaceholder,
        removeItemButton: true,
        searchFloor: this.minTermLength,
        searchResultLimit: parseInt(this.select.dataset.maxResults, 10) || 10,
        renderChoiceLimit: parseInt(this.select.dataset.maxRender, 10) || -1,
        shouldSort: false,
        fuseOptions: {
          threshold: 0.3 // Strict search

        },
        noResultsText: Joomla.Text._('JGLOBAL_SELECT_NO_RESULTS_MATCH', 'No results found'),
        noChoicesText: Joomla.Text._('JGLOBAL_SELECT_NO_RESULTS_MATCH', 'No results found'),
        itemSelectText: Joomla.Text._('JGLOBAL_SELECT_PRESS_TO_SELECT', 'Press to select'),
        // Redefine some classes
        classNames: {
          button: 'choices__button_joomla' // It is need because an original styling use unavailable Icon.svg file

        }
      }); // Handle typing of custom Term

      if (this.allowCustom) {
        // START Work around for issue https://github.com/joomla/joomla-cms/issues/29459
        // The choices.js always auto-highlights the first element
        // in the dropdown that not allow to add a custom Term.
        //
        // This workaround can be removed when choices.js
        // will have an option that allow to disable it.
        // eslint-disable-next-line no-underscore-dangle, prefer-destructuring
        var _highlightChoice = this.choicesInstance._highlightChoice; // eslint-disable-next-line no-underscore-dangle

        this.choicesInstance._highlightChoice = function (el) {
          // Prevent auto-highlight of first element, if nothing actually highlighted
          if (!el) return; // Call original highlighter

          _highlightChoice.call(_this3.choicesInstance, el);
        }; // Unhighlight any highlighted items, when mouse leave the dropdown


        this.addEventListener('mouseleave', function () {
          if (!_this3.choicesInstance.dropdown.isActive) {
            return;
          }

          var highlighted = Array.from(_this3.choicesInstance.dropdown.element.querySelectorAll("." + _this3.choicesInstance.config.classNames.highlightedState));
          highlighted.forEach(function (choice) {
            choice.classList.remove(_this3.choicesInstance.config.classNames.highlightedState);
            choice.setAttribute('aria-selected', 'false');
          }); // eslint-disable-next-line no-underscore-dangle

          _this3.choicesInstance._highlightPosition = 0;
        }); // END workaround for issue #29459
        // Add custom term on ENTER keydown

        this.addEventListener('keydown', function (event) {
          if (event.keyCode !== _this3.keyCode.ENTER || event.target !== _this3.choicesInstance.input.element) {
            return;
          }

          event.preventDefault(); // eslint-disable-next-line no-underscore-dangle

          if (_this3.choicesInstance._highlightPosition || !event.target.value) {
            return;
          } // Make sure nothing is highlighted


          var highlighted = _this3.choicesInstance.dropdown.element.querySelector("." + _this3.choicesInstance.config.classNames.highlightedState);

          if (highlighted) {
            return;
          } // Check if value already exist


          var lowerValue = event.target.value.toLowerCase();
          var valueInCache = false; // Check if value in existing choices

          _this3.choicesInstance.config.choices.some(function (choiceItem) {
            if (choiceItem.value.toLowerCase() === lowerValue || choiceItem.label.toLowerCase() === lowerValue) {
              valueInCache = choiceItem.value;
              return true;
            }

            return false;
          });

          if (valueInCache === false) {
            // Check if value in cache
            Object.keys(_this3.choicesCache).some(function (key) {
              if (key.toLowerCase() === lowerValue || _this3.choicesCache[key].toLowerCase() === lowerValue) {
                valueInCache = key;
                return true;
              }

              return false;
            });
          } // Make choice based on existing value


          if (valueInCache !== false) {
            _this3.choicesInstance.setChoiceByValue(valueInCache);

            event.target.value = null;

            _this3.choicesInstance.hideDropdown();

            return;
          } // Create and add new


          _this3.choicesInstance.setChoices([{
            value: _this3.newItemPrefix + event.target.value,
            label: event.target.value,
            selected: true,
            customProperties: {
              value: event.target.value // Store real value, just in case

            }
          }], 'value', 'label', false);

          _this3.choicesCache[event.target.value] = event.target.value;
          event.target.value = null;

          _this3.choicesInstance.hideDropdown();
        });
      } // Handle remote search


      if (this.remoteSearch && this.url) {
        // Cache existing
        this.choicesInstance.config.choices.forEach(function (choiceItem) {
          _this3.choicesCache[choiceItem.value] = choiceItem.label;
        });
        var lookupDelay = 300;
        var lookupTimeout = null;
        this.select.addEventListener('search', function () {
          clearTimeout(lookupTimeout);
          lookupTimeout = setTimeout(_this3.requestLookup.bind(_this3), lookupDelay);
        });
      }
    }
    /**
     * Lifecycle
     */
    ;

    _proto.disconnectedCallback = function disconnectedCallback() {
      // Destroy Choices instance, to unbind event listeners
      if (this.choicesInstance) {
        this.choicesInstance.destroy();
        this.isDisconnected = true;
      }

      if (this.activeXHR) {
        this.activeXHR.abort();
        this.activeXHR = null;
      }
    };

    _proto.requestLookup = function requestLookup() {
      var _this4 = this;

      var url = this.url;
      url += url.indexOf('?') === -1 ? '?' : '&';
      url += encodeURIComponent(this.termKey) + "=" + encodeURIComponent(this.choicesInstance.input.value); // Stop previous request if any

      if (this.activeXHR) {
        this.activeXHR.abort();
      }

      this.activeXHR = Joomla.request({
        url: url,
        onSuccess: function onSuccess(response) {
          _this4.activeXHR = null;
          var items = response ? JSON.parse(response) : [];

          if (!items.length) {
            return;
          } // Remove duplications


          var item; // eslint-disable-next-line no-plusplus

          for (var i = items.length - 1; i >= 0; i--) {
            // The loop must be form the end !!!
            item = items[i]; // eslint-disable-next-line prefer-template

            item.value = '' + item.value; // Make sure the value is a string, choices.js expect a string.

            if (_this4.choicesCache[item.value]) {
              items.splice(i, 1);
            } else {
              _this4.choicesCache[item.value] = item.text;
            }
          } // Add new options to field, assume that each item is object, eg {value: "foo", text: "bar"}


          if (items.length) {
            _this4.choicesInstance.setChoices(items, 'value', 'text', false);
          }
        },
        onError: function onError() {
          _this4.activeXHR = null;
        }
      });
    };

    _proto.disableAllOptions = function disableAllOptions() {
      // Choices.js does not offer a public API for accessing the choices
      // So we have to access the private store => don't eslint
      // eslint-disable-next-line no-underscore-dangle
      var choices = this.choicesInstance._store.choices;
      choices.forEach(function (elem, index) {
        choices[index].disabled = true;
        choices[index].selected = false;
      });
      this.choicesInstance.clearStore();
      this.choicesInstance.setChoices(choices, 'value', 'label', true);
    };

    _proto.enableAllOptions = function enableAllOptions() {
      // Choices.js does not offer a public API for accessing the choices
      // So we have to access the private store => don't eslint
      // eslint-disable-next-line no-underscore-dangle
      var choices = this.choicesInstance._store.choices;
      var values = this.choicesInstance.getValue(true);
      choices.forEach(function (elem, index) {
        choices[index].disabled = false;
      });
      this.choicesInstance.clearStore();
      this.choicesInstance.setChoices(choices, 'value', 'label', true);
      this.value = values;
    };

    _proto.disableByValue = function disableByValue($val) {
      // Choices.js does not offer a public API for accessing the choices
      // So we have to access the private store => don't eslint
      // eslint-disable-next-line no-underscore-dangle
      var choices = this.choicesInstance._store.choices;
      var values = this.choicesInstance.getValue(true);
      choices.forEach(function (elem, index) {
        if (elem.value === $val) {
          choices[index].disabled = true;
          choices[index].selected = false;
        }
      });
      var index = values.indexOf($val);

      if (index > -1) {
        values.slice(index, 1);
      }

      this.choicesInstance.clearStore();
      this.choicesInstance.setChoices(choices, 'value', 'label', true);
      this.value = values;
    };

    _proto.enableByValue = function enableByValue($val) {
      // Choices.js does not offer a public API for accessing the choices
      // So we have to access the private store => don't eslint
      // eslint-disable-next-line no-underscore-dangle
      var choices = this.choicesInstance._store.choices;
      var values = this.choicesInstance.getValue(true);
      choices.forEach(function (elem, index) {
        if (elem.value === $val) {
          choices[index].disabled = false;
        }
      });
      this.choicesInstance.clearStore();
      this.choicesInstance.setChoices(choices, 'value', 'label', true);
      this.value = values;
    };

    _createClass(_class, [{
      key: "allowCustom",
      get: // Attributes to monitor
      function get() {
        return this.hasAttribute('allow-custom');
      }
    }, {
      key: "remoteSearch",
      get: function get() {
        return this.hasAttribute('remote-search');
      }
    }, {
      key: "url",
      get: function get() {
        return this.getAttribute('url');
      }
    }, {
      key: "termKey",
      get: function get() {
        return this.getAttribute('term-key') || 'term';
      }
    }, {
      key: "minTermLength",
      get: function get() {
        return parseInt(this.getAttribute('min-term-length'), 10) || 1;
      }
    }, {
      key: "newItemPrefix",
      get: function get() {
        return this.getAttribute('new-item-prefix') || '';
      }
    }, {
      key: "placeholder",
      get: function get() {
        return this.getAttribute('placeholder');
      }
    }, {
      key: "searchPlaceholder",
      get: function get() {
        return this.getAttribute('search-placeholder');
      }
    }, {
      key: "value",
      get: function get() {
        return this.choicesInstance.getValue(true);
      },
      set: function set($val) {
        this.choicesInstance.setChoiceByValue($val);
      }
    }]);

    return _class;
  }( /*#__PURE__*/_wrapNativeSuper(HTMLElement)));

})();
