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_NOW_BUTTON + ''
- + 'Toggle Dropdown
';
+
}
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 = $('').addClass('form-check-label').attr('for', checkboxId).text(dateObj.date),
dateDiv = $('').attr({
'dateid' : dateObj.id,
@@ -815,6 +1003,7 @@
$.ajax({
dataType : 'json',
url : LAMS_URL + 'monitoring/emailProgress/sendLessonProgressEmail.do',
+ type: 'post',
cache : false,
data : {
'lessonID' : lessonId
@@ -1156,7 +1345,12 @@
$('#sequenceInfoDialog .modal-body').empty().append($('#sequenceInfoDialogContents').show());
- canvasFitScreen(learningDesignSvgFitScreen, true);
+ const learnerProgressUpdateSource = new EventSource(LAMS_URL + 'monitoring/monitoring/getLearnerProgressUpdateFlux.do?lessonId=' + lessonId);
+ learnerProgressUpdateSource.onmessage = function (event) {
+ if ("doRefresh" == event.data && $('#sequence-tab-content').length === 1){
+ updateSequenceTab();
+ }
+ }
}
function showIntroductionDialog(lessonId) {
@@ -1181,6 +1375,7 @@
autoRefreshBlocked = false;
$('#introductionDialog').remove();
}
+
/**
* Updates learner progress in sequence tab according to respose sent to refreshMonitor()
*/
@@ -1190,6 +1385,9 @@
}
sequenceRefreshInProgress = true;
+ drawLessonCompletionChart();
+
+ sequenceCanvas = $('#sequenceCanvas');
sequenceCanvas.css('visibility', 'hidden');
if (originalSequenceCanvas) {
@@ -1204,8 +1402,8 @@
}
}
- // clear all completed learner icons except the door
- $('#completedLearnersContainer :not(img#completedLessonLearnersIcon)').remove();
+ // clear all learner icons
+ $('.learner-icon, .more-learner-icon', '#canvas-container').remove();
var sequenceTopButtonsContainer = $('#sequenceTopButtonsContainer');
if ($('img#sequenceCanvasLoading', sequenceTopButtonsContainer).length == 0){
@@ -1283,8 +1481,6 @@
// modyfing SVG in DOM does not render changes, so we need to reload it
sequenceCanvas.html(sequenceCanvas.html());
- // SVG needs resizing after reload
- window.parent.resizeSequenceCanvas();
// only now show SVG so there is no "jump" when resizing
sequenceCanvas.css('visibility', 'visible');
@@ -1297,7 +1493,7 @@
}
var learnerTotalCount = learnerCount + response.completedLearnerCount;
- $('#learnersStartedPossibleCell').html(''+learnerTotalCount + ' / ' + response.numberPossibleLearners+'');
+ // $('#learnersStartedPossibleCell').html(''+learnerTotalCount + ' / ' + response.numberPossibleLearners+'');
addCompletedLearnerIcons(response.completedLearners, response.completedLearnerCount, learnerTotalCount);
$.each(response.activities, function(activityIndex, activity){
@@ -2031,17 +2227,20 @@
togglePagingCells(table, ajaxProperties.data.pageNumber, Math.ceil(userCount / 10));
$.each(users, function(userIndex, user) {
- var checkbox = $('').attr({
- 'type' : 'checkbox'
- }).change(function(){
- editClassMember($(this));
+ var checkboxId = 'class-list-' + role + '-' + user.id,
+ checkbox = $('').attr({
+ 'type' : 'checkbox',
+ 'id' : checkboxId
+ }).addClass('form-check-input me-1')
+ .change(function(){
+ editClassMember($(this));
}),
userDiv = $('').attr({
'userId' : user.id
})
.addClass('dialogListItem')
- .html(getLearnerDisplayName(user))
+ .prepend($('').addClass('form-check-label').attr('for', checkboxId).text(getLearnerDisplayName(user)))
.prepend(checkbox)
.appendTo(list);
@@ -2183,217 +2382,126 @@
}
}
-//********** LEARNERS TAB FUNCTIONS **********
/**
- * Inits Learners tab widgets.
+ * Refreshes the existing progress bars.
*/
-function initLearnersTab() {
- // search for users with the term the Monitor entered
- $("#learnersSearchPhrase").autocomplete( {
- 'source' : LAMS_URL + "monitoring/monitoring/autocomplete.do?scope=lesson&lessonID=" + lessonId,
- 'delay' : 700,
- 'select' : function(event, ui){
- // learner's ID in ui.item.value is not used here
- $(this).val(ui.item.label);
- $('#learnersSearchPhraseClear').show();
- loadLearnerProgressPage(1, ui.item.label);
- return false;
+function updateLearnersTab(){
+ let learnersAccordion = $('#learners-accordion').empty(),
+ itemTemplate = $('.learners-accordion-item-template').clone().removeClass('learners-accordion-item-template d-none');
+
+ $.ajax({
+ 'url' : LAMS_URL + 'monitoring/monitoring/getLearnerProgressPage.do',
+ 'data': {
+ lessonID: lessonId
+ },
+ 'dataType' : 'json',
+ 'success' : function(response) {
+ let learnerProgressSource = null;
+
+ $(response.learners).each(function(){
+ let learner = this,
+ itemHeaderId = 'learners-accordion-heading-' + learner.id,
+ itemCollapseId = 'learners-accordion-collapse-' + learner.id,
+ item = itemTemplate.clone().data('user-id', learner.id).attr('id', 'learners-accordion-item-' + learner.id).appendTo(learnersAccordion),
+ portraitSmall = $(definePortrait(learner.portraitId, learner.id, STYLE_SMALL, true, LAMS_URL)).addClass('me-2'),
+ portraitLarge = learner.portraitId ? $(definePortrait(learner.portraitId, learner.id, STYLE_LARGE, false, LAMS_URL)) : null
+
+ $('.accordion-header', item).attr('id', itemHeaderId)
+ .find('.accordion-button').attr('data-bs-target', '#' + itemCollapseId).attr('aria-controls', itemCollapseId)
+ .html('' + learner.firstName + ' ' + learner.lastName + '')
+ .prepend(portraitSmall);
+ $('.learners-accordion-name', item).text(learner.firstName + ' ' + learner.lastName);
+ $('.learners-accordion-login', item).html('' + learner.login);
+ $('.learners-accordion-email', item).html('' + learner.email);
+ if (portraitLarge) {
+ $('.learners-accordion-portrait', item).append(portraitLarge);
+ }
+
+ $('.accordion-collapse', item).attr('id', itemCollapseId).attr('data-bs-parent', '#learners-accordion')
+ .on('show.bs.collapse', function () {
+ if (learnerProgressSource) {
+ try {
+ learnerProgressSource.close();
+ } catch(e) {
+ console.error(e);
+ }
+ }
+
+ let learnerId = $(this).closest('.accordion-item').data('user-id');
+ learnerProgressSource = new EventSource(LAMS_URL + 'learning/learner/getLearnerProgressUpdateFlux.do?lessonId='
+ + lessonId + '&userId=' + learnerId);
+
+ learnerProgressSource.onmessage = function (event) {
+ if ($('#learners-accordion-item-' + learnerId).length === 1) {
+ drawLearnerTimeline(learnerId, event.data);
+ }
+ }
+ });
+ });
}
- })
- // run the real search when the Monitor presses Enter
- .keypress(function(e){
- if (e.which == 13) {
- $(this).autocomplete("close");
- if ($(this).val()) {
- $('#learnersSearchPhraseClear').show();
- }
- loadLearnerProgressPage(1);
- }
});
}
-
-/**
- * Handler for shift page numbers bar.
- */
-function learnersPageShift(increment){
- var pageNumberCell = $('#learnersPageLeft').next();
- if (pageNumberCell.hasClass('learnersHeaderPageCell')) {
- var startIndex = +pageNumberCell.text() + (increment ? 10 : -10);
- var endIndex = startIndex + 9;
- shiftLearnerProgressPageHeader(startIndex, endIndex);
- }
-}
-
-
-/**
- * Do the actual shifting of page numbers bar.
- */
-function shiftLearnerProgressPageHeader(startIndex, endIndex) {
- var pageLeftCell = $('#learnersPageLeft');
- var pageCount = Math.ceil(learnerPossibleNumber / 10);
- $('#tabLearnerControlTable td.learnersHeaderPageCell').remove();
+function drawLearnerTimeline(learnerId, data) {
+ let item = $('#learners-accordion-item-' + learnerId),
+ timelineContainer = $('.vertical-timeline-container', item),
+ timeline = $('.vertical-timeline', timelineContainer).empty(),
+ noProgressLabel = $('.no-progress', item);
- if (startIndex < 1) {
- startIndex = 1;
- endIndex = 10;
- }
- if (endIndex > pageCount) {
- // put a bit more on the left, since right end of scale is too short
- startIndex -= endIndex - pageCount;
- if (startIndex < 1) {
- startIndex = 1;
- }
- endIndex = pageCount;
- }
-
- for (var pageIndex = endIndex; pageIndex >= startIndex; pageIndex--) {
- // produce the page number cells
- var pageHeaderCell =
- $('')
- .text(pageIndex)
- .insertAfter(pageLeftCell)
- .click(function(){
- loadLearnerProgressPage(+$(this).text());
- });
- if (pageIndex == learnerProgressCurrentPageNumber){
- // highlight the currently selected one
- pageHeaderCell.addClass('selectedLearnersHeaderPageCell');
- }
- }
-}
-
-/**
- * After page change, refresh values in the control bar.
- */
-function updateLearnerProgressHeader(pageNumber) {
- var controlRow = $('#tabLearnerControlTable tr'),
- learnersSearchPhrase = $('#learnersSearchPhrase').val();
- if (learnerPossibleNumber < 10 && (!learnersSearchPhrase || learnersSearchPhrase.trim() == '')) {
- // do not show the bar at all
- $('.learnersHeaderCell', controlRow).hide();
+ if (!data) {
+ noProgressLabel.show();
return;
}
- // show the bar
- $('.learnersHeaderCell', controlRow).show();
+ noProgressLabel.hide();
+ data = JSON.parse(data);
+ let activityEntryTemplate = $('.learners-timeline-entry-template').clone().removeClass('learners-timeline-entry-template d-none');
- var pageCount = Math.ceil(learnerPossibleNumber / 10);
- if (!pageNumber) {
- pageNumber = 1;
- } else if (pageNumber > pageCount) {
- pageNumber = pageCount;
- }
- learnerProgressCurrentPageNumber = pageNumber;
- // update "Page X / Y" field
- $('#learnersPageCounter').html(pageNumber + ' / ' + pageCount + ' ');
-
- // remove arrows for shifting page numbers, if they are not needed
- if (pageCount < 10) {
- $('td.learnersPageShifter', controlRow).hide();
- }
-
- // calculate currently visible page numbers
- var pageStartIndex = pageNumber - 5,
- pageEndIndex = pageNumber + 5 - Math.min(pageStartIndex,0);
- shiftLearnerProgressPageHeader(pageStartIndex, pageEndIndex, pageNumber);
-}
-
-/**
- * Load the give page of learners' progress.
- */
-function loadLearnerProgressPage(pageNumber, learnersSearchPhrase){
-
- // prevent double refresh at the same time
- if (learnersRefreshInProgress) {
- return;
- }
- learnersRefreshInProgress = true;
-
- if (!learnerProgressCellsTemplate) {
- // fill the placeholder, after all required variables were initialised
- learnerProgressCellsTemplate =
- '' +
- ';11;';
-
- learnerProgressCellsTemplate +=
- ' '
- + LABELS.EMAIL_BUTTON
- + ' |
|
';
- }
-
- // remove existing progress bars
- $('#tabLearnersTable').html(null);
- bars = {};
- var isProgressSorted = $('#orderByCompletionCheckbox:checked').length > 0;
- // either go to the given page or refresh the current one
- pageNumber = pageNumber || learnerProgressCurrentPageNumber;
- learnersSearchPhrase = learnersSearchPhrase || $('#learnersSearchPhrase').val();
- if (learnersSearchPhrase && learnersSearchPhrase.trim() == ''){
- learnersSearchPhrase = null;
- }
-
- $.ajax({
- dataType : 'json',
- url : LAMS_URL + 'monitoring/monitoring/getLearnerProgressPage.do',
- cache : false,
- data : {
- 'lessonID' : lessonId,
- 'searchPhrase' : learnersSearchPhrase,
- 'pageNumber' : pageNumber,
- 'isProgressSorted' : isProgressSorted
+ $(data.activities).each(function(){
+ let activity = this,
+ entry = activityEntryTemplate.clone().appendTo(timeline),
+ icon = $('.timeline-icon', entry),
+ iconURL = null,
+ durationCell = $('.timeline-activity-duration', entry),
+ markCell = $('.timeline-activity-mark', entry);
- },
-
- success : function(response) {
- learnerPossibleNumber = response.learnerPossibleNumber;
- updateLearnerProgressHeader(pageNumber);
+ $('.timeline-title', entry).text(activity.name);
- if (response.learners) {
- $.each(response.learners, function(){
- var barId = 'bar' + this.id;
- // create a new bar metadata entry
- bars[barId] = {
- 'userId' : this.id,
- 'containerId' : 'progressBar' + this.id
- };
-
- // prepare HTML for progress bar
- var learnerProgressCellsInstance = learnerProgressCellsTemplate
- .replace(/;00;/g, this.id)
- .replace(/;11;/g, getLearnerDisplayName(this));
- $(learnerProgressCellsInstance).appendTo('#tabLearnersTable');
- addPortrait($("#portrait-"+this.id), this.portraitId, this.id, 'small', true, LAMS_URL );
-
- // request data to build progress bar SVG
- fillProgressBar(barId);
- });
- }
-
- learnersRefreshInProgress = false;
+ switch(activity.status){
+ case 0: icon.addClass('border-primary activity-current');break;
+ case 1: icon.addClass('border-success activity-complete');break;
}
+
+ if (activity.iconURL) {
+ iconURL = activity.iconURL;
+ } else if (activity.type === 'g') {
+ iconURL = 'images/svg/gateClosed.svg';
+ } else if (activity.type === 'o') {
+ iconURL = 'images/svg/branchingStart.svg';
+ } else if (activity.isGrouping) {
+ iconURL = 'images/svg/grouping.svg';
+ }
+
+ if (iconURL) {
+ $('
').attr('src', LAMS_URL + iconURL).appendTo(icon);
+ }
+
+ if (typeof activity.mark !== 'undefined') {
+ markCell.text(activity.mark + (activity.maxMark ? ' / ' + activity.maxMark : ''));
+ } else {
+ markCell.closest('tr').remove();
+ }
+
+ if (activity.duration) {
+ durationCell.text(activity.duration);
+ } else {
+ durationCell.closest('tr').remove();
+ }
});
-
-}
-
-
-/**
- * Refreshes the existing progress bars.
- */
-function updateLearnersTab(){
- // prevent double refresh
- if (learnersRefreshInProgress) {
- return;
- }
- learnersRefreshInProgress = true;
- for (var barId in bars) {
- fillProgressBar(barId);
- }
- learnersRefreshInProgress = false;
+ timelineContainer.show();
}
-
/**
* Clears previous run search for phrase, in Learners tab.
*/
@@ -2425,28 +2533,165 @@
showLearnerGroupDialog();
$('.dialogSearchPhraseClear', dialog).css('visibility', 'hidden');
}
-
//********** GRADEBOOK TAB FUNCTIONS **********
/**
* Inits Gradebook Tab.
*/
function initGradebookTab() {
- $("#gradebookLoading").hide();
+ /*
+ $.extend(true, $.jgrid.icons, {
+ fontAwesome6: $.extend(true, {}, $.jgrid.icons.fontAwesome, {
+ nav: { del: "fa-times" },
+ actions: { del: "fa-times" },
+ form: { del: "fa-times" }
+ })
+ $.extend(true, $.jgrid.icons.fontAwesome, {
+ common : "fa-solid"
+ });
+ });
+ */
+ $.extend(true, $.jgrid.guiStyles.bootstrap4, {
+ pager : {
+ pagerSelect : 'form-control-select'
+ },
+ searchToolbar : {
+ clearButton : 'btn btn-sm'
+ }
+ });
}
/**
* Refreshes Gradebook Tab.
*/
function updateGradebookTab() {
- // $("#gradebookLoading").show();
- $("#gradebookDiv").load(LAMS_URL + 'gradebook/gradebookMonitoring.do?lessonID=' + lessonId, function() {
+ $("#gradebookLoading").show();
+ $("#gradebookDiv").load(LAMS_URL + 'gradebook/gradebookMonitoring.do?isTab=true&lessonID=' + lessonId, function() {
$("#gradebookLoading").hide();
});
}
+
+function fixPagerInCenter(pagername, numcolshift) {
+ $('#'+pagername+'_right').css('display','inline');
+ if ( numcolshift > 0 ) {
+ var marginshift = - numcolshift * 12;
+ $('#'+pagername+'_center table').css('margin-left', marginshift+'px');
+ }
+}
+
+var popupWidth = 1280,
+ popupHeight = 720;
+
+// launches a popup from the page
+function launchPopup(url,title) {
+ var wd = null;
+ if(wd && wd.open && !wd.closed){
+ wd.close();
+ }
+
+ var left = ((screen.width / 2) - (popupWidth / 2));
+ var top = ((screen.height / 2) - (popupHeight / 2));
+
+ wd = window.open(url,title,'resizable,width='+popupWidth+',height='+popupHeight
+ +',scrollbars'
+ + ",top=" + top + ",left=" + left);
+ wd.window.focus();
+}
+
+/* gradebook dialog windows on the ipad do not update the grid width properly using setGridWidth. Calling this is
+-- setting the grid to parentWidth-1 and the width of the parent to parentWidth+1, leading to growing width window
+-- that overflows the dialog window. Keep the main grids slightly smaller than their containers and all is well.
+*/
+
+function resizeJqgrid(jqgrids) {
+ jqgrids.each(function(index) {
+ var gridId = $(this).attr('id');
+ var parent = jQuery('#gbox_' + gridId).parent();
+ var gridParentWidth = parent.width();
+ if ( parent.hasClass('grid-holder') ) {
+ gridParentWidth = gridParentWidth - 2;
+ }
+ jQuery('#' + gridId).setGridWidth(gridParentWidth, true);
+ });
+}
+
+
+/* Based on jqgrid internal functions */
+function displayCellErrorMessage(table, iRow, iCol, errorLabel, errorMessage, buttonText ) {
+ setTimeout(function () {
+ try {
+ var frozenRows = table.grid.fbRows,
+ tr = table.rows[iRow];
+ tr = frozenRows != null && frozenRows[0].cells.length > iCol ? frozenRows[tr.rowIndex] : tr;
+ var td = tr != null && tr.cells != null ? $(tr.cells[iCol]) : $(),
+ rect = td[0].getBoundingClientRect();
+ $.jgrid.info_dialog.call(table, errorLabel, errorMessage, buttonText, {left:rect.left-200, top:rect.top});
+ } catch (e) {
+ alert(errorMessage);
+ }
+ }, 50);
+}
+
+function blockExportButton(areaToBlock, exportExcelUrl) {
+ var token = new Date().getTime(),
+ area = $('#' + areaToBlock).css('cursor', 'wait'),
+ buttons = $('.btn', area).prop('disabled', true),
+ form = $('');
+
+ fileDownloadCheckTimer = window.setInterval(function () {
+ var cookieValue = $.cookie('fileDownloadToken');
+ if (cookieValue == token) {
+ //unBlock export button
+ window.clearInterval(fileDownloadCheckTimer);
+ $.cookie('fileDownloadToken', null); //clears this cookie value
+
+ area.css('cursor', 'auto');
+ buttons.prop('disabled', false);
+ form.remove();
+ }
+ }, 1000);
+
+ //dynamically create a form and submit it
+ form.attr("method", "post");
+ form.attr("action", exportExcelUrl);
+
+ var hiddenField = $('');
+ hiddenField.attr("type", "hidden");
+ hiddenField.attr("name", "downloadTokenValue");
+ hiddenField.attr("value", token);
+ form.append(hiddenField);
+
+ // The form needs to be a part of the document in order to be submitted
+ $(document.body).append(form);
+ form.submit();
+
+ return false;
+}
+
//********** COMMON FUNCTIONS **********
+
+// generic function for opening a pop up
+function openPopUp(url, title, h, w, status, forceNewWindow) {
+
+ var width = screen.width;
+ var height = screen.height;
+
+ var left = ((width / 2) - (w / 2));
+ var top = ((height / 2) - (h / 2));
+
+ if (forceNewWindow) {
+ // opens a new window rather than loading content to existing one
+ title += new Date().getTime();
+ }
+
+ window.open(url, title, "HEIGHT=" + h + ",WIDTH=" + w
+ + ",resizable=yes,scrollbars=yes,status=" + status
+ + ",menubar=no, toolbar=no"
+ + ",top=" + top + ",left=" + left);
+}
+
function isAutoRefreshBlocked(){
return autoRefreshBlocked || $('#learnerGroupDialog').hasClass('in');
}
@@ -2468,20 +2713,17 @@
}, autoRefreshInterval);
}
+ // update Lesson tab widgets (state, number of learners etc.)
+ updateLessonTab();
if (!tabName) {
- // update Lesson tab widgets (state, number of learners etc.)
updateLessonTab();
// update learner progress in Sequence tab
updateSequenceTab();
// update learner progress in Learners tab
- loadLearnerProgressPage();
- } else if (tabName == 'lesson') {
- updateLessonTab();
+ // loadLearnerProgressPage();
} else if (tabName == 'sequence'){
- updateLessonTab();
updateSequenceTab();
} else if (tabName == 'learners'){
- updateLessonTab();
updateLearnersTab();
} else if (tabName == 'gradebook'){
updateGradebookTab();
@@ -2628,7 +2870,7 @@
if (!isRefresh) {
// show buttons and labels depending on parameters
- $('span#learnerGroupMultiSelectLabel, button#learnerGroupDialogForceCompleteButton', learnerGroupDialog)
+ $('span#learnerGroupMultiSelectLabel, button#learnerGroupDialogForceCompleteButton, button#learnerGroupDialogForceCompleteAllButton', learnerGroupDialog)
.css('display', allowForceComplete ? 'inline' : 'none');
$('button#learnerGroupDialogViewButton', learnerGroupDialog)
.css('display', allowView ? 'inline' : 'none');
@@ -2790,6 +3032,13 @@
}
}
+function showToast(text) {
+ let toast = $('#toast-template').clone().attr('id', null).appendTo('#toast-container');
+ toast.find('.toast-body', toast).text(text);
+ toast = new bootstrap.Toast(toast[0]);
+ toast.show();
+}
+
/**
* Works as dblclick for mobile devices.
*/
Index: lams_monitoring/web/includes/javascript/monitorLesson5.js
===================================================================
diff -u -r20e62542c73330b5ccd425dc727597660abdd129 -rd30456574288df7fd628ad569ec5e74b23f971a3
--- lams_monitoring/web/includes/javascript/monitorLesson5.js (.../monitorLesson5.js) (revision 20e62542c73330b5ccd425dc727597660abdd129)
+++ lams_monitoring/web/includes/javascript/monitorLesson5.js (.../monitorLesson5.js) (revision d30456574288df7fd628ad569ec5e74b23f971a3)
@@ -1347,7 +1347,7 @@
const learnerProgressUpdateSource = new EventSource(LAMS_URL + 'monitoring/monitoring/getLearnerProgressUpdateFlux.do?lessonId=' + lessonId);
learnerProgressUpdateSource.onmessage = function (event) {
- if ("doRefresh" == event.data){
+ if ("doRefresh" == event.data && $('#sequence-tab-content').length === 1){
updateSequenceTab();
}
}
@@ -2433,7 +2433,9 @@
+ lessonId + '&userId=' + learnerId);
learnerProgressSource.onmessage = function (event) {
- drawLearnerTimeline(learnerId, event.data);
+ if ($('#learners-accordion-item-' + learnerId).length === 1) {
+ drawLearnerTimeline(learnerId, event.data);
+ }
}
});
});