src/modules/program/program.module.ts

Summary

Maintainability
D
1 day
Test Coverage
import { Program, ProgramEpisode } from './program.model';
import { ProgramService } from './program.service';
import { Module } from '../../libs';
import { DefaultResponse } from '../../_models';
import programTemplate from './program.template.html';
import episodeTemplate from './program-episodes.template.html';

export default class ProgramModule extends Module {

    private data;
    protected events = {
        'program.play': { control: 'enter', title: 'پخش ویدیو', icon: 'enter' },
        'program.back': { control: 'back,backspace', title: 'بازگشت به برنامه‌ها', icon: 'refresh' },
        'program.down': { control: 'down', title: 'قسمت بعدی', icon: 'bottom' },
        'program.up': { control: 'up', title: 'قسمت قبلی', icon: 'up' },
        'program.right': { control: 'right', title: 'برنامه بعدی', icon: 'right' },
        'program.left': { control: 'left', title: 'برنامه قبلی', icon: 'left' },
        'program.enter': { control: 'enter', title: 'نمایش قسمت‌ها', icon: 'enter' },
    };

    constructor(config?, layoutInstance?, moduleType?: string) {
        super(config, layoutInstance, moduleType);
        this.service = ProgramService.instance;
        this.events = this.prepareControls();
        this.load();
        return this;
    }

    public load(callback?: any): void {
        const self = this;
        this.templateHelper.loading();
        this.service.getLatest().done((data: DefaultResponse) => {
            self.data = data.data;
            self.render(self.data, (data: Program[]) => {
                // End loading
                self.templateHelper.loading(false);
                self.initializeSlider();
            });
        });
    }

    public render(data: Program[], callback): void {
        this.templateHelper.render(programTemplate, { items: data }, this.$el, 'html', function () {
            if (typeof callback === 'function')
                callback(data);
        });
    }

    private initializeSlider(): void {
        const self = this;
        const $el = $("ul.program-items");
        const slidesToShow = 3;
        if (!$el.is(':visible'))
            $el.show(1);
        $el.slick({
            rtl: $("body").hasClass('rtl'),
            accessibility: false,
            slidesToShow: slidesToShow,
            slidesToScroll: 1,
            centerMode: true,
            focusOnSelect: true,
            infinite: true,
            speed: self.config.transitionSpeed,
            useCSS: false,
            useTransform: false
        });
        this.registerKeyboardInputs($el);
    }

    private registerKeyboardInputs($carousel): void {
        const self = this;
        this.input.addEvent('right', false, this.events['program.right'], () => {
            // Next News
            $carousel.slick('slickPrev');
        });
        this.input.addEvent('left', false, this.events['program.left'], () => {
            // Prev News
            $carousel.slick('slickNext');
        });
        this.input.addEvent('enter', false, this.events['program.enter'], () => {
            self.showEpisodes($carousel);
        });
        $(document).on('click', "ul.program-items li", (e) => {
            self.showEpisodes($carousel, $(this));
        });
    }

    private showEpisodes($carousel, $item?): void {
        let id: number;
        let item: Program;

        if (typeof $item !== 'undefined' && typeof $item.attr('data-id') !== 'undefined' && $item.attr('data-id')) {
            id = ~~$item.attr('data-id')
        } else {
            id = ~~$carousel.find('.slick-current.slick-center li').attr('data-id');
        }
        if (id > 0) {
            this.data.forEach((program) => {
                if (program.id === id) {
                    item = program;
                }
            });
            this.loadEpisodes(id, item);
        }
    }

    private loadEpisodes(programId: number, currentProgram: Program): void {
        const self = this;
        self.removeEpisodeKeyboardEvents(() => {
            self.templateHelper.loading();
            self.service.getEpisodes(programId).done((data: DefaultResponse<ProgramEpisode[]>) => {
                let items = data.data;
                if (items.length <= 5) {
                    const requiredItems = 6 - items.length;
                    for (let i = 0; i < requiredItems; i = i + items.length) {
                        items = [...items, ...items];
                    }
                }
                const episodeData = {
                    items,
                    program: currentProgram
                };
                self.renderEpisodes(episodeData, () => {
                    // End loading
                    self.templateHelper.loading(false);
                    self.removeProgramKeyboardEvents();
                    self.initializeEpisodesSlider();
                });
            });
        }, true);
    }

