/**
 * DevExtreme (esm/ui/gallery.js)
 * Version: 21.1.7 (build 22012-0313)
 * Build date: Wed Jan 12 2022
 *
 * Copyright (c) 2012 - 2022 Developer Express Inc. ALL RIGHTS RESERVED
 * Read about DevExtreme licensing here: https://js.devexpress.com/Licensing/
 */
import $ from "../core/renderer";
import eventsEngine from "../events/core/events_engine";
import registerComponent from "../core/component_registrator";
import {
    noop
} from "../core/utils/common";
import {
    isDefined,
    isPlainObject
} from "../core/utils/type";
import {
    hasWindow
} from "../core/utils/window";
import {
    extend
} from "../core/utils/extend";
import {
    getPublicElement
} from "../core/element";
import fx from "../animation/fx";
import {
    name as clickEventName
} from "../events/click";
import {
    move
} from "../animation/translator";
import devices from "../core/devices";
import Widget from "./widget/ui.widget";
import {
    addNamespace
} from "../events/utils/index";
import CollectionWidget from "./collection/ui.collection_widget.edit";
import Swipeable from "../events/gesture/swipeable";
import {
    BindableTemplate
} from "../core/templates/bindable_template";
import {
    Deferred
} from "../core/utils/deferred";
var GALLERY_CLASS = "dx-gallery";
var GALLERY_WRAPPER_CLASS = GALLERY_CLASS + "-wrapper";
var GALLERY_LOOP_CLASS = "dx-gallery-loop";
var GALLERY_ITEM_CONTAINER_CLASS = GALLERY_CLASS + "-container";
var GALLERY_ACTIVE_CLASS = GALLERY_CLASS + "-active";
var GALLERY_ITEM_CLASS = GALLERY_CLASS + "-item";
var GALLERY_INVISIBLE_ITEM_CLASS = GALLERY_CLASS + "-item-invisible";
var GALLERY_LOOP_ITEM_CLASS = GALLERY_ITEM_CLASS + "-loop";
var GALLERY_ITEM_SELECTOR = "." + GALLERY_ITEM_CLASS;
var GALLERY_ITEM_SELECTED_CLASS = GALLERY_ITEM_CLASS + "-selected";
var GALLERY_INDICATOR_CLASS = GALLERY_CLASS + "-indicator";
var GALLERY_INDICATOR_ITEM_CLASS = GALLERY_INDICATOR_CLASS + "-item";
var GALLERY_INDICATOR_ITEM_SELECTOR = "." + GALLERY_INDICATOR_ITEM_CLASS;
var GALLERY_INDICATOR_ITEM_SELECTED_CLASS = GALLERY_INDICATOR_ITEM_CLASS + "-selected";
var GALLERY_IMAGE_CLASS = "dx-gallery-item-image";
var GALLERY_ITEM_DATA_KEY = "dxGalleryItemData";
var MAX_CALC_ERROR = 1;
var GalleryNavButton = Widget.inherit({
    _supportedKeys: function() {
        return extend(this.callBase(), {
            pageUp: noop,
            pageDown: noop
        })
    },
    _getDefaultOptions: function() {
        return extend(this.callBase(), {
            direction: "next",
            onClick: null,
            hoverStateEnabled: true,
            activeStateEnabled: true
        })
    },
    _render: function() {
        this.callBase();
        var that = this;
        var $element = this.$element();
        var eventName = addNamespace(clickEventName, this.NAME);
        $element.addClass(GALLERY_CLASS + "-nav-button-" + this.option("direction"));
        eventsEngine.off($element, eventName);
        eventsEngine.on($element, eventName, (function(e) {
            that._createActionByOption("onClick")({
                event: e
            })
        }))
    },
    _optionChanged: function(args) {
        switch (args.name) {
            case "onClick":
            case "direction":
                this._invalidate();
                break;
            default:
                this.callBase(args)
        }
    }
});
var Gallery = CollectionWidget.inherit({
    _activeStateUnit: GALLERY_ITEM_SELECTOR,
    _getDefaultOptions: function() {
        return extend(this.callBase(), {
            activeStateEnabled: false,
            animationDuration: 400,
            animationEnabled: true,
            loop: false,
            swipeEnabled: true,
            indicatorEnabled: true,
            showIndicator: true,
            selectedIndex: 0,
            slideshowDelay: 0,
            showNavButtons: false,
            wrapAround: false,
            initialItemWidth: void 0,
            stretchImages: false,
            _itemAttributes: {
                role: "option"
            },
            loopItemFocus: false,
            selectOnFocus: true,
            selectionMode: "single",
            selectionRequired: true,
            selectionByClick: false
        })
    },
    _defaultOptionsRules: function() {
        return this.callBase().concat([{
            device: function() {
                return "desktop" === devices.real().deviceType && !devices.isSimulator()
            },
            options: {
                focusStateEnabled: true
            }
        }])
    },
    _init: function() {
        this.callBase();
        this.option("loopItemFocus", this.option("loop"))
    },
    _initTemplates: function() {
        this.callBase();
        this._templateManager.addDefaultTemplates({
            item: new BindableTemplate(function($container, data) {
                var $img = $("<img>").addClass(GALLERY_IMAGE_CLASS);
                if (isPlainObject(data)) {
                    this._prepareDefaultItemTemplate(data, $container);
                    $img.attr({
                        src: data.imageSrc,
                        alt: data.imageAlt
                    }).appendTo($container)
                } else {
                    $img.attr("src", String(data)).appendTo($container)
                }
            }.bind(this), ["imageSrc", "imageAlt", "text", "html"], this.option("integrationOptions.watchMethod"))
        })
    },
    _dataSourceOptions: function() {
        return {
            paginate: false
        }
    },
    _itemContainer: function() {
        return this._$container
    },
    _itemClass: function() {
        return GALLERY_ITEM_CLASS
    },
    _itemDataKey: function() {
        return GALLERY_ITEM_DATA_KEY
    },
    _actualItemWidth: function() {
        var isWrapAround = this.option("wrapAround");
        if (this.option("stretchImages")) {
            var itemPerPage = isWrapAround ? this._itemsPerPage() + 1 : this._itemsPerPage();
            return 1 / itemPerPage
        }
        if (isWrapAround) {
            return this._itemPercentWidth() * this._itemsPerPage() / (this._itemsPerPage() + 1)
        }
        return this._itemPercentWidth()
    },
    _itemPercentWidth: function() {
        var percentWidth;
        var elementWidth = this.$element().outerWidth();
        var initialItemWidth = this.option("initialItemWidth");
        if (initialItemWidth && initialItemWidth <= elementWidth) {
            percentWidth = initialItemWidth / elementWidth
        } else {
            percentWidth = 1
        }
        return percentWidth
    },
    _itemsPerPage: function() {
        var itemsPerPage = hasWindow() ? Math.floor(1 / this._itemPercentWidth()) : 1;
        return Math.min(itemsPerPage, this._itemsCount())
    },
    _pagesCount: function() {
        return Math.ceil(this._itemsCount() / this._itemsPerPage())
    },
    _itemsCount: function() {
        return (this.option("items") || []).length
    },
    _offsetDirection: function() {
        return this.option("rtlEnabled") ? -1 : 1
    },
    _initMarkup: function() {
        this._renderWrapper();
        this._renderItemsContainer();
        this.$element().addClass(GALLERY_CLASS);
        this.$element().toggleClass(GALLERY_LOOP_CLASS, this.option("loop"));
        this.callBase();
        this.setAria({
            role: "listbox",
            label: "gallery"
        })
    },
    _render: function() {
        this._renderDragHandler();
        this._renderContainerPosition();
        this._renderItemSizes();
        this._renderItemPositions();
        this._renderNavButtons();
        this._renderIndicator();
        this._renderSelectedItem();
        this._renderItemVisibility();
        this._renderUserInteraction();
        this._setupSlideShow();
        this._reviseDimensions();
        this.callBase()
    },
    _dimensionChanged: function() {
        var selectedIndex = this.option("selectedIndex") || 0;
        this._stopItemAnimations();
        this._clearCacheWidth();
        this._cloneDuplicateItems();
        this._renderItemSizes();
        this._renderItemPositions();
        this._renderIndicator();
        this._renderContainerPosition(this._calculateIndexOffset(selectedIndex), true);
        this._renderItemVisibility()
    },
    _renderDragHandler: function() {
        var eventName = addNamespace("dragstart", this.NAME);
        eventsEngine.off(this.$element(), eventName);
        eventsEngine.on(this.$element(), eventName, "img", (function() {
            return false
        }))
    },
    _renderWrapper: function() {
        if (this._$wrapper) {
            return
        }
        this._$wrapper = $("<div>").addClass(GALLERY_WRAPPER_CLASS).appendTo(this.$element())
    },
    _renderItems: function(items) {
        if (!hasWindow()) {
            var selectedIndex = this.option("selectedIndex");
            items = items.length > selectedIndex ? items.slice(selectedIndex, selectedIndex + 1) : items.slice(0, 1)
        }
        this.callBase(items);
        this._loadNextPageIfNeeded()
    },
    _renderItemsContainer: function() {
        if (this._$container) {
            return
        }
        this._$container = $("<div>").addClass(GALLERY_ITEM_CONTAINER_CLASS).appendTo(this._$wrapper)
    },
    _cloneDuplicateItems: function() {
        if (!this.option("loop")) {
            return
        }
        var items = this.option("items") || [];
        var itemsCount = items.length;
        var lastItemIndex = itemsCount - 1;
        var i;
        if (!itemsCount) {
            return
        }
        this._getLoopedItems().remove();
        var duplicateCount = Math.min(this._itemsPerPage(), itemsCount);
        var $items = this._getRealItems();
        var $container = this._itemContainer();
        for (i = 0; i < duplicateCount; i++) {
            this._cloneItemForDuplicate($items[i], $container)
        }
        for (i = 0; i < duplicateCount; i++) {
            this._cloneItemForDuplicate($items[lastItemIndex - i], $container)
        }
    },
    _cloneItemForDuplicate: function(item, $container) {
        if (item) {
            $(item).clone(true).addClass(GALLERY_LOOP_ITEM_CLASS).css("margin", 0).appendTo($container)
        }
    },
    _getRealItems: function() {
        var selector = "." + GALLERY_ITEM_CLASS + ":not(." + GALLERY_LOOP_ITEM_CLASS + ")";
        return this.$element().find(selector)
    },
    _getLoopedItems: function() {
        return this.$element().find("." + GALLERY_LOOP_ITEM_CLASS)
    },
    _emptyMessageContainer: function() {
        return this._$wrapper
    },
    _renderItemSizes: function(startIndex) {
        var $items = this._itemElements();
        var itemWidth = this._actualItemWidth();
        if (void 0 !== startIndex) {
            $items = $items.slice(startIndex)
        }
        $items.each((function(index) {
            $($items[index]).outerWidth(100 * itemWidth + "%")
        }))
    },
    _renderItemPositions: function() {
        var itemWidth = this._actualItemWidth();
        var itemsCount = this._itemsCount();
        var itemsPerPage = this._itemsPerPage();
        var loopItemsCount = this.$element().find("." + GALLERY_LOOP_ITEM_CLASS).length;
        var lastItemDuplicateIndex = itemsCount + loopItemsCount - 1;
        var offsetRatio = this.option("wrapAround") ? .5 : 0;
        var freeSpace = this._itemFreeSpace();
        var isGapBetweenImages = !!freeSpace;
        var rtlEnabled = this.option("rtlEnabled");
        var selectedIndex = this.option("selectedIndex");
        var side = rtlEnabled ? "Right" : "Left";
        this._itemElements().each((function(index) {
            var realIndex = index;
            var isLoopItem = $(this).hasClass(GALLERY_LOOP_ITEM_CLASS);
            if (index > itemsCount + itemsPerPage - 1) {
                realIndex = lastItemDuplicateIndex - realIndex - itemsPerPage
            }
            if (!isLoopItem && 0 !== realIndex) {
                if (isGapBetweenImages) {
                    $(this).css("margin" + side, 100 * freeSpace + "%")
                }
                return
            }
            var itemPosition = itemWidth * (realIndex + offsetRatio) + freeSpace * (realIndex + 1 - offsetRatio);
            var property = isLoopItem ? side.toLowerCase() : "margin" + side;
            $(this).css(property, 100 * itemPosition + "%")
        }));
        this._relocateItems(selectedIndex, selectedIndex, true)
    },
    _itemFreeSpace: function() {
        var itemsPerPage = this._itemsPerPage();
        if (this.option("wrapAround")) {
            itemsPerPage += 1
        }
        return (1 - this._actualItemWidth() * itemsPerPage) / (itemsPerPage + 1)
    },
    _renderContainerPosition: function(offset, hideItems, animate) {
        this._releaseInvisibleItems();
        offset = offset || 0;
        var that = this;
        var itemWidth = this._actualItemWidth();
        var targetIndex = offset;
        var targetPosition = this._offsetDirection() * targetIndex * (itemWidth + this._itemFreeSpace());
        var positionReady;
        if (isDefined(this._animationOverride)) {
            animate = this._animationOverride;
            delete this._animationOverride
        }
        if (animate) {
            that._startSwipe();
            positionReady = that._animate(targetPosition).done(that._endSwipe.bind(that))
        } else {
            move(this._$container, {
                left: targetPosition * this._elementWidth(),
                top: 0
            });
            positionReady = (new Deferred).resolveWith(that)
        }
        positionReady.done((function() {
            this._deferredAnimate && that._deferredAnimate.resolveWith(that);
            hideItems && this._renderItemVisibility()
        }));
        return positionReady.promise()
    },
    _startSwipe: function() {
        this.$element().addClass(GALLERY_ACTIVE_CLASS)
    },
    _endSwipe: function() {
        this.$element().removeClass(GALLERY_ACTIVE_CLASS)
    },
    _animate: function(targetPosition, extraConfig) {
        var that = this;
        var $container = this._$container;
        var animationComplete = new Deferred;
        fx.animate(this._$container, extend({
            type: "slide",
            to: {
                left: targetPosition * this._elementWidth()
            },
            duration: that.option("animationDuration"),
            complete: function() {
                if (that._needMoveContainerForward()) {
                    move($container, {
                        left: 0,
                        top: 0
                    })
                }
                if (that._needMoveContainerBack()) {
                    move($container, {
                        left: that._maxContainerOffset() * that._elementWidth(),
                        top: 0
                    })
                }
                animationComplete.resolveWith(that)
            }
        }, extraConfig || {}));
        return animationComplete
    },
    _needMoveContainerForward: function() {
        var expectedPosition = this._$container.position().left * this._offsetDirection();
        var actualPosition = -this._maxItemWidth() * this._elementWidth() * this._itemsCount();
        return expectedPosition <= actualPosition + MAX_CALC_ERROR
    },
    _needMoveContainerBack: function() {
        var expectedPosition = this._$container.position().left * this._offsetDirection();
        var actualPosition = this._actualItemWidth() * this._elementWidth();
        return expectedPosition >= actualPosition - MAX_CALC_ERROR
    },
    _maxContainerOffset: function() {
        return -this._maxItemWidth() * (this._itemsCount() - this._itemsPerPage()) * this._offsetDirection()
    },
    _maxItemWidth: function() {
        return this._actualItemWidth() + this._itemFreeSpace()
    },
    _reviseDimensions: function() {
        var $firstItem = this._itemElements().first().find(".dx-item-content");
        if (!$firstItem || $firstItem.is(":hidden")) {
            return
        }
        if (!this.option("height")) {
            this.option("height", $firstItem.outerHeight())
        }
        if (!this.option("width")) {
            this.option("width", $firstItem.outerWidth())
        }
        this._dimensionChanged()
    },
    _renderIndicator: function() {
        this._cleanIndicators();
        if (!this.option("showIndicator")) {
            return
        }
        var indicator = this._$indicator = $("<div>").addClass(GALLERY_INDICATOR_CLASS).appendTo(this._$wrapper);
        var isIndicatorEnabled = this.option("indicatorEnabled");
        for (var i = 0; i < this._pagesCount(); i++) {
            var $indicatorItem = $("<div>").addClass(GALLERY_INDICATOR_ITEM_CLASS).appendTo(indicator);
            if (isIndicatorEnabled) {
                this._attachIndicatorClickHandler($indicatorItem, i)
            }
        }
        this._renderSelectedPageIndicator()
    },
    _attachIndicatorClickHandler: function($element, index) {
        eventsEngine.on($element, addNamespace(clickEventName, this.NAME), function(event) {
            this._indicatorSelectHandler(event, index)
        }.bind(this))
    },
    _detachIndicatorClickHandler: function($element) {
        eventsEngine.off($element, addNamespace(clickEventName, this.NAME))
    },
    _toggleIndicatorInteraction: function(clickEnabled) {
        var _this$_$indicator;
        var $indicatorItems = (null === (_this$_$indicator = this._$indicator) || void 0 === _this$_$indicator ? void 0 : _this$_$indicator.find(GALLERY_INDICATOR_ITEM_SELECTOR)) || [];
        if ($indicatorItems.length) {
            $indicatorItems.each(function(index, element) {
                clickEnabled ? this._attachIndicatorClickHandler($(element), index) : this._detachIndicatorClickHandler($(element))
            }.bind(this))
        }
    },
    _cleanIndicators: function() {
        if (this._$indicator) {
            this._$indicator.remove()
        }
    },
    _renderSelectedItem: function() {
        var selectedIndex = this.option("selectedIndex");
        this._itemElements().removeClass(GALLERY_ITEM_SELECTED_CLASS).eq(selectedIndex).addClass(GALLERY_ITEM_SELECTED_CLASS)
    },
    _renderItemVisibility: function() {
        if (this.option("initialItemWidth") || this.option("wrapAround")) {
            this._releaseInvisibleItems();
            return
        }
        this._itemElements().each(function(index, item) {
            if (this.option("selectedIndex") === index) {
                $(item).removeClass(GALLERY_INVISIBLE_ITEM_CLASS)
            } else {
                $(item).addClass(GALLERY_INVISIBLE_ITEM_CLASS)
            }
        }.bind(this));
        this._getLoopedItems().addClass(GALLERY_INVISIBLE_ITEM_CLASS)
    },
    _releaseInvisibleItems: function() {
        this._itemElements().removeClass(GALLERY_INVISIBLE_ITEM_CLASS);
        this._getLoopedItems().removeClass(GALLERY_INVISIBLE_ITEM_CLASS)
    },
    _renderSelectedPageIndicator: function() {
        if (!this._$indicator) {
            return
        }
        var itemIndex = this.option("selectedIndex");
        var lastIndex = this._pagesCount() - 1;
        var pageIndex = Math.ceil(itemIndex / this._itemsPerPage());
        pageIndex = Math.min(lastIndex, pageIndex);
        this._$indicator.find(GALLERY_INDICATOR_ITEM_SELECTOR).removeClass(GALLERY_INDICATOR_ITEM_SELECTED_CLASS).eq(pageIndex).addClass(GALLERY_INDICATOR_ITEM_SELECTED_CLASS)
    },
    _renderUserInteraction: function() {
        var rootElement = this.$element();
        var swipeEnabled = this.option("swipeEnabled") && this._itemsCount() > 1;
        this._createComponent(rootElement, Swipeable, {
            disabled: this.option("disabled") || !swipeEnabled,
            onStart: this._swipeStartHandler.bind(this),
            onUpdated: this._swipeUpdateHandler.bind(this),
            onEnd: this._swipeEndHandler.bind(this),
            itemSizeFunc: this._elementWidth.bind(this)
        })
    },
    _indicatorSelectHandler: function(e, indicatorIndex) {
        if (!this.option("indicatorEnabled")) {
            return
        }
        var itemIndex = this._fitPaginatedIndex(indicatorIndex * this._itemsPerPage());
        this._needLongMove = true;
        this.option("selectedIndex", itemIndex);
        this._loadNextPageIfNeeded(itemIndex)
    },
    _renderNavButtons: function() {
        var that = this;
        if (!that.option("showNavButtons")) {
            that._cleanNavButtons();
            return
        }
        that._prevNavButton = $("<div>").appendTo(this._$wrapper);
        that._createComponent(that._prevNavButton, GalleryNavButton, {
            direction: "prev",
            onClick: function() {
                that._prevPage()
            }
        });
        that._nextNavButton = $("<div>").appendTo(this._$wrapper);
        that._createComponent(that._nextNavButton, GalleryNavButton, {
            direction: "next",
            onClick: function() {
                that._nextPage()
            }
        });
        this._renderNavButtonsVisibility()
    },
    _prevPage: function() {
        var visiblePageSize = this._itemsPerPage();
        var newSelectedIndex = this.option("selectedIndex") - visiblePageSize;
        if (newSelectedIndex === -visiblePageSize && visiblePageSize === this._itemsCount()) {
            return this._relocateItems(newSelectedIndex, 0)
        } else {
            return this.goToItem(this._fitPaginatedIndex(newSelectedIndex))
        }
    },
    _nextPage: function() {
        var visiblePageSize = this._itemsPerPage();
        var newSelectedIndex = this.option("selectedIndex") + visiblePageSize;
        if (newSelectedIndex === visiblePageSize && visiblePageSize === this._itemsCount()) {
            return this._relocateItems(newSelectedIndex, 0)
        } else {
            return this.goToItem(this._fitPaginatedIndex(newSelectedIndex)).done(this._loadNextPageIfNeeded)
        }
    },
    _loadNextPageIfNeeded: function(selectedIndex) {
        selectedIndex = void 0 === selectedIndex ? this.option("selectedIndex") : selectedIndex;
        if (this._dataSource && this._dataSource.paginate() && this._shouldLoadNextPage(selectedIndex) && !this._isDataSourceLoading() && !this._isLastPage()) {
            this._loadNextPage().done(function() {
                this._renderIndicator();
                this._cloneDuplicateItems();
                this._renderItemPositions();
                this._renderNavButtonsVisibility();
                this._renderItemSizes(selectedIndex)
            }.bind(this))
        }
    },
    _shouldLoadNextPage: function(selectedIndex) {
        var visiblePageSize = this._itemsPerPage();
        return selectedIndex + 2 * visiblePageSize > this.option("items").length
    },
    _allowDynamicItemsAppend: function() {
        return true
    },
    _fitPaginatedIndex: function(itemIndex) {
        var itemsPerPage = this._itemsPerPage();
        var restItemsCount = itemIndex < 0 ? itemsPerPage + itemIndex : this._itemsCount() - itemIndex;
        if (itemIndex > this._itemsCount() - 1) {
            itemIndex = 0;
            this._goToGhostItem = true
        } else if (restItemsCount < itemsPerPage && restItemsCount > 0) {
            if (itemIndex > 0) {
                itemIndex -= itemsPerPage - restItemsCount
            } else {
                itemIndex += itemsPerPage - restItemsCount
            }
        }
        return itemIndex
    },
    _cleanNavButtons: function() {
        if (this._prevNavButton) {
            this._prevNavButton.remove();
            delete this._prevNavButton
        }
        if (this._nextNavButton) {
            this._nextNavButton.remove();
            delete this._nextNavButton
        }
    },
    _renderNavButtonsVisibility: function() {
        if (!this.option("showNavButtons") || !this._prevNavButton || !this._nextNavButton) {
            return
        }
        var selectedIndex = this.option("selectedIndex");
        var loop = this.option("loop");
        var itemsCount = this._itemsCount();
        this._prevNavButton.show();
        this._nextNavButton.show();
        if (0 === itemsCount) {
            this._prevNavButton.hide();
            this._nextNavButton.hide()
        }
        if (loop) {
            return
        }
        var nextHidden = selectedIndex === itemsCount - this._itemsPerPage();
        var prevHidden = itemsCount < 2 || 0 === selectedIndex;
        if (this._dataSource && this._dataSource.paginate()) {
            nextHidden = nextHidden && this._isLastPage()
        } else {
            nextHidden = nextHidden || itemsCount < 2
        }
        if (prevHidden) {
            this._prevNavButton.hide()
        }
        if (nextHidden) {
            this._nextNavButton.hide()
        }
    },
    _setupSlideShow: function() {
        var that = this;
        var slideshowDelay = that.option("slideshowDelay");
        clearTimeout(that._slideshowTimer);
        if (!slideshowDelay) {
            return
        }
        that._slideshowTimer = setTimeout((function() {
            if (that._userInteraction) {
                that._setupSlideShow();
                return
            }
            that.nextItem(true).done(that._setupSlideShow)
        }), slideshowDelay)
    },
    _elementWidth: function() {
        if (!this._cacheElementWidth) {
            this._cacheElementWidth = this.$element().width()
        }
        return this._cacheElementWidth
    },
    _clearCacheWidth: function() {
        delete this._cacheElementWidth
    },
    _swipeStartHandler: function(e) {
        this._releaseInvisibleItems();
        this._clearCacheWidth();
        this._elementWidth();
        var itemsCount = this._itemsCount();
        if (!itemsCount) {
            e.event.cancel = true;
            return
        }
        this._stopItemAnimations();
        this._startSwipe();
        this._userInteraction = true;
        if (!this.option("loop")) {
            var selectedIndex = this.option("selectedIndex");
            var startOffset = itemsCount - selectedIndex - this._itemsPerPage();
            var endOffset = selectedIndex;
            var rtlEnabled = this.option("rtlEnabled");
            e.event.maxLeftOffset = rtlEnabled ? endOffset : startOffset;
            e.event.maxRightOffset = rtlEnabled ? startOffset : endOffset
        }
    },
    _stopItemAnimations: function() {
        fx.stop(this._$container, true)
    },
    _swipeUpdateHandler: function(e) {
        var wrapAroundRatio = this.option("wrapAround") ? 1 : 0;
        var offset = this._offsetDirection() * e.event.offset * (this._itemsPerPage() + wrapAroundRatio) - this.option("selectedIndex");
        if (offset < 0) {
            this._loadNextPageIfNeeded(Math.ceil(Math.abs(offset)))
        }
        this._renderContainerPosition(offset)
    },
    _swipeEndHandler: function(e) {
        var targetOffset = e.event.targetOffset * this._offsetDirection() * this._itemsPerPage();
        var selectedIndex = this.option("selectedIndex");
        var newIndex = this._fitIndex(selectedIndex - targetOffset);
        var paginatedIndex = this._fitPaginatedIndex(newIndex);
        if (Math.abs(targetOffset) < this._itemsPerPage()) {
            this._relocateItems(selectedIndex);
            return
        }
        if (this._itemsPerPage() === this._itemsCount()) {
            if (targetOffset > 0) {
                this._relocateItems(-targetOffset)
            } else {
                this._relocateItems(0)
            }
            return
        }
        this.option("selectedIndex", paginatedIndex)
    },
    _setFocusOnSelect: function() {
        this._userInteraction = true;
        var selectedItem = this.itemElements().filter("." + GALLERY_ITEM_SELECTED_CLASS);
        this.option("focusedElement", getPublicElement(selectedItem));
        this._userInteraction = false
    },
    _flipIndex: function(index) {
        var itemsCount = this._itemsCount();
        index %= itemsCount;
        if (index > (itemsCount + 1) / 2) {
            index -= itemsCount
        }
        if (index < -(itemsCount - 1) / 2) {
            index += itemsCount
        }
        return index
    },
    _fitIndex: function(index) {
        if (!this.option("loop")) {
            return index
        }
        var itemsCount = this._itemsCount();
        if (index >= itemsCount || index < 0) {
            this._goToGhostItem = true
        }
        if (index >= itemsCount) {
            index = itemsCount - index
        }
        index %= itemsCount;
        if (index < 0) {
            index += itemsCount
        }
        return index
    },
    _clean: function() {
        this.callBase();
        this._cleanIndicators();
        this._cleanNavButtons()
    },
    _dispose: function() {
        clearTimeout(this._slideshowTimer);
        this.callBase()
    },
    _updateSelection: function(addedSelection, removedSelection) {
        this._stopItemAnimations();
        this._renderNavButtonsVisibility();
        this._renderSelectedItem();
        this._relocateItems(addedSelection[0], removedSelection[0]);
        this._renderSelectedPageIndicator()
    },
    _relocateItems: function(newIndex, prevIndex, withoutAnimation) {
        if (void 0 === prevIndex) {
            prevIndex = newIndex
        }
        var indexOffset = this._calculateIndexOffset(newIndex, prevIndex);
        this._renderContainerPosition(indexOffset, true, this.option("animationEnabled") && !withoutAnimation).done((function() {
            this._setFocusOnSelect();
            this._userInteraction = false;
            this._setupSlideShow()
        }))
    },
    _focusInHandler: function() {
        if (fx.isAnimating(this._$container) || this._userInteraction) {
            return
        }
        this.callBase.apply(this, arguments)
    },
    _focusOutHandler: function() {
        if (fx.isAnimating(this._$container) || this._userInteraction) {
            return
        }
        this.callBase.apply(this, arguments)
    },
    _selectFocusedItem: noop,
    _moveFocus: function() {
        this._stopItemAnimations();
        this.callBase.apply(this, arguments);
        var index = this.itemElements().index($(this.option("focusedElement")));
        this.goToItem(index, this.option("animationEnabled"))
    },
    _visibilityChanged: function(visible) {
        if (visible) {
            this._reviseDimensions()
        }
    },
    _calculateIndexOffset: function(newIndex, lastIndex) {
        if (void 0 === lastIndex) {
            lastIndex = newIndex
        }
        var indexOffset = lastIndex - newIndex;
        if (this.option("loop") && !this._needLongMove && this._goToGhostItem) {
            if (this._isItemOnFirstPage(newIndex) && this._isItemOnLastPage(lastIndex)) {
                indexOffset = -this._itemsPerPage()
            } else if (this._isItemOnLastPage(newIndex) && this._isItemOnFirstPage(lastIndex)) {
                indexOffset = this._itemsPerPage()
            }
            this._goToGhostItem = false
        }
        this._needLongMove = false;
        indexOffset -= lastIndex;
        return indexOffset
    },
    _isItemOnLastPage: function(itemIndex) {
        return itemIndex >= this._itemsCount() - this._itemsPerPage()
    },
    _isItemOnFirstPage: function(itemIndex) {
        return itemIndex <= this._itemsPerPage()
    },
    _optionChanged: function(args) {
        switch (args.name) {
            case "width":
            case "initialItemWidth":
                this.callBase.apply(this, arguments);
                this._dimensionChanged();
                break;
            case "animationDuration":
                this._renderNavButtonsVisibility();
                break;
            case "animationEnabled":
                break;
            case "loop":
                this.$element().toggleClass(GALLERY_LOOP_CLASS, args.value);
                this.option("loopItemFocus", args.value);
                if (hasWindow()) {
                    this._cloneDuplicateItems();
                    this._renderItemPositions();
                    this._renderNavButtonsVisibility()
                }
                break;
            case "showIndicator":
                this._renderIndicator();
                break;
            case "showNavButtons":
                this._renderNavButtons();
                break;
            case "slideshowDelay":
                this._setupSlideShow();
                break;
            case "wrapAround":
            case "stretchImages":
                if (hasWindow()) {
                    this._renderItemSizes();
                    this._renderItemPositions();
                    this._renderItemVisibility()
                }
                break;
            case "swipeEnabled":
                this._renderUserInteraction();
                break;
            case "indicatorEnabled":
                this._toggleIndicatorInteraction(args.value);
                break;
            default:
                this.callBase(args)
        }
    },
    goToItem: function(itemIndex, animation) {
        var selectedIndex = this.option("selectedIndex");
        var itemsCount = this._itemsCount();
        if (void 0 !== animation) {
            this._animationOverride = animation
        }
        itemIndex = this._fitIndex(itemIndex);
        this._deferredAnimate = new Deferred;
        if (itemIndex > itemsCount - 1 || itemIndex < 0 || selectedIndex === itemIndex) {
            return this._deferredAnimate.resolveWith(this).promise()
        }
        this.option("selectedIndex", itemIndex);
        return this._deferredAnimate.promise()
    },
    prevItem: function(animation) {
        return this.goToItem(this.option("selectedIndex") - 1, animation)
    },
    nextItem: function(animation) {
        return this.goToItem(this.option("selectedIndex") + 1, animation)
    }
});
registerComponent("dxGallery", Gallery);
export default Gallery;
