/**
 * DevExtreme (ui/gallery.js)
 * Version: 17.2.10 (build 18271)
 * Build date: Fri Sep 28 2018
 *
 * Copyright (c) 2012 - 2018 Developer Express Inc. ALL RIGHTS RESERVED
 * Read about DevExtreme licensing here: https://js.devexpress.com/Licensing/
 */
"use strict";
var $ = require("../core/renderer"),
    eventsEngine = require("../events/core/events_engine"),
    registerComponent = require("../core/component_registrator"),
    commonUtils = require("../core/utils/common"),
    typeUtils = require("../core/utils/type"),
    extend = require("../core/utils/extend").extend,
    getPublicElement = require("../core/utils/dom").getPublicElement,
    fx = require("../animation/fx"),
    clickEvent = require("../events/click"),
    translator = require("../animation/translator"),
    devices = require("../core/devices"),
    Widget = require("./widget/ui.widget"),
    eventUtils = require("../events/utils"),
    CollectionWidget = require("./collection/ui.collection_widget.edit"),
    Swipeable = require("../events/gesture/swipeable"),
    BindableTemplate = require("./widget/bindable_template"),
    Deferred = require("../core/utils/deferred").Deferred;
var GALLERY_CLASS = "dx-gallery",
    GALLERY_WRAPPER_CLASS = GALLERY_CLASS + "-wrapper",
    GALLERY_LOOP_CLASS = "dx-gallery-loop",
    GALLERY_ITEM_CONTAINER_CLASS = GALLERY_CLASS + "-container",
    GALLERY_ACTIVE_CLASS = GALLERY_CLASS + "-active",
    GALLERY_ITEM_CLASS = GALLERY_CLASS + "-item",
    GALLERY_INVISIBLE_ITEM_CLASS = GALLERY_CLASS + "-item-invisible",
    GALLERY_LOOP_ITEM_CLASS = GALLERY_ITEM_CLASS + "-loop",
    GALLERY_ITEM_SELECTOR = "." + GALLERY_ITEM_CLASS,
    GALLERY_ITEM_SELECTED_CLASS = GALLERY_ITEM_CLASS + "-selected",
    GALLERY_INDICATOR_CLASS = GALLERY_CLASS + "-indicator",
    GALLERY_INDICATOR_ITEM_CLASS = GALLERY_INDICATOR_CLASS + "-item",
    GALLERY_INDICATOR_ITEM_SELECTOR = "." + GALLERY_INDICATOR_ITEM_CLASS,
    GALLERY_INDICATOR_ITEM_SELECTED_CLASS = GALLERY_INDICATOR_ITEM_CLASS + "-selected",
    GALLERY_IMAGE_CLASS = "dx-gallery-item-image",
    GALLERY_ITEM_DATA_KEY = "dxGalleryItemData",
    MAX_CALC_ERROR = 1;
