/*eslint no-var: "error"*/
/*eslint no-console: "off"*/
/*eslint-disable no-trailing-spaces */
/*eslint-env es6*/
// Put this file in path/to/plugin/amd/src
import LeaderLine from './leaderline';
import {get_strings} from 'core/str';
import {load_strings} from './string-helper';
import {call} from 'core/ajax';
import notification from 'core/notification';
//import {fixLineWrappers} from './studyplan-processor';
export default {
install(Vue/*,options*/){
let strings = load_strings({
invalid: {
error: 'error',
},
grading: {
ungraded: "ungraded",
graded: "graded",
allgraded: "allgraded",
unsubmitted: "unsubmitted",
nogrades: "nogrades",
unknown: "unknown",
}
});
/************************************
* *
* Treestudyplan Editor components *
* *
************************************/
/**
* Check if element is visible
* @param {Object} elem The element to check
* @returns {boolean} True if visible
*/
function isVisible(elem){
return !!( elem.offsetWidth || elem.offsetHeight || elem.getClientRects().length );
}
Vue.component('r-report', {
props: {
value: {
type: Array,
},
guestmode: {
type: Boolean,
default: false,
},
teachermode: {
type: Boolean,
default: false,
}
},
data() {
return {
selectedstudyplan: null,
};
},
computed: {
displayedstudyplan(){
if(this.selectedstudyplan){
return this.selectedstudyplan;
} else if(this.value && this.value.length > 0){
return this.value[0];
} else {
return null;
}
}
},
methods: {
},
template: `
{{studyplan.name}}
`,
});
Vue.component('r-studyplan', {
props: {
value: {
type: Object,
},
guestmode: {
type: Boolean,
default: false,
},
teachermode: {
type: Boolean,
default: false,
}
},
data() {
return {
};
},
updated(){
this.$root.$emit('redrawLines');
},
mounted(){
this.$root.$emit('redrawLines');
},
computed: {
},
methods: {
},
template: `
`,
});
/*
* R-STUDYLINE
*/
Vue.component('r-studyline', {
props: ['color','name','code', 'slots','sequence','numlines','guestmode','teachermode'],
data() {
return {
};
},
computed: {
},
methods: {
},
template: `
`,
});
Vue.component('r-studyline-slot', {
props: {
type : {
type: String,
default: 'competency',
},
slotindex : {
type: Number,
default: 0,
},
lineid : {
type: Number,
default: 0,
},
value: {
type: Array,
default(){ return [];},
},
plan: {
type: Object,
default(){ return null;}
},
guestmode: {
type: Boolean,
default: false,
},
teachermode: {
type: Boolean,
default: false,
}
},
computed: {
sorted(){
let copy = [...this.value];
copy.sort(function(a,b){
return a.layer - b.layer;
});
return copy;
}
},
data() {
return {
};
},
methods: {
},
template: `
`,
});
Vue.component('r-item', {
props: {
value :{
type: Object,
default: function(){ return null;},
},
plan: {
type: Object,
default(){ return null;}
},
guestmode: {
type: Boolean,
default: false,
},
teachermode: {
type: Boolean,
default: false,
}
},
data() {
return {
lines: [],
};
},
methods: {
lineColor(){
if(this.teachermode){
return "#aaa";
}
else{
switch(this.value.completion){
default: // "incomplete"
return "#777";
case "failed":
return "#933";
case "progress":
return "#da3";
case "completed":
return "#383";
case "good":
return "#398";
case "excellent":
return "#36f";
}
}
},
redrawLine(conn){
let lineColor = this.lineColor();
// prepare lineinfo link or delete old line
let lineinfo = this.lines[conn.to_id];
if(lineinfo){
if(lineinfo.line){
if(lineinfo.lineElm ){
lineinfo.lineElm.parentNode.removeChild(lineinfo.lineElm);
lineinfo.lineElm = undefined;
} else {
lineinfo.line.remove();
}
lineinfo.line = undefined;
}
} else {
lineinfo = {};
this.lines[conn.to_id] = lineinfo;
}
// draw new line...
let start = document.getElementById('studyitem-'+conn.from_id);
let end= document.getElementById('studyitem-'+conn.to_id);
LeaderLine.positionByWindowResize = false;
if(start !== null && end !== null && isVisible(start) && isVisible(end)){
lineinfo.line = new LeaderLine(start,end,{
color: lineColor,
startSocket: 'right',
endSocket: 'left',
startSocketGravity: 75,
endSocketGravity: 75,
});
let elmWrapper = (this.plan.id >=0)?document.getElementById('studyplan-linewrapper-'+this.plan.id):null;
if(elmWrapper !== null){
let elmLine = document.querySelector('body > .leader-line:last-child');
elmWrapper.appendChild(elmLine);
lineinfo.lineElm = elmLine; // store line element so it can more easily be removed from the dom
}
setTimeout(function(){
if(lineinfo.line){
lineinfo.line.position();
}
},1);
}
},
redrawLines(){
for(let i in this.value.connections.out){
let conn = this.value.connections.out[i];
this.redrawLine(conn);
}
},
onWindowResize(){
this.redrawLines();
}
},
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(){
},
mounted(){
// Initialize connection lines when mounting
this.redrawLines();
setTimeout(()=>{
this.redrawLines();
},50);
// Add resize event listener
window.addEventListener('resize',this.onWindowResize);
},
beforeDestroy(){
for(let i in this.value.connections.out){
let conn = this.value.connections.out[i];
let lineinfo = this.lines[conn.to_id];
if(lineinfo){
if(lineinfo.line){
if(lineinfo.lineElm ){
lineinfo.lineElm.parentNode.removeChild(lineinfo.lineElm);
lineinfo.lineElm = undefined;
} else {
lineinfo.line.remove();
}
lineinfo.line = undefined;
}
}
}
// Remove resize event listener
window.removeEventListener('resize',this.onWindowResize);
},
beforeUpdate(){
},
updated(){
if(!this.dummy) {
this.redrawLines();
}
},
template: `
`,
});
Vue.component('r-item-invalid', {
props: {
'value' :{
type: Object,
default: function(){ return null;},
},
},
data() {
return {
text: strings.invalid,
};
},
methods: {
},
template: `
{{ text.error }}
`,
});
Vue.component('r-item-course', {
props: {
value :{
type: Object,
default(){ return null;},
},
guestmode: {
type: Boolean,
default(){ return false;}
},
teachermode: {
type: Boolean,
default(){ return false;}
},
plan: {
type: Object,
default(){ return null;}
}
},
data() {
return {
text: {
completion_incomplete: "completion_incomplete",
completion_failed: "completion_failed",
completion_pending: "completion_pending",
completion_progress: "completion_progress",
completion_completed: "completion_completed",
completion_good: "completion_good",
completion_excellent: "completion_excellent",
view_feedback: "view_feedback",
coursetiming_past: "coursetiming_past",
coursetiming_present: "coursetiming_present",
coursetiming_future: "coursetiming_future",
required_goal: "required_goal",
},
};
},
computed: {
},
created(){
const self = this;
// Get text strings for condition settings
let stringkeys = [];
for(const key in this.text){
stringkeys.push({ key: key, component: 'local_treestudyplan'});
}
get_strings(stringkeys).then(function(strings){
let i = 0;
for(const key in self.text){
self.text[key] = strings[i];
i++;
}
});
},
methods: {
completion_icon(completion) {
switch(completion){
default: // case "incomplete"
return "circle-o";
case "pending":
return "question-circle";
case "failed":
return "times-circle";
case "progress":
return "exclamation-circle";
case "completed":
return "check-circle";
case "good":
return "check-circle";
case "excellent":
return "check-circle";
}
},
},
template: `
{{ value.course.displayname }}
{{ value.course.context.path.join(" / ")}}
<-item-studentcompletion
v-if='!!value.course.completion'
v-model='value.course.completion'
:course='value.course'
:guestmode='guestmode'>
`,
});
Vue.component('r-item-studentgrades',{
props: {
value : {
type: Object,
default: function(){ return {};},
},
guestmode: {
type: Boolean,
default: false,
},
},
data() {
return {
};
},
computed: {
pendingsubmission(){
let result = false;
for(const ix in this.value.course.grades){
const g = this.value.course.grades[ix];
if(g.pendingsubmission){
result = true;
break;
}
}
return result;
},
useRequiredGrades() {
if(this.plan && this.plan.aggregation_info && this.plan.aggregation_info.useRequiredGrades !== undefined){
return this.plan.aggregation_info.useRequiredGrades;
}
else {
return false;
}
},
},
methods: {
completion_icon(completion) {
switch(completion){
default: // case "incomplete"
return "circle-o";
case "pending":
return "question-circle";
case "failed":
return "times-circle";
case "progress":
return "exclamation-circle";
case "completed":
return "check-circle";
case "good":
return "check-circle";
case "excellent":
return "check-circle";
}
},
},
template: `
`,
});
Vue.component('r-item-studentcompletion',{
props: {
value : {
type: Object,
default: function(){ return {};},
},
guestmode: {
type: Boolean,
default: false,
},
course: {
type: Object,
default: function(){ return {};},
},
},
data() {
return {
};
},
computed: {
},
methods: {
completion_icon(completion) {
switch(completion){
default: // case "incomplete"
return "circle-o";
case "pending":
return "question-circle";
case "failed":
return "times-circle";
case "progress":
return "exclamation-circle";
case "completed":
return "check-circle";
case "good":
return "check-circle";
case "excellent":
return "check-circle";
}
},
completion_tag(cgroup){
return cgroup.completion?'completed':'incomplete';
}
},
template: `
`,
});
Vue.component('r-item-teachercourse', {
props: {
value :{
type: Object,
default(){ return null;}
},
guestmode: {
type: Boolean,
default(){ return false;}
},
teachermode: {
type: Boolean,
default(){ return false;}
},
plan: {
type: Object,
default(){ return null;}
}
},
data() {
return {
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",
required_goal: "required_goal",
},
txt: {
grading: strings.grading,
}
};
},
computed: {
course_grading_needed(){
return this.course_grading_state();
},
course_grading_icon(){
return this.determine_grading_icon(this.course_grading_state());
},
filtered_grades(){
return this.value.course.grades.filter(g => g.selected);
},
useRequiredGrades() {
if(this.plan && this.plan.aggregation_info && this.plan.aggregation_info.useRequiredGrades !== undefined){
return this.plan.aggregation_info.useRequiredGrades;
}
else {
return false;
}
},
},
created(){
const self = this;
// Get text strings for condition settings
let stringkeys = [];
for(const key in this.text){
stringkeys.push({ key: key, component: 'local_treestudyplan'});
}
get_strings(stringkeys).then(function(strings){
let i = 0;
for(const key in self.text){
self.text[key] = strings[i];
i++;
}
});
},
methods: {
course_grading_state(){
let ungraded = 0;
let unknown = 0;
let graded = 0;
let allgraded = 0;
const grades = this.filtered_grades;
if(!Array.isArray(grades) || grades == 0){
return 'nogrades';
}
for(const ix in grades){
const grade = grades[ix];
if(grade.grading){
if(Number(grade.grading.ungraded) > 0){
ungraded++;
}
else if(Number(grade.grading.graded) > 0) {
if(Number(grade.grading.graded) == Number(grade.grading.students)){
allgraded++;
} else {
graded++;
}
}
} else {
unknown = true;
}
}
if(ungraded > 0){
return 'ungraded';
} else if(unknown) {
return 'unknown';
} else if(graded){
return 'graded';
} else if(allgraded){
return 'allgraded';
} else {
return 'unsubmitted';
}
},
determine_grading_icon(gradingstate){
switch(gradingstate){
default: // "nogrades":
return "circle-o";
case "ungraded":
return "exclamation-circle";
case "unknown":
return "question-circle-o";
case "graded":
return "check";
case "allgraded":
return "check";
case "unsubmitted":
return "dot-circle-o";
}
},
is_grading_needed(grade){
if(grade.grading){
if(grade.grading.ungraded){
return 'ungraded';
}
else if(grade.grading.graded){
if(Number(grade.grading.graded) == Number(grade.grading.students)){
return 'allgraded';
}
else {
return 'graded';
}
}
else {
return 'unsubmitted';
}
} else {
return 'unknown';
}
},
grading_icon(grade){
return this.determine_grading_icon(this.is_grading_needed(grade));
},
includeChanged(newValue,g){
call([{
methodname: 'local_treestudyplan_include_grade',
args: { 'grade_id': g.id,
'item_id': this.value.id,
'include': newValue,
'required': g.required,
}
}])[0].fail(notification.exception);
},
requiredChanged(newValue,g){
call([{
methodname: 'local_treestudyplan_include_grade',
args: { 'grade_id': g.id,
'item_id': this.value.id,
'include': g.selected,
'required': newValue,
}
}])[0].fail(notification.exception);
},
},
template: `
{{ value.course.displayname }}
{{ value.course.context.path.join(" / ")}}
{{g.name}}
g.name = fd.get('name')"
v-if="g.cmid > 0"
:cmid="g.cmid"
:coursectxid="value.course.ctxid"
genericonly>
|
|
|
{{ value.course.context.path.join(" / ")}} / {{value.course.displayname}}
-
{{text.grade_include}}{{text.grade_require}}
-
{{g.name}}
g.name = fd.get('name')"
v-if="g.cmid > 0"
:cmid="g.cmid"
:coursectxid="value.course.ctxid"
genericonly>
`,
});
Vue.component('r-grading-bar',{
props: {
value : {
type: Object,
default: function(){ return {};},
},
width: {
type: Number,
default: 150,
},
height: {
type: Number,
default: 15,
}
},
data() {
return {
text: strings.grading,
};
},
computed: {
width_unsubmitted() {
return this.width * this.fraction_unsubmitted();
},
width_graded() {
return this.width * this.fraction_graded();
},
width_ungraded() {
return this.width * this.fraction_ungraded();
},
count_unsubmitted(){
return (this.value.students - this.value.graded - this.value.ungraded);
}
},
methods: {
fraction_unsubmitted() {
if(this.value.students > 0){
return 1 - ((this.value.graded + this.value.ungraded) / this.value.students);
} else {
return 1;
}
},
fraction_graded() {
if(this.value.students > 0){
return this.value.graded / this.value.students;
} else {
return 0;
}
},
fraction_ungraded() {
if(this.value.students > 0){
return this.value.ungraded / this.value.students;
} else {
return 0;
}
},
},
template: `
`,
});
Vue.component('r-item-junction',{
props: {
value : {
type: Object,
default: function(){ return {};},
},
guestmode: {
type: Boolean,
default: false,
},
teachermode: {
type: Boolean,
default: false,
}
},
data() {
return {
};
},
computed: {
completion(){
if(this.value.completion){
return this.value.completion;
} else {
return "incomplete";
}
}
},
methods: {
},
template: `
`,
});
Vue.component('r-item-finish',{
props: {
value : {
type: Object,
default: function(){ return {};},
},
guestmode: {
type: Boolean,
default: false,
},
teachermode: {
type: Boolean,
default: false,
}
},
data() {
return {
};
},
computed: {
completion(){
if(this.value.completion){
return this.value.completion;
} else {
return "incomplete";
}
}
},
methods: {
},
template: `
`,
});
Vue.component('r-item-start',{
props: {
value : {
type: Object,
default: function(){ return {};},
},
guestmode: {
type: Boolean,
default: false,
},
teachermode: {
type: Boolean,
default: false,
}
},
data() {
return {
};
},
computed: {
completion(){
if(this.value.completion){
return this.value.completion;
} else {
return "incomplete";
}
}
},
created(){
},
methods: {
},
template: `
`,
});
Vue.component('r-item-badge',{
props: {
value : {
type: Object,
default: function(){ return {};},
},
guestmode: {
type: Boolean,
default: false,
},
teachermode: {
type: Boolean,
default: false,
}
},
data() {
return {
};
},
computed: {
completion() {
return this.value.badge.issued?"completed":"incomplete";
},
},
methods: {
},
template: `
`,
});
},
};