Index: lams_monitoring/web/includes/javascript/monitorLesson.js =================================================================== diff -u -r432fd5e118d63c8dab4bce32210b55f752cf0776 -rd30456574288df7fd628ad569ec5e74b23f971a3 --- lams_monitoring/web/includes/javascript/monitorLesson.js (.../monitorLesson.js) (revision 432fd5e118d63c8dab4bce32210b55f752cf0776) +++ lams_monitoring/web/includes/javascript/monitorLesson.js (.../monitorLesson.js) (revision d30456574288df7fd628ad569ec5e74b23f971a3) @@ -3,28 +3,18 @@ var originalSequenceCanvas = null, // DIV container for lesson SVG // it gets accessed so many times it's worth to cache it here - sequenceCanvas = $('#sequenceCanvas'), + sequenceCanvas = null, // switch between SVG original size and fit-to-sreen (enlarge/shrink) learningDesignSvgFitScreen = false, // info box show timeout sequenceInfoTimeout = 8000, // which learner was selected in the search box sequenceSearchedLearner = null, -// container for learners' progress bars metadata - bars = null, -// placeholder for single learner's progress bar and title - learnerProgressCellsTemplate = null, // for synchronisation purposes - learnersRefreshInProgress = false, sequenceRefreshInProgress = false, -// total number of learners with ongoing progress - learnerPossibleNumber = 0, -// page in Learners tab - learnerProgressCurrentPageNumber = 1, - //auto refresh all tabs every 30 seconds - autoRefreshInterval = 30 * 1000, + autoRefreshInterval = 99990 * 1000, autoRefreshIntervalObject = null, // when user is doing something, do not auto refresh autoRefreshBlocked = false, @@ -38,50 +28,110 @@ popupHeight = 720, gateOpenIconPath = 'images/svg/gateOpen.svg', - gateOpenIconData = null; + gateOpenIconData = null, + + fileDownloadCheckTimer; +$(document).ready(function(){ + initCommonElements(); + initSequenceTab(); + initGradebookTab(); + loadTab('sequence'); +}); +function loadTab(tabName, button) { + $('.navigate-btn, .lesson-properties').removeClass('active'); + $('.component-sidebar').removeClass('expanded'); + if (button) { + $(button).addClass('active'); + } + + let tabContent = $('.monitoring-page-content .tab-content'); + + if (tabName == 'sequence') { + tabContent.load(LAMS_URL + 'monitoring/monitoring/displaySequenceTab.do', function(){ + refreshMonitor('sequence'); + canvasFitScreen(learningDesignSvgFitScreen, true); + }); + } else if (tabName == 'learners') { + tabContent.load(LAMS_URL + 'monitoring/monitoring/displayLearnersTab.do', function(){ + refreshMonitor('learners'); + }); + } else if (tabName == 'gradebook') { + tabContent.load(LAMS_URL + 'monitoring/monitoring/displayGradebookTab.do', function(){ + refreshMonitor('gradebook'); + }); + } +} +function initCommonElements(){ + /* + $('.hamburger').click(function(){ + $(this).toggleClass('active'); + $('.component-sidebar, .monitoring-page-content').toggleClass('active'); + }); + */ + + $('#edit-lesson-btn').click(function(){ + $('.lesson-properties').toggleClass('active'); + $('.component-sidebar').toggleClass('expanded'); + }); + $('#load-sequence-tab-btn').click(function(){ + loadTab('sequence', this); + }); + + $('#load-learners-tab-btn').click(function(){ + loadTab('learners', this); + }); + + $('#load-gradebook-tab-btn').click(function(){ + loadTab('gradebook', this); + }); + + + initLessonTab(); +} + //********** LESSON TAB FUNCTIONS ********** /** * Sets up lesson tab. */ function initLessonTab(){ // sets presence availability. buttons may be temporarily disable by the tour. - $('#presenceButton').click(function(){ - var checked = $(this).toggleClass('btn-success').hasClass('btn-success'); - var data = { - 'presenceAvailable' : checked, - 'lessonID' : lessonId - }; + $('#presenceButton').change(function(){ + var checked = $(this).prop('checked'), + data = { + 'presenceAvailable' : checked, + 'lessonID' : lessonId + }; data[csrfTokenName] = csrfTokenValue; $.ajax({ url : LAMS_URL + 'monitoring/monitoring/presenceAvailable.do', type : 'POST', cache : false, data : data, success : function() { - updatePresenceAvailableCount(); + // updatePresenceAvailableCount(); if (checked) { - $('#imButton').show(); - $('#imButton').prop('disabled', false); - alert(LABELS.LESSON_PRESENCE_ENABLE_ALERT); + $('#imButtonWrapper').show(); + showToast(LABELS.LESSON_PRESENCE_ENABLE_ALERT); } else { - $('#imButton').removeClass('btn-success').hide(); - alert(LABELS.LESSON_PRESENCE_DISABLE_ALERT); + $('#imButtonWrapper, #openImButton').hide(); + $('#imButton').prop('checked', false); + showToast(LABELS.LESSON_PRESENCE_DISABLE_ALERT); } } }); }); // sets instant messaging availability $('#imButton').click(function(){ - var checked = $(this).toggleClass('btn-success').hasClass('btn-success'); - var data = { - 'presenceImAvailable' : checked, - 'lessonID' : lessonId - }; + var checked = $(this).prop('checked'), + data = { + 'presenceImAvailable' : checked, + 'lessonID' : lessonId + }; data[csrfTokenName] = csrfTokenValue; $.ajax({ url : LAMS_URL + 'monitoring/monitoring/presenceImAvailable.do', @@ -92,23 +142,24 @@ if (checked) { $('#openImButton').show(); $('#openImButton').prop('disabled', false); - alert(LABELS.LESSON_IM_ENABLE_ALERT); + showToast(LABELS.LESSON_IM_ENABLE_ALERT); } else { $('#openImButton').hide(); - alert(LABELS.LESSON_IM_DISABLE_ALERT); + showToast(LABELS.LESSON_IM_DISABLE_ALERT); } } }); }); $('#openImButton').click(openChatWindow); + //turn to inline mode for x-editable.js $.fn.editable.defaults.mode = 'inline'; // do not cancel on clicking outside of box $.fn.editable.defaults.onblur = 'ignore'; //enable renaming of lesson title - $('#lesson-name-strong').editable({ + $('#lesson-name').editable({ type: 'text', pk: lessonId, url: LAMS_URL + 'monitoring/monitoring/renameLesson.do?' + $("#csrf-form", window.parent.document).serialize(), @@ -156,7 +207,7 @@ } }, false); - $('.modal-body', classDialog).empty().append($('#classDialogContents').show()); + $('.modal-body', classDialog).empty().append($('#classDialogContents').show()).closest('.modal-dialog').addClass('modal-lg'); // search for users in the organisation with the term the Monitor entered $('.dialogSearchPhrase', classDialog).autocomplete({ @@ -196,7 +247,7 @@ 'height' : 500, 'width' : 510, 'title' : LABELS.PROGRESS_EMAIL_TITLE, - 'resizable' : true, + 'resizable' : false, 'open' : function(){ autoRefreshBlocked = true; }, @@ -209,9 +260,9 @@ $("#emaildatePicker").datetimepicker(); // sets gradebook on complete functionality - $('#gradebookOnCompleteButton').click(function(){ - var checked = $(this).toggleClass('btn-success').hasClass('btn-success'); - var data = { + $('#gradebookOnCompleteButton').change(function(){ + var checked = $(this).prop('checked'), + data = { 'gradebookOnComplete' : checked, 'lessonID' : lessonId }; @@ -223,9 +274,9 @@ data : data, success : function() { if (checked) { - alert(LABELS.LESSON_ACTIVITY_SCORES_ENABLE_ALERT); + showToast(LABELS.LESSON_ACTIVITY_SCORES_ENABLE_ALERT); } else { - alert(LABELS.LESSON_ACTIVITY_SCORES_DISABLE_ALERT); + showToast(LABELS.LESSON_ACTIVITY_SCORES_DISABLE_ALERT); } } }); @@ -262,7 +313,7 @@ $('#lessonStartApply').hide(); $('#lessonStateApply').hide(); $("#scheduleDisableLessonButton").html(LABELS.SCHEDULE); - $("#scheduleDisableLessonButton").css('display', 'inline'); // must be inline or it will be wrong size + $("#scheduleDisableLessonButton").css('display', 'block'); // must be inline or it will be wrong size $("#disableLessonButton").show(); $('#lessonDisableApply').show(); break; @@ -412,7 +463,7 @@ labelColour = 'danger'; break; } - $('#lessonStateLabel').attr('class', 'label label-' + labelColour).html(label + ' '); + $('#lessonStateLabel').attr('class', 'badge bg-' + labelColour).html(label + ' '); // update available options in change state dropdown menu var selectField = $('#lessonStateField'); @@ -498,11 +549,136 @@ } }); - drawChart('pie', 'chartDiv', - LAMS_URL + 'monitoring/monitoring/getLessonChartData.do?lessonID=' + lessonId, - true); + /* + drawChart('pie', 'chartDiv', + LAMS_URL + 'monitoring/monitoring/getLessonChartData.do?lessonID=' + lessonId, + true); + + updatePresenceAvailableCount(); + */ +} + +function drawLessonCompletionChart(){ + d3.json(LAMS_URL + 'monitoring/monitoring/getLessonChartData.do?lessonID=' + lessonId, + function(error, response){ + if (error) { + // forward error to browser + throw error; + } + + if (!response || $.isEmptyObject(response)) { + // if there is no data to display + return; + } + + let chartDiv = $('#completion-chart'), + lessonCompletionChart = chartDiv.data('chart'), + percent = [], + labels = [], + raw = []; + + $(response.data).each(function(){ + labels.push(this.name); + percent.push(this.value); + raw.push(this.raw); + }); + + if (lessonCompletionChart != null) { + // chart already exists, just update data + lessonCompletionChart.data.datasets[0].data = percent; + lessonCompletionChart.lessonCompletionChartRawData = raw; + lessonCompletionChart.update(); + return; + } - updatePresenceAvailableCount(); + let ctx = chartDiv[0].getContext('2d'); + lessonCompletionChart = new Chart(ctx, { + type : 'doughnut', + borderWidth : 0, + data : { + elements : { + arc : { + borderWidth : 0, + fontSize : 0, + } + }, + datasets : [ { + data : percent, + backgroundColor : [ 'rgba(255, 195, 55, 1)', + 'rgba(253, 60, 165, 1)', + 'rgba(5, 204, 214, 1)' + ], + borderWidth : 0 + } ], + labels : labels + }, + options : { + tooltips : { + enabled : true, + callbacks: { + label : function(tooltipItem, data) { + let index = tooltipItem.index, + + rawData = this._chart.lessonCompletionChartRawData, + percent = data.datasets[0].data, + + label = labels[index], + value = percent[index], + rawValue = rawData[index]; + + return label + ": " + rawValue + " (" + value + "%)"; + } + } + }, + legend : { + position: 'bottom', + align: 'start', + labels : { + generateLabels : function(chart) { + var data = chart.data; + if (data.labels.length && data.datasets.length) { + return data.labels.map(function(label, i) { + let meta = chart.getDatasetMeta(0), + style = meta.controller.getStyle(i), + value = data.datasets[0].data[i], + rawData = chart.lessonCompletionChartRawData || raw, + rawValue = rawData[i]; + + return { + text: label + ": " + rawValue + " (" + value + "%)", + fillStyle: style.backgroundColor, + strokeStyle: style.borderColor, + lineWidth: style.borderWidth, + hidden: isNaN(value) || meta.data[i].hidden, + + // Extra data used for toggling the + // correct item + index: i + }; + }); + } + return []; + } + } + }, + animation : { + animateScale : true, + animateRotate : true, + duration : 1000 + } + }, + plugins: [{ + beforeInit: function(chart) { + chart.legend.afterFit = function() { + this.height = this.height + 250; + }; + } + }] + }); + + lessonCompletionChart.lessonCompletionChartRawData = raw; + chartDiv.data('chart', lessonCompletionChart); + }); } function checkScheduleDate(startDateString, endDateString) { @@ -608,9 +784,9 @@ }, false, true); } - +/* function updatePresenceAvailableCount(){ - var checked = $('#presenceButton').hasClass('btn-success'), + var checked = $('#presenceButton').prop('checked'); counter = $('#presenceCounter'); if (checked) { $.ajax({ @@ -628,12 +804,20 @@ counter.hide(); } } +*/ function updateContributeActivities(contributeActivities) { - $('.contributeRow').remove(); - var header = $('#contributeHeader'), - row = header; + let requiredTasksPanel = $('#required-tasks'), + requiredTasksContent = $('#required-tasks-content', requiredTasksPanel); + + if (!contributeActivities || contributeActivities.length === 0) { + requiredTasksPanel.remove(); + return; + } + $('.contribute-row', requiredTasksContent).remove(); + + /* // special case - add a Live Edit option. This does not directly map to an activity if ( lockedForEdit && lockedForEditUserId == userId) { // show Live Edit task only if currently editing myself, not if someone else is editing. @@ -646,62 +830,63 @@ cell = $('
').addClass('contributeEntryCell').html(entryContent); row = row.append(cell); } + */ + if (contributeActivities) { $.each(contributeActivities, function(){ - var contributeId = 'contribute' + this.activityID, - contributeActivity = this, - cell = $('
').addClass('contributeActivityCell').text(this.title).attr('id', contributeId); - row = $('
').addClass('contributeRow').insertAfter(row).append(cell); + let contributeActivity = this; + let row = $('
').addClass('row contribute-row' + (contributeActivity.title ? ' ml-1' : '')) + .appendTo(requiredTasksContent); + $.each(this.contributeEntries, function(){ - var entryContent = ''; + var entryContent = '
' + (contributeActivity.title ? '' + contributeActivity.title + '
(' : ''); switch (this.contributionType) { - case 3 : entryContent = LABELS.CONTRIBUTE_GATE; break; - case 6 : entryContent = LABELS.CONTRIBUTE_GROUPING; break; - case 7 : entryContent = LABELS.CONTRIBUTE_TOOL; break; - case 9 : entryContent = LABELS.CONTRIBUTE_BRANCHING; break; - case 11 : entryContent = LABELS.CONTRIBUTE_CONTENT_EDITED; break; - case 12 : entryContent = LABELS.CONTRIBUTE_GATE_PASSWORD; break; + case 3 : entryContent += LABELS.CONTRIBUTE_GATE; break; + case 6 : entryContent += LABELS.CONTRIBUTE_GROUPING; break; + case 7 : entryContent += LABELS.CONTRIBUTE_TOOL; break; + case 9 : entryContent += LABELS.CONTRIBUTE_BRANCHING; break; + case 11 : entryContent += LABELS.CONTRIBUTE_CONTENT_EDITED; break; + case 12 : entryContent += LABELS.CONTRIBUTE_GATE_PASSWORD; break; } + if (contributeActivity.title) { + entryContent += ')'; + } + entryContent += '
'; switch (this.contributionType) { case 3 : case 12 : if (this.isComplete) { - entryContent += '
'; } else { - entryContent += '
' - + '
'; + + LABELS.CONTRIBUTE_OPEN_GATE_BUTTON + '
'; + } break; - default : entryContent += '' + LABELS.CONTRIBUTE_BUTTON + ''; + + '">' + LABELS.CONTRIBUTE_BUTTON + ''; } - cell = $('
').addClass('contributeEntryCell').html(entryContent); - row = row.append(cell); + row.html(entryContent + "
"); }); }); - } - - if ($('.contributeRow').length == 0) { - $('#requiredTasks').hide(); + + requiredTasksPanel.show(); } else { - $('#requiredTasks').show(); + requiredTasksPanel.hide(); } } @@ -789,13 +974,16 @@ return; // does not exist so add to list - var checkbox = $('').attr({ - 'type' : 'checkbox' - }).change(function(){ - editEmailProgressDate($(this)); - }), + var checkboxId = 'email-progress-date-' + dateObj.id, + checkbox = $('').attr({ + 'type' : 'checkbox', + 'id' : checkboxId + }).addClass('form-check-input me-1') + .change(function(){ + editEmailProgressDate($(this)); + }), - dateString = $('').html(dateObj.date), + dateString = $('