Source: components/guide.js

define('application/components/guide', [
    'rofl/widgets/component',
    'rofl/widgets/container',
    'rofl/widgets/label',
    'rofl/widgets/button',
    'rofl/widgets/image',
    'rofl/lib/l10n',
    'rofl/widgets/verticallist',
    'application/widgets/clock',
    'antie/runtimecontext',
    'application/managers/epg',
    'application/models/configuration',
    'rofl/lib/utils',
    'rofl/events/keyevent',
    'application/widgets/guide/pointerhorizontalcontainer',
    'application/utils',
    'application/widgets/infoblock',
    'application/widgets/guide/verticalcarousel',
    'application/managers/channel',
    'rofl/analytics/web/google',
    'antie/widgets/horizontallist',
    'application/widgets/guide/directdial',
    'application/managers/genre',
    'application/widgets/pointerfocusablebutton',
    'application/widgets/filters/button',
    'application/widgets/guide/filters/day',
    'application/formatters/guide',
    'application/managers/halo'
], function (
    Component,
    Container,
    Label,
    Button,
    Image,
    L10N,
    VerticalList,
    Clock,
    RuntimeContext,
    EpgManager,
    KPNConfiguration,
    Utils,
    KeyEvent,
    HorizontalCarousel,
    AppUtils,
    InfoBlock,
    VerticalCarousel,
    ChannelManager,
    GoogleAnalytics,
    HorizontalList,
    DirectDial,
    GenreManager,
    PointerFocusableButton,
    FilterButton,
    DayFilterButton,
    GuideFormatter,
    HaloManager
) {
    'use strict';

    var Guide,
        l10n = L10N.getInstance(),
        application = RuntimeContext.getCurrentApplication(),
        config = application.getConfiguration(),
        epgManager = EpgManager.getInstance(),
        channelManager = ChannelManager.getInstance(),
        carouselsDimensions = application.getLayout().guide,
        GA = GoogleAnalytics.getInstance(),
        infoblockConfig = {
            id: 'back-on-top-block',
            text: l10n.get('infoblock'),
            classname: ['icon', 'icon-back-v2'],
            position: 'right'
        },
        DEFAULT_FILTER_BACK_DAYS = Utils.getNested(config, 'guide', 'backDays') || 6,
        DEFAULT_FILTER_FORWARD_DAYS = Utils.getNested(config, 'guide', 'forwardDays') || 4,
        guideFormatter = new GuideFormatter();

    Guide = Component.extend({

        /**
         * Initialises the component.
         */
        init: function init () {
            init.base.call(this, 'guide');
            this._filterTime = this._getCurrentTimeframeStart();
            this._activeVerticalIndex = null;

            this._build();
            this._onKeyDownBound = Utils.bind(this._onKeyDown, this);
            this._onSelectBound = Utils.bind(this._onSelect, this);
            this._onFocusBound = Utils.bind(this._onFocus, this);
            this._onSelectedItemChangeBound = Utils.bind(this._onSelectedItemChange, this);
            this._unlockAssetsBound = Utils.bind(this._unlockAssets, this);
            this._lockAssetsBound = Utils.bind(this._lockAssets, this);
            this._onVisibilityChangeBound = Utils.bind(this._onVisibilityChange, this);
        },

        /**
         * Builds the component.
         *
         * @private
         */
        _build: function () {
            this._buildPageContainer();
            this._buildHeader();
            this._buildGradient();
            this._buildTitle();
            this._buildSubtitle();
            this._buildClock();
            this._buildFiltersContainer();
            this._buildDayFilters();
            this._buildTimeFilters();
            this._buildNowFilter();
            this._buildGenreFilters();
            this._buildVerticalCarousel();
            this._buildInfoBlock();
            this._buildDirectDial();
            this._buildNoResultsMessage();
        },

        /**
         * Builds the header.
         *
         * @private
         */
        _buildPageContainer: function () {
            var pageContainer = this._pageContainer = new VerticalList();

            this.appendChildWidget(pageContainer);
        },

        /**
         * Builds the header.
         *
         * @private
         */
        _buildHeader: function () {
            var header = this._header = new Container(),
                branding = this._branding = new Container();

            header.addClass('header');
            branding.addClass('page-branding');
            header.appendChildWidget(branding);
            this._pageContainer.appendChildWidget(header);
        },

        /**
         * Builds the gradient.
         *
         * @private
         */
        _buildGradient: function () {
            var gradient = this._gradient = new Container();

            gradient.addClass('header-gradient');

            this._header.appendChildWidget(gradient);
        },

        /**
         * Builds the header.
         *
         * @private
         */
        _buildFiltersContainer: function () {
            var filtersContainer = this._filterscontainer = new HorizontalList();

            filtersContainer.addClass('filters-container');
            filtersContainer.addClass('filters');

            this._header.appendChildWidget(filtersContainer);
        },

        /**
         * Builds the title.
         *
         * @private
         */
        _buildTitle: function () {
            var title = this._title = new Label({ text: l10n.get('guide.title'), classNames: ['top-title'] });

            this._branding.appendChildWidget(title);
        },

        /**
         * Builds the subtitle.
         *
         * @private
         */
        _buildSubtitle: function () {
            var subtitle = new Label({ text: l10n.get('guide.subtitle'), classNames: ['main-subtitle'] });

            this._header.appendChildWidget(subtitle);
        },

        /**
         * Builds the clock.
         *
         * @private
         */
        _buildClock: function () {
            var clock = new Clock();

            clock.addClass('header-clock');
            this._header.appendChildWidget(clock);
        },

        /**
         * Builds now filter.
         *
         * @private
         */
        _buildNowFilter: function () {
            var nowFilter = new PointerFocusableButton(),
                label = new Label({ text: l10n.get('guide.now'), classNames: ['v-align-target'] }),
                helper = new Label({ text: '', classNames: ['v-align-helper'] });

            nowFilter.addClass(['filter', 'now-filter', 'icon-button']);
            nowFilter.appendChildWidget(helper);
            nowFilter.appendChildWidget(label);

            this._filterscontainer.appendChildWidget(nowFilter);
        },

        /**
         * Builds day filters.
         *
         * @private
         */
        _buildDayFilters: function () {
            var dateString = [
                    l10n.get('asset.today')
                ].join(' '),
                dayFilters = this._dayfilters = new DayFilterButton({
                    text: dateString,
                    id: 'day-filter-btn',
                    isDropDown: true,
                    classList: 'icon-button'
                });

            this._filterscontainer.appendChildWidget(dayFilters);
        },

        /**
         * Builds day filters.
         *
         * @private
         */
        _buildTimeFilters: function () {
            var timeFilters = this._timefiltersButton = new FilterButton({
                text: this._filterTime.getHours() + ':00',
                id: 'filter-time-btn',
                isDropDown: true,
                classList: 'icon-button'
            });

            this._filterscontainer.appendChildWidget(timeFilters);
        },

        /**
         * Builds genre filters.
         *
         * @private
         */
        _buildGenreFilters: function () {
            var genreFilters = this._genrefiltersButton = new FilterButton({
                    text: l10n.get('genre.all'),
                id: 'filter-genre-btn',
                isDropDown: true,
                classList: 'icon-button'
            });

            this._filterscontainer.appendChildWidget(genreFilters);
        },

        /**
         * Builds channel logo and number.
         *
         * @param {Array} items - Array of items.
         * @returns {Object} Container - A new Container with channelinfo.
         *
         * @private
         */
        _buildChannelLogoAndNumber: function (items) {
            var channlInfoContainer = new Container(),
                channellogo = new Image(),
                channelNumber = channelManager.getChannelNumberForId(items[0].getChannelId()),
                labelNum = new Label({ text: channelNumber }),
                logoSrc;

            logoSrc = items[0].getChannelLogo();
            channellogo.setSrc(logoSrc);

            channlInfoContainer.addClass('channel-info');
            channlInfoContainer.appendChildWidget(labelNum);
            channlInfoContainer.appendChildWidget(channellogo);

            return channlInfoContainer;
        },

        /**
         * Builds the infoblock.
         *
         * @private
         */
        _buildInfoBlock: function () {
            var infoblock = this._infoblock = new InfoBlock(infoblockConfig);

            infoblock.addClass('info-block');
            this.appendChildWidget(infoblock);
        },

        /**
         * Builds the vertical carousel.
         *
         * @private
         */
        _buildVerticalCarousel: function () {
            var verticalCarousel = this._verticalCarousel = new VerticalCarousel();

            this._pageContainer.appendChildWidget(verticalCarousel);
        },

        /**
         * Builds the direct dial.
         *
         * @private
         */
        _buildDirectDial: function () {
            var directDial = this._directDial = new DirectDial();

            this.appendChildWidget(directDial);
        },

        /**
         * Builds the no results message.
         *
         * @private
         */
        _buildNoResultsMessage: function () {
            var noResultsLabel = this._noResultsLabel = new Label({ classNames: ['noresults'] });

            this.appendChildWidget(noResultsLabel);
        },

        /**
         * Builds the vertical carousel.
         *
         * @param {Object} currentDate - Current date.
         * @param {Array} datesArray - Array of dates.
         * @returns {number} Index.
         * @private
         */
        _getCurrentDateIndex: function (currentDate, datesArray) {
            var i, j, index;

            for (i = 0, j = datesArray.length; i < j; i++) {

                if (currentDate.getDate() === datesArray[i].getDate()) {
                    index = i;
                    break;
                }
            }

            return index;

        },

        /**
         * Builds the carousel.
         *
         * @param {Object} items - Items.
         * @returns {Object} Carousel - A new HorizontalCarousel.
         * @private
         */
        _buildCarousel: function (items) {
            var datesArray = this._generateDateArray(),
                dateStringsArray = this._getFilterDays(),
                dayIndex = this._getCurrentDateIndex(this._filterDate, datesArray),
                horizontalCarousel = new HorizontalCarousel(items, dayIndex, this._filterDate, dateStringsArray);

            horizontalCarousel.addClass('carousel-row');

            return horizontalCarousel;
        },

        /**
         * BeforeRender event.
         */
        onBeforeRender: function () {
            var now = application.getDate(),
                time = now.getHours(),
                day;

            time = time % 2 === 0 ? time : time - 1;
            time = this._convertToTime(time) + ':00';
            day = [
                l10n.get('asset.today')
            ].join(' ');

            this._setFilter('time', time);
            this._setFilter('day', day);
            this._setFilter('genre', l10n.get('genre.all'));

            this._loadingTime = application.getDate();
        },

        /**
         * OnBeforeShow event handler.
         */
        onBeforeShow: function () {
            var dayStart = application.getDate(),
                value = ((application.getDate() - this._loadingTime) / 1000).toFixed(2);

            this._activeTimeframe = '_nowIndex';
            this._dayfilters.focus();
            dayStart.setHours(2, 0, 0, 0);

            GA.onPageView('guide');
            this._filterGenre = null;
            this._startGuidePage = application.getDate();
            this._infoblock.hide({skipAnim: true});
            this._directDial.hide({skipAnim: true});
            this._setEventListeners();
            this._reset();

            this._loadData(dayStart, null, '_nowIndex');

            GA.onEvent('page', 'load', {
                eventLabel: 'guide',
                eventValue: value
            });

            this._updateProgressbars();

            HaloManager.getInstance()
                .getServiceMessage()
                .then(function (r) {
                    if (r) {
                        application.route('service');
                    }
                });
        },

        /**
         * OnBeforeHide event handler.
         *
         */
        onBeforeHide: function () {
            var activeFilter = this._dayfilters.getActiveChildWidget(),
                duration = ((application.getDate() - this._startGuidePage) / 60000).toFixed(2),
                now = application.getDate();

            if (activeFilter) {
                activeFilter.removeClass('selected');
            }

            this._genrefiltersButton.removeClass('selected');
            this._filterTime = now;
            clearInterval(this._progressInterval);
            this._progressInterval = null;
            this._removeEventListeners();

            GA.onEvent('page', 'time', {
                eventLabel: 'search',
                eventValue: duration
            });
        },

        /**
         * Sets the event listeners.
         *
         * @private
         */
        _setEventListeners: function () {
            this.addEventListener('selecteditemchange', this._onSelectedItemChangeBound);
            this.addEventListener('mousefocus', this._onFocusBound);
            application.addEventListener('$unlocked', this._unlockAssetsBound);
            application.addEventListener('$locked', this._lockAssetsBound);
            document.addEventListener('visibilitychange', Utils.bind(this._onVisibilityChangeBound, this));

            this._setSelectListener();
        },

        /**
         * Removes the select listener.
         *
         * @private
         */
        _setSelectListener: function () {

            if (this._listening) {
                return;
            }

            this._listening = true;
            this.addEventListener('select', this._onSelectBound);
            this.addEventListener('keydown', this._onKeyDownBound);
        },

        /**
         * Removes the select listener.
         *
         * @private
         */
        _removeSelectListener: function () {

            if (!this._listening) {
                return;
            }

            this._listening = false;
            this.removeEventListener('select', this._onSelectBound);
            this.removeEventListener('keydown', this._onKeyDownBound);

        },

        /**
         * Removes the event listeners.
         *
         * @private
         */
        _removeEventListeners: function () {
            this.removeEventListener('selecteditemchange', this._onSelectedItemChangeBound);
            this.removeEventListener('mousefocus', this._onFocusBound);
            application.removeEventListener('$unlocked', this._unlockAssetsBound);
            application.removeEventListener('$locked', this._lockAssetsBound);

            this._removeSelectListener();
        },

        /**
         * Returns actual timeframe.
         *
         * @returns {Date} Actual date object.
         * @private
         */
        _getCurrentTimeframeStart: function () {
            var date = application.getDate(),
                hours;

            hours = date.getHours() % 2 ? date.getHours() - 1 : date.getHours();
            date.setHours(hours, 0, 0, 0);
            return date;
        },

        /**
         * KeyDown event.
         *
         * @param {Object} e - The event data.
         * @private
         */
        _onKeyDown: function (e) {

            switch (e.keyCode) {
                case KeyEvent.VK_LEFT:
                    e.stopPropagation();
                    e.preventDefault();
                    application.focusMenu('main');
                    break;
                case KeyEvent.VK_RIGHT:
                    e.preventDefault();
                    e.stopPropagation();
                    break;
                case KeyEvent.VK_BACK:
                    e.stopPropagation();
                    e.preventDefault();
                    this._onBack();
                    break;
                case KeyEvent.VK_CHANNEL_UP:

                    if (this._verticalCarousel.getChildWidgetCount()) {
                        this._moveGuide(1);
                    }
                    break;
                case KeyEvent.VK_CHANNEL_DOWN:

                    if (this._verticalCarousel.getChildWidgetCount()) {
                        this._moveGuide(0);
                    }
                    break;
                case KeyEvent.VK_0:
                case KeyEvent.VK_1:
                case KeyEvent.VK_2:
                case KeyEvent.VK_3:
                case KeyEvent.VK_4:
                case KeyEvent.VK_5:
                case KeyEvent.VK_6:
                case KeyEvent.VK_7:
                case KeyEvent.VK_8:
                case KeyEvent.VK_9:
                    this._onDirectDialInput(e.keyChar);
                    break;

            }
        },

        /**
         * Focus event.
         *
         * @private
         */
        _onFocus: function () {
            var verticalCarousel = this._verticalCarousel,
                lastIndex = verticalCarousel._aligner._lastAlignIndex,
                activeCarouselRow = verticalCarousel.getActiveChildWidget(),
                index = verticalCarousel.getActiveChildIndex();

            if (activeCarouselRow.isFocussed() && (lastIndex !== index)) {
                activeCarouselRow.determinePointers(index);
                this._activeVerticalIndex = index;
                verticalCarousel.alignToIndex(index);
                this._activeVerticalIndex = null;
            }
        },

        /**
         * OnSelectedItemChange event.
         *
         * @param {Object} e - The event data.
         * @private
         */
        _onSelectedItemChange: function (e) {
            var target = e.target,
                item = e.item,
                index = e.index,
                activeRowNumber = index + 1,
                count = this._verticalCarousel.getChildWidgetCount() - 1,
                loadmoreRows = config.loading.guide.loadMoreAfterRows,
                channelMap,
                direction,
                channelId,
                startIndex,
                itemChannelIndex;

            if (target.hasClass('time-filters')) {
                this.focus();
            }

            // Target is the vertical carousel.
            if (target.hasClass('big-carousel')) {
                channelMap = channelManager.getChannelMap();
                direction = target.parentWidget.parentWidget.getAligner().getDirection();
                channelId = this._getChannelIdByCarouselDirection(direction);
                startIndex = channelMap.indexOf(channelId);
                itemChannelIndex = channelMap.indexOf(item.getChannelId());

                // Going down
                if (direction === 0 && (count - index) === loadmoreRows) {

                    if (channelMap.length - 1 === startIndex) {
                        return;
                    }

                    if (!this._filterGenre && !this._loadingMoreData) {
                        this._loadingMoreData = true;
                        this._loadMore(this._filterDate, startIndex + 1, startIndex + 20)
                            .then(Utils.bind(function (data) {
                                this._setLoadmoreData(data, direction);
                                this._loadingMoreData = false;
                            }, this));
                    }
                }

                // Going up
                if (direction === 1 && ((index === loadmoreRows) || (itemChannelIndex - startIndex === loadmoreRows - 1))) {

                    if (startIndex === 0) {
                        return;
                    }

                    if (!this._filterGenre && !this._loadingMoreData) {
                        this._loadingMoreData = true;
                        this._loadMore(this._filterDate, startIndex - 21, startIndex)
                            .then(Utils.bind(function (data) {
                                this._setLoadmoreData(data, direction);
                                this._loadingMoreData = false;
                            }, this));
                    }
                }

                if (activeRowNumber !== 1) {
                    this._infoblock.show();
                } else {
                    this._infoblock.hide();
                }

                if (index === 1) {
                    this._gradient.addClass('active-gradient');
                } else if (index === 0) {
                    this._gradient.removeClass('active-gradient');
                }
            }

            if (target.parentWidget.parentWidget.parentWidget.parentWidget === this) {

                this._updateFilter(item.getMask().getWidgetStrip().getActiveChildWidget(), true);
            }

            if (item.hasClass('focus')) {

                if (item.hasClass('asset')) {
                    this._updateFilter(item);
                } else if (item.hasClass('next-day-button')) {
                    this._startAlignPositionTimeout(1);
                } else if (item.hasClass('prev-day-button')) {
                    this._startAlignPositionTimeout(0);
                }
            }
        },

        /**
         * Get the channel ID based on the scroll direction of the Carousel.
         *
         * @param {number} direction - The direction Aligner.directions.FORWARD or Aligner.directions.BACKWARD.
         * @returns {number} - The channel ID.
         * @private
         */
        _getChannelIdByCarouselDirection: function (direction) {
            var lastIndex = this._verticalCarousel.getChildWidgets().length - 1;

            // Down
            if (direction === 0) {
                return this._verticalCarousel.getChildWidgetByIndex(lastIndex).getChannelId();
            }

            // Up
            if (direction === 1) {
                return this._verticalCarousel.getChildWidgetByIndex(0).getChannelId();
            }
        },

        /**
         * Updates the filter for the given item.
         *
         * @param {Object} item - The item.
         * @param {boolean} [direct] - Should the aligning happen direct.
         * @private
         */
        _updateFilter: function (item, direct) {
            var now = application.getDate(),
                itemDate,
                filter,
                dataItem = item.getDataItem();

            if (!item) {
                return;
            }

            if (dataItem) {
                itemDate = dataItem.endTime;
            }

            clearTimeout(this._timer);

            if (!itemDate) {
                if (direct) {
                    this.timeAlignItem(this._activeTimeframe);
                } else {

                    if (item.hasClass('next-day-button')) {
                        filter = '_lastIndex';
                    } else if (item.hasClass('prev-day-button')) {
                        filter = '_firstIndex';
                    }

                    this._setTimer(filter);
                }
                return;
            }

            clearTimeout(this._timer);
            this._setFilter('time', this._filterTimeString(itemDate.getHours()));

            filter = itemDate.getTime();

            if (item.startTime < now && itemDate > now) {
                filter = '_nowIndex';
            }

            if (direct) {
                this.timeAlignItem(this._activeTimeframe);
            } else {
                this._setTimer(filter);
            }
        },

        /**
         * Load content of present time.
         *
         * @private
         */
        _nowFilterSelectionSetting: function () {
            var today = application.getDate(),
                startDate = application.getDate(),
                hours = today.getHours(),
                dateString;

            if (this._genrefiltersButton.hasClass('selected')) {
                this._genrefiltersButton.removeClass('selected');
            }

            clearTimeout(this._timer);
            startDate.setHours(2, 0, 0, 0);
            this._activeVerticalIndex = this._verticalCarousel.getActiveIndex();
            this._filterGenre = null;
            this._loadData(startDate, null, '_nowIndex');
            dateString = [
                l10n.get('asset.today')
            ].join(' ');

            hours = this._filterTimeString(hours);

            this._setFilter('day', dateString);
            this._setFilter('time', hours);
            this._setFilter('genre', l10n.get('genre.all'));
            this._filterTime = today;
        },

        /**
         * Popup a modal to filter a time.
         *
         * @private
         */
        _timeFilterSelectionSetting: function () {
            var timeArray = this._generateHours(2);

            clearTimeout(this._timer);

            application.route('dropdown', {
                dataOptions: this._getFilterHours(2),
                dataValues: timeArray,
                prevValue: this._timefiltersButton.getText(),
                filter: 'time',
                callback: Utils.bind(this._onDropdownCallback, this),
                showGoBackButton: true
            });
        },

        /**
         * Popup a modal to filter a genre.
         *
         * @private
         */
        _genreFilterSelectionSetting: function () {
            var genreArray = [],
                genreValues = [],
                genres = GenreManager.GENRES,
                all = l10n.get('genre.all');

            genreArray.push(all);
            genreValues.push({
                name: all,
                values: null
            });

            Utils.each(genres, function (genre) {
                genreArray.push(genre.name);
                genreValues.push(genre);
            });

            clearTimeout(this._timer);

            application.route('dropdown', {
                dataOptions: genreArray,
                dataValues: genreValues,
                prevValue: this._genrefiltersButton.getText(),
                filter: 'genre',
                callback: Utils.bind(this._onDropdownCallback, this),
                showGoBackButton: true
            });
        },

        /**
         * Popup a modal to filter a day.
         *
         * @private
         */
        _dayFilterSelectionSetting: function () {

            clearTimeout(this._timer);

            application.route('dropdown', {
                dataOptions: this._getFilterDays(),
                dataValues: this._generateDateArray(),
                prevValue: this._dayfilters.getText(),
                filter: 'day',
                callback: Utils.bind(this._onDropdownCallback, this),
                showGoBackButton: true
            });
        },

        /**
         * Function fires after dropdown item is selected.
         *
         * @param {Object} event - Selected item.
         * @private
         */
        _onDropdownCallback: function (event) {
            var target,
                filterName,
                valueObject,
                date,
                time;

            event = event || {};
            target = event.target || {};
            filterName = target._filterName;
            valueObject = target._itemValueObject;

            switch (filterName) {
                case 'time':
                    date = new Date(this._filterDate);
                    date.setHours(
                        valueObject.getHours(),
                        valueObject.getMinutes(),
                        valueObject.getSeconds(),
                        0
                    );
                    time = this._convertToTime(date.getHours()) + ':00';
                    this.timeAlignVisible(date.getTime());
                    this._setFilter('time', time);
                    this._filterTime = date;
                    this._activeTimeframe = date.getTime();
                    break;
                case 'day':
                    date = new Date(valueObject);
                    valueObject.setHours(
                        this._filterTime.getHours(),
                        this._filterTime.getMinutes(),
                        this._filterTime.getSeconds(),
                        0);
                    this._filterDate = valueObject;
                    this._filterTime = valueObject;
                    date.setHours(2, 0, 0, 0);

                    this._activeVerticalIndex = this._verticalCarousel.getActiveIndex();
                    this._setFilter('day', target._labelText);

                    this._loadData(date, null, valueObject.getTime());
                    break;
                case 'genre':
                    this._filterGenre = valueObject;
                    this._setFilter('genre', valueObject.name);

                    if (!valueObject.values) {
                        this._filterGenre = null;
                        this._activeVerticalIndex = 0;
                        this._genrefiltersButton.removeClass('selected');
                    } else {
                        if (!this._genrefiltersButton.hasClass('selected')) {
                            this._genrefiltersButton.addClass('selected');
                        }
                    }

                    GA.onEvent('guide', 'select-genre', {
                        eventLabel: valueObject.name
                    });

                    this._filterDate.setHours(2, 0, 0, 0);
                    this._loadData(this._filterDate, null, this._filterDate.getTime());

                    break;
            }
        },

        /**
         * Get a generated array of days in string form.
         *
         * @param {Integer} [num] - Number of generated days.
         * @returns {Array} Days in string.
         * @private
         */
        _getFilterDays: function (num) {
            var daysStringArray = [],
                i,
                dateArray,
                dateString,
                currentDay = Math.floor(application.getDate().getTime() / 1000 / 60 / 60 / 24);

            num = num || DEFAULT_FILTER_FORWARD_DAYS + DEFAULT_FILTER_BACK_DAYS + 1;
            dateArray = this._generateDateArray();

            for (i = 0; i < num; i++) {

                switch (Math.floor(dateArray[i].getTime() / 1000 / 60 / 60 / 24)) {
                    case currentDay:
                        dateString = [
                            l10n.get('asset.today')
                        ].join(' ');
                        break;
                    case (currentDay + 1):
                        dateString = [
                            l10n.get('asset.tomorrow')
                        ].join(' ');
                        break;
                    case (currentDay - 1):
                        dateString = [
                            l10n.get('asset.yesterday')
                        ].join(' ');
                        break;
                    default:
                        dateString = [
                            l10n.get('asset.days.' + dateArray[i].getDay()),
                            dateArray[i].getDate(),
                            l10n.get('asset.monthsLong.' + dateArray[i].getMonth())
                        ].join(' ');
                }

                daysStringArray.push(dateString);
            }

            return daysStringArray;
        },

        /**
         * Generate array of dates.
         *
         * @param {number} back - Number of back days.
         * @param {number} forward - Number of forward days.
         * @returns {Array} Days.
         * @private
         */
        _generateDateArray: function (back, forward) {
            var i,
                date,
                backDays = back || DEFAULT_FILTER_BACK_DAYS,
                forwardDays = forward || DEFAULT_FILTER_FORWARD_DAYS,
                days = [];

            for (i = -backDays; i <= -1; i++) {  // Generates back days.
                date = this._generateDate(i);
                days.push(date);
            }

            days.push(this._generateDate(0));   // Generates today.

            for (i = 1; i <= forwardDays; i++) { // Generates forward days.
                date = this._generateDate(i);
                days.push(date);
            }

            return days;
        },

        /**
         * Generate a day.
         *
         * @param {Integer} i - Position due to this day.
         * @returns {Date} Generated date.
         * @private
         */
        _generateDate: function (i) {
            var day = application.getDate(),
                today = application.getDate();

            day.setDate(today.getDate() + i);
            return day;
        },

        /**
         * Generates an array of hours in string.
         *
         * @param {Integer} step - Step for another hour.
         * @returns {Array} Array of hours in string.
         * @private
         */
        _getFilterHours: function (step) {
            var i = 2,
                stringHoursArray = [];

            for (i; i < 24; i += step) {
                if (i < 10) {
                    stringHoursArray.push('0' + i + ':00');
                } else {
                    stringHoursArray.push(i + ':00');
                }
            }
            stringHoursArray.push('00:00');
            return stringHoursArray;
        },

        /**
         * Generates an hour array.
         *
         * @param {Integer} step - Step for another hour.
         * @returns {Array} Generated integer hours array.
         * @private
         */
        _generateHours: function (step) {
            var hours = [],
                time = 2,
                day;

            while (time < 24) {
                day = new Date(this._filterTime);
                day.setHours(time, 0, 0, 0);
                hours.push(day);
                time += step;
            }
            day = application.getDate();
            day.setHours(0, 0, 0, 0);
            hours.push(day);

            return hours;
        },

        /**
         * Upload text filter value.
         *
         * @param {string} filter - Filter name.
         * @param {string} value - Value which should be set.
         * @private
         */
        _setFilter: function (filter, value) {

            switch (filter) {
                case 'now':
                    break;
                case 'day':
                    this._dayfilters.setText(value);
                    break;
                case 'time':
                    this._timefiltersButton.setText(value);
                    break;
                case 'genre':
                    this._genrefiltersButton.setText(value);
                    break;
            }
        },

        /**
         * Select event.
         *
         * @param {Object} e - The event data.
         * @private
         */
        _onSelect: function (e) {
            var target = e.target,
                date = new Date(this._filterDate);

            if (target.id === 'back-on-top-block') {
                this._onBack();
                return;
            }

            if (target instanceof FilterButton) {
                this._onFilterSelect(target);
                return;
            }

            if (target.hasClass('now-filter')) {
                this._nowFilterSelectionSetting(target);
            } else if (target.hasClass('next-day-button')) {
                this._onSelectNextDay(date, target.getText());
            } else if (target.hasClass('prev-day-button')) {
                this._onSelectPreviousDay(date, target.getText());
            } else if (target.hasClass('asset')) {
                this._onSelectAsset(target);
            }
        },

        /**
         * Gets executed when the filter is selected.
         *
         * @param {Object} target - The target.
         * @private
         */
        _onFilterSelect: function (target) {
            switch (target.id) {
                case 'filter-genre-btn':
                    this._genreFilterSelectionSetting(target);
                    break;
                case 'day-filter-btn':
                    this._dayFilterSelectionSetting(target);
                    break;
                case 'filter-time-btn':
                    this._timeFilterSelectionSetting(target);
                    break;
            }
        },

        /**
         * Gets called when the next day is selected.
         *
         * @param {Date} date - The date.
         * @param {string} dayString - Date in string form.
         * @private
         */
        _onSelectNextDay: function (date, dayString) {

            date.setDate(this._filterDate.getDate() + 1);
            date.setHours(2, 0, 0, 0);

            this._activeVerticalIndex = this._verticalCarousel.getActiveIndex();
            this._filterDate = date;
            this._activeTimeframe = date.getTime();
            this._setFilter('day', dayString);

            this._loadData(date, null, date.getTime())
                .then(Utils.bind(function () {

                    this._setFilter('time', '02:00');

                }, this));
        },

        /**
         * Gets called when the previous day is selected.
         *
         * @param {Date} date - The date.
         * @param {string} dayString - Date in string form.
         * @private
         */
        _onSelectPreviousDay: function (date, dayString) {

            date.setDate(this._filterDate.getDate() - 1);
            date.setHours(2, 0, 0, 0);

            this._activeVerticalIndex = this._verticalCarousel.getActiveIndex();
            this._filterDate = date;
            this._setFilter('day', dayString);

            this._activeTimeframe = '_lastIndex';
            this._loadData(date, null, '_lastIndex')
                .then(Utils.bind(function () {

                    this._setFilter('time', '00:00');

                }, this));
        },

        /**
         * Unlock the assets.
         *
         * @private
         */
        _unlockAssets: function () {
            var verticalCarousel = this._verticalCarousel,
                mask = verticalCarousel.getMask(),
                rows = mask.getChildWidgetByIndex(0).getChildWidgets(),
                assets;

            Utils.each(rows, function (row) {
                assets = row.getCarousel().getMask().getChildWidgetByIndex(0).getChildWidgets();

                    Utils.each(assets, function (asset) {
                        if (!asset.hasClass('prev-day-button') &&
                            !asset.hasClass('next-day-button')) {

                            asset.unlock();
                        }
                    });
            });
        },

        /**
         * Locks the assets.
         *
         * @private
         */
        _lockAssets: function () {
            var verticalCarousel = this._verticalCarousel,
                mask = verticalCarousel.getMask(),
                rows = mask.getChildWidgetByIndex(0).getChildWidgets(),
                assets;

            Utils.each(rows, function (row) {
                assets = row.getCarousel().getMask().getChildWidgetByIndex(0).getChildWidgets();

                Utils.each(assets, function (asset) {
                    if (!asset.hasClass('prev-day-button') &&
                        !asset.hasClass('next-day-button')) {

                        asset.lock();
                    }
                });
            });
        },

        /**
         * Starts the progressbars update interval.
         */
        _updateProgressbars: function () {
            var gridAssets,
                currentProgramFinished,
                now = application.getDate(),
                dataItem,
                hasProgressbar,
                updateNextProgram,
                assetParentWidget,
                currentAssetParentWidget;

            if (this._progressInterval) {
                clearInterval(this._progressInterval);

                gridAssets = this._verticalCarousel.getCarouselItems();
                Utils.each(gridAssets, Utils.bind(function (gridAsset) {
                    hasProgressbar = gridAsset.updateProgressBar();
                    dataItem = gridAsset.getDataItem();

                    currentProgramFinished = now >= dataItem.endTime;
                    currentAssetParentWidget = gridAsset.parentWidget;

                    // Will update nex program if previous asset was updated and belongs to the same strip/channel lane.
                    if (updateNextProgram && assetParentWidget === currentAssetParentWidget) {
                        guideFormatter.format(dataItem.item, gridAsset);
                        updateNextProgram = false;
                    }

                    // Will update the current program (to be set to past).
                    if (currentProgramFinished && hasProgressbar) {
                        guideFormatter.format(dataItem.item, gridAsset);
                        updateNextProgram = true;
                        assetParentWidget = gridAsset.parentWidget;
                    }
                }, this));
            }

            this._progressInterval = setInterval(Utils.bind(this._updateProgressbars, this), 5000);
        },

        /**
         * Opens details modal.
         *
         * @param {Object} asset - Asset.
         * @private
         */
        _showAssetDetail: function (asset) {
            application.route('epgdetail', {
                data: asset,
                callback: Utils.bind(this.focus, this),
                callingPage: 'guide'
            });
        },

        /**
         * Gets called when the asset is selected.
         *
         * @param {Object} target - The target.
         * @private
         */
        _onSelectAsset: function (target) {
            var dataItem = target.getDataItem().item,
                carousels = this._verticalCarousel.getMask().getWidgetStrip().getChildWidgets();

            clearTimeout(this._timer);

            Utils.each(carousels, function (carousel) {
                carousel.completeAlignment();
            });

            if (target.isLocked()) {
                this._removeSelectListener();
                application.route('parentalpin', {
                    successCallback:
                        Utils.bind(function () {
                            this._showAssetDetail(dataItem);
                            this._unlockAssets();
                            this._setSelectListener();
                        }, this),
                    errorCallback: Utils.bind(this._setSelectListener, this),
                    escapeCallback: Utils.bind(this._setSelectListener, this)
                });
                return;
            }

            this._showAssetDetail(dataItem);
        },

        /**
         * Attempts to load the data for a given date.
         *
         * @param {Date} date - The date to load data for.
         * @param {number|null} [duration] - Duration to the end of the day.
         * @param {string|number} [alignIndex] - The alignindex.
         * @returns {Promise} - Promise resolving with data.
         * @private
         */
        _loadData: function (date, duration, alignIndex) {
            var durationTime = duration || (24 * 60 * 60) - 1,
                channelMap = channelManager.getChannelMap(),
                channelId = this._directDialChannel || this._getCurrentChannelId() ||
                    channelManager.getLastWatchedChannel() || channelMap[0],
                maxChannels = config.loading.guide.maxChannels,
                endIndex = maxChannels,
                index = channelMap.indexOf(channelId),
                maxIndex = channelMap.length - 1,
                channelNumber = channelManager.getChannelById(channelId).getNumber(),
                startIndex;

            this._directDialChannel = null;
            this._infoblock.hide({skipAnim: true});
            this._filterDate = date;
            application.showLoader(true);

            if (this._filterGenre) {
                return this._loadGenreData(this._filterGenre.name, alignIndex, channelId);
            }

            startIndex = index - (Math.floor(endIndex / 2));
            endIndex = index + (Math.ceil(endIndex / 2));

            if (startIndex < 0) {
                endIndex = endIndex + Math.abs(startIndex);
                startIndex = 0;
            } else if (endIndex > maxIndex) {
                startIndex = maxIndex - maxChannels;
                endIndex = maxIndex + 1;
            }

            return epgManager.getItems(
                channelManager.getChannelMap(startIndex, endIndex),
                Math.floor(date / 1000),
                durationTime)
                .then(Utils.bind(this._setData, this, date, alignIndex, channelNumber))
                ['catch'](function () {
                application.route('error', {
                    type: 'fullscreen',
                    title: L10N.getInstance().get('errors.cannot_load_page'),
                    button: {
                        id: 'error-close-button',
                        label: L10N.getInstance().get('errors.close')
                    },
                    imgUrl: 'src/assets/images/error-icon.png'
                });
            });
        },

        /**
         * Attempts to load the data for a given date, star and end index.
         *
         * @param {Date} date - The date to load data for.
         * @param {number} startIndex - Start index.
         * @param {number} endIndex - End index.
         * @returns {Promise} - Promise resolving with data.
         *
         * @private
         */
        _loadMore: function (date, startIndex, endIndex) {
            var durationTime = (24 * 60 * 60) - 1;

            if (startIndex < 0) {
                startIndex = 0;
            }

            return epgManager.getItems(
                channelManager.getChannelMap(startIndex, endIndex),
                Math.floor(date / 1000),
                durationTime)
                ['catch'](function () {
                application.route('error', {
                    type: 'fullscreen',
                    title: L10N.getInstance().get('errors.cannot_load_page'),
                    button: {
                        id: 'error-close-button',
                        label: L10N.getInstance().get('errors.close')
                    },
                    imgUrl: 'src/assets/images/error-icon.png'
                });
            });
        },

        /**
         * Attempts to load the genre data for the given key.
         *
         * @param {string} genreKey - The genre key.
         * @param {string|number} [alignIndex] - The align index.
         * @param {string|number} [channelId] - Channel Id we want to select.
         * @returns {Promise} - Promise resolving with the genre data set.
         * @private
         */
        _loadGenreData: function (genreKey, alignIndex, channelId) {
            var channelNumber = null;

            return GenreManager.getInstance().getData(genreKey, this._filterDate)
                .then(Utils.bind(function (data) {

                    if (data[channelId]) {
                        channelNumber = channelManager.getChannelById(channelId).getNumber();
                    }

                    this._genreFilteredData = data;
                    this._setData(this._filterDate, alignIndex, channelNumber, data);

                }, this));
        },

        /**
         * Applies timer to synch align carousels in guide.
         *
         * @param {Object} timeframe - Timeframe of focused asset.
         * @private
         */
        _setTimer: function (timeframe) {
            var self = this;

            clearTimeout(this._timer);

            this._timer = setTimeout(function () {
                self.timeAlignVisible(timeframe);
                self._activeTimeframe = timeframe;
            }, 2000);
        },

        /**
         * Start the timeout that aligns the guide to a certain position.
         *
         * @param {number} position - Position. 0 for the start, 1 for the end.
         * @private
         */
        _startAlignPositionTimeout: function (position) {
            var self = this,
                timeframe = position === 1 ? '_lastIndex' : '_firstIndex';

            clearTimeout(this._timer);

            this._timer = setTimeout(function () {
                self.timeAlignVisible(timeframe);
                self._activeTimeframe = timeframe;
            }, 2000);
        },

        /**
         * Append data to carousel.
         *
         * @param {Array} data - The data.
         * @param {number} direction - The direction Aligner.directions.FORWARD or Aligner.directions.BACKWARD.
         * @private
         */
        _setLoadmoreData: function (data, direction) {
            var verticalCarousel = this._verticalCarousel,
                widgetHeight = carouselsDimensions.carousels.widgetHeight,
                activeWidget = this._verticalCarousel.getActiveChildWidget(),
                fromLeftPosition = activeWidget.getFromLeftIndex(),
                carousel,
                alignIndex,
                channelLogoAndNumber,
                keys,
                i;

            data = this._formatEPGData(data);

            keys = Utils.keys(data);

            if (direction === 1) {
                keys = keys.reverse();
            }

            for (i = 0; i < keys.length; i++) {
                if (!data[keys[i]].length) {
                    continue;
                }

                carousel = this._buildCarousel(data[keys[i]]);
                channelLogoAndNumber = this._buildChannelLogoAndNumber(data[keys[i]]);
                carousel.insertChildWidget(0, channelLogoAndNumber);

                // Down
                if (direction === 0) {
                    verticalCarousel.append(carousel, widgetHeight);
                }

                // Up
                if (direction === 1) {
                    verticalCarousel.insert(0, carousel, widgetHeight);
                }

                if (!this._activeTimeframe) {
                    this._activeTimeframe = '_nowIndex';
                    fromLeftPosition = 1;
                }

                carousel.alignToTimeOfDay(this._activeTimeframe, {skipAnim: true}, fromLeftPosition);
            }

                alignIndex = verticalCarousel.getIndexOfChildWidget(verticalCarousel.getActiveChildWidget());

                this._activeVerticalIndex = alignIndex;
                verticalCarousel.alignToIndex(alignIndex, {
                    skipAnim: true
                });
                this._activeVerticalIndex = null;

        },

        /**
         * Append data to carousel.
         *
         * @param {Object} date - The date object.
         * @param {string} [alignIndex] - The align index.
         * @param {number} channelNumber - The channelnumber.
         * @param {Array} data - The data.
         * @private
         */
        _setData: function (date, alignIndex, channelNumber, data) {
            var verticalCarousel = this._verticalCarousel,
                noresultslabel,
                carousel,
                channelLogoAndNumber,
                position = 0;

            alignIndex = alignIndex || '_nowIndex';

            this._verticalCarousel.reset();

            if (Utils.isEmpty(data)) {
                application.hideLoader();
                this._filterscontainer.focus();

                if (this._filterGenre) {
                    noresultslabel = l10n.get('guide.noresults', {
                        genre: this._filterGenre.name
                    });
                } else {
                    noresultslabel = l10n.get('guide.noresultsfull');
                }

                this._noResultsLabel.setText(noresultslabel);
                this._noResultsLabel.show();
                return;
            }

            this._noResultsLabel.hide({skipAnim: true});

            data = this._formatEPGData(data);

            if (Utils.keys(data).length && channelNumber) {
                this._activeVerticalIndex = Utils.keys(data).indexOf(channelNumber.toString());
            }

            if (this._activeVerticalIndex !== 0) {
                this._gradient.addClass('active-gradient');
            }

            Utils.each(data, function (items) {
                carousel = this._buildCarousel(items);
                channelLogoAndNumber = this._buildChannelLogoAndNumber(items);
                carousel.insertChildWidget(0, channelLogoAndNumber);

                this._verticalCarousel.append(carousel, carouselsDimensions.carousels.widgetHeight);
            }, this);

            if (verticalCarousel.getChildWidgets().length > 0
                && this._activeVerticalIndex !== null) {

                if ((verticalCarousel.getChildWidgetCount() - 1 < this._activeVerticalIndex) || this._activeVerticalIndex === -1) {
                    this._activeVerticalIndex = verticalCarousel.getChildWidgetCount() - 1;
                }

                verticalCarousel.alignToIndex(this._activeVerticalIndex, {skipAnim: true});
            } else {
                verticalCarousel.alignToIndex(0, {skipAnim: true});
            }

            if (alignIndex === '_nowIndex') {
                position = 1;
            }

            this._isGoingBack = true;
            this.timeAlignVisible(alignIndex, {
                skipAnim: true
            }, position);

            this._activeVerticalIndex = null;
            application.hideLoader();
        },

        /**
         * Formats the epg data.
         *
         * @param {Object} data - The data.
         * @returns {Object} - The formatted EPG data.
         * @private
         */
        _formatEPGData: function (data) {
            var formatted = {};

            /*
             * EPG Manager gives data back according to channel Id.
             * We want to display it according to channel number instead.
             */
            Utils.each(data, function (items) {
                if (items && items.length) {
                    formatted[channelManager.getChannelNumberForId(items[0].getChannelId())] = items;
                }
            });

            return formatted;
        },

        /**
         * Aligns assets in visible carousels depending on time they start.
         *
         * @param {string|number} timeframe - Timeframe of focused asset.
         * @param {Object} [options] - Animation options.
         * @param {number} [position] - The align position.
         */
        timeAlignVisible: function (timeframe, options, position) {
            var activeWidget = this._verticalCarousel.getActiveChildWidget(),
                fromLeftPosition = activeWidget.getFromLeftIndex(),
                activeIndex = this._verticalCarousel.getActiveChildIndex(),
                isGoingBack = this._isGoingBack || false,
                i,
                carousel;

            options = options || {
                duration: 300,
                fps: 60,
                easing: 'easeInOut',
                skipAnim: false
            };

            if (position) {
                fromLeftPosition = position;
            }

            for (i = activeIndex - 3; i < activeIndex + 3; i++) {
                carousel = this._verticalCarousel.getChildWidgetByIndex(i);
                if (carousel && (!carousel.hasClass('focus') || isGoingBack)) {

                    if (timeframe === '_lastIndex') {
                        carousel.activateLast(options);
                    } else if (timeframe === '_firstIndex') {
                        carousel.alignToPosition(0, options);
                    } else {
                        carousel.alignToTimeOfDay(timeframe, options, fromLeftPosition);
                    }
                }
            }
            this._isGoingBack = null;
        },

        /**
         * Time aligns the items.
         *
         * @param {string|number} timeframe - The timeframe.
         * @param {Object} [options] - The alignment options.
         */
        timeAlignItem: function (timeframe, options) {
            var verticalCarousel = this._verticalCarousel,
                activeWidget = verticalCarousel.getActiveChildWidget(),
                fromLeftPosition = activeWidget.getFromLeftIndex(),
                activeIndex = verticalCarousel.getActiveChildIndex(),
                item;

            options = options || {
                skipAnim: true
            };

            item = verticalCarousel.getChildWidgetByIndex(activeIndex - 2);
            if (item) {
                if (timeframe === '_lastIndex') {
                    item.activateLast(options);
                } else if (timeframe === '_firstIndex') {
                    item.alignToPosition(0);
                } else {
                    item.alignToTimeOfDay(timeframe, options, fromLeftPosition);
                }
            }

            item = verticalCarousel.getChildWidgetByIndex(activeIndex + 2);
            if (item) {
                if (timeframe === '_lastIndex') {
                    item.activateLast(options);
                } else if (timeframe === '_firstIndex') {
                    item.alignToPosition(0, options);
                } else {
                    item.alignToTimeOfDay(timeframe, options, fromLeftPosition);
                }
            }
        },

        /**
         * Aligns all assets in carousel to with the last item.
         *
         * @param {Object} [options] - Animation options.
         */
        timeAlignLastItem: function (options) {
            var activeIndex = this._verticalCarousel.getActiveChildIndex(),
                i,
                carousel;

            options = options || {
                duration: 300,
                fps: 60,
                easing: 'easeInOut',
                skipAnim: false
            };

            for (i = activeIndex - 3; i < activeIndex + 3; i++) {
                carousel = this._verticalCarousel.getChildWidgetByIndex(i);
                if (carousel && !carousel.hasClass('focus')) {

                    carousel.activateLast(options);
                }
            }
        },

        /**
         * Sets "0" before number, if that is single number.
         *
         * @param {number} number - Number which should be corrected.
         * @returns {string} String number wich "0" on first position if needed.
         * @private
         */
        _convertToTime: function (number) {
            number = number.toString();

            return number.length === 1 ? '0' + number : number;
        },

        /**
         * Makes time form of a number.
         *
         * @param {number} filterTime - Input number.
         * @returns {string} String form of time.
         * @private
         */
        _filterTimeString: function (filterTime) {
            filterTime = filterTime % 2 === 0 ? filterTime : filterTime - 1;
            filterTime = filterTime.toString();
            filterTime = filterTime.length === 1 ? '0' + filterTime + ':00' : filterTime + ':00';
            return filterTime;
        },

        /**
         * Resets the guide.
         *
         * @private
         */
        _reset: function () {
            this._infoblock.hide({skipAnim: true});
            this._timefiltersButton.setActiveChildIndex(0);
            this._verticalCarousel.reset();
        },

        /**
         * Moves the guide.
         *
         * @param {number} direction - The direction. 1 for upwards, 0 for downwards.
         * @private
         */
        _moveGuide: function (direction) {
            var carousel = this._verticalCarousel,
                current = carousel.getActiveIndex(),
                max = carousel.getChildWidgetCount() - 1,
                min = 0,
                index = 0,
                animOption = {
                    skipAnim: !!this._movingGuide,
                    onComplete: Utils.bind(function () {
                        this._movingGuide = false;
                    }, this)
                },
                loadmoreRows = config.loading.guide.loadMoreAfterRows,
                channelMap = channelManager.getChannelMap(),
                channelId = this._getChannelIdByCarouselDirection(direction),
                startIndex = channelMap.indexOf(channelId);

            if (direction) {

                // Move up.
                index = current - 3;
            } else {

                // Move down.
                index = current + 3;
            }

            if (index > max) {
                index = max;
            } else if (index < min) {
                index = min;
            }

            // Going down
            if (direction === 0 && ((max - current) <= loadmoreRows) &&
                (channelMap.length - 1 !== startIndex) &&
                !this._loadingMoreData) {

                if (!this._filterGenre) {
                    this._loadingMoreData = true;
                    this._loadMore(this._filterDate, startIndex + 1, startIndex + 20)
                        .then(Utils.bind(function (data) {
                            this._setLoadmoreData(data, direction);
                            this._loadingMoreData = false;
                        }, this));
                }
            }

            // Going up
            if (direction === 1 && current <= loadmoreRows && startIndex !== 0 &&
                !this._loadingMoreData) {

                if (!this._filterGenre) {
                    this._loadingMoreData = true;
                    this._loadMore(this._filterDate, startIndex - 21, startIndex)
                        .then(Utils.bind(function (data) {
                            this._setLoadmoreData(data, direction);
                            this._loadingMoreData = false;
                        }, this));
                }
            }

            // Prevent carousel aligning if focus is on the first/last row and no more data is available.
            if ((current !== max && !direction) || (current !== min && direction)) {
                this._activeVerticalIndex = index;

                // Prevents guide list jumping behaviour.
                this._movingGuide = !(current === 0 && current === index);
                carousel.alignToIndex(index, animOption);
                this._activeVerticalIndex = null;
            }
        },

        /**
         * OnDirectDialInput.
         *
         * @param {string} char - The input character.
         * @private
         */
        _onDirectDialInput: function (char) {
            var directDial = this._directDial,
                timeoutValue = 3000;

            clearTimeout(this._timer);

            directDial.setKeyInput(char);

            if (!directDial.isVisible()) {
                directDial.show({duration: 200});
            }

            this._clearDirectDialTimeout();
            this._directDialTimeout = setTimeout(Utils.bind(this._onDirectDial, this), timeoutValue);
        },

        /**
         * Returns closest channel id for specific channel number from genre filtered data.
         *
         * @param {number} number - Channel number.
         * @returns {number} Channel id.
         * @private
         */
        _getGenreFilteredChannelId: function (number) {

            return Utils.map(Utils.keys(this._genreFilteredData), function (channelId) {
                return channelManager.getChannelById(channelId);
            }).sort(function (a, b) {
                return Math.abs(a.getNumber() - number) - Math.abs(b.getNumber() - number);
            }).shift().getId();
        },

        /**
         * Gets executed when the direct dial finalises.
         *
         * @private
         */
        _onDirectDial: function () {
            var number = parseInt(this._directDial.getCurrentInput()),
                self = this,
                channelId;

            if (!isNaN(number)) {

                if (this._filterGenre) {
                    channelId = this._getGenreFilteredChannelId(number);
                } else {
                    channelId = channelManager.getChannelByNumber(number).getId();
                }
                this._directDialChannel = channelId;
                application.showLoader();
                this._loadData(this._filterDate, null, this._activeTimeframe)
                    .then(function () {
                        self.focus();
                        application.hideLoader();
                    });

                this._directDial.hide({duration: 200});
                this._directDial.reset();
            }
        },

        /**
         * Clears the direct dial timeout.
         *
         * @private
         */
        _clearDirectDialTimeout: function () {
            clearTimeout(this._directDialTimeout);
        },

        /**
         * Gets executed when the user should go back.
         *
         * @private
         */
        _onBack: function () {
            var activeBigCarouselIndex = this._verticalCarousel.getActiveChildIndex(),
                filterHours = this._timefiltersButton.getText(),
                self = this,
                channelId,
                date;

            if (activeBigCarouselIndex !== 0) {
                channelId = channelManager.getChannelMap()[0];
                this._directDialChannel = channelId;
                this._isGoingBack = true;
                application.showLoader();

                date = new Date(this._filterDate);
                date.setHours(
                    parseInt(filterHours.slice(0, 2)),
                    parseInt(filterHours.slice(3, 5)),
                    0,
                    0
                );

                this._loadData(this._filterDate, null, date.getTime())
                    .then(function () {
                        self.focus();
                        application.hideLoader();
                    });
            } else {
                application.focusMenu('main');
            }
        },

        /**
         * Returns the currently active channel.
         *
         * @returns {number|null} - The currently active channel id or null.
         * @private
         */
        _getCurrentChannelId: function () {
            var activeWidget = this._verticalCarousel.getActiveChildWidget();

            if (activeWidget) {
                return activeWidget.getChannelId();
            }

            return null;
        },

        /**
         * Visibility change event.
         *
         * @param {Object} e - The event data.
         * @private
         */
        _onVisibilityChange: function (e) {
            if (this.isFocussed() && (document.visibilityState === 'visible' || e === 'visible')) {
                this._updateProgressbars();
            } else {
                this._clearProgressInterval();
            }
        },

        /**
         * Clears any progress interval update.
         *
         * @private
         */
        _clearProgressInterval: function () {
            clearInterval(this._progressInterval);
            this._progressInterval = null;
        }
    });

    return Guide;
});