3 lines
137 KiB
JavaScript
3 lines
137 KiB
JavaScript
define("local_treestudyplan/studyplan-editor-components",["exports","./simpleline/simpleline","core/ajax","core/notification","./util/string-helper","./util/date-helper","./studyplan-processor","./util/debugger","core/config","./downloader","./util/fittext-vue","./util/settings","./treestudyplan-components","./util/mform-helper","./util/psidebar-vue","./vue-easy-dnd/vue-easy-dnd.esm"],(function(_exports,_simpleline,_ajax,_notification,_stringHelper,_dateHelper,_studyplanProcessor,_debugger,_config,_downloader,_fittextVue,_settings,_treestudyplanComponents,_mformHelper,_psidebarVue,_vueEasyDnd){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),_fittextVue=_interopRequireDefault(_fittextVue),_treestudyplanComponents=_interopRequireDefault(_treestudyplanComponents),_mformHelper=_interopRequireDefault(_mformHelper),_psidebarVue=_interopRequireDefault(_psidebarVue);const STUDYPLAN_EDITOR_FIELDS=["name","shortname","description","idnumber","context_id","aggregation","aggregation_config"],PERIOD_EDITOR_FIELDS=["fullname","shortname","startdate","enddate"];var _default={install(Vue){Vue.component("drag",_vueEasyDnd.Drag),Vue.component("drop",_vueEasyDnd.Drop),Vue.component("drop-list",_vueEasyDnd.DropList),Vue.use(_treestudyplanComponents.default),Vue.use(_mformHelper.default),Vue.use(_psidebarVue.default),Vue.use(_fittextVue.default);let debug=new _debugger.default("treestudyplan-editor");function isVisible(elem){return!!(elem.offsetWidth||elem.offsetHeight||elem.getClientRects().length)}const ItemEventBus=new Vue;let stringKeys=(0,_stringHelper.loadStringKeys)({conditions:[{value:"ALL",textkey:"condition_all"},{value:"ANY",textkey:"condition_any"}]}),strings=(0,_stringHelper.loadStrings)({studyplanText:{studyline_editmode:"studyline_editmode",toolbox_toggle:"toolbox_toggle",editmode_modules_hidden:"editmode_modules_hidden",studyline_add:"studyline_add",add:"add@core",edit:"edit@core",delete:"delete@core",studyline_name:"studyline_name",studyline_name_ph:"studyline_name_ph",studyline_shortname:"studyline_shortname",studyline_shortname_ph:"studyline_shortname_ph",studyline_enrollable:"studyline_enrollable",studyline_enrolroles:"studyline_enrolroles",studyline_color:"studyline_color",associations:"associations",associated_cohorts:"associated_cohorts",associated_users:"associated_users",studyline_edit:"studyline_edit",studyplan_name:"studyplan_name",studyplan_name_ph:"studyplan_name_ph",studyplan_shortname:"studyplan_shortname",studyplan_shortname_ph:"studyplan_shortname_ph",studyplan_description:"studyplan_description",studyplan_description_ph:"studyplan_description_ph",studyplan_idnumber:"studyplan_idnumber",studyplan_idnumber_ph:"studyplan_idnumber_ph",studyplan_slots:"studyplan_slots",studyplan_startdate:"studyplan_startdate",studyplan_enddate:"studyplan_enddate",line_enrollable_0:"line_enrollable:0",line_enrollable_1:"line_enrollable:1",line_enrollable_2:"line_enrollable:2",line_enrollable_3:"line_enrollable:3",drophere:"drophere",studylineConfirmRemove:"studyline_confirm_remove",studyplanConfirmRemove:"studyplan_confirm_remove"},studyplanAdvanced:{advanced_tools:"advanced_tools",confirm_cancel:"confirm_cancel",confirm_ok:"confirm_ok",success:"success@core",error:"failed@completion",advanced_converted:"advanced_converted",advanced_skipped:"advanced_skipped",advanced_failed:"advanced_failed",advanced_locked:"advanced_locked",advanced_multiple:"advanced_multiple",advanced_error:"advanced_error",advanced_tools_heading:"advanced_tools_heading",advanced_warning_title:"advanced_warning_title",advanced_warning:"advanced_warning",advanced_pick_scale:"advanced_pick_scale",advanced_course_manipulation_title:"advanced_course_manipulation_title",advanced_bulk_course_timing:"advanced_bulk_course_timing",advanced_bulk_course_timing_desc:"advanced_bulk_course_timing_desc",advanced_force_scale_title:"advanced_force_scale_title",advanced_force_scale_desc:"advanced_force_scale_desc",advanced_force_scale_button:"advanced_force_scale_button",advanced_confirm_header:"advanced_confirm_header",advanced_force_scale_confirm:"advanced_force_scale_confirm",advanced_backup_restore:"advanced_backup_restore",advanced_restore:"advanced_restore",advanced_backup:"advanced_backup",advanced_restore_pages:"advanced_restore_pages",advanced_restore_lines:"advanced_restore_lines",advanced_backup_plan:"advanced_backup_plan",advanced_backup_page:"advanced_backup_page",advanced_export:"advanced_export",advanced_export_csv_plan:"advanced_export_csv_plan",advanced_export_csv_page:"advanced_export_csv_page",advanced_import_from_file:"advanced_import_from_file",advanced_purge:"advanced_purge",advanced_purge_plan:"advanced_purge_plan",advanced_purge_plan_expl:"advanced_purge_plan_expl",advanced_purge_page:"advanced_purge_page",advanced_purge_page_expl:"advanced_purge_page_expl",advanced_cascade_cohortsync_title:"advanced_cascade_cohortsync_title",advanced_cascade_cohortsync_desc:"advanced_cascade_cohortsync_desc",advanced_cascade_cohortsync:"advanced_cascade_cohortsync",currentpage:"currentpage"},studyplanEdit:{studyplanEdit:"studyplan_edit",studyplan_add:"studyplan_add",studyplanpage_add:"studyplanpage_add",studyplanpage_edit:"studyplanpage_edit",info_periodsextended:"studyplanpage_info_periodsextended",warning:"warning@core"},periodEdit:{edit:"period_edit",fullname:"studyplan_name",shortname:"studyplan_shortname",startdate:"studyplan_startdate",enddate:"studyplan_enddate"},courseTiming:{title:"course_timing_title",desc:"course_timing_desc",question:"course_timing_question",warning:"course_timing_warning",timing_ok:"course_timing_ok",timing_off:"course_timing_off",course:"course@core",period:"period",yes:"yes$core",no:"no$core",duration:"duration",years:"years$core",year:"year$core",weeks:"weeks$core",week:"week$core",days:"days$core",day:"day$core",rememberchoice:"course_timing_rememberchoice",hidewarning:"course_timing_hidewarning",periodspan:"course_period_span",periods:"periods",periodspan_desc:"course_period_span_desc"},studyplanAssociate:{associations:"associations",associated_cohorts:"associated_cohorts",associated_users:"associated_users",associated_coaches:"associated_coaches",associate_cohorts:"associate_cohorts",associate_users:"associate_users",associate_coached:"associate_coaches",add_association:"add_association",delete_association:"delete_association",associations_empty:"associations_empty",associations_search:"associations_search",cohorts:"cohorts",users:"users",coaches:"coaches",selected:"selected",name:"name",context:"context",search:"search"},itemText:{select_conditions:"select_conditions",item_configuration:"item_configuration",ok:"ok@core",delete:"delete@core",item_delete_message:"item_delete_message",type_course:"course@core",type_junction:"tool-junction",type_start:"tool-start",type_finish:"tool-finish",type_badge:"tool-badge",type_invalid:"course-invalid"},itemCourseText:{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",ok:"ok@core",cancel:"cancel@core",delete:"delete@core",noenddate:"noenddate"},invalid:{error:"error"},completion:{completion_completed:"completion_completed",completion_incomplete:"completion_incomplete",aggregation_all:"aggregation_all",aggregation_any:"aggregation_any",aggregation_overall_all:"aggregation_overall_all",aggregation_overall_any:"aggregation_overall_any",completion_not_configured:"completion_not_configured",configure_completion:"configure_completion"},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"},badge:{share_badge:"share_badge",dateissued:"dateissued",dateexpire:"dateexpire",badgeinfo:"badgeinfo"},toolbox:{toolbox:"toolbox",toolbarRight:"toolbar-right",courses:"courses",flow:"flow",toolJunction:"tool-junction",toolFinish:"tool-finish",toolStart:"tool-start",badges:"badges",relatedbadges:"relatedbages@badges",filter:"filter@core",sitebadges:"sitebadges@badges"}});Vue.component("t-studyplan-advanced",{props:{value:{type:Object,default:()=>null},selectedpage:{type:Object,default:()=>null}},data:()=>({forceScales:{selectedScale:null,result:[]},text:strings.studyplanAdvanced}),computed:{scales(){return[{id:null,disabled:!0,name:this.text.advanced_pick_scale}].concat(this.value.advanced.force_scales.scales)}},methods:{forceScalesStart(){const self=this;this.$bvModal.msgBoxConfirm(this.text.advanced_force_scale_confirm,{title:this.text.advanced_force_scale_confirm,okVariant:"danger",okTitle:this.text.confirm_ok,cancelTitle:this.text.confirm_cancel}).then((value=>{1==value&&(0,_ajax.call)([{methodname:"local_treestudyplan_force_studyplan_scale",args:{studyplan_id:this.value.id,scale_id:this.forceScales.selectedScale}}])[0].then((response=>{self.forceScales.result=response})).catch(_notification.default.exception)})).catch(_notification.default.exception)},exportPage(format){const self=this;null!=format&&["json","csv"].includes(format)||(format="json"),(0,_ajax.call)([{methodname:"local_treestudyplan_export_page",args:{page_id:this.selectedpage.id,format:format}}])[0].then((response=>{(0,_downloader.download)(self.value.shortname+".page."+format,response.content,response.format)})).catch(_notification.default.exception)},exportPlan(){const self=this;(0,_ajax.call)([{methodname:"local_treestudyplan_export_plan",args:{studyplan_id:this.value.id,format:"json"}}])[0].then((response=>{(0,_downloader.download)(self.value.shortname+".plan.json",response.content,response.format)})).catch(_notification.default.exception)},bulkCourseTiming(){const self=this;(0,_ajax.call)([{methodname:"local_treestudyplan_bulk_course_timing",args:{page_id:this.selectedpage.id}}])[0].then((response=>{response.success?location.reload():(self.$bvModal.msgBoxOk(response.msg,{title:"Could not set bulk course timing"}),debug.error("Could not set bulk course timing: ",response.msg))})).catch(_notification.default.exception)},importStudylines(){const self=this;(0,_downloader.upload)(((filename,content)=>{(0,_ajax.call)([{methodname:"local_treestudyplan_import_studylines",args:{page_id:this.selectedpage.id,content:content,format:"application/json"}}])[0].then((response=>{response.success?location.reload():(self.$bvModal.msgBoxOk(response.msg,{title:"Import failed"}),debug.error("Import failed: ",response.msg))})).catch(_notification.default.exception)}),"application/json")},importPages(){const self=this;(0,_downloader.upload)(((filename,content)=>{(0,_ajax.call)([{methodname:"local_treestudyplan_import_pages",args:{studyplan_id:this.value.id,content:content,format:"application/json"}}])[0].then((response=>{response.success?location.reload():(self.$bvModal.msgBoxOk(response.msg,{title:"Import failed"}),debug.error("Import failed: ",response.msg))})).catch(_notification.default.exception)}),"application/json")},purgeStudyplan(){const self=this;(0,_ajax.call)([{methodname:"local_treestudyplan_delete_studyplan",args:{id:this.value.id,force:!0}}])[0].then((response=>{response.success?location.reload():(self.$bvModal.msgBoxOk(response.msg,{title:"Could not delete plan "}),debug.error("Could not delete plan: ",response.msg))})).catch(_notification.default.exception)},purgeStudyplanpage(){const self=this;this.selectedpage&&(0,_ajax.call)([{methodname:"local_treestudyplan_delete_studyplanpage",args:{id:this.selectedpage.id,force:!0}}])[0].then((response=>{response.success?location.reload():(self.$bvModal.msgBoxOk(response.msg,{title:"Could not delete page"}),debug.error("Could not delete page: ",response.msg))})).catch(_notification.default.exception)},cascadeCohortsync(){const self=this;(0,_ajax.call)([{methodname:"local_treestudyplan_cascade_cohortsync",args:{studyplan_id:this.value.id}}])[0].then((response=>{self.$bvModal.msgBoxOk(response.success?self.text.success:self.text.error,{title:self.text.advanced_cascade_cohortsync})})).catch(_notification.default.exception)},modalClose(){this.forceScales.result=[]}},template:'\n <span>\n <a v-if="value.advanced"\n href=\'#\'\n @click.prevent=\'\'\n class=\'text-danger\'\n v-b-modal="\'t-studyplan-\'+value.id+\'-advanced\'"\n ><i class=\'fa fa-wrench\'></i> {{text.advanced_tools}}</a>\n <b-modal v-if="value.advanced"\n :id="\'t-studyplan-\'+value.id+\'-advanced\'"\n size="lg"\n :title="text.advanced_tools_heading"\n ok-only\n @hide="modalClose"\n body-class="p-0"\n >\n <b-tabs card>\n <b-tab :title="text.advanced_warning_title" active>\n {{ text.advanced_warning}}\n </b-tab>\n <b-tab :title="text.advanced_course_manipulation_title" >\n <h3>{{ text.advanced_cascade_cohortsync_title}}</h3>\n <p>{{ text.advanced_cascade_cohortsync_desc}}</p>\n <p class="mt-2"><b-button\n variant="info"\n @click.prevent="cascadeCohortsync"\n >{{ text.advanced_cascade_cohortsync}}</b-button></p>\n <h3>{{ text.advanced_bulk_course_timing}}</h3>\n <p>{{ text.advanced_bulk_course_timing_desc}}</p>\n <p>{{text.currentpage}} <i>{{selectedpage.fullname}}</i></p>\n <p class="mt-2"><b-button\n variant="info"\n @click.prevent="bulkCourseTiming"\n >{{ text.advanced_bulk_course_timing}}</b-button></p>\n <template v-if="[\'bistate\',\'tristate\'].includes(value.aggregation)">\n <h3>{{ text.advanced_force_scale_title}}</h3>\n {{ text.advanced_force_scale_desc}}\n <p class="mt-2"><b-form-select v-model="forceScales.selectedScale"\n :options="scales" text-field="name" value-field="id"\n ></b-form-select>\n <b-button\n variant="danger"\n :disabled="forceScales.selectedScale == null"\n @click.prevent="forceScalesStart"\n >{{ text.advanced_force_scale_button}}</b-button>\n </p>\n <p class="mt-2">\n <ul class=\'t-advanced-scrollable\' v-if="forceScales.result.length > 0">\n <li v-for="c in forceScales.result">\n <span class=\'t-advanced-coursename\'>{{c.course.fullname}}</span>\n <ul v-if="c.grades.length > 0">\n <li v-for=\'g in c.grades\'\n ><span class=\'t-advanced-gradename\'><span v-html="g.name"></span></span>\n <span v-if="g.changed == \'converted\'" class=\'t-advanced-status changed\'\n >{{text.advanced_converted}}</span\n ><span v-else-if="g.changed == \'skipped\'" class=\'t-advanced-status skipped\'\n >{{text.advanced_skipped}}</span\n ><span v-else class=\'t-advanced-status skipped\'\n >{{text.advanced_error}}</span\n ></li>\n </ul>\n </li>\n </ul>\n </p>\n </template>\n </b-tab>\n <b-tab :title=\'text.advanced_backup_restore\'>\n <h3>{{ text.advanced_backup }}</h3>\n <p><b-button\n variant="primary"\n @click.prevent="exportPage(\'json\')"\n >{{ text.advanced_backup_page }}</b-button>\n {{text.currentpage}} <i>{{selectedpage.fullname}}</i></p>\n <p><b-button\n variant="primary"\n @click.prevent="exportPlan(\'json\')"\n >{{ text.advanced_backup_plan }}</b-button></p>\n <h3>{{ text.advanced_restore }}</h3>\n <p><b-button\n variant="danger"\n @click.prevent="importStudylines"\n >{{ text.advanced_restore_lines}}</b-button></p>\n <p><b-button\n variant="danger"\n @click.prevent="importPages"\n >{{ text.advanced_restore_pages }}</b-button></p>\n <h3>{{ text.advanced_export }}</h3>\n <p><b-button\n variant="primary"\n @click.prevent="exportPage(\'csv\')"\n >{{ text.advanced_export_csv_page }}</b-button>\n {{text.currentpage}} <i>{{selectedpage.fullname}}</i></p>\n </b-tab>\n <b-tab :title=\'text.advanced_purge\'>\n <p>{{text.advanced_purge_page_expl}}</p>\n <p>{{text.currentpage}} <i>{{selectedpage.fullname}}</i></p>\n <p><b-button\n variant="danger"\n @click.prevent="purgeStudyplanpage"\n >{{ text.advanced_purge_page}}</b-button></p>\n <p>{{text.advanced_purge_plan_expl}}</p>\n <p><b-button\n variant="danger"\n @click.prevent="purgeStudyplan"\n >{{ text.advanced_purge_plan}}</b-button></p>\n </b-tab>\n </b-tabs>\n </b-modal>\n </span>\n '}),Vue.component("t-studyplan-edit",{props:{value:{type:Object,default:()=>null},mode:{type:String,default:()=>"edit"},type:{type:String,default:()=>"link"},variant:{type:String,default:()=>""},contextid:{type:Number,default:1}},data:()=>({text:strings.studyplanEdit}),computed:{},methods:{planSaved(updatedplan){const self=this;if(debug.info("Got new plan data",updatedplan),"create"==self.mode)self.$emit("created",updatedplan);else{const movedFrom=self.value.context_id,movedTo=updatedplan.context_id,moved=movedFrom!=movedTo;updatedplan.pages[0].periods!=self.value.pages[0].periods?(0,_ajax.call)([{methodname:"local_treestudyplan_get_studyplan_map",args:{id:self.value.id}}])[0].then((response=>{self.value=(0,_studyplanProcessor.processStudyplan)(response,!0),debug.info("studyplan processed"),self.$emit("input",self.value)})).catch((function(error){_notification.default.exception(error)})):((0,_studyplanProcessor.objCopy)(self.value,updatedplan,STUDYPLAN_EDITOR_FIELDS),self.$emit("input",self.value),moved&&self.$emit("moved",self.value,movedFrom,movedTo))}}},template:'\n <span class=\'s-studyplan-edit\'>\n <mform\n name="studyplan_editform"\n :params="{studyplan_id: value.id, mode: mode, contextid: contextid }"\n @saved="planSaved"\n :variant="variant"\n :type="type"\n :title="(mode == \'create\')?text.studyplan_add:text.studyplanEdit"\n ><slot><i class=\'fa fa-gear\'></i></slot></mform>\n </span>\n '}),Vue.component("t-studyplan-page-edit",{props:{value:{type:Object,default:()=>null},mode:{type:String,default:()=>"edit"},type:{type:String,default:()=>"link"},variant:{type:String,default:()=>""},studyplan:{type:Object}},data:()=>({text:strings.studyplanEdit}),computed:{},methods:{planSaved(updatedpage){const self=this;if("create"==self.mode)self.$emit("created",updatedpage);else{const page=(0,_studyplanProcessor.processStudyplanPage)(updatedpage);debug.info("studyplan page processed"),self.value.periods<page.periods&&this.$bvModal.msgBoxOk(this.text.info_periodsextended,{title:this.text.warning,okVariant:"success",centered:!0}),self.$emit("input",page)}}},template:'\n <span class=\'s-studyplan-page-edit\'>\n <mform\n name="studyplanpage_editform"\n :params="{page_id: value.id, studyplan_id: studyplan.id, mode: mode }"\n @saved="planSaved"\n :variant="variant"\n :type="type"\n :title="(mode == \'create\')?text.studyplanpage_add:text.studyplanpage_edit"\n ><slot><i class=\'fa fa-gear\'></i></slot></mform>\n </span>\n '}),Vue.component("t-studyplan-associate",{props:["value"],data:()=>({show:!1,config:{userfields:[{key:"selected"},{key:"firstname",sortable:!0},{key:"lastname",sortable:!0}],cohortfields:[{key:"selected"},{key:"name",sortable:!0},{key:"context",sortable:!0}]},association:{cohorts:[],users:[],coaches:[]},loading:{cohorts:!1,users:!1,coaches:!1},search:{users:[],cohorts:[],coaches:[]},selected:{search:{users:[],cohorts:[],coaches:[]},associated:{users:[],cohorts:[],coaches:[]}},text:strings.studyplanAssociate}),methods:{showModal(){this.show=!0,this.loadAssociations()},cohortOptionModel:c=>({value:c.id,text:c.name+" ("+c.context.path.join(" / ")+")"}),userOptionModel:u=>({value:u.id,text:u.firstname+" "+u.lastname}),loadAssociations(){const self=this;self.loading.cohorts=!0,self.loading.users=!0,(0,_ajax.call)([{methodname:"local_treestudyplan_associated_users",args:{studyplan_id:self.value.id}}])[0].then((response=>{self.association.users=response.map(self.userOptionModel),self.loading.users=!1})).catch(_notification.default.exception),(0,_ajax.call)([{methodname:"local_treestudyplan_associated_cohorts",args:{studyplan_id:self.value.id}}])[0].then((response=>{self.association.cohorts=response.map(self.cohortOptionModel),self.loading.cohorts=!1})).catch(_notification.default.exception),self.loading.coaches=!0,(0,_ajax.call)([{methodname:"local_treestudyplan_associated_coaches",args:{studyplan_id:self.value.id}}])[0].then((response=>{self.association.coaches=response.map(self.userOptionModel),self.loading.coaches=!1})).catch(_notification.default.exception)},searchCohorts(searchtext){const self=this;searchtext.length>0?(0,_ajax.call)([{methodname:"local_treestudyplan_list_cohort",args:{like:searchtext,studyplan_id:self.value.id}}])[0].then((response=>{self.search.cohorts=response.map(self.cohortOptionModel)})).catch(_notification.default.exception):self.search.cohorts=[]},cohortAssociate(){const self=this;const associated=self.association.cohorts,search=self.search.cohorts,searchselected=self.selected.search.cohorts;for(const i in searchselected){const r=searchselected[i];(0,_ajax.call)([{methodname:"local_treestudyplan_connect_cohort",args:{studyplan_id:self.value.id,cohort_id:r}}])[0].then((response=>{response.success&&(0,_studyplanProcessor.transportItem)(associated,search,r)})).catch(_notification.default.exception)}(0,_ajax.call)([])},cohortDisassociate(){const self=this,associatedselected=self.selected.associated.cohorts,associated=self.association.cohorts,search=self.search.cohorts;for(const i in associatedselected){const r=associatedselected[i];(0,_ajax.call)([{methodname:"local_treestudyplan_disconnect_cohort",args:{studyplan_id:self.value.id,cohort_id:r}}])[0].then((response=>{response.success&&(0,_studyplanProcessor.transportItem)(search,associated,r)})).catch(_notification.default.exception)}},searchUsers(searchtext){const self=this;searchtext.length>0?(0,_ajax.call)([{methodname:"local_treestudyplan_find_user",args:{like:searchtext,studyplan_id:self.value.id}}])[0].then((response=>{self.search.users=response.map(self.userOptionModel)})).catch(_notification.default.exception):self.search.users=[]},userAssociate(){const self=this,associated=self.association.users,search=self.search.users,searchselected=self.selected.search.users;for(const i in searchselected){const r=searchselected[i];(0,_ajax.call)([{methodname:"local_treestudyplan_connect_user",args:{studyplan_id:self.value.id,user_id:r}}])[0].then((response=>{response.success&&(0,_studyplanProcessor.transportItem)(associated,search,r)})).catch(_notification.default.exception)}},userDisassociate(){const self=this,associated=self.association.users,associatedselected=self.selected.associated.users,search=self.search.users;for(const i in associatedselected){const r=associatedselected[i];(0,_ajax.call)([{methodname:"local_treestudyplan_disconnect_user",args:{studyplan_id:self.value.id,user_id:r}}])[0].then((response=>{response.success&&(0,_studyplanProcessor.transportItem)(search,associated,r)})).catch(_notification.default.exception)}},searchCoaches(searchtext){const self=this;searchtext.length>0?(0,_ajax.call)([{methodname:"local_treestudyplan_find_coach",args:{like:searchtext,studyplan_id:self.value.id}}])[0].then((response=>{self.search.coaches=response.map(self.userOptionModel)})).catch(_notification.default.exception):self.search.coaches=[]},coachAssociate(){const self=this,associated=self.association.coaches,search=self.search.coaches,searchselected=self.selected.search.coaches;for(const i in searchselected){const r=searchselected[i];(0,_ajax.call)([{methodname:"local_treestudyplan_connect_coach",args:{studyplan_id:self.value.id,user_id:r}}])[0].then((response=>{response.success&&(0,_studyplanProcessor.transportItem)(associated,search,r)})).catch(_notification.default.exception)}},coachDisassociate(){const self=this,associated=self.association.coaches,associatedselected=self.selected.associated.coaches,search=self.search.coaches;for(const i in associatedselected){const r=associatedselected[i];(0,_ajax.call)([{methodname:"local_treestudyplan_disconnect_coach",args:{studyplan_id:self.value.id,user_id:r}}])[0].then((response=>{response.success&&(0,_studyplanProcessor.transportItem)(search,associated,r)})).catch(_notification.default.exception)}}},template:'\n<span class=\'s-studyplan-associate\'\n ><a href=\'#\' @click.prevent="showModal" ><slot><i class=\'fa fa-users\'></i></slot></a>\n <b-modal\n v-model="show"\n size="lg"\n ok-variant="primary"\n :title="text.associations + \' - \' + value.name"\n ok-only>\n <b-tabs class=\'s-studyplan-associate-window\'>\n <b-tab :title="text.cohorts">\n <b-container>\n <b-row class=\'mb-2 mt-2\'>\n <b-col>{{text.associated_cohorts}}</b-col>\n <b-col>{{text.associate_cohorts}}</b-col>\n </b-row>\n <b-row class=\'mb-2\'>\n <b-col>\n </b-col>\n <b-col>\n <b-form-input\n type="text" @input="searchCohorts($event)"\n :placeholder="text.search"></b-form-input>\n </b-col>\n </b-row>\n <b-row>\n <b-col>\n <b-form-select\n multiple\n v-model="selected.associated.cohorts"\n :options="association.cohorts"\n :select-size="10"\n ></b-form-select>\n </b-col>\n <b-col>\n <b-form-select\n multiple\n v-model="selected.search.cohorts"\n :options="search.cohorts"\n :select-size="10"\n ></b-form-select>\n </b-col>\n </b-row>\n <b-row class=\'mt-2\'>\n <b-col>\n <b-button variant=\'danger\' @click.prevent="cohortDisassociate()"\n ><i class=\'fa fa-chain-broken\'></i> {{text.delete_association}}</b-button>\n </b-col>\n <b-col>\n <b-button variant=\'success\' @click.prevent="cohortAssociate()"\n ><i class=\'fa fa-link\'></i> {{text.add_association}}</b-button>\n </b-col>\n </b-row>\n </b-container>\n </b-tab>\n <b-tab :title="text.users">\n <b-container>\n <b-row class=\'mb-2 mt-2\'>\n <b-col>{{text.associated_users}}</b-col>\n <b-col>{{text.associate_users}}</b-col>\n </b-row>\n <b-row class=\'mb-2\'>\n <b-col>\n </b-col>\n <b-col>\n <b-form-input\n type="text"\n @input="searchUsers($event)"\n :placeholder="text.search + \' \' + text.users"></b-form-input>\n </b-col>\n </b-row>\n <b-row>\n <b-col>\n <b-form-select\n multiple\n v-model="selected.associated.users"\n :options="association.users"\n :select-size="10"\n ></b-form-select>\n </b-col>\n <b-col>\n <b-form-select\n multiple\n v-model="selected.search.users"\n :options="search.users"\n :select-size="10"\n ></b-form-select>\n </b-col>\n </b-row>\n <b-row class=\'mt-2\'>\n <b-col>\n <b-button variant=\'danger\' @click.prevent="userDisassociate()"\n ><i class=\'fa fa-chain-broken\'></i> {{text.delete_association}}</b-button>\n </b-col>\n <b-col>\n <b-button variant=\'success\' @click.prevent="userAssociate()"\n ><i class=\'fa fa-link\'></i> {{text.add_association}}</b-button>\n </b-col>\n </b-row>\n </b-container>\n </b-tab>\n <b-tab :title="text.coaches">\n <b-container>\n <b-row class=\'mb-2 mt-2\'>\n <b-col>{{text.associated_coaches}}</b-col>\n <b-col>{{text.associate_coaches}}</b-col>\n </b-row>\n <b-row class=\'mb-2\'>\n <b-col>\n </b-col>\n <b-col>\n <b-form-input\n type="text"\n @input="searchCoaches($event)"\n :placeholder="text.search + \' \' + text.coaches"></b-form-input>\n </b-col>\n </b-row>\n <b-row>\n <b-col>\n <b-form-select\n multiple\n v-model="selected.associated.coaches"\n :options="association.coaches"\n :select-size="10"\n ></b-form-select>\n </b-col>\n <b-col>\n <b-form-select\n multiple\n v-model="selected.search.coaches"\n :options="search.coaches"\n :select-size="10"\n ></b-form-select>\n </b-col>\n </b-row>\n <b-row class=\'mt-2\'>\n <b-col>\n <b-button variant=\'danger\' @click.prevent="coachDisassociate()"\n ><i class=\'fa fa-chain-broken\'></i> {{text.delete_association}}</b-button>\n </b-col>\n <b-col>\n <b-button variant=\'success\' @click.prevent="coachAssociate()"\n ><i class=\'fa fa-link\'></i> {{text.add_association}}</b-button>\n </b-col>\n </b-row>\n </b-container>\n </b-tab>\n </b-tabs>\n </b-modal>\n</span>\n'}),Vue.component("t-period-edit",{props:{value:{type:Object,default:()=>null},type:{type:String,default:()=>"link"},variant:{type:String,default:()=>""},minstart:{type:String,default:()=>null},maxend:{type:String,default:()=>null}},data:()=>({show:!1,editdata:{fullname:"",shortname:"",startdate:(new Date).getFullYear()+"-08-01",enddate:(new Date).getFullYear()+1+"-08-01"},text:strings.periodEdit}),methods:{editStart(){(0,_studyplanProcessor.objCopy)(this.editdata,this.value,PERIOD_EDITOR_FIELDS),this.show=!0},editFinish(){const self=this;let args={id:this.value.id};(0,_studyplanProcessor.objCopy)(args,this.editdata,PERIOD_EDITOR_FIELDS),(0,_ajax.call)([{methodname:"local_treestudyplan_edit_period",args:args}])[0].then((response=>{(0,_studyplanProcessor.objCopy)(self.value,response,PERIOD_EDITOR_FIELDS),self.$emit("input",self.value),self.$emit("edited",self.value)})).catch(_notification.default.exception)},refresh(){const self=this;(0,_ajax.call)([{methodname:"local_treestudyplan_get_period",args:{id:this.value.id}}])[0].then((response=>{(0,_studyplanProcessor.objCopy)(self.value,response,PERIOD_EDITOR_FIELDS),self.$emit("input",self.value)})).catch(_notification.default.exception)},addDay:(date,days)=>(void 0===days&&(days=1),(0,_dateHelper.addDays)(date,days)),subDay:(date,days)=>(void 0===days&&(days=1),(0,_dateHelper.addDays)(date,0-days))},template:'\n <span class=\'t-period-edit\'>\n <b-button :variant="variant" v-if=\'type == "button"\' @click.prevent=\'editStart()\'\n ><slot><i class=\'fa fa-gear\'></i></slot></b-button>\n <a variant="variant" v-else href=\'#\' @click.prevent=\'editStart()\'\n ><slot><i class=\'fa fa-gear\'></i></slot></a>\n <b-modal\n v-model="show"\n size="lg"\n ok-variant="primary"\n :title="text.edit"\n @ok="editFinish()"\n :ok-disabled="Math.min(editdata.fullname.length,editdata.shortname.length) == 0"\n >\n <b-container>\n <b-row>\n <b-col cols="4">{{ text.fullname}}</b-col>\n <b-col cols="8">\n <b-form-input v-model="editdata.fullname"\n :state=\'editdata.fullname.length>0\'\n ></b-form-input>\n </b-col>\n </b-row>\n <b-row>\n <b-col cols="4">{{ text.shortname}}</b-col>\n <b-col cols="8">\n <b-form-input v-model="editdata.shortname"\n :state=\'editdata.shortname.length>0\'\n ></b-form-input>\n </b-col>\n </b-row>\n <b-row v-if="!value.timeless">\n <b-col cols="4">{{ text.studyplan_startdate}}</b-col>\n <b-col cols="8">\n <b-form-datepicker\n start-weekday="1"\n v-model="editdata.startdate"\n :min="(minstart ? minstart : \'\')"\n :max="subDay(value.enddate)"\n ></b-form-datepicker>\n </b-col>\n </b-row>\n <b-row v-if="!value.timeless">\n <b-col cols="4">{{ text.studyplan_enddate}}</b-col>\n <b-col cols="8">\n <b-form-datepicker\n start-weekday="1"\n v-model="editdata.enddate"\n :min="addDay(value.startdate)"\n :max="(maxend ? maxend : \'\')"\n ></b-form-datepicker>\n </b-col>\n </b-row>\n </b-container>\n </b-modal>\n </span>\n '}),Vue.component("t-studyplan",{props:{value:{type:Object},coaching:{type:Boolean,default:!1}},data:()=>({config:{userfields:[{key:"selected"},{key:"firstname",sortable:!0},{key:"lastname",sortable:!0}],cohortfields:[{key:"selected"},{key:"name",sortable:!0},{key:"context",sortable:!0}]},create:{studyline:{name:"",shortname:"",color:"#DDDDDD",enrol:{enrollable:0,enrolroles:[]}},page:{id:-1,name:"",shortname:""}},edit:{toolbox_shown:!1,studyline:{editmode:!1,data:{name:"",shortname:"",color:"#DDDDDD",enrol:{enrollable:0,enrolroles:[]}},original:{},availableroles:[]},studyplan:{data:{name:"",shortname:"",description:"",slots:4,startdate:"2020-08-01",enddate:"",aggregation:"",aggregation_config:"",aggregation_info:{useRequiredGrades:!0,useItemCondition:!1}},original:{}}},text:strings.studyplanText,cache:{linelayers:{}},selectedpageindex:0,emptyline:{id:-1,name:"<No study lines defined>",shortname:"<No study lines>",color:"#FF0000",filterslots:[{}],courseslots:[{}]},availableroles:[]}),created(){const self=this;ItemEventBus.$on("coursechange",(()=>{self.$emit("pagechanged",this.selectedpage)}))},mounted(){const self=this;0!=this.value.pages[0].studylines.length||this.coaching||(this.edit.studyline.editmode=!0),self.coaching?self.edit.toolbox_shown=!0:(0,_ajax.call)([{methodname:"local_treestudyplan_list_roles",args:{studyplan_id:this.value.id}}])[0].then((response=>{self.availableroles=response})).catch(_notification.default.exception),this.$root.$emit("redrawLines"),this.$emit("pagechanged",this.selectedpage)},beforeUnmount(){this.edit.toolbox_shown=!1,debug.info("Hiding toolbar because of destroy")},deactivated(){this.edit.toolbox_shown=!1,debug.info("Hiding toolbar because of deactivation")},activated(){this.coaching&&(self.edit.toolbox_shown=!0)},updated(){this.$root.$emit("redrawLines"),ItemEventBus.$emit("redrawLines")},computed:{selectedpage(){return this.value.pages[this.selectedpageindex]},hivizdrop:()=>(0,_settings.settings)("hivizdropslots")},methods:{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+";"},trashbinAccepts:type=>!!type.item,countLineLayers(line,page){if(this.cache.linelayers[line.id]&&new Date-this.cache.linelayers[line.id].timestamp<1e3)return this.cache.linelayers[line.id].value;{let maxLayer=-1;for(let i=0;i<=page.periods;i++)if(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 this.cache.linelayers[line.id]={value:maxLayer+1,timestamp:new Date},maxLayer+1}},slotsempty(slots){if(Array.isArray(slots)){let count=0;for(let i=0;i<slots.length;i++)Array.isArray(slots[i].courses)&&(count+=slots[i].courses.length),Array.isArray(slots[i].filters)&&(count+=slots[i].filters.length);return 0==count}return!1},movedStudyplan(plan,from,to){this.$emit("moved",plan,from,to)},addStudyLine(page,newlineinfo){(0,_ajax.call)([{methodname:"local_treestudyplan_add_studyline",args:{page_id:page.id,name:newlineinfo.name,shortname:newlineinfo.shortname,color:newlineinfo.color,sequence:page.studylines.length,enrollable:newlineinfo.enrol.enrollable,enrolroles:newlineinfo.enrol.enrolroles}}])[0].then((response=>{page.studylines.push(response),newlineinfo.name="",newlineinfo.shortname="",newlineinfo.color="#dddddd",newlineinfo.enrol.enrollable=0,newlineinfo.enrol.enrolroles=[]})).catch(_notification.default.exception)},editLineStart(line){const page=this.value.pages[this.selectedpageindex];debug.info("Starting line edit",line),Object.assign(this.edit.studyline.data,line),this.edit.studyline.original=line,this.$bvModal.show("modal-edit-studyline-"+page.id)},editLineFinish(){let editedline=this.edit.studyline.data,originalline=this.edit.studyline.original;(0,_ajax.call)([{methodname:"local_treestudyplan_edit_studyline",args:{id:editedline.id,name:editedline.name,shortname:editedline.shortname,color:editedline.color,enrollable:editedline.enrol.enrollable,enrolroles:editedline.enrol.enrolroles}}])[0].then((response=>{originalline.name=response.name,originalline.shortname=response.shortname,originalline.color=response.color,originalline.enrol.enrollable=response.enrol.enrollable,originalline.enrol.enrolroles=response.enrol.enrolroles})).catch(_notification.default.exception)},deleteLine(page,line){this.$bvModal.msgBoxConfirm(this.text.studylineConfirmRemove.replace("{$a}",line.name),{okTitle:this.text.delete,okVariant:"danger"}).then((modalresponse=>{modalresponse&&(0,_ajax.call)([{methodname:"local_treestudyplan_delete_studyline",args:{id:line.id}}])[0].then((response=>{if(1==response.success){let index=page.studylines.indexOf(line);page.studylines.splice(index,1)}})).catch(_notification.default.exception)})).catch(_notification.default.exception)},reorderLines(event,lines){event.apply(lines);let sequence=[];for(let idx in lines)sequence.push({id:lines[idx].id,sequence:idx});(0,_ajax.call)([{methodname:"local_treestudyplan_reorder_studylines",args:{sequence:sequence}}])[0].then((()=>{})).catch(_notification.default.exception)},deletePlan(studyplan){const self=this;self.$bvModal.msgBoxConfirm(this.text.studyplabConfirmRemove.replace("{$a}",studyplan.name),{okTitle:this.text.delete,okVariant:"danger"}).then((function(modalresponse){modalresponse&&(0,_ajax.call)([{methodname:"local_treestudyplan_delete_studyplan",args:{id:studyplan.id,force:!0}}])[0].then((response=>{1==response.success&&self.$root.$emit("studyplanRemoved",studyplan)})).catch(_notification.default.exception)})).catch(_notification.default.exception)},deleteStudyItem(event){let item=event.data;(0,_ajax.call)([{methodname:"local_treestudyplan_delete_studyitem",args:{id:item.id}}])[0].then((response=>{1==response.success&&event.source.$emit("cut",event)})).catch(_notification.default.exception)},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},periodEdited(pi){const prev=this.$refs["periodeditor-"+(pi.period-1)];prev&&prev[0]&&prev[0].refresh();const next=this.$refs["periodeditor-"+(pi.period+1)];next&&next[0]&&next[0].refresh()},addDay:(date,days)=>(void 0===days&&(days=1),(0,_dateHelper.addDays)(date,days)),subDay:(date,days)=>(void 0===days&&(days=1),(0,_dateHelper.addDays)(date,0-days)),toolboxSwitched(event){this.$emit("toggletoolbox",event)},pagecreated(page){this.value.pages.push(page)},selectedpageChanged(newTabIndex){const page=this.value.pages[newTabIndex];this.$emit("pagechanged",page)},sumLineLayers(idx,page){if(idx<0||0==page.studylines.count)return 0;{let sum=0;for(let i=0;i<idx;i++)sum+=this.countLineLayers(page.studylines[i],page)+1;return sum}},span(line,slot,layer){let span=1;for(const course of line.slots[slot].courses)course.slot==slot&&course.layer==layer&&(span=course.span);return span},onDrop(event,line,slot){debug.info("dropping",event,line,slot);const self=this;if(event.type.component)if(debug.info("Adding new component"),"gradable"==event.type.type){const lineslot=line.slots[slot].courses;let nextlayer=0;for(const itm of lineslot)itm.layer>=nextlayer&&(nextlayer=itm.layer+1);(0,_ajax.call)([{methodname:"local_treestudyplan_add_studyitem",args:{line_id:line.id,slot:slot,layer:nextlayer,type:"course",details:{competency_id:null,conditions:"",course_id:event.data.id,badge_id:null,continuation_id:null}}}])[0].then((response=>{let item=response;lineslot.push(item),self.$emit("input",self.value),this.$nextTick((()=>{this.$refs.timingChecker&&this.$refs.timingChecker.validateCoursePeriod()})),ItemEventBus.$emit("coursechange")})).catch(_notification.default.exception)}else if("filter"==event.type.type){debug.info("Adding new filter compenent");const lineslot=line.slots[slot].filters;let nextlayer=0;for(const itm of lineslot)itm.layer>=nextlayer&&(nextlayer=itm.layer+1);(0,_ajax.call)([{methodname:"local_treestudyplan_add_studyitem",args:{line_id:line.id,slot:slot,type:event.data.type,layer:nextlayer,details:{badge_id:event.data.badge?event.data.badge.id:void 0}}}])[0].then((response=>{let item=response;lineslot.push(item),self.$emit("input",self.value)})).catch(_notification.default.exception)}},checkTypeCourse:type=>"gradable"==type.type&&!(!(0,_settings.settings)("hivizdropslots")||type.item),checkTypeFilter:type=>"filter"==type.type&&!(!(0,_settings.settings)("hivizdropslots")||type.item)},template:'\n <div>\n <t-toolbox v-model="edit.toolbox_shown"\n :activepage="selectedpage"\n :coaching="coaching"\n :studyplanid="value.id"></t-toolbox>\n <div class=\'controlbox t-studyplan-controlbox\'>\n <div class="controlbox-group">\n <b-form-checkbox v-if="!coaching"\n v-model="edit.studyline.editmode" class="sw-studyplan-toolbar" switch\n @change="toolboxSwitched(edit.toolbox_shown && !edit.studyline.editmode); "\n >{{ text.studyline_editmode }}</b-form-checkbox>\n <b-form-checkbox\n v-if="!edit.studyline.editmode" v-model="edit.toolbox_shown" class="sw-studyplan-toolbar" switch\n @change="toolboxSwitched"\n >{{ text.toolbox_toggle}}</b-form-checkbox>\n <drop\n mode=\'copy\'\n class=\'t-item-deletebox text-danger border-danger\'\n @drop=\'deleteStudyItem\'\n :accepts-type="trashbinAccepts"\n ><i class=\'fa fa-trash\'></i>\n </drop>\n </div>\n <div class="controlbox-group" v-if="!coaching">\n <span class=\'control editable\'>\n <t-studyplan-advanced v-model="value" :selectedpage="selectedpage"></t-studyplan-advanced>\n </span>\n <span class=\'control editable\'>\n <t-studyplan-associate\n v-model="value"><i class=\'fa fa-users\'></i> {{text.associations}}</t-studyplan-associate>\n </span>\n <span class=\'control editable\'>\n <t-studyplan-edit v-model="value" @moved="movedStudyplan"\n ><i class=\'fa fa-gear\'></i> {{text.edit}}</t-studyplan-edit>\n </span>\n <span class=\'control deletable\'>\n <a v-if=\'value.pages.length == 0\' href=\'#\' @click.prevent=\'deletePlan(value)\'\n ><i class=\'text-danger fa fa-trash\'></i></a>\n </span>\n </div>\n </div>\n <b-card no-body>\n <b-tabs\n v-model=\'selectedpageindex\'\n @activate-tab=\'selectedpageChanged\'\n content-class="mt-1">\n \x3c!-- New Tab Button (Using tabs-end slot) --\x3e\n <template #tabs-end>\n <t-studyplan-page-edit\n v-if="!coaching"\n :studyplan="value"\n v-model="create.page"\n type="link"\n mode="create"\n @created="pagecreated"\n ><i class=\'fa fa-plus\'></i></t-studyplan-page-edit>\n </template>\n <b-tab\n v-for="(page,pageindex) in value.pages"\n :key="page.id"\n >\n <template #title>\n {{page.shortname}}\n <t-studyplan-page-edit\n v-if="!coaching && (pageindex == selectedpageindex)"\n v-model="value.pages[pageindex]"\n :studyplan="value"\n type="link"\n ></t-studyplan-page-edit>\n </template>\n <div class=\'t-studyplan-content-edit\'\n v-if="edit.studyline.editmode">\n <drop-list\n :items="page.studylines"\n class="t-slot-droplist"\n :accepts-type="\'studyline-\'+page.id"\n @reorder="reorderLines($event,page.studylines)"\n mode="copy"\n row\n >\n <template v-slot:item="{item}">\n <drag\n :key="item.id"\n class=\'t-studyline-drag\'\n :data="item"\n :type="\'studyline-\'+page.id"\n >\n <template v-slot:drag-image>\n <i class="fa fa-arrows text-primary"></i>\n </template>\n <t-studyline-edit\n v-if="!coaching"\n v-model="item"\n @edit=\'editLineStart(item)\'\n @delete=\'deleteLine(page,item)\'\n >\n <div v-if="!slotsempty(item.slots)"> {{ text.editmode_modules_hidden}} </div>\n </t-studyline-edit>\n </drag>\n </template>\n </drop-list>\n </div>\n <div class=\'t-studyplan-content\' v-else>\n \x3c!-- Now paint the headings column --\x3e\n <div class=\'t-studyplan-headings\'>\n <s-studyline-header-heading :identifier=\'Number(page.id)\'></s-studyline-header-heading>\n <template v-if="page.studylines.length > 0">\n <t-studyline-heading v-for="(line,lineindex) in page.studylines"\n :key="line.id"\n @resize="headingresized(lineindex,$event)"\n v-model="page.studylines[lineindex]"\n :layers=\'countLineLayers(line,page)+1\'\n :class=" \'t-studyline\' + ((lineindex%2==0)?\' odd \' :\' even \' )\n + ((lineindex==0)?\' first \':\' \')\n + ((lineindex==page.studylines.length-1)?\' last \':\' \')"\n ></t-studyline-heading>\n </template>\n <t-studyline-heading v-else\n @resize="headingresized(0,$event)"\n\n :layers="1"\n :class="\'odd first last\'"\n ></t-studyline-heading>\n </div>\n \x3c!-- Next, paint all the cells in the scrollable --\x3e\n <div class="t-studyplan-scrollable" >\n <div class="t-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 mode="edit"\n :x-index="index"\n :style="\'grid-area: 1 / \'+ ((2*index)) +\';\'"\n :identifier=\'Number(page.id)\'\n v-if="index > 0"\n v-model="page.perioddesc[index-1]"\n ><t-period-edit\n v-if="!coaching"\n :ref="\'periodeditor-\'+index"\n @edited="periodEdited"\n v-model="page.perioddesc[index-1]"\n :minstart="(index > 1) ? addDay(page.perioddesc[index-2].startdate,2) : null"\n :maxend="(index < page.periods) ? subDay(page.perioddesc[index].enddate,2) : null"\n ></t-period-edit\n ></s-studyline-header-period>\n <div class="s-studyline-header-filter"\n :x-index="index"\n :style="\'grid-area: 1 / \'+ ((2*index)+1) +\';\'"\n ></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)+1"\n ><template v-for="(n,index) in (page.periods+1)"\n ><t-studyline-slot\n v-if="index > 0 && showslot(page,line, index, layeridx, \'gradable\')"\n :style="\'grid-area: \'+ (1+sumLineLayers(lineindex,page)+layernr)\n + \' / \' + (2 * index)\n + \' / \' + (1 + sumLineLayers(lineindex,page)+layernr)\n + \' / \' + ( (2 * index) + (2*span(line,index,layeridx) - 1)) + \';\'"\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 :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 + ((layernr == countLineLayers(line,page)+1)?\' newlyr \':\' \')"\n ></t-studyline-slot\n ><t-studyline-slot\n type=\'filter\'\n :style="\'grid-area: \'+ (1+sumLineLayers(lineindex,page)+layernr) + \' / \' + (2*index+1) +\';\'"\n v-if="showslot(page,line, index, layeridx, \'filter\')"\n v-model="line.slots[index].filters"\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 + ((layernr == countLineLayers(line,page)+1)?\' newlyr \':\' \')"\n ></t-studyline-slot\n ></template\n ></template\n ></template\n ><template v-if="hivizdrop"\n ><template v-for="(line,lineindex) in page.studylines"\n ><template v-for="(n,index) in (page.periods+1)"\n ><drop v-if="index > 0"\n :style="\'grid-area: \'+ (2 + sumLineLayers(lineindex,page))\n + \' / \' + (2 * index)\n + \' / \' + (1 + sumLineLayers(lineindex + 1,page))\n + \' / \' + (2 * index) + \'; overflow: hidden;\'"\n :class="\'t-slot-drop t-slot-linedrop course hiviz\'"\n :accepts-type="checkTypeCourse"\n @drop="onDrop($event,line,index)"\n mode="cut"\n ><span>{{text.drophere}}</span></drop\n ><drop\n :style="\'grid-area: \'+ (2 + sumLineLayers(lineindex,page))\n + \' / \' + ((2 * index) + 1)\n + \' / \' + (1 + sumLineLayers(lineindex + 1,page))\n + \' / \' + ( (2 * index) + 1 ) + \'; overflow: hidden;\'"\n :class="\'t-slot-drop t-slot-linedrop filter hiviz\'"\n :accepts-type="checkTypeFilter"\n @drop="onDrop($event,line,index)"\n mode="cut"\n ><span>{{text.drophere}}</span></drop\n ></template\n ></template\n ></template\n ></div>\n </div>\n </div>\n <div v-if="edit.studyline.editmode" class=\'t-studyline-add ml-2 mt-1\'>\n <a href="#" v-b-modal="\'modal-add-studyline-\'+page.id" @click.prevent="false;"\n ><i class=\'fa fa-plus\'></i>{{ text.studyline_add }}</a>\n </div>\n <b-modal\n :id="\'modal-add-studyline-\'+page.id"\n size="lg"\n :ok-title="text.add"\n ok-variant="primary"\n :title="text.studyline_add"\n @ok="addStudyLine(page,create.studyline)"\n :ok-disabled="Math.min(create.studyline.name.length,create.studyline.shortname.length) == 0"\n >\n <b-container>\n <b-row>\n <b-col cols="3">{{text.studyline_name}}</b-col>\n <b-col>\n <b-form-input v-model="create.studyline.name" :placeholder="text.studyline_name_ph"></b-form-input>\n </b-col>\n </b-row>\n <b-row>\n <b-col cols="3">{{text.studyline_shortname}}</b-col>\n <b-col>\n <b-form-input\n v-model="create.studyline.shortname"\n :placeholder="text.studyline_shortname_ph"></b-form-input>\n </b-col>\n </b-row>\n <b-row>\n <b-col cols="3">{{text.studyline_color}}</b-col>\n <b-col>\n <input type="color" v-model="create.studyline.color" />\n \x3c!-- hsluv-picker v-model="create.studyline.color" horizontal displaysize="175" ></hsluv-picker --\x3e\n </b-col>\n </b-row>\n <b-row>\n <b-col cols="3">{{ text.studyline_enrollable}}</b-col>\n <b-col>\n <b-form-select v-model="create.studyline.enrol.enrollable">\n <b-form-select-option\n v-for="(nr,n) in 4"\n :value="n"\n :key="n"\n >{{text[\'line_enrollable_\'+n]}}</b-form-select-option>\n </b-form-select>\n </b-col>\n </b-row>\n <b-row v-if=\'[2,3].includes(create.studyline.enrol.enrollable)\'>\n <b-col cols="3">{{ text.studyline_enrolroles}}</b-col>\n <b-col>\n <b-form-select\n v-model="create.studyline.enrol.enrolroles"\n :options="availableroles"\n multiple\n value-field="id"\n text-field="name"\n :select-size="6"\n ></b-form-select>\n </b-col>\n </b-row>\n </b-container>\n </b-modal>\n <b-modal\n :id="\'modal-edit-studyline-\'+page.id"\n size="lg"\n ok-variant="primary"\n :title="text.studyline_edit"\n @ok="editLineFinish()"\n :ok-disabled="Math.min(edit.studyline.data.name.length,edit.studyline.data.shortname.length) == 0"\n >\n <b-container>\n <b-row>\n <b-col cols="3">{{ text.studyline_name}}</b-col>\n <b-col>\n <b-form-input\n v-model="edit.studyline.data.name"\n :placeholder="text.studyline_name_ph"></b-form-input>\n </b-col>\n </b-row>\n <b-row>\n <b-col cols="3">{{ text.studyline_shortname}}</b-col>\n <b-col>\n <b-form-input\n v-model="edit.studyline.data.shortname"\n :placeholder="text.studyline_shortname_ph"></b-form-input>\n </b-col>\n </b-row>\n <b-row>\n <b-col cols="3">{{ text.studyline_color}}</b-col>\n <b-col>\n <input type="color" v-model="edit.studyline.data.color" />\n </b-col>\n </b-row>\n <b-row>\n <b-col cols="3">{{ text.studyline_enrollable}}</b-col>\n <b-col>\n <b-form-select v-model="edit.studyline.data.enrol.enrollable">\n <b-form-select-option\n v-for="(nr,n) in 4"\n :value="n"\n >{{text[\'line_enrollable_\'+n]}}</b-form-select-option>\n </b-form-select>\n </b-col>\n </b-row>\n <b-row v-if=\'[2,3].includes(edit.studyline.data.enrol.enrollable)\'>\n <b-col cols="3">{{ text.studyline_enrolroles}}</b-col>\n <b-col>\n <b-form-select\n v-model="edit.studyline.data.enrol.enrolroles"\n :options="availableroles"\n multiple\n value-field="id"\n text-field="name"\n :select-size="6"\n ></b-form-select>\n </b-col>\n </b-row>\n </b-container>\n </b-modal>\n </b-tab>\n </b-tabs>\n </b-card>\n </div>\n '}),Vue.component("t-studyline-heading",{props:{value:{type:Object,default:()=>({})},layers:{type:Number,default:1}},data:()=>({layerHeights:{}}),created(){ItemEventBus.$on("lineHeightChange",this.onLineHeightChange)},computed:{},methods:{onLineHeightChange(lineid){if(this.$refs.main&&lineid==this.value.id){const items=document.querySelectorAll(`.t-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.main.style.height=heightStyle}}},template:'\n <div class="t-studyline t-studyline-heading "\n :data-studyline="value.id" ref="main"\n ><div class="t-studyline-handle" :style="\'background-color: \' + value.color"></div>\n <div class="t-studyline-title">\n <abbr v-b-tooltip.hover.right :title="value.name">{{ value.shortname }}</abbr>\n </div>\n </div>\n '}),Vue.component("t-studyline-edit",{props:{value:{type:Object,default:()=>({})}},data:()=>({}),computed:{deletable(){const slots=this.value.slots;if(Array.isArray(slots)){let count=0;for(let i=0;i<slots.length;i++)Array.isArray(slots[i].courses)&&(count+=slots[i].courses.length),Array.isArray(slots[i].filters)&&(count+=slots[i].filters.length);return 0==count}return!1},editable:()=>!0},methods:{onEdit(){this.$emit("edit",this.value)},onDelete(){this.$emit("delete",this.value)}},template:"\n <div :class=\"'t-studyline '\" >\n <div class=\"t-studyline-handle\" :style=\"'background-color: ' + value.color\"></div>\n <div class=\"t-studyline-title\">\n <div>\n <i class='fa fa-arrows text-primary'></i>\n <abbr v-b-tooltip.hover :title=\"value.name\">{{ value.shortname }}</abbr>\n </div>\n </div>\n <div class='t-studyline-editmode-content'>\n <slot></slot>\n </div>\n <div class='controlbox'>\n <template v-if='editable || deletable'>\n <span class='control editable' v-if='editable'>\n <a href='#' @click.prevent='onEdit'><i class='fa fa-pencil'></i></a>\n </span>\n <span class='control deletable' v-if='deletable'>\n <a v-if='deletable' href='#' @click.prevent='onDelete'><i class='text-danger fa fa-trash'></i></a>\n </span>\n </template>\n </div>\n </div>\n "}),Vue.component("t-studyline-slot",{props:{type:{type:String,default:"gradable"},slotindex:{type:Number,default:""},line:{type:Object,default:()=>null},layer:{type:Number},value:{type:Array,default:()=>[]},plan:{type:Object,default:()=>null},page:{type:Object,default:()=>null},period:{type:Object,default:()=>null}},mounted(){const self=this;"gradable"==self.type&&1==self.slotindex&&(self.resizeListener=new ResizeObserver((()=>{if(self.$refs.main){const size=self.$refs.main.getBoundingClientRect();ItemEventBus.$emit("lineHeightChange",self.line.id,self.layer,size.height)}})).observe(self.$refs.main))},unmounted(){this.resizeListener&&this.resizeListener.disconnect()},computed:{slotkey(){return`${this.type}'-'${this.line.id}-${this.slotindex}-${this.layer}`},itemidx(){for(const ix in this.value){if(this.value[ix].layer==this.layer)return ix}return null},item(){for(const ix in this.value){const itm=this.value[ix];if(itm.layer==this.layer)return itm}return null},listtype(){return this.type},courseHoverDummy(){return{course:this.hover.component}},spanCss(){return this.item&&this.item.span>1?"width: 100%; ":""}},data:()=>({text:strings.courseTiming,plantext:strings.studyplanText,resizeListener:null,hover:{component:null,type:null},datechanger:{coursespan:null,periodspan:null,default:!1,defaultchoice:!1,hidewarn:!1}}),methods:{hivizdrop:()=>(0,_settings.settings)("hivizdropslots"),onDrop(event){this.hover.component=null,this.hover.type=null,debug.info(event);const self=this;if(event.type.item){let item=event.data;item.layer=this.layer,item.slot=this.slotindex,self.value.push(item),self.$emit("input",self.value),self.relocateStudyItem(item).then((()=>{this.$refs.timingChecker&&this.$refs.timingChecker.validateCoursePeriod()})).catch(_notification.default.exception)}else event.type.component&&(debug.info("Adding new component"),"gradable"==event.type.type?(0,_ajax.call)([{methodname:"local_treestudyplan_add_studyitem",args:{line_id:self.line.id,slot:self.slotindex,layer:self.layer,type:"course",details:{competency_id:null,conditions:"",course_id:event.data.id,badge_id:null,continuation_id:null}}}])[0].then((response=>{let item=response;self.relocateStudyItem(item).then((()=>{self.value.push(item),self.$emit("input",self.value),this.$nextTick((()=>{this.$refs.timingChecker&&this.$refs.timingChecker.validateCoursePeriod()})),ItemEventBus.$emit("coursechange")})).catch(_notification.default.exception)})).catch(_notification.default.exception):"filter"==event.type.type&&(debug.info("Adding new filter compenent"),(0,_ajax.call)([{methodname:"local_treestudyplan_add_studyitem",args:{line_id:self.line.id,slot:self.slotindex,type:event.data.type,details:{badge_id:event.data.badge?event.data.badge.id:void 0}}}])[0].then((response=>{let item=response;self.relocateStudyItem(item).then((()=>{item.layer=this.layer,self.value.push(item),self.$emit("input",self.value)})).catch(_notification.default.exception)})).catch(_notification.default.exception)))},onCut(event){const self=this;let id=event.data.id;for(let i=0;i<self.value.length;i++)if(self.value[i].id==id){self.value.splice(i,1),i--;break}this.$emit("input",this.value),ItemEventBus.$emit("coursechange")},relocateStudyItem(item){const iteminfo={id:item.id,layer:this.layer,slot:this.slotindex,line_id:this.line.id};return(0,_ajax.call)([{methodname:"local_treestudyplan_reorder_studyitems",args:{items:[iteminfo]}}])[0].catch(_notification.default.exception)},onDragEnter(event){this.hover.component=event.data,this.hover.type=event.type},onDragLeave(){this.hover.component=null,this.hover.type=null},maxSpan(){return this.page.periods-this.slotindex+1},makeType(item){return{item:!0,component:!1,span:item.span,type:this.type}},checkType(type){return type.type==this.type&&(!((0,_settings.settings)("hivizdropslots")&&!type.item)&&("filter"==type||type.span<=this.maxSpan()))}},template:'\n <div :class=" \'t-studyline-slot \'+type \n + \' t-studyline-slot-\'+slotindex + \' \' \n + ((slotindex==0)?\' t-studyline-firstcolumn \':\' \')\n + \'periodcount-\' + page.periods + \' \'"\n :data-studyline="line.id" ref="main"\n :style=\'spanCss\'\n ><drag v-if="item"\n :key="item.id"\n class="t-slot-item"\n :data="item"\n :type="makeType(item)"\n @cut="onCut"\n ><t-item\n @deleted="onCut"\n v-model="value[itemidx]"\n :plan="plan"\n :line=\'line\'\n :page=\'page\'\n :period=\'period\'\n :maxspan=\'maxSpan()\'\n ></t-item\n ></drag\n ><drop v-else\n :class="\'t-slot-drop \'+type + (layer > 0?\' secondary\':\' primary\') + (hivizdrop()?\' hiviz\':\'\')"\n :accepts-type="checkType"\n @drop="onDrop"\n mode="cut"\n @dragenter="onDragEnter"\n @dragleave="onDragLeave"\n ><template v-if="hover.component">\n <div v-if="hover.type.item"\n class="t-slot-item feedback"\n :key="hover.component.id"\n ><t-item v-model="hover.component" dummy></t-item\n ></div\n ><div v-else-if="hover.type.type == \'gradable\'"\n class="t-slot-item feedback"\n :key="\'course-\'+hover.component.id"\n ><t-item-course v-model="courseHoverDummy"></t-item-course></div\n ><div v-else-if="hover.type.type == \'filter\'"\n class="t-slot-item feedback"\n key="tooldrop"\n ><t-item-junction v-if="hover.component.type == \'junction\'" ></t-item-junction\n ><t-item-start v-else-if="hover.component.type == \'start\'" ></t-item-start\n ><t-item-finish v-else-if="hover.component.type == \'finish\'" ></t-item-finish\n ><t-item-badge v-else-if="hover.component.type == \'badge\'" ></t-item-badge\n ></div\n ><div v-else\n class="t-slot-item feedback"\n :key="hover.type">--{{ hover.type }}--</div\n ></template\n ><span v-else-if="hivizdrop()">{{plantext.drophere}}</span></drop>\n <t-item-timing-checker hidden\n v-if="value && value[itemidx] && value[itemidx].course"\n ref="timingChecker"\n :maxspan="maxSpan()"\n :page="page"\n :line="line"\n :period="period"\n v-model="value[itemidx]"\n ></t-item-timing-checker>\n </div>\n '}),Vue.component("t-item-timing-checker",{props:{value:{type:Object},page:{type:Object},line:{type:Object},period:{type:Object},maxspan:{type:Number},hidden:{type:Boolean,default:!1}},computed:{endperiod(){const endperiodnr=Math.min(this.page.periods,this.period.period+(this.value.span-1));return this.page.perioddesc[endperiodnr-1]},coursePeriodMatches(){const self=this;return!!self.page.timeless||(self.value&&"course"==self.value.type?(self.datechanger.coursespan=(0,_dateHelper.datespaninfo)(self.value.course.startdate,self.value.course.enddate),self.datechanger.periodspan=(0,_dateHelper.datespaninfo)(self.period.startdate,self.endperiod.enddate),self.datechanger.coursespan.first.getTime()==self.datechanger.periodspan.first.getTime()&&self.datechanger.coursespan.last.getTime()==self.datechanger.periodspan.last.getTime()):(debug.warn("Timing thing not properly configured",self.value,self.period,self.maxspan),!1))}},data:()=>({id:Math.floor(Math.random()*Date.now()).toString(16),text:strings.courseTiming,datechanger:{coursespan:null,periodspan:null,globals:{default:!1,defaultchoice:!1,hidewarn:!1}}}),methods:{validateCoursePeriod(){const self=this;self.page.timeless?debug.info("Skipping course timing check because of timeless mode",self.datechanger):(debug.info("Validating course and period"),self.coursePeriodMatches?debug.info("Course timing matches period",self.datechanger):(debug.info("Course timing does not match period timing"),self.value.course.canupdatecourse?self.hidden&&self.datechanger.globals.default?self.datechanger.globals.defaultvalue&&self.changeCoursePeriod():this.$bvModal.show("t-course-timing-matching-"+this.id):self.hidden&&self.datechanger.globals.hidewarn||this.$bvModal.show("t-course-timing-warning-"+this.id)))},changeCoursePeriod(){const self=this;return self.datechanger.globals.default&&(self.datechanger.globals.defaultvalue=!0),(0,_ajax.call)([{methodname:"local_treestudyplan_course_period_timing",args:{period_id:self.period.id,course_id:this.value.course.id,span:this.value.span}}])[0].catch(_notification.default.exception).then((response=>{self.value.course.startdate=response.startdate,self.value.course.enddate=response.enddate,self.value.course.timing=response.timing,self.$emit("input",self.value)}))},checkFilterSlotBusy(slotindex){if(debug.info("checking filter",this.line.slots,slotindex,this.value.layer),this.line.slots[slotindex]){const list=this.line.slots[slotindex].filters;for(const ix in list)if(list[ix].layer==this.value.layer)return debug.info("Busy:",list[ix]),list[ix]}return null},nextFreeFilterLayer(slotindex){const layer=this.value.layer,list=this.line.slots[slotindex].filters,usedLayers=[];for(const ix in list)usedLayers.push(list[ix].layer);let nextlyr=layer+1;for(;usedLayers.includes(nextlyr);)nextlyr++;return nextlyr},checkCourseSlotBusy(slotindex){if(debug.info("checking ",this.line.slots,slotindex,this.value.layer),this.line.slots[slotindex]){const list=this.line.slots[slotindex].courses;for(const ix in list)if(list[ix].layer==this.value.layer)return debug.info("Busy:",list[ix]),list[ix]}return null},nextFreeCourseLayer(slotindex){const layer=this.value.layer,list=this.line.slots[slotindex].courses,usedLayers=[];for(const ix in list)usedLayers.push(list[ix].layer);let nextlyr=layer+1;for(;usedLayers.includes(nextlyr);)nextlyr++;return nextlyr},shiftCollisions(span){const items=[];for(let i=this.value.slot;i<this.value.slot+span;i++){const busyFilter=this.checkFilterSlotBusy(i);if(busyFilter){const nextlyr=this.nextFreeFilterLayer(i);items.push({id:busyFilter.id,layer:nextlyr,line_id:this.line.id,slot:busyFilter.slot}),busyFilter.layer=nextlyr}const busyCourse=this.checkCourseSlotBusy(i);if(busyCourse&&busyCourse.id!=this.value.id){const nextlyr=this.nextFreeCourseLayer(i);items.push({id:busyCourse.id,layer:nextlyr,line_id:this.line.id,slot:busyCourse.slot}),busyCourse.layer=nextlyr}}items.length>0&&(0,_ajax.call)([{methodname:"local_treestudyplan_reorder_studyitems",args:{items:items}}])[0].catch(_notification.default.exception)},changeSpan(span){const self=this;return this.shiftCollisions(span),(0,_ajax.call)([{methodname:"local_treestudyplan_set_studyitem_span",args:{id:self.value.id,span:span}}])[0].catch(_notification.default.exception).then((response=>{self.value.span=response.span,self.$emit("input",self.value),self.$nextTick((()=>{self.validateCoursePeriod()}))}))},formatDuration(dsi){let s="";return 1==dsi.years?s+=`1 ${this.text.year}, `:dsi.years>1&&(s+=`${dsi.years} ${this.text.years}, `),1==dsi.weeks?s+=`1 ${this.text.week}, `:dsi.weeks>1&&(s+=`${dsi.weeks} ${this.text.weeks}, `),1==dsi.days?s+=`1 ${this.text.day}, `:dsi.days>1&&(s+=`${dsi.days} ${this.text.days}, `),s.toLocaleLowerCase()}},template:'\n <div :class="\'t-item-timing-checker\'" :style="hidden?\'display: none \':\'\'">\n <template v-if="!hidden" >\n <span class="mr-1" v-if="coursePeriodMatches">\n <i class="text-success fa fa-calendar-check-o"\n v-b-tooltip.hover.topright :title="text.timing_ok"\n ></i>\n </span>\n <span class="mr-1" v-else>\n <a href=\'#\' @click.prevent="validateCoursePeriod()" class="text-warning"\n v-b-tooltip.hover.bottomleft :title="text.timing_off"\n ><i class="fa fa-calendar-times-o"\n ></i\n ><i class="fa fa-question-circle text-black-50"\n style="font-size: 0.8em; top: -0.3em; position: relative;"\n\n ></i\n ></a>\n </span>\n <span class="ml-1" v-b-tooltip.hover.bottomleft :title="text.periodspan_desc"\n >{{ text.periodspan\n }} <b-form-select v-if="maxspan > 1"\n class=""\n size="sm" @change="changeSpan" v-model="value.span">\n <b-form-select-option v-for="(n,i) in maxspan" :value=\'n\' :key=\'i\'\n >{{ n }}</b-form-select-option>\n </b-form-select\n ><span v-else>{{value.span}}</span> {{\n (value.span == 1)?text.period.toLowerCase():text.periods.toLowerCase()\n }}<i\n class="fa fa-question-circle text-black-50"\n style="font-size: 0.8em; top: -0.3em; position: relative;"\n ></i>\n </span>\n </template>\n <b-modal\n :id="\'t-course-timing-matching-\'+this.id"\n size="lg"\n :title="text.title"\n @ok="changeCoursePeriod"\n :ok-title="text.yes"\n ok-variant="danger"\n :cancel-title="text.no"\n cancel-variant="primary"\n >\n <b-container v-if="datechanger.coursespan && datechanger.periodspan && value && value.course">\n <b-row><b-col cols="12">{{ text.desc }}</b-col></b-row>\n <b-row><b-col cols="12"><div class="generalbox alert alert-warning">{{ text.question }}</div></b-col></b-row>\n <b-row>\n <b-col cols="6">\n <h3> {{ text.course }} </h3>\n <p class="mb-0"><b>{{ value.course.fullname }}</b></p>\n <p class="mb-1"><b>{{ value.course.shortname }}</b></p>\n <p class="mb-1">{{ datechanger.coursespan.formatted.first}} - {{ datechanger.coursespan.formatted.last}}</p>\n <p class="mb-0"><b>{{ text.duration }}</b><br>\n {{ formatDuration(datechanger.coursespan)}}</p>\n </b-col>\n <b-col cols="6">\n <h3> {{ text.period }} </h3>\n <p class="mb-0"><b>{{ period.fullname }}</b><b v-if="value.span > 1"> - {{ endperiod.fullname }}</b></p>\n <p class="mb-1"><b>{{ period.shortname }}</b><b v-if="value.span > 1"> - {{ endperiod.shortname }}</b></p>\n <p class="mb-1">{{ datechanger.periodspan.formatted.first}} - {{ datechanger.periodspan.formatted.last}}</p>\n <p class="mb-0"><b>{{ text.duration }}</b><br>\n {{ formatDuration(datechanger.periodspan)}}</p>\n </b-col>\n </b-row>\n <b-row v-if=\'hidden\' class="pt-2"><b-col cols="12">\n <b-form-checkbox type="checkbox" v-model="datechanger.globals.default">{{ text.rememberchoice }}</b-form-checkbox>\n </b-col></b-row>\n </b-container>\n </b-modal>\n <b-modal\n :id="\'t-course-timing-warning-\'+this.id"\n size="lg"\n ok-variant="primary"\n :title="text.title"\n :ok-title="text.yes"\n ok-only\n >\n <b-container v-if="datechanger.coursespan && datechanger.periodspan && value && value.course">\n <b-row><b-col cols="12">{{ text.desc }}</b-col></b-row>\n <b-row><b-col cols="12"><div class="generalbox alert alert-warning">{{ text.warning }}</div></b-col></b-row>\n <b-row>\n <b-col cols="6">\n <h3> {{ text.course }} </h3>\n <p class="mb-0"><b>{{ value.course.fullname }}</b></p>\n <p class="mb-1"><b>{{ value.course.shortname }}</b></p>\n <p class="mb-1">{{ datechanger.coursespan.formatted.first}} - {{ datechanger.coursespan.formatted.last}}</p>\n <p class="mb-0"><b>{{ text.duration }}</b><br>\n {{ formatDuration(datechanger.coursespan)}}</p>\n </b-col>\n <b-col cols=>"6">\n <h3> {{ text.period }} </h3>\n <p class="mb-0"><b>{{ period.fullname }}</b><b v-if="value.span > 1"> - {{ endperiod.fullname }}</b></p>\n <p class="mb-1"><b>{{ period.shortname }}</b><b v-if="value.span > 1"> - {{ endperiod.shortname }}</b></p>\n <p class="mb-1">{{ datechanger.periodspan.formatted.first}} - {{ datechanger.periodspan.formatted.last}}</p>\n <p class="mb-0"><b>{{ text.duration }}</b><br>\n {{ formatDuration(datechanger.periodspan)}}</p>\n </b-col>\n </b-row>\n <b-row v-if=\'hidden\' class="pt-2"><b-col cols="12">\n <b-form-checkbox type="checkbox" v-model="datechanger.globals.hidewarn">{{ text.hidewarning }}</b-form-checkbox>\n </b-col></b-row>\n </b-container>\n </b-modal>\n </div>\n '}),Vue.component("t-item",{props:{value:{type:Object,default:()=>null},dummy:{type:Boolean,default:()=>!1},plan:{type:Object,default:()=>null},line:{type:Object,default:()=>null},page:{type:Object,default:()=>null},period:{type:Object,default:()=>null},maxspan:{type:Number,default:()=>0}},data:()=>({dragLine:null,dragEventListener:null,deleteMode:!1,conditionOptions:stringKeys.conditions,text:strings.itemText,showContext:!1,lines:[]}),methods:{dragStart(event){this.deleteMode=!1;let start=document.getElementById("studyitem-"+this.value.id),dragelement=document.getElementById("t-item-cdrag-"+this.value.id);dragelement.style.position="fixed",dragelement.style.left=event.position.x+"px",dragelement.style.top=event.position.y+"px",this.dragLine=new _simpleline.SimpleLine(start,dragelement,{color:"#777",gravity:{start:1.3,end:1.3}}),document.addEventListener("mousemove",this.onMouseMove)},dragEnd(){null!==this.dragLine&&this.dragLine.remove();let dragelement=document.getElementById("t-item-cdrag-"+this.value.id);dragelement.style.removeProperty("left"),dragelement.style.removeProperty("top"),dragelement.style.removeProperty("position"),document.removeEventListener("mousemove",this.onMouseMove)},onMouseMove:function(event){let dragelement=document.getElementById("t-item-cdrag-"+this.value.id);dragelement.style.position="fixed",dragelement.style.left=event.clientX+"px",dragelement.style.top=event.clientY+"px"},onDrop(event){let fromid=event.data.id,toid=this.value.id;this.redrawLines(),(0,_ajax.call)([{methodname:"local_treestudyplan_connect_studyitems",args:{from_id:fromid,to_id:toid}}])[0].then((response=>{let conn={id:response.id,from_id:response.from_id,to_id:response.to_id};ItemEventBus.$emit("createdConnection",conn),this.value.connections.in.push(conn)})).catch(_notification.default.exception)},redrawLine(conn){const 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:"var(--success)",gravity:{start:1.3,end:1.3}}))},deleteLine(conn){const self=this;(0,_ajax.call)([{methodname:"local_treestudyplan_disconnect_studyitems",args:{from_id:conn.from_id,to_id:conn.to_id}}])[0].then((response=>{if(response.success){this.removeLine(conn),ItemEventBus.$emit("connectionDisconnected",conn);let index=self.value.connections.out.indexOf(conn);self.value.connections.out.splice(index,1)}})).catch(_notification.default.exception)},highlight(conn){this.lines[conn.to_id]&&this.lines[conn.to_id].setConfig({color:"var(--danger)"})},normalize(conn){this.lines[conn.to_id]&&this.lines[conn.to_id].setConfig({color:"var(--success)"})},updateItem(){(0,_ajax.call)([{methodname:"local_treestudyplan_edit_studyitem",args:{id:this.value.id,conditions:this.value.conditions,continuation_id:this.value.continuation_id}}])[0].catch(_notification.default.exception)},doShowContext(event){this.hasContext&&(this.showContext=!0,event.preventDefault())},redrawLines(){if(this.value.connections&&this.value.connections.out)for(let i in this.value.connections.out){let conn=this.value.connections.out[i];this.redrawLine(conn)}},onCreatedConnection(conn){conn.from_id==this.value.id&&(this.value.connections.out.push(conn),this.redrawLine(conn))},onRemovedConnection(conn){if(this.value.connections&&this.value.connections.out)for(let i in this.value.connections.in){let cin=this.value.connections.in[i];conn.id==cin.id&&self.value.connections.out.splice(i,1)}},onRePositioned(){this.redrawLines()},onDisPositioned(reid){if(this.value.connections&&this.value.connections.out)for(let i in this.value.connections.out){let conn=this.value.connections.out[i];conn.to_id==reid&&this.removeLine(conn)}},onItemDeleted(itemid){const self=this;if(this.value.connections&&this.value.connections.out)for(const i in this.value.connections.out){let conn=this.value.connections.out[i];conn.to_id==itemid&&(self.removeLine(conn),self.value.connections.out.splice(i,1))}if(this.value.connections&&this.value.connections.in)for(const i in this.value.connections.in){this.value.connections.in[i].from_id==itemid&&self.value.connections.out.splice(i,1)}},onRedrawLines(){this.redrawLines()},removeLine(conn){this.lines[conn.to_id]&&(this.lines[conn.to_id].remove(),delete this.lines[conn.to_id])},deleteItem(){const self=this,msgparams={item:this.text["type_"+this.value.type].toLocaleLowerCase(),name:"course"==this.value.type?this.value.course.displayname:"",line:this.line?this.line.name:"",period:this.period?this.period.fullname:this.plan.name};this.$bvModal.msgBoxConfirm((0,_stringHelper.strformat)(this.text.item_delete_message,msgparams),{okVariant:"danger",okTitle:this.text.ok,cancelTitle:this.text.cancel}).then((value=>{value&&(0,_ajax.call)([{methodname:"local_treestudyplan_delete_studyitem",args:{id:self.value.id}}])[0].then((response=>{1==response.success&&self.$emit("deleted",{data:self.value})})).catch(_notification.default.exception)})).catch((err=>{debug.console.error(err)}))}},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(){this.dummy||(ItemEventBus.$on("createdConnection",this.onCreatedConnection),ItemEventBus.$on("removedConnection",this.onRemovedConnection),ItemEventBus.$on("rePositioned",this.onRePositioned),ItemEventBus.$on("disPositioned",this.onDisPositioned),ItemEventBus.$on("itemDeleted",this.onItemDeleted),ItemEventBus.$on("redrawLines",this.onRedrawLines))},mounted(){this.dummy||(this.redrawLines(),setTimeout((()=>{ItemEventBus.$emit("rePositioned",this.value.id)}),10))},beforeDestroy(){if(!this.dummy){for(let i in this.value.connections.out){let conn=this.value.connections.out[i];this.removeLine(conn)}ItemEventBus.$emit("disPositioned",this.value.id),ItemEventBus.$off("createdConnection",this.onCreatedConnection),ItemEventBus.$off("removedConnection",this.onRemovedConnection),ItemEventBus.$off("rePositioned",this.onRePositioned),ItemEventBus.$off("disPositioned",this.onDisPositioned),ItemEventBus.$off("itemDeleted",this.onItemDeleted),ItemEventBus.$off("redrawLines",this.onRedrawLines)}},updated(){this.dummy||this.redrawLines()},template:'\n <div class="t-item-base" :id="\'studyitem-\'+value.id">\n <t-item-course\n v-if="value.type == \'course\'"\n @deleterq="deleteItem"\n v-model="value"\n :plan=\'plan\'\n :line=\'line\'\n :page=\'page\'\n :period=\'period\'\n :maxspan=\'maxspan\'\n ></t-item-course>\n <t-item-junction\n v-if="value.type == \'junction\'"\n @deleterq="deleteItem"\n v-model="value"\n ></t-item-junction>\n <t-item-start\n v-if="value.type == \'start\'"\n @deleterq="deleteItem"\n v-model="value"\n ></t-item-start>\n <t-item-finish\n v-if="value.type == \'finish\'"\n @deleterq="deleteItem"\n v-model="value"\n ></t-item-finish>\n <t-item-badge\n v-if="value.type == \'badge\'"\n @deleterq="deleteItem"\n v-model="value"\n ></t-item-badge>\n <t-item-invalid\n v-if="value.type == \'invalid\'"\n @deleterq="deleteItem"\n v-model="value"\n ></t-item-invalid>\n <drop v-if=\'!dummy && hasConnectionsIn\' accepts-type="linestart"\n :id="\'t-item-cend-\'+value.id"\n class="t-item-connector-end"\n mode="copy"\n @drop="onDrop"\n ><svg width=\'5px\' height=\'10px\'><rect ry="1px" rx="1px" y="0px" x="0px" height="10px" width="5px"/></svg></drop>\n <drag v-if=\'!dummy && hasConnectionsOut\' type="linestart"\n :id="\'t-item-cstart-\'+value.id"\n :class="\'t-item-connector-start \' + ((deleteMode&&value.connections.out.length)?\'deleteMode\':\'\')"\n :data="value"\n @dragstart="dragStart"\n @dragend="dragEnd"\n @click.prevent="deleteMode = (value.connections.out.length)?(!deleteMode):false"\n >\n <svg width=\'5px\' height=\'10px\'><rect ry="1px" rx="1px" y="0px" x="0px" height="10px" width="5px"/></svg>\n <template v-slot:drag-image="{data}"> <i :id="\'t-item-cdrag-\'+value.id" class="fa"></i>\n </template>\n </drag>\n <div class="deletebox" v-if="deleteMode && value.connections.out.length > 0"\n >\n <a v-for="conn in value.connections.out"\n @click.prevent="deleteLine(conn)"\n @mouseenter="highlight(conn)"\n @mouseleave="normalize(conn)"\n class="t-connection-delete text-danger"\n :title="conn.id">\n <i class="fa fa-trash"></i>\n </a>\n </div>\n <a v-if="hasContext" class="t-item-config"\n v-b-modal="\'t-item-config-\'+value.id" href="#" @click.prevent=""><i class="fa fa-gear"></i></a>\n <b-modal no-body class=""\n :id="\'t-item-config-\'+value.id"\n :title="text[\'item_configuration\']"\n scrollable\n ok-only\n class="t-item-contextview b-modal-justify-footer-between"\n >\n <b-form-group\n v-if="value.type != \'start\'"\n :label="text.select_conditions"\n >\n <b-form-select size="sm"\n @input="updateItem"\n v-model="value.conditions"\n :options="conditionOptions"\n ></b-form-select>\n </b-form-group>\n\n <template #modal-footer="{ ok, cancel, hide }" >\n <a href=\'#\' @click.prevent=\'deleteItem()\' class="text-danger"\n ><i class="fa fa-trash"></i>\n {{ text.delete }}\n </a>\n <b-button size="sm" variant="primary" @click.prevent="ok()">\n {{ text.ok }}\n </b-button>\n </template>\n\n </b-modal>\n </div>\n '}),Vue.component("t-item-invalid",{props:{value:{type:Object,default:()=>null}},data:()=>({text:strings.invalid}),methods:{},template:'\n <div class="t-item-invalid">\n <b-card no-body >\n <b-row no-gutters>\n <b-col md="1">\n <span class="t-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 <a href=\'#\' @click.prevent=\'$emit("deleterq")\' class="text-danger"\n ><i class="fa fa-trash"></i></a>\n </b-card-body>\n </b-col>\n </b-row>\n </b-card>\n </div>\n '}),Vue.component("t-item-course",{props:{value:{type:Object,default:()=>null},plan:{type:Object,default:()=>null},line:{type:Object,default:()=>null},page:{type:Object,default:()=>null},period:{type:Object,default:()=>null},maxspan:{type:Number,default:()=>0}},data:()=>({conditionOptions:stringKeys.conditions,text:strings.itemCourseText}),computed:{useItemConditions(){return!(!this.plan||!this.plan.aggregation_info||void 0===this.plan.aggregation_info.useItemConditions)&&this.plan.aggregation_info.useItemConditions},configurationState(){return this.hasGrades()||this.hasCompletions()||this.hasCompetencies()?"t-configured-ok":"t-configured-alert"},configurationIcon(){return this.hasGrades()||this.hasCompletions()||this.hasCompetencies()?"check":"exclamation-circle"},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:{hasGrades(){if(this.value.course.grades&&this.value.course.grades.length>0)for(const g of this.value.course.grades)if(g.selected)return!0;return!1},hasCompletions(){if(this.value.course.completion&&this.value.course.completion.conditions)for(const cgroup of this.value.course.completion.conditions)if(cgroup.items&&cgroup.items.length>0)return!0;return!1},hasCompetencies(){return!(!this.value.course.competency||!this.value.course.competency.competencies)&&this.value.course.competency.competencies.length>0},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)},updateConditions(){(0,_ajax.call)([{methodname:"local_treestudyplan_edit_studyitem",args:{id:this.value.id,conditions:this.value.conditions}}])[0].catch(_notification.default.exception)}},template:'\n <div class="t-item-course card">\n <div class=\'t-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="\'h-100 t-timing-indicator timing-\'+value.course.timing"\n ></div>\n <div class="t-item-course-title card-body h-100">\n <fittext maxsize="12pt" minsize="9pt">\n <a v-b-modal="\'t-item-course-config-\'+value.id"\n :id="\'t-item-course-details-\'+value.id"\n :href="wwwroot+\'/course/view.php?id=\'+value.course.id"\n @click.prevent.stop="">{{ value.course.displayname }}</a>\n </fittext>\n </div>\n <div class="h-100 t-item-course-indicator ">\n <a class="t-item-course-config"\n v-b-modal="\'t-item-course-config-\'+value.id"\n href="#" @click.prevent=""\n ><i :class="\'fa fa-\'+configurationIcon+\' \' + configurationState"></i></a>\n </div>\n </div>\n <b-modal class=""\n :id="\'t-item-course-config-\'+value.id"\n :title="value.course.displayname + \' - \' + value.course.fullname"\n ok-only\n size="lg"\n scrollable\n class="b-modal-justify-footer-between"\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>\n <a v-if=\'!!value.course.completion\'\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(" / ")}} / {{value.course.shortname}}\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 <t-item-timing-checker\n class="mt-1"\n :maxspan="maxspan"\n :page="page"\n :line="line"\n :period="period"\n v-model="value"\n ></t-item-timing-checker>\n </div>\n </template>\n\n <s-course-extrafields\n v-if="value.course.extrafields"\n v-model="value.course.extrafields"\n position="above"\n ></s-course-extrafields>\n <t-item-course-grades\n v-if=\'!!value.course.grades && value.course.grades.length > 0\'\n v-model=\'value\' :plan="plan"\n ></t-item-course-grades>\n <t-item-course-completion\n v-if=\'!!value.course.completion\'\n v-model=\'value.course.completion\'\n :course=\'value.course\'\n ></t-item-course-completion>\n <t-item-course-competency\n v-if=\'!!value.course.competency\'\n v-model=\'value.course.competency\'\n :item=\'value\'\n ></t-item-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 <template #modal-footer="{ ok, cancel, hide }" >\n <a href=\'#\' @click.prevent=\'$emit("deleterq")\' class="text-danger"\n ><i class="fa fa-trash"></i>\n {{ text.delete }}\n </a>\n <b-button size="sm" variant="primary" @click.prevent="ok()">\n {{ text.ok }}\n </b-button>\n </template>\n </b-modal>\n </div>\n '}),Vue.component("t-item-course-grades",{props:{value:{type:Object,default:()=>null},plan:{type:Object,default:()=>null}},data:()=>({conditionOptions:stringKeys.conditions,text:strings.itemCourseText}),computed:{useRequiredGrades(){return!(!this.plan||!this.plan.aggregation_info||void 0===this.plan.aggregation_info.useRequiredGrades)&&this.plan.aggregation_info.useRequiredGrades},selectedgrades(){let list=[];for(let ix in this.value.course.grades){let g=this.value.course.grades[ix];g.selected&&list.push(g)}return list}},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 <div>\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>\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><span v-html="g.name"></span></s-edit-mod>\n </li>\n </ul>\n </b-form-group>\n </div>\n '}),Vue.component("t-item-course-completion",{props:{value:{type:Object,default:()=>({})},guestmode:{type:Boolean,default:!1},course:{type:Object,default:()=>({})}},data:()=>({text:strings.completion}),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(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"},template:"\n <table class=\"r-item-course-grade-details\">\n <tr v-if=\"hasCompletions\">\n <td colspan='2'><span v-if=\"value.aggregation == 'all'\">{{ text.aggregation_overall_all}}</span\n ><span v-else>{{ text.aggregation_overall_any}}</span></td>\n </tr>\n <tr v-else>\n <td colspan='2'>{{text.completion_not_configured}}!\n <br/><a :href=\"wwwroot+'/course/completion.php?id='+course.id\" target='_blank'>{{text.configure_completion}}</a>\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 {{cgroup.title}}</th>\n </tr>\n <tr v-for='ci in cgroup.items'>\n <td><span v-html='ci.details.criteria'></span>\n </td>\n <td v-if=\"ci.details.requirement\" class=\"font-italic\">\n {{ci.details.requirement}}\n </td>\n </tr>\n </template>\n </table>\n "}),Vue.component("t-item-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:{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?`/admin/tool/lp/competencies.php?competencyid=${p.id}`:`/admin/tool/lp/competencies.php?competencyframeworkid=${p.id}&pagecontextid=${p.contextid}`,s+=`<a href="${url}">${p.title}</a>`}return s},requiredChanged(newValue,c){(0,_ajax.call)([{methodname:"local_treestudyplan_require_competency",args:{competency_id:c.id,item_id:this.item.id,required:newValue}}])[0].catch(_notification.default.exception)}},template:'\n <table class="t-item-course-competency-list">\n <tr v-if="value.competencies.length == 0">\n <td colspan=\'2\'>{{text.competency_not_configured}}\n <br><a :href="wwwroot+\'/admin/tool/lp/coursecompetencies.php?courseid=\'+item.course.id" target=\'_blank\'>{{text.configure_competency}}</a>\n </td>\n </tr>\n <template v-else>\n <tr class=\'t-item-course-competency-headers\'>\n <th>{{text.heading}}</th>\n <th></th>\n <th>{{text.required}}</th>\n </tr>\n <tr v-for=\'c in value.competencies\'>\n <td :colspan="(c.details)?1:2"><a href=\'#\' v-b-modal="\'modal-competency-id-\'+c.id"><span v-html=\'c.title\'></span></a></td>\n <td class=\'details\' v-if="c.details">\n <a href=\'#\' v-b-modal="\'modal-competency-id-\'+c.id"><span v-html=\'c.details\'></span></a>\n </td>\n <td>\n <b-form-checkbox inline\n @change="requiredChanged($event,c)"\n v-model="c.required"\n >{{ text.required }}</b-form-checkbox>\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=\'t-item-course-competency-list\'>\n <tr class=\'t-item-course-competency-headers\'>\n <th>{{text.heading}}</th>\n <th></th>\n <th>{{text.required}}</th>\n </tr>\n <tr v-for="cc in c.children">\n <td :colspan="(c.details)?1:2" ><span v-html=\'cc.title\'></span></td>\n <td class=\'details\' v-if="cc.details"><span v-html=\'cc.details\'></span></td>\n <td><span class="text-info">{{ cc.points }} {{ text.points }}</span></td>\n <td><span class="text-danger" v-if=\'cc.required\'>{{ text.required }}</span></td>\n </tr>\n </table>\n </template>\n </b-modal>\n </tr>\n </template>\n </table>\n '}),Vue.component("t-item-junction",{props:{value:{type:Object,default:()=>({})}},data:()=>({conditionOptions:stringKeys.conditions}),methods:{},template:"\n <div class='t-item-junction t-item-filter'>\n <i class=\"fa fa-check-circle\"></i>\n </div>\n "}),Vue.component("t-item-finish",{props:{value:{type:Object,default:()=>({})}},data:()=>({}),methods:{},template:"\n <div class='t-item-finish t-item-filter'>\n <i class=\"fa fa-stop-circle\"></i>\n </div>\n "}),Vue.component("t-item-start",{props:{value:{type:Object,default:()=>({})}},data:()=>({}),methods:{},template:"\n <div class='t-item-start t-item-filter'>\n <i class=\"fa fa-play-circle\"></i>\n </div>\n "}),Vue.component("t-item-badge",{props:{value:{type:Object,default:()=>({badge:{}})}},data:()=>({txt:strings,text:strings.itemText}),methods:{},template:'\n <div class=\'t-item-badge t-item-filter\' v-b-tooltip.hover :title="value.badge.name">\n <svg class="t-badge-backdrop " width=\'50px\' height=\'50px\' viewBox="0 0 100 100">\n <title>{{value.badge.name}}</title>\n <circle cx="50" cy="50" r="46"\n style="stroke: currentcolor; stroke-width: 4; fill: currentcolor; fill-opacity: 0.5;"/>\n <image class="badge-image" clip-path="circle() fill-box"\n :href="value.badge.imageurl" x="12" y="12" width="76" height="76"/>\n </svg>\n <a class="t-item-config badge"\n v-b-modal="\'t-item-badge-details-\'+value.id" href="#" @click.prevent=""><i class="fa fa-gear"></i></a>\n <b-modal class=""\n :id="\'t-item-badge-details-\'+value.id"\n :title="value.badge.name"\n size="lg"\n ok-only\n centered\n scrollable\n class="b-modal-justify-footer-between"\n >\n <template #modal-header>\n <div>\n <h1><i class="fa fa-certificate"></i>\n <a :href="value.badge.infolink" target="_blank"\n >{{ value.badge.name }}</a\n ></h1>\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 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><strong><i class="fa fa-link"></i>\n <a :href="value.badge.infolink">{{ txt.badge.badgeinfo }}</a></strong></p>\n </b-col></b-row>\n </b-container>\n <template #modal-footer="{ ok, cancel, hide }" >\n <a href=\'#\' @click.prevent=\'$emit("deleterq")\' class="text-danger"\n ><i class="fa fa-trash"></i>\n {{ text.delete }}\n </a>\n <b-button size="sm" variant="primary" @click.prevent="ok()">\n {{ text.ok }}\n </b-button>\n </template>\n </b-modal>\n\n </div>\n '}),Vue.component("t-coursecat-list",{props:{value:{type:Array,default:()=>({})}},data:()=>({}),methods:{},template:'\n <ul class="t-coursecat-list">\n <t-coursecat-list-item\n v-for="coursecat,idx in value"\n v-model="value[idx]"\n :key="coursecat.id"></t-coursecat-list-item>\n </ul>\n '}),Vue.component("t-coursecat-list-item",{props:{value:{type:Object,default:()=>({})}},data:()=>({loading:!1}),computed:{showSpinner(){return this.canLoadMore()},hasDetails(){return this.value.haschildren||this.value.hascourses}},methods:{canLoadMore(){return this.value.haschildren&&!this.value.children||this.value.hascourses&&!this.value.courses},onShowDetails(){const self=this;this.canLoadMore()&&(0,_ajax.call)([{methodname:"local_treestudyplan_get_category",args:{id:this.value.id}}])[0].then((response=>{self.$emit("input",response)})).catch(_notification.default.exception)}},template:'\n <li class="t-coursecat-list-item">\n <span v-if="hasDetails" v-b-toggle="\'coursecat-\'+value.id">\n <i class="when-closed fa fa-caret-right t-caret"></i>\n <i class="when-open fa fa-caret-down t-caret"></i>\n <span class="t-coursecat-heading">\n <i class="t-coursecat-list-item fa fa-tasks"></i>\n {{ value.category.name }}\n </span>\n </span>\n <span v-else>\n <i class="when-closed fa t-caret" style="visibility: hidden"></i>\n <span class="t-coursecat-heading">\n <i class="t-coursecat-list-item fa fa-tasks"></i>\n {{ value.category.name }}\n </span>\n </span>\n <b-collapse v-if="hasDetails" :id="\'coursecat-\'+value.id"\n @show="onShowDetails" :visible="!!(value.children) || !!(value.courses)">\n <b-spinner class="ml-4" v-if="showSpinner" small variant="primary"></b-spinner>\n <t-coursecat-list v-if="value.children" v-model="value.children"></t-coursecat-list>\n <t-course-list v-if="value.courses" v-model="value.courses"></t-course-list>\n </b-collapse>\n </li>\n '}),Vue.component("t-course-list",{props:{value:{type:Array,default:()=>({})}},data:()=>({}),methods:{makeType:()=>({item:!1,component:!0,span:1,type:"gradable"})},template:'\n <ul class="t-course-list">\n <li class="t-course-list-item" v-for="course in value" :key="course.id">\n <span class=\'t-course-heading\'>\n <drag\n class="draggable-course"\n :data="course"\n :type="makeType()"\n @cut=""\n >\n <i class="t-course-list-item fa fa-book"></i> {{ course.shortname }} - {{ course.fullname }}\n </drag>\n </span>\n </li>\n </ul>\n '}),Vue.component("t-toolbox",{props:{value:{type:Boolean,default:!0},activepage:{type:Object,default:()=>null},coaching:{type:Boolean,default:!1},studyplanid:{type:Number,default:0}},data:()=>({toolboxright:!(0,_settings.settings)("toolboxleft"),toolboxcoursesonly:(0,_settings.settings)("toolboxcoursesonly"),enablebadges:(0,_settings.settings)("enablebadges"),allowcoursebadges:(0,_settings.settings)("badges_allowcoursebadges"),text:strings.toolbox,relatedbadges:[],systembadges:[],courses:[],filters:{courses:"",systembadges:"",relatedbadges:""},loadingcourses:!1,loadingcategories:[],badgelistshown:{relatedbadges:!0,systembadges:!1}}),watch:{activepage(){this.filterRelatedbadges()}},mounted(){const self=this;this.initialize(),this.$root.$on("bv::collapse::state",((collapseId,isJustShown)=>{self.badgelistshown[collapseId]=!!isJustShown}))},computed:{filterComponentType:()=>({item:!1,component:!0,span:1,type:"filter"}),filteredCourses(){const self=this;return self.filters.courses?self.filterCategories(self.courses):self.courses}},methods:{hivizdrop:()=>(0,_settings.settings)("hivizdropslots"),filterCategories(catlist){const self=this,list=[],search=new RegExp(`.*?${self.filters.courses}.*?`,"ig");for(const cat of catlist){const clone=Object.assign({},cat);if(clone.courses=[],cat.courses)for(const course of cat.courses)(search.test(course.shortname)||search.test(course.fullname))&&clone.courses.push(course);else cat.hascourses&&!self.loadingcategories.includes(cat.id)&&(self.loadingcategories.push(cat.id),debug.info(`Loading from category ${cat.category.name}`,cat),(0,_ajax.call)([{methodname:"local_treestudyplan_get_category",args:{id:cat.id}}])[0].then((response=>{self.$set(cat,"children",response.children),self.$set(cat,"courses",response.courses),self.loadingcategories.splice(self.loadingcategories.indexOf(cat.id),1)})).catch(_notification.default.exception));cat.children?clone.children=self.filterCategories(cat.children):cat.haschildren&&!self.loadingcategories.includes(cat.id)&&(self.loadingcategories.push(cat.id),debug.info(`Loading from category ${cat.category.name}`,cat),(0,_ajax.call)([{methodname:"local_treestudyplan_get_category",args:{id:cat.id}}])[0].then((response=>{self.$set(cat,"children",response.children),self.loadingcategories.splice(self.loadingcategories.indexOf(cat.id),1)})).catch(_notification.default.exception)),(clone.children&&clone.children.length||clone.courses.length)&&list.push(clone)}return list},initialize(){const self=this;self.loadingcourses=!0,debug.info("Toolbox Loading courses and categories"),(0,_ajax.call)([{methodname:"local_treestudyplan_map_categories",args:{studyplan_id:self.studyplanid}}])[0].then((response=>{debug.info("Toolbox got courses and categories",response),self.courses=response,self.loadingcourses=!1})).catch(_notification.default.exception),!this.toolboxcoursesonly&&this.enablebadges&&(this.filterSystembadges(),this.allowcoursebadges&&this.filterRelatedbadges())},filterSystembadges(){const self=this;(0,_ajax.call)([{methodname:"local_treestudyplan_search_badges",args:{search:this.filters.systembadges||""}}])[0].then((response=>{self.systembadges=response})).catch(_notification.default.exception)},filterRelatedbadges(){const self=this;this.activepage&&(0,_ajax.call)([{methodname:"local_treestudyplan_search_related_badges",args:{page_id:this.activepage.id,search:this.filters.relatedbadges||""}}])[0].then((response=>{self.relatedbadges=response})).catch(_notification.default.exception)},resetSystembadges(){this.filters.systembadges="",this.filterSystembadges()},resetRelatedbadges(){this.filters.relatedbadges="",this.filterRelatedbadges()}},template:'\n <div class="t-toolbox">\n <p-sidebar\n class="t-toolbox-sidebar"\n :right=\'toolboxright\'\n shadow\n v-model="value"\n offsetRef="#page"\n >\n <div class="pt-3 pl-3 pr-3 border-bottom-1 border-primary"><h3>{{text.toolbox}}</h3></div>\n <div class=\'t-toolbox-preface\'>\n <b-form-checkbox v-model="toolboxright" switch>{{text.toolbarRight}}</b-form-checkbox>\n </div>\n <b-tabs content-class=\'mt-3\' class="t-toolbox-tabs">\n <b-tab :title="text.courses" class="t-toolbox-tab">\n <div v-if="loadingcourses"\n ><div class="spinner-border text-primary" role="status"></div\n ></div>\n <div v-else class="ml-2 t-toolbox-courses">\n <div class="flex-grow-0">\n <input v-model="filters.courses" :placeholder="text.filter"></input>\n <a @click="filters.courses = \'\'" v-if="filters.courses" href=\'#\'\n ><i class=\'fa fa-times\'></i></a\n ><b-spinner small v-if="loadingcategories.length > 0" variant="primary"></b-spinner>\n </div>\n <div class="t-toolbox-courselist">\n <t-coursecat-list v-model="filteredCourses"></t-coursecat-list>\n </div>\n </div>\n </b-tab>\n <b-tab :title="text.flow" v-if="!toolboxcoursesonly" class="t-toolbox-tab">\n <ul class="t-flow">\n <li><drag\n :type="filterComponentType"\n :data="{type: \'junction\'}"\n @cut=""\n ><t-item-junction></t-item-junction>{{ text.toolJunction }}\n <template v-slot:drag-image="{data}"><t-item-junction></t-item-junction></template>\n </drag></li>\n <li><drag\n :type="filterComponentType"\n :data="{type: \'finish\'}"\n @cut=""\n ><t-item-finish></t-item-finish>{{ text.toolFinish }}\n <template v-slot:drag-image="{data}"><t-item-finish></t-item-finish></template>\n </drag></li>\n <li><drag\n :type="filterComponentType"\n :data="{type: \'start\'}"\n @cut=""\n ><t-item-start></t-item-start>{{ text.toolStart }}\n <template v-slot:drag-image="{data}"><t-item-start></t-item-start></template>\n </drag></li>\n </ul>\n </b-tab>\n <b-tab :title="text.badges" v-if="!toolboxcoursesonly && enablebadges" class="t-toolbox-tab">\n\n <b-tabs content-class=\'mt-2 ml-2\' class="t-toolbox-badges">\n <b-tab :title="text.relatedbadges" v-if="allowcoursebadges">\n <div class="t-toolbox-badges-filter">\n <input v-model="filters.relatedbadges" @input="filterRelatedbadges" :placeholder="text.filter"></input>\n <a @click="resetRelatedbadges" v-if="filters.relatedbadges" href=\'#\'\n ><i class=\'fa fa-times\'></i></a>\n </div>\n <div class="t-toolbox-badges-list">\n <ul class="t-badges">\n <li v-for="b in relatedbadges"><drag\n class="t-badge-drag"\n :type="filterComponentType"\n :data="{type: \'badge\', badge: b}"\n @cut=""\n ><img :class="(!b.active)?\'disabled\':\'\'" :src="b.imageurl" :alt="b.name">\n <span :class="(!b.active)?\'disabled\':\'\'">{{b.name}}</span>\n <template v-slot:drag-image="{data}"\n ><img :class="(!b.active)?\'disabled\':\'\'" :src="b.imageurl" :alt="b.name"\n ></template>\n </drag></li>\n </ul>\n </div>\n </b-tab>\n <b-tab :title="text.sitebadges">\n <div class="t-toolbox-badges-filter">\n <input v-model="filters.systembadges" @input="filterSystembadges" :placeholder="text.filter"></input>\n <a @click="resetSystembadges" v-if="filters.systembadges" href=\'#\'\n ><i class=\'fa fa-times\'></i></a>\n </div>\n <div class="t-toolbox-badges-list">\n <ul class="t-badges">\n <li v-for="b in systembadges"><drag\n class="t-badge-drag"\n :type="filterComponentType"\n :data="{type: \'badge\', badge: b}"\n @cut=""\n ><img :class="(!b.active)?\'disabled\':\'\'" :src="b.imageurl" :alt="b.name">\n <span :class="(!b.active)?\'disabled\':\'\'">{{b.name}}</span>\n <template v-slot:drag-image="{data}"\n ><img :class="(!b.active)?\'disabled\':\'\'" :src="b.imageurl" :alt="b.name"\n ></template>\n </drag></li>\n </ul>\n </div>\n </b-tab>\n </b-tabs>\n </b-tab>\n </b-tabs>\n </p-sidebar>\n </div>\n '})}};return _exports.default=_default,_exports.default}));
|
|
|
|
//# sourceMappingURL=studyplan-editor-components.min.js.map
|