moodle_local_treestudyplan/amd/build/studyplan-editor-components.min.js

3 lines
No EOL
113 KiB
JavaScript

define("local_treestudyplan/studyplan-editor-components",["exports","./simpleline/simpleline","core/ajax","core/notification","core/str","./util/string-helper","./util/date-helper","./studyplan-processor","./util/debugger","core/config","./downloader","core/edit_switch","./treestudyplan-components","./util/mform-helper"],(function(_exports,_simpleline,_ajax,_notification,_str,_stringHelper,_dateHelper,_studyplanProcessor,_debugger,_config,_downloader,_edit_switch,_treestudyplanComponents,_mformHelper){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_notification=_interopRequireDefault(_notification),_debugger=_interopRequireDefault(_debugger),_config=_interopRequireDefault(_config),_treestudyplanComponents=_interopRequireDefault(_treestudyplanComponents),_mformHelper=_interopRequireDefault(_mformHelper);const STUDYPLAN_EDITOR_FIELDS=["name","shortname","description","idnumber","context_id","aggregation","aggregation_config"],PERIOD_EDITOR_FIELDS=["fullname","shortname","startdate","enddate"],datechanger_globals={default:!1,defaultchoice:!1,hidewarn:!1};var _default={STUDYPLAN_EDITOR_FIELDS:STUDYPLAN_EDITOR_FIELDS,install(Vue){Vue.use(_treestudyplanComponents.default),Vue.use(_mformHelper.default);let debug=new _debugger.default("treestudyplan-editor");function isVisible(elem){return!!(elem.offsetWidth||elem.offsetHeight||elem.getClientRects().length)}debug.info("config",_config.default);const ItemEventBus=new Vue;document.addEventListener(_edit_switch.eventTypes.editModeSet,(e=>{e.preventDefault(),ItemEventBus.$emit("editModeSet",e.detail.editMode)}));let string_keys=(0,_stringHelper.load_stringkeys)({conditions:[{value:"ALL",textkey:"condition_all"},{value:"ANY",textkey:"condition_any"}]}),strings=(0,_stringHelper.load_strings)({studyplan_text:{studyline_editmode:"studyline_editmode",toolbox_toggle:"toolbox_toggle",editmode_modules_hidden:"editmode_modules_hidden",studyline_add:"studyline_add",add$core:"add$core",edit$core:"edit$core",studyline_name:"studyline_name",studyline_name_ph:"studyline_name_ph",studyline_shortname:"studyline_shortname",studyline_shortname_ph:"studyline_shortname_ph",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"},studyplan_advanced:{advanced_tools:"advanced_tools",confirm_cancel:"confirm_cancel",confirm_ok:"confirm_ok",success$core:"success$core",error$core:"failed$core",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"},studyplan_edit:{studyplan_edit:"studyplan_edit",studyplan_add:"studyplan_add",studyplanpage_add:"studyplanpage_add",studyplanpage_edit:"studyplanpage_edit",info_periodsextended:"studyplanpage_info_periodsextended",warning:"warning@core"},period_edit:{edit:"period_edit",fullname:"studyplan_name",shortname:"studyplan_shortname",startdate:"studyplan_startdate",enddate:"studyplan_enddate"},course_timing:{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"},studyplan_associate:{associations:"associations",associated_cohorts:"associated_cohorts",associated_users:"associated_users",associate_cohorts:"associate_cohorts",associate_users:"associate_users",add_association:"add_association",delete_association:"delete_association",associations_empty:"associations_empty",associations_search:"associations_search",cohorts:"cohorts",users:"users",selected:"selected",name:"name",context:"context"},item_text:{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"},item_course_text:{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"},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"}});Vue.component("t-studyplan-advanced",{props:{value:{type:Object,default:()=>null},selectedpage:{type:Object,default:()=>null}},data:()=>({force_scales:{selected_scale:null,result:[]},text:strings.studyplan_advanced}),created(){},mounted(){},updated(){},computed:{scales(){return[{id:null,disabled:!0,name:this.text.advanced_pick_scale}].concat(this.value.advanced.force_scales.scales)}},methods:{force_scales_start(){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.force_scales.selected_scale}}])[0].then((function(response){self.force_scales.result=response})).catch(_notification.default.exception)}))},export_page(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((function(response){(0,_downloader.download)(self.value.shortname+".page."+format,response.content,response.format)})).catch(_notification.default.exception)},export_plan(){const self=this;(0,_ajax.call)([{methodname:"local_treestudyplan_export_plan",args:{studyplan_id:this.value.id,format:"json"}}])[0].then((function(response){(0,_downloader.download)(self.value.shortname+".plan.json",response.content,response.format)})).catch(_notification.default.exception)},bulk_course_timing(){(0,_ajax.call)([{methodname:"local_treestudyplan_bulk_course_timing",args:{page_id:this.selectedpage.id}}])[0].then((function(response){response.success?location.reload():(this.$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)},import_studylines(){(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((function(response){response.success?location.reload():(this.$bvModal.msgBoxOk(response.msg,{title:"Import failed"}),debug.error("Import failed: ",response.msg))})).catch(_notification.default.exception)}),"application/json")},import_pages(){(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((function(response){response.success?location.reload():(this.$bvModal.msgBoxOk(response.msg,{title:"Import failed"}),debug.error("Import failed: ",response.msg))})).catch(_notification.default.exception)}),"application/json")},purge_studyplan(){(0,_ajax.call)([{methodname:"local_treestudyplan_delete_studyplan",args:{id:this.value.id,force:!0}}])[0].then((function(response){response.success?location.reload():(this.$bvModal.msgBoxOk(response.msg,{title:"Could not delete plan "}),debug.error("Could not delete plan: ",response.msg))})).catch(_notification.default.exception)},purge_studyplanpage(){this.selectedpage&&(0,_ajax.call)([{methodname:"local_treestudyplan_delete_studyplanpage",args:{id:this.selectedpage.id,force:!0}}])[0].then((function(response){response.success?location.reload():(this.$bvModal.msgBoxOk(response.msg,{title:"Could not delete page"}),debug.error("Could not delete page: ",response.msg))})).catch(_notification.default.exception)},cascade_cohortsync(){const self=this;(0,_ajax.call)([{methodname:"local_treestudyplan_cascade_cohortsync",args:{studyplan_id:this.value.id}}])[0].then((function(response){self.$bvModal.msgBoxOk(response.success?self.text.success$core:self.text.error$core,{title:self.text.advanced_cascade_cohortsync})})).catch(_notification.default.exception)},modal_close(){this.force_scales.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="modal_close"\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="cascade_cohortsync"\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="bulk_course_timing"\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="force_scales.selected_scale"\n :options="scales" text-field="name" value-field="id"\n ></b-form-select>\n <b-button\n variant="danger"\n :disabled="force_scales.selected_scale == null"\n @click="force_scales_start"\n >{{ text.advanced_force_scale_button}}</b-button>\n </p>\n <p class="mt-2">\n <ul class=\'t-advanced-scrollable\' v-if="force_scales.result.length > 0">\n <li v-for="c in force_scales.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="export_page(\'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="export_plan(\'json\')"\n >{{ text.advanced_backup_plan }}</b-button></p>\n <h3>{{ text.advanced_restore }}</h3>\n <p><b-button\n variant="danger"\n @click="import_studylines"\n >{{ text.advanced_restore_lines}}</b-button></p>\n <p><b-button\n variant="danger"\n @click="import_pages"\n >{{ text.advanced_restore_pages }}</b-button></p>\n <h3>{{ text.advanced_export }}</h3>\n <p><b-button\n variant="primary"\n @click="export_page(\'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="purge_studyplanpage"\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="purge_studyplan"\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.studyplan_edit}),computed:{},methods:{planSaved(updatedplan){const self=this;if(debug.info("Got new plan data",updatedplan),"create"==self.mode)self.$emit("created",updatedplan);else{const moved_from=self.value.context_id,moved_to=updatedplan.context_id,moved=moved_from!=moved_to;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((function(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,moved_from,moved_to))}}},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.studyplan_edit"\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.studyplan_edit}),computed:{},methods:{planSaved(updatedpage){const self=this;if(debug.info("Got new page data",updatedpage),"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:[]},loading:{cohorts:!1,users:!1},search:{users:[],cohorts:[]},selected:{search:{users:[],cohorts:[]},associated:{users:[],cohorts:[]}},text:strings.studyplan_associate}),created(){},mounted(){},updated(){},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((function(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((function(response){self.association.cohorts=response.map(self.cohortOptionModel),self.loading.cohorts=!1})).catch(_notification.default.exception)},searchCohorts(searchtext){const self=this;searchtext.length>0?(0,_ajax.call)([{methodname:"local_treestudyplan_list_cohort",args:{like:searchtext,exclude_id:self.value.id}}])[0].then((function(response){self.search.cohorts=response.map(self.cohortOptionModel)})).catch(_notification.default.exception):self.search.cohorts=[]},cohortAssociate(){const self=this;let requests=[];const associated=self.association.cohorts,search=self.search.cohorts,searchselected=self.selected.search.cohorts;for(const i in searchselected){const r=searchselected[i];requests.push({methodname:"local_treestudyplan_connect_cohort",args:{studyplan_id:self.value.id,cohort_id:r},fail:_notification.default.exception,done:function(response){response.success&&(0,_studyplanProcessor.transportItem)(associated,search,r)}})}(0,_ajax.call)(requests)},cohortDisassociate(){const self=this;let requests=[];const associatedselected=self.selected.associated.cohorts,associated=self.association.cohorts,search=self.search.cohorts;for(const i in associatedselected){const r=associatedselected[i];requests.push({methodname:"local_treestudyplan_disconnect_cohort",args:{studyplan_id:self.value.id,cohort_id:r},fail:_notification.default.exception,done:function(response){response.success&&(0,_studyplanProcessor.transportItem)(search,associated,r)}})}(0,_ajax.call)(requests)},searchUsers(searchtext){const self=this;searchtext.length>0?(0,_ajax.call)([{methodname:"local_treestudyplan_find_user",args:{like:searchtext,exclude_id:self.value.id}}])[0].then((function(response){self.search.users=response.map(self.userOptionModel)})).catch(_notification.default.exception):self.search.users=[]},userAssociate(){const self=this;let requests=[];const associated=self.association.users,search=self.search.users,searchselected=self.selected.search.users;for(const i in searchselected){const r=searchselected[i];requests.push({methodname:"local_treestudyplan_connect_user",args:{studyplan_id:self.value.id,user_id:r},fail:_notification.default.exception,done:function(response){response.success&&(0,_studyplanProcessor.transportItem)(associated,search,r)}})}(0,_ajax.call)(requests)},userDisassociate(){const self=this;let requests=[];const associated=self.association.users,associatedselected=self.selected.associated.users,search=self.search.users;for(const i in associatedselected){const r=associatedselected[i];requests.push({methodname:"local_treestudyplan_disconnect_user",args:{studyplan_id:self.value.id,user_id:r},fail:_notification.default.exception,done:function(response){response.success&&(0,_studyplanProcessor.transportItem)(search,associated,r)}})}(0,_ajax.call)(requests)}},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="cohortDisassociate()"\n ><i class=\'fa fa-chain-broken\'></i>&nbsp;{{text.delete_association}}</b-button>\n </b-col>\n <b-col>\n <b-button variant=\'success\' @click="cohortAssociate()"\n ><i class=\'fa fa-link\'></i>&nbsp;{{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="Search 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="userDisassociate()"\n ><i class=\'fa fa-chain-broken\'></i>&nbsp;{{text.delete_association}}</b-button>\n </b-col>\n <b-col>\n <b-button variant=\'success\' @click="userAssociate()"\n ><i class=\'fa fa-link\'></i>&nbsp;{{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.period_edit}),created(){},mounted(){},updated(){},computed:{},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((function(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((function(response){(0,_studyplanProcessor.objCopy)(self.value,response,PERIOD_EDITOR_FIELDS),self.$emit("input",self.value)})).catch(_notification.default.exception)},add_day:(date,days)=>(void 0===days&&(days=1),(0,_dateHelper.add_days)(date,days)),sub_day:(date,days)=>(void 0===days&&(days=1),(0,_dateHelper.add_days)(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>\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="sub_day(value.enddate)"\n ></b-form-datepicker>\n </b-col>\n </b-row>\n <b-row>\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="add_day(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","index"],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"},page:{id:-1,name:"",shortname:""}},edit:{toolbox_shown:!1,studyline:{editmode:!1,data:{name:"",shortname:"",color:"#DDDDDD"},original:{}},studyplan:{data:{name:"",shortname:"",description:"",slots:4,startdate:"2020-08-01",enddate:"",aggregation:"",aggregation_config:"",aggregation_info:{useRequiredGrades:!0,useItemCondition:!1}},original:{}}},text:strings.studyplan_text,cache:{linelayers:{}},selectedpageindex:0,emptyline:{id:-1,name:"<No study lines defined>",shortname:"<No study lines>",color:"#FF0000",filterslots:[{}],courseslots:[{}]}}),created(){const self=this;ItemEventBus.$on("coursechange",(()=>{self.$emit("pagechanged",this.selectedpage)}))},mounted(){0==this.value.pages[0].studylines.length&&(this.edit.studyline.editmode=!0),this.$root.$emit("redrawLines"),this.$emit("pagechanged",this.selectedpage)},updated(){this.$root.$emit("redrawLines"),ItemEventBus.$emit("redrawLines")},computed:{selectedpage(){return this.value.pages[this.selectedpageindex]}},methods:{columns:page=>1+2*page.periods,columns_stylerule(page){let s="grid-template-columns: var(--studyplan-filter-width)";for(let i=0;i<page.periods;i++)s+=" var(--studyplan-course-width) var(--studyplan-filter-width)";return s+";"},trashbin_accepts: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]){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}}])[0].then((function(response){page.studylines.push(response),newlineinfo.name="",newlineinfo.shortname="",newlineinfo.color="#dddddd"})).catch(_notification.default.exception)},editLineStart(line){Object.assign(this.edit.studyline.data,line),this.edit.studyline.original=line,this.$bvModal.show("modal-edit-studyline-"+this.value.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}}])[0].then((function(response){originalline.name=response.name,originalline.shortname=response.shortname,originalline.color=response.color})).catch(_notification.default.exception)},deleteLine(page,line){const self=this;(0,_str.get_strings)([{key:"studyline_confirm_remove",param:line.name,component:"local_treestudyplan"},{key:"delete",component:"core"}]).then((function(s){self.$bvModal.msgBoxConfirm(s[0],{okTitle:s[1],okVariant:"danger"}).then((function(modalresponse){modalresponse&&(0,_ajax.call)([{methodname:"local_treestudyplan_delete_studyline",args:{id:line.id}}])[0].then((function(response){if(1==response.success){let index=page.studylines.indexOf(line);page.studylines.splice(index,1)}})).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((function(response){})).catch(_notification.default.exception)},deletePlan(studyplan){const self=this;(0,_str.get_strings)([{key:"studyplan_confirm_remove",param:studyplan.name,component:"local_treestudyplan"},{key:"delete",component:"core"}]).then((function(s){self.$bvModal.msgBoxConfirm(s[0],{okTitle:s[1],okVariant:"danger"}).then((function(modalresponse){modalresponse&&(0,_ajax.call)([{methodname:"local_treestudyplan_delete_studyplan",args:{id:studyplan.id,force:!0}}])[0].then((function(response){1==response.success&&self.$root.$emit("studyplanRemoved",studyplan)})).catch(_notification.default.exception)}))}))},deleteStudyItem(event){let item=event.data;(0,_ajax.call)([{methodname:"local_treestudyplan_delete_studyitem",args:{id:item.id}}])[0].then((function(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)][0];prev&&prev.refresh();const next=this.$refs["periodeditor-"+(pi.period+1)][0];next&&next.refresh()},add_day:(date,days)=>(void 0===days&&(days=1),(0,_dateHelper.add_days)(date,days)),sub_day:(date,days)=>(void 0===days&&(days=1),(0,_dateHelper.add_days)(date,0-days)),toolbox_switched(event){this.$emit("toggletoolbox",event)},pagecreated(page){this.value.pages.push(page)},selectedpageChanged(newTabIndex,prevTabIndex){const page=this.value.pages[newTabIndex];this.$emit("pagechanged",page)}},template:'\n <div>\n <div class=\'controlbox t-studyplan-controlbox\'>\n <div class="controlbox-group">\n <b-form-checkbox v-model="edit.studyline.editmode" class="sw-studyplan-toolbar" switch\n @change="toolbox_switched(edit.toolbox_shown && !edit.studyline.editmode); "\n >{{ text.studyline_editmode }}</b-form-checkbox>\n <b-form-checkbox v-if="!edit.studyline.editmode" v-model="edit.toolbox_shown" class="sw-studyplan-toolbar" switch\n @change="toolbox_switched"\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="trashbin_accepts"\n ><i class=\'fa fa-trash\'></i>\n </drop>\n </div>\n <div class="controlbox-group">\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$core}}</t-studyplan-edit>\n </span>\n <span class=\'control deletable\'>\n <a v-if=\'value.pages.length == 0\' href=\'#\' @click=\'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 :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="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 xreorder="$event.apply(page.studylines)"\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-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="columns_stylerule(page)">\n \x3c!-- add period information --\x3e \n <template v-for="(n,index) in (page.periods+1)">\n <s-studyline-header-period\n :identifier=\'Number(page.id)\'\n v-if="index > 0"\n v-model="page.perioddesc[index-1]"\n ><t-period-edit\n :ref="\'periodeditor-\'+index"\n @edited="periodEdited"\n v-model="page.perioddesc[index-1]"\n :minstart="(index > 1) ? add_day(page.perioddesc[index-2].startdate,2) : null"\n :maxend="(index < page.periods) ? sub_day(page.perioddesc[index].enddate,2) : null"\n ></t-period-edit\n ></s-studyline-header-period>\n <div class="s-studyline-header-filter"></div>\n </template>\n\n \x3c!-- Line by line add the items --\x3e\n \x3c!-- The grid layout handles putting it in rows and columns --\x3e\n <template v-for="(line,lineindex) in page.studylines"\n ><template v-for="(layernr,layeridx) in countLineLayers(line,page)+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 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 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 ></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="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$core"\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-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-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:function(){return{}}},layers:{type:Number,default:1}},data:()=>({layerHeights:{}}),created(){ItemEventBus.$on("lineHeightChange",this.onLineHeightChange)},computed:{},methods:{onLineHeightChange(lineid,layerid,newheight){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:function(){return{}}}},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='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='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},dragacceptlist(){return"gradable"==this.type?["course","gradable-item"]:["filter","filter-item"]},courseHoverDummy(){return{course:this.hover.component}},spanCss(){if(this.item&&this.item.span>1){return`width: 100%; grid-column: span ${2*this.item.span-1};`}return""}},data:()=>({text:strings.course_timing,resizeListener:null,hover:{component:null,type:null},datechanger:{coursespan:null,periodspan:null,default:!1,defaultchoice:!1,hidewarn:!1}}),methods:{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).done((()=>{this.$refs.timingChecker&&this.$refs.timingChecker.validate_course_period()}))}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).done((()=>{self.value.push(item),self.$emit("input",self.value),this.$nextTick((()=>{this.$refs.timingChecker&&this.$refs.timingChecker.validate_course_period()})),ItemEventBus.$emit("coursechange")}))})).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).done((()=>{item.layer=this.layer,self.value.push(item),self.$emit("input",self.value)}))})).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&&("filter"==type||type.span<=this.maxSpan())}},template:'\n <div :class="\'t-studyline-slot \'+type + \' t-studyline-slot-\'+slotindex + \' \' + ((slotindex==0)?\' t-studyline-firstcolumn \':\' \')"\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\')"\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 ></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]},course_period_matches(){const self=this;return 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.course_timing,datechanger:{coursespan:null,periodspan:null,globals:datechanger_globals}}),methods:{validate_course_period(){const self=this;debug.info("Validating course and period"),self.course_period_matches?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.change_course_period():this.$bvModal.show("t-course-timing-matching-"+this.id):self.hidden&&self.datechanger.globals.hidewarn||this.$bvModal.show("t-course-timing-warning-"+this.id))},has_filter(){this.page.slots},change_course_period(){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).done((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},shift_collisions(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).done((response=>{}))},change_span(span){const self=this;return this.shift_collisions(span),(0,_ajax.call)([{methodname:"local_treestudyplan_set_studyitem_span",args:{id:self.value.id,span:span}}])[0].catch(_notification.default.exception).done((response=>{self.value.span=response.span,self.$emit("input",self.value),self.$nextTick((()=>{self.validate_course_period()}))}))},format_duration(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="course_period_matches">\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="validate_course_period()" 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 }}&nbsp;<b-form-select v-if="maxspan > 1"\n class=""\n size="sm" @change="change_span" 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>&nbsp;{{\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="change_course_period"\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 {{ format_duration(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 {{ format_duration(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 {{ format_duration(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 {{ format_duration(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,condition_options:string_keys.conditions,text:strings.item_text,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 from_id=event.data.id,to_id=this.value.id;this.redrawLines(),(0,_ajax.call)([{methodname:"local_treestudyplan_connect_studyitems",args:{from_id:from_id,to_id:to_id}}])[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 c_in=this.value.connections.in[i];conn.id==c_in.id&&self.value.connections.out.splice(i,1)}},onRePositioned(){this.redrawLines()},onDisPositioned(re_id){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==re_id&&this.removeLine(conn)}},onItemDeleted(item_id){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==item_id&&(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==item_id&&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((function(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)}},beforeUpdate(){},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="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="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="condition_options"\n ></b-form-select>\n </b-form-group>\n\n <template #modal-footer="{ ok, cancel, hide }" >\n <a href=\'#\' @click=\'deleteItem()\' class="text-danger"\n ><i class="fa fa-trash"></i>\n {{ text.delete }}\n </a>\n <b-button size="sm" variant="primary" @click="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:function(){return 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=\'$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:()=>({condition_options:string_keys.conditions,text:strings.item_course_text}),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.format_date)(this.value.course.startdate)},enddate(){return this.value.course.enddate?(0,_dateHelper.format_date)(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)}},created(){},template:'\n <div class="t-item-course">\n <b-card no-body >\n <div class=\'d-flex flex-wrap mr-0 ml-0\'>\n <div>\n <span\n :title="text[\'coursetiming_\'+value.course.timing]"\n v-b-popover.hover.top="startdate+\' - \'+enddate"\n :class="\'t-timing-indicator timing-\'+value.course.timing"></span>\n </div>\n <div class="flex-fill align-items-center">\n <b-card-body >\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 <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 </b-card-body>\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=\'$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="ok()">\n {{ text.ok }}\n </b-button>\n </template>\n </b-modal>\n </b-card></div>\n '}),Vue.component("t-item-course-grades",{props:{value:{type:Object,default:()=>null},plan:{type:Object,default:()=>null}},data:()=>({condition_options:string_keys.conditions,text:strings.item_course_text}),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)}},created(){},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:function(){return{}}},guestmode:{type:Boolean,default:!1},course:{type:Object,default:function(){return{}}}},data:()=>({text:strings.completion}),created(){const self=this;let stringkeys=[];for(const key in this.text)stringkeys.push({key:key,component:"local_treestudyplan"});(0,_str.get_strings)(stringkeys).then((function(strings){let i=0;for(const key in self.text)self.text[key]=strings[i],i++}))},computed:{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:{completion_icon(completion){switch(completion){case"progress":return"exclamation-circle";case"complete":case"complete-pass":return"check-circle";case"complete-fail":return"times-circle";default:return"circle-o"}},completion_tag:cgroup=>cgroup.completion?"completed":"incomplete"},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:function(){return{}}},guestmode:{type:Boolean,default:!1},item:{type:Object,default:function(){return{id:null}}}},data:()=>({text:strings.competency}),created(){},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:function(){return{}}}},data:()=>({condition_options:string_keys.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:function(){return{}}}},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:function(){return{}}}},data:()=>({}),created(){},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:function(){return{badge:{}}}}},data:()=>({txt:strings,text:strings.item_text}),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=\'$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="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:function(){return{}}}},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:function(){return{}}}},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||0==this.value.children.length)||this.value.hascourses&&(!this.value.courses||0==this.value.courses.length)},onShowDetails(){const self=this;this.canLoadMore()&&(0,_ajax.call)([{methodname:"local_treestudyplan_get_category",args:{id:this.value.id}}])[0].then((function(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:function(){return{}}}},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 <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 </li>\n </ul>\n '})}};return _exports.default=_default,_exports.default}));
//# sourceMappingURL=studyplan-editor-components.min.js.map