diff --git a/amd/build/dashboard_quiz_inprogress.min.js b/amd/build/dashboard_quiz_inprogress.min.js index de18c4aa..fabbb74c 100644 --- a/amd/build/dashboard_quiz_inprogress.min.js +++ b/amd/build/dashboard_quiz_inprogress.min.js @@ -1,2 +1,2 @@ -define ("local_assessfreq/dashboard_quiz_inprogress",["core/ajax","core/templates","core/fragment","local_assessfreq/zoom_modal","core/str","core/notification"],function(a,b,c,d,e,f){var g={},h,i=60,j,k="name_asc",l=0,m=0,n=[{cardId:"local-assessfreq-quiz-summary-upcomming-graph",call:"upcomming_quizzes",aspect:!0},{cardId:"local-assessfreq-quiz-summary-inprogress-graph",call:"all_participants_inprogress",aspect:!0}],o=function(b,c){return a.call([{methodname:"core_user_update_user_preferences",args:{preferences:[{type:b,value:c}]}}])[0]},p=function(b){return a.call([{methodname:"core_user_get_user_preferences",args:{name:b}}])[0]},q=function debouncer(a,b){var c;return function(){for(var d=arguments.length,e=Array(d),f=0;f.\n\n/**\n * Javascript for quizzes in progress display and processing.\n *\n * @package local_assessfreq\n * @copyright 2020 Matt Porritt \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\ndefine(['core/ajax', 'core/templates', 'core/fragment', 'local_assessfreq/zoom_modal', 'core/str', 'core/notification'],\nfunction(Ajax, Templates, Fragment, ZoomModal, Str, Notification) {\n\n /**\n * Module level variables.\n */\n var DashboardQuizInprogress = {};\n var contextid;\n var refreshPeriod = 60;\n var counterid;\n var tablesort = 'name_asc';\n var hoursAhead = 0;\n var hoursBehind = 0;\n\n const cards = [\n {cardId: 'local-assessfreq-quiz-summary-upcomming-graph', call: 'upcomming_quizzes', aspect: true},\n {cardId: 'local-assessfreq-quiz-summary-inprogress-graph', call: 'all_participants_inprogress', aspect: true}\n ];\n\n /**\n * Generic handler to persist user preferences.\n *\n * @param {string} type The name of the attribute you're updating\n * @param {string} value The value of the attribute you're updating\n * @return {object} jQuery promise\n */\n const setUserPreference = function(type, value) {\n var request = {\n methodname: 'core_user_update_user_preferences',\n args: {\n preferences: [{type: type, value: value}]\n }\n };\n\n return Ajax.call([request])[0];\n };\n\n /**\n * Generic handler to get user preference.\n *\n * @param {string} name The name of the attribute you're getting.\n * @return {object} jQuery promise\n */\n const getUserPreference = function(name) {\n var request = {\n methodname: 'core_user_get_user_preferences',\n args: {\n 'name': name\n }\n };\n\n return Ajax.call([request])[0];\n };\n\n /**\n * Quick and dirty debounce method for the settings.\n * This stops the ajax method that updates the table from being updated\n * while the user is still checking options.\n *\n */\n const debouncer = function (func, wait) {\n let timeout;\n\n return function executedFunction(...args) {\n const later = () => {\n clearTimeout(timeout);\n func(...args);\n };\n\n clearTimeout(timeout);\n timeout = setTimeout(later, wait);\n };\n };\n\n const debounceTable = debouncer(() => {\n getSummaryTable();\n }, 750);\n\n /**\n *\n */\n const refreshCounter = function(reset) {\n let progressElement = document.getElementById('local-assessfreq-period-progress');\n\n // Reset the current count process.\n if (reset == true) {\n clearInterval(counterid);\n counterid = null;\n progressElement.setAttribute('style', 'width: 100%');\n progressElement.setAttribute('aria-valuenow', 100);\n }\n\n // Exit early if there is already a counter running.\n if (counterid) {\n return;\n }\n\n counterid = setInterval(() => {\n let progressWidthAria = progressElement.getAttribute('aria-valuenow');\n const progressStep = 100 / refreshPeriod;\n\n if ((progressWidthAria - progressStep) > 0) {\n progressElement.setAttribute('style', 'width: ' + (progressWidthAria - progressStep) + '%');\n progressElement.setAttribute('aria-valuenow', (progressWidthAria - progressStep));\n } else {\n clearInterval(counterid);\n counterid = null;\n progressElement.setAttribute('style', 'width: 100%');\n progressElement.setAttribute('aria-valuenow', 100);\n processDashboard();\n refreshCounter();\n }\n }, (1000));\n };\n\n /**\n * Process the search events from the quiz table.\n */\n const tableSearch = function(event) {\n if (event.key === 'Meta' || event.ctrlKey) {\n return false;\n }\n\n if (event.target.value.length === 0 || event.target.value.length > 2) {\n debounceTable();\n }\n };\n\n /**\n * Process the search reset click event from the quiz table.\n */\n const tableSearchReset = function() {\n let tableSearchInputElement = document.getElementById('local-assessfreq-quiz-inprogress-table-search');\n tableSearchInputElement.value = '';\n tableSearchInputElement.focus();\n getSummaryTable();\n };\n\n /**\n * For each of the cards on the dashbaord get their corresponding chart data.\n * Data is based on the year variable from the corresponding dropdown.\n * Chart data is loaded via ajax.\n *\n */\n const getCardCharts = function() {\n cards.forEach((cardData) => {\n let cardElement = document.getElementById(cardData.cardId);\n let spinner = cardElement.getElementsByClassName('overlay-icon-container')[0];\n let chartbody = cardElement.getElementsByClassName('chart-body')[0];\n let params = {\n 'data': JSON.stringify({'call': cardData.call, 'hoursahead': hoursAhead, 'hoursbehind': hoursBehind\n })};\n\n spinner.classList.remove('hide'); // Show sinner if not already shown.\n Fragment.loadFragment('local_assessfreq', 'get_quiz_inprogress_chart', contextid, params)\n .done((response) => {\n let resObj = JSON.parse(response);\n if (resObj.hasdata == true) {\n let context = { 'withtable' : true, 'chartdata' : JSON.stringify(resObj.chart), 'aspect' : cardData.aspect};\n Templates.render('local_assessfreq/chart', context).done((html, js) => {\n spinner.classList.add('hide'); // Hide spinner if not already hidden.\n // Load card body.\n Templates.replaceNodeContents(chartbody, html, js);\n }).fail(() => {\n Notification.exception(new Error('Failed to load chart template.'));\n return;\n });\n return;\n } else {\n Str.get_string('nodata', 'local_assessfreq').then((str) => {\n const noDatastr = document.createElement('h3');\n noDatastr.innerHTML = str;\n chartbody.innerHTML = noDatastr.outerHTML;\n spinner.classList.add('hide'); // Hide spinner if not already hidden.\n return;\n }).catch(() => {\n Notification.exception(new Error('Failed to load string: nodata'));\n });\n }\n }).fail(() => {\n Notification.exception(new Error('Failed to load card.'));\n return;\n });\n });\n };\n\n /**\n * Process the nav event from the quiz table.\n */\n const tableNav = function(event) {\n event.preventDefault();\n\n const linkUrl = new URL(event.target.closest('a').href);\n const page = linkUrl.searchParams.get('page');\n\n if (page) {\n getSummaryTable(page);\n }\n };\n\n /**\n * Process the row set event from the quiz table.\n */\n const tableSearchRowSet = function(event) {\n event.preventDefault();\n if (event.target.tagName.toLowerCase() === 'a') {\n let rows = event.target.dataset.metric;\n setUserPreference('local_assessfreq_quiz_table_inprogress_preference', rows)\n .then(() => {\n getSummaryTable(); // Reload the table.\n })\n .fail(() => {\n Notification.exception(new Error('Failed to update user preference: rows'));\n });\n }\n };\n\n /**\n * Get and process the selected assessment metric from the dropdown for the heatmap display,\n * and update the corresponding user perference.\n *\n * @param {event} event The triggered event for the element.\n */\n const tableSortButtonAction = function(event) {\n event.preventDefault();\n var element = event.target;\n\n if (element.tagName.toLowerCase() === 'a' && element.dataset.sort != tablesort) {\n tablesort = element.dataset.sort;\n\n let links = element.parentNode.getElementsByTagName('a');\n for (let i = 0; i < links.length; i++) {\n links[i].classList.remove('active');\n }\n\n element.classList.add('active');\n\n // Save selection as a user preference.\n setUserPreference('local_assessfreq_quiz_table_inprogress_sort_preference', tablesort);\n\n debounceTable(); // Call function to update table.\n\n }\n };\n\n /**\n * Re-add event listeners when the quiz table is updated.\n */\n const tableEventListeners = function() {\n const tableElement = document.getElementById('local-assessfreq-quiz-inprogress-table');\n const tableNavElement = tableElement.querySelectorAll('nav'); // There are two nav paging elements per table.\n\n tableNavElement.forEach((navElement) => {\n navElement.addEventListener('click', tableNav);\n });\n };\n\n /**\n * Display the table that contains all in progress quiz summaries.\n */\n const getSummaryTable = function(page) {\n if (typeof page === \"undefined\") {\n page = 0;\n }\n\n let tableElement = document.getElementById('local-assessfreq-quiz-inprogress-table');\n let spinner = tableElement.getElementsByClassName('overlay-icon-container')[0];\n let tableBody = tableElement.getElementsByClassName('table-body')[0];\n let search = document.getElementById('local-assessfreq-quiz-inprogress-table-search').value.trim();\n let sortarray = tablesort.split('_');\n let sorton = sortarray[0];\n let direction = sortarray[1];\n\n let params = {'data': JSON.stringify(\n {'search': search, 'page': page, 'sorton': sorton, 'direction': direction,\n 'hoursahead': hoursAhead, 'hoursbehind': hoursBehind}\n )};\n\n spinner.classList.remove('hide'); // Show sinner if not already shown.\n\n // Load table content.\n Fragment.loadFragment('local_assessfreq', 'get_quizzes_inprogress_table', contextid, params)\n .done((response, js) => {\n tableBody.innerHTML = response;\n Templates.runTemplateJS(js); // Magic call the initialises JS from template included in response template HTML.\n spinner.classList.add('hide'); // Hide spinner if not already hidden.\n tableEventListeners(); // Re-add table event listeners.\n\n }).fail(() => {\n Notification.exception(new Error('Failed to update table.'));\n return;\n });\n };\n\n /**\n * Starts the processing of the dashboard.\n */\n const processDashboard = function() {\n // Get summary quiz data.\n Ajax.call([{\n methodname: 'local_assessfreq_get_inprogress_counts',\n args: {},\n }])[0].then((response) => {\n let quizSummary = JSON.parse(response);\n let summaryElement = document.getElementById('local-assessfreq-quiz-dashboard-inprogress-summary-card');\n let summarySpinner = summaryElement.getElementsByClassName('overlay-icon-container')[0];\n let tableSearchInputElement = document.getElementById('local-assessfreq-quiz-inprogress-table-search');\n let tableSearchResetElement = document.getElementById('local-assessfreq-quiz-inprogress-table-search-reset');\n let tableSearchRowsElement = document.getElementById('local-assessfreq-quiz-inprogress-table-rows');\n let tableSortElement = document.getElementById('local-assessfreq-inprogress-table-sort');\n\n summaryElement.classList.remove('hide'); // Show the card.\n\n // Populate summary card with details.\n Templates.render('local_assessfreq/quiz-dashboard-inprogress-summary-card-content', quizSummary)\n .done((html) => {\n summarySpinner.classList.add('hide');\n\n let contentcontainer = document.getElementById('local-assessfreq-quiz-dashboard-inprogress-summary-card-content');\n Templates.replaceNodeContents(contentcontainer, html);\n }).fail(() => {\n Notification.exception(new Error('Failed to load quiz counts template.'));\n return;\n });\n\n getCardCharts();\n getSummaryTable();\n refreshCounter();\n\n // Table event listeners.\n tableSearchInputElement.addEventListener('keyup', tableSearch);\n tableSearchInputElement.addEventListener('paste', tableSearch);\n tableSearchResetElement.addEventListener('click', tableSearchReset);\n tableSearchRowsElement.addEventListener('click', tableSearchRowSet);\n tableSortElement.addEventListener('click', tableSortButtonAction);\n\n return;\n }).fail(() => {\n Notification.exception(new Error('Failed to get quiz summary counts'));\n });\n };\n\n /**\n * Handle processing of refresh and period button actions.\n */\n const refreshAction = function(event) {\n event.preventDefault();\n var element = event.target;\n\n if (element.closest('button') !== null && element.closest('button').id == 'local-assessfreq-refresh-quiz-dashboard') {\n refreshCounter(true);\n processDashboard();\n } else if (element.tagName.toLowerCase() === 'a') {\n refreshPeriod = element.dataset.period;\n refreshCounter(true);\n setUserPreference('local_assessfreq_quiz_refresh_preference', refreshPeriod);\n }\n };\n\n /**\n * Trigger the zoom graph. Thin wrapper to add extra data to click event.\n */\n const triggerZoomGraph = function(event) {\n let call = event.target.closest('div').dataset.call;\n let params = {'data': JSON.stringify({'call': call})};\n let method = 'get_quiz_inprogress_chart';\n\n ZoomModal.zoomGraph(event, params, method);\n };\n\n /**\n * Process the hours ahead event from the in progress quizzes table.\n */\n const quizzesAheadSet = function(event) {\n event.preventDefault();\n if (event.target.tagName.toLowerCase() === 'a') {\n let hours = event.target.dataset.metric;\n setUserPreference('local_assessfreq_quizzes_inprogress_table_hoursahead_preference', hours)\n .then(() => {\n hoursAhead = hours;\n processDashboard(); // Reload the table.\n })\n .fail(() => {\n Notification.exception(new Error('Failed to update user preference: hours ahead'));\n });\n }\n };\n\n /**\n * Process the hours behind event from the in progress quizzes table.\n */\n const quizzesBehindSet = function(event) {\n event.preventDefault();\n if (event.target.tagName.toLowerCase() === 'a') {\n let hours = event.target.dataset.metric;\n setUserPreference('local_assessfreq_quizzes_inprogress_table_hoursbehind_preference', hours)\n .then(() => {\n hoursBehind = hours;\n processDashboard(); // Reload the table.\n })\n .fail(() => {\n Notification.exception(new Error('Failed to update user preference: hours behind'));\n });\n }\n };\n\n /**\n * Initialise method for quizzes in progress dashboard rendering.\n */\n DashboardQuizInprogress.init = function(context) {\n contextid = context;\n ZoomModal.init(context); // Create the zoom modal.\n\n getUserPreference('local_assessfreq_quiz_refresh_preference')\n .then((response) => {\n refreshPeriod = response.preferences[0].value ? response.preferences[0].value : 60;\n })\n .fail(() => {\n Notification.exception(new Error('Failed to get use preference: refresh'));\n });\n\n getUserPreference('local_assessfreq_quiz_table_inprogress_sort_preference')\n .then((response) => {\n tablesort = response.preferences[0].value ? response.preferences[0].value : 'name_asc';\n })\n .fail(() => {\n Notification.exception(new Error('Failed to get use preference: tablesort'));\n });\n\n getUserPreference('local_assessfreq_quizzes_inprogress_table_hoursahead_preference')\n .then((response) => {\n hoursAhead = response.preferences[0].value ? response.preferences[0].value : 0;\n })\n .fail(() => {\n Notification.exception(new Error('Failed to get use preference: hoursahead'));\n });\n\n getUserPreference('local_assessfreq_quizzes_inprogress_table_hoursbehind_preference')\n .then((response) => {\n hoursBehind = response.preferences[0].value ? response.preferences[0].value : 0;\n })\n .fail(() => {\n Notification.exception(new Error('Failed to get use preference: hoursbehind'));\n });\n\n // Event handling for refresh and period buttons.\n let refreshElement = document.getElementById('local-assessfreq-period-container');\n refreshElement.addEventListener('click', refreshAction);\n\n // Set up zoom event listeners.\n let summaryZoom = document.getElementById('local-assessfreq-quiz-summary-inprogress-graph-zoom');\n summaryZoom.addEventListener('click', triggerZoomGraph);\n\n let upcommingZoom = document.getElementById('local-assessfreq-quiz-summary-upcomming-graph-zoom');\n upcommingZoom.addEventListener('click', triggerZoomGraph);\n\n // Set up behind and ahead quizzes event listeners.\n let quizzesAheadElement = document.getElementById('local-assessfreq-quiz-student-table-hoursahead');\n quizzesAheadElement.addEventListener('click', quizzesAheadSet);\n\n let quizzesBehindElement = document.getElementById('local-assessfreq-quiz-student-table-hoursbehind');\n quizzesBehindElement.addEventListener('click', quizzesBehindSet);\n\n processDashboard();\n\n };\n\n return DashboardQuizInprogress;\n});\n"],"file":"dashboard_quiz_inprogress.min.js"} \ No newline at end of file +{"version":3,"sources":["../src/dashboard_quiz_inprogress.js"],"names":["define","$","Ajax","Templates","Fragment","ZoomModal","Str","Notification","DashboardQuizInprogress","contextid","refreshPeriod","counterid","tablesort","hoursAhead","hoursBehind","cards","cardId","call","aspect","setUserPreference","type","value","methodname","args","preferences","getUserPreference","name","debounceTable","debouncer","func","wait","timeout","later","clearTimeout","setTimeout","getSummaryTable","refreshCounter","reset","progressElement","document","getElementById","clearInterval","setAttribute","setInterval","progressWidthAria","getAttribute","progressStep","processDashboard","tableSearch","event","key","ctrlKey","target","length","tableSearchReset","tableSearchInputElement","focus","getCardCharts","forEach","cardData","cardElement","spinner","getElementsByClassName","chartbody","params","JSON","stringify","classList","remove","loadFragment","done","response","resObj","parse","hasdata","context","chart","render","html","js","add","replaceNodeContents","fail","exception","Error","get_string","then","str","noDatastr","createElement","innerHTML","outerHTML","catch","tableNav","preventDefault","linkUrl","URL","closest","href","page","searchParams","get","tableSearchRowSet","tagName","toLowerCase","rows","dataset","metric","activeoptions","i","tableSortButtonAction","element","sort","links","parentNode","getElementsByTagName","tableEventListeners","tableElement","tableNavElement","querySelectorAll","navElement","addEventListener","tableBody","search","trim","sortarray","split","sorton","direction","runTemplateJS","tooltip","quizSummary","summaryElement","summarySpinner","tableSearchResetElement","tableSearchRowsElement","tableSortElement","contentcontainer","refreshAction","id","refreshElement","actionButton","textContent","period","triggerZoomGraph","zoomGraph","quizzesAheadSet","hours","quizzesBehindSet","init","summaryZoom","upcommingZoom","quizzesAheadElement","quizzesBehindElement"],"mappings":"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","sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Javascript for quizzes in progress display and processing.\n *\n * @package local_assessfreq\n * @copyright 2020 Matt Porritt \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\ndefine(['jquery', 'core/ajax', 'core/templates', 'core/fragment', 'local_assessfreq/zoom_modal', 'core/str', 'core/notification'],\nfunction($, Ajax, Templates, Fragment, ZoomModal, Str, Notification) {\n\n /**\n * Module level variables.\n */\n var DashboardQuizInprogress = {};\n var contextid;\n var refreshPeriod = 60;\n var counterid;\n var tablesort = 'name_asc';\n var hoursAhead = 0;\n var hoursBehind = 0;\n\n const cards = [\n {cardId: 'local-assessfreq-quiz-summary-upcomming-graph', call: 'upcomming_quizzes', aspect: true},\n {cardId: 'local-assessfreq-quiz-summary-inprogress-graph', call: 'all_participants_inprogress', aspect: true}\n ];\n\n /**\n * Generic handler to persist user preferences.\n *\n * @param {string} type The name of the attribute you're updating\n * @param {string} value The value of the attribute you're updating\n * @return {object} jQuery promise\n */\n const setUserPreference = function(type, value) {\n var request = {\n methodname: 'core_user_update_user_preferences',\n args: {\n preferences: [{type: type, value: value}]\n }\n };\n\n return Ajax.call([request])[0];\n };\n\n /**\n * Generic handler to get user preference.\n *\n * @param {string} name The name of the attribute you're getting.\n * @return {object} jQuery promise\n */\n const getUserPreference = function(name) {\n var request = {\n methodname: 'local_assessfreq_get_user_preferences',\n args: {\n 'name': name\n }\n };\n\n return Ajax.call([request])[0];\n };\n\n /**\n * Quick and dirty debounce method for the settings.\n * This stops the ajax method that updates the table from being updated\n * while the user is still checking options.\n *\n */\n const debouncer = function (func, wait) {\n let timeout;\n\n return function executedFunction(...args) {\n const later = () => {\n clearTimeout(timeout);\n func(...args);\n };\n\n clearTimeout(timeout);\n timeout = setTimeout(later, wait);\n };\n };\n\n const debounceTable = debouncer(() => {\n getSummaryTable();\n }, 750);\n\n /**\n *\n */\n const refreshCounter = function(reset) {\n let progressElement = document.getElementById('local-assessfreq-period-progress');\n\n // Reset the current count process.\n if (reset == true) {\n clearInterval(counterid);\n counterid = null;\n progressElement.setAttribute('style', 'width: 100%');\n progressElement.setAttribute('aria-valuenow', 100);\n }\n\n // Exit early if there is already a counter running.\n if (counterid) {\n return;\n }\n\n counterid = setInterval(() => {\n let progressWidthAria = progressElement.getAttribute('aria-valuenow');\n const progressStep = 100 / refreshPeriod;\n\n if ((progressWidthAria - progressStep) > 0) {\n progressElement.setAttribute('style', 'width: ' + (progressWidthAria - progressStep) + '%');\n progressElement.setAttribute('aria-valuenow', (progressWidthAria - progressStep));\n } else {\n clearInterval(counterid);\n counterid = null;\n progressElement.setAttribute('style', 'width: 100%');\n progressElement.setAttribute('aria-valuenow', 100);\n processDashboard();\n refreshCounter();\n }\n }, (1000));\n };\n\n /**\n * Process the search events from the quiz table.\n */\n const tableSearch = function(event) {\n if (event.key === 'Meta' || event.ctrlKey) {\n return false;\n }\n\n if (event.target.value.length === 0 || event.target.value.length > 2) {\n debounceTable();\n }\n };\n\n /**\n * Process the search reset click event from the quiz table.\n */\n const tableSearchReset = function() {\n let tableSearchInputElement = document.getElementById('local-assessfreq-quiz-inprogress-table-search');\n tableSearchInputElement.value = '';\n tableSearchInputElement.focus();\n getSummaryTable();\n };\n\n /**\n * For each of the cards on the dashbaord get their corresponding chart data.\n * Data is based on the year variable from the corresponding dropdown.\n * Chart data is loaded via ajax.\n *\n */\n const getCardCharts = function() {\n cards.forEach((cardData) => {\n let cardElement = document.getElementById(cardData.cardId);\n let spinner = cardElement.getElementsByClassName('overlay-icon-container')[0];\n let chartbody = cardElement.getElementsByClassName('chart-body')[0];\n let params = {\n 'data': JSON.stringify({'call': cardData.call, 'hoursahead': hoursAhead, 'hoursbehind': hoursBehind\n })};\n\n spinner.classList.remove('hide'); // Show sinner if not already shown.\n Fragment.loadFragment('local_assessfreq', 'get_quiz_inprogress_chart', contextid, params)\n .done((response) => {\n let resObj = JSON.parse(response);\n if (resObj.hasdata == true) {\n let context = { 'withtable' : true, 'chartdata' : JSON.stringify(resObj.chart), 'aspect' : cardData.aspect};\n Templates.render('local_assessfreq/chart', context).done((html, js) => {\n spinner.classList.add('hide'); // Hide spinner if not already hidden.\n // Load card body.\n Templates.replaceNodeContents(chartbody, html, js);\n }).fail(() => {\n Notification.exception(new Error('Failed to load chart template.'));\n return;\n });\n return;\n } else {\n Str.get_string('nodata', 'local_assessfreq').then((str) => {\n const noDatastr = document.createElement('h3');\n noDatastr.innerHTML = str;\n chartbody.innerHTML = noDatastr.outerHTML;\n spinner.classList.add('hide'); // Hide spinner if not already hidden.\n return;\n }).catch(() => {\n Notification.exception(new Error('Failed to load string: nodata'));\n });\n }\n }).fail(() => {\n Notification.exception(new Error('Failed to load card.'));\n return;\n });\n });\n };\n\n /**\n * Process the nav event from the quiz table.\n */\n const tableNav = function(event) {\n event.preventDefault();\n\n const linkUrl = new URL(event.target.closest('a').href);\n const page = linkUrl.searchParams.get('page');\n\n if (page) {\n getSummaryTable(page);\n }\n };\n\n /**\n * Process the row set event from the quiz table.\n */\n const tableSearchRowSet = function(event) {\n event.preventDefault();\n if (event.target.tagName.toLowerCase() === 'a') {\n let rows = event.target.dataset.metric;\n let activeoptions = document.getElementById('local-assessfreq-quiz-inprogress-table-rows')\n .getElementsByClassName('active');\n\n // Fix active classes.\n for (var i = 0; i < activeoptions.length; i++) {\n activeoptions[i].classList.remove('active');\n }\n event.target.classList.add('active');\n\n setUserPreference('local_assessfreq_quiz_table_inprogress_preference', rows)\n .then(() => {\n getSummaryTable(); // Reload the table.\n })\n .fail(() => {\n Notification.exception(new Error('Failed to update user preference: rows'));\n });\n }\n };\n\n /**\n * Get and process the selected assessment metric from the dropdown for the heatmap display,\n * and update the corresponding user perference.\n *\n * @param {event} event The triggered event for the element.\n */\n const tableSortButtonAction = function(event) {\n event.preventDefault();\n var element = event.target;\n\n if (element.tagName.toLowerCase() === 'a' && element.dataset.sort != tablesort) {\n tablesort = element.dataset.sort;\n\n let links = element.parentNode.getElementsByTagName('a');\n for (let i = 0; i < links.length; i++) {\n links[i].classList.remove('active');\n }\n\n element.classList.add('active');\n\n // Save selection as a user preference.\n setUserPreference('local_assessfreq_quiz_table_inprogress_sort_preference', tablesort);\n\n debounceTable(); // Call function to update table.\n\n }\n };\n\n /**\n * Re-add event listeners when the quiz table is updated.\n */\n const tableEventListeners = function() {\n const tableElement = document.getElementById('local-assessfreq-quiz-inprogress-table');\n const tableNavElement = tableElement.querySelectorAll('nav'); // There are two nav paging elements per table.\n\n tableNavElement.forEach((navElement) => {\n navElement.addEventListener('click', tableNav);\n });\n };\n\n /**\n * Display the table that contains all in progress quiz summaries.\n */\n const getSummaryTable = function(page) {\n if (typeof page === \"undefined\") {\n page = 0;\n }\n\n let tableElement = document.getElementById('local-assessfreq-quiz-inprogress-table');\n let spinner = tableElement.getElementsByClassName('overlay-icon-container')[0];\n let tableBody = tableElement.getElementsByClassName('table-body')[0];\n let search = document.getElementById('local-assessfreq-quiz-inprogress-table-search').value.trim();\n let sortarray = tablesort.split('_');\n let sorton = sortarray[0];\n let direction = sortarray[1];\n\n let params = {'data': JSON.stringify(\n {'search': search, 'page': page, 'sorton': sorton, 'direction': direction,\n 'hoursahead': hoursAhead, 'hoursbehind': hoursBehind}\n )};\n\n spinner.classList.remove('hide'); // Show sinner if not already shown.\n\n // Load table content.\n Fragment.loadFragment('local_assessfreq', 'get_quizzes_inprogress_table', contextid, params)\n .done((response, js) => {\n tableBody.innerHTML = response;\n Templates.runTemplateJS(js); // Magic call the initialises JS from template included in response template HTML.\n spinner.classList.add('hide'); // Hide spinner if not already hidden.\n tableEventListeners(); // Re-add table event listeners.\n $('[data-toggle=\"tooltip\"]').tooltip();\n\n }).fail(() => {\n Notification.exception(new Error('Failed to update table.'));\n return;\n });\n };\n\n /**\n * Starts the processing of the dashboard.\n */\n const processDashboard = function() {\n // Get summary quiz data.\n Ajax.call([{\n methodname: 'local_assessfreq_get_inprogress_counts',\n args: {},\n }])[0].then((response) => {\n let quizSummary = JSON.parse(response);\n let summaryElement = document.getElementById('local-assessfreq-quiz-dashboard-inprogress-summary-card');\n let summarySpinner = summaryElement.getElementsByClassName('overlay-icon-container')[0];\n let tableSearchInputElement = document.getElementById('local-assessfreq-quiz-inprogress-table-search');\n let tableSearchResetElement = document.getElementById('local-assessfreq-quiz-inprogress-table-search-reset');\n let tableSearchRowsElement = document.getElementById('local-assessfreq-quiz-inprogress-table-rows');\n let tableSortElement = document.getElementById('local-assessfreq-inprogress-table-sort');\n\n summaryElement.classList.remove('hide'); // Show the card.\n\n // Populate summary card with details.\n Templates.render('local_assessfreq/quiz-dashboard-inprogress-summary-card-content', quizSummary)\n .done((html) => {\n summarySpinner.classList.add('hide');\n\n let contentcontainer = document.getElementById('local-assessfreq-quiz-dashboard-inprogress-summary-card-content');\n Templates.replaceNodeContents(contentcontainer, html);\n }).fail(() => {\n Notification.exception(new Error('Failed to load quiz counts template.'));\n return;\n });\n\n getCardCharts();\n getSummaryTable();\n refreshCounter();\n\n // Table event listeners.\n tableSearchInputElement.addEventListener('keyup', tableSearch);\n tableSearchInputElement.addEventListener('paste', tableSearch);\n tableSearchResetElement.addEventListener('click', tableSearchReset);\n tableSearchRowsElement.addEventListener('click', tableSearchRowSet);\n tableSortElement.addEventListener('click', tableSortButtonAction);\n\n $('[data-toggle=\"tooltip\"]').tooltip();\n\n return;\n }).fail(() => {\n Notification.exception(new Error('Failed to get quiz summary counts'));\n });\n };\n\n /**\n * Handle processing of refresh and period button actions.\n */\n const refreshAction = function(event) {\n event.preventDefault();\n var element = event.target;\n\n if (element.closest('button') !== null && element.closest('button').id == 'local-assessfreq-refresh-quiz-dashboard') {\n refreshCounter(true);\n processDashboard();\n } else if (element.tagName.toLowerCase() === 'a') {\n let refreshElement = document.getElementById('local-assessfreq-period-container');\n let actionButton = refreshElement.getElementsByClassName('dropdown-toggle')[0];\n actionButton.textContent = element.innerHTML;\n\n let activeoptions = refreshElement.getElementsByClassName('active');\n\n // Fix active classes.\n for (var i = 0; i < activeoptions.length; i++) {\n activeoptions[i].classList.remove('active');\n }\n element.classList.add('active');\n\n refreshPeriod = element.dataset.period;\n refreshCounter(true);\n setUserPreference('local_assessfreq_quiz_refresh_preference', refreshPeriod);\n }\n };\n\n /**\n * Trigger the zoom graph. Thin wrapper to add extra data to click event.\n */\n const triggerZoomGraph = function(event) {\n let call = event.target.closest('div').dataset.call;\n let params = {'data': JSON.stringify({'call': call})};\n let method = 'get_quiz_inprogress_chart';\n\n ZoomModal.zoomGraph(event, params, method);\n };\n\n /**\n * Process the hours ahead event from the in progress quizzes table.\n */\n const quizzesAheadSet = function(event) {\n event.preventDefault();\n if (event.target.tagName.toLowerCase() === 'a') {\n let hours = event.target.dataset.metric;\n setUserPreference('local_assessfreq_quizzes_inprogress_table_hoursahead_preference', hours)\n .then(() => {\n hoursAhead = hours;\n processDashboard(); // Reload the table.\n })\n .fail(() => {\n Notification.exception(new Error('Failed to update user preference: hours ahead'));\n });\n }\n };\n\n /**\n * Process the hours behind event from the in progress quizzes table.\n */\n const quizzesBehindSet = function(event) {\n event.preventDefault();\n if (event.target.tagName.toLowerCase() === 'a') {\n let hours = event.target.dataset.metric;\n setUserPreference('local_assessfreq_quizzes_inprogress_table_hoursbehind_preference', hours)\n .then(() => {\n hoursBehind = hours;\n processDashboard(); // Reload the table.\n })\n .fail(() => {\n Notification.exception(new Error('Failed to update user preference: hours behind'));\n });\n }\n };\n\n /**\n * Initialise method for quizzes in progress dashboard rendering.\n */\n DashboardQuizInprogress.init = function(context) {\n contextid = context;\n ZoomModal.init(context); // Create the zoom modal.\n\n getUserPreference('local_assessfreq_quiz_refresh_preference')\n .then((response) => {\n refreshPeriod = response.preferences[0].value ? response.preferences[0].value : 60;\n })\n .fail(() => {\n Notification.exception(new Error('Failed to get use preference: refresh'));\n });\n\n getUserPreference('local_assessfreq_quiz_table_inprogress_sort_preference')\n .then((response) => {\n tablesort = response.preferences[0].value ? response.preferences[0].value : 'name_asc';\n })\n .fail(() => {\n Notification.exception(new Error('Failed to get use preference: tablesort'));\n });\n\n getUserPreference('local_assessfreq_quizzes_inprogress_table_hoursahead_preference')\n .then((response) => {\n hoursAhead = response.preferences[0].value ? response.preferences[0].value : 0;\n })\n .fail(() => {\n Notification.exception(new Error('Failed to get use preference: hoursahead'));\n });\n\n getUserPreference('local_assessfreq_quizzes_inprogress_table_hoursbehind_preference')\n .then((response) => {\n hoursBehind = response.preferences[0].value ? response.preferences[0].value : 0;\n })\n .fail(() => {\n Notification.exception(new Error('Failed to get use preference: hoursbehind'));\n });\n\n // Event handling for refresh and period buttons.\n let refreshElement = document.getElementById('local-assessfreq-period-container');\n refreshElement.addEventListener('click', refreshAction);\n\n // Set up zoom event listeners.\n let summaryZoom = document.getElementById('local-assessfreq-quiz-summary-inprogress-graph-zoom');\n summaryZoom.addEventListener('click', triggerZoomGraph);\n\n let upcommingZoom = document.getElementById('local-assessfreq-quiz-summary-upcomming-graph-zoom');\n upcommingZoom.addEventListener('click', triggerZoomGraph);\n\n // Set up behind and ahead quizzes event listeners.\n let quizzesAheadElement = document.getElementById('local-assessfreq-quiz-student-table-hoursahead');\n quizzesAheadElement.addEventListener('click', quizzesAheadSet);\n\n let quizzesBehindElement = document.getElementById('local-assessfreq-quiz-student-table-hoursbehind');\n quizzesBehindElement.addEventListener('click', quizzesBehindSet);\n\n processDashboard();\n\n };\n\n return DashboardQuizInprogress;\n});\n"],"file":"dashboard_quiz_inprogress.min.js"} \ No newline at end of file