From 3da472fe77c1917453b47a83c089cd59d9f498dc Mon Sep 17 00:00:00 2001 From: sokunthearithmakara Date: Fri, 27 Sep 2024 17:29:14 +0700 Subject: [PATCH] QA fixes --- amd/build/editannotation.min.js | 2 +- amd/build/editannotation.min.js.map | 2 +- amd/build/mod_form.min.js | 2 +- amd/build/mod_form.min.js.map | 2 +- amd/build/report.min.js | 2 +- amd/build/report.min.js.map | 2 +- amd/src/editannotation.js | 20 ++-- amd/src/mod_form.js | 25 ++--- amd/src/report.js | 97 +++++++++---------- plugins/decision/amd/build/main.min.js | 2 +- plugins/decision/amd/build/main.min.js.map | 2 +- plugins/decision/amd/src/main.js | 58 ++++++++--- plugins/decision/classes/form.php | 19 ++++ .../decision/lang/en/ivplugin_decision.php | 1 + plugins/form/amd/build/main.min.js | 2 +- plugins/form/amd/build/main.min.js.map | 2 +- plugins/form/amd/src/main.js | 19 ++-- plugins/form/styles.css | 2 +- 18 files changed, 155 insertions(+), 106 deletions(-) diff --git a/amd/build/editannotation.min.js b/amd/build/editannotation.min.js index 2eb89a9..5124151 100644 --- a/amd/build/editannotation.min.js +++ b/amd/build/editannotation.min.js @@ -5,6 +5,6 @@ * @copyright 2024 Sokunthearith Makara * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -define("mod_interactivevideo/editannotation",["jquery","core/toast","core/notification","core/event_dispatcher","mod_interactivevideo/libraries/jquery-ui"],(function($,addToast,Notification,_ref){let player,totaltime,currentTime,{dispatchEvent:dispatchEvent}=_ref,ctRenderer={},playerReady=!1;const replaceProgressBars=percentage=>{percentage=percentage>100?100:percentage,$("#video-nav #progress").css("width",percentage+"%"),$("#scrollbar, #scrollhead-top").css("left",percentage+"%")},renderVideoNav=async function(annos,start,totaltime){if(0==annos.length)return void $("#video-nav ul").empty();$("#video-nav ul").empty(),$("#video-timeline-wrapper .skipsegment").remove(),annos.forEach((async x=>{const render=ctRenderer[x.type];await render.renderItemOnVideoNavigation(x)}));const time=await player.getCurrentTime();replaceProgressBars((time-start)/totaltime*100),dispatchEvent("annotationitemsrendered",{annotations:annos})};return{init:function(url,coursemodule,interaction,course,start,end,coursecontextid){let type=arguments.length>7&&void 0!==arguments[7]?arguments[7]:"yt",displayoptions=arguments.length>8?arguments[8]:void 0,userid=arguments.length>9?arguments[9]:void 0;const addNotification=function(msg){let type=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"info";addToast.add(msg,{type:type})};start=Number(start),isNaN(start)&&(start=0),end=Number(end),isNaN(end)&&(end=null);let contentTypes,annotations=[];const convertSecondsToHMS=function(s){let dynamic=arguments.length>1&&void 0!==arguments[1]&&arguments[1],rounded=!(arguments.length>2&&void 0!==arguments[2])||arguments[2];rounded&&(s=Math.round(s));let hours=Math.floor(s/3600),minutes=Math.floor((s-3600*hours)/60),seconds=s-3600*hours-60*minutes;return rounded&&seconds>59.5&&(seconds=0,minutes++,minutes>59&&(minutes=0,hours++)),minutes<10&&(minutes="0"+minutes),seconds=rounded?Math.round(seconds):parseFloat(seconds).toFixed(2),seconds<10&&(seconds="0"+seconds),dynamic&&0==hours?minutes+":"+seconds:(hours<10?"0"+hours:hours)+":"+minutes+":"+seconds};let activeid=null;const renderAnnotationItems=annotations=>{if(renderVideoNav(annotations,start,totaltime),$("#annotationwrapper .loader").remove(),$("#annotation-list").empty().removeClass("d-flex align-items-center justify-content-center"),0==annotations.length)return void $("#annotation-list").html("".concat(M.util.get_string("clickaddtoaddinteraction","mod_interactivevideo"))).addClass("d-flex align-items-center justify-content-center");annotations.sort((function(a,b){return Number(a.timestamp)-Number(b.timestamp)})),annotations.forEach((function(item){let listItem=$("#annotation-template").clone();ctRenderer[item.type].renderEditItem(annotations,listItem,item)}));let xp=annotations.filter((x=>x.xp)).map((x=>Number(x.xp))).reduce(((a,b)=>a+b),0);if($("#xp span").text(xp),activeid){const activeAnno=annotations.find((x=>x.id==activeid));activeAnno&&ctRenderer[activeAnno.type].postEditCallback(activeAnno)}},getAnnotations=()=>{const getItems=$.ajax({url:M.cfg.wwwroot+"/mod/interactivevideo/ajax.php",method:"POST",dataType:"text",data:{action:"get_items",sesskey:M.cfg.sesskey,id:interaction,contextid:M.cfg.contextid,coursecontextid:M.cfg.courseContextId}}),getContentTypes=$.ajax({url:M.cfg.wwwroot+"/mod/interactivevideo/ajax.php",method:"POST",dataType:"text",data:{action:"getallcontenttypes",sesskey:M.cfg.sesskey,contextid:M.cfg.contextid,coursecontextid:M.cfg.courseContextId}});$.when(getItems,getContentTypes).done((function(items,contenttypes){annotations=JSON.parse(items[0]),contentTypes=JSON.parse(contenttypes[0]),annotations=annotations.filter((x=>contentTypes.find((y=>y.name===x.type))));const getRenderers=new Promise((resolve=>{let count=0;contentTypes.forEach((x=>{require([""+x.amdmodule],(function(Type){ctRenderer[x.name]=new Type(player,annotations,interaction,course,0,0,0,0,type,0,totaltime,start,end,x,coursemodule),count++,count==contentTypes.length&&resolve(ctRenderer),ctRenderer[x.name].init()}))}))}));annotations.map((x=>(x.prop=JSON.stringify(contentTypes.find((y=>y.name===x.type))),x))),getRenderers.then((()=>{renderAnnotationItems(annotations)})).catch((()=>{}))}))},validateTimestampFormat=(timestamp,fld,existing)=>!!/^([0-9]{2}):([0-5][0-9]):([0-5][0-9])(\.\d{2})?$/.test(timestamp)||(addNotification(M.util.get_string("invalidtimestampformat","mod_interactivevideo"),"danger"),existing?$(fld).val(existing):$(fld).val(convertSecondsToHMS(start,!1,!1)),!1),validateTimeStartEnd=(timestamp,fld,existing,seconds,checkduration,checkexisting,checkskipsegment)=>{const parts=timestamp.split(":");if(timestamp=3600*Number(parts[0])+60*Number(parts[1])+Number(parts[2]),checkduration&&(timestamp>end||timestampx.timestamp==timestamp))&×tamp!=seconds)return addNotification(M.util.get_string("interactionalreadyexists","mod_interactivevideo"),"danger"),existing?$(fld).val(existing):$(fld).val(convertSecondsToHMS(start,!1,!1)),-1;if(checkskipsegment){const skip=annotations.filter((annotation=>"skipsegment"==annotation.type)).find((x=>Number(x.timestamp)Number(timestamp)));if(skip)return addNotification(M.util.get_string("interactionisbetweentheskipsegment","mod_interactivevideo",{start:convertSecondsToHMS(skip.timestamp,!0,!1),end:convertSecondsToHMS(skip.title,!0,!1)}),"danger"),existing?$(fld).val(existing):$(fld).val(convertSecondsToHMS(start,!1,!1)),-1}return timestamp},runInteraction=annotation=>{$("#annotation-modal").modal("hide"),$("#message").not("[data-placement=bottom]").remove(),$("#end-screen").remove(),player.pause();ctRenderer[annotation.type].runInteraction(annotation)},onReady=async()=>{"vimeo"!=player.type&&"html5video"!=player.type&&$("#video-block").addClass("no-pointer"),0==player.support.playbackrate?$("#changerate").remove():$("#changerate").removeClass("d-none");let t=await player.getDuration();end=end?Math.min(end,t):t,start>end&&(start=0),totaltime=end-start;let ratio=16/9;displayoptions.usefixedratio&&0!=displayoptions.usefixedratio||(ratio=player.aspectratio),$("#video-wrapper").css("padding-bottom",1/ratio*100+"%"),playerReady=!0,$("#timeline-wrapper #video-timeline").css({"background-image":"url("+player.posterImage+")","background-size":"contain","background-repeat":"no-repeat"}),$("#timeline-wrapper #duration").text(convertSecondsToHMS(end,!0)),$("#timeline-wrapper #currenttime").text(convertSecondsToHMS(start,!0));const minutes=Math.floor(totaltime/60);$("#timeline-items-wrapper").css("width",300*minutes+"px");const relWidth=$("#timeline-items").width();$("#minute-markers, #minute-markers-bg, #vseek").css("width",relWidth+"px");let startPercentage=0,newStart=start;start%60!=0&&(startPercentage=(60-start%60)/totaltime*100,newStart=start+(60-start%60),$("#minute-markers, #minute-markers-bg").append('
'));for(let i=newStart;i<=end;i+=60){let percentage=(i-newStart)/totaltime*100+startPercentage,marker="";marker=i>=3600?Math.floor(i/3600)+"h"+Math.floor(i%3600/60)+"m":Math.floor(i/60)+"m",$("#minute-markers, #minute-markers-bg").append('
').concat(marker,"
"))}end%60!=0&&$("#minute-markers, #minute-markers-bg").append('
'),getAnnotations()},onEnded=()=>{player.pause(),$("#playpause").find("i").removeClass("bi-pause-fill").addClass("bi-play-fill"),$("#video-wrapper").append('
\n
'),$("#video-nav #progress").css("width","100%"),$("#scrollbar, #scrollhead-top").css("left","100%"),$("#message #restart").focus();const currentAnnotation=annotations.find((annotation=>annotation.timestamp==end));currentAnnotation&&($("#annotation-list tr").removeClass("active"),$('tr[data-id="'.concat(currentAnnotation.id,'"]')).addClass("active"),$('#video-nav .annotation[data-id="'+currentAnnotation.id+'"] .item').tooltip("show"),setTimeout((function(){$('#video-nav .annotation[data-id="'+currentAnnotation.id+'"] .item').tooltip("hide")}),2e3))},onSeek=async function(t){if(!playerReady)return;(t=t?Number(t):await player.getCurrentTime())>start&&t{$("#message, #end-screen").remove(),$("#playpause").find("i").removeClass("bi-play-fill").addClass("bi-pause-fill"),player.audio&&!visualized&&(player.visualizer(),visualized=!0);const intervalFunction=async function(){let thisTime=await player.getCurrentTime();const isPlaying=await player.isPlaying(),isEnded=await player.isEnded();if(!isPlaying||isEnded)return void cancelAnimationFrame(onPlayingInterval);if(thisTime=end)return player.stop(end),cancelAnimationFrame(onPlayingInterval),void onEnded();dispatchEvent("timeupdate",{time:thisTime}),$("#timeline-wrapper #currenttime").text(convertSecondsToHMS(thisTime,!0));let percentage=(thisTime-start)/totaltime*100;$("#video-nav #progress").css("width",percentage+"%"),$("#scrollbar, #scrollhead-top").css("left",percentage+"%");const scrollBar=document.getElementById("scrollbar"),rect=scrollBar.getBoundingClientRect();(rect.left<0||rect.right>window.innerWidth)&&scrollBar.scrollIntoView({behavior:"instant",block:"center",inline:"center"});const currentAnnotation=annotations.find((x=>thisTime-player.frequency<=x.timestamp&&thisTime+player.frequency>=x.timestamp));currentAnnotation&&($("#annotation-list tr").removeClass("active"),$('tr[data-id="'.concat(currentAnnotation.id,'"]')).addClass("active"),$('#video-nav .annotation[data-id="'+currentAnnotation.id+'"] .item').tooltip("show"),setTimeout((function(){$('#video-nav .annotation[data-id="'+currentAnnotation.id+'"] .item').tooltip("hide")}),2e3));let skip=annotations.filter((annotation=>"skipsegment"==annotation.type)).find((x=>Number(x.timestamp)Number(thisTime)));skip&&(await player.seek(Number(skip.title)),percentage=(skip.title-start)/totaltime*100,replaceProgressBars(percentage))};if("yt"==player.type||"wistia"==player.type){const animate=async()=>{intervalFunction(),onPlayingInterval=requestAnimationFrame(animate)};onPlayingInterval=requestAnimationFrame(animate)}else intervalFunction()},onPause=()=>{cancelAnimationFrame(onPlayingInterval),$("#playpause").find("i").removeClass("bi-pause-fill").addClass("bi-play-fill")};require(["mod_interactivevideo/player/"+type],(function(VideoPlayer){player=new VideoPlayer(url,start,end,!1,!0)})),$(document).on("iv:playerReady",(function(){onReady()})),$(document).on("iv:playerPaused",(function(){onPause()})),$(document).on("iv:playerPlaying",(function(){onPlaying()})),$(document).on("iv:playerEnded",(function(){onEnded()})),$(document).on("iv:playerSeek",(function(e){onSeek(e.detail.time)})),$(document).on("annotationupdated",(function(e){const action=e.originalEvent.detail.action;if("import"==action)return annotations=e.originalEvent.detail.annotations,renderAnnotationItems(annotations),void addNotification(M.util.get_string("interactionimported","mod_interactivevideo"),"success");let updated=e.originalEvent.detail.annotation;"edit"!=action&&"draft"!=action&&"savedraft"!=action||(annotations=annotations.filter((function(item){return item.id!=updated.id}))),updated.prop=JSON.stringify(contentTypes.find((x=>x.name===updated.type))),annotations.push(updated),activeid="add"==action?updated.id:null,renderAnnotationItems(annotations),"add"==action||"clone"==action?(addNotification(M.util.get_string("interactionadded","mod_interactivevideo"),"success"),$('tr[data-id="'.concat(updated.id,'"]')).addClass("active")):"edit"==action&&(addNotification(M.util.get_string("interactionupdated","mod_interactivevideo"),"success"),$('tr[data-id="'.concat(updated.id,'"]')).addClass("active"),setTimeout((function(){$('tr[data-id="'.concat(updated.id,'"]')).removeClass("active")}),1500)),annotations.find((x=>"draft"==x.status))?$("#timeline-wrapper #savedraft").removeAttr("disabled").addClass("pulse"):$("#timeline-wrapper #savedraft").attr("disabled","disabled").removeClass("pulse")})),$(document).on("annotationdeleted",(function(e){const annotation=e.originalEvent.detail.annotation;activeid=null,$('tr[data-id="'.concat(annotation.id,'"]')).addClass("deleted"),setTimeout((function(){annotations=annotations.filter((function(item){return item.id!=annotation.id})),renderAnnotationItems(annotations),addNotification(M.util.get_string("interactiondeleted","mod_interactivevideo"),"success")}),1e3)})),$(document).on("click","#addcontentdropdown .dropdown-item",(async function(e){if(!playerReady)return;e.preventDefault(),$("#addcontentdropdown .dropdown-item").removeClass("active");const ctype=$(this).data("type");player.pause();let timestamp=currentTime||await player.getCurrentTime();timestamp=Number(timestamp.toFixed(2));const contenttype=contentTypes.find((x=>x.name==ctype));if(contenttype.hastimestamp){if(annotations.find((x=>x.timestamp==timestamp)))return void addNotification(M.util.get_string("interactionalreadyexists","mod_interactivevideo"),"danger");if(annotations.filter((x=>"skipsegment"==x.type)).find((x=>Number(x.timestamp)Number(currentTime))))return void addNotification(M.util.get_string("interactionisbetweentheskipsegment","mod_interactivevideo"),"danger")}contenttype.allowmultiple||!annotations.find((x=>x.type==ctype))?(currentTime=null,ctRenderer[ctype].addAnnotation(annotations,timestamp,coursemodule)):addNotification(M.util.get_string("interactionalreadyexists","mod_interactivevideo"),"danger")})),$(document).on("click","tr.annotation .edit",(async function(e){e.preventDefault();const timestamp=$(this).closest(".annotation").data("timestamp");timestamp&&await player.seek(timestamp,!0),player.pause();const id=$(this).closest(".annotation").data("id"),contenttype=$(this).closest(".annotation").data("type");ctRenderer[contenttype].editAnnotation(annotations,id,coursemodule)})),$(document).on("click","tr.annotation .copy",(async function(e){e.preventDefault();const id=$(this).closest(".annotation").data("id"),contenttype=$(this).closest(".annotation").data("type"),time=await player.getCurrentTime();ctRenderer[contenttype].cloneAnnotation(id,time)})),$(document).on("click","tr.annotation .delete",(function(e){e.preventDefault(),player.pause();const id=$(this).closest(".annotation").data("id"),annotation=annotations.find((annotation=>annotation.id==id));try{Notification.deleteCancel(M.util.get_string("deleteinteraction","mod_interactivevideo"),M.util.get_string("deleteinteractionconfirm","mod_interactivevideo"),M.util.get_string("delete","mod_interactivevideo"),(function(){ctRenderer[annotation.type].deleteAnnotation(annotations,id)}),null)}catch{Notification.deleteCancelPromise(M.util.get_string("deleteinteraction","mod_interactivevideo"),M.util.get_string("deleteinteractionconfirm","mod_interactivevideo")).then((()=>ctRenderer[annotation.type].deleteAnnotation(annotations,id))).catch((()=>{}))}})),$(document).on("click","tr.annotation .title",(async function(e){e.preventDefault();const timestamp=$(this).closest(".annotation").data("timestamp");replaceProgressBars((timestamp-start)/totaltime*100),await player.seek(timestamp,!0),player.pause();const id=$(this).closest(".annotation").data("id"),theAnnotation=annotations.find((annotation=>annotation.id==id));setTimeout((()=>{runInteraction(theAnnotation)}),500)})),$(document).on("click","tr.annotation .timestamp",(async function(e){e.preventDefault();const timestamp=$(this).data("timestamp");await player.seek(timestamp),player.play()})),$(document).on("contextmenu","#vseek, #video-timeline",(async function(e){if(!playerReady)return;e.preventDefault(),e.stopImmediatePropagation();const percentage=e.offsetX/$(this).width();replaceProgressBars(100*percentage),currentTime=percentage*totaltime+start,await player.seek(currentTime),player.pause(),$("#addcontent").trigger("click")})),$(document).on("contextmenu","#scrollbar, #scrollhead-top",(async function(e){playerReady&&(e.preventDefault(),e.stopImmediatePropagation(),currentTime=await player.getCurrentTime(),$("#addcontent").trigger("click"))})),$(document).on("click","#playpause",(async function(e){if(!playerReady)return;if(e.preventDefault(),await player.isPlaying())player.pause();else{await player.getCurrentTime()>=end?$("#end-screen #restart").trigger("click"):player.play()}})),$(document).on("click","#video-block",(function(e){playerReady&&(e.preventDefault(),$("#playpause").trigger("click"))})),$(document).on("contextmenu","#video-nav .annotation",(function(e){e.preventDefault(),e.stopImmediatePropagation();const id=$(this).data("id");$('tr.annotation[data-id="'.concat(id,'"] .edit')).trigger("click")})),$(document).on("contextmenu","[data-editable]",(function(e){if(e.preventDefault(),e.stopImmediatePropagation(),$("[data-field].editing").length>0)return;const fld=$(this).data("editable");$(this).hide(),$(this).siblings('[data-field="'+fld+'"]').removeClass("d-none").focus().addClass("editing")})),$(document).on("keyup","[data-field].editing",(function(e){$(this).removeClass("is-invalid");const initialValue=$(this).data("initial-value"),val=$(this).val(),fld=$(this).data("field");if(""==val&&$(this).addClass("is-invalid"),"Escape"==e.key)return $(this).val(initialValue),$(this).removeClass("editing"),$(this).addClass("d-none"),void $(this).siblings("[data-editable]").show();if("Enter"!=e.key);else{let seconds;if("timestamp"==fld){const parts=initialValue.split(":");if(seconds=3600*Number(parts[0])+60*Number(parts[1])+Number(parts[2]),!validateTimestampFormat(val,"[data-field].editing",initialValue))return void $(this).addClass("is-invalid");const timestamp=validateTimeStartEnd(val,"[data-field].editing",initialValue,seconds,!0,!0,!0);if(-1==timestamp)return void $(this).addClass("is-invalid");seconds=timestamp}if($(this).hasClass("is-invalid"))return;if(val==initialValue)return $(this).removeClass("editing"),$(this).addClass("d-none"),void $(this).siblings("[data-editable]").show();const id=$(this).data("id");$.ajax({url:M.cfg.wwwroot+"/mod/interactivevideo/ajax.php",method:"POST",dataType:"text",data:{action:"quickeditfield",sesskey:M.cfg.sesskey,id:id,field:fld,contextid:M.cfg.contextid,value:"timestamp"==fld?seconds:val},success:function(data){const updated=JSON.parse(data);dispatchEvent("annotationupdated",{annotation:updated,action:"edit"})}})}})),$(document).on("blur","[data-field].editing",(function(){const initialValue=$(this).data("initial-value");$(this).val(initialValue),$(this).removeClass("editing"),$(this).addClass("d-none"),$(this).siblings("[data-editable]").show()})),$(document).on("click","#end-screen #restart",(async function(e){e.preventDefault(),$("#end-screen").remove(),await player.seek(start),player.play()})),$(document).on("mouseover","tr.annotation",(function(){const id=$(this).data("id");$('#video-nav ul li[data-id="'.concat(id,'"] .item')).trigger("mouseover")})),$(document).on("mouseout","tr.annotation",(function(){const id=$(this).data("id");$('#video-nav ul li[data-id="'.concat(id,'"] .item')).trigger("mouseout"),$(".tooltip").remove()})),$(document).on("mouseover","#video-nav ul li",(function(){const id=$(this).data("id");$('tr.annotation[data-id="'.concat(id,'"]')).addClass("active")})),$(document).on("mouseout","#video-nav ul li",(function(){const id=$(this).data("id");$('tr.annotation[data-id="'.concat(id,'"]')).removeClass("active")})),$(document).on("change",".timestamp-input, .timestamp-field input",(function(){$(this).removeClass("is-invalid");const parts=$(this).val().split(":"),seconds=3600*Number(parts[0])+60*Number(parts[1])+Number(parts[2]);if(!validateTimestampFormat($(this).val(),this))return void $(this).addClass("is-invalid");-1!=validateTimeStartEnd($(this).val(),this,"00:00:00",seconds,!0,!1,!0)||$(this).addClass("is-invalid")}));const appendTimestampMarker=(seconds,rounded)=>{const formattedTime=convertSecondsToHMS(seconds,!0,rounded);$("#vseek #bar").append('
\n
'.concat(formattedTime,"
"))};$(document).on("annotationitemsrendered",(function(){$('#timeline-wrapper [data-toggle="tooltip"]').tooltip({boundary:"window",container:"#timeline"});let targetAnnotation=null;try{$("#timeline-items .annotation, #video-timeline-wrapper .skipsegment").draggable("destroy"),$("#timeline-items .annotation, #video-timeline-wrapper .skipsegment").resizable("destroy")}catch(e){}$("#timeline-items .annotation.li-draggable").draggable({axis:"x",start:function(){appendTimestampMarker($(this).data("timestamp")),$(".tooltip, #message").remove(),$("#timeline-items").addClass("no-pointer-events")},drag:async function(event,ui){$(".tooltip").remove();let timestamp=(ui.position.left+5)/$("#timeline-items").width()*totaltime+start;timestampend&&(timestamp=end,ui.position.left=$("#timeline-items").width()-5),$("#scrollbar, #position-marker, #scrollhead-top").css("left",(timestamp-start)/totaltime*100+"%"),$(this).css("left",(timestamp-start)/totaltime*100+"%"),await player.seek(timestamp),player.pause(),$("#vseek #position").text(convertSecondsToHMS(timestamp,!0,!1))},stop:async function(event,ui){$(".tooltip").remove(),$("#vseek #position-marker").remove(),setTimeout((function(){$("#timeline-items").removeClass("no-pointer-events")}),200);let timestamp=(ui.position.left+5)/$("#timeline-items").width()*totaltime+start;timestampend&&(timestamp=end,$(this).css("left","calc(100% - 5px)")),$("#scrollbar, #position-marker, #scrollhead-top").css("left",(timestamp-start)/totaltime*100+"%");const id=$(this).data("id");targetAnnotation=annotations.find((x=>x.id==id));if(annotations.find((x=>x.timestamp==timestamp&&x.id!=id)))return addNotification(M.util.get_string("interactionalreadyexists","mod_interactivevideo"),"danger"),void renderAnnotationItems(annotations);targetAnnotation.timestamp!=timestamp&&(targetAnnotation.timestamp=timestamp,targetAnnotation.status="draft",dispatchEvent("annotationupdated",{annotation:targetAnnotation,action:"draft"}),await player.seek(timestamp),player.pause(),$("#scrollbar, #position-marker, #scrollhead-top").css("left",(timestamp-start)/totaltime*100+"%"))}}),$("#video-timeline-wrapper .skipsegment").draggable({axis:"x",start:function(){$("#message").remove(),appendTimestampMarker($(this).data("timestamp")),$("#timeline-items").addClass("no-pointer-events")},drag:async function(event,ui){const id=$(this).data("id");targetAnnotation=annotations.find((x=>x.id==id));let timestamp=ui.position.left/$("#video-timeline").width()*totaltime+start;timestampend&&(timestamp=end),$("#scrollbar, #position-marker, #scrollhead-top").css("left",(timestamp-start)/totaltime*100+"%"),await player.seek(timestamp),player.pause(),$("#vseek #position").text(convertSecondsToHMS(timestamp,!0,!1))},stop:async function(event,ui){$("#vseek #position-marker").remove(),setTimeout((function(){$("#timeline-items").removeClass("no-pointer-events")}),200);let timestamp=ui.position.left/$("#video-timeline").width()*totaltime+start;const id=$(this).data("id");targetAnnotation=annotations.find((x=>x.id==id));let skipduration=Number(targetAnnotation.title)-Number(targetAnnotation.timestamp);if(timestamp<0&×tamp+skipdurationend)return void renderAnnotationItems(annotations);if(timestampend&&(skipduration=Math.abs(end-timestamp),timestamp=end-skipduration),skipduration<=0)return void renderAnnotationItems(annotations);if(annotations.find((x=>x.timestamp==timestamp&&x.id!=id)))return addNotification(M.util.get_string("interactionalreadyexists","mod_interactivevideo"),"danger"),void renderAnnotationItems(annotations);targetAnnotation.timestamp!=timestamp?(targetAnnotation.timestamp=timestamp,targetAnnotation.title=timestamp+skipduration,targetAnnotation.title>end&&(targetAnnotation.title=end),targetAnnotation.status="draft",dispatchEvent("annotationupdated",{annotation:targetAnnotation,action:"draft"}),await player.seek(timestamp),player.pause(),$("#scrollbar, #position-marker, #scrollhead-top").css("left",(timestamp-start)/totaltime*100+"%")):renderAnnotationItems(annotations)}}),$("#video-timeline-wrapper .skipsegment").resizable({containment:"#video-timeline-wrapper",handles:"e, w",start:function(){$("#message").remove(),appendTimestampMarker($(this).data("timestamp")),$("#timeline-items").addClass("no-pointer-events")},resize:async function(event,ui){let timestamp;ui.originalPosition.left!=ui.position.left||ui.originalSize.width==ui.size.width?(ui.position.left<0&&(ui.position.left=0),timestamp=ui.position.left/$("#video-timeline").width()*totaltime+start):timestamp=(ui.position.left+ui.size.width)/$("#video-timeline").width()*totaltime+start,$("#scrollbar, #position-marker, #scrollhead-top").css("left",(timestamp-start)/totaltime*100+"%"),await player.seek(timestamp),player.pause(),$("#vseek #position").text(convertSecondsToHMS(timestamp,!0,!1))},stop:async function(event,ui){$("#vseek #position-marker").remove(),setTimeout((function(){$("#timeline-items").removeClass("no-pointer-events")}),200);const id=$(this).data("id");let timestamp,direction;targetAnnotation=annotations.find((x=>x.id==id)),ui.originalPosition.left!=ui.position.left?(ui.position.left<0&&(ui.position.left=0),timestamp=ui.position.left/$("#video-timeline").width()*totaltime+start,direction="left"):(timestamp=(ui.position.left+ui.size.width)/$("#video-timeline").width()*totaltime+start,direction="right");if(annotations.find((x=>x.timestamp==timestamp&&x.id!=id)))return addNotification(M.util.get_string("interactionalreadyexists","mod_interactivevideo"),"danger"),void renderAnnotationItems(annotations);targetAnnotation.timestamp!=timestamp&&("left"==direction?targetAnnotation.timestamp=timestamp:(targetAnnotation.title=timestamp,targetAnnotation.title>end&&(targetAnnotation.title=end)),targetAnnotation.status="draft",dispatchEvent("annotationupdated",{annotation:targetAnnotation,action:"draft"}),await player.seek(timestamp),player.pause(),$("#scrollbar, #position-marker, #scrollhead-top").css("left",(timestamp-start)/totaltime*100+"%"))}}),$("#video-timeline-wrapper .skipsegment").off("contextmenu").on("contextmenu",(function(e){e.preventDefault(),e.stopImmediatePropagation();const id=$(this).data("id");$('tr.annotation[data-id="'.concat(id,'"] .edit')).trigger("click")})),$("#video-timeline-wrapper .skipsegment").off("click").on("click",(async function(e){e.preventDefault();const timestamp=$(this).data("timestamp");await player.seek(timestamp),player.pause()})),$("#video-timeline-wrapper .skipsegment .delete-skipsegment").off("click").on("click",(function(e){e.preventDefault();const id=$(this).closest(".skipsegment").data("id");$('tr.annotation[data-id="'.concat(id,'"] .delete')).trigger("click")}))})),$("#scrollbar").draggable({containment:"#timeline-items",axis:"x",cursor:"col-resize",start:function(event,ui){$("#timeline-items").addClass("no-pointer-events"),$("#message").remove(),appendTimestampMarker(ui.position.left/$("#timeline-items").width()*totaltime+start,!1)},drag:async function(event,ui){let timestamp=ui.position.left/$("#timeline-items").width()*totaltime+start,percentage=(timestamp-start)/totaltime*100;$("#vseek #position").text(convertSecondsToHMS(timestamp,!0,!1)),$("#vseek #position-marker, #scrollhead-top, #scrollbar").css("left",percentage+"%"),await player.seek(timestamp),player.pause()},stop:function(event,ui){$("#vseek #position-marker").remove(),setTimeout((function(){$("#timeline-items").removeClass("no-pointer-events")}),200);let timestamp=ui.position.left/$("#timeline-items").width()*totaltime+start;$("#scrollbar, #scrollhead-top").css("left",(timestamp-start)/totaltime*100+"%")}}),$("#scrollhead-top").draggable({axis:"x",cursor:"col-resize",start:function(event,ui){$("#vseek").addClass("no-pointer-events"),$("#message").remove(),appendTimestampMarker(ui.position.left/$("#vseek").width()*totaltime+start,!1)},drag:async function(event,ui){let timestamp=ui.position.left/$("#vseek").width()*totaltime+start,percentage=(timestamp-start)/totaltime*100;timestamp992&&($("#separator").css("left",playerWidth+"px"),$("#player-region").css("width",playerWidth+"px"),$("#content-region").css("width","calc(100% - "+playerWidth+"px)"));const timelineHeight=localStorage.getItem("timeline-height");timelineHeight&&($("#timeline-wrapper").css("height",timelineHeight+"px"),$("#top-region").css("height","calc(100dvh - ".concat(Number(timelineHeight)+70,"px)"))),$("#vseek #bar, #video-timeline, #video-nav .annotation").on("mouseenter",(function(e){$("#cursorbar, #position-marker").remove(),e.preventDefault(),e.stopImmediatePropagation();let $scrollbar=$("#scrollbar").clone();$scrollbar.attr("id","cursorbar");const parentOffset=$(this).offset(),relX=e.pageX-parentOffset.left;$scrollbar.css("left",relX+5+"px"),$scrollbar.find("#scrollhead").remove();const percentage=relX/$(this).width(),formattedTime=convertSecondsToHMS(percentage*totaltime+start,!0,!1);$("#vseek #bar").append('
\n
'.concat(formattedTime,"
")),$("#vseek #position-marker").css("left",relX+"px"),$("#timeline-items").append($scrollbar)})),$("#vseek #bar, #video-timeline, #video-nav .annotation").on("mouseleave",(function(e){e.stopImmediatePropagation(),$("#vseek #position-marker, #cursorbar").remove()})),$("#vseek #bar, #video-timeline").on("mousemove",(function(e){e.stopImmediatePropagation();const parentOffset=$(this).offset(),relX=e.pageX-parentOffset.left;let time=relX/$(this).width()*totaltime+start;timex.id==id));await player.seek(annotation.timestamp),runInteraction(annotation)})),$(document).on("click","#vseek #bar, #video-timeline",(async function(e){e.preventDefault(),e.stopImmediatePropagation();const percentage=e.offsetX/$(this).width();replaceProgressBars(100*percentage),await player.seek(percentage*totaltime+start),player.pause(),$("#message, #end-screen").remove()})),$("#zoomout").on("click",(function(){const currentLevel=$("#timeline-items-wrapper").css("width"),newLevel=parseInt(currentLevel)-300;$("#timeline-items-wrapper").css("width",newLevel+"px");const relWidth=$("#timeline-items").width();$("#minute-markers, #minute-markers-bg, #vseek").css("width",relWidth+"px");let timelineElement=document.getElementById("timeline");timelineElement.scrollWidth<=timelineElement.clientWidth&&$(this).attr("disabled","disabled"),dispatchEvent("annotationitemsrendered",{annotations:annotations})})),$("#zoomin").on("click",(function(){const currentLevel=$("#timeline-items-wrapper").css("width"),newLevel=parseInt(currentLevel)+300;$("#timeline-items-wrapper").css("width",newLevel+"px");const relWidth=$("#timeline-items").width();$("#minute-markers, #minute-markers-bg, #vseek").css("width",relWidth+"px"),$("#zoomout").removeAttr("disabled"),dispatchEvent("annotationitemsrendered",{annotations:annotations})})),$("#timeline").on("wheel",(function(e){(e.ctrlKey||e.metaKey)&&(e.preventDefault(),e.originalEvent.deltaY<0?$("#zoomin").trigger("click"):$("#zoomout").trigger("click"))})),document.getElementById("timeline").addEventListener("scroll",(function(){document.getElementById("minute-markers-wrapper").scrollLeft=this.scrollLeft,document.getElementById("vseek").style.left=-this.scrollLeft+"px",document.getElementById("minute-markers-bg-wrapper").style.left=-this.scrollLeft+"px",document.getElementById("scrollbar").scrollHeight=this.scrollHeight})),$("#savedraft").on("click",(function(e){e.stopImmediatePropagation();let draftAnnotations=annotations.filter((x=>"draft"==x.status)),count=0;draftAnnotations.forEach((function(a){$.ajax({url:M.cfg.wwwroot+"/mod/interactivevideo/ajax.php",method:"POST",dataType:"text",data:{action:"quickeditfield",sesskey:M.cfg.sesskey,id:a.id,field:"timestamp",contextid:M.cfg.contextid,value:a.timestamp},success:function(data){const updated=JSON.parse(data);dispatchEvent("annotationupdated",{annotation:updated,action:"savedraft"})}}),"skipsegment"==a.type&&$.ajax({url:M.cfg.wwwroot+"/mod/interactivevideo/ajax.php",method:"POST",dataType:"text",data:{action:"quickeditfield",sesskey:M.cfg.sesskey,id:a.id,field:"title",contextid:M.cfg.contextid,value:a.title},success:function(data){const updated=JSON.parse(data);dispatchEvent("annotationupdated",{annotation:updated,action:"savedraft"})}}),count++,count==draftAnnotations.length&&addNotification(M.util.get_string("draftsaved","mod_interactivevideo"),"success")}))})),$("#addcontent").on("click",(async function(e){e.preventDefault(),playerReady&&$("#contentmodal").modal("show")})),$("#contentmodal").on("show.bs.modal",(function(){player.pause(),$("#addcontentdropdown").addClass("modal-body")})),$("#contentmodal").on("hide.bs.modal",(function(){$("#addcontentdropdown .dropdown-item").removeClass("active"),$("#addcontentdropdown").removeClass("modal-body")})),window.addEventListener("beforeunload",(e=>{if(annotations.find((x=>"draft"==x.status))){const confirmationMessage=M.util.get_string("unsavedchanges","mod_interactivevideo");return e.returnValue=confirmationMessage,confirmationMessage}return!0})),$(document).on("click",".changerate",(function(e){e.preventDefault();const rate=$(this).data("rate");player.setRate(rate),$(".changerate").find("i").removeClass("bi-check"),$(this).find("i").addClass("bi-check")})),$(document).on("iv:playerRateChange",(function(e){$(".changerate").find("i").removeClass("bi-check"),$('.changerate[data-rate="'.concat(e.originalEvent.detail.rate,'"]')).find("i").addClass("bi-check")}));let timelineWrapper=document.getElementById("timeline-wrapper"),resizeObserver=new ResizeObserver((()=>{const relWidth=$("#timeline-items").width();$("#minute-markers, #minute-markers-bg, #vseek").css("width",relWidth+"px")}));resizeObserver.observe(timelineWrapper),$(document).on("click","#importcontent",(function(e){e.preventDefault();const importmodal='");$("body").append(importmodal),$("#importmodal").modal("show"),$("#importmodal").on("hidden.bs.modal",(function(){$("#importmodal").remove()})),$("#importmodal").off("shown.bs.modal").on("shown.bs.modal",(function(){$("#importmodal .modal-dialog").draggable({handle:".modal-header"}),$.ajax({url:M.cfg.wwwroot+"/mod/interactivevideo/ajax.php",method:"POST",dataType:"text",data:{action:"get_taught_courses",sesskey:M.cfg.sesskey,contextid:M.cfg.contextid,userid:userid},success:function(data){let courses=JSON.parse(data);courses.sort(((a,b)=>b.fullname.localeCompare(a.fullname)));let courseSelect='";let selectfield='
\n \n ").concat(courseSelect,"
");$("#importmodal .modal-body").append(selectfield),$("#importmodal #importcourse").val(course),$("#importmodal #importcourse").trigger("change")}})}))})),$(document).on("change","#importmodal #importcourse",(function(){$("#importmodal .selectcm, #importmodal .select-interaction").remove(),$.ajax({url:M.cfg.wwwroot+"/mod/interactivevideo/ajax.php",method:"POST",dataType:"text",data:{action:"get_cm_by_courseid",sesskey:M.cfg.sesskey,contextid:M.cfg.contextid,courseid:$(this).val()},success:function(data){let cms=JSON.parse(data);cms.sort(((a,b)=>b.name.localeCompare(a.name)));let cmSelect='";let selectfield='
\n \n ").concat(cmSelect,"
");$("#importmodal .selectcourse").after(selectfield)}})})),$(document).on("change","#importmodal #importcm",(async function(){$("#importmodal .select-interaction").remove(),$("#importmodal #importcm").after('
\n
'));let interactions=await $.ajax({url:M.cfg.wwwroot+"/mod/interactivevideo/ajax.php",method:"POST",dataType:"text",data:{action:"get_items",sesskey:M.cfg.sesskey,id:$(this).val(),contextid:M.cfg.contextid,coursecontextid:M.cfg.courseContextId}});interactions=JSON.parse(interactions),interactions=interactions.filter((x=>"skipsegment"!=x.type)),0!=interactions.length?($("#importmodal .select-interaction").append('
\n \n \n \n
')),interactions=interactions.map((int=>{const ctype=contentTypes.find((y=>y.name===int.type));return int.prop=JSON.stringify(ctype),int.icon=ctype.icon,(int.timestamp>end||int.timestamp0?int.outside=!0:int.outside=!1,!ctype.allowmultiple&&annotations.find((x=>x.type==int.type))&&(int.disabled=!0),int})),interactions.sort(((a,b)=>a.timestamp-b.timestamp)),interactions.forEach((int=>{const inputgroup='
\n
\n \n
\n \n \n
');$("#importmodal .select-interaction").append(inputgroup)})),$(document).off("click","#importmodal #importcontentbutton").on("click","#importmodal #importcontentbutton",(async function(e){e.preventDefault();let $selected=$('#importmodal .select-interaction input[type="checkbox"]:checked'),selectedInt=[];if($selected.each((function(){let $row=$(this).closest(".input-group");const name=$row.find(".name").val();if(""==name.trim())return;let timestamp=$row.find(".timestamp-input").val();if(""==timestamp)return;if(Number(timestamp)<0)timestamp=Number(timestamp);else{const parts=timestamp.split(":");if(timestamp=3600*Number(parts[0])+60*Number(parts[1])+Number(parts[2]),annotations.find((x=>x.timestamp==timestamp)))return}let id=$row.data("id"),int=interactions.find((x=>x.id==id));int.title=name,int.timestamp=timestamp;let xp=Number($row.find(".xp").val());(isNaN(xp)||""==xp)&&(xp=0),int.xp=xp,selectedInt.push(int)})),0!=selectedInt.length){let interactions=await $.ajax({url:M.cfg.wwwroot+"/mod/interactivevideo/ajax.php",method:"POST",dataType:"text",data:{action:"import_annotations",sesskey:M.cfg.sesskey,contextid:M.cfg.contextid,annotations:JSON.stringify(selectedInt),tocourse:M.cfg.courseId,fromcourse:$("#importcourse").val(),tocm:interaction,fromcm:$("#importcm").val(),module:coursemodule}});interactions=JSON.parse(interactions),$("#importmodal").modal("hide"),annotations=annotations.concat(interactions),dispatchEvent("annotationupdated",{annotations:annotations,action:"import"}),interactions.forEach((int=>{int.allowmultiple||ctRenderer[int.type].init()}))}else addNotification(M.util.get_string("selectinteraction","mod_interactivevideo"),"danger")}))):$("#importmodal .select-interaction").append('
\n '.concat(M.util.get_string("nocontent","mod_interactivevideo"),"
"))}))}}})); +define("mod_interactivevideo/editannotation",["jquery","core/toast","core/notification","core/event_dispatcher","mod_interactivevideo/libraries/jquery-ui"],(function($,addToast,Notification,_ref){let player,totaltime,currentTime,{dispatchEvent:dispatchEvent}=_ref,ctRenderer={},playerReady=!1;const replaceProgressBars=percentage=>{percentage=percentage>100?100:percentage,$("#video-nav #progress").css("width",percentage+"%"),$("#scrollbar, #scrollhead-top").css("left",percentage+"%")},renderVideoNav=async function(annos,start,totaltime){if(0==annos.length)return void $("#video-nav ul").empty();$("#video-nav ul").empty(),$("#video-timeline-wrapper .skipsegment").remove(),annos.forEach((async x=>{const render=ctRenderer[x.type];await render.renderItemOnVideoNavigation(x)}));const time=await player.getCurrentTime();replaceProgressBars((time-start)/totaltime*100),dispatchEvent("annotationitemsrendered",{annotations:annos})};return{init:function(url,coursemodule,interaction,course,start,end,coursecontextid){let type=arguments.length>7&&void 0!==arguments[7]?arguments[7]:"yt",displayoptions=arguments.length>8?arguments[8]:void 0,userid=arguments.length>9?arguments[9]:void 0;const addNotification=function(msg){let type=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"info";addToast.add(msg,{type:type})};start=Number(start),isNaN(start)&&(start=0),end=Number(end),isNaN(end)&&(end=null);let contentTypes,annotations=[];const convertSecondsToHMS=function(s){let dynamic=arguments.length>1&&void 0!==arguments[1]&&arguments[1],rounded=!(arguments.length>2&&void 0!==arguments[2])||arguments[2];rounded&&(s=Math.round(s));let hours=Math.floor(s/3600),minutes=Math.floor((s-3600*hours)/60),seconds=s-3600*hours-60*minutes;return rounded&&seconds>59.5&&(seconds=0,minutes++,minutes>59&&(minutes=0,hours++)),minutes<10&&(minutes="0"+minutes),seconds=rounded?Math.round(seconds):parseFloat(seconds).toFixed(2),seconds<10&&(seconds="0"+seconds),dynamic&&0==hours?minutes+":"+seconds:(hours<10?"0"+hours:hours)+":"+minutes+":"+seconds};let activeid=null;const renderAnnotationItems=annotations=>{if(renderVideoNav(annotations,start,totaltime),$("#annotationwrapper .loader").remove(),$("#annotation-list").empty().removeClass("d-flex align-items-center justify-content-center"),0==annotations.length)return void $("#annotation-list").html("".concat(M.util.get_string("clickaddtoaddinteraction","mod_interactivevideo"))).addClass("d-flex align-items-center justify-content-center");annotations.sort((function(a,b){return Number(a.timestamp)-Number(b.timestamp)})),annotations.forEach((function(item){let listItem=$("#annotation-template").clone();ctRenderer[item.type].renderEditItem(annotations,listItem,item)}));let xp=annotations.filter((x=>x.xp)).map((x=>Number(x.xp))).reduce(((a,b)=>a+b),0);if($("#xp span").text(xp),activeid){const activeAnno=annotations.find((x=>x.id==activeid));activeAnno&&ctRenderer[activeAnno.type].postEditCallback(activeAnno)}},getAnnotations=()=>{const getItems=$.ajax({url:M.cfg.wwwroot+"/mod/interactivevideo/ajax.php",method:"POST",dataType:"text",data:{action:"get_items",sesskey:M.cfg.sesskey,id:interaction,contextid:M.cfg.contextid,coursecontextid:M.cfg.courseContextId}}),getContentTypes=$.ajax({url:M.cfg.wwwroot+"/mod/interactivevideo/ajax.php",method:"POST",dataType:"text",data:{action:"getallcontenttypes",sesskey:M.cfg.sesskey,contextid:M.cfg.contextid,coursecontextid:M.cfg.courseContextId}});$.when(getItems,getContentTypes).done((function(items,contenttypes){annotations=JSON.parse(items[0]),contentTypes=JSON.parse(contenttypes[0]),annotations=annotations.filter((x=>contentTypes.find((y=>y.name===x.type))));const getRenderers=new Promise((resolve=>{let count=0;contentTypes.forEach((x=>{require([""+x.amdmodule],(function(Type){ctRenderer[x.name]=new Type(player,annotations,interaction,course,0,0,0,0,type,0,totaltime,start,end,x,coursemodule),count++,count==contentTypes.length&&resolve(ctRenderer),ctRenderer[x.name].init()}))}))}));annotations.map((x=>(x.prop=JSON.stringify(contentTypes.find((y=>y.name===x.type))),x))),getRenderers.then((()=>{renderAnnotationItems(annotations)})).catch((()=>{}))}))},validateTimestampFormat=(timestamp,fld,existing)=>!!/^([0-9]{2}):([0-5][0-9]):([0-5][0-9])(\.\d{2})?$/.test(timestamp)||(addNotification(M.util.get_string("invalidtimestampformat","mod_interactivevideo"),"danger"),existing?$(fld).val(existing):$(fld).val(convertSecondsToHMS(start,!1,!1)),!1),validateTimeStartEnd=(timestamp,fld,existing,seconds,checkduration,checkexisting,checkskipsegment)=>{const parts=timestamp.split(":");if(timestamp=3600*Number(parts[0])+60*Number(parts[1])+Number(parts[2]),checkduration&&(timestamp>end||timestampx.timestamp==timestamp))&×tamp!=seconds)return addNotification(M.util.get_string("interactionalreadyexists","mod_interactivevideo"),"danger"),existing?$(fld).val(existing):$(fld).val(convertSecondsToHMS(start,!1,!1)),-1;if(checkskipsegment){const skip=annotations.filter((annotation=>"skipsegment"==annotation.type)).find((x=>Number(x.timestamp)Number(timestamp)));if(skip)return addNotification(M.util.get_string("interactionisbetweentheskipsegment","mod_interactivevideo",{start:convertSecondsToHMS(skip.timestamp,!0,!1),end:convertSecondsToHMS(skip.title,!0,!1)}),"danger"),existing?$(fld).val(existing):$(fld).val(convertSecondsToHMS(start,!1,!1)),-1}return timestamp},runInteraction=annotation=>{$("#annotation-modal").modal("hide"),$("#message").not("[data-placement=bottom]").remove(),$("#end-screen").remove(),player.pause();ctRenderer[annotation.type].runInteraction(annotation)},onReady=async()=>{"vimeo"!=player.type&&"html5video"!=player.type&&$("#video-block").addClass("no-pointer"),0==player.support.playbackrate?$("#changerate").remove():$("#changerate").removeClass("d-none");let t=await player.getDuration();end=end?Math.min(end,t):t,start>end&&(start=0),totaltime=end-start;let ratio=16/9;displayoptions.usefixedratio&&0!=displayoptions.usefixedratio||(ratio=player.aspectratio),$("#video-wrapper").css("padding-bottom",1/ratio*100+"%"),playerReady=!0,$("#timeline-wrapper #video-timeline").css({"background-image":"url("+player.posterImage+")","background-size":"contain","background-repeat":"no-repeat"}),$("#timeline-wrapper #duration").text(convertSecondsToHMS(end,!0)),$("#timeline-wrapper #currenttime").text(convertSecondsToHMS(start,!0));const minutes=Math.floor(totaltime/60);$("#timeline-items-wrapper").css("width",300*minutes+"px");const relWidth=$("#timeline-items").width();$("#minute-markers, #minute-markers-bg, #vseek").css("width",relWidth+"px");let startPercentage=0,newStart=start;start%60!=0&&(startPercentage=(60-start%60)/totaltime*100,newStart=start+(60-start%60),$("#minute-markers, #minute-markers-bg").append('
'));for(let i=newStart;i<=end;i+=60){let percentage=(i-newStart)/totaltime*100+startPercentage,marker="";marker=i>=3600?Math.floor(i/3600)+"h"+Math.floor(i%3600/60)+"m":Math.floor(i/60)+"m",$("#minute-markers, #minute-markers-bg").append('
').concat(marker,"
"))}end%60!=0&&$("#minute-markers, #minute-markers-bg").append('
'),getAnnotations()},onEnded=()=>{player.pause(),$("#playpause").find("i").removeClass("bi-pause-fill").addClass("bi-play-fill"),$("#video-wrapper").append('
\n
'),$("#video-nav #progress").css("width","100%"),$("#scrollbar, #scrollhead-top").css("left","100%"),$("#message #restart").focus();const currentAnnotation=annotations.find((annotation=>annotation.timestamp==end));currentAnnotation&&($("#annotation-list tr").removeClass("active"),$('tr[data-id="'.concat(currentAnnotation.id,'"]')).addClass("active"),$('#video-nav .annotation[data-id="'+currentAnnotation.id+'"] .item').tooltip("show"),setTimeout((function(){$('#video-nav .annotation[data-id="'+currentAnnotation.id+'"] .item').tooltip("hide")}),2e3))},onSeek=async function(t){if(!playerReady)return;(t=t?Number(t):await player.getCurrentTime())>start&&t{$("#message, #end-screen").remove(),$("#playpause").find("i").removeClass("bi-play-fill").addClass("bi-pause-fill"),player.audio&&!visualized&&(player.visualizer(),visualized=!0);const intervalFunction=async function(){let thisTime=await player.getCurrentTime();const isPlaying=await player.isPlaying(),isEnded=await player.isEnded();if(!isPlaying||isEnded)return void cancelAnimationFrame(onPlayingInterval);if(thisTime=end)return player.stop(end),cancelAnimationFrame(onPlayingInterval),void onEnded();dispatchEvent("timeupdate",{time:thisTime}),$("#timeline-wrapper #currenttime").text(convertSecondsToHMS(thisTime,!0));let percentage=(thisTime-start)/totaltime*100;$("#video-nav #progress").css("width",percentage+"%"),$("#scrollbar, #scrollhead-top").css("left",percentage+"%");const scrollBar=document.getElementById("scrollbar"),rect=scrollBar.getBoundingClientRect();(rect.left<0||rect.right>window.innerWidth)&&scrollBar.scrollIntoView({behavior:"instant",block:"center",inline:"center"});const currentAnnotation=annotations.find((x=>thisTime-player.frequency<=x.timestamp&&thisTime+player.frequency>=x.timestamp));currentAnnotation&&($("#annotation-list tr").removeClass("active"),$('tr[data-id="'.concat(currentAnnotation.id,'"]')).addClass("active"),$('#video-nav .annotation[data-id="'+currentAnnotation.id+'"] .item').tooltip("show"),setTimeout((function(){$('#video-nav .annotation[data-id="'+currentAnnotation.id+'"] .item').tooltip("hide")}),2e3));let skip=annotations.filter((annotation=>"skipsegment"==annotation.type)).find((x=>Number(x.timestamp)Number(thisTime)));skip&&(await player.seek(Number(skip.title)),percentage=(skip.title-start)/totaltime*100,replaceProgressBars(percentage))};if("yt"==player.type||"wistia"==player.type){const animate=async()=>{intervalFunction(),onPlayingInterval=requestAnimationFrame(animate)};onPlayingInterval=requestAnimationFrame(animate)}else intervalFunction()},onPause=()=>{cancelAnimationFrame(onPlayingInterval),$("#playpause").find("i").removeClass("bi-pause-fill").addClass("bi-play-fill")};require(["mod_interactivevideo/player/"+type],(function(VideoPlayer){player=new VideoPlayer(url,start,end,!1,!0)})),$(document).on("iv:playerReady",(function(){onReady()})),$(document).on("iv:playerPaused",(function(){onPause()})),$(document).on("iv:playerPlaying",(function(){onPlaying()})),$(document).on("iv:playerEnded",(function(){onEnded()})),$(document).on("iv:playerSeek",(function(e){onSeek(e.detail.time)})),$(document).on("annotationupdated",(function(e){const action=e.originalEvent.detail.action;if("import"==action)return annotations=e.originalEvent.detail.annotations,renderAnnotationItems(annotations),void addNotification(M.util.get_string("interactionimported","mod_interactivevideo"),"success");let updated=e.originalEvent.detail.annotation;"edit"!=action&&"draft"!=action&&"savedraft"!=action||(annotations=annotations.filter((function(item){return item.id!=updated.id}))),updated.prop=JSON.stringify(contentTypes.find((x=>x.name===updated.type))),annotations.push(updated),activeid="add"==action?updated.id:null,renderAnnotationItems(annotations),"add"==action||"clone"==action?(addNotification(M.util.get_string("interactionadded","mod_interactivevideo"),"success"),$('tr[data-id="'.concat(updated.id,'"]')).addClass("active")):"edit"==action&&(addNotification(M.util.get_string("interactionupdated","mod_interactivevideo"),"success"),$('tr[data-id="'.concat(updated.id,'"]')).addClass("active"),setTimeout((function(){$('tr[data-id="'.concat(updated.id,'"]')).removeClass("active")}),1500)),annotations.find((x=>"draft"==x.status))?$("#timeline-wrapper #savedraft").removeAttr("disabled").addClass("pulse"):$("#timeline-wrapper #savedraft").attr("disabled","disabled").removeClass("pulse")})),$(document).on("annotationdeleted",(function(e){const annotation=e.originalEvent.detail.annotation;activeid=null,$('tr[data-id="'.concat(annotation.id,'"]')).addClass("deleted"),setTimeout((function(){annotations=annotations.filter((function(item){return item.id!=annotation.id})),renderAnnotationItems(annotations),addNotification(M.util.get_string("interactiondeleted","mod_interactivevideo"),"success")}),1e3)})),$(document).on("click","#addcontentdropdown .dropdown-item",(async function(e){if(!playerReady)return;e.preventDefault(),$("#addcontentdropdown .dropdown-item").removeClass("active");const ctype=$(this).data("type");player.pause();let timestamp=currentTime||await player.getCurrentTime();timestamp=Number(timestamp.toFixed(2));const contenttype=contentTypes.find((x=>x.name==ctype));if(contenttype.hastimestamp){if(annotations.find((x=>x.timestamp==timestamp)))return void addNotification(M.util.get_string("interactionalreadyexists","mod_interactivevideo"),"danger");if(annotations.filter((x=>"skipsegment"==x.type)).find((x=>Number(x.timestamp)Number(currentTime))))return void addNotification(M.util.get_string("interactionisbetweentheskipsegment","mod_interactivevideo"),"danger")}contenttype.allowmultiple||!annotations.find((x=>x.type==ctype))?(currentTime=null,ctRenderer[ctype].addAnnotation(annotations,timestamp,coursemodule)):addNotification(M.util.get_string("interactionalreadyexists","mod_interactivevideo"),"danger")})),$(document).on("click","tr.annotation .edit",(async function(e){e.preventDefault();const timestamp=$(this).closest(".annotation").data("timestamp");timestamp&&await player.seek(timestamp,!0),player.pause();const id=$(this).closest(".annotation").data("id"),contenttype=$(this).closest(".annotation").data("type");ctRenderer[contenttype].editAnnotation(annotations,id,coursemodule)})),$(document).on("click","tr.annotation .copy",(async function(e){e.preventDefault();const id=$(this).closest(".annotation").data("id"),contenttype=$(this).closest(".annotation").data("type"),time=await player.getCurrentTime();ctRenderer[contenttype].cloneAnnotation(id,time)})),$(document).on("click","tr.annotation .delete",(function(e){e.preventDefault(),player.pause();const id=$(this).closest(".annotation").data("id"),annotation=annotations.find((annotation=>annotation.id==id));try{Notification.deleteCancelPromise(M.util.get_string("deleteinteraction","mod_interactivevideo"),M.util.get_string("deleteinteractionconfirm","mod_interactivevideo"),M.util.get_string("delete","mod_interactivevideo")).then((()=>ctRenderer[annotation.type].deleteAnnotation(annotations,id))).catch((()=>{}))}catch{Notification.saveCancel(M.util.get_string("deleteinteraction","mod_interactivevideo"),M.util.get_string("deleteinteractionconfirm","mod_interactivevideo"),M.util.get_string("delete","mod_interactivevideo"),(function(){return ctRenderer[annotation.type].deleteAnnotation(annotations,id)}))}})),$(document).on("click","tr.annotation .title",(async function(e){e.preventDefault();const timestamp=$(this).closest(".annotation").data("timestamp");replaceProgressBars((timestamp-start)/totaltime*100),await player.seek(timestamp,!0),player.pause();const id=$(this).closest(".annotation").data("id"),theAnnotation=annotations.find((annotation=>annotation.id==id));setTimeout((()=>{runInteraction(theAnnotation)}),500)})),$(document).on("click","tr.annotation .timestamp",(async function(e){e.preventDefault();const timestamp=$(this).data("timestamp");await player.seek(timestamp),player.play()})),$(document).on("contextmenu","#vseek, #video-timeline",(async function(e){if(!playerReady)return;e.preventDefault(),e.stopImmediatePropagation();const percentage=e.offsetX/$(this).width();replaceProgressBars(100*percentage),currentTime=percentage*totaltime+start,await player.seek(currentTime),player.pause(),$("#addcontent").trigger("click")})),$(document).on("contextmenu","#scrollbar, #scrollhead-top",(async function(e){playerReady&&(e.preventDefault(),e.stopImmediatePropagation(),currentTime=await player.getCurrentTime(),$("#addcontent").trigger("click"))})),$(document).on("click","#playpause",(async function(e){if(!playerReady)return;if(e.preventDefault(),await player.isPlaying())player.pause();else{await player.getCurrentTime()>=end?$("#end-screen #restart").trigger("click"):player.play()}})),$(document).on("click","#video-block",(function(e){playerReady&&(e.preventDefault(),$("#playpause").trigger("click"))})),$(document).on("contextmenu","#video-nav .annotation",(function(e){e.preventDefault(),e.stopImmediatePropagation();const id=$(this).data("id");$('tr.annotation[data-id="'.concat(id,'"] .edit')).trigger("click")})),$(document).on("contextmenu","[data-editable]",(function(e){if(e.preventDefault(),e.stopImmediatePropagation(),$("[data-field].editing").length>0)return;const fld=$(this).data("editable");$(this).hide(),$(this).siblings('[data-field="'+fld+'"]').removeClass("d-none").focus().addClass("editing")})),$(document).on("keyup","[data-field].editing",(function(e){$(this).removeClass("is-invalid");const initialValue=$(this).data("initial-value"),val=$(this).val(),fld=$(this).data("field");if(""==val&&$(this).addClass("is-invalid"),"Escape"==e.key)return $(this).val(initialValue),$(this).removeClass("editing"),$(this).addClass("d-none"),void $(this).siblings("[data-editable]").show();if("Enter"!=e.key);else{let seconds;if("timestamp"==fld){const parts=initialValue.split(":");if(seconds=3600*Number(parts[0])+60*Number(parts[1])+Number(parts[2]),!validateTimestampFormat(val,"[data-field].editing",initialValue))return void $(this).addClass("is-invalid");const timestamp=validateTimeStartEnd(val,"[data-field].editing",initialValue,seconds,!0,!0,!0);if(-1==timestamp)return void $(this).addClass("is-invalid");seconds=timestamp}if($(this).hasClass("is-invalid"))return;if(val==initialValue)return $(this).removeClass("editing"),$(this).addClass("d-none"),void $(this).siblings("[data-editable]").show();const id=$(this).data("id");$.ajax({url:M.cfg.wwwroot+"/mod/interactivevideo/ajax.php",method:"POST",dataType:"text",data:{action:"quickeditfield",sesskey:M.cfg.sesskey,id:id,field:fld,contextid:M.cfg.contextid,value:"timestamp"==fld?seconds:val},success:function(data){const updated=JSON.parse(data);dispatchEvent("annotationupdated",{annotation:updated,action:"edit"})}})}})),$(document).on("blur","[data-field].editing",(function(){const initialValue=$(this).data("initial-value");$(this).val(initialValue),$(this).removeClass("editing"),$(this).addClass("d-none"),$(this).siblings("[data-editable]").show()})),$(document).on("click","#end-screen #restart",(async function(e){e.preventDefault(),$("#end-screen").remove(),await player.seek(start),player.play()})),$(document).on("mouseover","tr.annotation",(function(){const id=$(this).data("id");$('#video-nav ul li[data-id="'.concat(id,'"] .item')).trigger("mouseover")})),$(document).on("mouseout","tr.annotation",(function(){const id=$(this).data("id");$('#video-nav ul li[data-id="'.concat(id,'"] .item')).trigger("mouseout"),$(".tooltip").remove()})),$(document).on("mouseover","#video-nav ul li",(function(){const id=$(this).data("id");$('tr.annotation[data-id="'.concat(id,'"]')).addClass("active")})),$(document).on("mouseout","#video-nav ul li",(function(){const id=$(this).data("id");$('tr.annotation[data-id="'.concat(id,'"]')).removeClass("active")})),$(document).on("change",".timestamp-input, .timestamp-field input",(function(){$(this).removeClass("is-invalid");const parts=$(this).val().split(":"),seconds=3600*Number(parts[0])+60*Number(parts[1])+Number(parts[2]);if(!validateTimestampFormat($(this).val(),this))return void $(this).addClass("is-invalid");-1!=validateTimeStartEnd($(this).val(),this,"00:00:00",seconds,!0,!1,!0)||$(this).addClass("is-invalid")}));const appendTimestampMarker=(seconds,rounded)=>{const formattedTime=convertSecondsToHMS(seconds,!0,rounded);$("#vseek #bar").append('
\n
'.concat(formattedTime,"
"))};$(document).on("annotationitemsrendered",(function(){$('#timeline-wrapper [data-toggle="tooltip"]').tooltip({boundary:"window",container:"#timeline"});let targetAnnotation=null;try{$("#timeline-items .annotation, #video-timeline-wrapper .skipsegment").draggable("destroy"),$("#timeline-items .annotation, #video-timeline-wrapper .skipsegment").resizable("destroy")}catch(e){}$("#timeline-items .annotation.li-draggable").draggable({axis:"x",start:function(){appendTimestampMarker($(this).data("timestamp")),$(".tooltip, #message").remove(),$("#timeline-items").addClass("no-pointer-events")},drag:async function(event,ui){$(".tooltip").remove();let timestamp=(ui.position.left+5)/$("#timeline-items").width()*totaltime+start;timestampend&&(timestamp=end,ui.position.left=$("#timeline-items").width()-5),$("#scrollbar, #position-marker, #scrollhead-top").css("left",(timestamp-start)/totaltime*100+"%"),$(this).css("left",(timestamp-start)/totaltime*100+"%"),await player.seek(timestamp),player.pause(),$("#vseek #position").text(convertSecondsToHMS(timestamp,!0,!1))},stop:async function(event,ui){$(".tooltip").remove(),$("#vseek #position-marker").remove(),setTimeout((function(){$("#timeline-items").removeClass("no-pointer-events")}),200);let timestamp=(ui.position.left+5)/$("#timeline-items").width()*totaltime+start;timestampend&&(timestamp=end,$(this).css("left","calc(100% - 5px)")),$("#scrollbar, #position-marker, #scrollhead-top").css("left",(timestamp-start)/totaltime*100+"%");const id=$(this).data("id");targetAnnotation=annotations.find((x=>x.id==id));if(annotations.find((x=>x.timestamp==timestamp&&x.id!=id)))return addNotification(M.util.get_string("interactionalreadyexists","mod_interactivevideo"),"danger"),void renderAnnotationItems(annotations);targetAnnotation.timestamp!=timestamp&&(targetAnnotation.timestamp=timestamp,targetAnnotation.status="draft",dispatchEvent("annotationupdated",{annotation:targetAnnotation,action:"draft"}),await player.seek(timestamp),player.pause(),$("#scrollbar, #position-marker, #scrollhead-top").css("left",(timestamp-start)/totaltime*100+"%"))}}),$("#video-timeline-wrapper .skipsegment").draggable({axis:"x",start:function(){$("#message").remove(),appendTimestampMarker($(this).data("timestamp")),$("#timeline-items").addClass("no-pointer-events")},drag:async function(event,ui){const id=$(this).data("id");targetAnnotation=annotations.find((x=>x.id==id));let timestamp=ui.position.left/$("#video-timeline").width()*totaltime+start;timestampend&&(timestamp=end),$("#scrollbar, #position-marker, #scrollhead-top").css("left",(timestamp-start)/totaltime*100+"%"),await player.seek(timestamp),player.pause(),$("#vseek #position").text(convertSecondsToHMS(timestamp,!0,!1))},stop:async function(event,ui){$("#vseek #position-marker").remove(),setTimeout((function(){$("#timeline-items").removeClass("no-pointer-events")}),200);let timestamp=ui.position.left/$("#video-timeline").width()*totaltime+start;const id=$(this).data("id");targetAnnotation=annotations.find((x=>x.id==id));let skipduration=Number(targetAnnotation.title)-Number(targetAnnotation.timestamp);if(timestamp<0&×tamp+skipdurationend)return void renderAnnotationItems(annotations);if(timestampend&&(skipduration=Math.abs(end-timestamp),timestamp=end-skipduration),skipduration<=0)return void renderAnnotationItems(annotations);if(annotations.find((x=>x.timestamp==timestamp&&x.id!=id)))return addNotification(M.util.get_string("interactionalreadyexists","mod_interactivevideo"),"danger"),void renderAnnotationItems(annotations);targetAnnotation.timestamp!=timestamp?(targetAnnotation.timestamp=timestamp,targetAnnotation.title=timestamp+skipduration,targetAnnotation.title>end&&(targetAnnotation.title=end),targetAnnotation.status="draft",dispatchEvent("annotationupdated",{annotation:targetAnnotation,action:"draft"}),await player.seek(timestamp),player.pause(),$("#scrollbar, #position-marker, #scrollhead-top").css("left",(timestamp-start)/totaltime*100+"%")):renderAnnotationItems(annotations)}}),$("#video-timeline-wrapper .skipsegment").resizable({containment:"#video-timeline-wrapper",handles:"e, w",start:function(){$("#message").remove(),appendTimestampMarker($(this).data("timestamp")),$("#timeline-items").addClass("no-pointer-events")},resize:async function(event,ui){let timestamp;ui.originalPosition.left!=ui.position.left||ui.originalSize.width==ui.size.width?(ui.position.left<0&&(ui.position.left=0),timestamp=ui.position.left/$("#video-timeline").width()*totaltime+start):timestamp=(ui.position.left+ui.size.width)/$("#video-timeline").width()*totaltime+start,$("#scrollbar, #position-marker, #scrollhead-top").css("left",(timestamp-start)/totaltime*100+"%"),await player.seek(timestamp),player.pause(),$("#vseek #position").text(convertSecondsToHMS(timestamp,!0,!1))},stop:async function(event,ui){$("#vseek #position-marker").remove(),setTimeout((function(){$("#timeline-items").removeClass("no-pointer-events")}),200);const id=$(this).data("id");let timestamp,direction;targetAnnotation=annotations.find((x=>x.id==id)),ui.originalPosition.left!=ui.position.left?(ui.position.left<0&&(ui.position.left=0),timestamp=ui.position.left/$("#video-timeline").width()*totaltime+start,direction="left"):(timestamp=(ui.position.left+ui.size.width)/$("#video-timeline").width()*totaltime+start,direction="right");if(annotations.find((x=>x.timestamp==timestamp&&x.id!=id)))return addNotification(M.util.get_string("interactionalreadyexists","mod_interactivevideo"),"danger"),void renderAnnotationItems(annotations);targetAnnotation.timestamp!=timestamp&&("left"==direction?targetAnnotation.timestamp=timestamp:(targetAnnotation.title=timestamp,targetAnnotation.title>end&&(targetAnnotation.title=end)),targetAnnotation.status="draft",dispatchEvent("annotationupdated",{annotation:targetAnnotation,action:"draft"}),await player.seek(timestamp),player.pause(),$("#scrollbar, #position-marker, #scrollhead-top").css("left",(timestamp-start)/totaltime*100+"%"))}}),$("#video-timeline-wrapper .skipsegment").off("contextmenu").on("contextmenu",(function(e){e.preventDefault(),e.stopImmediatePropagation();const id=$(this).data("id");$('tr.annotation[data-id="'.concat(id,'"] .edit')).trigger("click")})),$("#video-timeline-wrapper .skipsegment").off("click").on("click",(async function(e){e.preventDefault();const timestamp=$(this).data("timestamp");await player.seek(timestamp),player.pause()})),$("#video-timeline-wrapper .skipsegment .delete-skipsegment").off("click").on("click",(function(e){e.preventDefault();const id=$(this).closest(".skipsegment").data("id");$('tr.annotation[data-id="'.concat(id,'"] .delete')).trigger("click")}))})),$("#scrollbar").draggable({containment:"#timeline-items",axis:"x",cursor:"col-resize",start:function(event,ui){$("#timeline-items").addClass("no-pointer-events"),$("#message").remove(),appendTimestampMarker(ui.position.left/$("#timeline-items").width()*totaltime+start,!1)},drag:async function(event,ui){let timestamp=ui.position.left/$("#timeline-items").width()*totaltime+start,percentage=(timestamp-start)/totaltime*100;$("#vseek #position").text(convertSecondsToHMS(timestamp,!0,!1)),$("#vseek #position-marker, #scrollhead-top, #scrollbar").css("left",percentage+"%"),await player.seek(timestamp),player.pause()},stop:function(event,ui){$("#vseek #position-marker").remove(),setTimeout((function(){$("#timeline-items").removeClass("no-pointer-events")}),200);let timestamp=ui.position.left/$("#timeline-items").width()*totaltime+start;$("#scrollbar, #scrollhead-top").css("left",(timestamp-start)/totaltime*100+"%")}}),$("#scrollhead-top").draggable({axis:"x",cursor:"col-resize",start:function(event,ui){$("#vseek").addClass("no-pointer-events"),$("#message").remove(),appendTimestampMarker(ui.position.left/$("#vseek").width()*totaltime+start,!1)},drag:async function(event,ui){let timestamp=ui.position.left/$("#vseek").width()*totaltime+start,percentage=(timestamp-start)/totaltime*100;timestamp992&&($("#separator").css("left",playerWidth+"px"),$("#player-region").css("width",playerWidth+"px"),$("#content-region").css("width","calc(100% - "+playerWidth+"px)"));const timelineHeight=localStorage.getItem("timeline-height");timelineHeight&&($("#timeline-wrapper").css("height",timelineHeight+"px"),$("#top-region").css("height","calc(100dvh - ".concat(Number(timelineHeight)+70,"px)"))),$("#vseek #bar, #video-timeline, #video-nav .annotation").on("mouseenter",(function(e){$("#cursorbar, #position-marker").remove(),e.preventDefault(),e.stopImmediatePropagation();let $scrollbar=$("#scrollbar").clone();$scrollbar.attr("id","cursorbar");const parentOffset=$(this).offset(),relX=e.pageX-parentOffset.left;$scrollbar.css("left",relX+5+"px"),$scrollbar.find("#scrollhead").remove();const percentage=relX/$(this).width(),formattedTime=convertSecondsToHMS(percentage*totaltime+start,!0,!1);$("#vseek #bar").append('
\n
'.concat(formattedTime,"
")),$("#vseek #position-marker").css("left",relX+"px"),$("#timeline-items").append($scrollbar)})),$("#vseek #bar, #video-timeline, #video-nav .annotation").on("mouseleave",(function(e){e.stopImmediatePropagation(),$("#vseek #position-marker, #cursorbar").remove()})),$("#vseek #bar, #video-timeline").on("mousemove",(function(e){e.stopImmediatePropagation();const parentOffset=$(this).offset(),relX=e.pageX-parentOffset.left;let time=relX/$(this).width()*totaltime+start;timex.id==id));await player.seek(annotation.timestamp),runInteraction(annotation)})),$(document).on("click","#vseek #bar, #video-timeline",(async function(e){e.preventDefault(),e.stopImmediatePropagation();const percentage=e.offsetX/$(this).width();replaceProgressBars(100*percentage),await player.seek(percentage*totaltime+start),player.pause(),$("#message, #end-screen").remove()})),$("#zoomout").on("click",(function(){const currentLevel=$("#timeline-items-wrapper").css("width"),newLevel=parseInt(currentLevel)-300;$("#timeline-items-wrapper").css("width",newLevel+"px");const relWidth=$("#timeline-items").width();$("#minute-markers, #minute-markers-bg, #vseek").css("width",relWidth+"px");let timelineElement=document.getElementById("timeline");timelineElement.scrollWidth<=timelineElement.clientWidth&&$(this).attr("disabled","disabled"),dispatchEvent("annotationitemsrendered",{annotations:annotations})})),$("#zoomin").on("click",(function(){const currentLevel=$("#timeline-items-wrapper").css("width"),newLevel=parseInt(currentLevel)+300;$("#timeline-items-wrapper").css("width",newLevel+"px");const relWidth=$("#timeline-items").width();$("#minute-markers, #minute-markers-bg, #vseek").css("width",relWidth+"px"),$("#zoomout").removeAttr("disabled"),dispatchEvent("annotationitemsrendered",{annotations:annotations})})),$("#timeline").on("wheel",(function(e){(e.ctrlKey||e.metaKey)&&(e.preventDefault(),e.originalEvent.deltaY<0?$("#zoomin").trigger("click"):$("#zoomout").trigger("click"))})),document.getElementById("timeline").addEventListener("scroll",(function(){document.getElementById("minute-markers-wrapper").scrollLeft=this.scrollLeft,document.getElementById("vseek").style.left=-this.scrollLeft+"px",document.getElementById("minute-markers-bg-wrapper").style.left=-this.scrollLeft+"px",document.getElementById("scrollbar").scrollHeight=this.scrollHeight})),$("#savedraft").on("click",(function(e){e.stopImmediatePropagation();let draftAnnotations=annotations.filter((x=>"draft"==x.status)),count=0;draftAnnotations.forEach((function(a){$.ajax({url:M.cfg.wwwroot+"/mod/interactivevideo/ajax.php",method:"POST",dataType:"text",data:{action:"quickeditfield",sesskey:M.cfg.sesskey,id:a.id,field:"timestamp",contextid:M.cfg.contextid,value:a.timestamp},success:function(data){const updated=JSON.parse(data);dispatchEvent("annotationupdated",{annotation:updated,action:"savedraft"})}}),"skipsegment"==a.type&&$.ajax({url:M.cfg.wwwroot+"/mod/interactivevideo/ajax.php",method:"POST",dataType:"text",data:{action:"quickeditfield",sesskey:M.cfg.sesskey,id:a.id,field:"title",contextid:M.cfg.contextid,value:a.title},success:function(data){const updated=JSON.parse(data);dispatchEvent("annotationupdated",{annotation:updated,action:"savedraft"})}}),count++,count==draftAnnotations.length&&addNotification(M.util.get_string("draftsaved","mod_interactivevideo"),"success")}))})),$("#addcontent").on("click",(async function(e){e.preventDefault(),playerReady&&$("#contentmodal").modal("show")})),$("#contentmodal").on("show.bs.modal",(function(){player.pause(),$("#addcontentdropdown").addClass("modal-body")})),$("#contentmodal").on("hide.bs.modal",(function(){$("#addcontentdropdown .dropdown-item").removeClass("active"),$("#addcontentdropdown").removeClass("modal-body")})),window.addEventListener("beforeunload",(e=>{if(annotations.find((x=>"draft"==x.status))){const confirmationMessage=M.util.get_string("unsavedchanges","mod_interactivevideo");return e.returnValue=confirmationMessage,confirmationMessage}return!0})),$(document).on("click",".changerate",(function(e){e.preventDefault();const rate=$(this).data("rate");player.setRate(rate),$(".changerate").find("i").removeClass("bi-check"),$(this).find("i").addClass("bi-check")})),$(document).on("iv:playerRateChange",(function(e){$(".changerate").find("i").removeClass("bi-check"),$('.changerate[data-rate="'.concat(e.originalEvent.detail.rate,'"]')).find("i").addClass("bi-check")}));let timelineWrapper=document.getElementById("timeline-wrapper"),resizeObserver=new ResizeObserver((()=>{const relWidth=$("#timeline-items").width();$("#minute-markers, #minute-markers-bg, #vseek").css("width",relWidth+"px")}));resizeObserver.observe(timelineWrapper),$(document).on("click","#importcontent",(function(e){e.preventDefault();const importmodal='");$("body").append(importmodal),$("#importmodal").modal("show"),$("#importmodal").on("hidden.bs.modal",(function(){$("#importmodal").remove()})),$("#importmodal").off("shown.bs.modal").on("shown.bs.modal",(function(){$("#importmodal .modal-dialog").draggable({handle:".modal-header"}),$.ajax({url:M.cfg.wwwroot+"/mod/interactivevideo/ajax.php",method:"POST",dataType:"text",data:{action:"get_taught_courses",sesskey:M.cfg.sesskey,contextid:M.cfg.contextid,userid:userid},success:function(data){let courses=JSON.parse(data);courses.sort(((a,b)=>b.fullname.localeCompare(a.fullname)));let courseSelect='";let selectfield='
\n \n ").concat(courseSelect,"
");$("#importmodal .modal-body").append(selectfield),$("#importmodal #importcourse").val(course),$("#importmodal #importcourse").trigger("change")}})}))})),$(document).on("change","#importmodal #importcourse",(function(){$("#importmodal .selectcm, #importmodal .select-interaction").remove(),$.ajax({url:M.cfg.wwwroot+"/mod/interactivevideo/ajax.php",method:"POST",dataType:"text",data:{action:"get_cm_by_courseid",sesskey:M.cfg.sesskey,contextid:M.cfg.contextid,courseid:$(this).val()},success:function(data){let cms=JSON.parse(data);cms.sort(((a,b)=>b.name.localeCompare(a.name)));let cmSelect='";let selectfield='
\n \n ").concat(cmSelect,"
");$("#importmodal .selectcourse").after(selectfield)}})})),$(document).on("change","#importmodal #importcm",(async function(){$("#importmodal .select-interaction").remove(),$("#importmodal #importcm").after('
\n
'));let interactions=await $.ajax({url:M.cfg.wwwroot+"/mod/interactivevideo/ajax.php",method:"POST",dataType:"text",data:{action:"get_items",sesskey:M.cfg.sesskey,id:$(this).val(),contextid:M.cfg.contextid,coursecontextid:M.cfg.courseContextId}});interactions=JSON.parse(interactions),interactions=interactions.filter((x=>"skipsegment"!=x.type)),0!=interactions.length?($("#importmodal .select-interaction").append('
\n \n \n \n
')),interactions=interactions.map((int=>{const ctype=contentTypes.find((y=>y.name===int.type));return int.prop=JSON.stringify(ctype),int.icon=ctype.icon,(int.timestamp>end||int.timestamp0?int.outside=!0:int.outside=!1,!ctype.allowmultiple&&annotations.find((x=>x.type==int.type))&&(int.disabled=!0),int})),interactions.sort(((a,b)=>a.timestamp-b.timestamp)),interactions.forEach((int=>{const inputgroup='
\n
\n \n
\n \n \n
');$("#importmodal .select-interaction").append(inputgroup)})),$(document).off("click","#importmodal #importcontentbutton").on("click","#importmodal #importcontentbutton",(async function(e){e.preventDefault();let $selected=$('#importmodal .select-interaction input[type="checkbox"]:checked'),selectedInt=[];if($selected.each((function(){let $row=$(this).closest(".input-group");const name=$row.find(".name").val();if(""==name.trim())return;let timestamp=$row.find(".timestamp-input").val();if(""==timestamp)return;if(Number(timestamp)<0)timestamp=Number(timestamp);else{const parts=timestamp.split(":");if(timestamp=3600*Number(parts[0])+60*Number(parts[1])+Number(parts[2]),annotations.find((x=>x.timestamp==timestamp)))return}let id=$row.data("id"),int=interactions.find((x=>x.id==id));int.title=name,int.timestamp=timestamp;let xp=Number($row.find(".xp").val());(isNaN(xp)||""==xp)&&(xp=0),int.xp=xp,selectedInt.push(int)})),0!=selectedInt.length){let interactions=await $.ajax({url:M.cfg.wwwroot+"/mod/interactivevideo/ajax.php",method:"POST",dataType:"text",data:{action:"import_annotations",sesskey:M.cfg.sesskey,contextid:M.cfg.contextid,annotations:JSON.stringify(selectedInt),tocourse:M.cfg.courseId,fromcourse:$("#importcourse").val(),tocm:interaction,fromcm:$("#importcm").val(),module:coursemodule}});interactions=JSON.parse(interactions),$("#importmodal").modal("hide"),annotations=annotations.concat(interactions),dispatchEvent("annotationupdated",{annotations:annotations,action:"import"}),interactions.forEach((int=>{int.allowmultiple||ctRenderer[int.type].init()}))}else addNotification(M.util.get_string("selectinteraction","mod_interactivevideo"),"danger")}))):$("#importmodal .select-interaction").append('
\n '.concat(M.util.get_string("nocontent","mod_interactivevideo"),"
"))}))}}})); //# sourceMappingURL=editannotation.min.js.map \ No newline at end of file diff --git a/amd/build/editannotation.min.js.map b/amd/build/editannotation.min.js.map index 88b6e7d..b2b15b3 100644 --- a/amd/build/editannotation.min.js.map +++ b/amd/build/editannotation.min.js.map @@ -1 +1 @@ -{"version":3,"file":"editannotation.min.js","sources":["../src/editannotation.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Edit interactions module\n *\n * @module mod_interactivevideo/editannotation\n * @copyright 2024 Sokunthearith Makara \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(['jquery',\n 'core/toast',\n 'core/notification',\n 'core/event_dispatcher',\n 'mod_interactivevideo/libraries/jquery-ui',\n], function($, addToast, Notification, {dispatchEvent}) {\n let ctRenderer = {};\n let player;\n let totaltime;\n let currentTime;\n let playerReady = false;\n /**\n * Replace the progress bar on the video navigation.\n * @param {Number} percentage - Percentage to replace the progress bar.\n * @returns {void}\n * */\n const replaceProgressBars = (percentage) => {\n percentage = percentage > 100 ? 100 : percentage;\n $('#video-nav #progress').css('width', percentage + '%');\n $('#scrollbar, #scrollhead-top').css('left', percentage + '%');\n };\n /**\n * Render the annotations on the video navigation.\n * @param {Array} annos - Annotations to render.\n * @param {Number} start - Start time of the video.\n * @param {Number} totaltime - Total time of the video.\n * @returns {void}\n * */\n const renderVideoNav = async function(annos, start, totaltime) {\n if (annos.length == 0) {\n $(\"#video-nav ul\").empty();\n return;\n }\n\n $(\"#video-nav ul\").empty();\n $(\"#video-timeline-wrapper .skipsegment\").remove();\n annos.forEach(async (x) => {\n const render = ctRenderer[x.type];\n await render.renderItemOnVideoNavigation(x);\n });\n\n const time = await player.getCurrentTime();\n // Replace progress bar.\n const percentage = (time - start) / totaltime * 100;\n replaceProgressBars(percentage);\n dispatchEvent('annotationitemsrendered', {'annotations': annos});\n };\n\n return {\n /**\n * Initialize function on page loads.\n * @param {String} url video url\n * @param {Number} coursemodule cm id\n * @param {Number} interaction cm instance\n * @param {Number} course course id\n * @param {Number} start video start time\n * @param {Number} end video end time\n * @param {Number} coursecontextid course context id\n * @param {String} type video type\n * @param {Object} displayoptions display options\n * @param {Number} userid user id\n */\n init: function(url, coursemodule, interaction, course, start, end, coursecontextid, type = 'yt', displayoptions, userid) {\n\n /**\n * Util function to display notification\n * @param {String} msg message text\n * @param {String} type message type\n */\n const addNotification = (msg, type = \"info\") => {\n addToast.add(msg, {\n type: type\n });\n };\n\n start = Number(start);\n if (isNaN(start)) {\n start = 0;\n }\n\n end = Number(end);\n if (isNaN(end)) {\n end = null;\n }\n\n let annotations = []; // Annotations.\n let contentTypes; // Content types.\n\n /**\n * Convert seconds to HH:MM:SS format\n * @param {Number} s second\n * @param {Boolean} dynamic if true, only show minutes and seconds if less than one hour\n * @param {Boolean} rounded if true, second is rounded\n * @returns formatted timestamp\n */\n const convertSecondsToHMS = (s, dynamic = false, rounded = true) => {\n if (rounded) {\n s = Math.round(s);\n }\n let hours = Math.floor(s / 3600);\n let minutes = Math.floor((s - hours * 3600) / 60);\n let seconds = s - hours * 3600 - minutes * 60;\n if (rounded && seconds > 59.5) {\n seconds = 0;\n minutes++;\n if (minutes > 59) {\n minutes = 0;\n hours++;\n }\n }\n if (minutes < 10) {\n minutes = '0' + minutes;\n }\n\n if (rounded) {\n seconds = Math.round(seconds);\n } else {\n seconds = parseFloat(seconds).toFixed(2);\n }\n\n if (seconds < 10) {\n seconds = '0' + seconds;\n }\n\n if (dynamic && hours == 0) {\n return minutes + ':' + seconds;\n }\n\n return (hours < 10 ? '0' + hours : hours) + ':' + minutes + ':' + seconds;\n };\n\n let activeid = null; // Current active annotation id. Mainly used when editing to relaunch the interaction afte editing.\n\n /**\n * Handle rendering of annotation items on the list\n * @param {Array} annotations array of annotation objects\n * @returns\n */\n const renderAnnotationItems = (annotations) => {\n renderVideoNav(annotations, start, totaltime);\n $('#annotationwrapper .loader').remove();\n $('#annotation-list').empty().removeClass(\"d-flex align-items-center justify-content-center\");\n if (annotations.length == 0) {\n $('#annotation-list').html(`${M.util.get_string('clickaddtoaddinteraction', 'mod_interactivevideo')}`)\n .addClass(\"d-flex align-items-center justify-content-center\");\n return;\n }\n annotations.sort(function(a, b) {\n return Number(a.timestamp) - Number(b.timestamp);\n });\n\n annotations.forEach(function(item) {\n let listItem = $('#annotation-template').clone();\n ctRenderer[item.type].renderEditItem(annotations, listItem, item);\n });\n\n let xp = annotations.filter(x => x.xp).map(x => Number(x.xp)).reduce((a, b) => a + b, 0);\n $(\"#xp span\").text(xp);\n\n if (activeid) {\n const activeAnno = annotations.find(x => x.id == activeid);\n if (activeAnno) {\n ctRenderer[activeAnno.type].postEditCallback(activeAnno);\n }\n }\n };\n\n /**\n * Get annotations from the server and execute the rendering function\n * @returns\n */\n const getAnnotations = () => {\n const getItems = $.ajax({\n url: M.cfg.wwwroot + '/mod/interactivevideo/ajax.php',\n method: \"POST\",\n dataType: \"text\",\n data: {\n action: 'get_items',\n sesskey: M.cfg.sesskey,\n id: interaction,\n contextid: M.cfg.contextid,\n coursecontextid: M.cfg.courseContextId\n }\n });\n\n const getContentTypes = $.ajax({\n url: M.cfg.wwwroot + '/mod/interactivevideo/ajax.php',\n method: \"POST\",\n dataType: \"text\",\n data: {\n action: 'getallcontenttypes',\n sesskey: M.cfg.sesskey,\n contextid: M.cfg.contextid,\n coursecontextid: M.cfg.courseContextId\n }\n });\n\n $.when(getItems, getContentTypes).done(function(items, contenttypes) {\n annotations = JSON.parse(items[0]);\n contentTypes = JSON.parse(contenttypes[0]);\n // Remove all annotations that are not in the enabled content types.\n annotations = annotations.filter(x => contentTypes.find(y => y.name === x.type));\n const getRenderers = new Promise((resolve) => {\n let count = 0;\n contentTypes.forEach(x => {\n require(['' + x.amdmodule], function(Type) {\n ctRenderer[x.name] = new Type(player, annotations, interaction,\n course, 0, 0, 0, 0, type, 0, totaltime, start, end, x, coursemodule);\n count++;\n if (count == contentTypes.length) {\n resolve(ctRenderer);\n }\n ctRenderer[x.name].init();\n });\n });\n });\n annotations.map(x => {\n x.prop = JSON.stringify(contentTypes.find(y => y.name === x.type));\n return x;\n });\n getRenderers.then(() => {\n renderAnnotationItems(annotations);\n return;\n }).catch(() => {\n // Do nothing.\n });\n });\n };\n\n /**\n * Validate given timestamp against format\n * @param {String} timestamp formatted timestamp hh:mm:ss\n * @param {String} fld field selector\n * @param {String} existing existing value\n * @returns\n */\n const validateTimestampFormat = (timestamp, fld, existing) => {\n const regex = /^([0-9]{2}):([0-5][0-9]):([0-5][0-9])(\\.\\d{2})?$/;\n if (!regex.test(timestamp)) {\n addNotification(M.util.get_string('invalidtimestampformat', 'mod_interactivevideo'), 'danger');\n if (existing) {\n $(fld).val(existing);\n } else {\n $(fld).val(convertSecondsToHMS(start, false, false));\n }\n return false;\n }\n return true;\n };\n\n /**\n * Validate timestamp against start and end time of the video, existing timestamp and skip segments\n * @param {String} timestamp formatted timestamp\n * @param {String} fld field selector\n * @param {String} existing existing value\n * @param {Number} seconds\n * @param {Boolean} checkduration if true, check against start and end time\n * @param {Boolean} checkexisting if true, check against existing annotations\n * @param {Boolean} checkskipsegment if true, check against skip segments\n * @returns\n */\n const validateTimeStartEnd = (timestamp, fld, existing, seconds, checkduration,\n checkexisting, checkskipsegment) => {\n // Convert the timestamp to seconds.\n const parts = timestamp.split(':');\n timestamp = Number(parts[0]) * 3600 + Number(parts[1]) * 60 + Number(parts[2]);\n // Make sure the timestamp is between start and end.\n if (checkduration) {\n if (timestamp > end || timestamp < start) {\n const message = M.util.get_string('timemustbebetweenstartandendtime', 'mod_interactivevideo', {\n \"start\": convertSecondsToHMS(start, true, false),\n \"end\": convertSecondsToHMS(end, true, false)\n });\n addNotification(message, 'danger');\n if (existing) {\n $(fld).val(existing);\n } else {\n $(fld).val(convertSecondsToHMS(start, false, false));\n }\n return -1;\n }\n }\n\n // Make sure the timestamp is not already in the list.\n if (checkexisting) {\n if (annotations.find(x => x.timestamp == timestamp) && timestamp != seconds) {\n addNotification(M.util.get_string('interactionalreadyexists', 'mod_interactivevideo'), 'danger');\n if (existing) {\n $(fld).val(existing);\n } else {\n $(fld).val(convertSecondsToHMS(start, false, false));\n }\n return -1;\n }\n }\n\n // Make sure timestamp is not in a skip segment.\n if (checkskipsegment) {\n const skipsegments = annotations.filter((annotation) => annotation.type == 'skipsegment');\n const skip = skipsegments.find(x => Number(x.timestamp) < Number(timestamp)\n && Number(x.title) > Number(timestamp));\n if (skip) {\n addNotification(M.util.get_string('interactionisbetweentheskipsegment', 'mod_interactivevideo', {\n \"start\": convertSecondsToHMS(skip.timestamp, true, false),\n \"end\": convertSecondsToHMS(skip.title, true, false)\n }), 'danger');\n if (existing) {\n $(fld).val(existing);\n } else {\n $(fld).val(convertSecondsToHMS(start, false, false));\n }\n return -1;\n }\n }\n\n return timestamp;\n };\n\n /**\n * Run interaction\n * @param {Object} annotation annotation object\n */\n const runInteraction = (annotation) => {\n // Remove the previous message but keep the one below the video.\n $('#annotation-modal').modal('hide');\n $('#message').not('[data-placement=bottom]').remove();\n $('#end-screen').remove();\n player.pause();\n const activityType = ctRenderer[annotation.type];\n activityType.runInteraction(annotation);\n };\n\n /**\n * Set of events to run after the video player is ready.\n */\n const onReady = async () => {\n if (player.type != 'vimeo' && player.type != 'html5video') { // Vimeo/HTML5 does not pause/play on click.\n $('#video-block').addClass('no-pointer');\n }\n\n if (player.support.playbackrate == false) {\n $('#changerate').remove();\n } else {\n $('#changerate').removeClass('d-none');\n }\n let t = await player.getDuration();\n if (!end) {\n end = t;\n } else {\n end = Math.min(end, t);\n }\n\n if (start > end) {\n start = 0;\n }\n\n totaltime = end - start;\n // Recalculate the ratio of the video.\n let ratio = 16 / 9;\n if (!displayoptions.usefixedratio || displayoptions.usefixedratio == 0) {\n ratio = player.aspectratio;\n }\n $(\"#video-wrapper\").css('padding-bottom', (1 / ratio) * 100 + '%');\n\n playerReady = true;\n\n // Handle timeline block.\n $(\"#timeline-wrapper #video-timeline\").css({\n 'background-image': 'url(' + player.posterImage + ')',\n 'background-size': 'contain',\n 'background-repeat': 'no-repeat',\n });\n $(\"#timeline-wrapper #duration\").text(convertSecondsToHMS(end, true));\n $(\"#timeline-wrapper #currenttime\").text(convertSecondsToHMS(start, true));\n // Render minute markers.\n const minutes = Math.floor(totaltime / 60);\n $('#timeline-items-wrapper').css('width', (minutes * 300) + 'px');\n const relWidth = $('#timeline-items').width();\n $('#minute-markers, #minute-markers-bg, #vseek').css('width', relWidth + 'px');\n let startPercentage = 0;\n let newStart = start;\n if (start % 60 != 0) {\n startPercentage = (60 - (start % 60)) / totaltime * 100;\n newStart = start + (60 - (start % 60));\n $('#minute-markers, #minute-markers-bg').append(`
`);\n }\n for (let i = newStart; i <= end; i += 60) {\n let percentage = ((i - newStart) / totaltime * 100) + startPercentage;\n let marker = '';\n // Format h:m (e.g 3h1m).\n if (i >= 3600) {\n marker = Math.floor(i / 3600) + 'h' + Math.floor((i % 3600) / 60) + 'm';\n } else {\n marker = Math.floor(i / 60) + 'm';\n }\n $('#minute-markers, #minute-markers-bg').append(`
${marker}
`);\n }\n\n if (end % 60 != 0) {\n $('#minute-markers, #minute-markers-bg').append(`
`);\n }\n getAnnotations();\n };\n\n /**\n * Run when video ended (i.e. arrives at 'end' time)\n */\n const onEnded = () => {\n player.pause();\n $('#playpause').find('i').removeClass('bi-pause-fill').addClass('bi-play-fill');\n // Cover the video with a message on a white background div.\n $('#video-wrapper').append(`
\n
`);\n $('#video-nav #progress').css('width', '100%');\n $('#scrollbar, #scrollhead-top').css('left', '100%');\n // Focus on the restart button.\n $('#message #restart').focus();\n\n // If the current time matches the timestamp of an annotation, highlight the annotation.\n const currentAnnotation = annotations.find((annotation) => annotation.timestamp == end);\n if (currentAnnotation) {\n $('#annotation-list tr').removeClass('active');\n $(`tr[data-id=\"${currentAnnotation.id}\"]`).addClass('active');\n // Show tooltip for two seconds.\n $('#video-nav .annotation[data-id=\"' + currentAnnotation.id + '\"] .item').tooltip('show');\n setTimeout(function() {\n $('#video-nav .annotation[data-id=\"' + currentAnnotation.id + '\"] .item').tooltip('hide');\n }, 2000);\n }\n };\n\n /**\n * Execute when video is sought\n * @param {Number} t seconds\n * @returns\n */\n const onSeek = async function(t) {\n if (!playerReady) {\n return;\n }\n if (t) {\n t = Number(t);\n } else {\n t = await player.getCurrentTime();\n }\n if (t > start && t < end) {\n $('#end-screen').remove();\n }\n const percentage = (t - start) / (totaltime) * 100;\n $('#scrollbar, #scrollhead-top').css('left', percentage + '%');\n $('#timeline-wrapper #currenttime').text(convertSecondsToHMS(t, true));\n dispatchEvent('timeupdate', {'time': t});\n };\n\n let onPlayingInterval;\n let visualized = false;\n /**\n * Excute when video plays (i.e. start or resume)\n */\n const onPlaying = () => {\n $('#message, #end-screen').remove();\n $('#playpause').find('i').removeClass('bi-play-fill').addClass('bi-pause-fill');\n if (player.audio && !visualized) {\n player.visualizer();\n visualized = true;\n }\n const intervalFunction = async function() {\n let thisTime = await player.getCurrentTime();\n const isPlaying = await player.isPlaying();\n const isEnded = await player.isEnded();\n if (!isPlaying || isEnded) {\n cancelAnimationFrame(onPlayingInterval);\n return;\n }\n\n if (thisTime < start) {\n await player.seek(start);\n thisTime = start;\n }\n\n if (thisTime >= end) {\n player.stop(end);\n cancelAnimationFrame(onPlayingInterval);\n onEnded();\n return;\n }\n dispatchEvent('timeupdate', {'time': thisTime});\n $('#timeline-wrapper #currenttime').text(convertSecondsToHMS(thisTime, true));\n let percentage = (thisTime - start) / (totaltime) * 100;\n $('#video-nav #progress').css('width', percentage + '%');\n\n $(\"#scrollbar, #scrollhead-top\").css('left', percentage + '%');\n\n // Scroll the timeline so that the current time is in the middle of the timeline.\n const scrollBar = document.getElementById('scrollbar');\n // Check if the scrollbar is in view.\n const rect = scrollBar.getBoundingClientRect();\n if (rect.left < 0 || rect.right > window.innerWidth) {\n scrollBar.scrollIntoView({behavior: \"instant\", block: \"center\", inline: \"center\"});\n }\n\n // If the current time matches the timestamp of an annotation, highlight the annotation\n const currentAnnotation = annotations.find(x => (thisTime - player.frequency) <= x.timestamp\n && (thisTime + player.frequency) >= x.timestamp);\n if (currentAnnotation) {\n $('#annotation-list tr').removeClass('active');\n $(`tr[data-id=\"${currentAnnotation.id}\"]`).addClass('active');\n $('#video-nav .annotation[data-id=\"' + currentAnnotation.id + '\"] .item').tooltip('show');\n setTimeout(function() {\n $('#video-nav .annotation[data-id=\"' + currentAnnotation.id + '\"] .item').tooltip('hide');\n }, 2000);\n }\n\n // If current time is within the skipsegment, seek to the end of the segment.\n let skipsegments = annotations.filter((annotation) => annotation.type == 'skipsegment');\n let skip = skipsegments.find(x => Number(x.timestamp) < Number(thisTime)\n && Number(x.title) > Number(thisTime));\n if (skip) {\n await player.seek(Number(skip.title));\n // Replace the progress bar.\n percentage = (skip.title - start) / totaltime * 100;\n replaceProgressBars(percentage);\n }\n };\n if (player.type == 'yt' || player.type == 'wistia') {\n const animate = async () => {\n intervalFunction();\n onPlayingInterval = requestAnimationFrame(animate);\n };\n onPlayingInterval = requestAnimationFrame(animate);\n } else {\n intervalFunction();\n }\n };\n\n /**\n * Excute when video is paused.\n */\n const onPause = () => {\n cancelAnimationFrame(onPlayingInterval);\n $('#playpause').find('i').removeClass('bi-pause-fill').addClass('bi-play-fill');\n };\n\n // Implement the player\n require(['mod_interactivevideo/player/' + type], function(VideoPlayer) {\n player = new VideoPlayer(\n url,\n start,\n end,\n false,\n true,\n );\n });\n\n $(document).on('iv:playerReady', function() {\n onReady();\n });\n\n $(document).on('iv:playerPaused', function() {\n onPause();\n });\n\n $(document).on('iv:playerPlaying', function() {\n onPlaying();\n });\n\n $(document).on('iv:playerEnded', function() {\n onEnded();\n });\n\n $(document).on('iv:playerSeek', function(e) {\n onSeek(e.detail.time);\n });\n\n // Post annotation update (add, edit, clone).\n $(document).on('annotationupdated', function(e) {\n const action = e.originalEvent.detail.action;\n if (action == 'import') {\n annotations = e.originalEvent.detail.annotations;\n renderAnnotationItems(annotations);\n addNotification(M.util.get_string('interactionimported', 'mod_interactivevideo'), 'success');\n return;\n }\n let updated = e.originalEvent.detail.annotation;\n if (action == 'edit' || action == 'draft' || action == 'savedraft') {\n annotations = annotations.filter(function(item) {\n return item.id != updated.id;\n });\n }\n updated.prop = JSON.stringify(contentTypes.find(x => x.name === updated.type));\n annotations.push(updated);\n if (action == 'add') {\n activeid = updated.id;\n } else {\n activeid = null;\n }\n\n renderAnnotationItems(annotations);\n if (action == 'add' || action == 'clone') {\n addNotification(M.util.get_string('interactionadded', 'mod_interactivevideo'), 'success');\n $(`tr[data-id=\"${updated.id}\"]`).addClass('active');\n } else if (action == 'edit') {\n addNotification(M.util.get_string('interactionupdated', 'mod_interactivevideo'), 'success');\n $(`tr[data-id=\"${updated.id}\"]`).addClass('active');\n setTimeout(function() {\n $(`tr[data-id=\"${updated.id}\"]`).removeClass('active');\n }, 1500);\n }\n\n // If draft exists, activate the save button.\n if (annotations.find(x => x.status == 'draft')) {\n $('#timeline-wrapper #savedraft').removeAttr('disabled').addClass('pulse');\n } else {\n $('#timeline-wrapper #savedraft').attr('disabled', 'disabled').removeClass('pulse');\n }\n });\n\n // Re-render annotation list and timeline after an annotation is deleted.\n $(document).on('annotationdeleted', function(e) {\n const annotation = e.originalEvent.detail.annotation;\n activeid = null;\n $(`tr[data-id=\"${annotation.id}\"]`).addClass('deleted');\n setTimeout(function() {\n annotations = annotations.filter(function(item) {\n return item.id != annotation.id;\n });\n renderAnnotationItems(annotations);\n addNotification(M.util.get_string('interactiondeleted', 'mod_interactivevideo'), 'success');\n }, 1000);\n });\n\n // Implement create annotation\n $(document).on('click', '#addcontentdropdown .dropdown-item', async function(e) {\n if (!playerReady) {\n return;\n }\n e.preventDefault();\n $('#addcontentdropdown .dropdown-item').removeClass('active');\n const ctype = $(this).data('type');\n player.pause();\n let timestamp = currentTime || await player.getCurrentTime();\n timestamp = Number(timestamp.toFixed(2));\n const contenttype = contentTypes.find(x => x.name == ctype);\n if (contenttype.hastimestamp) {\n if (annotations.find(x => x.timestamp == timestamp)) {\n addNotification(M.util.get_string('interactionalreadyexists', 'mod_interactivevideo'), 'danger');\n return;\n }\n // Check skip segments\n const skipsegments = annotations.filter(x => x.type == 'skipsegment');\n const skip = skipsegments.find(x => Number(x.timestamp) < Number(currentTime)\n && Number(x.title) > Number(currentTime));\n if (skip) {\n addNotification(M.util.get_string('interactionisbetweentheskipsegment', 'mod_interactivevideo'), 'danger');\n return;\n }\n }\n if (!contenttype.allowmultiple && annotations.find(x => x.type == ctype)) {\n addNotification(M.util.get_string('interactionalreadyexists', 'mod_interactivevideo'), 'danger');\n return;\n }\n currentTime = null;\n ctRenderer[ctype].addAnnotation(annotations, timestamp, coursemodule);\n });\n\n // Implement edit annotation\n $(document).on('click', 'tr.annotation .edit', async function(e) {\n e.preventDefault();\n const timestamp = $(this).closest('.annotation').data('timestamp');\n if (timestamp) {\n await player.seek(timestamp, true);\n }\n player.pause();\n const id = $(this).closest('.annotation').data('id');\n const contenttype = $(this).closest('.annotation').data('type');\n ctRenderer[contenttype].editAnnotation(annotations, id, coursemodule);\n });\n\n // Implement copy annotation\n $(document).on('click', 'tr.annotation .copy', async function(e) {\n e.preventDefault();\n const id = $(this).closest('.annotation').data('id');\n const contenttype = $(this).closest('.annotation').data('type');\n const time = await player.getCurrentTime();\n ctRenderer[contenttype].cloneAnnotation(id, time);\n });\n\n // Implement delete annotation.\n $(document).on('click', 'tr.annotation .delete', function(e) {\n e.preventDefault();\n player.pause();\n const id = $(this).closest('.annotation').data('id');\n const annotation = annotations.find(annotation => annotation.id == id);\n try {\n Notification.deleteCancel(\n M.util.get_string('deleteinteraction', 'mod_interactivevideo'),\n M.util.get_string('deleteinteractionconfirm', 'mod_interactivevideo'),\n M.util.get_string('delete', 'mod_interactivevideo'),\n function() {\n ctRenderer[annotation.type].deleteAnnotation(annotations, id);\n },\n null\n );\n } catch {\n Notification.deleteCancelPromise(\n M.util.get_string('deleteinteraction', 'mod_interactivevideo'),\n M.util.get_string('deleteinteractionconfirm', 'mod_interactivevideo'),\n ).then(() => {\n return ctRenderer[annotation.type].deleteAnnotation(annotations, id);\n }).catch(() => {\n return;\n });\n }\n\n\n });\n\n // Implement view annotation.\n $(document).on('click', 'tr.annotation .title', async function(e) {\n e.preventDefault();\n const timestamp = $(this).closest('.annotation').data('timestamp');\n // Update the progress bar.\n const percentage = (timestamp - start) / totaltime * 100;\n replaceProgressBars(percentage);\n await player.seek(timestamp, true);\n player.pause();\n const id = $(this).closest('.annotation').data('id');\n const theAnnotation = annotations.find(annotation => annotation.id == id);\n setTimeout(() => {\n runInteraction(theAnnotation);\n }, 500);\n });\n\n // Implement go to timestamp.\n $(document).on('click', 'tr.annotation .timestamp', async function(e) {\n e.preventDefault();\n const timestamp = $(this).data('timestamp');\n await player.seek(timestamp);\n player.play();\n });\n\n // Right click on the video nav or video timeline to add a new interaction.\n $(document).on('contextmenu', '#vseek, #video-timeline', async function(e) {\n if (!playerReady) {\n return;\n }\n e.preventDefault();\n e.stopImmediatePropagation();\n const percentage = e.offsetX / $(this).width();\n replaceProgressBars(percentage * 100);\n currentTime = (percentage * totaltime) + start;\n await player.seek(currentTime);\n player.pause();\n $(\"#addcontent\").trigger('click');\n });\n\n // Right click on the scrollbar to add a new interaction.\n $(document).on('contextmenu', '#scrollbar, #scrollhead-top', async function(e) {\n if (!playerReady) {\n return;\n }\n e.preventDefault();\n e.stopImmediatePropagation();\n currentTime = await player.getCurrentTime();\n $(\"#addcontent\").trigger('click');\n });\n\n // Click the play/pause button on the timeline region to pause/play video.\n $(document).on('click', '#playpause', async function(e) {\n if (!playerReady) {\n return;\n }\n e.preventDefault();\n // Pause or resume the video.\n let isPlaying = await player.isPlaying();\n if (isPlaying) {\n player.pause();\n } else {\n let t = await player.getCurrentTime();\n if (t >= end) {\n $('#end-screen #restart').trigger('click');\n } else {\n player.play();\n }\n }\n });\n\n $(document).on('click', '#video-block', function(e) {\n if (!playerReady) {\n return;\n }\n e.preventDefault();\n $('#playpause').trigger('click');\n });\n\n // Right click on the annotation indicator to edit the annotation.\n $(document).on('contextmenu', '#video-nav .annotation', function(e) {\n e.preventDefault();\n e.stopImmediatePropagation();\n const id = $(this).data('id');\n // Trigger click on the edit button.\n $(`tr.annotation[data-id=\"${id}\"] .edit`).trigger('click');\n });\n\n // Quick edit.\n $(document).on('contextmenu', '[data-editable]', function(e) {\n e.preventDefault();\n e.stopImmediatePropagation();\n if ($('[data-field].editing').length > 0) {\n return;\n }\n const fld = $(this).data('editable');\n $(this).hide();\n $(this).siblings('[data-field=\"' + fld + '\"]').removeClass('d-none').focus().addClass('editing');\n });\n\n $(document).on('keyup', '[data-field].editing', function(e) {\n $(this).removeClass('is-invalid');\n const initialValue = $(this).data('initial-value');\n const val = $(this).val();\n const fld = $(this).data('field');\n if (val == '') {\n $(this).addClass('is-invalid');\n }\n\n // If escape key is pressed, revert the value.\n if (e.key == 'Escape') {\n $(this).val(initialValue);\n $(this).removeClass('editing');\n $(this).addClass('d-none');\n $(this).siblings('[data-editable]').show();\n return;\n }\n // If enter key is pressed, save the value.\n if (e.key == 'Enter') {\n let seconds;\n if (fld == 'timestamp') {\n const parts = initialValue.split(':');\n seconds = Number(parts[0]) * 3600 + Number(parts[1]) * 60 + Number(parts[2]);\n if (!validateTimestampFormat(val, '[data-field].editing', initialValue)) {\n $(this).addClass('is-invalid');\n return;\n }\n const timestamp = validateTimeStartEnd(val, '[data-field].editing', initialValue, seconds,\n true, true, true);\n if (timestamp == -1) {\n $(this).addClass('is-invalid');\n return;\n }\n seconds = timestamp;\n }\n\n if ($(this).hasClass('is-invalid')) {\n return;\n }\n if (val == initialValue) {\n $(this).removeClass('editing');\n $(this).addClass('d-none');\n $(this).siblings('[data-editable]').show();\n return;\n }\n const id = $(this).data('id');\n $.ajax({\n url: M.cfg.wwwroot + '/mod/interactivevideo/ajax.php',\n method: \"POST\",\n dataType: \"text\",\n data: {\n action: 'quickeditfield',\n sesskey: M.cfg.sesskey,\n id: id,\n field: fld,\n contextid: M.cfg.contextid,\n value: fld == 'timestamp' ? seconds : val,\n },\n success: function(data) {\n const updated = JSON.parse(data);\n dispatchEvent('annotationupdated', {\n annotation: updated,\n action: 'edit'\n });\n }\n });\n return;\n }\n });\n\n $(document).on('blur', '[data-field].editing', function() {\n const initialValue = $(this).data('initial-value');\n $(this).val(initialValue);\n $(this).removeClass('editing');\n $(this).addClass('d-none');\n $(this).siblings('[data-editable]').show();\n });\n // End quick edit.\n\n $(document).on('click', '#end-screen #restart', async function(e) {\n e.preventDefault();\n $('#end-screen').remove();\n await player.seek(start);\n player.play();\n });\n\n // Display tooltip on anntation indicator when annotation on the list is hovered.\n $(document).on('mouseover', 'tr.annotation', function() {\n const id = $(this).data('id');\n $(`#video-nav ul li[data-id=\"${id}\"] .item`).trigger('mouseover');\n });\n\n // Remove tooltip when annotation on the list is not hovered.\n $(document).on('mouseout', 'tr.annotation', function() {\n const id = $(this).data('id');\n $(`#video-nav ul li[data-id=\"${id}\"] .item`).trigger('mouseout');\n $('.tooltip').remove();\n });\n\n // Highlight annotation on the list when annotation indicator is hovered.\n $(document).on('mouseover', '#video-nav ul li', function() {\n const id = $(this).data('id');\n $(`tr.annotation[data-id=\"${id}\"]`).addClass('active');\n });\n\n // Remove highlight when annotation indicator is not hovered.\n $(document).on('mouseout', '#video-nav ul li', function() {\n const id = $(this).data('id');\n $(`tr.annotation[data-id=\"${id}\"]`).removeClass('active');\n });\n\n // Validate timestamp when the timestamp field is changed.\n $(document).on('change', '.timestamp-input, .timestamp-field input', function() {\n $(this).removeClass('is-invalid');\n const parts = $(this).val().split(':');\n const seconds = Number(parts[0]) * 3600 + Number(parts[1]) * 60 + Number(parts[2]);\n if (!validateTimestampFormat($(this).val(), this)) {\n $(this).addClass('is-invalid');\n return;\n }\n\n const timestamp = validateTimeStartEnd($(this).val(), this, \"00:00:00\", seconds, true, false, true);\n\n if (timestamp == -1) {\n $(this).addClass('is-invalid');\n return;\n }\n });\n\n const appendTimestampMarker = (seconds, rounded) => {\n const formattedTime = convertSecondsToHMS(seconds, true, rounded);\n $('#vseek #bar').append(`
\n
${formattedTime}
`);\n };\n\n $(document).on('annotationitemsrendered', function() {\n $('#timeline-wrapper [data-toggle=\"tooltip\"]').tooltip({\n 'boundary': 'window',\n 'container': '#timeline',\n });\n // Put the minute markers on the timeline;\n let targetAnnotation = null;\n // Destroy draggable and resizable if already initialized.\n try {\n $('#timeline-items .annotation, #video-timeline-wrapper .skipsegment').draggable('destroy');\n $('#timeline-items .annotation, #video-timeline-wrapper .skipsegment').resizable('destroy');\n } catch (e) {\n // Do nothing.\n }\n $('#timeline-items .annotation.li-draggable').draggable({\n 'axis': 'x',\n 'start': function() {\n appendTimestampMarker($(this).data('timestamp'));\n $('.tooltip, #message').remove();\n $('#timeline-items').addClass('no-pointer-events');\n },\n 'drag': async function(event, ui) {\n $('.tooltip').remove();\n let timestamp = ((ui.position.left + 5) / $('#timeline-items').width()) * totaltime + start;\n if (timestamp < start) {\n timestamp = start;\n ui.position.left = -5;\n }\n if (timestamp > end) {\n timestamp = end;\n ui.position.left = $('#timeline-items').width() - 5;\n }\n $('#scrollbar, #position-marker, #scrollhead-top').css('left', (timestamp - start) / totaltime * 100 + '%');\n $(this).css('left', (timestamp - start) / totaltime * 100 + '%');\n await player.seek(timestamp);\n player.pause();\n $('#vseek #position').text(convertSecondsToHMS(timestamp, true, false));\n },\n 'stop': async function(event, ui) {\n $('.tooltip').remove();\n $('#vseek #position-marker').remove();\n setTimeout(function() {\n $('#timeline-items').removeClass('no-pointer-events');\n }, 200);\n let timestamp = ((ui.position.left + 5) / $('#timeline-items').width()) * totaltime + start;\n if (timestamp < start) {\n timestamp = start;\n $(this).css('left', '-5px');\n }\n if (timestamp > end) {\n timestamp = end;\n $(this).css('left', 'calc(100% - 5px)');\n }\n $('#scrollbar, #position-marker, #scrollhead-top').css('left', (timestamp - start) / totaltime * 100 + '%');\n const id = $(this).data('id');\n targetAnnotation = annotations.find(x => x.id == id);\n const existingAnnotation = annotations.find(x => x.timestamp == timestamp && x.id != id);\n if (existingAnnotation) {\n addNotification(M.util.get_string('interactionalreadyexists', 'mod_interactivevideo'), 'danger');\n renderAnnotationItems(annotations);\n return;\n }\n if (targetAnnotation.timestamp == timestamp) {\n return;\n }\n targetAnnotation.timestamp = timestamp;\n targetAnnotation.status = \"draft\";\n dispatchEvent('annotationupdated', {\n annotation: targetAnnotation,\n action: 'draft'\n });\n await player.seek(timestamp); // Seek to the new position\n player.pause();\n $('#scrollbar, #position-marker, #scrollhead-top').css('left', (timestamp - start) / totaltime * 100 + '%');\n }\n });\n\n $('#video-timeline-wrapper .skipsegment').draggable({\n 'axis': 'x',\n 'start': function() {\n $('#message').remove();\n appendTimestampMarker($(this).data('timestamp'));\n $('#timeline-items').addClass('no-pointer-events');\n },\n 'drag': async function(event, ui) {\n const id = $(this).data('id');\n targetAnnotation = annotations.find(x => x.id == id);\n let timestamp = ((ui.position.left) / $('#video-timeline').width()) * totaltime + start;\n if (timestamp < start) {\n timestamp = start;\n }\n\n if (timestamp > end) {\n timestamp = end;\n }\n\n $('#scrollbar, #position-marker, #scrollhead-top').css('left', (timestamp - start) / totaltime * 100 + '%');\n await player.seek(timestamp);\n player.pause();\n $('#vseek #position').text(convertSecondsToHMS(timestamp, true, false));\n },\n 'stop': async function(event, ui) {\n $('#vseek #position-marker').remove();\n setTimeout(function() {\n $('#timeline-items').removeClass('no-pointer-events');\n }, 200);\n let timestamp = ((ui.position.left) / $('#video-timeline').width()) * totaltime + start;\n const id = $(this).data('id');\n targetAnnotation = annotations.find(x => x.id == id);\n let skipduration = Number(targetAnnotation.title) - Number(targetAnnotation.timestamp);\n if (timestamp < 0 && timestamp + skipduration < start) {\n renderAnnotationItems(annotations);\n return;\n }\n if (timestamp > end) {\n renderAnnotationItems(annotations);\n return;\n }\n if (timestamp < start) {\n skipduration = skipduration - Math.abs(start - timestamp);\n timestamp = start;\n }\n if (timestamp + skipduration > end) {\n skipduration = Math.abs(end - timestamp);\n timestamp = end - skipduration;\n }\n if (skipduration <= 0) {\n renderAnnotationItems(annotations);\n return;\n }\n const existingAnnotation = annotations.find(x => x.timestamp == timestamp && x.id != id);\n if (existingAnnotation) {\n addNotification(M.util.get_string('interactionalreadyexists', 'mod_interactivevideo'), 'danger');\n renderAnnotationItems(annotations);\n return;\n }\n if (targetAnnotation.timestamp == timestamp) {\n renderAnnotationItems(annotations);\n return;\n }\n targetAnnotation.timestamp = timestamp;\n targetAnnotation.title = timestamp + skipduration;\n if (targetAnnotation.title > end) {\n targetAnnotation.title = end;\n }\n targetAnnotation.status = \"draft\";\n dispatchEvent('annotationupdated', {\n annotation: targetAnnotation,\n action: 'draft'\n });\n await player.seek(timestamp); // Seek to the new position\n player.pause();\n $('#scrollbar, #position-marker, #scrollhead-top').css('left', (timestamp - start) / totaltime * 100 + '%');\n }\n });\n\n $('#video-timeline-wrapper .skipsegment').resizable({\n 'containment': '#video-timeline-wrapper',\n 'handles': 'e, w',\n 'start': function() {\n $('#message').remove();\n appendTimestampMarker($(this).data('timestamp'));\n $('#timeline-items').addClass('no-pointer-events');\n },\n 'resize': async function(event, ui) {\n let timestamp;\n if (ui.originalPosition.left != ui.position.left || ui.originalSize.width == ui.size.width) {\n if (ui.position.left < 0) {\n ui.position.left = 0;\n }\n timestamp = ((ui.position.left) / $('#video-timeline').width()) * totaltime + start;\n } else {\n timestamp = ((ui.position.left + ui.size.width) / $('#video-timeline').width()) * totaltime + start;\n }\n $('#scrollbar, #position-marker, #scrollhead-top').css('left', (timestamp - start) / totaltime * 100 + '%');\n await player.seek(timestamp);\n player.pause();\n $('#vseek #position').text(convertSecondsToHMS(timestamp, true, false));\n },\n 'stop': async function(event, ui) {\n $('#vseek #position-marker').remove();\n setTimeout(function() {\n $('#timeline-items').removeClass('no-pointer-events');\n }, 200);\n const id = $(this).data('id');\n targetAnnotation = annotations.find(x => x.id == id);\n let timestamp, direction;\n if (ui.originalPosition.left != ui.position.left) {\n if (ui.position.left < 0) {\n ui.position.left = 0;\n }\n timestamp = ((ui.position.left) / $('#video-timeline').width()) * totaltime + start;\n direction = \"left\";\n } else {\n timestamp = ((ui.position.left + ui.size.width) / $('#video-timeline').width()) * totaltime + start;\n direction = \"right\";\n }\n const existingAnnotation = annotations.find(x => x.timestamp == timestamp && x.id != id);\n if (existingAnnotation) {\n addNotification(M.util.get_string('interactionalreadyexists', 'mod_interactivevideo'), 'danger');\n renderAnnotationItems(annotations);\n return;\n }\n if (targetAnnotation.timestamp == timestamp) {\n return;\n }\n if (direction == \"left\") {\n targetAnnotation.timestamp = timestamp;\n } else {\n targetAnnotation.title = timestamp;\n if (targetAnnotation.title > end) {\n targetAnnotation.title = end;\n }\n }\n targetAnnotation.status = 'draft';\n dispatchEvent('annotationupdated', {\n annotation: targetAnnotation,\n action: 'draft'\n });\n await player.seek(timestamp);\n player.pause();\n $('#scrollbar, #position-marker, #scrollhead-top').css('left', (timestamp - start) / totaltime * 100 + '%');\n }\n });\n\n $('#video-timeline-wrapper .skipsegment').off('contextmenu').on('contextmenu', function(e) {\n e.preventDefault();\n e.stopImmediatePropagation();\n const id = $(this).data('id');\n $(`tr.annotation[data-id=\"${id}\"] .edit`).trigger('click');\n });\n\n $('#video-timeline-wrapper .skipsegment').off('click').on('click', async function(e) {\n e.preventDefault();\n const timestamp = $(this).data('timestamp');\n await player.seek(timestamp);\n player.pause();\n });\n\n $('#video-timeline-wrapper .skipsegment .delete-skipsegment').off('click').on('click', function(e) {\n e.preventDefault();\n const id = $(this).closest('.skipsegment').data('id');\n $(`tr.annotation[data-id=\"${id}\"] .delete`).trigger('click');\n });\n });\n\n $('#scrollbar').draggable({\n 'containment': '#timeline-items',\n 'axis': 'x',\n 'cursor': 'col-resize',\n 'start': function(event, ui) {\n $('#timeline-items').addClass('no-pointer-events');\n $(\"#message\").remove();\n appendTimestampMarker(((ui.position.left) / $('#timeline-items').width()) * totaltime + start, false);\n },\n 'drag': async function(event, ui) {\n let timestamp = ((ui.position.left) / $('#timeline-items').width()) * totaltime + start;\n let percentage = (timestamp - start) / totaltime * 100;\n $('#vseek #position').text(convertSecondsToHMS(timestamp, true, false));\n $('#vseek #position-marker, #scrollhead-top, #scrollbar')\n .css('left', percentage + '%');\n await player.seek(timestamp);\n player.pause();\n\n },\n 'stop': function(event, ui) {\n $('#vseek #position-marker').remove();\n setTimeout(function() {\n $('#timeline-items').removeClass('no-pointer-events');\n }, 200);\n // Convert the position to percentage\n let timestamp = ((ui.position.left) / $('#timeline-items').width()) * totaltime + start;\n $('#scrollbar, #scrollhead-top').css('left', (timestamp - start) / totaltime * 100 + '%');\n }\n });\n\n $('#scrollhead-top').draggable({\n 'axis': 'x',\n 'cursor': 'col-resize',\n 'start': function(event, ui) {\n $('#vseek').addClass('no-pointer-events');\n $(\"#message\").remove();\n appendTimestampMarker(((ui.position.left) / $('#vseek').width()) * totaltime + start, false);\n },\n 'drag': async function(event, ui) {\n let timestamp = ((ui.position.left) / $('#vseek').width()) * totaltime + start;\n let percentage = (timestamp - start) / totaltime * 100;\n if (timestamp < start) {\n timestamp = start;\n }\n $('#vseek #position').text(convertSecondsToHMS(timestamp, true, false));\n $('#vseek #position-marker, #scrollhead-top, #scrollbar')\n .css('left', percentage + '%');\n await player.seek(timestamp);\n player.pause();\n },\n 'stop': function(event, ui) {\n $('#vseek #position-marker').remove();\n setTimeout(function() {\n $('#vseek').removeClass('no-pointer-events');\n }, 200);\n // Convert the position to percentage\n let timestamp = ((ui.position.left) / $('#vseek').width()) * totaltime + start;\n if (timestamp < start) {\n timestamp = start;\n }\n $('#scrollbar, #scrollhead-top').css('left', (timestamp - start) / totaltime * 100 + '%');\n }\n });\n\n // Resize timeline.\n $('#timeline-wrapper').resizable({\n 'handles': 'n',\n 'minHeight': 125,\n 'maxHeight': 500,\n 'start': function() {\n $('#top-region, #timeline-wrapper').addClass('no-pointer-events');\n },\n 'resize': function(event, ui) {\n $('#top-region').css('height', `calc(100dvh - ${ui.size.height + 70}px)`);\n },\n 'stop': function() {\n $('#top-region, #timeline-wrapper').removeClass('no-pointer-events');\n localStorage.setItem('timeline-height', $('#timeline-wrapper').height());\n }\n });\n\n // Resize player region.\n $('#separator').draggable({\n 'axis': 'x',\n 'containment': '#wrapper',\n 'grid': [1, 0],\n 'start': function() {\n $('#wrapper').addClass('no-pointer-events');\n },\n drag: function() {\n const parentOffset = $(this).offset();\n const width = parentOffset.left;\n $('#player-region').css('width', width + 'px');\n $('#content-region').css('width', 'calc(100% - ' + width + 'px)');\n },\n stop: function() {\n const width = $(this).offset().left;\n // Save this to local storage\n localStorage.setItem('player-width', width);\n $('#wrapper').removeClass('no-pointer-events');\n }\n });\n\n // Set player region width from the saved width in local storage.\n const playerWidth = localStorage.getItem('player-width');\n if (playerWidth && window.innerWidth > 992) {\n $('#separator').css('left', playerWidth + 'px');\n $('#player-region').css('width', playerWidth + 'px');\n $('#content-region').css('width', 'calc(100% - ' + playerWidth + 'px)');\n }\n\n // Set timeline height from saved height in local storage.\n const timelineHeight = localStorage.getItem('timeline-height');\n if (timelineHeight) {\n $('#timeline-wrapper').css('height', timelineHeight + 'px');\n $('#top-region').css('height', `calc(100dvh - ${Number(timelineHeight) + 70}px)`);\n }\n\n // Seek bar functionalities\n $('#vseek #bar, #video-timeline, #video-nav .annotation').on('mouseenter', function(e) {\n $('#cursorbar, #position-marker').remove();\n e.preventDefault();\n e.stopImmediatePropagation();\n // First clone the #scrollbar and place it where the cursor is.\n let $scrollbar = $('#scrollbar').clone();\n $scrollbar.attr('id', 'cursorbar');\n\n const parentOffset = $(this).offset();\n const relX = e.pageX - parentOffset.left;\n\n $scrollbar.css('left', (relX + 5) + 'px');\n $scrollbar.find('#scrollhead').remove();\n const percentage = relX / $(this).width();\n const time = percentage * (totaltime) + start;\n const formattedTime = convertSecondsToHMS(time, true, false);\n $('#vseek #bar').append(`
\n
${formattedTime}
`);\n $('#vseek #position-marker').css('left', relX + 'px');\n $('#timeline-items').append($scrollbar);\n });\n\n $('#vseek #bar, #video-timeline, #video-nav .annotation').on('mouseleave', function(e) {\n e.stopImmediatePropagation();\n $('#vseek #position-marker, #cursorbar').remove();\n });\n\n $('#vseek #bar, #video-timeline').on('mousemove', function(e) {\n e.stopImmediatePropagation();\n const parentOffset = $(this).offset();\n const relX = e.pageX - parentOffset.left;\n const percentage = relX / $(this).width();\n let time = percentage * (totaltime) + start;\n if (time < start) {\n time = start;\n }\n const formattedTime = convertSecondsToHMS(time, true, false);\n // Move the cursorbar\n $('#cursorbar').css('left', (relX + 5) + 'px');\n $('#vseek #position').text(formattedTime);\n $('#vseek #position-marker').css('left', relX + 'px');\n });\n\n // Run interaction when annotation indicator is clicked.\n $(document).on('click', '#video-nav .annotation', async function(e) {\n e.preventDefault();\n e.stopImmediatePropagation();\n const id = $(this).data('id');\n const annotation = annotations.find(x => x.id == id);\n await player.seek(annotation.timestamp);\n runInteraction(annotation);\n });\n\n // Seek video on click on timeline or video nav.\n $(document).on('click', '#vseek #bar, #video-timeline', async function(e) {\n e.preventDefault();\n e.stopImmediatePropagation();\n const percentage = e.offsetX / $(this).width();\n replaceProgressBars(percentage * 100);\n await player.seek((percentage * totaltime) + start);\n player.pause();\n $(\"#message, #end-screen\").remove();\n });\n\n // Implement timeline zoom out.\n $('#zoomout').on('click', function() {\n const currentLevel = $('#timeline-items-wrapper').css('width'); // In px.\n const newLevel = parseInt(currentLevel) - 300;\n $('#timeline-items-wrapper').css('width', newLevel + 'px');\n const relWidth = $('#timeline-items').width();\n $('#minute-markers, #minute-markers-bg, #vseek').css('width', relWidth + 'px');\n let timelineElement = document.getElementById('timeline');\n if (timelineElement.scrollWidth <= timelineElement.clientWidth) {\n $(this).attr('disabled', 'disabled');\n }\n dispatchEvent('annotationitemsrendered', {'annotations': annotations});\n });\n\n // Implement timeline zoom in.\n $('#zoomin').on('click', function() {\n const currentLevel = $('#timeline-items-wrapper').css('width'); // In px.\n const newLevel = parseInt(currentLevel) + 300;\n $('#timeline-items-wrapper').css('width', newLevel + 'px');\n const relWidth = $('#timeline-items').width();\n $('#minute-markers, #minute-markers-bg, #vseek').css('width', relWidth + 'px');\n $('#zoomout').removeAttr('disabled');\n dispatchEvent('annotationitemsrendered', {'annotations': annotations});\n });\n\n // Implement zoom in and zoom out on mouse scroll on timeline.\n $(\"#timeline\").on('wheel', function(e) {\n if (e.ctrlKey || e.metaKey) {\n e.preventDefault();\n if (e.originalEvent.deltaY < 0) {\n $('#zoomin').trigger('click');\n } else {\n $('#zoomout').trigger('click');\n }\n }\n });\n\n document.getElementById('timeline').addEventListener('scroll', function() {\n document.getElementById('minute-markers-wrapper').scrollLeft = this.scrollLeft;\n document.getElementById('vseek').style.left = -this.scrollLeft + 'px';\n document.getElementById('minute-markers-bg-wrapper').style.left = -this.scrollLeft + 'px';\n document.getElementById('scrollbar').scrollHeight = this.scrollHeight;\n });\n\n // Save timeline changes.\n $('#savedraft').on('click', function(e) {\n e.stopImmediatePropagation();\n let draftAnnotations = annotations.filter(x => x.status == 'draft');\n let count = 0;\n draftAnnotations.forEach(function(a) {\n $.ajax({\n url: M.cfg.wwwroot + '/mod/interactivevideo/ajax.php',\n method: \"POST\",\n dataType: \"text\",\n data: {\n action: 'quickeditfield',\n sesskey: M.cfg.sesskey,\n id: a.id,\n field: 'timestamp',\n contextid: M.cfg.contextid,\n value: a.timestamp,\n },\n success: function(data) {\n const updated = JSON.parse(data);\n dispatchEvent('annotationupdated', {\n annotation: updated,\n action: 'savedraft'\n });\n }\n });\n if (a.type == 'skipsegment') {\n $.ajax({\n url: M.cfg.wwwroot + '/mod/interactivevideo/ajax.php',\n method: \"POST\",\n dataType: \"text\",\n data: {\n action: 'quickeditfield',\n sesskey: M.cfg.sesskey,\n id: a.id,\n field: 'title',\n contextid: M.cfg.contextid,\n value: a.title,\n },\n success: function(data) {\n const updated = JSON.parse(data);\n dispatchEvent('annotationupdated', {\n annotation: updated,\n action: 'savedraft'\n });\n }\n });\n }\n count++;\n if (count == draftAnnotations.length) {\n addNotification(M.util.get_string('draftsaved', 'mod_interactivevideo'), 'success');\n }\n });\n\n });\n\n // Launch content selection modal.\n $('#addcontent').on('click', async function(e) {\n e.preventDefault();\n if (!playerReady) {\n return;\n }\n $('#contentmodal').modal('show');\n });\n\n $('#contentmodal').on('show.bs.modal', function() {\n player.pause();\n $('#addcontentdropdown').addClass('modal-body');\n });\n\n $('#contentmodal').on('hide.bs.modal', function() {\n $('#addcontentdropdown .dropdown-item').removeClass('active');\n $('#addcontentdropdown').removeClass('modal-body');\n });\n\n // Inform user to save changes before close or unload the current page.\n window.addEventListener('beforeunload', (e) => {\n if (annotations.find(x => x.status == 'draft')) {\n const confirmationMessage = M.util.get_string('unsavedchanges', 'mod_interactivevideo');\n e.returnValue = confirmationMessage;\n return confirmationMessage;\n }\n return true;\n });\n\n // Implement the rate change.\n $(document).on('click', '.changerate', function(e) {\n e.preventDefault();\n const rate = $(this).data('rate');\n player.setRate(rate);\n $('.changerate').find('i').removeClass('bi-check');\n $(this).find('i').addClass('bi-check');\n });\n\n $(document).on('iv:playerRateChange', function(e) {\n $('.changerate').find('i').removeClass('bi-check');\n $(`.changerate[data-rate=\"${e.originalEvent.detail.rate}\"]`).find('i').addClass('bi-check');\n });\n\n // Observe timeline-wrapper width change.\n let timelineWrapper = document.getElementById('timeline-wrapper');\n let resizeObserver = new ResizeObserver(() => {\n const relWidth = $('#timeline-items').width();\n $('#minute-markers, #minute-markers-bg, #vseek').css('width', relWidth + 'px');\n });\n\n resizeObserver.observe(timelineWrapper);\n\n // Implement import content\n $(document).on('click', '#importcontent', function(e) {\n e.preventDefault();\n const importmodal = `
\n
\n
\n
\n
\n ${M.util.get_string('importcontent', 'mod_interactivevideo')}
\n \n
\n
\n
\n
\n \n \n
\n
\n
\n
`;\n $('body').append(importmodal);\n $('#importmodal').modal('show');\n\n $('#importmodal').on('hidden.bs.modal', function() {\n $('#importmodal').remove();\n });\n\n $('#importmodal').off('shown.bs.modal').on('shown.bs.modal', function() {\n // Make the modal draggable.\n $('#importmodal .modal-dialog').draggable({\n handle: \".modal-header\"\n });\n // Render the course select dropdown.\n $.ajax({\n url: M.cfg.wwwroot + '/mod/interactivevideo/ajax.php',\n method: \"POST\",\n dataType: \"text\",\n data: {\n action: 'get_taught_courses',\n sesskey: M.cfg.sesskey,\n contextid: M.cfg.contextid,\n userid: userid\n },\n success: function(data) {\n let courses = JSON.parse(data);\n // Sort courses by name.\n courses.sort((a, b) => b.fullname.localeCompare(a.fullname));\n let courseSelect = ``;\n let selectfield = `
\n \n ${courseSelect}
`;\n $('#importmodal .modal-body').append(selectfield);\n // Default current course.\n $('#importmodal #importcourse').val(course);\n $('#importmodal #importcourse').trigger('change');\n }\n });\n });\n });\n\n $(document).on('change', '#importmodal #importcourse', function() {\n $(`#importmodal .selectcm, #importmodal .select-interaction`).remove();\n $.ajax({\n url: M.cfg.wwwroot + '/mod/interactivevideo/ajax.php',\n method: \"POST\",\n dataType: \"text\",\n data: {\n action: 'get_cm_by_courseid',\n sesskey: M.cfg.sesskey,\n contextid: M.cfg.contextid,\n courseid: $(this).val()\n },\n success: function(data) {\n let cms = JSON.parse(data);\n cms.sort((a, b) => b.name.localeCompare(a.name));\n let cmSelect = ``;\n let selectfield = `
\n \n ${cmSelect}
`;\n $(`#importmodal .selectcourse`).after(selectfield);\n }\n });\n });\n\n $(document).on('change', '#importmodal #importcm', async function() {\n $(`#importmodal .select-interaction`).remove();\n $(`#importmodal #importcm`).after(`
\n
`);\n let interactions = await $.ajax({\n url: M.cfg.wwwroot + '/mod/interactivevideo/ajax.php',\n method: \"POST\",\n dataType: \"text\",\n data: {\n action: 'get_items',\n sesskey: M.cfg.sesskey,\n id: $(this).val(),\n contextid: M.cfg.contextid,\n coursecontextid: M.cfg.courseContextId\n }\n });\n interactions = JSON.parse(interactions);\n interactions = interactions.filter(x => x.type != 'skipsegment');\n if (interactions.length == 0) {\n $(`#importmodal .select-interaction`).append(`
\n ${M.util.get_string('nocontent', 'mod_interactivevideo')}
`);\n return;\n }\n\n $(`#importmodal .select-interaction`).append(`
\n
\n \n
\n \n \n
`);\n\n interactions = interactions.map(int => {\n // Get the icon and check if the interaction is out of range (start, end time);\n const ctype = contentTypes.find(y => y.name === int.type);\n int.prop = JSON.stringify(ctype);\n int.icon = ctype.icon;\n if ((int.timestamp > end || int.timestamp < start) && int.timestamp > 0) {\n int.outside = true;\n } else {\n int.outside = false;\n }\n // Check if the interaction can be added (e.g. annotation content type can only be added once per activity);\n if (!ctype.allowmultiple && annotations.find(x => x.type == int.type)) {\n int.disabled = true;\n }\n return int;\n });\n\n interactions.sort((a, b) => a.timestamp - b.timestamp);\n interactions.forEach(int => {\n const inputgroup = `
\n
\n \n
\n \n \n
`;\n $(`#importmodal .select-interaction`).append(inputgroup);\n });\n\n $(document).off('click', '#importmodal #importcontentbutton').on('click', '#importmodal #importcontentbutton',\n async function(e) {\n e.preventDefault();\n let $selected = $(`#importmodal .select-interaction input[type=\"checkbox\"]:checked`);\n let selectedInt = [];\n $selected.each(function() {\n let $row = $(this).closest('.input-group');\n const name = $row.find('.name').val();\n if (name.trim() == '') {\n return;\n }\n let timestamp = $row.find('.timestamp-input').val();\n if (timestamp == '') {\n return;\n }\n\n if (Number(timestamp) < 0) {\n timestamp = Number(timestamp);\n } else {\n const parts = timestamp.split(':');\n timestamp = Number(parts[0]) * 3600 + Number(parts[1]) * 60 + Number(parts[2]);\n if (annotations.find(x => x.timestamp == timestamp)) {\n return;\n }\n }\n let id = $row.data('id');\n let int = interactions.find(x => x.id == id);\n int.title = name;\n int.timestamp = timestamp;\n let xp = Number($row.find('.xp').val());\n if (isNaN(xp) || xp == '') {\n xp = 0;\n }\n int.xp = xp;\n selectedInt.push(int);\n });\n if (selectedInt.length == 0) {\n addNotification(M.util.get_string('selectinteraction', 'mod_interactivevideo'), 'danger');\n return;\n } else {\n let interactions = await $.ajax({\n url: M.cfg.wwwroot + '/mod/interactivevideo/ajax.php',\n method: \"POST\",\n dataType: \"text\",\n data: {\n action: 'import_annotations',\n sesskey: M.cfg.sesskey,\n contextid: M.cfg.contextid,\n annotations: JSON.stringify(selectedInt),\n tocourse: M.cfg.courseId,\n fromcourse: $('#importcourse').val(),\n tocm: interaction,\n fromcm: $('#importcm').val(),\n module: coursemodule\n }\n });\n interactions = JSON.parse(interactions);\n\n // Dismiss modal.\n $('#importmodal').modal('hide');\n\n // Add the imported annotations to the current annotations.\n annotations = annotations.concat(interactions);\n dispatchEvent('annotationupdated', {\n annotations: annotations,\n action: 'import'\n });\n\n // Get interaction that allowmultiple false and init each one.\n interactions.forEach(int => {\n if (!int.allowmultiple) {\n ctRenderer[int.type].init();\n }\n });\n }\n });\n });\n }\n };\n});"],"names":["define","$","addToast","Notification","player","totaltime","currentTime","dispatchEvent","ctRenderer","playerReady","replaceProgressBars","percentage","css","renderVideoNav","async","annos","start","length","empty","remove","forEach","render","x","type","renderItemOnVideoNavigation","time","getCurrentTime","init","url","coursemodule","interaction","course","end","coursecontextid","displayoptions","userid","addNotification","msg","add","Number","isNaN","contentTypes","annotations","convertSecondsToHMS","s","dynamic","rounded","Math","round","hours","floor","minutes","seconds","parseFloat","toFixed","activeid","renderAnnotationItems","removeClass","html","M","util","get_string","addClass","sort","a","b","timestamp","item","listItem","clone","renderEditItem","xp","filter","map","reduce","text","activeAnno","find","id","postEditCallback","getAnnotations","getItems","ajax","cfg","wwwroot","method","dataType","data","action","sesskey","contextid","courseContextId","getContentTypes","when","done","items","contenttypes","JSON","parse","y","name","getRenderers","Promise","resolve","count","require","amdmodule","Type","prop","stringify","then","catch","validateTimestampFormat","fld","existing","test","val","validateTimeStartEnd","checkduration","checkexisting","checkskipsegment","parts","split","message","skip","annotation","title","runInteraction","modal","not","pause","onReady","support","playbackrate","t","getDuration","min","ratio","usefixedratio","aspectratio","posterImage","relWidth","width","startPercentage","newStart","append","i","marker","onEnded","focus","currentAnnotation","tooltip","setTimeout","onSeek","onPlayingInterval","visualized","onPlaying","audio","visualizer","intervalFunction","thisTime","isPlaying","isEnded","cancelAnimationFrame","seek","stop","scrollBar","document","getElementById","rect","getBoundingClientRect","left","right","window","innerWidth","scrollIntoView","behavior","block","inline","frequency","animate","requestAnimationFrame","onPause","VideoPlayer","on","e","detail","originalEvent","updated","push","status","removeAttr","attr","preventDefault","ctype","this","contenttype","hastimestamp","allowmultiple","addAnnotation","closest","editAnnotation","cloneAnnotation","deleteCancelPromise","deleteAnnotation","saveCancel","theAnnotation","play","stopImmediatePropagation","offsetX","trigger","hide","siblings","initialValue","key","show","hasClass","field","value","success","appendTimestampMarker","formattedTime","targetAnnotation","draggable","resizable","event","ui","position","skipduration","abs","originalPosition","originalSize","size","direction","off","height","localStorage","setItem","drag","offset","playerWidth","getItem","timelineHeight","$scrollbar","parentOffset","relX","pageX","currentLevel","newLevel","parseInt","timelineElement","scrollWidth","clientWidth","ctrlKey","metaKey","deltaY","addEventListener","scrollLeft","style","scrollHeight","draftAnnotations","confirmationMessage","returnValue","rate","setRate","timelineWrapper","resizeObserver","ResizeObserver","observe","importmodal","handle","courses","fullname","localeCompare","courseSelect","shortname","selectfield","courseid","cms","cmSelect","cm","after","interactions","int","icon","outside","disabled","inputgroup","hascompletion","$selected","selectedInt","each","$row","trim","tocourse","courseId","fromcourse","tocm","fromcm","module","concat"],"mappings":"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"} \ No newline at end of file diff --git a/amd/build/mod_form.min.js b/amd/build/mod_form.min.js index e17b4d7..e21d8fe 100644 --- a/amd/build/mod_form.min.js +++ b/amd/build/mod_form.min.js @@ -5,6 +5,6 @@ * @copyright 2024 Sokunthearith Makara * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -define("mod_interactivevideo/mod_form",["jquery","core/notification","core_form/modalform","core/str"],(function($,notification,ModalForm,str){return{init:function(id,usercontextid){let totaltime,player,videowrapper=$("#video-wrapper"),endinput=$("input[name=end]"),startinput=$("input[name=start]"),startassistinput=$("input[name=startassist]"),endassistinput=$("input[name=endassist]"),totaltimeinput=$("input[name=totaltime]"),videourlinput=$("input[name=videourl]"),sourceinput=$("input[name=source]"),videoinput=$("input[name=video]"),uploadfield=$("#fitem_id_upload"),deletefield=$("#fitem_id_delete"),videofile=$("input[name=videofile]"),videotype=$("input[name=type]"),posterimage=$("input[name=posterimage]");const convertSecondsToHMS=s=>{let hours=Math.floor(s/3600),minutes=Math.floor((s-3600*hours)/60),seconds=s-3600*hours-60*minutes;seconds=seconds.toFixed(2);let result=hours<10?"0"+hours:hours;return result+=":"+(minutes<10?"0"+minutes:minutes),result+=":"+(seconds<10?"0"+seconds:seconds),result};$(document).on("iv:playerReady",(function(){!async function(){videowrapper.show();let ratio=player.aspectratio;ratio<1&&(ratio=1),$("#video-wrapper").css("padding-bottom",1/ratio*100+"%");const duration=await player.getDuration();totaltime=Number(duration.toFixed(2)),totaltimeinput.val(totaltime),Number(endinput.val())>0&&Number(endinput.val())>totaltime&&(endinput.val(totaltime),endassistinput.val(convertSecondsToHMS(totaltime))),Number(startinput.val())>0&&Number(startinput.val())>totaltime&&(startinput.val(0),startassistinput.val("00:00:00.00")),"00:00:00.00"!=endassistinput.val()&&""!=endassistinput.val()||(endassistinput.val(convertSecondsToHMS(totaltime)),endinput.val(totaltime)),$("#videototaltime").text("<= "+convertSecondsToHMS(totaltime)),posterimage.val(player.posterImage)}()})),$(document).on("iv:playerError",(async function(){let strings=await str.get_strings([{key:"thereisanissueloadingvideo",component:"mod_interactivevideo"}]);videourlinput.addClass("is-invalid"),videourlinput.after('")}));videourlinput.on("input",(async function(){videourlinput.removeClass("is-invalid"),videourlinput.next(".form-control-feedback").remove(),videotype.val(""),player&&player.destroy();let url=$(this).val().trim();if(""==url)return void videowrapper.hide();let regex=/(?:https?:\/\/)?(?:www\.)?(?:youtube.com|youtu.be)(?:\/embed\/|\/watch\?v=|\/)([^/]+)/g,match=regex.exec(url);if(match)return videowrapper.show(),videowrapper.html('
'),videotype.val("yt"),void require(["mod_interactivevideo/player/yt"],(function(VP){player=new VP(url,0,null,!0,!1,!0)}));regex=/(?:https?:\/\/)?(?:www\.)?(?:vimeo\.com)\/(?:channels\/[A-Za-z0-9]+\/|)([^\/]+)/g,match=regex.exec(url);let vid=match?match[1]:null;if(vid)return url="https://vimeo.com/"+vid,videourlinput.val(url),videowrapper.html('
'),videotype.val("vimeo"),void require(["mod_interactivevideo/player/vimeo"],(function(VP){player=new VP(url,0,null,!0)}));if(regex=/(?:https?:\/\/)?(?:www\.)?(?:dai\.ly|dailymotion\.com)\/(?:embed\/video\/|video\/|)([^\/]+)/g,match=regex.exec(url),match)return videowrapper.show(),videowrapper.html('
'),videotype.val("dailymotion"),void require(["mod_interactivevideo/player/dailymotion"],(function(VP){player=new VP(url,0,null,!0)}));match=/(?:https?:\/\/)?(?:www\.)?(?:wistia\.com)\/medias\/([^/]+)/g.exec(url);if(match?match[1]:null)return videowrapper.show(),videotype.val("wistia"),void require(["mod_interactivevideo/player/wistia"],(function(VP){player=new VP(url,0,null,!0)}));if(await(url=>new Promise((resolve=>{document.querySelector("video")&&document.querySelector("video").remove();let video=document.createElement("video");video.src=url,video.addEventListener("canplay",(function(){resolve(!0)})),video.addEventListener("error",(function(){resolve(!1)}))})))(url))return videowrapper.html(''),videotype.val("html5video"),void require(["mod_interactivevideo/player/html5video"],(function(VP){player=new VP(url,0,null,!0)}));const strings=await str.get_strings([{key:"invalidvideourl",component:"mod_interactivevideo"},{key:"error",component:"core"}]);notification.alert(strings[1],strings[0]),videowrapper.hide()})),startassistinput.on("change blur",(async function(){if(startassistinput.removeClass("is-invalid"),startassistinput.next(".form-control-feedback").remove(),""==startassistinput.val())return;let strings=await str.get_strings([{key:"starttimelesstotaltime",component:"mod_interactivevideo"},{key:"starttimelessthanendtime",component:"mod_interactivevideo"},{key:"invalidtimestampformat",component:"mod_interactivevideo"}]);const parts=startassistinput.val().split(":");let time=3600*Number(parts[0])+60*Number(parts[1])+Number(parts[2]);startinput.val(time),Number(startinput.val())>totaltime?(startassistinput.addClass("is-invalid"),startassistinput.after('"),startassistinput.val(convertSecondsToHMS(totaltime))):Number(endinput.val())&&0!=Number(endinput.val())&&Number(startinput.val())>Number(endinput.val())?(startassistinput.addClass("is-invalid"),startassistinput.after('"),startassistinput.val(convertSecondsToHMS(endinput.val()))):Number(startinput.val())>=Number(endinput.val())&&endassistinput.val(convertSecondsToHMS(0))})),endassistinput.on("change blur",(async function(){if(endassistinput.removeClass("is-invalid"),endassistinput.next(".form-control-feedback").remove(),""==endassistinput.val())return;const strings=await str.get_strings([{key:"endtimelesstotaltime",component:"mod_interactivevideo"},{key:"endtimegreaterstarttime",component:"mod_interactivevideo"},{key:"invalidtimestampformat",component:"mod_interactivevideo"}]),parts=endassistinput.val().split(":"),time=3600*Number(parts[0])+60*Number(parts[1])+Number(parts[2]);endinput.val(time),Number(endinput.val())>totaltime?(endassistinput.addClass("is-invalid"),endassistinput.after('"),endassistinput.val(convertSecondsToHMS(totaltime))):Number(startinput.val())&&Number(endinput.val())