/* eslint no-var: "error"*/
/* eslint no-console: "off"*/
/* eslint no-unused-vars: warn */
/* eslint max-len: ["error", { "code": 160 }] */
/* eslint promise/no-nesting: "off" */
/* eslint max-depth: ["error", 6] */
/* eslint no-trailing-spaces: warn */
/* eslint-env es6*/

import {SimpleLine} from './simpleline/simpleline';
import {loadStrings} from './util/string-helper';
import {formatDate, formatDatetime, studyplanPageTiming, studyplanTiming} from './util/date-helper';
import {addBrowserButtonEvent} from './util/browserbuttonevents';
import {call} from 'core/ajax';
import notification from 'core/notification';
import {svgarcpath} from './util/svgarc';
import Debugger from './util/debugger';
import Config from 'core/config';
import {processStudyplan, objCopy} from './studyplan-processor';
import TSComponents from './treestudyplan-components';
import FitTextVue from './util/fittext-vue';


// Make π available as a constant
const π = Math.PI;
// Gravity value for arrow lines - determines how much a line is pulled in the direction of the start/end before changing direction
const LINE_GRAVITY = 1.3;

/**
 * Studyline is not enrollable
 * @var int
 */
const ENROLLABLE_NONE = 0;

/**
 * Studyline can be enrolled into by the student
 * @var int
 */
const ENROLLABLE_SELF = 1;

/**
 * Studyline can be enrolled into by specific role(s)
 * @var int
 */
const ENROLLABLE_ROLE = 2;

/**
 * Studyline can be enrolled by user and/or role
 * @var int
 */
const ENROLLABLE_SELF_ROLE = 3;