var GalleryNavButton = Widget.inherit({
    _supportedKeys: function() {
        return extend(this.callBase(), {
            pageUp: commonUtils.noop,
            pageDown: commonUtils.noop
        })
    },
    _getDefaultOptions: function() {
        return extend(this.callBase(), {
            direction: "next",
            onClick: null,
            hoverStateEnabled: true,
            activeStateEnabled: true
        })
    },
    _render: function() {
        this.callBase();
        var that = this,
            $element = this.$element(),
            eventName = eventUtils.addNamespace(clickEvent.name, 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._defaultTemplates.item = new BindableTemplate(function($container, data) {
            var $img = $("<img>").addClass(GALLERY_IMAGE_CLASS);
            if (typeUtils.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", "html", "text"], 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 itemPerPage = this.option("wrapAround") ? this._itemsPerPage() + 1 : this._itemsPerPage();
        if (this.option("stretchImages")) {
            return 1 / itemPerPage
        }
        if (this.option("wrapAround")) {
            return this._itemPercentWidth() * this._itemsPerPage() / (this._itemsPerPage() + 1)
        }
        return this._itemPercentWidth()
    },
    _itemPercentWidth: function() {
        var percentWidth, elementWidth = this.$element().outerWidth(),
            initialItemWidth = this.option("initialItemWidth");
        if (initialItemWidth && initialItemWidth <= elementWidth) {
            percentWidth = this.option("initialItemWidth") / elementWidth
        } else {
            percentWidth = 1
        }
        return percentWidth
    },
    _itemsPerPage: function() {
        var itemsPerPage = Math.floor(1 / this._itemPercentWidth());
        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
    },
    _render: function() {
        this.$element().addClass(GALLERY_CLASS);
        this.$element().toggleClass(GALLERY_LOOP_CLASS, this.option("loop"));
        this._renderDragHandler();
        this._renderWrapper();
        this._renderItemsContainer();
        this.callBase();
        this._renderContainerPosition();
        this._renderItemSizes();
        this._renderItemPositions();
        this._renderNavButtons();
        this._renderIndicator();
        this._renderSelectedItem();
        this._renderItemVisibility();
        this._renderUserInteraction();
        this._setupSlideShow();
        this._reviseDimensions();
        this.setAria({
            role: "listbox",
            label: "gallery"
        });
        this._fireContentReadyAction()
    },
    _renderContent: function() {
        this._renderContentImpl()
    },
    _dimensionChanged: function() {
        var selectedIndex = this.option("selectedIndex") || 0;
        this._stopItemAnimations();
        this._clearCacheWidth();
        this._renderDuplicateItems();
        this._renderItemSizes();
        this._renderItemPositions();
        this._renderIndicator();
        this._renderContainerPosition(this._calculateIndexOffset(selectedIndex), true);
        this._renderItemVisibility()
    },
    _renderDragHandler: function() {
        var eventName = eventUtils.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) {
        this.callBase(items);
        this._loadNextPageIfNeeded()
    },
    _renderItemsContainer: function() {
        if (this._$container) {
            return
        }
        this._$container = $("<div>").addClass(GALLERY_ITEM_CONTAINER_CLASS).appendTo(this._$wrapper)
    },
    _renderDuplicateItems: function() {
        if (!this.option("loop")) {
            return
        }
        var i, items = this.option("items") || [],
            itemsCount = items.length,
            lastItemIndex = itemsCount - 1;
        if (!itemsCount) {
            return
        }
        this._getLoopedItems().remove();
        var duplicateCount = Math.min(this._itemsPerPage(), itemsCount);
        for (i = 0; i < duplicateCount; i++) {
            this._renderItem(0, items[i]).addClass(GALLERY_LOOP_ITEM_CLASS)
        }
        for (i = 0; i < duplicateCount; i++) {
            this._renderItem(0, items[lastItemIndex - i]).addClass(GALLERY_LOOP_ITEM_CLASS)
        }
    },
    _getLoopedItems: function() {
        return this.$element().find("." + GALLERY_LOOP_ITEM_CLASS)
    },
    _emptyMessageContainer: function() {
        return this._$wrapper
    },
    _renderItemSizes: function(startIndex) {
        var $items = this._itemElements(),
            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(),
            itemsCount = this._itemsCount(),
            itemsPerPage = this._itemsPerPage(),
            loopItemsCount = this.$element().find("." + GALLERY_LOOP_ITEM_CLASS).length,
            lastItemDuplicateIndex = itemsCount + loopItemsCount - 1,
            offsetRatio = this.option("wrapAround") ? .5 : 0,
            freeSpace = this._itemFreeSpace(),
            rtlEnabled = this.option("rtlEnabled");
        this._itemElements().each(function(index) {
            var realIndex = index;
            if (index > itemsCount + itemsPerPage - 1) {
                realIndex = lastItemDuplicateIndex - realIndex - itemsPerPage
            }
            var itemPosition = itemWidth * (realIndex + offsetRatio) + freeSpace * (realIndex + 1 - offsetRatio);
            $(this).css(rtlEnabled ? "right" : "left", 100 * itemPosition + "%")
        });
        this._relocateItems(this.option("selectedIndex"), this.option("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 positionReady, that = this,
            itemWidth = this._actualItemWidth(),
            targetIndex = offset,
            targetPosition = this._offsetDirection() * targetIndex * (itemWidth + this._itemFreeSpace());
        if (typeUtils.isDefined(this._animationOverride)) {
            animate = this._animationOverride;
            delete this._animationOverride
        }
        if (animate) {
            that._startSwipe();
            positionReady = that._animate(targetPosition).done(that._endSwipe.bind(that))
        } else {
            translator.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,
            $container = this._$container,
            animationComplete = new Deferred;
        fx.animate(this._$container, extend({
            type: "slide",
            to: {
                left: targetPosition * this._elementWidth()
            },
            duration: that.option("animationDuration"),
            complete: function() {
                if (that._needMoveContainerForward()) {
                    translator.move($container, {
                        left: 0,
                        top: 0
                    })
                }
                if (that._needMoveContainerBack()) {
                    translator.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(),
            actualPosition = -this._maxItemWidth() * this._elementWidth() * this._itemsCount();
        return expectedPosition <= actualPosition + MAX_CALC_ERROR
    },
    _needMoveContainerBack: function() {
        var expectedPosition = this._$container.position().left * this._offsetDirection(),
            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 that = this,
            $firstItem = that._itemElements().first().find(".dx-item-content");
        if (!$firstItem || $firstItem.is(":hidden")) {
            return
        }
        if (!that.option("height")) {
            that.option("height", $firstItem.outerHeight())
        }
        if (!that.option("width")) {
            that.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);
        for (var i = 0; i < this._pagesCount(); i++) {
            $("<div>").addClass(GALLERY_INDICATOR_ITEM_CLASS).appendTo(indicator)
        }
        this._renderSelectedPageIndicator()
    },
    _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"),
            lastIndex = this._pagesCount() - 1,
            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(),
            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)
        });
        var indicatorSelectAction = this._createAction(this._indicatorSelectHandler);
        eventsEngine.off(rootElement, eventUtils.addNamespace(clickEvent.name, this.NAME), GALLERY_INDICATOR_ITEM_SELECTOR);
        eventsEngine.on(rootElement, eventUtils.addNamespace(clickEvent.name, this.NAME), GALLERY_INDICATOR_ITEM_SELECTOR, function(e) {
            indicatorSelectAction({
                event: e
            })
        })
    },
    _indicatorSelectHandler: function(args) {
        var e = args.event,
            instance = args.component;
        if (!instance.option("indicatorEnabled")) {
            return
        }
        var indicatorIndex = $(e.target).index(),
            itemIndex = instance._fitPaginatedIndex(indicatorIndex * instance._itemsPerPage());
        instance._needLongMove = true;
        instance.option("selectedIndex", itemIndex);
        instance._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(),
            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(),
            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._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"),
            loop = this.option("loop"),
            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(),
            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,
            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"),
                startOffset = itemsCount - selectedIndex - this._itemsPerPage(),
                endOffset = selectedIndex,
                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(),
            selectedIndex = this.option("selectedIndex"),
            newIndex = this._fitIndex(selectedIndex - targetOffset),
            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: commonUtils.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) {
        var value = args.value;
        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.option("loopItemFocus", value);
                this.$element().toggleClass(GALLERY_LOOP_CLASS, value);
                this._renderDuplicateItems();
                this._renderItemPositions();
                this._renderNavButtonsVisibility();
                return;
            case "showIndicator":
                this._renderIndicator();
                return;
            case "showNavButtons":
                this._renderNavButtons();
                return;
            case "slideshowDelay":
                this._setupSlideShow();
                return;
            case "wrapAround":
            case "stretchImages":
                this._renderItemSizes();
                this._renderItemPositions();
                this._renderItemVisibility();
                break;
            case "swipeEnabled":
            case "indicatorEnabled":
                this._renderUserInteraction();
                return;
            default:
                this.callBase(args)
        }
    },
    goToItem: function(itemIndex, animation) {
        var selectedIndex = this.option("selectedIndex"),
            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);
module.exports = Gallery;
module.exports.default = module.exports;
