'use strict';

Liferay.Loader.define("frontend-taglib-clay$clay-multi-select@2.24.1/lib/ClayMultiSelect", ['module', 'exports', 'require', 'frontend-taglib-clay$clay-autocomplete', 'frontend-taglib-clay$clay-button', 'frontend-taglib-clay$clay-label', 'frontend-js-metal-web$metal-state', 'frontend-js-metal-web$metal-events', './htmlEscape', 'frontend-taglib-clay$clay-component', 'frontend-js-metal-web$metal-web-component', 'frontend-js-metal-web$metal-soy', './ClayMultiSelect.soy'], function (module, exports, require) {
	var define = undefined;
	var global = window;
	{
		Object.defineProperty(exports, "__esModule", {
			value: true
		});
		exports.ClayMultiSelect = undefined;

		var _createClass = function () {
			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);
				}
			}return function (Constructor, protoProps, staticProps) {
				if (protoProps) defineProperties(Constructor.prototype, protoProps);if (staticProps) defineProperties(Constructor, staticProps);return Constructor;
			};
		}();

		require("frontend-taglib-clay$clay-autocomplete");

		require("frontend-taglib-clay$clay-button");

		require("frontend-taglib-clay$clay-label");

		var _metalState = require("frontend-js-metal-web$metal-state");

		var _metalEvents = require("frontend-js-metal-web$metal-events");

		var _htmlEscape = require('./htmlEscape');

		var _clayComponent = require("frontend-taglib-clay$clay-component");

		var _clayComponent2 = _interopRequireDefault(_clayComponent);

		var _metalWebComponent = require("frontend-js-metal-web$metal-web-component");

		var _metalWebComponent2 = _interopRequireDefault(_metalWebComponent);

		var _metalSoy = require("frontend-js-metal-web$metal-soy");

		var _metalSoy2 = _interopRequireDefault(_metalSoy);

		var _ClayMultiSelectSoy = require("./ClayMultiSelect.soy");

		var _ClayMultiSelectSoy2 = _interopRequireDefault(_ClayMultiSelectSoy);

		function _interopRequireDefault(obj) {
			return obj && obj.__esModule ? obj : { default: obj };
		}

		function _classCallCheck(instance, Constructor) {
			if (!(instance instanceof Constructor)) {
				throw new TypeError("Cannot call a class as a function");
			}
		}

		function _possibleConstructorReturn(self, call) {
			if (!self) {
				throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
			}return call && (typeof call === "object" || typeof call === "function") ? call : self;
		}

		function _inherits(subClass, superClass) {
			if (typeof superClass !== "function" && superClass !== null) {
				throw new TypeError("Super expression must either be null or a function, not " + typeof superClass);
			}subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } });if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
		}

		/**
   * Metal ClayMultiSelect component.
   * @extends ClayComponent
   */
		var ClayMultiSelect = function (_ClayComponent) {
			_inherits(ClayMultiSelect, _ClayComponent);

			function ClayMultiSelect() {
				_classCallCheck(this, ClayMultiSelect);

				return _possibleConstructorReturn(this, (ClayMultiSelect.__proto__ || Object.getPrototypeOf(ClayMultiSelect)).apply(this, arguments));
			}

			_createClass(ClayMultiSelect, [{
				key: 'created',

				/**
     * @inheritDoc
     */
				value: function created() {
					this._itemFocused = null;
					this._eventHandler = new _metalEvents.EventHandler();
				}

				/**
     * @inheritDoc
     */

			}, {
				key: 'attached',
				value: function attached() {
					this.addListener('autocompleteItemSelected', this._defaultAutocompleteItemSelected, true);
					this.addListener('inputBlur', this._defaultInputBlur, true);
					this.addListener('inputChange', this._defaultInputChange, true);
					this.addListener('inputFocus', this._defaultInputFocus, true);
					this.addListener('labelItemAdded', this._defaultLabelItemAdded, true);
					this.addListener('labelItemRemoved', this._defaultLabelItemRemoved, true);
					this.addListener('labelItemCloseButtonClick', this._defaultLabelItemCloseButtonClick, true);
					this.addListener('labelItemKeyDown', this._defaultLabelItemKeyDown, true);
				}

				/**
     * @inheritDoc
     */

			}, {
				key: 'detached',
				value: function detached() {
					this._eventHandler.removeAllListeners();
				}

				/**
     * @inheritDoc
     */

			}, {
				key: 'disposed',
				value: function disposed() {
					this._eventHandler.removeAllListeners();
					this._itemFocused = null;
				}

				/**
     * Handles the autocomplete selected item event.
     * @param {!Event} event
     * @protected
     */

			}, {
				key: '_defaultAutocompleteItemSelected',
				value: function _defaultAutocompleteItemSelected(event) {
					var _event$data$item = event.data.item,
					    label = _event$data$item.label,
					    value = _event$data$item.value;

					var item = this._createItemObject((0, _htmlEscape.htmlEscape)(label), (0, _htmlEscape.htmlEscape)(value));

					this._addLabelItem(item);
				}

				/**
     *
     * @protected
     */

			}, {
				key: '_defaultInputBlur',
				value: function _defaultInputBlur() {
					if (!this.filteredItems || this.filteredItems && this.filteredItems.length === 0) {
						this._inputFocused = false;
						this.inputValue = '';
					}
				}

				/**
     *
     * @param {!Event} event
     * @protected
     */

			}, {
				key: '_defaultInputChange',
				value: function _defaultInputChange(event) {
					var _this2 = this;

					var char = event.data.char;
					var inputValue = event.data.value;
					var words = this._getWords(inputValue);

					if (char === ',' || words.length > 1) {
						words.forEach(function (word) {
							var added = _this2._addLabelItem(_this2._createItemObject((0, _htmlEscape.htmlEscape)(word)));

							inputValue = inputValue.replace(word + ',', added ? '' : word);
						});
					}

					this.inputValue = inputValue;
				}

				/**
     *
     * @protected
     */

			}, {
				key: '_defaultInputFocus',
				value: function _defaultInputFocus() {
					this._inputFocused = true;
				}

				/**
     *
     * @protected
     */

			}, {
				key: '_defaultLabelItemAdded',
				value: function _defaultLabelItemAdded() {
					this.inputValue = '';
					this._inputFocused = true;
				}

				/**
     * Handles the click on the close label item button.
     * @param {!Event} event
     * @protected
     */

			}, {
				key: '_defaultLabelItemCloseButtonClick',
				value: function _defaultLabelItemCloseButtonClick(event) {
					this._removeLabelItem(event.data.item);
				}

				/**
     * Handles the click on the close label item button.
     * @param {!Event} event
     * @protected
     */

			}, {
				key: '_defaultLabelItemKeyDown',
				value: function _defaultLabelItemKeyDown(event) {
					if (event.data.key == 'Backspace' || event.data.key == 'Enter') {
						event.preventDefault();

						this._removeLabelItem(event.data.element);
					}
				}

				/**
     * Focus the input after a labelItem is removed.
     * @protected
     */

			}, {
				key: '_defaultLabelItemRemoved',
				value: function _defaultLabelItemRemoved() {
					this.inputValue = '';
					this._inputFocused = true;
				}

				/**
     * Continues the propagation of the autocomplete item selected event.
     * @param {!Event} event
     * @protected
     * @return {?Boolean} If the event has been prevented or not.
     */

			}, {
				key: '_handleAutocompleteItemSelected',
				value: function _handleAutocompleteItemSelected(event) {
					return !this.emit({
						data: event.data,
						name: 'autocompleteItemSelected',
						originalEvent: event
					});
				}

				/**
     * Continues the propagation of the data change event
     * @param {!Event} event
     * @protected
     * @return {Boolean} If the event has been prevented or not.
     */

			}, {
				key: '_handleDataChange',
				value: function _handleDataChange(event) {
					return !this.emit({
						data: event.data,
						name: 'dataChange',
						originalEvent: event
					});
				}

				/**
     * Continues the propagation of the filteredItemsChanged event
     * @param {!Event} event
     * @protected
     */

			}, {
				key: '_handleFilteredItemsChange',
				value: function _handleFilteredItemsChange(event) {
					if (event.newVal !== event.prevVal) {
						this.filteredItems = event.newVal;
					}
				}

				/**
     * Continues the propagation of the the input blur event.
     * @param {!Event} event
     * @protected
     * @return {?Boolean} If the event has been prevented or not.
     */

			}, {
				key: '_handleInputBlur',
				value: function _handleInputBlur(event) {
					return !this.emit({
						data: event.data,
						name: 'inputBlur',
						originalEvent: event
					});
				}

				/**
     * Continues the propagation of the the input change event.
     * @param {!Event} event
     * @protected
     * @return {Boolean} If the event has been prevented or not.
     */

			}, {
				key: '_handleInputChange',
				value: function _handleInputChange(event) {
					return !this.emit({
						data: event.data,
						name: 'inputChange',
						originalEvent: event
					});
				}

				/**
     * Handles form interactions and propagates corresponding events.
     * @param {!Event} event
     * @protected
     * @return {Boolean} If the event has been prevented or not.
     */

			}, {
				key: '_handleInputKeyDown',
				value: function _handleInputKeyDown(event) {
					var _event$data = event.data,
					    element = _event$data.element,
					    key = _event$data.key;

					switch (key) {
						case 'Backspace':
							// Prevents page from returning when input is empty.
							// See: https://support.mozilla.org/en-US/questions/1057630
							if (!element.value) {
								event.preventDefault();
								return this._focusLastLabelItem();
							}
							break;

						case 'Enter':
							event.preventDefault();

							if (element.value) {
								var value = element.value;

								this._addLabelItem(this._createItemObject((0, _htmlEscape.htmlEscape)(value)));
							}
							break;
					}
				}

				/**
     * Continues the propagation of the the input focus event.
     * @param {!Event} event
     * @protected
     * @return {Boolean} If the event has been prevented or not.
     */

			}, {
				key: '_handleInputFocus',
				value: function _handleInputFocus(event) {
					return !this.emit({
						data: event.data,
						name: 'inputFocus',
						originalEvent: event
					});
				}

				/**
     * Continues the propagation of the the autocomplete label item click event.
     * @param {!Event} event
     * @protected
     * @return {?Boolean} If the event has been prevented or not.
     */

			}, {
				key: '_handleLabelItemClick',
				value: function _handleLabelItemClick(event) {
					return !this.emit({
						data: event.data,
						name: 'labelItemClick',
						originalEvent: event
					});
				}

				/**
     * Continues the propagation of the item close event.
     * @param {!Event} event
     * @protected
     * @return {Boolean} If the event has been prevented or not.
     */

			}, {
				key: '_handleLabelItemCloseButtonClick',
				value: function _handleLabelItemCloseButtonClick(event) {
					return !this.emit({
						data: Object.assign(event.data || {}, {
							item: event.target.element
						}),
						name: 'labelItemCloseButtonClick',
						originalEvent: event
					});
				}

				/**
     * Continues the propagation of the item focused event.
     * @param {!Event} event
     * @protected
     * @return {Boolean} If the event has been prevented or not.
     */

			}, {
				key: '_handleLabelItemKeyDown',
				value: function _handleLabelItemKeyDown(event) {
					return !this.emit({
						data: {
							element: event.currentTarget,
							key: event.key
						},
						name: 'labelItemKeyDown',
						originalEvent: event
					});
				}

				/**
     * Continues the propagation of the select button clicked event.
     * @param {!Event} event
     * @protected
     * @return {Boolean} If the event has been prevented or not.
     */

			}, {
				key: '_handleSelectButtonClicked',
				value: function _handleSelectButtonClicked(event) {
					return !this.emit({
						name: 'selectButtonClick',
						originalEvent: event
					});
				}

				/**
     * Continues the propagation of the itemAdded event.
     * @param {!Object} item
     * @protected
     * @return {?Boolean} If the item has been added or not.
     */

			}, {
				key: '_addLabelItem',
				value: function _addLabelItem(item) {
					var _item = item,
					    label = _item.label,
					    value = _item.value;

					var filteredItem = this.filteredItems.filter(function (filteredItem) {
						return (0, _htmlEscape.htmlEscape)(filteredItem.data.label.toLowerCase()) === label.toLowerCase();
					}).map(function (filteredItem) {
						return {
							label: filteredItem.data.label,
							value: filteredItem.data.value
						};
					})[0];

					if (!this.creatable) {
						item = filteredItem;
					} else {
						item = filteredItem || item;
					}

					var itemIsSelected = item && this._isItemSelected(item);

					if (item && value && !itemIsSelected) {
						var newSelectedItems = this.selectedItems.map(function (item) {
							return item;
						});
						newSelectedItems.push(item);
						this.selectedItems = newSelectedItems;

						this.filteredItems = [];

						this.emit({
							data: {
								item: item,
								selectedItems: this.selectedItems
							},
							name: 'labelItemAdded'
						});

						return true;
					} else {
						this.emit({
							data: {
								itemIsSelected: itemIsSelected,
								itemDoesNotExists: !this.creatable && !item
							},
							name: 'errorAddinglabelItem'
						});

						return false;
					}
				}

				/**
     * Assemble the schema of the item.
     * If only label is passed use it as value
     * @param {!string} label
     * @param {!string} [value=label]
     * @protected
     * @return {!Object}
     */

			}, {
				key: '_createItemObject',
				value: function _createItemObject(label, value) {
					label = label.trim();
					value = typeof value !== 'undefined' ? value.trim() : label;

					return { label: label, value: value };
				}

				/**
     * Focus a label item and emits the itemFocused event.
     * @param {!Element} item
     * @protected
     * @return {Boolean} If the event has been prevented or not.
     */

			}, {
				key: '_focusLabelItem',
				value: function _focusLabelItem(item) {
					if (item) {
						item.focus();

						return !this.emit({
							data: {
								item: item
							},
							name: 'labelItemFocused'
						});
					}
				}

				/**
     * Focus the last label item.
     * @param {!Element} item
     * @protected
     * @return {Boolean} If the event has been prevented or not.
     */

			}, {
				key: '_focusLastLabelItem',
				value: function _focusLastLabelItem() {
					var labelItems = this._getLabelItems();

					return this._focusLabelItem(labelItems[labelItems.length - 1]);
				}

				/**
     * Returns all the label items present in the autocomplete
     * @protected
     * @return {NodeList} The list of label items nodes
     */

			}, {
				key: '_getLabelItems',
				value: function _getLabelItems() {
					var labelItems = [];

					if (this.selectedItems.length > 0) {
						var autocomplete = this.refs.autocomplete;

						labelItems = autocomplete.element.querySelectorAll('span[id="item-tag"]');
					}

					return labelItems;
				}

				/**
     * Analyze the string and separate the values that
     * get a comma at the end.
     * @param {!string} string
     * @return {Array}
     */

			}, {
				key: '_getWords',
				value: function _getWords(string) {
					var hasComma = string.includes(',');

					if (hasComma) {
						var words = string.split(',').filter(Boolean);

						return words;
					}

					return [];
				}

				/**
     * @param {!Object} item
     * @return {Boolean} If the item is already selected.
     */

			}, {
				key: '_isItemSelected',
				value: function _isItemSelected(item) {
					return Boolean(this.selectedItems.find(function (selectedItem) {
						return selectedItem.label.toLowerCase() === item.label.toLowerCase() && selectedItem.value.toLowerCase() === item.value.toLowerCase();
					}));
				}

				/**
     * Removes an item and emits the 'itemRoved' event.
     * @param {!Element} element
     * @protected
     */

			}, {
				key: '_removeLabelItem',
				value: function _removeLabelItem(element) {
					var index = Number(element.getAttribute('data-tag'));
					var item = this.selectedItems[index];

					var newSelectedItems = this.selectedItems.map(function (item) {
						return item;
					});
					newSelectedItems.splice(index, 1);

					this.selectedItems = newSelectedItems;

					this.emit({
						data: {
							item: item,
							selectedItems: this.selectedItems
						},
						name: 'labelItemRemoved'
					});
				}
			}]);

			return ClayMultiSelect;
		}(_clayComponent2.default);

		/**
   * State definition.
   * @static
   * @type {!Object}
   */

		ClayMultiSelect.STATE = {
			/**
    * Internal flag to indicate that if the input is focused.
    * @instance
    * @default false
    * @memberof ClayMultiSelect
    * @type {?bool}
    */
			_inputFocused: _metalState.Config.bool().value(false).internal(),

			/**
    * Method or string as condition to filter items in autocomplete.
    * @instance
    * @default (elem) => elem
    * @memberof ClayMultiSelect
    * @type {?(function|string)}
    */
			autocompleteFilterCondition: _metalState.Config.oneOfType([_metalState.Config.func(), _metalState.Config.string()]).value(function (elem) {
				return elem;
			}),

			/**
    * Variation name to render different deltemplates.
    * @default undefined
    * @instance
    * @memberof ClayMultiSelect
    * @type {?(string|undefined)}
    */
			contentRenderer: _metalState.Config.string(),

			/**
    * Flag to indicate if only items from autocomplete should be accepted.
    * @default true
    * @instance
    * @memberof ClayMultiSelect
    * @type {?bool}
    */
			creatable: _metalState.Config.bool().value(true),

			/**
    * Data to add to the element.
    * @default undefined
    * @instance
    * @memberof ClayMultiSelect
    * @type {?object}
    */
			data: _metalState.Config.object(),

			/**
    * Set the request debounce time
    * @instance
    * @default 200
    * @memberof ClayMultiSelect
    * @type {?(number)}
    */
			dataProviderDebounceTime: _metalState.Config.number().value(200),

			/**
    * Set some initial data while the first request is being made
    * @instance
    * @default undefined
    * @memberof ClayMultiSelect
    * @type {?(object|array)}
    */
			dataProviderInitialData: _metalState.Config.oneOfType([_metalState.Config.object(), _metalState.Config.array()]),

			/**
    * The array of data items that the data source contains,
    * the URL for the data provider to request, or a function
    * that receives the query and returns a promise with the
    * elements.
    * @instance
    * @default undefined
    * @memberof ClayMultiSelect
    * @type {!(string|object|array|function)}
    */
			dataSource: _metalState.Config.oneOfType([_metalState.Config.array(), _metalState.Config.func(), _metalState.Config.object(), _metalState.Config.string()]),

			/**
    * Object that wires events with default listeners
    * @default undefined
    * @instance
    * @memberof ClayMultiSelect
    * @review
    * @type {?(object|undefined)}
    */
			defaultEventHandler: _metalState.Config.object(),

			/**
    * Element selector to render the autocomplete dropdown in.
    * @default undefined
    * @instance
    * @memberof ClayMultiSelect
    * @review
    * @type {?(object|undefined)}
    */
			dropdownPortalElement: _metalState.Config.string(),

			/**
    * CSS classes to be applied to the element.
    * @default undefined
    * @instance
    * @memberof ClayMultiSelect
    * @type {?(string|undefined)}
    */
			elementClasses: _metalState.Config.string(),

			/**
    * Flag to enable the filtered items with autocomplete default.
    * @default true
    * @instance
    * @memberof ClayMultiSelect
    * @type {?bool}
    */
			enableAutocomplete: _metalState.Config.validator(function (value) {
				if (value) {
					console.warn('🚨 `enableAutocomplete` has been deprecated and will be removed in the next major version.');
				}
			}),

			/**
    * List of filtered items for suggestion or autocomplete.
    * @default []
    * @instance
    * @memberof ClayMultiSelect
    * @type {?Array}
    */
			filteredItems: _metalState.Config.array(_metalState.Config.object()).value([]),

			/**
    * Help text to guide the user in the interaction.
    * @default undefined
    * @instance
    * @memberof ClayMultiSelect
    * @type {!string}
    */
			helpText: _metalState.Config.string().required(),

			/**
    * Id to be applied to the element.
    * @default undefined
    * @instance
    * @memberof ClayMultiSelect
    * @type {?(string|undefined)}
    */
			id: _metalState.Config.string(),

			/**
    * Characters allowed in the input element (e.g /[a-zA-Z0-9_]/g).
    * @default undefined
    * @instance
    * @memberof ClayMultiSelect
    * @type {?RegExp}
    */
			inputAllowedCharacters: _metalState.Config.instanceOf(RegExp),

			/**
    * Name for each selected item input hidden.
    * @default selectedItems
    * @instance
    * @memberof ClayMultiSelect
    * @type {?(string|undefined)}
    */
			inputName: _metalState.Config.string().value('selectedItems'),

			/*
    * Value of the input.
    * @default undefined
    * @instance
    * @memberof ClayMultiSelect
    * @private
    * @type {?(string|undefined)}
    */
			inputValue: _metalState.Config.string(),

			/**
    * Label of the input element.
    * @default undefined
    * @instance
    * @memberof ClayMultiSelect
    * @type {?(string|undefined)}
    */
			label: _metalState.Config.string(),

			/**
    * Sets the name of the field to map the label of the item.
    * @default label
    * @instance
    * @memberof ClayMultiSelect
    * @type {?(function|string)}
    */
			labelLocator: _metalState.Config.oneOfType([_metalState.Config.func(), _metalState.Config.string()]).value('label'),

			/**
    * Input placeholder.
    * @default undefined
    * @instance
    * @memberof ClayMultiSelect
    * @type {?(string|undefined)}
    */
			placeholder: _metalState.Config.string(),

			/**
    * Flag to define how often to refetch data (ms)
    * @instance
    * @default 0
    * @memberof ClayMultiSelect
    * @type {?(number|undefined)}
    */
			pollingInterval: _metalState.Config.number().value(0),

			/**
    * Set ups the request options
    * @instance
    * @default undefined
    * @memberof ClayMultiSelect
    * @type {?(object|undefined)}
    */
			requestOptions: _metalState.Config.shapeOf({
				method: _metalState.Config.string(),
				mode: _metalState.Config.string(),
				cache: _metalState.Config.string(),
				credentials: _metalState.Config.string(),
				headers: _metalState.Config.object(),
				redirect: _metalState.Config.string(),
				referrer: _metalState.Config.string(),
				body: _metalState.Config.object()
			}),

			/**
    * Flag to define how often to refetch data (ms)
    * @instance
    * @default 0
    * @memberof ClayMultiSelect
    * @type {?(number|undefined)}
    */
			requestPolling: _metalState.Config.validator(function (value) {
				if (value) {
					console.warn('🚨 `requestPolling` has been renamed to `pollingInterval` and will be deprecated and removed in the next release.');
				}
			}),

			/**
    * Define how many attempts will be made when the request fails
    * @instance
    * @default 5
    * @memberof ClayMultiSelect
    * @type {?(number|undefined)}
    */
			requestRetries: _metalState.Config.number().value(5),

			/**
    * Set timeout of the request
    * @instance
    * @default 30000
    * @memberof ClayMultiSelect
    * @type {?(number|undefined)}
    */
			requestTimeout: _metalState.Config.number().value(30000),

			/**
    * List of the selected Items.
    * @default []
    * @instance
    * @memberof ClayMultiSelect
    * @type {?Array<Object>}
    */
			selectedItems: _metalState.Config.array(_metalState.Config.object()).value([]),

			/**
    * Flag to indicate if select button should be shown or not.
    * @default true
    * @instance
    * @memberof ClayMultiSelect
    * @type {?bool}
    */
			showSelectButton: _metalState.Config.bool().value(true),

			/**
    * The path to the SVG spritemap file containing the icons.
    * @default undefined
    * @instance
    * @memberof ClayMultiSelect
    * @type {!string}
    */
			spritemap: _metalState.Config.string().required(),

			/**
    * Sets the name of the field to map the value of the item.
    * @default value
    * @instance
    * @memberof ClayMultiSelect
    * @type {?(function|string)}
    */
			valueLocator: _metalState.Config.oneOfType([_metalState.Config.func(), _metalState.Config.string()]).value('value')
		};

		(0, _metalWebComponent2.default)('clay-multi-select', ClayMultiSelect);

		_metalSoy2.default.register(ClayMultiSelect, _ClayMultiSelectSoy2.default);

		exports.ClayMultiSelect = ClayMultiSelect;
		exports.default = ClayMultiSelect;
		//# sourceMappingURL=ClayMultiSelect.js.map
	}
});
//# sourceMappingURL=ClayMultiSelect.js.map