Source: components/recordings.js

define('application/components/recordings', [
    'rofl/widgets/component',
    'rofl/widgets/verticallist',
    'application/widgets/recordings/filters',
    'application/widgets/recordings/grid',
    'application/widgets/clock',
    'rofl/lib/utils',
    'rofl/widgets/label',
    'rofl/lib/l10n',
    'antie/runtimecontext',
    'application/managers/recording',
    'rofl/events/keyevent',
    'application/widgets/infoblock',
    'rofl/lib/promise',
    'rofl/widgets/container',
    'rofl/analytics/web/google',
    'application/managers/halo'
], function (
    Component,
    VerticalList,
    Filters,
    Grid,
    Clock,
    Utils,
    Label,
    L10N,
    RuntimeContext,
    RecordingManager,
    KeyEvent,
    InfoBlock,
    Promise,
    Container,
    GoogleAnalytics,
    HaloManager
) {
    'use strict';

    var application = RuntimeContext.getCurrentApplication(),
        layout = application.getLayout(),
        defaultGridPosition = layout.recordings.grid.default,
        device = RuntimeContext.getDevice(),
        l10n = L10N.getInstance(),
        GA = GoogleAnalytics.getInstance(),
        infoblockConfig = {
            id: 'back-on-top-block',
            text: l10n.get('infoblock'),
            classname: ['icon', 'icon-back-v2'],
            position: 'right'
        },
        recordingManager = RecordingManager.getInstance();

    return Component.extend({

        /**
         * Initialises the component.
         */
        init: function init () {
            init.base.call(this, 'recordings');

            this.addClass('fullscreen');
            this._build();
            this._setBindings();
        },

        /**
         * Builds the component.
         *
         * @private
         */
        _build: function () {
            var list = this._list = new VerticalList(),
                filters = this._filters = new Filters(),
                grid = this._grid = new Grid(),
                infoBlock = this._infoBlock = new InfoBlock(infoblockConfig),
                clock = new Clock(),
                title = new Label({ text: l10n.get('recordings.title'), classNames: ['top-title'] }),
                branding = this._branding = new Container();

            branding.addClass('page-branding');
            infoBlock.addClass('info-block');
            clock.addClass('header-clock');

            list.appendChildWidget(filters);
            list.appendChildWidget(grid);

            branding.appendChildWidget(title);
            this.appendChildWidget(branding);
            this.appendChildWidget(clock);
            this.appendChildWidget(list);
            this.appendChildWidget(infoBlock);
        },

        /**
         * BeforeShow event.
         */
        onBeforeShow: function () {
            this._setEventListeners();
            this._filters.setSelectedItem(this._filters.getChildWidgetByIndex(0));

            GA.onPageView('recordings');

            this._reload = true;
            application.showLoader();
            this._reset();
            this._loadData({
                current: true
            });

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

        /**
         * BeforeHide event.
         */
        onBeforeHide: function () {
            this._removeEventListeners();
        },

        /**
         * Loads the data based on the options.
         *
         * @param {Object} options - The options.
         * @private
         */
        _loadData: function (options) {
            var promise;

            this._loadOptions = options;
            if (options.current) {
                promise = this._loadCurrentRecordings();
            } else if (options.planned) {
                promise = this._loadUpcomingRecordings();
            }

            promise
            .then(Utils.bind(this._setData, this, options, true))
            ['catch'](Utils.bind(this._onError, this));
        },

        /**
         * Loads the current recordings.
         *
         * @returns {Promise} - Promise resolving with the recordings.
         * @private
         */
        _loadCurrentRecordings: function () {

            return recordingManager.getCurrentSeriesRecordings({
                forceReload: this._reload
            });
        },

        /**
         * Loads the upcoming recordings.
         *
         * @returns {Promise} - Promise resolving with the recordings.
         * @private
         */
        _loadUpcomingRecordings: function () {
            return recordingManager.getPlannedSeriesRecordings();
        },

        /**
         * Sets the data to the grid.
         *
         * @param {Object} options - The load options.
         * @param {boolean} shouldFocus - True if the grid should receive focus.
         * @param {Object} data - The data.
         * @returns {Promise} - The promise.
         * @private
         */
        _setData: function (options, shouldFocus, data) {
            var title = '';

            return new Promise(Utils.bind(function (resolve) {
                if (!data.length) {

                    if (options.current) {
                        title = l10n.get('recordings.noresults.current');
                    } else {
                        title = l10n.get('recordings.noresults.planned');
                    }
                }

                this._reload = false;
                this._grid.setDataItem({
                    items: data,
                    title: title
                }, shouldFocus);

                if (shouldFocus) {

                    if (data.length) {
                        this._grid.focus();
                    } else {
                        this._filters.focus();
                    }
                }
                application.hideLoader();

                resolve();
            }, this));
        },

        /**
         * Gets executed when an error triggers.
         *
         * @private
         */
        _onError: 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'
            });
        },

        /**
         * Sets the bindings.
         *
         * @private
         */
        _setBindings: function () {
            this._onSelectBound = Utils.bind(this._onSelect, this);
            this._onKeyDownBound = Utils.bind(this._onKeyDown, this);
            this._onSelectedItemChangeBound = Utils.bind(this._onSelectedItemChange, this);
            this._onRecordChangeBound = Utils.bind(this._onRecordChange, this);
        },

        /**
         * Sets the event listeners.
         *
         * @private
         */
        _setEventListeners: function () {
            this.addEventListener('selecteditemchange', this._onSelectedItemChangeBound);
            application.addEventListener('$record', this._onRecordChangeBound);

            this._setSelectListener();
        },

        /**
         * Removes the event listeners.
         *
         * @private
         */
        _removeEventListeners: function () {
            this.removeEventListener('selecteditemchange', this._onSelectedItemChangeBound);
            application.removeEventListener('$record', this._onRecordChangeBound);

            this._removeSelectListener();
        },

        /**
         * 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);
        },

        /**
         * Ahows asset detail modal.
         *
         * @param {Object} data - Asset.
         * @private
         */
        _showAssetDetail: function (data) {
            application.route('epgdetail', {
                callback: Utils.bind(this.focus, this),
                data: data,
                callingPage: 'recordings',
                series: Utils.isFunction(data.getItems)
            });
        },

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

            switch (target.id) {
                case 'current-recordings':
                    this._filters.setSelectedItem(target);
                    this._loadData({
                        current: true
                    });
                    break;
                case 'planned-recordings':
                    this._filters.setSelectedItem(target);
                    this._loadData({
                        planned: true
                    });
                    break;
                case 'back-on-top-block':
                    this._grid.alignToFirstItem();
                    break;
                default:
                    if (target.hasClass('asset')) {
                        data = target.getDataItem().item;

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

                        this._showAssetDetail(data);
                    }
            }
        },

        /**
         * Unlock the assets.
         *
         * @private
         */
        _unlockAssets: function () {
            var rows = this._list.getChildWidgetByIndex(1).getList().getChildWidgets(),
                assets;

            Utils.each(rows, function (row) {
                assets = row.getChildWidgets();

                Utils.each(assets, function (asset) {
                    asset.unlock();
                });
            });
        },

        /**
         * Selected item change event.
         *
         * @param {Object} e - The event data.
         * @private
         */
        _onSelectedItemChange: function (e) {
            var infoBlock = this._infoBlock;

            if (e.target.hasClass('skeleton')) {
                if (e.index) {

                    if (!infoBlock.isVisible()) {
                        infoBlock.show();
                    }
                } else {

                    if (infoBlock.isVisible()) {
                        infoBlock.hide();
                    }
                }
            }
        },

        /**
         * KeyDown Event listener.
         *
         * @param {Object} e - The event data.
         * @private
         */
        _onKeyDown: function (e) {
            var samsungChannelKey = this._handleChannelKey(e);

            if (e.keyCode !== KeyEvent.VK_GUIDE &&
                e.keyCode !== KeyEvent.VK_INFO &&
                e.keyCode !== KeyEvent.VK_LIST &&
                !samsungChannelKey) {

                e.preventDefault();
                e.stopPropagation();
            }

            if (e.keyCode === KeyEvent.VK_LEFT) {

                application.focusMenu('main');
            } else if (e.keyCode === KeyEvent.VK_BACK) {

                this._onBack();
            }
        },

        /**
         * Determines if Samsung's channel key will be handled by the main app or the component.
         *
         * @param {Object} e - The keyEvent data.
         * @returns {boolean} - True if the channel key will be handled by main app instead of component.
         */
        _handleChannelKey: function (e) {
            var deviceBrand = device.getBrand();

            return (e.keyCode === KeyEvent.VK_CHANNEL_UP || e.keyCode === KeyEvent.VK_CHANNEL_DOWN) &&
                deviceBrand === 'samsung';
        },

        /**
         * Executes when the back button is used.
         *
         * @private
         */
        _onBack: function () {
            var gridItemsCount = this._grid.getList().getChildWidgetCount();

            if (gridItemsCount && this._grid.getActiveChildIndex() !== 0) {
                this._grid.alignToFirstItem();
            } else {
                application.focusMenu('main');
            }
        },

        /**
         * Resets the view.
         *
         * @private
         */
        _reset: function () {
            device.moveElementTo({
                el: this._grid.outputElement,
                skipAnim: true,
                to: {
                    top: defaultGridPosition.top,
                    left: defaultGridPosition.left
                }
            });

            this._filters.setActiveChildIndex(0);
            this._infoBlock.hide({skipAnim: true});
        },

        /**
         * Gets executed when a recording status changes.
         *
         * @param {Object} e - The event.
         * @private
         */
        _onRecordChange: function (e) {
            if (e.deleted) {

                this._reloadDisplay();
            }
        },

        /**
         * Reloads the component.
         *
         * @private
         */
        _reloadDisplay: function () {
            var grid = this._grid,
                activeRowIndex = grid.getActiveChildIndex(),
                activeItemIndex = grid.getActiveItemIndex(),
                gridList = grid.getList().getMask().getWidgetStrip(),
                self = this,
                activeRow,
                promise,
                rowChildWidgetCount;

            if (this._loadOptions.current) {
                promise = this._loadCurrentRecordings();
            } else {
                promise = this._loadUpcomingRecordings();
            }

            promise
                .then(Utils.bind(this._setData, this, this._loadOptions, false))
                .then(function () {
                    rowChildWidgetCount = grid.getRowChildWidgetCount();

                    if (rowChildWidgetCount > 0) {
                        if (rowChildWidgetCount - 1 < activeRowIndex) {
                            activeItemIndex = 3; // The last index.
                        }

                        // Check if the active row exists or should move to the previous row.
                        if (!gridList.getChildWidgetByIndex(activeRowIndex)) {
                            activeRowIndex -= 1;
                        }

                        if (activeRowIndex >= 0) {
                            grid.alignToIndexFunc(activeRowIndex);
                            activeRow = grid.getActiveRow();
                        }

                        if (!activeRow.getChildWidgetByIndex(activeItemIndex)) {
                            activeItemIndex = activeRow.getChildWidgetCount() - 1;
                        }

                        activeRow.setActiveChildIndex(activeItemIndex);
                    } else {
                        self.setActiveChildWidget(self._filters);
                        self._filters.focus();
                    }
                });
        }
    });
});