3 lines
115 KiB
JavaScript
3 lines
115 KiB
JavaScript
define("local_treestudyplan/report-viewer-components",["exports","./simpleline/simpleline","./util/string-helper","./util/date-helper","./util/browserbuttonevents","core/ajax","core/notification","./util/svgarc","./util/debugger","core/config","./studyplan-processor","./treestudyplan-components","./util/fittext-vue"],(function(_exports,_simpleline,_stringHelper,_dateHelper,_browserbuttonevents,_ajax,_notification,_svgarc,_debugger,_config,_studyplanProcessor,_treestudyplanComponents,_fittextVue){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),_fittextVue=_interopRequireDefault(_fittextVue);const π=Math.PI,ENROLLABLE_SELF=1,ENROLLABLE_ROLE=2,ENROLLABLE_SELF_ROLE=3;var _default={install(Vue){Vue.use(_treestudyplanComponents.default),Vue.use(_fittextVue.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.loadStrings)({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",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",noenddate:"noenddate"},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",noenddate:"noenddate"},competency:{competency_not_configured:"competency_not_configured",configure_competency:"configure_competency",when:"when",required:"required",points:"points@core_grades",heading:"competency_heading",details:"competency_details",results:"results",unrated:"unrated",progress:"completion_progress",view_feedback:"view_feedback"},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",overview:"overviewreport:all",oveviewperiod:"overviewreport:period"},lineheader:{cannot_enrol:"line_cannot_enrol",can_enrol:"line_can_enrol",is_enrolled:"line_is_enrolled",enrol:"line_enrol",unenrol:"line_unenrol",enrolled:"line_enrolled",notenrolled:"line_notenrolled",enrol_question:"line_enrol_question",enrollments:"line_enrollments",enrollment:"line_enrollment",info:"info@core",confirm:"confirm@core",yes:"yes@core",no:"no@core",enrolled_in:"line_enrolled_in",since:"since@core",byname:"byname@core",students:"students@core",firstname:"firstname@core",lastname:"lastname@core",email:"email@core",enrol_student_question:"line_enrol_student_question",unenrol_student_question:"line_unenrol_student_question"}});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},verifiedType(){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}},mounted(){this.loadStudyplans(),(0,_browserbuttonevents.addBrowserButtonEvent)(this.backPressed)},methods:{backPressed(){debug.log("Back button pressed"),this.selectedstudyplan&&(debug.log("Closing studyplan"),this.deselectStudyplan())},callArgs(o){const args={};return"object"!=typeof o||Array.isArray(o)||null===o||(0,_studyplanProcessor.objCopy)(args,o),"invited"==this.verifiedType?args.invitekey=this.invitekey:"other"==this.verifiedType&&(args.userid=this.userid),args},loadStudyplans(){const self=this;this.loading=!0,(0,_ajax.call)([{methodname:`local_treestudyplan_list_${this.verifiedType}_studyplans`,args:this.callArgs()}])[0].then((function(response){console.info("Loaded: plans",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)=>{let 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;const parts=window.location.hash.replace("#","").split("-");if(parts&&parts.length>0)for(const k in self.studyplans){const list=self.studyplans[k];for(const idx in list){const plan=list[idx];if(plan.id==parts[0]&&!plan.suspended)return void self.selectStudyplan(plan)}}if(1==this.studyplancount)if(self.studyplans.present.length>0){const plan=self.studyplans.present[0];plan.suspended||self.selectStudyplan(plan)}else if(self.studyplans.future.lengh>0){const plan=self.studyplans.future[0];plan.suspended||self.selectStudyplan(plan)}else{const plan=self.studyplans.past[0];plan.suspended||self.selectStudyplan(plan)}})).catch(_notification.default.exception)},selectStudyplan(plan){const self=this;this.loadingstudyplan=!0,(0,_ajax.call)([{methodname:`local_treestudyplan_get_${this.verifiedType}_studyplan`,args:this.callArgs({studyplanid:plan.id})}])[0].then((function(response){self.selectedstudyplan=(0,_studyplanProcessor.processStudyplan)(response),self.loadingstudyplan=!1,window.location.hash=self.selectedstudyplan.id})).catch(_notification.default.exception)},deselectStudyplan(){this.selectedstudyplan=null,this.loadStudyplans(),window.location.hash=""}},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},coaching:{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("present"==(0,_dateHelper.studyplanPageTiming)(page)){const s=new Date(page.startdate);(!firststart||firststart>s)&&(startpageindex=ix,firststart=s)}}return startpageindex},wwwroot:()=>_config.default.wwwroot},methods:{pageduration:page=>(0,_dateHelper.formatDate)(page.startdate,!1)+" - "+(0,_dateHelper.formatDate)(page.enddate,!1),columns:page=>1+2*page.periods,columnsStylerule(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++){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(){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 v-if="!page.timeless">\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)"\n ><a v-if="teachermode && selectedpage && !coaching" class="ml-2"\n :href="wwwroot+\'//local/treestudyplan/result-overview.php?page=\'+selectedpage.id"\n\n target=\'_blank\'><i class=\'fa fa-list-ul\'></i> {{text.overview}}</a\n ></s-studyline-header-heading>\n <r-studyline-heading v-for="(line,lineindex) in page.studylines"\n :key="line.id"\n :teachermode="teachermode"\n :guestmode="guestmode"\n v-model="page.studylines[lineindex]"\n :layers=\'countLineLayers(line,page)+1\'\n :studentid="value.userid"\n @enrolupdate="page.studylines[lineindex].enrol = $event"\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="columnsStylerule(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 ><a v-if="teachermode && selectedpage && !coaching"\n v-b-tooltip.hover\n :href="wwwroot+\'//local/treestudyplan/result-overview.php?page=\'+selectedpage.id\n +\'&firstperiod=\'+index+\'&lastperiod=\'+index"\n target=\'_blank\'\n :title="text.overviewperiod"><i class=\'fa fa-list-ul\'></i></a\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(line,page)"\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 + ((layernr == countLineLayers(line,page))?\' lastlyr \':\' \')"\n ></r-studyline-slot\n ><r-studyline-slot\n v-if="showslot(page,line, index, layeridx, \'filter\')"\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 + ((layernr == countLineLayers(line,page))?\' lastlyr \':\' \')"\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:()=>({})},guestmode:{type:Boolean,default:!1},teachermode:{type:Boolean,default:!1},layers:{type:Number,default:1},studentid:{type:Number}},data:()=>({layerHeights:{},text:strings.lineheader,students:null,canUnenrol:!1,sorting:{asc:!1,field:"enrolled_time"}}),created(){ItemEventBus.$on("lineHeightChange",this.onLineHeightChange)},computed:{enrollable(){return this.value.enrol.enrollable>0},enrollableSelf(){return[ENROLLABLE_SELF,ENROLLABLE_SELF_ROLE].includes(this.value.enrol.enrollable)},enrollableRole(){return[ENROLLABLE_ROLE,ENROLLABLE_SELF_ROLE].includes(this.value.enrol.enrollable)},enrolled(){return!!this.value.enrol.enrolled},canEnrol(){return!!this.value.enrol.can_enrol},enrolQuestion(){return this.text.enrol_question.replace("{$a}",this.value.name)},enrolledIn(){return this.text.enrolled_in.replace("{$a}",this.value.name)},by(){return this.text.byname.replace("{$a}","")},enrolldate(){return(0,_dateHelper.formatDatetime)(this.value.enrol.enrolled_time)},sortedStudents(){const self=this,list=Array.isArray(this.students)?this.students:[];return list.sort(((a,b)=>{let d=a,e=b;self.sorting.asc||(d=b,e=a);let df=d,ef=e;const field=self.sorting.field;if(d.user&&d.user.hasOwnProperty(field)?(df=d.user,ef=e.user):d.enrol&&d.enrol.hasOwnProperty(field)&&(df=d.enrol,ef=e.enrol),"enrolled"==field)return(df.enrolled?1:0)-(ef.enrolled?1:0);if("enrolled_time"==field){return(df[field]&&d.enrol.enrolled?df[field]:0)-(ef[field]&&e.enrol.enrolled?ef[field]:0)}return String(df[this.sorting.field]).localeCompare(String(ef[this.sorting.field]))})),list}},methods:{formatDatetime:_dateHelper.formatDatetime,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}},enrolSelf(){const self=this;(0,_ajax.call)([{methodname:"local_treestudyplan_line_enrol_self",args:{id:self.value.id}}])[0].then((function(response){self.$emit("enrolupdate",response)})).catch(_notification.default.exception)},enrolStudent(student){const self=this,user=student.user;let question=self.text.enrol_student_question.replace("{$a}",`${user.firstname} ${user.lastname}`);const options={okTitle:self.text.yes,cancelTitle:self.text.no,okVariant:"success",cancelVariant:"danger"};this.$bvModal.msgBoxConfirm(question,options).then((reply=>{reply&&(0,_ajax.call)([{methodname:"local_treestudyplan_line_enrol_students",args:{id:self.value.id,users:[user.id]}}])[0].then((function(response){student.enrol=response[0].enrol})).catch(_notification.default.exception)})).catch(_notification.default.exception)},unenrolStudent(student){const self=this,user=student.user;let question=self.text.unenrol_student_question.replace("{$a}",`${user.firstname} ${user.lastname}`);const options={okTitle:self.text.yes,cancelTitle:self.text.no,okVariant:"success",cancelVariant:"danger"};this.$bvModal.msgBoxConfirm(question,options).then((reply=>{reply&&(0,_ajax.call)([{methodname:"local_treestudyplan_line_unenrol_students",args:{id:self.value.id,users:[user.id]}}])[0].then((function(response){student.enrol=response[0].enrol})).catch(_notification.default.exception)})).catch(_notification.default.exception)},loadStudents(){const self=this;self.students=null,self.canUnenrol=!1,(0,_ajax.call)([{methodname:"local_treestudyplan_list_line_enrolled_students",args:{id:self.value.id}}])[0].then((function(response){self.students=response.userinfo,self.canUnenrol=response.can_unenrol})).catch(_notification.default.exception)},toggleSort(header){this.sorting.field==header?this.sorting.asc=!this.sorting.asc:(this.sorting.field=header,this.sorting.asc=!0)}},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"><div>\n <abbr v-b-tooltip.hover :title="value.name">{{ value.shortname }}</abbr>\n <template v-if="enrollable">\n <template v-if="teachermode">\n <br>\n <a v-if="!canEnrol"\n href=\'#\' @click.prevent=""\n v-b-modal="\'r-enrollments-\'+value.id"\n :title="text.cannot_enrol"\n ><i class=\'fa fa-lock text-danger\'></i> {{text.enrollments}}</a>\n <a v-else\n href=\'#\' @click.prevent=""\n v-b-modal="\'r-enrollments-\'+value.id"\n :title="text.can_enrol"\n ><i class=\'fa fa-unlock-alt text-success\'></i> {{text.enrollments}}</a>\n <b-modal\n :id="\'r-enrollments-\'+value.id"\n @show="loadStudents"\n size="xl"\n ok-only\n scrollable\n :title="value.name"\n >\n <table class="r-line-enroll-userlist">\n <thead>\n <th><a href=\'#\' @click.prevent="toggleSort(\'firstname\')"\n >{{text.firstname}}</a>\n <i v-if="sorting.field==\'firstname\' && sorting.asc" class=\'fa fa-sort-asc fa-fw\'></i\n ><i v-else-if="sorting.field==\'firstname\' && !sorting.asc" class=\'fa fa-sort-desc fa-fw\'></i>\n </th>\n <th><a href=\'#\' @click.prevent="toggleSort(\'lastname\')"\n >{{text.lastname}}</a>\n <i v-if="sorting.field==\'lastname\' && sorting.asc" class=\'fa fa-sort-asc fa-fw\'></i\n ><i v-else-if="sorting.field==\'lastname\' && !sorting.asc" class=\'fa fa-sort-desc fa-fw\'></i>\n </th>\n <th><a href=\'#\' @click.prevent="toggleSort(\'enrolled\')"\n >{{text.enrolled}}</a>\n <i v-if="sorting.field==\'enrolled\' && sorting.asc" class=\'fa fa-sort-asc fa-fw\'></i\n ><i v-else-if="sorting.field==\'enrolled\' && !sorting.asc" class=\'fa fa-sort-desc fa-fw\'></i>\n </th>\n <th><a href=\'#\' @click.prevent="toggleSort(\'enrolled_time\')"\n >{{text.since}}</a>\n <i v-if="sorting.field==\'enrolled_time\' && sorting.asc" class=\'fa fa-sort-asc fa-fw\'></i\n ><i v-else-if="sorting.field==\'enrolled_time\' && !sorting.asc" class=\'fa fa-sort-desc fa-fw\'></i>\n </th>\n <th><a href=\'#\' @click.prevent="toggleSort(\'enrolled_by\')"\n >{{by}}</a>\n <i v-if="sorting.field==\'enrolled_by\' && sorting.asc" class=\'fa fa-sort-asc fa-fw\'></i\n ><i v-else-if="sorting.field==\'enrolled_by\' && !sorting.asc" class=\'fa fa-sort-desc fa-fw\'></i>\n </th>\n <th v-if="canEnrol || canUnenrol">{{text.enrol}}</th>\n </thead>\n <tbody>\n <tr v-if="students == null"><td\n :colspan="4+(canEnrol?1:0)">\n <div class="spinner-border spinner-border-sm text-info" role="status"></div>\n </td></tr>\n <template v-else>\n <tr v-for="student in sortedStudents">\n <td>{{student.user.firstname}}</td>\n <td>{{student.user.lastname}}</td>\n <td><span v-if="student.enrol.enrolled" class="text-success">{{text.enrolled}}</span\n ><span v-else class="text-danger">{{text.notenrolled}}</span></td>\n <td><span v-if="student.enrol.enrolled"\n >{{formatDatetime(student.enrol.enrolled_time)}}</span></td>\n <td><span v-if="student.enrol.enrolled"\n >{{student.enrol.enrolled_by}}</span></td>\n <td ><b-button v-if="!student.enrol.enrolled && (canEnrol || canUnenrol)"\n variant="success"\n size="sm"\n @click="enrolStudent(student)"\n >{{text.enrol}}</b-button\n ><b-button v-else-if="student.enrol.enrolled && canUnenrol"\n variant="danger"\n size="sm"\n @click="unenrolStudent(student)"\n >{{text.unenrol}}</b-button>\n </td>\n </tr>\n </template>\n </tbody>\n </table>\n </b-modal>\n </template>\n <template v-else-if="value.enrol.selfview">\n <br>\n <a v-if="!enrolled && !canEnrol"\n @click.prevent=""\n href=\'#\'\n v-b-tooltip.focus\n :title="text.cannot_enrol"\n ><fittext maxsize="12pt"><i class=\'fa fa-lock text-danger\'></i> {{text.cannot_enrol}}</fittext></a>\n <a v-else-if="!enrolled && canEnrol"\n @click.prevent=""\n href=\'#\'\n v-b-modal="\'r-enrol-\'+value.id"\n :title="text.can_enrol"\n ><fittext maxsize="12pt"><i class=\'fa fa-unlock-alt text-info\'></i> {{text.enrol}}</fittext></a>\n <a v-else-if="enrolled"\n @click.prevent=""\n href=\'#\'\n v-b-modal="\'r-enrollment-\'+value.id"\n :title="text.enrolled"\n ><fittext maxsize="12pt"><i class=\'fa fa-unlock text-success\'></i> {{text.enrolled}}</fittext></a>\n <b-modal\n :id="\'r-enrol-\'+value.id"\n :title="text.confirm"\n :ok-title="text.yes"\n :cancel-title="text.no"\n ok-variant="success"\n cancel-variant="danger"\n @ok="enrolSelf"\n >\n <p>{{enrolQuestion}}</p>\n </b-modal>\n <b-modal\n :id="\'r-enrollment-\'+value.id"\n ok-only\n :title="text.enrollment"\n >\n <p>{{enrolledIn}}<p>\n <p><b>{{text.since}}</b> {{enrolldate}}<br>\n <b>{{by}}</b> {{this.value.enrol.enrolled_by}}</p>\n </b-modal>\n </template>\n <template v-else>\n \n <i v-if="!enrolled" class=\'fa fa-lock text-danger\'></i>\n <i v-else class=\'fa fa-unlock text-success\'></i>\n </template>\n </template>\n </div></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},spanCss(){if(this.item&&this.item.span>1){return`width: 100%; grid-column: span ${2*this.item.span-1};`}return""},cloud(){const enrol=this.line.enrol;return!this.teachermode&&enrol.enrollable>0&&!enrol.enrolled}},data:()=>({}),methods:{},template:"\n <div :class=\" 'r-studyline-slot ' + type + ' '\n + 'r-studyline-slot-' + slotindex + ' '\n + 'periodcount-' + page.periods + ' '\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 :cloud=\"cloud\"\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:()=>null},plan:{type:Object,default:()=>null},guestmode:{type:Boolean,default:!1},teachermode:{type:Boolean,default:!1},cloud:{type:Boolean,default:!1}},data:()=>({lines:[]}),methods:{lineColor(){if(this.teachermode)return"var(--gray)";if(this.cloud)return"#ccc";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},class:this.cloud?"r-dummy-line":""}))},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)},updated(){this.dummy||this.redrawLines()},template:'\n <div class="r-item-base" :id="\'studyitem-\'+value.id" :data-x=\'value.type\'>\n <template v-if="cloud">\n <r-item-dummy-course v-if="value.type == \'course\'"></r-item-dummy-course>\n <r-item-dummy-badge v-else-if="value.type == \'badge\'"></r-item-dummy-badge>\n <r-item-dummy-filter v-else></r-item-dummy-filter>\n </template>\n <template v-else>\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 </template>\n </div>\n '}),Vue.component("r-item-invalid",{props:{value:{type:Object,default:()=>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.formatDate)(this.value.course.startdate)},enddate(){return this.value.course.enddate>0?(0,_dateHelper.formatDate)(this.value.course.enddate):this.text.noenddate},courseprogress(){return this.value.course.enrolled?this.value.course.completion?this.value.course.completion.progress/this.value.course.completion.count:this.value.course.competency?this.value.course.competency.progress/this.value.course.competency.count:this.value.course.grades?this.gradeprogress(this.value.course.grades)/this.value.course.grades.length:0:0},hasprogressinfo(){return!!this.value.course.enrolled&&(this.value.course.completion||this.value.course.competency||this.value.course.grades)},wwwroot:()=>_config.default.wwwroot},methods:{completionIcon(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"}},circleIcon(completion){switch(completion){default:return null;case"failed":return"times";case"progress":return"";case"completed":case"good":case"excellent":return"check"}},gradeprogress(grades){let progress=0;for(const ix in grades){const g=grades[ix];["completed","excellent","good"].includes(g.completion)&&progress++}return progress}},template:"\n <div :class=\"'r-item-course card completion-'+value.completion\">\n <div class='r-item-course-cardwrapper mr-0 ml-0 h-100 '>\n <div\n :title=\"text['coursetiming_'+value.course.timing]\"\n v-b-popover.hover.top=\"startdate+' - '+enddate\"\n :class=\"'r-timing-indicator timing-'+value.course.timing\"\n ></div>\n <div class=\"r-item-course-title card-body h-100\">\n <fittext maxsize=\"12pt\" minsize=\"9pt\">\n <a v-b-modal=\"'r-item-course-details-'+value.id\"\n :href=\"(!guestmode)?(wwwroot+'/course/view.php?id='+value.course.id):'#'\"\n @click.prevent.stop=''\n >{{ value.course.displayname }}</a>\n </fittext>\n </div>\n <div class=\"h-100 r-item-course-indicator \">\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='hasprogressinfo'>\n <r-progress-circle v-if='[\"failed\", \"progress\",\"incomplete\"].includes(value.completion)'\n :value='courseprogress'\n :max='1'\n :min='0'\n :class=\"'r-course-result r-completion-'+value.completion\"\n :icon='circleIcon(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-'+completionIcon(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-'+completionIcon(value.completion)+\n ' r-completion-'+value.completion\"\n :title=\"text['completion_'+value.completion]\"></i>\n </template>\n </div>\n </div>\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 header-class=\"r-item-course-header\"\n >\n <template #modal-header >\n <div class=\"r-item-course-header-details\">\n <div>\n <h1><a :href=\"(!guestmode)?(wwwroot+'/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=\"fa fa-exclamation-triangle t-not-enrolled-alert\"\n :title=\"text.student_not_tracked\"></i>\n </template>\n <template v-else-if='hasprogressinfo && !value.course.showprogressbar'>\n {{text['completion_'+value.completion]}}\n <r-progress-circle v-if='[\"failed\", \"progress\",\"incomplete\"].includes(value.completion)'\n :value='courseprogress'\n :max='1'\n :min='0'\n :class=\"'r-progress-circle-popup r-completion-'+value.completion\"\n :icon='circleIcon(value.completion)'\n :title=\"text['completion_'+value.completion]\"\n ></r-progress-circle>\n <i v-else v-b-popover.top\n :class=\"'fa fa-'+completionIcon(value.completion)+\n ' 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-'+completionIcon(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 </div>\n <s-progress-bar\n v-if='value.course.showprogressbar && hasprogressinfo'\n v-model=\"courseprogress\"\n ></s-progress-bar>\n </template>\n <s-course-extrafields\n v-if=\"value.course.extrafields\"\n v-model=\"value.course.extrafields\"\n position=\"above\"\n ></s-course-extrafields>\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 <r-item-student-course-competency\n v-if='!!value.course.competency'\n v-model='value.course.competency'\n :item='value'\n ></r-item-student-course-competency>\n <s-course-extrafields\n v-if=\"value.course.extrafields\"\n v-model=\"value.course.extrafields\"\n position=\"below\"\n ></s-course-extrafields>\n </b-modal>\n </div>\n "}),Vue.component("r-item-studentgrades",{props:{value:{type:Object,default:()=>({})},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:{completionIcon(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-\'+completionIcon(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 @click.prevent.stop=""\n >{{ text["view_feedback"]}}</a>\n <b-modal\n :id="\'r-grade-feedback-\'+g.id"\n ok-only\n centered\n scrollable\n >\n <template #modal-header>\n <div>\n <h3><i class="fa fa-graduation-cap"></i>{{ value.course.fullname }}</h3><br>\n <span class="r-activity-icon" :title="g.typename" v-html="g.icon + g.name"></span>\n </div>\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:()=>({})},guestmode:{type:Boolean,default:!1},course:{type:Object,default:()=>({})}},data:()=>({text:strings.completion}),computed:{},methods:{completionIcon(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"}},completionTag: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\'><span v-html="ci.title"></span></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-\'+completionIcon(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 @click.prevent.stop=""\n >{{ text["view_feedback"]}}</a>\n <b-modal\n :id="\'r-grade-feedback-\'+ci.id"\n ok-only\n centered\n scrollable\n >\n <template #modal-header>\n <div>\n <h3><i class="fa fa-graduation-cap"></i>{{ course.fullname }}</h3>\n <span class="r-activity-icon" :title="ci.typename" v-html="ci.icon + ci.name"></span>\n </div>\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-student-course-competency",{props:{value:{type:Object,default:()=>({})},guestmode:{type:Boolean,default:!1},item:{type:Object,default:()=>({id:null})}},data:()=>({text:strings.competency}),computed:{hasCompletions(){if(this.value.conditions)for(const cgroup of this.value.conditions)if(cgroup.items&&cgroup.items.length>0)return!0;return!1},wwwroot:()=>_config.default.wwwroot},methods:{completionIcon:competency=>competency.proficient&&competency.courseproficient?"check-circle":competency.proficient?"check":!1===competency.proficient?"times-circle":"circle-o",completionTag:competency=>competency.proficient&&competency.courseproficient||competency.proficient?"completed":!1===competency.proficient?"failed":competency.progress?"progress":"incomplete",pathtags(competency){const path=competency.path;let s="";for(const ix in path){const p=path[ix];let url;ix>0&&(s+=" / "),url="competency"==p.type?_config.default.wwwroot+`/admin/tool/lp/user_competency_in_course.php?courseid=${this.item.course.id}&competencyid=${p.id}`:this.competencyurl(p),s+=`<a href="${url}" target="_blank">${p.title}</a>`}return s},competencyurl(c){return _config.default.wwwroot+`/admin/tool/lp/user_competency_in_course.php?courseid=${this.item.course.id}&competencyid=${c.id}`},usercompetencyurl:c=>_config.default.wwwroot+`/admin/tool/lp/user_competency.php?id=${c.ucid}`},template:'\n <table class="r-item-course-competency-list">\n <tr v-if="value.competencies.length == 0">\n <td colspan=\'2\'>{{text.competencies_not_configured}}!\n <br><a :href="wwwroot+\'/admin/tool/lp/coursecompetencies.php?courseid=\'+item.course.id" target=\'_blank\'>{{text.configure_competencies}}</a>\n </td>\n </tr>\n <template v-else>\n <tr v-for=\'c in value.competencies\'>\n <td>\n <a href="#" v-b-modal="\'modal-competency-id-\'+c.id" @click.prevent.stop=\'\'><span v-html=\'c.title\' ></span></a>\n </td>\n <td class=\'details\' >\n <a v-if="c.details" href="#" v-b-modal="\'modal-competency-id-\'+c.id" @click.prevent.stop=\'\' ><span v-html=\'c.details\'></span></a>\n <abbr v-if="c.required" :title="text.required"\n :class="\'s-required \' + + completionTag(c)"\n ><i class=\'fa fa-asterisk\' ></i\n ></abbr>\n </td>\n <td :colspan="(c.required)?1:2">\n <span :class="\'r-completion-\'+completionTag(c)">\n <template v-if="!c.progress && !c.count">\n <i :class="\'fa fa-\'+completionIcon(c)" :title="text[\'completion_\'+completionTag(c)]"></i>\n {{ (c.proficient === null)?text.unrated:c.grade }}\n </template>\n <template v-else>\n <r-progress-circle v-if=\'!c.proficient\'\n :value=\'c.progress\'\n :max=\'c.count\'\n :min=\'0\'\n :class="\'r-completion-\'+completionTag(c)"\n :title="text[\'completion_\'+completionTag(c)]"\n ></r-progress-circle>\n <i v-else :class="\'fa fa-\'+completionIcon(c)" :title="text[\'completion_\'+completionTag(c)]"></i>\n {{ (c.proficient === null)?((c.progress)?text.progress:text.unrated):c.grade }}\n </template>\n </span>\n </td>\n <td v-if="c.feedback">\n <a v-b-modal="\'r-competency-feedback-\'+c.id"\n @click.prevent.stop=\'\'\n href="#"\n >{{ text["view_feedback"]}}</a>\n <b-modal\n :id="\'r-competency-feedback-\'+c.id"\n ok-only\n centered\n scrollable\n >\n <template #modal-header>\n <div>\n <h3><i class="fa fa-puzzle-piece"></i>{{ c.title }}</h3>\n </div>\n </template>\n <span v-html="c.feedback"></span>\n </b-modal>\n </td>\n <b-modal :id="\'modal-competency-id-\'+c.id"\n size="lg"\n ok-only\n centered\n scrollable\n >\n <template #modal-header>\n <div>\n <h1><i class="fa fa-puzzle-piece"></i>\n <a :href="wwwroot+\'/admin/tool/lp/competencies.php?competencyid=\'+c.id" target="_blank"\n >{{c.title}} {{c.details}} </a\n ></h1>\n <div><span v-html="pathtags(c)"></span></div>\n </div>\n </template>\n <div class="mb-2" v-if="c.description"><span v-html=\'c.description\'></span></div>\n\n <template v-if="c.rule && c.children">\n <div>{{ c.ruleoutcome }} {{ text.when}} <span v-html="c.rule.toLocaleLowerCase()"></span></div>\n <table v-if="c.children" class=\'r-item-course-competency-list\'>\n <tr class=\'t-item-course-competency-headers\'>\n <th colspan="2">{{text.heading}}</th>\n <th colspan="3">{{text.results}}</th>\n </tr>\n <tr v-for="cc in c.children">\n <td >\n <a :href=\'usercompetencyurl(cc)\' target="_blank"><span v-html=\'cc.title\'></span></a>\n </td>\n <td class=\'details\'>\n <a v-if="cc.details" :href=\'usercompetencyurl(cc)\' target="_blank"><span v-html=\'cc.details\'></span></a>\n <abbr v-if="c.required" :title="text.required"\n :class="\'s-required \' + + completionTag(cc)"\n ><i class=\'fa fa-asterisk\' ></i\n ></abbr>\n </td>\n <td><span :class="\'r-completion-\'+completionTag(cc)"\n ><i :class="\'fa fa-\'+completionIcon(cc)" :title="text[\'completion_\'+completionTag(cc)]"></i>\n {{ (cc.proficient === null)?text.unrated:cc.grade }}</span></td>\n <td><span class="text-info">{{ cc.points }} {{ text.points }}</span></td>\n <td>\n </td>\n <td v-if="cc.feedback">\n <a v-b-modal="\'r-competency-feedback-\'+cc.id"\n href="#"\n @click.prevent.stop=""\n >{{ text["view_feedback"]}}</a>\n <b-modal\n :id="\'r-competency-feedback-\'+cc.id"\n ok-only\n centered\n scrollable\n >\n <template #modal-header>\n <div>\n <h3><i class="fa fa-puzzle-piece"></i>{{ cc.title }}</h3><br>\n </div>\n </template>\n <span v-html="cc.feedback"></span>\n </b-modal>\n </td>\n </tr>\n </table>\n </template>\n </b-modal>\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:{courseGradingNeeded(){return this.courseGradingState()},courseGradingIcon(){return this.determineGradingIcon(this.courseGradingState())},filteredGrades(){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},progressCircle(){const status={students:0,completed:0,completedPass:0,completedFail: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.completedPass+=itm.progress.completed_pass,status.completedFail+=itm.progress.completed_fail,status.ungraded+=itm.progress.ungraded);else if(this.value.course.competency)status.students=this.value.course.competency.total,status.completed=this.value.course.competency.proficient;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.completedPass+=g.grading.completed_pass,status.completedFail+=g.grading.completed_fail,status.ungraded+=g.grading.ungraded);return status},startdate(){return(0,_dateHelper.formatDate)(this.value.course.startdate)},enddate(){return this.value.course.enddate>0?(0,_dateHelper.formatDate)(this.value.course.enddate):this.text.noenddate},wwwroot:()=>_config.default.wwwroot},methods:{courseGradingState(){let ungraded=0,unknown=0,graded=0,allgraded=0;const grades=this.filteredGrades;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"},determineGradingIcon(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-course card \'+ (value.course.amteacher?\'r-course-am-teacher\':\'\')">\n <div class=\'r-item-course-cardwrapper mr-0 ml-0 h-100 \'>\n <div\n :title="text[\'coursetiming_\'+value.course.timing]"\n v-b-popover.hover.top="startdate+\' - \'+enddate"\n :class="\'r-timing-indicator timing-\'+value.course.timing"\n ></div>\n <div class="r-item-course-title card-body h-100">\n <fittext maxsize="12pt" minsize="9pt">\n <a v-b-modal="\'r-item-course-details-\'+value.id"\n :href="(!guestmode)?(wwwroot+\'/course/view.php?id=\'+value.course.id):\'#\'"\n @click.prevent.stop=\'\'\n >{{ value.course.displayname }}</a>\n </fittext>\n </div>\n <div class="h-100 r-item-course-indicator ">\n <r-completion-circle class="r-course-graded" :disabled="!isCompletable"\n v-model="progressCircle"></r-completion-circle>\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)?(wwwroot+\'/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="wwwroot+\'/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="progressCircle"></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 <s-course-extrafields\n v-if="value.course.extrafields"\n v-model="value.course.extrafields"\n position="above"\n ></s-course-extrafields>\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 <r-item-teacher-course-competency\n v-if=\'!!value.course.competency\'\n v-model=\'value.course.competency\'\n :item=\'value\'\n ></r-item-teacher-course-competency>\n <s-course-extrafields\n v-if="value.course.extrafields"\n v-model="value.course.extrafields"\n position="below"\n ></s-course-extrafields>\n </b-modal>\n\n </b-card>\n </div>\n '}),Vue.component("r-item-teacher-gradepicker",{props:{value:{type:Object,default:()=>({})},useRequiredGrades:{type:Boolean,default:()=>null}},data:()=>({text:strings.teachercourse}),computed:{startdate(){return(0,_dateHelper.formatDate)(this.value.course.startdate)},enddate(){return this.value.course.enddate>0?(0,_dateHelper.formatDate)(this.value.course.enddate):this.text.noenddate},wwwroot:()=>_config.default.wwwroot},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].catch(_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].catch(_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="wwwroot+\'/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 {{ startdate }} - {{ 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:()=>({})},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},filteredGrades(){return this.value.grades.filter((g=>g.selected))}},methods:{determineGradingIcon(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"}},gradingIcon(grade){return this.determineGradingIcon(this.isGradingNeeded(grade))},isGradingNeeded: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 filteredGrades">\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 \' + isGradingNeeded(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-\'+gradingIcon(g)+\' r-graded-\'+isGradingNeeded(g)"\n :title="txt.grading[isGradingNeeded(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:()=>({})},guestmode:{type:Boolean,default:!1},course:{type:Object,default:()=>({})}},data:()=>({text:strings.completion}),computed:{completionreport(){return`${_config.default.wwwroot}/report/completion/index.php?course=${this.course.id}`},wwwroot:()=>_config.default.wwwroot},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=\"wwwroot+'/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-item-teacher-course-competency",{props:{value:{type:Object,default:()=>({})},guestmode:{type:Boolean,default:!1},item:{type:Object,default:()=>({id:null})}},data:()=>({text:strings.competency}),computed:{hasCompletions(){if(this.value.conditions)for(const cgroup of this.value.conditions)if(cgroup.items&&cgroup.items.length>0)return!0;return!1},wwwroot:()=>_config.default.wwwroot},methods:{completionIcon:competency=>competency.proficient&&competency.courseproficient?"check-circle":competency.proficient?"check":!1===competency.proficient?"times-circle":"circle-o",completionTag:competency=>competency.proficient&&competency.courseproficient||competency.proficient?"completed":!1===competency.proficient?"failed":competency.progress?"progress":"incomplete",pathtags(competency){const path=competency.path;let s="";for(const ix in path){const p=path[ix];let url;ix>0&&(s+=" / "),url="competency"==p.type?_config.default.wwwroot+`/admin/tool/lp/user_competency_in_course.php?courseid=${this.item.course.id}&competencyid=${p.id}`:this.competencyurl(p),s+=`<a href="${url}" target="_blank">${p.title}</a>`}return s},competencyurl(c){return _config.default.wwwroot+`/admin/tool/lp/user_competency_in_course.php?courseid=${this.item.course.id}&competencyid=${c.id}`}},template:'\n <table class="r-item-course-competency-list">\n <tr v-if="value.competencies.length == 0">\n <td colspan=\'2\'>{{text.competencies_not_configured}}!\n <br><a :href="wwwroot+\'/admin/tool/lp/coursecompetencies.php?courseid=\'+item.course.id" target=\'_blank\'>{{text.configure_competencies}}</a>\n </td>\n </tr>\n <template v-else>\n <tr v-for=\'c in value.competencies\'>\n <td>\n <a href="#" v-b-modal="\'modal-competency-id-\'+c.id"><span v-html=\'c.title\'></span></a>\n </td>\n <td class=\'details\'>\n <a v-if="c.details" href="#" v-b-modal="\'modal-competency-id-\'+c.id"><span v-html=\'c.details\'></span></a>\n <abbr v-if="c.required" :title="text.required"\n :class="\'s-required \' + + completionTag(c)"\n ><i class=\'fa fa-asterisk\' ></i\n ></abbr>\n </td>\n <td><r-completion-bar v-model="c.completionstats" :width="150" :height="15"></r-completion-bar></td>\n <td v-if="c.feedback">\n <a v-b-modal="\'r-competency-feedback-\'+c.id"\n href="#"\n @click.prevent.stop=""\n >{{ text["view_feedback"]}}</a>\n <b-modal\n :id="\'r-competency-feedback-\'+c.id"\n ok-only\n centered\n scrollable\n >\n <template #modal-header>\n <div>\n <h3><i class="fa fa-puzzle-piece"></i>{{ c.title }}</h3><br>\n </div>\n </template>\n <span v-html="c.feedback"></span>\n </b-modal>\n </td>\n <b-modal :id="\'modal-competency-id-\'+c.id"\n size="lg"\n ok-only\n centered\n scrollable\n >\n <template #modal-header>\n <div>\n <h1><i class="fa fa-puzzle-piece"></i>\n <a :href="wwwroot+\'/admin/tool/lp/competencies.php?competencyid=\'+c.id" target="_blank"\n >{{c.title}} {{c.details}} </a\n ></h1>\n <div><span v-html="pathtags(c)"></span></div>\n </div>\n </template>\n <div class="mb-2" v-if="c.description"><span v-html=\'c.description\'></span></div>\n\n <template v-if="c.rule && c.children">\n <div>{{ c.ruleoutcome }} {{ text.when}} <span v-html="c.rule.toLocaleLowerCase()"></span></div>\n <table v-if="c.children" class=\'r-item-course-competency-list\'>\n <tr class=\'t-item-course-competency-headers\'>\n <th colspan="2">{{text.heading}}</th>\n <th colspan="3">{{text.results}}</th>\n </tr>\n <tr v-for="cc in c.children">\n <td>\n <a :href=\'competencyurl(cc)\' target="_blank"><span v-html=\'cc.title\'></span></a>\n </td>\n <td class=\'details\'>\n <a v-if="cc.details" :href=\'competencyurl(cc)\' target="_blank"><span v-html=\'cc.details\'></span></a>\n <abbr v-if="c.required" :title="text.required"\n :class="\'s-required \' + completionTag(cc)"\n ><i class=\'fa fa-asterisk\' ></i\n ></abbr>\n </td>\n <td><r-completion-bar v-model="cc.completionstats" :width="150" :height="15"></r-completion-bar></td>\n <td><span class="text-info">{{ cc.points }} {{ text.points }}</span></td>\n <td v-if="cc.feedback">\n <a v-b-modal="\'r-competency-feedback-\'+cc.id"\n href="#"\n @click.prevent.stop=""\n >{{ text["view_feedback"]}}</a>\n <b-modal\n :id="\'r-competency-feedback-\'+cc.id"\n ok-only\n centered\n scrollable\n >\n <template #modal-header>\n <div>\n <h3><i class="fa fa-puzzle-piece"></i>{{ cc.title }}</h3><br>\n </div>\n </template>\n <span v-html="cc.feedback"></span>\n </b-modal>\n </td>\n </tr>\n </table>\n </template>\n </b-modal>\n </tr>\n </template>\n </table>\n '}),Vue.component("r-grading-bar",{props:{value:{type:Object,default:()=>({})},width:{type:Number,default:150},height:{type:Number,default:15}},data:()=>({text:strings.grading}),computed:{widthUnsubmitted(){return this.width*this.fractionUnsubmitted()},widthGraded(){return this.width*this.fractionGraded()},widthUngraded(){return this.width*this.fractionUngraded()},countUnsubmitted(){return this.value.students-this.value.graded-this.value.ungraded}},methods:{fractionUnsubmitted(){return this.value.students>0?1-(this.value.graded+this.value.ungraded)/this.value.students:1},fractionGraded(){return this.value.students>0?this.value.graded/this.value.students:0},fractionUngraded(){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: widthUngraded+'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: widthGraded+'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: widthUnsubmitted+'px'}\"\n class='r-grading-bar-segment r-grading-bar-unsubmitted'\n :title=\"text.unsubmitted + ' (' + countUnsubmitted + ')'\" v-b-popover.hover.top\n ></span\n ></span>\n "}),Vue.component("r-completion-bar",{props:{value:{type:Object,default:()=>({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:{widthIncomplete(){return this.width*this.fractionIncomplete()},widthCompleted(){return this.width*this.fractionCompleted()},widthCompletedPass(){return this.width*this.fractionCompletedPass()},widthCompletedFail(){return this.width*this.fractionCompletedFail()},widthUngraded(){return this.width*this.fractionUngraded()},countIncomplete(){return this.value.students-this.value.completed-this.value.completed_pass-this.value.completed_fail-this.value.ungraded}},methods:{fractionIncomplete(){return this.value.students>0?1-(this.value.completed+this.value.completed_pass+this.value.completed_fail+this.value.ungraded)/this.value.students:1},fractionCompleted(){return this.value.students>0?this.value.completed/this.value.students:0},fractionCompletedPass(){return this.value.students>0?this.value.completed_pass/this.value.students:0},fractionCompletedFail(){return this.value.students>0?this.value.completed_fail/this.value.students:0},fractionUngraded(){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: widthUngraded+'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: widthCompleted+'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: widthCompletedPass+'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: widthCompletedFail+'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: widthIncomplete+'px'}\"\n class='r-grading-bar-segment r-completion-bar-incomplete'\n :title=\"text.incomplete + ' (' + countIncomplete + ')'\" v-b-popover.hover.top\n ></span\n ></span>\n "}),Vue.component("r-completion-circle",{props:{value:{type:Object,default:()=>({students:10,completed:2,completedPass:2,completedFail:2,ungraded:2})},stroke:{type:Number,default:.2},disabled:{type:Boolean,default:!1},title:{type:String,default:""}},computed:{completedPass(){return this.value.completed_pass?this.value.completed_pass:this.value.completedPass},completedFail(){return this.value.completed_fail?this.value.completed_fail:this.value.completedFail},radius(){return 50-50*this.stroke},arcpathUngraded(){return this.arcpath(0,this.fractionUngraded())},arcpathCompleted(){const begin=this.fractionUngraded();return this.arcpath(begin,this.fractionCompleted())},arcpathCompletedPass(){const begin=this.fractionUngraded()+this.fractionCompleted();return this.arcpath(begin,this.fractionCompletedPass())},arcpathCompletedFail(){const begin=this.fractionUngraded()+this.fractionCompleted()+this.fractionCompletedPass();return this.arcpath(begin,this.fractionCompletedFail())},arcpathIncomplete(){const begin=this.fractionUngraded()+this.fractionCompleted()+this.fractionCompletedPass()+this.fractionCompletedFail();return this.arcpath(begin,this.fractionIncomplete())}},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*π)},fractionIncomplete(){return this.value.students>0?1-(this.value.completed+this.completedPass+this.completedFail+this.value.ungraded)/this.value.students:1},fractionCompleted(){return this.value.students>0?this.value.completed/this.value.students:0},fractionCompletedPass(){return this.value.students>0?this.completedPass/this.value.students:0},fractionCompletedFail(){return this.value.students>0?this.completedFail/this.value.students:0},fractionUngraded(){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="arcpathUngraded"\n :style="\'stroke-width: \' + (stroke*100) +\'; stroke: var(--warning); fill: none;\'"/>\n <path :d="arcpathCompleted"\n :style="\'stroke-width: \' + (stroke*100) +\'; stroke: var(--info); fill: none;\'"/>\n <path :d="arcpathCompletedPass"\n :style="\'stroke-width: \' + (stroke*100) +\'; stroke: var(--success); fill: none;\'"/>\n <path :d="arcpathCompletedFail"\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:()=>({})},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:()=>({})},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:()=>({})},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-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:()=>({})},guestmode:{type:Boolean,default:!1},teachermode:{type:Boolean,default:!1}},data:()=>({text:strings.badge}),computed:{completion(){return this.value.badge.issued?"completed":"incomplete"},issuedIcon(){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}},arcpathIssued(){if(this.value.badge.studentcount){const fraction=this.value.badge.issuedcount/this.value.badge.studentcount;return this.arcpath(0,fraction)}return""},arcpathProgress(){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},completionIconRq:complete=>complete?"check-square-o":"square-o",completionIcon: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="arcpathIssued"\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="arcpathProgress"\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-\'+issuedIcon+\' 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-\'+completionIcon(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-\'+completionIconRq(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 '}),Vue.component("r-item-dummy-course",{props:{value:{type:Object,default:()=>null}},data:()=>({text:strings.invalid}),methods:{},template:'\n <div class="r-item-dummy-course">\n <b-card no-body class="r-item-course">\n <b-row no-gutters>\n <b-col md="1">\n <span class="r-timing-indicator timing-dummy"></span>\n </b-col>\n <b-col md="11">\n <b-card-body class="align-items-center">\n \n </b-card-body>\n </b-col>\n </b-row>\n </b-card>\n </div>\n '}),Vue.component("r-item-dummy-filter",{props:{},data:()=>({}),computed:{},methods:{},template:'\n <div :class="\'r-item-dummy-filter\'">\n <i class="fa fa-circle"></i>\n </div>\n '}),Vue.component("r-item-dummy-badge",{props:{},data:()=>({}),computed:{},methods:{},template:'\n <div :class="\'r-item-dummy-badge\'" >\n <i class="fa fa-circle"></i>\n </div>\n '})}};return _exports.default=_default,_exports.default}));
|
|
|
|
//# sourceMappingURL=report-viewer-components.min.js.map
|