Source: components/details/epg.js

define('application/components/details/epg', [
    'application/components/details/details',
    'rofl/widgets/container',
    'rofl/events/keyevent',
    'rofl/lib/utils',
    'application/widgets/detail/header',
    'application/widgets/detail/detailslist',
    'rofl/widgets/verticallist',
    'rofl/lib/l10n',
    'antie/runtimecontext',
    'rofl/analytics/web/google',
    'rofl/widgets/label',
    'rofl/lib/promise',
    'application/widgets/pointerbutton',
    'application/managers/recording',
    'application/managers/feature',
    'application/models/production/recordings/record',
    'application/constants'
], function (
    BaseDetail,
    Container,
    KeyEvent,
    Utils,
    Header,
    DetailsList,
    VerticalList,
    L10N,
    RuntimeContext,
    GoogleAnalytics,
    Label,
    Promise,
    PointerButton,
    RecordingManager,
    FeatureManager,
    Record,
    Constants
) {
    'use strict';

    var l10n = L10N.getInstance(),
        application = RuntimeContext.getCurrentApplication(),
        GA = GoogleAnalytics.getInstance(),
        recordingManager = RecordingManager.getInstance(),
        layout = application.getLayout(),
        device = RuntimeContext.getDevice(),
        featureManager = FeatureManager.getInstance();

    return BaseDetail.extend({

        /**
         * Initialises the component.
         */
        init: function init () {
            this._onSelectedItemChangeBound = Utils.bind(this._onSelectedItemChange, this);
            this._onRecordDeletedBound = Utils.bind(this._onRecordDeleted, this);
            this._onRecordingChangeBound = Utils.bind(this._onRecordingChange, this);

            init.base.call(this, 'epg');
        },

        /**
         * Sets the header data.
         *
         * @param {Object} data - Contains detail data and series episodes if any.
         * @private
         */
        _setHeaderData: function (data) {
            var contentDetail = data.contentDetail,
                seriesItem = this._isEpgSeries ? data.data : null;

            this._header.setDataItem(contentDetail, seriesItem);
        },

        /**
         * Sets the content's details.
         *
         * @param {Object} data - Contains detail data and series episodes if any.
         * @private
         */
        _setDetailData: function (data) {
            var contentDetail = data.contentDetail,
                episodes = data.epgSeriesEpisodes;

            if (episodes.length) {
                this._list.removeClass('minimalistic');
                this._buildEpisodes(episodes);

                this._header.setSeriesData(contentDetail);
            } else {
                this._list.addClass('minimalistic');
                this._detailslistbox.removeChildWidgets();

                this._header.setData(contentDetail);
            }

            this._header.focus();

            if (!this._header.getButtonListLength()) {
                this._backButton.focus();
            }
        },

        /**
         * Gets executed when the recording state changes.
         *
         * @param {Object} e - The event data.
         * @private
         */
        _onRecordingChange: function (e) {
            if (e.target === 'series' && e.id === this.getDataItem().getSeriesId()) {
                this._header.updateRecordingStatus();
            } else if (e.target === 'episode' && e.id === this.getDataItem().getId()) {
                this._header.updateRecordingStatus();
            }
        },

        /**
         * Builds the episodes.
         *
         * @param {Array} episodes - The episodes.
         * @private
         */
        _buildEpisodes: function (episodes) {
            var detailsList;

            this._createDetailsList(episodes);

            detailsList = this._detailsList;

            detailsList.setStylesTo({
                'top': layout.detailsHeader.bigheader.top + 'px'
            });
        },

        /**
         * Creates modal content list based on passed options and data.
         *
         * @param {Array} episodes - The episodes to append.
         */
        _createDetailsList: function (episodes) {
            var detailsList = this._detailsList = new DetailsList(episodes),
                detailsListContainer = this._detailslistbox;

            detailsListContainer.removeChildWidgets();
            this._buildPointerButtons();
            detailsListContainer.appendChildWidget(detailsList);
        },

        /**
         * Builds the pointer buttons.
         *
         * @private
         */
        _buildPointerButtons: function () {
            var upButton = this._upButton = new PointerButton(PointerButton.DIRECTIONS.UP),
                downButton = this._downButton = new PointerButton(PointerButton.DIRECTIONS.DOWN);

            this._downButton.enable();
            this._upButton.disable();
            this._detailslistbox.appendChildWidget(upButton);
            this._detailslistbox.appendChildWidget(downButton);
        },

        /**
         * On Selected item change event.
         *
         * @param {Event} e - The event data.
         */
        _onSelectedItemChange: function (e) {
            var target = e.target,
                item = e.item,
                header = this._header,
                toggle;

            if (target.hasClass('episodes-list')) {
                header.addClass('shortheader');
                this.align(target.getActiveChildWidget());
            } else if (item.hasClass('episodes-list')) {
                toggle = item.getActiveChildWidget().getToggle();

                header.addClass('shortheader');
                this.align(item.getActiveChildWidget());
                toggle.focus();
            } else if (item.hasClass('details-page-header') || item.hasClass('back-button')) {

                if (!this._detailsList) {
                    return;
                }

                device.moveElementTo({
                    el: this._detailsList.outputElement,
                    skipAnim: false,
                    duration: 200,
                    fps: 60,
                    easing: 'easeInOut',
                    to: {
                        top: layout.detailsHeader.bigheader.top
                    }
                });

                header.removeClass('shortheader');
            }

            if (target.hasClass('detail-content') || item.hasClass('back-button')) {

                if (!this._upButton) {
                    return;
                }

                if (e.index) {
                    this._upButton.enable();
                } else {
                    this._upButton.disable();
                }
            }
        },

        /**
         * On Select event.
         *
         * @param {Event} e - The event data.
         */
        _onSelect: function (e) {
            var target = e.target,
                id = target.id;

            if (id === 'up' || id === 'down') {
                this._focusItem(id);
                return;
            }

            target.focus();

            switch (id) {
                case 'playnow':
                    if (target.getDataItem().isRecording() && target.getDataItem().isLive()) {
                        this._onRestart(target.getDataItem());
                    } else {
                        this._onPlay(target.getDataItem());
                    }
                    GA.onEvent('player', 'started', {eventLabel: this._callingPage});
                    break;
                case 'restart':
                    this._onRestart(target.getDataItem());
                    break;
                case 'record':

                    // Falls through.
                case 'delete':
                    this._onRecord(target);
                    break;
                case 'close':
                    if (target.getDataItem() !== this._contentDetail) {
                        this._previousEpisodeItem.disable();
                        this._previousEpisodeItem.getToggle().focus();
                        this._previousEpisodeItem = null;
                    } else {
                        this._onBack();
                    }
                    break;
                case 'back-button':
                    this._onBack();
                    break;
                default:
                    if (target.hasClass('toggle') && target.hasClass('focus')) {
                        this._onSelectToggle(target);
                    }
            }
        },

        /**
         * Attempts to play the video.
         *
         * @param {Object} dataItem - The data item.
         * @private
         * @returns {boolean} False if user input is blocked.
         */
        _onPlay: function (dataItem) {
            var data = dataItem || this._contentDetail;

            if (application.blockUserInput) {
                return false;
            }

            if (data.canPlay()) {
                application.showLoader(true, true);

                if (data.isLive()) {

                    application.route('liveplayer', {
                        data: data,
                        channelId: data.getChannelId(),
                        type: Constants.LIVE_VIDEO_TYPE,
                        callingPage: 'detail'
                    });

                } else if (data.isRecording()) {

                    application.route('recordingplayer', {
                        data: data,
                        type: 'RECORDING',
                        callingPage: 'detail'
                    });

                } else {
                    application.route('catchupplayer', {
                        data: data,
                        type: 'VOD',
                        callingPage: 'detail'
                    });
                }
            }
        },

        /**
         * Starts video from beginning.
         *
         * @param {Object} dataItem - The data item.
         * @private
         */
        _onRestart: function (dataItem) {
            var data = dataItem || this._contentDetail;

            if (data.canPlay()) {
                application.route('liveplayer', {
                    data: data,
                    channelId: data.getChannelId(),
                    type: Constants.RESTART_VIDEO_TYPE,
                    callingPage: 'detail'
                });
            }
        },

        /**
         * Starts video recording.
         *
         * @param {Object} target - The record button target.
         * @private
         */
        _onRecord: function (target) {
            var data = target.getDataItem() || this._contentDetail;

            if (featureManager.isRecordingEnabled()) {
                application.route('record', {
                    item: data,
                    callingComponent: 'detail',
                    callback: Utils.bind(this._onRecordCallback, this, target),
                    flow: (data instanceof Record || data.getContentType() === 'RECORDING')
                    && data.getStartTime() <= application.getDate() / 1000 ? 'delete' : 'default'
                });
            } else {
                this._showRecordingDisabled();
            }
        },

        /**
         * Is called when the select toggle gets selected.
         *
         * @param {Object} target - The target.
         * @private
         */
        _onSelectToggle: function (target) {
            var episodeItem = target.parentWidget,
                description = episodeItem.getDescription();

            if (this._previousEpisodeItem) {
                this._previousEpisodeItem.disable();
            }

            episodeItem.enable();
            this.align(target.parentWidget);
            description.focus();

            this._previousEpisodeItem = episodeItem;
        },

        /**
         * Hides the warning box.
         *
         * @param {boolean} [skipAnim] - True if should skip the animations.
         */
        hideWarningBox: function (skipAnim) {
            this._warningBox.hide({
                skipAnim: skipAnim
            });
        },

        /**
         * Shows the warning box.
         *
         * @param {Object} config - The config.
         */
        showWarningBox: function (config) {
            this._warningBox.show(config);
        },

        /**
         * Aligns the component based on the given item.
         *
         * @param {Object} item - The item.
         */
        align: function (item) {
            var listElement = this._detailsList.outputElement,
                listTop = listElement.getBoundingClientRect().top,
                windowHeight = layout.requiredScreenSize.height,
                dimensions,
                perfectTop,
                difference,
                newTop;

            dimensions = item.outputElement.getBoundingClientRect();
            perfectTop = (windowHeight - dimensions.height) / 2;
            difference = dimensions.top - perfectTop;

            if (difference > 0) {
                newTop = listTop - difference;
            } else {
                newTop = listTop + -difference;
            }

            // height of smaller header
            if (newTop > 0) {

                if (this._header.hasClass('shortheader')) {
                    newTop = layout.detailsHeader.shortheader.top;
                } else {
                    newTop = layout.detailsHeader.bigheader.top;
                }
            }

            device.moveElementTo({
                el: listElement,
                skipAnim: false,
                duration: 200,
                fps: 60,
                easing: 'easeInOut',
                to: {
                    top: newTop
                }
            });
        },

        /**
         * Gets executed when the error triggers.
         *
         * @param {Function} callback - The callback.
         * @private
         */
        _onError: function (callback) {
            application.hideLoader();
            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',
                callback: Utils.bind(this._onErrorCallback, this, callback)
            });
        },

        /**
         * Focuses a specific item based on the direction.
         *
         * @param {string} direction - The direction.
         * @private
         */
        _focusItem: function (direction) {
            var episodes = this._detailsList.getChildWidgetByIndex(0),
                current = episodes.getActiveChildWidgetIndex(),
                length = episodes.getChildWidgetCount();

            if (!episodes.isFocussed()) {
                this._upButton.enable();
                episodes.focus();
                return;
            }

            if (direction === 'up') {
                current--;
            } else {
                current++;
            }

            if (current < 0) {

                this._header.focus();
                this._upButton.disable();

                if (!this._header.getButtonListLength()) {
                    this._backButton.focus();
                }
                return;
            }

            this._downButton.enable();

            episodes.setActiveChildIndex(current);

            if (current > length - 2) {

                this._downButton.disable();
            }
        },

        /**
         * Error callback.
         *
         * @param {Function|null} callback - The function callback, or null if not defined.
         * @private
         */
        _onErrorCallback: function (callback) {

            if (Utils.isFunction(callback)) {
                callback();
            }
        },

        /**
         * The record callback.
         *
         * @param {Object} target - The target.
         * @param {Object} e - The record callback.
         * @private
         */
        _onRecordCallback: function (target, e) {
            var config = {
                    happy: true,
                    icon: 'icon-check-v2'
                },
                hasRecording = recordingManager.hasRecording(e.item.getId(), e.item.getSeriesId());

            if (e.removed) {
                if (e.item.getEndTime() * 1000 > application.getDate()) {

                    // Future item gets canceled.
                    config.text = l10n.get('recordings.notification.cancelled', {
                        item: e.item.getTitle()
                    });
                } else {

                    // Old items get removed.
                    config.text = l10n.get('recordings.notification.removed', {
                        item: e.item.getTitle()
                    });
                }

                debugger;

                if (target.id === 'record') {
                    if (hasRecording.episode || hasRecording.series) {
                        target.recording();
                    } else {
                        target.record();
                    }
                }
            } else {

                config.text = l10n.get('recordings.notification.successful.' + e.type, { series: e.item.getTitle() });
                target.recording();
            }

            this.showWarningBox(config);
        },

        /**
         * Shows the program is already recorded message.
         *
         * @param {Object} item - The item.
         * @private
         */
        _showProgramAlreadyRecorded: function (item) {
            var config = {
                icon: 'icon-alert-v2',
                text: l10n.get('recordings.notification.recording', {
                    series: item.getTitle()
                })
            };

            this.showWarningBox(config);
        },

        /**
         * Shows the program can not be recorded message.
         *
         * @param {Object} item - The item.
         * @private
         */
        _showProgramNotRecordable: function (item) {
            var config = {
                icon: 'icon-alert-v2',
                text: l10n.get('recordings.notification.not_recordable', {
                    series: item.getTitle()
                })
            };

            this.showWarningBox(config);
        },

        /**
         * Shows recording disabled message.
         *
         * @private
         */
        _showRecordingDisabled: function () {
            var config = {
                text: l10n.get('player.warningbox.recording'),
                icon: 'icon-alert-v2'
            };

            this.showWarningBox(config);
        },

        /**
         * Gets executed when a record gets deleted.
         *
         * @param {Object} e - The event.
         * @private
         */
        _onRecordDeleted: function (e) {
            var episodeItem,
                episodeList,
                activeIndex;

            e.stopPropagation();


            if (e.target === this._contentDetail) {
                this._onBack();
            } else {

                episodeItem = e.target;
                episodeList = this._detailsList.getEpisodeList();
                activeIndex = episodeList.getActiveChildWidgetIndex();

                episodeList.removeChildWidget(episodeItem);

                activeIndex = activeIndex - 1;

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

                if (episodeList.getChildWidgetCount()) {
                    episodeList.setActiveChildIndex(activeIndex);
                } else {
                    this._onBack();
                }
            }
        }
    });
});