From f2fdaf8bb02961c60e53bd65d8c619fc5dc905cf Mon Sep 17 00:00:00 2001 From: Daniel Thies Date: Fri, 4 Aug 2023 06:51:11 -0500 Subject: [PATCH] VID-683: Move session code to pro pluing --- amd/build/player.min.js | 2 +- amd/build/player.min.js.map | 2 +- amd/build/resize_tab_player.min.js.map | 2 +- amd/build/videotime.min.js | 2 +- amd/build/videotime.min.js.map | 2 +- amd/src/videotime.js | 32 ++++++++++++++++--- plugin/videojs/amd/build/videotime.min.js | 2 +- plugin/videojs/amd/build/videotime.min.js.map | 2 +- plugin/videojs/amd/src/videotime.js | 14 ++------ 9 files changed, 37 insertions(+), 23 deletions(-) diff --git a/amd/build/player.min.js b/amd/build/player.min.js index d25dd02c..667e0299 100644 --- a/amd/build/player.min.js +++ b/amd/build/player.min.js @@ -1,3 +1,3 @@ -var e,t;e=window,t=function(){function r(e,t){for(var n=0;n\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Player from \"mod_videotime/player\";\nimport Notification from \"core/notification\";\n\nlet column;\n\n/**\n * Intialize listener\n */\nexport const initialize = () => {\n let observer = new ResizeObserver(resize),\n mutationobserver = new MutationObserver(resize);\n mutationobserver.observe(document.querySelector('#page-content'), {subtree: true, childList: true});\n document.querySelectorAll('.instance-container, div.videotime-tab-instance').forEach((container) => {\n observer.observe(container);\n });\n document.querySelectorAll('.videotime-tab-instance').forEach((instance) => {\n instance.style.position = 'absolute';\n });\n resize();\n\n window.removeEventListener('mousemove', mousemoveHandler);\n window.addEventListener('mousemove', mousemoveHandler);\n\n window.removeEventListener('dragstart', dragstartHandler);\n window.addEventListener('dragstart', dragstartHandler);\n\n window.removeEventListener('mouseup', dragendHandler);\n window.addEventListener('mouseup', dragendHandler);\n\n window.removeEventListener('click', cueVideo);\n window.addEventListener('click', cueVideo);\n};\n\n/**\n * Adjust player position when the page configuration is changed\n */\nconst resize = () => {\n document.querySelectorAll('.instance-container').forEach((container) => {\n if (!container.offsetWidth) {\n // Ignore if it is not visible.\n return;\n }\n container.closest('.videotimetabs').querySelectorAll('.videotime-tab-instance').forEach(() => {\n let instance = container.closest('.videotimetabs').querySelector('.videotime-tab-instance'),\n content = container.closest('.videotimetabs').querySelector('.tab-content');\n Object.assign(instance.style, {\n top: container.offsetTop + 'px',\n left: container.offsetLeft + 'px',\n maxWidth: container.offsetWidth + 'px',\n width: container.offsetWidth + 'px'\n });\n container.style.minHeight = instance.offsetHeight + 5 + 'px';\n container.closest('.videotimetabs').querySelectorAll('.videotime-tab-instance-cover').forEach((cover) => {\n Object.assign(cover.style, {\n height: content.offsetHeight + 'px',\n left: content.offsetLeft + 'px',\n top: content.offsetTop + 'px',\n width: content.offsetWidth + 'px'\n });\n });\n });\n });\n};\n\n/**\n * Reset handle when drag ends\n */\nconst dragendHandler = () => {\n document.querySelectorAll('.videotime-tab-instance-cover').forEach((cover) => {\n cover.style.display = 'none';\n });\n};\n\n/**\n * Prepare to drag divider\n *\n * @param {event} e mouse event\n */\nconst dragstartHandler = (e) => {\n if (e.target.classList.contains('videotimetab-resize-handle')) {\n column = e.target.closest('.tab-pane').querySelector('.videotimetab-resize');\n e.stopPropagation();\n e.preventDefault();\n document.querySelectorAll('.videotime-tab-instance-cover').forEach((cover) => {\n cover.style.display = 'block';\n });\n }\n};\n\n/**\n * Resize the content and player to mouse location\n *\n * @param {event} e mouse event\n */\nconst mousemoveHandler = (e) => {\n document.querySelectorAll('.videotimetab-resize-handle').forEach((h) => {\n if (h.closest('.tab-pane') && document.querySelector('.videotime-tab-instance-cover').style.display == 'block') {\n column.style.width = e.pageX - column.getBoundingClientRect().left + 'px';\n }\n });\n};\n\n/**\n * Move video to new time when link clicked\n *\n * @param {event} e mouse event\n */\nconst cueVideo = (e) => {\n if (e.target.closest('[data-action=\"cue\"]')) {\n let starttime = e.target.closest('a').getAttribute('data-start'),\n time = starttime.match(/((([0-9]+):)?(([0-9]+):))?([0-9]+(\\.[0-9]+)?)/),\n iframe = e.target.closest('.videotimetabs').querySelector('.vimeo-embed iframe'),\n player = new Player(iframe);\n e.preventDefault();\n e.stopPropagation();\n if (time) {\n player\n .setCurrentTime(3600 * Number(time[3] || 0) + 60 * Number(time[5] || 0) + Number(time[6]))\n .then(player.play.bind(player))\n .catch(Notification.exception);\n }\n }\n};\n"],"names":["column","observer","ResizeObserver","resize","MutationObserver","observe","document","querySelector","subtree","childList","querySelectorAll","forEach","container","instance","style","position","window","removeEventListener","mousemoveHandler","addEventListener","dragstartHandler","dragendHandler","cueVideo","offsetWidth","closest","content","Object","assign","top","offsetTop","left","offsetLeft","maxWidth","width","minHeight","offsetHeight","cover","height","display","e","target","classList","contains","stopPropagation","preventDefault","h","pageX","getBoundingClientRect","time","getAttribute","match","iframe","player","Player","setCurrentTime","Number","then","play","bind","catch","Notification","exception"],"mappings":";;;;;;;;SAYIA,0MAKsB,SAClBC,SAAW,IAAIC,eAAeC,QACX,IAAIC,iBAAiBD,QAC3BE,QAAQC,SAASC,cAAc,iBAAkB,CAACC,SAAS,EAAMC,WAAW,IAC7FH,SAASI,iBAAiB,mDAAmDC,SAASC,YAClFX,SAASI,QAAQO,cAErBN,SAASI,iBAAiB,2BAA2BC,SAASE,WAC1DA,SAASC,MAAMC,SAAW,cAE9BZ,SAEAa,OAAOC,oBAAoB,YAAaC,kBACxCF,OAAOG,iBAAiB,YAAaD,kBAErCF,OAAOC,oBAAoB,YAAaG,kBACxCJ,OAAOG,iBAAiB,YAAaC,kBAErCJ,OAAOC,oBAAoB,UAAWI,gBACtCL,OAAOG,iBAAiB,UAAWE,gBAEnCL,OAAOC,oBAAoB,QAASK,UACpCN,OAAOG,iBAAiB,QAASG,iBAM/BnB,OAAS,KACXG,SAASI,iBAAiB,uBAAuBC,SAASC,YACjDA,UAAUW,aAIfX,UAAUY,QAAQ,kBAAkBd,iBAAiB,2BAA2BC,SAAQ,SAChFE,SAAWD,UAAUY,QAAQ,kBAAkBjB,cAAc,2BAC7DkB,QAAUb,UAAUY,QAAQ,kBAAkBjB,cAAc,gBAChEmB,OAAOC,OAAOd,SAASC,MAAO,CAC1Bc,IAAKhB,UAAUiB,UAAY,KAC3BC,KAAMlB,UAAUmB,WAAa,KAC7BC,SAAUpB,UAAUW,YAAc,KAClCU,MAAOrB,UAAUW,YAAc,OAEnCX,UAAUE,MAAMoB,UAAYrB,SAASsB,aAAe,EAAI,KACxDvB,UAAUY,QAAQ,kBAAkBd,iBAAiB,iCAAiCC,SAASyB,QAC3FV,OAAOC,OAAOS,MAAMtB,MAAO,CACvBuB,OAAQZ,QAAQU,aAAe,KAC/BL,KAAML,QAAQM,WAAa,KAC3BH,IAAKH,QAAQI,UAAY,KACzBI,MAAOR,QAAQF,YAAc,iBAU3CF,eAAiB,KACnBf,SAASI,iBAAiB,iCAAiCC,SAASyB,QAChEA,MAAMtB,MAAMwB,QAAU,WASxBlB,iBAAoBmB,IAClBA,EAAEC,OAAOC,UAAUC,SAAS,gCAC5B1C,OAASuC,EAAEC,OAAOhB,QAAQ,aAAajB,cAAc,wBACrDgC,EAAEI,kBACFJ,EAAEK,iBACFtC,SAASI,iBAAiB,iCAAiCC,SAASyB,QAChEA,MAAMtB,MAAMwB,QAAU,aAU5BpB,iBAAoBqB,IACtBjC,SAASI,iBAAiB,+BAA+BC,SAASkC,IAC1DA,EAAErB,QAAQ,cAAyF,SAAzElB,SAASC,cAAc,iCAAiCO,MAAMwB,UACxFtC,OAAOc,MAAMmB,MAAQM,EAAEO,MAAQ9C,OAAO+C,wBAAwBjB,KAAO,UAU3ER,SAAYiB,OACVA,EAAEC,OAAOhB,QAAQ,uBAAwB,KAErCwB,KADYT,EAAEC,OAAOhB,QAAQ,KAAKyB,aAAa,cAC9BC,MAAM,iDACvBC,OAASZ,EAAEC,OAAOhB,QAAQ,kBAAkBjB,cAAc,uBAC1D6C,OAAS,IAAIC,gBAAOF,QACxBZ,EAAEK,iBACFL,EAAEI,kBACEK,MACAI,OACKE,eAAe,KAAOC,OAAOP,KAAK,IAAM,GAAK,GAAKO,OAAOP,KAAK,IAAM,GAAKO,OAAOP,KAAK,KACrFQ,KAAKJ,OAAOK,KAAKC,KAAKN,SACtBO,MAAMC,sBAAaC"} \ No newline at end of file +{"version":3,"file":"resize_tab_player.min.js","sources":["../src/resize_tab_player.js"],"sourcesContent":["/*\n * Position the Vimeo player within tab layout\n *\n * @package mod_videotime\n * @module mod_videotime/resize_tab_player\n * @copyright 2021 bdecent gmbh \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Player from \"mod_videotime/player\";\nimport Notification from \"core/notification\";\n\nlet column;\n\n/**\n * Intialize listener\n */\nexport const initialize = () => {\n let observer = new ResizeObserver(resize),\n mutationobserver = new MutationObserver(resize);\n mutationobserver.observe(document.querySelector('#page-content'), {subtree: true, childList: true});\n document.querySelectorAll('.instance-container, div.videotime-tab-instance').forEach((container) => {\n observer.observe(container);\n });\n document.querySelectorAll('.videotime-tab-instance').forEach((instance) => {\n instance.style.position = 'absolute';\n });\n resize();\n\n window.removeEventListener('mousemove', mousemoveHandler);\n window.addEventListener('mousemove', mousemoveHandler);\n\n window.removeEventListener('dragstart', dragstartHandler);\n window.addEventListener('dragstart', dragstartHandler);\n\n window.removeEventListener('mouseup', dragendHandler);\n window.addEventListener('mouseup', dragendHandler);\n\n window.removeEventListener('click', cueVideo);\n window.addEventListener('click', cueVideo);\n};\n\n/**\n * Adjust player position when the page configuration is changed\n */\nconst resize = () => {\n document.querySelectorAll('.instance-container').forEach((container) => {\n if (!container.offsetWidth) {\n // Ignore if it is not visible.\n return;\n }\n container.closest('.videotimetabs').querySelectorAll('.videotime-tab-instance').forEach(() => {\n let instance = container.closest('.videotimetabs').querySelector('.videotime-tab-instance'),\n content = container.closest('.videotimetabs').querySelector('.tab-content');\n Object.assign(instance.style, {\n top: container.offsetTop + 'px',\n left: container.offsetLeft + 'px',\n maxWidth: container.offsetWidth + 'px',\n width: container.offsetWidth + 'px'\n });\n container.style.minHeight = instance.offsetHeight + 5 + 'px';\n container.closest('.videotimetabs').querySelectorAll('.videotime-tab-instance-cover').forEach((cover) => {\n Object.assign(cover.style, {\n height: content.offsetHeight + 'px',\n left: content.offsetLeft + 'px',\n top: content.offsetTop + 'px',\n width: content.offsetWidth + 'px'\n });\n });\n });\n });\n};\n\n/**\n * Reset handle when drag ends\n */\nconst dragendHandler = () => {\n document.querySelectorAll('.videotime-tab-instance-cover').forEach((cover) => {\n cover.style.display = 'none';\n });\n};\n\n/**\n * Prepare to drag divider\n *\n * @param {event} e mouse event\n */\nconst dragstartHandler = (e) => {\n if (e.target.classList.contains('videotimetab-resize-handle')) {\n column = e.target.closest('.tab-pane').querySelector('.videotimetab-resize');\n e.stopPropagation();\n e.preventDefault();\n document.querySelectorAll('.videotime-tab-instance-cover').forEach((cover) => {\n cover.style.display = 'block';\n });\n }\n};\n\n/**\n * Resize the content and player to mouse location\n *\n * @param {event} e mouse event\n */\nconst mousemoveHandler = (e) => {\n document.querySelectorAll('.videotimetab-resize-handle').forEach((h) => {\n if (h.closest('.tab-pane') && document.querySelector('.videotime-tab-instance-cover').style.display == 'block') {\n column.style.width = e.pageX - column.getBoundingClientRect().left + 'px';\n }\n });\n};\n\n/**\n * Move video to new time when link clicked\n *\n * @param {event} e mouse event\n */\nconst cueVideo = (e) => {\n if (e.target.closest('[data-action=\"cue\"]')) {\n let starttime = e.target.closest('a').getAttribute('data-start'),\n time = starttime.match(/((([0-9]+):)?(([0-9]+):))?([0-9]+(\\.[0-9]+)?)/),\n iframe = e.target.closest('.videotimetabs').querySelector('.vimeo-embed iframe'),\n player = new Player(iframe);\n e.preventDefault();\n e.stopPropagation();\n if (time) {\n player\n .setCurrentTime(3600 * Number(time[3] || 0) + 60 * Number(time[5] || 0) + Number(time[6]))\n .then(player.play.bind(player))\n .catch(Notification.exception);\n }\n }\n};\n"],"names":["column","observer","ResizeObserver","resize","MutationObserver","observe","document","querySelector","subtree","childList","querySelectorAll","forEach","container","instance","style","position","window","removeEventListener","mousemoveHandler","addEventListener","dragstartHandler","dragendHandler","cueVideo","offsetWidth","closest","content","Object","assign","top","offsetTop","left","offsetLeft","maxWidth","width","minHeight","offsetHeight","cover","height","display","e","target","classList","contains","stopPropagation","preventDefault","h","pageX","getBoundingClientRect","time","getAttribute","match","iframe","player","Player","setCurrentTime","Number","then","play","bind","catch","Notification","exception"],"mappings":";;;;;;;;SAYIA,0MAKsB,SAClBC,SAAW,IAAIC,eAAeC,QACX,IAAIC,iBAAiBD,QAC3BE,QAAQC,SAASC,cAAc,iBAAkB,CAACC,SAAS,EAAMC,WAAW,IAC7FH,SAASI,iBAAiB,mDAAmDC,SAASC,YAClFX,SAASI,QAAQO,cAErBN,SAASI,iBAAiB,2BAA2BC,SAASE,WAC1DA,SAASC,MAAMC,SAAW,UAA1B,IAEJZ,SAEAa,OAAOC,oBAAoB,YAAaC,kBACxCF,OAAOG,iBAAiB,YAAaD,kBAErCF,OAAOC,oBAAoB,YAAaG,kBACxCJ,OAAOG,iBAAiB,YAAaC,kBAErCJ,OAAOC,oBAAoB,UAAWI,gBACtCL,OAAOG,iBAAiB,UAAWE,gBAEnCL,OAAOC,oBAAoB,QAASK,UACpCN,OAAOG,iBAAiB,QAASG,iBAM/BnB,OAAS,KACXG,SAASI,iBAAiB,uBAAuBC,SAASC,YACjDA,UAAUW,aAIfX,UAAUY,QAAQ,kBAAkBd,iBAAiB,2BAA2BC,SAAQ,SAChFE,SAAWD,UAAUY,QAAQ,kBAAkBjB,cAAc,2BAC7DkB,QAAUb,UAAUY,QAAQ,kBAAkBjB,cAAc,gBAChEmB,OAAOC,OAAOd,SAASC,MAAO,CAC1Bc,IAAKhB,UAAUiB,UAAY,KAC3BC,KAAMlB,UAAUmB,WAAa,KAC7BC,SAAUpB,UAAUW,YAAc,KAClCU,MAAOrB,UAAUW,YAAc,OAEnCX,UAAUE,MAAMoB,UAAYrB,SAASsB,aAAe,EAAI,KACxDvB,UAAUY,QAAQ,kBAAkBd,iBAAiB,iCAAiCC,SAASyB,QAC3FV,OAAOC,OAAOS,MAAMtB,MAAO,CACvBuB,OAAQZ,QAAQU,aAAe,KAC/BL,KAAML,QAAQM,WAAa,KAC3BH,IAAKH,QAAQI,UAAY,KACzBI,MAAOR,QAAQF,YAAc,iBAU3CF,eAAiB,KACnBf,SAASI,iBAAiB,iCAAiCC,SAASyB,QAChEA,MAAMtB,MAAMwB,QAAU,MAAtB,KASFlB,iBAAoBmB,IAClBA,EAAEC,OAAOC,UAAUC,SAAS,gCAC5B1C,OAASuC,EAAEC,OAAOhB,QAAQ,aAAajB,cAAc,wBACrDgC,EAAEI,kBACFJ,EAAEK,iBACFtC,SAASI,iBAAiB,iCAAiCC,SAASyB,QAChEA,MAAMtB,MAAMwB,QAAU,OAAtB,MAUNpB,iBAAoBqB,IACtBjC,SAASI,iBAAiB,+BAA+BC,SAASkC,IAC1DA,EAAErB,QAAQ,cAAyF,SAAzElB,SAASC,cAAc,iCAAiCO,MAAMwB,UACxFtC,OAAOc,MAAMmB,MAAQM,EAAEO,MAAQ9C,OAAO+C,wBAAwBjB,KAAO,UAU3ER,SAAYiB,OACVA,EAAEC,OAAOhB,QAAQ,uBAAwB,KAErCwB,KADYT,EAAEC,OAAOhB,QAAQ,KAAKyB,aAAa,cAC9BC,MAAM,iDACvBC,OAASZ,EAAEC,OAAOhB,QAAQ,kBAAkBjB,cAAc,uBAC1D6C,OAAS,IAAIC,gBAAOF,QACxBZ,EAAEK,iBACFL,EAAEI,kBACEK,MACAI,OACKE,eAAe,KAAOC,OAAOP,KAAK,IAAM,GAAK,GAAKO,OAAOP,KAAK,IAAM,GAAKO,OAAOP,KAAK,KACrFQ,KAAKJ,OAAOK,KAAKC,KAAKN,SACtBO,MAAMC,sBAAaC"} \ No newline at end of file diff --git a/amd/build/videotime.min.js b/amd/build/videotime.min.js index 0ea4f2c8..5376a70f 100644 --- a/amd/build/videotime.min.js +++ b/amd/build/videotime.min.js @@ -3,6 +3,6 @@ * @copyright 2021 bdecent gmbh * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -define("mod_videotime/videotime",["jquery","mod_videotime/player","core/ajax","core/config","core/log","core/templates","core/notification"],(function($,Vimeo,Ajax,Config,Log,Templates,Notification){let VideoTime=function(elementId,cmId,hasPro,interval,instance){this.elementId=elementId,this.cmId=cmId,this.hasPro=hasPro,this.interval=interval,this.player=null,this.resumeTime=null,this.session=null,this.instance=instance,this.played=!1,this.playing=!1,this.time=0,this.percent=0,this.currentTime=0,this.playbackRate=1,this.plugins=[],hasPro&&$("body").hasClass("path-course-view")&&!$("body").hasClass("vtinit")&&($("body").addClass("vtinit"),$(document).on("focus","body",this.initializeNewInstances.bind(this))),this.modulecount=$("body .activity.videotime").length};return VideoTime.prototype.getCmId=function(){return this.cmId},VideoTime.prototype.registerPlugin=function(plugin){this.plugins.push(plugin)},VideoTime.prototype.initialize=function(){let instance=this.instance;Log.debug("Initializing Video Time "+this.elementId),Log.debug("Initializing Vimeo player with options:"),Log.debug(instance),this.player=new Vimeo(this.elementId,{autopause:Number(instance.autopause),autoplay:Number(instance.autoplay),background:Number(instance.background),byline:Number(instance.byline),color:instance.color,controls:Number(instance.controls),dnt:Number(instance.dnt),height:instance.height,loop:Number(instance.option_loop),maxheight:instance.maxheight,maxwidth:instance.maxwidth,muted:Number(instance.muted),portrait:instance.portrait,pip:Number(instance.pip),playsinline:instance.playsinline,responsive:Number(instance.responsive),speed:instance.speed,title:Number(instance.title),transparent:Number(instance.transparent),url:instance.vimeo_url,width:instance.width});let url=new URL(window.location.href),q=url.searchParams.get("q"),starttime=(url.searchParams.get("time")||"").match(/^([0-9]+:){0,2}([0-9]+)(\.[0-9]+)$/);starttime?this.setStartTime(starttime[0]).then((function(){return q&&window.find&&window.find(q),!0})).catch(Notification.exception):q&&window.find&&window.find(q),this.addListeners();for(let i=0;i(this.hasPro&&this.startWatchInterval(),this.view(),!0))),this.hasPro&&(this.player.on("loaded",(()=>!this.instance.resume_playback||this.instance.resume_time<=0||this.getDuration().then((duration=>{let resumeTime=this.instance.resume_time;return resumeTime+1>=Math.floor(duration)&&(Log.debug("VIDEO_TIME video finished, resuming at start of video."),resumeTime=0),Log.debug("VIDEO_TIME duration is "+duration),Log.debug("VIDEO_TIME resuming at "+resumeTime),this.setCurrentPosition(resumeTime),!0})).fail(Notification.exception))),this.player.on("play",function(){this.playing=!0,Log.debug("VIDEO_TIME play")}.bind(this)),this.player.on("playing",function(){this.playing=!0,Log.debug("VIDEO_TIME playing")}.bind(this)),this.player.on("pause",function(){this.playing=!1,Log.debug("VIDEO_TIME pause")}.bind(this)),this.player.on("stalled",function(){this.playing=!1,Log.debug("VIDEO_TIME stalled")}.bind(this)),this.player.on("suspend",function(){this.playing=!1,Log.debug("VIDEO_TIME suspend")}.bind(this)),this.player.on("abort",function(){this.playing=!1,Log.debug("VIDEO_TIME abort")}.bind(this)),this.player.getPlaybackRate().then(function(playbackRate){this.playbackRate=playbackRate}.bind(this)).catch(Notification.exception),this.player.on("playbackratechange",function(event){this.playbackRate=event.playbackRate}.bind(this)),this.player.on("timeupdate",function(event){this.percent=event.percent,this.currentTime=event.seconds,Log.debug("VIDEO_TIME timeupdate. Percent: "+this.percent+". Current time: "+this.currentTime)}.bind(this)),this.player.on("ended",this.handleEnd.bind(this)))):Log.debug("Player was not properly initialized for course module "+this.cmId)},VideoTime.prototype.handleEnd=function(){this.playing=!1,Log.debug("VIDEO_TIME ended"),this.getSession().then(function(session){this.setSessionState(session.id,1).then((()=>this.setPercent(session.id,1))).then((()=>this.setCurrentTime(session.id,this.currentTime))).then((()=>this.getNextActivityButtonData(session.id).then((response=>{let data=JSON.parse(response.data);if(data.instance&&parseInt(data.instance.next_activity_auto)&&!data.is_restricted&&data.hasnextcm){let link=$('.aalink[href="'+data.nextcm_url+'"] img').first();$(".path-course-view").length&&link?link.click():window.location.href=data.nextcm_url}return Templates.render("videotime/next_activity_button",JSON.parse(response.data)).then((function(html){return $("#next-activity-button").html(html),!0}))})))).catch(Notification.exception)}.bind(this)).catch(Notification.exception)},VideoTime.prototype.startWatchInterval=function(){this.watchInterval||(this.watchInterval=setInterval(function(){this.playing&&(this.time+=this.playbackRate,this.getSession().then(function(session){return this.time%this.interval==0&&(Log.debug("VIDEO_TIME watch_time: "+this.time+". percent: "+this.percent),this.recordWatchTime(session.id,this.time),this.setPercent(session.id,this.percent),this.setCurrentTime(session.id,this.currentTime)),!0}.bind(this)).catch(Notification.exception))}.bind(this),1e3))},VideoTime.prototype.setSessionState=function(sessionId,state){if(this.instance.token){const url=new URL(Config.wwwroot+"/webservice/rest/server.php"),data=url.searchParams;return data.set("wstoken",this.instance.token),data.set("moodlewsrestformat","json"),data.set("wsfunction","videotimeplugin_pro_set_session_state"),data.set("state",state),data.set("session_id",sessionId),fetch(url).then((response=>(response.ok||Notification.exeption("Web service error"),response.json())))}return Ajax.call([{methodname:"videotimeplugin_pro_set_session_state",args:{session_id:sessionId,state:state},fail:Notification.exception}])[0]},VideoTime.prototype.setCurrentTime=function(sessionId,currentTime){if(this.instance.token){const url=new URL(Config.wwwroot+"/webservice/rest/server.php"),data=url.searchParams;return data.set("wstoken",this.instance.token),data.set("moodlewsrestformat","json"),data.set("wsfunction","videotimeplugin_pro_set_session_current_time"),data.set("current_time",currentTime),data.set("session_id",sessionId),fetch(url).then((response=>(response.ok||Notification.exeption("Web service error"),response.json())))}return Ajax.call([{methodname:"videotimeplugin_pro_set_session_current_time",args:{session_id:sessionId,current_time:currentTime},fail:Notification.exception}])[0]},VideoTime.prototype.setPercent=function(sessionId,percent){if(this.instance.token){const url=new URL(Config.wwwroot+"/webservice/rest/server.php"),data=url.searchParams;return data.set("wstoken",this.instance.token),data.set("moodlewsrestformat","json"),data.set("wsfunction","videotimeplugin_pro_set_percent"),data.set("percent",percent),data.set("session_id",sessionId),fetch(url).then((response=>(response.ok||Notification.exeption("Web service error"),response.json())))}return Ajax.call([{methodname:"videotimeplugin_pro_set_percent",args:{session_id:sessionId,percent:percent},fail:Notification.exception}])[0]},VideoTime.prototype.recordWatchTime=function(sessionId,time){if(this.instance.token){const url=new URL(Config.wwwroot+"/webservice/rest/server.php"),data=url.searchParams;return data.set("wstoken",this.instance.token),data.set("moodlewsrestformat","json"),data.set("wsfunction","videotimeplugin_pro_record_watch_time"),data.set("session_id",sessionId),data.set("time",time),fetch(url).then((response=>(response.ok||Notification.exeption("Web service error"),response.json())))}return Ajax.call([{methodname:"videotimeplugin_pro_record_watch_time",args:{session_id:sessionId,time:time},fail:Notification.exception}])[0]},VideoTime.prototype.getNextActivityButtonData=function(sessionId){return this.instance.token?Promise.resolve({data:"{}"}):Ajax.call([{methodname:"videotimeplugin_pro_get_next_activity_button_data",args:{session_id:sessionId}}])[0]},VideoTime.prototype.getInstance=function(){return this.instance?Promise.resolve(this.instance):Ajax.call([{methodname:"mod_videotime_get_videotime",args:{cmid:this.cmId},done:response=>(this.instance=response,this.instance),fail:Notification.exception}])[0]},VideoTime.prototype.getResumeTime=function(){return this.resumeTime?Promise.resolve(this.resumeTime):Ajax.call([{methodname:"videotimeplugin_pro_get_resume_time",args:{cmid:this.cmId},done:response=>(this.resumeTime=response.seconds,this.resumeTime),fail:Notification.exception}])[0]},VideoTime.prototype.getSession=function(){if(this.instance.token){if(!this.session){const url=new URL(Config.wwwroot+"/webservice/rest/server.php"),data=url.searchParams;data.set("wstoken",this.instance.token),data.set("moodlewsrestformat","json"),data.set("wsfunction","videotimeplugin_pro_get_new_session"),data.set("cmid",this.cmId),this.session=fetch(url).then((response=>(response.ok||Notification.exeption("Web service error"),response.json())))}return this.session}return this.session||(this.session=Ajax.call([{methodname:"videotimeplugin_pro_get_new_session",args:{cmid:this.cmId},fail:Notification.exception}])[0]),this.session},VideoTime.prototype.setStartTime=function(starttime){let time=starttime.match(/((([0-9]+):)?(([0-9]+):))?([0-9]+(\.[0-9]+))/);return time&&(this.resumeTime=3600*Number(time[3]||0)+60*Number(time[5]||0)+Number(time[6]),this.currentTime(this.resumeTime)),this.player.getCurrentTime()},VideoTime.prototype.view=function(){if(this.instance.token){const url=new URL(Config.wwwroot+"/webservice/rest/server.php"),data=url.searchParams;return data.set("wstoken",this.instance.token),data.set("moodlewsrestformat","json"),data.set("wsfunction","mod_videotime_view_videotime"),data.set("cmid",this.cmId),fetch(url).then((response=>(response.ok||Notification.exeption("Web service error"),response.json())))}return Ajax.call([{methodname:"mod_videotime_view_videotime",args:{cmid:this.cmId},fail:Notification.exception}])[0]},VideoTime.prototype.initializeNewInstances=function(){this.modulecount!=$("body .activity.videotime").length&&(this.modulecount=$("body .activity.videotime").length,$("body .activity.videotime").each(function(index,module){if(!$(module).find(".instancename").length&&$(module).find(".vimeo-embed").length&&!$(module).find(".vimeo-embed iframe").length){let instance={cmid:Number($(module).attr("id").replace("module-","")),haspro:!0,interval:this.interval,uniqueid:$(module).find(".vimeo-embed").first().attr("id").replace("vimeo-embed-","")};Templates.render("mod_videotime/videotime_instance",{instance:instance}).then((function(html,js){return Templates.runTemplateJS(js),!0})).fail(Notification.exception)}}.bind(this)))},VideoTime.prototype.getPlaybackRate=function(){return this.player.getPlaybackRate()},VideoTime.prototype.getDuration=function(){return this.player.getDuration()},VideoTime.prototype.setCurrentPosition=function(secs){return this.player.setCurrentTime(secs)},VideoTime.prototype.getCurrentPosition=function(){return this.player.getCurrentTime()},VideoTime})); +define("mod_videotime/videotime",["jquery","mod_videotime/player","core/ajax","core/config","core/log","core/templates","core/notification"],(function($,Vimeo,Ajax,Config,Log,Templates,Notification){let VideoTime=function(elementId,cmId,hasPro,interval,instance){this.elementId=elementId,this.cmId=cmId,this.hasPro=hasPro,this.interval=interval,this.player=null,this.resumeTime=null,this.session=null,this.instance=instance,this.played=!1,this.playing=!1,this.time=0,this.percent=0,this.currentTime=0,this.playbackRate=1,this.plugins=[],hasPro&&$("body").hasClass("path-course-view")&&!$("body").hasClass("vtinit")&&($("body").addClass("vtinit"),$(document).on("focus","body",this.initializeNewInstances.bind(this))),this.modulecount=$("body .activity.videotime").length};return VideoTime.prototype.getCmId=function(){return this.cmId},VideoTime.prototype.registerPlugin=function(plugin){this.plugins.push(plugin)},VideoTime.prototype.initialize=function(){let instance=this.instance;Log.debug("Initializing Video Time "+this.elementId),Log.debug("Initializing Vimeo player with options:"),Log.debug(instance),this.player=new Vimeo(this.elementId,{autopause:Number(instance.autopause),autoplay:Number(instance.autoplay),background:Number(instance.background),byline:Number(instance.byline),color:instance.color,controls:Number(instance.controls),dnt:Number(instance.dnt),height:instance.height,loop:Number(instance.option_loop),maxheight:instance.maxheight,maxwidth:instance.maxwidth,muted:Number(instance.muted),portrait:instance.portrait,pip:Number(instance.pip),playsinline:instance.playsinline,responsive:Number(instance.responsive),speed:instance.speed,title:Number(instance.title),transparent:Number(instance.transparent),url:instance.vimeo_url,width:instance.width});let url=new URL(window.location.href),q=url.searchParams.get("q"),starttime=(url.searchParams.get("time")||"").match(/^([0-9]+:){0,2}([0-9]+)(\.[0-9]+)$/);starttime?this.setStartTime(starttime[0]).then((function(){return q&&window.find&&window.find(q),!0})).catch(Notification.exception):q&&window.find&&window.find(q),this.addListeners();for(let i=0;i(this.hasPro&&this.startWatchInterval(),this.view(),!0))),this.hasPro&&(this.player.on("loaded",(()=>!this.instance.resume_playback||!this.instance.resume_time||this.instance.resume_time<=0||this.getDuration().then((duration=>{let resumeTime=this.instance.resume_time;return resumeTime+1>=Math.floor(duration)&&(Log.debug("VIDEO_TIME video finished, resuming at start of video."),resumeTime=0),Log.debug("VIDEO_TIME duration is "+duration),Log.debug("VIDEO_TIME resuming at "+resumeTime),this.setCurrentPosition(resumeTime),!0})).fail(Notification.exception))),this.player.on("play",function(){this.playing=!0,Log.debug("VIDEO_TIME play")}.bind(this)),this.player.on("playing",function(){this.playing=!0,Log.debug("VIDEO_TIME playing")}.bind(this)),this.player.on("pause",function(){this.playing=!1,Log.debug("VIDEO_TIME pause")}.bind(this)),this.player.on("stalled",function(){this.playing=!1,Log.debug("VIDEO_TIME stalled")}.bind(this)),this.player.on("suspend",function(){this.playing=!1,Log.debug("VIDEO_TIME suspend")}.bind(this)),this.player.on("abort",function(){this.playing=!1,Log.debug("VIDEO_TIME abort")}.bind(this)),this.player.getPlaybackRate().then(function(playbackRate){this.playbackRate=playbackRate}.bind(this)).catch(Notification.exception),this.player.on("playbackratechange",function(event){this.playbackRate=event.playbackRate}.bind(this)),this.player.on("timeupdate",function(event){this.percent=event.percent,this.currentTime=event.seconds}.bind(this)),this.player.on("ended",this.handleEnd.bind(this)))):Log.debug("Player was not properly initialized for course module "+this.cmId)},VideoTime.prototype.handleEnd=function(){this.playing=!1,Log.debug("VIDEO_TIME ended"),this.plugins.length>2?this.plugins.forEach((plugin=>{Log.debug(plugin),"function"==typeof plugin.handleEnd&&plugin.handleEnd()})):this.getSession().then(function(session){this.setSessionState(session.id,1).then((()=>this.setPercent(session.id,1))).then((()=>this.setCurrentTime(session.id,this.currentTime))).then((()=>this.getNextActivityButtonData(session.id).then((response=>{let data=JSON.parse(response.data);if(data.instance&&parseInt(data.instance.next_activity_auto)&&!data.is_restricted&&data.hasnextcm){let link=$('.aalink[href="'+data.nextcm_url+'"] img').first();$(".path-course-view").length&&link?link.click():window.location.href=data.nextcm_url}return Templates.render("videotime/next_activity_button",JSON.parse(response.data)).then((function(html){return $("#next-activity-button").html(html),!0}))})))).catch(Notification.exception)}.bind(this)).catch(Notification.exception)},VideoTime.prototype.startWatchInterval=function(){this.plugins.forEach((plugin=>{"function"==typeof plugin.startWatchInterval&&(this.watchInterval=!0,plugin.startWatchInterval())})),this.watchInterval||(this.watchInterval=setInterval(function(){this.playing&&(this.time+=this.playbackRate,this.getSession().then(function(session){return this.time%this.interval==0&&(Log.debug("VIDEO_TIME watch_time: "+this.time+". percent: "+this.percent),this.recordWatchTime(session.id,this.time),this.setPercent(session.id,this.percent),this.setCurrentTime(session.id,this.currentTime)),!0}.bind(this)).catch(Notification.exception))}.bind(this),1e3))},VideoTime.prototype.setSessionState=function(sessionId,state){if(this.instance.token){const url=new URL(Config.wwwroot+"/webservice/rest/server.php"),data=url.searchParams;return data.set("wstoken",this.instance.token),data.set("moodlewsrestformat","json"),data.set("wsfunction","videotimeplugin_pro_set_session_state"),data.set("state",state),data.set("session_id",sessionId),fetch(url).then((response=>(response.ok||Notification.exeption("Web service error"),response.json())))}return Ajax.call([{methodname:"videotimeplugin_pro_set_session_state",args:{session_id:sessionId,state:state},fail:Notification.exception}])[0]},VideoTime.prototype.setCurrentTime=function(sessionId,currentTime){if(this.instance.token){const url=new URL(Config.wwwroot+"/webservice/rest/server.php"),data=url.searchParams;return data.set("wstoken",this.instance.token),data.set("moodlewsrestformat","json"),data.set("wsfunction","videotimeplugin_pro_set_session_current_time"),data.set("current_time",currentTime),data.set("session_id",sessionId),fetch(url).then((response=>(response.ok||Notification.exeption("Web service error"),response.json())))}return Ajax.call([{methodname:"videotimeplugin_pro_set_session_current_time",args:{session_id:sessionId,current_time:currentTime},fail:Notification.exception}])[0]},VideoTime.prototype.setPercent=function(sessionId,percent){if(this.instance.token){const url=new URL(Config.wwwroot+"/webservice/rest/server.php"),data=url.searchParams;return data.set("wstoken",this.instance.token),data.set("moodlewsrestformat","json"),data.set("wsfunction","videotimeplugin_pro_set_percent"),data.set("percent",percent),data.set("session_id",sessionId),fetch(url).then((response=>(response.ok||Notification.exeption("Web service error"),response.json())))}return Ajax.call([{methodname:"videotimeplugin_pro_set_percent",args:{session_id:sessionId,percent:percent},fail:Notification.exception}])[0]},VideoTime.prototype.recordWatchTime=function(sessionId,time){if(this.instance.token){const url=new URL(Config.wwwroot+"/webservice/rest/server.php"),data=url.searchParams;return data.set("wstoken",this.instance.token),data.set("moodlewsrestformat","json"),data.set("wsfunction","videotimeplugin_pro_record_watch_time"),data.set("session_id",sessionId),data.set("time",time),fetch(url).then((response=>(response.ok||Notification.exeption("Web service error"),response.json())))}return Ajax.call([{methodname:"videotimeplugin_pro_record_watch_time",args:{session_id:sessionId,time:time},fail:Notification.exception}])[0]},VideoTime.prototype.getNextActivityButtonData=function(sessionId){return this.instance.token?Promise.resolve({data:"{}"}):Ajax.call([{methodname:"videotimeplugin_pro_get_next_activity_button_data",args:{session_id:sessionId}}])[0]},VideoTime.prototype.getInstance=function(){return this.instance?Promise.resolve(this.instance):Ajax.call([{methodname:"mod_videotime_get_videotime",args:{cmid:this.cmId},done:response=>(this.instance=response,this.instance),fail:Notification.exception}])[0]},VideoTime.prototype.getResumeTime=function(){return this.resumeTime?Promise.resolve(this.resumeTime):Ajax.call([{methodname:"videotimeplugin_pro_get_resume_time",args:{cmid:this.cmId},done:response=>(this.resumeTime=response.seconds,this.resumeTime),fail:Notification.exception}])[0]},VideoTime.prototype.getSession=function(){if(this.instance.token){if(!this.session){const url=new URL(Config.wwwroot+"/webservice/rest/server.php"),data=url.searchParams;data.set("wstoken",this.instance.token),data.set("moodlewsrestformat","json"),data.set("wsfunction","videotimeplugin_pro_get_new_session"),data.set("cmid",this.cmId),this.session=fetch(url).then((response=>(response.ok||Notification.exeption("Web service error"),response.json())))}return this.session}return this.session||(this.session=Ajax.call([{methodname:"videotimeplugin_pro_get_new_session",args:{cmid:this.cmId},fail:Notification.exception}])[0]),this.session},VideoTime.prototype.setStartTime=function(starttime){let time=starttime.match(/((([0-9]+):)?(([0-9]+):))?([0-9]+(\.[0-9]+))/);return time&&(this.resumeTime=3600*Number(time[3]||0)+60*Number(time[5]||0)+Number(time[6]),this.currentTime(this.resumeTime)),this.player.getCurrentTime()},VideoTime.prototype.view=function(){if(this.instance.token){const url=new URL(Config.wwwroot+"/webservice/rest/server.php"),data=url.searchParams;return data.set("wstoken",this.instance.token),data.set("moodlewsrestformat","json"),data.set("wsfunction","mod_videotime_view_videotime"),data.set("cmid",this.cmId),fetch(url).then((response=>(response.ok||Notification.exeption("Web service error"),response.json())))}return Ajax.call([{methodname:"mod_videotime_view_videotime",args:{cmid:this.cmId},fail:Notification.exception}])[0]},VideoTime.prototype.initializeNewInstances=function(){this.modulecount!=$("body .activity.videotime").length&&(this.modulecount=$("body .activity.videotime").length,$("body .activity.videotime").each(function(index,module){if(!$(module).find(".instancename").length&&$(module).find(".vimeo-embed").length&&!$(module).find(".vimeo-embed iframe").length){let instance={cmid:Number($(module).attr("id").replace("module-","")),haspro:!0,interval:this.interval,uniqueid:$(module).find(".vimeo-embed").first().attr("id").replace("vimeo-embed-","")};Templates.render("mod_videotime/videotime_instance",{instance:instance}).then((function(html,js){return Templates.runTemplateJS(js),!0})).fail(Notification.exception)}}.bind(this)))},VideoTime.prototype.getPlaybackRate=function(){return this.player.getPlaybackRate()},VideoTime.prototype.getDuration=function(){return this.player.getDuration()},VideoTime.prototype.setCurrentPosition=function(secs){return this.player.setCurrentTime(secs)},VideoTime.prototype.getCurrentPosition=function(){return this.player.getCurrentTime()},VideoTime})); //# sourceMappingURL=videotime.min.js.map \ No newline at end of file diff --git a/amd/build/videotime.min.js.map b/amd/build/videotime.min.js.map index e456bebb..1b085a43 100644 --- a/amd/build/videotime.min.js.map +++ b/amd/build/videotime.min.js.map @@ -1 +1 @@ -{"version":3,"file":"videotime.min.js","sources":["../src/videotime.js"],"sourcesContent":["/*\n * @package mod_videotime\n * @copyright 2021 bdecent gmbh \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\n/**\n * @module mod_videotime/videotime\n */\ndefine([\n 'jquery',\n 'mod_videotime/player',\n 'core/ajax',\n 'core/config',\n 'core/log',\n 'core/templates',\n 'core/notification'\n], function($, Vimeo, Ajax, Config, Log, Templates, Notification) {\n let VideoTime = function(elementId, cmId, hasPro, interval, instance) {\n this.elementId = elementId;\n this.cmId = cmId;\n this.hasPro = hasPro;\n this.interval = interval;\n this.player = null;\n this.resumeTime = null;\n this.session = null;\n this.instance = instance;\n\n this.played = false;\n\n this.playing = false;\n this.time = 0;\n this.percent = 0;\n this.currentTime = 0;\n this.playbackRate = 1;\n\n this.plugins = [];\n\n if (hasPro && $('body').hasClass('path-course-view') && !$('body').hasClass('vtinit')) {\n $('body').addClass('vtinit');\n $(document).on('focus', 'body', this.initializeNewInstances.bind(this));\n }\n this.modulecount = $('body .activity.videotime').length;\n };\n\n /**\n * Get course module ID of this VideoTime instance.\n *\n * @return {int}\n */\n VideoTime.prototype.getCmId = function() {\n return this.cmId;\n };\n\n /**\n * Register a plugin to hook into VideoTime functionality.\n *\n * @param {VideoTimePlugin} plugin\n */\n VideoTime.prototype.registerPlugin = function(plugin) {\n this.plugins.push(plugin);\n };\n\n VideoTime.prototype.initialize = function() {\n let instance = this.instance;\n Log.debug('Initializing Video Time ' + this.elementId);\n\n Log.debug('Initializing Vimeo player with options:');\n Log.debug(instance);\n this.player = new Vimeo(this.elementId, {\n autopause: Number(instance.autopause),\n autoplay: Number(instance.autoplay),\n background: Number(instance.background),\n byline: Number(instance.byline),\n color: instance.color,\n controls: Number(instance.controls),\n dnt: Number(instance.dnt),\n height: instance.height,\n loop: Number(instance.option_loop),\n maxheight: instance.maxheight,\n maxwidth: instance.maxwidth,\n muted: Number(instance.muted),\n portrait: instance.portrait,\n pip: Number(instance.pip),\n playsinline: instance.playsinline,\n responsive: Number(instance.responsive),\n speed: instance.speed,\n title: Number(instance.title),\n transparent: Number(instance.transparent),\n url: instance.vimeo_url,\n width: instance.width\n });\n\n let url = new URL(window.location.href),\n q = url.searchParams.get('q'),\n starttime = (url.searchParams.get('time') || '').match(/^([0-9]+:){0,2}([0-9]+)(\\.[0-9]+)$/);\n if (starttime) {\n this.setStartTime(starttime[0]).then(function() {\n if (q && window.find) {\n window.find(q);\n }\n return true;\n }).catch(Notification.exception);\n } else if (q && window.find) {\n window.find(q);\n }\n\n this.addListeners();\n\n for (let i = 0; i < this.plugins.length; i++) {\n const plugin = this.plugins[i];\n plugin.initialize(this, instance);\n }\n\n return true;\n };\n\n /**\n * Get Vimeo player object.\n *\n * @returns {Vimeo}\n */\n VideoTime.prototype.getPlayer = function() {\n return this.player;\n };\n\n /**\n * Register player events to respond to user interaction and play progress.\n */\n VideoTime.prototype.addListeners = function() {\n if (!this.player) {\n Log.debug('Player was not properly initialized for course module ' + this.cmId);\n return;\n }\n\n // If this is a tab play set time cues and listener.\n $($('#' + this.elementId).closest('.videotimetabs')).each(function(i, tabs) {\n $(tabs).find('[data-action=\"cue\"]').each(function(index, anchor) {\n let starttime = anchor.getAttribute('data-start'),\n time = starttime.match(/((([0-9]+):)?(([0-9]+):))?([0-9]+(\\.[0-9]+))/);\n if (time) {\n this.player.addCuePoint(\n 3600 * Number(time[3] || 0) + 60 * Number(time[5] || 0) + Number(time[6]),\n {\n starttime: starttime\n }\n ).catch(Notification.exeception);\n }\n }.bind(this));\n\n this.player.on('cuepoint', function(event) {\n if (event.data.starttime) {\n $(tabs).find('.videotime-highlight').removeClass('videotime-highlight');\n $(tabs).find('[data-action=\"cue\"][data-start=\"' + event.data.starttime + '\"]')\n .closest('.row')\n .addClass('videotime-highlight');\n $('.videotime-highlight').each(function() {\n if (this.offsetTop) {\n this.parentNode.scrollTo({\n top: this.offsetTop - 50,\n left: 0,\n behavior: 'smooth'\n });\n }\n });\n }\n });\n }.bind(this));\n\n // Fire view event in Moodle on first play only.\n this.player.on('play', () => {\n if (this.hasPro) {\n this.startWatchInterval();\n }\n this.view();\n return true;\n });\n\n // Features beyond this point are for pro only.\n if (!this.hasPro) {\n return;\n }\n\n // If resume is present force seek the player to that point.\n this.player.on(\"loaded\", () => {\n if (!this.instance.resume_playback || this.instance.resume_time <= 0) {\n return true;\n }\n\n return this.getDuration().then((duration) => {\n let resumeTime = this.instance.resume_time;\n // Duration is often a little greater than a resume time at the end of the video.\n // A user may have watched 100 seconds when the video ends, but the duration may be\n // 100.56 seconds. BUT, sometimes the duration is rounded depending on when the\n // video loads, so it may be 101 seconds. Hence the +1 and Math.floor usage.\n if (resumeTime + 1 >= Math.floor(duration)) {\n Log.debug(\n \"VIDEO_TIME video finished, resuming at start of video.\"\n );\n resumeTime = 0;\n }\n Log.debug(\"VIDEO_TIME duration is \" + duration);\n Log.debug(\"VIDEO_TIME resuming at \" + resumeTime);\n this.setCurrentPosition(resumeTime);\n return true;\n }).fail(Notification.exception);\n });\n\n // Note: Vimeo player does not support multiple events in a single on() call. Each requires it's own function.\n\n // Catch all events where video plays.\n this.player.on('play', function() {\n this.playing = true;\n Log.debug('VIDEO_TIME play');\n }.bind(this));\n this.player.on('playing', function() {\n this.playing = true;\n Log.debug('VIDEO_TIME playing');\n }.bind(this));\n\n // Catch all events where video stops.\n this.player.on('pause', function() {\n this.playing = false;\n Log.debug('VIDEO_TIME pause');\n }.bind(this));\n this.player.on('stalled', function() {\n this.playing = false;\n Log.debug('VIDEO_TIME stalled');\n }.bind(this));\n this.player.on('suspend', function() {\n this.playing = false;\n Log.debug('VIDEO_TIME suspend');\n }.bind(this));\n this.player.on('abort', function() {\n this.playing = false;\n Log.debug('VIDEO_TIME abort');\n }.bind(this));\n\n this.player.getPlaybackRate().then(function(playbackRate) {\n this.playbackRate = playbackRate;\n }.bind(this)).catch(Notification.exception);\n\n this.player.on('playbackratechange', function(event) {\n this.playbackRate = event.playbackRate;\n }.bind(this));\n\n // Always update internal values for percent and current time watched.\n this.player.on('timeupdate', function(event) {\n this.percent = event.percent;\n this.currentTime = event.seconds;\n Log.debug('VIDEO_TIME timeupdate. Percent: ' + this.percent + '. Current time: ' + this.currentTime);\n }.bind(this));\n\n // Initiate video finish procedure.\n this.player.on('ended', this.handleEnd.bind(this));\n };\n\n /**\n * Start interval that will periodically record user progress via Ajax.\n */\n VideoTime.prototype.handleEnd = function() {\n this.playing = false;\n Log.debug('VIDEO_TIME ended');\n\n this.getSession().then(function(session) {\n this.setSessionState(session.id, 1).then(() => {\n return this.setPercent(session.id, 1);\n }).then(() => {\n return this.setCurrentTime(session.id, this.currentTime);\n }).then(() => {\n return this.getNextActivityButtonData(session.id).then(response => {\n let data = JSON.parse(response.data);\n\n if (data.instance && parseInt(data.instance.next_activity_auto)) {\n if (!data.is_restricted && data.hasnextcm) {\n let link = $('.aalink[href=\"' + data.nextcm_url + '\"] img').first();\n if ($('.path-course-view').length && link) {\n link.click();\n } else {\n window.location.href = data.nextcm_url;\n }\n }\n }\n\n return Templates.render('videotime/next_activity_button', JSON.parse(response.data))\n .then(function(html) {\n $('#next-activity-button').html(html);\n return true;\n });\n });\n }).catch(Notification.exception);\n }.bind(this)).catch(Notification.exception);\n };\n\n /**\n * Start interval that will periodically record user progress via Ajax.\n */\n VideoTime.prototype.startWatchInterval = function() {\n if (this.watchInterval) {\n return;\n }\n\n this.watchInterval = setInterval(function() {\n if (this.playing) {\n this.time += this.playbackRate;\n\n this.getSession().then(function(session) {\n if (this.time % this.interval === 0) {\n Log.debug('VIDEO_TIME watch_time: ' + this.time + '. percent: ' + this.percent);\n this.recordWatchTime(session.id, this.time);\n this.setPercent(session.id, this.percent);\n this.setCurrentTime(session.id, this.currentTime);\n }\n return true;\n }.bind(this)).catch(Notification.exception);\n }\n }.bind(this), 1000);\n };\n\n /**\n * Set state on session.\n *\n * @param {int} sessionId\n * @param {int} state\n * @returns {Promise}\n */\n VideoTime.prototype.setSessionState = function(sessionId, state) {\n if (this.instance.token) {\n const url = new URL(Config.wwwroot + '/webservice/rest/server.php'),\n data = url.searchParams;\n data.set('wstoken', this.instance.token);\n data.set('moodlewsrestformat', 'json');\n data.set('wsfunction', 'videotimeplugin_pro_set_session_state');\n data.set('state', state);\n data.set('session_id', sessionId);\n return fetch(url).then((response) => {\n if (!response.ok) {\n Notification.exeption('Web service error');\n }\n return response.json();\n });\n }\n\n return Ajax.call([{\n methodname: 'videotimeplugin_pro_set_session_state',\n args: {\"session_id\": sessionId, state: state},\n fail: Notification.exception\n }])[0];\n };\n\n /**\n * Set current watch time for video. Used for resuming.\n *\n * @param {int} sessionId\n * @param {float} currentTime\n * @returns {Promise}\n */\n VideoTime.prototype.setCurrentTime = function(sessionId, currentTime) {\n if (this.instance.token) {\n const url = new URL(Config.wwwroot + '/webservice/rest/server.php'),\n data = url.searchParams;\n data.set('wstoken', this.instance.token);\n data.set('moodlewsrestformat', 'json');\n data.set('wsfunction', 'videotimeplugin_pro_set_session_current_time');\n data.set('current_time', currentTime);\n data.set('session_id', sessionId);\n return fetch(url).then((response) => {\n if (!response.ok) {\n Notification.exeption('Web service error');\n }\n return response.json();\n });\n }\n return Ajax.call([{\n methodname: 'videotimeplugin_pro_set_session_current_time',\n args: {\"session_id\": sessionId, \"current_time\": currentTime},\n fail: Notification.exception\n }])[0];\n };\n\n /**\n * Set video watch percentage for session.\n *\n * @param {int} sessionId\n * @param {float} percent\n * @returns {Promise}\n */\n VideoTime.prototype.setPercent = function(sessionId, percent) {\n if (this.instance.token) {\n const url = new URL(Config.wwwroot + '/webservice/rest/server.php'),\n data = url.searchParams;\n data.set('wstoken', this.instance.token);\n data.set('moodlewsrestformat', 'json');\n data.set('wsfunction', 'videotimeplugin_pro_set_percent');\n data.set('percent', percent);\n data.set('session_id', sessionId);\n return fetch(url).then((response) => {\n if (!response.ok) {\n Notification.exeption('Web service error');\n }\n return response.json();\n });\n }\n return Ajax.call([{\n methodname: 'videotimeplugin_pro_set_percent',\n args: {\"session_id\": sessionId, percent: percent},\n fail: Notification.exception\n }])[0];\n };\n\n /**\n * Record watch time for session.\n *\n * @param {int} sessionId\n * @param {float} time\n * @returns {Promise}\n */\n VideoTime.prototype.recordWatchTime = function(sessionId, time) {\n if (this.instance.token) {\n const url = new URL(Config.wwwroot + '/webservice/rest/server.php'),\n data = url.searchParams;\n data.set('wstoken', this.instance.token);\n data.set('moodlewsrestformat', 'json');\n data.set('wsfunction', 'videotimeplugin_pro_record_watch_time');\n data.set('session_id', sessionId);\n data.set('time', time);\n return fetch(url).then((response) => {\n if (!response.ok) {\n Notification.exeption('Web service error');\n }\n return response.json();\n });\n }\n return Ajax.call([{\n methodname: 'videotimeplugin_pro_record_watch_time',\n args: {\"session_id\": sessionId, time: time},\n fail: Notification.exception\n }])[0];\n };\n\n /**\n * Get data for next activity button.\n *\n * @param {int} sessionId\n * @returns {Promise}\n */\n VideoTime.prototype.getNextActivityButtonData = function(sessionId) {\n if (this.instance.token) {\n // We do not support button in iframe.\n return Promise.resolve({data: '{}'});\n }\n return Ajax.call([{\n methodname: 'videotimeplugin_pro_get_next_activity_button_data',\n args: {\"session_id\": sessionId}\n }])[0];\n };\n\n /**\n * Get VideoTime instance for this course module.\n *\n * @returns {Promise}\n */\n VideoTime.prototype.getInstance = function() {\n if (this.instance) {\n return Promise.resolve(this.instance);\n }\n\n return Ajax.call([{\n methodname: 'mod_videotime_get_videotime',\n args: {cmid: this.cmId},\n done: (response) => {\n this.instance = response;\n return this.instance;\n },\n fail: Notification.exception\n }])[0];\n };\n\n /**\n * Get time to resume video as seconds.\n *\n * @returns {Promise}\n */\n VideoTime.prototype.getResumeTime = function() {\n if (this.resumeTime) {\n return Promise.resolve(this.resumeTime);\n }\n\n return Ajax.call([{\n methodname: 'videotimeplugin_pro_get_resume_time',\n args: {cmid: this.cmId},\n done: (response) => {\n this.resumeTime = response.seconds;\n return this.resumeTime;\n },\n fail: Notification.exception\n }])[0];\n };\n\n /**\n * Get new or existing video viewing session.\n *\n * @returns {Promise}\n */\n VideoTime.prototype.getSession = function() {\n if (this.instance.token) {\n if (!this.session) {\n const url = new URL(Config.wwwroot + '/webservice/rest/server.php'),\n data = url.searchParams;\n data.set('wstoken', this.instance.token);\n data.set('moodlewsrestformat', 'json');\n data.set('wsfunction', 'videotimeplugin_pro_get_new_session');\n data.set('cmid', this.cmId);\n this.session = fetch(url).then((response) => {\n if (!response.ok) {\n Notification.exeption('Web service error');\n }\n return response.json();\n });\n }\n\n return this.session;\n }\n if (!this.session) {\n this.session = Ajax.call([{\n methodname: 'videotimeplugin_pro_get_new_session',\n args: {cmid: this.cmId},\n fail: Notification.exception\n }])[0];\n }\n return this.session;\n };\n\n /**\n * Parse start time and set player\n *\n * @param {string} starttime\n * @returns {Promise}\n */\n VideoTime.prototype.setStartTime = function(starttime) {\n let time = starttime.match(/((([0-9]+):)?(([0-9]+):))?([0-9]+(\\.[0-9]+))/);\n if (time) {\n this.resumeTime = 3600 * Number(time[3] || 0) + 60 * Number(time[5] || 0) + Number(time[6]);\n this.currentTime(this.resumeTime);\n }\n return this.player.getCurrentTime();\n };\n\n /**\n * Log the user view of video.\n *\n * @returns {Promise}\n */\n VideoTime.prototype.view = function() {\n if (this.instance.token) {\n const url = new URL(Config.wwwroot + '/webservice/rest/server.php'),\n data = url.searchParams;\n data.set('wstoken', this.instance.token);\n data.set('moodlewsrestformat', 'json');\n data.set('wsfunction', 'mod_videotime_view_videotime');\n data.set('cmid', this.cmId);\n return fetch(url).then((response) => {\n if (!response.ok) {\n Notification.exeption('Web service error');\n }\n return response.json();\n });\n\n }\n return Ajax.call([{\n methodname: 'mod_videotime_view_videotime',\n args: {cmid: this.cmId},\n fail: Notification.exception\n }])[0];\n };\n\n /**\n * Initialize new labels and preview when editing\n */\n VideoTime.prototype.initializeNewInstances = function() {\n if (this.modulecount == $('body .activity.videotime').length) {\n return;\n }\n this.modulecount = $('body .activity.videotime').length;\n $('body .activity.videotime').each(function(index, module) {\n if (\n !$(module).find('.instancename').length\n && $(module).find('.vimeo-embed').length\n && !$(module).find('.vimeo-embed iframe').length\n ) {\n let instance = {\n cmid: Number($(module).attr('id').replace('module-', '')),\n haspro: true,\n interval: this.interval,\n uniqueid: $(module).find('.vimeo-embed').first().attr('id').replace('vimeo-embed-', '')\n };\n Templates.render('mod_videotime/videotime_instance', {\n instance: instance\n }).then(function(html, js) {\n Templates.runTemplateJS(js);\n return true;\n }).fail(Notification.exception);\n }\n }.bind(this));\n };\n\n /**\n * Get play back rate\n *\n * @returns {Promise}\n */\n VideoTime.prototype.getPlaybackRate = function() {\n return this.player.getPlaybackRate();\n };\n\n /**\n * Get duration of video\n *\n * @returns {Promise}\n */\n VideoTime.prototype.getDuration = function() {\n return this.player.getDuration();\n };\n\n /**\n * Set current time of player\n *\n * @param {float} secs time\n * @returns {Promise}\n */\n VideoTime.prototype.setCurrentPosition = function(secs) {\n return this.player.setCurrentTime(secs);\n };\n\n /**\n * Get current time of player\n *\n * @returns {Promise}\n */\n VideoTime.prototype.getCurrentPosition = function() {\n return this.player.getCurrentTime();\n };\n\n return VideoTime;\n});\n"],"names":["define","$","Vimeo","Ajax","Config","Log","Templates","Notification","VideoTime","elementId","cmId","hasPro","interval","instance","player","resumeTime","session","played","playing","time","percent","currentTime","playbackRate","plugins","hasClass","addClass","document","on","this","initializeNewInstances","bind","modulecount","length","prototype","getCmId","registerPlugin","plugin","push","initialize","debug","autopause","Number","autoplay","background","byline","color","controls","dnt","height","loop","option_loop","maxheight","maxwidth","muted","portrait","pip","playsinline","responsive","speed","title","transparent","url","vimeo_url","width","URL","window","location","href","q","searchParams","get","starttime","match","setStartTime","then","find","catch","exception","addListeners","i","getPlayer","closest","each","tabs","index","anchor","getAttribute","addCuePoint","exeception","event","data","removeClass","offsetTop","parentNode","scrollTo","top","left","behavior","startWatchInterval","view","resume_playback","resume_time","getDuration","duration","Math","floor","setCurrentPosition","fail","getPlaybackRate","seconds","handleEnd","getSession","setSessionState","id","setPercent","setCurrentTime","getNextActivityButtonData","response","JSON","parse","parseInt","next_activity_auto","is_restricted","hasnextcm","link","nextcm_url","first","click","render","html","watchInterval","setInterval","recordWatchTime","sessionId","state","token","wwwroot","set","fetch","ok","exeption","json","call","methodname","args","Promise","resolve","getInstance","cmid","done","getResumeTime","getCurrentTime","module","attr","replace","haspro","uniqueid","js","runTemplateJS","secs","getCurrentPosition"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"videotime.min.js","sources":["../src/videotime.js"],"sourcesContent":["/*\n * @package mod_videotime\n * @copyright 2021 bdecent gmbh \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\n/**\n * @module mod_videotime/videotime\n */\ndefine([\n 'jquery',\n 'mod_videotime/player',\n 'core/ajax',\n 'core/config',\n 'core/log',\n 'core/templates',\n 'core/notification'\n], function($, Vimeo, Ajax, Config, Log, Templates, Notification) {\n let VideoTime = function(elementId, cmId, hasPro, interval, instance) {\n this.elementId = elementId;\n this.cmId = cmId;\n this.hasPro = hasPro;\n this.interval = interval;\n this.player = null;\n this.resumeTime = null;\n this.session = null;\n this.instance = instance;\n\n this.played = false;\n\n this.playing = false;\n this.time = 0;\n this.percent = 0;\n this.currentTime = 0;\n this.playbackRate = 1;\n\n this.plugins = [];\n\n if (hasPro && $('body').hasClass('path-course-view') && !$('body').hasClass('vtinit')) {\n $('body').addClass('vtinit');\n $(document).on('focus', 'body', this.initializeNewInstances.bind(this));\n }\n this.modulecount = $('body .activity.videotime').length;\n };\n\n /**\n * Get course module ID of this VideoTime instance.\n *\n * @return {int}\n */\n VideoTime.prototype.getCmId = function() {\n return this.cmId;\n };\n\n /**\n * Register a plugin to hook into VideoTime functionality.\n *\n * @param {VideoTimePlugin} plugin\n */\n VideoTime.prototype.registerPlugin = function(plugin) {\n this.plugins.push(plugin);\n };\n\n VideoTime.prototype.initialize = function() {\n let instance = this.instance;\n Log.debug('Initializing Video Time ' + this.elementId);\n\n Log.debug('Initializing Vimeo player with options:');\n Log.debug(instance);\n this.player = new Vimeo(this.elementId, {\n autopause: Number(instance.autopause),\n autoplay: Number(instance.autoplay),\n background: Number(instance.background),\n byline: Number(instance.byline),\n color: instance.color,\n controls: Number(instance.controls),\n dnt: Number(instance.dnt),\n height: instance.height,\n loop: Number(instance.option_loop),\n maxheight: instance.maxheight,\n maxwidth: instance.maxwidth,\n muted: Number(instance.muted),\n portrait: instance.portrait,\n pip: Number(instance.pip),\n playsinline: instance.playsinline,\n responsive: Number(instance.responsive),\n speed: instance.speed,\n title: Number(instance.title),\n transparent: Number(instance.transparent),\n url: instance.vimeo_url,\n width: instance.width\n });\n\n let url = new URL(window.location.href),\n q = url.searchParams.get('q'),\n starttime = (url.searchParams.get('time') || '').match(/^([0-9]+:){0,2}([0-9]+)(\\.[0-9]+)$/);\n if (starttime) {\n this.setStartTime(starttime[0]).then(function() {\n if (q && window.find) {\n window.find(q);\n }\n return true;\n }).catch(Notification.exception);\n } else if (q && window.find) {\n window.find(q);\n }\n\n this.addListeners();\n\n for (let i = 0; i < this.plugins.length; i++) {\n const plugin = this.plugins[i];\n plugin.initialize(this, instance);\n }\n\n return true;\n };\n\n /**\n * Get Vimeo player object.\n *\n * @returns {Vimeo}\n */\n VideoTime.prototype.getPlayer = function() {\n return this.player;\n };\n\n /**\n * Register player events to respond to user interaction and play progress.\n */\n VideoTime.prototype.addListeners = function() {\n if (!this.player) {\n Log.debug('Player was not properly initialized for course module ' + this.cmId);\n return;\n }\n\n // If this is a tab play set time cues and listener.\n $($('#' + this.elementId).closest('.videotimetabs')).each(function(i, tabs) {\n $(tabs).find('[data-action=\"cue\"]').each(function(index, anchor) {\n let starttime = anchor.getAttribute('data-start'),\n time = starttime.match(/((([0-9]+):)?(([0-9]+):))?([0-9]+(\\.[0-9]+))/);\n if (time) {\n this.player.addCuePoint(\n 3600 * Number(time[3] || 0) + 60 * Number(time[5] || 0) + Number(time[6]),\n {\n starttime: starttime\n }\n ).catch(Notification.exeception);\n }\n }.bind(this));\n\n this.player.on('cuepoint', function(event) {\n if (event.data.starttime) {\n $(tabs).find('.videotime-highlight').removeClass('videotime-highlight');\n $(tabs).find('[data-action=\"cue\"][data-start=\"' + event.data.starttime + '\"]')\n .closest('.row')\n .addClass('videotime-highlight');\n $('.videotime-highlight').each(function() {\n if (this.offsetTop) {\n this.parentNode.scrollTo({\n top: this.offsetTop - 50,\n left: 0,\n behavior: 'smooth'\n });\n }\n });\n }\n });\n }.bind(this));\n\n // Fire view event in Moodle on first play only.\n this.player.on('play', () => {\n if (this.hasPro) {\n this.startWatchInterval();\n }\n this.view();\n return true;\n });\n\n // Features beyond this point are for pro only.\n if (!this.hasPro) {\n return;\n }\n\n // If resume is present force seek the player to that point.\n this.player.on(\"loaded\", () => {\n if (!this.instance.resume_playback || !this.instance.resume_time || this.instance.resume_time <= 0) {\n return true;\n }\n\n return this.getDuration().then((duration) => {\n let resumeTime = this.instance.resume_time;\n // Duration is often a little greater than a resume time at the end of the video.\n // A user may have watched 100 seconds when the video ends, but the duration may be\n // 100.56 seconds. BUT, sometimes the duration is rounded depending on when the\n // video loads, so it may be 101 seconds. Hence the +1 and Math.floor usage.\n if (resumeTime + 1 >= Math.floor(duration)) {\n Log.debug(\n \"VIDEO_TIME video finished, resuming at start of video.\"\n );\n resumeTime = 0;\n }\n Log.debug(\"VIDEO_TIME duration is \" + duration);\n Log.debug(\"VIDEO_TIME resuming at \" + resumeTime);\n this.setCurrentPosition(resumeTime);\n return true;\n }).fail(Notification.exception);\n });\n\n // Note: Vimeo player does not support multiple events in a single on() call. Each requires it's own function.\n\n // Catch all events where video plays.\n this.player.on('play', function() {\n this.playing = true;\n Log.debug('VIDEO_TIME play');\n }.bind(this));\n this.player.on('playing', function() {\n this.playing = true;\n Log.debug('VIDEO_TIME playing');\n }.bind(this));\n\n // Catch all events where video stops.\n this.player.on('pause', function() {\n this.playing = false;\n Log.debug('VIDEO_TIME pause');\n }.bind(this));\n this.player.on('stalled', function() {\n this.playing = false;\n Log.debug('VIDEO_TIME stalled');\n }.bind(this));\n this.player.on('suspend', function() {\n this.playing = false;\n Log.debug('VIDEO_TIME suspend');\n }.bind(this));\n this.player.on('abort', function() {\n this.playing = false;\n Log.debug('VIDEO_TIME abort');\n }.bind(this));\n\n this.player.getPlaybackRate().then(function(playbackRate) {\n this.playbackRate = playbackRate;\n }.bind(this)).catch(Notification.exception);\n\n this.player.on('playbackratechange', function(event) {\n this.playbackRate = event.playbackRate;\n }.bind(this));\n\n // Always update internal values for percent and current time watched.\n this.player.on('timeupdate', function(event) {\n this.percent = event.percent;\n this.currentTime = event.seconds;\n }.bind(this));\n\n // Initiate video finish procedure.\n this.player.on('ended', this.handleEnd.bind(this));\n };\n\n /**\n * Start interval that will periodically record user progress via Ajax.\n */\n VideoTime.prototype.handleEnd = function() {\n this.playing = false;\n Log.debug('VIDEO_TIME ended');\n if (this.plugins.length > 2) {\n this.plugins.forEach(plugin => {\n Log.debug(plugin);\n if (typeof plugin.handleEnd == 'function') {\n plugin.handleEnd();\n }\n });\n } else {\n // This moved to pro plugin, but left for compatibility.\n this.getSession().then(function(session) {\n this.setSessionState(session.id, 1).then(() => {\n return this.setPercent(session.id, 1);\n }).then(() => {\n return this.setCurrentTime(session.id, this.currentTime);\n }).then(() => {\n return this.getNextActivityButtonData(session.id).then(response => {\n let data = JSON.parse(response.data);\n\n if (data.instance && parseInt(data.instance.next_activity_auto)) {\n if (!data.is_restricted && data.hasnextcm) {\n let link = $('.aalink[href=\"' + data.nextcm_url + '\"] img').first();\n if ($('.path-course-view').length && link) {\n link.click();\n } else {\n window.location.href = data.nextcm_url;\n }\n }\n }\n\n return Templates.render('videotime/next_activity_button', JSON.parse(response.data))\n .then(function(html) {\n $('#next-activity-button').html(html);\n return true;\n });\n });\n }).catch(Notification.exception);\n }.bind(this)).catch(Notification.exception);\n }\n };\n\n /**\n * Start interval that will periodically record user progress via Ajax.\n */\n VideoTime.prototype.startWatchInterval = function() {\n this.plugins.forEach(plugin => {\n if (typeof plugin.startWatchInterval == 'function') {\n this.watchInterval = true;\n plugin.startWatchInterval();\n }\n });\n if (this.watchInterval) {\n return;\n }\n\n this.watchInterval = setInterval(function() {\n if (this.playing) {\n this.time += this.playbackRate;\n\n this.getSession().then(function(session) {\n if (this.time % this.interval === 0) {\n Log.debug('VIDEO_TIME watch_time: ' + this.time + '. percent: ' + this.percent);\n this.recordWatchTime(session.id, this.time);\n this.setPercent(session.id, this.percent);\n this.setCurrentTime(session.id, this.currentTime);\n }\n return true;\n }.bind(this)).catch(Notification.exception);\n }\n }.bind(this), 1000);\n };\n\n /**\n * Set state on session.\n *\n * @param {int} sessionId\n * @param {int} state\n * @returns {Promise}\n */\n VideoTime.prototype.setSessionState = function(sessionId, state) {\n if (this.instance.token) {\n const url = new URL(Config.wwwroot + '/webservice/rest/server.php'),\n data = url.searchParams;\n data.set('wstoken', this.instance.token);\n data.set('moodlewsrestformat', 'json');\n data.set('wsfunction', 'videotimeplugin_pro_set_session_state');\n data.set('state', state);\n data.set('session_id', sessionId);\n return fetch(url).then((response) => {\n if (!response.ok) {\n Notification.exeption('Web service error');\n }\n return response.json();\n });\n }\n\n return Ajax.call([{\n methodname: 'videotimeplugin_pro_set_session_state',\n args: {\"session_id\": sessionId, state: state},\n fail: Notification.exception\n }])[0];\n };\n\n /**\n * Set current watch time for video. Used for resuming.\n *\n * @param {int} sessionId\n * @param {float} currentTime\n * @returns {Promise}\n */\n VideoTime.prototype.setCurrentTime = function(sessionId, currentTime) {\n if (this.instance.token) {\n const url = new URL(Config.wwwroot + '/webservice/rest/server.php'),\n data = url.searchParams;\n data.set('wstoken', this.instance.token);\n data.set('moodlewsrestformat', 'json');\n data.set('wsfunction', 'videotimeplugin_pro_set_session_current_time');\n data.set('current_time', currentTime);\n data.set('session_id', sessionId);\n return fetch(url).then((response) => {\n if (!response.ok) {\n Notification.exeption('Web service error');\n }\n return response.json();\n });\n }\n return Ajax.call([{\n methodname: 'videotimeplugin_pro_set_session_current_time',\n args: {\"session_id\": sessionId, \"current_time\": currentTime},\n fail: Notification.exception\n }])[0];\n };\n\n /**\n * Set video watch percentage for session.\n *\n * @param {int} sessionId\n * @param {float} percent\n * @returns {Promise}\n */\n VideoTime.prototype.setPercent = function(sessionId, percent) {\n if (this.instance.token) {\n const url = new URL(Config.wwwroot + '/webservice/rest/server.php'),\n data = url.searchParams;\n data.set('wstoken', this.instance.token);\n data.set('moodlewsrestformat', 'json');\n data.set('wsfunction', 'videotimeplugin_pro_set_percent');\n data.set('percent', percent);\n data.set('session_id', sessionId);\n return fetch(url).then((response) => {\n if (!response.ok) {\n Notification.exeption('Web service error');\n }\n return response.json();\n });\n }\n return Ajax.call([{\n methodname: 'videotimeplugin_pro_set_percent',\n args: {\"session_id\": sessionId, percent: percent},\n fail: Notification.exception\n }])[0];\n };\n\n /**\n * Record watch time for session.\n *\n * @param {int} sessionId\n * @param {float} time\n * @returns {Promise}\n */\n VideoTime.prototype.recordWatchTime = function(sessionId, time) {\n if (this.instance.token) {\n const url = new URL(Config.wwwroot + '/webservice/rest/server.php'),\n data = url.searchParams;\n data.set('wstoken', this.instance.token);\n data.set('moodlewsrestformat', 'json');\n data.set('wsfunction', 'videotimeplugin_pro_record_watch_time');\n data.set('session_id', sessionId);\n data.set('time', time);\n return fetch(url).then((response) => {\n if (!response.ok) {\n Notification.exeption('Web service error');\n }\n return response.json();\n });\n }\n return Ajax.call([{\n methodname: 'videotimeplugin_pro_record_watch_time',\n args: {\"session_id\": sessionId, time: time},\n fail: Notification.exception\n }])[0];\n };\n\n /**\n * Get data for next activity button.\n *\n * @param {int} sessionId\n * @returns {Promise}\n */\n VideoTime.prototype.getNextActivityButtonData = function(sessionId) {\n if (this.instance.token) {\n // We do not support button in iframe.\n return Promise.resolve({data: '{}'});\n }\n return Ajax.call([{\n methodname: 'videotimeplugin_pro_get_next_activity_button_data',\n args: {\"session_id\": sessionId}\n }])[0];\n };\n\n /**\n * Get VideoTime instance for this course module.\n *\n * @returns {Promise}\n */\n VideoTime.prototype.getInstance = function() {\n if (this.instance) {\n return Promise.resolve(this.instance);\n }\n\n return Ajax.call([{\n methodname: 'mod_videotime_get_videotime',\n args: {cmid: this.cmId},\n done: (response) => {\n this.instance = response;\n return this.instance;\n },\n fail: Notification.exception\n }])[0];\n };\n\n /**\n * Get time to resume video as seconds.\n *\n * @returns {Promise}\n */\n VideoTime.prototype.getResumeTime = function() {\n if (this.resumeTime) {\n return Promise.resolve(this.resumeTime);\n }\n\n return Ajax.call([{\n methodname: 'videotimeplugin_pro_get_resume_time',\n args: {cmid: this.cmId},\n done: (response) => {\n this.resumeTime = response.seconds;\n return this.resumeTime;\n },\n fail: Notification.exception\n }])[0];\n };\n\n /**\n * Get new or existing video viewing session.\n *\n * @returns {Promise}\n */\n VideoTime.prototype.getSession = function() {\n if (this.instance.token) {\n if (!this.session) {\n const url = new URL(Config.wwwroot + '/webservice/rest/server.php'),\n data = url.searchParams;\n data.set('wstoken', this.instance.token);\n data.set('moodlewsrestformat', 'json');\n data.set('wsfunction', 'videotimeplugin_pro_get_new_session');\n data.set('cmid', this.cmId);\n this.session = fetch(url).then((response) => {\n if (!response.ok) {\n Notification.exeption('Web service error');\n }\n return response.json();\n });\n }\n\n return this.session;\n }\n if (!this.session) {\n this.session = Ajax.call([{\n methodname: 'videotimeplugin_pro_get_new_session',\n args: {cmid: this.cmId},\n fail: Notification.exception\n }])[0];\n }\n return this.session;\n };\n\n /**\n * Parse start time and set player\n *\n * @param {string} starttime\n * @returns {Promise}\n */\n VideoTime.prototype.setStartTime = function(starttime) {\n let time = starttime.match(/((([0-9]+):)?(([0-9]+):))?([0-9]+(\\.[0-9]+))/);\n if (time) {\n this.resumeTime = 3600 * Number(time[3] || 0) + 60 * Number(time[5] || 0) + Number(time[6]);\n this.currentTime(this.resumeTime);\n }\n return this.player.getCurrentTime();\n };\n\n /**\n * Log the user view of video.\n *\n * @returns {Promise}\n */\n VideoTime.prototype.view = function() {\n if (this.instance.token) {\n const url = new URL(Config.wwwroot + '/webservice/rest/server.php'),\n data = url.searchParams;\n data.set('wstoken', this.instance.token);\n data.set('moodlewsrestformat', 'json');\n data.set('wsfunction', 'mod_videotime_view_videotime');\n data.set('cmid', this.cmId);\n return fetch(url).then((response) => {\n if (!response.ok) {\n Notification.exeption('Web service error');\n }\n return response.json();\n });\n\n }\n return Ajax.call([{\n methodname: 'mod_videotime_view_videotime',\n args: {cmid: this.cmId},\n fail: Notification.exception\n }])[0];\n };\n\n /**\n * Initialize new labels and preview when editing\n */\n VideoTime.prototype.initializeNewInstances = function() {\n if (this.modulecount == $('body .activity.videotime').length) {\n return;\n }\n this.modulecount = $('body .activity.videotime').length;\n $('body .activity.videotime').each(function(index, module) {\n if (\n !$(module).find('.instancename').length\n && $(module).find('.vimeo-embed').length\n && !$(module).find('.vimeo-embed iframe').length\n ) {\n let instance = {\n cmid: Number($(module).attr('id').replace('module-', '')),\n haspro: true,\n interval: this.interval,\n uniqueid: $(module).find('.vimeo-embed').first().attr('id').replace('vimeo-embed-', '')\n };\n Templates.render('mod_videotime/videotime_instance', {\n instance: instance\n }).then(function(html, js) {\n Templates.runTemplateJS(js);\n return true;\n }).fail(Notification.exception);\n }\n }.bind(this));\n };\n\n /**\n * Get play back rate\n *\n * @returns {Promise}\n */\n VideoTime.prototype.getPlaybackRate = function() {\n return this.player.getPlaybackRate();\n };\n\n /**\n * Get duration of video\n *\n * @returns {Promise}\n */\n VideoTime.prototype.getDuration = function() {\n return this.player.getDuration();\n };\n\n /**\n * Set current time of player\n *\n * @param {float} secs time\n * @returns {Promise}\n */\n VideoTime.prototype.setCurrentPosition = function(secs) {\n return this.player.setCurrentTime(secs);\n };\n\n /**\n * Get current time of player\n *\n * @returns {Promise}\n */\n VideoTime.prototype.getCurrentPosition = function() {\n return this.player.getCurrentTime();\n };\n\n return VideoTime;\n});\n"],"names":["define","$","Vimeo","Ajax","Config","Log","Templates","Notification","VideoTime","elementId","cmId","hasPro","interval","instance","player","resumeTime","session","played","playing","time","percent","currentTime","playbackRate","plugins","hasClass","addClass","document","on","this","initializeNewInstances","bind","modulecount","length","prototype","getCmId","registerPlugin","plugin","push","initialize","debug","autopause","Number","autoplay","background","byline","color","controls","dnt","height","loop","option_loop","maxheight","maxwidth","muted","portrait","pip","playsinline","responsive","speed","title","transparent","url","vimeo_url","width","URL","window","location","href","q","searchParams","get","starttime","match","setStartTime","then","find","catch","exception","addListeners","i","getPlayer","closest","each","tabs","index","anchor","getAttribute","addCuePoint","exeception","event","data","removeClass","offsetTop","parentNode","scrollTo","top","left","behavior","startWatchInterval","view","resume_playback","resume_time","getDuration","duration","Math","floor","setCurrentPosition","fail","getPlaybackRate","seconds","handleEnd","forEach","getSession","setSessionState","id","setPercent","setCurrentTime","getNextActivityButtonData","response","JSON","parse","parseInt","next_activity_auto","is_restricted","hasnextcm","link","nextcm_url","first","click","render","html","watchInterval","setInterval","recordWatchTime","sessionId","state","token","wwwroot","set","fetch","ok","exeption","json","call","methodname","args","Promise","resolve","getInstance","cmid","done","getResumeTime","getCurrentTime","module","attr","replace","haspro","uniqueid","js","runTemplateJS","secs","getCurrentPosition"],"mappings":"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"} \ No newline at end of file diff --git a/amd/src/videotime.js b/amd/src/videotime.js index 69087754..9186ab8a 100644 --- a/amd/src/videotime.js +++ b/amd/src/videotime.js @@ -183,7 +183,7 @@ define([ // If resume is present force seek the player to that point. this.player.on("loaded", () => { - if (!this.instance.resume_playback || this.instance.resume_time <= 0) { + if (!this.instance.resume_playback || !this.instance.resume_time || this.instance.resume_time <= 0) { return true; } @@ -248,7 +248,15 @@ define([ this.player.on('timeupdate', function(event) { this.percent = event.percent; this.currentTime = event.seconds; - Log.debug('VIDEO_TIME timeupdate. Percent: ' + this.percent + '. Current time: ' + this.currentTime); + if (events.seconds === events.duration) { + this.plugins.forEach(plugin => { + if (typeof plugin.setCurrentTime == 'function') { + plugin.getSessions().then(session => { + plugin.setCurrentTime(session.id, event.seconds); + } + } + }); + } }.bind(this)); // Initiate video finish procedure. @@ -259,9 +267,16 @@ define([ * Start interval that will periodically record user progress via Ajax. */ VideoTime.prototype.handleEnd = function() { - this.playing = false; - Log.debug('VIDEO_TIME ended'); - + this.playing = false; + Log.debug('VIDEO_TIME ended'); + if (this.plugins.length > 2) { + this.plugins.forEach(plugin => { + if (typeof plugin.handleEnd == 'function') { + plugin.handleEnd(); + } + }); + } else { + // This moved to pro plugin, but left for compatibility. this.getSession().then(function(session) { this.setSessionState(session.id, 1).then(() => { return this.setPercent(session.id, 1); @@ -290,12 +305,19 @@ define([ }); }).catch(Notification.exception); }.bind(this)).catch(Notification.exception); + } }; /** * Start interval that will periodically record user progress via Ajax. */ VideoTime.prototype.startWatchInterval = function() { + this.plugins.forEach(plugin => { + if (typeof plugin.startWatchInterval == 'function') { + this.watchInterval = true; + plugin.startWatchInterval(); + } + }); if (this.watchInterval) { return; } diff --git a/plugin/videojs/amd/build/videotime.min.js b/plugin/videojs/amd/build/videotime.min.js index 54845a2d..78eec972 100644 --- a/plugin/videojs/amd/build/videotime.min.js +++ b/plugin/videojs/amd/build/videotime.min.js @@ -6,6 +6,6 @@ define("videotimeplugin_videojs/videotime",["exports","jquery","mod_videotime/vi * @module videotimeplugin_videojs/videotime * @copyright 2022 bdecent gmbh * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_jquery=_interopRequireDefault(_jquery),_videotime=_interopRequireDefault(_videotime),_log=_interopRequireDefault(_log),_notification=_interopRequireDefault(_notification),_videoLazy=_interopRequireDefault(_videoLazy);class VideoTime extends _videotime.default{initialize(){_log.default.debug("Initializing Video Time "+this.elementId);let instance=this.instance,options={autoplay:Number(instance.autoplay),controls:Number(instance.controls),sources:[{type:instance.type,src:instance.vimeo_url}],loop:Number(instance.option_loop),fluid:Number(instance.responsive),playbackRates:instance.speed?[.5,.75,1,1.25,1.5,2]:[1],muted:Number(instance.muted)};"video/youtube"===instance.type&&(options.techOrder=["youtube"]),!Number(instance.responsive)&&Number(instance.height)&&Number(instance.width)&&(options.height=Number(instance.height),options.width=Number(instance.width)),_log.default.debug("Initializing VideoJS player with options:"),_log.default.debug(options),this.player=new _videoLazy.default(this.elementId,options),this.player.on("loadedmetadata",(()=>{if(!instance.resume_playback||instance.resume_time<=0||this.resumed)return!0;let duration=this.getPlayer().duration(),resumeTime=instance.resume_time;return resumeTime+1>=Math.floor(duration)&&(_log.default.debug("VIDEO_TIME video finished, resuming at start of video."),resumeTime=0),_log.default.debug("VIDEO_TIME duration is "+duration),_log.default.debug("VIDEO_TIME resuming at "+resumeTime),resumeTime&&setTimeout((()=>{this.setCurrentPosition(resumeTime)}),10),!0}));let url=new URL(window.location.href),q=url.searchParams.get("q"),starttime=(url.searchParams.get("time")||"").match(/^([0-9]+:){0,2}([0-9]+)(\.[0-9]+)$/);starttime?this.setStartTime(starttime[0]).then((function(){return q&&window.find&&window.find(q),!0})).catch(_notification.default.exception):q&&window.find&&window.find(q),this.addListeners();for(let i=0;i(this.played||(this.hasPro?this.getSession().then((()=>(this.view(),this.startWatchInterval(),!0))).catch(_notification.default.exception):this.view()),!0))),this.hasPro&&(this.player.on("play",function(){this.playing=!0,_log.default.debug("VIDEO_TIME play")}.bind(this)),this.player.on("playing",function(){this.playing=!0,_log.default.debug("VIDEO_TIME playing")}.bind(this)),this.player.on("pause",function(){this.playing=!1,_log.default.debug("VIDEO_TIME pause")}.bind(this)),this.player.on("stalled",function(){this.playing=!1,_log.default.debug("VIDEO_TIME stalled")}.bind(this)),this.player.on("suspend",function(){this.playing=!1,_log.default.debug("VIDEO_TIME suspend")}.bind(this)),this.player.on("abort",function(){this.playing=!1,_log.default.debug("VIDEO_TIME abort")}.bind(this)),this.player.on("playbackrateschange",function(){this.playbackRate=this.player.playbackRate()}.bind(this)),this.player.on("timeupdate",function(){this.currentTime=this.player.currentTime(),this.percent=this.currentTime/this.player.duration(),_log.default.debug("VIDEO_TIME timeupdate. Percent: "+this.percent+". Current time: "+this.currentTime)}.bind(this)),this.player.on("ended",this.handleEnd.bind(this)),this.player.options().responsive)){new ResizeObserver((()=>{this.player.height(this.player.videoHeight()/this.player.videoWidth()*this.player.currentWidth())})).observe(document.querySelector("#"+this.elementId))}}else _log.default.debug("Player was not properly initialized for course module "+this.cmId)}setStartTime(starttime){let time=starttime.match(/((([0-9]+):)?(([0-9]+):))?([0-9]+(\.[0-9]+))/);return time?(this.resumeTime=3600*Number(time[3]||0)+60*Number(time[5]||0)+Number(time[6]),this.player.currentTime(this.resumeTime)):(_log.default.debug("Set start time:"+starttime),this.player.currentTime())}getDuration(){return new Promise((resolve=>(resolve(this.player.duration()),!0)))}getPlaybackRate(){return new Promise((resolve=>(resolve(this.player.playbackRate()),!0)))}setCurrentPosition(secs){return new Promise((resolve=>(resolve(this.player.currentTime(secs)),!0)))}getCurrentPosition(){return new Promise((resolve=>(resolve(this.player.currentTime()),!0)))}}return _exports.default=VideoTime,_exports.default})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_jquery=_interopRequireDefault(_jquery),_videotime=_interopRequireDefault(_videotime),_log=_interopRequireDefault(_log),_notification=_interopRequireDefault(_notification),_videoLazy=_interopRequireDefault(_videoLazy);class VideoTime extends _videotime.default{initialize(){_log.default.debug("Initializing Video Time "+this.elementId);let instance=this.instance,options={autoplay:Number(instance.autoplay),controls:Number(instance.controls),sources:[{type:instance.type,src:instance.vimeo_url}],loop:Number(instance.option_loop),fluid:Number(instance.responsive),playbackRates:instance.speed?[.5,.75,1,1.25,1.5,2]:[1],muted:Number(instance.muted)};"video/youtube"===instance.type&&(options.techOrder=["youtube"]),!Number(instance.responsive)&&Number(instance.height)&&Number(instance.width)&&(options.height=Number(instance.height),options.width=Number(instance.width)),_log.default.debug("Initializing VideoJS player with options:"),_log.default.debug(options),this.player=new _videoLazy.default(this.elementId,options),this.player.on("loadedmetadata",(()=>{if(!instance.resume_playback||instance.resume_time<=0||this.resumed)return!0;let duration=this.getPlayer().duration(),resumeTime=instance.resume_time;return resumeTime+1>=Math.floor(duration)&&(_log.default.debug("VIDEO_TIME video finished, resuming at start of video."),resumeTime=0),_log.default.debug("VIDEO_TIME duration is "+duration),_log.default.debug("VIDEO_TIME resuming at "+resumeTime),resumeTime&&setTimeout((()=>{this.setCurrentPosition(resumeTime)}),10),!0}));let url=new URL(window.location.href),q=url.searchParams.get("q"),starttime=(url.searchParams.get("time")||"").match(/^([0-9]+:){0,2}([0-9]+)(\.[0-9]+)$/);starttime?this.setStartTime(starttime[0]).then((function(){return q&&window.find&&window.find(q),!0})).catch(_notification.default.exception):q&&window.find&&window.find(q),this.addListeners();for(let i=0;i(this.played||(this.hasPro&&this.startWatchInterval(),this.view()),!0))),this.hasPro&&(this.player.on("play",function(){this.playing=!0,_log.default.debug("VIDEO_TIME play")}.bind(this)),this.player.on("playing",function(){this.playing=!0,_log.default.debug("VIDEO_TIME playing")}.bind(this)),this.player.on("pause",function(){this.playing=!1,_log.default.debug("VIDEO_TIME pause")}.bind(this)),this.player.on("stalled",function(){this.playing=!1,_log.default.debug("VIDEO_TIME stalled")}.bind(this)),this.player.on("suspend",function(){this.playing=!1,_log.default.debug("VIDEO_TIME suspend")}.bind(this)),this.player.on("abort",function(){this.playing=!1,_log.default.debug("VIDEO_TIME abort")}.bind(this)),this.player.on("playbackrateschange",function(){this.playbackRate=this.player.playbackRate()}.bind(this)),this.player.on("timeupdate",function(){this.currentTime=this.player.currentTime(),this.percent=this.currentTime/this.player.duration(),_log.default.debug("VIDEO_TIME timeupdate. Percent: "+this.percent+". Current time: "+this.currentTime)}.bind(this)),this.player.on("ended",this.handleEnd.bind(this)),this.player.options().responsive)){new ResizeObserver((()=>{this.player.height(this.player.videoHeight()/this.player.videoWidth()*this.player.currentWidth())})).observe(document.querySelector("#"+this.elementId))}}else _log.default.debug("Player was not properly initialized for course module "+this.cmId)}setStartTime(starttime){let time=starttime.match(/((([0-9]+):)?(([0-9]+):))?([0-9]+(\.[0-9]+))/);return time?(this.resumeTime=3600*Number(time[3]||0)+60*Number(time[5]||0)+Number(time[6]),this.player.currentTime(this.resumeTime)):(_log.default.debug("Set start time:"+starttime),this.player.currentTime())}getDuration(){return new Promise((resolve=>(resolve(this.player.duration()),!0)))}getPlaybackRate(){return new Promise((resolve=>(resolve(this.player.playbackRate()),!0)))}setCurrentPosition(secs){return new Promise((resolve=>(resolve(this.player.currentTime(secs)),!0)))}getCurrentPosition(){return new Promise((resolve=>(resolve(this.player.currentTime()),!0)))}}return _exports.default=VideoTime,_exports.default})); //# sourceMappingURL=videotime.min.js.map \ No newline at end of file diff --git a/plugin/videojs/amd/build/videotime.min.js.map b/plugin/videojs/amd/build/videotime.min.js.map index 21884528..adc58873 100644 --- a/plugin/videojs/amd/build/videotime.min.js.map +++ b/plugin/videojs/amd/build/videotime.min.js.map @@ -1 +1 @@ -{"version":3,"file":"videotime.min.js","sources":["../src/videotime.js"],"sourcesContent":["/*\n * Video time player specific js\n *\n * @package videotimeplugin_videojs\n * @module videotimeplugin_videojs/videotime\n * @copyright 2022 bdecent gmbh \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport $ from \"jquery\";\nimport VideoTimeBase from \"mod_videotime/videotime\";\nimport Log from \"core/log\";\nimport Notification from \"core/notification\";\nimport Player from \"media_videojs/video-lazy\";\nimport \"media_videojs/Youtube-lazy\";\n\nexport default class VideoTime extends VideoTimeBase {\n initialize() {\n Log.debug(\"Initializing Video Time \" + this.elementId);\n\n let instance = this.instance,\n options = {\n autoplay: Number(instance.autoplay),\n controls: Number(instance.controls),\n sources: [{type: instance.type, src: instance.vimeo_url}],\n loop: Number(instance.option_loop),\n fluid: Number(instance.responsive),\n playbackRates: instance.speed\n ? [0.5, 0.75, 1, 1.25, 1.5, 2]\n : [1],\n muted: Number(instance.muted)\n };\n if (instance.type === \"video/youtube\") {\n options.techOrder = [\"youtube\"];\n }\n if (!Number(instance.responsive) && Number(instance.height) && Number(instance.width)) {\n options.height = Number(instance.height);\n options.width = Number(instance.width);\n }\n Log.debug(\"Initializing VideoJS player with options:\");\n Log.debug(options);\n this.player = new Player(this.elementId, options);\n\n this.player.on(\"loadedmetadata\", () => {\n if (!instance.resume_playback || instance.resume_time <= 0 || this.resumed) {\n return true;\n }\n\n let duration = this.getPlayer().duration(),\n resumeTime = instance.resume_time;\n // Duration is often a little greater than a resume time at the end of the video.\n // A user may have watched 100 seconds when the video ends, but the duration may be\n // 100.56 seconds. BUT, sometimes the duration is rounded depending on when the\n // video loads, so it may be 101 seconds. Hence the +1 and Math.floor usage.\n if (resumeTime + 1 >= Math.floor(duration)) {\n Log.debug(\n \"VIDEO_TIME video finished, resuming at start of video.\"\n );\n resumeTime = 0;\n }\n Log.debug(\"VIDEO_TIME duration is \" + duration);\n Log.debug(\"VIDEO_TIME resuming at \" + resumeTime);\n if (resumeTime) {\n setTimeout(() => {\n this.setCurrentPosition(resumeTime);\n }, 10);\n }\n return true;\n });\n\n let url = new URL(window.location.href),\n q = url.searchParams.get(\"q\"),\n starttime = (url.searchParams.get(\"time\") || \"\").match(\n /^([0-9]+:){0,2}([0-9]+)(\\.[0-9]+)$/\n );\n if (starttime) {\n this.setStartTime(starttime[0])\n .then(function() {\n if (q && window.find) {\n window.find(q);\n }\n return true;\n })\n .catch(Notification.exception);\n } else if (q && window.find) {\n window.find(q);\n }\n\n this.addListeners();\n\n for (let i = 0; i < this.plugins.length; i++) {\n const plugin = this.plugins[i];\n plugin.initialize(this, instance);\n }\n\n return true;\n }\n\n /**\n * Register player events to respond to user interaction and play progress.\n */\n addListeners() {\n // If this is a tab play set time cues and listener.\n $($(\"#\" + this.elementId).closest(\".videotimetabs\")).each(\n function(i, tabs) {\n $(tabs)\n .find('[data-action=\"cue\"]')\n .each(\n function(index, anchor) {\n let starttime = anchor.getAttribute(\"data-start\"),\n time = starttime.match(\n /((([0-9]+):)?(([0-9]+):))?([0-9]+(\\.[0-9]+))/\n );\n if (time) {\n this.player\n .addCuePoint(\n 3600 * Number(time[3] || 0) +\n 60 * Number(time[5] || 0) +\n Number(time[6]),\n {\n starttime: starttime\n }\n )\n .catch(Notification.exeception);\n }\n }.bind(this)\n );\n\n this.player.on(\"cuepoint\", function(event) {\n if (event.data.starttime) {\n $(tabs)\n .find(\".videotime-highlight\")\n .removeClass(\"videotime-highlight\");\n $(tabs)\n .find(\n '[data-action=\"cue\"][data-start=\"' +\n event.data.starttime +\n '\"]'\n )\n .closest(\".row\")\n .addClass(\"videotime-highlight\");\n $(\".videotime-highlight\").each(function() {\n if (this.offsetTop) {\n this.parentNode.scrollTo({\n top: this.offsetTop - 50,\n left: 0,\n behavior: \"smooth\"\n });\n }\n });\n }\n });\n }.bind(this)\n );\n\n if (!this.player) {\n Log.debug(\n \"Player was not properly initialized for course module \" +\n this.cmId\n );\n return;\n }\n\n // Fire view event in Moodle on first play only.\n this.player.on(\"play\", () => {\n if (!this.played) {\n if (this.hasPro) {\n // Getting a new session on first play.\n this.getSession()\n .then(() => {\n this.view();\n this.startWatchInterval();\n return true;\n })\n .catch(Notification.exception);\n } else {\n // Free version can still mark completion on video time view.\n this.view();\n }\n }\n return true;\n });\n\n // Features beyond this point are for pro only.\n if (!this.hasPro) {\n return;\n }\n\n // Note: Vimeo player does not support multiple events in a single on() call. Each requires it's own function.\n\n // Catch all events where video plays.\n this.player.on(\n \"play\",\n function() {\n this.playing = true;\n Log.debug(\"VIDEO_TIME play\");\n }.bind(this)\n );\n this.player.on(\n \"playing\",\n function() {\n this.playing = true;\n Log.debug(\"VIDEO_TIME playing\");\n }.bind(this)\n );\n\n // Catch all events where video stops.\n this.player.on(\n \"pause\",\n function() {\n this.playing = false;\n Log.debug(\"VIDEO_TIME pause\");\n }.bind(this)\n );\n this.player.on(\n \"stalled\",\n function() {\n this.playing = false;\n Log.debug(\"VIDEO_TIME stalled\");\n }.bind(this)\n );\n this.player.on(\n \"suspend\",\n function() {\n this.playing = false;\n Log.debug(\"VIDEO_TIME suspend\");\n }.bind(this)\n );\n this.player.on(\n \"abort\",\n function() {\n this.playing = false;\n Log.debug(\"VIDEO_TIME abort\");\n }.bind(this)\n );\n\n this.player.on(\n \"playbackrateschange\",\n function() {\n this.playbackRate = this.player.playbackRate();\n }.bind(this)\n );\n\n // Always update internal values for percent and current time watched.\n this.player.on(\n \"timeupdate\",\n function() {\n this.currentTime = this.player.currentTime();\n this.percent = this.currentTime / this.player.duration();\n Log.debug(\n \"VIDEO_TIME timeupdate. Percent: \" +\n this.percent +\n \". Current time: \" +\n this.currentTime\n );\n }.bind(this)\n );\n\n // Initiate video finish procedure.\n this.player.on(\"ended\", this.handleEnd.bind(this));\n\n // Readjust height when responsive player is resized.\n if (this.player.options().responsive) {\n let observer = new ResizeObserver(() => {\n this.player.height(\n (this.player.videoHeight() / this.player.videoWidth()) *\n this.player.currentWidth()\n );\n });\n observer.observe(document.querySelector(\"#\" + this.elementId));\n }\n }\n\n /**\n * Parse start time and set player\n *\n * @param {string} starttime\n * @returns {Promise}\n */\n setStartTime(starttime) {\n let time = starttime.match(\n /((([0-9]+):)?(([0-9]+):))?([0-9]+(\\.[0-9]+))/\n );\n if (time) {\n this.resumeTime =\n 3600 * Number(time[3] || 0) +\n 60 * Number(time[5] || 0) +\n Number(time[6]);\n return this.player.currentTime(this.resumeTime);\n }\n Log.debug(\"Set start time:\" + starttime);\n return this.player.currentTime();\n }\n\n /**\n * Get play back rate\n *\n * @returns {Promise}\n */\n getDuration() {\n return new Promise(resolve => {\n resolve(this.player.duration());\n return true;\n });\n }\n\n /**\n * Get duration of video\n *\n * @returns {Promise}\n */\n getPlaybackRate() {\n return new Promise(resolve => {\n resolve(this.player.playbackRate());\n return true;\n });\n }\n\n /**\n * Set current time of player\n *\n * @param {float} secs time\n * @returns {Promise}\n */\n setCurrentPosition(secs) {\n return new Promise(resolve => {\n resolve(this.player.currentTime(secs));\n return true;\n });\n }\n\n /**\n * Get current time of player\n *\n * @returns {Promise}\n */\n getCurrentPosition() {\n return new Promise(resolve => {\n resolve(this.player.currentTime());\n return true;\n });\n }\n}\n"],"names":["VideoTime","VideoTimeBase","initialize","debug","this","elementId","instance","options","autoplay","Number","controls","sources","type","src","vimeo_url","loop","option_loop","fluid","responsive","playbackRates","speed","muted","techOrder","height","width","player","Player","on","resume_playback","resume_time","resumed","duration","getPlayer","resumeTime","Math","floor","setTimeout","setCurrentPosition","url","URL","window","location","href","q","searchParams","get","starttime","match","setStartTime","then","find","catch","Notification","exception","addListeners","i","plugins","length","closest","each","tabs","index","anchor","getAttribute","time","addCuePoint","exeception","bind","event","data","removeClass","addClass","offsetTop","parentNode","scrollTo","top","left","behavior","played","hasPro","getSession","view","startWatchInterval","playing","playbackRate","currentTime","percent","handleEnd","ResizeObserver","videoHeight","videoWidth","currentWidth","observe","document","querySelector","cmId","getDuration","Promise","resolve","getPlaybackRate","secs","getCurrentPosition"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"videotime.min.js","sources":["../src/videotime.js"],"sourcesContent":["/*\n * Video time player specific js\n *\n * @package videotimeplugin_videojs\n * @module videotimeplugin_videojs/videotime\n * @copyright 2022 bdecent gmbh \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport $ from \"jquery\";\nimport VideoTimeBase from \"mod_videotime/videotime\";\nimport Log from \"core/log\";\nimport Notification from \"core/notification\";\nimport Player from \"media_videojs/video-lazy\";\nimport \"media_videojs/Youtube-lazy\";\n\nexport default class VideoTime extends VideoTimeBase {\n initialize() {\n Log.debug(\"Initializing Video Time \" + this.elementId);\n\n let instance = this.instance,\n options = {\n autoplay: Number(instance.autoplay),\n controls: Number(instance.controls),\n sources: [{type: instance.type, src: instance.vimeo_url}],\n loop: Number(instance.option_loop),\n fluid: Number(instance.responsive),\n playbackRates: instance.speed\n ? [0.5, 0.75, 1, 1.25, 1.5, 2]\n : [1],\n muted: Number(instance.muted)\n };\n if (instance.type === \"video/youtube\") {\n options.techOrder = [\"youtube\"];\n }\n if (!Number(instance.responsive) && Number(instance.height) && Number(instance.width)) {\n options.height = Number(instance.height);\n options.width = Number(instance.width);\n }\n Log.debug(\"Initializing VideoJS player with options:\");\n Log.debug(options);\n this.player = new Player(this.elementId, options);\n\n this.player.on(\"loadedmetadata\", () => {\n if (!instance.resume_playback || instance.resume_time <= 0 || this.resumed) {\n return true;\n }\n\n let duration = this.getPlayer().duration(),\n resumeTime = instance.resume_time;\n // Duration is often a little greater than a resume time at the end of the video.\n // A user may have watched 100 seconds when the video ends, but the duration may be\n // 100.56 seconds. BUT, sometimes the duration is rounded depending on when the\n // video loads, so it may be 101 seconds. Hence the +1 and Math.floor usage.\n if (resumeTime + 1 >= Math.floor(duration)) {\n Log.debug(\n \"VIDEO_TIME video finished, resuming at start of video.\"\n );\n resumeTime = 0;\n }\n Log.debug(\"VIDEO_TIME duration is \" + duration);\n Log.debug(\"VIDEO_TIME resuming at \" + resumeTime);\n if (resumeTime) {\n setTimeout(() => {\n this.setCurrentPosition(resumeTime);\n }, 10);\n }\n return true;\n });\n\n let url = new URL(window.location.href),\n q = url.searchParams.get(\"q\"),\n starttime = (url.searchParams.get(\"time\") || \"\").match(\n /^([0-9]+:){0,2}([0-9]+)(\\.[0-9]+)$/\n );\n if (starttime) {\n this.setStartTime(starttime[0])\n .then(function() {\n if (q && window.find) {\n window.find(q);\n }\n return true;\n })\n .catch(Notification.exception);\n } else if (q && window.find) {\n window.find(q);\n }\n\n this.addListeners();\n\n for (let i = 0; i < this.plugins.length; i++) {\n const plugin = this.plugins[i];\n plugin.initialize(this, instance);\n }\n\n return true;\n }\n\n /**\n * Register player events to respond to user interaction and play progress.\n */\n addListeners() {\n // If this is a tab play set time cues and listener.\n $($(\"#\" + this.elementId).closest(\".videotimetabs\")).each(\n function(i, tabs) {\n $(tabs)\n .find('[data-action=\"cue\"]')\n .each(\n function(index, anchor) {\n let starttime = anchor.getAttribute(\"data-start\"),\n time = starttime.match(\n /((([0-9]+):)?(([0-9]+):))?([0-9]+(\\.[0-9]+))/\n );\n if (time) {\n this.player\n .addCuePoint(\n 3600 * Number(time[3] || 0) +\n 60 * Number(time[5] || 0) +\n Number(time[6]),\n {\n starttime: starttime\n }\n )\n .catch(Notification.exeception);\n }\n }.bind(this)\n );\n\n this.player.on(\"cuepoint\", function(event) {\n if (event.data.starttime) {\n $(tabs)\n .find(\".videotime-highlight\")\n .removeClass(\"videotime-highlight\");\n $(tabs)\n .find(\n '[data-action=\"cue\"][data-start=\"' +\n event.data.starttime +\n '\"]'\n )\n .closest(\".row\")\n .addClass(\"videotime-highlight\");\n $(\".videotime-highlight\").each(function() {\n if (this.offsetTop) {\n this.parentNode.scrollTo({\n top: this.offsetTop - 50,\n left: 0,\n behavior: \"smooth\"\n });\n }\n });\n }\n });\n }.bind(this)\n );\n\n if (!this.player) {\n Log.debug(\n \"Player was not properly initialized for course module \" +\n this.cmId\n );\n return;\n }\n\n // Fire view event in Moodle on first play only.\n this.player.on(\"play\", () => {\n if (!this.played) {\n if (this.hasPro) {\n this.startWatchInterval();\n }\n // Free version can still mark completion on video time view.\n this.view();\n }\n return true;\n });\n\n // Features beyond this point are for pro only.\n if (!this.hasPro) {\n return;\n }\n\n // Note: Vimeo player does not support multiple events in a single on() call. Each requires it's own function.\n\n // Catch all events where video plays.\n this.player.on(\n \"play\",\n function() {\n this.playing = true;\n Log.debug(\"VIDEO_TIME play\");\n }.bind(this)\n );\n this.player.on(\n \"playing\",\n function() {\n this.playing = true;\n Log.debug(\"VIDEO_TIME playing\");\n }.bind(this)\n );\n\n // Catch all events where video stops.\n this.player.on(\n \"pause\",\n function() {\n this.playing = false;\n Log.debug(\"VIDEO_TIME pause\");\n }.bind(this)\n );\n this.player.on(\n \"stalled\",\n function() {\n this.playing = false;\n Log.debug(\"VIDEO_TIME stalled\");\n }.bind(this)\n );\n this.player.on(\n \"suspend\",\n function() {\n this.playing = false;\n Log.debug(\"VIDEO_TIME suspend\");\n }.bind(this)\n );\n this.player.on(\n \"abort\",\n function() {\n this.playing = false;\n Log.debug(\"VIDEO_TIME abort\");\n }.bind(this)\n );\n\n this.player.on(\n \"playbackrateschange\",\n function() {\n this.playbackRate = this.player.playbackRate();\n }.bind(this)\n );\n\n // Always update internal values for percent and current time watched.\n this.player.on(\n \"timeupdate\",\n function() {\n this.currentTime = this.player.currentTime();\n this.percent = this.currentTime / this.player.duration();\n Log.debug(\n \"VIDEO_TIME timeupdate. Percent: \" +\n this.percent +\n \". Current time: \" +\n this.currentTime\n );\n }.bind(this)\n );\n\n // Initiate video finish procedure.\n this.player.on(\"ended\", this.handleEnd.bind(this));\n\n // Readjust height when responsive player is resized.\n if (this.player.options().responsive) {\n let observer = new ResizeObserver(() => {\n this.player.height(\n (this.player.videoHeight() / this.player.videoWidth()) *\n this.player.currentWidth()\n );\n });\n observer.observe(document.querySelector(\"#\" + this.elementId));\n }\n }\n\n /**\n * Parse start time and set player\n *\n * @param {string} starttime\n * @returns {Promise}\n */\n setStartTime(starttime) {\n let time = starttime.match(\n /((([0-9]+):)?(([0-9]+):))?([0-9]+(\\.[0-9]+))/\n );\n if (time) {\n this.resumeTime =\n 3600 * Number(time[3] || 0) +\n 60 * Number(time[5] || 0) +\n Number(time[6]);\n return this.player.currentTime(this.resumeTime);\n }\n Log.debug(\"Set start time:\" + starttime);\n return this.player.currentTime();\n }\n\n /**\n * Get play back rate\n *\n * @returns {Promise}\n */\n getDuration() {\n return new Promise(resolve => {\n resolve(this.player.duration());\n return true;\n });\n }\n\n /**\n * Get duration of video\n *\n * @returns {Promise}\n */\n getPlaybackRate() {\n return new Promise(resolve => {\n resolve(this.player.playbackRate());\n return true;\n });\n }\n\n /**\n * Set current time of player\n *\n * @param {float} secs time\n * @returns {Promise}\n */\n setCurrentPosition(secs) {\n return new Promise(resolve => {\n resolve(this.player.currentTime(secs));\n return true;\n });\n }\n\n /**\n * Get current time of player\n *\n * @returns {Promise}\n */\n getCurrentPosition() {\n return new Promise(resolve => {\n resolve(this.player.currentTime());\n return true;\n });\n }\n}\n"],"names":["VideoTime","VideoTimeBase","initialize","debug","this","elementId","instance","options","autoplay","Number","controls","sources","type","src","vimeo_url","loop","option_loop","fluid","responsive","playbackRates","speed","muted","techOrder","height","width","player","Player","on","resume_playback","resume_time","resumed","duration","getPlayer","resumeTime","Math","floor","setTimeout","setCurrentPosition","url","URL","window","location","href","q","searchParams","get","starttime","match","setStartTime","then","find","catch","Notification","exception","addListeners","i","plugins","length","closest","each","tabs","index","anchor","getAttribute","time","addCuePoint","exeception","bind","event","data","removeClass","addClass","offsetTop","parentNode","scrollTo","top","left","behavior","played","hasPro","startWatchInterval","view","playing","playbackRate","currentTime","percent","handleEnd","ResizeObserver","videoHeight","videoWidth","currentWidth","observe","document","querySelector","cmId","getDuration","Promise","resolve","getPlaybackRate","secs","getCurrentPosition"],"mappings":";;;;;;;;qTAgBqBA,kBAAkBC,mBACnCC,0BACQC,MAAM,2BAA6BC,KAAKC,eAExCC,SAAWF,KAAKE,SAChBC,QAAU,CACNC,SAAUC,OAAOH,SAASE,UAC1BE,SAAUD,OAAOH,SAASI,UAC1BC,QAAS,CAAC,CAACC,KAAMN,SAASM,KAAMC,IAAKP,SAASQ,YAC9CC,KAAMN,OAAOH,SAASU,aACtBC,MAAOR,OAAOH,SAASY,YACvBC,cAAeb,SAASc,MAClB,CAAC,GAAK,IAAM,EAAG,KAAM,IAAK,GAC1B,CAAC,GACPC,MAAOZ,OAAOH,SAASe,QAET,kBAAlBf,SAASM,OACTL,QAAQe,UAAY,CAAC,aAEpBb,OAAOH,SAASY,aAAeT,OAAOH,SAASiB,SAAWd,OAAOH,SAASkB,SAC3EjB,QAAQgB,OAASd,OAAOH,SAASiB,QACjChB,QAAQiB,MAAQf,OAAOH,SAASkB,qBAEhCrB,MAAM,0DACNA,MAAMI,cACLkB,OAAS,IAAIC,mBAAOtB,KAAKC,UAAWE,cAEpCkB,OAAOE,GAAG,kBAAkB,SACxBrB,SAASsB,iBAAmBtB,SAASuB,aAAe,GAAKzB,KAAK0B,eACxD,MAGPC,SAAW3B,KAAK4B,YAAYD,WAC5BE,WAAa3B,SAASuB,mBAKtBI,WAAa,GAAKC,KAAKC,MAAMJ,yBACzB5B,MACA,0DAEJ8B,WAAa,gBAEb9B,MAAM,0BAA4B4B,uBAClC5B,MAAM,0BAA4B8B,YAClCA,YACAG,YAAW,UACNC,mBAAmBJ,cACrB,KAEA,CAAP,QAGAK,IAAM,IAAIC,IAAIC,OAAOC,SAASC,MAC9BC,EAAIL,IAAIM,aAAaC,IAAI,KACzBC,WAAaR,IAAIM,aAAaC,IAAI,SAAW,IAAIE,MAC7C,sCAEJD,eACKE,aAAaF,UAAU,IACvBG,MAAK,kBACEN,GAAKH,OAAOU,MACZV,OAAOU,KAAKP,IAET,KAEVQ,MAAMC,sBAAaC,WACjBV,GAAKH,OAAOU,MACnBV,OAAOU,KAAKP,QAGXW,mBAEA,IAAIC,EAAI,EAAGA,EAAInD,KAAKoD,QAAQC,OAAQF,IAAK,CAC3BnD,KAAKoD,QAAQD,GACrBrD,WAAWE,KAAME,iBAGrB,EAMXgD,uCAEM,mBAAE,IAAMlD,KAAKC,WAAWqD,QAAQ,mBAAmBC,KACjD,SAASJ,EAAGK,0BACNA,MACGV,KAAK,uBACLS,KACG,SAASE,MAAOC,YACRhB,UAAYgB,OAAOC,aAAa,cAChCC,KAAOlB,UAAUC,MACb,gDAEJiB,WACKvC,OACAwC,YACG,KAAOxD,OAAOuD,KAAK,IAAM,GACrB,GAAKvD,OAAOuD,KAAK,IAAM,GACvBvD,OAAOuD,KAAK,IAChB,CACIlB,UAAWA,YAGlBK,MAAMC,sBAAac,aAE9BC,KAAK/D,YAGVqB,OAAOE,GAAG,YAAY,SAASyC,OAC5BA,MAAMC,KAAKvB,gCACTc,MACGV,KAAK,wBACLoB,YAAY,2CACfV,MACGV,KACG,mCACIkB,MAAMC,KAAKvB,UACX,MAEPY,QAAQ,QACRa,SAAS,2CACZ,wBAAwBZ,MAAK,WACvBvD,KAAKoE,gBACAC,WAAWC,SAAS,CACrBC,IAAKvE,KAAKoE,UAAY,GACtBI,KAAM,EACNC,SAAU,mBAMhCV,KAAK/D,OAGNA,KAAKqB,gBASLA,OAAOE,GAAG,QAAQ,KACdvB,KAAK0E,SACF1E,KAAK2E,aACAC,0BAGJC,SAEF,KAIN7E,KAAK2E,cAOLtD,OAAOE,GACR,OACA,gBACSuD,SAAU,eACX/E,MAAM,oBACZgE,KAAK/D,YAENqB,OAAOE,GACR,UACA,gBACSuD,SAAU,eACX/E,MAAM,uBACZgE,KAAK/D,YAINqB,OAAOE,GACR,QACA,gBACSuD,SAAU,eACX/E,MAAM,qBACZgE,KAAK/D,YAENqB,OAAOE,GACR,UACA,gBACSuD,SAAU,eACX/E,MAAM,uBACZgE,KAAK/D,YAENqB,OAAOE,GACR,UACA,gBACSuD,SAAU,eACX/E,MAAM,uBACZgE,KAAK/D,YAENqB,OAAOE,GACR,QACA,gBACSuD,SAAU,eACX/E,MAAM,qBACZgE,KAAK/D,YAGNqB,OAAOE,GACR,sBACA,gBACSwD,aAAe/E,KAAKqB,OAAO0D,gBAClChB,KAAK/D,YAINqB,OAAOE,GACR,aACA,gBACSyD,YAAchF,KAAKqB,OAAO2D,mBAC1BC,QAAUjF,KAAKgF,YAAchF,KAAKqB,OAAOM,wBAC1C5B,MACA,mCACIC,KAAKiF,QACL,mBACAjF,KAAKgF,cAEfjB,KAAK/D,YAINqB,OAAOE,GAAG,QAASvB,KAAKkF,UAAUnB,KAAK/D,OAGxCA,KAAKqB,OAAOlB,UAAUW,YAAY,CACnB,IAAIqE,gBAAe,UACzB9D,OAAOF,OACPnB,KAAKqB,OAAO+D,cAAgBpF,KAAKqB,OAAOgE,aACrCrF,KAAKqB,OAAOiE,mBAGfC,QAAQC,SAASC,cAAc,IAAMzF,KAAKC,+BAzG/CF,MACA,yDACIC,KAAK0F,MAiHrB9C,aAAaF,eACLkB,KAAOlB,UAAUC,MACjB,uDAEAiB,WACK/B,WACD,KAAOxB,OAAOuD,KAAK,IAAM,GACzB,GAAKvD,OAAOuD,KAAK,IAAM,GACvBvD,OAAOuD,KAAK,IACT5D,KAAKqB,OAAO2D,YAAYhF,KAAK6B,2BAEpC9B,MAAM,kBAAoB2C,WACvB1C,KAAKqB,OAAO2D,eAQvBW,qBACW,IAAIC,SAAQC,UACfA,QAAQ7F,KAAKqB,OAAOM,aACb,KASfmE,yBACW,IAAIF,SAAQC,UACfA,QAAQ7F,KAAKqB,OAAO0D,iBACb,KAUf9C,mBAAmB8D,aACR,IAAIH,SAAQC,UACfA,QAAQ7F,KAAKqB,OAAO2D,YAAYe,QACzB,KASfC,4BACW,IAAIJ,SAAQC,UACfA,QAAQ7F,KAAKqB,OAAO2D,gBACb"} \ No newline at end of file diff --git a/plugin/videojs/amd/src/videotime.js b/plugin/videojs/amd/src/videotime.js index 25241f56..e6b55da1 100644 --- a/plugin/videojs/amd/src/videotime.js +++ b/plugin/videojs/amd/src/videotime.js @@ -165,18 +165,10 @@ export default class VideoTime extends VideoTimeBase { this.player.on("play", () => { if (!this.played) { if (this.hasPro) { - // Getting a new session on first play. - this.getSession() - .then(() => { - this.view(); - this.startWatchInterval(); - return true; - }) - .catch(Notification.exception); - } else { - // Free version can still mark completion on video time view. - this.view(); + this.startWatchInterval(); } + // Free version can still mark completion on video time view. + this.view(); } return true; });