3 lines
78 KiB
JavaScript
3 lines
78 KiB
JavaScript
define("local_treestudyplan/report-viewer-components",["exports","./simpleline/simpleline","core/str","./util/string-helper","./util/date-helper","core/ajax","core/notification","./util/svgarc","./util/debugger","core/config","./studyplan-processor","./treestudyplan-components"],(function(_exports,_simpleline,_str,_stringHelper,_dateHelper,_ajax,_notification,_svgarc,_debugger,_config,_studyplanProcessor,_treestudyplanComponents){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_notification=_interopRequireDefault(_notification),_debugger=_interopRequireDefault(_debugger),_config=_interopRequireDefault(_config),_treestudyplanComponents=_interopRequireDefault(_treestudyplanComponents);const π=Math.PI;var _default={install(Vue){Vue.use(_treestudyplanComponents.default);let debug=new _debugger.default("treestudyplan-viewer"),lastCaller=null;function scrollCurrentIntoView(handle){const elCurrentHeader=document.querySelector(".r-studyplan-scrollable").querySelector(".s-studyline-header-period.current");!elCurrentHeader||handle&&handle==lastCaller||(lastCaller=handle,elCurrentHeader.scrollIntoView({behavior:"smooth",block:"start",inline:"center"}))}let strings=(0,_stringHelper.load_strings)({report:{loading:"loadinghelp@core",studyplan_past:"studyplan_past",studyplan_present:"studyplan_present",studyplan_future:"studyplan_future",back:"back"},invalid:{error:"error"},grading:{ungraded:"ungraded",graded:"graded",allgraded:"allgraded",unsubmitted:"unsubmitted",nogrades:"nogrades",unknown:"unknown"},completion:{completed:"completion_completed",incomplete:"completion_incomplete",completed_pass:"completion_passed",completed_fail:"completion_failed",ungraded:"ungraded",aggregation_all:"aggregation_all",aggregation_any:"aggregation_any",aggregation_one:"aggregation_one",aggregation_overall_all:"aggregation_overall_all",aggregation_overall_any:"aggregation_overall_any",aggregation_overall_one:"aggregation_overall_one",completion_not_configured:"completion_not_configured",configure_completion:"configure_completion",view_completion_report:"view_completion_report",completion_incomplete:"completion_incomplete",completion_failed:"completion_failed",completion_pending:"completion_pending",completion_progress:"completion_progress",completion_completed:"completion_completed",completion_good:"completion_good",completion_excellent:"completion_excellent",view_feedback:"view_feedback",coursetiming_past:"coursetiming_past",coursetiming_present:"coursetiming_present",coursetiming_future:"coursetiming_future",required_goal:"required_goal",student_not_tracked:"student_not_tracked",completion_not_enabled:"completion_not_enabled"},badge:{share_badge:"share_badge",dateissued:"dateissued",dateexpire:"dateexpire",badgeinfo:"badgeinfo",badgeissuedstats:"badgeissuedstats",completion_incomplete:"completion_incomplete_badge",completion_completed:"completion_completed_badge",completioninfo:"completioninfo",badgedisabled:"badgedisabled"},course:{completion_incomplete:"completion_incomplete",completion_failed:"completion_failed",completion_pending:"completion_pending",completion_progress:"completion_progress",completion_completed:"completion_completed",completion_good:"completion_good",completion_excellent:"completion_excellent",view_feedback:"view_feedback",coursetiming_past:"coursetiming_past",coursetiming_present:"coursetiming_present",coursetiming_future:"coursetiming_future",required_goal:"required_goal",student_not_tracked:"student_not_tracked",not_enrolled:"not_enrolled"},teachercourse:{select_conditions:"select_conditions",select_grades:"select_grades",coursetiming_past:"coursetiming_past",coursetiming_present:"coursetiming_present",coursetiming_future:"coursetiming_future",grade_include:"grade_include",grade_require:"grade_require",required_goal:"required_goal",student_from_plan_enrolled:"student_from_plan_enrolled",students_from_plan_enrolled:"students_from_plan_enrolled"},pageinfo:{edit:"period_edit",fullname:"studyplan_name",shortname:"studyplan_shortname",startdate:"studyplan_startdate",enddate:"studyplan_enddate",description:"studyplan_description",duration:"studyplan_duration",details:"studyplan_details"}});function isVisible(elem){return!!(elem.offsetWidth||elem.offsetHeight||elem.getClientRects().length)}const ItemEventBus=new Vue;Vue.component("r-progress-circle",{props:{value:{type:Number},max:{type:Number,default:100},min:{type:Number,default:0},stroke:{type:Number,default:.2},bgopacity:{type:Number,default:.2},title:{type:String,default:""},icon:{type:String}},data:()=>({selectedstudyplan:null}),computed:{range(){return this.max-this.min},fraction(){return this.max-this.min==0?0:(this.value-this.min)/(this.max-this.min)},radius(){return 50-50*this.stroke},arcpath(){let fraction=0;const r=50-50*this.stroke;this.max-this.min!=0&&(fraction=(this.value-this.min)/(this.max-this.min));const Δ=2*fraction*π;return(0,_svgarc.svgarcpath)([50,50],[r,r],[0,Δ],1.5*π)}},methods:{},template:'\n <div style="display: inline-block; width: 1em; height: 1em; position:relative; padding: 0;">\n <svg width="1em" height="1em" viewBox="0 0 100 100"\n style="position: absolute;top: 0;left: 0;">\n <title>{{title}}</title>\n <circle v-if="fraction >= 1.0" cx="50" cy="50" :r="radius"\n :style="\'opacity: 1; stroke-width: \'+ (stroke*100)+\'; stroke: currentcolor; fill: none;\'"/>\n <g v-else>\n <circle cx="50" cy="50" :r="radius"\n :style="\'opacity: \' + bgopacity + \';stroke-width: \'+ (stroke*100)+\'; stroke: currentcolor; fill: none;\'"/>\n <path :d="arcpath"\n :style="\'stroke-width: \' + (stroke*100) +\'; stroke: currentcolor; fill: none;\'"/>\n </g>\n </svg>\n <i v-if=\'icon\' :class="\'fa fa-\'+icon"\n style=" position: absolute; top: 0.42em; left: 0.43em; text-align:center; display: inline-block;\n width:1em; font-size: 0.55em; "\n ></i>\n </div>\n '}),Vue.component("r-report",{props:{invitekey:{type:String,default:()=>null},userid:{type:Number,default:()=>0},type:{type:String,default:()=>"own"}},data:()=>({text:strings.report,studyplans:{past:[],present:[],future:[]},selectedstudyplan:null,loadingstudyplan:!1,loading:!0}),computed:{teachermode(){return"teaching"==this.type},guestmode(){return"invited"==this.type},verified_type(){return["invited","other","teaching","own"].includes(this.type)?this.type:"own"},studyplancount(){return this.studyplans.past.length+this.studyplans.present.length+this.studyplans.future.length}},updated(){},mounted(){this.loadStudyplans()},methods:{call_args(o){const args={};return"object"!=typeof o||Array.isArray(o)||null===o||(0,_studyplanProcessor.objCopy)(args,o),"invited"==this.verified_type?args.invitekey=this.invitekey:"other"==this.verified_type&&(args.userid=this.userid),args},loadStudyplans(){const self=this;this.loading=!0,(0,_ajax.call)([{methodname:`local_treestudyplan_list_${this.verified_type}_studyplans`,args:this.call_args()}])[0].done((function(response){const plans={future:[],present:[],past:[]};for(const ix in response){const plan=response[ix];plans[(0,_dateHelper.studyplanTiming)(plan)].push(plan)}for(const ix in plans)plans[ix].sort(((a,b)=>{const t=new Date(b.startdate).getTime()-new Date(a.startdate).getTime();return 0==t&&(t=a.name.localeCompare(b.name)),t}));self.studyplans=plans,self.loading=!1,1==self.studyplans.present.length?self.selectStudyplan(self.studyplans.present[0]):1==this.studyplancount&&(self.studyplans.future.lengh>0?self.selectStudyplan(self.studyplans.future[0]):self.selectStudyplan(self.studyplans.past[0]))})).fail(_notification.default.exception)},selectStudyplan(plan){const self=this;this.loadingstudyplan=!0,(0,_ajax.call)([{methodname:`local_treestudyplan_get_${this.verified_type}_studyplan`,args:this.call_args({studyplanid:plan.id})}])[0].done((function(response){self.selectedstudyplan=(0,_studyplanProcessor.ProcessStudyplan)(response),self.loadingstudyplan=!1})).fail(_notification.default.exception)},deselectStudyplan(){this.selectedstudyplan=null,this.loadStudyplans()}},template:"\n <div class='t-studyplan-container r-report-tabs'>\n <div v-if='loading' class=\"vue-loader spinner-border text-primary\" role=\"status\"></div>\n <template v-else>\n <div v-if='!loadingstudyplan && selectedstudyplan'>\n <div class=\"mb-2\">\n <a href='#' @click='deselectStudyplan'><h5 class=\"d-inline\"><i class=\"fa fa-chevron-left\"></i> {{ text.back }}</h5></a>\n <h4 class=\"d-inline ml-3\">{{ selectedstudyplan.name}}\n <s-studyplan-details \n class=\"mb-2\"\n size=\"sm\"\n v-model=\"selectedstudyplan\" \n v-if=\"selectedstudyplan.description\"\n ><i class='fa fa-info-circle'></i></s-studyplan-details></h4>\n </div>\n <r-studyplan\n v-model='selectedstudyplan'\n :guestmode='guestmode'\n :teachermode='teachermode'\n ></r-studyplan>\n </div>\n <div v-else-if='loadingstudyplan' class=\"vue-loader spinner-border text-primary\" role=\"status\">\n <span class=\"sr-only\">{{ text.loading }}</span>\n </div>\n <div v-else class='t-studyplan-notselected'>\n <template v-for=\"timing in ['present', 'past', 'future']\">\n <template v-if=\"studyplans[timing].length > 0\">\n <h4>{{ text[\"studyplan_\"+timing]}}:</h4>\n <b-card-group deck>\n <s-studyplan-card\n v-for='(studyplan, planindex) in studyplans[timing]'\n :key='studyplan.id'\n v-model='studyplans[timing][planindex]'\n open\n @open='selectStudyplan(studyplan)'\n ></s-studyplan-card>\n </b-card-group>\n </template>\n </template>\n </div>\n </template>\n </div>\n "}),Vue.component("r-studyplan",{props:{value:{type:Object},guestmode:{type:Boolean,default:!1},teachermode:{type:Boolean,default:!1}},data:()=>({selectedpageindex:-1,text:strings.pageinfo}),computed:{selectedpage(){return this.value.pages[this.selectedpageindex]},startpageindex(){let startpageindex=0,firststart=null;for(const ix in this.value.pages){const page=this.value.pages[ix];if(debug.info(`Checking page ${ix} - timing ${(0,_dateHelper.studyplanPageTiming)(page)}`,page),"present"==(0,_dateHelper.studyplanPageTiming)(page)){debug.info(`Found page nr ${ix} to be present`);const s=new Date(page.startdate);(!firststart||firststart>s)&&(startpageindex=ix,firststart=s)}}return startpageindex}},methods:{pageduration:page=>(0,_dateHelper.format_date)(page.startdate,!1)+" - "+(0,_dateHelper.format_date)(page.enddate,!1),columns:page=>1+2*page.periods,columns_stylerule(page){let s="grid-template-columns: var(--studyplan-filter-width)";for(let i=0;i<page.periods;i++)s+=" var(--studyplan-course-width) var(--studyplan-filter-width)";return s+";"},countLineLayers(line,page){let maxLayer=-1;for(let i=0;i<=page.periods;i++){line.slots[i];for(const ix in line.slots[i].courses){const item=line.slots[i].courses[ix];item.layer>maxLayer&&(maxLayer=item.layer)}for(const ix in line.slots[i].filters){const item=line.slots[i].filters[ix];item.layer>maxLayer&&(maxLayer=item.layer)}}return maxLayer>=0?maxLayer+1:1},showslot(page,line,index,layeridx,type){const forGradable="gradable"==type,periods=page.periods;let show=!0;for(let i=0;i<periods;i++)if(line.slots[index-i]&&line.slots[index-i].courses){const list=line.slots[index-i].courses;for(const ix in list){const item=list[ix];item.layer==layeridx&&(forGradable?i>0&&item.span-i>0&&(show=!1):item.span-i>1&&(show=!1))}}return show},selectedpageChanged(newTabIndex,prevTabIndex){ItemEventBus.$emit("redrawLines",null),scrollCurrentIntoView(this.selectedpage.id)}},mounted(){this.$root.$emit("redrawLines")},updated(){scrollCurrentIntoView(this.selectedpage.id),ItemEventBus.$emit("lineHeightChange",null),this.$root.$emit("redrawLines"),ItemEventBus.$emit("redrawLines")},template:'\n <div>\n <b-card no-body>\n <b-tabs \n v-model=\'selectedpageindex\' \n @activate-tab=\'selectedpageChanged\'\n content-class="mt-1">\n <b-tab\n v-for="(page,pageindex) in value.pages"\n :active="(pageindex == startpageindex)"\n :key="page.id"\n :title-item-class="\'s-studyplanpage-tab \'+ page.timing"\n ><template #title>\n <span v-b-tooltip.hover :title=\'page.fullname\'>{{page.shortname}}</span>\n <a href="#" v-b-modal="\'studyplanpage-info-\'+page.id" class=\'text-info\'\n v-if=\'pageindex == selectedpageindex\'\n ><i class=\'fa fa-info-circle\'></i></a>\n </template>\n \n <b-modal\n :id="\'studyplanpage-info-\'+page.id"\n scrollable\n ok-only\n >\n <template #modal-title>\n {{page.fullname}}\n </template>\n <b-container>\n <b-row>\n <b-col cols="4"><b>{{ text.shortname}}</b></b-col>\n <b-col cols="8">\n {{ page.shortname }}\n </b-col>\n </b-row>\n <b-row>\n <b-col cols="4"><b>{{ text.duration}}</b></b-col>\n <b-col cols="8">\n {{ pageduration(page) }}\n </b-col>\n </b-row>\n <b-row v-if="page.description">\n <b-col cols="12"><b>{{ text.description}}</b></b-col>\n </b-row>\n <b-row v-if="page.description">\n <b-col cols="12">\n <span v-html="page.description"></span>\n </b-col>\n </b-row>\n </b-container>\n </b-modal>\n <div v-if="page.studylines.length > 0" class=\'r-studyplan-content\'>\n \x3c!-- First paint the headings--\x3e\n <div class=\'r-studyplan-headings\'\n ><s-studyline-header-heading :identifier="Number(page.id)"></s-studyline-header-heading>\n <r-studyline-heading v-for="(line,lineindex) in page.studylines"\n :key="line.id"\n v-model="page.studylines[lineindex]"\n :layers=\'countLineLayers(page,line)+1\'\n :class=" \'t-studyline\' + ((lineindex%2==0)?\' odd \' :\' even \' )\n + ((lineindex==0)?\' first \':\' \')\n + ((lineindex==page.studylines.length-1)?\' last \':\' \')"\n ></r-studyline-heading\n ></div>\n \x3c!-- Next, paint all the cells in the scrollable --\x3e\n <div class="r-studyplan-scrollable" >\n <div class="r-studyplan-timeline" :style="columns_stylerule(page)">\n \x3c!-- add period information --\x3e\n <template v-for="(n,index) in (page.periods+1)">\n <s-studyline-header-period\n v-if="index > 0"\n v-model="page.perioddesc[index-1]"\n :identifier="Number(page.id)"\n ></s-studyline-header-period>\n <div class="s-studyline-header-filter"></div>\n </template>\n\n \x3c!-- Line by line add the items --\x3e\n \x3c!-- The grid layout handles putting it in rows and columns --\x3e\n <template v-for="(line,lineindex) in page.studylines"\n ><template v-for="(layernr,layeridx) in countLineLayers(page,line)"\n ><template v-for="(n,index) in (page.periods+1)"\n ><r-studyline-slot\n v-if="index > 0 && showslot(page,line, index, layeridx, \'gradable\')"\n type=\'gradable\'\n v-model="line.slots[index].courses"\n :key="\'c-\'+lineindex+\'-\'+index+\'-\'+layernr"\n :slotindex="index"\n :line="line"\n :plan="value"\n :page="page"\n :period="page.perioddesc[index-1]"\n :guestmode=\'guestmode\'\n :teachermode=\'teachermode\'\n :layer="layeridx"\n :class="\'t-studyline \' + ((lineindex%2==0)?\' odd \':\' even \')\n + ((lineindex==0 && layernr==1)?\' first \':\' \')\n + ((lineindex==page.studylines.length-1)?\' last \':\' \')"\n ></r-studyline-slot\n ><r-studyline-slot\n v-if="showslot(page,line, index, layeridx, \'gradable\')"\n type=\'filter\'\n v-model="line.slots[index].filters"\n :teachermode=\'teachermode\'\n :key="\'f-\'+lineindex+\'-\'+index+\'-\'+layernr"\n :slotindex="index"\n :line="line"\n :plan="value"\n :page="page"\n :layer="layeridx"\n :class="\'t-studyline \' + ((lineindex%2==0)?\' odd \':\' even \')\n + ((lineindex==0 && layernr==1)?\' first \':\'\')\n + ((lineindex==page.studylines.length-1)?\' last \':\' \')\n + ((index==page.periods)?\' rightmost\':\'\')"\n >\n </r-studyline-slot\n ></template\n ></template\n ></template\n ></div\n ></div\n ></div>\n </b-tab>\n </b-tabs>\n </b-card>\n </div>\n '}),Vue.component("r-studyline-heading",{props:{value:{type:Object,default:function(){return{}}},layers:{type:Number,default:1}},data:()=>({layerHeights:{}}),created(){ItemEventBus.$on("lineHeightChange",this.onLineHeightChange)},computed:{},methods:{onLineHeightChange(lineid){if(this.$refs.mainEl&&(lineid==this.value.id||null===lineid)){const items=document.querySelectorAll(`.r-studyline-slot-0[data-studyline='${this.value.id}']`);let heightSum=0;items.forEach((el=>{const height=el.getBoundingClientRect().height;heightSum+=height}));const heightStyle=`${heightSum}px`;this.$refs.mainEl.style.height=heightStyle}}},template:'\n <div class="r-studyline r-studyline-heading "\n :data-studyline="value.id" ref="mainEl"\n ><div class="r-studyline-handle" :style="\'background-color: \' + value.color"></div>\n <div class="r-studyline-title">\n <abbr v-b-tooltip.hover :title="value.name">{{ value.shortname }}</abbr>\n </div>\n </div>\n '}),Vue.component("r-studyline-slot",{props:{value:{type:Array,default:()=>[]},type:{type:String,default:"gradable"},slotindex:{type:Number,default:0},line:{type:Object,default:()=>null},layer:{type:Number},plan:{type:Object,default:()=>null},page:{type:Object,default:()=>null},guestmode:{type:Boolean,default:!1},teachermode:{type:Boolean,default:!1},period:{type:Object,default:()=>null}},mounted(){const self=this;"gradable"==self.type&&1==self.slotindex&&(self.resizeListener=new ResizeObserver((()=>{if(self.$refs.sizeElement){const height=self.$refs.sizeElement.getBoundingClientRect().height;ItemEventBus.$emit("lineHeightChange",self.line.id,self.layer,height)}})).observe(self.$refs.sizeElement))},computed:{item(){for(const ix in this.value){const itm=this.value[ix];if(itm.layer==this.layer)return itm}return null},current(){if(this.period&&this.period.startdate&&this.period.enddate){const now=new Date,pstart=new Date(this.period.startdate),pend=new Date(this.period.enddate);return now>=pstart&&now<pend}return!1},spanCss(){if(this.item&&this.item.span>1){return`width: 100%; grid-column: span ${2*this.item.span-1};`}return""}},data:()=>({}),methods:{},template:"\n <div :class=\" 'r-studyline-slot ' + type + ' ' + (current?'current ':' ')\n + 'r-studyline-slot-' + slotindex + ' '\n + ((slotindex==0)?'r-studyline-firstcolumn ':' ')\"\n :data-studyline=\"line.id\" ref=\"sizeElement\"\n :style='spanCss'\n ><div class=\"r-slot-item\" v-if=\"item\"\n ><r-item\n v-model=\"item\"\n :plan=\"plan\"\n :guestmode='guestmode'\n :teachermode='teachermode'></r-item\n ></div\n ></r-item\n ></div>\n "}),Vue.component("r-item",{props:{value:{type:Object,default:function(){return null}},plan:{type:Object,default:()=>null},guestmode:{type:Boolean,default:!1},teachermode:{type:Boolean,default:!1}},data:()=>({lines:[]}),methods:{lineColor(){if(this.teachermode)return"var(--gray)";switch(this.value.completion){default:return"var(--gray)";case"failed":return"var(--danger)";case"progress":return"var(--warning)";case"completed":return"var(--success)";case"good":return"var(--info)";case"excellent":return"var(--blue)"}},redrawLine(conn){let lineColor=this.lineColor(),start=document.getElementById("studyitem-"+conn.from_id),end=document.getElementById("studyitem-"+conn.to_id);this.lines[conn.to_id]&&(this.lines[conn.to_id].remove(),delete this.lines[conn.to_id]),null!==start&&null!==end&&isVisible(start)&&isVisible(end)&&(this.lines[conn.to_id]=new _simpleline.SimpleLine(start,end,{color:lineColor,gravity:{start:1.3,end:1.3}}))},redrawLines(){for(let ix in this.lines){let lineinfo=this.lines[ix];lineinfo&&lineinfo.line&&(lineinfo.line.remove(),lineinfo.line=void 0)}for(let i in this.value.connections.out){let conn=this.value.connections.out[i];this.redrawLine(conn)}},onWindowResize(){this.redrawLines()},onRedrawLines(){this.redrawLines()},removeLine(conn){this.lines[conn.to_id]&&(this.lines[conn.to_id].remove(),delete this.lines[conn.to_id])}},computed:{hasConnectionsOut(){return!["finish"].includes(this.value.type)},hasConnectionsIn(){return!["start"].includes(this.value.type)},hasContext(){return["start","junction","finish"].includes(this.value.type)}},created(){ItemEventBus.$on("redrawLines",this.onRedrawLines)},mounted(){this.redrawLines(),setTimeout((()=>{this.redrawLines()}),50),window.addEventListener("resize",this.onWindowResize)},beforeDestroy(){for(let i in this.value.connections.out){let conn=this.value.connections.out[i];this.removeLine(conn)}window.removeEventListener("resize",this.onWindowResize),ItemEventBus.$off("redrawLines",this.onRedrawLines)},beforeUpdate(){},updated(){this.dummy||this.redrawLines()},template:'\n <div class="r-item-base" :id="\'studyitem-\'+value.id" :data-x=\'value.type\'>\n <r-item-competency v-if="value.type == \'competency\'"\n v-model="value" :guestmode="guestmode" :teachermode="teachermode" ></r-item-competency>\n <r-item-course v-if="value.type == \'course\' && !teachermode" :plan="plan"\n v-model="value" :guestmode="guestmode" :teachermode="teachermode" ></r-item-course>\n <r-item-teachercourse v-if="value.type == \'course\' && teachermode" :plan="plan"\n v-model="value" :guestmode="guestmode" :teachermode="teachermode" ></r-item-teachercourse>\n <r-item-junction v-if="value.type == \'junction\'"\n v-model="value" :guestmode="guestmode" :teachermode="teachermode" ></r-item-junction>\n <r-item-start v-if="value.type == \'start\'"\n v-model="value" :guestmode="guestmode" :teachermode="teachermode" ></r-item-start>\n <r-item-finish v-if="value.type == \'finish\'"\n v-model="value" :guestmode="guestmode" :teachermode="teachermode" ></r-item-finish>\n <r-item-badge v-if="value.type == \'badge\'"\n v-model="value" :guestmode="guestmode" :teachermode="teachermode" ></r-item-badge>\n <r-item-invalid v-if="value.type == \'invalid\' && teachermode"\n v-model="value" ></r-item-invalid>\n </div>\n '}),Vue.component("r-item-invalid",{props:{value:{type:Object,default:function(){return null}}},data:()=>({text:strings.invalid}),methods:{},template:'\n <div class="r-item-invalid">\n <b-card no-body class="r-item-invalid">\n <b-row no-gutters>\n <b-col md="1">\n <span class="r-timing-indicator timing-invalid"></span>\n </b-col>\n <b-col md="11">\n <b-card-body class="align-items-center">\n <i class="fa fa-exclamation"></i> {{ text.error }}\n </b-card-body>\n </b-col>\n </b-row>\n </b-card>\n </div>\n '}),Vue.component("r-item-course",{props:{value:{type:Object,default:()=>null},guestmode:{type:Boolean,default:()=>!1},teachermode:{type:Boolean,default:()=>!1},plan:{type:Object,default:()=>null}},data:()=>({text:strings.course}),computed:{startdate(){return(0,_dateHelper.format_date)(this.value.course.startdate)},enddate(){return this.value.course.enddate?(0,_dateHelper.format_date)(this.value.course.enddate):this.text.noenddate}},created(){},methods:{completion_icon(completion){switch(completion){default:return"circle-o";case"pending":return"question-circle";case"failed":return"times-circle";case"progress":return"exclamation-circle";case"completed":case"good":case"excellent":return"check-circle"}},circle_icon(completion){switch(completion){default:return null;case"failed":return"times";case"progress":return"exclamation";case"completed":case"good":case"excellent":return"check"}}},template:"\n <div :class=\"'r-item-competency completion-'+value.completion\">\n <b-card no-body >\n <b-row no-gutters>\n <b-col md=\"1\">\n <span\n :title=\"text['coursetiming_'+value.course.timing]\"\n v-b-popover.hover.top=\"startdate+' - '+enddate\"\n :class=\"'r-timing-indicator timing-'+value.course.timing\"></span>\n </b-col>\n <b-col md=\"11\" class=\"align-items-center\">\n <b-card-body >\n <template v-if='!value.course.enrolled'>\n <i v-b-popover.top\n class=\"r-course-result fa fa-exclamation-triangle t-not-enrolled-alert\"\n :title=\"text.student_not_tracked\"></i>\n </template>\n <template v-else-if='value.course.completion'>\n <r-progress-circle v-if='[\"failed\", \"progress\",\"incomplete\"].includes(value.completion)'\n :value='value.course.completion.progress'\n :max='value.course.completion.count'\n :min='0'\n :class=\"'r-course-result r-completion-'+value.completion\"\n :icon='circle_icon(value.completion)'\n :title=\"text['completion_'+value.completion]\"\n ></r-progress-circle>\n <i v-else v-b-popover.top\n :class=\"'r-course-result fa fa-'+completion_icon(value.completion)+\n ' r-completion-'+value.completion\"\n :title=\"text['completion_'+value.completion]\"></i>\n </template>\n <template v-else>\n <i v-b-popover.top\n :class=\"'r-course-result fa fa-'+completion_icon(value.completion)+\n ' r-completion-'+value.completion\"\n :title=\"text['completion_'+value.completion]\"></i>\n </template>\n <a v-b-modal=\"'r-item-course-details-'+value.id\"\n :href=\"(!guestmode)?('/course/view.php?id='+value.course.id):'#'\"\n @click.prevent.stop=''\n >{{ value.course.displayname }}</i></a>\n </b-card-body>\n </b-col>\n </b-row>\n\n <b-modal\n :id=\"'r-item-course-details-'+value.id\"\n :title=\"value.course.displayname + ' - ' + value.course.fullname\"\n size=\"lg\"\n ok-only\n centered\n scrollable\n >\n <template #modal-header>\n <div>\n <h1><a :href=\"(!guestmode)?('/course/view.php?id='+value.course.id):undefined\" target=\"_blank\"\n ><i class=\"fa fa-graduation-cap\"></i> {{ value.course.fullname }}</a></h1>\n {{ value.course.context.path.join(\" / \")}}\n </div>\n <div class=\"r-course-detail-header-right\">\n <div class=\"r-completion-detail-header\">\n <template v-if='!value.course.enrolled'>\n {{text.not_enrolled}}\n <i v-b-popover.top\n class=\"r-course-result fa fa-exclamation-triangle t-not-enrolled-alert\"\n :title=\"text.student_not_tracked\"></i>\n </template>\n <template v-else-if='value.course.completion'>\n {{text['completion_'+value.completion]}}\n <r-progress-circle v-if='[\"failed\",\"progress\",\"incomplete\"].includes(value.completion)'\n :value='value.course.completion.progress'\n :max='value.course.completion.count'\n :min='0'\n :title=\"text['completion_'+value.completion]\"\n :class=\"'r-progress-circle-popup r-completion-'+value.completion\"\n :icon='circle_icon(value.completion)'\n ></r-progress-circle\n ><i v-else v-b-popover.top\n :class=\"'fa fa-'+completion_icon(value.completion)+\n ' r-progress-icon-popup r-completion-'+value.completion\"\n :title=\"text['completion_'+value.completion]\"></i>\n </template>\n <template v-else>\n {{text['completion_'+value.completion]}}\n <i :class=\"'fa fa-'+completion_icon(value.completion)+' r-completion-'+value.completion\"\n :title=\"text['completion_'+value.completion]\"></i>\n </template>\n </div>\n <div :class=\"'r-timing-'+value.course.timing\">\n {{text['coursetiming_'+value.course.timing]}}<br>\n {{ startdate }} - {{ enddate }}\n </div>\n </div>\n </template>\n <r-item-studentgrades\n v-if='!!value.course.grades && value.course.grades.length > 0'\n v-model='value'\n :guestmode='guestmode'></r-item-studentgrades>\n <r-item-studentcompletion\n v-if='!!value.course.completion'\n v-model='value.course.completion'\n :course='value.course'\n :guestmode='guestmode'></r-item-studentcompletion>\n </b-modal>\n </b-card></div>\n "}),Vue.component("r-item-studentgrades",{props:{value:{type:Object,default:function(){return{}}},guestmode:{type:Boolean,default:!1}},data:()=>({text:strings.course}),computed:{pendingsubmission(){let result=!1;for(const ix in this.value.course.grades){if(this.value.course.grades[ix].pendingsubmission){result=!0;break}}return result},useRequiredGrades(){return!(!this.plan||!this.plan.aggregation_info||void 0===this.plan.aggregation_info.useRequiredGrades)&&this.plan.aggregation_info.useRequiredGrades}},methods:{completion_icon(completion){switch(completion){default:return"circle-o";case"pending":return"question-circle";case"failed":return"times-circle";case"progress":return"exclamation-circle";case"completed":case"good":case"excellent":return"check-circle"}}},template:'\n <table class="r-item-course-grade-details">\n <tr v-for="g in value.course.grades">\n <td><span class="r-activity-icon" :title="g.typename" v-html="g.icon"></span\n ><a\n :href="(!guestmode)?(g.link):undefined" target="_blank"><span v-html="g.name"></span></a>\n <abbr v-if="useRequiredGrades && g.required" :title="text.required_goal"\n :class="\'s-required \' + g.completion"\n ><i class=\'fa fa-asterisk\' ></i\n ></abbr>\n </td>\n <td><span :class="\' r-completion-\'+g.completion">{{g.grade}}</span></td>\n <td><i :class="\'fa fa-\'+completion_icon(g.completion)+\' r-completion-\'+g.completion"\n :title="text[\'completion_\'+g.completion]"></i>\n <i v-if=\'g.pendingsubmission\' :title="text[\'completion_pending\']"\n class="r-pendingsubmission fa fa-clock-o"></i></td>\n <td v-if="g.feedback">\n <a v-b-modal="\'r-grade-feedback-\'+g.id"\n href="#"\n >{{ text["view_feedback"]}}</a>\n <b-modal\n :id="\'r-grade-feedback-\'+g.id"\n size="sm"\n ok-only\n centered\n scrollable\n >\n <template #modal-header>\n <h2><i class="fa fa-graduation-cap"></i>{{ value.course.fullname }}</h2><br>\n <span class="r-activity-icon" :title="g.typename" v-html="g.icon"></span>{{g.name}}\n </template>\n <span v-html="g.feedback"></span>\n </b-modal>\n </td>\n </tr>\n </table>\n '}),Vue.component("r-item-studentcompletion",{props:{value:{type:Object,default:function(){return{}}},guestmode:{type:Boolean,default:!1},course:{type:Object,default:function(){return{}}}},data:()=>({text:strings.completion}),created(){},computed:{},methods:{completion_icon(completion){switch(completion){case"progress":return"exclamation-circle";case"complete":case"complete-pass":return"check-circle";case"complete-fail":return"times-circle";default:return"circle-o"}},completion_tag:cgroup=>cgroup.completion?"completed":"incomplete",hasCompletions(){if(this.value.conditions)for(const cgroup of this.value.conditions)if(cgroup.items&&cgroup.items.length>0)return!0;return!1},requirementHTML(requirements){const rqs=requirements.split(/, */);let html="";for(const ix in rqs){html+=`${rqs[ix]}<br>`}return html},addTargetBlank(html){const m=/^([^<]*\< *a +)(.*)/.exec(html);return m?`${m[1]} target="_blank" ${m[2]}`:html}},template:'\n <table class="r-item-course-grade-details">\n <tr v-if="hasCompletions">\n <td colspan=\'2\'\n ><span v-if="value.conditions.length <= 1">{{ text.aggregation_overall_one }}</span\n ><span v-else if="value.aggregation == \'all\'">{{ text.aggregation_overall_all}}</span\n ><span v-else>{{ text.aggregation_overall_any }}</span\n ></td>\n </tr>\n <tr v-else>\n <td colspan=\'2\'>{{text.completion_not_configured}}!\n </td>\n </tr>\n <template v-for=\'cgroup in value.conditions\' v-if=\'value.enabled && value.tracked\'>\n <tr>\n <th colspan=\'2\'><span v-if="cgroup.items.length > 1"\n ><span v-if="cgroup.aggregation == \'all\'">{{ text.aggregation_all}}</span\n ><span v-else>{{ text.aggregation_any}}</span></span\n ><span v-else>{{ text.aggregation_one }}</span>\n {{ cgroup.title.toLowerCase() }}:\n </th>\n <th><r-progress-circle v-if="cgroup.progress < cgroup.count"\n :value=\'cgroup.progress\'\n :max=\'cgroup.count\'\n :class="\'r-completion-\'+cgroup.status"\n :title="text[\'completion_\'+cgroup.status]"\n ></r-progress-circle>\n <i v-else :class="\'fa fa-check-circle r-completion-\'+cgroup.status"></i>\n </th>\n </tr>\n <tr v-for=\'ci in cgroup.items\'>\n <td><span v-if=\'guestmode\'>{{ci.title}}</span>\n <span v-else v-html=\'addTargetBlank(ci.details.criteria)\'></span>\n <a href="#" v-b-tooltip.click.hover.right.html="{ customClass: \'r-tooltip \' + ci.status}"\n :title="requirementHTML(ci.details.requirement)"\n class="text-primary"><i v-if="ci.details.requirement"\n class=\'fa fa-question-circle\'\n ></i></a>\n <td\n ><span :class="\' r-completion-\'+ci.status">{{ci.grade}}</span>\n <span v-if="ci.warning"\n ><i class="text-primary fa fa-exclamation-triangle"\n v-b-tooltip.hover.right.click="{ customClass: \'r-tooltip info\' }"\n :title="ci.warning"\n ></i\n ></span\n ></td>\n <td><i :class="\'fa fa-\'+completion_icon(ci.status)+\' r-completion-\'+ci.status"\n :title="text[\'completion_\'+ci.status]"></i>\n <i v-if=\'ci.pending\' :title="text[\'completion_pending\']"\n class="r-pendingsubmission fa fa-clock-o"></i>\n </td>\n <td v-if="ci.feedback">\n <a v-b-modal="\'r-grade-feedback-\'+ci.id"\n href="#"\n >{{ text["view_feedback"]}}</a>\n <b-modal\n :id="\'r-grade-feedback-\'+ci.id"\n size="sm"\n ok-only\n centered\n scrollable\n >\n <template #modal-header>\n <h2><i class="fa fa-graduation-cap"></i>{{ course.fullname }}</h2><br>\n <span class="r-activity-icon" :title="ci.typename" v-html="ci.icon"></span>{{ci.name}}\n </template>\n <span v-html="ci.feedback"></span>\n </b-modal>\n </td>\n </tr>\n </template>\n <template v-else>\n <tr v-if=\'! value.enabled\'>\n <td colspan=\'4\'>{{text.completion_not_enabled}}</td>\n </tr>\n <tr v-else>\n <td colspan=\'4\'>{{text.student_not_tracked}}</td>\n </tr>\n </template>\n </table>\n '}),Vue.component("r-item-teachercourse",{props:{value:{type:Object,default:()=>null},guestmode:{type:Boolean,default:()=>!1},teachermode:{type:Boolean,default:()=>!1},plan:{type:Object,default:()=>null}},data:()=>({text:strings.teachercourse,txt:{grading:strings.grading}}),computed:{course_grading_needed(){return this.course_grading_state()},course_grading_icon(){return this.determine_grading_icon(this.course_grading_state())},filtered_grades(){return this.value.course.grades.filter((g=>g.selected))},useRequiredGrades(){return!(!this.plan||!this.plan.aggregation_info||void 0===this.plan.aggregation_info.useRequiredGrades)&&this.plan.aggregation_info.useRequiredGrades},isCompletable(){let completable=!1;return this.value.course.completion?this.value.course.completion.conditions.length>0&&(completable=!0):this.value.course.grades&&this.value.course.grades.length>0&&(completable=!0),completable},progress_circle(){const status={students:0,completed:0,completed_pass:0,completed_fail:0,ungraded:0};if(this.value.course.completion)for(const cond of this.value.course.completion.conditions)for(const itm of cond.items)itm.progress&&(status.students+=itm.progress.students,status.completed+=itm.progress.completed,status.completed_pass+=itm.progress.completed_pass,status.completed_fail+=itm.progress.completed_fail,status.ungraded+=itm.progress.ungraded);else if(this.value.course.grades)for(const g of this.value.course.grades)g.grading&&(status.students+=g.grading.students,status.completed+=g.grading.completed,status.completed_pass+=g.grading.completed_pass,status.completed_fail+=g.grading.completed_fail,status.ungraded+=g.grading.ungraded);return status},startdate(){return(0,_dateHelper.format_date)(this.value.course.startdate)},enddate(){return this.value.course.enddate?(0,_dateHelper.format_date)(this.value.course.enddate):this.text.noenddate}},created(){},methods:{course_grading_state(){let ungraded=0,unknown=0,graded=0,allgraded=0;const grades=this.filtered_grades;if(!Array.isArray(grades)||0==grades)return"nogrades";for(const ix in grades){const grade=grades[ix];grade.grading?Number(grade.grading.ungraded)>0?ungraded++:Number(grade.grading.graded)>0&&(Number(grade.grading.graded)==Number(grade.grading.students)?allgraded++:graded++):unknown=!0}return ungraded>0?"ungraded":unknown?"unknown":graded?"graded":allgraded?"allgraded":"unsubmitted"},determine_grading_icon(gradingstate){switch(gradingstate){default:return"circle-o";case"ungraded":return"exclamation-circle";case"unknown":return"question-circle-o";case"graded":case"allgraded":return"check";case"unsubmitted":return"dot-circle-o"}}},template:'\n <div :class="\'r-item-competency \'+ (value.course.amteacher?\'r-course-am-teacher\':\'\')">\n <b-card no-body>\n <div class=\'d-flex flex-wrap mr-0 ml-0\'>\n <div>\n <span\n :title="text[\'coursetiming_\'+value.course.timing]"\n v-b-popover.hover.top="startdate+\' - \'+enddate"\n :class="\'r-timing-indicator timing-\'+value.course.timing"></span>\n </div>\n <div class="flex-fill align-items-center">\n <b-card-body>\n <a v-b-modal="\'r-item-course-details-\'+value.id"\n :href="(!guestmode)?(\'/course/view.php?id=\'+value.course.id):\'#\'"\n @click.prevent.stop=\'\'\n >{{ value.course.displayname }}</i></a>\n <r-completion-circle class="r-course-graded" :disabled="!isCompletable"\n v-model="progress_circle"></r-completion-circle>\n </b-card-body>\n </div>\n </div>\n <b-modal\n v-if="true"\n :id="\'r-item-course-details-\'+value.id"\n :title="value.course.displayname + \' - \' + value.course.fullname"\n size="lg"\n ok-only\n centered\n scrollable\n >\n <template #modal-header>\n <div>\n <h1><a :href="(!guestmode)?(\'/course/view.php?id=\'+value.course.id):undefined" target="_blank"\n ><i class="fa fa-graduation-cap"></i> {{ value.course.fullname }}</a>\n <r-item-teacher-gradepicker v-model="value"\n v-if="value.course.grades && value.course.grades.length > 0"\n :useRequiredGrades="useRequiredGrades"\n :plan="plan"\n ></r-item-teacher-gradepicker>\n <a v-if=\'!!value.course.completion && value.course.amteacher\'\n :href="\'/course/completion.php?id=\'+value.course.id" target="_blank"\n :title="text.configure_completion"><i class="fa fa-gear"></i></a>\n </h1>\n {{ value.course.context.path.join(" / ") }}\n <div class=\'mt-1 text-info\'>\n <span v-if=\'value.course.numenrolled != 1\'>{{ value.course.numenrolled }} {{ text.students_from_plan_enrolled }}</span>\n <span v-else> 1 {{ text.student_from_plan_enrolled }} </span>\n </div>\n </div>\n <div class="r-course-detail-header-right">\n <div class="r-completion-detail-header">\n <r-completion-circle class="r-progress-circle-popup" :disabled="!isCompletable"\n v-model="progress_circle"></r-completion-circle>\n </div>\n <div :class="\'r-timing-\'+value.course.timing">\n {{ text[\'coursetiming_\'+value.course.timing] }}<br>\n {{ startdate }} - {{ enddate }}\n </div>\n </div>\n </template>\n <r-item-teachergrades\n v-if=\'!!value.course.grades && value.course.grades.length > 0\'\n v-model=\'value.course\'\n :useRequiredGrades="useRequiredGrades"\n ></r-item-teachergrades>\n <r-item-teachercompletion\n v-if=\'!!value.course.completion\'\n v-model=\'value.course.completion\'\n :course=\'value.course\'\n ></r-item-teachercompletion>\n </b-modal>\n\n </b-card>\n </div>\n '}),Vue.component("r-item-teacher-gradepicker",{props:{value:{type:Object,default:function(){return{}}},useRequiredGrades:{type:Boolean,default:()=>null}},data:()=>({text:strings.teachercourse}),computed:{},methods:{includeChanged(newValue,g){(0,_ajax.call)([{methodname:"local_treestudyplan_include_grade",args:{grade_id:g.id,item_id:this.value.id,include:newValue,required:g.required}}])[0].fail(_notification.default.exception)},requiredChanged(newValue,g){(0,_ajax.call)([{methodname:"local_treestudyplan_include_grade",args:{grade_id:g.id,item_id:this.value.id,include:g.selected,required:newValue}}])[0].fail(_notification.default.exception)}},template:'\n <a v-if="value.course.canselectgradables" href=\'#\'\n v-b-modal="\'r-item-course-config-\'+value.id"\n @click.prevent.stop=\'\'\n ><i class=\'fa fa-cog\'></i>\n <b-modal v-if=\'value.course.canselectgradables\'\n :id="\'r-item-course-config-\'+value.id"\n :title="value.course.displayname + \' - \' + value.course.fullname"\n ok-only\n scrollable\n >\n <template #modal-header>\n <div>\n <h1><a :href="\'/course/view.php?id=\'+value.course.id" target="_blank"\n ><i class="fa fa-graduation-cap"></i> {{ value.course.fullname }}</a></h1>\n {{ value.course.context.path.join(" / ")}} / {{value.course.displayname}}\n </div>\n <div class="r-course-detail-header-right">\n <div :class="\'r-timing-\'+value.course.timing">\n {{text[\'coursetiming_\'+value.course.timing]}}<br>\n {{ value.course.startdate }} - {{ value.course.enddate }}\n </div>\n </div>\n </template>\n <b-form-group\n :label="text.select_grades"\n ><ul class="t-item-module-children">\n <li class="t-item-course-gradeinfo">\n <span class=\'t-item-course-chk-lbl\'>{{text.grade_include}}</span\n ><span v-if="useRequiredGrades" class=\'t-item-course-chk-lbl\'>{{text.grade_require}}</span>\n </li>\n <li class="t-item-course-gradeinfo" v-for="g in value.course.grades">\n <b-form-checkbox inline\n @change="includeChanged($event,g)" v-model="g.selected"\n ></b-form-checkbox>\n <b-form-checkbox v-if="useRequiredGrades" inline :disabled="!g.selected"\n @change="requiredChanged($event,g)" v-model="g.required"\n ></b-form-checkbox>\n <span :title="g.typename" v-html="g.icon"></span><a\n :href="g.link" target="_blank">{{g.name}}</a>\n <s-edit-mod\n :title="value.course.fullname"\n @saved="(fd) => g.name = fd.get(\'name\')"\n v-if="g.cmid > 0"\n :cmid="g.cmid"\n :coursectxid="value.course.ctxid"\n genericonly></s-edit-mod>\n </li>\n </ul>\n </b-form-group>\n </b-modal>\n </a>\n '}),Vue.component("r-item-teachergrades",{props:{value:{type:Object,default:function(){return{}}},useRequiredGrades:{type:Boolean,default:!1}},data:()=>({text:strings.teachercourse,txt:{grading:strings.grading}}),computed:{pendingsubmission(){let result=!1;for(const ix in this.value.grades){if(this.value.grades[ix].pendingsubmission){result=!0;break}}return result},filtered_grades(){return this.value.grades.filter((g=>g.selected))}},methods:{determine_grading_icon(gradingstate){switch(gradingstate){default:return"circle-o";case"ungraded":return"exclamation-circle";case"unknown":return"question-circle-o";case"graded":case"allgraded":return"check";case"unsubmitted":return"dot-circle-o"}},grading_icon(grade){return this.determine_grading_icon(this.is_grading_needed(grade))},is_grading_needed:grade=>grade.grading?grade.grading.ungraded?"ungraded":grade.grading.completed_pass||grade.grading.completed||grade.grading.completed_fail?Number(grade.grading.completed)+Number(grade.grading.completed_pass)+Number(grade.grading.completed_fail)==Number(grade.grading.students)?"allgraded":"graded":"unsubmitted":"unknown"},template:'\n <div>\n <table class="r-item-course-grade-details">\n <tr v-for="g in filtered_grades">\n <td><span class="r-activity-icon" :title="g.typename" v-html="g.icon"></span\n ><a\n :href="g.gradinglink"\n target="_blank" :title="g.name"><span v-html="g.name"></span></a>\n <s-edit-mod\n :title="value.fullname"\n @saved="(fd) => g.name = fd.get(\'name\')"\n v-if="g.cmid > 0"\n :cmid="g.cmid"\n :coursectxid="value.ctxid"\n genericonly></s-edit-mod>\n <abbr v-if="useRequiredGrades && g.required" :title="text.required_goal"\n :class="\'s-required \' + is_grading_needed(g)"\n ><i class=\'fa fa-asterisk\' ></i\n ></abbr>\n </td>\n <td v-if=\'g.grading\'\n ><i :class="\'r-course-grading fa fa-\'+grading_icon(g)+\' r-graded-\'+is_grading_needed(g)"\n :title="txt.grading[is_grading_needed(g)]"></i>\n </td>\n <td v-if=\'g.grading\'>\n <r-completion-bar v-model="g.grading" :width="150" :height="15"></r-completion-bar>\n </td>\n </tr>\n </table>\n </div>\n '}),Vue.component("r-item-teachercompletion",{props:{value:{type:Object,default:function(){return{}}},guestmode:{type:Boolean,default:!1},course:{type:Object,default:function(){return{}}}},data:()=>({text:strings.completion}),created(){const self=this;let stringkeys=[];for(const key in this.text)stringkeys.push({key:key,component:"local_treestudyplan"});(0,_str.get_strings)(stringkeys).then((function(strings){let i=0;for(const key in self.text)self.text[key]=strings[i],i++}))},computed:{completionreport(){return`${_config.default.wwwroot}/report/completion/index.php?course=${this.course.id}`}},methods:{hasCompletions(){if(this.value.conditions)for(const cgroup of this.value.conditions)if(cgroup.items&&cgroup.items.length>0)return!0;return!1}},template:"\n <table class=\"r-item-course-grade-details\">\n <tr v-if=\"hasCompletions\">\n <td colspan='2'\n ><span v-if=\"value.conditions.length <= 1\">{{ text.aggregation_overall_one }}</span\n ><span v-else if=\"value.aggregation == 'all'\">{{ text.aggregation_overall_all}}</span\n ><span v-else>{{ text.aggregation_overall_any }}</span\n ></td>\n </tr>\n <tr v-else>\n <td colspan='2'>{{text.completion_not_configured}}!\n <span v-if=\"course.amteacher\">\n <br><a :href=\"'/course/completion.php?id='+course.id\" target='_blank'>{{text.configure_completion}}</a>\n </span>\n </td>\n </tr>\n <template v-for='cgroup in value.conditions'>\n <tr>\n <th colspan='2'><span v-if=\"cgroup.items.length > 1\"\n ><span v-if=\"cgroup.aggregation == 'all'\">{{ text.aggregation_all}}</span\n ><span v-else>{{ text.aggregation_any}}</span></span\n ><span v-else>{{ text.aggregation_one }}</span>\n {{ cgroup.title.toLowerCase() }}:\n </th>\n </tr>\n <tr v-for='ci in cgroup.items'>\n <td><span v-html='ci.details.criteria'></span>\n <a href=\"#\" v-b-tooltip.click\n :title=\"ci.details.requirement\"\n class='text-info'><i v-if=\"ci.details.requirement\"\n class='fa fa-question-circle'\n ></i></a>\n </td>\n <td>\n <r-completion-bar v-model=\"ci.progress\" :width=\"150\" :height=\"15\"></r-completion-bar>\n </td>\n </tr>\n </template>\n <tr><td colspan='2' class='pt-2'>\n <a target=\"_blank\" :href='completionreport'>{{ text.view_completion_report}}\n <i class='fa fa-external-link'></i></a></td></tr>\n </table>\n "}),Vue.component("r-grading-bar",{props:{value:{type:Object,default:function(){return{}}},width:{type:Number,default:150},height:{type:Number,default:15}},data:()=>({text:strings.grading}),computed:{width_unsubmitted(){return this.width*this.fraction_unsubmitted()},width_graded(){return this.width*this.fraction_graded()},width_ungraded(){return this.width*this.fraction_ungraded()},count_unsubmitted(){return this.value.students-this.value.graded-this.value.ungraded}},methods:{fraction_unsubmitted(){return this.value.students>0?1-(this.value.graded+this.value.ungraded)/this.value.students:1},fraction_graded(){return this.value.students>0?this.value.graded/this.value.students:0},fraction_ungraded(){return this.value.students>0?this.value.ungraded/this.value.students:0}},template:"\n <span class=\"r-grading-bar\" :style=\"{height: height+'px'}\"\n ><span :style=\"{height: height+'px', width: width_ungraded+'px'}\"\n class='r-grading-bar-segment r-grading-bar-ungraded'\n :title=\"text.ungraded + ' (' + this.value.ungraded + ')'\" v-b-popover.hover.top\n ></span\n ><span :style=\"{height: height+'px', width: width_graded+'px'}\"\n class='r-grading-bar-segment r-grading-bar-graded'\n :title=\"text.graded+ ' (' + this.value.graded + ')'\" v-b-popover.hover.top\n ></span\n ><span :style=\"{height: height+'px', width: width_unsubmitted+'px'}\"\n class='r-grading-bar-segment r-grading-bar-unsubmitted'\n :title=\"text.unsubmitted + ' (' + count_unsubmitted + ')'\" v-b-popover.hover.top\n ></span\n ></span>\n "}),Vue.component("r-completion-bar",{props:{value:{type:Object,default:function(){return{students:0,completed:0,completed_pass:0,completed_fail:0,ungraded:0}}},width:{type:Number,default:150},height:{type:Number,default:15}},data:()=>({text:strings.completion}),computed:{width_incomplete(){return this.width*this.fraction_incomplete()},width_completed(){return this.width*this.fraction_completed()},width_completed_pass(){return this.width*this.fraction_completed_pass()},width_completed_fail(){return this.width*this.fraction_completed_fail()},width_ungraded(){return this.width*this.fraction_ungraded()},count_incomplete(){return this.value.students-this.value.completed-this.value.completed_pass-this.value.completed_fail-this.value.ungraded}},methods:{fraction_incomplete(){return this.value.students>0?1-(this.value.completed+this.value.completed_pass+this.value.completed_fail+this.value.ungraded)/this.value.students:1},fraction_completed(){return this.value.students>0?this.value.completed/this.value.students:0},fraction_completed_pass(){return this.value.students>0?this.value.completed_pass/this.value.students:0},fraction_completed_fail(){return this.value.students>0?this.value.completed_fail/this.value.students:0},fraction_ungraded(){return this.value.students>0?this.value.ungraded/this.value.students:0}},template:"\n <span class=\"r-grading-bar\" :style=\"{height: height+'px'}\"\n ><span :style=\"{height: height+'px', width: width_ungraded+'px'}\"\n class='r-grading-bar-segment r-completion-bar-ungraded'\n :title=\"text.ungraded + ' (' + this.value.ungraded + ')'\" v-b-popover.hover.top\n ></span\n ><span :style=\"{height: height+'px', width: width_completed+'px'}\"\n class='r-grading-bar-segment r-completion-bar-completed'\n :title=\"text.completed + ' (' + this.value.completed + ')'\" v-b-popover.hover.top\n ></span\n ><span :style=\"{height: height+'px', width: width_completed_pass+'px'}\"\n class='r-grading-bar-segment r-completion-bar-completed-pass'\n :title=\"text.completed_pass + ' (' + this.value.completed_pass + ')'\" v-b-popover.hover.top\n ></span\n ><span :style=\"{height: height+'px', width: width_completed_fail+'px'}\"\n class='r-grading-bar-segment r-completion-bar-completed-fail'\n :title=\"text.completed_fail + ' (' + this.value.completed_fail + ')'\" v-b-popover.hover.top\n ></span\n ><span :style=\"{height: height+'px', width: width_incomplete+'px'}\"\n class='r-grading-bar-segment r-completion-bar-incomplete'\n :title=\"text.incomplete + ' (' + count_incomplete + ')'\" v-b-popover.hover.top\n ></span\n ></span>\n "}),Vue.component("r-completion-circle",{props:{value:{type:Object,default:function(){return{students:10,completed:2,completed_pass:2,completed_fail:2,ungraded:2}}},stroke:{type:Number,default:.2},disabled:{type:Boolean,default:!1},title:{type:String,default:""}},computed:{radius(){return 50-50*this.stroke},arcpath_ungraded(){return this.arcpath(0,this.fraction_ungraded())},arcpath_completed(){const begin=this.fraction_ungraded();return this.arcpath(begin,this.fraction_completed())},arcpath_completed_pass(){const begin=this.fraction_ungraded()+this.fraction_completed();return this.arcpath(begin,this.fraction_completed_pass())},arcpath_completed_fail(){const begin=this.fraction_ungraded()+this.fraction_completed()+this.fraction_completed_pass();return this.arcpath(begin,this.fraction_completed_fail())},arcpath_incomplete(){const begin=this.fraction_ungraded()+this.fraction_completed()+this.fraction_completed_pass()+this.fraction_completed_fail();return this.arcpath(begin,this.fraction_incomplete())}},methods:{arcpath(start,end){const r=50-50*this.stroke,t1=2*start*π,Δ=2*end*π;return(0,_svgarc.svgarcpath)([50,50],[r,r],[t1,Δ],1.5*π)},fraction_incomplete(){return this.value.students>0?1-(this.value.completed+this.value.completed_pass+this.value.completed_fail+this.value.ungraded)/this.value.students:1},fraction_completed(){return this.value.students>0?this.value.completed/this.value.students:0},fraction_completed_pass(){return this.value.students>0?this.value.completed_pass/this.value.students:0},fraction_completed_fail(){return this.value.students>0?this.value.completed_fail/this.value.students:0},fraction_ungraded(){return this.value.students>0?this.value.ungraded/this.value.students:0}},template:'\n <svg width="1em" height="1em" viewBox="0 0 100 100">\n <title>{{title}}</title>\n <circle cx="50" cy="50" :r="radius"\n :style="\'stroke-width: \' + (stroke*100)+\'; stroke: #ccc; fill: none;\'"/>\n <path :d="arcpath_ungraded"\n :style="\'stroke-width: \' + (stroke*100) +\'; stroke: var(--warning); fill: none;\'"/>\n <path :d="arcpath_completed"\n :style="\'stroke-width: \' + (stroke*100) +\'; stroke: var(--info); fill: none;\'"/>\n <path :d="arcpath_completed_pass"\n :style="\'stroke-width: \' + (stroke*100) +\'; stroke: var(--success); fill: none;\'"/>\n <path :d="arcpath_completed_fail"\n :style="\'stroke-width: \' + (stroke*100) +\'; stroke: var(--danger); fill: none;\'"/>\n\n <circle v-if="disabled" cx="50" cy="50" :r="radius/2"\n :style="\'fill: var(--dark);\'"/>\n <circle v-else-if="value.ungraded > 0" cx="50" cy="50" :r="radius/2"\n :style="\'fill: var(--warning);\'"/>\n </g>\n </svg>\n '}),Vue.component("r-item-junction",{props:{value:{type:Object,default:function(){return{}}},guestmode:{type:Boolean,default:!1},teachermode:{type:Boolean,default:!1}},data:()=>({}),computed:{completion(){return this.value.completion?this.value.completion:"incomplete"}},methods:{},template:'\n <div :class="\'r-item-junction r-item-filter completion-\'+completion">\n <i v-if="value.completion==\'incomplete\'" class="fa fa-circle-o"></i>\n <i v-else-if="value.completion==\'failed\'" class="fa fa-times-circle"></i>\n <i v-else-if="value.completion==\'progress\'" class="fa fa-exclamation-circle"></i>\n <i v-else class="fa fa-check-circle"></i>\n </div>\n '}),Vue.component("r-item-finish",{props:{value:{type:Object,default:function(){return{}}},guestmode:{type:Boolean,default:!1},teachermode:{type:Boolean,default:!1}},data:()=>({}),computed:{completion(){return this.value.completion?this.value.completion:"incomplete"}},methods:{},template:'\n <div :class="\'r-item-finish r-item-filter completion-\'+completion">\n <i class="fa fa-stop-circle"></i>\n </div>\n '}),Vue.component("r-item-start",{props:{value:{type:Object,default:function(){return{}}},guestmode:{type:Boolean,default:!1},teachermode:{type:Boolean,default:!1}},data:()=>({}),computed:{completion(){return this.value.completion?this.value.completion:"incomplete"}},created(){},methods:{},template:'\n <div :class="\'r-item-start r-item-filter completion-\'+completion">\n <i class="fa fa-play-circle"></i>\n </div>\n '}),Vue.component("r-item-badge",{props:{value:{type:Object,default:function(){return{}}},guestmode:{type:Boolean,default:!1},teachermode:{type:Boolean,default:!1}},data:()=>({text:strings.badge}),computed:{completion(){return this.value.badge.issued?"completed":"incomplete"},issued_icon(){return!0===this.value.badge.issued?"check":"circle-o"},issuestats(){return{students:this.value.badge.studentcount?this.value.badge.studentcount:0,completed:this.value.badge.issuedcount?this.value.badge.issuedcount:0,completed_pass:0,completed_fail:0,ungraded:0}},arcpath_issued(){if(this.value.badge.studentcount){const fraction=this.value.badge.issuedcount/this.value.badge.studentcount;return this.arcpath(0,fraction)}return""},arcpath_progress(){if(this.value.badge.completion){const fraction=this.value.badge.completion.progress/this.value.badge.completion.count;return this.arcpath(0,fraction)}return""},badgeinprogress(){return this.value.badge.issued||this.teachermode||this.value.badge.completion&&this.value.badge.completion.progress>=this.value.badge.completion.count}},methods:{arcpath(start,end){const t1=2*start*π,Δ=2*end*π-.01;return(0,_svgarc.svgarcpath)([50,50],[44,44],[t1,Δ],1.5*π)},addTargetBlank(html){const m=/^([^<]*\< *a +)(.*)/.exec(html);return m?`${m[1]} target="_blank" ${m[2]}`:html},completion_icon_rq:complete=>complete?"check-square-o":"square-o",completion_icon:complete=>complete?"check-circle":"times-circle",status:complete=>complete?"complete":"incomplete"},template:'\n <div :class="\'r-item-badge r-item-filter r-completion-\'+completion" v-b-tooltip.hover :title="value.badge.name">\n <a v-b-modal="\'r-item-badge-details-\'+value.id"\n ><svg class="r-badge-backdrop " width=\'50px\' height=\'50px\' viewBox="0 0 100 100">\n <title>{{value.badge.name}}</title>\n <template v-if="teachermode">\n <circle cx="50" cy="50" r="44"\n style="stroke: #ccc; stroke-width: 8; fill: #ddd; fill-opacity: 0.8;"/>\n <path :d="arcpath_issued"\n :style="\'stroke-width: 8; stroke: var(--info); fill: none;\'"/>\n </template>\n <circle v-else-if="value.badge.issued" cx="50" cy="50" r="46"\n style="stroke: currentcolor; stroke-width: 4; fill: currentcolor; fill-opacity: 0.5;"/>\n <template v-else-if="value.badge.completion">\n <circle cx="50" cy="50" r="44"\n style="stroke: #ccc; stroke-width: 8; fill: #ddd; fill-opacity: 0.8;"/>\n <path :d="arcpath_progress"\n :style="\'stroke-width: 8; stroke: var(--info); fill: none;\'"/>\n </template>\n <circle v-else cx="50" cy="50" r="46"\n stroke-dasharray="6 9"\n style="stroke: #999; stroke-width: 6; fill: #ddd; fill-opacity: 0.8;"/>\n <image class="badge-image" clip-path="circle() fill-box"\n :href="value.badge.imageurl" x="12" y="12" width="76" height="76"\n :style="(badgeinprogress)?\'\':\'opacity: 0.4;\'" />\n </svg></a>\n\n <b-modal\n :id="\'r-item-badge-details-\'+value.id"\n :title="value.badge.name"\n size="lg"\n ok-only\n centered\n scrollable\n >\n <template #modal-header>\n <div>\n <h1><i class="fa fa-certificate"></i>\n <a :href="(!guestmode)?(value.badge.infolink):undefined" target="_blank"\n >{{ value.badge.name }}</a\n ></h1>\n </div>\n <div class="r-course-detail-header-right" v-if="!teachermode">\n <div class="r-completion-detail-header">\n {{ text[\'completion_\'+completion] }}\n <i v-b-popover.hover :class="\'fa fa-\'+issued_icon+\' r-completion-\'+completion"\n :title="text[\'completion_\'+completion]"></i>\n </div>\n </div>\n </template>\n <b-container fluid>\n <b-row><b-col cols="3">\n <img :src="value.badge.imageurl"/>\n </b-col><b-col cols="9">\n <p>{{value.badge.description}}</p>\n <ul v-if="value.badge.issued" class="list-unstyled pt-1 mb-1 border-grey border-top">\n <li><strong><i class="fa fa-calendar-check-o r-completion-complete-pass"></i>\n {{text.dateissued}}:</strong> {{ value.badge.dateissued }}</li>\n <li v-if=\'value.badge.dateexpired\'\n ><strong><i class="fa fa-calendar-times-o r-completion-complete"></i>\n {{text.dateexpired}}:</strong> {{ value.badge.dateexpired }}</li>\n <li><strong><i class="fa fa-share-alt r-completion-complete-pass"></i>\n <a :href="value.badge.issuedlink">{{text.share_badge}}</a></strong> </li>\n </ul>\n <table v-if=\'value.badge.completion && !value.badge.issued\' class="r-item-course-grade-details mb-2">\n <tr v-if="value.badge.completion.types.length > 1">\n <th colspan="2"><span v-html="value.badge.completion.title"></span></th>\n </tr>\n <template v-for=\'cgroup in value.badge.completion.types\' >\n <tr>\n <td colspan="2" v-if="value.badge.completion.types.length > 1"\n ><span v-html="cgroup.title"></span></td>\n <th colspan="2" v-else><span v-html="cgroup.title"></span></th>\n </tr>\n <template v-for=\'ci in cgroup.criteria\'>\n <tr>\n <td class="pl-3"><span v-if=\'guestmode\'><span v-html="ci.title"></span></span>\n <a target=\'_blank\' v-else-if=\'ci.link\' :href=\'ci.link\'\n ><span v-html="ci.title"></span></a>\n <span v-else><span v-html="ci.title"></span></span>\n <td><i :class="\'fa fa-\'+completion_icon(ci.completed)+\' r-completion-\'+status(ci.completed)"\n :title="text[\'completion_\'+status(ci.completed)]"></i>\n </td>\n </tr>\n <template v-if="ci.requirements.length > 1">\n <tr v-for="rq in ci.requirements">\n <td class="pl-4" colspan="2"\n ><i :class="\'fa fa-\'+completion_icon_rq(rq.completed)+\' r-completion-incomplete\'"\n :title="text[\'completion_\'+status(rq.completed)]"></i>\n <span class="t-badge-criteria-requirement"><span v-html="rq.title"></span></span></td>\n </tr>\n </template>\n </template>\n </template>\n <tr v-if="!value.badge.active" class="mt-1">\n <td colspan="2" class="alert alert-warning alert-block">{{text.badgedisabled}}</td>\n </tr>\n </table>\n <ul class="list-unstyled w-100 border-grey border-top border-bottom pt-1 pb-1 mb-1"\n v-if="value.badge.criteria"><li v-for="crit in value.badge.criteria"\n ><span v-html=\'crit\'></span></li></ul>\n <p v-if="(!guestmode)"><strong><i class="fa fa-link"></i>\n <a :href="value.badge.infolink" target="_blank"\n >{{ text.badgeinfo }}</a></strong></p>\n <p v-if="teachermode && !guestmode"\n >{{text.badgeissuedstats}}:<br>\n <r-completion-bar v-model="issuestats" :width="150" :height="15"></r-completion-bar>\n </p>\n </b-col></b-row>\n </b-container>\n </b-modal>\n </div>\n '})}};return _exports.default=_default,_exports.default}));
|
|
|
|
//# sourceMappingURL=report-viewer-components.min.js.map
|