export default {
    install(Vue/* ,options */) {
        Vue.use(TSComponents);
        Vue.use(FitTextVue);
        let debug = new Debugger("treestudyplan-viewer");

        let lastCaller = null;
        /**
         * Scroll current period into view
         * @param {*} handle A key to pass so subsequent calls with the same key won't trigger (always triggers when null or undefined)
         */
        function scrollCurrentIntoView(handle) {
            const elScrollContainer = document.querySelector(".r-studyplan-scrollable");
            const elCurrentHeader = elScrollContainer.querySelector(".s-studyline-header-period.current");

            if (elCurrentHeader && ((!handle) || (handle != lastCaller))) {
                lastCaller = handle;
                elCurrentHeader.scrollIntoView({
                    behavior: "smooth",
                    block: "start",
                    inline: "center",
                });
            }
        }

        let strings = loadStrings({
            report: {
                loading: "loadinghelp@core",
                'studyplan_past': "studyplan_past",
                'studyplan_present': "studyplan_present",
                'studyplan_future': "studyplan_future",
                back: "back",
            },
            invalid: {
                error: 'error',
            },
            grading: {
                ungraded: "ungraded",
                graded: "graded",
                allgraded: "allgraded",
                unsubmitted: "unsubmitted",
                nogrades: "nogrades",
                unknown: "unknown",
            },
            completion: {
                completed: "completion_completed",
                incomplete: "completion_incomplete",
                'completed_pass': "completion_passed",
                'completed_fail': "completion_failed",
                ungraded: "ungraded",
                'aggregation_all': "aggregation_all",
                'aggregation_any': "aggregation_any",
                'aggregation_one': "aggregation_one",
                'aggregation_overall_all': "aggregation_overall_all",
                'aggregation_overall_any': "aggregation_overall_any",
                'aggregation_overall_one': "aggregation_overall_one",
                'completion_not_configured': "completion_not_configured",
                'configure_completion': "configure_completion",
                'view_completion_report': "view_completion_report",
                'completion_incomplete': "completion_incomplete",
                'completion_failed': "completion_failed",
                'completion_pending': "completion_pending",
                'completion_progress': "completion_progress",
                'completion_completed': "completion_completed",
                'completion_good': "completion_good",
                'completion_excellent': "completion_excellent",
                'view_feedback': "view_feedback",
                'coursetiming_past': "coursetiming_past",
                'coursetiming_present': "coursetiming_present",
                'coursetiming_future': "coursetiming_future",
                'required_goal': "required_goal",
                'student_not_tracked': "student_not_tracked",
                'completion_not_enabled': "completion_not_enabled",
            },
            badge: {
                'share_badge': "share_badge",
                dateissued: "dateissued",
                dateexpire: "dateexpire",
                badgeinfo: "badgeinfo",
                badgeissuedstats: "badgeissuedstats",
                'completion_incomplete': "completion_incomplete_badge",
                'completion_completed': "completion_completed_badge",
                badgedisabled: "badgedisabled"
            },
            course: {
                'completion_incomplete': "completion_incomplete",
                'completion_failed': "completion_failed",
                'completion_pending': "completion_pending",
                'completion_progress': "completion_progress",
                'completion_completed': "completion_completed",
                'completion_good': "completion_good",
                'completion_excellent': "completion_excellent",
                'view_feedback': "view_feedback",
                'coursetiming_past': "coursetiming_past",
                'coursetiming_present': "coursetiming_present",
                'coursetiming_future': "coursetiming_future",
                'required_goal': "required_goal",
                'student_not_tracked': "student_not_tracked",
                'not_enrolled': "not_enrolled",
                noenddate: "noenddate",
            },
            teachercourse: {
                'select_conditions': "select_conditions",
                'select_grades': "select_grades",
                'coursetiming_past': "coursetiming_past",
                'coursetiming_present': "coursetiming_present",
                'coursetiming_future': "coursetiming_future",
                'grade_include': "grade_include",
                'grade_require': "grade_require",
                'required_goal': "required_goal",
                'student_from_plan_enrolled': "student_from_plan_enrolled",
                'students_from_plan_enrolled': "students_from_plan_enrolled",
                noenddate: "noenddate",
            },
            competency: {
                'competency_not_configured': "competency_not_configured",
                'configure_competency': "configure_competency",
                when: "when",
                required: "required",
                points: "points@core_grades",
                heading: "competency_heading",
                details: "competency_details",
                results: "results",
                unrated: "unrated",
                progress: "completion_progress",
                'view_feedback': "view_feedback",
            },
            pageinfo: {
                edit: 'period_edit',
                fullname: 'studyplan_name',
                shortname: 'studyplan_shortname',
                startdate: 'studyplan_startdate',
                enddate: 'studyplan_enddate',
                description: 'studyplan_description',
                duration: 'studyplan_duration',
                details: 'studyplan_details',
                overview: 'overviewreport:all',
                oveviewperiod: 'overviewreport:period'
            },
            lineheader: {
                'cannot_enrol': 'line_cannot_enrol',
                'can_enrol': 'line_can_enrol',
                'is_enrolled': 'line_is_enrolled',
                enrol: 'line_enrol',
                unenrol: 'line_unenrol',
                enrolled: 'line_enrolled',
                notenrolled: 'line_notenrolled',
                'enrol_question': 'line_enrol_question',
                enrollments: 'line_enrollments',
                enrollment: 'line_enrollment',
                info: 'info@core',
                confirm: 'confirm@core',
                yes: 'yes@core',
                no: 'no@core',
                'enrolled_in': 'line_enrolled_in',
                since: 'since@core',
                byname: 'byname@core',
                students: 'students@core',
                firstname: 'firstname@core',
                lastname: 'lastname@core',
                email: 'email@core',
                'enrol_student_question': 'line_enrol_student_question',
                'unenrol_student_question': 'line_unenrol_student_question',

            }

        });

        /* **********************************
         *                                  *
         * Treestudyplan Viewer 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);
        }

        // Create new eventbus for interaction between item components
        const ItemEventBus = new Vue();

        /*
        // Add event listener for the edit mode event so we can react to it, or at the very least ignore it
        document.addEventListener(editSwEventTypes.editModeSet,(e) => {
            e.preventDefault();
            ItemEventBus.$emit('editModeSet',e.detail.editMode);
        });
        */

        Vue.component('r-progress-circle', {
            props: {
                value: {
                    type: Number,
                },
                max: {
                    type: Number,
                    'default': 100,
                },
                min: {
                    type: Number,
                    'default': 0,
                },
                stroke: {
                    type: Number,
                    'default': 0.2,
                },
                bgopacity: {
                    type: Number,
                    'default': 0.2,
                },
                title: {
                    type: String,
                    'default': "",
                },
                icon: {
                    type: String,
                }

            },
            data() {
                return {
                    selectedstudyplan: null,
                };
            },
            computed: {
                range() {
                    return this.max - this.min;
                },
                fraction() {
                    if (this.max - this.min == 0) {
                        return 0;
                        // 0 size is always empty :)
                    } else {
                        return (this.value - this.min) / (this.max - this.min);
                    }
                },
                radius() {
                    return 50 - (50 * this.stroke);
                },
                arcpath() {
                    let fraction = 0;
                    const r = 50 - (50 * this.stroke);
                    if (this.max - this.min != 0) {
                        fraction = (this.value - this.min) / (this.max - this.min);
                    }

                    const Δ = fraction * 2 * π;
                    return svgarcpath([50, 50], [r, r], [0, Δ], 1.5 * π);
                },
            },
            methods: {
            },
            template: `
            <div style="display: inline-block; width: 1em; height: 1em; position:relative; padding: 0;">
                <svg width="1em" height="1em" viewBox="0 0 100 100"
                     style="position: absolute;top: 0;left: 0;">
                    <title>{{title}}</title>
                    <circle v-if="fraction >= 1.0" cx="50" cy="50" :r="radius"
                        :style="'opacity: 1; stroke-width: '+ (stroke*100)+'; stroke: currentcolor; fill: none;'"/>
                    <g v-else>
                        <circle cx="50" cy="50" :r="radius"
                            :style="'opacity: ' + bgopacity + ';stroke-width: '+ (stroke*100)+'; stroke: currentcolor; fill: none;'"/>
                        <path :d="arcpath"
                            :style="'stroke-width: ' + (stroke*100) +'; stroke: currentcolor; fill: none;'"/>
                    </g>
                </svg>
                <i v-if='icon' :class="'fa fa-'+icon"
                    style=" position: absolute; top: 0.42em; left: 0.43em; text-align:center; display: inline-block;
                            width:1em; font-size: 0.55em; "
                ></i>
            </div>
            `,
        });


        Vue.component('r-report', {
            props: {
                invitekey: {
                    type: String,
                    default() {
                        return null;
                    },
                },
                userid: {
                    type: Number,
                    default() {
                        return 0;
                    },
                },
                type: {
                    type: String,
                    default() {
                        return "own";
                    },
                },
            },
            data() {
                return {
                    text: strings.report,
                    studyplans: {
                        past: [],
                        present: [],
                        future: [],
                    },

                    selectedstudyplan: null,
                    loadingstudyplan: false,
                    loading: true,
                };
            },
            computed: {
                teachermode() {
                    return (this.type == "teaching");
                },
                guestmode() {
                    return (this.type == "invited");
                },
                verifiedType() {
                    if (!["invited", "other", "teaching", "own"].includes(this.type)) {
                        return "own";
                    } else {
                        return this.type;
                    }
                },
                studyplancount() {
                    return this.studyplans.past.length + this.studyplans.present.length + this.studyplans.future.length;
                }
            },

            mounted() {
                this.loadStudyplans();
                addBrowserButtonEvent(this.backPressed);
            },

            methods: {
                backPressed() {
                    debug.log("Back button pressed");
                    if (this.selectedstudyplan) {
                        debug.log("Closing studyplan");
                        this.deselectStudyplan();
                    }
                },
                callArgs(o) {
                    const args = {};
                    if (typeof o == 'object' && !Array.isArray(o) && o !== null) {
                        objCopy(args, o);
                    }

                    if (this.verifiedType == "invited") {
                        args.invitekey = this.invitekey;
                    } else if (this.verifiedType == "other") {
                        args.userid = this.userid;
                    }
                    return args;
                },
                loadStudyplans() {
                    const self = this;
                    this.loading = true;

                    call([{
                        methodname: `local_treestudyplan_list_${this.verifiedType}_studyplans`,
                        args: this.callArgs(),
                    }])[0].then(function(response) {
                        console.info("Loaded: plans", response);
                        const plans = {future: [], present: [], past: []};

                        for (const ix in response) {
                            const plan = response[ix];
                            const timing = studyplanTiming(plan);
                            plans[timing].push(plan);
                        }

                        for (const ix in plans) {
                            plans[ix].sort((a, b) => {
                                let t = new Date(b.startdate).getTime() - new Date(a.startdate).getTime();
                                if (t == 0) {
                                    // Sort by name if timing is equal
                                    t = a.name.localeCompare(b.name);
                                }
                                return t;
                            });
                        }

                        self.studyplans = plans;
                        self.loading = false;

                        // Load studyplan from hash if applicable
                        const hash = window.location.hash.replace('#', '');
                        const parts = hash.split("-");

                        if (!!parts && parts.length > 0) {
                            for (const k in self.studyplans) {
                                const list = self.studyplans[k];
                                for (const idx in list) {
                                    const plan = list[idx];
                                    if (plan.id == parts[0] && !plan.suspended) {
                                        self.selectStudyplan(plan);
                                        return;
                                    }
                                }
                            }
                        }

                        // If there is but a single studyplan, select it anyway, even if it is not current...
                        if (this.studyplancount == 1) {
                            if (self.studyplans.present.length > 0) {
                                // Directly show the current study plan if it's the only current one
                                const plan = self.studyplans.present[0];
                                if (!plan.suspended) {
                                    self.selectStudyplan(plan);
                                }
                            } else if (self.studyplans.future.lengh > 0) {
                                const plan = self.studyplans.future[0];
                                if (!plan.suspended) {
                                    self.selectStudyplan(plan);
                                }
                            } else {
                                const plan = self.studyplans.past[0];
                                if (!plan.suspended) {
                                    self.selectStudyplan(plan);
                                }
                            }
                        }
                        return;
                    }).catch(notification.exception);
                },
                selectStudyplan(plan) {
                    const self = this;
                    this.loadingstudyplan = true;
                    call([{
                        methodname: `local_treestudyplan_get_${this.verifiedType}_studyplan`,
                        args: this.callArgs({
                            studyplanid: plan.id,
                        }),
                    }])[0].then(function(response) {
                        self.selectedstudyplan = processStudyplan(response);
                        self.loadingstudyplan = false;
                        window.location.hash = self.selectedstudyplan.id;
                        return;
                    }).catch(notification.exception);
                },
                deselectStudyplan() {
                    this.selectedstudyplan = null;
                    this.loadStudyplans(); // Reload the list of studyplans.
                    window.location.hash = '';
                }
            },
            template: `
            <div class='t-studyplan-container r-report-tabs'>
                <div v-if='loading' class="vue-loader spinner-border text-primary" role="status"></div>
                <template v-else>
                    <div v-if='!loadingstudyplan && selectedstudyplan'>
                        <div class="mb-2">
                            <a href='#' @click='deselectStudyplan'><h5 class="d-inline"><i class="fa fa-chevron-left"></i> {{ text.back }}</h5></a>
                            <h4 class="d-inline ml-3">{{ selectedstudyplan.name}}
                            <s-studyplan-details
                                class="mb-2"
                                size="sm"
                                v-model="selectedstudyplan"
                                v-if="selectedstudyplan.description"
                            ><i class='fa fa-info-circle'></i></s-studyplan-details></h4>
                        </div>
                        <r-studyplan
                            v-model='selectedstudyplan'
                            :guestmode='guestmode'
                            :teachermode='teachermode'
                        ></r-studyplan>
                    </div>
                    <div v-else-if='loadingstudyplan' class="vue-loader spinner-border text-primary" role="status">
                        <span class="sr-only">{{ text.loading }}</span>
                    </div>
                    <div v-else class='t-studyplan-notselected'>
                        <template v-for="timing in ['present', 'past', 'future']">
                            <template v-if="studyplans[timing].length > 0">
                                <h4>{{ text["studyplan_"+timing]}}:</h4>
                                <b-card-group deck>
                                    <s-studyplan-card
                                        v-for='(studyplan, planindex) in studyplans[timing]'
                                        :key='studyplan.id'
                                        v-model='studyplans[timing][planindex]'
                                        open
                                        @open='selectStudyplan(studyplan)'
                                        ></s-studyplan-card>
                                </b-card-group>
                            </template>
                        </template>
                    </div>
                </template>
            </div>
            `,
        });

        Vue.component('r-studyplan', {
            props: {
                value: {
                    type: Object,
                },
                guestmode: {
                    type: Boolean,
                    'default': false,
                },
                teachermode: {
                    type: Boolean,
                    'default': false,
                },
                coaching: {
                    type: Boolean,
                    'default': false,
                },
            },
            data() {
                return {
                    selectedpageindex: -1,
                    text: strings.pageinfo,
                };
            },
            computed: {

                selectedpage() {
                    return this.value.pages[this.selectedpageindex];
                },
                startpageindex() {
                    let startpageindex = 0;
                    let firststart = null;
                    for (const ix in this.value.pages) {
                        const page = this.value.pages[ix];
                        if (studyplanPageTiming(page) == "present") {
                            const s = new Date(page.startdate);
                            if ((!firststart) || firststart > s) {
                                startpageindex = ix;
                                firststart = s;
                            }
                        }
                    }
                    return startpageindex;
                },
                wwwroot() {
                    return Config.wwwroot;
                },
            },
            methods: {
                pageduration(page) {
                    return formatDate(page.startdate, false) + " - " + formatDate(page.enddate, false);
                },
                columns(page) {
                    return 1 + (page.periods * 2);
                },
                columnsStylerule(page) {
                    // Uses css variables, so width for slots and filters can be configured in css
                    let s = "grid-template-columns: var(--studyplan-filter-width)"; // Ese css variable here
                    for (let i = 0; i < page.periods; i++) {
                        s += " var(--studyplan-course-width) var(--studyplan-filter-width)";
                    }
                    return s + ";";
                },
                countLineLayers(line, page) {
                    let maxLayer = -1;
                    for (let i = 0; i <= page.periods; i++) {
                        // Determine the amount of used layers in a studyline slot
                        for (const ix in line.slots[i].courses) {
                            const item = line.slots[i].courses[ix];
                            if (item.layer > maxLayer) {
                                maxLayer = item.layer;
                            }
                        }
                        for (const ix in line.slots[i].filters) {
                            const item = line.slots[i].filters[ix];
                            if (item.layer > maxLayer) {
                                maxLayer = item.layer;
                            }
                        }
                    }
                    return (maxLayer >= 0) ? (maxLayer + 1) : 1;
                },
                showslot(page, line, index, layeridx, type) {
                    // Check if the slot should be hidden because a previous slot has an item with a span
                    // so big that it hides this slot
                    const forGradable = (type == 'gradable') ? true : false;
                    const periods = page.periods;
                    let show = true;
                    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];
                                if (item.layer == layeridx) {
                                    if (forGradable) {
                                        if (i > 0 && (item.span - i) > 0) {
                                            show = false;
                                        }
                                    } else {
                                        if ((item.span - i) > 1) {
                                            show = false;
                                        }
                                    }
                                }
                            }
                        }
                    }

                    return show;
                },
                selectedpageChanged(/* Params: newTabIndex, prevTabIndex */) {

                    ItemEventBus.$emit('redrawLines', null);
                    scrollCurrentIntoView(this.selectedpage.id);
                },
            },
            mounted() {
                this.$root.$emit('redrawLines');
            },
            updated() {

                scrollCurrentIntoView(this.selectedpage.id);
                ItemEventBus.$emit('lineHeightChange', null);
                this.$root.$emit('redrawLines');
                ItemEventBus.$emit('redrawLines');
            },
            template: `
            <div>
            <b-card no-body>
                <b-tabs
                    v-model='selectedpageindex'
                    @activate-tab='selectedpageChanged'
                    content-class="mt-1">
                    <b-tab
                        v-for="(page,pageindex) in value.pages"
                        :active="(pageindex == startpageindex)"
                        :key="page.id"
                        :title-item-class="'s-studyplanpage-tab '+ page.timing"
                        ><template #title>
                            <span :id='"page-tab-"+page.id'><span v-html="page.display.shortname"></span></span>
                            <b-tooltip :target='"page-tab-"+page.id'><span v-html="page.display.fullname"></span></b-tooltip>
                            <a href="#" v-b-modal="'studyplanpage-info-'+page.id" class='text-info'
                             v-if='pageindex == selectedpageindex'
                            ><i class='fa fa-info-circle'></i></a>
                        </template>

                        <b-modal
                            :id="'studyplanpage-info-'+page.id"
                            scrollable
                            ok-only
                            >
                            <template #modal-title>
                                <span v-html="page.display.fullname"></span>
                            </template>
                            <b-container>
                                <b-row>
                                    <b-col cols="4"><b>{{ text.shortname}}</b></b-col>
                                    <b-col cols="8">
                                        <span v-html="page.display.shortname"></span>
                                    </b-col>
                                </b-row>
                                <b-row v-if="!page.timeless">
                                    <b-col cols="4"><b>{{ text.duration}}</b></b-col>
                                    <b-col cols="8">
                                        {{ pageduration(page) }}
                                    </b-col>
                                </b-row>
                                <b-row v-if="page.description">
                                    <b-col cols="12"><b>{{ text.description}}</b></b-col>
                                </b-row>
                                <b-row v-if="page.description">
                                    <b-col cols="12">
                                        <span v-html="page.display.description"></span>
                                    </b-col>
                                </b-row>
                            </b-container>
                        </b-modal>
                        <div v-if="page.studylines.length > 0" class='r-studyplan-content'>
                            <!-- First paint the headings-->
                            <div class='r-studyplan-headings'
                                ><s-studyline-header-heading :identifier="Number(page.id)"
                                    ><a v-if="teachermode && selectedpage && !coaching" class="ml-2"
                                        :href="wwwroot+'/local/treestudyplan/result-overview.php?page='+selectedpage.id"

                                        target='_blank'><i class='fa fa-list-ul'></i>&nbsp;{{text.overview}}</a
                                ></s-studyline-header-heading>
                                <r-studyline-heading  v-for="(line,lineindex) in page.studylines"
                                    :key="line.id"
                                    :teachermode="teachermode"
                                    :guestmode="guestmode"
                                    v-model="page.studylines[lineindex]"
                                    :layers='countLineLayers(line,page)+1'
                                    :studentid="value.userid"
                                    @enrolupdate="page.studylines[lineindex].enrol = $event"
                                    :class=" 't-studyline' + ((lineindex%2==0)?' odd ' :' even ' )
                                            + ((lineindex==0)?' first ':' ')
                                            + ((lineindex==page.studylines.length-1)?' last ':' ')"
                                ></r-studyline-heading
                            ></div>
                            <!-- Next, paint all the cells in the scrollable -->
                            <div class="r-studyplan-scrollable" >
                                <div class="r-studyplan-timeline" :style="columnsStylerule(page)">
                                <!-- add period information -->
                                <template v-for="(n,index) in (page.periods+1)">
                                    <s-studyline-header-period
                                        v-if="index > 0"
                                        v-model="page.perioddesc[index-1]"
                                        :identifier="Number(page.id)"
                                        ><a v-if="teachermode && selectedpage && !coaching"
                                            v-b-tooltip.hover
                                            :href="wwwroot+'/local/treestudyplan/result-overview.php?page='+selectedpage.id
                                                   +'&firstperiod='+index+'&lastperiod='+index"
                                            target='_blank'
                                            :title="text.overviewperiod"><i class='fa fa-list-ul'></i></a
                                    ></s-studyline-header-period>
                                    <div class="s-studyline-header-filter"></div>
                                </template>

                                <!-- Line by line add the items -->
                                <!-- The grid layout handles putting it in rows and columns -->
                                <template v-for="(line,lineindex) in page.studylines"
                                    ><template v-for="(layernr,layeridx) in countLineLayers(line,page)"
                                        ><template v-for="(n,index) in (page.periods+1)"
                                            ><r-studyline-slot
                                                v-if="index > 0 && showslot(page,line, index, layeridx, 'gradable')"
                                                type='gradable'
                                                v-model="line.slots[index].courses"
                                                :key="'c-'+lineindex+'-'+index+'-'+layernr"
                                                :slotindex="index"
                                                :line="line"
                                                :plan="value"
                                                :page="page"
                                                :period="page.perioddesc[index-1]"
                                                :guestmode='guestmode'
                                                :teachermode='teachermode'
                                                :layer="layeridx"
                                                :class="'t-studyline ' + ((lineindex%2==0)?' odd ':' even ')
                                                        + ((lineindex==0 && layernr==1)?' first ':' ')
                                                        + ((lineindex==page.studylines.length-1)?' last ':' ')
                                                        + ((layernr == countLineLayers(line,page))?' lastlyr ':' ')"
                                            ></r-studyline-slot
                                            ><r-studyline-slot
                                                v-if="showslot(page,line, index, layeridx, 'filter')"
                                                type='filter'
                                                v-model="line.slots[index].filters"
                                                :teachermode='teachermode'
                                                :key="'f-'+lineindex+'-'+index+'-'+layernr"
                                                :slotindex="index"
                                                :line="line"
                                                :plan="value"
                                                :page="page"
                                                :layer="layeridx"
                                                :class="'t-studyline ' + ((lineindex%2==0)?' odd ':' even ')
                                                        + ((lineindex==0 && layernr==1)?' first ':'')
                                                        + ((lineindex==page.studylines.length-1)?' last ':' ')
                                                        + ((index==page.periods)?' rightmost':'')
                                                        + ((layernr == countLineLayers(line,page))?' lastlyr ':' ')"
                                                >
                                            </r-studyline-slot
                                        ></template
                                    ></template
                                ></template
                                ></div
                            ></div
                        ></div>
                    </b-tab>
                </b-tabs>
            </b-card>
            </div>
            `,
        });

        /*
        * R-STUDYLINE-HEADER
        */
        Vue.component('r-studyline-heading', {
            props: {
                value: {
                    type: Object, // Studyline
                    default() {
                        return {};
                    },
                },
                guestmode: {
                    type: Boolean,
                    'default': false,
                },
                teachermode: {
                    type: Boolean,
                    'default': false,
                },
                layers: {
                    type: Number,
                    'default': 1,
                },
                studentid: {
                    type: Number,
                },
            },
            data() {
                return {
                    layerHeights: {},
                    text: strings.lineheader,
                    students: null,
                    canUnenrol: false,
                    sorting: {
                        asc: false,
                        field: 'enrolled_time'
                    }
                };
            },
            created() {
                    // Listener for the signal that a new connection was made and needs to be drawn
                    // Sent by the incoming item  - By convention, outgoing items are responsible for drawing the lines
                    ItemEventBus.$on('lineHeightChange', this.onLineHeightChange);
            },
            computed: {
                enrollable() {
                    return this.value.enrol.enrollable > ENROLLABLE_NONE;
                },
                enrollableSelf() {
                    return [ENROLLABLE_SELF, ENROLLABLE_SELF_ROLE].includes(this.value.enrol.enrollable);
                },
                enrollableRole() {
                    return [ENROLLABLE_ROLE, ENROLLABLE_SELF_ROLE].includes(this.value.enrol.enrollable);
                },
                enrolled() {
                    return this.value.enrol.enrolled ? true : false;
                },
                canEnrol() {
                    return this.value.enrol.can_enrol ? true : false;
                },
                enrolQuestion() {
                    return this.text.enrol_question.replace('{$a}', this.value.name);
                },
                enrolledIn() {
                    return this.text.enrolled_in.replace('{$a}', this.value.name);
                },
                by() {
                    return this.text.byname.replace('{$a}', '');
                },
                enrolldate() {
                    return formatDatetime(this.value.enrol.enrolled_time);
                },
                sortedStudents() {
                    const self = this;
                    const list = Array.isArray(this.students) ? this.students : [];
                    list.sort((a, b) => {
                        let d = a;
                        let e = b;
                        if (!self.sorting.asc) {
                            d = b;
                            e = a;
                        }
                        let df = d;
                        let ef = e;
                        const field = self.sorting.field;
                        if (d.user && d.user.hasOwnProperty(field)) {
                            df = d.user;
                            ef = e.user;
                        } else if (d.enrol && d.enrol.hasOwnProperty(field)) {
                            df = d.enrol;
                            ef = e.enrol;
                        }
                        if (field == 'enrolled') {
                            return ((df.enrolled) ? 1 : 0) - ((ef.enrolled) ? 1 : 0);
                        } else if (field == "enrolled_time") {
                            const dvalue = (df[field] && d.enrol.enrolled) ? df[field] : 0;
                            const evalue = (ef[field] && e.enrol.enrolled) ? ef[field] : 0;
                            return dvalue - evalue;
                        } else {
                            return String(df[this.sorting.field]).localeCompare(String(ef[this.sorting.field]));
                        }

                    });
                    return list;
                }
            },
            methods: {
                formatDatetime,
                onLineHeightChange(lineid) {
                    // All layers for this line have the first slot send an update message on layer height change.
                    // When one of those updates is received, record the height and recalculate the total height of the
                    // header
                    if (this.$refs.mainEl && (lineid == this.value.id || lineid === null)) {
                        const items = document.querySelectorAll(
                            `.r-studyline-slot-0[data-studyline='${this.value.id}']`);
                        // Determine the height of all the lines and add them up.
                        let heightSum = 0;
                        items.forEach((el) => {
                            // Function getBoundingClientRect() Gets the actual fractional height instead of rounded to integer pixels
                            const r = el.getBoundingClientRect();
                            const height = r.height;
                            heightSum += height;
                        });

                        const heightStyle = `${heightSum}px`;
                        this.$refs.mainEl.style.height = heightStyle;
                    }
                },
                enrolSelf() {
                    const self = this;
                    call([{
                        methodname: 'local_treestudyplan_line_enrol_self',
                        args: {
                            id: self.value.id,
                        },
                    }])[0].then(function(response) {
                        self.$emit('enrolupdate', response);
                        return;
                    }).catch(notification.exception);
                },

                enrolStudent(student) {
                    const self = this;
                    const user = student.user;
                    let question = self.text.enrol_student_question.replace('{$a}', `${user.firstname} ${user.lastname}`);
                    const options = {
                        okTitle: self.text.yes,
                        cancelTitle: self.text.no,
                        okVariant: "success",
                        cancelVariant: "danger",
                    };
                    this.$bvModal.msgBoxConfirm(question, options).then(reply => {
                        if (reply) {
                            call([{
                                methodname: 'local_treestudyplan_line_enrol_students',
                                args: {
                                    id: self.value.id,
                                    users: [user.id],
                                },
                            }])[0].then(function(response) {
                                student.enrol = response[0].enrol;
                                return;
                            }).catch(notification.exception);
                        }
                        return;
                    }).catch(notification.exception);
                },
                unenrolStudent(student) {
                    const self = this;
                    const user = student.user;
                    let question = self.text.unenrol_student_question.replace('{$a}', `${user.firstname} ${user.lastname}`);
                    const options = {
                        okTitle: self.text.yes,
                        cancelTitle: self.text.no,
                        okVariant: "success",
                        cancelVariant: "danger",
                    };
                    this.$bvModal.msgBoxConfirm(question, options).then(reply => {
                        if (reply) {
                            call([{
                                methodname: 'local_treestudyplan_line_unenrol_students',
                                args: {
                                    id: self.value.id,
                                    users: [user.id],
                                },
                            }])[0].then(function(response) {
                                student.enrol = response[0].enrol;
                                return;
                            }).catch(notification.exception);
                        }
                        return;
                    }).catch(notification.exception);
                },
                loadStudents() {
                    const self = this;
                    self.students = null;
                    self.canUnenrol = false;
                    call([{
                        methodname: 'local_treestudyplan_list_line_enrolled_students',
                        args: {
                            id: self.value.id,
                        },
                    }])[0].then(function(response) {
                        self.students = response.userinfo;
                        self.canUnenrol = response.can_unenrol;
                        return;
                    }).catch(notification.exception);
                },
                toggleSort(header) {
                    if (this.sorting.field == header) {
                        this.sorting.asc = !this.sorting.asc;
                    } else {
                        this.sorting.field = header;
                        this.sorting.asc = true;
                    }
                },

            },
            template: `
            <div class="r-studyline r-studyline-heading "
                :data-studyline="value.id" ref="mainEl"
                ><div class="r-studyline-handle" :style="'background-color: ' + value.color"></div>
                <div class="r-studyline-title"><div>
                    <abbr v-b-tooltip.hover :title="value.name">{{ value.shortname }}</abbr>
                    <template v-if="enrollable">
                        <template v-if="teachermode">
                            <br>
                            <a  v-if="!canEnrol"
                                href='#' @click.prevent=""
                                v-b-modal="'r-enrollments-'+value.id"
                                :title="text.cannot_enrol"
                                ><i class='fa fa-lock text-danger'></i>&nbsp;{{text.enrollments}}</a>
                            <a  v-else
                                href='#' @click.prevent=""
                                v-b-modal="'r-enrollments-'+value.id"
                                :title="text.can_enrol"
                                ><i class='fa fa-unlock-alt text-success'></i>&nbsp;{{text.enrollments}}</a>
                            <b-modal
                                :id="'r-enrollments-'+value.id"
                                @show="loadStudents"
                                size="xl"
                                ok-only
                                scrollable
                                :title="value.name"
                                >
                                <table class="r-line-enroll-userlist">
                                <thead>
                                    <th><a href='#' @click.prevent="toggleSort('firstname')"
                                        >{{text.firstname}}</a>
                                        <i v-if="sorting.field=='firstname' && sorting.asc" class='fa fa-sort-asc fa-fw'></i
                                        ><i v-else-if="sorting.field=='firstname' && !sorting.asc" class='fa fa-sort-desc fa-fw'></i>
                                    </th>
                                    <th><a href='#' @click.prevent="toggleSort('lastname')"
                                        >{{text.lastname}}</a>
                                        <i v-if="sorting.field=='lastname' && sorting.asc" class='fa fa-sort-asc fa-fw'></i
                                        ><i v-else-if="sorting.field=='lastname' && !sorting.asc" class='fa fa-sort-desc fa-fw'></i>
                                    </th>
                                    <th><a href='#' @click.prevent="toggleSort('enrolled')"
                                        >{{text.enrolled}}</a>
                                        <i v-if="sorting.field=='enrolled' && sorting.asc" class='fa fa-sort-asc fa-fw'></i
                                        ><i v-else-if="sorting.field=='enrolled' && !sorting.asc" class='fa fa-sort-desc fa-fw'></i>
                                    </th>
                                    <th><a href='#' @click.prevent="toggleSort('enrolled_time')"
                                        >{{text.since}}</a>
                                        <i v-if="sorting.field=='enrolled_time' && sorting.asc" class='fa fa-sort-asc fa-fw'></i
                                        ><i v-else-if="sorting.field=='enrolled_time' && !sorting.asc" class='fa fa-sort-desc fa-fw'></i>
                                    </th>
                                    <th><a href='#' @click.prevent="toggleSort('enrolled_by')"
                                        >{{by}}</a>
                                        <i v-if="sorting.field=='enrolled_by' && sorting.asc" class='fa fa-sort-asc fa-fw'></i
                                        ><i v-else-if="sorting.field=='enrolled_by' && !sorting.asc" class='fa fa-sort-desc fa-fw'></i>
                                    </th>
                                    <th v-if="canEnrol || canUnenrol">{{text.enrol}}</th>
                                </thead>
                                <tbody>
                                    <tr v-if="students == null"><td
                                        :colspan="4+(canEnrol?1:0)">
                                        <div class="spinner-border spinner-border-sm text-info" role="status"></div>
                                    </td></tr>
                                    <template v-else>
                                    <tr v-for="student in sortedStudents">
                                        <td>{{student.user.firstname}}</td>
                                        <td>{{student.user.lastname}}</td>
                                        <td><span v-if="student.enrol.enrolled" class="text-success">{{text.enrolled}}</span
                                            ><span v-else class="text-danger">{{text.notenrolled}}</span></td>
                                        <td><span v-if="student.enrol.enrolled"
                                            >{{formatDatetime(student.enrol.enrolled_time)}}</span></td>
                                        <td><span v-if="student.enrol.enrolled"
                                            >{{student.enrol.enrolled_by}}</span></td>
                                        <td ><b-button v-if="!student.enrol.enrolled && (canEnrol || canUnenrol)"
                                                variant="success"
                                                size="sm"
                                                @click="enrolStudent(student)"
                                            >{{text.enrol}}</b-button
                                            ><b-button v-else-if="student.enrol.enrolled && canUnenrol"
                                                variant="danger"
                                                size="sm"
                                                @click="unenrolStudent(student)"
                                            >{{text.unenrol}}</b-button>
                                        </td>
                                    </tr>
                                    </template>
                                </tbody>
                                </table>
                            </b-modal>
                        </template>
                        <template v-else-if="value.enrol.selfview">
                            <br>
                            <a  v-if="!enrolled && !canEnrol"
                                @click.prevent=""
                                href='#'
                                v-b-tooltip.focus
                                :title="text.cannot_enrol"
                                ><fittext maxsize="12pt"><i class='fa fa-lock text-danger'></i>&nbsp;{{text.cannot_enrol}}</fittext></a>
                            <a  v-else-if="!enrolled && canEnrol"
                                @click.prevent=""
                                href='#'
                                v-b-modal="'r-enrol-'+value.id"
                                :title="text.can_enrol"
                                ><fittext maxsize="12pt"><i class='fa fa-unlock-alt text-info'></i>&nbsp;{{text.enrol}}</fittext></a>
                            <a  v-else-if="enrolled"
                                @click.prevent=""
                                href='#'
                                v-b-modal="'r-enrollment-'+value.id"
                                :title="text.enrolled"
                                ><fittext maxsize="12pt"><i class='fa fa-unlock text-success'></i>&nbsp;{{text.enrolled}}</fittext></a>
                            <b-modal
                                :id="'r-enrol-'+value.id"
                                :title="text.confirm"
                                :ok-title="text.yes"
                                :cancel-title="text.no"
                                ok-variant="success"
                                cancel-variant="danger"
                                @ok="enrolSelf"
                                >
                                <p>{{enrolQuestion}}</p>
                            </b-modal>
                            <b-modal
                                :id="'r-enrollment-'+value.id"
                                ok-only
                                :title="text.enrollment"
                                >
                                <p>{{enrolledIn}}<p>
                                <p><b>{{text.since}}</b> {{enrolldate}}<br>
                                <b>{{by}}</b> {{this.value.enrol.enrolled_by}}</p>
                            </b-modal>
                        </template>
                        <template v-else>
                            &nbsp;
                            <i v-if="!enrolled" class='fa fa-lock text-danger'></i>
                            <i v-else class='fa fa-unlock text-success'></i>
                        </template>
                    </template>
                </div></div>
            </div>
            `,
        });

        Vue.component('r-studyline-slot', {
            props: {
                value: {
                    type: Array, // Item to display
                    default() {
                        return [];
                    },
                },
                type: {
                    type: String,
                    'default': 'gradable',
                },
                slotindex: {
                    type: Number,
                    'default': 0,
                },
                line: {
                    type: Object,
                    default() {
                        return null;
                    },
                },
                layer: {
                    type: Number,
                },
                plan: {
                    type: Object,
                    default() {
                        return null;
                    },
                },
                page: {
                    type: Object,
                    default() {
                        return null;
                    },
                },
                guestmode: {
                    type: Boolean,
                    'default': false,
                },
                teachermode: {
                    type: Boolean,
                    'default': false,
                },
                period: {
                    type: Object,
                    default() {
                        return null;
                    },
                }
            },
            mounted() {
                const self = this;
                if (self.type == "gradable" && self.slotindex == 1) {
                    self.resizeListener = new ResizeObserver(() => {
                        if (self.$refs.sizeElement) {
                            const height = self.$refs.sizeElement.getBoundingClientRect().height;
                            ItemEventBus.$emit('lineHeightChange', self.line.id, self.layer, height);
                        }
                    }).observe(self.$refs.sizeElement);
                }
            },
            computed: {
                item() {
                    for (const ix in this.value) {
                        const itm = this.value[ix];
                        if (itm.layer == this.layer) {
                            return itm;
                        }
                    }
                    return null;
                },
                spanCss() {
                    if (this.item && this.item.span > 1) {
                        const span = (2 * this.item.span) - 1;
                        return `width: 100%; grid-column: span ${span};`;
                    } else {
                        return "";
                    }
                },
                cloud() {
                    const enrol = this.line.enrol;
                    return (!this.teachermode) && (enrol.enrollable > 0) && (!enrol.enrolled);
                }
            },
            data() {
                return {
                };
            },
            methods: {

            },
            template: `
            <div :class="  'r-studyline-slot ' + type + ' '
                         + 'r-studyline-slot-' + slotindex + ' '
                         + 'periodcount-' + page.periods + ' '
                         + ((slotindex==0)?'r-studyline-firstcolumn ':' ')"
                :data-studyline="line.id" ref="sizeElement"
                :style='spanCss'
                ><div class="r-slot-item" v-if="item"
                    ><r-item
                        v-model="item"
                        :cloud="cloud"
                        :plan="plan"
                        :guestmode='guestmode'
                        :teachermode='teachermode'></r-item
                    ></div
                ></r-item
            ></div>
            `,
        });


        Vue.component('r-item', {
            props: {
                value: {
                    type: Object,
                    default() {
                        return null;
                    },
                },
                plan: {
                    type: Object,
                    default() {
                        return null;
                    }
                },
                guestmode: {
                    type: Boolean,
                    'default': false,
                },
                teachermode: {
                    type: Boolean,
                    'default': false,
                },
                cloud: {
                    type: Boolean,
                    'default': false,
                }
            },
            data() {
                return {
                    lines: [],
                };
            },
            methods: {
                lineColor() {
                    if (this.teachermode) {
                        return "var(--color-incomplete)";
                    } else if (this.cloud) {
                        return "#ccc";
                    } else {
                        switch (this.value.completion) {
                            default: // "incomplete"
                                return "var(--color-incomplete)";
                            case "failed":
                                return "var(--color-fail)";
                            case "progress":
                                return "var(--color-progress)";
                            case "completed":
                                return "var(--color-pass)";
                            case "good":
                                return "var(--color-good)";
                            case "excellent":
                                return "var(--color-excellent)";
                        }
                    }
                },
                redrawLine(conn) {
                    let lineColor = this.lineColor();
                    // Draw new line...
                    let start = document.getElementById('studyitem-' + conn.from_id);
                    let end = document.getElementById('studyitem-' + conn.to_id);

                    // Delete old line
                    if (this.lines[conn.to_id]) {
                        this.lines[conn.to_id].remove();
                        delete this.lines[conn.to_id];
                    }

                    if (start !== null && end !== null && isVisible(start) && isVisible(end)) {
                        this.lines[conn.to_id] = new SimpleLine(start, end, {
                            color: lineColor,
                            gravity: {
                                start: LINE_GRAVITY,
                                end: LINE_GRAVITY,
                            },
                            'class': (this.cloud ? "r-dummy-line" : ""),
                        });
                    }
                },
                redrawLines() {
                    // Clean all old lines
                    for (let ix in this.lines) {
                        let lineinfo = this.lines[ix];
                        if (lineinfo && lineinfo.line) {
                            lineinfo.line.remove();
                            lineinfo.line = undefined;
                        }
                    }

                    // Create new lines
                    for (let i in this.value.connections.out) {
                        let conn = this.value.connections.out[i];
                        this.redrawLine(conn);
                    }
                },
                onWindowResize() {
                    this.redrawLines();
                },
                onRedrawLines() {
                    this.redrawLines();
                },
                removeLine(conn) {
                    if (this.lines[conn.to_id]) {
                        this.lines[conn.to_id].remove();
                        delete this.lines[conn.to_id];
                    }
                },

            },
            computed: {
                hasConnectionsOut() {
                    return !(["finish"].includes(this.value.type));
                },
                hasConnectionsIn() {
                    return !(["start"].includes(this.value.type));
                },
                hasContext() {
                    return ['start', 'junction', 'finish'].includes(this.value.type);
                }
            },
            created() {
                ItemEventBus.$on('redrawLines', this.onRedrawLines);
            },
            mounted() {
                // 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];
                    this.removeLine(conn);
                }
                // Remove resize event listener
                window.removeEventListener('resize', this.onWindowResize);
                ItemEventBus.$off('redrawLines', this.onRedrawLines);
            },
            updated() {
                if (!this.dummy) {
                    this.redrawLines();
                }
            },
            template: `
            <div class="r-item-base" :id="'studyitem-'+value.id" :data-x='value.type'>
                <template v-if="cloud">
                    <r-item-dummy-course v-if="value.type == 'course'"></r-item-dummy-course>
                    <r-item-dummy-badge v-else-if="value.type == 'badge'"></r-item-dummy-badge>
                    <r-item-dummy-filter v-else></r-item-dummy-filter>
                </template>
                <template v-else>
                <r-item-course        v-if="value.type == 'course' && !teachermode" :plan="plan"
                    v-model="value" :guestmode="guestmode" :teachermode="teachermode" ></r-item-course>
                <r-item-teachercourse v-if="value.type == 'course' && teachermode" :plan="plan"
                    v-model="value" :guestmode="guestmode" :teachermode="teachermode" ></r-item-teachercourse>
                <r-item-junction      v-if="value.type == 'junction'"
                    v-model="value" :guestmode="guestmode" :teachermode="teachermode" ></r-item-junction>
                <r-item-start         v-if="value.type == 'start'"
                    v-model="value" :guestmode="guestmode" :teachermode="teachermode" ></r-item-start>
                <r-item-finish        v-if="value.type == 'finish'"
                    v-model="value" :guestmode="guestmode" :teachermode="teachermode" ></r-item-finish>
                <r-item-badge         v-if="value.type == 'badge'"
                    v-model="value" :guestmode="guestmode" :teachermode="teachermode" ></r-item-badge>
                <r-item-invalid       v-if="value.type == 'invalid' && teachermode"
                    v-model="value" ></r-item-invalid>
                </template>
            </div>
            `,
        });

        Vue.component('r-item-invalid', {
            props: {
                'value': {
                    type: Object,
                    default() {
                        return null;
                    },
                },
            },
            data() {
                return {
                    text: strings.invalid,
                };
            },
            methods: {
            },
            template: `
            <div class="r-item-invalid">
            <b-card no-body class="r-item-invalid">
                <b-row no-gutters>
                    <b-col md="1">
                        <span class="r-timing-indicator timing-invalid"></span>
                    </b-col>
                    <b-col md="11">
                        <b-card-body class="align-items-center">
                            <i class="fa fa-exclamation"></i> {{ text.error }}
                        </b-card-body>
                    </b-col>
                </b-row>
            </b-card>
            </div>
            `,
        });

        // TAG: Item Course
        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: strings.course,
                };
            },
            computed: {
                startdate() {
                    return formatDate(this.value.course.startdate);
                },
                enddate() {
                    if (this.value.course.enddate.length > 0) {
                        return formatDate(this.value.course.enddate);
                    } else {
                        return this.text.noenddate;
                    }
                },
                courseprogress() {
                    if (!this.value.course.enrolled) {
                        return 0;
                    } else if (this.value.course.completion) {
                        return (this.value.course.completion.progress / this.value.course.completion.count);
                    } else if (this.value.course.competency) {
                        return (this.value.course.competency.progress / this.value.course.competency.count);
                    } else if (this.value.course.grades) {
                        return (this.gradeprogress(this.value.course.grades) / this.value.course.grades.length);
                    } else {
                        return 0;
                    }
                },
                hasprogressinfo() {
                    if (!this.value.course.enrolled) {
                        return false;
                    } else {
                        return (this.value.course.completion || this.value.course.competency || this.value.course.grades);
                    }
                },
                wwwroot() {
                    return Config.wwwroot;
                }
            },
            methods: {
                completionIcon(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";
                    }
                },
                circleIcon(completion) {
                    switch (completion) {
                        default: // Case "incomplete"
                            return null;
                        case "failed":
                            return "times";
                        case "progress":
                            return "";
                        case "completed":
                            return "check";
                        case "good":
                            return "check";
                        case "excellent":
                            return "check";
                    }
                },
                gradeprogress(grades) {
                    let progress = 0;
                    for (const ix in grades) {
                        const g = grades[ix];
                        if (["completed", "excellent", "good"].includes(g.completion)) {
                            progress++;
                        }
                    }
                    return progress;
                },
            },
            template: `
            <div :class="'r-item-course card completion-'+value.completion">
                <div class='r-item-course-cardwrapper mr-0 ml-0 h-100 '>
                    <div
                        :title="text['coursetiming_'+value.course.timing]"
                        v-b-popover.hover.top="startdate+' - '+enddate"
                        :class="'r-timing-indicator timing-'+value.course.timing"
                    ></div>
                    <div class="r-item-course-title card-body h-100">
                        <fittext maxsize="12pt" minsize="9pt">
                        <a  v-b-modal="'r-item-course-details-'+value.id"
                            :href="(!guestmode)?(wwwroot+'/course/view.php?id='+value.course.id):'#'"
                            @click.prevent.stop=''
                            ><span v-html="value.course.displayname"></span></a>
                        </fittext>
                    </div>
                    <div class="h-100 r-item-course-indicator ">
                        <template v-if='!value.course.enrolled'>
                            <i v-b-popover.top
                                class="r-course-result fa fa-exclamation-triangle t-not-enrolled-alert"
                                :title="text.student_not_tracked"></i>
                        </template>
                        <template v-else-if='hasprogressinfo'>
                            <r-progress-circle v-if='["failed", "progress","incomplete"].includes(value.completion)'
                                :value='courseprogress'
                                :max='1'
                                :min='0'
                                :class="'r-course-result r-completion-'+value.completion"
                                :icon='circleIcon(value.completion)'
                                :title="text['completion_'+value.completion]"
                            ></r-progress-circle>
                            <i v-else v-b-popover.top
                                :class="'r-course-result fa fa-'+completionIcon(value.completion)+
                                        ' r-completion-'+value.completion"
                                :title="text['completion_'+value.completion]"></i>
                        </template>
                        <template v-else>
                            <i v-b-popover.top
                                :class="'r-course-result fa fa-'+completionIcon(value.completion)+
                                        ' r-completion-'+value.completion"
                                :title="text['completion_'+value.completion]"></i>
                        </template>
                    </div>
                </div>
                <b-modal
                    :id="'r-item-course-details-'+value.id"
                    :title="value.course.displayname + ' - ' + value.course.fullname"
                    size="lg"
                    ok-only
                    centered
                    scrollable
                    header-class="r-item-course-header"
                    >
                    <template #modal-header >
                        <div class="r-item-course-header-details">
                            <div>
                                <h1><a :href="(!guestmode)?(wwwroot+'/course/view.php?id='+value.course.id):undefined" target="_blank"
                                    ><i class="fa fa-graduation-cap"></i> <span v-html="value.course.fullname"></span>"</a></h1>
                                 <span v-html="value.course.category.path.join(' / ')"></span>
                            </div>
                            <div class="r-course-detail-header-right">
                                <div class="r-completion-detail-header">
                                    <template v-if='!value.course.enrolled'>
                                        {{text.not_enrolled}}
                                        <i v-b-popover.top
                                            class="fa fa-exclamation-triangle t-not-enrolled-alert"
                                            :title="text.student_not_tracked"></i>
                                    </template>
                                    <template v-else-if='hasprogressinfo && !value.course.showprogressbar'>
                                        {{text['completion_'+value.completion]}}
                                        <r-progress-circle v-if='["failed", "progress","incomplete"].includes(value.completion)'
                                            :value='courseprogress'
                                            :max='1'
                                            :min='0'
                                            :class="'r-progress-circle-popup r-completion-'+value.completion"
                                            :icon='circleIcon(value.completion)'
                                            :title="text['completion_'+value.completion]"
                                        ></r-progress-circle>
                                        <i v-else v-b-popover.top
                                            :class="'fa fa-'+completionIcon(value.completion)+
                                                    ' r-completion-'+value.completion"
                                            :title="text['completion_'+value.completion]"></i>
                                    </template>
                                    <template v-else>
                                        {{text['completion_'+value.completion]}}
                                        <i  :class="'fa fa-'+completionIcon(value.completion)+' r-completion-'+value.completion"
                                            :title="text['completion_'+value.completion]"></i>
                                    </template>
                                </div>
                                <div :class="'r-timing-'+value.course.timing">
                                    {{text['coursetiming_'+value.course.timing]}}<br>
                                    {{ startdate }} - {{ enddate }}
                                </div>
                            </div>
                        </div>
                        <s-progress-bar
                            v-if='value.course.showprogressbar && hasprogressinfo'
                            v-model="courseprogress"
                        ></s-progress-bar>
                    </template>
                    <s-course-extrafields
                        v-if="value.course.extrafields"
                        v-model="value.course.extrafields"
                        position="above"
                        ></s-course-extrafields>
                    <r-item-studentgrades
                        v-if='!!value.course.grades && value.course.grades.length > 0'
                        v-model='value'
                        :plan='plan'
                        :guestmode='guestmode'></r-item-studentgrades>
                    <r-item-studentcompletion
                        v-if='!!value.course.completion'
                        v-model='value.course.completion'
                        :course='value.course'
                        :guestmode='guestmode'></r-item-studentcompletion>
                    <r-item-student-course-competency
                        v-if='!!value.course.competency'
                        v-model='value.course.competency'
                        :item='value'
                        ></r-item-student-course-competency>
                    <s-course-extrafields
                        v-if="value.course.extrafields"
                        v-model="value.course.extrafields"
                        position="below"
                        ></s-course-extrafields>
                </b-modal>
            </div>
            `,
        });

        // Selected activities dispaly
        Vue.component('r-item-studentgrades', {
            props: {
                value: {
                    type: Object,
                    default() {
                        return {};
                    },
                },
                guestmode: {
                    type: Boolean,
                    'default': false,
                },
                plan: {
                    type: Object,
                    default() {
                        return null;
                    },
                },
            },
            data() {
                return {
                    text: strings.course,
                };
            },
            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: {
                completionIcon(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: `
            <table class="r-item-course-grade-details">
                <tr v-for="g in value.course.grades">
                    <td><span class="r-activity-icon" :title="g.typename" v-html="g.icon"></span
                        ><a
                        :href="(!guestmode)?(g.link):undefined" target="_blank"><span v-html="g.name"></span></a>
                        <abbr v-if="useRequiredGrades && g.required" :title="text.required_goal"
                            :class="'s-required ' + g.completion"
                            ><i class='fa fa-asterisk' ></i
                        ></abbr>
                    </td>
                    <td><span :class="' r-completion-'+g.completion">{{g.grade}}</span></td>
                    <td><i :class="'fa fa-'+completionIcon(g.completion)+' r-completion-'+g.completion"
                        :title="text['completion_'+g.completion]"></i>
                        <i v-if='g.pendingsubmission' :title="text['completion_pending']"
                            class="r-pendingsubmission fa fa-clock-o"></i></td>
                    <td v-if="g.feedback">
                        <a  v-b-modal="'r-grade-feedback-'+g.id"
                            href="#"
                            @click.prevent.stop=""
                        >{{ text["view_feedback"]}}</a>
                        <b-modal
                            :id="'r-grade-feedback-'+g.id"
                            ok-only
                            centered
                            scrollable
                        >
                            <template #modal-header>
                                <div>
                                <h3><i class="fa fa-graduation-cap"></i><span v-html="value.course.fullname"></span></h3><br>
                                <span class="r-activity-icon" :title="g.typename" v-html="g.icon + g.name"></span>
                                </div>
                            </template>
                            <span v-html="g.feedback"></span>
                        </b-modal>
                    </td>
                </tr>
            </table>
            `,
        });

        // Core completion version of student course info
        Vue.component('r-item-studentcompletion', {
            props: {
                value: {
                    type: Object,
                    default() {
                        return {};
                    },
                },
                guestmode: {
                    type: Boolean,
                    'default': false,
                },
                course: {
                    type: Object,
                    default() {
                        return {};
                    },
                },
            },
            data() {
                return {
                    text: strings.completion,
                };
            },
            computed: {
            },
            methods: {
                completionIcon(completion) {
                    switch (completion) {
                        case "progress":
                            return "exclamation-circle";
                        case "complete":
                            return "check-circle";
                        case "complete-pass":
                            return "check-circle";
                        case "complete-fail":
                            return "times-circle";
                        default: // Case "incomplete"
                            return "circle-o";
                    }
                },
                completionTag(cgroup) {
                    return cgroup.completion ? 'completed' : 'incomplete';
                },
                hasCompletions() {
                    if (this.value.conditions) {
                        for (const cgroup of this.value.conditions) {
                            if (cgroup.items && cgroup.items.length > 0) {
                                return true;
                            }
                        }
                    }
                    return false;
                },
                requirementHTML(requirements) {
                    const rqs = requirements.split(/, */);
                    let html = "";
                    for (const ix in rqs) {
                        const rq = rqs[ix];
                        html += `${rq}<br>`;
                    }
                    return html;
                },
                addTargetBlank(html) {
                    const m = /^([^<]*< *a +)(.*)/.exec(html);
                    if (m) {
                        return `${m[1]} target="_blank" ${m[2]}`;
                    } else {
                        return html;
                    }
                }
            },
            template: `
            <table class="r-item-course-grade-details">
                <tr v-if="hasCompletions">
                    <td colspan='2'
                        ><span v-if="value.conditions.length <= 1">{{ text.aggregation_overall_one }}</span
                        ><span v-else-if="value.aggregation == 'all'">{{ text.aggregation_overall_all}}</span
                        ><span v-else>{{ text.aggregation_overall_any }}</span
                    ></td>
                </tr>
                <tr v-else>
                    <td colspan='2'>{{text.completion_not_configured}}!
                    </td>
                </tr>
                <template v-for='cgroup in value.conditions' v-if='value.enabled && value.tracked'>
                    <tr>
                        <th colspan='2'><span v-if="cgroup.items.length > 1"
                        ><span v-if="cgroup.aggregation == 'all'">{{ text.aggregation_all}}</span
                        ><span v-else>{{ text.aggregation_any}}</span></span
                        ><span v-else>{{ text.aggregation_one }}</span>
                        {{ cgroup.title.toLowerCase() }}:
                        </th>
                        <th><r-progress-circle v-if="cgroup.progress < cgroup.count"
                                :value='cgroup.progress'
                                :max='cgroup.count'
                                :class="'r-completion-'+cgroup.status"
                                :title="text['completion_'+cgroup.status]"
                            ></r-progress-circle>
                            <i v-else :class="'fa fa-check-circle r-completion-'+cgroup.status"></i>
                            </th>
                    </tr>
                    <tr v-for='ci in cgroup.items'>
                        <td><span v-if='guestmode'><span v-html="ci.title"></span></span>
                            <span v-else v-html='addTargetBlank(ci.details.criteria)'></span>
                            <a href="#" v-b-tooltip.click.hover.right.html="{ customClass: 'r-tooltip ' + ci.status}"
                                :title="requirementHTML(ci.details.requirement)"
                                class="text-primary"><i  v-if="ci.details.requirement"
                                class='fa fa-question-circle'
                                ></i></a>
                        <td
                            ><span :class="' r-completion-'+ci.status">{{ci.grade}}</span>
                            <span v-if="ci.warning"
                                ><i class="text-primary fa fa-exclamation-triangle"
                                    v-b-tooltip.hover.right.click="{ customClass: 'r-tooltip info' }"
                                    :title="ci.warning"
                                ></i
                            ></span
                        ></td>
                        <td><i :class="'fa fa-'+completionIcon(ci.status)+' r-completion-'+ci.status"
                            :title="text['completion_'+ci.status]"></i>
                            <i v-if='ci.pending' :title="text['completion_pending']"
                                class="r-pendingsubmission fa fa-clock-o"></i>
                        </td>
                        <td v-if="ci.feedback">
                            <a  v-b-modal="'r-grade-feedback-'+ci.id"
                                href="#"
                                @click.prevent.stop=""
                            >{{ text["view_feedback"]}}</a>
                            <b-modal
                                :id="'r-grade-feedback-'+ci.id"
                                ok-only
                                centered
                                scrollable
                            >
                                <template #modal-header>
                                    <div>
                                        <h3><i class="fa fa-graduation-cap"></i><span v-html="course.fullname"></span></h3>
                                        <span class="r-activity-icon" :title="ci.typename" v-html="ci.icon + ci.name"></span>
                                    </div>
                                </template>
                                <span v-html="ci.feedback"></span>
                            </b-modal>
                        </td>
                    </tr>
                </template>
                <template v-else>
                    <tr v-if='! value.enabled'>
                        <td colspan='4'>{{text.completion_not_enabled}}</td>
                    </tr>
                    <tr v-else>
                        <td colspan='4'>{{text.student_not_tracked}}</td>
                    </tr>
                </template>
            </table>
            `,
        });

        // TAG: STUDENT Course competency
        Vue.component('r-item-student-course-competency', {
            props: {
                value: {
                    type: Object,
                    default() {
                        return {};
                    },
                },
                guestmode: {
                    type: Boolean,
                    'default': false,
                },
                item: {
                    type: Object,
                    default() {
                        return {id: null};
                    },
                }
            },
            data() {
                return {
                    text: strings.competency,
                };
            },
            computed: {
                hasCompletions() {
                    if (this.value.conditions) {
                        for (const cgroup of this.value.conditions) {
                            if (cgroup.items && cgroup.items.length > 0) {
                                return true;
                            }
                        }
                    }
                    return false;
                },
                wwwroot() {
                    return Config.wwwroot;
                }
            },
            methods: {
                completionIcon(competency) {
                    if (competency.proficient && competency.courseproficient) {
                        return "check-circle";
                    } else if (competency.proficient) {
                        return "check";
                    } else if (competency.proficient === false) {
                        return "times-circle";
                    } else {
                        return "circle-o";
                    }
                },

                completionTag(competency) {
                    if (competency.proficient && competency.courseproficient) {
                        return "completed";
                    } else if (competency.proficient) {
                        return "completed";
                    } else if (competency.proficient === false) {
                        return "failed";
                    } else if (competency.progress) {
                        return "progress";
                    } else {
                        return "incomplete";
                    }
                },

                pathtags(competency) {
                    const path = competency.path;
                    let s = "";
                    for (const ix in path) {
                        const p = path[ix];
                        if (ix > 0) {
                            s += " / ";
                        }
                        let url;
                        if (p.type == 'competency') {
                            url = Config.wwwroot + `/admin/tool/lp/user_competency_in_course.php?courseid=${this.item.course.id}&competencyid=${p.id}`;
                        } else {
                            url = this.competencyurl(p);
                        }

                        s += `<a href="${url}" target="_blank">${p.title}</a>`;
                    }
                    return s;
                },
                competencyurl(c) {
                    return Config.wwwroot + `/admin/tool/lp/user_competency_in_course.php?courseid=${this.item.course.id}&competencyid=${c.id}`;
                },
                usercompetencyurl(c) {
                    return Config.wwwroot + `/admin/tool/lp/user_competency.php?id=${c.ucid}`;
                }
            },
            template: `
            <table class="r-item-course-competency-list">
                <tr v-if="value.competencies.length == 0">
                    <td colspan='2'>{{text.competencies_not_configured}}!
                    <br><a :href="wwwroot+'/admin/tool/lp/coursecompetencies.php?courseid='+item.course.id" target='_blank'>{{text.configure_competencies}}</a>
                    </td>
                </tr>
                <template v-else>
                    <tr v-for='c in value.competencies'>
                        <td>
                            <a href="#" v-b-modal="'modal-competency-id-'+c.id" @click.prevent.stop=''><span v-html='c.title'  ></span></a>
                        </td>
                        <td class='details' >
                            <a v-if="c.details" href="#" v-b-modal="'modal-competency-id-'+c.id" @click.prevent.stop='' ><span v-html='c.details'></span></a>
                            <abbr v-if="c.required" :title="text.required"
                                :class="'s-required ' + + completionTag(c)"
                                ><i class='fa fa-asterisk' ></i
                            ></abbr>
                        </td>
                        <td :colspan="(c.required)?1:2">
                            <span :class="'r-completion-'+completionTag(c)">
                                <template v-if="!c.progress && !c.count">
                                    <i :class="'fa fa-'+completionIcon(c)" :title="text['completion_'+completionTag(c)]"></i>
                                    {{ (c.proficient === null)?text.unrated:c.grade }}
                                </template>
                                <template v-else>
                                    <r-progress-circle v-if='!c.proficient'
                                        :value='c.progress'
                                        :max='c.count'
                                        :min='0'
                                        :class="'r-completion-'+completionTag(c)"
                                        :title="text['completion_'+completionTag(c)]"
                                    ></r-progress-circle>
                                    <i v-else :class="'fa fa-'+completionIcon(c)" :title="text['completion_'+completionTag(c)]"></i>
                                    {{ (c.proficient === null)?((c.progress)?text.progress:text.unrated):c.grade }}
                                </template>
                            </span>
                        </td>
                        <td v-if="c.feedback">
                            <a  v-b-modal="'r-competency-feedback-'+c.id"
                                @click.prevent.stop=''
                                href="#"
                            >{{ text["view_feedback"]}}</a>
                            <b-modal
                                :id="'r-competency-feedback-'+c.id"
                                ok-only
                                centered
                                scrollable
                            >
                                <template #modal-header>
                                    <div>
                                    <h3><i class="fa fa-puzzle-piece"></i>{{ c.title }}</h3>
                                    </div>
                                </template>
                                <span v-html="c.feedback"></span>
                            </b-modal>
                        </td>
                        <b-modal :id="'modal-competency-id-'+c.id"
                            size="lg"
                            ok-only
                            centered
                            scrollable
                            >
                            <template #modal-header>
                                <div>
                                    <h1><i class="fa fa-puzzle-piece"></i>
                                        <a :href="wwwroot+'/admin/tool/lp/competencies.php?competencyid='+c.id" target="_blank"
                                        >{{c.title}} {{c.details}} </a
                                    ></h1>
                                    <div><span v-html="pathtags(c)"></span></div>
                                </div>
                            </template>
                            <div class="mb-2" v-if="c.description"><span v-html='c.description'></span></div>

                            <template v-if="c.rule && c.children">
                                <div>{{ c.ruleoutcome }} {{ text.when}} <span v-html="c.rule.toLocaleLowerCase()"></span></div>
                                <table v-if="c.children" class='r-item-course-competency-list'>
                                    <tr class='t-item-course-competency-headers'>
                                        <th colspan="2">{{text.heading}}</th>
                                        <th colspan="3">{{text.results}}</th>
                                    </tr>
                                    <tr v-for="cc in c.children">
                                        <td >
                                            <a :href='usercompetencyurl(cc)' target="_blank"><span v-html='cc.title'></span></a>
                                        </td>
                                        <td class='details'>
                                            <a v-if="cc.details" :href='usercompetencyurl(cc)' target="_blank"><span v-html='cc.details'></span></a>
                                            <abbr v-if="c.required" :title="text.required"
                                                :class="'s-required ' + + completionTag(cc)"
                                                ><i class='fa fa-asterisk' ></i
                                            ></abbr>
                                        </td>
                                        <td><span :class="'r-completion-'+completionTag(cc)"
                                            ><i :class="'fa fa-'+completionIcon(cc)" :title="text['completion_'+completionTag(cc)]"></i>
                                            {{ (cc.proficient === null)?text.unrated:cc.grade }}</span></td>
                                        <td><span class="text-info">{{ cc.points }} {{ text.points }}</span></td>
                                        <td>
                                        </td>
                                        <td v-if="cc.feedback">
                                            <a  v-b-modal="'r-competency-feedback-'+cc.id"
                                                href="#"
                                                @click.prevent.stop=""
                                            >{{ text["view_feedback"]}}</a>
                                            <b-modal
                                                :id="'r-competency-feedback-'+cc.id"
                                                ok-only
                                                centered
                                                scrollable
                                            >
                                                <template #modal-header>
                                                    <div>
                                                    <h3><i class="fa fa-puzzle-piece"></i>{{ cc.title }}</h3><br>
                                                    </div>
                                                </template>
                                                <span v-html="cc.feedback"></span>
                                            </b-modal>
                                        </td>
                                    </tr>
                                </table>
                            </template>
                        </b-modal>
                    </tr>
                </template>
            </table>
            `,
        });

        // TAG: Teacher course
        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: strings.teachercourse,
                    txt: {
                        grading: strings.grading,
                    }
                };
            },
            computed: {
                courseGradingNeeded() {
                    return this.courseGradingState();
                },
                courseGradingIcon() {
                    return this.determineGradingIcon(this.courseGradingState());
                },
                filteredGrades() {
                    return this.value.course.grades.filter(g => g.selected);
                },
                useRequiredGrades() {
                    if (this.plan && this.plan.aggregation_info && this.plan.aggregation_info.useRequiredGrades !== undefined) {
                        return this.plan.aggregation_info.useRequiredGrades;
                    } else {
                        return false;
                    }
                },
                isCompletable() {
                    let completable = false;
                    if (this.value.course.completion) {
                        if (this.value.course.completion.conditions.length > 0) {
                            completable = true;
                        }
                    } else if (this.value.course.grades) {
                        if (this.value.course.grades.length > 0) {
                            completable = true;
                        }
                    }

                    return completable;
                },
                progressCircle() {
                    const status = {
                        students: 0,
                        completed: 0,
                        completedPass: 0,
                        completedFail: 0,
                        ungraded: 0,
                    };

                    if (this.value.course.completion) {
                        for (const cond of this.value.course.completion.conditions) {
                            for (const itm of cond.items) {
                                if (itm.progress) {
                                    status.students += itm.progress.students;
                                    status.completed += itm.progress.completed;
                                    status.completedPass += itm.progress.completed_pass;
                                    status.completedFail += itm.progress.completed_fail;
                                    status.ungraded += itm.progress.ungraded;
                                }
                            }
                        }
                    } else if (this.value.course.competency) {
                        status.students = this.value.course.competency.total;
                        status.completedPass = this.value.course.competency.proficient;
                        status.completedFail = this.value.course.competency.failed;
                        status.ungraded = this.value.course.competency.needreview;
                    } else if (this.value.course.grades) {
                        for (const g of this.value.course.grades) {
                            if (g.grading) {
                                status.students += g.grading.students;
                                status.completed += g.grading.completed;
                                status.completedPass += g.grading.completed_pass;
                                status.completedFail += g.grading.completed_fail;
                                status.ungraded += g.grading.ungraded;
                            }
                        }
                    }

                    return status;
                },
                startdate() {
                    return formatDate(this.value.course.startdate);
                },
                enddate() {
                    if (this.value.course.enddate.length > 0) {
                        return formatDate(this.value.course.enddate);
                    } else {
                        return this.text.noenddate;
                    }
                },
                wwwroot() {
                    return Config.wwwroot;
                }

            },
            methods: {
                courseGradingState() {
                    let ungraded = 0;
                    let unknown = 0;
                    let graded = 0;
                    let allgraded = 0;
                    const grades = this.filteredGrades;

                    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';
                    }
                },
                determineGradingIcon(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";
                    }
                },
            },
            template: `
            <div  :class="'r-item-course card '+ (value.course.amteacher?'r-course-am-teacher':'')">
                <div class='r-item-course-cardwrapper mr-0 ml-0 h-100 '>
                    <div
                        :title="text['coursetiming_'+value.course.timing]"
                        v-b-popover.hover.top="startdate+' - '+enddate"
                        :class="'r-timing-indicator timing-'+value.course.timing"
                    ></div>
                    <div class="r-item-course-title card-body h-100">
                        <fittext maxsize="12pt" minsize="9pt">
                        <a  v-b-modal="'r-item-course-details-'+value.id"
                            :href="(!guestmode)?(wwwroot+'/course/view.php?id='+value.course.id):'#'"
                            @click.prevent.stop=''
                            ><span v-html="value.course.displayname"></span></a>
                        </fittext>
                    </div>
                    <div class="h-100 r-item-course-indicator ">
                        <r-completion-circle class="r-course-graded" :disabled="!isCompletable"
                        v-model="progressCircle"></r-completion-circle>
                    </div>
                </div>
                <b-modal
                    v-if="true"
                    :id="'r-item-course-details-'+value.id"
                    :title="value.course.displayname + ' - ' + value.course.fullname"
                    size="lg"
                    ok-only
                    centered
                    scrollable
                    >
                    <template #modal-header>
                        <div>
                            <h1><a :href="(!guestmode)?(wwwroot+'/course/view.php?id='+value.course.id):undefined" target="_blank"
                                ><i class="fa fa-graduation-cap"></i> <span v-html="value.course.fullname"></span></a>
                                <r-item-teacher-gradepicker v-model="value"
                                    v-if="value.course.grades && value.course.grades.length > 0"
                                    :useRequiredGrades="useRequiredGrades"
                                    :plan="plan"
                                ></r-item-teacher-gradepicker>
                                <a v-if='!!value.course.completion && value.course.amteacher'
                                    :href="wwwroot+'/course/completion.php?id='+value.course.id"  target="_blank"
                                    :title="text.configure_completion"><i class="fa fa-gear"></i></a>
                            </h1>
                            <span v-html="value.course.category.path.join(' / ')"></span>
                            <div class='mt-1 text-info'>
                            <span v-if='value.course.numenrolled != 1'>{{ value.course.numenrolled }} {{ text.students_from_plan_enrolled }}</span>
                              <span v-else> 1 {{ text.student_from_plan_enrolled }} </span>
                            </div>
                        </div>
                        <div class="r-course-detail-header-right">
                            <div class="r-completion-detail-header">
                                <r-completion-circle class="r-progress-circle-popup" :disabled="!isCompletable"
                                    v-model="progressCircle"></r-completion-circle>
                            </div>
                            <div :class="'r-timing-'+value.course.timing">
                                {{ text['coursetiming_'+value.course.timing] }}<br>
                                {{ startdate }} - {{ enddate }}
                            </div>
                        </div>
                    </template>
                    <s-course-extrafields
                        v-if="value.course.extrafields"
                        v-model="value.course.extrafields"
                        position="above"
                        ></s-course-extrafields>
                    <r-item-teachergrades
                        v-if='!!value.course.grades && value.course.grades.length > 0'
                        v-model='value.course'
                        :useRequiredGrades="useRequiredGrades"
                        ></r-item-teachergrades>
                    <r-item-teachercompletion
                        v-if='!!value.course.completion'
                        v-model='value.course.completion'
                        :course='value.course'
                        ></r-item-teachercompletion>
                    <r-item-teacher-course-competency
                        v-if='!!value.course.competency'
                        v-model='value.course.competency'
                        :item='value'
                        ></r-item-teacher-course-competency>
                    <s-course-extrafields
                        v-if="value.course.extrafields"
                        v-model="value.course.extrafields"
                        position="below"
                        ></s-course-extrafields>
                </b-modal>

            </b-card>
            </div>
            `,
        });


        // Select activities to use in grade overview
        Vue.component('r-item-teacher-gradepicker', {
            props: {
                value: {
                    type: Object, // Item
                    default() {
                        return {};
                    },
                },
                useRequiredGrades: {
                    type: Boolean,
                    default() {
                        return null;
                    }
                }
            },
            data() {
                return {
                    text: strings.teachercourse,
                };
            },
            computed: {
                startdate() {
                    return formatDate(this.value.course.startdate);
                },
                enddate() {
                    if (this.value.course.enddate.length > 0) {
                        return formatDate(this.value.course.enddate);
                    } else {
                        return this.text.noenddate;
                    }
                },
                wwwroot() {
                    return Config.wwwroot;
                }
            },
            methods: {
                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].catch(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].catch(notification.exception);
                },
            },
            template: `
                <a  v-if="value.course.canselectgradables" href='#'
                                            v-b-modal="'r-item-course-config-'+value.id"
                                            @click.prevent.stop=''
                                            ><i class='fa fa-cog'></i>
                    <b-modal v-if='value.course.canselectgradables'
                        :id="'r-item-course-config-'+value.id"
                        :title="value.course.displayname + ' - ' + value.course.fullname"
                        ok-only
                        scrollable
                        >
                        <template #modal-header>
                            <div>
                                <h1><a :href="wwwroot+'/course/view.php?id='+value.course.id" target="_blank"
                                    ><i class="fa fa-graduation-cap"></i> <span v-html="value.course.fullname"></span></a></h1>
                                 <span v-html="value.course.category.path.join(' / ')"></span> / <span v-html="value.course.displayname"></span>>
                            </div>
                            <div class="r-course-detail-header-right">
                                <div :class="'r-timing-'+value.course.timing">
                                    {{text['coursetiming_'+value.course.timing]}}<br>
                                    {{ startdate }} - {{ enddate }}
                                </div>
                            </div>
                        </template>
                        <b-form-group
                            :label="text.select_grades"
                            ><ul class="t-item-module-children">
                                <li class="t-item-course-gradeinfo">
                                    <span class='t-item-course-chk-lbl'>{{text.grade_include}}</span
                                    ><span v-if="useRequiredGrades"  class='t-item-course-chk-lbl'>{{text.grade_require}}</span>
                                </li>
                                <li class="t-item-course-gradeinfo" v-for="g in value.course.grades">
                                    <b-form-checkbox inline
                                        @change="includeChanged($event,g)" v-model="g.selected"
                                        ></b-form-checkbox>
                                    <b-form-checkbox v-if="useRequiredGrades" inline :disabled="!g.selected"
                                        @change="requiredChanged($event,g)" v-model="g.required"
                                        ></b-form-checkbox>
                                    <span :title="g.typename" v-html="g.icon"></span><a
                                        :href="g.link" target="_blank">{{g.name}}</a>
                                    <s-edit-mod
                                        :title="value.course.fullname"
                                        @saved="(fd) => g.name = fd.get('name')"
                                        v-if="g.cmid > 0"
                                        :cmid="g.cmid"
                                        :coursectxid="value.course.ctxid"
                                        genericonly></s-edit-mod>
                                </li>
                            </ul>
                        </b-form-group>
                    </b-modal>
                </a>
            `,
        });

        // Selected activities dispaly
        Vue.component('r-item-teachergrades', {
            props: {
                value: {
                    type: Object,
                    default() {
                        return {};
                    },
                },
                useRequiredGrades: {
                    type: Boolean,
                    'default': false,
                },
            },
            data() {
                return {
                    text: strings.teachercourse,
                    txt: {
                        grading: strings.grading,
                    }
                };
            },
            computed: {
                pendingsubmission() {
                    let result = false;
                    for (const ix in this.value.grades) {
                        const g = this.value.grades[ix];
                        if (g.pendingsubmission) {
                            result = true;
                            break;
                        }
                    }
                    return result;
                },
                filteredGrades() {
                    return this.value.grades.filter(g => g.selected);
                },
            },
            methods: {
                determineGradingIcon(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";
                    }
                },
                gradingIcon(grade) {
                    return this.determineGradingIcon(this.isGradingNeeded(grade));
                },
                isGradingNeeded(grade) {
                    if (grade.grading) {
                        if (grade.grading.ungraded) {
                            return 'ungraded';
                        } else if (grade.grading.completed_pass || grade.grading.completed || grade.grading.completed_fail) {
                            if (Number(grade.grading.completed) + Number(grade.grading.completed_pass)
                                + Number(grade.grading.completed_fail)
                                == Number(grade.grading.students)) {
                                return 'allgraded';
                            } else {
                                return 'graded';
                            }
                        } else {
                            return 'unsubmitted';
                        }
                    } else {
                        return 'unknown';
                    }
                },

            },
            template: `
            <div>
                <table class="r-item-course-grade-details">
                    <tr v-for="g in filteredGrades">
                        <td><span class="r-activity-icon" :title="g.typename" v-html="g.icon"></span
                            ><a
                            :href="g.gradinglink"
                            target="_blank" :title="g.name"><span v-html="g.name"></span></a>
                            <s-edit-mod
                                :title="value.fullname"
                                @saved="(fd) => g.name = fd.get('name')"
                                v-if="g.cmid > 0"
                                :cmid="g.cmid"
                                :coursectxid="value.ctxid"
                                genericonly></s-edit-mod>
                            <abbr v-if="useRequiredGrades && g.required" :title="text.required_goal"
                                :class="'s-required ' + isGradingNeeded(g)"
                                ><i class='fa fa-asterisk' ></i
                            ></abbr>
                        </td>
                        <td v-if='g.grading'
                            ><i :class="'r-course-grading fa fa-'+gradingIcon(g)+' r-graded-'+isGradingNeeded(g)"
                            :title="txt.grading[isGradingNeeded(g)]"></i>
                        </td>
                        <td v-if='g.grading'>
                            <r-completion-bar v-model="g.grading" :width="150" :height="15"></r-completion-bar>
                        </td>
                    </tr>
                </table>
            </div>
            `,
        });

        // Core completion version of student course info
        Vue.component('r-item-teachercompletion', {
            props: {
                value: {
                    type: Object,
                    default() {
                    return {};
                },
                },
                guestmode: {
                    type: Boolean,
                    'default': false,
                },
                course: {
                    type: Object,
                    default() {
                        return {};
                    },
                },
            },
            data() {
                return {
                    text: strings.completion,
                };
            },
            computed: {
                completionreport() {
                    return `${Config.wwwroot}/report/completion/index.php?course=${this.course.id}`;
                },
                wwwroot() {
                    return Config.wwwroot;
                }
            },
            methods: {
                hasCompletions() {
                    if (this.value.conditions) {
                        for (const cgroup of this.value.conditions) {
                            if (cgroup.items && cgroup.items.length > 0) {
                                return true;
                            }
                        }
                    }
                    return false;
                },
            },
            template: `
            <table class="r-item-course-grade-details">
                <tr v-if="hasCompletions">
                    <td colspan='2'
                        ><span v-if="value.conditions.length <= 1">{{ text.aggregation_overall_one }}</span
                        ><span v-else-if="value.aggregation == 'all'">{{ text.aggregation_overall_all}}</span
                        ><span v-else>{{ text.aggregation_overall_any }}</span
                    ></td>
                </tr>
                <tr v-else>
                    <td colspan='2'>{{text.completion_not_configured}}!
                    <span v-if="course.amteacher">
                        <br><a :href="wwwroot+'/course/completion.php?id='+course.id" target='_blank'>{{text.configure_completion}}</a>
                    </span>
                    </td>
                </tr>
                <template v-for='cgroup in value.conditions'>
                    <tr>
                        <th colspan='2'><span v-if="cgroup.items.length > 1"
                        ><span v-if="cgroup.aggregation == 'all'">{{ text.aggregation_all}}</span
                        ><span v-else>{{ text.aggregation_any}}</span></span
                        ><span v-else>{{ text.aggregation_one }}</span>
                        {{ cgroup.title.toLowerCase() }}:
                        </th>
                    </tr>
                    <tr v-for='ci in cgroup.items'>
                        <td><span v-html='ci.details.criteria'></span>
                            <a  href="#" v-b-tooltip.click
                                :title="ci.details.requirement"
                                class='text-info'><i  v-if="ci.details.requirement"
                                class='fa fa-question-circle'
                                ></i></a>
                        </td>
                        <td>
                            <r-completion-bar v-model="ci.progress" :width="150" :height="15"></r-completion-bar>
                        </td>
                    </tr>
                </template>
                <tr><td colspan='2' class='pt-2'>
                    <a target="_blank" :href='completionreport'>{{ text.view_completion_report}}
                    <i class='fa fa-external-link'></i></a></td></tr>
            </table>
            `,
        });


        // TAG: Teacher Course competency
        Vue.component('r-item-teacher-course-competency', {
            props: {
                value: {
                    type: Object,
                    default() {
                        return {};
                    },
                },
                guestmode: {
                    type: Boolean,
                    'default': false,
                },
                item: {
                    type: Object,
                    default() {
                        return {id: null};
                    },
                }
            },
            data() {
                return {
                    text: strings.competency,
                };
            },
            computed: {
                hasCompletions() {
                    if (this.value.conditions) {
                        for (const cgroup of this.value.conditions) {
                            if (cgroup.items && cgroup.items.length > 0) {
                                return true;
                            }
                        }
                    }
                    return false;
                },
                wwwroot() {
                    return Config.wwwroot;
                }
            },
            methods: {
                completionIcon(competency) {
                    if (competency.proficient && competency.courseproficient) {
                        return "check-circle";
                    } else if (competency.proficient) {
                        return "check";
                    } else if (competency.proficient === false) {
                        return "times-circle";
                    } else {
                        return "circle-o";
                    }
                },

                completionTag(competency) {
                    if (competency.proficient && competency.courseproficient) {
                        return "completed";
                    } else if (competency.proficient) {
                        return "completed";
                    } else if (competency.proficient === false) {
                        return "failed";
                    } else if (competency.progress) {
                        return "progress";
                    } else {
                        return "incomplete";
                    }
                },

                pathtags(competency) {
                    const path = competency.path;
                    let s = "";
                    for (const ix in path) {
                        const p = path[ix];
                        if (ix > 0) {
                            s += " / ";
                        }
                        let url;
                        if (p.type == 'competency') {
                            url = Config.wwwroot + `/admin/tool/lp/user_competency_in_course.php?courseid=${this.item.course.id}&competencyid=${p.id}`;
                        } else {
                            url = this.competencyurl(p);
                        }

                        s += `<a href="${url}" target="_blank">${p.title}</a>`;
                    }
                    return s;
                },
                competencyurl(c) {
                    return Config.wwwroot + `/admin/tool/lp/user_competency_in_course.php?courseid=${this.item.course.id}&competencyid=${c.id}`;
                },
            },
            template: `
            <table class="r-item-course-competency-list">
                <tr v-if="value.competencies.length == 0">
                    <td colspan='2'>{{text.competencies_not_configured}}!
                    <br><a :href="wwwroot+'/admin/tool/lp/coursecompetencies.php?courseid='+item.course.id" target='_blank'>{{text.configure_competencies}}</a>
                    </td>
                </tr>
                <template v-else>
                    <tr v-for='c in value.competencies'>
                        <td>
                            <a :href="wwwroot+'/admin/tool/lp/competencies.php?competencyid='+c.id"
                                @click.prevent  target="_blank" v-b-modal="'modal-competency-id-'+c.id"><span v-html='c.title'></span></a>
                        </td>
                        <td class='details'>
                            <a  v-if="c.details" href="wwwroot+'/admin/tool/lp/competencies.php?competencyid='+c.id"
                                @click.prevent v-b-modal="'modal-competency-id-'+c.id"><span v-html='c.details'></span></a>
                            <abbr v-if="c.required" :title="text.required"
                                :class="'s-required ' + + completionTag(c)"
                                ><i class='fa fa-asterisk' ></i
                            ></abbr>
                        </td>
                        <td><r-completion-bar v-model="c.completionstats" :width="150" :height="15"></r-completion-bar></td>
                        <td v-if="c.feedback">
                            <a  v-b-modal="'r-competency-feedback-'+c.id"
                                href="#"
                                @click.prevent.stop=""
                            >{{ text["view_feedback"]}}</a>
                            <b-modal
                                :id="'r-competency-feedback-'+c.id"
                                ok-only
                                centered
                                scrollable
                            >
                                <template #modal-header>
                                    <div>
                                    <h3><i class="fa fa-puzzle-piece"></i>{{ c.title }}</h3><br>
                                    </div>
                                </template>
                                <span v-html="c.feedback"></span>
                            </b-modal>
                        </td>
                        <b-modal :id="'modal-competency-id-'+c.id"
                            size="lg"
                            ok-only
                            centered
                            scrollable
                            >
                            <template #modal-header>
                                <div>
                                    <h1><i class="fa fa-puzzle-piece"></i>
                                        <a :href="wwwroot+'/admin/tool/lp/competencies.php?competencyid='+c.id" target="_blank"
                                        >{{c.title}} {{c.details}} </a
                                    ></h1>
                                    <div><span v-html="pathtags(c)"></span></div>
                                </div>
                            </template>
                            <div class="mb-2" v-if="c.description"><span v-html='c.description'></span></div>

                            <template v-if="c.rule && c.children">
                                <div>{{ c.ruleoutcome }} {{ text.when}} <span v-html="c.rule.toLocaleLowerCase()"></span></div>
                                <table v-if="c.children" class='r-item-course-competency-list'>
                                    <tr class='t-item-course-competency-headers'>
                                        <th colspan="2">{{text.heading}}</th>
                                        <th colspan="3">{{text.results}}</th>
                                    </tr>
                                    <tr v-for="cc in c.children">
                                        <td>
                                            <a :href='competencyurl(cc)' target="_blank"><span v-html='cc.title'></span></a>
                                        </td>
                                        <td class='details'>
                                            <a  v-if="cc.details" :href='competencyurl(cc)' target="_blank"><span v-html='cc.details'></span></a>
                                            <abbr v-if="c.required" :title="text.required"
                                                :class="'s-required ' + completionTag(cc)"
                                                ><i class='fa fa-asterisk' ></i
                                            ></abbr>
                                        </td>
                                        <td><r-completion-bar v-model="cc.completionstats" :width="150" :height="15"></r-completion-bar></td>
                                        <td><span class="text-info">{{ cc.points }} {{ text.points }}</span></td>
                                        <td v-if="cc.feedback">
                                            <a  v-b-modal="'r-competency-feedback-'+cc.id"
                                                href="#"
                                                @click.prevent.stop=""
                                            >{{ text["view_feedback"]}}</a>
                                            <b-modal
                                                :id="'r-competency-feedback-'+cc.id"
                                                ok-only
                                                centered
                                                scrollable
                                            >
                                                <template #modal-header>
                                                    <div>
                                                    <h3><i class="fa fa-puzzle-piece"></i>{{ cc.title }}</h3><br>
                                                    </div>
                                                </template>
                                                <span v-html="cc.feedback"></span>
                                            </b-modal>
                                        </td>
                                    </tr>
                                </table>
                            </template>
                        </b-modal>
                    </tr>
                </template>
            </table>
            `,
        });


        Vue.component('r-grading-bar', {
            props: {
                value: {
                    type: Object,
                    default() {
                        return {};
                    },
                },
                width: {
                    type: Number,
                    'default': 150,
                },
                height: {
                    type: Number,
                    'default': 15,
                }
            },
            data() {
                return {
                    text: strings.grading,
                };
            },
            computed: {
                widthUnsubmitted() {
                    return this.width * this.fractionUnsubmitted();
                },
                widthGraded() {
                    return this.width * this.fractionGraded();
                },
                widthUngraded() {
                    return this.width * this.fractionUngraded();
                },
                countUnsubmitted() {
                    return (this.value.students - this.value.graded - this.value.ungraded);
                }
            },
            methods: {
                fractionUnsubmitted() {
                    if (this.value.students > 0) {
                        return 1 - ((this.value.graded + this.value.ungraded) / this.value.students);
                    } else {
                        return 1;
                    }
                },
                fractionGraded() {
                    if (this.value.students > 0) {
                        return this.value.graded / this.value.students;
                    } else {
                        return 0;
                    }
                },
                fractionUngraded() {
                    if (this.value.students > 0) {
                        return this.value.ungraded / this.value.students;
                    } else {
                        return 0;
                    }
                },
            },
            template: `
            <span class="r-grading-bar" :style="{height: height+'px'}"
                ><span  :style="{height: height+'px', width: widthUngraded+'px'}"
                        class='r-grading-bar-segment r-grading-bar-ungraded'
                        :title="text.ungraded + ' (' + this.value.ungraded + ')'" v-b-popover.hover.top
                ></span
                ><span  :style="{height: height+'px', width: widthGraded+'px'}"
                        class='r-grading-bar-segment r-grading-bar-graded'
                        :title="text.graded+ ' (' + this.value.graded + ')'" v-b-popover.hover.top
                ></span
                ><span  :style="{height: height+'px', width: widthUnsubmitted+'px'}"
                        class='r-grading-bar-segment r-grading-bar-unsubmitted'
                        :title="text.unsubmitted + ' (' + countUnsubmitted + ')'" v-b-popover.hover.top
                ></span
            ></span>
            `,
        });

        Vue.component('r-completion-bar', {
            props: {
                value: {
                    type: Object,
                    default() {
                        return {
                            students: 0,
                            completed: 0,
                            'completed_pass': 0,
                            'completed_fail': 0,
                            ungraded: 0,
                        };
                    },
                },
                width: {
                    type: Number,
                    'default': 150,
                },
                height: {
                    type: Number,
                    'default': 15,
                }
            },
            data() {
                return {
                    text: strings.completion,
                };
            },
            computed: {
                widthIncomplete() {
                    return this.width * this.fractionIncomplete();
                },
                widthCompleted() {
                    return this.width * this.fractionCompleted();
                },
                widthCompletedPass() {
                    return this.width * this.fractionCompletedPass();
                },
                widthCompletedFail() {
                    return this.width * this.fractionCompletedFail();
                },
                widthUngraded() {
                    return this.width * this.fractionUngraded();
                },
                countIncomplete() {
                    return (this.value.students - this.value.completed - this.value.completed_pass
                             - this.value.completed_fail - this.value.ungraded);
                }
            },
            methods: {
                fractionIncomplete() {
                    if (this.value.students > 0) {
                        return 1 - (
                            (this.value.completed + this.value.completed_pass +
                             this.value.completed_fail + this.value.ungraded) / this.value.students);
                    } else {
                        return 1;
                    }
                },
                fractionCompleted() {
                    if (this.value.students > 0) {
                        return this.value.completed / this.value.students;
                    } else {
                        return 0;
                    }
                },
                fractionCompletedPass() {
                    if (this.value.students > 0) {
                        return this.value.completed_pass / this.value.students;
                    } else {
                        return 0;
                    }
                },
                fractionCompletedFail() {
                    if (this.value.students > 0) {
                        return this.value.completed_fail / this.value.students;
                    } else {
                        return 0;
                    }
                },
                fractionUngraded() {
                    if (this.value.students > 0) {
                        return this.value.ungraded / this.value.students;
                    } else {
                        return 0;
                    }
                },
            },
            template: `
            <span class="r-grading-bar" :style="{height: height+'px'}"
                ><span  :style="{height: height+'px', width: widthUngraded+'px'}"
                        class='r-grading-bar-segment r-completion-bar-ungraded'
                        :title="text.ungraded + ' (' + this.value.ungraded + ')'" v-b-popover.hover.top
                ></span
                ><span  :style="{height: height+'px', width: widthCompleted+'px'}"
                        class='r-grading-bar-segment r-completion-bar-completed'
                        :title="text.completed + ' (' + this.value.completed + ')'" v-b-popover.hover.top
                ></span
                ><span  :style="{height: height+'px', width: widthCompletedPass+'px'}"
                        class='r-grading-bar-segment r-completion-bar-completed-pass'
                        :title="text.completed_pass + ' (' + this.value.completed_pass + ')'" v-b-popover.hover.top
                ></span
                ><span  :style="{height: height+'px', width: widthCompletedFail+'px'}"
                        class='r-grading-bar-segment r-completion-bar-completed-fail'
                        :title="text.completed_fail + ' (' + this.value.completed_fail + ')'" v-b-popover.hover.top
                ></span
                ><span  :style="{height: height+'px', width: widthIncomplete+'px'}"
                        class='r-grading-bar-segment r-completion-bar-incomplete'
                        :title="text.incomplete + ' (' + countIncomplete + ')'" v-b-popover.hover.top
                ></span
            ></span>
            `,
        });

        Vue.component('r-completion-circle', {
            props: {
                value: {
                    type: Object,
                    default() {
                        return {
                            students: 10,
                            completed: 2,
                            completedPass: 2,
                            completedFail: 2,
                            ungraded: 2,
                        };
                    },
                },
                stroke: {
                    type: Number,
                    'default': 0.2,
                },
                disabled: {
                    type: Boolean,
                    'default': false,
                },
                title: {
                    type: String,
                    'default': "",
                }
            },
            computed: {
                completedPass() {
                    if (this.value.completed_pass) {
                        return this.value.completed_pass;
                    } else {
                        return this.value.completedPass;
                    }
                },
                completedFail() {
                    if (this.value.completed_fail) {
                        return this.value.completed_fail;
                    } else {
                        return this.value.completedFail;
                    }
                },
                radius() {
                    return 50 - (50 * this.stroke);
                },

                arcpathUngraded() {
                    const begin = 0;
                    return this.arcpath(begin, this.fractionUngraded());
                },
                arcpathCompleted() {
                    const begin = this.fractionUngraded();
                    return this.arcpath(begin, this.fractionCompleted());
                },
                arcpathCompletedPass() {
                    const begin = this.fractionUngraded()
                                + this.fractionCompleted();
                    return this.arcpath(begin, this.fractionCompletedPass());
                },
                arcpathCompletedFail() {
                    const begin = this.fractionUngraded()
                                + this.fractionCompleted()
                                + this.fractionCompletedPass();
                    return this.arcpath(begin, this.fractionCompletedFail());
                },
                arcpathIncomplete() {
                    const begin = this.fractionUngraded()
                                + this.fractionCompleted()
                                + this.fractionCompletedPass()
                                + this.fractionCompletedFail();
                    return this.arcpath(begin, this.fractionIncomplete());
                },

            },
            methods: {
                arcpath(start, end) {
                    const r = 50 - (50 * this.stroke);
                    const t1 = start * 2 * π;
                    const Δ = end * 2 * π;
                    return svgarcpath([50, 50], [r, r], [t1, Δ], 1.5 * π);
                },
                fractionIncomplete() {
                    if (this.value.students > 0) {
                        return 1 - (
                            (this.value.completed + this.completedPass +
                                this.completedFail + this.value.ungraded) / this.value.students);
                    } else {
                        return 1;
                    }
                },
                fractionCompleted() {
                    if (this.value.students > 0) {
                        return this.value.completed / this.value.students;
                    } else {
                        return 0;
                    }
                },
                fractionCompletedPass() {
                    if (this.value.students > 0) {
                        return this.completedPass / this.value.students;
                    } else {
                        return 0;
                    }
                },
                fractionCompletedFail() {
                    if (this.value.students > 0) {
                        return this.completedFail / this.value.students;
                    } else {
                        return 0;
                    }
                },
                fractionUngraded() {
                    if (this.value.students > 0) {
                        return this.value.ungraded / this.value.students;
                    } else {
                        return 0;
                    }
                },
            },
            template: `
            <svg width="1em" height="1em" viewBox="0 0 100 100">
                <title>{{title}}</title>
                <circle cx="50" cy="50" :r="radius"
                    :style="'stroke-width: ' + (stroke*100)+'; stroke: #ccc; fill: none;'"/>
                <path :d="arcpathUngraded"
                    :style="'stroke-width: ' + (stroke*100) +'; stroke: var(--statbar-color-ungraded); fill: none;'"/>
                <path :d="arcpathCompleted"
                    :style="'stroke-width: ' + (stroke*100) +'; stroke: var(--statbar-color-completed); fill: none;'"/>
                <path :d="arcpathCompletedPass"
                    :style="'stroke-width: ' + (stroke*100) +'; stroke: var(--statbar-color-pass); fill: none;'"/>
                <path :d="arcpathCompletedFail"
                    :style="'stroke-width: ' + (stroke*100) +'; stroke: var(--statbar-color-fail); fill: none;'"/>

                <circle v-if="disabled" cx="50" cy="50" :r="radius/2"
                    :style="'fill: var(--color-disabled);'"/>
                <circle v-else-if="value.ungraded > 0" cx="50" cy="50" :r="radius/2"
                    :style="'fill: var(--statbar-color-ungraded);'"/>
            </g>
            </svg>
            `,
        });

        Vue.component('r-item-junction', {
            props: {
                value: {
                    type: Object,
                    default() {
                        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: `
            <div :class="'r-item-junction r-item-filter completion-'+completion">
                <i v-if="value.completion=='incomplete'" class="fa fa-circle-o"></i>
                <i v-else-if="value.completion=='failed'" class="fa fa-times-circle"></i>
                <i v-else-if="value.completion=='progress'" class="fa fa-exclamation-circle"></i>
                <i v-else class="fa fa-check-circle"></i>
            </div>
            `,
        });

        Vue.component('r-item-finish', {
            props: {
                value: {
                    type: Object,
                    default() {
                    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: `
            <div :class="'r-item-finish r-item-filter completion-'+completion">
                <i class="fa fa-stop-circle"></i>
            </div>
            `,
        });

        Vue.component('r-item-start', {
            props: {
                value: {
                    type: Object,
                    default() {
                        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: `
            <div :class="'r-item-start r-item-filter completion-'+completion">
                <i class="fa fa-play-circle"></i>
            </div>
            `,
        });

        Vue.component('r-item-badge', {
            props: {
                value: {
                    type: Object,
                    default() {
                        return {};
                    },
                },
                guestmode: {
                    type: Boolean,
                    'default': false,
                },
                teachermode: {
                    type: Boolean,
                    'default': false,
                }
            },
            data() {
                return {
                    text: strings.badge,
                };
            },
            computed: {
                completion() {
                    return this.value.badge.issued ? "completed" : "incomplete";
                },
                issuedIcon() {
                    switch (this.value.badge.issued) {
                        default: // "nogrades":
                            return "circle-o";
                        case true:
                            return "check";
                    }
                },
                issuestats() {
                    // So the r-completion-bar can be used to show issuing stats
                    return {
                        students: (this.value.badge.studentcount) ? this.value.badge.studentcount : 0,
                        completed: (this.value.badge.issuedcount) ? this.value.badge.issuedcount : 0,
                        'completed_pass': 0,
                        'completed_fail': 0,
                        ungraded: 0,
                    };
                },
                arcpathIssued() {
                    if (this.value.badge.studentcount) {
                        const fraction = this.value.badge.issuedcount / this.value.badge.studentcount;
                        return this.arcpath(0, fraction);
                    } else {
                        return ""; // No path
                    }
                },
                arcpathProgress() {
                    if (this.value.badge.completion) {
                        const fraction = this.value.badge.completion.progress / this.value.badge.completion.count;
                        return this.arcpath(0, fraction);
                    } else {
                        return ""; // No path
                    }
                },
                badgeinprogress() {
                    return (
                        this.value.badge.issued || this.teachermode ||
                        (
                            this.value.badge.completion
                            && this.value.badge.completion.progress >= this.value.badge.completion.count
                        )
                    );
                }
            },
            methods: {
                arcpath(start, end) {
                    const r = 44;

                    const t1 = start * 2 * π;
                    const Δ = (end * 2 * π - 0.01);
                    return svgarcpath([50, 50], [r, r], [t1, Δ], 1.5 * π);
                },
                addTargetBlank(html) {
                    const m = /^([^<]*< *a +)(.*)/.exec(html);
                    if (m) {
                        return `${m[1]} target="_blank" ${m[2]}`;
                    } else {
                        return html;
                    }
                },
                completionIconRq(complete) {
                    if (complete) {
                        return "check-square-o";
                    } else {
                        return "square-o";
                    }
                },
                completionIcon(complete) {
                    if (complete) {
                        return "check-circle";
                    } else {
                        return "times-circle";
                    }
                },
                status(complete) {
                    if (complete) {
                        return "complete";
                    } else {
                        return "incomplete";
                    }
                }
            },
            template: `
            <div :class="'r-item-badge r-item-filter r-completion-'+completion" v-b-tooltip.hover :title="value.badge.name">
                <a v-b-modal="'r-item-badge-details-'+value.id"
                ><svg class="r-badge-backdrop " width='50px' height='50px' viewBox="0 0 100 100">
                    <title>{{value.badge.name}}</title>
                    <template v-if="teachermode">
                        <circle cx="50" cy="50" r="44"
                            style="stroke: #ccc; stroke-width: 8; fill: #ddd; fill-opacity: 0.8;"/>
                        <path :d="arcpathIssued"
                            :style="'stroke-width: 8; stroke: var(--color-completed); fill: none;'"/>
                    </template>
                    <circle v-else-if="value.badge.issued" cx="50" cy="50" r="46"
                        style="stroke: currentcolor; stroke-width: 4; fill: currentcolor; fill-opacity: 0.5;"/>
                    <template v-else-if="value.badge.completion">
                        <circle cx="50" cy="50" r="44"
                            style="stroke: #ccc; stroke-width: 8; fill: #ddd; fill-opacity: 0.8;"/>
                        <path :d="arcpathProgress"
                            :style="'stroke-width: 8; stroke: var(--color-completed); fill: none;'"/>
                    </template>
                    <circle v-else cx="50" cy="50" r="46"
                        stroke-dasharray="6 9"
                        style="stroke: #999; stroke-width: 6; fill: #ddd; fill-opacity: 0.8;"/>
                    <image class="badge-image" clip-path="circle() fill-box"
                        :href="value.badge.imageurl" x="12" y="12" width="76" height="76"
                        :style="(badgeinprogress)?'':'opacity: 0.4;'" />
                </svg></a>

                <b-modal
                    :id="'r-item-badge-details-'+value.id"
                    :title="value.badge.name"
                    size="lg"
                    ok-only
                    centered
                    scrollable
                    >
                    <template #modal-header>
                        <div>
                            <h1><i class="fa fa-certificate"></i>
                                <a :href="(!guestmode)?(value.badge.infolink):undefined" target="_blank"
                                >{{ value.badge.name }}</a
                            ></h1>
                        </div>
                        <div class="r-course-detail-header-right" v-if="!teachermode">
                            <div class="r-completion-detail-header">
                                {{ text['completion_'+completion] }}
                                <i v-b-popover.hover :class="'fa fa-'+issuedIcon+' r-completion-'+completion"
                                    :title="text['completion_'+completion]"></i>
                            </div>
                        </div>
                    </template>
                    <b-container fluid>
                        <b-row><b-col cols="3">
                            <img :src="value.badge.imageurl"/>
                        </b-col><b-col cols="9">
                            <p>{{value.badge.description}}</p>
                            <ul v-if="value.badge.issued" class="list-unstyled pt-1 mb-1 border-grey border-top">
                                <li><strong><i class="fa fa-calendar-check-o r-completion-complete-pass"></i>
                                    {{text.dateissued}}:</strong> {{ value.badge.dateissued }}</li>
                                <li v-if='value.badge.dateexpired'
                                ><strong><i class="fa fa-calendar-times-o r-completion-complete"></i>
                                    {{text.dateexpired}}:</strong> {{ value.badge.dateexpired }}</li>
                                <li><strong><i class="fa fa-share-alt r-completion-complete-pass"></i>
                                    <a :href="value.badge.issuedlink">{{text.share_badge}}</a></strong> </li>
                            </ul>
                            <table v-if='value.badge.completion && !value.badge.issued' class="r-item-course-grade-details mb-2">
                                <tr v-if="value.badge.completion.types.length > 1">
                                    <th colspan="2"><span v-html="value.badge.completion.title"></span></th>
                                </tr>
                                <template v-for='cgroup in value.badge.completion.types' >
                                    <tr>
                                        <td colspan="2" v-if="value.badge.completion.types.length > 1"
                                            ><span v-html="cgroup.title"></span></td>
                                        <th colspan="2" v-else><span v-html="cgroup.title"></span></th>
                                    </tr>
                                    <template v-for='ci in cgroup.criteria'>
                                        <tr>
                                            <td class="pl-3"><span v-if='guestmode'><span v-html="ci.title"></span></span>
                                                <a target='_blank' v-else-if='ci.link' :href='ci.link'
                                                    ><span v-html="ci.title"></span></a>
                                                <span v-else><span v-html="ci.title"></span></span>
                                            <td><i :class="'fa fa-'+completionIcon(ci.completed)+' r-completion-'+status(ci.completed)"
                                                :title="text['completion_'+status(ci.completed)]"></i>
                                            </td>
                                        </tr>
                                        <template v-if="ci.requirements.length > 1">
                                            <tr v-for="rq in ci.requirements">
                                                <td class="pl-4" colspan="2"
                                                    ><i :class="'fa fa-'+completionIconRq(rq.completed)+' r-completion-incomplete'"
                                                    :title="text['completion_'+status(rq.completed)]"></i>
                                                    <span class="t-badge-criteria-requirement"><span v-html="rq.title"></span></span></td>
                                            </tr>
                                        </template>
                                    </template>
                                </template>
                                <tr v-if="!value.badge.active" class="mt-1">
                                    <td colspan="2" class="alert alert-warning alert-block">{{text.badgedisabled}}</td>
                                </tr>
                            </table>
                            <ul class="list-unstyled w-100 border-grey border-top border-bottom pt-1 pb-1 mb-1"
                             v-if="value.badge.criteriatext"><li v-for="crit in value.badge.criteriatext"
                             ><span v-html='crit'></span></li></ul>
                            <p v-if="(!guestmode)"><strong><i class="fa fa-link"></i>
                                <a :href="value.badge.infolink" target="_blank"
                                    >{{ text.badgeinfo }}</a></strong></p>
                            <p v-if="teachermode && !guestmode"
                                >{{text.badgeissuedstats}}:<br>
                                <r-completion-bar v-model="issuestats" :width="150" :height="15"></r-completion-bar>
                            </p>
                        </b-col></b-row>
                    </b-container>
                </b-modal>
            </div>
            `,
        });

        Vue.component('r-item-dummy-course', {
            props: {
                value: {
                    type: Object,
                    default() {
                        return null;
                    },
                },
            },
            data() {
                return {
                    text: strings.invalid,
                };
            },
            methods: {
            },
            template: `
            <div class="r-item-dummy-course">
            <b-card no-body class="r-item-course">
                <b-row no-gutters>
                    <b-col md="1">
                        <span class="r-timing-indicator timing-dummy"></span>
                    </b-col>
                    <b-col md="11">
                        <b-card-body class="align-items-center">
                            
                        </b-card-body>
                    </b-col>
                </b-row>
            </b-card>
            </div>
            `,
        });

        Vue.component('r-item-dummy-filter', {
            props: {
            },
            data() {
                return {};
            },
            computed: {
            },
            methods: {
            },
            template: `
            <div :class="'r-item-dummy-filter'">
                <i class="fa fa-circle"></i>
            </div>
            `,
        });

        Vue.component('r-item-dummy-badge', {
            props: {
            },
            data() {
                return {};
            },
            computed: {
            },
            methods: {
            },
            template: `
            <div :class="'r-item-dummy-badge'" >
                <i class="fa fa-circle"></i>
            </div>
            `,
        });
    },
};