'use strict';

Liferay.Loader.define("frontend-taglib-clay$clay-dropdown@2.24.1/lib/ClayDropdownBase", ['module', 'exports', 'require', 'frontend-taglib-clay$clay-button', 'frontend-taglib-clay$clay-link', 'frontend-taglib-clay$clay-portal', './ClayDropdownItem', 'frontend-taglib-clay$clay-component', 'frontend-js-metal-web$metal-soy', 'frontend-js-metal-web$metal-dom', 'frontend-js-metal-web$metal-position', 'frontend-js-metal-web$metal-state', 'frontend-js-metal-web$metal-events', './validators', './ClayDropdownBase.soy'], function (module, exports, require) {
	var define = undefined;
	var global = window;
	{
		Object.defineProperty(exports, "__esModule", {
			value: true
		});
		exports.flatten = exports.ClayDropdownBase = 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-button");

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

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

		require('./ClayDropdownItem');

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

		var _clayComponent2 = _interopRequireDefault(_clayComponent);

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

		var _metalSoy2 = _interopRequireDefault(_metalSoy);

		var _metalDom = require("frontend-js-metal-web$metal-dom");

		var _metalDom2 = _interopRequireDefault(_metalDom);

		var _metalPosition = require("frontend-js-metal-web$metal-position");

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

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

		var _validators = require('./validators');

		var _ClayDropdownBaseSoy = require("./ClayDropdownBase.soy");

		var _ClayDropdownBaseSoy2 = _interopRequireDefault(_ClayDropdownBaseSoy);

		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;
		}

		var KEY_CODE_ESC = 27;

		var KEY_ESCAPE = 'Escape';
		var KEY_TAB = 'Tab';

		/**
   * Flattens all items into an array of arrays.
   * @param {Array} items
   * @param {Bool} groupItems This keeps items in their original groupings
   * @private
   * @return {Array}
   */
		var flatten = function flatten(items, groupItems) {
			var newItems = items.map(function (item) {
				return item.items ? item.items : [item];
			});

			return groupItems ? newItems : newItems.reduce(function (acc, cur) {
				return acc.concat(cur);
			}, []);
		};

		/**
   * Implementation of the base for Metal Clay Dropdown.
   * @extends ClayComponent
   */

		var ClayDropdownBase = function (_ClayComponent) {
			_inherits(ClayDropdownBase, _ClayComponent);

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

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

			_createClass(ClayDropdownBase, [{
				key: 'attached',

				/**
     * @inheritDoc
     */
				value: function attached() {
					this.addListener('toggle', this._defaultToggle, true);
					this.refs.portal.on('rendered', this._handleRenderedPortal.bind(this));
				}

				/**
     * @inheritDoc
     */

			}, {
				key: 'created',
				value: function created() {
					this._eventHandler = new _metalEvents.EventHandler();
					this._filteredItems = this.items;
				}

				/**
     * @inheritDoc
     */

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

				/**
     * @inheritDoc
     */

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

				/**
     * @inheritDoc
     */

			}, {
				key: 'willReceiveState',
				value: function willReceiveState(changes) {
					if (changes.items && changes.items.newVal !== changes.items.prevVal) {
						this._filteredItems = this.items;

						// Checks if there is any search in progress when the new item
						// values change to search the new values.
						if (this.refs.portal && this.refs.portal.refs.searchInput && this.refs.portal.refs.searchInput.value !== '') {
							this._handleSearch({
								delegateTarget: {
									value: this.refs.portal.refs.searchInput.value
								}
							});
						}
					}
				}

				/**
     * Reassembles flattened items into the correct items structure.
     * This method is used in conjunction with `flatten(..., true)`
     * @param {Array} flattenedItems
     * @private
     * @return {Array}
     */

			}, {
				key: '_assembleFromFlattenedGroups',
				value: function _assembleFromFlattenedGroups(flattenedItems) {
					return this._filteredItems.map(function (item, i) {
						if (item.items) {
							item.items = flattenedItems[i];
						} else {
							item = flattenedItems[i][0];
						}

						return item;
					});
				}

				/**
     * Toggles the dropdown, closing it when open or opening it when closed.
     * @protected
     */

			}, {
				key: '_defaultToggle',
				value: function _defaultToggle() {
					this.expanded = !this.expanded;
				}

				/**
     * Returns the dropdown index of the element.
     * @param {!Node} element
     * @private
     * @return {?array|undefined} the index.
     */

			}, {
				key: '_getDropdownItemIndex',
				value: function _getDropdownItemIndex(element) {
					return Array.prototype.indexOf.call(Array.prototype.filter.call(element.parentElement.children, function (childrenElement) {
						return childrenElement.getAttribute('role') !== 'presentation';
					}), element);
				}

				/**
     * Gets the indexes of the next active item.
     * @param {Array} items
     * @param {Number} initialActive
     * @param {Number} initialSubActive
     * @param {Boolean} reverse
     * @private
     * @return {Number}
     */

			}, {
				key: '_getNextIndexes',
				value: function _getNextIndexes(items, initialActive, initialSubActive) {
					var reverse = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;

					var _incrementActiveItemI = this._incrementActiveItemIndex(items, initialActive === -1 ? 0 : initialActive, initialSubActive, reverse),
					    active = _incrementActiveItemI.active,
					    subActive = _incrementActiveItemI.subActive;

					var allItems = items.reduce(function (prev, curr) {
						return prev.concat(curr);
					}, []);

					var tick = 0;

					while (items[active][subActive].disabled && tick < allItems.length) {
						var item = this._incrementActiveItemIndex(items, active, subActive, reverse);

						active = item.active;
						subActive = item.subActive;

						tick++;
					}

					return { active: active, subActive: subActive };
				}

				/**
     * Handles document click in order to hide menu.
     * @param {!Event} event
     * @protected
     */

			}, {
				key: '_handleDocClick',
				value: function _handleDocClick(event) {
					if (this.element.contains(event.target) || this.refs.portal && this.refs.portal.element.contains(event.target)) {
						return;
					}
					this.toggle();
				}

				/**
     * Handles footer button click.
     * @param {!Event} event
     * @protected
     */

			}, {
				key: '_handleButtonClick',
				value: function _handleButtonClick(event) {
					this.emit('buttonClicked', event);
				}

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

			}, {
				key: '_handleItemClick',
				value: function _handleItemClick(event) {
					var element = event.delegateTarget;
					var elementIndex = this._getDropdownItemIndex(element);
					var flatenItems = flatten(this._filteredItems);
					var item = flatenItems[elementIndex];

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

				/**
     * Handles the item key down event
     * @param {!Event} event
     * @protected
     */

			}, {
				key: '_handleItemKeyDown',
				value: function _handleItemKeyDown(event) {
					if (event.key === KEY_TAB) {
						var element = event.delegateTarget;
						var elementIndex = this._getDropdownItemIndex(element);
						var totalElements = element.parentElement.querySelectorAll('li :not([role="presentation"])').length;

						if (elementIndex === 0 && event.shiftKey || elementIndex === totalElements && !event.shiftKey) {
							event.preventDefault();
							this.refs.triggerButton.focus();
						} else if (elementIndex === totalElements - 1 && !event.shiftKey) {
							this.toggle();
							this.refs.triggerButton.focus();
						}
					} else if (event.key === KEY_ESCAPE) {
						this.toggle();
						this.refs.triggerButton.focus();
					}
				}

				/**
     * Handle click key code esc and close dropdown.
     * @param {!Event} event
     * @private
     */

			}, {
				key: '_handleKeyup',
				value: function _handleKeyup(event) {
					if (event.keyCode === KEY_CODE_ESC) {
						this.toggle();
					}
				}

				/**
     * Handle when the lifecycle `rendered` is called in ClayPortal.
     * @protected
     */

			}, {
				key: '_handleRenderedPortal',
				value: function _handleRenderedPortal() {
					if (this.expanded && this._alignElementSelector) {
						var alignElement = this.element.querySelector(this._alignElementSelector);
						if (alignElement) {
							var bodyElement = this.refs.portal.refs.menu;

							this._alignedPosition = _metalPosition.Align.align(bodyElement, alignElement, this.preferredAlign);
						}
					}
				}

				/**
     * Handles Search in Dropdown.
     * @param {!Event} event
     * @protected
     * @return {Boolean} If the event has been prevented or not.
     */

			}, {
				key: '_handleSearch',
				value: function _handleSearch(event) {
					var searchValue = event.delegateTarget.value.toLowerCase();

					this._filteredItems = this.items;

					this._filteredItems = this._filteredItems.filter(function (item) {
						if (item.items) {
							if (!item._originalItems) {
								item._originalItems = item.items;
							}

							item.items = item._originalItems.filter(function (nestedItem) {
								return nestedItem.label && nestedItem.type !== 'group' && nestedItem.type !== 'header' && nestedItem.type !== 'separator' && nestedItem.label.toLowerCase().indexOf(searchValue) !== -1;
							});

							return item.items.length > 0;
						} else {
							return item.label && item.type !== 'group' && item.type !== 'header' && item.type !== 'separator' && item.label.toLowerCase().indexOf(searchValue) !== -1;
						}
					});

					return !this.emit({
						data: {
							filteredItems: this._filteredItems,
							originalItems: this.items
						},
						name: 'itemsFiltered',
						originalEvent: event
					});
				}

				/**
     * Handles the key down event over the trigger
     * @param {!Event} event
     * @protected
     */

			}, {
				key: '_handleTriggerKeyDown',
				value: function _handleTriggerKeyDown(event) {
					if (event.key === KEY_TAB) {
						if (event.shiftKey && this.expanded) {
							this.toggle();
						} else {
							var item = this.refs.portal.refs.item0;

							item = item.refs.item0 || item;

							if (item) {
								item.element.focus();
							}
						}
					}
				}

				/**
     * Handles blur window in order to hide menu.
     * @private
     */

			}, {
				key: '_handleWinBlur',
				value: function _handleWinBlur() {
					var activeElement = document.activeElement;
					if (activeElement != null && activeElement.nodeName === 'IFRAME') {
						this.toggle();
					}
				}

				/**
     * Increments to the index to the next item.
     * @param {Array} items
     * @param {Number} active
     * @param {Number} subActive
     * @param {Boolean} reverse
     * @private
     * @return {Object}
     */

			}, {
				key: '_incrementActiveItemIndex',
				value: function _incrementActiveItemIndex(items, active, subActive, reverse) {
					var totalItems = items.length;

					subActive = reverse ? subActive - 1 : subActive + 1;

					if (reverse) {
						if (subActive < 0) {
							var nextActive = active - 1;

							active = nextActive < 0 ? totalItems - 1 : nextActive;

							subActive = items[active].length - 1;
						}
					} else {
						if (subActive === items[active].length) {
							var _nextActive = active + 1;

							active = _nextActive === totalItems ? 0 : _nextActive;

							subActive = 0;
						}
					}

					return { active: active, subActive: subActive };
				}

				/**
     * Sets the next item in the list as active.
     * @param {Boolean} reverse
     * @private
     */

			}, {
				key: '_setNextActive',
				value: function _setNextActive(reverse) {
					var items = flatten(this._filteredItems, true);
					var activeSubIndex = -1;

					var activeIndex = items.findIndex(function (item) {
						var subIndex = item.findIndex(function (item) {
							return item.active;
						});

						if (subIndex !== -1) {
							activeSubIndex = subIndex;

							return true;
						}
					});

					var _getNextIndexes2 = this._getNextIndexes(items, activeIndex, activeSubIndex, reverse),
					    active = _getNextIndexes2.active,
					    subActive = _getNextIndexes2.subActive;

					if (items[activeIndex] && items[activeIndex][activeSubIndex]) {
						items[activeIndex][activeSubIndex].active = false;
					}

					items[active][subActive].active = true;

					this._filteredItems = this._assembleFromFlattenedGroups(items);
				}

				/**
     * Set preferred alignment with Align API.
     * @private
     * @param {!string} value
     * @return {number}
     */

			}, {
				key: '_setPreferredAlign',
				value: function _setPreferredAlign(value) {
					switch (value) {
						case 'TopCenter':
							return _metalPosition.Align.TopCenter;
						case 'RightCenter':
							return _metalPosition.Align.RightCenter;
						case 'BottomCenter':
							return _metalPosition.Align.BottomCenter;
						case 'LeftCenter':
							return _metalPosition.Align.LeftCenter;
						case 'TopRight':
							return _metalPosition.Align.TopRight;
						case 'BottomRight':
							return _metalPosition.Align.BottomRight;
						case 'BottomLeft':
							return _metalPosition.Align.BottomLeft;
						case 'TopLeft':
							return _metalPosition.Align.TopLeft;
						default:
							return _metalPosition.Align.BottomLeft;
					}
				}

				/**
     * Workaround for avoiding Closure Templates render an empty `.dropdown-caption` element.
     * This workaround was placed here because Soy isn't considering a `msg` as a `string`
     * making impossible the usage of `kind="html|string"`.
     *
     * When having a `html` inside `kind` Soy's attribute, this property is considered a function,
     * impeding us to know if there is content or not to render `caption`.
     * @private
     */

			}, {
				key: 'syncCaption',
				value: function syncCaption() {
					if (this.refs.portal && this.refs.portal.element) {
						var dropdownCaption = this.refs.portal.element.querySelector('.dropdown-caption');
						if (dropdownCaption) {
							if (dropdownCaption.innerText === '') {
								this._showCaption = false;
							} else {
								this._showCaption = true;
							}
						}
					}
				}

				/**
     * @inheritDoc
     */

			}, {
				key: 'syncExpanded',
				value: function syncExpanded() {
					if (this.expanded) {
						this._eventHandler.add(_metalDom2.default.on(document, 'click', this._handleDocClick.bind(this), true), _metalDom2.default.on(document, 'touchend', this._handleDocClick.bind(this), true), _metalDom2.default.on(window, 'blur', this._handleWinBlur.bind(this), true));
					} else if (this._eventHandler.eventHandles_.length) {
						this._eventHandler.removeAllListeners();
					}
				}

				/**
     * Propagate the event toggle.
     * @param {!Event} event
     * @return {Boolean} If the event has been prevented or not.
     */

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

			return ClayDropdownBase;
		}(_clayComponent2.default);

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

		ClayDropdownBase.STATE = {
			/**
    * The current position of the tooltip after being aligned via `Align.align`.
    * @default Align.isValidPosition
    * @instance
    * @memberof ClayDropdownBase
    * @type {!number}
    */
			_alignedPosition: _metalState.Config.validator(_metalPosition.Align.isValidPosition).internal(),

			/**
    * Element selector used to position dropdown according to trigger position.
    * @default .dropdown-toggle
    * @instance
    * @memberof ClayDropdownBase
    * @type {?string}
    */
			_alignElementSelector: _metalState.Config.string().value('.dropdown-toggle').internal(),

			/**
    * List of menu items filtered
    * @default this.items
    * @instance
    * @memberof ClayDropdownBase
    * @type {!array}
    */
			_filteredItems: _validators.itemsValidator.internal(),

			/**
    * Flag to indicate if Dropdown's Caption can be shown.
    * @default false
    * @instance
    * @memberof ClayDropdownBase
    * @type {?Boolean}
    */
			_showCaption: _metalState.Config.bool().value(true).internal(),

			/**
    * Button configuration to place a button at dropdown footer.
    * @default undefined
    * @instance
    * @memberof ClayDropdownBase
    * @type {?(string|undefined)}
    */
			button: _metalState.Config.shapeOf({
				href: _metalState.Config.string(),
				label: _metalState.Config.string().required(),
				style: _metalState.Config.oneOf(['primary', 'secondary']).value('primary'),
				type: _metalState.Config.oneOf(['button', 'reset', 'submit']).value('button')
			}),

			/**
    * Caption text of the dropdown.
    * @default undefined
    * @instance
    * @memberof ClayDropdownBase
    * @type {?(html|string|undefined)}
    */
			caption: _metalState.Config.any(),

			/**
    * Content Renderer name of items.
    * @default undefined
    * @instance
    * @memberof ClayDropdownBase
    * @type {?(string|undefined)}
    */
			contentRenderer: _metalState.Config.string(),

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

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

			/**
    * Flag to indicate if menu is disabled
    * @default false
    * @instance
    * @memberof ClayDropdownBase
    * @type {?bool}
    */
			disabled: _metalState.Config.bool().value(false),

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

			/**
    * Flag to indicate if menu is expanded.
    * @default false
    * @instance
    * @memberof ClayDropdownBase
    * @type {?bool}
    */
			expanded: _metalState.Config.bool().value(false),

			/**
    * Help text to be shown on top of the open dropdown.
    * @default undefined
    * @instance
    * @memberof ClayDropdownBase
    * @type {?(string|undefined)}
    */
			helpText: _metalState.Config.string(),

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

			/**
    * List of menu items.
    * @default undefined
    * @instance
    * @memberof ClayDropdownBase
    * @type {!array}
    */
			items: _validators.itemsValidator.required(),

			/**
    * Position in which item icons will be placed. Needed if any item has icons.
    * @default undefined
    * @instance
    * @memberof ClayDropdownBase
    * @type {?(string|undefined)}
    */
			itemsIconAlignment: _metalState.Config.oneOf(['left', 'right', 'left-right']),

			/**
    * Label of the trigger button.
    * @default undefined
    * @instance
    * @memberof ClayDropdownBase
    * @type {!(html|string)}
    */
			label: _metalState.Config.any().required(),

			/**
    * Id to be used for portal element.
    * @default clay_dropdown_portal
    * @instance
    * @memberof ClayDropdownBase
    * @type {!string}
    */
			portalElementId: _metalState.Config.string().value('clay_dropdown_portal').internal(),

			/**
    * The preferred alignment for the dropdown content.
    * @default BottomLeft
    * @instance
    * @memberof ClayDropdownBase
    * @type {?(string|undefined)}
    */
			preferredAlign: _validators.preferredAlign.setter('_setPreferredAlign').value('BottomLeft'),

			/**
    * Flag to indicate if menu has a search field and search through elements
    * is possible.
    * @default false
    * @instance
    * @memberof ClayDropdownBase
    * @type {?bool}
    */
			searchable: _metalState.Config.bool().value(false),

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

			/**
    * Style of the trigger button.
    * @default unstyled
    * @instance
    * @memberof ClayDropdownBase
    * @type {?string}
    */
			style: _metalState.Config.oneOf(['link', 'primary', 'secondary', 'unstyled']).value('unstyled'),

			/**
    * Aria label attribute for the trigger element.
    * @default undefined
    * @instance
    * @memberof ClayDropdownBase
    * @type {?(string|undefined)}
    */
			triggerAriaLabel: _metalState.Config.string(),

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

			/**
    * Size of the trigger button.
    * @default undefined
    * @instance
    * @memberof ClayDropdownBase
    * @type {?(string|undefined)}
    */
			triggerSize: _metalState.Config.oneOf(['sm']),

			/**
    * The title attribute of the trigger element.
    * @default undefined
    * @instance
    * @memberof ClayDropdownBase
    * @type {?(string|undefined)}
    */
			triggerTitle: _metalState.Config.string(),

			/**
    * Flag to indicate whether the search element will be wrapped
    * with `<form />` tag.
    * @default true
    * @instance
    * @memberof ClayDropdownBase
    * @type {?bool}
    */
			useSearchableWithForm: _metalState.Config.bool().value(true)
		};

		_metalSoy2.default.register(ClayDropdownBase, _ClayDropdownBaseSoy2.default);

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