summaryrefslogtreecommitdiff
path: root/addons/website_slides/static/src/js/slides_course_fullscreen_player.js
diff options
context:
space:
mode:
Diffstat (limited to 'addons/website_slides/static/src/js/slides_course_fullscreen_player.js')
-rw-r--r--addons/website_slides/static/src/js/slides_course_fullscreen_player.js427
1 files changed, 427 insertions, 0 deletions
diff --git a/addons/website_slides/static/src/js/slides_course_fullscreen_player.js b/addons/website_slides/static/src/js/slides_course_fullscreen_player.js
new file mode 100644
index 00000000000..1da173a2f46
--- /dev/null
+++ b/addons/website_slides/static/src/js/slides_course_fullscreen_player.js
@@ -0,0 +1,427 @@
+var onYouTubeIframeAPIReady;
+
+odoo.define('website_slides.fullscreen', function (require) {
+ 'use strict';
+ var sAnimations = require('website.content.snippets.animation');
+ var Widget = require('web.Widget');
+ var core = require('web.core');
+ var QWeb = core.qweb;
+
+ var tag = document.createElement('script');
+
+ var QuizWidget = require('website_slides.quiz');
+
+ tag.src = "https://www.youtube.com/iframe_api";
+ var firstScriptTag = document.getElementsByTagName('script')[0];
+ firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
+
+
+ var Fullscreen = Widget.extend({
+ /**
+ * @override
+ * @param {Object} el
+ * @param {Object} data holding channelId and optionally upload and publish control parameters
+ */
+ init: function(el, course_id,slide_id, user_id){
+ this._super.apply(this,arguments);
+ this.courseID = parseInt(course_id, 10);
+ this.slideID = parseInt(slide_id, 10);
+ this.userID = parseInt(user_id, 10);
+ this.course = undefined;
+ this.slide = undefined;
+ this.slides = [];
+ this.nextSlide = undefined;
+ this.previousSlide = undefined;
+ this.url = undefined;
+ this.urlToSmallScreen = undefined;
+ this.activetab = undefined;
+ this.player = undefined;
+ this.goToQuiz = false;
+ this.answeredQuestions = [];
+ this.slideTitle = undefined;
+ },
+ start: function(){
+ var self = this;
+ this.url = window.location.pathname;
+ this.urlToSmallScreen = this.url.replace('/fullscreen','');
+ this._getSlides();
+ this._renderPlayer();
+ this._bindListEvents();
+
+ $(document).keydown(this._onKeyDown.bind(this));
+ return this._super.apply(this, arguments);
+ },
+ //--------------------------------------------------------------------------
+ // Private
+ //--------------------------------------------------------------------------
+ /**
+ * @private
+ * Renders the player accordingly to the current slide
+ */
+ _renderPlayer: function(){
+ var self = this;
+ var embed_url;
+ if(self.slide.slide_type !== 'webpage' || self.slide.htmlContent){
+ if((self.slide.slide_type == "quiz" || self.slide.hasQuiz) && !self.slide.quiz){
+ self._fetchQuiz();
+ } else {
+ embed_url = $(this.slide.embed_code).attr('src');
+ if (self.slide.slide_type === "video"){
+ embed_url = "https://" + embed_url + "&rel=0&autoplay=1&enablejsapi=1&origin=" + window.location.origin;
+ }
+ $('.o_wslides_fullscreen_player').html(QWeb.render('website.slides.fullscreen', {
+ slide: self.slide,
+ nextSlide: self.nextSlide,
+ questions: self.slide.quiz ? self.slide.quiz.questions: '',
+ reward: self.slide.quiz ? self.slide.quiz.nb_attempts < 3 ? self.slide.quiz.possible_rewards[self.slide.quiz.nb_attempts] : self.slide.quiz.possible_rewards[3]: self.slide.maxPoints,
+ embed_url: embed_url,
+ question_count: self.slide.quiz ? self.slide.quiz.questions.length : '',
+ letters: self.slide.quiz ? self.letters : '',
+ showMiniQuiz: self.goToQuiz
+ }));
+ if(self.slide.slide_type === "video"){
+ self._renderYoutubeIframe();
+ }
+ if(self.slide.slide_type == 'webpage'){
+ self._renderWebpage();
+ }
+ if((self.slide.slide_type == "presentation" || self.slide.slide_type == "document" || self.slide.slide_type == "infographic" || self.slide.slide_type == "webpage") && !self.slide.quiz){
+ self._setSlideStateAsDone();
+ }
+ if ((self.slide.quiz && self.slide.slide_type === "quiz") || self.goToQuiz){
+ self._renderQuiz();
+ }
+ }
+ } else {
+ self._fetchHtmlContent();
+ }
+ self._renderTitle();
+ },
+ _renderYoutubeIframe: function(){
+ var self = this;
+ /**
+ * Due to issues of synchronization between the youtube api script and the widget's instanciation.
+ */
+ try{
+ self._setupYoutubePlayer();
+ }
+ catch {
+ onYouTubeIframeAPIReady = function(){
+ var self = this;
+ self._setupYoutubePlayer();
+ }.bind(this)
+ }
+ },
+ _renderWebpage: function(){
+ var self = this;
+ $(self.slide.htmlContent).appendTo('.o_wslides_fullscreen_webpage_content');
+ },
+ _renderQuiz: function(){
+ var self = this;
+ var Quiz = new QuizWidget(this, self.slide);
+ Quiz.appendTo('.o_wslides_fullscreen_player');
+ $('.next-slide').click(function(){
+ self._goToNextSlide();
+ })
+ $('.back-to-video').click(function(){
+ self.goToQuiz = false;
+ self._renderPlayer();
+ });
+ },
+ _renderTitle: function(){
+ var self = this;
+ $('.o_wslides_fullscreen_slide_title').empty().html(QWeb.render('website.course.fullscreen.title', {
+ slide: self.slide,
+ miniQuiz: self.goToQuiz
+ }));
+ },
+ /**
+ * @private
+ * Links the youtube api to the iframe present in the template
+ */
+ _setupYoutubePlayer: function(){
+ var self = this;
+ self.player = new YT.Player('youtube-player', {
+ host: 'https://www.youtube.com',
+ playerVars: {'autoplay': 1, 'origin': window.location.origin},
+ autoplay: 1,
+ events: {
+ 'onReady': self._onPlayerReady,
+ 'onStateChange': this._onPlayerStateChange.bind(self)
+ }
+ });
+ },
+ /**
+ * @param {*} event
+ * Specific method of the youtube api.
+ * Whenever the player starts playing, a setinterval is created.
+ * This setinterval is used to check te user's progress in the video.
+ * Once the user reaches a particular time in the video, the slide will be considered as completed if the video doesn't have a mini-quiz.
+ * This method also allows to automatically go to the next slide (or the quiz associated to the current video) once the video is over
+ */
+ _onPlayerStateChange: function(event){
+ var self = this;
+ var tid;
+ clearInterval(self.tid);
+ if (event.data == YT.PlayerState.PLAYING && !self.slide.done) {
+ self.tid = setInterval(function(){
+ if(event.target.getCurrentTime){
+ var currentTime = event.target.getCurrentTime();
+ var totalTime = event.target.getDuration();
+ if(totalTime && currentTime > totalTime - 30){
+ clearInterval(self.tid);
+ if(!self.slide.hasQuiz && !self.slide.done){
+ self.slide.done = true;
+ self._setSlideStateAsDone();
+ }
+ }
+ }
+ }, 1000);
+ }
+ if(event.data == YT.PlayerState.ENDED){
+ self.player = undefined;
+ self._goToNextSlide();
+ }
+ },
+ /**
+ * @private
+ * Creates slides objects from every slide-list-cells attributes
+ */
+ _getSlides: function(){
+ var self = this;
+ var slides = $('.o_wslides_fullscreen_sidebar_slide_tab');
+ for(var i = 0; i < slides.length;i++){
+ var slide = $(slides[i]);
+ self.slides.push({
+ id: parseInt(slide.attr('slide_id'), 10),
+ name: slide.attr('slide_name'),
+ embed_code: slide.attr('slide_embed_code'),
+ slide_type: slide.attr('slide_type'),
+ done: slide.attr('done'),
+ hasQuiz: slide.attr('quiz'),
+ slug: slide.attr('slide_slug'),
+ htmlContent: undefined
+ });
+ this._getActiveSlide();
+ }
+ },
+ /**
+ * @private
+ * @param {object} slide
+ * Fetch the quiz for a particular slide
+ */
+ _fetchQuiz: function(){
+ var self = this;
+ self._rpc({
+ route:"/slide/quiz/get",
+ params: {
+ 'slide_id': self.slide.id
+ }
+ }).then(function(data){
+ if(data){
+ self.slide.quiz = data;
+ self._renderPlayer();
+ }
+ })
+ },
+ _fetchHtmlContent: function(){
+ var self = this;
+ self._rpc({
+ route:"/slide/html_content/get",
+ params: {
+ 'slide_id': self.slide.id
+ }
+ }).then(function(data){
+ self.slide.htmlContent = data.html_content;
+ self._renderPlayer();
+ })
+ },
+ /**
+ * @private
+ * Once the completion conditions are filled,
+ * sends a json request to the backend to set the relation between the slide and the user as being completed
+ */
+ _setSlideStateAsDone: function(){
+ var self = this;
+ self._rpc({
+ route: '/slides/set_completed',
+ params: {
+ slide_id: self.slide.id,
+ }
+ }).then(function(data){
+ $('#check-'+self.slide.id).replaceWith($('<i class="check-done o_wslides_slide_completed fa fa-check-circle"></i>'))
+ self.slide.done = true;
+ clearInterval(self.tid);
+ self.channelCompletion = data.channel_completion;
+ self._updateProgressbar();
+ });
+ },
+ _updateProgressbar: function(){
+ var self = this;
+ var completion = self.channelCompletion <= 100 ? self.channelCompletion : 100;
+ $('.o_wslides_fullscreen_sidebar_progress_gauge').css('width', completion + "%" );
+ $('.o_wslides_progress_percentage').text(completion);
+ },
+ /**
+ * @private
+ * Creates an array of letters to be used in the quiz with a foreach
+ */
+ _generateQuizLetters: function(){
+ var letters = [];
+ for(var i = 65; i < 91; i++){
+ letters.push(String.fromCharCode(i));
+ }
+ return letters;
+ },
+ _goToNextSlide: function(){
+ var self = this;
+ clearInterval(self.tid);
+ self.player = undefined;
+ self.goToQuiz = self.slide.hasQuiz && !self.goToQuiz;
+ if(self.nextSlide && !self.goToQuiz){
+ self.slide = self.nextSlide;
+ self.index++;
+ self._setActiveTab();
+ self._renderPlayer();
+ self._setPreviousAndNextSlides();
+ self._updateUrl();
+ history.pushState(null,'',self.url);
+ }
+ else if(self.nextSlide){
+ self._renderPlayer();
+ }
+ },
+ _goToPreviousSlide: function(){
+ var self = this;
+ clearInterval(self.tid);
+ self.goToQuiz = false;
+ self.player = undefined;
+ if(self.previousSlide){
+ self.slide = self.previousSlide;
+ self.index--;
+ self._setActiveTab();
+ self._renderPlayer();
+ self._setPreviousAndNextSlides();
+ self._updateUrl();
+ history.pushState(null,'',self.url);
+ }
+ },
+ _setPreviousAndNextSlides: function(){
+ var self = this;
+ self.previousSlide = self.index > 0 ? self.slides[self.index-1] : undefined;
+ self.nextSlide = self.index < (self.slides.length - 1) ? self.slides[self.index+1] : undefined;
+ },
+ /**
+ * Changes the url whenever the user changes slides.
+ * This allows the user to refresh the page and stay at the right video
+ */
+ _updateUrl: function(){
+ var self = this;
+ var url = window.location.pathname.split('/');
+ url[url.length-1] = self.slide.slug;
+ url = url.join('/');
+ self.url = url;
+ self.urlToSmallScreen = self.url;
+ self.url += "?fullscreen=1";
+ $('.o_wslides_small_screen').attr('href', self.urlToSmallScreen);
+ },
+ /**
+ * Whenever the user changes slide, change the active tab
+ */
+ _setActiveTab: function(){
+ var self = this;
+ self.activeTab.removeClass('active');
+ $('li.active').removeClass('active');
+ $('li[slide_id='+self.slide.id+']').addClass('active');
+ self.activeTab = $('.o_wslides_fullscreen_sidebar_slide_tab[index="'+self.index+'"]')
+ self.activeTab.addClass('active');
+ },
+ /**
+ * The first time the user gets on the player,
+ * get the slide that is represented by the active tab in the sidebar
+ */
+ _getActiveSlide: function(){
+ var self = this;
+ self.activeTab = $('.o_wslides_fullscreen_sidebar_slide_tab.active');
+ self.index = parseInt(self.activeTab.attr('index'), 10);
+ self.slide = self.slides[self.index];
+ self._setPreviousAndNextSlides();
+ },
+ //--------------------------------------------------------------------------
+ // Handlers
+ //--------------------------------------------------------------------------
+ _onListCellClick: function(ev){
+ var self = this;
+ clearInterval(self.tid);
+ self.player = undefined;
+ var target = $(ev.currentTarget);
+ self.goToQuiz = false;
+ if(target[0] !== self.activeTab[0]){
+ self.activeTab.removeClass('active');
+ target.addClass('active');
+ self.index = parseInt(target.attr('index'));
+ self._getActiveSlide();
+ self._renderPlayer();
+ $('li.active').removeClass('active');
+ $('li[slide_id='+self.slide.id+']').addClass('active');
+ self._setPreviousAndNextSlides();
+ self._updateUrl();
+ history.pushState(null,'',self.url);
+ }
+ },
+ _onMiniQuizClick: function(ev){
+ var self = this;
+ self.index = parseInt($(ev.currentTarget).attr('index'));
+ self.slide = self.slides[self.index];
+ self.goToQuiz = true;
+ self._setPreviousAndNextSlides();
+ self._renderPlayer();
+ self._setActiveTab();
+ self._updateUrl();
+ history.pushState(null,'' ,self.url);
+ },
+ /**
+ * @private
+ * Binds events related to the list
+ */
+ _bindListEvents: function(){
+ var self = this;
+ $('.o_wslides_fullscreen_sidebar_slide_tab').each(function () {
+ $(this).click(self._onListCellClick.bind(self));
+ });
+
+ $('.o_wslides_slide_quiz ').each(function(){
+ $(this).click(self._onMiniQuizClick.bind(self));
+ })
+ },
+ _onKeyDown: function(ev){
+ var self = this;
+ switch(ev.key){
+ case "ArrowRight":
+ self._goToNextSlide();
+ break;
+ case "ArrowLeft":
+ self._goToPreviousSlide();
+ break;
+ }
+ },
+ })
+
+ sAnimations.registry.websiteSlidesFullscreenPlayer = Widget.extend({
+ selector: '.oe_js_course_slide',
+ xmlDependencies: ['/website_slides/static/src/xml/website_slides_fullscreen.xml'],
+ init: function(el){
+ this._super.apply(this, arguments);
+ },
+ start: function(){
+ this._super.apply(this, arguments);
+ var user_id = this.$el.attr('user_id');
+ var course_id = this.$el.attr('course_id');
+ var slide_id = this.$el.attr('slide_id');
+ var fullscreen = new Fullscreen(this, course_id, slide_id, user_id);
+ fullscreen.appendTo(".oe_js_course_slide");
+ }
+ });
+
+ return Fullscreen;
+}); \ No newline at end of file