    private removeProgramKeyboardEvents(callback?): void {
        this.input.removeEvent('enter', this.events['program.enter']);
        this.input.removeEvent('left', this.events['program.right']);
        this.input.removeEvent('right', this.events['program.left']);
        if (typeof callback === 'function')
            callback();
    }

    private renderEpisodes(data, callback): void {
        this.templateHelper.render(episodeTemplate, { items: data }, $('#program-episodes'), 'html', function () {
            if (typeof callback === 'function')
                callback(data);
        });
    }

    private initializeEpisodesSlider(): void {
        const self = this;
        const $el = $("ul.episode-items");
        const slidesToShow = 5;
        if (!$el.is(':visible'))
            $el.show(1);
        $el.on('afterChange', () => {
            self.input.removeEvent('enter', self.events['program.play']);
            setTimeout(() => {
                self.input.addEvent('enter', false, self.events['program.play'], () => {
                    self.playVideo($el);
                });
            }, 500);
        });
        $el.slick({
            slidesToShow: slidesToShow,
            slidesToScroll: 1,
            vertical: true,
            centerMode: true,
            lazyLoad: 'ondemand',
        });
        this.registerEpisodesKeyboardInputs($el);
    }

    private registerEpisodesKeyboardInputs($carousel = $("ul.episode-items")): void {
        const self = this;
        this.input.removeEvent('back,backspace', { key: 'module.exit' });
        this.input.addEvent('back,backspace', false, this.events['program.back'], () => {
            self.unloadEpisodes();
        });
        this.input.addEvent('down', false, this.events['program.down'], () => {
            // Next Episode
            $carousel.slick('slickNext');
        });
        this.input.addEvent('up', false, this.events['program.up'], () => {
            // Prev Episode
            $carousel.slick('slickPrev');
        });
        setTimeout(() => {
            self.input.addEvent('enter', false, self.events['program.play'], () => {
                self.playVideo($carousel);
            });
        }, 200);
    }

    private removeEpisodeKeyboardEvents(callback?, handleReturn: boolean = true): void {
        this.input.removeEvent('up', this.events['program.up']);
        this.input.removeEvent('down', this.events['program.down']);
        this.input.removeEvent('enter', this.events['program.play']);
        if (handleReturn)
            this.input.removeEvent('back,backspace', this.events['program.back']);
        if (typeof callback === 'function')
            callback();
    }

    private getMediaUrl($carousel): string {
        const $current = $carousel.find('.slick-current.slick-center li');
        return $current.attr('data-media');
    }

    private getPoster($carousel): string {
        const $current = $carousel.find('.slick-current.slick-center li');
        return $current.find('img:first').attr('src');
    }

    private unloadEpisodes(): void {
        const self = this;
        $('#program-episodes').empty();
        this.removeEpisodeKeyboardEvents(() => {
            setTimeout(() => {
                self.registerKeyboardInputs($("ul.program-items"));
            }, 500)
        });
        setTimeout(() => {
            self.layoutInstance.prepareUnloadModule();
        }, 200);
    }

    private playVideo($carousel): void {
        if (this.templateHelper.hasClass('player-mode'))
            return;
        const self = this;
        const playerParams = {
            unloadMethod: () => {
                setTimeout(() => {
                    self.registerEpisodesKeyboardInputs();
                }, 200);
            },
            sources: [{
                src: self.getMediaUrl($carousel),
                poster: self.getPoster($carousel),
                type: self.getMediaUrl($carousel).indexOf('.m3u8') !== -1 ? 'application/x-mpegURL' : 'video/mp4',
                overrideNative: true
            }],
            html5: {
                vhs: {
                    overrideNative: true
                },
                nativeAudioTracks: false,
                nativeVideoTracks: false
            }
        };
        this.playerInstance = new this.playerService('mediaplayer', playerParams);
        this.removeEpisodeKeyboardEvents();
    }
}