diff --git a/admin/tool/analytics/amd/build/log_info.min.js.map b/admin/tool/analytics/amd/build/log_info.min.js.map
index 38c60d0eedc..eb0a63c98db 100644
--- a/admin/tool/analytics/amd/build/log_info.min.js.map
+++ b/admin/tool/analytics/amd/build/log_info.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"log_info.min.js","sources":["../src/log_info.js"],"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 * Shows a dialogue with info about this logs.\n *\n * @module tool_analytics/log_info\n * @copyright 2017 David Monllao {@link http://www.davidmonllao.com}\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(['jquery', 'core/str', 'core/modal_factory', 'core/notification'], function($, str, ModalFactory, Notification) {\n\n return /** @alias module:tool_analytics/log_info */ {\n\n /**\n * Prepares a modal info for a log's results.\n *\n * @method loadInfo\n * @param {int} id\n * @param {string[]} info\n */\n loadInfo: function(id, info) {\n\n var link = $('[data-model-log-id=\"' + id + '\"]');\n str.get_string('loginfo', 'tool_analytics').then(function(langString) {\n\n var bodyInfo = $(\"
\");\n\n return ModalFactory.create({\n title: langString,\n body: bodyInfo.html(),\n large: true,\n }, link);\n\n }).catch(Notification.exception);\n }\n };\n});\n"],"names":["define","$","str","ModalFactory","Notification","loadInfo","id","info","link","get_string","then","langString","bodyInfo","forEach","item","append","create","title","body","html","large","catch","exception"],"mappings":";;;;;;;AAsBAA,iCAAO,CAAC,SAAU,WAAY,qBAAsB,sBAAsB,SAASC,EAAGC,IAAKC,aAAcC,oBAEjD,CAShDC,SAAU,SAASC,GAAIC,UAEfC,KAAOP,EAAE,uBAAyBK,GAAK,MAC3CJ,IAAIO,WAAW,UAAW,kBAAkBC,MAAK,SAASC,gBAElDC,SAAWX,EAAE,eACjBM,KAAKM,SAAQ,SAASC,MAClBF,SAASG,OAAO,OAASD,KAAO,YAEpCF,SAASG,OAAO,SAETZ,aAAaa,OAAO,CACvBC,MAAON,WACPO,KAAMN,SAASO,OACfC,OAAO,GACRZ,SAEJa,MAAMjB,aAAakB"}
\ No newline at end of file
+{"version":3,"file":"log_info.min.js","sources":["../src/log_info.js"],"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 * Shows a dialogue with info about this logs.\n *\n * @module tool_analytics/log_info\n * @copyright 2017 David Monllao {@link http://www.davidmonllao.com}\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(['jquery', 'core/str', 'core/modal_factory', 'core/notification'], function($, str, ModalFactory, Notification) {\n\n return /** @alias module:tool_analytics/log_info */ {\n\n /**\n * Prepares a modal info for a log's results.\n *\n * @method loadInfo\n * @param {int} id\n * @param {string[]} info\n */\n loadInfo: function(id, info) {\n\n var link = $('[data-model-log-id=\"' + id + '\"]');\n str.get_string('loginfo', 'tool_analytics').then(function(langString) {\n\n var bodyInfo = $(\"
\");\n\n return ModalFactory.create({\n title: langString,\n body: bodyInfo.html(),\n large: true,\n }, link);\n\n }).catch(Notification.exception);\n }\n };\n});\n"],"names":["define","$","str","ModalFactory","Notification","loadInfo","id","info","link","get_string","then","langString","bodyInfo","forEach","item","append","create","title","body","html","large","catch","exception"],"mappings":";;;;;;;AAsBAA,iCAAO,CAAC,SAAU,WAAY,qBAAsB,sBAAsB,SAASC,EAAGC,IAAKC,aAAcC,cAErG,MAAoD,CAShDC,SAAU,SAASC,GAAIC,MAEnB,IAAIC,KAAOP,EAAE,uBAAyBK,GAAK,MAC3CJ,IAAIO,WAAW,UAAW,kBAAkBC,MAAK,SAASC,YAEtD,IAAIC,SAAWX,EAAE,QAMjB,OALAM,KAAKM,SAAQ,SAASC,MAClBF,SAASG,OAAO,OAASD,KAAO,QACpC,IACAF,SAASG,OAAO,SAETZ,aAAaa,OAAO,CACvBC,MAAON,WACPO,KAAMN,SAASO,OACfC,OAAO,GACRZ,KAEN,IAAEa,MAAMjB,aAAakB,UAC1B,EAER"}
\ No newline at end of file
diff --git a/admin/tool/analytics/amd/build/model.min.js.map b/admin/tool/analytics/amd/build/model.min.js.map
index f50f1c0f05a..237c1d8d0a5 100644
--- a/admin/tool/analytics/amd/build/model.min.js.map
+++ b/admin/tool/analytics/amd/build/model.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"model.min.js","sources":["../src/model.js"],"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 * AMD module for model actions confirmation.\n *\n * @module tool_analytics/model\n * @copyright 2017 David Monllao\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(['jquery', 'core/str', 'core/log', 'core/notification', 'core/modal_factory', 'core/modal_events', 'core/templates'],\n function($, Str, log, Notification, ModalFactory, ModalEvents, Templates) {\n\n /**\n * List of actions that require confirmation and confirmation message.\n */\n var actionsList = {\n clear: {\n title: {\n key: 'clearpredictions',\n component: 'tool_analytics'\n }, body: {\n key: 'clearmodelpredictions',\n component: 'tool_analytics'\n }\n\n },\n 'delete': {\n title: {\n key: 'delete',\n component: 'tool_analytics'\n }, body: {\n key: 'deletemodelconfirmation',\n component: 'tool_analytics'\n }\n }\n };\n\n /**\n * Returns the model name.\n *\n * @param {Object} actionItem The action item DOM node.\n * @return {String}\n */\n var getModelName = function(actionItem) {\n var wrap = $(actionItem).closest('[data-model-name]');\n\n if (wrap.length) {\n return wrap.attr('data-model-name');\n\n } else {\n log.error('Unexpected DOM error - unable to obtain the model name');\n return '';\n }\n };\n\n /** @alias module:tool_analytics/model */\n return {\n\n /**\n * Displays a confirm modal window before executing the action.\n *\n * @param {String} actionId\n * @param {String} actionType\n */\n confirmAction: function(actionId, actionType) {\n $('[data-action-id=\"' + actionId + '\"]').on('click', function(ev) {\n ev.preventDefault();\n\n var a = $(ev.currentTarget);\n\n if (typeof actionsList[actionType] === \"undefined\") {\n log.error('Action \"' + actionType + '\" is not allowed.');\n return;\n }\n\n var reqStrings = [\n actionsList[actionType].title,\n actionsList[actionType].body\n ];\n reqStrings[1].param = getModelName(a);\n\n var stringsPromise = Str.get_strings(reqStrings);\n var modalPromise = ModalFactory.create({type: ModalFactory.types.SAVE_CANCEL});\n\n $.when(stringsPromise, modalPromise).then(function(strings, modal) {\n modal.setTitle(strings[0]);\n modal.setBody(strings[1]);\n modal.setSaveButtonText(strings[0]);\n modal.getRoot().on(ModalEvents.save, function() {\n window.location.href = a.attr('href');\n });\n modal.show();\n return modal;\n }).fail(Notification.exception);\n });\n },\n\n /**\n * Displays evaluation mode and time-splitting method choices.\n *\n * @param {String} actionId\n * @param {Boolean} trainedOnlyExternally\n */\n selectEvaluationOptions: function(actionId, trainedOnlyExternally) {\n $('[data-action-id=\"' + actionId + '\"]').on('click', function(ev) {\n ev.preventDefault();\n\n var a = $(ev.currentTarget);\n\n var timeSplittingMethods = $(this).attr('data-timesplitting-methods');\n\n var stringsPromise = Str.get_strings([\n {\n key: 'evaluatemodel',\n component: 'tool_analytics'\n }, {\n key: 'evaluate',\n component: 'tool_analytics'\n }\n ]);\n var modalPromise = ModalFactory.create({type: ModalFactory.types.SAVE_CANCEL});\n var bodyPromise = Templates.render('tool_analytics/evaluation_options', {\n trainedexternally: trainedOnlyExternally,\n timesplittingmethods: JSON.parse(timeSplittingMethods)\n });\n\n $.when(stringsPromise, modalPromise).then(function(strings, modal) {\n\n\n modal.getRoot().on(ModalEvents.hidden, modal.destroy.bind(modal));\n\n modal.setTitle(strings[0]);\n modal.setSaveButtonText(strings[1]);\n modal.setBody(bodyPromise);\n\n modal.getRoot().on(ModalEvents.save, function() {\n\n // Evaluation mode.\n var evaluationMode = $(\"input[name='evaluationmode']:checked\").val();\n if (evaluationMode == 'trainedmodel') {\n a.attr('href', a.attr('href') + '&mode=trainedmodel');\n }\n\n // Selected time-splitting id.\n var timeSplittingMethod = $(\"#id-evaluation-timesplitting\").val();\n a.attr('href', a.attr('href') + '×plitting=' + timeSplittingMethod);\n\n window.location.href = a.attr('href');\n return;\n });\n\n modal.show();\n return modal;\n }).fail(Notification.exception);\n });\n },\n\n /**\n * Displays export options.\n *\n * We have two main options: export training data and export configuration.\n * The 2nd option has an extra option: include the trained algorithm weights.\n *\n * @param {String} actionId\n * @param {Boolean} isTrained\n */\n selectExportOptions: function(actionId, isTrained) {\n $('[data-action-id=\"' + actionId + '\"]').on('click', function(ev) {\n ev.preventDefault();\n\n var a = $(ev.currentTarget);\n\n if (!isTrained) {\n // Export the model configuration if the model is not trained. We can't export anything else.\n a.attr('href', a.attr('href') + '&action=exportmodel&includeweights=0');\n window.location.href = a.attr('href');\n return;\n }\n\n var stringsPromise = Str.get_strings([\n {\n key: 'export',\n component: 'tool_analytics'\n }\n ]);\n var modalPromise = ModalFactory.create({type: ModalFactory.types.SAVE_CANCEL});\n var bodyPromise = Templates.render('tool_analytics/export_options', {});\n\n $.when(stringsPromise, modalPromise).then(function(strings, modal) {\n\n modal.getRoot().on(ModalEvents.hidden, modal.destroy.bind(modal));\n\n modal.setTitle(strings[0]);\n modal.setSaveButtonText(strings[0]);\n modal.setBody(bodyPromise);\n\n modal.getRoot().on(ModalEvents.save, function() {\n\n var exportOption = $(\"input[name='exportoption']:checked\").val();\n\n if (exportOption == 'exportdata') {\n a.attr('href', a.attr('href') + '&action=exportdata');\n\n } else {\n a.attr('href', a.attr('href') + '&action=exportmodel');\n if ($(\"#id-includeweights\").is(':checked')) {\n a.attr('href', a.attr('href') + '&includeweights=1');\n } else {\n a.attr('href', a.attr('href') + '&includeweights=0');\n }\n }\n\n window.location.href = a.attr('href');\n return;\n });\n\n modal.show();\n return modal;\n }).fail(Notification.exception);\n });\n }\n };\n});\n"],"names":["define","$","Str","log","Notification","ModalFactory","ModalEvents","Templates","actionsList","clear","title","key","component","body","confirmAction","actionId","actionType","on","ev","preventDefault","a","currentTarget","wrap","reqStrings","param","closest","length","attr","error","stringsPromise","get_strings","modalPromise","create","type","types","SAVE_CANCEL","when","then","strings","modal","setTitle","setBody","setSaveButtonText","getRoot","save","window","location","href","show","fail","exception","selectEvaluationOptions","trainedOnlyExternally","timeSplittingMethods","this","bodyPromise","render","trainedexternally","timesplittingmethods","JSON","parse","hidden","destroy","bind","val","timeSplittingMethod","selectExportOptions","isTrained","is"],"mappings":"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"}
\ No newline at end of file
+{"version":3,"file":"model.min.js","sources":["../src/model.js"],"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 * AMD module for model actions confirmation.\n *\n * @module tool_analytics/model\n * @copyright 2017 David Monllao\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(['jquery', 'core/str', 'core/log', 'core/notification', 'core/modal_factory', 'core/modal_events', 'core/templates'],\n function($, Str, log, Notification, ModalFactory, ModalEvents, Templates) {\n\n /**\n * List of actions that require confirmation and confirmation message.\n */\n var actionsList = {\n clear: {\n title: {\n key: 'clearpredictions',\n component: 'tool_analytics'\n }, body: {\n key: 'clearmodelpredictions',\n component: 'tool_analytics'\n }\n\n },\n 'delete': {\n title: {\n key: 'delete',\n component: 'tool_analytics'\n }, body: {\n key: 'deletemodelconfirmation',\n component: 'tool_analytics'\n }\n }\n };\n\n /**\n * Returns the model name.\n *\n * @param {Object} actionItem The action item DOM node.\n * @return {String}\n */\n var getModelName = function(actionItem) {\n var wrap = $(actionItem).closest('[data-model-name]');\n\n if (wrap.length) {\n return wrap.attr('data-model-name');\n\n } else {\n log.error('Unexpected DOM error - unable to obtain the model name');\n return '';\n }\n };\n\n /** @alias module:tool_analytics/model */\n return {\n\n /**\n * Displays a confirm modal window before executing the action.\n *\n * @param {String} actionId\n * @param {String} actionType\n */\n confirmAction: function(actionId, actionType) {\n $('[data-action-id=\"' + actionId + '\"]').on('click', function(ev) {\n ev.preventDefault();\n\n var a = $(ev.currentTarget);\n\n if (typeof actionsList[actionType] === \"undefined\") {\n log.error('Action \"' + actionType + '\" is not allowed.');\n return;\n }\n\n var reqStrings = [\n actionsList[actionType].title,\n actionsList[actionType].body\n ];\n reqStrings[1].param = getModelName(a);\n\n var stringsPromise = Str.get_strings(reqStrings);\n var modalPromise = ModalFactory.create({type: ModalFactory.types.SAVE_CANCEL});\n\n $.when(stringsPromise, modalPromise).then(function(strings, modal) {\n modal.setTitle(strings[0]);\n modal.setBody(strings[1]);\n modal.setSaveButtonText(strings[0]);\n modal.getRoot().on(ModalEvents.save, function() {\n window.location.href = a.attr('href');\n });\n modal.show();\n return modal;\n }).fail(Notification.exception);\n });\n },\n\n /**\n * Displays evaluation mode and time-splitting method choices.\n *\n * @param {String} actionId\n * @param {Boolean} trainedOnlyExternally\n */\n selectEvaluationOptions: function(actionId, trainedOnlyExternally) {\n $('[data-action-id=\"' + actionId + '\"]').on('click', function(ev) {\n ev.preventDefault();\n\n var a = $(ev.currentTarget);\n\n var timeSplittingMethods = $(this).attr('data-timesplitting-methods');\n\n var stringsPromise = Str.get_strings([\n {\n key: 'evaluatemodel',\n component: 'tool_analytics'\n }, {\n key: 'evaluate',\n component: 'tool_analytics'\n }\n ]);\n var modalPromise = ModalFactory.create({type: ModalFactory.types.SAVE_CANCEL});\n var bodyPromise = Templates.render('tool_analytics/evaluation_options', {\n trainedexternally: trainedOnlyExternally,\n timesplittingmethods: JSON.parse(timeSplittingMethods)\n });\n\n $.when(stringsPromise, modalPromise).then(function(strings, modal) {\n\n\n modal.getRoot().on(ModalEvents.hidden, modal.destroy.bind(modal));\n\n modal.setTitle(strings[0]);\n modal.setSaveButtonText(strings[1]);\n modal.setBody(bodyPromise);\n\n modal.getRoot().on(ModalEvents.save, function() {\n\n // Evaluation mode.\n var evaluationMode = $(\"input[name='evaluationmode']:checked\").val();\n if (evaluationMode == 'trainedmodel') {\n a.attr('href', a.attr('href') + '&mode=trainedmodel');\n }\n\n // Selected time-splitting id.\n var timeSplittingMethod = $(\"#id-evaluation-timesplitting\").val();\n a.attr('href', a.attr('href') + '×plitting=' + timeSplittingMethod);\n\n window.location.href = a.attr('href');\n return;\n });\n\n modal.show();\n return modal;\n }).fail(Notification.exception);\n });\n },\n\n /**\n * Displays export options.\n *\n * We have two main options: export training data and export configuration.\n * The 2nd option has an extra option: include the trained algorithm weights.\n *\n * @param {String} actionId\n * @param {Boolean} isTrained\n */\n selectExportOptions: function(actionId, isTrained) {\n $('[data-action-id=\"' + actionId + '\"]').on('click', function(ev) {\n ev.preventDefault();\n\n var a = $(ev.currentTarget);\n\n if (!isTrained) {\n // Export the model configuration if the model is not trained. We can't export anything else.\n a.attr('href', a.attr('href') + '&action=exportmodel&includeweights=0');\n window.location.href = a.attr('href');\n return;\n }\n\n var stringsPromise = Str.get_strings([\n {\n key: 'export',\n component: 'tool_analytics'\n }\n ]);\n var modalPromise = ModalFactory.create({type: ModalFactory.types.SAVE_CANCEL});\n var bodyPromise = Templates.render('tool_analytics/export_options', {});\n\n $.when(stringsPromise, modalPromise).then(function(strings, modal) {\n\n modal.getRoot().on(ModalEvents.hidden, modal.destroy.bind(modal));\n\n modal.setTitle(strings[0]);\n modal.setSaveButtonText(strings[0]);\n modal.setBody(bodyPromise);\n\n modal.getRoot().on(ModalEvents.save, function() {\n\n var exportOption = $(\"input[name='exportoption']:checked\").val();\n\n if (exportOption == 'exportdata') {\n a.attr('href', a.attr('href') + '&action=exportdata');\n\n } else {\n a.attr('href', a.attr('href') + '&action=exportmodel');\n if ($(\"#id-includeweights\").is(':checked')) {\n a.attr('href', a.attr('href') + '&includeweights=1');\n } else {\n a.attr('href', a.attr('href') + '&includeweights=0');\n }\n }\n\n window.location.href = a.attr('href');\n return;\n });\n\n modal.show();\n return modal;\n }).fail(Notification.exception);\n });\n }\n };\n});\n"],"names":["define","$","Str","log","Notification","ModalFactory","ModalEvents","Templates","actionsList","clear","title","key","component","body","delete","confirmAction","actionId","actionType","on","ev","preventDefault","a","currentTarget","wrap","reqStrings","param","closest","length","attr","error","stringsPromise","get_strings","modalPromise","create","type","types","SAVE_CANCEL","when","then","strings","modal","setTitle","setBody","setSaveButtonText","getRoot","save","window","location","href","show","fail","exception","selectEvaluationOptions","trainedOnlyExternally","timeSplittingMethods","this","bodyPromise","render","trainedexternally","timesplittingmethods","JSON","parse","hidden","destroy","bind","val","timeSplittingMethod","selectExportOptions","isTrained","is"],"mappings":"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"}
\ No newline at end of file
diff --git a/admin/tool/analytics/amd/build/potential-contexts.min.js.map b/admin/tool/analytics/amd/build/potential-contexts.min.js.map
index 82ec224e954..70cac7783d4 100644
--- a/admin/tool/analytics/amd/build/potential-contexts.min.js.map
+++ b/admin/tool/analytics/amd/build/potential-contexts.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"potential-contexts.min.js","sources":["../src/potential-contexts.js"],"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 * Potential contexts selector module.\n *\n * @module tool_analytics/potential-contexts\n * @copyright 2019 David Monllao\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\ndefine(['jquery', 'core/ajax'], function($, Ajax) {\n\n return /** @alias module:tool_analytics/potential-contexts */ {\n\n processResults: function(selector, results) {\n var contexts = [];\n if ($.isArray(results)) {\n $.each(results, function(index, context) {\n contexts.push({\n value: context.id,\n label: context.name\n });\n });\n return contexts;\n\n } else {\n return results;\n }\n },\n\n transport: function(selector, query, success, failure) {\n var promise;\n\n let modelid = $(selector).attr('modelid') || null;\n promise = Ajax.call([{\n methodname: 'tool_analytics_potential_contexts',\n args: {\n query: query,\n modelid: modelid\n }\n }]);\n\n promise[0].then(success).fail(failure);\n }\n\n };\n\n});\n"],"names":["define","$","Ajax","processResults","selector","results","contexts","isArray","each","index","context","push","value","id","label","name","transport","query","success","failure","modelid","attr","call","methodname","args","then","fail"],"mappings":";;;;;;;AAuBAA,2CAAO,CAAC,SAAU,cAAc,SAASC,EAAGC,YAEsB,CAE1DC,eAAgB,SAASC,SAAUC,aAC3BC,SAAW,UACXL,EAAEM,QAAQF,UACVJ,EAAEO,KAAKH,SAAS,SAASI,MAAOC,SAC5BJ,SAASK,KAAK,CACVC,MAAOF,QAAQG,GACfC,MAAOJ,QAAQK,UAGhBT,UAGAD,SAIfW,UAAW,SAASZ,SAAUa,MAAOC,QAASC,aAGtCC,QAAUnB,EAAEG,UAAUiB,KAAK,YAAc,KACnCnB,KAAKoB,KAAK,CAAC,CACjBC,WAAY,oCACZC,KAAM,CACFP,MAAOA,MACPG,QAASA,YAIT,GAAGK,KAAKP,SAASQ,KAAKP"}
\ No newline at end of file
+{"version":3,"file":"potential-contexts.min.js","sources":["../src/potential-contexts.js"],"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 * Potential contexts selector module.\n *\n * @module tool_analytics/potential-contexts\n * @copyright 2019 David Monllao\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\ndefine(['jquery', 'core/ajax'], function($, Ajax) {\n\n return /** @alias module:tool_analytics/potential-contexts */ {\n\n processResults: function(selector, results) {\n var contexts = [];\n if ($.isArray(results)) {\n $.each(results, function(index, context) {\n contexts.push({\n value: context.id,\n label: context.name\n });\n });\n return contexts;\n\n } else {\n return results;\n }\n },\n\n transport: function(selector, query, success, failure) {\n var promise;\n\n let modelid = $(selector).attr('modelid') || null;\n promise = Ajax.call([{\n methodname: 'tool_analytics_potential_contexts',\n args: {\n query: query,\n modelid: modelid\n }\n }]);\n\n promise[0].then(success).fail(failure);\n }\n\n };\n\n});\n"],"names":["define","$","Ajax","processResults","selector","results","contexts","isArray","each","index","context","push","value","id","label","name","transport","query","success","failure","modelid","attr","call","methodname","args","then","fail"],"mappings":";;;;;;;AAuBAA,OAAM,oCAAC,CAAC,SAAU,cAAc,SAASC,EAAGC,MAExC,MAA8D,CAE1DC,eAAgB,SAASC,SAAUC,SAC/B,IAAIC,SAAW,GACf,OAAIL,EAAEM,QAAQF,UACVJ,EAAEO,KAAKH,SAAS,SAASI,MAAOC,SAC5BJ,SAASK,KAAK,CACVC,MAAOF,QAAQG,GACfC,MAAOJ,QAAQK,MAEvB,IACOT,UAGAD,OAEd,EAEDW,UAAW,SAASZ,SAAUa,MAAOC,QAASC,SAG1C,IAAIC,QAAUnB,EAAEG,UAAUiB,KAAK,YAAc,KACnCnB,KAAKoB,KAAK,CAAC,CACjBC,WAAY,oCACZC,KAAM,CACFP,MAAOA,MACPG,QAASA,YAIT,GAAGK,KAAKP,SAASQ,KAAKP,QAClC,EAIR"}
\ No newline at end of file
diff --git a/admin/tool/behat/amd/build/steps.min.js.map b/admin/tool/behat/amd/build/steps.min.js.map
index 55696afe495..4c6561126ff 100644
--- a/admin/tool/behat/amd/build/steps.min.js.map
+++ b/admin/tool/behat/amd/build/steps.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"steps.min.js","sources":["../src/steps.js"],"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\nimport Ajax from 'core/ajax';\nimport Templates from 'core/templates';\nimport PendingJS from 'core/pending';\n\n/**\n * Enhancements for the step definitions page.\n *\n * @module tool_behat/steps\n * @copyright 2022 Catalyst IT EU\n * @author Mark Johnson \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\n/**\n * Call the get_entity_generator web service function\n *\n * Takes the name of an entity generator and returns an object containing a list of the required fields.\n *\n * @param {String} entityType\n * @returns {Promise}\n */\nconst getGeneratorEntities = (entityType) => Ajax.call([{\n methodname: 'tool_behat_get_entity_generator',\n args: {entitytype: entityType}\n}])[0];\n\n/**\n * Render HTML for required fields\n *\n * Takes the entity data returned from getGeneratorEntities and renders the HTML to display the required fields.\n *\n * @param {String} entityData\n * @return {Promise}\n */\nconst getRequiredFieldsContent = (entityData) => {\n if (!entityData.required?.length) {\n return Promise.resolve({\n html: '',\n js: ''\n });\n }\n return Templates.renderForPromise('tool_behat/steprequiredfields', {fields: entityData.required});\n};\n\nexport const init = () => {\n // When an entity is selected in the \"the following exist\" step, fetch and display the required fields.\n document.addEventListener('change', async(e) => {\n const entityElement = e.target.closest('.entities');\n const stepElement = e.target.closest('.stepcontent');\n if (!entityElement || !stepElement) {\n return;\n }\n\n const pendingPromise = new PendingJS('tool_behat/steps:change');\n\n const entityData = await getGeneratorEntities(e.target.value);\n const {html, js} = await getRequiredFieldsContent(entityData);\n\n const stepRequiredFields = stepElement.querySelector('.steprequiredfields');\n if (stepRequiredFields) {\n await Templates.replaceNode(stepRequiredFields, html, js);\n } else {\n await Templates.appendNodeContents(stepElement, html, js);\n }\n pendingPromise.resolve();\n });\n};\n"],"names":["document","addEventListener","async","entityElement","e","target","closest","stepElement","pendingPromise","PendingJS","entityData","entityType","value","Ajax","call","methodname","args","entitytype","html","js","required","_entityData$required","length","Templates","renderForPromise","fields","Promise","resolve","getRequiredFieldsContent","stepRequiredFields","querySelector","replaceNode","appendNodeContents"],"mappings":";;;;;;;;4NA2DoB,KAEhBA,SAASC,iBAAiB,UAAUC,MAAAA,UAC1BC,cAAgBC,EAAEC,OAAOC,QAAQ,aACjCC,YAAcH,EAAEC,OAAOC,QAAQ,oBAChCH,gBAAkBI,yBAIjBC,eAAiB,IAAIC,iBAAU,2BAE/BC,iBAlCgBC,WAkCwBP,EAAEC,OAAOO,MAlClBC,cAAKC,KAAK,CAAC,CACpDC,WAAY,kCACZC,KAAM,CAACC,WAAYN,eACnB,IAH0BA,IAAAA,iBAmChBO,KAACA,KAADC,GAAOA,SAtBaT,CAAAA,0EACzBA,WAAWU,0CAAXC,qBAAqBC,OAMnBC,mBAAUC,iBAAiB,gCAAiC,CAACC,OAAQf,WAAWU,WAL5EM,QAAQC,QAAQ,CACnBT,KAAM,GACNC,GAAI,MAkBiBS,CAAyBlB,YAE5CmB,mBAAqBtB,YAAYuB,cAAc,uBACjDD,yBACMN,mBAAUQ,YAAYF,mBAAoBX,KAAMC,UAEhDI,mBAAUS,mBAAmBzB,YAAaW,KAAMC,IAE1DX,eAAemB"}
\ No newline at end of file
+{"version":3,"file":"steps.min.js","sources":["../src/steps.js"],"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\nimport Ajax from 'core/ajax';\nimport Templates from 'core/templates';\nimport PendingJS from 'core/pending';\n\n/**\n * Enhancements for the step definitions page.\n *\n * @module tool_behat/steps\n * @copyright 2022 Catalyst IT EU\n * @author Mark Johnson \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\n/**\n * Call the get_entity_generator web service function\n *\n * Takes the name of an entity generator and returns an object containing a list of the required fields.\n *\n * @param {String} entityType\n * @returns {Promise}\n */\nconst getGeneratorEntities = (entityType) => Ajax.call([{\n methodname: 'tool_behat_get_entity_generator',\n args: {entitytype: entityType}\n}])[0];\n\n/**\n * Render HTML for required fields\n *\n * Takes the entity data returned from getGeneratorEntities and renders the HTML to display the required fields.\n *\n * @param {String} entityData\n * @return {Promise}\n */\nconst getRequiredFieldsContent = (entityData) => {\n if (!entityData.required?.length) {\n return Promise.resolve({\n html: '',\n js: ''\n });\n }\n return Templates.renderForPromise('tool_behat/steprequiredfields', {fields: entityData.required});\n};\n\nexport const init = () => {\n // When an entity is selected in the \"the following exist\" step, fetch and display the required fields.\n document.addEventListener('change', async(e) => {\n const entityElement = e.target.closest('.entities');\n const stepElement = e.target.closest('.stepcontent');\n if (!entityElement || !stepElement) {\n return;\n }\n\n const pendingPromise = new PendingJS('tool_behat/steps:change');\n\n const entityData = await getGeneratorEntities(e.target.value);\n const {html, js} = await getRequiredFieldsContent(entityData);\n\n const stepRequiredFields = stepElement.querySelector('.steprequiredfields');\n if (stepRequiredFields) {\n await Templates.replaceNode(stepRequiredFields, html, js);\n } else {\n await Templates.appendNodeContents(stepElement, html, js);\n }\n pendingPromise.resolve();\n });\n};\n"],"names":["_interopRequireDefault","obj","__esModule","default","_ajax","_templates","_pending","_exports","init","document","addEventListener","async","entityElement","e","target","closest","stepElement","pendingPromise","PendingJS","entityData","entityType","value","Ajax","call","methodname","args","entitytype","html","js","_entityData$required","required","length","Templates","renderForPromise","fields","Promise","resolve","getRequiredFieldsContent","stepRequiredFields","querySelector","replaceNode","appendNodeContents"],"mappings":"gIAiBqC,SAAAA,uBAAAC,KAAAA,OAAAA,KAAAA,IAAAC,WAAAD,IAAAE,CAAAA,QAAAF,IAAA;;;;;;;;kFAFrCG,MAAAJ,uBAAAI,OACAC,WAAAL,uBAAAK,YACAC,SAAAN,uBAAAM,UAgEEC,SAAAC,KAtBkBA,KAEhBC,SAASC,iBAAiB,UAAUC,UAChC,MAAMC,cAAgBC,EAAEC,OAAOC,QAAQ,aACjCC,YAAcH,EAAEC,OAAOC,QAAQ,gBACrC,IAAKH,gBAAkBI,YACnB,OAGJ,MAAMC,eAAiB,IAAIC,SAASf,QAAC,2BAE/BgB,iBAlCgBC,WAkCwBP,EAAEC,OAAOO,MAlClBC,cAAKC,KAAK,CAAC,CACpDC,WAAY,kCACZC,KAAM,CAACC,WAAYN,eACnB,IAH0BA,eAmCtB,MAAMO,KAACA,KAAIC,GAAEA,SAtBaT,cAAe,IAAAU,qBAC7C,OAAwBA,QAApBA,qBAACV,WAAWW,gBAAXD,IAAmBA,sBAAnBA,qBAAqBE,OAMnBC,WAAS7B,QAAC8B,iBAAiB,gCAAiC,CAACC,OAAQf,WAAWW,WAL5EK,QAAQC,QAAQ,CACnBT,KAAM,GACNC,GAAI,IAGqF,EAepES,CAAyBlB,YAE5CmB,mBAAqBtB,YAAYuB,cAAc,uBACjDD,yBACMN,WAAAA,QAAUQ,YAAYF,mBAAoBX,KAAMC,UAEhDI,WAAAA,QAAUS,mBAAmBzB,YAAaW,KAAMC,IAE1DX,eAAemB,SAAS,GAC1B,CACJ"}
\ No newline at end of file
diff --git a/admin/tool/capability/yui/build/moodle-tool_capability-search/moodle-tool_capability-search-min.js b/admin/tool/capability/yui/build/moodle-tool_capability-search/moodle-tool_capability-search-min.js
index fee3a858e71..b90e165c5b8 100644
--- a/admin/tool/capability/yui/build/moodle-tool_capability-search/moodle-tool_capability-search-min.js
+++ b/admin/tool/capability/yui/build/moodle-tool_capability-search/moodle-tool_capability-search-min.js
@@ -1 +1 @@
-YUI.add("moodle-tool_capability-search",function(s,t){var e=function(){e.superclass.constructor.apply(this,arguments)};s.extend(e,s.Base,e.prototype={form:null,select:null,selectoptions:{},input:null,button:null,cancel:null,lastsearch:null,initializer:function(){this.form=s.one("#capability-overview-form"),this.select=this.form.one("select[data-search=capability]"),this.select.setStyle("minWidth",this.select.get("offsetWidth")),this.select.get("options").each(function(t){var e=t.get("value");this.selectoptions[e]=t},this),this.button=this.form.all("input[type=submit]"),this.lastsearch=this.form.one("input[name=search]");var t=s.Node.create(''),e=s.Node.create('");this.cancel=s.Node.create(''),this.input=s.Node.create(''),t.append(e).append(this.input).append(this.cancel),this.select.insert(t,"before"),this.input.on("keyup",this.typed,this),this.select.on("change",this.validate,this),this.cancel.on("click",function(){this.input.set("value",""),this.typed()},this),this.lastsearch&&(this.input.set("value",this.lastsearch.get("value")),this.typed(),this.select.one("option[selected]")&&this.select.set("scrollTop",this.select.one("option[selected]").get("getX"))),this.validate()},validate:function(){this.button.set("disabled",""===this.select.get("value"))},typed:function(){var t,e=this.input.get("value"),s=0,i=null;for(t in this.lastsearch&&this.lastsearch.set("value",e),this.select.all("option").remove(),this.selectoptions)0<=t.indexOf(e)&&(s++,i=this.selectoptions[t],this.select.append(this.selectoptions[t]));0===s?this.input.addClass("error"):(this.input.removeClass("error"),1===s&&i.set("selected",!0)),""!==e?this.cancel.removeClass("d-none"):this.cancel.addClass("d-none"),this.validate()}},{NAME:"tool_capability-search",ATTRS:{strsearch:{}}}),M.tool_capability=M.tool_capability||{},M.tool_capability.init_capability_search=function(t){new e(t)}},"@VERSION@",{requires:["base","node"]});
\ No newline at end of file
+YUI.add("moodle-tool_capability-search",function(s,t){var e=function(){e.superclass.constructor.apply(this,arguments)};s.extend(e,s.Base,e.prototype={form:null,select:null,selectoptions:{},input:null,button:null,cancel:null,lastsearch:null,initializer:function(){this.form=s.one("#capability-overview-form"),this.select=this.form.one("select[data-search=capability]"),this.select.setStyle("minWidth",this.select.get("offsetWidth")),this.select.get("options").each(function(t){var e=t.get("value");this.selectoptions[e]=t},this),this.button=this.form.all("input[type=submit]"),this.lastsearch=this.form.one("input[name=search]");var t=s.Node.create(''),e=s.Node.create('");this.cancel=s.Node.create(''),this.input=s.Node.create(''),t.append(e).append(this.input).append(this.cancel),this.select.insert(t,"before"),this.input.on("keyup",this.typed,this),this.select.on("change",this.validate,this),this.cancel.on("click",function(){this.input.set("value",""),this.typed()},this),this.lastsearch&&(this.input.set("value",this.lastsearch.get("value")),this.typed(),this.select.one("option[selected]"))&&this.select.set("scrollTop",this.select.one("option[selected]").get("getX")),this.validate()},validate:function(){this.button.set("disabled",""===this.select.get("value"))},typed:function(){var t,e=this.input.get("value"),s=0,i=null;for(t in this.lastsearch&&this.lastsearch.set("value",e),this.select.all("option").remove(),this.selectoptions)0<=t.indexOf(e)&&(s++,i=this.selectoptions[t],this.select.append(this.selectoptions[t]));0===s?this.input.addClass("error"):(this.input.removeClass("error"),1===s&&i.set("selected",!0)),""!==e?this.cancel.removeClass("d-none"):this.cancel.addClass("d-none"),this.validate()}},{NAME:"tool_capability-search",ATTRS:{strsearch:{}}}),M.tool_capability=M.tool_capability||{},M.tool_capability.init_capability_search=function(t){new e(t)}},"@VERSION@",{requires:["base","node"]});
\ No newline at end of file
diff --git a/admin/tool/componentlibrary/amd/build/clipboardwrapper.min.js.map b/admin/tool/componentlibrary/amd/build/clipboardwrapper.min.js.map
index 0c6e79f2a59..634749e1d34 100644
--- a/admin/tool/componentlibrary/amd/build/clipboardwrapper.min.js.map
+++ b/admin/tool/componentlibrary/amd/build/clipboardwrapper.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"clipboardwrapper.min.js","sources":["../src/clipboardwrapper.js"],"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 * Wrapper to ensure that all Hugo example snippets have a \"Copy to clipboard\" button.\n *\n * @module tool_componentlibrary/clipboardwrapper\n * @copyright 2021 Bas Brands \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport 'core/copy_to_clipboard';\nimport selectors from 'tool_componentlibrary/selectors';\nimport Templates from 'core/templates';\n\nlet idCounter = 0;\n\n/**\n * Initialise the clipboard button on all reusable code.\n *\n * @method\n */\nexport const clipboardWrapper = async() => {\n document.querySelectorAll(selectors.clipboardcontent).forEach(element => {\n if (!element.id) {\n element.id = `tool_componentlibrary_content-${idCounter++}`;\n }\n Templates.renderForPromise('tool_componentlibrary/clipboardbutton', {clipboardtarget: `#${element.id} code`})\n .then(({html, js}) => {\n Templates.prependNodeContents(element, html, js);\n return;\n })\n .catch();\n });\n};\n"],"names":["idCounter","async","document","querySelectorAll","selectors","clipboardcontent","forEach","element","id","renderForPromise","clipboardtarget","then","_ref","html","js","prependNodeContents","catch"],"mappings":";;;;;;;8LA0BIA,UAAY,4BAOgBC,UAC5BC,SAASC,iBAAiBC,mBAAUC,kBAAkBC,SAAQC,UACrDA,QAAQC,KACTD,QAAQC,2CAAsCR,iCAExCS,iBAAiB,wCAAyC,CAACC,2BAAqBH,QAAQC,cACjGG,MAAKC,WAACC,KAACA,KAADC,GAAOA,4BACAC,oBAAoBR,QAASM,KAAMC,OAGhDE"}
\ No newline at end of file
+{"version":3,"file":"clipboardwrapper.min.js","sources":["../src/clipboardwrapper.js"],"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 * Wrapper to ensure that all Hugo example snippets have a \"Copy to clipboard\" button.\n *\n * @module tool_componentlibrary/clipboardwrapper\n * @copyright 2021 Bas Brands \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport 'core/copy_to_clipboard';\nimport selectors from 'tool_componentlibrary/selectors';\nimport Templates from 'core/templates';\n\nlet idCounter = 0;\n\n/**\n * Initialise the clipboard button on all reusable code.\n *\n * @method\n */\nexport const clipboardWrapper = async() => {\n document.querySelectorAll(selectors.clipboardcontent).forEach(element => {\n if (!element.id) {\n element.id = `tool_componentlibrary_content-${idCounter++}`;\n }\n Templates.renderForPromise('tool_componentlibrary/clipboardbutton', {clipboardtarget: `#${element.id} code`})\n .then(({html, js}) => {\n Templates.prependNodeContents(element, html, js);\n return;\n })\n .catch();\n });\n};\n"],"names":["_interopRequireDefault","obj","__esModule","default","_selectors","_templates","idCounter","_exports","clipboardWrapper","async","document","querySelectorAll","selectors","clipboardcontent","forEach","element","id","concat","Templates","renderForPromise","clipboardtarget","then","_ref","html","js","prependNodeContents","catch"],"mappings":"qMAwBuC,SAAAA,uBAAAC,KAAAA,OAAAA,KAAAA,IAAAC,WAAAD,IAAAE,CAAAA,QAAAF,IAAA;;;;;;;8FADvCG,WAAAJ,uBAAAI,YACAC,WAAAL,uBAAAK,YAEA,IAAIC,UAAY,EAmBdC,SAAAC,iBAZ8BC,UAC5BC,SAASC,iBAAiBC,WAAST,QAACU,kBAAkBC,SAAQC,UACrDA,QAAQC,KACTD,QAAQC,GAAE,iCAAAC,OAAoCX,cAElDY,WAAAA,QAAUC,iBAAiB,wCAAyC,CAACC,gBAAeH,IAAAA,OAAMF,QAAQC,GAAE,WACnGK,MAAKC,OAAgB,IAAfC,KAACA,KAAIC,GAAEA,IAAGF,KACbJ,WAASf,QAACsB,oBAAoBV,QAASQ,KAAMC,GAC7C,IAEHE,OAAO,GACV,CACJ"}
\ No newline at end of file
diff --git a/admin/tool/componentlibrary/amd/build/jsrunner.min.js.map b/admin/tool/componentlibrary/amd/build/jsrunner.min.js.map
index cba5b132059..959e62f072e 100644
--- a/admin/tool/componentlibrary/amd/build/jsrunner.min.js.map
+++ b/admin/tool/componentlibrary/amd/build/jsrunner.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"jsrunner.min.js","sources":["../src/jsrunner.js"],"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 * Run the JS required for example code to work in the library.\n *\n * @module tool_componentlibrary/jsrunner\n * @copyright 2021 Bas Brands \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport selectors from 'tool_componentlibrary/selectors';\n\n/**\n * The Hugo shortcodes changes the JavaScript in markdownfiles from\n * the Moodle mustache {{js}} code... {{/js}} syntax into a div with\n * attribute data-action='runjs'. See hugo/site/layouts/shortcodes/example.html.\n * This code fetches and runs the JavaScript content.\n *\n * @method\n */\nexport const jsRunner = () => {\n const compLib = document.querySelector(selectors.componentlibrary);\n compLib.querySelectorAll(selectors.jscode).forEach(runjs => {\n const script = document.createElement('script');\n script.type = 'text/javascript';\n script.innerHTML = runjs.textContent;\n document.head.appendChild(script);\n });\n};\n"],"names":["document","querySelector","selectors","componentlibrary","querySelectorAll","jscode","forEach","runjs","script","createElement","type","innerHTML","textContent","head","appendChild"],"mappings":";;;;;;;sKAiCwB,KACJA,SAASC,cAAcC,mBAAUC,kBACzCC,iBAAiBF,mBAAUG,QAAQC,SAAQC,cACzCC,OAASR,SAASS,cAAc,UACtCD,OAAOE,KAAO,kBACdF,OAAOG,UAAYJ,MAAMK,YACzBZ,SAASa,KAAKC,YAAYN"}
\ No newline at end of file
+{"version":3,"file":"jsrunner.min.js","sources":["../src/jsrunner.js"],"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 * Run the JS required for example code to work in the library.\n *\n * @module tool_componentlibrary/jsrunner\n * @copyright 2021 Bas Brands \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport selectors from 'tool_componentlibrary/selectors';\n\n/**\n * The Hugo shortcodes changes the JavaScript in markdownfiles from\n * the Moodle mustache {{js}} code... {{/js}} syntax into a div with\n * attribute data-action='runjs'. See hugo/site/layouts/shortcodes/example.html.\n * This code fetches and runs the JavaScript content.\n *\n * @method\n */\nexport const jsRunner = () => {\n const compLib = document.querySelector(selectors.componentlibrary);\n compLib.querySelectorAll(selectors.jscode).forEach(runjs => {\n const script = document.createElement('script');\n script.type = 'text/javascript';\n script.innerHTML = runjs.textContent;\n document.head.appendChild(script);\n });\n};\n"],"names":["obj","_selectors","__esModule","default","_exports","jsRunner","document","querySelector","selectors","componentlibrary","querySelectorAll","jscode","forEach","runjs","script","createElement","type","innerHTML","textContent","head","appendChild"],"mappings":"qHAuBwD,IAAAA;;;;;;;sFAAxDC,YAAwDD,IAAxDC,aAAwDD,IAAAE,WAAAF,IAAAG,CAAAA,QAAAH,KAkBtDI,SAAAC,SARsBA,KACJC,SAASC,cAAcC,WAAAA,QAAUC,kBACzCC,iBAAiBF,WAASL,QAACQ,QAAQC,SAAQC,QAC/C,MAAMC,OAASR,SAASS,cAAc,UACtCD,OAAOE,KAAO,kBACdF,OAAOG,UAAYJ,MAAMK,YACzBZ,SAASa,KAAKC,YAAYN,OAAO,GACnC,CACJ"}
\ No newline at end of file
diff --git a/admin/tool/componentlibrary/amd/build/loader.min.js.map b/admin/tool/componentlibrary/amd/build/loader.min.js.map
index cd8e5c606e5..9ddf25fcff5 100644
--- a/admin/tool/componentlibrary/amd/build/loader.min.js.map
+++ b/admin/tool/componentlibrary/amd/build/loader.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"loader.min.js","sources":["../src/loader.js"],"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 * This initialises the component library JS\n *\n * @module tool_componentlibrary/loader\n * @copyright 2021 Bas Brands \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {mustache} from './mustache';\nimport {jsRunner} from './jsrunner';\nimport {clipboardWrapper} from './clipboardwrapper';\nimport {search} from './search';\n\n/**\n * Load all the component library JavaScript.\n *\n * @param {string} jsonFile Full path to the JSON file with the search DB.\n */\nexport const init = jsonFile => {\n mustache();\n jsRunner();\n clipboardWrapper();\n search(jsonFile);\n};\n"],"names":["jsonFile"],"mappings":"yQAiCoBA,yHAITA"}
\ No newline at end of file
+{"version":3,"file":"loader.min.js","sources":["../src/loader.js"],"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 * This initialises the component library JS\n *\n * @module tool_componentlibrary/loader\n * @copyright 2021 Bas Brands \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {mustache} from './mustache';\nimport {jsRunner} from './jsrunner';\nimport {clipboardWrapper} from './clipboardwrapper';\nimport {search} from './search';\n\n/**\n * Load all the component library JavaScript.\n *\n * @param {string} jsonFile Full path to the JSON file with the search DB.\n */\nexport const init = jsonFile => {\n mustache();\n jsRunner();\n clipboardWrapper();\n search(jsonFile);\n};\n"],"names":["_exports","init","jsonFile","mustache","jsRunner","clipboardWrapper","search"],"mappings":"2PAsCEA,SAAAC,KALkBC,YAChB,EAAAC,uBACA,EAAAC,uBACA,EAAAC,uCACA,EAAAC,QAAAA,QAAOJ,SAAS,CAClB"}
\ No newline at end of file
diff --git a/admin/tool/componentlibrary/amd/build/lunr.min.js b/admin/tool/componentlibrary/amd/build/lunr.min.js
index 9273bde7c14..22497d81b86 100644
--- a/admin/tool/componentlibrary/amd/build/lunr.min.js
+++ b/admin/tool/componentlibrary/amd/build/lunr.min.js
@@ -3,6 +3,6 @@
* Copyright (C) 2020 Oliver Nightingale
* @license MIT
*/
-!function(){var global,step2list,step3list,v,C,re_mgr0,re_mgr1,re_meq1,re_s_v,re_1a,re2_1a,re_1b,re2_1b,re_1b_2,re2_1b_2,re3_1b_2,re4_1b_2,re_1c,re_2,re_3,re_4,re2_4,re_5,re_5_1,re3_5,porterStemmer,root,factory,lunr=function(config){var builder=new lunr.Builder;return builder.pipeline.add(lunr.trimmer,lunr.stopWordFilter,lunr.stemmer),builder.searchPipeline.add(lunr.stemmer),config.call(builder,builder),builder.build()};lunr.version="2.3.9",lunr.utils={},lunr.utils.warn=(global=this,function(message){global.console&&console.warn&&console.warn(message)}),lunr.utils.asString=function(obj){return null==obj?"":obj.toString()},lunr.utils.clone=function(obj){if(null==obj)return obj;for(var clone=Object.create(null),keys=Object.keys(obj),i=0;i0){var tokenMetadata=lunr.utils.clone(metadata)||{};tokenMetadata.position=[sliceStart,sliceLength],tokenMetadata.index=tokens.length,tokens.push(new lunr.Token(str.slice(sliceStart,sliceEnd),tokenMetadata))}sliceStart=sliceEnd+1}}return tokens},lunr.tokenizer.separator=/[\s\-]+/,lunr.Pipeline=function(){this._stack=[]},lunr.Pipeline.registeredFunctions=Object.create(null),lunr.Pipeline.registerFunction=function(fn,label){label in this.registeredFunctions&&lunr.utils.warn("Overwriting existing registered function: "+label),fn.label=label,lunr.Pipeline.registeredFunctions[fn.label]=fn},lunr.Pipeline.warnIfFunctionNotRegistered=function(fn){fn.label&&fn.label in this.registeredFunctions||lunr.utils.warn("Function is not registered with pipeline. This may cause problems when serialising the index.\n",fn)},lunr.Pipeline.load=function(serialised){var pipeline=new lunr.Pipeline;return serialised.forEach((function(fnName){var fn=lunr.Pipeline.registeredFunctions[fnName];if(!fn)throw new Error("Cannot load unregistered function: "+fnName);pipeline.add(fn)})),pipeline},lunr.Pipeline.prototype.add=function(){var fns=Array.prototype.slice.call(arguments);fns.forEach((function(fn){lunr.Pipeline.warnIfFunctionNotRegistered(fn),this._stack.push(fn)}),this)},lunr.Pipeline.prototype.after=function(existingFn,newFn){lunr.Pipeline.warnIfFunctionNotRegistered(newFn);var pos=this._stack.indexOf(existingFn);if(-1==pos)throw new Error("Cannot find existingFn");pos+=1,this._stack.splice(pos,0,newFn)},lunr.Pipeline.prototype.before=function(existingFn,newFn){lunr.Pipeline.warnIfFunctionNotRegistered(newFn);var pos=this._stack.indexOf(existingFn);if(-1==pos)throw new Error("Cannot find existingFn");this._stack.splice(pos,0,newFn)},lunr.Pipeline.prototype.remove=function(fn){var pos=this._stack.indexOf(fn);-1!=pos&&this._stack.splice(pos,1)},lunr.Pipeline.prototype.run=function(tokens){for(var stackLength=this._stack.length,i=0;i1&&(pivotIndexindex&&(end=pivotPoint),pivotIndex!=index);)sliceLength=end-start,pivotPoint=start+Math.floor(sliceLength/2),pivotIndex=this.elements[2*pivotPoint];return pivotIndex==index||pivotIndex>index?2*pivotPoint:pivotIndexbVal?j+=2:aVal==bVal&&(dotProduct+=a[i+1]*b[j+1],i+=2,j+=2);return dotProduct},lunr.Vector.prototype.similarity=function(otherVector){return this.dot(otherVector)/this.magnitude()||0},lunr.Vector.prototype.toArray=function(){for(var output=new Array(this.elements.length/2),i=1,j=0;i0){var noEditNode,char=frame.str.charAt(0);char in frame.node.edges?noEditNode=frame.node.edges[char]:(noEditNode=new lunr.TokenSet,frame.node.edges[char]=noEditNode),1==frame.str.length&&(noEditNode.final=!0),stack.push({node:noEditNode,editsRemaining:frame.editsRemaining,str:frame.str.slice(1)})}if(0!=frame.editsRemaining){if("*"in frame.node.edges)var insertionNode=frame.node.edges["*"];else{insertionNode=new lunr.TokenSet;frame.node.edges["*"]=insertionNode}if(0==frame.str.length&&(insertionNode.final=!0),stack.push({node:insertionNode,editsRemaining:frame.editsRemaining-1,str:frame.str}),frame.str.length>1&&stack.push({node:frame.node,editsRemaining:frame.editsRemaining-1,str:frame.str.slice(1)}),1==frame.str.length&&(frame.node.final=!0),frame.str.length>=1){if("*"in frame.node.edges)var substitutionNode=frame.node.edges["*"];else{substitutionNode=new lunr.TokenSet;frame.node.edges["*"]=substitutionNode}1==frame.str.length&&(substitutionNode.final=!0),stack.push({node:substitutionNode,editsRemaining:frame.editsRemaining-1,str:frame.str.slice(1)})}if(frame.str.length>1){var transposeNode,charA=frame.str.charAt(0),charB=frame.str.charAt(1);charB in frame.node.edges?transposeNode=frame.node.edges[charB]:(transposeNode=new lunr.TokenSet,frame.node.edges[charB]=transposeNode),1==frame.str.length&&(transposeNode.final=!0),stack.push({node:transposeNode,editsRemaining:frame.editsRemaining-1,str:charA+frame.str.slice(2)})}}}return root},lunr.TokenSet.fromString=function(str){for(var node=new lunr.TokenSet,root=node,i=0,len=str.length;i=downTo;i--){var node=this.uncheckedNodes[i],childKey=node.child.toString();childKey in this.minimizedNodes?node.parent.edges[node.char]=this.minimizedNodes[childKey]:(node.child._str=childKey,this.minimizedNodes[childKey]=node.child),this.uncheckedNodes.pop()}},lunr.Index=function(attrs){this.invertedIndex=attrs.invertedIndex,this.fieldVectors=attrs.fieldVectors,this.tokenSet=attrs.tokenSet,this.fields=attrs.fields,this.pipeline=attrs.pipeline},lunr.Index.prototype.search=function(queryString){return this.query((function(query){new lunr.QueryParser(queryString,query).parse()}))},lunr.Index.prototype.query=function(fn){for(var query=new lunr.Query(this.fields),matchingFields=Object.create(null),queryVectors=Object.create(null),termFieldCache=Object.create(null),requiredMatches=Object.create(null),prohibitedMatches=Object.create(null),i=0;i1?1:number},lunr.Builder.prototype.k1=function(number){this._k1=number},lunr.Builder.prototype.add=function(doc,attributes){var docRef=doc[this._ref],fields=Object.keys(this._fields);this._documents[docRef]=attributes||{},this.documentCount+=1;for(var i=0;i=this.length)return lunr.QueryLexer.EOS;var char=this.str.charAt(this.pos);return this.pos+=1,char},lunr.QueryLexer.prototype.width=function(){return this.pos-this.start},lunr.QueryLexer.prototype.ignore=function(){this.start==this.pos&&(this.pos+=1),this.start=this.pos},lunr.QueryLexer.prototype.backup=function(){this.pos-=1},lunr.QueryLexer.prototype.acceptDigitRun=function(){var char,charCode;do{charCode=(char=this.next()).charCodeAt(0)}while(charCode>47&&charCode<58);char!=lunr.QueryLexer.EOS&&this.backup()},lunr.QueryLexer.prototype.more=function(){return this.pos1&&(lexer.backup(),lexer.emit(lunr.QueryLexer.TERM)),lexer.ignore(),lexer.more())return lunr.QueryLexer.lexText},lunr.QueryLexer.lexEditDistance=function(lexer){return lexer.ignore(),lexer.acceptDigitRun(),lexer.emit(lunr.QueryLexer.EDIT_DISTANCE),lunr.QueryLexer.lexText},lunr.QueryLexer.lexBoost=function(lexer){return lexer.ignore(),lexer.acceptDigitRun(),lexer.emit(lunr.QueryLexer.BOOST),lunr.QueryLexer.lexText},lunr.QueryLexer.lexEOS=function(lexer){lexer.width()>0&&lexer.emit(lunr.QueryLexer.TERM)},lunr.QueryLexer.termSeparator=lunr.tokenizer.separator,lunr.QueryLexer.lexText=function(lexer){for(;;){var char=lexer.next();if(char==lunr.QueryLexer.EOS)return lunr.QueryLexer.lexEOS;if(92!=char.charCodeAt(0)){if(":"==char)return lunr.QueryLexer.lexField;if("~"==char)return lexer.backup(),lexer.width()>0&&lexer.emit(lunr.QueryLexer.TERM),lunr.QueryLexer.lexEditDistance;if("^"==char)return lexer.backup(),lexer.width()>0&&lexer.emit(lunr.QueryLexer.TERM),lunr.QueryLexer.lexBoost;if("+"==char&&1===lexer.width())return lexer.emit(lunr.QueryLexer.PRESENCE),lunr.QueryLexer.lexText;if("-"==char&&1===lexer.width())return lexer.emit(lunr.QueryLexer.PRESENCE),lunr.QueryLexer.lexText;if(char.match(lunr.QueryLexer.termSeparator))return lunr.QueryLexer.lexTerm}else lexer.escapeCharacter()}},lunr.QueryParser=function(str,query){this.lexer=new lunr.QueryLexer(str),this.query=query,this.currentClause={},this.lexemeIdx=0},lunr.QueryParser.prototype.parse=function(){this.lexer.run(),this.lexemes=this.lexer.lexemes;for(var state=lunr.QueryParser.parseClause;state;)state=state(this);return this.query},lunr.QueryParser.prototype.peekLexeme=function(){return this.lexemes[this.lexemeIdx]},lunr.QueryParser.prototype.consumeLexeme=function(){var lexeme=this.peekLexeme();return this.lexemeIdx+=1,lexeme},lunr.QueryParser.prototype.nextClause=function(){var completedClause=this.currentClause;this.query.clause(completedClause),this.currentClause={}},lunr.QueryParser.parseClause=function(parser){var lexeme=parser.peekLexeme();if(null!=lexeme)switch(lexeme.type){case lunr.QueryLexer.PRESENCE:return lunr.QueryParser.parsePresence;case lunr.QueryLexer.FIELD:return lunr.QueryParser.parseField;case lunr.QueryLexer.TERM:return lunr.QueryParser.parseTerm;default:var errorMessage="expected either a field or a term, found "+lexeme.type;throw lexeme.str.length>=1&&(errorMessage+=" with value '"+lexeme.str+"'"),new lunr.QueryParseError(errorMessage,lexeme.start,lexeme.end)}},lunr.QueryParser.parsePresence=function(parser){var lexeme=parser.consumeLexeme();if(null!=lexeme){switch(lexeme.str){case"-":parser.currentClause.presence=lunr.Query.presence.PROHIBITED;break;case"+":parser.currentClause.presence=lunr.Query.presence.REQUIRED;break;default:var errorMessage="unrecognised presence operator'"+lexeme.str+"'";throw new lunr.QueryParseError(errorMessage,lexeme.start,lexeme.end)}var nextLexeme=parser.peekLexeme();if(null==nextLexeme){errorMessage="expecting term or field, found nothing";throw new lunr.QueryParseError(errorMessage,lexeme.start,lexeme.end)}switch(nextLexeme.type){case lunr.QueryLexer.FIELD:return lunr.QueryParser.parseField;case lunr.QueryLexer.TERM:return lunr.QueryParser.parseTerm;default:errorMessage="expecting term or field, found '"+nextLexeme.type+"'";throw new lunr.QueryParseError(errorMessage,nextLexeme.start,nextLexeme.end)}}},lunr.QueryParser.parseField=function(parser){var lexeme=parser.consumeLexeme();if(null!=lexeme){if(-1==parser.query.allFields.indexOf(lexeme.str)){var possibleFields=parser.query.allFields.map((function(f){return"'"+f+"'"})).join(", "),errorMessage="unrecognised field '"+lexeme.str+"', possible fields: "+possibleFields;throw new lunr.QueryParseError(errorMessage,lexeme.start,lexeme.end)}parser.currentClause.fields=[lexeme.str];var nextLexeme=parser.peekLexeme();if(null==nextLexeme){errorMessage="expecting term, found nothing";throw new lunr.QueryParseError(errorMessage,lexeme.start,lexeme.end)}if(nextLexeme.type===lunr.QueryLexer.TERM)return lunr.QueryParser.parseTerm;errorMessage="expecting term, found '"+nextLexeme.type+"'";throw new lunr.QueryParseError(errorMessage,nextLexeme.start,nextLexeme.end)}},lunr.QueryParser.parseTerm=function(parser){var lexeme=parser.consumeLexeme();if(null!=lexeme){parser.currentClause.term=lexeme.str.toLowerCase(),-1!=lexeme.str.indexOf("*")&&(parser.currentClause.usePipeline=!1);var nextLexeme=parser.peekLexeme();if(null!=nextLexeme)switch(nextLexeme.type){case lunr.QueryLexer.TERM:return parser.nextClause(),lunr.QueryParser.parseTerm;case lunr.QueryLexer.FIELD:return parser.nextClause(),lunr.QueryParser.parseField;case lunr.QueryLexer.EDIT_DISTANCE:return lunr.QueryParser.parseEditDistance;case lunr.QueryLexer.BOOST:return lunr.QueryParser.parseBoost;case lunr.QueryLexer.PRESENCE:return parser.nextClause(),lunr.QueryParser.parsePresence;default:var errorMessage="Unexpected lexeme type '"+nextLexeme.type+"'";throw new lunr.QueryParseError(errorMessage,nextLexeme.start,nextLexeme.end)}else parser.nextClause()}},lunr.QueryParser.parseEditDistance=function(parser){var lexeme=parser.consumeLexeme();if(null!=lexeme){var editDistance=parseInt(lexeme.str,10);if(isNaN(editDistance)){var errorMessage="edit distance must be numeric";throw new lunr.QueryParseError(errorMessage,lexeme.start,lexeme.end)}parser.currentClause.editDistance=editDistance;var nextLexeme=parser.peekLexeme();if(null!=nextLexeme)switch(nextLexeme.type){case lunr.QueryLexer.TERM:return parser.nextClause(),lunr.QueryParser.parseTerm;case lunr.QueryLexer.FIELD:return parser.nextClause(),lunr.QueryParser.parseField;case lunr.QueryLexer.EDIT_DISTANCE:return lunr.QueryParser.parseEditDistance;case lunr.QueryLexer.BOOST:return lunr.QueryParser.parseBoost;case lunr.QueryLexer.PRESENCE:return parser.nextClause(),lunr.QueryParser.parsePresence;default:errorMessage="Unexpected lexeme type '"+nextLexeme.type+"'";throw new lunr.QueryParseError(errorMessage,nextLexeme.start,nextLexeme.end)}else parser.nextClause()}},lunr.QueryParser.parseBoost=function(parser){var lexeme=parser.consumeLexeme();if(null!=lexeme){var boost=parseInt(lexeme.str,10);if(isNaN(boost)){var errorMessage="boost must be numeric";throw new lunr.QueryParseError(errorMessage,lexeme.start,lexeme.end)}parser.currentClause.boost=boost;var nextLexeme=parser.peekLexeme();if(null!=nextLexeme)switch(nextLexeme.type){case lunr.QueryLexer.TERM:return parser.nextClause(),lunr.QueryParser.parseTerm;case lunr.QueryLexer.FIELD:return parser.nextClause(),lunr.QueryParser.parseField;case lunr.QueryLexer.EDIT_DISTANCE:return lunr.QueryParser.parseEditDistance;case lunr.QueryLexer.BOOST:return lunr.QueryParser.parseBoost;case lunr.QueryLexer.PRESENCE:return parser.nextClause(),lunr.QueryParser.parsePresence;default:errorMessage="Unexpected lexeme type '"+nextLexeme.type+"'";throw new lunr.QueryParseError(errorMessage,nextLexeme.start,nextLexeme.end)}else parser.nextClause()}},root=this,factory=function(){return lunr},"function"==typeof define&&define.amd?define("tool_componentlibrary/lunr",factory):"object"==typeof exports?module.exports=factory():root.lunr=factory()}();
+!function(){var global,step2list,step3list,v,C,V,meq1,mgr1,s_v,re_mgr0,re_mgr1,re_meq1,re_s_v,re_1a,re2_1a,re_1b,re2_1b,re_1b_2,re2_1b_2,re3_1b_2,re4_1b_2,re_1c,re_2,re_3,re_4,re2_4,re_5,re_5_1,re3_5,porterStemmer,root,factory,lunr=function(config){var builder=new lunr.Builder;return builder.pipeline.add(lunr.trimmer,lunr.stopWordFilter,lunr.stemmer),builder.searchPipeline.add(lunr.stemmer),config.call(builder,builder),builder.build()};lunr.version="2.3.9",lunr.utils={},lunr.utils.warn=(global=this,function(message){global.console&&console.warn&&console.warn(message)}),lunr.utils.asString=function(obj){return null==obj?"":obj.toString()},lunr.utils.clone=function(obj){if(null==obj)return obj;for(var clone=Object.create(null),keys=Object.keys(obj),i=0;i0){var tokenMetadata=lunr.utils.clone(metadata)||{};tokenMetadata.position=[sliceStart,sliceLength],tokenMetadata.index=tokens.length,tokens.push(new lunr.Token(str.slice(sliceStart,sliceEnd),tokenMetadata))}sliceStart=sliceEnd+1}}return tokens},lunr.tokenizer.separator=/[\s\-]+/,lunr.Pipeline=function(){this._stack=[]},lunr.Pipeline.registeredFunctions=Object.create(null),lunr.Pipeline.registerFunction=function(fn,label){label in this.registeredFunctions&&lunr.utils.warn("Overwriting existing registered function: "+label),fn.label=label,lunr.Pipeline.registeredFunctions[fn.label]=fn},lunr.Pipeline.warnIfFunctionNotRegistered=function(fn){fn.label&&fn.label in this.registeredFunctions||lunr.utils.warn("Function is not registered with pipeline. This may cause problems when serialising the index.\n",fn)},lunr.Pipeline.load=function(serialised){var pipeline=new lunr.Pipeline;return serialised.forEach((function(fnName){var fn=lunr.Pipeline.registeredFunctions[fnName];if(!fn)throw new Error("Cannot load unregistered function: "+fnName);pipeline.add(fn)})),pipeline},lunr.Pipeline.prototype.add=function(){Array.prototype.slice.call(arguments).forEach((function(fn){lunr.Pipeline.warnIfFunctionNotRegistered(fn),this._stack.push(fn)}),this)},lunr.Pipeline.prototype.after=function(existingFn,newFn){lunr.Pipeline.warnIfFunctionNotRegistered(newFn);var pos=this._stack.indexOf(existingFn);if(-1==pos)throw new Error("Cannot find existingFn");pos+=1,this._stack.splice(pos,0,newFn)},lunr.Pipeline.prototype.before=function(existingFn,newFn){lunr.Pipeline.warnIfFunctionNotRegistered(newFn);var pos=this._stack.indexOf(existingFn);if(-1==pos)throw new Error("Cannot find existingFn");this._stack.splice(pos,0,newFn)},lunr.Pipeline.prototype.remove=function(fn){var pos=this._stack.indexOf(fn);-1!=pos&&this._stack.splice(pos,1)},lunr.Pipeline.prototype.run=function(tokens){for(var stackLength=this._stack.length,i=0;i1&&(pivotIndexindex&&(end=pivotPoint),pivotIndex!=index);)sliceLength=end-start,pivotPoint=start+Math.floor(sliceLength/2),pivotIndex=this.elements[2*pivotPoint];return pivotIndex==index||pivotIndex>index?2*pivotPoint:pivotIndexbVal?j+=2:aVal==bVal&&(dotProduct+=a[i+1]*b[j+1],i+=2,j+=2);return dotProduct},lunr.Vector.prototype.similarity=function(otherVector){return this.dot(otherVector)/this.magnitude()||0},lunr.Vector.prototype.toArray=function(){for(var output=new Array(this.elements.length/2),i=1,j=0;i0){var noEditNode,char=frame.str.charAt(0);char in frame.node.edges?noEditNode=frame.node.edges[char]:(noEditNode=new lunr.TokenSet,frame.node.edges[char]=noEditNode),1==frame.str.length&&(noEditNode.final=!0),stack.push({node:noEditNode,editsRemaining:frame.editsRemaining,str:frame.str.slice(1)})}if(0!=frame.editsRemaining){if("*"in frame.node.edges)var insertionNode=frame.node.edges["*"];else{insertionNode=new lunr.TokenSet;frame.node.edges["*"]=insertionNode}if(0==frame.str.length&&(insertionNode.final=!0),stack.push({node:insertionNode,editsRemaining:frame.editsRemaining-1,str:frame.str}),frame.str.length>1&&stack.push({node:frame.node,editsRemaining:frame.editsRemaining-1,str:frame.str.slice(1)}),1==frame.str.length&&(frame.node.final=!0),frame.str.length>=1){if("*"in frame.node.edges)var substitutionNode=frame.node.edges["*"];else{substitutionNode=new lunr.TokenSet;frame.node.edges["*"]=substitutionNode}1==frame.str.length&&(substitutionNode.final=!0),stack.push({node:substitutionNode,editsRemaining:frame.editsRemaining-1,str:frame.str.slice(1)})}if(frame.str.length>1){var transposeNode,charA=frame.str.charAt(0),charB=frame.str.charAt(1);charB in frame.node.edges?transposeNode=frame.node.edges[charB]:(transposeNode=new lunr.TokenSet,frame.node.edges[charB]=transposeNode),1==frame.str.length&&(transposeNode.final=!0),stack.push({node:transposeNode,editsRemaining:frame.editsRemaining-1,str:charA+frame.str.slice(2)})}}}return root},lunr.TokenSet.fromString=function(str){for(var node=new lunr.TokenSet,root=node,i=0,len=str.length;i=downTo;i--){var node=this.uncheckedNodes[i],childKey=node.child.toString();childKey in this.minimizedNodes?node.parent.edges[node.char]=this.minimizedNodes[childKey]:(node.child._str=childKey,this.minimizedNodes[childKey]=node.child),this.uncheckedNodes.pop()}},lunr.Index=function(attrs){this.invertedIndex=attrs.invertedIndex,this.fieldVectors=attrs.fieldVectors,this.tokenSet=attrs.tokenSet,this.fields=attrs.fields,this.pipeline=attrs.pipeline},lunr.Index.prototype.search=function(queryString){return this.query((function(query){new lunr.QueryParser(queryString,query).parse()}))},lunr.Index.prototype.query=function(fn){for(var query=new lunr.Query(this.fields),matchingFields=Object.create(null),queryVectors=Object.create(null),termFieldCache=Object.create(null),requiredMatches=Object.create(null),prohibitedMatches=Object.create(null),i=0;i1?1:number},lunr.Builder.prototype.k1=function(number){this._k1=number},lunr.Builder.prototype.add=function(doc,attributes){var docRef=doc[this._ref],fields=Object.keys(this._fields);this._documents[docRef]=attributes||{},this.documentCount+=1;for(var i=0;i=this.length)return lunr.QueryLexer.EOS;var char=this.str.charAt(this.pos);return this.pos+=1,char},lunr.QueryLexer.prototype.width=function(){return this.pos-this.start},lunr.QueryLexer.prototype.ignore=function(){this.start==this.pos&&(this.pos+=1),this.start=this.pos},lunr.QueryLexer.prototype.backup=function(){this.pos-=1},lunr.QueryLexer.prototype.acceptDigitRun=function(){var char,charCode;do{charCode=(char=this.next()).charCodeAt(0)}while(charCode>47&&charCode<58);char!=lunr.QueryLexer.EOS&&this.backup()},lunr.QueryLexer.prototype.more=function(){return this.pos1&&(lexer.backup(),lexer.emit(lunr.QueryLexer.TERM)),lexer.ignore(),lexer.more())return lunr.QueryLexer.lexText},lunr.QueryLexer.lexEditDistance=function(lexer){return lexer.ignore(),lexer.acceptDigitRun(),lexer.emit(lunr.QueryLexer.EDIT_DISTANCE),lunr.QueryLexer.lexText},lunr.QueryLexer.lexBoost=function(lexer){return lexer.ignore(),lexer.acceptDigitRun(),lexer.emit(lunr.QueryLexer.BOOST),lunr.QueryLexer.lexText},lunr.QueryLexer.lexEOS=function(lexer){lexer.width()>0&&lexer.emit(lunr.QueryLexer.TERM)},lunr.QueryLexer.termSeparator=lunr.tokenizer.separator,lunr.QueryLexer.lexText=function(lexer){for(;;){var char=lexer.next();if(char==lunr.QueryLexer.EOS)return lunr.QueryLexer.lexEOS;if(92!=char.charCodeAt(0)){if(":"==char)return lunr.QueryLexer.lexField;if("~"==char)return lexer.backup(),lexer.width()>0&&lexer.emit(lunr.QueryLexer.TERM),lunr.QueryLexer.lexEditDistance;if("^"==char)return lexer.backup(),lexer.width()>0&&lexer.emit(lunr.QueryLexer.TERM),lunr.QueryLexer.lexBoost;if("+"==char&&1===lexer.width())return lexer.emit(lunr.QueryLexer.PRESENCE),lunr.QueryLexer.lexText;if("-"==char&&1===lexer.width())return lexer.emit(lunr.QueryLexer.PRESENCE),lunr.QueryLexer.lexText;if(char.match(lunr.QueryLexer.termSeparator))return lunr.QueryLexer.lexTerm}else lexer.escapeCharacter()}},lunr.QueryParser=function(str,query){this.lexer=new lunr.QueryLexer(str),this.query=query,this.currentClause={},this.lexemeIdx=0},lunr.QueryParser.prototype.parse=function(){this.lexer.run(),this.lexemes=this.lexer.lexemes;for(var state=lunr.QueryParser.parseClause;state;)state=state(this);return this.query},lunr.QueryParser.prototype.peekLexeme=function(){return this.lexemes[this.lexemeIdx]},lunr.QueryParser.prototype.consumeLexeme=function(){var lexeme=this.peekLexeme();return this.lexemeIdx+=1,lexeme},lunr.QueryParser.prototype.nextClause=function(){var completedClause=this.currentClause;this.query.clause(completedClause),this.currentClause={}},lunr.QueryParser.parseClause=function(parser){var lexeme=parser.peekLexeme();if(null!=lexeme)switch(lexeme.type){case lunr.QueryLexer.PRESENCE:return lunr.QueryParser.parsePresence;case lunr.QueryLexer.FIELD:return lunr.QueryParser.parseField;case lunr.QueryLexer.TERM:return lunr.QueryParser.parseTerm;default:var errorMessage="expected either a field or a term, found "+lexeme.type;throw lexeme.str.length>=1&&(errorMessage+=" with value '"+lexeme.str+"'"),new lunr.QueryParseError(errorMessage,lexeme.start,lexeme.end)}},lunr.QueryParser.parsePresence=function(parser){var lexeme=parser.consumeLexeme();if(null!=lexeme){switch(lexeme.str){case"-":parser.currentClause.presence=lunr.Query.presence.PROHIBITED;break;case"+":parser.currentClause.presence=lunr.Query.presence.REQUIRED;break;default:var errorMessage="unrecognised presence operator'"+lexeme.str+"'";throw new lunr.QueryParseError(errorMessage,lexeme.start,lexeme.end)}var nextLexeme=parser.peekLexeme();if(null==nextLexeme){errorMessage="expecting term or field, found nothing";throw new lunr.QueryParseError(errorMessage,lexeme.start,lexeme.end)}switch(nextLexeme.type){case lunr.QueryLexer.FIELD:return lunr.QueryParser.parseField;case lunr.QueryLexer.TERM:return lunr.QueryParser.parseTerm;default:errorMessage="expecting term or field, found '"+nextLexeme.type+"'";throw new lunr.QueryParseError(errorMessage,nextLexeme.start,nextLexeme.end)}}},lunr.QueryParser.parseField=function(parser){var lexeme=parser.consumeLexeme();if(null!=lexeme){if(-1==parser.query.allFields.indexOf(lexeme.str)){var possibleFields=parser.query.allFields.map((function(f){return"'"+f+"'"})).join(", "),errorMessage="unrecognised field '"+lexeme.str+"', possible fields: "+possibleFields;throw new lunr.QueryParseError(errorMessage,lexeme.start,lexeme.end)}parser.currentClause.fields=[lexeme.str];var nextLexeme=parser.peekLexeme();if(null==nextLexeme){errorMessage="expecting term, found nothing";throw new lunr.QueryParseError(errorMessage,lexeme.start,lexeme.end)}if(nextLexeme.type===lunr.QueryLexer.TERM)return lunr.QueryParser.parseTerm;errorMessage="expecting term, found '"+nextLexeme.type+"'";throw new lunr.QueryParseError(errorMessage,nextLexeme.start,nextLexeme.end)}},lunr.QueryParser.parseTerm=function(parser){var lexeme=parser.consumeLexeme();if(null!=lexeme){parser.currentClause.term=lexeme.str.toLowerCase(),-1!=lexeme.str.indexOf("*")&&(parser.currentClause.usePipeline=!1);var nextLexeme=parser.peekLexeme();if(null!=nextLexeme)switch(nextLexeme.type){case lunr.QueryLexer.TERM:return parser.nextClause(),lunr.QueryParser.parseTerm;case lunr.QueryLexer.FIELD:return parser.nextClause(),lunr.QueryParser.parseField;case lunr.QueryLexer.EDIT_DISTANCE:return lunr.QueryParser.parseEditDistance;case lunr.QueryLexer.BOOST:return lunr.QueryParser.parseBoost;case lunr.QueryLexer.PRESENCE:return parser.nextClause(),lunr.QueryParser.parsePresence;default:var errorMessage="Unexpected lexeme type '"+nextLexeme.type+"'";throw new lunr.QueryParseError(errorMessage,nextLexeme.start,nextLexeme.end)}else parser.nextClause()}},lunr.QueryParser.parseEditDistance=function(parser){var lexeme=parser.consumeLexeme();if(null!=lexeme){var editDistance=parseInt(lexeme.str,10);if(isNaN(editDistance)){var errorMessage="edit distance must be numeric";throw new lunr.QueryParseError(errorMessage,lexeme.start,lexeme.end)}parser.currentClause.editDistance=editDistance;var nextLexeme=parser.peekLexeme();if(null!=nextLexeme)switch(nextLexeme.type){case lunr.QueryLexer.TERM:return parser.nextClause(),lunr.QueryParser.parseTerm;case lunr.QueryLexer.FIELD:return parser.nextClause(),lunr.QueryParser.parseField;case lunr.QueryLexer.EDIT_DISTANCE:return lunr.QueryParser.parseEditDistance;case lunr.QueryLexer.BOOST:return lunr.QueryParser.parseBoost;case lunr.QueryLexer.PRESENCE:return parser.nextClause(),lunr.QueryParser.parsePresence;default:errorMessage="Unexpected lexeme type '"+nextLexeme.type+"'";throw new lunr.QueryParseError(errorMessage,nextLexeme.start,nextLexeme.end)}else parser.nextClause()}},lunr.QueryParser.parseBoost=function(parser){var lexeme=parser.consumeLexeme();if(null!=lexeme){var boost=parseInt(lexeme.str,10);if(isNaN(boost)){var errorMessage="boost must be numeric";throw new lunr.QueryParseError(errorMessage,lexeme.start,lexeme.end)}parser.currentClause.boost=boost;var nextLexeme=parser.peekLexeme();if(null!=nextLexeme)switch(nextLexeme.type){case lunr.QueryLexer.TERM:return parser.nextClause(),lunr.QueryParser.parseTerm;case lunr.QueryLexer.FIELD:return parser.nextClause(),lunr.QueryParser.parseField;case lunr.QueryLexer.EDIT_DISTANCE:return lunr.QueryParser.parseEditDistance;case lunr.QueryLexer.BOOST:return lunr.QueryParser.parseBoost;case lunr.QueryLexer.PRESENCE:return parser.nextClause(),lunr.QueryParser.parsePresence;default:errorMessage="Unexpected lexeme type '"+nextLexeme.type+"'";throw new lunr.QueryParseError(errorMessage,nextLexeme.start,nextLexeme.end)}else parser.nextClause()}},root=this,factory=function(){return lunr},"function"==typeof define&&define.amd?define("tool_componentlibrary/lunr",factory):"object"==typeof exports?module.exports=factory():root.lunr=factory()}();
//# sourceMappingURL=lunr.min.js.map
\ No newline at end of file
diff --git a/admin/tool/componentlibrary/amd/build/lunr.min.js.map b/admin/tool/componentlibrary/amd/build/lunr.min.js.map
index fb888b2a655..289794a2303 100644
--- a/admin/tool/componentlibrary/amd/build/lunr.min.js.map
+++ b/admin/tool/componentlibrary/amd/build/lunr.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"lunr.min.js","sources":["../src/lunr.js"],"sourcesContent":["/**\n * moodle readme\n *\n * Lunrjs can be downloaded from https://github.com/olivernn/lunr.js. To update this library get the lunr.js file\n * from this project and replace the content below with the new content.\n */\n\n/**\n * lunr - http://lunrjs.com - A bit like Solr, but much smaller and not as bright - 2.3.9\n * Copyright (C) 2020 Oliver Nightingale\n * @license MIT\n */\n\n;(function(){\n\n/**\n * A convenience function for configuring and constructing\n * a new lunr Index.\n *\n * A lunr.Builder instance is created and the pipeline setup\n * with a trimmer, stop word filter and stemmer.\n *\n * This builder object is yielded to the configuration function\n * that is passed as a parameter, allowing the list of fields\n * and other builder parameters to be customised.\n *\n * All documents _must_ be added within the passed config function.\n *\n * @example\n * var idx = lunr(function () {\n * this.field('title')\n * this.field('body')\n * this.ref('id')\n *\n * documents.forEach(function (doc) {\n * this.add(doc)\n * }, this)\n * })\n *\n * @see {@link lunr.Builder}\n * @see {@link lunr.Pipeline}\n * @see {@link lunr.trimmer}\n * @see {@link lunr.stopWordFilter}\n * @see {@link lunr.stemmer}\n * @namespace {function} lunr\n */\nvar lunr = function (config) {\n var builder = new lunr.Builder\n\n builder.pipeline.add(\n lunr.trimmer,\n lunr.stopWordFilter,\n lunr.stemmer\n )\n\n builder.searchPipeline.add(\n lunr.stemmer\n )\n\n config.call(builder, builder)\n return builder.build()\n}\n\nlunr.version = \"2.3.9\"\n/*!\n * lunr.utils\n * Copyright (C) 2020 Oliver Nightingale\n */\n\n/**\n * A namespace containing utils for the rest of the lunr library\n * @namespace lunr.utils\n */\nlunr.utils = {}\n\n/**\n * Print a warning message to the console.\n *\n * @param {String} message The message to be printed.\n * @memberOf lunr.utils\n * @function\n */\nlunr.utils.warn = (function (global) {\n /* eslint-disable no-console */\n return function (message) {\n if (global.console && console.warn) {\n console.warn(message)\n }\n }\n /* eslint-enable no-console */\n})(this)\n\n/**\n * Convert an object to a string.\n *\n * In the case of `null` and `undefined` the function returns\n * the empty string, in all other cases the result of calling\n * `toString` on the passed object is returned.\n *\n * @param {Any} obj The object to convert to a string.\n * @return {String} string representation of the passed object.\n * @memberOf lunr.utils\n */\nlunr.utils.asString = function (obj) {\n if (obj === void 0 || obj === null) {\n return \"\"\n } else {\n return obj.toString()\n }\n}\n\n/**\n * Clones an object.\n *\n * Will create a copy of an existing object such that any mutations\n * on the copy cannot affect the original.\n *\n * Only shallow objects are supported, passing a nested object to this\n * function will cause a TypeError.\n *\n * Objects with primitives, and arrays of primitives are supported.\n *\n * @param {Object} obj The object to clone.\n * @return {Object} a clone of the passed object.\n * @throws {TypeError} when a nested object is passed.\n * @memberOf Utils\n */\nlunr.utils.clone = function (obj) {\n if (obj === null || obj === undefined) {\n return obj\n }\n\n var clone = Object.create(null),\n keys = Object.keys(obj)\n\n for (var i = 0; i < keys.length; i++) {\n var key = keys[i],\n val = obj[key]\n\n if (Array.isArray(val)) {\n clone[key] = val.slice()\n continue\n }\n\n if (typeof val === 'string' ||\n typeof val === 'number' ||\n typeof val === 'boolean') {\n clone[key] = val\n continue\n }\n\n throw new TypeError(\"clone is not deep and does not support nested objects\")\n }\n\n return clone\n}\nlunr.FieldRef = function (docRef, fieldName, stringValue) {\n this.docRef = docRef\n this.fieldName = fieldName\n this._stringValue = stringValue\n}\n\nlunr.FieldRef.joiner = \"/\"\n\nlunr.FieldRef.fromString = function (s) {\n var n = s.indexOf(lunr.FieldRef.joiner)\n\n if (n === -1) {\n throw \"malformed field ref string\"\n }\n\n var fieldRef = s.slice(0, n),\n docRef = s.slice(n + 1)\n\n return new lunr.FieldRef (docRef, fieldRef, s)\n}\n\nlunr.FieldRef.prototype.toString = function () {\n if (this._stringValue == undefined) {\n this._stringValue = this.fieldName + lunr.FieldRef.joiner + this.docRef\n }\n\n return this._stringValue\n}\n/*!\n * lunr.Set\n * Copyright (C) 2020 Oliver Nightingale\n */\n\n/**\n * A lunr set.\n *\n * @constructor\n */\nlunr.Set = function (elements) {\n this.elements = Object.create(null)\n\n if (elements) {\n this.length = elements.length\n\n for (var i = 0; i < this.length; i++) {\n this.elements[elements[i]] = true\n }\n } else {\n this.length = 0\n }\n}\n\n/**\n * A complete set that contains all elements.\n *\n * @static\n * @readonly\n * @type {lunr.Set}\n */\nlunr.Set.complete = {\n intersect: function (other) {\n return other\n },\n\n union: function () {\n return this\n },\n\n contains: function () {\n return true\n }\n}\n\n/**\n * An empty set that contains no elements.\n *\n * @static\n * @readonly\n * @type {lunr.Set}\n */\nlunr.Set.empty = {\n intersect: function () {\n return this\n },\n\n union: function (other) {\n return other\n },\n\n contains: function () {\n return false\n }\n}\n\n/**\n * Returns true if this set contains the specified object.\n *\n * @param {object} object - Object whose presence in this set is to be tested.\n * @returns {boolean} - True if this set contains the specified object.\n */\nlunr.Set.prototype.contains = function (object) {\n return !!this.elements[object]\n}\n\n/**\n * Returns a new set containing only the elements that are present in both\n * this set and the specified set.\n *\n * @param {lunr.Set} other - set to intersect with this set.\n * @returns {lunr.Set} a new set that is the intersection of this and the specified set.\n */\n\nlunr.Set.prototype.intersect = function (other) {\n var a, b, elements, intersection = []\n\n if (other === lunr.Set.complete) {\n return this\n }\n\n if (other === lunr.Set.empty) {\n return other\n }\n\n if (this.length < other.length) {\n a = this\n b = other\n } else {\n a = other\n b = this\n }\n\n elements = Object.keys(a.elements)\n\n for (var i = 0; i < elements.length; i++) {\n var element = elements[i]\n if (element in b.elements) {\n intersection.push(element)\n }\n }\n\n return new lunr.Set (intersection)\n}\n\n/**\n * Returns a new set combining the elements of this and the specified set.\n *\n * @param {lunr.Set} other - set to union with this set.\n * @return {lunr.Set} a new set that is the union of this and the specified set.\n */\n\nlunr.Set.prototype.union = function (other) {\n if (other === lunr.Set.complete) {\n return lunr.Set.complete\n }\n\n if (other === lunr.Set.empty) {\n return this\n }\n\n return new lunr.Set(Object.keys(this.elements).concat(Object.keys(other.elements)))\n}\n/**\n * A function to calculate the inverse document frequency for\n * a posting. This is shared between the builder and the index\n *\n * @private\n * @param {object} posting - The posting for a given term\n * @param {number} documentCount - The total number of documents.\n */\nlunr.idf = function (posting, documentCount) {\n var documentsWithTerm = 0\n\n for (var fieldName in posting) {\n if (fieldName == '_index') continue // Ignore the term index, its not a field\n documentsWithTerm += Object.keys(posting[fieldName]).length\n }\n\n var x = (documentCount - documentsWithTerm + 0.5) / (documentsWithTerm + 0.5)\n\n return Math.log(1 + Math.abs(x))\n}\n\n/**\n * A token wraps a string representation of a token\n * as it is passed through the text processing pipeline.\n *\n * @constructor\n * @param {string} [str=''] - The string token being wrapped.\n * @param {object} [metadata={}] - Metadata associated with this token.\n */\nlunr.Token = function (str, metadata) {\n this.str = str || \"\"\n this.metadata = metadata || {}\n}\n\n/**\n * Returns the token string that is being wrapped by this object.\n *\n * @returns {string}\n */\nlunr.Token.prototype.toString = function () {\n return this.str\n}\n\n/**\n * A token update function is used when updating or optionally\n * when cloning a token.\n *\n * @callback lunr.Token~updateFunction\n * @param {string} str - The string representation of the token.\n * @param {Object} metadata - All metadata associated with this token.\n */\n\n/**\n * Applies the given function to the wrapped string token.\n *\n * @example\n * token.update(function (str, metadata) {\n * return str.toUpperCase()\n * })\n *\n * @param {lunr.Token~updateFunction} fn - A function to apply to the token string.\n * @returns {lunr.Token}\n */\nlunr.Token.prototype.update = function (fn) {\n this.str = fn(this.str, this.metadata)\n return this\n}\n\n/**\n * Creates a clone of this token. Optionally a function can be\n * applied to the cloned token.\n *\n * @param {lunr.Token~updateFunction} [fn] - An optional function to apply to the cloned token.\n * @returns {lunr.Token}\n */\nlunr.Token.prototype.clone = function (fn) {\n fn = fn || function (s) { return s }\n return new lunr.Token (fn(this.str, this.metadata), this.metadata)\n}\n/*!\n * lunr.tokenizer\n * Copyright (C) 2020 Oliver Nightingale\n */\n\n/**\n * A function for splitting a string into tokens ready to be inserted into\n * the search index. Uses `lunr.tokenizer.separator` to split strings, change\n * the value of this property to change how strings are split into tokens.\n *\n * This tokenizer will convert its parameter to a string by calling `toString` and\n * then will split this string on the character in `lunr.tokenizer.separator`.\n * Arrays will have their elements converted to strings and wrapped in a lunr.Token.\n *\n * Optional metadata can be passed to the tokenizer, this metadata will be cloned and\n * added as metadata to every token that is created from the object to be tokenized.\n *\n * @static\n * @param {?(string|object|object[])} obj - The object to convert into tokens\n * @param {?object} metadata - Optional metadata to associate with every token\n * @returns {lunr.Token[]}\n * @see {@link lunr.Pipeline}\n */\nlunr.tokenizer = function (obj, metadata) {\n if (obj == null || obj == undefined) {\n return []\n }\n\n if (Array.isArray(obj)) {\n return obj.map(function (t) {\n return new lunr.Token(\n lunr.utils.asString(t).toLowerCase(),\n lunr.utils.clone(metadata)\n )\n })\n }\n\n var str = obj.toString().toLowerCase(),\n len = str.length,\n tokens = []\n\n for (var sliceEnd = 0, sliceStart = 0; sliceEnd <= len; sliceEnd++) {\n var char = str.charAt(sliceEnd),\n sliceLength = sliceEnd - sliceStart\n\n if ((char.match(lunr.tokenizer.separator) || sliceEnd == len)) {\n\n if (sliceLength > 0) {\n var tokenMetadata = lunr.utils.clone(metadata) || {}\n tokenMetadata[\"position\"] = [sliceStart, sliceLength]\n tokenMetadata[\"index\"] = tokens.length\n\n tokens.push(\n new lunr.Token (\n str.slice(sliceStart, sliceEnd),\n tokenMetadata\n )\n )\n }\n\n sliceStart = sliceEnd + 1\n }\n\n }\n\n return tokens\n}\n\n/**\n * The separator used to split a string into tokens. Override this property to change the behaviour of\n * `lunr.tokenizer` behaviour when tokenizing strings. By default this splits on whitespace and hyphens.\n *\n * @static\n * @see lunr.tokenizer\n */\nlunr.tokenizer.separator = /[\\s\\-]+/\n/*!\n * lunr.Pipeline\n * Copyright (C) 2020 Oliver Nightingale\n */\n\n/**\n * lunr.Pipelines maintain an ordered list of functions to be applied to all\n * tokens in documents entering the search index and queries being ran against\n * the index.\n *\n * An instance of lunr.Index created with the lunr shortcut will contain a\n * pipeline with a stop word filter and an English language stemmer. Extra\n * functions can be added before or after either of these functions or these\n * default functions can be removed.\n *\n * When run the pipeline will call each function in turn, passing a token, the\n * index of that token in the original list of all tokens and finally a list of\n * all the original tokens.\n *\n * The output of functions in the pipeline will be passed to the next function\n * in the pipeline. To exclude a token from entering the index the function\n * should return undefined, the rest of the pipeline will not be called with\n * this token.\n *\n * For serialisation of pipelines to work, all functions used in an instance of\n * a pipeline should be registered with lunr.Pipeline. Registered functions can\n * then be loaded. If trying to load a serialised pipeline that uses functions\n * that are not registered an error will be thrown.\n *\n * If not planning on serialising the pipeline then registering pipeline functions\n * is not necessary.\n *\n * @constructor\n */\nlunr.Pipeline = function () {\n this._stack = []\n}\n\nlunr.Pipeline.registeredFunctions = Object.create(null)\n\n/**\n * A pipeline function maps lunr.Token to lunr.Token. A lunr.Token contains the token\n * string as well as all known metadata. A pipeline function can mutate the token string\n * or mutate (or add) metadata for a given token.\n *\n * A pipeline function can indicate that the passed token should be discarded by returning\n * null, undefined or an empty string. This token will not be passed to any downstream pipeline\n * functions and will not be added to the index.\n *\n * Multiple tokens can be returned by returning an array of tokens. Each token will be passed\n * to any downstream pipeline functions and all will returned tokens will be added to the index.\n *\n * Any number of pipeline functions may be chained together using a lunr.Pipeline.\n *\n * @interface lunr.PipelineFunction\n * @param {lunr.Token} token - A token from the document being processed.\n * @param {number} i - The index of this token in the complete list of tokens for this document/field.\n * @param {lunr.Token[]} tokens - All tokens for this document/field.\n * @returns {(?lunr.Token|lunr.Token[])}\n */\n\n/**\n * Register a function with the pipeline.\n *\n * Functions that are used in the pipeline should be registered if the pipeline\n * needs to be serialised, or a serialised pipeline needs to be loaded.\n *\n * Registering a function does not add it to a pipeline, functions must still be\n * added to instances of the pipeline for them to be used when running a pipeline.\n *\n * @param {lunr.PipelineFunction} fn - The function to check for.\n * @param {String} label - The label to register this function with\n */\nlunr.Pipeline.registerFunction = function (fn, label) {\n if (label in this.registeredFunctions) {\n lunr.utils.warn('Overwriting existing registered function: ' + label)\n }\n\n fn.label = label\n lunr.Pipeline.registeredFunctions[fn.label] = fn\n}\n\n/**\n * Warns if the function is not registered as a Pipeline function.\n *\n * @param {lunr.PipelineFunction} fn - The function to check for.\n * @private\n */\nlunr.Pipeline.warnIfFunctionNotRegistered = function (fn) {\n var isRegistered = fn.label && (fn.label in this.registeredFunctions)\n\n if (!isRegistered) {\n lunr.utils.warn('Function is not registered with pipeline. This may cause problems when serialising the index.\\n', fn)\n }\n}\n\n/**\n * Loads a previously serialised pipeline.\n *\n * All functions to be loaded must already be registered with lunr.Pipeline.\n * If any function from the serialised data has not been registered then an\n * error will be thrown.\n *\n * @param {Object} serialised - The serialised pipeline to load.\n * @returns {lunr.Pipeline}\n */\nlunr.Pipeline.load = function (serialised) {\n var pipeline = new lunr.Pipeline\n\n serialised.forEach(function (fnName) {\n var fn = lunr.Pipeline.registeredFunctions[fnName]\n\n if (fn) {\n pipeline.add(fn)\n } else {\n throw new Error('Cannot load unregistered function: ' + fnName)\n }\n })\n\n return pipeline\n}\n\n/**\n * Adds new functions to the end of the pipeline.\n *\n * Logs a warning if the function has not been registered.\n *\n * @param {lunr.PipelineFunction[]} functions - Any number of functions to add to the pipeline.\n */\nlunr.Pipeline.prototype.add = function () {\n var fns = Array.prototype.slice.call(arguments)\n\n fns.forEach(function (fn) {\n lunr.Pipeline.warnIfFunctionNotRegistered(fn)\n this._stack.push(fn)\n }, this)\n}\n\n/**\n * Adds a single function after a function that already exists in the\n * pipeline.\n *\n * Logs a warning if the function has not been registered.\n *\n * @param {lunr.PipelineFunction} existingFn - A function that already exists in the pipeline.\n * @param {lunr.PipelineFunction} newFn - The new function to add to the pipeline.\n */\nlunr.Pipeline.prototype.after = function (existingFn, newFn) {\n lunr.Pipeline.warnIfFunctionNotRegistered(newFn)\n\n var pos = this._stack.indexOf(existingFn)\n if (pos == -1) {\n throw new Error('Cannot find existingFn')\n }\n\n pos = pos + 1\n this._stack.splice(pos, 0, newFn)\n}\n\n/**\n * Adds a single function before a function that already exists in the\n * pipeline.\n *\n * Logs a warning if the function has not been registered.\n *\n * @param {lunr.PipelineFunction} existingFn - A function that already exists in the pipeline.\n * @param {lunr.PipelineFunction} newFn - The new function to add to the pipeline.\n */\nlunr.Pipeline.prototype.before = function (existingFn, newFn) {\n lunr.Pipeline.warnIfFunctionNotRegistered(newFn)\n\n var pos = this._stack.indexOf(existingFn)\n if (pos == -1) {\n throw new Error('Cannot find existingFn')\n }\n\n this._stack.splice(pos, 0, newFn)\n}\n\n/**\n * Removes a function from the pipeline.\n *\n * @param {lunr.PipelineFunction} fn The function to remove from the pipeline.\n */\nlunr.Pipeline.prototype.remove = function (fn) {\n var pos = this._stack.indexOf(fn)\n if (pos == -1) {\n return\n }\n\n this._stack.splice(pos, 1)\n}\n\n/**\n * Runs the current list of functions that make up the pipeline against the\n * passed tokens.\n *\n * @param {Array} tokens The tokens to run through the pipeline.\n * @returns {Array}\n */\nlunr.Pipeline.prototype.run = function (tokens) {\n var stackLength = this._stack.length\n\n for (var i = 0; i < stackLength; i++) {\n var fn = this._stack[i]\n var memo = []\n\n for (var j = 0; j < tokens.length; j++) {\n var result = fn(tokens[j], j, tokens)\n\n if (result === null || result === void 0 || result === '') continue\n\n if (Array.isArray(result)) {\n for (var k = 0; k < result.length; k++) {\n memo.push(result[k])\n }\n } else {\n memo.push(result)\n }\n }\n\n tokens = memo\n }\n\n return tokens\n}\n\n/**\n * Convenience method for passing a string through a pipeline and getting\n * strings out. This method takes care of wrapping the passed string in a\n * token and mapping the resulting tokens back to strings.\n *\n * @param {string} str - The string to pass through the pipeline.\n * @param {?object} metadata - Optional metadata to associate with the token\n * passed to the pipeline.\n * @returns {string[]}\n */\nlunr.Pipeline.prototype.runString = function (str, metadata) {\n var token = new lunr.Token (str, metadata)\n\n return this.run([token]).map(function (t) {\n return t.toString()\n })\n}\n\n/**\n * Resets the pipeline by removing any existing processors.\n *\n */\nlunr.Pipeline.prototype.reset = function () {\n this._stack = []\n}\n\n/**\n * Returns a representation of the pipeline ready for serialisation.\n *\n * Logs a warning if the function has not been registered.\n *\n * @returns {Array}\n */\nlunr.Pipeline.prototype.toJSON = function () {\n return this._stack.map(function (fn) {\n lunr.Pipeline.warnIfFunctionNotRegistered(fn)\n\n return fn.label\n })\n}\n/*!\n * lunr.Vector\n * Copyright (C) 2020 Oliver Nightingale\n */\n\n/**\n * A vector is used to construct the vector space of documents and queries. These\n * vectors support operations to determine the similarity between two documents or\n * a document and a query.\n *\n * Normally no parameters are required for initializing a vector, but in the case of\n * loading a previously dumped vector the raw elements can be provided to the constructor.\n *\n * For performance reasons vectors are implemented with a flat array, where an elements\n * index is immediately followed by its value. E.g. [index, value, index, value]. This\n * allows the underlying array to be as sparse as possible and still offer decent\n * performance when being used for vector calculations.\n *\n * @constructor\n * @param {Number[]} [elements] - The flat list of element index and element value pairs.\n */\nlunr.Vector = function (elements) {\n this._magnitude = 0\n this.elements = elements || []\n}\n\n\n/**\n * Calculates the position within the vector to insert a given index.\n *\n * This is used internally by insert and upsert. If there are duplicate indexes then\n * the position is returned as if the value for that index were to be updated, but it\n * is the callers responsibility to check whether there is a duplicate at that index\n *\n * @param {Number} insertIdx - The index at which the element should be inserted.\n * @returns {Number}\n */\nlunr.Vector.prototype.positionForIndex = function (index) {\n // For an empty vector the tuple can be inserted at the beginning\n if (this.elements.length == 0) {\n return 0\n }\n\n var start = 0,\n end = this.elements.length / 2,\n sliceLength = end - start,\n pivotPoint = Math.floor(sliceLength / 2),\n pivotIndex = this.elements[pivotPoint * 2]\n\n while (sliceLength > 1) {\n if (pivotIndex < index) {\n start = pivotPoint\n }\n\n if (pivotIndex > index) {\n end = pivotPoint\n }\n\n if (pivotIndex == index) {\n break\n }\n\n sliceLength = end - start\n pivotPoint = start + Math.floor(sliceLength / 2)\n pivotIndex = this.elements[pivotPoint * 2]\n }\n\n if (pivotIndex == index) {\n return pivotPoint * 2\n }\n\n if (pivotIndex > index) {\n return pivotPoint * 2\n }\n\n if (pivotIndex < index) {\n return (pivotPoint + 1) * 2\n }\n}\n\n/**\n * Inserts an element at an index within the vector.\n *\n * Does not allow duplicates, will throw an error if there is already an entry\n * for this index.\n *\n * @param {Number} insertIdx - The index at which the element should be inserted.\n * @param {Number} val - The value to be inserted into the vector.\n */\nlunr.Vector.prototype.insert = function (insertIdx, val) {\n this.upsert(insertIdx, val, function () {\n throw \"duplicate index\"\n })\n}\n\n/**\n * Inserts or updates an existing index within the vector.\n *\n * @param {Number} insertIdx - The index at which the element should be inserted.\n * @param {Number} val - The value to be inserted into the vector.\n * @param {function} fn - A function that is called for updates, the existing value and the\n * requested value are passed as arguments\n */\nlunr.Vector.prototype.upsert = function (insertIdx, val, fn) {\n this._magnitude = 0\n var position = this.positionForIndex(insertIdx)\n\n if (this.elements[position] == insertIdx) {\n this.elements[position + 1] = fn(this.elements[position + 1], val)\n } else {\n this.elements.splice(position, 0, insertIdx, val)\n }\n}\n\n/**\n * Calculates the magnitude of this vector.\n *\n * @returns {Number}\n */\nlunr.Vector.prototype.magnitude = function () {\n if (this._magnitude) return this._magnitude\n\n var sumOfSquares = 0,\n elementsLength = this.elements.length\n\n for (var i = 1; i < elementsLength; i += 2) {\n var val = this.elements[i]\n sumOfSquares += val * val\n }\n\n return this._magnitude = Math.sqrt(sumOfSquares)\n}\n\n/**\n * Calculates the dot product of this vector and another vector.\n *\n * @param {lunr.Vector} otherVector - The vector to compute the dot product with.\n * @returns {Number}\n */\nlunr.Vector.prototype.dot = function (otherVector) {\n var dotProduct = 0,\n a = this.elements, b = otherVector.elements,\n aLen = a.length, bLen = b.length,\n aVal = 0, bVal = 0,\n i = 0, j = 0\n\n while (i < aLen && j < bLen) {\n aVal = a[i], bVal = b[j]\n if (aVal < bVal) {\n i += 2\n } else if (aVal > bVal) {\n j += 2\n } else if (aVal == bVal) {\n dotProduct += a[i + 1] * b[j + 1]\n i += 2\n j += 2\n }\n }\n\n return dotProduct\n}\n\n/**\n * Calculates the similarity between this vector and another vector.\n *\n * @param {lunr.Vector} otherVector - The other vector to calculate the\n * similarity with.\n * @returns {Number}\n */\nlunr.Vector.prototype.similarity = function (otherVector) {\n return this.dot(otherVector) / this.magnitude() || 0\n}\n\n/**\n * Converts the vector to an array of the elements within the vector.\n *\n * @returns {Number[]}\n */\nlunr.Vector.prototype.toArray = function () {\n var output = new Array (this.elements.length / 2)\n\n for (var i = 1, j = 0; i < this.elements.length; i += 2, j++) {\n output[j] = this.elements[i]\n }\n\n return output\n}\n\n/**\n * A JSON serializable representation of the vector.\n *\n * @returns {Number[]}\n */\nlunr.Vector.prototype.toJSON = function () {\n return this.elements\n}\n/* eslint-disable */\n/*!\n * lunr.stemmer\n * Copyright (C) 2020 Oliver Nightingale\n * Includes code from - http://tartarus.org/~martin/PorterStemmer/js.txt\n */\n\n/**\n * lunr.stemmer is an english language stemmer, this is a JavaScript\n * implementation of the PorterStemmer taken from http://tartarus.org/~martin\n *\n * @static\n * @implements {lunr.PipelineFunction}\n * @param {lunr.Token} token - The string to stem\n * @returns {lunr.Token}\n * @see {@link lunr.Pipeline}\n * @function\n */\nlunr.stemmer = (function(){\n var step2list = {\n \"ational\" : \"ate\",\n \"tional\" : \"tion\",\n \"enci\" : \"ence\",\n \"anci\" : \"ance\",\n \"izer\" : \"ize\",\n \"bli\" : \"ble\",\n \"alli\" : \"al\",\n \"entli\" : \"ent\",\n \"eli\" : \"e\",\n \"ousli\" : \"ous\",\n \"ization\" : \"ize\",\n \"ation\" : \"ate\",\n \"ator\" : \"ate\",\n \"alism\" : \"al\",\n \"iveness\" : \"ive\",\n \"fulness\" : \"ful\",\n \"ousness\" : \"ous\",\n \"aliti\" : \"al\",\n \"iviti\" : \"ive\",\n \"biliti\" : \"ble\",\n \"logi\" : \"log\"\n },\n\n step3list = {\n \"icate\" : \"ic\",\n \"ative\" : \"\",\n \"alize\" : \"al\",\n \"iciti\" : \"ic\",\n \"ical\" : \"ic\",\n \"ful\" : \"\",\n \"ness\" : \"\"\n },\n\n c = \"[^aeiou]\", // consonant\n v = \"[aeiouy]\", // vowel\n C = c + \"[^aeiouy]*\", // consonant sequence\n V = v + \"[aeiou]*\", // vowel sequence\n\n mgr0 = \"^(\" + C + \")?\" + V + C, // [C]VC... is m>0\n meq1 = \"^(\" + C + \")?\" + V + C + \"(\" + V + \")?$\", // [C]VC[V] is m=1\n mgr1 = \"^(\" + C + \")?\" + V + C + V + C, // [C]VCVC... is m>1\n s_v = \"^(\" + C + \")?\" + v; // vowel in stem\n\n var re_mgr0 = new RegExp(mgr0);\n var re_mgr1 = new RegExp(mgr1);\n var re_meq1 = new RegExp(meq1);\n var re_s_v = new RegExp(s_v);\n\n var re_1a = /^(.+?)(ss|i)es$/;\n var re2_1a = /^(.+?)([^s])s$/;\n var re_1b = /^(.+?)eed$/;\n var re2_1b = /^(.+?)(ed|ing)$/;\n var re_1b_2 = /.$/;\n var re2_1b_2 = /(at|bl|iz)$/;\n var re3_1b_2 = new RegExp(\"([^aeiouylsz])\\\\1$\");\n var re4_1b_2 = new RegExp(\"^\" + C + v + \"[^aeiouwxy]$\");\n\n var re_1c = /^(.+?[^aeiou])y$/;\n var re_2 = /^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/;\n\n var re_3 = /^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/;\n\n var re_4 = /^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/;\n var re2_4 = /^(.+?)(s|t)(ion)$/;\n\n var re_5 = /^(.+?)e$/;\n var re_5_1 = /ll$/;\n var re3_5 = new RegExp(\"^\" + C + v + \"[^aeiouwxy]$\");\n\n var porterStemmer = function porterStemmer(w) {\n var stem,\n suffix,\n firstch,\n re,\n re2,\n re3,\n re4;\n\n if (w.length < 3) { return w; }\n\n firstch = w.substr(0,1);\n if (firstch == \"y\") {\n w = firstch.toUpperCase() + w.substr(1);\n }\n\n // Step 1a\n re = re_1a\n re2 = re2_1a;\n\n if (re.test(w)) { w = w.replace(re,\"$1$2\"); }\n else if (re2.test(w)) { w = w.replace(re2,\"$1$2\"); }\n\n // Step 1b\n re = re_1b;\n re2 = re2_1b;\n if (re.test(w)) {\n var fp = re.exec(w);\n re = re_mgr0;\n if (re.test(fp[1])) {\n re = re_1b_2;\n w = w.replace(re,\"\");\n }\n } else if (re2.test(w)) {\n var fp = re2.exec(w);\n stem = fp[1];\n re2 = re_s_v;\n if (re2.test(stem)) {\n w = stem;\n re2 = re2_1b_2;\n re3 = re3_1b_2;\n re4 = re4_1b_2;\n if (re2.test(w)) { w = w + \"e\"; }\n else if (re3.test(w)) { re = re_1b_2; w = w.replace(re,\"\"); }\n else if (re4.test(w)) { w = w + \"e\"; }\n }\n }\n\n // Step 1c - replace suffix y or Y by i if preceded by a non-vowel which is not the first letter of the word (so cry -> cri, by -> by, say -> say)\n re = re_1c;\n if (re.test(w)) {\n var fp = re.exec(w);\n stem = fp[1];\n w = stem + \"i\";\n }\n\n // Step 2\n re = re_2;\n if (re.test(w)) {\n var fp = re.exec(w);\n stem = fp[1];\n suffix = fp[2];\n re = re_mgr0;\n if (re.test(stem)) {\n w = stem + step2list[suffix];\n }\n }\n\n // Step 3\n re = re_3;\n if (re.test(w)) {\n var fp = re.exec(w);\n stem = fp[1];\n suffix = fp[2];\n re = re_mgr0;\n if (re.test(stem)) {\n w = stem + step3list[suffix];\n }\n }\n\n // Step 4\n re = re_4;\n re2 = re2_4;\n if (re.test(w)) {\n var fp = re.exec(w);\n stem = fp[1];\n re = re_mgr1;\n if (re.test(stem)) {\n w = stem;\n }\n } else if (re2.test(w)) {\n var fp = re2.exec(w);\n stem = fp[1] + fp[2];\n re2 = re_mgr1;\n if (re2.test(stem)) {\n w = stem;\n }\n }\n\n // Step 5\n re = re_5;\n if (re.test(w)) {\n var fp = re.exec(w);\n stem = fp[1];\n re = re_mgr1;\n re2 = re_meq1;\n re3 = re3_5;\n if (re.test(stem) || (re2.test(stem) && !(re3.test(stem)))) {\n w = stem;\n }\n }\n\n re = re_5_1;\n re2 = re_mgr1;\n if (re.test(w) && re2.test(w)) {\n re = re_1b_2;\n w = w.replace(re,\"\");\n }\n\n // and turn initial Y back to y\n\n if (firstch == \"y\") {\n w = firstch.toLowerCase() + w.substr(1);\n }\n\n return w;\n };\n\n return function (token) {\n return token.update(porterStemmer);\n }\n})();\n\nlunr.Pipeline.registerFunction(lunr.stemmer, 'stemmer')\n/*!\n * lunr.stopWordFilter\n * Copyright (C) 2020 Oliver Nightingale\n */\n\n/**\n * lunr.generateStopWordFilter builds a stopWordFilter function from the provided\n * list of stop words.\n *\n * The built in lunr.stopWordFilter is built using this generator and can be used\n * to generate custom stopWordFilters for applications or non English languages.\n *\n * @function\n * @param {Array} token The token to pass through the filter\n * @returns {lunr.PipelineFunction}\n * @see lunr.Pipeline\n * @see lunr.stopWordFilter\n */\nlunr.generateStopWordFilter = function (stopWords) {\n var words = stopWords.reduce(function (memo, stopWord) {\n memo[stopWord] = stopWord\n return memo\n }, {})\n\n return function (token) {\n if (token && words[token.toString()] !== token.toString()) return token\n }\n}\n\n/**\n * lunr.stopWordFilter is an English language stop word list filter, any words\n * contained in the list will not be passed through the filter.\n *\n * This is intended to be used in the Pipeline. If the token does not pass the\n * filter then undefined will be returned.\n *\n * @function\n * @implements {lunr.PipelineFunction}\n * @params {lunr.Token} token - A token to check for being a stop word.\n * @returns {lunr.Token}\n * @see {@link lunr.Pipeline}\n */\nlunr.stopWordFilter = lunr.generateStopWordFilter([\n 'a',\n 'able',\n 'about',\n 'across',\n 'after',\n 'all',\n 'almost',\n 'also',\n 'am',\n 'among',\n 'an',\n 'and',\n 'any',\n 'are',\n 'as',\n 'at',\n 'be',\n 'because',\n 'been',\n 'but',\n 'by',\n 'can',\n 'cannot',\n 'could',\n 'dear',\n 'did',\n 'do',\n 'does',\n 'either',\n 'else',\n 'ever',\n 'every',\n 'for',\n 'from',\n 'get',\n 'got',\n 'had',\n 'has',\n 'have',\n 'he',\n 'her',\n 'hers',\n 'him',\n 'his',\n 'how',\n 'however',\n 'i',\n 'if',\n 'in',\n 'into',\n 'is',\n 'it',\n 'its',\n 'just',\n 'least',\n 'let',\n 'like',\n 'likely',\n 'may',\n 'me',\n 'might',\n 'most',\n 'must',\n 'my',\n 'neither',\n 'no',\n 'nor',\n 'not',\n 'of',\n 'off',\n 'often',\n 'on',\n 'only',\n 'or',\n 'other',\n 'our',\n 'own',\n 'rather',\n 'said',\n 'say',\n 'says',\n 'she',\n 'should',\n 'since',\n 'so',\n 'some',\n 'than',\n 'that',\n 'the',\n 'their',\n 'them',\n 'then',\n 'there',\n 'these',\n 'they',\n 'this',\n 'tis',\n 'to',\n 'too',\n 'twas',\n 'us',\n 'wants',\n 'was',\n 'we',\n 'were',\n 'what',\n 'when',\n 'where',\n 'which',\n 'while',\n 'who',\n 'whom',\n 'why',\n 'will',\n 'with',\n 'would',\n 'yet',\n 'you',\n 'your'\n])\n\nlunr.Pipeline.registerFunction(lunr.stopWordFilter, 'stopWordFilter')\n/*!\n * lunr.trimmer\n * Copyright (C) 2020 Oliver Nightingale\n */\n\n/**\n * lunr.trimmer is a pipeline function for trimming non word\n * characters from the beginning and end of tokens before they\n * enter the index.\n *\n * This implementation may not work correctly for non latin\n * characters and should either be removed or adapted for use\n * with languages with non-latin characters.\n *\n * @static\n * @implements {lunr.PipelineFunction}\n * @param {lunr.Token} token The token to pass through the filter\n * @returns {lunr.Token}\n * @see lunr.Pipeline\n */\nlunr.trimmer = function (token) {\n return token.update(function (s) {\n return s.replace(/^\\W+/, '').replace(/\\W+$/, '')\n })\n}\n\nlunr.Pipeline.registerFunction(lunr.trimmer, 'trimmer')\n/*!\n * lunr.TokenSet\n * Copyright (C) 2020 Oliver Nightingale\n */\n\n/**\n * A token set is used to store the unique list of all tokens\n * within an index. Token sets are also used to represent an\n * incoming query to the index, this query token set and index\n * token set are then intersected to find which tokens to look\n * up in the inverted index.\n *\n * A token set can hold multiple tokens, as in the case of the\n * index token set, or it can hold a single token as in the\n * case of a simple query token set.\n *\n * Additionally token sets are used to perform wildcard matching.\n * Leading, contained and trailing wildcards are supported, and\n * from this edit distance matching can also be provided.\n *\n * Token sets are implemented as a minimal finite state automata,\n * where both common prefixes and suffixes are shared between tokens.\n * This helps to reduce the space used for storing the token set.\n *\n * @constructor\n */\nlunr.TokenSet = function () {\n this.final = false\n this.edges = {}\n this.id = lunr.TokenSet._nextId\n lunr.TokenSet._nextId += 1\n}\n\n/**\n * Keeps track of the next, auto increment, identifier to assign\n * to a new tokenSet.\n *\n * TokenSets require a unique identifier to be correctly minimised.\n *\n * @private\n */\nlunr.TokenSet._nextId = 1\n\n/**\n * Creates a TokenSet instance from the given sorted array of words.\n *\n * @param {String[]} arr - A sorted array of strings to create the set from.\n * @returns {lunr.TokenSet}\n * @throws Will throw an error if the input array is not sorted.\n */\nlunr.TokenSet.fromArray = function (arr) {\n var builder = new lunr.TokenSet.Builder\n\n for (var i = 0, len = arr.length; i < len; i++) {\n builder.insert(arr[i])\n }\n\n builder.finish()\n return builder.root\n}\n\n/**\n * Creates a token set from a query clause.\n *\n * @private\n * @param {Object} clause - A single clause from lunr.Query.\n * @param {string} clause.term - The query clause term.\n * @param {number} [clause.editDistance] - The optional edit distance for the term.\n * @returns {lunr.TokenSet}\n */\nlunr.TokenSet.fromClause = function (clause) {\n if ('editDistance' in clause) {\n return lunr.TokenSet.fromFuzzyString(clause.term, clause.editDistance)\n } else {\n return lunr.TokenSet.fromString(clause.term)\n }\n}\n\n/**\n * Creates a token set representing a single string with a specified\n * edit distance.\n *\n * Insertions, deletions, substitutions and transpositions are each\n * treated as an edit distance of 1.\n *\n * Increasing the allowed edit distance will have a dramatic impact\n * on the performance of both creating and intersecting these TokenSets.\n * It is advised to keep the edit distance less than 3.\n *\n * @param {string} str - The string to create the token set from.\n * @param {number} editDistance - The allowed edit distance to match.\n * @returns {lunr.Vector}\n */\nlunr.TokenSet.fromFuzzyString = function (str, editDistance) {\n var root = new lunr.TokenSet\n\n var stack = [{\n node: root,\n editsRemaining: editDistance,\n str: str\n }]\n\n while (stack.length) {\n var frame = stack.pop()\n\n // no edit\n if (frame.str.length > 0) {\n var char = frame.str.charAt(0),\n noEditNode\n\n if (char in frame.node.edges) {\n noEditNode = frame.node.edges[char]\n } else {\n noEditNode = new lunr.TokenSet\n frame.node.edges[char] = noEditNode\n }\n\n if (frame.str.length == 1) {\n noEditNode.final = true\n }\n\n stack.push({\n node: noEditNode,\n editsRemaining: frame.editsRemaining,\n str: frame.str.slice(1)\n })\n }\n\n if (frame.editsRemaining == 0) {\n continue\n }\n\n // insertion\n if (\"*\" in frame.node.edges) {\n var insertionNode = frame.node.edges[\"*\"]\n } else {\n var insertionNode = new lunr.TokenSet\n frame.node.edges[\"*\"] = insertionNode\n }\n\n if (frame.str.length == 0) {\n insertionNode.final = true\n }\n\n stack.push({\n node: insertionNode,\n editsRemaining: frame.editsRemaining - 1,\n str: frame.str\n })\n\n // deletion\n // can only do a deletion if we have enough edits remaining\n // and if there are characters left to delete in the string\n if (frame.str.length > 1) {\n stack.push({\n node: frame.node,\n editsRemaining: frame.editsRemaining - 1,\n str: frame.str.slice(1)\n })\n }\n\n // deletion\n // just removing the last character from the str\n if (frame.str.length == 1) {\n frame.node.final = true\n }\n\n // substitution\n // can only do a substitution if we have enough edits remaining\n // and if there are characters left to substitute\n if (frame.str.length >= 1) {\n if (\"*\" in frame.node.edges) {\n var substitutionNode = frame.node.edges[\"*\"]\n } else {\n var substitutionNode = new lunr.TokenSet\n frame.node.edges[\"*\"] = substitutionNode\n }\n\n if (frame.str.length == 1) {\n substitutionNode.final = true\n }\n\n stack.push({\n node: substitutionNode,\n editsRemaining: frame.editsRemaining - 1,\n str: frame.str.slice(1)\n })\n }\n\n // transposition\n // can only do a transposition if there are edits remaining\n // and there are enough characters to transpose\n if (frame.str.length > 1) {\n var charA = frame.str.charAt(0),\n charB = frame.str.charAt(1),\n transposeNode\n\n if (charB in frame.node.edges) {\n transposeNode = frame.node.edges[charB]\n } else {\n transposeNode = new lunr.TokenSet\n frame.node.edges[charB] = transposeNode\n }\n\n if (frame.str.length == 1) {\n transposeNode.final = true\n }\n\n stack.push({\n node: transposeNode,\n editsRemaining: frame.editsRemaining - 1,\n str: charA + frame.str.slice(2)\n })\n }\n }\n\n return root\n}\n\n/**\n * Creates a TokenSet from a string.\n *\n * The string may contain one or more wildcard characters (*)\n * that will allow wildcard matching when intersecting with\n * another TokenSet.\n *\n * @param {string} str - The string to create a TokenSet from.\n * @returns {lunr.TokenSet}\n */\nlunr.TokenSet.fromString = function (str) {\n var node = new lunr.TokenSet,\n root = node\n\n /*\n * Iterates through all characters within the passed string\n * appending a node for each character.\n *\n * When a wildcard character is found then a self\n * referencing edge is introduced to continually match\n * any number of any characters.\n */\n for (var i = 0, len = str.length; i < len; i++) {\n var char = str[i],\n final = (i == len - 1)\n\n if (char == \"*\") {\n node.edges[char] = node\n node.final = final\n\n } else {\n var next = new lunr.TokenSet\n next.final = final\n\n node.edges[char] = next\n node = next\n }\n }\n\n return root\n}\n\n/**\n * Converts this TokenSet into an array of strings\n * contained within the TokenSet.\n *\n * This is not intended to be used on a TokenSet that\n * contains wildcards, in these cases the results are\n * undefined and are likely to cause an infinite loop.\n *\n * @returns {string[]}\n */\nlunr.TokenSet.prototype.toArray = function () {\n var words = []\n\n var stack = [{\n prefix: \"\",\n node: this\n }]\n\n while (stack.length) {\n var frame = stack.pop(),\n edges = Object.keys(frame.node.edges),\n len = edges.length\n\n if (frame.node.final) {\n /* In Safari, at this point the prefix is sometimes corrupted, see:\n * https://github.com/olivernn/lunr.js/issues/279 Calling any\n * String.prototype method forces Safari to \"cast\" this string to what\n * it's supposed to be, fixing the bug. */\n frame.prefix.charAt(0)\n words.push(frame.prefix)\n }\n\n for (var i = 0; i < len; i++) {\n var edge = edges[i]\n\n stack.push({\n prefix: frame.prefix.concat(edge),\n node: frame.node.edges[edge]\n })\n }\n }\n\n return words\n}\n\n/**\n * Generates a string representation of a TokenSet.\n *\n * This is intended to allow TokenSets to be used as keys\n * in objects, largely to aid the construction and minimisation\n * of a TokenSet. As such it is not designed to be a human\n * friendly representation of the TokenSet.\n *\n * @returns {string}\n */\nlunr.TokenSet.prototype.toString = function () {\n // NOTE: Using Object.keys here as this.edges is very likely\n // to enter 'hash-mode' with many keys being added\n //\n // avoiding a for-in loop here as it leads to the function\n // being de-optimised (at least in V8). From some simple\n // benchmarks the performance is comparable, but allowing\n // V8 to optimize may mean easy performance wins in the future.\n\n if (this._str) {\n return this._str\n }\n\n var str = this.final ? '1' : '0',\n labels = Object.keys(this.edges).sort(),\n len = labels.length\n\n for (var i = 0; i < len; i++) {\n var label = labels[i],\n node = this.edges[label]\n\n str = str + label + node.id\n }\n\n return str\n}\n\n/**\n * Returns a new TokenSet that is the intersection of\n * this TokenSet and the passed TokenSet.\n *\n * This intersection will take into account any wildcards\n * contained within the TokenSet.\n *\n * @param {lunr.TokenSet} b - An other TokenSet to intersect with.\n * @returns {lunr.TokenSet}\n */\nlunr.TokenSet.prototype.intersect = function (b) {\n var output = new lunr.TokenSet,\n frame = undefined\n\n var stack = [{\n qNode: b,\n output: output,\n node: this\n }]\n\n while (stack.length) {\n frame = stack.pop()\n\n // NOTE: As with the #toString method, we are using\n // Object.keys and a for loop instead of a for-in loop\n // as both of these objects enter 'hash' mode, causing\n // the function to be de-optimised in V8\n var qEdges = Object.keys(frame.qNode.edges),\n qLen = qEdges.length,\n nEdges = Object.keys(frame.node.edges),\n nLen = nEdges.length\n\n for (var q = 0; q < qLen; q++) {\n var qEdge = qEdges[q]\n\n for (var n = 0; n < nLen; n++) {\n var nEdge = nEdges[n]\n\n if (nEdge == qEdge || qEdge == '*') {\n var node = frame.node.edges[nEdge],\n qNode = frame.qNode.edges[qEdge],\n final = node.final && qNode.final,\n next = undefined\n\n if (nEdge in frame.output.edges) {\n // an edge already exists for this character\n // no need to create a new node, just set the finality\n // bit unless this node is already final\n next = frame.output.edges[nEdge]\n next.final = next.final || final\n\n } else {\n // no edge exists yet, must create one\n // set the finality bit and insert it\n // into the output\n next = new lunr.TokenSet\n next.final = final\n frame.output.edges[nEdge] = next\n }\n\n stack.push({\n qNode: qNode,\n output: next,\n node: node\n })\n }\n }\n }\n }\n\n return output\n}\nlunr.TokenSet.Builder = function () {\n this.previousWord = \"\"\n this.root = new lunr.TokenSet\n this.uncheckedNodes = []\n this.minimizedNodes = {}\n}\n\nlunr.TokenSet.Builder.prototype.insert = function (word) {\n var node,\n commonPrefix = 0\n\n if (word < this.previousWord) {\n throw new Error (\"Out of order word insertion\")\n }\n\n for (var i = 0; i < word.length && i < this.previousWord.length; i++) {\n if (word[i] != this.previousWord[i]) break\n commonPrefix++\n }\n\n this.minimize(commonPrefix)\n\n if (this.uncheckedNodes.length == 0) {\n node = this.root\n } else {\n node = this.uncheckedNodes[this.uncheckedNodes.length - 1].child\n }\n\n for (var i = commonPrefix; i < word.length; i++) {\n var nextNode = new lunr.TokenSet,\n char = word[i]\n\n node.edges[char] = nextNode\n\n this.uncheckedNodes.push({\n parent: node,\n char: char,\n child: nextNode\n })\n\n node = nextNode\n }\n\n node.final = true\n this.previousWord = word\n}\n\nlunr.TokenSet.Builder.prototype.finish = function () {\n this.minimize(0)\n}\n\nlunr.TokenSet.Builder.prototype.minimize = function (downTo) {\n for (var i = this.uncheckedNodes.length - 1; i >= downTo; i--) {\n var node = this.uncheckedNodes[i],\n childKey = node.child.toString()\n\n if (childKey in this.minimizedNodes) {\n node.parent.edges[node.char] = this.minimizedNodes[childKey]\n } else {\n // Cache the key for this node since\n // we know it can't change anymore\n node.child._str = childKey\n\n this.minimizedNodes[childKey] = node.child\n }\n\n this.uncheckedNodes.pop()\n }\n}\n/*!\n * lunr.Index\n * Copyright (C) 2020 Oliver Nightingale\n */\n\n/**\n * An index contains the built index of all documents and provides a query interface\n * to the index.\n *\n * Usually instances of lunr.Index will not be created using this constructor, instead\n * lunr.Builder should be used to construct new indexes, or lunr.Index.load should be\n * used to load previously built and serialized indexes.\n *\n * @constructor\n * @param {Object} attrs - The attributes of the built search index.\n * @param {Object} attrs.invertedIndex - An index of term/field to document reference.\n * @param {Object} attrs.fieldVectors - Field vectors\n * @param {lunr.TokenSet} attrs.tokenSet - An set of all corpus tokens.\n * @param {string[]} attrs.fields - The names of indexed document fields.\n * @param {lunr.Pipeline} attrs.pipeline - The pipeline to use for search terms.\n */\nlunr.Index = function (attrs) {\n this.invertedIndex = attrs.invertedIndex\n this.fieldVectors = attrs.fieldVectors\n this.tokenSet = attrs.tokenSet\n this.fields = attrs.fields\n this.pipeline = attrs.pipeline\n}\n\n/**\n * A result contains details of a document matching a search query.\n * @typedef {Object} lunr.Index~Result\n * @property {string} ref - The reference of the document this result represents.\n * @property {number} score - A number between 0 and 1 representing how similar this document is to the query.\n * @property {lunr.MatchData} matchData - Contains metadata about this match including which term(s) caused the match.\n */\n\n/**\n * Although lunr provides the ability to create queries using lunr.Query, it also provides a simple\n * query language which itself is parsed into an instance of lunr.Query.\n *\n * For programmatically building queries it is advised to directly use lunr.Query, the query language\n * is best used for human entered text rather than program generated text.\n *\n * At its simplest queries can just be a single term, e.g. `hello`, multiple terms are also supported\n * and will be combined with OR, e.g `hello world` will match documents that contain either 'hello'\n * or 'world', though those that contain both will rank higher in the results.\n *\n * Wildcards can be included in terms to match one or more unspecified characters, these wildcards can\n * be inserted anywhere within the term, and more than one wildcard can exist in a single term. Adding\n * wildcards will increase the number of documents that will be found but can also have a negative\n * impact on query performance, especially with wildcards at the beginning of a term.\n *\n * Terms can be restricted to specific fields, e.g. `title:hello`, only documents with the term\n * hello in the title field will match this query. Using a field not present in the index will lead\n * to an error being thrown.\n *\n * Modifiers can also be added to terms, lunr supports edit distance and boost modifiers on terms. A term\n * boost will make documents matching that term score higher, e.g. `foo^5`. Edit distance is also supported\n * to provide fuzzy matching, e.g. 'hello~2' will match documents with hello with an edit distance of 2.\n * Avoid large values for edit distance to improve query performance.\n *\n * Each term also supports a presence modifier. By default a term's presence in document is optional, however\n * this can be changed to either required or prohibited. For a term's presence to be required in a document the\n * term should be prefixed with a '+', e.g. `+foo bar` is a search for documents that must contain 'foo' and\n * optionally contain 'bar'. Conversely a leading '-' sets the terms presence to prohibited, i.e. it must not\n * appear in a document, e.g. `-foo bar` is a search for documents that do not contain 'foo' but may contain 'bar'.\n *\n * To escape special characters the backslash character '\\' can be used, this allows searches to include\n * characters that would normally be considered modifiers, e.g. `foo\\~2` will search for a term \"foo~2\" instead\n * of attempting to apply a boost of 2 to the search term \"foo\".\n *\n * @typedef {string} lunr.Index~QueryString\n * @example
Simple single term query
\n * hello\n * @example
Multiple term query
\n * hello world\n * @example
term scoped to a field
\n * title:hello\n * @example
term with a boost of 10
\n * hello^10\n * @example
term with an edit distance of 2
\n * hello~2\n * @example
terms with presence modifiers
\n * -foo +bar baz\n */\n\n/**\n * Performs a search against the index using lunr query syntax.\n *\n * Results will be returned sorted by their score, the most relevant results\n * will be returned first. For details on how the score is calculated, please see\n * the {@link https://lunrjs.com/guides/searching.html#scoring|guide}.\n *\n * For more programmatic querying use lunr.Index#query.\n *\n * @param {lunr.Index~QueryString} queryString - A string containing a lunr query.\n * @throws {lunr.QueryParseError} If the passed query string cannot be parsed.\n * @returns {lunr.Index~Result[]}\n */\nlunr.Index.prototype.search = function (queryString) {\n return this.query(function (query) {\n var parser = new lunr.QueryParser(queryString, query)\n parser.parse()\n })\n}\n\n/**\n * A query builder callback provides a query object to be used to express\n * the query to perform on the index.\n *\n * @callback lunr.Index~queryBuilder\n * @param {lunr.Query} query - The query object to build up.\n * @this lunr.Query\n */\n\n/**\n * Performs a query against the index using the yielded lunr.Query object.\n *\n * If performing programmatic queries against the index, this method is preferred\n * over lunr.Index#search so as to avoid the additional query parsing overhead.\n *\n * A query object is yielded to the supplied function which should be used to\n * express the query to be run against the index.\n *\n * Note that although this function takes a callback parameter it is _not_ an\n * asynchronous operation, the callback is just yielded a query object to be\n * customized.\n *\n * @param {lunr.Index~queryBuilder} fn - A function that is used to build the query.\n * @returns {lunr.Index~Result[]}\n */\nlunr.Index.prototype.query = function (fn) {\n // for each query clause\n // * process terms\n // * expand terms from token set\n // * find matching documents and metadata\n // * get document vectors\n // * score documents\n\n var query = new lunr.Query(this.fields),\n matchingFields = Object.create(null),\n queryVectors = Object.create(null),\n termFieldCache = Object.create(null),\n requiredMatches = Object.create(null),\n prohibitedMatches = Object.create(null)\n\n /*\n * To support field level boosts a query vector is created per\n * field. An empty vector is eagerly created to support negated\n * queries.\n */\n for (var i = 0; i < this.fields.length; i++) {\n queryVectors[this.fields[i]] = new lunr.Vector\n }\n\n fn.call(query, query)\n\n for (var i = 0; i < query.clauses.length; i++) {\n /*\n * Unless the pipeline has been disabled for this term, which is\n * the case for terms with wildcards, we need to pass the clause\n * term through the search pipeline. A pipeline returns an array\n * of processed terms. Pipeline functions may expand the passed\n * term, which means we may end up performing multiple index lookups\n * for a single query term.\n */\n var clause = query.clauses[i],\n terms = null,\n clauseMatches = lunr.Set.empty\n\n if (clause.usePipeline) {\n terms = this.pipeline.runString(clause.term, {\n fields: clause.fields\n })\n } else {\n terms = [clause.term]\n }\n\n for (var m = 0; m < terms.length; m++) {\n var term = terms[m]\n\n /*\n * Each term returned from the pipeline needs to use the same query\n * clause object, e.g. the same boost and or edit distance. The\n * simplest way to do this is to re-use the clause object but mutate\n * its term property.\n */\n clause.term = term\n\n /*\n * From the term in the clause we create a token set which will then\n * be used to intersect the indexes token set to get a list of terms\n * to lookup in the inverted index\n */\n var termTokenSet = lunr.TokenSet.fromClause(clause),\n expandedTerms = this.tokenSet.intersect(termTokenSet).toArray()\n\n /*\n * If a term marked as required does not exist in the tokenSet it is\n * impossible for the search to return any matches. We set all the field\n * scoped required matches set to empty and stop examining any further\n * clauses.\n */\n if (expandedTerms.length === 0 && clause.presence === lunr.Query.presence.REQUIRED) {\n for (var k = 0; k < clause.fields.length; k++) {\n var field = clause.fields[k]\n requiredMatches[field] = lunr.Set.empty\n }\n\n break\n }\n\n for (var j = 0; j < expandedTerms.length; j++) {\n /*\n * For each term get the posting and termIndex, this is required for\n * building the query vector.\n */\n var expandedTerm = expandedTerms[j],\n posting = this.invertedIndex[expandedTerm],\n termIndex = posting._index\n\n for (var k = 0; k < clause.fields.length; k++) {\n /*\n * For each field that this query term is scoped by (by default\n * all fields are in scope) we need to get all the document refs\n * that have this term in that field.\n *\n * The posting is the entry in the invertedIndex for the matching\n * term from above.\n */\n var field = clause.fields[k],\n fieldPosting = posting[field],\n matchingDocumentRefs = Object.keys(fieldPosting),\n termField = expandedTerm + \"/\" + field,\n matchingDocumentsSet = new lunr.Set(matchingDocumentRefs)\n\n /*\n * if the presence of this term is required ensure that the matching\n * documents are added to the set of required matches for this clause.\n *\n */\n if (clause.presence == lunr.Query.presence.REQUIRED) {\n clauseMatches = clauseMatches.union(matchingDocumentsSet)\n\n if (requiredMatches[field] === undefined) {\n requiredMatches[field] = lunr.Set.complete\n }\n }\n\n /*\n * if the presence of this term is prohibited ensure that the matching\n * documents are added to the set of prohibited matches for this field,\n * creating that set if it does not yet exist.\n */\n if (clause.presence == lunr.Query.presence.PROHIBITED) {\n if (prohibitedMatches[field] === undefined) {\n prohibitedMatches[field] = lunr.Set.empty\n }\n\n prohibitedMatches[field] = prohibitedMatches[field].union(matchingDocumentsSet)\n\n /*\n * Prohibited matches should not be part of the query vector used for\n * similarity scoring and no metadata should be extracted so we continue\n * to the next field\n */\n continue\n }\n\n /*\n * The query field vector is populated using the termIndex found for\n * the term and a unit value with the appropriate boost applied.\n * Using upsert because there could already be an entry in the vector\n * for the term we are working with. In that case we just add the scores\n * together.\n */\n queryVectors[field].upsert(termIndex, clause.boost, function (a, b) { return a + b })\n\n /**\n * If we've already seen this term, field combo then we've already collected\n * the matching documents and metadata, no need to go through all that again\n */\n if (termFieldCache[termField]) {\n continue\n }\n\n for (var l = 0; l < matchingDocumentRefs.length; l++) {\n /*\n * All metadata for this term/field/document triple\n * are then extracted and collected into an instance\n * of lunr.MatchData ready to be returned in the query\n * results\n */\n var matchingDocumentRef = matchingDocumentRefs[l],\n matchingFieldRef = new lunr.FieldRef (matchingDocumentRef, field),\n metadata = fieldPosting[matchingDocumentRef],\n fieldMatch\n\n if ((fieldMatch = matchingFields[matchingFieldRef]) === undefined) {\n matchingFields[matchingFieldRef] = new lunr.MatchData (expandedTerm, field, metadata)\n } else {\n fieldMatch.add(expandedTerm, field, metadata)\n }\n\n }\n\n termFieldCache[termField] = true\n }\n }\n }\n\n /**\n * If the presence was required we need to update the requiredMatches field sets.\n * We do this after all fields for the term have collected their matches because\n * the clause terms presence is required in _any_ of the fields not _all_ of the\n * fields.\n */\n if (clause.presence === lunr.Query.presence.REQUIRED) {\n for (var k = 0; k < clause.fields.length; k++) {\n var field = clause.fields[k]\n requiredMatches[field] = requiredMatches[field].intersect(clauseMatches)\n }\n }\n }\n\n /**\n * Need to combine the field scoped required and prohibited\n * matching documents into a global set of required and prohibited\n * matches\n */\n var allRequiredMatches = lunr.Set.complete,\n allProhibitedMatches = lunr.Set.empty\n\n for (var i = 0; i < this.fields.length; i++) {\n var field = this.fields[i]\n\n if (requiredMatches[field]) {\n allRequiredMatches = allRequiredMatches.intersect(requiredMatches[field])\n }\n\n if (prohibitedMatches[field]) {\n allProhibitedMatches = allProhibitedMatches.union(prohibitedMatches[field])\n }\n }\n\n var matchingFieldRefs = Object.keys(matchingFields),\n results = [],\n matches = Object.create(null)\n\n /*\n * If the query is negated (contains only prohibited terms)\n * we need to get _all_ fieldRefs currently existing in the\n * index. This is only done when we know that the query is\n * entirely prohibited terms to avoid any cost of getting all\n * fieldRefs unnecessarily.\n *\n * Additionally, blank MatchData must be created to correctly\n * populate the results.\n */\n if (query.isNegated()) {\n matchingFieldRefs = Object.keys(this.fieldVectors)\n\n for (var i = 0; i < matchingFieldRefs.length; i++) {\n var matchingFieldRef = matchingFieldRefs[i]\n var fieldRef = lunr.FieldRef.fromString(matchingFieldRef)\n matchingFields[matchingFieldRef] = new lunr.MatchData\n }\n }\n\n for (var i = 0; i < matchingFieldRefs.length; i++) {\n /*\n * Currently we have document fields that match the query, but we\n * need to return documents. The matchData and scores are combined\n * from multiple fields belonging to the same document.\n *\n * Scores are calculated by field, using the query vectors created\n * above, and combined into a final document score using addition.\n */\n var fieldRef = lunr.FieldRef.fromString(matchingFieldRefs[i]),\n docRef = fieldRef.docRef\n\n if (!allRequiredMatches.contains(docRef)) {\n continue\n }\n\n if (allProhibitedMatches.contains(docRef)) {\n continue\n }\n\n var fieldVector = this.fieldVectors[fieldRef],\n score = queryVectors[fieldRef.fieldName].similarity(fieldVector),\n docMatch\n\n if ((docMatch = matches[docRef]) !== undefined) {\n docMatch.score += score\n docMatch.matchData.combine(matchingFields[fieldRef])\n } else {\n var match = {\n ref: docRef,\n score: score,\n matchData: matchingFields[fieldRef]\n }\n matches[docRef] = match\n results.push(match)\n }\n }\n\n /*\n * Sort the results objects by score, highest first.\n */\n return results.sort(function (a, b) {\n return b.score - a.score\n })\n}\n\n/**\n * Prepares the index for JSON serialization.\n *\n * The schema for this JSON blob will be described in a\n * separate JSON schema file.\n *\n * @returns {Object}\n */\nlunr.Index.prototype.toJSON = function () {\n var invertedIndex = Object.keys(this.invertedIndex)\n .sort()\n .map(function (term) {\n return [term, this.invertedIndex[term]]\n }, this)\n\n var fieldVectors = Object.keys(this.fieldVectors)\n .map(function (ref) {\n return [ref, this.fieldVectors[ref].toJSON()]\n }, this)\n\n return {\n version: lunr.version,\n fields: this.fields,\n fieldVectors: fieldVectors,\n invertedIndex: invertedIndex,\n pipeline: this.pipeline.toJSON()\n }\n}\n\n/**\n * Loads a previously serialized lunr.Index\n *\n * @param {Object} serializedIndex - A previously serialized lunr.Index\n * @returns {lunr.Index}\n */\nlunr.Index.load = function (serializedIndex) {\n var attrs = {},\n fieldVectors = {},\n serializedVectors = serializedIndex.fieldVectors,\n invertedIndex = Object.create(null),\n serializedInvertedIndex = serializedIndex.invertedIndex,\n tokenSetBuilder = new lunr.TokenSet.Builder,\n pipeline = lunr.Pipeline.load(serializedIndex.pipeline)\n\n if (serializedIndex.version != lunr.version) {\n lunr.utils.warn(\"Version mismatch when loading serialised index. Current version of lunr '\" + lunr.version + \"' does not match serialized index '\" + serializedIndex.version + \"'\")\n }\n\n for (var i = 0; i < serializedVectors.length; i++) {\n var tuple = serializedVectors[i],\n ref = tuple[0],\n elements = tuple[1]\n\n fieldVectors[ref] = new lunr.Vector(elements)\n }\n\n for (var i = 0; i < serializedInvertedIndex.length; i++) {\n var tuple = serializedInvertedIndex[i],\n term = tuple[0],\n posting = tuple[1]\n\n tokenSetBuilder.insert(term)\n invertedIndex[term] = posting\n }\n\n tokenSetBuilder.finish()\n\n attrs.fields = serializedIndex.fields\n\n attrs.fieldVectors = fieldVectors\n attrs.invertedIndex = invertedIndex\n attrs.tokenSet = tokenSetBuilder.root\n attrs.pipeline = pipeline\n\n return new lunr.Index(attrs)\n}\n/*!\n * lunr.Builder\n * Copyright (C) 2020 Oliver Nightingale\n */\n\n/**\n * lunr.Builder performs indexing on a set of documents and\n * returns instances of lunr.Index ready for querying.\n *\n * All configuration of the index is done via the builder, the\n * fields to index, the document reference, the text processing\n * pipeline and document scoring parameters are all set on the\n * builder before indexing.\n *\n * @constructor\n * @property {string} _ref - Internal reference to the document reference field.\n * @property {string[]} _fields - Internal reference to the document fields to index.\n * @property {object} invertedIndex - The inverted index maps terms to document fields.\n * @property {object} documentTermFrequencies - Keeps track of document term frequencies.\n * @property {object} documentLengths - Keeps track of the length of documents added to the index.\n * @property {lunr.tokenizer} tokenizer - Function for splitting strings into tokens for indexing.\n * @property {lunr.Pipeline} pipeline - The pipeline performs text processing on tokens before indexing.\n * @property {lunr.Pipeline} searchPipeline - A pipeline for processing search terms before querying the index.\n * @property {number} documentCount - Keeps track of the total number of documents indexed.\n * @property {number} _b - A parameter to control field length normalization, setting this to 0 disabled normalization, 1 fully normalizes field lengths, the default value is 0.75.\n * @property {number} _k1 - A parameter to control how quickly an increase in term frequency results in term frequency saturation, the default value is 1.2.\n * @property {number} termIndex - A counter incremented for each unique term, used to identify a terms position in the vector space.\n * @property {array} metadataWhitelist - A list of metadata keys that have been whitelisted for entry in the index.\n */\nlunr.Builder = function () {\n this._ref = \"id\"\n this._fields = Object.create(null)\n this._documents = Object.create(null)\n this.invertedIndex = Object.create(null)\n this.fieldTermFrequencies = {}\n this.fieldLengths = {}\n this.tokenizer = lunr.tokenizer\n this.pipeline = new lunr.Pipeline\n this.searchPipeline = new lunr.Pipeline\n this.documentCount = 0\n this._b = 0.75\n this._k1 = 1.2\n this.termIndex = 0\n this.metadataWhitelist = []\n}\n\n/**\n * Sets the document field used as the document reference. Every document must have this field.\n * The type of this field in the document should be a string, if it is not a string it will be\n * coerced into a string by calling toString.\n *\n * The default ref is 'id'.\n *\n * The ref should _not_ be changed during indexing, it should be set before any documents are\n * added to the index. Changing it during indexing can lead to inconsistent results.\n *\n * @param {string} ref - The name of the reference field in the document.\n */\nlunr.Builder.prototype.ref = function (ref) {\n this._ref = ref\n}\n\n/**\n * A function that is used to extract a field from a document.\n *\n * Lunr expects a field to be at the top level of a document, if however the field\n * is deeply nested within a document an extractor function can be used to extract\n * the right field for indexing.\n *\n * @callback fieldExtractor\n * @param {object} doc - The document being added to the index.\n * @returns {?(string|object|object[])} obj - The object that will be indexed for this field.\n * @example
Extracting a nested field
\n * function (doc) { return doc.nested.field }\n */\n\n/**\n * Adds a field to the list of document fields that will be indexed. Every document being\n * indexed should have this field. Null values for this field in indexed documents will\n * not cause errors but will limit the chance of that document being retrieved by searches.\n *\n * All fields should be added before adding documents to the index. Adding fields after\n * a document has been indexed will have no effect on already indexed documents.\n *\n * Fields can be boosted at build time. This allows terms within that field to have more\n * importance when ranking search results. Use a field boost to specify that matches within\n * one field are more important than other fields.\n *\n * @param {string} fieldName - The name of a field to index in all documents.\n * @param {object} attributes - Optional attributes associated with this field.\n * @param {number} [attributes.boost=1] - Boost applied to all terms within this field.\n * @param {fieldExtractor} [attributes.extractor] - Function to extract a field from a document.\n * @throws {RangeError} fieldName cannot contain unsupported characters '/'\n */\nlunr.Builder.prototype.field = function (fieldName, attributes) {\n if (/\\//.test(fieldName)) {\n throw new RangeError (\"Field '\" + fieldName + \"' contains illegal character '/'\")\n }\n\n this._fields[fieldName] = attributes || {}\n}\n\n/**\n * A parameter to tune the amount of field length normalisation that is applied when\n * calculating relevance scores. A value of 0 will completely disable any normalisation\n * and a value of 1 will fully normalise field lengths. The default is 0.75. Values of b\n * will be clamped to the range 0 - 1.\n *\n * @param {number} number - The value to set for this tuning parameter.\n */\nlunr.Builder.prototype.b = function (number) {\n if (number < 0) {\n this._b = 0\n } else if (number > 1) {\n this._b = 1\n } else {\n this._b = number\n }\n}\n\n/**\n * A parameter that controls the speed at which a rise in term frequency results in term\n * frequency saturation. The default value is 1.2. Setting this to a higher value will give\n * slower saturation levels, a lower value will result in quicker saturation.\n *\n * @param {number} number - The value to set for this tuning parameter.\n */\nlunr.Builder.prototype.k1 = function (number) {\n this._k1 = number\n}\n\n/**\n * Adds a document to the index.\n *\n * Before adding fields to the index the index should have been fully setup, with the document\n * ref and all fields to index already having been specified.\n *\n * The document must have a field name as specified by the ref (by default this is 'id') and\n * it should have all fields defined for indexing, though null or undefined values will not\n * cause errors.\n *\n * Entire documents can be boosted at build time. Applying a boost to a document indicates that\n * this document should rank higher in search results than other documents.\n *\n * @param {object} doc - The document to add to the index.\n * @param {object} attributes - Optional attributes associated with this document.\n * @param {number} [attributes.boost=1] - Boost applied to all terms within this document.\n */\nlunr.Builder.prototype.add = function (doc, attributes) {\n var docRef = doc[this._ref],\n fields = Object.keys(this._fields)\n\n this._documents[docRef] = attributes || {}\n this.documentCount += 1\n\n for (var i = 0; i < fields.length; i++) {\n var fieldName = fields[i],\n extractor = this._fields[fieldName].extractor,\n field = extractor ? extractor(doc) : doc[fieldName],\n tokens = this.tokenizer(field, {\n fields: [fieldName]\n }),\n terms = this.pipeline.run(tokens),\n fieldRef = new lunr.FieldRef (docRef, fieldName),\n fieldTerms = Object.create(null)\n\n this.fieldTermFrequencies[fieldRef] = fieldTerms\n this.fieldLengths[fieldRef] = 0\n\n // store the length of this field for this document\n this.fieldLengths[fieldRef] += terms.length\n\n // calculate term frequencies for this field\n for (var j = 0; j < terms.length; j++) {\n var term = terms[j]\n\n if (fieldTerms[term] == undefined) {\n fieldTerms[term] = 0\n }\n\n fieldTerms[term] += 1\n\n // add to inverted index\n // create an initial posting if one doesn't exist\n if (this.invertedIndex[term] == undefined) {\n var posting = Object.create(null)\n posting[\"_index\"] = this.termIndex\n this.termIndex += 1\n\n for (var k = 0; k < fields.length; k++) {\n posting[fields[k]] = Object.create(null)\n }\n\n this.invertedIndex[term] = posting\n }\n\n // add an entry for this term/fieldName/docRef to the invertedIndex\n if (this.invertedIndex[term][fieldName][docRef] == undefined) {\n this.invertedIndex[term][fieldName][docRef] = Object.create(null)\n }\n\n // store all whitelisted metadata about this token in the\n // inverted index\n for (var l = 0; l < this.metadataWhitelist.length; l++) {\n var metadataKey = this.metadataWhitelist[l],\n metadata = term.metadata[metadataKey]\n\n if (this.invertedIndex[term][fieldName][docRef][metadataKey] == undefined) {\n this.invertedIndex[term][fieldName][docRef][metadataKey] = []\n }\n\n this.invertedIndex[term][fieldName][docRef][metadataKey].push(metadata)\n }\n }\n\n }\n}\n\n/**\n * Calculates the average document length for this index\n *\n * @private\n */\nlunr.Builder.prototype.calculateAverageFieldLengths = function () {\n\n var fieldRefs = Object.keys(this.fieldLengths),\n numberOfFields = fieldRefs.length,\n accumulator = {},\n documentsWithField = {}\n\n for (var i = 0; i < numberOfFields; i++) {\n var fieldRef = lunr.FieldRef.fromString(fieldRefs[i]),\n field = fieldRef.fieldName\n\n documentsWithField[field] || (documentsWithField[field] = 0)\n documentsWithField[field] += 1\n\n accumulator[field] || (accumulator[field] = 0)\n accumulator[field] += this.fieldLengths[fieldRef]\n }\n\n var fields = Object.keys(this._fields)\n\n for (var i = 0; i < fields.length; i++) {\n var fieldName = fields[i]\n accumulator[fieldName] = accumulator[fieldName] / documentsWithField[fieldName]\n }\n\n this.averageFieldLength = accumulator\n}\n\n/**\n * Builds a vector space model of every document using lunr.Vector\n *\n * @private\n */\nlunr.Builder.prototype.createFieldVectors = function () {\n var fieldVectors = {},\n fieldRefs = Object.keys(this.fieldTermFrequencies),\n fieldRefsLength = fieldRefs.length,\n termIdfCache = Object.create(null)\n\n for (var i = 0; i < fieldRefsLength; i++) {\n var fieldRef = lunr.FieldRef.fromString(fieldRefs[i]),\n fieldName = fieldRef.fieldName,\n fieldLength = this.fieldLengths[fieldRef],\n fieldVector = new lunr.Vector,\n termFrequencies = this.fieldTermFrequencies[fieldRef],\n terms = Object.keys(termFrequencies),\n termsLength = terms.length\n\n\n var fieldBoost = this._fields[fieldName].boost || 1,\n docBoost = this._documents[fieldRef.docRef].boost || 1\n\n for (var j = 0; j < termsLength; j++) {\n var term = terms[j],\n tf = termFrequencies[term],\n termIndex = this.invertedIndex[term]._index,\n idf, score, scoreWithPrecision\n\n if (termIdfCache[term] === undefined) {\n idf = lunr.idf(this.invertedIndex[term], this.documentCount)\n termIdfCache[term] = idf\n } else {\n idf = termIdfCache[term]\n }\n\n score = idf * ((this._k1 + 1) * tf) / (this._k1 * (1 - this._b + this._b * (fieldLength / this.averageFieldLength[fieldName])) + tf)\n score *= fieldBoost\n score *= docBoost\n scoreWithPrecision = Math.round(score * 1000) / 1000\n // Converts 1.23456789 to 1.234.\n // Reducing the precision so that the vectors take up less\n // space when serialised. Doing it now so that they behave\n // the same before and after serialisation. Also, this is\n // the fastest approach to reducing a number's precision in\n // JavaScript.\n\n fieldVector.insert(termIndex, scoreWithPrecision)\n }\n\n fieldVectors[fieldRef] = fieldVector\n }\n\n this.fieldVectors = fieldVectors\n}\n\n/**\n * Creates a token set of all tokens in the index using lunr.TokenSet\n *\n * @private\n */\nlunr.Builder.prototype.createTokenSet = function () {\n this.tokenSet = lunr.TokenSet.fromArray(\n Object.keys(this.invertedIndex).sort()\n )\n}\n\n/**\n * Builds the index, creating an instance of lunr.Index.\n *\n * This completes the indexing process and should only be called\n * once all documents have been added to the index.\n *\n * @returns {lunr.Index}\n */\nlunr.Builder.prototype.build = function () {\n this.calculateAverageFieldLengths()\n this.createFieldVectors()\n this.createTokenSet()\n\n return new lunr.Index({\n invertedIndex: this.invertedIndex,\n fieldVectors: this.fieldVectors,\n tokenSet: this.tokenSet,\n fields: Object.keys(this._fields),\n pipeline: this.searchPipeline\n })\n}\n\n/**\n * Applies a plugin to the index builder.\n *\n * A plugin is a function that is called with the index builder as its context.\n * Plugins can be used to customise or extend the behaviour of the index\n * in some way. A plugin is just a function, that encapsulated the custom\n * behaviour that should be applied when building the index.\n *\n * The plugin function will be called with the index builder as its argument, additional\n * arguments can also be passed when calling use. The function will be called\n * with the index builder as its context.\n *\n * @param {Function} plugin The plugin to apply.\n */\nlunr.Builder.prototype.use = function (fn) {\n var args = Array.prototype.slice.call(arguments, 1)\n args.unshift(this)\n fn.apply(this, args)\n}\n/**\n * Contains and collects metadata about a matching document.\n * A single instance of lunr.MatchData is returned as part of every\n * lunr.Index~Result.\n *\n * @constructor\n * @param {string} term - The term this match data is associated with\n * @param {string} field - The field in which the term was found\n * @param {object} metadata - The metadata recorded about this term in this field\n * @property {object} metadata - A cloned collection of metadata associated with this document.\n * @see {@link lunr.Index~Result}\n */\nlunr.MatchData = function (term, field, metadata) {\n var clonedMetadata = Object.create(null),\n metadataKeys = Object.keys(metadata || {})\n\n // Cloning the metadata to prevent the original\n // being mutated during match data combination.\n // Metadata is kept in an array within the inverted\n // index so cloning the data can be done with\n // Array#slice\n for (var i = 0; i < metadataKeys.length; i++) {\n var key = metadataKeys[i]\n clonedMetadata[key] = metadata[key].slice()\n }\n\n this.metadata = Object.create(null)\n\n if (term !== undefined) {\n this.metadata[term] = Object.create(null)\n this.metadata[term][field] = clonedMetadata\n }\n}\n\n/**\n * An instance of lunr.MatchData will be created for every term that matches a\n * document. However only one instance is required in a lunr.Index~Result. This\n * method combines metadata from another instance of lunr.MatchData with this\n * objects metadata.\n *\n * @param {lunr.MatchData} otherMatchData - Another instance of match data to merge with this one.\n * @see {@link lunr.Index~Result}\n */\nlunr.MatchData.prototype.combine = function (otherMatchData) {\n var terms = Object.keys(otherMatchData.metadata)\n\n for (var i = 0; i < terms.length; i++) {\n var term = terms[i],\n fields = Object.keys(otherMatchData.metadata[term])\n\n if (this.metadata[term] == undefined) {\n this.metadata[term] = Object.create(null)\n }\n\n for (var j = 0; j < fields.length; j++) {\n var field = fields[j],\n keys = Object.keys(otherMatchData.metadata[term][field])\n\n if (this.metadata[term][field] == undefined) {\n this.metadata[term][field] = Object.create(null)\n }\n\n for (var k = 0; k < keys.length; k++) {\n var key = keys[k]\n\n if (this.metadata[term][field][key] == undefined) {\n this.metadata[term][field][key] = otherMatchData.metadata[term][field][key]\n } else {\n this.metadata[term][field][key] = this.metadata[term][field][key].concat(otherMatchData.metadata[term][field][key])\n }\n\n }\n }\n }\n}\n\n/**\n * Add metadata for a term/field pair to this instance of match data.\n *\n * @param {string} term - The term this match data is associated with\n * @param {string} field - The field in which the term was found\n * @param {object} metadata - The metadata recorded about this term in this field\n */\nlunr.MatchData.prototype.add = function (term, field, metadata) {\n if (!(term in this.metadata)) {\n this.metadata[term] = Object.create(null)\n this.metadata[term][field] = metadata\n return\n }\n\n if (!(field in this.metadata[term])) {\n this.metadata[term][field] = metadata\n return\n }\n\n var metadataKeys = Object.keys(metadata)\n\n for (var i = 0; i < metadataKeys.length; i++) {\n var key = metadataKeys[i]\n\n if (key in this.metadata[term][field]) {\n this.metadata[term][field][key] = this.metadata[term][field][key].concat(metadata[key])\n } else {\n this.metadata[term][field][key] = metadata[key]\n }\n }\n}\n/**\n * A lunr.Query provides a programmatic way of defining queries to be performed\n * against a {@link lunr.Index}.\n *\n * Prefer constructing a lunr.Query using the {@link lunr.Index#query} method\n * so the query object is pre-initialized with the right index fields.\n *\n * @constructor\n * @property {lunr.Query~Clause[]} clauses - An array of query clauses.\n * @property {string[]} allFields - An array of all available fields in a lunr.Index.\n */\nlunr.Query = function (allFields) {\n this.clauses = []\n this.allFields = allFields\n}\n\n/**\n * Constants for indicating what kind of automatic wildcard insertion will be used when constructing a query clause.\n *\n * This allows wildcards to be added to the beginning and end of a term without having to manually do any string\n * concatenation.\n *\n * The wildcard constants can be bitwise combined to select both leading and trailing wildcards.\n *\n * @constant\n * @default\n * @property {number} wildcard.NONE - The term will have no wildcards inserted, this is the default behaviour\n * @property {number} wildcard.LEADING - Prepend the term with a wildcard, unless a leading wildcard already exists\n * @property {number} wildcard.TRAILING - Append a wildcard to the term, unless a trailing wildcard already exists\n * @see lunr.Query~Clause\n * @see lunr.Query#clause\n * @see lunr.Query#term\n * @example
\n * query.term('foo', {\n * wildcard: lunr.Query.wildcard.LEADING | lunr.Query.wildcard.TRAILING\n * })\n */\n\nlunr.Query.wildcard = new String (\"*\")\nlunr.Query.wildcard.NONE = 0\nlunr.Query.wildcard.LEADING = 1\nlunr.Query.wildcard.TRAILING = 2\n\n/**\n * Constants for indicating what kind of presence a term must have in matching documents.\n *\n * @constant\n * @enum {number}\n * @see lunr.Query~Clause\n * @see lunr.Query#clause\n * @see lunr.Query#term\n * @example
query term with required presence
\n * query.term('foo', { presence: lunr.Query.presence.REQUIRED })\n */\nlunr.Query.presence = {\n /**\n * Term's presence in a document is optional, this is the default value.\n */\n OPTIONAL: 1,\n\n /**\n * Term's presence in a document is required, documents that do not contain\n * this term will not be returned.\n */\n REQUIRED: 2,\n\n /**\n * Term's presence in a document is prohibited, documents that do contain\n * this term will not be returned.\n */\n PROHIBITED: 3\n}\n\n/**\n * A single clause in a {@link lunr.Query} contains a term and details on how to\n * match that term against a {@link lunr.Index}.\n *\n * @typedef {Object} lunr.Query~Clause\n * @property {string[]} fields - The fields in an index this clause should be matched against.\n * @property {number} [boost=1] - Any boost that should be applied when matching this clause.\n * @property {number} [editDistance] - Whether the term should have fuzzy matching applied, and how fuzzy the match should be.\n * @property {boolean} [usePipeline] - Whether the term should be passed through the search pipeline.\n * @property {number} [wildcard=lunr.Query.wildcard.NONE] - Whether the term should have wildcards appended or prepended.\n * @property {number} [presence=lunr.Query.presence.OPTIONAL] - The terms presence in any matching documents.\n */\n\n/**\n * Adds a {@link lunr.Query~Clause} to this query.\n *\n * Unless the clause contains the fields to be matched all fields will be matched. In addition\n * a default boost of 1 is applied to the clause.\n *\n * @param {lunr.Query~Clause} clause - The clause to add to this query.\n * @see lunr.Query~Clause\n * @returns {lunr.Query}\n */\nlunr.Query.prototype.clause = function (clause) {\n if (!('fields' in clause)) {\n clause.fields = this.allFields\n }\n\n if (!('boost' in clause)) {\n clause.boost = 1\n }\n\n if (!('usePipeline' in clause)) {\n clause.usePipeline = true\n }\n\n if (!('wildcard' in clause)) {\n clause.wildcard = lunr.Query.wildcard.NONE\n }\n\n if ((clause.wildcard & lunr.Query.wildcard.LEADING) && (clause.term.charAt(0) != lunr.Query.wildcard)) {\n clause.term = \"*\" + clause.term\n }\n\n if ((clause.wildcard & lunr.Query.wildcard.TRAILING) && (clause.term.slice(-1) != lunr.Query.wildcard)) {\n clause.term = \"\" + clause.term + \"*\"\n }\n\n if (!('presence' in clause)) {\n clause.presence = lunr.Query.presence.OPTIONAL\n }\n\n this.clauses.push(clause)\n\n return this\n}\n\n/**\n * A negated query is one in which every clause has a presence of\n * prohibited. These queries require some special processing to return\n * the expected results.\n *\n * @returns boolean\n */\nlunr.Query.prototype.isNegated = function () {\n for (var i = 0; i < this.clauses.length; i++) {\n if (this.clauses[i].presence != lunr.Query.presence.PROHIBITED) {\n return false\n }\n }\n\n return true\n}\n\n/**\n * Adds a term to the current query, under the covers this will create a {@link lunr.Query~Clause}\n * to the list of clauses that make up this query.\n *\n * The term is used as is, i.e. no tokenization will be performed by this method. Instead conversion\n * to a token or token-like string should be done before calling this method.\n *\n * The term will be converted to a string by calling `toString`. Multiple terms can be passed as an\n * array, each term in the array will share the same options.\n *\n * @param {object|object[]} term - The term(s) to add to the query.\n * @param {object} [options] - Any additional properties to add to the query clause.\n * @returns {lunr.Query}\n * @see lunr.Query#clause\n * @see lunr.Query~Clause\n * @example
adding a single term to a query
\n * query.term(\"foo\")\n * @example
adding a single term to a query and specifying search fields, term boost and automatic trailing wildcard
using lunr.tokenizer to convert a string to tokens before using them as terms
\n * query.term(lunr.tokenizer(\"foo bar\"))\n */\nlunr.Query.prototype.term = function (term, options) {\n if (Array.isArray(term)) {\n term.forEach(function (t) { this.term(t, lunr.utils.clone(options)) }, this)\n return this\n }\n\n var clause = options || {}\n clause.term = term.toString()\n\n this.clause(clause)\n\n return this\n}\nlunr.QueryParseError = function (message, start, end) {\n this.name = \"QueryParseError\"\n this.message = message\n this.start = start\n this.end = end\n}\n\nlunr.QueryParseError.prototype = new Error\nlunr.QueryLexer = function (str) {\n this.lexemes = []\n this.str = str\n this.length = str.length\n this.pos = 0\n this.start = 0\n this.escapeCharPositions = []\n}\n\nlunr.QueryLexer.prototype.run = function () {\n var state = lunr.QueryLexer.lexText\n\n while (state) {\n state = state(this)\n }\n}\n\nlunr.QueryLexer.prototype.sliceString = function () {\n var subSlices = [],\n sliceStart = this.start,\n sliceEnd = this.pos\n\n for (var i = 0; i < this.escapeCharPositions.length; i++) {\n sliceEnd = this.escapeCharPositions[i]\n subSlices.push(this.str.slice(sliceStart, sliceEnd))\n sliceStart = sliceEnd + 1\n }\n\n subSlices.push(this.str.slice(sliceStart, this.pos))\n this.escapeCharPositions.length = 0\n\n return subSlices.join('')\n}\n\nlunr.QueryLexer.prototype.emit = function (type) {\n this.lexemes.push({\n type: type,\n str: this.sliceString(),\n start: this.start,\n end: this.pos\n })\n\n this.start = this.pos\n}\n\nlunr.QueryLexer.prototype.escapeCharacter = function () {\n this.escapeCharPositions.push(this.pos - 1)\n this.pos += 1\n}\n\nlunr.QueryLexer.prototype.next = function () {\n if (this.pos >= this.length) {\n return lunr.QueryLexer.EOS\n }\n\n var char = this.str.charAt(this.pos)\n this.pos += 1\n return char\n}\n\nlunr.QueryLexer.prototype.width = function () {\n return this.pos - this.start\n}\n\nlunr.QueryLexer.prototype.ignore = function () {\n if (this.start == this.pos) {\n this.pos += 1\n }\n\n this.start = this.pos\n}\n\nlunr.QueryLexer.prototype.backup = function () {\n this.pos -= 1\n}\n\nlunr.QueryLexer.prototype.acceptDigitRun = function () {\n var char, charCode\n\n do {\n char = this.next()\n charCode = char.charCodeAt(0)\n } while (charCode > 47 && charCode < 58)\n\n if (char != lunr.QueryLexer.EOS) {\n this.backup()\n }\n}\n\nlunr.QueryLexer.prototype.more = function () {\n return this.pos < this.length\n}\n\nlunr.QueryLexer.EOS = 'EOS'\nlunr.QueryLexer.FIELD = 'FIELD'\nlunr.QueryLexer.TERM = 'TERM'\nlunr.QueryLexer.EDIT_DISTANCE = 'EDIT_DISTANCE'\nlunr.QueryLexer.BOOST = 'BOOST'\nlunr.QueryLexer.PRESENCE = 'PRESENCE'\n\nlunr.QueryLexer.lexField = function (lexer) {\n lexer.backup()\n lexer.emit(lunr.QueryLexer.FIELD)\n lexer.ignore()\n return lunr.QueryLexer.lexText\n}\n\nlunr.QueryLexer.lexTerm = function (lexer) {\n if (lexer.width() > 1) {\n lexer.backup()\n lexer.emit(lunr.QueryLexer.TERM)\n }\n\n lexer.ignore()\n\n if (lexer.more()) {\n return lunr.QueryLexer.lexText\n }\n}\n\nlunr.QueryLexer.lexEditDistance = function (lexer) {\n lexer.ignore()\n lexer.acceptDigitRun()\n lexer.emit(lunr.QueryLexer.EDIT_DISTANCE)\n return lunr.QueryLexer.lexText\n}\n\nlunr.QueryLexer.lexBoost = function (lexer) {\n lexer.ignore()\n lexer.acceptDigitRun()\n lexer.emit(lunr.QueryLexer.BOOST)\n return lunr.QueryLexer.lexText\n}\n\nlunr.QueryLexer.lexEOS = function (lexer) {\n if (lexer.width() > 0) {\n lexer.emit(lunr.QueryLexer.TERM)\n }\n}\n\n// This matches the separator used when tokenising fields\n// within a document. These should match otherwise it is\n// not possible to search for some tokens within a document.\n//\n// It is possible for the user to change the separator on the\n// tokenizer so it _might_ clash with any other of the special\n// characters already used within the search string, e.g. :.\n//\n// This means that it is possible to change the separator in\n// such a way that makes some words unsearchable using a search\n// string.\nlunr.QueryLexer.termSeparator = lunr.tokenizer.separator\n\nlunr.QueryLexer.lexText = function (lexer) {\n while (true) {\n var char = lexer.next()\n\n if (char == lunr.QueryLexer.EOS) {\n return lunr.QueryLexer.lexEOS\n }\n\n // Escape character is '\\'\n if (char.charCodeAt(0) == 92) {\n lexer.escapeCharacter()\n continue\n }\n\n if (char == \":\") {\n return lunr.QueryLexer.lexField\n }\n\n if (char == \"~\") {\n lexer.backup()\n if (lexer.width() > 0) {\n lexer.emit(lunr.QueryLexer.TERM)\n }\n return lunr.QueryLexer.lexEditDistance\n }\n\n if (char == \"^\") {\n lexer.backup()\n if (lexer.width() > 0) {\n lexer.emit(lunr.QueryLexer.TERM)\n }\n return lunr.QueryLexer.lexBoost\n }\n\n // \"+\" indicates term presence is required\n // checking for length to ensure that only\n // leading \"+\" are considered\n if (char == \"+\" && lexer.width() === 1) {\n lexer.emit(lunr.QueryLexer.PRESENCE)\n return lunr.QueryLexer.lexText\n }\n\n // \"-\" indicates term presence is prohibited\n // checking for length to ensure that only\n // leading \"-\" are considered\n if (char == \"-\" && lexer.width() === 1) {\n lexer.emit(lunr.QueryLexer.PRESENCE)\n return lunr.QueryLexer.lexText\n }\n\n if (char.match(lunr.QueryLexer.termSeparator)) {\n return lunr.QueryLexer.lexTerm\n }\n }\n}\n\nlunr.QueryParser = function (str, query) {\n this.lexer = new lunr.QueryLexer (str)\n this.query = query\n this.currentClause = {}\n this.lexemeIdx = 0\n}\n\nlunr.QueryParser.prototype.parse = function () {\n this.lexer.run()\n this.lexemes = this.lexer.lexemes\n\n var state = lunr.QueryParser.parseClause\n\n while (state) {\n state = state(this)\n }\n\n return this.query\n}\n\nlunr.QueryParser.prototype.peekLexeme = function () {\n return this.lexemes[this.lexemeIdx]\n}\n\nlunr.QueryParser.prototype.consumeLexeme = function () {\n var lexeme = this.peekLexeme()\n this.lexemeIdx += 1\n return lexeme\n}\n\nlunr.QueryParser.prototype.nextClause = function () {\n var completedClause = this.currentClause\n this.query.clause(completedClause)\n this.currentClause = {}\n}\n\nlunr.QueryParser.parseClause = function (parser) {\n var lexeme = parser.peekLexeme()\n\n if (lexeme == undefined) {\n return\n }\n\n switch (lexeme.type) {\n case lunr.QueryLexer.PRESENCE:\n return lunr.QueryParser.parsePresence\n case lunr.QueryLexer.FIELD:\n return lunr.QueryParser.parseField\n case lunr.QueryLexer.TERM:\n return lunr.QueryParser.parseTerm\n default:\n var errorMessage = \"expected either a field or a term, found \" + lexeme.type\n\n if (lexeme.str.length >= 1) {\n errorMessage += \" with value '\" + lexeme.str + \"'\"\n }\n\n throw new lunr.QueryParseError (errorMessage, lexeme.start, lexeme.end)\n }\n}\n\nlunr.QueryParser.parsePresence = function (parser) {\n var lexeme = parser.consumeLexeme()\n\n if (lexeme == undefined) {\n return\n }\n\n switch (lexeme.str) {\n case \"-\":\n parser.currentClause.presence = lunr.Query.presence.PROHIBITED\n break\n case \"+\":\n parser.currentClause.presence = lunr.Query.presence.REQUIRED\n break\n default:\n var errorMessage = \"unrecognised presence operator'\" + lexeme.str + \"'\"\n throw new lunr.QueryParseError (errorMessage, lexeme.start, lexeme.end)\n }\n\n var nextLexeme = parser.peekLexeme()\n\n if (nextLexeme == undefined) {\n var errorMessage = \"expecting term or field, found nothing\"\n throw new lunr.QueryParseError (errorMessage, lexeme.start, lexeme.end)\n }\n\n switch (nextLexeme.type) {\n case lunr.QueryLexer.FIELD:\n return lunr.QueryParser.parseField\n case lunr.QueryLexer.TERM:\n return lunr.QueryParser.parseTerm\n default:\n var errorMessage = \"expecting term or field, found '\" + nextLexeme.type + \"'\"\n throw new lunr.QueryParseError (errorMessage, nextLexeme.start, nextLexeme.end)\n }\n}\n\nlunr.QueryParser.parseField = function (parser) {\n var lexeme = parser.consumeLexeme()\n\n if (lexeme == undefined) {\n return\n }\n\n if (parser.query.allFields.indexOf(lexeme.str) == -1) {\n var possibleFields = parser.query.allFields.map(function (f) { return \"'\" + f + \"'\" }).join(', '),\n errorMessage = \"unrecognised field '\" + lexeme.str + \"', possible fields: \" + possibleFields\n\n throw new lunr.QueryParseError (errorMessage, lexeme.start, lexeme.end)\n }\n\n parser.currentClause.fields = [lexeme.str]\n\n var nextLexeme = parser.peekLexeme()\n\n if (nextLexeme == undefined) {\n var errorMessage = \"expecting term, found nothing\"\n throw new lunr.QueryParseError (errorMessage, lexeme.start, lexeme.end)\n }\n\n switch (nextLexeme.type) {\n case lunr.QueryLexer.TERM:\n return lunr.QueryParser.parseTerm\n default:\n var errorMessage = \"expecting term, found '\" + nextLexeme.type + \"'\"\n throw new lunr.QueryParseError (errorMessage, nextLexeme.start, nextLexeme.end)\n }\n}\n\nlunr.QueryParser.parseTerm = function (parser) {\n var lexeme = parser.consumeLexeme()\n\n if (lexeme == undefined) {\n return\n }\n\n parser.currentClause.term = lexeme.str.toLowerCase()\n\n if (lexeme.str.indexOf(\"*\") != -1) {\n parser.currentClause.usePipeline = false\n }\n\n var nextLexeme = parser.peekLexeme()\n\n if (nextLexeme == undefined) {\n parser.nextClause()\n return\n }\n\n switch (nextLexeme.type) {\n case lunr.QueryLexer.TERM:\n parser.nextClause()\n return lunr.QueryParser.parseTerm\n case lunr.QueryLexer.FIELD:\n parser.nextClause()\n return lunr.QueryParser.parseField\n case lunr.QueryLexer.EDIT_DISTANCE:\n return lunr.QueryParser.parseEditDistance\n case lunr.QueryLexer.BOOST:\n return lunr.QueryParser.parseBoost\n case lunr.QueryLexer.PRESENCE:\n parser.nextClause()\n return lunr.QueryParser.parsePresence\n default:\n var errorMessage = \"Unexpected lexeme type '\" + nextLexeme.type + \"'\"\n throw new lunr.QueryParseError (errorMessage, nextLexeme.start, nextLexeme.end)\n }\n}\n\nlunr.QueryParser.parseEditDistance = function (parser) {\n var lexeme = parser.consumeLexeme()\n\n if (lexeme == undefined) {\n return\n }\n\n var editDistance = parseInt(lexeme.str, 10)\n\n if (isNaN(editDistance)) {\n var errorMessage = \"edit distance must be numeric\"\n throw new lunr.QueryParseError (errorMessage, lexeme.start, lexeme.end)\n }\n\n parser.currentClause.editDistance = editDistance\n\n var nextLexeme = parser.peekLexeme()\n\n if (nextLexeme == undefined) {\n parser.nextClause()\n return\n }\n\n switch (nextLexeme.type) {\n case lunr.QueryLexer.TERM:\n parser.nextClause()\n return lunr.QueryParser.parseTerm\n case lunr.QueryLexer.FIELD:\n parser.nextClause()\n return lunr.QueryParser.parseField\n case lunr.QueryLexer.EDIT_DISTANCE:\n return lunr.QueryParser.parseEditDistance\n case lunr.QueryLexer.BOOST:\n return lunr.QueryParser.parseBoost\n case lunr.QueryLexer.PRESENCE:\n parser.nextClause()\n return lunr.QueryParser.parsePresence\n default:\n var errorMessage = \"Unexpected lexeme type '\" + nextLexeme.type + \"'\"\n throw new lunr.QueryParseError (errorMessage, nextLexeme.start, nextLexeme.end)\n }\n}\n\nlunr.QueryParser.parseBoost = function (parser) {\n var lexeme = parser.consumeLexeme()\n\n if (lexeme == undefined) {\n return\n }\n\n var boost = parseInt(lexeme.str, 10)\n\n if (isNaN(boost)) {\n var errorMessage = \"boost must be numeric\"\n throw new lunr.QueryParseError (errorMessage, lexeme.start, lexeme.end)\n }\n\n parser.currentClause.boost = boost\n\n var nextLexeme = parser.peekLexeme()\n\n if (nextLexeme == undefined) {\n parser.nextClause()\n return\n }\n\n switch (nextLexeme.type) {\n case lunr.QueryLexer.TERM:\n parser.nextClause()\n return lunr.QueryParser.parseTerm\n case lunr.QueryLexer.FIELD:\n parser.nextClause()\n return lunr.QueryParser.parseField\n case lunr.QueryLexer.EDIT_DISTANCE:\n return lunr.QueryParser.parseEditDistance\n case lunr.QueryLexer.BOOST:\n return lunr.QueryParser.parseBoost\n case lunr.QueryLexer.PRESENCE:\n parser.nextClause()\n return lunr.QueryParser.parsePresence\n default:\n var errorMessage = \"Unexpected lexeme type '\" + nextLexeme.type + \"'\"\n throw new lunr.QueryParseError (errorMessage, nextLexeme.start, nextLexeme.end)\n }\n}\n\n /**\n * export the module via AMD, CommonJS or as a browser global\n * Export code from https://github.com/umdjs/umd/blob/master/returnExports.js\n */\n ;(function (root, factory) {\n if (typeof define === 'function' && define.amd) {\n // AMD. Register as an anonymous module.\n define(factory)\n } else if (typeof exports === 'object') {\n /**\n * Node. Does not work with strict CommonJS, but\n * only CommonJS-like enviroments that support module.exports,\n * like Node.\n */\n module.exports = factory()\n } else {\n // Browser globals (root is window)\n root.lunr = factory()\n }\n }(this, function () {\n /**\n * Just return a value to define the module export.\n * This example returns an object, but the module\n * can return a function as the exported value.\n */\n return lunr\n }))\n})();\n"],"names":["global","step2list","step3list","v","C","re_mgr0","re_mgr1","re_meq1","re_s_v","re_1a","re2_1a","re_1b","re2_1b","re_1b_2","re2_1b_2","re3_1b_2","re4_1b_2","re_1c","re_2","re_3","re_4","re2_4","re_5","re_5_1","re3_5","porterStemmer","root","factory","lunr","config","builder","Builder","pipeline","add","trimmer","stopWordFilter","stemmer","searchPipeline","call","build","version","utils","warn","this","message","console","asString","obj","toString","clone","Object","create","keys","i","length","key","val","Array","isArray","slice","TypeError","FieldRef","docRef","fieldName","stringValue","_stringValue","joiner","fromString","s","n","indexOf","fieldRef","prototype","undefined","Set","elements","complete","intersect","other","union","contains","empty","object","a","b","intersection","element","push","concat","idf","posting","documentCount","documentsWithTerm","x","Math","log","abs","Token","str","metadata","update","fn","tokenizer","map","t","toLowerCase","len","tokens","sliceEnd","sliceStart","sliceLength","charAt","match","separator","tokenMetadata","Pipeline","_stack","registeredFunctions","registerFunction","label","warnIfFunctionNotRegistered","load","serialised","forEach","fnName","Error","fns","arguments","after","existingFn","newFn","pos","splice","before","remove","run","stackLength","memo","j","result","k","runString","token","reset","toJSON","Vector","_magnitude","positionForIndex","index","start","end","pivotPoint","floor","pivotIndex","insert","insertIdx","upsert","position","magnitude","sumOfSquares","elementsLength","sqrt","dot","otherVector","dotProduct","aLen","bLen","aVal","bVal","similarity","toArray","output","c","RegExp","w","stem","suffix","firstch","re","re2","re3","re4","substr","toUpperCase","test","replace","fp","exec","generateStopWordFilter","stopWords","words","reduce","stopWord","TokenSet","final","edges","id","_nextId","fromArray","arr","finish","fromClause","clause","fromFuzzyString","term","editDistance","stack","node","editsRemaining","frame","pop","noEditNode","char","insertionNode","substitutionNode","transposeNode","charA","charB","next","prefix","edge","_str","labels","sort","qNode","qEdges","qLen","nEdges","nLen","q","qEdge","nEdge","previousWord","uncheckedNodes","minimizedNodes","word","commonPrefix","minimize","child","nextNode","parent","downTo","childKey","Index","attrs","invertedIndex","fieldVectors","tokenSet","fields","search","queryString","query","QueryParser","parse","Query","matchingFields","queryVectors","termFieldCache","requiredMatches","prohibitedMatches","clauses","terms","clauseMatches","usePipeline","m","termTokenSet","expandedTerms","presence","REQUIRED","field","expandedTerm","termIndex","_index","fieldPosting","matchingDocumentRefs","termField","matchingDocumentsSet","PROHIBITED","boost","l","fieldMatch","matchingDocumentRef","matchingFieldRef","MatchData","allRequiredMatches","allProhibitedMatches","matchingFieldRefs","results","matches","isNegated","docMatch","fieldVector","score","matchData","combine","ref","serializedIndex","serializedVectors","serializedInvertedIndex","tokenSetBuilder","tuple","_ref","_fields","_documents","fieldTermFrequencies","fieldLengths","_b","_k1","metadataWhitelist","attributes","RangeError","number","k1","doc","extractor","fieldTerms","metadataKey","calculateAverageFieldLengths","fieldRefs","numberOfFields","accumulator","documentsWithField","averageFieldLength","createFieldVectors","fieldRefsLength","termIdfCache","fieldLength","termFrequencies","termsLength","fieldBoost","docBoost","scoreWithPrecision","tf","round","createTokenSet","use","args","unshift","apply","clonedMetadata","metadataKeys","otherMatchData","allFields","wildcard","String","NONE","LEADING","TRAILING","OPTIONAL","options","QueryParseError","name","QueryLexer","lexemes","escapeCharPositions","state","lexText","sliceString","subSlices","join","emit","type","escapeCharacter","EOS","width","ignore","backup","acceptDigitRun","charCode","charCodeAt","more","FIELD","TERM","EDIT_DISTANCE","BOOST","PRESENCE","lexField","lexer","lexTerm","lexEditDistance","lexBoost","lexEOS","termSeparator","currentClause","lexemeIdx","parseClause","peekLexeme","consumeLexeme","lexeme","nextClause","completedClause","parser","parsePresence","parseField","parseTerm","errorMessage","nextLexeme","possibleFields","f","parseEditDistance","parseBoost","parseInt","isNaN","define","amd","exports","module"],"mappings":"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"}
\ No newline at end of file
+{"version":3,"file":"lunr.min.js","sources":["../src/lunr.js"],"sourcesContent":["/**\n * moodle readme\n *\n * Lunrjs can be downloaded from https://github.com/olivernn/lunr.js. To update this library get the lunr.js file\n * from this project and replace the content below with the new content.\n */\n\n/**\n * lunr - http://lunrjs.com - A bit like Solr, but much smaller and not as bright - 2.3.9\n * Copyright (C) 2020 Oliver Nightingale\n * @license MIT\n */\n\n;(function(){\n\n/**\n * A convenience function for configuring and constructing\n * a new lunr Index.\n *\n * A lunr.Builder instance is created and the pipeline setup\n * with a trimmer, stop word filter and stemmer.\n *\n * This builder object is yielded to the configuration function\n * that is passed as a parameter, allowing the list of fields\n * and other builder parameters to be customised.\n *\n * All documents _must_ be added within the passed config function.\n *\n * @example\n * var idx = lunr(function () {\n * this.field('title')\n * this.field('body')\n * this.ref('id')\n *\n * documents.forEach(function (doc) {\n * this.add(doc)\n * }, this)\n * })\n *\n * @see {@link lunr.Builder}\n * @see {@link lunr.Pipeline}\n * @see {@link lunr.trimmer}\n * @see {@link lunr.stopWordFilter}\n * @see {@link lunr.stemmer}\n * @namespace {function} lunr\n */\nvar lunr = function (config) {\n var builder = new lunr.Builder\n\n builder.pipeline.add(\n lunr.trimmer,\n lunr.stopWordFilter,\n lunr.stemmer\n )\n\n builder.searchPipeline.add(\n lunr.stemmer\n )\n\n config.call(builder, builder)\n return builder.build()\n}\n\nlunr.version = \"2.3.9\"\n/*!\n * lunr.utils\n * Copyright (C) 2020 Oliver Nightingale\n */\n\n/**\n * A namespace containing utils for the rest of the lunr library\n * @namespace lunr.utils\n */\nlunr.utils = {}\n\n/**\n * Print a warning message to the console.\n *\n * @param {String} message The message to be printed.\n * @memberOf lunr.utils\n * @function\n */\nlunr.utils.warn = (function (global) {\n /* eslint-disable no-console */\n return function (message) {\n if (global.console && console.warn) {\n console.warn(message)\n }\n }\n /* eslint-enable no-console */\n})(this)\n\n/**\n * Convert an object to a string.\n *\n * In the case of `null` and `undefined` the function returns\n * the empty string, in all other cases the result of calling\n * `toString` on the passed object is returned.\n *\n * @param {Any} obj The object to convert to a string.\n * @return {String} string representation of the passed object.\n * @memberOf lunr.utils\n */\nlunr.utils.asString = function (obj) {\n if (obj === void 0 || obj === null) {\n return \"\"\n } else {\n return obj.toString()\n }\n}\n\n/**\n * Clones an object.\n *\n * Will create a copy of an existing object such that any mutations\n * on the copy cannot affect the original.\n *\n * Only shallow objects are supported, passing a nested object to this\n * function will cause a TypeError.\n *\n * Objects with primitives, and arrays of primitives are supported.\n *\n * @param {Object} obj The object to clone.\n * @return {Object} a clone of the passed object.\n * @throws {TypeError} when a nested object is passed.\n * @memberOf Utils\n */\nlunr.utils.clone = function (obj) {\n if (obj === null || obj === undefined) {\n return obj\n }\n\n var clone = Object.create(null),\n keys = Object.keys(obj)\n\n for (var i = 0; i < keys.length; i++) {\n var key = keys[i],\n val = obj[key]\n\n if (Array.isArray(val)) {\n clone[key] = val.slice()\n continue\n }\n\n if (typeof val === 'string' ||\n typeof val === 'number' ||\n typeof val === 'boolean') {\n clone[key] = val\n continue\n }\n\n throw new TypeError(\"clone is not deep and does not support nested objects\")\n }\n\n return clone\n}\nlunr.FieldRef = function (docRef, fieldName, stringValue) {\n this.docRef = docRef\n this.fieldName = fieldName\n this._stringValue = stringValue\n}\n\nlunr.FieldRef.joiner = \"/\"\n\nlunr.FieldRef.fromString = function (s) {\n var n = s.indexOf(lunr.FieldRef.joiner)\n\n if (n === -1) {\n throw \"malformed field ref string\"\n }\n\n var fieldRef = s.slice(0, n),\n docRef = s.slice(n + 1)\n\n return new lunr.FieldRef (docRef, fieldRef, s)\n}\n\nlunr.FieldRef.prototype.toString = function () {\n if (this._stringValue == undefined) {\n this._stringValue = this.fieldName + lunr.FieldRef.joiner + this.docRef\n }\n\n return this._stringValue\n}\n/*!\n * lunr.Set\n * Copyright (C) 2020 Oliver Nightingale\n */\n\n/**\n * A lunr set.\n *\n * @constructor\n */\nlunr.Set = function (elements) {\n this.elements = Object.create(null)\n\n if (elements) {\n this.length = elements.length\n\n for (var i = 0; i < this.length; i++) {\n this.elements[elements[i]] = true\n }\n } else {\n this.length = 0\n }\n}\n\n/**\n * A complete set that contains all elements.\n *\n * @static\n * @readonly\n * @type {lunr.Set}\n */\nlunr.Set.complete = {\n intersect: function (other) {\n return other\n },\n\n union: function () {\n return this\n },\n\n contains: function () {\n return true\n }\n}\n\n/**\n * An empty set that contains no elements.\n *\n * @static\n * @readonly\n * @type {lunr.Set}\n */\nlunr.Set.empty = {\n intersect: function () {\n return this\n },\n\n union: function (other) {\n return other\n },\n\n contains: function () {\n return false\n }\n}\n\n/**\n * Returns true if this set contains the specified object.\n *\n * @param {object} object - Object whose presence in this set is to be tested.\n * @returns {boolean} - True if this set contains the specified object.\n */\nlunr.Set.prototype.contains = function (object) {\n return !!this.elements[object]\n}\n\n/**\n * Returns a new set containing only the elements that are present in both\n * this set and the specified set.\n *\n * @param {lunr.Set} other - set to intersect with this set.\n * @returns {lunr.Set} a new set that is the intersection of this and the specified set.\n */\n\nlunr.Set.prototype.intersect = function (other) {\n var a, b, elements, intersection = []\n\n if (other === lunr.Set.complete) {\n return this\n }\n\n if (other === lunr.Set.empty) {\n return other\n }\n\n if (this.length < other.length) {\n a = this\n b = other\n } else {\n a = other\n b = this\n }\n\n elements = Object.keys(a.elements)\n\n for (var i = 0; i < elements.length; i++) {\n var element = elements[i]\n if (element in b.elements) {\n intersection.push(element)\n }\n }\n\n return new lunr.Set (intersection)\n}\n\n/**\n * Returns a new set combining the elements of this and the specified set.\n *\n * @param {lunr.Set} other - set to union with this set.\n * @return {lunr.Set} a new set that is the union of this and the specified set.\n */\n\nlunr.Set.prototype.union = function (other) {\n if (other === lunr.Set.complete) {\n return lunr.Set.complete\n }\n\n if (other === lunr.Set.empty) {\n return this\n }\n\n return new lunr.Set(Object.keys(this.elements).concat(Object.keys(other.elements)))\n}\n/**\n * A function to calculate the inverse document frequency for\n * a posting. This is shared between the builder and the index\n *\n * @private\n * @param {object} posting - The posting for a given term\n * @param {number} documentCount - The total number of documents.\n */\nlunr.idf = function (posting, documentCount) {\n var documentsWithTerm = 0\n\n for (var fieldName in posting) {\n if (fieldName == '_index') continue // Ignore the term index, its not a field\n documentsWithTerm += Object.keys(posting[fieldName]).length\n }\n\n var x = (documentCount - documentsWithTerm + 0.5) / (documentsWithTerm + 0.5)\n\n return Math.log(1 + Math.abs(x))\n}\n\n/**\n * A token wraps a string representation of a token\n * as it is passed through the text processing pipeline.\n *\n * @constructor\n * @param {string} [str=''] - The string token being wrapped.\n * @param {object} [metadata={}] - Metadata associated with this token.\n */\nlunr.Token = function (str, metadata) {\n this.str = str || \"\"\n this.metadata = metadata || {}\n}\n\n/**\n * Returns the token string that is being wrapped by this object.\n *\n * @returns {string}\n */\nlunr.Token.prototype.toString = function () {\n return this.str\n}\n\n/**\n * A token update function is used when updating or optionally\n * when cloning a token.\n *\n * @callback lunr.Token~updateFunction\n * @param {string} str - The string representation of the token.\n * @param {Object} metadata - All metadata associated with this token.\n */\n\n/**\n * Applies the given function to the wrapped string token.\n *\n * @example\n * token.update(function (str, metadata) {\n * return str.toUpperCase()\n * })\n *\n * @param {lunr.Token~updateFunction} fn - A function to apply to the token string.\n * @returns {lunr.Token}\n */\nlunr.Token.prototype.update = function (fn) {\n this.str = fn(this.str, this.metadata)\n return this\n}\n\n/**\n * Creates a clone of this token. Optionally a function can be\n * applied to the cloned token.\n *\n * @param {lunr.Token~updateFunction} [fn] - An optional function to apply to the cloned token.\n * @returns {lunr.Token}\n */\nlunr.Token.prototype.clone = function (fn) {\n fn = fn || function (s) { return s }\n return new lunr.Token (fn(this.str, this.metadata), this.metadata)\n}\n/*!\n * lunr.tokenizer\n * Copyright (C) 2020 Oliver Nightingale\n */\n\n/**\n * A function for splitting a string into tokens ready to be inserted into\n * the search index. Uses `lunr.tokenizer.separator` to split strings, change\n * the value of this property to change how strings are split into tokens.\n *\n * This tokenizer will convert its parameter to a string by calling `toString` and\n * then will split this string on the character in `lunr.tokenizer.separator`.\n * Arrays will have their elements converted to strings and wrapped in a lunr.Token.\n *\n * Optional metadata can be passed to the tokenizer, this metadata will be cloned and\n * added as metadata to every token that is created from the object to be tokenized.\n *\n * @static\n * @param {?(string|object|object[])} obj - The object to convert into tokens\n * @param {?object} metadata - Optional metadata to associate with every token\n * @returns {lunr.Token[]}\n * @see {@link lunr.Pipeline}\n */\nlunr.tokenizer = function (obj, metadata) {\n if (obj == null || obj == undefined) {\n return []\n }\n\n if (Array.isArray(obj)) {\n return obj.map(function (t) {\n return new lunr.Token(\n lunr.utils.asString(t).toLowerCase(),\n lunr.utils.clone(metadata)\n )\n })\n }\n\n var str = obj.toString().toLowerCase(),\n len = str.length,\n tokens = []\n\n for (var sliceEnd = 0, sliceStart = 0; sliceEnd <= len; sliceEnd++) {\n var char = str.charAt(sliceEnd),\n sliceLength = sliceEnd - sliceStart\n\n if ((char.match(lunr.tokenizer.separator) || sliceEnd == len)) {\n\n if (sliceLength > 0) {\n var tokenMetadata = lunr.utils.clone(metadata) || {}\n tokenMetadata[\"position\"] = [sliceStart, sliceLength]\n tokenMetadata[\"index\"] = tokens.length\n\n tokens.push(\n new lunr.Token (\n str.slice(sliceStart, sliceEnd),\n tokenMetadata\n )\n )\n }\n\n sliceStart = sliceEnd + 1\n }\n\n }\n\n return tokens\n}\n\n/**\n * The separator used to split a string into tokens. Override this property to change the behaviour of\n * `lunr.tokenizer` behaviour when tokenizing strings. By default this splits on whitespace and hyphens.\n *\n * @static\n * @see lunr.tokenizer\n */\nlunr.tokenizer.separator = /[\\s\\-]+/\n/*!\n * lunr.Pipeline\n * Copyright (C) 2020 Oliver Nightingale\n */\n\n/**\n * lunr.Pipelines maintain an ordered list of functions to be applied to all\n * tokens in documents entering the search index and queries being ran against\n * the index.\n *\n * An instance of lunr.Index created with the lunr shortcut will contain a\n * pipeline with a stop word filter and an English language stemmer. Extra\n * functions can be added before or after either of these functions or these\n * default functions can be removed.\n *\n * When run the pipeline will call each function in turn, passing a token, the\n * index of that token in the original list of all tokens and finally a list of\n * all the original tokens.\n *\n * The output of functions in the pipeline will be passed to the next function\n * in the pipeline. To exclude a token from entering the index the function\n * should return undefined, the rest of the pipeline will not be called with\n * this token.\n *\n * For serialisation of pipelines to work, all functions used in an instance of\n * a pipeline should be registered with lunr.Pipeline. Registered functions can\n * then be loaded. If trying to load a serialised pipeline that uses functions\n * that are not registered an error will be thrown.\n *\n * If not planning on serialising the pipeline then registering pipeline functions\n * is not necessary.\n *\n * @constructor\n */\nlunr.Pipeline = function () {\n this._stack = []\n}\n\nlunr.Pipeline.registeredFunctions = Object.create(null)\n\n/**\n * A pipeline function maps lunr.Token to lunr.Token. A lunr.Token contains the token\n * string as well as all known metadata. A pipeline function can mutate the token string\n * or mutate (or add) metadata for a given token.\n *\n * A pipeline function can indicate that the passed token should be discarded by returning\n * null, undefined or an empty string. This token will not be passed to any downstream pipeline\n * functions and will not be added to the index.\n *\n * Multiple tokens can be returned by returning an array of tokens. Each token will be passed\n * to any downstream pipeline functions and all will returned tokens will be added to the index.\n *\n * Any number of pipeline functions may be chained together using a lunr.Pipeline.\n *\n * @interface lunr.PipelineFunction\n * @param {lunr.Token} token - A token from the document being processed.\n * @param {number} i - The index of this token in the complete list of tokens for this document/field.\n * @param {lunr.Token[]} tokens - All tokens for this document/field.\n * @returns {(?lunr.Token|lunr.Token[])}\n */\n\n/**\n * Register a function with the pipeline.\n *\n * Functions that are used in the pipeline should be registered if the pipeline\n * needs to be serialised, or a serialised pipeline needs to be loaded.\n *\n * Registering a function does not add it to a pipeline, functions must still be\n * added to instances of the pipeline for them to be used when running a pipeline.\n *\n * @param {lunr.PipelineFunction} fn - The function to check for.\n * @param {String} label - The label to register this function with\n */\nlunr.Pipeline.registerFunction = function (fn, label) {\n if (label in this.registeredFunctions) {\n lunr.utils.warn('Overwriting existing registered function: ' + label)\n }\n\n fn.label = label\n lunr.Pipeline.registeredFunctions[fn.label] = fn\n}\n\n/**\n * Warns if the function is not registered as a Pipeline function.\n *\n * @param {lunr.PipelineFunction} fn - The function to check for.\n * @private\n */\nlunr.Pipeline.warnIfFunctionNotRegistered = function (fn) {\n var isRegistered = fn.label && (fn.label in this.registeredFunctions)\n\n if (!isRegistered) {\n lunr.utils.warn('Function is not registered with pipeline. This may cause problems when serialising the index.\\n', fn)\n }\n}\n\n/**\n * Loads a previously serialised pipeline.\n *\n * All functions to be loaded must already be registered with lunr.Pipeline.\n * If any function from the serialised data has not been registered then an\n * error will be thrown.\n *\n * @param {Object} serialised - The serialised pipeline to load.\n * @returns {lunr.Pipeline}\n */\nlunr.Pipeline.load = function (serialised) {\n var pipeline = new lunr.Pipeline\n\n serialised.forEach(function (fnName) {\n var fn = lunr.Pipeline.registeredFunctions[fnName]\n\n if (fn) {\n pipeline.add(fn)\n } else {\n throw new Error('Cannot load unregistered function: ' + fnName)\n }\n })\n\n return pipeline\n}\n\n/**\n * Adds new functions to the end of the pipeline.\n *\n * Logs a warning if the function has not been registered.\n *\n * @param {lunr.PipelineFunction[]} functions - Any number of functions to add to the pipeline.\n */\nlunr.Pipeline.prototype.add = function () {\n var fns = Array.prototype.slice.call(arguments)\n\n fns.forEach(function (fn) {\n lunr.Pipeline.warnIfFunctionNotRegistered(fn)\n this._stack.push(fn)\n }, this)\n}\n\n/**\n * Adds a single function after a function that already exists in the\n * pipeline.\n *\n * Logs a warning if the function has not been registered.\n *\n * @param {lunr.PipelineFunction} existingFn - A function that already exists in the pipeline.\n * @param {lunr.PipelineFunction} newFn - The new function to add to the pipeline.\n */\nlunr.Pipeline.prototype.after = function (existingFn, newFn) {\n lunr.Pipeline.warnIfFunctionNotRegistered(newFn)\n\n var pos = this._stack.indexOf(existingFn)\n if (pos == -1) {\n throw new Error('Cannot find existingFn')\n }\n\n pos = pos + 1\n this._stack.splice(pos, 0, newFn)\n}\n\n/**\n * Adds a single function before a function that already exists in the\n * pipeline.\n *\n * Logs a warning if the function has not been registered.\n *\n * @param {lunr.PipelineFunction} existingFn - A function that already exists in the pipeline.\n * @param {lunr.PipelineFunction} newFn - The new function to add to the pipeline.\n */\nlunr.Pipeline.prototype.before = function (existingFn, newFn) {\n lunr.Pipeline.warnIfFunctionNotRegistered(newFn)\n\n var pos = this._stack.indexOf(existingFn)\n if (pos == -1) {\n throw new Error('Cannot find existingFn')\n }\n\n this._stack.splice(pos, 0, newFn)\n}\n\n/**\n * Removes a function from the pipeline.\n *\n * @param {lunr.PipelineFunction} fn The function to remove from the pipeline.\n */\nlunr.Pipeline.prototype.remove = function (fn) {\n var pos = this._stack.indexOf(fn)\n if (pos == -1) {\n return\n }\n\n this._stack.splice(pos, 1)\n}\n\n/**\n * Runs the current list of functions that make up the pipeline against the\n * passed tokens.\n *\n * @param {Array} tokens The tokens to run through the pipeline.\n * @returns {Array}\n */\nlunr.Pipeline.prototype.run = function (tokens) {\n var stackLength = this._stack.length\n\n for (var i = 0; i < stackLength; i++) {\n var fn = this._stack[i]\n var memo = []\n\n for (var j = 0; j < tokens.length; j++) {\n var result = fn(tokens[j], j, tokens)\n\n if (result === null || result === void 0 || result === '') continue\n\n if (Array.isArray(result)) {\n for (var k = 0; k < result.length; k++) {\n memo.push(result[k])\n }\n } else {\n memo.push(result)\n }\n }\n\n tokens = memo\n }\n\n return tokens\n}\n\n/**\n * Convenience method for passing a string through a pipeline and getting\n * strings out. This method takes care of wrapping the passed string in a\n * token and mapping the resulting tokens back to strings.\n *\n * @param {string} str - The string to pass through the pipeline.\n * @param {?object} metadata - Optional metadata to associate with the token\n * passed to the pipeline.\n * @returns {string[]}\n */\nlunr.Pipeline.prototype.runString = function (str, metadata) {\n var token = new lunr.Token (str, metadata)\n\n return this.run([token]).map(function (t) {\n return t.toString()\n })\n}\n\n/**\n * Resets the pipeline by removing any existing processors.\n *\n */\nlunr.Pipeline.prototype.reset = function () {\n this._stack = []\n}\n\n/**\n * Returns a representation of the pipeline ready for serialisation.\n *\n * Logs a warning if the function has not been registered.\n *\n * @returns {Array}\n */\nlunr.Pipeline.prototype.toJSON = function () {\n return this._stack.map(function (fn) {\n lunr.Pipeline.warnIfFunctionNotRegistered(fn)\n\n return fn.label\n })\n}\n/*!\n * lunr.Vector\n * Copyright (C) 2020 Oliver Nightingale\n */\n\n/**\n * A vector is used to construct the vector space of documents and queries. These\n * vectors support operations to determine the similarity between two documents or\n * a document and a query.\n *\n * Normally no parameters are required for initializing a vector, but in the case of\n * loading a previously dumped vector the raw elements can be provided to the constructor.\n *\n * For performance reasons vectors are implemented with a flat array, where an elements\n * index is immediately followed by its value. E.g. [index, value, index, value]. This\n * allows the underlying array to be as sparse as possible and still offer decent\n * performance when being used for vector calculations.\n *\n * @constructor\n * @param {Number[]} [elements] - The flat list of element index and element value pairs.\n */\nlunr.Vector = function (elements) {\n this._magnitude = 0\n this.elements = elements || []\n}\n\n\n/**\n * Calculates the position within the vector to insert a given index.\n *\n * This is used internally by insert and upsert. If there are duplicate indexes then\n * the position is returned as if the value for that index were to be updated, but it\n * is the callers responsibility to check whether there is a duplicate at that index\n *\n * @param {Number} insertIdx - The index at which the element should be inserted.\n * @returns {Number}\n */\nlunr.Vector.prototype.positionForIndex = function (index) {\n // For an empty vector the tuple can be inserted at the beginning\n if (this.elements.length == 0) {\n return 0\n }\n\n var start = 0,\n end = this.elements.length / 2,\n sliceLength = end - start,\n pivotPoint = Math.floor(sliceLength / 2),\n pivotIndex = this.elements[pivotPoint * 2]\n\n while (sliceLength > 1) {\n if (pivotIndex < index) {\n start = pivotPoint\n }\n\n if (pivotIndex > index) {\n end = pivotPoint\n }\n\n if (pivotIndex == index) {\n break\n }\n\n sliceLength = end - start\n pivotPoint = start + Math.floor(sliceLength / 2)\n pivotIndex = this.elements[pivotPoint * 2]\n }\n\n if (pivotIndex == index) {\n return pivotPoint * 2\n }\n\n if (pivotIndex > index) {\n return pivotPoint * 2\n }\n\n if (pivotIndex < index) {\n return (pivotPoint + 1) * 2\n }\n}\n\n/**\n * Inserts an element at an index within the vector.\n *\n * Does not allow duplicates, will throw an error if there is already an entry\n * for this index.\n *\n * @param {Number} insertIdx - The index at which the element should be inserted.\n * @param {Number} val - The value to be inserted into the vector.\n */\nlunr.Vector.prototype.insert = function (insertIdx, val) {\n this.upsert(insertIdx, val, function () {\n throw \"duplicate index\"\n })\n}\n\n/**\n * Inserts or updates an existing index within the vector.\n *\n * @param {Number} insertIdx - The index at which the element should be inserted.\n * @param {Number} val - The value to be inserted into the vector.\n * @param {function} fn - A function that is called for updates, the existing value and the\n * requested value are passed as arguments\n */\nlunr.Vector.prototype.upsert = function (insertIdx, val, fn) {\n this._magnitude = 0\n var position = this.positionForIndex(insertIdx)\n\n if (this.elements[position] == insertIdx) {\n this.elements[position + 1] = fn(this.elements[position + 1], val)\n } else {\n this.elements.splice(position, 0, insertIdx, val)\n }\n}\n\n/**\n * Calculates the magnitude of this vector.\n *\n * @returns {Number}\n */\nlunr.Vector.prototype.magnitude = function () {\n if (this._magnitude) return this._magnitude\n\n var sumOfSquares = 0,\n elementsLength = this.elements.length\n\n for (var i = 1; i < elementsLength; i += 2) {\n var val = this.elements[i]\n sumOfSquares += val * val\n }\n\n return this._magnitude = Math.sqrt(sumOfSquares)\n}\n\n/**\n * Calculates the dot product of this vector and another vector.\n *\n * @param {lunr.Vector} otherVector - The vector to compute the dot product with.\n * @returns {Number}\n */\nlunr.Vector.prototype.dot = function (otherVector) {\n var dotProduct = 0,\n a = this.elements, b = otherVector.elements,\n aLen = a.length, bLen = b.length,\n aVal = 0, bVal = 0,\n i = 0, j = 0\n\n while (i < aLen && j < bLen) {\n aVal = a[i], bVal = b[j]\n if (aVal < bVal) {\n i += 2\n } else if (aVal > bVal) {\n j += 2\n } else if (aVal == bVal) {\n dotProduct += a[i + 1] * b[j + 1]\n i += 2\n j += 2\n }\n }\n\n return dotProduct\n}\n\n/**\n * Calculates the similarity between this vector and another vector.\n *\n * @param {lunr.Vector} otherVector - The other vector to calculate the\n * similarity with.\n * @returns {Number}\n */\nlunr.Vector.prototype.similarity = function (otherVector) {\n return this.dot(otherVector) / this.magnitude() || 0\n}\n\n/**\n * Converts the vector to an array of the elements within the vector.\n *\n * @returns {Number[]}\n */\nlunr.Vector.prototype.toArray = function () {\n var output = new Array (this.elements.length / 2)\n\n for (var i = 1, j = 0; i < this.elements.length; i += 2, j++) {\n output[j] = this.elements[i]\n }\n\n return output\n}\n\n/**\n * A JSON serializable representation of the vector.\n *\n * @returns {Number[]}\n */\nlunr.Vector.prototype.toJSON = function () {\n return this.elements\n}\n/* eslint-disable */\n/*!\n * lunr.stemmer\n * Copyright (C) 2020 Oliver Nightingale\n * Includes code from - http://tartarus.org/~martin/PorterStemmer/js.txt\n */\n\n/**\n * lunr.stemmer is an english language stemmer, this is a JavaScript\n * implementation of the PorterStemmer taken from http://tartarus.org/~martin\n *\n * @static\n * @implements {lunr.PipelineFunction}\n * @param {lunr.Token} token - The string to stem\n * @returns {lunr.Token}\n * @see {@link lunr.Pipeline}\n * @function\n */\nlunr.stemmer = (function(){\n var step2list = {\n \"ational\" : \"ate\",\n \"tional\" : \"tion\",\n \"enci\" : \"ence\",\n \"anci\" : \"ance\",\n \"izer\" : \"ize\",\n \"bli\" : \"ble\",\n \"alli\" : \"al\",\n \"entli\" : \"ent\",\n \"eli\" : \"e\",\n \"ousli\" : \"ous\",\n \"ization\" : \"ize\",\n \"ation\" : \"ate\",\n \"ator\" : \"ate\",\n \"alism\" : \"al\",\n \"iveness\" : \"ive\",\n \"fulness\" : \"ful\",\n \"ousness\" : \"ous\",\n \"aliti\" : \"al\",\n \"iviti\" : \"ive\",\n \"biliti\" : \"ble\",\n \"logi\" : \"log\"\n },\n\n step3list = {\n \"icate\" : \"ic\",\n \"ative\" : \"\",\n \"alize\" : \"al\",\n \"iciti\" : \"ic\",\n \"ical\" : \"ic\",\n \"ful\" : \"\",\n \"ness\" : \"\"\n },\n\n c = \"[^aeiou]\", // consonant\n v = \"[aeiouy]\", // vowel\n C = c + \"[^aeiouy]*\", // consonant sequence\n V = v + \"[aeiou]*\", // vowel sequence\n\n mgr0 = \"^(\" + C + \")?\" + V + C, // [C]VC... is m>0\n meq1 = \"^(\" + C + \")?\" + V + C + \"(\" + V + \")?$\", // [C]VC[V] is m=1\n mgr1 = \"^(\" + C + \")?\" + V + C + V + C, // [C]VCVC... is m>1\n s_v = \"^(\" + C + \")?\" + v; // vowel in stem\n\n var re_mgr0 = new RegExp(mgr0);\n var re_mgr1 = new RegExp(mgr1);\n var re_meq1 = new RegExp(meq1);\n var re_s_v = new RegExp(s_v);\n\n var re_1a = /^(.+?)(ss|i)es$/;\n var re2_1a = /^(.+?)([^s])s$/;\n var re_1b = /^(.+?)eed$/;\n var re2_1b = /^(.+?)(ed|ing)$/;\n var re_1b_2 = /.$/;\n var re2_1b_2 = /(at|bl|iz)$/;\n var re3_1b_2 = new RegExp(\"([^aeiouylsz])\\\\1$\");\n var re4_1b_2 = new RegExp(\"^\" + C + v + \"[^aeiouwxy]$\");\n\n var re_1c = /^(.+?[^aeiou])y$/;\n var re_2 = /^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/;\n\n var re_3 = /^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/;\n\n var re_4 = /^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/;\n var re2_4 = /^(.+?)(s|t)(ion)$/;\n\n var re_5 = /^(.+?)e$/;\n var re_5_1 = /ll$/;\n var re3_5 = new RegExp(\"^\" + C + v + \"[^aeiouwxy]$\");\n\n var porterStemmer = function porterStemmer(w) {\n var stem,\n suffix,\n firstch,\n re,\n re2,\n re3,\n re4;\n\n if (w.length < 3) { return w; }\n\n firstch = w.substr(0,1);\n if (firstch == \"y\") {\n w = firstch.toUpperCase() + w.substr(1);\n }\n\n // Step 1a\n re = re_1a\n re2 = re2_1a;\n\n if (re.test(w)) { w = w.replace(re,\"$1$2\"); }\n else if (re2.test(w)) { w = w.replace(re2,\"$1$2\"); }\n\n // Step 1b\n re = re_1b;\n re2 = re2_1b;\n if (re.test(w)) {\n var fp = re.exec(w);\n re = re_mgr0;\n if (re.test(fp[1])) {\n re = re_1b_2;\n w = w.replace(re,\"\");\n }\n } else if (re2.test(w)) {\n var fp = re2.exec(w);\n stem = fp[1];\n re2 = re_s_v;\n if (re2.test(stem)) {\n w = stem;\n re2 = re2_1b_2;\n re3 = re3_1b_2;\n re4 = re4_1b_2;\n if (re2.test(w)) { w = w + \"e\"; }\n else if (re3.test(w)) { re = re_1b_2; w = w.replace(re,\"\"); }\n else if (re4.test(w)) { w = w + \"e\"; }\n }\n }\n\n // Step 1c - replace suffix y or Y by i if preceded by a non-vowel which is not the first letter of the word (so cry -> cri, by -> by, say -> say)\n re = re_1c;\n if (re.test(w)) {\n var fp = re.exec(w);\n stem = fp[1];\n w = stem + \"i\";\n }\n\n // Step 2\n re = re_2;\n if (re.test(w)) {\n var fp = re.exec(w);\n stem = fp[1];\n suffix = fp[2];\n re = re_mgr0;\n if (re.test(stem)) {\n w = stem + step2list[suffix];\n }\n }\n\n // Step 3\n re = re_3;\n if (re.test(w)) {\n var fp = re.exec(w);\n stem = fp[1];\n suffix = fp[2];\n re = re_mgr0;\n if (re.test(stem)) {\n w = stem + step3list[suffix];\n }\n }\n\n // Step 4\n re = re_4;\n re2 = re2_4;\n if (re.test(w)) {\n var fp = re.exec(w);\n stem = fp[1];\n re = re_mgr1;\n if (re.test(stem)) {\n w = stem;\n }\n } else if (re2.test(w)) {\n var fp = re2.exec(w);\n stem = fp[1] + fp[2];\n re2 = re_mgr1;\n if (re2.test(stem)) {\n w = stem;\n }\n }\n\n // Step 5\n re = re_5;\n if (re.test(w)) {\n var fp = re.exec(w);\n stem = fp[1];\n re = re_mgr1;\n re2 = re_meq1;\n re3 = re3_5;\n if (re.test(stem) || (re2.test(stem) && !(re3.test(stem)))) {\n w = stem;\n }\n }\n\n re = re_5_1;\n re2 = re_mgr1;\n if (re.test(w) && re2.test(w)) {\n re = re_1b_2;\n w = w.replace(re,\"\");\n }\n\n // and turn initial Y back to y\n\n if (firstch == \"y\") {\n w = firstch.toLowerCase() + w.substr(1);\n }\n\n return w;\n };\n\n return function (token) {\n return token.update(porterStemmer);\n }\n})();\n\nlunr.Pipeline.registerFunction(lunr.stemmer, 'stemmer')\n/*!\n * lunr.stopWordFilter\n * Copyright (C) 2020 Oliver Nightingale\n */\n\n/**\n * lunr.generateStopWordFilter builds a stopWordFilter function from the provided\n * list of stop words.\n *\n * The built in lunr.stopWordFilter is built using this generator and can be used\n * to generate custom stopWordFilters for applications or non English languages.\n *\n * @function\n * @param {Array} token The token to pass through the filter\n * @returns {lunr.PipelineFunction}\n * @see lunr.Pipeline\n * @see lunr.stopWordFilter\n */\nlunr.generateStopWordFilter = function (stopWords) {\n var words = stopWords.reduce(function (memo, stopWord) {\n memo[stopWord] = stopWord\n return memo\n }, {})\n\n return function (token) {\n if (token && words[token.toString()] !== token.toString()) return token\n }\n}\n\n/**\n * lunr.stopWordFilter is an English language stop word list filter, any words\n * contained in the list will not be passed through the filter.\n *\n * This is intended to be used in the Pipeline. If the token does not pass the\n * filter then undefined will be returned.\n *\n * @function\n * @implements {lunr.PipelineFunction}\n * @params {lunr.Token} token - A token to check for being a stop word.\n * @returns {lunr.Token}\n * @see {@link lunr.Pipeline}\n */\nlunr.stopWordFilter = lunr.generateStopWordFilter([\n 'a',\n 'able',\n 'about',\n 'across',\n 'after',\n 'all',\n 'almost',\n 'also',\n 'am',\n 'among',\n 'an',\n 'and',\n 'any',\n 'are',\n 'as',\n 'at',\n 'be',\n 'because',\n 'been',\n 'but',\n 'by',\n 'can',\n 'cannot',\n 'could',\n 'dear',\n 'did',\n 'do',\n 'does',\n 'either',\n 'else',\n 'ever',\n 'every',\n 'for',\n 'from',\n 'get',\n 'got',\n 'had',\n 'has',\n 'have',\n 'he',\n 'her',\n 'hers',\n 'him',\n 'his',\n 'how',\n 'however',\n 'i',\n 'if',\n 'in',\n 'into',\n 'is',\n 'it',\n 'its',\n 'just',\n 'least',\n 'let',\n 'like',\n 'likely',\n 'may',\n 'me',\n 'might',\n 'most',\n 'must',\n 'my',\n 'neither',\n 'no',\n 'nor',\n 'not',\n 'of',\n 'off',\n 'often',\n 'on',\n 'only',\n 'or',\n 'other',\n 'our',\n 'own',\n 'rather',\n 'said',\n 'say',\n 'says',\n 'she',\n 'should',\n 'since',\n 'so',\n 'some',\n 'than',\n 'that',\n 'the',\n 'their',\n 'them',\n 'then',\n 'there',\n 'these',\n 'they',\n 'this',\n 'tis',\n 'to',\n 'too',\n 'twas',\n 'us',\n 'wants',\n 'was',\n 'we',\n 'were',\n 'what',\n 'when',\n 'where',\n 'which',\n 'while',\n 'who',\n 'whom',\n 'why',\n 'will',\n 'with',\n 'would',\n 'yet',\n 'you',\n 'your'\n])\n\nlunr.Pipeline.registerFunction(lunr.stopWordFilter, 'stopWordFilter')\n/*!\n * lunr.trimmer\n * Copyright (C) 2020 Oliver Nightingale\n */\n\n/**\n * lunr.trimmer is a pipeline function for trimming non word\n * characters from the beginning and end of tokens before they\n * enter the index.\n *\n * This implementation may not work correctly for non latin\n * characters and should either be removed or adapted for use\n * with languages with non-latin characters.\n *\n * @static\n * @implements {lunr.PipelineFunction}\n * @param {lunr.Token} token The token to pass through the filter\n * @returns {lunr.Token}\n * @see lunr.Pipeline\n */\nlunr.trimmer = function (token) {\n return token.update(function (s) {\n return s.replace(/^\\W+/, '').replace(/\\W+$/, '')\n })\n}\n\nlunr.Pipeline.registerFunction(lunr.trimmer, 'trimmer')\n/*!\n * lunr.TokenSet\n * Copyright (C) 2020 Oliver Nightingale\n */\n\n/**\n * A token set is used to store the unique list of all tokens\n * within an index. Token sets are also used to represent an\n * incoming query to the index, this query token set and index\n * token set are then intersected to find which tokens to look\n * up in the inverted index.\n *\n * A token set can hold multiple tokens, as in the case of the\n * index token set, or it can hold a single token as in the\n * case of a simple query token set.\n *\n * Additionally token sets are used to perform wildcard matching.\n * Leading, contained and trailing wildcards are supported, and\n * from this edit distance matching can also be provided.\n *\n * Token sets are implemented as a minimal finite state automata,\n * where both common prefixes and suffixes are shared between tokens.\n * This helps to reduce the space used for storing the token set.\n *\n * @constructor\n */\nlunr.TokenSet = function () {\n this.final = false\n this.edges = {}\n this.id = lunr.TokenSet._nextId\n lunr.TokenSet._nextId += 1\n}\n\n/**\n * Keeps track of the next, auto increment, identifier to assign\n * to a new tokenSet.\n *\n * TokenSets require a unique identifier to be correctly minimised.\n *\n * @private\n */\nlunr.TokenSet._nextId = 1\n\n/**\n * Creates a TokenSet instance from the given sorted array of words.\n *\n * @param {String[]} arr - A sorted array of strings to create the set from.\n * @returns {lunr.TokenSet}\n * @throws Will throw an error if the input array is not sorted.\n */\nlunr.TokenSet.fromArray = function (arr) {\n var builder = new lunr.TokenSet.Builder\n\n for (var i = 0, len = arr.length; i < len; i++) {\n builder.insert(arr[i])\n }\n\n builder.finish()\n return builder.root\n}\n\n/**\n * Creates a token set from a query clause.\n *\n * @private\n * @param {Object} clause - A single clause from lunr.Query.\n * @param {string} clause.term - The query clause term.\n * @param {number} [clause.editDistance] - The optional edit distance for the term.\n * @returns {lunr.TokenSet}\n */\nlunr.TokenSet.fromClause = function (clause) {\n if ('editDistance' in clause) {\n return lunr.TokenSet.fromFuzzyString(clause.term, clause.editDistance)\n } else {\n return lunr.TokenSet.fromString(clause.term)\n }\n}\n\n/**\n * Creates a token set representing a single string with a specified\n * edit distance.\n *\n * Insertions, deletions, substitutions and transpositions are each\n * treated as an edit distance of 1.\n *\n * Increasing the allowed edit distance will have a dramatic impact\n * on the performance of both creating and intersecting these TokenSets.\n * It is advised to keep the edit distance less than 3.\n *\n * @param {string} str - The string to create the token set from.\n * @param {number} editDistance - The allowed edit distance to match.\n * @returns {lunr.Vector}\n */\nlunr.TokenSet.fromFuzzyString = function (str, editDistance) {\n var root = new lunr.TokenSet\n\n var stack = [{\n node: root,\n editsRemaining: editDistance,\n str: str\n }]\n\n while (stack.length) {\n var frame = stack.pop()\n\n // no edit\n if (frame.str.length > 0) {\n var char = frame.str.charAt(0),\n noEditNode\n\n if (char in frame.node.edges) {\n noEditNode = frame.node.edges[char]\n } else {\n noEditNode = new lunr.TokenSet\n frame.node.edges[char] = noEditNode\n }\n\n if (frame.str.length == 1) {\n noEditNode.final = true\n }\n\n stack.push({\n node: noEditNode,\n editsRemaining: frame.editsRemaining,\n str: frame.str.slice(1)\n })\n }\n\n if (frame.editsRemaining == 0) {\n continue\n }\n\n // insertion\n if (\"*\" in frame.node.edges) {\n var insertionNode = frame.node.edges[\"*\"]\n } else {\n var insertionNode = new lunr.TokenSet\n frame.node.edges[\"*\"] = insertionNode\n }\n\n if (frame.str.length == 0) {\n insertionNode.final = true\n }\n\n stack.push({\n node: insertionNode,\n editsRemaining: frame.editsRemaining - 1,\n str: frame.str\n })\n\n // deletion\n // can only do a deletion if we have enough edits remaining\n // and if there are characters left to delete in the string\n if (frame.str.length > 1) {\n stack.push({\n node: frame.node,\n editsRemaining: frame.editsRemaining - 1,\n str: frame.str.slice(1)\n })\n }\n\n // deletion\n // just removing the last character from the str\n if (frame.str.length == 1) {\n frame.node.final = true\n }\n\n // substitution\n // can only do a substitution if we have enough edits remaining\n // and if there are characters left to substitute\n if (frame.str.length >= 1) {\n if (\"*\" in frame.node.edges) {\n var substitutionNode = frame.node.edges[\"*\"]\n } else {\n var substitutionNode = new lunr.TokenSet\n frame.node.edges[\"*\"] = substitutionNode\n }\n\n if (frame.str.length == 1) {\n substitutionNode.final = true\n }\n\n stack.push({\n node: substitutionNode,\n editsRemaining: frame.editsRemaining - 1,\n str: frame.str.slice(1)\n })\n }\n\n // transposition\n // can only do a transposition if there are edits remaining\n // and there are enough characters to transpose\n if (frame.str.length > 1) {\n var charA = frame.str.charAt(0),\n charB = frame.str.charAt(1),\n transposeNode\n\n if (charB in frame.node.edges) {\n transposeNode = frame.node.edges[charB]\n } else {\n transposeNode = new lunr.TokenSet\n frame.node.edges[charB] = transposeNode\n }\n\n if (frame.str.length == 1) {\n transposeNode.final = true\n }\n\n stack.push({\n node: transposeNode,\n editsRemaining: frame.editsRemaining - 1,\n str: charA + frame.str.slice(2)\n })\n }\n }\n\n return root\n}\n\n/**\n * Creates a TokenSet from a string.\n *\n * The string may contain one or more wildcard characters (*)\n * that will allow wildcard matching when intersecting with\n * another TokenSet.\n *\n * @param {string} str - The string to create a TokenSet from.\n * @returns {lunr.TokenSet}\n */\nlunr.TokenSet.fromString = function (str) {\n var node = new lunr.TokenSet,\n root = node\n\n /*\n * Iterates through all characters within the passed string\n * appending a node for each character.\n *\n * When a wildcard character is found then a self\n * referencing edge is introduced to continually match\n * any number of any characters.\n */\n for (var i = 0, len = str.length; i < len; i++) {\n var char = str[i],\n final = (i == len - 1)\n\n if (char == \"*\") {\n node.edges[char] = node\n node.final = final\n\n } else {\n var next = new lunr.TokenSet\n next.final = final\n\n node.edges[char] = next\n node = next\n }\n }\n\n return root\n}\n\n/**\n * Converts this TokenSet into an array of strings\n * contained within the TokenSet.\n *\n * This is not intended to be used on a TokenSet that\n * contains wildcards, in these cases the results are\n * undefined and are likely to cause an infinite loop.\n *\n * @returns {string[]}\n */\nlunr.TokenSet.prototype.toArray = function () {\n var words = []\n\n var stack = [{\n prefix: \"\",\n node: this\n }]\n\n while (stack.length) {\n var frame = stack.pop(),\n edges = Object.keys(frame.node.edges),\n len = edges.length\n\n if (frame.node.final) {\n /* In Safari, at this point the prefix is sometimes corrupted, see:\n * https://github.com/olivernn/lunr.js/issues/279 Calling any\n * String.prototype method forces Safari to \"cast\" this string to what\n * it's supposed to be, fixing the bug. */\n frame.prefix.charAt(0)\n words.push(frame.prefix)\n }\n\n for (var i = 0; i < len; i++) {\n var edge = edges[i]\n\n stack.push({\n prefix: frame.prefix.concat(edge),\n node: frame.node.edges[edge]\n })\n }\n }\n\n return words\n}\n\n/**\n * Generates a string representation of a TokenSet.\n *\n * This is intended to allow TokenSets to be used as keys\n * in objects, largely to aid the construction and minimisation\n * of a TokenSet. As such it is not designed to be a human\n * friendly representation of the TokenSet.\n *\n * @returns {string}\n */\nlunr.TokenSet.prototype.toString = function () {\n // NOTE: Using Object.keys here as this.edges is very likely\n // to enter 'hash-mode' with many keys being added\n //\n // avoiding a for-in loop here as it leads to the function\n // being de-optimised (at least in V8). From some simple\n // benchmarks the performance is comparable, but allowing\n // V8 to optimize may mean easy performance wins in the future.\n\n if (this._str) {\n return this._str\n }\n\n var str = this.final ? '1' : '0',\n labels = Object.keys(this.edges).sort(),\n len = labels.length\n\n for (var i = 0; i < len; i++) {\n var label = labels[i],\n node = this.edges[label]\n\n str = str + label + node.id\n }\n\n return str\n}\n\n/**\n * Returns a new TokenSet that is the intersection of\n * this TokenSet and the passed TokenSet.\n *\n * This intersection will take into account any wildcards\n * contained within the TokenSet.\n *\n * @param {lunr.TokenSet} b - An other TokenSet to intersect with.\n * @returns {lunr.TokenSet}\n */\nlunr.TokenSet.prototype.intersect = function (b) {\n var output = new lunr.TokenSet,\n frame = undefined\n\n var stack = [{\n qNode: b,\n output: output,\n node: this\n }]\n\n while (stack.length) {\n frame = stack.pop()\n\n // NOTE: As with the #toString method, we are using\n // Object.keys and a for loop instead of a for-in loop\n // as both of these objects enter 'hash' mode, causing\n // the function to be de-optimised in V8\n var qEdges = Object.keys(frame.qNode.edges),\n qLen = qEdges.length,\n nEdges = Object.keys(frame.node.edges),\n nLen = nEdges.length\n\n for (var q = 0; q < qLen; q++) {\n var qEdge = qEdges[q]\n\n for (var n = 0; n < nLen; n++) {\n var nEdge = nEdges[n]\n\n if (nEdge == qEdge || qEdge == '*') {\n var node = frame.node.edges[nEdge],\n qNode = frame.qNode.edges[qEdge],\n final = node.final && qNode.final,\n next = undefined\n\n if (nEdge in frame.output.edges) {\n // an edge already exists for this character\n // no need to create a new node, just set the finality\n // bit unless this node is already final\n next = frame.output.edges[nEdge]\n next.final = next.final || final\n\n } else {\n // no edge exists yet, must create one\n // set the finality bit and insert it\n // into the output\n next = new lunr.TokenSet\n next.final = final\n frame.output.edges[nEdge] = next\n }\n\n stack.push({\n qNode: qNode,\n output: next,\n node: node\n })\n }\n }\n }\n }\n\n return output\n}\nlunr.TokenSet.Builder = function () {\n this.previousWord = \"\"\n this.root = new lunr.TokenSet\n this.uncheckedNodes = []\n this.minimizedNodes = {}\n}\n\nlunr.TokenSet.Builder.prototype.insert = function (word) {\n var node,\n commonPrefix = 0\n\n if (word < this.previousWord) {\n throw new Error (\"Out of order word insertion\")\n }\n\n for (var i = 0; i < word.length && i < this.previousWord.length; i++) {\n if (word[i] != this.previousWord[i]) break\n commonPrefix++\n }\n\n this.minimize(commonPrefix)\n\n if (this.uncheckedNodes.length == 0) {\n node = this.root\n } else {\n node = this.uncheckedNodes[this.uncheckedNodes.length - 1].child\n }\n\n for (var i = commonPrefix; i < word.length; i++) {\n var nextNode = new lunr.TokenSet,\n char = word[i]\n\n node.edges[char] = nextNode\n\n this.uncheckedNodes.push({\n parent: node,\n char: char,\n child: nextNode\n })\n\n node = nextNode\n }\n\n node.final = true\n this.previousWord = word\n}\n\nlunr.TokenSet.Builder.prototype.finish = function () {\n this.minimize(0)\n}\n\nlunr.TokenSet.Builder.prototype.minimize = function (downTo) {\n for (var i = this.uncheckedNodes.length - 1; i >= downTo; i--) {\n var node = this.uncheckedNodes[i],\n childKey = node.child.toString()\n\n if (childKey in this.minimizedNodes) {\n node.parent.edges[node.char] = this.minimizedNodes[childKey]\n } else {\n // Cache the key for this node since\n // we know it can't change anymore\n node.child._str = childKey\n\n this.minimizedNodes[childKey] = node.child\n }\n\n this.uncheckedNodes.pop()\n }\n}\n/*!\n * lunr.Index\n * Copyright (C) 2020 Oliver Nightingale\n */\n\n/**\n * An index contains the built index of all documents and provides a query interface\n * to the index.\n *\n * Usually instances of lunr.Index will not be created using this constructor, instead\n * lunr.Builder should be used to construct new indexes, or lunr.Index.load should be\n * used to load previously built and serialized indexes.\n *\n * @constructor\n * @param {Object} attrs - The attributes of the built search index.\n * @param {Object} attrs.invertedIndex - An index of term/field to document reference.\n * @param {Object} attrs.fieldVectors - Field vectors\n * @param {lunr.TokenSet} attrs.tokenSet - An set of all corpus tokens.\n * @param {string[]} attrs.fields - The names of indexed document fields.\n * @param {lunr.Pipeline} attrs.pipeline - The pipeline to use for search terms.\n */\nlunr.Index = function (attrs) {\n this.invertedIndex = attrs.invertedIndex\n this.fieldVectors = attrs.fieldVectors\n this.tokenSet = attrs.tokenSet\n this.fields = attrs.fields\n this.pipeline = attrs.pipeline\n}\n\n/**\n * A result contains details of a document matching a search query.\n * @typedef {Object} lunr.Index~Result\n * @property {string} ref - The reference of the document this result represents.\n * @property {number} score - A number between 0 and 1 representing how similar this document is to the query.\n * @property {lunr.MatchData} matchData - Contains metadata about this match including which term(s) caused the match.\n */\n\n/**\n * Although lunr provides the ability to create queries using lunr.Query, it also provides a simple\n * query language which itself is parsed into an instance of lunr.Query.\n *\n * For programmatically building queries it is advised to directly use lunr.Query, the query language\n * is best used for human entered text rather than program generated text.\n *\n * At its simplest queries can just be a single term, e.g. `hello`, multiple terms are also supported\n * and will be combined with OR, e.g `hello world` will match documents that contain either 'hello'\n * or 'world', though those that contain both will rank higher in the results.\n *\n * Wildcards can be included in terms to match one or more unspecified characters, these wildcards can\n * be inserted anywhere within the term, and more than one wildcard can exist in a single term. Adding\n * wildcards will increase the number of documents that will be found but can also have a negative\n * impact on query performance, especially with wildcards at the beginning of a term.\n *\n * Terms can be restricted to specific fields, e.g. `title:hello`, only documents with the term\n * hello in the title field will match this query. Using a field not present in the index will lead\n * to an error being thrown.\n *\n * Modifiers can also be added to terms, lunr supports edit distance and boost modifiers on terms. A term\n * boost will make documents matching that term score higher, e.g. `foo^5`. Edit distance is also supported\n * to provide fuzzy matching, e.g. 'hello~2' will match documents with hello with an edit distance of 2.\n * Avoid large values for edit distance to improve query performance.\n *\n * Each term also supports a presence modifier. By default a term's presence in document is optional, however\n * this can be changed to either required or prohibited. For a term's presence to be required in a document the\n * term should be prefixed with a '+', e.g. `+foo bar` is a search for documents that must contain 'foo' and\n * optionally contain 'bar'. Conversely a leading '-' sets the terms presence to prohibited, i.e. it must not\n * appear in a document, e.g. `-foo bar` is a search for documents that do not contain 'foo' but may contain 'bar'.\n *\n * To escape special characters the backslash character '\\' can be used, this allows searches to include\n * characters that would normally be considered modifiers, e.g. `foo\\~2` will search for a term \"foo~2\" instead\n * of attempting to apply a boost of 2 to the search term \"foo\".\n *\n * @typedef {string} lunr.Index~QueryString\n * @example
Simple single term query
\n * hello\n * @example
Multiple term query
\n * hello world\n * @example
term scoped to a field
\n * title:hello\n * @example
term with a boost of 10
\n * hello^10\n * @example
term with an edit distance of 2
\n * hello~2\n * @example
terms with presence modifiers
\n * -foo +bar baz\n */\n\n/**\n * Performs a search against the index using lunr query syntax.\n *\n * Results will be returned sorted by their score, the most relevant results\n * will be returned first. For details on how the score is calculated, please see\n * the {@link https://lunrjs.com/guides/searching.html#scoring|guide}.\n *\n * For more programmatic querying use lunr.Index#query.\n *\n * @param {lunr.Index~QueryString} queryString - A string containing a lunr query.\n * @throws {lunr.QueryParseError} If the passed query string cannot be parsed.\n * @returns {lunr.Index~Result[]}\n */\nlunr.Index.prototype.search = function (queryString) {\n return this.query(function (query) {\n var parser = new lunr.QueryParser(queryString, query)\n parser.parse()\n })\n}\n\n/**\n * A query builder callback provides a query object to be used to express\n * the query to perform on the index.\n *\n * @callback lunr.Index~queryBuilder\n * @param {lunr.Query} query - The query object to build up.\n * @this lunr.Query\n */\n\n/**\n * Performs a query against the index using the yielded lunr.Query object.\n *\n * If performing programmatic queries against the index, this method is preferred\n * over lunr.Index#search so as to avoid the additional query parsing overhead.\n *\n * A query object is yielded to the supplied function which should be used to\n * express the query to be run against the index.\n *\n * Note that although this function takes a callback parameter it is _not_ an\n * asynchronous operation, the callback is just yielded a query object to be\n * customized.\n *\n * @param {lunr.Index~queryBuilder} fn - A function that is used to build the query.\n * @returns {lunr.Index~Result[]}\n */\nlunr.Index.prototype.query = function (fn) {\n // for each query clause\n // * process terms\n // * expand terms from token set\n // * find matching documents and metadata\n // * get document vectors\n // * score documents\n\n var query = new lunr.Query(this.fields),\n matchingFields = Object.create(null),\n queryVectors = Object.create(null),\n termFieldCache = Object.create(null),\n requiredMatches = Object.create(null),\n prohibitedMatches = Object.create(null)\n\n /*\n * To support field level boosts a query vector is created per\n * field. An empty vector is eagerly created to support negated\n * queries.\n */\n for (var i = 0; i < this.fields.length; i++) {\n queryVectors[this.fields[i]] = new lunr.Vector\n }\n\n fn.call(query, query)\n\n for (var i = 0; i < query.clauses.length; i++) {\n /*\n * Unless the pipeline has been disabled for this term, which is\n * the case for terms with wildcards, we need to pass the clause\n * term through the search pipeline. A pipeline returns an array\n * of processed terms. Pipeline functions may expand the passed\n * term, which means we may end up performing multiple index lookups\n * for a single query term.\n */\n var clause = query.clauses[i],\n terms = null,\n clauseMatches = lunr.Set.empty\n\n if (clause.usePipeline) {\n terms = this.pipeline.runString(clause.term, {\n fields: clause.fields\n })\n } else {\n terms = [clause.term]\n }\n\n for (var m = 0; m < terms.length; m++) {\n var term = terms[m]\n\n /*\n * Each term returned from the pipeline needs to use the same query\n * clause object, e.g. the same boost and or edit distance. The\n * simplest way to do this is to re-use the clause object but mutate\n * its term property.\n */\n clause.term = term\n\n /*\n * From the term in the clause we create a token set which will then\n * be used to intersect the indexes token set to get a list of terms\n * to lookup in the inverted index\n */\n var termTokenSet = lunr.TokenSet.fromClause(clause),\n expandedTerms = this.tokenSet.intersect(termTokenSet).toArray()\n\n /*\n * If a term marked as required does not exist in the tokenSet it is\n * impossible for the search to return any matches. We set all the field\n * scoped required matches set to empty and stop examining any further\n * clauses.\n */\n if (expandedTerms.length === 0 && clause.presence === lunr.Query.presence.REQUIRED) {\n for (var k = 0; k < clause.fields.length; k++) {\n var field = clause.fields[k]\n requiredMatches[field] = lunr.Set.empty\n }\n\n break\n }\n\n for (var j = 0; j < expandedTerms.length; j++) {\n /*\n * For each term get the posting and termIndex, this is required for\n * building the query vector.\n */\n var expandedTerm = expandedTerms[j],\n posting = this.invertedIndex[expandedTerm],\n termIndex = posting._index\n\n for (var k = 0; k < clause.fields.length; k++) {\n /*\n * For each field that this query term is scoped by (by default\n * all fields are in scope) we need to get all the document refs\n * that have this term in that field.\n *\n * The posting is the entry in the invertedIndex for the matching\n * term from above.\n */\n var field = clause.fields[k],\n fieldPosting = posting[field],\n matchingDocumentRefs = Object.keys(fieldPosting),\n termField = expandedTerm + \"/\" + field,\n matchingDocumentsSet = new lunr.Set(matchingDocumentRefs)\n\n /*\n * if the presence of this term is required ensure that the matching\n * documents are added to the set of required matches for this clause.\n *\n */\n if (clause.presence == lunr.Query.presence.REQUIRED) {\n clauseMatches = clauseMatches.union(matchingDocumentsSet)\n\n if (requiredMatches[field] === undefined) {\n requiredMatches[field] = lunr.Set.complete\n }\n }\n\n /*\n * if the presence of this term is prohibited ensure that the matching\n * documents are added to the set of prohibited matches for this field,\n * creating that set if it does not yet exist.\n */\n if (clause.presence == lunr.Query.presence.PROHIBITED) {\n if (prohibitedMatches[field] === undefined) {\n prohibitedMatches[field] = lunr.Set.empty\n }\n\n prohibitedMatches[field] = prohibitedMatches[field].union(matchingDocumentsSet)\n\n /*\n * Prohibited matches should not be part of the query vector used for\n * similarity scoring and no metadata should be extracted so we continue\n * to the next field\n */\n continue\n }\n\n /*\n * The query field vector is populated using the termIndex found for\n * the term and a unit value with the appropriate boost applied.\n * Using upsert because there could already be an entry in the vector\n * for the term we are working with. In that case we just add the scores\n * together.\n */\n queryVectors[field].upsert(termIndex, clause.boost, function (a, b) { return a + b })\n\n /**\n * If we've already seen this term, field combo then we've already collected\n * the matching documents and metadata, no need to go through all that again\n */\n if (termFieldCache[termField]) {\n continue\n }\n\n for (var l = 0; l < matchingDocumentRefs.length; l++) {\n /*\n * All metadata for this term/field/document triple\n * are then extracted and collected into an instance\n * of lunr.MatchData ready to be returned in the query\n * results\n */\n var matchingDocumentRef = matchingDocumentRefs[l],\n matchingFieldRef = new lunr.FieldRef (matchingDocumentRef, field),\n metadata = fieldPosting[matchingDocumentRef],\n fieldMatch\n\n if ((fieldMatch = matchingFields[matchingFieldRef]) === undefined) {\n matchingFields[matchingFieldRef] = new lunr.MatchData (expandedTerm, field, metadata)\n } else {\n fieldMatch.add(expandedTerm, field, metadata)\n }\n\n }\n\n termFieldCache[termField] = true\n }\n }\n }\n\n /**\n * If the presence was required we need to update the requiredMatches field sets.\n * We do this after all fields for the term have collected their matches because\n * the clause terms presence is required in _any_ of the fields not _all_ of the\n * fields.\n */\n if (clause.presence === lunr.Query.presence.REQUIRED) {\n for (var k = 0; k < clause.fields.length; k++) {\n var field = clause.fields[k]\n requiredMatches[field] = requiredMatches[field].intersect(clauseMatches)\n }\n }\n }\n\n /**\n * Need to combine the field scoped required and prohibited\n * matching documents into a global set of required and prohibited\n * matches\n */\n var allRequiredMatches = lunr.Set.complete,\n allProhibitedMatches = lunr.Set.empty\n\n for (var i = 0; i < this.fields.length; i++) {\n var field = this.fields[i]\n\n if (requiredMatches[field]) {\n allRequiredMatches = allRequiredMatches.intersect(requiredMatches[field])\n }\n\n if (prohibitedMatches[field]) {\n allProhibitedMatches = allProhibitedMatches.union(prohibitedMatches[field])\n }\n }\n\n var matchingFieldRefs = Object.keys(matchingFields),\n results = [],\n matches = Object.create(null)\n\n /*\n * If the query is negated (contains only prohibited terms)\n * we need to get _all_ fieldRefs currently existing in the\n * index. This is only done when we know that the query is\n * entirely prohibited terms to avoid any cost of getting all\n * fieldRefs unnecessarily.\n *\n * Additionally, blank MatchData must be created to correctly\n * populate the results.\n */\n if (query.isNegated()) {\n matchingFieldRefs = Object.keys(this.fieldVectors)\n\n for (var i = 0; i < matchingFieldRefs.length; i++) {\n var matchingFieldRef = matchingFieldRefs[i]\n var fieldRef = lunr.FieldRef.fromString(matchingFieldRef)\n matchingFields[matchingFieldRef] = new lunr.MatchData\n }\n }\n\n for (var i = 0; i < matchingFieldRefs.length; i++) {\n /*\n * Currently we have document fields that match the query, but we\n * need to return documents. The matchData and scores are combined\n * from multiple fields belonging to the same document.\n *\n * Scores are calculated by field, using the query vectors created\n * above, and combined into a final document score using addition.\n */\n var fieldRef = lunr.FieldRef.fromString(matchingFieldRefs[i]),\n docRef = fieldRef.docRef\n\n if (!allRequiredMatches.contains(docRef)) {\n continue\n }\n\n if (allProhibitedMatches.contains(docRef)) {\n continue\n }\n\n var fieldVector = this.fieldVectors[fieldRef],\n score = queryVectors[fieldRef.fieldName].similarity(fieldVector),\n docMatch\n\n if ((docMatch = matches[docRef]) !== undefined) {\n docMatch.score += score\n docMatch.matchData.combine(matchingFields[fieldRef])\n } else {\n var match = {\n ref: docRef,\n score: score,\n matchData: matchingFields[fieldRef]\n }\n matches[docRef] = match\n results.push(match)\n }\n }\n\n /*\n * Sort the results objects by score, highest first.\n */\n return results.sort(function (a, b) {\n return b.score - a.score\n })\n}\n\n/**\n * Prepares the index for JSON serialization.\n *\n * The schema for this JSON blob will be described in a\n * separate JSON schema file.\n *\n * @returns {Object}\n */\nlunr.Index.prototype.toJSON = function () {\n var invertedIndex = Object.keys(this.invertedIndex)\n .sort()\n .map(function (term) {\n return [term, this.invertedIndex[term]]\n }, this)\n\n var fieldVectors = Object.keys(this.fieldVectors)\n .map(function (ref) {\n return [ref, this.fieldVectors[ref].toJSON()]\n }, this)\n\n return {\n version: lunr.version,\n fields: this.fields,\n fieldVectors: fieldVectors,\n invertedIndex: invertedIndex,\n pipeline: this.pipeline.toJSON()\n }\n}\n\n/**\n * Loads a previously serialized lunr.Index\n *\n * @param {Object} serializedIndex - A previously serialized lunr.Index\n * @returns {lunr.Index}\n */\nlunr.Index.load = function (serializedIndex) {\n var attrs = {},\n fieldVectors = {},\n serializedVectors = serializedIndex.fieldVectors,\n invertedIndex = Object.create(null),\n serializedInvertedIndex = serializedIndex.invertedIndex,\n tokenSetBuilder = new lunr.TokenSet.Builder,\n pipeline = lunr.Pipeline.load(serializedIndex.pipeline)\n\n if (serializedIndex.version != lunr.version) {\n lunr.utils.warn(\"Version mismatch when loading serialised index. Current version of lunr '\" + lunr.version + \"' does not match serialized index '\" + serializedIndex.version + \"'\")\n }\n\n for (var i = 0; i < serializedVectors.length; i++) {\n var tuple = serializedVectors[i],\n ref = tuple[0],\n elements = tuple[1]\n\n fieldVectors[ref] = new lunr.Vector(elements)\n }\n\n for (var i = 0; i < serializedInvertedIndex.length; i++) {\n var tuple = serializedInvertedIndex[i],\n term = tuple[0],\n posting = tuple[1]\n\n tokenSetBuilder.insert(term)\n invertedIndex[term] = posting\n }\n\n tokenSetBuilder.finish()\n\n attrs.fields = serializedIndex.fields\n\n attrs.fieldVectors = fieldVectors\n attrs.invertedIndex = invertedIndex\n attrs.tokenSet = tokenSetBuilder.root\n attrs.pipeline = pipeline\n\n return new lunr.Index(attrs)\n}\n/*!\n * lunr.Builder\n * Copyright (C) 2020 Oliver Nightingale\n */\n\n/**\n * lunr.Builder performs indexing on a set of documents and\n * returns instances of lunr.Index ready for querying.\n *\n * All configuration of the index is done via the builder, the\n * fields to index, the document reference, the text processing\n * pipeline and document scoring parameters are all set on the\n * builder before indexing.\n *\n * @constructor\n * @property {string} _ref - Internal reference to the document reference field.\n * @property {string[]} _fields - Internal reference to the document fields to index.\n * @property {object} invertedIndex - The inverted index maps terms to document fields.\n * @property {object} documentTermFrequencies - Keeps track of document term frequencies.\n * @property {object} documentLengths - Keeps track of the length of documents added to the index.\n * @property {lunr.tokenizer} tokenizer - Function for splitting strings into tokens for indexing.\n * @property {lunr.Pipeline} pipeline - The pipeline performs text processing on tokens before indexing.\n * @property {lunr.Pipeline} searchPipeline - A pipeline for processing search terms before querying the index.\n * @property {number} documentCount - Keeps track of the total number of documents indexed.\n * @property {number} _b - A parameter to control field length normalization, setting this to 0 disabled normalization, 1 fully normalizes field lengths, the default value is 0.75.\n * @property {number} _k1 - A parameter to control how quickly an increase in term frequency results in term frequency saturation, the default value is 1.2.\n * @property {number} termIndex - A counter incremented for each unique term, used to identify a terms position in the vector space.\n * @property {array} metadataWhitelist - A list of metadata keys that have been whitelisted for entry in the index.\n */\nlunr.Builder = function () {\n this._ref = \"id\"\n this._fields = Object.create(null)\n this._documents = Object.create(null)\n this.invertedIndex = Object.create(null)\n this.fieldTermFrequencies = {}\n this.fieldLengths = {}\n this.tokenizer = lunr.tokenizer\n this.pipeline = new lunr.Pipeline\n this.searchPipeline = new lunr.Pipeline\n this.documentCount = 0\n this._b = 0.75\n this._k1 = 1.2\n this.termIndex = 0\n this.metadataWhitelist = []\n}\n\n/**\n * Sets the document field used as the document reference. Every document must have this field.\n * The type of this field in the document should be a string, if it is not a string it will be\n * coerced into a string by calling toString.\n *\n * The default ref is 'id'.\n *\n * The ref should _not_ be changed during indexing, it should be set before any documents are\n * added to the index. Changing it during indexing can lead to inconsistent results.\n *\n * @param {string} ref - The name of the reference field in the document.\n */\nlunr.Builder.prototype.ref = function (ref) {\n this._ref = ref\n}\n\n/**\n * A function that is used to extract a field from a document.\n *\n * Lunr expects a field to be at the top level of a document, if however the field\n * is deeply nested within a document an extractor function can be used to extract\n * the right field for indexing.\n *\n * @callback fieldExtractor\n * @param {object} doc - The document being added to the index.\n * @returns {?(string|object|object[])} obj - The object that will be indexed for this field.\n * @example
Extracting a nested field
\n * function (doc) { return doc.nested.field }\n */\n\n/**\n * Adds a field to the list of document fields that will be indexed. Every document being\n * indexed should have this field. Null values for this field in indexed documents will\n * not cause errors but will limit the chance of that document being retrieved by searches.\n *\n * All fields should be added before adding documents to the index. Adding fields after\n * a document has been indexed will have no effect on already indexed documents.\n *\n * Fields can be boosted at build time. This allows terms within that field to have more\n * importance when ranking search results. Use a field boost to specify that matches within\n * one field are more important than other fields.\n *\n * @param {string} fieldName - The name of a field to index in all documents.\n * @param {object} attributes - Optional attributes associated with this field.\n * @param {number} [attributes.boost=1] - Boost applied to all terms within this field.\n * @param {fieldExtractor} [attributes.extractor] - Function to extract a field from a document.\n * @throws {RangeError} fieldName cannot contain unsupported characters '/'\n */\nlunr.Builder.prototype.field = function (fieldName, attributes) {\n if (/\\//.test(fieldName)) {\n throw new RangeError (\"Field '\" + fieldName + \"' contains illegal character '/'\")\n }\n\n this._fields[fieldName] = attributes || {}\n}\n\n/**\n * A parameter to tune the amount of field length normalisation that is applied when\n * calculating relevance scores. A value of 0 will completely disable any normalisation\n * and a value of 1 will fully normalise field lengths. The default is 0.75. Values of b\n * will be clamped to the range 0 - 1.\n *\n * @param {number} number - The value to set for this tuning parameter.\n */\nlunr.Builder.prototype.b = function (number) {\n if (number < 0) {\n this._b = 0\n } else if (number > 1) {\n this._b = 1\n } else {\n this._b = number\n }\n}\n\n/**\n * A parameter that controls the speed at which a rise in term frequency results in term\n * frequency saturation. The default value is 1.2. Setting this to a higher value will give\n * slower saturation levels, a lower value will result in quicker saturation.\n *\n * @param {number} number - The value to set for this tuning parameter.\n */\nlunr.Builder.prototype.k1 = function (number) {\n this._k1 = number\n}\n\n/**\n * Adds a document to the index.\n *\n * Before adding fields to the index the index should have been fully setup, with the document\n * ref and all fields to index already having been specified.\n *\n * The document must have a field name as specified by the ref (by default this is 'id') and\n * it should have all fields defined for indexing, though null or undefined values will not\n * cause errors.\n *\n * Entire documents can be boosted at build time. Applying a boost to a document indicates that\n * this document should rank higher in search results than other documents.\n *\n * @param {object} doc - The document to add to the index.\n * @param {object} attributes - Optional attributes associated with this document.\n * @param {number} [attributes.boost=1] - Boost applied to all terms within this document.\n */\nlunr.Builder.prototype.add = function (doc, attributes) {\n var docRef = doc[this._ref],\n fields = Object.keys(this._fields)\n\n this._documents[docRef] = attributes || {}\n this.documentCount += 1\n\n for (var i = 0; i < fields.length; i++) {\n var fieldName = fields[i],\n extractor = this._fields[fieldName].extractor,\n field = extractor ? extractor(doc) : doc[fieldName],\n tokens = this.tokenizer(field, {\n fields: [fieldName]\n }),\n terms = this.pipeline.run(tokens),\n fieldRef = new lunr.FieldRef (docRef, fieldName),\n fieldTerms = Object.create(null)\n\n this.fieldTermFrequencies[fieldRef] = fieldTerms\n this.fieldLengths[fieldRef] = 0\n\n // store the length of this field for this document\n this.fieldLengths[fieldRef] += terms.length\n\n // calculate term frequencies for this field\n for (var j = 0; j < terms.length; j++) {\n var term = terms[j]\n\n if (fieldTerms[term] == undefined) {\n fieldTerms[term] = 0\n }\n\n fieldTerms[term] += 1\n\n // add to inverted index\n // create an initial posting if one doesn't exist\n if (this.invertedIndex[term] == undefined) {\n var posting = Object.create(null)\n posting[\"_index\"] = this.termIndex\n this.termIndex += 1\n\n for (var k = 0; k < fields.length; k++) {\n posting[fields[k]] = Object.create(null)\n }\n\n this.invertedIndex[term] = posting\n }\n\n // add an entry for this term/fieldName/docRef to the invertedIndex\n if (this.invertedIndex[term][fieldName][docRef] == undefined) {\n this.invertedIndex[term][fieldName][docRef] = Object.create(null)\n }\n\n // store all whitelisted metadata about this token in the\n // inverted index\n for (var l = 0; l < this.metadataWhitelist.length; l++) {\n var metadataKey = this.metadataWhitelist[l],\n metadata = term.metadata[metadataKey]\n\n if (this.invertedIndex[term][fieldName][docRef][metadataKey] == undefined) {\n this.invertedIndex[term][fieldName][docRef][metadataKey] = []\n }\n\n this.invertedIndex[term][fieldName][docRef][metadataKey].push(metadata)\n }\n }\n\n }\n}\n\n/**\n * Calculates the average document length for this index\n *\n * @private\n */\nlunr.Builder.prototype.calculateAverageFieldLengths = function () {\n\n var fieldRefs = Object.keys(this.fieldLengths),\n numberOfFields = fieldRefs.length,\n accumulator = {},\n documentsWithField = {}\n\n for (var i = 0; i < numberOfFields; i++) {\n var fieldRef = lunr.FieldRef.fromString(fieldRefs[i]),\n field = fieldRef.fieldName\n\n documentsWithField[field] || (documentsWithField[field] = 0)\n documentsWithField[field] += 1\n\n accumulator[field] || (accumulator[field] = 0)\n accumulator[field] += this.fieldLengths[fieldRef]\n }\n\n var fields = Object.keys(this._fields)\n\n for (var i = 0; i < fields.length; i++) {\n var fieldName = fields[i]\n accumulator[fieldName] = accumulator[fieldName] / documentsWithField[fieldName]\n }\n\n this.averageFieldLength = accumulator\n}\n\n/**\n * Builds a vector space model of every document using lunr.Vector\n *\n * @private\n */\nlunr.Builder.prototype.createFieldVectors = function () {\n var fieldVectors = {},\n fieldRefs = Object.keys(this.fieldTermFrequencies),\n fieldRefsLength = fieldRefs.length,\n termIdfCache = Object.create(null)\n\n for (var i = 0; i < fieldRefsLength; i++) {\n var fieldRef = lunr.FieldRef.fromString(fieldRefs[i]),\n fieldName = fieldRef.fieldName,\n fieldLength = this.fieldLengths[fieldRef],\n fieldVector = new lunr.Vector,\n termFrequencies = this.fieldTermFrequencies[fieldRef],\n terms = Object.keys(termFrequencies),\n termsLength = terms.length\n\n\n var fieldBoost = this._fields[fieldName].boost || 1,\n docBoost = this._documents[fieldRef.docRef].boost || 1\n\n for (var j = 0; j < termsLength; j++) {\n var term = terms[j],\n tf = termFrequencies[term],\n termIndex = this.invertedIndex[term]._index,\n idf, score, scoreWithPrecision\n\n if (termIdfCache[term] === undefined) {\n idf = lunr.idf(this.invertedIndex[term], this.documentCount)\n termIdfCache[term] = idf\n } else {\n idf = termIdfCache[term]\n }\n\n score = idf * ((this._k1 + 1) * tf) / (this._k1 * (1 - this._b + this._b * (fieldLength / this.averageFieldLength[fieldName])) + tf)\n score *= fieldBoost\n score *= docBoost\n scoreWithPrecision = Math.round(score * 1000) / 1000\n // Converts 1.23456789 to 1.234.\n // Reducing the precision so that the vectors take up less\n // space when serialised. Doing it now so that they behave\n // the same before and after serialisation. Also, this is\n // the fastest approach to reducing a number's precision in\n // JavaScript.\n\n fieldVector.insert(termIndex, scoreWithPrecision)\n }\n\n fieldVectors[fieldRef] = fieldVector\n }\n\n this.fieldVectors = fieldVectors\n}\n\n/**\n * Creates a token set of all tokens in the index using lunr.TokenSet\n *\n * @private\n */\nlunr.Builder.prototype.createTokenSet = function () {\n this.tokenSet = lunr.TokenSet.fromArray(\n Object.keys(this.invertedIndex).sort()\n )\n}\n\n/**\n * Builds the index, creating an instance of lunr.Index.\n *\n * This completes the indexing process and should only be called\n * once all documents have been added to the index.\n *\n * @returns {lunr.Index}\n */\nlunr.Builder.prototype.build = function () {\n this.calculateAverageFieldLengths()\n this.createFieldVectors()\n this.createTokenSet()\n\n return new lunr.Index({\n invertedIndex: this.invertedIndex,\n fieldVectors: this.fieldVectors,\n tokenSet: this.tokenSet,\n fields: Object.keys(this._fields),\n pipeline: this.searchPipeline\n })\n}\n\n/**\n * Applies a plugin to the index builder.\n *\n * A plugin is a function that is called with the index builder as its context.\n * Plugins can be used to customise or extend the behaviour of the index\n * in some way. A plugin is just a function, that encapsulated the custom\n * behaviour that should be applied when building the index.\n *\n * The plugin function will be called with the index builder as its argument, additional\n * arguments can also be passed when calling use. The function will be called\n * with the index builder as its context.\n *\n * @param {Function} plugin The plugin to apply.\n */\nlunr.Builder.prototype.use = function (fn) {\n var args = Array.prototype.slice.call(arguments, 1)\n args.unshift(this)\n fn.apply(this, args)\n}\n/**\n * Contains and collects metadata about a matching document.\n * A single instance of lunr.MatchData is returned as part of every\n * lunr.Index~Result.\n *\n * @constructor\n * @param {string} term - The term this match data is associated with\n * @param {string} field - The field in which the term was found\n * @param {object} metadata - The metadata recorded about this term in this field\n * @property {object} metadata - A cloned collection of metadata associated with this document.\n * @see {@link lunr.Index~Result}\n */\nlunr.MatchData = function (term, field, metadata) {\n var clonedMetadata = Object.create(null),\n metadataKeys = Object.keys(metadata || {})\n\n // Cloning the metadata to prevent the original\n // being mutated during match data combination.\n // Metadata is kept in an array within the inverted\n // index so cloning the data can be done with\n // Array#slice\n for (var i = 0; i < metadataKeys.length; i++) {\n var key = metadataKeys[i]\n clonedMetadata[key] = metadata[key].slice()\n }\n\n this.metadata = Object.create(null)\n\n if (term !== undefined) {\n this.metadata[term] = Object.create(null)\n this.metadata[term][field] = clonedMetadata\n }\n}\n\n/**\n * An instance of lunr.MatchData will be created for every term that matches a\n * document. However only one instance is required in a lunr.Index~Result. This\n * method combines metadata from another instance of lunr.MatchData with this\n * objects metadata.\n *\n * @param {lunr.MatchData} otherMatchData - Another instance of match data to merge with this one.\n * @see {@link lunr.Index~Result}\n */\nlunr.MatchData.prototype.combine = function (otherMatchData) {\n var terms = Object.keys(otherMatchData.metadata)\n\n for (var i = 0; i < terms.length; i++) {\n var term = terms[i],\n fields = Object.keys(otherMatchData.metadata[term])\n\n if (this.metadata[term] == undefined) {\n this.metadata[term] = Object.create(null)\n }\n\n for (var j = 0; j < fields.length; j++) {\n var field = fields[j],\n keys = Object.keys(otherMatchData.metadata[term][field])\n\n if (this.metadata[term][field] == undefined) {\n this.metadata[term][field] = Object.create(null)\n }\n\n for (var k = 0; k < keys.length; k++) {\n var key = keys[k]\n\n if (this.metadata[term][field][key] == undefined) {\n this.metadata[term][field][key] = otherMatchData.metadata[term][field][key]\n } else {\n this.metadata[term][field][key] = this.metadata[term][field][key].concat(otherMatchData.metadata[term][field][key])\n }\n\n }\n }\n }\n}\n\n/**\n * Add metadata for a term/field pair to this instance of match data.\n *\n * @param {string} term - The term this match data is associated with\n * @param {string} field - The field in which the term was found\n * @param {object} metadata - The metadata recorded about this term in this field\n */\nlunr.MatchData.prototype.add = function (term, field, metadata) {\n if (!(term in this.metadata)) {\n this.metadata[term] = Object.create(null)\n this.metadata[term][field] = metadata\n return\n }\n\n if (!(field in this.metadata[term])) {\n this.metadata[term][field] = metadata\n return\n }\n\n var metadataKeys = Object.keys(metadata)\n\n for (var i = 0; i < metadataKeys.length; i++) {\n var key = metadataKeys[i]\n\n if (key in this.metadata[term][field]) {\n this.metadata[term][field][key] = this.metadata[term][field][key].concat(metadata[key])\n } else {\n this.metadata[term][field][key] = metadata[key]\n }\n }\n}\n/**\n * A lunr.Query provides a programmatic way of defining queries to be performed\n * against a {@link lunr.Index}.\n *\n * Prefer constructing a lunr.Query using the {@link lunr.Index#query} method\n * so the query object is pre-initialized with the right index fields.\n *\n * @constructor\n * @property {lunr.Query~Clause[]} clauses - An array of query clauses.\n * @property {string[]} allFields - An array of all available fields in a lunr.Index.\n */\nlunr.Query = function (allFields) {\n this.clauses = []\n this.allFields = allFields\n}\n\n/**\n * Constants for indicating what kind of automatic wildcard insertion will be used when constructing a query clause.\n *\n * This allows wildcards to be added to the beginning and end of a term without having to manually do any string\n * concatenation.\n *\n * The wildcard constants can be bitwise combined to select both leading and trailing wildcards.\n *\n * @constant\n * @default\n * @property {number} wildcard.NONE - The term will have no wildcards inserted, this is the default behaviour\n * @property {number} wildcard.LEADING - Prepend the term with a wildcard, unless a leading wildcard already exists\n * @property {number} wildcard.TRAILING - Append a wildcard to the term, unless a trailing wildcard already exists\n * @see lunr.Query~Clause\n * @see lunr.Query#clause\n * @see lunr.Query#term\n * @example
\n * query.term('foo', {\n * wildcard: lunr.Query.wildcard.LEADING | lunr.Query.wildcard.TRAILING\n * })\n */\n\nlunr.Query.wildcard = new String (\"*\")\nlunr.Query.wildcard.NONE = 0\nlunr.Query.wildcard.LEADING = 1\nlunr.Query.wildcard.TRAILING = 2\n\n/**\n * Constants for indicating what kind of presence a term must have in matching documents.\n *\n * @constant\n * @enum {number}\n * @see lunr.Query~Clause\n * @see lunr.Query#clause\n * @see lunr.Query#term\n * @example
query term with required presence
\n * query.term('foo', { presence: lunr.Query.presence.REQUIRED })\n */\nlunr.Query.presence = {\n /**\n * Term's presence in a document is optional, this is the default value.\n */\n OPTIONAL: 1,\n\n /**\n * Term's presence in a document is required, documents that do not contain\n * this term will not be returned.\n */\n REQUIRED: 2,\n\n /**\n * Term's presence in a document is prohibited, documents that do contain\n * this term will not be returned.\n */\n PROHIBITED: 3\n}\n\n/**\n * A single clause in a {@link lunr.Query} contains a term and details on how to\n * match that term against a {@link lunr.Index}.\n *\n * @typedef {Object} lunr.Query~Clause\n * @property {string[]} fields - The fields in an index this clause should be matched against.\n * @property {number} [boost=1] - Any boost that should be applied when matching this clause.\n * @property {number} [editDistance] - Whether the term should have fuzzy matching applied, and how fuzzy the match should be.\n * @property {boolean} [usePipeline] - Whether the term should be passed through the search pipeline.\n * @property {number} [wildcard=lunr.Query.wildcard.NONE] - Whether the term should have wildcards appended or prepended.\n * @property {number} [presence=lunr.Query.presence.OPTIONAL] - The terms presence in any matching documents.\n */\n\n/**\n * Adds a {@link lunr.Query~Clause} to this query.\n *\n * Unless the clause contains the fields to be matched all fields will be matched. In addition\n * a default boost of 1 is applied to the clause.\n *\n * @param {lunr.Query~Clause} clause - The clause to add to this query.\n * @see lunr.Query~Clause\n * @returns {lunr.Query}\n */\nlunr.Query.prototype.clause = function (clause) {\n if (!('fields' in clause)) {\n clause.fields = this.allFields\n }\n\n if (!('boost' in clause)) {\n clause.boost = 1\n }\n\n if (!('usePipeline' in clause)) {\n clause.usePipeline = true\n }\n\n if (!('wildcard' in clause)) {\n clause.wildcard = lunr.Query.wildcard.NONE\n }\n\n if ((clause.wildcard & lunr.Query.wildcard.LEADING) && (clause.term.charAt(0) != lunr.Query.wildcard)) {\n clause.term = \"*\" + clause.term\n }\n\n if ((clause.wildcard & lunr.Query.wildcard.TRAILING) && (clause.term.slice(-1) != lunr.Query.wildcard)) {\n clause.term = \"\" + clause.term + \"*\"\n }\n\n if (!('presence' in clause)) {\n clause.presence = lunr.Query.presence.OPTIONAL\n }\n\n this.clauses.push(clause)\n\n return this\n}\n\n/**\n * A negated query is one in which every clause has a presence of\n * prohibited. These queries require some special processing to return\n * the expected results.\n *\n * @returns boolean\n */\nlunr.Query.prototype.isNegated = function () {\n for (var i = 0; i < this.clauses.length; i++) {\n if (this.clauses[i].presence != lunr.Query.presence.PROHIBITED) {\n return false\n }\n }\n\n return true\n}\n\n/**\n * Adds a term to the current query, under the covers this will create a {@link lunr.Query~Clause}\n * to the list of clauses that make up this query.\n *\n * The term is used as is, i.e. no tokenization will be performed by this method. Instead conversion\n * to a token or token-like string should be done before calling this method.\n *\n * The term will be converted to a string by calling `toString`. Multiple terms can be passed as an\n * array, each term in the array will share the same options.\n *\n * @param {object|object[]} term - The term(s) to add to the query.\n * @param {object} [options] - Any additional properties to add to the query clause.\n * @returns {lunr.Query}\n * @see lunr.Query#clause\n * @see lunr.Query~Clause\n * @example
adding a single term to a query
\n * query.term(\"foo\")\n * @example
adding a single term to a query and specifying search fields, term boost and automatic trailing wildcard
using lunr.tokenizer to convert a string to tokens before using them as terms
\n * query.term(lunr.tokenizer(\"foo bar\"))\n */\nlunr.Query.prototype.term = function (term, options) {\n if (Array.isArray(term)) {\n term.forEach(function (t) { this.term(t, lunr.utils.clone(options)) }, this)\n return this\n }\n\n var clause = options || {}\n clause.term = term.toString()\n\n this.clause(clause)\n\n return this\n}\nlunr.QueryParseError = function (message, start, end) {\n this.name = \"QueryParseError\"\n this.message = message\n this.start = start\n this.end = end\n}\n\nlunr.QueryParseError.prototype = new Error\nlunr.QueryLexer = function (str) {\n this.lexemes = []\n this.str = str\n this.length = str.length\n this.pos = 0\n this.start = 0\n this.escapeCharPositions = []\n}\n\nlunr.QueryLexer.prototype.run = function () {\n var state = lunr.QueryLexer.lexText\n\n while (state) {\n state = state(this)\n }\n}\n\nlunr.QueryLexer.prototype.sliceString = function () {\n var subSlices = [],\n sliceStart = this.start,\n sliceEnd = this.pos\n\n for (var i = 0; i < this.escapeCharPositions.length; i++) {\n sliceEnd = this.escapeCharPositions[i]\n subSlices.push(this.str.slice(sliceStart, sliceEnd))\n sliceStart = sliceEnd + 1\n }\n\n subSlices.push(this.str.slice(sliceStart, this.pos))\n this.escapeCharPositions.length = 0\n\n return subSlices.join('')\n}\n\nlunr.QueryLexer.prototype.emit = function (type) {\n this.lexemes.push({\n type: type,\n str: this.sliceString(),\n start: this.start,\n end: this.pos\n })\n\n this.start = this.pos\n}\n\nlunr.QueryLexer.prototype.escapeCharacter = function () {\n this.escapeCharPositions.push(this.pos - 1)\n this.pos += 1\n}\n\nlunr.QueryLexer.prototype.next = function () {\n if (this.pos >= this.length) {\n return lunr.QueryLexer.EOS\n }\n\n var char = this.str.charAt(this.pos)\n this.pos += 1\n return char\n}\n\nlunr.QueryLexer.prototype.width = function () {\n return this.pos - this.start\n}\n\nlunr.QueryLexer.prototype.ignore = function () {\n if (this.start == this.pos) {\n this.pos += 1\n }\n\n this.start = this.pos\n}\n\nlunr.QueryLexer.prototype.backup = function () {\n this.pos -= 1\n}\n\nlunr.QueryLexer.prototype.acceptDigitRun = function () {\n var char, charCode\n\n do {\n char = this.next()\n charCode = char.charCodeAt(0)\n } while (charCode > 47 && charCode < 58)\n\n if (char != lunr.QueryLexer.EOS) {\n this.backup()\n }\n}\n\nlunr.QueryLexer.prototype.more = function () {\n return this.pos < this.length\n}\n\nlunr.QueryLexer.EOS = 'EOS'\nlunr.QueryLexer.FIELD = 'FIELD'\nlunr.QueryLexer.TERM = 'TERM'\nlunr.QueryLexer.EDIT_DISTANCE = 'EDIT_DISTANCE'\nlunr.QueryLexer.BOOST = 'BOOST'\nlunr.QueryLexer.PRESENCE = 'PRESENCE'\n\nlunr.QueryLexer.lexField = function (lexer) {\n lexer.backup()\n lexer.emit(lunr.QueryLexer.FIELD)\n lexer.ignore()\n return lunr.QueryLexer.lexText\n}\n\nlunr.QueryLexer.lexTerm = function (lexer) {\n if (lexer.width() > 1) {\n lexer.backup()\n lexer.emit(lunr.QueryLexer.TERM)\n }\n\n lexer.ignore()\n\n if (lexer.more()) {\n return lunr.QueryLexer.lexText\n }\n}\n\nlunr.QueryLexer.lexEditDistance = function (lexer) {\n lexer.ignore()\n lexer.acceptDigitRun()\n lexer.emit(lunr.QueryLexer.EDIT_DISTANCE)\n return lunr.QueryLexer.lexText\n}\n\nlunr.QueryLexer.lexBoost = function (lexer) {\n lexer.ignore()\n lexer.acceptDigitRun()\n lexer.emit(lunr.QueryLexer.BOOST)\n return lunr.QueryLexer.lexText\n}\n\nlunr.QueryLexer.lexEOS = function (lexer) {\n if (lexer.width() > 0) {\n lexer.emit(lunr.QueryLexer.TERM)\n }\n}\n\n// This matches the separator used when tokenising fields\n// within a document. These should match otherwise it is\n// not possible to search for some tokens within a document.\n//\n// It is possible for the user to change the separator on the\n// tokenizer so it _might_ clash with any other of the special\n// characters already used within the search string, e.g. :.\n//\n// This means that it is possible to change the separator in\n// such a way that makes some words unsearchable using a search\n// string.\nlunr.QueryLexer.termSeparator = lunr.tokenizer.separator\n\nlunr.QueryLexer.lexText = function (lexer) {\n while (true) {\n var char = lexer.next()\n\n if (char == lunr.QueryLexer.EOS) {\n return lunr.QueryLexer.lexEOS\n }\n\n // Escape character is '\\'\n if (char.charCodeAt(0) == 92) {\n lexer.escapeCharacter()\n continue\n }\n\n if (char == \":\") {\n return lunr.QueryLexer.lexField\n }\n\n if (char == \"~\") {\n lexer.backup()\n if (lexer.width() > 0) {\n lexer.emit(lunr.QueryLexer.TERM)\n }\n return lunr.QueryLexer.lexEditDistance\n }\n\n if (char == \"^\") {\n lexer.backup()\n if (lexer.width() > 0) {\n lexer.emit(lunr.QueryLexer.TERM)\n }\n return lunr.QueryLexer.lexBoost\n }\n\n // \"+\" indicates term presence is required\n // checking for length to ensure that only\n // leading \"+\" are considered\n if (char == \"+\" && lexer.width() === 1) {\n lexer.emit(lunr.QueryLexer.PRESENCE)\n return lunr.QueryLexer.lexText\n }\n\n // \"-\" indicates term presence is prohibited\n // checking for length to ensure that only\n // leading \"-\" are considered\n if (char == \"-\" && lexer.width() === 1) {\n lexer.emit(lunr.QueryLexer.PRESENCE)\n return lunr.QueryLexer.lexText\n }\n\n if (char.match(lunr.QueryLexer.termSeparator)) {\n return lunr.QueryLexer.lexTerm\n }\n }\n}\n\nlunr.QueryParser = function (str, query) {\n this.lexer = new lunr.QueryLexer (str)\n this.query = query\n this.currentClause = {}\n this.lexemeIdx = 0\n}\n\nlunr.QueryParser.prototype.parse = function () {\n this.lexer.run()\n this.lexemes = this.lexer.lexemes\n\n var state = lunr.QueryParser.parseClause\n\n while (state) {\n state = state(this)\n }\n\n return this.query\n}\n\nlunr.QueryParser.prototype.peekLexeme = function () {\n return this.lexemes[this.lexemeIdx]\n}\n\nlunr.QueryParser.prototype.consumeLexeme = function () {\n var lexeme = this.peekLexeme()\n this.lexemeIdx += 1\n return lexeme\n}\n\nlunr.QueryParser.prototype.nextClause = function () {\n var completedClause = this.currentClause\n this.query.clause(completedClause)\n this.currentClause = {}\n}\n\nlunr.QueryParser.parseClause = function (parser) {\n var lexeme = parser.peekLexeme()\n\n if (lexeme == undefined) {\n return\n }\n\n switch (lexeme.type) {\n case lunr.QueryLexer.PRESENCE:\n return lunr.QueryParser.parsePresence\n case lunr.QueryLexer.FIELD:\n return lunr.QueryParser.parseField\n case lunr.QueryLexer.TERM:\n return lunr.QueryParser.parseTerm\n default:\n var errorMessage = \"expected either a field or a term, found \" + lexeme.type\n\n if (lexeme.str.length >= 1) {\n errorMessage += \" with value '\" + lexeme.str + \"'\"\n }\n\n throw new lunr.QueryParseError (errorMessage, lexeme.start, lexeme.end)\n }\n}\n\nlunr.QueryParser.parsePresence = function (parser) {\n var lexeme = parser.consumeLexeme()\n\n if (lexeme == undefined) {\n return\n }\n\n switch (lexeme.str) {\n case \"-\":\n parser.currentClause.presence = lunr.Query.presence.PROHIBITED\n break\n case \"+\":\n parser.currentClause.presence = lunr.Query.presence.REQUIRED\n break\n default:\n var errorMessage = \"unrecognised presence operator'\" + lexeme.str + \"'\"\n throw new lunr.QueryParseError (errorMessage, lexeme.start, lexeme.end)\n }\n\n var nextLexeme = parser.peekLexeme()\n\n if (nextLexeme == undefined) {\n var errorMessage = \"expecting term or field, found nothing\"\n throw new lunr.QueryParseError (errorMessage, lexeme.start, lexeme.end)\n }\n\n switch (nextLexeme.type) {\n case lunr.QueryLexer.FIELD:\n return lunr.QueryParser.parseField\n case lunr.QueryLexer.TERM:\n return lunr.QueryParser.parseTerm\n default:\n var errorMessage = \"expecting term or field, found '\" + nextLexeme.type + \"'\"\n throw new lunr.QueryParseError (errorMessage, nextLexeme.start, nextLexeme.end)\n }\n}\n\nlunr.QueryParser.parseField = function (parser) {\n var lexeme = parser.consumeLexeme()\n\n if (lexeme == undefined) {\n return\n }\n\n if (parser.query.allFields.indexOf(lexeme.str) == -1) {\n var possibleFields = parser.query.allFields.map(function (f) { return \"'\" + f + \"'\" }).join(', '),\n errorMessage = \"unrecognised field '\" + lexeme.str + \"', possible fields: \" + possibleFields\n\n throw new lunr.QueryParseError (errorMessage, lexeme.start, lexeme.end)\n }\n\n parser.currentClause.fields = [lexeme.str]\n\n var nextLexeme = parser.peekLexeme()\n\n if (nextLexeme == undefined) {\n var errorMessage = \"expecting term, found nothing\"\n throw new lunr.QueryParseError (errorMessage, lexeme.start, lexeme.end)\n }\n\n switch (nextLexeme.type) {\n case lunr.QueryLexer.TERM:\n return lunr.QueryParser.parseTerm\n default:\n var errorMessage = \"expecting term, found '\" + nextLexeme.type + \"'\"\n throw new lunr.QueryParseError (errorMessage, nextLexeme.start, nextLexeme.end)\n }\n}\n\nlunr.QueryParser.parseTerm = function (parser) {\n var lexeme = parser.consumeLexeme()\n\n if (lexeme == undefined) {\n return\n }\n\n parser.currentClause.term = lexeme.str.toLowerCase()\n\n if (lexeme.str.indexOf(\"*\") != -1) {\n parser.currentClause.usePipeline = false\n }\n\n var nextLexeme = parser.peekLexeme()\n\n if (nextLexeme == undefined) {\n parser.nextClause()\n return\n }\n\n switch (nextLexeme.type) {\n case lunr.QueryLexer.TERM:\n parser.nextClause()\n return lunr.QueryParser.parseTerm\n case lunr.QueryLexer.FIELD:\n parser.nextClause()\n return lunr.QueryParser.parseField\n case lunr.QueryLexer.EDIT_DISTANCE:\n return lunr.QueryParser.parseEditDistance\n case lunr.QueryLexer.BOOST:\n return lunr.QueryParser.parseBoost\n case lunr.QueryLexer.PRESENCE:\n parser.nextClause()\n return lunr.QueryParser.parsePresence\n default:\n var errorMessage = \"Unexpected lexeme type '\" + nextLexeme.type + \"'\"\n throw new lunr.QueryParseError (errorMessage, nextLexeme.start, nextLexeme.end)\n }\n}\n\nlunr.QueryParser.parseEditDistance = function (parser) {\n var lexeme = parser.consumeLexeme()\n\n if (lexeme == undefined) {\n return\n }\n\n var editDistance = parseInt(lexeme.str, 10)\n\n if (isNaN(editDistance)) {\n var errorMessage = \"edit distance must be numeric\"\n throw new lunr.QueryParseError (errorMessage, lexeme.start, lexeme.end)\n }\n\n parser.currentClause.editDistance = editDistance\n\n var nextLexeme = parser.peekLexeme()\n\n if (nextLexeme == undefined) {\n parser.nextClause()\n return\n }\n\n switch (nextLexeme.type) {\n case lunr.QueryLexer.TERM:\n parser.nextClause()\n return lunr.QueryParser.parseTerm\n case lunr.QueryLexer.FIELD:\n parser.nextClause()\n return lunr.QueryParser.parseField\n case lunr.QueryLexer.EDIT_DISTANCE:\n return lunr.QueryParser.parseEditDistance\n case lunr.QueryLexer.BOOST:\n return lunr.QueryParser.parseBoost\n case lunr.QueryLexer.PRESENCE:\n parser.nextClause()\n return lunr.QueryParser.parsePresence\n default:\n var errorMessage = \"Unexpected lexeme type '\" + nextLexeme.type + \"'\"\n throw new lunr.QueryParseError (errorMessage, nextLexeme.start, nextLexeme.end)\n }\n}\n\nlunr.QueryParser.parseBoost = function (parser) {\n var lexeme = parser.consumeLexeme()\n\n if (lexeme == undefined) {\n return\n }\n\n var boost = parseInt(lexeme.str, 10)\n\n if (isNaN(boost)) {\n var errorMessage = \"boost must be numeric\"\n throw new lunr.QueryParseError (errorMessage, lexeme.start, lexeme.end)\n }\n\n parser.currentClause.boost = boost\n\n var nextLexeme = parser.peekLexeme()\n\n if (nextLexeme == undefined) {\n parser.nextClause()\n return\n }\n\n switch (nextLexeme.type) {\n case lunr.QueryLexer.TERM:\n parser.nextClause()\n return lunr.QueryParser.parseTerm\n case lunr.QueryLexer.FIELD:\n parser.nextClause()\n return lunr.QueryParser.parseField\n case lunr.QueryLexer.EDIT_DISTANCE:\n return lunr.QueryParser.parseEditDistance\n case lunr.QueryLexer.BOOST:\n return lunr.QueryParser.parseBoost\n case lunr.QueryLexer.PRESENCE:\n parser.nextClause()\n return lunr.QueryParser.parsePresence\n default:\n var errorMessage = \"Unexpected lexeme type '\" + nextLexeme.type + \"'\"\n throw new lunr.QueryParseError (errorMessage, nextLexeme.start, nextLexeme.end)\n }\n}\n\n /**\n * export the module via AMD, CommonJS or as a browser global\n * Export code from https://github.com/umdjs/umd/blob/master/returnExports.js\n */\n ;(function (root, factory) {\n if (typeof define === 'function' && define.amd) {\n // AMD. Register as an anonymous module.\n define(factory)\n } else if (typeof exports === 'object') {\n /**\n * Node. Does not work with strict CommonJS, but\n * only CommonJS-like enviroments that support module.exports,\n * like Node.\n */\n module.exports = factory()\n } else {\n // Browser globals (root is window)\n root.lunr = factory()\n }\n }(this, function () {\n /**\n * Just return a value to define the module export.\n * This example returns an object, but the module\n * can return a function as the exported value.\n */\n return lunr\n }))\n})();\n"],"names":["global","step2list","step3list","v","C","V","meq1","mgr1","s_v","re_mgr0","re_mgr1","re_meq1","re_s_v","re_1a","re2_1a","re_1b","re2_1b","re_1b_2","re2_1b_2","re3_1b_2","re4_1b_2","re_1c","re_2","re_3","re_4","re2_4","re_5","re_5_1","re3_5","porterStemmer","root","factory","lunr","config","builder","Builder","pipeline","add","trimmer","stopWordFilter","stemmer","searchPipeline","call","build","version","utils","warn","this","message","console","asString","obj","toString","clone","Object","create","keys","i","length","key","val","Array","isArray","slice","TypeError","FieldRef","docRef","fieldName","stringValue","_stringValue","joiner","fromString","s","n","indexOf","fieldRef","prototype","undefined","Set","elements","complete","intersect","other","union","contains","empty","object","a","b","intersection","element","push","concat","idf","posting","documentCount","documentsWithTerm","x","Math","log","abs","Token","str","metadata","update","fn","tokenizer","map","t","toLowerCase","len","tokens","sliceEnd","sliceStart","sliceLength","charAt","match","separator","tokenMetadata","Pipeline","_stack","registeredFunctions","registerFunction","label","warnIfFunctionNotRegistered","load","serialised","forEach","fnName","Error","arguments","after","existingFn","newFn","pos","splice","before","remove","run","stackLength","memo","j","result","k","runString","token","reset","toJSON","Vector","_magnitude","positionForIndex","index","start","end","pivotPoint","floor","pivotIndex","insert","insertIdx","upsert","position","magnitude","sumOfSquares","elementsLength","sqrt","dot","otherVector","dotProduct","aLen","bLen","aVal","bVal","similarity","toArray","output","ational","tional","enci","anci","izer","bli","alli","entli","eli","ousli","ization","ation","ator","alism","iveness","fulness","ousness","aliti","iviti","biliti","logi","icate","ative","alize","iciti","ical","ful","ness","c","RegExp","w","stem","suffix","firstch","re","re2","re3","re4","substr","toUpperCase","test","replace","fp","exec","generateStopWordFilter","stopWords","words","reduce","stopWord","TokenSet","final","edges","id","_nextId","fromArray","arr","finish","fromClause","clause","fromFuzzyString","term","editDistance","stack","node","editsRemaining","frame","pop","noEditNode","char","insertionNode","substitutionNode","transposeNode","charA","charB","next","prefix","edge","_str","labels","sort","qNode","qEdges","qLen","nEdges","nLen","q","qEdge","nEdge","previousWord","uncheckedNodes","minimizedNodes","word","commonPrefix","minimize","child","nextNode","parent","downTo","childKey","Index","attrs","invertedIndex","fieldVectors","tokenSet","fields","search","queryString","query","QueryParser","parse","Query","matchingFields","queryVectors","termFieldCache","requiredMatches","prohibitedMatches","clauses","terms","clauseMatches","usePipeline","m","termTokenSet","expandedTerms","presence","REQUIRED","field","expandedTerm","termIndex","_index","fieldPosting","matchingDocumentRefs","termField","matchingDocumentsSet","PROHIBITED","boost","l","fieldMatch","matchingDocumentRef","matchingFieldRef","MatchData","allRequiredMatches","allProhibitedMatches","matchingFieldRefs","results","matches","isNegated","docMatch","fieldVector","score","matchData","combine","ref","serializedIndex","serializedVectors","serializedInvertedIndex","tokenSetBuilder","tuple","_ref","_fields","_documents","fieldTermFrequencies","fieldLengths","_b","_k1","metadataWhitelist","attributes","RangeError","number","k1","doc","extractor","fieldTerms","metadataKey","calculateAverageFieldLengths","fieldRefs","numberOfFields","accumulator","documentsWithField","averageFieldLength","createFieldVectors","fieldRefsLength","termIdfCache","fieldLength","termFrequencies","termsLength","fieldBoost","docBoost","scoreWithPrecision","tf","round","createTokenSet","use","args","unshift","apply","clonedMetadata","metadataKeys","otherMatchData","allFields","wildcard","String","NONE","LEADING","TRAILING","OPTIONAL","options","QueryParseError","name","QueryLexer","lexemes","escapeCharPositions","state","lexText","sliceString","subSlices","join","emit","type","escapeCharacter","EOS","width","ignore","backup","acceptDigitRun","charCode","charCodeAt","more","FIELD","TERM","EDIT_DISTANCE","BOOST","PRESENCE","lexField","lexer","lexTerm","lexEditDistance","lexBoost","lexEOS","termSeparator","currentClause","lexemeIdx","parseClause","peekLexeme","consumeLexeme","lexeme","nextClause","completedClause","parser","parsePresence","parseField","parseTerm","errorMessage","nextLexeme","possibleFields","f","parseEditDistance","parseBoost","parseInt","isNaN","define","amd","exports","module"],"mappings":"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"}
\ No newline at end of file
diff --git a/admin/tool/componentlibrary/amd/build/mustache.min.js.map b/admin/tool/componentlibrary/amd/build/mustache.min.js.map
index 3e3fa85e215..54ed48f7789 100644
--- a/admin/tool/componentlibrary/amd/build/mustache.min.js.map
+++ b/admin/tool/componentlibrary/amd/build/mustache.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"mustache.min.js","sources":["../src/mustache.js"],"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 * Render mustache template examples within the component library.\n *\n * @module tool_componentlibrary/mustache\n * @copyright 2021 Bas Brands \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport selectors from 'tool_componentlibrary/selectors';\nimport Ajax from 'core/ajax';\nimport Config from 'core/config';\nimport Templates from 'core/templates';\nimport Log from 'core/log';\nimport Notification from 'core/notification';\n\n/**\n * Handle a template loaded response.\n *\n * @method\n * @private\n * @param {String} container The template container\n * @param {String} templateName The template name\n * @param {String} context Data for the template.\n */\nconst renderTemplate = async(container, templateName, context) => {\n try {\n context = JSON.parse(context);\n } catch (e) {\n Log.debug('Could not parse json example context for template.');\n Log.debug(e);\n }\n\n const {html, js} = await Templates.renderForPromise(templateName, context);\n\n const rendercontainer = container.querySelector(selectors.mustacherendered);\n\n // Load the rendered content in the renderer tab.\n await Templates.replaceNodeContents(rendercontainer, html, js);\n};\n\n/**\n * Load the a template source from Moodle.\n *\n * @method\n * @private\n * @param {String} container The template container\n */\nconst loadTemplate = container => {\n const sourcecontainer = container.querySelector(selectors.mustachesource);\n const contextcontainer = container.querySelector(selectors.mustachecontext);\n const templateName = container.dataset.template;\n let context = container.querySelector(selectors.mustacherawcontext).textContent;\n\n const parts = templateName.split('/');\n const component = parts.shift();\n const name = parts.join('/');\n\n const request = {\n methodname: 'core_output_load_template',\n args: {\n component: component,\n template: name,\n themename: Config.theme,\n includecomments: true\n }\n };\n\n Ajax.call([request])[0]\n .done((source) => {\n // Load the source template in Template tab.\n sourcecontainer.textContent = source;\n if (!context) {\n const example = source.match(/Example context \\(json\\):([\\s\\S]+?)(}})/);\n context = example[1];\n // Load the variables in the Variables tab.\n const precontainer = document.createElement(\"pre\");\n precontainer.innerHTML = JSON.stringify(JSON.parse(context), null, 4);\n contextcontainer.parentNode.appendChild(precontainer);\n contextcontainer.classList.add('d-none');\n }\n renderTemplate(container, templateName, context);\n })\n .fail(Notification.exception);\n};\n\n/**\n * Initialize the module.\n *\n * @method\n */\nexport const mustache = () => {\n document.querySelectorAll(selectors.mustachecode).forEach((container) => {\n loadTemplate(container);\n });\n};\n"],"names":["loadTemplate","container","sourcecontainer","querySelector","selectors","mustachesource","contextcontainer","mustachecontext","templateName","dataset","template","context","mustacherawcontext","textContent","parts","split","request","methodname","args","component","shift","join","themename","Config","theme","includecomments","call","done","source","example","match","precontainer","document","createElement","innerHTML","JSON","stringify","parse","parentNode","appendChild","classList","add","async","e","debug","html","js","Templates","renderForPromise","rendercontainer","mustacherendered","replaceNodeContents","renderTemplate","fail","Notification","exception","querySelectorAll","mustachecode","forEach"],"mappings":";;;;;;;0VA8DMA,aAAeC,kBACPC,gBAAkBD,UAAUE,cAAcC,mBAAUC,gBACpDC,iBAAmBL,UAAUE,cAAcC,mBAAUG,iBACrDC,aAAeP,UAAUQ,QAAQC,aACnCC,QAAUV,UAAUE,cAAcC,mBAAUQ,oBAAoBC,kBAE9DC,MAAQN,aAAaO,MAAM,KAI3BC,QAAU,CACZC,WAAY,4BACZC,KAAM,CACFC,UANUL,MAAMM,QAOhBV,SANKI,MAAMO,KAAK,KAOhBC,UAAWC,gBAAOC,MAClBC,iBAAiB,kBAIpBC,KAAK,CAACV,UAAU,GAChBW,MAAMC,YAEH1B,gBAAgBW,YAAce,QACzBjB,QAAS,OACJkB,QAAUD,OAAOE,MAAM,2CAC7BnB,QAAUkB,QAAQ,SAEZE,aAAeC,SAASC,cAAc,OAC5CF,aAAaG,UAAYC,KAAKC,UAAUD,KAAKE,MAAM1B,SAAU,KAAM,GACnEL,iBAAiBgC,WAAWC,YAAYR,cACxCzB,iBAAiBkC,UAAUC,IAAI,UAtD5BC,OAAMzC,UAAWO,aAAcG,eAE9CA,QAAUwB,KAAKE,MAAM1B,SACvB,MAAOgC,gBACDC,MAAM,mEACNA,MAAMD,SAGRE,KAACA,KAADC,GAAOA,UAAYC,mBAAUC,iBAAiBxC,aAAcG,SAE5DsC,gBAAkBhD,UAAUE,cAAcC,mBAAU8C,wBAGpDH,mBAAUI,oBAAoBF,gBAAiBJ,KAAMC,KA2C/CM,CAAenD,UAAWO,aAAcG,YAE3C0C,KAAKC,sBAAaC,8BAQP,KACpBvB,SAASwB,iBAAiBpD,mBAAUqD,cAAcC,SAASzD,YACvDD,aAAaC"}
\ No newline at end of file
+{"version":3,"file":"mustache.min.js","sources":["../src/mustache.js"],"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 * Render mustache template examples within the component library.\n *\n * @module tool_componentlibrary/mustache\n * @copyright 2021 Bas Brands \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport selectors from 'tool_componentlibrary/selectors';\nimport Ajax from 'core/ajax';\nimport Config from 'core/config';\nimport Templates from 'core/templates';\nimport Log from 'core/log';\nimport Notification from 'core/notification';\n\n/**\n * Handle a template loaded response.\n *\n * @method\n * @private\n * @param {String} container The template container\n * @param {String} templateName The template name\n * @param {String} context Data for the template.\n */\nconst renderTemplate = async(container, templateName, context) => {\n try {\n context = JSON.parse(context);\n } catch (e) {\n Log.debug('Could not parse json example context for template.');\n Log.debug(e);\n }\n\n const {html, js} = await Templates.renderForPromise(templateName, context);\n\n const rendercontainer = container.querySelector(selectors.mustacherendered);\n\n // Load the rendered content in the renderer tab.\n await Templates.replaceNodeContents(rendercontainer, html, js);\n};\n\n/**\n * Load the a template source from Moodle.\n *\n * @method\n * @private\n * @param {String} container The template container\n */\nconst loadTemplate = container => {\n const sourcecontainer = container.querySelector(selectors.mustachesource);\n const contextcontainer = container.querySelector(selectors.mustachecontext);\n const templateName = container.dataset.template;\n let context = container.querySelector(selectors.mustacherawcontext).textContent;\n\n const parts = templateName.split('/');\n const component = parts.shift();\n const name = parts.join('/');\n\n const request = {\n methodname: 'core_output_load_template',\n args: {\n component: component,\n template: name,\n themename: Config.theme,\n includecomments: true\n }\n };\n\n Ajax.call([request])[0]\n .done((source) => {\n // Load the source template in Template tab.\n sourcecontainer.textContent = source;\n if (!context) {\n const example = source.match(/Example context \\(json\\):([\\s\\S]+?)(}})/);\n context = example[1];\n // Load the variables in the Variables tab.\n const precontainer = document.createElement(\"pre\");\n precontainer.innerHTML = JSON.stringify(JSON.parse(context), null, 4);\n contextcontainer.parentNode.appendChild(precontainer);\n contextcontainer.classList.add('d-none');\n }\n renderTemplate(container, templateName, context);\n })\n .fail(Notification.exception);\n};\n\n/**\n * Initialize the module.\n *\n * @method\n */\nexport const mustache = () => {\n document.querySelectorAll(selectors.mustachecode).forEach((container) => {\n loadTemplate(container);\n });\n};\n"],"names":["_interopRequireDefault","obj","__esModule","default","_selectors","_ajax","_config","_templates","_log","_notification","loadTemplate","container","sourcecontainer","querySelector","selectors","mustachesource","contextcontainer","mustachecontext","templateName","dataset","template","context","mustacherawcontext","textContent","parts","split","request","methodname","args","component","shift","join","themename","Config","theme","includecomments","Ajax","call","done","source","example","match","precontainer","document","createElement","innerHTML","JSON","stringify","parse","parentNode","appendChild","classList","add","async","e","Log","debug","html","js","Templates","renderForPromise","rendercontainer","mustacherendered","replaceNodeContents","renderTemplate","fail","Notification","exception","_exports","mustache","querySelectorAll","mustachecode","forEach"],"mappings":"2OA4B6C,SAAAA,uBAAAC,KAAAA,OAAAA,KAAAA,IAAAC,WAAAD,IAAAE,CAAAA,QAAAF,IAAA;;;;;;;sFAL7CG,WAAAJ,uBAAAI,YACAC,MAAAL,uBAAAK,OACAC,QAAAN,uBAAAM,SACAC,WAAAP,uBAAAO,YACAC,KAAAR,uBAAAQ,MACAC,cAAAT,uBAAAS,eAWA,MAuBMC,aAAeC,YACb,MAAMC,gBAAkBD,UAAUE,cAAcC,WAAAA,QAAUC,gBACpDC,iBAAmBL,UAAUE,cAAcC,WAAAA,QAAUG,iBACrDC,aAAeP,UAAUQ,QAAQC,SACvC,IAAIC,QAAUV,UAAUE,cAAcC,WAAAA,QAAUQ,oBAAoBC,YAEpE,MAAMC,MAAQN,aAAaO,MAAM,KAI3BC,QAAU,CACZC,WAAY,4BACZC,KAAM,CACFC,UANUL,MAAMM,QAOhBV,SANKI,MAAMO,KAAK,KAOhBC,UAAWC,QAAM9B,QAAC+B,MAClBC,iBAAiB,IAIzBC,MAAAA,QAAKC,KAAK,CAACX,UAAU,GAChBY,MAAMC,SAGH,GADA3B,gBAAgBW,YAAcgB,QACzBlB,QAAS,CACV,MAAMmB,QAAUD,OAAOE,MAAM,2CAC7BpB,QAAUmB,QAAQ,GAElB,MAAME,aAAeC,SAASC,cAAc,OAC5CF,aAAaG,UAAYC,KAAKC,UAAUD,KAAKE,MAAM3B,SAAU,KAAM,GACnEL,iBAAiBiC,WAAWC,YAAYR,cACxC1B,iBAAiBmC,UAAUC,IAAI,SACnC,CAvDOC,OAAM1C,UAAWO,aAAcG,WAClD,IACIA,QAAUyB,KAAKE,MAAM3B,QACxB,CAAC,MAAOiC,GACLC,KAAAA,QAAIC,MAAM,sDACVD,KAAAA,QAAIC,MAAMF,EACd,CAEA,MAAMG,KAACA,KAAIC,GAAEA,UAAYC,WAASxD,QAACyD,iBAAiB1C,aAAcG,SAE5DwC,gBAAkBlD,UAAUE,cAAcC,WAAAA,QAAUgD,wBAGpDH,WAAAA,QAAUI,oBAAoBF,gBAAiBJ,KAAMC,GAAG,EA2ClDM,CAAerD,UAAWO,aAAcG,QAAQ,IAEnD4C,KAAKC,cAAY/D,QAACgE,UAAU,EAYvCC,SAAAC,SAJsBA,KACpB1B,SAAS2B,iBAAiBxD,WAASX,QAACoE,cAAcC,SAAS7D,YACvDD,aAAaC,UAAU,GACzB,CACJ"}
\ No newline at end of file
diff --git a/admin/tool/componentlibrary/amd/build/search.min.js.map b/admin/tool/componentlibrary/amd/build/search.min.js.map
index 82b9e5a4a44..17522e84591 100644
--- a/admin/tool/componentlibrary/amd/build/search.min.js.map
+++ b/admin/tool/componentlibrary/amd/build/search.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"search.min.js","sources":["../src/search.js"],"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 * Interface to the Lunr search engines.\n *\n * @module tool_componentlibrary/search\n * @copyright 2021 Bas Brands \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport lunrJs from 'tool_componentlibrary/lunr';\nimport selectors from 'tool_componentlibrary/selectors';\nimport Log from 'core/log';\nimport Notification from 'core/notification';\nimport {enter, escape} from 'core/key_codes';\n\nlet lunrIndex = null;\nlet pagesIndex = null;\n\n/**\n * Get the jsonFile that is generated when the component library is build.\n *\n * @method\n * @private\n * @param {String} jsonFile the URL to the json file.\n * @return {Object}\n */\nconst fetchJson = async(jsonFile) => {\n const response = await fetch(jsonFile);\n\n if (!response.ok) {\n Log.debug(`Error getting Hugo index file: ${response.status}`);\n }\n\n return await response.json();\n};\n\n/**\n * Initiate lunr on the data in the jsonFile and add the jsondata to the pagesIndex\n *\n * @method\n * @private\n * @param {String} jsonFile the URL to the json file.\n */\nconst initLunr = jsonFile => {\n fetchJson(jsonFile).then(jsondata => {\n pagesIndex = jsondata;\n // Using an arrow function here will break lunr on compile.\n lunrIndex = lunrJs(function() {\n this.ref('uri');\n this.field('title', {boost: 10});\n this.field('content');\n this.field('tags', {boost: 5});\n jsondata.forEach(p => {\n this.add(p);\n });\n });\n return null;\n }).catch(Notification.exception);\n};\n\n/**\n * Setup the eventlistener to listen on user input on the search field.\n *\n * @method\n * @private\n */\nconst initUI = () => {\n const searchInput = document.querySelector(selectors.searchinput);\n searchInput.addEventListener('keyup', e => {\n const query = e.currentTarget.value;\n if (query.length < 2) {\n document.querySelector(selectors.dropdownmenu).classList.remove('show');\n return;\n }\n renderResults(searchIndex(query));\n });\n searchInput.addEventListener('keydown', e => {\n if (e.keyCode === enter) {\n e.preventDefault();\n }\n if (e.keyCode === escape) {\n searchInput.value = '';\n }\n });\n};\n\n/**\n * Trigger a search in lunr and transform the result.\n *\n * @method\n * @private\n * @param {String} query\n * @return {Array} results\n */\nconst searchIndex = query => {\n // Find the item in our index corresponding to the lunr one to have more info\n // Lunr result:\n // {ref: \"/section/page1\", score: 0.2725657778206127}\n // Our result:\n // {title:\"Page1\", href:\"/section/page1\", ...}\n\n return lunrIndex.search(query + ' ' + query + '*').map(result => {\n return pagesIndex.filter(page => {\n return page.uri === result.ref;\n })[0];\n });\n};\n\n/**\n * Display the 10 first results\n *\n * @method\n * @private\n * @param {Array} results to display\n */\nconst renderResults = results => {\n const dropdownMenu = document.querySelector(selectors.dropdownmenu);\n if (!results.length) {\n dropdownMenu.classList.remove('show');\n return;\n }\n\n // Clear out the results.\n dropdownMenu.innerHTML = '';\n\n const baseUrl = M.cfg.wwwroot + '/admin/tool/componentlibrary/docspage.php';\n\n // Only show the ten first results\n results.slice(0, 10).forEach(function(result) {\n const link = document.createElement(\"a\");\n const chapter = result.uri.split('/')[1];\n link.appendChild(document.createTextNode(`${chapter} > ${result.title}`));\n link.classList.add('dropdown-item');\n link.href = baseUrl + result.uri;\n\n dropdownMenu.appendChild(link);\n });\n\n dropdownMenu.classList.add('show');\n};\n\n/**\n * Initialize module.\n *\n * @method\n * @param {String} jsonFile Full path to the search DB json file.\n */\nexport const search = jsonFile => {\n initLunr(jsonFile);\n initUI();\n};\n"],"names":["lunrIndex","pagesIndex","initLunr","jsonFile","async","response","fetch","ok","debug","status","json","fetchJson","then","jsondata","ref","field","boost","forEach","p","add","catch","Notification","exception","searchIndex","query","search","map","result","filter","page","uri","renderResults","results","dropdownMenu","document","querySelector","selectors","dropdownmenu","length","classList","remove","innerHTML","baseUrl","M","cfg","wwwroot","slice","link","createElement","chapter","split","appendChild","createTextNode","title","href","searchInput","searchinput","addEventListener","e","currentTarget","value","keyCode","enter","preventDefault","escape","initUI"],"mappings":";;;;;;;gQA6BIA,UAAY,KACZC,WAAa,WA2BXC,SAAWC,WAjBCC,OAAAA,iBACRC,eAAiBC,MAAMH,iBAExBE,SAASE,iBACNC,+CAAwCH,SAASI,eAG5CJ,SAASK,QAWtBC,CAAUR,UAAUS,MAAKC,WACrBZ,WAAaY,SAEbb,WAAY,kBAAO,gBACVc,IAAI,YACJC,MAAM,QAAS,CAACC,MAAO,UACvBD,MAAM,gBACNA,MAAM,OAAQ,CAACC,MAAO,IAC3BH,SAASI,SAAQC,SACRC,IAAID,SAGV,QACRE,MAAMC,sBAAaC,YAqCpBC,YAAcC,OAOTxB,UAAUyB,OAAOD,MAAQ,IAAMA,MAAQ,KAAKE,KAAIC,QAC5C1B,WAAW2B,QAAOC,MACdA,KAAKC,MAAQH,OAAOb,MAC5B,KAWLiB,cAAgBC,gBACZC,aAAeC,SAASC,cAAcC,mBAAUC,kBACjDL,QAAQM,mBACTL,aAAaM,UAAUC,OAAO,QAKlCP,aAAaQ,UAAY,SAEnBC,QAAUC,EAAEC,IAAIC,QAAU,4CAGhCb,QAAQc,MAAM,EAAG,IAAI7B,SAAQ,SAASU,cAC5BoB,KAAOb,SAASc,cAAc,KAC9BC,QAAUtB,OAAOG,IAAIoB,MAAM,KAAK,GACtCH,KAAKI,YAAYjB,SAASkB,yBAAkBH,sBAAatB,OAAO0B,SAChEN,KAAKR,UAAUpB,IAAI,iBACnB4B,KAAKO,KAAOZ,QAAUf,OAAOG,IAE7BG,aAAakB,YAAYJ,SAG7Bd,aAAaM,UAAUpB,IAAI,yBASThB,WAClBD,SAASC,UAlFE,YACLoD,YAAcrB,SAASC,cAAcC,mBAAUoB,aACrDD,YAAYE,iBAAiB,SAASC,UAC5BlC,MAAQkC,EAAEC,cAAcC,MAC1BpC,MAAMc,OAAS,EACfJ,SAASC,cAAcC,mBAAUC,cAAcE,UAAUC,OAAO,QAGpET,cAAcR,YAAYC,WAE9B+B,YAAYE,iBAAiB,WAAWC,IAChCA,EAAEG,UAAYC,kBACdJ,EAAEK,iBAEFL,EAAEG,UAAYG,oBACdT,YAAYK,MAAQ,QAoE5BK"}
\ No newline at end of file
+{"version":3,"file":"search.min.js","sources":["../src/search.js"],"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 * Interface to the Lunr search engines.\n *\n * @module tool_componentlibrary/search\n * @copyright 2021 Bas Brands \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport lunrJs from 'tool_componentlibrary/lunr';\nimport selectors from 'tool_componentlibrary/selectors';\nimport Log from 'core/log';\nimport Notification from 'core/notification';\nimport {enter, escape} from 'core/key_codes';\n\nlet lunrIndex = null;\nlet pagesIndex = null;\n\n/**\n * Get the jsonFile that is generated when the component library is build.\n *\n * @method\n * @private\n * @param {String} jsonFile the URL to the json file.\n * @return {Object}\n */\nconst fetchJson = async(jsonFile) => {\n const response = await fetch(jsonFile);\n\n if (!response.ok) {\n Log.debug(`Error getting Hugo index file: ${response.status}`);\n }\n\n return await response.json();\n};\n\n/**\n * Initiate lunr on the data in the jsonFile and add the jsondata to the pagesIndex\n *\n * @method\n * @private\n * @param {String} jsonFile the URL to the json file.\n */\nconst initLunr = jsonFile => {\n fetchJson(jsonFile).then(jsondata => {\n pagesIndex = jsondata;\n // Using an arrow function here will break lunr on compile.\n lunrIndex = lunrJs(function() {\n this.ref('uri');\n this.field('title', {boost: 10});\n this.field('content');\n this.field('tags', {boost: 5});\n jsondata.forEach(p => {\n this.add(p);\n });\n });\n return null;\n }).catch(Notification.exception);\n};\n\n/**\n * Setup the eventlistener to listen on user input on the search field.\n *\n * @method\n * @private\n */\nconst initUI = () => {\n const searchInput = document.querySelector(selectors.searchinput);\n searchInput.addEventListener('keyup', e => {\n const query = e.currentTarget.value;\n if (query.length < 2) {\n document.querySelector(selectors.dropdownmenu).classList.remove('show');\n return;\n }\n renderResults(searchIndex(query));\n });\n searchInput.addEventListener('keydown', e => {\n if (e.keyCode === enter) {\n e.preventDefault();\n }\n if (e.keyCode === escape) {\n searchInput.value = '';\n }\n });\n};\n\n/**\n * Trigger a search in lunr and transform the result.\n *\n * @method\n * @private\n * @param {String} query\n * @return {Array} results\n */\nconst searchIndex = query => {\n // Find the item in our index corresponding to the lunr one to have more info\n // Lunr result:\n // {ref: \"/section/page1\", score: 0.2725657778206127}\n // Our result:\n // {title:\"Page1\", href:\"/section/page1\", ...}\n\n return lunrIndex.search(query + ' ' + query + '*').map(result => {\n return pagesIndex.filter(page => {\n return page.uri === result.ref;\n })[0];\n });\n};\n\n/**\n * Display the 10 first results\n *\n * @method\n * @private\n * @param {Array} results to display\n */\nconst renderResults = results => {\n const dropdownMenu = document.querySelector(selectors.dropdownmenu);\n if (!results.length) {\n dropdownMenu.classList.remove('show');\n return;\n }\n\n // Clear out the results.\n dropdownMenu.innerHTML = '';\n\n const baseUrl = M.cfg.wwwroot + '/admin/tool/componentlibrary/docspage.php';\n\n // Only show the ten first results\n results.slice(0, 10).forEach(function(result) {\n const link = document.createElement(\"a\");\n const chapter = result.uri.split('/')[1];\n link.appendChild(document.createTextNode(`${chapter} > ${result.title}`));\n link.classList.add('dropdown-item');\n link.href = baseUrl + result.uri;\n\n dropdownMenu.appendChild(link);\n });\n\n dropdownMenu.classList.add('show');\n};\n\n/**\n * Initialize module.\n *\n * @method\n * @param {String} jsonFile Full path to the search DB json file.\n */\nexport const search = jsonFile => {\n initLunr(jsonFile);\n initUI();\n};\n"],"names":["_interopRequireDefault","obj","__esModule","default","_lunr","_selectors","_log","_notification","lunrIndex","pagesIndex","initLunr","jsonFile","async","response","fetch","ok","Log","debug","concat","status","json","fetchJson","then","jsondata","lunrJs","this","ref","field","boost","forEach","p","add","catch","Notification","exception","searchIndex","query","search","map","result","filter","page","uri","renderResults","results","dropdownMenu","document","querySelector","selectors","dropdownmenu","length","classList","remove","innerHTML","baseUrl","M","cfg","wwwroot","slice","link","createElement","chapter","split","appendChild","createTextNode","title","href","_exports","initUI","searchInput","searchinput","addEventListener","e","currentTarget","value","keyCode","enter","preventDefault","escape"],"mappings":"oOA0B6C,SAAAA,uBAAAC,KAAAA,OAAAA,KAAAA,IAAAC,WAAAD,IAAAE,CAAAA,QAAAF,IAAA;;;;;;;oFAH7CG,MAAAJ,uBAAAI,OACAC,WAAAL,uBAAAK,YACAC,KAAAN,uBAAAM,MACAC,cAAAP,uBAAAO,eAGA,IAAIC,UAAY,KACZC,WAAa,KAUjB,MAiBMC,SAAWC,WAjBCC,kBACd,MAAMC,eAAiBC,MAAMH,UAM7B,OAJKE,SAASE,IACVC,KAAGb,QAACc,MAAKC,kCAAAA,OAAmCL,SAASM,eAG5CN,SAASO,MAAM,EAW5BC,CAAUV,UAAUW,MAAKC,WACrBd,WAAac,SAEbf,WAAY,EAAAgB,MAAMrB,UAAC,WACfsB,KAAKC,IAAI,OACTD,KAAKE,MAAM,QAAS,CAACC,MAAO,KAC5BH,KAAKE,MAAM,WACXF,KAAKE,MAAM,OAAQ,CAACC,MAAO,IAC3BL,SAASM,SAAQC,IACbL,KAAKM,IAAID,EAAE,GAEnB,IACO,QACRE,MAAMC,cAAY9B,QAAC+B,UAAU,EAqC9BC,YAAcC,OAOT5B,UAAU6B,OAAOD,MAAQ,IAAMA,MAAQ,KAAKE,KAAIC,QAC5C9B,WAAW+B,QAAOC,MACdA,KAAKC,MAAQH,OAAOb,MAC5B,KAWLiB,cAAgBC,UAClB,MAAMC,aAAeC,SAASC,cAAcC,WAAAA,QAAUC,cACtD,IAAKL,QAAQM,OAET,YADAL,aAAaM,UAAUC,OAAO,QAKlCP,aAAaQ,UAAY,GAEzB,MAAMC,QAAUC,EAAEC,IAAIC,QAAU,4CAGhCb,QAAQc,MAAM,EAAG,IAAI7B,SAAQ,SAASU,QAClC,MAAMoB,KAAOb,SAASc,cAAc,KAC9BC,QAAUtB,OAAOG,IAAIoB,MAAM,KAAK,GACtCH,KAAKI,YAAYjB,SAASkB,eAAc9C,GAAAA,OAAI2C,eAAO3C,OAAMqB,OAAO0B,SAChEN,KAAKR,UAAUpB,IAAI,iBACnB4B,KAAKO,KAAOZ,QAAUf,OAAOG,IAE7BG,aAAakB,YAAYJ,KAC7B,IAEAd,aAAaM,UAAUpB,IAAI,OAAO,EAYpCoC,SAAA9B,OAHoB1B,WAClBD,SAASC,UAlFEyD,MACX,MAAMC,YAAcvB,SAASC,cAAcC,WAAAA,QAAUsB,aACrDD,YAAYE,iBAAiB,SAASC,IAClC,MAAMpC,MAAQoC,EAAEC,cAAcC,MAC1BtC,MAAMc,OAAS,EACfJ,SAASC,cAAcC,WAAAA,QAAUC,cAAcE,UAAUC,OAAO,QAGpET,cAAcR,YAAYC,OAAO,IAErCiC,YAAYE,iBAAiB,WAAWC,IAChCA,EAAEG,UAAYC,kBACdJ,EAAEK,iBAEFL,EAAEG,UAAYG,oBACdT,YAAYK,MAAQ,GACxB,GACF,EAkEFN,EAAQ,CACV"}
\ No newline at end of file
diff --git a/admin/tool/componentlibrary/amd/build/selectors.min.js.map b/admin/tool/componentlibrary/amd/build/selectors.min.js.map
index 3f14335b2e2..ff89c1dacad 100644
--- a/admin/tool/componentlibrary/amd/build/selectors.min.js.map
+++ b/admin/tool/componentlibrary/amd/build/selectors.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"selectors.min.js","sources":["../src/selectors.js"],"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 * Selectors for the component library\n *\n * @module tool_componentlibrary/selectors\n * @copyright 2021 Bas Brands \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nexport default {\n /**\n * A selector relating to the 'Copy to clipboard' button.\n *\n * @type {string}\n */\n clipboardbutton: '.btn-clipboard',\n\n /**\n * A selector relating to the content copied by the 'Copy to clipboard' button.\n *\n * @type {string}\n */\n clipboardcontent: 'figure.highlight, div.highlight',\n\n /**\n * A selector relating to the 'Search' text input.\n *\n * @type {string}\n */\n searchinput: '[data-region=\"docsearch\"] input',\n\n /**\n * A selector relating to the 'Search' submit btton.\n *\n * @type {string}\n */\n searchsubmit: '[data-region=\"docsearch\"] .btn-submit',\n\n /**\n * A selector relating to the search dropdown menu.\n *\n * @type {string}\n */\n dropdownmenu: '[data-region=\"docsearch\"] .dropdown-menu',\n\n /**\n * A selector relating to the entire Component Library content region.\n *\n * @type {string}\n */\n componentlibrary: '[data-region=\"componentlibrary\"]',\n\n /**\n * A selector relating to JS Code which is to be run for examples to function.\n *\n * @type {string}\n */\n jscode: '[data-action=\"runjs\"]',\n\n /**\n * A selector relating to Mustache Template code regions.\n *\n * @type {string}\n */\n mustachecode: '[data-region=\"mustachecode\"]',\n\n /**\n * A selector relating to raw Mustache content regions.\n *\n * @type {string}\n */\n mustacherawcontext: '[data-region=\"rawcontext\"]',\n\n /**\n * A selector relating to rendered Mustache content regions.\n *\n * @type {string}\n */\n mustacherendered: '[data-region=\"mustacherendered\"]',\n\n /**\n * A selector relating to Mustache source code regions.\n *\n * @type {string}\n */\n mustachesource: '[data-region=\"mustachesource\"]',\n\n /**\n * A selector relating to Mustache context regions.\n *\n * @type {string}\n */\n mustachecontext: '[data-region=\"mustachecontext\"]',\n};\n"],"names":["clipboardbutton","clipboardcontent","searchinput","searchsubmit","dropdownmenu","componentlibrary","jscode","mustachecode","mustacherawcontext","mustacherendered","mustachesource","mustachecontext"],"mappings":"iLAsBe,CAMXA,gBAAiB,iBAOjBC,iBAAkB,kCAOlBC,YAAa,kCAObC,aAAc,wCAOdC,aAAc,2CAOdC,iBAAkB,mCAOlBC,OAAQ,wBAORC,aAAc,+BAOdC,mBAAoB,6BAOpBC,iBAAkB,mCAOlBC,eAAgB,iCAOhBC,gBAAiB"}
\ No newline at end of file
+{"version":3,"file":"selectors.min.js","sources":["../src/selectors.js"],"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 * Selectors for the component library\n *\n * @module tool_componentlibrary/selectors\n * @copyright 2021 Bas Brands \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nexport default {\n /**\n * A selector relating to the 'Copy to clipboard' button.\n *\n * @type {string}\n */\n clipboardbutton: '.btn-clipboard',\n\n /**\n * A selector relating to the content copied by the 'Copy to clipboard' button.\n *\n * @type {string}\n */\n clipboardcontent: 'figure.highlight, div.highlight',\n\n /**\n * A selector relating to the 'Search' text input.\n *\n * @type {string}\n */\n searchinput: '[data-region=\"docsearch\"] input',\n\n /**\n * A selector relating to the 'Search' submit btton.\n *\n * @type {string}\n */\n searchsubmit: '[data-region=\"docsearch\"] .btn-submit',\n\n /**\n * A selector relating to the search dropdown menu.\n *\n * @type {string}\n */\n dropdownmenu: '[data-region=\"docsearch\"] .dropdown-menu',\n\n /**\n * A selector relating to the entire Component Library content region.\n *\n * @type {string}\n */\n componentlibrary: '[data-region=\"componentlibrary\"]',\n\n /**\n * A selector relating to JS Code which is to be run for examples to function.\n *\n * @type {string}\n */\n jscode: '[data-action=\"runjs\"]',\n\n /**\n * A selector relating to Mustache Template code regions.\n *\n * @type {string}\n */\n mustachecode: '[data-region=\"mustachecode\"]',\n\n /**\n * A selector relating to raw Mustache content regions.\n *\n * @type {string}\n */\n mustacherawcontext: '[data-region=\"rawcontext\"]',\n\n /**\n * A selector relating to rendered Mustache content regions.\n *\n * @type {string}\n */\n mustacherendered: '[data-region=\"mustacherendered\"]',\n\n /**\n * A selector relating to Mustache source code regions.\n *\n * @type {string}\n */\n mustachesource: '[data-region=\"mustachesource\"]',\n\n /**\n * A selector relating to Mustache context regions.\n *\n * @type {string}\n */\n mustachecontext: '[data-region=\"mustachecontext\"]',\n};\n"],"names":["_exports","default","clipboardbutton","clipboardcontent","searchinput","searchsubmit","dropdownmenu","componentlibrary","jscode","mustachecode","mustacherawcontext","mustacherendered","mustachesource","mustachecontext"],"mappings":"yJA0GC,OAAAA,SAAAC,QApFc,CAMXC,gBAAiB,iBAOjBC,iBAAkB,kCAOlBC,YAAa,kCAObC,aAAc,wCAOdC,aAAc,2CAOdC,iBAAkB,mCAOlBC,OAAQ,wBAORC,aAAc,+BAOdC,mBAAoB,6BAOpBC,iBAAkB,mCAOlBC,eAAgB,iCAOhBC,gBAAiB,mCACpBb,SAAAC,OAAA"}
\ No newline at end of file
diff --git a/admin/tool/dataprivacy/amd/build/add_category.min.js.map b/admin/tool/dataprivacy/amd/build/add_category.min.js.map
index 2ee9d89dc99..cef834a5efd 100644
--- a/admin/tool/dataprivacy/amd/build/add_category.min.js.map
+++ b/admin/tool/dataprivacy/amd/build/add_category.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"add_category.min.js","sources":["../src/add_category.js"],"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 * Module to add categories.\n *\n * @module tool_dataprivacy/add_category\n * @copyright 2018 David Monllao\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine([\n 'jquery',\n 'core/str',\n 'core/ajax',\n 'core/notification',\n 'core/modal_factory',\n 'core/modal_events',\n 'core/fragment',\n 'core_form/changechecker',\n], function(\n $,\n Str,\n Ajax,\n Notification,\n ModalFactory,\n ModalEvents,\n Fragment,\n FormChangeChecker\n) {\n\n var SELECTORS = {\n CATEGORY_LINK: '[data-add-element=\"category\"]',\n };\n\n var AddCategory = function(contextId) {\n this.contextId = contextId;\n\n var stringKeys = [\n {\n key: 'addcategory',\n component: 'tool_dataprivacy'\n },\n {\n key: 'save',\n component: 'admin'\n }\n ];\n this.strings = Str.get_strings(stringKeys);\n\n this.registerEventListeners();\n };\n\n /**\n * @var {int} contextId\n * @private\n */\n AddCategory.prototype.contextId = 0;\n\n /**\n * @var {Promise}\n * @private\n */\n AddCategory.prototype.strings = 0;\n\n AddCategory.prototype.registerEventListeners = function() {\n\n var trigger = $(SELECTORS.CATEGORY_LINK);\n trigger.on('click', function() {\n return this.strings.then(function(strings) {\n ModalFactory.create({\n type: ModalFactory.types.SAVE_CANCEL,\n title: strings[0],\n body: '',\n }, trigger).done(function(modal) {\n this.setupFormModal(modal, strings[1]);\n }.bind(this));\n }.bind(this))\n .fail(Notification.exception);\n }.bind(this));\n\n };\n\n /**\n * @method getBody\n * @param {Object} formdata\n * @private\n * @return {Promise}\n */\n AddCategory.prototype.getBody = function(formdata) {\n\n var params = null;\n if (typeof formdata !== \"undefined\") {\n params = {jsonformdata: JSON.stringify(formdata)};\n }\n // Get the content of the modal.\n return Fragment.loadFragment('tool_dataprivacy', 'addcategory_form', this.contextId, params);\n };\n\n AddCategory.prototype.setupFormModal = function(modal, saveText) {\n modal.setLarge();\n\n modal.setSaveButtonText(saveText);\n\n // We want to reset the form every time it is opened.\n modal.getRoot().on(ModalEvents.hidden, this.destroy.bind(this));\n\n modal.setBody(this.getBody());\n\n // We catch the modal save event, and use it to submit the form inside the modal.\n // Triggering a form submission will give JS validation scripts a chance to check for errors.\n modal.getRoot().on(ModalEvents.save, this.submitForm.bind(this));\n // We also catch the form submit event and use it to submit the form with ajax.\n modal.getRoot().on('submit', 'form', this.submitFormAjax.bind(this));\n\n this.modal = modal;\n\n modal.show();\n };\n\n /**\n * This triggers a form submission, so that any mform elements can do final tricks before the form submission is processed.\n *\n * @method submitForm\n * @param {Event} e Form submission event.\n * @private\n */\n AddCategory.prototype.submitForm = function(e) {\n e.preventDefault();\n this.modal.getRoot().find('form').submit();\n };\n\n AddCategory.prototype.submitFormAjax = function(e) {\n // We don't want to do a real form submission.\n e.preventDefault();\n\n // Convert all the form elements values to a serialised string.\n var formData = this.modal.getRoot().find('form').serialize();\n\n Ajax.call([{\n methodname: 'tool_dataprivacy_create_category_form',\n args: {jsonformdata: JSON.stringify(formData)},\n done: function(data) {\n if (data.validationerrors) {\n this.modal.setBody(this.getBody(formData));\n } else {\n this.close();\n }\n }.bind(this),\n fail: Notification.exception\n }]);\n };\n\n AddCategory.prototype.close = function() {\n this.destroy();\n document.location.reload();\n };\n\n AddCategory.prototype.destroy = function() {\n FormChangeChecker.resetAllFormDirtyStates();\n this.modal.destroy();\n };\n\n AddCategory.prototype.removeListeners = function() {\n $(SELECTORS.CATEGORY_LINK).off('click');\n };\n\n return /** @alias module:tool_dataprivacy/add_category */ {\n getInstance: function(contextId) {\n return new AddCategory(contextId);\n }\n };\n }\n);\n\n"],"names":["define","$","Str","Ajax","Notification","ModalFactory","ModalEvents","Fragment","FormChangeChecker","SELECTORS","AddCategory","contextId","strings","get_strings","key","component","registerEventListeners","prototype","trigger","on","this","then","create","type","types","SAVE_CANCEL","title","body","done","modal","setupFormModal","bind","fail","exception","getBody","formdata","params","jsonformdata","JSON","stringify","loadFragment","saveText","setLarge","setSaveButtonText","getRoot","hidden","destroy","setBody","save","submitForm","submitFormAjax","show","e","preventDefault","find","submit","formData","serialize","call","methodname","args","data","validationerrors","close","document","location","reload","resetAllFormDirtyStates","removeListeners","off","getInstance"],"mappings":";;;;;;;AAsBAA,uCAAO,CACH,SACA,WACA,YACA,oBACA,qBACA,oBACA,gBACA,4BACD,SACCC,EACAC,IACAC,KACAC,aACAC,aACAC,YACAC,SACAC,uBAGQC,wBACe,gCAGfC,YAAc,SAASC,gBAClBA,UAAYA,eAYZC,QAAUV,IAAIW,YAVF,CACb,CACIC,IAAK,cACLC,UAAW,oBAEf,CACID,IAAK,OACLC,UAAW,gBAKdC,iCAOTN,YAAYO,UAAUN,UAAY,EAMlCD,YAAYO,UAAUL,QAAU,EAEhCF,YAAYO,UAAUD,uBAAyB,eAEvCE,QAAUjB,EAAEQ,yBAChBS,QAAQC,GAAG,QAAS,kBACTC,KAAKR,QAAQS,KAAK,SAAST,SAC9BP,aAAaiB,OAAO,CAChBC,KAAMlB,aAAamB,MAAMC,YACzBC,MAAOd,QAAQ,GACfe,KAAM,IACPT,SAASU,KAAK,SAASC,YACjBC,eAAeD,MAAOjB,QAAQ,KACrCmB,KAAKX,QACTW,KAAKX,OACNY,KAAK5B,aAAa6B,YACrBF,KAAKX,QAUXV,YAAYO,UAAUiB,QAAU,SAASC,cAEjCC,OAAS,iBACW,IAAbD,WACPC,OAAS,CAACC,aAAcC,KAAKC,UAAUJ,YAGpC5B,SAASiC,aAAa,mBAAoB,mBAAoBpB,KAAKT,UAAWyB,SAGzF1B,YAAYO,UAAUa,eAAiB,SAASD,MAAOY,UACnDZ,MAAMa,WAENb,MAAMc,kBAAkBF,UAGxBZ,MAAMe,UAAUzB,GAAGb,YAAYuC,OAAQzB,KAAK0B,QAAQf,KAAKX,OAEzDS,MAAMkB,QAAQ3B,KAAKc,WAInBL,MAAMe,UAAUzB,GAAGb,YAAY0C,KAAM5B,KAAK6B,WAAWlB,KAAKX,OAE1DS,MAAMe,UAAUzB,GAAG,SAAU,OAAQC,KAAK8B,eAAenB,KAAKX,YAEzDS,MAAQA,MAEbA,MAAMsB,QAUVzC,YAAYO,UAAUgC,WAAa,SAASG,GACxCA,EAAEC,sBACGxB,MAAMe,UAAUU,KAAK,QAAQC,UAGtC7C,YAAYO,UAAUiC,eAAiB,SAASE,GAE5CA,EAAEC,qBAGEG,SAAWpC,KAAKS,MAAMe,UAAUU,KAAK,QAAQG,YAEjDtD,KAAKuD,KAAK,CAAC,CACPC,WAAY,wCACZC,KAAM,CAACvB,aAAcC,KAAKC,UAAUiB,WACpC5B,KAAM,SAASiC,MACPA,KAAKC,sBACAjC,MAAMkB,QAAQ3B,KAAKc,QAAQsB,gBAE3BO,SAEXhC,KAAKX,MACPY,KAAM5B,aAAa6B,cAI3BvB,YAAYO,UAAU8C,MAAQ,gBACrBjB,UACLkB,SAASC,SAASC,UAGtBxD,YAAYO,UAAU6B,QAAU,WAC5BtC,kBAAkB2D,+BACbtC,MAAMiB,WAGfpC,YAAYO,UAAUmD,gBAAkB,WACpCnE,EAAEQ,yBAAyB4D,IAAI,UAGuB,CACtDC,YAAa,SAAS3D,kBACX,IAAID,YAAYC"}
\ No newline at end of file
+{"version":3,"file":"add_category.min.js","sources":["../src/add_category.js"],"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 * Module to add categories.\n *\n * @module tool_dataprivacy/add_category\n * @copyright 2018 David Monllao\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine([\n 'jquery',\n 'core/str',\n 'core/ajax',\n 'core/notification',\n 'core/modal_factory',\n 'core/modal_events',\n 'core/fragment',\n 'core_form/changechecker',\n], function(\n $,\n Str,\n Ajax,\n Notification,\n ModalFactory,\n ModalEvents,\n Fragment,\n FormChangeChecker\n) {\n\n var SELECTORS = {\n CATEGORY_LINK: '[data-add-element=\"category\"]',\n };\n\n var AddCategory = function(contextId) {\n this.contextId = contextId;\n\n var stringKeys = [\n {\n key: 'addcategory',\n component: 'tool_dataprivacy'\n },\n {\n key: 'save',\n component: 'admin'\n }\n ];\n this.strings = Str.get_strings(stringKeys);\n\n this.registerEventListeners();\n };\n\n /**\n * @var {int} contextId\n * @private\n */\n AddCategory.prototype.contextId = 0;\n\n /**\n * @var {Promise}\n * @private\n */\n AddCategory.prototype.strings = 0;\n\n AddCategory.prototype.registerEventListeners = function() {\n\n var trigger = $(SELECTORS.CATEGORY_LINK);\n trigger.on('click', function() {\n return this.strings.then(function(strings) {\n ModalFactory.create({\n type: ModalFactory.types.SAVE_CANCEL,\n title: strings[0],\n body: '',\n }, trigger).done(function(modal) {\n this.setupFormModal(modal, strings[1]);\n }.bind(this));\n }.bind(this))\n .fail(Notification.exception);\n }.bind(this));\n\n };\n\n /**\n * @method getBody\n * @param {Object} formdata\n * @private\n * @return {Promise}\n */\n AddCategory.prototype.getBody = function(formdata) {\n\n var params = null;\n if (typeof formdata !== \"undefined\") {\n params = {jsonformdata: JSON.stringify(formdata)};\n }\n // Get the content of the modal.\n return Fragment.loadFragment('tool_dataprivacy', 'addcategory_form', this.contextId, params);\n };\n\n AddCategory.prototype.setupFormModal = function(modal, saveText) {\n modal.setLarge();\n\n modal.setSaveButtonText(saveText);\n\n // We want to reset the form every time it is opened.\n modal.getRoot().on(ModalEvents.hidden, this.destroy.bind(this));\n\n modal.setBody(this.getBody());\n\n // We catch the modal save event, and use it to submit the form inside the modal.\n // Triggering a form submission will give JS validation scripts a chance to check for errors.\n modal.getRoot().on(ModalEvents.save, this.submitForm.bind(this));\n // We also catch the form submit event and use it to submit the form with ajax.\n modal.getRoot().on('submit', 'form', this.submitFormAjax.bind(this));\n\n this.modal = modal;\n\n modal.show();\n };\n\n /**\n * This triggers a form submission, so that any mform elements can do final tricks before the form submission is processed.\n *\n * @method submitForm\n * @param {Event} e Form submission event.\n * @private\n */\n AddCategory.prototype.submitForm = function(e) {\n e.preventDefault();\n this.modal.getRoot().find('form').submit();\n };\n\n AddCategory.prototype.submitFormAjax = function(e) {\n // We don't want to do a real form submission.\n e.preventDefault();\n\n // Convert all the form elements values to a serialised string.\n var formData = this.modal.getRoot().find('form').serialize();\n\n Ajax.call([{\n methodname: 'tool_dataprivacy_create_category_form',\n args: {jsonformdata: JSON.stringify(formData)},\n done: function(data) {\n if (data.validationerrors) {\n this.modal.setBody(this.getBody(formData));\n } else {\n this.close();\n }\n }.bind(this),\n fail: Notification.exception\n }]);\n };\n\n AddCategory.prototype.close = function() {\n this.destroy();\n document.location.reload();\n };\n\n AddCategory.prototype.destroy = function() {\n FormChangeChecker.resetAllFormDirtyStates();\n this.modal.destroy();\n };\n\n AddCategory.prototype.removeListeners = function() {\n $(SELECTORS.CATEGORY_LINK).off('click');\n };\n\n return /** @alias module:tool_dataprivacy/add_category */ {\n getInstance: function(contextId) {\n return new AddCategory(contextId);\n }\n };\n }\n);\n\n"],"names":["define","$","Str","Ajax","Notification","ModalFactory","ModalEvents","Fragment","FormChangeChecker","SELECTORS","AddCategory","contextId","this","strings","get_strings","key","component","registerEventListeners","prototype","trigger","on","then","create","type","types","SAVE_CANCEL","title","body","done","modal","setupFormModal","bind","fail","exception","getBody","formdata","params","jsonformdata","JSON","stringify","loadFragment","saveText","setLarge","setSaveButtonText","getRoot","hidden","destroy","setBody","save","submitForm","submitFormAjax","show","e","preventDefault","find","submit","formData","serialize","call","methodname","args","data","validationerrors","close","document","location","reload","resetAllFormDirtyStates","removeListeners","off","getInstance"],"mappings":";;;;;;;AAsBAA,OAAO,gCAAA,CACH,SACA,WACA,YACA,oBACA,qBACA,oBACA,gBACA,4BACD,SACCC,EACAC,IACAC,KACAC,aACAC,aACAC,YACAC,SACAC,mBAGI,IAAIC,wBACe,gCAGfC,YAAc,SAASC,WACvBC,KAAKD,UAAYA,UAYjBC,KAAKC,QAAUX,IAAIY,YAVF,CACb,CACIC,IAAK,cACLC,UAAW,oBAEf,CACID,IAAK,OACLC,UAAW,WAKnBJ,KAAKK,0BAqHT,OA9GAP,YAAYQ,UAAUP,UAAY,EAMlCD,YAAYQ,UAAUL,QAAU,EAEhCH,YAAYQ,UAAUD,uBAAyB,WAE3C,IAAIE,QAAUlB,EAAEQ,yBAChBU,QAAQC,GAAG,QAAS,WAChB,OAAOR,KAAKC,QAAQQ,KAAK,SAASR,SAC9BR,aAAaiB,OAAO,CAChBC,KAAMlB,aAAamB,MAAMC,YACzBC,MAAOb,QAAQ,GACfc,KAAM,IACPR,SAASS,KAAK,SAASC,OACtBjB,KAAKkB,eAAeD,MAAOhB,QAAQ,GACvC,EAAEkB,KAAKnB,MACX,EAAEmB,KAAKnB,OACNoB,KAAK5B,aAAa6B,UACvB,EAAEF,KAAKnB,QAUXF,YAAYQ,UAAUgB,QAAU,SAASC,UAErC,IAAIC,OAAS,KAKb,YAJwB,IAAbD,WACPC,OAAS,CAACC,aAAcC,KAAKC,UAAUJ,YAGpC5B,SAASiC,aAAa,mBAAoB,mBAAoB5B,KAAKD,UAAWyB,SAGzF1B,YAAYQ,UAAUY,eAAiB,SAASD,MAAOY,UACnDZ,MAAMa,WAENb,MAAMc,kBAAkBF,UAGxBZ,MAAMe,UAAUxB,GAAGd,YAAYuC,OAAQjC,KAAKkC,QAAQf,KAAKnB,OAEzDiB,MAAMkB,QAAQnC,KAAKsB,WAInBL,MAAMe,UAAUxB,GAAGd,YAAY0C,KAAMpC,KAAKqC,WAAWlB,KAAKnB,OAE1DiB,MAAMe,UAAUxB,GAAG,SAAU,OAAQR,KAAKsC,eAAenB,KAAKnB,OAE9DA,KAAKiB,MAAQA,MAEbA,MAAMsB,QAUVzC,YAAYQ,UAAU+B,WAAa,SAASG,GACxCA,EAAEC,iBACFzC,KAAKiB,MAAMe,UAAUU,KAAK,QAAQC,UAGtC7C,YAAYQ,UAAUgC,eAAiB,SAASE,GAE5CA,EAAEC,iBAGF,IAAIG,SAAW5C,KAAKiB,MAAMe,UAAUU,KAAK,QAAQG,YAEjDtD,KAAKuD,KAAK,CAAC,CACPC,WAAY,wCACZC,KAAM,CAACvB,aAAcC,KAAKC,UAAUiB,WACpC5B,KAAM,SAASiC,MACPA,KAAKC,iBACLlD,KAAKiB,MAAMkB,QAAQnC,KAAKsB,QAAQsB,WAEhC5C,KAAKmD,OAEb,EAAEhC,KAAKnB,MACPoB,KAAM5B,aAAa6B,cAI3BvB,YAAYQ,UAAU6C,MAAQ,WAC1BnD,KAAKkC,UACLkB,SAASC,SAASC,UAGtBxD,YAAYQ,UAAU4B,QAAU,WAC5BtC,kBAAkB2D,0BAClBvD,KAAKiB,MAAMiB,WAGfpC,YAAYQ,UAAUkD,gBAAkB,WACpCnE,EAAEQ,yBAAyB4D,IAAI,UAGuB,CACtDC,YAAa,SAAS3D,WAClB,OAAO,IAAID,YAAYC,UAC3B,EAER"}
\ No newline at end of file
diff --git a/admin/tool/dataprivacy/amd/build/add_purpose.min.js.map b/admin/tool/dataprivacy/amd/build/add_purpose.min.js.map
index 6b9b9f5b460..4169faa6e5c 100644
--- a/admin/tool/dataprivacy/amd/build/add_purpose.min.js.map
+++ b/admin/tool/dataprivacy/amd/build/add_purpose.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"add_purpose.min.js","sources":["../src/add_purpose.js"],"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 * Module to add purposes.\n *\n * @module tool_dataprivacy/add_purpose\n * @copyright 2018 David Monllao\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine([\n 'jquery',\n 'core/str',\n 'core/ajax',\n 'core/notification',\n 'core/modal_factory',\n 'core/modal_events',\n 'core/fragment',\n 'core_form/changechecker',\n], function(\n $,\n Str,\n Ajax,\n Notification,\n ModalFactory,\n ModalEvents,\n Fragment,\n FormChangeChecker\n) {\n\n var SELECTORS = {\n PURPOSE_LINK: '[data-add-element=\"purpose\"]',\n };\n\n var AddPurpose = function(contextId) {\n this.contextId = contextId;\n\n var stringKeys = [\n {\n key: 'addpurpose',\n component: 'tool_dataprivacy'\n },\n {\n key: 'save',\n component: 'admin'\n }\n ];\n this.strings = Str.get_strings(stringKeys);\n\n this.registerEventListeners();\n };\n\n /**\n * @var {int} contextId\n * @private\n */\n AddPurpose.prototype.contextId = 0;\n\n /**\n * @var {Promise}\n * @private\n */\n AddPurpose.prototype.strings = 0;\n\n AddPurpose.prototype.registerEventListeners = function() {\n\n var trigger = $(SELECTORS.PURPOSE_LINK);\n trigger.on('click', function() {\n return this.strings.then(function(strings) {\n ModalFactory.create({\n type: ModalFactory.types.SAVE_CANCEL,\n title: strings[0],\n body: '',\n }, trigger).done(function(modal) {\n this.setupFormModal(modal, strings[1]);\n }.bind(this));\n }.bind(this))\n .fail(Notification.exception);\n }.bind(this));\n\n };\n\n /**\n * @method getBody\n * @param {Object} formdata\n * @private\n * @return {Promise}\n */\n AddPurpose.prototype.getBody = function(formdata) {\n\n var params = null;\n if (typeof formdata !== \"undefined\") {\n params = {jsonformdata: JSON.stringify(formdata)};\n }\n // Get the content of the modal.\n return Fragment.loadFragment('tool_dataprivacy', 'addpurpose_form', this.contextId, params);\n };\n\n AddPurpose.prototype.setupFormModal = function(modal, saveText) {\n modal.setLarge();\n\n modal.setSaveButtonText(saveText);\n\n // We want to reset the form every time it is opened.\n modal.getRoot().on(ModalEvents.hidden, this.destroy.bind(this));\n\n modal.setBody(this.getBody());\n\n // We catch the modal save event, and use it to submit the form inside the modal.\n // Triggering a form submission will give JS validation scripts a chance to check for errors.\n modal.getRoot().on(ModalEvents.save, this.submitForm.bind(this));\n // We also catch the form submit event and use it to submit the form with ajax.\n modal.getRoot().on('submit', 'form', this.submitFormAjax.bind(this));\n\n this.modal = modal;\n\n modal.show();\n };\n\n /**\n * This triggers a form submission, so that any mform elements can do final tricks before the form submission is processed.\n *\n * @method submitForm\n * @param {Event} e Form submission event.\n * @private\n */\n AddPurpose.prototype.submitForm = function(e) {\n e.preventDefault();\n this.modal.getRoot().find('form').submit();\n };\n\n AddPurpose.prototype.submitFormAjax = function(e) {\n // We don't want to do a real form submission.\n e.preventDefault();\n\n // Convert all the form elements values to a serialised string.\n var formData = this.modal.getRoot().find('form').serialize();\n\n Ajax.call([{\n methodname: 'tool_dataprivacy_create_purpose_form',\n args: {jsonformdata: JSON.stringify(formData)},\n done: function(data) {\n if (data.validationerrors) {\n this.modal.setBody(this.getBody(formData));\n } else {\n this.close();\n }\n }.bind(this),\n\n fail: Notification.exception\n }]);\n };\n\n AddPurpose.prototype.close = function() {\n this.destroy();\n document.location.reload();\n };\n\n AddPurpose.prototype.destroy = function() {\n FormChangeChecker.resetAllFormDirtyStates();\n this.modal.destroy();\n };\n\n AddPurpose.prototype.removeListeners = function() {\n $(SELECTORS.PURPOSE_LINK).off('click');\n };\n\n return /** @alias module:tool_dataprivacy/add_purpose */ {\n getInstance: function(contextId) {\n return new AddPurpose(contextId);\n }\n };\n }\n);\n\n"],"names":["define","$","Str","Ajax","Notification","ModalFactory","ModalEvents","Fragment","FormChangeChecker","SELECTORS","AddPurpose","contextId","strings","get_strings","key","component","registerEventListeners","prototype","trigger","on","this","then","create","type","types","SAVE_CANCEL","title","body","done","modal","setupFormModal","bind","fail","exception","getBody","formdata","params","jsonformdata","JSON","stringify","loadFragment","saveText","setLarge","setSaveButtonText","getRoot","hidden","destroy","setBody","save","submitForm","submitFormAjax","show","e","preventDefault","find","submit","formData","serialize","call","methodname","args","data","validationerrors","close","document","location","reload","resetAllFormDirtyStates","removeListeners","off","getInstance"],"mappings":";;;;;;;AAsBAA,sCAAO,CACH,SACA,WACA,YACA,oBACA,qBACA,oBACA,gBACA,4BACD,SACCC,EACAC,IACAC,KACAC,aACAC,aACAC,YACAC,SACAC,uBAGQC,uBACc,+BAGdC,WAAa,SAASC,gBACjBA,UAAYA,eAYZC,QAAUV,IAAIW,YAVF,CACb,CACIC,IAAK,aACLC,UAAW,oBAEf,CACID,IAAK,OACLC,UAAW,gBAKdC,iCAOTN,WAAWO,UAAUN,UAAY,EAMjCD,WAAWO,UAAUL,QAAU,EAE/BF,WAAWO,UAAUD,uBAAyB,eAEtCE,QAAUjB,EAAEQ,wBAChBS,QAAQC,GAAG,QAAS,kBACTC,KAAKR,QAAQS,KAAK,SAAST,SAC9BP,aAAaiB,OAAO,CAChBC,KAAMlB,aAAamB,MAAMC,YACzBC,MAAOd,QAAQ,GACfe,KAAM,IACPT,SAASU,KAAK,SAASC,YACjBC,eAAeD,MAAOjB,QAAQ,KACrCmB,KAAKX,QACTW,KAAKX,OACNY,KAAK5B,aAAa6B,YACrBF,KAAKX,QAUXV,WAAWO,UAAUiB,QAAU,SAASC,cAEhCC,OAAS,iBACW,IAAbD,WACPC,OAAS,CAACC,aAAcC,KAAKC,UAAUJ,YAGpC5B,SAASiC,aAAa,mBAAoB,kBAAmBpB,KAAKT,UAAWyB,SAGxF1B,WAAWO,UAAUa,eAAiB,SAASD,MAAOY,UAClDZ,MAAMa,WAENb,MAAMc,kBAAkBF,UAGxBZ,MAAMe,UAAUzB,GAAGb,YAAYuC,OAAQzB,KAAK0B,QAAQf,KAAKX,OAEzDS,MAAMkB,QAAQ3B,KAAKc,WAInBL,MAAMe,UAAUzB,GAAGb,YAAY0C,KAAM5B,KAAK6B,WAAWlB,KAAKX,OAE1DS,MAAMe,UAAUzB,GAAG,SAAU,OAAQC,KAAK8B,eAAenB,KAAKX,YAEzDS,MAAQA,MAEbA,MAAMsB,QAUVzC,WAAWO,UAAUgC,WAAa,SAASG,GACvCA,EAAEC,sBACGxB,MAAMe,UAAUU,KAAK,QAAQC,UAGtC7C,WAAWO,UAAUiC,eAAiB,SAASE,GAE3CA,EAAEC,qBAGEG,SAAWpC,KAAKS,MAAMe,UAAUU,KAAK,QAAQG,YAEjDtD,KAAKuD,KAAK,CAAC,CACPC,WAAY,uCACZC,KAAM,CAACvB,aAAcC,KAAKC,UAAUiB,WACpC5B,KAAM,SAASiC,MACPA,KAAKC,sBACAjC,MAAMkB,QAAQ3B,KAAKc,QAAQsB,gBAE3BO,SAEXhC,KAAKX,MAEPY,KAAM5B,aAAa6B,cAI3BvB,WAAWO,UAAU8C,MAAQ,gBACpBjB,UACLkB,SAASC,SAASC,UAGtBxD,WAAWO,UAAU6B,QAAU,WAC3BtC,kBAAkB2D,+BACbtC,MAAMiB,WAGfpC,WAAWO,UAAUmD,gBAAkB,WACnCnE,EAAEQ,wBAAwB4D,IAAI,UAGuB,CACrDC,YAAa,SAAS3D,kBACX,IAAID,WAAWC"}
\ No newline at end of file
+{"version":3,"file":"add_purpose.min.js","sources":["../src/add_purpose.js"],"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 * Module to add purposes.\n *\n * @module tool_dataprivacy/add_purpose\n * @copyright 2018 David Monllao\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine([\n 'jquery',\n 'core/str',\n 'core/ajax',\n 'core/notification',\n 'core/modal_factory',\n 'core/modal_events',\n 'core/fragment',\n 'core_form/changechecker',\n], function(\n $,\n Str,\n Ajax,\n Notification,\n ModalFactory,\n ModalEvents,\n Fragment,\n FormChangeChecker\n) {\n\n var SELECTORS = {\n PURPOSE_LINK: '[data-add-element=\"purpose\"]',\n };\n\n var AddPurpose = function(contextId) {\n this.contextId = contextId;\n\n var stringKeys = [\n {\n key: 'addpurpose',\n component: 'tool_dataprivacy'\n },\n {\n key: 'save',\n component: 'admin'\n }\n ];\n this.strings = Str.get_strings(stringKeys);\n\n this.registerEventListeners();\n };\n\n /**\n * @var {int} contextId\n * @private\n */\n AddPurpose.prototype.contextId = 0;\n\n /**\n * @var {Promise}\n * @private\n */\n AddPurpose.prototype.strings = 0;\n\n AddPurpose.prototype.registerEventListeners = function() {\n\n var trigger = $(SELECTORS.PURPOSE_LINK);\n trigger.on('click', function() {\n return this.strings.then(function(strings) {\n ModalFactory.create({\n type: ModalFactory.types.SAVE_CANCEL,\n title: strings[0],\n body: '',\n }, trigger).done(function(modal) {\n this.setupFormModal(modal, strings[1]);\n }.bind(this));\n }.bind(this))\n .fail(Notification.exception);\n }.bind(this));\n\n };\n\n /**\n * @method getBody\n * @param {Object} formdata\n * @private\n * @return {Promise}\n */\n AddPurpose.prototype.getBody = function(formdata) {\n\n var params = null;\n if (typeof formdata !== \"undefined\") {\n params = {jsonformdata: JSON.stringify(formdata)};\n }\n // Get the content of the modal.\n return Fragment.loadFragment('tool_dataprivacy', 'addpurpose_form', this.contextId, params);\n };\n\n AddPurpose.prototype.setupFormModal = function(modal, saveText) {\n modal.setLarge();\n\n modal.setSaveButtonText(saveText);\n\n // We want to reset the form every time it is opened.\n modal.getRoot().on(ModalEvents.hidden, this.destroy.bind(this));\n\n modal.setBody(this.getBody());\n\n // We catch the modal save event, and use it to submit the form inside the modal.\n // Triggering a form submission will give JS validation scripts a chance to check for errors.\n modal.getRoot().on(ModalEvents.save, this.submitForm.bind(this));\n // We also catch the form submit event and use it to submit the form with ajax.\n modal.getRoot().on('submit', 'form', this.submitFormAjax.bind(this));\n\n this.modal = modal;\n\n modal.show();\n };\n\n /**\n * This triggers a form submission, so that any mform elements can do final tricks before the form submission is processed.\n *\n * @method submitForm\n * @param {Event} e Form submission event.\n * @private\n */\n AddPurpose.prototype.submitForm = function(e) {\n e.preventDefault();\n this.modal.getRoot().find('form').submit();\n };\n\n AddPurpose.prototype.submitFormAjax = function(e) {\n // We don't want to do a real form submission.\n e.preventDefault();\n\n // Convert all the form elements values to a serialised string.\n var formData = this.modal.getRoot().find('form').serialize();\n\n Ajax.call([{\n methodname: 'tool_dataprivacy_create_purpose_form',\n args: {jsonformdata: JSON.stringify(formData)},\n done: function(data) {\n if (data.validationerrors) {\n this.modal.setBody(this.getBody(formData));\n } else {\n this.close();\n }\n }.bind(this),\n\n fail: Notification.exception\n }]);\n };\n\n AddPurpose.prototype.close = function() {\n this.destroy();\n document.location.reload();\n };\n\n AddPurpose.prototype.destroy = function() {\n FormChangeChecker.resetAllFormDirtyStates();\n this.modal.destroy();\n };\n\n AddPurpose.prototype.removeListeners = function() {\n $(SELECTORS.PURPOSE_LINK).off('click');\n };\n\n return /** @alias module:tool_dataprivacy/add_purpose */ {\n getInstance: function(contextId) {\n return new AddPurpose(contextId);\n }\n };\n }\n);\n\n"],"names":["define","$","Str","Ajax","Notification","ModalFactory","ModalEvents","Fragment","FormChangeChecker","SELECTORS","AddPurpose","contextId","this","strings","get_strings","key","component","registerEventListeners","prototype","trigger","on","then","create","type","types","SAVE_CANCEL","title","body","done","modal","setupFormModal","bind","fail","exception","getBody","formdata","params","jsonformdata","JSON","stringify","loadFragment","saveText","setLarge","setSaveButtonText","getRoot","hidden","destroy","setBody","save","submitForm","submitFormAjax","show","e","preventDefault","find","submit","formData","serialize","call","methodname","args","data","validationerrors","close","document","location","reload","resetAllFormDirtyStates","removeListeners","off","getInstance"],"mappings":";;;;;;;AAsBAA,OAAO,+BAAA,CACH,SACA,WACA,YACA,oBACA,qBACA,oBACA,gBACA,4BACD,SACCC,EACAC,IACAC,KACAC,aACAC,aACAC,YACAC,SACAC,mBAGI,IAAIC,uBACc,+BAGdC,WAAa,SAASC,WACtBC,KAAKD,UAAYA,UAYjBC,KAAKC,QAAUX,IAAIY,YAVF,CACb,CACIC,IAAK,aACLC,UAAW,oBAEf,CACID,IAAK,OACLC,UAAW,WAKnBJ,KAAKK,0BAsHT,OA/GAP,WAAWQ,UAAUP,UAAY,EAMjCD,WAAWQ,UAAUL,QAAU,EAE/BH,WAAWQ,UAAUD,uBAAyB,WAE1C,IAAIE,QAAUlB,EAAEQ,wBAChBU,QAAQC,GAAG,QAAS,WAChB,OAAOR,KAAKC,QAAQQ,KAAK,SAASR,SAC9BR,aAAaiB,OAAO,CAChBC,KAAMlB,aAAamB,MAAMC,YACzBC,MAAOb,QAAQ,GACfc,KAAM,IACPR,SAASS,KAAK,SAASC,OACtBjB,KAAKkB,eAAeD,MAAOhB,QAAQ,GACvC,EAAEkB,KAAKnB,MACX,EAAEmB,KAAKnB,OACNoB,KAAK5B,aAAa6B,UACvB,EAAEF,KAAKnB,QAUXF,WAAWQ,UAAUgB,QAAU,SAASC,UAEpC,IAAIC,OAAS,KAKb,YAJwB,IAAbD,WACPC,OAAS,CAACC,aAAcC,KAAKC,UAAUJ,YAGpC5B,SAASiC,aAAa,mBAAoB,kBAAmB5B,KAAKD,UAAWyB,SAGxF1B,WAAWQ,UAAUY,eAAiB,SAASD,MAAOY,UAClDZ,MAAMa,WAENb,MAAMc,kBAAkBF,UAGxBZ,MAAMe,UAAUxB,GAAGd,YAAYuC,OAAQjC,KAAKkC,QAAQf,KAAKnB,OAEzDiB,MAAMkB,QAAQnC,KAAKsB,WAInBL,MAAMe,UAAUxB,GAAGd,YAAY0C,KAAMpC,KAAKqC,WAAWlB,KAAKnB,OAE1DiB,MAAMe,UAAUxB,GAAG,SAAU,OAAQR,KAAKsC,eAAenB,KAAKnB,OAE9DA,KAAKiB,MAAQA,MAEbA,MAAMsB,QAUVzC,WAAWQ,UAAU+B,WAAa,SAASG,GACvCA,EAAEC,iBACFzC,KAAKiB,MAAMe,UAAUU,KAAK,QAAQC,UAGtC7C,WAAWQ,UAAUgC,eAAiB,SAASE,GAE3CA,EAAEC,iBAGF,IAAIG,SAAW5C,KAAKiB,MAAMe,UAAUU,KAAK,QAAQG,YAEjDtD,KAAKuD,KAAK,CAAC,CACPC,WAAY,uCACZC,KAAM,CAACvB,aAAcC,KAAKC,UAAUiB,WACpC5B,KAAM,SAASiC,MACPA,KAAKC,iBACLlD,KAAKiB,MAAMkB,QAAQnC,KAAKsB,QAAQsB,WAEhC5C,KAAKmD,OAEb,EAAEhC,KAAKnB,MAEPoB,KAAM5B,aAAa6B,cAI3BvB,WAAWQ,UAAU6C,MAAQ,WACzBnD,KAAKkC,UACLkB,SAASC,SAASC,UAGtBxD,WAAWQ,UAAU4B,QAAU,WAC3BtC,kBAAkB2D,0BAClBvD,KAAKiB,MAAMiB,WAGfpC,WAAWQ,UAAUkD,gBAAkB,WACnCnE,EAAEQ,wBAAwB4D,IAAI,UAGuB,CACrDC,YAAa,SAAS3D,WAClB,OAAO,IAAID,WAAWC,UAC1B,EAER"}
\ No newline at end of file
diff --git a/admin/tool/dataprivacy/amd/build/categoriesactions.min.js.map b/admin/tool/dataprivacy/amd/build/categoriesactions.min.js.map
index 683666ec3bb..4b3d57ffe59 100644
--- a/admin/tool/dataprivacy/amd/build/categoriesactions.min.js.map
+++ b/admin/tool/dataprivacy/amd/build/categoriesactions.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"categoriesactions.min.js","sources":["../src/categoriesactions.js"],"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 * AMD module for categories actions.\n *\n * @module tool_dataprivacy/categoriesactions\n * @copyright 2018 David Monllao\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine([\n 'jquery',\n 'core/ajax',\n 'core/notification',\n 'core/str',\n 'core/modal_factory',\n 'core/modal_events'],\nfunction($, Ajax, Notification, Str, ModalFactory, ModalEvents) {\n\n /**\n * List of action selectors.\n *\n * @type {{DELETE: string}}\n */\n var ACTIONS = {\n DELETE: '[data-action=\"deletecategory\"]',\n };\n\n /**\n * CategoriesActions class.\n */\n var CategoriesActions = function() {\n this.registerEvents();\n };\n\n /**\n * Register event listeners.\n */\n CategoriesActions.prototype.registerEvents = function() {\n $(ACTIONS.DELETE).click(function(e) {\n e.preventDefault();\n\n var id = $(this).data('id');\n var categoryname = $(this).data('name');\n var stringkeys = [\n {\n key: 'deletecategory',\n component: 'tool_dataprivacy'\n },\n {\n key: 'deletecategorytext',\n component: 'tool_dataprivacy',\n param: categoryname\n },\n {\n key: 'delete'\n }\n ];\n\n Str.get_strings(stringkeys).then(function(langStrings) {\n var title = langStrings[0];\n var confirmMessage = langStrings[1];\n var buttonText = langStrings[2];\n return ModalFactory.create({\n title: title,\n body: confirmMessage,\n type: ModalFactory.types.SAVE_CANCEL\n }).then(function(modal) {\n modal.setSaveButtonText(buttonText);\n\n // Handle save event.\n modal.getRoot().on(ModalEvents.save, function() {\n\n var request = {\n methodname: 'tool_dataprivacy_delete_category',\n args: {'id': id}\n };\n\n Ajax.call([request])[0].done(function(data) {\n if (data.result) {\n $('tr[data-categoryid=\"' + id + '\"]').remove();\n } else {\n Notification.addNotification({\n message: data.warnings[0].message,\n type: 'error'\n });\n }\n }).fail(Notification.exception);\n });\n\n // Handle hidden event.\n modal.getRoot().on(ModalEvents.hidden, function() {\n // Destroy when hidden.\n modal.destroy();\n });\n\n return modal;\n });\n }).done(function(modal) {\n modal.show();\n\n }).fail(Notification.exception);\n });\n };\n\n return /** @alias module:tool_dataprivacy/categoriesactions */ {\n // Public variables and functions.\n\n /**\n * Initialise the module.\n *\n * @method init\n * @return {CategoriesActions}\n */\n 'init': function() {\n return new CategoriesActions();\n }\n };\n});\n"],"names":["define","$","Ajax","Notification","Str","ModalFactory","ModalEvents","ACTIONS","CategoriesActions","registerEvents","prototype","click","e","preventDefault","id","this","data","stringkeys","key","component","param","get_strings","then","langStrings","title","confirmMessage","buttonText","create","body","type","types","SAVE_CANCEL","modal","setSaveButtonText","getRoot","on","save","request","methodname","args","call","done","result","remove","addNotification","message","warnings","fail","exception","hidden","destroy","show"],"mappings":";;;;;;;AAsBAA,4CAAO,CACH,SACA,YACA,oBACA,WACA,qBACA,sBACJ,SAASC,EAAGC,KAAMC,aAAcC,IAAKC,aAAcC,iBAO3CC,eACQ,iCAMRC,kBAAoB,gBACfC,yBAMTD,kBAAkBE,UAAUD,eAAiB,WACzCR,EAAEM,gBAAgBI,OAAM,SAASC,GAC7BA,EAAEC,qBAEEC,GAAKb,EAAEc,MAAMC,KAAK,MAElBC,WAAa,CACb,CACIC,IAAK,iBACLC,UAAW,oBAEf,CACID,IAAK,qBACLC,UAAW,mBACXC,MATWnB,EAAEc,MAAMC,KAAK,SAW5B,CACIE,IAAK,WAIbd,IAAIiB,YAAYJ,YAAYK,MAAK,SAASC,iBAClCC,MAAQD,YAAY,GACpBE,eAAiBF,YAAY,GAC7BG,WAAaH,YAAY,UACtBlB,aAAasB,OAAO,CACvBH,MAAOA,MACPI,KAAMH,eACNI,KAAMxB,aAAayB,MAAMC,cAC1BT,MAAK,SAASU,cACbA,MAAMC,kBAAkBP,YAGxBM,MAAME,UAAUC,GAAG7B,YAAY8B,MAAM,eAE7BC,QAAU,CACVC,WAAY,mCACZC,KAAM,IAAOzB,KAGjBZ,KAAKsC,KAAK,CAACH,UAAU,GAAGI,MAAK,SAASzB,MAC9BA,KAAK0B,OACLzC,EAAE,uBAAyBa,GAAK,MAAM6B,SAEtCxC,aAAayC,gBAAgB,CACzBC,QAAS7B,KAAK8B,SAAS,GAAGD,QAC1BhB,KAAM,aAGfkB,KAAK5C,aAAa6C,cAIzBhB,MAAME,UAAUC,GAAG7B,YAAY2C,QAAQ,WAEnCjB,MAAMkB,aAGHlB,YAEZS,MAAK,SAAST,OACbA,MAAMmB,UAEPJ,KAAK5C,aAAa6C,eAIkC,MASnD,kBACG,IAAIxC"}
\ No newline at end of file
+{"version":3,"file":"categoriesactions.min.js","sources":["../src/categoriesactions.js"],"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 * AMD module for categories actions.\n *\n * @module tool_dataprivacy/categoriesactions\n * @copyright 2018 David Monllao\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine([\n 'jquery',\n 'core/ajax',\n 'core/notification',\n 'core/str',\n 'core/modal_factory',\n 'core/modal_events'],\nfunction($, Ajax, Notification, Str, ModalFactory, ModalEvents) {\n\n /**\n * List of action selectors.\n *\n * @type {{DELETE: string}}\n */\n var ACTIONS = {\n DELETE: '[data-action=\"deletecategory\"]',\n };\n\n /**\n * CategoriesActions class.\n */\n var CategoriesActions = function() {\n this.registerEvents();\n };\n\n /**\n * Register event listeners.\n */\n CategoriesActions.prototype.registerEvents = function() {\n $(ACTIONS.DELETE).click(function(e) {\n e.preventDefault();\n\n var id = $(this).data('id');\n var categoryname = $(this).data('name');\n var stringkeys = [\n {\n key: 'deletecategory',\n component: 'tool_dataprivacy'\n },\n {\n key: 'deletecategorytext',\n component: 'tool_dataprivacy',\n param: categoryname\n },\n {\n key: 'delete'\n }\n ];\n\n Str.get_strings(stringkeys).then(function(langStrings) {\n var title = langStrings[0];\n var confirmMessage = langStrings[1];\n var buttonText = langStrings[2];\n return ModalFactory.create({\n title: title,\n body: confirmMessage,\n type: ModalFactory.types.SAVE_CANCEL\n }).then(function(modal) {\n modal.setSaveButtonText(buttonText);\n\n // Handle save event.\n modal.getRoot().on(ModalEvents.save, function() {\n\n var request = {\n methodname: 'tool_dataprivacy_delete_category',\n args: {'id': id}\n };\n\n Ajax.call([request])[0].done(function(data) {\n if (data.result) {\n $('tr[data-categoryid=\"' + id + '\"]').remove();\n } else {\n Notification.addNotification({\n message: data.warnings[0].message,\n type: 'error'\n });\n }\n }).fail(Notification.exception);\n });\n\n // Handle hidden event.\n modal.getRoot().on(ModalEvents.hidden, function() {\n // Destroy when hidden.\n modal.destroy();\n });\n\n return modal;\n });\n }).done(function(modal) {\n modal.show();\n\n }).fail(Notification.exception);\n });\n };\n\n return /** @alias module:tool_dataprivacy/categoriesactions */ {\n // Public variables and functions.\n\n /**\n * Initialise the module.\n *\n * @method init\n * @return {CategoriesActions}\n */\n 'init': function() {\n return new CategoriesActions();\n }\n };\n});\n"],"names":["define","$","Ajax","Notification","Str","ModalFactory","ModalEvents","ACTIONS","CategoriesActions","this","registerEvents","prototype","click","e","preventDefault","id","data","stringkeys","key","component","param","get_strings","then","langStrings","title","confirmMessage","buttonText","create","body","type","types","SAVE_CANCEL","modal","setSaveButtonText","getRoot","on","save","request","methodname","args","call","done","result","remove","addNotification","message","warnings","fail","exception","hidden","destroy","show","init"],"mappings":";;;;;;;AAsBAA,OAAM,qCAAC,CACH,SACA,YACA,oBACA,WACA,qBACA,sBACJ,SAASC,EAAGC,KAAMC,aAAcC,IAAKC,aAAcC,aAO/C,IAAIC,eACQ,iCAMRC,kBAAoB,WACpBC,KAAKC,kBAyET,OAnEAF,kBAAkBG,UAAUD,eAAiB,WACzCT,EAAEM,gBAAgBK,OAAM,SAASC,GAC7BA,EAAEC,iBAEF,IAAIC,GAAKd,EAAEQ,MAAMO,KAAK,MAElBC,WAAa,CACb,CACIC,IAAK,iBACLC,UAAW,oBAEf,CACID,IAAK,qBACLC,UAAW,mBACXC,MATWnB,EAAEQ,MAAMO,KAAK,SAW5B,CACIE,IAAK,WAIbd,IAAIiB,YAAYJ,YAAYK,MAAK,SAASC,aACtC,IAAIC,MAAQD,YAAY,GACpBE,eAAiBF,YAAY,GAC7BG,WAAaH,YAAY,GAC7B,OAAOlB,aAAasB,OAAO,CACvBH,MAAOA,MACPI,KAAMH,eACNI,KAAMxB,aAAayB,MAAMC,cAC1BT,MAAK,SAASU,OA6Bb,OA5BAA,MAAMC,kBAAkBP,YAGxBM,MAAME,UAAUC,GAAG7B,YAAY8B,MAAM,WAEjC,IAAIC,QAAU,CACVC,WAAY,mCACZC,KAAM,CAACxB,GAAMA,KAGjBb,KAAKsC,KAAK,CAACH,UAAU,GAAGI,MAAK,SAASzB,MAC9BA,KAAK0B,OACLzC,EAAE,uBAAyBc,GAAK,MAAM4B,SAEtCxC,aAAayC,gBAAgB,CACzBC,QAAS7B,KAAK8B,SAAS,GAAGD,QAC1BhB,KAAM,SAGjB,IAAEkB,KAAK5C,aAAa6C,UACzB,IAGAhB,MAAME,UAAUC,GAAG7B,YAAY2C,QAAQ,WAEnCjB,MAAMkB,SACV,IAEOlB,KACX,GACJ,IAAGS,MAAK,SAAST,OACbA,MAAMmB,MAET,IAAEJ,KAAK5C,aAAa6C,UACzB,KAG2D,CAS3DI,KAAQ,WACJ,OAAO,IAAI5C,iBACf,EAER"}
\ No newline at end of file
diff --git a/admin/tool/dataprivacy/amd/build/contactdpo.min.js.map b/admin/tool/dataprivacy/amd/build/contactdpo.min.js.map
index 680d7bb355d..1d68f9e367f 100644
--- a/admin/tool/dataprivacy/amd/build/contactdpo.min.js.map
+++ b/admin/tool/dataprivacy/amd/build/contactdpo.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"contactdpo.min.js","sources":["../src/contactdpo.js"],"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 module for contacting the site DPO\n *\n * @module tool_dataprivacy/contactdpo\n * @copyright 2021 Paul Holden \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport ModalForm from 'core_form/modalform';\nimport Notification from 'core/notification';\nimport {get_string as getString} from 'core/str';\nimport {add as addToast} from 'core/toast';\n\nconst SELECTORS = {\n CONTACT_DPO: '[data-action=\"contactdpo\"]',\n};\n\n/**\n * Initialize module\n */\nexport const init = () => {\n const triggerElement = document.querySelector(SELECTORS.CONTACT_DPO);\n\n triggerElement.addEventListener('click', event => {\n event.preventDefault();\n\n const modalForm = new ModalForm({\n modalConfig: {\n title: getString('contactdataprotectionofficer', 'tool_dataprivacy'),\n },\n formClass: 'tool_dataprivacy\\\\form\\\\contactdpo',\n saveButtonText: getString('send', 'tool_dataprivacy'),\n returnFocus: triggerElement,\n });\n\n // Show a toast notification when the form is submitted.\n modalForm.addEventListener(modalForm.events.FORM_SUBMITTED, event => {\n if (event.detail.result) {\n getString('requestsubmitted', 'tool_dataprivacy').then(addToast).catch();\n } else {\n const warningMessages = event.detail.warnings.map(warning => warning.message);\n Notification.addNotification({\n type: 'error',\n message: warningMessages.join(' ')\n });\n }\n });\n\n modalForm.show();\n });\n};\n"],"names":["SELECTORS","triggerElement","document","querySelector","addEventListener","event","preventDefault","modalForm","ModalForm","modalConfig","title","formClass","saveButtonText","returnFocus","events","FORM_SUBMITTED","detail","result","then","addToast","catch","warningMessages","warnings","map","warning","message","addNotification","type","join","show"],"mappings":";;;;;;;0LA4BMA,sBACW,2CAMG,WACVC,eAAiBC,SAASC,cAAcH,uBAE9CC,eAAeG,iBAAiB,SAASC,QACrCA,MAAMC,uBAEAC,UAAY,IAAIC,mBAAU,CAC5BC,YAAa,CACTC,OAAO,mBAAU,+BAAgC,qBAErDC,UAAW,qCACXC,gBAAgB,mBAAU,OAAQ,oBAClCC,YAAaZ,iBAIjBM,UAAUH,iBAAiBG,UAAUO,OAAOC,gBAAgBV,WACpDA,MAAMW,OAAOC,2BACH,mBAAoB,oBAAoBC,KAAKC,YAAUC,YAC9D,OACGC,gBAAkBhB,MAAMW,OAAOM,SAASC,KAAIC,SAAWA,QAAQC,gCACxDC,gBAAgB,CACzBC,KAAM,QACNF,QAASJ,gBAAgBO,KAAK,cAK1CrB,UAAUsB"}
\ No newline at end of file
+{"version":3,"file":"contactdpo.min.js","sources":["../src/contactdpo.js"],"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 module for contacting the site DPO\n *\n * @module tool_dataprivacy/contactdpo\n * @copyright 2021 Paul Holden \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport ModalForm from 'core_form/modalform';\nimport Notification from 'core/notification';\nimport {get_string as getString} from 'core/str';\nimport {add as addToast} from 'core/toast';\n\nconst SELECTORS = {\n CONTACT_DPO: '[data-action=\"contactdpo\"]',\n};\n\n/**\n * Initialize module\n */\nexport const init = () => {\n const triggerElement = document.querySelector(SELECTORS.CONTACT_DPO);\n\n triggerElement.addEventListener('click', event => {\n event.preventDefault();\n\n const modalForm = new ModalForm({\n modalConfig: {\n title: getString('contactdataprotectionofficer', 'tool_dataprivacy'),\n },\n formClass: 'tool_dataprivacy\\\\form\\\\contactdpo',\n saveButtonText: getString('send', 'tool_dataprivacy'),\n returnFocus: triggerElement,\n });\n\n // Show a toast notification when the form is submitted.\n modalForm.addEventListener(modalForm.events.FORM_SUBMITTED, event => {\n if (event.detail.result) {\n getString('requestsubmitted', 'tool_dataprivacy').then(addToast).catch();\n } else {\n const warningMessages = event.detail.warnings.map(warning => warning.message);\n Notification.addNotification({\n type: 'error',\n message: warningMessages.join(' ')\n });\n }\n });\n\n modalForm.show();\n });\n};\n"],"names":["_interopRequireDefault","obj","__esModule","default","_modalform","_notification","SELECTORS","_exports","init","triggerElement","document","querySelector","addEventListener","event","preventDefault","modalForm","ModalForm","modalConfig","title","getString","formClass","saveButtonText","returnFocus","events","FORM_SUBMITTED","detail","result","get_string","then","addToast","catch","warningMessages","warnings","map","warning","message","Notification","addNotification","type","join","show"],"mappings":"4KAwB6C,SAAAA,uBAAAC,KAAAA,OAAAA,KAAAA,IAAAC,WAAAD,IAAAE,CAAAA,QAAAF,IAAA;;;;;;;kFAD7CG,WAAAJ,uBAAAI,YACAC,cAAAL,uBAAAK,eAIA,MAAMC,sBACW,6BAoCfC,SAAAC,KA9BkBA,KAChB,MAAMC,eAAiBC,SAASC,cAAcL,uBAE9CG,eAAeG,iBAAiB,SAASC,QACrCA,MAAMC,iBAEN,MAAMC,UAAY,IAAIC,WAAAA,QAAU,CAC5BC,YAAa,CACTC,OAAO,EAAAC,KAAAA,YAAU,+BAAgC,qBAErDC,UAAW,qCACXC,gBAAgB,EAAAF,KAAAA,YAAU,OAAQ,oBAClCG,YAAab,iBAIjBM,UAAUH,iBAAiBG,UAAUQ,OAAOC,gBAAgBX,QACxD,GAAIA,MAAMY,OAAOC,QACb,EAAAP,KAASQ,YAAC,mBAAoB,oBAAoBC,KAAKC,OAAAA,KAAUC,YAC9D,CACH,MAAMC,gBAAkBlB,MAAMY,OAAOO,SAASC,KAAIC,SAAWA,QAAQC,UACrEC,cAAYjC,QAACkC,gBAAgB,CACzBC,KAAM,QACNH,QAASJ,gBAAgBQ,KAAK,SAEtC,KAGJxB,UAAUyB,MAAM,GAClB,CACJ"}
\ No newline at end of file
diff --git a/admin/tool/dataprivacy/amd/build/data_deletion.min.js.map b/admin/tool/dataprivacy/amd/build/data_deletion.min.js.map
index 79fec21d7e6..0d6cefaf7c0 100644
--- a/admin/tool/dataprivacy/amd/build/data_deletion.min.js.map
+++ b/admin/tool/dataprivacy/amd/build/data_deletion.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"data_deletion.min.js","sources":["../src/data_deletion.js"],"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 * Request actions.\n *\n * @module tool_dataprivacy/data_deletion\n * @copyright 2018 Jun Pataleta\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine([\n 'jquery',\n 'core/ajax',\n 'core/notification',\n 'core/str',\n 'core/modal_factory',\n 'core/modal_events'],\nfunction($, Ajax, Notification, Str, ModalFactory, ModalEvents) {\n\n /**\n * List of action selectors.\n *\n * @type {{MARK_FOR_DELETION: string}}\n * @type {{SELECT_ALL: string}}\n */\n var ACTIONS = {\n MARK_FOR_DELETION: '[data-action=\"markfordeletion\"]',\n SELECT_ALL: '[data-action=\"selectall\"]',\n };\n\n /**\n * List of selectors.\n *\n * @type {{SELECTCONTEXT: string}}\n */\n var SELECTORS = {\n SELECTCONTEXT: '.selectcontext',\n };\n\n /**\n * DataDeletionActions class.\n */\n var DataDeletionActions = function() {\n this.registerEvents();\n };\n\n /**\n * Register event listeners.\n */\n DataDeletionActions.prototype.registerEvents = function() {\n $(ACTIONS.MARK_FOR_DELETION).click(function(e) {\n e.preventDefault();\n\n var selectedIds = [];\n $(SELECTORS.SELECTCONTEXT).each(function() {\n var checkbox = $(this);\n if (checkbox.is(':checked')) {\n selectedIds.push(checkbox.val());\n }\n });\n showConfirmation(selectedIds);\n });\n\n $(ACTIONS.SELECT_ALL).change(function(e) {\n e.preventDefault();\n\n var selectallnone = $(this);\n if (selectallnone.is(':checked')) {\n $(SELECTORS.SELECTCONTEXT).attr('checked', 'checked');\n } else {\n $(SELECTORS.SELECTCONTEXT).removeAttr('checked');\n }\n });\n };\n\n /**\n * Show the confirmation dialogue.\n *\n * @param {Array} ids The array of expired context record IDs.\n */\n function showConfirmation(ids) {\n var keys = [\n {\n key: 'confirm',\n component: 'moodle'\n },\n {\n key: 'confirmcontextdeletion',\n component: 'tool_dataprivacy'\n }\n ];\n var wsfunction = 'tool_dataprivacy_confirm_contexts_for_deletion';\n\n var modalTitle = '';\n Str.get_strings(keys).then(function(langStrings) {\n modalTitle = langStrings[0];\n var confirmMessage = langStrings[1];\n return ModalFactory.create({\n title: modalTitle,\n body: confirmMessage,\n type: ModalFactory.types.SAVE_CANCEL\n });\n }).then(function(modal) {\n modal.setSaveButtonText(modalTitle);\n\n // Handle save event.\n modal.getRoot().on(ModalEvents.save, function() {\n // Confirm the request.\n var params = {\n 'ids': ids\n };\n\n var request = {\n methodname: wsfunction,\n args: params\n };\n\n Ajax.call([request])[0].done(function(data) {\n if (data.result) {\n window.location.reload();\n } else {\n Notification.addNotification({\n message: data.warnings[0].message,\n type: 'error'\n });\n }\n }).fail(Notification.exception);\n });\n\n // Handle hidden event.\n modal.getRoot().on(ModalEvents.hidden, function() {\n // Destroy when hidden.\n modal.destroy();\n });\n\n return modal;\n }).done(function(modal) {\n modal.show();\n }).fail(Notification.exception);\n }\n\n return DataDeletionActions;\n});\n"],"names":["define","$","Ajax","Notification","Str","ModalFactory","ModalEvents","ACTIONS","SELECTORS","DataDeletionActions","registerEvents","prototype","click","e","preventDefault","ids","keys","wsfunction","modalTitle","selectedIds","each","checkbox","this","is","push","val","key","component","get_strings","then","langStrings","confirmMessage","create","title","body","type","types","SAVE_CANCEL","modal","setSaveButtonText","getRoot","on","save","request","methodname","args","call","done","data","result","window","location","reload","addNotification","message","warnings","fail","exception","hidden","destroy","show","change","attr","removeAttr"],"mappings":";;;;;;;AAsBAA,wCAAO,CACH,SACA,YACA,oBACA,WACA,qBACA,sBACJ,SAASC,EAAGC,KAAMC,aAAcC,IAAKC,aAAcC,iBAQ3CC,0BACmB,kCADnBA,mBAEY,4BAQZC,wBACe,iBAMfC,oBAAsB,gBACjBC,yBAMTD,oBAAoBE,UAAUD,eAAiB,WAC3CT,EAAEM,2BAA2BK,OAAM,SAASC,GACxCA,EAAEC,qBA6BgBC,IAClBC,KAUAC,WAEAC,WAxCIC,YAAc,GAClBlB,EAAEO,yBAAyBY,MAAK,eACxBC,SAAWpB,EAAEqB,MACbD,SAASE,GAAG,aACZJ,YAAYK,KAAKH,SAASI,UAuBhBV,IApBDI,YAqBjBH,KAAO,CACP,CACIU,IAAK,UACLC,UAAW,UAEf,CACID,IAAK,yBACLC,UAAW,qBAGfV,WAAa,iDAEbC,WAAa,GACjBd,IAAIwB,YAAYZ,MAAMa,MAAK,SAASC,aAChCZ,WAAaY,YAAY,OACrBC,eAAiBD,YAAY,UAC1BzB,aAAa2B,OAAO,CACvBC,MAAOf,WACPgB,KAAMH,eACNI,KAAM9B,aAAa+B,MAAMC,iBAE9BR,MAAK,SAASS,cACbA,MAAMC,kBAAkBrB,YAGxBoB,MAAME,UAAUC,GAAGnC,YAAYoC,MAAM,eAM7BC,QAAU,CACVC,WAAY3B,WACZ4B,KANS,KACF9B,MAQXb,KAAK4C,KAAK,CAACH,UAAU,GAAGI,MAAK,SAASC,MAC9BA,KAAKC,OACLC,OAAOC,SAASC,SAEhBjD,aAAakD,gBAAgB,CACzBC,QAASN,KAAKO,SAAS,GAAGD,QAC1BnB,KAAM,aAGfqB,KAAKrD,aAAasD,cAIzBnB,MAAME,UAAUC,GAAGnC,YAAYoD,QAAQ,WAEnCpB,MAAMqB,aAGHrB,SACRS,MAAK,SAAST,OACbA,MAAMsB,UACPJ,KAAKrD,aAAasD,cA3ErBxD,EAAEM,oBAAoBsD,QAAO,SAAShD,GAClCA,EAAEC,iBAEkBb,EAAEqB,MACJC,GAAG,YACjBtB,EAAEO,yBAAyBsD,KAAK,UAAW,WAE3C7D,EAAEO,yBAAyBuD,WAAW,eAuE3CtD"}
\ No newline at end of file
+{"version":3,"file":"data_deletion.min.js","sources":["../src/data_deletion.js"],"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 * Request actions.\n *\n * @module tool_dataprivacy/data_deletion\n * @copyright 2018 Jun Pataleta\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine([\n 'jquery',\n 'core/ajax',\n 'core/notification',\n 'core/str',\n 'core/modal_factory',\n 'core/modal_events'],\nfunction($, Ajax, Notification, Str, ModalFactory, ModalEvents) {\n\n /**\n * List of action selectors.\n *\n * @type {{MARK_FOR_DELETION: string}}\n * @type {{SELECT_ALL: string}}\n */\n var ACTIONS = {\n MARK_FOR_DELETION: '[data-action=\"markfordeletion\"]',\n SELECT_ALL: '[data-action=\"selectall\"]',\n };\n\n /**\n * List of selectors.\n *\n * @type {{SELECTCONTEXT: string}}\n */\n var SELECTORS = {\n SELECTCONTEXT: '.selectcontext',\n };\n\n /**\n * DataDeletionActions class.\n */\n var DataDeletionActions = function() {\n this.registerEvents();\n };\n\n /**\n * Register event listeners.\n */\n DataDeletionActions.prototype.registerEvents = function() {\n $(ACTIONS.MARK_FOR_DELETION).click(function(e) {\n e.preventDefault();\n\n var selectedIds = [];\n $(SELECTORS.SELECTCONTEXT).each(function() {\n var checkbox = $(this);\n if (checkbox.is(':checked')) {\n selectedIds.push(checkbox.val());\n }\n });\n showConfirmation(selectedIds);\n });\n\n $(ACTIONS.SELECT_ALL).change(function(e) {\n e.preventDefault();\n\n var selectallnone = $(this);\n if (selectallnone.is(':checked')) {\n $(SELECTORS.SELECTCONTEXT).attr('checked', 'checked');\n } else {\n $(SELECTORS.SELECTCONTEXT).removeAttr('checked');\n }\n });\n };\n\n /**\n * Show the confirmation dialogue.\n *\n * @param {Array} ids The array of expired context record IDs.\n */\n function showConfirmation(ids) {\n var keys = [\n {\n key: 'confirm',\n component: 'moodle'\n },\n {\n key: 'confirmcontextdeletion',\n component: 'tool_dataprivacy'\n }\n ];\n var wsfunction = 'tool_dataprivacy_confirm_contexts_for_deletion';\n\n var modalTitle = '';\n Str.get_strings(keys).then(function(langStrings) {\n modalTitle = langStrings[0];\n var confirmMessage = langStrings[1];\n return ModalFactory.create({\n title: modalTitle,\n body: confirmMessage,\n type: ModalFactory.types.SAVE_CANCEL\n });\n }).then(function(modal) {\n modal.setSaveButtonText(modalTitle);\n\n // Handle save event.\n modal.getRoot().on(ModalEvents.save, function() {\n // Confirm the request.\n var params = {\n 'ids': ids\n };\n\n var request = {\n methodname: wsfunction,\n args: params\n };\n\n Ajax.call([request])[0].done(function(data) {\n if (data.result) {\n window.location.reload();\n } else {\n Notification.addNotification({\n message: data.warnings[0].message,\n type: 'error'\n });\n }\n }).fail(Notification.exception);\n });\n\n // Handle hidden event.\n modal.getRoot().on(ModalEvents.hidden, function() {\n // Destroy when hidden.\n modal.destroy();\n });\n\n return modal;\n }).done(function(modal) {\n modal.show();\n }).fail(Notification.exception);\n }\n\n return DataDeletionActions;\n});\n"],"names":["define","$","Ajax","Notification","Str","ModalFactory","ModalEvents","ACTIONS","SELECTORS","DataDeletionActions","this","registerEvents","prototype","click","e","preventDefault","ids","keys","wsfunction","modalTitle","selectedIds","each","checkbox","is","push","val","key","component","get_strings","then","langStrings","confirmMessage","create","title","body","type","types","SAVE_CANCEL","modal","setSaveButtonText","getRoot","on","save","request","methodname","args","call","done","data","result","window","location","reload","addNotification","message","warnings","fail","exception","hidden","destroy","show","change","attr","removeAttr"],"mappings":";;;;;;;AAsBAA,OAAM,iCAAC,CACH,SACA,YACA,oBACA,WACA,qBACA,sBACJ,SAASC,EAAGC,KAAMC,aAAcC,IAAKC,aAAcC,aAQ/C,IAAIC,0BACmB,kCADnBA,mBAEY,4BAQZC,wBACe,iBAMfC,oBAAsB,WACtBC,KAAKC,kBAkGT,OA5FAF,oBAAoBG,UAAUD,eAAiB,WAC3CV,EAAEM,2BAA2BM,OAAM,SAASC,GACxCA,EAAEC,iBAEF,IA2BkBC,IAClBC,KAUAC,WAEAC,WAxCIC,YAAc,GAClBnB,EAAEO,yBAAyBa,MAAK,WAC5B,IAAIC,SAAWrB,EAAES,MACbY,SAASC,GAAG,aACZH,YAAYI,KAAKF,SAASG,MAElC,IAqBkBT,IApBDI,YAqBjBH,KAAO,CACP,CACIS,IAAK,UACLC,UAAW,UAEf,CACID,IAAK,yBACLC,UAAW,qBAGfT,WAAa,iDAEbC,WAAa,GACjBf,IAAIwB,YAAYX,MAAMY,MAAK,SAASC,aAChCX,WAAaW,YAAY,GACzB,IAAIC,eAAiBD,YAAY,GACjC,OAAOzB,aAAa2B,OAAO,CACvBC,MAAOd,WACPe,KAAMH,eACNI,KAAM9B,aAAa+B,MAAMC,aAEjC,IAAGR,MAAK,SAASS,OAiCb,OAhCAA,MAAMC,kBAAkBpB,YAGxBmB,MAAME,UAAUC,GAAGnC,YAAYoC,MAAM,WAEjC,IAIIC,QAAU,CACVC,WAAY1B,WACZ2B,KANS,CACT7B,IAAOA,MAQXd,KAAK4C,KAAK,CAACH,UAAU,GAAGI,MAAK,SAASC,MAC9BA,KAAKC,OACLC,OAAOC,SAASC,SAEhBjD,aAAakD,gBAAgB,CACzBC,QAASN,KAAKO,SAAS,GAAGD,QAC1BnB,KAAM,SAGjB,IAAEqB,KAAKrD,aAAasD,UACzB,IAGAnB,MAAME,UAAUC,GAAGnC,YAAYoD,QAAQ,WAEnCpB,MAAMqB,SACV,IAEOrB,KACX,IAAGS,MAAK,SAAST,OACbA,MAAMsB,MACT,IAAEJ,KAAKrD,aAAasD,UA7ErB,IAEAxD,EAAEM,oBAAoBsD,QAAO,SAAS/C,GAClCA,EAAEC,iBAEkBd,EAAES,MACJa,GAAG,YACjBtB,EAAEO,yBAAyBsD,KAAK,UAAW,WAE3C7D,EAAEO,yBAAyBuD,WAAW,UAE9C,KAqEGtD,mBACX"}
\ No newline at end of file
diff --git a/admin/tool/dataprivacy/amd/build/data_registry.min.js.map b/admin/tool/dataprivacy/amd/build/data_registry.min.js.map
index 235aef226ce..a7b5903fc9c 100644
--- a/admin/tool/dataprivacy/amd/build/data_registry.min.js.map
+++ b/admin/tool/dataprivacy/amd/build/data_registry.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"data_registry.min.js","sources":["../src/data_registry.js"],"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 * Request actions.\n *\n * @module tool_dataprivacy/data_registry\n * @copyright 2018 David Monllao\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(['jquery', 'core/str', 'core/ajax', 'core/notification', 'core/templates', 'core/modal_factory',\n 'core/modal_events', 'core/fragment', 'tool_dataprivacy/add_purpose', 'tool_dataprivacy/add_category'],\n function($, Str, Ajax, Notification, Templates, ModalFactory, ModalEvents, Fragment, AddPurpose, AddCategory) {\n\n var SELECTORS = {\n TREE_NODES: '[data-context-tree-node=1]',\n FORM_CONTAINER: '#context-form-container',\n };\n\n var DataRegistry = function(systemContextId, initContextLevel, initContextId) {\n this.systemContextId = systemContextId;\n this.currentContextLevel = initContextLevel;\n this.currentContextId = initContextId;\n this.init();\n };\n\n /**\n * @var {int} systemContextId\n * @private\n */\n DataRegistry.prototype.systemContextId = 0;\n\n /**\n * @var {int} currentContextLevel\n * @private\n */\n DataRegistry.prototype.currentContextLevel = 0;\n\n /**\n * @var {int} currentContextId\n * @private\n */\n DataRegistry.prototype.currentContextId = 0;\n\n /**\n * @var {AddPurpose} addpurpose\n * @private\n */\n DataRegistry.prototype.addpurpose = null;\n\n /**\n * @var {AddCategory} addcategory\n * @private\n */\n DataRegistry.prototype.addcategory = null;\n\n DataRegistry.prototype.init = function() {\n // Add purpose and category modals always at system context.\n this.addpurpose = AddPurpose.getInstance(this.systemContextId);\n this.addcategory = AddCategory.getInstance(this.systemContextId);\n\n var stringKeys = [\n {\n key: 'changessaved',\n component: 'moodle'\n }, {\n key: 'contextpurposecategorysaved',\n component: 'tool_dataprivacy'\n }, {\n key: 'noblockstoload',\n component: 'tool_dataprivacy'\n }, {\n key: 'noactivitiestoload',\n component: 'tool_dataprivacy'\n }, {\n key: 'nocoursestoload',\n component: 'tool_dataprivacy'\n }\n ];\n this.strings = Str.get_strings(stringKeys);\n\n this.registerEventListeners();\n\n // Load the default context level form.\n if (this.currentContextId) {\n this.loadForm('context_form', [this.currentContextId], this.submitContextFormAjax.bind(this));\n } else {\n this.loadForm('contextlevel_form', [this.currentContextLevel], this.submitContextLevelFormAjax.bind(this));\n }\n };\n\n DataRegistry.prototype.registerEventListeners = function() {\n $(SELECTORS.TREE_NODES).on('click', function(ev) {\n ev.preventDefault();\n\n var trigger = $(ev.currentTarget);\n\n // Active node.\n $(SELECTORS.TREE_NODES).removeClass('active');\n trigger.addClass('active');\n\n var contextLevel = trigger.data('contextlevel');\n var contextId = trigger.data('contextid');\n if (contextLevel) {\n // Context level level.\n\n window.history.pushState({}, null, '?contextlevel=' + contextLevel);\n\n // Remove previous add purpose and category listeners to avoid memory leaks.\n this.addpurpose.removeListeners();\n this.addcategory.removeListeners();\n\n // Load the context level form.\n this.currentContextLevel = contextLevel;\n this.loadForm('contextlevel_form', [this.currentContextLevel], this.submitContextLevelFormAjax.bind(this));\n } else if (contextId) {\n // Context instance level.\n\n window.history.pushState({}, null, '?contextid=' + contextId);\n\n // Remove previous add purpose and category listeners to avoid memory leaks.\n this.addpurpose.removeListeners();\n this.addcategory.removeListeners();\n\n // Load the context level form.\n this.currentContextId = contextId;\n this.loadForm('context_form', [this.currentContextId], this.submitContextFormAjax.bind(this));\n } else {\n // Expandable nodes.\n\n var expandContextId = trigger.data('expandcontextid');\n var expandElement = trigger.data('expandelement');\n var expanded = trigger.data('expanded');\n\n // Extra checking that there is an expandElement because we remove it after loading 0 branches.\n if (expandElement) {\n\n if (!expanded) {\n if (trigger.data('loaded') || !expandContextId || !expandElement) {\n this.expand(trigger);\n } else {\n\n trigger.find('> i').removeClass('fa-plus');\n trigger.find('> i').addClass('fa-circle-o-notch fa-spin');\n this.loadExtra(trigger, expandContextId, expandElement);\n }\n } else {\n this.collapse(trigger);\n }\n }\n }\n\n }.bind(this));\n };\n\n DataRegistry.prototype.removeListeners = function() {\n $(SELECTORS.TREE_NODES).off('click');\n };\n\n DataRegistry.prototype.loadForm = function(fragmentName, fragmentArgs, formSubmitCallback) {\n\n this.clearForm();\n\n var fragment = Fragment.loadFragment('tool_dataprivacy', fragmentName, this.systemContextId, fragmentArgs);\n fragment.done(function(html, js) {\n\n $(SELECTORS.FORM_CONTAINER).html(html);\n Templates.runTemplateJS(js);\n\n this.addpurpose.registerEventListeners();\n this.addcategory.registerEventListeners();\n\n // We also catch the form submit event and use it to submit the form with ajax.\n $(SELECTORS.FORM_CONTAINER).on('submit', 'form', formSubmitCallback);\n\n }.bind(this)).fail(Notification.exception);\n };\n\n DataRegistry.prototype.clearForm = function() {\n // Remove previous listeners.\n $(SELECTORS.FORM_CONTAINER).off('submit', 'form');\n };\n\n /**\n * This triggers a form submission, so that any mform elements can do final tricks before the form submission is processed.\n *\n * @method submitForm\n * @param {Event} e Form submission event.\n * @private\n */\n DataRegistry.prototype.submitForm = function(e) {\n e.preventDefault();\n $(SELECTORS.FORM_CONTAINER).find('form').submit();\n };\n\n DataRegistry.prototype.submitContextLevelFormAjax = function(e) {\n this.submitFormAjax(e, 'tool_dataprivacy_set_contextlevel_form');\n };\n\n DataRegistry.prototype.submitContextFormAjax = function(e) {\n this.submitFormAjax(e, 'tool_dataprivacy_set_context_form');\n };\n\n DataRegistry.prototype.submitFormAjax = function(e, saveMethodName) {\n // We don't want to do a real form submission.\n e.preventDefault();\n\n // Convert all the form elements values to a serialised string.\n var formData = $(SELECTORS.FORM_CONTAINER).find('form').serialize();\n return this.strings.then(function(strings) {\n Ajax.call([{\n methodname: saveMethodName,\n args: {jsonformdata: JSON.stringify(formData)},\n done: function() {\n Notification.alert(strings[0], strings[1]);\n },\n fail: Notification.exception\n }]);\n return;\n }).catch(Notification.exception);\n\n };\n\n DataRegistry.prototype.loadExtra = function(parentNode, expandContextId, expandElement) {\n\n Ajax.call([{\n methodname: 'tool_dataprivacy_tree_extra_branches',\n args: {\n contextid: expandContextId,\n element: expandElement,\n },\n done: function(data) {\n if (data.branches.length == 0) {\n this.noElements(parentNode, expandElement);\n return;\n }\n Templates.render('tool_dataprivacy/context_tree_branches', data)\n .then(function(html) {\n parentNode.after(html);\n this.removeListeners();\n this.registerEventListeners();\n this.expand(parentNode);\n parentNode.data('loaded', 1);\n return;\n }.bind(this))\n .fail(Notification.exception);\n }.bind(this),\n fail: Notification.exception\n }]);\n };\n\n DataRegistry.prototype.noElements = function(node, expandElement) {\n node.data('expandcontextid', '');\n node.data('expandelement', '');\n this.strings.then(function(strings) {\n\n // 2 = blocks, 3 = activities, 4 = courses (although courses is not likely really).\n var key = 2;\n if (expandElement == 'module') {\n key = 3;\n } else if (expandElement == 'course') {\n key = 4;\n }\n node.text(strings[key]);\n return;\n }).fail(Notification.exception);\n };\n\n DataRegistry.prototype.collapse = function(node) {\n node.data('expanded', 0);\n node.siblings('nav').addClass('hidden');\n node.find('> i').removeClass('fa-minus');\n node.find('> i').addClass('fa-plus');\n };\n\n DataRegistry.prototype.expand = function(node) {\n node.data('expanded', 1);\n node.siblings('nav').removeClass('hidden');\n node.find('> i').removeClass('fa-plus');\n // Also remove the spinning one if data was just loaded.\n node.find('> i').removeClass('fa-circle-o-notch fa-spin');\n node.find('> i').addClass('fa-minus');\n };\n return /** @alias module:tool_dataprivacy/data_registry */ {\n\n /**\n * Initialise the page.\n *\n * @param {Number} systemContextId\n * @param {Number} initContextLevel\n * @param {Number} initContextId\n * @return {DataRegistry}\n */\n init: function(systemContextId, initContextLevel, initContextId) {\n return new DataRegistry(systemContextId, initContextLevel, initContextId);\n }\n };\n }\n);\n\n"],"names":["define","$","Str","Ajax","Notification","Templates","ModalFactory","ModalEvents","Fragment","AddPurpose","AddCategory","SELECTORS","DataRegistry","systemContextId","initContextLevel","initContextId","currentContextLevel","currentContextId","init","prototype","addpurpose","addcategory","getInstance","this","strings","get_strings","key","component","registerEventListeners","loadForm","submitContextFormAjax","bind","submitContextLevelFormAjax","on","ev","preventDefault","trigger","currentTarget","removeClass","addClass","contextLevel","data","contextId","window","history","pushState","removeListeners","expandContextId","expandElement","expanded","collapse","find","loadExtra","expand","off","fragmentName","fragmentArgs","formSubmitCallback","clearForm","loadFragment","done","html","js","runTemplateJS","fail","exception","submitForm","e","submit","submitFormAjax","saveMethodName","formData","serialize","then","call","methodname","args","jsonformdata","JSON","stringify","alert","catch","parentNode","contextid","element","branches","length","render","after","noElements","node","text","siblings"],"mappings":"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"}
\ No newline at end of file
+{"version":3,"file":"data_registry.min.js","sources":["../src/data_registry.js"],"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 * Request actions.\n *\n * @module tool_dataprivacy/data_registry\n * @copyright 2018 David Monllao\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(['jquery', 'core/str', 'core/ajax', 'core/notification', 'core/templates', 'core/modal_factory',\n 'core/modal_events', 'core/fragment', 'tool_dataprivacy/add_purpose', 'tool_dataprivacy/add_category'],\n function($, Str, Ajax, Notification, Templates, ModalFactory, ModalEvents, Fragment, AddPurpose, AddCategory) {\n\n var SELECTORS = {\n TREE_NODES: '[data-context-tree-node=1]',\n FORM_CONTAINER: '#context-form-container',\n };\n\n var DataRegistry = function(systemContextId, initContextLevel, initContextId) {\n this.systemContextId = systemContextId;\n this.currentContextLevel = initContextLevel;\n this.currentContextId = initContextId;\n this.init();\n };\n\n /**\n * @var {int} systemContextId\n * @private\n */\n DataRegistry.prototype.systemContextId = 0;\n\n /**\n * @var {int} currentContextLevel\n * @private\n */\n DataRegistry.prototype.currentContextLevel = 0;\n\n /**\n * @var {int} currentContextId\n * @private\n */\n DataRegistry.prototype.currentContextId = 0;\n\n /**\n * @var {AddPurpose} addpurpose\n * @private\n */\n DataRegistry.prototype.addpurpose = null;\n\n /**\n * @var {AddCategory} addcategory\n * @private\n */\n DataRegistry.prototype.addcategory = null;\n\n DataRegistry.prototype.init = function() {\n // Add purpose and category modals always at system context.\n this.addpurpose = AddPurpose.getInstance(this.systemContextId);\n this.addcategory = AddCategory.getInstance(this.systemContextId);\n\n var stringKeys = [\n {\n key: 'changessaved',\n component: 'moodle'\n }, {\n key: 'contextpurposecategorysaved',\n component: 'tool_dataprivacy'\n }, {\n key: 'noblockstoload',\n component: 'tool_dataprivacy'\n }, {\n key: 'noactivitiestoload',\n component: 'tool_dataprivacy'\n }, {\n key: 'nocoursestoload',\n component: 'tool_dataprivacy'\n }\n ];\n this.strings = Str.get_strings(stringKeys);\n\n this.registerEventListeners();\n\n // Load the default context level form.\n if (this.currentContextId) {\n this.loadForm('context_form', [this.currentContextId], this.submitContextFormAjax.bind(this));\n } else {\n this.loadForm('contextlevel_form', [this.currentContextLevel], this.submitContextLevelFormAjax.bind(this));\n }\n };\n\n DataRegistry.prototype.registerEventListeners = function() {\n $(SELECTORS.TREE_NODES).on('click', function(ev) {\n ev.preventDefault();\n\n var trigger = $(ev.currentTarget);\n\n // Active node.\n $(SELECTORS.TREE_NODES).removeClass('active');\n trigger.addClass('active');\n\n var contextLevel = trigger.data('contextlevel');\n var contextId = trigger.data('contextid');\n if (contextLevel) {\n // Context level level.\n\n window.history.pushState({}, null, '?contextlevel=' + contextLevel);\n\n // Remove previous add purpose and category listeners to avoid memory leaks.\n this.addpurpose.removeListeners();\n this.addcategory.removeListeners();\n\n // Load the context level form.\n this.currentContextLevel = contextLevel;\n this.loadForm('contextlevel_form', [this.currentContextLevel], this.submitContextLevelFormAjax.bind(this));\n } else if (contextId) {\n // Context instance level.\n\n window.history.pushState({}, null, '?contextid=' + contextId);\n\n // Remove previous add purpose and category listeners to avoid memory leaks.\n this.addpurpose.removeListeners();\n this.addcategory.removeListeners();\n\n // Load the context level form.\n this.currentContextId = contextId;\n this.loadForm('context_form', [this.currentContextId], this.submitContextFormAjax.bind(this));\n } else {\n // Expandable nodes.\n\n var expandContextId = trigger.data('expandcontextid');\n var expandElement = trigger.data('expandelement');\n var expanded = trigger.data('expanded');\n\n // Extra checking that there is an expandElement because we remove it after loading 0 branches.\n if (expandElement) {\n\n if (!expanded) {\n if (trigger.data('loaded') || !expandContextId || !expandElement) {\n this.expand(trigger);\n } else {\n\n trigger.find('> i').removeClass('fa-plus');\n trigger.find('> i').addClass('fa-circle-o-notch fa-spin');\n this.loadExtra(trigger, expandContextId, expandElement);\n }\n } else {\n this.collapse(trigger);\n }\n }\n }\n\n }.bind(this));\n };\n\n DataRegistry.prototype.removeListeners = function() {\n $(SELECTORS.TREE_NODES).off('click');\n };\n\n DataRegistry.prototype.loadForm = function(fragmentName, fragmentArgs, formSubmitCallback) {\n\n this.clearForm();\n\n var fragment = Fragment.loadFragment('tool_dataprivacy', fragmentName, this.systemContextId, fragmentArgs);\n fragment.done(function(html, js) {\n\n $(SELECTORS.FORM_CONTAINER).html(html);\n Templates.runTemplateJS(js);\n\n this.addpurpose.registerEventListeners();\n this.addcategory.registerEventListeners();\n\n // We also catch the form submit event and use it to submit the form with ajax.\n $(SELECTORS.FORM_CONTAINER).on('submit', 'form', formSubmitCallback);\n\n }.bind(this)).fail(Notification.exception);\n };\n\n DataRegistry.prototype.clearForm = function() {\n // Remove previous listeners.\n $(SELECTORS.FORM_CONTAINER).off('submit', 'form');\n };\n\n /**\n * This triggers a form submission, so that any mform elements can do final tricks before the form submission is processed.\n *\n * @method submitForm\n * @param {Event} e Form submission event.\n * @private\n */\n DataRegistry.prototype.submitForm = function(e) {\n e.preventDefault();\n $(SELECTORS.FORM_CONTAINER).find('form').submit();\n };\n\n DataRegistry.prototype.submitContextLevelFormAjax = function(e) {\n this.submitFormAjax(e, 'tool_dataprivacy_set_contextlevel_form');\n };\n\n DataRegistry.prototype.submitContextFormAjax = function(e) {\n this.submitFormAjax(e, 'tool_dataprivacy_set_context_form');\n };\n\n DataRegistry.prototype.submitFormAjax = function(e, saveMethodName) {\n // We don't want to do a real form submission.\n e.preventDefault();\n\n // Convert all the form elements values to a serialised string.\n var formData = $(SELECTORS.FORM_CONTAINER).find('form').serialize();\n return this.strings.then(function(strings) {\n Ajax.call([{\n methodname: saveMethodName,\n args: {jsonformdata: JSON.stringify(formData)},\n done: function() {\n Notification.alert(strings[0], strings[1]);\n },\n fail: Notification.exception\n }]);\n return;\n }).catch(Notification.exception);\n\n };\n\n DataRegistry.prototype.loadExtra = function(parentNode, expandContextId, expandElement) {\n\n Ajax.call([{\n methodname: 'tool_dataprivacy_tree_extra_branches',\n args: {\n contextid: expandContextId,\n element: expandElement,\n },\n done: function(data) {\n if (data.branches.length == 0) {\n this.noElements(parentNode, expandElement);\n return;\n }\n Templates.render('tool_dataprivacy/context_tree_branches', data)\n .then(function(html) {\n parentNode.after(html);\n this.removeListeners();\n this.registerEventListeners();\n this.expand(parentNode);\n parentNode.data('loaded', 1);\n return;\n }.bind(this))\n .fail(Notification.exception);\n }.bind(this),\n fail: Notification.exception\n }]);\n };\n\n DataRegistry.prototype.noElements = function(node, expandElement) {\n node.data('expandcontextid', '');\n node.data('expandelement', '');\n this.strings.then(function(strings) {\n\n // 2 = blocks, 3 = activities, 4 = courses (although courses is not likely really).\n var key = 2;\n if (expandElement == 'module') {\n key = 3;\n } else if (expandElement == 'course') {\n key = 4;\n }\n node.text(strings[key]);\n return;\n }).fail(Notification.exception);\n };\n\n DataRegistry.prototype.collapse = function(node) {\n node.data('expanded', 0);\n node.siblings('nav').addClass('hidden');\n node.find('> i').removeClass('fa-minus');\n node.find('> i').addClass('fa-plus');\n };\n\n DataRegistry.prototype.expand = function(node) {\n node.data('expanded', 1);\n node.siblings('nav').removeClass('hidden');\n node.find('> i').removeClass('fa-plus');\n // Also remove the spinning one if data was just loaded.\n node.find('> i').removeClass('fa-circle-o-notch fa-spin');\n node.find('> i').addClass('fa-minus');\n };\n return /** @alias module:tool_dataprivacy/data_registry */ {\n\n /**\n * Initialise the page.\n *\n * @param {Number} systemContextId\n * @param {Number} initContextLevel\n * @param {Number} initContextId\n * @return {DataRegistry}\n */\n init: function(systemContextId, initContextLevel, initContextId) {\n return new DataRegistry(systemContextId, initContextLevel, initContextId);\n }\n };\n }\n);\n\n"],"names":["define","$","Str","Ajax","Notification","Templates","ModalFactory","ModalEvents","Fragment","AddPurpose","AddCategory","SELECTORS","DataRegistry","systemContextId","initContextLevel","initContextId","this","currentContextLevel","currentContextId","init","prototype","addpurpose","addcategory","getInstance","strings","get_strings","key","component","registerEventListeners","loadForm","submitContextFormAjax","bind","submitContextLevelFormAjax","on","ev","preventDefault","trigger","currentTarget","removeClass","addClass","contextLevel","data","contextId","window","history","pushState","removeListeners","expandContextId","expandElement","expanded","collapse","find","loadExtra","expand","off","fragmentName","fragmentArgs","formSubmitCallback","clearForm","loadFragment","done","html","js","runTemplateJS","fail","exception","submitForm","e","submit","submitFormAjax","saveMethodName","formData","serialize","then","call","methodname","args","jsonformdata","JSON","stringify","alert","catch","parentNode","contextid","element","branches","length","render","after","noElements","node","text","siblings"],"mappings":"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"}
\ No newline at end of file
diff --git a/admin/tool/dataprivacy/amd/build/data_request_modal.min.js.map b/admin/tool/dataprivacy/amd/build/data_request_modal.min.js.map
index ef4fbb7deda..ece5de5201d 100644
--- a/admin/tool/dataprivacy/amd/build/data_request_modal.min.js.map
+++ b/admin/tool/dataprivacy/amd/build/data_request_modal.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"data_request_modal.min.js","sources":["../src/data_request_modal.js"],"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 * Request actions.\n *\n * @module tool_dataprivacy/data_request_modal\n * @copyright 2018 Jun Pataleta\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(['jquery', 'core/notification', 'core/custom_interaction_events', 'core/modal', 'core/modal_registry',\n 'tool_dataprivacy/events'],\n function($, Notification, CustomEvents, Modal, ModalRegistry, DataPrivacyEvents) {\n\n var registered = false;\n var SELECTORS = {\n APPROVE_BUTTON: '[data-action=\"approve\"]',\n DENY_BUTTON: '[data-action=\"deny\"]',\n COMPLETE_BUTTON: '[data-action=\"complete\"]'\n };\n\n /**\n * Constructor for the Modal.\n *\n * @param {object} root The root jQuery element for the modal\n */\n var ModalDataRequest = function(root) {\n Modal.call(this, root);\n };\n\n ModalDataRequest.TYPE = 'tool_dataprivacy-data_request';\n ModalDataRequest.prototype = Object.create(Modal.prototype);\n ModalDataRequest.prototype.constructor = ModalDataRequest;\n\n /**\n * Set up all of the event handling for the modal.\n *\n * @method registerEventListeners\n */\n ModalDataRequest.prototype.registerEventListeners = function() {\n // Apply parent event listeners.\n Modal.prototype.registerEventListeners.call(this);\n\n this.getModal().on(CustomEvents.events.activate, SELECTORS.APPROVE_BUTTON, function(e, data) {\n var approveEvent = $.Event(DataPrivacyEvents.approve);\n this.getRoot().trigger(approveEvent, this);\n\n if (!approveEvent.isDefaultPrevented()) {\n this.hide();\n data.originalEvent.preventDefault();\n }\n }.bind(this));\n\n this.getModal().on(CustomEvents.events.activate, SELECTORS.DENY_BUTTON, function(e, data) {\n var denyEvent = $.Event(DataPrivacyEvents.deny);\n this.getRoot().trigger(denyEvent, this);\n\n if (!denyEvent.isDefaultPrevented()) {\n this.hide();\n data.originalEvent.preventDefault();\n }\n }.bind(this));\n\n this.getModal().on(CustomEvents.events.activate, SELECTORS.COMPLETE_BUTTON, function(e, data) {\n var completeEvent = $.Event(DataPrivacyEvents.complete);\n this.getRoot().trigger(completeEvent, this);\n\n if (!completeEvent.isDefaultPrevented()) {\n this.hide();\n data.originalEvent.preventDefault();\n }\n }.bind(this));\n };\n\n // Automatically register with the modal registry the first time this module is imported so that you can create modals\n // of this type using the modal factory.\n if (!registered) {\n ModalRegistry.register(ModalDataRequest.TYPE, ModalDataRequest, 'tool_dataprivacy/data_request_modal');\n registered = true;\n }\n\n return ModalDataRequest;\n });"],"names":["define","$","Notification","CustomEvents","Modal","ModalRegistry","DataPrivacyEvents","registered","SELECTORS","ModalDataRequest","root","call","this","TYPE","prototype","Object","create","constructor","registerEventListeners","getModal","on","events","activate","e","data","approveEvent","Event","approve","getRoot","trigger","isDefaultPrevented","hide","originalEvent","preventDefault","bind","denyEvent","deny","completeEvent","complete","register"],"mappings":";;;;;;;AAsBAA,6CAAO,CAAC,SAAU,oBAAqB,iCAAkC,aAAc,sBAC/E,4BACJ,SAASC,EAAGC,aAAcC,aAAcC,MAAOC,cAAeC,uBAEtDC,YAAa,EACbC,yBACgB,0BADhBA,sBAEa,uBAFbA,0BAGiB,2BAQjBC,iBAAmB,SAASC,MAC5BN,MAAMO,KAAKC,KAAMF,cAGrBD,iBAAiBI,KAAO,iCACxBJ,iBAAiBK,UAAYC,OAAOC,OAAOZ,MAAMU,YACtBG,YAAcR,iBAOzCA,iBAAiBK,UAAUI,uBAAyB,WAEhDd,MAAMU,UAAUI,uBAAuBP,KAAKC,WAEvCO,WAAWC,GAAGjB,aAAakB,OAAOC,SAAUd,yBAA0B,SAASe,EAAGC,UAC/EC,aAAexB,EAAEyB,MAAMpB,kBAAkBqB,cACxCC,UAAUC,QAAQJ,aAAcb,MAEhCa,aAAaK,4BACTC,OACLP,KAAKQ,cAAcC,mBAEzBC,KAAKtB,YAEFO,WAAWC,GAAGjB,aAAakB,OAAOC,SAAUd,sBAAuB,SAASe,EAAGC,UAC5EW,UAAYlC,EAAEyB,MAAMpB,kBAAkB8B,WACrCR,UAAUC,QAAQM,UAAWvB,MAE7BuB,UAAUL,4BACNC,OACLP,KAAKQ,cAAcC,mBAEzBC,KAAKtB,YAEFO,WAAWC,GAAGjB,aAAakB,OAAOC,SAAUd,0BAA2B,SAASe,EAAGC,UAChFa,cAAgBpC,EAAEyB,MAAMpB,kBAAkBgC,eACzCV,UAAUC,QAAQQ,cAAezB,MAEjCyB,cAAcP,4BACVC,OACLP,KAAKQ,cAAcC,mBAEzBC,KAAKtB,QAKNL,aACDF,cAAckC,SAAS9B,iBAAiBI,KAAMJ,iBAAkB,uCAChEF,YAAa,GAGVE"}
\ No newline at end of file
+{"version":3,"file":"data_request_modal.min.js","sources":["../src/data_request_modal.js"],"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 * Request actions.\n *\n * @module tool_dataprivacy/data_request_modal\n * @copyright 2018 Jun Pataleta\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(['jquery', 'core/notification', 'core/custom_interaction_events', 'core/modal', 'core/modal_registry',\n 'tool_dataprivacy/events'],\n function($, Notification, CustomEvents, Modal, ModalRegistry, DataPrivacyEvents) {\n\n var registered = false;\n var SELECTORS = {\n APPROVE_BUTTON: '[data-action=\"approve\"]',\n DENY_BUTTON: '[data-action=\"deny\"]',\n COMPLETE_BUTTON: '[data-action=\"complete\"]'\n };\n\n /**\n * Constructor for the Modal.\n *\n * @param {object} root The root jQuery element for the modal\n */\n var ModalDataRequest = function(root) {\n Modal.call(this, root);\n };\n\n ModalDataRequest.TYPE = 'tool_dataprivacy-data_request';\n ModalDataRequest.prototype = Object.create(Modal.prototype);\n ModalDataRequest.prototype.constructor = ModalDataRequest;\n\n /**\n * Set up all of the event handling for the modal.\n *\n * @method registerEventListeners\n */\n ModalDataRequest.prototype.registerEventListeners = function() {\n // Apply parent event listeners.\n Modal.prototype.registerEventListeners.call(this);\n\n this.getModal().on(CustomEvents.events.activate, SELECTORS.APPROVE_BUTTON, function(e, data) {\n var approveEvent = $.Event(DataPrivacyEvents.approve);\n this.getRoot().trigger(approveEvent, this);\n\n if (!approveEvent.isDefaultPrevented()) {\n this.hide();\n data.originalEvent.preventDefault();\n }\n }.bind(this));\n\n this.getModal().on(CustomEvents.events.activate, SELECTORS.DENY_BUTTON, function(e, data) {\n var denyEvent = $.Event(DataPrivacyEvents.deny);\n this.getRoot().trigger(denyEvent, this);\n\n if (!denyEvent.isDefaultPrevented()) {\n this.hide();\n data.originalEvent.preventDefault();\n }\n }.bind(this));\n\n this.getModal().on(CustomEvents.events.activate, SELECTORS.COMPLETE_BUTTON, function(e, data) {\n var completeEvent = $.Event(DataPrivacyEvents.complete);\n this.getRoot().trigger(completeEvent, this);\n\n if (!completeEvent.isDefaultPrevented()) {\n this.hide();\n data.originalEvent.preventDefault();\n }\n }.bind(this));\n };\n\n // Automatically register with the modal registry the first time this module is imported so that you can create modals\n // of this type using the modal factory.\n if (!registered) {\n ModalRegistry.register(ModalDataRequest.TYPE, ModalDataRequest, 'tool_dataprivacy/data_request_modal');\n registered = true;\n }\n\n return ModalDataRequest;\n });"],"names":["define","$","Notification","CustomEvents","Modal","ModalRegistry","DataPrivacyEvents","registered","SELECTORS","ModalDataRequest","root","call","this","TYPE","prototype","Object","create","constructor","registerEventListeners","getModal","on","events","activate","e","data","approveEvent","Event","approve","getRoot","trigger","isDefaultPrevented","hide","originalEvent","preventDefault","bind","denyEvent","deny","completeEvent","complete","register"],"mappings":";;;;;;;AAsBAA,OAAM,sCAAC,CAAC,SAAU,oBAAqB,iCAAkC,aAAc,sBAC/E,4BACJ,SAASC,EAAGC,aAAcC,aAAcC,MAAOC,cAAeC,mBAE1D,IAAIC,YAAa,EACbC,yBACgB,0BADhBA,sBAEa,uBAFbA,0BAGiB,2BAQjBC,iBAAmB,SAASC,MAC5BN,MAAMO,KAAKC,KAAMF,OAsDrB,OAnDAD,iBAAiBI,KAAO,iCACxBJ,iBAAiBK,UAAYC,OAAOC,OAAOZ,MAAMU,YACtBG,YAAcR,iBAOzCA,iBAAiBK,UAAUI,uBAAyB,WAEhDd,MAAMU,UAAUI,uBAAuBP,KAAKC,MAE5CA,KAAKO,WAAWC,GAAGjB,aAAakB,OAAOC,SAAUd,yBAA0B,SAASe,EAAGC,MACnF,IAAIC,aAAexB,EAAEyB,MAAMpB,kBAAkBqB,SAC7Cf,KAAKgB,UAAUC,QAAQJ,aAAcb,MAEhCa,aAAaK,uBACdlB,KAAKmB,OACLP,KAAKQ,cAAcC,iBAE3B,EAAEC,KAAKtB,OAEPA,KAAKO,WAAWC,GAAGjB,aAAakB,OAAOC,SAAUd,sBAAuB,SAASe,EAAGC,MAChF,IAAIW,UAAYlC,EAAEyB,MAAMpB,kBAAkB8B,MAC1CxB,KAAKgB,UAAUC,QAAQM,UAAWvB,MAE7BuB,UAAUL,uBACXlB,KAAKmB,OACLP,KAAKQ,cAAcC,iBAE3B,EAAEC,KAAKtB,OAEPA,KAAKO,WAAWC,GAAGjB,aAAakB,OAAOC,SAAUd,0BAA2B,SAASe,EAAGC,MACpF,IAAIa,cAAgBpC,EAAEyB,MAAMpB,kBAAkBgC,UAC9C1B,KAAKgB,UAAUC,QAAQQ,cAAezB,MAEjCyB,cAAcP,uBACflB,KAAKmB,OACLP,KAAKQ,cAAcC,iBAE3B,EAAEC,KAAKtB,QAKNL,aACDF,cAAckC,SAAS9B,iBAAiBI,KAAMJ,iBAAkB,uCAChEF,YAAa,GAGVE,gBACX"}
\ No newline at end of file
diff --git a/admin/tool/dataprivacy/amd/build/defaultsactions.min.js.map b/admin/tool/dataprivacy/amd/build/defaultsactions.min.js.map
index 50b7d083a24..7301cd7f014 100644
--- a/admin/tool/dataprivacy/amd/build/defaultsactions.min.js.map
+++ b/admin/tool/dataprivacy/amd/build/defaultsactions.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"defaultsactions.min.js","sources":["../src/defaultsactions.js"],"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 * AMD module for data registry defaults actions.\n *\n * @module tool_dataprivacy/defaultsactions\n * @copyright 2018 Jun Pataleta\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine([\n 'jquery',\n 'core/ajax',\n 'core/notification',\n 'core/str',\n 'core/modal_factory',\n 'core/modal_events',\n 'core/templates'],\nfunction($, Ajax, Notification, Str, ModalFactory, ModalEvents, Templates) {\n\n /**\n * List of action selectors.\n *\n * @type {{EDIT_LEVEL_DEFAULTS: string}}\n * @type {{NEW_ACTIVITY_DEFAULTS: string}}\n * @type {{EDIT_ACTIVITY_DEFAULTS: string}}\n * @type {{DELETE_ACTIVITY_DEFAULTS: string}}\n */\n var ACTIONS = {\n EDIT_LEVEL_DEFAULTS: '[data-action=\"edit-level-defaults\"]',\n NEW_ACTIVITY_DEFAULTS: '[data-action=\"new-activity-defaults\"]',\n EDIT_ACTIVITY_DEFAULTS: '[data-action=\"edit-activity-defaults\"]',\n DELETE_ACTIVITY_DEFAULTS: '[data-action=\"delete-activity-defaults\"]'\n };\n\n /** @type {{INHERIT: Number}} **/\n var INHERIT = -1;\n\n /**\n * DefaultsActions class.\n */\n var DefaultsActions = function() {\n this.registerEvents();\n };\n\n /**\n * Register event listeners.\n */\n DefaultsActions.prototype.registerEvents = function() {\n $(ACTIONS.EDIT_LEVEL_DEFAULTS).click(function(e) {\n e.preventDefault();\n\n var button = $(this);\n var contextLevel = button.data('contextlevel');\n var category = button.data('category');\n var purpose = button.data('purpose');\n\n // Get options.\n var requests = [\n {methodname: 'tool_dataprivacy_get_category_options', args: {}},\n {methodname: 'tool_dataprivacy_get_purpose_options', args: {}}\n ];\n\n var promises = Ajax.call(requests);\n var titlePromise = Str.get_string('editdefaults', 'tool_dataprivacy', $('#defaults-header').text());\n $.when(promises[0], promises[1], titlePromise).then(function(categoryResponse, purposeResponse, title) {\n var categories = categoryResponse.options;\n var purposes = purposeResponse.options;\n showDefaultsFormModal(title, contextLevel, category, purpose, null, categories, purposes, null);\n\n return true;\n }).catch(Notification.exception);\n });\n\n $(ACTIONS.NEW_ACTIVITY_DEFAULTS).click(function(e) {\n e.preventDefault();\n\n var button = $(this);\n var contextLevel = button.data('contextlevel');\n\n // Get options.\n var requests = [\n {methodname: 'tool_dataprivacy_get_category_options', args: {}},\n {methodname: 'tool_dataprivacy_get_purpose_options', args: {}},\n {methodname: 'tool_dataprivacy_get_activity_options', args: {'nodefaults': true}}\n ];\n\n var promises = Ajax.call(requests);\n var titlePromise = Str.get_string('addnewdefaults', 'tool_dataprivacy');\n\n $.when(promises[0], promises[1], promises[2], titlePromise).then(\n function(categoryResponse, purposeResponse, activityResponse, title) {\n var categories = categoryResponse.options;\n var purposes = purposeResponse.options;\n var activities = activityResponse.options;\n\n showDefaultsFormModal(title, contextLevel, null, null, null, categories, purposes, activities);\n\n return true;\n\n }).catch(Notification.exception);\n }\n );\n\n $(ACTIONS.EDIT_ACTIVITY_DEFAULTS).click(function(e) {\n e.preventDefault();\n\n var button = $(this);\n var contextLevel = button.data('contextlevel');\n var category = button.data('category');\n var purpose = button.data('purpose');\n var activity = button.data('activityname');\n\n // Get options.\n var requests = [\n {methodname: 'tool_dataprivacy_get_category_options', args: {}},\n {methodname: 'tool_dataprivacy_get_purpose_options', args: {}},\n {methodname: 'tool_dataprivacy_get_activity_options', args: {}}\n ];\n\n var promises = Ajax.call(requests);\n var titlePromise = Str.get_string('editmoduledefaults', 'tool_dataprivacy');\n\n $.when(promises[0], promises[1], promises[2], titlePromise).then(\n function(categoryResponse, purposeResponse, activityResponse, title) {\n var categories = categoryResponse.options;\n var purposes = purposeResponse.options;\n var activities = activityResponse.options;\n\n showDefaultsFormModal(title, contextLevel, category, purpose, activity, categories, purposes, activities);\n\n return true;\n\n }).catch(Notification.exception);\n }\n );\n\n $(ACTIONS.DELETE_ACTIVITY_DEFAULTS).click(function(e) {\n e.preventDefault();\n\n var button = $(this);\n var contextLevel = button.data('contextlevel');\n var activity = button.data('activityname');\n var activityDisplayName = button.data('activitydisplayname');\n // Set category and purpose to inherit (-1).\n var category = INHERIT;\n var purpose = INHERIT;\n\n ModalFactory.create({\n title: Str.get_string('deletedefaults', 'tool_dataprivacy', activityDisplayName),\n body: Templates.render('tool_dataprivacy/delete_activity_defaults', {\"activityname\": activityDisplayName}),\n type: ModalFactory.types.SAVE_CANCEL,\n large: true\n }).then(function(modal) {\n modal.setSaveButtonText(Str.get_string('delete'));\n\n // Handle save event.\n modal.getRoot().on(ModalEvents.save, function() {\n setContextDefaults(contextLevel, category, purpose, activity, false);\n });\n\n // Handle hidden event.\n modal.getRoot().on(ModalEvents.hidden, function() {\n // Destroy when hidden.\n modal.destroy();\n });\n\n modal.show();\n\n return true;\n }).catch(Notification.exception);\n });\n };\n\n /**\n * Prepares and renders the modal for setting the defaults for the given context level/plugin.\n *\n * @param {String} title The modal's title.\n * @param {Number} contextLevel The context level to set defaults for.\n * @param {Number} category The current category ID.\n * @param {Number} purpose The current purpose ID.\n * @param {String} activity The plugin name of the activity. Optional.\n * @param {Array} categoryOptions The list of category options.\n * @param {Array} purposeOptions The list of purpose options.\n * @param {Array} activityOptions The list of activity options. Optional.\n */\n function showDefaultsFormModal(title, contextLevel, category, purpose, activity,\n categoryOptions, purposeOptions, activityOptions) {\n\n if (category !== null) {\n categoryOptions.forEach(function(currentValue) {\n if (currentValue.id === category) {\n currentValue.selected = true;\n }\n });\n }\n\n if (purpose !== null) {\n purposeOptions.forEach(function(currentValue) {\n if (currentValue.id === purpose) {\n currentValue.selected = true;\n }\n });\n }\n\n var templateContext = {\n \"contextlevel\": contextLevel,\n \"categoryoptions\": categoryOptions,\n \"purposeoptions\": purposeOptions\n };\n\n // Check the activityOptions parameter that was passed.\n if (activityOptions !== null && activityOptions.length) {\n // Check the activity parameter that was passed.\n if (activity === null) {\n // We're setting a new defaults for a module.\n templateContext.newactivitydefaults = true;\n\n } else {\n // Edit mode. Set selection.\n activityOptions.forEach(function(currentValue) {\n if (activity === currentValue.name) {\n currentValue.selected = true;\n }\n });\n }\n\n templateContext.modemodule = true;\n templateContext.activityoptions = activityOptions;\n }\n\n ModalFactory.create({\n title: title,\n body: Templates.render('tool_dataprivacy/category_purpose_form', templateContext),\n type: ModalFactory.types.SAVE_CANCEL,\n large: true\n }).then(function(modal) {\n\n // Handle save event.\n modal.getRoot().on(ModalEvents.save, function() {\n var activity = $('#activity');\n var activityVal = typeof activity !== 'undefined' ? activity.val() : null;\n var override = $('#override');\n var overrideVal = typeof override !== 'undefined' ? override.is(':checked') : false;\n\n setContextDefaults($('#contextlevel').val(), $('#category').val(), $('#purpose').val(), activityVal, overrideVal);\n });\n\n // Handle hidden event.\n modal.getRoot().on(ModalEvents.hidden, function() {\n // Destroy when hidden.\n modal.destroy();\n });\n\n modal.show();\n\n return modal;\n }).catch(Notification.exception);\n }\n\n /**\n * Calls a the tool_dataprivacy_set_context_defaults WS function.\n *\n * @param {Number} contextLevel The context level.\n * @param {Number} category The category ID.\n * @param {Number} purpose The purpose ID.\n * @param {String} activity The plugin name of the activity module.\n * @param {Boolean} override Whether to override custom instances.\n */\n function setContextDefaults(contextLevel, category, purpose, activity, override) {\n var request = {\n methodname: 'tool_dataprivacy_set_context_defaults',\n args: {\n 'contextlevel': contextLevel,\n 'category': category,\n 'purpose': purpose,\n 'override': override,\n 'activity': activity\n }\n };\n\n Ajax.call([request])[0].done(function(data) {\n if (data.result) {\n window.location.reload();\n }\n });\n }\n\n return /** @alias module:tool_dataprivacy/defaultsactions */ {\n // Public variables and functions.\n\n /**\n * Initialise the module.\n *\n * @method init\n * @return {DefaultsActions}\n */\n 'init': function() {\n return new DefaultsActions();\n }\n };\n});\n"],"names":["define","$","Ajax","Notification","Str","ModalFactory","ModalEvents","Templates","ACTIONS","DefaultsActions","registerEvents","showDefaultsFormModal","title","contextLevel","category","purpose","activity","categoryOptions","purposeOptions","activityOptions","forEach","currentValue","id","selected","templateContext","length","newactivitydefaults","name","modemodule","activityoptions","create","body","render","type","types","SAVE_CANCEL","large","then","modal","getRoot","on","save","activityVal","val","override","overrideVal","is","setContextDefaults","hidden","destroy","show","catch","exception","request","methodname","args","call","done","data","result","window","location","reload","prototype","click","e","preventDefault","button","this","promises","titlePromise","get_string","text","when","categoryResponse","purposeResponse","categories","options","purposes","activityResponse","activities","activityDisplayName","setSaveButtonText"],"mappings":"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"}
\ No newline at end of file
+{"version":3,"file":"defaultsactions.min.js","sources":["../src/defaultsactions.js"],"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 * AMD module for data registry defaults actions.\n *\n * @module tool_dataprivacy/defaultsactions\n * @copyright 2018 Jun Pataleta\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine([\n 'jquery',\n 'core/ajax',\n 'core/notification',\n 'core/str',\n 'core/modal_factory',\n 'core/modal_events',\n 'core/templates'],\nfunction($, Ajax, Notification, Str, ModalFactory, ModalEvents, Templates) {\n\n /**\n * List of action selectors.\n *\n * @type {{EDIT_LEVEL_DEFAULTS: string}}\n * @type {{NEW_ACTIVITY_DEFAULTS: string}}\n * @type {{EDIT_ACTIVITY_DEFAULTS: string}}\n * @type {{DELETE_ACTIVITY_DEFAULTS: string}}\n */\n var ACTIONS = {\n EDIT_LEVEL_DEFAULTS: '[data-action=\"edit-level-defaults\"]',\n NEW_ACTIVITY_DEFAULTS: '[data-action=\"new-activity-defaults\"]',\n EDIT_ACTIVITY_DEFAULTS: '[data-action=\"edit-activity-defaults\"]',\n DELETE_ACTIVITY_DEFAULTS: '[data-action=\"delete-activity-defaults\"]'\n };\n\n /** @type {{INHERIT: Number}} **/\n var INHERIT = -1;\n\n /**\n * DefaultsActions class.\n */\n var DefaultsActions = function() {\n this.registerEvents();\n };\n\n /**\n * Register event listeners.\n */\n DefaultsActions.prototype.registerEvents = function() {\n $(ACTIONS.EDIT_LEVEL_DEFAULTS).click(function(e) {\n e.preventDefault();\n\n var button = $(this);\n var contextLevel = button.data('contextlevel');\n var category = button.data('category');\n var purpose = button.data('purpose');\n\n // Get options.\n var requests = [\n {methodname: 'tool_dataprivacy_get_category_options', args: {}},\n {methodname: 'tool_dataprivacy_get_purpose_options', args: {}}\n ];\n\n var promises = Ajax.call(requests);\n var titlePromise = Str.get_string('editdefaults', 'tool_dataprivacy', $('#defaults-header').text());\n $.when(promises[0], promises[1], titlePromise).then(function(categoryResponse, purposeResponse, title) {\n var categories = categoryResponse.options;\n var purposes = purposeResponse.options;\n showDefaultsFormModal(title, contextLevel, category, purpose, null, categories, purposes, null);\n\n return true;\n }).catch(Notification.exception);\n });\n\n $(ACTIONS.NEW_ACTIVITY_DEFAULTS).click(function(e) {\n e.preventDefault();\n\n var button = $(this);\n var contextLevel = button.data('contextlevel');\n\n // Get options.\n var requests = [\n {methodname: 'tool_dataprivacy_get_category_options', args: {}},\n {methodname: 'tool_dataprivacy_get_purpose_options', args: {}},\n {methodname: 'tool_dataprivacy_get_activity_options', args: {'nodefaults': true}}\n ];\n\n var promises = Ajax.call(requests);\n var titlePromise = Str.get_string('addnewdefaults', 'tool_dataprivacy');\n\n $.when(promises[0], promises[1], promises[2], titlePromise).then(\n function(categoryResponse, purposeResponse, activityResponse, title) {\n var categories = categoryResponse.options;\n var purposes = purposeResponse.options;\n var activities = activityResponse.options;\n\n showDefaultsFormModal(title, contextLevel, null, null, null, categories, purposes, activities);\n\n return true;\n\n }).catch(Notification.exception);\n }\n );\n\n $(ACTIONS.EDIT_ACTIVITY_DEFAULTS).click(function(e) {\n e.preventDefault();\n\n var button = $(this);\n var contextLevel = button.data('contextlevel');\n var category = button.data('category');\n var purpose = button.data('purpose');\n var activity = button.data('activityname');\n\n // Get options.\n var requests = [\n {methodname: 'tool_dataprivacy_get_category_options', args: {}},\n {methodname: 'tool_dataprivacy_get_purpose_options', args: {}},\n {methodname: 'tool_dataprivacy_get_activity_options', args: {}}\n ];\n\n var promises = Ajax.call(requests);\n var titlePromise = Str.get_string('editmoduledefaults', 'tool_dataprivacy');\n\n $.when(promises[0], promises[1], promises[2], titlePromise).then(\n function(categoryResponse, purposeResponse, activityResponse, title) {\n var categories = categoryResponse.options;\n var purposes = purposeResponse.options;\n var activities = activityResponse.options;\n\n showDefaultsFormModal(title, contextLevel, category, purpose, activity, categories, purposes, activities);\n\n return true;\n\n }).catch(Notification.exception);\n }\n );\n\n $(ACTIONS.DELETE_ACTIVITY_DEFAULTS).click(function(e) {\n e.preventDefault();\n\n var button = $(this);\n var contextLevel = button.data('contextlevel');\n var activity = button.data('activityname');\n var activityDisplayName = button.data('activitydisplayname');\n // Set category and purpose to inherit (-1).\n var category = INHERIT;\n var purpose = INHERIT;\n\n ModalFactory.create({\n title: Str.get_string('deletedefaults', 'tool_dataprivacy', activityDisplayName),\n body: Templates.render('tool_dataprivacy/delete_activity_defaults', {\"activityname\": activityDisplayName}),\n type: ModalFactory.types.SAVE_CANCEL,\n large: true\n }).then(function(modal) {\n modal.setSaveButtonText(Str.get_string('delete'));\n\n // Handle save event.\n modal.getRoot().on(ModalEvents.save, function() {\n setContextDefaults(contextLevel, category, purpose, activity, false);\n });\n\n // Handle hidden event.\n modal.getRoot().on(ModalEvents.hidden, function() {\n // Destroy when hidden.\n modal.destroy();\n });\n\n modal.show();\n\n return true;\n }).catch(Notification.exception);\n });\n };\n\n /**\n * Prepares and renders the modal for setting the defaults for the given context level/plugin.\n *\n * @param {String} title The modal's title.\n * @param {Number} contextLevel The context level to set defaults for.\n * @param {Number} category The current category ID.\n * @param {Number} purpose The current purpose ID.\n * @param {String} activity The plugin name of the activity. Optional.\n * @param {Array} categoryOptions The list of category options.\n * @param {Array} purposeOptions The list of purpose options.\n * @param {Array} activityOptions The list of activity options. Optional.\n */\n function showDefaultsFormModal(title, contextLevel, category, purpose, activity,\n categoryOptions, purposeOptions, activityOptions) {\n\n if (category !== null) {\n categoryOptions.forEach(function(currentValue) {\n if (currentValue.id === category) {\n currentValue.selected = true;\n }\n });\n }\n\n if (purpose !== null) {\n purposeOptions.forEach(function(currentValue) {\n if (currentValue.id === purpose) {\n currentValue.selected = true;\n }\n });\n }\n\n var templateContext = {\n \"contextlevel\": contextLevel,\n \"categoryoptions\": categoryOptions,\n \"purposeoptions\": purposeOptions\n };\n\n // Check the activityOptions parameter that was passed.\n if (activityOptions !== null && activityOptions.length) {\n // Check the activity parameter that was passed.\n if (activity === null) {\n // We're setting a new defaults for a module.\n templateContext.newactivitydefaults = true;\n\n } else {\n // Edit mode. Set selection.\n activityOptions.forEach(function(currentValue) {\n if (activity === currentValue.name) {\n currentValue.selected = true;\n }\n });\n }\n\n templateContext.modemodule = true;\n templateContext.activityoptions = activityOptions;\n }\n\n ModalFactory.create({\n title: title,\n body: Templates.render('tool_dataprivacy/category_purpose_form', templateContext),\n type: ModalFactory.types.SAVE_CANCEL,\n large: true\n }).then(function(modal) {\n\n // Handle save event.\n modal.getRoot().on(ModalEvents.save, function() {\n var activity = $('#activity');\n var activityVal = typeof activity !== 'undefined' ? activity.val() : null;\n var override = $('#override');\n var overrideVal = typeof override !== 'undefined' ? override.is(':checked') : false;\n\n setContextDefaults($('#contextlevel').val(), $('#category').val(), $('#purpose').val(), activityVal, overrideVal);\n });\n\n // Handle hidden event.\n modal.getRoot().on(ModalEvents.hidden, function() {\n // Destroy when hidden.\n modal.destroy();\n });\n\n modal.show();\n\n return modal;\n }).catch(Notification.exception);\n }\n\n /**\n * Calls a the tool_dataprivacy_set_context_defaults WS function.\n *\n * @param {Number} contextLevel The context level.\n * @param {Number} category The category ID.\n * @param {Number} purpose The purpose ID.\n * @param {String} activity The plugin name of the activity module.\n * @param {Boolean} override Whether to override custom instances.\n */\n function setContextDefaults(contextLevel, category, purpose, activity, override) {\n var request = {\n methodname: 'tool_dataprivacy_set_context_defaults',\n args: {\n 'contextlevel': contextLevel,\n 'category': category,\n 'purpose': purpose,\n 'override': override,\n 'activity': activity\n }\n };\n\n Ajax.call([request])[0].done(function(data) {\n if (data.result) {\n window.location.reload();\n }\n });\n }\n\n return /** @alias module:tool_dataprivacy/defaultsactions */ {\n // Public variables and functions.\n\n /**\n * Initialise the module.\n *\n * @method init\n * @return {DefaultsActions}\n */\n 'init': function() {\n return new DefaultsActions();\n }\n };\n});\n"],"names":["define","$","Ajax","Notification","Str","ModalFactory","ModalEvents","Templates","ACTIONS","DefaultsActions","this","registerEvents","showDefaultsFormModal","title","contextLevel","category","purpose","activity","categoryOptions","purposeOptions","activityOptions","forEach","currentValue","id","selected","templateContext","contextlevel","categoryoptions","purposeoptions","length","newactivitydefaults","name","modemodule","activityoptions","create","body","render","type","types","SAVE_CANCEL","large","then","modal","getRoot","on","save","activityVal","val","override","overrideVal","is","setContextDefaults","hidden","destroy","show","catch","exception","request","methodname","args","call","done","data","result","window","location","reload","prototype","click","e","preventDefault","button","promises","titlePromise","get_string","text","when","categoryResponse","purposeResponse","categories","options","purposes","nodefaults","activityResponse","activities","activityDisplayName","activityname","setSaveButtonText","init"],"mappings":"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"}
\ No newline at end of file
diff --git a/admin/tool/dataprivacy/amd/build/effective_retention_period.min.js.map b/admin/tool/dataprivacy/amd/build/effective_retention_period.min.js.map
index ac590ba9ea6..5440248c910 100644
--- a/admin/tool/dataprivacy/amd/build/effective_retention_period.min.js.map
+++ b/admin/tool/dataprivacy/amd/build/effective_retention_period.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"effective_retention_period.min.js","sources":["../src/effective_retention_period.js"],"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 * Module to update the displayed retention period.\n *\n * @module tool_dataprivacy/effective_retention_period\n * @copyright 2018 David Monllao\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(['jquery'],\n function($) {\n\n var SELECTORS = {\n PURPOSE_SELECT: '#id_purposeid',\n RETENTION_FIELD: '#fitem_id_retention_current [data-fieldtype=static]',\n };\n\n /**\n * Constructor for the retention period display.\n *\n * @param {Array} purposeRetentionPeriods Associative array of purposeids with effective retention period at this context\n */\n var EffectiveRetentionPeriod = function(purposeRetentionPeriods) {\n this.purposeRetentionPeriods = purposeRetentionPeriods;\n this.registerEventListeners();\n };\n\n /**\n * Removes the current 'change' listeners.\n *\n * Useful when a new form is loaded.\n */\n var removeListeners = function() {\n $(SELECTORS.PURPOSE_SELECT).off('change');\n };\n\n /**\n * @var {Array} purposeRetentionPeriods\n * @private\n */\n EffectiveRetentionPeriod.prototype.purposeRetentionPeriods = [];\n\n /**\n * Add purpose change listeners.\n *\n * @method registerEventListeners\n */\n EffectiveRetentionPeriod.prototype.registerEventListeners = function() {\n\n $(SELECTORS.PURPOSE_SELECT).on('change', function(ev) {\n var selected = $(ev.currentTarget).val();\n var selectedPurpose = this.purposeRetentionPeriods[selected];\n $(SELECTORS.RETENTION_FIELD).text(selectedPurpose);\n }.bind(this));\n };\n\n return /** @alias module:tool_dataprivacy/effective_retention_period */ {\n init: function(purposeRetentionPeriods) {\n // Remove previously attached listeners.\n removeListeners();\n return new EffectiveRetentionPeriod(purposeRetentionPeriods);\n }\n };\n }\n);\n\n"],"names":["define","$","SELECTORS","EffectiveRetentionPeriod","purposeRetentionPeriods","registerEventListeners","prototype","on","ev","selected","currentTarget","val","selectedPurpose","this","text","bind","init","off"],"mappings":";;;;;;;AAsBAA,qDAAO,CAAC,WACJ,SAASC,OAEDC,yBACgB,gBADhBA,0BAEiB,sDAQjBC,yBAA2B,SAASC,8BAC/BA,wBAA0BA,6BAC1BC,iCAgBTF,yBAAyBG,UAAUF,wBAA0B,GAO7DD,yBAAyBG,UAAUD,uBAAyB,WAExDJ,EAAEC,0BAA0BK,GAAG,SAAU,SAASC,QAC1CC,SAAWR,EAAEO,GAAGE,eAAeC,MAC/BC,gBAAkBC,KAAKT,wBAAwBK,UACnDR,EAAEC,2BAA2BY,KAAKF,kBACpCG,KAAKF,QAG6D,CACpEG,KAAM,SAASZ,gCAxBfH,EAAEC,0BAA0Be,IAAI,UA2BrB,IAAId,yBAAyBC"}
\ No newline at end of file
+{"version":3,"file":"effective_retention_period.min.js","sources":["../src/effective_retention_period.js"],"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 * Module to update the displayed retention period.\n *\n * @module tool_dataprivacy/effective_retention_period\n * @copyright 2018 David Monllao\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(['jquery'],\n function($) {\n\n var SELECTORS = {\n PURPOSE_SELECT: '#id_purposeid',\n RETENTION_FIELD: '#fitem_id_retention_current [data-fieldtype=static]',\n };\n\n /**\n * Constructor for the retention period display.\n *\n * @param {Array} purposeRetentionPeriods Associative array of purposeids with effective retention period at this context\n */\n var EffectiveRetentionPeriod = function(purposeRetentionPeriods) {\n this.purposeRetentionPeriods = purposeRetentionPeriods;\n this.registerEventListeners();\n };\n\n /**\n * Removes the current 'change' listeners.\n *\n * Useful when a new form is loaded.\n */\n var removeListeners = function() {\n $(SELECTORS.PURPOSE_SELECT).off('change');\n };\n\n /**\n * @var {Array} purposeRetentionPeriods\n * @private\n */\n EffectiveRetentionPeriod.prototype.purposeRetentionPeriods = [];\n\n /**\n * Add purpose change listeners.\n *\n * @method registerEventListeners\n */\n EffectiveRetentionPeriod.prototype.registerEventListeners = function() {\n\n $(SELECTORS.PURPOSE_SELECT).on('change', function(ev) {\n var selected = $(ev.currentTarget).val();\n var selectedPurpose = this.purposeRetentionPeriods[selected];\n $(SELECTORS.RETENTION_FIELD).text(selectedPurpose);\n }.bind(this));\n };\n\n return /** @alias module:tool_dataprivacy/effective_retention_period */ {\n init: function(purposeRetentionPeriods) {\n // Remove previously attached listeners.\n removeListeners();\n return new EffectiveRetentionPeriod(purposeRetentionPeriods);\n }\n };\n }\n);\n\n"],"names":["define","$","SELECTORS","EffectiveRetentionPeriod","purposeRetentionPeriods","this","registerEventListeners","prototype","on","ev","selected","currentTarget","val","selectedPurpose","text","bind","init","off"],"mappings":";;;;;;;AAsBAA,qDAAO,CAAC,WACJ,SAASC,GAEL,IAAIC,yBACgB,gBADhBA,0BAEiB,sDAQjBC,yBAA2B,SAASC,yBACpCC,KAAKD,wBAA0BA,wBAC/BC,KAAKC,0BAgCT,OAhBAH,yBAAyBI,UAAUH,wBAA0B,GAO7DD,yBAAyBI,UAAUD,uBAAyB,WAExDL,EAAEC,0BAA0BM,GAAG,SAAU,SAASC,IAC9C,IAAIC,SAAWT,EAAEQ,GAAGE,eAAeC,MAC/BC,gBAAkBR,KAAKD,wBAAwBM,UACnDT,EAAEC,2BAA2BY,KAAKD,gBACtC,EAAEE,KAAKV,QAG6D,CACpEW,KAAM,SAASZ,yBAGX,OA3BJH,EAAEC,0BAA0Be,IAAI,UA2BrB,IAAId,yBAAyBC,wBACxC,EAER"}
\ No newline at end of file
diff --git a/admin/tool/dataprivacy/amd/build/events.min.js.map b/admin/tool/dataprivacy/amd/build/events.min.js.map
index 287e52800b6..57aa7a662fd 100644
--- a/admin/tool/dataprivacy/amd/build/events.min.js.map
+++ b/admin/tool/dataprivacy/amd/build/events.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"events.min.js","sources":["../src/events.js"],"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 * Contain the events the data privacy tool can fire.\n *\n * @module tool_dataprivacy/events\n * @copyright 2018 Jun Pataleta\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine([], function() {\n return {\n approve: 'tool_dataprivacy-data_request:approve',\n bulkApprove: 'tool_dataprivacy-data_request:bulk_approve',\n deny: 'tool_dataprivacy-data_request:deny',\n bulkDeny: 'tool_dataprivacy-data_request:bulk_deny',\n complete: 'tool_dataprivacy-data_request:complete'\n };\n});\n"],"names":["define","approve","bulkApprove","deny","bulkDeny","complete"],"mappings":";;;;;;;AAsBAA,iCAAO,IAAI,iBACA,CACHC,QAAS,wCACTC,YAAa,6CACbC,KAAM,qCACNC,SAAU,0CACVC,SAAU"}
\ No newline at end of file
+{"version":3,"file":"events.min.js","sources":["../src/events.js"],"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 * Contain the events the data privacy tool can fire.\n *\n * @module tool_dataprivacy/events\n * @copyright 2018 Jun Pataleta\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine([], function() {\n return {\n approve: 'tool_dataprivacy-data_request:approve',\n bulkApprove: 'tool_dataprivacy-data_request:bulk_approve',\n deny: 'tool_dataprivacy-data_request:deny',\n bulkDeny: 'tool_dataprivacy-data_request:bulk_deny',\n complete: 'tool_dataprivacy-data_request:complete'\n };\n});\n"],"names":["define","approve","bulkApprove","deny","bulkDeny","complete"],"mappings":";;;;;;;AAsBAA,OAAM,0BAAC,IAAI,WACP,MAAO,CACHC,QAAS,wCACTC,YAAa,6CACbC,KAAM,qCACNC,SAAU,0CACVC,SAAU,yCAElB"}
\ No newline at end of file
diff --git a/admin/tool/dataprivacy/amd/build/expand_contract.min.js.map b/admin/tool/dataprivacy/amd/build/expand_contract.min.js.map
index 2be5226c3af..5b7fb580f0e 100644
--- a/admin/tool/dataprivacy/amd/build/expand_contract.min.js.map
+++ b/admin/tool/dataprivacy/amd/build/expand_contract.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"expand_contract.min.js","sources":["../src/expand_contract.js"],"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 * Potential user selector module.\n *\n * @module tool_dataprivacy/expand_contract\n * @copyright 2018 Adrian Greeve\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\ndefine(['jquery', 'core/url', 'core/str', 'core/notification'], function($, url, str, Notification) {\n\n var expandedImage = $('');\n var collapsedImage = $('');\n\n /*\n * Class names to apply when expanding/collapsing nodes.\n */\n var CLASSES = {\n EXPAND: 'fa-caret-right',\n COLLAPSE: 'fa-caret-down'\n };\n\n return /** @alias module:tool_dataprivacy/expand-collapse */ {\n /**\n * Expand or collapse a selected node.\n *\n * @param {object} targetnode The node that we want to expand / collapse\n * @param {object} thisnode The node that was clicked.\n */\n expandCollapse: function(targetnode, thisnode) {\n if (targetnode.hasClass('hide')) {\n targetnode.removeClass('hide');\n targetnode.addClass('visible');\n targetnode.attr('aria-expanded', true);\n thisnode.find(':header i.fa').removeClass(CLASSES.EXPAND);\n thisnode.find(':header i.fa').addClass(CLASSES.COLLAPSE);\n thisnode.find(':header img.icon').attr('src', expandedImage.attr('src'));\n } else {\n targetnode.removeClass('visible');\n targetnode.addClass('hide');\n targetnode.attr('aria-expanded', false);\n thisnode.find(':header i.fa').removeClass(CLASSES.COLLAPSE);\n thisnode.find(':header i.fa').addClass(CLASSES.EXPAND);\n thisnode.find(':header img.icon').attr('src', collapsedImage.attr('src'));\n }\n },\n\n /**\n * Expand or collapse all nodes on this page.\n *\n * @param {string} nextstate The next state to change to.\n */\n expandCollapseAll: function(nextstate) {\n var currentstate = (nextstate == 'visible') ? 'hide' : 'visible';\n var ariaexpandedstate = (nextstate == 'visible') ? true : false;\n var iconclassnow = (nextstate == 'visible') ? CLASSES.EXPAND : CLASSES.COLLAPSE;\n var iconclassnext = (nextstate == 'visible') ? CLASSES.COLLAPSE : CLASSES.EXPAND;\n var imagenow = (nextstate == 'visible') ? expandedImage.attr('src') : collapsedImage.attr('src');\n $('.' + currentstate).each(function() {\n $(this).removeClass(currentstate);\n $(this).addClass(nextstate);\n $(this).attr('aria-expanded', ariaexpandedstate);\n });\n $('.tool_dataprivacy-expand-all').data('visibilityState', currentstate);\n\n str.get_string(currentstate, 'tool_dataprivacy').then(function(langString) {\n $('.tool_dataprivacy-expand-all').html(langString);\n return;\n }).catch(Notification.exception);\n\n $(':header i.fa').each(function() {\n $(this).removeClass(iconclassnow);\n $(this).addClass(iconclassnext);\n });\n $(':header img.icon').each(function() {\n $(this).attr('src', imagenow);\n });\n }\n };\n});\n"],"names":["define","$","url","str","Notification","expandedImage","imageUrl","collapsedImage","CLASSES","expandCollapse","targetnode","thisnode","hasClass","removeClass","addClass","attr","find","expandCollapseAll","nextstate","currentstate","ariaexpandedstate","iconclassnow","iconclassnext","imagenow","each","this","data","get_string","then","langString","html","catch","exception"],"mappings":";;;;;;;AAuBAA,0CAAO,CAAC,SAAU,WAAY,WAAY,sBAAsB,SAASC,EAAGC,IAAKC,IAAKC,kBAE9EC,cAAgBJ,EAAE,oBAAsBC,IAAII,SAAS,cAAgB,OACrEC,eAAiBN,EAAE,oBAAsBC,IAAII,SAAS,eAAiB,OAKvEE,eACQ,iBADRA,iBAEU,sBAG+C,CAOzDC,eAAgB,SAASC,WAAYC,UAC7BD,WAAWE,SAAS,SACpBF,WAAWG,YAAY,QACvBH,WAAWI,SAAS,WACpBJ,WAAWK,KAAK,iBAAiB,GACjCJ,SAASK,KAAK,gBAAgBH,YAAYL,gBAC1CG,SAASK,KAAK,gBAAgBF,SAASN,kBACvCG,SAASK,KAAK,oBAAoBD,KAAK,MAAOV,cAAcU,KAAK,UAEjEL,WAAWG,YAAY,WACvBH,WAAWI,SAAS,QACpBJ,WAAWK,KAAK,iBAAiB,GACjCJ,SAASK,KAAK,gBAAgBH,YAAYL,kBAC1CG,SAASK,KAAK,gBAAgBF,SAASN,gBACvCG,SAASK,KAAK,oBAAoBD,KAAK,MAAOR,eAAeQ,KAAK,UAS1EE,kBAAmB,SAASC,eACpBC,aAA6B,WAAbD,UAA0B,OAAS,UACnDE,kBAAkC,WAAbF,UACrBG,aAA6B,WAAbH,UAA0BV,eAAiBA,iBAC3Dc,cAA8B,WAAbJ,UAA0BV,iBAAmBA,eAC9De,SAAyB,WAAbL,UAA0Bb,cAAcU,KAAK,OAASR,eAAeQ,KAAK,OAC1Fd,EAAE,IAAMkB,cAAcK,MAAK,WACvBvB,EAAEwB,MAAMZ,YAAYM,cACpBlB,EAAEwB,MAAMX,SAASI,WACjBjB,EAAEwB,MAAMV,KAAK,gBAAiBK,sBAElCnB,EAAE,gCAAgCyB,KAAK,kBAAmBP,cAE1DhB,IAAIwB,WAAWR,aAAc,oBAAoBS,MAAK,SAASC,YAC3D5B,EAAE,gCAAgC6B,KAAKD,eAExCE,MAAM3B,aAAa4B,WAEtB/B,EAAE,gBAAgBuB,MAAK,WACnBvB,EAAEwB,MAAMZ,YAAYQ,cACpBpB,EAAEwB,MAAMX,SAASQ,kBAErBrB,EAAE,oBAAoBuB,MAAK,WACvBvB,EAAEwB,MAAMV,KAAK,MAAOQ"}
\ No newline at end of file
+{"version":3,"file":"expand_contract.min.js","sources":["../src/expand_contract.js"],"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 * Potential user selector module.\n *\n * @module tool_dataprivacy/expand_contract\n * @copyright 2018 Adrian Greeve\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\ndefine(['jquery', 'core/url', 'core/str', 'core/notification'], function($, url, str, Notification) {\n\n var expandedImage = $('');\n var collapsedImage = $('');\n\n /*\n * Class names to apply when expanding/collapsing nodes.\n */\n var CLASSES = {\n EXPAND: 'fa-caret-right',\n COLLAPSE: 'fa-caret-down'\n };\n\n return /** @alias module:tool_dataprivacy/expand-collapse */ {\n /**\n * Expand or collapse a selected node.\n *\n * @param {object} targetnode The node that we want to expand / collapse\n * @param {object} thisnode The node that was clicked.\n */\n expandCollapse: function(targetnode, thisnode) {\n if (targetnode.hasClass('hide')) {\n targetnode.removeClass('hide');\n targetnode.addClass('visible');\n targetnode.attr('aria-expanded', true);\n thisnode.find(':header i.fa').removeClass(CLASSES.EXPAND);\n thisnode.find(':header i.fa').addClass(CLASSES.COLLAPSE);\n thisnode.find(':header img.icon').attr('src', expandedImage.attr('src'));\n } else {\n targetnode.removeClass('visible');\n targetnode.addClass('hide');\n targetnode.attr('aria-expanded', false);\n thisnode.find(':header i.fa').removeClass(CLASSES.COLLAPSE);\n thisnode.find(':header i.fa').addClass(CLASSES.EXPAND);\n thisnode.find(':header img.icon').attr('src', collapsedImage.attr('src'));\n }\n },\n\n /**\n * Expand or collapse all nodes on this page.\n *\n * @param {string} nextstate The next state to change to.\n */\n expandCollapseAll: function(nextstate) {\n var currentstate = (nextstate == 'visible') ? 'hide' : 'visible';\n var ariaexpandedstate = (nextstate == 'visible') ? true : false;\n var iconclassnow = (nextstate == 'visible') ? CLASSES.EXPAND : CLASSES.COLLAPSE;\n var iconclassnext = (nextstate == 'visible') ? CLASSES.COLLAPSE : CLASSES.EXPAND;\n var imagenow = (nextstate == 'visible') ? expandedImage.attr('src') : collapsedImage.attr('src');\n $('.' + currentstate).each(function() {\n $(this).removeClass(currentstate);\n $(this).addClass(nextstate);\n $(this).attr('aria-expanded', ariaexpandedstate);\n });\n $('.tool_dataprivacy-expand-all').data('visibilityState', currentstate);\n\n str.get_string(currentstate, 'tool_dataprivacy').then(function(langString) {\n $('.tool_dataprivacy-expand-all').html(langString);\n return;\n }).catch(Notification.exception);\n\n $(':header i.fa').each(function() {\n $(this).removeClass(iconclassnow);\n $(this).addClass(iconclassnext);\n });\n $(':header img.icon').each(function() {\n $(this).attr('src', imagenow);\n });\n }\n };\n});\n"],"names":["define","$","url","str","Notification","expandedImage","imageUrl","collapsedImage","CLASSES","expandCollapse","targetnode","thisnode","hasClass","removeClass","addClass","attr","find","expandCollapseAll","nextstate","currentstate","ariaexpandedstate","iconclassnow","iconclassnext","imagenow","each","this","data","get_string","then","langString","html","catch","exception"],"mappings":";;;;;;;AAuBAA,0CAAO,CAAC,SAAU,WAAY,WAAY,sBAAsB,SAASC,EAAGC,IAAKC,IAAKC,cAElF,IAAIC,cAAgBJ,EAAE,oBAAsBC,IAAII,SAAS,cAAgB,OACrEC,eAAiBN,EAAE,oBAAsBC,IAAII,SAAS,eAAiB,OAKvEE,eACQ,iBADRA,iBAEU,gBAGd,MAA6D,CAOzDC,eAAgB,SAASC,WAAYC,UAC7BD,WAAWE,SAAS,SACpBF,WAAWG,YAAY,QACvBH,WAAWI,SAAS,WACpBJ,WAAWK,KAAK,iBAAiB,GACjCJ,SAASK,KAAK,gBAAgBH,YAAYL,gBAC1CG,SAASK,KAAK,gBAAgBF,SAASN,kBACvCG,SAASK,KAAK,oBAAoBD,KAAK,MAAOV,cAAcU,KAAK,UAEjEL,WAAWG,YAAY,WACvBH,WAAWI,SAAS,QACpBJ,WAAWK,KAAK,iBAAiB,GACjCJ,SAASK,KAAK,gBAAgBH,YAAYL,kBAC1CG,SAASK,KAAK,gBAAgBF,SAASN,gBACvCG,SAASK,KAAK,oBAAoBD,KAAK,MAAOR,eAAeQ,KAAK,QAEzE,EAODE,kBAAmB,SAASC,WACxB,IAAIC,aAA6B,WAAbD,UAA0B,OAAS,UACnDE,kBAAkC,WAAbF,UACrBG,aAA6B,WAAbH,UAA0BV,eAAiBA,iBAC3Dc,cAA8B,WAAbJ,UAA0BV,iBAAmBA,eAC9De,SAAyB,WAAbL,UAA0Bb,cAAcU,KAAK,OAASR,eAAeQ,KAAK,OAC1Fd,EAAE,IAAMkB,cAAcK,MAAK,WACvBvB,EAAEwB,MAAMZ,YAAYM,cACpBlB,EAAEwB,MAAMX,SAASI,WACjBjB,EAAEwB,MAAMV,KAAK,gBAAiBK,kBAClC,IACAnB,EAAE,gCAAgCyB,KAAK,kBAAmBP,cAE1DhB,IAAIwB,WAAWR,aAAc,oBAAoBS,MAAK,SAASC,YAC3D5B,EAAE,gCAAgC6B,KAAKD,WAE1C,IAAEE,MAAM3B,aAAa4B,WAEtB/B,EAAE,gBAAgBuB,MAAK,WACnBvB,EAAEwB,MAAMZ,YAAYQ,cACpBpB,EAAEwB,MAAMX,SAASQ,cACrB,IACArB,EAAE,oBAAoBuB,MAAK,WACvBvB,EAAEwB,MAAMV,KAAK,MAAOQ,SACxB,GACJ,EAER"}
\ No newline at end of file
diff --git a/admin/tool/dataprivacy/amd/build/form-user-selector.min.js.map b/admin/tool/dataprivacy/amd/build/form-user-selector.min.js.map
index 5d218425475..2c67080082d 100644
--- a/admin/tool/dataprivacy/amd/build/form-user-selector.min.js.map
+++ b/admin/tool/dataprivacy/amd/build/form-user-selector.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"form-user-selector.min.js","sources":["../src/form-user-selector.js"],"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 * Potential user selector module.\n *\n * @module tool_dataprivacy/form-user-selector\n * @copyright 2018 Jun Pataleta\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\ndefine(['jquery', 'core/ajax', 'core/templates'], function($, Ajax, Templates) {\n\n return /** @alias module:tool_dataprivacy/form-user-selector */ {\n\n processResults: function(selector, results) {\n var users = [];\n $.each(results, function(index, user) {\n users.push({\n value: user.id,\n label: user._label\n });\n });\n return users;\n },\n\n transport: function(selector, query, success, failure) {\n var promise;\n\n promise = Ajax.call([{\n methodname: 'tool_dataprivacy_get_users',\n args: {\n query: query\n }\n }]);\n\n promise[0].then(function(results) {\n var promises = [],\n i = 0;\n\n // Render the label.\n $.each(results, function(index, user) {\n promises.push(Templates.render('tool_dataprivacy/form-user-selector-suggestion', user));\n });\n\n // Apply the label to the results.\n return $.when.apply($.when, promises).then(function() {\n var args = arguments;\n $.each(results, function(index, user) {\n user._label = args[i];\n i++;\n });\n success(results);\n return;\n });\n\n }).fail(failure);\n }\n\n };\n\n});\n"],"names":["define","$","Ajax","Templates","processResults","selector","results","users","each","index","user","push","value","id","label","_label","transport","query","success","failure","call","methodname","args","then","promises","i","render","when","apply","arguments","fail"],"mappings":";;;;;;;AAuBAA,6CAAO,CAAC,SAAU,YAAa,mBAAmB,SAASC,EAAGC,KAAMC,iBAEA,CAE5DC,eAAgB,SAASC,SAAUC,aAC3BC,MAAQ,UACZN,EAAEO,KAAKF,SAAS,SAASG,MAAOC,MAC5BH,MAAMI,KAAK,CACPC,MAAOF,KAAKG,GACZC,MAAOJ,KAAKK,YAGbR,OAGXS,UAAW,SAASX,SAAUY,MAAOC,QAASC,SAGhCjB,KAAKkB,KAAK,CAAC,CACjBC,WAAY,6BACZC,KAAM,CACFL,MAAOA,UAIP,GAAGM,MAAK,SAASjB,aACjBkB,SAAW,GACXC,EAAI,SAGRxB,EAAEO,KAAKF,SAAS,SAASG,MAAOC,MAC5Bc,SAASb,KAAKR,UAAUuB,OAAO,iDAAkDhB,UAI9ET,EAAE0B,KAAKC,MAAM3B,EAAE0B,KAAMH,UAAUD,MAAK,eACnCD,KAAOO,UACX5B,EAAEO,KAAKF,SAAS,SAASG,MAAOC,MAC5BA,KAAKK,OAASO,KAAKG,GACnBA,OAEJP,QAAQZ,eAIbwB,KAAKX"}
\ No newline at end of file
+{"version":3,"file":"form-user-selector.min.js","sources":["../src/form-user-selector.js"],"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 * Potential user selector module.\n *\n * @module tool_dataprivacy/form-user-selector\n * @copyright 2018 Jun Pataleta\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\ndefine(['jquery', 'core/ajax', 'core/templates'], function($, Ajax, Templates) {\n\n return /** @alias module:tool_dataprivacy/form-user-selector */ {\n\n processResults: function(selector, results) {\n var users = [];\n $.each(results, function(index, user) {\n users.push({\n value: user.id,\n label: user._label\n });\n });\n return users;\n },\n\n transport: function(selector, query, success, failure) {\n var promise;\n\n promise = Ajax.call([{\n methodname: 'tool_dataprivacy_get_users',\n args: {\n query: query\n }\n }]);\n\n promise[0].then(function(results) {\n var promises = [],\n i = 0;\n\n // Render the label.\n $.each(results, function(index, user) {\n promises.push(Templates.render('tool_dataprivacy/form-user-selector-suggestion', user));\n });\n\n // Apply the label to the results.\n return $.when.apply($.when, promises).then(function() {\n var args = arguments;\n $.each(results, function(index, user) {\n user._label = args[i];\n i++;\n });\n success(results);\n return;\n });\n\n }).fail(failure);\n }\n\n };\n\n});\n"],"names":["define","$","Ajax","Templates","processResults","selector","results","users","each","index","user","push","value","id","label","_label","transport","query","success","failure","call","methodname","args","then","promises","i","render","when","apply","arguments","fail"],"mappings":";;;;;;;AAuBAA,OAAO,sCAAA,CAAC,SAAU,YAAa,mBAAmB,SAASC,EAAGC,KAAMC,WAEhE,MAAgE,CAE5DC,eAAgB,SAASC,SAAUC,SAC/B,IAAIC,MAAQ,GAOZ,OANAN,EAAEO,KAAKF,SAAS,SAASG,MAAOC,MAC5BH,MAAMI,KAAK,CACPC,MAAOF,KAAKG,GACZC,MAAOJ,KAAKK,QAEpB,IACOR,KACV,EAEDS,UAAW,SAASX,SAAUY,MAAOC,QAASC,SAGhCjB,KAAKkB,KAAK,CAAC,CACjBC,WAAY,6BACZC,KAAM,CACFL,MAAOA,UAIP,GAAGM,MAAK,SAASjB,SACrB,IAAIkB,SAAW,GACXC,EAAI,EAQR,OALAxB,EAAEO,KAAKF,SAAS,SAASG,MAAOC,MAC5Bc,SAASb,KAAKR,UAAUuB,OAAO,iDAAkDhB,MACrF,IAGOT,EAAE0B,KAAKC,MAAM3B,EAAE0B,KAAMH,UAAUD,MAAK,WACvC,IAAID,KAAOO,UACX5B,EAAEO,KAAKF,SAAS,SAASG,MAAOC,MAC5BA,KAAKK,OAASO,KAAKG,GACnBA,GACJ,IACAP,QAAQZ,QAEZ,GAEJ,IAAGwB,KAAKX,QACZ,EAIR"}
\ No newline at end of file
diff --git a/admin/tool/dataprivacy/amd/build/myrequestactions.min.js.map b/admin/tool/dataprivacy/amd/build/myrequestactions.min.js.map
index ac1d3817f17..ea946aedb19 100644
--- a/admin/tool/dataprivacy/amd/build/myrequestactions.min.js.map
+++ b/admin/tool/dataprivacy/amd/build/myrequestactions.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"myrequestactions.min.js","sources":["../src/myrequestactions.js"],"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 * AMD module to enable users to manage their own data requests.\n *\n * @module tool_dataprivacy/myrequestactions\n * @copyright 2018 Jun Pataleta\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Ajax from 'core/ajax';\nimport Notification from 'core/notification';\nimport Pending from 'core/pending';\nimport {get_strings as getStrings} from 'core/str';\n\nconst SELECTORS = {\n CANCEL_REQUEST: '[data-action=\"cancel\"][data-requestid]',\n};\n\n/**\n * Initialize module\n */\nexport const init = () => {\n document.addEventListener('click', event => {\n const triggerElement = event.target.closest(SELECTORS.CANCEL_REQUEST);\n if (triggerElement === null) {\n return;\n }\n\n event.preventDefault();\n\n const requiredStrings = [\n {key: 'cancelrequest', component: 'tool_dataprivacy'},\n {key: 'cancelrequestconfirmation', component: 'tool_dataprivacy'},\n ];\n\n getStrings(requiredStrings).then(([cancelRequest, cancelConfirm]) => {\n return Notification.confirm(cancelRequest, cancelConfirm, cancelRequest, null, () => {\n const pendingPromise = new Pending('tool/dataprivacy:cancelRequest');\n const request = {\n methodname: 'tool_dataprivacy_cancel_data_request',\n args: {requestid: triggerElement.dataset.requestid}\n };\n\n Ajax.call([request])[0].then(response => {\n if (response.result) {\n window.location.reload();\n } else {\n Notification.addNotification({\n type: 'error',\n message: response.warnings[0].message\n });\n }\n return pendingPromise.resolve();\n }).catch(Notification.exception);\n });\n }).catch();\n });\n};\n"],"names":["SELECTORS","document","addEventListener","event","triggerElement","target","closest","preventDefault","key","component","then","_ref","cancelRequest","cancelConfirm","Notification","confirm","pendingPromise","Pending","request","methodname","args","requestid","dataset","call","response","result","window","location","reload","addNotification","type","message","warnings","resolve","catch","exception"],"mappings":";;;;;;;0NA4BMA,yBACc,uDAMA,KAChBC,SAASC,iBAAiB,SAASC,cACzBC,eAAiBD,MAAME,OAAOC,QAAQN,6BACrB,OAAnBI,sBAIJD,MAAMI,sCAEkB,CACpB,CAACC,IAAK,gBAAiBC,UAAW,oBAClC,CAACD,IAAK,4BAA6BC,UAAW,sBAGtBC,MAAKC,WAAEC,cAAeC,2BACvCC,sBAAaC,QAAQH,cAAeC,cAAeD,cAAe,MAAM,WACrEI,eAAiB,IAAIC,iBAAQ,kCAC7BC,QAAU,CACZC,WAAY,uCACZC,KAAM,CAACC,UAAWjB,eAAekB,QAAQD,0BAGxCE,KAAK,CAACL,UAAU,GAAGR,MAAKc,WACrBA,SAASC,OACTC,OAAOC,SAASC,+BAEHC,gBAAgB,CACzBC,KAAM,QACNC,QAASP,SAASQ,SAAS,GAAGD,UAG/Bf,eAAeiB,aACvBC,MAAMpB,sBAAaqB,iBAE3BD"}
\ No newline at end of file
+{"version":3,"file":"myrequestactions.min.js","sources":["../src/myrequestactions.js"],"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 * AMD module to enable users to manage their own data requests.\n *\n * @module tool_dataprivacy/myrequestactions\n * @copyright 2018 Jun Pataleta\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Ajax from 'core/ajax';\nimport Notification from 'core/notification';\nimport Pending from 'core/pending';\nimport {get_strings as getStrings} from 'core/str';\n\nconst SELECTORS = {\n CANCEL_REQUEST: '[data-action=\"cancel\"][data-requestid]',\n};\n\n/**\n * Initialize module\n */\nexport const init = () => {\n document.addEventListener('click', event => {\n const triggerElement = event.target.closest(SELECTORS.CANCEL_REQUEST);\n if (triggerElement === null) {\n return;\n }\n\n event.preventDefault();\n\n const requiredStrings = [\n {key: 'cancelrequest', component: 'tool_dataprivacy'},\n {key: 'cancelrequestconfirmation', component: 'tool_dataprivacy'},\n ];\n\n getStrings(requiredStrings).then(([cancelRequest, cancelConfirm]) => {\n return Notification.confirm(cancelRequest, cancelConfirm, cancelRequest, null, () => {\n const pendingPromise = new Pending('tool/dataprivacy:cancelRequest');\n const request = {\n methodname: 'tool_dataprivacy_cancel_data_request',\n args: {requestid: triggerElement.dataset.requestid}\n };\n\n Ajax.call([request])[0].then(response => {\n if (response.result) {\n window.location.reload();\n } else {\n Notification.addNotification({\n type: 'error',\n message: response.warnings[0].message\n });\n }\n return pendingPromise.resolve();\n }).catch(Notification.exception);\n });\n }).catch();\n });\n};\n"],"names":["_interopRequireDefault","obj","__esModule","default","_ajax","_notification","_pending","SELECTORS","_exports","init","document","addEventListener","event","triggerElement","target","closest","preventDefault","getStrings","key","component","then","_ref","cancelRequest","cancelConfirm","Notification","confirm","pendingPromise","Pending","request","methodname","args","requestid","dataset","Ajax","call","response","result","window","location","reload","addNotification","type","message","warnings","resolve","catch","exception"],"mappings":"uKAyBmC,SAAAA,uBAAAC,KAAAA,OAAAA,KAAAA,IAAAC,WAAAD,IAAAE,CAAAA,QAAAF,IAAA;;;;;;;kFAFnCG,MAAAJ,uBAAAI,OACAC,cAAAL,uBAAAK,eACAC,SAAAN,uBAAAM,UAGA,MAAMC,yBACc,yCA0ClBC,SAAAC,KApCkBA,KAChBC,SAASC,iBAAiB,SAASC,QAC/B,MAAMC,eAAiBD,MAAME,OAAOC,QAAQR,0BAC5C,GAAuB,OAAnBM,eACA,OAGJD,MAAMI,kBAON,EAAAC,KAAAA,aALwB,CACpB,CAACC,IAAK,gBAAiBC,UAAW,oBAClC,CAACD,IAAK,4BAA6BC,UAAW,sBAGtBC,MAAKC,OAAoC,IAAlCC,cAAeC,eAAcF,KAC5D,OAAOG,cAAYrB,QAACsB,QAAQH,cAAeC,cAAeD,cAAe,MAAM,KAC3E,MAAMI,eAAiB,IAAIC,SAAOxB,QAAC,kCAC7ByB,QAAU,CACZC,WAAY,uCACZC,KAAM,CAACC,UAAWlB,eAAemB,QAAQD,YAG7CE,MAAAA,QAAKC,KAAK,CAACN,UAAU,GAAGR,MAAKe,WACrBA,SAASC,OACTC,OAAOC,SAASC,SAEhBf,cAAYrB,QAACqC,gBAAgB,CACzBC,KAAM,QACNC,QAASP,SAASQ,SAAS,GAAGD,UAG/BhB,eAAekB,aACvBC,MAAMrB,cAAYrB,QAAC2C,UAAU,GAClC,IACHD,OAAO,GACZ,CACJ"}
\ No newline at end of file
diff --git a/admin/tool/dataprivacy/amd/build/purposesactions.min.js.map b/admin/tool/dataprivacy/amd/build/purposesactions.min.js.map
index 6e7f8f4da8f..a692a2ffe22 100644
--- a/admin/tool/dataprivacy/amd/build/purposesactions.min.js.map
+++ b/admin/tool/dataprivacy/amd/build/purposesactions.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"purposesactions.min.js","sources":["../src/purposesactions.js"],"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 * AMD module for purposes actions.\n *\n * @module tool_dataprivacy/purposesactions\n * @copyright 2018 David Monllao\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine([\n 'jquery',\n 'core/ajax',\n 'core/notification',\n 'core/str',\n 'core/modal_factory',\n 'core/modal_events'],\nfunction($, Ajax, Notification, Str, ModalFactory, ModalEvents) {\n\n /**\n * List of action selectors.\n *\n * @type {{DELETE: string}}\n */\n var ACTIONS = {\n DELETE: '[data-action=\"deletepurpose\"]',\n };\n\n /**\n * PurposesActions class.\n */\n var PurposesActions = function() {\n this.registerEvents();\n };\n\n /**\n * Register event listeners.\n */\n PurposesActions.prototype.registerEvents = function() {\n $(ACTIONS.DELETE).click(function(e) {\n e.preventDefault();\n\n var id = $(this).data('id');\n var purposename = $(this).data('name');\n var stringkeys = [\n {\n key: 'deletepurpose',\n component: 'tool_dataprivacy'\n },\n {\n key: 'deletepurposetext',\n component: 'tool_dataprivacy',\n param: purposename\n },\n {\n key: 'delete'\n }\n ];\n\n Str.get_strings(stringkeys).then(function(langStrings) {\n var title = langStrings[0];\n var confirmMessage = langStrings[1];\n var buttonText = langStrings[2];\n return ModalFactory.create({\n title: title,\n body: confirmMessage,\n type: ModalFactory.types.SAVE_CANCEL\n }).then(function(modal) {\n modal.setSaveButtonText(buttonText);\n\n // Handle save event.\n modal.getRoot().on(ModalEvents.save, function() {\n\n var request = {\n methodname: 'tool_dataprivacy_delete_purpose',\n args: {'id': id}\n };\n\n Ajax.call([request])[0].done(function(data) {\n if (data.result) {\n $('tr[data-purposeid=\"' + id + '\"]').remove();\n } else {\n Notification.addNotification({\n message: data.warnings[0].message,\n type: 'error'\n });\n }\n }).fail(Notification.exception);\n });\n\n // Handle hidden event.\n modal.getRoot().on(ModalEvents.hidden, function() {\n // Destroy when hidden.\n modal.destroy();\n });\n\n return modal;\n });\n }).done(function(modal) {\n modal.show();\n\n }).fail(Notification.exception);\n });\n };\n\n return /** @alias module:tool_dataprivacy/purposesactions */ {\n // Public variables and functions.\n\n /**\n * Initialise the module.\n *\n * @method init\n * @return {PurposesActions}\n */\n 'init': function() {\n return new PurposesActions();\n }\n };\n});\n"],"names":["define","$","Ajax","Notification","Str","ModalFactory","ModalEvents","ACTIONS","PurposesActions","registerEvents","prototype","click","e","preventDefault","id","this","data","stringkeys","key","component","param","get_strings","then","langStrings","title","confirmMessage","buttonText","create","body","type","types","SAVE_CANCEL","modal","setSaveButtonText","getRoot","on","save","request","methodname","args","call","done","result","remove","addNotification","message","warnings","fail","exception","hidden","destroy","show"],"mappings":";;;;;;;AAsBAA,0CAAO,CACH,SACA,YACA,oBACA,WACA,qBACA,sBACJ,SAASC,EAAGC,KAAMC,aAAcC,IAAKC,aAAcC,iBAO3CC,eACQ,gCAMRC,gBAAkB,gBACbC,yBAMTD,gBAAgBE,UAAUD,eAAiB,WACvCR,EAAEM,gBAAgBI,OAAM,SAASC,GAC7BA,EAAEC,qBAEEC,GAAKb,EAAEc,MAAMC,KAAK,MAElBC,WAAa,CACb,CACIC,IAAK,gBACLC,UAAW,oBAEf,CACID,IAAK,oBACLC,UAAW,mBACXC,MATUnB,EAAEc,MAAMC,KAAK,SAW3B,CACIE,IAAK,WAIbd,IAAIiB,YAAYJ,YAAYK,MAAK,SAASC,iBAClCC,MAAQD,YAAY,GACpBE,eAAiBF,YAAY,GAC7BG,WAAaH,YAAY,UACtBlB,aAAasB,OAAO,CACvBH,MAAOA,MACPI,KAAMH,eACNI,KAAMxB,aAAayB,MAAMC,cAC1BT,MAAK,SAASU,cACbA,MAAMC,kBAAkBP,YAGxBM,MAAME,UAAUC,GAAG7B,YAAY8B,MAAM,eAE7BC,QAAU,CACVC,WAAY,kCACZC,KAAM,IAAOzB,KAGjBZ,KAAKsC,KAAK,CAACH,UAAU,GAAGI,MAAK,SAASzB,MAC9BA,KAAK0B,OACLzC,EAAE,sBAAwBa,GAAK,MAAM6B,SAErCxC,aAAayC,gBAAgB,CACzBC,QAAS7B,KAAK8B,SAAS,GAAGD,QAC1BhB,KAAM,aAGfkB,KAAK5C,aAAa6C,cAIzBhB,MAAME,UAAUC,GAAG7B,YAAY2C,QAAQ,WAEnCjB,MAAMkB,aAGHlB,YAEZS,MAAK,SAAST,OACbA,MAAMmB,UAEPJ,KAAK5C,aAAa6C,eAIgC,MASjD,kBACG,IAAIxC"}
\ No newline at end of file
+{"version":3,"file":"purposesactions.min.js","sources":["../src/purposesactions.js"],"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 * AMD module for purposes actions.\n *\n * @module tool_dataprivacy/purposesactions\n * @copyright 2018 David Monllao\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine([\n 'jquery',\n 'core/ajax',\n 'core/notification',\n 'core/str',\n 'core/modal_factory',\n 'core/modal_events'],\nfunction($, Ajax, Notification, Str, ModalFactory, ModalEvents) {\n\n /**\n * List of action selectors.\n *\n * @type {{DELETE: string}}\n */\n var ACTIONS = {\n DELETE: '[data-action=\"deletepurpose\"]',\n };\n\n /**\n * PurposesActions class.\n */\n var PurposesActions = function() {\n this.registerEvents();\n };\n\n /**\n * Register event listeners.\n */\n PurposesActions.prototype.registerEvents = function() {\n $(ACTIONS.DELETE).click(function(e) {\n e.preventDefault();\n\n var id = $(this).data('id');\n var purposename = $(this).data('name');\n var stringkeys = [\n {\n key: 'deletepurpose',\n component: 'tool_dataprivacy'\n },\n {\n key: 'deletepurposetext',\n component: 'tool_dataprivacy',\n param: purposename\n },\n {\n key: 'delete'\n }\n ];\n\n Str.get_strings(stringkeys).then(function(langStrings) {\n var title = langStrings[0];\n var confirmMessage = langStrings[1];\n var buttonText = langStrings[2];\n return ModalFactory.create({\n title: title,\n body: confirmMessage,\n type: ModalFactory.types.SAVE_CANCEL\n }).then(function(modal) {\n modal.setSaveButtonText(buttonText);\n\n // Handle save event.\n modal.getRoot().on(ModalEvents.save, function() {\n\n var request = {\n methodname: 'tool_dataprivacy_delete_purpose',\n args: {'id': id}\n };\n\n Ajax.call([request])[0].done(function(data) {\n if (data.result) {\n $('tr[data-purposeid=\"' + id + '\"]').remove();\n } else {\n Notification.addNotification({\n message: data.warnings[0].message,\n type: 'error'\n });\n }\n }).fail(Notification.exception);\n });\n\n // Handle hidden event.\n modal.getRoot().on(ModalEvents.hidden, function() {\n // Destroy when hidden.\n modal.destroy();\n });\n\n return modal;\n });\n }).done(function(modal) {\n modal.show();\n\n }).fail(Notification.exception);\n });\n };\n\n return /** @alias module:tool_dataprivacy/purposesactions */ {\n // Public variables and functions.\n\n /**\n * Initialise the module.\n *\n * @method init\n * @return {PurposesActions}\n */\n 'init': function() {\n return new PurposesActions();\n }\n };\n});\n"],"names":["define","$","Ajax","Notification","Str","ModalFactory","ModalEvents","ACTIONS","PurposesActions","this","registerEvents","prototype","click","e","preventDefault","id","data","stringkeys","key","component","param","get_strings","then","langStrings","title","confirmMessage","buttonText","create","body","type","types","SAVE_CANCEL","modal","setSaveButtonText","getRoot","on","save","request","methodname","args","call","done","result","remove","addNotification","message","warnings","fail","exception","hidden","destroy","show","init"],"mappings":";;;;;;;AAsBAA,OAAM,mCAAC,CACH,SACA,YACA,oBACA,WACA,qBACA,sBACJ,SAASC,EAAGC,KAAMC,aAAcC,IAAKC,aAAcC,aAO/C,IAAIC,eACQ,gCAMRC,gBAAkB,WAClBC,KAAKC,kBAyET,OAnEAF,gBAAgBG,UAAUD,eAAiB,WACvCT,EAAEM,gBAAgBK,OAAM,SAASC,GAC7BA,EAAEC,iBAEF,IAAIC,GAAKd,EAAEQ,MAAMO,KAAK,MAElBC,WAAa,CACb,CACIC,IAAK,gBACLC,UAAW,oBAEf,CACID,IAAK,oBACLC,UAAW,mBACXC,MATUnB,EAAEQ,MAAMO,KAAK,SAW3B,CACIE,IAAK,WAIbd,IAAIiB,YAAYJ,YAAYK,MAAK,SAASC,aACtC,IAAIC,MAAQD,YAAY,GACpBE,eAAiBF,YAAY,GAC7BG,WAAaH,YAAY,GAC7B,OAAOlB,aAAasB,OAAO,CACvBH,MAAOA,MACPI,KAAMH,eACNI,KAAMxB,aAAayB,MAAMC,cAC1BT,MAAK,SAASU,OA6Bb,OA5BAA,MAAMC,kBAAkBP,YAGxBM,MAAME,UAAUC,GAAG7B,YAAY8B,MAAM,WAEjC,IAAIC,QAAU,CACVC,WAAY,kCACZC,KAAM,CAACxB,GAAMA,KAGjBb,KAAKsC,KAAK,CAACH,UAAU,GAAGI,MAAK,SAASzB,MAC9BA,KAAK0B,OACLzC,EAAE,sBAAwBc,GAAK,MAAM4B,SAErCxC,aAAayC,gBAAgB,CACzBC,QAAS7B,KAAK8B,SAAS,GAAGD,QAC1BhB,KAAM,SAGjB,IAAEkB,KAAK5C,aAAa6C,UACzB,IAGAhB,MAAME,UAAUC,GAAG7B,YAAY2C,QAAQ,WAEnCjB,MAAMkB,SACV,IAEOlB,KACX,GACJ,IAAGS,MAAK,SAAST,OACbA,MAAMmB,MAET,IAAEJ,KAAK5C,aAAa6C,UACzB,KAGyD,CASzDI,KAAQ,WACJ,OAAO,IAAI5C,eACf,EAER"}
\ No newline at end of file
diff --git a/admin/tool/dataprivacy/amd/build/request_filter.min.js.map b/admin/tool/dataprivacy/amd/build/request_filter.min.js.map
index 2bc86c5ab28..88b14ec6118 100644
--- a/admin/tool/dataprivacy/amd/build/request_filter.min.js.map
+++ b/admin/tool/dataprivacy/amd/build/request_filter.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"request_filter.min.js","sources":["../src/request_filter.js"],"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 * JS module for the data requests filter.\n *\n * @module tool_dataprivacy/request_filter\n * @copyright 2018 Jun Pataleta\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(['jquery', 'core/form-autocomplete', 'core/str', 'core/notification'], function($, Autocomplete, Str, Notification) {\n\n /**\n * Selectors.\n *\n * @access private\n * @type {{REQUEST_FILTERS: string}}\n */\n var SELECTORS = {\n REQUEST_FILTERS: '#request-filters'\n };\n\n /**\n * Init function.\n *\n * @method init\n * @private\n */\n var init = function() {\n var stringkeys = [\n {\n key: 'filter',\n component: 'moodle'\n },\n {\n key: 'nofiltersapplied',\n component: 'moodle'\n }\n ];\n\n Str.get_strings(stringkeys).then(function(langstrings) {\n var placeholder = langstrings[0];\n var noSelectionString = langstrings[1];\n return Autocomplete.enhance(SELECTORS.REQUEST_FILTERS, false, '', placeholder, false, true, noSelectionString, true);\n }).fail(Notification.exception);\n\n var last = $(SELECTORS.REQUEST_FILTERS).val();\n $(SELECTORS.REQUEST_FILTERS).on('change', function() {\n var current = $(this).val();\n // Prevent form from submitting unnecessarily, eg. on blur when no filter is selected.\n if (last.join(',') !== current.join(',')) {\n // If we're submitting without filters, set the hidden input 'filters-cleared' to 1.\n if (current.length === 0) {\n $('#filters-cleared').val(1);\n }\n $(this.form).submit();\n }\n });\n };\n\n return /** @alias module:core/form-autocomplete */ {\n /**\n * Initialise the unified user filter.\n *\n * @method init\n */\n init: function() {\n init();\n }\n };\n});\n"],"names":["define","$","Autocomplete","Str","Notification","SELECTORS","init","get_strings","key","component","then","langstrings","placeholder","noSelectionString","enhance","fail","exception","last","val","on","current","this","join","length","form","submit"],"mappings":";;;;;;;AAsBAA,yCAAO,CAAC,SAAU,yBAA0B,WAAY,sBAAsB,SAASC,EAAGC,aAAcC,IAAKC,kBAQrGC,0BACiB,yBAyC8B,CAM/CC,KAAM,YAtCC,WAYPH,IAAII,YAXa,CACb,CACIC,IAAK,SACLC,UAAW,UAEf,CACID,IAAK,mBACLC,UAAW,YAISC,MAAK,SAASC,iBAClCC,YAAcD,YAAY,GAC1BE,kBAAoBF,YAAY,UAC7BT,aAAaY,QAAQT,2BAA2B,EAAO,GAAIO,aAAa,GAAO,EAAMC,mBAAmB,MAChHE,KAAKX,aAAaY,eAEjBC,KAAOhB,EAAEI,2BAA2Ba,MACxCjB,EAAEI,2BAA2Bc,GAAG,UAAU,eAClCC,QAAUnB,EAAEoB,MAAMH,MAElBD,KAAKK,KAAK,OAASF,QAAQE,KAAK,OAET,IAAnBF,QAAQG,QACRtB,EAAE,oBAAoBiB,IAAI,GAE9BjB,EAAEoB,KAAKG,MAAMC,aAYjBnB"}
\ No newline at end of file
+{"version":3,"file":"request_filter.min.js","sources":["../src/request_filter.js"],"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 * JS module for the data requests filter.\n *\n * @module tool_dataprivacy/request_filter\n * @copyright 2018 Jun Pataleta\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(['jquery', 'core/form-autocomplete', 'core/str', 'core/notification'], function($, Autocomplete, Str, Notification) {\n\n /**\n * Selectors.\n *\n * @access private\n * @type {{REQUEST_FILTERS: string}}\n */\n var SELECTORS = {\n REQUEST_FILTERS: '#request-filters'\n };\n\n /**\n * Init function.\n *\n * @method init\n * @private\n */\n var init = function() {\n var stringkeys = [\n {\n key: 'filter',\n component: 'moodle'\n },\n {\n key: 'nofiltersapplied',\n component: 'moodle'\n }\n ];\n\n Str.get_strings(stringkeys).then(function(langstrings) {\n var placeholder = langstrings[0];\n var noSelectionString = langstrings[1];\n return Autocomplete.enhance(SELECTORS.REQUEST_FILTERS, false, '', placeholder, false, true, noSelectionString, true);\n }).fail(Notification.exception);\n\n var last = $(SELECTORS.REQUEST_FILTERS).val();\n $(SELECTORS.REQUEST_FILTERS).on('change', function() {\n var current = $(this).val();\n // Prevent form from submitting unnecessarily, eg. on blur when no filter is selected.\n if (last.join(',') !== current.join(',')) {\n // If we're submitting without filters, set the hidden input 'filters-cleared' to 1.\n if (current.length === 0) {\n $('#filters-cleared').val(1);\n }\n $(this.form).submit();\n }\n });\n };\n\n return /** @alias module:core/form-autocomplete */ {\n /**\n * Initialise the unified user filter.\n *\n * @method init\n */\n init: function() {\n init();\n }\n };\n});\n"],"names":["define","$","Autocomplete","Str","Notification","SELECTORS","init","get_strings","key","component","then","langstrings","placeholder","noSelectionString","enhance","fail","exception","last","val","on","current","this","join","length","form","submit"],"mappings":";;;;;;;AAsBAA,yCAAO,CAAC,SAAU,yBAA0B,WAAY,sBAAsB,SAASC,EAAGC,aAAcC,IAAKC,cAQzG,IAAIC,0BACiB,mBAyCrB,MAAmD,CAM/CC,KAAM,YAtCC,WAYPH,IAAII,YAXa,CACb,CACIC,IAAK,SACLC,UAAW,UAEf,CACID,IAAK,mBACLC,UAAW,YAISC,MAAK,SAASC,aACtC,IAAIC,YAAcD,YAAY,GAC1BE,kBAAoBF,YAAY,GACpC,OAAOT,aAAaY,QAAQT,2BAA2B,EAAO,GAAIO,aAAa,GAAO,EAAMC,mBAAmB,EAClH,IAAEE,KAAKX,aAAaY,WAErB,IAAIC,KAAOhB,EAAEI,2BAA2Ba,MACxCjB,EAAEI,2BAA2Bc,GAAG,UAAU,WACtC,IAAIC,QAAUnB,EAAEoB,MAAMH,MAElBD,KAAKK,KAAK,OAASF,QAAQE,KAAK,OAET,IAAnBF,QAAQG,QACRtB,EAAE,oBAAoBiB,IAAI,GAE9BjB,EAAEoB,KAAKG,MAAMC,SAErB,IAUInB,EACJ,EAER"}
\ No newline at end of file
diff --git a/admin/tool/dataprivacy/amd/build/requestactions.min.js.map b/admin/tool/dataprivacy/amd/build/requestactions.min.js.map
index 4d822b3e809..9f4c6fbc406 100644
--- a/admin/tool/dataprivacy/amd/build/requestactions.min.js.map
+++ b/admin/tool/dataprivacy/amd/build/requestactions.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"requestactions.min.js","sources":["../src/requestactions.js"],"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 * Request actions.\n *\n * @module tool_dataprivacy/requestactions\n * @copyright 2018 Jun Pataleta\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine([\n 'jquery',\n 'core/ajax',\n 'core/notification',\n 'core/str',\n 'core/modal_factory',\n 'core/modal_events',\n 'core/templates',\n 'tool_dataprivacy/data_request_modal',\n 'tool_dataprivacy/events'],\nfunction($, Ajax, Notification, Str, ModalFactory, ModalEvents, Templates, ModalDataRequest, DataPrivacyEvents) {\n\n /**\n * List of action selectors.\n *\n * @type {{APPROVE_REQUEST: string}}\n * @type {{DENY_REQUEST: string}}\n * @type {{VIEW_REQUEST: string}}\n * @type {{MARK_COMPLETE: string}}\n * @type {{CHANGE_BULK_ACTION: string}}\n * @type {{CONFIRM_BULK_ACTION: string}}\n * @type {{SELECT_ALL: string}}\n */\n var ACTIONS = {\n APPROVE_REQUEST: '[data-action=\"approve\"]',\n DENY_REQUEST: '[data-action=\"deny\"]',\n VIEW_REQUEST: '[data-action=\"view\"]',\n MARK_COMPLETE: '[data-action=\"complete\"]',\n CHANGE_BULK_ACTION: '[id=\"bulk-action\"]',\n CONFIRM_BULK_ACTION: '[id=\"confirm-bulk-action\"]',\n SELECT_ALL: '[data-action=\"selectall\"]'\n };\n\n /**\n * List of available bulk actions.\n *\n * @type {{APPROVE: number}}\n * @type {{DENY: number}}\n */\n var BULK_ACTIONS = {\n APPROVE: 1,\n DENY: 2\n };\n\n /**\n * List of selectors.\n *\n * @type {{SELECT_REQUEST: string}}\n */\n var SELECTORS = {\n SELECT_REQUEST: '.selectrequests'\n };\n\n /**\n * RequestActions class.\n */\n var RequestActions = function() {\n this.registerEvents();\n };\n\n /**\n * Register event listeners.\n */\n RequestActions.prototype.registerEvents = function() {\n $(ACTIONS.VIEW_REQUEST).click(function(e) {\n e.preventDefault();\n\n var requestId = $(this).data('requestid');\n\n // Cancel the request.\n var params = {\n 'requestid': requestId\n };\n\n var request = {\n methodname: 'tool_dataprivacy_get_data_request',\n args: params\n };\n\n var promises = Ajax.call([request]);\n $.when(promises[0]).then(function(data) {\n if (data.result) {\n return data.result;\n }\n // Fail.\n Notification.addNotification({\n message: data.warnings[0].message,\n type: 'error'\n });\n return false;\n\n }).then(function(data) {\n var body = Templates.render('tool_dataprivacy/request_details', data);\n var templateContext = {\n approvedeny: data.approvedeny,\n canmarkcomplete: data.canmarkcomplete\n };\n return ModalFactory.create({\n title: data.typename,\n body: body,\n type: ModalDataRequest.TYPE,\n large: true,\n templateContext: templateContext\n });\n\n }).then(function(modal) {\n // Handle approve event.\n modal.getRoot().on(DataPrivacyEvents.approve, function() {\n showConfirmation(DataPrivacyEvents.approve, approveEventWsData(requestId));\n });\n\n // Handle deny event.\n modal.getRoot().on(DataPrivacyEvents.deny, function() {\n showConfirmation(DataPrivacyEvents.deny, denyEventWsData(requestId));\n });\n\n // Handle send event.\n modal.getRoot().on(DataPrivacyEvents.complete, function() {\n var params = {\n 'requestid': requestId\n };\n handleSave('tool_dataprivacy_mark_complete', params);\n });\n\n // Handle hidden event.\n modal.getRoot().on(ModalEvents.hidden, function() {\n // Destroy when hidden.\n modal.destroy();\n });\n\n // Show the modal!\n modal.show();\n\n return;\n\n }).catch(Notification.exception);\n });\n\n $(ACTIONS.APPROVE_REQUEST).click(function(e) {\n e.preventDefault();\n\n var requestId = $(this).data('requestid');\n showConfirmation(DataPrivacyEvents.approve, approveEventWsData(requestId));\n });\n\n $(ACTIONS.DENY_REQUEST).click(function(e) {\n e.preventDefault();\n\n var requestId = $(this).data('requestid');\n showConfirmation(DataPrivacyEvents.deny, denyEventWsData(requestId));\n });\n\n $(ACTIONS.MARK_COMPLETE).click(function(e) {\n e.preventDefault();\n\n var requestId = $(this).data('requestid');\n showConfirmation(DataPrivacyEvents.complete, completeEventWsData(requestId));\n });\n\n $(ACTIONS.CONFIRM_BULK_ACTION).click(function() {\n var requestIds = [];\n var actionEvent = '';\n var wsdata = {};\n var bulkActionKeys = [\n {\n key: 'selectbulkaction',\n component: 'tool_dataprivacy'\n },\n {\n key: 'selectdatarequests',\n component: 'tool_dataprivacy'\n },\n {\n key: 'ok'\n }\n ];\n\n var bulkaction = parseInt($('#bulk-action').val());\n\n if (bulkaction != BULK_ACTIONS.APPROVE && bulkaction != BULK_ACTIONS.DENY) {\n Str.get_strings(bulkActionKeys).done(function(langStrings) {\n Notification.alert('', langStrings[0], langStrings[2]);\n }).fail(Notification.exception);\n\n return;\n }\n\n $(\".selectrequests:checked\").each(function() {\n requestIds.push($(this).val());\n });\n\n if (requestIds.length < 1) {\n Str.get_strings(bulkActionKeys).done(function(langStrings) {\n Notification.alert('', langStrings[1], langStrings[2]);\n }).fail(Notification.exception);\n\n return;\n }\n\n switch (bulkaction) {\n case BULK_ACTIONS.APPROVE:\n actionEvent = DataPrivacyEvents.bulkApprove;\n wsdata = bulkApproveEventWsData(requestIds);\n break;\n case BULK_ACTIONS.DENY:\n actionEvent = DataPrivacyEvents.bulkDeny;\n wsdata = bulkDenyEventWsData(requestIds);\n }\n\n showConfirmation(actionEvent, wsdata);\n });\n\n $(ACTIONS.SELECT_ALL).change(function(e) {\n e.preventDefault();\n\n var selectAll = $(this).is(':checked');\n $(SELECTORS.SELECT_REQUEST).prop('checked', selectAll);\n });\n };\n\n /**\n * Return the webservice data for the approve request action.\n *\n * @param {Number} requestId The ID of the request.\n * @return {Object}\n */\n function approveEventWsData(requestId) {\n return {\n 'wsfunction': 'tool_dataprivacy_approve_data_request',\n 'wsparams': {'requestid': requestId}\n };\n }\n\n /**\n * Return the webservice data for the bulk approve request action.\n *\n * @param {Array} requestIds The array of request ID's.\n * @return {Object}\n */\n function bulkApproveEventWsData(requestIds) {\n return {\n 'wsfunction': 'tool_dataprivacy_bulk_approve_data_requests',\n 'wsparams': {'requestids': requestIds}\n };\n }\n\n /**\n * Return the webservice data for the deny request action.\n *\n * @param {Number} requestId The ID of the request.\n * @return {Object}\n */\n function denyEventWsData(requestId) {\n return {\n 'wsfunction': 'tool_dataprivacy_deny_data_request',\n 'wsparams': {'requestid': requestId}\n };\n }\n\n /**\n * Return the webservice data for the bulk deny request action.\n *\n * @param {Array} requestIds The array of request ID's.\n * @return {Object}\n */\n function bulkDenyEventWsData(requestIds) {\n return {\n 'wsfunction': 'tool_dataprivacy_bulk_deny_data_requests',\n 'wsparams': {'requestids': requestIds}\n };\n }\n\n /**\n * Return the webservice data for the complete request action.\n *\n * @param {Number} requestId The ID of the request.\n * @return {Object}\n */\n function completeEventWsData(requestId) {\n return {\n 'wsfunction': 'tool_dataprivacy_mark_complete',\n 'wsparams': {'requestid': requestId}\n };\n }\n\n /**\n * Show the confirmation dialogue.\n *\n * @param {String} action The action name.\n * @param {Object} wsdata Object containing ws data.\n */\n function showConfirmation(action, wsdata) {\n var keys = [];\n\n switch (action) {\n case DataPrivacyEvents.approve:\n keys = [\n {\n key: 'approverequest',\n component: 'tool_dataprivacy'\n },\n {\n key: 'confirmapproval',\n component: 'tool_dataprivacy'\n }\n ];\n break;\n case DataPrivacyEvents.bulkApprove:\n keys = [\n {\n key: 'bulkapproverequests',\n component: 'tool_dataprivacy'\n },\n {\n key: 'confirmbulkapproval',\n component: 'tool_dataprivacy'\n }\n ];\n break;\n case DataPrivacyEvents.deny:\n keys = [\n {\n key: 'denyrequest',\n component: 'tool_dataprivacy'\n },\n {\n key: 'confirmdenial',\n component: 'tool_dataprivacy'\n }\n ];\n break;\n case DataPrivacyEvents.bulkDeny:\n keys = [\n {\n key: 'bulkdenyrequests',\n component: 'tool_dataprivacy'\n },\n {\n key: 'confirmbulkdenial',\n component: 'tool_dataprivacy'\n }\n ];\n break;\n case DataPrivacyEvents.complete:\n keys = [\n {\n key: 'markcomplete',\n component: 'tool_dataprivacy'\n },\n {\n key: 'confirmcompletion',\n component: 'tool_dataprivacy'\n }\n ];\n break;\n }\n\n var modalTitle = '';\n Str.get_strings(keys).then(function(langStrings) {\n modalTitle = langStrings[0];\n var confirmMessage = langStrings[1];\n return ModalFactory.create({\n title: modalTitle,\n body: confirmMessage,\n type: ModalFactory.types.SAVE_CANCEL\n });\n }).then(function(modal) {\n modal.setSaveButtonText(modalTitle);\n\n // Handle save event.\n modal.getRoot().on(ModalEvents.save, function() {\n handleSave(wsdata.wsfunction, wsdata.wsparams);\n });\n\n // Handle hidden event.\n modal.getRoot().on(ModalEvents.hidden, function() {\n // Destroy when hidden.\n modal.destroy();\n });\n\n modal.show();\n\n return;\n\n }).catch(Notification.exception);\n }\n\n /**\n * Calls a web service function and reloads the page on success and shows a notification.\n * Displays an error notification, otherwise.\n *\n * @param {String} wsfunction The web service function to call.\n * @param {Object} params The parameters for the web service functoon.\n */\n function handleSave(wsfunction, params) {\n // Confirm the request.\n var request = {\n methodname: wsfunction,\n args: params\n };\n\n Ajax.call([request])[0].done(function(data) {\n if (data.result) {\n // On success, reload the page so that the data request table will be updated.\n // TODO: Probably in the future, better to reload the table or the target data request via AJAX.\n window.location.reload();\n } else {\n // Add the notification.\n Notification.addNotification({\n message: data.warnings[0].message,\n type: 'error'\n });\n }\n }).fail(Notification.exception);\n }\n\n return RequestActions;\n});\n"],"names":["define","$","Ajax","Notification","Str","ModalFactory","ModalEvents","Templates","ModalDataRequest","DataPrivacyEvents","ACTIONS","BULK_ACTIONS","SELECTORS","RequestActions","registerEvents","approveEventWsData","requestId","denyEventWsData","showConfirmation","action","wsdata","keys","approve","key","component","bulkApprove","deny","bulkDeny","complete","modalTitle","get_strings","then","langStrings","confirmMessage","create","title","body","type","types","SAVE_CANCEL","modal","setSaveButtonText","getRoot","on","save","handleSave","wsfunction","wsparams","hidden","destroy","show","catch","exception","params","request","methodname","args","call","done","data","result","window","location","reload","addNotification","message","warnings","fail","prototype","click","e","preventDefault","this","promises","when","render","templateContext","approvedeny","canmarkcomplete","typename","TYPE","large","completeEventWsData","requestIds","actionEvent","bulkActionKeys","bulkaction","parseInt","val","each","push","length","alert","bulkApproveEventWsData","bulkDenyEventWsData","change","selectAll","is","prop"],"mappings":"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"}
\ No newline at end of file
+{"version":3,"file":"requestactions.min.js","sources":["../src/requestactions.js"],"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 * Request actions.\n *\n * @module tool_dataprivacy/requestactions\n * @copyright 2018 Jun Pataleta\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine([\n 'jquery',\n 'core/ajax',\n 'core/notification',\n 'core/str',\n 'core/modal_factory',\n 'core/modal_events',\n 'core/templates',\n 'tool_dataprivacy/data_request_modal',\n 'tool_dataprivacy/events'],\nfunction($, Ajax, Notification, Str, ModalFactory, ModalEvents, Templates, ModalDataRequest, DataPrivacyEvents) {\n\n /**\n * List of action selectors.\n *\n * @type {{APPROVE_REQUEST: string}}\n * @type {{DENY_REQUEST: string}}\n * @type {{VIEW_REQUEST: string}}\n * @type {{MARK_COMPLETE: string}}\n * @type {{CHANGE_BULK_ACTION: string}}\n * @type {{CONFIRM_BULK_ACTION: string}}\n * @type {{SELECT_ALL: string}}\n */\n var ACTIONS = {\n APPROVE_REQUEST: '[data-action=\"approve\"]',\n DENY_REQUEST: '[data-action=\"deny\"]',\n VIEW_REQUEST: '[data-action=\"view\"]',\n MARK_COMPLETE: '[data-action=\"complete\"]',\n CHANGE_BULK_ACTION: '[id=\"bulk-action\"]',\n CONFIRM_BULK_ACTION: '[id=\"confirm-bulk-action\"]',\n SELECT_ALL: '[data-action=\"selectall\"]'\n };\n\n /**\n * List of available bulk actions.\n *\n * @type {{APPROVE: number}}\n * @type {{DENY: number}}\n */\n var BULK_ACTIONS = {\n APPROVE: 1,\n DENY: 2\n };\n\n /**\n * List of selectors.\n *\n * @type {{SELECT_REQUEST: string}}\n */\n var SELECTORS = {\n SELECT_REQUEST: '.selectrequests'\n };\n\n /**\n * RequestActions class.\n */\n var RequestActions = function() {\n this.registerEvents();\n };\n\n /**\n * Register event listeners.\n */\n RequestActions.prototype.registerEvents = function() {\n $(ACTIONS.VIEW_REQUEST).click(function(e) {\n e.preventDefault();\n\n var requestId = $(this).data('requestid');\n\n // Cancel the request.\n var params = {\n 'requestid': requestId\n };\n\n var request = {\n methodname: 'tool_dataprivacy_get_data_request',\n args: params\n };\n\n var promises = Ajax.call([request]);\n $.when(promises[0]).then(function(data) {\n if (data.result) {\n return data.result;\n }\n // Fail.\n Notification.addNotification({\n message: data.warnings[0].message,\n type: 'error'\n });\n return false;\n\n }).then(function(data) {\n var body = Templates.render('tool_dataprivacy/request_details', data);\n var templateContext = {\n approvedeny: data.approvedeny,\n canmarkcomplete: data.canmarkcomplete\n };\n return ModalFactory.create({\n title: data.typename,\n body: body,\n type: ModalDataRequest.TYPE,\n large: true,\n templateContext: templateContext\n });\n\n }).then(function(modal) {\n // Handle approve event.\n modal.getRoot().on(DataPrivacyEvents.approve, function() {\n showConfirmation(DataPrivacyEvents.approve, approveEventWsData(requestId));\n });\n\n // Handle deny event.\n modal.getRoot().on(DataPrivacyEvents.deny, function() {\n showConfirmation(DataPrivacyEvents.deny, denyEventWsData(requestId));\n });\n\n // Handle send event.\n modal.getRoot().on(DataPrivacyEvents.complete, function() {\n var params = {\n 'requestid': requestId\n };\n handleSave('tool_dataprivacy_mark_complete', params);\n });\n\n // Handle hidden event.\n modal.getRoot().on(ModalEvents.hidden, function() {\n // Destroy when hidden.\n modal.destroy();\n });\n\n // Show the modal!\n modal.show();\n\n return;\n\n }).catch(Notification.exception);\n });\n\n $(ACTIONS.APPROVE_REQUEST).click(function(e) {\n e.preventDefault();\n\n var requestId = $(this).data('requestid');\n showConfirmation(DataPrivacyEvents.approve, approveEventWsData(requestId));\n });\n\n $(ACTIONS.DENY_REQUEST).click(function(e) {\n e.preventDefault();\n\n var requestId = $(this).data('requestid');\n showConfirmation(DataPrivacyEvents.deny, denyEventWsData(requestId));\n });\n\n $(ACTIONS.MARK_COMPLETE).click(function(e) {\n e.preventDefault();\n\n var requestId = $(this).data('requestid');\n showConfirmation(DataPrivacyEvents.complete, completeEventWsData(requestId));\n });\n\n $(ACTIONS.CONFIRM_BULK_ACTION).click(function() {\n var requestIds = [];\n var actionEvent = '';\n var wsdata = {};\n var bulkActionKeys = [\n {\n key: 'selectbulkaction',\n component: 'tool_dataprivacy'\n },\n {\n key: 'selectdatarequests',\n component: 'tool_dataprivacy'\n },\n {\n key: 'ok'\n }\n ];\n\n var bulkaction = parseInt($('#bulk-action').val());\n\n if (bulkaction != BULK_ACTIONS.APPROVE && bulkaction != BULK_ACTIONS.DENY) {\n Str.get_strings(bulkActionKeys).done(function(langStrings) {\n Notification.alert('', langStrings[0], langStrings[2]);\n }).fail(Notification.exception);\n\n return;\n }\n\n $(\".selectrequests:checked\").each(function() {\n requestIds.push($(this).val());\n });\n\n if (requestIds.length < 1) {\n Str.get_strings(bulkActionKeys).done(function(langStrings) {\n Notification.alert('', langStrings[1], langStrings[2]);\n }).fail(Notification.exception);\n\n return;\n }\n\n switch (bulkaction) {\n case BULK_ACTIONS.APPROVE:\n actionEvent = DataPrivacyEvents.bulkApprove;\n wsdata = bulkApproveEventWsData(requestIds);\n break;\n case BULK_ACTIONS.DENY:\n actionEvent = DataPrivacyEvents.bulkDeny;\n wsdata = bulkDenyEventWsData(requestIds);\n }\n\n showConfirmation(actionEvent, wsdata);\n });\n\n $(ACTIONS.SELECT_ALL).change(function(e) {\n e.preventDefault();\n\n var selectAll = $(this).is(':checked');\n $(SELECTORS.SELECT_REQUEST).prop('checked', selectAll);\n });\n };\n\n /**\n * Return the webservice data for the approve request action.\n *\n * @param {Number} requestId The ID of the request.\n * @return {Object}\n */\n function approveEventWsData(requestId) {\n return {\n 'wsfunction': 'tool_dataprivacy_approve_data_request',\n 'wsparams': {'requestid': requestId}\n };\n }\n\n /**\n * Return the webservice data for the bulk approve request action.\n *\n * @param {Array} requestIds The array of request ID's.\n * @return {Object}\n */\n function bulkApproveEventWsData(requestIds) {\n return {\n 'wsfunction': 'tool_dataprivacy_bulk_approve_data_requests',\n 'wsparams': {'requestids': requestIds}\n };\n }\n\n /**\n * Return the webservice data for the deny request action.\n *\n * @param {Number} requestId The ID of the request.\n * @return {Object}\n */\n function denyEventWsData(requestId) {\n return {\n 'wsfunction': 'tool_dataprivacy_deny_data_request',\n 'wsparams': {'requestid': requestId}\n };\n }\n\n /**\n * Return the webservice data for the bulk deny request action.\n *\n * @param {Array} requestIds The array of request ID's.\n * @return {Object}\n */\n function bulkDenyEventWsData(requestIds) {\n return {\n 'wsfunction': 'tool_dataprivacy_bulk_deny_data_requests',\n 'wsparams': {'requestids': requestIds}\n };\n }\n\n /**\n * Return the webservice data for the complete request action.\n *\n * @param {Number} requestId The ID of the request.\n * @return {Object}\n */\n function completeEventWsData(requestId) {\n return {\n 'wsfunction': 'tool_dataprivacy_mark_complete',\n 'wsparams': {'requestid': requestId}\n };\n }\n\n /**\n * Show the confirmation dialogue.\n *\n * @param {String} action The action name.\n * @param {Object} wsdata Object containing ws data.\n */\n function showConfirmation(action, wsdata) {\n var keys = [];\n\n switch (action) {\n case DataPrivacyEvents.approve:\n keys = [\n {\n key: 'approverequest',\n component: 'tool_dataprivacy'\n },\n {\n key: 'confirmapproval',\n component: 'tool_dataprivacy'\n }\n ];\n break;\n case DataPrivacyEvents.bulkApprove:\n keys = [\n {\n key: 'bulkapproverequests',\n component: 'tool_dataprivacy'\n },\n {\n key: 'confirmbulkapproval',\n component: 'tool_dataprivacy'\n }\n ];\n break;\n case DataPrivacyEvents.deny:\n keys = [\n {\n key: 'denyrequest',\n component: 'tool_dataprivacy'\n },\n {\n key: 'confirmdenial',\n component: 'tool_dataprivacy'\n }\n ];\n break;\n case DataPrivacyEvents.bulkDeny:\n keys = [\n {\n key: 'bulkdenyrequests',\n component: 'tool_dataprivacy'\n },\n {\n key: 'confirmbulkdenial',\n component: 'tool_dataprivacy'\n }\n ];\n break;\n case DataPrivacyEvents.complete:\n keys = [\n {\n key: 'markcomplete',\n component: 'tool_dataprivacy'\n },\n {\n key: 'confirmcompletion',\n component: 'tool_dataprivacy'\n }\n ];\n break;\n }\n\n var modalTitle = '';\n Str.get_strings(keys).then(function(langStrings) {\n modalTitle = langStrings[0];\n var confirmMessage = langStrings[1];\n return ModalFactory.create({\n title: modalTitle,\n body: confirmMessage,\n type: ModalFactory.types.SAVE_CANCEL\n });\n }).then(function(modal) {\n modal.setSaveButtonText(modalTitle);\n\n // Handle save event.\n modal.getRoot().on(ModalEvents.save, function() {\n handleSave(wsdata.wsfunction, wsdata.wsparams);\n });\n\n // Handle hidden event.\n modal.getRoot().on(ModalEvents.hidden, function() {\n // Destroy when hidden.\n modal.destroy();\n });\n\n modal.show();\n\n return;\n\n }).catch(Notification.exception);\n }\n\n /**\n * Calls a web service function and reloads the page on success and shows a notification.\n * Displays an error notification, otherwise.\n *\n * @param {String} wsfunction The web service function to call.\n * @param {Object} params The parameters for the web service functoon.\n */\n function handleSave(wsfunction, params) {\n // Confirm the request.\n var request = {\n methodname: wsfunction,\n args: params\n };\n\n Ajax.call([request])[0].done(function(data) {\n if (data.result) {\n // On success, reload the page so that the data request table will be updated.\n // TODO: Probably in the future, better to reload the table or the target data request via AJAX.\n window.location.reload();\n } else {\n // Add the notification.\n Notification.addNotification({\n message: data.warnings[0].message,\n type: 'error'\n });\n }\n }).fail(Notification.exception);\n }\n\n return RequestActions;\n});\n"],"names":["define","$","Ajax","Notification","Str","ModalFactory","ModalEvents","Templates","ModalDataRequest","DataPrivacyEvents","ACTIONS","BULK_ACTIONS","SELECTORS","RequestActions","this","registerEvents","approveEventWsData","requestId","wsfunction","wsparams","requestid","denyEventWsData","showConfirmation","action","wsdata","keys","approve","key","component","bulkApprove","deny","bulkDeny","complete","modalTitle","get_strings","then","langStrings","confirmMessage","create","title","body","type","types","SAVE_CANCEL","modal","setSaveButtonText","getRoot","on","save","handleSave","hidden","destroy","show","catch","exception","params","request","methodname","args","call","done","data","result","window","location","reload","addNotification","message","warnings","fail","prototype","click","e","preventDefault","promises","when","render","templateContext","approvedeny","canmarkcomplete","typename","TYPE","large","completeEventWsData","requestIds","actionEvent","bulkActionKeys","bulkaction","parseInt","val","each","push","length","alert","requestids","bulkApproveEventWsData","bulkDenyEventWsData","change","selectAll","is","prop"],"mappings":";;;;;;;AAsBAA,yCAAO,CACH,SACA,YACA,oBACA,WACA,qBACA,oBACA,iBACA,sCACA,4BACJ,SAASC,EAAGC,KAAMC,aAAcC,IAAKC,aAAcC,YAAaC,UAAWC,iBAAkBC,mBAazF,IAAIC,wBACiB,0BADjBA,qBAEc,uBAFdA,qBAGc,uBAHdA,sBAIe,2BAJfA,4BAMqB,6BANrBA,mBAOY,4BASZC,qBACS,EADTA,kBAEM,EAQNC,yBACgB,kBAMhBC,eAAiB,WACjBC,KAAKC,kBAyKT,SAASC,mBAAmBC,WACxB,MAAO,CACHC,WAAc,wCACdC,SAAY,CAACC,UAAaH,WAElC,CAqBA,SAASI,gBAAgBJ,WACrB,MAAO,CACHC,WAAc,qCACdC,SAAY,CAACC,UAAaH,WAElC,CAkCA,SAASK,iBAAiBC,OAAQC,QAC9B,IAAIC,KAAO,GAEX,OAAQF,QACJ,KAAKd,kBAAkBiB,QACnBD,KAAO,CACH,CACIE,IAAK,iBACLC,UAAW,oBAEf,CACID,IAAK,kBACLC,UAAW,qBAGnB,MACJ,KAAKnB,kBAAkBoB,YACnBJ,KAAO,CACH,CACIE,IAAK,sBACLC,UAAW,oBAEf,CACID,IAAK,sBACLC,UAAW,qBAGnB,MACJ,KAAKnB,kBAAkBqB,KACnBL,KAAO,CACH,CACIE,IAAK,cACLC,UAAW,oBAEf,CACID,IAAK,gBACLC,UAAW,qBAGnB,MACJ,KAAKnB,kBAAkBsB,SACnBN,KAAO,CACH,CACIE,IAAK,mBACLC,UAAW,oBAEf,CACID,IAAK,oBACLC,UAAW,qBAGnB,MACJ,KAAKnB,kBAAkBuB,SACnBP,KAAO,CACH,CACIE,IAAK,eACLC,UAAW,oBAEf,CACID,IAAK,oBACLC,UAAW,qBAM3B,IAAIK,WAAa,GACjB7B,IAAI8B,YAAYT,MAAMU,MAAK,SAASC,aAChCH,WAAaG,YAAY,GACzB,IAAIC,eAAiBD,YAAY,GACjC,OAAO/B,aAAaiC,OAAO,CACvBC,MAAON,WACPO,KAAMH,eACNI,KAAMpC,aAAaqC,MAAMC,aAEjC,IAAGR,MAAK,SAASS,OACbA,MAAMC,kBAAkBZ,YAGxBW,MAAME,UAAUC,GAAGzC,YAAY0C,MAAM,WACjCC,WAAWzB,OAAON,WAAYM,OAAOL,SACzC,IAGAyB,MAAME,UAAUC,GAAGzC,YAAY4C,QAAQ,WAEnCN,MAAMO,SACV,IAEAP,MAAMQ,MAIT,IAAEC,MAAMlD,aAAamD,UAC1B,CASA,SAASL,WAAW/B,WAAYqC,QAE5B,IAAIC,QAAU,CACVC,WAAYvC,WACZwC,KAAMH,QAGVrD,KAAKyD,KAAK,CAACH,UAAU,GAAGI,MAAK,SAASC,MAC9BA,KAAKC,OAGLC,OAAOC,SAASC,SAGhB9D,aAAa+D,gBAAgB,CACzBC,QAASN,KAAKO,SAAS,GAAGD,QAC1B1B,KAAM,SAGjB,IAAE4B,KAAKlE,aAAamD,UACzB,CAEA,OAjWAzC,eAAeyD,UAAUvD,eAAiB,WACtCd,EAAES,sBAAsB6D,OAAM,SAASC,GACnCA,EAAEC,iBAEF,IAAIxD,UAAYhB,EAAEa,MAAM+C,KAAK,aAOzBL,QAAU,CACVC,WAAY,oCACZC,KANS,CACTtC,UAAaH,YAQbyD,SAAWxE,KAAKyD,KAAK,CAACH,UAC1BvD,EAAE0E,KAAKD,SAAS,IAAIvC,MAAK,SAAS0B,MAC9B,OAAIA,KAAKC,OACED,KAAKC,QAGhB3D,aAAa+D,gBAAgB,CACzBC,QAASN,KAAKO,SAAS,GAAGD,QAC1B1B,KAAM,WAEH,EAEX,IAAGN,MAAK,SAAS0B,MACb,IAAIrB,KAAOjC,UAAUqE,OAAO,mCAAoCf,MAC5DgB,gBAAkB,CAClBC,YAAajB,KAAKiB,YAClBC,gBAAiBlB,KAAKkB,iBAE1B,OAAO1E,aAAaiC,OAAO,CACvBC,MAAOsB,KAAKmB,SACZxC,KAAMA,KACNC,KAAMjC,iBAAiByE,KACvBC,OAAO,EACPL,gBAAiBA,iBAGzB,IAAG1C,MAAK,SAASS,OAEbA,MAAME,UAAUC,GAAGtC,kBAAkBiB,SAAS,WAC1CJ,iBAAiBb,kBAAkBiB,QAASV,mBAAmBC,WACnE,IAGA2B,MAAME,UAAUC,GAAGtC,kBAAkBqB,MAAM,WACvCR,iBAAiBb,kBAAkBqB,KAAMT,gBAAgBJ,WAC7D,IAGA2B,MAAME,UAAUC,GAAGtC,kBAAkBuB,UAAU,WAI3CiB,WAAW,iCAHE,CACT7B,UAAaH,WAGrB,IAGA2B,MAAME,UAAUC,GAAGzC,YAAY4C,QAAQ,WAEnCN,MAAMO,SACV,IAGAP,MAAMQ,MAIT,IAAEC,MAAMlD,aAAamD,UAC1B,IAEArD,EAAES,yBAAyB6D,OAAM,SAASC,GACtCA,EAAEC,iBAEF,IAAIxD,UAAYhB,EAAEa,MAAM+C,KAAK,aAC7BvC,iBAAiBb,kBAAkBiB,QAASV,mBAAmBC,WACnE,IAEAhB,EAAES,sBAAsB6D,OAAM,SAASC,GACnCA,EAAEC,iBAEF,IAAIxD,UAAYhB,EAAEa,MAAM+C,KAAK,aAC7BvC,iBAAiBb,kBAAkBqB,KAAMT,gBAAgBJ,WAC7D,IAEAhB,EAAES,uBAAuB6D,OAAM,SAASC,GACpCA,EAAEC,iBAEF,IAAIxD,UAAYhB,EAAEa,MAAM+C,KAAK,aAC7BvC,iBAAiBb,kBAAkBuB,SA0H3C,SAA6Bf,WACzB,MAAO,CACHC,WAAc,iCACdC,SAAY,CAACC,UAAaH,WAElC,CA/HqDkE,CAAoBlE,WACrE,IAEAhB,EAAES,6BAA6B6D,OAAM,WACjC,IAAIa,WAAa,GACbC,YAAc,GACd7D,OAAS,CAAA,EACT8D,eAAiB,CACjB,CACI3D,IAAK,mBACLC,UAAW,oBAEf,CACID,IAAK,qBACLC,UAAW,oBAEf,CACID,IAAK,OAIT4D,WAAaC,SAASvF,EAAE,gBAAgBwF,OAE5C,GAAIF,YAAc5E,sBAAwB4E,YAAc5E,kBAYxD,GAJAV,EAAE,2BAA2ByF,MAAK,WAC9BN,WAAWO,KAAK1F,EAAEa,MAAM2E,MAC5B,IAEIL,WAAWQ,OAAS,EACpBxF,IAAI8B,YAAYoD,gBAAgB1B,MAAK,SAASxB,aAC1CjC,aAAa0F,MAAM,GAAIzD,YAAY,GAAIA,YAAY,GACtD,IAAEiC,KAAKlE,aAAamD,eAHzB,CAQA,OAAQiC,YACJ,KAAK5E,qBACD0E,YAAc5E,kBAAkBoB,YAChCL,OAqChB,SAAgC4D,YAC5B,MAAO,CACHlE,WAAc,8CACdC,SAAY,CAAC2E,WAAcV,YAEnC,CA1CyBW,CAAuBX,YAChC,MACJ,KAAKzE,kBACD0E,YAAc5E,kBAAkBsB,SAChCP,OA2DhB,SAA6B4D,YACzB,MAAO,CACHlE,WAAc,2CACdC,SAAY,CAAC2E,WAAcV,YAEnC,CAhEyBY,CAAoBZ,YAGrC9D,iBAAiB+D,YAAa7D,OAZ9B,MAjBIpB,IAAI8B,YAAYoD,gBAAgB1B,MAAK,SAASxB,aAC1CjC,aAAa0F,MAAM,GAAIzD,YAAY,GAAIA,YAAY,GACtD,IAAEiC,KAAKlE,aAAamD,UA4B7B,IAEArD,EAAES,oBAAoBuF,QAAO,SAASzB,GAClCA,EAAEC,iBAEF,IAAIyB,UAAYjG,EAAEa,MAAMqF,GAAG,YAC3BlG,EAAEW,0BAA0BwF,KAAK,UAAWF,UAChD,KAuMGrF,cACX"}
\ No newline at end of file
diff --git a/admin/tool/langimport/amd/build/search.min.js.map b/admin/tool/langimport/amd/build/search.min.js.map
index 273a4d3edba..f2437c3b1c0 100644
--- a/admin/tool/langimport/amd/build/search.min.js.map
+++ b/admin/tool/langimport/amd/build/search.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"search.min.js","sources":["../src/search.js"],"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 * Add search filtering of available language packs\n *\n * @module tool_langimport/search\n * @copyright 2021 Paul Holden \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Pending from 'core/pending';\nimport {debounce} from 'core/utils';\n\nconst SELECTORS = {\n AVAILABLE_LANG_SELECT: 'select',\n AVAILABLE_LANG_SEARCH: '[data-action=\"search\"]',\n};\n\nconst DEBOUNCE_TIMER = 250;\n\n/**\n * Initialize module\n *\n * @param {Element} form\n */\nexport const init = form => {\n const availableLangsElement = form.querySelector(SELECTORS.AVAILABLE_LANG_SELECT);\n\n const availableLangsFilter = (event) => {\n const pendingPromise = new Pending('tool_langimport/search:filter');\n\n // Remove existing options.\n availableLangsElement.querySelectorAll('option').forEach((option) => {\n option.remove();\n });\n\n // Filter for matching languages.\n const searchTerm = event.target.value.toLowerCase();\n const availableLanguages = JSON.parse(availableLangsElement.dataset.availableLanguages);\n const filteredLanguages = Object.keys(availableLanguages).reduce((matches, langcode) => {\n if (availableLanguages[langcode].toLowerCase().includes(searchTerm)) {\n matches[langcode] = availableLanguages[langcode];\n }\n return matches;\n }, []);\n\n // Re-create filtered options.\n Object.entries(filteredLanguages).forEach(([langcode, langname]) => {\n const option = document.createElement('option');\n option.value = langcode;\n option.innerText = langname;\n availableLangsElement.append(option);\n });\n\n pendingPromise.resolve();\n };\n\n // Cache initial available language options.\n const availableLanguages = {};\n availableLangsElement.querySelectorAll('option').forEach((option) => {\n availableLanguages[option.value] = option.text;\n });\n availableLangsElement.dataset.availableLanguages = JSON.stringify(availableLanguages);\n\n // Register event listeners on the search element.\n const availableLangsSearch = form.querySelector(SELECTORS.AVAILABLE_LANG_SEARCH);\n availableLangsSearch.addEventListener('keydown', (event) => {\n if (event.key === 'Enter') {\n event.preventDefault();\n }\n });\n\n // Debounce the event listener to allow the user to finish typing.\n const availableLangsSearchDebounce = debounce(availableLangsFilter, DEBOUNCE_TIMER);\n availableLangsSearch.addEventListener('keyup', (event) => {\n const pendingPromise = new Pending('tool_langimport/search:keyup');\n\n availableLangsSearchDebounce(event);\n setTimeout(() => {\n pendingPromise.resolve();\n }, DEBOUNCE_TIMER);\n });\n};\n"],"names":["SELECTORS","form","availableLangsElement","querySelector","availableLanguages","querySelectorAll","forEach","option","value","text","dataset","JSON","stringify","availableLangsSearch","addEventListener","event","key","preventDefault","availableLangsSearchDebounce","pendingPromise","Pending","remove","searchTerm","target","toLowerCase","parse","filteredLanguages","Object","keys","reduce","matches","langcode","includes","entries","_ref","langname","document","createElement","innerText","append","resolve","setTimeout"],"mappings":";;;;;;;kJA0BMA,gCACqB,SADrBA,gCAEqB,uCAUPC,aACVC,sBAAwBD,KAAKE,cAAcH,iCAgC3CI,mBAAqB,GAC3BF,sBAAsBG,iBAAiB,UAAUC,SAASC,SACtDH,mBAAmBG,OAAOC,OAASD,OAAOE,QAE9CP,sBAAsBQ,QAAQN,mBAAqBO,KAAKC,UAAUR,0BAG5DS,qBAAuBZ,KAAKE,cAAcH,iCAChDa,qBAAqBC,iBAAiB,WAAYC,QAC5B,UAAdA,MAAMC,KACND,MAAME,0BAKRC,8BAA+B,oBA7CPH,cACpBI,eAAiB,IAAIC,iBAAQ,iCAGnClB,sBAAsBG,iBAAiB,UAAUC,SAASC,SACtDA,OAAOc,kBAILC,WAAaP,MAAMQ,OAAOf,MAAMgB,cAChCpB,mBAAqBO,KAAKc,MAAMvB,sBAAsBQ,QAAQN,oBAC9DsB,kBAAoBC,OAAOC,KAAKxB,oBAAoByB,QAAO,CAACC,QAASC,YACnE3B,mBAAmB2B,UAAUP,cAAcQ,SAASV,cACpDQ,QAAQC,UAAY3B,mBAAmB2B,WAEpCD,UACR,IAGHH,OAAOM,QAAQP,mBAAmBpB,SAAQ4B,WAAEH,SAAUI,qBAC5C5B,OAAS6B,SAASC,cAAc,UACtC9B,OAAOC,MAAQuB,SACfxB,OAAO+B,UAAYH,SACnBjC,sBAAsBqC,OAAOhC,WAGjCY,eAAeqB,YApCA,KAwDnB3B,qBAAqBC,iBAAiB,SAAUC,cACtCI,eAAiB,IAAIC,iBAAQ,gCAEnCF,6BAA6BH,OAC7B0B,YAAW,KACPtB,eAAeqB,YA7DJ"}
\ No newline at end of file
+{"version":3,"file":"search.min.js","sources":["../src/search.js"],"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 * Add search filtering of available language packs\n *\n * @module tool_langimport/search\n * @copyright 2021 Paul Holden \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Pending from 'core/pending';\nimport {debounce} from 'core/utils';\n\nconst SELECTORS = {\n AVAILABLE_LANG_SELECT: 'select',\n AVAILABLE_LANG_SEARCH: '[data-action=\"search\"]',\n};\n\nconst DEBOUNCE_TIMER = 250;\n\n/**\n * Initialize module\n *\n * @param {Element} form\n */\nexport const init = form => {\n const availableLangsElement = form.querySelector(SELECTORS.AVAILABLE_LANG_SELECT);\n\n const availableLangsFilter = (event) => {\n const pendingPromise = new Pending('tool_langimport/search:filter');\n\n // Remove existing options.\n availableLangsElement.querySelectorAll('option').forEach((option) => {\n option.remove();\n });\n\n // Filter for matching languages.\n const searchTerm = event.target.value.toLowerCase();\n const availableLanguages = JSON.parse(availableLangsElement.dataset.availableLanguages);\n const filteredLanguages = Object.keys(availableLanguages).reduce((matches, langcode) => {\n if (availableLanguages[langcode].toLowerCase().includes(searchTerm)) {\n matches[langcode] = availableLanguages[langcode];\n }\n return matches;\n }, []);\n\n // Re-create filtered options.\n Object.entries(filteredLanguages).forEach(([langcode, langname]) => {\n const option = document.createElement('option');\n option.value = langcode;\n option.innerText = langname;\n availableLangsElement.append(option);\n });\n\n pendingPromise.resolve();\n };\n\n // Cache initial available language options.\n const availableLanguages = {};\n availableLangsElement.querySelectorAll('option').forEach((option) => {\n availableLanguages[option.value] = option.text;\n });\n availableLangsElement.dataset.availableLanguages = JSON.stringify(availableLanguages);\n\n // Register event listeners on the search element.\n const availableLangsSearch = form.querySelector(SELECTORS.AVAILABLE_LANG_SEARCH);\n availableLangsSearch.addEventListener('keydown', (event) => {\n if (event.key === 'Enter') {\n event.preventDefault();\n }\n });\n\n // Debounce the event listener to allow the user to finish typing.\n const availableLangsSearchDebounce = debounce(availableLangsFilter, DEBOUNCE_TIMER);\n availableLangsSearch.addEventListener('keyup', (event) => {\n const pendingPromise = new Pending('tool_langimport/search:keyup');\n\n availableLangsSearchDebounce(event);\n setTimeout(() => {\n pendingPromise.resolve();\n }, DEBOUNCE_TIMER);\n });\n};\n"],"names":["obj","_pending","__esModule","default","SELECTORS","_exports","init","form","availableLangsElement","querySelector","availableLanguages","querySelectorAll","forEach","option","value","text","dataset","JSON","stringify","availableLangsSearch","addEventListener","event","key","preventDefault","availableLangsSearchDebounce","debounce","pendingPromise","Pending","remove","searchTerm","target","toLowerCase","parse","filteredLanguages","Object","keys","reduce","matches","langcode","includes","entries","_ref","langname","document","createElement","innerText","append","resolve","setTimeout"],"mappings":"4GAuBmC,IAAAA;;;;;;;kFAAnCC,UAAmCD,IAAnCC,WAAmCD,IAAAE,WAAAF,IAAAG,CAAAA,QAAAH,KAGnC,MAAMI,gCACqB,SADrBA,gCAEqB,yBAmEzBC,SAAAC,KAzDkBC,OAChB,MAAMC,sBAAwBD,KAAKE,cAAcL,iCAgC3CM,mBAAqB,CAAA,EAC3BF,sBAAsBG,iBAAiB,UAAUC,SAASC,SACtDH,mBAAmBG,OAAOC,OAASD,OAAOE,IAAI,IAElDP,sBAAsBQ,QAAQN,mBAAqBO,KAAKC,UAAUR,oBAGlE,MAAMS,qBAAuBZ,KAAKE,cAAcL,iCAChDe,qBAAqBC,iBAAiB,WAAYC,QAC5B,UAAdA,MAAMC,KACND,MAAME,gBACV,IAIJ,MAAMC,8BAA+B,EAAAC,OAAAA,WA7CPJ,QAC1B,MAAMK,eAAiB,IAAIC,SAAOxB,QAAC,iCAGnCK,sBAAsBG,iBAAiB,UAAUC,SAASC,SACtDA,OAAOe,QAAQ,IAInB,MAAMC,WAAaR,MAAMS,OAAOhB,MAAMiB,cAChCrB,mBAAqBO,KAAKe,MAAMxB,sBAAsBQ,QAAQN,oBAC9DuB,kBAAoBC,OAAOC,KAAKzB,oBAAoB0B,QAAO,CAACC,QAASC,YACnE5B,mBAAmB4B,UAAUP,cAAcQ,SAASV,cACpDQ,QAAQC,UAAY5B,mBAAmB4B,WAEpCD,UACR,IAGHH,OAAOM,QAAQP,mBAAmBrB,SAAQ6B,OAA0B,IAAxBH,SAAUI,UAASD,KAC3D,MAAM5B,OAAS8B,SAASC,cAAc,UACtC/B,OAAOC,MAAQwB,SACfzB,OAAOgC,UAAYH,SACnBlC,sBAAsBsC,OAAOjC,OAAO,IAGxCa,eAAeqB,SAAS,GApCT,KAwDnB5B,qBAAqBC,iBAAiB,SAAUC,QAC5C,MAAMK,eAAiB,IAAIC,SAAOxB,QAAC,gCAEnCqB,6BAA6BH,OAC7B2B,YAAW,KACPtB,eAAeqB,SAAS,GA7Db,IA8DG,GACpB,CACJ"}
\ No newline at end of file
diff --git a/admin/tool/licensemanager/amd/build/delete_license.min.js.map b/admin/tool/licensemanager/amd/build/delete_license.min.js.map
index 71785b2bf5c..30e44982e2f 100644
--- a/admin/tool/licensemanager/amd/build/delete_license.min.js.map
+++ b/admin/tool/licensemanager/amd/build/delete_license.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"delete_license.min.js","sources":["../src/delete_license.js"],"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 * Modal for confirming deletion of a custom license.\n *\n * @module tool_licensemanager/delete_license\n * @copyright 2019 Tom Dickman \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(['jquery', 'core/modal_factory', 'core/modal_events', 'core/url', 'core/str'],\n function($, ModalFactory, ModalEvents, Url, String) {\n\n var trigger = $('.delete-license');\n ModalFactory.create({\n type: ModalFactory.types.SAVE_CANCEL,\n title: String.get_string('deletelicense', 'tool_licensemanager'),\n body: String.get_string('deletelicenseconfirmmessage', 'tool_licensemanager'),\n preShowCallback: function(triggerElement, modal) {\n triggerElement = $(triggerElement);\n let params = {\n 'action': 'delete',\n 'license': triggerElement.data('license')\n };\n modal.deleteURL = Url.relativeUrl('/admin/tool/licensemanager/index.php', params, true);\n },\n large: true,\n }, trigger)\n .done(function(modal) {\n modal.getRoot().on(ModalEvents.save, function(e) {\n // Stop the default save button behaviour which is to close the modal.\n e.preventDefault();\n // Redirect to delete url.\n window.location.href = modal.deleteURL;\n });\n });\n });\n"],"names":["define","$","ModalFactory","ModalEvents","Url","String","trigger","create","type","types","SAVE_CANCEL","title","get_string","body","preShowCallback","triggerElement","modal","params","data","deleteURL","relativeUrl","large","done","getRoot","on","save","e","preventDefault","window","location","href"],"mappings":";;;;;;;AAsBAA,4CAAO,CAAC,SAAU,qBAAsB,oBAAqB,WAAY,aACrE,SAASC,EAAGC,aAAcC,YAAaC,IAAKC,YAEpCC,QAAUL,EAAE,mBAChBC,aAAaK,OAAO,CAChBC,KAAMN,aAAaO,MAAMC,YACzBC,MAAON,OAAOO,WAAW,gBAAiB,uBAC1CC,KAAMR,OAAOO,WAAW,8BAA+B,uBACvDE,gBAAiB,SAASC,eAAgBC,WAElCC,OAAS,QACC,kBAFdF,eAAiBd,EAAEc,iBAGWG,KAAK,YAEnCF,MAAMG,UAAYf,IAAIgB,YAAY,uCAAwCH,QAAQ,IAEtFI,OAAO,GACRf,SACEgB,MAAK,SAASN,OACXA,MAAMO,UAAUC,GAAGrB,YAAYsB,MAAM,SAASC,GAE1CA,EAAEC,iBAEFC,OAAOC,SAASC,KAAOd,MAAMG"}
\ No newline at end of file
+{"version":3,"file":"delete_license.min.js","sources":["../src/delete_license.js"],"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 * Modal for confirming deletion of a custom license.\n *\n * @module tool_licensemanager/delete_license\n * @copyright 2019 Tom Dickman \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(['jquery', 'core/modal_factory', 'core/modal_events', 'core/url', 'core/str'],\n function($, ModalFactory, ModalEvents, Url, String) {\n\n var trigger = $('.delete-license');\n ModalFactory.create({\n type: ModalFactory.types.SAVE_CANCEL,\n title: String.get_string('deletelicense', 'tool_licensemanager'),\n body: String.get_string('deletelicenseconfirmmessage', 'tool_licensemanager'),\n preShowCallback: function(triggerElement, modal) {\n triggerElement = $(triggerElement);\n let params = {\n 'action': 'delete',\n 'license': triggerElement.data('license')\n };\n modal.deleteURL = Url.relativeUrl('/admin/tool/licensemanager/index.php', params, true);\n },\n large: true,\n }, trigger)\n .done(function(modal) {\n modal.getRoot().on(ModalEvents.save, function(e) {\n // Stop the default save button behaviour which is to close the modal.\n e.preventDefault();\n // Redirect to delete url.\n window.location.href = modal.deleteURL;\n });\n });\n });\n"],"names":["define","$","ModalFactory","ModalEvents","Url","String","trigger","create","type","types","SAVE_CANCEL","title","get_string","body","preShowCallback","triggerElement","modal","params","action","license","data","deleteURL","relativeUrl","large","done","getRoot","on","save","e","preventDefault","window","location","href"],"mappings":";;;;;;;AAsBAA,OAAM,qCAAC,CAAC,SAAU,qBAAsB,oBAAqB,WAAY,aACrE,SAASC,EAAGC,aAAcC,YAAaC,IAAKC,QAExC,IAAIC,QAAUL,EAAE,mBAChBC,aAAaK,OAAO,CAChBC,KAAMN,aAAaO,MAAMC,YACzBC,MAAON,OAAOO,WAAW,gBAAiB,uBAC1CC,KAAMR,OAAOO,WAAW,8BAA+B,uBACvDE,gBAAiB,SAASC,eAAgBC,OAEtC,IAAIC,OAAS,CACTC,OAAU,SACVC,SAHJJ,eAAiBd,EAAEc,iBAGWK,KAAK,YAEnCJ,MAAMK,UAAYjB,IAAIkB,YAAY,uCAAwCL,QAAQ,EACrF,EACDM,OAAO,GACRjB,SACEkB,MAAK,SAASR,OACXA,MAAMS,UAAUC,GAAGvB,YAAYwB,MAAM,SAASC,GAE1CA,EAAEC,iBAEFC,OAAOC,SAASC,KAAOhB,MAAMK,SACjC,GACJ,GACR"}
\ No newline at end of file
diff --git a/admin/tool/lp/amd/build/actionselector.min.js.map b/admin/tool/lp/amd/build/actionselector.min.js.map
index 1061e04a7e2..1a1f845b27c 100644
--- a/admin/tool/lp/amd/build/actionselector.min.js.map
+++ b/admin/tool/lp/amd/build/actionselector.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"actionselector.min.js","sources":["../src/actionselector.js"],"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 * Action selector.\n *\n * To handle 'save' events use: actionselector.on('save')\n * This will receive the information to display in popup.\n * The actions have the format [{'text': sometext, 'value' : somevalue}].\n *\n * @module tool_lp/actionselector\n * @copyright 2016 Serge Gauthier - \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\ndefine(['jquery',\n 'core/notification',\n 'core/ajax',\n 'core/templates',\n 'tool_lp/dialogue',\n 'tool_lp/event_base'],\n function($, Notification, Ajax, Templates, Dialogue, EventBase) {\n\n /**\n * Action selector class.\n *\n * @class tool_lp/actionselector\n * @param {String} title The title of popup.\n * @param {String} message The message to display.\n * @param {object} actions The actions that can be selected.\n * @param {String} confirm Text for confirm button.\n * @param {String} cancel Text for cancel button.\n */\n var ActionSelector = function(title, message, actions, confirm, cancel) {\n var self = this;\n\n EventBase.prototype.constructor.apply(this, []);\n self._title = title;\n self._message = message;\n self._actions = actions;\n self._confirm = confirm;\n self._cancel = cancel;\n self._selectedValue = null;\n self._reset();\n };\n\n ActionSelector.prototype = Object.create(EventBase.prototype);\n\n /** @property {String} The value that was selected. */\n ActionSelector.prototype._selectedValue = null;\n /** @property {Dialogue} The reference to the dialogue. */\n ActionSelector.prototype._popup = null;\n /** @property {String} The title of popup. */\n ActionSelector.prototype._title = null;\n /** @property {String} The message in popup. */\n ActionSelector.prototype._message = null;\n /** @property {object} The information for radion buttons. */\n ActionSelector.prototype._actions = null;\n /** @property {String} The text for confirm button. */\n ActionSelector.prototype._confirm = null;\n /** @property {String} The text for cancel button. */\n ActionSelector.prototype._cancel = null;\n\n /**\n * Hook to executed after the view is rendered.\n *\n * @method _afterRender\n */\n ActionSelector.prototype._afterRender = function() {\n var self = this;\n\n // Confirm button is disabled until a choice is done.\n self._find('[data-action=\"action-selector-confirm\"]').attr('disabled', 'disabled');\n\n // Add listener for radio buttons change.\n self._find('[data-region=\"action-selector-radio-buttons\"]').change(function() {\n self._selectedValue = $(\"input[type='radio']:checked\").val();\n self._find('[data-action=\"action-selector-confirm\"]').removeAttr('disabled');\n self._refresh.bind(self);\n });\n\n // Add listener for cancel.\n self._find('[data-action=\"action-selector-cancel\"]').click(function(e) {\n e.preventDefault();\n self.close();\n });\n\n // Add listener for confirm.\n self._find('[data-action=\"action-selector-confirm\"]').click(function(e) {\n e.preventDefault();\n if (!self._selectedValue.length) {\n return;\n }\n self._trigger('save', {action: self._selectedValue});\n self.close();\n });\n };\n\n /**\n * Close the dialogue.\n *\n * @method close\n */\n ActionSelector.prototype.close = function() {\n var self = this;\n self._popup.close();\n self._reset();\n };\n\n /**\n * Opens the action selector.\n *\n * @method display\n * @return {Promise}\n */\n ActionSelector.prototype.display = function() {\n var self = this;\n return self._render().then(function(html) {\n self._popup = new Dialogue(\n self._title,\n html,\n self._afterRender.bind(self)\n );\n return;\n }).fail(Notification.exception);\n };\n\n /**\n * Find a node in the dialogue.\n *\n * @param {String} selector\n * @return {JQuery} The node\n * @method _find\n */\n ActionSelector.prototype._find = function(selector) {\n return $(this._popup.getContent()).find(selector);\n };\n\n /**\n * Refresh the view.\n *\n * @method _refresh\n * @return {Promise}\n */\n ActionSelector.prototype._refresh = function() {\n var self = this;\n return self._render().then(function(html) {\n self._find('[data-region=\"action-selector\"]').replaceWith(html);\n self._afterRender();\n return;\n });\n };\n\n /**\n * Render the dialogue.\n *\n * @method _render\n * @return {Promise}\n */\n ActionSelector.prototype._render = function() {\n var self = this;\n var choices = [];\n for (var i in self._actions) {\n choices.push(self._actions[i]);\n }\n var content = {'message': self._message, 'choices': choices,\n 'confirm': self._confirm, 'cancel': self._cancel};\n\n return Templates.render('tool_lp/action_selector', content);\n };\n\n /**\n * Reset the dialogue properties.\n *\n * This does not reset everything, just enough to reset the UI.\n *\n * @method _reset\n */\n ActionSelector.prototype._reset = function() {\n this._popup = null;\n this._selectedValue = '';\n };\n\n return ActionSelector;\n\n});\n"],"names":["define","$","Notification","Ajax","Templates","Dialogue","EventBase","ActionSelector","title","message","actions","confirm","cancel","prototype","constructor","apply","this","_title","_message","_actions","_confirm","_cancel","_selectedValue","_reset","Object","create","_popup","_afterRender","self","_find","attr","change","val","removeAttr","_refresh","bind","click","e","preventDefault","close","length","_trigger","action","display","_render","then","html","fail","exception","selector","getContent","find","replaceWith","choices","i","push","content","render"],"mappings":";;;;;;;;;;;AA2BAA,gCAAO,CAAC,SACA,oBACA,YACA,iBACA,mBACA,uBACA,SAASC,EAAGC,aAAcC,KAAMC,UAAWC,SAAUC,eAYrDC,eAAiB,SAASC,MAAOC,QAASC,QAASC,QAASC,QAG5DN,UAAUO,UAAUC,YAAYC,MAAMC,KAAM,IAFjCA,KAGNC,OAAST,MAHHQ,KAINE,SAAWT,QAJLO,KAKNG,SAAWT,QALLM,KAMNI,SAAWT,QANLK,KAONK,QAAUT,OAPJI,KAQNM,eAAiB,KARXN,KASNO,iBAGThB,eAAeM,UAAYW,OAAOC,OAAOnB,UAAUO,YAG1BS,eAAiB,KAE1Cf,eAAeM,UAAUa,OAAS,KAElCnB,eAAeM,UAAUI,OAAS,KAElCV,eAAeM,UAAUK,SAAW,KAEpCX,eAAeM,UAAUM,SAAW,KAEpCZ,eAAeM,UAAUO,SAAW,KAEpCb,eAAeM,UAAUQ,QAAU,KAOnCd,eAAeM,UAAUc,aAAe,eAChCC,KAAOZ,KAGXY,KAAKC,MAAM,2CAA2CC,KAAK,WAAY,YAGvEF,KAAKC,MAAM,iDAAiDE,QAAO,WAC/DH,KAAKN,eAAiBrB,EAAE,+BAA+B+B,MACvDJ,KAAKC,MAAM,2CAA2CI,WAAW,YACjEL,KAAKM,SAASC,KAAKP,SAIvBA,KAAKC,MAAM,0CAA0CO,OAAM,SAASC,GAChEA,EAAEC,iBACFV,KAAKW,WAITX,KAAKC,MAAM,2CAA2CO,OAAM,SAASC,GACjEA,EAAEC,iBACGV,KAAKN,eAAekB,SAGzBZ,KAAKa,SAAS,OAAQ,CAACC,OAAQd,KAAKN,iBACpCM,KAAKW,aASbhC,eAAeM,UAAU0B,MAAQ,WAClBvB,KACNU,OAAOa,QADDvB,KAENO,UASThB,eAAeM,UAAU8B,QAAU,eAC3Bf,KAAOZ,YACJY,KAAKgB,UAAUC,MAAK,SAASC,MAChClB,KAAKF,OAAS,IAAIrB,SACduB,KAAKX,OACL6B,KACAlB,KAAKD,aAAaQ,KAAKP,UAG5BmB,KAAK7C,aAAa8C,YAUzBzC,eAAeM,UAAUgB,MAAQ,SAASoB,iBAC/BhD,EAAEe,KAAKU,OAAOwB,cAAcC,KAAKF,WAS5C1C,eAAeM,UAAUqB,SAAW,eAC5BN,KAAOZ,YACJY,KAAKgB,UAAUC,MAAK,SAASC,MAChClB,KAAKC,MAAM,mCAAmCuB,YAAYN,MAC1DlB,KAAKD,mBAWbpB,eAAeM,UAAU+B,QAAU,eAE3BS,QAAU,OACT,IAAIC,KAFEtC,KAEQG,SACfkC,QAAQE,KAHDvC,KAGWG,SAASmC,QAE3BE,QAAU,SALHxC,KAKoBE,iBAAqBmC,gBALzCrC,KAMSI,gBANTJ,KAMkCK,gBAEtCjB,UAAUqD,OAAO,0BAA2BD,UAUvDjD,eAAeM,UAAUU,OAAS,gBACzBG,OAAS,UACTJ,eAAiB,IAGnBf"}
\ No newline at end of file
+{"version":3,"file":"actionselector.min.js","sources":["../src/actionselector.js"],"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 * Action selector.\n *\n * To handle 'save' events use: actionselector.on('save')\n * This will receive the information to display in popup.\n * The actions have the format [{'text': sometext, 'value' : somevalue}].\n *\n * @module tool_lp/actionselector\n * @copyright 2016 Serge Gauthier - \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\ndefine(['jquery',\n 'core/notification',\n 'core/ajax',\n 'core/templates',\n 'tool_lp/dialogue',\n 'tool_lp/event_base'],\n function($, Notification, Ajax, Templates, Dialogue, EventBase) {\n\n /**\n * Action selector class.\n *\n * @class tool_lp/actionselector\n * @param {String} title The title of popup.\n * @param {String} message The message to display.\n * @param {object} actions The actions that can be selected.\n * @param {String} confirm Text for confirm button.\n * @param {String} cancel Text for cancel button.\n */\n var ActionSelector = function(title, message, actions, confirm, cancel) {\n var self = this;\n\n EventBase.prototype.constructor.apply(this, []);\n self._title = title;\n self._message = message;\n self._actions = actions;\n self._confirm = confirm;\n self._cancel = cancel;\n self._selectedValue = null;\n self._reset();\n };\n\n ActionSelector.prototype = Object.create(EventBase.prototype);\n\n /** @property {String} The value that was selected. */\n ActionSelector.prototype._selectedValue = null;\n /** @property {Dialogue} The reference to the dialogue. */\n ActionSelector.prototype._popup = null;\n /** @property {String} The title of popup. */\n ActionSelector.prototype._title = null;\n /** @property {String} The message in popup. */\n ActionSelector.prototype._message = null;\n /** @property {object} The information for radion buttons. */\n ActionSelector.prototype._actions = null;\n /** @property {String} The text for confirm button. */\n ActionSelector.prototype._confirm = null;\n /** @property {String} The text for cancel button. */\n ActionSelector.prototype._cancel = null;\n\n /**\n * Hook to executed after the view is rendered.\n *\n * @method _afterRender\n */\n ActionSelector.prototype._afterRender = function() {\n var self = this;\n\n // Confirm button is disabled until a choice is done.\n self._find('[data-action=\"action-selector-confirm\"]').attr('disabled', 'disabled');\n\n // Add listener for radio buttons change.\n self._find('[data-region=\"action-selector-radio-buttons\"]').change(function() {\n self._selectedValue = $(\"input[type='radio']:checked\").val();\n self._find('[data-action=\"action-selector-confirm\"]').removeAttr('disabled');\n self._refresh.bind(self);\n });\n\n // Add listener for cancel.\n self._find('[data-action=\"action-selector-cancel\"]').click(function(e) {\n e.preventDefault();\n self.close();\n });\n\n // Add listener for confirm.\n self._find('[data-action=\"action-selector-confirm\"]').click(function(e) {\n e.preventDefault();\n if (!self._selectedValue.length) {\n return;\n }\n self._trigger('save', {action: self._selectedValue});\n self.close();\n });\n };\n\n /**\n * Close the dialogue.\n *\n * @method close\n */\n ActionSelector.prototype.close = function() {\n var self = this;\n self._popup.close();\n self._reset();\n };\n\n /**\n * Opens the action selector.\n *\n * @method display\n * @return {Promise}\n */\n ActionSelector.prototype.display = function() {\n var self = this;\n return self._render().then(function(html) {\n self._popup = new Dialogue(\n self._title,\n html,\n self._afterRender.bind(self)\n );\n return;\n }).fail(Notification.exception);\n };\n\n /**\n * Find a node in the dialogue.\n *\n * @param {String} selector\n * @return {JQuery} The node\n * @method _find\n */\n ActionSelector.prototype._find = function(selector) {\n return $(this._popup.getContent()).find(selector);\n };\n\n /**\n * Refresh the view.\n *\n * @method _refresh\n * @return {Promise}\n */\n ActionSelector.prototype._refresh = function() {\n var self = this;\n return self._render().then(function(html) {\n self._find('[data-region=\"action-selector\"]').replaceWith(html);\n self._afterRender();\n return;\n });\n };\n\n /**\n * Render the dialogue.\n *\n * @method _render\n * @return {Promise}\n */\n ActionSelector.prototype._render = function() {\n var self = this;\n var choices = [];\n for (var i in self._actions) {\n choices.push(self._actions[i]);\n }\n var content = {'message': self._message, 'choices': choices,\n 'confirm': self._confirm, 'cancel': self._cancel};\n\n return Templates.render('tool_lp/action_selector', content);\n };\n\n /**\n * Reset the dialogue properties.\n *\n * This does not reset everything, just enough to reset the UI.\n *\n * @method _reset\n */\n ActionSelector.prototype._reset = function() {\n this._popup = null;\n this._selectedValue = '';\n };\n\n return ActionSelector;\n\n});\n"],"names":["define","$","Notification","Ajax","Templates","Dialogue","EventBase","ActionSelector","title","message","actions","confirm","cancel","prototype","constructor","apply","this","_title","_message","_actions","_confirm","_cancel","_selectedValue","_reset","Object","create","_popup","_afterRender","self","_find","attr","change","val","removeAttr","_refresh","bind","click","e","preventDefault","close","length","_trigger","action","display","_render","then","html","fail","exception","selector","getContent","find","replaceWith","choices","i","push","content","render"],"mappings":";;;;;;;;;;;AA2BAA,OAAM,yBAAC,CAAC,SACA,oBACA,YACA,iBACA,mBACA,uBACA,SAASC,EAAGC,aAAcC,KAAMC,UAAWC,SAAUC,WAYzD,IAAIC,eAAiB,SAASC,MAAOC,QAASC,QAASC,QAASC,QAG5DN,UAAUO,UAAUC,YAAYC,MAAMC,KAAM,IAFjCA,KAGNC,OAAST,MAHHQ,KAINE,SAAWT,QAJLO,KAKNG,SAAWT,QALLM,KAMNI,SAAWT,QANLK,KAONK,QAAUT,OAPJI,KAQNM,eAAiB,KARXN,KASNO,UA4IT,OAzIAhB,eAAeM,UAAYW,OAAOC,OAAOnB,UAAUO,YAG1BS,eAAiB,KAE1Cf,eAAeM,UAAUa,OAAS,KAElCnB,eAAeM,UAAUI,OAAS,KAElCV,eAAeM,UAAUK,SAAW,KAEpCX,eAAeM,UAAUM,SAAW,KAEpCZ,eAAeM,UAAUO,SAAW,KAEpCb,eAAeM,UAAUQ,QAAU,KAOnCd,eAAeM,UAAUc,aAAe,WACpC,IAAIC,KAAOZ,KAGXY,KAAKC,MAAM,2CAA2CC,KAAK,WAAY,YAGvEF,KAAKC,MAAM,iDAAiDE,QAAO,WAC/DH,KAAKN,eAAiBrB,EAAE,+BAA+B+B,MACvDJ,KAAKC,MAAM,2CAA2CI,WAAW,YACjEL,KAAKM,SAASC,KAAKP,KACvB,IAGAA,KAAKC,MAAM,0CAA0CO,OAAM,SAASC,GAChEA,EAAEC,iBACFV,KAAKW,OACT,IAGAX,KAAKC,MAAM,2CAA2CO,OAAM,SAASC,GACjEA,EAAEC,iBACGV,KAAKN,eAAekB,SAGzBZ,KAAKa,SAAS,OAAQ,CAACC,OAAQd,KAAKN,iBACpCM,KAAKW,QACT,KAQJhC,eAAeM,UAAU0B,MAAQ,WAClBvB,KACNU,OAAOa,QADDvB,KAENO,UASThB,eAAeM,UAAU8B,QAAU,WAC/B,IAAIf,KAAOZ,KACX,OAAOY,KAAKgB,UAAUC,MAAK,SAASC,MAChClB,KAAKF,OAAS,IAAIrB,SACduB,KAAKX,OACL6B,KACAlB,KAAKD,aAAaQ,KAAKP,MAG9B,IAAEmB,KAAK7C,aAAa8C,YAUzBzC,eAAeM,UAAUgB,MAAQ,SAASoB,UACtC,OAAOhD,EAAEe,KAAKU,OAAOwB,cAAcC,KAAKF,WAS5C1C,eAAeM,UAAUqB,SAAW,WAChC,IAAIN,KAAOZ,KACX,OAAOY,KAAKgB,UAAUC,MAAK,SAASC,MAChClB,KAAKC,MAAM,mCAAmCuB,YAAYN,MAC1DlB,KAAKD,cAET,KASJpB,eAAeM,UAAU+B,QAAU,WAC/B,IACIS,QAAU,GACd,IAAK,IAAIC,KAFEtC,KAEQG,SACfkC,QAAQE,KAHDvC,KAGWG,SAASmC,IAE/B,IAAIE,QAAU,CAAC/C,QALJO,KAKoBE,SAAUmC,QAAWA,QAChD1C,QANOK,KAMSI,SAAUR,OANnBI,KAMkCK,SAE7C,OAAOjB,UAAUqD,OAAO,0BAA2BD,UAUvDjD,eAAeM,UAAUU,OAAS,WAC9BP,KAAKU,OAAS,KACdV,KAAKM,eAAiB,IAGnBf,cAEX"}
\ No newline at end of file
diff --git a/admin/tool/lp/amd/build/competencies.min.js.map b/admin/tool/lp/amd/build/competencies.min.js.map
index 38cbcbe25d1..364bb966074 100644
--- a/admin/tool/lp/amd/build/competencies.min.js.map
+++ b/admin/tool/lp/amd/build/competencies.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"competencies.min.js","sources":["../src/competencies.js"],"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 * Handle add/remove competency links.\n *\n * @module tool_lp/competencies\n * @copyright 2015 Damyon Wiese \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(['jquery',\n 'core/notification',\n 'core/ajax',\n 'core/templates',\n 'core/str',\n 'tool_lp/competencypicker',\n 'tool_lp/dragdrop-reorder',\n 'core/pending'],\n function($, notification, ajax, templates, str, Picker, dragdrop, Pending) {\n\n /**\n * Constructor\n *\n * @class tool_lp/competencies\n * @param {Number} itemid\n * @param {String} itemtype\n * @param {Number} pagectxid\n */\n var competencies = function(itemid, itemtype, pagectxid) {\n this.itemid = itemid;\n this.itemtype = itemtype;\n this.pageContextId = pagectxid;\n this.pickerInstance = null;\n\n $('[data-region=\"actions\"] button').prop('disabled', false);\n this.registerEvents();\n this.registerDragDrop();\n };\n\n /**\n * Initialise the drag/drop code.\n * @method registerDragDrop\n */\n competencies.prototype.registerDragDrop = function() {\n var localthis = this;\n // Init this module.\n str.get_string('movecompetency', 'tool_lp').done(\n function(movestring) {\n dragdrop.dragdrop('movecompetency',\n movestring,\n {identifier: 'movecompetency', component: 'tool_lp'},\n {identifier: 'movecompetencyafter', component: 'tool_lp'},\n 'drag-samenode',\n 'drag-parentnode',\n 'drag-handlecontainer',\n function(drag, drop) {\n localthis.handleDrop(drag, drop);\n });\n }\n ).fail(notification.exception);\n\n };\n\n /**\n * Handle a drop from a drag/drop operation.\n *\n * @method handleDrop\n * @param {DOMNode} drag The dragged node.\n * @param {DOMNode} drop The dropped on node.\n */\n competencies.prototype.handleDrop = function(drag, drop) {\n var fromid = $(drag).data('id');\n var toid = $(drop).data('id');\n var localthis = this;\n var requests = [];\n\n if (localthis.itemtype == 'course') {\n requests = ajax.call([\n {\n methodname: 'core_competency_reorder_course_competency',\n args: {courseid: localthis.itemid, competencyidfrom: fromid, competencyidto: toid}\n }\n ]);\n } else if (localthis.itemtype == 'template') {\n requests = ajax.call([\n {\n methodname: 'core_competency_reorder_template_competency',\n args: {templateid: localthis.itemid, competencyidfrom: fromid, competencyidto: toid}\n }\n ]);\n } else if (localthis.itemtype == 'plan') {\n requests = ajax.call([\n {\n methodname: 'core_competency_reorder_plan_competency',\n args: {planid: localthis.itemid, competencyidfrom: fromid, competencyidto: toid}\n }\n ]);\n } else {\n return;\n }\n\n requests[0].fail(notification.exception);\n };\n\n /**\n * Pick a competency\n *\n * @method pickCompetency\n * @return {Promise}\n */\n competencies.prototype.pickCompetency = function() {\n var self = this;\n var requests;\n var pagerender;\n var pageregion;\n var pageContextIncludes;\n\n if (!self.pickerInstance) {\n if (self.itemtype === 'template' || self.itemtype === 'course') {\n pageContextIncludes = 'parents';\n }\n self.pickerInstance = new Picker(self.pageContextId, false, pageContextIncludes);\n self.pickerInstance.on('save', function(e, data) {\n var compIds = data.competencyIds;\n var pendingPromise = new Pending();\n\n if (self.itemtype === \"course\") {\n requests = [];\n\n $.each(compIds, function(index, compId) {\n requests.push({\n methodname: 'core_competency_add_competency_to_course',\n args: {courseid: self.itemid, competencyid: compId}\n });\n });\n requests.push({\n methodname: 'tool_lp_data_for_course_competencies_page',\n args: {courseid: self.itemid, moduleid: 0}\n });\n\n pagerender = 'tool_lp/course_competencies_page';\n pageregion = 'coursecompetenciespage';\n\n } else if (self.itemtype === \"template\") {\n requests = [];\n\n $.each(compIds, function(index, compId) {\n requests.push({\n methodname: 'core_competency_add_competency_to_template',\n args: {templateid: self.itemid, competencyid: compId}\n });\n });\n requests.push({\n methodname: 'tool_lp_data_for_template_competencies_page',\n args: {templateid: self.itemid, pagecontext: {contextid: self.pageContextId}}\n });\n pagerender = 'tool_lp/template_competencies_page';\n pageregion = 'templatecompetenciespage';\n } else if (self.itemtype === \"plan\") {\n requests = [];\n\n $.each(compIds, function(index, compId) {\n requests.push({\n methodname: 'core_competency_add_competency_to_plan',\n args: {planid: self.itemid, competencyid: compId}\n });\n });\n requests.push({\n methodname: 'tool_lp_data_for_plan_page',\n args: {planid: self.itemid}\n });\n pagerender = 'tool_lp/plan_page';\n pageregion = 'plan-page';\n }\n ajax.call(requests)[requests.length - 1]\n .then(function(context) {\n return templates.render(pagerender, context);\n })\n .then(function(html, js) {\n templates.replaceNode($('[data-region=\"' + pageregion + '\"]'), html, js);\n return;\n })\n .then(pendingPromise.resolve)\n .catch(notification.exception);\n });\n }\n\n return self.pickerInstance.display();\n };\n\n /**\n * Delete the link between competency and course, template or plan. Reload the page.\n *\n * @method doDelete\n * @param {int} deleteid The id of record to delete.\n */\n competencies.prototype.doDelete = function(deleteid) {\n var localthis = this;\n var requests = [],\n pagerender = '',\n pageregion = '';\n\n // Delete the link and reload the page template.\n if (localthis.itemtype == 'course') {\n requests = ajax.call([\n {methodname: 'core_competency_remove_competency_from_course',\n args: {courseid: localthis.itemid, competencyid: deleteid}},\n {methodname: 'tool_lp_data_for_course_competencies_page',\n args: {courseid: localthis.itemid, moduleid: 0}}\n ]);\n pagerender = 'tool_lp/course_competencies_page';\n pageregion = 'coursecompetenciespage';\n } else if (localthis.itemtype == 'template') {\n requests = ajax.call([\n {methodname: 'core_competency_remove_competency_from_template',\n args: {templateid: localthis.itemid, competencyid: deleteid}},\n {methodname: 'tool_lp_data_for_template_competencies_page',\n args: {templateid: localthis.itemid, pagecontext: {contextid: localthis.pageContextId}}}\n ]);\n pagerender = 'tool_lp/template_competencies_page';\n pageregion = 'templatecompetenciespage';\n } else if (localthis.itemtype == 'plan') {\n requests = ajax.call([\n {methodname: 'core_competency_remove_competency_from_plan',\n args: {planid: localthis.itemid, competencyid: deleteid}},\n {methodname: 'tool_lp_data_for_plan_page',\n args: {planid: localthis.itemid}}\n ]);\n pagerender = 'tool_lp/plan_page';\n pageregion = 'plan-page';\n }\n\n requests[1].done(function(context) {\n templates.render(pagerender, context).done(function(html, js) {\n $('[data-region=\"' + pageregion + '\"]').replaceWith(html);\n templates.runTemplateJS(js);\n }).fail(notification.exception);\n }).fail(notification.exception);\n\n };\n\n /**\n * Show a confirm dialogue before deleting a competency.\n *\n * @method deleteHandler\n * @param {int} deleteid The id of record to delete.\n */\n competencies.prototype.deleteHandler = function(deleteid) {\n var localthis = this;\n var requests = [];\n var message;\n\n if (localthis.itemtype == 'course') {\n message = 'unlinkcompetencycourse';\n } else if (localthis.itemtype == 'template') {\n message = 'unlinkcompetencytemplate';\n } else if (localthis.itemtype == 'plan') {\n message = 'unlinkcompetencyplan';\n } else {\n return;\n }\n\n requests = ajax.call([{\n methodname: 'core_competency_read_competency',\n args: {id: deleteid}\n }]);\n\n requests[0].done(function(competency) {\n str.get_strings([\n {key: 'confirm', component: 'moodle'},\n {key: message, component: 'tool_lp', param: competency.shortname},\n {key: 'confirm', component: 'moodle'},\n {key: 'cancel', component: 'moodle'}\n ]).done(function(strings) {\n notification.confirm(\n strings[0], // Confirm.\n strings[1], // Unlink the competency X from the course?\n strings[2], // Confirm.\n strings[3], // Cancel.\n function() {\n localthis.doDelete(deleteid);\n }\n );\n }).fail(notification.exception);\n }).fail(notification.exception);\n };\n\n /**\n * Register the javascript event handlers for this page.\n *\n * @method registerEvents\n */\n competencies.prototype.registerEvents = function() {\n var localthis = this;\n\n if (localthis.itemtype == 'course') {\n // Course completion rule handling.\n $('[data-region=\"coursecompetenciespage\"]').on('change', 'select[data-field=\"ruleoutcome\"]', function(e) {\n var pendingPromise = new Pending();\n var requests = [];\n var pagerender = 'tool_lp/course_competencies_page';\n var pageregion = 'coursecompetenciespage';\n var coursecompetencyid = $(e.target).data('id');\n var ruleoutcome = $(e.target).val();\n requests = ajax.call([\n {methodname: 'core_competency_set_course_competency_ruleoutcome',\n args: {coursecompetencyid: coursecompetencyid, ruleoutcome: ruleoutcome}},\n {methodname: 'tool_lp_data_for_course_competencies_page',\n args: {courseid: localthis.itemid, moduleid: 0}}\n ]);\n\n requests[1].then(function(context) {\n return templates.render(pagerender, context);\n })\n .then(function(html, js) {\n return templates.replaceNode($('[data-region=\"' + pageregion + '\"]'), html, js);\n })\n .then(pendingPromise.resolve)\n .catch(notification.exception);\n });\n }\n\n $('[data-region=\"actions\"] button').click(function(e) {\n var pendingPromise = new Pending();\n e.preventDefault();\n\n localthis.pickCompetency()\n .then(pendingPromise.resolve)\n .catch();\n });\n $('[data-action=\"delete-competency-link\"]').click(function(e) {\n e.preventDefault();\n\n var deleteid = $(e.target).closest('[data-id]').data('id');\n localthis.deleteHandler(deleteid);\n });\n };\n\n return /** @alias module:tool_lp/competencies */ competencies;\n});\n"],"names":["define","$","notification","ajax","templates","str","Picker","dragdrop","Pending","competencies","itemid","itemtype","pagectxid","pageContextId","pickerInstance","prop","registerEvents","registerDragDrop","prototype","localthis","this","get_string","done","movestring","identifier","component","drag","drop","handleDrop","fail","exception","fromid","data","toid","requests","call","methodname","args","courseid","competencyidfrom","competencyidto","templateid","planid","pickCompetency","pagerender","pageregion","pageContextIncludes","self","on","e","compIds","competencyIds","pendingPromise","each","index","compId","push","competencyid","moduleid","pagecontext","contextid","length","then","context","render","html","js","replaceNode","resolve","catch","display","doDelete","deleteid","replaceWith","runTemplateJS","deleteHandler","message","id","competency","get_strings","key","param","shortname","strings","confirm","coursecompetencyid","target","ruleoutcome","val","click","preventDefault","closest"],"mappings":"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"}
\ No newline at end of file
+{"version":3,"file":"competencies.min.js","sources":["../src/competencies.js"],"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 * Handle add/remove competency links.\n *\n * @module tool_lp/competencies\n * @copyright 2015 Damyon Wiese \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(['jquery',\n 'core/notification',\n 'core/ajax',\n 'core/templates',\n 'core/str',\n 'tool_lp/competencypicker',\n 'tool_lp/dragdrop-reorder',\n 'core/pending'],\n function($, notification, ajax, templates, str, Picker, dragdrop, Pending) {\n\n /**\n * Constructor\n *\n * @class tool_lp/competencies\n * @param {Number} itemid\n * @param {String} itemtype\n * @param {Number} pagectxid\n */\n var competencies = function(itemid, itemtype, pagectxid) {\n this.itemid = itemid;\n this.itemtype = itemtype;\n this.pageContextId = pagectxid;\n this.pickerInstance = null;\n\n $('[data-region=\"actions\"] button').prop('disabled', false);\n this.registerEvents();\n this.registerDragDrop();\n };\n\n /**\n * Initialise the drag/drop code.\n * @method registerDragDrop\n */\n competencies.prototype.registerDragDrop = function() {\n var localthis = this;\n // Init this module.\n str.get_string('movecompetency', 'tool_lp').done(\n function(movestring) {\n dragdrop.dragdrop('movecompetency',\n movestring,\n {identifier: 'movecompetency', component: 'tool_lp'},\n {identifier: 'movecompetencyafter', component: 'tool_lp'},\n 'drag-samenode',\n 'drag-parentnode',\n 'drag-handlecontainer',\n function(drag, drop) {\n localthis.handleDrop(drag, drop);\n });\n }\n ).fail(notification.exception);\n\n };\n\n /**\n * Handle a drop from a drag/drop operation.\n *\n * @method handleDrop\n * @param {DOMNode} drag The dragged node.\n * @param {DOMNode} drop The dropped on node.\n */\n competencies.prototype.handleDrop = function(drag, drop) {\n var fromid = $(drag).data('id');\n var toid = $(drop).data('id');\n var localthis = this;\n var requests = [];\n\n if (localthis.itemtype == 'course') {\n requests = ajax.call([\n {\n methodname: 'core_competency_reorder_course_competency',\n args: {courseid: localthis.itemid, competencyidfrom: fromid, competencyidto: toid}\n }\n ]);\n } else if (localthis.itemtype == 'template') {\n requests = ajax.call([\n {\n methodname: 'core_competency_reorder_template_competency',\n args: {templateid: localthis.itemid, competencyidfrom: fromid, competencyidto: toid}\n }\n ]);\n } else if (localthis.itemtype == 'plan') {\n requests = ajax.call([\n {\n methodname: 'core_competency_reorder_plan_competency',\n args: {planid: localthis.itemid, competencyidfrom: fromid, competencyidto: toid}\n }\n ]);\n } else {\n return;\n }\n\n requests[0].fail(notification.exception);\n };\n\n /**\n * Pick a competency\n *\n * @method pickCompetency\n * @return {Promise}\n */\n competencies.prototype.pickCompetency = function() {\n var self = this;\n var requests;\n var pagerender;\n var pageregion;\n var pageContextIncludes;\n\n if (!self.pickerInstance) {\n if (self.itemtype === 'template' || self.itemtype === 'course') {\n pageContextIncludes = 'parents';\n }\n self.pickerInstance = new Picker(self.pageContextId, false, pageContextIncludes);\n self.pickerInstance.on('save', function(e, data) {\n var compIds = data.competencyIds;\n var pendingPromise = new Pending();\n\n if (self.itemtype === \"course\") {\n requests = [];\n\n $.each(compIds, function(index, compId) {\n requests.push({\n methodname: 'core_competency_add_competency_to_course',\n args: {courseid: self.itemid, competencyid: compId}\n });\n });\n requests.push({\n methodname: 'tool_lp_data_for_course_competencies_page',\n args: {courseid: self.itemid, moduleid: 0}\n });\n\n pagerender = 'tool_lp/course_competencies_page';\n pageregion = 'coursecompetenciespage';\n\n } else if (self.itemtype === \"template\") {\n requests = [];\n\n $.each(compIds, function(index, compId) {\n requests.push({\n methodname: 'core_competency_add_competency_to_template',\n args: {templateid: self.itemid, competencyid: compId}\n });\n });\n requests.push({\n methodname: 'tool_lp_data_for_template_competencies_page',\n args: {templateid: self.itemid, pagecontext: {contextid: self.pageContextId}}\n });\n pagerender = 'tool_lp/template_competencies_page';\n pageregion = 'templatecompetenciespage';\n } else if (self.itemtype === \"plan\") {\n requests = [];\n\n $.each(compIds, function(index, compId) {\n requests.push({\n methodname: 'core_competency_add_competency_to_plan',\n args: {planid: self.itemid, competencyid: compId}\n });\n });\n requests.push({\n methodname: 'tool_lp_data_for_plan_page',\n args: {planid: self.itemid}\n });\n pagerender = 'tool_lp/plan_page';\n pageregion = 'plan-page';\n }\n ajax.call(requests)[requests.length - 1]\n .then(function(context) {\n return templates.render(pagerender, context);\n })\n .then(function(html, js) {\n templates.replaceNode($('[data-region=\"' + pageregion + '\"]'), html, js);\n return;\n })\n .then(pendingPromise.resolve)\n .catch(notification.exception);\n });\n }\n\n return self.pickerInstance.display();\n };\n\n /**\n * Delete the link between competency and course, template or plan. Reload the page.\n *\n * @method doDelete\n * @param {int} deleteid The id of record to delete.\n */\n competencies.prototype.doDelete = function(deleteid) {\n var localthis = this;\n var requests = [],\n pagerender = '',\n pageregion = '';\n\n // Delete the link and reload the page template.\n if (localthis.itemtype == 'course') {\n requests = ajax.call([\n {methodname: 'core_competency_remove_competency_from_course',\n args: {courseid: localthis.itemid, competencyid: deleteid}},\n {methodname: 'tool_lp_data_for_course_competencies_page',\n args: {courseid: localthis.itemid, moduleid: 0}}\n ]);\n pagerender = 'tool_lp/course_competencies_page';\n pageregion = 'coursecompetenciespage';\n } else if (localthis.itemtype == 'template') {\n requests = ajax.call([\n {methodname: 'core_competency_remove_competency_from_template',\n args: {templateid: localthis.itemid, competencyid: deleteid}},\n {methodname: 'tool_lp_data_for_template_competencies_page',\n args: {templateid: localthis.itemid, pagecontext: {contextid: localthis.pageContextId}}}\n ]);\n pagerender = 'tool_lp/template_competencies_page';\n pageregion = 'templatecompetenciespage';\n } else if (localthis.itemtype == 'plan') {\n requests = ajax.call([\n {methodname: 'core_competency_remove_competency_from_plan',\n args: {planid: localthis.itemid, competencyid: deleteid}},\n {methodname: 'tool_lp_data_for_plan_page',\n args: {planid: localthis.itemid}}\n ]);\n pagerender = 'tool_lp/plan_page';\n pageregion = 'plan-page';\n }\n\n requests[1].done(function(context) {\n templates.render(pagerender, context).done(function(html, js) {\n $('[data-region=\"' + pageregion + '\"]').replaceWith(html);\n templates.runTemplateJS(js);\n }).fail(notification.exception);\n }).fail(notification.exception);\n\n };\n\n /**\n * Show a confirm dialogue before deleting a competency.\n *\n * @method deleteHandler\n * @param {int} deleteid The id of record to delete.\n */\n competencies.prototype.deleteHandler = function(deleteid) {\n var localthis = this;\n var requests = [];\n var message;\n\n if (localthis.itemtype == 'course') {\n message = 'unlinkcompetencycourse';\n } else if (localthis.itemtype == 'template') {\n message = 'unlinkcompetencytemplate';\n } else if (localthis.itemtype == 'plan') {\n message = 'unlinkcompetencyplan';\n } else {\n return;\n }\n\n requests = ajax.call([{\n methodname: 'core_competency_read_competency',\n args: {id: deleteid}\n }]);\n\n requests[0].done(function(competency) {\n str.get_strings([\n {key: 'confirm', component: 'moodle'},\n {key: message, component: 'tool_lp', param: competency.shortname},\n {key: 'confirm', component: 'moodle'},\n {key: 'cancel', component: 'moodle'}\n ]).done(function(strings) {\n notification.confirm(\n strings[0], // Confirm.\n strings[1], // Unlink the competency X from the course?\n strings[2], // Confirm.\n strings[3], // Cancel.\n function() {\n localthis.doDelete(deleteid);\n }\n );\n }).fail(notification.exception);\n }).fail(notification.exception);\n };\n\n /**\n * Register the javascript event handlers for this page.\n *\n * @method registerEvents\n */\n competencies.prototype.registerEvents = function() {\n var localthis = this;\n\n if (localthis.itemtype == 'course') {\n // Course completion rule handling.\n $('[data-region=\"coursecompetenciespage\"]').on('change', 'select[data-field=\"ruleoutcome\"]', function(e) {\n var pendingPromise = new Pending();\n var requests = [];\n var pagerender = 'tool_lp/course_competencies_page';\n var pageregion = 'coursecompetenciespage';\n var coursecompetencyid = $(e.target).data('id');\n var ruleoutcome = $(e.target).val();\n requests = ajax.call([\n {methodname: 'core_competency_set_course_competency_ruleoutcome',\n args: {coursecompetencyid: coursecompetencyid, ruleoutcome: ruleoutcome}},\n {methodname: 'tool_lp_data_for_course_competencies_page',\n args: {courseid: localthis.itemid, moduleid: 0}}\n ]);\n\n requests[1].then(function(context) {\n return templates.render(pagerender, context);\n })\n .then(function(html, js) {\n return templates.replaceNode($('[data-region=\"' + pageregion + '\"]'), html, js);\n })\n .then(pendingPromise.resolve)\n .catch(notification.exception);\n });\n }\n\n $('[data-region=\"actions\"] button').click(function(e) {\n var pendingPromise = new Pending();\n e.preventDefault();\n\n localthis.pickCompetency()\n .then(pendingPromise.resolve)\n .catch();\n });\n $('[data-action=\"delete-competency-link\"]').click(function(e) {\n e.preventDefault();\n\n var deleteid = $(e.target).closest('[data-id]').data('id');\n localthis.deleteHandler(deleteid);\n });\n };\n\n return /** @alias module:tool_lp/competencies */ competencies;\n});\n"],"names":["define","$","notification","ajax","templates","str","Picker","dragdrop","Pending","competencies","itemid","itemtype","pagectxid","this","pageContextId","pickerInstance","prop","registerEvents","registerDragDrop","prototype","localthis","get_string","done","movestring","identifier","component","drag","drop","handleDrop","fail","exception","fromid","data","toid","requests","call","methodname","args","courseid","competencyidfrom","competencyidto","templateid","planid","pickCompetency","pagerender","pageregion","pageContextIncludes","self","on","e","compIds","competencyIds","pendingPromise","each","index","compId","push","competencyid","moduleid","pagecontext","contextid","length","then","context","render","html","js","replaceNode","resolve","catch","display","doDelete","deleteid","replaceWith","runTemplateJS","deleteHandler","message","id","competency","get_strings","key","param","shortname","strings","confirm","coursecompetencyid","target","ruleoutcome","val","click","preventDefault","closest"],"mappings":"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"}
\ No newline at end of file
diff --git a/admin/tool/lp/amd/build/competency_outcomes.min.js.map b/admin/tool/lp/amd/build/competency_outcomes.min.js.map
index e3bf7de9983..21e84e73a39 100644
--- a/admin/tool/lp/amd/build/competency_outcomes.min.js.map
+++ b/admin/tool/lp/amd/build/competency_outcomes.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"competency_outcomes.min.js","sources":["../src/competency_outcomes.js"],"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 * Competency rule config.\n *\n * @module tool_lp/competency_outcomes\n * @copyright 2015 Frédéric Massart - FMCorz.net\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\ndefine(['jquery',\n 'core/str'],\n function($, Str) {\n\n var OUTCOME_NONE = 0,\n OUTCOME_EVIDENCE = 1,\n OUTCOME_COMPLETE = 2,\n OUTCOME_RECOMMEND = 3;\n\n return {\n\n NONE: OUTCOME_NONE,\n EVIDENCE: OUTCOME_EVIDENCE,\n COMPLETE: OUTCOME_COMPLETE,\n RECOMMEND: OUTCOME_RECOMMEND,\n\n /**\n * Get all the outcomes.\n *\n * @return {Object} Indexed by outcome code, contains code and name.\n * @method getAll\n */\n getAll: function() {\n var self = this;\n return Str.get_strings([\n {key: 'competencyoutcome_none', component: 'tool_lp'},\n {key: 'competencyoutcome_evidence', component: 'tool_lp'},\n {key: 'competencyoutcome_recommend', component: 'tool_lp'},\n {key: 'competencyoutcome_complete', component: 'tool_lp'},\n ]).then(function(strings) {\n var outcomes = {};\n outcomes[self.NONE] = {code: self.NONE, name: strings[0]};\n outcomes[self.EVIDENCE] = {code: self.EVIDENCE, name: strings[1]};\n outcomes[self.RECOMMEND] = {code: self.RECOMMEND, name: strings[2]};\n outcomes[self.COMPLETE] = {code: self.COMPLETE, name: strings[3]};\n return outcomes;\n });\n },\n\n /**\n * Get the string for an outcome.\n *\n * @param {Number} id The outcome code.\n * @return {Promise} Resolved with the string.\n * @method getString\n */\n getString: function(id) {\n var self = this,\n all = self.getAll();\n\n return all.then(function(outcomes) {\n if (typeof outcomes[id] === 'undefined') {\n return $.Deferred().reject().promise();\n }\n return outcomes[id].name;\n });\n }\n };\n});\n"],"names":["define","$","Str","NONE","EVIDENCE","COMPLETE","RECOMMEND","getAll","self","this","get_strings","key","component","then","strings","outcomes","code","name","getString","id","Deferred","reject","promise"],"mappings":";;;;;;;AAuBAA,qCAAO,CAAC,SACA,aACA,SAASC,EAAGC,WAOT,CAEHC,KAPe,EAQfC,SAPmB,EAQnBC,SAPmB,EAQnBC,UAPoB,EAepBC,OAAQ,eACAC,KAAOC,YACJP,IAAIQ,YAAY,CACnB,CAACC,IAAK,yBAA0BC,UAAW,WAC3C,CAACD,IAAK,6BAA8BC,UAAW,WAC/C,CAACD,IAAK,8BAA+BC,UAAW,WAChD,CAACD,IAAK,6BAA8BC,UAAW,aAChDC,MAAK,SAASC,aACTC,SAAW,UACfA,SAASP,KAAKL,MAAQ,CAACa,KAAMR,KAAKL,KAAMc,KAAMH,QAAQ,IACtDC,SAASP,KAAKJ,UAAY,CAACY,KAAMR,KAAKJ,SAAUa,KAAMH,QAAQ,IAC9DC,SAASP,KAAKF,WAAa,CAACU,KAAMR,KAAKF,UAAWW,KAAMH,QAAQ,IAChEC,SAASP,KAAKH,UAAY,CAACW,KAAMR,KAAKH,SAAUY,KAAMH,QAAQ,IACvDC,aAWfG,UAAW,SAASC,WACLV,KACIF,SAEJM,MAAK,SAASE,sBACO,IAAjBA,SAASI,IACTlB,EAAEmB,WAAWC,SAASC,UAE1BP,SAASI,IAAIF"}
\ No newline at end of file
+{"version":3,"file":"competency_outcomes.min.js","sources":["../src/competency_outcomes.js"],"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 * Competency rule config.\n *\n * @module tool_lp/competency_outcomes\n * @copyright 2015 Frédéric Massart - FMCorz.net\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\ndefine(['jquery',\n 'core/str'],\n function($, Str) {\n\n var OUTCOME_NONE = 0,\n OUTCOME_EVIDENCE = 1,\n OUTCOME_COMPLETE = 2,\n OUTCOME_RECOMMEND = 3;\n\n return {\n\n NONE: OUTCOME_NONE,\n EVIDENCE: OUTCOME_EVIDENCE,\n COMPLETE: OUTCOME_COMPLETE,\n RECOMMEND: OUTCOME_RECOMMEND,\n\n /**\n * Get all the outcomes.\n *\n * @return {Object} Indexed by outcome code, contains code and name.\n * @method getAll\n */\n getAll: function() {\n var self = this;\n return Str.get_strings([\n {key: 'competencyoutcome_none', component: 'tool_lp'},\n {key: 'competencyoutcome_evidence', component: 'tool_lp'},\n {key: 'competencyoutcome_recommend', component: 'tool_lp'},\n {key: 'competencyoutcome_complete', component: 'tool_lp'},\n ]).then(function(strings) {\n var outcomes = {};\n outcomes[self.NONE] = {code: self.NONE, name: strings[0]};\n outcomes[self.EVIDENCE] = {code: self.EVIDENCE, name: strings[1]};\n outcomes[self.RECOMMEND] = {code: self.RECOMMEND, name: strings[2]};\n outcomes[self.COMPLETE] = {code: self.COMPLETE, name: strings[3]};\n return outcomes;\n });\n },\n\n /**\n * Get the string for an outcome.\n *\n * @param {Number} id The outcome code.\n * @return {Promise} Resolved with the string.\n * @method getString\n */\n getString: function(id) {\n var self = this,\n all = self.getAll();\n\n return all.then(function(outcomes) {\n if (typeof outcomes[id] === 'undefined') {\n return $.Deferred().reject().promise();\n }\n return outcomes[id].name;\n });\n }\n };\n});\n"],"names":["define","$","Str","NONE","EVIDENCE","COMPLETE","RECOMMEND","getAll","self","this","get_strings","key","component","then","strings","outcomes","code","name","getString","id","Deferred","reject","promise"],"mappings":";;;;;;;AAuBAA,OAAM,8BAAC,CAAC,SACA,aACA,SAASC,EAAGC,KAOhB,MAAO,CAEHC,KAPe,EAQfC,SAPmB,EAQnBC,SAPmB,EAQnBC,UAPoB,EAepBC,OAAQ,WACJ,IAAIC,KAAOC,KACX,OAAOP,IAAIQ,YAAY,CACnB,CAACC,IAAK,yBAA0BC,UAAW,WAC3C,CAACD,IAAK,6BAA8BC,UAAW,WAC/C,CAACD,IAAK,8BAA+BC,UAAW,WAChD,CAACD,IAAK,6BAA8BC,UAAW,aAChDC,MAAK,SAASC,SACb,IAAIC,SAAW,CAAA,EAKf,OAJAA,SAASP,KAAKL,MAAQ,CAACa,KAAMR,KAAKL,KAAMc,KAAMH,QAAQ,IACtDC,SAASP,KAAKJ,UAAY,CAACY,KAAMR,KAAKJ,SAAUa,KAAMH,QAAQ,IAC9DC,SAASP,KAAKF,WAAa,CAACU,KAAMR,KAAKF,UAAWW,KAAMH,QAAQ,IAChEC,SAASP,KAAKH,UAAY,CAACW,KAAMR,KAAKH,SAAUY,KAAMH,QAAQ,IACvDC,QACX,GACH,EASDG,UAAW,SAASC,IAIhB,OAHWV,KACIF,SAEJM,MAAK,SAASE,UACrB,YAA4B,IAAjBA,SAASI,IACTlB,EAAEmB,WAAWC,SAASC,UAE1BP,SAASI,IAAIF,IACxB,GACJ,EAER"}
\ No newline at end of file
diff --git a/admin/tool/lp/amd/build/competency_plan_navigation.min.js.map b/admin/tool/lp/amd/build/competency_plan_navigation.min.js.map
index 613139fd6e8..06915ce77e5 100644
--- a/admin/tool/lp/amd/build/competency_plan_navigation.min.js.map
+++ b/admin/tool/lp/amd/build/competency_plan_navigation.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"competency_plan_navigation.min.js","sources":["../src/competency_plan_navigation.js"],"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 * Event click on selecting competency in the competency autocomplete.\n *\n * @module tool_lp/competency_plan_navigation\n * @copyright 2016 Issam Taboubi \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\ndefine(['jquery'], function($) {\n\n /**\n * CompetencyPlanNavigation\n *\n * @class\n * @param {String} competencySelector The selector of the competency element.\n * @param {String} baseUrl The base url for the page (no params).\n * @param {Number} userId The user id\n * @param {Number} competencyId The competency id\n * @param {Number} planId The plan id\n */\n var CompetencyPlanNavigation = function(competencySelector, baseUrl, userId, competencyId, planId) {\n this._baseUrl = baseUrl;\n this._userId = userId + '';\n this._competencyId = competencyId + '';\n this._planId = planId;\n this._ignoreFirstCompetency = true;\n\n $(competencySelector).on('change', this._competencyChanged.bind(this));\n };\n\n /**\n * The competency was changed in the select list.\n *\n * @method _competencyChanged\n * @param {Event} e\n */\n CompetencyPlanNavigation.prototype._competencyChanged = function(e) {\n if (this._ignoreFirstCompetency) {\n this._ignoreFirstCompetency = false;\n return;\n }\n var newCompetencyId = $(e.target).val();\n var queryStr = '?userid=' + this._userId + '&planid=' + this._planId + '&competencyid=' + newCompetencyId;\n document.location = this._baseUrl + queryStr;\n };\n\n /** @property {Number} The id of the competency. */\n CompetencyPlanNavigation.prototype._competencyId = null;\n /** @property {Number} The id of the user. */\n CompetencyPlanNavigation.prototype._userId = null;\n /** @property {Number} The id of the plan. */\n CompetencyPlanNavigation.prototype._planId = null;\n /** @property {String} Plugin base url. */\n CompetencyPlanNavigation.prototype._baseUrl = null;\n /** @property {Boolean} Ignore the first change event for competencies. */\n CompetencyPlanNavigation.prototype._ignoreFirstCompetency = null;\n\n return CompetencyPlanNavigation;\n});\n"],"names":["define","$","CompetencyPlanNavigation","competencySelector","baseUrl","userId","competencyId","planId","_baseUrl","_userId","_competencyId","_planId","_ignoreFirstCompetency","on","this","_competencyChanged","bind","prototype","e","newCompetencyId","target","val","queryStr","document","location"],"mappings":";;;;;;;AAuBAA,4CAAO,CAAC,WAAW,SAASC,OAYpBC,yBAA2B,SAASC,mBAAoBC,QAASC,OAAQC,aAAcC,aAClFC,SAAWJ,aACXK,QAAUJ,OAAS,QACnBK,cAAgBJ,aAAe,QAC/BK,QAAUJ,YACVK,wBAAyB,EAE9BX,EAAEE,oBAAoBU,GAAG,SAAUC,KAAKC,mBAAmBC,KAAKF,eASpEZ,yBAAyBe,UAAUF,mBAAqB,SAASG,MACzDJ,KAAKF,4BACAA,wBAAyB,WAG9BO,gBAAkBlB,EAAEiB,EAAEE,QAAQC,MAC9BC,SAAW,WAAaR,KAAKL,QAAU,WAAaK,KAAKH,QAAU,iBAAmBQ,gBAC1FI,SAASC,SAAWV,KAAKN,SAAWc,WAIxCpB,yBAAyBe,UAAUP,cAAgB,KAEnDR,yBAAyBe,UAAUR,QAAU,KAE7CP,yBAAyBe,UAAUN,QAAU,KAE7CT,yBAAyBe,UAAUT,SAAW,KAE9CN,yBAAyBe,UAAUL,uBAAyB,KAErDV"}
\ No newline at end of file
+{"version":3,"file":"competency_plan_navigation.min.js","sources":["../src/competency_plan_navigation.js"],"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 * Event click on selecting competency in the competency autocomplete.\n *\n * @module tool_lp/competency_plan_navigation\n * @copyright 2016 Issam Taboubi \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\ndefine(['jquery'], function($) {\n\n /**\n * CompetencyPlanNavigation\n *\n * @class\n * @param {String} competencySelector The selector of the competency element.\n * @param {String} baseUrl The base url for the page (no params).\n * @param {Number} userId The user id\n * @param {Number} competencyId The competency id\n * @param {Number} planId The plan id\n */\n var CompetencyPlanNavigation = function(competencySelector, baseUrl, userId, competencyId, planId) {\n this._baseUrl = baseUrl;\n this._userId = userId + '';\n this._competencyId = competencyId + '';\n this._planId = planId;\n this._ignoreFirstCompetency = true;\n\n $(competencySelector).on('change', this._competencyChanged.bind(this));\n };\n\n /**\n * The competency was changed in the select list.\n *\n * @method _competencyChanged\n * @param {Event} e\n */\n CompetencyPlanNavigation.prototype._competencyChanged = function(e) {\n if (this._ignoreFirstCompetency) {\n this._ignoreFirstCompetency = false;\n return;\n }\n var newCompetencyId = $(e.target).val();\n var queryStr = '?userid=' + this._userId + '&planid=' + this._planId + '&competencyid=' + newCompetencyId;\n document.location = this._baseUrl + queryStr;\n };\n\n /** @property {Number} The id of the competency. */\n CompetencyPlanNavigation.prototype._competencyId = null;\n /** @property {Number} The id of the user. */\n CompetencyPlanNavigation.prototype._userId = null;\n /** @property {Number} The id of the plan. */\n CompetencyPlanNavigation.prototype._planId = null;\n /** @property {String} Plugin base url. */\n CompetencyPlanNavigation.prototype._baseUrl = null;\n /** @property {Boolean} Ignore the first change event for competencies. */\n CompetencyPlanNavigation.prototype._ignoreFirstCompetency = null;\n\n return CompetencyPlanNavigation;\n});\n"],"names":["define","$","CompetencyPlanNavigation","competencySelector","baseUrl","userId","competencyId","planId","this","_baseUrl","_userId","_competencyId","_planId","_ignoreFirstCompetency","on","_competencyChanged","bind","prototype","e","newCompetencyId","target","val","queryStr","document","location"],"mappings":";;;;;;;AAuBAA,4CAAO,CAAC,WAAW,SAASC,GAYxB,IAAIC,yBAA2B,SAASC,mBAAoBC,QAASC,OAAQC,aAAcC,QACvFC,KAAKC,SAAWL,QAChBI,KAAKE,QAAUL,OAAS,GACxBG,KAAKG,cAAgBL,aAAe,GACpCE,KAAKI,QAAUL,OACfC,KAAKK,wBAAyB,EAE9BZ,EAAEE,oBAAoBW,GAAG,SAAUN,KAAKO,mBAAmBC,KAAKR,QA8BpE,OArBAN,yBAAyBe,UAAUF,mBAAqB,SAASG,GAC7D,GAAIV,KAAKK,uBACLL,KAAKK,wBAAyB,MADlC,CAIA,IAAIM,gBAAkBlB,EAAEiB,EAAEE,QAAQC,MAC9BC,SAAW,WAAad,KAAKE,QAAU,WAAaF,KAAKI,QAAU,iBAAmBO,gBAC1FI,SAASC,SAAWhB,KAAKC,SAAWa,QAHpC,GAOJpB,yBAAyBe,UAAUN,cAAgB,KAEnDT,yBAAyBe,UAAUP,QAAU,KAE7CR,yBAAyBe,UAAUL,QAAU,KAE7CV,yBAAyBe,UAAUR,SAAW,KAE9CP,yBAAyBe,UAAUJ,uBAAyB,KAErDX,wBACX"}
\ No newline at end of file
diff --git a/admin/tool/lp/amd/build/competency_rule.min.js.map b/admin/tool/lp/amd/build/competency_rule.min.js.map
index a0fe9913190..7f7eec6de61 100644
--- a/admin/tool/lp/amd/build/competency_rule.min.js.map
+++ b/admin/tool/lp/amd/build/competency_rule.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"competency_rule.min.js","sources":["../src/competency_rule.js"],"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 * Competency rule base module.\n *\n * @module tool_lp/competencyrule\n * @copyright 2015 Frédéric Massart - FMCorz.net\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\ndefine(['jquery'], function($) {\n\n /**\n * Competency rule abstract class.\n *\n * Any competency rule should extend this object. The event 'change' should be\n * triggered on the instance when the configuration has changed. This will allow\n * the components using the rule to gather the config, or check its validity.\n *\n * this._triggerChange();\n *\n * @param {Tree} tree The competency tree.\n */\n var Rule = function(tree) {\n this._eventNode = $('
');\n this._ready = $.Deferred();\n this._tree = tree;\n };\n\n /** @property {Object} The current competency. */\n Rule.prototype._competency = null;\n /** @property {Node} The node we attach the events to. */\n Rule.prototype._eventNode = null;\n /** @property {Promise} Resolved when the object is ready. */\n Rule.prototype._ready = null;\n /** @property {Tree} The competency tree. */\n Rule.prototype._tree = null;\n\n /**\n * Whether or not the current competency can be configured using this rule.\n *\n * @return {Boolean}\n * @method canConfig\n */\n Rule.prototype.canConfig = function() {\n return this._tree.hasChildren(this._competency.id);\n };\n\n /**\n * The config established by this rule.\n *\n * To override in subclasses when relevant.\n *\n * @return {String|null}\n * @method getConfig\n */\n Rule.prototype.getConfig = function() {\n return null;\n };\n\n // eslint-disable-line valid-jsdoc\n /**\n * Return the type of the module.\n *\n * @return {String}\n * @method getType\n */\n // eslint-enable-line valid-jsdoc\n Rule.prototype.getType = function() {\n throw new Error('Not implemented');\n };\n\n /**\n * The init process.\n *\n * Do not override this, instead override _load.\n *\n * @return {Promise} Revoled when the plugin is initialised.\n * @method init\n */\n Rule.prototype.init = function() {\n return this._load();\n };\n\n /**\n * Callback to inject the template.\n *\n * @returns {Promise} Resolved when done.\n * @method injectTemplate\n */\n Rule.prototype.injectTemplate = function() {\n return $.Deferred().reject().promise();\n };\n\n /**\n * Whether or not the current config is valid.\n *\n * Plugins should override this.\n *\n * @return {Boolean}\n * @method _isValid\n */\n Rule.prototype.isValid = function() {\n return false;\n };\n\n /**\n * Load the class.\n *\n * @return {Promise}\n * @method _load\n * @protected\n */\n Rule.prototype._load = function() {\n return $.when();\n };\n\n /**\n * Register an event listener.\n *\n * @param {String} type The event type.\n * @param {Function} handler The event listener.\n * @method on\n */\n Rule.prototype.on = function(type, handler) {\n this._eventNode.on(type, handler);\n };\n\n /**\n * Sets the current competency.\n *\n * @param {Competency} competency\n * @method setTargetCompetency\n */\n Rule.prototype.setTargetCompetency = function(competency) {\n this._competency = competency;\n };\n\n /**\n * Trigger an event.\n *\n * @param {String} type The type of event.\n * @param {Object} data The data to pass to the listeners.\n * @method _trigger\n * @protected\n */\n Rule.prototype._trigger = function(type, data) {\n this._eventNode.trigger(type, [data]);\n };\n\n /**\n * Trigger the change event.\n *\n * @method _triggerChange\n * @protected\n */\n Rule.prototype._triggerChange = function() {\n this._trigger('change', this);\n };\n\n return /** @alias module:tool_lp/competency_rule */ Rule;\n\n});\n"],"names":["define","$","Rule","tree","_eventNode","_ready","Deferred","_tree","prototype","_competency","canConfig","this","hasChildren","id","getConfig","getType","Error","init","_load","injectTemplate","reject","promise","isValid","when","on","type","handler","setTargetCompetency","competency","_trigger","data","trigger","_triggerChange"],"mappings":";;;;;;;AAuBAA,iCAAO,CAAC,WAAW,SAASC,OAapBC,KAAO,SAASC,WACXC,WAAaH,EAAE,cACfI,OAASJ,EAAEK,gBACXC,MAAQJ,aAIjBD,KAAKM,UAAUC,YAAc,KAE7BP,KAAKM,UAAUJ,WAAa,KAE5BF,KAAKM,UAAUH,OAAS,KAExBH,KAAKM,UAAUD,MAAQ,KAQvBL,KAAKM,UAAUE,UAAY,kBAChBC,KAAKJ,MAAMK,YAAYD,KAAKF,YAAYI,KAWnDX,KAAKM,UAAUM,UAAY,kBAChB,MAWXZ,KAAKM,UAAUO,QAAU,iBACf,IAAIC,MAAM,oBAWpBd,KAAKM,UAAUS,KAAO,kBACXN,KAAKO,SAShBhB,KAAKM,UAAUW,eAAiB,kBACrBlB,EAAEK,WAAWc,SAASC,WAWjCnB,KAAKM,UAAUc,QAAU,kBACd,GAUXpB,KAAKM,UAAUU,MAAQ,kBACZjB,EAAEsB,QAUbrB,KAAKM,UAAUgB,GAAK,SAASC,KAAMC,cAC1BtB,WAAWoB,GAAGC,KAAMC,UAS7BxB,KAAKM,UAAUmB,oBAAsB,SAASC,iBACrCnB,YAAcmB,YAWvB1B,KAAKM,UAAUqB,SAAW,SAASJ,KAAMK,WAChC1B,WAAW2B,QAAQN,KAAM,CAACK,QASnC5B,KAAKM,UAAUwB,eAAiB,gBACvBH,SAAS,SAAUlB,OAGwBT"}
\ No newline at end of file
+{"version":3,"file":"competency_rule.min.js","sources":["../src/competency_rule.js"],"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 * Competency rule base module.\n *\n * @module tool_lp/competencyrule\n * @copyright 2015 Frédéric Massart - FMCorz.net\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\ndefine(['jquery'], function($) {\n\n /**\n * Competency rule abstract class.\n *\n * Any competency rule should extend this object. The event 'change' should be\n * triggered on the instance when the configuration has changed. This will allow\n * the components using the rule to gather the config, or check its validity.\n *\n * this._triggerChange();\n *\n * @param {Tree} tree The competency tree.\n */\n var Rule = function(tree) {\n this._eventNode = $('
');\n this._ready = $.Deferred();\n this._tree = tree;\n };\n\n /** @property {Object} The current competency. */\n Rule.prototype._competency = null;\n /** @property {Node} The node we attach the events to. */\n Rule.prototype._eventNode = null;\n /** @property {Promise} Resolved when the object is ready. */\n Rule.prototype._ready = null;\n /** @property {Tree} The competency tree. */\n Rule.prototype._tree = null;\n\n /**\n * Whether or not the current competency can be configured using this rule.\n *\n * @return {Boolean}\n * @method canConfig\n */\n Rule.prototype.canConfig = function() {\n return this._tree.hasChildren(this._competency.id);\n };\n\n /**\n * The config established by this rule.\n *\n * To override in subclasses when relevant.\n *\n * @return {String|null}\n * @method getConfig\n */\n Rule.prototype.getConfig = function() {\n return null;\n };\n\n // eslint-disable-line valid-jsdoc\n /**\n * Return the type of the module.\n *\n * @return {String}\n * @method getType\n */\n // eslint-enable-line valid-jsdoc\n Rule.prototype.getType = function() {\n throw new Error('Not implemented');\n };\n\n /**\n * The init process.\n *\n * Do not override this, instead override _load.\n *\n * @return {Promise} Revoled when the plugin is initialised.\n * @method init\n */\n Rule.prototype.init = function() {\n return this._load();\n };\n\n /**\n * Callback to inject the template.\n *\n * @returns {Promise} Resolved when done.\n * @method injectTemplate\n */\n Rule.prototype.injectTemplate = function() {\n return $.Deferred().reject().promise();\n };\n\n /**\n * Whether or not the current config is valid.\n *\n * Plugins should override this.\n *\n * @return {Boolean}\n * @method _isValid\n */\n Rule.prototype.isValid = function() {\n return false;\n };\n\n /**\n * Load the class.\n *\n * @return {Promise}\n * @method _load\n * @protected\n */\n Rule.prototype._load = function() {\n return $.when();\n };\n\n /**\n * Register an event listener.\n *\n * @param {String} type The event type.\n * @param {Function} handler The event listener.\n * @method on\n */\n Rule.prototype.on = function(type, handler) {\n this._eventNode.on(type, handler);\n };\n\n /**\n * Sets the current competency.\n *\n * @param {Competency} competency\n * @method setTargetCompetency\n */\n Rule.prototype.setTargetCompetency = function(competency) {\n this._competency = competency;\n };\n\n /**\n * Trigger an event.\n *\n * @param {String} type The type of event.\n * @param {Object} data The data to pass to the listeners.\n * @method _trigger\n * @protected\n */\n Rule.prototype._trigger = function(type, data) {\n this._eventNode.trigger(type, [data]);\n };\n\n /**\n * Trigger the change event.\n *\n * @method _triggerChange\n * @protected\n */\n Rule.prototype._triggerChange = function() {\n this._trigger('change', this);\n };\n\n return /** @alias module:tool_lp/competency_rule */ Rule;\n\n});\n"],"names":["define","$","Rule","tree","this","_eventNode","_ready","Deferred","_tree","prototype","_competency","canConfig","hasChildren","id","getConfig","getType","Error","init","_load","injectTemplate","reject","promise","isValid","when","on","type","handler","setTargetCompetency","competency","_trigger","data","trigger","_triggerChange"],"mappings":";;;;;;;AAuBAA,iCAAO,CAAC,WAAW,SAASC,GAaxB,IAAIC,KAAO,SAASC,MAChBC,KAAKC,WAAaJ,EAAE,SACpBG,KAAKE,OAASL,EAAEM,WAChBH,KAAKI,MAAQL,MAsIjB,OAlIAD,KAAKO,UAAUC,YAAc,KAE7BR,KAAKO,UAAUJ,WAAa,KAE5BH,KAAKO,UAAUH,OAAS,KAExBJ,KAAKO,UAAUD,MAAQ,KAQvBN,KAAKO,UAAUE,UAAY,WACvB,OAAOP,KAAKI,MAAMI,YAAYR,KAAKM,YAAYG,KAWnDX,KAAKO,UAAUK,UAAY,WACvB,OAAO,MAWXZ,KAAKO,UAAUM,QAAU,WACrB,MAAM,IAAIC,MAAM,oBAWpBd,KAAKO,UAAUQ,KAAO,WAClB,OAAOb,KAAKc,SAShBhB,KAAKO,UAAUU,eAAiB,WAC5B,OAAOlB,EAAEM,WAAWa,SAASC,WAWjCnB,KAAKO,UAAUa,QAAU,WACrB,OAAO,GAUXpB,KAAKO,UAAUS,MAAQ,WACnB,OAAOjB,EAAEsB,QAUbrB,KAAKO,UAAUe,GAAK,SAASC,KAAMC,SAC/BtB,KAAKC,WAAWmB,GAAGC,KAAMC,UAS7BxB,KAAKO,UAAUkB,oBAAsB,SAASC,YAC1CxB,KAAKM,YAAckB,YAWvB1B,KAAKO,UAAUoB,SAAW,SAASJ,KAAMK,MACrC1B,KAAKC,WAAW0B,QAAQN,KAAM,CAACK,QASnC5B,KAAKO,UAAUuB,eAAiB,WAC5B5B,KAAKyB,SAAS,SAAUzB,OAGwBF,IAExD"}
\ No newline at end of file
diff --git a/admin/tool/lp/amd/build/competency_rule_all.min.js.map b/admin/tool/lp/amd/build/competency_rule_all.min.js.map
index cb5f90d05f7..400f8734e59 100644
--- a/admin/tool/lp/amd/build/competency_rule_all.min.js.map
+++ b/admin/tool/lp/amd/build/competency_rule_all.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"competency_rule_all.min.js","sources":["../src/competency_rule_all.js"],"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 * Competency rule all module.\n *\n * @module tool_lp/competency_rule_all\n * @copyright 2015 Frédéric Massart - FMCorz.net\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\ndefine(['jquery',\n 'core/str',\n 'tool_lp/competency_rule',\n ],\n function($, Str, RuleBase) {\n\n /**\n * Competency rule all class.\n *\n * @class tool_lp/competency_rule_all\n */\n var Rule = function() {\n RuleBase.apply(this, arguments);\n };\n Rule.prototype = Object.create(RuleBase.prototype);\n\n /**\n * Return the type of the module.\n *\n * @return {String}\n * @method getType\n */\n Rule.prototype.getType = function() {\n return 'core_competency\\\\competency_rule_all';\n };\n\n /**\n * Whether or not the current config is valid.\n *\n * @return {Boolean}\n * @method isValid\n */\n Rule.prototype.isValid = function() {\n return true;\n };\n\n return Rule;\n});\n"],"names":["define","$","Str","RuleBase","Rule","apply","this","arguments","prototype","Object","create","getType","isValid"],"mappings":";;;;;;;AAuBAA,qCAAO,CAAC,SACA,WACA,4BAEA,SAASC,EAAGC,IAAKC,cAOjBC,KAAO,WACPD,SAASE,MAAMC,KAAMC,mBAEzBH,KAAKI,UAAYC,OAAOC,OAAOP,SAASK,YAQzBG,QAAU,iBACd,wCASXP,KAAKI,UAAUI,QAAU,kBACd,GAGJR"}
\ No newline at end of file
+{"version":3,"file":"competency_rule_all.min.js","sources":["../src/competency_rule_all.js"],"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 * Competency rule all module.\n *\n * @module tool_lp/competency_rule_all\n * @copyright 2015 Frédéric Massart - FMCorz.net\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\ndefine(['jquery',\n 'core/str',\n 'tool_lp/competency_rule',\n ],\n function($, Str, RuleBase) {\n\n /**\n * Competency rule all class.\n *\n * @class tool_lp/competency_rule_all\n */\n var Rule = function() {\n RuleBase.apply(this, arguments);\n };\n Rule.prototype = Object.create(RuleBase.prototype);\n\n /**\n * Return the type of the module.\n *\n * @return {String}\n * @method getType\n */\n Rule.prototype.getType = function() {\n return 'core_competency\\\\competency_rule_all';\n };\n\n /**\n * Whether or not the current config is valid.\n *\n * @return {Boolean}\n * @method isValid\n */\n Rule.prototype.isValid = function() {\n return true;\n };\n\n return Rule;\n});\n"],"names":["define","$","Str","RuleBase","Rule","apply","this","arguments","prototype","Object","create","getType","isValid"],"mappings":";;;;;;;AAuBAA,OAAO,8BAAA,CAAC,SACA,WACA,4BAEA,SAASC,EAAGC,IAAKC,UAOrB,IAAIC,KAAO,WACPD,SAASE,MAAMC,KAAMC,YAwBzB,OAtBAH,KAAKI,UAAYC,OAAOC,OAAOP,SAASK,YAQzBG,QAAU,WACrB,MAAO,wCASXP,KAAKI,UAAUI,QAAU,WACrB,OAAO,GAGJR,IACX"}
\ No newline at end of file
diff --git a/admin/tool/lp/amd/build/competency_rule_points.min.js.map b/admin/tool/lp/amd/build/competency_rule_points.min.js.map
index d3583f13916..c047dacfdaf 100644
--- a/admin/tool/lp/amd/build/competency_rule_points.min.js.map
+++ b/admin/tool/lp/amd/build/competency_rule_points.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"competency_rule_points.min.js","sources":["../src/competency_rule_points.js"],"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 * Competency rule points module.\n *\n * @module tool_lp/competency_rule_all\n * @copyright 2015 Frédéric Massart - FMCorz.net\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\ndefine(['jquery',\n 'core/str',\n 'core/templates',\n 'tool_lp/competency_rule',\n ],\n function($, Str, Templates, RuleBase) {\n\n /**\n * Competency rule points class.\n */\n var Rule = function() {\n RuleBase.apply(this, arguments);\n };\n Rule.prototype = Object.create(RuleBase.prototype);\n\n /** @property {Node} Reference to the container in which the template was included. */\n Rule.prototype._container = null;\n /** @property {Boolean} Whether or not the template was included. */\n Rule.prototype._templateLoaded = false;\n\n /**\n * The config established by this rule.\n *\n * @return {String}\n * @method getConfig\n */\n Rule.prototype.getConfig = function() {\n return JSON.stringify({\n base: {\n points: this._getRequiredPoints(),\n },\n competencies: this._getCompetenciesConfig()\n });\n };\n\n /**\n * Gathers the input provided by the user for competencies.\n *\n * @return {Array} Containing id, points and required.\n * @method _getCompetenciesConfig\n * @protected\n */\n Rule.prototype._getCompetenciesConfig = function() {\n var competencies = [];\n\n this._container.find('[data-competency]').each(function() {\n var node = $(this),\n id = node.data('competency'),\n points = parseInt(node.find('[name=\"points\"]').val(), 10),\n required = node.find('[name=\"required\"]').prop('checked');\n\n competencies.push({\n id: id,\n points: points,\n required: required ? 1 : 0\n });\n });\n\n return competencies;\n };\n\n /**\n * Fetches the required points set by the user.\n *\n * @return {Number}\n * @method _getRequiredPoints\n * @protected\n */\n Rule.prototype._getRequiredPoints = function() {\n return parseInt(this._container.find('[name=\"requiredpoints\"]').val() || 1, 10);\n };\n\n /**\n * Return the type of the module.\n *\n * @return {String}\n * @method getType\n */\n Rule.prototype.getType = function() {\n return 'core_competency\\\\competency_rule_points';\n };\n\n /**\n * Callback to inject the template.\n *\n * @param {Node} container Node to inject in.\n * @return {Promise} Resolved when done.\n * @method injectTemplate\n */\n Rule.prototype.injectTemplate = function(container) {\n var self = this,\n children = this._tree.getChildren(this._competency.id),\n context,\n config = {\n base: {points: 2},\n competencies: []\n };\n\n this._templateLoaded = false;\n\n // Only pre-load the configuration when the competency is using this rule.\n if (self._competency.ruletype == self.getType()) {\n try {\n config = JSON.parse(self._competency.ruleconfig);\n } catch (e) {\n // eslint-disable-line no-empty\n }\n }\n\n context = {\n requiredpoints: (config && config.base) ? config.base.points : 2,\n competency: self._competency,\n children: []\n };\n\n $.each(children, function(index, child) {\n var competency = {\n id: child.id,\n shortname: child.shortname,\n required: false,\n points: 0\n };\n\n if (config) {\n $.each(config.competencies, function(index, comp) {\n if (comp.id == competency.id) {\n competency.required = comp.required ? true : false;\n competency.points = comp.points;\n }\n });\n }\n\n context.children.push(competency);\n });\n\n return Templates.render('tool_lp/competency_rule_points', context).then(function(html) {\n self._container = container;\n container.html(html);\n container.find('input').change(function() {\n self._triggerChange();\n });\n\n // We're done, let's trigger a change.\n self._templateLoaded = true;\n self._triggerChange();\n return;\n });\n };\n\n /**\n * Whether or not the current config is valid.\n *\n * @return {Boolean}\n * @method isValid\n */\n Rule.prototype.isValid = function() {\n if (!this._templateLoaded) {\n return false;\n }\n\n var required = this._getRequiredPoints(),\n max = 0,\n valid = true;\n\n $.each(this._getCompetenciesConfig(), function(index, competency) {\n if (competency.points < 0) {\n valid = false;\n }\n max += competency.points;\n });\n\n valid = valid && max >= required;\n return valid;\n };\n\n return Rule;\n});\n"],"names":["define","$","Str","Templates","RuleBase","Rule","apply","this","arguments","prototype","Object","create","_container","_templateLoaded","getConfig","JSON","stringify","base","points","_getRequiredPoints","competencies","_getCompetenciesConfig","find","each","node","id","data","parseInt","val","required","prop","push","getType","injectTemplate","container","context","self","children","_tree","getChildren","_competency","config","ruletype","parse","ruleconfig","e","requiredpoints","competency","index","child","shortname","comp","render","then","html","change","_triggerChange","isValid","max","valid"],"mappings":";;;;;;;AAuBAA,wCAAO,CAAC,SACA,WACA,iBACA,4BAEA,SAASC,EAAGC,IAAKC,UAAWC,cAK5BC,KAAO,WACPD,SAASE,MAAMC,KAAMC,mBAEzBH,KAAKI,UAAYC,OAAOC,OAAOP,SAASK,YAGzBG,WAAa,KAE5BP,KAAKI,UAAUI,iBAAkB,EAQjCR,KAAKI,UAAUK,UAAY,kBAChBC,KAAKC,UAAU,CAClBC,KAAM,CACFC,OAAQX,KAAKY,sBAEjBC,aAAcb,KAAKc,4BAW3BhB,KAAKI,UAAUY,uBAAyB,eAChCD,aAAe,eAEdR,WAAWU,KAAK,qBAAqBC,MAAK,eACvCC,KAAOvB,EAAEM,MACTkB,GAAKD,KAAKE,KAAK,cACfR,OAASS,SAASH,KAAKF,KAAK,mBAAmBM,MAAO,IACtDC,SAAWL,KAAKF,KAAK,qBAAqBQ,KAAK,WAEnDV,aAAaW,KAAK,CACdN,GAAIA,GACJP,OAAQA,OACRW,SAAUA,SAAW,EAAI,OAI1BT,cAUXf,KAAKI,UAAUU,mBAAqB,kBACzBQ,SAASpB,KAAKK,WAAWU,KAAK,2BAA2BM,OAAS,EAAG,KAShFvB,KAAKI,UAAUuB,QAAU,iBACd,2CAUX3B,KAAKI,UAAUwB,eAAiB,SAASC,eAGjCC,QAFAC,KAAO7B,KACP8B,SAAW9B,KAAK+B,MAAMC,YAAYhC,KAAKiC,YAAYf,IAEnDgB,OAAS,CACLxB,KAAM,CAACC,OAAQ,GACfE,aAAc,YAGjBP,iBAAkB,EAGnBuB,KAAKI,YAAYE,UAAYN,KAAKJ,cAE9BS,OAAS1B,KAAK4B,MAAMP,KAAKI,YAAYI,YACvC,MAAOC,WAKbV,QAAU,CACNW,eAAiBL,QAAUA,OAAOxB,KAAQwB,OAAOxB,KAAKC,OAAS,EAC/D6B,WAAYX,KAAKI,YACjBH,SAAU,IAGdpC,EAAEsB,KAAKc,UAAU,SAASW,MAAOC,WACzBF,WAAa,CACbtB,GAAIwB,MAAMxB,GACVyB,UAAWD,MAAMC,UACjBrB,UAAU,EACVX,OAAQ,GAGRuB,QACAxC,EAAEsB,KAAKkB,OAAOrB,cAAc,SAAS4B,MAAOG,MACpCA,KAAK1B,IAAMsB,WAAWtB,KACtBsB,WAAWlB,WAAWsB,KAAKtB,SAC3BkB,WAAW7B,OAASiC,KAAKjC,WAKrCiB,QAAQE,SAASN,KAAKgB,eAGnB5C,UAAUiD,OAAO,iCAAkCjB,SAASkB,MAAK,SAASC,MAC7ElB,KAAKxB,WAAasB,UAClBA,UAAUoB,KAAKA,MACfpB,UAAUZ,KAAK,SAASiC,QAAO,WAC3BnB,KAAKoB,oBAITpB,KAAKvB,iBAAkB,EACvBuB,KAAKoB,qBAWbnD,KAAKI,UAAUgD,QAAU,eAChBlD,KAAKM,uBACC,MAGPgB,SAAWtB,KAAKY,qBAChBuC,IAAM,EACNC,OAAQ,SAEZ1D,EAAEsB,KAAKhB,KAAKc,0BAA0B,SAAS2B,MAAOD,YAC9CA,WAAW7B,OAAS,IACpByC,OAAQ,GAEZD,KAAOX,WAAW7B,UAGtByC,MAAQA,OAASD,KAAO7B,UAIrBxB"}
\ No newline at end of file
+{"version":3,"file":"competency_rule_points.min.js","sources":["../src/competency_rule_points.js"],"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 * Competency rule points module.\n *\n * @module tool_lp/competency_rule_all\n * @copyright 2015 Frédéric Massart - FMCorz.net\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\ndefine(['jquery',\n 'core/str',\n 'core/templates',\n 'tool_lp/competency_rule',\n ],\n function($, Str, Templates, RuleBase) {\n\n /**\n * Competency rule points class.\n */\n var Rule = function() {\n RuleBase.apply(this, arguments);\n };\n Rule.prototype = Object.create(RuleBase.prototype);\n\n /** @property {Node} Reference to the container in which the template was included. */\n Rule.prototype._container = null;\n /** @property {Boolean} Whether or not the template was included. */\n Rule.prototype._templateLoaded = false;\n\n /**\n * The config established by this rule.\n *\n * @return {String}\n * @method getConfig\n */\n Rule.prototype.getConfig = function() {\n return JSON.stringify({\n base: {\n points: this._getRequiredPoints(),\n },\n competencies: this._getCompetenciesConfig()\n });\n };\n\n /**\n * Gathers the input provided by the user for competencies.\n *\n * @return {Array} Containing id, points and required.\n * @method _getCompetenciesConfig\n * @protected\n */\n Rule.prototype._getCompetenciesConfig = function() {\n var competencies = [];\n\n this._container.find('[data-competency]').each(function() {\n var node = $(this),\n id = node.data('competency'),\n points = parseInt(node.find('[name=\"points\"]').val(), 10),\n required = node.find('[name=\"required\"]').prop('checked');\n\n competencies.push({\n id: id,\n points: points,\n required: required ? 1 : 0\n });\n });\n\n return competencies;\n };\n\n /**\n * Fetches the required points set by the user.\n *\n * @return {Number}\n * @method _getRequiredPoints\n * @protected\n */\n Rule.prototype._getRequiredPoints = function() {\n return parseInt(this._container.find('[name=\"requiredpoints\"]').val() || 1, 10);\n };\n\n /**\n * Return the type of the module.\n *\n * @return {String}\n * @method getType\n */\n Rule.prototype.getType = function() {\n return 'core_competency\\\\competency_rule_points';\n };\n\n /**\n * Callback to inject the template.\n *\n * @param {Node} container Node to inject in.\n * @return {Promise} Resolved when done.\n * @method injectTemplate\n */\n Rule.prototype.injectTemplate = function(container) {\n var self = this,\n children = this._tree.getChildren(this._competency.id),\n context,\n config = {\n base: {points: 2},\n competencies: []\n };\n\n this._templateLoaded = false;\n\n // Only pre-load the configuration when the competency is using this rule.\n if (self._competency.ruletype == self.getType()) {\n try {\n config = JSON.parse(self._competency.ruleconfig);\n } catch (e) {\n // eslint-disable-line no-empty\n }\n }\n\n context = {\n requiredpoints: (config && config.base) ? config.base.points : 2,\n competency: self._competency,\n children: []\n };\n\n $.each(children, function(index, child) {\n var competency = {\n id: child.id,\n shortname: child.shortname,\n required: false,\n points: 0\n };\n\n if (config) {\n $.each(config.competencies, function(index, comp) {\n if (comp.id == competency.id) {\n competency.required = comp.required ? true : false;\n competency.points = comp.points;\n }\n });\n }\n\n context.children.push(competency);\n });\n\n return Templates.render('tool_lp/competency_rule_points', context).then(function(html) {\n self._container = container;\n container.html(html);\n container.find('input').change(function() {\n self._triggerChange();\n });\n\n // We're done, let's trigger a change.\n self._templateLoaded = true;\n self._triggerChange();\n return;\n });\n };\n\n /**\n * Whether or not the current config is valid.\n *\n * @return {Boolean}\n * @method isValid\n */\n Rule.prototype.isValid = function() {\n if (!this._templateLoaded) {\n return false;\n }\n\n var required = this._getRequiredPoints(),\n max = 0,\n valid = true;\n\n $.each(this._getCompetenciesConfig(), function(index, competency) {\n if (competency.points < 0) {\n valid = false;\n }\n max += competency.points;\n });\n\n valid = valid && max >= required;\n return valid;\n };\n\n return Rule;\n});\n"],"names":["define","$","Str","Templates","RuleBase","Rule","apply","this","arguments","prototype","Object","create","_container","_templateLoaded","getConfig","JSON","stringify","base","points","_getRequiredPoints","competencies","_getCompetenciesConfig","find","each","node","id","data","parseInt","val","required","prop","push","getType","injectTemplate","container","context","self","children","_tree","getChildren","_competency","config","ruletype","parse","ruleconfig","e","requiredpoints","competency","index","child","shortname","comp","render","then","html","change","_triggerChange","isValid","max","valid"],"mappings":";;;;;;;AAuBAA,wCAAO,CAAC,SACA,WACA,iBACA,4BAEA,SAASC,EAAGC,IAAKC,UAAWC,UAKhC,IAAIC,KAAO,WACPD,SAASE,MAAMC,KAAMC,YAoKzB,OAlKAH,KAAKI,UAAYC,OAAOC,OAAOP,SAASK,YAGzBG,WAAa,KAE5BP,KAAKI,UAAUI,iBAAkB,EAQjCR,KAAKI,UAAUK,UAAY,WACvB,OAAOC,KAAKC,UAAU,CAClBC,KAAM,CACFC,OAAQX,KAAKY,sBAEjBC,aAAcb,KAAKc,4BAW3BhB,KAAKI,UAAUY,uBAAyB,WACpC,IAAID,aAAe,GAenB,OAbAb,KAAKK,WAAWU,KAAK,qBAAqBC,MAAK,WAC3C,IAAIC,KAAOvB,EAAEM,MACTkB,GAAKD,KAAKE,KAAK,cACfR,OAASS,SAASH,KAAKF,KAAK,mBAAmBM,MAAO,IACtDC,SAAWL,KAAKF,KAAK,qBAAqBQ,KAAK,WAEnDV,aAAaW,KAAK,CACdN,GAAIA,GACJP,OAAQA,OACRW,SAAUA,SAAW,EAAI,GAEjC,IAEOT,cAUXf,KAAKI,UAAUU,mBAAqB,WAChC,OAAOQ,SAASpB,KAAKK,WAAWU,KAAK,2BAA2BM,OAAS,EAAG,KAShFvB,KAAKI,UAAUuB,QAAU,WACrB,MAAO,2CAUX3B,KAAKI,UAAUwB,eAAiB,SAASC,WACrC,IAEIC,QAFAC,KAAO7B,KACP8B,SAAW9B,KAAK+B,MAAMC,YAAYhC,KAAKiC,YAAYf,IAEnDgB,OAAS,CACLxB,KAAM,CAACC,OAAQ,GACfE,aAAc,IAMtB,GAHAb,KAAKM,iBAAkB,EAGnBuB,KAAKI,YAAYE,UAAYN,KAAKJ,UAClC,IACIS,OAAS1B,KAAK4B,MAAMP,KAAKI,YAAYI,WACzC,CAAE,MAAOC,GAET,CA6BJ,OA1BAV,QAAU,CACNW,eAAiBL,QAAUA,OAAOxB,KAAQwB,OAAOxB,KAAKC,OAAS,EAC/D6B,WAAYX,KAAKI,YACjBH,SAAU,IAGdpC,EAAEsB,KAAKc,UAAU,SAASW,MAAOC,OAC7B,IAAIF,WAAa,CACbtB,GAAIwB,MAAMxB,GACVyB,UAAWD,MAAMC,UACjBrB,UAAU,EACVX,OAAQ,GAGRuB,QACAxC,EAAEsB,KAAKkB,OAAOrB,cAAc,SAAS4B,MAAOG,MACpCA,KAAK1B,IAAMsB,WAAWtB,KACtBsB,WAAWlB,WAAWsB,KAAKtB,SAC3BkB,WAAW7B,OAASiC,KAAKjC,OAEjC,IAGJiB,QAAQE,SAASN,KAAKgB,WAC1B,IAEO5C,UAAUiD,OAAO,iCAAkCjB,SAASkB,MAAK,SAASC,MAC7ElB,KAAKxB,WAAasB,UAClBA,UAAUoB,KAAKA,MACfpB,UAAUZ,KAAK,SAASiC,QAAO,WAC3BnB,KAAKoB,gBACT,IAGApB,KAAKvB,iBAAkB,EACvBuB,KAAKoB,gBAET,KASJnD,KAAKI,UAAUgD,QAAU,WACrB,IAAKlD,KAAKM,gBACN,OAAO,EAGX,IAAIgB,SAAWtB,KAAKY,qBAChBuC,IAAM,EACNC,OAAQ,EAUZ,OARA1D,EAAEsB,KAAKhB,KAAKc,0BAA0B,SAAS2B,MAAOD,YAC9CA,WAAW7B,OAAS,IACpByC,OAAQ,GAEZD,KAAOX,WAAW7B,MACtB,IAEAyC,MAAQA,OAASD,KAAO7B,UAIrBxB,IACX"}
\ No newline at end of file
diff --git a/admin/tool/lp/amd/build/competencyactions.min.js.map b/admin/tool/lp/amd/build/competencyactions.min.js.map
index aeac60ed8e9..dffbf67e91e 100644
--- a/admin/tool/lp/amd/build/competencyactions.min.js.map
+++ b/admin/tool/lp/amd/build/competencyactions.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"competencyactions.min.js","sources":["../src/competencyactions.js"],"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 * Handle selection changes and actions on the competency tree.\n *\n * @module tool_lp/competencyactions\n * @copyright 2015 Damyon Wiese \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(['jquery',\n 'core/url',\n 'core/templates',\n 'core/notification',\n 'core/str',\n 'core/ajax',\n 'tool_lp/dragdrop-reorder',\n 'tool_lp/tree',\n 'tool_lp/dialogue',\n 'tool_lp/menubar',\n 'tool_lp/competencypicker',\n 'tool_lp/competency_outcomes',\n 'tool_lp/competencyruleconfig',\n 'core/pending',\n ],\n function(\n $, url, templates, notification, str, ajax, dragdrop, Ariatree, Dialogue, menubar, Picker, Outcomes, RuleConfig, Pending\n ) {\n\n // Private variables and functions.\n /** @var {Object} treeModel - This is an object representing the nodes in the tree. */\n var treeModel = null;\n /** @var {Node} moveSource - The start of a drag operation */\n var moveSource = null;\n /** @var {Node} moveTarget - The end of a drag operation */\n var moveTarget = null;\n /** @var {Number} pageContextId The page context ID. */\n var pageContextId;\n /** @var {Object} Picker instance. */\n var pickerInstance;\n /** @var {Object} Rule config instance. */\n var ruleConfigInstance;\n /** @var {Object} The competency we're picking a relation to. */\n var relatedTarget;\n /** @var {Object} Taxonomy constants indexed per level. */\n var taxonomiesConstants;\n /** @var {Array} The rules modules. Values are object containing type, namd and amd. */\n var rulesModules;\n /** @var {Number} the selected competency ID. */\n var selectedCompetencyId = null;\n\n /**\n * Respond to choosing the \"Add\" menu item for the selected node in the tree.\n * @method addHandler\n */\n var addHandler = function() {\n var parent = $('[data-region=\"competencyactions\"]').data('competency');\n\n var params = {\n competencyframeworkid: treeModel.getCompetencyFrameworkId(),\n pagecontextid: pageContextId\n };\n\n if (parent !== null) {\n // We are adding at a sub node.\n params.parentid = parent.id;\n }\n\n var relocate = function() {\n var queryparams = $.param(params);\n window.location = url.relativeUrl('/admin/tool/lp/editcompetency.php?' + queryparams);\n };\n\n if (parent !== null && treeModel.hasRule(parent.id)) {\n str.get_strings([\n {key: 'confirm', component: 'moodle'},\n {key: 'addingcompetencywillresetparentrule', component: 'tool_lp', param: parent.shortname},\n {key: 'yes', component: 'core'},\n {key: 'no', component: 'core'}\n ]).done(function(strings) {\n notification.confirm(\n strings[0],\n strings[1],\n strings[2],\n strings[3],\n relocate\n );\n }).fail(notification.exception);\n } else {\n relocate();\n }\n };\n\n /**\n * A source and destination has been chosen - so time to complete a move.\n * @method doMove\n */\n var doMove = function() {\n var frameworkid = $('[data-region=\"filtercompetencies\"]').data('frameworkid');\n var requests = ajax.call([{\n methodname: 'core_competency_set_parent_competency',\n args: {competencyid: moveSource, parentid: moveTarget}\n }, {\n methodname: 'tool_lp_data_for_competencies_manage_page',\n args: {competencyframeworkid: frameworkid,\n search: $('[data-region=\"filtercompetencies\"] input').val()}\n }]);\n requests[1].done(reloadPage).fail(notification.exception);\n };\n\n /**\n * Confirms a competency move.\n *\n * @method confirmMove\n */\n var confirmMove = function() {\n moveTarget = typeof moveTarget === \"undefined\" ? 0 : moveTarget;\n if (moveTarget == moveSource) {\n // No move to do.\n return;\n }\n\n var targetComp = treeModel.getCompetency(moveTarget) || {},\n sourceComp = treeModel.getCompetency(moveSource) || {},\n confirmMessage = 'movecompetencywillresetrules',\n showConfirm = false;\n\n // We shouldn't be moving the competency to the same parent.\n if (sourceComp.parentid == moveTarget) {\n return;\n }\n\n // If we are moving to a child of self.\n if (targetComp.path && targetComp.path.indexOf('/' + sourceComp.id + '/') >= 0) {\n confirmMessage = 'movecompetencytochildofselfwillresetrules';\n\n // Show a confirmation if self has rules, as they'll disappear.\n showConfirm = showConfirm || treeModel.hasRule(sourceComp.id);\n }\n\n // Show a confirmation if the current parent, or the destination have rules.\n showConfirm = showConfirm || (treeModel.hasRule(targetComp.id) || treeModel.hasRule(sourceComp.parentid));\n\n // Show confirm, and/or do the things.\n if (showConfirm) {\n str.get_strings([\n {key: 'confirm', component: 'moodle'},\n {key: confirmMessage, component: 'tool_lp'},\n {key: 'yes', component: 'moodle'},\n {key: 'no', component: 'moodle'}\n ]).done(function(strings) {\n notification.confirm(\n strings[0], // Confirm.\n strings[1], // Delete competency X?\n strings[2], // Delete.\n strings[3], // Cancel.\n doMove\n );\n }).fail(notification.exception);\n\n } else {\n doMove();\n }\n };\n\n /**\n * A move competency popup was opened - initialise the aria tree in it.\n * @method initMovePopup\n * @param {dialogue} popup The tool_lp/dialogue that was created.\n */\n var initMovePopup = function(popup) {\n var body = $(popup.getContent());\n var treeRoot = body.find('[data-enhance=movetree]');\n var tree = new Ariatree(treeRoot, false);\n tree.on('selectionchanged', function(evt, params) {\n var target = params.selected;\n moveTarget = $(target).data('id');\n });\n treeRoot.show();\n\n body.on('click', '[data-action=\"move\"]', function() {\n popup.close();\n confirmMove();\n });\n body.on('click', '[data-action=\"cancel\"]', function() {\n popup.close();\n });\n };\n\n /**\n * Turn a flat list of competencies into a tree structure (recursive).\n * @method addCompetencyChildren\n * @param {Object} parent The current parent node in the tree\n * @param {Object[]} competencies The flat list of competencies\n */\n var addCompetencyChildren = function(parent, competencies) {\n var i;\n\n for (i = 0; i < competencies.length; i++) {\n if (competencies[i].parentid == parent.id) {\n parent.haschildren = true;\n competencies[i].children = [];\n competencies[i].haschildren = false;\n parent.children[parent.children.length] = competencies[i];\n addCompetencyChildren(competencies[i], competencies);\n }\n }\n };\n\n /**\n * A node was chosen and \"Move\" was selected from the menu. Open a popup to select the target.\n * @param {Event} e\n * @method moveHandler\n */\n var moveHandler = function(e) {\n e.preventDefault();\n var competency = $('[data-region=\"competencyactions\"]').data('competency');\n\n // Remember what we are moving.\n moveSource = competency.id;\n\n // Load data for the template.\n var requests = ajax.call([\n {\n methodname: 'core_competency_search_competencies',\n args: {\n competencyframeworkid: competency.competencyframeworkid,\n searchtext: ''\n }\n }, {\n methodname: 'core_competency_read_competency_framework',\n args: {\n id: competency.competencyframeworkid\n }\n }\n ]);\n\n // When all data has arrived, continue.\n $.when.apply(null, requests).done(function(competencies, framework) {\n\n // Expand the list of competencies into a tree.\n var i;\n var competenciestree = [];\n for (i = 0; i < competencies.length; i++) {\n var onecompetency = competencies[i];\n if (onecompetency.parentid == \"0\") {\n onecompetency.children = [];\n onecompetency.haschildren = 0;\n competenciestree[competenciestree.length] = onecompetency;\n addCompetencyChildren(onecompetency, competencies);\n }\n }\n\n str.get_strings([\n {key: 'movecompetency', component: 'tool_lp', param: competency.shortname},\n {key: 'move', component: 'tool_lp'},\n {key: 'cancel', component: 'moodle'}\n ]).done(function(strings) {\n\n var context = {\n framework: framework,\n competencies: competenciestree\n };\n\n templates.render('tool_lp/competencies_move_tree', context)\n .done(function(tree) {\n new Dialogue(\n strings[0], // Move competency x.\n tree, // The move tree.\n initMovePopup\n );\n\n }).fail(notification.exception);\n\n }).fail(notification.exception);\n\n }).fail(notification.exception);\n\n };\n\n /**\n * Edit the selected competency.\n * @method editHandler\n */\n var editHandler = function() {\n var competency = $('[data-region=\"competencyactions\"]').data('competency');\n\n var params = {\n competencyframeworkid: treeModel.getCompetencyFrameworkId(),\n id: competency.id,\n parentid: competency.parentid,\n pagecontextid: pageContextId\n };\n\n var queryparams = $.param(params);\n window.location = url.relativeUrl('/admin/tool/lp/editcompetency.php?' + queryparams);\n };\n\n /**\n * Re-render the page with the latest data.\n * @param {Object} context\n * @method reloadPage\n */\n var reloadPage = function(context) {\n templates.render('tool_lp/manage_competencies_page', context)\n .done(function(newhtml, newjs) {\n $('[data-region=\"managecompetencies\"]').replaceWith(newhtml);\n templates.runTemplateJS(newjs);\n })\n .fail(notification.exception);\n };\n\n /**\n * Perform a search and render the page with the new search results.\n * @param {Event} e\n * @method updateSearchHandler\n */\n var updateSearchHandler = function(e) {\n e.preventDefault();\n\n var frameworkid = $('[data-region=\"filtercompetencies\"]').data('frameworkid');\n\n var requests = ajax.call([{\n methodname: 'tool_lp_data_for_competencies_manage_page',\n args: {competencyframeworkid: frameworkid,\n search: $('[data-region=\"filtercompetencies\"] input').val()}\n }]);\n requests[0].done(reloadPage).fail(notification.exception);\n };\n\n /**\n * Move a competency \"up\". This only affects the sort order within the same branch of the tree.\n * @method moveUpHandler\n */\n var moveUpHandler = function() {\n // We are chaining ajax requests here.\n var competency = $('[data-region=\"competencyactions\"]').data('competency');\n var requests = ajax.call([{\n methodname: 'core_competency_move_up_competency',\n args: {id: competency.id}\n }, {\n methodname: 'tool_lp_data_for_competencies_manage_page',\n args: {competencyframeworkid: competency.competencyframeworkid,\n search: $('[data-region=\"filtercompetencies\"] input').val()}\n }]);\n requests[1].done(reloadPage).fail(notification.exception);\n };\n\n /**\n * Move a competency \"down\". This only affects the sort order within the same branch of the tree.\n * @method moveDownHandler\n */\n var moveDownHandler = function() {\n // We are chaining ajax requests here.\n var competency = $('[data-region=\"competencyactions\"]').data('competency');\n var requests = ajax.call([{\n methodname: 'core_competency_move_down_competency',\n args: {id: competency.id}\n }, {\n methodname: 'tool_lp_data_for_competencies_manage_page',\n args: {competencyframeworkid: competency.competencyframeworkid,\n search: $('[data-region=\"filtercompetencies\"] input').val()}\n }]);\n requests[1].done(reloadPage).fail(notification.exception);\n };\n\n /**\n * Open a dialogue to show all the courses using the selected competency.\n * @method seeCoursesHandler\n */\n var seeCoursesHandler = function() {\n var competency = $('[data-region=\"competencyactions\"]').data('competency');\n\n var requests = ajax.call([{\n methodname: 'tool_lp_list_courses_using_competency',\n args: {id: competency.id}\n }]);\n\n requests[0].done(function(courses) {\n var context = {\n courses: courses\n };\n templates.render('tool_lp/linked_courses_summary', context).done(function(html) {\n str.get_string('linkedcourses', 'tool_lp').done(function(linkedcourses) {\n new Dialogue(\n linkedcourses, // Title.\n html, // The linked courses.\n initMovePopup\n );\n }).fail(notification.exception);\n }).fail(notification.exception);\n }).fail(notification.exception);\n };\n\n /**\n * Open a competencies popup to relate competencies.\n *\n * @method relateCompetenciesHandler\n */\n var relateCompetenciesHandler = function() {\n relatedTarget = $('[data-region=\"competencyactions\"]').data('competency');\n\n if (!pickerInstance) {\n pickerInstance = new Picker(pageContextId, relatedTarget.competencyframeworkid);\n pickerInstance.on('save', function(e, data) {\n var pendingPromise = new Pending();\n var compIds = data.competencyIds;\n\n var calls = [];\n $.each(compIds, function(index, value) {\n calls.push({\n methodname: 'core_competency_add_related_competency',\n args: {competencyid: value, relatedcompetencyid: relatedTarget.id}\n });\n });\n\n calls.push({\n methodname: 'tool_lp_data_for_related_competencies_section',\n args: {competencyid: relatedTarget.id}\n });\n\n var promises = ajax.call(calls);\n\n promises[calls.length - 1].then(function(context) {\n return templates.render('tool_lp/related_competencies', context);\n }).then(function(html, js) {\n $('[data-region=\"relatedcompetencies\"]').replaceWith(html);\n templates.runTemplateJS(js);\n updatedRelatedCompetencies();\n return;\n })\n .then(pendingPromise.resolve)\n .catch(notification.exception);\n });\n }\n\n pickerInstance.setDisallowedCompetencyIDs([relatedTarget.id]);\n pickerInstance.display();\n };\n\n var ruleConfigHandler = function(e) {\n e.preventDefault();\n relatedTarget = $('[data-region=\"competencyactions\"]').data('competency');\n ruleConfigInstance.setTargetCompetencyId(relatedTarget.id);\n ruleConfigInstance.display();\n };\n\n var ruleConfigSaveHandler = function(e, config) {\n var update = {\n id: relatedTarget.id,\n shortname: relatedTarget.shortname,\n idnumber: relatedTarget.idnumber,\n description: relatedTarget.description,\n descriptionformat: relatedTarget.descriptionformat,\n ruletype: config.ruletype,\n ruleoutcome: config.ruleoutcome,\n ruleconfig: config.ruleconfig\n };\n var promise = ajax.call([{\n methodname: 'core_competency_update_competency',\n args: {competency: update}\n }]);\n promise[0].then(function(result) {\n if (result) {\n relatedTarget.ruletype = config.ruletype;\n relatedTarget.ruleoutcome = config.ruleoutcome;\n relatedTarget.ruleconfig = config.ruleconfig;\n renderCompetencySummary(relatedTarget);\n }\n return;\n }).catch(notification.exception);\n };\n\n /**\n * Delete a competency.\n * @method doDelete\n */\n var doDelete = function() {\n // We are chaining ajax requests here.\n var competency = $('[data-region=\"competencyactions\"]').data('competency');\n var requests = ajax.call([{\n methodname: 'core_competency_delete_competency',\n args: {id: competency.id}\n }, {\n methodname: 'tool_lp_data_for_competencies_manage_page',\n args: {competencyframeworkid: competency.competencyframeworkid,\n search: $('[data-region=\"filtercompetencies\"] input').val()}\n }]);\n requests[0].done(function(success) {\n if (success === false) {\n str.get_strings([\n {key: 'competencycannotbedeleted', component: 'tool_lp', param: competency.shortname},\n {key: 'cancel', component: 'moodle'}\n ]).done(function(strings) {\n notification.alert(\n null,\n strings[0]\n );\n }).fail(notification.exception);\n }\n }).fail(notification.exception);\n requests[1].done(reloadPage).fail(notification.exception);\n };\n\n /**\n * Show a confirm dialogue before deleting a competency.\n * @method deleteCompetencyHandler\n */\n var deleteCompetencyHandler = function() {\n var competency = $('[data-region=\"competencyactions\"]').data('competency'),\n confirmMessage = 'deletecompetency';\n\n if (treeModel.hasRule(competency.parentid)) {\n confirmMessage = 'deletecompetencyparenthasrule';\n }\n\n str.get_strings([\n {key: 'confirm', component: 'moodle'},\n {key: confirmMessage, component: 'tool_lp', param: competency.shortname},\n {key: 'delete', component: 'moodle'},\n {key: 'cancel', component: 'moodle'}\n ]).done(function(strings) {\n notification.confirm(\n strings[0], // Confirm.\n strings[1], // Delete competency X?\n strings[2], // Delete.\n strings[3], // Cancel.\n doDelete\n );\n }).fail(notification.exception);\n };\n\n /**\n * HTML5 implementation of drag/drop (there is an accesible alternative in the menus).\n * @method dragStart\n * @param {Event} e\n */\n var dragStart = function(e) {\n e.originalEvent.dataTransfer.setData('text', $(e.target).parent().data('id'));\n };\n\n /**\n * HTML5 implementation of drag/drop (there is an accesible alternative in the menus).\n * @method allowDrop\n * @param {Event} e\n */\n var allowDrop = function(e) {\n e.originalEvent.dataTransfer.dropEffect = 'move';\n e.preventDefault();\n };\n\n /**\n * HTML5 implementation of drag/drop (there is an accesible alternative in the menus).\n * @method dragEnter\n * @param {Event} e\n */\n var dragEnter = function(e) {\n e.preventDefault();\n $(this).addClass('currentdragtarget');\n };\n\n /**\n * HTML5 implementation of drag/drop (there is an accesible alternative in the menus).\n * @method dragLeave\n * @param {Event} e\n */\n var dragLeave = function(e) {\n e.preventDefault();\n $(this).removeClass('currentdragtarget');\n };\n\n /**\n * HTML5 implementation of drag/drop (there is an accesible alternative in the menus).\n * @method dropOver\n * @param {Event} e\n */\n var dropOver = function(e) {\n e.preventDefault();\n moveSource = e.originalEvent.dataTransfer.getData('text');\n moveTarget = $(e.target).parent().data('id');\n $(this).removeClass('currentdragtarget');\n\n confirmMove();\n };\n\n /**\n * Deletes a related competency without confirmation.\n *\n * @param {Event} e The event that triggered the action.\n * @method deleteRelatedHandler\n */\n var deleteRelatedHandler = function(e) {\n e.preventDefault();\n\n var relatedid = this.id.substr(11);\n var competency = $('[data-region=\"competencyactions\"]').data('competency');\n var removeRelated = ajax.call([\n {methodname: 'core_competency_remove_related_competency',\n args: {relatedcompetencyid: relatedid, competencyid: competency.id}},\n {methodname: 'tool_lp_data_for_related_competencies_section',\n args: {competencyid: competency.id}}\n ]);\n\n removeRelated[1].done(function(context) {\n templates.render('tool_lp/related_competencies', context).done(function(html) {\n $('[data-region=\"relatedcompetencies\"]').replaceWith(html);\n updatedRelatedCompetencies();\n }).fail(notification.exception);\n }).fail(notification.exception);\n };\n\n /**\n * Updates the competencies list (with relations) and add listeners.\n *\n * @method updatedRelatedCompetencies\n */\n var updatedRelatedCompetencies = function() {\n\n // Listeners to newly loaded related competencies.\n $('[data-action=\"deleterelation\"]').on('click', deleteRelatedHandler);\n\n };\n\n /**\n * Log the competency viewed event.\n *\n * @param {Object} competency The competency.\n * @method triggerCompetencyViewedEvent\n */\n var triggerCompetencyViewedEvent = function(competency) {\n if (competency.id !== selectedCompetencyId) {\n // Set the selected competency id.\n selectedCompetencyId = competency.id;\n ajax.call([{\n methodname: 'core_competency_competency_viewed',\n args: {id: competency.id}\n }]);\n }\n };\n\n /**\n * Return the taxonomy constant for a level.\n *\n * @param {Number} level The level.\n * @return {String}\n * @function getTaxonomyAtLevel\n */\n var getTaxonomyAtLevel = function(level) {\n var constant = taxonomiesConstants[level];\n if (!constant) {\n constant = 'competency';\n }\n return constant;\n };\n\n /**\n * Render the competency summary.\n *\n * @param {Object} competency The competency.\n */\n var renderCompetencySummary = function(competency) {\n var promise = $.Deferred().resolve().promise(),\n context = {};\n\n context.competency = competency;\n context.showdeleterelatedaction = true;\n context.showrelatedcompetencies = true;\n context.showrule = false;\n context.pluginbaseurl = url.relativeUrl('/admin/tool/lp');\n\n if (competency.ruleoutcome != Outcomes.NONE) {\n // Get the outcome and rule name.\n promise = Outcomes.getString(competency.ruleoutcome).then(function(str) {\n var name;\n $.each(rulesModules, function(index, modInfo) {\n if (modInfo.type == competency.ruletype) {\n name = modInfo.name;\n }\n });\n return [str, name];\n });\n }\n\n promise.then(function(strs) {\n if (typeof strs !== 'undefined') {\n context.showrule = true;\n context.rule = {\n outcome: strs[0],\n type: strs[1]\n };\n }\n return context;\n }).then(function(context) {\n return templates.render('tool_lp/competency_summary', context);\n }).then(function(html) {\n $('[data-region=\"competencyinfo\"]').html(html);\n $('[data-action=\"deleterelation\"]').on('click', deleteRelatedHandler);\n return templates.render('tool_lp/loading', {});\n }).then(function(html, js) {\n templates.replaceNodeContents('[data-region=\"relatedcompetencies\"]', html, js);\n return ajax.call([{\n methodname: 'tool_lp_data_for_related_competencies_section',\n args: {competencyid: competency.id}\n }])[0];\n }).then(function(context) {\n return templates.render('tool_lp/related_competencies', context);\n }).then(function(html, js) {\n $('[data-region=\"relatedcompetencies\"]').replaceWith(html);\n templates.runTemplateJS(js);\n updatedRelatedCompetencies();\n return;\n }).catch(notification.exception);\n };\n\n /**\n * Return the string \"Add \".\n *\n * @param {Number} level The level.\n * @return {String}\n * @function strAddTaxonomy\n */\n var strAddTaxonomy = function(level) {\n return str.get_string('taxonomy_add_' + getTaxonomyAtLevel(level), 'tool_lp');\n };\n\n /**\n * Return the string \"Selected \".\n *\n * @param {Number} level The level.\n * @return {String}\n * @function strSelectedTaxonomy\n */\n var strSelectedTaxonomy = function(level) {\n return str.get_string('taxonomy_selected_' + getTaxonomyAtLevel(level), 'tool_lp');\n };\n\n /**\n * Handler when a node in the aria tree is selected.\n * @method selectionChanged\n * @param {Event} evt The event that triggered the selection change.\n * @param {Object} params The parameters for the event. Contains a list of selected nodes.\n * @return {Boolean}\n */\n var selectionChanged = function(evt, params) {\n var node = params.selected,\n id = $(node).data('id'),\n btn = $('[data-region=\"competencyactions\"] [data-action=\"add\"]'),\n actionMenu = $('[data-region=\"competencyactionsmenu\"]'),\n selectedTitle = $('[data-region=\"selected-competency\"]'),\n level = 0,\n sublevel = 1;\n\n menubar.closeAll();\n\n if (typeof id === \"undefined\") {\n // Assume this is the root of the tree.\n // Here we are only getting the text from the top of the tree, to do it we clone the tree,\n // remove all children and then call text on the result.\n $('[data-region=\"competencyinfo\"]').html(node.clone().children().remove().end().text());\n $('[data-region=\"competencyactions\"]').data('competency', null);\n actionMenu.hide();\n\n } else {\n var competency = treeModel.getCompetency(id);\n\n level = treeModel.getCompetencyLevel(id);\n sublevel = level + 1;\n\n actionMenu.show();\n $('[data-region=\"competencyactions\"]').data('competency', competency);\n renderCompetencySummary(competency);\n // Log Competency viewed event.\n triggerCompetencyViewedEvent(competency);\n }\n strSelectedTaxonomy(level).then(function(str) {\n selectedTitle.text(str);\n return;\n }).catch(notification.exception);\n\n strAddTaxonomy(sublevel).then(function(str) {\n btn.show()\n .find('[data-region=\"term\"]')\n .text(str);\n return;\n }).catch(notification.exception);\n\n // We handled this event so consume it.\n evt.preventDefault();\n return false;\n };\n\n /**\n * Return the string \"Selected \".\n *\n * @function parseTaxonomies\n * @param {String} taxonomiesstr Comma separated list of taxonomies.\n * @return {Array} of level => taxonomystr\n */\n var parseTaxonomies = function(taxonomiesstr) {\n var all = taxonomiesstr.split(',');\n all.unshift(\"\");\n delete all[0];\n\n // Note we don't need to fill holes, because other functions check for empty anyway.\n return all;\n };\n\n return {\n /**\n * Initialise this page (attach event handlers etc).\n *\n * @method init\n * @param {Object} model The tree model provides some useful functions for loading and searching competencies.\n * @param {Number} pagectxid The page context ID.\n * @param {Object} taxonomies Constants indexed by level.\n * @param {Object} rulesMods The modules of the rules.\n */\n init: function(model, pagectxid, taxonomies, rulesMods) {\n treeModel = model;\n pageContextId = pagectxid;\n taxonomiesConstants = parseTaxonomies(taxonomies);\n rulesModules = rulesMods;\n\n $('[data-region=\"competencyactions\"] [data-action=\"add\"]').on('click', addHandler);\n\n menubar.enhance('.competencyactionsmenu', {\n '[data-action=\"edit\"]': editHandler,\n '[data-action=\"delete\"]': deleteCompetencyHandler,\n '[data-action=\"move\"]': moveHandler,\n '[data-action=\"moveup\"]': moveUpHandler,\n '[data-action=\"movedown\"]': moveDownHandler,\n '[data-action=\"linkedcourses\"]': seeCoursesHandler,\n '[data-action=\"relatedcompetencies\"]': relateCompetenciesHandler.bind(this),\n '[data-action=\"competencyrules\"]': ruleConfigHandler.bind(this)\n });\n $('[data-region=\"competencyactionsmenu\"]').hide();\n $('[data-region=\"competencyactions\"] [data-action=\"add\"]').hide();\n\n $('[data-region=\"filtercompetencies\"]').on('submit', updateSearchHandler);\n // Simple html5 drag drop because we already added an accessible alternative.\n var top = $('[data-region=\"managecompetencies\"] [data-enhance=\"tree\"]');\n top.on('dragstart', 'li>span', dragStart)\n .on('dragover', 'li>span', allowDrop)\n .on('dragenter', 'li>span', dragEnter)\n .on('dragleave', 'li>span', dragLeave)\n .on('drop', 'li>span', dropOver);\n\n model.on('selectionchanged', selectionChanged);\n\n // Prepare the configuration tool.\n ruleConfigInstance = new RuleConfig(treeModel, rulesModules);\n ruleConfigInstance.on('save', ruleConfigSaveHandler.bind(this));\n }\n };\n});\n"],"names":["define","$","url","templates","notification","str","ajax","dragdrop","Ariatree","Dialogue","menubar","Picker","Outcomes","RuleConfig","Pending","pageContextId","pickerInstance","ruleConfigInstance","relatedTarget","taxonomiesConstants","rulesModules","treeModel","moveSource","moveTarget","selectedCompetencyId","addHandler","parent","data","params","competencyframeworkid","getCompetencyFrameworkId","pagecontextid","parentid","id","relocate","queryparams","param","window","location","relativeUrl","hasRule","get_strings","key","component","shortname","done","strings","confirm","fail","exception","doMove","frameworkid","call","methodname","args","competencyid","search","val","reloadPage","confirmMove","targetComp","getCompetency","sourceComp","confirmMessage","showConfirm","path","indexOf","initMovePopup","popup","body","getContent","treeRoot","find","on","evt","target","selected","show","close","addCompetencyChildren","competencies","i","length","haschildren","children","moveHandler","e","preventDefault","competency","requests","searchtext","when","apply","framework","competenciestree","onecompetency","context","render","tree","editHandler","newhtml","newjs","replaceWith","runTemplateJS","updateSearchHandler","moveUpHandler","moveDownHandler","seeCoursesHandler","courses","html","get_string","linkedcourses","relateCompetenciesHandler","pendingPromise","compIds","competencyIds","calls","each","index","value","push","relatedcompetencyid","then","js","updatedRelatedCompetencies","resolve","catch","setDisallowedCompetencyIDs","display","ruleConfigHandler","setTargetCompetencyId","ruleConfigSaveHandler","config","update","idnumber","description","descriptionformat","ruletype","ruleoutcome","ruleconfig","result","renderCompetencySummary","doDelete","success","alert","deleteCompetencyHandler","dragStart","originalEvent","dataTransfer","setData","allowDrop","dropEffect","dragEnter","this","addClass","dragLeave","removeClass","dropOver","getData","deleteRelatedHandler","relatedid","substr","getTaxonomyAtLevel","level","constant","promise","Deferred","showdeleterelatedaction","showrelatedcompetencies","showrule","pluginbaseurl","NONE","getString","name","modInfo","type","strs","rule","outcome","replaceNodeContents","selectionChanged","node","btn","actionMenu","selectedTitle","sublevel","closeAll","clone","remove","end","text","hide","getCompetencyLevel","triggerCompetencyViewedEvent","strSelectedTaxonomy","strAddTaxonomy","init","model","pagectxid","taxonomies","rulesMods","all","split","unshift","enhance","bind"],"mappings":"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"}
\ No newline at end of file
+{"version":3,"file":"competencyactions.min.js","sources":["../src/competencyactions.js"],"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 * Handle selection changes and actions on the competency tree.\n *\n * @module tool_lp/competencyactions\n * @copyright 2015 Damyon Wiese \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(['jquery',\n 'core/url',\n 'core/templates',\n 'core/notification',\n 'core/str',\n 'core/ajax',\n 'tool_lp/dragdrop-reorder',\n 'tool_lp/tree',\n 'tool_lp/dialogue',\n 'tool_lp/menubar',\n 'tool_lp/competencypicker',\n 'tool_lp/competency_outcomes',\n 'tool_lp/competencyruleconfig',\n 'core/pending',\n ],\n function(\n $, url, templates, notification, str, ajax, dragdrop, Ariatree, Dialogue, menubar, Picker, Outcomes, RuleConfig, Pending\n ) {\n\n // Private variables and functions.\n /** @var {Object} treeModel - This is an object representing the nodes in the tree. */\n var treeModel = null;\n /** @var {Node} moveSource - The start of a drag operation */\n var moveSource = null;\n /** @var {Node} moveTarget - The end of a drag operation */\n var moveTarget = null;\n /** @var {Number} pageContextId The page context ID. */\n var pageContextId;\n /** @var {Object} Picker instance. */\n var pickerInstance;\n /** @var {Object} Rule config instance. */\n var ruleConfigInstance;\n /** @var {Object} The competency we're picking a relation to. */\n var relatedTarget;\n /** @var {Object} Taxonomy constants indexed per level. */\n var taxonomiesConstants;\n /** @var {Array} The rules modules. Values are object containing type, namd and amd. */\n var rulesModules;\n /** @var {Number} the selected competency ID. */\n var selectedCompetencyId = null;\n\n /**\n * Respond to choosing the \"Add\" menu item for the selected node in the tree.\n * @method addHandler\n */\n var addHandler = function() {\n var parent = $('[data-region=\"competencyactions\"]').data('competency');\n\n var params = {\n competencyframeworkid: treeModel.getCompetencyFrameworkId(),\n pagecontextid: pageContextId\n };\n\n if (parent !== null) {\n // We are adding at a sub node.\n params.parentid = parent.id;\n }\n\n var relocate = function() {\n var queryparams = $.param(params);\n window.location = url.relativeUrl('/admin/tool/lp/editcompetency.php?' + queryparams);\n };\n\n if (parent !== null && treeModel.hasRule(parent.id)) {\n str.get_strings([\n {key: 'confirm', component: 'moodle'},\n {key: 'addingcompetencywillresetparentrule', component: 'tool_lp', param: parent.shortname},\n {key: 'yes', component: 'core'},\n {key: 'no', component: 'core'}\n ]).done(function(strings) {\n notification.confirm(\n strings[0],\n strings[1],\n strings[2],\n strings[3],\n relocate\n );\n }).fail(notification.exception);\n } else {\n relocate();\n }\n };\n\n /**\n * A source and destination has been chosen - so time to complete a move.\n * @method doMove\n */\n var doMove = function() {\n var frameworkid = $('[data-region=\"filtercompetencies\"]').data('frameworkid');\n var requests = ajax.call([{\n methodname: 'core_competency_set_parent_competency',\n args: {competencyid: moveSource, parentid: moveTarget}\n }, {\n methodname: 'tool_lp_data_for_competencies_manage_page',\n args: {competencyframeworkid: frameworkid,\n search: $('[data-region=\"filtercompetencies\"] input').val()}\n }]);\n requests[1].done(reloadPage).fail(notification.exception);\n };\n\n /**\n * Confirms a competency move.\n *\n * @method confirmMove\n */\n var confirmMove = function() {\n moveTarget = typeof moveTarget === \"undefined\" ? 0 : moveTarget;\n if (moveTarget == moveSource) {\n // No move to do.\n return;\n }\n\n var targetComp = treeModel.getCompetency(moveTarget) || {},\n sourceComp = treeModel.getCompetency(moveSource) || {},\n confirmMessage = 'movecompetencywillresetrules',\n showConfirm = false;\n\n // We shouldn't be moving the competency to the same parent.\n if (sourceComp.parentid == moveTarget) {\n return;\n }\n\n // If we are moving to a child of self.\n if (targetComp.path && targetComp.path.indexOf('/' + sourceComp.id + '/') >= 0) {\n confirmMessage = 'movecompetencytochildofselfwillresetrules';\n\n // Show a confirmation if self has rules, as they'll disappear.\n showConfirm = showConfirm || treeModel.hasRule(sourceComp.id);\n }\n\n // Show a confirmation if the current parent, or the destination have rules.\n showConfirm = showConfirm || (treeModel.hasRule(targetComp.id) || treeModel.hasRule(sourceComp.parentid));\n\n // Show confirm, and/or do the things.\n if (showConfirm) {\n str.get_strings([\n {key: 'confirm', component: 'moodle'},\n {key: confirmMessage, component: 'tool_lp'},\n {key: 'yes', component: 'moodle'},\n {key: 'no', component: 'moodle'}\n ]).done(function(strings) {\n notification.confirm(\n strings[0], // Confirm.\n strings[1], // Delete competency X?\n strings[2], // Delete.\n strings[3], // Cancel.\n doMove\n );\n }).fail(notification.exception);\n\n } else {\n doMove();\n }\n };\n\n /**\n * A move competency popup was opened - initialise the aria tree in it.\n * @method initMovePopup\n * @param {dialogue} popup The tool_lp/dialogue that was created.\n */\n var initMovePopup = function(popup) {\n var body = $(popup.getContent());\n var treeRoot = body.find('[data-enhance=movetree]');\n var tree = new Ariatree(treeRoot, false);\n tree.on('selectionchanged', function(evt, params) {\n var target = params.selected;\n moveTarget = $(target).data('id');\n });\n treeRoot.show();\n\n body.on('click', '[data-action=\"move\"]', function() {\n popup.close();\n confirmMove();\n });\n body.on('click', '[data-action=\"cancel\"]', function() {\n popup.close();\n });\n };\n\n /**\n * Turn a flat list of competencies into a tree structure (recursive).\n * @method addCompetencyChildren\n * @param {Object} parent The current parent node in the tree\n * @param {Object[]} competencies The flat list of competencies\n */\n var addCompetencyChildren = function(parent, competencies) {\n var i;\n\n for (i = 0; i < competencies.length; i++) {\n if (competencies[i].parentid == parent.id) {\n parent.haschildren = true;\n competencies[i].children = [];\n competencies[i].haschildren = false;\n parent.children[parent.children.length] = competencies[i];\n addCompetencyChildren(competencies[i], competencies);\n }\n }\n };\n\n /**\n * A node was chosen and \"Move\" was selected from the menu. Open a popup to select the target.\n * @param {Event} e\n * @method moveHandler\n */\n var moveHandler = function(e) {\n e.preventDefault();\n var competency = $('[data-region=\"competencyactions\"]').data('competency');\n\n // Remember what we are moving.\n moveSource = competency.id;\n\n // Load data for the template.\n var requests = ajax.call([\n {\n methodname: 'core_competency_search_competencies',\n args: {\n competencyframeworkid: competency.competencyframeworkid,\n searchtext: ''\n }\n }, {\n methodname: 'core_competency_read_competency_framework',\n args: {\n id: competency.competencyframeworkid\n }\n }\n ]);\n\n // When all data has arrived, continue.\n $.when.apply(null, requests).done(function(competencies, framework) {\n\n // Expand the list of competencies into a tree.\n var i;\n var competenciestree = [];\n for (i = 0; i < competencies.length; i++) {\n var onecompetency = competencies[i];\n if (onecompetency.parentid == \"0\") {\n onecompetency.children = [];\n onecompetency.haschildren = 0;\n competenciestree[competenciestree.length] = onecompetency;\n addCompetencyChildren(onecompetency, competencies);\n }\n }\n\n str.get_strings([\n {key: 'movecompetency', component: 'tool_lp', param: competency.shortname},\n {key: 'move', component: 'tool_lp'},\n {key: 'cancel', component: 'moodle'}\n ]).done(function(strings) {\n\n var context = {\n framework: framework,\n competencies: competenciestree\n };\n\n templates.render('tool_lp/competencies_move_tree', context)\n .done(function(tree) {\n new Dialogue(\n strings[0], // Move competency x.\n tree, // The move tree.\n initMovePopup\n );\n\n }).fail(notification.exception);\n\n }).fail(notification.exception);\n\n }).fail(notification.exception);\n\n };\n\n /**\n * Edit the selected competency.\n * @method editHandler\n */\n var editHandler = function() {\n var competency = $('[data-region=\"competencyactions\"]').data('competency');\n\n var params = {\n competencyframeworkid: treeModel.getCompetencyFrameworkId(),\n id: competency.id,\n parentid: competency.parentid,\n pagecontextid: pageContextId\n };\n\n var queryparams = $.param(params);\n window.location = url.relativeUrl('/admin/tool/lp/editcompetency.php?' + queryparams);\n };\n\n /**\n * Re-render the page with the latest data.\n * @param {Object} context\n * @method reloadPage\n */\n var reloadPage = function(context) {\n templates.render('tool_lp/manage_competencies_page', context)\n .done(function(newhtml, newjs) {\n $('[data-region=\"managecompetencies\"]').replaceWith(newhtml);\n templates.runTemplateJS(newjs);\n })\n .fail(notification.exception);\n };\n\n /**\n * Perform a search and render the page with the new search results.\n * @param {Event} e\n * @method updateSearchHandler\n */\n var updateSearchHandler = function(e) {\n e.preventDefault();\n\n var frameworkid = $('[data-region=\"filtercompetencies\"]').data('frameworkid');\n\n var requests = ajax.call([{\n methodname: 'tool_lp_data_for_competencies_manage_page',\n args: {competencyframeworkid: frameworkid,\n search: $('[data-region=\"filtercompetencies\"] input').val()}\n }]);\n requests[0].done(reloadPage).fail(notification.exception);\n };\n\n /**\n * Move a competency \"up\". This only affects the sort order within the same branch of the tree.\n * @method moveUpHandler\n */\n var moveUpHandler = function() {\n // We are chaining ajax requests here.\n var competency = $('[data-region=\"competencyactions\"]').data('competency');\n var requests = ajax.call([{\n methodname: 'core_competency_move_up_competency',\n args: {id: competency.id}\n }, {\n methodname: 'tool_lp_data_for_competencies_manage_page',\n args: {competencyframeworkid: competency.competencyframeworkid,\n search: $('[data-region=\"filtercompetencies\"] input').val()}\n }]);\n requests[1].done(reloadPage).fail(notification.exception);\n };\n\n /**\n * Move a competency \"down\". This only affects the sort order within the same branch of the tree.\n * @method moveDownHandler\n */\n var moveDownHandler = function() {\n // We are chaining ajax requests here.\n var competency = $('[data-region=\"competencyactions\"]').data('competency');\n var requests = ajax.call([{\n methodname: 'core_competency_move_down_competency',\n args: {id: competency.id}\n }, {\n methodname: 'tool_lp_data_for_competencies_manage_page',\n args: {competencyframeworkid: competency.competencyframeworkid,\n search: $('[data-region=\"filtercompetencies\"] input').val()}\n }]);\n requests[1].done(reloadPage).fail(notification.exception);\n };\n\n /**\n * Open a dialogue to show all the courses using the selected competency.\n * @method seeCoursesHandler\n */\n var seeCoursesHandler = function() {\n var competency = $('[data-region=\"competencyactions\"]').data('competency');\n\n var requests = ajax.call([{\n methodname: 'tool_lp_list_courses_using_competency',\n args: {id: competency.id}\n }]);\n\n requests[0].done(function(courses) {\n var context = {\n courses: courses\n };\n templates.render('tool_lp/linked_courses_summary', context).done(function(html) {\n str.get_string('linkedcourses', 'tool_lp').done(function(linkedcourses) {\n new Dialogue(\n linkedcourses, // Title.\n html, // The linked courses.\n initMovePopup\n );\n }).fail(notification.exception);\n }).fail(notification.exception);\n }).fail(notification.exception);\n };\n\n /**\n * Open a competencies popup to relate competencies.\n *\n * @method relateCompetenciesHandler\n */\n var relateCompetenciesHandler = function() {\n relatedTarget = $('[data-region=\"competencyactions\"]').data('competency');\n\n if (!pickerInstance) {\n pickerInstance = new Picker(pageContextId, relatedTarget.competencyframeworkid);\n pickerInstance.on('save', function(e, data) {\n var pendingPromise = new Pending();\n var compIds = data.competencyIds;\n\n var calls = [];\n $.each(compIds, function(index, value) {\n calls.push({\n methodname: 'core_competency_add_related_competency',\n args: {competencyid: value, relatedcompetencyid: relatedTarget.id}\n });\n });\n\n calls.push({\n methodname: 'tool_lp_data_for_related_competencies_section',\n args: {competencyid: relatedTarget.id}\n });\n\n var promises = ajax.call(calls);\n\n promises[calls.length - 1].then(function(context) {\n return templates.render('tool_lp/related_competencies', context);\n }).then(function(html, js) {\n $('[data-region=\"relatedcompetencies\"]').replaceWith(html);\n templates.runTemplateJS(js);\n updatedRelatedCompetencies();\n return;\n })\n .then(pendingPromise.resolve)\n .catch(notification.exception);\n });\n }\n\n pickerInstance.setDisallowedCompetencyIDs([relatedTarget.id]);\n pickerInstance.display();\n };\n\n var ruleConfigHandler = function(e) {\n e.preventDefault();\n relatedTarget = $('[data-region=\"competencyactions\"]').data('competency');\n ruleConfigInstance.setTargetCompetencyId(relatedTarget.id);\n ruleConfigInstance.display();\n };\n\n var ruleConfigSaveHandler = function(e, config) {\n var update = {\n id: relatedTarget.id,\n shortname: relatedTarget.shortname,\n idnumber: relatedTarget.idnumber,\n description: relatedTarget.description,\n descriptionformat: relatedTarget.descriptionformat,\n ruletype: config.ruletype,\n ruleoutcome: config.ruleoutcome,\n ruleconfig: config.ruleconfig\n };\n var promise = ajax.call([{\n methodname: 'core_competency_update_competency',\n args: {competency: update}\n }]);\n promise[0].then(function(result) {\n if (result) {\n relatedTarget.ruletype = config.ruletype;\n relatedTarget.ruleoutcome = config.ruleoutcome;\n relatedTarget.ruleconfig = config.ruleconfig;\n renderCompetencySummary(relatedTarget);\n }\n return;\n }).catch(notification.exception);\n };\n\n /**\n * Delete a competency.\n * @method doDelete\n */\n var doDelete = function() {\n // We are chaining ajax requests here.\n var competency = $('[data-region=\"competencyactions\"]').data('competency');\n var requests = ajax.call([{\n methodname: 'core_competency_delete_competency',\n args: {id: competency.id}\n }, {\n methodname: 'tool_lp_data_for_competencies_manage_page',\n args: {competencyframeworkid: competency.competencyframeworkid,\n search: $('[data-region=\"filtercompetencies\"] input').val()}\n }]);\n requests[0].done(function(success) {\n if (success === false) {\n str.get_strings([\n {key: 'competencycannotbedeleted', component: 'tool_lp', param: competency.shortname},\n {key: 'cancel', component: 'moodle'}\n ]).done(function(strings) {\n notification.alert(\n null,\n strings[0]\n );\n }).fail(notification.exception);\n }\n }).fail(notification.exception);\n requests[1].done(reloadPage).fail(notification.exception);\n };\n\n /**\n * Show a confirm dialogue before deleting a competency.\n * @method deleteCompetencyHandler\n */\n var deleteCompetencyHandler = function() {\n var competency = $('[data-region=\"competencyactions\"]').data('competency'),\n confirmMessage = 'deletecompetency';\n\n if (treeModel.hasRule(competency.parentid)) {\n confirmMessage = 'deletecompetencyparenthasrule';\n }\n\n str.get_strings([\n {key: 'confirm', component: 'moodle'},\n {key: confirmMessage, component: 'tool_lp', param: competency.shortname},\n {key: 'delete', component: 'moodle'},\n {key: 'cancel', component: 'moodle'}\n ]).done(function(strings) {\n notification.confirm(\n strings[0], // Confirm.\n strings[1], // Delete competency X?\n strings[2], // Delete.\n strings[3], // Cancel.\n doDelete\n );\n }).fail(notification.exception);\n };\n\n /**\n * HTML5 implementation of drag/drop (there is an accesible alternative in the menus).\n * @method dragStart\n * @param {Event} e\n */\n var dragStart = function(e) {\n e.originalEvent.dataTransfer.setData('text', $(e.target).parent().data('id'));\n };\n\n /**\n * HTML5 implementation of drag/drop (there is an accesible alternative in the menus).\n * @method allowDrop\n * @param {Event} e\n */\n var allowDrop = function(e) {\n e.originalEvent.dataTransfer.dropEffect = 'move';\n e.preventDefault();\n };\n\n /**\n * HTML5 implementation of drag/drop (there is an accesible alternative in the menus).\n * @method dragEnter\n * @param {Event} e\n */\n var dragEnter = function(e) {\n e.preventDefault();\n $(this).addClass('currentdragtarget');\n };\n\n /**\n * HTML5 implementation of drag/drop (there is an accesible alternative in the menus).\n * @method dragLeave\n * @param {Event} e\n */\n var dragLeave = function(e) {\n e.preventDefault();\n $(this).removeClass('currentdragtarget');\n };\n\n /**\n * HTML5 implementation of drag/drop (there is an accesible alternative in the menus).\n * @method dropOver\n * @param {Event} e\n */\n var dropOver = function(e) {\n e.preventDefault();\n moveSource = e.originalEvent.dataTransfer.getData('text');\n moveTarget = $(e.target).parent().data('id');\n $(this).removeClass('currentdragtarget');\n\n confirmMove();\n };\n\n /**\n * Deletes a related competency without confirmation.\n *\n * @param {Event} e The event that triggered the action.\n * @method deleteRelatedHandler\n */\n var deleteRelatedHandler = function(e) {\n e.preventDefault();\n\n var relatedid = this.id.substr(11);\n var competency = $('[data-region=\"competencyactions\"]').data('competency');\n var removeRelated = ajax.call([\n {methodname: 'core_competency_remove_related_competency',\n args: {relatedcompetencyid: relatedid, competencyid: competency.id}},\n {methodname: 'tool_lp_data_for_related_competencies_section',\n args: {competencyid: competency.id}}\n ]);\n\n removeRelated[1].done(function(context) {\n templates.render('tool_lp/related_competencies', context).done(function(html) {\n $('[data-region=\"relatedcompetencies\"]').replaceWith(html);\n updatedRelatedCompetencies();\n }).fail(notification.exception);\n }).fail(notification.exception);\n };\n\n /**\n * Updates the competencies list (with relations) and add listeners.\n *\n * @method updatedRelatedCompetencies\n */\n var updatedRelatedCompetencies = function() {\n\n // Listeners to newly loaded related competencies.\n $('[data-action=\"deleterelation\"]').on('click', deleteRelatedHandler);\n\n };\n\n /**\n * Log the competency viewed event.\n *\n * @param {Object} competency The competency.\n * @method triggerCompetencyViewedEvent\n */\n var triggerCompetencyViewedEvent = function(competency) {\n if (competency.id !== selectedCompetencyId) {\n // Set the selected competency id.\n selectedCompetencyId = competency.id;\n ajax.call([{\n methodname: 'core_competency_competency_viewed',\n args: {id: competency.id}\n }]);\n }\n };\n\n /**\n * Return the taxonomy constant for a level.\n *\n * @param {Number} level The level.\n * @return {String}\n * @function getTaxonomyAtLevel\n */\n var getTaxonomyAtLevel = function(level) {\n var constant = taxonomiesConstants[level];\n if (!constant) {\n constant = 'competency';\n }\n return constant;\n };\n\n /**\n * Render the competency summary.\n *\n * @param {Object} competency The competency.\n */\n var renderCompetencySummary = function(competency) {\n var promise = $.Deferred().resolve().promise(),\n context = {};\n\n context.competency = competency;\n context.showdeleterelatedaction = true;\n context.showrelatedcompetencies = true;\n context.showrule = false;\n context.pluginbaseurl = url.relativeUrl('/admin/tool/lp');\n\n if (competency.ruleoutcome != Outcomes.NONE) {\n // Get the outcome and rule name.\n promise = Outcomes.getString(competency.ruleoutcome).then(function(str) {\n var name;\n $.each(rulesModules, function(index, modInfo) {\n if (modInfo.type == competency.ruletype) {\n name = modInfo.name;\n }\n });\n return [str, name];\n });\n }\n\n promise.then(function(strs) {\n if (typeof strs !== 'undefined') {\n context.showrule = true;\n context.rule = {\n outcome: strs[0],\n type: strs[1]\n };\n }\n return context;\n }).then(function(context) {\n return templates.render('tool_lp/competency_summary', context);\n }).then(function(html) {\n $('[data-region=\"competencyinfo\"]').html(html);\n $('[data-action=\"deleterelation\"]').on('click', deleteRelatedHandler);\n return templates.render('tool_lp/loading', {});\n }).then(function(html, js) {\n templates.replaceNodeContents('[data-region=\"relatedcompetencies\"]', html, js);\n return ajax.call([{\n methodname: 'tool_lp_data_for_related_competencies_section',\n args: {competencyid: competency.id}\n }])[0];\n }).then(function(context) {\n return templates.render('tool_lp/related_competencies', context);\n }).then(function(html, js) {\n $('[data-region=\"relatedcompetencies\"]').replaceWith(html);\n templates.runTemplateJS(js);\n updatedRelatedCompetencies();\n return;\n }).catch(notification.exception);\n };\n\n /**\n * Return the string \"Add \".\n *\n * @param {Number} level The level.\n * @return {String}\n * @function strAddTaxonomy\n */\n var strAddTaxonomy = function(level) {\n return str.get_string('taxonomy_add_' + getTaxonomyAtLevel(level), 'tool_lp');\n };\n\n /**\n * Return the string \"Selected \".\n *\n * @param {Number} level The level.\n * @return {String}\n * @function strSelectedTaxonomy\n */\n var strSelectedTaxonomy = function(level) {\n return str.get_string('taxonomy_selected_' + getTaxonomyAtLevel(level), 'tool_lp');\n };\n\n /**\n * Handler when a node in the aria tree is selected.\n * @method selectionChanged\n * @param {Event} evt The event that triggered the selection change.\n * @param {Object} params The parameters for the event. Contains a list of selected nodes.\n * @return {Boolean}\n */\n var selectionChanged = function(evt, params) {\n var node = params.selected,\n id = $(node).data('id'),\n btn = $('[data-region=\"competencyactions\"] [data-action=\"add\"]'),\n actionMenu = $('[data-region=\"competencyactionsmenu\"]'),\n selectedTitle = $('[data-region=\"selected-competency\"]'),\n level = 0,\n sublevel = 1;\n\n menubar.closeAll();\n\n if (typeof id === \"undefined\") {\n // Assume this is the root of the tree.\n // Here we are only getting the text from the top of the tree, to do it we clone the tree,\n // remove all children and then call text on the result.\n $('[data-region=\"competencyinfo\"]').html(node.clone().children().remove().end().text());\n $('[data-region=\"competencyactions\"]').data('competency', null);\n actionMenu.hide();\n\n } else {\n var competency = treeModel.getCompetency(id);\n\n level = treeModel.getCompetencyLevel(id);\n sublevel = level + 1;\n\n actionMenu.show();\n $('[data-region=\"competencyactions\"]').data('competency', competency);\n renderCompetencySummary(competency);\n // Log Competency viewed event.\n triggerCompetencyViewedEvent(competency);\n }\n strSelectedTaxonomy(level).then(function(str) {\n selectedTitle.text(str);\n return;\n }).catch(notification.exception);\n\n strAddTaxonomy(sublevel).then(function(str) {\n btn.show()\n .find('[data-region=\"term\"]')\n .text(str);\n return;\n }).catch(notification.exception);\n\n // We handled this event so consume it.\n evt.preventDefault();\n return false;\n };\n\n /**\n * Return the string \"Selected \".\n *\n * @function parseTaxonomies\n * @param {String} taxonomiesstr Comma separated list of taxonomies.\n * @return {Array} of level => taxonomystr\n */\n var parseTaxonomies = function(taxonomiesstr) {\n var all = taxonomiesstr.split(',');\n all.unshift(\"\");\n delete all[0];\n\n // Note we don't need to fill holes, because other functions check for empty anyway.\n return all;\n };\n\n return {\n /**\n * Initialise this page (attach event handlers etc).\n *\n * @method init\n * @param {Object} model The tree model provides some useful functions for loading and searching competencies.\n * @param {Number} pagectxid The page context ID.\n * @param {Object} taxonomies Constants indexed by level.\n * @param {Object} rulesMods The modules of the rules.\n */\n init: function(model, pagectxid, taxonomies, rulesMods) {\n treeModel = model;\n pageContextId = pagectxid;\n taxonomiesConstants = parseTaxonomies(taxonomies);\n rulesModules = rulesMods;\n\n $('[data-region=\"competencyactions\"] [data-action=\"add\"]').on('click', addHandler);\n\n menubar.enhance('.competencyactionsmenu', {\n '[data-action=\"edit\"]': editHandler,\n '[data-action=\"delete\"]': deleteCompetencyHandler,\n '[data-action=\"move\"]': moveHandler,\n '[data-action=\"moveup\"]': moveUpHandler,\n '[data-action=\"movedown\"]': moveDownHandler,\n '[data-action=\"linkedcourses\"]': seeCoursesHandler,\n '[data-action=\"relatedcompetencies\"]': relateCompetenciesHandler.bind(this),\n '[data-action=\"competencyrules\"]': ruleConfigHandler.bind(this)\n });\n $('[data-region=\"competencyactionsmenu\"]').hide();\n $('[data-region=\"competencyactions\"] [data-action=\"add\"]').hide();\n\n $('[data-region=\"filtercompetencies\"]').on('submit', updateSearchHandler);\n // Simple html5 drag drop because we already added an accessible alternative.\n var top = $('[data-region=\"managecompetencies\"] [data-enhance=\"tree\"]');\n top.on('dragstart', 'li>span', dragStart)\n .on('dragover', 'li>span', allowDrop)\n .on('dragenter', 'li>span', dragEnter)\n .on('dragleave', 'li>span', dragLeave)\n .on('drop', 'li>span', dropOver);\n\n model.on('selectionchanged', selectionChanged);\n\n // Prepare the configuration tool.\n ruleConfigInstance = new RuleConfig(treeModel, rulesModules);\n ruleConfigInstance.on('save', ruleConfigSaveHandler.bind(this));\n }\n };\n});\n"],"names":["define","$","url","templates","notification","str","ajax","dragdrop","Ariatree","Dialogue","menubar","Picker","Outcomes","RuleConfig","Pending","pageContextId","pickerInstance","ruleConfigInstance","relatedTarget","taxonomiesConstants","rulesModules","treeModel","moveSource","moveTarget","selectedCompetencyId","addHandler","parent","data","params","competencyframeworkid","getCompetencyFrameworkId","pagecontextid","parentid","id","relocate","queryparams","param","window","location","relativeUrl","hasRule","get_strings","key","component","shortname","done","strings","confirm","fail","exception","doMove","frameworkid","call","methodname","args","competencyid","search","val","reloadPage","confirmMove","targetComp","getCompetency","sourceComp","confirmMessage","showConfirm","path","indexOf","initMovePopup","popup","body","getContent","treeRoot","find","on","evt","target","selected","show","close","addCompetencyChildren","competencies","i","length","haschildren","children","moveHandler","e","preventDefault","competency","requests","searchtext","when","apply","framework","competenciestree","onecompetency","context","render","tree","editHandler","newhtml","newjs","replaceWith","runTemplateJS","updateSearchHandler","moveUpHandler","moveDownHandler","seeCoursesHandler","courses","html","get_string","linkedcourses","relateCompetenciesHandler","pendingPromise","compIds","competencyIds","calls","each","index","value","push","relatedcompetencyid","then","js","updatedRelatedCompetencies","resolve","catch","setDisallowedCompetencyIDs","display","ruleConfigHandler","setTargetCompetencyId","ruleConfigSaveHandler","config","update","idnumber","description","descriptionformat","ruletype","ruleoutcome","ruleconfig","result","renderCompetencySummary","doDelete","success","alert","deleteCompetencyHandler","dragStart","originalEvent","dataTransfer","setData","allowDrop","dropEffect","dragEnter","this","addClass","dragLeave","removeClass","dropOver","getData","deleteRelatedHandler","relatedid","substr","getTaxonomyAtLevel","level","constant","promise","Deferred","showdeleterelatedaction","showrelatedcompetencies","showrule","pluginbaseurl","NONE","getString","name","modInfo","type","strs","rule","outcome","replaceNodeContents","selectionChanged","node","btn","actionMenu","selectedTitle","sublevel","closeAll","clone","remove","end","text","hide","getCompetencyLevel","triggerCompetencyViewedEvent","strSelectedTaxonomy","strAddTaxonomy","init","model","pagectxid","taxonomies","rulesMods","all","split","unshift","enhance","bind"],"mappings":"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"}
\ No newline at end of file
diff --git a/admin/tool/lp/amd/build/competencydialogue.min.js.map b/admin/tool/lp/amd/build/competencydialogue.min.js.map
index 3a66a43e0e3..4c05f338921 100644
--- a/admin/tool/lp/amd/build/competencydialogue.min.js.map
+++ b/admin/tool/lp/amd/build/competencydialogue.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"competencydialogue.min.js","sources":["../src/competencydialogue.js"],"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 * Display Competency in dialogue box.\n *\n * @module tool_lp/Competencydialogue\n * @copyright 2015 Issam Taboubi \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(['jquery',\n 'core/notification',\n 'core/ajax',\n 'core/templates',\n 'core/str',\n 'tool_lp/dialogue'],\n function($, notification, ajax, templates, str, Dialogue) {\n\n /**\n * The main instance we'll be working with.\n *\n * @type {Competencydialogue}\n */\n var instance;\n\n /**\n * Constructor for CompetencyDialogue.\n */\n var Competencydialogue = function() {\n // Intentionally left empty.\n };\n\n /**\n * Log the competency viewed event.\n *\n * @param {Number} competencyId The competency ID.\n * @method triggerCompetencyViewedEvent\n */\n Competencydialogue.prototype.triggerCompetencyViewedEvent = function(competencyId) {\n ajax.call([{\n methodname: 'core_competency_competency_viewed',\n args: {id: competencyId}\n }]);\n };\n\n /**\n * Display a dialogue box by competencyid.\n *\n * @param {Number} competencyid The competency ID.\n * @param {Object} options The options.\n * @method showDialogue\n */\n Competencydialogue.prototype.showDialogue = function(competencyid, options) {\n\n var datapromise = this.getCompetencyDataPromise(competencyid, options);\n var localthis = this;\n datapromise.done(function(data) {\n // Inner Html in the dialogue content.\n templates.render('tool_lp/competency_summary', data)\n .done(function(html) {\n // Log competency viewed event.\n localthis.triggerCompetencyViewedEvent(competencyid);\n\n // Show the dialogue.\n new Dialogue(\n data.competency.shortname,\n html\n );\n }).fail(notification.exception);\n }).fail(notification.exception);\n };\n\n /**\n * Display a dialogue box from data.\n *\n * @param {Object} dataSource data to be used to display dialogue box\n * @method showDialogueFromData\n */\n Competencydialogue.prototype.showDialogueFromData = function(dataSource) {\n\n var localthis = this;\n // Inner Html in the dialogue content.\n templates.render('tool_lp/competency_summary', dataSource)\n .done(function(html) {\n // Log competency viewed event.\n localthis.triggerCompetencyViewedEvent(dataSource.id);\n\n // Show the dialogue.\n new Dialogue(\n dataSource.shortname,\n html,\n localthis.enhanceDialogue\n );\n }).fail(notification.exception);\n };\n\n /**\n * The action on the click event.\n *\n * @param {Event} e event click\n * @method clickEventHandler\n */\n Competencydialogue.prototype.clickEventHandler = function(e) {\n\n var compdialogue = e.data.compdialogue;\n var currentTarget = $(e.currentTarget);\n var competencyid = currentTarget.data('id');\n var includerelated = !(currentTarget.data('excluderelated'));\n var includecourses = currentTarget.data('includecourses');\n\n // Show the dialogue box.\n compdialogue.showDialogue(competencyid, {\n includerelated: includerelated,\n includecourses: includecourses\n });\n e.preventDefault();\n };\n\n /**\n * Get a promise on data competency.\n *\n * @param {Number} competencyid\n * @param {Object} options\n * @return {Promise} return promise on data request\n * @method getCompetencyDataPromise\n */\n Competencydialogue.prototype.getCompetencyDataPromise = function(competencyid, options) {\n\n var requests = ajax.call([\n {methodname: 'tool_lp_data_for_competency_summary',\n args: {competencyid: competencyid,\n includerelated: options.includerelated || false,\n includecourses: options.includecourses || false\n }\n }\n ]);\n\n return requests[0].then(function(context) {\n return context;\n }).fail(notification.exception);\n };\n\n return /** @alias module:tool_lp/competencydialogue */ {\n\n /**\n * Initialise the competency dialogue module.\n *\n * Only the first call matters.\n */\n init: function() {\n if (typeof instance !== 'undefined') {\n return;\n }\n\n // Instantiate the one instance and delegate event on the body.\n instance = new Competencydialogue();\n $('body').delegate('[data-action=\"competency-dialogue\"]', 'click', {compdialogue: instance},\n instance.clickEventHandler.bind(instance));\n }\n };\n});\n"],"names":["define","$","notification","ajax","templates","str","Dialogue","instance","Competencydialogue","prototype","triggerCompetencyViewedEvent","competencyId","call","methodname","args","id","showDialogue","competencyid","options","datapromise","this","getCompetencyDataPromise","localthis","done","data","render","html","competency","shortname","fail","exception","showDialogueFromData","dataSource","enhanceDialogue","clickEventHandler","e","compdialogue","currentTarget","includerelated","includecourses","preventDefault","then","context","init","delegate","bind"],"mappings":";;;;;;;AAsBAA,oCAAO,CAAC,SACA,oBACA,YACA,iBACA,WACA,qBACD,SAASC,EAAGC,aAAcC,KAAMC,UAAWC,IAAKC,cAO/CC,SAKAC,mBAAqB,oBAUzBA,mBAAmBC,UAAUC,6BAA+B,SAASC,cACjER,KAAKS,KAAK,CAAC,CACHC,WAAY,oCACZC,KAAM,CAACC,GAAIJ,kBAWvBH,mBAAmBC,UAAUO,aAAe,SAASC,aAAcC,aAE3DC,YAAcC,KAAKC,yBAAyBJ,aAAcC,SAC1DI,UAAYF,KAChBD,YAAYI,MAAK,SAASC,MAEtBpB,UAAUqB,OAAO,6BAA8BD,MAC1CD,MAAK,SAASG,MAEXJ,UAAUZ,6BAA6BO,kBAGnCX,SACAkB,KAAKG,WAAWC,UAChBF,SAELG,KAAK3B,aAAa4B,cAC1BD,KAAK3B,aAAa4B,YASzBtB,mBAAmBC,UAAUsB,qBAAuB,SAASC,gBAErDV,UAAYF,KAEhBhB,UAAUqB,OAAO,6BAA8BO,YAC1CT,MAAK,SAASG,MAEXJ,UAAUZ,6BAA6BsB,WAAWjB,QAG9CT,SACA0B,WAAWJ,UACXF,KACAJ,UAAUW,oBAEfJ,KAAK3B,aAAa4B,YAS7BtB,mBAAmBC,UAAUyB,kBAAoB,SAASC,OAElDC,aAAeD,EAAEX,KAAKY,aACtBC,cAAgBpC,EAAEkC,EAAEE,eACpBpB,aAAeoB,cAAcb,KAAK,MAClCc,gBAAmBD,cAAcb,KAAK,kBACtCe,eAAiBF,cAAcb,KAAK,kBAGxCY,aAAapB,aAAaC,aAAc,CACpCqB,eAAgBA,eAChBC,eAAgBA,iBAEpBJ,EAAEK,kBAWNhC,mBAAmBC,UAAUY,yBAA2B,SAASJ,aAAcC,gBAE5Df,KAAKS,KAAK,CACrB,CAACC,WAAY,sCACXC,KAAM,CAACG,aAAcA,aACbqB,eAAgBpB,QAAQoB,iBAAkB,EAC1CC,eAAgBrB,QAAQqB,iBAAkB,MAKxC,GAAGE,MAAK,SAASC,gBACvBA,WACPb,KAAK3B,aAAa4B,YAG8B,CAOnDa,KAAM,gBACsB,IAAbpC,WAKXA,SAAW,IAAIC,mBACfP,EAAE,QAAQ2C,SAAS,sCAAuC,QAAS,CAACR,aAAc7B,UAC9EA,SAAS2B,kBAAkBW,KAAKtC"}
\ No newline at end of file
+{"version":3,"file":"competencydialogue.min.js","sources":["../src/competencydialogue.js"],"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 * Display Competency in dialogue box.\n *\n * @module tool_lp/Competencydialogue\n * @copyright 2015 Issam Taboubi \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(['jquery',\n 'core/notification',\n 'core/ajax',\n 'core/templates',\n 'core/str',\n 'tool_lp/dialogue'],\n function($, notification, ajax, templates, str, Dialogue) {\n\n /**\n * The main instance we'll be working with.\n *\n * @type {Competencydialogue}\n */\n var instance;\n\n /**\n * Constructor for CompetencyDialogue.\n */\n var Competencydialogue = function() {\n // Intentionally left empty.\n };\n\n /**\n * Log the competency viewed event.\n *\n * @param {Number} competencyId The competency ID.\n * @method triggerCompetencyViewedEvent\n */\n Competencydialogue.prototype.triggerCompetencyViewedEvent = function(competencyId) {\n ajax.call([{\n methodname: 'core_competency_competency_viewed',\n args: {id: competencyId}\n }]);\n };\n\n /**\n * Display a dialogue box by competencyid.\n *\n * @param {Number} competencyid The competency ID.\n * @param {Object} options The options.\n * @method showDialogue\n */\n Competencydialogue.prototype.showDialogue = function(competencyid, options) {\n\n var datapromise = this.getCompetencyDataPromise(competencyid, options);\n var localthis = this;\n datapromise.done(function(data) {\n // Inner Html in the dialogue content.\n templates.render('tool_lp/competency_summary', data)\n .done(function(html) {\n // Log competency viewed event.\n localthis.triggerCompetencyViewedEvent(competencyid);\n\n // Show the dialogue.\n new Dialogue(\n data.competency.shortname,\n html\n );\n }).fail(notification.exception);\n }).fail(notification.exception);\n };\n\n /**\n * Display a dialogue box from data.\n *\n * @param {Object} dataSource data to be used to display dialogue box\n * @method showDialogueFromData\n */\n Competencydialogue.prototype.showDialogueFromData = function(dataSource) {\n\n var localthis = this;\n // Inner Html in the dialogue content.\n templates.render('tool_lp/competency_summary', dataSource)\n .done(function(html) {\n // Log competency viewed event.\n localthis.triggerCompetencyViewedEvent(dataSource.id);\n\n // Show the dialogue.\n new Dialogue(\n dataSource.shortname,\n html,\n localthis.enhanceDialogue\n );\n }).fail(notification.exception);\n };\n\n /**\n * The action on the click event.\n *\n * @param {Event} e event click\n * @method clickEventHandler\n */\n Competencydialogue.prototype.clickEventHandler = function(e) {\n\n var compdialogue = e.data.compdialogue;\n var currentTarget = $(e.currentTarget);\n var competencyid = currentTarget.data('id');\n var includerelated = !(currentTarget.data('excluderelated'));\n var includecourses = currentTarget.data('includecourses');\n\n // Show the dialogue box.\n compdialogue.showDialogue(competencyid, {\n includerelated: includerelated,\n includecourses: includecourses\n });\n e.preventDefault();\n };\n\n /**\n * Get a promise on data competency.\n *\n * @param {Number} competencyid\n * @param {Object} options\n * @return {Promise} return promise on data request\n * @method getCompetencyDataPromise\n */\n Competencydialogue.prototype.getCompetencyDataPromise = function(competencyid, options) {\n\n var requests = ajax.call([\n {methodname: 'tool_lp_data_for_competency_summary',\n args: {competencyid: competencyid,\n includerelated: options.includerelated || false,\n includecourses: options.includecourses || false\n }\n }\n ]);\n\n return requests[0].then(function(context) {\n return context;\n }).fail(notification.exception);\n };\n\n return /** @alias module:tool_lp/competencydialogue */ {\n\n /**\n * Initialise the competency dialogue module.\n *\n * Only the first call matters.\n */\n init: function() {\n if (typeof instance !== 'undefined') {\n return;\n }\n\n // Instantiate the one instance and delegate event on the body.\n instance = new Competencydialogue();\n $('body').delegate('[data-action=\"competency-dialogue\"]', 'click', {compdialogue: instance},\n instance.clickEventHandler.bind(instance));\n }\n };\n});\n"],"names":["define","$","notification","ajax","templates","str","Dialogue","instance","Competencydialogue","prototype","triggerCompetencyViewedEvent","competencyId","call","methodname","args","id","showDialogue","competencyid","options","datapromise","this","getCompetencyDataPromise","localthis","done","data","render","html","competency","shortname","fail","exception","showDialogueFromData","dataSource","enhanceDialogue","clickEventHandler","e","compdialogue","currentTarget","includerelated","includecourses","preventDefault","then","context","init","delegate","bind"],"mappings":";;;;;;;AAsBAA,OAAM,6BAAC,CAAC,SACA,oBACA,YACA,iBACA,WACA,qBACD,SAASC,EAAGC,aAAcC,KAAMC,UAAWC,IAAKC,UAOnD,IAAIC,SAKAC,mBAAqB,aAkHzB,OAxGAA,mBAAmBC,UAAUC,6BAA+B,SAASC,cACjER,KAAKS,KAAK,CAAC,CACHC,WAAY,oCACZC,KAAM,CAACC,GAAIJ,kBAWvBH,mBAAmBC,UAAUO,aAAe,SAASC,aAAcC,SAE/D,IAAIC,YAAcC,KAAKC,yBAAyBJ,aAAcC,SAC1DI,UAAYF,KAChBD,YAAYI,MAAK,SAASC,MAEtBpB,UAAUqB,OAAO,6BAA8BD,MAC1CD,MAAK,SAASG,MAEXJ,UAAUZ,6BAA6BO,cAGvC,IAAIX,SACAkB,KAAKG,WAAWC,UAChBF,KAEP,IAAEG,KAAK3B,aAAa4B,UAC5B,IAAED,KAAK3B,aAAa4B,YASzBtB,mBAAmBC,UAAUsB,qBAAuB,SAASC,YAEzD,IAAIV,UAAYF,KAEhBhB,UAAUqB,OAAO,6BAA8BO,YAC1CT,MAAK,SAASG,MAEXJ,UAAUZ,6BAA6BsB,WAAWjB,IAGlD,IAAIT,SACA0B,WAAWJ,UACXF,KACAJ,UAAUW,gBAEjB,IAAEJ,KAAK3B,aAAa4B,YAS7BtB,mBAAmBC,UAAUyB,kBAAoB,SAASC,GAEtD,IAAIC,aAAeD,EAAEX,KAAKY,aACtBC,cAAgBpC,EAAEkC,EAAEE,eACpBpB,aAAeoB,cAAcb,KAAK,MAClCc,gBAAmBD,cAAcb,KAAK,kBACtCe,eAAiBF,cAAcb,KAAK,kBAGxCY,aAAapB,aAAaC,aAAc,CACpCqB,eAAgBA,eAChBC,eAAgBA,iBAEpBJ,EAAEK,kBAWNhC,mBAAmBC,UAAUY,yBAA2B,SAASJ,aAAcC,SAW3E,OATef,KAAKS,KAAK,CACrB,CAACC,WAAY,sCACXC,KAAM,CAACG,aAAcA,aACbqB,eAAgBpB,QAAQoB,iBAAkB,EAC1CC,eAAgBrB,QAAQqB,iBAAkB,MAKxC,GAAGE,MAAK,SAASC,SAC9B,OAAOA,OACT,IAAEb,KAAK3B,aAAa4B,YAG8B,CAOnDa,KAAM,gBACsB,IAAbpC,WAKXA,SAAW,IAAIC,mBACfP,EAAE,QAAQ2C,SAAS,sCAAuC,QAAS,CAACR,aAAc7B,UAC9EA,SAAS2B,kBAAkBW,KAAKtC,WACxC,EAER"}
\ No newline at end of file
diff --git a/admin/tool/lp/amd/build/competencypicker.min.js.map b/admin/tool/lp/amd/build/competencypicker.min.js.map
index 6fca0dc076b..a3a62cff38a 100644
--- a/admin/tool/lp/amd/build/competencypicker.min.js.map
+++ b/admin/tool/lp/amd/build/competencypicker.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"competencypicker.min.js","sources":["../src/competencypicker.js"],"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 * Competency picker.\n *\n * To handle 'save' events use: picker.on('save')\n * This will receive a object with either a single 'competencyId', or an array in 'competencyIds'\n * depending on the value of multiSelect.\n *\n * @module tool_lp/competencypicker\n * @copyright 2015 Frédéric Massart - FMCorz.net\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\ndefine(['jquery',\n 'core/notification',\n 'core/ajax',\n 'core/templates',\n 'tool_lp/dialogue',\n 'core/str',\n 'tool_lp/tree',\n 'core/pending'\n ],\n function($, Notification, Ajax, Templates, Dialogue, Str, Tree, Pending) {\n\n /**\n * Competency picker class.\n * @param {Number} pageContextId The page context ID.\n * @param {Number|false} singleFramework The ID of the framework when limited to one.\n * @param {String} pageContextIncludes One of 'children', 'parents', 'self'.\n * @param {Boolean} multiSelect Support multi-select in the tree.\n */\n var Picker = function(pageContextId, singleFramework, pageContextIncludes, multiSelect) {\n var self = this;\n self._eventNode = $('');\n self._frameworks = [];\n self._reset();\n\n self._pageContextId = pageContextId;\n self._pageContextIncludes = pageContextIncludes || 'children';\n self._multiSelect = (typeof multiSelect === 'undefined' || multiSelect === true);\n if (singleFramework) {\n self._frameworkId = singleFramework;\n self._singleFramework = true;\n }\n };\n\n /** @property {Array} The competencies fetched. */\n Picker.prototype._competencies = null;\n /** @property {Array} The competencies that cannot be picked. */\n Picker.prototype._disallowedCompetencyIDs = null;\n /** @property {Node} The node we attach the events to. */\n Picker.prototype._eventNode = null;\n /** @property {Array} The list of frameworks fetched. */\n Picker.prototype._frameworks = null;\n /** @property {Number} The current framework ID. */\n Picker.prototype._frameworkId = null;\n /** @property {Number} The page context ID. */\n Picker.prototype._pageContextId = null;\n /** @property {Number} Relevant contexts inclusion. */\n Picker.prototype._pageContextIncludes = null;\n /** @property {Dialogue} The reference to the dialogue. */\n Picker.prototype._popup = null;\n /** @property {String} The string we filter the competencies with. */\n Picker.prototype._searchText = '';\n /** @property {Object} The competency that was selected. */\n Picker.prototype._selectedCompetencies = null;\n /** @property {Boolean} Whether we can browse frameworks or not. */\n Picker.prototype._singleFramework = false;\n /** @property {Boolean} Do we allow multi select? */\n Picker.prototype._multiSelect = true;\n /** @property {Boolean} Do we allow to display hidden framework? */\n Picker.prototype._onlyVisible = true;\n\n /**\n * Hook to executed after the view is rendered.\n *\n * @method _afterRender\n */\n Picker.prototype._afterRender = function() {\n var self = this;\n\n // Initialise the tree.\n var tree = new Tree(self._find('[data-enhance=linktree]'), self._multiSelect);\n\n // To prevent jiggling we only show the tree after it is enhanced.\n self._find('[data-enhance=linktree]').show();\n\n tree.on('selectionchanged', function(evt, params) {\n var selected = params.selected;\n evt.preventDefault();\n var validIds = [];\n $.each(selected, function(index, item) {\n var compId = $(item).data('id'),\n valid = true;\n\n if (typeof compId === 'undefined') {\n // Do not allow picking nodes with no id.\n valid = false;\n } else {\n $.each(self._disallowedCompetencyIDs, function(i, id) {\n if (id == compId) {\n valid = false;\n }\n });\n }\n if (valid) {\n validIds.push(compId);\n }\n });\n\n self._selectedCompetencies = validIds;\n\n // TODO Implement disabling of nodes in the tree module somehow.\n if (!self._selectedCompetencies.length) {\n self._find('[data-region=\"competencylinktree\"] [data-action=\"add\"]').attr('disabled', 'disabled');\n } else {\n self._find('[data-region=\"competencylinktree\"] [data-action=\"add\"]').removeAttr('disabled');\n }\n });\n\n // Add listener for framework change.\n if (!self._singleFramework) {\n self._find('[data-action=\"chooseframework\"]').change(function(e) {\n self._frameworkId = $(e.target).val();\n self._loadCompetencies().then(self._refresh.bind(self)).catch(Notification.exception);\n });\n }\n\n // Add listener for search.\n self._find('[data-region=\"filtercompetencies\"] button').click(function(e) {\n e.preventDefault();\n $(e.target).attr('disabled', 'disabled');\n self._searchText = self._find('[data-region=\"filtercompetencies\"] input').val() || '';\n return self._refresh().always(function() {\n $(e.target).removeAttr('disabled');\n });\n });\n\n // Add listener for cancel.\n self._find('[data-region=\"competencylinktree\"] [data-action=\"cancel\"]').click(function(e) {\n e.preventDefault();\n self.close();\n });\n\n // Add listener for add.\n self._find('[data-region=\"competencylinktree\"] [data-action=\"add\"]').click(function(e) {\n e.preventDefault();\n var pendingPromise = new Pending();\n if (!self._selectedCompetencies.length) {\n return;\n }\n\n if (self._multiSelect) {\n self._trigger('save', {competencyIds: self._selectedCompetencies});\n } else {\n // We checked above that the array has at least one value.\n self._trigger('save', {competencyId: self._selectedCompetencies[0]});\n }\n\n // The dialogue here is a YUI dialogue and doesn't support Promises at all.\n // However, it is typically synchronous so this shoudl suffice.\n self.close();\n pendingPromise.resolve();\n });\n\n // The list of selected competencies will be modified while looping (because of the listeners above).\n var currentItems = self._selectedCompetencies.slice(0);\n\n $.each(currentItems, function(index, id) {\n var node = self._find('[data-id=' + id + ']');\n if (node.length) {\n tree.toggleItem(node);\n tree.updateFocus(node);\n }\n });\n\n };\n\n /**\n * Close the dialogue.\n *\n * @method close\n */\n Picker.prototype.close = function() {\n var self = this;\n self._popup.close();\n self._reset();\n };\n\n /**\n * Opens the picker.\n *\n * @method display\n * @return {Promise}\n */\n Picker.prototype.display = function() {\n var self = this;\n return $.when(Str.get_string('competencypicker', 'tool_lp'), self._render())\n .then(function(title, render) {\n self._popup = new Dialogue(\n title,\n render[0],\n self._afterRender.bind(self)\n );\n return;\n }).catch(Notification.exception);\n };\n\n /**\n * Fetch the competencies.\n *\n * @param {Number} frameworkId The frameworkId.\n * @param {String} searchText Limit the competencies to those matching the text.\n * @method _fetchCompetencies\n * @return {Promise}\n */\n Picker.prototype._fetchCompetencies = function(frameworkId, searchText) {\n var self = this;\n\n return Ajax.call([\n {methodname: 'core_competency_search_competencies', args: {\n searchtext: searchText,\n competencyframeworkid: frameworkId\n }}\n ])[0].done(function(competencies) {\n /**\n * @param {Object} parent\n * @param {Array} competencies\n */\n function addCompetencyChildren(parent, competencies) {\n for (var i = 0; i < competencies.length; i++) {\n if (competencies[i].parentid == parent.id) {\n parent.haschildren = true;\n competencies[i].children = [];\n competencies[i].haschildren = false;\n parent.children[parent.children.length] = competencies[i];\n addCompetencyChildren(competencies[i], competencies);\n }\n }\n }\n\n // Expand the list of competencies into a tree.\n var i, comp;\n var tree = [];\n for (i = 0; i < competencies.length; i++) {\n comp = competencies[i];\n if (comp.parentid == \"0\") { // Loose check for now, because WS returns a string.\n comp.children = [];\n comp.haschildren = 0;\n tree[tree.length] = comp;\n addCompetencyChildren(comp, competencies);\n }\n }\n\n self._competencies = tree;\n\n }).fail(Notification.exception);\n };\n\n /**\n * Find a node in the dialogue.\n *\n * @param {String} selector\n * @return {JQuery}\n * @method _find\n */\n Picker.prototype._find = function(selector) {\n return $(this._popup.getContent()).find(selector);\n };\n\n /**\n * Convenience method to get a framework object.\n *\n * @param {Number} fid The framework ID.\n * @return {Object}\n * @method _getFramework\n */\n Picker.prototype._getFramework = function(fid) {\n var frm;\n $.each(this._frameworks, function(i, f) {\n if (f.id == fid) {\n frm = f;\n return;\n }\n });\n return frm;\n };\n\n /**\n * Load the competencies.\n *\n * @method _loadCompetencies\n * @return {Promise}\n */\n Picker.prototype._loadCompetencies = function() {\n return this._fetchCompetencies(this._frameworkId, this._searchText);\n };\n\n /**\n * Load the frameworks.\n *\n * @method _loadFrameworks\n * @return {Promise}\n */\n Picker.prototype._loadFrameworks = function() {\n var promise,\n self = this;\n\n // Quit early because we already have the data.\n if (self._frameworks.length > 0) {\n return $.when();\n }\n\n if (self._singleFramework) {\n promise = Ajax.call([\n {methodname: 'core_competency_read_competency_framework', args: {\n id: this._frameworkId\n }}\n ])[0].then(function(framework) {\n return [framework];\n });\n } else {\n promise = Ajax.call([\n {methodname: 'core_competency_list_competency_frameworks', args: {\n sort: 'shortname',\n context: {contextid: self._pageContextId},\n includes: self._pageContextIncludes,\n onlyvisible: self._onlyVisible\n }}\n ])[0];\n }\n\n return promise.done(function(frameworks) {\n self._frameworks = frameworks;\n }).fail(Notification.exception);\n };\n\n /**\n * Register an event listener.\n *\n * @param {String} type The event type.\n * @param {Function} handler The event listener.\n * @method on\n */\n Picker.prototype.on = function(type, handler) {\n this._eventNode.on(type, handler);\n };\n\n /**\n * Hook to executed before render.\n *\n * @method _preRender\n * @return {Promise}\n */\n Picker.prototype._preRender = function() {\n var self = this;\n return self._loadFrameworks().then(function() {\n if (!self._frameworkId && self._frameworks.length > 0) {\n self._frameworkId = self._frameworks[0].id;\n }\n\n // We could not set a framework ID, that probably means there are no frameworks accessible.\n if (!self._frameworkId) {\n self._frameworks = [];\n return $.when();\n }\n\n return self._loadCompetencies();\n });\n };\n\n /**\n * Refresh the view.\n *\n * @method _refresh\n * @return {Promise}\n */\n Picker.prototype._refresh = function() {\n var self = this;\n return self._render().then(function(html) {\n self._find('[data-region=\"competencylinktree\"]').replaceWith(html);\n self._afterRender();\n return;\n });\n };\n\n /**\n * Render the dialogue.\n *\n * @method _render\n * @return {Promise}\n */\n Picker.prototype._render = function() {\n var self = this;\n return self._preRender().then(function() {\n\n if (!self._singleFramework) {\n $.each(self._frameworks, function(i, framework) {\n if (framework.id == self._frameworkId) {\n framework.selected = true;\n } else {\n framework.selected = false;\n }\n });\n }\n\n var context = {\n competencies: self._competencies,\n framework: self._getFramework(self._frameworkId),\n frameworks: self._frameworks,\n search: self._searchText,\n singleFramework: self._singleFramework,\n };\n\n return Templates.render('tool_lp/competency_picker', context);\n });\n };\n\n /**\n * Reset the dialogue properties.\n *\n * This does not reset everything, just enough to reset the UI.\n *\n * @method _reset\n */\n Picker.prototype._reset = function() {\n this._competencies = [];\n this._disallowedCompetencyIDs = [];\n this._popup = null;\n this._searchText = '';\n this._selectedCompetencies = [];\n };\n\n /**\n * Set what competencies cannot be picked.\n *\n * This needs to be set after reset/close.\n *\n * @param {Number[]} ids The IDs.\n * @method _setDisallowedCompetencyIDs\n */\n Picker.prototype.setDisallowedCompetencyIDs = function(ids) {\n this._disallowedCompetencyIDs = ids;\n };\n\n /**\n * Trigger an event.\n *\n * @param {String} type The type of event.\n * @param {Object} data The data to pass to the listeners.\n * @method _reset\n */\n Picker.prototype._trigger = function(type, data) {\n this._eventNode.trigger(type, [data]);\n };\n\n return Picker;\n\n});\n"],"names":["define","$","Notification","Ajax","Templates","Dialogue","Str","Tree","Pending","Picker","pageContextId","singleFramework","pageContextIncludes","multiSelect","this","_eventNode","_frameworks","_reset","_pageContextId","_pageContextIncludes","_multiSelect","_frameworkId","_singleFramework","prototype","_competencies","_disallowedCompetencyIDs","_popup","_searchText","_selectedCompetencies","_onlyVisible","_afterRender","self","tree","_find","show","on","evt","params","selected","preventDefault","validIds","each","index","item","compId","data","valid","i","id","push","length","removeAttr","attr","change","e","target","val","_loadCompetencies","then","_refresh","bind","catch","exception","click","always","close","pendingPromise","_trigger","competencyIds","competencyId","resolve","currentItems","slice","node","toggleItem","updateFocus","display","when","get_string","_render","title","render","_fetchCompetencies","frameworkId","searchText","call","methodname","args","searchtext","competencyframeworkid","done","competencies","addCompetencyChildren","parent","parentid","haschildren","children","comp","fail","selector","getContent","find","_getFramework","fid","frm","f","_loadFrameworks","framework","sort","context","contextid","includes","onlyvisible","frameworks","type","handler","_preRender","html","replaceWith","search","setDisallowedCompetencyIDs","ids","trigger"],"mappings":"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"}
\ No newline at end of file
+{"version":3,"file":"competencypicker.min.js","sources":["../src/competencypicker.js"],"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 * Competency picker.\n *\n * To handle 'save' events use: picker.on('save')\n * This will receive a object with either a single 'competencyId', or an array in 'competencyIds'\n * depending on the value of multiSelect.\n *\n * @module tool_lp/competencypicker\n * @copyright 2015 Frédéric Massart - FMCorz.net\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\ndefine(['jquery',\n 'core/notification',\n 'core/ajax',\n 'core/templates',\n 'tool_lp/dialogue',\n 'core/str',\n 'tool_lp/tree',\n 'core/pending'\n ],\n function($, Notification, Ajax, Templates, Dialogue, Str, Tree, Pending) {\n\n /**\n * Competency picker class.\n * @param {Number} pageContextId The page context ID.\n * @param {Number|false} singleFramework The ID of the framework when limited to one.\n * @param {String} pageContextIncludes One of 'children', 'parents', 'self'.\n * @param {Boolean} multiSelect Support multi-select in the tree.\n */\n var Picker = function(pageContextId, singleFramework, pageContextIncludes, multiSelect) {\n var self = this;\n self._eventNode = $('');\n self._frameworks = [];\n self._reset();\n\n self._pageContextId = pageContextId;\n self._pageContextIncludes = pageContextIncludes || 'children';\n self._multiSelect = (typeof multiSelect === 'undefined' || multiSelect === true);\n if (singleFramework) {\n self._frameworkId = singleFramework;\n self._singleFramework = true;\n }\n };\n\n /** @property {Array} The competencies fetched. */\n Picker.prototype._competencies = null;\n /** @property {Array} The competencies that cannot be picked. */\n Picker.prototype._disallowedCompetencyIDs = null;\n /** @property {Node} The node we attach the events to. */\n Picker.prototype._eventNode = null;\n /** @property {Array} The list of frameworks fetched. */\n Picker.prototype._frameworks = null;\n /** @property {Number} The current framework ID. */\n Picker.prototype._frameworkId = null;\n /** @property {Number} The page context ID. */\n Picker.prototype._pageContextId = null;\n /** @property {Number} Relevant contexts inclusion. */\n Picker.prototype._pageContextIncludes = null;\n /** @property {Dialogue} The reference to the dialogue. */\n Picker.prototype._popup = null;\n /** @property {String} The string we filter the competencies with. */\n Picker.prototype._searchText = '';\n /** @property {Object} The competency that was selected. */\n Picker.prototype._selectedCompetencies = null;\n /** @property {Boolean} Whether we can browse frameworks or not. */\n Picker.prototype._singleFramework = false;\n /** @property {Boolean} Do we allow multi select? */\n Picker.prototype._multiSelect = true;\n /** @property {Boolean} Do we allow to display hidden framework? */\n Picker.prototype._onlyVisible = true;\n\n /**\n * Hook to executed after the view is rendered.\n *\n * @method _afterRender\n */\n Picker.prototype._afterRender = function() {\n var self = this;\n\n // Initialise the tree.\n var tree = new Tree(self._find('[data-enhance=linktree]'), self._multiSelect);\n\n // To prevent jiggling we only show the tree after it is enhanced.\n self._find('[data-enhance=linktree]').show();\n\n tree.on('selectionchanged', function(evt, params) {\n var selected = params.selected;\n evt.preventDefault();\n var validIds = [];\n $.each(selected, function(index, item) {\n var compId = $(item).data('id'),\n valid = true;\n\n if (typeof compId === 'undefined') {\n // Do not allow picking nodes with no id.\n valid = false;\n } else {\n $.each(self._disallowedCompetencyIDs, function(i, id) {\n if (id == compId) {\n valid = false;\n }\n });\n }\n if (valid) {\n validIds.push(compId);\n }\n });\n\n self._selectedCompetencies = validIds;\n\n // TODO Implement disabling of nodes in the tree module somehow.\n if (!self._selectedCompetencies.length) {\n self._find('[data-region=\"competencylinktree\"] [data-action=\"add\"]').attr('disabled', 'disabled');\n } else {\n self._find('[data-region=\"competencylinktree\"] [data-action=\"add\"]').removeAttr('disabled');\n }\n });\n\n // Add listener for framework change.\n if (!self._singleFramework) {\n self._find('[data-action=\"chooseframework\"]').change(function(e) {\n self._frameworkId = $(e.target).val();\n self._loadCompetencies().then(self._refresh.bind(self)).catch(Notification.exception);\n });\n }\n\n // Add listener for search.\n self._find('[data-region=\"filtercompetencies\"] button').click(function(e) {\n e.preventDefault();\n $(e.target).attr('disabled', 'disabled');\n self._searchText = self._find('[data-region=\"filtercompetencies\"] input').val() || '';\n return self._refresh().always(function() {\n $(e.target).removeAttr('disabled');\n });\n });\n\n // Add listener for cancel.\n self._find('[data-region=\"competencylinktree\"] [data-action=\"cancel\"]').click(function(e) {\n e.preventDefault();\n self.close();\n });\n\n // Add listener for add.\n self._find('[data-region=\"competencylinktree\"] [data-action=\"add\"]').click(function(e) {\n e.preventDefault();\n var pendingPromise = new Pending();\n if (!self._selectedCompetencies.length) {\n return;\n }\n\n if (self._multiSelect) {\n self._trigger('save', {competencyIds: self._selectedCompetencies});\n } else {\n // We checked above that the array has at least one value.\n self._trigger('save', {competencyId: self._selectedCompetencies[0]});\n }\n\n // The dialogue here is a YUI dialogue and doesn't support Promises at all.\n // However, it is typically synchronous so this shoudl suffice.\n self.close();\n pendingPromise.resolve();\n });\n\n // The list of selected competencies will be modified while looping (because of the listeners above).\n var currentItems = self._selectedCompetencies.slice(0);\n\n $.each(currentItems, function(index, id) {\n var node = self._find('[data-id=' + id + ']');\n if (node.length) {\n tree.toggleItem(node);\n tree.updateFocus(node);\n }\n });\n\n };\n\n /**\n * Close the dialogue.\n *\n * @method close\n */\n Picker.prototype.close = function() {\n var self = this;\n self._popup.close();\n self._reset();\n };\n\n /**\n * Opens the picker.\n *\n * @method display\n * @return {Promise}\n */\n Picker.prototype.display = function() {\n var self = this;\n return $.when(Str.get_string('competencypicker', 'tool_lp'), self._render())\n .then(function(title, render) {\n self._popup = new Dialogue(\n title,\n render[0],\n self._afterRender.bind(self)\n );\n return;\n }).catch(Notification.exception);\n };\n\n /**\n * Fetch the competencies.\n *\n * @param {Number} frameworkId The frameworkId.\n * @param {String} searchText Limit the competencies to those matching the text.\n * @method _fetchCompetencies\n * @return {Promise}\n */\n Picker.prototype._fetchCompetencies = function(frameworkId, searchText) {\n var self = this;\n\n return Ajax.call([\n {methodname: 'core_competency_search_competencies', args: {\n searchtext: searchText,\n competencyframeworkid: frameworkId\n }}\n ])[0].done(function(competencies) {\n /**\n * @param {Object} parent\n * @param {Array} competencies\n */\n function addCompetencyChildren(parent, competencies) {\n for (var i = 0; i < competencies.length; i++) {\n if (competencies[i].parentid == parent.id) {\n parent.haschildren = true;\n competencies[i].children = [];\n competencies[i].haschildren = false;\n parent.children[parent.children.length] = competencies[i];\n addCompetencyChildren(competencies[i], competencies);\n }\n }\n }\n\n // Expand the list of competencies into a tree.\n var i, comp;\n var tree = [];\n for (i = 0; i < competencies.length; i++) {\n comp = competencies[i];\n if (comp.parentid == \"0\") { // Loose check for now, because WS returns a string.\n comp.children = [];\n comp.haschildren = 0;\n tree[tree.length] = comp;\n addCompetencyChildren(comp, competencies);\n }\n }\n\n self._competencies = tree;\n\n }).fail(Notification.exception);\n };\n\n /**\n * Find a node in the dialogue.\n *\n * @param {String} selector\n * @return {JQuery}\n * @method _find\n */\n Picker.prototype._find = function(selector) {\n return $(this._popup.getContent()).find(selector);\n };\n\n /**\n * Convenience method to get a framework object.\n *\n * @param {Number} fid The framework ID.\n * @return {Object}\n * @method _getFramework\n */\n Picker.prototype._getFramework = function(fid) {\n var frm;\n $.each(this._frameworks, function(i, f) {\n if (f.id == fid) {\n frm = f;\n return;\n }\n });\n return frm;\n };\n\n /**\n * Load the competencies.\n *\n * @method _loadCompetencies\n * @return {Promise}\n */\n Picker.prototype._loadCompetencies = function() {\n return this._fetchCompetencies(this._frameworkId, this._searchText);\n };\n\n /**\n * Load the frameworks.\n *\n * @method _loadFrameworks\n * @return {Promise}\n */\n Picker.prototype._loadFrameworks = function() {\n var promise,\n self = this;\n\n // Quit early because we already have the data.\n if (self._frameworks.length > 0) {\n return $.when();\n }\n\n if (self._singleFramework) {\n promise = Ajax.call([\n {methodname: 'core_competency_read_competency_framework', args: {\n id: this._frameworkId\n }}\n ])[0].then(function(framework) {\n return [framework];\n });\n } else {\n promise = Ajax.call([\n {methodname: 'core_competency_list_competency_frameworks', args: {\n sort: 'shortname',\n context: {contextid: self._pageContextId},\n includes: self._pageContextIncludes,\n onlyvisible: self._onlyVisible\n }}\n ])[0];\n }\n\n return promise.done(function(frameworks) {\n self._frameworks = frameworks;\n }).fail(Notification.exception);\n };\n\n /**\n * Register an event listener.\n *\n * @param {String} type The event type.\n * @param {Function} handler The event listener.\n * @method on\n */\n Picker.prototype.on = function(type, handler) {\n this._eventNode.on(type, handler);\n };\n\n /**\n * Hook to executed before render.\n *\n * @method _preRender\n * @return {Promise}\n */\n Picker.prototype._preRender = function() {\n var self = this;\n return self._loadFrameworks().then(function() {\n if (!self._frameworkId && self._frameworks.length > 0) {\n self._frameworkId = self._frameworks[0].id;\n }\n\n // We could not set a framework ID, that probably means there are no frameworks accessible.\n if (!self._frameworkId) {\n self._frameworks = [];\n return $.when();\n }\n\n return self._loadCompetencies();\n });\n };\n\n /**\n * Refresh the view.\n *\n * @method _refresh\n * @return {Promise}\n */\n Picker.prototype._refresh = function() {\n var self = this;\n return self._render().then(function(html) {\n self._find('[data-region=\"competencylinktree\"]').replaceWith(html);\n self._afterRender();\n return;\n });\n };\n\n /**\n * Render the dialogue.\n *\n * @method _render\n * @return {Promise}\n */\n Picker.prototype._render = function() {\n var self = this;\n return self._preRender().then(function() {\n\n if (!self._singleFramework) {\n $.each(self._frameworks, function(i, framework) {\n if (framework.id == self._frameworkId) {\n framework.selected = true;\n } else {\n framework.selected = false;\n }\n });\n }\n\n var context = {\n competencies: self._competencies,\n framework: self._getFramework(self._frameworkId),\n frameworks: self._frameworks,\n search: self._searchText,\n singleFramework: self._singleFramework,\n };\n\n return Templates.render('tool_lp/competency_picker', context);\n });\n };\n\n /**\n * Reset the dialogue properties.\n *\n * This does not reset everything, just enough to reset the UI.\n *\n * @method _reset\n */\n Picker.prototype._reset = function() {\n this._competencies = [];\n this._disallowedCompetencyIDs = [];\n this._popup = null;\n this._searchText = '';\n this._selectedCompetencies = [];\n };\n\n /**\n * Set what competencies cannot be picked.\n *\n * This needs to be set after reset/close.\n *\n * @param {Number[]} ids The IDs.\n * @method _setDisallowedCompetencyIDs\n */\n Picker.prototype.setDisallowedCompetencyIDs = function(ids) {\n this._disallowedCompetencyIDs = ids;\n };\n\n /**\n * Trigger an event.\n *\n * @param {String} type The type of event.\n * @param {Object} data The data to pass to the listeners.\n * @method _reset\n */\n Picker.prototype._trigger = function(type, data) {\n this._eventNode.trigger(type, [data]);\n };\n\n return Picker;\n\n});\n"],"names":["define","$","Notification","Ajax","Templates","Dialogue","Str","Tree","Pending","Picker","pageContextId","singleFramework","pageContextIncludes","multiSelect","this","_eventNode","_frameworks","_reset","_pageContextId","_pageContextIncludes","_multiSelect","_frameworkId","_singleFramework","prototype","_competencies","_disallowedCompetencyIDs","_popup","_searchText","_selectedCompetencies","_onlyVisible","_afterRender","self","tree","_find","show","on","evt","params","selected","preventDefault","validIds","each","index","item","compId","data","valid","i","id","push","length","removeAttr","attr","change","e","target","val","_loadCompetencies","then","_refresh","bind","catch","exception","click","always","close","pendingPromise","_trigger","competencyIds","competencyId","resolve","currentItems","slice","node","toggleItem","updateFocus","display","when","get_string","_render","title","render","_fetchCompetencies","frameworkId","searchText","call","methodname","args","searchtext","competencyframeworkid","done","competencies","addCompetencyChildren","parent","parentid","haschildren","children","comp","fail","selector","getContent","find","_getFramework","fid","frm","f","_loadFrameworks","framework","sort","context","contextid","includes","onlyvisible","frameworks","type","handler","_preRender","html","replaceWith","search","setDisallowedCompetencyIDs","ids","trigger"],"mappings":"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"}
\ No newline at end of file
diff --git a/admin/tool/lp/amd/build/competencypicker_user_plans.min.js.map b/admin/tool/lp/amd/build/competencypicker_user_plans.min.js.map
index 26cd77c5717..e335b578fb7 100644
--- a/admin/tool/lp/amd/build/competencypicker_user_plans.min.js.map
+++ b/admin/tool/lp/amd/build/competencypicker_user_plans.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"competencypicker_user_plans.min.js","sources":["../src/competencypicker_user_plans.js"],"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 * Competency picker from user plans.\n *\n * To handle 'save' events use: picker.on('save').\n *\n * This will receive a object with either a single 'competencyId', or an array in 'competencyIds'\n * depending on the value of multiSelect.\n *\n * @module tool_lp/competencypicker_user_plans\n * @copyright 2015 Frédéric Massart - FMCorz.net\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\ndefine(['jquery',\n 'core/notification',\n 'core/ajax',\n 'core/templates',\n 'core/str',\n 'tool_lp/tree',\n 'tool_lp/competencypicker'\n ],\n function($, Notification, Ajax, Templates, Str, Tree, PickerBase) {\n\n /**\n * Competency picker in plan class.\n *\n * @class tool_lp/competencypicker_user_plans\n * @param {Number} userId\n * @param {Number|false} singlePlan The ID of the plan when limited to one.\n * @param {Boolean} multiSelect Support multi-select in the tree.\n */\n var Picker = function(userId, singlePlan, multiSelect) {\n PickerBase.prototype.constructor.apply(this, [1, false, 'self', multiSelect]);\n this._userId = userId;\n this._plans = [];\n\n if (singlePlan) {\n this._planId = singlePlan;\n this._singlePlan = true;\n }\n };\n Picker.prototype = Object.create(PickerBase.prototype);\n\n /** @property {Array} The list of plans fetched. */\n Picker.prototype._plans = null;\n /** @property {Number} The current plan ID. */\n Picker.prototype._planId = null;\n /** @property {Boolean} Whether we can browse plans or not. */\n Picker.prototype._singlePlan = false;\n /** @property {Number} The user the plans belongs to. */\n Picker.prototype._userId = null;\n\n /**\n * Hook to executed after the view is rendered.\n *\n * @method _afterRender\n */\n Picker.prototype._afterRender = function() {\n var self = this;\n PickerBase.prototype._afterRender.apply(self, arguments);\n\n // Add listener for framework change.\n if (!self._singlePlan) {\n self._find('[data-action=\"chooseplan\"]').change(function(e) {\n self._planId = $(e.target).val();\n self._loadCompetencies().then(self._refresh.bind(self))\n .catch(Notification.exception);\n });\n }\n };\n\n /**\n * Fetch the competencies.\n *\n * @param {Number} planId The planId.\n * @param {String} searchText Limit the competencies to those matching the text.\n * @method _fetchCompetencies\n * @return {Promise} The promise object.\n */\n Picker.prototype._fetchCompetencies = function(planId, searchText) {\n var self = this;\n\n return Ajax.call([\n {methodname: 'core_competency_list_plan_competencies', args: {\n id: planId\n }}\n ])[0].done(function(competencies) {\n\n // Expand the list of competencies into a fake tree.\n var i, comp;\n var tree = [];\n for (i = 0; i < competencies.length; i++) {\n comp = competencies[i].competency;\n if (comp.shortname.toLowerCase().indexOf(searchText.toLowerCase()) < 0) {\n continue;\n }\n comp.children = [];\n comp.haschildren = 0;\n tree.push(comp);\n }\n\n self._competencies = tree;\n\n }).fail(Notification.exception);\n };\n\n /**\n * Convenience method to get a plan object.\n *\n * @param {Number} id The plan ID.\n * @return {Object|undefined} The plan.\n * @method _getPlan\n */\n Picker.prototype._getPlan = function(id) {\n var plan;\n $.each(this._plans, function(i, f) {\n if (f.id == id) {\n plan = f;\n return;\n }\n });\n return plan;\n };\n\n /**\n * Load the competencies.\n *\n * @method _loadCompetencies\n * @return {Promise}\n */\n Picker.prototype._loadCompetencies = function() {\n return this._fetchCompetencies(this._planId, this._searchText);\n };\n\n /**\n * Load the plans.\n *\n * @method _loadPlans\n * @return {Promise}\n */\n Picker.prototype._loadPlans = function() {\n var promise,\n self = this;\n\n // Quit early because we already have the data.\n if (self._plans.length > 0) {\n return $.when();\n }\n\n if (self._singlePlan) {\n promise = Ajax.call([\n {methodname: 'core_competency_read_plan', args: {\n id: this._planId\n }}\n ])[0].then(function(plan) {\n return [plan];\n });\n } else {\n promise = Ajax.call([\n {methodname: 'core_competency_list_user_plans', args: {\n userid: self._userId\n }}\n ])[0];\n }\n\n return promise.done(function(plans) {\n self._plans = plans;\n }).fail(Notification.exception);\n };\n\n /**\n * Hook to executed before render.\n *\n * @method _preRender\n * @return {Promise}\n */\n Picker.prototype._preRender = function() {\n var self = this;\n return self._loadPlans().then(function() {\n if (!self._planId && self._plans.length > 0) {\n self._planId = self._plans[0].id;\n }\n\n // We could not set a framework ID, that probably means there are no frameworks accessible.\n if (!self._planId) {\n self._plans = [];\n return $.when();\n }\n\n return self._loadCompetencies();\n });\n };\n\n /**\n * Render the dialogue.\n *\n * @method _render\n * @return {Promise}\n */\n Picker.prototype._render = function() {\n var self = this;\n return self._preRender().then(function() {\n\n if (!self._singlePlan) {\n $.each(self._plans, function(i, plan) {\n if (plan.id == self._planId) {\n plan.selected = true;\n } else {\n plan.selected = false;\n }\n });\n }\n\n var context = {\n competencies: self._competencies,\n plan: self._getPlan(self._planId),\n plans: self._plans,\n search: self._searchText,\n singlePlan: self._singlePlan,\n };\n\n return Templates.render('tool_lp/competency_picker_user_plans', context);\n });\n };\n\n return Picker;\n});\n"],"names":["define","$","Notification","Ajax","Templates","Str","Tree","PickerBase","Picker","userId","singlePlan","multiSelect","prototype","constructor","apply","this","_userId","_plans","_planId","_singlePlan","Object","create","_afterRender","self","arguments","_find","change","e","target","val","_loadCompetencies","then","_refresh","bind","catch","exception","_fetchCompetencies","planId","searchText","call","methodname","args","id","done","competencies","i","comp","tree","length","competency","shortname","toLowerCase","indexOf","children","haschildren","push","_competencies","fail","_getPlan","plan","each","f","_searchText","_loadPlans","when","userid","plans","_preRender","_render","selected","context","search","render"],"mappings":"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"}
\ No newline at end of file
+{"version":3,"file":"competencypicker_user_plans.min.js","sources":["../src/competencypicker_user_plans.js"],"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 * Competency picker from user plans.\n *\n * To handle 'save' events use: picker.on('save').\n *\n * This will receive a object with either a single 'competencyId', or an array in 'competencyIds'\n * depending on the value of multiSelect.\n *\n * @module tool_lp/competencypicker_user_plans\n * @copyright 2015 Frédéric Massart - FMCorz.net\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\ndefine(['jquery',\n 'core/notification',\n 'core/ajax',\n 'core/templates',\n 'core/str',\n 'tool_lp/tree',\n 'tool_lp/competencypicker'\n ],\n function($, Notification, Ajax, Templates, Str, Tree, PickerBase) {\n\n /**\n * Competency picker in plan class.\n *\n * @class tool_lp/competencypicker_user_plans\n * @param {Number} userId\n * @param {Number|false} singlePlan The ID of the plan when limited to one.\n * @param {Boolean} multiSelect Support multi-select in the tree.\n */\n var Picker = function(userId, singlePlan, multiSelect) {\n PickerBase.prototype.constructor.apply(this, [1, false, 'self', multiSelect]);\n this._userId = userId;\n this._plans = [];\n\n if (singlePlan) {\n this._planId = singlePlan;\n this._singlePlan = true;\n }\n };\n Picker.prototype = Object.create(PickerBase.prototype);\n\n /** @property {Array} The list of plans fetched. */\n Picker.prototype._plans = null;\n /** @property {Number} The current plan ID. */\n Picker.prototype._planId = null;\n /** @property {Boolean} Whether we can browse plans or not. */\n Picker.prototype._singlePlan = false;\n /** @property {Number} The user the plans belongs to. */\n Picker.prototype._userId = null;\n\n /**\n * Hook to executed after the view is rendered.\n *\n * @method _afterRender\n */\n Picker.prototype._afterRender = function() {\n var self = this;\n PickerBase.prototype._afterRender.apply(self, arguments);\n\n // Add listener for framework change.\n if (!self._singlePlan) {\n self._find('[data-action=\"chooseplan\"]').change(function(e) {\n self._planId = $(e.target).val();\n self._loadCompetencies().then(self._refresh.bind(self))\n .catch(Notification.exception);\n });\n }\n };\n\n /**\n * Fetch the competencies.\n *\n * @param {Number} planId The planId.\n * @param {String} searchText Limit the competencies to those matching the text.\n * @method _fetchCompetencies\n * @return {Promise} The promise object.\n */\n Picker.prototype._fetchCompetencies = function(planId, searchText) {\n var self = this;\n\n return Ajax.call([\n {methodname: 'core_competency_list_plan_competencies', args: {\n id: planId\n }}\n ])[0].done(function(competencies) {\n\n // Expand the list of competencies into a fake tree.\n var i, comp;\n var tree = [];\n for (i = 0; i < competencies.length; i++) {\n comp = competencies[i].competency;\n if (comp.shortname.toLowerCase().indexOf(searchText.toLowerCase()) < 0) {\n continue;\n }\n comp.children = [];\n comp.haschildren = 0;\n tree.push(comp);\n }\n\n self._competencies = tree;\n\n }).fail(Notification.exception);\n };\n\n /**\n * Convenience method to get a plan object.\n *\n * @param {Number} id The plan ID.\n * @return {Object|undefined} The plan.\n * @method _getPlan\n */\n Picker.prototype._getPlan = function(id) {\n var plan;\n $.each(this._plans, function(i, f) {\n if (f.id == id) {\n plan = f;\n return;\n }\n });\n return plan;\n };\n\n /**\n * Load the competencies.\n *\n * @method _loadCompetencies\n * @return {Promise}\n */\n Picker.prototype._loadCompetencies = function() {\n return this._fetchCompetencies(this._planId, this._searchText);\n };\n\n /**\n * Load the plans.\n *\n * @method _loadPlans\n * @return {Promise}\n */\n Picker.prototype._loadPlans = function() {\n var promise,\n self = this;\n\n // Quit early because we already have the data.\n if (self._plans.length > 0) {\n return $.when();\n }\n\n if (self._singlePlan) {\n promise = Ajax.call([\n {methodname: 'core_competency_read_plan', args: {\n id: this._planId\n }}\n ])[0].then(function(plan) {\n return [plan];\n });\n } else {\n promise = Ajax.call([\n {methodname: 'core_competency_list_user_plans', args: {\n userid: self._userId\n }}\n ])[0];\n }\n\n return promise.done(function(plans) {\n self._plans = plans;\n }).fail(Notification.exception);\n };\n\n /**\n * Hook to executed before render.\n *\n * @method _preRender\n * @return {Promise}\n */\n Picker.prototype._preRender = function() {\n var self = this;\n return self._loadPlans().then(function() {\n if (!self._planId && self._plans.length > 0) {\n self._planId = self._plans[0].id;\n }\n\n // We could not set a framework ID, that probably means there are no frameworks accessible.\n if (!self._planId) {\n self._plans = [];\n return $.when();\n }\n\n return self._loadCompetencies();\n });\n };\n\n /**\n * Render the dialogue.\n *\n * @method _render\n * @return {Promise}\n */\n Picker.prototype._render = function() {\n var self = this;\n return self._preRender().then(function() {\n\n if (!self._singlePlan) {\n $.each(self._plans, function(i, plan) {\n if (plan.id == self._planId) {\n plan.selected = true;\n } else {\n plan.selected = false;\n }\n });\n }\n\n var context = {\n competencies: self._competencies,\n plan: self._getPlan(self._planId),\n plans: self._plans,\n search: self._searchText,\n singlePlan: self._singlePlan,\n };\n\n return Templates.render('tool_lp/competency_picker_user_plans', context);\n });\n };\n\n return Picker;\n});\n"],"names":["define","$","Notification","Ajax","Templates","Str","Tree","PickerBase","Picker","userId","singlePlan","multiSelect","prototype","constructor","apply","this","_userId","_plans","_planId","_singlePlan","Object","create","_afterRender","self","arguments","_find","change","e","target","val","_loadCompetencies","then","_refresh","bind","catch","exception","_fetchCompetencies","planId","searchText","call","methodname","args","id","done","competencies","i","comp","tree","length","competency","shortname","toLowerCase","indexOf","children","haschildren","push","_competencies","fail","_getPlan","plan","each","f","_searchText","_loadPlans","when","userid","plans","_preRender","_render","selected","context","search","render"],"mappings":"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"}
\ No newline at end of file
diff --git a/admin/tool/lp/amd/build/competencyruleconfig.min.js.map b/admin/tool/lp/amd/build/competencyruleconfig.min.js.map
index b288205ebb3..662a2b995d5 100644
--- a/admin/tool/lp/amd/build/competencyruleconfig.min.js.map
+++ b/admin/tool/lp/amd/build/competencyruleconfig.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"competencyruleconfig.min.js","sources":["../src/competencyruleconfig.js"],"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 * Competency rule config.\n *\n * @module tool_lp/competencyruleconfig\n * @copyright 2015 Frédéric Massart - FMCorz.net\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\ndefine(['jquery',\n 'core/notification',\n 'core/templates',\n 'tool_lp/dialogue',\n 'tool_lp/competency_outcomes',\n 'core/str'],\n function($, Notification, Templates, Dialogue, Outcomes, Str) {\n\n /**\n * Competency rule class.\n *\n * When implementing this you should attach a listener to the event 'save'\n * on the instance. E.g.\n *\n * var config = new RuleConfig(tree, modules);\n * config.on('save', function(e, config) { ... });\n *\n * @param {competencytree} tree The competency tree.\n * @param {Array} rulesModules The modules containing the rules: [{ typeName: { amd: amdModule, name: ruleName }}].\n */\n var RuleConfig = function(tree, rulesModules) {\n this._eventNode = $('');\n this._tree = tree;\n this._rulesModules = rulesModules;\n this._setUp();\n };\n\n /** @property {Object} The current competency. */\n RuleConfig.prototype._competency = null;\n /** @property {Node} The node we attach the events to. */\n RuleConfig.prototype._eventNode = null;\n /** @property {Array} Outcomes options. */\n RuleConfig.prototype._outcomesOption = null;\n /** @property {Dialogue} The dialogue. */\n RuleConfig.prototype._popup = null;\n /** @property {Promise} Resolved when the module is ready. */\n RuleConfig.prototype._ready = null;\n /** @property {Array} The rules. */\n RuleConfig.prototype._rules = null;\n /** @property {Array} The rules modules. */\n RuleConfig.prototype._rulesModules = null;\n /** @property {competencytree} The competency tree. */\n RuleConfig.prototype._tree = null;\n\n /**\n * After change.\n *\n * Triggered when a change occured.\n *\n * @method _afterChange\n * @protected\n */\n RuleConfig.prototype._afterChange = function() {\n if (!this._isValid()) {\n this._find('[data-action=\"save\"]').prop('disabled', true);\n } else {\n this._find('[data-action=\"save\"]').prop('disabled', false);\n }\n };\n\n /**\n * After change in rule's config.\n *\n * Triggered when a change occured in a specific rule config.\n *\n * @method _afterRuleConfigChange\n * @protected\n * @param {Event} e\n * @param {Rule} rule\n */\n RuleConfig.prototype._afterRuleConfigChange = function(e, rule) {\n if (rule != this._getRule()) {\n // This rule is not the current one any more, we can ignore.\n return;\n }\n this._afterChange();\n };\n\n /**\n * After render hook.\n *\n * @method _afterRender\n * @protected\n */\n RuleConfig.prototype._afterRender = function() {\n var self = this;\n\n self._find('[name=\"outcome\"]').on('change', function() {\n self._switchedOutcome();\n }).trigger('change');\n\n self._find('[name=\"rule\"]').on('change', function() {\n self._switchedRule();\n }).trigger('change');\n\n self._find('[data-action=\"save\"]').on('click', function() {\n self._trigger('save', self._getConfig());\n self.close();\n });\n\n self._find('[data-action=\"cancel\"]').on('click', function() {\n self.close();\n });\n };\n\n /**\n * Whether the current competency can be configured.\n *\n * @return {Boolean}\n * @method canBeConfigured\n */\n RuleConfig.prototype.canBeConfigured = function() {\n var can = false;\n $.each(this._rules, function(index, rule) {\n if (rule.canConfig()) {\n can = true;\n return;\n }\n });\n return can;\n };\n\n /**\n * Close the dialogue.\n *\n * @method close\n */\n RuleConfig.prototype.close = function() {\n this._popup.close();\n this._popup = null;\n };\n\n /**\n * Opens the picker.\n *\n * @method display\n * @returns {Promise}\n */\n RuleConfig.prototype.display = function() {\n var self = this;\n if (!self._competency) {\n return false;\n }\n return $.when(Str.get_string('competencyrule', 'tool_lp'), self._render())\n .then(function(title, render) {\n self._popup = new Dialogue(\n title,\n render[0],\n self._afterRender.bind(self),\n null,\n false,\n '515px'\n );\n return;\n }).fail(Notification.exception);\n };\n\n /**\n * Find a node in the dialogue.\n *\n * @param {String} selector\n * @return {JQuery}\n * @method _find\n * @protected\n */\n RuleConfig.prototype._find = function(selector) {\n return $(this._popup.getContent()).find(selector);\n };\n\n /**\n * Get the applicable outcome options.\n *\n * @return {Array}\n * @method _getApplicableOutcomesOptions\n * @protected\n */\n RuleConfig.prototype._getApplicableOutcomesOptions = function() {\n var self = this,\n options = [];\n\n $.each(self._outcomesOption, function(index, outcome) {\n options.push({\n code: outcome.code,\n name: outcome.name,\n selected: (outcome.code == self._competency.ruleoutcome) ? true : false,\n });\n });\n\n return options;\n };\n\n /**\n * Get the applicable rules options.\n *\n * @return {Array}\n * @method _getApplicableRulesOptions\n * @protected\n */\n RuleConfig.prototype._getApplicableRulesOptions = function() {\n var self = this,\n options = [];\n\n $.each(self._rules, function(index, rule) {\n if (!rule.canConfig()) {\n return;\n }\n options.push({\n name: self._getRuleName(rule.getType()),\n type: rule.getType(),\n selected: (rule.getType() == self._competency.ruletype) ? true : false,\n });\n });\n\n return options;\n };\n\n /**\n * Get the full config for the competency.\n *\n * @return {Object} Contains rule, ruleoutcome and ruleconfig.\n * @method _getConfig\n * @protected\n */\n RuleConfig.prototype._getConfig = function() {\n var rule = this._getRule();\n return {\n ruletype: rule ? rule.getType() : null,\n ruleconfig: rule ? rule.getConfig() : null,\n ruleoutcome: this._getOutcome()\n };\n };\n\n /**\n * Get the selected outcome code.\n *\n * @return {String}\n * @method _getOutcome\n * @protected\n */\n RuleConfig.prototype._getOutcome = function() {\n return this._find('[name=\"outcome\"]').val();\n };\n\n /**\n * Get the selected rule.\n *\n * @return {null|Rule}\n * @method _getRule\n * @protected\n */\n RuleConfig.prototype._getRule = function() {\n var result,\n type = this._find('[name=\"rule\"]').val();\n\n $.each(this._rules, function(index, rule) {\n if (rule.getType() == type) {\n result = rule;\n return;\n }\n });\n\n return result;\n };\n\n /**\n * Return the name of a rule.\n *\n * @param {String} type The type of a rule.\n * @return {String}\n * @method _getRuleName\n * @protected\n */\n RuleConfig.prototype._getRuleName = function(type) {\n var self = this,\n name;\n $.each(self._rulesModules, function(index, modInfo) {\n if (modInfo.type == type) {\n name = modInfo.name;\n return;\n }\n });\n return name;\n };\n\n /**\n * Initialise the outcomes.\n *\n * @return {Promise}\n * @method _initOutcomes\n * @protected\n */\n RuleConfig.prototype._initOutcomes = function() {\n var self = this;\n return Outcomes.getAll().then(function(outcomes) {\n self._outcomesOption = outcomes;\n return;\n });\n };\n\n /**\n * Initialise the rules.\n *\n * @return {Promise}\n * @method _initRules\n * @protected\n */\n RuleConfig.prototype._initRules = function() {\n var self = this,\n promises = [];\n $.each(self._rules, function(index, rule) {\n var promise = rule.init().then(function() {\n rule.setTargetCompetency(self._competency);\n rule.on('change', self._afterRuleConfigChange.bind(self));\n return;\n }, function() {\n // Upon failure remove the rule, and resolve the promise.\n self._rules.splice(index, 1);\n return $.when();\n });\n promises.push(promise);\n });\n\n return $.when.apply($.when, promises);\n };\n\n /**\n * Whether or not the current config is valid.\n *\n * @return {Boolean}\n * @method _isValid\n * @protected\n */\n RuleConfig.prototype._isValid = function() {\n var outcome = this._getOutcome(),\n rule = this._getRule();\n\n if (outcome == Outcomes.NONE) {\n return true;\n } else if (!rule) {\n return false;\n }\n\n return rule.isValid();\n };\n\n /**\n * Register an event listener.\n *\n * @param {String} type The event type.\n * @param {Function} handler The event listener.\n * @method on\n */\n RuleConfig.prototype.on = function(type, handler) {\n this._eventNode.on(type, handler);\n };\n\n /**\n * Hook to executed before render.\n *\n * @method _preRender\n * @protected\n * @return {Promise}\n */\n RuleConfig.prototype._preRender = function() {\n // We need to have all the information about the rule plugins first.\n return this.ready();\n };\n\n /**\n * Returns a promise that is resolved when the module is ready.\n *\n * @return {Promise}\n * @method ready\n * @protected\n */\n RuleConfig.prototype.ready = function() {\n return this._ready.promise();\n };\n\n /**\n * Render the dialogue.\n *\n * @method _render\n * @protected\n * @return {Promise}\n */\n RuleConfig.prototype._render = function() {\n var self = this;\n return this._preRender().then(function() {\n var config;\n\n if (!self.canBeConfigured()) {\n config = false;\n } else {\n config = {};\n config.outcomes = self._getApplicableOutcomesOptions();\n config.rules = self._getApplicableRulesOptions();\n }\n\n var context = {\n competencyshortname: self._competency.shortname,\n config: config\n };\n\n return Templates.render('tool_lp/competency_rule_config', context);\n });\n };\n\n /**\n * Set the target competency.\n *\n * @param {Number} competencyId The target competency Id.\n * @method setTargetCompetencyId\n */\n RuleConfig.prototype.setTargetCompetencyId = function(competencyId) {\n var self = this;\n self._competency = self._tree.getCompetency(competencyId);\n $.each(self._rules, function(index, rule) {\n rule.setTargetCompetency(self._competency);\n });\n };\n\n /**\n * Set up the instance.\n *\n * @method _setUp\n * @protected\n */\n RuleConfig.prototype._setUp = function() {\n var self = this,\n promises = [],\n modules = [];\n\n self._ready = $.Deferred();\n self._rules = [];\n\n $.each(self._rulesModules, function(index, rule) {\n modules.push(rule.amd);\n });\n\n // Load all the modules.\n require(modules, function() {\n $.each(arguments, function(index, Module) {\n // Instantiate the rule and listen to it.\n var rule = new Module(self._tree);\n self._rules.push(rule);\n });\n\n // Load all the option values.\n promises.push(self._initRules());\n promises.push(self._initOutcomes());\n\n // Ready when everything is done.\n $.when.apply($.when, promises).always(function() {\n self._ready.resolve();\n });\n });\n };\n\n /**\n * Called when the user switches outcome.\n *\n * @method _switchedOutcome\n * @protected\n */\n RuleConfig.prototype._switchedOutcome = function() {\n var self = this,\n type = self._getOutcome();\n\n if (type == Outcomes.NONE) {\n // Reset to defaults.\n self._find('[data-region=\"rule-type\"]').hide()\n .find('[name=\"rule\"]').val(-1);\n self._find('[data-region=\"rule-config\"]').empty().hide();\n self._afterChange();\n return;\n }\n\n self._find('[data-region=\"rule-type\"]').show();\n self._find('[data-region=\"rule-config\"]').show();\n self._afterChange();\n };\n\n /**\n * Called when the user switches rule.\n *\n * @method _switchedRule\n * @protected\n */\n RuleConfig.prototype._switchedRule = function() {\n var self = this,\n container = self._find('[data-region=\"rule-config\"]'),\n rule = self._getRule();\n\n if (!rule) {\n container.empty().hide();\n self._afterChange();\n return;\n }\n rule.injectTemplate(container).then(function() {\n container.show();\n return;\n }).always(function() {\n self._afterChange();\n }).catch(function() {\n container.empty().hide();\n });\n };\n\n /**\n * Trigger an event.\n *\n * @param {String} type The type of event.\n * @param {Object} data The data to pass to the listeners.\n * @method _trigger\n * @protected\n */\n RuleConfig.prototype._trigger = function(type, data) {\n this._eventNode.trigger(type, [data]);\n };\n\n return /** @alias module:tool_lp/competencyruleconfig */ RuleConfig;\n\n});\n"],"names":["define","$","Notification","Templates","Dialogue","Outcomes","Str","RuleConfig","tree","rulesModules","_eventNode","_tree","_rulesModules","_setUp","prototype","_competency","_outcomesOption","_popup","_ready","_rules","_afterChange","this","_isValid","_find","prop","_afterRuleConfigChange","e","rule","_getRule","_afterRender","self","on","_switchedOutcome","trigger","_switchedRule","_trigger","_getConfig","close","canBeConfigured","can","each","index","canConfig","display","when","get_string","_render","then","title","render","bind","fail","exception","selector","getContent","find","_getApplicableOutcomesOptions","options","outcome","push","code","name","selected","ruleoutcome","_getApplicableRulesOptions","_getRuleName","getType","type","ruletype","ruleconfig","getConfig","_getOutcome","val","result","modInfo","_initOutcomes","getAll","outcomes","_initRules","promises","promise","init","setTargetCompetency","splice","apply","NONE","isValid","handler","_preRender","ready","config","rules","context","competencyshortname","shortname","setTargetCompetencyId","competencyId","getCompetency","modules","Deferred","amd","require","arguments","Module","always","resolve","hide","empty","show","container","injectTemplate","catch","data"],"mappings":"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"}
\ No newline at end of file
+{"version":3,"file":"competencyruleconfig.min.js","sources":["../src/competencyruleconfig.js"],"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 * Competency rule config.\n *\n * @module tool_lp/competencyruleconfig\n * @copyright 2015 Frédéric Massart - FMCorz.net\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\ndefine(['jquery',\n 'core/notification',\n 'core/templates',\n 'tool_lp/dialogue',\n 'tool_lp/competency_outcomes',\n 'core/str'],\n function($, Notification, Templates, Dialogue, Outcomes, Str) {\n\n /**\n * Competency rule class.\n *\n * When implementing this you should attach a listener to the event 'save'\n * on the instance. E.g.\n *\n * var config = new RuleConfig(tree, modules);\n * config.on('save', function(e, config) { ... });\n *\n * @param {competencytree} tree The competency tree.\n * @param {Array} rulesModules The modules containing the rules: [{ typeName: { amd: amdModule, name: ruleName }}].\n */\n var RuleConfig = function(tree, rulesModules) {\n this._eventNode = $('');\n this._tree = tree;\n this._rulesModules = rulesModules;\n this._setUp();\n };\n\n /** @property {Object} The current competency. */\n RuleConfig.prototype._competency = null;\n /** @property {Node} The node we attach the events to. */\n RuleConfig.prototype._eventNode = null;\n /** @property {Array} Outcomes options. */\n RuleConfig.prototype._outcomesOption = null;\n /** @property {Dialogue} The dialogue. */\n RuleConfig.prototype._popup = null;\n /** @property {Promise} Resolved when the module is ready. */\n RuleConfig.prototype._ready = null;\n /** @property {Array} The rules. */\n RuleConfig.prototype._rules = null;\n /** @property {Array} The rules modules. */\n RuleConfig.prototype._rulesModules = null;\n /** @property {competencytree} The competency tree. */\n RuleConfig.prototype._tree = null;\n\n /**\n * After change.\n *\n * Triggered when a change occured.\n *\n * @method _afterChange\n * @protected\n */\n RuleConfig.prototype._afterChange = function() {\n if (!this._isValid()) {\n this._find('[data-action=\"save\"]').prop('disabled', true);\n } else {\n this._find('[data-action=\"save\"]').prop('disabled', false);\n }\n };\n\n /**\n * After change in rule's config.\n *\n * Triggered when a change occured in a specific rule config.\n *\n * @method _afterRuleConfigChange\n * @protected\n * @param {Event} e\n * @param {Rule} rule\n */\n RuleConfig.prototype._afterRuleConfigChange = function(e, rule) {\n if (rule != this._getRule()) {\n // This rule is not the current one any more, we can ignore.\n return;\n }\n this._afterChange();\n };\n\n /**\n * After render hook.\n *\n * @method _afterRender\n * @protected\n */\n RuleConfig.prototype._afterRender = function() {\n var self = this;\n\n self._find('[name=\"outcome\"]').on('change', function() {\n self._switchedOutcome();\n }).trigger('change');\n\n self._find('[name=\"rule\"]').on('change', function() {\n self._switchedRule();\n }).trigger('change');\n\n self._find('[data-action=\"save\"]').on('click', function() {\n self._trigger('save', self._getConfig());\n self.close();\n });\n\n self._find('[data-action=\"cancel\"]').on('click', function() {\n self.close();\n });\n };\n\n /**\n * Whether the current competency can be configured.\n *\n * @return {Boolean}\n * @method canBeConfigured\n */\n RuleConfig.prototype.canBeConfigured = function() {\n var can = false;\n $.each(this._rules, function(index, rule) {\n if (rule.canConfig()) {\n can = true;\n return;\n }\n });\n return can;\n };\n\n /**\n * Close the dialogue.\n *\n * @method close\n */\n RuleConfig.prototype.close = function() {\n this._popup.close();\n this._popup = null;\n };\n\n /**\n * Opens the picker.\n *\n * @method display\n * @returns {Promise}\n */\n RuleConfig.prototype.display = function() {\n var self = this;\n if (!self._competency) {\n return false;\n }\n return $.when(Str.get_string('competencyrule', 'tool_lp'), self._render())\n .then(function(title, render) {\n self._popup = new Dialogue(\n title,\n render[0],\n self._afterRender.bind(self),\n null,\n false,\n '515px'\n );\n return;\n }).fail(Notification.exception);\n };\n\n /**\n * Find a node in the dialogue.\n *\n * @param {String} selector\n * @return {JQuery}\n * @method _find\n * @protected\n */\n RuleConfig.prototype._find = function(selector) {\n return $(this._popup.getContent()).find(selector);\n };\n\n /**\n * Get the applicable outcome options.\n *\n * @return {Array}\n * @method _getApplicableOutcomesOptions\n * @protected\n */\n RuleConfig.prototype._getApplicableOutcomesOptions = function() {\n var self = this,\n options = [];\n\n $.each(self._outcomesOption, function(index, outcome) {\n options.push({\n code: outcome.code,\n name: outcome.name,\n selected: (outcome.code == self._competency.ruleoutcome) ? true : false,\n });\n });\n\n return options;\n };\n\n /**\n * Get the applicable rules options.\n *\n * @return {Array}\n * @method _getApplicableRulesOptions\n * @protected\n */\n RuleConfig.prototype._getApplicableRulesOptions = function() {\n var self = this,\n options = [];\n\n $.each(self._rules, function(index, rule) {\n if (!rule.canConfig()) {\n return;\n }\n options.push({\n name: self._getRuleName(rule.getType()),\n type: rule.getType(),\n selected: (rule.getType() == self._competency.ruletype) ? true : false,\n });\n });\n\n return options;\n };\n\n /**\n * Get the full config for the competency.\n *\n * @return {Object} Contains rule, ruleoutcome and ruleconfig.\n * @method _getConfig\n * @protected\n */\n RuleConfig.prototype._getConfig = function() {\n var rule = this._getRule();\n return {\n ruletype: rule ? rule.getType() : null,\n ruleconfig: rule ? rule.getConfig() : null,\n ruleoutcome: this._getOutcome()\n };\n };\n\n /**\n * Get the selected outcome code.\n *\n * @return {String}\n * @method _getOutcome\n * @protected\n */\n RuleConfig.prototype._getOutcome = function() {\n return this._find('[name=\"outcome\"]').val();\n };\n\n /**\n * Get the selected rule.\n *\n * @return {null|Rule}\n * @method _getRule\n * @protected\n */\n RuleConfig.prototype._getRule = function() {\n var result,\n type = this._find('[name=\"rule\"]').val();\n\n $.each(this._rules, function(index, rule) {\n if (rule.getType() == type) {\n result = rule;\n return;\n }\n });\n\n return result;\n };\n\n /**\n * Return the name of a rule.\n *\n * @param {String} type The type of a rule.\n * @return {String}\n * @method _getRuleName\n * @protected\n */\n RuleConfig.prototype._getRuleName = function(type) {\n var self = this,\n name;\n $.each(self._rulesModules, function(index, modInfo) {\n if (modInfo.type == type) {\n name = modInfo.name;\n return;\n }\n });\n return name;\n };\n\n /**\n * Initialise the outcomes.\n *\n * @return {Promise}\n * @method _initOutcomes\n * @protected\n */\n RuleConfig.prototype._initOutcomes = function() {\n var self = this;\n return Outcomes.getAll().then(function(outcomes) {\n self._outcomesOption = outcomes;\n return;\n });\n };\n\n /**\n * Initialise the rules.\n *\n * @return {Promise}\n * @method _initRules\n * @protected\n */\n RuleConfig.prototype._initRules = function() {\n var self = this,\n promises = [];\n $.each(self._rules, function(index, rule) {\n var promise = rule.init().then(function() {\n rule.setTargetCompetency(self._competency);\n rule.on('change', self._afterRuleConfigChange.bind(self));\n return;\n }, function() {\n // Upon failure remove the rule, and resolve the promise.\n self._rules.splice(index, 1);\n return $.when();\n });\n promises.push(promise);\n });\n\n return $.when.apply($.when, promises);\n };\n\n /**\n * Whether or not the current config is valid.\n *\n * @return {Boolean}\n * @method _isValid\n * @protected\n */\n RuleConfig.prototype._isValid = function() {\n var outcome = this._getOutcome(),\n rule = this._getRule();\n\n if (outcome == Outcomes.NONE) {\n return true;\n } else if (!rule) {\n return false;\n }\n\n return rule.isValid();\n };\n\n /**\n * Register an event listener.\n *\n * @param {String} type The event type.\n * @param {Function} handler The event listener.\n * @method on\n */\n RuleConfig.prototype.on = function(type, handler) {\n this._eventNode.on(type, handler);\n };\n\n /**\n * Hook to executed before render.\n *\n * @method _preRender\n * @protected\n * @return {Promise}\n */\n RuleConfig.prototype._preRender = function() {\n // We need to have all the information about the rule plugins first.\n return this.ready();\n };\n\n /**\n * Returns a promise that is resolved when the module is ready.\n *\n * @return {Promise}\n * @method ready\n * @protected\n */\n RuleConfig.prototype.ready = function() {\n return this._ready.promise();\n };\n\n /**\n * Render the dialogue.\n *\n * @method _render\n * @protected\n * @return {Promise}\n */\n RuleConfig.prototype._render = function() {\n var self = this;\n return this._preRender().then(function() {\n var config;\n\n if (!self.canBeConfigured()) {\n config = false;\n } else {\n config = {};\n config.outcomes = self._getApplicableOutcomesOptions();\n config.rules = self._getApplicableRulesOptions();\n }\n\n var context = {\n competencyshortname: self._competency.shortname,\n config: config\n };\n\n return Templates.render('tool_lp/competency_rule_config', context);\n });\n };\n\n /**\n * Set the target competency.\n *\n * @param {Number} competencyId The target competency Id.\n * @method setTargetCompetencyId\n */\n RuleConfig.prototype.setTargetCompetencyId = function(competencyId) {\n var self = this;\n self._competency = self._tree.getCompetency(competencyId);\n $.each(self._rules, function(index, rule) {\n rule.setTargetCompetency(self._competency);\n });\n };\n\n /**\n * Set up the instance.\n *\n * @method _setUp\n * @protected\n */\n RuleConfig.prototype._setUp = function() {\n var self = this,\n promises = [],\n modules = [];\n\n self._ready = $.Deferred();\n self._rules = [];\n\n $.each(self._rulesModules, function(index, rule) {\n modules.push(rule.amd);\n });\n\n // Load all the modules.\n require(modules, function() {\n $.each(arguments, function(index, Module) {\n // Instantiate the rule and listen to it.\n var rule = new Module(self._tree);\n self._rules.push(rule);\n });\n\n // Load all the option values.\n promises.push(self._initRules());\n promises.push(self._initOutcomes());\n\n // Ready when everything is done.\n $.when.apply($.when, promises).always(function() {\n self._ready.resolve();\n });\n });\n };\n\n /**\n * Called when the user switches outcome.\n *\n * @method _switchedOutcome\n * @protected\n */\n RuleConfig.prototype._switchedOutcome = function() {\n var self = this,\n type = self._getOutcome();\n\n if (type == Outcomes.NONE) {\n // Reset to defaults.\n self._find('[data-region=\"rule-type\"]').hide()\n .find('[name=\"rule\"]').val(-1);\n self._find('[data-region=\"rule-config\"]').empty().hide();\n self._afterChange();\n return;\n }\n\n self._find('[data-region=\"rule-type\"]').show();\n self._find('[data-region=\"rule-config\"]').show();\n self._afterChange();\n };\n\n /**\n * Called when the user switches rule.\n *\n * @method _switchedRule\n * @protected\n */\n RuleConfig.prototype._switchedRule = function() {\n var self = this,\n container = self._find('[data-region=\"rule-config\"]'),\n rule = self._getRule();\n\n if (!rule) {\n container.empty().hide();\n self._afterChange();\n return;\n }\n rule.injectTemplate(container).then(function() {\n container.show();\n return;\n }).always(function() {\n self._afterChange();\n }).catch(function() {\n container.empty().hide();\n });\n };\n\n /**\n * Trigger an event.\n *\n * @param {String} type The type of event.\n * @param {Object} data The data to pass to the listeners.\n * @method _trigger\n * @protected\n */\n RuleConfig.prototype._trigger = function(type, data) {\n this._eventNode.trigger(type, [data]);\n };\n\n return /** @alias module:tool_lp/competencyruleconfig */ RuleConfig;\n\n});\n"],"names":["define","$","Notification","Templates","Dialogue","Outcomes","Str","RuleConfig","tree","rulesModules","this","_eventNode","_tree","_rulesModules","_setUp","prototype","_competency","_outcomesOption","_popup","_ready","_rules","_afterChange","_isValid","_find","prop","_afterRuleConfigChange","e","rule","_getRule","_afterRender","self","on","_switchedOutcome","trigger","_switchedRule","_trigger","_getConfig","close","canBeConfigured","can","each","index","canConfig","display","when","get_string","_render","then","title","render","bind","fail","exception","selector","getContent","find","_getApplicableOutcomesOptions","options","outcome","push","code","name","selected","ruleoutcome","_getApplicableRulesOptions","_getRuleName","getType","type","ruletype","ruleconfig","getConfig","_getOutcome","val","result","modInfo","_initOutcomes","getAll","outcomes","_initRules","promises","promise","init","setTargetCompetency","splice","apply","NONE","isValid","handler","_preRender","ready","config","rules","context","competencyshortname","shortname","setTargetCompetencyId","competencyId","getCompetency","modules","Deferred","amd","require","arguments","Module","always","resolve","hide","empty","show","container","injectTemplate","catch","data"],"mappings":"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"}
\ No newline at end of file
diff --git a/admin/tool/lp/amd/build/competencytree.min.js.map b/admin/tool/lp/amd/build/competencytree.min.js.map
index 30b8cbf7e8e..c28221adb54 100644
--- a/admin/tool/lp/amd/build/competencytree.min.js.map
+++ b/admin/tool/lp/amd/build/competencytree.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"competencytree.min.js","sources":["../src/competencytree.js"],"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 * Handle selection changes on the competency tree.\n *\n * @module tool_lp/competencyselect\n * @copyright 2015 Damyon Wiese \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(['core/ajax', 'core/notification', 'core/templates', 'tool_lp/tree', 'tool_lp/competency_outcomes', 'jquery'],\n function(ajax, notification, templates, Ariatree, CompOutcomes, $) {\n\n // Private variables and functions.\n /** @var {Object[]} competencies - Cached list of competencies */\n var competencies = {};\n\n /** @var {Number} competencyFrameworkId - The current framework id */\n var competencyFrameworkId = 0;\n\n /** @var {String} competencyFrameworkShortName - The current framework short name */\n var competencyFrameworkShortName = '';\n\n /** @var {String} treeSelector - The selector for the root of the tree. */\n var treeSelector = '';\n\n /** @var {String} currentNodeId - The data-id of the current node in the tree. */\n var currentNodeId = '';\n\n /** @var {Boolean} competencyFramworkCanManage - Can manage the competencies framework */\n var competencyFramworkCanManage = false;\n\n /**\n * Build a tree from the flat list of competencies.\n * @param {Object} parent The parent competency.\n * @param {Array} all The list of all competencies.\n */\n var addChildren = function(parent, all) {\n var i = 0;\n var current = false;\n parent.haschildren = false;\n parent.children = [];\n for (i = 0; i < all.length; i++) {\n current = all[i];\n if (current.parentid == parent.id) {\n parent.haschildren = true;\n parent.children.push(current);\n addChildren(current, all);\n }\n }\n };\n\n /**\n * Load the list of competencies via ajax. Competencies are filtered by the searchtext.\n * @param {String} searchtext The text to filter on.\n * @return {promise}\n */\n var loadCompetencies = function(searchtext) {\n var deferred = $.Deferred();\n\n templates.render('tool_lp/loading', {}).done(function(loadinghtml, loadingjs) {\n templates.replaceNodeContents($(treeSelector), loadinghtml, loadingjs);\n\n var promises = ajax.call([{\n methodname: 'core_competency_search_competencies',\n args: {\n searchtext: searchtext,\n competencyframeworkid: competencyFrameworkId\n }\n }]);\n promises[0].done(function(result) {\n competencies = {};\n var i = 0;\n for (i = 0; i < result.length; i++) {\n competencies[result[i].id] = result[i];\n }\n\n var children = [];\n var competency = false;\n for (i = 0; i < result.length; i++) {\n competency = result[i];\n if (parseInt(competency.parentid, 10) === 0) {\n children.push(competency);\n addChildren(competency, result);\n }\n }\n var context = {\n shortname: competencyFrameworkShortName,\n canmanage: competencyFramworkCanManage,\n competencies: children\n };\n templates.render('tool_lp/competencies_tree_root', context).done(function(html, js) {\n templates.replaceNodeContents($(treeSelector), $(html).html(), js);\n var tree = new Ariatree(treeSelector, false);\n\n if (currentNodeId) {\n var node = $(treeSelector).find('[data-id=' + currentNodeId + ']');\n if (node.length) {\n tree.selectItem(node);\n tree.updateFocus(node);\n }\n }\n deferred.resolve(competencies);\n }).fail(deferred.reject);\n }).fail(deferred.reject);\n });\n\n return deferred.promise();\n };\n\n /**\n * Whenever the current item in the tree is changed - remember the \"id\".\n * @param {Event} evt\n * @param {Object} params The parameters for the event (This is the selected node).\n */\n var rememberCurrent = function(evt, params) {\n var node = params.selected;\n currentNodeId = node.attr('data-id');\n };\n\n return /** @alias module:tool_lp/competencytree */ {\n // Public variables and functions.\n /**\n * Initialise the tree.\n *\n * @param {Number} id The competency framework id.\n * @param {String} shortname The framework shortname\n * @param {String} search The current search string\n * @param {String} selector The selector for the tree div\n * @param {Boolean} canmanage Can manage the competencies\n * @param {Number} competencyid The id of the competency to show first\n */\n init: function(id, shortname, search, selector, canmanage, competencyid) {\n competencyFrameworkId = id;\n competencyFrameworkShortName = shortname;\n competencyFramworkCanManage = canmanage;\n treeSelector = selector;\n loadCompetencies(search).fail(notification.exception);\n if (competencyid > 0) {\n currentNodeId = competencyid;\n }\n\n this.on('selectionchanged', rememberCurrent);\n },\n\n /**\n * Add an event handler for custom events emitted by the tree.\n *\n * @param {String} eventname The name of the event - only \"selectionchanged\" for now\n * @param {Function} handler The handler for the event.\n */\n on: function(eventname, handler) {\n // We can't use the tree on function directly\n // because the tree gets rebuilt whenever the search string changes,\n // instead we attach the listner to the root node of the tree which never\n // gets destroyed (same as \"on()\" code in the tree.js).\n $(treeSelector).on(eventname, handler);\n },\n\n /**\n * Get the children of a competency.\n *\n * @param {Number} id The competency ID.\n * @return {Array}\n * @method getChildren\n */\n getChildren: function(id) {\n var children = [];\n $.each(competencies, function(index, competency) {\n if (competency.parentid == id) {\n children.push(competency);\n }\n });\n return children;\n },\n\n /**\n * Get the competency framework id this model was initiliased with.\n *\n * @return {Number}\n */\n getCompetencyFrameworkId: function() {\n return competencyFrameworkId;\n },\n\n /**\n * Get a competency by id\n *\n * @param {Number} id The competency id\n * @return {Object}\n */\n getCompetency: function(id) {\n return competencies[id];\n },\n\n /**\n * Get the competency level.\n *\n * @param {Number} id The competency ID.\n * @return {Number}\n */\n getCompetencyLevel: function(id) {\n var competency = this.getCompetency(id),\n level = competency.path.replace(/^\\/|\\/$/g, '').split('/').length;\n return level;\n },\n\n /**\n * Whether a competency has children.\n *\n * @param {Number} id The competency ID.\n * @return {Boolean}\n * @method hasChildren\n */\n hasChildren: function(id) {\n return this.getChildren(id).length > 0;\n },\n\n /**\n * Does the competency have a rule?\n *\n * @param {Number} id The competency ID.\n * @return {Boolean}\n */\n hasRule: function(id) {\n var comp = this.getCompetency(id);\n if (comp) {\n return comp.ruleoutcome != CompOutcomes.OUTCOME_NONE\n && comp.ruletype;\n }\n return false;\n },\n\n /**\n * Reload all the page competencies framework competencies.\n * @method reloadCompetencies\n * @return {Promise}\n */\n reloadCompetencies: function() {\n return loadCompetencies('').fail(notification.exception);\n },\n\n /**\n * Get all competencies for this framework.\n *\n * @return {Object[]}\n */\n listCompetencies: function() {\n return competencies;\n },\n\n };\n });\n"],"names":["define","ajax","notification","templates","Ariatree","CompOutcomes","$","competencies","competencyFrameworkId","competencyFrameworkShortName","treeSelector","currentNodeId","competencyFramworkCanManage","addChildren","parent","all","i","current","haschildren","children","length","parentid","id","push","loadCompetencies","searchtext","deferred","Deferred","render","done","loadinghtml","loadingjs","replaceNodeContents","call","methodname","args","competencyframeworkid","result","competency","parseInt","context","shortname","canmanage","html","js","tree","node","find","selectItem","updateFocus","resolve","fail","reject","promise","rememberCurrent","evt","params","selected","attr","init","search","selector","competencyid","exception","on","eventname","handler","getChildren","each","index","getCompetencyFrameworkId","getCompetency","getCompetencyLevel","this","path","replace","split","hasChildren","hasRule","comp","ruleoutcome","OUTCOME_NONE","ruletype","reloadCompetencies","listCompetencies"],"mappings":"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"}
\ No newline at end of file
+{"version":3,"file":"competencytree.min.js","sources":["../src/competencytree.js"],"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 * Handle selection changes on the competency tree.\n *\n * @module tool_lp/competencyselect\n * @copyright 2015 Damyon Wiese \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(['core/ajax', 'core/notification', 'core/templates', 'tool_lp/tree', 'tool_lp/competency_outcomes', 'jquery'],\n function(ajax, notification, templates, Ariatree, CompOutcomes, $) {\n\n // Private variables and functions.\n /** @var {Object[]} competencies - Cached list of competencies */\n var competencies = {};\n\n /** @var {Number} competencyFrameworkId - The current framework id */\n var competencyFrameworkId = 0;\n\n /** @var {String} competencyFrameworkShortName - The current framework short name */\n var competencyFrameworkShortName = '';\n\n /** @var {String} treeSelector - The selector for the root of the tree. */\n var treeSelector = '';\n\n /** @var {String} currentNodeId - The data-id of the current node in the tree. */\n var currentNodeId = '';\n\n /** @var {Boolean} competencyFramworkCanManage - Can manage the competencies framework */\n var competencyFramworkCanManage = false;\n\n /**\n * Build a tree from the flat list of competencies.\n * @param {Object} parent The parent competency.\n * @param {Array} all The list of all competencies.\n */\n var addChildren = function(parent, all) {\n var i = 0;\n var current = false;\n parent.haschildren = false;\n parent.children = [];\n for (i = 0; i < all.length; i++) {\n current = all[i];\n if (current.parentid == parent.id) {\n parent.haschildren = true;\n parent.children.push(current);\n addChildren(current, all);\n }\n }\n };\n\n /**\n * Load the list of competencies via ajax. Competencies are filtered by the searchtext.\n * @param {String} searchtext The text to filter on.\n * @return {promise}\n */\n var loadCompetencies = function(searchtext) {\n var deferred = $.Deferred();\n\n templates.render('tool_lp/loading', {}).done(function(loadinghtml, loadingjs) {\n templates.replaceNodeContents($(treeSelector), loadinghtml, loadingjs);\n\n var promises = ajax.call([{\n methodname: 'core_competency_search_competencies',\n args: {\n searchtext: searchtext,\n competencyframeworkid: competencyFrameworkId\n }\n }]);\n promises[0].done(function(result) {\n competencies = {};\n var i = 0;\n for (i = 0; i < result.length; i++) {\n competencies[result[i].id] = result[i];\n }\n\n var children = [];\n var competency = false;\n for (i = 0; i < result.length; i++) {\n competency = result[i];\n if (parseInt(competency.parentid, 10) === 0) {\n children.push(competency);\n addChildren(competency, result);\n }\n }\n var context = {\n shortname: competencyFrameworkShortName,\n canmanage: competencyFramworkCanManage,\n competencies: children\n };\n templates.render('tool_lp/competencies_tree_root', context).done(function(html, js) {\n templates.replaceNodeContents($(treeSelector), $(html).html(), js);\n var tree = new Ariatree(treeSelector, false);\n\n if (currentNodeId) {\n var node = $(treeSelector).find('[data-id=' + currentNodeId + ']');\n if (node.length) {\n tree.selectItem(node);\n tree.updateFocus(node);\n }\n }\n deferred.resolve(competencies);\n }).fail(deferred.reject);\n }).fail(deferred.reject);\n });\n\n return deferred.promise();\n };\n\n /**\n * Whenever the current item in the tree is changed - remember the \"id\".\n * @param {Event} evt\n * @param {Object} params The parameters for the event (This is the selected node).\n */\n var rememberCurrent = function(evt, params) {\n var node = params.selected;\n currentNodeId = node.attr('data-id');\n };\n\n return /** @alias module:tool_lp/competencytree */ {\n // Public variables and functions.\n /**\n * Initialise the tree.\n *\n * @param {Number} id The competency framework id.\n * @param {String} shortname The framework shortname\n * @param {String} search The current search string\n * @param {String} selector The selector for the tree div\n * @param {Boolean} canmanage Can manage the competencies\n * @param {Number} competencyid The id of the competency to show first\n */\n init: function(id, shortname, search, selector, canmanage, competencyid) {\n competencyFrameworkId = id;\n competencyFrameworkShortName = shortname;\n competencyFramworkCanManage = canmanage;\n treeSelector = selector;\n loadCompetencies(search).fail(notification.exception);\n if (competencyid > 0) {\n currentNodeId = competencyid;\n }\n\n this.on('selectionchanged', rememberCurrent);\n },\n\n /**\n * Add an event handler for custom events emitted by the tree.\n *\n * @param {String} eventname The name of the event - only \"selectionchanged\" for now\n * @param {Function} handler The handler for the event.\n */\n on: function(eventname, handler) {\n // We can't use the tree on function directly\n // because the tree gets rebuilt whenever the search string changes,\n // instead we attach the listner to the root node of the tree which never\n // gets destroyed (same as \"on()\" code in the tree.js).\n $(treeSelector).on(eventname, handler);\n },\n\n /**\n * Get the children of a competency.\n *\n * @param {Number} id The competency ID.\n * @return {Array}\n * @method getChildren\n */\n getChildren: function(id) {\n var children = [];\n $.each(competencies, function(index, competency) {\n if (competency.parentid == id) {\n children.push(competency);\n }\n });\n return children;\n },\n\n /**\n * Get the competency framework id this model was initiliased with.\n *\n * @return {Number}\n */\n getCompetencyFrameworkId: function() {\n return competencyFrameworkId;\n },\n\n /**\n * Get a competency by id\n *\n * @param {Number} id The competency id\n * @return {Object}\n */\n getCompetency: function(id) {\n return competencies[id];\n },\n\n /**\n * Get the competency level.\n *\n * @param {Number} id The competency ID.\n * @return {Number}\n */\n getCompetencyLevel: function(id) {\n var competency = this.getCompetency(id),\n level = competency.path.replace(/^\\/|\\/$/g, '').split('/').length;\n return level;\n },\n\n /**\n * Whether a competency has children.\n *\n * @param {Number} id The competency ID.\n * @return {Boolean}\n * @method hasChildren\n */\n hasChildren: function(id) {\n return this.getChildren(id).length > 0;\n },\n\n /**\n * Does the competency have a rule?\n *\n * @param {Number} id The competency ID.\n * @return {Boolean}\n */\n hasRule: function(id) {\n var comp = this.getCompetency(id);\n if (comp) {\n return comp.ruleoutcome != CompOutcomes.OUTCOME_NONE\n && comp.ruletype;\n }\n return false;\n },\n\n /**\n * Reload all the page competencies framework competencies.\n * @method reloadCompetencies\n * @return {Promise}\n */\n reloadCompetencies: function() {\n return loadCompetencies('').fail(notification.exception);\n },\n\n /**\n * Get all competencies for this framework.\n *\n * @return {Object[]}\n */\n listCompetencies: function() {\n return competencies;\n },\n\n };\n });\n"],"names":["define","ajax","notification","templates","Ariatree","CompOutcomes","$","competencies","competencyFrameworkId","competencyFrameworkShortName","treeSelector","currentNodeId","competencyFramworkCanManage","addChildren","parent","all","i","current","haschildren","children","length","parentid","id","push","loadCompetencies","searchtext","deferred","Deferred","render","done","loadinghtml","loadingjs","replaceNodeContents","call","methodname","args","competencyframeworkid","result","competency","parseInt","context","shortname","canmanage","html","js","tree","node","find","selectItem","updateFocus","resolve","fail","reject","promise","rememberCurrent","evt","params","selected","attr","init","search","selector","competencyid","exception","this","on","eventname","handler","getChildren","each","index","getCompetencyFrameworkId","getCompetency","getCompetencyLevel","path","replace","split","hasChildren","hasRule","comp","ruleoutcome","OUTCOME_NONE","ruletype","reloadCompetencies","listCompetencies"],"mappings":"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"}
\ No newline at end of file
diff --git a/admin/tool/lp/amd/build/course_competency_settings.min.js.map b/admin/tool/lp/amd/build/course_competency_settings.min.js.map
index f473cf570da..7769451a3ca 100644
--- a/admin/tool/lp/amd/build/course_competency_settings.min.js.map
+++ b/admin/tool/lp/amd/build/course_competency_settings.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"course_competency_settings.min.js","sources":["../src/course_competency_settings.js"],"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 * Change the course competency settings in a popup.\n *\n * @module tool_lp/configurecoursecompetencysettings\n * @copyright 2015 Damyon Wiese \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(['jquery',\n 'core/notification',\n 'tool_lp/dialogue',\n 'core/str',\n 'core/ajax',\n 'core/templates',\n 'core/pending'\n ],\n function($, notification, Dialogue, str, ajax, templates, Pending) {\n\n /**\n * Constructor\n *\n * @param {String} selector - selector for the links to open the dialogue.\n */\n var settingsMod = function(selector) {\n $(selector).on('click', this.configureSettings.bind(this));\n };\n\n /** @property {Dialogue} Reference to the dialogue that we opened. */\n settingsMod.prototype._dialogue = null;\n\n /**\n * Open the configure settings dialogue.\n *\n * @param {Event} e\n * @method configureSettings\n */\n settingsMod.prototype.configureSettings = function(e) {\n var pendingPromise = new Pending();\n var courseid = $(e.target).closest('a').data('courseid');\n var currentValue = $(e.target).closest('a').data('pushratingstouserplans');\n var context = {\n courseid: courseid,\n settings: {pushratingstouserplans: currentValue}\n };\n e.preventDefault();\n\n $.when(\n str.get_string('configurecoursecompetencysettings', 'tool_lp'),\n templates.render('tool_lp/course_competency_settings', context),\n )\n .then(function(title, templateResult) {\n this._dialogue = new Dialogue(\n title,\n templateResult[0],\n this.addListeners.bind(this)\n );\n\n return this._dialogue;\n }.bind(this))\n .then(pendingPromise.resolve)\n .catch(notification.exception);\n };\n\n /**\n * Add the save listener to the form.\n *\n * @method addSaveListener\n */\n settingsMod.prototype.addListeners = function() {\n var save = this._find('[data-action=\"save\"]');\n save.on('click', this.saveSettings.bind(this));\n var cancel = this._find('[data-action=\"cancel\"]');\n cancel.on('click', this.cancelChanges.bind(this));\n };\n\n /**\n * Cancel the changes.\n *\n * @param {Event} e\n * @method cancelChanges\n */\n settingsMod.prototype.cancelChanges = function(e) {\n e.preventDefault();\n this._dialogue.close();\n };\n\n /**\n * Cancel the changes.\n *\n * @param {String} selector\n * @return {JQuery}\n */\n settingsMod.prototype._find = function(selector) {\n return $('[data-region=\"coursecompetencysettings\"]').find(selector);\n };\n\n /**\n * Save the settings.\n *\n * @param {Event} e\n * @method saveSettings\n */\n settingsMod.prototype.saveSettings = function(e) {\n var pendingPromise = new Pending();\n e.preventDefault();\n\n var newValue = this._find('input[name=\"pushratingstouserplans\"]:checked').val();\n var courseId = this._find('input[name=\"courseid\"]').val();\n var settings = {pushratingstouserplans: newValue};\n\n ajax.call([\n {methodname: 'core_competency_update_course_competency_settings',\n args: {courseid: courseId, settings: settings}}\n ])[0]\n .then(function() {\n return this.refreshCourseCompetenciesPage();\n }.bind(this))\n .then(pendingPromise.resolve)\n .catch(notification.exception);\n\n };\n\n /**\n * Refresh the course competencies page.\n *\n * @method saveSettings\n */\n settingsMod.prototype.refreshCourseCompetenciesPage = function() {\n var courseId = this._find('input[name=\"courseid\"]').val();\n var pendingPromise = new Pending();\n\n ajax.call([\n {methodname: 'tool_lp_data_for_course_competencies_page',\n args: {courseid: courseId, moduleid: 0}}\n ])[0]\n .then(function(context) {\n return templates.render('tool_lp/course_competencies_page', context);\n })\n .then(function(html, js) {\n templates.replaceNode($('[data-region=\"coursecompetenciespage\"]'), html, js);\n this._dialogue.close();\n\n return;\n }.bind(this))\n .then(pendingPromise.resolve)\n .catch(notification.exception);\n };\n\n return /** @alias module:tool_lp/configurecoursecompetencysettings */ settingsMod;\n});\n"],"names":["define","$","notification","Dialogue","str","ajax","templates","Pending","settingsMod","selector","on","this","configureSettings","bind","prototype","_dialogue","e","pendingPromise","context","courseid","target","closest","data","settings","pushratingstouserplans","preventDefault","when","get_string","render","then","title","templateResult","addListeners","resolve","catch","exception","_find","saveSettings","cancelChanges","close","find","newValue","val","courseId","call","methodname","args","refreshCourseCompetenciesPage","moduleid","html","js","replaceNode"],"mappings":";;;;;;;AAsBAA,4CAAO,CAAC,SACA,oBACA,mBACA,WACA,YACA,iBACA,iBAED,SAASC,EAAGC,aAAcC,SAAUC,IAAKC,KAAMC,UAAWC,aAOzDC,YAAc,SAASC,UACvBR,EAAEQ,UAAUC,GAAG,QAASC,KAAKC,kBAAkBC,KAAKF,eAIxDH,YAAYM,UAAUC,UAAY,KAQlCP,YAAYM,UAAUF,kBAAoB,SAASI,OAC3CC,eAAiB,IAAIV,QAGrBW,QAAU,CACVC,SAHWlB,EAAEe,EAAEI,QAAQC,QAAQ,KAAKC,KAAK,YAIzCC,SAAU,CAACC,uBAHIvB,EAAEe,EAAEI,QAAQC,QAAQ,KAAKC,KAAK,4BAKjDN,EAAES,iBAEFxB,EAAEyB,KACEtB,IAAIuB,WAAW,oCAAqC,WACpDrB,UAAUsB,OAAO,qCAAsCV,UAE1DW,KAAK,SAASC,MAAOC,4BACbhB,UAAY,IAAIZ,SACjB2B,MACAC,eAAe,GACfpB,KAAKqB,aAAanB,KAAKF,OAGpBA,KAAKI,WACdF,KAAKF,OACNkB,KAAKZ,eAAegB,SACpBC,MAAMhC,aAAaiC,YAQxB3B,YAAYM,UAAUkB,aAAe,WACtBrB,KAAKyB,MAAM,wBACjB1B,GAAG,QAASC,KAAK0B,aAAaxB,KAAKF,OAC3BA,KAAKyB,MAAM,0BACjB1B,GAAG,QAASC,KAAK2B,cAAczB,KAAKF,QAS/CH,YAAYM,UAAUwB,cAAgB,SAAStB,GAC3CA,EAAES,sBACGV,UAAUwB,SASnB/B,YAAYM,UAAUsB,MAAQ,SAAS3B,iBAC5BR,EAAE,4CAA4CuC,KAAK/B,WAS9DD,YAAYM,UAAUuB,aAAe,SAASrB,OACtCC,eAAiB,IAAIV,QACzBS,EAAES,qBAEEgB,SAAW9B,KAAKyB,MAAM,gDAAgDM,MACtEC,SAAWhC,KAAKyB,MAAM,0BAA0BM,MAChDnB,SAAW,CAACC,uBAAwBiB,UAExCpC,KAAKuC,KAAK,CACN,CAACC,WAAY,oDACXC,KAAM,CAAC3B,SAAUwB,SAAUpB,SAAUA,aACxC,GACFM,KAAK,kBACKlB,KAAKoC,iCACdlC,KAAKF,OACNkB,KAAKZ,eAAegB,SACpBC,MAAMhC,aAAaiC,YASxB3B,YAAYM,UAAUiC,8BAAgC,eAC9CJ,SAAWhC,KAAKyB,MAAM,0BAA0BM,MAChDzB,eAAiB,IAAIV,QAEzBF,KAAKuC,KAAK,CACN,CAACC,WAAY,4CACXC,KAAM,CAAC3B,SAAUwB,SAAUK,SAAU,MACxC,GACFnB,MAAK,SAASX,gBACJZ,UAAUsB,OAAO,mCAAoCV,YAE/DW,KAAK,SAASoB,KAAMC,IACjB5C,UAAU6C,YAAYlD,EAAE,0CAA2CgD,KAAMC,SACpEnC,UAAUwB,SAGjB1B,KAAKF,OACNkB,KAAKZ,eAAegB,SACpBC,MAAMhC,aAAaiC,YAG8C3B"}
\ No newline at end of file
+{"version":3,"file":"course_competency_settings.min.js","sources":["../src/course_competency_settings.js"],"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 * Change the course competency settings in a popup.\n *\n * @module tool_lp/configurecoursecompetencysettings\n * @copyright 2015 Damyon Wiese \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(['jquery',\n 'core/notification',\n 'tool_lp/dialogue',\n 'core/str',\n 'core/ajax',\n 'core/templates',\n 'core/pending'\n ],\n function($, notification, Dialogue, str, ajax, templates, Pending) {\n\n /**\n * Constructor\n *\n * @param {String} selector - selector for the links to open the dialogue.\n */\n var settingsMod = function(selector) {\n $(selector).on('click', this.configureSettings.bind(this));\n };\n\n /** @property {Dialogue} Reference to the dialogue that we opened. */\n settingsMod.prototype._dialogue = null;\n\n /**\n * Open the configure settings dialogue.\n *\n * @param {Event} e\n * @method configureSettings\n */\n settingsMod.prototype.configureSettings = function(e) {\n var pendingPromise = new Pending();\n var courseid = $(e.target).closest('a').data('courseid');\n var currentValue = $(e.target).closest('a').data('pushratingstouserplans');\n var context = {\n courseid: courseid,\n settings: {pushratingstouserplans: currentValue}\n };\n e.preventDefault();\n\n $.when(\n str.get_string('configurecoursecompetencysettings', 'tool_lp'),\n templates.render('tool_lp/course_competency_settings', context),\n )\n .then(function(title, templateResult) {\n this._dialogue = new Dialogue(\n title,\n templateResult[0],\n this.addListeners.bind(this)\n );\n\n return this._dialogue;\n }.bind(this))\n .then(pendingPromise.resolve)\n .catch(notification.exception);\n };\n\n /**\n * Add the save listener to the form.\n *\n * @method addSaveListener\n */\n settingsMod.prototype.addListeners = function() {\n var save = this._find('[data-action=\"save\"]');\n save.on('click', this.saveSettings.bind(this));\n var cancel = this._find('[data-action=\"cancel\"]');\n cancel.on('click', this.cancelChanges.bind(this));\n };\n\n /**\n * Cancel the changes.\n *\n * @param {Event} e\n * @method cancelChanges\n */\n settingsMod.prototype.cancelChanges = function(e) {\n e.preventDefault();\n this._dialogue.close();\n };\n\n /**\n * Cancel the changes.\n *\n * @param {String} selector\n * @return {JQuery}\n */\n settingsMod.prototype._find = function(selector) {\n return $('[data-region=\"coursecompetencysettings\"]').find(selector);\n };\n\n /**\n * Save the settings.\n *\n * @param {Event} e\n * @method saveSettings\n */\n settingsMod.prototype.saveSettings = function(e) {\n var pendingPromise = new Pending();\n e.preventDefault();\n\n var newValue = this._find('input[name=\"pushratingstouserplans\"]:checked').val();\n var courseId = this._find('input[name=\"courseid\"]').val();\n var settings = {pushratingstouserplans: newValue};\n\n ajax.call([\n {methodname: 'core_competency_update_course_competency_settings',\n args: {courseid: courseId, settings: settings}}\n ])[0]\n .then(function() {\n return this.refreshCourseCompetenciesPage();\n }.bind(this))\n .then(pendingPromise.resolve)\n .catch(notification.exception);\n\n };\n\n /**\n * Refresh the course competencies page.\n *\n * @method saveSettings\n */\n settingsMod.prototype.refreshCourseCompetenciesPage = function() {\n var courseId = this._find('input[name=\"courseid\"]').val();\n var pendingPromise = new Pending();\n\n ajax.call([\n {methodname: 'tool_lp_data_for_course_competencies_page',\n args: {courseid: courseId, moduleid: 0}}\n ])[0]\n .then(function(context) {\n return templates.render('tool_lp/course_competencies_page', context);\n })\n .then(function(html, js) {\n templates.replaceNode($('[data-region=\"coursecompetenciespage\"]'), html, js);\n this._dialogue.close();\n\n return;\n }.bind(this))\n .then(pendingPromise.resolve)\n .catch(notification.exception);\n };\n\n return /** @alias module:tool_lp/configurecoursecompetencysettings */ settingsMod;\n});\n"],"names":["define","$","notification","Dialogue","str","ajax","templates","Pending","settingsMod","selector","on","this","configureSettings","bind","prototype","_dialogue","e","pendingPromise","context","courseid","target","closest","data","settings","pushratingstouserplans","preventDefault","when","get_string","render","then","title","templateResult","addListeners","resolve","catch","exception","_find","saveSettings","cancelChanges","close","find","newValue","val","courseId","call","methodname","args","refreshCourseCompetenciesPage","moduleid","html","js","replaceNode"],"mappings":";;;;;;;AAsBAA,OAAO,qCAAA,CAAC,SACA,oBACA,mBACA,WACA,YACA,iBACA,iBAED,SAASC,EAAGC,aAAcC,SAAUC,IAAKC,KAAMC,UAAWC,SAO7D,IAAIC,YAAc,SAASC,UACvBR,EAAEQ,UAAUC,GAAG,QAASC,KAAKC,kBAAkBC,KAAKF,QA4HxD,OAxHAH,YAAYM,UAAUC,UAAY,KAQlCP,YAAYM,UAAUF,kBAAoB,SAASI,GAC/C,IAAIC,eAAiB,IAAIV,QAGrBW,QAAU,CACVC,SAHWlB,EAAEe,EAAEI,QAAQC,QAAQ,KAAKC,KAAK,YAIzCC,SAAU,CAACC,uBAHIvB,EAAEe,EAAEI,QAAQC,QAAQ,KAAKC,KAAK,4BAKjDN,EAAES,iBAEFxB,EAAEyB,KACEtB,IAAIuB,WAAW,oCAAqC,WACpDrB,UAAUsB,OAAO,qCAAsCV,UAE1DW,KAAK,SAASC,MAAOC,gBAOlB,OANApB,KAAKI,UAAY,IAAIZ,SACjB2B,MACAC,eAAe,GACfpB,KAAKqB,aAAanB,KAAKF,OAGpBA,KAAKI,SACf,EAACF,KAAKF,OACNkB,KAAKZ,eAAegB,SACpBC,MAAMhC,aAAaiC,YAQxB3B,YAAYM,UAAUkB,aAAe,WACtBrB,KAAKyB,MAAM,wBACjB1B,GAAG,QAASC,KAAK0B,aAAaxB,KAAKF,OAC3BA,KAAKyB,MAAM,0BACjB1B,GAAG,QAASC,KAAK2B,cAAczB,KAAKF,QAS/CH,YAAYM,UAAUwB,cAAgB,SAAStB,GAC3CA,EAAES,iBACFd,KAAKI,UAAUwB,SASnB/B,YAAYM,UAAUsB,MAAQ,SAAS3B,UACnC,OAAOR,EAAE,4CAA4CuC,KAAK/B,WAS9DD,YAAYM,UAAUuB,aAAe,SAASrB,GAC1C,IAAIC,eAAiB,IAAIV,QACzBS,EAAES,iBAEF,IAAIgB,SAAW9B,KAAKyB,MAAM,gDAAgDM,MACtEC,SAAWhC,KAAKyB,MAAM,0BAA0BM,MAChDnB,SAAW,CAACC,uBAAwBiB,UAExCpC,KAAKuC,KAAK,CACN,CAACC,WAAY,oDACXC,KAAM,CAAC3B,SAAUwB,SAAUpB,SAAUA,aACxC,GACFM,KAAK,WACF,OAAOlB,KAAKoC,+BACf,EAAClC,KAAKF,OACNkB,KAAKZ,eAAegB,SACpBC,MAAMhC,aAAaiC,YASxB3B,YAAYM,UAAUiC,8BAAgC,WAClD,IAAIJ,SAAWhC,KAAKyB,MAAM,0BAA0BM,MAChDzB,eAAiB,IAAIV,QAEzBF,KAAKuC,KAAK,CACN,CAACC,WAAY,4CACXC,KAAM,CAAC3B,SAAUwB,SAAUK,SAAU,MACxC,GACFnB,MAAK,SAASX,SACX,OAAOZ,UAAUsB,OAAO,mCAAoCV,QAC/D,IACAW,KAAK,SAASoB,KAAMC,IACjB5C,UAAU6C,YAAYlD,EAAE,0CAA2CgD,KAAMC,IACzEvC,KAAKI,UAAUwB,OAGlB,EAAC1B,KAAKF,OACNkB,KAAKZ,eAAegB,SACpBC,MAAMhC,aAAaiC,YAG8C3B,WAC1E"}
\ No newline at end of file
diff --git a/admin/tool/lp/amd/build/dialogue.min.js.map b/admin/tool/lp/amd/build/dialogue.min.js.map
index 67352fce7b2..9dcb1ec9faf 100644
--- a/admin/tool/lp/amd/build/dialogue.min.js.map
+++ b/admin/tool/lp/amd/build/dialogue.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"dialogue.min.js","sources":["../src/dialogue.js"],"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 * Wrapper for the YUI M.core.notification class. Allows us to\n * use the YUI version in AMD code until it is replaced.\n *\n * @module tool_lp/dialogue\n * @copyright 2015 Damyon Wiese \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(['core/yui'], function(Y) {\n\n // Private variables and functions.\n /**\n * Constructor\n *\n * @param {String} title Title for the window.\n * @param {String} content The content for the window.\n * @param {function} afterShow Callback executed after the window is opened.\n * @param {function} afterHide Callback executed after the window is closed.\n * @param {Boolean} wide Specify we want an extra wide dialogue (the size is standard, but wider than the default).\n * @param {String} height The height of the dialogue.\n */\n var dialogue = function(title, content, afterShow, afterHide, wide, height) {\n M.util.js_pending('tool_lp/dialogue:dialogue');\n\n this.yuiDialogue = null;\n var parent = this;\n\n // Default for wide is false.\n if (typeof wide == 'undefined') {\n wide = false;\n }\n\n Y.use('moodle-core-notification', 'timers', function() {\n var width = '480px';\n if (wide) {\n width = '800px';\n }\n\n if (!height) {\n height = 'auto';\n }\n\n parent.yuiDialogue = new M.core.dialogue({\n headerContent: title,\n bodyContent: content,\n draggable: true,\n visible: false,\n center: true,\n modal: true,\n width: width,\n height: height\n });\n\n parent.yuiDialogue.before('visibleChange', function() {\n M.util.js_pending('tool_lp/dialogue:before:visibleChange');\n });\n\n parent.yuiDialogue.after('visibleChange', function(e) {\n if (e.newVal) {\n // Delay the callback call to the next tick, otherwise it can happen that it is\n // executed before the dialogue constructor returns.\n if ((typeof afterShow !== 'undefined')) {\n Y.soon(function() {\n afterShow(parent);\n parent.yuiDialogue.centerDialogue();\n M.util.js_complete('tool_lp/dialogue:before:visibleChange');\n });\n } else {\n M.util.js_complete('tool_lp/dialogue:before:visibleChange');\n }\n } else {\n if ((typeof afterHide !== 'undefined')) {\n Y.soon(function() {\n afterHide(parent);\n M.util.js_complete('tool_lp/dialogue:before:visibleChange');\n });\n } else {\n M.util.js_complete('tool_lp/dialogue:before:visibleChange');\n }\n }\n });\n\n parent.yuiDialogue.show();\n M.util.js_complete('tool_lp/dialogue:dialogue');\n });\n };\n\n /**\n * Close this window.\n */\n dialogue.prototype.close = function() {\n this.yuiDialogue.hide();\n this.yuiDialogue.destroy();\n };\n\n /**\n * Get content.\n * @return {node}\n */\n dialogue.prototype.getContent = function() {\n return this.yuiDialogue.bodyNode.getDOMNode();\n };\n\n return /** @alias module:tool_lp/dialogue */ dialogue;\n});\n"],"names":["define","Y","dialogue","title","content","afterShow","afterHide","wide","height","M","util","js_pending","yuiDialogue","parent","this","use","width","core","headerContent","bodyContent","draggable","visible","center","modal","before","after","e","newVal","soon","centerDialogue","js_complete","show","prototype","close","hide","destroy","getContent","bodyNode","getDOMNode"],"mappings":";;;;;;;;AAuBAA,0BAAO,CAAC,aAAa,SAASC,OAatBC,SAAW,SAASC,MAAOC,QAASC,UAAWC,UAAWC,KAAMC,QAChEC,EAAEC,KAAKC,WAAW,kCAEbC,YAAc,SACfC,OAASC,UAGM,IAARP,OACPA,MAAO,GAGXN,EAAEc,IAAI,2BAA4B,UAAU,eACpCC,MAAQ,QACRT,OACAS,MAAQ,SAGPR,SACDA,OAAS,QAGbK,OAAOD,YAAc,IAAIH,EAAEQ,KAAKf,SAAS,CACrCgB,cAAef,MACfgB,YAAaf,QACbgB,WAAW,EACXC,SAAS,EACTC,QAAQ,EACRC,OAAO,EACPP,MAAOA,MACPR,OAAQA,SAGZK,OAAOD,YAAYY,OAAO,iBAAiB,WACvCf,EAAEC,KAAKC,WAAW,4CAGtBE,OAAOD,YAAYa,MAAM,iBAAiB,SAASC,GAC3CA,EAAEC,YAGwB,IAAdtB,UACRJ,EAAE2B,MAAK,WACHvB,UAAUQ,QACVA,OAAOD,YAAYiB,iBACnBpB,EAAEC,KAAKoB,YAAY,4CAGvBrB,EAAEC,KAAKoB,YAAY,8CAGG,IAAdxB,UACRL,EAAE2B,MAAK,WACHtB,UAAUO,QACVJ,EAAEC,KAAKoB,YAAY,4CAGvBrB,EAAEC,KAAKoB,YAAY,4CAK/BjB,OAAOD,YAAYmB,OACnBtB,EAAEC,KAAKoB,YAAY,wCAO3B5B,SAAS8B,UAAUC,MAAQ,gBAClBrB,YAAYsB,YACZtB,YAAYuB,WAOrBjC,SAAS8B,UAAUI,WAAa,kBACrBtB,KAAKF,YAAYyB,SAASC,cAGQpC"}
\ No newline at end of file
+{"version":3,"file":"dialogue.min.js","sources":["../src/dialogue.js"],"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 * Wrapper for the YUI M.core.notification class. Allows us to\n * use the YUI version in AMD code until it is replaced.\n *\n * @module tool_lp/dialogue\n * @copyright 2015 Damyon Wiese \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(['core/yui'], function(Y) {\n\n // Private variables and functions.\n /**\n * Constructor\n *\n * @param {String} title Title for the window.\n * @param {String} content The content for the window.\n * @param {function} afterShow Callback executed after the window is opened.\n * @param {function} afterHide Callback executed after the window is closed.\n * @param {Boolean} wide Specify we want an extra wide dialogue (the size is standard, but wider than the default).\n * @param {String} height The height of the dialogue.\n */\n var dialogue = function(title, content, afterShow, afterHide, wide, height) {\n M.util.js_pending('tool_lp/dialogue:dialogue');\n\n this.yuiDialogue = null;\n var parent = this;\n\n // Default for wide is false.\n if (typeof wide == 'undefined') {\n wide = false;\n }\n\n Y.use('moodle-core-notification', 'timers', function() {\n var width = '480px';\n if (wide) {\n width = '800px';\n }\n\n if (!height) {\n height = 'auto';\n }\n\n parent.yuiDialogue = new M.core.dialogue({\n headerContent: title,\n bodyContent: content,\n draggable: true,\n visible: false,\n center: true,\n modal: true,\n width: width,\n height: height\n });\n\n parent.yuiDialogue.before('visibleChange', function() {\n M.util.js_pending('tool_lp/dialogue:before:visibleChange');\n });\n\n parent.yuiDialogue.after('visibleChange', function(e) {\n if (e.newVal) {\n // Delay the callback call to the next tick, otherwise it can happen that it is\n // executed before the dialogue constructor returns.\n if ((typeof afterShow !== 'undefined')) {\n Y.soon(function() {\n afterShow(parent);\n parent.yuiDialogue.centerDialogue();\n M.util.js_complete('tool_lp/dialogue:before:visibleChange');\n });\n } else {\n M.util.js_complete('tool_lp/dialogue:before:visibleChange');\n }\n } else {\n if ((typeof afterHide !== 'undefined')) {\n Y.soon(function() {\n afterHide(parent);\n M.util.js_complete('tool_lp/dialogue:before:visibleChange');\n });\n } else {\n M.util.js_complete('tool_lp/dialogue:before:visibleChange');\n }\n }\n });\n\n parent.yuiDialogue.show();\n M.util.js_complete('tool_lp/dialogue:dialogue');\n });\n };\n\n /**\n * Close this window.\n */\n dialogue.prototype.close = function() {\n this.yuiDialogue.hide();\n this.yuiDialogue.destroy();\n };\n\n /**\n * Get content.\n * @return {node}\n */\n dialogue.prototype.getContent = function() {\n return this.yuiDialogue.bodyNode.getDOMNode();\n };\n\n return /** @alias module:tool_lp/dialogue */ dialogue;\n});\n"],"names":["define","Y","dialogue","title","content","afterShow","afterHide","wide","height","M","util","js_pending","this","yuiDialogue","parent","use","width","core","headerContent","bodyContent","draggable","visible","center","modal","before","after","e","newVal","soon","centerDialogue","js_complete","show","prototype","close","hide","destroy","getContent","bodyNode","getDOMNode"],"mappings":";;;;;;;;AAuBAA,0BAAO,CAAC,aAAa,SAASC,GAa1B,IAAIC,SAAW,SAASC,MAAOC,QAASC,UAAWC,UAAWC,KAAMC,QAChEC,EAAEC,KAAKC,WAAW,6BAElBC,KAAKC,YAAc,KACnB,IAAIC,OAASF,UAGM,IAARL,OACPA,MAAO,GAGXN,EAAEc,IAAI,2BAA4B,UAAU,WACxC,IAAIC,MAAQ,QACRT,OACAS,MAAQ,SAGPR,SACDA,OAAS,QAGbM,OAAOD,YAAc,IAAIJ,EAAEQ,KAAKf,SAAS,CACrCgB,cAAef,MACfgB,YAAaf,QACbgB,WAAW,EACXC,SAAS,EACTC,QAAQ,EACRC,OAAO,EACPP,MAAOA,MACPR,OAAQA,SAGZM,OAAOD,YAAYW,OAAO,iBAAiB,WACvCf,EAAEC,KAAKC,WAAW,wCACtB,IAEAG,OAAOD,YAAYY,MAAM,iBAAiB,SAASC,GAC3CA,EAAEC,YAGwB,IAAdtB,UACRJ,EAAE2B,MAAK,WACHvB,UAAUS,QACVA,OAAOD,YAAYgB,iBACnBpB,EAAEC,KAAKoB,YAAY,wCACvB,IAEArB,EAAEC,KAAKoB,YAAY,8CAGG,IAAdxB,UACRL,EAAE2B,MAAK,WACHtB,UAAUQ,QACVL,EAAEC,KAAKoB,YAAY,wCACvB,IAEArB,EAAEC,KAAKoB,YAAY,wCAG/B,IAEAhB,OAAOD,YAAYkB,OACnBtB,EAAEC,KAAKoB,YAAY,4BACvB,KAmBJ,OAbA5B,SAAS8B,UAAUC,MAAQ,WACvBrB,KAAKC,YAAYqB,OACjBtB,KAAKC,YAAYsB,WAOrBjC,SAAS8B,UAAUI,WAAa,WAC5B,OAAOxB,KAAKC,YAAYwB,SAASC,cAGQpC,QACjD"}
\ No newline at end of file
diff --git a/admin/tool/lp/amd/build/dragdrop-reorder.min.js.map b/admin/tool/lp/amd/build/dragdrop-reorder.min.js.map
index ed41f5b52a8..0ab578b223d 100644
--- a/admin/tool/lp/amd/build/dragdrop-reorder.min.js.map
+++ b/admin/tool/lp/amd/build/dragdrop-reorder.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"dragdrop-reorder.min.js","sources":["../src/dragdrop-reorder.js"],"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 * Drag and drop reorder via HTML5.\n *\n * @module tool_lp/dragdrop-reorder\n * @copyright 2015 Damyon Wiese \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(['core/str', 'core/yui'], function(str, Y) {\n // Private variables and functions.\n\n /**\n * Store the current instance of the core drag drop.\n *\n * @property {object} dragDropInstance M.tool_lp.dragdrop_reorder\n */\n var dragDropInstance = null;\n\n /**\n * Translate the drophit event from YUI\n * into simple drag and drop nodes.\n * @param {Y.Event} e The yui drop event.\n */\n var proxyCallback = function(e) {\n var dragNode = e.drag.get('node');\n var dropNode = e.drop.get('node');\n this.callback(dragNode.getDOMNode(), dropNode.getDOMNode());\n };\n\n return /** @alias module:tool_lp/dragdrop-reorder */ {\n // Public variables and functions.\n /**\n * Create an instance of M.tool_lp.dragdrop\n *\n * @param {String} group Unique string to identify this interaction.\n * @param {String} dragHandleText Alt text for the drag handle.\n * @param {String} sameNodeText Used in keyboard drag drop for the list of items target.\n * @param {String} parentNodeText Used in keyboard drag drop for the parent target.\n * @param {String} sameNodeClass class used to find the each of the list of items.\n * @param {String} parentNodeClass class used to find the container for the list of items.\n * @param {String} dragHandleInsertClass class used to find the location to insert the drag handles.\n * @param {function} callback Drop hit handler.\n */\n dragdrop: function(group,\n dragHandleText,\n sameNodeText,\n parentNodeText,\n sameNodeClass,\n parentNodeClass,\n dragHandleInsertClass,\n callback) {\n // Here we are wrapping YUI. This allows us to start transitioning, but\n // wait for a good alternative without having inconsistent UIs.\n str.get_strings([\n {key: 'emptydragdropregion', component: 'moodle'},\n {key: 'movecontent', component: 'moodle'},\n {key: 'tocontent', component: 'moodle'},\n ]).done(function() {\n Y.use('moodle-tool_lp-dragdrop-reorder', function() {\n\n var context = {\n callback: callback\n };\n if (dragDropInstance) {\n dragDropInstance.destroy();\n }\n dragDropInstance = M.tool_lp.dragdrop_reorder({\n group: group,\n dragHandleText: dragHandleText,\n sameNodeText: sameNodeText,\n parentNodeText: parentNodeText,\n sameNodeClass: sameNodeClass,\n parentNodeClass: parentNodeClass,\n dragHandleInsertClass: dragHandleInsertClass,\n callback: Y.bind(proxyCallback, context)\n });\n });\n });\n }\n\n };\n});\n"],"names":["define","str","Y","dragDropInstance","proxyCallback","e","dragNode","drag","get","dropNode","drop","callback","getDOMNode","dragdrop","group","dragHandleText","sameNodeText","parentNodeText","sameNodeClass","parentNodeClass","dragHandleInsertClass","get_strings","key","component","done","use","context","destroy","M","tool_lp","dragdrop_reorder","bind"],"mappings":";;;;;;;AAsBAA,kCAAO,CAAC,WAAY,aAAa,SAASC,IAAKC,OAQvCC,iBAAmB,KAOnBC,cAAgB,SAASC,OACrBC,SAAWD,EAAEE,KAAKC,IAAI,QACtBC,SAAWJ,EAAEK,KAAKF,IAAI,aACrBG,SAASL,SAASM,aAAcH,SAASG,qBAGG,CAcjDC,SAAU,SAASC,MACAC,eACAC,aACAC,eACAC,cACAC,gBACAC,sBACAT,UAGfV,IAAIoB,YAAY,CACZ,CAACC,IAAK,sBAAuBC,UAAW,UACxC,CAACD,IAAK,cAAeC,UAAW,UAChC,CAACD,IAAK,YAAaC,UAAW,YAC/BC,MAAK,WACJtB,EAAEuB,IAAI,mCAAmC,eAEjCC,QAAU,CACVf,SAAUA,UAEVR,kBACAA,iBAAiBwB,UAErBxB,iBAAmByB,EAAEC,QAAQC,iBAAiB,CAC1ChB,MAAOA,MACPC,eAAgBA,eAChBC,aAAcA,aACdC,eAAgBA,eAChBC,cAAeA,cACfC,gBAAiBA,gBACjBC,sBAAuBA,sBACvBT,SAAUT,EAAE6B,KAAK3B,cAAesB"}
\ No newline at end of file
+{"version":3,"file":"dragdrop-reorder.min.js","sources":["../src/dragdrop-reorder.js"],"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 * Drag and drop reorder via HTML5.\n *\n * @module tool_lp/dragdrop-reorder\n * @copyright 2015 Damyon Wiese \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(['core/str', 'core/yui'], function(str, Y) {\n // Private variables and functions.\n\n /**\n * Store the current instance of the core drag drop.\n *\n * @property {object} dragDropInstance M.tool_lp.dragdrop_reorder\n */\n var dragDropInstance = null;\n\n /**\n * Translate the drophit event from YUI\n * into simple drag and drop nodes.\n * @param {Y.Event} e The yui drop event.\n */\n var proxyCallback = function(e) {\n var dragNode = e.drag.get('node');\n var dropNode = e.drop.get('node');\n this.callback(dragNode.getDOMNode(), dropNode.getDOMNode());\n };\n\n return /** @alias module:tool_lp/dragdrop-reorder */ {\n // Public variables and functions.\n /**\n * Create an instance of M.tool_lp.dragdrop\n *\n * @param {String} group Unique string to identify this interaction.\n * @param {String} dragHandleText Alt text for the drag handle.\n * @param {String} sameNodeText Used in keyboard drag drop for the list of items target.\n * @param {String} parentNodeText Used in keyboard drag drop for the parent target.\n * @param {String} sameNodeClass class used to find the each of the list of items.\n * @param {String} parentNodeClass class used to find the container for the list of items.\n * @param {String} dragHandleInsertClass class used to find the location to insert the drag handles.\n * @param {function} callback Drop hit handler.\n */\n dragdrop: function(group,\n dragHandleText,\n sameNodeText,\n parentNodeText,\n sameNodeClass,\n parentNodeClass,\n dragHandleInsertClass,\n callback) {\n // Here we are wrapping YUI. This allows us to start transitioning, but\n // wait for a good alternative without having inconsistent UIs.\n str.get_strings([\n {key: 'emptydragdropregion', component: 'moodle'},\n {key: 'movecontent', component: 'moodle'},\n {key: 'tocontent', component: 'moodle'},\n ]).done(function() {\n Y.use('moodle-tool_lp-dragdrop-reorder', function() {\n\n var context = {\n callback: callback\n };\n if (dragDropInstance) {\n dragDropInstance.destroy();\n }\n dragDropInstance = M.tool_lp.dragdrop_reorder({\n group: group,\n dragHandleText: dragHandleText,\n sameNodeText: sameNodeText,\n parentNodeText: parentNodeText,\n sameNodeClass: sameNodeClass,\n parentNodeClass: parentNodeClass,\n dragHandleInsertClass: dragHandleInsertClass,\n callback: Y.bind(proxyCallback, context)\n });\n });\n });\n }\n\n };\n});\n"],"names":["define","str","Y","dragDropInstance","proxyCallback","e","dragNode","drag","get","dropNode","drop","this","callback","getDOMNode","dragdrop","group","dragHandleText","sameNodeText","parentNodeText","sameNodeClass","parentNodeClass","dragHandleInsertClass","get_strings","key","component","done","use","context","destroy","M","tool_lp","dragdrop_reorder","bind"],"mappings":";;;;;;;AAsBAA,OAAM,2BAAC,CAAC,WAAY,aAAa,SAASC,IAAKC,GAQ3C,IAAIC,iBAAmB,KAOnBC,cAAgB,SAASC,GACzB,IAAIC,SAAWD,EAAEE,KAAKC,IAAI,QACtBC,SAAWJ,EAAEK,KAAKF,IAAI,QAC1BG,KAAKC,SAASN,SAASO,aAAcJ,SAASI,eAGlD,MAAqD,CAcjDC,SAAU,SAASC,MACAC,eACAC,aACAC,eACAC,cACAC,gBACAC,sBACAT,UAGfX,IAAIqB,YAAY,CACZ,CAACC,IAAK,sBAAuBC,UAAW,UACxC,CAACD,IAAK,cAAeC,UAAW,UAChC,CAACD,IAAK,YAAaC,UAAW,YAC/BC,MAAK,WACJvB,EAAEwB,IAAI,mCAAmC,WAErC,IAAIC,QAAU,CACVf,SAAUA,UAEVT,kBACAA,iBAAiByB,UAErBzB,iBAAmB0B,EAAEC,QAAQC,iBAAiB,CAC1ChB,MAAOA,MACPC,eAAgBA,eAChBC,aAAcA,aACdC,eAAgBA,eAChBC,cAAeA,cACfC,gBAAiBA,gBACjBC,sBAAuBA,sBACvBT,SAAUV,EAAE8B,KAAK5B,cAAeuB,UAExC,GACJ,GACJ,EAGR"}
\ No newline at end of file
diff --git a/admin/tool/lp/amd/build/event_base.min.js.map b/admin/tool/lp/amd/build/event_base.min.js.map
index 5acbe6740c4..7416ebf238c 100644
--- a/admin/tool/lp/amd/build/event_base.min.js.map
+++ b/admin/tool/lp/amd/build/event_base.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"event_base.min.js","sources":["../src/event_base.js"],"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 * Event base javascript module.\n *\n * @module tool_lp/event_base\n * @copyright 2015 Frédéric Massart - FMCorz.net\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(['jquery'], function($) {\n\n /**\n * Base class.\n */\n var Base = function() {\n this._eventNode = $('');\n };\n\n /** @property {Node} The node we attach the events to. */\n Base.prototype._eventNode = null;\n\n /**\n * Register an event listener.\n *\n * @param {String} type The event type.\n * @param {Function} handler The event listener.\n * @method on\n */\n Base.prototype.on = function(type, handler) {\n this._eventNode.on(type, handler);\n };\n\n /**\n * Trigger an event.\n *\n * @param {String} type The type of event.\n * @param {Object} data The data to pass to the listeners.\n * @method _trigger\n */\n Base.prototype._trigger = function(type, data) {\n this._eventNode.trigger(type, [data]);\n };\n\n return /** @alias module:tool_lp/event_base */ Base;\n});\n"],"names":["define","$","Base","_eventNode","prototype","on","type","handler","_trigger","data","trigger"],"mappings":";;;;;;;AAsBAA,4BAAO,CAAC,WAAW,SAASC,OAKpBC,KAAO,gBACFC,WAAaF,EAAE,uBAIxBC,KAAKE,UAAUD,WAAa,KAS5BD,KAAKE,UAAUC,GAAK,SAASC,KAAMC,cAC1BJ,WAAWE,GAAGC,KAAMC,UAU7BL,KAAKE,UAAUI,SAAW,SAASF,KAAMG,WAChCN,WAAWO,QAAQJ,KAAM,CAACG,QAGYP"}
\ No newline at end of file
+{"version":3,"file":"event_base.min.js","sources":["../src/event_base.js"],"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 * Event base javascript module.\n *\n * @module tool_lp/event_base\n * @copyright 2015 Frédéric Massart - FMCorz.net\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(['jquery'], function($) {\n\n /**\n * Base class.\n */\n var Base = function() {\n this._eventNode = $('');\n };\n\n /** @property {Node} The node we attach the events to. */\n Base.prototype._eventNode = null;\n\n /**\n * Register an event listener.\n *\n * @param {String} type The event type.\n * @param {Function} handler The event listener.\n * @method on\n */\n Base.prototype.on = function(type, handler) {\n this._eventNode.on(type, handler);\n };\n\n /**\n * Trigger an event.\n *\n * @param {String} type The type of event.\n * @param {Object} data The data to pass to the listeners.\n * @method _trigger\n */\n Base.prototype._trigger = function(type, data) {\n this._eventNode.trigger(type, [data]);\n };\n\n return /** @alias module:tool_lp/event_base */ Base;\n});\n"],"names":["define","$","Base","this","_eventNode","prototype","on","type","handler","_trigger","data","trigger"],"mappings":";;;;;;;AAsBAA,4BAAO,CAAC,WAAW,SAASC,GAKxB,IAAIC,KAAO,WACPC,KAAKC,WAAaH,EAAE,gBA4BxB,OAxBAC,KAAKG,UAAUD,WAAa,KAS5BF,KAAKG,UAAUC,GAAK,SAASC,KAAMC,SAC/BL,KAAKC,WAAWE,GAAGC,KAAMC,UAU7BN,KAAKG,UAAUI,SAAW,SAASF,KAAMG,MACrCP,KAAKC,WAAWO,QAAQJ,KAAM,CAACG,QAGYR,IACnD"}
\ No newline at end of file
diff --git a/admin/tool/lp/amd/build/evidence_delete.min.js.map b/admin/tool/lp/amd/build/evidence_delete.min.js.map
index f16b333f0b6..45d1e2cf7bc 100644
--- a/admin/tool/lp/amd/build/evidence_delete.min.js.map
+++ b/admin/tool/lp/amd/build/evidence_delete.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"evidence_delete.min.js","sources":["../src/evidence_delete.js"],"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 * Evidence delete.\n *\n * @module tool_lp/evidence_delete\n * @copyright 2016 Frédéric Massart - FMCorz.net\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\ndefine(['jquery',\n 'core/notification',\n 'core/ajax',\n 'core/str',\n 'core/log'],\n function($, Notification, Ajax, Str, Log) {\n\n var selectors = {};\n\n /**\n * Register an event listener.\n *\n * @param {String} triggerSelector The node on which the click will happen.\n * @param {String} containerSelector The parent node that will be removed and contains the evidence ID.\n */\n var register = function(triggerSelector, containerSelector) {\n if (typeof selectors[triggerSelector] !== 'undefined') {\n return;\n }\n\n selectors[triggerSelector] = $('body').delegate(triggerSelector, 'click', function(e) {\n var parent = $(e.currentTarget).parents(containerSelector);\n if (!parent.length || parent.length > 1) {\n Log.error('None or too many evidence container were found.');\n return;\n }\n var evidenceId = parent.data('id');\n if (!evidenceId) {\n Log.error('Evidence ID was not found.');\n return;\n }\n\n e.preventDefault();\n e.stopPropagation();\n\n Str.get_strings([\n {key: 'confirm', component: 'moodle'},\n {key: 'areyousure', component: 'moodle'},\n {key: 'delete', component: 'moodle'},\n {key: 'cancel', component: 'moodle'}\n ]).done(function(strings) {\n Notification.confirm(\n strings[0], // Confirm.\n strings[1], // Are you sure?\n strings[2], // Delete.\n strings[3], // Cancel.\n function() {\n var promise = Ajax.call([{\n methodname: 'core_competency_delete_evidence',\n args: {\n id: evidenceId\n }\n }]);\n promise[0].then(function() {\n parent.remove();\n return;\n }).fail(Notification.exception);\n }\n );\n }).fail(Notification.exception);\n\n\n });\n };\n\n return /** @alias module:tool_lp/evidence_delete */ {\n\n /**\n * Register an event listener.\n *\n * @param {String} triggerSelector The node on which the click will happen.\n * @param {String} containerSelector The parent node that will be removed and contains the evidence ID.\n * @return {Void}\n */\n register: register\n };\n\n});\n"],"names":["define","$","Notification","Ajax","Str","Log","selectors","register","triggerSelector","containerSelector","delegate","e","parent","currentTarget","parents","length","error","evidenceId","data","preventDefault","stopPropagation","get_strings","key","component","done","strings","confirm","call","methodname","args","id","then","remove","fail","exception"],"mappings":";;;;;;;AAuBAA,iCAAO,CAAC,SACA,oBACA,YACA,WACA,aACA,SAASC,EAAGC,aAAcC,KAAMC,IAAKC,SAErCC,UAAY,SA0DoC,CAShDC,SA3DW,SAASC,gBAAiBC,wBACK,IAA/BH,UAAUE,mBAIrBF,UAAUE,iBAAmBP,EAAE,QAAQS,SAASF,gBAAiB,SAAS,SAASG,OAC3EC,OAASX,EAAEU,EAAEE,eAAeC,QAAQL,uBACnCG,OAAOG,QAAUH,OAAOG,OAAS,EAClCV,IAAIW,MAAM,4DAGVC,WAAaL,OAAOM,KAAK,MACxBD,YAKLN,EAAEQ,iBACFR,EAAES,kBAEFhB,IAAIiB,YAAY,CACZ,CAACC,IAAK,UAAWC,UAAW,UAC5B,CAACD,IAAK,aAAcC,UAAW,UAC/B,CAACD,IAAK,SAAUC,UAAW,UAC3B,CAACD,IAAK,SAAUC,UAAW,YAC5BC,MAAK,SAASC,SACbvB,aAAawB,QACTD,QAAQ,GACRA,QAAQ,GACRA,QAAQ,GACRA,QAAQ,IACR,WACkBtB,KAAKwB,KAAK,CAAC,CACrBC,WAAY,kCACZC,KAAM,CACFC,GAAIb,eAGJ,GAAGc,MAAK,WACZnB,OAAOoB,YAERC,KAAK/B,aAAagC,iBAG9BD,KAAK/B,aAAagC,YA/BjB7B,IAAIW,MAAM"}
\ No newline at end of file
+{"version":3,"file":"evidence_delete.min.js","sources":["../src/evidence_delete.js"],"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 * Evidence delete.\n *\n * @module tool_lp/evidence_delete\n * @copyright 2016 Frédéric Massart - FMCorz.net\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\ndefine(['jquery',\n 'core/notification',\n 'core/ajax',\n 'core/str',\n 'core/log'],\n function($, Notification, Ajax, Str, Log) {\n\n var selectors = {};\n\n /**\n * Register an event listener.\n *\n * @param {String} triggerSelector The node on which the click will happen.\n * @param {String} containerSelector The parent node that will be removed and contains the evidence ID.\n */\n var register = function(triggerSelector, containerSelector) {\n if (typeof selectors[triggerSelector] !== 'undefined') {\n return;\n }\n\n selectors[triggerSelector] = $('body').delegate(triggerSelector, 'click', function(e) {\n var parent = $(e.currentTarget).parents(containerSelector);\n if (!parent.length || parent.length > 1) {\n Log.error('None or too many evidence container were found.');\n return;\n }\n var evidenceId = parent.data('id');\n if (!evidenceId) {\n Log.error('Evidence ID was not found.');\n return;\n }\n\n e.preventDefault();\n e.stopPropagation();\n\n Str.get_strings([\n {key: 'confirm', component: 'moodle'},\n {key: 'areyousure', component: 'moodle'},\n {key: 'delete', component: 'moodle'},\n {key: 'cancel', component: 'moodle'}\n ]).done(function(strings) {\n Notification.confirm(\n strings[0], // Confirm.\n strings[1], // Are you sure?\n strings[2], // Delete.\n strings[3], // Cancel.\n function() {\n var promise = Ajax.call([{\n methodname: 'core_competency_delete_evidence',\n args: {\n id: evidenceId\n }\n }]);\n promise[0].then(function() {\n parent.remove();\n return;\n }).fail(Notification.exception);\n }\n );\n }).fail(Notification.exception);\n\n\n });\n };\n\n return /** @alias module:tool_lp/evidence_delete */ {\n\n /**\n * Register an event listener.\n *\n * @param {String} triggerSelector The node on which the click will happen.\n * @param {String} containerSelector The parent node that will be removed and contains the evidence ID.\n * @return {Void}\n */\n register: register\n };\n\n});\n"],"names":["define","$","Notification","Ajax","Str","Log","selectors","register","triggerSelector","containerSelector","delegate","e","parent","currentTarget","parents","length","error","evidenceId","data","preventDefault","stopPropagation","get_strings","key","component","done","strings","confirm","call","methodname","args","id","then","remove","fail","exception"],"mappings":";;;;;;;AAuBAA,OAAM,0BAAC,CAAC,SACA,oBACA,YACA,WACA,aACA,SAASC,EAAGC,aAAcC,KAAMC,IAAKC,KAEzC,IAAIC,UAAY,CAAA,EA0DhB,MAAoD,CAShDC,SA3DW,SAASC,gBAAiBC,wBACK,IAA/BH,UAAUE,mBAIrBF,UAAUE,iBAAmBP,EAAE,QAAQS,SAASF,gBAAiB,SAAS,SAASG,GAC/E,IAAIC,OAASX,EAAEU,EAAEE,eAAeC,QAAQL,mBACxC,IAAKG,OAAOG,QAAUH,OAAOG,OAAS,EAClCV,IAAIW,MAAM,uDADd,CAIA,IAAIC,WAAaL,OAAOM,KAAK,MACxBD,YAKLN,EAAEQ,iBACFR,EAAES,kBAEFhB,IAAIiB,YAAY,CACZ,CAACC,IAAK,UAAWC,UAAW,UAC5B,CAACD,IAAK,aAAcC,UAAW,UAC/B,CAACD,IAAK,SAAUC,UAAW,UAC3B,CAACD,IAAK,SAAUC,UAAW,YAC5BC,MAAK,SAASC,SACbvB,aAAawB,QACTD,QAAQ,GACRA,QAAQ,GACRA,QAAQ,GACRA,QAAQ,IACR,WACkBtB,KAAKwB,KAAK,CAAC,CACrBC,WAAY,kCACZC,KAAM,CACFC,GAAIb,eAGJ,GAAGc,MAAK,WACZnB,OAAOoB,QAEV,IAAEC,KAAK/B,aAAagC,UACzB,GAEP,IAAED,KAAK/B,aAAagC,YA/BjB7B,IAAIW,MAAM,6BAHd,CAqCJ,MAeR"}
\ No newline at end of file
diff --git a/admin/tool/lp/amd/build/form-cohort-selector.min.js.map b/admin/tool/lp/amd/build/form-cohort-selector.min.js.map
index aa365af512a..2da3709e6e9 100644
--- a/admin/tool/lp/amd/build/form-cohort-selector.min.js.map
+++ b/admin/tool/lp/amd/build/form-cohort-selector.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"form-cohort-selector.min.js","sources":["../src/form-cohort-selector.js"],"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 * Cohort selector module.\n *\n * @module tool_lp/form-cohort-selector\n * @copyright 2015 Frédéric Massart - FMCorz.net\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\ndefine(['jquery', 'core/ajax', 'core/templates'], function($, Ajax, Templates) {\n\n return /** @alias module:tool_lp/form-cohort-selector */ {\n\n processResults: function(selector, results) {\n var cohorts = [];\n $.each(results, function(index, cohort) {\n cohorts.push({\n value: cohort.id,\n label: cohort._label\n });\n });\n return cohorts;\n },\n\n transport: function(selector, query, success, failure) {\n var promise,\n contextid = parseInt($(selector).data('contextid'), 10),\n includes = $(selector).data('includes');\n\n promise = Ajax.call([{\n methodname: 'tool_lp_search_cohorts',\n args: {\n query: query,\n context: {contextid: contextid},\n includes: includes\n }\n }]);\n promise[0].then(function(results) {\n var promises = [],\n i = 0;\n\n // Render the label.\n $.each(results.cohorts, function(index, cohort) {\n promises.push(Templates.render('tool_lp/form-cohort-selector-suggestion', cohort));\n });\n\n // Apply the label to the results.\n return $.when.apply($.when, promises).then(function() {\n var args = arguments;\n $.each(results.cohorts, function(index, cohort) {\n cohort._label = args[i];\n i++;\n });\n success(results.cohorts);\n return;\n });\n\n }).catch(failure);\n }\n\n };\n\n});\n"],"names":["define","$","Ajax","Templates","processResults","selector","results","cohorts","each","index","cohort","push","value","id","label","_label","transport","query","success","failure","contextid","parseInt","data","includes","call","methodname","args","context","then","promises","i","render","when","apply","arguments","catch"],"mappings":";;;;;;;AAuBAA,sCAAO,CAAC,SAAU,YAAa,mBAAmB,SAASC,EAAGC,KAAMC,iBAEP,CAErDC,eAAgB,SAASC,SAAUC,aAC3BC,QAAU,UACdN,EAAEO,KAAKF,SAAS,SAASG,MAAOC,QAC5BH,QAAQI,KAAK,CACTC,MAAOF,OAAOG,GACdC,MAAOJ,OAAOK,YAGfR,SAGXS,UAAW,SAASX,SAAUY,MAAOC,QAASC,aAEtCC,UAAYC,SAASpB,EAAEI,UAAUiB,KAAK,aAAc,IACpDC,SAAWtB,EAAEI,UAAUiB,KAAK,YAEtBpB,KAAKsB,KAAK,CAAC,CACjBC,WAAY,yBACZC,KAAM,CACFT,MAAOA,MACPU,QAAS,CAACP,UAAWA,WACrBG,SAAUA,aAGV,GAAGK,MAAK,SAAStB,aACjBuB,SAAW,GACXC,EAAI,SAGR7B,EAAEO,KAAKF,QAAQC,SAAS,SAASE,MAAOC,QACpCmB,SAASlB,KAAKR,UAAU4B,OAAO,0CAA2CrB,YAIvET,EAAE+B,KAAKC,MAAMhC,EAAE+B,KAAMH,UAAUD,MAAK,eACnCF,KAAOQ,UACXjC,EAAEO,KAAKF,QAAQC,SAAS,SAASE,MAAOC,QACpCA,OAAOK,OAASW,KAAKI,GACrBA,OAEJZ,QAAQZ,QAAQC,eAIrB4B,MAAMhB"}
\ No newline at end of file
+{"version":3,"file":"form-cohort-selector.min.js","sources":["../src/form-cohort-selector.js"],"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 * Cohort selector module.\n *\n * @module tool_lp/form-cohort-selector\n * @copyright 2015 Frédéric Massart - FMCorz.net\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\ndefine(['jquery', 'core/ajax', 'core/templates'], function($, Ajax, Templates) {\n\n return /** @alias module:tool_lp/form-cohort-selector */ {\n\n processResults: function(selector, results) {\n var cohorts = [];\n $.each(results, function(index, cohort) {\n cohorts.push({\n value: cohort.id,\n label: cohort._label\n });\n });\n return cohorts;\n },\n\n transport: function(selector, query, success, failure) {\n var promise,\n contextid = parseInt($(selector).data('contextid'), 10),\n includes = $(selector).data('includes');\n\n promise = Ajax.call([{\n methodname: 'tool_lp_search_cohorts',\n args: {\n query: query,\n context: {contextid: contextid},\n includes: includes\n }\n }]);\n promise[0].then(function(results) {\n var promises = [],\n i = 0;\n\n // Render the label.\n $.each(results.cohorts, function(index, cohort) {\n promises.push(Templates.render('tool_lp/form-cohort-selector-suggestion', cohort));\n });\n\n // Apply the label to the results.\n return $.when.apply($.when, promises).then(function() {\n var args = arguments;\n $.each(results.cohorts, function(index, cohort) {\n cohort._label = args[i];\n i++;\n });\n success(results.cohorts);\n return;\n });\n\n }).catch(failure);\n }\n\n };\n\n});\n"],"names":["define","$","Ajax","Templates","processResults","selector","results","cohorts","each","index","cohort","push","value","id","label","_label","transport","query","success","failure","contextid","parseInt","data","includes","call","methodname","args","context","then","promises","i","render","when","apply","arguments","catch"],"mappings":";;;;;;;AAuBAA,OAAO,+BAAA,CAAC,SAAU,YAAa,mBAAmB,SAASC,EAAGC,KAAMC,WAEhE,MAAyD,CAErDC,eAAgB,SAASC,SAAUC,SAC/B,IAAIC,QAAU,GAOd,OANAN,EAAEO,KAAKF,SAAS,SAASG,MAAOC,QAC5BH,QAAQI,KAAK,CACTC,MAAOF,OAAOG,GACdC,MAAOJ,OAAOK,QAEtB,IACOR,OACV,EAEDS,UAAW,SAASX,SAAUY,MAAOC,QAASC,SAC1C,IACIC,UAAYC,SAASpB,EAAEI,UAAUiB,KAAK,aAAc,IACpDC,SAAWtB,EAAEI,UAAUiB,KAAK,YAEtBpB,KAAKsB,KAAK,CAAC,CACjBC,WAAY,yBACZC,KAAM,CACFT,MAAOA,MACPU,QAAS,CAACP,UAAWA,WACrBG,SAAUA,aAGV,GAAGK,MAAK,SAAStB,SACrB,IAAIuB,SAAW,GACXC,EAAI,EAQR,OALA7B,EAAEO,KAAKF,QAAQC,SAAS,SAASE,MAAOC,QACpCmB,SAASlB,KAAKR,UAAU4B,OAAO,0CAA2CrB,QAC9E,IAGOT,EAAE+B,KAAKC,MAAMhC,EAAE+B,KAAMH,UAAUD,MAAK,WACvC,IAAIF,KAAOQ,UACXjC,EAAEO,KAAKF,QAAQC,SAAS,SAASE,MAAOC,QACpCA,OAAOK,OAASW,KAAKI,GACrBA,GACJ,IACAZ,QAAQZ,QAAQC,QAEpB,GAEJ,IAAG4B,MAAMhB,QACb,EAIR"}
\ No newline at end of file
diff --git a/admin/tool/lp/amd/build/form-user-selector.min.js.map b/admin/tool/lp/amd/build/form-user-selector.min.js.map
index 795c5d36195..74095aa806e 100644
--- a/admin/tool/lp/amd/build/form-user-selector.min.js.map
+++ b/admin/tool/lp/amd/build/form-user-selector.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"form-user-selector.min.js","sources":["../src/form-user-selector.js"],"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 * User selector module.\n *\n * @module tool_lp/form-user-selector\n * @copyright 2015 Frédéric Massart - FMCorz.net\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\ndefine(['jquery', 'core/ajax', 'core/templates'], function($, Ajax, Templates) {\n\n return /** @alias module:tool_lp/form-user-selector */ {\n\n processResults: function(selector, results) {\n var users = [];\n $.each(results, function(index, user) {\n users.push({\n value: user.id,\n label: user._label\n });\n });\n return users;\n },\n\n transport: function(selector, query, success, failure) {\n var promise;\n var capability = $(selector).data('capability');\n if (typeof capability === \"undefined\") {\n capability = '';\n }\n\n promise = Ajax.call([{\n methodname: 'tool_lp_search_users',\n args: {\n query: query,\n capability: capability\n }\n }]);\n\n promise[0].then(function(results) {\n var promises = [],\n i = 0;\n\n // Render the label.\n $.each(results.users, function(index, user) {\n var ctx = user,\n identity = [];\n $.each(['idnumber', 'email', 'phone1', 'phone2', 'department', 'institution'], function(i, k) {\n if (typeof user[k] !== 'undefined' && user[k] !== '') {\n ctx.hasidentity = true;\n identity.push(user[k]);\n }\n });\n ctx.identity = identity.join(', ');\n promises.push(Templates.render('tool_lp/form-user-selector-suggestion', ctx));\n });\n\n // Apply the label to the results.\n return $.when.apply($.when, promises).then(function() {\n var args = arguments;\n $.each(results.users, function(index, user) {\n user._label = args[i];\n i++;\n });\n success(results.users);\n return;\n });\n\n }).catch(failure);\n }\n\n };\n\n});\n"],"names":["define","$","Ajax","Templates","processResults","selector","results","users","each","index","user","push","value","id","label","_label","transport","query","success","failure","capability","data","call","methodname","args","then","promises","i","ctx","identity","k","hasidentity","join","render","when","apply","arguments","catch"],"mappings":";;;;;;;AAuBAA,oCAAO,CAAC,SAAU,YAAa,mBAAmB,SAASC,EAAGC,KAAMC,iBAET,CAEnDC,eAAgB,SAASC,SAAUC,aAC3BC,MAAQ,UACZN,EAAEO,KAAKF,SAAS,SAASG,MAAOC,MAC5BH,MAAMI,KAAK,CACPC,MAAOF,KAAKG,GACZC,MAAOJ,KAAKK,YAGbR,OAGXS,UAAW,SAASX,SAAUY,MAAOC,QAASC,aAEtCC,WAAanB,EAAEI,UAAUgB,KAAK,mBACR,IAAfD,aACPA,WAAa,IAGPlB,KAAKoB,KAAK,CAAC,CACjBC,WAAY,uBACZC,KAAM,CACFP,MAAOA,MACPG,WAAYA,eAIZ,GAAGK,MAAK,SAASnB,aACjBoB,SAAW,GACXC,EAAI,SAGR1B,EAAEO,KAAKF,QAAQC,OAAO,SAASE,MAAOC,UAC9BkB,IAAMlB,KACNmB,SAAW,GACf5B,EAAEO,KAAK,CAAC,WAAY,QAAS,SAAU,SAAU,aAAc,gBAAgB,SAASmB,EAAGG,QAChE,IAAZpB,KAAKoB,IAAkC,KAAZpB,KAAKoB,KACvCF,IAAIG,aAAc,EAClBF,SAASlB,KAAKD,KAAKoB,QAG3BF,IAAIC,SAAWA,SAASG,KAAK,MAC7BN,SAASf,KAAKR,UAAU8B,OAAO,wCAAyCL,SAIrE3B,EAAEiC,KAAKC,MAAMlC,EAAEiC,KAAMR,UAAUD,MAAK,eACnCD,KAAOY,UACXnC,EAAEO,KAAKF,QAAQC,OAAO,SAASE,MAAOC,MAClCA,KAAKK,OAASS,KAAKG,GACnBA,OAEJT,QAAQZ,QAAQC,aAIrB8B,MAAMlB"}
\ No newline at end of file
+{"version":3,"file":"form-user-selector.min.js","sources":["../src/form-user-selector.js"],"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 * User selector module.\n *\n * @module tool_lp/form-user-selector\n * @copyright 2015 Frédéric Massart - FMCorz.net\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\ndefine(['jquery', 'core/ajax', 'core/templates'], function($, Ajax, Templates) {\n\n return /** @alias module:tool_lp/form-user-selector */ {\n\n processResults: function(selector, results) {\n var users = [];\n $.each(results, function(index, user) {\n users.push({\n value: user.id,\n label: user._label\n });\n });\n return users;\n },\n\n transport: function(selector, query, success, failure) {\n var promise;\n var capability = $(selector).data('capability');\n if (typeof capability === \"undefined\") {\n capability = '';\n }\n\n promise = Ajax.call([{\n methodname: 'tool_lp_search_users',\n args: {\n query: query,\n capability: capability\n }\n }]);\n\n promise[0].then(function(results) {\n var promises = [],\n i = 0;\n\n // Render the label.\n $.each(results.users, function(index, user) {\n var ctx = user,\n identity = [];\n $.each(['idnumber', 'email', 'phone1', 'phone2', 'department', 'institution'], function(i, k) {\n if (typeof user[k] !== 'undefined' && user[k] !== '') {\n ctx.hasidentity = true;\n identity.push(user[k]);\n }\n });\n ctx.identity = identity.join(', ');\n promises.push(Templates.render('tool_lp/form-user-selector-suggestion', ctx));\n });\n\n // Apply the label to the results.\n return $.when.apply($.when, promises).then(function() {\n var args = arguments;\n $.each(results.users, function(index, user) {\n user._label = args[i];\n i++;\n });\n success(results.users);\n return;\n });\n\n }).catch(failure);\n }\n\n };\n\n});\n"],"names":["define","$","Ajax","Templates","processResults","selector","results","users","each","index","user","push","value","id","label","_label","transport","query","success","failure","capability","data","call","methodname","args","then","promises","i","ctx","identity","k","hasidentity","join","render","when","apply","arguments","catch"],"mappings":";;;;;;;AAuBAA,OAAO,6BAAA,CAAC,SAAU,YAAa,mBAAmB,SAASC,EAAGC,KAAMC,WAEhE,MAAuD,CAEnDC,eAAgB,SAASC,SAAUC,SAC/B,IAAIC,MAAQ,GAOZ,OANAN,EAAEO,KAAKF,SAAS,SAASG,MAAOC,MAC5BH,MAAMI,KAAK,CACPC,MAAOF,KAAKG,GACZC,MAAOJ,KAAKK,QAEpB,IACOR,KACV,EAEDS,UAAW,SAASX,SAAUY,MAAOC,QAASC,SAC1C,IACIC,WAAanB,EAAEI,UAAUgB,KAAK,mBACR,IAAfD,aACPA,WAAa,IAGPlB,KAAKoB,KAAK,CAAC,CACjBC,WAAY,uBACZC,KAAM,CACFP,MAAOA,MACPG,WAAYA,eAIZ,GAAGK,MAAK,SAASnB,SACrB,IAAIoB,SAAW,GACXC,EAAI,EAiBR,OAdA1B,EAAEO,KAAKF,QAAQC,OAAO,SAASE,MAAOC,MAClC,IAAIkB,IAAMlB,KACNmB,SAAW,GACf5B,EAAEO,KAAK,CAAC,WAAY,QAAS,SAAU,SAAU,aAAc,gBAAgB,SAASmB,EAAGG,QAChE,IAAZpB,KAAKoB,IAAkC,KAAZpB,KAAKoB,KACvCF,IAAIG,aAAc,EAClBF,SAASlB,KAAKD,KAAKoB,IAE3B,IACAF,IAAIC,SAAWA,SAASG,KAAK,MAC7BN,SAASf,KAAKR,UAAU8B,OAAO,wCAAyCL,KAC5E,IAGO3B,EAAEiC,KAAKC,MAAMlC,EAAEiC,KAAMR,UAAUD,MAAK,WACvC,IAAID,KAAOY,UACXnC,EAAEO,KAAKF,QAAQC,OAAO,SAASE,MAAOC,MAClCA,KAAKK,OAASS,KAAKG,GACnBA,GACJ,IACAT,QAAQZ,QAAQC,MAEpB,GAEJ,IAAG8B,MAAMlB,QACb,EAIR"}
\ No newline at end of file
diff --git a/admin/tool/lp/amd/build/form_competency_element.min.js.map b/admin/tool/lp/amd/build/form_competency_element.min.js.map
index 32c2f6f7dfa..de7bb9b0348 100644
--- a/admin/tool/lp/amd/build/form_competency_element.min.js.map
+++ b/admin/tool/lp/amd/build/form_competency_element.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"form_competency_element.min.js","sources":["../src/form_competency_element.js"],"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 * Badge select competency actions\n *\n * @module tool_lp/form_competency_element\n * @copyright 2019 Damyon Wiese \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(['jquery', 'tool_lp/competencypicker', 'core/ajax', 'core/notification', 'core/templates'],\n function($, Picker, Ajax, Notification, Templates) {\n\n var pickerInstance = null;\n\n var pageContextId = 1;\n\n /**\n * Re-render the list of selected competencies.\n *\n * @method renderCompetencies\n * @return {boolean}\n */\n var renderCompetencies = function() {\n var currentCompetencies = $('[data-action=\"competencies\"]').val();\n var requests = [];\n var i = 0;\n\n if (currentCompetencies != '') {\n currentCompetencies = currentCompetencies.split(',');\n for (i = 0; i < currentCompetencies.length; i++) {\n requests[requests.length] = {\n methodname: 'core_competency_read_competency',\n args: {id: currentCompetencies[i]}\n };\n }\n }\n\n $.when.apply($, Ajax.call(requests, false)).then(function() {\n var i = 0,\n competencies = [];\n\n for (i = 0; i < arguments.length; i++) {\n competencies[i] = arguments[i];\n }\n var context = {\n competencies: competencies\n };\n\n return Templates.render('tool_lp/form_competency_list', context);\n }).then(function(html, js) {\n Templates.replaceNode($('[data-region=\"competencies\"]'), html, js);\n return true;\n }).fail(Notification.exception);\n\n return true;\n };\n\n /**\n * Deselect a competency\n *\n * @method unpickCompetenciesHandler\n * @param {Event} e\n * @return {boolean}\n */\n var unpickCompetenciesHandler = function(e) {\n var currentCompetencies = $('[data-action=\"competencies\"]').val().split(','),\n newCompetencies = [],\n i,\n toRemove = $(e.currentTarget).data('id');\n\n for (i = 0; i < currentCompetencies.length; i++) {\n if (currentCompetencies[i] != toRemove) {\n newCompetencies[newCompetencies.length] = currentCompetencies[i];\n }\n }\n\n $('[data-action=\"competencies\"]').val(newCompetencies.join(','));\n\n return renderCompetencies();\n };\n\n /**\n * Open a competencies popup to relate competencies.\n *\n * @method pickCompetenciesHandler\n */\n var pickCompetenciesHandler = function() {\n var currentCompetencies = $('[data-action=\"competencies\"]').val().split(',');\n\n if (!pickerInstance) {\n pickerInstance = new Picker(pageContextId, false, 'parents', true);\n pickerInstance.on('save', function(e, data) {\n var before = $('[data-action=\"competencies\"]').val();\n var compIds = data.competencyIds;\n if (before != '') {\n compIds = compIds.concat(before.split(','));\n }\n var value = compIds.join(',');\n\n $('[data-action=\"competencies\"]').val(value);\n\n return renderCompetencies();\n });\n }\n\n pickerInstance.setDisallowedCompetencyIDs(currentCompetencies);\n pickerInstance.display();\n };\n\n return /** @alias module:tool_lp/form_competency_element */ {\n /**\n * Listen for clicks on the competency picker and push the changes to the form element.\n *\n * @method init\n * @param {Integer} contextId\n */\n init: function(contextId) {\n pageContextId = contextId;\n renderCompetencies();\n $('[data-action=\"select-competencies\"]').on('click', pickCompetenciesHandler);\n $('body').on('click', '[data-action=\"deselect-competency\"]', unpickCompetenciesHandler);\n }\n };\n});\n"],"names":["define","$","Picker","Ajax","Notification","Templates","pickerInstance","pageContextId","renderCompetencies","currentCompetencies","val","requests","i","split","length","methodname","args","id","when","apply","call","then","competencies","arguments","context","render","html","js","replaceNode","fail","exception","unpickCompetenciesHandler","e","newCompetencies","toRemove","currentTarget","data","join","pickCompetenciesHandler","on","before","compIds","competencyIds","concat","value","setDisallowedCompetencyIDs","display","init","contextId"],"mappings":";;;;;;;AAsBAA,yCAAO,CAAC,SAAU,2BAA4B,YAAa,oBAAqB,mBACxE,SAASC,EAAGC,OAAQC,KAAMC,aAAcC,eAExCC,eAAiB,KAEjBC,cAAgB,EAQhBC,mBAAqB,eACjBC,oBAAsBR,EAAE,gCAAgCS,MACxDC,SAAW,GACXC,EAAI,KAEmB,IAAvBH,wBACAA,oBAAsBA,oBAAoBI,MAAM,KAC3CD,EAAI,EAAGA,EAAIH,oBAAoBK,OAAQF,IACxCD,SAASA,SAASG,QAAU,CACxBC,WAAY,kCACZC,KAAM,CAACC,GAAIR,oBAAoBG,YAK3CX,EAAEiB,KAAKC,MAAMlB,EAAGE,KAAKiB,KAAKT,UAAU,IAAQU,MAAK,eACzCT,EAAI,EACJU,aAAe,OAEdV,EAAI,EAAGA,EAAIW,UAAUT,OAAQF,IAC9BU,aAAaV,GAAKW,UAAUX,OAE5BY,QAAU,CACVF,aAAcA,qBAGXjB,UAAUoB,OAAO,+BAAgCD,YACzDH,MAAK,SAASK,KAAMC,WACnBtB,UAAUuB,YAAY3B,EAAE,gCAAiCyB,KAAMC,KACxD,KACRE,KAAKzB,aAAa0B,YAEd,GAUPC,0BAA4B,SAASC,OAGjCpB,EAFAH,oBAAsBR,EAAE,gCAAgCS,MAAMG,MAAM,KACpEoB,gBAAkB,GAElBC,SAAWjC,EAAE+B,EAAEG,eAAeC,KAAK,UAElCxB,EAAI,EAAGA,EAAIH,oBAAoBK,OAAQF,IACpCH,oBAAoBG,IAAMsB,WAC1BD,gBAAgBA,gBAAgBnB,QAAUL,oBAAoBG,WAItEX,EAAE,gCAAgCS,IAAIuB,gBAAgBI,KAAK,MAEpD7B,sBAQP8B,wBAA0B,eACtB7B,oBAAsBR,EAAE,gCAAgCS,MAAMG,MAAM,KAEnEP,iBACDA,eAAiB,IAAIJ,OAAOK,eAAe,EAAO,WAAW,IAC9CgC,GAAG,QAAQ,SAASP,EAAGI,UAC9BI,OAASvC,EAAE,gCAAgCS,MAC3C+B,QAAUL,KAAKM,cACL,IAAVF,SACAC,QAAUA,QAAQE,OAAOH,OAAO3B,MAAM,WAEtC+B,MAAQH,QAAQJ,KAAK,YAEzBpC,EAAE,gCAAgCS,IAAIkC,OAE/BpC,wBAIfF,eAAeuC,2BAA2BpC,qBAC1CH,eAAewC,iBAGyC,CAOxDC,KAAM,SAASC,WACXzC,cAAgByC,UAChBxC,qBACAP,EAAE,uCAAuCsC,GAAG,QAASD,yBACrDrC,EAAE,QAAQsC,GAAG,QAAS,sCAAuCR"}
\ No newline at end of file
+{"version":3,"file":"form_competency_element.min.js","sources":["../src/form_competency_element.js"],"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 * Badge select competency actions\n *\n * @module tool_lp/form_competency_element\n * @copyright 2019 Damyon Wiese \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(['jquery', 'tool_lp/competencypicker', 'core/ajax', 'core/notification', 'core/templates'],\n function($, Picker, Ajax, Notification, Templates) {\n\n var pickerInstance = null;\n\n var pageContextId = 1;\n\n /**\n * Re-render the list of selected competencies.\n *\n * @method renderCompetencies\n * @return {boolean}\n */\n var renderCompetencies = function() {\n var currentCompetencies = $('[data-action=\"competencies\"]').val();\n var requests = [];\n var i = 0;\n\n if (currentCompetencies != '') {\n currentCompetencies = currentCompetencies.split(',');\n for (i = 0; i < currentCompetencies.length; i++) {\n requests[requests.length] = {\n methodname: 'core_competency_read_competency',\n args: {id: currentCompetencies[i]}\n };\n }\n }\n\n $.when.apply($, Ajax.call(requests, false)).then(function() {\n var i = 0,\n competencies = [];\n\n for (i = 0; i < arguments.length; i++) {\n competencies[i] = arguments[i];\n }\n var context = {\n competencies: competencies\n };\n\n return Templates.render('tool_lp/form_competency_list', context);\n }).then(function(html, js) {\n Templates.replaceNode($('[data-region=\"competencies\"]'), html, js);\n return true;\n }).fail(Notification.exception);\n\n return true;\n };\n\n /**\n * Deselect a competency\n *\n * @method unpickCompetenciesHandler\n * @param {Event} e\n * @return {boolean}\n */\n var unpickCompetenciesHandler = function(e) {\n var currentCompetencies = $('[data-action=\"competencies\"]').val().split(','),\n newCompetencies = [],\n i,\n toRemove = $(e.currentTarget).data('id');\n\n for (i = 0; i < currentCompetencies.length; i++) {\n if (currentCompetencies[i] != toRemove) {\n newCompetencies[newCompetencies.length] = currentCompetencies[i];\n }\n }\n\n $('[data-action=\"competencies\"]').val(newCompetencies.join(','));\n\n return renderCompetencies();\n };\n\n /**\n * Open a competencies popup to relate competencies.\n *\n * @method pickCompetenciesHandler\n */\n var pickCompetenciesHandler = function() {\n var currentCompetencies = $('[data-action=\"competencies\"]').val().split(',');\n\n if (!pickerInstance) {\n pickerInstance = new Picker(pageContextId, false, 'parents', true);\n pickerInstance.on('save', function(e, data) {\n var before = $('[data-action=\"competencies\"]').val();\n var compIds = data.competencyIds;\n if (before != '') {\n compIds = compIds.concat(before.split(','));\n }\n var value = compIds.join(',');\n\n $('[data-action=\"competencies\"]').val(value);\n\n return renderCompetencies();\n });\n }\n\n pickerInstance.setDisallowedCompetencyIDs(currentCompetencies);\n pickerInstance.display();\n };\n\n return /** @alias module:tool_lp/form_competency_element */ {\n /**\n * Listen for clicks on the competency picker and push the changes to the form element.\n *\n * @method init\n * @param {Integer} contextId\n */\n init: function(contextId) {\n pageContextId = contextId;\n renderCompetencies();\n $('[data-action=\"select-competencies\"]').on('click', pickCompetenciesHandler);\n $('body').on('click', '[data-action=\"deselect-competency\"]', unpickCompetenciesHandler);\n }\n };\n});\n"],"names":["define","$","Picker","Ajax","Notification","Templates","pickerInstance","pageContextId","renderCompetencies","currentCompetencies","val","requests","i","split","length","methodname","args","id","when","apply","call","then","competencies","arguments","context","render","html","js","replaceNode","fail","exception","unpickCompetenciesHandler","e","newCompetencies","toRemove","currentTarget","data","join","pickCompetenciesHandler","on","before","compIds","competencyIds","concat","value","setDisallowedCompetencyIDs","display","init","contextId"],"mappings":";;;;;;;AAsBAA,OAAM,kCAAC,CAAC,SAAU,2BAA4B,YAAa,oBAAqB,mBACxE,SAASC,EAAGC,OAAQC,KAAMC,aAAcC,WAE5C,IAAIC,eAAiB,KAEjBC,cAAgB,EAQhBC,mBAAqB,WACrB,IAAIC,oBAAsBR,EAAE,gCAAgCS,MACxDC,SAAW,GACXC,EAAI,EAER,GAA2B,IAAvBH,oBAEA,IADAA,oBAAsBA,oBAAoBI,MAAM,KAC3CD,EAAI,EAAGA,EAAIH,oBAAoBK,OAAQF,IACxCD,SAASA,SAASG,QAAU,CACxBC,WAAY,kCACZC,KAAM,CAACC,GAAIR,oBAAoBG,KAsB3C,OAjBAX,EAAEiB,KAAKC,MAAMlB,EAAGE,KAAKiB,KAAKT,UAAU,IAAQU,MAAK,WAC7C,IAAIT,EAAI,EACJU,aAAe,GAEnB,IAAKV,EAAI,EAAGA,EAAIW,UAAUT,OAAQF,IAC9BU,aAAaV,GAAKW,UAAUX,GAEhC,IAAIY,QAAU,CACVF,aAAcA,cAGlB,OAAOjB,UAAUoB,OAAO,+BAAgCD,QAC3D,IAAEH,MAAK,SAASK,KAAMC,IAEnB,OADAtB,UAAUuB,YAAY3B,EAAE,gCAAiCyB,KAAMC,KACxD,CACV,IAAEE,KAAKzB,aAAa0B,YAEd,GAUPC,0BAA4B,SAASC,GACrC,IAEIpB,EAFAH,oBAAsBR,EAAE,gCAAgCS,MAAMG,MAAM,KACpEoB,gBAAkB,GAElBC,SAAWjC,EAAE+B,EAAEG,eAAeC,KAAK,MAEvC,IAAKxB,EAAI,EAAGA,EAAIH,oBAAoBK,OAAQF,IACpCH,oBAAoBG,IAAMsB,WAC1BD,gBAAgBA,gBAAgBnB,QAAUL,oBAAoBG,IAMtE,OAFAX,EAAE,gCAAgCS,IAAIuB,gBAAgBI,KAAK,MAEpD7B,sBAQP8B,wBAA0B,WAC1B,IAAI7B,oBAAsBR,EAAE,gCAAgCS,MAAMG,MAAM,KAEnEP,iBACDA,eAAiB,IAAIJ,OAAOK,eAAe,EAAO,WAAW,IAC9CgC,GAAG,QAAQ,SAASP,EAAGI,MAClC,IAAII,OAASvC,EAAE,gCAAgCS,MAC3C+B,QAAUL,KAAKM,cACL,IAAVF,SACAC,QAAUA,QAAQE,OAAOH,OAAO3B,MAAM,OAE1C,IAAI+B,MAAQH,QAAQJ,KAAK,KAIzB,OAFApC,EAAE,gCAAgCS,IAAIkC,OAE/BpC,oBACX,IAGJF,eAAeuC,2BAA2BpC,qBAC1CH,eAAewC,WAGnB,MAA4D,CAOxDC,KAAM,SAASC,WACXzC,cAAgByC,UAChBxC,qBACAP,EAAE,uCAAuCsC,GAAG,QAASD,yBACrDrC,EAAE,QAAQsC,GAAG,QAAS,sCAAuCR,0BACjE,EAER"}
\ No newline at end of file
diff --git a/admin/tool/lp/amd/build/frameworkactions.min.js.map b/admin/tool/lp/amd/build/frameworkactions.min.js.map
index 96e2bbfc0ee..82454831a9f 100644
--- a/admin/tool/lp/amd/build/frameworkactions.min.js.map
+++ b/admin/tool/lp/amd/build/frameworkactions.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"frameworkactions.min.js","sources":["../src/frameworkactions.js"],"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 * Competency frameworks actions via ajax.\n *\n * @module tool_lp/frameworkactions\n * @copyright 2015 Damyon Wiese \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(['jquery', 'core/templates', 'core/ajax', 'core/notification', 'core/str'], function($, templates, ajax, notification, str) {\n // Private variables and functions.\n\n /** @var {Number} pagecontextid The id of the context */\n var pagecontextid = 0;\n\n /** @var {Number} frameworkid The id of the framework */\n var frameworkid = 0;\n\n /**\n * Callback to replace the dom element with the rendered template.\n *\n * @param {String} newhtml The new html to insert.\n * @param {String} newjs The new js to run.\n */\n var updatePage = function(newhtml, newjs) {\n $('[data-region=\"managecompetencies\"]').replaceWith(newhtml);\n templates.runTemplateJS(newjs);\n };\n\n /**\n * Callback to render the page template again and update the page.\n *\n * @param {Object} context The context for the template.\n */\n var reloadList = function(context) {\n templates.render('tool_lp/manage_competency_frameworks_page', context)\n .done(updatePage)\n .fail(notification.exception);\n };\n\n /**\n * Duplicate a framework and reload the page.\n * @method doDuplicate\n * @param {Event} e\n */\n var doDuplicate = function(e) {\n e.preventDefault();\n\n frameworkid = $(this).attr('data-frameworkid');\n\n // We are chaining ajax requests here.\n var requests = ajax.call([{\n methodname: 'core_competency_duplicate_competency_framework',\n args: {id: frameworkid}\n }, {\n methodname: 'tool_lp_data_for_competency_frameworks_manage_page',\n args: {\n pagecontext: {\n contextid: pagecontextid\n }\n }\n }]);\n requests[1].done(reloadList).fail(notification.exception);\n };\n /**\n * Delete a framework and reload the page.\n */\n var doDelete = function() {\n\n // We are chaining ajax requests here.\n var requests = ajax.call([{\n methodname: 'core_competency_delete_competency_framework',\n args: {id: frameworkid}\n }, {\n methodname: 'tool_lp_data_for_competency_frameworks_manage_page',\n args: {\n pagecontext: {\n contextid: pagecontextid\n }\n }\n }]);\n requests[0].done(function(success) {\n if (success === false) {\n var req = ajax.call([{\n methodname: 'core_competency_read_competency_framework',\n args: {id: frameworkid}\n }]);\n req[0].done(function(framework) {\n str.get_strings([\n {key: 'frameworkcannotbedeleted', component: 'tool_lp', param: framework.shortname},\n {key: 'cancel', component: 'moodle'}\n ]).done(function(strings) {\n notification.alert(\n null,\n strings[0]\n );\n }).fail(notification.exception);\n });\n }\n }).fail(notification.exception);\n requests[1].done(reloadList).fail(notification.exception);\n };\n\n /**\n * Handler for \"Delete competency framework\" actions.\n * @param {Event} e\n */\n var confirmDelete = function(e) {\n e.preventDefault();\n\n var id = $(this).attr('data-frameworkid');\n frameworkid = id;\n\n var requests = ajax.call([{\n methodname: 'core_competency_read_competency_framework',\n args: {id: frameworkid}\n }]);\n\n requests[0].done(function(framework) {\n str.get_strings([\n {key: 'confirm', component: 'moodle'},\n {key: 'deletecompetencyframework', component: 'tool_lp', param: framework.shortname},\n {key: 'delete', component: 'moodle'},\n {key: 'cancel', component: 'moodle'}\n ]).done(function(strings) {\n notification.confirm(\n strings[0], // Confirm.\n strings[1], // Delete competency framework X?\n strings[2], // Delete.\n strings[3], // Cancel.\n doDelete\n );\n }).fail(notification.exception);\n }).fail(notification.exception);\n\n };\n\n\n return /** @alias module:tool_lp/frameworkactions */ {\n // Public variables and functions.\n\n /**\n * Expose the event handler for delete.\n * @method deleteHandler\n * @param {Event} e\n */\n deleteHandler: confirmDelete,\n\n /**\n * Expose the event handler for duplicate.\n * @method duplicateHandler\n * @param {Event} e\n */\n duplicateHandler: doDuplicate,\n\n /**\n * Initialise the module.\n * @method init\n * @param {Number} contextid The context id of the page.\n */\n init: function(contextid) {\n pagecontextid = contextid;\n }\n };\n});\n"],"names":["define","$","templates","ajax","notification","str","pagecontextid","frameworkid","updatePage","newhtml","newjs","replaceWith","runTemplateJS","reloadList","context","render","done","fail","exception","doDelete","requests","call","methodname","args","id","pagecontext","contextid","success","framework","get_strings","key","component","param","shortname","strings","alert","deleteHandler","e","preventDefault","this","attr","confirm","duplicateHandler","init"],"mappings":";;;;;;;AAsBAA,kCAAO,CAAC,SAAU,iBAAkB,YAAa,oBAAqB,aAAa,SAASC,EAAGC,UAAWC,KAAMC,aAAcC,SAItHC,cAAgB,EAGhBC,YAAc,EAQdC,WAAa,SAASC,QAASC,OAC/BT,EAAE,sCAAsCU,YAAYF,SACpDP,UAAUU,cAAcF,QAQxBG,WAAa,SAASC,SACtBZ,UAAUa,OAAO,4CAA6CD,SACzDE,KAAKR,YACLS,KAAKb,aAAac,YA8BvBC,SAAW,eAGPC,SAAWjB,KAAKkB,KAAK,CAAC,CACtBC,WAAY,8CACZC,KAAM,CAACC,GAAIjB,cACZ,CACCe,WAAY,qDACZC,KAAM,CACFE,YAAa,CACTC,UAAWpB,mBAIvBc,SAAS,GAAGJ,MAAK,SAASW,UACN,IAAZA,SACUxB,KAAKkB,KAAK,CAAC,CACjBC,WAAY,4CACZC,KAAM,CAACC,GAAIjB,gBAEX,GAAGS,MAAK,SAASY,WACjBvB,IAAIwB,YAAY,CACZ,CAACC,IAAK,2BAA4BC,UAAW,UAAWC,MAAOJ,UAAUK,WACzE,CAACH,IAAK,SAAUC,UAAW,YAC5Bf,MAAK,SAASkB,SACb9B,aAAa+B,MACT,KACAD,QAAQ,OAEbjB,KAAKb,aAAac,iBAG9BD,KAAKb,aAAac,WACrBE,SAAS,GAAGJ,KAAKH,YAAYI,KAAKb,aAAac,kBAsCE,CAQjDkB,cAvCgB,SAASC,GACzBA,EAAEC,qBAEEd,GAAKvB,EAAEsC,MAAMC,KAAK,oBACtBjC,YAAciB,GAECrB,KAAKkB,KAAK,CAAC,CACtBC,WAAY,4CACZC,KAAM,CAACC,GAAIjB,gBAGN,GAAGS,MAAK,SAASY,WACtBvB,IAAIwB,YAAY,CACZ,CAACC,IAAK,UAAWC,UAAW,UAC5B,CAACD,IAAK,4BAA6BC,UAAW,UAAWC,MAAOJ,UAAUK,WAC1E,CAACH,IAAK,SAAUC,UAAW,UAC3B,CAACD,IAAK,SAAUC,UAAW,YAC5Bf,MAAK,SAASkB,SACb9B,aAAaqC,QACTP,QAAQ,GACRA,QAAQ,GACRA,QAAQ,GACRA,QAAQ,GACRf,aAELF,KAAKb,aAAac,cACtBD,KAAKb,aAAac,YAoBrBwB,iBA5Gc,SAASL,GACvBA,EAAEC,iBAEF/B,YAAcN,EAAEsC,MAAMC,KAAK,oBAGZrC,KAAKkB,KAAK,CAAC,CACtBC,WAAY,iDACZC,KAAM,CAACC,GAAIjB,cACZ,CACCe,WAAY,qDACZC,KAAM,CACFE,YAAa,CACTC,UAAWpB,mBAId,GAAGU,KAAKH,YAAYI,KAAKb,aAAac,YAkG/CyB,KAAM,SAASjB,WACXpB,cAAgBoB"}
\ No newline at end of file
+{"version":3,"file":"frameworkactions.min.js","sources":["../src/frameworkactions.js"],"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 * Competency frameworks actions via ajax.\n *\n * @module tool_lp/frameworkactions\n * @copyright 2015 Damyon Wiese \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(['jquery', 'core/templates', 'core/ajax', 'core/notification', 'core/str'], function($, templates, ajax, notification, str) {\n // Private variables and functions.\n\n /** @var {Number} pagecontextid The id of the context */\n var pagecontextid = 0;\n\n /** @var {Number} frameworkid The id of the framework */\n var frameworkid = 0;\n\n /**\n * Callback to replace the dom element with the rendered template.\n *\n * @param {String} newhtml The new html to insert.\n * @param {String} newjs The new js to run.\n */\n var updatePage = function(newhtml, newjs) {\n $('[data-region=\"managecompetencies\"]').replaceWith(newhtml);\n templates.runTemplateJS(newjs);\n };\n\n /**\n * Callback to render the page template again and update the page.\n *\n * @param {Object} context The context for the template.\n */\n var reloadList = function(context) {\n templates.render('tool_lp/manage_competency_frameworks_page', context)\n .done(updatePage)\n .fail(notification.exception);\n };\n\n /**\n * Duplicate a framework and reload the page.\n * @method doDuplicate\n * @param {Event} e\n */\n var doDuplicate = function(e) {\n e.preventDefault();\n\n frameworkid = $(this).attr('data-frameworkid');\n\n // We are chaining ajax requests here.\n var requests = ajax.call([{\n methodname: 'core_competency_duplicate_competency_framework',\n args: {id: frameworkid}\n }, {\n methodname: 'tool_lp_data_for_competency_frameworks_manage_page',\n args: {\n pagecontext: {\n contextid: pagecontextid\n }\n }\n }]);\n requests[1].done(reloadList).fail(notification.exception);\n };\n /**\n * Delete a framework and reload the page.\n */\n var doDelete = function() {\n\n // We are chaining ajax requests here.\n var requests = ajax.call([{\n methodname: 'core_competency_delete_competency_framework',\n args: {id: frameworkid}\n }, {\n methodname: 'tool_lp_data_for_competency_frameworks_manage_page',\n args: {\n pagecontext: {\n contextid: pagecontextid\n }\n }\n }]);\n requests[0].done(function(success) {\n if (success === false) {\n var req = ajax.call([{\n methodname: 'core_competency_read_competency_framework',\n args: {id: frameworkid}\n }]);\n req[0].done(function(framework) {\n str.get_strings([\n {key: 'frameworkcannotbedeleted', component: 'tool_lp', param: framework.shortname},\n {key: 'cancel', component: 'moodle'}\n ]).done(function(strings) {\n notification.alert(\n null,\n strings[0]\n );\n }).fail(notification.exception);\n });\n }\n }).fail(notification.exception);\n requests[1].done(reloadList).fail(notification.exception);\n };\n\n /**\n * Handler for \"Delete competency framework\" actions.\n * @param {Event} e\n */\n var confirmDelete = function(e) {\n e.preventDefault();\n\n var id = $(this).attr('data-frameworkid');\n frameworkid = id;\n\n var requests = ajax.call([{\n methodname: 'core_competency_read_competency_framework',\n args: {id: frameworkid}\n }]);\n\n requests[0].done(function(framework) {\n str.get_strings([\n {key: 'confirm', component: 'moodle'},\n {key: 'deletecompetencyframework', component: 'tool_lp', param: framework.shortname},\n {key: 'delete', component: 'moodle'},\n {key: 'cancel', component: 'moodle'}\n ]).done(function(strings) {\n notification.confirm(\n strings[0], // Confirm.\n strings[1], // Delete competency framework X?\n strings[2], // Delete.\n strings[3], // Cancel.\n doDelete\n );\n }).fail(notification.exception);\n }).fail(notification.exception);\n\n };\n\n\n return /** @alias module:tool_lp/frameworkactions */ {\n // Public variables and functions.\n\n /**\n * Expose the event handler for delete.\n * @method deleteHandler\n * @param {Event} e\n */\n deleteHandler: confirmDelete,\n\n /**\n * Expose the event handler for duplicate.\n * @method duplicateHandler\n * @param {Event} e\n */\n duplicateHandler: doDuplicate,\n\n /**\n * Initialise the module.\n * @method init\n * @param {Number} contextid The context id of the page.\n */\n init: function(contextid) {\n pagecontextid = contextid;\n }\n };\n});\n"],"names":["define","$","templates","ajax","notification","str","pagecontextid","frameworkid","updatePage","newhtml","newjs","replaceWith","runTemplateJS","reloadList","context","render","done","fail","exception","doDelete","requests","call","methodname","args","id","pagecontext","contextid","success","framework","get_strings","key","component","param","shortname","strings","alert","deleteHandler","e","preventDefault","this","attr","confirm","duplicateHandler","init"],"mappings":";;;;;;;AAsBAA,OAAM,2BAAC,CAAC,SAAU,iBAAkB,YAAa,oBAAqB,aAAa,SAASC,EAAGC,UAAWC,KAAMC,aAAcC,KAI1H,IAAIC,cAAgB,EAGhBC,YAAc,EAQdC,WAAa,SAASC,QAASC,OAC/BT,EAAE,sCAAsCU,YAAYF,SACpDP,UAAUU,cAAcF,QAQxBG,WAAa,SAASC,SACtBZ,UAAUa,OAAO,4CAA6CD,SACzDE,KAAKR,YACLS,KAAKb,aAAac,YA8BvBC,SAAW,WAGX,IAAIC,SAAWjB,KAAKkB,KAAK,CAAC,CACtBC,WAAY,8CACZC,KAAM,CAACC,GAAIjB,cACZ,CACCe,WAAY,qDACZC,KAAM,CACFE,YAAa,CACTC,UAAWpB,mBAIvBc,SAAS,GAAGJ,MAAK,SAASW,UACN,IAAZA,SACUxB,KAAKkB,KAAK,CAAC,CACjBC,WAAY,4CACZC,KAAM,CAACC,GAAIjB,gBAEX,GAAGS,MAAK,SAASY,WACjBvB,IAAIwB,YAAY,CACZ,CAACC,IAAK,2BAA4BC,UAAW,UAAWC,MAAOJ,UAAUK,WACzE,CAACH,IAAK,SAAUC,UAAW,YAC5Bf,MAAK,SAASkB,SACb9B,aAAa+B,MACT,KACAD,QAAQ,GAEf,IAAEjB,KAAKb,aAAac,UACzB,GAEP,IAAED,KAAKb,aAAac,WACrBE,SAAS,GAAGJ,KAAKH,YAAYI,KAAKb,aAAac,YAsCnD,MAAqD,CAQjDkB,cAvCgB,SAASC,GACzBA,EAAEC,iBAEF,IAAId,GAAKvB,EAAEsC,MAAMC,KAAK,oBACtBjC,YAAciB,GAECrB,KAAKkB,KAAK,CAAC,CACtBC,WAAY,4CACZC,KAAM,CAACC,GAAIjB,gBAGN,GAAGS,MAAK,SAASY,WACtBvB,IAAIwB,YAAY,CACZ,CAACC,IAAK,UAAWC,UAAW,UAC5B,CAACD,IAAK,4BAA6BC,UAAW,UAAWC,MAAOJ,UAAUK,WAC1E,CAACH,IAAK,SAAUC,UAAW,UAC3B,CAACD,IAAK,SAAUC,UAAW,YAC5Bf,MAAK,SAASkB,SACb9B,aAAaqC,QACTP,QAAQ,GACRA,QAAQ,GACRA,QAAQ,GACRA,QAAQ,GACRf,SAEP,IAAEF,KAAKb,aAAac,UACxB,IAAED,KAAKb,aAAac,YAoBrBwB,iBA5Gc,SAASL,GACvBA,EAAEC,iBAEF/B,YAAcN,EAAEsC,MAAMC,KAAK,oBAGZrC,KAAKkB,KAAK,CAAC,CACtBC,WAAY,iDACZC,KAAM,CAACC,GAAIjB,cACZ,CACCe,WAAY,qDACZC,KAAM,CACFE,YAAa,CACTC,UAAWpB,mBAId,GAAGU,KAAKH,YAAYI,KAAKb,aAAac,YAkG/CyB,KAAM,SAASjB,WACXpB,cAAgBoB,SACpB,EAER"}
\ No newline at end of file
diff --git a/admin/tool/lp/amd/build/frameworks_datasource.min.js.map b/admin/tool/lp/amd/build/frameworks_datasource.min.js.map
index 26a6873f93f..a124c0a32d7 100644
--- a/admin/tool/lp/amd/build/frameworks_datasource.min.js.map
+++ b/admin/tool/lp/amd/build/frameworks_datasource.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"frameworks_datasource.min.js","sources":["../src/frameworks_datasource.js"],"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 * Frameworks datasource.\n *\n * This module is compatible with core/form-autocomplete.\n *\n * @module tool_lp/frameworks_datasource\n * @copyright 2016 Frédéric Massart - FMCorz.net\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\ndefine(['jquery', 'core/ajax', 'core/notification'], function($, Ajax, Notification) {\n\n return /** @alias module:tool_lpmigrate/frameworks_datasource */ {\n\n /**\n * List frameworks.\n *\n * @param {Number} contextId The context ID.\n * @param {Object} options Additional parameters to pass to the external function.\n * @return {Promise}\n */\n list: function(contextId, options) {\n var args = {\n context: {\n contextid: contextId\n }\n };\n\n $.extend(args, typeof options === 'undefined' ? {} : options);\n return Ajax.call([{\n methodname: 'core_competency_list_competency_frameworks',\n args: args\n }])[0];\n },\n\n /**\n * Process the results for auto complete elements.\n *\n * @param {String} selector The selector of the auto complete element.\n * @param {Array} results An array or results.\n * @return {Array} New array of results.\n */\n processResults: function(selector, results) {\n var options = [];\n $.each(results, function(index, data) {\n options.push({\n value: data.id,\n label: data.shortname + ' ' + data.idnumber\n });\n });\n return options;\n },\n\n /**\n * Source of data for Ajax element.\n *\n * @param {String} selector The selector of the auto complete element.\n * @param {String} query The query string.\n * @param {Function} callback A callback function receiving an array of results.\n */\n /* eslint-disable promise/no-callback-in-promise */\n transport: function(selector, query, callback) {\n var el = $(selector),\n contextId = el.data('contextid'),\n onlyVisible = el.data('onlyvisible');\n\n if (!contextId) {\n throw new Error('The attribute data-contextid is required on ' + selector);\n }\n this.list(contextId, {\n query: query,\n onlyvisible: onlyVisible,\n }).then(callback).catch(Notification.exception);\n }\n };\n\n});\n"],"names":["define","$","Ajax","Notification","list","contextId","options","args","context","contextid","extend","call","methodname","processResults","selector","results","each","index","data","push","value","id","label","shortname","idnumber","transport","query","callback","el","onlyVisible","Error","onlyvisible","then","catch","exception"],"mappings":";;;;;;;;;AAyBAA,uCAAO,CAAC,SAAU,YAAa,sBAAsB,SAASC,EAAGC,KAAMC,oBAEF,CAS7DC,KAAM,SAASC,UAAWC,aAClBC,KAAO,CACHC,QAAS,CACLC,UAAWJ,mBAIvBJ,EAAES,OAAOH,UAAyB,IAAZD,QAA0B,GAAKA,SAC9CJ,KAAKS,KAAK,CAAC,CACdC,WAAY,6CACZL,KAAMA,QACN,IAURM,eAAgB,SAASC,SAAUC,aAC3BT,QAAU,UACdL,EAAEe,KAAKD,SAAS,SAASE,MAAOC,MAC5BZ,QAAQa,KAAK,CACTC,MAAOF,KAAKG,GACZC,MAAOJ,KAAKK,UAAY,IAAML,KAAKM,cAGpClB,SAWXmB,UAAW,SAASX,SAAUY,MAAOC,cAC7BC,GAAK3B,EAAEa,UACPT,UAAYuB,GAAGV,KAAK,aACpBW,YAAcD,GAAGV,KAAK,mBAErBb,gBACK,IAAIyB,MAAM,+CAAiDhB,eAEhEV,KAAKC,UAAW,CACjBqB,MAAOA,MACPK,YAAaF,cACdG,KAAKL,UAAUM,MAAM9B,aAAa+B"}
\ No newline at end of file
+{"version":3,"file":"frameworks_datasource.min.js","sources":["../src/frameworks_datasource.js"],"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 * Frameworks datasource.\n *\n * This module is compatible with core/form-autocomplete.\n *\n * @module tool_lp/frameworks_datasource\n * @copyright 2016 Frédéric Massart - FMCorz.net\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\ndefine(['jquery', 'core/ajax', 'core/notification'], function($, Ajax, Notification) {\n\n return /** @alias module:tool_lpmigrate/frameworks_datasource */ {\n\n /**\n * List frameworks.\n *\n * @param {Number} contextId The context ID.\n * @param {Object} options Additional parameters to pass to the external function.\n * @return {Promise}\n */\n list: function(contextId, options) {\n var args = {\n context: {\n contextid: contextId\n }\n };\n\n $.extend(args, typeof options === 'undefined' ? {} : options);\n return Ajax.call([{\n methodname: 'core_competency_list_competency_frameworks',\n args: args\n }])[0];\n },\n\n /**\n * Process the results for auto complete elements.\n *\n * @param {String} selector The selector of the auto complete element.\n * @param {Array} results An array or results.\n * @return {Array} New array of results.\n */\n processResults: function(selector, results) {\n var options = [];\n $.each(results, function(index, data) {\n options.push({\n value: data.id,\n label: data.shortname + ' ' + data.idnumber\n });\n });\n return options;\n },\n\n /**\n * Source of data for Ajax element.\n *\n * @param {String} selector The selector of the auto complete element.\n * @param {String} query The query string.\n * @param {Function} callback A callback function receiving an array of results.\n */\n /* eslint-disable promise/no-callback-in-promise */\n transport: function(selector, query, callback) {\n var el = $(selector),\n contextId = el.data('contextid'),\n onlyVisible = el.data('onlyvisible');\n\n if (!contextId) {\n throw new Error('The attribute data-contextid is required on ' + selector);\n }\n this.list(contextId, {\n query: query,\n onlyvisible: onlyVisible,\n }).then(callback).catch(Notification.exception);\n }\n };\n\n});\n"],"names":["define","$","Ajax","Notification","list","contextId","options","args","context","contextid","extend","call","methodname","processResults","selector","results","each","index","data","push","value","id","label","shortname","idnumber","transport","query","callback","el","onlyVisible","Error","this","onlyvisible","then","catch","exception"],"mappings":";;;;;;;;;AAyBAA,OAAO,gCAAA,CAAC,SAAU,YAAa,sBAAsB,SAASC,EAAGC,KAAMC,cAEnE,MAAiE,CAS7DC,KAAM,SAASC,UAAWC,SACtB,IAAIC,KAAO,CACHC,QAAS,CACLC,UAAWJ,YAKvB,OADAJ,EAAES,OAAOH,UAAyB,IAAZD,QAA0B,CAAA,EAAKA,SAC9CJ,KAAKS,KAAK,CAAC,CACdC,WAAY,6CACZL,KAAMA,QACN,EACP,EASDM,eAAgB,SAASC,SAAUC,SAC/B,IAAIT,QAAU,GAOd,OANAL,EAAEe,KAAKD,SAAS,SAASE,MAAOC,MAC5BZ,QAAQa,KAAK,CACTC,MAAOF,KAAKG,GACZC,MAAOJ,KAAKK,UAAY,IAAML,KAAKM,UAE3C,IACOlB,OACV,EAUDmB,UAAW,SAASX,SAAUY,MAAOC,UACjC,IAAIC,GAAK3B,EAAEa,UACPT,UAAYuB,GAAGV,KAAK,aACpBW,YAAcD,GAAGV,KAAK,eAE1B,IAAKb,UACD,MAAM,IAAIyB,MAAM,+CAAiDhB,UAErEiB,KAAK3B,KAAKC,UAAW,CACjBqB,MAAOA,MACPM,YAAaH,cACdI,KAAKN,UAAUO,MAAM/B,aAAagC,UACzC,EAGR"}
\ No newline at end of file
diff --git a/admin/tool/lp/amd/build/grade_dialogue.min.js.map b/admin/tool/lp/amd/build/grade_dialogue.min.js.map
index 52764f4e8fe..64af8431358 100644
--- a/admin/tool/lp/amd/build/grade_dialogue.min.js.map
+++ b/admin/tool/lp/amd/build/grade_dialogue.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"grade_dialogue.min.js","sources":["../src/grade_dialogue.js"],"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 * Grade dialogue.\n *\n * @module tool_lp/grade_dialogue\n * @copyright 2016 Frédéric Massart - FMCorz.net\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\ndefine(['jquery',\n 'core/notification',\n 'core/templates',\n 'tool_lp/dialogue',\n 'tool_lp/event_base',\n 'core/str'],\n function($, Notification, Templates, Dialogue, EventBase, Str) {\n\n /**\n * Grade dialogue class.\n *\n * @class tool_lp/grade_dialogue\n * @param {Array} ratingOptions\n */\n var Grade = function(ratingOptions) {\n EventBase.prototype.constructor.apply(this, []);\n this._ratingOptions = ratingOptions;\n };\n Grade.prototype = Object.create(EventBase.prototype);\n\n /** @property {Dialogue} The dialogue. */\n Grade.prototype._popup = null;\n /** @property {Array} Array of objects containing, 'value', 'name' and optionally 'selected'. */\n Grade.prototype._ratingOptions = null;\n\n /**\n * After render hook.\n *\n * @method _afterRender\n * @protected\n */\n Grade.prototype._afterRender = function() {\n var btnRate = this._find('[data-action=\"rate\"]'),\n lstRating = this._find('[name=\"rating\"]'),\n txtComment = this._find('[name=\"comment\"]');\n\n this._find('[data-action=\"cancel\"]').click(function(e) {\n e.preventDefault();\n this._trigger('cancelled');\n this.close();\n }.bind(this));\n\n lstRating.change(function() {\n var node = $(this);\n if (!node.val()) {\n btnRate.prop('disabled', true);\n } else {\n btnRate.prop('disabled', false);\n }\n }).change();\n\n btnRate.click(function(e) {\n e.preventDefault();\n var val = lstRating.val();\n if (!val) {\n return;\n }\n this._trigger('rated', {\n 'rating': val,\n 'note': txtComment.val()\n });\n this.close();\n }.bind(this));\n };\n\n /**\n * Close the dialogue.\n *\n * @method close\n */\n Grade.prototype.close = function() {\n this._popup.close();\n this._popup = null;\n };\n\n /**\n * Opens the picker.\n *\n * @method display\n * @return {Promise}\n */\n Grade.prototype.display = function() {\n M.util.js_pending('tool_lp/grade_dialogue:display');\n return $.when(\n Str.get_string('rate', 'tool_lp'),\n this._render()\n )\n .then(function(title, templateResult) {\n this._popup = new Dialogue(\n title,\n templateResult[0],\n function() {\n this._afterRender();\n M.util.js_complete('tool_lp/grade_dialogue:display');\n }.bind(this)\n );\n\n return this._popup;\n }.bind(this))\n .catch(Notification.exception);\n };\n\n /**\n * Find a node in the dialogue.\n *\n * @param {String} selector\n * @method _find\n * @returns {node} The node\n * @protected\n */\n Grade.prototype._find = function(selector) {\n return $(this._popup.getContent()).find(selector);\n };\n\n /**\n * Render the dialogue.\n *\n * @method _render\n * @protected\n * @return {Promise}\n */\n Grade.prototype._render = function() {\n var context = {\n cangrade: this._canGrade,\n ratings: this._ratingOptions\n };\n return Templates.render('tool_lp/competency_grader', context);\n };\n\n return Grade;\n});\n"],"names":["define","$","Notification","Templates","Dialogue","EventBase","Str","Grade","ratingOptions","prototype","constructor","apply","this","_ratingOptions","Object","create","_popup","_afterRender","btnRate","_find","lstRating","txtComment","click","e","preventDefault","_trigger","close","bind","change","val","prop","display","M","util","js_pending","when","get_string","_render","then","title","templateResult","js_complete","catch","exception","selector","getContent","find","context","cangrade","_canGrade","ratings","render"],"mappings":";;;;;;;AAuBAA,gCAAO,CAAC,SACA,oBACA,iBACA,mBACA,qBACA,aACA,SAASC,EAAGC,aAAcC,UAAWC,SAAUC,UAAWC,SAQ1DC,MAAQ,SAASC,eACjBH,UAAUI,UAAUC,YAAYC,MAAMC,KAAM,SACvCC,eAAiBL,sBAE1BD,MAAME,UAAYK,OAAOC,OAAOV,UAAUI,YAG1BO,OAAS,KAEzBT,MAAME,UAAUI,eAAiB,KAQjCN,MAAME,UAAUQ,aAAe,eACvBC,QAAUN,KAAKO,MAAM,wBACrBC,UAAYR,KAAKO,MAAM,mBACvBE,WAAaT,KAAKO,MAAM,yBAEvBA,MAAM,0BAA0BG,MAAM,SAASC,GAChDA,EAAEC,sBACGC,SAAS,kBACTC,SACPC,KAAKf,OAEPQ,UAAUQ,QAAO,WACF3B,EAAEW,MACHiB,MAGNX,QAAQY,KAAK,YAAY,GAFzBZ,QAAQY,KAAK,YAAY,MAI9BF,SAEHV,QAAQI,MAAM,SAASC,GACnBA,EAAEC,qBACEK,IAAMT,UAAUS,MACfA,WAGAJ,SAAS,QAAS,QACTI,SACFR,WAAWQ,aAElBH,UACPC,KAAKf,QAQXL,MAAME,UAAUiB,MAAQ,gBACfV,OAAOU,aACPV,OAAS,MASlBT,MAAME,UAAUsB,QAAU,kBACtBC,EAAEC,KAAKC,WAAW,kCACXjC,EAAEkC,KACL7B,IAAI8B,WAAW,OAAQ,WACvBxB,KAAKyB,WAERC,KAAK,SAASC,MAAOC,4BACbxB,OAAS,IAAIZ,SACdmC,MACAC,eAAe,GACf,gBACSvB,eACLe,EAAEC,KAAKQ,YAAY,mCACrBd,KAAKf,OAGJA,KAAKI,QACdW,KAAKf,OACN8B,MAAMxC,aAAayC,YAWxBpC,MAAME,UAAUU,MAAQ,SAASyB,iBACtB3C,EAAEW,KAAKI,OAAO6B,cAAcC,KAAKF,WAU5CrC,MAAME,UAAU4B,QAAU,eAClBU,QAAU,CACVC,SAAUpC,KAAKqC,UACfC,QAAStC,KAAKC,uBAEXV,UAAUgD,OAAO,4BAA6BJ,UAGlDxC"}
\ No newline at end of file
+{"version":3,"file":"grade_dialogue.min.js","sources":["../src/grade_dialogue.js"],"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 * Grade dialogue.\n *\n * @module tool_lp/grade_dialogue\n * @copyright 2016 Frédéric Massart - FMCorz.net\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\ndefine(['jquery',\n 'core/notification',\n 'core/templates',\n 'tool_lp/dialogue',\n 'tool_lp/event_base',\n 'core/str'],\n function($, Notification, Templates, Dialogue, EventBase, Str) {\n\n /**\n * Grade dialogue class.\n *\n * @class tool_lp/grade_dialogue\n * @param {Array} ratingOptions\n */\n var Grade = function(ratingOptions) {\n EventBase.prototype.constructor.apply(this, []);\n this._ratingOptions = ratingOptions;\n };\n Grade.prototype = Object.create(EventBase.prototype);\n\n /** @property {Dialogue} The dialogue. */\n Grade.prototype._popup = null;\n /** @property {Array} Array of objects containing, 'value', 'name' and optionally 'selected'. */\n Grade.prototype._ratingOptions = null;\n\n /**\n * After render hook.\n *\n * @method _afterRender\n * @protected\n */\n Grade.prototype._afterRender = function() {\n var btnRate = this._find('[data-action=\"rate\"]'),\n lstRating = this._find('[name=\"rating\"]'),\n txtComment = this._find('[name=\"comment\"]');\n\n this._find('[data-action=\"cancel\"]').click(function(e) {\n e.preventDefault();\n this._trigger('cancelled');\n this.close();\n }.bind(this));\n\n lstRating.change(function() {\n var node = $(this);\n if (!node.val()) {\n btnRate.prop('disabled', true);\n } else {\n btnRate.prop('disabled', false);\n }\n }).change();\n\n btnRate.click(function(e) {\n e.preventDefault();\n var val = lstRating.val();\n if (!val) {\n return;\n }\n this._trigger('rated', {\n 'rating': val,\n 'note': txtComment.val()\n });\n this.close();\n }.bind(this));\n };\n\n /**\n * Close the dialogue.\n *\n * @method close\n */\n Grade.prototype.close = function() {\n this._popup.close();\n this._popup = null;\n };\n\n /**\n * Opens the picker.\n *\n * @method display\n * @return {Promise}\n */\n Grade.prototype.display = function() {\n M.util.js_pending('tool_lp/grade_dialogue:display');\n return $.when(\n Str.get_string('rate', 'tool_lp'),\n this._render()\n )\n .then(function(title, templateResult) {\n this._popup = new Dialogue(\n title,\n templateResult[0],\n function() {\n this._afterRender();\n M.util.js_complete('tool_lp/grade_dialogue:display');\n }.bind(this)\n );\n\n return this._popup;\n }.bind(this))\n .catch(Notification.exception);\n };\n\n /**\n * Find a node in the dialogue.\n *\n * @param {String} selector\n * @method _find\n * @returns {node} The node\n * @protected\n */\n Grade.prototype._find = function(selector) {\n return $(this._popup.getContent()).find(selector);\n };\n\n /**\n * Render the dialogue.\n *\n * @method _render\n * @protected\n * @return {Promise}\n */\n Grade.prototype._render = function() {\n var context = {\n cangrade: this._canGrade,\n ratings: this._ratingOptions\n };\n return Templates.render('tool_lp/competency_grader', context);\n };\n\n return Grade;\n});\n"],"names":["define","$","Notification","Templates","Dialogue","EventBase","Str","Grade","ratingOptions","prototype","constructor","apply","this","_ratingOptions","Object","create","_popup","_afterRender","btnRate","_find","lstRating","txtComment","click","e","preventDefault","_trigger","close","bind","change","val","prop","rating","note","display","M","util","js_pending","when","get_string","_render","then","title","templateResult","js_complete","catch","exception","selector","getContent","find","context","cangrade","_canGrade","ratings","render"],"mappings":";;;;;;;AAuBAA,OAAM,yBAAC,CAAC,SACA,oBACA,iBACA,mBACA,qBACA,aACA,SAASC,EAAGC,aAAcC,UAAWC,SAAUC,UAAWC,KAQ9D,IAAIC,MAAQ,SAASC,eACjBH,UAAUI,UAAUC,YAAYC,MAAMC,KAAM,IAC5CA,KAAKC,eAAiBL,eAiH1B,OA/GAD,MAAME,UAAYK,OAAOC,OAAOV,UAAUI,YAG1BO,OAAS,KAEzBT,MAAME,UAAUI,eAAiB,KAQjCN,MAAME,UAAUQ,aAAe,WAC3B,IAAIC,QAAUN,KAAKO,MAAM,wBACrBC,UAAYR,KAAKO,MAAM,mBACvBE,WAAaT,KAAKO,MAAM,oBAE5BP,KAAKO,MAAM,0BAA0BG,MAAM,SAASC,GAChDA,EAAEC,iBACFZ,KAAKa,SAAS,aACdb,KAAKc,OACT,EAAEC,KAAKf,OAEPQ,UAAUQ,QAAO,WACF3B,EAAEW,MACHiB,MAGNX,QAAQY,KAAK,YAAY,GAFzBZ,QAAQY,KAAK,YAAY,MAI9BF,SAEHV,QAAQI,MAAM,SAASC,GACnBA,EAAEC,iBACF,IAAIK,IAAMT,UAAUS,MACfA,MAGLjB,KAAKa,SAAS,QAAS,CACnBM,OAAUF,IACVG,KAAQX,WAAWQ,QAEvBjB,KAAKc,QACT,EAAEC,KAAKf,QAQXL,MAAME,UAAUiB,MAAQ,WACpBd,KAAKI,OAAOU,QACZd,KAAKI,OAAS,MASlBT,MAAME,UAAUwB,QAAU,WAEtB,OADAC,EAAEC,KAAKC,WAAW,kCACXnC,EAAEoC,KACL/B,IAAIgC,WAAW,OAAQ,WACvB1B,KAAK2B,WAERC,KAAK,SAASC,MAAOC,gBAUlB,OATA9B,KAAKI,OAAS,IAAIZ,SACdqC,MACAC,eAAe,GACf,WACI9B,KAAKK,eACLiB,EAAEC,KAAKQ,YAAY,iCACvB,EAAEhB,KAAKf,OAGJA,KAAKI,MAChB,EAAEW,KAAKf,OACNgC,MAAM1C,aAAa2C,YAWxBtC,MAAME,UAAUU,MAAQ,SAAS2B,UAC7B,OAAO7C,EAAEW,KAAKI,OAAO+B,cAAcC,KAAKF,WAU5CvC,MAAME,UAAU8B,QAAU,WACtB,IAAIU,QAAU,CACVC,SAAUtC,KAAKuC,UACfC,QAASxC,KAAKC,gBAElB,OAAOV,UAAUkD,OAAO,4BAA6BJ,UAGlD1C,KACX"}
\ No newline at end of file
diff --git a/admin/tool/lp/amd/build/grade_user_competency_inline.min.js.map b/admin/tool/lp/amd/build/grade_user_competency_inline.min.js.map
index 180d674de1a..ab0a1e7bf7d 100644
--- a/admin/tool/lp/amd/build/grade_user_competency_inline.min.js.map
+++ b/admin/tool/lp/amd/build/grade_user_competency_inline.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"grade_user_competency_inline.min.js","sources":["../src/grade_user_competency_inline.js"],"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 * Module to enable inline editing of a comptency grade.\n *\n * @module tool_lp/grade_user_competency_inline\n * @copyright 2015 Damyon Wiese\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\ndefine(['jquery',\n 'core/notification',\n 'core/ajax',\n 'core/log',\n 'tool_lp/grade_dialogue',\n 'tool_lp/event_base',\n 'tool_lp/scalevalues',\n ], function($, notification, ajax, log, GradeDialogue, EventBase, ScaleValues) {\n\n /**\n * InlineEditor\n *\n * @class tool_lp/grade_user_competency_inline\n * @param {String} selector The selector to trigger the grading.\n * @param {Number} scaleId The id of the scale for this competency.\n * @param {Number} competencyId The id of the competency.\n * @param {Number} userId The id of the user.\n * @param {Number} planId The id of the plan.\n * @param {Number} courseId The id of the course.\n * @param {String} chooseStr Language string for choose a rating.\n */\n var InlineEditor = function(selector, scaleId, competencyId, userId, planId, courseId, chooseStr) {\n EventBase.prototype.constructor.apply(this, []);\n\n var trigger = $(selector);\n if (!trigger.length) {\n throw new Error('Could not find the trigger');\n }\n\n this._scaleId = scaleId;\n this._competencyId = competencyId;\n this._userId = userId;\n this._planId = planId;\n this._courseId = courseId;\n this._chooseStr = chooseStr;\n this._setUp();\n\n trigger.click(function(e) {\n e.preventDefault();\n this._dialogue.display();\n }.bind(this));\n\n if (this._planId) {\n this._methodName = 'core_competency_grade_competency_in_plan';\n this._args = {\n competencyid: this._competencyId,\n planid: this._planId\n };\n } else if (this._courseId) {\n this._methodName = 'core_competency_grade_competency_in_course';\n this._args = {\n competencyid: this._competencyId,\n courseid: this._courseId,\n userid: this._userId\n };\n } else {\n this._methodName = 'core_competency_grade_competency';\n this._args = {\n userid: this._userId,\n competencyid: this._competencyId\n };\n }\n };\n InlineEditor.prototype = Object.create(EventBase.prototype);\n\n /**\n * Setup.\n *\n * @method _setUp\n */\n InlineEditor.prototype._setUp = function() {\n var options = [],\n self = this;\n\n M.util.js_pending('tool_lp/grade_user_competency_inline:_setUp');\n var promise = ScaleValues.get_values(self._scaleId);\n promise.then(function(scalevalues) {\n options.push({\n value: '',\n name: self._chooseStr\n });\n\n for (var i = 0; i < scalevalues.length; i++) {\n var optionConfig = scalevalues[i];\n options.push({\n value: optionConfig.id,\n name: optionConfig.name\n });\n }\n\n return options;\n })\n .then(function(options) {\n return new GradeDialogue(options);\n })\n .then(function(dialogue) {\n dialogue.on('rated', function(e, data) {\n var args = self._args;\n args.grade = data.rating;\n args.note = data.note;\n ajax.call([{\n methodname: self._methodName,\n args: args,\n done: function(evidence) {\n self._trigger('competencyupdated', {args: args, evidence: evidence});\n },\n fail: notification.exception\n }]);\n });\n\n return dialogue;\n })\n .then(function(dialogue) {\n self._dialogue = dialogue;\n\n M.util.js_complete('tool_lp/grade_user_competency_inline:_setUp');\n return;\n })\n .fail(notification.exception);\n };\n\n /** @property {Number} The scale id for this competency. */\n InlineEditor.prototype._scaleId = null;\n /** @property {Number} The id of the competency. */\n InlineEditor.prototype._competencyId = null;\n /** @property {Number} The id of the user. */\n InlineEditor.prototype._userId = null;\n /** @property {Number} The id of the plan. */\n InlineEditor.prototype._planId = null;\n /** @property {Number} The id of the course. */\n InlineEditor.prototype._courseId = null;\n /** @property {String} The text for Choose rating. */\n InlineEditor.prototype._chooseStr = null;\n /** @property {GradeDialogue} The grading dialogue. */\n InlineEditor.prototype._dialogue = null;\n\n return InlineEditor;\n});\n"],"names":["define","$","notification","ajax","log","GradeDialogue","EventBase","ScaleValues","InlineEditor","selector","scaleId","competencyId","userId","planId","courseId","chooseStr","prototype","constructor","apply","this","trigger","length","Error","_scaleId","_competencyId","_userId","_planId","_courseId","_chooseStr","_setUp","click","e","preventDefault","_dialogue","display","bind","_methodName","_args","competencyid","planid","courseid","userid","Object","create","options","self","M","util","js_pending","get_values","then","scalevalues","push","value","name","i","optionConfig","id","dialogue","on","data","args","grade","rating","note","call","methodname","done","evidence","_trigger","fail","exception","js_complete"],"mappings":";;;;;;;AAuBAA,8CAAO,CAAC,SACA,oBACA,YACA,WACA,yBACA,qBACA,wBACD,SAASC,EAAGC,aAAcC,KAAMC,IAAKC,cAAeC,UAAWC,iBAc9DC,aAAe,SAASC,SAAUC,QAASC,aAAcC,OAAQC,OAAQC,SAAUC,WACnFT,UAAUU,UAAUC,YAAYC,MAAMC,KAAM,QAExCC,QAAUnB,EAAEQ,cACXW,QAAQC,aACH,IAAIC,MAAM,mCAGfC,SAAWb,aACXc,cAAgBb,kBAChBc,QAAUb,YACVc,QAAUb,YACVc,UAAYb,cACZc,WAAab,eACbc,SAELT,QAAQU,MAAM,SAASC,GACnBA,EAAEC,sBACGC,UAAUC,WACjBC,KAAKhB,OAEHA,KAAKO,cACAU,YAAc,gDACdC,MAAQ,CACTC,aAAcnB,KAAKK,cACnBe,OAAQpB,KAAKO,UAEVP,KAAKQ,gBACPS,YAAc,kDACdC,MAAQ,CACTC,aAAcnB,KAAKK,cACnBgB,SAAUrB,KAAKQ,UACfc,OAAQtB,KAAKM,gBAGZW,YAAc,wCACdC,MAAQ,CACTI,OAAQtB,KAAKM,QACba,aAAcnB,KAAKK,wBAI/BhB,aAAaQ,UAAY0B,OAAOC,OAAOrC,UAAUU,YAO1Ba,OAAS,eACxBe,QAAU,GACVC,KAAO1B,KAEX2B,EAAEC,KAAKC,WAAW,+CACJzC,YAAY0C,WAAWJ,KAAKtB,UAClC2B,MAAK,SAASC,aAClBP,QAAQQ,KAAK,CACTC,MAAO,GACPC,KAAMT,KAAKjB,iBAGV,IAAI2B,EAAI,EAAGA,EAAIJ,YAAY9B,OAAQkC,IAAK,KACrCC,aAAeL,YAAYI,GAC/BX,QAAQQ,KAAK,CACTC,MAAOG,aAAaC,GACpBH,KAAME,aAAaF,cAIpBV,WAEVM,MAAK,SAASN,gBACJ,IAAIvC,cAAcuC,YAE5BM,MAAK,SAASQ,iBACXA,SAASC,GAAG,SAAS,SAAS5B,EAAG6B,UACzBC,KAAOhB,KAAKR,MAChBwB,KAAKC,MAAQF,KAAKG,OAClBF,KAAKG,KAAOJ,KAAKI,KACjB7D,KAAK8D,KAAK,CAAC,CACPC,WAAYrB,KAAKT,YACjByB,KAAMA,KACNM,KAAM,SAASC,UACXvB,KAAKwB,SAAS,oBAAqB,CAACR,KAAMA,KAAMO,SAAUA,YAE9DE,KAAMpE,aAAaqE,gBAIpBb,YAEVR,MAAK,SAASQ,UACXb,KAAKZ,UAAYyB,SAEjBZ,EAAEC,KAAKyB,YAAY,kDAGtBF,KAAKpE,aAAaqE,YAIvB/D,aAAaQ,UAAUO,SAAW,KAElCf,aAAaQ,UAAUQ,cAAgB,KAEvChB,aAAaQ,UAAUS,QAAU,KAEjCjB,aAAaQ,UAAUU,QAAU,KAEjClB,aAAaQ,UAAUW,UAAY,KAEnCnB,aAAaQ,UAAUY,WAAa,KAEpCpB,aAAaQ,UAAUiB,UAAY,KAE5BzB"}
\ No newline at end of file
+{"version":3,"file":"grade_user_competency_inline.min.js","sources":["../src/grade_user_competency_inline.js"],"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 * Module to enable inline editing of a comptency grade.\n *\n * @module tool_lp/grade_user_competency_inline\n * @copyright 2015 Damyon Wiese\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\ndefine(['jquery',\n 'core/notification',\n 'core/ajax',\n 'core/log',\n 'tool_lp/grade_dialogue',\n 'tool_lp/event_base',\n 'tool_lp/scalevalues',\n ], function($, notification, ajax, log, GradeDialogue, EventBase, ScaleValues) {\n\n /**\n * InlineEditor\n *\n * @class tool_lp/grade_user_competency_inline\n * @param {String} selector The selector to trigger the grading.\n * @param {Number} scaleId The id of the scale for this competency.\n * @param {Number} competencyId The id of the competency.\n * @param {Number} userId The id of the user.\n * @param {Number} planId The id of the plan.\n * @param {Number} courseId The id of the course.\n * @param {String} chooseStr Language string for choose a rating.\n */\n var InlineEditor = function(selector, scaleId, competencyId, userId, planId, courseId, chooseStr) {\n EventBase.prototype.constructor.apply(this, []);\n\n var trigger = $(selector);\n if (!trigger.length) {\n throw new Error('Could not find the trigger');\n }\n\n this._scaleId = scaleId;\n this._competencyId = competencyId;\n this._userId = userId;\n this._planId = planId;\n this._courseId = courseId;\n this._chooseStr = chooseStr;\n this._setUp();\n\n trigger.click(function(e) {\n e.preventDefault();\n this._dialogue.display();\n }.bind(this));\n\n if (this._planId) {\n this._methodName = 'core_competency_grade_competency_in_plan';\n this._args = {\n competencyid: this._competencyId,\n planid: this._planId\n };\n } else if (this._courseId) {\n this._methodName = 'core_competency_grade_competency_in_course';\n this._args = {\n competencyid: this._competencyId,\n courseid: this._courseId,\n userid: this._userId\n };\n } else {\n this._methodName = 'core_competency_grade_competency';\n this._args = {\n userid: this._userId,\n competencyid: this._competencyId\n };\n }\n };\n InlineEditor.prototype = Object.create(EventBase.prototype);\n\n /**\n * Setup.\n *\n * @method _setUp\n */\n InlineEditor.prototype._setUp = function() {\n var options = [],\n self = this;\n\n M.util.js_pending('tool_lp/grade_user_competency_inline:_setUp');\n var promise = ScaleValues.get_values(self._scaleId);\n promise.then(function(scalevalues) {\n options.push({\n value: '',\n name: self._chooseStr\n });\n\n for (var i = 0; i < scalevalues.length; i++) {\n var optionConfig = scalevalues[i];\n options.push({\n value: optionConfig.id,\n name: optionConfig.name\n });\n }\n\n return options;\n })\n .then(function(options) {\n return new GradeDialogue(options);\n })\n .then(function(dialogue) {\n dialogue.on('rated', function(e, data) {\n var args = self._args;\n args.grade = data.rating;\n args.note = data.note;\n ajax.call([{\n methodname: self._methodName,\n args: args,\n done: function(evidence) {\n self._trigger('competencyupdated', {args: args, evidence: evidence});\n },\n fail: notification.exception\n }]);\n });\n\n return dialogue;\n })\n .then(function(dialogue) {\n self._dialogue = dialogue;\n\n M.util.js_complete('tool_lp/grade_user_competency_inline:_setUp');\n return;\n })\n .fail(notification.exception);\n };\n\n /** @property {Number} The scale id for this competency. */\n InlineEditor.prototype._scaleId = null;\n /** @property {Number} The id of the competency. */\n InlineEditor.prototype._competencyId = null;\n /** @property {Number} The id of the user. */\n InlineEditor.prototype._userId = null;\n /** @property {Number} The id of the plan. */\n InlineEditor.prototype._planId = null;\n /** @property {Number} The id of the course. */\n InlineEditor.prototype._courseId = null;\n /** @property {String} The text for Choose rating. */\n InlineEditor.prototype._chooseStr = null;\n /** @property {GradeDialogue} The grading dialogue. */\n InlineEditor.prototype._dialogue = null;\n\n return InlineEditor;\n});\n"],"names":["define","$","notification","ajax","log","GradeDialogue","EventBase","ScaleValues","InlineEditor","selector","scaleId","competencyId","userId","planId","courseId","chooseStr","prototype","constructor","apply","this","trigger","length","Error","_scaleId","_competencyId","_userId","_planId","_courseId","_chooseStr","_setUp","click","e","preventDefault","_dialogue","display","bind","_methodName","_args","competencyid","planid","courseid","userid","Object","create","options","self","M","util","js_pending","get_values","then","scalevalues","push","value","name","i","optionConfig","id","dialogue","on","data","args","grade","rating","note","call","methodname","done","evidence","_trigger","fail","exception","js_complete"],"mappings":";;;;;;;AAuBAA,OAAO,uCAAA,CAAC,SACA,oBACA,YACA,WACA,yBACA,qBACA,wBACD,SAASC,EAAGC,aAAcC,KAAMC,IAAKC,cAAeC,UAAWC,aAclE,IAAIC,aAAe,SAASC,SAAUC,QAASC,aAAcC,OAAQC,OAAQC,SAAUC,WACnFT,UAAUU,UAAUC,YAAYC,MAAMC,KAAM,IAE5C,IAAIC,QAAUnB,EAAEQ,UAChB,IAAKW,QAAQC,OACT,MAAM,IAAIC,MAAM,8BAGpBH,KAAKI,SAAWb,QAChBS,KAAKK,cAAgBb,aACrBQ,KAAKM,QAAUb,OACfO,KAAKO,QAAUb,OACfM,KAAKQ,UAAYb,SACjBK,KAAKS,WAAab,UAClBI,KAAKU,SAELT,QAAQU,MAAM,SAASC,GACnBA,EAAEC,iBACFb,KAAKc,UAAUC,SACnB,EAAEC,KAAKhB,OAEHA,KAAKO,SACLP,KAAKiB,YAAc,2CACnBjB,KAAKkB,MAAQ,CACTC,aAAcnB,KAAKK,cACnBe,OAAQpB,KAAKO,UAEVP,KAAKQ,WACZR,KAAKiB,YAAc,6CACnBjB,KAAKkB,MAAQ,CACTC,aAAcnB,KAAKK,cACnBgB,SAAUrB,KAAKQ,UACfc,OAAQtB,KAAKM,WAGjBN,KAAKiB,YAAc,mCACnBjB,KAAKkB,MAAQ,CACTI,OAAQtB,KAAKM,QACba,aAAcnB,KAAKK,iBA6E/B,OAzEAhB,aAAaQ,UAAY0B,OAAOC,OAAOrC,UAAUU,YAO1Ba,OAAS,WAC5B,IAAIe,QAAU,GACVC,KAAO1B,KAEX2B,EAAEC,KAAKC,WAAW,+CACJzC,YAAY0C,WAAWJ,KAAKtB,UAClC2B,MAAK,SAASC,aAClBP,QAAQQ,KAAK,CACTC,MAAO,GACPC,KAAMT,KAAKjB,aAGf,IAAK,IAAI2B,EAAI,EAAGA,EAAIJ,YAAY9B,OAAQkC,IAAK,CACzC,IAAIC,aAAeL,YAAYI,GAC/BX,QAAQQ,KAAK,CACTC,MAAOG,aAAaC,GACpBH,KAAME,aAAaF,MAE3B,CAEA,OAAOV,OACX,IACCM,MAAK,SAASN,SACX,OAAO,IAAIvC,cAAcuC,QAC7B,IACCM,MAAK,SAASQ,UAeX,OAdAA,SAASC,GAAG,SAAS,SAAS5B,EAAG6B,MAC7B,IAAIC,KAAOhB,KAAKR,MAChBwB,KAAKC,MAAQF,KAAKG,OAClBF,KAAKG,KAAOJ,KAAKI,KACjB7D,KAAK8D,KAAK,CAAC,CACPC,WAAYrB,KAAKT,YACjByB,KAAMA,KACNM,KAAM,SAASC,UACXvB,KAAKwB,SAAS,oBAAqB,CAACR,KAAMA,KAAMO,SAAUA,UAC7D,EACDE,KAAMpE,aAAaqE,YAE3B,IAEOb,QACX,IACCR,MAAK,SAASQ,UACXb,KAAKZ,UAAYyB,SAEjBZ,EAAEC,KAAKyB,YAAY,8CAEtB,IACAF,KAAKpE,aAAaqE,YAIvB/D,aAAaQ,UAAUO,SAAW,KAElCf,aAAaQ,UAAUQ,cAAgB,KAEvChB,aAAaQ,UAAUS,QAAU,KAEjCjB,aAAaQ,UAAUU,QAAU,KAEjClB,aAAaQ,UAAUW,UAAY,KAEnCnB,aAAaQ,UAAUY,WAAa,KAEpCpB,aAAaQ,UAAUiB,UAAY,KAE5BzB,YACX"}
\ No newline at end of file
diff --git a/admin/tool/lp/amd/build/menubar.min.js.map b/admin/tool/lp/amd/build/menubar.min.js.map
index 01dbfe4212f..9b8268c505f 100644
--- a/admin/tool/lp/amd/build/menubar.min.js.map
+++ b/admin/tool/lp/amd/build/menubar.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"menubar.min.js","sources":["../src/menubar.js"],"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 * Aria menubar functionality. Enhances a simple nested list structure into a full aria widget.\n * Based on the open ajax example: http://oaa-accessibility.org/example/26/\n *\n * @module tool_lp/menubar\n * @copyright 2015 Damyon Wiese \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(['jquery'], function($) {\n\n /** @property {boolean} Flag to indicate if we have already registered a click event handler for the document. */\n var documentClickHandlerRegistered = false;\n\n /** @property {boolean} Flag to indicate whether there's an active, open menu. */\n var menuActive = false;\n\n /**\n * Close all open submenus anywhere in the page (there should only ever be one open at a time).\n *\n * @method closeAllSubMenus\n */\n var closeAllSubMenus = function() {\n $('.tool-lp-menu .tool-lp-sub-menu').attr('aria-hidden', 'true');\n // Every menu's closed at this point, so set the menu active flag to false.\n menuActive = false;\n };\n\n /**\n * Constructor\n *\n * @param {jQuery} menuRoot Jquery collection matching the root of the menu.\n * @param {Function[]} handlers called when a menu item is chosen.\n */\n var Menubar = function(menuRoot, handlers) {\n // Setup private class variables.\n this.menuRoot = menuRoot;\n this.handlers = handlers;\n this.rootMenus = this.menuRoot.children('li');\n this.subMenus = this.rootMenus.children('ul');\n this.subMenuItems = this.subMenus.children('li');\n this.allItems = this.rootMenus.add(this.subMenuItems);\n this.activeItem = null;\n this.isChildOpen = false;\n\n this.keys = {\n tab: 9,\n enter: 13,\n esc: 27,\n space: 32,\n left: 37,\n up: 38,\n right: 39,\n down: 40\n };\n\n this.addAriaAttributes();\n // Add the event listeners.\n this.addEventListeners();\n };\n\n /**\n * Open a submenu, first it closes all other sub-menus and sets the open direction.\n * @method openSubMenu\n * @param {Node} menu\n */\n Menubar.prototype.openSubMenu = function(menu) {\n this.setOpenDirection();\n closeAllSubMenus();\n menu.attr('aria-hidden', 'false');\n // Set menu active flag to true when a menu is opened.\n menuActive = true;\n };\n\n\n /**\n * Bind the event listeners to the DOM\n * @method addEventListeners\n */\n Menubar.prototype.addEventListeners = function() {\n var currentThis = this;\n\n // When clicking outside the menubar.\n if (documentClickHandlerRegistered === false) {\n $(document).click(function() {\n // Check if a menu is opened.\n if (menuActive) {\n // Close menu.\n closeAllSubMenus();\n }\n });\n // Set this flag to true so that we won't need to add a document click handler for the other Menubar instances.\n documentClickHandlerRegistered = true;\n }\n\n // Hovers.\n this.subMenuItems.mouseenter(function() {\n $(this).addClass('menu-hover');\n return true;\n });\n\n this.subMenuItems.mouseout(function() {\n $(this).removeClass('menu-hover');\n return true;\n });\n\n // Mouse listeners.\n this.allItems.click(function(e) {\n return currentThis.handleClick($(this), e);\n });\n\n // Key listeners.\n this.allItems.keydown(function(e) {\n return currentThis.handleKeyDown($(this), e);\n });\n\n this.allItems.focus(function() {\n return currentThis.handleFocus($(this));\n });\n\n this.allItems.blur(function() {\n return currentThis.handleBlur($(this));\n });\n };\n\n /**\n * Process click events for the top menus.\n *\n * @method handleClick\n * @param {Object} item is the jquery object of the item firing the event\n * @param {Event} e is the associated event object\n * @return {boolean} Returns false\n */\n Menubar.prototype.handleClick = function(item, e) {\n e.stopPropagation();\n\n var parentUL = item.parent();\n\n if (parentUL.is('.tool-lp-menu')) {\n // Toggle the child menu open/closed.\n if (item.children('ul').first().attr('aria-hidden') == 'true') {\n this.openSubMenu(item.children('ul').first());\n } else {\n item.children('ul').first().attr('aria-hidden', 'true');\n }\n } else {\n // Remove hover and focus styling.\n this.allItems.removeClass('menu-hover menu-focus');\n\n // Clear the active item.\n this.activeItem = null;\n\n // Close the menu.\n this.menuRoot.find('ul').not('.root-level').attr('aria-hidden', 'true');\n // Follow any link, or call the click handlers.\n var anchor = item.find('a').first();\n var clickEvent = new $.Event('click');\n clickEvent.target = anchor;\n var eventHandled = false;\n if (this.handlers) {\n $.each(this.handlers, function(selector, handler) {\n if (eventHandled) {\n return;\n }\n if (item.find(selector).length > 0) {\n var callable = $.proxy(handler, anchor);\n // False means stop propogatting events.\n eventHandled = (callable(clickEvent) === false) || clickEvent.isDefaultPrevented();\n }\n });\n }\n // If we didn't find a handler, and the HREF is # that probably means that\n // we are handling it from somewhere else. Let's just do nothing in that case.\n if (!eventHandled && anchor.attr('href') !== '#') {\n window.location.href = anchor.attr('href');\n }\n }\n return false;\n };\n\n /*\n * Process focus events for the menu.\n *\n * @method handleFocus\n * @param {Object} item is the jquery object of the item firing the event\n * @return boolean Returns false\n */\n Menubar.prototype.handleFocus = function(item) {\n\n // If activeItem is null, we are getting focus from outside the menu. Store\n // the item that triggered the event.\n if (this.activeItem === null) {\n this.activeItem = item;\n } else if (item[0] != this.activeItem[0]) {\n return true;\n }\n\n // Get the set of jquery objects for all the parent items of the active item.\n var parentItems = this.activeItem.parentsUntil('ul.tool-lp-menu').filter('li');\n\n // Remove focus styling from all other menu items.\n this.allItems.removeClass('menu-focus');\n\n // Add focus styling to the active item.\n this.activeItem.addClass('menu-focus');\n\n // Add focus styling to all parent items.\n parentItems.addClass('menu-focus');\n\n // If the bChildOpen flag has been set, open the active item's child menu (if applicable).\n if (this.isChildOpen === true) {\n\n var itemUL = item.parent();\n\n // If the itemUL is a root-level menu and item is a parent item,\n // show the child menu.\n if (itemUL.is('.tool-lp-menu') && (item.attr('aria-haspopup') == 'true')) {\n this.openSubMenu(item.children('ul').first());\n }\n }\n\n return true;\n };\n\n /*\n * Process blur events for the menu.\n *\n * @method handleBlur\n * @param {Object} item is the jquery object of the item firing the event\n * @return boolean Returns false\n */\n Menubar.prototype.handleBlur = function(item) {\n item.removeClass('menu-focus');\n\n return true;\n };\n\n /*\n * Determine if the menu should open to the left, or the right,\n * based on the screen size and menu position.\n * @method setOpenDirection\n */\n Menubar.prototype.setOpenDirection = function() {\n var pos = this.menuRoot.offset();\n var isRTL = $(document.body).hasClass('dir-rtl');\n var openLeft = true;\n var heightmenuRoot = this.rootMenus.outerHeight();\n var widthmenuRoot = this.rootMenus.outerWidth();\n // Sometimes the menuMinWidth is not enough to figure out if menu exceeds the window width.\n // So we have to calculate the real menu width.\n var subMenuContainer = this.rootMenus.find('ul.tool-lp-sub-menu');\n\n // Reset margins.\n subMenuContainer.css('margin-right', '');\n subMenuContainer.css('margin-left', '');\n subMenuContainer.css('margin-top', '');\n\n subMenuContainer.attr('aria-hidden', false);\n var menuRealWidth = subMenuContainer.outerWidth(),\n menuRealHeight = subMenuContainer.outerHeight();\n\n var margintop = null,\n marginright = null,\n marginleft = null;\n var top = pos.top - $(window).scrollTop();\n // Top is the same for RTL and LTR.\n if (top + menuRealHeight > $(window).height()) {\n margintop = menuRealHeight + heightmenuRoot;\n subMenuContainer.css('margin-top', '-' + margintop + 'px');\n }\n\n if (isRTL) {\n if (pos.left - menuRealWidth < 0) {\n marginright = menuRealWidth - widthmenuRoot;\n subMenuContainer.css('margin-right', '-' + marginright + 'px');\n }\n } else {\n if (pos.left + menuRealWidth > $(window).width()) {\n marginleft = menuRealWidth - widthmenuRoot;\n subMenuContainer.css('margin-left', '-' + marginleft + 'px');\n }\n }\n\n if (openLeft) {\n this.menuRoot.addClass('tool-lp-menu-open-left');\n } else {\n this.menuRoot.removeClass('tool-lp-menu-open-left');\n }\n\n };\n\n /*\n * Process keyDown events for the menu.\n *\n * @method handleKeyDown\n * @param {Object} item is the jquery object of the item firing the event\n * @param {Event} e is the associated event object\n * @return boolean Returns false if consuming the event\n */\n Menubar.prototype.handleKeyDown = function(item, e) {\n\n if (e.altKey || e.ctrlKey) {\n // Modifier key pressed: Do not process.\n return true;\n }\n\n switch (e.keyCode) {\n case this.keys.tab: {\n\n // Hide all menu items and update their aria attributes.\n this.menuRoot.find('ul').attr('aria-hidden', 'true');\n\n // Remove focus styling from all menu items.\n this.allItems.removeClass('menu-focus');\n\n this.activeItem = null;\n\n this.isChildOpen = false;\n\n break;\n }\n case this.keys.esc: {\n var itemUL = item.parent();\n\n if (itemUL.is('.tool-lp-menu')) {\n // Hide the child menu and update the aria attributes.\n item.children('ul').first().attr('aria-hidden', 'true');\n } else {\n\n // Move up one level.\n this.activeItem = itemUL.parent();\n\n // Reset the isChildOpen flag.\n this.isChildOpen = false;\n\n // Set focus on the new item.\n this.activeItem.focus();\n\n // Hide the active menu and update the aria attributes.\n itemUL.attr('aria-hidden', 'true');\n }\n\n e.stopPropagation();\n return false;\n }\n case this.keys.enter:\n case this.keys.space: {\n // Trigger click handler.\n return this.handleClick(item, e);\n }\n\n case this.keys.left: {\n\n this.activeItem = this.moveToPrevious(item);\n\n this.activeItem.focus();\n\n e.stopPropagation();\n return false;\n }\n case this.keys.right: {\n\n this.activeItem = this.moveToNext(item);\n\n this.activeItem.focus();\n\n e.stopPropagation();\n return false;\n }\n case this.keys.up: {\n\n this.activeItem = this.moveUp(item);\n\n this.activeItem.focus();\n\n e.stopPropagation();\n return false;\n }\n case this.keys.down: {\n\n this.activeItem = this.moveDown(item);\n\n this.activeItem.focus();\n\n e.stopPropagation();\n return false;\n }\n }\n\n return true;\n\n };\n\n\n /**\n * Move to the next menu level.\n * This will be either the next root-level menu or the child of a menu parent. If\n * at the root level and the active item is the last in the menu, this function will loop\n * to the first menu item.\n *\n * If the menu is a horizontal menu, the first child element of the newly selected menu will\n * be selected\n *\n * @method moveToNext\n * @param {Object} item is the active menu item\n * @return {Object} Returns the item to move to. Returns item is no move is possible\n */\n Menubar.prototype.moveToNext = function(item) {\n // Item's containing menu.\n var itemUL = item.parent();\n\n // The items in the currently active menu.\n var menuItems = itemUL.children('li');\n\n // The number of items in the active menu.\n var menuNum = menuItems.length;\n // The items index in its menu.\n var menuIndex = menuItems.index(item);\n var newItem = null;\n var childMenu = null;\n\n if (itemUL.is('.tool-lp-menu')) {\n // This is the root level move to next sibling. This will require closing\n // the current child menu and opening the new one.\n\n if (menuIndex < menuNum - 1) {\n // Not the last root menu.\n newItem = item.next();\n } else { // Wrap to first item.\n newItem = menuItems.first();\n }\n\n // Close the current child menu (if applicable).\n if (item.attr('aria-haspopup') == 'true') {\n\n childMenu = item.children('ul').first();\n\n if (childMenu.attr('aria-hidden') == 'false') {\n // Update the child menu's aria-hidden attribute.\n childMenu.attr('aria-hidden', 'true');\n this.isChildOpen = true;\n }\n }\n\n // Remove the focus styling from the current menu.\n item.removeClass('menu-focus');\n\n // Open the new child menu (if applicable).\n if ((newItem.attr('aria-haspopup') === 'true') && (this.isChildOpen === true)) {\n\n childMenu = newItem.children('ul').first();\n\n // Update the child's aria-hidden attribute.\n this.openSubMenu(childMenu);\n }\n } else {\n // This is not the root level. If there is a child menu to be moved into, do that;\n // otherwise, move to the next root-level menu if there is one.\n if (item.attr('aria-haspopup') == 'true') {\n\n childMenu = item.children('ul').first();\n\n newItem = childMenu.children('li').first();\n\n // Show the child menu and update its aria attributes.\n this.openSubMenu(childMenu);\n } else {\n // At deepest level, move to the next root-level menu.\n\n var parentMenus = null;\n var rootItem = null;\n\n // Get list of all parent menus for item, up to the root level.\n parentMenus = item.parentsUntil('ul.tool-lp-menu').filter('ul').not('.tool-lp-menu');\n\n // Hide the current menu and update its aria attributes accordingly.\n parentMenus.attr('aria-hidden', 'true');\n\n // Remove the focus styling from the active menu.\n parentMenus.find('li').removeClass('menu-focus');\n parentMenus.last().parent().removeClass('menu-focus');\n\n // The containing root for the menu.\n rootItem = parentMenus.last().parent();\n\n menuIndex = this.rootMenus.index(rootItem);\n\n // If this is not the last root menu item, move to the next one.\n if (menuIndex < this.rootMenus.length - 1) {\n newItem = rootItem.next();\n } else {\n // Loop.\n newItem = this.rootMenus.first();\n }\n\n // Add the focus styling to the new menu.\n newItem.addClass('menu-focus');\n\n if (newItem.attr('aria-haspopup') == 'true') {\n childMenu = newItem.children('ul').first();\n\n newItem = childMenu.children('li').first();\n\n // Show the child menu and update it's aria attributes.\n this.openSubMenu(childMenu);\n this.isChildOpen = true;\n }\n }\n }\n\n return newItem;\n };\n\n /**\n * Member function to move to the previous menu level.\n * This will be either the previous root-level menu or the child of a menu parent. If\n * at the root level and the active item is the first in the menu, this function will loop\n * to the last menu item.\n *\n * If the menu is a horizontal menu, the first child element of the newly selected menu will\n * be selected\n *\n * @method moveToPrevious\n * @param {Object} item is the active menu item\n * @return {Object} Returns the item to move to. Returns item is no move is possible\n */\n Menubar.prototype.moveToPrevious = function(item) {\n // Item's containing menu.\n var itemUL = item.parent();\n // The items in the currently active menu.\n var menuItems = itemUL.children('li');\n // The items index in its menu.\n var menuIndex = menuItems.index(item);\n var newItem = null;\n var childMenu = null;\n\n if (itemUL.is('.tool-lp-menu')) {\n // This is the root level move to previous sibling. This will require closing\n // the current child menu and opening the new one.\n\n if (menuIndex > 0) {\n // Not the first root menu.\n newItem = item.prev();\n } else {\n // Wrap to last item.\n newItem = menuItems.last();\n }\n\n // Close the current child menu (if applicable).\n if (item.attr('aria-haspopup') == 'true') {\n childMenu = item.children('ul').first();\n\n if (childMenu.attr('aria-hidden') == 'false') {\n // Update the child menu's aria-hidden attribute.\n childMenu.attr('aria-hidden', 'true');\n this.isChildOpen = true;\n }\n }\n\n // Remove the focus styling from the current menu.\n item.removeClass('menu-focus');\n\n // Open the new child menu (if applicable).\n if ((newItem.attr('aria-haspopup') === 'true') && (this.isChildOpen === true)) {\n\n childMenu = newItem.children('ul').first();\n\n // Update the child's aria-hidden attribute.\n this.openSubMenu(childMenu);\n\n }\n } else {\n // This is not the root level. If there is a parent menu that is not the\n // root menu, move up one level; otherwise, move to first item of the previous\n // root menu.\n\n var parentLI = itemUL.parent();\n var parentUL = parentLI.parent();\n\n // If this is a vertical menu or is not the first child menu\n // of the root-level menu, move up one level.\n if (!parentUL.is('.tool-lp-menu')) {\n\n newItem = itemUL.parent();\n\n // Hide the active menu and update aria-hidden.\n itemUL.attr('aria-hidden', 'true');\n\n // Remove the focus highlight from the item.\n item.removeClass('menu-focus');\n\n } else {\n // Move to previous root-level menu.\n\n // Hide the current menu and update the aria attributes accordingly.\n itemUL.attr('aria-hidden', 'true');\n\n // Remove the focus styling from the active menu.\n item.removeClass('menu-focus');\n parentLI.removeClass('menu-focus');\n\n menuIndex = this.rootMenus.index(parentLI);\n\n if (menuIndex > 0) {\n // Move to the previous root-level menu.\n newItem = parentLI.prev();\n } else {\n // Loop to last root-level menu.\n newItem = this.rootMenus.last();\n }\n\n // Add the focus styling to the new menu.\n newItem.addClass('menu-focus');\n\n if (newItem.attr('aria-haspopup') == 'true') {\n childMenu = newItem.children('ul').first();\n\n // Show the child menu and update it's aria attributes.\n this.openSubMenu(childMenu);\n this.isChildOpen = true;\n\n newItem = childMenu.children('li').first();\n }\n }\n }\n\n return newItem;\n };\n\n /**\n * Member function to select the next item in a menu.\n * If the active item is the last in the menu, this function will loop to the\n * first menu item.\n *\n * @method moveDown\n * @param {Object} item is the active menu item\n * @param {String} startChr is the character to attempt to match against the beginning of the\n * menu item titles. If found, focus moves to the next menu item beginning with that character.\n * @return {Object} Returns the item to move to. Returns item is no move is possible\n */\n Menubar.prototype.moveDown = function(item, startChr) {\n // Item's containing menu.\n var itemUL = item.parent();\n // The items in the currently active menu.\n var menuItems = itemUL.children('li').not('.separator');\n // The number of items in the active menu.\n var menuNum = menuItems.length;\n // The items index in its menu.\n var menuIndex = menuItems.index(item);\n var newItem = null;\n var newItemUL = null;\n\n if (itemUL.is('.tool-lp-menu')) {\n // This is the root level menu.\n\n if (item.attr('aria-haspopup') != 'true') {\n // No child menu to move to.\n return item;\n }\n\n // Move to the first item in the child menu.\n newItemUL = item.children('ul').first();\n newItem = newItemUL.children('li').first();\n\n // Make sure the child menu is visible.\n this.openSubMenu(newItemUL);\n\n return newItem;\n }\n\n // If $item is not the last item in its menu, move to the next item. If startChr is specified, move\n // to the next item with a title that begins with that character.\n if (startChr) {\n var match = false;\n var curNdx = menuIndex + 1;\n\n // Check if the active item was the last one on the list.\n if (curNdx == menuNum) {\n curNdx = 0;\n }\n\n // Iterate through the menu items (starting from the current item and wrapping) until a match is found\n // or the loop returns to the current menu item.\n while (curNdx != menuIndex) {\n\n var titleChr = menuItems.eq(curNdx).html().charAt(0);\n\n if (titleChr.toLowerCase() == startChr) {\n match = true;\n break;\n }\n\n curNdx = curNdx + 1;\n\n if (curNdx == menuNum) {\n // Reached the end of the list, start again at the beginning.\n curNdx = 0;\n }\n }\n\n if (match === true) {\n newItem = menuItems.eq(curNdx);\n\n // Remove the focus styling from the current item.\n item.removeClass('menu-focus');\n\n return newItem;\n } else {\n return item;\n }\n } else {\n if (menuIndex < menuNum - 1) {\n newItem = menuItems.eq(menuIndex + 1);\n } else {\n newItem = menuItems.first();\n }\n }\n\n // Remove the focus styling from the current item.\n item.removeClass('menu-focus');\n\n return newItem;\n };\n\n /**\n * Function moveUp() is a member function to select the previous item in a menu.\n * If the active item is the first in the menu, this function will loop to the\n * last menu item.\n *\n * @method moveUp\n * @param {Object} item is the active menu item\n * @return {Object} Returns the item to move to. Returns item is no move is possible\n */\n Menubar.prototype.moveUp = function(item) {\n // Item's containing menu.\n var itemUL = item.parent();\n // The items in the currently active menu.\n var menuItems = itemUL.children('li').not('.separator');\n // The items index in its menu.\n var menuIndex = menuItems.index(item);\n var newItem = null;\n\n if (itemUL.is('.tool-lp-menu')) {\n // This is the root level menu.\n // Nothing to do.\n return item;\n }\n\n // If item is not the first item in its menu, move to the previous item.\n if (menuIndex > 0) {\n newItem = menuItems.eq(menuIndex - 1);\n } else {\n // Loop to top of menu.\n newItem = menuItems.last();\n }\n\n // Remove the focus styling from the current item.\n item.removeClass('menu-focus');\n\n return newItem;\n };\n\n /**\n * Enhance the dom with aria attributes.\n * @method addAriaAttributes\n */\n Menubar.prototype.addAriaAttributes = function() {\n this.menuRoot.attr('role', 'menubar');\n this.rootMenus.attr('role', 'menuitem');\n this.rootMenus.attr('tabindex', '0');\n this.rootMenus.attr('aria-haspopup', 'true');\n this.subMenus.attr('role', 'menu');\n this.subMenus.attr('aria-hidden', 'true');\n this.subMenuItems.attr('role', 'menuitem');\n this.subMenuItems.attr('tabindex', '-1');\n\n // For CSS styling and effects.\n this.menuRoot.addClass('tool-lp-menu');\n this.allItems.addClass('tool-lp-menu-item');\n this.rootMenus.addClass('tool-lp-root-menu');\n this.subMenus.addClass('tool-lp-sub-menu');\n this.subMenuItems.addClass('dropdown-item');\n };\n\n return /** @alias module:tool_lp/menubar */ {\n /**\n * Create a menu bar object for every node matching the selector.\n *\n * The expected DOM structure is shown below.\n *
<- This is the target of the selector parameter.\n *
<- This is repeated for each top level menu.\n * Text <- This is the text for the top level menu.\n *
<- This is a list of the entries in this top level menu.\n *
<- This is repeated for each menu entry.\n * Choice 1 <- The anchor for the menu.\n *
\n *
\n *
\n *
\n *\n * @method enhance\n * @param {String} selector - The selector for the outer most menu node.\n * @param {Function} handler - Javascript handler for when a menu item was chosen. If the\n * handler returns true (or does not exist), the\n * menu will look for an anchor with a link to follow.\n * For example, if the menu entry has a \"data-action\" attribute\n * and we want to call a javascript function when that entry is chosen,\n * we could pass a list of handlers like this:\n * { \"[data-action='add']\" : callAddFunction }\n */\n enhance: function(selector, handler) {\n $(selector).each(function(index, element) {\n var menuRoot = $(element);\n // Don't enhance the same menu twice.\n if (menuRoot.data(\"menubarEnhanced\") !== true) {\n (new Menubar(menuRoot, handler));\n menuRoot.data(\"menubarEnhanced\", true);\n }\n });\n },\n\n /**\n * Handy function to close all open menus anywhere on the page.\n * @method closeAll\n */\n closeAll: closeAllSubMenus\n };\n});\n"],"names":["define","$","documentClickHandlerRegistered","menuActive","closeAllSubMenus","attr","Menubar","menuRoot","handlers","rootMenus","this","children","subMenus","subMenuItems","allItems","add","activeItem","isChildOpen","keys","tab","enter","esc","space","left","up","right","down","addAriaAttributes","addEventListeners","prototype","openSubMenu","menu","setOpenDirection","currentThis","document","click","mouseenter","addClass","mouseout","removeClass","e","handleClick","keydown","handleKeyDown","focus","handleFocus","blur","handleBlur","item","stopPropagation","parent","is","first","find","not","anchor","clickEvent","Event","target","eventHandled","each","selector","handler","length","callable","proxy","isDefaultPrevented","window","location","href","parentItems","parentsUntil","filter","pos","offset","isRTL","body","hasClass","heightmenuRoot","outerHeight","widthmenuRoot","outerWidth","subMenuContainer","css","menuRealWidth","menuRealHeight","margintop","marginright","marginleft","top","scrollTop","height","width","altKey","ctrlKey","keyCode","itemUL","moveToPrevious","moveToNext","moveUp","moveDown","menuItems","menuNum","menuIndex","index","newItem","childMenu","next","parentMenus","rootItem","last","prev","parentLI","startChr","newItemUL","match","curNdx","eq","html","charAt","toLowerCase","enhance","element","data","closeAll"],"mappings":"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"}
\ No newline at end of file
+{"version":3,"file":"menubar.min.js","sources":["../src/menubar.js"],"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 * Aria menubar functionality. Enhances a simple nested list structure into a full aria widget.\n * Based on the open ajax example: http://oaa-accessibility.org/example/26/\n *\n * @module tool_lp/menubar\n * @copyright 2015 Damyon Wiese \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(['jquery'], function($) {\n\n /** @property {boolean} Flag to indicate if we have already registered a click event handler for the document. */\n var documentClickHandlerRegistered = false;\n\n /** @property {boolean} Flag to indicate whether there's an active, open menu. */\n var menuActive = false;\n\n /**\n * Close all open submenus anywhere in the page (there should only ever be one open at a time).\n *\n * @method closeAllSubMenus\n */\n var closeAllSubMenus = function() {\n $('.tool-lp-menu .tool-lp-sub-menu').attr('aria-hidden', 'true');\n // Every menu's closed at this point, so set the menu active flag to false.\n menuActive = false;\n };\n\n /**\n * Constructor\n *\n * @param {jQuery} menuRoot Jquery collection matching the root of the menu.\n * @param {Function[]} handlers called when a menu item is chosen.\n */\n var Menubar = function(menuRoot, handlers) {\n // Setup private class variables.\n this.menuRoot = menuRoot;\n this.handlers = handlers;\n this.rootMenus = this.menuRoot.children('li');\n this.subMenus = this.rootMenus.children('ul');\n this.subMenuItems = this.subMenus.children('li');\n this.allItems = this.rootMenus.add(this.subMenuItems);\n this.activeItem = null;\n this.isChildOpen = false;\n\n this.keys = {\n tab: 9,\n enter: 13,\n esc: 27,\n space: 32,\n left: 37,\n up: 38,\n right: 39,\n down: 40\n };\n\n this.addAriaAttributes();\n // Add the event listeners.\n this.addEventListeners();\n };\n\n /**\n * Open a submenu, first it closes all other sub-menus and sets the open direction.\n * @method openSubMenu\n * @param {Node} menu\n */\n Menubar.prototype.openSubMenu = function(menu) {\n this.setOpenDirection();\n closeAllSubMenus();\n menu.attr('aria-hidden', 'false');\n // Set menu active flag to true when a menu is opened.\n menuActive = true;\n };\n\n\n /**\n * Bind the event listeners to the DOM\n * @method addEventListeners\n */\n Menubar.prototype.addEventListeners = function() {\n var currentThis = this;\n\n // When clicking outside the menubar.\n if (documentClickHandlerRegistered === false) {\n $(document).click(function() {\n // Check if a menu is opened.\n if (menuActive) {\n // Close menu.\n closeAllSubMenus();\n }\n });\n // Set this flag to true so that we won't need to add a document click handler for the other Menubar instances.\n documentClickHandlerRegistered = true;\n }\n\n // Hovers.\n this.subMenuItems.mouseenter(function() {\n $(this).addClass('menu-hover');\n return true;\n });\n\n this.subMenuItems.mouseout(function() {\n $(this).removeClass('menu-hover');\n return true;\n });\n\n // Mouse listeners.\n this.allItems.click(function(e) {\n return currentThis.handleClick($(this), e);\n });\n\n // Key listeners.\n this.allItems.keydown(function(e) {\n return currentThis.handleKeyDown($(this), e);\n });\n\n this.allItems.focus(function() {\n return currentThis.handleFocus($(this));\n });\n\n this.allItems.blur(function() {\n return currentThis.handleBlur($(this));\n });\n };\n\n /**\n * Process click events for the top menus.\n *\n * @method handleClick\n * @param {Object} item is the jquery object of the item firing the event\n * @param {Event} e is the associated event object\n * @return {boolean} Returns false\n */\n Menubar.prototype.handleClick = function(item, e) {\n e.stopPropagation();\n\n var parentUL = item.parent();\n\n if (parentUL.is('.tool-lp-menu')) {\n // Toggle the child menu open/closed.\n if (item.children('ul').first().attr('aria-hidden') == 'true') {\n this.openSubMenu(item.children('ul').first());\n } else {\n item.children('ul').first().attr('aria-hidden', 'true');\n }\n } else {\n // Remove hover and focus styling.\n this.allItems.removeClass('menu-hover menu-focus');\n\n // Clear the active item.\n this.activeItem = null;\n\n // Close the menu.\n this.menuRoot.find('ul').not('.root-level').attr('aria-hidden', 'true');\n // Follow any link, or call the click handlers.\n var anchor = item.find('a').first();\n var clickEvent = new $.Event('click');\n clickEvent.target = anchor;\n var eventHandled = false;\n if (this.handlers) {\n $.each(this.handlers, function(selector, handler) {\n if (eventHandled) {\n return;\n }\n if (item.find(selector).length > 0) {\n var callable = $.proxy(handler, anchor);\n // False means stop propogatting events.\n eventHandled = (callable(clickEvent) === false) || clickEvent.isDefaultPrevented();\n }\n });\n }\n // If we didn't find a handler, and the HREF is # that probably means that\n // we are handling it from somewhere else. Let's just do nothing in that case.\n if (!eventHandled && anchor.attr('href') !== '#') {\n window.location.href = anchor.attr('href');\n }\n }\n return false;\n };\n\n /*\n * Process focus events for the menu.\n *\n * @method handleFocus\n * @param {Object} item is the jquery object of the item firing the event\n * @return boolean Returns false\n */\n Menubar.prototype.handleFocus = function(item) {\n\n // If activeItem is null, we are getting focus from outside the menu. Store\n // the item that triggered the event.\n if (this.activeItem === null) {\n this.activeItem = item;\n } else if (item[0] != this.activeItem[0]) {\n return true;\n }\n\n // Get the set of jquery objects for all the parent items of the active item.\n var parentItems = this.activeItem.parentsUntil('ul.tool-lp-menu').filter('li');\n\n // Remove focus styling from all other menu items.\n this.allItems.removeClass('menu-focus');\n\n // Add focus styling to the active item.\n this.activeItem.addClass('menu-focus');\n\n // Add focus styling to all parent items.\n parentItems.addClass('menu-focus');\n\n // If the bChildOpen flag has been set, open the active item's child menu (if applicable).\n if (this.isChildOpen === true) {\n\n var itemUL = item.parent();\n\n // If the itemUL is a root-level menu and item is a parent item,\n // show the child menu.\n if (itemUL.is('.tool-lp-menu') && (item.attr('aria-haspopup') == 'true')) {\n this.openSubMenu(item.children('ul').first());\n }\n }\n\n return true;\n };\n\n /*\n * Process blur events for the menu.\n *\n * @method handleBlur\n * @param {Object} item is the jquery object of the item firing the event\n * @return boolean Returns false\n */\n Menubar.prototype.handleBlur = function(item) {\n item.removeClass('menu-focus');\n\n return true;\n };\n\n /*\n * Determine if the menu should open to the left, or the right,\n * based on the screen size and menu position.\n * @method setOpenDirection\n */\n Menubar.prototype.setOpenDirection = function() {\n var pos = this.menuRoot.offset();\n var isRTL = $(document.body).hasClass('dir-rtl');\n var openLeft = true;\n var heightmenuRoot = this.rootMenus.outerHeight();\n var widthmenuRoot = this.rootMenus.outerWidth();\n // Sometimes the menuMinWidth is not enough to figure out if menu exceeds the window width.\n // So we have to calculate the real menu width.\n var subMenuContainer = this.rootMenus.find('ul.tool-lp-sub-menu');\n\n // Reset margins.\n subMenuContainer.css('margin-right', '');\n subMenuContainer.css('margin-left', '');\n subMenuContainer.css('margin-top', '');\n\n subMenuContainer.attr('aria-hidden', false);\n var menuRealWidth = subMenuContainer.outerWidth(),\n menuRealHeight = subMenuContainer.outerHeight();\n\n var margintop = null,\n marginright = null,\n marginleft = null;\n var top = pos.top - $(window).scrollTop();\n // Top is the same for RTL and LTR.\n if (top + menuRealHeight > $(window).height()) {\n margintop = menuRealHeight + heightmenuRoot;\n subMenuContainer.css('margin-top', '-' + margintop + 'px');\n }\n\n if (isRTL) {\n if (pos.left - menuRealWidth < 0) {\n marginright = menuRealWidth - widthmenuRoot;\n subMenuContainer.css('margin-right', '-' + marginright + 'px');\n }\n } else {\n if (pos.left + menuRealWidth > $(window).width()) {\n marginleft = menuRealWidth - widthmenuRoot;\n subMenuContainer.css('margin-left', '-' + marginleft + 'px');\n }\n }\n\n if (openLeft) {\n this.menuRoot.addClass('tool-lp-menu-open-left');\n } else {\n this.menuRoot.removeClass('tool-lp-menu-open-left');\n }\n\n };\n\n /*\n * Process keyDown events for the menu.\n *\n * @method handleKeyDown\n * @param {Object} item is the jquery object of the item firing the event\n * @param {Event} e is the associated event object\n * @return boolean Returns false if consuming the event\n */\n Menubar.prototype.handleKeyDown = function(item, e) {\n\n if (e.altKey || e.ctrlKey) {\n // Modifier key pressed: Do not process.\n return true;\n }\n\n switch (e.keyCode) {\n case this.keys.tab: {\n\n // Hide all menu items and update their aria attributes.\n this.menuRoot.find('ul').attr('aria-hidden', 'true');\n\n // Remove focus styling from all menu items.\n this.allItems.removeClass('menu-focus');\n\n this.activeItem = null;\n\n this.isChildOpen = false;\n\n break;\n }\n case this.keys.esc: {\n var itemUL = item.parent();\n\n if (itemUL.is('.tool-lp-menu')) {\n // Hide the child menu and update the aria attributes.\n item.children('ul').first().attr('aria-hidden', 'true');\n } else {\n\n // Move up one level.\n this.activeItem = itemUL.parent();\n\n // Reset the isChildOpen flag.\n this.isChildOpen = false;\n\n // Set focus on the new item.\n this.activeItem.focus();\n\n // Hide the active menu and update the aria attributes.\n itemUL.attr('aria-hidden', 'true');\n }\n\n e.stopPropagation();\n return false;\n }\n case this.keys.enter:\n case this.keys.space: {\n // Trigger click handler.\n return this.handleClick(item, e);\n }\n\n case this.keys.left: {\n\n this.activeItem = this.moveToPrevious(item);\n\n this.activeItem.focus();\n\n e.stopPropagation();\n return false;\n }\n case this.keys.right: {\n\n this.activeItem = this.moveToNext(item);\n\n this.activeItem.focus();\n\n e.stopPropagation();\n return false;\n }\n case this.keys.up: {\n\n this.activeItem = this.moveUp(item);\n\n this.activeItem.focus();\n\n e.stopPropagation();\n return false;\n }\n case this.keys.down: {\n\n this.activeItem = this.moveDown(item);\n\n this.activeItem.focus();\n\n e.stopPropagation();\n return false;\n }\n }\n\n return true;\n\n };\n\n\n /**\n * Move to the next menu level.\n * This will be either the next root-level menu or the child of a menu parent. If\n * at the root level and the active item is the last in the menu, this function will loop\n * to the first menu item.\n *\n * If the menu is a horizontal menu, the first child element of the newly selected menu will\n * be selected\n *\n * @method moveToNext\n * @param {Object} item is the active menu item\n * @return {Object} Returns the item to move to. Returns item is no move is possible\n */\n Menubar.prototype.moveToNext = function(item) {\n // Item's containing menu.\n var itemUL = item.parent();\n\n // The items in the currently active menu.\n var menuItems = itemUL.children('li');\n\n // The number of items in the active menu.\n var menuNum = menuItems.length;\n // The items index in its menu.\n var menuIndex = menuItems.index(item);\n var newItem = null;\n var childMenu = null;\n\n if (itemUL.is('.tool-lp-menu')) {\n // This is the root level move to next sibling. This will require closing\n // the current child menu and opening the new one.\n\n if (menuIndex < menuNum - 1) {\n // Not the last root menu.\n newItem = item.next();\n } else { // Wrap to first item.\n newItem = menuItems.first();\n }\n\n // Close the current child menu (if applicable).\n if (item.attr('aria-haspopup') == 'true') {\n\n childMenu = item.children('ul').first();\n\n if (childMenu.attr('aria-hidden') == 'false') {\n // Update the child menu's aria-hidden attribute.\n childMenu.attr('aria-hidden', 'true');\n this.isChildOpen = true;\n }\n }\n\n // Remove the focus styling from the current menu.\n item.removeClass('menu-focus');\n\n // Open the new child menu (if applicable).\n if ((newItem.attr('aria-haspopup') === 'true') && (this.isChildOpen === true)) {\n\n childMenu = newItem.children('ul').first();\n\n // Update the child's aria-hidden attribute.\n this.openSubMenu(childMenu);\n }\n } else {\n // This is not the root level. If there is a child menu to be moved into, do that;\n // otherwise, move to the next root-level menu if there is one.\n if (item.attr('aria-haspopup') == 'true') {\n\n childMenu = item.children('ul').first();\n\n newItem = childMenu.children('li').first();\n\n // Show the child menu and update its aria attributes.\n this.openSubMenu(childMenu);\n } else {\n // At deepest level, move to the next root-level menu.\n\n var parentMenus = null;\n var rootItem = null;\n\n // Get list of all parent menus for item, up to the root level.\n parentMenus = item.parentsUntil('ul.tool-lp-menu').filter('ul').not('.tool-lp-menu');\n\n // Hide the current menu and update its aria attributes accordingly.\n parentMenus.attr('aria-hidden', 'true');\n\n // Remove the focus styling from the active menu.\n parentMenus.find('li').removeClass('menu-focus');\n parentMenus.last().parent().removeClass('menu-focus');\n\n // The containing root for the menu.\n rootItem = parentMenus.last().parent();\n\n menuIndex = this.rootMenus.index(rootItem);\n\n // If this is not the last root menu item, move to the next one.\n if (menuIndex < this.rootMenus.length - 1) {\n newItem = rootItem.next();\n } else {\n // Loop.\n newItem = this.rootMenus.first();\n }\n\n // Add the focus styling to the new menu.\n newItem.addClass('menu-focus');\n\n if (newItem.attr('aria-haspopup') == 'true') {\n childMenu = newItem.children('ul').first();\n\n newItem = childMenu.children('li').first();\n\n // Show the child menu and update it's aria attributes.\n this.openSubMenu(childMenu);\n this.isChildOpen = true;\n }\n }\n }\n\n return newItem;\n };\n\n /**\n * Member function to move to the previous menu level.\n * This will be either the previous root-level menu or the child of a menu parent. If\n * at the root level and the active item is the first in the menu, this function will loop\n * to the last menu item.\n *\n * If the menu is a horizontal menu, the first child element of the newly selected menu will\n * be selected\n *\n * @method moveToPrevious\n * @param {Object} item is the active menu item\n * @return {Object} Returns the item to move to. Returns item is no move is possible\n */\n Menubar.prototype.moveToPrevious = function(item) {\n // Item's containing menu.\n var itemUL = item.parent();\n // The items in the currently active menu.\n var menuItems = itemUL.children('li');\n // The items index in its menu.\n var menuIndex = menuItems.index(item);\n var newItem = null;\n var childMenu = null;\n\n if (itemUL.is('.tool-lp-menu')) {\n // This is the root level move to previous sibling. This will require closing\n // the current child menu and opening the new one.\n\n if (menuIndex > 0) {\n // Not the first root menu.\n newItem = item.prev();\n } else {\n // Wrap to last item.\n newItem = menuItems.last();\n }\n\n // Close the current child menu (if applicable).\n if (item.attr('aria-haspopup') == 'true') {\n childMenu = item.children('ul').first();\n\n if (childMenu.attr('aria-hidden') == 'false') {\n // Update the child menu's aria-hidden attribute.\n childMenu.attr('aria-hidden', 'true');\n this.isChildOpen = true;\n }\n }\n\n // Remove the focus styling from the current menu.\n item.removeClass('menu-focus');\n\n // Open the new child menu (if applicable).\n if ((newItem.attr('aria-haspopup') === 'true') && (this.isChildOpen === true)) {\n\n childMenu = newItem.children('ul').first();\n\n // Update the child's aria-hidden attribute.\n this.openSubMenu(childMenu);\n\n }\n } else {\n // This is not the root level. If there is a parent menu that is not the\n // root menu, move up one level; otherwise, move to first item of the previous\n // root menu.\n\n var parentLI = itemUL.parent();\n var parentUL = parentLI.parent();\n\n // If this is a vertical menu or is not the first child menu\n // of the root-level menu, move up one level.\n if (!parentUL.is('.tool-lp-menu')) {\n\n newItem = itemUL.parent();\n\n // Hide the active menu and update aria-hidden.\n itemUL.attr('aria-hidden', 'true');\n\n // Remove the focus highlight from the item.\n item.removeClass('menu-focus');\n\n } else {\n // Move to previous root-level menu.\n\n // Hide the current menu and update the aria attributes accordingly.\n itemUL.attr('aria-hidden', 'true');\n\n // Remove the focus styling from the active menu.\n item.removeClass('menu-focus');\n parentLI.removeClass('menu-focus');\n\n menuIndex = this.rootMenus.index(parentLI);\n\n if (menuIndex > 0) {\n // Move to the previous root-level menu.\n newItem = parentLI.prev();\n } else {\n // Loop to last root-level menu.\n newItem = this.rootMenus.last();\n }\n\n // Add the focus styling to the new menu.\n newItem.addClass('menu-focus');\n\n if (newItem.attr('aria-haspopup') == 'true') {\n childMenu = newItem.children('ul').first();\n\n // Show the child menu and update it's aria attributes.\n this.openSubMenu(childMenu);\n this.isChildOpen = true;\n\n newItem = childMenu.children('li').first();\n }\n }\n }\n\n return newItem;\n };\n\n /**\n * Member function to select the next item in a menu.\n * If the active item is the last in the menu, this function will loop to the\n * first menu item.\n *\n * @method moveDown\n * @param {Object} item is the active menu item\n * @param {String} startChr is the character to attempt to match against the beginning of the\n * menu item titles. If found, focus moves to the next menu item beginning with that character.\n * @return {Object} Returns the item to move to. Returns item is no move is possible\n */\n Menubar.prototype.moveDown = function(item, startChr) {\n // Item's containing menu.\n var itemUL = item.parent();\n // The items in the currently active menu.\n var menuItems = itemUL.children('li').not('.separator');\n // The number of items in the active menu.\n var menuNum = menuItems.length;\n // The items index in its menu.\n var menuIndex = menuItems.index(item);\n var newItem = null;\n var newItemUL = null;\n\n if (itemUL.is('.tool-lp-menu')) {\n // This is the root level menu.\n\n if (item.attr('aria-haspopup') != 'true') {\n // No child menu to move to.\n return item;\n }\n\n // Move to the first item in the child menu.\n newItemUL = item.children('ul').first();\n newItem = newItemUL.children('li').first();\n\n // Make sure the child menu is visible.\n this.openSubMenu(newItemUL);\n\n return newItem;\n }\n\n // If $item is not the last item in its menu, move to the next item. If startChr is specified, move\n // to the next item with a title that begins with that character.\n if (startChr) {\n var match = false;\n var curNdx = menuIndex + 1;\n\n // Check if the active item was the last one on the list.\n if (curNdx == menuNum) {\n curNdx = 0;\n }\n\n // Iterate through the menu items (starting from the current item and wrapping) until a match is found\n // or the loop returns to the current menu item.\n while (curNdx != menuIndex) {\n\n var titleChr = menuItems.eq(curNdx).html().charAt(0);\n\n if (titleChr.toLowerCase() == startChr) {\n match = true;\n break;\n }\n\n curNdx = curNdx + 1;\n\n if (curNdx == menuNum) {\n // Reached the end of the list, start again at the beginning.\n curNdx = 0;\n }\n }\n\n if (match === true) {\n newItem = menuItems.eq(curNdx);\n\n // Remove the focus styling from the current item.\n item.removeClass('menu-focus');\n\n return newItem;\n } else {\n return item;\n }\n } else {\n if (menuIndex < menuNum - 1) {\n newItem = menuItems.eq(menuIndex + 1);\n } else {\n newItem = menuItems.first();\n }\n }\n\n // Remove the focus styling from the current item.\n item.removeClass('menu-focus');\n\n return newItem;\n };\n\n /**\n * Function moveUp() is a member function to select the previous item in a menu.\n * If the active item is the first in the menu, this function will loop to the\n * last menu item.\n *\n * @method moveUp\n * @param {Object} item is the active menu item\n * @return {Object} Returns the item to move to. Returns item is no move is possible\n */\n Menubar.prototype.moveUp = function(item) {\n // Item's containing menu.\n var itemUL = item.parent();\n // The items in the currently active menu.\n var menuItems = itemUL.children('li').not('.separator');\n // The items index in its menu.\n var menuIndex = menuItems.index(item);\n var newItem = null;\n\n if (itemUL.is('.tool-lp-menu')) {\n // This is the root level menu.\n // Nothing to do.\n return item;\n }\n\n // If item is not the first item in its menu, move to the previous item.\n if (menuIndex > 0) {\n newItem = menuItems.eq(menuIndex - 1);\n } else {\n // Loop to top of menu.\n newItem = menuItems.last();\n }\n\n // Remove the focus styling from the current item.\n item.removeClass('menu-focus');\n\n return newItem;\n };\n\n /**\n * Enhance the dom with aria attributes.\n * @method addAriaAttributes\n */\n Menubar.prototype.addAriaAttributes = function() {\n this.menuRoot.attr('role', 'menubar');\n this.rootMenus.attr('role', 'menuitem');\n this.rootMenus.attr('tabindex', '0');\n this.rootMenus.attr('aria-haspopup', 'true');\n this.subMenus.attr('role', 'menu');\n this.subMenus.attr('aria-hidden', 'true');\n this.subMenuItems.attr('role', 'menuitem');\n this.subMenuItems.attr('tabindex', '-1');\n\n // For CSS styling and effects.\n this.menuRoot.addClass('tool-lp-menu');\n this.allItems.addClass('tool-lp-menu-item');\n this.rootMenus.addClass('tool-lp-root-menu');\n this.subMenus.addClass('tool-lp-sub-menu');\n this.subMenuItems.addClass('dropdown-item');\n };\n\n return /** @alias module:tool_lp/menubar */ {\n /**\n * Create a menu bar object for every node matching the selector.\n *\n * The expected DOM structure is shown below.\n *
<- This is the target of the selector parameter.\n *
<- This is repeated for each top level menu.\n * Text <- This is the text for the top level menu.\n *
<- This is a list of the entries in this top level menu.\n *
<- This is repeated for each menu entry.\n * Choice 1 <- The anchor for the menu.\n *
\n *
\n *
\n *
\n *\n * @method enhance\n * @param {String} selector - The selector for the outer most menu node.\n * @param {Function} handler - Javascript handler for when a menu item was chosen. If the\n * handler returns true (or does not exist), the\n * menu will look for an anchor with a link to follow.\n * For example, if the menu entry has a \"data-action\" attribute\n * and we want to call a javascript function when that entry is chosen,\n * we could pass a list of handlers like this:\n * { \"[data-action='add']\" : callAddFunction }\n */\n enhance: function(selector, handler) {\n $(selector).each(function(index, element) {\n var menuRoot = $(element);\n // Don't enhance the same menu twice.\n if (menuRoot.data(\"menubarEnhanced\") !== true) {\n (new Menubar(menuRoot, handler));\n menuRoot.data(\"menubarEnhanced\", true);\n }\n });\n },\n\n /**\n * Handy function to close all open menus anywhere on the page.\n * @method closeAll\n */\n closeAll: closeAllSubMenus\n };\n});\n"],"names":["define","$","documentClickHandlerRegistered","menuActive","closeAllSubMenus","attr","Menubar","menuRoot","handlers","this","rootMenus","children","subMenus","subMenuItems","allItems","add","activeItem","isChildOpen","keys","tab","enter","esc","space","left","up","right","down","addAriaAttributes","addEventListeners","prototype","openSubMenu","menu","setOpenDirection","currentThis","document","click","mouseenter","addClass","mouseout","removeClass","e","handleClick","keydown","handleKeyDown","focus","handleFocus","blur","handleBlur","item","stopPropagation","parent","is","first","find","not","anchor","clickEvent","Event","target","eventHandled","each","selector","handler","length","callable","proxy","isDefaultPrevented","window","location","href","parentItems","parentsUntil","filter","pos","offset","isRTL","body","hasClass","heightmenuRoot","outerHeight","widthmenuRoot","outerWidth","subMenuContainer","css","menuRealWidth","menuRealHeight","margintop","marginright","marginleft","top","scrollTop","height","width","altKey","ctrlKey","keyCode","itemUL","moveToPrevious","moveToNext","moveUp","moveDown","menuItems","menuNum","menuIndex","index","newItem","childMenu","next","parentMenus","rootItem","last","prev","parentLI","startChr","newItemUL","match","curNdx","eq","html","charAt","toLowerCase","enhance","element","data","closeAll"],"mappings":"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"}
\ No newline at end of file
diff --git a/admin/tool/lp/amd/build/module_navigation.min.js.map b/admin/tool/lp/amd/build/module_navigation.min.js.map
index adddb1a552d..a07297f8603 100644
--- a/admin/tool/lp/amd/build/module_navigation.min.js.map
+++ b/admin/tool/lp/amd/build/module_navigation.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"module_navigation.min.js","sources":["../src/module_navigation.js"],"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 * Module to navigation between users in a course.\n *\n * @module tool_lp/module_navigation\n * @copyright 2019 Damyon Wiese\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\ndefine(['jquery'], function($) {\n\n /**\n * ModuleNavigation\n *\n * @class tool_lp/module_navigation\n * @param {String} moduleSelector The selector of the module element.\n * @param {String} baseUrl The base url for the page (no params).\n * @param {Number} courseId The course id\n * @param {Number} moduleId The activity module (filter)\n */\n var ModuleNavigation = function(moduleSelector, baseUrl, courseId, moduleId) {\n this._baseUrl = baseUrl;\n this._moduleId = moduleId;\n this._courseId = courseId;\n\n $(moduleSelector).on('change', this._moduleChanged.bind(this));\n };\n\n /**\n * The module was changed in the select list.\n *\n * @method _moduleChanged\n * @param {Event} e the event\n */\n ModuleNavigation.prototype._moduleChanged = function(e) {\n var newModuleId = $(e.target).val();\n var queryStr = '?mod=' + newModuleId + '&courseid=' + this._courseId;\n document.location = this._baseUrl + queryStr;\n };\n\n /** @property {Number} The id of the course. */\n ModuleNavigation.prototype._courseId = null;\n /** @property {Number} The id of the module. */\n ModuleNavigation.prototype._moduleId = null;\n /** @property {String} Plugin base url. */\n ModuleNavigation.prototype._baseUrl = null;\n\n return ModuleNavigation;\n});\n"],"names":["define","$","ModuleNavigation","moduleSelector","baseUrl","courseId","moduleId","_baseUrl","_moduleId","_courseId","on","this","_moduleChanged","bind","prototype","e","queryStr","target","val","document","location"],"mappings":";;;;;;;AAuBAA,mCAAO,CAAC,WAAW,SAASC,OAWpBC,iBAAmB,SAASC,eAAgBC,QAASC,SAAUC,eAC1DC,SAAWH,aACXI,UAAYF,cACZG,UAAYJ,SAEjBJ,EAAEE,gBAAgBO,GAAG,SAAUC,KAAKC,eAAeC,KAAKF,eAS5DT,iBAAiBY,UAAUF,eAAiB,SAASG,OAE7CC,SAAW,QADGf,EAAEc,EAAEE,QAAQC,MACS,aAAeP,KAAKF,UAC3DU,SAASC,SAAWT,KAAKJ,SAAWS,UAIxCd,iBAAiBY,UAAUL,UAAY,KAEvCP,iBAAiBY,UAAUN,UAAY,KAEvCN,iBAAiBY,UAAUP,SAAW,KAE/BL"}
\ No newline at end of file
+{"version":3,"file":"module_navigation.min.js","sources":["../src/module_navigation.js"],"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 * Module to navigation between users in a course.\n *\n * @module tool_lp/module_navigation\n * @copyright 2019 Damyon Wiese\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\ndefine(['jquery'], function($) {\n\n /**\n * ModuleNavigation\n *\n * @class tool_lp/module_navigation\n * @param {String} moduleSelector The selector of the module element.\n * @param {String} baseUrl The base url for the page (no params).\n * @param {Number} courseId The course id\n * @param {Number} moduleId The activity module (filter)\n */\n var ModuleNavigation = function(moduleSelector, baseUrl, courseId, moduleId) {\n this._baseUrl = baseUrl;\n this._moduleId = moduleId;\n this._courseId = courseId;\n\n $(moduleSelector).on('change', this._moduleChanged.bind(this));\n };\n\n /**\n * The module was changed in the select list.\n *\n * @method _moduleChanged\n * @param {Event} e the event\n */\n ModuleNavigation.prototype._moduleChanged = function(e) {\n var newModuleId = $(e.target).val();\n var queryStr = '?mod=' + newModuleId + '&courseid=' + this._courseId;\n document.location = this._baseUrl + queryStr;\n };\n\n /** @property {Number} The id of the course. */\n ModuleNavigation.prototype._courseId = null;\n /** @property {Number} The id of the module. */\n ModuleNavigation.prototype._moduleId = null;\n /** @property {String} Plugin base url. */\n ModuleNavigation.prototype._baseUrl = null;\n\n return ModuleNavigation;\n});\n"],"names":["define","$","ModuleNavigation","moduleSelector","baseUrl","courseId","moduleId","this","_baseUrl","_moduleId","_courseId","on","_moduleChanged","bind","prototype","e","queryStr","target","val","document","location"],"mappings":";;;;;;;AAuBAA,mCAAO,CAAC,WAAW,SAASC,GAWxB,IAAIC,iBAAmB,SAASC,eAAgBC,QAASC,SAAUC,UAC/DC,KAAKC,SAAWJ,QAChBG,KAAKE,UAAYH,SACjBC,KAAKG,UAAYL,SAEjBJ,EAAEE,gBAAgBQ,GAAG,SAAUJ,KAAKK,eAAeC,KAAKN,QAsB5D,OAbAL,iBAAiBY,UAAUF,eAAiB,SAASG,GACjD,IACIC,SAAW,QADGf,EAAEc,EAAEE,QAAQC,MACS,aAAeX,KAAKG,UAC3DS,SAASC,SAAWb,KAAKC,SAAWQ,UAIxCd,iBAAiBY,UAAUJ,UAAY,KAEvCR,iBAAiBY,UAAUL,UAAY,KAEvCP,iBAAiBY,UAAUN,SAAW,KAE/BN,gBACX"}
\ No newline at end of file
diff --git a/admin/tool/lp/amd/build/parentcompetency_form.min.js.map b/admin/tool/lp/amd/build/parentcompetency_form.min.js.map
index 0b2c3238c2b..9d1cb026805 100644
--- a/admin/tool/lp/amd/build/parentcompetency_form.min.js.map
+++ b/admin/tool/lp/amd/build/parentcompetency_form.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"parentcompetency_form.min.js","sources":["../src/parentcompetency_form.js"],"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 * Handle selecting parent competency in competency form.\n *\n * @module tool_lp/parentcompetency_form\n * @copyright 2015 Issam Taboubi \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(['jquery', 'core/ajax', 'core/str', 'tool_lp/competencypicker', 'core/templates', 'core/notification'],\n function($, ajax, Str, Picker, Templates, Notification) {\n\n /**\n * Parent Competency Form object.\n * @param {String} buttonSelector The parent competency button selector.\n * @param {String} inputHiddenSelector The hidden input field selector.\n * @param {String} staticElementSelector The static element displaying the parent competency.\n * @param {Number} frameworkId The competency framework ID.\n * @param {Number} pageContextId The page context ID.\n */\n var ParentCompetencyForm = function(buttonSelector,\n inputHiddenSelector,\n staticElementSelector,\n frameworkId,\n pageContextId) {\n this.buttonSelector = buttonSelector;\n this.inputHiddenSelector = inputHiddenSelector;\n this.staticElementSelector = staticElementSelector;\n this.frameworkId = frameworkId;\n this.pageContextId = pageContextId;\n\n // Register the events.\n this.registerEvents();\n };\n\n /** @var {String} The parent competency button selector. */\n ParentCompetencyForm.prototype.buttonSelector = null;\n /** @var {String} The hidden input field selector. */\n ParentCompetencyForm.prototype.inputHiddenSelector = null;\n /** @var {String} The static element displaying the parent competency. */\n ParentCompetencyForm.prototype.staticElementSelector = null;\n /** @var {Number} The competency framework ID. */\n ParentCompetencyForm.prototype.frameworkId = null;\n /** @var {Number} The page context ID. */\n ParentCompetencyForm.prototype.pageContextId = null;\n\n /**\n * Set the parent competency in the competency form.\n *\n * @param {Object} data Data containing selected competency.\n * @method setParent\n */\n ParentCompetencyForm.prototype.setParent = function(data) {\n var self = this;\n\n if (data.competencyId !== 0) {\n ajax.call([\n {methodname: 'core_competency_read_competency', args: {\n id: data.competencyId\n }}\n ])[0].done(function(competency) {\n $(self.staticElementSelector).html(competency.shortname);\n $(self.inputHiddenSelector).val(competency.id);\n }).fail(Notification.exception);\n } else {\n // Root of competency framework selected.\n Str.get_string('competencyframeworkroot', 'tool_lp').then(function(rootframework) {\n $(self.staticElementSelector).html(rootframework);\n $(self.inputHiddenSelector).val(data.competencyId);\n return;\n }).fail(Notification.exception);\n }\n };\n\n /**\n * Register the events of parent competency button click.\n *\n * @method registerEvents\n */\n ParentCompetencyForm.prototype.registerEvents = function() {\n var self = this;\n\n // Event on edit parent button.\n $(self.buttonSelector).on('click', function(e) {\n e.preventDefault();\n\n var picker = new Picker(self.pageContextId, self.frameworkId, 'self', false);\n\n // Override the render method to make framework selectable.\n picker._render = function() {\n var self = this;\n return self._preRender().then(function() {\n var context = {\n competencies: self._competencies,\n framework: self._getFramework(self._frameworkId),\n frameworks: self._frameworks,\n search: self._searchText,\n singleFramework: self._singleFramework,\n };\n\n return Templates.render('tool_lp/competency_picker_competencyform', context);\n });\n };\n\n // On selected competency.\n picker.on('save', function(e, data) {\n self.setParent(data);\n });\n\n picker.display();\n });\n };\n\n return {\n\n /**\n * Main initialisation.\n * @param {String} buttonSelector The parent competency button selector.\n * @param {String} inputSelector The hidden input field selector.\n * @param {String} staticElementSelector The static element displaying the parent competency.\n * @param {Number} frameworkId The competency framework ID.\n * @param {Number} pageContextId The page context ID.\n * @method init\n */\n init: function(buttonSelector,\n inputSelector,\n staticElementSelector,\n frameworkId,\n pageContextId) {\n // Create instance.\n new ParentCompetencyForm(buttonSelector,\n inputSelector,\n staticElementSelector,\n frameworkId,\n pageContextId);\n }\n };\n});\n"],"names":["define","$","ajax","Str","Picker","Templates","Notification","ParentCompetencyForm","buttonSelector","inputHiddenSelector","staticElementSelector","frameworkId","pageContextId","registerEvents","prototype","setParent","data","self","this","competencyId","call","methodname","args","id","done","competency","html","shortname","val","fail","exception","get_string","then","rootframework","on","e","preventDefault","picker","_render","_preRender","context","competencies","_competencies","framework","_getFramework","_frameworkId","frameworks","_frameworks","search","_searchText","singleFramework","_singleFramework","render","display","init","inputSelector"],"mappings":";;;;;;;AAsBAA,uCAAO,CAAC,SAAU,YAAa,WAAY,2BAA4B,iBAAkB,sBACrF,SAASC,EAAGC,KAAMC,IAAKC,OAAQC,UAAWC,kBAUtCC,qBAAuB,SAASC,eACAC,oBACAC,sBACAC,YACAC,oBAC3BJ,eAAiBA,oBACjBC,oBAAsBA,yBACtBC,sBAAwBA,2BACxBC,YAAcA,iBACdC,cAAgBA,mBAGhBC,yBAITN,qBAAqBO,UAAUN,eAAiB,KAEhDD,qBAAqBO,UAAUL,oBAAsB,KAErDF,qBAAqBO,UAAUJ,sBAAwB,KAEvDH,qBAAqBO,UAAUH,YAAc,KAE7CJ,qBAAqBO,UAAUF,cAAgB,KAQ/CL,qBAAqBO,UAAUC,UAAY,SAASC,UAC5CC,KAAOC,KAEe,IAAtBF,KAAKG,aACLjB,KAAKkB,KAAK,CACN,CAACC,WAAY,kCAAmCC,KAAM,CAClDC,GAAIP,KAAKG,iBAEd,GAAGK,MAAK,SAASC,YAChBxB,EAAEgB,KAAKP,uBAAuBgB,KAAKD,WAAWE,WAC9C1B,EAAEgB,KAAKR,qBAAqBmB,IAAIH,WAAWF,OAC5CM,KAAKvB,aAAawB,WAGrB3B,IAAI4B,WAAW,0BAA2B,WAAWC,MAAK,SAASC,eAC/DhC,EAAEgB,KAAKP,uBAAuBgB,KAAKO,eACnChC,EAAEgB,KAAKR,qBAAqBmB,IAAIZ,KAAKG,iBAEtCU,KAAKvB,aAAawB,YAS7BvB,qBAAqBO,UAAUD,eAAiB,eACxCI,KAAOC,KAGXjB,EAAEgB,KAAKT,gBAAgB0B,GAAG,SAAS,SAASC,GACxCA,EAAEC,qBAEEC,OAAS,IAAIjC,OAAOa,KAAKL,cAAeK,KAAKN,YAAa,QAAQ,GAGtE0B,OAAOC,QAAU,eACTrB,KAAOC,YACJD,KAAKsB,aAAaP,MAAK,eACtBQ,QAAU,CACVC,aAAcxB,KAAKyB,cACnBC,UAAW1B,KAAK2B,cAAc3B,KAAK4B,cACnCC,WAAY7B,KAAK8B,YACjBC,OAAQ/B,KAAKgC,YACbC,gBAAiBjC,KAAKkC,yBAGnB9C,UAAU+C,OAAO,2CAA4CZ,aAK5EH,OAAOH,GAAG,QAAQ,SAASC,EAAGnB,MAC1BC,KAAKF,UAAUC,SAGnBqB,OAAOgB,cAIR,CAWHC,KAAM,SAAS9C,eACC+C,cACA7C,sBACAC,YACAC,mBAERL,qBAAqBC,eACD+C,cACA7C,sBACAC,YACAC"}
\ No newline at end of file
+{"version":3,"file":"parentcompetency_form.min.js","sources":["../src/parentcompetency_form.js"],"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 * Handle selecting parent competency in competency form.\n *\n * @module tool_lp/parentcompetency_form\n * @copyright 2015 Issam Taboubi \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(['jquery', 'core/ajax', 'core/str', 'tool_lp/competencypicker', 'core/templates', 'core/notification'],\n function($, ajax, Str, Picker, Templates, Notification) {\n\n /**\n * Parent Competency Form object.\n * @param {String} buttonSelector The parent competency button selector.\n * @param {String} inputHiddenSelector The hidden input field selector.\n * @param {String} staticElementSelector The static element displaying the parent competency.\n * @param {Number} frameworkId The competency framework ID.\n * @param {Number} pageContextId The page context ID.\n */\n var ParentCompetencyForm = function(buttonSelector,\n inputHiddenSelector,\n staticElementSelector,\n frameworkId,\n pageContextId) {\n this.buttonSelector = buttonSelector;\n this.inputHiddenSelector = inputHiddenSelector;\n this.staticElementSelector = staticElementSelector;\n this.frameworkId = frameworkId;\n this.pageContextId = pageContextId;\n\n // Register the events.\n this.registerEvents();\n };\n\n /** @var {String} The parent competency button selector. */\n ParentCompetencyForm.prototype.buttonSelector = null;\n /** @var {String} The hidden input field selector. */\n ParentCompetencyForm.prototype.inputHiddenSelector = null;\n /** @var {String} The static element displaying the parent competency. */\n ParentCompetencyForm.prototype.staticElementSelector = null;\n /** @var {Number} The competency framework ID. */\n ParentCompetencyForm.prototype.frameworkId = null;\n /** @var {Number} The page context ID. */\n ParentCompetencyForm.prototype.pageContextId = null;\n\n /**\n * Set the parent competency in the competency form.\n *\n * @param {Object} data Data containing selected competency.\n * @method setParent\n */\n ParentCompetencyForm.prototype.setParent = function(data) {\n var self = this;\n\n if (data.competencyId !== 0) {\n ajax.call([\n {methodname: 'core_competency_read_competency', args: {\n id: data.competencyId\n }}\n ])[0].done(function(competency) {\n $(self.staticElementSelector).html(competency.shortname);\n $(self.inputHiddenSelector).val(competency.id);\n }).fail(Notification.exception);\n } else {\n // Root of competency framework selected.\n Str.get_string('competencyframeworkroot', 'tool_lp').then(function(rootframework) {\n $(self.staticElementSelector).html(rootframework);\n $(self.inputHiddenSelector).val(data.competencyId);\n return;\n }).fail(Notification.exception);\n }\n };\n\n /**\n * Register the events of parent competency button click.\n *\n * @method registerEvents\n */\n ParentCompetencyForm.prototype.registerEvents = function() {\n var self = this;\n\n // Event on edit parent button.\n $(self.buttonSelector).on('click', function(e) {\n e.preventDefault();\n\n var picker = new Picker(self.pageContextId, self.frameworkId, 'self', false);\n\n // Override the render method to make framework selectable.\n picker._render = function() {\n var self = this;\n return self._preRender().then(function() {\n var context = {\n competencies: self._competencies,\n framework: self._getFramework(self._frameworkId),\n frameworks: self._frameworks,\n search: self._searchText,\n singleFramework: self._singleFramework,\n };\n\n return Templates.render('tool_lp/competency_picker_competencyform', context);\n });\n };\n\n // On selected competency.\n picker.on('save', function(e, data) {\n self.setParent(data);\n });\n\n picker.display();\n });\n };\n\n return {\n\n /**\n * Main initialisation.\n * @param {String} buttonSelector The parent competency button selector.\n * @param {String} inputSelector The hidden input field selector.\n * @param {String} staticElementSelector The static element displaying the parent competency.\n * @param {Number} frameworkId The competency framework ID.\n * @param {Number} pageContextId The page context ID.\n * @method init\n */\n init: function(buttonSelector,\n inputSelector,\n staticElementSelector,\n frameworkId,\n pageContextId) {\n // Create instance.\n new ParentCompetencyForm(buttonSelector,\n inputSelector,\n staticElementSelector,\n frameworkId,\n pageContextId);\n }\n };\n});\n"],"names":["define","$","ajax","Str","Picker","Templates","Notification","ParentCompetencyForm","buttonSelector","inputHiddenSelector","staticElementSelector","frameworkId","pageContextId","this","registerEvents","prototype","setParent","data","self","competencyId","call","methodname","args","id","done","competency","html","shortname","val","fail","exception","get_string","then","rootframework","on","e","preventDefault","picker","_render","_preRender","context","competencies","_competencies","framework","_getFramework","_frameworkId","frameworks","_frameworks","search","_searchText","singleFramework","_singleFramework","render","display","init","inputSelector"],"mappings":";;;;;;;AAsBAA,OAAM,gCAAC,CAAC,SAAU,YAAa,WAAY,2BAA4B,iBAAkB,sBACrF,SAASC,EAAGC,KAAMC,IAAKC,OAAQC,UAAWC,cAU1C,IAAIC,qBAAuB,SAASC,eACAC,oBACAC,sBACAC,YACAC,eAChCC,KAAKL,eAAiBA,eACtBK,KAAKJ,oBAAsBA,oBAC3BI,KAAKH,sBAAwBA,sBAC7BG,KAAKF,YAAcA,YACnBE,KAAKD,cAAgBA,cAGrBC,KAAKC,kBAiFT,OA7EAP,qBAAqBQ,UAAUP,eAAiB,KAEhDD,qBAAqBQ,UAAUN,oBAAsB,KAErDF,qBAAqBQ,UAAUL,sBAAwB,KAEvDH,qBAAqBQ,UAAUJ,YAAc,KAE7CJ,qBAAqBQ,UAAUH,cAAgB,KAQ/CL,qBAAqBQ,UAAUC,UAAY,SAASC,MAChD,IAAIC,KAAOL,KAEe,IAAtBI,KAAKE,aACLjB,KAAKkB,KAAK,CACN,CAACC,WAAY,kCAAmCC,KAAM,CAClDC,GAAIN,KAAKE,iBAEd,GAAGK,MAAK,SAASC,YAChBxB,EAAEiB,KAAKR,uBAAuBgB,KAAKD,WAAWE,WAC9C1B,EAAEiB,KAAKT,qBAAqBmB,IAAIH,WAAWF,GAC9C,IAAEM,KAAKvB,aAAawB,WAGrB3B,IAAI4B,WAAW,0BAA2B,WAAWC,MAAK,SAASC,eAC/DhC,EAAEiB,KAAKR,uBAAuBgB,KAAKO,eACnChC,EAAEiB,KAAKT,qBAAqBmB,IAAIX,KAAKE,aAExC,IAAEU,KAAKvB,aAAawB,YAS7BvB,qBAAqBQ,UAAUD,eAAiB,WAC5C,IAAII,KAAOL,KAGXZ,EAAEiB,KAAKV,gBAAgB0B,GAAG,SAAS,SAASC,GACxCA,EAAEC,iBAEF,IAAIC,OAAS,IAAIjC,OAAOc,KAAKN,cAAeM,KAAKP,YAAa,QAAQ,GAGtE0B,OAAOC,QAAU,WACb,IAAIpB,KAAOL,KACX,OAAOK,KAAKqB,aAAaP,MAAK,WAC1B,IAAIQ,QAAU,CACVC,aAAcvB,KAAKwB,cACnBC,UAAWzB,KAAK0B,cAAc1B,KAAK2B,cACnCC,WAAY5B,KAAK6B,YACjBC,OAAQ9B,KAAK+B,YACbC,gBAAiBhC,KAAKiC,kBAG1B,OAAO9C,UAAU+C,OAAO,2CAA4CZ,QACxE,KAIJH,OAAOH,GAAG,QAAQ,SAASC,EAAGlB,MAC1BC,KAAKF,UAAUC,KACnB,IAEAoB,OAAOgB,SACX,KAGG,CAWHC,KAAM,SAAS9C,eACC+C,cACA7C,sBACAC,YACAC,eAEZ,IAAIL,qBAAqBC,eACD+C,cACA7C,sBACAC,YACAC,cAC5B,EAER"}
\ No newline at end of file
diff --git a/admin/tool/lp/amd/build/planactions.min.js.map b/admin/tool/lp/amd/build/planactions.min.js.map
index 9ef56027339..24172b0ba15 100644
--- a/admin/tool/lp/amd/build/planactions.min.js.map
+++ b/admin/tool/lp/amd/build/planactions.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"planactions.min.js","sources":["../src/planactions.js"],"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 * Plan actions via ajax.\n *\n * @module tool_lp/planactions\n * @copyright 2015 David Monllao\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(['jquery',\n 'core/templates',\n 'core/ajax',\n 'core/notification',\n 'core/str',\n 'tool_lp/menubar',\n 'tool_lp/dialogue'],\n function($, templates, ajax, notification, str, Menubar, Dialogue) {\n\n /**\n * PlanActions class.\n *\n * Note that presently this cannot be instantiated more than once per page.\n *\n * @param {String} type The type of page we're in.\n */\n var PlanActions = function(type) {\n this._type = type;\n\n if (type === 'plan') {\n // This is the page to view one plan.\n this._region = '[data-region=\"plan-page\"]';\n this._planNode = '[data-region=\"plan-page\"]';\n this._template = 'tool_lp/plan_page';\n this._contextMethod = 'tool_lp_data_for_plan_page';\n\n } else if (type === 'plans') {\n // This is the page to view a list of plans.\n this._region = '[data-region=\"plans\"]';\n this._planNode = '[data-region=\"plan-node\"]';\n this._template = 'tool_lp/plans_page';\n this._contextMethod = 'tool_lp_data_for_plans_page';\n\n } else {\n throw new TypeError('Unexpected type.');\n }\n };\n\n /** @property {String} Ajax method to fetch the page data from. */\n PlanActions.prototype._contextMethod = null;\n /** @property {String} Selector to find the node describing the plan. */\n PlanActions.prototype._planNode = null;\n /** @property {String} Selector mapping to the region to update. Usually similar to wrapper. */\n PlanActions.prototype._region = null;\n /** @property {String} Name of the template used to render the region. */\n PlanActions.prototype._template = null;\n /** @property {String} Type of page/region we're in. */\n PlanActions.prototype._type = null;\n\n /**\n * Resolve the arguments to refresh the region.\n *\n * @param {Object} planData Plan data from plan node.\n * @return {Object} List of arguments.\n */\n PlanActions.prototype._getContextArgs = function(planData) {\n var self = this,\n args = {};\n\n if (self._type === 'plan') {\n args = {\n planid: planData.id\n };\n\n } else if (self._type === 'plans') {\n args = {\n userid: planData.userid\n };\n }\n\n return args;\n };\n\n /**\n * Refresh the plan view.\n *\n * This is useful when you only want to refresh the view.\n *\n * @param {String} selector The node to search the plan data from.\n */\n PlanActions.prototype.refresh = function(selector) {\n var planData = this._findPlanData($(selector));\n this._callAndRefresh([], planData);\n };\n\n /**\n * Callback to render the region template.\n *\n * @param {Object} context The context for the template.\n * @return {Promise}\n */\n PlanActions.prototype._renderView = function(context) {\n var self = this;\n return templates.render(self._template, context)\n .then(function(newhtml, newjs) {\n $(self._region).replaceWith(newhtml);\n templates.runTemplateJS(newjs);\n return;\n });\n };\n\n /**\n * Call multiple ajax methods, and refresh.\n *\n * @param {Array} calls List of Ajax calls.\n * @param {Object} planData Plan data from plan node.\n * @return {Promise}\n */\n PlanActions.prototype._callAndRefresh = function(calls, planData) {\n // Because this function causes a refresh, we must track the JS completion from start to finish to prevent\n // stale reference issues in Behat.\n var callKey = 'tool_lp/planactions:_callAndRefresh-' + Math.floor(Math.random() * Math.floor(1000));\n M.util.js_pending(callKey);\n\n var self = this;\n calls.push({\n methodname: self._contextMethod,\n args: self._getContextArgs(planData)\n });\n\n // Apply all the promises, and refresh when the last one is resolved.\n return $.when.apply($, ajax.call(calls))\n .then(function() {\n return self._renderView(arguments[arguments.length - 1]);\n })\n .fail(notification.exception)\n .always(function() {\n return M.util.js_complete(callKey);\n });\n };\n\n /**\n * Delete a plan and reload the region.\n *\n * @param {Object} planData Plan data from plan node.\n */\n PlanActions.prototype._doDelete = function(planData) {\n var self = this,\n calls = [{\n methodname: 'core_competency_delete_plan',\n args: {id: planData.id}\n }];\n self._callAndRefresh(calls, planData);\n };\n\n /**\n * Delete a plan.\n *\n * @param {Object} planData Plan data from plan node.\n */\n PlanActions.prototype.deletePlan = function(planData) {\n var self = this,\n requests;\n\n requests = ajax.call([{\n methodname: 'core_competency_read_plan',\n args: {id: planData.id}\n }]);\n\n requests[0].done(function(plan) {\n str.get_strings([\n {key: 'confirm', component: 'moodle'},\n {key: 'deleteplan', component: 'tool_lp', param: plan.name},\n {key: 'delete', component: 'moodle'},\n {key: 'cancel', component: 'moodle'}\n ]).done(function(strings) {\n notification.confirm(\n strings[0], // Confirm.\n strings[1], // Delete plan X?\n strings[2], // Delete.\n strings[3], // Cancel.\n function() {\n self._doDelete(planData);\n }\n );\n }).fail(notification.exception);\n }).fail(notification.exception);\n\n };\n\n /**\n * Reopen plan and reload the region.\n *\n * @param {Object} planData Plan data from plan node.\n */\n PlanActions.prototype._doReopenPlan = function(planData) {\n var self = this,\n calls = [{\n methodname: 'core_competency_reopen_plan',\n args: {planid: planData.id}\n }];\n self._callAndRefresh(calls, planData);\n };\n\n /**\n * Reopen a plan.\n *\n * @param {Object} planData Plan data from plan node.\n */\n PlanActions.prototype.reopenPlan = function(planData) {\n var self = this,\n requests = ajax.call([{\n methodname: 'core_competency_read_plan',\n args: {id: planData.id}\n }]);\n\n requests[0].done(function(plan) {\n str.get_strings([\n {key: 'confirm', component: 'moodle'},\n {key: 'reopenplanconfirm', component: 'tool_lp', param: plan.name},\n {key: 'reopenplan', component: 'tool_lp'},\n {key: 'cancel', component: 'moodle'}\n ]).done(function(strings) {\n notification.confirm(\n strings[0], // Confirm.\n strings[1], // Reopen plan X?\n strings[2], // Reopen.\n strings[3], // Cancel.\n function() {\n self._doReopenPlan(planData);\n }\n );\n }).fail(notification.exception);\n }).fail(notification.exception);\n\n };\n\n /**\n * Complete plan and reload the region.\n *\n * @param {Object} planData Plan data from plan node.\n */\n PlanActions.prototype._doCompletePlan = function(planData) {\n var self = this,\n calls = [{\n methodname: 'core_competency_complete_plan',\n args: {planid: planData.id}\n }];\n self._callAndRefresh(calls, planData);\n };\n\n /**\n * Complete a plan process.\n *\n * @param {Object} planData Plan data from plan node.\n */\n PlanActions.prototype.completePlan = function(planData) {\n var self = this,\n requests = ajax.call([{\n methodname: 'core_competency_read_plan',\n args: {id: planData.id}\n }]);\n\n requests[0].done(function(plan) {\n str.get_strings([\n {key: 'confirm', component: 'moodle'},\n {key: 'completeplanconfirm', component: 'tool_lp', param: plan.name},\n {key: 'completeplan', component: 'tool_lp'},\n {key: 'cancel', component: 'moodle'}\n ]).done(function(strings) {\n notification.confirm(\n strings[0], // Confirm.\n strings[1], // Complete plan X?\n strings[2], // Complete.\n strings[3], // Cancel.\n function() {\n self._doCompletePlan(planData);\n }\n );\n }).fail(notification.exception);\n }).fail(notification.exception);\n };\n\n /**\n * Unlink plan and reload the region.\n *\n * @param {Object} planData Plan data from plan node.\n */\n PlanActions.prototype._doUnlinkPlan = function(planData) {\n var self = this,\n calls = [{\n methodname: 'core_competency_unlink_plan_from_template',\n args: {planid: planData.id}\n }];\n self._callAndRefresh(calls, planData);\n };\n\n /**\n * Unlink a plan process.\n *\n * @param {Object} planData Plan data from plan node.\n */\n PlanActions.prototype.unlinkPlan = function(planData) {\n var self = this,\n requests = ajax.call([{\n methodname: 'core_competency_read_plan',\n args: {id: planData.id}\n }]);\n\n requests[0].done(function(plan) {\n str.get_strings([\n {key: 'confirm', component: 'moodle'},\n {key: 'unlinkplantemplateconfirm', component: 'tool_lp', param: plan.name},\n {key: 'unlinkplantemplate', component: 'tool_lp'},\n {key: 'cancel', component: 'moodle'}\n ]).done(function(strings) {\n notification.confirm(\n strings[0], // Confirm.\n strings[1], // Unlink plan X?\n strings[2], // Unlink.\n strings[3], // Cancel.\n function() {\n self._doUnlinkPlan(planData);\n }\n );\n }).fail(notification.exception);\n }).fail(notification.exception);\n };\n\n /**\n * Request review of a plan.\n *\n * @param {Object} planData Plan data from plan node.\n * @method _doRequestReview\n */\n PlanActions.prototype._doRequestReview = function(planData) {\n var calls = [{\n methodname: 'core_competency_plan_request_review',\n args: {\n id: planData.id\n }\n }];\n this._callAndRefresh(calls, planData);\n };\n\n /**\n * Request review of a plan.\n *\n * @param {Object} planData Plan data from plan node.\n * @method requestReview\n */\n PlanActions.prototype.requestReview = function(planData) {\n this._doRequestReview(planData);\n };\n\n /**\n * Cancel review request of a plan.\n *\n * @param {Object} planData Plan data from plan node.\n * @method _doCancelReviewRequest\n */\n PlanActions.prototype._doCancelReviewRequest = function(planData) {\n var calls = [{\n methodname: 'core_competency_plan_cancel_review_request',\n args: {\n id: planData.id\n }\n }];\n this._callAndRefresh(calls, planData);\n };\n\n /**\n * Cancel review request of a plan.\n *\n * @param {Object} planData Plan data from plan node.\n * @method cancelReviewRequest\n */\n PlanActions.prototype.cancelReviewRequest = function(planData) {\n this._doCancelReviewRequest(planData);\n };\n\n /**\n * Start review of a plan.\n *\n * @param {Object} planData Plan data from plan node.\n * @method _doStartReview\n */\n PlanActions.prototype._doStartReview = function(planData) {\n var calls = [{\n methodname: 'core_competency_plan_start_review',\n args: {\n id: planData.id\n }\n }];\n this._callAndRefresh(calls, planData);\n };\n\n /**\n * Start review of a plan.\n *\n * @param {Object} planData Plan data from plan node.\n * @method startReview\n */\n PlanActions.prototype.startReview = function(planData) {\n this._doStartReview(planData);\n };\n\n /**\n * Stop review of a plan.\n *\n * @param {Object} planData Plan data from plan node.\n * @method _doStopReview\n */\n PlanActions.prototype._doStopReview = function(planData) {\n var calls = [{\n methodname: 'core_competency_plan_stop_review',\n args: {\n id: planData.id\n }\n }];\n this._callAndRefresh(calls, planData);\n };\n\n /**\n * Stop review of a plan.\n *\n * @param {Object} planData Plan data from plan node.\n * @method stopReview\n */\n PlanActions.prototype.stopReview = function(planData) {\n this._doStopReview(planData);\n };\n\n /**\n * Approve a plan.\n *\n * @param {Object} planData Plan data from plan node.\n * @method _doApprove\n */\n PlanActions.prototype._doApprove = function(planData) {\n var calls = [{\n methodname: 'core_competency_approve_plan',\n args: {\n id: planData.id\n }\n }];\n this._callAndRefresh(calls, planData);\n };\n\n /**\n * Approve a plan.\n *\n * @param {Object} planData Plan data from plan node.\n * @method approve\n */\n PlanActions.prototype.approve = function(planData) {\n this._doApprove(planData);\n };\n\n /**\n * Unapprove a plan.\n *\n * @param {Object} planData Plan data from plan node.\n * @method _doUnapprove\n */\n PlanActions.prototype._doUnapprove = function(planData) {\n var calls = [{\n methodname: 'core_competency_unapprove_plan',\n args: {\n id: planData.id\n }\n }];\n this._callAndRefresh(calls, planData);\n };\n\n /**\n * Unapprove a plan.\n *\n * @param {Object} planData Plan data from plan node.\n * @method unapprove\n */\n PlanActions.prototype.unapprove = function(planData) {\n this._doUnapprove(planData);\n };\n\n /**\n * Display list of linked courses on a modal dialogue.\n *\n * @param {Event} e The event.\n */\n PlanActions.prototype._showLinkedCoursesHandler = function(e) {\n e.preventDefault();\n\n var competencyid = $(e.target).data('id');\n var requests = ajax.call([{\n methodname: 'tool_lp_list_courses_using_competency',\n args: {id: competencyid}\n }]);\n\n requests[0].done(function(courses) {\n var context = {\n courses: courses\n };\n templates.render('tool_lp/linked_courses_summary', context).done(function(html) {\n str.get_string('linkedcourses', 'tool_lp').done(function(linkedcourses) {\n new Dialogue(\n linkedcourses, // Title.\n html // The linked courses.\n );\n }).fail(notification.exception);\n }).fail(notification.exception);\n }).fail(notification.exception);\n };\n\n /**\n * Plan event handler.\n *\n * @param {String} method The method to call.\n * @param {Event} e The event.\n * @method _eventHandler\n */\n PlanActions.prototype._eventHandler = function(method, e) {\n e.preventDefault();\n var data = this._findPlanData($(e.target));\n this[method](data);\n };\n\n /**\n * Find the plan data from the plan node.\n *\n * @param {Node} node The node to search from.\n * @return {Object} Plan data.\n */\n PlanActions.prototype._findPlanData = function(node) {\n var parent = node.parentsUntil($(this._region).parent(), this._planNode),\n data;\n\n if (parent.length != 1) {\n throw new Error('The plan node was not located.');\n }\n\n data = parent.data();\n if (typeof data === 'undefined' || typeof data.id === 'undefined') {\n throw new Error('Plan data could not be found.');\n }\n\n return data;\n };\n\n /**\n * Enhance a menu bar.\n *\n * @param {String} selector Menubar selector.\n */\n PlanActions.prototype.enhanceMenubar = function(selector) {\n Menubar.enhance(selector, {\n '[data-action=\"plan-delete\"]': this._eventHandler.bind(this, 'deletePlan'),\n '[data-action=\"plan-complete\"]': this._eventHandler.bind(this, 'completePlan'),\n '[data-action=\"plan-reopen\"]': this._eventHandler.bind(this, 'reopenPlan'),\n '[data-action=\"plan-unlink\"]': this._eventHandler.bind(this, 'unlinkPlan'),\n '[data-action=\"plan-request-review\"]': this._eventHandler.bind(this, 'requestReview'),\n '[data-action=\"plan-cancel-review-request\"]': this._eventHandler.bind(this, 'cancelReviewRequest'),\n '[data-action=\"plan-start-review\"]': this._eventHandler.bind(this, 'startReview'),\n '[data-action=\"plan-stop-review\"]': this._eventHandler.bind(this, 'stopReview'),\n '[data-action=\"plan-approve\"]': this._eventHandler.bind(this, 'approve'),\n '[data-action=\"plan-unapprove\"]': this._eventHandler.bind(this, 'unapprove'),\n });\n };\n\n /**\n * Register the events in the region.\n *\n * At this stage this cannot be used with enhanceMenubar or multiple handlers\n * will be added to the same node.\n */\n PlanActions.prototype.registerEvents = function() {\n var wrapper = $(this._region);\n\n wrapper.find('[data-action=\"plan-delete\"]').click(this._eventHandler.bind(this, 'deletePlan'));\n wrapper.find('[data-action=\"plan-complete\"]').click(this._eventHandler.bind(this, 'completePlan'));\n wrapper.find('[data-action=\"plan-reopen\"]').click(this._eventHandler.bind(this, 'reopenPlan'));\n wrapper.find('[data-action=\"plan-unlink\"]').click(this._eventHandler.bind(this, 'unlinkPlan'));\n\n wrapper.find('[data-action=\"plan-request-review\"]').click(this._eventHandler.bind(this, 'requestReview'));\n wrapper.find('[data-action=\"plan-cancel-review-request\"]').click(this._eventHandler.bind(this, 'cancelReviewRequest'));\n wrapper.find('[data-action=\"plan-start-review\"]').click(this._eventHandler.bind(this, 'startReview'));\n wrapper.find('[data-action=\"plan-stop-review\"]').click(this._eventHandler.bind(this, 'stopReview'));\n wrapper.find('[data-action=\"plan-approve\"]').click(this._eventHandler.bind(this, 'approve'));\n wrapper.find('[data-action=\"plan-unapprove\"]').click(this._eventHandler.bind(this, 'unapprove'));\n\n wrapper.find('[data-action=\"find-courses-link\"]').click(this._showLinkedCoursesHandler.bind(this));\n };\n\n return PlanActions;\n});\n"],"names":["define","$","templates","ajax","notification","str","Menubar","Dialogue","PlanActions","type","_type","_region","_planNode","_template","_contextMethod","TypeError","prototype","_getContextArgs","planData","args","this","planid","id","userid","refresh","selector","_findPlanData","_callAndRefresh","_renderView","context","self","render","then","newhtml","newjs","replaceWith","runTemplateJS","calls","callKey","Math","floor","random","M","util","js_pending","push","methodname","when","apply","call","arguments","length","fail","exception","always","js_complete","_doDelete","deletePlan","done","plan","get_strings","key","component","param","name","strings","confirm","_doReopenPlan","reopenPlan","_doCompletePlan","completePlan","_doUnlinkPlan","unlinkPlan","_doRequestReview","requestReview","_doCancelReviewRequest","cancelReviewRequest","_doStartReview","startReview","_doStopReview","stopReview","_doApprove","approve","_doUnapprove","unapprove","_showLinkedCoursesHandler","e","preventDefault","competencyid","target","data","courses","html","get_string","linkedcourses","_eventHandler","method","node","parent","parentsUntil","Error","enhanceMenubar","enhance","bind","registerEvents","wrapper","find","click"],"mappings":"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"}
\ No newline at end of file
+{"version":3,"file":"planactions.min.js","sources":["../src/planactions.js"],"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 * Plan actions via ajax.\n *\n * @module tool_lp/planactions\n * @copyright 2015 David Monllao\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(['jquery',\n 'core/templates',\n 'core/ajax',\n 'core/notification',\n 'core/str',\n 'tool_lp/menubar',\n 'tool_lp/dialogue'],\n function($, templates, ajax, notification, str, Menubar, Dialogue) {\n\n /**\n * PlanActions class.\n *\n * Note that presently this cannot be instantiated more than once per page.\n *\n * @param {String} type The type of page we're in.\n */\n var PlanActions = function(type) {\n this._type = type;\n\n if (type === 'plan') {\n // This is the page to view one plan.\n this._region = '[data-region=\"plan-page\"]';\n this._planNode = '[data-region=\"plan-page\"]';\n this._template = 'tool_lp/plan_page';\n this._contextMethod = 'tool_lp_data_for_plan_page';\n\n } else if (type === 'plans') {\n // This is the page to view a list of plans.\n this._region = '[data-region=\"plans\"]';\n this._planNode = '[data-region=\"plan-node\"]';\n this._template = 'tool_lp/plans_page';\n this._contextMethod = 'tool_lp_data_for_plans_page';\n\n } else {\n throw new TypeError('Unexpected type.');\n }\n };\n\n /** @property {String} Ajax method to fetch the page data from. */\n PlanActions.prototype._contextMethod = null;\n /** @property {String} Selector to find the node describing the plan. */\n PlanActions.prototype._planNode = null;\n /** @property {String} Selector mapping to the region to update. Usually similar to wrapper. */\n PlanActions.prototype._region = null;\n /** @property {String} Name of the template used to render the region. */\n PlanActions.prototype._template = null;\n /** @property {String} Type of page/region we're in. */\n PlanActions.prototype._type = null;\n\n /**\n * Resolve the arguments to refresh the region.\n *\n * @param {Object} planData Plan data from plan node.\n * @return {Object} List of arguments.\n */\n PlanActions.prototype._getContextArgs = function(planData) {\n var self = this,\n args = {};\n\n if (self._type === 'plan') {\n args = {\n planid: planData.id\n };\n\n } else if (self._type === 'plans') {\n args = {\n userid: planData.userid\n };\n }\n\n return args;\n };\n\n /**\n * Refresh the plan view.\n *\n * This is useful when you only want to refresh the view.\n *\n * @param {String} selector The node to search the plan data from.\n */\n PlanActions.prototype.refresh = function(selector) {\n var planData = this._findPlanData($(selector));\n this._callAndRefresh([], planData);\n };\n\n /**\n * Callback to render the region template.\n *\n * @param {Object} context The context for the template.\n * @return {Promise}\n */\n PlanActions.prototype._renderView = function(context) {\n var self = this;\n return templates.render(self._template, context)\n .then(function(newhtml, newjs) {\n $(self._region).replaceWith(newhtml);\n templates.runTemplateJS(newjs);\n return;\n });\n };\n\n /**\n * Call multiple ajax methods, and refresh.\n *\n * @param {Array} calls List of Ajax calls.\n * @param {Object} planData Plan data from plan node.\n * @return {Promise}\n */\n PlanActions.prototype._callAndRefresh = function(calls, planData) {\n // Because this function causes a refresh, we must track the JS completion from start to finish to prevent\n // stale reference issues in Behat.\n var callKey = 'tool_lp/planactions:_callAndRefresh-' + Math.floor(Math.random() * Math.floor(1000));\n M.util.js_pending(callKey);\n\n var self = this;\n calls.push({\n methodname: self._contextMethod,\n args: self._getContextArgs(planData)\n });\n\n // Apply all the promises, and refresh when the last one is resolved.\n return $.when.apply($, ajax.call(calls))\n .then(function() {\n return self._renderView(arguments[arguments.length - 1]);\n })\n .fail(notification.exception)\n .always(function() {\n return M.util.js_complete(callKey);\n });\n };\n\n /**\n * Delete a plan and reload the region.\n *\n * @param {Object} planData Plan data from plan node.\n */\n PlanActions.prototype._doDelete = function(planData) {\n var self = this,\n calls = [{\n methodname: 'core_competency_delete_plan',\n args: {id: planData.id}\n }];\n self._callAndRefresh(calls, planData);\n };\n\n /**\n * Delete a plan.\n *\n * @param {Object} planData Plan data from plan node.\n */\n PlanActions.prototype.deletePlan = function(planData) {\n var self = this,\n requests;\n\n requests = ajax.call([{\n methodname: 'core_competency_read_plan',\n args: {id: planData.id}\n }]);\n\n requests[0].done(function(plan) {\n str.get_strings([\n {key: 'confirm', component: 'moodle'},\n {key: 'deleteplan', component: 'tool_lp', param: plan.name},\n {key: 'delete', component: 'moodle'},\n {key: 'cancel', component: 'moodle'}\n ]).done(function(strings) {\n notification.confirm(\n strings[0], // Confirm.\n strings[1], // Delete plan X?\n strings[2], // Delete.\n strings[3], // Cancel.\n function() {\n self._doDelete(planData);\n }\n );\n }).fail(notification.exception);\n }).fail(notification.exception);\n\n };\n\n /**\n * Reopen plan and reload the region.\n *\n * @param {Object} planData Plan data from plan node.\n */\n PlanActions.prototype._doReopenPlan = function(planData) {\n var self = this,\n calls = [{\n methodname: 'core_competency_reopen_plan',\n args: {planid: planData.id}\n }];\n self._callAndRefresh(calls, planData);\n };\n\n /**\n * Reopen a plan.\n *\n * @param {Object} planData Plan data from plan node.\n */\n PlanActions.prototype.reopenPlan = function(planData) {\n var self = this,\n requests = ajax.call([{\n methodname: 'core_competency_read_plan',\n args: {id: planData.id}\n }]);\n\n requests[0].done(function(plan) {\n str.get_strings([\n {key: 'confirm', component: 'moodle'},\n {key: 'reopenplanconfirm', component: 'tool_lp', param: plan.name},\n {key: 'reopenplan', component: 'tool_lp'},\n {key: 'cancel', component: 'moodle'}\n ]).done(function(strings) {\n notification.confirm(\n strings[0], // Confirm.\n strings[1], // Reopen plan X?\n strings[2], // Reopen.\n strings[3], // Cancel.\n function() {\n self._doReopenPlan(planData);\n }\n );\n }).fail(notification.exception);\n }).fail(notification.exception);\n\n };\n\n /**\n * Complete plan and reload the region.\n *\n * @param {Object} planData Plan data from plan node.\n */\n PlanActions.prototype._doCompletePlan = function(planData) {\n var self = this,\n calls = [{\n methodname: 'core_competency_complete_plan',\n args: {planid: planData.id}\n }];\n self._callAndRefresh(calls, planData);\n };\n\n /**\n * Complete a plan process.\n *\n * @param {Object} planData Plan data from plan node.\n */\n PlanActions.prototype.completePlan = function(planData) {\n var self = this,\n requests = ajax.call([{\n methodname: 'core_competency_read_plan',\n args: {id: planData.id}\n }]);\n\n requests[0].done(function(plan) {\n str.get_strings([\n {key: 'confirm', component: 'moodle'},\n {key: 'completeplanconfirm', component: 'tool_lp', param: plan.name},\n {key: 'completeplan', component: 'tool_lp'},\n {key: 'cancel', component: 'moodle'}\n ]).done(function(strings) {\n notification.confirm(\n strings[0], // Confirm.\n strings[1], // Complete plan X?\n strings[2], // Complete.\n strings[3], // Cancel.\n function() {\n self._doCompletePlan(planData);\n }\n );\n }).fail(notification.exception);\n }).fail(notification.exception);\n };\n\n /**\n * Unlink plan and reload the region.\n *\n * @param {Object} planData Plan data from plan node.\n */\n PlanActions.prototype._doUnlinkPlan = function(planData) {\n var self = this,\n calls = [{\n methodname: 'core_competency_unlink_plan_from_template',\n args: {planid: planData.id}\n }];\n self._callAndRefresh(calls, planData);\n };\n\n /**\n * Unlink a plan process.\n *\n * @param {Object} planData Plan data from plan node.\n */\n PlanActions.prototype.unlinkPlan = function(planData) {\n var self = this,\n requests = ajax.call([{\n methodname: 'core_competency_read_plan',\n args: {id: planData.id}\n }]);\n\n requests[0].done(function(plan) {\n str.get_strings([\n {key: 'confirm', component: 'moodle'},\n {key: 'unlinkplantemplateconfirm', component: 'tool_lp', param: plan.name},\n {key: 'unlinkplantemplate', component: 'tool_lp'},\n {key: 'cancel', component: 'moodle'}\n ]).done(function(strings) {\n notification.confirm(\n strings[0], // Confirm.\n strings[1], // Unlink plan X?\n strings[2], // Unlink.\n strings[3], // Cancel.\n function() {\n self._doUnlinkPlan(planData);\n }\n );\n }).fail(notification.exception);\n }).fail(notification.exception);\n };\n\n /**\n * Request review of a plan.\n *\n * @param {Object} planData Plan data from plan node.\n * @method _doRequestReview\n */\n PlanActions.prototype._doRequestReview = function(planData) {\n var calls = [{\n methodname: 'core_competency_plan_request_review',\n args: {\n id: planData.id\n }\n }];\n this._callAndRefresh(calls, planData);\n };\n\n /**\n * Request review of a plan.\n *\n * @param {Object} planData Plan data from plan node.\n * @method requestReview\n */\n PlanActions.prototype.requestReview = function(planData) {\n this._doRequestReview(planData);\n };\n\n /**\n * Cancel review request of a plan.\n *\n * @param {Object} planData Plan data from plan node.\n * @method _doCancelReviewRequest\n */\n PlanActions.prototype._doCancelReviewRequest = function(planData) {\n var calls = [{\n methodname: 'core_competency_plan_cancel_review_request',\n args: {\n id: planData.id\n }\n }];\n this._callAndRefresh(calls, planData);\n };\n\n /**\n * Cancel review request of a plan.\n *\n * @param {Object} planData Plan data from plan node.\n * @method cancelReviewRequest\n */\n PlanActions.prototype.cancelReviewRequest = function(planData) {\n this._doCancelReviewRequest(planData);\n };\n\n /**\n * Start review of a plan.\n *\n * @param {Object} planData Plan data from plan node.\n * @method _doStartReview\n */\n PlanActions.prototype._doStartReview = function(planData) {\n var calls = [{\n methodname: 'core_competency_plan_start_review',\n args: {\n id: planData.id\n }\n }];\n this._callAndRefresh(calls, planData);\n };\n\n /**\n * Start review of a plan.\n *\n * @param {Object} planData Plan data from plan node.\n * @method startReview\n */\n PlanActions.prototype.startReview = function(planData) {\n this._doStartReview(planData);\n };\n\n /**\n * Stop review of a plan.\n *\n * @param {Object} planData Plan data from plan node.\n * @method _doStopReview\n */\n PlanActions.prototype._doStopReview = function(planData) {\n var calls = [{\n methodname: 'core_competency_plan_stop_review',\n args: {\n id: planData.id\n }\n }];\n this._callAndRefresh(calls, planData);\n };\n\n /**\n * Stop review of a plan.\n *\n * @param {Object} planData Plan data from plan node.\n * @method stopReview\n */\n PlanActions.prototype.stopReview = function(planData) {\n this._doStopReview(planData);\n };\n\n /**\n * Approve a plan.\n *\n * @param {Object} planData Plan data from plan node.\n * @method _doApprove\n */\n PlanActions.prototype._doApprove = function(planData) {\n var calls = [{\n methodname: 'core_competency_approve_plan',\n args: {\n id: planData.id\n }\n }];\n this._callAndRefresh(calls, planData);\n };\n\n /**\n * Approve a plan.\n *\n * @param {Object} planData Plan data from plan node.\n * @method approve\n */\n PlanActions.prototype.approve = function(planData) {\n this._doApprove(planData);\n };\n\n /**\n * Unapprove a plan.\n *\n * @param {Object} planData Plan data from plan node.\n * @method _doUnapprove\n */\n PlanActions.prototype._doUnapprove = function(planData) {\n var calls = [{\n methodname: 'core_competency_unapprove_plan',\n args: {\n id: planData.id\n }\n }];\n this._callAndRefresh(calls, planData);\n };\n\n /**\n * Unapprove a plan.\n *\n * @param {Object} planData Plan data from plan node.\n * @method unapprove\n */\n PlanActions.prototype.unapprove = function(planData) {\n this._doUnapprove(planData);\n };\n\n /**\n * Display list of linked courses on a modal dialogue.\n *\n * @param {Event} e The event.\n */\n PlanActions.prototype._showLinkedCoursesHandler = function(e) {\n e.preventDefault();\n\n var competencyid = $(e.target).data('id');\n var requests = ajax.call([{\n methodname: 'tool_lp_list_courses_using_competency',\n args: {id: competencyid}\n }]);\n\n requests[0].done(function(courses) {\n var context = {\n courses: courses\n };\n templates.render('tool_lp/linked_courses_summary', context).done(function(html) {\n str.get_string('linkedcourses', 'tool_lp').done(function(linkedcourses) {\n new Dialogue(\n linkedcourses, // Title.\n html // The linked courses.\n );\n }).fail(notification.exception);\n }).fail(notification.exception);\n }).fail(notification.exception);\n };\n\n /**\n * Plan event handler.\n *\n * @param {String} method The method to call.\n * @param {Event} e The event.\n * @method _eventHandler\n */\n PlanActions.prototype._eventHandler = function(method, e) {\n e.preventDefault();\n var data = this._findPlanData($(e.target));\n this[method](data);\n };\n\n /**\n * Find the plan data from the plan node.\n *\n * @param {Node} node The node to search from.\n * @return {Object} Plan data.\n */\n PlanActions.prototype._findPlanData = function(node) {\n var parent = node.parentsUntil($(this._region).parent(), this._planNode),\n data;\n\n if (parent.length != 1) {\n throw new Error('The plan node was not located.');\n }\n\n data = parent.data();\n if (typeof data === 'undefined' || typeof data.id === 'undefined') {\n throw new Error('Plan data could not be found.');\n }\n\n return data;\n };\n\n /**\n * Enhance a menu bar.\n *\n * @param {String} selector Menubar selector.\n */\n PlanActions.prototype.enhanceMenubar = function(selector) {\n Menubar.enhance(selector, {\n '[data-action=\"plan-delete\"]': this._eventHandler.bind(this, 'deletePlan'),\n '[data-action=\"plan-complete\"]': this._eventHandler.bind(this, 'completePlan'),\n '[data-action=\"plan-reopen\"]': this._eventHandler.bind(this, 'reopenPlan'),\n '[data-action=\"plan-unlink\"]': this._eventHandler.bind(this, 'unlinkPlan'),\n '[data-action=\"plan-request-review\"]': this._eventHandler.bind(this, 'requestReview'),\n '[data-action=\"plan-cancel-review-request\"]': this._eventHandler.bind(this, 'cancelReviewRequest'),\n '[data-action=\"plan-start-review\"]': this._eventHandler.bind(this, 'startReview'),\n '[data-action=\"plan-stop-review\"]': this._eventHandler.bind(this, 'stopReview'),\n '[data-action=\"plan-approve\"]': this._eventHandler.bind(this, 'approve'),\n '[data-action=\"plan-unapprove\"]': this._eventHandler.bind(this, 'unapprove'),\n });\n };\n\n /**\n * Register the events in the region.\n *\n * At this stage this cannot be used with enhanceMenubar or multiple handlers\n * will be added to the same node.\n */\n PlanActions.prototype.registerEvents = function() {\n var wrapper = $(this._region);\n\n wrapper.find('[data-action=\"plan-delete\"]').click(this._eventHandler.bind(this, 'deletePlan'));\n wrapper.find('[data-action=\"plan-complete\"]').click(this._eventHandler.bind(this, 'completePlan'));\n wrapper.find('[data-action=\"plan-reopen\"]').click(this._eventHandler.bind(this, 'reopenPlan'));\n wrapper.find('[data-action=\"plan-unlink\"]').click(this._eventHandler.bind(this, 'unlinkPlan'));\n\n wrapper.find('[data-action=\"plan-request-review\"]').click(this._eventHandler.bind(this, 'requestReview'));\n wrapper.find('[data-action=\"plan-cancel-review-request\"]').click(this._eventHandler.bind(this, 'cancelReviewRequest'));\n wrapper.find('[data-action=\"plan-start-review\"]').click(this._eventHandler.bind(this, 'startReview'));\n wrapper.find('[data-action=\"plan-stop-review\"]').click(this._eventHandler.bind(this, 'stopReview'));\n wrapper.find('[data-action=\"plan-approve\"]').click(this._eventHandler.bind(this, 'approve'));\n wrapper.find('[data-action=\"plan-unapprove\"]').click(this._eventHandler.bind(this, 'unapprove'));\n\n wrapper.find('[data-action=\"find-courses-link\"]').click(this._showLinkedCoursesHandler.bind(this));\n };\n\n return PlanActions;\n});\n"],"names":["define","$","templates","ajax","notification","str","Menubar","Dialogue","PlanActions","type","this","_type","_region","_planNode","_template","_contextMethod","TypeError","prototype","_getContextArgs","planData","args","planid","id","userid","refresh","selector","_findPlanData","_callAndRefresh","_renderView","context","self","render","then","newhtml","newjs","replaceWith","runTemplateJS","calls","callKey","Math","floor","random","M","util","js_pending","push","methodname","when","apply","call","arguments","length","fail","exception","always","js_complete","_doDelete","deletePlan","done","plan","get_strings","key","component","param","name","strings","confirm","_doReopenPlan","reopenPlan","_doCompletePlan","completePlan","_doUnlinkPlan","unlinkPlan","_doRequestReview","requestReview","_doCancelReviewRequest","cancelReviewRequest","_doStartReview","startReview","_doStopReview","stopReview","_doApprove","approve","_doUnapprove","unapprove","_showLinkedCoursesHandler","e","preventDefault","competencyid","target","data","courses","html","get_string","linkedcourses","_eventHandler","method","node","parent","parentsUntil","Error","enhanceMenubar","enhance","bind","registerEvents","wrapper","find","click"],"mappings":"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"}
\ No newline at end of file
diff --git a/admin/tool/lp/amd/build/scaleconfig.min.js.map b/admin/tool/lp/amd/build/scaleconfig.min.js.map
index 3a709b50add..d6e8d7d7336 100644
--- a/admin/tool/lp/amd/build/scaleconfig.min.js.map
+++ b/admin/tool/lp/amd/build/scaleconfig.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"scaleconfig.min.js","sources":["../src/scaleconfig.js"],"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 * Handle opening a dialogue to configure scale data.\n *\n * @module tool_lp/scaleconfig\n * @copyright 2015 Adrian Greeve \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(['jquery', 'core/notification', 'core/templates', 'core/ajax', 'tool_lp/dialogue', 'tool_lp/scalevalues'],\n function($, notification, templates, ajax, Dialogue, ModScaleValues) {\n\n /**\n * Scale config object.\n * @param {String} selectSelector The select box selector.\n * @param {String} inputSelector The hidden input field selector.\n * @param {String} triggerSelector The trigger selector.\n */\n var ScaleConfig = function(selectSelector, inputSelector, triggerSelector) {\n this.selectSelector = selectSelector;\n this.inputSelector = inputSelector;\n this.triggerSelector = triggerSelector;\n\n // Get the current scale ID.\n this.originalscaleid = $(selectSelector).val();\n $(selectSelector).on('change', this.scaleChangeHandler.bind(this)).change();\n $(triggerSelector).click(this.showConfig.bind(this));\n };\n\n /** @var {String} The select box selector. */\n ScaleConfig.prototype.selectSelector = null;\n /** @var {String} The hidden field selector. */\n ScaleConfig.prototype.inputSelector = null;\n /** @var {String} The trigger selector. */\n ScaleConfig.prototype.triggerSelector = null;\n /** @var {Array} scalevalues ID and name of the scales. */\n ScaleConfig.prototype.scalevalues = null;\n /** @var {Number) originalscaleid Original scale ID when the page loads. */\n ScaleConfig.prototype.originalscaleid = 0;\n /** @var {Number} scaleid Current scale ID. */\n ScaleConfig.prototype.scaleid = 0;\n /** @var {Dialogue} Reference to the popup. */\n ScaleConfig.prototype.popup = null;\n\n /**\n * Displays the scale configuration dialogue.\n *\n * @method showConfig\n */\n ScaleConfig.prototype.showConfig = function() {\n var self = this;\n\n this.scaleid = $(this.selectSelector).val();\n if (this.scaleid <= 0) {\n // This should not happen.\n return;\n }\n\n var scalename = $(this.selectSelector).find(\"option:selected\").text();\n this.getScaleValues(this.scaleid).done(function() {\n\n var context = {\n scalename: scalename,\n scales: self.scalevalues\n };\n\n // Dish up the form.\n templates.render('tool_lp/scale_configuration_page', context)\n .done(function(html) {\n new Dialogue(\n scalename,\n html,\n self.initScaleConfig.bind(self)\n );\n }).fail(notification.exception);\n }).fail(notification.exception);\n };\n\n /**\n * Gets the original scale configuration if it was set.\n *\n * @method retrieveOriginalScaleConfig\n * @return {Object|String} scale configuration or empty string.\n */\n ScaleConfig.prototype.retrieveOriginalScaleConfig = function() {\n var jsonstring = $(this.inputSelector).val();\n if (jsonstring !== '') {\n var scaleconfiguration = $.parseJSON(jsonstring);\n // The first object should contain the scale ID for the configuration.\n var scaledetail = scaleconfiguration.shift();\n // Check that this scale id matches the one from the page before returning the configuration.\n if (scaledetail.scaleid === this.originalscaleid) {\n return scaleconfiguration;\n }\n }\n return '';\n };\n\n /**\n * Initialises the scale configuration dialogue.\n *\n * @method initScaleConfig\n * @param {Dialogue} popup Dialogue object to initialise.\n */\n ScaleConfig.prototype.initScaleConfig = function(popup) {\n this.popup = popup;\n var body = $(popup.getContent());\n if (this.originalscaleid === this.scaleid) {\n // Set up the popup to show the current configuration.\n var currentconfig = this.retrieveOriginalScaleConfig();\n // Set up the form only if there is configuration settings to set.\n if (currentconfig !== '') {\n currentconfig.forEach(function(value) {\n if (value.scaledefault === 1) {\n body.find('[data-field=\"tool_lp_scale_default_' + value.id + '\"]').attr('checked', true);\n }\n if (value.proficient === 1) {\n body.find('[data-field=\"tool_lp_scale_proficient_' + value.id + '\"]').attr('checked', true);\n }\n });\n }\n }\n body.on('click', '[data-action=\"close\"]', function() {\n this.setScaleConfig();\n popup.close();\n }.bind(this));\n body.on('click', '[data-action=\"cancel\"]', function() {\n popup.close();\n });\n };\n\n /**\n * Set the scale configuration back into a JSON string in the hidden element.\n *\n * @method setScaleConfig\n */\n ScaleConfig.prototype.setScaleConfig = function() {\n var body = $(this.popup.getContent());\n // Get the data.\n var data = [{scaleid: this.scaleid}];\n this.scalevalues.forEach(function(value) {\n var scaledefault = 0;\n var proficient = 0;\n if (body.find('[data-field=\"tool_lp_scale_default_' + value.id + '\"]').is(':checked')) {\n scaledefault = 1;\n }\n if (body.find('[data-field=\"tool_lp_scale_proficient_' + value.id + '\"]').is(':checked')) {\n proficient = 1;\n }\n\n if (!scaledefault && !proficient) {\n return;\n }\n\n data.push({\n id: value.id,\n scaledefault: scaledefault,\n proficient: proficient\n });\n });\n var datastring = JSON.stringify(data);\n // Send to the hidden field on the form.\n $(this.inputSelector).val(datastring);\n // Once the configuration has been saved then the original scale ID is set to the current scale ID.\n this.originalscaleid = this.scaleid;\n };\n\n /**\n * Get the scale values for the selected scale.\n *\n * @method getScaleValues\n * @param {Number} scaleid The scale ID of the selected scale.\n * @return {Promise} A deffered object with the scale values.\n */\n ScaleConfig.prototype.getScaleValues = function(scaleid) {\n return ModScaleValues.get_values(scaleid).then(function(values) {\n this.scalevalues = values;\n return values;\n }.bind(this));\n };\n\n /**\n * Triggered when a scale is selected.\n *\n * @name scaleChangeHandler\n * @param {Event} e\n * @function\n */\n ScaleConfig.prototype.scaleChangeHandler = function(e) {\n if ($(e.target).val() <= 0) {\n $(this.triggerSelector).prop('disabled', true);\n } else {\n $(this.triggerSelector).prop('disabled', false);\n }\n\n };\n\n return {\n\n /**\n * Main initialisation.\n *\n * @param {String} selectSelector The select box selector.\n * @param {String} inputSelector The hidden input field selector.\n * @param {String} triggerSelector The trigger selector.\n * @return {ScaleConfig} A new instance of ScaleConfig.\n * @method init\n */\n init: function(selectSelector, inputSelector, triggerSelector) {\n return new ScaleConfig(selectSelector, inputSelector, triggerSelector);\n }\n };\n});\n"],"names":["define","$","notification","templates","ajax","Dialogue","ModScaleValues","ScaleConfig","selectSelector","inputSelector","triggerSelector","originalscaleid","val","on","this","scaleChangeHandler","bind","change","click","showConfig","prototype","scalevalues","scaleid","popup","self","scalename","find","text","getScaleValues","done","context","scales","render","html","initScaleConfig","fail","exception","retrieveOriginalScaleConfig","jsonstring","scaleconfiguration","parseJSON","shift","body","getContent","currentconfig","forEach","value","scaledefault","id","attr","proficient","setScaleConfig","close","data","is","push","datastring","JSON","stringify","get_values","then","values","e","target","prop","init"],"mappings":"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"}
\ No newline at end of file
+{"version":3,"file":"scaleconfig.min.js","sources":["../src/scaleconfig.js"],"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 * Handle opening a dialogue to configure scale data.\n *\n * @module tool_lp/scaleconfig\n * @copyright 2015 Adrian Greeve \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(['jquery', 'core/notification', 'core/templates', 'core/ajax', 'tool_lp/dialogue', 'tool_lp/scalevalues'],\n function($, notification, templates, ajax, Dialogue, ModScaleValues) {\n\n /**\n * Scale config object.\n * @param {String} selectSelector The select box selector.\n * @param {String} inputSelector The hidden input field selector.\n * @param {String} triggerSelector The trigger selector.\n */\n var ScaleConfig = function(selectSelector, inputSelector, triggerSelector) {\n this.selectSelector = selectSelector;\n this.inputSelector = inputSelector;\n this.triggerSelector = triggerSelector;\n\n // Get the current scale ID.\n this.originalscaleid = $(selectSelector).val();\n $(selectSelector).on('change', this.scaleChangeHandler.bind(this)).change();\n $(triggerSelector).click(this.showConfig.bind(this));\n };\n\n /** @var {String} The select box selector. */\n ScaleConfig.prototype.selectSelector = null;\n /** @var {String} The hidden field selector. */\n ScaleConfig.prototype.inputSelector = null;\n /** @var {String} The trigger selector. */\n ScaleConfig.prototype.triggerSelector = null;\n /** @var {Array} scalevalues ID and name of the scales. */\n ScaleConfig.prototype.scalevalues = null;\n /** @var {Number) originalscaleid Original scale ID when the page loads. */\n ScaleConfig.prototype.originalscaleid = 0;\n /** @var {Number} scaleid Current scale ID. */\n ScaleConfig.prototype.scaleid = 0;\n /** @var {Dialogue} Reference to the popup. */\n ScaleConfig.prototype.popup = null;\n\n /**\n * Displays the scale configuration dialogue.\n *\n * @method showConfig\n */\n ScaleConfig.prototype.showConfig = function() {\n var self = this;\n\n this.scaleid = $(this.selectSelector).val();\n if (this.scaleid <= 0) {\n // This should not happen.\n return;\n }\n\n var scalename = $(this.selectSelector).find(\"option:selected\").text();\n this.getScaleValues(this.scaleid).done(function() {\n\n var context = {\n scalename: scalename,\n scales: self.scalevalues\n };\n\n // Dish up the form.\n templates.render('tool_lp/scale_configuration_page', context)\n .done(function(html) {\n new Dialogue(\n scalename,\n html,\n self.initScaleConfig.bind(self)\n );\n }).fail(notification.exception);\n }).fail(notification.exception);\n };\n\n /**\n * Gets the original scale configuration if it was set.\n *\n * @method retrieveOriginalScaleConfig\n * @return {Object|String} scale configuration or empty string.\n */\n ScaleConfig.prototype.retrieveOriginalScaleConfig = function() {\n var jsonstring = $(this.inputSelector).val();\n if (jsonstring !== '') {\n var scaleconfiguration = $.parseJSON(jsonstring);\n // The first object should contain the scale ID for the configuration.\n var scaledetail = scaleconfiguration.shift();\n // Check that this scale id matches the one from the page before returning the configuration.\n if (scaledetail.scaleid === this.originalscaleid) {\n return scaleconfiguration;\n }\n }\n return '';\n };\n\n /**\n * Initialises the scale configuration dialogue.\n *\n * @method initScaleConfig\n * @param {Dialogue} popup Dialogue object to initialise.\n */\n ScaleConfig.prototype.initScaleConfig = function(popup) {\n this.popup = popup;\n var body = $(popup.getContent());\n if (this.originalscaleid === this.scaleid) {\n // Set up the popup to show the current configuration.\n var currentconfig = this.retrieveOriginalScaleConfig();\n // Set up the form only if there is configuration settings to set.\n if (currentconfig !== '') {\n currentconfig.forEach(function(value) {\n if (value.scaledefault === 1) {\n body.find('[data-field=\"tool_lp_scale_default_' + value.id + '\"]').attr('checked', true);\n }\n if (value.proficient === 1) {\n body.find('[data-field=\"tool_lp_scale_proficient_' + value.id + '\"]').attr('checked', true);\n }\n });\n }\n }\n body.on('click', '[data-action=\"close\"]', function() {\n this.setScaleConfig();\n popup.close();\n }.bind(this));\n body.on('click', '[data-action=\"cancel\"]', function() {\n popup.close();\n });\n };\n\n /**\n * Set the scale configuration back into a JSON string in the hidden element.\n *\n * @method setScaleConfig\n */\n ScaleConfig.prototype.setScaleConfig = function() {\n var body = $(this.popup.getContent());\n // Get the data.\n var data = [{scaleid: this.scaleid}];\n this.scalevalues.forEach(function(value) {\n var scaledefault = 0;\n var proficient = 0;\n if (body.find('[data-field=\"tool_lp_scale_default_' + value.id + '\"]').is(':checked')) {\n scaledefault = 1;\n }\n if (body.find('[data-field=\"tool_lp_scale_proficient_' + value.id + '\"]').is(':checked')) {\n proficient = 1;\n }\n\n if (!scaledefault && !proficient) {\n return;\n }\n\n data.push({\n id: value.id,\n scaledefault: scaledefault,\n proficient: proficient\n });\n });\n var datastring = JSON.stringify(data);\n // Send to the hidden field on the form.\n $(this.inputSelector).val(datastring);\n // Once the configuration has been saved then the original scale ID is set to the current scale ID.\n this.originalscaleid = this.scaleid;\n };\n\n /**\n * Get the scale values for the selected scale.\n *\n * @method getScaleValues\n * @param {Number} scaleid The scale ID of the selected scale.\n * @return {Promise} A deffered object with the scale values.\n */\n ScaleConfig.prototype.getScaleValues = function(scaleid) {\n return ModScaleValues.get_values(scaleid).then(function(values) {\n this.scalevalues = values;\n return values;\n }.bind(this));\n };\n\n /**\n * Triggered when a scale is selected.\n *\n * @name scaleChangeHandler\n * @param {Event} e\n * @function\n */\n ScaleConfig.prototype.scaleChangeHandler = function(e) {\n if ($(e.target).val() <= 0) {\n $(this.triggerSelector).prop('disabled', true);\n } else {\n $(this.triggerSelector).prop('disabled', false);\n }\n\n };\n\n return {\n\n /**\n * Main initialisation.\n *\n * @param {String} selectSelector The select box selector.\n * @param {String} inputSelector The hidden input field selector.\n * @param {String} triggerSelector The trigger selector.\n * @return {ScaleConfig} A new instance of ScaleConfig.\n * @method init\n */\n init: function(selectSelector, inputSelector, triggerSelector) {\n return new ScaleConfig(selectSelector, inputSelector, triggerSelector);\n }\n };\n});\n"],"names":["define","$","notification","templates","ajax","Dialogue","ModScaleValues","ScaleConfig","selectSelector","inputSelector","triggerSelector","this","originalscaleid","val","on","scaleChangeHandler","bind","change","click","showConfig","prototype","scalevalues","scaleid","popup","self","scalename","find","text","getScaleValues","done","context","scales","render","html","initScaleConfig","fail","exception","retrieveOriginalScaleConfig","jsonstring","scaleconfiguration","parseJSON","shift","body","getContent","currentconfig","forEach","value","scaledefault","id","attr","proficient","setScaleConfig","close","data","is","push","datastring","JSON","stringify","get_values","then","values","e","target","prop","init"],"mappings":"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"}
\ No newline at end of file
diff --git a/admin/tool/lp/amd/build/scalevalues.min.js.map b/admin/tool/lp/amd/build/scalevalues.min.js.map
index f15ca392296..a4dfa805766 100644
--- a/admin/tool/lp/amd/build/scalevalues.min.js.map
+++ b/admin/tool/lp/amd/build/scalevalues.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"scalevalues.min.js","sources":["../src/scalevalues.js"],"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 * Module to get the scale values.\n *\n * @module tool_lp/scalevalues\n * @copyright 2016 Serge Gauthier\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(['jquery', 'core/ajax'], function($, ajax) {\n var localCache = [];\n\n return {\n\n /**\n * Return a promise object that will be resolved into a string eventually (maybe immediately).\n *\n * @method get_values\n * @param {Number} scaleid The scale id\n * @return [] {Promise}\n */\n // eslint-disable-next-line camelcase\n get_values: function(scaleid) {\n\n var deferred = $.Deferred();\n\n if (typeof localCache[scaleid] === 'undefined') {\n ajax.call([{\n methodname: 'core_competency_get_scale_values',\n args: {scaleid: scaleid},\n done: function(scaleinfo) {\n localCache[scaleid] = scaleinfo;\n deferred.resolve(scaleinfo);\n },\n fail: (deferred.reject)\n }]);\n } else {\n deferred.resolve(localCache[scaleid]);\n }\n\n return deferred.promise();\n }\n };\n});\n"],"names":["define","$","ajax","localCache","get_values","scaleid","deferred","Deferred","call","methodname","args","done","scaleinfo","resolve","fail","reject","promise"],"mappings":";;;;;;;AAsBAA,6BAAO,CAAC,SAAU,cAAc,SAASC,EAAGC,UACpCC,WAAa,SAEV,CAUHC,WAAY,SAASC,aAEbC,SAAWL,EAAEM,uBAEkB,IAAxBJ,WAAWE,SAClBH,KAAKM,KAAK,CAAC,CACPC,WAAY,mCACZC,KAAM,CAACL,QAASA,SAChBM,KAAM,SAASC,WACXT,WAAWE,SAAWO,UACtBN,SAASO,QAAQD,YAErBE,KAAOR,SAASS,UAGpBT,SAASO,QAAQV,WAAWE,UAGzBC,SAASU"}
\ No newline at end of file
+{"version":3,"file":"scalevalues.min.js","sources":["../src/scalevalues.js"],"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 * Module to get the scale values.\n *\n * @module tool_lp/scalevalues\n * @copyright 2016 Serge Gauthier\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(['jquery', 'core/ajax'], function($, ajax) {\n var localCache = [];\n\n return {\n\n /**\n * Return a promise object that will be resolved into a string eventually (maybe immediately).\n *\n * @method get_values\n * @param {Number} scaleid The scale id\n * @return [] {Promise}\n */\n // eslint-disable-next-line camelcase\n get_values: function(scaleid) {\n\n var deferred = $.Deferred();\n\n if (typeof localCache[scaleid] === 'undefined') {\n ajax.call([{\n methodname: 'core_competency_get_scale_values',\n args: {scaleid: scaleid},\n done: function(scaleinfo) {\n localCache[scaleid] = scaleinfo;\n deferred.resolve(scaleinfo);\n },\n fail: (deferred.reject)\n }]);\n } else {\n deferred.resolve(localCache[scaleid]);\n }\n\n return deferred.promise();\n }\n };\n});\n"],"names":["define","$","ajax","localCache","get_values","scaleid","deferred","Deferred","call","methodname","args","done","scaleinfo","resolve","fail","reject","promise"],"mappings":";;;;;;;AAsBAA,OAAM,sBAAC,CAAC,SAAU,cAAc,SAASC,EAAGC,MACxC,IAAIC,WAAa,GAEjB,MAAO,CAUHC,WAAY,SAASC,SAEjB,IAAIC,SAAWL,EAAEM,WAgBjB,YAdmC,IAAxBJ,WAAWE,SAClBH,KAAKM,KAAK,CAAC,CACPC,WAAY,mCACZC,KAAM,CAACL,QAASA,SAChBM,KAAM,SAASC,WACXT,WAAWE,SAAWO,UACtBN,SAASO,QAAQD,UACpB,EACDE,KAAOR,SAASS,UAGpBT,SAASO,QAAQV,WAAWE,UAGzBC,SAASU,SACpB,EAER"}
\ No newline at end of file
diff --git a/admin/tool/lp/amd/build/templateactions.min.js.map b/admin/tool/lp/amd/build/templateactions.min.js.map
index 6c72d2794ca..6924e9b3e83 100644
--- a/admin/tool/lp/amd/build/templateactions.min.js.map
+++ b/admin/tool/lp/amd/build/templateactions.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"templateactions.min.js","sources":["../src/templateactions.js"],"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 * Handle actions on learning plan templates via ajax.\n *\n * @module tool_lp/templateactions\n * @copyright 2015 Damyon Wiese \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(['jquery', 'core/templates', 'core/ajax', 'core/notification', 'core/str', 'tool_lp/actionselector'],\n function($, templates, ajax, notification, str, Actionselector) {\n // Private variables and functions.\n\n /** @var {Number} pagecontextid The id of the context */\n var pagecontextid = 0;\n\n /** @var {Number} templateid The id of the template */\n var templateid = 0;\n\n /** @var {Boolean} Action to apply to plans when deleting a template */\n var deleteplans = true;\n\n /**\n * Callback to replace the dom element with the rendered template.\n *\n * @method updatePage\n * @param {String} newhtml The new html to insert.\n * @param {String} newjs The new js to run.\n */\n var updatePage = function(newhtml, newjs) {\n $('[data-region=\"managetemplates\"]').replaceWith(newhtml);\n templates.runTemplateJS(newjs);\n };\n\n /**\n * Callback to render the page template again and update the page.\n *\n * @method reloadList\n * @param {Object} context The context for the template.\n */\n var reloadList = function(context) {\n templates.render('tool_lp/manage_templates_page', context)\n .done(updatePage)\n .fail(notification.exception);\n };\n\n /**\n * Delete a template and reload the page.\n * @method doDelete\n */\n var doDelete = function() {\n\n // We are chaining ajax requests here.\n var requests = ajax.call([{\n methodname: 'core_competency_delete_template',\n args: {id: templateid,\n deleteplans: deleteplans}\n }, {\n methodname: 'tool_lp_data_for_templates_manage_page',\n args: {\n pagecontext: {\n contextid: pagecontextid\n }\n }\n }]);\n requests[1].done(reloadList).fail(notification.exception);\n };\n\n /**\n * Duplicate a template and reload the page.\n * @method doDuplicate\n * @param {Event} e\n */\n var doDuplicate = function(e) {\n e.preventDefault();\n\n templateid = $(this).attr('data-templateid');\n\n // We are chaining ajax requests here.\n var requests = ajax.call([{\n methodname: 'core_competency_duplicate_template',\n args: {id: templateid}\n }, {\n methodname: 'tool_lp_data_for_templates_manage_page',\n args: {\n pagecontext: {\n contextid: pagecontextid\n }\n }\n }]);\n requests[1].done(reloadList).fail(notification.exception);\n };\n\n /**\n * Handler for \"Delete learning plan template\" actions.\n * @method confirmDelete\n * @param {Event} e\n */\n var confirmDelete = function(e) {\n e.preventDefault();\n\n var id = $(this).attr('data-templateid');\n templateid = id;\n deleteplans = true;\n\n var requests = ajax.call([{\n methodname: 'core_competency_read_template',\n args: {id: templateid}\n }, {\n methodname: 'core_competency_template_has_related_data',\n args: {id: templateid}\n }]);\n\n requests[0].done(function(template) {\n requests[1].done(function(templatehasrelateddata) {\n if (templatehasrelateddata) {\n str.get_strings([\n {key: 'deletetemplate', component: 'tool_lp', param: template.shortname},\n {key: 'deletetemplatewithplans', component: 'tool_lp'},\n {key: 'deleteplans', component: 'tool_lp'},\n {key: 'unlinkplanstemplate', component: 'tool_lp'},\n {key: 'confirm', component: 'moodle'},\n {key: 'cancel', component: 'moodle'}\n ]).done(function(strings) {\n var actions = [{'text': strings[2], 'value': 'delete'},\n {'text': strings[3], 'value': 'unlink'}];\n var actionselector = new Actionselector(\n strings[0], // Title.\n strings[1], // Message\n actions, // Radio button options.\n strings[4], // Confirm.\n strings[5]); // Cancel.\n actionselector.display();\n actionselector.on('save', function(e, data) {\n if (data.action != 'delete') {\n deleteplans = false;\n }\n doDelete();\n });\n }).fail(notification.exception);\n } else {\n str.get_strings([\n {key: 'confirm', component: 'moodle'},\n {key: 'deletetemplate', component: 'tool_lp', param: template.shortname},\n {key: 'delete', component: 'moodle'},\n {key: 'cancel', component: 'moodle'}\n ]).done(function(strings) {\n notification.confirm(\n strings[0], // Confirm.\n strings[1], // Delete learning plan template X?\n strings[2], // Delete.\n strings[3], // Cancel.\n doDelete\n );\n }).fail(notification.exception);\n }\n }).fail(notification.exception);\n }).fail(notification.exception);\n\n };\n\n return /** @alias module:tool_lp/templateactions */ {\n // Public variables and functions.\n /**\n * Expose the event handler for the delete.\n * @method deleteHandler\n * @param {Event} e\n */\n deleteHandler: confirmDelete,\n\n /**\n * Expose the event handler for the duplicate.\n * @method duplicateHandler\n * @param {Event} e\n */\n duplicateHandler: doDuplicate,\n\n /**\n * Initialise the module.\n * @method init\n * @param {Number} contextid The context id of the page.\n */\n init: function(contextid) {\n pagecontextid = contextid;\n }\n };\n});\n"],"names":["define","$","templates","ajax","notification","str","Actionselector","pagecontextid","templateid","deleteplans","updatePage","newhtml","newjs","replaceWith","runTemplateJS","reloadList","context","render","done","fail","exception","doDelete","call","methodname","args","id","pagecontext","contextid","deleteHandler","e","preventDefault","this","attr","requests","template","templatehasrelateddata","get_strings","key","component","param","shortname","strings","actions","actionselector","display","on","data","action","confirm","duplicateHandler","init"],"mappings":";;;;;;;AAsBAA,iCAAO,CAAC,SAAU,iBAAkB,YAAa,oBAAqB,WAAY,2BAC3E,SAASC,EAAGC,UAAWC,KAAMC,aAAcC,IAAKC,oBAI/CC,cAAgB,EAGhBC,WAAa,EAGbC,aAAc,EASdC,WAAa,SAASC,QAASC,OAC/BX,EAAE,mCAAmCY,YAAYF,SACjDT,UAAUY,cAAcF,QASxBG,WAAa,SAASC,SACtBd,UAAUe,OAAO,gCAAiCD,SAC7CE,KAAKR,YACLS,KAAKf,aAAagB,YAOvBC,SAAW,WAGIlB,KAAKmB,KAAK,CAAC,CACtBC,WAAY,kCACZC,KAAM,CAACC,GAAIjB,WACHC,YAAaA,cACtB,CACCc,WAAY,yCACZC,KAAM,CACFE,YAAa,CACTC,UAAWpB,mBAId,GAAGW,KAAKH,YAAYI,KAAKf,aAAagB,kBAgGC,CAOhDQ,cAtEgB,SAASC,GACzBA,EAAEC,qBAEEL,GAAKxB,EAAE8B,MAAMC,KAAK,mBACtBxB,WAAaiB,GACbhB,aAAc,MAEVwB,SAAW9B,KAAKmB,KAAK,CAAC,CACtBC,WAAY,gCACZC,KAAM,CAACC,GAAIjB,aACZ,CACCe,WAAY,4CACZC,KAAM,CAACC,GAAIjB,eAGfyB,SAAS,GAAGf,MAAK,SAASgB,UACtBD,SAAS,GAAGf,MAAK,SAASiB,wBAClBA,uBACA9B,IAAI+B,YAAY,CACZ,CAACC,IAAK,iBAAkBC,UAAW,UAAWC,MAAOL,SAASM,WAC9D,CAACH,IAAK,0BAA2BC,UAAW,WAC5C,CAACD,IAAK,cAAeC,UAAW,WAChC,CAACD,IAAK,sBAAuBC,UAAW,WACxC,CAACD,IAAK,UAAWC,UAAW,UAC5B,CAACD,IAAK,SAAUC,UAAW,YAC5BpB,MAAK,SAASuB,aACTC,QAAU,CAAC,MAASD,QAAQ,SAAa,UAC9B,MAASA,QAAQ,SAAa,WACzCE,eAAiB,IAAIrC,eACjBmC,QAAQ,GACRA,QAAQ,GACRC,QACAD,QAAQ,GACRA,QAAQ,IAChBE,eAAeC,UACfD,eAAeE,GAAG,QAAQ,SAAShB,EAAGiB,MACf,UAAfA,KAAKC,SACLtC,aAAc,GAElBY,iBAELF,KAAKf,aAAagB,WAErBf,IAAI+B,YAAY,CACZ,CAACC,IAAK,UAAWC,UAAW,UAC5B,CAACD,IAAK,iBAAkBC,UAAW,UAAWC,MAAOL,SAASM,WAC9D,CAACH,IAAK,SAAUC,UAAW,UAC3B,CAACD,IAAK,SAAUC,UAAW,YAC5BpB,MAAK,SAASuB,SACbrC,aAAa4C,QACbP,QAAQ,GACRA,QAAQ,GACRA,QAAQ,GACRA,QAAQ,GACRpB,aAEDF,KAAKf,aAAagB,cAE1BD,KAAKf,aAAagB,cACtBD,KAAKf,aAAagB,YAkBrB6B,iBAtGc,SAASpB,GACvBA,EAAEC,iBAEFtB,WAAaP,EAAE8B,MAAMC,KAAK,mBAGX7B,KAAKmB,KAAK,CAAC,CACtBC,WAAY,qCACZC,KAAM,CAACC,GAAIjB,aACZ,CACCe,WAAY,yCACZC,KAAM,CACFE,YAAa,CACTC,UAAWpB,mBAId,GAAGW,KAAKH,YAAYI,KAAKf,aAAagB,YA4F/C8B,KAAM,SAASvB,WACXpB,cAAgBoB"}
\ No newline at end of file
+{"version":3,"file":"templateactions.min.js","sources":["../src/templateactions.js"],"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 * Handle actions on learning plan templates via ajax.\n *\n * @module tool_lp/templateactions\n * @copyright 2015 Damyon Wiese \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(['jquery', 'core/templates', 'core/ajax', 'core/notification', 'core/str', 'tool_lp/actionselector'],\n function($, templates, ajax, notification, str, Actionselector) {\n // Private variables and functions.\n\n /** @var {Number} pagecontextid The id of the context */\n var pagecontextid = 0;\n\n /** @var {Number} templateid The id of the template */\n var templateid = 0;\n\n /** @var {Boolean} Action to apply to plans when deleting a template */\n var deleteplans = true;\n\n /**\n * Callback to replace the dom element with the rendered template.\n *\n * @method updatePage\n * @param {String} newhtml The new html to insert.\n * @param {String} newjs The new js to run.\n */\n var updatePage = function(newhtml, newjs) {\n $('[data-region=\"managetemplates\"]').replaceWith(newhtml);\n templates.runTemplateJS(newjs);\n };\n\n /**\n * Callback to render the page template again and update the page.\n *\n * @method reloadList\n * @param {Object} context The context for the template.\n */\n var reloadList = function(context) {\n templates.render('tool_lp/manage_templates_page', context)\n .done(updatePage)\n .fail(notification.exception);\n };\n\n /**\n * Delete a template and reload the page.\n * @method doDelete\n */\n var doDelete = function() {\n\n // We are chaining ajax requests here.\n var requests = ajax.call([{\n methodname: 'core_competency_delete_template',\n args: {id: templateid,\n deleteplans: deleteplans}\n }, {\n methodname: 'tool_lp_data_for_templates_manage_page',\n args: {\n pagecontext: {\n contextid: pagecontextid\n }\n }\n }]);\n requests[1].done(reloadList).fail(notification.exception);\n };\n\n /**\n * Duplicate a template and reload the page.\n * @method doDuplicate\n * @param {Event} e\n */\n var doDuplicate = function(e) {\n e.preventDefault();\n\n templateid = $(this).attr('data-templateid');\n\n // We are chaining ajax requests here.\n var requests = ajax.call([{\n methodname: 'core_competency_duplicate_template',\n args: {id: templateid}\n }, {\n methodname: 'tool_lp_data_for_templates_manage_page',\n args: {\n pagecontext: {\n contextid: pagecontextid\n }\n }\n }]);\n requests[1].done(reloadList).fail(notification.exception);\n };\n\n /**\n * Handler for \"Delete learning plan template\" actions.\n * @method confirmDelete\n * @param {Event} e\n */\n var confirmDelete = function(e) {\n e.preventDefault();\n\n var id = $(this).attr('data-templateid');\n templateid = id;\n deleteplans = true;\n\n var requests = ajax.call([{\n methodname: 'core_competency_read_template',\n args: {id: templateid}\n }, {\n methodname: 'core_competency_template_has_related_data',\n args: {id: templateid}\n }]);\n\n requests[0].done(function(template) {\n requests[1].done(function(templatehasrelateddata) {\n if (templatehasrelateddata) {\n str.get_strings([\n {key: 'deletetemplate', component: 'tool_lp', param: template.shortname},\n {key: 'deletetemplatewithplans', component: 'tool_lp'},\n {key: 'deleteplans', component: 'tool_lp'},\n {key: 'unlinkplanstemplate', component: 'tool_lp'},\n {key: 'confirm', component: 'moodle'},\n {key: 'cancel', component: 'moodle'}\n ]).done(function(strings) {\n var actions = [{'text': strings[2], 'value': 'delete'},\n {'text': strings[3], 'value': 'unlink'}];\n var actionselector = new Actionselector(\n strings[0], // Title.\n strings[1], // Message\n actions, // Radio button options.\n strings[4], // Confirm.\n strings[5]); // Cancel.\n actionselector.display();\n actionselector.on('save', function(e, data) {\n if (data.action != 'delete') {\n deleteplans = false;\n }\n doDelete();\n });\n }).fail(notification.exception);\n } else {\n str.get_strings([\n {key: 'confirm', component: 'moodle'},\n {key: 'deletetemplate', component: 'tool_lp', param: template.shortname},\n {key: 'delete', component: 'moodle'},\n {key: 'cancel', component: 'moodle'}\n ]).done(function(strings) {\n notification.confirm(\n strings[0], // Confirm.\n strings[1], // Delete learning plan template X?\n strings[2], // Delete.\n strings[3], // Cancel.\n doDelete\n );\n }).fail(notification.exception);\n }\n }).fail(notification.exception);\n }).fail(notification.exception);\n\n };\n\n return /** @alias module:tool_lp/templateactions */ {\n // Public variables and functions.\n /**\n * Expose the event handler for the delete.\n * @method deleteHandler\n * @param {Event} e\n */\n deleteHandler: confirmDelete,\n\n /**\n * Expose the event handler for the duplicate.\n * @method duplicateHandler\n * @param {Event} e\n */\n duplicateHandler: doDuplicate,\n\n /**\n * Initialise the module.\n * @method init\n * @param {Number} contextid The context id of the page.\n */\n init: function(contextid) {\n pagecontextid = contextid;\n }\n };\n});\n"],"names":["define","$","templates","ajax","notification","str","Actionselector","pagecontextid","templateid","deleteplans","updatePage","newhtml","newjs","replaceWith","runTemplateJS","reloadList","context","render","done","fail","exception","doDelete","call","methodname","args","id","pagecontext","contextid","deleteHandler","e","preventDefault","this","attr","requests","template","templatehasrelateddata","get_strings","key","component","param","shortname","strings","actions","text","value","actionselector","display","on","data","action","confirm","duplicateHandler","init"],"mappings":";;;;;;;AAsBAA,OAAM,0BAAC,CAAC,SAAU,iBAAkB,YAAa,oBAAqB,WAAY,2BAC3E,SAASC,EAAGC,UAAWC,KAAMC,aAAcC,IAAKC,gBAInD,IAAIC,cAAgB,EAGhBC,WAAa,EAGbC,aAAc,EASdC,WAAa,SAASC,QAASC,OAC/BX,EAAE,mCAAmCY,YAAYF,SACjDT,UAAUY,cAAcF,QASxBG,WAAa,SAASC,SACtBd,UAAUe,OAAO,gCAAiCD,SAC7CE,KAAKR,YACLS,KAAKf,aAAagB,YAOvBC,SAAW,WAGIlB,KAAKmB,KAAK,CAAC,CACtBC,WAAY,kCACZC,KAAM,CAACC,GAAIjB,WACHC,YAAaA,cACtB,CACCc,WAAY,yCACZC,KAAM,CACFE,YAAa,CACTC,UAAWpB,mBAId,GAAGW,KAAKH,YAAYI,KAAKf,aAAagB,YAgGnD,MAAoD,CAOhDQ,cAtEgB,SAASC,GACzBA,EAAEC,iBAEF,IAAIL,GAAKxB,EAAE8B,MAAMC,KAAK,mBACtBxB,WAAaiB,GACbhB,aAAc,EAEd,IAAIwB,SAAW9B,KAAKmB,KAAK,CAAC,CACtBC,WAAY,gCACZC,KAAM,CAACC,GAAIjB,aACZ,CACCe,WAAY,4CACZC,KAAM,CAACC,GAAIjB,eAGfyB,SAAS,GAAGf,MAAK,SAASgB,UACtBD,SAAS,GAAGf,MAAK,SAASiB,wBAClBA,uBACA9B,IAAI+B,YAAY,CACZ,CAACC,IAAK,iBAAkBC,UAAW,UAAWC,MAAOL,SAASM,WAC9D,CAACH,IAAK,0BAA2BC,UAAW,WAC5C,CAACD,IAAK,cAAeC,UAAW,WAChC,CAACD,IAAK,sBAAuBC,UAAW,WACxC,CAACD,IAAK,UAAWC,UAAW,UAC5B,CAACD,IAAK,SAAUC,UAAW,YAC5BpB,MAAK,SAASuB,SACb,IAAIC,QAAU,CAAC,CAACC,KAAQF,QAAQ,GAAIG,MAAS,UAC9B,CAACD,KAAQF,QAAQ,GAAIG,MAAS,WACzCC,eAAiB,IAAIvC,eACjBmC,QAAQ,GACRA,QAAQ,GACRC,QACAD,QAAQ,GACRA,QAAQ,IAChBI,eAAeC,UACfD,eAAeE,GAAG,QAAQ,SAASlB,EAAGmB,MACf,UAAfA,KAAKC,SACLxC,aAAc,GAElBY,UACJ,GACH,IAAEF,KAAKf,aAAagB,WAErBf,IAAI+B,YAAY,CACZ,CAACC,IAAK,UAAWC,UAAW,UAC5B,CAACD,IAAK,iBAAkBC,UAAW,UAAWC,MAAOL,SAASM,WAC9D,CAACH,IAAK,SAAUC,UAAW,UAC3B,CAACD,IAAK,SAAUC,UAAW,YAC5BpB,MAAK,SAASuB,SACbrC,aAAa8C,QACbT,QAAQ,GACRA,QAAQ,GACRA,QAAQ,GACRA,QAAQ,GACRpB,SAEH,IAAEF,KAAKf,aAAagB,UAE5B,IAAED,KAAKf,aAAagB,UACxB,IAAED,KAAKf,aAAagB,YAkBrB+B,iBAtGc,SAAStB,GACvBA,EAAEC,iBAEFtB,WAAaP,EAAE8B,MAAMC,KAAK,mBAGX7B,KAAKmB,KAAK,CAAC,CACtBC,WAAY,qCACZC,KAAM,CAACC,GAAIjB,aACZ,CACCe,WAAY,yCACZC,KAAM,CACFE,YAAa,CACTC,UAAWpB,mBAId,GAAGW,KAAKH,YAAYI,KAAKf,aAAagB,YA4F/CgC,KAAM,SAASzB,WACXpB,cAAgBoB,SACpB,EAER"}
\ No newline at end of file
diff --git a/admin/tool/lp/amd/build/tree.min.js.map b/admin/tool/lp/amd/build/tree.min.js.map
index d2b98e75cb3..b57f62fadab 100644
--- a/admin/tool/lp/amd/build/tree.min.js.map
+++ b/admin/tool/lp/amd/build/tree.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"tree.min.js","sources":["../src/tree.js"],"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 * Implement an accessible aria tree widget, from a nested unordered list.\n * Based on http://oaa-accessibility.org/example/41/\n *\n * To respond to selection changed events - use tree.on(\"selectionchanged\", handler).\n * The handler will receive an array of nodes, which are the list items that are currently\n * selected. (Or a single node if multiselect is disabled).\n *\n * @module tool_lp/tree\n * @copyright 2015 Damyon Wiese \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(['jquery', 'core/url', 'core/log'], function($, url, log) {\n // Private variables and functions.\n /** @var {String} expandedImage The html for an expanded tree node twistie. */\n var expandedImage = $('');\n /** @var {String} collapsedImage The html for a collapsed tree node twistie. */\n var collapsedImage = $('');\n\n /**\n * Constructor\n *\n * @param {String} selector\n * @param {Boolean} multiSelect\n */\n var Tree = function(selector, multiSelect) {\n this.treeRoot = $(selector);\n this.multiSelect = (typeof multiSelect === 'undefined' || multiSelect === true);\n\n this.items = this.treeRoot.find('li');\n this.expandAll = this.items.length < 20;\n this.parents = this.treeRoot.find('li:has(ul)');\n\n if (multiSelect) {\n this.treeRoot.attr('aria-multiselectable', 'true');\n }\n\n this.items.attr('aria-selected', 'false');\n\n this.visibleItems = null;\n this.activeItem = null;\n this.lastActiveItem = null;\n\n this.keys = {\n tab: 9,\n enter: 13,\n space: 32,\n pageup: 33,\n pagedown: 34,\n end: 35,\n home: 36,\n left: 37,\n up: 38,\n right: 39,\n down: 40,\n eight: 56,\n asterisk: 106\n };\n\n this.init();\n\n this.bindEventHandlers();\n };\n // Public variables and functions.\n\n /**\n * Init this tree\n * @method init\n */\n Tree.prototype.init = function() {\n this.parents.attr('aria-expanded', 'true');\n this.parents.prepend(expandedImage.clone());\n\n this.items.attr('role', 'tree-item');\n this.items.attr('tabindex', '-1');\n this.parents.attr('role', 'group');\n this.treeRoot.attr('role', 'tree');\n\n this.visibleItems = this.treeRoot.find('li');\n\n var thisObj = this;\n if (!this.expandAll) {\n this.parents.each(function() {\n thisObj.collapseGroup($(this));\n });\n this.expandGroup(this.parents.first());\n }\n };\n\n /**\n * Expand a collapsed group.\n *\n * @method expandGroup\n * @param {Object} item is the jquery id of the parent item of the group\n */\n Tree.prototype.expandGroup = function(item) {\n // Find the first child ul node.\n var group = item.children('ul');\n\n // Expand the group.\n group.show().attr('aria-hidden', 'false');\n\n item.attr('aria-expanded', 'true');\n\n item.children('img').attr('src', expandedImage.attr('src'));\n\n // Update the list of visible items.\n this.visibleItems = this.treeRoot.find('li:visible');\n };\n\n /**\n * Collapse an expanded group.\n *\n * @method collapseGroup\n * @param {Object} item is the jquery id of the parent item of the group\n */\n Tree.prototype.collapseGroup = function(item) {\n var group = item.children('ul');\n\n // Collapse the group.\n group.hide().attr('aria-hidden', 'true');\n\n item.attr('aria-expanded', 'false');\n\n item.children('img').attr('src', collapsedImage.attr('src'));\n\n // Update the list of visible items.\n this.visibleItems = this.treeRoot.find('li:visible');\n };\n\n /**\n * Expand or collapse a group.\n *\n * @method toggleGroup\n * @param {Object} item is the jquery id of the parent item of the group\n */\n Tree.prototype.toggleGroup = function(item) {\n if (item.attr('aria-expanded') == 'true') {\n this.collapseGroup(item);\n } else {\n this.expandGroup(item);\n }\n };\n\n /**\n * Whenever the currently selected node has changed, trigger an event using this function.\n *\n * @method triggerChange\n */\n Tree.prototype.triggerChange = function() {\n var allSelected = this.items.filter('[aria-selected=true]');\n if (!this.multiSelect) {\n allSelected = allSelected.first();\n }\n this.treeRoot.trigger('selectionchanged', {selected: allSelected});\n };\n\n /**\n * Select all the items between the last focused item and this currently focused item.\n *\n * @method multiSelectItem\n * @param {Object} item is the jquery id of the newly selected item.\n */\n Tree.prototype.multiSelectItem = function(item) {\n if (!this.multiSelect) {\n this.items.attr('aria-selected', 'false');\n } else if (this.lastActiveItem !== null) {\n var lastIndex = this.visibleItems.index(this.lastActiveItem);\n var currentIndex = this.visibleItems.index(this.activeItem);\n var oneItem = null;\n\n while (lastIndex < currentIndex) {\n oneItem = $(this.visibleItems.get(lastIndex));\n oneItem.attr('aria-selected', 'true');\n lastIndex++;\n }\n while (lastIndex > currentIndex) {\n oneItem = $(this.visibleItems.get(lastIndex));\n oneItem.attr('aria-selected', 'true');\n lastIndex--;\n }\n }\n\n item.attr('aria-selected', 'true');\n this.triggerChange();\n };\n\n /**\n * Select a single item. Make sure all the parents are expanded. De-select all other items.\n *\n * @method selectItem\n * @param {Object} item is the jquery id of the newly selected item.\n */\n Tree.prototype.selectItem = function(item) {\n // Expand all nodes up the tree.\n var walk = item.parent();\n while (walk.attr('role') != 'tree') {\n walk = walk.parent();\n if (walk.attr('aria-expanded') == 'false') {\n this.expandGroup(walk);\n }\n walk = walk.parent();\n }\n this.items.attr('aria-selected', 'false');\n item.attr('aria-selected', 'true');\n this.triggerChange();\n };\n\n /**\n * Toggle the selected state for an item back and forth.\n *\n * @method toggleItem\n * @param {Object} item is the jquery id of the item to toggle.\n */\n Tree.prototype.toggleItem = function(item) {\n if (!this.multiSelect) {\n this.selectItem(item);\n return;\n }\n\n var current = item.attr('aria-selected');\n if (current === 'true') {\n current = 'false';\n } else {\n current = 'true';\n }\n item.attr('aria-selected', current);\n this.triggerChange();\n };\n\n /**\n * Set the focus to this item.\n *\n * @method updateFocus\n * @param {Object} item is the jquery id of the parent item of the group\n */\n Tree.prototype.updateFocus = function(item) {\n this.lastActiveItem = this.activeItem;\n this.activeItem = item;\n // Expand all nodes up the tree.\n var walk = item.parent();\n while (walk.attr('role') != 'tree') {\n walk = walk.parent();\n if (walk.attr('aria-expanded') == 'false') {\n this.expandGroup(walk);\n }\n walk = walk.parent();\n }\n this.items.attr('tabindex', '-1');\n item.attr('tabindex', 0);\n };\n\n /**\n * Handle a key down event - ie navigate the tree.\n *\n * @method handleKeyDown\n * @param {Object} item is the jquery id of the parent item of the group\n * @param {Event} e The event.\n * @return {Boolean}\n */\n // This function should be simplified. In the meantime..\n // eslint-disable-next-line complexity\n Tree.prototype.handleKeyDown = function(item, e) {\n var currentIndex = this.visibleItems.index(item);\n var newItem = null;\n var hasKeyModifier = e.shiftKey || e.ctrlKey || e.metaKey || e.altKey;\n var thisObj = this;\n\n switch (e.keyCode) {\n case this.keys.home: {\n // Jump to first item in tree.\n newItem = this.parents.first();\n newItem.focus();\n if (e.shiftKey) {\n this.multiSelectItem(newItem);\n } else if (!hasKeyModifier) {\n this.selectItem(newItem);\n }\n\n e.stopPropagation();\n return false;\n }\n case this.keys.end: {\n // Jump to last visible item.\n newItem = this.visibleItems.last();\n newItem.focus();\n if (e.shiftKey) {\n this.multiSelectItem(newItem);\n } else if (!hasKeyModifier) {\n this.selectItem(newItem);\n }\n\n e.stopPropagation();\n return false;\n }\n case this.keys.enter:\n case this.keys.space: {\n\n if (e.shiftKey) {\n this.multiSelectItem(item);\n } else if (e.metaKey || e.ctrlKey) {\n this.toggleItem(item);\n } else {\n this.selectItem(item);\n }\n\n e.stopPropagation();\n return false;\n }\n case this.keys.left: {\n if (item.has('ul') && item.attr('aria-expanded') == 'true') {\n this.collapseGroup(item);\n } else {\n // Move up to the parent.\n var itemUL = item.parent();\n var itemParent = itemUL.parent();\n if (itemParent.is('li')) {\n itemParent.focus();\n if (e.shiftKey) {\n this.multiSelectItem(itemParent);\n } else if (!hasKeyModifier) {\n this.selectItem(itemParent);\n }\n }\n }\n\n e.stopPropagation();\n return false;\n }\n case this.keys.right: {\n if (item.has('ul') && item.attr('aria-expanded') == 'false') {\n this.expandGroup(item);\n } else {\n // Move to the first item in the child group.\n newItem = item.children('ul').children('li').first();\n if (newItem.length > 0) {\n newItem.focus();\n if (e.shiftKey) {\n this.multiSelectItem(newItem);\n } else if (!hasKeyModifier) {\n this.selectItem(newItem);\n }\n }\n }\n\n e.stopPropagation();\n return false;\n }\n case this.keys.up: {\n\n if (currentIndex > 0) {\n var prev = this.visibleItems.eq(currentIndex - 1);\n prev.focus();\n if (e.shiftKey) {\n this.multiSelectItem(prev);\n } else if (!hasKeyModifier) {\n this.selectItem(prev);\n }\n }\n\n e.stopPropagation();\n return false;\n }\n case this.keys.down: {\n\n if (currentIndex < this.visibleItems.length - 1) {\n var next = this.visibleItems.eq(currentIndex + 1);\n next.focus();\n if (e.shiftKey) {\n this.multiSelectItem(next);\n } else if (!hasKeyModifier) {\n this.selectItem(next);\n }\n }\n e.stopPropagation();\n return false;\n }\n case this.keys.asterisk: {\n // Expand all groups.\n this.parents.each(function() {\n thisObj.expandGroup($(this));\n });\n\n e.stopPropagation();\n return false;\n }\n case this.keys.eight: {\n if (e.shiftKey) {\n // Expand all groups.\n this.parents.each(function() {\n thisObj.expandGroup($(this));\n });\n\n e.stopPropagation();\n }\n\n return false;\n }\n }\n\n return true;\n };\n\n /**\n * Handle a key press event - ie navigate the tree.\n *\n * @method handleKeyPress\n * @param {Object} item is the jquery id of the parent item of the group\n * @param {Event} e The event.\n * @return {Boolean}\n */\n Tree.prototype.handleKeyPress = function(item, e) {\n if (e.altKey || e.ctrlKey || e.shiftKey || e.metaKey) {\n // Do nothing.\n return true;\n }\n\n switch (e.keyCode) {\n case this.keys.tab: {\n return true;\n }\n case this.keys.enter:\n case this.keys.home:\n case this.keys.end:\n case this.keys.left:\n case this.keys.right:\n case this.keys.up:\n case this.keys.down: {\n e.stopPropagation();\n return false;\n }\n default : {\n var chr = String.fromCharCode(e.which);\n var match = false;\n var itemIndex = this.visibleItems.index(item);\n var itemCount = this.visibleItems.length;\n var currentIndex = itemIndex + 1;\n\n // Check if the active item was the last one on the list.\n if (currentIndex == itemCount) {\n currentIndex = 0;\n }\n\n // Iterate through the menu items (starting from the current item and wrapping) until a match is found\n // or the loop returns to the current menu item.\n while (currentIndex != itemIndex) {\n\n var currentItem = this.visibleItems.eq(currentIndex);\n var titleChr = currentItem.text().charAt(0);\n\n if (currentItem.has('ul')) {\n titleChr = currentItem.find('span').text().charAt(0);\n }\n\n if (titleChr.toLowerCase() == chr) {\n match = true;\n break;\n }\n\n currentIndex = currentIndex + 1;\n if (currentIndex == itemCount) {\n // Reached the end of the list, start again at the beginning.\n currentIndex = 0;\n }\n }\n\n if (match === true) {\n this.updateFocus(this.visibleItems.eq(currentIndex));\n }\n e.stopPropagation();\n return false;\n }\n }\n\n // eslint-disable-next-line no-unreachable\n return true;\n };\n\n /**\n * Attach an event listener to the tree.\n *\n * @method on\n * @param {String} eventname This is the name of the event to listen for. Only 'selectionchanged' is supported for now.\n * @param {Function} handler The function to call when the event is triggered.\n */\n Tree.prototype.on = function(eventname, handler) {\n if (eventname !== 'selectionchanged') {\n log.warning('Invalid custom event name for tree. Only \"selectionchanged\" is supported.');\n } else {\n this.treeRoot.on(eventname, handler);\n }\n };\n\n /**\n * Handle a double click (expand/collapse).\n *\n * @method handleDblClick\n * @param {Object} item is the jquery id of the parent item of the group\n * @param {Event} e The event.\n * @return {Boolean}\n */\n Tree.prototype.handleDblClick = function(item, e) {\n\n if (e.altKey || e.ctrlKey || e.shiftKey || e.metaKey) {\n // Do nothing.\n return true;\n }\n\n // Apply the focus markup.\n this.updateFocus(item);\n\n // Expand or collapse the group.\n this.toggleGroup(item);\n\n e.stopPropagation();\n return false;\n };\n\n /**\n * Handle a click (select).\n *\n * @method handleExpandCollapseClick\n * @param {Object} item is the jquery id of the parent item of the group\n * @param {Event} e The event.\n * @return {Boolean}\n */\n Tree.prototype.handleExpandCollapseClick = function(item, e) {\n\n // Do not shift the focus.\n this.toggleGroup(item);\n e.stopPropagation();\n return false;\n };\n\n\n /**\n * Handle a click (select).\n *\n * @method handleClick\n * @param {Object} item is the jquery id of the parent item of the group\n * @param {Event} e The event.\n * @return {Boolean}\n */\n Tree.prototype.handleClick = function(item, e) {\n\n if (e.shiftKey) {\n this.multiSelectItem(item);\n } else if (e.metaKey || e.ctrlKey) {\n this.toggleItem(item);\n } else {\n this.selectItem(item);\n }\n this.updateFocus(item);\n e.stopPropagation();\n return false;\n };\n\n /**\n * Handle a blur event\n *\n * @method handleBlur\n * @return {Boolean}\n */\n Tree.prototype.handleBlur = function() {\n return true;\n };\n\n /**\n * Handle a focus event\n *\n * @method handleFocus\n * @param {Object} item item is the jquery id of the parent item of the group\n * @return {Boolean}\n */\n Tree.prototype.handleFocus = function(item) {\n\n this.updateFocus(item);\n\n return true;\n };\n\n /**\n * Bind the event listeners we require.\n *\n * @method bindEventHandlers\n */\n Tree.prototype.bindEventHandlers = function() {\n var thisObj = this;\n\n // Bind a dblclick handler to the parent items.\n this.parents.dblclick(function(e) {\n return thisObj.handleDblClick($(this), e);\n });\n\n // Bind a click handler.\n this.items.click(function(e) {\n return thisObj.handleClick($(this), e);\n });\n\n // Bind a toggle handler to the expand/collapse icons.\n this.items.children('img').click(function(e) {\n return thisObj.handleExpandCollapseClick($(this).parent(), e);\n });\n\n // Bind a keydown handler.\n this.items.keydown(function(e) {\n return thisObj.handleKeyDown($(this), e);\n });\n\n // Bind a keypress handler.\n this.items.keypress(function(e) {\n return thisObj.handleKeyPress($(this), e);\n });\n\n // Bind a focus handler.\n this.items.focus(function(e) {\n return thisObj.handleFocus($(this), e);\n });\n\n // Bind a blur handler.\n this.items.blur(function(e) {\n return thisObj.handleBlur($(this), e);\n });\n\n };\n\n return /** @alias module:tool_lp/tree */ Tree;\n});\n"],"names":["define","$","url","log","expandedImage","imageUrl","collapsedImage","Tree","selector","multiSelect","treeRoot","items","this","find","expandAll","length","parents","attr","visibleItems","activeItem","lastActiveItem","keys","tab","enter","space","pageup","pagedown","end","home","left","up","right","down","eight","asterisk","init","bindEventHandlers","prototype","prepend","clone","thisObj","each","collapseGroup","expandGroup","first","item","children","show","hide","toggleGroup","triggerChange","allSelected","filter","trigger","selected","multiSelectItem","lastIndex","index","currentIndex","get","selectItem","walk","parent","toggleItem","current","updateFocus","handleKeyDown","e","newItem","hasKeyModifier","shiftKey","ctrlKey","metaKey","altKey","keyCode","focus","stopPropagation","last","has","itemParent","is","prev","eq","next","handleKeyPress","chr","String","fromCharCode","which","match","itemIndex","itemCount","currentItem","titleChr","text","charAt","toLowerCase","on","eventname","handler","warning","handleDblClick","handleExpandCollapseClick","handleClick","handleBlur","handleFocus","dblclick","click","keydown","keypress","blur"],"mappings":";;;;;;;;;;;;AA2BAA,sBAAO,CAAC,SAAU,WAAY,aAAa,SAASC,EAAGC,IAAKC,SAGpDC,cAAgBH,EAAE,oBAAsBC,IAAIG,SAAS,cAAgB,OAErEC,eAAiBL,EAAE,oBAAsBC,IAAIG,SAAS,eAAiB,OAQvEE,KAAO,SAASC,SAAUC,kBACrBC,SAAWT,EAAEO,eACbC,iBAAsC,IAAhBA,cAA+C,IAAhBA,iBAErDE,MAAQC,KAAKF,SAASG,KAAK,WAC3BC,UAAYF,KAAKD,MAAMI,OAAS,QAChCC,QAAUJ,KAAKF,SAASG,KAAK,cAE9BJ,kBACKC,SAASO,KAAK,uBAAwB,aAG1CN,MAAMM,KAAK,gBAAiB,cAE5BC,aAAe,UACfC,WAAa,UACbC,eAAiB,UAEjBC,KAAO,CACRC,IAAU,EACVC,MAAU,GACVC,MAAU,GACVC,OAAU,GACVC,SAAU,GACVC,IAAU,GACVC,KAAU,GACVC,KAAU,GACVC,GAAU,GACVC,MAAU,GACVC,KAAU,GACVC,MAAU,GACVC,SAAU,UAGTC,YAEAC,4BAQT7B,KAAK8B,UAAUF,KAAO,gBACbnB,QAAQC,KAAK,gBAAiB,aAC9BD,QAAQsB,QAAQlC,cAAcmC,cAE9B5B,MAAMM,KAAK,OAAQ,kBACnBN,MAAMM,KAAK,WAAY,WACvBD,QAAQC,KAAK,OAAQ,cACrBP,SAASO,KAAK,OAAQ,aAEtBC,aAAeN,KAAKF,SAASG,KAAK,UAEnC2B,QAAU5B,KACTA,KAAKE,iBACDE,QAAQyB,MAAK,WACdD,QAAQE,cAAczC,EAAEW,eAEvB+B,YAAY/B,KAAKI,QAAQ4B,WAUtCrC,KAAK8B,UAAUM,YAAc,SAASE,MAEtBA,KAAKC,SAAS,MAGpBC,OAAO9B,KAAK,cAAe,SAEjC4B,KAAK5B,KAAK,gBAAiB,QAE3B4B,KAAKC,SAAS,OAAO7B,KAAK,MAAOb,cAAca,KAAK,aAG/CC,aAAeN,KAAKF,SAASG,KAAK,eAS3CN,KAAK8B,UAAUK,cAAgB,SAASG,MACxBA,KAAKC,SAAS,MAGpBE,OAAO/B,KAAK,cAAe,QAEjC4B,KAAK5B,KAAK,gBAAiB,SAE3B4B,KAAKC,SAAS,OAAO7B,KAAK,MAAOX,eAAeW,KAAK,aAGhDC,aAAeN,KAAKF,SAASG,KAAK,eAS3CN,KAAK8B,UAAUY,YAAc,SAASJ,MACA,QAA9BA,KAAK5B,KAAK,sBACLyB,cAAcG,WAEdF,YAAYE,OASzBtC,KAAK8B,UAAUa,cAAgB,eACvBC,YAAcvC,KAAKD,MAAMyC,OAAO,wBAC/BxC,KAAKH,cACN0C,YAAcA,YAAYP,cAEzBlC,SAAS2C,QAAQ,mBAAoB,CAACC,SAAUH,eASzD5C,KAAK8B,UAAUkB,gBAAkB,SAASV,SACjCjC,KAAKH,aAEH,GAA4B,OAAxBG,KAAKQ,eAAyB,SACjCoC,UAAY5C,KAAKM,aAAauC,MAAM7C,KAAKQ,gBACzCsC,aAAe9C,KAAKM,aAAauC,MAAM7C,KAAKO,YAGzCqC,UAAYE,cACLzD,EAAEW,KAAKM,aAAayC,IAAIH,YAC1BvC,KAAK,gBAAiB,QAC9BuC,iBAEGA,UAAYE,cACLzD,EAAEW,KAAKM,aAAayC,IAAIH,YAC1BvC,KAAK,gBAAiB,QAC9BuC,uBAdC7C,MAAMM,KAAK,gBAAiB,SAkBrC4B,KAAK5B,KAAK,gBAAiB,aACtBiC,iBAST3C,KAAK8B,UAAUuB,WAAa,SAASf,cAE7BgB,KAAOhB,KAAKiB,SACY,QAArBD,KAAK5C,KAAK,SAEqB,UADlC4C,KAAOA,KAAKC,UACH7C,KAAK,uBACL0B,YAAYkB,MAErBA,KAAOA,KAAKC,cAEXnD,MAAMM,KAAK,gBAAiB,SACjC4B,KAAK5B,KAAK,gBAAiB,aACtBiC,iBAST3C,KAAK8B,UAAU0B,WAAa,SAASlB,SAC5BjC,KAAKH,iBAKNuD,QAAUnB,KAAK5B,KAAK,iBAEpB+C,QADY,SAAZA,QACU,QAEA,OAEdnB,KAAK5B,KAAK,gBAAiB+C,cACtBd,0BAXIU,WAAWf,OAoBxBtC,KAAK8B,UAAU4B,YAAc,SAASpB,WAC7BzB,eAAiBR,KAAKO,gBACtBA,WAAa0B,aAEdgB,KAAOhB,KAAKiB,SACY,QAArBD,KAAK5C,KAAK,SAEqB,UADlC4C,KAAOA,KAAKC,UACH7C,KAAK,uBACL0B,YAAYkB,MAErBA,KAAOA,KAAKC,cAEXnD,MAAMM,KAAK,WAAY,MAC5B4B,KAAK5B,KAAK,WAAY,IAa1BV,KAAK8B,UAAU6B,cAAgB,SAASrB,KAAMsB,OACtCT,aAAe9C,KAAKM,aAAauC,MAAMZ,MACvCuB,QAAU,KACVC,eAAiBF,EAAEG,UAAYH,EAAEI,SAAWJ,EAAEK,SAAWL,EAAEM,OAC3DjC,QAAU5B,YAENuD,EAAEO,cACD9D,KAAKS,KAAKO,YAEXwC,QAAUxD,KAAKI,QAAQ4B,SACf+B,QACJR,EAAEG,cACGf,gBAAgBa,SACbC,qBACHT,WAAWQ,SAGpBD,EAAES,mBACK,OAENhE,KAAKS,KAAKM,WAEXyC,QAAUxD,KAAKM,aAAa2D,QACpBF,QACJR,EAAEG,cACGf,gBAAgBa,SACbC,qBACHT,WAAWQ,SAGpBD,EAAES,mBACK,OAENhE,KAAKS,KAAKE,WACVX,KAAKS,KAAKG,aAEP2C,EAAEG,cACGf,gBAAgBV,MACdsB,EAAEK,SAAWL,EAAEI,aACjBR,WAAWlB,WAEXe,WAAWf,MAGpBsB,EAAES,mBACK,OAENhE,KAAKS,KAAKQ,QACPgB,KAAKiC,IAAI,OAAuC,QAA9BjC,KAAK5B,KAAK,sBACvByB,cAAcG,UAChB,KAGCkC,WADSlC,KAAKiB,SACMA,SACpBiB,WAAWC,GAAG,QACdD,WAAWJ,QACPR,EAAEG,cACGf,gBAAgBwB,YACbV,qBACHT,WAAWmB,oBAK5BZ,EAAES,mBACK,OAENhE,KAAKS,KAAKU,aACPc,KAAKiC,IAAI,OAAuC,SAA9BjC,KAAK5B,KAAK,sBACvB0B,YAAYE,OAGjBuB,QAAUvB,KAAKC,SAAS,MAAMA,SAAS,MAAMF,SACjC7B,OAAS,IACjBqD,QAAQO,QACJR,EAAEG,cACGf,gBAAgBa,SACbC,qBACHT,WAAWQ,UAK5BD,EAAES,mBACK,OAENhE,KAAKS,KAAKS,MAEP4B,aAAe,EAAG,KACduB,KAAOrE,KAAKM,aAAagE,GAAGxB,aAAe,GAC/CuB,KAAKN,QACDR,EAAEG,cACGf,gBAAgB0B,MACbZ,qBACHT,WAAWqB,aAIxBd,EAAES,mBACK,OAENhE,KAAKS,KAAKW,QAEP0B,aAAe9C,KAAKM,aAAaH,OAAS,EAAG,KACzCoE,KAAOvE,KAAKM,aAAagE,GAAGxB,aAAe,GAC/CyB,KAAKR,QACDR,EAAEG,cACGf,gBAAgB4B,MACbd,qBACHT,WAAWuB,aAGxBhB,EAAES,mBACK,OAENhE,KAAKS,KAAKa,qBAENlB,QAAQyB,MAAK,WACdD,QAAQG,YAAY1C,EAAEW,UAG1BuD,EAAES,mBACK,OAENhE,KAAKS,KAAKY,aACPkC,EAAEG,gBAEGtD,QAAQyB,MAAK,WACdD,QAAQG,YAAY1C,EAAEW,UAG1BuD,EAAES,oBAGC,SAIR,GAWXrE,KAAK8B,UAAU+C,eAAiB,SAASvC,KAAMsB,MACvCA,EAAEM,QAAUN,EAAEI,SAAWJ,EAAEG,UAAYH,EAAEK,eAElC,SAGHL,EAAEO,cACD9D,KAAKS,KAAKC,WACJ,OAENV,KAAKS,KAAKE,WACVX,KAAKS,KAAKO,UACVhB,KAAKS,KAAKM,SACVf,KAAKS,KAAKQ,UACVjB,KAAKS,KAAKU,WACVnB,KAAKS,KAAKS,QACVlB,KAAKS,KAAKW,YACXmC,EAAES,mBACK,cAGHS,IAAMC,OAAOC,aAAapB,EAAEqB,OAC5BC,OAAQ,EACRC,UAAY9E,KAAKM,aAAauC,MAAMZ,MACpC8C,UAAY/E,KAAKM,aAAaH,OAC9B2C,aAAegC,UAAY,MAG3BhC,cAAgBiC,YAChBjC,aAAe,GAKZA,cAAgBgC,WAAW,KAE1BE,YAAchF,KAAKM,aAAagE,GAAGxB,cACnCmC,SAAWD,YAAYE,OAAOC,OAAO,MAErCH,YAAYd,IAAI,QAChBe,SAAWD,YAAY/E,KAAK,QAAQiF,OAAOC,OAAO,IAGlDF,SAASG,eAAiBX,IAAK,CAC/BI,OAAQ,SAIZ/B,cAA8B,IACViC,YAEhBjC,aAAe,UAIT,IAAV+B,YACKxB,YAAYrD,KAAKM,aAAagE,GAAGxB,eAE1CS,EAAES,mBACK,SAKR,GAUXrE,KAAK8B,UAAU4D,GAAK,SAASC,UAAWC,SAClB,qBAAdD,UACA/F,IAAIiG,QAAQ,kFAEP1F,SAASuF,GAAGC,UAAWC,UAYpC5F,KAAK8B,UAAUgE,eAAiB,SAASxD,KAAMsB,YAEvCA,EAAEM,QAAUN,EAAEI,SAAWJ,EAAEG,UAAYH,EAAEK,gBAMxCP,YAAYpB,WAGZI,YAAYJ,MAEjBsB,EAAES,mBACK,IAWXrE,KAAK8B,UAAUiE,0BAA4B,SAASzD,KAAMsB,eAGjDlB,YAAYJ,MACjBsB,EAAES,mBACK,GAYXrE,KAAK8B,UAAUkE,YAAc,SAAS1D,KAAMsB,UAEpCA,EAAEG,cACGf,gBAAgBV,MACdsB,EAAEK,SAAWL,EAAEI,aACjBR,WAAWlB,WAEXe,WAAWf,WAEfoB,YAAYpB,MACjBsB,EAAES,mBACK,GASXrE,KAAK8B,UAAUmE,WAAa,kBACjB,GAUXjG,KAAK8B,UAAUoE,YAAc,SAAS5D,kBAE7BoB,YAAYpB,OAEV,GAQXtC,KAAK8B,UAAUD,kBAAoB,eAC3BI,QAAU5B,UAGTI,QAAQ0F,UAAS,SAASvC,UACpB3B,QAAQ6D,eAAepG,EAAEW,MAAOuD,WAItCxD,MAAMgG,OAAM,SAASxC,UACf3B,QAAQ+D,YAAYtG,EAAEW,MAAOuD,WAInCxD,MAAMmC,SAAS,OAAO6D,OAAM,SAASxC,UAC/B3B,QAAQ8D,0BAA0BrG,EAAEW,MAAMkD,SAAUK,WAI1DxD,MAAMiG,SAAQ,SAASzC,UACjB3B,QAAQ0B,cAAcjE,EAAEW,MAAOuD,WAIrCxD,MAAMkG,UAAS,SAAS1C,UAClB3B,QAAQ4C,eAAenF,EAAEW,MAAOuD,WAItCxD,MAAMgE,OAAM,SAASR,UACf3B,QAAQiE,YAAYxG,EAAEW,MAAOuD,WAInCxD,MAAMmG,MAAK,SAAS3C,UACd3B,QAAQgE,WAAWvG,EAAEW,MAAOuD,OAKF5D"}
\ No newline at end of file
+{"version":3,"file":"tree.min.js","sources":["../src/tree.js"],"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 * Implement an accessible aria tree widget, from a nested unordered list.\n * Based on http://oaa-accessibility.org/example/41/\n *\n * To respond to selection changed events - use tree.on(\"selectionchanged\", handler).\n * The handler will receive an array of nodes, which are the list items that are currently\n * selected. (Or a single node if multiselect is disabled).\n *\n * @module tool_lp/tree\n * @copyright 2015 Damyon Wiese \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(['jquery', 'core/url', 'core/log'], function($, url, log) {\n // Private variables and functions.\n /** @var {String} expandedImage The html for an expanded tree node twistie. */\n var expandedImage = $('');\n /** @var {String} collapsedImage The html for a collapsed tree node twistie. */\n var collapsedImage = $('');\n\n /**\n * Constructor\n *\n * @param {String} selector\n * @param {Boolean} multiSelect\n */\n var Tree = function(selector, multiSelect) {\n this.treeRoot = $(selector);\n this.multiSelect = (typeof multiSelect === 'undefined' || multiSelect === true);\n\n this.items = this.treeRoot.find('li');\n this.expandAll = this.items.length < 20;\n this.parents = this.treeRoot.find('li:has(ul)');\n\n if (multiSelect) {\n this.treeRoot.attr('aria-multiselectable', 'true');\n }\n\n this.items.attr('aria-selected', 'false');\n\n this.visibleItems = null;\n this.activeItem = null;\n this.lastActiveItem = null;\n\n this.keys = {\n tab: 9,\n enter: 13,\n space: 32,\n pageup: 33,\n pagedown: 34,\n end: 35,\n home: 36,\n left: 37,\n up: 38,\n right: 39,\n down: 40,\n eight: 56,\n asterisk: 106\n };\n\n this.init();\n\n this.bindEventHandlers();\n };\n // Public variables and functions.\n\n /**\n * Init this tree\n * @method init\n */\n Tree.prototype.init = function() {\n this.parents.attr('aria-expanded', 'true');\n this.parents.prepend(expandedImage.clone());\n\n this.items.attr('role', 'tree-item');\n this.items.attr('tabindex', '-1');\n this.parents.attr('role', 'group');\n this.treeRoot.attr('role', 'tree');\n\n this.visibleItems = this.treeRoot.find('li');\n\n var thisObj = this;\n if (!this.expandAll) {\n this.parents.each(function() {\n thisObj.collapseGroup($(this));\n });\n this.expandGroup(this.parents.first());\n }\n };\n\n /**\n * Expand a collapsed group.\n *\n * @method expandGroup\n * @param {Object} item is the jquery id of the parent item of the group\n */\n Tree.prototype.expandGroup = function(item) {\n // Find the first child ul node.\n var group = item.children('ul');\n\n // Expand the group.\n group.show().attr('aria-hidden', 'false');\n\n item.attr('aria-expanded', 'true');\n\n item.children('img').attr('src', expandedImage.attr('src'));\n\n // Update the list of visible items.\n this.visibleItems = this.treeRoot.find('li:visible');\n };\n\n /**\n * Collapse an expanded group.\n *\n * @method collapseGroup\n * @param {Object} item is the jquery id of the parent item of the group\n */\n Tree.prototype.collapseGroup = function(item) {\n var group = item.children('ul');\n\n // Collapse the group.\n group.hide().attr('aria-hidden', 'true');\n\n item.attr('aria-expanded', 'false');\n\n item.children('img').attr('src', collapsedImage.attr('src'));\n\n // Update the list of visible items.\n this.visibleItems = this.treeRoot.find('li:visible');\n };\n\n /**\n * Expand or collapse a group.\n *\n * @method toggleGroup\n * @param {Object} item is the jquery id of the parent item of the group\n */\n Tree.prototype.toggleGroup = function(item) {\n if (item.attr('aria-expanded') == 'true') {\n this.collapseGroup(item);\n } else {\n this.expandGroup(item);\n }\n };\n\n /**\n * Whenever the currently selected node has changed, trigger an event using this function.\n *\n * @method triggerChange\n */\n Tree.prototype.triggerChange = function() {\n var allSelected = this.items.filter('[aria-selected=true]');\n if (!this.multiSelect) {\n allSelected = allSelected.first();\n }\n this.treeRoot.trigger('selectionchanged', {selected: allSelected});\n };\n\n /**\n * Select all the items between the last focused item and this currently focused item.\n *\n * @method multiSelectItem\n * @param {Object} item is the jquery id of the newly selected item.\n */\n Tree.prototype.multiSelectItem = function(item) {\n if (!this.multiSelect) {\n this.items.attr('aria-selected', 'false');\n } else if (this.lastActiveItem !== null) {\n var lastIndex = this.visibleItems.index(this.lastActiveItem);\n var currentIndex = this.visibleItems.index(this.activeItem);\n var oneItem = null;\n\n while (lastIndex < currentIndex) {\n oneItem = $(this.visibleItems.get(lastIndex));\n oneItem.attr('aria-selected', 'true');\n lastIndex++;\n }\n while (lastIndex > currentIndex) {\n oneItem = $(this.visibleItems.get(lastIndex));\n oneItem.attr('aria-selected', 'true');\n lastIndex--;\n }\n }\n\n item.attr('aria-selected', 'true');\n this.triggerChange();\n };\n\n /**\n * Select a single item. Make sure all the parents are expanded. De-select all other items.\n *\n * @method selectItem\n * @param {Object} item is the jquery id of the newly selected item.\n */\n Tree.prototype.selectItem = function(item) {\n // Expand all nodes up the tree.\n var walk = item.parent();\n while (walk.attr('role') != 'tree') {\n walk = walk.parent();\n if (walk.attr('aria-expanded') == 'false') {\n this.expandGroup(walk);\n }\n walk = walk.parent();\n }\n this.items.attr('aria-selected', 'false');\n item.attr('aria-selected', 'true');\n this.triggerChange();\n };\n\n /**\n * Toggle the selected state for an item back and forth.\n *\n * @method toggleItem\n * @param {Object} item is the jquery id of the item to toggle.\n */\n Tree.prototype.toggleItem = function(item) {\n if (!this.multiSelect) {\n this.selectItem(item);\n return;\n }\n\n var current = item.attr('aria-selected');\n if (current === 'true') {\n current = 'false';\n } else {\n current = 'true';\n }\n item.attr('aria-selected', current);\n this.triggerChange();\n };\n\n /**\n * Set the focus to this item.\n *\n * @method updateFocus\n * @param {Object} item is the jquery id of the parent item of the group\n */\n Tree.prototype.updateFocus = function(item) {\n this.lastActiveItem = this.activeItem;\n this.activeItem = item;\n // Expand all nodes up the tree.\n var walk = item.parent();\n while (walk.attr('role') != 'tree') {\n walk = walk.parent();\n if (walk.attr('aria-expanded') == 'false') {\n this.expandGroup(walk);\n }\n walk = walk.parent();\n }\n this.items.attr('tabindex', '-1');\n item.attr('tabindex', 0);\n };\n\n /**\n * Handle a key down event - ie navigate the tree.\n *\n * @method handleKeyDown\n * @param {Object} item is the jquery id of the parent item of the group\n * @param {Event} e The event.\n * @return {Boolean}\n */\n // This function should be simplified. In the meantime..\n // eslint-disable-next-line complexity\n Tree.prototype.handleKeyDown = function(item, e) {\n var currentIndex = this.visibleItems.index(item);\n var newItem = null;\n var hasKeyModifier = e.shiftKey || e.ctrlKey || e.metaKey || e.altKey;\n var thisObj = this;\n\n switch (e.keyCode) {\n case this.keys.home: {\n // Jump to first item in tree.\n newItem = this.parents.first();\n newItem.focus();\n if (e.shiftKey) {\n this.multiSelectItem(newItem);\n } else if (!hasKeyModifier) {\n this.selectItem(newItem);\n }\n\n e.stopPropagation();\n return false;\n }\n case this.keys.end: {\n // Jump to last visible item.\n newItem = this.visibleItems.last();\n newItem.focus();\n if (e.shiftKey) {\n this.multiSelectItem(newItem);\n } else if (!hasKeyModifier) {\n this.selectItem(newItem);\n }\n\n e.stopPropagation();\n return false;\n }\n case this.keys.enter:\n case this.keys.space: {\n\n if (e.shiftKey) {\n this.multiSelectItem(item);\n } else if (e.metaKey || e.ctrlKey) {\n this.toggleItem(item);\n } else {\n this.selectItem(item);\n }\n\n e.stopPropagation();\n return false;\n }\n case this.keys.left: {\n if (item.has('ul') && item.attr('aria-expanded') == 'true') {\n this.collapseGroup(item);\n } else {\n // Move up to the parent.\n var itemUL = item.parent();\n var itemParent = itemUL.parent();\n if (itemParent.is('li')) {\n itemParent.focus();\n if (e.shiftKey) {\n this.multiSelectItem(itemParent);\n } else if (!hasKeyModifier) {\n this.selectItem(itemParent);\n }\n }\n }\n\n e.stopPropagation();\n return false;\n }\n case this.keys.right: {\n if (item.has('ul') && item.attr('aria-expanded') == 'false') {\n this.expandGroup(item);\n } else {\n // Move to the first item in the child group.\n newItem = item.children('ul').children('li').first();\n if (newItem.length > 0) {\n newItem.focus();\n if (e.shiftKey) {\n this.multiSelectItem(newItem);\n } else if (!hasKeyModifier) {\n this.selectItem(newItem);\n }\n }\n }\n\n e.stopPropagation();\n return false;\n }\n case this.keys.up: {\n\n if (currentIndex > 0) {\n var prev = this.visibleItems.eq(currentIndex - 1);\n prev.focus();\n if (e.shiftKey) {\n this.multiSelectItem(prev);\n } else if (!hasKeyModifier) {\n this.selectItem(prev);\n }\n }\n\n e.stopPropagation();\n return false;\n }\n case this.keys.down: {\n\n if (currentIndex < this.visibleItems.length - 1) {\n var next = this.visibleItems.eq(currentIndex + 1);\n next.focus();\n if (e.shiftKey) {\n this.multiSelectItem(next);\n } else if (!hasKeyModifier) {\n this.selectItem(next);\n }\n }\n e.stopPropagation();\n return false;\n }\n case this.keys.asterisk: {\n // Expand all groups.\n this.parents.each(function() {\n thisObj.expandGroup($(this));\n });\n\n e.stopPropagation();\n return false;\n }\n case this.keys.eight: {\n if (e.shiftKey) {\n // Expand all groups.\n this.parents.each(function() {\n thisObj.expandGroup($(this));\n });\n\n e.stopPropagation();\n }\n\n return false;\n }\n }\n\n return true;\n };\n\n /**\n * Handle a key press event - ie navigate the tree.\n *\n * @method handleKeyPress\n * @param {Object} item is the jquery id of the parent item of the group\n * @param {Event} e The event.\n * @return {Boolean}\n */\n Tree.prototype.handleKeyPress = function(item, e) {\n if (e.altKey || e.ctrlKey || e.shiftKey || e.metaKey) {\n // Do nothing.\n return true;\n }\n\n switch (e.keyCode) {\n case this.keys.tab: {\n return true;\n }\n case this.keys.enter:\n case this.keys.home:\n case this.keys.end:\n case this.keys.left:\n case this.keys.right:\n case this.keys.up:\n case this.keys.down: {\n e.stopPropagation();\n return false;\n }\n default : {\n var chr = String.fromCharCode(e.which);\n var match = false;\n var itemIndex = this.visibleItems.index(item);\n var itemCount = this.visibleItems.length;\n var currentIndex = itemIndex + 1;\n\n // Check if the active item was the last one on the list.\n if (currentIndex == itemCount) {\n currentIndex = 0;\n }\n\n // Iterate through the menu items (starting from the current item and wrapping) until a match is found\n // or the loop returns to the current menu item.\n while (currentIndex != itemIndex) {\n\n var currentItem = this.visibleItems.eq(currentIndex);\n var titleChr = currentItem.text().charAt(0);\n\n if (currentItem.has('ul')) {\n titleChr = currentItem.find('span').text().charAt(0);\n }\n\n if (titleChr.toLowerCase() == chr) {\n match = true;\n break;\n }\n\n currentIndex = currentIndex + 1;\n if (currentIndex == itemCount) {\n // Reached the end of the list, start again at the beginning.\n currentIndex = 0;\n }\n }\n\n if (match === true) {\n this.updateFocus(this.visibleItems.eq(currentIndex));\n }\n e.stopPropagation();\n return false;\n }\n }\n\n // eslint-disable-next-line no-unreachable\n return true;\n };\n\n /**\n * Attach an event listener to the tree.\n *\n * @method on\n * @param {String} eventname This is the name of the event to listen for. Only 'selectionchanged' is supported for now.\n * @param {Function} handler The function to call when the event is triggered.\n */\n Tree.prototype.on = function(eventname, handler) {\n if (eventname !== 'selectionchanged') {\n log.warning('Invalid custom event name for tree. Only \"selectionchanged\" is supported.');\n } else {\n this.treeRoot.on(eventname, handler);\n }\n };\n\n /**\n * Handle a double click (expand/collapse).\n *\n * @method handleDblClick\n * @param {Object} item is the jquery id of the parent item of the group\n * @param {Event} e The event.\n * @return {Boolean}\n */\n Tree.prototype.handleDblClick = function(item, e) {\n\n if (e.altKey || e.ctrlKey || e.shiftKey || e.metaKey) {\n // Do nothing.\n return true;\n }\n\n // Apply the focus markup.\n this.updateFocus(item);\n\n // Expand or collapse the group.\n this.toggleGroup(item);\n\n e.stopPropagation();\n return false;\n };\n\n /**\n * Handle a click (select).\n *\n * @method handleExpandCollapseClick\n * @param {Object} item is the jquery id of the parent item of the group\n * @param {Event} e The event.\n * @return {Boolean}\n */\n Tree.prototype.handleExpandCollapseClick = function(item, e) {\n\n // Do not shift the focus.\n this.toggleGroup(item);\n e.stopPropagation();\n return false;\n };\n\n\n /**\n * Handle a click (select).\n *\n * @method handleClick\n * @param {Object} item is the jquery id of the parent item of the group\n * @param {Event} e The event.\n * @return {Boolean}\n */\n Tree.prototype.handleClick = function(item, e) {\n\n if (e.shiftKey) {\n this.multiSelectItem(item);\n } else if (e.metaKey || e.ctrlKey) {\n this.toggleItem(item);\n } else {\n this.selectItem(item);\n }\n this.updateFocus(item);\n e.stopPropagation();\n return false;\n };\n\n /**\n * Handle a blur event\n *\n * @method handleBlur\n * @return {Boolean}\n */\n Tree.prototype.handleBlur = function() {\n return true;\n };\n\n /**\n * Handle a focus event\n *\n * @method handleFocus\n * @param {Object} item item is the jquery id of the parent item of the group\n * @return {Boolean}\n */\n Tree.prototype.handleFocus = function(item) {\n\n this.updateFocus(item);\n\n return true;\n };\n\n /**\n * Bind the event listeners we require.\n *\n * @method bindEventHandlers\n */\n Tree.prototype.bindEventHandlers = function() {\n var thisObj = this;\n\n // Bind a dblclick handler to the parent items.\n this.parents.dblclick(function(e) {\n return thisObj.handleDblClick($(this), e);\n });\n\n // Bind a click handler.\n this.items.click(function(e) {\n return thisObj.handleClick($(this), e);\n });\n\n // Bind a toggle handler to the expand/collapse icons.\n this.items.children('img').click(function(e) {\n return thisObj.handleExpandCollapseClick($(this).parent(), e);\n });\n\n // Bind a keydown handler.\n this.items.keydown(function(e) {\n return thisObj.handleKeyDown($(this), e);\n });\n\n // Bind a keypress handler.\n this.items.keypress(function(e) {\n return thisObj.handleKeyPress($(this), e);\n });\n\n // Bind a focus handler.\n this.items.focus(function(e) {\n return thisObj.handleFocus($(this), e);\n });\n\n // Bind a blur handler.\n this.items.blur(function(e) {\n return thisObj.handleBlur($(this), e);\n });\n\n };\n\n return /** @alias module:tool_lp/tree */ Tree;\n});\n"],"names":["define","$","url","log","expandedImage","imageUrl","collapsedImage","Tree","selector","multiSelect","this","treeRoot","items","find","expandAll","length","parents","attr","visibleItems","activeItem","lastActiveItem","keys","tab","enter","space","pageup","pagedown","end","home","left","up","right","down","eight","asterisk","init","bindEventHandlers","prototype","prepend","clone","thisObj","each","collapseGroup","expandGroup","first","item","children","show","hide","toggleGroup","triggerChange","allSelected","filter","trigger","selected","multiSelectItem","lastIndex","index","currentIndex","get","selectItem","walk","parent","toggleItem","current","updateFocus","handleKeyDown","e","newItem","hasKeyModifier","shiftKey","ctrlKey","metaKey","altKey","keyCode","focus","stopPropagation","last","has","itemParent","is","prev","eq","next","handleKeyPress","chr","String","fromCharCode","which","match","itemIndex","itemCount","currentItem","titleChr","text","charAt","toLowerCase","on","eventname","handler","warning","handleDblClick","handleExpandCollapseClick","handleClick","handleBlur","handleFocus","dblclick","click","keydown","keypress","blur"],"mappings":"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"}
\ No newline at end of file
diff --git a/admin/tool/lp/amd/build/user_competency_course_navigation.min.js.map b/admin/tool/lp/amd/build/user_competency_course_navigation.min.js.map
index 180e7138397..1fcb3c1cd9b 100644
--- a/admin/tool/lp/amd/build/user_competency_course_navigation.min.js.map
+++ b/admin/tool/lp/amd/build/user_competency_course_navigation.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"user_competency_course_navigation.min.js","sources":["../src/user_competency_course_navigation.js"],"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 * Module to enable inline editing of a comptency grade.\n *\n * @module tool_lp/user_competency_course_navigation\n * @copyright 2015 Damyon Wiese\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\ndefine(['jquery'], function($) {\n\n /**\n * UserCompetencyCourseNavigation\n *\n * @class tool_lp/user_competency_course_navigation\n * @param {String} userSelector The selector of the user element.\n * @param {String} competencySelector The selector of the competency element.\n * @param {String} baseUrl The base url for the page (no params).\n * @param {Number} userId The user id\n * @param {Number} competencyId The competency id\n * @param {Number} courseId The course id\n */\n var UserCompetencyCourseNavigation = function(userSelector, competencySelector, baseUrl, userId, competencyId, courseId) {\n this._baseUrl = baseUrl;\n this._userId = userId + '';\n this._competencyId = competencyId + '';\n this._courseId = courseId;\n\n $(userSelector).on('change', this._userChanged.bind(this));\n $(competencySelector).on('change', this._competencyChanged.bind(this));\n };\n\n /**\n * The user was changed in the select list.\n *\n * @method _userChanged\n * @param {Event} e\n */\n UserCompetencyCourseNavigation.prototype._userChanged = function(e) {\n var newUserId = $(e.target).val();\n var queryStr = '?userid=' + newUserId + '&courseid=' + this._courseId + '&competencyid=' + this._competencyId;\n document.location = this._baseUrl + queryStr;\n };\n\n /**\n * The competency was changed in the select list.\n *\n * @method _competencyChanged\n * @param {Event} e\n */\n UserCompetencyCourseNavigation.prototype._competencyChanged = function(e) {\n var newCompetencyId = $(e.target).val();\n var queryStr = '?userid=' + this._userId + '&courseid=' + this._courseId + '&competencyid=' + newCompetencyId;\n document.location = this._baseUrl + queryStr;\n };\n\n /** @property {Number} The id of the competency. */\n UserCompetencyCourseNavigation.prototype._competencyId = null;\n /** @property {Number} The id of the user. */\n UserCompetencyCourseNavigation.prototype._userId = null;\n /** @property {Number} The id of the course. */\n UserCompetencyCourseNavigation.prototype._courseId = null;\n /** @property {String} Plugin base url. */\n UserCompetencyCourseNavigation.prototype._baseUrl = null;\n /** @property {Boolean} Ignore the first change event for competencies. */\n UserCompetencyCourseNavigation.prototype._ignoreFirstCompetency = null;\n\n return UserCompetencyCourseNavigation;\n});\n"],"names":["define","$","UserCompetencyCourseNavigation","userSelector","competencySelector","baseUrl","userId","competencyId","courseId","_baseUrl","_userId","_competencyId","_courseId","on","this","_userChanged","bind","_competencyChanged","prototype","e","queryStr","target","val","document","location","newCompetencyId","_ignoreFirstCompetency"],"mappings":";;;;;;;AAuBAA,mDAAO,CAAC,WAAW,SAASC,OAapBC,+BAAiC,SAASC,aAAcC,mBAAoBC,QAASC,OAAQC,aAAcC,eACtGC,SAAWJ,aACXK,QAAUJ,OAAS,QACnBK,cAAgBJ,aAAe,QAC/BK,UAAYJ,SAEjBP,EAAEE,cAAcU,GAAG,SAAUC,KAAKC,aAAaC,KAAKF,OACpDb,EAAEG,oBAAoBS,GAAG,SAAUC,KAAKG,mBAAmBD,KAAKF,eASpEZ,+BAA+BgB,UAAUH,aAAe,SAASI,OAEzDC,SAAW,WADCnB,EAAEkB,EAAEE,QAAQC,MACY,aAAeR,KAAKF,UAAY,iBAAmBE,KAAKH,cAChGY,SAASC,SAAWV,KAAKL,SAAWW,UASxClB,+BAA+BgB,UAAUD,mBAAqB,SAASE,OAC/DM,gBAAkBxB,EAAEkB,EAAEE,QAAQC,MAC9BF,SAAW,WAAaN,KAAKJ,QAAU,aAAeI,KAAKF,UAAY,iBAAmBa,gBAC9FF,SAASC,SAAWV,KAAKL,SAAWW,UAIxClB,+BAA+BgB,UAAUP,cAAgB,KAEzDT,+BAA+BgB,UAAUR,QAAU,KAEnDR,+BAA+BgB,UAAUN,UAAY,KAErDV,+BAA+BgB,UAAUT,SAAW,KAEpDP,+BAA+BgB,UAAUQ,uBAAyB,KAE3DxB"}
\ No newline at end of file
+{"version":3,"file":"user_competency_course_navigation.min.js","sources":["../src/user_competency_course_navigation.js"],"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 * Module to enable inline editing of a comptency grade.\n *\n * @module tool_lp/user_competency_course_navigation\n * @copyright 2015 Damyon Wiese\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\ndefine(['jquery'], function($) {\n\n /**\n * UserCompetencyCourseNavigation\n *\n * @class tool_lp/user_competency_course_navigation\n * @param {String} userSelector The selector of the user element.\n * @param {String} competencySelector The selector of the competency element.\n * @param {String} baseUrl The base url for the page (no params).\n * @param {Number} userId The user id\n * @param {Number} competencyId The competency id\n * @param {Number} courseId The course id\n */\n var UserCompetencyCourseNavigation = function(userSelector, competencySelector, baseUrl, userId, competencyId, courseId) {\n this._baseUrl = baseUrl;\n this._userId = userId + '';\n this._competencyId = competencyId + '';\n this._courseId = courseId;\n\n $(userSelector).on('change', this._userChanged.bind(this));\n $(competencySelector).on('change', this._competencyChanged.bind(this));\n };\n\n /**\n * The user was changed in the select list.\n *\n * @method _userChanged\n * @param {Event} e\n */\n UserCompetencyCourseNavigation.prototype._userChanged = function(e) {\n var newUserId = $(e.target).val();\n var queryStr = '?userid=' + newUserId + '&courseid=' + this._courseId + '&competencyid=' + this._competencyId;\n document.location = this._baseUrl + queryStr;\n };\n\n /**\n * The competency was changed in the select list.\n *\n * @method _competencyChanged\n * @param {Event} e\n */\n UserCompetencyCourseNavigation.prototype._competencyChanged = function(e) {\n var newCompetencyId = $(e.target).val();\n var queryStr = '?userid=' + this._userId + '&courseid=' + this._courseId + '&competencyid=' + newCompetencyId;\n document.location = this._baseUrl + queryStr;\n };\n\n /** @property {Number} The id of the competency. */\n UserCompetencyCourseNavigation.prototype._competencyId = null;\n /** @property {Number} The id of the user. */\n UserCompetencyCourseNavigation.prototype._userId = null;\n /** @property {Number} The id of the course. */\n UserCompetencyCourseNavigation.prototype._courseId = null;\n /** @property {String} Plugin base url. */\n UserCompetencyCourseNavigation.prototype._baseUrl = null;\n /** @property {Boolean} Ignore the first change event for competencies. */\n UserCompetencyCourseNavigation.prototype._ignoreFirstCompetency = null;\n\n return UserCompetencyCourseNavigation;\n});\n"],"names":["define","$","UserCompetencyCourseNavigation","userSelector","competencySelector","baseUrl","userId","competencyId","courseId","this","_baseUrl","_userId","_competencyId","_courseId","on","_userChanged","bind","_competencyChanged","prototype","e","queryStr","target","val","document","location","newCompetencyId","_ignoreFirstCompetency"],"mappings":";;;;;;;AAuBAA,mDAAO,CAAC,WAAW,SAASC,GAaxB,IAAIC,+BAAiC,SAASC,aAAcC,mBAAoBC,QAASC,OAAQC,aAAcC,UAC3GC,KAAKC,SAAWL,QAChBI,KAAKE,QAAUL,OAAS,GACxBG,KAAKG,cAAgBL,aAAe,GACpCE,KAAKI,UAAYL,SAEjBP,EAAEE,cAAcW,GAAG,SAAUL,KAAKM,aAAaC,KAAKP,OACpDR,EAAEG,oBAAoBU,GAAG,SAAUL,KAAKQ,mBAAmBD,KAAKP,QAsCpE,OA7BAP,+BAA+BgB,UAAUH,aAAe,SAASI,GAC7D,IACIC,SAAW,WADCnB,EAAEkB,EAAEE,QAAQC,MACY,aAAeb,KAAKI,UAAY,iBAAmBJ,KAAKG,cAChGW,SAASC,SAAWf,KAAKC,SAAWU,UASxClB,+BAA+BgB,UAAUD,mBAAqB,SAASE,GACnE,IAAIM,gBAAkBxB,EAAEkB,EAAEE,QAAQC,MAC9BF,SAAW,WAAaX,KAAKE,QAAU,aAAeF,KAAKI,UAAY,iBAAmBY,gBAC9FF,SAASC,SAAWf,KAAKC,SAAWU,UAIxClB,+BAA+BgB,UAAUN,cAAgB,KAEzDV,+BAA+BgB,UAAUP,QAAU,KAEnDT,+BAA+BgB,UAAUL,UAAY,KAErDX,+BAA+BgB,UAAUR,SAAW,KAEpDR,+BAA+BgB,UAAUQ,uBAAyB,KAE3DxB,8BACX"}
\ No newline at end of file
diff --git a/admin/tool/lp/amd/build/user_competency_info.min.js.map b/admin/tool/lp/amd/build/user_competency_info.min.js.map
index 6a86054a7a4..c09041b6c6d 100644
--- a/admin/tool/lp/amd/build/user_competency_info.min.js.map
+++ b/admin/tool/lp/amd/build/user_competency_info.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"user_competency_info.min.js","sources":["../src/user_competency_info.js"],"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 * Module to refresh a user competency summary in a page.\n *\n * @module tool_lp/user_competency_info\n * @copyright 2015 Damyon Wiese\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\ndefine(['jquery', 'core/notification', 'core/ajax', 'core/templates'], function($, notification, ajax, templates) {\n\n /**\n * Info\n *\n * @param {JQuery} rootElement Selector to replace when the information needs updating.\n * @param {Number} competencyId The id of the competency.\n * @param {Number} userId The id of the user.\n * @param {Number} planId The id of the plan.\n * @param {Number} courseId The id of the course.\n * @param {Boolean} displayuser If we should display the user info.\n */\n var Info = function(rootElement, competencyId, userId, planId, courseId, displayuser) {\n this._rootElement = rootElement;\n this._competencyId = competencyId;\n this._userId = userId;\n this._planId = planId;\n this._courseId = courseId;\n this._valid = true;\n this._displayuser = (typeof displayuser !== 'undefined') ? displayuser : false;\n\n if (this._planId) {\n this._methodName = 'tool_lp_data_for_user_competency_summary_in_plan';\n this._args = {competencyid: this._competencyId, planid: this._planId};\n this._templateName = 'tool_lp/user_competency_summary_in_plan';\n } else if (this._courseId) {\n this._methodName = 'tool_lp_data_for_user_competency_summary_in_course';\n this._args = {userid: this._userId, competencyid: this._competencyId, courseid: this._courseId};\n this._templateName = 'tool_lp/user_competency_summary_in_course';\n } else {\n this._methodName = 'tool_lp_data_for_user_competency_summary';\n this._args = {userid: this._userId, competencyid: this._competencyId};\n this._templateName = 'tool_lp/user_competency_summary';\n }\n };\n\n /**\n * Reload the info for this user competency.\n *\n * @method reload\n */\n Info.prototype.reload = function() {\n var self = this,\n promises = [];\n\n if (!this._valid) {\n return;\n }\n\n promises = ajax.call([{\n methodname: this._methodName,\n args: this._args\n }]);\n\n promises[0].done(function(context) {\n // Check if we should also the user info.\n if (self._displayuser) {\n context.displayuser = true;\n }\n templates.render(self._templateName, context).done(function(html, js) {\n templates.replaceNode(self._rootElement, html, js);\n }).fail(notification.exception);\n }).fail(notification.exception);\n };\n\n /** @property {JQuery} The root element to replace in the DOM. */\n Info.prototype._rootElement = null;\n /** @property {Number} The id of the course. */\n Info.prototype._courseId = null;\n /** @property {Boolean} Is this module valid? */\n Info.prototype._valid = null;\n /** @property {Number} The id of the plan. */\n Info.prototype._planId = null;\n /** @property {Number} The id of the competency. */\n Info.prototype._competencyId = null;\n /** @property {Number} The id of the user. */\n Info.prototype._userId = null;\n /** @property {String} The method name to load the data. */\n Info.prototype._methodName = null;\n /** @property {Object} The arguments to load the data. */\n Info.prototype._args = null;\n /** @property {String} The template to reload the fragment. */\n Info.prototype._templateName = null;\n /** @property {Boolean} If we should display the user info? */\n Info.prototype._displayuser = false;\n\n return /** @alias module:tool_lp/user_competency_info */ Info;\n\n});\n"],"names":["define","$","notification","ajax","templates","Info","rootElement","competencyId","userId","planId","courseId","displayuser","_rootElement","_competencyId","_userId","_planId","_courseId","_valid","_displayuser","this","_methodName","_args","competencyid","planid","_templateName","userid","courseid","prototype","reload","self","call","methodname","args","done","context","render","html","js","replaceNode","fail","exception"],"mappings":";;;;;;;AAuBAA,sCAAO,CAAC,SAAU,oBAAqB,YAAa,mBAAmB,SAASC,EAAGC,aAAcC,KAAMC,eAY/FC,KAAO,SAASC,YAAaC,aAAcC,OAAQC,OAAQC,SAAUC,kBAChEC,aAAeN,iBACfO,cAAgBN,kBAChBO,QAAUN,YACVO,QAAUN,YACVO,UAAYN,cACZO,QAAS,OACTC,kBAAuC,IAAhBP,aAA+BA,YAEvDQ,KAAKJ,cACAK,YAAc,wDACdC,MAAQ,CAACC,aAAcH,KAAKN,cAAeU,OAAQJ,KAAKJ,cACxDS,cAAgB,2CACdL,KAAKH,gBACPI,YAAc,0DACdC,MAAQ,CAACI,OAAQN,KAAKL,QAASQ,aAAcH,KAAKN,cAAea,SAAUP,KAAKH,gBAChFQ,cAAgB,mDAEhBJ,YAAc,gDACdC,MAAQ,CAACI,OAAQN,KAAKL,QAASQ,aAAcH,KAAKN,oBAClDW,cAAgB,2CAS7BnB,KAAKsB,UAAUC,OAAS,eAChBC,KAAOV,KAGNA,KAAKF,QAICd,KAAK2B,KAAK,CAAC,CAClBC,WAAYZ,KAAKC,YACjBY,KAAMb,KAAKE,SAGN,GAAGY,MAAK,SAASC,SAElBL,KAAKX,eACLgB,QAAQvB,aAAc,GAE1BP,UAAU+B,OAAON,KAAKL,cAAeU,SAASD,MAAK,SAASG,KAAMC,IAC9DjC,UAAUkC,YAAYT,KAAKjB,aAAcwB,KAAMC,OAChDE,KAAKrC,aAAasC,cACtBD,KAAKrC,aAAasC,YAIzBnC,KAAKsB,UAAUf,aAAe,KAE9BP,KAAKsB,UAAUX,UAAY,KAE3BX,KAAKsB,UAAUV,OAAS,KAExBZ,KAAKsB,UAAUZ,QAAU,KAEzBV,KAAKsB,UAAUd,cAAgB,KAE/BR,KAAKsB,UAAUb,QAAU,KAEzBT,KAAKsB,UAAUP,YAAc,KAE7Bf,KAAKsB,UAAUN,MAAQ,KAEvBhB,KAAKsB,UAAUH,cAAgB,KAE/BnB,KAAKsB,UAAUT,cAAe,EAE2Bb"}
\ No newline at end of file
+{"version":3,"file":"user_competency_info.min.js","sources":["../src/user_competency_info.js"],"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 * Module to refresh a user competency summary in a page.\n *\n * @module tool_lp/user_competency_info\n * @copyright 2015 Damyon Wiese\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\ndefine(['jquery', 'core/notification', 'core/ajax', 'core/templates'], function($, notification, ajax, templates) {\n\n /**\n * Info\n *\n * @param {JQuery} rootElement Selector to replace when the information needs updating.\n * @param {Number} competencyId The id of the competency.\n * @param {Number} userId The id of the user.\n * @param {Number} planId The id of the plan.\n * @param {Number} courseId The id of the course.\n * @param {Boolean} displayuser If we should display the user info.\n */\n var Info = function(rootElement, competencyId, userId, planId, courseId, displayuser) {\n this._rootElement = rootElement;\n this._competencyId = competencyId;\n this._userId = userId;\n this._planId = planId;\n this._courseId = courseId;\n this._valid = true;\n this._displayuser = (typeof displayuser !== 'undefined') ? displayuser : false;\n\n if (this._planId) {\n this._methodName = 'tool_lp_data_for_user_competency_summary_in_plan';\n this._args = {competencyid: this._competencyId, planid: this._planId};\n this._templateName = 'tool_lp/user_competency_summary_in_plan';\n } else if (this._courseId) {\n this._methodName = 'tool_lp_data_for_user_competency_summary_in_course';\n this._args = {userid: this._userId, competencyid: this._competencyId, courseid: this._courseId};\n this._templateName = 'tool_lp/user_competency_summary_in_course';\n } else {\n this._methodName = 'tool_lp_data_for_user_competency_summary';\n this._args = {userid: this._userId, competencyid: this._competencyId};\n this._templateName = 'tool_lp/user_competency_summary';\n }\n };\n\n /**\n * Reload the info for this user competency.\n *\n * @method reload\n */\n Info.prototype.reload = function() {\n var self = this,\n promises = [];\n\n if (!this._valid) {\n return;\n }\n\n promises = ajax.call([{\n methodname: this._methodName,\n args: this._args\n }]);\n\n promises[0].done(function(context) {\n // Check if we should also the user info.\n if (self._displayuser) {\n context.displayuser = true;\n }\n templates.render(self._templateName, context).done(function(html, js) {\n templates.replaceNode(self._rootElement, html, js);\n }).fail(notification.exception);\n }).fail(notification.exception);\n };\n\n /** @property {JQuery} The root element to replace in the DOM. */\n Info.prototype._rootElement = null;\n /** @property {Number} The id of the course. */\n Info.prototype._courseId = null;\n /** @property {Boolean} Is this module valid? */\n Info.prototype._valid = null;\n /** @property {Number} The id of the plan. */\n Info.prototype._planId = null;\n /** @property {Number} The id of the competency. */\n Info.prototype._competencyId = null;\n /** @property {Number} The id of the user. */\n Info.prototype._userId = null;\n /** @property {String} The method name to load the data. */\n Info.prototype._methodName = null;\n /** @property {Object} The arguments to load the data. */\n Info.prototype._args = null;\n /** @property {String} The template to reload the fragment. */\n Info.prototype._templateName = null;\n /** @property {Boolean} If we should display the user info? */\n Info.prototype._displayuser = false;\n\n return /** @alias module:tool_lp/user_competency_info */ Info;\n\n});\n"],"names":["define","$","notification","ajax","templates","Info","rootElement","competencyId","userId","planId","courseId","displayuser","this","_rootElement","_competencyId","_userId","_planId","_courseId","_valid","_displayuser","_methodName","_args","competencyid","planid","_templateName","userid","courseid","prototype","reload","self","call","methodname","args","done","context","render","html","js","replaceNode","fail","exception"],"mappings":";;;;;;;AAuBAA,sCAAO,CAAC,SAAU,oBAAqB,YAAa,mBAAmB,SAASC,EAAGC,aAAcC,KAAMC,WAYnG,IAAIC,KAAO,SAASC,YAAaC,aAAcC,OAAQC,OAAQC,SAAUC,aACrEC,KAAKC,aAAeP,YACpBM,KAAKE,cAAgBP,aACrBK,KAAKG,QAAUP,OACfI,KAAKI,QAAUP,OACfG,KAAKK,UAAYP,SACjBE,KAAKM,QAAS,EACdN,KAAKO,kBAAuC,IAAhBR,aAA+BA,YAEvDC,KAAKI,SACLJ,KAAKQ,YAAc,mDACnBR,KAAKS,MAAQ,CAACC,aAAcV,KAAKE,cAAeS,OAAQX,KAAKI,SAC7DJ,KAAKY,cAAgB,2CACdZ,KAAKK,WACZL,KAAKQ,YAAc,qDACnBR,KAAKS,MAAQ,CAACI,OAAQb,KAAKG,QAASO,aAAcV,KAAKE,cAAeY,SAAUd,KAAKK,WACrFL,KAAKY,cAAgB,8CAErBZ,KAAKQ,YAAc,2CACnBR,KAAKS,MAAQ,CAACI,OAAQb,KAAKG,QAASO,aAAcV,KAAKE,eACvDF,KAAKY,cAAgB,oCAsD7B,OA7CAnB,KAAKsB,UAAUC,OAAS,WACpB,IAAIC,KAAOjB,KAGNA,KAAKM,QAICf,KAAK2B,KAAK,CAAC,CAClBC,WAAYnB,KAAKQ,YACjBY,KAAMpB,KAAKS,SAGN,GAAGY,MAAK,SAASC,SAElBL,KAAKV,eACLe,QAAQvB,aAAc,GAE1BP,UAAU+B,OAAON,KAAKL,cAAeU,SAASD,MAAK,SAASG,KAAMC,IAC9DjC,UAAUkC,YAAYT,KAAKhB,aAAcuB,KAAMC,GAClD,IAAEE,KAAKrC,aAAasC,UACxB,IAAED,KAAKrC,aAAasC,YAIzBnC,KAAKsB,UAAUd,aAAe,KAE9BR,KAAKsB,UAAUV,UAAY,KAE3BZ,KAAKsB,UAAUT,OAAS,KAExBb,KAAKsB,UAAUX,QAAU,KAEzBX,KAAKsB,UAAUb,cAAgB,KAE/BT,KAAKsB,UAAUZ,QAAU,KAEzBV,KAAKsB,UAAUP,YAAc,KAE7Bf,KAAKsB,UAAUN,MAAQ,KAEvBhB,KAAKsB,UAAUH,cAAgB,KAE/BnB,KAAKsB,UAAUR,cAAe,EAE2Bd,IAE7D"}
\ No newline at end of file
diff --git a/admin/tool/lp/amd/build/user_competency_plan_popup.min.js.map b/admin/tool/lp/amd/build/user_competency_plan_popup.min.js.map
index 14958c5e65c..e6b2ac15e1b 100644
--- a/admin/tool/lp/amd/build/user_competency_plan_popup.min.js.map
+++ b/admin/tool/lp/amd/build/user_competency_plan_popup.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"user_competency_plan_popup.min.js","sources":["../src/user_competency_plan_popup.js"],"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 * Module to open user competency plan in popup\n *\n * @module tool_lp/user_competency_plan_popup\n * @copyright 2016 Issam Taboubi \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\ndefine(['jquery', 'core/notification', 'core/str', 'core/ajax', 'core/templates', 'tool_lp/dialogue'],\n function($, notification, str, ajax, templates, Dialogue) {\n\n /**\n * UserCompetencyPopup\n *\n * @param {String} regionSelector The regionSelector\n * @param {String} userCompetencySelector The userCompetencySelector\n * @param {Number} planId The plan ID\n */\n var UserCompetencyPopup = function(regionSelector, userCompetencySelector, planId) {\n this._regionSelector = regionSelector;\n this._userCompetencySelector = userCompetencySelector;\n this._planId = planId;\n\n $(this._regionSelector).on('click', this._userCompetencySelector, this._handleClick.bind(this));\n };\n\n /**\n * Get the data from the closest TR and open the popup.\n *\n * @method _handleClick\n * @param {Event} e\n */\n UserCompetencyPopup.prototype._handleClick = function(e) {\n e.preventDefault();\n var tr = $(e.target).closest('tr');\n var competencyId = $(tr).data('competencyid');\n var userId = $(tr).data('userid');\n var planId = this._planId;\n\n var requests = ajax.call([{\n methodname: 'tool_lp_data_for_user_competency_summary_in_plan',\n args: {competencyid: competencyId, planid: planId},\n done: this._contextLoaded.bind(this),\n fail: notification.exception\n }]);\n // Log the user competency viewed in plan event.\n requests[0].then(function(result) {\n var eventMethodName = 'core_competency_user_competency_viewed_in_plan';\n // Trigger core_competency_user_competency_plan_viewed event instead if plan is already completed.\n if (result.plan.iscompleted) {\n eventMethodName = 'core_competency_user_competency_plan_viewed';\n }\n return ajax.call([{\n methodname: eventMethodName,\n args: {competencyid: competencyId, userid: userId, planid: planId}\n }])[0];\n }).catch(notification.exception);\n };\n\n /**\n * We loaded the context, now render the template.\n *\n * @method _contextLoaded\n * @param {Object} context\n */\n UserCompetencyPopup.prototype._contextLoaded = function(context) {\n var self = this;\n templates.render('tool_lp/user_competency_summary_in_plan', context).done(function(html, js) {\n str.get_string('usercompetencysummary', 'report_competency').done(function(title) {\n (new Dialogue(title, html, templates.runTemplateJS.bind(templates, js), self._refresh.bind(self), true));\n }).fail(notification.exception);\n }).fail(notification.exception);\n };\n\n /**\n * Refresh the page.\n *\n * @method _refresh\n */\n UserCompetencyPopup.prototype._refresh = function() {\n var planId = this._planId;\n\n ajax.call([{\n methodname: 'tool_lp_data_for_plan_page',\n args: {planid: planId},\n done: this._pageContextLoaded.bind(this),\n fail: notification.exception\n }]);\n };\n\n /**\n * We loaded the context, now render the template.\n *\n * @method _pageContextLoaded\n * @param {Object} context\n */\n UserCompetencyPopup.prototype._pageContextLoaded = function(context) {\n var self = this;\n templates.render('tool_lp/plan_page', context).done(function(html, js) {\n templates.replaceNode(self._regionSelector, html, js);\n }).fail(notification.exception);\n };\n\n /** @property {String} The selector for the region with the user competencies */\n UserCompetencyPopup.prototype._regionSelector = null;\n /** @property {String} The selector for the region with a single user competencies */\n UserCompetencyPopup.prototype._userCompetencySelector = null;\n /** @property {Number} The plan Id */\n UserCompetencyPopup.prototype._planId = null;\n\n return /** @alias module:tool_lp/user_competency_plan_popup */ UserCompetencyPopup;\n\n});\n"],"names":["define","$","notification","str","ajax","templates","Dialogue","UserCompetencyPopup","regionSelector","userCompetencySelector","planId","_regionSelector","_userCompetencySelector","_planId","this","on","_handleClick","bind","prototype","e","preventDefault","tr","target","closest","competencyId","data","userId","call","methodname","args","competencyid","planid","done","_contextLoaded","fail","exception","then","result","eventMethodName","plan","iscompleted","userid","catch","context","self","render","html","js","get_string","title","runTemplateJS","_refresh","_pageContextLoaded","replaceNode"],"mappings":";;;;;;;AAuBAA,4CAAO,CAAC,SAAU,oBAAqB,WAAY,YAAa,iBAAkB,qBAC3E,SAASC,EAAGC,aAAcC,IAAKC,KAAMC,UAAWC,cAS/CC,oBAAsB,SAASC,eAAgBC,uBAAwBC,aAClEC,gBAAkBH,oBAClBI,wBAA0BH,4BAC1BI,QAAUH,OAEfT,EAAEa,KAAKH,iBAAiBI,GAAG,QAASD,KAAKF,wBAAyBE,KAAKE,aAAaC,KAAKH,eAS7FP,oBAAoBW,UAAUF,aAAe,SAASG,GAClDA,EAAEC,qBACEC,GAAKpB,EAAEkB,EAAEG,QAAQC,QAAQ,MACzBC,aAAevB,EAAEoB,IAAII,KAAK,gBAC1BC,OAASzB,EAAEoB,IAAII,KAAK,UACpBf,OAASI,KAAKD,QAEHT,KAAKuB,KAAK,CAAC,CACtBC,WAAY,mDACZC,KAAM,CAACC,aAAcN,aAAcO,OAAQrB,QAC3CsB,KAAMlB,KAAKmB,eAAehB,KAAKH,MAC/BoB,KAAMhC,aAAaiC,aAGd,GAAGC,MAAK,SAASC,YAClBC,gBAAkB,wDAElBD,OAAOE,KAAKC,cACZF,gBAAkB,+CAEflC,KAAKuB,KAAK,CAAC,CACdC,WAAYU,gBACZT,KAAM,CAACC,aAAcN,aAAciB,OAAQf,OAAQK,OAAQrB,WAC3D,MACLgC,MAAMxC,aAAaiC,YAS1B5B,oBAAoBW,UAAUe,eAAiB,SAASU,aAChDC,KAAO9B,KACXT,UAAUwC,OAAO,0CAA2CF,SAASX,MAAK,SAASc,KAAMC,IACrF5C,IAAI6C,WAAW,wBAAyB,qBAAqBhB,MAAK,SAASiB,WAClE3C,SAAS2C,MAAOH,KAAMzC,UAAU6C,cAAcjC,KAAKZ,UAAW0C,IAAKH,KAAKO,SAASlC,KAAK2B,OAAO,MACnGV,KAAKhC,aAAaiC,cACtBD,KAAKhC,aAAaiC,YAQzB5B,oBAAoBW,UAAUiC,SAAW,eACjCzC,OAASI,KAAKD,QAElBT,KAAKuB,KAAK,CAAC,CACPC,WAAY,6BACZC,KAAM,CAACE,OAAQrB,QACfsB,KAAMlB,KAAKsC,mBAAmBnC,KAAKH,MACnCoB,KAAMhC,aAAaiC,cAU3B5B,oBAAoBW,UAAUkC,mBAAqB,SAAST,aACpDC,KAAO9B,KACXT,UAAUwC,OAAO,oBAAqBF,SAASX,MAAK,SAASc,KAAMC,IAC/D1C,UAAUgD,YAAYT,KAAKjC,gBAAiBmC,KAAMC,OACnDb,KAAKhC,aAAaiC,YAIzB5B,oBAAoBW,UAAUP,gBAAkB,KAEhDJ,oBAAoBW,UAAUN,wBAA0B,KAExDL,oBAAoBW,UAAUL,QAAU,KAEuBN"}
\ No newline at end of file
+{"version":3,"file":"user_competency_plan_popup.min.js","sources":["../src/user_competency_plan_popup.js"],"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 * Module to open user competency plan in popup\n *\n * @module tool_lp/user_competency_plan_popup\n * @copyright 2016 Issam Taboubi \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\ndefine(['jquery', 'core/notification', 'core/str', 'core/ajax', 'core/templates', 'tool_lp/dialogue'],\n function($, notification, str, ajax, templates, Dialogue) {\n\n /**\n * UserCompetencyPopup\n *\n * @param {String} regionSelector The regionSelector\n * @param {String} userCompetencySelector The userCompetencySelector\n * @param {Number} planId The plan ID\n */\n var UserCompetencyPopup = function(regionSelector, userCompetencySelector, planId) {\n this._regionSelector = regionSelector;\n this._userCompetencySelector = userCompetencySelector;\n this._planId = planId;\n\n $(this._regionSelector).on('click', this._userCompetencySelector, this._handleClick.bind(this));\n };\n\n /**\n * Get the data from the closest TR and open the popup.\n *\n * @method _handleClick\n * @param {Event} e\n */\n UserCompetencyPopup.prototype._handleClick = function(e) {\n e.preventDefault();\n var tr = $(e.target).closest('tr');\n var competencyId = $(tr).data('competencyid');\n var userId = $(tr).data('userid');\n var planId = this._planId;\n\n var requests = ajax.call([{\n methodname: 'tool_lp_data_for_user_competency_summary_in_plan',\n args: {competencyid: competencyId, planid: planId},\n done: this._contextLoaded.bind(this),\n fail: notification.exception\n }]);\n // Log the user competency viewed in plan event.\n requests[0].then(function(result) {\n var eventMethodName = 'core_competency_user_competency_viewed_in_plan';\n // Trigger core_competency_user_competency_plan_viewed event instead if plan is already completed.\n if (result.plan.iscompleted) {\n eventMethodName = 'core_competency_user_competency_plan_viewed';\n }\n return ajax.call([{\n methodname: eventMethodName,\n args: {competencyid: competencyId, userid: userId, planid: planId}\n }])[0];\n }).catch(notification.exception);\n };\n\n /**\n * We loaded the context, now render the template.\n *\n * @method _contextLoaded\n * @param {Object} context\n */\n UserCompetencyPopup.prototype._contextLoaded = function(context) {\n var self = this;\n templates.render('tool_lp/user_competency_summary_in_plan', context).done(function(html, js) {\n str.get_string('usercompetencysummary', 'report_competency').done(function(title) {\n (new Dialogue(title, html, templates.runTemplateJS.bind(templates, js), self._refresh.bind(self), true));\n }).fail(notification.exception);\n }).fail(notification.exception);\n };\n\n /**\n * Refresh the page.\n *\n * @method _refresh\n */\n UserCompetencyPopup.prototype._refresh = function() {\n var planId = this._planId;\n\n ajax.call([{\n methodname: 'tool_lp_data_for_plan_page',\n args: {planid: planId},\n done: this._pageContextLoaded.bind(this),\n fail: notification.exception\n }]);\n };\n\n /**\n * We loaded the context, now render the template.\n *\n * @method _pageContextLoaded\n * @param {Object} context\n */\n UserCompetencyPopup.prototype._pageContextLoaded = function(context) {\n var self = this;\n templates.render('tool_lp/plan_page', context).done(function(html, js) {\n templates.replaceNode(self._regionSelector, html, js);\n }).fail(notification.exception);\n };\n\n /** @property {String} The selector for the region with the user competencies */\n UserCompetencyPopup.prototype._regionSelector = null;\n /** @property {String} The selector for the region with a single user competencies */\n UserCompetencyPopup.prototype._userCompetencySelector = null;\n /** @property {Number} The plan Id */\n UserCompetencyPopup.prototype._planId = null;\n\n return /** @alias module:tool_lp/user_competency_plan_popup */ UserCompetencyPopup;\n\n});\n"],"names":["define","$","notification","str","ajax","templates","Dialogue","UserCompetencyPopup","regionSelector","userCompetencySelector","planId","this","_regionSelector","_userCompetencySelector","_planId","on","_handleClick","bind","prototype","e","preventDefault","tr","target","closest","competencyId","data","userId","call","methodname","args","competencyid","planid","done","_contextLoaded","fail","exception","then","result","eventMethodName","plan","iscompleted","userid","catch","context","self","render","html","js","get_string","title","runTemplateJS","_refresh","_pageContextLoaded","replaceNode"],"mappings":";;;;;;;AAuBAA,OAAM,qCAAC,CAAC,SAAU,oBAAqB,WAAY,YAAa,iBAAkB,qBAC3E,SAASC,EAAGC,aAAcC,IAAKC,KAAMC,UAAWC,UASnD,IAAIC,oBAAsB,SAASC,eAAgBC,uBAAwBC,QACvEC,KAAKC,gBAAkBJ,eACvBG,KAAKE,wBAA0BJ,uBAC/BE,KAAKG,QAAUJ,OAEfT,EAAEU,KAAKC,iBAAiBG,GAAG,QAASJ,KAAKE,wBAAyBF,KAAKK,aAAaC,KAAKN,QAuF7F,OA9EAJ,oBAAoBW,UAAUF,aAAe,SAASG,GAClDA,EAAEC,iBACF,IAAIC,GAAKpB,EAAEkB,EAAEG,QAAQC,QAAQ,MACzBC,aAAevB,EAAEoB,IAAII,KAAK,gBAC1BC,OAASzB,EAAEoB,IAAII,KAAK,UACpBf,OAASC,KAAKG,QAEHV,KAAKuB,KAAK,CAAC,CACtBC,WAAY,mDACZC,KAAM,CAACC,aAAcN,aAAcO,OAAQrB,QAC3CsB,KAAMrB,KAAKsB,eAAehB,KAAKN,MAC/BuB,KAAMhC,aAAaiC,aAGd,GAAGC,MAAK,SAASC,QACtB,IAAIC,gBAAkB,iDAKtB,OAHID,OAAOE,KAAKC,cACZF,gBAAkB,+CAEflC,KAAKuB,KAAK,CAAC,CACdC,WAAYU,gBACZT,KAAM,CAACC,aAAcN,aAAciB,OAAQf,OAAQK,OAAQrB,WAC3D,EACP,IAAEgC,MAAMxC,aAAaiC,YAS1B5B,oBAAoBW,UAAUe,eAAiB,SAASU,SACpD,IAAIC,KAAOjC,KACXN,UAAUwC,OAAO,0CAA2CF,SAASX,MAAK,SAASc,KAAMC,IACrF5C,IAAI6C,WAAW,wBAAyB,qBAAqBhB,MAAK,SAASiB,OACtE,IAAI3C,SAAS2C,MAAOH,KAAMzC,UAAU6C,cAAcjC,KAAKZ,UAAW0C,IAAKH,KAAKO,SAASlC,KAAK2B,OAAO,EACrG,IAAEV,KAAKhC,aAAaiC,UACxB,IAAED,KAAKhC,aAAaiC,YAQzB5B,oBAAoBW,UAAUiC,SAAW,WACrC,IAAIzC,OAASC,KAAKG,QAElBV,KAAKuB,KAAK,CAAC,CACPC,WAAY,6BACZC,KAAM,CAACE,OAAQrB,QACfsB,KAAMrB,KAAKyC,mBAAmBnC,KAAKN,MACnCuB,KAAMhC,aAAaiC,cAU3B5B,oBAAoBW,UAAUkC,mBAAqB,SAAST,SACxD,IAAIC,KAAOjC,KACXN,UAAUwC,OAAO,oBAAqBF,SAASX,MAAK,SAASc,KAAMC,IAC/D1C,UAAUgD,YAAYT,KAAKhC,gBAAiBkC,KAAMC,GACrD,IAAEb,KAAKhC,aAAaiC,YAIzB5B,oBAAoBW,UAAUN,gBAAkB,KAEhDL,oBAAoBW,UAAUL,wBAA0B,KAExDN,oBAAoBW,UAAUJ,QAAU,KAEuBP,mBAEnE"}
\ No newline at end of file
diff --git a/admin/tool/lp/amd/build/user_competency_workflow.min.js.map b/admin/tool/lp/amd/build/user_competency_workflow.min.js.map
index 6828088a678..b5e6fd2665d 100644
--- a/admin/tool/lp/amd/build/user_competency_workflow.min.js.map
+++ b/admin/tool/lp/amd/build/user_competency_workflow.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"user_competency_workflow.min.js","sources":["../src/user_competency_workflow.js"],"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 * User competency workflow.\n *\n * @module tool_lp/user_competency_workflow\n * @copyright 2015 Frédéric Massart - FMCorz.net\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(['jquery',\n 'core/templates',\n 'core/ajax',\n 'core/notification',\n 'core/str',\n 'tool_lp/menubar',\n 'tool_lp/event_base'],\n function($, Templates, Ajax, Notification, Str, Menubar, EventBase) {\n\n /**\n * UserCompetencyWorkflow class.\n */\n var UserCompetencyWorkflow = function() {\n EventBase.prototype.constructor.apply(this, []);\n };\n UserCompetencyWorkflow.prototype = Object.create(EventBase.prototype);\n\n /** @property {String} The selector to find the user competency data. */\n UserCompetencyWorkflow.prototype._nodeSelector = '[data-node=\"user-competency\"]';\n\n /**\n * Cancel a review request and refresh the view.\n *\n * @param {Object} data The user competency data.\n * @method _cancelReviewRequest\n */\n UserCompetencyWorkflow.prototype._cancelReviewRequest = function(data) {\n var call = {\n methodname: 'core_competency_user_competency_cancel_review_request',\n args: {\n userid: data.userid,\n competencyid: data.competencyid\n }\n };\n\n Ajax.call([call])[0].then(function() {\n this._trigger('review-request-cancelled', data);\n this._trigger('status-changed', data);\n }.bind(this)).catch(function() {\n this._trigger('error-occured', data);\n }.bind(this));\n };\n\n /**\n * Cancel a review request an refresh the view.\n *\n * @param {Object} data The user competency data.\n * @method cancelReviewRequest\n */\n UserCompetencyWorkflow.prototype.cancelReviewRequest = function(data) {\n this._cancelReviewRequest(data);\n };\n\n /**\n * Cancel a review request handler.\n *\n * @param {Event} e The event.\n * @method _cancelReviewRequestHandler\n */\n UserCompetencyWorkflow.prototype._cancelReviewRequestHandler = function(e) {\n e.preventDefault();\n var data = this._findUserCompetencyData($(e.target));\n this.cancelReviewRequest(data);\n };\n\n /**\n * Request a review and refresh the view.\n *\n * @param {Object} data The user competency data.\n * @method _requestReview\n */\n UserCompetencyWorkflow.prototype._requestReview = function(data) {\n var call = {\n methodname: 'core_competency_user_competency_request_review',\n args: {\n userid: data.userid,\n competencyid: data.competencyid\n }\n };\n\n Ajax.call([call])[0].then(function() {\n this._trigger('review-requested', data);\n this._trigger('status-changed', data);\n }.bind(this)).catch(function() {\n this._trigger('error-occured', data);\n }.bind(this));\n };\n\n /**\n * Request a review.\n *\n * @param {Object} data The user competency data.\n * @method requestReview\n */\n UserCompetencyWorkflow.prototype.requestReview = function(data) {\n this._requestReview(data);\n };\n\n /**\n * Request a review handler.\n *\n * @param {Event} e The event.\n * @method _requestReviewHandler\n */\n UserCompetencyWorkflow.prototype._requestReviewHandler = function(e) {\n e.preventDefault();\n var data = this._findUserCompetencyData($(e.target));\n this.requestReview(data);\n };\n\n /**\n * Start a review and refresh the view.\n *\n * @param {Object} data The user competency data.\n * @method _startReview\n */\n UserCompetencyWorkflow.prototype._startReview = function(data) {\n var call = {\n methodname: 'core_competency_user_competency_start_review',\n args: {\n userid: data.userid,\n competencyid: data.competencyid\n }\n };\n Ajax.call([call])[0].then(function() {\n this._trigger('review-started', data);\n this._trigger('status-changed', data);\n }.bind(this)).catch(function() {\n this._trigger('error-occured', data);\n }.bind(this));\n };\n\n /**\n * Start a review.\n *\n * @param {Object} data The user competency data.\n * @method startReview\n */\n UserCompetencyWorkflow.prototype.startReview = function(data) {\n this._startReview(data);\n };\n\n /**\n * Start a review handler.\n *\n * @param {Event} e The event.\n * @method _startReviewHandler\n */\n UserCompetencyWorkflow.prototype._startReviewHandler = function(e) {\n e.preventDefault();\n var data = this._findUserCompetencyData($(e.target));\n this.startReview(data);\n };\n\n /**\n * Stop a review and refresh the view.\n *\n * @param {Object} data The user competency data.\n * @method _stopReview\n */\n UserCompetencyWorkflow.prototype._stopReview = function(data) {\n var call = {\n methodname: 'core_competency_user_competency_stop_review',\n args: {\n userid: data.userid,\n competencyid: data.competencyid\n }\n };\n\n Ajax.call([call])[0].then(function() {\n this._trigger('review-stopped', data);\n this._trigger('status-changed', data);\n }.bind(this)).catch(function() {\n this._trigger('error-occured', data);\n }.bind(this));\n };\n\n /**\n * Stop a review.\n *\n * @param {Object} data The user competency data.\n * @method stopReview\n */\n UserCompetencyWorkflow.prototype.stopReview = function(data) {\n this._stopReview(data);\n };\n\n /**\n * Stop a review handler.\n *\n * @param {Event} e The event.\n * @method _stopReviewHandler\n */\n UserCompetencyWorkflow.prototype._stopReviewHandler = function(e) {\n e.preventDefault();\n var data = this._findUserCompetencyData($(e.target));\n this.stopReview(data);\n };\n\n /**\n * Enhance a menu bar.\n *\n * @param {String} selector Menubar selector.\n */\n UserCompetencyWorkflow.prototype.enhanceMenubar = function(selector) {\n Menubar.enhance(selector, {\n '[data-action=\"request-review\"]': this._requestReviewHandler.bind(this),\n '[data-action=\"cancel-review-request\"]': this._cancelReviewRequestHandler.bind(this),\n });\n };\n\n /**\n * Find the user competency data from a node.\n *\n * @param {Node} node The node to search from.\n * @return {Object} User competency data.\n */\n UserCompetencyWorkflow.prototype._findUserCompetencyData = function(node) {\n var parent = node.parents(this._nodeSelector),\n data;\n\n if (parent.length != 1) {\n throw new Error('The evidence node was not located.');\n }\n\n data = parent.data();\n if (typeof data === 'undefined' || typeof data.userid === 'undefined' || typeof data.competencyid === 'undefined') {\n throw new Error('User competency data could not be found.');\n }\n\n return data;\n };\n\n /**\n * Enhance a menu bar.\n *\n * @param {String} selector Menubar selector.\n */\n UserCompetencyWorkflow.prototype.enhanceMenubar = function(selector) {\n Menubar.enhance(selector, {\n '[data-action=\"request-review\"]': this._requestReviewHandler.bind(this),\n '[data-action=\"cancel-review-request\"]': this._cancelReviewRequestHandler.bind(this),\n '[data-action=\"start-review\"]': this._startReviewHandler.bind(this),\n '[data-action=\"stop-review\"]': this._stopReviewHandler.bind(this),\n });\n };\n\n /**\n * Register the events in the region.\n *\n * @param {String} selector The base selector to search nodes in and attach events.\n */\n UserCompetencyWorkflow.prototype.registerEvents = function(selector) {\n var wrapper = $(selector);\n\n wrapper.find('[data-action=\"request-review\"]').click(this._requestReviewHandler.bind(this));\n wrapper.find('[data-action=\"cancel-review-request\"]').click(this._cancelReviewRequestHandler.bind(this));\n wrapper.find('[data-action=\"start-review\"]').click(this._startReviewHandler.bind(this));\n wrapper.find('[data-action=\"stop-review\"]').click(this._stopReviewHandler.bind(this));\n };\n\n return /** @alias module:tool_lp/user_competency_actions */ UserCompetencyWorkflow;\n});\n"],"names":["define","$","Templates","Ajax","Notification","Str","Menubar","EventBase","UserCompetencyWorkflow","prototype","constructor","apply","this","Object","create","_nodeSelector","_cancelReviewRequest","data","call","methodname","args","userid","competencyid","then","_trigger","bind","catch","cancelReviewRequest","_cancelReviewRequestHandler","e","preventDefault","_findUserCompetencyData","target","_requestReview","requestReview","_requestReviewHandler","_startReview","startReview","_startReviewHandler","_stopReview","stopReview","_stopReviewHandler","enhanceMenubar","selector","enhance","node","parent","parents","length","Error","registerEvents","wrapper","find","click"],"mappings":"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"}
\ No newline at end of file
+{"version":3,"file":"user_competency_workflow.min.js","sources":["../src/user_competency_workflow.js"],"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 * User competency workflow.\n *\n * @module tool_lp/user_competency_workflow\n * @copyright 2015 Frédéric Massart - FMCorz.net\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(['jquery',\n 'core/templates',\n 'core/ajax',\n 'core/notification',\n 'core/str',\n 'tool_lp/menubar',\n 'tool_lp/event_base'],\n function($, Templates, Ajax, Notification, Str, Menubar, EventBase) {\n\n /**\n * UserCompetencyWorkflow class.\n */\n var UserCompetencyWorkflow = function() {\n EventBase.prototype.constructor.apply(this, []);\n };\n UserCompetencyWorkflow.prototype = Object.create(EventBase.prototype);\n\n /** @property {String} The selector to find the user competency data. */\n UserCompetencyWorkflow.prototype._nodeSelector = '[data-node=\"user-competency\"]';\n\n /**\n * Cancel a review request and refresh the view.\n *\n * @param {Object} data The user competency data.\n * @method _cancelReviewRequest\n */\n UserCompetencyWorkflow.prototype._cancelReviewRequest = function(data) {\n var call = {\n methodname: 'core_competency_user_competency_cancel_review_request',\n args: {\n userid: data.userid,\n competencyid: data.competencyid\n }\n };\n\n Ajax.call([call])[0].then(function() {\n this._trigger('review-request-cancelled', data);\n this._trigger('status-changed', data);\n }.bind(this)).catch(function() {\n this._trigger('error-occured', data);\n }.bind(this));\n };\n\n /**\n * Cancel a review request an refresh the view.\n *\n * @param {Object} data The user competency data.\n * @method cancelReviewRequest\n */\n UserCompetencyWorkflow.prototype.cancelReviewRequest = function(data) {\n this._cancelReviewRequest(data);\n };\n\n /**\n * Cancel a review request handler.\n *\n * @param {Event} e The event.\n * @method _cancelReviewRequestHandler\n */\n UserCompetencyWorkflow.prototype._cancelReviewRequestHandler = function(e) {\n e.preventDefault();\n var data = this._findUserCompetencyData($(e.target));\n this.cancelReviewRequest(data);\n };\n\n /**\n * Request a review and refresh the view.\n *\n * @param {Object} data The user competency data.\n * @method _requestReview\n */\n UserCompetencyWorkflow.prototype._requestReview = function(data) {\n var call = {\n methodname: 'core_competency_user_competency_request_review',\n args: {\n userid: data.userid,\n competencyid: data.competencyid\n }\n };\n\n Ajax.call([call])[0].then(function() {\n this._trigger('review-requested', data);\n this._trigger('status-changed', data);\n }.bind(this)).catch(function() {\n this._trigger('error-occured', data);\n }.bind(this));\n };\n\n /**\n * Request a review.\n *\n * @param {Object} data The user competency data.\n * @method requestReview\n */\n UserCompetencyWorkflow.prototype.requestReview = function(data) {\n this._requestReview(data);\n };\n\n /**\n * Request a review handler.\n *\n * @param {Event} e The event.\n * @method _requestReviewHandler\n */\n UserCompetencyWorkflow.prototype._requestReviewHandler = function(e) {\n e.preventDefault();\n var data = this._findUserCompetencyData($(e.target));\n this.requestReview(data);\n };\n\n /**\n * Start a review and refresh the view.\n *\n * @param {Object} data The user competency data.\n * @method _startReview\n */\n UserCompetencyWorkflow.prototype._startReview = function(data) {\n var call = {\n methodname: 'core_competency_user_competency_start_review',\n args: {\n userid: data.userid,\n competencyid: data.competencyid\n }\n };\n Ajax.call([call])[0].then(function() {\n this._trigger('review-started', data);\n this._trigger('status-changed', data);\n }.bind(this)).catch(function() {\n this._trigger('error-occured', data);\n }.bind(this));\n };\n\n /**\n * Start a review.\n *\n * @param {Object} data The user competency data.\n * @method startReview\n */\n UserCompetencyWorkflow.prototype.startReview = function(data) {\n this._startReview(data);\n };\n\n /**\n * Start a review handler.\n *\n * @param {Event} e The event.\n * @method _startReviewHandler\n */\n UserCompetencyWorkflow.prototype._startReviewHandler = function(e) {\n e.preventDefault();\n var data = this._findUserCompetencyData($(e.target));\n this.startReview(data);\n };\n\n /**\n * Stop a review and refresh the view.\n *\n * @param {Object} data The user competency data.\n * @method _stopReview\n */\n UserCompetencyWorkflow.prototype._stopReview = function(data) {\n var call = {\n methodname: 'core_competency_user_competency_stop_review',\n args: {\n userid: data.userid,\n competencyid: data.competencyid\n }\n };\n\n Ajax.call([call])[0].then(function() {\n this._trigger('review-stopped', data);\n this._trigger('status-changed', data);\n }.bind(this)).catch(function() {\n this._trigger('error-occured', data);\n }.bind(this));\n };\n\n /**\n * Stop a review.\n *\n * @param {Object} data The user competency data.\n * @method stopReview\n */\n UserCompetencyWorkflow.prototype.stopReview = function(data) {\n this._stopReview(data);\n };\n\n /**\n * Stop a review handler.\n *\n * @param {Event} e The event.\n * @method _stopReviewHandler\n */\n UserCompetencyWorkflow.prototype._stopReviewHandler = function(e) {\n e.preventDefault();\n var data = this._findUserCompetencyData($(e.target));\n this.stopReview(data);\n };\n\n /**\n * Enhance a menu bar.\n *\n * @param {String} selector Menubar selector.\n */\n UserCompetencyWorkflow.prototype.enhanceMenubar = function(selector) {\n Menubar.enhance(selector, {\n '[data-action=\"request-review\"]': this._requestReviewHandler.bind(this),\n '[data-action=\"cancel-review-request\"]': this._cancelReviewRequestHandler.bind(this),\n });\n };\n\n /**\n * Find the user competency data from a node.\n *\n * @param {Node} node The node to search from.\n * @return {Object} User competency data.\n */\n UserCompetencyWorkflow.prototype._findUserCompetencyData = function(node) {\n var parent = node.parents(this._nodeSelector),\n data;\n\n if (parent.length != 1) {\n throw new Error('The evidence node was not located.');\n }\n\n data = parent.data();\n if (typeof data === 'undefined' || typeof data.userid === 'undefined' || typeof data.competencyid === 'undefined') {\n throw new Error('User competency data could not be found.');\n }\n\n return data;\n };\n\n /**\n * Enhance a menu bar.\n *\n * @param {String} selector Menubar selector.\n */\n UserCompetencyWorkflow.prototype.enhanceMenubar = function(selector) {\n Menubar.enhance(selector, {\n '[data-action=\"request-review\"]': this._requestReviewHandler.bind(this),\n '[data-action=\"cancel-review-request\"]': this._cancelReviewRequestHandler.bind(this),\n '[data-action=\"start-review\"]': this._startReviewHandler.bind(this),\n '[data-action=\"stop-review\"]': this._stopReviewHandler.bind(this),\n });\n };\n\n /**\n * Register the events in the region.\n *\n * @param {String} selector The base selector to search nodes in and attach events.\n */\n UserCompetencyWorkflow.prototype.registerEvents = function(selector) {\n var wrapper = $(selector);\n\n wrapper.find('[data-action=\"request-review\"]').click(this._requestReviewHandler.bind(this));\n wrapper.find('[data-action=\"cancel-review-request\"]').click(this._cancelReviewRequestHandler.bind(this));\n wrapper.find('[data-action=\"start-review\"]').click(this._startReviewHandler.bind(this));\n wrapper.find('[data-action=\"stop-review\"]').click(this._stopReviewHandler.bind(this));\n };\n\n return /** @alias module:tool_lp/user_competency_actions */ UserCompetencyWorkflow;\n});\n"],"names":["define","$","Templates","Ajax","Notification","Str","Menubar","EventBase","UserCompetencyWorkflow","prototype","constructor","apply","this","Object","create","_nodeSelector","_cancelReviewRequest","data","call","methodname","args","userid","competencyid","then","_trigger","bind","catch","cancelReviewRequest","_cancelReviewRequestHandler","e","preventDefault","_findUserCompetencyData","target","_requestReview","requestReview","_requestReviewHandler","_startReview","startReview","_startReviewHandler","_stopReview","stopReview","_stopReviewHandler","enhanceMenubar","selector","enhance","node","parent","parents","length","Error","registerEvents","wrapper","find","click"],"mappings":"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"}
\ No newline at end of file
diff --git a/admin/tool/lp/amd/build/user_evidence_actions.min.js.map b/admin/tool/lp/amd/build/user_evidence_actions.min.js.map
index 719e3f31120..a7704095990 100644
--- a/admin/tool/lp/amd/build/user_evidence_actions.min.js.map
+++ b/admin/tool/lp/amd/build/user_evidence_actions.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"user_evidence_actions.min.js","sources":["../src/user_evidence_actions.js"],"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 * User evidence actions.\n *\n * @module tool_lp/user_evidence_actions\n * @copyright 2015 Frédéric Massart - FMCorz.net\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(['jquery',\n 'core/templates',\n 'core/ajax',\n 'core/notification',\n 'core/str',\n 'tool_lp/menubar',\n 'tool_lp/competencypicker_user_plans'],\n function($, templates, ajax, notification, str, Menubar, PickerUserPlans) {\n\n /**\n * UserEvidenceActions class.\n *\n * Note that presently this cannot be instantiated more than once per page.\n *\n * @param {String} type The type of page we're in.\n */\n var UserEvidenceActions = function(type) {\n this._type = type;\n\n if (type === 'evidence') {\n // This is the page to view one evidence.\n this._region = '[data-region=\"user-evidence-page\"]';\n this._evidenceNode = '[data-region=\"user-evidence-page\"]';\n this._template = 'tool_lp/user_evidence_page';\n this._contextMethod = 'tool_lp_data_for_user_evidence_page';\n\n } else if (type === 'list') {\n // This is the page to view a list of evidence.\n this._region = '[data-region=\"user-evidence-list\"]';\n this._evidenceNode = '[data-region=\"user-evidence-node\"]';\n this._template = 'tool_lp/user_evidence_list_page';\n this._contextMethod = 'tool_lp_data_for_user_evidence_list_page';\n\n } else {\n throw new TypeError('Unexpected type.');\n }\n };\n\n /** @property {String} Ajax method to fetch the page data from. */\n UserEvidenceActions.prototype._contextMethod = null;\n /** @property {String} Selector to find the node describing the evidence. */\n UserEvidenceActions.prototype._evidenceNode = null;\n /** @property {String} Selector mapping to the region to update. Usually similar to wrapper. */\n UserEvidenceActions.prototype._region = null;\n /** @property {String} Name of the template used to render the region. */\n UserEvidenceActions.prototype._template = null;\n /** @property {String} Type of page/region we're in. */\n UserEvidenceActions.prototype._type = null;\n\n /**\n * Resolve the arguments to refresh the region.\n *\n * @param {Object} evidenceData Evidence data from evidence node.\n * @return {Object} List of arguments.\n */\n UserEvidenceActions.prototype._getContextArgs = function(evidenceData) {\n var self = this,\n args = {};\n\n if (self._type === 'evidence') {\n args = {\n id: evidenceData.id\n };\n\n } else if (self._type === 'list') {\n args = {\n userid: evidenceData.userid\n };\n }\n\n return args;\n };\n\n /**\n * Callback to render the region template.\n *\n * @param {Object} context The context for the template.\n * @return {Promise}\n */\n UserEvidenceActions.prototype._renderView = function(context) {\n var self = this;\n return templates.render(self._template, context)\n .then(function(newhtml, newjs) {\n templates.replaceNode($(self._region), newhtml, newjs);\n return;\n });\n };\n\n /**\n * Call multiple ajax methods, and refresh.\n *\n * @param {Array} calls List of Ajax calls.\n * @param {Object} evidenceData Evidence data from evidence node.\n * @return {Promise}\n */\n UserEvidenceActions.prototype._callAndRefresh = function(calls, evidenceData) {\n var self = this;\n calls.push({\n methodname: self._contextMethod,\n args: self._getContextArgs(evidenceData)\n });\n\n // Apply all the promises, and refresh when the last one is resolved.\n return $.when.apply($.when, ajax.call(calls))\n .then(function() {\n return self._renderView(arguments[arguments.length - 1]);\n })\n .fail(notification.exception);\n };\n\n /**\n * Delete a plan and reload the region.\n *\n * @param {Object} evidenceData Evidence data from evidence node.\n */\n UserEvidenceActions.prototype._doDelete = function(evidenceData) {\n var self = this,\n calls = [{\n methodname: 'core_competency_delete_user_evidence',\n args: {id: evidenceData.id}\n }];\n self._callAndRefresh(calls, evidenceData);\n };\n\n /**\n * Delete a plan.\n *\n * @param {Object} evidenceData Evidence data from evidence node.\n */\n UserEvidenceActions.prototype.deleteEvidence = function(evidenceData) {\n var self = this,\n requests;\n\n requests = ajax.call([{\n methodname: 'core_competency_read_user_evidence',\n args: {id: evidenceData.id}\n }]);\n\n requests[0].done(function(evidence) {\n str.get_strings([\n {key: 'confirm', component: 'moodle'},\n {key: 'deleteuserevidence', component: 'tool_lp', param: evidence.name},\n {key: 'delete', component: 'moodle'},\n {key: 'cancel', component: 'moodle'}\n ]).done(function(strings) {\n notification.confirm(\n strings[0], // Confirm.\n strings[1], // Delete evidence X?\n strings[2], // Delete.\n strings[3], // Cancel.\n function() {\n self._doDelete(evidenceData);\n }\n );\n }).fail(notification.exception);\n }).fail(notification.exception);\n\n };\n\n /**\n * Delete evidence handler.\n *\n * @param {Event} e The event.\n */\n UserEvidenceActions.prototype._deleteEvidenceHandler = function(e) {\n e.preventDefault();\n var data = this._findEvidenceData($(e.target));\n this.deleteEvidence(data);\n };\n\n /**\n * Link a competency and reload.\n *\n * @param {Object} evidenceData Evidence data from evidence node.\n * @param {Number} competencyIds The competency IDs.\n */\n UserEvidenceActions.prototype._doCreateUserEvidenceCompetency = function(evidenceData, competencyIds) {\n var self = this,\n calls = [];\n\n $.each(competencyIds, function(index, competencyId) {\n calls.push({\n methodname: 'core_competency_create_user_evidence_competency',\n args: {\n userevidenceid: evidenceData.id,\n competencyid: competencyId,\n }\n });\n });\n\n self._callAndRefresh(calls, evidenceData);\n };\n\n /**\n * Create a user evidence competency.\n *\n * @param {Object} evidenceData Evidence data from evidence node.\n */\n UserEvidenceActions.prototype.createUserEvidenceCompetency = function(evidenceData) {\n var self = this,\n picker = new PickerUserPlans(evidenceData.userid);\n\n picker.on('save', function(e, data) {\n var competencyIds = data.competencyIds;\n self._doCreateUserEvidenceCompetency(evidenceData, competencyIds, data.requestReview);\n });\n\n picker.display();\n };\n\n /**\n * Create user evidence competency handler.\n *\n * @param {Event} e The event.\n */\n UserEvidenceActions.prototype._createUserEvidenceCompetencyHandler = function(e) {\n e.preventDefault();\n var data = this._findEvidenceData($(e.target));\n this.createUserEvidenceCompetency(data);\n };\n\n /**\n * Remove a linked competency and reload.\n *\n * @param {Object} evidenceData Evidence data from evidence node.\n * @param {Number} competencyId The competency ID.\n */\n UserEvidenceActions.prototype._doDeleteUserEvidenceCompetency = function(evidenceData, competencyId) {\n var self = this,\n calls = [];\n\n calls.push({\n methodname: 'core_competency_delete_user_evidence_competency',\n args: {\n userevidenceid: evidenceData.id,\n competencyid: competencyId,\n }\n });\n\n self._callAndRefresh(calls, evidenceData);\n };\n\n /**\n * Delete a user evidence competency.\n *\n * @param {Object} evidenceData Evidence data from evidence node.\n * @param {Number} competencyId The competency ID.\n */\n UserEvidenceActions.prototype.deleteUserEvidenceCompetency = function(evidenceData, competencyId) {\n this._doDeleteUserEvidenceCompetency(evidenceData, competencyId);\n };\n\n /**\n * Delete user evidence competency handler.\n *\n * @param {Event} e The event.\n */\n UserEvidenceActions.prototype._deleteUserEvidenceCompetencyHandler = function(e) {\n var data = this._findEvidenceData($(e.currentTarget)),\n competencyId = $(e.currentTarget).data('id');\n e.preventDefault();\n this.deleteUserEvidenceCompetency(data, competencyId);\n };\n\n /**\n * Send request review for user evidence competencies and reload the region.\n *\n * @param {Object} evidenceData Evidence data from evidence node.\n */\n UserEvidenceActions.prototype._doReviewUserEvidenceCompetencies = function(evidenceData) {\n var self = this,\n calls = [{\n methodname: 'core_competency_request_review_of_user_evidence_linked_competencies',\n args: {id: evidenceData.id}\n }];\n self._callAndRefresh(calls, evidenceData);\n };\n\n /**\n * Send request review for user evidence competencies.\n *\n * @param {Object} evidenceData Evidence data from evidence node.\n */\n UserEvidenceActions.prototype.reviewUserEvidenceCompetencies = function(evidenceData) {\n var self = this,\n requests;\n\n requests = ajax.call([{\n methodname: 'core_competency_read_user_evidence',\n args: {id: evidenceData.id}\n }]);\n\n requests[0].done(function(evidence) {\n str.get_strings([\n {key: 'confirm', component: 'moodle'},\n {key: 'sendallcompetenciestoreview', component: 'tool_lp', param: evidence.name},\n {key: 'confirm', component: 'moodle'},\n {key: 'cancel', component: 'moodle'}\n ]).done(function(strings) {\n notification.confirm(\n strings[0], // Confirm.\n strings[1], // Send all competencies in review for X?\n strings[2], // Confirm.\n strings[3], // Cancel.\n function() {\n self._doReviewUserEvidenceCompetencies(evidenceData);\n }\n );\n }).fail(notification.exception);\n }).fail(notification.exception);\n\n };\n\n /**\n * Send request review for user evidence competencies handler.\n *\n * @param {Event} e The event.\n */\n UserEvidenceActions.prototype._reviewUserEvidenceCompetenciesHandler = function(e) {\n e.preventDefault();\n var data = this._findEvidenceData($(e.target));\n this.reviewUserEvidenceCompetencies(data);\n };\n\n /**\n * Find the evidence data from the evidence node.\n *\n * @param {Node} node The node to search from.\n * @return {Object} Evidence data.\n */\n UserEvidenceActions.prototype._findEvidenceData = function(node) {\n var parent = node.parentsUntil($(this._region).parent(), this._evidenceNode),\n data;\n\n if (parent.length != 1) {\n throw new Error('The evidence node was not located.');\n }\n\n data = parent.data();\n if (typeof data === 'undefined' || typeof data.id === 'undefined') {\n throw new Error('Evidence data could not be found.');\n }\n\n return data;\n };\n\n /**\n * Enhance a menu bar.\n *\n * @param {String} selector Menubar selector.\n */\n UserEvidenceActions.prototype.enhanceMenubar = function(selector) {\n var self = this;\n Menubar.enhance(selector, {\n '[data-action=\"user-evidence-delete\"]': self._deleteEvidenceHandler.bind(self),\n '[data-action=\"link-competency\"]': self._createUserEvidenceCompetencyHandler.bind(self),\n '[data-action=\"send-competencies-review\"]': self._reviewUserEvidenceCompetenciesHandler.bind(self),\n });\n };\n\n /**\n * Register the events in the region.\n *\n * At this stage this cannot be used with enhanceMenubar or multiple handlers\n * will be added to the same node.\n */\n UserEvidenceActions.prototype.registerEvents = function() {\n var wrapper = $(this._region),\n self = this;\n\n wrapper.find('[data-action=\"user-evidence-delete\"]').click(self._deleteEvidenceHandler.bind(self));\n wrapper.find('[data-action=\"link-competency\"]').click(self._createUserEvidenceCompetencyHandler.bind(self));\n wrapper.find('[data-action=\"delete-competency-link\"]').click(self._deleteUserEvidenceCompetencyHandler.bind(self));\n wrapper.find('[data-action=\"send-competencies-review\"]').click(self._reviewUserEvidenceCompetenciesHandler.bind(self));\n };\n\n return /** @alias module:tool_lp/user_evidence_actions */ UserEvidenceActions;\n});\n"],"names":["define","$","templates","ajax","notification","str","Menubar","PickerUserPlans","UserEvidenceActions","type","_type","_region","_evidenceNode","_template","_contextMethod","TypeError","prototype","_getContextArgs","evidenceData","args","this","id","userid","_renderView","context","self","render","then","newhtml","newjs","replaceNode","_callAndRefresh","calls","push","methodname","when","apply","call","arguments","length","fail","exception","_doDelete","deleteEvidence","done","evidence","get_strings","key","component","param","name","strings","confirm","_deleteEvidenceHandler","e","preventDefault","data","_findEvidenceData","target","_doCreateUserEvidenceCompetency","competencyIds","each","index","competencyId","userevidenceid","competencyid","createUserEvidenceCompetency","picker","on","requestReview","display","_createUserEvidenceCompetencyHandler","_doDeleteUserEvidenceCompetency","deleteUserEvidenceCompetency","_deleteUserEvidenceCompetencyHandler","currentTarget","_doReviewUserEvidenceCompetencies","reviewUserEvidenceCompetencies","_reviewUserEvidenceCompetenciesHandler","node","parent","parentsUntil","Error","enhanceMenubar","selector","enhance","bind","registerEvents","wrapper","find","click"],"mappings":"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"}
\ No newline at end of file
+{"version":3,"file":"user_evidence_actions.min.js","sources":["../src/user_evidence_actions.js"],"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 * User evidence actions.\n *\n * @module tool_lp/user_evidence_actions\n * @copyright 2015 Frédéric Massart - FMCorz.net\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(['jquery',\n 'core/templates',\n 'core/ajax',\n 'core/notification',\n 'core/str',\n 'tool_lp/menubar',\n 'tool_lp/competencypicker_user_plans'],\n function($, templates, ajax, notification, str, Menubar, PickerUserPlans) {\n\n /**\n * UserEvidenceActions class.\n *\n * Note that presently this cannot be instantiated more than once per page.\n *\n * @param {String} type The type of page we're in.\n */\n var UserEvidenceActions = function(type) {\n this._type = type;\n\n if (type === 'evidence') {\n // This is the page to view one evidence.\n this._region = '[data-region=\"user-evidence-page\"]';\n this._evidenceNode = '[data-region=\"user-evidence-page\"]';\n this._template = 'tool_lp/user_evidence_page';\n this._contextMethod = 'tool_lp_data_for_user_evidence_page';\n\n } else if (type === 'list') {\n // This is the page to view a list of evidence.\n this._region = '[data-region=\"user-evidence-list\"]';\n this._evidenceNode = '[data-region=\"user-evidence-node\"]';\n this._template = 'tool_lp/user_evidence_list_page';\n this._contextMethod = 'tool_lp_data_for_user_evidence_list_page';\n\n } else {\n throw new TypeError('Unexpected type.');\n }\n };\n\n /** @property {String} Ajax method to fetch the page data from. */\n UserEvidenceActions.prototype._contextMethod = null;\n /** @property {String} Selector to find the node describing the evidence. */\n UserEvidenceActions.prototype._evidenceNode = null;\n /** @property {String} Selector mapping to the region to update. Usually similar to wrapper. */\n UserEvidenceActions.prototype._region = null;\n /** @property {String} Name of the template used to render the region. */\n UserEvidenceActions.prototype._template = null;\n /** @property {String} Type of page/region we're in. */\n UserEvidenceActions.prototype._type = null;\n\n /**\n * Resolve the arguments to refresh the region.\n *\n * @param {Object} evidenceData Evidence data from evidence node.\n * @return {Object} List of arguments.\n */\n UserEvidenceActions.prototype._getContextArgs = function(evidenceData) {\n var self = this,\n args = {};\n\n if (self._type === 'evidence') {\n args = {\n id: evidenceData.id\n };\n\n } else if (self._type === 'list') {\n args = {\n userid: evidenceData.userid\n };\n }\n\n return args;\n };\n\n /**\n * Callback to render the region template.\n *\n * @param {Object} context The context for the template.\n * @return {Promise}\n */\n UserEvidenceActions.prototype._renderView = function(context) {\n var self = this;\n return templates.render(self._template, context)\n .then(function(newhtml, newjs) {\n templates.replaceNode($(self._region), newhtml, newjs);\n return;\n });\n };\n\n /**\n * Call multiple ajax methods, and refresh.\n *\n * @param {Array} calls List of Ajax calls.\n * @param {Object} evidenceData Evidence data from evidence node.\n * @return {Promise}\n */\n UserEvidenceActions.prototype._callAndRefresh = function(calls, evidenceData) {\n var self = this;\n calls.push({\n methodname: self._contextMethod,\n args: self._getContextArgs(evidenceData)\n });\n\n // Apply all the promises, and refresh when the last one is resolved.\n return $.when.apply($.when, ajax.call(calls))\n .then(function() {\n return self._renderView(arguments[arguments.length - 1]);\n })\n .fail(notification.exception);\n };\n\n /**\n * Delete a plan and reload the region.\n *\n * @param {Object} evidenceData Evidence data from evidence node.\n */\n UserEvidenceActions.prototype._doDelete = function(evidenceData) {\n var self = this,\n calls = [{\n methodname: 'core_competency_delete_user_evidence',\n args: {id: evidenceData.id}\n }];\n self._callAndRefresh(calls, evidenceData);\n };\n\n /**\n * Delete a plan.\n *\n * @param {Object} evidenceData Evidence data from evidence node.\n */\n UserEvidenceActions.prototype.deleteEvidence = function(evidenceData) {\n var self = this,\n requests;\n\n requests = ajax.call([{\n methodname: 'core_competency_read_user_evidence',\n args: {id: evidenceData.id}\n }]);\n\n requests[0].done(function(evidence) {\n str.get_strings([\n {key: 'confirm', component: 'moodle'},\n {key: 'deleteuserevidence', component: 'tool_lp', param: evidence.name},\n {key: 'delete', component: 'moodle'},\n {key: 'cancel', component: 'moodle'}\n ]).done(function(strings) {\n notification.confirm(\n strings[0], // Confirm.\n strings[1], // Delete evidence X?\n strings[2], // Delete.\n strings[3], // Cancel.\n function() {\n self._doDelete(evidenceData);\n }\n );\n }).fail(notification.exception);\n }).fail(notification.exception);\n\n };\n\n /**\n * Delete evidence handler.\n *\n * @param {Event} e The event.\n */\n UserEvidenceActions.prototype._deleteEvidenceHandler = function(e) {\n e.preventDefault();\n var data = this._findEvidenceData($(e.target));\n this.deleteEvidence(data);\n };\n\n /**\n * Link a competency and reload.\n *\n * @param {Object} evidenceData Evidence data from evidence node.\n * @param {Number} competencyIds The competency IDs.\n */\n UserEvidenceActions.prototype._doCreateUserEvidenceCompetency = function(evidenceData, competencyIds) {\n var self = this,\n calls = [];\n\n $.each(competencyIds, function(index, competencyId) {\n calls.push({\n methodname: 'core_competency_create_user_evidence_competency',\n args: {\n userevidenceid: evidenceData.id,\n competencyid: competencyId,\n }\n });\n });\n\n self._callAndRefresh(calls, evidenceData);\n };\n\n /**\n * Create a user evidence competency.\n *\n * @param {Object} evidenceData Evidence data from evidence node.\n */\n UserEvidenceActions.prototype.createUserEvidenceCompetency = function(evidenceData) {\n var self = this,\n picker = new PickerUserPlans(evidenceData.userid);\n\n picker.on('save', function(e, data) {\n var competencyIds = data.competencyIds;\n self._doCreateUserEvidenceCompetency(evidenceData, competencyIds, data.requestReview);\n });\n\n picker.display();\n };\n\n /**\n * Create user evidence competency handler.\n *\n * @param {Event} e The event.\n */\n UserEvidenceActions.prototype._createUserEvidenceCompetencyHandler = function(e) {\n e.preventDefault();\n var data = this._findEvidenceData($(e.target));\n this.createUserEvidenceCompetency(data);\n };\n\n /**\n * Remove a linked competency and reload.\n *\n * @param {Object} evidenceData Evidence data from evidence node.\n * @param {Number} competencyId The competency ID.\n */\n UserEvidenceActions.prototype._doDeleteUserEvidenceCompetency = function(evidenceData, competencyId) {\n var self = this,\n calls = [];\n\n calls.push({\n methodname: 'core_competency_delete_user_evidence_competency',\n args: {\n userevidenceid: evidenceData.id,\n competencyid: competencyId,\n }\n });\n\n self._callAndRefresh(calls, evidenceData);\n };\n\n /**\n * Delete a user evidence competency.\n *\n * @param {Object} evidenceData Evidence data from evidence node.\n * @param {Number} competencyId The competency ID.\n */\n UserEvidenceActions.prototype.deleteUserEvidenceCompetency = function(evidenceData, competencyId) {\n this._doDeleteUserEvidenceCompetency(evidenceData, competencyId);\n };\n\n /**\n * Delete user evidence competency handler.\n *\n * @param {Event} e The event.\n */\n UserEvidenceActions.prototype._deleteUserEvidenceCompetencyHandler = function(e) {\n var data = this._findEvidenceData($(e.currentTarget)),\n competencyId = $(e.currentTarget).data('id');\n e.preventDefault();\n this.deleteUserEvidenceCompetency(data, competencyId);\n };\n\n /**\n * Send request review for user evidence competencies and reload the region.\n *\n * @param {Object} evidenceData Evidence data from evidence node.\n */\n UserEvidenceActions.prototype._doReviewUserEvidenceCompetencies = function(evidenceData) {\n var self = this,\n calls = [{\n methodname: 'core_competency_request_review_of_user_evidence_linked_competencies',\n args: {id: evidenceData.id}\n }];\n self._callAndRefresh(calls, evidenceData);\n };\n\n /**\n * Send request review for user evidence competencies.\n *\n * @param {Object} evidenceData Evidence data from evidence node.\n */\n UserEvidenceActions.prototype.reviewUserEvidenceCompetencies = function(evidenceData) {\n var self = this,\n requests;\n\n requests = ajax.call([{\n methodname: 'core_competency_read_user_evidence',\n args: {id: evidenceData.id}\n }]);\n\n requests[0].done(function(evidence) {\n str.get_strings([\n {key: 'confirm', component: 'moodle'},\n {key: 'sendallcompetenciestoreview', component: 'tool_lp', param: evidence.name},\n {key: 'confirm', component: 'moodle'},\n {key: 'cancel', component: 'moodle'}\n ]).done(function(strings) {\n notification.confirm(\n strings[0], // Confirm.\n strings[1], // Send all competencies in review for X?\n strings[2], // Confirm.\n strings[3], // Cancel.\n function() {\n self._doReviewUserEvidenceCompetencies(evidenceData);\n }\n );\n }).fail(notification.exception);\n }).fail(notification.exception);\n\n };\n\n /**\n * Send request review for user evidence competencies handler.\n *\n * @param {Event} e The event.\n */\n UserEvidenceActions.prototype._reviewUserEvidenceCompetenciesHandler = function(e) {\n e.preventDefault();\n var data = this._findEvidenceData($(e.target));\n this.reviewUserEvidenceCompetencies(data);\n };\n\n /**\n * Find the evidence data from the evidence node.\n *\n * @param {Node} node The node to search from.\n * @return {Object} Evidence data.\n */\n UserEvidenceActions.prototype._findEvidenceData = function(node) {\n var parent = node.parentsUntil($(this._region).parent(), this._evidenceNode),\n data;\n\n if (parent.length != 1) {\n throw new Error('The evidence node was not located.');\n }\n\n data = parent.data();\n if (typeof data === 'undefined' || typeof data.id === 'undefined') {\n throw new Error('Evidence data could not be found.');\n }\n\n return data;\n };\n\n /**\n * Enhance a menu bar.\n *\n * @param {String} selector Menubar selector.\n */\n UserEvidenceActions.prototype.enhanceMenubar = function(selector) {\n var self = this;\n Menubar.enhance(selector, {\n '[data-action=\"user-evidence-delete\"]': self._deleteEvidenceHandler.bind(self),\n '[data-action=\"link-competency\"]': self._createUserEvidenceCompetencyHandler.bind(self),\n '[data-action=\"send-competencies-review\"]': self._reviewUserEvidenceCompetenciesHandler.bind(self),\n });\n };\n\n /**\n * Register the events in the region.\n *\n * At this stage this cannot be used with enhanceMenubar or multiple handlers\n * will be added to the same node.\n */\n UserEvidenceActions.prototype.registerEvents = function() {\n var wrapper = $(this._region),\n self = this;\n\n wrapper.find('[data-action=\"user-evidence-delete\"]').click(self._deleteEvidenceHandler.bind(self));\n wrapper.find('[data-action=\"link-competency\"]').click(self._createUserEvidenceCompetencyHandler.bind(self));\n wrapper.find('[data-action=\"delete-competency-link\"]').click(self._deleteUserEvidenceCompetencyHandler.bind(self));\n wrapper.find('[data-action=\"send-competencies-review\"]').click(self._reviewUserEvidenceCompetenciesHandler.bind(self));\n };\n\n return /** @alias module:tool_lp/user_evidence_actions */ UserEvidenceActions;\n});\n"],"names":["define","$","templates","ajax","notification","str","Menubar","PickerUserPlans","UserEvidenceActions","type","this","_type","_region","_evidenceNode","_template","_contextMethod","TypeError","prototype","_getContextArgs","evidenceData","args","id","userid","_renderView","context","self","render","then","newhtml","newjs","replaceNode","_callAndRefresh","calls","push","methodname","when","apply","call","arguments","length","fail","exception","_doDelete","deleteEvidence","done","evidence","get_strings","key","component","param","name","strings","confirm","_deleteEvidenceHandler","e","preventDefault","data","_findEvidenceData","target","_doCreateUserEvidenceCompetency","competencyIds","each","index","competencyId","userevidenceid","competencyid","createUserEvidenceCompetency","picker","on","requestReview","display","_createUserEvidenceCompetencyHandler","_doDeleteUserEvidenceCompetency","deleteUserEvidenceCompetency","_deleteUserEvidenceCompetencyHandler","currentTarget","_doReviewUserEvidenceCompetencies","reviewUserEvidenceCompetencies","_reviewUserEvidenceCompetenciesHandler","node","parent","parentsUntil","Error","enhanceMenubar","selector","enhance","bind","registerEvents","wrapper","find","click"],"mappings":"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"}
\ No newline at end of file
diff --git a/admin/tool/monitor/amd/build/dropdown.min.js.map b/admin/tool/monitor/amd/build/dropdown.min.js.map
index 93bf5355381..7eff12d136c 100644
--- a/admin/tool/monitor/amd/build/dropdown.min.js.map
+++ b/admin/tool/monitor/amd/build/dropdown.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"dropdown.min.js","sources":["../src/dropdown.js"],"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 * Dropdown handler for the Event monitor tool.\n *\n * @module tool_monitor/dropdown\n * @copyright 2023 Andrew Lyons \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nexport const init = () => {\n const componentSelector = document.querySelector('[data-field=\"component\"]');\n const eventSelector = document.querySelector('[data-field=\"eventname\"]');\n\n const matchesComponent = (component, event) => event.startsWith(`\\\\${component}\\\\`);\n\n // Helper to fetch events for a component.\n const getEventsForComponent = (component) => {\n const events = Object.entries(JSON.parse(eventSelector.dataset.eventlist));\n return events.filter(([eventName], index) => {\n // Always return the Choose... option.\n if (index === 0) {\n return true;\n }\n return matchesComponent(component, eventName);\n });\n };\n\n // Helper to fetch the
')),this.children.push(i),this.inner.one(".availability-children").appendChild(i.node)},M.core_availability.List.prototype.focusAfterAdd=function(){this.inner.one("button").focus()},M.core_availability.List.prototype.isIndividualShowIcons=function(){var i,t;if(this.root)return i="!"===this.node.one(".availability-neg").get("value"),t="|"===this.node.one(".availability-op").get("value"),!i&&!t||i&&t;throw"Can only call this on root list"},M.core_availability.List.prototype.renumber=function(i){var t,e={count:this.children.length},a=i===undefined?e.number="":(e.number=i+":",i+"."),i=M.util.get_string("setheading","availability",e);for(this.node.one("> h3").set("innerHTML",i),t=0;t .availability-children").removeAttribute("aria-hidden"),this.inner.one("> .availability-none").setAttribute("aria-hidden","true"),this.inner.one("> .availability-header").removeAttribute("aria-hidden"),1 .availability-children").setAttribute("aria-hidden","true"),this.inner.one("> .availability-none").removeAttribute("aria-hidden"),this.inner.one("> .availability-header").setAttribute("aria-hidden","true")),this.root){for(i=this.isIndividualShowIcons(),t=0;t .availability-children > .availability-connector span.label").each(function(i){i.set("innerHTML",a)})},M.core_availability.List.prototype.deleteDescendant=function(i){for(var t,e,a=0;a .availability-children").removeChild(e),M.core_availability.form.update(),this.updateHtml(),this.inner.one("> .availability-button").one("button").focus(),!0;if(t instanceof M.core_availability.List&&t.deleteDescendant(i))return!0}return!1},M.core_availability.List.prototype.clickAdd=function(){var i,t,e,a,l,n=d.Node.create('
"),o=n.one("button"),s={dialog:null},r=n.one("ul");for(l in M.core_availability.form.plugins)M.core_availability.form.plugins[l].allowAdd&&(i=d.Node.create(''),(e=d.Node.create('")).on("click",this.getAddHandler(l,s),this),i.appendChild(e),a=d.Node.create('"),i.appendChild(a),r.appendChild(i));i=d.Node.create(''),(e=d.Node.create('")).on("click",this.getAddHandler(null,s),this),i.appendChild(e),a=d.Node.create('"),i.appendChild(a),r.appendChild(i),n={headerContent:M.util.get_string("addrestriction","availability"),bodyContent:n,additionalBaseClass:"availability-dialogue",draggable:!0,modal:!0,closeButton:!1,width:"450px"},s.dialog=new M.core.dialogue(n),s.dialog.show(),o.on("click",function(){s.dialog.destroy(),this.inner.one("> .availability-button").one("button").focus()},this)},M.core_availability.List.prototype.getAddHandler=function(t,e){return function(){var i=t?new M.core_availability.Item({type:t,creating:!0},this.root):new M.core_availability.List({c:[],showc:!0},!1,this.root);this.addChild(i),M.core_availability.form.update(),M.core_availability.form.rootList.renumber(),this.updateHtml(),e.dialog.destroy(),i.focusAfterAdd()}},M.core_availability.List.prototype.getValue=function(){var i,t={};for(t.op=this.node.one(".availability-neg").get("value")+this.node.one(".availability-op").get("value"),t.c=[],i=0;i'+M.util.get_string("missingplugin","availability")+"
"),o.appendChild(r),d(0,c));p=function(){var e,n=g.one("#backup-bytype");o.currentlyshown?n.setHTML(M.util.get_string("showtypes","backup")):n.setHTML(M.util.get_string("hidetypes","backup")),o.currentlyshown=!o.currentlyshown,n={node:o,duration:.2},o.currentlyshown?(o.show(),n.to={maxHeight:o.get("clientHeight")+"px"},o.setStyle("maxHeight","0px"),(e=new g.Anim(n)).on("end",function(){o.setStyle("maxHeight","none")})):(n.to={maxHeight:"0px"},o.setStyle("maxHeight",o.get("clientHeight")+"px"),(e=new g.Anim(n)).on("end",function(){o.hide(),o.setStyle("maxHeight","none")})),e.run()},g.one("#backup-bytype").on("click",function(e){e.preventDefault(),p()}),g.one("#backup-all-included").on("click",function(e){a(e,!0,"_included")}),g.one("#backup-none-included").on("click",function(e){a(e,!1,"_included")}),t&&(g.one("#backup-all-userdata").on("click",function(e){a(e,!0,t)}),g.one("#backup-none-userdata").on("click",function(e){a(e,!1,t)}))}}},"@VERSION@",{requires:["node","event","node-event-simulate","anim"]});
\ No newline at end of file
+YUI.add("moodle-backup-backupselectall",function(g,e){M.core_backup=M.core_backup||{},M.core_backup.backupselectall=function(e){var t,n,i,o,c,l,a,u,s=null,d=function(e,t,i,n){var o,c;e.preventDefault(),o=void 0!==n?"setting_activity_"+n+"_":"",c=i.length,g.all('input[type="checkbox"]').each(function(e){var n=e.get("name");o&&n.substring(0,o.length)!==o||n.substring(n.length-c)===i&&e.set("checked",t)}),s&&M.form&&M.form.updateFormState(s)},r=function(e,n,t,i){return void 0===i&&(i=""),'
"),c.appendChild(a),o(0,l));u=function(){var e,n=g.one("#backup-bytype");c.currentlyshown?n.setHTML(M.util.get_string("showtypes","backup")):n.setHTML(M.util.get_string("hidetypes","backup")),c.currentlyshown=!c.currentlyshown,n={node:c,duration:.2},c.currentlyshown?(c.show(),n.to={maxHeight:c.get("clientHeight")+"px"},c.setStyle("maxHeight","0px"),(e=new g.Anim(n)).on("end",function(){c.setStyle("maxHeight","none")})):(n.to={maxHeight:"0px"},c.setStyle("maxHeight",c.get("clientHeight")+"px"),(e=new g.Anim(n)).on("end",function(){c.hide(),c.setStyle("maxHeight","none")})),e.run()},g.one("#backup-bytype").on("click",function(e){e.preventDefault(),u()}),g.one("#backup-all-included").on("click",function(e){d(e,!0,"_included")}),g.one("#backup-none-included").on("click",function(e){d(e,!1,"_included")}),t&&(g.one("#backup-all-userdata").on("click",function(e){d(e,!0,t)}),g.one("#backup-none-userdata").on("click",function(e){d(e,!1,t)}))}}},"@VERSION@",{requires:["node","event","node-event-simulate","anim"]});
\ No newline at end of file
diff --git a/badges/amd/build/backpackactions.min.js.map b/badges/amd/build/backpackactions.min.js.map
index d409d5dce56..4a4304fe195 100644
--- a/badges/amd/build/backpackactions.min.js.map
+++ b/badges/amd/build/backpackactions.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"backpackactions.min.js","sources":["../src/backpackactions.js"],"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 * Action methods related to backpacks.\n *\n * @module core_badges/backpackactions\n * @copyright 2020 Sara Arjona \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport $ from 'jquery';\nimport selectors from 'core_badges/selectors';\nimport {get_string as getString} from 'core/str';\nimport Pending from 'core/pending';\nimport ModalFactory from 'core/modal_factory';\nimport ModalEvents from 'core/modal_events';\nimport Config from 'core/config';\n\n/**\n * Set up the actions.\n *\n * @method init\n */\nexport const init = () => {\n const pendingPromise = new Pending();\n\n const root = $(selectors.elements.main);\n registerListenerEvents(root);\n\n pendingPromise.resolve();\n};\n\n/**\n * Register backpack related event listeners.\n *\n * @method registerListenerEvents\n * @param {Object} root The root element.\n */\nconst registerListenerEvents = (root) => {\n\n root.on('click', selectors.actions.deletebackpack, async(e) => {\n e.preventDefault();\n\n const link = $(e.currentTarget);\n const modal = await buildModal(link);\n\n displayModal(modal, link);\n });\n};\n\nconst buildModal = async(link) => {\n\n const backpackurl = link.closest(selectors.elements.backpackurl).attr('data-backpackurl');\n\n return ModalFactory.create({\n title: await getString('delexternalbackpack', 'core_badges'),\n body: await getString('delexternalbackpackconfirm', 'core_badges', backpackurl),\n type: ModalFactory.types.SAVE_CANCEL,\n });\n\n};\n\nconst displayModal = async(modal, link) => {\n modal.setSaveButtonText(await getString('delete', 'core'));\n\n modal.getRoot().on(ModalEvents.save, function() {\n window.location.href = link.attr('href') + '&sesskey=' + Config.sesskey + '&confirm=1';\n });\n\n modal.getRoot().on(ModalEvents.hidden, function() {\n modal.destroy();\n });\n\n modal.show();\n};\n"],"names":["pendingPromise","Pending","root","selectors","elements","main","registerListenerEvents","resolve","on","actions","deletebackpack","async","e","preventDefault","link","currentTarget","modal","buildModal","displayModal","backpackurl","closest","attr","ModalFactory","create","title","body","type","types","SAVE_CANCEL","setSaveButtonText","getRoot","ModalEvents","save","window","location","href","Config","sesskey","hidden","destroy","show"],"mappings":";;;;;;;kXAoCoB,WACVA,eAAiB,IAAIC,iBAErBC,MAAO,mBAAEC,mBAAUC,SAASC,MAClCC,uBAAuBJ,MAEvBF,eAAeO,iBASbD,uBAA0BJ,OAE5BA,KAAKM,GAAG,QAASL,mBAAUM,QAAQC,gBAAgBC,MAAAA,IAC/CC,EAAEC,uBAEIC,MAAO,mBAAEF,EAAEG,eACXC,YAAcC,WAAWH,MAE/BI,aAAaF,MAAOF,UAItBG,WAAaN,MAAAA,aAETQ,YAAcL,KAAKM,QAAQjB,mBAAUC,SAASe,aAAaE,KAAK,2BAE/DC,uBAAaC,OAAO,CACvBC,YAAa,mBAAU,sBAAuB,eAC9CC,WAAY,mBAAU,6BAA8B,cAAeN,aACnEO,KAAMJ,uBAAaK,MAAMC,eAK3BV,aAAeP,MAAMK,MAAOF,QAC9BE,MAAMa,wBAAwB,mBAAU,SAAU,SAElDb,MAAMc,UAAUtB,GAAGuB,sBAAYC,MAAM,WACjCC,OAAOC,SAASC,KAAOrB,KAAKO,KAAK,QAAU,YAAce,gBAAOC,QAAU,gBAG9ErB,MAAMc,UAAUtB,GAAGuB,sBAAYO,QAAQ,WACnCtB,MAAMuB,aAGVvB,MAAMwB"}
\ No newline at end of file
+{"version":3,"file":"backpackactions.min.js","sources":["../src/backpackactions.js"],"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 * Action methods related to backpacks.\n *\n * @module core_badges/backpackactions\n * @copyright 2020 Sara Arjona \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport $ from 'jquery';\nimport selectors from 'core_badges/selectors';\nimport {get_string as getString} from 'core/str';\nimport Pending from 'core/pending';\nimport ModalFactory from 'core/modal_factory';\nimport ModalEvents from 'core/modal_events';\nimport Config from 'core/config';\n\n/**\n * Set up the actions.\n *\n * @method init\n */\nexport const init = () => {\n const pendingPromise = new Pending();\n\n const root = $(selectors.elements.main);\n registerListenerEvents(root);\n\n pendingPromise.resolve();\n};\n\n/**\n * Register backpack related event listeners.\n *\n * @method registerListenerEvents\n * @param {Object} root The root element.\n */\nconst registerListenerEvents = (root) => {\n\n root.on('click', selectors.actions.deletebackpack, async(e) => {\n e.preventDefault();\n\n const link = $(e.currentTarget);\n const modal = await buildModal(link);\n\n displayModal(modal, link);\n });\n};\n\nconst buildModal = async(link) => {\n\n const backpackurl = link.closest(selectors.elements.backpackurl).attr('data-backpackurl');\n\n return ModalFactory.create({\n title: await getString('delexternalbackpack', 'core_badges'),\n body: await getString('delexternalbackpackconfirm', 'core_badges', backpackurl),\n type: ModalFactory.types.SAVE_CANCEL,\n });\n\n};\n\nconst displayModal = async(modal, link) => {\n modal.setSaveButtonText(await getString('delete', 'core'));\n\n modal.getRoot().on(ModalEvents.save, function() {\n window.location.href = link.attr('href') + '&sesskey=' + Config.sesskey + '&confirm=1';\n });\n\n modal.getRoot().on(ModalEvents.hidden, function() {\n modal.destroy();\n });\n\n modal.show();\n};\n"],"names":["_interopRequireDefault","obj","__esModule","default","_jquery","_selectors","_pending","_modal_factory","_modal_events","_config","_exports","init","pendingPromise","Pending","root","$","selectors","elements","main","registerListenerEvents","resolve","on","actions","deletebackpack","async","e","preventDefault","link","currentTarget","modal","buildModal","displayModal","backpackurl","closest","attr","ModalFactory","create","title","getString","body","get_string","type","types","SAVE_CANCEL","setSaveButtonText","getRoot","ModalEvents","save","window","location","href","Config","sesskey","hidden","destroy","show"],"mappings":"6PA6BiC,SAAAA,uBAAAC,KAAAA,OAAAA,KAAAA,IAAAC,WAAAD,IAAAE,CAAAA,QAAAF,IAAA;;;;;;;kFANjCG,QAAAJ,uBAAAI,SACAC,WAAAL,uBAAAK,YAEAC,SAAAN,uBAAAM,UACAC,eAAAP,uBAAAO,gBACAC,cAAAR,uBAAAQ,eACAC,QAAAT,uBAAAS,SAcEC,SAAAC,KAPkBA,KAChB,MAAMC,eAAiB,IAAIC,SAAAA,QAErBC,MAAO,EAAAC,QAACZ,SAACa,mBAAUC,SAASC,MAClCC,uBAAuBL,MAEvBF,eAAeQ,SAAS,EAS5B,MAAMD,uBAA0BL,OAE5BA,KAAKO,GAAG,QAASL,WAAAA,QAAUM,QAAQC,gBAAgBC,UAC/CC,EAAEC,iBAEF,MAAMC,MAAO,EAAAZ,QAAAA,SAAEU,EAAEG,eACXC,YAAcC,WAAWH,MAE/BI,aAAaF,MAAOF,KAAK,GAC3B,EAGAG,WAAaN,aAEf,MAAMQ,YAAcL,KAAKM,QAAQjB,WAASb,QAACc,SAASe,aAAaE,KAAK,oBAEtE,OAAOC,eAAAA,QAAaC,OAAO,CACvBC,YAAa,EAAAC,KAAAA,YAAU,sBAAuB,eAC9CC,WAAY,EAAAD,KAASE,YAAC,6BAA8B,cAAeR,aACnES,KAAMN,eAAAA,QAAaO,MAAMC,aAC3B,EAIAZ,aAAeP,MAAMK,MAAOF,QAC9BE,MAAMe,wBAAwB,EAAAN,KAASE,YAAC,SAAU,SAElDX,MAAMgB,UAAUxB,GAAGyB,cAAW3C,QAAC4C,MAAM,WACjCC,OAAOC,SAASC,KAAOvB,KAAKO,KAAK,QAAU,YAAciB,gBAAOC,QAAU,YAC9E,IAEAvB,MAAMgB,UAAUxB,GAAGyB,cAAW3C,QAACkD,QAAQ,WACnCxB,MAAMyB,SACV,IAEAzB,MAAM0B,MAAM,CACd"}
\ No newline at end of file
diff --git a/badges/amd/build/selectors.min.js.map b/badges/amd/build/selectors.min.js.map
index 36bcd53ad01..42206a2ca07 100644
--- a/badges/amd/build/selectors.min.js.map
+++ b/badges/amd/build/selectors.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"selectors.min.js","sources":["../src/selectors.js"],"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 * Define all of the selectors we will be using on the backpack interface.\n *\n * @module core_badges/selectors\n * @copyright 2020 Sara Arjona \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\n/**\n * A small helper function to build queryable data selectors.\n *\n * @method getDataSelector\n * @param {String} name\n * @param {String} value\n * @return {string}\n */\nconst getDataSelector = (name, value) => {\n return `[data-${name}=\"${value}\"]`;\n};\n\nexport default {\n actions: {\n deletebackpack: getDataSelector('action', 'deletebackpack'),\n },\n elements: {\n clearsearch: '.input-group-append .clear-icon',\n main: '#backpacklist',\n backpackurl: '[data-backpackurl]',\n },\n};\n"],"names":["name","value","actions","deletebackpack","elements","clearsearch","main","backpackurl"],"mappings":"mJA+ByBA,KAAMC,eAIhB,CACXC,QAAS,CACLC,gBANiBH,KAMe,SANTC,MAMmB,iCAL9BD,kBAASC,cAOzBG,SAAU,CACNC,YAAa,kCACbC,KAAM,gBACNC,YAAa"}
\ No newline at end of file
+{"version":3,"file":"selectors.min.js","sources":["../src/selectors.js"],"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 * Define all of the selectors we will be using on the backpack interface.\n *\n * @module core_badges/selectors\n * @copyright 2020 Sara Arjona \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\n/**\n * A small helper function to build queryable data selectors.\n *\n * @method getDataSelector\n * @param {String} name\n * @param {String} value\n * @return {string}\n */\nconst getDataSelector = (name, value) => {\n return `[data-${name}=\"${value}\"]`;\n};\n\nexport default {\n actions: {\n deletebackpack: getDataSelector('action', 'deletebackpack'),\n },\n elements: {\n clearsearch: '.input-group-append .clear-icon',\n main: '#backpacklist',\n backpackurl: '[data-backpackurl]',\n },\n};\n"],"names":["name","value","_default","actions","deletebackpack","concat","elements","clearsearch","main","backpackurl","_exports","default"],"mappings":"+IAiCE,IAFuBA,KAAMC,MAE7BC,SAEa,CACXC,QAAS,CACLC,gBANiBJ,KAMe,SANTC,MAMmB,iBAL9C,SAAAI,OAAgBL,KAAIK,MAAAA,OAAKJ,MAAK,QAO9BK,SAAU,CACNC,YAAa,kCACbC,KAAM,gBACNC,YAAa,uBAEpB,OAAAC,SAAAC,QAAAT,SAAAQ,SAAAC,OAAA"}
\ No newline at end of file
diff --git a/blocks/accessreview/amd/build/module.min.js b/blocks/accessreview/amd/build/module.min.js
index bb66a3b0c6f..796a0759b1d 100644
--- a/blocks/accessreview/amd/build/module.min.js
+++ b/blocks/accessreview/amd/build/module.min.js
@@ -6,6 +6,6 @@ define("block_accessreview/module",["exports","core/ajax","core/templates","core
* @author Max Larkin
* @copyright 2020 Brickfield Education Labs
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */(Templates);let toggleState=!0;const renderTemplate=(element,errorCount,checkCount,displayFormat,minViews,viewDelta)=>{const weight=parseInt((errorCount-minViews)/viewDelta*2),context={resultPassed:!errorCount,classList:"",passRate:{errorCount:errorCount,checkCount:checkCount,failureRate:Math.round(errorCount/checkCount*100)}};if(!element)return Promise.resolve();const elementClassList=["block_accessreview"];context.resultPassed?elementClassList.push("block_accessreview_success"):weight?elementClassList.push("block_accessreview_danger"):elementClassList.push("block_accessreview_warning");const showIcons="showicons"==displayFormat||"showboth"==displayFormat,showBackground="showbackground"==displayFormat||"showboth"==displayFormat;return showBackground&&!showIcons?(element.classList.add(...elementClassList,"alert"),Promise.resolve()):(showIcons&&!showBackground&&(context.classList=elementClassList.join(" ")),Templates.renderForPromise("block_accessreview/status",context).then((_ref=>{let{html:html,js:js}=_ref;Templates.appendNodeContents(element,html,js),showBackground&&element.classList.add(...elementClassList,"alert")})).catch())},showAccessMap=function(courseId,displayFormat){let updatePreference=arguments.length>2&&void 0!==arguments[2]&&arguments[2];return Promise.all(fetchReviewData(courseId,updatePreference)).then((_ref2=>{let[sectionData,moduleData]=_ref2;const{minViews:minViews,viewDelta:viewDelta}=getErrorTotals(sectionData,moduleData);return sectionData.forEach((section=>{const element=document.querySelector("#section-".concat(section.section," .summary"));element&&renderTemplate(element,section.numerrors,section.numchecks,displayFormat,minViews,viewDelta)})),moduleData.forEach((module=>{const element=document.getElementById("module-".concat(module.cmid));element&&renderTemplate(element,module.numerrors,module.numchecks,displayFormat,minViews,viewDelta)})),document.querySelector(".icon-accessmap").classList.remove("fa-eye-slash"),document.querySelector(".icon-accessmap").classList.add("fa-eye"),{sectionData:sectionData,moduleData:moduleData}})).catch(_notification.exception)},toggleAccessMap=(courseId,displayFormat)=>{toggleState=!toggleState,toggleState?showAccessMap(courseId,displayFormat,!0):function(){let updatePreference=arguments.length>0&&void 0!==arguments[0]&&arguments[0];document.querySelectorAll(".block_accessreview_view").forEach((node=>node.remove()));const classList=["block_accessreview","block_accessreview_success","block_accessreview_warning","block_accessreview_danger","block_accessreview_view","alert"];document.querySelectorAll(".block_accessreview").forEach((node=>node.classList.remove(...classList))),updatePreference&&setToggleStatePreference(!1),document.querySelector(".icon-accessmap").classList.remove("fa-eye"),document.querySelector(".icon-accessmap").classList.add("fa-eye-slash")}(!0)},getErrorTotals=(sectionData,moduleData)=>{const totals={totalErrors:0,totalUsers:0,minViews:0,maxViews:0,viewDelta:0};return[].concat(sectionData,moduleData).forEach((item=>{totals.totalErrors+=item.numerrors,item.numerrorstotals.maxViews&&(totals.maxViews=item.numerrors),totals.totalUsers+=item.numchecks})),totals.viewDelta=totals.maxViews-totals.minViews+1,totals},getTogglePreferenceParams=toggleState=>({methodname:"core_user_update_user_preferences",args:{preferences:[{type:"block_accessreviewtogglestate",value:toggleState}]}}),setToggleStatePreference=toggleState=>(0,_ajax.call)([getTogglePreferenceParams(toggleState)]),fetchReviewData=function(courseid){let updatePreference=arguments.length>1&&void 0!==arguments[1]&&arguments[1];const calls=[{methodname:"block_accessreview_get_section_data",args:{courseid:courseid}},{methodname:"block_accessreview_get_module_data",args:{courseid:courseid}}];return updatePreference&&calls.push(getTogglePreferenceParams(!0)),(0,_ajax.call)(calls)};_exports.init=(toggled,displayFormat,courseId)=>{toggleState=1==toggled,toggleState&&showAccessMap(courseId,displayFormat),((courseId,displayFormat)=>{document.addEventListener("click",(e=>{e.target.closest("#toggle-accessmap")&&(e.preventDefault(),toggleAccessMap(courseId,displayFormat))}))})(courseId,displayFormat)}}));
+ */(Templates);let toggleState=!0;const renderTemplate=(element,errorCount,checkCount,displayFormat,minViews,viewDelta)=>{const weight=parseInt((errorCount-minViews)/viewDelta*2),context={resultPassed:!errorCount,classList:"",passRate:{errorCount:errorCount,checkCount:checkCount,failureRate:Math.round(errorCount/checkCount*100)}};if(!element)return Promise.resolve();const elementClassList=["block_accessreview"];context.resultPassed?elementClassList.push("block_accessreview_success"):weight?elementClassList.push("block_accessreview_danger"):elementClassList.push("block_accessreview_warning");const showIcons="showicons"==displayFormat||"showboth"==displayFormat,showBackground="showbackground"==displayFormat||"showboth"==displayFormat;return showBackground&&!showIcons?(element.classList.add(...elementClassList,"alert"),Promise.resolve()):(showIcons&&!showBackground&&(context.classList=elementClassList.join(" ")),Templates.renderForPromise("block_accessreview/status",context).then((_ref=>{let{html:html,js:js}=_ref;Templates.appendNodeContents(element,html,js),showBackground&&element.classList.add(...elementClassList,"alert")})).catch())},showAccessMap=function(courseId,displayFormat){let updatePreference=arguments.length>2&&void 0!==arguments[2]&&arguments[2];return Promise.all(fetchReviewData(courseId,updatePreference)).then((_ref2=>{let[sectionData,moduleData]=_ref2;const{minViews:minViews,viewDelta:viewDelta}=getErrorTotals(sectionData,moduleData);return sectionData.forEach((section=>{const element=document.querySelector("#section-".concat(section.section," .summary"));element&&renderTemplate(element,section.numerrors,section.numchecks,displayFormat,minViews,viewDelta)})),moduleData.forEach((module=>{const element=document.getElementById("module-".concat(module.cmid));element&&renderTemplate(element,module.numerrors,module.numchecks,displayFormat,minViews,viewDelta)})),document.querySelector(".icon-accessmap").classList.remove("fa-eye-slash"),document.querySelector(".icon-accessmap").classList.add("fa-eye"),{sectionData:sectionData,moduleData:moduleData}})).catch(_notification.exception)},toggleAccessMap=(courseId,displayFormat)=>{toggleState=!toggleState,toggleState?showAccessMap(courseId,displayFormat,!0):function(){let updatePreference=arguments.length>0&&void 0!==arguments[0]&&arguments[0];document.querySelectorAll(".block_accessreview_view").forEach((node=>node.remove()));const classList=["block_accessreview","block_accessreview_success","block_accessreview_warning","block_accessreview_danger","block_accessreview_view","alert"];document.querySelectorAll(".block_accessreview").forEach((node=>node.classList.remove(...classList))),updatePreference&&setToggleStatePreference(!1),document.querySelector(".icon-accessmap").classList.remove("fa-eye"),document.querySelector(".icon-accessmap").classList.add("fa-eye-slash")}(!0)},getErrorTotals=(sectionData,moduleData)=>{const totals={totalErrors:0,totalUsers:0,minViews:0,maxViews:0,viewDelta:0};return[].concat(sectionData,moduleData).forEach((item=>{totals.totalErrors+=item.numerrors,item.numerrorstotals.maxViews&&(totals.maxViews=item.numerrors),totals.totalUsers+=item.numchecks})),totals.viewDelta=totals.maxViews-totals.minViews+1,totals},getTogglePreferenceParams=toggleState=>({methodname:"core_user_update_user_preferences",args:{preferences:[{type:"block_accessreviewtogglestate",value:toggleState}]}}),setToggleStatePreference=toggleState=>(0,_ajax.call)([getTogglePreferenceParams(toggleState)]),fetchReviewData=function(courseid){const calls=[{methodname:"block_accessreview_get_section_data",args:{courseid:courseid}},{methodname:"block_accessreview_get_module_data",args:{courseid:courseid}}];return arguments.length>1&&void 0!==arguments[1]&&arguments[1]&&calls.push(getTogglePreferenceParams(!0)),(0,_ajax.call)(calls)};_exports.init=(toggled,displayFormat,courseId)=>{toggleState=1==toggled,toggleState&&showAccessMap(courseId,displayFormat),((courseId,displayFormat)=>{document.addEventListener("click",(e=>{e.target.closest("#toggle-accessmap")&&(e.preventDefault(),toggleAccessMap(courseId,displayFormat))}))})(courseId,displayFormat)}}));
//# sourceMappingURL=module.min.js.map
\ No newline at end of file
diff --git a/blocks/accessreview/amd/build/module.min.js.map b/blocks/accessreview/amd/build/module.min.js.map
index dca3203930d..b98e8b81df3 100644
--- a/blocks/accessreview/amd/build/module.min.js.map
+++ b/blocks/accessreview/amd/build/module.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"module.min.js","sources":["../src/module.js"],"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 * Manager for the accessreview block.\n *\n * @module block_accessreview/module\n * @author Max Larkin \n * @copyright 2020 Brickfield Education Labs \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {call as fetchMany} from 'core/ajax';\nimport * as Templates from 'core/templates';\nimport {exception as displayError} from 'core/notification';\n\n/**\n * The number of colours used to represent the heatmap. (Indexed on 0.)\n * @type {number}\n */\nconst numColours = 2;\n\n/**\n * The toggle state of the heatmap.\n * @type {boolean}\n */\nlet toggleState = true;\n\n/**\n * Renders the HTML template onto a particular HTML element.\n * @param {HTMLElement} element The element to attach the HTML to.\n * @param {number} errorCount The number of errors on this module/section.\n * @param {number} checkCount The number of checks triggered on this module/section.\n * @param {String} displayFormat\n * @param {Number} minViews\n * @param {Number} viewDelta\n * @returns {Promise}\n */\nconst renderTemplate = (element, errorCount, checkCount, displayFormat, minViews, viewDelta) => {\n // Calculate a weight?\n const weight = parseInt((errorCount - minViews) / viewDelta * numColours);\n\n const context = {\n resultPassed: !errorCount,\n classList: '',\n passRate: {\n errorCount,\n checkCount,\n failureRate: Math.round(errorCount / checkCount * 100),\n },\n };\n\n if (!element) {\n return Promise.resolve();\n }\n\n const elementClassList = ['block_accessreview'];\n if (context.resultPassed) {\n elementClassList.push('block_accessreview_success');\n } else if (weight) {\n elementClassList.push('block_accessreview_danger');\n } else {\n elementClassList.push('block_accessreview_warning');\n }\n\n const showIcons = (displayFormat == 'showicons') || (displayFormat == 'showboth');\n const showBackground = (displayFormat == 'showbackground') || (displayFormat == 'showboth');\n\n if (showBackground && !showIcons) {\n // Only the background is displayed.\n // No need to display the template.\n // Note: The case where both the background and icons are shown is handled later to avoid jankiness.\n element.classList.add(...elementClassList, 'alert');\n\n return Promise.resolve();\n }\n\n if (showIcons && !showBackground) {\n context.classList = elementClassList.join(' ');\n }\n\n // The icons are displayed either with, or without, the background.\n return Templates.renderForPromise('block_accessreview/status', context)\n .then(({html, js}) => {\n Templates.appendNodeContents(element, html, js);\n\n if (showBackground) {\n element.classList.add(...elementClassList, 'alert');\n }\n\n return;\n })\n .catch();\n};\n\n/**\n * Applies the template to all sections and modules on the course page.\n *\n * @param {Number} courseId\n * @param {String} displayFormat\n * @param {Boolean} updatePreference\n * @returns {Promise}\n */\nconst showAccessMap = (courseId, displayFormat, updatePreference = false) => {\n // Get error data.\n return Promise.all(fetchReviewData(courseId, updatePreference))\n .then(([sectionData, moduleData]) => {\n // Get total data.\n const {minViews, viewDelta} = getErrorTotals(sectionData, moduleData);\n\n sectionData.forEach(section => {\n const element = document.querySelector(`#section-${section.section} .summary`);\n if (!element) {\n return;\n }\n\n renderTemplate(element, section.numerrors, section.numchecks, displayFormat, minViews, viewDelta);\n });\n\n moduleData.forEach(module => {\n const element = document.getElementById(`module-${module.cmid}`);\n if (!element) {\n return;\n }\n\n renderTemplate(element, module.numerrors, module.numchecks, displayFormat, minViews, viewDelta);\n });\n\n // Change the icon display.\n document.querySelector('.icon-accessmap').classList.remove(...['fa-eye-slash']);\n document.querySelector('.icon-accessmap').classList.add(...['fa-eye']);\n\n return {\n sectionData,\n moduleData,\n };\n })\n .catch(displayError);\n};\n\n\n/**\n * Hides or removes the templates from the HTML of the current page.\n *\n * @param {Boolean} updatePreference\n */\nconst hideAccessMap = (updatePreference = false) => {\n // Removes the added elements.\n document.querySelectorAll('.block_accessreview_view').forEach(node => node.remove());\n\n const classList = [\n 'block_accessreview',\n 'block_accessreview_success',\n 'block_accessreview_warning',\n 'block_accessreview_danger',\n 'block_accessreview_view',\n 'alert',\n ];\n\n // Removes the added classes.\n document.querySelectorAll('.block_accessreview').forEach(node => node.classList.remove(...classList));\n\n if (updatePreference) {\n setToggleStatePreference(false);\n }\n\n // Change the icon display.\n document.querySelector('.icon-accessmap').classList.remove(...['fa-eye']);\n document.querySelector('.icon-accessmap').classList.add(...['fa-eye-slash']);\n};\n\n\n/**\n * Toggles the heatmap on/off.\n *\n * @param {Number} courseId\n * @param {String} displayFormat\n */\nconst toggleAccessMap = (courseId, displayFormat) => {\n toggleState = !toggleState;\n if (!toggleState) {\n hideAccessMap(true);\n } else {\n showAccessMap(courseId, displayFormat, true);\n }\n};\n\n/**\n * Parses information on the errors, generating the min, max and totals.\n *\n * @param {Object[]} sectionData The error data for course sections.\n * @param {Object[]} moduleData The error data for course modules.\n * @returns {Object} An object representing the extra error information.\n */\nconst getErrorTotals = (sectionData, moduleData) => {\n const totals = {\n totalErrors: 0,\n totalUsers: 0,\n minViews: 0,\n maxViews: 0,\n viewDelta: 0,\n };\n\n [].concat(sectionData, moduleData).forEach(item => {\n totals.totalErrors += item.numerrors;\n if (item.numerrors < totals.minViews) {\n totals.minViews = item.numerrors;\n }\n\n if (item.numerrors > totals.maxViews) {\n totals.maxViews = item.numerrors;\n }\n totals.totalUsers += item.numchecks;\n });\n\n totals.viewDelta = totals.maxViews - totals.minViews + 1;\n\n return totals;\n};\n\nconst registerEventListeners = (courseId, displayFormat) => {\n document.addEventListener('click', e => {\n if (e.target.closest('#toggle-accessmap')) {\n e.preventDefault();\n toggleAccessMap(courseId, displayFormat);\n }\n });\n};\n\n/**\n * Set the user preference for the toggle value.\n *\n * @param {Boolean} toggleState\n * @returns {Promise}\n */\nconst getTogglePreferenceParams = toggleState => {\n return {\n methodname: 'core_user_update_user_preferences',\n args: {\n preferences: [{\n type: 'block_accessreviewtogglestate',\n value: toggleState,\n }],\n }\n };\n};\n\nconst setToggleStatePreference = toggleState => fetchMany([getTogglePreferenceParams(toggleState)]);\n\n/**\n * Fetch the review data.\n *\n * @param {Number} courseid\n * @param {Boolean} updatePreference\n * @returns {Promise[]}\n */\nconst fetchReviewData = (courseid, updatePreference = false) => {\n const calls = [\n {\n methodname: 'block_accessreview_get_section_data',\n args: {courseid}\n },\n {\n methodname: 'block_accessreview_get_module_data',\n args: {courseid}\n },\n ];\n\n if (updatePreference) {\n calls.push(getTogglePreferenceParams(true));\n }\n\n return fetchMany(calls);\n};\n\n/**\n * Setting up the access review module.\n * @param {number} toggled A number represnting the state of the review toggle.\n * @param {string} displayFormat A string representing the display format for icons.\n * @param {number} courseId The course ID.\n */\nexport const init = (toggled, displayFormat, courseId) => {\n // Settings consts.\n toggleState = toggled == 1;\n\n if (toggleState) {\n showAccessMap(courseId, displayFormat);\n }\n\n registerEventListeners(courseId, displayFormat);\n};\n"],"names":["toggleState","renderTemplate","element","errorCount","checkCount","displayFormat","minViews","viewDelta","weight","parseInt","context","resultPassed","classList","passRate","failureRate","Math","round","Promise","resolve","elementClassList","push","showIcons","showBackground","add","join","Templates","renderForPromise","then","_ref","html","js","appendNodeContents","catch","showAccessMap","courseId","updatePreference","all","fetchReviewData","_ref2","sectionData","moduleData","getErrorTotals","forEach","section","document","querySelector","numerrors","numchecks","module","getElementById","cmid","remove","displayError","toggleAccessMap","querySelectorAll","node","setToggleStatePreference","hideAccessMap","totals","totalErrors","totalUsers","maxViews","concat","item","getTogglePreferenceParams","methodname","args","preferences","type","value","courseid","calls","toggled","addEventListener","e","target","closest","preventDefault","registerEventListeners"],"mappings":"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"}
\ No newline at end of file
+{"version":3,"file":"module.min.js","sources":["../src/module.js"],"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 * Manager for the accessreview block.\n *\n * @module block_accessreview/module\n * @author Max Larkin \n * @copyright 2020 Brickfield Education Labs \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {call as fetchMany} from 'core/ajax';\nimport * as Templates from 'core/templates';\nimport {exception as displayError} from 'core/notification';\n\n/**\n * The number of colours used to represent the heatmap. (Indexed on 0.)\n * @type {number}\n */\nconst numColours = 2;\n\n/**\n * The toggle state of the heatmap.\n * @type {boolean}\n */\nlet toggleState = true;\n\n/**\n * Renders the HTML template onto a particular HTML element.\n * @param {HTMLElement} element The element to attach the HTML to.\n * @param {number} errorCount The number of errors on this module/section.\n * @param {number} checkCount The number of checks triggered on this module/section.\n * @param {String} displayFormat\n * @param {Number} minViews\n * @param {Number} viewDelta\n * @returns {Promise}\n */\nconst renderTemplate = (element, errorCount, checkCount, displayFormat, minViews, viewDelta) => {\n // Calculate a weight?\n const weight = parseInt((errorCount - minViews) / viewDelta * numColours);\n\n const context = {\n resultPassed: !errorCount,\n classList: '',\n passRate: {\n errorCount,\n checkCount,\n failureRate: Math.round(errorCount / checkCount * 100),\n },\n };\n\n if (!element) {\n return Promise.resolve();\n }\n\n const elementClassList = ['block_accessreview'];\n if (context.resultPassed) {\n elementClassList.push('block_accessreview_success');\n } else if (weight) {\n elementClassList.push('block_accessreview_danger');\n } else {\n elementClassList.push('block_accessreview_warning');\n }\n\n const showIcons = (displayFormat == 'showicons') || (displayFormat == 'showboth');\n const showBackground = (displayFormat == 'showbackground') || (displayFormat == 'showboth');\n\n if (showBackground && !showIcons) {\n // Only the background is displayed.\n // No need to display the template.\n // Note: The case where both the background and icons are shown is handled later to avoid jankiness.\n element.classList.add(...elementClassList, 'alert');\n\n return Promise.resolve();\n }\n\n if (showIcons && !showBackground) {\n context.classList = elementClassList.join(' ');\n }\n\n // The icons are displayed either with, or without, the background.\n return Templates.renderForPromise('block_accessreview/status', context)\n .then(({html, js}) => {\n Templates.appendNodeContents(element, html, js);\n\n if (showBackground) {\n element.classList.add(...elementClassList, 'alert');\n }\n\n return;\n })\n .catch();\n};\n\n/**\n * Applies the template to all sections and modules on the course page.\n *\n * @param {Number} courseId\n * @param {String} displayFormat\n * @param {Boolean} updatePreference\n * @returns {Promise}\n */\nconst showAccessMap = (courseId, displayFormat, updatePreference = false) => {\n // Get error data.\n return Promise.all(fetchReviewData(courseId, updatePreference))\n .then(([sectionData, moduleData]) => {\n // Get total data.\n const {minViews, viewDelta} = getErrorTotals(sectionData, moduleData);\n\n sectionData.forEach(section => {\n const element = document.querySelector(`#section-${section.section} .summary`);\n if (!element) {\n return;\n }\n\n renderTemplate(element, section.numerrors, section.numchecks, displayFormat, minViews, viewDelta);\n });\n\n moduleData.forEach(module => {\n const element = document.getElementById(`module-${module.cmid}`);\n if (!element) {\n return;\n }\n\n renderTemplate(element, module.numerrors, module.numchecks, displayFormat, minViews, viewDelta);\n });\n\n // Change the icon display.\n document.querySelector('.icon-accessmap').classList.remove(...['fa-eye-slash']);\n document.querySelector('.icon-accessmap').classList.add(...['fa-eye']);\n\n return {\n sectionData,\n moduleData,\n };\n })\n .catch(displayError);\n};\n\n\n/**\n * Hides or removes the templates from the HTML of the current page.\n *\n * @param {Boolean} updatePreference\n */\nconst hideAccessMap = (updatePreference = false) => {\n // Removes the added elements.\n document.querySelectorAll('.block_accessreview_view').forEach(node => node.remove());\n\n const classList = [\n 'block_accessreview',\n 'block_accessreview_success',\n 'block_accessreview_warning',\n 'block_accessreview_danger',\n 'block_accessreview_view',\n 'alert',\n ];\n\n // Removes the added classes.\n document.querySelectorAll('.block_accessreview').forEach(node => node.classList.remove(...classList));\n\n if (updatePreference) {\n setToggleStatePreference(false);\n }\n\n // Change the icon display.\n document.querySelector('.icon-accessmap').classList.remove(...['fa-eye']);\n document.querySelector('.icon-accessmap').classList.add(...['fa-eye-slash']);\n};\n\n\n/**\n * Toggles the heatmap on/off.\n *\n * @param {Number} courseId\n * @param {String} displayFormat\n */\nconst toggleAccessMap = (courseId, displayFormat) => {\n toggleState = !toggleState;\n if (!toggleState) {\n hideAccessMap(true);\n } else {\n showAccessMap(courseId, displayFormat, true);\n }\n};\n\n/**\n * Parses information on the errors, generating the min, max and totals.\n *\n * @param {Object[]} sectionData The error data for course sections.\n * @param {Object[]} moduleData The error data for course modules.\n * @returns {Object} An object representing the extra error information.\n */\nconst getErrorTotals = (sectionData, moduleData) => {\n const totals = {\n totalErrors: 0,\n totalUsers: 0,\n minViews: 0,\n maxViews: 0,\n viewDelta: 0,\n };\n\n [].concat(sectionData, moduleData).forEach(item => {\n totals.totalErrors += item.numerrors;\n if (item.numerrors < totals.minViews) {\n totals.minViews = item.numerrors;\n }\n\n if (item.numerrors > totals.maxViews) {\n totals.maxViews = item.numerrors;\n }\n totals.totalUsers += item.numchecks;\n });\n\n totals.viewDelta = totals.maxViews - totals.minViews + 1;\n\n return totals;\n};\n\nconst registerEventListeners = (courseId, displayFormat) => {\n document.addEventListener('click', e => {\n if (e.target.closest('#toggle-accessmap')) {\n e.preventDefault();\n toggleAccessMap(courseId, displayFormat);\n }\n });\n};\n\n/**\n * Set the user preference for the toggle value.\n *\n * @param {Boolean} toggleState\n * @returns {Promise}\n */\nconst getTogglePreferenceParams = toggleState => {\n return {\n methodname: 'core_user_update_user_preferences',\n args: {\n preferences: [{\n type: 'block_accessreviewtogglestate',\n value: toggleState,\n }],\n }\n };\n};\n\nconst setToggleStatePreference = toggleState => fetchMany([getTogglePreferenceParams(toggleState)]);\n\n/**\n * Fetch the review data.\n *\n * @param {Number} courseid\n * @param {Boolean} updatePreference\n * @returns {Promise[]}\n */\nconst fetchReviewData = (courseid, updatePreference = false) => {\n const calls = [\n {\n methodname: 'block_accessreview_get_section_data',\n args: {courseid}\n },\n {\n methodname: 'block_accessreview_get_module_data',\n args: {courseid}\n },\n ];\n\n if (updatePreference) {\n calls.push(getTogglePreferenceParams(true));\n }\n\n return fetchMany(calls);\n};\n\n/**\n * Setting up the access review module.\n * @param {number} toggled A number represnting the state of the review toggle.\n * @param {string} displayFormat A string representing the display format for icons.\n * @param {number} courseId The course ID.\n */\nexport const init = (toggled, displayFormat, courseId) => {\n // Settings consts.\n toggleState = toggled == 1;\n\n if (toggleState) {\n showAccessMap(courseId, displayFormat);\n }\n\n registerEventListeners(courseId, displayFormat);\n};\n"],"names":["_getRequireWildcardCache","nodeInterop","WeakMap","cacheBabelInterop","cacheNodeInterop","Templates","obj","__esModule","default","cache","has","get","newObj","hasPropertyDescriptor","Object","defineProperty","getOwnPropertyDescriptor","key","prototype","hasOwnProperty","call","desc","set","_interopRequireWildcard","toggleState","renderTemplate","element","errorCount","checkCount","displayFormat","minViews","viewDelta","weight","parseInt","context","resultPassed","classList","passRate","failureRate","Math","round","Promise","resolve","elementClassList","push","showIcons","showBackground","add","join","renderForPromise","then","_ref","html","js","appendNodeContents","catch","showAccessMap","courseId","updatePreference","arguments","length","undefined","all","fetchReviewData","_ref2","sectionData","moduleData","getErrorTotals","forEach","section","document","querySelector","concat","numerrors","numchecks","module","getElementById","cmid","remove","displayError","toggleAccessMap","querySelectorAll","node","setToggleStatePreference","hideAccessMap","totals","totalErrors","totalUsers","maxViews","item","getTogglePreferenceParams","methodname","args","preferences","type","value","fetchMany","courseid","calls","_exports","init","toggled","registerEventListeners","addEventListener","e","target","closest","preventDefault"],"mappings":"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"}
\ No newline at end of file
diff --git a/blocks/amd/build/events.min.js.map b/blocks/amd/build/events.min.js.map
index aa41e06a66b..baf3953ba3e 100644
--- a/blocks/amd/build/events.min.js.map
+++ b/blocks/amd/build/events.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"events.min.js","sources":["../src/events.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/ //\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 events for the `core_block` subsystem.\n *\n * @module core_block/events\n * @copyright 2021 Andrew Nicols \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @since 4.0\n *\n * @example
Example of listening to a block event.
\n * import {eventTypes as blockEventTypes} from 'core_block/events';\n *\n * document.addEventListener(blockEventTypes.blockContentUpdated, e => {\n * window.console.log(e.target); // The HTMLElement relating to the block whose content was updated.\n * window.console.log(e.detail.instanceId); // The instanceId of the block that was updated.\n * });\n */\n\nimport {dispatchEvent} from 'core/event_dispatcher';\n\n/**\n * Events for `core_block`.\n *\n * @constant\n * @property {String} blockContentUpdated See {@link event:blockContentUpdated}\n */\nexport const eventTypes = {\n /**\n * An event triggered when the content of a block has changed.\n *\n * @event blockContentUpdated\n * @type {CustomEvent}\n * @property {HTMLElement} target The block element that was updated\n * @property {object} detail\n * @property {number} detail.instanceId The block instance id\n */\n blockContentUpdated: 'core_block/contentUpdated',\n};\n\n/**\n * Trigger an event to indicate that the content of a block was updated.\n *\n * @method notifyBlockContentUpdated\n * @param {HTMLElement} element The HTMLElement containing the updated block.\n * @returns {CustomEvent}\n * @fires blockContentUpdated\n */\nexport const notifyBlockContentUpdated = element => dispatchEvent(\n eventTypes.blockContentUpdated,\n {\n instanceId: element.dataset.instanceId,\n },\n element\n);\n\nlet legacyEventsRegistered = false;\nif (!legacyEventsRegistered) {\n // The following event triggers are legacy and will be removed in the future.\n // The following approach provides a backwards-compatability layer for the new events.\n // Code should be updated to make use of native events.\n\n Y.use('event', 'moodle-core-event', Y => {\n // Provide a backwards-compatability layer for YUI Events.\n document.addEventListener(eventTypes.blockContentUpdated, e => {\n // Trigger the legacy YUI event.\n Y.Global.fire(M.core.event.BLOCK_CONTENT_UPDATED, {instanceid: e.detail.instanceId});\n });\n });\n\n legacyEventsRegistered = true;\n}\n"],"names":["eventTypes","blockContentUpdated","element","instanceId","dataset","legacyEventsRegistered","Y","use","document","addEventListener","e","Global","fire","M","core","event","BLOCK_CONTENT_UPDATED","instanceid","detail"],"mappings":";;;;;;;;;;;;;;;;;MAuCaA,WAAa,CAUtBC,oBAAqB,+FAWgBC,UAAW,mCAChDF,WAAWC,oBACX,CACIE,WAAYD,QAAQE,QAAQD,YAEhCD,aAGAG,wBAAyB,EACxBA,yBAKDC,EAAEC,IAAI,QAAS,qBAAqBD,IAEhCE,SAASC,iBAAiBT,WAAWC,qBAAqBS,IAEtDJ,EAAEK,OAAOC,KAAKC,EAAEC,KAAKC,MAAMC,sBAAuB,CAACC,WAAYP,EAAEQ,OAAOf,mBAIhFE,wBAAyB"}
\ No newline at end of file
+{"version":3,"file":"events.min.js","sources":["../src/events.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/ //\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 events for the `core_block` subsystem.\n *\n * @module core_block/events\n * @copyright 2021 Andrew Nicols \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @since 4.0\n *\n * @example
Example of listening to a block event.
\n * import {eventTypes as blockEventTypes} from 'core_block/events';\n *\n * document.addEventListener(blockEventTypes.blockContentUpdated, e => {\n * window.console.log(e.target); // The HTMLElement relating to the block whose content was updated.\n * window.console.log(e.detail.instanceId); // The instanceId of the block that was updated.\n * });\n */\n\nimport {dispatchEvent} from 'core/event_dispatcher';\n\n/**\n * Events for `core_block`.\n *\n * @constant\n * @property {String} blockContentUpdated See {@link event:blockContentUpdated}\n */\nexport const eventTypes = {\n /**\n * An event triggered when the content of a block has changed.\n *\n * @event blockContentUpdated\n * @type {CustomEvent}\n * @property {HTMLElement} target The block element that was updated\n * @property {object} detail\n * @property {number} detail.instanceId The block instance id\n */\n blockContentUpdated: 'core_block/contentUpdated',\n};\n\n/**\n * Trigger an event to indicate that the content of a block was updated.\n *\n * @method notifyBlockContentUpdated\n * @param {HTMLElement} element The HTMLElement containing the updated block.\n * @returns {CustomEvent}\n * @fires blockContentUpdated\n */\nexport const notifyBlockContentUpdated = element => dispatchEvent(\n eventTypes.blockContentUpdated,\n {\n instanceId: element.dataset.instanceId,\n },\n element\n);\n\nlet legacyEventsRegistered = false;\nif (!legacyEventsRegistered) {\n // The following event triggers are legacy and will be removed in the future.\n // The following approach provides a backwards-compatability layer for the new events.\n // Code should be updated to make use of native events.\n\n Y.use('event', 'moodle-core-event', Y => {\n // Provide a backwards-compatability layer for YUI Events.\n document.addEventListener(eventTypes.blockContentUpdated, e => {\n // Trigger the legacy YUI event.\n Y.Global.fire(M.core.event.BLOCK_CONTENT_UPDATED, {instanceid: e.detail.instanceId});\n });\n });\n\n legacyEventsRegistered = true;\n}\n"],"names":["eventTypes","blockContentUpdated","_exports","notifyBlockContentUpdated","element","dispatchEvent","instanceId","dataset","legacyEventsRegistered","Y","use","document","addEventListener","e","Global","fire","M","core","event","BLOCK_CONTENT_UPDATED","instanceid","detail"],"mappings":";;;;;;;;;;;;;;;;;AAuCO,MAAMA,WAAa,CAUtBC,oBAAqB,6BACvBC,SAAAF,WAAAA,WAgBAE,SAAAC,0BANuCC,UAAW,EAAAC,iCAChDL,WAAWC,oBACX,CACIK,WAAYF,QAAQG,QAAQD,YAEhCF,SAGJ,IAAII,wBAAyB,EACxBA,yBAKDC,EAAEC,IAAI,QAAS,qBAAqBD,IAEhCE,SAASC,iBAAiBZ,WAAWC,qBAAqBY,IAEtDJ,EAAEK,OAAOC,KAAKC,EAAEC,KAAKC,MAAMC,sBAAuB,CAACC,WAAYP,EAAEQ,OAAOf,YAAY,GACtF,IAGNE,wBAAyB,EAC5B"}
\ No newline at end of file
diff --git a/blocks/myoverview/amd/build/main.min.js.map b/blocks/myoverview/amd/build/main.min.js.map
index 774fae61211..64390a7abff 100644
--- a/blocks/myoverview/amd/build/main.min.js.map
+++ b/blocks/myoverview/amd/build/main.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"main.min.js","sources":["../src/main.js"],"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 to initialise the myoverview block.\n *\n * @copyright 2018 Bas Brands \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport * as View from 'block_myoverview/view';\nimport * as ViewNav from 'block_myoverview/view_nav';\n\n/**\n * Initialise all of the modules for the overview block.\n *\n * @param {object} root The root element for the overview block.\n */\nexport const init = (root) => {\n // Initialise the course navigation elements.\n ViewNav.init(root);\n // Initialise the courses view modules.\n View.init(root);\n};\n"],"names":["root","ViewNav","init","View"],"mappings":";;;;;;4KA8BqBA,OAEjBC,QAAQC,KAAKF,MAEbG,KAAKD,KAAKF"}
\ No newline at end of file
+{"version":3,"file":"main.min.js","sources":["../src/main.js"],"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 to initialise the myoverview block.\n *\n * @copyright 2018 Bas Brands \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport * as View from 'block_myoverview/view';\nimport * as ViewNav from 'block_myoverview/view_nav';\n\n/**\n * Initialise all of the modules for the overview block.\n *\n * @param {object} root The root element for the overview block.\n */\nexport const init = (root) => {\n // Initialise the course navigation elements.\n ViewNav.init(root);\n // Initialise the courses view modules.\n View.init(root);\n};\n"],"names":["_getRequireWildcardCache","nodeInterop","WeakMap","cacheBabelInterop","cacheNodeInterop","_interopRequireWildcard","obj","__esModule","default","cache","has","get","newObj","hasPropertyDescriptor","Object","defineProperty","getOwnPropertyDescriptor","key","prototype","hasOwnProperty","call","desc","set","View","ViewNav","_exports","init","root"],"mappings":"gIAuBqD,SAAAA,yBAAAC,aAAA,GAAA,mBAAAC,QAAA,OAAA,KAAA,IAAAC,kBAAAD,IAAAA,QAAAE,iBAAAF,IAAAA,eAAAF,yBAAA,SAAAC,aAAAA,OAAAA,YAAAG,iBAAAD,oBAAAF,YAAA,CAAA,SAAAI,wBAAAC,IAAAL,aAAAA,IAAAA,aAAAK,KAAAA,IAAAC,WAAAD,OAAAA,IAAAA,GAAAA,OAAAA,KAAAA,iBAAAA,KAAAE,mBAAAF,IAAAE,MAAAA,CAAAA,QAAAF,KAAAG,IAAAA,MAAAT,yBAAAC,aAAA,GAAAQ,OAAAA,MAAAC,IAAAJ,KAAA,OAAAG,MAAAE,IAAAL,KAAA,IAAAM,OAAAC,GAAAA,sBAAAC,OAAAC,gBAAAD,OAAAE,yBAAAC,IAAAA,IAAAA,OAAAX,IAAAW,eAAAA,KAAAH,OAAAI,UAAAC,eAAAC,KAAAd,IAAAW,KAAA,CAAA,IAAAI,KAAAR,sBAAAC,OAAAE,yBAAAV,IAAAW,KAAAI,KAAAA,OAAAA,KAAAV,KAAAU,KAAAC,KAAAR,OAAAC,eAAAH,OAAAK,IAAAI,MAAAT,OAAAK,KAAAX,IAAAW,IAAAL,QAAAA,OAAAJ,QAAAF,IAAAG,OAAAA,MAAAa,IAAAhB,IAAAM,QAAAA,MAAA;;;;;;kFADrDW,KAAAlB,wBAAAkB,MACAC,QAAAnB,wBAAAmB,SAYEC,SAAAC,KALmBC,OAEjBH,QAAQE,KAAKC,MAEbJ,KAAKG,KAAKC,KAAK,CACjB"}
\ No newline at end of file
diff --git a/blocks/myoverview/amd/build/repository.min.js.map b/blocks/myoverview/amd/build/repository.min.js.map
index 06850a439a5..8c8632b7071 100644
--- a/blocks/myoverview/amd/build/repository.min.js.map
+++ b/blocks/myoverview/amd/build/repository.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"repository.min.js","sources":["../src/repository.js"],"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 * A javascript module to retrieve enrolled coruses from the server.\n *\n * @module block_myoverview/repository\n * @copyright 2018 Bas Brands \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Ajax from 'core/ajax';\nimport * as Notification from 'core/notification';\n\n/**\n * Retrieve a list of enrolled courses.\n *\n * Valid args are:\n * string classification future, inprogress, past\n * int limit number of records to retreive\n * int Offset offset for pagination\n * int sort sort by lastaccess or name\n *\n * @method getEnrolledCoursesByTimeline\n * @param {object} args The request arguments\n * @return {promise} Resolved with an array of courses\n */\nexport const getEnrolledCoursesByTimeline = args => {\n const request = {\n methodname: 'core_course_get_enrolled_courses_by_timeline_classification',\n args: args\n };\n\n return Ajax.call([request])[0];\n};\n\n/**\n * Set the favourite state on a list of courses.\n *\n * Valid args are:\n * Array courses list of course id numbers.\n *\n * @param {Object} args Arguments send to the webservice.\n * @return {Promise} Resolve with warnings.\n */\nexport const setFavouriteCourses = args => {\n const request = {\n methodname: 'core_course_set_favourite_courses',\n args: args\n };\n\n return Ajax.call([request])[0];\n};\n\n/**\n * Update the user preferences.\n *\n * @param {Object} args Arguments send to the webservice.\n *\n * Sample args:\n * {\n * preferences: [\n * {\n * type: 'block_example_user_sort_preference'\n * value: 'title'\n * }\n * ]\n * }\n */\nexport const updateUserPreferences = args => {\n const request = {\n methodname: 'core_user_update_user_preferences',\n args: args\n };\n\n Ajax.call([request])[0]\n .fail(Notification.exception);\n};\n"],"names":["args","request","methodname","Ajax","call","fail","Notification","exception"],"mappings":";;;;;;;2rCAuC4CA,aAClCC,QAAU,CACZC,WAAY,8DACZF,KAAMA,aAGHG,cAAKC,KAAK,CAACH,UAAU,iCAYGD,aACzBC,QAAU,CACZC,WAAY,oCACZF,KAAMA,aAGHG,cAAKC,KAAK,CAACH,UAAU,mCAkBKD,aAC3BC,QAAU,CACZC,WAAY,oCACZF,KAAMA,oBAGLI,KAAK,CAACH,UAAU,GAChBI,KAAKC,aAAaC"}
\ No newline at end of file
+{"version":3,"file":"repository.min.js","sources":["../src/repository.js"],"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 * A javascript module to retrieve enrolled coruses from the server.\n *\n * @module block_myoverview/repository\n * @copyright 2018 Bas Brands \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Ajax from 'core/ajax';\nimport * as Notification from 'core/notification';\n\n/**\n * Retrieve a list of enrolled courses.\n *\n * Valid args are:\n * string classification future, inprogress, past\n * int limit number of records to retreive\n * int Offset offset for pagination\n * int sort sort by lastaccess or name\n *\n * @method getEnrolledCoursesByTimeline\n * @param {object} args The request arguments\n * @return {promise} Resolved with an array of courses\n */\nexport const getEnrolledCoursesByTimeline = args => {\n const request = {\n methodname: 'core_course_get_enrolled_courses_by_timeline_classification',\n args: args\n };\n\n return Ajax.call([request])[0];\n};\n\n/**\n * Set the favourite state on a list of courses.\n *\n * Valid args are:\n * Array courses list of course id numbers.\n *\n * @param {Object} args Arguments send to the webservice.\n * @return {Promise} Resolve with warnings.\n */\nexport const setFavouriteCourses = args => {\n const request = {\n methodname: 'core_course_set_favourite_courses',\n args: args\n };\n\n return Ajax.call([request])[0];\n};\n\n/**\n * Update the user preferences.\n *\n * @param {Object} args Arguments send to the webservice.\n *\n * Sample args:\n * {\n * preferences: [\n * {\n * type: 'block_example_user_sort_preference'\n * value: 'title'\n * }\n * ]\n * }\n */\nexport const updateUserPreferences = args => {\n const request = {\n methodname: 'core_user_update_user_preferences',\n args: args\n };\n\n Ajax.call([request])[0]\n .fail(Notification.exception);\n};\n"],"names":["obj","_getRequireWildcardCache","nodeInterop","WeakMap","cacheBabelInterop","cacheNodeInterop","_ajax","__esModule","default","Notification","cache","has","get","newObj","hasPropertyDescriptor","Object","defineProperty","getOwnPropertyDescriptor","key","prototype","hasOwnProperty","call","desc","set","_interopRequireWildcard","_exports","getEnrolledCoursesByTimeline","args","request","methodname","Ajax","setFavouriteCourses","updateUserPreferences","fail","exception"],"mappings":"wHAwBkD,IAAAA;;;;;;;KAAA,SAAAC,yBAAAC,aAAA,GAAA,mBAAAC,QAAA,OAAA,KAAA,IAAAC,kBAAAD,IAAAA,QAAAE,iBAAAF,IAAAA,eAAAF,yBAAA,SAAAC,aAAAA,OAAAA,YAAAG,iBAAAD,oBAAAF,YAAA,kKADlDI,OACkDN,IADlDM,QACkDN,IAAAO,WAAAP,IAAAQ,CAAAA,QAAAR,KAAlDS,aAAkD,SAAAT,IAAAE,aAAAA,IAAAA,aAAAF,KAAAA,IAAAO,WAAAP,OAAAA,IAAAA,GAAAA,OAAAA,KAAAA,iBAAAA,KAAAQ,mBAAAR,IAAAQ,MAAAA,CAAAA,QAAAR,KAAAU,IAAAA,MAAAT,yBAAAC,aAAA,GAAAQ,OAAAA,MAAAC,IAAAX,KAAA,OAAAU,MAAAE,IAAAZ,KAAA,IAAAa,OAAAC,GAAAA,sBAAAC,OAAAC,gBAAAD,OAAAE,yBAAAC,IAAAA,IAAAA,OAAAlB,IAAAkB,eAAAA,KAAAH,OAAAI,UAAAC,eAAAC,KAAArB,IAAAkB,KAAA,CAAA,IAAAI,KAAAR,sBAAAC,OAAAE,yBAAAjB,IAAAkB,KAAAI,KAAAA,OAAAA,KAAAV,KAAAU,KAAAC,KAAAR,OAAAC,eAAAH,OAAAK,IAAAI,MAAAT,OAAAK,KAAAlB,IAAAkB,IAAAL,CAAAA,OAAAL,QAAAR,IAAAU,OAAAA,MAAAa,IAAAvB,IAAAa,eAAAA,MAAA,CAAlDW,CAAAf,cAsBEgB,SAAAC,6BAP0CC,OACxC,MAAMC,QAAU,CACZC,WAAY,8DACZF,KAAMA,MAGV,OAAOG,MAAAA,QAAKT,KAAK,CAACO,UAAU,EAAE,EAmBhCH,SAAAM,oBAPiCJ,OAC/B,MAAMC,QAAU,CACZC,WAAY,oCACZF,KAAMA,MAGV,OAAOG,MAAAA,QAAKT,KAAK,CAACO,UAAU,EAAE,EA0BhCH,SAAAO,sBARmCL,OACjC,MAAMC,QAAU,CACZC,WAAY,oCACZF,KAAMA,MAGVG,MAAAA,QAAKT,KAAK,CAACO,UAAU,GAChBK,KAAKxB,aAAayB,UAAU,CACnC"}
\ No newline at end of file
diff --git a/blocks/myoverview/amd/build/selectors.min.js.map b/blocks/myoverview/amd/build/selectors.min.js.map
index 70c1d52d33e..ad5d486f221 100644
--- a/blocks/myoverview/amd/build/selectors.min.js.map
+++ b/blocks/myoverview/amd/build/selectors.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"selectors.min.js","sources":["../src/selectors.js"],"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 to initialise the selectors for the myoverview block.\n *\n * @copyright 2018 Peter Dias \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nexport default {\n courseView: {\n region: '[data-region=\"courses-view\"]',\n regionContent: '[data-region=\"course-view-content\"]'\n },\n FILTERS: '[data-region=\"filter\"]',\n FILTER_OPTION: '[data-filter]',\n DISPLAY_OPTION: '[data-display-option]',\n ACTION_HIDE_COURSE: '[data-action=\"hide-course\"]',\n ACTION_SHOW_COURSE: '[data-action=\"show-course\"]',\n ACTION_ADD_FAVOURITE: '[data-action=\"add-favourite\"]',\n ACTION_REMOVE_FAVOURITE: '[data-action=\"remove-favourite\"]',\n FAVOURITE_ICON: '[data-region=\"favourite-icon\"]',\n ICON_IS_FAVOURITE: '[data-region=\"is-favourite\"]',\n ICON_NOT_FAVOURITE: '[data-region=\"not-favourite\"]',\n region: {\n selectBlock: '[data-region=\"myoverview\"]',\n clearIcon: '[data-action=\"clearsearch\"]',\n searchInput: '[data-action=\"search\"]',\n },\n};\n"],"names":["courseView","region","regionContent","FILTERS","FILTER_OPTION","DISPLAY_OPTION","ACTION_HIDE_COURSE","ACTION_SHOW_COURSE","ACTION_ADD_FAVOURITE","ACTION_REMOVE_FAVOURITE","FAVOURITE_ICON","ICON_IS_FAVOURITE","ICON_NOT_FAVOURITE","selectBlock","clearIcon","searchInput"],"mappings":"4KAsBe,CACXA,WAAY,CACRC,OAAQ,+BACRC,cAAe,uCAEnBC,QAAS,yBACTC,cAAe,gBACfC,eAAgB,wBAChBC,mBAAoB,8BACpBC,mBAAoB,8BACpBC,qBAAsB,gCACtBC,wBAAyB,mCACzBC,eAAgB,iCAChBC,kBAAmB,+BACnBC,mBAAoB,gCACpBX,OAAQ,CACJY,YAAa,6BACbC,UAAW,8BACXC,YAAa"}
\ No newline at end of file
+{"version":3,"file":"selectors.min.js","sources":["../src/selectors.js"],"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 to initialise the selectors for the myoverview block.\n *\n * @copyright 2018 Peter Dias \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nexport default {\n courseView: {\n region: '[data-region=\"courses-view\"]',\n regionContent: '[data-region=\"course-view-content\"]'\n },\n FILTERS: '[data-region=\"filter\"]',\n FILTER_OPTION: '[data-filter]',\n DISPLAY_OPTION: '[data-display-option]',\n ACTION_HIDE_COURSE: '[data-action=\"hide-course\"]',\n ACTION_SHOW_COURSE: '[data-action=\"show-course\"]',\n ACTION_ADD_FAVOURITE: '[data-action=\"add-favourite\"]',\n ACTION_REMOVE_FAVOURITE: '[data-action=\"remove-favourite\"]',\n FAVOURITE_ICON: '[data-region=\"favourite-icon\"]',\n ICON_IS_FAVOURITE: '[data-region=\"is-favourite\"]',\n ICON_NOT_FAVOURITE: '[data-region=\"not-favourite\"]',\n region: {\n selectBlock: '[data-region=\"myoverview\"]',\n clearIcon: '[data-action=\"clearsearch\"]',\n searchInput: '[data-action=\"search\"]',\n },\n};\n"],"names":["_exports","default","courseView","region","regionContent","FILTERS","FILTER_OPTION","DISPLAY_OPTION","ACTION_HIDE_COURSE","ACTION_SHOW_COURSE","ACTION_ADD_FAVOURITE","ACTION_REMOVE_FAVOURITE","FAVOURITE_ICON","ICON_IS_FAVOURITE","ICON_NOT_FAVOURITE","selectBlock","clearIcon","searchInput"],"mappings":"oJA0CC,OAAAA,SAAAC,QApBc,CACXC,WAAY,CACRC,OAAQ,+BACRC,cAAe,uCAEnBC,QAAS,yBACTC,cAAe,gBACfC,eAAgB,wBAChBC,mBAAoB,8BACpBC,mBAAoB,8BACpBC,qBAAsB,gCACtBC,wBAAyB,mCACzBC,eAAgB,iCAChBC,kBAAmB,+BACnBC,mBAAoB,gCACpBX,OAAQ,CACJY,YAAa,6BACbC,UAAW,8BACXC,YAAa,2BAEpBjB,SAAAC,OAAA"}
\ No newline at end of file
diff --git a/blocks/myoverview/amd/build/view.min.js b/blocks/myoverview/amd/build/view.min.js
index d63f0422495..1d72954594f 100644
--- a/blocks/myoverview/amd/build/view.min.js
+++ b/blocks/myoverview/amd/build/view.min.js
@@ -4,6 +4,6 @@ define("block_myoverview/view",["exports","jquery","block_myoverview/repository"
*
* @copyright 2018 Bas Brands
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.reset=_exports.init=_exports.clearSearch=void 0,_jquery=_interopRequireDefault(_jquery),Repository=_interopRequireWildcard(Repository),PagedContentFactory=_interopRequireWildcard(PagedContentFactory),PubSub=_interopRequireWildcard(PubSub),CustomEvents=_interopRequireWildcard(CustomEvents),Notification=_interopRequireWildcard(Notification),Templates=_interopRequireWildcard(Templates),CourseEvents=_interopRequireWildcard(CourseEvents),_selectors=_interopRequireDefault(_selectors),PagedContentEvents=_interopRequireWildcard(PagedContentEvents),Aria=_interopRequireWildcard(Aria);const TEMPLATES_COURSES_CARDS="block_myoverview/view-cards",TEMPLATES_COURSES_LIST="block_myoverview/view-list",TEMPLATES_COURSES_SUMMARY="block_myoverview/view-summary",TEMPLATES_NOCOURSES="core_course/no-courses",GROUPINGS_GROUPING_ALLINCLUDINGHIDDEN="allincludinghidden",NUMCOURSES_PERPAGE=[12,24,48,96,0];let loadedPages=[],courseOffset=0,lastPage=0,lastLimit=0,namespace=null;const getFilterValues=root=>{const courseRegion=root.find(_selectors.default.courseView.region);return{display:courseRegion.attr("data-display"),grouping:courseRegion.attr("data-grouping"),sort:courseRegion.attr("data-sort"),displaycategories:courseRegion.attr("data-displaycategories"),customfieldname:courseRegion.attr("data-customfieldname"),customfieldvalue:courseRegion.attr("data-customfieldvalue")}},DEFAULT_PAGED_CONTENT_CONFIG={ignoreControlWhileLoading:!0,controlPlacementBottom:!0,persistentLimitKey:"block_myoverview_user_paging_preference"},getFavouriteIconContainer=(root,courseId)=>root.find(_selectors.default.FAVOURITE_ICON+'[data-course-id="'+courseId+'"]'),getPagedContentContainer=(root,index)=>root.find('[data-region="paged-content-page"][data-page="'+index+'"]'),getCourseId=root=>root.attr("data-course-id"),getAddFavouriteMenuItem=(root,courseId)=>root.find('[data-action="add-favourite"][data-course-id="'+courseId+'"]'),getRemoveFavouriteMenuItem=(root,courseId)=>root.find('[data-action="remove-favourite"][data-course-id="'+courseId+'"]'),addToFavourites=(root,courseId)=>{const removeAction=getRemoveFavouriteMenuItem(root,courseId),addAction=getAddFavouriteMenuItem(root,courseId);setCourseFavouriteState(courseId,!0).then((success=>{success?(PubSub.publish(CourseEvents.favourited,courseId),removeAction.removeClass("hidden"),addAction.addClass("hidden"),((root,courseId)=>{const iconContainer=getFavouriteIconContainer(root,courseId),isFavouriteIcon=iconContainer.find(_selectors.default.ICON_IS_FAVOURITE);isFavouriteIcon.removeClass("hidden"),Aria.unhide(isFavouriteIcon);const notFavourteIcon=iconContainer.find(_selectors.default.ICON_NOT_FAVOURITE);notFavourteIcon.addClass("hidden"),Aria.hide(notFavourteIcon)})(root,courseId)):Notification.alert("Starring course failed","Could not change favourite state")})).catch(Notification.exception)},removeFromFavourites=(root,courseId)=>{const removeAction=getRemoveFavouriteMenuItem(root,courseId),addAction=getAddFavouriteMenuItem(root,courseId);setCourseFavouriteState(courseId,!1).then((success=>{success?(PubSub.publish(CourseEvents.unfavorited,courseId),removeAction.addClass("hidden"),addAction.removeClass("hidden"),((root,courseId)=>{const iconContainer=getFavouriteIconContainer(root,courseId),isFavouriteIcon=iconContainer.find(_selectors.default.ICON_IS_FAVOURITE);isFavouriteIcon.addClass("hidden"),Aria.hide(isFavouriteIcon);const notFavourteIcon=iconContainer.find(_selectors.default.ICON_NOT_FAVOURITE);notFavourteIcon.removeClass("hidden"),Aria.unhide(notFavourteIcon)})(root,courseId)):Notification.alert("Starring course failed","Could not change favourite state")})).catch(Notification.exception)},getHideCourseMenuItem=(root,courseId)=>root.find('[data-action="hide-course"][data-course-id="'+courseId+'"]'),getShowCourseMenuItem=(root,courseId)=>root.find('[data-action="show-course"][data-course-id="'+courseId+'"]'),setCourseHiddenState=(courseId,status)=>(!1===status&&(status=null),Repository.updateUserPreferences({preferences:[{type:"block_myoverview_hidden_course_"+courseId,value:status}]})),hideElement=(root,id)=>{const pagingBar=root.find('[data-region="paging-bar"]'),jumpto=parseInt(pagingBar.attr("data-active-page-number"));let reducedCourse=loadedPages[jumpto].courses.reduce(((accumulator,current)=>(+id!=+current.id&&accumulator.push(current),accumulator)),[]);if(void 0!==loadedPages[jumpto+1]){const newElement=loadedPages[jumpto+1].courses.slice(0,1);loadedPages.forEach(((courseList,index)=>{if(index>jumpto){let popElement=[];void 0!==loadedPages[index+1]&&(popElement=loadedPages[index+1].courses.slice(0,1)),loadedPages[index].courses=[...loadedPages[index].courses.slice(1),...popElement]}})),reducedCourse=[...reducedCourse,...newElement]}if(lastPage===jumpto+1&&0===loadedPages[jumpto+1].courses.length){const pagedContentContainer=root.find('[data-region="paged-content-container"]');PagedContentFactory.resetLastPageNumber((0,_jquery.default)(pagedContentContainer).attr("id"),jumpto)}loadedPages[jumpto].courses=reducedCourse,courseOffset--;const pagedContentPage=getPagedContentContainer(root,jumpto);renderCourses(root,loadedPages[jumpto]).then(((html,js)=>Templates.replaceNodeContents(pagedContentPage,html,js))).catch(Notification.exception),loadedPages.forEach(((courseList,index)=>{if(index>jumpto){getPagedContentContainer(root,index).remove()}}))},setCourseFavouriteState=(courseId,status)=>Repository.setFavouriteCourses({courses:[{id:courseId,favourite:status}]}).then((result=>0===result.warnings.length&&(loadedPages.forEach((courseList=>{courseList.courses.forEach(((course,index)=>{course.id==courseId&&(courseList.courses[index].isfavourite=status)}))})),!0))).catch(Notification.exception),noCoursesRender=root=>{const nocoursesimg=root.find(_selectors.default.courseView.region).attr("data-nocoursesimg"),newcourseurl=root.find(_selectors.default.courseView.region).attr("data-newcourseurl");return Templates.render(TEMPLATES_NOCOURSES,{nocoursesimg:nocoursesimg,newcourseurl:newcourseurl})},renderCourses=(root,coursesData)=>{const filters=getFilterValues(root);let currentTemplate="";return currentTemplate="card"===filters.display?TEMPLATES_COURSES_CARDS:"list"===filters.display?TEMPLATES_COURSES_LIST:TEMPLATES_COURSES_SUMMARY,coursesData?(!1===Array.isArray(coursesData.courses)&&(coursesData.courses=Object.values(coursesData.courses)),coursesData.courses=coursesData.courses.map((course=>(course.showcoursecategory="on"===filters.displaycategories,course))),coursesData.courses.length?Templates.render(currentTemplate,{courses:coursesData.courses}):noCoursesRender(root)):noCoursesRender(root)},registerPagedEventHandlers=(root,namespace)=>{const event=namespace+PagedContentEvents.SET_ITEMS_PER_PAGE_LIMIT;PubSub.subscribe(event,(root=>limit=>root.find(_selectors.default.courseView.region).attr("data-paging",limit))(root))},itemsPerPageFunc=(pagingLimit,root)=>{let itemsPerPage=NUMCOURSES_PERPAGE.map((value=>{let active=!1;return value===pagingLimit&&(active=!0),{value:value,active:active}}));const totalCourseCount=parseInt(root.find(_selectors.default.courseView.region).attr("data-totalcoursecount"),10);return itemsPerPage.filter((pagingOption=>pagingOption.value4&&void 0!==arguments[4]?arguments[4]:null,courses=coursesData.courses?coursesData.courses:coursesData,nextPageStart=0,pageCourses=[];if(void 0!==loadedPages[currentPage]){pageCourses=loadedPages[currentPage].courses;const currentPageLength=pageCourses.length;currentPageLength0?courses.slice(0,pageData.limit):courses;loadedPages[currentPage]={courses:pageCourses};const remainingCourses=!1!==nextPageStart?courses.slice(nextPageStart,courses.length):[];remainingCourses.length&&(loadedPages[currentPage+1]={courses:remainingCourses}),loadedPages[currentPage].courses.length{courseOffset=0,loadedPages=[],lastPage=0,lastLimit=0},standardFunctionalityCurry=()=>(resetGlobals(),(filters,currentPage,pageData,actions,root,promises,limit)=>{const pagePromise=((filters,limit)=>Repository.getEnrolledCoursesByTimeline({offset:courseOffset,limit:limit,classification:filters.grouping,sort:filters.sort,customfieldname:filters.customfieldname,customfieldvalue:filters.customfieldvalue}))(filters,limit).then((coursesData=>(pageBuilder(coursesData,currentPage,pageData,actions),renderCourses(root,loadedPages[currentPage])))).catch(Notification.exception);promises.push(pagePromise)}),searchFunctionalityCurry=()=>(resetGlobals(),(filters,currentPage,pageData,actions,root,promises,limit,inputValue)=>{const searchingPromise=((filters,limit,searchValue)=>Repository.getEnrolledCoursesByTimeline({offset:courseOffset,limit:limit,classification:"search",sort:filters.sort,customfieldname:filters.customfieldname,customfieldvalue:filters.customfieldvalue,searchvalue:searchValue}))(filters,limit,inputValue).then((coursesData=>(pageBuilder(coursesData,currentPage,pageData,actions),renderCourses(root,loadedPages[currentPage])))).catch(Notification.exception);promises.push(searchingPromise)}),initializePagedContent=function(root,promiseFunction){let inputValue=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null;const pagingLimit=parseInt(root.find(_selectors.default.courseView.region).attr("data-paging"),10);let itemsPerPage=itemsPerPageFunc(pagingLimit,root);const filters=getFilterValues(root),config={...DEFAULT_PAGED_CONTENT_CONFIG};config.eventNamespace=namespace;const pagedContentPromise=PagedContentFactory.createWithLimit(itemsPerPage,((pagesData,actions)=>{let promises=[];return pagesData.forEach((pageData=>{const currentPage=pageData.pageNumber;let limit=pageData.limit>0?pageData.limit:0;if(+lastLimit!=+limit&&(loadedPages=[],courseOffset=0,lastPage=0),lastPage===currentPage)return actions.allItemsLoaded(lastPage),void promises.push(renderCourses(root,loadedPages[currentPage]));lastLimit=limit,void 0===loadedPages[currentPage+1]&&void 0===loadedPages[currentPage]&&(limit*=2),promiseFunction(filters,currentPage,pageData,actions,root,promises,limit,inputValue)})),promises}),config);pagedContentPromise.then(((html,js)=>(registerPagedEventHandlers(root,namespace),Templates.replaceNodeContents(root.find(_selectors.default.courseView.region),html,js)))).catch(Notification.exception)},registerEventListeners=(root,page)=>{CustomEvents.define(root,[CustomEvents.events.activate]),root.on(CustomEvents.events.activate,_selectors.default.ACTION_ADD_FAVOURITE,((e,data)=>{const favourite=(0,_jquery.default)(e.target).closest(_selectors.default.ACTION_ADD_FAVOURITE),courseId=getCourseId(favourite);addToFavourites(root,courseId),data.originalEvent.preventDefault()})),root.on(CustomEvents.events.activate,_selectors.default.ACTION_REMOVE_FAVOURITE,((e,data)=>{const favourite=(0,_jquery.default)(e.target).closest(_selectors.default.ACTION_REMOVE_FAVOURITE),courseId=getCourseId(favourite);removeFromFavourites(root,courseId),data.originalEvent.preventDefault()})),root.on(CustomEvents.events.activate,_selectors.default.FAVOURITE_ICON,((e,data)=>{data.originalEvent.preventDefault()})),root.on(CustomEvents.events.activate,_selectors.default.ACTION_HIDE_COURSE,((e,data)=>{const target=(0,_jquery.default)(e.target).closest(_selectors.default.ACTION_HIDE_COURSE),courseId=getCourseId(target);((root,courseId)=>{const hideAction=getHideCourseMenuItem(root,courseId),showAction=getShowCourseMenuItem(root,courseId),filters=getFilterValues(root);setCourseHiddenState(courseId,!0),filters.grouping!==GROUPINGS_GROUPING_ALLINCLUDINGHIDDEN&&hideElement(root,courseId),hideAction.addClass("hidden"),showAction.removeClass("hidden")})(root,courseId),data.originalEvent.preventDefault()})),root.on(CustomEvents.events.activate,_selectors.default.ACTION_SHOW_COURSE,((e,data)=>{const target=(0,_jquery.default)(e.target).closest(_selectors.default.ACTION_SHOW_COURSE),courseId=getCourseId(target);((root,courseId)=>{const hideAction=getHideCourseMenuItem(root,courseId),showAction=getShowCourseMenuItem(root,courseId),filters=getFilterValues(root);setCourseHiddenState(courseId,null),filters.grouping!==GROUPINGS_GROUPING_ALLINCLUDINGHIDDEN&&hideElement(root,courseId),hideAction.removeClass("hidden"),showAction.addClass("hidden")})(root,courseId),data.originalEvent.preventDefault()}));const input=page.querySelector(_selectors.default.region.searchInput),clearIcon=page.querySelector(_selectors.default.region.clearIcon);clearIcon.addEventListener("click",(()=>{input.value="",input.focus(),clearSearch(clearIcon,root)})),input.addEventListener("input",(0,_utils.debounce)((()=>{""===input.value?clearSearch(clearIcon,root):(activeSearch(clearIcon),initializePagedContent(root,searchFunctionalityCurry(),input.value.trim()))}),1e3))},clearSearch=(clearIcon,root)=>{clearIcon.classList.add("d-none"),init(root)};_exports.clearSearch=clearSearch;const activeSearch=clearIcon=>{clearIcon.classList.remove("d-none")},init=root=>{if(root=(0,_jquery.default)(root),loadedPages=[],lastPage=0,courseOffset=0,!root.attr("data-init")){const page=document.querySelector(_selectors.default.region.selectBlock);registerEventListeners(root,page),namespace="block_myoverview_"+root.attr("id")+"_"+Math.random(),root.attr("data-init",!0)}initializePagedContent(root,standardFunctionalityCurry())};_exports.init=init;_exports.reset=root=>{loadedPages.length>0?loadedPages.forEach(((courseList,index)=>{let pagedContentPage=getPagedContentContainer(root,index);renderCourses(root,courseList).then(((html,js)=>Templates.replaceNodeContents(pagedContentPage,html,js))).catch(Notification.exception)})):init(root)}}));
+ */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.reset=_exports.init=_exports.clearSearch=void 0,_jquery=_interopRequireDefault(_jquery),Repository=_interopRequireWildcard(Repository),PagedContentFactory=_interopRequireWildcard(PagedContentFactory),PubSub=_interopRequireWildcard(PubSub),CustomEvents=_interopRequireWildcard(CustomEvents),Notification=_interopRequireWildcard(Notification),Templates=_interopRequireWildcard(Templates),CourseEvents=_interopRequireWildcard(CourseEvents),_selectors=_interopRequireDefault(_selectors),PagedContentEvents=_interopRequireWildcard(PagedContentEvents),Aria=_interopRequireWildcard(Aria);const TEMPLATES_COURSES_CARDS="block_myoverview/view-cards",TEMPLATES_COURSES_LIST="block_myoverview/view-list",TEMPLATES_COURSES_SUMMARY="block_myoverview/view-summary",TEMPLATES_NOCOURSES="core_course/no-courses",GROUPINGS_GROUPING_ALLINCLUDINGHIDDEN="allincludinghidden",NUMCOURSES_PERPAGE=[12,24,48,96,0];let loadedPages=[],courseOffset=0,lastPage=0,lastLimit=0,namespace=null;const getFilterValues=root=>{const courseRegion=root.find(_selectors.default.courseView.region);return{display:courseRegion.attr("data-display"),grouping:courseRegion.attr("data-grouping"),sort:courseRegion.attr("data-sort"),displaycategories:courseRegion.attr("data-displaycategories"),customfieldname:courseRegion.attr("data-customfieldname"),customfieldvalue:courseRegion.attr("data-customfieldvalue")}},DEFAULT_PAGED_CONTENT_CONFIG={ignoreControlWhileLoading:!0,controlPlacementBottom:!0,persistentLimitKey:"block_myoverview_user_paging_preference"},getFavouriteIconContainer=(root,courseId)=>root.find(_selectors.default.FAVOURITE_ICON+'[data-course-id="'+courseId+'"]'),getPagedContentContainer=(root,index)=>root.find('[data-region="paged-content-page"][data-page="'+index+'"]'),getCourseId=root=>root.attr("data-course-id"),getAddFavouriteMenuItem=(root,courseId)=>root.find('[data-action="add-favourite"][data-course-id="'+courseId+'"]'),getRemoveFavouriteMenuItem=(root,courseId)=>root.find('[data-action="remove-favourite"][data-course-id="'+courseId+'"]'),addToFavourites=(root,courseId)=>{const removeAction=getRemoveFavouriteMenuItem(root,courseId),addAction=getAddFavouriteMenuItem(root,courseId);setCourseFavouriteState(courseId,!0).then((success=>{success?(PubSub.publish(CourseEvents.favourited,courseId),removeAction.removeClass("hidden"),addAction.addClass("hidden"),((root,courseId)=>{const iconContainer=getFavouriteIconContainer(root,courseId),isFavouriteIcon=iconContainer.find(_selectors.default.ICON_IS_FAVOURITE);isFavouriteIcon.removeClass("hidden"),Aria.unhide(isFavouriteIcon);const notFavourteIcon=iconContainer.find(_selectors.default.ICON_NOT_FAVOURITE);notFavourteIcon.addClass("hidden"),Aria.hide(notFavourteIcon)})(root,courseId)):Notification.alert("Starring course failed","Could not change favourite state")})).catch(Notification.exception)},removeFromFavourites=(root,courseId)=>{const removeAction=getRemoveFavouriteMenuItem(root,courseId),addAction=getAddFavouriteMenuItem(root,courseId);setCourseFavouriteState(courseId,!1).then((success=>{success?(PubSub.publish(CourseEvents.unfavorited,courseId),removeAction.addClass("hidden"),addAction.removeClass("hidden"),((root,courseId)=>{const iconContainer=getFavouriteIconContainer(root,courseId),isFavouriteIcon=iconContainer.find(_selectors.default.ICON_IS_FAVOURITE);isFavouriteIcon.addClass("hidden"),Aria.hide(isFavouriteIcon);const notFavourteIcon=iconContainer.find(_selectors.default.ICON_NOT_FAVOURITE);notFavourteIcon.removeClass("hidden"),Aria.unhide(notFavourteIcon)})(root,courseId)):Notification.alert("Starring course failed","Could not change favourite state")})).catch(Notification.exception)},getHideCourseMenuItem=(root,courseId)=>root.find('[data-action="hide-course"][data-course-id="'+courseId+'"]'),getShowCourseMenuItem=(root,courseId)=>root.find('[data-action="show-course"][data-course-id="'+courseId+'"]'),setCourseHiddenState=(courseId,status)=>(!1===status&&(status=null),Repository.updateUserPreferences({preferences:[{type:"block_myoverview_hidden_course_"+courseId,value:status}]})),hideElement=(root,id)=>{const pagingBar=root.find('[data-region="paging-bar"]'),jumpto=parseInt(pagingBar.attr("data-active-page-number"));let reducedCourse=loadedPages[jumpto].courses.reduce(((accumulator,current)=>(+id!=+current.id&&accumulator.push(current),accumulator)),[]);if(void 0!==loadedPages[jumpto+1]){const newElement=loadedPages[jumpto+1].courses.slice(0,1);loadedPages.forEach(((courseList,index)=>{if(index>jumpto){let popElement=[];void 0!==loadedPages[index+1]&&(popElement=loadedPages[index+1].courses.slice(0,1)),loadedPages[index].courses=[...loadedPages[index].courses.slice(1),...popElement]}})),reducedCourse=[...reducedCourse,...newElement]}if(lastPage===jumpto+1&&0===loadedPages[jumpto+1].courses.length){const pagedContentContainer=root.find('[data-region="paged-content-container"]');PagedContentFactory.resetLastPageNumber((0,_jquery.default)(pagedContentContainer).attr("id"),jumpto)}loadedPages[jumpto].courses=reducedCourse,courseOffset--;const pagedContentPage=getPagedContentContainer(root,jumpto);renderCourses(root,loadedPages[jumpto]).then(((html,js)=>Templates.replaceNodeContents(pagedContentPage,html,js))).catch(Notification.exception),loadedPages.forEach(((courseList,index)=>{if(index>jumpto){getPagedContentContainer(root,index).remove()}}))},setCourseFavouriteState=(courseId,status)=>Repository.setFavouriteCourses({courses:[{id:courseId,favourite:status}]}).then((result=>0===result.warnings.length&&(loadedPages.forEach((courseList=>{courseList.courses.forEach(((course,index)=>{course.id==courseId&&(courseList.courses[index].isfavourite=status)}))})),!0))).catch(Notification.exception),noCoursesRender=root=>{const nocoursesimg=root.find(_selectors.default.courseView.region).attr("data-nocoursesimg"),newcourseurl=root.find(_selectors.default.courseView.region).attr("data-newcourseurl");return Templates.render(TEMPLATES_NOCOURSES,{nocoursesimg:nocoursesimg,newcourseurl:newcourseurl})},renderCourses=(root,coursesData)=>{const filters=getFilterValues(root);let currentTemplate="";return currentTemplate="card"===filters.display?TEMPLATES_COURSES_CARDS:"list"===filters.display?TEMPLATES_COURSES_LIST:TEMPLATES_COURSES_SUMMARY,coursesData?(!1===Array.isArray(coursesData.courses)&&(coursesData.courses=Object.values(coursesData.courses)),coursesData.courses=coursesData.courses.map((course=>(course.showcoursecategory="on"===filters.displaycategories,course))),coursesData.courses.length?Templates.render(currentTemplate,{courses:coursesData.courses}):noCoursesRender(root)):noCoursesRender(root)},registerPagedEventHandlers=(root,namespace)=>{const event=namespace+PagedContentEvents.SET_ITEMS_PER_PAGE_LIMIT;PubSub.subscribe(event,(root=>limit=>root.find(_selectors.default.courseView.region).attr("data-paging",limit))(root))},itemsPerPageFunc=(pagingLimit,root)=>{let itemsPerPage=NUMCOURSES_PERPAGE.map((value=>{let active=!1;return value===pagingLimit&&(active=!0),{value:value,active:active}}));const totalCourseCount=parseInt(root.find(_selectors.default.courseView.region).attr("data-totalcoursecount"),10);return itemsPerPage.filter((pagingOption=>pagingOption.value4&&void 0!==arguments[4]?arguments[4]:null,courses=coursesData.courses?coursesData.courses:coursesData,nextPageStart=0,pageCourses=[];if(void 0!==loadedPages[currentPage]){pageCourses=loadedPages[currentPage].courses;const currentPageLength=pageCourses.length;currentPageLength0?courses.slice(0,pageData.limit):courses;loadedPages[currentPage]={courses:pageCourses};const remainingCourses=!1!==nextPageStart?courses.slice(nextPageStart,courses.length):[];remainingCourses.length&&(loadedPages[currentPage+1]={courses:remainingCourses}),loadedPages[currentPage].courses.length{courseOffset=0,loadedPages=[],lastPage=0,lastLimit=0},standardFunctionalityCurry=()=>(resetGlobals(),(filters,currentPage,pageData,actions,root,promises,limit)=>{const pagePromise=((filters,limit)=>Repository.getEnrolledCoursesByTimeline({offset:courseOffset,limit:limit,classification:filters.grouping,sort:filters.sort,customfieldname:filters.customfieldname,customfieldvalue:filters.customfieldvalue}))(filters,limit).then((coursesData=>(pageBuilder(coursesData,currentPage,pageData,actions),renderCourses(root,loadedPages[currentPage])))).catch(Notification.exception);promises.push(pagePromise)}),searchFunctionalityCurry=()=>(resetGlobals(),(filters,currentPage,pageData,actions,root,promises,limit,inputValue)=>{const searchingPromise=((filters,limit,searchValue)=>Repository.getEnrolledCoursesByTimeline({offset:courseOffset,limit:limit,classification:"search",sort:filters.sort,customfieldname:filters.customfieldname,customfieldvalue:filters.customfieldvalue,searchvalue:searchValue}))(filters,limit,inputValue).then((coursesData=>(pageBuilder(coursesData,currentPage,pageData,actions),renderCourses(root,loadedPages[currentPage])))).catch(Notification.exception);promises.push(searchingPromise)}),initializePagedContent=function(root,promiseFunction){let inputValue=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null;const pagingLimit=parseInt(root.find(_selectors.default.courseView.region).attr("data-paging"),10);let itemsPerPage=itemsPerPageFunc(pagingLimit,root);const filters=getFilterValues(root),config={...DEFAULT_PAGED_CONTENT_CONFIG};config.eventNamespace=namespace;PagedContentFactory.createWithLimit(itemsPerPage,((pagesData,actions)=>{let promises=[];return pagesData.forEach((pageData=>{const currentPage=pageData.pageNumber;let limit=pageData.limit>0?pageData.limit:0;if(+lastLimit!=+limit&&(loadedPages=[],courseOffset=0,lastPage=0),lastPage===currentPage)return actions.allItemsLoaded(lastPage),void promises.push(renderCourses(root,loadedPages[currentPage]));lastLimit=limit,void 0===loadedPages[currentPage+1]&&void 0===loadedPages[currentPage]&&(limit*=2),promiseFunction(filters,currentPage,pageData,actions,root,promises,limit,inputValue)})),promises}),config).then(((html,js)=>(registerPagedEventHandlers(root,namespace),Templates.replaceNodeContents(root.find(_selectors.default.courseView.region),html,js)))).catch(Notification.exception)},registerEventListeners=(root,page)=>{CustomEvents.define(root,[CustomEvents.events.activate]),root.on(CustomEvents.events.activate,_selectors.default.ACTION_ADD_FAVOURITE,((e,data)=>{const favourite=(0,_jquery.default)(e.target).closest(_selectors.default.ACTION_ADD_FAVOURITE),courseId=getCourseId(favourite);addToFavourites(root,courseId),data.originalEvent.preventDefault()})),root.on(CustomEvents.events.activate,_selectors.default.ACTION_REMOVE_FAVOURITE,((e,data)=>{const favourite=(0,_jquery.default)(e.target).closest(_selectors.default.ACTION_REMOVE_FAVOURITE),courseId=getCourseId(favourite);removeFromFavourites(root,courseId),data.originalEvent.preventDefault()})),root.on(CustomEvents.events.activate,_selectors.default.FAVOURITE_ICON,((e,data)=>{data.originalEvent.preventDefault()})),root.on(CustomEvents.events.activate,_selectors.default.ACTION_HIDE_COURSE,((e,data)=>{const target=(0,_jquery.default)(e.target).closest(_selectors.default.ACTION_HIDE_COURSE),courseId=getCourseId(target);((root,courseId)=>{const hideAction=getHideCourseMenuItem(root,courseId),showAction=getShowCourseMenuItem(root,courseId),filters=getFilterValues(root);setCourseHiddenState(courseId,!0),filters.grouping!==GROUPINGS_GROUPING_ALLINCLUDINGHIDDEN&&hideElement(root,courseId),hideAction.addClass("hidden"),showAction.removeClass("hidden")})(root,courseId),data.originalEvent.preventDefault()})),root.on(CustomEvents.events.activate,_selectors.default.ACTION_SHOW_COURSE,((e,data)=>{const target=(0,_jquery.default)(e.target).closest(_selectors.default.ACTION_SHOW_COURSE),courseId=getCourseId(target);((root,courseId)=>{const hideAction=getHideCourseMenuItem(root,courseId),showAction=getShowCourseMenuItem(root,courseId),filters=getFilterValues(root);setCourseHiddenState(courseId,null),filters.grouping!==GROUPINGS_GROUPING_ALLINCLUDINGHIDDEN&&hideElement(root,courseId),hideAction.removeClass("hidden"),showAction.addClass("hidden")})(root,courseId),data.originalEvent.preventDefault()}));const input=page.querySelector(_selectors.default.region.searchInput),clearIcon=page.querySelector(_selectors.default.region.clearIcon);clearIcon.addEventListener("click",(()=>{input.value="",input.focus(),clearSearch(clearIcon,root)})),input.addEventListener("input",(0,_utils.debounce)((()=>{""===input.value?clearSearch(clearIcon,root):(activeSearch(clearIcon),initializePagedContent(root,searchFunctionalityCurry(),input.value.trim()))}),1e3))},clearSearch=(clearIcon,root)=>{clearIcon.classList.add("d-none"),init(root)};_exports.clearSearch=clearSearch;const activeSearch=clearIcon=>{clearIcon.classList.remove("d-none")},init=root=>{if(root=(0,_jquery.default)(root),loadedPages=[],lastPage=0,courseOffset=0,!root.attr("data-init")){const page=document.querySelector(_selectors.default.region.selectBlock);registerEventListeners(root,page),namespace="block_myoverview_"+root.attr("id")+"_"+Math.random(),root.attr("data-init",!0)}initializePagedContent(root,standardFunctionalityCurry())};_exports.init=init;_exports.reset=root=>{loadedPages.length>0?loadedPages.forEach(((courseList,index)=>{let pagedContentPage=getPagedContentContainer(root,index);renderCourses(root,courseList).then(((html,js)=>Templates.replaceNodeContents(pagedContentPage,html,js))).catch(Notification.exception)})):init(root)}}));
//# sourceMappingURL=view.min.js.map
\ No newline at end of file
diff --git a/blocks/myoverview/amd/build/view.min.js.map b/blocks/myoverview/amd/build/view.min.js.map
index 03a2f786126..c74458233f7 100644
--- a/blocks/myoverview/amd/build/view.min.js.map
+++ b/blocks/myoverview/amd/build/view.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"view.min.js","sources":["../src/view.js"],"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 * Manage the courses view for the overview block.\n *\n * @copyright 2018 Bas Brands \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport $ from 'jquery';\nimport * as Repository from 'block_myoverview/repository';\nimport * as PagedContentFactory from 'core/paged_content_factory';\nimport * as PubSub from 'core/pubsub';\nimport * as CustomEvents from 'core/custom_interaction_events';\nimport * as Notification from 'core/notification';\nimport * as Templates from 'core/templates';\nimport * as CourseEvents from 'core_course/events';\nimport SELECTORS from 'block_myoverview/selectors';\nimport * as PagedContentEvents from 'core/paged_content_events';\nimport * as Aria from 'core/aria';\nimport {debounce} from 'core/utils';\n\nconst TEMPLATES = {\n COURSES_CARDS: 'block_myoverview/view-cards',\n COURSES_LIST: 'block_myoverview/view-list',\n COURSES_SUMMARY: 'block_myoverview/view-summary',\n NOCOURSES: 'core_course/no-courses'\n};\n\nconst GROUPINGS = {\n GROUPING_ALLINCLUDINGHIDDEN: 'allincludinghidden',\n GROUPING_ALL: 'all',\n GROUPING_INPROGRESS: 'inprogress',\n GROUPING_FUTURE: 'future',\n GROUPING_PAST: 'past',\n GROUPING_FAVOURITES: 'favourites',\n GROUPING_HIDDEN: 'hidden'\n};\n\nconst NUMCOURSES_PERPAGE = [12, 24, 48, 96, 0];\n\nlet loadedPages = [];\n\nlet courseOffset = 0;\n\nlet lastPage = 0;\n\nlet lastLimit = 0;\n\nlet namespace = null;\n\n/**\n * Get filter values from DOM.\n *\n * @param {object} root The root element for the courses view.\n * @return {filters} Set filters.\n */\nconst getFilterValues = root => {\n const courseRegion = root.find(SELECTORS.courseView.region);\n return {\n display: courseRegion.attr('data-display'),\n grouping: courseRegion.attr('data-grouping'),\n sort: courseRegion.attr('data-sort'),\n displaycategories: courseRegion.attr('data-displaycategories'),\n customfieldname: courseRegion.attr('data-customfieldname'),\n customfieldvalue: courseRegion.attr('data-customfieldvalue'),\n };\n};\n\n// We want the paged content controls below the paged content area.\n// and the controls should be ignored while data is loading.\nconst DEFAULT_PAGED_CONTENT_CONFIG = {\n ignoreControlWhileLoading: true,\n controlPlacementBottom: true,\n persistentLimitKey: 'block_myoverview_user_paging_preference'\n};\n\n/**\n * Get enrolled courses from backend.\n *\n * @param {object} filters The filters for this view.\n * @param {int} limit The number of courses to show.\n * @return {promise} Resolved with an array of courses.\n */\nconst getMyCourses = (filters, limit) => {\n return Repository.getEnrolledCoursesByTimeline({\n offset: courseOffset,\n limit: limit,\n classification: filters.grouping,\n sort: filters.sort,\n customfieldname: filters.customfieldname,\n customfieldvalue: filters.customfieldvalue\n });\n};\n\n/**\n * Search for enrolled courses from backend.\n *\n * @param {object} filters The filters for this view.\n * @param {int} limit The number of courses to show.\n * @param {string} searchValue What does the user want to search within their courses.\n * @return {promise} Resolved with an array of courses.\n */\nconst getSearchMyCourses = (filters, limit, searchValue) => {\n return Repository.getEnrolledCoursesByTimeline({\n offset: courseOffset,\n limit: limit,\n classification: 'search',\n sort: filters.sort,\n customfieldname: filters.customfieldname,\n customfieldvalue: filters.customfieldvalue,\n searchvalue: searchValue\n });\n};\n\n/**\n * Get the container element for the favourite icon.\n *\n * @param {Object} root The course overview container\n * @param {Number} courseId Course id number\n * @return {Object} The favourite icon container\n */\nconst getFavouriteIconContainer = (root, courseId) => {\n return root.find(SELECTORS.FAVOURITE_ICON + '[data-course-id=\"' + courseId + '\"]');\n};\n\n/**\n * Get the paged content container element.\n *\n * @param {Object} root The course overview container\n * @param {Number} index Rendered page index.\n * @return {Object} The rendered paged container.\n */\nconst getPagedContentContainer = (root, index) => {\n return root.find('[data-region=\"paged-content-page\"][data-page=\"' + index + '\"]');\n};\n\n/**\n * Get the course id from a favourite element.\n *\n * @param {Object} root The favourite icon container element.\n * @return {Number} Course id.\n */\nconst getCourseId = root => {\n return root.attr('data-course-id');\n};\n\n/**\n * Hide the favourite icon.\n *\n * @param {Object} root The favourite icon container element.\n * @param {Number} courseId Course id number.\n */\nconst hideFavouriteIcon = (root, courseId) => {\n const iconContainer = getFavouriteIconContainer(root, courseId);\n\n const isFavouriteIcon = iconContainer.find(SELECTORS.ICON_IS_FAVOURITE);\n isFavouriteIcon.addClass('hidden');\n Aria.hide(isFavouriteIcon);\n\n const notFavourteIcon = iconContainer.find(SELECTORS.ICON_NOT_FAVOURITE);\n notFavourteIcon.removeClass('hidden');\n Aria.unhide(notFavourteIcon);\n};\n\n/**\n * Show the favourite icon.\n *\n * @param {Object} root The course overview container.\n * @param {Number} courseId Course id number.\n */\nconst showFavouriteIcon = (root, courseId) => {\n const iconContainer = getFavouriteIconContainer(root, courseId);\n\n const isFavouriteIcon = iconContainer.find(SELECTORS.ICON_IS_FAVOURITE);\n isFavouriteIcon.removeClass('hidden');\n Aria.unhide(isFavouriteIcon);\n\n const notFavourteIcon = iconContainer.find(SELECTORS.ICON_NOT_FAVOURITE);\n notFavourteIcon.addClass('hidden');\n Aria.hide(notFavourteIcon);\n};\n\n/**\n * Get the action menu item\n *\n * @param {Object} root The course overview container\n * @param {Number} courseId Course id.\n * @return {Object} The add to favourite menu item.\n */\nconst getAddFavouriteMenuItem = (root, courseId) => {\n return root.find('[data-action=\"add-favourite\"][data-course-id=\"' + courseId + '\"]');\n};\n\n/**\n * Get the action menu item\n *\n * @param {Object} root The course overview container\n * @param {Number} courseId Course id.\n * @return {Object} The remove from favourites menu item.\n */\nconst getRemoveFavouriteMenuItem = (root, courseId) => {\n return root.find('[data-action=\"remove-favourite\"][data-course-id=\"' + courseId + '\"]');\n};\n\n/**\n * Add course to favourites\n *\n * @param {Object} root The course overview container\n * @param {Number} courseId Course id number\n */\nconst addToFavourites = (root, courseId) => {\n const removeAction = getRemoveFavouriteMenuItem(root, courseId);\n const addAction = getAddFavouriteMenuItem(root, courseId);\n\n setCourseFavouriteState(courseId, true).then(success => {\n if (success) {\n PubSub.publish(CourseEvents.favourited, courseId);\n removeAction.removeClass('hidden');\n addAction.addClass('hidden');\n showFavouriteIcon(root, courseId);\n } else {\n Notification.alert('Starring course failed', 'Could not change favourite state');\n }\n return;\n }).catch(Notification.exception);\n};\n\n/**\n * Remove course from favourites\n *\n * @param {Object} root The course overview container\n * @param {Number} courseId Course id number\n */\nconst removeFromFavourites = (root, courseId) => {\n const removeAction = getRemoveFavouriteMenuItem(root, courseId);\n const addAction = getAddFavouriteMenuItem(root, courseId);\n\n setCourseFavouriteState(courseId, false).then(success => {\n if (success) {\n PubSub.publish(CourseEvents.unfavorited, courseId);\n removeAction.addClass('hidden');\n addAction.removeClass('hidden');\n hideFavouriteIcon(root, courseId);\n } else {\n Notification.alert('Starring course failed', 'Could not change favourite state');\n }\n return;\n }).catch(Notification.exception);\n};\n\n/**\n * Get the action menu item\n *\n * @param {Object} root The course overview container\n * @param {Number} courseId Course id.\n * @return {Object} The hide course menu item.\n */\nconst getHideCourseMenuItem = (root, courseId) => {\n return root.find('[data-action=\"hide-course\"][data-course-id=\"' + courseId + '\"]');\n};\n\n/**\n * Get the action menu item\n *\n * @param {Object} root The course overview container\n * @param {Number} courseId Course id.\n * @return {Object} The show course menu item.\n */\nconst getShowCourseMenuItem = (root, courseId) => {\n return root.find('[data-action=\"show-course\"][data-course-id=\"' + courseId + '\"]');\n};\n\n/**\n * Hide course\n *\n * @param {Object} root The course overview container\n * @param {Number} courseId Course id number\n */\nconst hideCourse = (root, courseId) => {\n const hideAction = getHideCourseMenuItem(root, courseId);\n const showAction = getShowCourseMenuItem(root, courseId);\n const filters = getFilterValues(root);\n\n setCourseHiddenState(courseId, true);\n\n // Remove the course from this view as it is now hidden and thus not covered by this view anymore.\n // Do only if we are not in \"All (including archived)\" view mode where really all courses are shown.\n if (filters.grouping !== GROUPINGS.GROUPING_ALLINCLUDINGHIDDEN) {\n hideElement(root, courseId);\n }\n\n hideAction.addClass('hidden');\n showAction.removeClass('hidden');\n};\n\n/**\n * Show course\n *\n * @param {Object} root The course overview container\n * @param {Number} courseId Course id number\n */\nconst showCourse = (root, courseId) => {\n const hideAction = getHideCourseMenuItem(root, courseId);\n const showAction = getShowCourseMenuItem(root, courseId);\n const filters = getFilterValues(root);\n\n setCourseHiddenState(courseId, null);\n\n // Remove the course from this view as it is now shown again and thus not covered by this view anymore.\n // Do only if we are not in \"All (including archived)\" view mode where really all courses are shown.\n if (filters.grouping !== GROUPINGS.GROUPING_ALLINCLUDINGHIDDEN) {\n hideElement(root, courseId);\n }\n\n hideAction.removeClass('hidden');\n showAction.addClass('hidden');\n};\n\n/**\n * Set the courses hidden status and push to repository\n *\n * @param {Number} courseId Course id to favourite.\n * @param {Boolean} status new hidden status.\n * @return {Promise} Repository promise.\n */\nconst setCourseHiddenState = (courseId, status) => {\n\n // If the given status is not hidden, the preference has to be deleted with a null value.\n if (status === false) {\n status = null;\n }\n return Repository.updateUserPreferences({\n preferences: [\n {\n type: 'block_myoverview_hidden_course_' + courseId,\n value: status\n }\n ]\n });\n};\n\n/**\n * Reset the loadedPages dataset to take into account the hidden element\n *\n * @param {Object} root The course overview container\n * @param {Number} id The course id number\n */\nconst hideElement = (root, id) => {\n const pagingBar = root.find('[data-region=\"paging-bar\"]');\n const jumpto = parseInt(pagingBar.attr('data-active-page-number'));\n\n // Get a reduced dataset for the current page.\n const courseList = loadedPages[jumpto];\n let reducedCourse = courseList.courses.reduce((accumulator, current) => {\n if (+id !== +current.id) {\n accumulator.push(current);\n }\n return accumulator;\n }, []);\n\n // Get the next page's data if loaded and pop the first element from it.\n if (typeof (loadedPages[jumpto + 1]) !== 'undefined') {\n const newElement = loadedPages[jumpto + 1].courses.slice(0, 1);\n\n // Adjust the dataset for the reset of the pages that are loaded.\n loadedPages.forEach((courseList, index) => {\n if (index > jumpto) {\n let popElement = [];\n if (typeof (loadedPages[index + 1]) !== 'undefined') {\n popElement = loadedPages[index + 1].courses.slice(0, 1);\n }\n loadedPages[index].courses = [...loadedPages[index].courses.slice(1), ...popElement];\n }\n });\n\n reducedCourse = [...reducedCourse, ...newElement];\n }\n\n // Check if the next page is the last page and if it still has data associated to it.\n if (lastPage === jumpto + 1 && loadedPages[jumpto + 1].courses.length === 0) {\n const pagedContentContainer = root.find('[data-region=\"paged-content-container\"]');\n PagedContentFactory.resetLastPageNumber($(pagedContentContainer).attr('id'), jumpto);\n }\n\n loadedPages[jumpto].courses = reducedCourse;\n\n // Reduce the course offset.\n courseOffset--;\n\n // Render the paged content for the current.\n const pagedContentPage = getPagedContentContainer(root, jumpto);\n renderCourses(root, loadedPages[jumpto]).then((html, js) => {\n return Templates.replaceNodeContents(pagedContentPage, html, js);\n }).catch(Notification.exception);\n\n // Delete subsequent pages in order to trigger the callback.\n loadedPages.forEach((courseList, index) => {\n if (index > jumpto) {\n const page = getPagedContentContainer(root, index);\n page.remove();\n }\n });\n};\n\n/**\n * Set the courses favourite status and push to repository\n *\n * @param {Number} courseId Course id to favourite.\n * @param {boolean} status new favourite status.\n * @return {Promise} Repository promise.\n */\nconst setCourseFavouriteState = (courseId, status) => {\n\n return Repository.setFavouriteCourses({\n courses: [\n {\n 'id': courseId,\n 'favourite': status\n }\n ]\n }).then(result => {\n if (result.warnings.length === 0) {\n loadedPages.forEach(courseList => {\n courseList.courses.forEach((course, index) => {\n if (course.id == courseId) {\n courseList.courses[index].isfavourite = status;\n }\n });\n });\n return true;\n } else {\n return false;\n }\n }).catch(Notification.exception);\n};\n\n/**\n * Given there are no courses to render provide the rendered template.\n *\n * @param {object} root The root element for the courses view.\n * @return {promise} jQuery promise resolved after rendering is complete.\n */\nconst noCoursesRender = root => {\n const nocoursesimg = root.find(SELECTORS.courseView.region).attr('data-nocoursesimg');\n const newcourseurl = root.find(SELECTORS.courseView.region).attr('data-newcourseurl');\n return Templates.render(TEMPLATES.NOCOURSES, {\n nocoursesimg: nocoursesimg,\n newcourseurl: newcourseurl\n });\n};\n\n/**\n * Render the dashboard courses.\n *\n * @param {object} root The root element for the courses view.\n * @param {array} coursesData containing array of returned courses.\n * @return {promise} jQuery promise resolved after rendering is complete.\n */\nconst renderCourses = (root, coursesData) => {\n\n const filters = getFilterValues(root);\n\n let currentTemplate = '';\n if (filters.display === 'card') {\n currentTemplate = TEMPLATES.COURSES_CARDS;\n } else if (filters.display === 'list') {\n currentTemplate = TEMPLATES.COURSES_LIST;\n } else {\n currentTemplate = TEMPLATES.COURSES_SUMMARY;\n }\n\n if (!coursesData) {\n return noCoursesRender(root);\n } else {\n // Sometimes we get weird objects coming after a failed search, cast to ensure typing functions.\n if (Array.isArray(coursesData.courses) === false) {\n coursesData.courses = Object.values(coursesData.courses);\n }\n // Whether the course category should be displayed in the course item.\n coursesData.courses = coursesData.courses.map(course => {\n course.showcoursecategory = filters.displaycategories === 'on';\n return course;\n });\n if (coursesData.courses.length) {\n return Templates.render(currentTemplate, {\n courses: coursesData.courses,\n });\n } else {\n return noCoursesRender(root);\n }\n }\n};\n\n/**\n * Return the callback to be passed to the subscribe event\n *\n * @param {object} root The root element for the courses view\n * @return {function} Partially applied function that'll execute when passed a limit\n */\nconst setLimit = root => {\n // @param {Number} limit The paged limit that is passed through the event.\n return limit => root.find(SELECTORS.courseView.region).attr('data-paging', limit);\n};\n\n/**\n * Intialise the paged list and cards views on page load.\n * Returns an array of paged contents that we would like to handle here\n *\n * @param {object} root The root element for the courses view\n * @param {string} namespace The namespace for all the events attached\n */\nconst registerPagedEventHandlers = (root, namespace) => {\n const event = namespace + PagedContentEvents.SET_ITEMS_PER_PAGE_LIMIT;\n PubSub.subscribe(event, setLimit(root));\n};\n\n/**\n * Figure out how many items are going to be allowed to be rendered in the block.\n *\n * @param {Number} pagingLimit How many courses to display\n * @param {Object} root The course overview container\n * @return {Number[]} How many courses will be rendered\n */\nconst itemsPerPageFunc = (pagingLimit, root) => {\n let itemsPerPage = NUMCOURSES_PERPAGE.map(value => {\n let active = false;\n if (value === pagingLimit) {\n active = true;\n }\n\n return {\n value: value,\n active: active\n };\n });\n\n // Filter out all pagination options which are too large for the amount of courses user is enrolled in.\n const totalCourseCount = parseInt(root.find(SELECTORS.courseView.region).attr('data-totalcoursecount'), 10);\n return itemsPerPage.filter(pagingOption => {\n return pagingOption.value < totalCourseCount || pagingOption.value === 0;\n });\n};\n\n/**\n * Mutates and controls the loadedPages array and handles the bootstrapping.\n *\n * @param {Array|Object} coursesData Array of all of the courses to start building the page from\n * @param {Number} currentPage What page are we currently on?\n * @param {Object} pageData Any current page information\n * @param {Object} actions Paged content helper\n * @param {null|boolean} activeSearch Are we currently actively searching and building up search results?\n */\nconst pageBuilder = (coursesData, currentPage, pageData, actions, activeSearch = null) => {\n // If the courseData comes in an object then get the value otherwise it is a pure array.\n let courses = coursesData.courses ? coursesData.courses : coursesData;\n let nextPageStart = 0;\n let pageCourses = [];\n\n // If current page's data is loaded make sure we max it to page limit.\n if (typeof (loadedPages[currentPage]) !== 'undefined') {\n pageCourses = loadedPages[currentPage].courses;\n const currentPageLength = pageCourses.length;\n if (currentPageLength < pageData.limit) {\n nextPageStart = pageData.limit - currentPageLength;\n pageCourses = {...loadedPages[currentPage].courses, ...courses.slice(0, nextPageStart)};\n }\n } else {\n // When the page limit is zero, there is only one page of courses, no start for next page.\n nextPageStart = pageData.limit || false;\n pageCourses = (pageData.limit > 0) ? courses.slice(0, pageData.limit) : courses;\n }\n\n // Finished setting up the current page.\n loadedPages[currentPage] = {\n courses: pageCourses\n };\n\n // Set up the next page (if there is more than one page).\n const remainingCourses = nextPageStart !== false ? courses.slice(nextPageStart, courses.length) : [];\n if (remainingCourses.length) {\n loadedPages[currentPage + 1] = {\n courses: remainingCourses\n };\n }\n\n // Set the last page to either the current or next page.\n if (loadedPages[currentPage].courses.length < pageData.limit || !remainingCourses.length) {\n lastPage = currentPage;\n if (activeSearch === null) {\n actions.allItemsLoaded(currentPage);\n }\n } else if (typeof (loadedPages[currentPage + 1]) !== 'undefined'\n && loadedPages[currentPage + 1].courses.length < pageData.limit) {\n lastPage = currentPage + 1;\n }\n\n courseOffset = coursesData.nextoffset;\n};\n\n/**\n * In cases when switching between regular rendering and search rendering we need to reset some variables.\n */\nconst resetGlobals = () => {\n courseOffset = 0;\n loadedPages = [];\n lastPage = 0;\n lastLimit = 0;\n};\n\n/**\n * The default functionality of fetching paginated courses without special handling.\n *\n * @return {function(Object, Object, Object, Object, Object, Promise, Number): void}\n */\nconst standardFunctionalityCurry = () => {\n resetGlobals();\n return (filters, currentPage, pageData, actions, root, promises, limit) => {\n const pagePromise = getMyCourses(\n filters,\n limit\n ).then(coursesData => {\n pageBuilder(coursesData, currentPage, pageData, actions);\n return renderCourses(root, loadedPages[currentPage]);\n }).catch(Notification.exception);\n\n promises.push(pagePromise);\n };\n};\n\n/**\n * Initialize the searching functionality so we can call it when required.\n *\n * @return {function(Object, Number, Object, Object, Object, Promise, Number, String): void}\n */\nconst searchFunctionalityCurry = () => {\n resetGlobals();\n return (filters, currentPage, pageData, actions, root, promises, limit, inputValue) => {\n const searchingPromise = getSearchMyCourses(\n filters,\n limit,\n inputValue\n ).then(coursesData => {\n pageBuilder(coursesData, currentPage, pageData, actions);\n return renderCourses(root, loadedPages[currentPage]);\n }).catch(Notification.exception);\n\n promises.push(searchingPromise);\n };\n};\n\n/**\n * Initialise the courses list and cards views on page load.\n *\n * @param {object} root The root element for the courses view.\n * @param {function} promiseFunction How do we fetch the courses and what do we do with them?\n * @param {null | string} inputValue What to search for\n */\nconst initializePagedContent = (root, promiseFunction, inputValue = null) => {\n const pagingLimit = parseInt(root.find(SELECTORS.courseView.region).attr('data-paging'), 10);\n let itemsPerPage = itemsPerPageFunc(pagingLimit, root);\n\n const filters = getFilterValues(root);\n const config = {...{}, ...DEFAULT_PAGED_CONTENT_CONFIG};\n config.eventNamespace = namespace;\n\n const pagedContentPromise = PagedContentFactory.createWithLimit(\n itemsPerPage,\n (pagesData, actions) => {\n let promises = [];\n pagesData.forEach(pageData => {\n const currentPage = pageData.pageNumber;\n let limit = (pageData.limit > 0) ? pageData.limit : 0;\n\n // Reset local variables if limits have changed.\n if (+lastLimit !== +limit) {\n loadedPages = [];\n courseOffset = 0;\n lastPage = 0;\n }\n\n if (lastPage === currentPage) {\n // If we are on the last page and have it's data then load it from cache.\n actions.allItemsLoaded(lastPage);\n promises.push(renderCourses(root, loadedPages[currentPage]));\n return;\n }\n\n lastLimit = limit;\n\n // Get 2 pages worth of data as we will need it for the hidden functionality.\n if (typeof (loadedPages[currentPage + 1]) === 'undefined') {\n if (typeof (loadedPages[currentPage]) === 'undefined') {\n limit *= 2;\n }\n }\n\n // Call the curried function that'll handle the course promise and any manipulation of it.\n promiseFunction(filters, currentPage, pageData, actions, root, promises, limit, inputValue);\n });\n return promises;\n },\n config\n );\n\n pagedContentPromise.then((html, js) => {\n registerPagedEventHandlers(root, namespace);\n return Templates.replaceNodeContents(root.find(SELECTORS.courseView.region), html, js);\n }).catch(Notification.exception);\n};\n\n/**\n * Listen to, and handle events for the myoverview block.\n *\n * @param {Object} root The myoverview block container element.\n * @param {HTMLElement} page The whole HTMLElement for our block.\n */\nconst registerEventListeners = (root, page) => {\n\n CustomEvents.define(root, [\n CustomEvents.events.activate\n ]);\n\n root.on(CustomEvents.events.activate, SELECTORS.ACTION_ADD_FAVOURITE, (e, data) => {\n const favourite = $(e.target).closest(SELECTORS.ACTION_ADD_FAVOURITE);\n const courseId = getCourseId(favourite);\n addToFavourites(root, courseId);\n data.originalEvent.preventDefault();\n });\n\n root.on(CustomEvents.events.activate, SELECTORS.ACTION_REMOVE_FAVOURITE, (e, data) => {\n const favourite = $(e.target).closest(SELECTORS.ACTION_REMOVE_FAVOURITE);\n const courseId = getCourseId(favourite);\n removeFromFavourites(root, courseId);\n data.originalEvent.preventDefault();\n });\n\n root.on(CustomEvents.events.activate, SELECTORS.FAVOURITE_ICON, (e, data) => {\n data.originalEvent.preventDefault();\n });\n\n root.on(CustomEvents.events.activate, SELECTORS.ACTION_HIDE_COURSE, (e, data) => {\n const target = $(e.target).closest(SELECTORS.ACTION_HIDE_COURSE);\n const courseId = getCourseId(target);\n hideCourse(root, courseId);\n data.originalEvent.preventDefault();\n });\n\n root.on(CustomEvents.events.activate, SELECTORS.ACTION_SHOW_COURSE, (e, data) => {\n const target = $(e.target).closest(SELECTORS.ACTION_SHOW_COURSE);\n const courseId = getCourseId(target);\n showCourse(root, courseId);\n data.originalEvent.preventDefault();\n });\n\n // Searching functionality event handlers.\n const input = page.querySelector(SELECTORS.region.searchInput);\n const clearIcon = page.querySelector(SELECTORS.region.clearIcon);\n\n clearIcon.addEventListener('click', () => {\n input.value = '';\n input.focus();\n clearSearch(clearIcon, root);\n });\n\n input.addEventListener('input', debounce(() => {\n if (input.value === '') {\n clearSearch(clearIcon, root);\n } else {\n activeSearch(clearIcon);\n initializePagedContent(root, searchFunctionalityCurry(), input.value.trim());\n }\n }, 1000));\n};\n\n/**\n * Reset the search icon and trigger the init for the block.\n *\n * @param {HTMLElement} clearIcon Our closing icon to manipulate.\n * @param {Object} root The myoverview block container element.\n */\nexport const clearSearch = (clearIcon, root) => {\n clearIcon.classList.add('d-none');\n init(root);\n};\n\n/**\n * Change the searching icon to its' active state.\n *\n * @param {HTMLElement} clearIcon Our closing icon to manipulate.\n */\nconst activeSearch = (clearIcon) => {\n clearIcon.classList.remove('d-none');\n};\n\n/**\n * Intialise the courses list and cards views on page load.\n *\n * @param {object} root The root element for the courses view.\n */\nexport const init = root => {\n root = $(root);\n loadedPages = [];\n lastPage = 0;\n courseOffset = 0;\n\n if (!root.attr('data-init')) {\n const page = document.querySelector(SELECTORS.region.selectBlock);\n registerEventListeners(root, page);\n namespace = \"block_myoverview_\" + root.attr('id') + \"_\" + Math.random();\n root.attr('data-init', true);\n }\n\n initializePagedContent(root, standardFunctionalityCurry());\n};\n\n/**\n * Reset the courses views to their original\n * state on first page load.courseOffset\n *\n * This is called when configuration has changed for the event lists\n * to cause them to reload their data.\n *\n * @param {Object} root The root element for the timeline view.\n */\nexport const reset = root => {\n if (loadedPages.length > 0) {\n loadedPages.forEach((courseList, index) => {\n let pagedContentPage = getPagedContentContainer(root, index);\n renderCourses(root, courseList).then((html, js) => {\n return Templates.replaceNodeContents(pagedContentPage, html, js);\n }).catch(Notification.exception);\n });\n } else {\n init(root);\n }\n};\n"],"names":["TEMPLATES","GROUPINGS","NUMCOURSES_PERPAGE","loadedPages","courseOffset","lastPage","lastLimit","namespace","getFilterValues","root","courseRegion","find","SELECTORS","courseView","region","display","attr","grouping","sort","displaycategories","customfieldname","customfieldvalue","DEFAULT_PAGED_CONTENT_CONFIG","ignoreControlWhileLoading","controlPlacementBottom","persistentLimitKey","getFavouriteIconContainer","courseId","FAVOURITE_ICON","getPagedContentContainer","index","getCourseId","getAddFavouriteMenuItem","getRemoveFavouriteMenuItem","addToFavourites","removeAction","addAction","setCourseFavouriteState","then","success","PubSub","publish","CourseEvents","favourited","removeClass","addClass","iconContainer","isFavouriteIcon","ICON_IS_FAVOURITE","Aria","unhide","notFavourteIcon","ICON_NOT_FAVOURITE","hide","showFavouriteIcon","Notification","alert","catch","exception","removeFromFavourites","unfavorited","hideFavouriteIcon","getHideCourseMenuItem","getShowCourseMenuItem","setCourseHiddenState","status","Repository","updateUserPreferences","preferences","type","value","hideElement","id","pagingBar","jumpto","parseInt","reducedCourse","courses","reduce","accumulator","current","push","newElement","slice","forEach","courseList","popElement","length","pagedContentContainer","PagedContentFactory","resetLastPageNumber","pagedContentPage","renderCourses","html","js","Templates","replaceNodeContents","remove","setFavouriteCourses","result","warnings","course","isfavourite","noCoursesRender","nocoursesimg","newcourseurl","render","coursesData","filters","currentTemplate","Array","isArray","Object","values","map","showcoursecategory","registerPagedEventHandlers","event","PagedContentEvents","SET_ITEMS_PER_PAGE_LIMIT","subscribe","limit","setLimit","itemsPerPageFunc","pagingLimit","itemsPerPage","active","totalCourseCount","filter","pagingOption","pageBuilder","currentPage","pageData","actions","activeSearch","nextPageStart","pageCourses","currentPageLength","remainingCourses","allItemsLoaded","nextoffset","resetGlobals","standardFunctionalityCurry","promises","pagePromise","getEnrolledCoursesByTimeline","offset","classification","getMyCourses","searchFunctionalityCurry","inputValue","searchingPromise","searchValue","searchvalue","getSearchMyCourses","initializePagedContent","promiseFunction","config","eventNamespace","pagedContentPromise","createWithLimit","pagesData","pageNumber","registerEventListeners","page","CustomEvents","define","events","activate","on","ACTION_ADD_FAVOURITE","e","data","favourite","target","closest","originalEvent","preventDefault","ACTION_REMOVE_FAVOURITE","ACTION_HIDE_COURSE","hideAction","showAction","hideCourse","ACTION_SHOW_COURSE","showCourse","input","querySelector","searchInput","clearIcon","addEventListener","focus","clearSearch","trim","classList","add","init","document","selectBlock","Math","random"],"mappings":"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"}
\ No newline at end of file
+{"version":3,"file":"view.min.js","sources":["../src/view.js"],"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 * Manage the courses view for the overview block.\n *\n * @copyright 2018 Bas Brands \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport $ from 'jquery';\nimport * as Repository from 'block_myoverview/repository';\nimport * as PagedContentFactory from 'core/paged_content_factory';\nimport * as PubSub from 'core/pubsub';\nimport * as CustomEvents from 'core/custom_interaction_events';\nimport * as Notification from 'core/notification';\nimport * as Templates from 'core/templates';\nimport * as CourseEvents from 'core_course/events';\nimport SELECTORS from 'block_myoverview/selectors';\nimport * as PagedContentEvents from 'core/paged_content_events';\nimport * as Aria from 'core/aria';\nimport {debounce} from 'core/utils';\n\nconst TEMPLATES = {\n COURSES_CARDS: 'block_myoverview/view-cards',\n COURSES_LIST: 'block_myoverview/view-list',\n COURSES_SUMMARY: 'block_myoverview/view-summary',\n NOCOURSES: 'core_course/no-courses'\n};\n\nconst GROUPINGS = {\n GROUPING_ALLINCLUDINGHIDDEN: 'allincludinghidden',\n GROUPING_ALL: 'all',\n GROUPING_INPROGRESS: 'inprogress',\n GROUPING_FUTURE: 'future',\n GROUPING_PAST: 'past',\n GROUPING_FAVOURITES: 'favourites',\n GROUPING_HIDDEN: 'hidden'\n};\n\nconst NUMCOURSES_PERPAGE = [12, 24, 48, 96, 0];\n\nlet loadedPages = [];\n\nlet courseOffset = 0;\n\nlet lastPage = 0;\n\nlet lastLimit = 0;\n\nlet namespace = null;\n\n/**\n * Get filter values from DOM.\n *\n * @param {object} root The root element for the courses view.\n * @return {filters} Set filters.\n */\nconst getFilterValues = root => {\n const courseRegion = root.find(SELECTORS.courseView.region);\n return {\n display: courseRegion.attr('data-display'),\n grouping: courseRegion.attr('data-grouping'),\n sort: courseRegion.attr('data-sort'),\n displaycategories: courseRegion.attr('data-displaycategories'),\n customfieldname: courseRegion.attr('data-customfieldname'),\n customfieldvalue: courseRegion.attr('data-customfieldvalue'),\n };\n};\n\n// We want the paged content controls below the paged content area.\n// and the controls should be ignored while data is loading.\nconst DEFAULT_PAGED_CONTENT_CONFIG = {\n ignoreControlWhileLoading: true,\n controlPlacementBottom: true,\n persistentLimitKey: 'block_myoverview_user_paging_preference'\n};\n\n/**\n * Get enrolled courses from backend.\n *\n * @param {object} filters The filters for this view.\n * @param {int} limit The number of courses to show.\n * @return {promise} Resolved with an array of courses.\n */\nconst getMyCourses = (filters, limit) => {\n return Repository.getEnrolledCoursesByTimeline({\n offset: courseOffset,\n limit: limit,\n classification: filters.grouping,\n sort: filters.sort,\n customfieldname: filters.customfieldname,\n customfieldvalue: filters.customfieldvalue\n });\n};\n\n/**\n * Search for enrolled courses from backend.\n *\n * @param {object} filters The filters for this view.\n * @param {int} limit The number of courses to show.\n * @param {string} searchValue What does the user want to search within their courses.\n * @return {promise} Resolved with an array of courses.\n */\nconst getSearchMyCourses = (filters, limit, searchValue) => {\n return Repository.getEnrolledCoursesByTimeline({\n offset: courseOffset,\n limit: limit,\n classification: 'search',\n sort: filters.sort,\n customfieldname: filters.customfieldname,\n customfieldvalue: filters.customfieldvalue,\n searchvalue: searchValue\n });\n};\n\n/**\n * Get the container element for the favourite icon.\n *\n * @param {Object} root The course overview container\n * @param {Number} courseId Course id number\n * @return {Object} The favourite icon container\n */\nconst getFavouriteIconContainer = (root, courseId) => {\n return root.find(SELECTORS.FAVOURITE_ICON + '[data-course-id=\"' + courseId + '\"]');\n};\n\n/**\n * Get the paged content container element.\n *\n * @param {Object} root The course overview container\n * @param {Number} index Rendered page index.\n * @return {Object} The rendered paged container.\n */\nconst getPagedContentContainer = (root, index) => {\n return root.find('[data-region=\"paged-content-page\"][data-page=\"' + index + '\"]');\n};\n\n/**\n * Get the course id from a favourite element.\n *\n * @param {Object} root The favourite icon container element.\n * @return {Number} Course id.\n */\nconst getCourseId = root => {\n return root.attr('data-course-id');\n};\n\n/**\n * Hide the favourite icon.\n *\n * @param {Object} root The favourite icon container element.\n * @param {Number} courseId Course id number.\n */\nconst hideFavouriteIcon = (root, courseId) => {\n const iconContainer = getFavouriteIconContainer(root, courseId);\n\n const isFavouriteIcon = iconContainer.find(SELECTORS.ICON_IS_FAVOURITE);\n isFavouriteIcon.addClass('hidden');\n Aria.hide(isFavouriteIcon);\n\n const notFavourteIcon = iconContainer.find(SELECTORS.ICON_NOT_FAVOURITE);\n notFavourteIcon.removeClass('hidden');\n Aria.unhide(notFavourteIcon);\n};\n\n/**\n * Show the favourite icon.\n *\n * @param {Object} root The course overview container.\n * @param {Number} courseId Course id number.\n */\nconst showFavouriteIcon = (root, courseId) => {\n const iconContainer = getFavouriteIconContainer(root, courseId);\n\n const isFavouriteIcon = iconContainer.find(SELECTORS.ICON_IS_FAVOURITE);\n isFavouriteIcon.removeClass('hidden');\n Aria.unhide(isFavouriteIcon);\n\n const notFavourteIcon = iconContainer.find(SELECTORS.ICON_NOT_FAVOURITE);\n notFavourteIcon.addClass('hidden');\n Aria.hide(notFavourteIcon);\n};\n\n/**\n * Get the action menu item\n *\n * @param {Object} root The course overview container\n * @param {Number} courseId Course id.\n * @return {Object} The add to favourite menu item.\n */\nconst getAddFavouriteMenuItem = (root, courseId) => {\n return root.find('[data-action=\"add-favourite\"][data-course-id=\"' + courseId + '\"]');\n};\n\n/**\n * Get the action menu item\n *\n * @param {Object} root The course overview container\n * @param {Number} courseId Course id.\n * @return {Object} The remove from favourites menu item.\n */\nconst getRemoveFavouriteMenuItem = (root, courseId) => {\n return root.find('[data-action=\"remove-favourite\"][data-course-id=\"' + courseId + '\"]');\n};\n\n/**\n * Add course to favourites\n *\n * @param {Object} root The course overview container\n * @param {Number} courseId Course id number\n */\nconst addToFavourites = (root, courseId) => {\n const removeAction = getRemoveFavouriteMenuItem(root, courseId);\n const addAction = getAddFavouriteMenuItem(root, courseId);\n\n setCourseFavouriteState(courseId, true).then(success => {\n if (success) {\n PubSub.publish(CourseEvents.favourited, courseId);\n removeAction.removeClass('hidden');\n addAction.addClass('hidden');\n showFavouriteIcon(root, courseId);\n } else {\n Notification.alert('Starring course failed', 'Could not change favourite state');\n }\n return;\n }).catch(Notification.exception);\n};\n\n/**\n * Remove course from favourites\n *\n * @param {Object} root The course overview container\n * @param {Number} courseId Course id number\n */\nconst removeFromFavourites = (root, courseId) => {\n const removeAction = getRemoveFavouriteMenuItem(root, courseId);\n const addAction = getAddFavouriteMenuItem(root, courseId);\n\n setCourseFavouriteState(courseId, false).then(success => {\n if (success) {\n PubSub.publish(CourseEvents.unfavorited, courseId);\n removeAction.addClass('hidden');\n addAction.removeClass('hidden');\n hideFavouriteIcon(root, courseId);\n } else {\n Notification.alert('Starring course failed', 'Could not change favourite state');\n }\n return;\n }).catch(Notification.exception);\n};\n\n/**\n * Get the action menu item\n *\n * @param {Object} root The course overview container\n * @param {Number} courseId Course id.\n * @return {Object} The hide course menu item.\n */\nconst getHideCourseMenuItem = (root, courseId) => {\n return root.find('[data-action=\"hide-course\"][data-course-id=\"' + courseId + '\"]');\n};\n\n/**\n * Get the action menu item\n *\n * @param {Object} root The course overview container\n * @param {Number} courseId Course id.\n * @return {Object} The show course menu item.\n */\nconst getShowCourseMenuItem = (root, courseId) => {\n return root.find('[data-action=\"show-course\"][data-course-id=\"' + courseId + '\"]');\n};\n\n/**\n * Hide course\n *\n * @param {Object} root The course overview container\n * @param {Number} courseId Course id number\n */\nconst hideCourse = (root, courseId) => {\n const hideAction = getHideCourseMenuItem(root, courseId);\n const showAction = getShowCourseMenuItem(root, courseId);\n const filters = getFilterValues(root);\n\n setCourseHiddenState(courseId, true);\n\n // Remove the course from this view as it is now hidden and thus not covered by this view anymore.\n // Do only if we are not in \"All (including archived)\" view mode where really all courses are shown.\n if (filters.grouping !== GROUPINGS.GROUPING_ALLINCLUDINGHIDDEN) {\n hideElement(root, courseId);\n }\n\n hideAction.addClass('hidden');\n showAction.removeClass('hidden');\n};\n\n/**\n * Show course\n *\n * @param {Object} root The course overview container\n * @param {Number} courseId Course id number\n */\nconst showCourse = (root, courseId) => {\n const hideAction = getHideCourseMenuItem(root, courseId);\n const showAction = getShowCourseMenuItem(root, courseId);\n const filters = getFilterValues(root);\n\n setCourseHiddenState(courseId, null);\n\n // Remove the course from this view as it is now shown again and thus not covered by this view anymore.\n // Do only if we are not in \"All (including archived)\" view mode where really all courses are shown.\n if (filters.grouping !== GROUPINGS.GROUPING_ALLINCLUDINGHIDDEN) {\n hideElement(root, courseId);\n }\n\n hideAction.removeClass('hidden');\n showAction.addClass('hidden');\n};\n\n/**\n * Set the courses hidden status and push to repository\n *\n * @param {Number} courseId Course id to favourite.\n * @param {Boolean} status new hidden status.\n * @return {Promise} Repository promise.\n */\nconst setCourseHiddenState = (courseId, status) => {\n\n // If the given status is not hidden, the preference has to be deleted with a null value.\n if (status === false) {\n status = null;\n }\n return Repository.updateUserPreferences({\n preferences: [\n {\n type: 'block_myoverview_hidden_course_' + courseId,\n value: status\n }\n ]\n });\n};\n\n/**\n * Reset the loadedPages dataset to take into account the hidden element\n *\n * @param {Object} root The course overview container\n * @param {Number} id The course id number\n */\nconst hideElement = (root, id) => {\n const pagingBar = root.find('[data-region=\"paging-bar\"]');\n const jumpto = parseInt(pagingBar.attr('data-active-page-number'));\n\n // Get a reduced dataset for the current page.\n const courseList = loadedPages[jumpto];\n let reducedCourse = courseList.courses.reduce((accumulator, current) => {\n if (+id !== +current.id) {\n accumulator.push(current);\n }\n return accumulator;\n }, []);\n\n // Get the next page's data if loaded and pop the first element from it.\n if (typeof (loadedPages[jumpto + 1]) !== 'undefined') {\n const newElement = loadedPages[jumpto + 1].courses.slice(0, 1);\n\n // Adjust the dataset for the reset of the pages that are loaded.\n loadedPages.forEach((courseList, index) => {\n if (index > jumpto) {\n let popElement = [];\n if (typeof (loadedPages[index + 1]) !== 'undefined') {\n popElement = loadedPages[index + 1].courses.slice(0, 1);\n }\n loadedPages[index].courses = [...loadedPages[index].courses.slice(1), ...popElement];\n }\n });\n\n reducedCourse = [...reducedCourse, ...newElement];\n }\n\n // Check if the next page is the last page and if it still has data associated to it.\n if (lastPage === jumpto + 1 && loadedPages[jumpto + 1].courses.length === 0) {\n const pagedContentContainer = root.find('[data-region=\"paged-content-container\"]');\n PagedContentFactory.resetLastPageNumber($(pagedContentContainer).attr('id'), jumpto);\n }\n\n loadedPages[jumpto].courses = reducedCourse;\n\n // Reduce the course offset.\n courseOffset--;\n\n // Render the paged content for the current.\n const pagedContentPage = getPagedContentContainer(root, jumpto);\n renderCourses(root, loadedPages[jumpto]).then((html, js) => {\n return Templates.replaceNodeContents(pagedContentPage, html, js);\n }).catch(Notification.exception);\n\n // Delete subsequent pages in order to trigger the callback.\n loadedPages.forEach((courseList, index) => {\n if (index > jumpto) {\n const page = getPagedContentContainer(root, index);\n page.remove();\n }\n });\n};\n\n/**\n * Set the courses favourite status and push to repository\n *\n * @param {Number} courseId Course id to favourite.\n * @param {boolean} status new favourite status.\n * @return {Promise} Repository promise.\n */\nconst setCourseFavouriteState = (courseId, status) => {\n\n return Repository.setFavouriteCourses({\n courses: [\n {\n 'id': courseId,\n 'favourite': status\n }\n ]\n }).then(result => {\n if (result.warnings.length === 0) {\n loadedPages.forEach(courseList => {\n courseList.courses.forEach((course, index) => {\n if (course.id == courseId) {\n courseList.courses[index].isfavourite = status;\n }\n });\n });\n return true;\n } else {\n return false;\n }\n }).catch(Notification.exception);\n};\n\n/**\n * Given there are no courses to render provide the rendered template.\n *\n * @param {object} root The root element for the courses view.\n * @return {promise} jQuery promise resolved after rendering is complete.\n */\nconst noCoursesRender = root => {\n const nocoursesimg = root.find(SELECTORS.courseView.region).attr('data-nocoursesimg');\n const newcourseurl = root.find(SELECTORS.courseView.region).attr('data-newcourseurl');\n return Templates.render(TEMPLATES.NOCOURSES, {\n nocoursesimg: nocoursesimg,\n newcourseurl: newcourseurl\n });\n};\n\n/**\n * Render the dashboard courses.\n *\n * @param {object} root The root element for the courses view.\n * @param {array} coursesData containing array of returned courses.\n * @return {promise} jQuery promise resolved after rendering is complete.\n */\nconst renderCourses = (root, coursesData) => {\n\n const filters = getFilterValues(root);\n\n let currentTemplate = '';\n if (filters.display === 'card') {\n currentTemplate = TEMPLATES.COURSES_CARDS;\n } else if (filters.display === 'list') {\n currentTemplate = TEMPLATES.COURSES_LIST;\n } else {\n currentTemplate = TEMPLATES.COURSES_SUMMARY;\n }\n\n if (!coursesData) {\n return noCoursesRender(root);\n } else {\n // Sometimes we get weird objects coming after a failed search, cast to ensure typing functions.\n if (Array.isArray(coursesData.courses) === false) {\n coursesData.courses = Object.values(coursesData.courses);\n }\n // Whether the course category should be displayed in the course item.\n coursesData.courses = coursesData.courses.map(course => {\n course.showcoursecategory = filters.displaycategories === 'on';\n return course;\n });\n if (coursesData.courses.length) {\n return Templates.render(currentTemplate, {\n courses: coursesData.courses,\n });\n } else {\n return noCoursesRender(root);\n }\n }\n};\n\n/**\n * Return the callback to be passed to the subscribe event\n *\n * @param {object} root The root element for the courses view\n * @return {function} Partially applied function that'll execute when passed a limit\n */\nconst setLimit = root => {\n // @param {Number} limit The paged limit that is passed through the event.\n return limit => root.find(SELECTORS.courseView.region).attr('data-paging', limit);\n};\n\n/**\n * Intialise the paged list and cards views on page load.\n * Returns an array of paged contents that we would like to handle here\n *\n * @param {object} root The root element for the courses view\n * @param {string} namespace The namespace for all the events attached\n */\nconst registerPagedEventHandlers = (root, namespace) => {\n const event = namespace + PagedContentEvents.SET_ITEMS_PER_PAGE_LIMIT;\n PubSub.subscribe(event, setLimit(root));\n};\n\n/**\n * Figure out how many items are going to be allowed to be rendered in the block.\n *\n * @param {Number} pagingLimit How many courses to display\n * @param {Object} root The course overview container\n * @return {Number[]} How many courses will be rendered\n */\nconst itemsPerPageFunc = (pagingLimit, root) => {\n let itemsPerPage = NUMCOURSES_PERPAGE.map(value => {\n let active = false;\n if (value === pagingLimit) {\n active = true;\n }\n\n return {\n value: value,\n active: active\n };\n });\n\n // Filter out all pagination options which are too large for the amount of courses user is enrolled in.\n const totalCourseCount = parseInt(root.find(SELECTORS.courseView.region).attr('data-totalcoursecount'), 10);\n return itemsPerPage.filter(pagingOption => {\n return pagingOption.value < totalCourseCount || pagingOption.value === 0;\n });\n};\n\n/**\n * Mutates and controls the loadedPages array and handles the bootstrapping.\n *\n * @param {Array|Object} coursesData Array of all of the courses to start building the page from\n * @param {Number} currentPage What page are we currently on?\n * @param {Object} pageData Any current page information\n * @param {Object} actions Paged content helper\n * @param {null|boolean} activeSearch Are we currently actively searching and building up search results?\n */\nconst pageBuilder = (coursesData, currentPage, pageData, actions, activeSearch = null) => {\n // If the courseData comes in an object then get the value otherwise it is a pure array.\n let courses = coursesData.courses ? coursesData.courses : coursesData;\n let nextPageStart = 0;\n let pageCourses = [];\n\n // If current page's data is loaded make sure we max it to page limit.\n if (typeof (loadedPages[currentPage]) !== 'undefined') {\n pageCourses = loadedPages[currentPage].courses;\n const currentPageLength = pageCourses.length;\n if (currentPageLength < pageData.limit) {\n nextPageStart = pageData.limit - currentPageLength;\n pageCourses = {...loadedPages[currentPage].courses, ...courses.slice(0, nextPageStart)};\n }\n } else {\n // When the page limit is zero, there is only one page of courses, no start for next page.\n nextPageStart = pageData.limit || false;\n pageCourses = (pageData.limit > 0) ? courses.slice(0, pageData.limit) : courses;\n }\n\n // Finished setting up the current page.\n loadedPages[currentPage] = {\n courses: pageCourses\n };\n\n // Set up the next page (if there is more than one page).\n const remainingCourses = nextPageStart !== false ? courses.slice(nextPageStart, courses.length) : [];\n if (remainingCourses.length) {\n loadedPages[currentPage + 1] = {\n courses: remainingCourses\n };\n }\n\n // Set the last page to either the current or next page.\n if (loadedPages[currentPage].courses.length < pageData.limit || !remainingCourses.length) {\n lastPage = currentPage;\n if (activeSearch === null) {\n actions.allItemsLoaded(currentPage);\n }\n } else if (typeof (loadedPages[currentPage + 1]) !== 'undefined'\n && loadedPages[currentPage + 1].courses.length < pageData.limit) {\n lastPage = currentPage + 1;\n }\n\n courseOffset = coursesData.nextoffset;\n};\n\n/**\n * In cases when switching between regular rendering and search rendering we need to reset some variables.\n */\nconst resetGlobals = () => {\n courseOffset = 0;\n loadedPages = [];\n lastPage = 0;\n lastLimit = 0;\n};\n\n/**\n * The default functionality of fetching paginated courses without special handling.\n *\n * @return {function(Object, Object, Object, Object, Object, Promise, Number): void}\n */\nconst standardFunctionalityCurry = () => {\n resetGlobals();\n return (filters, currentPage, pageData, actions, root, promises, limit) => {\n const pagePromise = getMyCourses(\n filters,\n limit\n ).then(coursesData => {\n pageBuilder(coursesData, currentPage, pageData, actions);\n return renderCourses(root, loadedPages[currentPage]);\n }).catch(Notification.exception);\n\n promises.push(pagePromise);\n };\n};\n\n/**\n * Initialize the searching functionality so we can call it when required.\n *\n * @return {function(Object, Number, Object, Object, Object, Promise, Number, String): void}\n */\nconst searchFunctionalityCurry = () => {\n resetGlobals();\n return (filters, currentPage, pageData, actions, root, promises, limit, inputValue) => {\n const searchingPromise = getSearchMyCourses(\n filters,\n limit,\n inputValue\n ).then(coursesData => {\n pageBuilder(coursesData, currentPage, pageData, actions);\n return renderCourses(root, loadedPages[currentPage]);\n }).catch(Notification.exception);\n\n promises.push(searchingPromise);\n };\n};\n\n/**\n * Initialise the courses list and cards views on page load.\n *\n * @param {object} root The root element for the courses view.\n * @param {function} promiseFunction How do we fetch the courses and what do we do with them?\n * @param {null | string} inputValue What to search for\n */\nconst initializePagedContent = (root, promiseFunction, inputValue = null) => {\n const pagingLimit = parseInt(root.find(SELECTORS.courseView.region).attr('data-paging'), 10);\n let itemsPerPage = itemsPerPageFunc(pagingLimit, root);\n\n const filters = getFilterValues(root);\n const config = {...{}, ...DEFAULT_PAGED_CONTENT_CONFIG};\n config.eventNamespace = namespace;\n\n const pagedContentPromise = PagedContentFactory.createWithLimit(\n itemsPerPage,\n (pagesData, actions) => {\n let promises = [];\n pagesData.forEach(pageData => {\n const currentPage = pageData.pageNumber;\n let limit = (pageData.limit > 0) ? pageData.limit : 0;\n\n // Reset local variables if limits have changed.\n if (+lastLimit !== +limit) {\n loadedPages = [];\n courseOffset = 0;\n lastPage = 0;\n }\n\n if (lastPage === currentPage) {\n // If we are on the last page and have it's data then load it from cache.\n actions.allItemsLoaded(lastPage);\n promises.push(renderCourses(root, loadedPages[currentPage]));\n return;\n }\n\n lastLimit = limit;\n\n // Get 2 pages worth of data as we will need it for the hidden functionality.\n if (typeof (loadedPages[currentPage + 1]) === 'undefined') {\n if (typeof (loadedPages[currentPage]) === 'undefined') {\n limit *= 2;\n }\n }\n\n // Call the curried function that'll handle the course promise and any manipulation of it.\n promiseFunction(filters, currentPage, pageData, actions, root, promises, limit, inputValue);\n });\n return promises;\n },\n config\n );\n\n pagedContentPromise.then((html, js) => {\n registerPagedEventHandlers(root, namespace);\n return Templates.replaceNodeContents(root.find(SELECTORS.courseView.region), html, js);\n }).catch(Notification.exception);\n};\n\n/**\n * Listen to, and handle events for the myoverview block.\n *\n * @param {Object} root The myoverview block container element.\n * @param {HTMLElement} page The whole HTMLElement for our block.\n */\nconst registerEventListeners = (root, page) => {\n\n CustomEvents.define(root, [\n CustomEvents.events.activate\n ]);\n\n root.on(CustomEvents.events.activate, SELECTORS.ACTION_ADD_FAVOURITE, (e, data) => {\n const favourite = $(e.target).closest(SELECTORS.ACTION_ADD_FAVOURITE);\n const courseId = getCourseId(favourite);\n addToFavourites(root, courseId);\n data.originalEvent.preventDefault();\n });\n\n root.on(CustomEvents.events.activate, SELECTORS.ACTION_REMOVE_FAVOURITE, (e, data) => {\n const favourite = $(e.target).closest(SELECTORS.ACTION_REMOVE_FAVOURITE);\n const courseId = getCourseId(favourite);\n removeFromFavourites(root, courseId);\n data.originalEvent.preventDefault();\n });\n\n root.on(CustomEvents.events.activate, SELECTORS.FAVOURITE_ICON, (e, data) => {\n data.originalEvent.preventDefault();\n });\n\n root.on(CustomEvents.events.activate, SELECTORS.ACTION_HIDE_COURSE, (e, data) => {\n const target = $(e.target).closest(SELECTORS.ACTION_HIDE_COURSE);\n const courseId = getCourseId(target);\n hideCourse(root, courseId);\n data.originalEvent.preventDefault();\n });\n\n root.on(CustomEvents.events.activate, SELECTORS.ACTION_SHOW_COURSE, (e, data) => {\n const target = $(e.target).closest(SELECTORS.ACTION_SHOW_COURSE);\n const courseId = getCourseId(target);\n showCourse(root, courseId);\n data.originalEvent.preventDefault();\n });\n\n // Searching functionality event handlers.\n const input = page.querySelector(SELECTORS.region.searchInput);\n const clearIcon = page.querySelector(SELECTORS.region.clearIcon);\n\n clearIcon.addEventListener('click', () => {\n input.value = '';\n input.focus();\n clearSearch(clearIcon, root);\n });\n\n input.addEventListener('input', debounce(() => {\n if (input.value === '') {\n clearSearch(clearIcon, root);\n } else {\n activeSearch(clearIcon);\n initializePagedContent(root, searchFunctionalityCurry(), input.value.trim());\n }\n }, 1000));\n};\n\n/**\n * Reset the search icon and trigger the init for the block.\n *\n * @param {HTMLElement} clearIcon Our closing icon to manipulate.\n * @param {Object} root The myoverview block container element.\n */\nexport const clearSearch = (clearIcon, root) => {\n clearIcon.classList.add('d-none');\n init(root);\n};\n\n/**\n * Change the searching icon to its' active state.\n *\n * @param {HTMLElement} clearIcon Our closing icon to manipulate.\n */\nconst activeSearch = (clearIcon) => {\n clearIcon.classList.remove('d-none');\n};\n\n/**\n * Intialise the courses list and cards views on page load.\n *\n * @param {object} root The root element for the courses view.\n */\nexport const init = root => {\n root = $(root);\n loadedPages = [];\n lastPage = 0;\n courseOffset = 0;\n\n if (!root.attr('data-init')) {\n const page = document.querySelector(SELECTORS.region.selectBlock);\n registerEventListeners(root, page);\n namespace = \"block_myoverview_\" + root.attr('id') + \"_\" + Math.random();\n root.attr('data-init', true);\n }\n\n initializePagedContent(root, standardFunctionalityCurry());\n};\n\n/**\n * Reset the courses views to their original\n * state on first page load.courseOffset\n *\n * This is called when configuration has changed for the event lists\n * to cause them to reload their data.\n *\n * @param {Object} root The root element for the timeline view.\n */\nexport const reset = root => {\n if (loadedPages.length > 0) {\n loadedPages.forEach((courseList, index) => {\n let pagedContentPage = getPagedContentContainer(root, index);\n renderCourses(root, courseList).then((html, js) => {\n return Templates.replaceNodeContents(pagedContentPage, html, js);\n }).catch(Notification.exception);\n });\n } else {\n init(root);\n }\n};\n"],"names":["_getRequireWildcardCache","nodeInterop","WeakMap","cacheBabelInterop","cacheNodeInterop","_interopRequireWildcard","obj","__esModule","default","cache","has","get","newObj","hasPropertyDescriptor","Object","defineProperty","getOwnPropertyDescriptor","key","prototype","hasOwnProperty","call","desc","set","_interopRequireDefault","_jquery","Repository","PagedContentFactory","PubSub","CustomEvents","Notification","Templates","CourseEvents","_selectors","PagedContentEvents","Aria","TEMPLATES","GROUPINGS","NUMCOURSES_PERPAGE","loadedPages","courseOffset","lastPage","lastLimit","namespace","getFilterValues","root","courseRegion","find","SELECTORS","courseView","region","display","attr","grouping","sort","displaycategories","customfieldname","customfieldvalue","DEFAULT_PAGED_CONTENT_CONFIG","ignoreControlWhileLoading","controlPlacementBottom","persistentLimitKey","getFavouriteIconContainer","courseId","FAVOURITE_ICON","getPagedContentContainer","index","getCourseId","getAddFavouriteMenuItem","getRemoveFavouriteMenuItem","addToFavourites","removeAction","addAction","setCourseFavouriteState","then","success","publish","favourited","removeClass","addClass","showFavouriteIcon","iconContainer","isFavouriteIcon","ICON_IS_FAVOURITE","unhide","notFavourteIcon","ICON_NOT_FAVOURITE","hide","alert","catch","exception","removeFromFavourites","unfavorited","hideFavouriteIcon","getHideCourseMenuItem","getShowCourseMenuItem","setCourseHiddenState","status","updateUserPreferences","preferences","type","value","hideElement","id","pagingBar","jumpto","parseInt","reducedCourse","courses","reduce","accumulator","current","push","newElement","slice","forEach","courseList","popElement","length","pagedContentContainer","resetLastPageNumber","$","pagedContentPage","renderCourses","html","js","replaceNodeContents","remove","setFavouriteCourses","favourite","result","warnings","course","isfavourite","noCoursesRender","nocoursesimg","newcourseurl","render","coursesData","filters","currentTemplate","Array","isArray","values","map","showcoursecategory","registerPagedEventHandlers","event","SET_ITEMS_PER_PAGE_LIMIT","subscribe","limit","setLimit","itemsPerPageFunc","pagingLimit","itemsPerPage","active","totalCourseCount","filter","pagingOption","pageBuilder","currentPage","pageData","actions","activeSearch","arguments","undefined","nextPageStart","pageCourses","currentPageLength","remainingCourses","allItemsLoaded","nextoffset","resetGlobals","standardFunctionalityCurry","promises","pagePromise","getMyCourses","getEnrolledCoursesByTimeline","offset","classification","searchFunctionalityCurry","inputValue","searchingPromise","getSearchMyCourses","searchValue","searchvalue","initializePagedContent","promiseFunction","config","eventNamespace","createWithLimit","pagesData","pageNumber","registerEventListeners","page","define","events","activate","on","ACTION_ADD_FAVOURITE","e","data","target","closest","originalEvent","preventDefault","ACTION_REMOVE_FAVOURITE","ACTION_HIDE_COURSE","hideCourse","hideAction","showAction","ACTION_SHOW_COURSE","showCourse","input","querySelector","searchInput","clearIcon","addEventListener","focus","clearSearch","debounce","trim","classList","add","init","_exports","document","selectBlock","Math","random","reset"],"mappings":"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"}
\ No newline at end of file
diff --git a/blocks/myoverview/amd/build/view_nav.min.js.map b/blocks/myoverview/amd/build/view_nav.min.js.map
index c2c822eac0f..58ade7d4484 100644
--- a/blocks/myoverview/amd/build/view_nav.min.js.map
+++ b/blocks/myoverview/amd/build/view_nav.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"view_nav.min.js","sources":["../src/view_nav.js"],"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 * Manage the timeline view navigation for the overview block.\n *\n * @copyright 2018 Bas Brands \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport $ from 'jquery';\nimport * as CustomEvents from 'core/custom_interaction_events';\nimport * as Repository from 'block_myoverview/repository';\nimport * as View from 'block_myoverview/view';\nimport SELECTORS from 'block_myoverview/selectors';\n\n/**\n * Update the user preference for the block.\n *\n * @param {String} filter The type of filter: display/sort/grouping.\n * @param {String} value The current preferred value.\n */\nconst updatePreferences = (filter, value) => {\n let type = null;\n if (filter === 'display') {\n type = 'block_myoverview_user_view_preference';\n } else if (filter === 'sort') {\n type = 'block_myoverview_user_sort_preference';\n } else if (filter === 'customfieldvalue') {\n type = 'block_myoverview_user_grouping_customfieldvalue_preference';\n } else {\n type = 'block_myoverview_user_grouping_preference';\n }\n\n Repository.updateUserPreferences({\n preferences: [\n {\n type: type,\n value: value\n }\n ]\n });\n};\n\n/**\n * Event listener for the Display filter (cards, list).\n *\n * @param {object} root The root element for the overview block\n */\nconst registerSelector = root => {\n\n const Selector = root.find(SELECTORS.FILTERS);\n\n CustomEvents.define(Selector, [CustomEvents.events.activate]);\n Selector.on(\n CustomEvents.events.activate,\n SELECTORS.FILTER_OPTION,\n (e, data) => {\n const option = $(e.target);\n\n if (option.hasClass('active')) {\n // If it's already active then we don't need to do anything.\n return;\n }\n\n const filter = option.attr('data-filter');\n const pref = option.attr('data-pref');\n const customfieldvalue = option.attr('data-customfieldvalue');\n\n root.find(SELECTORS.courseView.region).attr('data-' + filter, option.attr('data-value'));\n updatePreferences(filter, pref);\n\n if (customfieldvalue) {\n root.find(SELECTORS.courseView.region).attr('data-customfieldvalue', customfieldvalue);\n updatePreferences('customfieldvalue', customfieldvalue);\n }\n\n // Reset the views.\n\n // Check if the user is currently in a searching state, if so we'll reset it.\n const page = document.querySelector(SELECTORS.region.selectBlock);\n const input = page.querySelector(SELECTORS.region.searchInput);\n if (input.value !== '') {\n const clearIcon = page.querySelector(SELECTORS.region.clearIcon);\n input.value = '';\n // Triggers the init so wont need to call it again.\n View.clearSearch(clearIcon, root);\n } else {\n View.init(root);\n }\n\n data.originalEvent.preventDefault();\n }\n );\n\n Selector.on(\n CustomEvents.events.activate,\n SELECTORS.DISPLAY_OPTION,\n (e, data) => {\n const option = $(e.target);\n\n if (option.hasClass('active')) {\n return;\n }\n\n const filter = option.attr('data-display-option');\n const pref = option.attr('data-pref');\n\n root.find(SELECTORS.courseView.region).attr('data-display', option.attr('data-value'));\n updatePreferences(filter, pref);\n View.reset(root);\n data.originalEvent.preventDefault();\n }\n );\n};\n\n/**\n * Initialise the timeline view navigation by adding event listeners to\n * the navigation elements.\n *\n * @param {object} root The root element for the myoverview block\n */\nexport const init = root => {\n root = $(root);\n registerSelector(root);\n};\n"],"names":["updatePreferences","filter","value","type","Repository","updateUserPreferences","preferences","root","Selector","find","SELECTORS","FILTERS","CustomEvents","define","events","activate","on","FILTER_OPTION","e","data","option","target","hasClass","attr","pref","customfieldvalue","courseView","region","page","document","querySelector","selectBlock","input","searchInput","clearIcon","View","clearSearch","init","originalEvent","preventDefault","DISPLAY_OPTION","reset","registerSelector"],"mappings":";;;;;;mTAkCMA,kBAAoB,CAACC,OAAQC,aAC3BC,KAAO,KAEPA,KADW,YAAXF,OACO,wCACW,SAAXA,OACA,wCACW,qBAAXA,OACA,6DAEA,4CAGXG,WAAWC,sBAAsB,CAC7BC,YAAa,CACT,CACIH,KAAMA,KACND,MAAOA,yBAoFHK,OAzEKA,CAAAA,aAEfC,SAAWD,KAAKE,KAAKC,mBAAUC,SAErCC,aAAaC,OAAOL,SAAU,CAACI,aAAaE,OAAOC,WACnDP,SAASQ,GACLJ,aAAaE,OAAOC,SACpBL,mBAAUO,eACV,CAACC,EAAGC,cACMC,QAAS,mBAAEF,EAAEG,WAEfD,OAAOE,SAAS,uBAKdrB,OAASmB,OAAOG,KAAK,eACrBC,KAAOJ,OAAOG,KAAK,aACnBE,iBAAmBL,OAAOG,KAAK,yBAErChB,KAAKE,KAAKC,mBAAUgB,WAAWC,QAAQJ,KAAK,QAAUtB,OAAQmB,OAAOG,KAAK,eAC1EvB,kBAAkBC,OAAQuB,MAEtBC,mBACAlB,KAAKE,KAAKC,mBAAUgB,WAAWC,QAAQJ,KAAK,wBAAyBE,kBACrEzB,kBAAkB,mBAAoByB,yBAMpCG,KAAOC,SAASC,cAAcpB,mBAAUiB,OAAOI,aAC/CC,MAAQJ,KAAKE,cAAcpB,mBAAUiB,OAAOM,gBAC9B,KAAhBD,MAAM9B,MAAc,OACdgC,UAAYN,KAAKE,cAAcpB,mBAAUiB,OAAOO,WACtDF,MAAM9B,MAAQ,GAEdiC,KAAKC,YAAYF,UAAW3B,WAE5B4B,KAAKE,KAAK9B,MAGdY,KAAKmB,cAAcC,oBAI3B/B,SAASQ,GACLJ,aAAaE,OAAOC,SACpBL,mBAAU8B,gBACV,CAACtB,EAAGC,cACMC,QAAS,mBAAEF,EAAEG,WAEfD,OAAOE,SAAS,uBAIdrB,OAASmB,OAAOG,KAAK,uBACrBC,KAAOJ,OAAOG,KAAK,aAEzBhB,KAAKE,KAAKC,mBAAUgB,WAAWC,QAAQJ,KAAK,eAAgBH,OAAOG,KAAK,eACxEvB,kBAAkBC,OAAQuB,MAC1BW,KAAKM,MAAMlC,MACXY,KAAKmB,cAAcC,qBAa3BG,CADAnC,MAAO,mBAAEA"}
\ No newline at end of file
+{"version":3,"file":"view_nav.min.js","sources":["../src/view_nav.js"],"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 * Manage the timeline view navigation for the overview block.\n *\n * @copyright 2018 Bas Brands \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport $ from 'jquery';\nimport * as CustomEvents from 'core/custom_interaction_events';\nimport * as Repository from 'block_myoverview/repository';\nimport * as View from 'block_myoverview/view';\nimport SELECTORS from 'block_myoverview/selectors';\n\n/**\n * Update the user preference for the block.\n *\n * @param {String} filter The type of filter: display/sort/grouping.\n * @param {String} value The current preferred value.\n */\nconst updatePreferences = (filter, value) => {\n let type = null;\n if (filter === 'display') {\n type = 'block_myoverview_user_view_preference';\n } else if (filter === 'sort') {\n type = 'block_myoverview_user_sort_preference';\n } else if (filter === 'customfieldvalue') {\n type = 'block_myoverview_user_grouping_customfieldvalue_preference';\n } else {\n type = 'block_myoverview_user_grouping_preference';\n }\n\n Repository.updateUserPreferences({\n preferences: [\n {\n type: type,\n value: value\n }\n ]\n });\n};\n\n/**\n * Event listener for the Display filter (cards, list).\n *\n * @param {object} root The root element for the overview block\n */\nconst registerSelector = root => {\n\n const Selector = root.find(SELECTORS.FILTERS);\n\n CustomEvents.define(Selector, [CustomEvents.events.activate]);\n Selector.on(\n CustomEvents.events.activate,\n SELECTORS.FILTER_OPTION,\n (e, data) => {\n const option = $(e.target);\n\n if (option.hasClass('active')) {\n // If it's already active then we don't need to do anything.\n return;\n }\n\n const filter = option.attr('data-filter');\n const pref = option.attr('data-pref');\n const customfieldvalue = option.attr('data-customfieldvalue');\n\n root.find(SELECTORS.courseView.region).attr('data-' + filter, option.attr('data-value'));\n updatePreferences(filter, pref);\n\n if (customfieldvalue) {\n root.find(SELECTORS.courseView.region).attr('data-customfieldvalue', customfieldvalue);\n updatePreferences('customfieldvalue', customfieldvalue);\n }\n\n // Reset the views.\n\n // Check if the user is currently in a searching state, if so we'll reset it.\n const page = document.querySelector(SELECTORS.region.selectBlock);\n const input = page.querySelector(SELECTORS.region.searchInput);\n if (input.value !== '') {\n const clearIcon = page.querySelector(SELECTORS.region.clearIcon);\n input.value = '';\n // Triggers the init so wont need to call it again.\n View.clearSearch(clearIcon, root);\n } else {\n View.init(root);\n }\n\n data.originalEvent.preventDefault();\n }\n );\n\n Selector.on(\n CustomEvents.events.activate,\n SELECTORS.DISPLAY_OPTION,\n (e, data) => {\n const option = $(e.target);\n\n if (option.hasClass('active')) {\n return;\n }\n\n const filter = option.attr('data-display-option');\n const pref = option.attr('data-pref');\n\n root.find(SELECTORS.courseView.region).attr('data-display', option.attr('data-value'));\n updatePreferences(filter, pref);\n View.reset(root);\n data.originalEvent.preventDefault();\n }\n );\n};\n\n/**\n * Initialise the timeline view navigation by adding event listeners to\n * the navigation elements.\n *\n * @param {object} root The root element for the myoverview block\n */\nexport const init = root => {\n root = $(root);\n registerSelector(root);\n};\n"],"names":["_getRequireWildcardCache","nodeInterop","WeakMap","cacheBabelInterop","cacheNodeInterop","_interopRequireWildcard","obj","__esModule","default","cache","has","get","newObj","hasPropertyDescriptor","Object","defineProperty","getOwnPropertyDescriptor","key","prototype","hasOwnProperty","call","desc","set","_interopRequireDefault","_jquery","CustomEvents","Repository","View","_selectors","updatePreferences","filter","value","type","updateUserPreferences","preferences","_exports","init","root","Selector","find","SELECTORS","FILTERS","define","events","activate","on","FILTER_OPTION","e","data","option","$","target","hasClass","attr","pref","customfieldvalue","courseView","region","page","document","querySelector","selectBlock","input","searchInput","clearIcon","clearSearch","originalEvent","preventDefault","DISPLAY_OPTION","reset","registerSelector"],"mappings":"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"}
\ No newline at end of file
diff --git a/blocks/navigation/amd/build/ajax_response_renderer.min.js b/blocks/navigation/amd/build/ajax_response_renderer.min.js
index bfd9e6ac7a2..57b8abf22d6 100644
--- a/blocks/navigation/amd/build/ajax_response_renderer.min.js
+++ b/blocks/navigation/amd/build/ajax_response_renderer.min.js
@@ -6,6 +6,6 @@
* @copyright 2015 John Okely
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
-define("block_navigation/ajax_response_renderer",["jquery","core/templates","core/notification","core/url","core/aria"],(function($,Templates,Notification,Url,Aria){var NODETYPE_ACTIVITY=40,NODETYPE_RESOURCE=50;function buildDOM(rootElement,nodes){var ul=$("
");ul.attr("role","group"),Aria.hide(ul),$.each(nodes,(function(index,node){if("object"==typeof node){var li=$(""),p=$(""),id=node.id||node.key+"_tree_item",icon=null,isBranch=!(!node.expandable&&!node.haschildren);li.attr("role","treeitem"),p.addClass("tree_item"),p.attr("id",id),p.attr("tabindex","-1"),node.requiresajaxloading&&(li.attr("data-requires-ajax",!0),li.attr("data-node-id",node.id),li.attr("data-node-key",node.key),li.attr("data-node-type",node.type)),isBranch&&(li.addClass("collapsed contains_branch"),li.attr("aria-expanded",!1),p.addClass("branch"));var eleToAddIcon=null;if(node.link){var link=$('');eleToAddIcon=link,link.append(''+node.name+""),node.hidden&&link.addClass("dimmed"),p.append(link)}else{var span=$("");eleToAddIcon=span,span.append(''+node.name+""),node.hidden&&span.addClass("dimmed"),p.append(span)}!node.icon||isBranch&&node.type!==NODETYPE.ACTIVITY&&node.type!==NODETYPE.RESOURCE||(li.addClass("item_with_icon"),p.addClass("hasicon"),node.type===NODETYPE.ACTIVITY||node.type===NODETYPE.RESOURCE?((icon=$("")).attr("alt",node.icon.alt),icon.attr("title",node.icon.title),icon.attr("src",Url.imageUrl(node.icon.pix,node.icon.component)),$.each(node.icon.classes,(function(index,className){icon.addClass(className)})),eleToAddIcon.prepend(icon)):("moodle"==node.icon.component&&(node.icon.component="core"),Templates.renderPix(node.icon.pix,node.icon.component,node.icon.title).then((function(html){eleToAddIcon.prepend(html)})).catch(Notification.exception))),li.append(p),ul.append(li),node.children&&node.children.length?buildDOM(li,node.children):isBranch&&!node.requiresajaxloading&&(li.removeClass("contains_branch"),p.addClass("emptybranch"))}})),rootElement.append(ul);var id=rootElement.attr("id")+"_group";ul.attr("id",id),rootElement.attr("aria-owns",id),rootElement.attr("role","treeitem")}return{render:function(element,nodes){if(nodes.children&&nodes.children.length){buildDOM(element,nodes.children);var item=element.children("[role='treeitem']").first(),group=element.find("#"+item.attr("aria-owns"));item.attr("aria-expanded",!0),Aria.unhide(group)}else element.hasClass("contains_branch")&&(element.removeClass("contains_branch"),element.addClass("emptybranch"))}}}));
//# sourceMappingURL=ajax_response_renderer.min.js.map
\ No newline at end of file
diff --git a/blocks/navigation/amd/build/ajax_response_renderer.min.js.map b/blocks/navigation/amd/build/ajax_response_renderer.min.js.map
index 9326e87fb70..c6d3bd4c56e 100644
--- a/blocks/navigation/amd/build/ajax_response_renderer.min.js.map
+++ b/blocks/navigation/amd/build/ajax_response_renderer.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"ajax_response_renderer.min.js","sources":["../src/ajax_response_renderer.js"],"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 * Parse the response from the navblock ajax page and render the correct DOM\n * structure for the tree from it.\n *\n * @module block_navigation/ajax_response_renderer\n * @copyright 2015 John Okely \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine([\n 'jquery',\n 'core/templates',\n 'core/notification',\n 'core/url',\n 'core/aria',\n], function(\n $,\n Templates,\n Notification,\n Url,\n Aria\n) {\n\n // Mappings for the different types of nodes coming from the navigation.\n // Copied from lib/navigationlib.php navigation_node constants.\n var NODETYPE = {\n // @type int Activity (course module) = 40.\n ACTIVITY: 40,\n // @type int Resource (course module = 50.\n RESOURCE: 50,\n };\n\n /**\n * Build DOM.\n *\n * @method buildDOM\n * @param {Object} rootElement the root element of DOM.\n * @param {object} nodes jquery object representing the nodes to be build.\n */\n function buildDOM(rootElement, nodes) {\n var ul = $('
');\n ul.attr('role', 'group');\n Aria.hide(ul);\n\n $.each(nodes, function(index, node) {\n if (typeof node !== 'object') {\n return;\n }\n\n var li = $('');\n var p = $('');\n var id = node.id || node.key + '_tree_item';\n var icon = null;\n var isBranch = (node.expandable || node.haschildren) ? true : false;\n\n li.attr('role', 'treeitem');\n p.addClass('tree_item');\n p.attr('id', id);\n // Negative tab index to allow it to receive focus.\n p.attr('tabindex', '-1');\n\n if (node.requiresajaxloading) {\n li.attr('data-requires-ajax', true);\n li.attr('data-node-id', node.id);\n li.attr('data-node-key', node.key);\n li.attr('data-node-type', node.type);\n }\n\n if (isBranch) {\n li.addClass('collapsed contains_branch');\n li.attr('aria-expanded', false);\n p.addClass('branch');\n }\n\n var eleToAddIcon = null;\n if (node.link) {\n var link = $('');\n\n eleToAddIcon = link;\n link.append('' + node.name + '');\n\n if (node.hidden) {\n link.addClass('dimmed');\n }\n\n p.append(link);\n } else {\n var span = $('');\n\n eleToAddIcon = span;\n span.append('' + node.name + '');\n\n if (node.hidden) {\n span.addClass('dimmed');\n }\n\n p.append(span);\n }\n\n if (node.icon && (!isBranch || node.type === NODETYPE.ACTIVITY || node.type === NODETYPE.RESOURCE)) {\n li.addClass('item_with_icon');\n p.addClass('hasicon');\n\n if (node.type === NODETYPE.ACTIVITY || node.type === NODETYPE.RESOURCE) {\n icon = $('');\n icon.attr('alt', node.icon.alt);\n icon.attr('title', node.icon.title);\n icon.attr('src', Url.imageUrl(node.icon.pix, node.icon.component));\n $.each(node.icon.classes, function(index, className) {\n icon.addClass(className);\n });\n eleToAddIcon.prepend(icon);\n } else {\n if (node.icon.component == 'moodle') {\n node.icon.component = 'core';\n }\n Templates.renderPix(node.icon.pix, node.icon.component, node.icon.title).then(function(html) {\n // Prepend.\n eleToAddIcon.prepend(html);\n return;\n }).catch(Notification.exception);\n }\n }\n\n li.append(p);\n ul.append(li);\n\n if (node.children && node.children.length) {\n buildDOM(li, node.children);\n } else if (isBranch && !node.requiresajaxloading) {\n li.removeClass('contains_branch');\n p.addClass('emptybranch');\n }\n });\n\n rootElement.append(ul);\n var id = rootElement.attr('id') + '_group';\n ul.attr('id', id);\n rootElement.attr('aria-owns', id);\n rootElement.attr('role', 'treeitem');\n }\n\n return {\n render: function(element, nodes) {\n // The first element of the response is the existing node so we start with processing the children.\n if (nodes.children && nodes.children.length) {\n buildDOM(element, nodes.children);\n\n var item = element.children(\"[role='treeitem']\").first();\n var group = element.find('#' + item.attr('aria-owns'));\n\n item.attr('aria-expanded', true);\n Aria.unhide(group);\n } else {\n if (element.hasClass('contains_branch')) {\n element.removeClass('contains_branch');\n element.addClass('emptybranch');\n }\n }\n }\n };\n});\n"],"names":["define","$","Templates","Notification","Url","Aria","NODETYPE","buildDOM","rootElement","nodes","ul","attr","hide","each","index","node","li","p","id","key","icon","isBranch","expandable","haschildren","addClass","requiresajaxloading","type","eleToAddIcon","link","title","append","name","hidden","span","alt","imageUrl","pix","component","classes","className","prepend","renderPix","then","html","catch","exception","children","length","removeClass","render","element","item","first","group","find","unhide","hasClass"],"mappings":"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"}
\ No newline at end of file
+{"version":3,"file":"ajax_response_renderer.min.js","sources":["../src/ajax_response_renderer.js"],"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 * Parse the response from the navblock ajax page and render the correct DOM\n * structure for the tree from it.\n *\n * @module block_navigation/ajax_response_renderer\n * @copyright 2015 John Okely \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine([\n 'jquery',\n 'core/templates',\n 'core/notification',\n 'core/url',\n 'core/aria',\n], function(\n $,\n Templates,\n Notification,\n Url,\n Aria\n) {\n\n // Mappings for the different types of nodes coming from the navigation.\n // Copied from lib/navigationlib.php navigation_node constants.\n var NODETYPE = {\n // @type int Activity (course module) = 40.\n ACTIVITY: 40,\n // @type int Resource (course module = 50.\n RESOURCE: 50,\n };\n\n /**\n * Build DOM.\n *\n * @method buildDOM\n * @param {Object} rootElement the root element of DOM.\n * @param {object} nodes jquery object representing the nodes to be build.\n */\n function buildDOM(rootElement, nodes) {\n var ul = $('
');\n ul.attr('role', 'group');\n Aria.hide(ul);\n\n $.each(nodes, function(index, node) {\n if (typeof node !== 'object') {\n return;\n }\n\n var li = $('');\n var p = $('');\n var id = node.id || node.key + '_tree_item';\n var icon = null;\n var isBranch = (node.expandable || node.haschildren) ? true : false;\n\n li.attr('role', 'treeitem');\n p.addClass('tree_item');\n p.attr('id', id);\n // Negative tab index to allow it to receive focus.\n p.attr('tabindex', '-1');\n\n if (node.requiresajaxloading) {\n li.attr('data-requires-ajax', true);\n li.attr('data-node-id', node.id);\n li.attr('data-node-key', node.key);\n li.attr('data-node-type', node.type);\n }\n\n if (isBranch) {\n li.addClass('collapsed contains_branch');\n li.attr('aria-expanded', false);\n p.addClass('branch');\n }\n\n var eleToAddIcon = null;\n if (node.link) {\n var link = $('');\n\n eleToAddIcon = link;\n link.append('' + node.name + '');\n\n if (node.hidden) {\n link.addClass('dimmed');\n }\n\n p.append(link);\n } else {\n var span = $('');\n\n eleToAddIcon = span;\n span.append('' + node.name + '');\n\n if (node.hidden) {\n span.addClass('dimmed');\n }\n\n p.append(span);\n }\n\n if (node.icon && (!isBranch || node.type === NODETYPE.ACTIVITY || node.type === NODETYPE.RESOURCE)) {\n li.addClass('item_with_icon');\n p.addClass('hasicon');\n\n if (node.type === NODETYPE.ACTIVITY || node.type === NODETYPE.RESOURCE) {\n icon = $('');\n icon.attr('alt', node.icon.alt);\n icon.attr('title', node.icon.title);\n icon.attr('src', Url.imageUrl(node.icon.pix, node.icon.component));\n $.each(node.icon.classes, function(index, className) {\n icon.addClass(className);\n });\n eleToAddIcon.prepend(icon);\n } else {\n if (node.icon.component == 'moodle') {\n node.icon.component = 'core';\n }\n Templates.renderPix(node.icon.pix, node.icon.component, node.icon.title).then(function(html) {\n // Prepend.\n eleToAddIcon.prepend(html);\n return;\n }).catch(Notification.exception);\n }\n }\n\n li.append(p);\n ul.append(li);\n\n if (node.children && node.children.length) {\n buildDOM(li, node.children);\n } else if (isBranch && !node.requiresajaxloading) {\n li.removeClass('contains_branch');\n p.addClass('emptybranch');\n }\n });\n\n rootElement.append(ul);\n var id = rootElement.attr('id') + '_group';\n ul.attr('id', id);\n rootElement.attr('aria-owns', id);\n rootElement.attr('role', 'treeitem');\n }\n\n return {\n render: function(element, nodes) {\n // The first element of the response is the existing node so we start with processing the children.\n if (nodes.children && nodes.children.length) {\n buildDOM(element, nodes.children);\n\n var item = element.children(\"[role='treeitem']\").first();\n var group = element.find('#' + item.attr('aria-owns'));\n\n item.attr('aria-expanded', true);\n Aria.unhide(group);\n } else {\n if (element.hasClass('contains_branch')) {\n element.removeClass('contains_branch');\n element.addClass('emptybranch');\n }\n }\n }\n };\n});\n"],"names":["define","$","Templates","Notification","Url","Aria","NODETYPE","ACTIVITY","RESOURCE","buildDOM","rootElement","nodes","ul","attr","hide","each","index","node","li","p","id","key","icon","isBranch","expandable","haschildren","addClass","requiresajaxloading","type","eleToAddIcon","link","title","append","name","hidden","span","alt","imageUrl","pix","component","classes","className","prepend","renderPix","then","html","catch","exception","children","length","removeClass","render","element","item","first","group","find","unhide","hasClass"],"mappings":"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"}
\ No newline at end of file
diff --git a/blocks/navigation/amd/build/nav_loader.min.js.map b/blocks/navigation/amd/build/nav_loader.min.js.map
index e53f935caea..bdea1e1d680 100644
--- a/blocks/navigation/amd/build/nav_loader.min.js.map
+++ b/blocks/navigation/amd/build/nav_loader.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"nav_loader.min.js","sources":["../src/nav_loader.js"],"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 * Load the nav tree items via ajax and render the response.\n *\n * @module block_navigation/nav_loader\n * @copyright 2015 John Okely \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(['jquery', 'core/ajax', 'core/config', 'block_navigation/ajax_response_renderer'],\n function($, ajax, config, renderer) {\n var URL = config.wwwroot + '/lib/ajax/getnavbranch.php';\n\n /**\n * Get the block instance id.\n *\n * @function getBlockInstanceId\n * @param {Element} element\n * @returns {String} the instance id\n */\n function getBlockInstanceId(element) {\n return element.closest('[data-block]').attr('data-instanceid');\n }\n\n return {\n load: function(element) {\n element = $(element);\n var promise = $.Deferred();\n var data = {\n elementid: element.attr('data-node-id'),\n id: element.attr('data-node-key'),\n type: element.attr('data-node-type'),\n sesskey: config.sesskey,\n instance: getBlockInstanceId(element)\n };\n var settings = {\n type: 'POST',\n dataType: 'json',\n data: data\n };\n\n $.ajax(URL, settings).done(function(nodes) {\n renderer.render(element, nodes);\n promise.resolve();\n });\n\n return promise;\n }\n };\n});\n"],"names":["define","$","ajax","config","renderer","URL","wwwroot","getBlockInstanceId","element","closest","attr","load","promise","Deferred","settings","type","dataType","data","elementid","id","sesskey","instance","done","nodes","render","resolve"],"mappings":";;;;;;;AAsBAA,qCAAO,CAAC,SAAU,YAAa,cAAe,4CAC1C,SAASC,EAAGC,KAAMC,OAAQC,cAClBC,IAAMF,OAAOG,QAAU,sCASlBC,mBAAmBC,gBACjBA,QAAQC,QAAQ,gBAAgBC,KAAK,yBAG7C,CACHC,KAAM,SAASH,SACXA,QAAUP,EAAEO,aACRI,QAAUX,EAAEY,WAQZC,SAAW,CACXC,KAAM,OACNC,SAAU,OACVC,KAVO,CACPC,UAAWV,QAAQE,KAAK,gBACxBS,GAAIX,QAAQE,KAAK,iBACjBK,KAAMP,QAAQE,KAAK,kBACnBU,QAASjB,OAAOiB,QAChBC,SAAUd,mBAAmBC,kBAQjCP,EAAEC,KAAKG,IAAKS,UAAUQ,MAAK,SAASC,OAChCnB,SAASoB,OAAOhB,QAASe,OACzBX,QAAQa,aAGLb"}
\ No newline at end of file
+{"version":3,"file":"nav_loader.min.js","sources":["../src/nav_loader.js"],"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 * Load the nav tree items via ajax and render the response.\n *\n * @module block_navigation/nav_loader\n * @copyright 2015 John Okely \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(['jquery', 'core/ajax', 'core/config', 'block_navigation/ajax_response_renderer'],\n function($, ajax, config, renderer) {\n var URL = config.wwwroot + '/lib/ajax/getnavbranch.php';\n\n /**\n * Get the block instance id.\n *\n * @function getBlockInstanceId\n * @param {Element} element\n * @returns {String} the instance id\n */\n function getBlockInstanceId(element) {\n return element.closest('[data-block]').attr('data-instanceid');\n }\n\n return {\n load: function(element) {\n element = $(element);\n var promise = $.Deferred();\n var data = {\n elementid: element.attr('data-node-id'),\n id: element.attr('data-node-key'),\n type: element.attr('data-node-type'),\n sesskey: config.sesskey,\n instance: getBlockInstanceId(element)\n };\n var settings = {\n type: 'POST',\n dataType: 'json',\n data: data\n };\n\n $.ajax(URL, settings).done(function(nodes) {\n renderer.render(element, nodes);\n promise.resolve();\n });\n\n return promise;\n }\n };\n});\n"],"names":["define","$","ajax","config","renderer","URL","wwwroot","getBlockInstanceId","element","closest","attr","load","promise","Deferred","settings","type","dataType","data","elementid","id","sesskey","instance","done","nodes","render","resolve"],"mappings":";;;;;;;AAsBAA,qCAAO,CAAC,SAAU,YAAa,cAAe,4CAC1C,SAASC,EAAGC,KAAMC,OAAQC,UACtB,IAAIC,IAAMF,OAAOG,QAAU,6BAS3B,SAASC,mBAAmBC,SACxB,OAAOA,QAAQC,QAAQ,gBAAgBC,KAAK,kBAChD,CAEJ,MAAO,CACHC,KAAM,SAASH,SACXA,QAAUP,EAAEO,SACZ,IAAII,QAAUX,EAAEY,WAQZC,SAAW,CACXC,KAAM,OACNC,SAAU,OACVC,KAVO,CACPC,UAAWV,QAAQE,KAAK,gBACxBS,GAAIX,QAAQE,KAAK,iBACjBK,KAAMP,QAAQE,KAAK,kBACnBU,QAASjB,OAAOiB,QAChBC,SAAUd,mBAAmBC,WAajC,OALAP,EAAEC,KAAKG,IAAKS,UAAUQ,MAAK,SAASC,OAChCnB,SAASoB,OAAOhB,QAASe,OACzBX,QAAQa,SACZ,IAEOb,OACX,EAER"}
\ No newline at end of file
diff --git a/blocks/navigation/amd/build/navblock.min.js.map b/blocks/navigation/amd/build/navblock.min.js.map
index 1f963812e7a..828458a204b 100644
--- a/blocks/navigation/amd/build/navblock.min.js.map
+++ b/blocks/navigation/amd/build/navblock.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"navblock.min.js","sources":["../src/navblock.js"],"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 * Load the navigation tree javascript.\n *\n * @module block_navigation/navblock\n * @copyright 2015 John Okely \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport {notifyBlockContentUpdated} from 'core_block/events';\nimport Tree from 'core/tree';\n\n/**\n * Initialise the navblock javascript for the specified block instance.\n *\n * @method\n * @param {Number} instanceId\n */\nexport const init = instanceId => {\n const navTree = new Tree(\".block_navigation .block_tree\");\n const blockNode = document.querySelector(`[data-instance-id=\"${instanceId}\"]`);\n\n /**\n * The method to call when then the navtree finishes expanding a group.\n *\n * @method finishExpandingGroup\n * @param {Object} item\n * @fires event:blockContentUpdated\n */\n navTree.finishExpandingGroup = item => {\n Tree.prototype.finishExpandingGroup.call(navTree, item);\n notifyBlockContentUpdated(blockNode);\n };\n\n /**\n * The method to call whe then the navtree collapses a group\n *\n * @method collapseGroup\n * @param {Object} item\n * @fires event:blockContentUpdated\n */\n navTree.collapseGroup = item => {\n Tree.prototype.collapseGroup.call(navTree, item);\n notifyBlockContentUpdated(blockNode);\n };\n};\n"],"names":["instanceId","navTree","Tree","blockNode","document","querySelector","finishExpandingGroup","item","prototype","call","collapseGroup"],"mappings":";;;;;;;oJA+BoBA,mBACVC,QAAU,IAAIC,cAAK,iCACnBC,UAAYC,SAASC,2CAAoCL,kBAS/DC,QAAQK,qBAAuBC,qBACtBC,UAAUF,qBAAqBG,KAAKR,QAASM,4CACxBJ,YAU9BF,QAAQS,cAAgBH,qBACfC,UAAUE,cAAcD,KAAKR,QAASM,4CACjBJ"}
\ No newline at end of file
+{"version":3,"file":"navblock.min.js","sources":["../src/navblock.js"],"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 * Load the navigation tree javascript.\n *\n * @module block_navigation/navblock\n * @copyright 2015 John Okely \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport {notifyBlockContentUpdated} from 'core_block/events';\nimport Tree from 'core/tree';\n\n/**\n * Initialise the navblock javascript for the specified block instance.\n *\n * @method\n * @param {Number} instanceId\n */\nexport const init = instanceId => {\n const navTree = new Tree(\".block_navigation .block_tree\");\n const blockNode = document.querySelector(`[data-instance-id=\"${instanceId}\"]`);\n\n /**\n * The method to call when then the navtree finishes expanding a group.\n *\n * @method finishExpandingGroup\n * @param {Object} item\n * @fires event:blockContentUpdated\n */\n navTree.finishExpandingGroup = item => {\n Tree.prototype.finishExpandingGroup.call(navTree, item);\n notifyBlockContentUpdated(blockNode);\n };\n\n /**\n * The method to call whe then the navtree collapses a group\n *\n * @method collapseGroup\n * @param {Object} item\n * @fires event:blockContentUpdated\n */\n navTree.collapseGroup = item => {\n Tree.prototype.collapseGroup.call(navTree, item);\n notifyBlockContentUpdated(blockNode);\n };\n};\n"],"names":["obj","_tree","__esModule","default","_exports","init","instanceId","navTree","Tree","blockNode","document","querySelector","concat","finishExpandingGroup","item","prototype","call","notifyBlockContentUpdated","collapseGroup"],"mappings":"iHAuB6B,IAAAA;;;;;;;kFAA7BC,OAA6BD,IAA7BC,QAA6BD,IAAAE,WAAAF,IAAAG,CAAAA,QAAAH,KAmC3BI,SAAAC,KA3BkBC,aAChB,MAAMC,QAAU,IAAIC,MAAIL,QAAC,iCACnBM,UAAYC,SAASC,cAAaC,sBAAAA,OAAuBN,WAAe,OAS9EC,QAAQM,qBAAuBC,OAC3BN,MAAIL,QAACY,UAAUF,qBAAqBG,KAAKT,QAASO,OAClD,EAAAG,QAAAA,2BAA0BR,UAAU,EAUxCF,QAAQW,cAAgBJ,OACpBN,MAAIL,QAACY,UAAUG,cAAcF,KAAKT,QAASO,OAC3C,EAAAG,QAAAA,2BAA0BR,UAAU,CACvC,CACH"}
\ No newline at end of file
diff --git a/blocks/navigation/amd/build/site_admin_loader.min.js.map b/blocks/navigation/amd/build/site_admin_loader.min.js.map
index 4eecd6aaf71..02042ca0621 100644
--- a/blocks/navigation/amd/build/site_admin_loader.min.js.map
+++ b/blocks/navigation/amd/build/site_admin_loader.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"site_admin_loader.min.js","sources":["../src/site_admin_loader.js"],"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 * Load the site admin nav tree via ajax and render the response.\n *\n * @module block_navigation/site_admin_loader\n * @copyright 2015 John Okely \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(['jquery', 'core/ajax', 'core/config', 'block_navigation/ajax_response_renderer'],\n function($, ajax, config, renderer) {\n\n var SITE_ADMIN_NODE_TYPE = 71;\n var URL = config.wwwroot + '/lib/ajax/getsiteadminbranch.php';\n\n return {\n load: function(element) {\n element = $(element);\n var promise = $.Deferred();\n var data = {\n type: SITE_ADMIN_NODE_TYPE,\n sesskey: config.sesskey\n };\n var settings = {\n type: 'POST',\n dataType: 'json',\n data: data\n };\n\n $.ajax(URL, settings).done(function(nodes) {\n renderer.render(element, nodes);\n promise.resolve();\n });\n\n return promise;\n }\n };\n});\n"],"names":["define","$","ajax","config","renderer","URL","wwwroot","load","element","promise","Deferred","settings","type","dataType","data","sesskey","done","nodes","render","resolve"],"mappings":";;;;;;;AAsBAA,4CAAO,CAAC,SAAU,YAAa,cAAe,4CACtC,SAASC,EAAGC,KAAMC,OAAQC,cAG1BC,IAAMF,OAAOG,QAAU,yCAEpB,CACHC,KAAM,SAASC,SACXA,QAAUP,EAAEO,aACRC,QAAUR,EAAES,WAKZC,SAAW,CACXC,KAAM,OACNC,SAAU,OACVC,KAPO,CACPF,KARe,GASfG,QAASZ,OAAOY,iBAQpBd,EAAEC,KAAKG,IAAKM,UAAUK,MAAK,SAASC,OAChCb,SAASc,OAAOV,QAASS,OACzBR,QAAQU,aAGLV"}
\ No newline at end of file
+{"version":3,"file":"site_admin_loader.min.js","sources":["../src/site_admin_loader.js"],"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 * Load the site admin nav tree via ajax and render the response.\n *\n * @module block_navigation/site_admin_loader\n * @copyright 2015 John Okely \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(['jquery', 'core/ajax', 'core/config', 'block_navigation/ajax_response_renderer'],\n function($, ajax, config, renderer) {\n\n var SITE_ADMIN_NODE_TYPE = 71;\n var URL = config.wwwroot + '/lib/ajax/getsiteadminbranch.php';\n\n return {\n load: function(element) {\n element = $(element);\n var promise = $.Deferred();\n var data = {\n type: SITE_ADMIN_NODE_TYPE,\n sesskey: config.sesskey\n };\n var settings = {\n type: 'POST',\n dataType: 'json',\n data: data\n };\n\n $.ajax(URL, settings).done(function(nodes) {\n renderer.render(element, nodes);\n promise.resolve();\n });\n\n return promise;\n }\n };\n});\n"],"names":["define","$","ajax","config","renderer","URL","wwwroot","load","element","promise","Deferred","settings","type","dataType","data","sesskey","done","nodes","render","resolve"],"mappings":";;;;;;;AAsBAA,4CAAO,CAAC,SAAU,YAAa,cAAe,4CACtC,SAASC,EAAGC,KAAMC,OAAQC,UAE9B,IACIC,IAAMF,OAAOG,QAAU,mCAE3B,MAAO,CACHC,KAAM,SAASC,SACXA,QAAUP,EAAEO,SACZ,IAAIC,QAAUR,EAAES,WAKZC,SAAW,CACXC,KAAM,OACNC,SAAU,OACVC,KAPO,CACPF,KARe,GASfG,QAASZ,OAAOY,UAapB,OALAd,EAAEC,KAAKG,IAAKM,UAAUK,MAAK,SAASC,OAChCb,SAASc,OAAOV,QAASS,OACzBR,QAAQU,SACZ,IAEOV,OACX,EAER"}
\ No newline at end of file
diff --git a/blocks/online_users/amd/build/change_user_visibility.min.js.map b/blocks/online_users/amd/build/change_user_visibility.min.js.map
index 683db00dfa2..2a23a1c26fe 100644
--- a/blocks/online_users/amd/build/change_user_visibility.min.js.map
+++ b/blocks/online_users/amd/build/change_user_visibility.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"change_user_visibility.min.js","sources":["../src/change_user_visibility.js"],"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 * A javascript module that handles the change of the user's visibility in the\n * online users block.\n *\n * @module block_online_users/change_user_visibility\n * @copyright 2018 Mihail Geshoski \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(['jquery', 'core/ajax', 'core/str', 'core/notification'],\n function($, Ajax, Str, Notification) {\n\n /**\n * Selectors.\n *\n * @access private\n * @type {Object}\n */\n var SELECTORS = {\n CHANGE_VISIBILITY_LINK: '#change-user-visibility',\n CHANGE_VISIBILITY_ICON: '#change-user-visibility .icon'\n };\n\n /**\n * Change user visibility in the online users block.\n *\n * @method changeVisibility\n * @param {String} action\n * @param {String} userid\n * @private\n */\n var changeVisibility = function(action, userid) {\n\n var value = action == \"show\" ? 1 : 0;\n var preferences = [{\n 'name': 'block_online_users_uservisibility',\n 'value': value,\n 'userid': userid\n }];\n\n var request = {\n methodname: 'core_user_set_user_preferences',\n args: {\n preferences: preferences\n }\n };\n Ajax.call([request])[0].then(function(data) {\n if (data.saved) {\n var newAction = oppositeAction(action);\n changeVisibilityLinkAttr(newAction);\n changeVisibilityIconAttr(newAction);\n }\n return;\n }).catch(Notification.exception);\n };\n\n /**\n * Get the opposite action.\n *\n * @method oppositeAction\n * @param {String} action\n * @return {String}\n * @private\n */\n var oppositeAction = function(action) {\n return action == 'show' ? 'hide' : 'show';\n };\n\n /**\n * Change the attribute values of the user visibility link in the online users block.\n *\n * @method changeVisibilityLinkAttr\n * @param {String} action\n * @private\n */\n var changeVisibilityLinkAttr = function(action) {\n getTitle(action).then(function(title) {\n $(SELECTORS.CHANGE_VISIBILITY_LINK).attr({\n 'data-action': action,\n 'title': title\n });\n return;\n }).catch(Notification.exception);\n };\n\n /**\n * Change the attribute values of the user visibility icon in the online users block.\n *\n * @method changeVisibilityIconAttr\n * @param {String} action\n * @private\n */\n var changeVisibilityIconAttr = function(action) {\n var icon = $(SELECTORS.CHANGE_VISIBILITY_ICON);\n getTitle(action).then(function(title) {\n // Add the proper title to the icon.\n $(icon).attr({\n 'title': title,\n 'aria-label': title\n });\n // If the icon is an image.\n if (icon.is(\"img\")) {\n $(icon).attr({\n 'src': M.util.image_url('t/' + action),\n 'alt': title\n });\n } else {\n // Add the new icon class and remove the old one.\n $(icon).addClass(getIconClass(action));\n $(icon).removeClass(getIconClass(oppositeAction(action)));\n }\n return;\n }).catch(Notification.exception);\n };\n\n /**\n * Get the proper class for the user visibility icon in the online users block.\n *\n * @method getIconClass\n * @param {String} action\n * @return {String}\n * @private\n */\n var getIconClass = function(action) {\n return action == 'show' ? 'fa-eye-slash' : 'fa-eye';\n };\n\n /**\n * Get the title description of the user visibility link in the online users block.\n *\n * @method getTitle\n * @param {String} action\n * @return {object} jQuery promise\n * @private\n */\n var getTitle = function(action) {\n return Str.get_string('online_status:' + action, 'block_online_users');\n };\n\n return {\n // Public variables and functions.\n /**\n * Initialise change user visibility function.\n *\n * @method init\n */\n init: function() {\n $(SELECTORS.CHANGE_VISIBILITY_LINK).on('click', function(e) {\n e.preventDefault();\n var action = ($(this).attr('data-action'));\n var userid = ($(this).attr('data-userid'));\n changeVisibility(action, userid);\n });\n }\n };\n});\n"],"names":["define","$","Ajax","Str","Notification","SELECTORS","oppositeAction","action","changeVisibilityLinkAttr","getTitle","then","title","attr","catch","exception","changeVisibilityIconAttr","icon","is","M","util","image_url","addClass","getIconClass","removeClass","get_string","init","on","e","preventDefault","userid","request","methodname","args","preferences","call","data","saved","newAction","changeVisibility","this"],"mappings":";;;;;;;;AAuBAA,mDAAO,CAAC,SAAU,YAAa,WAAY,sBACnC,SAASC,EAAGC,KAAMC,IAAKC,kBAQvBC,iCACwB,0BADxBA,iCAEwB,gCA4CxBC,eAAiB,SAASC,cACT,QAAVA,OAAmB,OAAS,QAUnCC,yBAA2B,SAASD,QACpCE,SAASF,QAAQG,MAAK,SAASC,OAC3BV,EAAEI,kCAAkCO,KAAK,eACtBL,aACNI,WAGdE,MAAMT,aAAaU,YAUtBC,yBAA2B,SAASR,YAChCS,KAAOf,EAAEI,kCACbI,SAASF,QAAQG,MAAK,SAASC,OAE3BV,EAAEe,MAAMJ,KAAK,OACAD,mBACKA,QAGdK,KAAKC,GAAG,OACRhB,EAAEe,MAAMJ,KAAK,KACFM,EAAEC,KAAKC,UAAU,KAAOb,YACxBI,SAIXV,EAAEe,MAAMK,SAASC,aAAaf,SAC9BN,EAAEe,MAAMO,YAAYD,aAAahB,eAAeC,cAGrDM,MAAMT,aAAaU,YAWtBQ,aAAe,SAASf,cACP,QAAVA,OAAmB,eAAiB,UAW3CE,SAAW,SAASF,eACbJ,IAAIqB,WAAW,iBAAmBjB,OAAQ,6BAG9C,CAOHkB,KAAM,WACFxB,EAAEI,kCAAkCqB,GAAG,SAAS,SAASC,GACrDA,EAAEC,iBArHS,SAASrB,OAAQsB,YAShCC,QAAU,CACVC,WAAY,iCACZC,KAAM,CACFC,YATU,CAAC,MACP,0CAFU,QAAV1B,OAAmB,EAAI,SAIrBsB,WASd3B,KAAKgC,KAAK,CAACJ,UAAU,GAAGpB,MAAK,SAASyB,SAC9BA,KAAKC,MAAO,KACRC,UAAY/B,eAAeC,QAC/BC,yBAAyB6B,WACzBtB,yBAAyBsB,eAG9BxB,MAAMT,aAAaU,WAkGdwB,CAFcrC,EAAEsC,MAAM3B,KAAK,eACbX,EAAEsC,MAAM3B,KAAK"}
\ No newline at end of file
+{"version":3,"file":"change_user_visibility.min.js","sources":["../src/change_user_visibility.js"],"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 * A javascript module that handles the change of the user's visibility in the\n * online users block.\n *\n * @module block_online_users/change_user_visibility\n * @copyright 2018 Mihail Geshoski \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(['jquery', 'core/ajax', 'core/str', 'core/notification'],\n function($, Ajax, Str, Notification) {\n\n /**\n * Selectors.\n *\n * @access private\n * @type {Object}\n */\n var SELECTORS = {\n CHANGE_VISIBILITY_LINK: '#change-user-visibility',\n CHANGE_VISIBILITY_ICON: '#change-user-visibility .icon'\n };\n\n /**\n * Change user visibility in the online users block.\n *\n * @method changeVisibility\n * @param {String} action\n * @param {String} userid\n * @private\n */\n var changeVisibility = function(action, userid) {\n\n var value = action == \"show\" ? 1 : 0;\n var preferences = [{\n 'name': 'block_online_users_uservisibility',\n 'value': value,\n 'userid': userid\n }];\n\n var request = {\n methodname: 'core_user_set_user_preferences',\n args: {\n preferences: preferences\n }\n };\n Ajax.call([request])[0].then(function(data) {\n if (data.saved) {\n var newAction = oppositeAction(action);\n changeVisibilityLinkAttr(newAction);\n changeVisibilityIconAttr(newAction);\n }\n return;\n }).catch(Notification.exception);\n };\n\n /**\n * Get the opposite action.\n *\n * @method oppositeAction\n * @param {String} action\n * @return {String}\n * @private\n */\n var oppositeAction = function(action) {\n return action == 'show' ? 'hide' : 'show';\n };\n\n /**\n * Change the attribute values of the user visibility link in the online users block.\n *\n * @method changeVisibilityLinkAttr\n * @param {String} action\n * @private\n */\n var changeVisibilityLinkAttr = function(action) {\n getTitle(action).then(function(title) {\n $(SELECTORS.CHANGE_VISIBILITY_LINK).attr({\n 'data-action': action,\n 'title': title\n });\n return;\n }).catch(Notification.exception);\n };\n\n /**\n * Change the attribute values of the user visibility icon in the online users block.\n *\n * @method changeVisibilityIconAttr\n * @param {String} action\n * @private\n */\n var changeVisibilityIconAttr = function(action) {\n var icon = $(SELECTORS.CHANGE_VISIBILITY_ICON);\n getTitle(action).then(function(title) {\n // Add the proper title to the icon.\n $(icon).attr({\n 'title': title,\n 'aria-label': title\n });\n // If the icon is an image.\n if (icon.is(\"img\")) {\n $(icon).attr({\n 'src': M.util.image_url('t/' + action),\n 'alt': title\n });\n } else {\n // Add the new icon class and remove the old one.\n $(icon).addClass(getIconClass(action));\n $(icon).removeClass(getIconClass(oppositeAction(action)));\n }\n return;\n }).catch(Notification.exception);\n };\n\n /**\n * Get the proper class for the user visibility icon in the online users block.\n *\n * @method getIconClass\n * @param {String} action\n * @return {String}\n * @private\n */\n var getIconClass = function(action) {\n return action == 'show' ? 'fa-eye-slash' : 'fa-eye';\n };\n\n /**\n * Get the title description of the user visibility link in the online users block.\n *\n * @method getTitle\n * @param {String} action\n * @return {object} jQuery promise\n * @private\n */\n var getTitle = function(action) {\n return Str.get_string('online_status:' + action, 'block_online_users');\n };\n\n return {\n // Public variables and functions.\n /**\n * Initialise change user visibility function.\n *\n * @method init\n */\n init: function() {\n $(SELECTORS.CHANGE_VISIBILITY_LINK).on('click', function(e) {\n e.preventDefault();\n var action = ($(this).attr('data-action'));\n var userid = ($(this).attr('data-userid'));\n changeVisibility(action, userid);\n });\n }\n };\n});\n"],"names":["define","$","Ajax","Str","Notification","SELECTORS","oppositeAction","action","changeVisibilityLinkAttr","getTitle","then","title","attr","catch","exception","changeVisibilityIconAttr","icon","is","src","M","util","image_url","alt","addClass","getIconClass","removeClass","get_string","init","on","e","preventDefault","userid","request","methodname","args","preferences","name","value","call","data","saved","newAction","changeVisibility","this"],"mappings":";;;;;;;;AAuBAA,mDAAO,CAAC,SAAU,YAAa,WAAY,sBACnC,SAASC,EAAGC,KAAMC,IAAKC,cAQ3B,IAAIC,iCACwB,0BADxBA,iCAEwB,gCA4CxBC,eAAiB,SAASC,QAC1B,MAAiB,QAAVA,OAAmB,OAAS,QAUnCC,yBAA2B,SAASD,QACpCE,SAASF,QAAQG,MAAK,SAASC,OAC3BV,EAAEI,kCAAkCO,KAAK,CACrC,cAAeL,OACfI,MAASA,OAGhB,IAAEE,MAAMT,aAAaU,YAUtBC,yBAA2B,SAASR,QACpC,IAAIS,KAAOf,EAAEI,kCACbI,SAASF,QAAQG,MAAK,SAASC,OAE3BV,EAAEe,MAAMJ,KAAK,CACTD,MAASA,MACT,aAAcA,QAGdK,KAAKC,GAAG,OACRhB,EAAEe,MAAMJ,KAAK,CACTM,IAAOC,EAAEC,KAAKC,UAAU,KAAOd,QAC/Be,IAAOX,SAIXV,EAAEe,MAAMO,SAASC,aAAajB,SAC9BN,EAAEe,MAAMS,YAAYD,aAAalB,eAAeC,UAGvD,IAAEM,MAAMT,aAAaU,YAWtBU,aAAe,SAASjB,QACxB,MAAiB,QAAVA,OAAmB,eAAiB,UAW3CE,SAAW,SAASF,QACpB,OAAOJ,IAAIuB,WAAW,iBAAmBnB,OAAQ,uBAGrD,MAAO,CAOHoB,KAAM,WACF1B,EAAEI,kCAAkCuB,GAAG,SAAS,SAASC,GACrDA,EAAEC,iBArHS,SAASvB,OAAQwB,QAEpC,IAOIC,QAAU,CACVC,WAAY,iCACZC,KAAM,CACFC,YATU,CAAC,CACfC,KAAQ,oCACRC,MAHkB,QAAV9B,OAAmB,EAAI,EAI/BwB,OAAUA,WASd7B,KAAKoC,KAAK,CAACN,UAAU,GAAGtB,MAAK,SAAS6B,MAClC,GAAIA,KAAKC,MAAO,CACZ,IAAIC,UAAYnC,eAAeC,QAC/BC,yBAAyBiC,WACzB1B,yBAAyB0B,UAC7B,CAEH,IAAE5B,MAAMT,aAAaU,WAkGd4B,CAFczC,EAAE0C,MAAM/B,KAAK,eACbX,EAAE0C,MAAM/B,KAAK,eAE/B,GACJ,EAER"}
\ No newline at end of file
diff --git a/blocks/private_files/amd/build/files_tree.min.js.map b/blocks/private_files/amd/build/files_tree.min.js.map
index 7acba375ae7..96e76d5a415 100644
--- a/blocks/private_files/amd/build/files_tree.min.js.map
+++ b/blocks/private_files/amd/build/files_tree.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"files_tree.min.js","sources":["../src/files_tree.js"],"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 * Changes the display of directories and files into a tree.\n *\n * @module block_private_files/files_tree\n * @copyright 2021 Shamim Rezaie \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport Tree from 'core/tree';\n\n/**\n * The init function that does the job.\n * It changes the display of directories and files into a tree.\n *\n * @param {string} blockId\n */\nexport const init = (blockId) => {\n new Tree(`#${blockId} [role=\"tree\"]`);\n};\n"],"names":["blockId","Tree"],"mappings":";;;;;;;oJA8BqBA,cACbC,yBAASD"}
\ No newline at end of file
+{"version":3,"file":"files_tree.min.js","sources":["../src/files_tree.js"],"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 * Changes the display of directories and files into a tree.\n *\n * @module block_private_files/files_tree\n * @copyright 2021 Shamim Rezaie \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport Tree from 'core/tree';\n\n/**\n * The init function that does the job.\n * It changes the display of directories and files into a tree.\n *\n * @param {string} blockId\n */\nexport const init = (blockId) => {\n new Tree(`#${blockId} [role=\"tree\"]`);\n};\n"],"names":["obj","_tree","__esModule","default","_exports","init","blockId","Tree","concat"],"mappings":"0FAsB6B,IAAAA;;;;;;;kFAA7BC,OAA6BD,IAA7BC,QAA6BD,IAAAE,WAAAF,IAAAG,CAAAA,QAAAH,KAU3BI,SAAAC,KAFmBC,UACjB,IAAIC,MAAIJ,QAAA,IAAAK,OAAKF,QAAwB,kBAAA,CACvC"}
\ No newline at end of file
diff --git a/blocks/recentlyaccessedcourses/amd/build/main.min.js.map b/blocks/recentlyaccessedcourses/amd/build/main.min.js.map
index 2e42d7b4895..3c493f2f4b3 100644
--- a/blocks/recentlyaccessedcourses/amd/build/main.min.js.map
+++ b/blocks/recentlyaccessedcourses/amd/build/main.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"main.min.js","sources":["../src/main.js"],"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 to initialise the Recently accessed courses block.\n *\n * @module block_recentlyaccessedcourses/main\n * @copyright 2018 Victor Deniz \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\ndefine(\n [\n 'jquery',\n 'core/custom_interaction_events',\n 'core/notification',\n 'core/pubsub',\n 'core/paged_content_paging_bar',\n 'core/templates',\n 'core_course/events',\n 'core_course/repository',\n 'core/aria',\n ],\n function(\n $,\n CustomEvents,\n Notification,\n PubSub,\n PagedContentPagingBar,\n Templates,\n CourseEvents,\n CoursesRepository,\n Aria\n ) {\n\n // Constants.\n var NUM_COURSES_TOTAL = 10;\n var SELECTORS = {\n BLOCK_CONTAINER: '[data-region=\"recentlyaccessedcourses\"]',\n CARD_CONTAINER: '[data-region=\"card-deck\"]',\n COURSE_IS_FAVOURITE: '[data-region=\"is-favourite\"]',\n CONTENT: '[data-region=\"view-content\"]',\n EMPTY_MESSAGE: '[data-region=\"empty-message\"]',\n LOADING_PLACEHOLDER: '[data-region=\"loading-placeholder\"]',\n PAGING_BAR: '[data-region=\"paging-bar\"]',\n PAGING_BAR_NEXT: '[data-control=\"next\"]',\n PAGING_BAR_PREVIOUS: '[data-control=\"previous\"]'\n };\n // Module variables.\n var contentLoaded = false;\n var allCourses = [];\n var visibleCoursesId = null;\n var cardWidth = null;\n var viewIndex = 0;\n var availableVisibleCards = 1;\n\n /**\n * Show the empty message when no course are found.\n *\n * @param {object} root The root element for the courses view.\n */\n var showEmptyMessage = function(root) {\n root.find(SELECTORS.EMPTY_MESSAGE).removeClass('hidden');\n root.find(SELECTORS.LOADING_PLACEHOLDER).addClass('hidden');\n root.find(SELECTORS.CONTENT).addClass('hidden');\n };\n\n /**\n * Show the empty message when no course are found.\n *\n * @param {object} root The root element for the courses view.\n */\n var showContent = function(root) {\n root.find(SELECTORS.CONTENT).removeClass('hidden');\n root.find(SELECTORS.EMPTY_MESSAGE).addClass('hidden');\n root.find(SELECTORS.LOADING_PLACEHOLDER).addClass('hidden');\n };\n\n /**\n * Show the paging bar.\n *\n * @param {object} root The root element for the courses view.\n */\n var showPagingBar = function(root) {\n var pagingBar = root.find(SELECTORS.PAGING_BAR);\n pagingBar.css('opacity', 1);\n pagingBar.css('visibility', 'visible');\n Aria.unhide(pagingBar);\n };\n\n /**\n * Hide the paging bar.\n *\n * @param {object} root The root element for the courses view.\n */\n var hidePagingBar = function(root) {\n var pagingBar = root.find(SELECTORS.PAGING_BAR);\n pagingBar.css('opacity', 0);\n pagingBar.css('visibility', 'hidden');\n Aria.hide(pagingBar);\n };\n\n /**\n * Show the favourite indicator for the given course (if it's in the list).\n *\n * @param {object} root The root element for the courses view.\n * @param {number} courseId The id of the course to be favourited.\n */\n var favouriteCourse = function(root, courseId) {\n allCourses.forEach(function(course) {\n if (course.attr('data-course-id') == courseId) {\n course.find(SELECTORS.COURSE_IS_FAVOURITE).removeClass('hidden');\n }\n });\n };\n\n /**\n * Hide the favourite indicator for the given course (if it's in the list).\n *\n * @param {object} root The root element for the courses view.\n * @param {number} courseId The id of the course to be unfavourited.\n */\n var unfavouriteCourse = function(root, courseId) {\n allCourses.forEach(function(course) {\n if (course.attr('data-course-id') == courseId) {\n course.find(SELECTORS.COURSE_IS_FAVOURITE).addClass('hidden');\n }\n });\n };\n\n /**\n * Render the a list of courses.\n *\n * @param {array} courses containing array of courses.\n * @return {promise} Resolved with list of rendered courses as jQuery objects.\n */\n var renderAllCourses = function(courses) {\n var showcoursecategory = $(SELECTORS.BLOCK_CONTAINER).data('displaycoursecategory');\n var promises = courses.map(function(course) {\n course.showcoursecategory = showcoursecategory;\n return Templates.render('block_recentlyaccessedcourses/course-card', course);\n });\n\n return $.when.apply(null, promises).then(function() {\n var renderedCourses = [];\n\n promises.forEach(function(promise) {\n promise.then(function(html) {\n renderedCourses.push($(html));\n return;\n })\n .catch(Notification.exception);\n });\n\n return renderedCourses;\n });\n };\n\n /**\n * Fetch user's recently accessed courses and reload the content of the block.\n *\n * @param {int} userid User whose courses will be shown\n * @returns {promise} The updated content for the block.\n */\n var loadContent = function(userid) {\n return CoursesRepository.getLastAccessedCourses(userid, NUM_COURSES_TOTAL)\n .then(function(courses) {\n return renderAllCourses(courses);\n });\n };\n\n /**\n * Recalculate the number of courses that should be visible.\n *\n * @param {object} root The root element for the courses view.\n */\n var recalculateVisibleCourses = function(root) {\n var container = root.find(SELECTORS.CONTENT).find(SELECTORS.CARD_CONTAINER);\n var availableWidth = parseFloat(root.css('width'));\n var numberOfCourses = allCourses.length;\n var start = 0;\n\n if (!cardWidth) {\n container.html(allCourses[0]);\n // Render one card initially to calculate the width of the cards\n // including the margins.\n cardWidth = allCourses[0].outerWidth(true);\n }\n\n availableVisibleCards = Math.floor(availableWidth / cardWidth);\n\n if (viewIndex + availableVisibleCards < numberOfCourses) {\n start = viewIndex;\n } else {\n var overflow = (viewIndex + availableVisibleCards) - numberOfCourses;\n start = viewIndex - overflow;\n start = start >= 0 ? start : 0;\n }\n\n // At least show one card.\n if (availableVisibleCards === 0) {\n availableVisibleCards = 1;\n }\n\n var coursesToShow = allCourses.slice(start, start + availableVisibleCards);\n // Create an id for the list of courses we expect to be displayed.\n var newVisibleCoursesId = coursesToShow.reduce(function(carry, course) {\n return carry + course.attr('data-course-id');\n }, '');\n\n // Centre the courses if we have an overflow of courses.\n if (allCourses.length > coursesToShow.length) {\n container.addClass('justify-content-center');\n container.removeClass('justify-content-start');\n } else {\n container.removeClass('justify-content-center');\n container.addClass('justify-content-start');\n }\n\n // Don't bother updating the DOM unless the visible courses have changed.\n if (visibleCoursesId != newVisibleCoursesId) {\n var pagingBar = root.find(PagedContentPagingBar.rootSelector);\n container.html(coursesToShow);\n visibleCoursesId = newVisibleCoursesId;\n\n if (availableVisibleCards >= allCourses.length) {\n hidePagingBar(root);\n } else {\n showPagingBar(root);\n\n if (viewIndex === 0) {\n PagedContentPagingBar.disablePreviousControlButtons(pagingBar);\n } else {\n PagedContentPagingBar.enablePreviousControlButtons(pagingBar);\n }\n\n if (viewIndex + availableVisibleCards >= allCourses.length) {\n PagedContentPagingBar.disableNextControlButtons(pagingBar);\n } else {\n PagedContentPagingBar.enableNextControlButtons(pagingBar);\n }\n }\n }\n };\n\n /**\n * Register event listeners for the block.\n *\n * @param {object} root The root element for the recentlyaccessedcourses block.\n */\n var registerEventListeners = function(root) {\n var resizeTimeout = null;\n var drawerToggling = false;\n\n PubSub.subscribe(CourseEvents.favourited, function(courseId) {\n favouriteCourse(root, courseId);\n });\n\n PubSub.subscribe(CourseEvents.unfavorited, function(courseId) {\n unfavouriteCourse(root, courseId);\n });\n\n PubSub.subscribe('nav-drawer-toggle-start', function() {\n if (!contentLoaded || !allCourses.length || drawerToggling) {\n // Nothing to recalculate.\n return;\n }\n\n drawerToggling = true;\n var recalculationCount = 0;\n // This function is going to recalculate the number of courses while\n // the nav drawer is opening or closes (up to a maximum of 5 recalcs).\n var doRecalculation = function() {\n setTimeout(function() {\n recalculateVisibleCourses(root);\n recalculationCount++;\n\n if (recalculationCount < 5 && drawerToggling) {\n // If we haven't done too many recalculations and the drawer\n // is still toggling then recurse.\n doRecalculation();\n }\n }, 100);\n };\n\n // Start the recalculations.\n doRecalculation(root);\n });\n\n PubSub.subscribe('nav-drawer-toggle-end', function() {\n drawerToggling = false;\n });\n\n $(window).on('resize', function() {\n if (!contentLoaded || !allCourses.length) {\n // Nothing to reclculate.\n return;\n }\n\n // Resize events fire rapidly so recalculating the visible courses each\n // time can be expensive. Let's debounce them,\n if (!resizeTimeout) {\n resizeTimeout = setTimeout(function() {\n resizeTimeout = null;\n recalculateVisibleCourses(root);\n // The recalculateVisibleCourses function will execute at a rate of 15fps.\n }, 66);\n }\n });\n\n CustomEvents.define(root, [CustomEvents.events.activate]);\n root.on(CustomEvents.events.activate, SELECTORS.PAGING_BAR_NEXT, function(e, data) {\n var button = $(e.target).closest(SELECTORS.PAGING_BAR_NEXT);\n if (!button.hasClass('disabled')) {\n viewIndex = viewIndex + availableVisibleCards;\n recalculateVisibleCourses(root);\n }\n\n data.originalEvent.preventDefault();\n });\n\n root.on(CustomEvents.events.activate, SELECTORS.PAGING_BAR_PREVIOUS, function(e, data) {\n var button = $(e.target).closest(SELECTORS.PAGING_BAR_PREVIOUS);\n if (!button.hasClass('disabled')) {\n viewIndex = viewIndex - availableVisibleCards;\n viewIndex = viewIndex < 0 ? 0 : viewIndex;\n recalculateVisibleCourses(root);\n }\n\n data.originalEvent.preventDefault();\n });\n };\n\n /**\n * Get and show the recent courses into the block.\n *\n * @param {int} userid User from which the courses will be obtained\n * @param {object} root The root element for the recentlyaccessedcourses block.\n */\n var init = function(userid, root) {\n root = $(root);\n\n registerEventListeners(root);\n loadContent(userid)\n .then(function(renderedCourses) {\n allCourses = renderedCourses;\n contentLoaded = true;\n\n if (allCourses.length) {\n showContent(root);\n recalculateVisibleCourses(root);\n } else {\n showEmptyMessage(root);\n }\n\n return;\n })\n .catch(Notification.exception);\n };\n\n return {\n init: init\n };\n });\n"],"names":["define","$","CustomEvents","Notification","PubSub","PagedContentPagingBar","Templates","CourseEvents","CoursesRepository","Aria","SELECTORS","contentLoaded","allCourses","visibleCoursesId","cardWidth","viewIndex","availableVisibleCards","loadContent","userid","getLastAccessedCourses","then","courses","showcoursecategory","data","promises","map","course","render","when","apply","renderedCourses","forEach","promise","html","push","catch","exception","renderAllCourses","recalculateVisibleCourses","root","container","find","availableWidth","parseFloat","css","numberOfCourses","length","start","outerWidth","Math","floor","coursesToShow","slice","newVisibleCoursesId","reduce","carry","attr","addClass","removeClass","pagingBar","rootSelector","hide","hidePagingBar","unhide","showPagingBar","disablePreviousControlButtons","enablePreviousControlButtons","disableNextControlButtons","enableNextControlButtons","registerEventListeners","resizeTimeout","drawerToggling","subscribe","favourited","courseId","favouriteCourse","unfavorited","unfavouriteCourse","recalculationCount","doRecalculation","setTimeout","window","on","events","activate","e","target","closest","hasClass","originalEvent","preventDefault","init","showContent","showEmptyMessage"],"mappings":"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"}
\ No newline at end of file
+{"version":3,"file":"main.min.js","sources":["../src/main.js"],"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 to initialise the Recently accessed courses block.\n *\n * @module block_recentlyaccessedcourses/main\n * @copyright 2018 Victor Deniz \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\ndefine(\n [\n 'jquery',\n 'core/custom_interaction_events',\n 'core/notification',\n 'core/pubsub',\n 'core/paged_content_paging_bar',\n 'core/templates',\n 'core_course/events',\n 'core_course/repository',\n 'core/aria',\n ],\n function(\n $,\n CustomEvents,\n Notification,\n PubSub,\n PagedContentPagingBar,\n Templates,\n CourseEvents,\n CoursesRepository,\n Aria\n ) {\n\n // Constants.\n var NUM_COURSES_TOTAL = 10;\n var SELECTORS = {\n BLOCK_CONTAINER: '[data-region=\"recentlyaccessedcourses\"]',\n CARD_CONTAINER: '[data-region=\"card-deck\"]',\n COURSE_IS_FAVOURITE: '[data-region=\"is-favourite\"]',\n CONTENT: '[data-region=\"view-content\"]',\n EMPTY_MESSAGE: '[data-region=\"empty-message\"]',\n LOADING_PLACEHOLDER: '[data-region=\"loading-placeholder\"]',\n PAGING_BAR: '[data-region=\"paging-bar\"]',\n PAGING_BAR_NEXT: '[data-control=\"next\"]',\n PAGING_BAR_PREVIOUS: '[data-control=\"previous\"]'\n };\n // Module variables.\n var contentLoaded = false;\n var allCourses = [];\n var visibleCoursesId = null;\n var cardWidth = null;\n var viewIndex = 0;\n var availableVisibleCards = 1;\n\n /**\n * Show the empty message when no course are found.\n *\n * @param {object} root The root element for the courses view.\n */\n var showEmptyMessage = function(root) {\n root.find(SELECTORS.EMPTY_MESSAGE).removeClass('hidden');\n root.find(SELECTORS.LOADING_PLACEHOLDER).addClass('hidden');\n root.find(SELECTORS.CONTENT).addClass('hidden');\n };\n\n /**\n * Show the empty message when no course are found.\n *\n * @param {object} root The root element for the courses view.\n */\n var showContent = function(root) {\n root.find(SELECTORS.CONTENT).removeClass('hidden');\n root.find(SELECTORS.EMPTY_MESSAGE).addClass('hidden');\n root.find(SELECTORS.LOADING_PLACEHOLDER).addClass('hidden');\n };\n\n /**\n * Show the paging bar.\n *\n * @param {object} root The root element for the courses view.\n */\n var showPagingBar = function(root) {\n var pagingBar = root.find(SELECTORS.PAGING_BAR);\n pagingBar.css('opacity', 1);\n pagingBar.css('visibility', 'visible');\n Aria.unhide(pagingBar);\n };\n\n /**\n * Hide the paging bar.\n *\n * @param {object} root The root element for the courses view.\n */\n var hidePagingBar = function(root) {\n var pagingBar = root.find(SELECTORS.PAGING_BAR);\n pagingBar.css('opacity', 0);\n pagingBar.css('visibility', 'hidden');\n Aria.hide(pagingBar);\n };\n\n /**\n * Show the favourite indicator for the given course (if it's in the list).\n *\n * @param {object} root The root element for the courses view.\n * @param {number} courseId The id of the course to be favourited.\n */\n var favouriteCourse = function(root, courseId) {\n allCourses.forEach(function(course) {\n if (course.attr('data-course-id') == courseId) {\n course.find(SELECTORS.COURSE_IS_FAVOURITE).removeClass('hidden');\n }\n });\n };\n\n /**\n * Hide the favourite indicator for the given course (if it's in the list).\n *\n * @param {object} root The root element for the courses view.\n * @param {number} courseId The id of the course to be unfavourited.\n */\n var unfavouriteCourse = function(root, courseId) {\n allCourses.forEach(function(course) {\n if (course.attr('data-course-id') == courseId) {\n course.find(SELECTORS.COURSE_IS_FAVOURITE).addClass('hidden');\n }\n });\n };\n\n /**\n * Render the a list of courses.\n *\n * @param {array} courses containing array of courses.\n * @return {promise} Resolved with list of rendered courses as jQuery objects.\n */\n var renderAllCourses = function(courses) {\n var showcoursecategory = $(SELECTORS.BLOCK_CONTAINER).data('displaycoursecategory');\n var promises = courses.map(function(course) {\n course.showcoursecategory = showcoursecategory;\n return Templates.render('block_recentlyaccessedcourses/course-card', course);\n });\n\n return $.when.apply(null, promises).then(function() {\n var renderedCourses = [];\n\n promises.forEach(function(promise) {\n promise.then(function(html) {\n renderedCourses.push($(html));\n return;\n })\n .catch(Notification.exception);\n });\n\n return renderedCourses;\n });\n };\n\n /**\n * Fetch user's recently accessed courses and reload the content of the block.\n *\n * @param {int} userid User whose courses will be shown\n * @returns {promise} The updated content for the block.\n */\n var loadContent = function(userid) {\n return CoursesRepository.getLastAccessedCourses(userid, NUM_COURSES_TOTAL)\n .then(function(courses) {\n return renderAllCourses(courses);\n });\n };\n\n /**\n * Recalculate the number of courses that should be visible.\n *\n * @param {object} root The root element for the courses view.\n */\n var recalculateVisibleCourses = function(root) {\n var container = root.find(SELECTORS.CONTENT).find(SELECTORS.CARD_CONTAINER);\n var availableWidth = parseFloat(root.css('width'));\n var numberOfCourses = allCourses.length;\n var start = 0;\n\n if (!cardWidth) {\n container.html(allCourses[0]);\n // Render one card initially to calculate the width of the cards\n // including the margins.\n cardWidth = allCourses[0].outerWidth(true);\n }\n\n availableVisibleCards = Math.floor(availableWidth / cardWidth);\n\n if (viewIndex + availableVisibleCards < numberOfCourses) {\n start = viewIndex;\n } else {\n var overflow = (viewIndex + availableVisibleCards) - numberOfCourses;\n start = viewIndex - overflow;\n start = start >= 0 ? start : 0;\n }\n\n // At least show one card.\n if (availableVisibleCards === 0) {\n availableVisibleCards = 1;\n }\n\n var coursesToShow = allCourses.slice(start, start + availableVisibleCards);\n // Create an id for the list of courses we expect to be displayed.\n var newVisibleCoursesId = coursesToShow.reduce(function(carry, course) {\n return carry + course.attr('data-course-id');\n }, '');\n\n // Centre the courses if we have an overflow of courses.\n if (allCourses.length > coursesToShow.length) {\n container.addClass('justify-content-center');\n container.removeClass('justify-content-start');\n } else {\n container.removeClass('justify-content-center');\n container.addClass('justify-content-start');\n }\n\n // Don't bother updating the DOM unless the visible courses have changed.\n if (visibleCoursesId != newVisibleCoursesId) {\n var pagingBar = root.find(PagedContentPagingBar.rootSelector);\n container.html(coursesToShow);\n visibleCoursesId = newVisibleCoursesId;\n\n if (availableVisibleCards >= allCourses.length) {\n hidePagingBar(root);\n } else {\n showPagingBar(root);\n\n if (viewIndex === 0) {\n PagedContentPagingBar.disablePreviousControlButtons(pagingBar);\n } else {\n PagedContentPagingBar.enablePreviousControlButtons(pagingBar);\n }\n\n if (viewIndex + availableVisibleCards >= allCourses.length) {\n PagedContentPagingBar.disableNextControlButtons(pagingBar);\n } else {\n PagedContentPagingBar.enableNextControlButtons(pagingBar);\n }\n }\n }\n };\n\n /**\n * Register event listeners for the block.\n *\n * @param {object} root The root element for the recentlyaccessedcourses block.\n */\n var registerEventListeners = function(root) {\n var resizeTimeout = null;\n var drawerToggling = false;\n\n PubSub.subscribe(CourseEvents.favourited, function(courseId) {\n favouriteCourse(root, courseId);\n });\n\n PubSub.subscribe(CourseEvents.unfavorited, function(courseId) {\n unfavouriteCourse(root, courseId);\n });\n\n PubSub.subscribe('nav-drawer-toggle-start', function() {\n if (!contentLoaded || !allCourses.length || drawerToggling) {\n // Nothing to recalculate.\n return;\n }\n\n drawerToggling = true;\n var recalculationCount = 0;\n // This function is going to recalculate the number of courses while\n // the nav drawer is opening or closes (up to a maximum of 5 recalcs).\n var doRecalculation = function() {\n setTimeout(function() {\n recalculateVisibleCourses(root);\n recalculationCount++;\n\n if (recalculationCount < 5 && drawerToggling) {\n // If we haven't done too many recalculations and the drawer\n // is still toggling then recurse.\n doRecalculation();\n }\n }, 100);\n };\n\n // Start the recalculations.\n doRecalculation(root);\n });\n\n PubSub.subscribe('nav-drawer-toggle-end', function() {\n drawerToggling = false;\n });\n\n $(window).on('resize', function() {\n if (!contentLoaded || !allCourses.length) {\n // Nothing to reclculate.\n return;\n }\n\n // Resize events fire rapidly so recalculating the visible courses each\n // time can be expensive. Let's debounce them,\n if (!resizeTimeout) {\n resizeTimeout = setTimeout(function() {\n resizeTimeout = null;\n recalculateVisibleCourses(root);\n // The recalculateVisibleCourses function will execute at a rate of 15fps.\n }, 66);\n }\n });\n\n CustomEvents.define(root, [CustomEvents.events.activate]);\n root.on(CustomEvents.events.activate, SELECTORS.PAGING_BAR_NEXT, function(e, data) {\n var button = $(e.target).closest(SELECTORS.PAGING_BAR_NEXT);\n if (!button.hasClass('disabled')) {\n viewIndex = viewIndex + availableVisibleCards;\n recalculateVisibleCourses(root);\n }\n\n data.originalEvent.preventDefault();\n });\n\n root.on(CustomEvents.events.activate, SELECTORS.PAGING_BAR_PREVIOUS, function(e, data) {\n var button = $(e.target).closest(SELECTORS.PAGING_BAR_PREVIOUS);\n if (!button.hasClass('disabled')) {\n viewIndex = viewIndex - availableVisibleCards;\n viewIndex = viewIndex < 0 ? 0 : viewIndex;\n recalculateVisibleCourses(root);\n }\n\n data.originalEvent.preventDefault();\n });\n };\n\n /**\n * Get and show the recent courses into the block.\n *\n * @param {int} userid User from which the courses will be obtained\n * @param {object} root The root element for the recentlyaccessedcourses block.\n */\n var init = function(userid, root) {\n root = $(root);\n\n registerEventListeners(root);\n loadContent(userid)\n .then(function(renderedCourses) {\n allCourses = renderedCourses;\n contentLoaded = true;\n\n if (allCourses.length) {\n showContent(root);\n recalculateVisibleCourses(root);\n } else {\n showEmptyMessage(root);\n }\n\n return;\n })\n .catch(Notification.exception);\n };\n\n return {\n init: init\n };\n });\n"],"names":["define","$","CustomEvents","Notification","PubSub","PagedContentPagingBar","Templates","CourseEvents","CoursesRepository","Aria","SELECTORS","contentLoaded","allCourses","visibleCoursesId","cardWidth","viewIndex","availableVisibleCards","loadContent","userid","getLastAccessedCourses","then","courses","showcoursecategory","data","promises","map","course","render","when","apply","renderedCourses","forEach","promise","html","push","catch","exception","renderAllCourses","recalculateVisibleCourses","root","container","find","availableWidth","parseFloat","css","numberOfCourses","length","start","outerWidth","Math","floor","coursesToShow","slice","newVisibleCoursesId","reduce","carry","attr","addClass","removeClass","pagingBar","rootSelector","hide","hidePagingBar","unhide","showPagingBar","disablePreviousControlButtons","enablePreviousControlButtons","disableNextControlButtons","enableNextControlButtons","registerEventListeners","resizeTimeout","drawerToggling","subscribe","favourited","courseId","favouriteCourse","unfavorited","unfavouriteCourse","recalculationCount","doRecalculation","setTimeout","window","on","events","activate","e","target","closest","hasClass","originalEvent","preventDefault","init","showContent","showEmptyMessage"],"mappings":"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"}
\ No newline at end of file
diff --git a/blocks/recentlyaccesseditems/amd/build/main.min.js.map b/blocks/recentlyaccesseditems/amd/build/main.min.js.map
index e1cd3bc1fc1..6886e806b74 100644
--- a/blocks/recentlyaccesseditems/amd/build/main.min.js.map
+++ b/blocks/recentlyaccesseditems/amd/build/main.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"main.min.js","sources":["../src/main.js"],"sourcesContent":["\n// 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 to initialise the Recently accessed items block.\n *\n * @module block_recentlyaccesseditems/main\n * @copyright 2018 Victor Deniz \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\ndefine(\n [\n 'jquery',\n 'block_recentlyaccesseditems/repository',\n 'core/templates',\n 'core/notification'\n ],\n function(\n $,\n Repository,\n Templates,\n Notification\n ) {\n\n var NUM_ITEMS = 9;\n // Maximum number of elements to display in the block initially.\n var NUM_ITEMS_INIT = 3;\n\n var SELECTORS = {\n CARDDECK_CONTAINER: '[data-region=\"recentlyaccesseditems-view\"]',\n CARDDECK: '[data-region=\"recentlyaccesseditems-view-content\"]',\n SHOWMORE_LINK: '[data-region=\"recentlyaccesseditems-view\"] [data-action=\"more-items\"]',\n };\n\n /**\n * Register event listeners.\n */\n const registerEventListeners = () => {\n const showmoreLink = document.querySelector(SELECTORS.SHOWMORE_LINK);\n\n // Hide \"Show more\" link and show additional items.\n showmoreLink.addEventListener('click', () => {\n showmoreLink.classList.add('d-none');\n\n const hiddenItems = document.querySelector('[data-region=\"items-list\"]').children;\n hiddenItems.forEach(function(hiddenItem) {\n hiddenItem.style = \"display: block\";\n });\n });\n };\n\n /**\n * Get recent items from backend.\n *\n * @method getRecentItems\n * @param {int} limit Only return this many results\n * @return {array} Items user most recently has accessed\n */\n var getRecentItems = function(limit) {\n return Repository.getRecentItems(limit);\n };\n\n /**\n * Render the block content.\n *\n * @method renderItems\n * @param {object} root The root element for the items view.\n * @param {array} items containing array of returned items.\n * @return {promise} Resolved with HTML and JS strings\n */\n var renderItems = function(root, items) {\n if (items.length > 0) {\n let hasmoreitems = false;\n if (items.length > NUM_ITEMS_INIT) {\n hasmoreitems = true;\n }\n return Templates.render('block_recentlyaccesseditems/view-cards', {\n items: items,\n hasmoreitems: hasmoreitems\n });\n } else {\n var noitemsimgurl = root.attr('data-noitemsimgurl');\n return Templates.render('block_recentlyaccesseditems/no-items', {\n noitemsimgurl: noitemsimgurl\n });\n }\n };\n\n /**\n * Get and show the recent items into the block.\n *\n * @param {object} root The root element for the items block.\n */\n var init = function(root) {\n root = $(root);\n\n var itemsContainer = root.find(SELECTORS.CARDDECK_CONTAINER);\n var itemsContent = root.find(SELECTORS.CARDDECK);\n\n var itemsPromise = getRecentItems(NUM_ITEMS);\n\n itemsPromise.then(function(items) {\n var pageContentPromise = renderItems(itemsContainer, items);\n\n pageContentPromise.then(function(html, js) {\n Templates.replaceNodeContents(itemsContent, html, js);\n if (items.length > 3) {\n registerEventListeners();\n }\n return null;\n }).catch(Notification.exception);\n }).catch(Notification.exception);\n };\n\n return {\n init: init\n };\n });"],"names":["define","$","Repository","Templates","Notification","SELECTORS","init","root","limit","itemsContainer","find","itemsContent","getRecentItems","then","items","pageContentPromise","length","hasmoreitems","render","noitemsimgurl","attr","renderItems","html","js","replaceNodeContents","showmoreLink","document","querySelector","addEventListener","classList","add","children","forEach","hiddenItem","style","registerEventListeners","catch","exception"],"mappings":";;;;;;;AAwBAA,0CACI,CACI,SACA,yCACA,iBACA,sBAEJ,SACIC,EACAC,WACAC,UACAC,kBAOIC,6BACoB,6CADpBA,mBAEU,qDAFVA,wBAGe,8EAmFZ,CACHC,KAtBO,SAASC,UAnCUC,MAsCtBC,gBAFJF,KAAON,EAAEM,OAEiBG,KAAKL,8BAC3BM,aAAeJ,KAAKG,KAAKL,qBAvCHG,MAlCd,EAmCLN,WAAWU,eAAeJ,QA0CpBK,MAAK,SAASC,WACnBC,mBAhCM,SAASR,KAAMO,UACzBA,MAAME,OAAS,EAAG,KACdC,cAAe,SACfH,MAAME,OA/CG,IAgDTC,cAAe,GAEZd,UAAUe,OAAO,yCAA0C,CAC9DJ,MAAOA,MACPG,aAAcA,mBAGdE,cAAgBZ,KAAKa,KAAK,6BACvBjB,UAAUe,OAAO,uCAAwC,CAC5DC,cAAeA,gBAmBME,CAAYZ,eAAgBK,OAErDC,mBAAmBF,MAAK,SAASS,KAAMC,WACnCpB,UAAUqB,oBAAoBb,aAAcW,KAAMC,IAC9CT,MAAME,OAAS,GArEA,YACrBS,aAAeC,SAASC,cAActB,yBAG5CoB,aAAaG,iBAAiB,SAAS,KACnCH,aAAaI,UAAUC,IAAI,UAEPJ,SAASC,cAAc,8BAA8BI,SAC7DC,SAAQ,SAASC,YACzBA,WAAWC,MAAQ,wBA6DfC,GAEG,QACRC,MAAMhC,aAAaiC,cACvBD,MAAMhC,aAAaiC"}
\ No newline at end of file
+{"version":3,"file":"main.min.js","sources":["../src/main.js"],"sourcesContent":["\n// 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 to initialise the Recently accessed items block.\n *\n * @module block_recentlyaccesseditems/main\n * @copyright 2018 Victor Deniz \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\ndefine(\n [\n 'jquery',\n 'block_recentlyaccesseditems/repository',\n 'core/templates',\n 'core/notification'\n ],\n function(\n $,\n Repository,\n Templates,\n Notification\n ) {\n\n var NUM_ITEMS = 9;\n // Maximum number of elements to display in the block initially.\n var NUM_ITEMS_INIT = 3;\n\n var SELECTORS = {\n CARDDECK_CONTAINER: '[data-region=\"recentlyaccesseditems-view\"]',\n CARDDECK: '[data-region=\"recentlyaccesseditems-view-content\"]',\n SHOWMORE_LINK: '[data-region=\"recentlyaccesseditems-view\"] [data-action=\"more-items\"]',\n };\n\n /**\n * Register event listeners.\n */\n const registerEventListeners = () => {\n const showmoreLink = document.querySelector(SELECTORS.SHOWMORE_LINK);\n\n // Hide \"Show more\" link and show additional items.\n showmoreLink.addEventListener('click', () => {\n showmoreLink.classList.add('d-none');\n\n const hiddenItems = document.querySelector('[data-region=\"items-list\"]').children;\n hiddenItems.forEach(function(hiddenItem) {\n hiddenItem.style = \"display: block\";\n });\n });\n };\n\n /**\n * Get recent items from backend.\n *\n * @method getRecentItems\n * @param {int} limit Only return this many results\n * @return {array} Items user most recently has accessed\n */\n var getRecentItems = function(limit) {\n return Repository.getRecentItems(limit);\n };\n\n /**\n * Render the block content.\n *\n * @method renderItems\n * @param {object} root The root element for the items view.\n * @param {array} items containing array of returned items.\n * @return {promise} Resolved with HTML and JS strings\n */\n var renderItems = function(root, items) {\n if (items.length > 0) {\n let hasmoreitems = false;\n if (items.length > NUM_ITEMS_INIT) {\n hasmoreitems = true;\n }\n return Templates.render('block_recentlyaccesseditems/view-cards', {\n items: items,\n hasmoreitems: hasmoreitems\n });\n } else {\n var noitemsimgurl = root.attr('data-noitemsimgurl');\n return Templates.render('block_recentlyaccesseditems/no-items', {\n noitemsimgurl: noitemsimgurl\n });\n }\n };\n\n /**\n * Get and show the recent items into the block.\n *\n * @param {object} root The root element for the items block.\n */\n var init = function(root) {\n root = $(root);\n\n var itemsContainer = root.find(SELECTORS.CARDDECK_CONTAINER);\n var itemsContent = root.find(SELECTORS.CARDDECK);\n\n var itemsPromise = getRecentItems(NUM_ITEMS);\n\n itemsPromise.then(function(items) {\n var pageContentPromise = renderItems(itemsContainer, items);\n\n pageContentPromise.then(function(html, js) {\n Templates.replaceNodeContents(itemsContent, html, js);\n if (items.length > 3) {\n registerEventListeners();\n }\n return null;\n }).catch(Notification.exception);\n }).catch(Notification.exception);\n };\n\n return {\n init: init\n };\n });"],"names":["define","$","Repository","Templates","Notification","SELECTORS","init","root","limit","itemsContainer","find","itemsContent","getRecentItems","then","items","pageContentPromise","length","hasmoreitems","render","noitemsimgurl","attr","renderItems","html","js","replaceNodeContents","registerEventListeners","showmoreLink","document","querySelector","addEventListener","classList","add","children","forEach","hiddenItem","style","catch","exception"],"mappings":";;;;;;;AAwBAA,0CACI,CACI,SACA,yCACA,iBACA,sBAEJ,SACIC,EACAC,WACAC,UACAC,cAGA,IAIIC,6BACoB,6CADpBA,mBAEU,qDAFVA,wBAGe,wEAmFnB,MAAO,CACHC,KAtBO,SAASC,MAGhB,IAtC0BC,MAsCtBC,gBAFJF,KAAON,EAAEM,OAEiBG,KAAKL,8BAC3BM,aAAeJ,KAAKG,KAAKL,qBAvCHG,MAlCd,EAmCLN,WAAWU,eAAeJ,QA0CpBK,MAAK,SAASC,OACvB,IAAIC,mBAhCM,SAASR,KAAMO,OAC7B,GAAIA,MAAME,OAAS,EAAG,CAClB,IAAIC,cAAe,EAInB,OAHIH,MAAME,OA/CG,IAgDTC,cAAe,GAEZd,UAAUe,OAAO,yCAA0C,CAC9DJ,MAAOA,MACPG,aAAcA,cAEtB,CACI,IAAIE,cAAgBZ,KAAKa,KAAK,sBAC9B,OAAOjB,UAAUe,OAAO,uCAAwC,CAC5DC,cAAeA,gBAmBME,CAAYZ,eAAgBK,OAErDC,mBAAmBF,MAAK,SAASS,KAAMC,IAKnC,OAJApB,UAAUqB,oBAAoBb,aAAcW,KAAMC,IAC9CT,MAAME,OAAS,GArEAS,MAC3B,MAAMC,aAAeC,SAASC,cAAcvB,yBAG5CqB,aAAaG,iBAAiB,SAAS,KACnCH,aAAaI,UAAUC,IAAI,UAEPJ,SAASC,cAAc,8BAA8BI,SAC7DC,SAAQ,SAASC,YACzBA,WAAWC,MAAQ,gBACvB,GAAE,GACJ,EA2DUV,GAEG,IACV,IAAEW,MAAMhC,aAAaiC,UACzB,IAAED,MAAMhC,aAAaiC,YAM9B"}
\ No newline at end of file
diff --git a/blocks/recentlyaccesseditems/amd/build/repository.min.js.map b/blocks/recentlyaccesseditems/amd/build/repository.min.js.map
index 07ca64dd574..47ffc64ffaf 100644
--- a/blocks/recentlyaccesseditems/amd/build/repository.min.js.map
+++ b/blocks/recentlyaccesseditems/amd/build/repository.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"repository.min.js","sources":["../src/repository.js"],"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 * A javascript module to handle user ajax actions.\n *\n * @module block_recentlyaccesseditems/repository\n * @copyright 2018 Victor Deniz \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(['core/ajax'], function(Ajax) {\n\n /**\n * Get the list of items that the user has most recently accessed.\n *\n * @method getRecentItems\n * @param {int} limit Only return this many results\n * @return {promise} Resolved with an array of items\n */\n var getRecentItems = function(limit) {\n var args = {};\n if (typeof limit !== 'undefined') {\n args.limit = limit;\n }\n var request = {\n methodname: 'block_recentlyaccesseditems_get_recent_items',\n args: args\n };\n return Ajax.call([request])[0];\n };\n return {\n getRecentItems: getRecentItems\n };\n});"],"names":["define","Ajax","getRecentItems","limit","args","request","methodname","call"],"mappings":";;;;;;;AAsBAA,gDAAO,CAAC,cAAc,SAASC,YAoBpB,CACHC,eAZiB,SAASC,WACtBC,KAAO,QACU,IAAVD,QACPC,KAAKD,MAAQA,WAEbE,QAAU,CACVC,WAAY,+CACZF,KAAMA,aAEHH,KAAKM,KAAK,CAACF,UAAU"}
\ No newline at end of file
+{"version":3,"file":"repository.min.js","sources":["../src/repository.js"],"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 * A javascript module to handle user ajax actions.\n *\n * @module block_recentlyaccesseditems/repository\n * @copyright 2018 Victor Deniz \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(['core/ajax'], function(Ajax) {\n\n /**\n * Get the list of items that the user has most recently accessed.\n *\n * @method getRecentItems\n * @param {int} limit Only return this many results\n * @return {promise} Resolved with an array of items\n */\n var getRecentItems = function(limit) {\n var args = {};\n if (typeof limit !== 'undefined') {\n args.limit = limit;\n }\n var request = {\n methodname: 'block_recentlyaccesseditems_get_recent_items',\n args: args\n };\n return Ajax.call([request])[0];\n };\n return {\n getRecentItems: getRecentItems\n };\n});"],"names":["define","Ajax","getRecentItems","limit","args","request","methodname","call"],"mappings":";;;;;;;AAsBAA,gDAAO,CAAC,cAAc,SAASC,MAoB3B,MAAO,CACHC,eAZiB,SAASC,OAC1B,IAAIC,KAAO,CAAA,OACU,IAAVD,QACPC,KAAKD,MAAQA,OAEjB,IAAIE,QAAU,CACVC,WAAY,+CACZF,KAAMA,MAEV,OAAOH,KAAKM,KAAK,CAACF,UAAU,IAKpC"}
\ No newline at end of file
diff --git a/blocks/settings/amd/build/settingsblock.min.js.map b/blocks/settings/amd/build/settingsblock.min.js.map
index 50d99b7282f..9ebc5c42d46 100644
--- a/blocks/settings/amd/build/settingsblock.min.js.map
+++ b/blocks/settings/amd/build/settingsblock.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"settingsblock.min.js","sources":["../src/settingsblock.js"],"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 * Load the settings block tree javscript\n *\n * @module block_settings/settingsblock\n * @copyright 2015 John Okely \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport {notifyBlockContentUpdated} from 'core_block/events';\nimport Tree from 'core/tree';\n\nexport const init = (instanceId, siteAdminNodeId) => {\n const adminTree = new Tree(\".block_settings .block_tree\");\n const blockNode = document.querySelector(`[data-instance-id=\"${instanceId}\"]`);\n\n if (siteAdminNodeId) {\n const siteAdminLink = adminTree.treeRoot.get(0).querySelector(`#${siteAdminNodeId} a`);\n const newContainer = document.createElement('span');\n newContainer.setAttribute('tabindex', '0');\n siteAdminLink.childNodes.forEach(node => newContainer.appendChild(node));\n siteAdminLink.replaceWith(newContainer);\n }\n\n /**\n * The method to call when then the navtree finishes expanding a group.\n *\n * @method finishExpandingGroup\n * @param {Object} item\n * @fires event:blockContentUpdated\n */\n adminTree.finishExpandingGroup = function(item) {\n Tree.prototype.finishExpandingGroup.call(adminTree, item);\n notifyBlockContentUpdated(blockNode);\n };\n\n /**\n * The method to call whe then the navtree collapses a group\n *\n * @method collapseGroup\n * @param {Object} item\n * @fires event:blockContentUpdated\n */\n adminTree.collapseGroup = function(item) {\n Tree.prototype.collapseGroup.call(adminTree, item);\n notifyBlockContentUpdated(blockNode);\n };\n};\n"],"names":["instanceId","siteAdminNodeId","adminTree","Tree","blockNode","document","querySelector","siteAdminLink","treeRoot","get","newContainer","createElement","setAttribute","childNodes","forEach","node","appendChild","replaceWith","finishExpandingGroup","item","prototype","call","collapseGroup"],"mappings":";;;;;;;oJAyBoB,CAACA,WAAYC,yBACvBC,UAAY,IAAIC,cAAK,+BACrBC,UAAYC,SAASC,2CAAoCN,qBAE3DC,gBAAiB,OACXM,cAAgBL,UAAUM,SAASC,IAAI,GAAGH,yBAAkBL,uBAC5DS,aAAeL,SAASM,cAAc,QAC5CD,aAAaE,aAAa,WAAY,KACtCL,cAAcM,WAAWC,SAAQC,MAAQL,aAAaM,YAAYD,QAClER,cAAcU,YAAYP,cAU9BR,UAAUgB,qBAAuB,SAASC,oBACjCC,UAAUF,qBAAqBG,KAAKnB,UAAWiB,4CAC1Bf,YAU9BF,UAAUoB,cAAgB,SAASH,oBAC1BC,UAAUE,cAAcD,KAAKnB,UAAWiB,4CACnBf"}
\ No newline at end of file
+{"version":3,"file":"settingsblock.min.js","sources":["../src/settingsblock.js"],"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 * Load the settings block tree javscript\n *\n * @module block_settings/settingsblock\n * @copyright 2015 John Okely \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport {notifyBlockContentUpdated} from 'core_block/events';\nimport Tree from 'core/tree';\n\nexport const init = (instanceId, siteAdminNodeId) => {\n const adminTree = new Tree(\".block_settings .block_tree\");\n const blockNode = document.querySelector(`[data-instance-id=\"${instanceId}\"]`);\n\n if (siteAdminNodeId) {\n const siteAdminLink = adminTree.treeRoot.get(0).querySelector(`#${siteAdminNodeId} a`);\n const newContainer = document.createElement('span');\n newContainer.setAttribute('tabindex', '0');\n siteAdminLink.childNodes.forEach(node => newContainer.appendChild(node));\n siteAdminLink.replaceWith(newContainer);\n }\n\n /**\n * The method to call when then the navtree finishes expanding a group.\n *\n * @method finishExpandingGroup\n * @param {Object} item\n * @fires event:blockContentUpdated\n */\n adminTree.finishExpandingGroup = function(item) {\n Tree.prototype.finishExpandingGroup.call(adminTree, item);\n notifyBlockContentUpdated(blockNode);\n };\n\n /**\n * The method to call whe then the navtree collapses a group\n *\n * @method collapseGroup\n * @param {Object} item\n * @fires event:blockContentUpdated\n */\n adminTree.collapseGroup = function(item) {\n Tree.prototype.collapseGroup.call(adminTree, item);\n notifyBlockContentUpdated(blockNode);\n };\n};\n"],"names":["obj","_tree","__esModule","default","_exports","init","instanceId","siteAdminNodeId","adminTree","Tree","blockNode","document","querySelector","concat","siteAdminLink","treeRoot","get","newContainer","createElement","setAttribute","childNodes","forEach","node","appendChild","replaceWith","finishExpandingGroup","item","prototype","call","notifyBlockContentUpdated","collapseGroup"],"mappings":"oHAuB6B,IAAAA;;;;;;;kFAA7BC,OAA6BD,IAA7BC,QAA6BD,IAAAE,WAAAF,IAAAG,CAAAA,QAAAH,KAqC3BI,SAAAC,KAnCkBA,CAACC,WAAYC,mBAC7B,MAAMC,UAAY,IAAIC,MAAIN,QAAC,+BACrBO,UAAYC,SAASC,cAAaC,sBAAAA,OAAuBP,WAAe,OAE9E,GAAIC,gBAAiB,CACjB,MAAMO,cAAgBN,UAAUO,SAASC,IAAI,GAAGJ,cAAa,IAAAC,OAAKN,gBAAoB,OAChFU,aAAeN,SAASO,cAAc,QAC5CD,aAAaE,aAAa,WAAY,KACtCL,cAAcM,WAAWC,SAAQC,MAAQL,aAAaM,YAAYD,QAClER,cAAcU,YAAYP,aAC9B,CASAT,UAAUiB,qBAAuB,SAASC,MACtCjB,MAAIN,QAACwB,UAAUF,qBAAqBG,KAAKpB,UAAWkB,OACpD,EAAAG,QAAAA,2BAA0BnB,YAU9BF,UAAUsB,cAAgB,SAASJ,MAC/BjB,MAAIN,QAACwB,UAAUG,cAAcF,KAAKpB,UAAWkB,OAC7C,EAAAG,QAAAA,2BAA0BnB,WAC7B,CACH"}
\ No newline at end of file
diff --git a/blocks/starredcourses/amd/build/main.min.js.map b/blocks/starredcourses/amd/build/main.min.js.map
index 6f4db1059de..a5327e52fed 100644
--- a/blocks/starredcourses/amd/build/main.min.js.map
+++ b/blocks/starredcourses/amd/build/main.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"main.min.js","sources":["../src/main.js"],"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 to initialise the starred courses block.\n *\n * @module block_starredcourses/main\n * @copyright 2018 Simey Lameze \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\ndefine(\n[\n 'jquery',\n 'core/notification',\n 'block_starredcourses/repository',\n 'core/pubsub',\n 'core/templates',\n 'core_course/events'\n],\nfunction(\n $,\n Notification,\n Repository,\n PubSub,\n Templates,\n CourseEvents\n) {\n\n var SELECTORS = {\n BLOCK_CONTAINER: '[data-region=\"starred-courses\"]',\n STARRED_COURSES_REGION_VIEW: '[data-region=\"starred-courses-view\"]',\n STARRED_COURSES_REGION: '[data-region=\"starred-courses-view-content\"]'\n };\n\n /**\n * Render the starred courses.\n *\n * @method renderCourses\n * @param {object} root The root element for the starred view.\n * @param {array} courses containing array of returned courses.\n * @returns {promise} Resolved with HTML and JS strings\n */\n var renderCourses = function(root, courses) {\n if (courses.length > 0) {\n return Templates.render('core_course/view-cards', {\n courses: courses\n });\n } else {\n var nocoursesimg = root.find(SELECTORS.STARRED_COURSES_REGION_VIEW).attr('data-nocoursesimg');\n return Templates.render('block_starredcourses/no-courses', {\n nocoursesimg: nocoursesimg\n });\n }\n };\n\n /**\n * Fetch user's starred courses and reload the content of the block.\n *\n * @param {object} root The root element for the starred view.\n * @returns {promise} The updated content for the block.\n */\n var reloadContent = function(root) {\n var content = root.find(SELECTORS.STARRED_COURSES_REGION);\n\n var args = {\n limit: 0,\n offset: 0,\n };\n\n return Repository.getStarredCourses(args)\n .then(function(courses) {\n // Whether the course category should be displayed in the course item.\n var showcoursecategory = $(SELECTORS.BLOCK_CONTAINER).data('displaycoursecategory');\n courses = courses.map(function(course) {\n course.showcoursecategory = showcoursecategory;\n return course;\n });\n return renderCourses(root, courses);\n }).then(function(html, js) {\n return Templates.replaceNodeContents(content, html, js);\n }).catch(Notification.exception);\n };\n\n /**\n * Register event listeners for the block.\n *\n * @param {object} root The calendar root element\n */\n var registerEventListeners = function(root) {\n PubSub.subscribe(CourseEvents.favourited, function() {\n reloadContent(root);\n });\n\n PubSub.subscribe(CourseEvents.unfavorited, function() {\n reloadContent(root);\n });\n };\n\n /**\n * Initialise all of the modules for the starred courses block.\n *\n * @param {object} root The root element for the block.\n */\n var init = function(root) {\n root = $(root);\n\n registerEventListeners(root);\n reloadContent(root);\n };\n\n return {\n init: init\n };\n});\n"],"names":["define","$","Notification","Repository","PubSub","Templates","CourseEvents","SELECTORS","reloadContent","root","content","find","getStarredCourses","limit","offset","then","courses","showcoursecategory","data","map","course","length","render","nocoursesimg","attr","renderCourses","html","js","replaceNodeContents","catch","exception","init","subscribe","favourited","unfavorited","registerEventListeners"],"mappings":";;;;;;;AAuBAA,mCACA,CACI,SACA,oBACA,kCACA,cACA,iBACA,uBAEJ,SACIC,EACAC,aACAC,WACAC,OACAC,UACAC,kBAGIC,0BACiB,kCADjBA,sCAE6B,uCAF7BA,iCAGwB,+CA8BxBC,cAAgB,SAASC,UACrBC,QAAUD,KAAKE,KAAKJ,yCAOjBJ,WAAWS,kBALP,CACPC,MAAO,EACPC,OAAQ,IAIPC,MAAK,SAASC,aAEPC,mBAAqBhB,EAAEM,2BAA2BW,KAAK,gCAC3DF,QAAUA,QAAQG,KAAI,SAASC,eAC3BA,OAAOH,mBAAqBA,mBACrBG,UAjCH,SAASX,KAAMO,YAC3BA,QAAQK,OAAS,SACVhB,UAAUiB,OAAO,yBAA0B,CAC9CN,QAASA,cAGTO,aAAed,KAAKE,KAAKJ,uCAAuCiB,KAAK,4BAClEnB,UAAUiB,OAAO,kCAAmC,CACvDC,aAAcA,eA2BPE,CAAchB,KAAMO,YAC5BD,MAAK,SAASW,KAAMC,WACZtB,UAAUuB,oBAAoBlB,QAASgB,KAAMC,OACrDE,MAAM3B,aAAa4B,kBA8BvB,CACHC,KARO,SAAStB,OAfS,SAASA,MAClCL,OAAO4B,UAAU1B,aAAa2B,YAAY,WACtCzB,cAAcC,SAGlBL,OAAO4B,UAAU1B,aAAa4B,aAAa,WACvC1B,cAAcC,UAYlB0B,CAFA1B,KAAOR,EAAEQ,OAGTD,cAAcC"}
\ No newline at end of file
+{"version":3,"file":"main.min.js","sources":["../src/main.js"],"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 to initialise the starred courses block.\n *\n * @module block_starredcourses/main\n * @copyright 2018 Simey Lameze \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\ndefine(\n[\n 'jquery',\n 'core/notification',\n 'block_starredcourses/repository',\n 'core/pubsub',\n 'core/templates',\n 'core_course/events'\n],\nfunction(\n $,\n Notification,\n Repository,\n PubSub,\n Templates,\n CourseEvents\n) {\n\n var SELECTORS = {\n BLOCK_CONTAINER: '[data-region=\"starred-courses\"]',\n STARRED_COURSES_REGION_VIEW: '[data-region=\"starred-courses-view\"]',\n STARRED_COURSES_REGION: '[data-region=\"starred-courses-view-content\"]'\n };\n\n /**\n * Render the starred courses.\n *\n * @method renderCourses\n * @param {object} root The root element for the starred view.\n * @param {array} courses containing array of returned courses.\n * @returns {promise} Resolved with HTML and JS strings\n */\n var renderCourses = function(root, courses) {\n if (courses.length > 0) {\n return Templates.render('core_course/view-cards', {\n courses: courses\n });\n } else {\n var nocoursesimg = root.find(SELECTORS.STARRED_COURSES_REGION_VIEW).attr('data-nocoursesimg');\n return Templates.render('block_starredcourses/no-courses', {\n nocoursesimg: nocoursesimg\n });\n }\n };\n\n /**\n * Fetch user's starred courses and reload the content of the block.\n *\n * @param {object} root The root element for the starred view.\n * @returns {promise} The updated content for the block.\n */\n var reloadContent = function(root) {\n var content = root.find(SELECTORS.STARRED_COURSES_REGION);\n\n var args = {\n limit: 0,\n offset: 0,\n };\n\n return Repository.getStarredCourses(args)\n .then(function(courses) {\n // Whether the course category should be displayed in the course item.\n var showcoursecategory = $(SELECTORS.BLOCK_CONTAINER).data('displaycoursecategory');\n courses = courses.map(function(course) {\n course.showcoursecategory = showcoursecategory;\n return course;\n });\n return renderCourses(root, courses);\n }).then(function(html, js) {\n return Templates.replaceNodeContents(content, html, js);\n }).catch(Notification.exception);\n };\n\n /**\n * Register event listeners for the block.\n *\n * @param {object} root The calendar root element\n */\n var registerEventListeners = function(root) {\n PubSub.subscribe(CourseEvents.favourited, function() {\n reloadContent(root);\n });\n\n PubSub.subscribe(CourseEvents.unfavorited, function() {\n reloadContent(root);\n });\n };\n\n /**\n * Initialise all of the modules for the starred courses block.\n *\n * @param {object} root The root element for the block.\n */\n var init = function(root) {\n root = $(root);\n\n registerEventListeners(root);\n reloadContent(root);\n };\n\n return {\n init: init\n };\n});\n"],"names":["define","$","Notification","Repository","PubSub","Templates","CourseEvents","SELECTORS","reloadContent","root","content","find","getStarredCourses","limit","offset","then","courses","showcoursecategory","data","map","course","length","render","nocoursesimg","attr","renderCourses","html","js","replaceNodeContents","catch","exception","init","subscribe","favourited","unfavorited","registerEventListeners"],"mappings":";;;;;;;AAuBAA,OAAM,4BACN,CACI,SACA,oBACA,kCACA,cACA,iBACA,uBAEJ,SACIC,EACAC,aACAC,WACAC,OACAC,UACAC,cAGA,IAAIC,0BACiB,kCADjBA,sCAE6B,uCAF7BA,iCAGwB,+CA8BxBC,cAAgB,SAASC,MACzB,IAAIC,QAAUD,KAAKE,KAAKJ,kCAOxB,OAAOJ,WAAWS,kBALP,CACPC,MAAO,EACPC,OAAQ,IAIPC,MAAK,SAASC,SAEX,IAAIC,mBAAqBhB,EAAEM,2BAA2BW,KAAK,yBAK3D,OAJAF,QAAUA,QAAQG,KAAI,SAASC,QAE3B,OADAA,OAAOH,mBAAqBA,mBACrBG,MACX,IAlCQ,SAASX,KAAMO,SAC/B,GAAIA,QAAQK,OAAS,EACjB,OAAOhB,UAAUiB,OAAO,yBAA0B,CAC9CN,QAASA,UAGb,IAAIO,aAAed,KAAKE,KAAKJ,uCAAuCiB,KAAK,qBACzE,OAAOnB,UAAUiB,OAAO,kCAAmC,CACvDC,aAAcA,eA2BPE,CAAchB,KAAMO,QAC9B,IAAED,MAAK,SAASW,KAAMC,IACnB,OAAOtB,UAAUuB,oBAAoBlB,QAASgB,KAAMC,GACvD,IAAEE,MAAM3B,aAAa4B,YA8B9B,MAAO,CACHC,KARO,SAAStB,OAfS,SAASA,MAClCL,OAAO4B,UAAU1B,aAAa2B,YAAY,WACtCzB,cAAcC,KAClB,IAEAL,OAAO4B,UAAU1B,aAAa4B,aAAa,WACvC1B,cAAcC,KAClB,KAWA0B,CAFA1B,KAAOR,EAAEQ,OAGTD,cAAcC,OAMtB"}
\ No newline at end of file
diff --git a/blocks/starredcourses/amd/build/repository.min.js.map b/blocks/starredcourses/amd/build/repository.min.js.map
index e2d69161fdf..e0038d3b2ea 100644
--- a/blocks/starredcourses/amd/build/repository.min.js.map
+++ b/blocks/starredcourses/amd/build/repository.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"repository.min.js","sources":["../src/repository.js"],"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 * A javascript module to retrieve user's starred courses.\n *\n * @module block_starredcourses/repository\n * @copyright 2018 Simey Lameze \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(['jquery', 'core/ajax', 'core/notification'], function($, Ajax, Notification) {\n\n /**\n * Retrieve a list of starred courses.\n *\n * Valid args are:\n * int limit number of records to retrieve\n * int offset the offset of records to retrieve\n *\n * @method getStarredCourses\n * @param {object} args The request arguments\n * @return {promise} Resolved with an array of courses\n */\n var getStarredCourses = function(args) {\n\n var request = {\n methodname: 'block_starredcourses_get_starred_courses',\n args: args\n };\n\n var promise = Ajax.call([request])[0];\n\n promise.fail(Notification.exception);\n\n return promise;\n };\n\n return {\n getStarredCourses: getStarredCourses\n };\n});\n"],"names":["define","$","Ajax","Notification","getStarredCourses","args","request","methodname","promise","call","fail","exception"],"mappings":";;;;;;;AAsBAA,yCAAO,CAAC,SAAU,YAAa,sBAAsB,SAASC,EAAGC,KAAMC,oBA2B5D,CACHC,kBAfoB,SAASC,UAEzBC,QAAU,CACVC,WAAY,2CACZF,KAAMA,MAGNG,QAAUN,KAAKO,KAAK,CAACH,UAAU,UAEnCE,QAAQE,KAAKP,aAAaQ,WAEnBH"}
\ No newline at end of file
+{"version":3,"file":"repository.min.js","sources":["../src/repository.js"],"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 * A javascript module to retrieve user's starred courses.\n *\n * @module block_starredcourses/repository\n * @copyright 2018 Simey Lameze \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(['jquery', 'core/ajax', 'core/notification'], function($, Ajax, Notification) {\n\n /**\n * Retrieve a list of starred courses.\n *\n * Valid args are:\n * int limit number of records to retrieve\n * int offset the offset of records to retrieve\n *\n * @method getStarredCourses\n * @param {object} args The request arguments\n * @return {promise} Resolved with an array of courses\n */\n var getStarredCourses = function(args) {\n\n var request = {\n methodname: 'block_starredcourses_get_starred_courses',\n args: args\n };\n\n var promise = Ajax.call([request])[0];\n\n promise.fail(Notification.exception);\n\n return promise;\n };\n\n return {\n getStarredCourses: getStarredCourses\n };\n});\n"],"names":["define","$","Ajax","Notification","getStarredCourses","args","request","methodname","promise","call","fail","exception"],"mappings":";;;;;;;AAsBAA,OAAO,kCAAA,CAAC,SAAU,YAAa,sBAAsB,SAASC,EAAGC,KAAMC,cA2BnE,MAAO,CACHC,kBAfoB,SAASC,MAE7B,IAAIC,QAAU,CACVC,WAAY,2CACZF,KAAMA,MAGNG,QAAUN,KAAKO,KAAK,CAACH,UAAU,GAInC,OAFAE,QAAQE,KAAKP,aAAaQ,WAEnBH,SAMf"}
\ No newline at end of file
diff --git a/blocks/timeline/amd/build/calendar_events_repository.min.js.map b/blocks/timeline/amd/build/calendar_events_repository.min.js.map
index f8dfa0e612e..543d1ce2ce3 100644
--- a/blocks/timeline/amd/build/calendar_events_repository.min.js.map
+++ b/blocks/timeline/amd/build/calendar_events_repository.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"calendar_events_repository.min.js","sources":["../src/calendar_events_repository.js"],"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 * A javascript module to retrieve calendar events from the server.\n *\n * @module block_timeline/calendar_events_repository\n * @copyright 2018 Ryan Wyllie \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(['jquery', 'core/ajax', 'core/notification'], function($, Ajax, Notification) {\n\n var DEFAULT_LIMIT = 20;\n\n /**\n * Retrieve a list of calendar events for the logged in user for the\n * given course.\n *\n * Valid args are:\n * int courseid Only get events for this course\n * int starttime Only get events after this time\n * int endtime Only get events before this time\n * int limit Limit the number of results returned\n * int aftereventid Offset the result set from the given id\n *\n * @method queryByCourse\n * @param {object} args The request arguments\n * @return {promise} Resolved with an array of the calendar events\n */\n var queryByCourse = function(args) {\n if (!args.hasOwnProperty('limit')) {\n args.limit = DEFAULT_LIMIT;\n }\n\n args.limitnum = args.limit;\n delete args.limit;\n\n if (args.hasOwnProperty('starttime')) {\n args.timesortfrom = args.starttime;\n delete args.starttime;\n }\n\n if (args.hasOwnProperty('endtime')) {\n args.timesortto = args.endtime;\n delete args.endtime;\n }\n\n var request = {\n methodname: 'core_calendar_get_action_events_by_course',\n args: args\n };\n\n var promise = Ajax.call([request])[0];\n\n promise.fail(Notification.exception);\n\n return promise;\n };\n\n /**\n * Retrieve a list of calendar events for the given courses for the\n * logged in user.\n *\n * Valid args are:\n * array courseids Get events for these courses\n * int starttime Only get events after this time\n * int endtime Only get events before this time\n * int limit Limit the number of results returned\n *\n * @method queryByCourses\n * @param {object} args The request arguments\n * @return {promise} Resolved with an array of the calendar events\n */\n var queryByCourses = function(args) {\n if (!args.hasOwnProperty('limit')) {\n // This is intentionally smaller than the default limit.\n args.limit = 10;\n }\n\n args.limitnum = args.limit;\n delete args.limit;\n\n if (args.hasOwnProperty('starttime')) {\n args.timesortfrom = args.starttime;\n delete args.starttime;\n }\n\n if (args.hasOwnProperty('endtime')) {\n args.timesortto = args.endtime;\n delete args.endtime;\n }\n\n var request = {\n methodname: 'core_calendar_get_action_events_by_courses',\n args: args\n };\n\n var promise = Ajax.call([request])[0];\n\n promise.fail(Notification.exception);\n\n return promise;\n };\n\n /**\n * Retrieve a list of calendar events for the logged in user after the given\n * time.\n *\n * Valid args are:\n * int starttime Only get events after this time\n * int endtime Only get events before this time\n * int limit Limit the number of results returned\n * int aftereventid Offset the result set from the given id\n *\n * @method queryByTime\n * @param {object} args The request arguments\n * @return {promise} Resolved with an array of the calendar events\n */\n var queryByTime = function(args) {\n if (!args.hasOwnProperty('limit')) {\n args.limit = DEFAULT_LIMIT;\n }\n\n args.limitnum = args.limit;\n delete args.limit;\n\n if (args.hasOwnProperty('starttime')) {\n args.timesortfrom = args.starttime;\n delete args.starttime;\n }\n\n if (args.hasOwnProperty('endtime')) {\n args.timesortto = args.endtime;\n delete args.endtime;\n }\n // Don't show events related to courses that the user is suspended in.\n args.limittononsuspendedevents = true;\n\n var request = {\n methodname: 'core_calendar_get_action_events_by_timesort',\n args: args\n };\n\n var promise = Ajax.call([request])[0];\n\n promise.fail(Notification.exception);\n\n return promise;\n };\n\n return {\n queryByTime: queryByTime,\n queryByCourse: queryByCourse,\n queryByCourses: queryByCourses,\n };\n});\n"],"names":["define","$","Ajax","Notification","queryByTime","args","hasOwnProperty","limit","limitnum","timesortfrom","starttime","timesortto","endtime","limittononsuspendedevents","request","methodname","promise","call","fail","exception","queryByCourse","queryByCourses"],"mappings":";;;;;;;AAsBAA,mDAAO,CAAC,SAAU,YAAa,sBAAsB,SAASC,EAAGC,KAAMC,oBA4I5D,CACHC,YAjCc,SAASC,MAClBA,KAAKC,eAAe,WACrBD,KAAKE,MA5GO,IA+GhBF,KAAKG,SAAWH,KAAKE,aACdF,KAAKE,MAERF,KAAKC,eAAe,eACpBD,KAAKI,aAAeJ,KAAKK,iBAClBL,KAAKK,WAGZL,KAAKC,eAAe,aACpBD,KAAKM,WAAaN,KAAKO,eAChBP,KAAKO,SAGhBP,KAAKQ,2BAA4B,MAE7BC,QAAU,CACVC,WAAY,8CACZV,KAAMA,MAGNW,QAAUd,KAAKe,KAAK,CAACH,UAAU,UAEnCE,QAAQE,KAAKf,aAAagB,WAEnBH,SAKPI,cA3HgB,SAASf,MACpBA,KAAKC,eAAe,WACrBD,KAAKE,MAnBO,IAsBhBF,KAAKG,SAAWH,KAAKE,aACdF,KAAKE,MAERF,KAAKC,eAAe,eACpBD,KAAKI,aAAeJ,KAAKK,iBAClBL,KAAKK,WAGZL,KAAKC,eAAe,aACpBD,KAAKM,WAAaN,KAAKO,eAChBP,KAAKO,aAGZE,QAAU,CACVC,WAAY,4CACZV,KAAMA,MAGNW,QAAUd,KAAKe,KAAK,CAACH,UAAU,UAEnCE,QAAQE,KAAKf,aAAagB,WAEnBH,SAiGPK,eAhFiB,SAAShB,MACrBA,KAAKC,eAAe,WAErBD,KAAKE,MAAQ,IAGjBF,KAAKG,SAAWH,KAAKE,aACdF,KAAKE,MAERF,KAAKC,eAAe,eACpBD,KAAKI,aAAeJ,KAAKK,iBAClBL,KAAKK,WAGZL,KAAKC,eAAe,aACpBD,KAAKM,WAAaN,KAAKO,eAChBP,KAAKO,aAGZE,QAAU,CACVC,WAAY,6CACZV,KAAMA,MAGNW,QAAUd,KAAKe,KAAK,CAACH,UAAU,UAEnCE,QAAQE,KAAKf,aAAagB,WAEnBH"}
\ No newline at end of file
+{"version":3,"file":"calendar_events_repository.min.js","sources":["../src/calendar_events_repository.js"],"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 * A javascript module to retrieve calendar events from the server.\n *\n * @module block_timeline/calendar_events_repository\n * @copyright 2018 Ryan Wyllie \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(['jquery', 'core/ajax', 'core/notification'], function($, Ajax, Notification) {\n\n var DEFAULT_LIMIT = 20;\n\n /**\n * Retrieve a list of calendar events for the logged in user for the\n * given course.\n *\n * Valid args are:\n * int courseid Only get events for this course\n * int starttime Only get events after this time\n * int endtime Only get events before this time\n * int limit Limit the number of results returned\n * int aftereventid Offset the result set from the given id\n *\n * @method queryByCourse\n * @param {object} args The request arguments\n * @return {promise} Resolved with an array of the calendar events\n */\n var queryByCourse = function(args) {\n if (!args.hasOwnProperty('limit')) {\n args.limit = DEFAULT_LIMIT;\n }\n\n args.limitnum = args.limit;\n delete args.limit;\n\n if (args.hasOwnProperty('starttime')) {\n args.timesortfrom = args.starttime;\n delete args.starttime;\n }\n\n if (args.hasOwnProperty('endtime')) {\n args.timesortto = args.endtime;\n delete args.endtime;\n }\n\n var request = {\n methodname: 'core_calendar_get_action_events_by_course',\n args: args\n };\n\n var promise = Ajax.call([request])[0];\n\n promise.fail(Notification.exception);\n\n return promise;\n };\n\n /**\n * Retrieve a list of calendar events for the given courses for the\n * logged in user.\n *\n * Valid args are:\n * array courseids Get events for these courses\n * int starttime Only get events after this time\n * int endtime Only get events before this time\n * int limit Limit the number of results returned\n *\n * @method queryByCourses\n * @param {object} args The request arguments\n * @return {promise} Resolved with an array of the calendar events\n */\n var queryByCourses = function(args) {\n if (!args.hasOwnProperty('limit')) {\n // This is intentionally smaller than the default limit.\n args.limit = 10;\n }\n\n args.limitnum = args.limit;\n delete args.limit;\n\n if (args.hasOwnProperty('starttime')) {\n args.timesortfrom = args.starttime;\n delete args.starttime;\n }\n\n if (args.hasOwnProperty('endtime')) {\n args.timesortto = args.endtime;\n delete args.endtime;\n }\n\n var request = {\n methodname: 'core_calendar_get_action_events_by_courses',\n args: args\n };\n\n var promise = Ajax.call([request])[0];\n\n promise.fail(Notification.exception);\n\n return promise;\n };\n\n /**\n * Retrieve a list of calendar events for the logged in user after the given\n * time.\n *\n * Valid args are:\n * int starttime Only get events after this time\n * int endtime Only get events before this time\n * int limit Limit the number of results returned\n * int aftereventid Offset the result set from the given id\n *\n * @method queryByTime\n * @param {object} args The request arguments\n * @return {promise} Resolved with an array of the calendar events\n */\n var queryByTime = function(args) {\n if (!args.hasOwnProperty('limit')) {\n args.limit = DEFAULT_LIMIT;\n }\n\n args.limitnum = args.limit;\n delete args.limit;\n\n if (args.hasOwnProperty('starttime')) {\n args.timesortfrom = args.starttime;\n delete args.starttime;\n }\n\n if (args.hasOwnProperty('endtime')) {\n args.timesortto = args.endtime;\n delete args.endtime;\n }\n // Don't show events related to courses that the user is suspended in.\n args.limittononsuspendedevents = true;\n\n var request = {\n methodname: 'core_calendar_get_action_events_by_timesort',\n args: args\n };\n\n var promise = Ajax.call([request])[0];\n\n promise.fail(Notification.exception);\n\n return promise;\n };\n\n return {\n queryByTime: queryByTime,\n queryByCourse: queryByCourse,\n queryByCourses: queryByCourses,\n };\n});\n"],"names":["define","$","Ajax","Notification","queryByTime","args","hasOwnProperty","limit","limitnum","timesortfrom","starttime","timesortto","endtime","limittononsuspendedevents","request","methodname","promise","call","fail","exception","queryByCourse","queryByCourses"],"mappings":";;;;;;;AAsBAA,OAAO,4CAAA,CAAC,SAAU,YAAa,sBAAsB,SAASC,EAAGC,KAAMC,cA4InE,MAAO,CACHC,YAjCc,SAASC,MAClBA,KAAKC,eAAe,WACrBD,KAAKE,MA5GO,IA+GhBF,KAAKG,SAAWH,KAAKE,aACdF,KAAKE,MAERF,KAAKC,eAAe,eACpBD,KAAKI,aAAeJ,KAAKK,iBAClBL,KAAKK,WAGZL,KAAKC,eAAe,aACpBD,KAAKM,WAAaN,KAAKO,eAChBP,KAAKO,SAGhBP,KAAKQ,2BAA4B,EAEjC,IAAIC,QAAU,CACVC,WAAY,8CACZV,KAAMA,MAGNW,QAAUd,KAAKe,KAAK,CAACH,UAAU,GAInC,OAFAE,QAAQE,KAAKf,aAAagB,WAEnBH,SAKPI,cA3HgB,SAASf,MACpBA,KAAKC,eAAe,WACrBD,KAAKE,MAnBO,IAsBhBF,KAAKG,SAAWH,KAAKE,aACdF,KAAKE,MAERF,KAAKC,eAAe,eACpBD,KAAKI,aAAeJ,KAAKK,iBAClBL,KAAKK,WAGZL,KAAKC,eAAe,aACpBD,KAAKM,WAAaN,KAAKO,eAChBP,KAAKO,SAGhB,IAAIE,QAAU,CACVC,WAAY,4CACZV,KAAMA,MAGNW,QAAUd,KAAKe,KAAK,CAACH,UAAU,GAInC,OAFAE,QAAQE,KAAKf,aAAagB,WAEnBH,SAiGPK,eAhFiB,SAAShB,MACrBA,KAAKC,eAAe,WAErBD,KAAKE,MAAQ,IAGjBF,KAAKG,SAAWH,KAAKE,aACdF,KAAKE,MAERF,KAAKC,eAAe,eACpBD,KAAKI,aAAeJ,KAAKK,iBAClBL,KAAKK,WAGZL,KAAKC,eAAe,aACpBD,KAAKM,WAAaN,KAAKO,eAChBP,KAAKO,SAGhB,IAAIE,QAAU,CACVC,WAAY,6CACZV,KAAMA,MAGNW,QAAUd,KAAKe,KAAK,CAACH,UAAU,GAInC,OAFAE,QAAQE,KAAKf,aAAagB,WAEnBH,SAsDf"}
\ No newline at end of file
diff --git a/blocks/timeline/amd/build/event_list.min.js b/blocks/timeline/amd/build/event_list.min.js
index 6027347a307..841a87ef8b3 100644
--- a/blocks/timeline/amd/build/event_list.min.js
+++ b/blocks/timeline/amd/build/event_list.min.js
@@ -6,6 +6,6 @@
* @copyright 2016 Ryan Wyllie
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
-define("block_timeline/event_list",["jquery","core/notification","core/templates","core/str","core/user_date","block_timeline/calendar_events_repository","core/pending"],(function($,Notification,Templates,Str,UserDate,CalendarEventsRepository,Pending){var courseview=!1,SELECTORS_EMPTY_MESSAGE='[data-region="no-events-empty-message"]',SELECTORS_EVENT_LIST_CONTENT='[data-region="event-list-content"]',SELECTORS_EVENT_LIST_WRAPPER='[data-region="event-list-wrapper"]',SELECTORS_EVENT_LIST_LOADING_PLACEHOLDER='[data-region="event-list-loading-placeholder"]',SELECTORS_TIMELINE_BLOCK='[data-region="timeline"]',SELECTORS_TIMELINE_SEARCH='[data-action="search"]',SELECTORS_MORE_ACTIVITIES_BUTTON='[data-action="more-events"]',SELECTORS_MORE_ACTIVITIES_BUTTON_CONTAINER='[data-region="more-events-button-container"]',TEMPLATES_EVENT_LIST_CONTENT="block_timeline/event-list-content",TEMPLATES_MORE_ACTIVITIES_BUTTON="block_timeline/event-list-loadmore",TEMPLATES_LOADING_ICON="core/loading";var hideContent=function(root){root.find(SELECTORS_EVENT_LIST_CONTENT).addClass("hidden"),root.find(SELECTORS_EMPTY_MESSAGE).removeClass("hidden")},showContent=function(root){root.find(SELECTORS_EVENT_LIST_CONTENT).removeClass("hidden"),root.find(SELECTORS_EMPTY_MESSAGE).addClass("hidden")},emptyContent=function(root){root.find(SELECTORS_EVENT_LIST_CONTENT).empty()},render=function(calendarEvents){var templateContext=function(calendarEvents){var eventsByDay={},templateContext={courseview:courseview,eventsbyday:[]};return calendarEvents.forEach((function(calendarEvent){var dayTimestamp=calendarEvent.timeusermidnight;eventsByDay[dayTimestamp]?eventsByDay[dayTimestamp].push(calendarEvent):eventsByDay[dayTimestamp]=[calendarEvent]})),Object.keys(eventsByDay).forEach((function(dayTimestamp){var events=eventsByDay[dayTimestamp];templateContext.eventsbyday.push({dayTimestamp:dayTimestamp,events:events})})),templateContext}(calendarEvents),templateName=TEMPLATES_EVENT_LIST_CONTENT;return Templates.render(templateName,templateContext)};const createLazyLoadingContent=(root,firstLoad,itemLimit,midnight,lastId,courseId,daysOffset,daysLimit,searchValue)=>loadEventsForLazyLoading(root,itemLimit,midnight,lastId,courseId,daysOffset,daysLimit,searchValue).then((data=>{if(data.calendarEvents.length){const lastEventId=data.calendarEvents.at(-1).id,lastTimeStamp=data.calendarEvents.at(-1).timeusermidnight;return firstLoad.resolve({hasContent:!0,lastId:lastEventId,lastTimeStamp:lastTimeStamp,loadedAll:data.loadedAll}),render(data.calendarEvents)}return firstLoad.resolve({hasContent:!1,lastId:0,lastTimeStamp:0,loadedAll:!0}),data.calendarEvents})).catch(Notification.exception),loadEventsForLazyLoading=(root,itemLimit,midnight,lastId,courseId,daysOffset,daysLimit,searchValue)=>{const eventsPromise=function(midnight,limit,daysOffset,daysLimit,lastId,courseId,searchValue){var endTime=null!=daysLimit&&midnight+86400*daysLimit,args={starttime:midnight+86400*daysOffset,limit:limit};return lastId&&(args.aftereventid=lastId),endTime&&(args.endtime=endTime),searchValue&&(args.searchvalue=searchValue),courseId?(args.courseid=courseId,CalendarEventsRepository.queryByCourse(args)):CalendarEventsRepository.queryByTime(args)}(midnight,itemLimit+1,daysOffset,daysLimit,lastId,courseId,searchValue);let calendarEvents=[],loadedAll=!0;return eventsPromise.then((result=>{if(!result.events.length)return{calendarEvents:calendarEvents,loadedAll:loadedAll};const overdueFilter=document.querySelector("[data-filtername='overdue']"),filterByOverdue=overdueFilter&&overdueFilter.getAttribute("aria-current");if(calendarEvents=result.events.filter((event=>{if("open"==event.eventtype||"opensubmission"==event.eventtype){return UserDate.getUserMidnightForTimestamp(event.timesort,midnight)>midnight}return!filterByOverdue||event.overdue})),loadedAll=calendarEvents.length<=itemLimit,loadedAll||calendarEvents.pop(),calendarEvents.length){const lastEventId=calendarEvents.at(-1).id;setOffset(root,lastEventId)}return{calendarEvents:calendarEvents,loadedAll:loadedAll}}))},getOffset=element=>parseInt(element.attr("data-lazyload-offset"),10),setOffset=(element,offset)=>{element.attr("data-lazyload-offset",offset)},getLastTimestamp=element=>parseInt(element.attr("data-timestamp"),10),setLastTimestamp=(element,timestamp)=>{element.attr("data-timestamp",timestamp)},disableMoreActivitiesButtonLoading=root=>{root.find(SELECTORS_MORE_ACTIVITIES_BUTTON_CONTAINER).remove()},initEventListener=root=>{root.find(SELECTORS_MORE_ACTIVITIES_BUTTON).on("click",(()=>{(root=>{const loadMoreButton=root.find(SELECTORS_MORE_ACTIVITIES_BUTTON);loadMoreButton.prop("disabled",!0),Templates.render(TEMPLATES_LOADING_ICON,{}).then((html=>(loadMoreButton.append(html),html))).catch((()=>!1))})(root),(root=>{const midnight=parseInt(root.attr("data-midnight"),10),courseId=root.attr("data-course-id"),daysOffset=parseInt(root.attr("data-days-offset"),10),daysLimit=root.attr("data-days-limit"),lastId=getOffset(root),eventListWrapper=root.find(SELECTORS_EVENT_LIST_WRAPPER),searchValue=root.closest(SELECTORS_TIMELINE_BLOCK).find(SELECTORS_TIMELINE_SEARCH).val();loadEventsForLazyLoading(root,10,midnight,lastId,courseId,daysOffset,daysLimit,searchValue).then((data=>{if(data.calendarEvents.length){const renderPromise=render(data.calendarEvents),lastTimestamp=getLastTimestamp(root);renderPromise.then(((html,js)=>((html=$(html)).find('[data-timestamp="'.concat(lastTimestamp,'"]')).remove(),Templates.appendNodeContents(eventListWrapper,html.html(),js),data.loadedAll||Templates.render(TEMPLATES_MORE_ACTIVITIES_BUTTON,{}).then((html=>(eventListWrapper.append(html),setLastTimestamp(root,data.calendarEvents.at(-1).timeusermidnight),initEventListener(root),html))).catch((()=>!1)),html))).catch(Notification.exception)}return data})).then((()=>disableMoreActivitiesButtonLoading(root))).catch(Notification.exception)})(root)}))};return{init:function(root){let additionalConfig=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};const pendingPromise=new Pending("block/timeline:event-init");root=$(root),courseview=!!additionalConfig.courseview;var firstLoad=$.Deferred(),eventListContent=root.find(SELECTORS_EVENT_LIST_CONTENT),loadingPlaceholder=root.find(SELECTORS_EVENT_LIST_LOADING_PLACEHOLDER),courseId=root.attr("data-course-id"),daysOffset=parseInt(root.attr("data-days-offset"),10),daysLimit=root.attr("data-days-limit"),midnight=parseInt(root.attr("data-midnight"),10);const searchValue=root.closest(SELECTORS_TIMELINE_BLOCK).find(SELECTORS_TIMELINE_SEARCH).val();return emptyContent(root),showContent(root),loadingPlaceholder.removeClass("hidden"),null!=daysLimit&&(daysLimit=parseInt(daysLimit,10)),createLazyLoadingContent(root,firstLoad,5,midnight,0,courseId,daysOffset,daysLimit,searchValue).then((function(html,js){return firstLoad.then((function(data){return data.hasContent?((html=$(html)).addClass("hidden"),Templates.replaceNodeContents(eventListContent,html,js),html.removeClass("hidden"),loadingPlaceholder.addClass("hidden"),data.loadedAll||Templates.render(TEMPLATES_MORE_ACTIVITIES_BUTTON,{courseview:courseview}).then((function(html){return eventListContent.append(html),setLastTimestamp(root,data.lastTimeStamp),initEventListener(root),html})).catch((function(){return!1})),data):(loadingPlaceholder.addClass("hidden"),hideContent(root))})).catch((function(){return!1})),html})).then((()=>pendingPromise.resolve())).catch(Notification.exception)},rootSelector:'[data-region="event-list-container"]'}}));
+define("block_timeline/event_list",["jquery","core/notification","core/templates","core/str","core/user_date","block_timeline/calendar_events_repository","core/pending"],(function($,Notification,Templates,Str,UserDate,CalendarEventsRepository,Pending){var courseview=!1,SELECTORS={EMPTY_MESSAGE:'[data-region="no-events-empty-message"]',ROOT:'[data-region="event-list-container"]',EVENT_LIST_CONTENT:'[data-region="event-list-content"]',EVENT_LIST_WRAPPER:'[data-region="event-list-wrapper"]',EVENT_LIST_LOADING_PLACEHOLDER:'[data-region="event-list-loading-placeholder"]',TIMELINE_BLOCK:'[data-region="timeline"]',TIMELINE_SEARCH:'[data-action="search"]',MORE_ACTIVITIES_BUTTON:'[data-action="more-events"]',MORE_ACTIVITIES_BUTTON_CONTAINER:'[data-region="more-events-button-container"]'},TEMPLATES={EVENT_LIST_CONTENT:"block_timeline/event-list-content",MORE_ACTIVITIES_BUTTON:"block_timeline/event-list-loadmore",LOADING_ICON:"core/loading"};const DEFAULT_LAZY_LOADING_ITEMS_FIRST_LOAD=5;var hideContent=function(root){root.find(SELECTORS.EVENT_LIST_CONTENT).addClass("hidden"),root.find(SELECTORS.EMPTY_MESSAGE).removeClass("hidden")},showContent=function(root){root.find(SELECTORS.EVENT_LIST_CONTENT).removeClass("hidden"),root.find(SELECTORS.EMPTY_MESSAGE).addClass("hidden")},emptyContent=function(root){root.find(SELECTORS.EVENT_LIST_CONTENT).empty()},render=function(calendarEvents){var templateContext=function(calendarEvents){var eventsByDay={},templateContext={courseview:courseview,eventsbyday:[]};return calendarEvents.forEach((function(calendarEvent){var dayTimestamp=calendarEvent.timeusermidnight;eventsByDay[dayTimestamp]?eventsByDay[dayTimestamp].push(calendarEvent):eventsByDay[dayTimestamp]=[calendarEvent]})),Object.keys(eventsByDay).forEach((function(dayTimestamp){var events=eventsByDay[dayTimestamp];templateContext.eventsbyday.push({dayTimestamp:dayTimestamp,events:events})})),templateContext}(calendarEvents),templateName=TEMPLATES.EVENT_LIST_CONTENT;return Templates.render(templateName,templateContext)};const createLazyLoadingContent=(root,firstLoad,itemLimit,midnight,lastId,courseId,daysOffset,daysLimit,searchValue)=>loadEventsForLazyLoading(root,itemLimit,midnight,lastId,courseId,daysOffset,daysLimit,searchValue).then((data=>{if(data.calendarEvents.length){const lastEventId=data.calendarEvents.at(-1).id,lastTimeStamp=data.calendarEvents.at(-1).timeusermidnight;return firstLoad.resolve({hasContent:!0,lastId:lastEventId,lastTimeStamp:lastTimeStamp,loadedAll:data.loadedAll}),render(data.calendarEvents)}return firstLoad.resolve({hasContent:!1,lastId:0,lastTimeStamp:0,loadedAll:!0}),data.calendarEvents})).catch(Notification.exception),loadEventsForLazyLoading=(root,itemLimit,midnight,lastId,courseId,daysOffset,daysLimit,searchValue)=>{const eventsPromise=function(midnight,limit,daysOffset,daysLimit,lastId,courseId,searchValue){var endTime=null!=daysLimit&&midnight+86400*daysLimit,args={starttime:midnight+86400*daysOffset,limit:limit};return lastId&&(args.aftereventid=lastId),endTime&&(args.endtime=endTime),searchValue&&(args.searchvalue=searchValue),courseId?(args.courseid=courseId,CalendarEventsRepository.queryByCourse(args)):CalendarEventsRepository.queryByTime(args)}(midnight,itemLimit+1,daysOffset,daysLimit,lastId,courseId,searchValue);let calendarEvents=[],loadedAll=!0;return eventsPromise.then((result=>{if(!result.events.length)return{calendarEvents:calendarEvents,loadedAll:loadedAll};const overdueFilter=document.querySelector("[data-filtername='overdue']"),filterByOverdue=overdueFilter&&overdueFilter.getAttribute("aria-current");if(calendarEvents=result.events.filter((event=>{if("open"==event.eventtype||"opensubmission"==event.eventtype){return UserDate.getUserMidnightForTimestamp(event.timesort,midnight)>midnight}return!filterByOverdue||event.overdue})),loadedAll=calendarEvents.length<=itemLimit,loadedAll||calendarEvents.pop(),calendarEvents.length){const lastEventId=calendarEvents.at(-1).id;setOffset(root,lastEventId)}return{calendarEvents:calendarEvents,loadedAll:loadedAll}}))},getOffset=element=>parseInt(element.attr("data-lazyload-offset"),10),setOffset=(element,offset)=>{element.attr("data-lazyload-offset",offset)},getLastTimestamp=element=>parseInt(element.attr("data-timestamp"),10),setLastTimestamp=(element,timestamp)=>{element.attr("data-timestamp",timestamp)},disableMoreActivitiesButtonLoading=root=>{root.find(SELECTORS.MORE_ACTIVITIES_BUTTON_CONTAINER).remove()},initEventListener=root=>{root.find(SELECTORS.MORE_ACTIVITIES_BUTTON).on("click",(()=>{(root=>{const loadMoreButton=root.find(SELECTORS.MORE_ACTIVITIES_BUTTON);loadMoreButton.prop("disabled",!0),Templates.render(TEMPLATES.LOADING_ICON,{}).then((html=>(loadMoreButton.append(html),html))).catch((()=>!1))})(root),(root=>{const midnight=parseInt(root.attr("data-midnight"),10),courseId=root.attr("data-course-id"),daysOffset=parseInt(root.attr("data-days-offset"),10),daysLimit=root.attr("data-days-limit"),lastId=getOffset(root),eventListWrapper=root.find(SELECTORS.EVENT_LIST_WRAPPER),searchValue=root.closest(SELECTORS.TIMELINE_BLOCK).find(SELECTORS.TIMELINE_SEARCH).val();loadEventsForLazyLoading(root,10,midnight,lastId,courseId,daysOffset,daysLimit,searchValue).then((data=>{if(data.calendarEvents.length){const renderPromise=render(data.calendarEvents),lastTimestamp=getLastTimestamp(root);renderPromise.then(((html,js)=>((html=$(html)).find('[data-timestamp="'.concat(lastTimestamp,'"]')).remove(),Templates.appendNodeContents(eventListWrapper,html.html(),js),data.loadedAll||Templates.render(TEMPLATES.MORE_ACTIVITIES_BUTTON,{}).then((html=>(eventListWrapper.append(html),setLastTimestamp(root,data.calendarEvents.at(-1).timeusermidnight),initEventListener(root),html))).catch((()=>!1)),html))).catch(Notification.exception)}return data})).then((()=>disableMoreActivitiesButtonLoading(root))).catch(Notification.exception)})(root)}))};return{init:function(root){let additionalConfig=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};const pendingPromise=new Pending("block/timeline:event-init");root=$(root),courseview=!!additionalConfig.courseview;var firstLoad=$.Deferred(),eventListContent=root.find(SELECTORS.EVENT_LIST_CONTENT),loadingPlaceholder=root.find(SELECTORS.EVENT_LIST_LOADING_PLACEHOLDER),courseId=root.attr("data-course-id"),daysOffset=parseInt(root.attr("data-days-offset"),10),daysLimit=root.attr("data-days-limit"),midnight=parseInt(root.attr("data-midnight"),10);const searchValue=root.closest(SELECTORS.TIMELINE_BLOCK).find(SELECTORS.TIMELINE_SEARCH).val();return emptyContent(root),showContent(root),loadingPlaceholder.removeClass("hidden"),null!=daysLimit&&(daysLimit=parseInt(daysLimit,10)),createLazyLoadingContent(root,firstLoad,DEFAULT_LAZY_LOADING_ITEMS_FIRST_LOAD,midnight,0,courseId,daysOffset,daysLimit,searchValue).then((function(html,js){return firstLoad.then((function(data){return data.hasContent?((html=$(html)).addClass("hidden"),Templates.replaceNodeContents(eventListContent,html,js),html.removeClass("hidden"),loadingPlaceholder.addClass("hidden"),data.loadedAll||Templates.render(TEMPLATES.MORE_ACTIVITIES_BUTTON,{courseview:courseview}).then((function(html){return eventListContent.append(html),setLastTimestamp(root,data.lastTimeStamp),initEventListener(root),html})).catch((function(){return!1})),data):(loadingPlaceholder.addClass("hidden"),hideContent(root))})).catch((function(){return!1})),html})).then((()=>pendingPromise.resolve())).catch(Notification.exception)},rootSelector:SELECTORS.ROOT}}));
//# sourceMappingURL=event_list.min.js.map
\ No newline at end of file
diff --git a/blocks/timeline/amd/build/event_list.min.js.map b/blocks/timeline/amd/build/event_list.min.js.map
index 7c831abb698..d235bb69f2c 100644
--- a/blocks/timeline/amd/build/event_list.min.js.map
+++ b/blocks/timeline/amd/build/event_list.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"event_list.min.js","sources":["../src/event_list.js"],"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 to load and render the list of calendar events for a\n * given day range.\n *\n * @module block_timeline/event_list\n * @copyright 2016 Ryan Wyllie \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(\n[\n 'jquery',\n 'core/notification',\n 'core/templates',\n 'core/str',\n 'core/user_date',\n 'block_timeline/calendar_events_repository',\n 'core/pending'\n],\nfunction(\n $,\n Notification,\n Templates,\n Str,\n UserDate,\n CalendarEventsRepository,\n Pending\n) {\n\n var SECONDS_IN_DAY = 60 * 60 * 24;\n var courseview = false;\n\n var SELECTORS = {\n EMPTY_MESSAGE: '[data-region=\"no-events-empty-message\"]',\n ROOT: '[data-region=\"event-list-container\"]',\n EVENT_LIST_CONTENT: '[data-region=\"event-list-content\"]',\n EVENT_LIST_WRAPPER: '[data-region=\"event-list-wrapper\"]',\n EVENT_LIST_LOADING_PLACEHOLDER: '[data-region=\"event-list-loading-placeholder\"]',\n TIMELINE_BLOCK: '[data-region=\"timeline\"]',\n TIMELINE_SEARCH: '[data-action=\"search\"]',\n MORE_ACTIVITIES_BUTTON: '[data-action=\"more-events\"]',\n MORE_ACTIVITIES_BUTTON_CONTAINER: '[data-region=\"more-events-button-container\"]'\n };\n\n var TEMPLATES = {\n EVENT_LIST_CONTENT: 'block_timeline/event-list-content',\n MORE_ACTIVITIES_BUTTON: 'block_timeline/event-list-loadmore',\n LOADING_ICON: 'core/loading'\n };\n\n /** @property {number} The total items will be shown on the first load. */\n const DEFAULT_LAZY_LOADING_ITEMS_FIRST_LOAD = 5;\n /** @property {number} The total items will be shown when click on the Show more activities button. */\n const DEFAULT_LAZY_LOADING_ITEMS_OTHER_LOAD = 10;\n\n /**\n * Hide the content area and display the empty content message.\n *\n * @param {object} root The container element\n */\n var hideContent = function(root) {\n root.find(SELECTORS.EVENT_LIST_CONTENT).addClass('hidden');\n root.find(SELECTORS.EMPTY_MESSAGE).removeClass('hidden');\n };\n\n /**\n * Show the content area and hide the empty content message.\n *\n * @param {object} root The container element\n */\n var showContent = function(root) {\n root.find(SELECTORS.EVENT_LIST_CONTENT).removeClass('hidden');\n root.find(SELECTORS.EMPTY_MESSAGE).addClass('hidden');\n };\n\n /**\n * Empty the content area.\n *\n * @param {object} root The container element\n */\n var emptyContent = function(root) {\n root.find(SELECTORS.EVENT_LIST_CONTENT).empty();\n };\n\n /**\n * Construct the template context from a list of calendar events. The events\n * are grouped by which day they are on. The day is calculated from the user's\n * midnight timestamp to ensure that the calculation is timezone agnostic.\n *\n * The return data structure will look like:\n * {\n * eventsbyday: [\n * {\n * dayTimestamp: 1533744000,\n * events: [\n * { ...event 1 data... },\n * { ...event 2 data... }\n * ]\n * },\n * {\n * dayTimestamp: 1533830400,\n * events: [\n * { ...event 3 data... },\n * { ...event 4 data... }\n * ]\n * }\n * ]\n * }\n *\n * Each day timestamp is the day's midnight in the user's timezone.\n *\n * @param {array} calendarEvents List of calendar events\n * @return {object}\n */\n var buildTemplateContext = function(calendarEvents) {\n var eventsByDay = {};\n var templateContext = {\n courseview,\n eventsbyday: []\n };\n\n calendarEvents.forEach(function(calendarEvent) {\n var dayTimestamp = calendarEvent.timeusermidnight;\n if (eventsByDay[dayTimestamp]) {\n eventsByDay[dayTimestamp].push(calendarEvent);\n } else {\n eventsByDay[dayTimestamp] = [calendarEvent];\n }\n });\n\n Object.keys(eventsByDay).forEach(function(dayTimestamp) {\n var events = eventsByDay[dayTimestamp];\n templateContext.eventsbyday.push({\n dayTimestamp: dayTimestamp,\n events: events\n });\n });\n\n return templateContext;\n };\n\n /**\n * Render the HTML for the given calendar events.\n *\n * @param {array} calendarEvents A list of calendar events\n * @return {promise} Resolved with HTML and JS strings.\n */\n var render = function(calendarEvents) {\n var templateContext = buildTemplateContext(calendarEvents);\n var templateName = TEMPLATES.EVENT_LIST_CONTENT;\n\n return Templates.render(templateName, templateContext);\n };\n\n /**\n * Retrieve a list of calendar events from the server for the given\n * constraints.\n *\n * @param {Number} midnight The user's midnight time in unix timestamp.\n * @param {Number} limit Limit the result set to this number of items\n * @param {Number} daysOffset How many days (from midnight) to offset the results from\n * @param {int|undefined} daysLimit How many dates (from midnight) to limit the result to\n * @param {int|false} lastId The ID of the last seen event (if any)\n * @param {int|undefined} courseId Course ID to restrict events to\n * @param {string|undefined} searchValue Search value\n * @return {Promise} A jquery promise\n */\n var load = function(midnight, limit, daysOffset, daysLimit, lastId, courseId, searchValue) {\n var startTime = midnight + (daysOffset * SECONDS_IN_DAY);\n var endTime = daysLimit != undefined ? midnight + (daysLimit * SECONDS_IN_DAY) : false;\n\n var args = {\n starttime: startTime,\n limit: limit,\n };\n\n if (lastId) {\n args.aftereventid = lastId;\n }\n\n if (endTime) {\n args.endtime = endTime;\n }\n\n if (searchValue) {\n args.searchvalue = searchValue;\n }\n\n if (courseId) {\n // If we have a course id then we only want events from that course.\n args.courseid = courseId;\n return CalendarEventsRepository.queryByCourse(args);\n } else {\n // Otherwise we want events from any course.\n return CalendarEventsRepository.queryByTime(args);\n }\n };\n\n /**\n * Create a lazy-loading region for the calendar events in the given root element.\n *\n * @param {object} root The event list container element.\n * @param {object} additionalConfig Additional config options to pass to pagedContentFactory.\n */\n var init = function(root, additionalConfig = {}) {\n const pendingPromise = new Pending('block/timeline:event-init');\n root = $(root);\n\n courseview = !!additionalConfig.courseview;\n\n // Create a promise that will be resolved once the first set of page\n // data has been loaded. This ensures that the loading placeholder isn't\n // hidden until we have all of the data back to prevent the page elements\n // jumping around.\n var firstLoad = $.Deferred();\n var eventListContent = root.find(SELECTORS.EVENT_LIST_CONTENT);\n var loadingPlaceholder = root.find(SELECTORS.EVENT_LIST_LOADING_PLACEHOLDER);\n var courseId = root.attr('data-course-id');\n var daysOffset = parseInt(root.attr('data-days-offset'), 10);\n var daysLimit = root.attr('data-days-limit');\n var midnight = parseInt(root.attr('data-midnight'), 10);\n const searchValue = root.closest(SELECTORS.TIMELINE_BLOCK).find(SELECTORS.TIMELINE_SEARCH).val();\n\n // Make sure the content area and loading placeholder is visible.\n // This is because the init function can be called to re-initialise\n // an existing event list area.\n emptyContent(root);\n showContent(root);\n loadingPlaceholder.removeClass('hidden');\n\n // Days limit isn't mandatory.\n if (daysLimit != undefined) {\n daysLimit = parseInt(daysLimit, 10);\n }\n\n // Create the lazy loading content element.\n return createLazyLoadingContent(root, firstLoad,\n DEFAULT_LAZY_LOADING_ITEMS_FIRST_LOAD, midnight, 0, courseId, daysOffset, daysLimit, searchValue)\n .then(function(html, js) {\n firstLoad.then(function(data) {\n if (!data.hasContent) {\n loadingPlaceholder.addClass('hidden');\n // If we didn't get any data then show the empty data message.\n return hideContent(root);\n }\n\n html = $(html);\n // Hide the content for now.\n html.addClass('hidden');\n // Replace existing elements with the newly created lazy-loading region.\n Templates.replaceNodeContents(eventListContent, html, js);\n\n // Prevent changing page elements too much by only showing the content\n // once we've loaded some data for the first time. This allows our\n // fancy loading placeholder to shine.\n html.removeClass('hidden');\n loadingPlaceholder.addClass('hidden');\n\n if (!data.loadedAll) {\n Templates.render(TEMPLATES.MORE_ACTIVITIES_BUTTON, {courseview}).then(function(html) {\n eventListContent.append(html);\n setLastTimestamp(root, data.lastTimeStamp);\n // Init the event handler.\n initEventListener(root);\n return html;\n }).catch(function() {\n return false;\n });\n }\n\n return data;\n })\n .catch(function() {\n return false;\n });\n\n return html;\n }).then(() => {\n return pendingPromise.resolve();\n })\n .catch(Notification.exception);\n };\n\n /**\n * Create a lazy-loading content element for showing the event list for the initial load.\n *\n * @param {object} root The event list container element.\n * @param {object} firstLoad A jQuery promise to be resolved after the first set of data is loaded.\n * @param {int} itemLimit Limit the number of items.\n * @param {Number} midnight The user's midnight time in unix timestamp.\n * @param {int} lastId The last event ID for each loaded page. Page number is key, id is value.\n * @param {int|undefined} courseId Course ID to restrict events to.\n * @param {Number} daysOffset How many days (from midnight) to offset the results from.\n * @param {int|undefined} daysLimit How many dates (from midnight) to limit the result to.\n * @param {string|undefined} searchValue Search value.\n * @return {object} jQuery promise resolved with calendar events.\n */\n const createLazyLoadingContent = (root, firstLoad, itemLimit, midnight, lastId,\n courseId, daysOffset, daysLimit, searchValue) => {\n return loadEventsForLazyLoading(\n root,\n itemLimit,\n midnight,\n lastId,\n courseId,\n daysOffset,\n daysLimit,\n searchValue\n ).then(data => {\n if (data.calendarEvents.length) {\n const lastEventId = data.calendarEvents.at(-1).id;\n const lastTimeStamp = data.calendarEvents.at(-1).timeusermidnight;\n firstLoad.resolve({\n hasContent: true,\n lastId: lastEventId,\n lastTimeStamp: lastTimeStamp,\n loadedAll: data.loadedAll\n });\n return render(data.calendarEvents, midnight);\n } else {\n firstLoad.resolve({\n hasContent: false,\n lastId: 0,\n lastTimeStamp: 0,\n loadedAll: true\n });\n return data.calendarEvents;\n }\n }).catch(Notification.exception);\n };\n\n /**\n * Handle the request from the lazy-loading region.\n * Uses the given data like course id, offset... to request the events from the server.\n *\n * @param {object} root The event list container element.\n * @param {int} itemLimit Limit the number of items.\n * @param {Number} midnight The user's midnight time in unix timestamp.\n * @param {int} lastId The last event ID for each loaded page.\n * @param {int|undefined} courseId Course ID to restrict events to.\n * @param {Number} daysOffset How many days (from midnight) to offset the results from.\n * @param {int|undefined} daysLimit How many dates (from midnight) to limit the result to.\n * @param {string|undefined} searchValue Search value.\n * @return {object} jQuery promise resolved with calendar events.\n */\n const loadEventsForLazyLoading = (root, itemLimit, midnight, lastId, courseId, daysOffset, daysLimit, searchValue) => {\n // Load one more than the given limit so that we can tell if there\n // is more content to load after this.\n const eventsPromise = load(midnight, itemLimit + 1, daysOffset, daysLimit, lastId, courseId, searchValue);\n let calendarEvents = [];\n let loadedAll = true;\n\n return eventsPromise.then(result => {\n if (!result.events.length) {\n return {calendarEvents, loadedAll};\n }\n\n // Determine if the overdue filter is applied.\n const overdueFilter = document.querySelector(\"[data-filtername='overdue']\");\n const filterByOverdue = (overdueFilter && overdueFilter.getAttribute('aria-current'));\n\n calendarEvents = result.events.filter(event => {\n if (event.eventtype == 'open' || event.eventtype == 'opensubmission') {\n const dayTimestamp = UserDate.getUserMidnightForTimestamp(event.timesort, midnight);\n return dayTimestamp > midnight;\n }\n // When filtering by overdue, we fetch all events due today, in case any have elapsed already and are overdue.\n // This means if filtering by overdue, some events fetched might not be required (eg if due later today).\n return (!filterByOverdue || event.overdue);\n });\n\n loadedAll = calendarEvents.length <= itemLimit;\n\n if (!loadedAll) {\n // Remove the last element from the array because it isn't\n // needed in this result set.\n calendarEvents.pop();\n }\n\n if (calendarEvents.length) {\n const lastEventId = calendarEvents.at(-1).id;\n setOffset(root, lastEventId);\n }\n\n return {calendarEvents, loadedAll};\n });\n };\n\n /**\n * Load new events and append to current list.\n *\n * @param {object} root The event list container element.\n */\n const loadMoreEvents = root => {\n const midnight = parseInt(root.attr('data-midnight'), 10);\n const courseId = root.attr('data-course-id');\n const daysOffset = parseInt(root.attr('data-days-offset'), 10);\n const daysLimit = root.attr('data-days-limit');\n const lastId = getOffset(root);\n const eventListWrapper = root.find(SELECTORS.EVENT_LIST_WRAPPER);\n const searchValue = root.closest(SELECTORS.TIMELINE_BLOCK).find(SELECTORS.TIMELINE_SEARCH).val();\n const eventsPromise = loadEventsForLazyLoading(\n root,\n DEFAULT_LAZY_LOADING_ITEMS_OTHER_LOAD,\n midnight,\n lastId,\n courseId,\n daysOffset,\n daysLimit,\n searchValue\n );\n eventsPromise.then(data => {\n if (data.calendarEvents.length) {\n const renderPromise = render(data.calendarEvents);\n const lastTimestamp = getLastTimestamp(root);\n renderPromise.then((html, js) => {\n html = $(html);\n\n // Remove the date heading if it has the same value as the previous one.\n html.find(`[data-timestamp=\"${lastTimestamp}\"]`).remove();\n Templates.appendNodeContents(eventListWrapper, html.html(), js);\n\n if (!data.loadedAll) {\n Templates.render(TEMPLATES.MORE_ACTIVITIES_BUTTON, {}).then(html => {\n eventListWrapper.append(html);\n setLastTimestamp(root, data.calendarEvents.at(-1).timeusermidnight);\n // Init the event handler.\n initEventListener(root);\n\n return html;\n }).catch(() => {\n return false;\n });\n }\n\n return html;\n }).catch(Notification.exception);\n }\n\n return data;\n }).then(() => {\n return disableMoreActivitiesButtonLoading(root);\n }).catch(Notification.exception);\n };\n\n /**\n * Return the offset value for lazy loading fetching.\n *\n * @param {object} element The event list container element.\n * @return {Number} Offset value.\n */\n const getOffset = element => {\n return parseInt(element.attr('data-lazyload-offset'), 10);\n };\n\n /**\n * Set the offset value for lazy loading fetching.\n *\n * @param {object} element The event list container element.\n * @param {Number} offset Offset value.\n */\n const setOffset = (element, offset) => {\n element.attr('data-lazyload-offset', offset);\n };\n\n /**\n * Return the timestamp value for lazy loading fetching.\n *\n * @param {object} element The event list container element.\n * @return {Number} Timestamp value.\n */\n const getLastTimestamp = element => {\n return parseInt(element.attr('data-timestamp'), 10);\n };\n\n /**\n * Set the timestamp value for lazy loading fetching.\n *\n * @param {object} element The event list container element.\n * @param {Number} timestamp Timestamp value.\n */\n const setLastTimestamp = (element, timestamp) => {\n element.attr('data-timestamp', timestamp);\n };\n\n /**\n * Add the \"Show more activities\" button and remove and loading spinner.\n *\n * @param {object} root The event list container element.\n */\n const enableMoreActivitiesButtonLoading = root => {\n const loadMoreButton = root.find(SELECTORS.MORE_ACTIVITIES_BUTTON);\n loadMoreButton.prop('disabled', true);\n Templates.render(TEMPLATES.LOADING_ICON, {}).then(html => {\n loadMoreButton.append(html);\n return html;\n }).catch(() => {\n // It's not important if this false so just do so silently.\n return false;\n });\n };\n\n /**\n * Remove the \"Show more activities\" button and remove and loading spinner.\n *\n * @param {object} root The event list container element.\n */\n const disableMoreActivitiesButtonLoading = root => {\n const loadMoreButtonContainer = root.find(SELECTORS.MORE_ACTIVITIES_BUTTON_CONTAINER);\n loadMoreButtonContainer.remove();\n };\n\n /**\n * Event initialise.\n *\n * @param {object} root The event list container element.\n */\n const initEventListener = root => {\n const loadMoreButton = root.find(SELECTORS.MORE_ACTIVITIES_BUTTON);\n loadMoreButton.on('click', () => {\n enableMoreActivitiesButtonLoading(root);\n loadMoreEvents(root);\n });\n };\n\n return {\n init: init,\n rootSelector: SELECTORS.ROOT,\n };\n});\n"],"names":["define","$","Notification","Templates","Str","UserDate","CalendarEventsRepository","Pending","courseview","SELECTORS","TEMPLATES","hideContent","root","find","addClass","removeClass","showContent","emptyContent","empty","render","calendarEvents","templateContext","eventsByDay","eventsbyday","forEach","calendarEvent","dayTimestamp","timeusermidnight","push","Object","keys","events","buildTemplateContext","templateName","createLazyLoadingContent","firstLoad","itemLimit","midnight","lastId","courseId","daysOffset","daysLimit","searchValue","loadEventsForLazyLoading","then","data","length","lastEventId","at","id","lastTimeStamp","resolve","hasContent","loadedAll","catch","exception","eventsPromise","limit","endTime","undefined","args","starttime","aftereventid","endtime","searchvalue","courseid","queryByCourse","queryByTime","load","result","overdueFilter","document","querySelector","filterByOverdue","getAttribute","filter","event","eventtype","getUserMidnightForTimestamp","timesort","overdue","pop","setOffset","getOffset","element","parseInt","attr","offset","getLastTimestamp","setLastTimestamp","timestamp","disableMoreActivitiesButtonLoading","remove","initEventListener","on","loadMoreButton","prop","html","append","enableMoreActivitiesButtonLoading","eventListWrapper","closest","val","renderPromise","lastTimestamp","js","appendNodeContents","loadMoreEvents","init","additionalConfig","pendingPromise","Deferred","eventListContent","loadingPlaceholder","replaceNodeContents","rootSelector"],"mappings":"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"}
\ No newline at end of file
+{"version":3,"file":"event_list.min.js","sources":["../src/event_list.js"],"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 to load and render the list of calendar events for a\n * given day range.\n *\n * @module block_timeline/event_list\n * @copyright 2016 Ryan Wyllie \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(\n[\n 'jquery',\n 'core/notification',\n 'core/templates',\n 'core/str',\n 'core/user_date',\n 'block_timeline/calendar_events_repository',\n 'core/pending'\n],\nfunction(\n $,\n Notification,\n Templates,\n Str,\n UserDate,\n CalendarEventsRepository,\n Pending\n) {\n\n var SECONDS_IN_DAY = 60 * 60 * 24;\n var courseview = false;\n\n var SELECTORS = {\n EMPTY_MESSAGE: '[data-region=\"no-events-empty-message\"]',\n ROOT: '[data-region=\"event-list-container\"]',\n EVENT_LIST_CONTENT: '[data-region=\"event-list-content\"]',\n EVENT_LIST_WRAPPER: '[data-region=\"event-list-wrapper\"]',\n EVENT_LIST_LOADING_PLACEHOLDER: '[data-region=\"event-list-loading-placeholder\"]',\n TIMELINE_BLOCK: '[data-region=\"timeline\"]',\n TIMELINE_SEARCH: '[data-action=\"search\"]',\n MORE_ACTIVITIES_BUTTON: '[data-action=\"more-events\"]',\n MORE_ACTIVITIES_BUTTON_CONTAINER: '[data-region=\"more-events-button-container\"]'\n };\n\n var TEMPLATES = {\n EVENT_LIST_CONTENT: 'block_timeline/event-list-content',\n MORE_ACTIVITIES_BUTTON: 'block_timeline/event-list-loadmore',\n LOADING_ICON: 'core/loading'\n };\n\n /** @property {number} The total items will be shown on the first load. */\n const DEFAULT_LAZY_LOADING_ITEMS_FIRST_LOAD = 5;\n /** @property {number} The total items will be shown when click on the Show more activities button. */\n const DEFAULT_LAZY_LOADING_ITEMS_OTHER_LOAD = 10;\n\n /**\n * Hide the content area and display the empty content message.\n *\n * @param {object} root The container element\n */\n var hideContent = function(root) {\n root.find(SELECTORS.EVENT_LIST_CONTENT).addClass('hidden');\n root.find(SELECTORS.EMPTY_MESSAGE).removeClass('hidden');\n };\n\n /**\n * Show the content area and hide the empty content message.\n *\n * @param {object} root The container element\n */\n var showContent = function(root) {\n root.find(SELECTORS.EVENT_LIST_CONTENT).removeClass('hidden');\n root.find(SELECTORS.EMPTY_MESSAGE).addClass('hidden');\n };\n\n /**\n * Empty the content area.\n *\n * @param {object} root The container element\n */\n var emptyContent = function(root) {\n root.find(SELECTORS.EVENT_LIST_CONTENT).empty();\n };\n\n /**\n * Construct the template context from a list of calendar events. The events\n * are grouped by which day they are on. The day is calculated from the user's\n * midnight timestamp to ensure that the calculation is timezone agnostic.\n *\n * The return data structure will look like:\n * {\n * eventsbyday: [\n * {\n * dayTimestamp: 1533744000,\n * events: [\n * { ...event 1 data... },\n * { ...event 2 data... }\n * ]\n * },\n * {\n * dayTimestamp: 1533830400,\n * events: [\n * { ...event 3 data... },\n * { ...event 4 data... }\n * ]\n * }\n * ]\n * }\n *\n * Each day timestamp is the day's midnight in the user's timezone.\n *\n * @param {array} calendarEvents List of calendar events\n * @return {object}\n */\n var buildTemplateContext = function(calendarEvents) {\n var eventsByDay = {};\n var templateContext = {\n courseview,\n eventsbyday: []\n };\n\n calendarEvents.forEach(function(calendarEvent) {\n var dayTimestamp = calendarEvent.timeusermidnight;\n if (eventsByDay[dayTimestamp]) {\n eventsByDay[dayTimestamp].push(calendarEvent);\n } else {\n eventsByDay[dayTimestamp] = [calendarEvent];\n }\n });\n\n Object.keys(eventsByDay).forEach(function(dayTimestamp) {\n var events = eventsByDay[dayTimestamp];\n templateContext.eventsbyday.push({\n dayTimestamp: dayTimestamp,\n events: events\n });\n });\n\n return templateContext;\n };\n\n /**\n * Render the HTML for the given calendar events.\n *\n * @param {array} calendarEvents A list of calendar events\n * @return {promise} Resolved with HTML and JS strings.\n */\n var render = function(calendarEvents) {\n var templateContext = buildTemplateContext(calendarEvents);\n var templateName = TEMPLATES.EVENT_LIST_CONTENT;\n\n return Templates.render(templateName, templateContext);\n };\n\n /**\n * Retrieve a list of calendar events from the server for the given\n * constraints.\n *\n * @param {Number} midnight The user's midnight time in unix timestamp.\n * @param {Number} limit Limit the result set to this number of items\n * @param {Number} daysOffset How many days (from midnight) to offset the results from\n * @param {int|undefined} daysLimit How many dates (from midnight) to limit the result to\n * @param {int|false} lastId The ID of the last seen event (if any)\n * @param {int|undefined} courseId Course ID to restrict events to\n * @param {string|undefined} searchValue Search value\n * @return {Promise} A jquery promise\n */\n var load = function(midnight, limit, daysOffset, daysLimit, lastId, courseId, searchValue) {\n var startTime = midnight + (daysOffset * SECONDS_IN_DAY);\n var endTime = daysLimit != undefined ? midnight + (daysLimit * SECONDS_IN_DAY) : false;\n\n var args = {\n starttime: startTime,\n limit: limit,\n };\n\n if (lastId) {\n args.aftereventid = lastId;\n }\n\n if (endTime) {\n args.endtime = endTime;\n }\n\n if (searchValue) {\n args.searchvalue = searchValue;\n }\n\n if (courseId) {\n // If we have a course id then we only want events from that course.\n args.courseid = courseId;\n return CalendarEventsRepository.queryByCourse(args);\n } else {\n // Otherwise we want events from any course.\n return CalendarEventsRepository.queryByTime(args);\n }\n };\n\n /**\n * Create a lazy-loading region for the calendar events in the given root element.\n *\n * @param {object} root The event list container element.\n * @param {object} additionalConfig Additional config options to pass to pagedContentFactory.\n */\n var init = function(root, additionalConfig = {}) {\n const pendingPromise = new Pending('block/timeline:event-init');\n root = $(root);\n\n courseview = !!additionalConfig.courseview;\n\n // Create a promise that will be resolved once the first set of page\n // data has been loaded. This ensures that the loading placeholder isn't\n // hidden until we have all of the data back to prevent the page elements\n // jumping around.\n var firstLoad = $.Deferred();\n var eventListContent = root.find(SELECTORS.EVENT_LIST_CONTENT);\n var loadingPlaceholder = root.find(SELECTORS.EVENT_LIST_LOADING_PLACEHOLDER);\n var courseId = root.attr('data-course-id');\n var daysOffset = parseInt(root.attr('data-days-offset'), 10);\n var daysLimit = root.attr('data-days-limit');\n var midnight = parseInt(root.attr('data-midnight'), 10);\n const searchValue = root.closest(SELECTORS.TIMELINE_BLOCK).find(SELECTORS.TIMELINE_SEARCH).val();\n\n // Make sure the content area and loading placeholder is visible.\n // This is because the init function can be called to re-initialise\n // an existing event list area.\n emptyContent(root);\n showContent(root);\n loadingPlaceholder.removeClass('hidden');\n\n // Days limit isn't mandatory.\n if (daysLimit != undefined) {\n daysLimit = parseInt(daysLimit, 10);\n }\n\n // Create the lazy loading content element.\n return createLazyLoadingContent(root, firstLoad,\n DEFAULT_LAZY_LOADING_ITEMS_FIRST_LOAD, midnight, 0, courseId, daysOffset, daysLimit, searchValue)\n .then(function(html, js) {\n firstLoad.then(function(data) {\n if (!data.hasContent) {\n loadingPlaceholder.addClass('hidden');\n // If we didn't get any data then show the empty data message.\n return hideContent(root);\n }\n\n html = $(html);\n // Hide the content for now.\n html.addClass('hidden');\n // Replace existing elements with the newly created lazy-loading region.\n Templates.replaceNodeContents(eventListContent, html, js);\n\n // Prevent changing page elements too much by only showing the content\n // once we've loaded some data for the first time. This allows our\n // fancy loading placeholder to shine.\n html.removeClass('hidden');\n loadingPlaceholder.addClass('hidden');\n\n if (!data.loadedAll) {\n Templates.render(TEMPLATES.MORE_ACTIVITIES_BUTTON, {courseview}).then(function(html) {\n eventListContent.append(html);\n setLastTimestamp(root, data.lastTimeStamp);\n // Init the event handler.\n initEventListener(root);\n return html;\n }).catch(function() {\n return false;\n });\n }\n\n return data;\n })\n .catch(function() {\n return false;\n });\n\n return html;\n }).then(() => {\n return pendingPromise.resolve();\n })\n .catch(Notification.exception);\n };\n\n /**\n * Create a lazy-loading content element for showing the event list for the initial load.\n *\n * @param {object} root The event list container element.\n * @param {object} firstLoad A jQuery promise to be resolved after the first set of data is loaded.\n * @param {int} itemLimit Limit the number of items.\n * @param {Number} midnight The user's midnight time in unix timestamp.\n * @param {int} lastId The last event ID for each loaded page. Page number is key, id is value.\n * @param {int|undefined} courseId Course ID to restrict events to.\n * @param {Number} daysOffset How many days (from midnight) to offset the results from.\n * @param {int|undefined} daysLimit How many dates (from midnight) to limit the result to.\n * @param {string|undefined} searchValue Search value.\n * @return {object} jQuery promise resolved with calendar events.\n */\n const createLazyLoadingContent = (root, firstLoad, itemLimit, midnight, lastId,\n courseId, daysOffset, daysLimit, searchValue) => {\n return loadEventsForLazyLoading(\n root,\n itemLimit,\n midnight,\n lastId,\n courseId,\n daysOffset,\n daysLimit,\n searchValue\n ).then(data => {\n if (data.calendarEvents.length) {\n const lastEventId = data.calendarEvents.at(-1).id;\n const lastTimeStamp = data.calendarEvents.at(-1).timeusermidnight;\n firstLoad.resolve({\n hasContent: true,\n lastId: lastEventId,\n lastTimeStamp: lastTimeStamp,\n loadedAll: data.loadedAll\n });\n return render(data.calendarEvents, midnight);\n } else {\n firstLoad.resolve({\n hasContent: false,\n lastId: 0,\n lastTimeStamp: 0,\n loadedAll: true\n });\n return data.calendarEvents;\n }\n }).catch(Notification.exception);\n };\n\n /**\n * Handle the request from the lazy-loading region.\n * Uses the given data like course id, offset... to request the events from the server.\n *\n * @param {object} root The event list container element.\n * @param {int} itemLimit Limit the number of items.\n * @param {Number} midnight The user's midnight time in unix timestamp.\n * @param {int} lastId The last event ID for each loaded page.\n * @param {int|undefined} courseId Course ID to restrict events to.\n * @param {Number} daysOffset How many days (from midnight) to offset the results from.\n * @param {int|undefined} daysLimit How many dates (from midnight) to limit the result to.\n * @param {string|undefined} searchValue Search value.\n * @return {object} jQuery promise resolved with calendar events.\n */\n const loadEventsForLazyLoading = (root, itemLimit, midnight, lastId, courseId, daysOffset, daysLimit, searchValue) => {\n // Load one more than the given limit so that we can tell if there\n // is more content to load after this.\n const eventsPromise = load(midnight, itemLimit + 1, daysOffset, daysLimit, lastId, courseId, searchValue);\n let calendarEvents = [];\n let loadedAll = true;\n\n return eventsPromise.then(result => {\n if (!result.events.length) {\n return {calendarEvents, loadedAll};\n }\n\n // Determine if the overdue filter is applied.\n const overdueFilter = document.querySelector(\"[data-filtername='overdue']\");\n const filterByOverdue = (overdueFilter && overdueFilter.getAttribute('aria-current'));\n\n calendarEvents = result.events.filter(event => {\n if (event.eventtype == 'open' || event.eventtype == 'opensubmission') {\n const dayTimestamp = UserDate.getUserMidnightForTimestamp(event.timesort, midnight);\n return dayTimestamp > midnight;\n }\n // When filtering by overdue, we fetch all events due today, in case any have elapsed already and are overdue.\n // This means if filtering by overdue, some events fetched might not be required (eg if due later today).\n return (!filterByOverdue || event.overdue);\n });\n\n loadedAll = calendarEvents.length <= itemLimit;\n\n if (!loadedAll) {\n // Remove the last element from the array because it isn't\n // needed in this result set.\n calendarEvents.pop();\n }\n\n if (calendarEvents.length) {\n const lastEventId = calendarEvents.at(-1).id;\n setOffset(root, lastEventId);\n }\n\n return {calendarEvents, loadedAll};\n });\n };\n\n /**\n * Load new events and append to current list.\n *\n * @param {object} root The event list container element.\n */\n const loadMoreEvents = root => {\n const midnight = parseInt(root.attr('data-midnight'), 10);\n const courseId = root.attr('data-course-id');\n const daysOffset = parseInt(root.attr('data-days-offset'), 10);\n const daysLimit = root.attr('data-days-limit');\n const lastId = getOffset(root);\n const eventListWrapper = root.find(SELECTORS.EVENT_LIST_WRAPPER);\n const searchValue = root.closest(SELECTORS.TIMELINE_BLOCK).find(SELECTORS.TIMELINE_SEARCH).val();\n const eventsPromise = loadEventsForLazyLoading(\n root,\n DEFAULT_LAZY_LOADING_ITEMS_OTHER_LOAD,\n midnight,\n lastId,\n courseId,\n daysOffset,\n daysLimit,\n searchValue\n );\n eventsPromise.then(data => {\n if (data.calendarEvents.length) {\n const renderPromise = render(data.calendarEvents);\n const lastTimestamp = getLastTimestamp(root);\n renderPromise.then((html, js) => {\n html = $(html);\n\n // Remove the date heading if it has the same value as the previous one.\n html.find(`[data-timestamp=\"${lastTimestamp}\"]`).remove();\n Templates.appendNodeContents(eventListWrapper, html.html(), js);\n\n if (!data.loadedAll) {\n Templates.render(TEMPLATES.MORE_ACTIVITIES_BUTTON, {}).then(html => {\n eventListWrapper.append(html);\n setLastTimestamp(root, data.calendarEvents.at(-1).timeusermidnight);\n // Init the event handler.\n initEventListener(root);\n\n return html;\n }).catch(() => {\n return false;\n });\n }\n\n return html;\n }).catch(Notification.exception);\n }\n\n return data;\n }).then(() => {\n return disableMoreActivitiesButtonLoading(root);\n }).catch(Notification.exception);\n };\n\n /**\n * Return the offset value for lazy loading fetching.\n *\n * @param {object} element The event list container element.\n * @return {Number} Offset value.\n */\n const getOffset = element => {\n return parseInt(element.attr('data-lazyload-offset'), 10);\n };\n\n /**\n * Set the offset value for lazy loading fetching.\n *\n * @param {object} element The event list container element.\n * @param {Number} offset Offset value.\n */\n const setOffset = (element, offset) => {\n element.attr('data-lazyload-offset', offset);\n };\n\n /**\n * Return the timestamp value for lazy loading fetching.\n *\n * @param {object} element The event list container element.\n * @return {Number} Timestamp value.\n */\n const getLastTimestamp = element => {\n return parseInt(element.attr('data-timestamp'), 10);\n };\n\n /**\n * Set the timestamp value for lazy loading fetching.\n *\n * @param {object} element The event list container element.\n * @param {Number} timestamp Timestamp value.\n */\n const setLastTimestamp = (element, timestamp) => {\n element.attr('data-timestamp', timestamp);\n };\n\n /**\n * Add the \"Show more activities\" button and remove and loading spinner.\n *\n * @param {object} root The event list container element.\n */\n const enableMoreActivitiesButtonLoading = root => {\n const loadMoreButton = root.find(SELECTORS.MORE_ACTIVITIES_BUTTON);\n loadMoreButton.prop('disabled', true);\n Templates.render(TEMPLATES.LOADING_ICON, {}).then(html => {\n loadMoreButton.append(html);\n return html;\n }).catch(() => {\n // It's not important if this false so just do so silently.\n return false;\n });\n };\n\n /**\n * Remove the \"Show more activities\" button and remove and loading spinner.\n *\n * @param {object} root The event list container element.\n */\n const disableMoreActivitiesButtonLoading = root => {\n const loadMoreButtonContainer = root.find(SELECTORS.MORE_ACTIVITIES_BUTTON_CONTAINER);\n loadMoreButtonContainer.remove();\n };\n\n /**\n * Event initialise.\n *\n * @param {object} root The event list container element.\n */\n const initEventListener = root => {\n const loadMoreButton = root.find(SELECTORS.MORE_ACTIVITIES_BUTTON);\n loadMoreButton.on('click', () => {\n enableMoreActivitiesButtonLoading(root);\n loadMoreEvents(root);\n });\n };\n\n return {\n init: init,\n rootSelector: SELECTORS.ROOT,\n };\n});\n"],"names":["define","$","Notification","Templates","Str","UserDate","CalendarEventsRepository","Pending","courseview","SELECTORS","EMPTY_MESSAGE","ROOT","EVENT_LIST_CONTENT","EVENT_LIST_WRAPPER","EVENT_LIST_LOADING_PLACEHOLDER","TIMELINE_BLOCK","TIMELINE_SEARCH","MORE_ACTIVITIES_BUTTON","MORE_ACTIVITIES_BUTTON_CONTAINER","TEMPLATES","LOADING_ICON","DEFAULT_LAZY_LOADING_ITEMS_FIRST_LOAD","hideContent","root","find","addClass","removeClass","showContent","emptyContent","empty","render","calendarEvents","templateContext","eventsByDay","eventsbyday","forEach","calendarEvent","dayTimestamp","timeusermidnight","push","Object","keys","events","buildTemplateContext","templateName","createLazyLoadingContent","firstLoad","itemLimit","midnight","lastId","courseId","daysOffset","daysLimit","searchValue","loadEventsForLazyLoading","then","data","length","lastEventId","at","id","lastTimeStamp","resolve","hasContent","loadedAll","catch","exception","eventsPromise","limit","endTime","undefined","args","starttime","aftereventid","endtime","searchvalue","courseid","queryByCourse","queryByTime","load","result","overdueFilter","document","querySelector","filterByOverdue","getAttribute","filter","event","eventtype","getUserMidnightForTimestamp","timesort","overdue","pop","setOffset","getOffset","element","parseInt","attr","offset","getLastTimestamp","setLastTimestamp","timestamp","disableMoreActivitiesButtonLoading","remove","initEventListener","on","loadMoreButton","prop","html","append","enableMoreActivitiesButtonLoading","eventListWrapper","closest","val","renderPromise","lastTimestamp","js","concat","appendNodeContents","loadMoreEvents","init","additionalConfig","arguments","pendingPromise","Deferred","eventListContent","loadingPlaceholder","replaceNodeContents","rootSelector"],"mappings":"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"}
\ No newline at end of file
diff --git a/blocks/timeline/amd/build/main.min.js.map b/blocks/timeline/amd/build/main.min.js.map
index b8a83abf783..ad4f53333f3 100644
--- a/blocks/timeline/amd/build/main.min.js.map
+++ b/blocks/timeline/amd/build/main.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"main.min.js","sources":["../src/main.js"],"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 to initialise the timeline block.\n *\n * @copyright 2018 Ryan Wyllie \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\ndefine(\n[\n 'jquery',\n 'block_timeline/view_nav',\n 'block_timeline/view'\n],\nfunction(\n $,\n ViewNav,\n View\n) {\n\n var SELECTORS = {\n TIMELINE_VIEW: '[data-region=\"timeline-view\"]'\n };\n\n /**\n * Initialise all of the modules for the timeline block.\n *\n * @param {object} root The root element for the timeline block.\n */\n var init = function(root) {\n root = $(root);\n var viewRoot = root.find(SELECTORS.TIMELINE_VIEW);\n\n // Initialise the timeline navigation elements.\n ViewNav.init(root, viewRoot);\n // Initialise the timeline view modules.\n View.init(viewRoot);\n };\n\n return {\n init: init\n };\n});\n"],"names":["define","$","ViewNav","View","SELECTORS","init","root","viewRoot","find"],"mappings":";;;;;;AAsBAA,6BACA,CACI,SACA,0BACA,wBAEJ,SACIC,EACAC,QACAC,UAGIC,wBACe,sCAkBZ,CACHC,KAXO,SAASC,UAEZC,UADJD,KAAOL,EAAEK,OACWE,KAAKJ,yBAGzBF,QAAQG,KAAKC,KAAMC,UAEnBJ,KAAKE,KAAKE"}
\ No newline at end of file
+{"version":3,"file":"main.min.js","sources":["../src/main.js"],"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 to initialise the timeline block.\n *\n * @copyright 2018 Ryan Wyllie \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\ndefine(\n[\n 'jquery',\n 'block_timeline/view_nav',\n 'block_timeline/view'\n],\nfunction(\n $,\n ViewNav,\n View\n) {\n\n var SELECTORS = {\n TIMELINE_VIEW: '[data-region=\"timeline-view\"]'\n };\n\n /**\n * Initialise all of the modules for the timeline block.\n *\n * @param {object} root The root element for the timeline block.\n */\n var init = function(root) {\n root = $(root);\n var viewRoot = root.find(SELECTORS.TIMELINE_VIEW);\n\n // Initialise the timeline navigation elements.\n ViewNav.init(root, viewRoot);\n // Initialise the timeline view modules.\n View.init(viewRoot);\n };\n\n return {\n init: init\n };\n});\n"],"names":["define","$","ViewNav","View","SELECTORS","init","root","viewRoot","find"],"mappings":";;;;;;AAsBAA,OACA,sBAAA,CACI,SACA,0BACA,wBAEJ,SACIC,EACAC,QACAC,MAGA,IAAIC,wBACe,gCAkBnB,MAAO,CACHC,KAXO,SAASC,MAEhB,IAAIC,UADJD,KAAOL,EAAEK,OACWE,KAAKJ,yBAGzBF,QAAQG,KAAKC,KAAMC,UAEnBJ,KAAKE,KAAKE,WAMlB"}
\ No newline at end of file
diff --git a/blocks/timeline/amd/build/view.min.js.map b/blocks/timeline/amd/build/view.min.js.map
index 515b6be786b..67f772df581 100644
--- a/blocks/timeline/amd/build/view.min.js.map
+++ b/blocks/timeline/amd/build/view.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"view.min.js","sources":["../src/view.js"],"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 * Manage the timeline view for the timeline block.\n *\n * @copyright 2018 Ryan Wyllie \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\ndefine(\n[\n 'jquery',\n 'block_timeline/view_dates',\n 'block_timeline/view_courses',\n],\nfunction(\n $,\n ViewDates,\n ViewCourses\n) {\n\n var SELECTORS = {\n TIMELINE_DATES_VIEW: '[data-region=\"view-dates\"]',\n TIMELINE_COURSES_VIEW: '[data-region=\"view-courses\"]',\n };\n\n /**\n * Intialise the timeline dates and courses views on page load.\n * This function should only be called once per page load because\n * it can cause event listeners to be added to the page.\n *\n * @param {object} root The root element for the timeline view.\n */\n var init = function(root) {\n root = $(root);\n var datesViewRoot = root.find(SELECTORS.TIMELINE_DATES_VIEW);\n var coursesViewRoot = root.find(SELECTORS.TIMELINE_COURSES_VIEW);\n\n ViewDates.init(datesViewRoot);\n ViewCourses.init(coursesViewRoot);\n };\n\n /**\n * Reset the timeline dates and courses views to their original\n * state on first page load.\n *\n * This is called when configuration has changed for the event lists\n * to cause them to reload their data.\n *\n * @param {object} root The root element for the timeline view.\n */\n var reset = function(root) {\n var datesViewRoot = root.find(SELECTORS.TIMELINE_DATES_VIEW);\n var coursesViewRoot = root.find(SELECTORS.TIMELINE_COURSES_VIEW);\n ViewDates.reset(datesViewRoot);\n ViewCourses.reset(coursesViewRoot);\n };\n\n /**\n * Tell the timeline dates or courses view that it has been displayed.\n *\n * This is called each time one of the views is displayed and is used to\n * lazy load the data within it on first load.\n *\n * @param {object} root The root element for the timeline view.\n */\n var shown = function(root) {\n var datesViewRoot = root.find(SELECTORS.TIMELINE_DATES_VIEW);\n var coursesViewRoot = root.find(SELECTORS.TIMELINE_COURSES_VIEW);\n\n if (datesViewRoot.hasClass('active')) {\n ViewDates.shown(datesViewRoot);\n } else {\n ViewCourses.shown(coursesViewRoot);\n }\n };\n\n return {\n init: init,\n reset: reset,\n shown: shown,\n };\n});\n"],"names":["define","$","ViewDates","ViewCourses","SELECTORS","init","root","datesViewRoot","find","coursesViewRoot","reset","shown","hasClass"],"mappings":";;;;;;AAsBAA,6BACA,CACI,SACA,4BACA,gCAEJ,SACIC,EACAC,UACAC,iBAGIC,8BACqB,6BADrBA,gCAEuB,qCAsDpB,CACHC,KA7CO,SAASC,UAEZC,eADJD,KAAOL,EAAEK,OACgBE,KAAKJ,+BAC1BK,gBAAkBH,KAAKE,KAAKJ,iCAEhCF,UAAUG,KAAKE,eACfJ,YAAYE,KAAKI,kBAwCjBC,MA5BQ,SAASJ,UACbC,cAAgBD,KAAKE,KAAKJ,+BAC1BK,gBAAkBH,KAAKE,KAAKJ,iCAChCF,UAAUQ,MAAMH,eAChBJ,YAAYO,MAAMD,kBAyBlBE,MAdQ,SAASL,UACbC,cAAgBD,KAAKE,KAAKJ,+BAC1BK,gBAAkBH,KAAKE,KAAKJ,iCAE5BG,cAAcK,SAAS,UACvBV,UAAUS,MAAMJ,eAEhBJ,YAAYQ,MAAMF"}
\ No newline at end of file
+{"version":3,"file":"view.min.js","sources":["../src/view.js"],"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 * Manage the timeline view for the timeline block.\n *\n * @copyright 2018 Ryan Wyllie \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\ndefine(\n[\n 'jquery',\n 'block_timeline/view_dates',\n 'block_timeline/view_courses',\n],\nfunction(\n $,\n ViewDates,\n ViewCourses\n) {\n\n var SELECTORS = {\n TIMELINE_DATES_VIEW: '[data-region=\"view-dates\"]',\n TIMELINE_COURSES_VIEW: '[data-region=\"view-courses\"]',\n };\n\n /**\n * Intialise the timeline dates and courses views on page load.\n * This function should only be called once per page load because\n * it can cause event listeners to be added to the page.\n *\n * @param {object} root The root element for the timeline view.\n */\n var init = function(root) {\n root = $(root);\n var datesViewRoot = root.find(SELECTORS.TIMELINE_DATES_VIEW);\n var coursesViewRoot = root.find(SELECTORS.TIMELINE_COURSES_VIEW);\n\n ViewDates.init(datesViewRoot);\n ViewCourses.init(coursesViewRoot);\n };\n\n /**\n * Reset the timeline dates and courses views to their original\n * state on first page load.\n *\n * This is called when configuration has changed for the event lists\n * to cause them to reload their data.\n *\n * @param {object} root The root element for the timeline view.\n */\n var reset = function(root) {\n var datesViewRoot = root.find(SELECTORS.TIMELINE_DATES_VIEW);\n var coursesViewRoot = root.find(SELECTORS.TIMELINE_COURSES_VIEW);\n ViewDates.reset(datesViewRoot);\n ViewCourses.reset(coursesViewRoot);\n };\n\n /**\n * Tell the timeline dates or courses view that it has been displayed.\n *\n * This is called each time one of the views is displayed and is used to\n * lazy load the data within it on first load.\n *\n * @param {object} root The root element for the timeline view.\n */\n var shown = function(root) {\n var datesViewRoot = root.find(SELECTORS.TIMELINE_DATES_VIEW);\n var coursesViewRoot = root.find(SELECTORS.TIMELINE_COURSES_VIEW);\n\n if (datesViewRoot.hasClass('active')) {\n ViewDates.shown(datesViewRoot);\n } else {\n ViewCourses.shown(coursesViewRoot);\n }\n };\n\n return {\n init: init,\n reset: reset,\n shown: shown,\n };\n});\n"],"names":["define","$","ViewDates","ViewCourses","SELECTORS","init","root","datesViewRoot","find","coursesViewRoot","reset","shown","hasClass"],"mappings":";;;;;;AAsBAA,OACA,sBAAA,CACI,SACA,4BACA,gCAEJ,SACIC,EACAC,UACAC,aAGA,IAAIC,8BACqB,6BADrBA,gCAEuB,+BAsD3B,MAAO,CACHC,KA7CO,SAASC,MAEhB,IAAIC,eADJD,KAAOL,EAAEK,OACgBE,KAAKJ,+BAC1BK,gBAAkBH,KAAKE,KAAKJ,iCAEhCF,UAAUG,KAAKE,eACfJ,YAAYE,KAAKI,kBAwCjBC,MA5BQ,SAASJ,MACjB,IAAIC,cAAgBD,KAAKE,KAAKJ,+BAC1BK,gBAAkBH,KAAKE,KAAKJ,iCAChCF,UAAUQ,MAAMH,eAChBJ,YAAYO,MAAMD,kBAyBlBE,MAdQ,SAASL,MACjB,IAAIC,cAAgBD,KAAKE,KAAKJ,+BAC1BK,gBAAkBH,KAAKE,KAAKJ,iCAE5BG,cAAcK,SAAS,UACvBV,UAAUS,MAAMJ,eAEhBJ,YAAYQ,MAAMF,kBAS9B"}
\ No newline at end of file
diff --git a/blocks/timeline/amd/build/view_courses.min.js b/blocks/timeline/amd/build/view_courses.min.js
index 11367597895..c818906ad48 100644
--- a/blocks/timeline/amd/build/view_courses.min.js
+++ b/blocks/timeline/amd/build/view_courses.min.js
@@ -4,6 +4,6 @@
* @copyright 2018 Ryan Wyllie
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
-define("block_timeline/view_courses",["jquery","core/notification","core/custom_interaction_events","core/templates","block_timeline/event_list","core_course/repository","block_timeline/calendar_events_repository","core/pending"],(function($,Notification,CustomEvents,Templates,EventList,CourseRepository,EventsRepository,Pending){var SELECTORS_MORE_COURSES_BUTTON='[data-action="more-courses"]',SELECTORS_MORE_COURSES_BUTTON_CONTAINER='[data-region="more-courses-button-container"]',SELECTORS_NO_COURSES_EMPTY_MESSAGE='[data-region="no-courses-empty-message"]',SELECTORS_NO_COURSES_WITH_EVENTS_MESSAGE='[data-region="no-events-empty-message"]',SELECTORS_COURSES_LIST='[data-region="courses-list"]',SELECTORS_COURSE_ITEMS_LOADING_PLACEHOLDER='[data-region="course-items-loading-placeholder"]',SELECTORS_LOADING_ICON=".loading-icon",SELECTORS_TIMELINE_BLOCK='[data-region="timeline"]',SELECTORS_TIMELINE_SEARCH='[data-action="search"]',TEMPLATES_COURSE_ITEMS="block_timeline/course-items",TEMPLATES_LOADING_ICON="core/loading";const additionalConfig={courseview:!0};var hideLoadingPlaceholder=function(root){root.find(SELECTORS_COURSE_ITEMS_LOADING_PLACEHOLDER).addClass("hidden")};var hideMoreCoursesButton=function(root){root.find(SELECTORS_MORE_COURSES_BUTTON_CONTAINER).addClass("hidden")},showMoreCoursesButton=function(root){root.find(SELECTORS_MORE_COURSES_BUTTON_CONTAINER).removeClass("hidden")},disableMoreCoursesButtonLoading=function(root){var button=root.find(SELECTORS_MORE_COURSES_BUTTON);button.prop("disabled",!1),button.find(SELECTORS_LOADING_ICON).remove()};const showNoCoursesWithEventsMessage=function(root){const container=root.find(SELECTORS_COURSES_LIST);Templates.replaceNodeContents(container,"",""),root.find(SELECTORS_NO_COURSES_WITH_EVENTS_MESSAGE).removeClass("hidden")};var getOffset=function(root){return parseInt(root.attr("data-offset"),10)},setOffset=function(root,offset){root.attr("data-offset",offset)},getLimit=function(root){return parseInt(root.attr("data-limit"),10)},getDaysOffset=function(root){return parseInt(root.attr("data-days-offset"),10)},getDaysLimit=function(root){var daysLimit=root.attr("data-days-limit");return null!=daysLimit?parseInt(daysLimit,10):void 0},getMidnight=function(root){return parseInt(root.attr("data-midnight"),10)},getStartTime=function(root){return getMidnight(root)+86400*getDaysOffset(root)},getEndTime=function(root){let endTime=null;if(root.attr("data-filter-overdue"))endTime=Math.floor(Date.now()/1e3);else{const midnight=getMidnight(root),daysLimit=getDaysLimit(root);null!=daysLimit&&(endTime=midnight+86400*daysLimit)}return endTime},hasReloadedEventsSince=function(root,time){return function(root){return root.data("last-event-load-time")}(root)>time},loadEventsForCourses=function(courses,startTime,endTime,searchValue){return function(courseIds,startTime,limit,endTime,searchValue){var args={courseids:courseIds,starttime:startTime,limit:limit};return endTime&&(args.endtime=endTime),searchValue&&(args.searchvalue=searchValue),EventsRepository.queryByCourses(args)}(courses.map((function(course){return course.id})),startTime,6,endTime,searchValue)},updateDisplayFromCourses=function(courses,root,midnight,daysOffset,daysLimit,append){return Templates.render(TEMPLATES_COURSE_ITEMS,{courses:courses,midnight:midnight,hasdaysoffset:!0,hasdayslimit:null!=daysLimit,daysoffset:daysOffset,dayslimit:daysLimit,nodayslimit:null==daysLimit,courseview:!0,hascourses:!0}).then((function(html){return hideLoadingPlaceholder(root),html&&function(root,html){let append=arguments.length>2&&void 0!==arguments[2]&&arguments[2];var container=root.find(SELECTORS_COURSES_LIST);append?Templates.appendNodeContents(container,html,""):Templates.replaceNodeContents(container,html,"")}(root,html,append),html})).then((function(html){return courses.length<2?hideMoreCoursesButton(root):showMoreCoursesButton(root),html})).catch((function(){hideLoadingPlaceholder(root)}))},loadMoreCourses=function(root){let append=arguments.length>1&&void 0!==arguments[1]&&arguments[1];const pendingPromise=new Pending("block/timeline:load-more-courses");var offset=getOffset(root),limit=getLimit(root);const startTime=getStartTime(root),endTime=getEndTime(root),searchValue=root.closest(SELECTORS_TIMELINE_BLOCK).find(SELECTORS_TIMELINE_SEARCH).val();return CourseRepository.getEnrolledCoursesWithEventsByTimelineClassification("all",limit,offset,"fullname asc",searchValue,startTime,endTime).then((function(result){var startEventLoadingTime=Date.now(),courses=result.courses,nextOffset=result.nextoffset,daysOffset=getDaysOffset(root),daysLimit=getDaysLimit(root),midnight=getMidnight(root);const moreCoursesAvailable=result.morecoursesavailable;setOffset(root,nextOffset);var eventsPromise=loadEventsForCourses(courses,startTime,endTime,searchValue),renderPromise=updateDisplayFromCourses(courses,root,midnight,daysOffset,daysLimit,append);return $.when(eventsPromise,renderPromise).then((function(eventsByCourse){return hasReloadedEventsSince(root,startEventLoadingTime)||(courses.length>0?(courses.forEach((function(course){const containerSelector='[data-region="course-events-container"][data-course-id="'+course.id+'"]',eventListRoot=root.find(containerSelector).find(EventList.rootSelector);EventList.init(eventListRoot,additionalConfig)})),moreCoursesAvailable?showMoreCoursesButton(root):hideMoreCoursesButton(root)):(hideMoreCoursesButton(root),0==offset&&showNoCoursesWithEventsMessage(root))),eventsByCourse}))})).then((()=>pendingPromise.resolve())).catch(Notification.exception)},registerEventListeners=function(root){CustomEvents.define(root,[CustomEvents.events.activate]),root.on(CustomEvents.events.activate,SELECTORS_MORE_COURSES_BUTTON,(function(e,data){!function(root){var button=root.find(SELECTORS_MORE_COURSES_BUTTON);button.prop("disabled",!0),Templates.render(TEMPLATES_LOADING_ICON,{}).then((function(html){return button.append(html),html})).catch((function(){return!1}))}(root),loadMoreCourses(root,!0).then((function(){disableMoreCoursesButtonLoading(root)})).catch((function(){disableMoreCoursesButtonLoading(root)})),data&&(data.originalEvent.preventDefault(),data.originalEvent.stopPropagation()),e.stopPropagation()}))},shown=function(root){root.attr("data-seen")||root.find(SELECTORS_NO_COURSES_EMPTY_MESSAGE).length||(loadMoreCourses(root),root.attr("data-seen",!0))};return{init:function(root){(root=$(root)).find(SELECTORS_NO_COURSES_EMPTY_MESSAGE).length||(!function(root,time){root.data("last-event-load-time",time)}(root,Date.now()),root.hasClass("active")&&(loadMoreCourses(root),root.attr("data-seen",!0)),registerEventListeners(root))},reset:function(root){setOffset(root,0),function(root){root.find(SELECTORS_COURSE_ITEMS_LOADING_PLACEHOLDER).removeClass("hidden")}(root),function(root){root.find(SELECTORS_NO_COURSES_WITH_EVENTS_MESSAGE).addClass("hidden")}(root),root.removeAttr("data-seen"),root.hasClass("active")&&shown(root)},shown:shown}}));
+define("block_timeline/view_courses",["jquery","core/notification","core/custom_interaction_events","core/templates","block_timeline/event_list","core_course/repository","block_timeline/calendar_events_repository","core/pending"],(function($,Notification,CustomEvents,Templates,EventList,CourseRepository,EventsRepository,Pending){var SELECTORS={MORE_COURSES_BUTTON:'[data-action="more-courses"]',MORE_COURSES_BUTTON_CONTAINER:'[data-region="more-courses-button-container"]',NO_COURSES_EMPTY_MESSAGE:'[data-region="no-courses-empty-message"]',NO_COURSES_WITH_EVENTS_MESSAGE:'[data-region="no-events-empty-message"]',COURSES_LIST:'[data-region="courses-list"]',COURSE_ITEMS_LOADING_PLACEHOLDER:'[data-region="course-items-loading-placeholder"]',COURSE_EVENTS_CONTAINER:'[data-region="course-events-container"]',COURSE_NAME:'[data-region="course-name"]',LOADING_ICON:".loading-icon",TIMELINE_BLOCK:'[data-region="timeline"]',TIMELINE_SEARCH:'[data-action="search"]'},TEMPLATES_COURSE_ITEMS="block_timeline/course-items",TEMPLATES_LOADING_ICON="core/loading",COURSE_CLASSIFICATION="all",COURSE_SORT="fullname asc";const additionalConfig={courseview:!0};var hideLoadingPlaceholder=function(root){root.find(SELECTORS.COURSE_ITEMS_LOADING_PLACEHOLDER).addClass("hidden")};var hideMoreCoursesButton=function(root){root.find(SELECTORS.MORE_COURSES_BUTTON_CONTAINER).addClass("hidden")},showMoreCoursesButton=function(root){root.find(SELECTORS.MORE_COURSES_BUTTON_CONTAINER).removeClass("hidden")},disableMoreCoursesButtonLoading=function(root){var button=root.find(SELECTORS.MORE_COURSES_BUTTON);button.prop("disabled",!1),button.find(SELECTORS.LOADING_ICON).remove()};const showNoCoursesWithEventsMessage=function(root){const container=root.find(SELECTORS.COURSES_LIST);Templates.replaceNodeContents(container,"",""),root.find(SELECTORS.NO_COURSES_WITH_EVENTS_MESSAGE).removeClass("hidden")};var getOffset=function(root){return parseInt(root.attr("data-offset"),10)},setOffset=function(root,offset){root.attr("data-offset",offset)},getLimit=function(root){return parseInt(root.attr("data-limit"),10)},getDaysOffset=function(root){return parseInt(root.attr("data-days-offset"),10)},getDaysLimit=function(root){var daysLimit=root.attr("data-days-limit");return null!=daysLimit?parseInt(daysLimit,10):void 0},getMidnight=function(root){return parseInt(root.attr("data-midnight"),10)},getStartTime=function(root){return getMidnight(root)+86400*getDaysOffset(root)},getEndTime=function(root){let endTime=null;if(root.attr("data-filter-overdue"))endTime=Math.floor(Date.now()/1e3);else{const midnight=getMidnight(root),daysLimit=getDaysLimit(root);null!=daysLimit&&(endTime=midnight+86400*daysLimit)}return endTime},hasReloadedEventsSince=function(root,time){return function(root){return root.data("last-event-load-time")}(root)>time},loadEventsForCourses=function(courses,startTime,endTime,searchValue){return function(courseIds,startTime,limit,endTime,searchValue){var args={courseids:courseIds,starttime:startTime,limit:limit};return endTime&&(args.endtime=endTime),searchValue&&(args.searchvalue=searchValue),EventsRepository.queryByCourses(args)}(courses.map((function(course){return course.id})),startTime,6,endTime,searchValue)},updateDisplayFromCourses=function(courses,root,midnight,daysOffset,daysLimit,append){return Templates.render(TEMPLATES_COURSE_ITEMS,{courses:courses,midnight:midnight,hasdaysoffset:!0,hasdayslimit:null!=daysLimit,daysoffset:daysOffset,dayslimit:daysLimit,nodayslimit:null==daysLimit,courseview:!0,hascourses:!0}).then((function(html){return hideLoadingPlaceholder(root),html&&function(root,html){let append=arguments.length>2&&void 0!==arguments[2]&&arguments[2];var container=root.find(SELECTORS.COURSES_LIST);append?Templates.appendNodeContents(container,html,""):Templates.replaceNodeContents(container,html,"")}(root,html,append),html})).then((function(html){return courses.length<2?hideMoreCoursesButton(root):showMoreCoursesButton(root),html})).catch((function(){hideLoadingPlaceholder(root)}))},loadMoreCourses=function(root){let append=arguments.length>1&&void 0!==arguments[1]&&arguments[1];const pendingPromise=new Pending("block/timeline:load-more-courses");var offset=getOffset(root),limit=getLimit(root);const startTime=getStartTime(root),endTime=getEndTime(root),searchValue=root.closest(SELECTORS.TIMELINE_BLOCK).find(SELECTORS.TIMELINE_SEARCH).val();return CourseRepository.getEnrolledCoursesWithEventsByTimelineClassification(COURSE_CLASSIFICATION,limit,offset,COURSE_SORT,searchValue,startTime,endTime).then((function(result){var startEventLoadingTime=Date.now(),courses=result.courses,nextOffset=result.nextoffset,daysOffset=getDaysOffset(root),daysLimit=getDaysLimit(root),midnight=getMidnight(root);const moreCoursesAvailable=result.morecoursesavailable;setOffset(root,nextOffset);var eventsPromise=loadEventsForCourses(courses,startTime,endTime,searchValue),renderPromise=updateDisplayFromCourses(courses,root,midnight,daysOffset,daysLimit,append);return $.when(eventsPromise,renderPromise).then((function(eventsByCourse){return hasReloadedEventsSince(root,startEventLoadingTime)||(courses.length>0?(courses.forEach((function(course){const containerSelector='[data-region="course-events-container"][data-course-id="'+course.id+'"]',eventListRoot=root.find(containerSelector).find(EventList.rootSelector);EventList.init(eventListRoot,additionalConfig)})),moreCoursesAvailable?showMoreCoursesButton(root):hideMoreCoursesButton(root)):(hideMoreCoursesButton(root),0==offset&&showNoCoursesWithEventsMessage(root))),eventsByCourse}))})).then((()=>pendingPromise.resolve())).catch(Notification.exception)},registerEventListeners=function(root){CustomEvents.define(root,[CustomEvents.events.activate]),root.on(CustomEvents.events.activate,SELECTORS.MORE_COURSES_BUTTON,(function(e,data){!function(root){var button=root.find(SELECTORS.MORE_COURSES_BUTTON);button.prop("disabled",!0),Templates.render(TEMPLATES_LOADING_ICON,{}).then((function(html){return button.append(html),html})).catch((function(){return!1}))}(root),loadMoreCourses(root,!0).then((function(){disableMoreCoursesButtonLoading(root)})).catch((function(){disableMoreCoursesButtonLoading(root)})),data&&(data.originalEvent.preventDefault(),data.originalEvent.stopPropagation()),e.stopPropagation()}))},shown=function(root){root.attr("data-seen")||root.find(SELECTORS.NO_COURSES_EMPTY_MESSAGE).length||(loadMoreCourses(root),root.attr("data-seen",!0))};return{init:function(root){(root=$(root)).find(SELECTORS.NO_COURSES_EMPTY_MESSAGE).length||(!function(root,time){root.data("last-event-load-time",time)}(root,Date.now()),root.hasClass("active")&&(loadMoreCourses(root),root.attr("data-seen",!0)),registerEventListeners(root))},reset:function(root){setOffset(root,0),function(root){root.find(SELECTORS.COURSE_ITEMS_LOADING_PLACEHOLDER).removeClass("hidden")}(root),function(root){root.find(SELECTORS.NO_COURSES_WITH_EVENTS_MESSAGE).addClass("hidden")}(root),root.removeAttr("data-seen"),root.hasClass("active")&&shown(root)},shown:shown}}));
//# sourceMappingURL=view_courses.min.js.map
\ No newline at end of file
diff --git a/blocks/timeline/amd/build/view_courses.min.js.map b/blocks/timeline/amd/build/view_courses.min.js.map
index 7e81f9914dd..81a0d1a4881 100644
--- a/blocks/timeline/amd/build/view_courses.min.js.map
+++ b/blocks/timeline/amd/build/view_courses.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"view_courses.min.js","sources":["../src/view_courses.js"],"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 * Manage the timeline courses view for the timeline block.\n *\n * @copyright 2018 Ryan Wyllie \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\ndefine(\n[\n 'jquery',\n 'core/notification',\n 'core/custom_interaction_events',\n 'core/templates',\n 'block_timeline/event_list',\n 'core_course/repository',\n 'block_timeline/calendar_events_repository',\n 'core/pending'\n],\nfunction(\n $,\n Notification,\n CustomEvents,\n Templates,\n EventList,\n CourseRepository,\n EventsRepository,\n Pending\n) {\n\n var SELECTORS = {\n MORE_COURSES_BUTTON: '[data-action=\"more-courses\"]',\n MORE_COURSES_BUTTON_CONTAINER: '[data-region=\"more-courses-button-container\"]',\n NO_COURSES_EMPTY_MESSAGE: '[data-region=\"no-courses-empty-message\"]',\n NO_COURSES_WITH_EVENTS_MESSAGE: '[data-region=\"no-events-empty-message\"]',\n COURSES_LIST: '[data-region=\"courses-list\"]',\n COURSE_ITEMS_LOADING_PLACEHOLDER: '[data-region=\"course-items-loading-placeholder\"]',\n COURSE_EVENTS_CONTAINER: '[data-region=\"course-events-container\"]',\n COURSE_NAME: '[data-region=\"course-name\"]',\n LOADING_ICON: '.loading-icon',\n TIMELINE_BLOCK: '[data-region=\"timeline\"]',\n TIMELINE_SEARCH: '[data-action=\"search\"]'\n };\n\n var TEMPLATES = {\n COURSE_ITEMS: 'block_timeline/course-items',\n LOADING_ICON: 'core/loading'\n };\n\n var COURSE_CLASSIFICATION = 'all';\n var COURSE_SORT = 'fullname asc';\n var COURSE_EVENT_LIMIT = 5;\n var COURSE_LIMIT = 2;\n var SECONDS_IN_DAY = 60 * 60 * 24;\n\n const additionalConfig = {courseview: true};\n\n /**\n * Hide the loading placeholder elements.\n *\n * @param {object} root The rool element.\n */\n var hideLoadingPlaceholder = function(root) {\n root.find(SELECTORS.COURSE_ITEMS_LOADING_PLACEHOLDER).addClass('hidden');\n };\n\n /**\n * Show the loading placeholder elements.\n *\n * @param {object} root The rool element.\n */\n const showLoadingPlaceholder = function(root) {\n root.find(SELECTORS.COURSE_ITEMS_LOADING_PLACEHOLDER).removeClass('hidden');\n };\n\n /**\n * Hide the \"more courses\" button.\n *\n * @param {object} root The rool element.\n */\n var hideMoreCoursesButton = function(root) {\n root.find(SELECTORS.MORE_COURSES_BUTTON_CONTAINER).addClass('hidden');\n };\n\n /**\n * Show the \"more courses\" button.\n *\n * @param {object} root The rool element.\n */\n var showMoreCoursesButton = function(root) {\n root.find(SELECTORS.MORE_COURSES_BUTTON_CONTAINER).removeClass('hidden');\n };\n\n /**\n * Disable the \"more courses\" button and show the loading spinner.\n *\n * @param {object} root The rool element.\n */\n var enableMoreCoursesButtonLoading = function(root) {\n var button = root.find(SELECTORS.MORE_COURSES_BUTTON);\n button.prop('disabled', true);\n Templates.render(TEMPLATES.LOADING_ICON, {})\n .then(function(html) {\n button.append(html);\n return html;\n })\n .catch(function() {\n // It's not important if this false so just do so silently.\n return false;\n });\n };\n\n /**\n * Enable the \"more courses\" button and remove the loading spinner.\n *\n * @param {object} root The rool element.\n */\n var disableMoreCoursesButtonLoading = function(root) {\n var button = root.find(SELECTORS.MORE_COURSES_BUTTON);\n button.prop('disabled', false);\n button.find(SELECTORS.LOADING_ICON).remove();\n };\n\n /**\n * Display the message for when courses have no events available (within the current filtering).\n *\n * @param {object} root The rool element.\n */\n const showNoCoursesWithEventsMessage = function(root) {\n // Remove any course list contents, since we will display the no events message.\n const container = root.find(SELECTORS.COURSES_LIST);\n Templates.replaceNodeContents(container, '', '');\n root.find(SELECTORS.NO_COURSES_WITH_EVENTS_MESSAGE).removeClass('hidden');\n };\n\n /**\n * Hide the message for when courses have no events available (within the current filtering).\n *\n * @param {object} root The rool element.\n */\n const hideNoCoursesWithEventsMessage = function(root) {\n root.find(SELECTORS.NO_COURSES_WITH_EVENTS_MESSAGE).addClass('hidden');\n };\n\n /**\n * Render the course items HTML to the page.\n *\n * @param {object} root The rool element.\n * @param {string} html The course items HTML to render.\n * @param {boolean} append Whether the HTML should be appended (eg pressed \"show more courses\").\n * Defaults to false - replaces the existing content (eg when modifying filter values).\n */\n var renderCourseItemsHTML = function(root, html, append = false) {\n var container = root.find(SELECTORS.COURSES_LIST);\n\n if (append) {\n Templates.appendNodeContents(container, html, '');\n } else {\n Templates.replaceNodeContents(container, html, '');\n }\n };\n\n /**\n * Return the offset value for fetching courses.\n *\n * @param {object} root The rool element.\n * @return {Number}\n */\n var getOffset = function(root) {\n return parseInt(root.attr('data-offset'), 10);\n };\n\n /**\n * Set the offset value for fetching courses.\n *\n * @param {object} root The rool element.\n * @param {Number} offset Offset value.\n */\n var setOffset = function(root, offset) {\n root.attr('data-offset', offset);\n };\n\n /**\n * Return the limit value for fetching courses.\n *\n * @param {object} root The rool element.\n * @return {Number}\n */\n var getLimit = function(root) {\n return parseInt(root.attr('data-limit'), 10);\n };\n\n /**\n * Return the days offset value for fetching events.\n *\n * @param {object} root The rool element.\n * @return {Number}\n */\n var getDaysOffset = function(root) {\n return parseInt(root.attr('data-days-offset'), 10);\n };\n\n /**\n * Return the days limit value for fetching events. The days\n * limit is optional so undefined will be returned if it isn't\n * set.\n *\n * @param {object} root The rool element.\n * @return {int|undefined}\n */\n var getDaysLimit = function(root) {\n var daysLimit = root.attr('data-days-limit');\n return daysLimit != undefined ? parseInt(daysLimit, 10) : undefined;\n };\n\n /**\n * Return the timestamp for the user's midnight.\n *\n * @param {object} root The rool element.\n * @return {Number}\n */\n var getMidnight = function(root) {\n return parseInt(root.attr('data-midnight'), 10);\n };\n\n /**\n * Return the start time for fetching events. This is calculated\n * based on the user's midnight value so that timezones are\n * preserved.\n *\n * @param {object} root The rool element.\n * @return {Number}\n */\n var getStartTime = function(root) {\n var midnight = getMidnight(root);\n var daysOffset = getDaysOffset(root);\n return midnight + (daysOffset * SECONDS_IN_DAY);\n };\n\n /**\n * Return the end time for fetching events. This is calculated\n * based on the user's midnight value so that timezones are\n * preserved, unless filtering by overdue, where the current UNIX timestamp is used.\n *\n * @param {object} root The rool element.\n * @return {Number}\n */\n var getEndTime = function(root) {\n let endTime = null;\n\n if (root.attr('data-filter-overdue')) {\n // If filtering by overdue, end time will be the current timestamp in seconds.\n endTime = Math.floor(Date.now() / 1000);\n } else {\n const midnight = getMidnight(root);\n const daysLimit = getDaysLimit(root);\n\n if (daysLimit != undefined) {\n endTime = midnight + (daysLimit * SECONDS_IN_DAY);\n }\n }\n\n return endTime;\n };\n\n /**\n * Get a list of events for the given course ids. Returns a promise that will\n * be resolved with the events.\n *\n * @param {array} courseIds The list of course ids to fetch events for.\n * @param {Number} startTime Timestamp to fetch events from.\n * @param {Number} limit Limit to the number of events (this applies per course, not total)\n * @param {Number} endTime Timestamp to fetch events to.\n * @param {string|undefined} searchValue Search value\n * @return {object} jQuery promise.\n */\n var getEventsForCourseIds = function(courseIds, startTime, limit, endTime, searchValue) {\n var args = {\n courseids: courseIds,\n starttime: startTime,\n limit: limit\n };\n\n if (endTime) {\n args.endtime = endTime;\n }\n\n if (searchValue) {\n args.searchvalue = searchValue;\n }\n\n return EventsRepository.queryByCourses(args);\n };\n\n /**\n * Get the last time the events were reloaded.\n *\n * @param {object} root The rool element.\n * @return {Number}\n */\n var getEventReloadTime = function(root) {\n return root.data('last-event-load-time');\n };\n\n /**\n * Set the last time the events were reloaded.\n *\n * @param {object} root The rool element.\n * @param {Number} time Timestamp in milliseconds.\n */\n var setEventReloadTime = function(root, time) {\n root.data('last-event-load-time', time);\n };\n\n /**\n * Check if events have begun reloading since the given\n * time.\n *\n * @param {object} root The rool element.\n * @param {Number} time Timestamp in milliseconds.\n * @return {bool}\n */\n var hasReloadedEventsSince = function(root, time) {\n return getEventReloadTime(root) > time;\n };\n\n /**\n * Send a request to the server to load the events for the courses.\n *\n * @param {array} courses List of course objects.\n * @param {Number} startTime Timestamp to load events after.\n * @param {int|undefined} endTime Timestamp to load events up until.\n * @param {string|undefined} searchValue Search value\n * @return {object} jQuery promise resolved with the events.\n */\n var loadEventsForCourses = function(courses, startTime, endTime, searchValue) {\n var courseIds = courses.map(function(course) {\n return course.id;\n });\n\n return getEventsForCourseIds(courseIds, startTime, COURSE_EVENT_LIMIT + 1, endTime, searchValue);\n };\n\n /**\n * Render the courses in the DOM once the server has returned the courses.\n *\n * @param {array} courses List of course objects.\n * @param {object} root The root element\n * @param {Number} midnight The midnight timestamp in the user's timezone.\n * @param {Number} daysOffset Number of days from today to offset the events.\n * @param {Number} daysLimit Number of days from today to limit the events to.\n * @param {boolean} append Whether new content should be appended instead of replaced (eg \"show more courses\").\n * @return {object} jQuery promise resolved after rendering is complete.\n */\n var updateDisplayFromCourses = function(courses, root, midnight, daysOffset, daysLimit, append) {\n // Render the courses template.\n return Templates.render(TEMPLATES.COURSE_ITEMS, {\n courses: courses,\n midnight: midnight,\n hasdaysoffset: true,\n hasdayslimit: daysLimit != undefined,\n daysoffset: daysOffset,\n dayslimit: daysLimit,\n nodayslimit: daysLimit == undefined,\n courseview: true,\n hascourses: true\n }).then(function(html) {\n hideLoadingPlaceholder(root);\n\n if (html) {\n // Template rendering is complete and we have the HTML so we can\n // add it to the DOM.\n renderCourseItemsHTML(root, html, append);\n }\n\n return html;\n })\n .then(function(html) {\n if (courses.length < COURSE_LIMIT) {\n // We know there aren't any more courses because we got back less\n // than we asked for so hide the button to request more.\n hideMoreCoursesButton(root);\n } else {\n // Make sure the button is visible if there are more courses to load.\n showMoreCoursesButton(root);\n }\n\n return html;\n })\n .catch(function() {\n hideLoadingPlaceholder(root);\n });\n };\n\n /**\n * Find all of the visible course blocks and initialise the event\n * list module to being loading the events for the course block.\n *\n * @param {object} root The root element for the timeline courses view.\n * @param {boolean} append Whether content should be appended instead of replaced (eg \"show more courses\"). False by default.\n * @return {object} jQuery promise resolved with courses and events.\n */\n var loadMoreCourses = function(root, append = false) {\n const pendingPromise = new Pending('block/timeline:load-more-courses');\n var offset = getOffset(root);\n var limit = getLimit(root);\n const startTime = getStartTime(root);\n const endTime = getEndTime(root);\n const searchValue = root.closest(SELECTORS.TIMELINE_BLOCK).find(SELECTORS.TIMELINE_SEARCH).val();\n\n // Start loading the next set of courses.\n // Fetch up to limit number of courses with at least one action event in the time filtering specified.\n // Courses without events will also be fetched, but hidden in case they have events in other timespans.\n return CourseRepository.getEnrolledCoursesWithEventsByTimelineClassification(\n COURSE_CLASSIFICATION,\n limit,\n offset,\n COURSE_SORT,\n searchValue,\n startTime,\n endTime\n ).then(function(result) {\n var startEventLoadingTime = Date.now();\n var courses = result.courses;\n var nextOffset = result.nextoffset;\n var daysOffset = getDaysOffset(root);\n var daysLimit = getDaysLimit(root);\n var midnight = getMidnight(root);\n const moreCoursesAvailable = result.morecoursesavailable;\n\n // Record the next offset if we want to request more courses.\n setOffset(root, nextOffset);\n // Load the events for these courses.\n var eventsPromise = loadEventsForCourses(courses, startTime, endTime, searchValue);\n // Render the courses in the DOM.\n var renderPromise = updateDisplayFromCourses(courses, root, midnight, daysOffset, daysLimit, append);\n\n return $.when(eventsPromise, renderPromise)\n .then(function(eventsByCourse) {\n if (hasReloadedEventsSince(root, startEventLoadingTime)) {\n // All of the events are being reloaded so ignore our results.\n return eventsByCourse;\n }\n\n if (courses.length > 0) {\n // Render the events in the correct course event list.\n courses.forEach(function(course) {\n const courseId = course.id;\n const containerSelector = '[data-region=\"course-events-container\"][data-course-id=\"' + courseId + '\"]';\n const courseEventsContainer = root.find(containerSelector);\n const eventListRoot = courseEventsContainer.find(EventList.rootSelector);\n\n EventList.init(eventListRoot, additionalConfig);\n });\n\n if (!moreCoursesAvailable) {\n // If no more courses with events matching the current filtering exist, hide the more courses button.\n hideMoreCoursesButton(root);\n } else {\n // If more courses exist with events matching the current filtering, show the more courses button.\n showMoreCoursesButton(root);\n }\n } else {\n // No more courses to load, hide the more courses button.\n hideMoreCoursesButton(root);\n\n // A zero offset means this was not loading \"more courses\", so we need to display the no results message.\n if (offset == 0) {\n showNoCoursesWithEventsMessage(root);\n }\n }\n\n return eventsByCourse;\n });\n }).then(() => {\n return pendingPromise.resolve();\n }).catch(Notification.exception);\n };\n\n /**\n * Add event listeners to load more courses for the courses view.\n *\n * @param {object} root The root element for the timeline courses view.\n */\n var registerEventListeners = function(root) {\n CustomEvents.define(root, [CustomEvents.events.activate]);\n // Show more courses and load their events when the user clicks the \"more courses\" button.\n root.on(CustomEvents.events.activate, SELECTORS.MORE_COURSES_BUTTON, function(e, data) {\n enableMoreCoursesButtonLoading(root);\n loadMoreCourses(root, true)\n .then(function() {\n disableMoreCoursesButtonLoading(root);\n return;\n })\n .catch(function() {\n disableMoreCoursesButtonLoading(root);\n });\n\n if (data) {\n data.originalEvent.preventDefault();\n data.originalEvent.stopPropagation();\n }\n e.stopPropagation();\n });\n };\n\n /**\n * Initialise the timeline courses view. Begin loading the events\n * if this view is active. Add the relevant event listeners.\n *\n * This function should only be called once per page load because it\n * is adding event listeners to the page.\n *\n * @param {object} root The root element for the timeline courses view.\n */\n var init = function(root) {\n root = $(root);\n\n // Only need to handle course loading if the user is actively enrolled in a course.\n if (!root.find(SELECTORS.NO_COURSES_EMPTY_MESSAGE).length) {\n setEventReloadTime(root, Date.now());\n\n if (root.hasClass('active')) {\n // Only load if this is active otherwise it will be lazy loaded later.\n loadMoreCourses(root);\n root.attr('data-seen', true);\n }\n\n registerEventListeners(root);\n }\n };\n\n /**\n * Reset the element back to it's initial state. Begin loading the events again\n * if this view is active.\n *\n * @param {object} root The root element for the timeline courses view.\n */\n var reset = function(root) {\n\n setOffset(root, 0);\n showLoadingPlaceholder(root);\n hideNoCoursesWithEventsMessage(root);\n root.removeAttr('data-seen');\n\n if (root.hasClass('active')) {\n shown(root);\n }\n };\n\n /**\n * Begin loading the events unless we know there are no actively enrolled courses.\n *\n * @param {object} root The root element for the timeline courses view.\n */\n var shown = function(root) {\n if (!root.attr('data-seen') && !root.find(SELECTORS.NO_COURSES_EMPTY_MESSAGE).length) {\n loadMoreCourses(root);\n root.attr('data-seen', true);\n }\n };\n\n return {\n init: init,\n reset: reset,\n shown: shown\n };\n});\n"],"names":["define","$","Notification","CustomEvents","Templates","EventList","CourseRepository","EventsRepository","Pending","SELECTORS","TEMPLATES","additionalConfig","courseview","hideLoadingPlaceholder","root","find","addClass","hideMoreCoursesButton","showMoreCoursesButton","removeClass","disableMoreCoursesButtonLoading","button","prop","remove","showNoCoursesWithEventsMessage","container","replaceNodeContents","getOffset","parseInt","attr","setOffset","offset","getLimit","getDaysOffset","getDaysLimit","daysLimit","undefined","getMidnight","getStartTime","getEndTime","endTime","Math","floor","Date","now","midnight","hasReloadedEventsSince","time","data","getEventReloadTime","loadEventsForCourses","courses","startTime","searchValue","courseIds","limit","args","courseids","starttime","endtime","searchvalue","queryByCourses","getEventsForCourseIds","map","course","id","COURSE_EVENT_LIMIT","updateDisplayFromCourses","daysOffset","append","render","hasdaysoffset","hasdayslimit","daysoffset","dayslimit","nodayslimit","hascourses","then","html","appendNodeContents","renderCourseItemsHTML","length","catch","loadMoreCourses","pendingPromise","closest","val","getEnrolledCoursesWithEventsByTimelineClassification","result","startEventLoadingTime","nextOffset","nextoffset","moreCoursesAvailable","morecoursesavailable","eventsPromise","renderPromise","when","eventsByCourse","forEach","containerSelector","eventListRoot","rootSelector","init","resolve","exception","registerEventListeners","events","activate","on","e","enableMoreCoursesButtonLoading","originalEvent","preventDefault","stopPropagation","shown","setEventReloadTime","hasClass","reset","showLoadingPlaceholder","hideNoCoursesWithEventsMessage","removeAttr"],"mappings":"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"}
\ No newline at end of file
+{"version":3,"file":"view_courses.min.js","sources":["../src/view_courses.js"],"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 * Manage the timeline courses view for the timeline block.\n *\n * @copyright 2018 Ryan Wyllie \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\ndefine(\n[\n 'jquery',\n 'core/notification',\n 'core/custom_interaction_events',\n 'core/templates',\n 'block_timeline/event_list',\n 'core_course/repository',\n 'block_timeline/calendar_events_repository',\n 'core/pending'\n],\nfunction(\n $,\n Notification,\n CustomEvents,\n Templates,\n EventList,\n CourseRepository,\n EventsRepository,\n Pending\n) {\n\n var SELECTORS = {\n MORE_COURSES_BUTTON: '[data-action=\"more-courses\"]',\n MORE_COURSES_BUTTON_CONTAINER: '[data-region=\"more-courses-button-container\"]',\n NO_COURSES_EMPTY_MESSAGE: '[data-region=\"no-courses-empty-message\"]',\n NO_COURSES_WITH_EVENTS_MESSAGE: '[data-region=\"no-events-empty-message\"]',\n COURSES_LIST: '[data-region=\"courses-list\"]',\n COURSE_ITEMS_LOADING_PLACEHOLDER: '[data-region=\"course-items-loading-placeholder\"]',\n COURSE_EVENTS_CONTAINER: '[data-region=\"course-events-container\"]',\n COURSE_NAME: '[data-region=\"course-name\"]',\n LOADING_ICON: '.loading-icon',\n TIMELINE_BLOCK: '[data-region=\"timeline\"]',\n TIMELINE_SEARCH: '[data-action=\"search\"]'\n };\n\n var TEMPLATES = {\n COURSE_ITEMS: 'block_timeline/course-items',\n LOADING_ICON: 'core/loading'\n };\n\n var COURSE_CLASSIFICATION = 'all';\n var COURSE_SORT = 'fullname asc';\n var COURSE_EVENT_LIMIT = 5;\n var COURSE_LIMIT = 2;\n var SECONDS_IN_DAY = 60 * 60 * 24;\n\n const additionalConfig = {courseview: true};\n\n /**\n * Hide the loading placeholder elements.\n *\n * @param {object} root The rool element.\n */\n var hideLoadingPlaceholder = function(root) {\n root.find(SELECTORS.COURSE_ITEMS_LOADING_PLACEHOLDER).addClass('hidden');\n };\n\n /**\n * Show the loading placeholder elements.\n *\n * @param {object} root The rool element.\n */\n const showLoadingPlaceholder = function(root) {\n root.find(SELECTORS.COURSE_ITEMS_LOADING_PLACEHOLDER).removeClass('hidden');\n };\n\n /**\n * Hide the \"more courses\" button.\n *\n * @param {object} root The rool element.\n */\n var hideMoreCoursesButton = function(root) {\n root.find(SELECTORS.MORE_COURSES_BUTTON_CONTAINER).addClass('hidden');\n };\n\n /**\n * Show the \"more courses\" button.\n *\n * @param {object} root The rool element.\n */\n var showMoreCoursesButton = function(root) {\n root.find(SELECTORS.MORE_COURSES_BUTTON_CONTAINER).removeClass('hidden');\n };\n\n /**\n * Disable the \"more courses\" button and show the loading spinner.\n *\n * @param {object} root The rool element.\n */\n var enableMoreCoursesButtonLoading = function(root) {\n var button = root.find(SELECTORS.MORE_COURSES_BUTTON);\n button.prop('disabled', true);\n Templates.render(TEMPLATES.LOADING_ICON, {})\n .then(function(html) {\n button.append(html);\n return html;\n })\n .catch(function() {\n // It's not important if this false so just do so silently.\n return false;\n });\n };\n\n /**\n * Enable the \"more courses\" button and remove the loading spinner.\n *\n * @param {object} root The rool element.\n */\n var disableMoreCoursesButtonLoading = function(root) {\n var button = root.find(SELECTORS.MORE_COURSES_BUTTON);\n button.prop('disabled', false);\n button.find(SELECTORS.LOADING_ICON).remove();\n };\n\n /**\n * Display the message for when courses have no events available (within the current filtering).\n *\n * @param {object} root The rool element.\n */\n const showNoCoursesWithEventsMessage = function(root) {\n // Remove any course list contents, since we will display the no events message.\n const container = root.find(SELECTORS.COURSES_LIST);\n Templates.replaceNodeContents(container, '', '');\n root.find(SELECTORS.NO_COURSES_WITH_EVENTS_MESSAGE).removeClass('hidden');\n };\n\n /**\n * Hide the message for when courses have no events available (within the current filtering).\n *\n * @param {object} root The rool element.\n */\n const hideNoCoursesWithEventsMessage = function(root) {\n root.find(SELECTORS.NO_COURSES_WITH_EVENTS_MESSAGE).addClass('hidden');\n };\n\n /**\n * Render the course items HTML to the page.\n *\n * @param {object} root The rool element.\n * @param {string} html The course items HTML to render.\n * @param {boolean} append Whether the HTML should be appended (eg pressed \"show more courses\").\n * Defaults to false - replaces the existing content (eg when modifying filter values).\n */\n var renderCourseItemsHTML = function(root, html, append = false) {\n var container = root.find(SELECTORS.COURSES_LIST);\n\n if (append) {\n Templates.appendNodeContents(container, html, '');\n } else {\n Templates.replaceNodeContents(container, html, '');\n }\n };\n\n /**\n * Return the offset value for fetching courses.\n *\n * @param {object} root The rool element.\n * @return {Number}\n */\n var getOffset = function(root) {\n return parseInt(root.attr('data-offset'), 10);\n };\n\n /**\n * Set the offset value for fetching courses.\n *\n * @param {object} root The rool element.\n * @param {Number} offset Offset value.\n */\n var setOffset = function(root, offset) {\n root.attr('data-offset', offset);\n };\n\n /**\n * Return the limit value for fetching courses.\n *\n * @param {object} root The rool element.\n * @return {Number}\n */\n var getLimit = function(root) {\n return parseInt(root.attr('data-limit'), 10);\n };\n\n /**\n * Return the days offset value for fetching events.\n *\n * @param {object} root The rool element.\n * @return {Number}\n */\n var getDaysOffset = function(root) {\n return parseInt(root.attr('data-days-offset'), 10);\n };\n\n /**\n * Return the days limit value for fetching events. The days\n * limit is optional so undefined will be returned if it isn't\n * set.\n *\n * @param {object} root The rool element.\n * @return {int|undefined}\n */\n var getDaysLimit = function(root) {\n var daysLimit = root.attr('data-days-limit');\n return daysLimit != undefined ? parseInt(daysLimit, 10) : undefined;\n };\n\n /**\n * Return the timestamp for the user's midnight.\n *\n * @param {object} root The rool element.\n * @return {Number}\n */\n var getMidnight = function(root) {\n return parseInt(root.attr('data-midnight'), 10);\n };\n\n /**\n * Return the start time for fetching events. This is calculated\n * based on the user's midnight value so that timezones are\n * preserved.\n *\n * @param {object} root The rool element.\n * @return {Number}\n */\n var getStartTime = function(root) {\n var midnight = getMidnight(root);\n var daysOffset = getDaysOffset(root);\n return midnight + (daysOffset * SECONDS_IN_DAY);\n };\n\n /**\n * Return the end time for fetching events. This is calculated\n * based on the user's midnight value so that timezones are\n * preserved, unless filtering by overdue, where the current UNIX timestamp is used.\n *\n * @param {object} root The rool element.\n * @return {Number}\n */\n var getEndTime = function(root) {\n let endTime = null;\n\n if (root.attr('data-filter-overdue')) {\n // If filtering by overdue, end time will be the current timestamp in seconds.\n endTime = Math.floor(Date.now() / 1000);\n } else {\n const midnight = getMidnight(root);\n const daysLimit = getDaysLimit(root);\n\n if (daysLimit != undefined) {\n endTime = midnight + (daysLimit * SECONDS_IN_DAY);\n }\n }\n\n return endTime;\n };\n\n /**\n * Get a list of events for the given course ids. Returns a promise that will\n * be resolved with the events.\n *\n * @param {array} courseIds The list of course ids to fetch events for.\n * @param {Number} startTime Timestamp to fetch events from.\n * @param {Number} limit Limit to the number of events (this applies per course, not total)\n * @param {Number} endTime Timestamp to fetch events to.\n * @param {string|undefined} searchValue Search value\n * @return {object} jQuery promise.\n */\n var getEventsForCourseIds = function(courseIds, startTime, limit, endTime, searchValue) {\n var args = {\n courseids: courseIds,\n starttime: startTime,\n limit: limit\n };\n\n if (endTime) {\n args.endtime = endTime;\n }\n\n if (searchValue) {\n args.searchvalue = searchValue;\n }\n\n return EventsRepository.queryByCourses(args);\n };\n\n /**\n * Get the last time the events were reloaded.\n *\n * @param {object} root The rool element.\n * @return {Number}\n */\n var getEventReloadTime = function(root) {\n return root.data('last-event-load-time');\n };\n\n /**\n * Set the last time the events were reloaded.\n *\n * @param {object} root The rool element.\n * @param {Number} time Timestamp in milliseconds.\n */\n var setEventReloadTime = function(root, time) {\n root.data('last-event-load-time', time);\n };\n\n /**\n * Check if events have begun reloading since the given\n * time.\n *\n * @param {object} root The rool element.\n * @param {Number} time Timestamp in milliseconds.\n * @return {bool}\n */\n var hasReloadedEventsSince = function(root, time) {\n return getEventReloadTime(root) > time;\n };\n\n /**\n * Send a request to the server to load the events for the courses.\n *\n * @param {array} courses List of course objects.\n * @param {Number} startTime Timestamp to load events after.\n * @param {int|undefined} endTime Timestamp to load events up until.\n * @param {string|undefined} searchValue Search value\n * @return {object} jQuery promise resolved with the events.\n */\n var loadEventsForCourses = function(courses, startTime, endTime, searchValue) {\n var courseIds = courses.map(function(course) {\n return course.id;\n });\n\n return getEventsForCourseIds(courseIds, startTime, COURSE_EVENT_LIMIT + 1, endTime, searchValue);\n };\n\n /**\n * Render the courses in the DOM once the server has returned the courses.\n *\n * @param {array} courses List of course objects.\n * @param {object} root The root element\n * @param {Number} midnight The midnight timestamp in the user's timezone.\n * @param {Number} daysOffset Number of days from today to offset the events.\n * @param {Number} daysLimit Number of days from today to limit the events to.\n * @param {boolean} append Whether new content should be appended instead of replaced (eg \"show more courses\").\n * @return {object} jQuery promise resolved after rendering is complete.\n */\n var updateDisplayFromCourses = function(courses, root, midnight, daysOffset, daysLimit, append) {\n // Render the courses template.\n return Templates.render(TEMPLATES.COURSE_ITEMS, {\n courses: courses,\n midnight: midnight,\n hasdaysoffset: true,\n hasdayslimit: daysLimit != undefined,\n daysoffset: daysOffset,\n dayslimit: daysLimit,\n nodayslimit: daysLimit == undefined,\n courseview: true,\n hascourses: true\n }).then(function(html) {\n hideLoadingPlaceholder(root);\n\n if (html) {\n // Template rendering is complete and we have the HTML so we can\n // add it to the DOM.\n renderCourseItemsHTML(root, html, append);\n }\n\n return html;\n })\n .then(function(html) {\n if (courses.length < COURSE_LIMIT) {\n // We know there aren't any more courses because we got back less\n // than we asked for so hide the button to request more.\n hideMoreCoursesButton(root);\n } else {\n // Make sure the button is visible if there are more courses to load.\n showMoreCoursesButton(root);\n }\n\n return html;\n })\n .catch(function() {\n hideLoadingPlaceholder(root);\n });\n };\n\n /**\n * Find all of the visible course blocks and initialise the event\n * list module to being loading the events for the course block.\n *\n * @param {object} root The root element for the timeline courses view.\n * @param {boolean} append Whether content should be appended instead of replaced (eg \"show more courses\"). False by default.\n * @return {object} jQuery promise resolved with courses and events.\n */\n var loadMoreCourses = function(root, append = false) {\n const pendingPromise = new Pending('block/timeline:load-more-courses');\n var offset = getOffset(root);\n var limit = getLimit(root);\n const startTime = getStartTime(root);\n const endTime = getEndTime(root);\n const searchValue = root.closest(SELECTORS.TIMELINE_BLOCK).find(SELECTORS.TIMELINE_SEARCH).val();\n\n // Start loading the next set of courses.\n // Fetch up to limit number of courses with at least one action event in the time filtering specified.\n // Courses without events will also be fetched, but hidden in case they have events in other timespans.\n return CourseRepository.getEnrolledCoursesWithEventsByTimelineClassification(\n COURSE_CLASSIFICATION,\n limit,\n offset,\n COURSE_SORT,\n searchValue,\n startTime,\n endTime\n ).then(function(result) {\n var startEventLoadingTime = Date.now();\n var courses = result.courses;\n var nextOffset = result.nextoffset;\n var daysOffset = getDaysOffset(root);\n var daysLimit = getDaysLimit(root);\n var midnight = getMidnight(root);\n const moreCoursesAvailable = result.morecoursesavailable;\n\n // Record the next offset if we want to request more courses.\n setOffset(root, nextOffset);\n // Load the events for these courses.\n var eventsPromise = loadEventsForCourses(courses, startTime, endTime, searchValue);\n // Render the courses in the DOM.\n var renderPromise = updateDisplayFromCourses(courses, root, midnight, daysOffset, daysLimit, append);\n\n return $.when(eventsPromise, renderPromise)\n .then(function(eventsByCourse) {\n if (hasReloadedEventsSince(root, startEventLoadingTime)) {\n // All of the events are being reloaded so ignore our results.\n return eventsByCourse;\n }\n\n if (courses.length > 0) {\n // Render the events in the correct course event list.\n courses.forEach(function(course) {\n const courseId = course.id;\n const containerSelector = '[data-region=\"course-events-container\"][data-course-id=\"' + courseId + '\"]';\n const courseEventsContainer = root.find(containerSelector);\n const eventListRoot = courseEventsContainer.find(EventList.rootSelector);\n\n EventList.init(eventListRoot, additionalConfig);\n });\n\n if (!moreCoursesAvailable) {\n // If no more courses with events matching the current filtering exist, hide the more courses button.\n hideMoreCoursesButton(root);\n } else {\n // If more courses exist with events matching the current filtering, show the more courses button.\n showMoreCoursesButton(root);\n }\n } else {\n // No more courses to load, hide the more courses button.\n hideMoreCoursesButton(root);\n\n // A zero offset means this was not loading \"more courses\", so we need to display the no results message.\n if (offset == 0) {\n showNoCoursesWithEventsMessage(root);\n }\n }\n\n return eventsByCourse;\n });\n }).then(() => {\n return pendingPromise.resolve();\n }).catch(Notification.exception);\n };\n\n /**\n * Add event listeners to load more courses for the courses view.\n *\n * @param {object} root The root element for the timeline courses view.\n */\n var registerEventListeners = function(root) {\n CustomEvents.define(root, [CustomEvents.events.activate]);\n // Show more courses and load their events when the user clicks the \"more courses\" button.\n root.on(CustomEvents.events.activate, SELECTORS.MORE_COURSES_BUTTON, function(e, data) {\n enableMoreCoursesButtonLoading(root);\n loadMoreCourses(root, true)\n .then(function() {\n disableMoreCoursesButtonLoading(root);\n return;\n })\n .catch(function() {\n disableMoreCoursesButtonLoading(root);\n });\n\n if (data) {\n data.originalEvent.preventDefault();\n data.originalEvent.stopPropagation();\n }\n e.stopPropagation();\n });\n };\n\n /**\n * Initialise the timeline courses view. Begin loading the events\n * if this view is active. Add the relevant event listeners.\n *\n * This function should only be called once per page load because it\n * is adding event listeners to the page.\n *\n * @param {object} root The root element for the timeline courses view.\n */\n var init = function(root) {\n root = $(root);\n\n // Only need to handle course loading if the user is actively enrolled in a course.\n if (!root.find(SELECTORS.NO_COURSES_EMPTY_MESSAGE).length) {\n setEventReloadTime(root, Date.now());\n\n if (root.hasClass('active')) {\n // Only load if this is active otherwise it will be lazy loaded later.\n loadMoreCourses(root);\n root.attr('data-seen', true);\n }\n\n registerEventListeners(root);\n }\n };\n\n /**\n * Reset the element back to it's initial state. Begin loading the events again\n * if this view is active.\n *\n * @param {object} root The root element for the timeline courses view.\n */\n var reset = function(root) {\n\n setOffset(root, 0);\n showLoadingPlaceholder(root);\n hideNoCoursesWithEventsMessage(root);\n root.removeAttr('data-seen');\n\n if (root.hasClass('active')) {\n shown(root);\n }\n };\n\n /**\n * Begin loading the events unless we know there are no actively enrolled courses.\n *\n * @param {object} root The root element for the timeline courses view.\n */\n var shown = function(root) {\n if (!root.attr('data-seen') && !root.find(SELECTORS.NO_COURSES_EMPTY_MESSAGE).length) {\n loadMoreCourses(root);\n root.attr('data-seen', true);\n }\n };\n\n return {\n init: init,\n reset: reset,\n shown: shown\n };\n});\n"],"names":["define","$","Notification","CustomEvents","Templates","EventList","CourseRepository","EventsRepository","Pending","SELECTORS","MORE_COURSES_BUTTON","MORE_COURSES_BUTTON_CONTAINER","NO_COURSES_EMPTY_MESSAGE","NO_COURSES_WITH_EVENTS_MESSAGE","COURSES_LIST","COURSE_ITEMS_LOADING_PLACEHOLDER","COURSE_EVENTS_CONTAINER","COURSE_NAME","LOADING_ICON","TIMELINE_BLOCK","TIMELINE_SEARCH","TEMPLATES","COURSE_CLASSIFICATION","COURSE_SORT","additionalConfig","courseview","hideLoadingPlaceholder","root","find","addClass","hideMoreCoursesButton","showMoreCoursesButton","removeClass","disableMoreCoursesButtonLoading","button","prop","remove","showNoCoursesWithEventsMessage","container","replaceNodeContents","getOffset","parseInt","attr","setOffset","offset","getLimit","getDaysOffset","getDaysLimit","daysLimit","undefined","getMidnight","getStartTime","getEndTime","endTime","Math","floor","Date","now","midnight","hasReloadedEventsSince","time","data","getEventReloadTime","loadEventsForCourses","courses","startTime","searchValue","courseIds","limit","args","courseids","starttime","endtime","searchvalue","queryByCourses","getEventsForCourseIds","map","course","id","COURSE_EVENT_LIMIT","updateDisplayFromCourses","daysOffset","append","render","hasdaysoffset","hasdayslimit","daysoffset","dayslimit","nodayslimit","hascourses","then","html","arguments","length","appendNodeContents","renderCourseItemsHTML","catch","loadMoreCourses","pendingPromise","closest","val","getEnrolledCoursesWithEventsByTimelineClassification","result","startEventLoadingTime","nextOffset","nextoffset","moreCoursesAvailable","morecoursesavailable","eventsPromise","renderPromise","when","eventsByCourse","forEach","containerSelector","eventListRoot","rootSelector","init","resolve","exception","registerEventListeners","events","activate","on","e","enableMoreCoursesButtonLoading","originalEvent","preventDefault","stopPropagation","shown","setEventReloadTime","hasClass","reset","showLoadingPlaceholder","hideNoCoursesWithEventsMessage","removeAttr"],"mappings":";;;;;;AAsBAA,OACA,8BAAA,CACI,SACA,oBACA,iCACA,iBACA,4BACA,yBACA,4CACA,iBAEJ,SACIC,EACAC,aACAC,aACAC,UACAC,UACAC,iBACAC,iBACAC,SAGA,IAAIC,UAAY,CACZC,oBAAqB,+BACrBC,8BAA+B,gDAC/BC,yBAA0B,2CAC1BC,+BAAgC,0CAChCC,aAAc,+BACdC,iCAAkC,mDAClCC,wBAAyB,0CACzBC,YAAa,8BACbC,aAAc,gBACdC,eAAgB,2BAChBC,gBAAiB,0BAGjBC,uBACc,8BADdA,uBAEc,eAGdC,sBAAwB,MACxBC,YAAc,eAKlB,MAAMC,iBAAmB,CAACC,YAAY,GAOtC,IAAIC,uBAAyB,SAASC,MAClCA,KAAKC,KAAKnB,UAAUM,kCAAkCc,SAAS,WAiBnE,IAAIC,sBAAwB,SAASH,MACjCA,KAAKC,KAAKnB,UAAUE,+BAA+BkB,SAAS,WAQ5DE,sBAAwB,SAASJ,MACjCA,KAAKC,KAAKnB,UAAUE,+BAA+BqB,YAAY,WA2B/DC,gCAAkC,SAASN,MAC3C,IAAIO,OAASP,KAAKC,KAAKnB,UAAUC,qBACjCwB,OAAOC,KAAK,YAAY,GACxBD,OAAON,KAAKnB,UAAUS,cAAckB,UAQxC,MAAMC,+BAAiC,SAASV,MAE5C,MAAMW,UAAYX,KAAKC,KAAKnB,UAAUK,cACtCV,UAAUmC,oBAAoBD,UAAW,GAAI,IAC7CX,KAAKC,KAAKnB,UAAUI,gCAAgCmB,YAAY,WAoBpE,IAgBIQ,UAAY,SAASb,MACrB,OAAOc,SAASd,KAAKe,KAAK,eAAgB,KAS1CC,UAAY,SAAShB,KAAMiB,QAC3BjB,KAAKe,KAAK,cAAeE,SASzBC,SAAW,SAASlB,MACpB,OAAOc,SAASd,KAAKe,KAAK,cAAe,KASzCI,cAAgB,SAASnB,MACzB,OAAOc,SAASd,KAAKe,KAAK,oBAAqB,KAW/CK,aAAe,SAASpB,MACxB,IAAIqB,UAAYrB,KAAKe,KAAK,mBAC1B,OAAoBO,MAAbD,UAAyBP,SAASO,UAAW,SAAMC,GAS1DC,YAAc,SAASvB,MACvB,OAAOc,SAASd,KAAKe,KAAK,iBAAkB,KAW5CS,aAAe,SAASxB,MAGxB,OAFeuB,YAAYvB,MArLV,MAsLAmB,cAAcnB,OAY/ByB,WAAa,SAASzB,MACtB,IAAI0B,QAAU,KAEd,GAAI1B,KAAKe,KAAK,uBAEVW,QAAUC,KAAKC,MAAMC,KAAKC,MAAQ,SAC/B,CACH,MAAMC,SAAWR,YAAYvB,MACvBqB,UAAYD,aAAapB,MAEdsB,MAAbD,YACAK,QAAUK,SA7MD,MA6MaV,UAE9B,CAEA,OAAOK,SA4DPM,uBAAyB,SAAShC,KAAMiC,MACxC,OAvBqB,SAASjC,MAC9B,OAAOA,KAAKkC,KAAK,wBAsBVC,CAAmBnC,MAAQiC,MAYlCG,qBAAuB,SAASC,QAASC,UAAWZ,QAASa,aAK7D,OAhEwB,SAASC,UAAWF,UAAWG,MAAOf,QAASa,aACvE,IAAIG,KAAO,CACPC,UAAWH,UACXI,UAAWN,UACXG,MAAOA,OAWX,OARIf,UACAgB,KAAKG,QAAUnB,SAGfa,cACAG,KAAKI,YAAcP,aAGhB3D,iBAAiBmE,eAAeL,MAiDhCM,CAJSX,QAAQY,KAAI,SAASC,QACjC,OAAOA,OAAOC,EAClB,IAEwCb,UAAWc,EAAwB1B,QAASa,cAcpFc,yBAA2B,SAAShB,QAASrC,KAAM+B,SAAUuB,WAAYjC,UAAWkC,QAEpF,OAAO9E,UAAU+E,OAAO9D,uBAAwB,CAC5C2C,QAASA,QACTN,SAAUA,SACV0B,eAAe,EACfC,aAA2BpC,MAAbD,UACdsC,WAAYL,WACZM,UAAWvC,UACXwC,YAA0BvC,MAAbD,UACbvB,YAAY,EACZgE,YAAY,IACbC,MAAK,SAASC,MASb,OARAjE,uBAAuBC,MAEnBgE,MAzNgB,SAAShE,KAAMgE,MAAsB,IAAhBT,OAAMU,UAAAC,OAAA,QAAA5C,IAAA2C,UAAA,IAAAA,UAAA,GACnD,IAAItD,UAAYX,KAAKC,KAAKnB,UAAUK,cAEhCoE,OACA9E,UAAU0F,mBAAmBxD,UAAWqD,KAAM,IAE9CvF,UAAUmC,oBAAoBD,UAAWqD,KAAM,IAsN3CI,CAAsBpE,KAAMgE,KAAMT,QAG/BS,IACX,IACCD,MAAK,SAASC,MAUX,OATI3B,QAAQ6B,OAtUD,EAyUP/D,sBAAsBH,MAGtBI,sBAAsBJ,MAGnBgE,IACX,IACCK,OAAM,WACHtE,uBAAuBC,KAC3B,KAWAsE,gBAAkB,SAAStE,MAAsB,IAAhBuD,OAAMU,UAAAC,OAAA,QAAA5C,IAAA2C,UAAA,IAAAA,UAAA,GACvC,MAAMM,eAAiB,IAAI1F,QAAQ,oCACnC,IAAIoC,OAASJ,UAAUb,MACnByC,MAAQvB,SAASlB,MACrB,MAAMsC,UAAYd,aAAaxB,MACzB0B,QAAUD,WAAWzB,MACrBuC,YAAcvC,KAAKwE,QAAQ1F,UAAUU,gBAAgBS,KAAKnB,UAAUW,iBAAiBgF,MAK3F,OAAO9F,iBAAiB+F,qDACpB/E,sBACA8C,MACAxB,OACArB,YACA2C,YACAD,UACAZ,SACFqC,MAAK,SAASY,QACZ,IAAIC,sBAAwB/C,KAAKC,MAC7BO,QAAUsC,OAAOtC,QACjBwC,WAAaF,OAAOG,WACpBxB,WAAanC,cAAcnB,MAC3BqB,UAAYD,aAAapB,MACzB+B,SAAWR,YAAYvB,MAC3B,MAAM+E,qBAAuBJ,OAAOK,qBAGpChE,UAAUhB,KAAM6E,YAEhB,IAAII,cAAgB7C,qBAAqBC,QAASC,UAAWZ,QAASa,aAElE2C,cAAgB7B,yBAAyBhB,QAASrC,KAAM+B,SAAUuB,WAAYjC,UAAWkC,QAE7F,OAAOjF,EAAE6G,KAAKF,cAAeC,eACxBnB,MAAK,SAASqB,gBACX,OAAIpD,uBAAuBhC,KAAM4E,yBAK7BvC,QAAQ6B,OAAS,GAEjB7B,QAAQgD,SAAQ,SAASnC,QACrB,MACMoC,kBAAoB,2DADTpC,OAAOC,GAC0E,KAE5FoC,cADwBvF,KAAKC,KAAKqF,mBACIrF,KAAKvB,UAAU8G,cAE3D9G,UAAU+G,KAAKF,cAAe1F,iBAClC,IAEKkF,qBAKD3E,sBAAsBJ,MAHtBG,sBAAsBH,QAO1BG,sBAAsBH,MAGR,GAAViB,QACAP,+BAA+BV,QA3B5BoF,cAgCf,GACR,IAAGrB,MAAK,IACGQ,eAAemB,YACvBrB,MAAM9F,aAAaoH,YAQtBC,uBAAyB,SAAS5F,MAClCxB,aAAaH,OAAO2B,KAAM,CAACxB,aAAaqH,OAAOC,WAE/C9F,KAAK+F,GAAGvH,aAAaqH,OAAOC,SAAUhH,UAAUC,qBAAqB,SAASiH,EAAG9D,OArYhD,SAASlC,MAC1C,IAAIO,OAASP,KAAKC,KAAKnB,UAAUC,qBACjCwB,OAAOC,KAAK,YAAY,GACxB/B,UAAU+E,OAAO9D,uBAAwB,CAAA,GACpCqE,MAAK,SAASC,MAEX,OADAzD,OAAOgD,OAAOS,MACPA,IACX,IACCK,OAAM,WAEH,OAAO,CACX,IA2XA4B,CAA+BjG,MAC/BsE,gBAAgBtE,MAAM,GACjB+D,MAAK,WACFzD,gCAAgCN,KAEpC,IACCqE,OAAM,WACH/D,gCAAgCN,KACpC,IAEAkC,OACAA,KAAKgE,cAAcC,iBACnBjE,KAAKgE,cAAcE,mBAEvBJ,EAAEI,iBACN,KAoDAC,MAAQ,SAASrG,MACZA,KAAKe,KAAK,cAAiBf,KAAKC,KAAKnB,UAAUG,0BAA0BiF,SAC1EI,gBAAgBtE,MAChBA,KAAKe,KAAK,aAAa,KAI/B,MAAO,CACH0E,KAhDO,SAASzF,OAChBA,KAAO1B,EAAE0B,OAGCC,KAAKnB,UAAUG,0BAA0BiF,UAjN9B,SAASlE,KAAMiC,MACpCjC,KAAKkC,KAAK,uBAAwBD,MAiN9BqE,CAAmBtG,KAAM6B,KAAKC,OAE1B9B,KAAKuG,SAAS,YAEdjC,gBAAgBtE,MAChBA,KAAKe,KAAK,aAAa,IAG3B6E,uBAAuB5F,QAoC3BwG,MA1BQ,SAASxG,MAEjBgB,UAAUhB,KAAM,GArdW,SAASA,MACpCA,KAAKC,KAAKnB,UAAUM,kCAAkCiB,YAAY,UAqdlEoG,CAAuBzG,MAjZY,SAASA,MAC5CA,KAAKC,KAAKnB,UAAUI,gCAAgCgB,SAAS,UAiZ7DwG,CAA+B1G,MAC/BA,KAAK2G,WAAW,aAEZ3G,KAAKuG,SAAS,WACdF,MAAMrG,OAmBVqG,MAAOA,MAEf"}
\ No newline at end of file
diff --git a/blocks/timeline/amd/build/view_dates.min.js.map b/blocks/timeline/amd/build/view_dates.min.js.map
index 209dac7dccf..e9ec00e31db 100644
--- a/blocks/timeline/amd/build/view_dates.min.js.map
+++ b/blocks/timeline/amd/build/view_dates.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"view_dates.min.js","sources":["../src/view_dates.js"],"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 * Manage the timeline dates view for the timeline block.\n *\n * @copyright 2018 Ryan Wyllie \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\ndefine(\n[\n 'jquery',\n 'block_timeline/event_list',\n 'core/pubsub',\n 'core/paged_content_events'\n],\nfunction(\n $,\n EventList,\n PubSub,\n PagedContentEvents\n) {\n\n var SELECTORS = {\n EVENT_LIST_CONTAINER: '[data-region=\"event-list-container\"]',\n NO_COURSES_EMPTY_MESSAGE: '[data-region=\"no-courses-empty-message\"]',\n };\n\n /**\n * Setup the listeners for the timeline block\n *\n * @param {string} root view dates container\n * @param {string} namespace The namespace for the paged content\n */\n var registerEventListeners = function(root, namespace) {\n var event = namespace + PagedContentEvents.SET_ITEMS_PER_PAGE_LIMIT;\n PubSub.subscribe(event, function(limit) {\n $(root).data('limit', limit);\n });\n };\n\n /**\n * Initialise the event list and being loading the events.\n *\n * @param {object} root The root element for the timeline dates view.\n */\n var load = function(root) {\n\n if (!root.find(SELECTORS.NO_COURSES_EMPTY_MESSAGE).length) {\n var eventListContainer = root.find(SELECTORS.EVENT_LIST_CONTAINER);\n var namespace = $(eventListContainer).attr('id') + \"user_block_timeline\" + Math.random();\n registerEventListeners(root, namespace);\n\n var config = {\n persistentLimitKey: \"block_timeline_user_limit_preference\",\n eventNamespace: namespace\n };\n EventList.init(eventListContainer, config);\n }\n };\n\n /**\n * Initialise the timeline dates view. Begin loading the events\n * if this view is active.\n *\n * @param {object} root The root element for the timeline courses view.\n */\n var init = function(root) {\n root = $(root);\n\n // Only need to handle events loading if the user is actively enrolled in a course and this view is active.\n if (root.hasClass('active') && !root.find(SELECTORS.NO_COURSES_EMPTY_MESSAGE).length) {\n load(root);\n root.attr('data-seen', true);\n }\n };\n\n /**\n * Reset the view back to it's initial state. If this view is active then\n * beging loading the events.\n *\n * @param {object} root The root element for the timeline courses view.\n */\n var reset = function(root) {\n root.removeAttr('data-seen');\n if (root.hasClass('active')) {\n load(root);\n root.attr('data-seen', true);\n }\n };\n\n /**\n * Load the events if this is the first time the view is displayed.\n *\n * @param {object} root The root element for the timeline courses view.\n */\n var shown = function(root) {\n if (!root.attr('data-seen')) {\n load(root);\n root.attr('data-seen', true);\n }\n };\n\n return {\n init: init,\n reset: reset,\n shown: shown\n };\n});\n"],"names":["define","$","EventList","PubSub","PagedContentEvents","SELECTORS","load","root","find","length","eventListContainer","namespace","attr","Math","random","event","SET_ITEMS_PER_PAGE_LIMIT","subscribe","limit","data","registerEventListeners","config","persistentLimitKey","eventNamespace","init","hasClass","reset","removeAttr","shown"],"mappings":";;;;;;AAsBAA,mCACA,CACI,SACA,4BACA,cACA,8BAEJ,SACIC,EACAC,UACAC,OACAC,wBAGIC,+BACsB,uCADtBA,mCAE0B,2CAqB1BC,KAAO,SAASC,UAEXA,KAAKC,KAAKH,oCAAoCI,OAAQ,KACnDC,mBAAqBH,KAAKC,KAAKH,gCAC/BM,UAAYV,EAAES,oBAAoBE,KAAK,MAAQ,sBAAwBC,KAAKC,UAhB3D,SAASP,KAAMI,eACpCI,MAAQJ,UAAYP,mBAAmBY,yBAC3Cb,OAAOc,UAAUF,OAAO,SAASG,OAC7BjB,EAAEM,MAAMY,KAAK,QAASD,UActBE,CAAuBb,KAAMI,eAEzBU,OAAS,CACTC,mBAAoB,uCACpBC,eAAgBZ,WAEpBT,UAAUsB,KAAKd,mBAAoBW,gBA8CpC,CACHG,KArCO,SAASjB,OAChBA,KAAON,EAAEM,OAGAkB,SAAS,YAAclB,KAAKC,KAAKH,oCAAoCI,SAC1EH,KAAKC,MACLA,KAAKK,KAAK,aAAa,KAgC3Bc,MAtBQ,SAASnB,MACjBA,KAAKoB,WAAW,aACZpB,KAAKkB,SAAS,YACdnB,KAAKC,MACLA,KAAKK,KAAK,aAAa,KAmB3BgB,MAVQ,SAASrB,MACZA,KAAKK,KAAK,eACXN,KAAKC,MACLA,KAAKK,KAAK,aAAa"}
\ No newline at end of file
+{"version":3,"file":"view_dates.min.js","sources":["../src/view_dates.js"],"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 * Manage the timeline dates view for the timeline block.\n *\n * @copyright 2018 Ryan Wyllie \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\ndefine(\n[\n 'jquery',\n 'block_timeline/event_list',\n 'core/pubsub',\n 'core/paged_content_events'\n],\nfunction(\n $,\n EventList,\n PubSub,\n PagedContentEvents\n) {\n\n var SELECTORS = {\n EVENT_LIST_CONTAINER: '[data-region=\"event-list-container\"]',\n NO_COURSES_EMPTY_MESSAGE: '[data-region=\"no-courses-empty-message\"]',\n };\n\n /**\n * Setup the listeners for the timeline block\n *\n * @param {string} root view dates container\n * @param {string} namespace The namespace for the paged content\n */\n var registerEventListeners = function(root, namespace) {\n var event = namespace + PagedContentEvents.SET_ITEMS_PER_PAGE_LIMIT;\n PubSub.subscribe(event, function(limit) {\n $(root).data('limit', limit);\n });\n };\n\n /**\n * Initialise the event list and being loading the events.\n *\n * @param {object} root The root element for the timeline dates view.\n */\n var load = function(root) {\n\n if (!root.find(SELECTORS.NO_COURSES_EMPTY_MESSAGE).length) {\n var eventListContainer = root.find(SELECTORS.EVENT_LIST_CONTAINER);\n var namespace = $(eventListContainer).attr('id') + \"user_block_timeline\" + Math.random();\n registerEventListeners(root, namespace);\n\n var config = {\n persistentLimitKey: \"block_timeline_user_limit_preference\",\n eventNamespace: namespace\n };\n EventList.init(eventListContainer, config);\n }\n };\n\n /**\n * Initialise the timeline dates view. Begin loading the events\n * if this view is active.\n *\n * @param {object} root The root element for the timeline courses view.\n */\n var init = function(root) {\n root = $(root);\n\n // Only need to handle events loading if the user is actively enrolled in a course and this view is active.\n if (root.hasClass('active') && !root.find(SELECTORS.NO_COURSES_EMPTY_MESSAGE).length) {\n load(root);\n root.attr('data-seen', true);\n }\n };\n\n /**\n * Reset the view back to it's initial state. If this view is active then\n * beging loading the events.\n *\n * @param {object} root The root element for the timeline courses view.\n */\n var reset = function(root) {\n root.removeAttr('data-seen');\n if (root.hasClass('active')) {\n load(root);\n root.attr('data-seen', true);\n }\n };\n\n /**\n * Load the events if this is the first time the view is displayed.\n *\n * @param {object} root The root element for the timeline courses view.\n */\n var shown = function(root) {\n if (!root.attr('data-seen')) {\n load(root);\n root.attr('data-seen', true);\n }\n };\n\n return {\n init: init,\n reset: reset,\n shown: shown\n };\n});\n"],"names":["define","$","EventList","PubSub","PagedContentEvents","SELECTORS","load","root","find","length","eventListContainer","namespace","attr","Math","random","event","SET_ITEMS_PER_PAGE_LIMIT","subscribe","limit","data","registerEventListeners","config","persistentLimitKey","eventNamespace","init","hasClass","reset","removeAttr","shown"],"mappings":";;;;;;AAsBAA,mCACA,CACI,SACA,4BACA,cACA,8BAEJ,SACIC,EACAC,UACAC,OACAC,oBAGA,IAAIC,+BACsB,uCADtBA,mCAE0B,2CAqB1BC,KAAO,SAASC,MAEhB,IAAKA,KAAKC,KAAKH,oCAAoCI,OAAQ,CACvD,IAAIC,mBAAqBH,KAAKC,KAAKH,gCAC/BM,UAAYV,EAAES,oBAAoBE,KAAK,MAAQ,sBAAwBC,KAAKC,UAhB3D,SAASP,KAAMI,WACxC,IAAII,MAAQJ,UAAYP,mBAAmBY,yBAC3Cb,OAAOc,UAAUF,OAAO,SAASG,OAC7BjB,EAAEM,MAAMY,KAAK,QAASD,MAC1B,IAaIE,CAAuBb,KAAMI,WAE7B,IAAIU,OAAS,CACTC,mBAAoB,uCACpBC,eAAgBZ,WAEpBT,UAAUsB,KAAKd,mBAAoBW,OACvC,GA6CJ,MAAO,CACHG,KArCO,SAASjB,OAChBA,KAAON,EAAEM,OAGAkB,SAAS,YAAclB,KAAKC,KAAKH,oCAAoCI,SAC1EH,KAAKC,MACLA,KAAKK,KAAK,aAAa,KAgC3Bc,MAtBQ,SAASnB,MACjBA,KAAKoB,WAAW,aACZpB,KAAKkB,SAAS,YACdnB,KAAKC,MACLA,KAAKK,KAAK,aAAa,KAmB3BgB,MAVQ,SAASrB,MACZA,KAAKK,KAAK,eACXN,KAAKC,MACLA,KAAKK,KAAK,aAAa,KASnC"}
\ No newline at end of file
diff --git a/blocks/timeline/amd/build/view_nav.min.js.map b/blocks/timeline/amd/build/view_nav.min.js.map
index c84536dfd6f..cd90dad6e60 100644
--- a/blocks/timeline/amd/build/view_nav.min.js.map
+++ b/blocks/timeline/amd/build/view_nav.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"view_nav.min.js","sources":["../src/view_nav.js"],"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 * Manage the timeline view navigation for the timeline block.\n *\n * @copyright 2018 Ryan Wyllie \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\ndefine(\n[\n 'jquery',\n 'core/custom_interaction_events',\n 'block_timeline/view',\n 'core/ajax',\n 'core/notification',\n 'core/utils'\n],\nfunction(\n $,\n CustomEvents,\n View,\n Ajax,\n Notification,\n Utils\n) {\n\n var SELECTORS = {\n TIMELINE_DAY_FILTER: '[data-region=\"day-filter\"]',\n TIMELINE_DAY_FILTER_OPTION: '[data-from]',\n TIMELINE_VIEW_SELECTOR: '[data-region=\"view-selector\"]',\n DATA_DAYS_OFFSET: '[data-days-offset]',\n DATA_DAYS_LIMIT: '[data-days-limit]',\n TIMELINE_SEARCH_INPUT: '[data-action=\"search\"]',\n TIMELINE_SEARCH_CLEAR_ICON: '[data-action=\"clearsearch\"]',\n NO_COURSES_EMPTY_MESSAGE: '[data-region=\"no-courses-empty-message\"]',\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 */\n var updateUserPreferences = function(type, value) {\n var request = {\n methodname: 'core_user_update_user_preferences',\n args: {\n preferences: [\n {\n type: type,\n value: value\n }\n ]\n }\n };\n\n Ajax.call([request])[0]\n .fail(Notification.exception);\n };\n\n /**\n * Event listener for the day selector (\"Next 7 days\", \"Next 30 days\", etc).\n *\n * @param {object} root The root element for the timeline block\n * @param {object} timelineViewRoot The root element for the timeline view\n */\n var registerTimelineDaySelector = function(root, timelineViewRoot) {\n var timelineDaySelectorContainer = root.find(SELECTORS.TIMELINE_DAY_FILTER);\n\n CustomEvents.define(timelineDaySelectorContainer, [CustomEvents.events.activate]);\n timelineDaySelectorContainer.on(\n CustomEvents.events.activate,\n SELECTORS.TIMELINE_DAY_FILTER_OPTION,\n function(e, data) {\n // Update the user preference\n var filtername = $(e.currentTarget).data('filtername');\n var type = 'block_timeline_user_filter_preference';\n updateUserPreferences(type, filtername);\n\n var option = $(e.target).closest(SELECTORS.TIMELINE_DAY_FILTER_OPTION);\n\n if (option.attr('aria-current') == 'true') {\n // If it's already active then we don't need to do anything.\n return;\n }\n\n var daysOffset = option.attr('data-from');\n var daysLimit = option.attr('data-to');\n var elementsWithDaysOffset = root.find(SELECTORS.DATA_DAYS_OFFSET);\n\n elementsWithDaysOffset.attr('data-days-offset', daysOffset);\n\n if (daysLimit != undefined) {\n elementsWithDaysOffset.attr('data-days-limit', daysLimit);\n } else {\n elementsWithDaysOffset.removeAttr('data-days-limit');\n }\n\n if (option.attr('data-filtername') === 'overdue') {\n elementsWithDaysOffset.attr('data-filter-overdue', true);\n } else {\n elementsWithDaysOffset.removeAttr('data-filter-overdue');\n }\n\n // Reset the views to reinitialise the event lists now that we've\n // updated the day limits.\n View.reset(timelineViewRoot);\n\n data.originalEvent.preventDefault();\n }\n );\n };\n\n /**\n * Event listener for the \"sort\" button in the timeline navigation that allows for\n * changing between the timeline dates and courses views.\n *\n * On a view change we tell the timeline view module that the view has been shown\n * so that it can handle how to display the appropriate view.\n *\n * @param {object} root The root element for the timeline block\n * @param {object} timelineViewRoot The root element for the timeline view\n */\n var registerViewSelector = function(root, timelineViewRoot) {\n var viewSelector = root.find(SELECTORS.TIMELINE_VIEW_SELECTOR);\n\n // Listen for when the user changes tab so that we can show the first set of courses\n // and load their events when they request the sort by courses view for the first time.\n viewSelector.on('shown shown.bs.tab', function(e) {\n View.shown(timelineViewRoot);\n $(e.target).removeClass('active');\n });\n\n\n // Event selector for user_sort\n CustomEvents.define(viewSelector, [CustomEvents.events.activate]);\n viewSelector.on(CustomEvents.events.activate, \"[data-toggle='tab']\", function(e) {\n var filtername = $(e.currentTarget).data('filtername');\n var type = 'block_timeline_user_sort_preference';\n updateUserPreferences(type, filtername);\n });\n };\n\n /**\n * Event listener for the \"search\" input field in the timeline navigation that allows for\n * searching the activity name, course name and activity type.\n *\n * @param {object} root The root element for the timeline block\n * @param {object} timelineViewRoot The root element for the timeline view\n */\n const registerSearch = (root, timelineViewRoot) => {\n const searchInput = root.find(SELECTORS.TIMELINE_SEARCH_INPUT);\n const clearSearchIcon = root.find(SELECTORS.TIMELINE_SEARCH_CLEAR_ICON);\n searchInput.on('input', Utils.debounce(() => {\n if (searchInput.val() !== '') {\n activeSearchState(clearSearchIcon, timelineViewRoot);\n } else {\n clearSearchState(clearSearchIcon, timelineViewRoot);\n }\n }, 1000));\n clearSearchIcon.on('click', () => {\n searchInput.val('');\n clearSearchState(clearSearchIcon, timelineViewRoot);\n searchInput.focus();\n });\n };\n\n /**\n * Show the clear search icon.\n *\n * @param {object} clearSearchIcon Clear search icon element.\n * @param {object} timelineViewRoot The root element for the timeline view\n */\n const activeSearchState = (clearSearchIcon, timelineViewRoot) => {\n clearSearchIcon.removeClass('d-none');\n View.reset(timelineViewRoot);\n };\n\n /**\n * Hide the clear search icon.\n *\n * @param {object} clearSearchIcon Clear search icon element.\n * @param {object} timelineViewRoot The root element for the timeline view\n */\n const clearSearchState = (clearSearchIcon, timelineViewRoot) => {\n clearSearchIcon.addClass('d-none');\n View.reset(timelineViewRoot);\n };\n\n /**\n * Initialise the timeline view navigation by adding event listeners to\n * the navigation elements.\n *\n * @param {object} root The root element for the timeline block\n * @param {object} timelineViewRoot The root element for the timeline view\n */\n var init = function(root, timelineViewRoot) {\n root = $(root);\n\n registerViewSelector(root, timelineViewRoot);\n\n // Only need to handle filtering if the user is actively enrolled in a course.\n if (!root.find(SELECTORS.NO_COURSES_EMPTY_MESSAGE).length) {\n registerTimelineDaySelector(root, timelineViewRoot);\n registerSearch(root, timelineViewRoot);\n }\n };\n\n return {\n init: init\n };\n});\n"],"names":["define","$","CustomEvents","View","Ajax","Notification","Utils","SELECTORS","updateUserPreferences","type","value","request","methodname","args","preferences","call","fail","exception","activeSearchState","clearSearchIcon","timelineViewRoot","removeClass","reset","clearSearchState","addClass","init","root","viewSelector","find","on","e","shown","target","events","activate","filtername","currentTarget","data","registerViewSelector","length","timelineDaySelectorContainer","option","closest","attr","daysOffset","daysLimit","elementsWithDaysOffset","undefined","removeAttr","originalEvent","preventDefault","registerTimelineDaySelector","searchInput","debounce","val","focus","registerSearch"],"mappings":";;;;;;AAsBAA,iCACA,CACI,SACA,iCACA,sBACA,YACA,oBACA,eAEJ,SACIC,EACAC,aACAC,KACAC,KACAC,aACAC,WAGIC,8BACqB,6BADrBA,qCAE4B,cAF5BA,iCAGwB,gCAHxBA,2BAIkB,qBAJlBA,gCAMuB,yBANvBA,qCAO4B,8BAP5BA,mCAQ0B,2CAS1BC,sBAAwB,SAASC,KAAMC,WACnCC,QAAU,CACVC,WAAY,oCACZC,KAAM,CACFC,YAAa,CACT,CACIL,KAAMA,KACNC,MAAOA,UAMvBN,KAAKW,KAAK,CAACJ,UAAU,GAChBK,KAAKX,aAAaY,kBAoHrBC,kBAAoB,CAACC,gBAAiBC,oBACxCD,gBAAgBE,YAAY,UAC5BlB,KAAKmB,MAAMF,mBASTG,iBAAmB,CAACJ,gBAAiBC,oBACvCD,gBAAgBK,SAAS,UACzBrB,KAAKmB,MAAMF,yBAsBR,CACHK,KAbO,SAASC,KAAMN,mBAzEC,SAASM,KAAMN,sBAClCO,aAAeD,KAAKE,KAAKrB,kCAI7BoB,aAAaE,GAAG,sBAAsB,SAASC,GAC3C3B,KAAK4B,MAAMX,kBACXnB,EAAE6B,EAAEE,QAAQX,YAAY,aAK5BnB,aAAaF,OAAO2B,aAAc,CAACzB,aAAa+B,OAAOC,WACvDP,aAAaE,GAAG3B,aAAa+B,OAAOC,SAAU,uBAAuB,SAASJ,OACtEK,WAAalC,EAAE6B,EAAEM,eAAeC,KAAK,cAEzC7B,sBADW,sCACiB2B,gBA4DhCG,CAFAZ,KAAOzB,EAAEyB,MAEkBN,kBAGtBM,KAAKE,KAAKrB,oCAAoCgC,SAxIrB,SAASb,KAAMN,sBACzCoB,6BAA+Bd,KAAKE,KAAKrB,+BAE7CL,aAAaF,OAAOwC,6BAA8B,CAACtC,aAAa+B,OAAOC,WACvEM,6BAA6BX,GACzB3B,aAAa+B,OAAOC,SACpB3B,sCACA,SAASuB,EAAGO,UAEJF,WAAalC,EAAE6B,EAAEM,eAAeC,KAAK,cAEzC7B,sBADW,wCACiB2B,gBAExBM,OAASxC,EAAE6B,EAAEE,QAAQU,QAAQnC,yCAEE,QAA/BkC,OAAOE,KAAK,qBAKZC,WAAaH,OAAOE,KAAK,aACzBE,UAAYJ,OAAOE,KAAK,WACxBG,uBAAyBpB,KAAKE,KAAKrB,4BAEvCuC,uBAAuBH,KAAK,mBAAoBC,YAE/BG,MAAbF,UACAC,uBAAuBH,KAAK,kBAAmBE,WAE/CC,uBAAuBE,WAAW,mBAGC,YAAnCP,OAAOE,KAAK,mBACZG,uBAAuBH,KAAK,uBAAuB,GAEnDG,uBAAuBE,WAAW,uBAKtC7C,KAAKmB,MAAMF,kBAEXiB,KAAKY,cAAcC,qBA+FvBC,CAA4BzB,KAAMN,kBArDnB,EAACM,KAAMN,0BACpBgC,YAAc1B,KAAKE,KAAKrB,iCACxBY,gBAAkBO,KAAKE,KAAKrB,sCAClC6C,YAAYvB,GAAG,QAASvB,MAAM+C,UAAS,KACT,KAAtBD,YAAYE,MACZpC,kBAAkBC,gBAAiBC,kBAEnCG,iBAAiBJ,gBAAiBC,oBAEvC,MACHD,gBAAgBU,GAAG,SAAS,KACxBuB,YAAYE,IAAI,IAChB/B,iBAAiBJ,gBAAiBC,kBAClCgC,YAAYG,YAyCZC,CAAe9B,KAAMN"}
\ No newline at end of file
+{"version":3,"file":"view_nav.min.js","sources":["../src/view_nav.js"],"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 * Manage the timeline view navigation for the timeline block.\n *\n * @copyright 2018 Ryan Wyllie \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\ndefine(\n[\n 'jquery',\n 'core/custom_interaction_events',\n 'block_timeline/view',\n 'core/ajax',\n 'core/notification',\n 'core/utils'\n],\nfunction(\n $,\n CustomEvents,\n View,\n Ajax,\n Notification,\n Utils\n) {\n\n var SELECTORS = {\n TIMELINE_DAY_FILTER: '[data-region=\"day-filter\"]',\n TIMELINE_DAY_FILTER_OPTION: '[data-from]',\n TIMELINE_VIEW_SELECTOR: '[data-region=\"view-selector\"]',\n DATA_DAYS_OFFSET: '[data-days-offset]',\n DATA_DAYS_LIMIT: '[data-days-limit]',\n TIMELINE_SEARCH_INPUT: '[data-action=\"search\"]',\n TIMELINE_SEARCH_CLEAR_ICON: '[data-action=\"clearsearch\"]',\n NO_COURSES_EMPTY_MESSAGE: '[data-region=\"no-courses-empty-message\"]',\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 */\n var updateUserPreferences = function(type, value) {\n var request = {\n methodname: 'core_user_update_user_preferences',\n args: {\n preferences: [\n {\n type: type,\n value: value\n }\n ]\n }\n };\n\n Ajax.call([request])[0]\n .fail(Notification.exception);\n };\n\n /**\n * Event listener for the day selector (\"Next 7 days\", \"Next 30 days\", etc).\n *\n * @param {object} root The root element for the timeline block\n * @param {object} timelineViewRoot The root element for the timeline view\n */\n var registerTimelineDaySelector = function(root, timelineViewRoot) {\n var timelineDaySelectorContainer = root.find(SELECTORS.TIMELINE_DAY_FILTER);\n\n CustomEvents.define(timelineDaySelectorContainer, [CustomEvents.events.activate]);\n timelineDaySelectorContainer.on(\n CustomEvents.events.activate,\n SELECTORS.TIMELINE_DAY_FILTER_OPTION,\n function(e, data) {\n // Update the user preference\n var filtername = $(e.currentTarget).data('filtername');\n var type = 'block_timeline_user_filter_preference';\n updateUserPreferences(type, filtername);\n\n var option = $(e.target).closest(SELECTORS.TIMELINE_DAY_FILTER_OPTION);\n\n if (option.attr('aria-current') == 'true') {\n // If it's already active then we don't need to do anything.\n return;\n }\n\n var daysOffset = option.attr('data-from');\n var daysLimit = option.attr('data-to');\n var elementsWithDaysOffset = root.find(SELECTORS.DATA_DAYS_OFFSET);\n\n elementsWithDaysOffset.attr('data-days-offset', daysOffset);\n\n if (daysLimit != undefined) {\n elementsWithDaysOffset.attr('data-days-limit', daysLimit);\n } else {\n elementsWithDaysOffset.removeAttr('data-days-limit');\n }\n\n if (option.attr('data-filtername') === 'overdue') {\n elementsWithDaysOffset.attr('data-filter-overdue', true);\n } else {\n elementsWithDaysOffset.removeAttr('data-filter-overdue');\n }\n\n // Reset the views to reinitialise the event lists now that we've\n // updated the day limits.\n View.reset(timelineViewRoot);\n\n data.originalEvent.preventDefault();\n }\n );\n };\n\n /**\n * Event listener for the \"sort\" button in the timeline navigation that allows for\n * changing between the timeline dates and courses views.\n *\n * On a view change we tell the timeline view module that the view has been shown\n * so that it can handle how to display the appropriate view.\n *\n * @param {object} root The root element for the timeline block\n * @param {object} timelineViewRoot The root element for the timeline view\n */\n var registerViewSelector = function(root, timelineViewRoot) {\n var viewSelector = root.find(SELECTORS.TIMELINE_VIEW_SELECTOR);\n\n // Listen for when the user changes tab so that we can show the first set of courses\n // and load their events when they request the sort by courses view for the first time.\n viewSelector.on('shown shown.bs.tab', function(e) {\n View.shown(timelineViewRoot);\n $(e.target).removeClass('active');\n });\n\n\n // Event selector for user_sort\n CustomEvents.define(viewSelector, [CustomEvents.events.activate]);\n viewSelector.on(CustomEvents.events.activate, \"[data-toggle='tab']\", function(e) {\n var filtername = $(e.currentTarget).data('filtername');\n var type = 'block_timeline_user_sort_preference';\n updateUserPreferences(type, filtername);\n });\n };\n\n /**\n * Event listener for the \"search\" input field in the timeline navigation that allows for\n * searching the activity name, course name and activity type.\n *\n * @param {object} root The root element for the timeline block\n * @param {object} timelineViewRoot The root element for the timeline view\n */\n const registerSearch = (root, timelineViewRoot) => {\n const searchInput = root.find(SELECTORS.TIMELINE_SEARCH_INPUT);\n const clearSearchIcon = root.find(SELECTORS.TIMELINE_SEARCH_CLEAR_ICON);\n searchInput.on('input', Utils.debounce(() => {\n if (searchInput.val() !== '') {\n activeSearchState(clearSearchIcon, timelineViewRoot);\n } else {\n clearSearchState(clearSearchIcon, timelineViewRoot);\n }\n }, 1000));\n clearSearchIcon.on('click', () => {\n searchInput.val('');\n clearSearchState(clearSearchIcon, timelineViewRoot);\n searchInput.focus();\n });\n };\n\n /**\n * Show the clear search icon.\n *\n * @param {object} clearSearchIcon Clear search icon element.\n * @param {object} timelineViewRoot The root element for the timeline view\n */\n const activeSearchState = (clearSearchIcon, timelineViewRoot) => {\n clearSearchIcon.removeClass('d-none');\n View.reset(timelineViewRoot);\n };\n\n /**\n * Hide the clear search icon.\n *\n * @param {object} clearSearchIcon Clear search icon element.\n * @param {object} timelineViewRoot The root element for the timeline view\n */\n const clearSearchState = (clearSearchIcon, timelineViewRoot) => {\n clearSearchIcon.addClass('d-none');\n View.reset(timelineViewRoot);\n };\n\n /**\n * Initialise the timeline view navigation by adding event listeners to\n * the navigation elements.\n *\n * @param {object} root The root element for the timeline block\n * @param {object} timelineViewRoot The root element for the timeline view\n */\n var init = function(root, timelineViewRoot) {\n root = $(root);\n\n registerViewSelector(root, timelineViewRoot);\n\n // Only need to handle filtering if the user is actively enrolled in a course.\n if (!root.find(SELECTORS.NO_COURSES_EMPTY_MESSAGE).length) {\n registerTimelineDaySelector(root, timelineViewRoot);\n registerSearch(root, timelineViewRoot);\n }\n };\n\n return {\n init: init\n };\n});\n"],"names":["define","$","CustomEvents","View","Ajax","Notification","Utils","SELECTORS","updateUserPreferences","type","value","request","methodname","args","preferences","call","fail","exception","activeSearchState","clearSearchIcon","timelineViewRoot","removeClass","reset","clearSearchState","addClass","init","root","viewSelector","find","on","e","shown","target","events","activate","filtername","currentTarget","data","registerViewSelector","length","timelineDaySelectorContainer","option","closest","attr","daysOffset","daysLimit","elementsWithDaysOffset","undefined","removeAttr","originalEvent","preventDefault","registerTimelineDaySelector","registerSearch","searchInput","debounce","val","focus"],"mappings":";;;;;;AAsBAA,OAAM,0BACN,CACI,SACA,iCACA,sBACA,YACA,oBACA,eAEJ,SACIC,EACAC,aACAC,KACAC,KACAC,aACAC,OAGA,IAAIC,8BACqB,6BADrBA,qCAE4B,cAF5BA,iCAGwB,gCAHxBA,2BAIkB,qBAJlBA,gCAMuB,yBANvBA,qCAO4B,8BAP5BA,mCAQ0B,2CAS1BC,sBAAwB,SAASC,KAAMC,OACvC,IAAIC,QAAU,CACVC,WAAY,oCACZC,KAAM,CACFC,YAAa,CACT,CACIL,KAAMA,KACNC,MAAOA,UAMvBN,KAAKW,KAAK,CAACJ,UAAU,GAChBK,KAAKX,aAAaY,YA6F3B,MAuBMC,kBAAoBA,CAACC,gBAAiBC,oBACxCD,gBAAgBE,YAAY,UAC5BlB,KAAKmB,MAAMF,iBAAiB,EAS1BG,iBAAmBA,CAACJ,gBAAiBC,oBACvCD,gBAAgBK,SAAS,UACzBrB,KAAKmB,MAAMF,iBAAiB,EAsBhC,MAAO,CACHK,KAbO,SAASC,KAAMN,mBAzEC,SAASM,KAAMN,kBACtC,IAAIO,aAAeD,KAAKE,KAAKrB,kCAI7BoB,aAAaE,GAAG,sBAAsB,SAASC,GAC3C3B,KAAK4B,MAAMX,kBACXnB,EAAE6B,EAAEE,QAAQX,YAAY,SAC5B,IAIAnB,aAAaF,OAAO2B,aAAc,CAACzB,aAAa+B,OAAOC,WACvDP,aAAaE,GAAG3B,aAAa+B,OAAOC,SAAU,uBAAuB,SAASJ,GAC1E,IAAIK,WAAalC,EAAE6B,EAAEM,eAAeC,KAAK,cAEzC7B,sBADW,sCACiB2B,WAChC,KA2DAG,CAFAZ,KAAOzB,EAAEyB,MAEkBN,kBAGtBM,KAAKE,KAAKrB,oCAAoCgC,SAxIrB,SAASb,KAAMN,kBAC7C,IAAIoB,6BAA+Bd,KAAKE,KAAKrB,+BAE7CL,aAAaF,OAAOwC,6BAA8B,CAACtC,aAAa+B,OAAOC,WACvEM,6BAA6BX,GACzB3B,aAAa+B,OAAOC,SACpB3B,sCACA,SAASuB,EAAGO,MAER,IAAIF,WAAalC,EAAE6B,EAAEM,eAAeC,KAAK,cAEzC7B,sBADW,wCACiB2B,YAE5B,IAAIM,OAASxC,EAAE6B,EAAEE,QAAQU,QAAQnC,sCAEjC,GAAmC,QAA/BkC,OAAOE,KAAK,gBAAhB,CAKA,IAAIC,WAAaH,OAAOE,KAAK,aACzBE,UAAYJ,OAAOE,KAAK,WACxBG,uBAAyBpB,KAAKE,KAAKrB,4BAEvCuC,uBAAuBH,KAAK,mBAAoBC,YAE/BG,MAAbF,UACAC,uBAAuBH,KAAK,kBAAmBE,WAE/CC,uBAAuBE,WAAW,mBAGC,YAAnCP,OAAOE,KAAK,mBACZG,uBAAuBH,KAAK,uBAAuB,GAEnDG,uBAAuBE,WAAW,uBAKtC7C,KAAKmB,MAAMF,kBAEXiB,KAAKY,cAAcC,gBAxBnB,CAyBJ,IA8FAC,CAA4BzB,KAAMN,kBArDnBgC,EAAC1B,KAAMN,oBAC1B,MAAMiC,YAAc3B,KAAKE,KAAKrB,iCACxBY,gBAAkBO,KAAKE,KAAKrB,sCAClC8C,YAAYxB,GAAG,QAASvB,MAAMgD,UAAS,KACT,KAAtBD,YAAYE,MACZrC,kBAAkBC,gBAAiBC,kBAEnCG,iBAAiBJ,gBAAiBC,iBACtC,GACD,MACHD,gBAAgBU,GAAG,SAAS,KACxBwB,YAAYE,IAAI,IAChBhC,iBAAiBJ,gBAAiBC,kBAClCiC,YAAYG,OAAO,GACrB,EAwCEJ,CAAe1B,KAAMN,oBAOjC"}
\ No newline at end of file
diff --git a/calendar/amd/build/calendar.min.js.map b/calendar/amd/build/calendar.min.js.map
index 363129b3ed2..35cb8e0e006 100644
--- a/calendar/amd/build/calendar.min.js.map
+++ b/calendar/amd/build/calendar.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"calendar.min.js","sources":["../src/calendar.js"],"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 * This module is the highest level module for the calendar. It is\n * responsible for initialising all of the components required for\n * the calendar to run. It also coordinates the interaction between\n * components by listening for and responding to different events\n * triggered within the calendar UI.\n *\n * @module core_calendar/calendar\n * @copyright 2017 Simey Lameze \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine([\n 'jquery',\n 'core/ajax',\n 'core/str',\n 'core/templates',\n 'core/notification',\n 'core/custom_interaction_events',\n 'core/modal_events',\n 'core/modal_factory',\n 'core_calendar/modal_event_form',\n 'core_calendar/summary_modal',\n 'core_calendar/repository',\n 'core_calendar/events',\n 'core_calendar/view_manager',\n 'core_calendar/crud',\n 'core_calendar/selectors',\n 'core/config',\n ],\n function(\n $,\n Ajax,\n Str,\n Templates,\n Notification,\n CustomEvents,\n ModalEvents,\n ModalFactory,\n ModalEventForm,\n SummaryModal,\n CalendarRepository,\n CalendarEvents,\n CalendarViewManager,\n CalendarCrud,\n CalendarSelectors,\n Config,\n ) {\n\n var SELECTORS = {\n ROOT: \"[data-region='calendar']\",\n DAY: \"[data-region='day']\",\n NEW_EVENT_BUTTON: \"[data-action='new-event-button']\",\n DAY_CONTENT: \"[data-region='day-content']\",\n LOADING_ICON: '.loading-icon',\n VIEW_DAY_LINK: \"[data-action='view-day-link']\",\n CALENDAR_MONTH_WRAPPER: \".calendarwrapper\",\n TODAY: '.today',\n DAY_NUMBER_CIRCLE: '.day-number-circle',\n DAY_NUMBER: '.day-number'\n };\n\n /**\n * Handler for the drag and drop move event. Provides a loading indicator\n * while the request is sent to the server to update the event start date.\n *\n * Triggers a eventMoved calendar javascript event if the event was successfully\n * updated.\n *\n * @param {event} e The calendar move event\n * @param {int} eventId The event id being moved\n * @param {object|null} originElement The jQuery element for where the event is moving from\n * @param {object} destinationElement The jQuery element for where the event is moving to\n */\n var handleMoveEvent = function(e, eventId, originElement, destinationElement) {\n var originTimestamp = null;\n var destinationTimestamp = destinationElement.attr('data-day-timestamp');\n\n if (originElement) {\n originTimestamp = originElement.attr('data-day-timestamp');\n }\n\n // If the event has actually changed day.\n if (!originElement || originTimestamp != destinationTimestamp) {\n Templates.render('core/loading', {})\n .then(function(html, js) {\n // First we show some loading icons in each of the days being affected.\n destinationElement.find(SELECTORS.DAY_CONTENT).addClass('hidden');\n Templates.appendNodeContents(destinationElement, html, js);\n\n if (originElement) {\n originElement.find(SELECTORS.DAY_CONTENT).addClass('hidden');\n Templates.appendNodeContents(originElement, html, js);\n }\n return;\n })\n .then(function() {\n // Send a request to the server to make the change.\n return CalendarRepository.updateEventStartDay(eventId, destinationTimestamp);\n })\n .then(function() {\n // If the update was successful then broadcast an event letting the calendar\n // know that an event has been moved.\n $('body').trigger(CalendarEvents.eventMoved, [eventId, originElement, destinationElement]);\n return;\n })\n .always(function() {\n // Always remove the loading icons regardless of whether the update\n // request was successful or not.\n var destinationLoadingElement = destinationElement.find(SELECTORS.LOADING_ICON);\n destinationElement.find(SELECTORS.DAY_CONTENT).removeClass('hidden');\n Templates.replaceNode(destinationLoadingElement, '', '');\n\n if (originElement) {\n var originLoadingElement = originElement.find(SELECTORS.LOADING_ICON);\n originElement.find(SELECTORS.DAY_CONTENT).removeClass('hidden');\n Templates.replaceNode(originLoadingElement, '', '');\n }\n return;\n })\n .fail(Notification.exception);\n }\n };\n\n /**\n * Listen to and handle any calendar events fired by the calendar UI.\n *\n * @method registerCalendarEventListeners\n * @param {object} root The calendar root element\n * @param {object} eventFormModalPromise A promise reolved with the event form modal\n */\n var registerCalendarEventListeners = function(root, eventFormModalPromise) {\n var body = $('body');\n\n body.on(CalendarEvents.created, function() {\n CalendarViewManager.reloadCurrentMonth(root);\n });\n body.on(CalendarEvents.deleted, function() {\n CalendarViewManager.reloadCurrentMonth(root);\n });\n body.on(CalendarEvents.updated, function() {\n CalendarViewManager.reloadCurrentMonth(root);\n });\n body.on(CalendarEvents.editActionEvent, function(e, url) {\n // Action events needs to be edit directly on the course module.\n window.location.assign(url);\n });\n // Handle the event fired by the drag and drop code.\n body.on(CalendarEvents.moveEvent, handleMoveEvent);\n // When an event is successfully moved we should updated the UI.\n body.on(CalendarEvents.eventMoved, function() {\n CalendarViewManager.reloadCurrentMonth(root);\n });\n\n CalendarCrud.registerEditListeners(root, eventFormModalPromise);\n };\n\n /**\n * Register event listeners for the module.\n *\n * @param {object} root The calendar root element\n */\n var registerEventListeners = function(root) {\n const viewingFullCalendar = document.getElementById(CalendarSelectors.fullCalendarView);\n // Listen the click on the day link to render the day view.\n root.on('click', SELECTORS.VIEW_DAY_LINK, function(e) {\n var dayLink = $(e.target).closest(SELECTORS.VIEW_DAY_LINK);\n var year = dayLink.data('year'),\n month = dayLink.data('month'),\n day = dayLink.data('day'),\n courseId = dayLink.data('courseid'),\n categoryId = dayLink.data('categoryid');\n const url = '?view=day&time=' + dayLink.data('timestamp');\n if (viewingFullCalendar) {\n CalendarViewManager.refreshDayContent(root, year, month, day, courseId, categoryId, root,\n 'core_calendar/calendar_day').then(function() {\n e.preventDefault();\n return CalendarViewManager.updateUrl(url);\n }).fail(Notification.exception);\n } else {\n window.location.assign(Config.wwwroot + '/calendar/view.php' + url);\n }\n });\n\n root.on('change', CalendarSelectors.elements.courseSelector, function() {\n var selectElement = $(this);\n var courseId = selectElement.val();\n CalendarViewManager.reloadCurrentMonth(root, courseId, null)\n .then(function() {\n // We need to get the selector again because the content has changed.\n return root.find(CalendarSelectors.elements.courseSelector).val(courseId);\n })\n .fail(Notification.exception);\n });\n\n var eventFormPromise = CalendarCrud.registerEventFormModal(root),\n contextId = $(SELECTORS.CALENDAR_MONTH_WRAPPER).data('context-id');\n registerCalendarEventListeners(root, eventFormPromise);\n\n if (contextId) {\n // Bind click events to calendar days.\n root.on('click', SELECTORS.DAY, function(e) {\n var target = $(e.target);\n const displayingSmallBlockCalendar = root.parents('aside').data('blockregion') === 'side-pre';\n\n if (!viewingFullCalendar && displayingSmallBlockCalendar) {\n const dateContainer = target.closest(SELECTORS.DAY);\n const url = '?view=day&time=' + dateContainer.data('day-timestamp');\n window.location.assign(Config.wwwroot + '/calendar/view.php' + url);\n } else {\n const hasViewDayLink = target.closest(SELECTORS.VIEW_DAY_LINK).length;\n const shouldShowNewEventModal = !hasViewDayLink;\n if (shouldShowNewEventModal) {\n var startTime = $(this).attr('data-new-event-timestamp');\n eventFormPromise.then(function(modal) {\n var wrapper = target.closest(CalendarSelectors.wrapper);\n modal.setCourseId(wrapper.data('courseid'));\n\n var categoryId = wrapper.data('categoryid');\n if (typeof categoryId !== 'undefined') {\n modal.setCategoryId(categoryId);\n }\n\n modal.setContextId(wrapper.data('contextId'));\n modal.setStartTime(startTime);\n modal.show();\n return;\n }).fail(Notification.exception);\n }\n }\n e.preventDefault();\n });\n }\n };\n\n return {\n init: function(root) {\n root = $(root);\n CalendarViewManager.init(root);\n registerEventListeners(root);\n }\n };\n});\n"],"names":["define","$","Ajax","Str","Templates","Notification","CustomEvents","ModalEvents","ModalFactory","ModalEventForm","SummaryModal","CalendarRepository","CalendarEvents","CalendarViewManager","CalendarCrud","CalendarSelectors","Config","SELECTORS","handleMoveEvent","e","eventId","originElement","destinationElement","originTimestamp","destinationTimestamp","attr","render","then","html","js","find","addClass","appendNodeContents","updateEventStartDay","trigger","eventMoved","always","destinationLoadingElement","removeClass","replaceNode","originLoadingElement","fail","exception","registerEventListeners","root","viewingFullCalendar","document","getElementById","fullCalendarView","on","dayLink","target","closest","year","data","month","day","courseId","categoryId","url","refreshDayContent","preventDefault","updateUrl","window","location","assign","wwwroot","elements","courseSelector","this","val","reloadCurrentMonth","eventFormPromise","registerEventFormModal","contextId","eventFormModalPromise","body","created","deleted","updated","editActionEvent","moveEvent","registerEditListeners","registerCalendarEventListeners","displayingSmallBlockCalendar","parents","length","startTime","modal","wrapper","setCourseId","setCategoryId","setContextId","setStartTime","show","init"],"mappings":"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"}
\ No newline at end of file
+{"version":3,"file":"calendar.min.js","sources":["../src/calendar.js"],"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 * This module is the highest level module for the calendar. It is\n * responsible for initialising all of the components required for\n * the calendar to run. It also coordinates the interaction between\n * components by listening for and responding to different events\n * triggered within the calendar UI.\n *\n * @module core_calendar/calendar\n * @copyright 2017 Simey Lameze \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine([\n 'jquery',\n 'core/ajax',\n 'core/str',\n 'core/templates',\n 'core/notification',\n 'core/custom_interaction_events',\n 'core/modal_events',\n 'core/modal_factory',\n 'core_calendar/modal_event_form',\n 'core_calendar/summary_modal',\n 'core_calendar/repository',\n 'core_calendar/events',\n 'core_calendar/view_manager',\n 'core_calendar/crud',\n 'core_calendar/selectors',\n 'core/config',\n ],\n function(\n $,\n Ajax,\n Str,\n Templates,\n Notification,\n CustomEvents,\n ModalEvents,\n ModalFactory,\n ModalEventForm,\n SummaryModal,\n CalendarRepository,\n CalendarEvents,\n CalendarViewManager,\n CalendarCrud,\n CalendarSelectors,\n Config,\n ) {\n\n var SELECTORS = {\n ROOT: \"[data-region='calendar']\",\n DAY: \"[data-region='day']\",\n NEW_EVENT_BUTTON: \"[data-action='new-event-button']\",\n DAY_CONTENT: \"[data-region='day-content']\",\n LOADING_ICON: '.loading-icon',\n VIEW_DAY_LINK: \"[data-action='view-day-link']\",\n CALENDAR_MONTH_WRAPPER: \".calendarwrapper\",\n TODAY: '.today',\n DAY_NUMBER_CIRCLE: '.day-number-circle',\n DAY_NUMBER: '.day-number'\n };\n\n /**\n * Handler for the drag and drop move event. Provides a loading indicator\n * while the request is sent to the server to update the event start date.\n *\n * Triggers a eventMoved calendar javascript event if the event was successfully\n * updated.\n *\n * @param {event} e The calendar move event\n * @param {int} eventId The event id being moved\n * @param {object|null} originElement The jQuery element for where the event is moving from\n * @param {object} destinationElement The jQuery element for where the event is moving to\n */\n var handleMoveEvent = function(e, eventId, originElement, destinationElement) {\n var originTimestamp = null;\n var destinationTimestamp = destinationElement.attr('data-day-timestamp');\n\n if (originElement) {\n originTimestamp = originElement.attr('data-day-timestamp');\n }\n\n // If the event has actually changed day.\n if (!originElement || originTimestamp != destinationTimestamp) {\n Templates.render('core/loading', {})\n .then(function(html, js) {\n // First we show some loading icons in each of the days being affected.\n destinationElement.find(SELECTORS.DAY_CONTENT).addClass('hidden');\n Templates.appendNodeContents(destinationElement, html, js);\n\n if (originElement) {\n originElement.find(SELECTORS.DAY_CONTENT).addClass('hidden');\n Templates.appendNodeContents(originElement, html, js);\n }\n return;\n })\n .then(function() {\n // Send a request to the server to make the change.\n return CalendarRepository.updateEventStartDay(eventId, destinationTimestamp);\n })\n .then(function() {\n // If the update was successful then broadcast an event letting the calendar\n // know that an event has been moved.\n $('body').trigger(CalendarEvents.eventMoved, [eventId, originElement, destinationElement]);\n return;\n })\n .always(function() {\n // Always remove the loading icons regardless of whether the update\n // request was successful or not.\n var destinationLoadingElement = destinationElement.find(SELECTORS.LOADING_ICON);\n destinationElement.find(SELECTORS.DAY_CONTENT).removeClass('hidden');\n Templates.replaceNode(destinationLoadingElement, '', '');\n\n if (originElement) {\n var originLoadingElement = originElement.find(SELECTORS.LOADING_ICON);\n originElement.find(SELECTORS.DAY_CONTENT).removeClass('hidden');\n Templates.replaceNode(originLoadingElement, '', '');\n }\n return;\n })\n .fail(Notification.exception);\n }\n };\n\n /**\n * Listen to and handle any calendar events fired by the calendar UI.\n *\n * @method registerCalendarEventListeners\n * @param {object} root The calendar root element\n * @param {object} eventFormModalPromise A promise reolved with the event form modal\n */\n var registerCalendarEventListeners = function(root, eventFormModalPromise) {\n var body = $('body');\n\n body.on(CalendarEvents.created, function() {\n CalendarViewManager.reloadCurrentMonth(root);\n });\n body.on(CalendarEvents.deleted, function() {\n CalendarViewManager.reloadCurrentMonth(root);\n });\n body.on(CalendarEvents.updated, function() {\n CalendarViewManager.reloadCurrentMonth(root);\n });\n body.on(CalendarEvents.editActionEvent, function(e, url) {\n // Action events needs to be edit directly on the course module.\n window.location.assign(url);\n });\n // Handle the event fired by the drag and drop code.\n body.on(CalendarEvents.moveEvent, handleMoveEvent);\n // When an event is successfully moved we should updated the UI.\n body.on(CalendarEvents.eventMoved, function() {\n CalendarViewManager.reloadCurrentMonth(root);\n });\n\n CalendarCrud.registerEditListeners(root, eventFormModalPromise);\n };\n\n /**\n * Register event listeners for the module.\n *\n * @param {object} root The calendar root element\n */\n var registerEventListeners = function(root) {\n const viewingFullCalendar = document.getElementById(CalendarSelectors.fullCalendarView);\n // Listen the click on the day link to render the day view.\n root.on('click', SELECTORS.VIEW_DAY_LINK, function(e) {\n var dayLink = $(e.target).closest(SELECTORS.VIEW_DAY_LINK);\n var year = dayLink.data('year'),\n month = dayLink.data('month'),\n day = dayLink.data('day'),\n courseId = dayLink.data('courseid'),\n categoryId = dayLink.data('categoryid');\n const url = '?view=day&time=' + dayLink.data('timestamp');\n if (viewingFullCalendar) {\n CalendarViewManager.refreshDayContent(root, year, month, day, courseId, categoryId, root,\n 'core_calendar/calendar_day').then(function() {\n e.preventDefault();\n return CalendarViewManager.updateUrl(url);\n }).fail(Notification.exception);\n } else {\n window.location.assign(Config.wwwroot + '/calendar/view.php' + url);\n }\n });\n\n root.on('change', CalendarSelectors.elements.courseSelector, function() {\n var selectElement = $(this);\n var courseId = selectElement.val();\n CalendarViewManager.reloadCurrentMonth(root, courseId, null)\n .then(function() {\n // We need to get the selector again because the content has changed.\n return root.find(CalendarSelectors.elements.courseSelector).val(courseId);\n })\n .fail(Notification.exception);\n });\n\n var eventFormPromise = CalendarCrud.registerEventFormModal(root),\n contextId = $(SELECTORS.CALENDAR_MONTH_WRAPPER).data('context-id');\n registerCalendarEventListeners(root, eventFormPromise);\n\n if (contextId) {\n // Bind click events to calendar days.\n root.on('click', SELECTORS.DAY, function(e) {\n var target = $(e.target);\n const displayingSmallBlockCalendar = root.parents('aside').data('blockregion') === 'side-pre';\n\n if (!viewingFullCalendar && displayingSmallBlockCalendar) {\n const dateContainer = target.closest(SELECTORS.DAY);\n const url = '?view=day&time=' + dateContainer.data('day-timestamp');\n window.location.assign(Config.wwwroot + '/calendar/view.php' + url);\n } else {\n const hasViewDayLink = target.closest(SELECTORS.VIEW_DAY_LINK).length;\n const shouldShowNewEventModal = !hasViewDayLink;\n if (shouldShowNewEventModal) {\n var startTime = $(this).attr('data-new-event-timestamp');\n eventFormPromise.then(function(modal) {\n var wrapper = target.closest(CalendarSelectors.wrapper);\n modal.setCourseId(wrapper.data('courseid'));\n\n var categoryId = wrapper.data('categoryid');\n if (typeof categoryId !== 'undefined') {\n modal.setCategoryId(categoryId);\n }\n\n modal.setContextId(wrapper.data('contextId'));\n modal.setStartTime(startTime);\n modal.show();\n return;\n }).fail(Notification.exception);\n }\n }\n e.preventDefault();\n });\n }\n };\n\n return {\n init: function(root) {\n root = $(root);\n CalendarViewManager.init(root);\n registerEventListeners(root);\n }\n };\n});\n"],"names":["define","$","Ajax","Str","Templates","Notification","CustomEvents","ModalEvents","ModalFactory","ModalEventForm","SummaryModal","CalendarRepository","CalendarEvents","CalendarViewManager","CalendarCrud","CalendarSelectors","Config","SELECTORS","handleMoveEvent","e","eventId","originElement","destinationElement","originTimestamp","destinationTimestamp","attr","render","then","html","js","find","addClass","appendNodeContents","updateEventStartDay","trigger","eventMoved","always","destinationLoadingElement","removeClass","replaceNode","originLoadingElement","fail","exception","registerEventListeners","root","viewingFullCalendar","document","getElementById","fullCalendarView","on","dayLink","target","closest","year","data","month","day","courseId","categoryId","url","refreshDayContent","preventDefault","updateUrl","window","location","assign","wwwroot","elements","courseSelector","this","val","reloadCurrentMonth","eventFormPromise","registerEventFormModal","contextId","eventFormModalPromise","body","created","deleted","updated","editActionEvent","moveEvent","registerEditListeners","registerCalendarEventListeners","displayingSmallBlockCalendar","parents","length","startTime","modal","wrapper","setCourseId","setCategoryId","setContextId","setStartTime","show","init"],"mappings":"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"}
\ No newline at end of file
diff --git a/calendar/amd/build/calendar_filter.min.js.map b/calendar/amd/build/calendar_filter.min.js.map
index 14d1fe3be3c..35474ca44a4 100644
--- a/calendar/amd/build/calendar_filter.min.js.map
+++ b/calendar/amd/build/calendar_filter.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"calendar_filter.min.js","sources":["../src/calendar_filter.js"],"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 * This module is responsible for the calendar filter.\n *\n * @module core_calendar/calendar_filter\n * @copyright 2017 Andrew Nicols \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine([\n 'jquery',\n 'core_calendar/selectors',\n 'core_calendar/events',\n 'core/str',\n 'core/templates',\n],\nfunction(\n $,\n CalendarSelectors,\n CalendarEvents,\n Str,\n Templates\n) {\n\n var registerEventListeners = function(root) {\n root.on('click', CalendarSelectors.eventFilterItem, function(e) {\n var target = $(e.currentTarget);\n\n toggleFilter(target);\n\n e.preventDefault();\n });\n\n $('body').on(CalendarEvents.viewUpdated, function() {\n var filters = root.find(CalendarSelectors.eventFilterItem);\n\n filters.each(function(i, filter) {\n filter = $(filter);\n if (filter.data('eventtype-hidden')) {\n var data = getFilterData(filter);\n fireFilterChangedEvent(data);\n }\n });\n });\n };\n\n var toggleFilter = function(target) {\n var data = getFilterData(target);\n\n // Toggle the hidden. We need to render the template before we change the value.\n data.hidden = !data.hidden;\n\n M.util.js_pending(\"core_calendar/calendar_filter:toggleFilter\");\n return Str.get_string('eventtype' + data.eventtype, 'calendar')\n .then(function(nameStr) {\n data.name = nameStr;\n data.icon = true;\n data.key = 'i/' + data.eventtype + 'event';\n data.component = 'core';\n\n return data;\n })\n .then(function(context) {\n return Templates.render('core_calendar/event_filter_key', context);\n })\n .then(function(html, js) {\n return Templates.replaceNode(target, html, js);\n })\n .then(function() {\n fireFilterChangedEvent(data);\n M.util.js_complete(\"core_calendar/calendar_filter:toggleFilter\");\n return;\n });\n };\n\n /**\n * Fire the filterChanged event for the specified data.\n *\n * @param {object} data The data to include\n */\n var fireFilterChangedEvent = function(data) {\n M.util.js_pending(\"month-mini-filterChanged\");\n $('body').trigger(CalendarEvents.filterChanged, {\n type: data.eventtype,\n hidden: data.hidden,\n });\n M.util.js_complete(\"month-mini-filterChanged\");\n };\n\n /**\n * Get the filter data for the specified target.\n *\n * @param {jQuery} target The target node\n * @return {Object}\n */\n var getFilterData = function(target) {\n return {\n eventtype: target.data('eventtype'),\n hidden: target.data('eventtype-hidden'),\n };\n };\n\n return {\n init: function(root) {\n root = $(root);\n\n registerEventListeners(root);\n }\n };\n});\n"],"names":["define","$","CalendarSelectors","CalendarEvents","Str","Templates","toggleFilter","target","data","getFilterData","hidden","M","util","js_pending","get_string","eventtype","then","nameStr","name","icon","key","component","context","render","html","js","replaceNode","fireFilterChangedEvent","js_complete","trigger","filterChanged","type","init","root","on","eventFilterItem","e","currentTarget","preventDefault","viewUpdated","find","each","i","filter","registerEventListeners"],"mappings":";;;;;;;AAsBAA,uCAAO,CACH,SACA,0BACA,uBACA,WACA,mBAEJ,SACIC,EACAC,kBACAC,eACAC,IACAC,eAyBIC,aAAe,SAASC,YACpBC,KAAOC,cAAcF,eAGzBC,KAAKE,QAAUF,KAAKE,OAEpBC,EAAEC,KAAKC,WAAW,8CACXT,IAAIU,WAAW,YAAcN,KAAKO,UAAW,YACnDC,MAAK,SAASC,gBACXT,KAAKU,KAAOD,QACZT,KAAKW,MAAO,EACZX,KAAKY,IAAM,KAAOZ,KAAKO,UAAY,QACnCP,KAAKa,UAAY,OAEVb,QAEVQ,MAAK,SAASM,gBACJjB,UAAUkB,OAAO,iCAAkCD,YAE7DN,MAAK,SAASQ,KAAMC,WACVpB,UAAUqB,YAAYnB,OAAQiB,KAAMC,OAE9CT,MAAK,WACFW,uBAAuBnB,MACvBG,EAAEC,KAAKgB,YAAY,kDAUvBD,uBAAyB,SAASnB,MAClCG,EAAEC,KAAKC,WAAW,4BAClBZ,EAAE,QAAQ4B,QAAQ1B,eAAe2B,cAAe,CAC5CC,KAAMvB,KAAKO,UACXL,OAAQF,KAAKE,SAEjBC,EAAEC,KAAKgB,YAAY,6BASnBnB,cAAgB,SAASF,cAClB,CACHQ,UAAWR,OAAOC,KAAK,aACvBE,OAAQH,OAAOC,KAAK,4BAIrB,CACHwB,KAAM,SAASC,OA/EU,SAASA,MAClCA,KAAKC,GAAG,QAAShC,kBAAkBiC,iBAAiB,SAASC,OACrD7B,OAASN,EAAEmC,EAAEC,eAEjB/B,aAAaC,QAEb6B,EAAEE,oBAGNrC,EAAE,QAAQiC,GAAG/B,eAAeoC,aAAa,WACvBN,KAAKO,KAAKtC,kBAAkBiC,iBAElCM,MAAK,SAASC,EAAGC,YACrBA,OAAS1C,EAAE0C,SACAnC,KAAK,oBAAqB,KAC7BA,KAAOC,cAAckC,QACzBhB,uBAAuBnB,aAkE/BoC,CAFAX,KAAOhC,EAAEgC"}
\ No newline at end of file
+{"version":3,"file":"calendar_filter.min.js","sources":["../src/calendar_filter.js"],"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 * This module is responsible for the calendar filter.\n *\n * @module core_calendar/calendar_filter\n * @copyright 2017 Andrew Nicols \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine([\n 'jquery',\n 'core_calendar/selectors',\n 'core_calendar/events',\n 'core/str',\n 'core/templates',\n],\nfunction(\n $,\n CalendarSelectors,\n CalendarEvents,\n Str,\n Templates\n) {\n\n var registerEventListeners = function(root) {\n root.on('click', CalendarSelectors.eventFilterItem, function(e) {\n var target = $(e.currentTarget);\n\n toggleFilter(target);\n\n e.preventDefault();\n });\n\n $('body').on(CalendarEvents.viewUpdated, function() {\n var filters = root.find(CalendarSelectors.eventFilterItem);\n\n filters.each(function(i, filter) {\n filter = $(filter);\n if (filter.data('eventtype-hidden')) {\n var data = getFilterData(filter);\n fireFilterChangedEvent(data);\n }\n });\n });\n };\n\n var toggleFilter = function(target) {\n var data = getFilterData(target);\n\n // Toggle the hidden. We need to render the template before we change the value.\n data.hidden = !data.hidden;\n\n M.util.js_pending(\"core_calendar/calendar_filter:toggleFilter\");\n return Str.get_string('eventtype' + data.eventtype, 'calendar')\n .then(function(nameStr) {\n data.name = nameStr;\n data.icon = true;\n data.key = 'i/' + data.eventtype + 'event';\n data.component = 'core';\n\n return data;\n })\n .then(function(context) {\n return Templates.render('core_calendar/event_filter_key', context);\n })\n .then(function(html, js) {\n return Templates.replaceNode(target, html, js);\n })\n .then(function() {\n fireFilterChangedEvent(data);\n M.util.js_complete(\"core_calendar/calendar_filter:toggleFilter\");\n return;\n });\n };\n\n /**\n * Fire the filterChanged event for the specified data.\n *\n * @param {object} data The data to include\n */\n var fireFilterChangedEvent = function(data) {\n M.util.js_pending(\"month-mini-filterChanged\");\n $('body').trigger(CalendarEvents.filterChanged, {\n type: data.eventtype,\n hidden: data.hidden,\n });\n M.util.js_complete(\"month-mini-filterChanged\");\n };\n\n /**\n * Get the filter data for the specified target.\n *\n * @param {jQuery} target The target node\n * @return {Object}\n */\n var getFilterData = function(target) {\n return {\n eventtype: target.data('eventtype'),\n hidden: target.data('eventtype-hidden'),\n };\n };\n\n return {\n init: function(root) {\n root = $(root);\n\n registerEventListeners(root);\n }\n };\n});\n"],"names":["define","$","CalendarSelectors","CalendarEvents","Str","Templates","toggleFilter","target","data","getFilterData","hidden","M","util","js_pending","get_string","eventtype","then","nameStr","name","icon","key","component","context","render","html","js","replaceNode","fireFilterChangedEvent","js_complete","trigger","filterChanged","type","init","root","on","eventFilterItem","e","currentTarget","preventDefault","viewUpdated","find","each","i","filter","registerEventListeners"],"mappings":";;;;;;;AAsBAA,OAAM,gCAAC,CACH,SACA,0BACA,uBACA,WACA,mBAEJ,SACIC,EACAC,kBACAC,eACAC,IACAC,WAGA,IAsBIC,aAAe,SAASC,QACxB,IAAIC,KAAOC,cAAcF,QAMzB,OAHAC,KAAKE,QAAUF,KAAKE,OAEpBC,EAAEC,KAAKC,WAAW,8CACXT,IAAIU,WAAW,YAAcN,KAAKO,UAAW,YACnDC,MAAK,SAASC,SAMX,OALAT,KAAKU,KAAOD,QACZT,KAAKW,MAAO,EACZX,KAAKY,IAAM,KAAOZ,KAAKO,UAAY,QACnCP,KAAKa,UAAY,OAEVb,IACX,IACCQ,MAAK,SAASM,SACX,OAAOjB,UAAUkB,OAAO,iCAAkCD,QAC7D,IACAN,MAAK,SAASQ,KAAMC,IACjB,OAAOpB,UAAUqB,YAAYnB,OAAQiB,KAAMC,GAC/C,IACCT,MAAK,WACFW,uBAAuBnB,MACvBG,EAAEC,KAAKgB,YAAY,6CAEvB,KAQAD,uBAAyB,SAASnB,MAClCG,EAAEC,KAAKC,WAAW,4BAClBZ,EAAE,QAAQ4B,QAAQ1B,eAAe2B,cAAe,CAC5CC,KAAMvB,KAAKO,UACXL,OAAQF,KAAKE,SAEjBC,EAAEC,KAAKgB,YAAY,6BASnBnB,cAAgB,SAASF,QACzB,MAAO,CACHQ,UAAWR,OAAOC,KAAK,aACvBE,OAAQH,OAAOC,KAAK,sBAI5B,MAAO,CACHwB,KAAM,SAASC,OA/EU,SAASA,MAClCA,KAAKC,GAAG,QAAShC,kBAAkBiC,iBAAiB,SAASC,GACzD,IAAI7B,OAASN,EAAEmC,EAAEC,eAEjB/B,aAAaC,QAEb6B,EAAEE,gBACN,IAEArC,EAAE,QAAQiC,GAAG/B,eAAeoC,aAAa,WACvBN,KAAKO,KAAKtC,kBAAkBiC,iBAElCM,MAAK,SAASC,EAAGC,QAErB,IADAA,OAAS1C,EAAE0C,SACAnC,KAAK,oBAAqB,CACjC,IAAIA,KAAOC,cAAckC,QACzBhB,uBAAuBnB,KAC3B,CACJ,GACJ,IA+DIoC,CAFAX,KAAOhC,EAAEgC,MAGb,EAER"}
\ No newline at end of file
diff --git a/calendar/amd/build/calendar_mini.min.js.map b/calendar/amd/build/calendar_mini.min.js.map
index 6aa3420722f..9d32406a0e3 100644
--- a/calendar/amd/build/calendar_mini.min.js.map
+++ b/calendar/amd/build/calendar_mini.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"calendar_mini.min.js","sources":["../src/calendar_mini.js"],"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 * This module is the highest level module for the calendar. It is\n * responsible for initialising all of the components required for\n * the calendar to run. It also coordinates the interaction between\n * components by listening for and responding to different events\n * triggered within the calendar UI.\n *\n * @module core_calendar/calendar_mini\n * @copyright 2017 Andrew Nicols \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine([\n 'jquery',\n 'core_calendar/selectors',\n 'core_calendar/events',\n 'core_calendar/view_manager',\n],\nfunction(\n $,\n CalendarSelectors,\n CalendarEvents,\n CalendarViewManager\n) {\n\n /**\n * Listen to and handle any calendar events fired by the calendar UI.\n *\n * @method registerCalendarEventListeners\n * @param {object} root The calendar root element\n */\n var registerCalendarEventListeners = function(root) {\n var body = $('body');\n var namespace = '.' + root.attr('id');\n\n body.on(CalendarEvents.created + namespace, root, reloadMonth);\n body.on(CalendarEvents.deleted + namespace, root, reloadMonth);\n body.on(CalendarEvents.updated + namespace, root, reloadMonth);\n body.on(CalendarEvents.eventMoved + namespace, root, reloadMonth);\n };\n\n /**\n * Reload the month view in this month.\n *\n * @param {EventFacade} e\n */\n var reloadMonth = function(e) {\n var root = e.data;\n var body = $('body');\n var namespace = '.' + root.attr('id');\n\n if (root.is(':visible')) {\n CalendarViewManager.reloadCurrentMonth(root);\n } else {\n // The root has been removed.\n // Remove all events in the namespace.\n body.off(CalendarEvents.created + namespace);\n body.off(CalendarEvents.deleted + namespace);\n body.off(CalendarEvents.updated + namespace);\n body.off(CalendarEvents.eventMoved + namespace);\n }\n };\n\n var registerEventListeners = function(root) {\n $('body').on(CalendarEvents.filterChanged, function(e, data) {\n var daysWithEvent = root.find(CalendarSelectors.eventType[data.type]);\n\n daysWithEvent.toggleClass('calendar_event_' + data.type, !data.hidden);\n });\n\n var namespace = '.' + root.attr('id');\n $('body').on('change' + namespace, CalendarSelectors.elements.courseSelector, function() {\n if (root.is(':visible')) {\n var selectElement = $(this);\n var courseId = selectElement.val();\n var categoryId = null;\n\n CalendarViewManager.reloadCurrentMonth(root, courseId, categoryId);\n } else {\n $('body').off('change' + namespace);\n }\n });\n\n };\n\n return {\n init: function(root, loadOnInit) {\n root = $(root);\n\n CalendarViewManager.init(root);\n registerEventListeners(root);\n registerCalendarEventListeners(root);\n\n if (loadOnInit) {\n // The calendar hasn't yet loaded it's events so we\n // should load them as soon as we've initialised.\n CalendarViewManager.reloadCurrentMonth(root);\n }\n\n }\n };\n});\n"],"names":["define","$","CalendarSelectors","CalendarEvents","CalendarViewManager","reloadMonth","e","root","data","body","namespace","attr","is","reloadCurrentMonth","off","created","deleted","updated","eventMoved","init","loadOnInit","on","filterChanged","find","eventType","type","toggleClass","hidden","elements","courseSelector","courseId","this","val","registerEventListeners","registerCalendarEventListeners"],"mappings":";;;;;;;;;;;AA0BAA,qCAAO,CACH,SACA,0BACA,uBACA,+BAEJ,SACIC,EACAC,kBACAC,eACAC,yBAwBIC,YAAc,SAASC,OACnBC,KAAOD,EAAEE,KACTC,KAAOR,EAAE,QACTS,UAAY,IAAMH,KAAKI,KAAK,MAE5BJ,KAAKK,GAAG,YACRR,oBAAoBS,mBAAmBN,OAIvCE,KAAKK,IAAIX,eAAeY,QAAUL,WAClCD,KAAKK,IAAIX,eAAea,QAAUN,WAClCD,KAAKK,IAAIX,eAAec,QAAUP,WAClCD,KAAKK,IAAIX,eAAee,WAAaR,mBA0BtC,CACHS,KAAM,SAASZ,KAAMa,YACjBb,KAAON,EAAEM,MAETH,oBAAoBe,KAAKZ,MA1BJ,SAASA,MAClCN,EAAE,QAAQoB,GAAGlB,eAAemB,eAAe,SAAShB,EAAGE,MAC/BD,KAAKgB,KAAKrB,kBAAkBsB,UAAUhB,KAAKiB,OAEjDC,YAAY,kBAAoBlB,KAAKiB,MAAOjB,KAAKmB,eAG/DjB,UAAY,IAAMH,KAAKI,KAAK,MAChCV,EAAE,QAAQoB,GAAG,SAAWX,UAAWR,kBAAkB0B,SAASC,gBAAgB,cACtEtB,KAAKK,GAAG,YAAa,KAEjBkB,SADgB7B,EAAE8B,MACOC,MAG7B5B,oBAAoBS,mBAAmBN,KAAMuB,SAF5B,WAIjB7B,EAAE,QAAQa,IAAI,SAAWJ,cAW7BuB,CAAuB1B,MA3DM,SAASA,UACtCE,KAAOR,EAAE,QACTS,UAAY,IAAMH,KAAKI,KAAK,MAEhCF,KAAKY,GAAGlB,eAAeY,QAAUL,UAAWH,KAAMF,aAClDI,KAAKY,GAAGlB,eAAea,QAAUN,UAAWH,KAAMF,aAClDI,KAAKY,GAAGlB,eAAec,QAAUP,UAAWH,KAAMF,aAClDI,KAAKY,GAAGlB,eAAee,WAAaR,UAAWH,KAAMF,aAqDjD6B,CAA+B3B,MAE3Ba,YAGAhB,oBAAoBS,mBAAmBN"}
\ No newline at end of file
+{"version":3,"file":"calendar_mini.min.js","sources":["../src/calendar_mini.js"],"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 * This module is the highest level module for the calendar. It is\n * responsible for initialising all of the components required for\n * the calendar to run. It also coordinates the interaction between\n * components by listening for and responding to different events\n * triggered within the calendar UI.\n *\n * @module core_calendar/calendar_mini\n * @copyright 2017 Andrew Nicols \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine([\n 'jquery',\n 'core_calendar/selectors',\n 'core_calendar/events',\n 'core_calendar/view_manager',\n],\nfunction(\n $,\n CalendarSelectors,\n CalendarEvents,\n CalendarViewManager\n) {\n\n /**\n * Listen to and handle any calendar events fired by the calendar UI.\n *\n * @method registerCalendarEventListeners\n * @param {object} root The calendar root element\n */\n var registerCalendarEventListeners = function(root) {\n var body = $('body');\n var namespace = '.' + root.attr('id');\n\n body.on(CalendarEvents.created + namespace, root, reloadMonth);\n body.on(CalendarEvents.deleted + namespace, root, reloadMonth);\n body.on(CalendarEvents.updated + namespace, root, reloadMonth);\n body.on(CalendarEvents.eventMoved + namespace, root, reloadMonth);\n };\n\n /**\n * Reload the month view in this month.\n *\n * @param {EventFacade} e\n */\n var reloadMonth = function(e) {\n var root = e.data;\n var body = $('body');\n var namespace = '.' + root.attr('id');\n\n if (root.is(':visible')) {\n CalendarViewManager.reloadCurrentMonth(root);\n } else {\n // The root has been removed.\n // Remove all events in the namespace.\n body.off(CalendarEvents.created + namespace);\n body.off(CalendarEvents.deleted + namespace);\n body.off(CalendarEvents.updated + namespace);\n body.off(CalendarEvents.eventMoved + namespace);\n }\n };\n\n var registerEventListeners = function(root) {\n $('body').on(CalendarEvents.filterChanged, function(e, data) {\n var daysWithEvent = root.find(CalendarSelectors.eventType[data.type]);\n\n daysWithEvent.toggleClass('calendar_event_' + data.type, !data.hidden);\n });\n\n var namespace = '.' + root.attr('id');\n $('body').on('change' + namespace, CalendarSelectors.elements.courseSelector, function() {\n if (root.is(':visible')) {\n var selectElement = $(this);\n var courseId = selectElement.val();\n var categoryId = null;\n\n CalendarViewManager.reloadCurrentMonth(root, courseId, categoryId);\n } else {\n $('body').off('change' + namespace);\n }\n });\n\n };\n\n return {\n init: function(root, loadOnInit) {\n root = $(root);\n\n CalendarViewManager.init(root);\n registerEventListeners(root);\n registerCalendarEventListeners(root);\n\n if (loadOnInit) {\n // The calendar hasn't yet loaded it's events so we\n // should load them as soon as we've initialised.\n CalendarViewManager.reloadCurrentMonth(root);\n }\n\n }\n };\n});\n"],"names":["define","$","CalendarSelectors","CalendarEvents","CalendarViewManager","reloadMonth","e","root","data","body","namespace","attr","is","reloadCurrentMonth","off","created","deleted","updated","eventMoved","init","loadOnInit","on","filterChanged","find","eventType","type","toggleClass","hidden","elements","courseSelector","courseId","this","val","registerEventListeners","registerCalendarEventListeners"],"mappings":";;;;;;;;;;;AA0BAA,qCAAO,CACH,SACA,0BACA,uBACA,+BAEJ,SACIC,EACAC,kBACAC,eACAC,qBASA,IAeIC,YAAc,SAASC,GACvB,IAAIC,KAAOD,EAAEE,KACTC,KAAOR,EAAE,QACTS,UAAY,IAAMH,KAAKI,KAAK,MAE5BJ,KAAKK,GAAG,YACRR,oBAAoBS,mBAAmBN,OAIvCE,KAAKK,IAAIX,eAAeY,QAAUL,WAClCD,KAAKK,IAAIX,eAAea,QAAUN,WAClCD,KAAKK,IAAIX,eAAec,QAAUP,WAClCD,KAAKK,IAAIX,eAAee,WAAaR,aA0B7C,MAAO,CACHS,KAAM,SAASZ,KAAMa,YACjBb,KAAON,EAAEM,MAETH,oBAAoBe,KAAKZ,MA1BJ,SAASA,MAClCN,EAAE,QAAQoB,GAAGlB,eAAemB,eAAe,SAAShB,EAAGE,MAC/BD,KAAKgB,KAAKrB,kBAAkBsB,UAAUhB,KAAKiB,OAEjDC,YAAY,kBAAoBlB,KAAKiB,MAAOjB,KAAKmB,OACnE,IAEA,IAAIjB,UAAY,IAAMH,KAAKI,KAAK,MAChCV,EAAE,QAAQoB,GAAG,SAAWX,UAAWR,kBAAkB0B,SAASC,gBAAgB,WAC1E,GAAItB,KAAKK,GAAG,YAAa,CACrB,IACIkB,SADgB7B,EAAE8B,MACOC,MAG7B5B,oBAAoBS,mBAAmBN,KAAMuB,SAF5B,KAGrB,MACI7B,EAAE,QAAQa,IAAI,SAAWJ,UAEjC,IASIuB,CAAuB1B,MA3DM,SAASA,MAC1C,IAAIE,KAAOR,EAAE,QACTS,UAAY,IAAMH,KAAKI,KAAK,MAEhCF,KAAKY,GAAGlB,eAAeY,QAAUL,UAAWH,KAAMF,aAClDI,KAAKY,GAAGlB,eAAea,QAAUN,UAAWH,KAAMF,aAClDI,KAAKY,GAAGlB,eAAec,QAAUP,UAAWH,KAAMF,aAClDI,KAAKY,GAAGlB,eAAee,WAAaR,UAAWH,KAAMF,aAqDjD6B,CAA+B3B,MAE3Ba,YAGAhB,oBAAoBS,mBAAmBN,KAG/C,EAER"}
\ No newline at end of file
diff --git a/calendar/amd/build/calendar_threemonth.min.js.map b/calendar/amd/build/calendar_threemonth.min.js.map
index fc4a2155b07..a1f32c02425 100644
--- a/calendar/amd/build/calendar_threemonth.min.js.map
+++ b/calendar/amd/build/calendar_threemonth.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"calendar_threemonth.min.js","sources":["../src/calendar_threemonth.js"],"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 * This module handles display of multiple mini calendars in a view, and\n * movement through them.\n *\n * @deprecated since 4.0 MDL-72810.\n * @todo MDL-73117 This will be deleted in Moodle 4.4.\n * @module core_calendar/calendar_threemonth\n * @copyright 2017 Andrew Nicols \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine([\n 'jquery',\n 'core/notification',\n 'core_calendar/selectors',\n 'core_calendar/events',\n 'core/templates',\n 'core_calendar/view_manager',\n],\nfunction(\n $,\n Notification,\n CalendarSelectors,\n CalendarEvents,\n Templates,\n CalendarViewManager\n) {\n\n /**\n * Listen to and handle any calendar events fired by the calendar UI.\n *\n * @method registerCalendarEventListeners\n * @param {object} root The calendar root element\n */\n var registerCalendarEventListeners = function(root) {\n var body = $('body');\n body.on([CalendarEvents.monthChanged, CalendarEvents.dayChanged].join(' '), function(e, year, month, courseId, categoryId) {\n // We have to use a queue here because the calling code is decoupled from these listeners.\n // It's possible for the event to be called multiple times before one call is fully resolved.\n root.queue(function(next) {\n return processRequest(e, year, month, courseId, categoryId)\n .then(function() {\n return next();\n })\n .fail(Notification.exception)\n ;\n });\n });\n\n var processRequest = function(e, year, month, courseId, categoryId) {\n var newCurrentMonth = root.find('[data-year=\"' + year + '\"][data-month=\"' + month + '\"]');\n var newParent = newCurrentMonth.closest(CalendarSelectors.calendarPeriods.month);\n var allMonths = root.find(CalendarSelectors.calendarPeriods.month);\n\n var previousMonth = $(allMonths[0]);\n var nextMonth = $(allMonths[2]);\n\n var placeHolder = $('');\n placeHolder.attr('data-template', 'core_calendar/threemonth_month');\n placeHolder.attr('data-includenavigation', false);\n placeHolder.attr('data-mini', true);\n var placeHolderContainer = $('
');\n placeHolderContainer.hide();\n placeHolderContainer.append(placeHolder);\n\n var requestYear;\n var requestMonth;\n var oldMonth;\n\n if (newParent.is(previousMonth)) {\n // Fetch the new previous month.\n placeHolderContainer.insertBefore(previousMonth);\n\n requestYear = previousMonth.data('previousYear');\n requestMonth = previousMonth.data('previousMonth');\n oldMonth = nextMonth;\n } else if (newParent.is(nextMonth)) {\n // Fetch the new next month.\n placeHolderContainer.insertAfter(nextMonth);\n requestYear = nextMonth.data('nextYear');\n requestMonth = nextMonth.data('nextMonth');\n oldMonth = previousMonth;\n } else {\n return $.Deferred().resolve();\n }\n\n return CalendarViewManager.refreshMonthContent(\n placeHolder,\n requestYear,\n requestMonth,\n courseId,\n categoryId,\n placeHolder\n )\n .then(function() {\n var slideUpPromise = $.Deferred();\n var slideDownPromise = $.Deferred();\n oldMonth.slideUp('fast', function() {\n $(this).remove();\n slideUpPromise.resolve();\n });\n placeHolderContainer.slideDown('fast', function() {\n slideDownPromise.resolve();\n });\n\n return $.when(slideUpPromise, slideDownPromise);\n });\n };\n\n // Listen for a click on the day link in the three month block to load the day view.\n root.on('click', CalendarSelectors.links.miniDayLink, function(e) {\n\n var miniDayLink = $(e.target);\n var year = miniDayLink.data('year'),\n month = miniDayLink.data('month'),\n day = miniDayLink.text(),\n courseId = miniDayLink.data('courseid'),\n categoryId = miniDayLink.data('categoryid'),\n calendarRoot = $('body').find(CalendarSelectors.calendarMain);\n CalendarViewManager.refreshDayContent(calendarRoot, year, month, day, courseId, categoryId,\n calendarRoot.find('[id^=\"calendar-\"][data-template^=\"core_calendar/\"]'), 'core_calendar/calendar_day');\n e.preventDefault();\n CalendarViewManager.updateUrl('?view=day');\n });\n };\n\n return {\n init: function(root) {\n root = $(root);\n\n registerCalendarEventListeners(root);\n }\n };\n});\n"],"names":["define","$","Notification","CalendarSelectors","CalendarEvents","Templates","CalendarViewManager","init","root","on","monthChanged","dayChanged","join","e","year","month","courseId","categoryId","queue","next","processRequest","then","fail","exception","newParent","find","closest","calendarPeriods","allMonths","previousMonth","nextMonth","placeHolder","attr","requestYear","requestMonth","oldMonth","placeHolderContainer","hide","append","is","insertBefore","data","Deferred","resolve","insertAfter","refreshMonthContent","slideUpPromise","slideDownPromise","slideUp","this","remove","slideDown","when","links","miniDayLink","target","day","text","calendarRoot","calendarMain","refreshDayContent","preventDefault","updateUrl","registerCalendarEventListeners"],"mappings":";;;;;;;;;;AAyBAA,2CAAO,CACH,SACA,oBACA,0BACA,uBACA,iBACA,+BAEJ,SACIC,EACAC,aACAC,kBACAC,eACAC,UACAC,2BAqGO,CACHC,KAAM,SAASC,OA7FkB,SAASA,MAC/BP,EAAE,QACRQ,GAAG,CAACL,eAAeM,aAAcN,eAAeO,YAAYC,KAAK,MAAM,SAASC,EAAGC,KAAMC,MAAOC,SAAUC,YAG3GT,KAAKU,OAAM,SAASC,aACTC,eAAeP,EAAGC,KAAMC,MAAOC,SAAUC,YAC/CI,MAAK,kBACKF,UAEVG,KAAKpB,aAAaqB,qBAKvBH,eAAiB,SAASP,EAAGC,KAAMC,MAAOC,SAAUC,gBAEhDO,UADkBhB,KAAKiB,KAAK,eAAiBX,KAAO,kBAAoBC,MAAQ,MACpDW,QAAQvB,kBAAkBwB,gBAAgBZ,OACtEa,UAAYpB,KAAKiB,KAAKtB,kBAAkBwB,gBAAgBZ,OAExDc,cAAgB5B,EAAE2B,UAAU,IAC5BE,UAAY7B,EAAE2B,UAAU,IAExBG,YAAc9B,EAAE,UACpB8B,YAAYC,KAAK,gBAAiB,kCAClCD,YAAYC,KAAK,0BAA0B,GAC3CD,YAAYC,KAAK,aAAa,OAK1BC,YACAC,aACAC,SANAC,qBAAuBnC,EAAE,YAC7BmC,qBAAqBC,OACrBD,qBAAqBE,OAAOP,aAMxBP,UAAUe,GAAGV,eAEbO,qBAAqBI,aAAaX,eAElCI,YAAcJ,cAAcY,KAAK,gBACjCP,aAAeL,cAAcY,KAAK,iBAClCN,SAAWL,cACR,CAAA,IAAIN,UAAUe,GAAGT,kBAOb7B,EAAEyC,WAAWC,UALpBP,qBAAqBQ,YAAYd,WACjCG,YAAcH,UAAUW,KAAK,YAC7BP,aAAeJ,UAAUW,KAAK,aAC9BN,SAAWN,qBAKRvB,oBAAoBuC,oBACvBd,YACAE,YACAC,aACAlB,SACAC,WACAc,aAEHV,MAAK,eACEyB,eAAiB7C,EAAEyC,WACnBK,iBAAmB9C,EAAEyC,kBACzBP,SAASa,QAAQ,QAAQ,WACrB/C,EAAEgD,MAAMC,SACRJ,eAAeH,aAEnBP,qBAAqBe,UAAU,QAAQ,WACnCJ,iBAAiBJ,aAGd1C,EAAEmD,KAAKN,eAAgBC,sBAKtCvC,KAAKC,GAAG,QAASN,kBAAkBkD,MAAMC,aAAa,SAASzC,OAEnDyC,YAAcrD,EAAEY,EAAE0C,QAClBzC,KAAOwC,YAAYb,KAAK,QACxB1B,MAAQuC,YAAYb,KAAK,SACzBe,IAAMF,YAAYG,OAClBzC,SAAWsC,YAAYb,KAAK,YAC5BxB,WAAaqC,YAAYb,KAAK,cAC9BiB,aAAezD,EAAE,QAAQwB,KAAKtB,kBAAkBwD,cACpDrD,oBAAoBsD,kBAAkBF,aAAc5C,KAAMC,MAAOyC,IAAKxC,SAAUC,WAC5EyC,aAAajC,KAAK,sDAAuD,8BAC7EZ,EAAEgD,iBACFvD,oBAAoBwD,UAAU,gBAQlCC,CAFAvD,KAAOP,EAAEO"}
\ No newline at end of file
+{"version":3,"file":"calendar_threemonth.min.js","sources":["../src/calendar_threemonth.js"],"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 * This module handles display of multiple mini calendars in a view, and\n * movement through them.\n *\n * @deprecated since 4.0 MDL-72810.\n * @todo MDL-73117 This will be deleted in Moodle 4.4.\n * @module core_calendar/calendar_threemonth\n * @copyright 2017 Andrew Nicols \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine([\n 'jquery',\n 'core/notification',\n 'core_calendar/selectors',\n 'core_calendar/events',\n 'core/templates',\n 'core_calendar/view_manager',\n],\nfunction(\n $,\n Notification,\n CalendarSelectors,\n CalendarEvents,\n Templates,\n CalendarViewManager\n) {\n\n /**\n * Listen to and handle any calendar events fired by the calendar UI.\n *\n * @method registerCalendarEventListeners\n * @param {object} root The calendar root element\n */\n var registerCalendarEventListeners = function(root) {\n var body = $('body');\n body.on([CalendarEvents.monthChanged, CalendarEvents.dayChanged].join(' '), function(e, year, month, courseId, categoryId) {\n // We have to use a queue here because the calling code is decoupled from these listeners.\n // It's possible for the event to be called multiple times before one call is fully resolved.\n root.queue(function(next) {\n return processRequest(e, year, month, courseId, categoryId)\n .then(function() {\n return next();\n })\n .fail(Notification.exception)\n ;\n });\n });\n\n var processRequest = function(e, year, month, courseId, categoryId) {\n var newCurrentMonth = root.find('[data-year=\"' + year + '\"][data-month=\"' + month + '\"]');\n var newParent = newCurrentMonth.closest(CalendarSelectors.calendarPeriods.month);\n var allMonths = root.find(CalendarSelectors.calendarPeriods.month);\n\n var previousMonth = $(allMonths[0]);\n var nextMonth = $(allMonths[2]);\n\n var placeHolder = $('');\n placeHolder.attr('data-template', 'core_calendar/threemonth_month');\n placeHolder.attr('data-includenavigation', false);\n placeHolder.attr('data-mini', true);\n var placeHolderContainer = $('
');\n placeHolderContainer.hide();\n placeHolderContainer.append(placeHolder);\n\n var requestYear;\n var requestMonth;\n var oldMonth;\n\n if (newParent.is(previousMonth)) {\n // Fetch the new previous month.\n placeHolderContainer.insertBefore(previousMonth);\n\n requestYear = previousMonth.data('previousYear');\n requestMonth = previousMonth.data('previousMonth');\n oldMonth = nextMonth;\n } else if (newParent.is(nextMonth)) {\n // Fetch the new next month.\n placeHolderContainer.insertAfter(nextMonth);\n requestYear = nextMonth.data('nextYear');\n requestMonth = nextMonth.data('nextMonth');\n oldMonth = previousMonth;\n } else {\n return $.Deferred().resolve();\n }\n\n return CalendarViewManager.refreshMonthContent(\n placeHolder,\n requestYear,\n requestMonth,\n courseId,\n categoryId,\n placeHolder\n )\n .then(function() {\n var slideUpPromise = $.Deferred();\n var slideDownPromise = $.Deferred();\n oldMonth.slideUp('fast', function() {\n $(this).remove();\n slideUpPromise.resolve();\n });\n placeHolderContainer.slideDown('fast', function() {\n slideDownPromise.resolve();\n });\n\n return $.when(slideUpPromise, slideDownPromise);\n });\n };\n\n // Listen for a click on the day link in the three month block to load the day view.\n root.on('click', CalendarSelectors.links.miniDayLink, function(e) {\n\n var miniDayLink = $(e.target);\n var year = miniDayLink.data('year'),\n month = miniDayLink.data('month'),\n day = miniDayLink.text(),\n courseId = miniDayLink.data('courseid'),\n categoryId = miniDayLink.data('categoryid'),\n calendarRoot = $('body').find(CalendarSelectors.calendarMain);\n CalendarViewManager.refreshDayContent(calendarRoot, year, month, day, courseId, categoryId,\n calendarRoot.find('[id^=\"calendar-\"][data-template^=\"core_calendar/\"]'), 'core_calendar/calendar_day');\n e.preventDefault();\n CalendarViewManager.updateUrl('?view=day');\n });\n };\n\n return {\n init: function(root) {\n root = $(root);\n\n registerCalendarEventListeners(root);\n }\n };\n});\n"],"names":["define","$","Notification","CalendarSelectors","CalendarEvents","Templates","CalendarViewManager","init","root","on","monthChanged","dayChanged","join","e","year","month","courseId","categoryId","queue","next","processRequest","then","fail","exception","newParent","find","closest","calendarPeriods","allMonths","previousMonth","nextMonth","placeHolder","attr","requestYear","requestMonth","oldMonth","placeHolderContainer","hide","append","is","insertBefore","data","Deferred","resolve","insertAfter","refreshMonthContent","slideUpPromise","slideDownPromise","slideUp","this","remove","slideDown","when","links","miniDayLink","target","day","text","calendarRoot","calendarMain","refreshDayContent","preventDefault","updateUrl","registerCalendarEventListeners"],"mappings":";;;;;;;;;;AAyBAA,OAAM,oCAAC,CACH,SACA,oBACA,0BACA,uBACA,iBACA,+BAEJ,SACIC,EACAC,aACAC,kBACAC,eACAC,UACAC,qBAqGA,MAAO,CACHC,KAAM,SAASC,OA7FkB,SAASA,MAC/BP,EAAE,QACRQ,GAAG,CAACL,eAAeM,aAAcN,eAAeO,YAAYC,KAAK,MAAM,SAASC,EAAGC,KAAMC,MAAOC,SAAUC,YAG3GT,KAAKU,OAAM,SAASC,MAChB,OAAOC,eAAeP,EAAGC,KAAMC,MAAOC,SAAUC,YAC/CI,MAAK,WACF,OAAOF,MACV,IACAG,KAAKpB,aAAaqB,UAEvB,GACJ,IAEA,IAAIH,eAAiB,SAASP,EAAGC,KAAMC,MAAOC,SAAUC,YACpD,IACIO,UADkBhB,KAAKiB,KAAK,eAAiBX,KAAO,kBAAoBC,MAAQ,MACpDW,QAAQvB,kBAAkBwB,gBAAgBZ,OACtEa,UAAYpB,KAAKiB,KAAKtB,kBAAkBwB,gBAAgBZ,OAExDc,cAAgB5B,EAAE2B,UAAU,IAC5BE,UAAY7B,EAAE2B,UAAU,IAExBG,YAAc9B,EAAE,UACpB8B,YAAYC,KAAK,gBAAiB,kCAClCD,YAAYC,KAAK,0BAA0B,GAC3CD,YAAYC,KAAK,aAAa,GAC9B,IAIIC,YACAC,aACAC,SANAC,qBAAuBnC,EAAE,SAQ7B,GAPAmC,qBAAqBC,OACrBD,qBAAqBE,OAAOP,aAMxBP,UAAUe,GAAGV,eAEbO,qBAAqBI,aAAaX,eAElCI,YAAcJ,cAAcY,KAAK,gBACjCP,aAAeL,cAAcY,KAAK,iBAClCN,SAAWL,cACR,KAAIN,UAAUe,GAAGT,WAOpB,OAAO7B,EAAEyC,WAAWC,UALpBP,qBAAqBQ,YAAYd,WACjCG,YAAcH,UAAUW,KAAK,YAC7BP,aAAeJ,UAAUW,KAAK,aAC9BN,SAAWN,aAGf,CAEA,OAAOvB,oBAAoBuC,oBACvBd,YACAE,YACAC,aACAlB,SACAC,WACAc,aAEHV,MAAK,WACF,IAAIyB,eAAiB7C,EAAEyC,WACnBK,iBAAmB9C,EAAEyC,WASzB,OARAP,SAASa,QAAQ,QAAQ,WACrB/C,EAAEgD,MAAMC,SACRJ,eAAeH,SACnB,IACAP,qBAAqBe,UAAU,QAAQ,WACnCJ,iBAAiBJ,SACrB,IAEO1C,EAAEmD,KAAKN,eAAgBC,iBAClC,KAIJvC,KAAKC,GAAG,QAASN,kBAAkBkD,MAAMC,aAAa,SAASzC,GAEvD,IAAIyC,YAAcrD,EAAEY,EAAE0C,QAClBzC,KAAOwC,YAAYb,KAAK,QACxB1B,MAAQuC,YAAYb,KAAK,SACzBe,IAAMF,YAAYG,OAClBzC,SAAWsC,YAAYb,KAAK,YAC5BxB,WAAaqC,YAAYb,KAAK,cAC9BiB,aAAezD,EAAE,QAAQwB,KAAKtB,kBAAkBwD,cACpDrD,oBAAoBsD,kBAAkBF,aAAc5C,KAAMC,MAAOyC,IAAKxC,SAAUC,WAC5EyC,aAAajC,KAAK,sDAAuD,8BAC7EZ,EAAEgD,iBACFvD,oBAAoBwD,UAAU,YACtC,IAOIC,CAFAvD,KAAOP,EAAEO,MAGb,EAER"}
\ No newline at end of file
diff --git a/calendar/amd/build/calendar_view.min.js.map b/calendar/amd/build/calendar_view.min.js.map
index afacfe7aa48..49149ab5bbd 100644
--- a/calendar/amd/build/calendar_view.min.js.map
+++ b/calendar/amd/build/calendar_view.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"calendar_view.min.js","sources":["../src/calendar_view.js"],"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 * This module is responsible for handle calendar day and upcoming view.\n *\n * @module core_calendar/calendar\n * @copyright 2017 Simey Lameze \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine([\n 'jquery',\n 'core/str',\n 'core/notification',\n 'core_calendar/selectors',\n 'core_calendar/events',\n 'core_calendar/view_manager',\n 'core_calendar/repository',\n 'core/modal_factory',\n 'core_calendar/modal_event_form',\n 'core/modal_events',\n 'core_calendar/crud'\n ],\n function(\n $,\n Str,\n Notification,\n CalendarSelectors,\n CalendarEvents,\n CalendarViewManager,\n CalendarRepository,\n ModalFactory,\n ModalEventForm,\n ModalEvents,\n CalendarCrud\n ) {\n\n var registerEventListeners = function(root, type) {\n var body = $('body');\n\n CalendarCrud.registerRemove(root);\n\n var reloadFunction = 'reloadCurrent' + type.charAt(0).toUpperCase() + type.slice(1);\n\n body.on(CalendarEvents.created, function() {\n CalendarViewManager[reloadFunction](root);\n });\n body.on(CalendarEvents.deleted, function() {\n CalendarViewManager[reloadFunction](root);\n });\n body.on(CalendarEvents.updated, function() {\n CalendarViewManager[reloadFunction](root);\n });\n\n root.on('change', CalendarSelectors.courseSelector, function() {\n var selectElement = $(this);\n var courseId = selectElement.val();\n CalendarViewManager[reloadFunction](root, courseId, null)\n .then(function() {\n // We need to get the selector again because the content has changed.\n return root.find(CalendarSelectors.courseSelector).val(courseId);\n })\n .then(function() {\n CalendarViewManager.updateUrl('?view=upcoming&course=' + courseId);\n })\n .fail(Notification.exception);\n });\n\n body.on(CalendarEvents.filterChanged, function(e, data) {\n var daysWithEvent = root.find(CalendarSelectors.eventType[data.type]);\n if (data.hidden == true) {\n daysWithEvent.addClass('hidden');\n } else {\n daysWithEvent.removeClass('hidden');\n }\n CalendarViewManager.foldDayEvents(root);\n });\n\n var eventFormPromise = CalendarCrud.registerEventFormModal(root);\n CalendarCrud.registerEditListeners(root, eventFormPromise);\n };\n\n return {\n init: function(root, type) {\n root = $(root);\n\n CalendarViewManager.init(root, type);\n registerEventListeners(root, type);\n }\n };\n });\n"],"names":["define","$","Str","Notification","CalendarSelectors","CalendarEvents","CalendarViewManager","CalendarRepository","ModalFactory","ModalEventForm","ModalEvents","CalendarCrud","init","root","type","body","registerRemove","reloadFunction","charAt","toUpperCase","slice","on","created","deleted","updated","courseSelector","courseId","this","val","then","find","updateUrl","fail","exception","filterChanged","e","data","daysWithEvent","eventType","hidden","addClass","removeClass","foldDayEvents","eventFormPromise","registerEventFormModal","registerEditListeners","registerEventListeners"],"mappings":";;;;;;;AAsBAA,qCAAO,CACC,SACA,WACA,oBACA,0BACA,uBACA,6BACA,2BACA,qBACA,iCACA,oBACA,uBAEJ,SACIC,EACAC,IACAC,aACAC,kBACAC,eACAC,oBACAC,mBACAC,aACAC,eACAC,YACAC,oBAgDO,CACHC,KAAM,SAASC,KAAMC,MACjBD,KAAOZ,EAAEY,MAETP,oBAAoBM,KAAKC,KAAMC,MAjDV,SAASD,KAAMC,UACpCC,KAAOd,EAAE,QAEbU,aAAaK,eAAeH,UAExBI,eAAiB,gBAAkBH,KAAKI,OAAO,GAAGC,cAAgBL,KAAKM,MAAM,GAEjFL,KAAKM,GAAGhB,eAAeiB,SAAS,WAC5BhB,oBAAoBW,gBAAgBJ,SAExCE,KAAKM,GAAGhB,eAAekB,SAAS,WAC5BjB,oBAAoBW,gBAAgBJ,SAExCE,KAAKM,GAAGhB,eAAemB,SAAS,WAC5BlB,oBAAoBW,gBAAgBJ,SAGxCA,KAAKQ,GAAG,SAAUjB,kBAAkBqB,gBAAgB,eAE5CC,SADgBzB,EAAE0B,MACOC,MAC7BtB,oBAAoBW,gBAAgBJ,KAAMa,SAAU,MAC/CG,MAAK,kBAEKhB,KAAKiB,KAAK1B,kBAAkBqB,gBAAgBG,IAAIF,aAE1DG,MAAK,WACFvB,oBAAoByB,UAAU,yBAA2BL,aAE5DM,KAAK7B,aAAa8B,cAG3BlB,KAAKM,GAAGhB,eAAe6B,eAAe,SAASC,EAAGC,UAC1CC,cAAgBxB,KAAKiB,KAAK1B,kBAAkBkC,UAAUF,KAAKtB,OAC5C,GAAfsB,KAAKG,OACLF,cAAcG,SAAS,UAEvBH,cAAcI,YAAY,UAE9BnC,oBAAoBoC,cAAc7B,aAGlC8B,iBAAmBhC,aAAaiC,uBAAuB/B,MAC3DF,aAAakC,sBAAsBhC,KAAM8B,kBAQrCG,CAAuBjC,KAAMC"}
\ No newline at end of file
+{"version":3,"file":"calendar_view.min.js","sources":["../src/calendar_view.js"],"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 * This module is responsible for handle calendar day and upcoming view.\n *\n * @module core_calendar/calendar\n * @copyright 2017 Simey Lameze \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine([\n 'jquery',\n 'core/str',\n 'core/notification',\n 'core_calendar/selectors',\n 'core_calendar/events',\n 'core_calendar/view_manager',\n 'core_calendar/repository',\n 'core/modal_factory',\n 'core_calendar/modal_event_form',\n 'core/modal_events',\n 'core_calendar/crud'\n ],\n function(\n $,\n Str,\n Notification,\n CalendarSelectors,\n CalendarEvents,\n CalendarViewManager,\n CalendarRepository,\n ModalFactory,\n ModalEventForm,\n ModalEvents,\n CalendarCrud\n ) {\n\n var registerEventListeners = function(root, type) {\n var body = $('body');\n\n CalendarCrud.registerRemove(root);\n\n var reloadFunction = 'reloadCurrent' + type.charAt(0).toUpperCase() + type.slice(1);\n\n body.on(CalendarEvents.created, function() {\n CalendarViewManager[reloadFunction](root);\n });\n body.on(CalendarEvents.deleted, function() {\n CalendarViewManager[reloadFunction](root);\n });\n body.on(CalendarEvents.updated, function() {\n CalendarViewManager[reloadFunction](root);\n });\n\n root.on('change', CalendarSelectors.courseSelector, function() {\n var selectElement = $(this);\n var courseId = selectElement.val();\n CalendarViewManager[reloadFunction](root, courseId, null)\n .then(function() {\n // We need to get the selector again because the content has changed.\n return root.find(CalendarSelectors.courseSelector).val(courseId);\n })\n .then(function() {\n CalendarViewManager.updateUrl('?view=upcoming&course=' + courseId);\n })\n .fail(Notification.exception);\n });\n\n body.on(CalendarEvents.filterChanged, function(e, data) {\n var daysWithEvent = root.find(CalendarSelectors.eventType[data.type]);\n if (data.hidden == true) {\n daysWithEvent.addClass('hidden');\n } else {\n daysWithEvent.removeClass('hidden');\n }\n CalendarViewManager.foldDayEvents(root);\n });\n\n var eventFormPromise = CalendarCrud.registerEventFormModal(root);\n CalendarCrud.registerEditListeners(root, eventFormPromise);\n };\n\n return {\n init: function(root, type) {\n root = $(root);\n\n CalendarViewManager.init(root, type);\n registerEventListeners(root, type);\n }\n };\n });\n"],"names":["define","$","Str","Notification","CalendarSelectors","CalendarEvents","CalendarViewManager","CalendarRepository","ModalFactory","ModalEventForm","ModalEvents","CalendarCrud","init","root","type","body","registerRemove","reloadFunction","charAt","toUpperCase","slice","on","created","deleted","updated","courseSelector","courseId","this","val","then","find","updateUrl","fail","exception","filterChanged","e","data","daysWithEvent","eventType","hidden","addClass","removeClass","foldDayEvents","eventFormPromise","registerEventFormModal","registerEditListeners","registerEventListeners"],"mappings":";;;;;;;AAsBAA,OAAM,8BAAC,CACC,SACA,WACA,oBACA,0BACA,uBACA,6BACA,2BACA,qBACA,iCACA,oBACA,uBAEJ,SACIC,EACAC,IACAC,aACAC,kBACAC,eACAC,oBACAC,mBACAC,aACAC,eACAC,YACAC,cAgDA,MAAO,CACHC,KAAM,SAASC,KAAMC,MACjBD,KAAOZ,EAAEY,MAETP,oBAAoBM,KAAKC,KAAMC,MAjDV,SAASD,KAAMC,MACxC,IAAIC,KAAOd,EAAE,QAEbU,aAAaK,eAAeH,MAE5B,IAAII,eAAiB,gBAAkBH,KAAKI,OAAO,GAAGC,cAAgBL,KAAKM,MAAM,GAEjFL,KAAKM,GAAGhB,eAAeiB,SAAS,WAC5BhB,oBAAoBW,gBAAgBJ,KACxC,IACAE,KAAKM,GAAGhB,eAAekB,SAAS,WAC5BjB,oBAAoBW,gBAAgBJ,KACxC,IACAE,KAAKM,GAAGhB,eAAemB,SAAS,WAC5BlB,oBAAoBW,gBAAgBJ,KACxC,IAEAA,KAAKQ,GAAG,SAAUjB,kBAAkBqB,gBAAgB,WAChD,IACIC,SADgBzB,EAAE0B,MACOC,MAC7BtB,oBAAoBW,gBAAgBJ,KAAMa,SAAU,MAC/CG,MAAK,WAEF,OAAOhB,KAAKiB,KAAK1B,kBAAkBqB,gBAAgBG,IAAIF,SAC3D,IACCG,MAAK,WACFvB,oBAAoByB,UAAU,yBAA2BL,SAC5D,IACAM,KAAK7B,aAAa8B,UAC3B,IAEAlB,KAAKM,GAAGhB,eAAe6B,eAAe,SAASC,EAAGC,MAC9C,IAAIC,cAAgBxB,KAAKiB,KAAK1B,kBAAkBkC,UAAUF,KAAKtB,OAC5C,GAAfsB,KAAKG,OACLF,cAAcG,SAAS,UAEvBH,cAAcI,YAAY,UAE9BnC,oBAAoBoC,cAAc7B,KACtC,IAEA,IAAI8B,iBAAmBhC,aAAaiC,uBAAuB/B,MAC3DF,aAAakC,sBAAsBhC,KAAM8B,kBAQrCG,CAAuBjC,KAAMC,KACjC,EAER"}
\ No newline at end of file
diff --git a/calendar/amd/build/crud.min.js.map b/calendar/amd/build/crud.min.js.map
index 707dd194a41..ef311b75d59 100644
--- a/calendar/amd/build/crud.min.js.map
+++ b/calendar/amd/build/crud.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"crud.min.js","sources":["../src/crud.js"],"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 * A module to handle CRUD operations within the UI.\n *\n * @module core_calendar/crud\n * @copyright 2017 Andrew Nicols \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine([\n 'jquery',\n 'core/str',\n 'core/notification',\n 'core/custom_interaction_events',\n 'core/modal',\n 'core/modal_registry',\n 'core/modal_factory',\n 'core/modal_events',\n 'core_calendar/modal_event_form',\n 'core_calendar/repository',\n 'core_calendar/events',\n 'core_calendar/modal_delete',\n 'core_calendar/selectors',\n 'core/pending',\n],\nfunction(\n $,\n Str,\n Notification,\n CustomEvents,\n Modal,\n ModalRegistry,\n ModalFactory,\n ModalEvents,\n ModalEventForm,\n CalendarRepository,\n CalendarEvents,\n ModalDelete,\n CalendarSelectors,\n Pending\n) {\n\n /**\n * Prepares the action for the summary modal's delete action.\n *\n * @param {Number} eventId The ID of the event.\n * @param {string} eventTitle The event title.\n * @param {Number} eventCount The number of events in the series.\n * @return {Promise}\n */\n function confirmDeletion(eventId, eventTitle, eventCount) {\n var pendingPromise = new Pending('core_calendar/crud:confirmDeletion');\n var deleteStrings = [\n {\n key: 'deleteevent',\n component: 'calendar'\n },\n ];\n\n eventCount = parseInt(eventCount, 10);\n var deletePromise;\n var isRepeatedEvent = eventCount > 1;\n if (isRepeatedEvent) {\n deleteStrings.push({\n key: 'confirmeventseriesdelete',\n component: 'calendar',\n param: {\n name: eventTitle,\n count: eventCount,\n },\n });\n\n deletePromise = ModalFactory.create(\n {\n type: ModalDelete.TYPE\n }\n );\n } else {\n deleteStrings.push({\n key: 'confirmeventdelete',\n component: 'calendar',\n param: eventTitle\n });\n\n\n deletePromise = ModalFactory.create({\n type: ModalFactory.types.SAVE_CANCEL,\n });\n }\n\n var stringsPromise = Str.get_strings(deleteStrings);\n\n var finalPromise = $.when(stringsPromise, deletePromise)\n .then(function(strings, deleteModal) {\n deleteModal.setRemoveOnClose(true);\n deleteModal.setTitle(strings[0]);\n deleteModal.setBody(strings[1]);\n if (!isRepeatedEvent) {\n deleteModal.setSaveButtonText(strings[0]);\n }\n\n deleteModal.show();\n\n deleteModal.getRoot().on(ModalEvents.save, function() {\n var pendingPromise = new Pending('calendar/crud:initModal:deletedevent');\n CalendarRepository.deleteEvent(eventId, false)\n .then(function() {\n $('body').trigger(CalendarEvents.deleted, [eventId, false]);\n return;\n })\n .then(pendingPromise.resolve)\n .catch(Notification.exception);\n });\n\n deleteModal.getRoot().on(CalendarEvents.deleteAll, function() {\n var pendingPromise = new Pending('calendar/crud:initModal:deletedallevent');\n CalendarRepository.deleteEvent(eventId, true)\n .then(function() {\n $('body').trigger(CalendarEvents.deleted, [eventId, true]);\n return;\n })\n .then(pendingPromise.resolve)\n .catch(Notification.exception);\n });\n\n return deleteModal;\n })\n .then(function(modal) {\n pendingPromise.resolve();\n\n return modal;\n })\n .catch(Notification.exception);\n\n return finalPromise;\n }\n\n /**\n * Create the event form modal for creating new events and\n * editing existing events.\n *\n * @method registerEventFormModal\n * @param {object} root The calendar root element\n * @return {object} The create modal promise\n */\n var registerEventFormModal = function(root) {\n var eventFormPromise = ModalFactory.create({\n type: ModalEventForm.TYPE,\n large: true\n });\n\n // Bind click event on the new event button.\n root.on('click', CalendarSelectors.actions.create, function(e) {\n eventFormPromise.then(function(modal) {\n var wrapper = root.find(CalendarSelectors.wrapper);\n\n var categoryId = wrapper.data('categoryid');\n if (typeof categoryId !== 'undefined') {\n modal.setCategoryId(categoryId);\n }\n\n // Attempt to find the cell for today.\n // If it can't be found, then use the start time of the first day on the calendar.\n var today = root.find(CalendarSelectors.today);\n var firstDay = root.find(CalendarSelectors.day);\n if (!today.length && firstDay.length) {\n modal.setStartTime(firstDay.data('newEventTimestamp'));\n }\n\n modal.setContextId(wrapper.data('contextId'));\n modal.setCourseId(wrapper.data('courseid'));\n modal.show();\n return;\n })\n .fail(Notification.exception);\n\n e.preventDefault();\n });\n\n root.on('click', CalendarSelectors.actions.edit, function(e) {\n e.preventDefault();\n var target = $(e.currentTarget),\n calendarWrapper = target.closest(CalendarSelectors.wrapper),\n eventWrapper = target.closest(CalendarSelectors.eventItem);\n\n eventFormPromise.then(function(modal) {\n // When something within the calendar tells us the user wants\n // to edit an event then show the event form modal.\n modal.setEventId(eventWrapper.data('eventId'));\n\n modal.setContextId(calendarWrapper.data('contextId'));\n modal.setCourseId(eventWrapper.data('courseId'));\n modal.show();\n\n e.stopImmediatePropagation();\n return;\n }).fail(Notification.exception);\n });\n\n\n return eventFormPromise;\n };\n /**\n * Register the listeners required to remove the event.\n *\n * @param {jQuery} root\n */\n function registerRemove(root) {\n root.on('click', CalendarSelectors.actions.remove, function(e) {\n // Fetch the event title, count, and pass them into the new dialogue.\n var eventSource = $(this).closest(CalendarSelectors.eventItem);\n var eventId = eventSource.data('eventId'),\n eventTitle = eventSource.data('eventTitle'),\n eventCount = eventSource.data('eventCount');\n confirmDeletion(eventId, eventTitle, eventCount);\n\n e.preventDefault();\n });\n }\n\n /**\n * Register the listeners required to edit the event.\n *\n * @param {jQuery} root\n * @param {Promise} eventFormModalPromise\n * @returns {Promise}\n */\n function registerEditListeners(root, eventFormModalPromise) {\n var pendingPromise = new Pending('core_calendar/crud:registerEditListeners');\n\n return eventFormModalPromise\n .then(function(modal) {\n // When something within the calendar tells us the user wants\n // to edit an event then show the event form modal.\n $('body').on(CalendarEvents.editEvent, function(e, eventId) {\n var target = root.find(`[data-event-id=${eventId}]`),\n calendarWrapper = root.find(CalendarSelectors.wrapper);\n\n modal.setEventId(eventId);\n modal.setContextId(calendarWrapper.data('contextId'));\n modal.setReturnElement(target);\n modal.show();\n\n e.stopImmediatePropagation();\n });\n return modal;\n })\n .then(function(modal) {\n pendingPromise.resolve();\n\n return modal;\n })\n .catch(Notification.exception);\n }\n\n return {\n registerRemove: registerRemove,\n registerEditListeners: registerEditListeners,\n registerEventFormModal: registerEventFormModal\n };\n});\n"],"names":["define","$","Str","Notification","CustomEvents","Modal","ModalRegistry","ModalFactory","ModalEvents","ModalEventForm","CalendarRepository","CalendarEvents","ModalDelete","CalendarSelectors","Pending","registerRemove","root","on","actions","remove","e","eventSource","this","closest","eventItem","eventId","eventTitle","eventCount","deletePromise","pendingPromise","deleteStrings","key","component","isRepeatedEvent","parseInt","push","param","name","count","create","type","TYPE","types","SAVE_CANCEL","stringsPromise","get_strings","when","then","strings","deleteModal","setRemoveOnClose","setTitle","setBody","setSaveButtonText","show","getRoot","save","deleteEvent","trigger","deleted","resolve","catch","exception","deleteAll","modal","confirmDeletion","data","preventDefault","registerEditListeners","eventFormModalPromise","editEvent","target","find","calendarWrapper","wrapper","setEventId","setContextId","setReturnElement","stopImmediatePropagation","registerEventFormModal","eventFormPromise","large","categoryId","setCategoryId","today","firstDay","day","length","setStartTime","setCourseId","fail","edit","currentTarget","eventWrapper"],"mappings":"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"}
\ No newline at end of file
+{"version":3,"file":"crud.min.js","sources":["../src/crud.js"],"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 * A module to handle CRUD operations within the UI.\n *\n * @module core_calendar/crud\n * @copyright 2017 Andrew Nicols \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine([\n 'jquery',\n 'core/str',\n 'core/notification',\n 'core/custom_interaction_events',\n 'core/modal',\n 'core/modal_registry',\n 'core/modal_factory',\n 'core/modal_events',\n 'core_calendar/modal_event_form',\n 'core_calendar/repository',\n 'core_calendar/events',\n 'core_calendar/modal_delete',\n 'core_calendar/selectors',\n 'core/pending',\n],\nfunction(\n $,\n Str,\n Notification,\n CustomEvents,\n Modal,\n ModalRegistry,\n ModalFactory,\n ModalEvents,\n ModalEventForm,\n CalendarRepository,\n CalendarEvents,\n ModalDelete,\n CalendarSelectors,\n Pending\n) {\n\n /**\n * Prepares the action for the summary modal's delete action.\n *\n * @param {Number} eventId The ID of the event.\n * @param {string} eventTitle The event title.\n * @param {Number} eventCount The number of events in the series.\n * @return {Promise}\n */\n function confirmDeletion(eventId, eventTitle, eventCount) {\n var pendingPromise = new Pending('core_calendar/crud:confirmDeletion');\n var deleteStrings = [\n {\n key: 'deleteevent',\n component: 'calendar'\n },\n ];\n\n eventCount = parseInt(eventCount, 10);\n var deletePromise;\n var isRepeatedEvent = eventCount > 1;\n if (isRepeatedEvent) {\n deleteStrings.push({\n key: 'confirmeventseriesdelete',\n component: 'calendar',\n param: {\n name: eventTitle,\n count: eventCount,\n },\n });\n\n deletePromise = ModalFactory.create(\n {\n type: ModalDelete.TYPE\n }\n );\n } else {\n deleteStrings.push({\n key: 'confirmeventdelete',\n component: 'calendar',\n param: eventTitle\n });\n\n\n deletePromise = ModalFactory.create({\n type: ModalFactory.types.SAVE_CANCEL,\n });\n }\n\n var stringsPromise = Str.get_strings(deleteStrings);\n\n var finalPromise = $.when(stringsPromise, deletePromise)\n .then(function(strings, deleteModal) {\n deleteModal.setRemoveOnClose(true);\n deleteModal.setTitle(strings[0]);\n deleteModal.setBody(strings[1]);\n if (!isRepeatedEvent) {\n deleteModal.setSaveButtonText(strings[0]);\n }\n\n deleteModal.show();\n\n deleteModal.getRoot().on(ModalEvents.save, function() {\n var pendingPromise = new Pending('calendar/crud:initModal:deletedevent');\n CalendarRepository.deleteEvent(eventId, false)\n .then(function() {\n $('body').trigger(CalendarEvents.deleted, [eventId, false]);\n return;\n })\n .then(pendingPromise.resolve)\n .catch(Notification.exception);\n });\n\n deleteModal.getRoot().on(CalendarEvents.deleteAll, function() {\n var pendingPromise = new Pending('calendar/crud:initModal:deletedallevent');\n CalendarRepository.deleteEvent(eventId, true)\n .then(function() {\n $('body').trigger(CalendarEvents.deleted, [eventId, true]);\n return;\n })\n .then(pendingPromise.resolve)\n .catch(Notification.exception);\n });\n\n return deleteModal;\n })\n .then(function(modal) {\n pendingPromise.resolve();\n\n return modal;\n })\n .catch(Notification.exception);\n\n return finalPromise;\n }\n\n /**\n * Create the event form modal for creating new events and\n * editing existing events.\n *\n * @method registerEventFormModal\n * @param {object} root The calendar root element\n * @return {object} The create modal promise\n */\n var registerEventFormModal = function(root) {\n var eventFormPromise = ModalFactory.create({\n type: ModalEventForm.TYPE,\n large: true\n });\n\n // Bind click event on the new event button.\n root.on('click', CalendarSelectors.actions.create, function(e) {\n eventFormPromise.then(function(modal) {\n var wrapper = root.find(CalendarSelectors.wrapper);\n\n var categoryId = wrapper.data('categoryid');\n if (typeof categoryId !== 'undefined') {\n modal.setCategoryId(categoryId);\n }\n\n // Attempt to find the cell for today.\n // If it can't be found, then use the start time of the first day on the calendar.\n var today = root.find(CalendarSelectors.today);\n var firstDay = root.find(CalendarSelectors.day);\n if (!today.length && firstDay.length) {\n modal.setStartTime(firstDay.data('newEventTimestamp'));\n }\n\n modal.setContextId(wrapper.data('contextId'));\n modal.setCourseId(wrapper.data('courseid'));\n modal.show();\n return;\n })\n .fail(Notification.exception);\n\n e.preventDefault();\n });\n\n root.on('click', CalendarSelectors.actions.edit, function(e) {\n e.preventDefault();\n var target = $(e.currentTarget),\n calendarWrapper = target.closest(CalendarSelectors.wrapper),\n eventWrapper = target.closest(CalendarSelectors.eventItem);\n\n eventFormPromise.then(function(modal) {\n // When something within the calendar tells us the user wants\n // to edit an event then show the event form modal.\n modal.setEventId(eventWrapper.data('eventId'));\n\n modal.setContextId(calendarWrapper.data('contextId'));\n modal.setCourseId(eventWrapper.data('courseId'));\n modal.show();\n\n e.stopImmediatePropagation();\n return;\n }).fail(Notification.exception);\n });\n\n\n return eventFormPromise;\n };\n /**\n * Register the listeners required to remove the event.\n *\n * @param {jQuery} root\n */\n function registerRemove(root) {\n root.on('click', CalendarSelectors.actions.remove, function(e) {\n // Fetch the event title, count, and pass them into the new dialogue.\n var eventSource = $(this).closest(CalendarSelectors.eventItem);\n var eventId = eventSource.data('eventId'),\n eventTitle = eventSource.data('eventTitle'),\n eventCount = eventSource.data('eventCount');\n confirmDeletion(eventId, eventTitle, eventCount);\n\n e.preventDefault();\n });\n }\n\n /**\n * Register the listeners required to edit the event.\n *\n * @param {jQuery} root\n * @param {Promise} eventFormModalPromise\n * @returns {Promise}\n */\n function registerEditListeners(root, eventFormModalPromise) {\n var pendingPromise = new Pending('core_calendar/crud:registerEditListeners');\n\n return eventFormModalPromise\n .then(function(modal) {\n // When something within the calendar tells us the user wants\n // to edit an event then show the event form modal.\n $('body').on(CalendarEvents.editEvent, function(e, eventId) {\n var target = root.find(`[data-event-id=${eventId}]`),\n calendarWrapper = root.find(CalendarSelectors.wrapper);\n\n modal.setEventId(eventId);\n modal.setContextId(calendarWrapper.data('contextId'));\n modal.setReturnElement(target);\n modal.show();\n\n e.stopImmediatePropagation();\n });\n return modal;\n })\n .then(function(modal) {\n pendingPromise.resolve();\n\n return modal;\n })\n .catch(Notification.exception);\n }\n\n return {\n registerRemove: registerRemove,\n registerEditListeners: registerEditListeners,\n registerEventFormModal: registerEventFormModal\n };\n});\n"],"names":["define","$","Str","Notification","CustomEvents","Modal","ModalRegistry","ModalFactory","ModalEvents","ModalEventForm","CalendarRepository","CalendarEvents","ModalDelete","CalendarSelectors","Pending","registerRemove","root","on","actions","remove","e","eventSource","this","closest","eventItem","eventId","eventTitle","eventCount","deletePromise","pendingPromise","deleteStrings","key","component","isRepeatedEvent","parseInt","push","param","name","count","create","type","TYPE","types","SAVE_CANCEL","stringsPromise","get_strings","when","then","strings","deleteModal","setRemoveOnClose","setTitle","setBody","setSaveButtonText","show","getRoot","save","deleteEvent","trigger","deleted","resolve","catch","exception","deleteAll","modal","confirmDeletion","data","preventDefault","registerEditListeners","eventFormModalPromise","editEvent","target","find","concat","calendarWrapper","wrapper","setEventId","setContextId","setReturnElement","stopImmediatePropagation","registerEventFormModal","eventFormPromise","large","categoryId","setCategoryId","today","firstDay","day","length","setStartTime","setCourseId","fail","edit","currentTarget","eventWrapper"],"mappings":"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"}
\ No newline at end of file
diff --git a/calendar/amd/build/drag_drop_data_store.min.js.map b/calendar/amd/build/drag_drop_data_store.min.js.map
index bbba73e8937..fa4f7e42cdf 100644
--- a/calendar/amd/build/drag_drop_data_store.min.js.map
+++ b/calendar/amd/build/drag_drop_data_store.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"drag_drop_data_store.min.js","sources":["../src/drag_drop_data_store.js"],"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 * A javascript module to store calendar drag and drop data.\n *\n * This module is unfortunately required because of the limitations\n * of the HTML5 drag and drop API and it's ability to provide data\n * between the different stages of the drag/drop lifecycle.\n *\n * @module core_calendar/drag_drop_data_store\n * @copyright 2017 Ryan Wyllie \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine([], function() {\n /* @var {int|null} eventId The id of the event being dragged */\n var eventId = null;\n /* @var {int|null} durationDays How many days the event spans */\n var durationDays = null;\n /* @var {int|null} minTimestart The earliest valid timestart */\n var minTimestart = null;\n /* @var {int|null} maxTimestart The latest valid tiemstart */\n var maxTimestart = null;\n /* @var {string|null} minError Error message for min timestamp violation */\n var minError = null;\n /* @var {string|null} maxError Error message for max timestamp violation */\n var maxError = null;\n\n /**\n * Store the id of the event being dragged.\n *\n * @param {int} id The event id\n */\n var setEventId = function(id) {\n eventId = id;\n };\n\n /**\n * Get the stored event id.\n *\n * @return {int|null}\n */\n var getEventId = function() {\n return eventId;\n };\n\n /**\n * Check if the store has an event id.\n *\n * @return {bool}\n */\n var hasEventId = function() {\n return eventId !== null;\n };\n\n /**\n * Store the duration (in days) of the event being dragged.\n *\n * @param {int} days Number of days the event spans\n */\n var setDurationDays = function(days) {\n durationDays = days;\n };\n\n /**\n * Get the stored number of days.\n *\n * @return {int|null}\n */\n var getDurationDays = function() {\n return durationDays;\n };\n\n /**\n * Store the minimum timestart valid for an event being dragged.\n *\n * @param {int} timestamp The unix timstamp\n */\n var setMinTimestart = function(timestamp) {\n minTimestart = timestamp;\n };\n\n /**\n * Get the minimum valid timestart.\n *\n * @return {int|null}\n */\n var getMinTimestart = function() {\n return minTimestart;\n };\n\n /**\n * Check if a minimum timestamp is set.\n *\n * @return {bool}\n */\n var hasMinTimestart = function() {\n return minTimestart !== null;\n };\n\n /**\n * Store the maximum timestart valid for an event being dragged.\n *\n * @param {int} timestamp The unix timstamp\n */\n var setMaxTimestart = function(timestamp) {\n maxTimestart = timestamp;\n };\n\n /**\n * Get the maximum valid timestart.\n *\n * @return {int|null}\n */\n var getMaxTimestart = function() {\n return maxTimestart;\n };\n\n /**\n * Check if a maximum timestamp is set.\n *\n * @return {bool}\n */\n var hasMaxTimestart = function() {\n return maxTimestart !== null;\n };\n\n /**\n * Store the error string to display if trying to drag an event\n * earlier than the minimum allowed date.\n *\n * @param {string} message The error message\n */\n var setMinError = function(message) {\n minError = message;\n };\n\n /**\n * Get the error message for a minimum time start violation.\n *\n * @return {string|null}\n */\n var getMinError = function() {\n return minError;\n };\n\n /**\n * Store the error string to display if trying to drag an event\n * later than the maximum allowed date.\n *\n * @param {string} message The error message\n */\n var setMaxError = function(message) {\n maxError = message;\n };\n\n /**\n * Get the error message for a maximum time start violation.\n *\n * @return {string|null}\n */\n var getMaxError = function() {\n return maxError;\n };\n\n /**\n * Reset all of the stored values.\n */\n var clearAll = function() {\n setEventId(null);\n setDurationDays(null);\n setMinTimestart(null);\n setMaxTimestart(null);\n setMinError(null);\n setMaxError(null);\n };\n\n return {\n setEventId: setEventId,\n getEventId: getEventId,\n hasEventId: hasEventId,\n setDurationDays: setDurationDays,\n getDurationDays: getDurationDays,\n setMinTimestart: setMinTimestart,\n getMinTimestart: getMinTimestart,\n hasMinTimestart: hasMinTimestart,\n setMaxTimestart: setMaxTimestart,\n getMaxTimestart: getMaxTimestart,\n hasMaxTimestart: hasMaxTimestart,\n setMinError: setMinError,\n getMinError: getMinError,\n setMaxError: setMaxError,\n getMaxError: getMaxError,\n clearAll: clearAll\n };\n});\n"],"names":["define","eventId","durationDays","minTimestart","maxTimestart","minError","maxError","setEventId","id","setDurationDays","days","setMinTimestart","timestamp","setMaxTimestart","setMinError","message","setMaxError","getEventId","hasEventId","getDurationDays","getMinTimestart","hasMinTimestart","getMaxTimestart","hasMaxTimestart","getMinError","getMaxError","clearAll"],"mappings":";;;;;;;;;;;AA0BAA,4CAAO,IAAI,eAEHC,QAAU,KAEVC,aAAe,KAEfC,aAAe,KAEfC,aAAe,KAEfC,SAAW,KAEXC,SAAW,KAOXC,WAAa,SAASC,IACtBP,QAAUO,IA0BVC,gBAAkB,SAASC,MAC3BR,aAAeQ,MAiBfC,gBAAkB,SAASC,WAC3BT,aAAeS,WA0BfC,gBAAkB,SAASD,WAC3BR,aAAeQ,WA2BfE,YAAc,SAASC,SACvBV,SAAWU,SAkBXC,YAAc,SAASD,SACvBT,SAAWS,eAwBR,CACHR,WAAYA,WACZU,WAzIa,kBACNhB,SAyIPiB,WAjIa,kBACM,OAAZjB,SAiIPQ,gBAAiBA,gBACjBU,gBAjHkB,kBACXjB,cAiHPS,gBAAiBA,gBACjBS,gBAjGkB,kBACXjB,cAiGPkB,gBAzFkB,kBACM,OAAjBlB,cAyFPU,gBAAiBA,gBACjBS,gBAzEkB,kBACXlB,cAyEPmB,gBAjEkB,kBACM,OAAjBnB,cAiEPU,YAAaA,YACbU,YAhDc,kBACPnB,UAgDPW,YAAaA,YACbS,YA/Bc,kBACPnB,UA+BPoB,SAzBW,WACXnB,WAAW,MACXE,gBAAgB,MAChBE,gBAAgB,MAChBE,gBAAgB,MAChBC,YAAY,MACZE,YAAY"}
\ No newline at end of file
+{"version":3,"file":"drag_drop_data_store.min.js","sources":["../src/drag_drop_data_store.js"],"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 * A javascript module to store calendar drag and drop data.\n *\n * This module is unfortunately required because of the limitations\n * of the HTML5 drag and drop API and it's ability to provide data\n * between the different stages of the drag/drop lifecycle.\n *\n * @module core_calendar/drag_drop_data_store\n * @copyright 2017 Ryan Wyllie \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine([], function() {\n /* @var {int|null} eventId The id of the event being dragged */\n var eventId = null;\n /* @var {int|null} durationDays How many days the event spans */\n var durationDays = null;\n /* @var {int|null} minTimestart The earliest valid timestart */\n var minTimestart = null;\n /* @var {int|null} maxTimestart The latest valid tiemstart */\n var maxTimestart = null;\n /* @var {string|null} minError Error message for min timestamp violation */\n var minError = null;\n /* @var {string|null} maxError Error message for max timestamp violation */\n var maxError = null;\n\n /**\n * Store the id of the event being dragged.\n *\n * @param {int} id The event id\n */\n var setEventId = function(id) {\n eventId = id;\n };\n\n /**\n * Get the stored event id.\n *\n * @return {int|null}\n */\n var getEventId = function() {\n return eventId;\n };\n\n /**\n * Check if the store has an event id.\n *\n * @return {bool}\n */\n var hasEventId = function() {\n return eventId !== null;\n };\n\n /**\n * Store the duration (in days) of the event being dragged.\n *\n * @param {int} days Number of days the event spans\n */\n var setDurationDays = function(days) {\n durationDays = days;\n };\n\n /**\n * Get the stored number of days.\n *\n * @return {int|null}\n */\n var getDurationDays = function() {\n return durationDays;\n };\n\n /**\n * Store the minimum timestart valid for an event being dragged.\n *\n * @param {int} timestamp The unix timstamp\n */\n var setMinTimestart = function(timestamp) {\n minTimestart = timestamp;\n };\n\n /**\n * Get the minimum valid timestart.\n *\n * @return {int|null}\n */\n var getMinTimestart = function() {\n return minTimestart;\n };\n\n /**\n * Check if a minimum timestamp is set.\n *\n * @return {bool}\n */\n var hasMinTimestart = function() {\n return minTimestart !== null;\n };\n\n /**\n * Store the maximum timestart valid for an event being dragged.\n *\n * @param {int} timestamp The unix timstamp\n */\n var setMaxTimestart = function(timestamp) {\n maxTimestart = timestamp;\n };\n\n /**\n * Get the maximum valid timestart.\n *\n * @return {int|null}\n */\n var getMaxTimestart = function() {\n return maxTimestart;\n };\n\n /**\n * Check if a maximum timestamp is set.\n *\n * @return {bool}\n */\n var hasMaxTimestart = function() {\n return maxTimestart !== null;\n };\n\n /**\n * Store the error string to display if trying to drag an event\n * earlier than the minimum allowed date.\n *\n * @param {string} message The error message\n */\n var setMinError = function(message) {\n minError = message;\n };\n\n /**\n * Get the error message for a minimum time start violation.\n *\n * @return {string|null}\n */\n var getMinError = function() {\n return minError;\n };\n\n /**\n * Store the error string to display if trying to drag an event\n * later than the maximum allowed date.\n *\n * @param {string} message The error message\n */\n var setMaxError = function(message) {\n maxError = message;\n };\n\n /**\n * Get the error message for a maximum time start violation.\n *\n * @return {string|null}\n */\n var getMaxError = function() {\n return maxError;\n };\n\n /**\n * Reset all of the stored values.\n */\n var clearAll = function() {\n setEventId(null);\n setDurationDays(null);\n setMinTimestart(null);\n setMaxTimestart(null);\n setMinError(null);\n setMaxError(null);\n };\n\n return {\n setEventId: setEventId,\n getEventId: getEventId,\n hasEventId: hasEventId,\n setDurationDays: setDurationDays,\n getDurationDays: getDurationDays,\n setMinTimestart: setMinTimestart,\n getMinTimestart: getMinTimestart,\n hasMinTimestart: hasMinTimestart,\n setMaxTimestart: setMaxTimestart,\n getMaxTimestart: getMaxTimestart,\n hasMaxTimestart: hasMaxTimestart,\n setMinError: setMinError,\n getMinError: getMinError,\n setMaxError: setMaxError,\n getMaxError: getMaxError,\n clearAll: clearAll\n };\n});\n"],"names":["define","eventId","durationDays","minTimestart","maxTimestart","minError","maxError","setEventId","id","setDurationDays","days","setMinTimestart","timestamp","setMaxTimestart","setMinError","message","setMaxError","getEventId","hasEventId","getDurationDays","getMinTimestart","hasMinTimestart","getMaxTimestart","hasMaxTimestart","getMinError","getMaxError","clearAll"],"mappings":";;;;;;;;;;;AA0BAA,OAAM,qCAAC,IAAI,WAEP,IAAIC,QAAU,KAEVC,aAAe,KAEfC,aAAe,KAEfC,aAAe,KAEfC,SAAW,KAEXC,SAAW,KAOXC,WAAa,SAASC,IACtBP,QAAUO,IA0BVC,gBAAkB,SAASC,MAC3BR,aAAeQ,MAiBfC,gBAAkB,SAASC,WAC3BT,aAAeS,WA0BfC,gBAAkB,SAASD,WAC3BR,aAAeQ,WA2BfE,YAAc,SAASC,SACvBV,SAAWU,SAkBXC,YAAc,SAASD,SACvBT,SAAWS,SAwBf,MAAO,CACHR,WAAYA,WACZU,WAzIa,WACb,OAAOhB,SAyIPiB,WAjIa,WACb,OAAmB,OAAZjB,SAiIPQ,gBAAiBA,gBACjBU,gBAjHkB,WAClB,OAAOjB,cAiHPS,gBAAiBA,gBACjBS,gBAjGkB,WAClB,OAAOjB,cAiGPkB,gBAzFkB,WAClB,OAAwB,OAAjBlB,cAyFPU,gBAAiBA,gBACjBS,gBAzEkB,WAClB,OAAOlB,cAyEPmB,gBAjEkB,WAClB,OAAwB,OAAjBnB,cAiEPU,YAAaA,YACbU,YAhDc,WACd,OAAOnB,UAgDPW,YAAaA,YACbS,YA/Bc,WACd,OAAOnB,UA+BPoB,SAzBW,WACXnB,WAAW,MACXE,gBAAgB,MAChBE,gBAAgB,MAChBE,gBAAgB,MAChBC,YAAY,MACZE,YAAY,OAqBpB"}
\ No newline at end of file
diff --git a/calendar/amd/build/event_form.min.js.map b/calendar/amd/build/event_form.min.js.map
index f6f74a8f14b..a2c7a0d78b5 100644
--- a/calendar/amd/build/event_form.min.js.map
+++ b/calendar/amd/build/event_form.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"event_form.min.js","sources":["../src/event_form.js"],"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 * A javascript module to enhance the event form.\n *\n * @module core_calendar/event_form\n * @copyright 2017 Ryan Wyllie \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(['jquery', 'core_calendar/repository', 'core/notification'], function($, CalendarRepository, Notification) {\n\n var SELECTORS = {\n EVENT_GROUP_COURSE_ID: '[name=\"groupcourseid\"]',\n EVENT_GROUP_ID: '[name=\"groupid\"]',\n SELECT_OPTION: 'option'\n };\n\n /**\n * Listen for when the user changes the group course when configuring\n * a group event and filter the options in the group select to only\n * show the groups available within the course the user has selected.\n *\n * @method addCourseGroupSelectListeners\n * @param {object} formElement The root form element\n */\n var addCourseGroupSelectListeners = function(formElement) {\n var courseGroupSelect = formElement.find(SELECTORS.EVENT_GROUP_COURSE_ID);\n\n var loadGroupSelectOptions = function(groups) {\n var groupSelect = formElement.find(SELECTORS.EVENT_GROUP_ID),\n groupSelectOptions = groupSelect.find(SELECTORS.SELECT_OPTION),\n courseGroups = $(groups);\n\n // Let's clear all options first.\n groupSelectOptions.remove();\n groupSelect.prop(\"disabled\", false);\n courseGroups.each(function(id, group) {\n $(groupSelect).append($(\"
\").attr(\"value\", group.id).text(group.name));\n });\n };\n\n // If the user choose a course in the selector do a WS request to get groups.\n courseGroupSelect.on('change', function() {\n var courseId = formElement.find(SELECTORS.EVENT_GROUP_COURSE_ID).val();\n CalendarRepository.getCourseGroupsData(courseId)\n .then(function(groups) {\n return loadGroupSelectOptions(groups);\n })\n .catch(Notification.exception);\n });\n };\n\n /**\n * Initialise all of the form enhancements.\n *\n * @method init\n * @param {string} formId The value of the form's id attribute\n */\n var init = function(formId) {\n var formElement = $('#' + formId);\n addCourseGroupSelectListeners(formElement);\n };\n\n return {\n init: init,\n };\n});\n"],"names":["define","$","CalendarRepository","Notification","SELECTORS","addCourseGroupSelectListeners","formElement","courseGroupSelect","find","on","courseId","val","getCourseGroupsData","then","groups","groupSelect","groupSelectOptions","courseGroups","remove","prop","each","id","group","append","attr","text","name","loadGroupSelectOptions","catch","exception","init","formId"],"mappings":";;;;;;;AAsBAA,kCAAO,CAAC,SAAU,2BAA4B,sBAAsB,SAASC,EAAGC,mBAAoBC,kBAE5FC,gCACuB,yBADvBA,yBAEgB,mBAFhBA,wBAGe,SAWfC,8BAAgC,SAASC,iBACrCC,kBAAoBD,YAAYE,KAAKJ,iCAgBzCG,kBAAkBE,GAAG,UAAU,eACvBC,SAAWJ,YAAYE,KAAKJ,iCAAiCO,MACjET,mBAAmBU,oBAAoBF,UAClCG,MAAK,SAASC,eAjBM,SAASA,YAC9BC,YAAcT,YAAYE,KAAKJ,0BAC/BY,mBAAqBD,YAAYP,KAAKJ,yBACtCa,aAAehB,EAAEa,QAGrBE,mBAAmBE,SACnBH,YAAYI,KAAK,YAAY,GAC7BF,aAAaG,MAAK,SAASC,GAAIC,OAC3BrB,EAAEc,aAAaQ,OAAOtB,EAAE,qBAAqBuB,KAAK,QAASF,MAAMD,IAAII,KAAKH,MAAMI,UASrEC,CAAuBb,WAEjCc,MAAMzB,aAAa0B,qBAezB,CACHC,KANO,SAASC,YACZzB,YAAcL,EAAE,IAAM8B,QAC1B1B,8BAA8BC"}
\ No newline at end of file
+{"version":3,"file":"event_form.min.js","sources":["../src/event_form.js"],"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 * A javascript module to enhance the event form.\n *\n * @module core_calendar/event_form\n * @copyright 2017 Ryan Wyllie \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(['jquery', 'core_calendar/repository', 'core/notification'], function($, CalendarRepository, Notification) {\n\n var SELECTORS = {\n EVENT_GROUP_COURSE_ID: '[name=\"groupcourseid\"]',\n EVENT_GROUP_ID: '[name=\"groupid\"]',\n SELECT_OPTION: 'option'\n };\n\n /**\n * Listen for when the user changes the group course when configuring\n * a group event and filter the options in the group select to only\n * show the groups available within the course the user has selected.\n *\n * @method addCourseGroupSelectListeners\n * @param {object} formElement The root form element\n */\n var addCourseGroupSelectListeners = function(formElement) {\n var courseGroupSelect = formElement.find(SELECTORS.EVENT_GROUP_COURSE_ID);\n\n var loadGroupSelectOptions = function(groups) {\n var groupSelect = formElement.find(SELECTORS.EVENT_GROUP_ID),\n groupSelectOptions = groupSelect.find(SELECTORS.SELECT_OPTION),\n courseGroups = $(groups);\n\n // Let's clear all options first.\n groupSelectOptions.remove();\n groupSelect.prop(\"disabled\", false);\n courseGroups.each(function(id, group) {\n $(groupSelect).append($(\"
\").attr(\"value\", group.id).text(group.name));\n });\n };\n\n // If the user choose a course in the selector do a WS request to get groups.\n courseGroupSelect.on('change', function() {\n var courseId = formElement.find(SELECTORS.EVENT_GROUP_COURSE_ID).val();\n CalendarRepository.getCourseGroupsData(courseId)\n .then(function(groups) {\n return loadGroupSelectOptions(groups);\n })\n .catch(Notification.exception);\n });\n };\n\n /**\n * Initialise all of the form enhancements.\n *\n * @method init\n * @param {string} formId The value of the form's id attribute\n */\n var init = function(formId) {\n var formElement = $('#' + formId);\n addCourseGroupSelectListeners(formElement);\n };\n\n return {\n init: init,\n };\n});\n"],"names":["define","$","CalendarRepository","Notification","SELECTORS","addCourseGroupSelectListeners","formElement","courseGroupSelect","find","on","courseId","val","getCourseGroupsData","then","groups","groupSelect","groupSelectOptions","courseGroups","remove","prop","each","id","group","append","attr","text","name","loadGroupSelectOptions","catch","exception","init","formId"],"mappings":";;;;;;;AAsBAA,OAAO,2BAAA,CAAC,SAAU,2BAA4B,sBAAsB,SAASC,EAAGC,mBAAoBC,cAEhG,IAAIC,gCACuB,yBADvBA,yBAEgB,mBAFhBA,wBAGe,SAWfC,8BAAgC,SAASC,aACzC,IAAIC,kBAAoBD,YAAYE,KAAKJ,iCAgBzCG,kBAAkBE,GAAG,UAAU,WAC3B,IAAIC,SAAWJ,YAAYE,KAAKJ,iCAAiCO,MACjET,mBAAmBU,oBAAoBF,UAClCG,MAAK,SAASC,QACX,OAlBiB,SAASA,QAClC,IAAIC,YAAcT,YAAYE,KAAKJ,0BAC/BY,mBAAqBD,YAAYP,KAAKJ,yBACtCa,aAAehB,EAAEa,QAGrBE,mBAAmBE,SACnBH,YAAYI,KAAK,YAAY,GAC7BF,aAAaG,MAAK,SAASC,GAAIC,OAC3BrB,EAAEc,aAAaQ,OAAOtB,EAAE,qBAAqBuB,KAAK,QAASF,MAAMD,IAAII,KAAKH,MAAMI,MACpF,IAQeC,CAAuBb,OACjC,IACAc,MAAMzB,aAAa0B,UAC5B,KAcJ,MAAO,CACHC,KANO,SAASC,QAChB,IAAIzB,YAAcL,EAAE,IAAM8B,QAC1B1B,8BAA8BC,cAMtC"}
\ No newline at end of file
diff --git a/calendar/amd/build/events.min.js.map b/calendar/amd/build/events.min.js.map
index 9f0441077e5..fd304cdde7d 100644
--- a/calendar/amd/build/events.min.js.map
+++ b/calendar/amd/build/events.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"events.min.js","sources":["../src/events.js"],"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 * Contain the events the calendar component can fire.\n *\n * @module core_calendar/events\n * @copyright 2017 Simey Lameze \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine([], function() {\n return {\n created: 'calendar-events:created',\n deleted: 'calendar-events:deleted',\n deleteAll: 'calendar-events:delete_all',\n updated: 'calendar-events:updated',\n editEvent: 'calendar-events:edit_event',\n editActionEvent: 'calendar-events:edit_action_event',\n eventMoved: 'calendar-events:event_moved',\n dayChanged: 'calendar-events:day_changed',\n monthChanged: 'calendar-events:month_changed',\n moveEvent: 'calendar-events:move_event',\n filterChanged: 'calendar-events:filter_changed',\n courseChanged: 'calendar-events:course_changed',\n viewUpdated: 'calendar-events:view_updated',\n };\n});\n"],"names":["define","created","deleted","deleteAll","updated","editEvent","editActionEvent","eventMoved","dayChanged","monthChanged","moveEvent","filterChanged","courseChanged","viewUpdated"],"mappings":";;;;;;;AAsBAA,8BAAO,IAAI,iBACA,CACHC,QAAS,0BACTC,QAAS,0BACTC,UAAW,6BACXC,QAAS,0BACTC,UAAW,6BACXC,gBAAiB,oCACjBC,WAAY,8BACZC,WAAY,8BACZC,aAAc,gCACdC,UAAW,6BACXC,cAAe,iCACfC,cAAe,iCACfC,YAAa"}
\ No newline at end of file
+{"version":3,"file":"events.min.js","sources":["../src/events.js"],"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 * Contain the events the calendar component can fire.\n *\n * @module core_calendar/events\n * @copyright 2017 Simey Lameze \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine([], function() {\n return {\n created: 'calendar-events:created',\n deleted: 'calendar-events:deleted',\n deleteAll: 'calendar-events:delete_all',\n updated: 'calendar-events:updated',\n editEvent: 'calendar-events:edit_event',\n editActionEvent: 'calendar-events:edit_action_event',\n eventMoved: 'calendar-events:event_moved',\n dayChanged: 'calendar-events:day_changed',\n monthChanged: 'calendar-events:month_changed',\n moveEvent: 'calendar-events:move_event',\n filterChanged: 'calendar-events:filter_changed',\n courseChanged: 'calendar-events:course_changed',\n viewUpdated: 'calendar-events:view_updated',\n };\n});\n"],"names":["define","created","deleted","deleteAll","updated","editEvent","editActionEvent","eventMoved","dayChanged","monthChanged","moveEvent","filterChanged","courseChanged","viewUpdated"],"mappings":";;;;;;;AAsBAA,OAAM,uBAAC,IAAI,WACP,MAAO,CACHC,QAAS,0BACTC,QAAS,0BACTC,UAAW,6BACXC,QAAS,0BACTC,UAAW,6BACXC,gBAAiB,oCACjBC,WAAY,8BACZC,WAAY,8BACZC,aAAc,gCACdC,UAAW,6BACXC,cAAe,iCACfC,cAAe,iCACfC,YAAa,+BAErB"}
\ No newline at end of file
diff --git a/calendar/amd/build/export.min.js.map b/calendar/amd/build/export.min.js.map
index 994ab043961..7b6584ca2b5 100644
--- a/calendar/amd/build/export.min.js.map
+++ b/calendar/amd/build/export.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"export.min.js","sources":["../src/export.js"],"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 * A javascript module to enhance the calendar export form.\n *\n * @module core_calendar/export\n * @copyright 2021 Jun Pataleta\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport 'core/copy_to_clipboard';\n\n/**\n * Selectors for the calendar export page.\n *\n * @property {string} copyUrlId The element ID of the Copy URL button.\n */\nconst selectors = {\n copyUrlId: 'copyexporturl',\n};\n\n/**\n * Initialises the calendar export JS module.\n *\n * @method init\n */\nexport const init = () => {\n // Enable the copy URL button and focus on it.\n const copyUrl = document.getElementById(selectors.copyUrlId);\n copyUrl.removeAttribute('disabled');\n copyUrl.focus();\n};\n"],"names":["selectors","copyUrl","document","getElementById","removeAttribute","focus"],"mappings":";;;;;;;;MA8BMA,oBACS,8BAQK,WAEVC,QAAUC,SAASC,eAAeH,qBACxCC,QAAQG,gBAAgB,YACxBH,QAAQI"}
\ No newline at end of file
+{"version":3,"file":"export.min.js","sources":["../src/export.js"],"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 * A javascript module to enhance the calendar export form.\n *\n * @module core_calendar/export\n * @copyright 2021 Jun Pataleta\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport 'core/copy_to_clipboard';\n\n/**\n * Selectors for the calendar export page.\n *\n * @property {string} copyUrlId The element ID of the Copy URL button.\n */\nconst selectors = {\n copyUrlId: 'copyexporturl',\n};\n\n/**\n * Initialises the calendar export JS module.\n *\n * @method init\n */\nexport const init = () => {\n // Enable the copy URL button and focus on it.\n const copyUrl = document.getElementById(selectors.copyUrlId);\n copyUrl.removeAttribute('disabled');\n copyUrl.focus();\n};\n"],"names":["selectors","_exports","init","copyUrl","document","getElementById","removeAttribute","focus"],"mappings":";;;;;;;;AA8BA,MAAMA,oBACS,gBAabC,SAAAC,KALkBA,KAEhB,MAAMC,QAAUC,SAASC,eAAeL,qBACxCG,QAAQG,gBAAgB,YACxBH,QAAQI,OAAO,CACjB"}
\ No newline at end of file
diff --git a/calendar/amd/build/manage_subscriptions.min.js.map b/calendar/amd/build/manage_subscriptions.min.js.map
index cc6f51151b5..ea9f456387a 100644
--- a/calendar/amd/build/manage_subscriptions.min.js.map
+++ b/calendar/amd/build/manage_subscriptions.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"manage_subscriptions.min.js","sources":["../src/manage_subscriptions.js"],"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 * A module to handle Delete/Update operations of the manage subscription page.\n *\n * @module core_calendar/manage_subscriptions\n * @copyright 2021 Huong Nguyen \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @since 4.0\n */\n\nimport * as CalendarSelectors from 'core_calendar/selectors';\nimport * as CalendarRepository from 'core_calendar/repository';\nimport * as Modal from 'core/modal_factory';\nimport * as ModalEvents from 'core/modal_events';\nimport {displayException, addNotification, fetchNotifications} from 'core/notification';\nimport Prefetch from 'core/prefetch';\nimport {get_string as getString} from 'core/str';\nimport {eventTypes} from 'core/local/inplace_editable/events';\n\n/**\n * Get subscription id for given element.\n *\n * @param {HTMLElement} element update/delete link\n * @return {Number}\n */\nconst getSubscriptionId = element => {\n return parseInt(element.closest('tr').dataset.subid);\n};\n\n/**\n * Get subscription name for given element.\n *\n * @param {HTMLElement} element update/delete link\n * @return {String}\n */\nconst getSubscriptionName = element => {\n return element.closest('tr').dataset.subname;\n};\n\n/**\n * Get subscription table row for subscription id.\n *\n * @param {string} subscriptionId Subscription id\n * @return {Element}\n */\nconst getSubscriptionRow = subscriptionId => {\n return document.querySelector(`tr[data-subid=\"${subscriptionId}\"]`);\n};\n\n/**\n * Create modal.\n *\n * @param {HTMLElement} element\n * @param {string} messageCode Message code.\n * @return {promise} Promise for modal\n */\nconst createModal = (element, messageCode) => {\n const subscriptionName = getSubscriptionName(element);\n return Modal.create({\n type: Modal.types.SAVE_CANCEL,\n title: getString('confirmation', 'admin'),\n body: getString(messageCode, 'calendar', subscriptionName),\n buttons: {\n save: getString('yes')\n },\n }).then(modal => {\n modal.getRoot().on(ModalEvents.hidden, () => {\n element.focus();\n });\n modal.show();\n return modal;\n });\n};\n\n/**\n * Response handler for delete action.\n *\n * @param {HTMLElement} element\n * @param {Object} data\n * @return {Promise}\n */\nconst responseHandlerForDelete = async(element, data) => {\n const subscriptionName = getSubscriptionName(element);\n const message = data.status ? await getString('subscriptionremoved', 'calendar', subscriptionName) : data.warnings[0].message;\n const type = data.status ? 'info' : 'error';\n return addNotification({message, type});\n};\n\n/**\n * Register events for update/delete links.\n */\nconst registerEventListeners = () => {\n document.addEventListener('click', e => {\n const deleteAction = e.target.closest(CalendarSelectors.actions.deleteSubscription);\n if (deleteAction) {\n e.preventDefault();\n const modalPromise = createModal(deleteAction, 'confirmsubscriptiondelete');\n modalPromise.then(modal => {\n modal.getRoot().on(ModalEvents.save, () => {\n const subscriptionId = getSubscriptionId(deleteAction);\n CalendarRepository.deleteSubscription(subscriptionId).then(data => {\n const response = responseHandlerForDelete(deleteAction, data);\n return response.then(() => {\n const subscriptionRow = getSubscriptionRow(subscriptionId);\n return subscriptionRow.remove();\n });\n }).catch(displayException);\n });\n\n return modal;\n }).catch(displayException);\n }\n });\n\n document.addEventListener(eventTypes.elementUpdated, e => {\n const inplaceEditable = e.target;\n if (inplaceEditable.getAttribute('data-component') == 'core_calendar') {\n fetchNotifications();\n }\n });\n};\n\n/**\n * Initialises.\n */\nexport const init = () => {\n Prefetch.prefetchStrings('moodle', ['yes']);\n Prefetch.prefetchStrings('core_admin', ['confirmation']);\n Prefetch.prefetchStrings('core_calendar', ['confirmsubscriptiondelete', 'subscriptionremoved']);\n registerEventListeners();\n};\n"],"names":["getSubscriptionName","element","closest","dataset","subname","registerEventListeners","document","addEventListener","e","deleteAction","target","CalendarSelectors","actions","deleteSubscription","preventDefault","messageCode","subscriptionName","Modal","create","type","types","SAVE_CANCEL","title","body","buttons","save","then","modal","getRoot","on","ModalEvents","hidden","focus","show","createModal","subscriptionId","parseInt","subid","CalendarRepository","data","response","async","message","status","warnings","responseHandlerForDelete","subscriptionRow","querySelector","getSubscriptionRow","remove","catch","displayException","eventTypes","elementUpdated","getAttribute","prefetchStrings"],"mappings":";;;;;;;;sWAiDMA,oBAAsBC,SACjBA,QAAQC,QAAQ,MAAMC,QAAQC,QAuDnCC,uBAAyB,KAC3BC,SAASC,iBAAiB,SAASC,UACzBC,aAAeD,EAAEE,OAAOR,QAAQS,kBAAkBC,QAAQC,uBAC5DJ,aAAc,CACdD,EAAEM,iBAvCM,EAACb,QAASc,qBACpBC,iBAAmBhB,oBAAoBC,gBACtCgB,MAAMC,OAAO,CAChBC,KAAMF,MAAMG,MAAMC,YAClBC,OAAO,mBAAU,eAAgB,SACjCC,MAAM,mBAAUR,YAAa,WAAYC,kBACzCQ,QAAS,CACLC,MAAM,mBAAU,UAErBC,MAAKC,QACJA,MAAMC,UAAUC,GAAGC,YAAYC,QAAQ,KACnC9B,QAAQ+B,WAEZL,MAAMM,OACCN,UA0BkBO,CAAYzB,aAAc,6BAClCiB,MAAKC,QACdA,MAAMC,UAAUC,GAAGC,YAAYL,MAAM,WAC3BU,eAzEfC,SAyEkD3B,aAzEjCP,QAAQ,MAAMC,QAAQkC,OA0E9BC,mBAAmBzB,mBAAmBsB,gBAAgBT,MAAKa,aACjDC,SApBGC,OAAMxC,QAASsC,cACtCvB,iBAAmBhB,oBAAoBC,SACvCyC,QAAUH,KAAKI,aAAe,mBAAU,sBAAuB,WAAY3B,kBAAoBuB,KAAKK,SAAS,GAAGF,QAChHvB,KAAOoB,KAAKI,OAAS,OAAS,eAC7B,iCAAgB,CAACD,QAAAA,QAASvB,KAAAA,QAgBI0B,CAAyBpC,aAAc8B,aACjDC,SAASd,MAAK,WACXoB,gBA1DPX,CAAAA,gBAChB7B,SAASyC,uCAAgCZ,sBAyDAa,CAAmBb,uBACpCW,gBAAgBG,eAE5BC,MAAMC,mCAGNxB,SACRuB,MAAMC,oCAIjB7C,SAASC,iBAAiB6C,mBAAWC,gBAAgB7C,IAEK,iBAD9BA,EAAEE,OACN4C,aAAa,4EASrB,uBACPC,gBAAgB,SAAU,CAAC,0BAC3BA,gBAAgB,aAAc,CAAC,mCAC/BA,gBAAgB,gBAAiB,CAAC,4BAA6B,wBACxElD"}
\ No newline at end of file
+{"version":3,"file":"manage_subscriptions.min.js","sources":["../src/manage_subscriptions.js"],"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 * A module to handle Delete/Update operations of the manage subscription page.\n *\n * @module core_calendar/manage_subscriptions\n * @copyright 2021 Huong Nguyen \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @since 4.0\n */\n\nimport * as CalendarSelectors from 'core_calendar/selectors';\nimport * as CalendarRepository from 'core_calendar/repository';\nimport * as Modal from 'core/modal_factory';\nimport * as ModalEvents from 'core/modal_events';\nimport {displayException, addNotification, fetchNotifications} from 'core/notification';\nimport Prefetch from 'core/prefetch';\nimport {get_string as getString} from 'core/str';\nimport {eventTypes} from 'core/local/inplace_editable/events';\n\n/**\n * Get subscription id for given element.\n *\n * @param {HTMLElement} element update/delete link\n * @return {Number}\n */\nconst getSubscriptionId = element => {\n return parseInt(element.closest('tr').dataset.subid);\n};\n\n/**\n * Get subscription name for given element.\n *\n * @param {HTMLElement} element update/delete link\n * @return {String}\n */\nconst getSubscriptionName = element => {\n return element.closest('tr').dataset.subname;\n};\n\n/**\n * Get subscription table row for subscription id.\n *\n * @param {string} subscriptionId Subscription id\n * @return {Element}\n */\nconst getSubscriptionRow = subscriptionId => {\n return document.querySelector(`tr[data-subid=\"${subscriptionId}\"]`);\n};\n\n/**\n * Create modal.\n *\n * @param {HTMLElement} element\n * @param {string} messageCode Message code.\n * @return {promise} Promise for modal\n */\nconst createModal = (element, messageCode) => {\n const subscriptionName = getSubscriptionName(element);\n return Modal.create({\n type: Modal.types.SAVE_CANCEL,\n title: getString('confirmation', 'admin'),\n body: getString(messageCode, 'calendar', subscriptionName),\n buttons: {\n save: getString('yes')\n },\n }).then(modal => {\n modal.getRoot().on(ModalEvents.hidden, () => {\n element.focus();\n });\n modal.show();\n return modal;\n });\n};\n\n/**\n * Response handler for delete action.\n *\n * @param {HTMLElement} element\n * @param {Object} data\n * @return {Promise}\n */\nconst responseHandlerForDelete = async(element, data) => {\n const subscriptionName = getSubscriptionName(element);\n const message = data.status ? await getString('subscriptionremoved', 'calendar', subscriptionName) : data.warnings[0].message;\n const type = data.status ? 'info' : 'error';\n return addNotification({message, type});\n};\n\n/**\n * Register events for update/delete links.\n */\nconst registerEventListeners = () => {\n document.addEventListener('click', e => {\n const deleteAction = e.target.closest(CalendarSelectors.actions.deleteSubscription);\n if (deleteAction) {\n e.preventDefault();\n const modalPromise = createModal(deleteAction, 'confirmsubscriptiondelete');\n modalPromise.then(modal => {\n modal.getRoot().on(ModalEvents.save, () => {\n const subscriptionId = getSubscriptionId(deleteAction);\n CalendarRepository.deleteSubscription(subscriptionId).then(data => {\n const response = responseHandlerForDelete(deleteAction, data);\n return response.then(() => {\n const subscriptionRow = getSubscriptionRow(subscriptionId);\n return subscriptionRow.remove();\n });\n }).catch(displayException);\n });\n\n return modal;\n }).catch(displayException);\n }\n });\n\n document.addEventListener(eventTypes.elementUpdated, e => {\n const inplaceEditable = e.target;\n if (inplaceEditable.getAttribute('data-component') == 'core_calendar') {\n fetchNotifications();\n }\n });\n};\n\n/**\n * Initialises.\n */\nexport const init = () => {\n Prefetch.prefetchStrings('moodle', ['yes']);\n Prefetch.prefetchStrings('core_admin', ['confirmation']);\n Prefetch.prefetchStrings('core_calendar', ['confirmsubscriptiondelete', 'subscriptionremoved']);\n registerEventListeners();\n};\n"],"names":["obj","_getRequireWildcardCache","nodeInterop","WeakMap","cacheBabelInterop","cacheNodeInterop","_interopRequireWildcard","__esModule","default","cache","has","get","newObj","hasPropertyDescriptor","Object","defineProperty","getOwnPropertyDescriptor","key","prototype","hasOwnProperty","call","desc","set","CalendarSelectors","CalendarRepository","Modal","ModalEvents","_prefetch","getSubscriptionName","element","closest","dataset","subname","registerEventListeners","document","addEventListener","e","deleteAction","target","actions","deleteSubscription","preventDefault","createModal","messageCode","subscriptionName","create","type","types","SAVE_CANCEL","title","getString","body","get_string","buttons","save","then","modal","getRoot","on","hidden","focus","show","subscriptionId","parseInt","subid","data","response","async","message","status","warnings","addNotification","responseHandlerForDelete","subscriptionRow","querySelector","concat","getSubscriptionRow","remove","catch","displayException","eventTypes","elementUpdated","getAttribute","fetchNotifications","_exports","init","Prefetch","prefetchStrings"],"mappings":"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"}
\ No newline at end of file
diff --git a/calendar/amd/build/modal_delete.min.js.map b/calendar/amd/build/modal_delete.min.js.map
index 8aee0d38d16..1fb6e3df7c0 100644
--- a/calendar/amd/build/modal_delete.min.js.map
+++ b/calendar/amd/build/modal_delete.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"modal_delete.min.js","sources":["../src/modal_delete.js"],"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 * Contain the logic for the delete modal.\n *\n * @module core_calendar/modal_delete\n * @copyright 2017 Andrew Nicols \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine([\n 'jquery',\n 'core/notification',\n 'core/custom_interaction_events',\n 'core/modal',\n 'core/modal_events',\n 'core/modal_registry',\n 'core_calendar/events',\n],\nfunction(\n $,\n Notification,\n CustomEvents,\n Modal,\n ModalEvents,\n ModalRegistry,\n CalendarEvents\n) {\n\n var registered = false;\n var SELECTORS = {\n DELETE_ONE_BUTTON: '[data-action=\"deleteone\"]',\n DELETE_ALL_BUTTON: '[data-action=\"deleteall\"]',\n CANCEL_BUTTON: '[data-action=\"cancel\"]',\n };\n\n /**\n * Constructor for the Modal.\n *\n * @class\n * @param {object} root The root jQuery element for the modal\n */\n var ModalDelete = function(root) {\n Modal.call(this, root);\n\n this.setRemoveOnClose(true);\n };\n\n ModalDelete.TYPE = 'core_calendar-modal_delete';\n ModalDelete.prototype = Object.create(Modal.prototype);\n ModalDelete.prototype.constructor = ModalDelete;\n\n /**\n * Set up all of the event handling for the modal.\n *\n * @method registerEventListeners\n */\n ModalDelete.prototype.registerEventListeners = function() {\n // Apply parent event listeners.\n Modal.prototype.registerEventListeners.call(this);\n\n this.getModal().on(CustomEvents.events.activate, SELECTORS.DELETE_ONE_BUTTON, function(e, data) {\n var saveEvent = $.Event(ModalEvents.save);\n this.getRoot().trigger(saveEvent, this);\n\n if (!saveEvent.isDefaultPrevented()) {\n this.hide();\n data.originalEvent.preventDefault();\n }\n }.bind(this));\n\n this.getModal().on(CustomEvents.events.activate, SELECTORS.DELETE_ALL_BUTTON, function(e, data) {\n var saveEvent = $.Event(CalendarEvents.deleteAll);\n this.getRoot().trigger(saveEvent, this);\n\n if (!saveEvent.isDefaultPrevented()) {\n this.hide();\n data.originalEvent.preventDefault();\n }\n }.bind(this));\n\n this.getModal().on(CustomEvents.events.activate, SELECTORS.CANCEL_BUTTON, function(e, data) {\n var cancelEvent = $.Event(ModalEvents.cancel);\n this.getRoot().trigger(cancelEvent, this);\n\n if (!cancelEvent.isDefaultPrevented()) {\n this.hide();\n data.originalEvent.preventDefault();\n }\n }.bind(this));\n };\n\n // Automatically register with the modal registry the first time this module is imported so that you can create modals\n // of this type using the modal factory.\n if (!registered) {\n ModalRegistry.register(ModalDelete.TYPE, ModalDelete, 'calendar/event_delete_modal');\n registered = true;\n }\n\n return ModalDelete;\n});\n"],"names":["define","$","Notification","CustomEvents","Modal","ModalEvents","ModalRegistry","CalendarEvents","registered","SELECTORS","ModalDelete","root","call","this","setRemoveOnClose","TYPE","prototype","Object","create","constructor","registerEventListeners","getModal","on","events","activate","e","data","saveEvent","Event","save","getRoot","trigger","isDefaultPrevented","hide","originalEvent","preventDefault","bind","deleteAll","cancelEvent","cancel","register"],"mappings":";;;;;;;AAsBAA,oCAAO,CACH,SACA,oBACA,iCACA,aACA,oBACA,sBACA,yBAEJ,SACIC,EACAC,aACAC,aACAC,MACAC,YACAC,cACAC,oBAGIC,YAAa,EACbC,4BACmB,4BADnBA,4BAEmB,4BAFnBA,wBAGe,yBASfC,YAAc,SAASC,MACvBP,MAAMQ,KAAKC,KAAMF,WAEZG,kBAAiB,WAG1BJ,YAAYK,KAAO,8BACnBL,YAAYM,UAAYC,OAAOC,OAAOd,MAAMY,YACtBG,YAAcT,YAOpCA,YAAYM,UAAUI,uBAAyB,WAE3ChB,MAAMY,UAAUI,uBAAuBR,KAAKC,WAEvCQ,WAAWC,GAAGnB,aAAaoB,OAAOC,SAAUf,4BAA6B,SAASgB,EAAGC,UAClFC,UAAY1B,EAAE2B,MAAMvB,YAAYwB,WAC/BC,UAAUC,QAAQJ,UAAWd,MAE7Bc,UAAUK,4BACNC,OACLP,KAAKQ,cAAcC,mBAEzBC,KAAKvB,YAEFQ,WAAWC,GAAGnB,aAAaoB,OAAOC,SAAUf,4BAA6B,SAASgB,EAAGC,UAClFC,UAAY1B,EAAE2B,MAAMrB,eAAe8B,gBAClCP,UAAUC,QAAQJ,UAAWd,MAE7Bc,UAAUK,4BACNC,OACLP,KAAKQ,cAAcC,mBAEzBC,KAAKvB,YAEFQ,WAAWC,GAAGnB,aAAaoB,OAAOC,SAAUf,wBAAyB,SAASgB,EAAGC,UAC9EY,YAAcrC,EAAE2B,MAAMvB,YAAYkC,aACjCT,UAAUC,QAAQO,YAAazB,MAE/ByB,YAAYN,4BACRC,OACLP,KAAKQ,cAAcC,mBAEzBC,KAAKvB,QAKNL,aACDF,cAAckC,SAAS9B,YAAYK,KAAML,YAAa,+BACtDF,YAAa,GAGVE"}
\ No newline at end of file
+{"version":3,"file":"modal_delete.min.js","sources":["../src/modal_delete.js"],"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 * Contain the logic for the delete modal.\n *\n * @module core_calendar/modal_delete\n * @copyright 2017 Andrew Nicols \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine([\n 'jquery',\n 'core/notification',\n 'core/custom_interaction_events',\n 'core/modal',\n 'core/modal_events',\n 'core/modal_registry',\n 'core_calendar/events',\n],\nfunction(\n $,\n Notification,\n CustomEvents,\n Modal,\n ModalEvents,\n ModalRegistry,\n CalendarEvents\n) {\n\n var registered = false;\n var SELECTORS = {\n DELETE_ONE_BUTTON: '[data-action=\"deleteone\"]',\n DELETE_ALL_BUTTON: '[data-action=\"deleteall\"]',\n CANCEL_BUTTON: '[data-action=\"cancel\"]',\n };\n\n /**\n * Constructor for the Modal.\n *\n * @class\n * @param {object} root The root jQuery element for the modal\n */\n var ModalDelete = function(root) {\n Modal.call(this, root);\n\n this.setRemoveOnClose(true);\n };\n\n ModalDelete.TYPE = 'core_calendar-modal_delete';\n ModalDelete.prototype = Object.create(Modal.prototype);\n ModalDelete.prototype.constructor = ModalDelete;\n\n /**\n * Set up all of the event handling for the modal.\n *\n * @method registerEventListeners\n */\n ModalDelete.prototype.registerEventListeners = function() {\n // Apply parent event listeners.\n Modal.prototype.registerEventListeners.call(this);\n\n this.getModal().on(CustomEvents.events.activate, SELECTORS.DELETE_ONE_BUTTON, function(e, data) {\n var saveEvent = $.Event(ModalEvents.save);\n this.getRoot().trigger(saveEvent, this);\n\n if (!saveEvent.isDefaultPrevented()) {\n this.hide();\n data.originalEvent.preventDefault();\n }\n }.bind(this));\n\n this.getModal().on(CustomEvents.events.activate, SELECTORS.DELETE_ALL_BUTTON, function(e, data) {\n var saveEvent = $.Event(CalendarEvents.deleteAll);\n this.getRoot().trigger(saveEvent, this);\n\n if (!saveEvent.isDefaultPrevented()) {\n this.hide();\n data.originalEvent.preventDefault();\n }\n }.bind(this));\n\n this.getModal().on(CustomEvents.events.activate, SELECTORS.CANCEL_BUTTON, function(e, data) {\n var cancelEvent = $.Event(ModalEvents.cancel);\n this.getRoot().trigger(cancelEvent, this);\n\n if (!cancelEvent.isDefaultPrevented()) {\n this.hide();\n data.originalEvent.preventDefault();\n }\n }.bind(this));\n };\n\n // Automatically register with the modal registry the first time this module is imported so that you can create modals\n // of this type using the modal factory.\n if (!registered) {\n ModalRegistry.register(ModalDelete.TYPE, ModalDelete, 'calendar/event_delete_modal');\n registered = true;\n }\n\n return ModalDelete;\n});\n"],"names":["define","$","Notification","CustomEvents","Modal","ModalEvents","ModalRegistry","CalendarEvents","registered","SELECTORS","ModalDelete","root","call","this","setRemoveOnClose","TYPE","prototype","Object","create","constructor","registerEventListeners","getModal","on","events","activate","e","data","saveEvent","Event","save","getRoot","trigger","isDefaultPrevented","hide","originalEvent","preventDefault","bind","deleteAll","cancelEvent","cancel","register"],"mappings":";;;;;;;AAsBAA,OAAO,6BAAA,CACH,SACA,oBACA,iCACA,aACA,oBACA,sBACA,yBAEJ,SACIC,EACAC,aACAC,aACAC,MACAC,YACAC,cACAC,gBAGA,IAAIC,YAAa,EACbC,4BACmB,4BADnBA,4BAEmB,4BAFnBA,wBAGe,yBASfC,YAAc,SAASC,MACvBP,MAAMQ,KAAKC,KAAMF,MAEjBE,KAAKC,kBAAiB,IAsD1B,OAnDAJ,YAAYK,KAAO,8BACnBL,YAAYM,UAAYC,OAAOC,OAAOd,MAAMY,YACtBG,YAAcT,YAOpCA,YAAYM,UAAUI,uBAAyB,WAE3ChB,MAAMY,UAAUI,uBAAuBR,KAAKC,MAE5CA,KAAKQ,WAAWC,GAAGnB,aAAaoB,OAAOC,SAAUf,4BAA6B,SAASgB,EAAGC,MACtF,IAAIC,UAAY1B,EAAE2B,MAAMvB,YAAYwB,MACpChB,KAAKiB,UAAUC,QAAQJ,UAAWd,MAE7Bc,UAAUK,uBACXnB,KAAKoB,OACLP,KAAKQ,cAAcC,iBAE3B,EAAEC,KAAKvB,OAEPA,KAAKQ,WAAWC,GAAGnB,aAAaoB,OAAOC,SAAUf,4BAA6B,SAASgB,EAAGC,MACtF,IAAIC,UAAY1B,EAAE2B,MAAMrB,eAAe8B,WACvCxB,KAAKiB,UAAUC,QAAQJ,UAAWd,MAE7Bc,UAAUK,uBACXnB,KAAKoB,OACLP,KAAKQ,cAAcC,iBAE3B,EAAEC,KAAKvB,OAEPA,KAAKQ,WAAWC,GAAGnB,aAAaoB,OAAOC,SAAUf,wBAAyB,SAASgB,EAAGC,MAClF,IAAIY,YAAcrC,EAAE2B,MAAMvB,YAAYkC,QACtC1B,KAAKiB,UAAUC,QAAQO,YAAazB,MAE/ByB,YAAYN,uBACbnB,KAAKoB,OACLP,KAAKQ,cAAcC,iBAE3B,EAAEC,KAAKvB,QAKNL,aACDF,cAAckC,SAAS9B,YAAYK,KAAML,YAAa,+BACtDF,YAAa,GAGVE,WACX"}
\ No newline at end of file
diff --git a/calendar/amd/build/modal_event_form.min.js.map b/calendar/amd/build/modal_event_form.min.js.map
index e0b9e917c2e..974453c9325 100644
--- a/calendar/amd/build/modal_event_form.min.js.map
+++ b/calendar/amd/build/modal_event_form.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"modal_event_form.min.js","sources":["../src/modal_event_form.js"],"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 * Contain the logic for the quick add or update event modal.\n *\n * @module core_calendar/modal_quick_add_event\n * @copyright 2017 Ryan Wyllie \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine([\n 'jquery',\n 'core_form/events',\n 'core/str',\n 'core/notification',\n 'core/templates',\n 'core/custom_interaction_events',\n 'core/modal',\n 'core/modal_registry',\n 'core/fragment',\n 'core_calendar/events',\n 'core_calendar/repository'\n],\nfunction(\n $,\n FormEvents,\n Str,\n Notification,\n Templates,\n CustomEvents,\n Modal,\n ModalRegistry,\n Fragment,\n CalendarEvents,\n Repository\n) {\n var registered = false;\n var SELECTORS = {\n SAVE_BUTTON: '[data-action=\"save\"]',\n LOADING_ICON_CONTAINER: '[data-region=\"loading-icon-container\"]',\n };\n\n /**\n * Constructor for the Modal.\n *\n * @param {object} root The root jQuery element for the modal\n */\n var ModalEventForm = function(root) {\n Modal.call(this, root);\n this.eventId = null;\n this.startTime = null;\n this.courseId = null;\n this.categoryId = null;\n this.contextId = null;\n this.reloadingBody = false;\n this.reloadingTitle = false;\n this.saveButton = this.getFooter().find(SELECTORS.SAVE_BUTTON);\n };\n\n ModalEventForm.TYPE = 'core_calendar-modal_event_form';\n ModalEventForm.prototype = Object.create(Modal.prototype);\n ModalEventForm.prototype.constructor = ModalEventForm;\n\n /**\n * Set the context id to the given value.\n *\n * @method setContextId\n * @param {Number} id The event id\n */\n ModalEventForm.prototype.setContextId = function(id) {\n this.contextId = id;\n };\n\n /**\n * Retrieve the current context id, if any.\n *\n * @method getContextId\n * @return {Number|null} The event id\n */\n ModalEventForm.prototype.getContextId = function() {\n return this.contextId;\n };\n\n /**\n * Set the course id to the given value.\n *\n * @method setCourseId\n * @param {int} id The event id\n */\n ModalEventForm.prototype.setCourseId = function(id) {\n this.courseId = id;\n };\n\n /**\n * Retrieve the current course id, if any.\n *\n * @method getCourseId\n * @return {int|null} The event id\n */\n ModalEventForm.prototype.getCourseId = function() {\n return this.courseId;\n };\n\n /**\n * Set the category id to the given value.\n *\n * @method setCategoryId\n * @param {int} id The event id\n */\n ModalEventForm.prototype.setCategoryId = function(id) {\n this.categoryId = id;\n };\n\n /**\n * Retrieve the current category id, if any.\n *\n * @method getCategoryId\n * @return {int|null} The event id\n */\n ModalEventForm.prototype.getCategoryId = function() {\n return this.categoryId;\n };\n\n /**\n * Check if the modal has an course id.\n *\n * @method hasCourseId\n * @return {bool}\n */\n ModalEventForm.prototype.hasCourseId = function() {\n return this.courseId !== null;\n };\n\n /**\n * Check if the modal has an category id.\n *\n * @method hasCategoryId\n * @return {bool}\n */\n ModalEventForm.prototype.hasCategoryId = function() {\n return this.categoryId !== null;\n };\n\n /**\n * Set the event id to the given value.\n *\n * @method setEventId\n * @param {int} id The event id\n */\n ModalEventForm.prototype.setEventId = function(id) {\n this.eventId = id;\n };\n\n /**\n * Retrieve the current event id, if any.\n *\n * @method getEventId\n * @return {int|null} The event id\n */\n ModalEventForm.prototype.getEventId = function() {\n return this.eventId;\n };\n\n /**\n * Check if the modal has an event id.\n *\n * @method hasEventId\n * @return {bool}\n */\n ModalEventForm.prototype.hasEventId = function() {\n return this.eventId !== null;\n };\n\n /**\n * Set the start time to the given value.\n *\n * @method setStartTime\n * @param {int} time The start time\n */\n ModalEventForm.prototype.setStartTime = function(time) {\n this.startTime = time;\n };\n\n /**\n * Retrieve the current start time, if any.\n *\n * @method getStartTime\n * @return {int|null} The start time\n */\n ModalEventForm.prototype.getStartTime = function() {\n return this.startTime;\n };\n\n /**\n * Check if the modal has start time.\n *\n * @method hasStartTime\n * @return {bool}\n */\n ModalEventForm.prototype.hasStartTime = function() {\n return this.startTime !== null;\n };\n\n /**\n * Get the form element from the modal.\n *\n * @method getForm\n * @return {object}\n */\n ModalEventForm.prototype.getForm = function() {\n return this.getBody().find('form');\n };\n\n /**\n * Disable the buttons in the footer.\n *\n * @method disableButtons\n */\n ModalEventForm.prototype.disableButtons = function() {\n this.saveButton.prop('disabled', true);\n };\n\n /**\n * Enable the buttons in the footer.\n *\n * @method enableButtons\n */\n ModalEventForm.prototype.enableButtons = function() {\n this.saveButton.prop('disabled', false);\n };\n\n /**\n * Reload the title for the modal to the appropriate value\n * depending on whether we are creating a new event or\n * editing an existing event.\n *\n * @method reloadTitleContent\n * @return {object} A promise resolved with the new title text\n */\n ModalEventForm.prototype.reloadTitleContent = function() {\n if (this.reloadingTitle) {\n return this.titlePromise;\n }\n\n this.reloadingTitle = true;\n\n if (this.hasEventId()) {\n this.titlePromise = Str.get_string('editevent', 'calendar');\n } else {\n this.titlePromise = Str.get_string('newevent', 'calendar');\n }\n\n this.titlePromise.then(function(string) {\n this.setTitle(string);\n return string;\n }.bind(this))\n .always(function() {\n this.reloadingTitle = false;\n return;\n }.bind(this))\n .fail(Notification.exception);\n\n return this.titlePromise;\n };\n\n /**\n * Send a request to the server to get the event_form in a fragment\n * and render the result in the body of the modal.\n *\n * If serialised form data is provided then it will be sent in the\n * request to the server to have the form rendered with the data. This\n * is used when the form had a server side error and we need the server\n * to re-render it for us to display the error to the user.\n *\n * @method reloadBodyContent\n * @param {string} formData The serialised form data\n * @return {object} A promise resolved with the fragment html and js from\n */\n ModalEventForm.prototype.reloadBodyContent = function(formData) {\n if (this.reloadingBody) {\n return this.bodyPromise;\n }\n\n this.reloadingBody = true;\n this.disableButtons();\n\n var args = {};\n\n if (this.hasEventId()) {\n args.eventid = this.getEventId();\n }\n\n if (this.hasStartTime()) {\n args.starttime = this.getStartTime();\n }\n\n if (this.hasCourseId()) {\n args.courseid = this.getCourseId();\n }\n\n if (this.hasCategoryId()) {\n args.categoryid = this.getCategoryId();\n }\n\n if (typeof formData !== 'undefined') {\n args.formdata = formData;\n }\n\n this.bodyPromise = Fragment.loadFragment('calendar', 'event_form', this.getContextId(), args);\n\n this.setBody(this.bodyPromise);\n\n this.bodyPromise.then(function() {\n this.enableButtons();\n return;\n }.bind(this))\n .fail(Notification.exception)\n .always(function() {\n this.reloadingBody = false;\n return;\n }.bind(this))\n .fail(Notification.exception);\n\n return this.bodyPromise;\n };\n\n /**\n * Reload both the title and body content.\n *\n * @method reloadAllContent\n * @return {object} promise\n */\n ModalEventForm.prototype.reloadAllContent = function() {\n return $.when(this.reloadTitleContent(), this.reloadBodyContent());\n };\n\n /**\n * Kick off a reload the modal content before showing it. This\n * is to allow us to re-use the same modal for creating and\n * editing different events within the page.\n *\n * We do the reload when showing the modal rather than hiding it\n * to save a request to the server if the user closes the modal\n * and never re-opens it.\n *\n * @method show\n */\n ModalEventForm.prototype.show = function() {\n this.reloadAllContent();\n Modal.prototype.show.call(this);\n };\n\n /**\n * Clear the event id from the modal when it's closed so\n * that it is loaded fresh next time it's displayed.\n *\n * The event id will be set by the calling code if it wants\n * to edit a specific event.\n *\n * @method hide\n */\n ModalEventForm.prototype.hide = function() {\n Modal.prototype.hide.call(this);\n this.setEventId(null);\n this.setStartTime(null);\n this.setCourseId(null);\n this.setCategoryId(null);\n };\n\n /**\n * Get the serialised form data.\n *\n * @method getFormData\n * @return {string} serialised form data\n */\n ModalEventForm.prototype.getFormData = function() {\n return this.getForm().serialize();\n };\n\n /**\n * Send the form data to the server to create or update\n * an event.\n *\n * If there is a server side validation error then we re-request the\n * rendered form (with the data) from the server in order to get the\n * server side errors to display.\n *\n * On success the modal is hidden and the page is reloaded so that the\n * new event will display.\n *\n * @method save\n * @return {object} A promise\n */\n ModalEventForm.prototype.save = function() {\n var invalid,\n loadingContainer = this.saveButton.find(SELECTORS.LOADING_ICON_CONTAINER);\n\n // Now the change events have run, see if there are any \"invalid\" form fields.\n invalid = this.getForm().find('[aria-invalid=\"true\"]');\n\n // If we found invalid fields, focus on the first one and do not submit via ajax.\n if (invalid.length) {\n invalid.first().focus();\n return Promise.resolve();\n }\n\n loadingContainer.removeClass('hidden');\n this.disableButtons();\n\n var formData = this.getFormData();\n // Send the form data to the server for processing.\n return Repository.submitCreateUpdateForm(formData)\n .then(function(response) {\n if (response.validationerror) {\n // If there was a server side validation error then\n // we need to re-request the rendered form from the server\n // in order to display the error for the user.\n this.reloadBodyContent(formData);\n return;\n } else {\n // Check whether this was a new event or not.\n // The hide function unsets the form data so grab this before the hide.\n var isExisting = this.hasEventId();\n\n // No problemo! Our work here is done.\n this.hide();\n\n // Trigger the appropriate calendar event so that the view can be updated.\n if (isExisting) {\n $('body').trigger(CalendarEvents.updated, [response.event]);\n } else {\n $('body').trigger(CalendarEvents.created, [response.event]);\n }\n }\n\n return;\n }.bind(this))\n .always(function() {\n // Regardless of success or error we should always stop\n // the loading icon and re-enable the buttons.\n loadingContainer.addClass('hidden');\n this.enableButtons();\n\n return;\n }.bind(this))\n .fail(Notification.exception);\n };\n\n /**\n * Set up all of the event handling for the modal.\n *\n * @method registerEventListeners\n * @fires event:uploadStarted\n * @fires event:formSubmittedByJavascript\n */\n ModalEventForm.prototype.registerEventListeners = function() {\n // Apply parent event listeners.\n Modal.prototype.registerEventListeners.call(this);\n\n // When the user clicks the save button we trigger the form submission. We need to\n // trigger an actual submission because there is some JS code in the form that is\n // listening for this event and doing some stuff (e.g. saving draft areas etc).\n this.getModal().on(CustomEvents.events.activate, SELECTORS.SAVE_BUTTON, function(e, data) {\n this.getForm().submit();\n data.originalEvent.preventDefault();\n e.stopPropagation();\n }.bind(this));\n\n // Catch the submit event before it is actually processed by the browser and\n // prevent the submission. We'll take it from here.\n this.getModal().on('submit', function(e) {\n FormEvents.notifyFormSubmittedByJavascript(this.getForm()[0]);\n\n this.save();\n\n // Stop the form from actually submitting and prevent it's\n // propagation because we have already handled the event.\n e.preventDefault();\n e.stopPropagation();\n }.bind(this));\n };\n\n // Automatically register with the modal registry the first time this module is imported so that you can create modals\n // of this type using the modal factory.\n if (!registered) {\n ModalRegistry.register(ModalEventForm.TYPE, ModalEventForm, 'calendar/modal_event_form');\n registered = true;\n }\n\n return ModalEventForm;\n});\n"],"names":["define","$","FormEvents","Str","Notification","Templates","CustomEvents","Modal","ModalRegistry","Fragment","CalendarEvents","Repository","registered","SELECTORS","ModalEventForm","root","call","this","eventId","startTime","courseId","categoryId","contextId","reloadingBody","reloadingTitle","saveButton","getFooter","find","TYPE","prototype","Object","create","constructor","setContextId","id","getContextId","setCourseId","getCourseId","setCategoryId","getCategoryId","hasCourseId","hasCategoryId","setEventId","getEventId","hasEventId","setStartTime","time","getStartTime","hasStartTime","getForm","getBody","disableButtons","prop","enableButtons","reloadTitleContent","titlePromise","get_string","then","string","setTitle","bind","always","fail","exception","reloadBodyContent","formData","bodyPromise","args","eventid","starttime","courseid","categoryid","formdata","loadFragment","setBody","reloadAllContent","when","show","hide","getFormData","serialize","save","invalid","loadingContainer","length","first","focus","Promise","resolve","removeClass","submitCreateUpdateForm","response","validationerror","isExisting","trigger","updated","event","created","addClass","registerEventListeners","getModal","on","events","activate","e","data","submit","originalEvent","preventDefault","stopPropagation","notifyFormSubmittedByJavascript","register"],"mappings":"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"}
\ No newline at end of file
+{"version":3,"file":"modal_event_form.min.js","sources":["../src/modal_event_form.js"],"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 * Contain the logic for the quick add or update event modal.\n *\n * @module core_calendar/modal_quick_add_event\n * @copyright 2017 Ryan Wyllie \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine([\n 'jquery',\n 'core_form/events',\n 'core/str',\n 'core/notification',\n 'core/templates',\n 'core/custom_interaction_events',\n 'core/modal',\n 'core/modal_registry',\n 'core/fragment',\n 'core_calendar/events',\n 'core_calendar/repository'\n],\nfunction(\n $,\n FormEvents,\n Str,\n Notification,\n Templates,\n CustomEvents,\n Modal,\n ModalRegistry,\n Fragment,\n CalendarEvents,\n Repository\n) {\n var registered = false;\n var SELECTORS = {\n SAVE_BUTTON: '[data-action=\"save\"]',\n LOADING_ICON_CONTAINER: '[data-region=\"loading-icon-container\"]',\n };\n\n /**\n * Constructor for the Modal.\n *\n * @param {object} root The root jQuery element for the modal\n */\n var ModalEventForm = function(root) {\n Modal.call(this, root);\n this.eventId = null;\n this.startTime = null;\n this.courseId = null;\n this.categoryId = null;\n this.contextId = null;\n this.reloadingBody = false;\n this.reloadingTitle = false;\n this.saveButton = this.getFooter().find(SELECTORS.SAVE_BUTTON);\n };\n\n ModalEventForm.TYPE = 'core_calendar-modal_event_form';\n ModalEventForm.prototype = Object.create(Modal.prototype);\n ModalEventForm.prototype.constructor = ModalEventForm;\n\n /**\n * Set the context id to the given value.\n *\n * @method setContextId\n * @param {Number} id The event id\n */\n ModalEventForm.prototype.setContextId = function(id) {\n this.contextId = id;\n };\n\n /**\n * Retrieve the current context id, if any.\n *\n * @method getContextId\n * @return {Number|null} The event id\n */\n ModalEventForm.prototype.getContextId = function() {\n return this.contextId;\n };\n\n /**\n * Set the course id to the given value.\n *\n * @method setCourseId\n * @param {int} id The event id\n */\n ModalEventForm.prototype.setCourseId = function(id) {\n this.courseId = id;\n };\n\n /**\n * Retrieve the current course id, if any.\n *\n * @method getCourseId\n * @return {int|null} The event id\n */\n ModalEventForm.prototype.getCourseId = function() {\n return this.courseId;\n };\n\n /**\n * Set the category id to the given value.\n *\n * @method setCategoryId\n * @param {int} id The event id\n */\n ModalEventForm.prototype.setCategoryId = function(id) {\n this.categoryId = id;\n };\n\n /**\n * Retrieve the current category id, if any.\n *\n * @method getCategoryId\n * @return {int|null} The event id\n */\n ModalEventForm.prototype.getCategoryId = function() {\n return this.categoryId;\n };\n\n /**\n * Check if the modal has an course id.\n *\n * @method hasCourseId\n * @return {bool}\n */\n ModalEventForm.prototype.hasCourseId = function() {\n return this.courseId !== null;\n };\n\n /**\n * Check if the modal has an category id.\n *\n * @method hasCategoryId\n * @return {bool}\n */\n ModalEventForm.prototype.hasCategoryId = function() {\n return this.categoryId !== null;\n };\n\n /**\n * Set the event id to the given value.\n *\n * @method setEventId\n * @param {int} id The event id\n */\n ModalEventForm.prototype.setEventId = function(id) {\n this.eventId = id;\n };\n\n /**\n * Retrieve the current event id, if any.\n *\n * @method getEventId\n * @return {int|null} The event id\n */\n ModalEventForm.prototype.getEventId = function() {\n return this.eventId;\n };\n\n /**\n * Check if the modal has an event id.\n *\n * @method hasEventId\n * @return {bool}\n */\n ModalEventForm.prototype.hasEventId = function() {\n return this.eventId !== null;\n };\n\n /**\n * Set the start time to the given value.\n *\n * @method setStartTime\n * @param {int} time The start time\n */\n ModalEventForm.prototype.setStartTime = function(time) {\n this.startTime = time;\n };\n\n /**\n * Retrieve the current start time, if any.\n *\n * @method getStartTime\n * @return {int|null} The start time\n */\n ModalEventForm.prototype.getStartTime = function() {\n return this.startTime;\n };\n\n /**\n * Check if the modal has start time.\n *\n * @method hasStartTime\n * @return {bool}\n */\n ModalEventForm.prototype.hasStartTime = function() {\n return this.startTime !== null;\n };\n\n /**\n * Get the form element from the modal.\n *\n * @method getForm\n * @return {object}\n */\n ModalEventForm.prototype.getForm = function() {\n return this.getBody().find('form');\n };\n\n /**\n * Disable the buttons in the footer.\n *\n * @method disableButtons\n */\n ModalEventForm.prototype.disableButtons = function() {\n this.saveButton.prop('disabled', true);\n };\n\n /**\n * Enable the buttons in the footer.\n *\n * @method enableButtons\n */\n ModalEventForm.prototype.enableButtons = function() {\n this.saveButton.prop('disabled', false);\n };\n\n /**\n * Reload the title for the modal to the appropriate value\n * depending on whether we are creating a new event or\n * editing an existing event.\n *\n * @method reloadTitleContent\n * @return {object} A promise resolved with the new title text\n */\n ModalEventForm.prototype.reloadTitleContent = function() {\n if (this.reloadingTitle) {\n return this.titlePromise;\n }\n\n this.reloadingTitle = true;\n\n if (this.hasEventId()) {\n this.titlePromise = Str.get_string('editevent', 'calendar');\n } else {\n this.titlePromise = Str.get_string('newevent', 'calendar');\n }\n\n this.titlePromise.then(function(string) {\n this.setTitle(string);\n return string;\n }.bind(this))\n .always(function() {\n this.reloadingTitle = false;\n return;\n }.bind(this))\n .fail(Notification.exception);\n\n return this.titlePromise;\n };\n\n /**\n * Send a request to the server to get the event_form in a fragment\n * and render the result in the body of the modal.\n *\n * If serialised form data is provided then it will be sent in the\n * request to the server to have the form rendered with the data. This\n * is used when the form had a server side error and we need the server\n * to re-render it for us to display the error to the user.\n *\n * @method reloadBodyContent\n * @param {string} formData The serialised form data\n * @return {object} A promise resolved with the fragment html and js from\n */\n ModalEventForm.prototype.reloadBodyContent = function(formData) {\n if (this.reloadingBody) {\n return this.bodyPromise;\n }\n\n this.reloadingBody = true;\n this.disableButtons();\n\n var args = {};\n\n if (this.hasEventId()) {\n args.eventid = this.getEventId();\n }\n\n if (this.hasStartTime()) {\n args.starttime = this.getStartTime();\n }\n\n if (this.hasCourseId()) {\n args.courseid = this.getCourseId();\n }\n\n if (this.hasCategoryId()) {\n args.categoryid = this.getCategoryId();\n }\n\n if (typeof formData !== 'undefined') {\n args.formdata = formData;\n }\n\n this.bodyPromise = Fragment.loadFragment('calendar', 'event_form', this.getContextId(), args);\n\n this.setBody(this.bodyPromise);\n\n this.bodyPromise.then(function() {\n this.enableButtons();\n return;\n }.bind(this))\n .fail(Notification.exception)\n .always(function() {\n this.reloadingBody = false;\n return;\n }.bind(this))\n .fail(Notification.exception);\n\n return this.bodyPromise;\n };\n\n /**\n * Reload both the title and body content.\n *\n * @method reloadAllContent\n * @return {object} promise\n */\n ModalEventForm.prototype.reloadAllContent = function() {\n return $.when(this.reloadTitleContent(), this.reloadBodyContent());\n };\n\n /**\n * Kick off a reload the modal content before showing it. This\n * is to allow us to re-use the same modal for creating and\n * editing different events within the page.\n *\n * We do the reload when showing the modal rather than hiding it\n * to save a request to the server if the user closes the modal\n * and never re-opens it.\n *\n * @method show\n */\n ModalEventForm.prototype.show = function() {\n this.reloadAllContent();\n Modal.prototype.show.call(this);\n };\n\n /**\n * Clear the event id from the modal when it's closed so\n * that it is loaded fresh next time it's displayed.\n *\n * The event id will be set by the calling code if it wants\n * to edit a specific event.\n *\n * @method hide\n */\n ModalEventForm.prototype.hide = function() {\n Modal.prototype.hide.call(this);\n this.setEventId(null);\n this.setStartTime(null);\n this.setCourseId(null);\n this.setCategoryId(null);\n };\n\n /**\n * Get the serialised form data.\n *\n * @method getFormData\n * @return {string} serialised form data\n */\n ModalEventForm.prototype.getFormData = function() {\n return this.getForm().serialize();\n };\n\n /**\n * Send the form data to the server to create or update\n * an event.\n *\n * If there is a server side validation error then we re-request the\n * rendered form (with the data) from the server in order to get the\n * server side errors to display.\n *\n * On success the modal is hidden and the page is reloaded so that the\n * new event will display.\n *\n * @method save\n * @return {object} A promise\n */\n ModalEventForm.prototype.save = function() {\n var invalid,\n loadingContainer = this.saveButton.find(SELECTORS.LOADING_ICON_CONTAINER);\n\n // Now the change events have run, see if there are any \"invalid\" form fields.\n invalid = this.getForm().find('[aria-invalid=\"true\"]');\n\n // If we found invalid fields, focus on the first one and do not submit via ajax.\n if (invalid.length) {\n invalid.first().focus();\n return Promise.resolve();\n }\n\n loadingContainer.removeClass('hidden');\n this.disableButtons();\n\n var formData = this.getFormData();\n // Send the form data to the server for processing.\n return Repository.submitCreateUpdateForm(formData)\n .then(function(response) {\n if (response.validationerror) {\n // If there was a server side validation error then\n // we need to re-request the rendered form from the server\n // in order to display the error for the user.\n this.reloadBodyContent(formData);\n return;\n } else {\n // Check whether this was a new event or not.\n // The hide function unsets the form data so grab this before the hide.\n var isExisting = this.hasEventId();\n\n // No problemo! Our work here is done.\n this.hide();\n\n // Trigger the appropriate calendar event so that the view can be updated.\n if (isExisting) {\n $('body').trigger(CalendarEvents.updated, [response.event]);\n } else {\n $('body').trigger(CalendarEvents.created, [response.event]);\n }\n }\n\n return;\n }.bind(this))\n .always(function() {\n // Regardless of success or error we should always stop\n // the loading icon and re-enable the buttons.\n loadingContainer.addClass('hidden');\n this.enableButtons();\n\n return;\n }.bind(this))\n .fail(Notification.exception);\n };\n\n /**\n * Set up all of the event handling for the modal.\n *\n * @method registerEventListeners\n * @fires event:uploadStarted\n * @fires event:formSubmittedByJavascript\n */\n ModalEventForm.prototype.registerEventListeners = function() {\n // Apply parent event listeners.\n Modal.prototype.registerEventListeners.call(this);\n\n // When the user clicks the save button we trigger the form submission. We need to\n // trigger an actual submission because there is some JS code in the form that is\n // listening for this event and doing some stuff (e.g. saving draft areas etc).\n this.getModal().on(CustomEvents.events.activate, SELECTORS.SAVE_BUTTON, function(e, data) {\n this.getForm().submit();\n data.originalEvent.preventDefault();\n e.stopPropagation();\n }.bind(this));\n\n // Catch the submit event before it is actually processed by the browser and\n // prevent the submission. We'll take it from here.\n this.getModal().on('submit', function(e) {\n FormEvents.notifyFormSubmittedByJavascript(this.getForm()[0]);\n\n this.save();\n\n // Stop the form from actually submitting and prevent it's\n // propagation because we have already handled the event.\n e.preventDefault();\n e.stopPropagation();\n }.bind(this));\n };\n\n // Automatically register with the modal registry the first time this module is imported so that you can create modals\n // of this type using the modal factory.\n if (!registered) {\n ModalRegistry.register(ModalEventForm.TYPE, ModalEventForm, 'calendar/modal_event_form');\n registered = true;\n }\n\n return ModalEventForm;\n});\n"],"names":["define","$","FormEvents","Str","Notification","Templates","CustomEvents","Modal","ModalRegistry","Fragment","CalendarEvents","Repository","registered","SELECTORS","ModalEventForm","root","call","this","eventId","startTime","courseId","categoryId","contextId","reloadingBody","reloadingTitle","saveButton","getFooter","find","TYPE","prototype","Object","create","constructor","setContextId","id","getContextId","setCourseId","getCourseId","setCategoryId","getCategoryId","hasCourseId","hasCategoryId","setEventId","getEventId","hasEventId","setStartTime","time","getStartTime","hasStartTime","getForm","getBody","disableButtons","prop","enableButtons","reloadTitleContent","titlePromise","get_string","then","string","setTitle","bind","always","fail","exception","reloadBodyContent","formData","bodyPromise","args","eventid","starttime","courseid","categoryid","formdata","loadFragment","setBody","reloadAllContent","when","show","hide","getFormData","serialize","save","invalid","loadingContainer","length","first","focus","Promise","resolve","removeClass","submitCreateUpdateForm","response","validationerror","isExisting","trigger","updated","event","created","addClass","registerEventListeners","getModal","on","events","activate","e","data","submit","originalEvent","preventDefault","stopPropagation","notifyFormSubmittedByJavascript","register"],"mappings":"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"}
\ No newline at end of file
diff --git a/calendar/amd/build/month_navigation_drag_drop.min.js.map b/calendar/amd/build/month_navigation_drag_drop.min.js.map
index d10b3363d11..975fe768dfe 100644
--- a/calendar/amd/build/month_navigation_drag_drop.min.js.map
+++ b/calendar/amd/build/month_navigation_drag_drop.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"month_navigation_drag_drop.min.js","sources":["../src/month_navigation_drag_drop.js"],"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 * A javascript module to handle calendar drag and drop in the calendar\n * month view navigation.\n *\n * This code is run each time the calendar month view is re-rendered. We\n * only register the event handlers once per page load so that the in place\n * DOM updates that happen on month change don't continue to register handlers.\n *\n * @module core_calendar/month_navigation_drag_drop\n * @copyright 2017 Ryan Wyllie \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine([\n 'jquery',\n 'core_calendar/drag_drop_data_store',\n ],\n function(\n $,\n DataStore\n ) {\n\n var SELECTORS = {\n DRAGGABLE: '[draggable=\"true\"][data-region=\"event-item\"]',\n DROP_ZONE: '[data-drop-zone=\"nav-link\"]',\n };\n var HOVER_CLASS = 'bg-primary text-white';\n var TARGET_CLASS = 'drop-target';\n var HOVER_TIME = 1000; // 1 second hover to change month.\n\n // We store some static variables at the module level because this\n // module is called each time the calendar month view is reloaded but\n // we want some actions to only occur ones.\n\n /* @var {bool} registered If the event listeners have been added */\n var registered = false;\n /* @var {int} hoverTimer The timeout id of any timeout waiting for hover */\n var hoverTimer = null;\n /* @var {object} root The root nav element we're operating on */\n var root = null;\n\n /**\n * Add or remove the appropriate styling to indicate whether\n * the drop target is being hovered over.\n *\n * @param {object} target The target drop zone element\n * @param {bool} hovered If the element is hovered over ot not\n */\n var updateHoverState = function(target, hovered) {\n if (hovered) {\n target.addClass(HOVER_CLASS);\n } else {\n target.removeClass(HOVER_CLASS);\n }\n };\n\n /**\n * Add some styling to the UI to indicate that the nav links\n * are an acceptable drop target.\n */\n var addDropZoneIndicator = function() {\n root.find(SELECTORS.DROP_ZONE).addClass(TARGET_CLASS);\n };\n\n /**\n * Remove the styling from the nav links.\n */\n var removeDropZoneIndicator = function() {\n root.find(SELECTORS.DROP_ZONE).removeClass(TARGET_CLASS);\n };\n\n /**\n * Get the drop zone target from the event, if one is found.\n *\n * @param {event} e Javascript event\n * @return {object|null}\n */\n var getTargetFromEvent = function(e) {\n var target = $(e.target).closest(SELECTORS.DROP_ZONE);\n return (target.length) ? target : null;\n };\n\n /**\n * This will add a visual indicator to the calendar UI to\n * indicate which nav link is a valid drop zone.\n *\n * @param {Event} e\n */\n var dragstartHandler = function(e) {\n // Make sure the drag event is for a calendar event.\n var eventElement = $(e.target).closest(SELECTORS.DRAGGABLE);\n\n if (eventElement.length) {\n addDropZoneIndicator();\n }\n };\n\n /**\n * Update the hover state of the target nav element when\n * the user is dragging an event over it.\n *\n * This will add a visual indicator to the calendar UI to\n * indicate which nav link is being hovered.\n *\n * @param {event} e The dragover event\n */\n var dragoverHandler = function(e) {\n // Ignore dragging of non calendar events.\n if (!DataStore.hasEventId()) {\n return;\n }\n\n e.preventDefault();\n var target = getTargetFromEvent(e);\n\n if (!target) {\n return;\n }\n\n // If we're not draggin a calendar event then\n // ignore it.\n if (!DataStore.hasEventId()) {\n return;\n }\n\n if (!hoverTimer) {\n hoverTimer = setTimeout(function() {\n target.click();\n hoverTimer = null;\n }, HOVER_TIME);\n }\n\n updateHoverState(target, true);\n removeDropZoneIndicator();\n };\n\n /**\n * Update the hover state of the target nav element that was\n * previously dragged over but has is no longer a drag target.\n *\n * This will remove the visual indicator from the calendar UI\n * that was added by the dragoverHandler.\n *\n * @param {event} e The dragstart event\n */\n var dragleaveHandler = function(e) {\n // Ignore dragging of non calendar events.\n if (!DataStore.hasEventId()) {\n return;\n }\n\n var target = getTargetFromEvent(e);\n\n if (!target) {\n return;\n }\n\n if (hoverTimer) {\n clearTimeout(hoverTimer);\n hoverTimer = null;\n }\n\n updateHoverState(target, false);\n addDropZoneIndicator();\n e.preventDefault();\n };\n\n /**\n * Remove the visual indicator from the calendar UI that was\n * added by the dragoverHandler.\n *\n * @param {event} e The drop event\n */\n var dropHandler = function(e) {\n // Ignore dragging of non calendar events.\n if (!DataStore.hasEventId()) {\n return;\n }\n\n removeDropZoneIndicator();\n var target = getTargetFromEvent(e);\n\n if (!target) {\n return;\n }\n\n updateHoverState(target, false);\n e.preventDefault();\n };\n\n return {\n /**\n * Initialise the event handlers for the drag events.\n *\n * @param {object} rootElement The element containing calendar nav links\n */\n init: function(rootElement) {\n // Only register the handlers once on the first load.\n if (!registered) {\n // These handlers are only added the first time the module\n // is loaded because we don't want to have a new listener\n // added each time the \"init\" function is called otherwise we'll\n // end up with lots of stale handlers.\n document.addEventListener('dragstart', dragstartHandler, false);\n document.addEventListener('dragover', dragoverHandler, false);\n document.addEventListener('dragleave', dragleaveHandler, false);\n document.addEventListener('drop', dropHandler, false);\n document.addEventListener('dragend', removeDropZoneIndicator, false);\n registered = true;\n }\n\n // Update the module variable to operate on the given\n // root element.\n root = $(rootElement);\n\n // If we're currently dragging then add the indicators.\n if (DataStore.hasEventId()) {\n addDropZoneIndicator();\n }\n },\n };\n});\n"],"names":["define","$","DataStore","SELECTORS","registered","hoverTimer","root","updateHoverState","target","hovered","addClass","removeClass","addDropZoneIndicator","find","removeDropZoneIndicator","getTargetFromEvent","e","closest","length","dragstartHandler","dragoverHandler","hasEventId","preventDefault","setTimeout","click","dragleaveHandler","clearTimeout","dropHandler","init","rootElement","document","addEventListener"],"mappings":";;;;;;;;;;;;AA2BAA,kDAAO,CACK,SACA,uCAEJ,SACIC,EACAC,eAGJC,oBACW,+CADXA,oBAEW,8BAWXC,YAAa,EAEbC,WAAa,KAEbC,KAAO,KASPC,iBAAmB,SAASC,OAAQC,SAChCA,QACAD,OAAOE,SAxBG,yBA0BVF,OAAOG,YA1BG,0BAkCdC,qBAAuB,WACvBN,KAAKO,KAAKV,qBAAqBO,SAlChB,gBAwCfI,wBAA0B,WAC1BR,KAAKO,KAAKV,qBAAqBQ,YAzChB,gBAkDfI,mBAAqB,SAASC,OAC1BR,OAASP,EAAEe,EAAER,QAAQS,QAAQd,4BACzBK,OAAOU,OAAUV,OAAS,MASlCW,iBAAmB,SAASH,GAETf,EAAEe,EAAER,QAAQS,QAAQd,qBAEtBe,QACbN,wBAaJQ,gBAAkB,SAASJ,MAEtBd,UAAUmB,cAIfL,EAAEM,qBACEd,OAASO,mBAAmBC,GAE3BR,QAMAN,UAAUmB,eAIVhB,aACDA,WAAakB,YAAW,WACpBf,OAAOgB,QACPnB,WAAa,OApGR,MAwGbE,iBAAiBC,QAAQ,GACzBM,6BAYAW,iBAAmB,SAAST,MAEvBd,UAAUmB,kBAIXb,OAASO,mBAAmBC,GAE3BR,SAIDH,aACAqB,aAAarB,YACbA,WAAa,MAGjBE,iBAAiBC,QAAQ,GACzBI,uBACAI,EAAEM,oBASFK,YAAc,SAASX,MAElBd,UAAUmB,cAIfP,8BACIN,OAASO,mBAAmBC,GAE3BR,SAILD,iBAAiBC,QAAQ,GACzBQ,EAAEM,0BAGC,CAMHM,KAAM,SAASC,aAENzB,aAKD0B,SAASC,iBAAiB,YAAaZ,kBAAkB,GACzDW,SAASC,iBAAiB,WAAYX,iBAAiB,GACvDU,SAASC,iBAAiB,YAAaN,kBAAkB,GACzDK,SAASC,iBAAiB,OAAQJ,aAAa,GAC/CG,SAASC,iBAAiB,UAAWjB,yBAAyB,GAC9DV,YAAa,GAKjBE,KAAOL,EAAE4B,aAGL3B,UAAUmB,cACVT"}
\ No newline at end of file
+{"version":3,"file":"month_navigation_drag_drop.min.js","sources":["../src/month_navigation_drag_drop.js"],"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 * A javascript module to handle calendar drag and drop in the calendar\n * month view navigation.\n *\n * This code is run each time the calendar month view is re-rendered. We\n * only register the event handlers once per page load so that the in place\n * DOM updates that happen on month change don't continue to register handlers.\n *\n * @module core_calendar/month_navigation_drag_drop\n * @copyright 2017 Ryan Wyllie \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine([\n 'jquery',\n 'core_calendar/drag_drop_data_store',\n ],\n function(\n $,\n DataStore\n ) {\n\n var SELECTORS = {\n DRAGGABLE: '[draggable=\"true\"][data-region=\"event-item\"]',\n DROP_ZONE: '[data-drop-zone=\"nav-link\"]',\n };\n var HOVER_CLASS = 'bg-primary text-white';\n var TARGET_CLASS = 'drop-target';\n var HOVER_TIME = 1000; // 1 second hover to change month.\n\n // We store some static variables at the module level because this\n // module is called each time the calendar month view is reloaded but\n // we want some actions to only occur ones.\n\n /* @var {bool} registered If the event listeners have been added */\n var registered = false;\n /* @var {int} hoverTimer The timeout id of any timeout waiting for hover */\n var hoverTimer = null;\n /* @var {object} root The root nav element we're operating on */\n var root = null;\n\n /**\n * Add or remove the appropriate styling to indicate whether\n * the drop target is being hovered over.\n *\n * @param {object} target The target drop zone element\n * @param {bool} hovered If the element is hovered over ot not\n */\n var updateHoverState = function(target, hovered) {\n if (hovered) {\n target.addClass(HOVER_CLASS);\n } else {\n target.removeClass(HOVER_CLASS);\n }\n };\n\n /**\n * Add some styling to the UI to indicate that the nav links\n * are an acceptable drop target.\n */\n var addDropZoneIndicator = function() {\n root.find(SELECTORS.DROP_ZONE).addClass(TARGET_CLASS);\n };\n\n /**\n * Remove the styling from the nav links.\n */\n var removeDropZoneIndicator = function() {\n root.find(SELECTORS.DROP_ZONE).removeClass(TARGET_CLASS);\n };\n\n /**\n * Get the drop zone target from the event, if one is found.\n *\n * @param {event} e Javascript event\n * @return {object|null}\n */\n var getTargetFromEvent = function(e) {\n var target = $(e.target).closest(SELECTORS.DROP_ZONE);\n return (target.length) ? target : null;\n };\n\n /**\n * This will add a visual indicator to the calendar UI to\n * indicate which nav link is a valid drop zone.\n *\n * @param {Event} e\n */\n var dragstartHandler = function(e) {\n // Make sure the drag event is for a calendar event.\n var eventElement = $(e.target).closest(SELECTORS.DRAGGABLE);\n\n if (eventElement.length) {\n addDropZoneIndicator();\n }\n };\n\n /**\n * Update the hover state of the target nav element when\n * the user is dragging an event over it.\n *\n * This will add a visual indicator to the calendar UI to\n * indicate which nav link is being hovered.\n *\n * @param {event} e The dragover event\n */\n var dragoverHandler = function(e) {\n // Ignore dragging of non calendar events.\n if (!DataStore.hasEventId()) {\n return;\n }\n\n e.preventDefault();\n var target = getTargetFromEvent(e);\n\n if (!target) {\n return;\n }\n\n // If we're not draggin a calendar event then\n // ignore it.\n if (!DataStore.hasEventId()) {\n return;\n }\n\n if (!hoverTimer) {\n hoverTimer = setTimeout(function() {\n target.click();\n hoverTimer = null;\n }, HOVER_TIME);\n }\n\n updateHoverState(target, true);\n removeDropZoneIndicator();\n };\n\n /**\n * Update the hover state of the target nav element that was\n * previously dragged over but has is no longer a drag target.\n *\n * This will remove the visual indicator from the calendar UI\n * that was added by the dragoverHandler.\n *\n * @param {event} e The dragstart event\n */\n var dragleaveHandler = function(e) {\n // Ignore dragging of non calendar events.\n if (!DataStore.hasEventId()) {\n return;\n }\n\n var target = getTargetFromEvent(e);\n\n if (!target) {\n return;\n }\n\n if (hoverTimer) {\n clearTimeout(hoverTimer);\n hoverTimer = null;\n }\n\n updateHoverState(target, false);\n addDropZoneIndicator();\n e.preventDefault();\n };\n\n /**\n * Remove the visual indicator from the calendar UI that was\n * added by the dragoverHandler.\n *\n * @param {event} e The drop event\n */\n var dropHandler = function(e) {\n // Ignore dragging of non calendar events.\n if (!DataStore.hasEventId()) {\n return;\n }\n\n removeDropZoneIndicator();\n var target = getTargetFromEvent(e);\n\n if (!target) {\n return;\n }\n\n updateHoverState(target, false);\n e.preventDefault();\n };\n\n return {\n /**\n * Initialise the event handlers for the drag events.\n *\n * @param {object} rootElement The element containing calendar nav links\n */\n init: function(rootElement) {\n // Only register the handlers once on the first load.\n if (!registered) {\n // These handlers are only added the first time the module\n // is loaded because we don't want to have a new listener\n // added each time the \"init\" function is called otherwise we'll\n // end up with lots of stale handlers.\n document.addEventListener('dragstart', dragstartHandler, false);\n document.addEventListener('dragover', dragoverHandler, false);\n document.addEventListener('dragleave', dragleaveHandler, false);\n document.addEventListener('drop', dropHandler, false);\n document.addEventListener('dragend', removeDropZoneIndicator, false);\n registered = true;\n }\n\n // Update the module variable to operate on the given\n // root element.\n root = $(rootElement);\n\n // If we're currently dragging then add the indicators.\n if (DataStore.hasEventId()) {\n addDropZoneIndicator();\n }\n },\n };\n});\n"],"names":["define","$","DataStore","SELECTORS","registered","hoverTimer","root","updateHoverState","target","hovered","addClass","removeClass","addDropZoneIndicator","find","removeDropZoneIndicator","getTargetFromEvent","e","closest","length","dragstartHandler","dragoverHandler","hasEventId","preventDefault","setTimeout","click","dragleaveHandler","clearTimeout","dropHandler","init","rootElement","document","addEventListener"],"mappings":";;;;;;;;;;;;AA2BAA,OAAM,2CAAC,CACK,SACA,uCAEJ,SACIC,EACAC,WAGR,IAAIC,oBACW,+CADXA,oBAEW,8BAWXC,YAAa,EAEbC,WAAa,KAEbC,KAAO,KASPC,iBAAmB,SAASC,OAAQC,SAChCA,QACAD,OAAOE,SAxBG,yBA0BVF,OAAOG,YA1BG,0BAkCdC,qBAAuB,WACvBN,KAAKO,KAAKV,qBAAqBO,SAlChB,gBAwCfI,wBAA0B,WAC1BR,KAAKO,KAAKV,qBAAqBQ,YAzChB,gBAkDfI,mBAAqB,SAASC,GAC9B,IAAIR,OAASP,EAAEe,EAAER,QAAQS,QAAQd,qBACjC,OAAQK,OAAOU,OAAUV,OAAS,MASlCW,iBAAmB,SAASH,GAETf,EAAEe,EAAER,QAAQS,QAAQd,qBAEtBe,QACbN,wBAaJQ,gBAAkB,SAASJ,GAE3B,GAAKd,UAAUmB,aAAf,CAIAL,EAAEM,iBACF,IAAId,OAASO,mBAAmBC,GAE3BR,QAMAN,UAAUmB,eAIVhB,aACDA,WAAakB,YAAW,WACpBf,OAAOgB,QACPnB,WAAa,IAChB,GArGQ,MAwGbE,iBAAiBC,QAAQ,GACzBM,0BAvBA,GAmCAW,iBAAmB,SAAST,GAE5B,GAAKd,UAAUmB,aAAf,CAIA,IAAIb,OAASO,mBAAmBC,GAE3BR,SAIDH,aACAqB,aAAarB,YACbA,WAAa,MAGjBE,iBAAiBC,QAAQ,GACzBI,uBACAI,EAAEM,iBAfF,GAwBAK,YAAc,SAASX,GAEvB,GAAKd,UAAUmB,aAAf,CAIAP,0BACA,IAAIN,OAASO,mBAAmBC,GAE3BR,SAILD,iBAAiBC,QAAQ,GACzBQ,EAAEM,iBAVF,GAaJ,MAAO,CAMHM,KAAM,SAASC,aAENzB,aAKD0B,SAASC,iBAAiB,YAAaZ,kBAAkB,GACzDW,SAASC,iBAAiB,WAAYX,iBAAiB,GACvDU,SAASC,iBAAiB,YAAaN,kBAAkB,GACzDK,SAASC,iBAAiB,OAAQJ,aAAa,GAC/CG,SAASC,iBAAiB,UAAWjB,yBAAyB,GAC9DV,YAAa,GAKjBE,KAAOL,EAAE4B,aAGL3B,UAAUmB,cACVT,sBAER,EAER"}
\ No newline at end of file
diff --git a/calendar/amd/build/month_view_drag_drop.min.js.map b/calendar/amd/build/month_view_drag_drop.min.js.map
index cf1e4eb5c42..ded5b38f0d5 100644
--- a/calendar/amd/build/month_view_drag_drop.min.js.map
+++ b/calendar/amd/build/month_view_drag_drop.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"month_view_drag_drop.min.js","sources":["../src/month_view_drag_drop.js"],"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 * A javascript module to handle calendar drag and drop in the calendar\n * month view.\n *\n * @module core_calendar/month_view_drag_drop\n * @copyright 2017 Ryan Wyllie \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine([\n 'jquery',\n 'core/notification',\n 'core/str',\n 'core_calendar/events',\n 'core_calendar/drag_drop_data_store'\n ],\n function(\n $,\n Notification,\n Str,\n CalendarEvents,\n DataStore\n ) {\n\n var SELECTORS = {\n ROOT: \"[data-region='calendar']\",\n DRAGGABLE: '[draggable=\"true\"][data-region=\"event-item\"]',\n DROP_ZONE: '[data-drop-zone=\"month-view-day\"]',\n WEEK: '[data-region=\"month-view-week\"]',\n };\n var INVALID_DROP_ZONE_CLASS = 'bg-faded';\n var INVALID_HOVER_CLASS = 'bg-danger text-white';\n var VALID_HOVER_CLASS = 'bg-primary text-white';\n var ALL_CLASSES = INVALID_DROP_ZONE_CLASS + ' ' + INVALID_HOVER_CLASS + ' ' + VALID_HOVER_CLASS;\n /* @var {bool} registered If the event listeners have been added */\n var registered = false;\n\n /**\n * Get the correct drop zone element from the given javascript\n * event.\n *\n * @param {event} e The javascript event\n * @return {object|null}\n */\n var getDropZoneFromEvent = function(e) {\n var dropZone = $(e.target).closest(SELECTORS.DROP_ZONE);\n return (dropZone.length) ? dropZone : null;\n };\n\n /**\n * Determine if the given dropzone element is within the acceptable\n * time range.\n *\n * The drop zone timestamp is midnight on that day so we should check\n * that the event's acceptable timestart value\n *\n * @param {object} dropZone The drop zone day from the calendar\n * @return {bool}\n */\n var isValidDropZone = function(dropZone) {\n var dropTimestamp = dropZone.attr('data-day-timestamp');\n var minTimestart = DataStore.getMinTimestart();\n var maxTimestart = DataStore.getMaxTimestart();\n\n if (minTimestart && minTimestart > dropTimestamp) {\n return false;\n }\n\n if (maxTimestart && maxTimestart < dropTimestamp) {\n return false;\n }\n\n return true;\n };\n\n /**\n * Get the error string to display for a given drop zone element\n * if it is invalid.\n *\n * @param {object} dropZone The drop zone day from the calendar\n * @return {string}\n */\n var getDropZoneError = function(dropZone) {\n var dropTimestamp = dropZone.attr('data-day-timestamp');\n var minTimestart = DataStore.getMinTimestart();\n var maxTimestart = DataStore.getMaxTimestart();\n\n if (minTimestart && minTimestart > dropTimestamp) {\n return DataStore.getMinError();\n }\n\n if (maxTimestart && maxTimestart < dropTimestamp) {\n return DataStore.getMaxError();\n }\n\n return null;\n };\n\n /**\n * Remove all of the styling from each of the drop zones in the calendar.\n */\n var clearAllDropZonesState = function() {\n $(SELECTORS.ROOT).find(SELECTORS.DROP_ZONE).each(function(index, dropZone) {\n dropZone = $(dropZone);\n dropZone.removeClass(ALL_CLASSES);\n });\n };\n\n /**\n * Update the hover state for the event in the calendar to reflect\n * which days the event will be moved to.\n *\n * If the drop zone is not being hovered then it will apply some\n * styling to reflect whether the drop zone is a valid or invalid\n * drop place for the current dragging event.\n *\n * This funciton supports events spanning multiple days and will\n * recurse to highlight (or remove highlight) each of the days\n * that the event will be moved to.\n *\n * For example: An event with a duration of 3 days will have\n * 3 days highlighted when it's dragged elsewhere in the calendar.\n * The current drag target and the 2 days following it (including\n * wrapping to the next week if necessary).\n *\n * @param {string|object} dropZone The drag target element\n * @param {bool} hovered If the target is hovered or not\n * @param {Number} count How many days to highlight (default to duration)\n */\n var updateHoverState = function(dropZone, hovered, count) {\n if (typeof count === 'undefined') {\n // This is how many days we need to highlight.\n count = DataStore.getDurationDays();\n }\n\n var valid = isValidDropZone(dropZone);\n dropZone.removeClass(ALL_CLASSES);\n\n if (hovered) {\n\n if (valid) {\n dropZone.addClass(VALID_HOVER_CLASS);\n } else {\n dropZone.addClass(INVALID_HOVER_CLASS);\n }\n } else {\n dropZone.removeClass(VALID_HOVER_CLASS + ' ' + INVALID_HOVER_CLASS);\n\n if (!valid) {\n dropZone.addClass(INVALID_DROP_ZONE_CLASS);\n }\n }\n\n count--;\n\n // If we've still got days to highlight then we should\n // find the next day.\n if (count > 0) {\n var nextDropZone = dropZone.next();\n\n // If there are no more days in this week then we\n // need to move down to the next week in the calendar.\n if (!nextDropZone.length) {\n var nextWeek = dropZone.closest(SELECTORS.WEEK).next();\n\n if (nextWeek.length) {\n nextDropZone = nextWeek.children(SELECTORS.DROP_ZONE).first();\n }\n }\n\n // If we found another day then let's recursively\n // update it's hover state.\n if (nextDropZone.length) {\n updateHoverState(nextDropZone, hovered, count);\n }\n }\n };\n\n /**\n * Find all of the calendar event drop zones in the calendar and update the display\n * for the user to indicate which zones are valid and invalid.\n */\n var updateAllDropZonesState = function() {\n $(SELECTORS.ROOT).find(SELECTORS.DROP_ZONE).each(function(index, dropZone) {\n dropZone = $(dropZone);\n\n if (!isValidDropZone(dropZone)) {\n updateHoverState(dropZone, false);\n }\n });\n };\n\n\n /**\n * Set up the module level variables to track which event is being\n * dragged and how many days it spans.\n *\n * @param {event} e The dragstart event\n */\n var dragstartHandler = function(e) {\n var target = $(e.target);\n var draggableElement = target.closest(SELECTORS.DRAGGABLE);\n\n if (!draggableElement.length) {\n return;\n }\n\n var eventElement = draggableElement.find('[data-event-id]');\n var eventId = eventElement.attr('data-event-id');\n var minTimestart = draggableElement.attr('data-min-day-timestamp');\n var maxTimestart = draggableElement.attr('data-max-day-timestamp');\n var minError = draggableElement.attr('data-min-day-error');\n var maxError = draggableElement.attr('data-max-day-error');\n var eventsSelector = SELECTORS.ROOT + ' [data-event-id=\"' + eventId + '\"]';\n var duration = $(eventsSelector).length;\n\n DataStore.setEventId(eventId);\n DataStore.setDurationDays(duration);\n\n if (minTimestart) {\n DataStore.setMinTimestart(minTimestart);\n }\n\n if (maxTimestart) {\n DataStore.setMaxTimestart(maxTimestart);\n }\n\n if (minError) {\n DataStore.setMinError(minError);\n }\n\n if (maxError) {\n DataStore.setMaxError(maxError);\n }\n\n e.dataTransfer.effectAllowed = \"move\";\n e.dataTransfer.dropEffect = \"move\";\n // Firefox requires a value to be set here or the drag won't\n // work and the dragover handler won't fire.\n e.dataTransfer.setData('text/plain', eventId);\n e.dropEffect = \"move\";\n\n updateAllDropZonesState();\n };\n\n /**\n * Update the hover state of the target day element when\n * the user is dragging an event over it.\n *\n * This will add a visual indicator to the calendar UI to\n * indicate which day(s) the event will be moved to.\n *\n * @param {event} e The dragstart event\n */\n var dragoverHandler = function(e) {\n // Ignore dragging of non calendar events.\n if (!DataStore.hasEventId()) {\n return;\n }\n\n e.preventDefault();\n\n var dropZone = getDropZoneFromEvent(e);\n\n if (!dropZone) {\n return;\n }\n\n updateHoverState(dropZone, true);\n };\n\n /**\n * Update the hover state of the target day element that was\n * previously dragged over but has is no longer a drag target.\n *\n * This will remove the visual indicator from the calendar UI\n * that was added by the dragoverHandler.\n *\n * @param {event} e The dragstart event\n */\n var dragleaveHandler = function(e) {\n // Ignore dragging of non calendar events.\n if (!DataStore.hasEventId()) {\n return;\n }\n\n var dropZone = getDropZoneFromEvent(e);\n\n if (!dropZone) {\n return;\n }\n\n updateHoverState(dropZone, false);\n e.preventDefault();\n };\n\n /**\n * Determines the event element, origin day, and destination day\n * once the user drops the calendar event. These three bits of data\n * are provided as the payload to the \"moveEvent\" calendar javascript\n * event that is fired.\n *\n * This will remove the visual indicator from the calendar UI\n * that was added by the dragoverHandler.\n *\n * @param {event} e The dragstart event\n */\n var dropHandler = function(e) {\n // Ignore dragging of non calendar events.\n if (!DataStore.hasEventId()) {\n return;\n }\n\n var dropZone = getDropZoneFromEvent(e);\n\n if (!dropZone) {\n DataStore.clearAll();\n clearAllDropZonesState();\n return;\n }\n\n if (isValidDropZone(dropZone)) {\n var eventId = DataStore.getEventId();\n var eventElementSelector = SELECTORS.ROOT + ' [data-event-id=\"' + eventId + '\"]';\n var eventElement = $(eventElementSelector);\n var origin = null;\n\n if (eventElement.length) {\n origin = eventElement.closest(SELECTORS.DROP_ZONE);\n }\n\n $('body').trigger(CalendarEvents.moveEvent, [eventId, origin, dropZone]);\n } else {\n // If the drop zone is not valid then there is not need for us to\n // try to process it. Instead we can just show an error to the user.\n var message = getDropZoneError(dropZone);\n Str.get_string('errorinvaliddate', 'calendar').then(function(string) {\n Notification.exception({\n name: string,\n message: message || string\n });\n });\n }\n\n DataStore.clearAll();\n clearAllDropZonesState();\n\n e.preventDefault();\n };\n\n /**\n * Clear the data store and remove the drag indicators from the UI\n * when the drag event has finished.\n */\n var dragendHandler = function() {\n DataStore.clearAll();\n clearAllDropZonesState();\n };\n\n /**\n * Re-render the drop zones in the new month to highlight\n * which areas are or aren't acceptable to drop the calendar\n * event.\n */\n var calendarMonthChangedHandler = function() {\n updateAllDropZonesState();\n };\n\n return {\n /**\n * Initialise the event handlers for the drag events.\n */\n init: function() {\n if (!registered) {\n // These handlers are only added the first time the module\n // is loaded because we don't want to have a new listener\n // added each time the \"init\" function is called otherwise we'll\n // end up with lots of stale handlers.\n document.addEventListener('dragstart', dragstartHandler, false);\n document.addEventListener('dragover', dragoverHandler, false);\n document.addEventListener('dragleave', dragleaveHandler, false);\n document.addEventListener('drop', dropHandler, false);\n document.addEventListener('dragend', dragendHandler, false);\n $('body').on(CalendarEvents.monthChanged, calendarMonthChangedHandler);\n registered = true;\n }\n },\n };\n});\n"],"names":["define","$","Notification","Str","CalendarEvents","DataStore","SELECTORS","ALL_CLASSES","INVALID_DROP_ZONE_CLASS","registered","getDropZoneFromEvent","e","dropZone","target","closest","length","isValidDropZone","dropTimestamp","attr","minTimestart","getMinTimestart","maxTimestart","getMaxTimestart","clearAllDropZonesState","find","each","index","removeClass","updateHoverState","hovered","count","getDurationDays","valid","addClass","VALID_HOVER_CLASS","nextDropZone","next","nextWeek","children","first","updateAllDropZonesState","dragstartHandler","draggableElement","eventId","minError","maxError","duration","setEventId","setDurationDays","setMinTimestart","setMaxTimestart","setMinError","setMaxError","dataTransfer","effectAllowed","dropEffect","setData","dragoverHandler","hasEventId","preventDefault","dragleaveHandler","dropHandler","clearAll","getEventId","eventElement","origin","trigger","moveEvent","message","getMinError","getMaxError","getDropZoneError","get_string","then","string","exception","name","dragendHandler","calendarMonthChangedHandler","init","document","addEventListener","on","monthChanged"],"mappings":"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"}
\ No newline at end of file
+{"version":3,"file":"month_view_drag_drop.min.js","sources":["../src/month_view_drag_drop.js"],"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 * A javascript module to handle calendar drag and drop in the calendar\n * month view.\n *\n * @module core_calendar/month_view_drag_drop\n * @copyright 2017 Ryan Wyllie \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine([\n 'jquery',\n 'core/notification',\n 'core/str',\n 'core_calendar/events',\n 'core_calendar/drag_drop_data_store'\n ],\n function(\n $,\n Notification,\n Str,\n CalendarEvents,\n DataStore\n ) {\n\n var SELECTORS = {\n ROOT: \"[data-region='calendar']\",\n DRAGGABLE: '[draggable=\"true\"][data-region=\"event-item\"]',\n DROP_ZONE: '[data-drop-zone=\"month-view-day\"]',\n WEEK: '[data-region=\"month-view-week\"]',\n };\n var INVALID_DROP_ZONE_CLASS = 'bg-faded';\n var INVALID_HOVER_CLASS = 'bg-danger text-white';\n var VALID_HOVER_CLASS = 'bg-primary text-white';\n var ALL_CLASSES = INVALID_DROP_ZONE_CLASS + ' ' + INVALID_HOVER_CLASS + ' ' + VALID_HOVER_CLASS;\n /* @var {bool} registered If the event listeners have been added */\n var registered = false;\n\n /**\n * Get the correct drop zone element from the given javascript\n * event.\n *\n * @param {event} e The javascript event\n * @return {object|null}\n */\n var getDropZoneFromEvent = function(e) {\n var dropZone = $(e.target).closest(SELECTORS.DROP_ZONE);\n return (dropZone.length) ? dropZone : null;\n };\n\n /**\n * Determine if the given dropzone element is within the acceptable\n * time range.\n *\n * The drop zone timestamp is midnight on that day so we should check\n * that the event's acceptable timestart value\n *\n * @param {object} dropZone The drop zone day from the calendar\n * @return {bool}\n */\n var isValidDropZone = function(dropZone) {\n var dropTimestamp = dropZone.attr('data-day-timestamp');\n var minTimestart = DataStore.getMinTimestart();\n var maxTimestart = DataStore.getMaxTimestart();\n\n if (minTimestart && minTimestart > dropTimestamp) {\n return false;\n }\n\n if (maxTimestart && maxTimestart < dropTimestamp) {\n return false;\n }\n\n return true;\n };\n\n /**\n * Get the error string to display for a given drop zone element\n * if it is invalid.\n *\n * @param {object} dropZone The drop zone day from the calendar\n * @return {string}\n */\n var getDropZoneError = function(dropZone) {\n var dropTimestamp = dropZone.attr('data-day-timestamp');\n var minTimestart = DataStore.getMinTimestart();\n var maxTimestart = DataStore.getMaxTimestart();\n\n if (minTimestart && minTimestart > dropTimestamp) {\n return DataStore.getMinError();\n }\n\n if (maxTimestart && maxTimestart < dropTimestamp) {\n return DataStore.getMaxError();\n }\n\n return null;\n };\n\n /**\n * Remove all of the styling from each of the drop zones in the calendar.\n */\n var clearAllDropZonesState = function() {\n $(SELECTORS.ROOT).find(SELECTORS.DROP_ZONE).each(function(index, dropZone) {\n dropZone = $(dropZone);\n dropZone.removeClass(ALL_CLASSES);\n });\n };\n\n /**\n * Update the hover state for the event in the calendar to reflect\n * which days the event will be moved to.\n *\n * If the drop zone is not being hovered then it will apply some\n * styling to reflect whether the drop zone is a valid or invalid\n * drop place for the current dragging event.\n *\n * This funciton supports events spanning multiple days and will\n * recurse to highlight (or remove highlight) each of the days\n * that the event will be moved to.\n *\n * For example: An event with a duration of 3 days will have\n * 3 days highlighted when it's dragged elsewhere in the calendar.\n * The current drag target and the 2 days following it (including\n * wrapping to the next week if necessary).\n *\n * @param {string|object} dropZone The drag target element\n * @param {bool} hovered If the target is hovered or not\n * @param {Number} count How many days to highlight (default to duration)\n */\n var updateHoverState = function(dropZone, hovered, count) {\n if (typeof count === 'undefined') {\n // This is how many days we need to highlight.\n count = DataStore.getDurationDays();\n }\n\n var valid = isValidDropZone(dropZone);\n dropZone.removeClass(ALL_CLASSES);\n\n if (hovered) {\n\n if (valid) {\n dropZone.addClass(VALID_HOVER_CLASS);\n } else {\n dropZone.addClass(INVALID_HOVER_CLASS);\n }\n } else {\n dropZone.removeClass(VALID_HOVER_CLASS + ' ' + INVALID_HOVER_CLASS);\n\n if (!valid) {\n dropZone.addClass(INVALID_DROP_ZONE_CLASS);\n }\n }\n\n count--;\n\n // If we've still got days to highlight then we should\n // find the next day.\n if (count > 0) {\n var nextDropZone = dropZone.next();\n\n // If there are no more days in this week then we\n // need to move down to the next week in the calendar.\n if (!nextDropZone.length) {\n var nextWeek = dropZone.closest(SELECTORS.WEEK).next();\n\n if (nextWeek.length) {\n nextDropZone = nextWeek.children(SELECTORS.DROP_ZONE).first();\n }\n }\n\n // If we found another day then let's recursively\n // update it's hover state.\n if (nextDropZone.length) {\n updateHoverState(nextDropZone, hovered, count);\n }\n }\n };\n\n /**\n * Find all of the calendar event drop zones in the calendar and update the display\n * for the user to indicate which zones are valid and invalid.\n */\n var updateAllDropZonesState = function() {\n $(SELECTORS.ROOT).find(SELECTORS.DROP_ZONE).each(function(index, dropZone) {\n dropZone = $(dropZone);\n\n if (!isValidDropZone(dropZone)) {\n updateHoverState(dropZone, false);\n }\n });\n };\n\n\n /**\n * Set up the module level variables to track which event is being\n * dragged and how many days it spans.\n *\n * @param {event} e The dragstart event\n */\n var dragstartHandler = function(e) {\n var target = $(e.target);\n var draggableElement = target.closest(SELECTORS.DRAGGABLE);\n\n if (!draggableElement.length) {\n return;\n }\n\n var eventElement = draggableElement.find('[data-event-id]');\n var eventId = eventElement.attr('data-event-id');\n var minTimestart = draggableElement.attr('data-min-day-timestamp');\n var maxTimestart = draggableElement.attr('data-max-day-timestamp');\n var minError = draggableElement.attr('data-min-day-error');\n var maxError = draggableElement.attr('data-max-day-error');\n var eventsSelector = SELECTORS.ROOT + ' [data-event-id=\"' + eventId + '\"]';\n var duration = $(eventsSelector).length;\n\n DataStore.setEventId(eventId);\n DataStore.setDurationDays(duration);\n\n if (minTimestart) {\n DataStore.setMinTimestart(minTimestart);\n }\n\n if (maxTimestart) {\n DataStore.setMaxTimestart(maxTimestart);\n }\n\n if (minError) {\n DataStore.setMinError(minError);\n }\n\n if (maxError) {\n DataStore.setMaxError(maxError);\n }\n\n e.dataTransfer.effectAllowed = \"move\";\n e.dataTransfer.dropEffect = \"move\";\n // Firefox requires a value to be set here or the drag won't\n // work and the dragover handler won't fire.\n e.dataTransfer.setData('text/plain', eventId);\n e.dropEffect = \"move\";\n\n updateAllDropZonesState();\n };\n\n /**\n * Update the hover state of the target day element when\n * the user is dragging an event over it.\n *\n * This will add a visual indicator to the calendar UI to\n * indicate which day(s) the event will be moved to.\n *\n * @param {event} e The dragstart event\n */\n var dragoverHandler = function(e) {\n // Ignore dragging of non calendar events.\n if (!DataStore.hasEventId()) {\n return;\n }\n\n e.preventDefault();\n\n var dropZone = getDropZoneFromEvent(e);\n\n if (!dropZone) {\n return;\n }\n\n updateHoverState(dropZone, true);\n };\n\n /**\n * Update the hover state of the target day element that was\n * previously dragged over but has is no longer a drag target.\n *\n * This will remove the visual indicator from the calendar UI\n * that was added by the dragoverHandler.\n *\n * @param {event} e The dragstart event\n */\n var dragleaveHandler = function(e) {\n // Ignore dragging of non calendar events.\n if (!DataStore.hasEventId()) {\n return;\n }\n\n var dropZone = getDropZoneFromEvent(e);\n\n if (!dropZone) {\n return;\n }\n\n updateHoverState(dropZone, false);\n e.preventDefault();\n };\n\n /**\n * Determines the event element, origin day, and destination day\n * once the user drops the calendar event. These three bits of data\n * are provided as the payload to the \"moveEvent\" calendar javascript\n * event that is fired.\n *\n * This will remove the visual indicator from the calendar UI\n * that was added by the dragoverHandler.\n *\n * @param {event} e The dragstart event\n */\n var dropHandler = function(e) {\n // Ignore dragging of non calendar events.\n if (!DataStore.hasEventId()) {\n return;\n }\n\n var dropZone = getDropZoneFromEvent(e);\n\n if (!dropZone) {\n DataStore.clearAll();\n clearAllDropZonesState();\n return;\n }\n\n if (isValidDropZone(dropZone)) {\n var eventId = DataStore.getEventId();\n var eventElementSelector = SELECTORS.ROOT + ' [data-event-id=\"' + eventId + '\"]';\n var eventElement = $(eventElementSelector);\n var origin = null;\n\n if (eventElement.length) {\n origin = eventElement.closest(SELECTORS.DROP_ZONE);\n }\n\n $('body').trigger(CalendarEvents.moveEvent, [eventId, origin, dropZone]);\n } else {\n // If the drop zone is not valid then there is not need for us to\n // try to process it. Instead we can just show an error to the user.\n var message = getDropZoneError(dropZone);\n Str.get_string('errorinvaliddate', 'calendar').then(function(string) {\n Notification.exception({\n name: string,\n message: message || string\n });\n });\n }\n\n DataStore.clearAll();\n clearAllDropZonesState();\n\n e.preventDefault();\n };\n\n /**\n * Clear the data store and remove the drag indicators from the UI\n * when the drag event has finished.\n */\n var dragendHandler = function() {\n DataStore.clearAll();\n clearAllDropZonesState();\n };\n\n /**\n * Re-render the drop zones in the new month to highlight\n * which areas are or aren't acceptable to drop the calendar\n * event.\n */\n var calendarMonthChangedHandler = function() {\n updateAllDropZonesState();\n };\n\n return {\n /**\n * Initialise the event handlers for the drag events.\n */\n init: function() {\n if (!registered) {\n // These handlers are only added the first time the module\n // is loaded because we don't want to have a new listener\n // added each time the \"init\" function is called otherwise we'll\n // end up with lots of stale handlers.\n document.addEventListener('dragstart', dragstartHandler, false);\n document.addEventListener('dragover', dragoverHandler, false);\n document.addEventListener('dragleave', dragleaveHandler, false);\n document.addEventListener('drop', dropHandler, false);\n document.addEventListener('dragend', dragendHandler, false);\n $('body').on(CalendarEvents.monthChanged, calendarMonthChangedHandler);\n registered = true;\n }\n },\n };\n});\n"],"names":["define","$","Notification","Str","CalendarEvents","DataStore","SELECTORS","ALL_CLASSES","INVALID_DROP_ZONE_CLASS","registered","getDropZoneFromEvent","e","dropZone","target","closest","length","isValidDropZone","dropTimestamp","attr","minTimestart","getMinTimestart","maxTimestart","getMaxTimestart","clearAllDropZonesState","find","each","index","removeClass","updateHoverState","hovered","count","getDurationDays","valid","addClass","VALID_HOVER_CLASS","nextDropZone","next","nextWeek","children","first","updateAllDropZonesState","dragstartHandler","draggableElement","eventId","minError","maxError","duration","setEventId","setDurationDays","setMinTimestart","setMaxTimestart","setMinError","setMaxError","dataTransfer","effectAllowed","dropEffect","setData","dragoverHandler","hasEventId","preventDefault","dragleaveHandler","dropHandler","clearAll","getEventId","eventElement","origin","trigger","moveEvent","message","getMinError","getMaxError","getDropZoneError","get_string","then","string","exception","name","dragendHandler","calendarMonthChangedHandler","init","document","addEventListener","on","monthChanged"],"mappings":"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"}
\ No newline at end of file
diff --git a/calendar/amd/build/popover.min.js.map b/calendar/amd/build/popover.min.js.map
index 0eea6d29076..4ee6e73684b 100644
--- a/calendar/amd/build/popover.min.js.map
+++ b/calendar/amd/build/popover.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"popover.min.js","sources":["../src/popover.js"],"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 popover for the `core_calendar` subsystem.\n *\n * @module core_calendar/popover\n * @copyright 2021 Huong Nguyen \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @since 4.0\n */\n\nimport 'theme_boost/popover';\nimport jQuery from 'jquery';\nimport * as CalendarSelectors from 'core_calendar/selectors';\n\n/**\n * Check if we are allowing to enable the popover or not.\n * @param {Element} dateContainer\n * @returns {boolean}\n */\nconst isPopoverAvailable = (dateContainer) => {\n return window.getComputedStyle(dateContainer.querySelector(CalendarSelectors.elements.dateContent)).display === 'none';\n};\n\nconst isPopoverConfigured = new Map();\n\nconst showPopover = target => {\n if (!isPopoverConfigured.has(target)) {\n const dateEle = jQuery(target);\n dateEle.popover({\n trigger: 'manual',\n placement: 'top',\n html: true,\n content: () => {\n const source = dateEle.find(CalendarSelectors.elements.dateContent);\n const content = jQuery('
');\n if (source.length) {\n const temptContent = source.find('.hidden').clone(false);\n content.html(temptContent.html());\n }\n return content.html();\n }\n });\n\n isPopoverConfigured.set(target, true);\n }\n\n if (isPopoverAvailable(target)) {\n jQuery(target).popover('show');\n target.addEventListener('mouseleave', hidePopover);\n target.addEventListener('focusout', hidePopover);\n }\n};\n\nconst hidePopover = e => {\n const target = e.target;\n const dateContainer = e.target.closest(CalendarSelectors.elements.dateContainer);\n if (!dateContainer) {\n return;\n }\n if (isPopoverConfigured.has(dateContainer)) {\n const isTargetActive = target.contains(document.activeElement);\n const isTargetHover = target.matches(':hover');\n if (!isTargetActive && !isTargetHover) {\n jQuery(dateContainer).popover('hide');\n dateContainer.removeEventListener('mouseleave', hidePopover);\n dateContainer.removeEventListener('focusout', hidePopover);\n }\n }\n};\n\n/**\n * Register events for date container.\n */\nconst registerEventListeners = () => {\n const showPopoverHandler = (e) => {\n const dateContainer = e.target.closest(CalendarSelectors.elements.dateContainer);\n if (!dateContainer) {\n return;\n }\n\n e.preventDefault();\n showPopover(dateContainer);\n };\n\n document.addEventListener('mouseover', showPopoverHandler);\n document.addEventListener('focusin', showPopoverHandler);\n};\n\nlet listenersRegistered = false;\nif (!listenersRegistered) {\n registerEventListeners();\n listenersRegistered = true;\n}\n"],"names":["isPopoverConfigured","Map","showPopover","target","has","dateEle","popover","trigger","placement","html","content","source","find","CalendarSelectors","elements","dateContent","length","temptContent","clone","set","dateContainer","window","getComputedStyle","querySelector","display","addEventListener","hidePopover","e","closest","isTargetActive","contains","document","activeElement","isTargetHover","matches","removeEventListener","listenersRegistered","showPopoverHandler","preventDefault","registerEventListeners"],"mappings":";;;;;;;;wgCAqCMA,oBAAsB,IAAIC,IAE1BC,YAAcC,aACXH,oBAAoBI,IAAID,QAAS,OAC5BE,SAAU,mBAAOF,QACvBE,QAAQC,QAAQ,CACZC,QAAS,SACTC,UAAW,MACXC,MAAM,EACNC,QAAS,WACCC,OAASN,QAAQO,KAAKC,kBAAkBC,SAASC,aACjDL,SAAU,mBAAO,YACnBC,OAAOK,OAAQ,OACTC,aAAeN,OAAOC,KAAK,WAAWM,OAAM,GAClDR,QAAQD,KAAKQ,aAAaR,eAEvBC,QAAQD,UAIvBT,oBAAoBmB,IAAIhB,QAAQ,GAxBZiB,IAAAA,cAAAA,cA2BDjB,OA1ByF,SAAzGkB,OAAOC,iBAAiBF,cAAcG,cAAcV,kBAAkBC,SAASC,cAAcS,8BA2BzFrB,QAAQG,QAAQ,QACvBH,OAAOsB,iBAAiB,aAAcC,aACtCvB,OAAOsB,iBAAiB,WAAYC,eAItCA,YAAcC,UACVxB,OAASwB,EAAExB,OACXiB,cAAgBO,EAAExB,OAAOyB,QAAQf,kBAAkBC,SAASM,kBAC7DA,eAGDpB,oBAAoBI,IAAIgB,eAAgB,OAClCS,eAAiB1B,OAAO2B,SAASC,SAASC,eAC1CC,cAAgB9B,OAAO+B,QAAQ,UAChCL,gBAAmBI,oCACbb,eAAed,QAAQ,QAC9Bc,cAAce,oBAAoB,aAAcT,aAChDN,cAAce,oBAAoB,WAAYT,oBAuBtDU,qBAAsB,EACrBA,sBAhB0B,YACrBC,mBAAsBV,UAClBP,cAAgBO,EAAExB,OAAOyB,QAAQf,kBAAkBC,SAASM,eAC7DA,gBAILO,EAAEW,iBACFpC,YAAYkB,iBAGhBW,SAASN,iBAAiB,YAAaY,oBACvCN,SAASN,iBAAiB,UAAWY,qBAKrCE,GACAH,qBAAsB"}
\ No newline at end of file
+{"version":3,"file":"popover.min.js","sources":["../src/popover.js"],"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 popover for the `core_calendar` subsystem.\n *\n * @module core_calendar/popover\n * @copyright 2021 Huong Nguyen \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @since 4.0\n */\n\nimport 'theme_boost/popover';\nimport jQuery from 'jquery';\nimport * as CalendarSelectors from 'core_calendar/selectors';\n\n/**\n * Check if we are allowing to enable the popover or not.\n * @param {Element} dateContainer\n * @returns {boolean}\n */\nconst isPopoverAvailable = (dateContainer) => {\n return window.getComputedStyle(dateContainer.querySelector(CalendarSelectors.elements.dateContent)).display === 'none';\n};\n\nconst isPopoverConfigured = new Map();\n\nconst showPopover = target => {\n if (!isPopoverConfigured.has(target)) {\n const dateEle = jQuery(target);\n dateEle.popover({\n trigger: 'manual',\n placement: 'top',\n html: true,\n content: () => {\n const source = dateEle.find(CalendarSelectors.elements.dateContent);\n const content = jQuery('
');\n if (source.length) {\n const temptContent = source.find('.hidden').clone(false);\n content.html(temptContent.html());\n }\n return content.html();\n }\n });\n\n isPopoverConfigured.set(target, true);\n }\n\n if (isPopoverAvailable(target)) {\n jQuery(target).popover('show');\n target.addEventListener('mouseleave', hidePopover);\n target.addEventListener('focusout', hidePopover);\n }\n};\n\nconst hidePopover = e => {\n const target = e.target;\n const dateContainer = e.target.closest(CalendarSelectors.elements.dateContainer);\n if (!dateContainer) {\n return;\n }\n if (isPopoverConfigured.has(dateContainer)) {\n const isTargetActive = target.contains(document.activeElement);\n const isTargetHover = target.matches(':hover');\n if (!isTargetActive && !isTargetHover) {\n jQuery(dateContainer).popover('hide');\n dateContainer.removeEventListener('mouseleave', hidePopover);\n dateContainer.removeEventListener('focusout', hidePopover);\n }\n }\n};\n\n/**\n * Register events for date container.\n */\nconst registerEventListeners = () => {\n const showPopoverHandler = (e) => {\n const dateContainer = e.target.closest(CalendarSelectors.elements.dateContainer);\n if (!dateContainer) {\n return;\n }\n\n e.preventDefault();\n showPopover(dateContainer);\n };\n\n document.addEventListener('mouseover', showPopoverHandler);\n document.addEventListener('focusin', showPopoverHandler);\n};\n\nlet listenersRegistered = false;\nif (!listenersRegistered) {\n registerEventListeners();\n listenersRegistered = true;\n}\n"],"names":["obj","_getRequireWildcardCache","nodeInterop","WeakMap","cacheBabelInterop","cacheNodeInterop","_jquery","__esModule","default","CalendarSelectors","cache","has","get","newObj","hasPropertyDescriptor","Object","defineProperty","getOwnPropertyDescriptor","key","prototype","hasOwnProperty","call","desc","set","_interopRequireWildcard","isPopoverConfigured","Map","showPopover","target","dateEle","jQuery","popover","trigger","placement","html","content","source","find","elements","dateContent","length","temptContent","clone","dateContainer","window","getComputedStyle","querySelector","display","addEventListener","hidePopover","e","closest","isTargetActive","contains","document","activeElement","isTargetHover","matches","removeEventListener","listenersRegistered","registerEventListeners","showPopoverHandler","preventDefault"],"mappings":"wIA0B6D,IAAAA;;;;;;;;KAAA,SAAAC,yBAAAC,aAAA,GAAA,mBAAAC,QAAA,OAAA,KAAA,IAAAC,kBAAAD,IAAAA,QAAAE,iBAAAF,IAAAA,eAAAF,yBAAA,SAAAC,aAAAA,OAAAA,YAAAG,iBAAAD,oBAAAF,YAAA,CAD7DI,SAC6DN,IAD7DM,UAC6DN,IAAAO,WAAAP,IAAAQ,CAAAA,QAAAR,KAA7DS,kBAA6D,SAAAT,IAAAE,aAAAA,IAAAA,aAAAF,KAAAA,IAAAO,WAAAP,OAAAA,IAAAA,GAAAA,OAAAA,KAAAA,iBAAAA,KAAAQ,mBAAAR,IAAAQ,MAAAA,CAAAA,QAAAR,KAAAU,IAAAA,MAAAT,yBAAAC,aAAA,GAAAQ,OAAAA,MAAAC,IAAAX,KAAA,OAAAU,MAAAE,IAAAZ,KAAA,IAAAa,OAAAC,GAAAA,sBAAAC,OAAAC,gBAAAD,OAAAE,yBAAAC,IAAAA,IAAAA,OAAAlB,IAAAkB,eAAAA,KAAAH,OAAAI,UAAAC,eAAAC,KAAArB,IAAAkB,KAAA,CAAA,IAAAI,KAAAR,sBAAAC,OAAAE,yBAAAjB,IAAAkB,KAAAI,KAAAA,OAAAA,KAAAV,KAAAU,KAAAC,KAAAR,OAAAC,eAAAH,OAAAK,IAAAI,MAAAT,OAAAK,KAAAlB,IAAAkB,IAAAL,CAAAA,OAAAL,QAAAR,IAAAU,OAAAA,MAAAa,IAAAvB,IAAAa,eAAAA,MAAA,CAA7DW,CAAAf,mBAOA,MAIMgB,oBAAsB,IAAIC,IAE1BC,YAAcC,SAChB,IAAKH,oBAAoBd,IAAIiB,QAAS,CAClC,MAAMC,SAAU,EAAAC,QAAMtB,SAACoB,QACvBC,QAAQE,QAAQ,CACZC,QAAS,SACTC,UAAW,MACXC,MAAM,EACNC,QAASA,KACL,MAAMC,OAASP,QAAQQ,KAAK5B,kBAAkB6B,SAASC,aACjDJ,SAAU,EAAAL,QAAMtB,SAAC,SACvB,GAAI4B,OAAOI,OAAQ,CACf,MAAMC,aAAeL,OAAOC,KAAK,WAAWK,OAAM,GAClDP,QAAQD,KAAKO,aAAaP,OAC9B,CACA,OAAOC,QAAQD,MAAM,IAI7BT,oBAAoBF,IAAIK,QAAQ,EACpC,CAzBwBe,gCA2BDf,OA1ByF,SAAzGgB,OAAOC,iBAAiBF,cAAcG,cAAcrC,kBAAkB6B,SAASC,cAAcQ,WA2BhG,EAAAjB,QAAAA,SAAOF,QAAQG,QAAQ,QACvBH,OAAOoB,iBAAiB,aAAcC,aACtCrB,OAAOoB,iBAAiB,WAAYC,aACxC,EAGEA,YAAcC,IAChB,MAAMtB,OAASsB,EAAEtB,OACXe,cAAgBO,EAAEtB,OAAOuB,QAAQ1C,kBAAkB6B,SAASK,eAClE,GAAKA,eAGDlB,oBAAoBd,IAAIgC,eAAgB,CACxC,MAAMS,eAAiBxB,OAAOyB,SAASC,SAASC,eAC1CC,cAAgB5B,OAAO6B,QAAQ,UAChCL,gBAAmBI,iBACpB,EAAA1B,QAAAA,SAAOa,eAAeZ,QAAQ,QAC9BY,cAAce,oBAAoB,aAAcT,aAChDN,cAAce,oBAAoB,WAAYT,aAEtD,GAqBJ,IAAIU,qBAAsB,EACrBA,sBAhB0BC,MAC3B,MAAMC,mBAAsBX,IACxB,MAAMP,cAAgBO,EAAEtB,OAAOuB,QAAQ1C,kBAAkB6B,SAASK,eAC7DA,gBAILO,EAAEY,iBACFnC,YAAYgB,eAAc,EAG9BW,SAASN,iBAAiB,YAAaa,oBACvCP,SAASN,iBAAiB,UAAWa,mBAAmB,EAKxDD,GACAD,qBAAsB,EACzB"}
\ No newline at end of file
diff --git a/calendar/amd/build/repository.min.js b/calendar/amd/build/repository.min.js
index 4e890eaf9b8..22d60e49418 100644
--- a/calendar/amd/build/repository.min.js
+++ b/calendar/amd/build/repository.min.js
@@ -5,6 +5,6 @@ define("core_calendar/repository",["exports","core/ajax"],(function(_exports,_aj
* @module core_calendar/repository
* @copyright 2017 Simey Lameze
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.updateEventStartDay=_exports.submitCreateUpdateForm=_exports.getEventById=_exports.getCourseGroupsData=_exports.getCalendarUpcomingData=_exports.getCalendarMonthData=_exports.getCalendarDayData=_exports.deleteSubscription=_exports.deleteEvent=void 0,_ajax=(obj=_ajax)&&obj.__esModule?obj:{default:obj};_exports.deleteEvent=function(eventId){let deleteSeries=arguments.length>1&&void 0!==arguments[1]&&arguments[1];const request={methodname:"core_calendar_delete_calendar_events",args:{events:[{eventid:eventId,repeat:deleteSeries}]}};return _ajax.default.call([request])[0]};_exports.getEventById=eventId=>{const request={methodname:"core_calendar_get_calendar_event_by_id",args:{eventid:eventId}};return _ajax.default.call([request])[0]};_exports.submitCreateUpdateForm=formData=>{const request={methodname:"core_calendar_submit_create_update_form",args:{formdata:formData}};return _ajax.default.call([request])[0]};_exports.getCalendarMonthData=function(year,month,courseId,categoryId,includeNavigation,mini){let day=arguments.length>6&&void 0!==arguments[6]?arguments[6]:1,view=arguments.length>7&&void 0!==arguments[7]?arguments[7]:"month";const request={methodname:"core_calendar_get_calendar_monthly_view",args:{year:year,month:month,courseid:courseId,categoryid:categoryId,includenavigation:includeNavigation,mini:mini,day:day,view:view}};return _ajax.default.call([request])[0]};_exports.getCalendarDayData=(year,month,day,courseId,categoryId)=>{const request={methodname:"core_calendar_get_calendar_day_view",args:{year:year,month:month,day:day,courseid:courseId,categoryid:categoryId}};return _ajax.default.call([request])[0]};_exports.updateEventStartDay=(eventId,dayTimestamp)=>{const request={methodname:"core_calendar_update_event_start_day",args:{eventid:eventId,daytimestamp:dayTimestamp}};return _ajax.default.call([request])[0]};_exports.getCalendarUpcomingData=(courseId,categoryId)=>{const request={methodname:"core_calendar_get_calendar_upcoming_view",args:{courseid:courseId,categoryid:categoryId}};return _ajax.default.call([request])[0]};_exports.getCourseGroupsData=courseId=>{const request={methodname:"core_group_get_course_groups",args:{courseid:courseId}};return _ajax.default.call([request])[0]};_exports.deleteSubscription=subscriptionId=>{const request={methodname:"core_calendar_delete_subscription",args:{subscriptionid:subscriptionId}};return _ajax.default.call([request])[0]}}));
+ */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.updateEventStartDay=_exports.submitCreateUpdateForm=_exports.getEventById=_exports.getCourseGroupsData=_exports.getCalendarUpcomingData=_exports.getCalendarMonthData=_exports.getCalendarDayData=_exports.deleteSubscription=_exports.deleteEvent=void 0,_ajax=(obj=_ajax)&&obj.__esModule?obj:{default:obj};_exports.deleteEvent=function(eventId){const request={methodname:"core_calendar_delete_calendar_events",args:{events:[{eventid:eventId,repeat:arguments.length>1&&void 0!==arguments[1]&&arguments[1]}]}};return _ajax.default.call([request])[0]};_exports.getEventById=eventId=>{const request={methodname:"core_calendar_get_calendar_event_by_id",args:{eventid:eventId}};return _ajax.default.call([request])[0]};_exports.submitCreateUpdateForm=formData=>{const request={methodname:"core_calendar_submit_create_update_form",args:{formdata:formData}};return _ajax.default.call([request])[0]};_exports.getCalendarMonthData=function(year,month,courseId,categoryId,includeNavigation,mini){const request={methodname:"core_calendar_get_calendar_monthly_view",args:{year:year,month:month,courseid:courseId,categoryid:categoryId,includenavigation:includeNavigation,mini:mini,day:arguments.length>6&&void 0!==arguments[6]?arguments[6]:1,view:arguments.length>7&&void 0!==arguments[7]?arguments[7]:"month"}};return _ajax.default.call([request])[0]};_exports.getCalendarDayData=(year,month,day,courseId,categoryId)=>{const request={methodname:"core_calendar_get_calendar_day_view",args:{year:year,month:month,day:day,courseid:courseId,categoryid:categoryId}};return _ajax.default.call([request])[0]};_exports.updateEventStartDay=(eventId,dayTimestamp)=>{const request={methodname:"core_calendar_update_event_start_day",args:{eventid:eventId,daytimestamp:dayTimestamp}};return _ajax.default.call([request])[0]};_exports.getCalendarUpcomingData=(courseId,categoryId)=>{const request={methodname:"core_calendar_get_calendar_upcoming_view",args:{courseid:courseId,categoryid:categoryId}};return _ajax.default.call([request])[0]};_exports.getCourseGroupsData=courseId=>{const request={methodname:"core_group_get_course_groups",args:{courseid:courseId}};return _ajax.default.call([request])[0]};_exports.deleteSubscription=subscriptionId=>{const request={methodname:"core_calendar_delete_subscription",args:{subscriptionid:subscriptionId}};return _ajax.default.call([request])[0]}}));
//# sourceMappingURL=repository.min.js.map
\ No newline at end of file
diff --git a/calendar/amd/build/repository.min.js.map b/calendar/amd/build/repository.min.js.map
index d20225f269a..54fcfdd1215 100644
--- a/calendar/amd/build/repository.min.js.map
+++ b/calendar/amd/build/repository.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"repository.min.js","sources":["../src/repository.js"],"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 * A javascript module to handle calendar ajax actions.\n *\n * @module core_calendar/repository\n * @copyright 2017 Simey Lameze \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport Ajax from 'core/ajax';\n\n/**\n * Delete a calendar event.\n *\n * @method deleteEvent\n * @param {number} eventId The event id.\n * @param {boolean} deleteSeries Whether to delete all events in the series\n * @return {promise} Resolved with requested calendar event\n */\nexport const deleteEvent = (eventId, deleteSeries = false) => {\n const request = {\n methodname: 'core_calendar_delete_calendar_events',\n args: {\n events: [{\n eventid: eventId,\n repeat: deleteSeries,\n }]\n }\n };\n\n return Ajax.call([request])[0];\n};\n\n/**\n * Get a calendar event by id.\n *\n * @method getEventById\n * @param {number} eventId The event id.\n * @return {promise} Resolved with requested calendar event\n */\nexport const getEventById = (eventId) => {\n\n const request = {\n methodname: 'core_calendar_get_calendar_event_by_id',\n args: {\n eventid: eventId\n }\n };\n\n return Ajax.call([request])[0];\n};\n\n/**\n * Submit the form data for the event form.\n *\n * @method submitCreateUpdateForm\n * @param {string} formData The URL encoded values from the form\n * @return {promise} Resolved with the new or edited event\n */\nexport const submitCreateUpdateForm = (formData) => {\n const request = {\n methodname: 'core_calendar_submit_create_update_form',\n args: {\n formdata: formData\n }\n };\n\n return Ajax.call([request])[0];\n};\n\n/**\n * Get calendar data for the month view.\n *\n * @method getCalendarMonthData\n * @param {number} year Year\n * @param {number} month Month\n * @param {number} courseId The course id.\n * @param {number} categoryId The category id.\n * @param {boolean} includeNavigation Whether to include navigation.\n * @param {boolean} mini Whether the month is in mini view.\n * @param {number} day Day (optional)\n * @param {string} view The calendar view mode.\n * @return {promise} Resolved with the month view data.\n */\nexport const getCalendarMonthData = (year, month, courseId, categoryId, includeNavigation, mini, day = 1, view = 'month') => {\n const request = {\n methodname: 'core_calendar_get_calendar_monthly_view',\n args: {\n year,\n month,\n courseid: courseId,\n categoryid: categoryId,\n includenavigation: includeNavigation,\n mini,\n day,\n view,\n }\n };\n\n return Ajax.call([request])[0];\n};\n\n/**\n * Get calendar data for the day view.\n *\n * @method getCalendarDayData\n * @param {number} year Year\n * @param {number} month Month\n * @param {number} day Day\n * @param {number} courseId The course id.\n * @param {number} categoryId The id of the category whose events are shown\n * @return {promise} Resolved with the day view data.\n */\nexport const getCalendarDayData = (year, month, day, courseId, categoryId) => {\n const request = {\n methodname: 'core_calendar_get_calendar_day_view',\n args: {\n year,\n month,\n day,\n courseid: courseId,\n categoryid: categoryId,\n }\n };\n\n return Ajax.call([request])[0];\n};\n\n/**\n * Change the start day for the given event id. The day timestamp\n * only has to be any time during the target day because only the\n * date information is extracted, the time of the day is ignored.\n *\n * @param {int} eventId The id of the event to update\n * @param {int} dayTimestamp A timestamp for some time during the target day\n * @return {promise}\n */\nexport const updateEventStartDay = (eventId, dayTimestamp) => {\n const request = {\n methodname: 'core_calendar_update_event_start_day',\n args: {\n eventid: eventId,\n daytimestamp: dayTimestamp\n }\n };\n\n return Ajax.call([request])[0];\n};\n\n/**\n * Get calendar upcoming data.\n *\n * @method getCalendarUpcomingData\n * @param {number} courseId The course id.\n * @param {number} categoryId The category id.\n * @return {promise} Resolved with the month view data.\n */\nexport const getCalendarUpcomingData = (courseId, categoryId) => {\n const request = {\n methodname: 'core_calendar_get_calendar_upcoming_view',\n args: {\n courseid: courseId,\n categoryid: categoryId,\n }\n };\n\n return Ajax.call([request])[0];\n};\n\n/**\n * Get the groups by course id.\n *\n * @param {Number} courseId The course id to fetch the groups from.\n * @return {promise} Resolved with the course groups.\n */\nexport const getCourseGroupsData = (courseId) => {\n const request = {\n methodname: 'core_group_get_course_groups',\n args: {\n courseid: courseId\n }\n };\n\n return Ajax.call([request])[0];\n};\n\n/**\n * Delete calendar subscription by id.\n *\n * @param {Number} subscriptionId The subscription id\n * @return {promise}\n */\nexport const deleteSubscription = (subscriptionId) => {\n const request = {\n methodname: 'core_calendar_delete_subscription',\n args: {\n subscriptionid: subscriptionId\n }\n };\n\n return Ajax.call([request])[0];\n};\n"],"names":["eventId","deleteSeries","request","methodname","args","events","eventid","repeat","Ajax","call","formData","formdata","year","month","courseId","categoryId","includeNavigation","mini","day","view","courseid","categoryid","includenavigation","dayTimestamp","daytimestamp","subscriptionId","subscriptionid"],"mappings":";;;;;;;yYAgC2B,SAACA,aAASC,2EAC3BC,QAAU,CACZC,WAAY,uCACZC,KAAM,CACFC,OAAQ,CAAC,CACLC,QAASN,QACTO,OAAQN,wBAKbO,cAAKC,KAAK,CAACP,UAAU,0BAUHF,gBAEnBE,QAAU,CACZC,WAAY,yCACZC,KAAM,CACFE,QAASN,iBAIVQ,cAAKC,KAAK,CAACP,UAAU,oCAUOQ,iBAC7BR,QAAU,CACZC,WAAY,0CACZC,KAAM,CACFO,SAAUD,kBAIXF,cAAKC,KAAK,CAACP,UAAU,kCAiBI,SAACU,KAAMC,MAAOC,SAAUC,WAAYC,kBAAmBC,UAAMC,2DAAM,EAAGC,4DAAO,cACvGjB,QAAU,CACZC,WAAY,0CACZC,KAAM,CACFQ,KAAAA,KACAC,MAAAA,MACAO,SAAUN,SACVO,WAAYN,WACZO,kBAAmBN,kBACnBC,KAAAA,KACAC,IAAAA,IACAC,KAAAA,cAIDX,cAAKC,KAAK,CAACP,UAAU,gCAcE,CAACU,KAAMC,MAAOK,IAAKJ,SAAUC,oBACrDb,QAAU,CACZC,WAAY,sCACZC,KAAM,CACFQ,KAAAA,KACAC,MAAAA,MACAK,IAAAA,IACAE,SAAUN,SACVO,WAAYN,oBAIbP,cAAKC,KAAK,CAACP,UAAU,iCAYG,CAACF,QAASuB,sBACnCrB,QAAU,CACZC,WAAY,uCACZC,KAAM,CACFE,QAASN,QACTwB,aAAcD,sBAIff,cAAKC,KAAK,CAACP,UAAU,qCAWO,CAACY,SAAUC,oBACxCb,QAAU,CACZC,WAAY,2CACZC,KAAM,CACFgB,SAAUN,SACVO,WAAYN,oBAIbP,cAAKC,KAAK,CAACP,UAAU,iCASIY,iBAC1BZ,QAAU,CACZC,WAAY,+BACZC,KAAM,CACFgB,SAAUN,kBAIXN,cAAKC,KAAK,CAACP,UAAU,gCASGuB,uBACzBvB,QAAU,CACZC,WAAY,oCACZC,KAAM,CACFsB,eAAgBD,wBAIjBjB,cAAKC,KAAK,CAACP,UAAU"}
\ No newline at end of file
+{"version":3,"file":"repository.min.js","sources":["../src/repository.js"],"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 * A javascript module to handle calendar ajax actions.\n *\n * @module core_calendar/repository\n * @copyright 2017 Simey Lameze \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport Ajax from 'core/ajax';\n\n/**\n * Delete a calendar event.\n *\n * @method deleteEvent\n * @param {number} eventId The event id.\n * @param {boolean} deleteSeries Whether to delete all events in the series\n * @return {promise} Resolved with requested calendar event\n */\nexport const deleteEvent = (eventId, deleteSeries = false) => {\n const request = {\n methodname: 'core_calendar_delete_calendar_events',\n args: {\n events: [{\n eventid: eventId,\n repeat: deleteSeries,\n }]\n }\n };\n\n return Ajax.call([request])[0];\n};\n\n/**\n * Get a calendar event by id.\n *\n * @method getEventById\n * @param {number} eventId The event id.\n * @return {promise} Resolved with requested calendar event\n */\nexport const getEventById = (eventId) => {\n\n const request = {\n methodname: 'core_calendar_get_calendar_event_by_id',\n args: {\n eventid: eventId\n }\n };\n\n return Ajax.call([request])[0];\n};\n\n/**\n * Submit the form data for the event form.\n *\n * @method submitCreateUpdateForm\n * @param {string} formData The URL encoded values from the form\n * @return {promise} Resolved with the new or edited event\n */\nexport const submitCreateUpdateForm = (formData) => {\n const request = {\n methodname: 'core_calendar_submit_create_update_form',\n args: {\n formdata: formData\n }\n };\n\n return Ajax.call([request])[0];\n};\n\n/**\n * Get calendar data for the month view.\n *\n * @method getCalendarMonthData\n * @param {number} year Year\n * @param {number} month Month\n * @param {number} courseId The course id.\n * @param {number} categoryId The category id.\n * @param {boolean} includeNavigation Whether to include navigation.\n * @param {boolean} mini Whether the month is in mini view.\n * @param {number} day Day (optional)\n * @param {string} view The calendar view mode.\n * @return {promise} Resolved with the month view data.\n */\nexport const getCalendarMonthData = (year, month, courseId, categoryId, includeNavigation, mini, day = 1, view = 'month') => {\n const request = {\n methodname: 'core_calendar_get_calendar_monthly_view',\n args: {\n year,\n month,\n courseid: courseId,\n categoryid: categoryId,\n includenavigation: includeNavigation,\n mini,\n day,\n view,\n }\n };\n\n return Ajax.call([request])[0];\n};\n\n/**\n * Get calendar data for the day view.\n *\n * @method getCalendarDayData\n * @param {number} year Year\n * @param {number} month Month\n * @param {number} day Day\n * @param {number} courseId The course id.\n * @param {number} categoryId The id of the category whose events are shown\n * @return {promise} Resolved with the day view data.\n */\nexport const getCalendarDayData = (year, month, day, courseId, categoryId) => {\n const request = {\n methodname: 'core_calendar_get_calendar_day_view',\n args: {\n year,\n month,\n day,\n courseid: courseId,\n categoryid: categoryId,\n }\n };\n\n return Ajax.call([request])[0];\n};\n\n/**\n * Change the start day for the given event id. The day timestamp\n * only has to be any time during the target day because only the\n * date information is extracted, the time of the day is ignored.\n *\n * @param {int} eventId The id of the event to update\n * @param {int} dayTimestamp A timestamp for some time during the target day\n * @return {promise}\n */\nexport const updateEventStartDay = (eventId, dayTimestamp) => {\n const request = {\n methodname: 'core_calendar_update_event_start_day',\n args: {\n eventid: eventId,\n daytimestamp: dayTimestamp\n }\n };\n\n return Ajax.call([request])[0];\n};\n\n/**\n * Get calendar upcoming data.\n *\n * @method getCalendarUpcomingData\n * @param {number} courseId The course id.\n * @param {number} categoryId The category id.\n * @return {promise} Resolved with the month view data.\n */\nexport const getCalendarUpcomingData = (courseId, categoryId) => {\n const request = {\n methodname: 'core_calendar_get_calendar_upcoming_view',\n args: {\n courseid: courseId,\n categoryid: categoryId,\n }\n };\n\n return Ajax.call([request])[0];\n};\n\n/**\n * Get the groups by course id.\n *\n * @param {Number} courseId The course id to fetch the groups from.\n * @return {promise} Resolved with the course groups.\n */\nexport const getCourseGroupsData = (courseId) => {\n const request = {\n methodname: 'core_group_get_course_groups',\n args: {\n courseid: courseId\n }\n };\n\n return Ajax.call([request])[0];\n};\n\n/**\n * Delete calendar subscription by id.\n *\n * @param {Number} subscriptionId The subscription id\n * @return {promise}\n */\nexport const deleteSubscription = (subscriptionId) => {\n const request = {\n methodname: 'core_calendar_delete_subscription',\n args: {\n subscriptionid: subscriptionId\n }\n };\n\n return Ajax.call([request])[0];\n};\n"],"names":["obj","_ajax","__esModule","default","_exports","deleteEvent","eventId","request","methodname","args","events","eventid","repeat","arguments","length","undefined","Ajax","call","getEventById","submitCreateUpdateForm","formData","formdata","getCalendarMonthData","year","month","courseId","categoryId","includeNavigation","mini","courseid","categoryid","includenavigation","day","view","getCalendarDayData","updateEventStartDay","dayTimestamp","daytimestamp","getCalendarUpcomingData","getCourseGroupsData","deleteSubscription","subscriptionId","subscriptionid"],"mappings":"oFAsB6B,IAAAA;;;;;;;gUAA7BC,OAA6BD,IAA7BC,QAA6BD,IAAAE,WAAAF,IAAAG,CAAAA,QAAAH,KAsB3BI,SAAAC,YAZyB,SAACC,SACxB,MAAMC,QAAU,CACZC,WAAY,uCACZC,KAAM,CACFC,OAAQ,CAAC,CACLC,QAASL,QACTM,OANiCC,UAAAC,OAAA,QAAAC,IAAAF,UAAA,IAAAA,UAAA,OAW7C,OAAOG,MAAAA,QAAKC,KAAK,CAACV,UAAU,IAoB9BH,SAAAc,aAV2BZ,UAEzB,MAAMC,QAAU,CACZC,WAAY,yCACZC,KAAM,CACFE,QAASL,UAIjB,OAAOU,MAAAA,QAAKC,KAAK,CAACV,UAAU,EAAE,EAmBhCH,SAAAe,uBATqCC,WACnC,MAAMb,QAAU,CACZC,WAAY,0CACZC,KAAM,CACFY,SAAUD,WAIlB,OAAOJ,MAAAA,QAAKC,KAAK,CAACV,UAAU,EAAE,EAiChCH,SAAAkB,qBAhBkC,SAACC,KAAMC,MAAOC,SAAUC,WAAYC,kBAAmBC,MACvF,MAAMrB,QAAU,CACZC,WAAY,0CACZC,KAAM,CACFc,UACAC,YACAK,SAAUJ,SACVK,WAAYJ,WACZK,kBAAmBJ,kBACnBC,UACAI,IAVwFnB,UAAAC,OAAA,QAAAC,IAAAF,UAAA,GAAAA,UAAA,GAAG,EAW3FoB,KAXkGpB,UAAAC,OAAA,QAAAC,IAAAF,UAAA,GAAAA,UAAA,GAAG,UAe7G,OAAOG,MAAAA,QAAKC,KAAK,CAACV,UAAU,IA2B9BH,SAAA8B,mBAbgCA,CAACX,KAAMC,MAAOQ,IAAKP,SAAUC,cAC3D,MAAMnB,QAAU,CACZC,WAAY,sCACZC,KAAM,CACFc,UACAC,YACAQ,QACAH,SAAUJ,SACVK,WAAYJ,aAIpB,OAAOV,MAAAA,QAAKC,KAAK,CAACV,UAAU,EAAE,EAsBhCH,SAAA+B,oBAViCA,CAAC7B,QAAS8B,gBACzC,MAAM7B,QAAU,CACZC,WAAY,uCACZC,KAAM,CACFE,QAASL,QACT+B,aAAcD,eAItB,OAAOpB,MAAAA,QAAKC,KAAK,CAACV,UAAU,EAAE,EAqBhCH,SAAAkC,wBAVqCA,CAACb,SAAUC,cAC9C,MAAMnB,QAAU,CACZC,WAAY,2CACZC,KAAM,CACFoB,SAAUJ,SACVK,WAAYJ,aAIpB,OAAOV,MAAAA,QAAKC,KAAK,CAACV,UAAU,EAAE,EAkBhCH,SAAAmC,oBATkCd,WAChC,MAAMlB,QAAU,CACZC,WAAY,+BACZC,KAAM,CACFoB,SAAUJ,WAIlB,OAAOT,MAAAA,QAAKC,KAAK,CAACV,UAAU,EAAE,EAkBhCH,SAAAoC,mBATiCC,iBAC/B,MAAMlC,QAAU,CACZC,WAAY,oCACZC,KAAM,CACFiC,eAAgBD,iBAIxB,OAAOzB,MAAAA,QAAKC,KAAK,CAACV,UAAU,EAAE,CAChC"}
\ No newline at end of file
diff --git a/calendar/amd/build/selectors.min.js.map b/calendar/amd/build/selectors.min.js.map
index 6a3984a1cfc..a5a471e4a71 100644
--- a/calendar/amd/build/selectors.min.js.map
+++ b/calendar/amd/build/selectors.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"selectors.min.js","sources":["../src/selectors.js"],"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 * This module is responsible for the calendar filter.\n *\n * @module core_calendar/calendar_selectors\n * @copyright 2017 Andrew Nicols \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine([], function() {\n return {\n eventFilterItem: \"[data-action='filter-event-type']\",\n eventType: {\n site: \"[data-eventtype-site]\",\n category: \"[data-eventtype-category]\",\n course: \"[data-eventtype-course]\",\n group: \"[data-eventtype-group]\",\n user: \"[data-eventtype-user]\",\n other: \"[data-eventtype-other]\",\n },\n popoverType: {\n site: \"[data-popover-eventtype-site]\",\n category: \"[data-popover-eventtype-category]\",\n course: \"[data-popover-eventtype-course]\",\n group: \"[data-popover-eventtype-group]\",\n user: \"[data-popover-eventtype-user]\",\n other: \"[data-popover-eventtype-other]\",\n },\n calendarPeriods: {\n month: \"[data-period='month']\",\n },\n courseSelector: 'select[name=\"course\"]',\n viewSelector: 'div[data-region=\"view-selector\"]',\n actions: {\n create: '[data-action=\"new-event-button\"]',\n edit: '[data-action=\"edit\"]',\n remove: '[data-action=\"delete\"]',\n viewEvent: '[data-action=\"view-event\"]',\n deleteSubscription: '[data-action=\"delete-subscription\"]',\n },\n elements: {\n courseSelector: 'select[name=\"course\"]',\n dateContainer: '.clickable.hasevent',\n dateContent: '[data-region=\"day-content\"]',\n monthDetailed: '.calendarmonth.calendartable',\n },\n today: '.today',\n day: '[data-region=\"day\"]',\n calendarMain: '[data-region=\"calendar\"]',\n wrapper: '.calendarwrapper',\n eventItem: '[data-type=\"event\"]',\n links: {\n navLink: '.calendarwrapper .arrow_link',\n eventLink: \"[data-region='event-item']\",\n miniDayLink: \"[data-region='mini-day-link']\",\n },\n containers: {\n loadingIcon: '[data-region=\"overlay-icon-container\"]',\n },\n mainCalendar: '.maincalendar .heightcontainer',\n fullCalendarView: 'page-calendar-view',\n };\n});\n"],"names":["define","eventFilterItem","eventType","site","category","course","group","user","other","popoverType","calendarPeriods","month","courseSelector","viewSelector","actions","create","edit","remove","viewEvent","deleteSubscription","elements","dateContainer","dateContent","monthDetailed","today","day","calendarMain","wrapper","eventItem","links","navLink","eventLink","miniDayLink","containers","loadingIcon","mainCalendar","fullCalendarView"],"mappings":";;;;;;;AAsBAA,iCAAO,IAAI,iBACA,CACHC,gBAAiB,oCACjBC,UAAW,CACPC,KAAM,wBACNC,SAAU,4BACVC,OAAQ,0BACRC,MAAO,yBACPC,KAAM,wBACNC,MAAO,0BAEXC,YAAa,CACTN,KAAM,gCACNC,SAAU,oCACVC,OAAQ,kCACRC,MAAO,iCACPC,KAAM,gCACNC,MAAO,kCAEXE,gBAAiB,CACbC,MAAO,yBAEXC,eAAgB,wBAChBC,aAAc,mCACdC,QAAS,CACLC,OAAQ,mCACRC,KAAM,uBACNC,OAAQ,yBACRC,UAAW,6BACXC,mBAAoB,uCAExBC,SAAU,CACNR,eAAgB,wBAChBS,cAAe,sBACfC,YAAa,8BACbC,cAAe,gCAEnBC,MAAO,SACPC,IAAK,sBACLC,aAAc,2BACdC,QAAS,mBACTC,UAAW,sBACXC,MAAO,CACHC,QAAS,+BACTC,UAAW,6BACXC,YAAa,iCAEjBC,WAAY,CACRC,YAAa,0CAEjBC,aAAc,iCACdC,iBAAkB"}
\ No newline at end of file
+{"version":3,"file":"selectors.min.js","sources":["../src/selectors.js"],"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 * This module is responsible for the calendar filter.\n *\n * @module core_calendar/calendar_selectors\n * @copyright 2017 Andrew Nicols \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine([], function() {\n return {\n eventFilterItem: \"[data-action='filter-event-type']\",\n eventType: {\n site: \"[data-eventtype-site]\",\n category: \"[data-eventtype-category]\",\n course: \"[data-eventtype-course]\",\n group: \"[data-eventtype-group]\",\n user: \"[data-eventtype-user]\",\n other: \"[data-eventtype-other]\",\n },\n popoverType: {\n site: \"[data-popover-eventtype-site]\",\n category: \"[data-popover-eventtype-category]\",\n course: \"[data-popover-eventtype-course]\",\n group: \"[data-popover-eventtype-group]\",\n user: \"[data-popover-eventtype-user]\",\n other: \"[data-popover-eventtype-other]\",\n },\n calendarPeriods: {\n month: \"[data-period='month']\",\n },\n courseSelector: 'select[name=\"course\"]',\n viewSelector: 'div[data-region=\"view-selector\"]',\n actions: {\n create: '[data-action=\"new-event-button\"]',\n edit: '[data-action=\"edit\"]',\n remove: '[data-action=\"delete\"]',\n viewEvent: '[data-action=\"view-event\"]',\n deleteSubscription: '[data-action=\"delete-subscription\"]',\n },\n elements: {\n courseSelector: 'select[name=\"course\"]',\n dateContainer: '.clickable.hasevent',\n dateContent: '[data-region=\"day-content\"]',\n monthDetailed: '.calendarmonth.calendartable',\n },\n today: '.today',\n day: '[data-region=\"day\"]',\n calendarMain: '[data-region=\"calendar\"]',\n wrapper: '.calendarwrapper',\n eventItem: '[data-type=\"event\"]',\n links: {\n navLink: '.calendarwrapper .arrow_link',\n eventLink: \"[data-region='event-item']\",\n miniDayLink: \"[data-region='mini-day-link']\",\n },\n containers: {\n loadingIcon: '[data-region=\"overlay-icon-container\"]',\n },\n mainCalendar: '.maincalendar .heightcontainer',\n fullCalendarView: 'page-calendar-view',\n };\n});\n"],"names":["define","eventFilterItem","eventType","site","category","course","group","user","other","popoverType","calendarPeriods","month","courseSelector","viewSelector","actions","create","edit","remove","viewEvent","deleteSubscription","elements","dateContainer","dateContent","monthDetailed","today","day","calendarMain","wrapper","eventItem","links","navLink","eventLink","miniDayLink","containers","loadingIcon","mainCalendar","fullCalendarView"],"mappings":";;;;;;;AAsBAA,OAAM,0BAAC,IAAI,WACP,MAAO,CACHC,gBAAiB,oCACjBC,UAAW,CACPC,KAAM,wBACNC,SAAU,4BACVC,OAAQ,0BACRC,MAAO,yBACPC,KAAM,wBACNC,MAAO,0BAEXC,YAAa,CACTN,KAAM,gCACNC,SAAU,oCACVC,OAAQ,kCACRC,MAAO,iCACPC,KAAM,gCACNC,MAAO,kCAEXE,gBAAiB,CACbC,MAAO,yBAEXC,eAAgB,wBAChBC,aAAc,mCACdC,QAAS,CACLC,OAAQ,mCACRC,KAAM,uBACNC,OAAQ,yBACRC,UAAW,6BACXC,mBAAoB,uCAExBC,SAAU,CACNR,eAAgB,wBAChBS,cAAe,sBACfC,YAAa,8BACbC,cAAe,gCAEnBC,MAAO,SACPC,IAAK,sBACLC,aAAc,2BACdC,QAAS,mBACTC,UAAW,sBACXC,MAAO,CACHC,QAAS,+BACTC,UAAW,6BACXC,YAAa,iCAEjBC,WAAY,CACRC,YAAa,0CAEjBC,aAAc,iCACdC,iBAAkB,qBAE1B"}
\ No newline at end of file
diff --git a/calendar/amd/build/summary_modal.min.js.map b/calendar/amd/build/summary_modal.min.js.map
index 423c4ada941..dc33ca742d9 100644
--- a/calendar/amd/build/summary_modal.min.js.map
+++ b/calendar/amd/build/summary_modal.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"summary_modal.min.js","sources":["../src/summary_modal.js"],"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 * A javascript module to handle summary modal.\n *\n * @module core_calendar/summary_modal\n * @copyright 2017 Simey Lameze \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine([\n 'jquery',\n 'core/str',\n 'core/notification',\n 'core/custom_interaction_events',\n 'core/modal',\n 'core/modal_registry',\n 'core/modal_factory',\n 'core/modal_events',\n 'core_calendar/repository',\n 'core_calendar/events',\n 'core_calendar/crud',\n],\nfunction(\n $,\n Str,\n Notification,\n CustomEvents,\n Modal,\n ModalRegistry,\n ModalFactory,\n ModalEvents,\n CalendarRepository,\n CalendarEvents,\n CalendarCrud\n) {\n\n var registered = false;\n var SELECTORS = {\n ROOT: \"[data-region='summary-modal-container']\",\n EDIT_BUTTON: '[data-action=\"edit\"]',\n DELETE_BUTTON: '[data-action=\"delete\"]',\n };\n\n /**\n * Constructor for the Modal.\n *\n * @param {object} root The root jQuery element for the modal\n */\n var ModalEventSummary = function(root) {\n Modal.call(this, root);\n };\n\n ModalEventSummary.TYPE = 'core_calendar-event_summary';\n ModalEventSummary.prototype = Object.create(Modal.prototype);\n ModalEventSummary.prototype.constructor = ModalEventSummary;\n\n /**\n * Get the edit button element from the footer. The button is cached\n * as it's not expected to change.\n *\n * @method getEditButton\n * @return {object} button element\n */\n ModalEventSummary.prototype.getEditButton = function() {\n if (typeof this.editButton == 'undefined') {\n this.editButton = this.getFooter().find(SELECTORS.EDIT_BUTTON);\n }\n\n return this.editButton;\n };\n\n /**\n * Get the delete button element from the footer. The button is cached\n * as it's not expected to change.\n *\n * @method getDeleteButton\n * @return {object} button element\n */\n ModalEventSummary.prototype.getDeleteButton = function() {\n if (typeof this.deleteButton == 'undefined') {\n this.deleteButton = this.getFooter().find(SELECTORS.DELETE_BUTTON);\n }\n\n return this.deleteButton;\n };\n\n /**\n * Get the id for the event being shown in this modal. This value is\n * not cached because it will change depending on which event is\n * being displayed.\n *\n * @method getEventId\n * @return {int}\n */\n ModalEventSummary.prototype.getEventId = function() {\n return this.getBody().find(SELECTORS.ROOT).attr('data-event-id');\n };\n\n /**\n * Get the title for the event being shown in this modal. This value is\n * not cached because it will change depending on which event is\n * being displayed.\n *\n * @method getEventTitle\n * @return {String}\n */\n ModalEventSummary.prototype.getEventTitle = function() {\n return this.getBody().find(SELECTORS.ROOT).attr('data-event-title');\n };\n\n /**\n * Get the number of events in the series for the event being shown in\n * this modal. This value is not cached because it will change\n * depending on which event is being displayed.\n *\n * @method getEventCount\n * @return {int}\n */\n ModalEventSummary.prototype.getEventCount = function() {\n return this.getBody().find(SELECTORS.ROOT).attr('data-event-count');\n };\n\n /**\n * Get the url for the event being shown in this modal.\n *\n * @method getEventUrl\n * @return {String}\n */\n ModalEventSummary.prototype.getEditUrl = function() {\n return this.getBody().find(SELECTORS.ROOT).attr('data-edit-url');\n };\n\n /**\n * Is this an action event.\n *\n * @method getEventUrl\n * @return {String}\n */\n ModalEventSummary.prototype.isActionEvent = function() {\n return (this.getBody().find(SELECTORS.ROOT).attr('data-action-event') == 'true');\n };\n\n /**\n * Set up all of the event handling for the modal.\n *\n * @method registerEventListeners\n */\n ModalEventSummary.prototype.registerEventListeners = function() {\n // Apply parent event listeners.\n Modal.prototype.registerEventListeners.call(this);\n\n // We have to wait for the modal to finish rendering in order to ensure that\n // the data-event-title property is available to use as the modal title.\n M.util.js_pending('core_calendar/summary_modal:registerEventListeners:bodyRendered');\n this.getRoot().on(ModalEvents.bodyRendered, function() {\n this.getModal().data({\n eventTitle: this.getEventTitle(),\n eventId: this.getEventId(),\n eventCount: this.getEventCount(),\n })\n .attr('data-type', 'event');\n CalendarCrud.registerRemove(this.getModal());\n M.util.js_complete('core_calendar/summary_modal:registerEventListeners:bodyRendered');\n }.bind(this));\n\n $('body').on(CalendarEvents.deleted, function() {\n // Close the dialogue on delete.\n this.hide();\n }.bind(this));\n\n CustomEvents.define(this.getEditButton(), [\n CustomEvents.events.activate\n ]);\n\n this.getEditButton().on(CustomEvents.events.activate, function(e, data) {\n if (this.isActionEvent()) {\n // Action events cannot be edited on the event form and must be redirected to the module UI.\n $('body').trigger(CalendarEvents.editActionEvent, [this.getEditUrl()]);\n } else {\n // When the edit button is clicked we fire an event for the calendar UI to handle.\n // We don't care how the UI chooses to handle it.\n $('body').trigger(CalendarEvents.editEvent, [this.getEventId()]);\n }\n\n // There is nothing else for us to do so let's hide.\n this.hide();\n\n // We've handled this event so no need to propagate it.\n e.preventDefault();\n e.stopPropagation();\n data.originalEvent.preventDefault();\n data.originalEvent.stopPropagation();\n }.bind(this));\n };\n\n // Automatically register with the modal registry the first time this module is imported so that you can create modals\n // of this type using the modal factory.\n if (!registered) {\n ModalRegistry.register(ModalEventSummary.TYPE, ModalEventSummary, 'core_calendar/event_summary_modal');\n registered = true;\n }\n\n return ModalEventSummary;\n});\n"],"names":["define","$","Str","Notification","CustomEvents","Modal","ModalRegistry","ModalFactory","ModalEvents","CalendarRepository","CalendarEvents","CalendarCrud","registered","SELECTORS","ModalEventSummary","root","call","this","TYPE","prototype","Object","create","constructor","getEditButton","editButton","getFooter","find","getDeleteButton","deleteButton","getEventId","getBody","attr","getEventTitle","getEventCount","getEditUrl","isActionEvent","registerEventListeners","M","util","js_pending","getRoot","on","bodyRendered","getModal","data","eventTitle","eventId","eventCount","registerRemove","js_complete","bind","deleted","hide","events","activate","e","trigger","editActionEvent","editEvent","preventDefault","stopPropagation","originalEvent","register"],"mappings":";;;;;;;AAsBAA,qCAAO,CACH,SACA,WACA,oBACA,iCACA,aACA,sBACA,qBACA,oBACA,2BACA,uBACA,uBAEJ,SACIC,EACAC,IACAC,aACAC,aACAC,MACAC,cACAC,aACAC,YACAC,mBACAC,eACAC,kBAGIC,YAAa,EACbC,eACM,0CADNA,sBAEa,uBAFbA,wBAGe,yBAQfC,kBAAoB,SAASC,MAC7BV,MAAMW,KAAKC,KAAMF,cAGrBD,kBAAkBI,KAAO,+BACzBJ,kBAAkBK,UAAYC,OAAOC,OAAOhB,MAAMc,YACtBG,YAAcR,kBAS1CA,kBAAkBK,UAAUI,cAAgB,uBACV,IAAnBN,KAAKO,kBACPA,WAAaP,KAAKQ,YAAYC,KAAKb,wBAGrCI,KAAKO,YAUhBV,kBAAkBK,UAAUQ,gBAAkB,uBACV,IAArBV,KAAKW,oBACPA,aAAeX,KAAKQ,YAAYC,KAAKb,0BAGvCI,KAAKW,cAWhBd,kBAAkBK,UAAUU,WAAa,kBAC9BZ,KAAKa,UAAUJ,KAAKb,gBAAgBkB,KAAK,kBAWpDjB,kBAAkBK,UAAUa,cAAgB,kBACjCf,KAAKa,UAAUJ,KAAKb,gBAAgBkB,KAAK,qBAWpDjB,kBAAkBK,UAAUc,cAAgB,kBACjChB,KAAKa,UAAUJ,KAAKb,gBAAgBkB,KAAK,qBASpDjB,kBAAkBK,UAAUe,WAAa,kBAC9BjB,KAAKa,UAAUJ,KAAKb,gBAAgBkB,KAAK,kBASpDjB,kBAAkBK,UAAUgB,cAAgB,iBACiC,QAAjElB,KAAKa,UAAUJ,KAAKb,gBAAgBkB,KAAK,sBAQrDjB,kBAAkBK,UAAUiB,uBAAyB,WAEjD/B,MAAMc,UAAUiB,uBAAuBpB,KAAKC,MAI5CoB,EAAEC,KAAKC,WAAW,wEACbC,UAAUC,GAAGjC,YAAYkC,aAAc,gBACnCC,WAAWC,KAAK,CACjBC,WAAY5B,KAAKe,gBACjBc,QAAS7B,KAAKY,aACdkB,WAAY9B,KAAKgB,kBAEpBF,KAAK,YAAa,SACnBpB,aAAaqC,eAAe/B,KAAK0B,YACjCN,EAAEC,KAAKW,YAAY,oEACrBC,KAAKjC,OAEPhB,EAAE,QAAQwC,GAAG/B,eAAeyC,QAAS,gBAE5BC,QACPF,KAAKjC,OAEPb,aAAaJ,OAAOiB,KAAKM,gBAAiB,CACtCnB,aAAaiD,OAAOC,gBAGnB/B,gBAAgBkB,GAAGrC,aAAaiD,OAAOC,SAAU,SAASC,EAAGX,MAC1D3B,KAAKkB,gBAELlC,EAAE,QAAQuD,QAAQ9C,eAAe+C,gBAAiB,CAACxC,KAAKiB,eAIxDjC,EAAE,QAAQuD,QAAQ9C,eAAegD,UAAW,CAACzC,KAAKY,oBAIjDuB,OAGLG,EAAEI,iBACFJ,EAAEK,kBACFhB,KAAKiB,cAAcF,iBACnBf,KAAKiB,cAAcD,mBACrBV,KAAKjC,QAKNL,aACDN,cAAcwD,SAAShD,kBAAkBI,KAAMJ,kBAAmB,qCAClEF,YAAa,GAGVE"}
\ No newline at end of file
+{"version":3,"file":"summary_modal.min.js","sources":["../src/summary_modal.js"],"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 * A javascript module to handle summary modal.\n *\n * @module core_calendar/summary_modal\n * @copyright 2017 Simey Lameze \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine([\n 'jquery',\n 'core/str',\n 'core/notification',\n 'core/custom_interaction_events',\n 'core/modal',\n 'core/modal_registry',\n 'core/modal_factory',\n 'core/modal_events',\n 'core_calendar/repository',\n 'core_calendar/events',\n 'core_calendar/crud',\n],\nfunction(\n $,\n Str,\n Notification,\n CustomEvents,\n Modal,\n ModalRegistry,\n ModalFactory,\n ModalEvents,\n CalendarRepository,\n CalendarEvents,\n CalendarCrud\n) {\n\n var registered = false;\n var SELECTORS = {\n ROOT: \"[data-region='summary-modal-container']\",\n EDIT_BUTTON: '[data-action=\"edit\"]',\n DELETE_BUTTON: '[data-action=\"delete\"]',\n };\n\n /**\n * Constructor for the Modal.\n *\n * @param {object} root The root jQuery element for the modal\n */\n var ModalEventSummary = function(root) {\n Modal.call(this, root);\n };\n\n ModalEventSummary.TYPE = 'core_calendar-event_summary';\n ModalEventSummary.prototype = Object.create(Modal.prototype);\n ModalEventSummary.prototype.constructor = ModalEventSummary;\n\n /**\n * Get the edit button element from the footer. The button is cached\n * as it's not expected to change.\n *\n * @method getEditButton\n * @return {object} button element\n */\n ModalEventSummary.prototype.getEditButton = function() {\n if (typeof this.editButton == 'undefined') {\n this.editButton = this.getFooter().find(SELECTORS.EDIT_BUTTON);\n }\n\n return this.editButton;\n };\n\n /**\n * Get the delete button element from the footer. The button is cached\n * as it's not expected to change.\n *\n * @method getDeleteButton\n * @return {object} button element\n */\n ModalEventSummary.prototype.getDeleteButton = function() {\n if (typeof this.deleteButton == 'undefined') {\n this.deleteButton = this.getFooter().find(SELECTORS.DELETE_BUTTON);\n }\n\n return this.deleteButton;\n };\n\n /**\n * Get the id for the event being shown in this modal. This value is\n * not cached because it will change depending on which event is\n * being displayed.\n *\n * @method getEventId\n * @return {int}\n */\n ModalEventSummary.prototype.getEventId = function() {\n return this.getBody().find(SELECTORS.ROOT).attr('data-event-id');\n };\n\n /**\n * Get the title for the event being shown in this modal. This value is\n * not cached because it will change depending on which event is\n * being displayed.\n *\n * @method getEventTitle\n * @return {String}\n */\n ModalEventSummary.prototype.getEventTitle = function() {\n return this.getBody().find(SELECTORS.ROOT).attr('data-event-title');\n };\n\n /**\n * Get the number of events in the series for the event being shown in\n * this modal. This value is not cached because it will change\n * depending on which event is being displayed.\n *\n * @method getEventCount\n * @return {int}\n */\n ModalEventSummary.prototype.getEventCount = function() {\n return this.getBody().find(SELECTORS.ROOT).attr('data-event-count');\n };\n\n /**\n * Get the url for the event being shown in this modal.\n *\n * @method getEventUrl\n * @return {String}\n */\n ModalEventSummary.prototype.getEditUrl = function() {\n return this.getBody().find(SELECTORS.ROOT).attr('data-edit-url');\n };\n\n /**\n * Is this an action event.\n *\n * @method getEventUrl\n * @return {String}\n */\n ModalEventSummary.prototype.isActionEvent = function() {\n return (this.getBody().find(SELECTORS.ROOT).attr('data-action-event') == 'true');\n };\n\n /**\n * Set up all of the event handling for the modal.\n *\n * @method registerEventListeners\n */\n ModalEventSummary.prototype.registerEventListeners = function() {\n // Apply parent event listeners.\n Modal.prototype.registerEventListeners.call(this);\n\n // We have to wait for the modal to finish rendering in order to ensure that\n // the data-event-title property is available to use as the modal title.\n M.util.js_pending('core_calendar/summary_modal:registerEventListeners:bodyRendered');\n this.getRoot().on(ModalEvents.bodyRendered, function() {\n this.getModal().data({\n eventTitle: this.getEventTitle(),\n eventId: this.getEventId(),\n eventCount: this.getEventCount(),\n })\n .attr('data-type', 'event');\n CalendarCrud.registerRemove(this.getModal());\n M.util.js_complete('core_calendar/summary_modal:registerEventListeners:bodyRendered');\n }.bind(this));\n\n $('body').on(CalendarEvents.deleted, function() {\n // Close the dialogue on delete.\n this.hide();\n }.bind(this));\n\n CustomEvents.define(this.getEditButton(), [\n CustomEvents.events.activate\n ]);\n\n this.getEditButton().on(CustomEvents.events.activate, function(e, data) {\n if (this.isActionEvent()) {\n // Action events cannot be edited on the event form and must be redirected to the module UI.\n $('body').trigger(CalendarEvents.editActionEvent, [this.getEditUrl()]);\n } else {\n // When the edit button is clicked we fire an event for the calendar UI to handle.\n // We don't care how the UI chooses to handle it.\n $('body').trigger(CalendarEvents.editEvent, [this.getEventId()]);\n }\n\n // There is nothing else for us to do so let's hide.\n this.hide();\n\n // We've handled this event so no need to propagate it.\n e.preventDefault();\n e.stopPropagation();\n data.originalEvent.preventDefault();\n data.originalEvent.stopPropagation();\n }.bind(this));\n };\n\n // Automatically register with the modal registry the first time this module is imported so that you can create modals\n // of this type using the modal factory.\n if (!registered) {\n ModalRegistry.register(ModalEventSummary.TYPE, ModalEventSummary, 'core_calendar/event_summary_modal');\n registered = true;\n }\n\n return ModalEventSummary;\n});\n"],"names":["define","$","Str","Notification","CustomEvents","Modal","ModalRegistry","ModalFactory","ModalEvents","CalendarRepository","CalendarEvents","CalendarCrud","registered","SELECTORS","ModalEventSummary","root","call","this","TYPE","prototype","Object","create","constructor","getEditButton","editButton","getFooter","find","getDeleteButton","deleteButton","getEventId","getBody","attr","getEventTitle","getEventCount","getEditUrl","isActionEvent","registerEventListeners","M","util","js_pending","getRoot","on","bodyRendered","getModal","data","eventTitle","eventId","eventCount","registerRemove","js_complete","bind","deleted","hide","events","activate","e","trigger","editActionEvent","editEvent","preventDefault","stopPropagation","originalEvent","register"],"mappings":";;;;;;;AAsBAA,OAAM,8BAAC,CACH,SACA,WACA,oBACA,iCACA,aACA,sBACA,qBACA,oBACA,2BACA,uBACA,uBAEJ,SACIC,EACAC,IACAC,aACAC,aACAC,MACAC,cACAC,aACAC,YACAC,mBACAC,eACAC,cAGA,IAAIC,YAAa,EACbC,eACM,0CADNA,sBAEa,uBAFbA,wBAGe,yBAQfC,kBAAoB,SAASC,MAC7BV,MAAMW,KAAKC,KAAMF,OAyJrB,OAtJAD,kBAAkBI,KAAO,+BACzBJ,kBAAkBK,UAAYC,OAAOC,OAAOhB,MAAMc,YACtBG,YAAcR,kBAS1CA,kBAAkBK,UAAUI,cAAgB,WAKxC,YAJ8B,IAAnBN,KAAKO,aACZP,KAAKO,WAAaP,KAAKQ,YAAYC,KAAKb,wBAGrCI,KAAKO,YAUhBV,kBAAkBK,UAAUQ,gBAAkB,WAK1C,YAJgC,IAArBV,KAAKW,eACZX,KAAKW,aAAeX,KAAKQ,YAAYC,KAAKb,0BAGvCI,KAAKW,cAWhBd,kBAAkBK,UAAUU,WAAa,WACrC,OAAOZ,KAAKa,UAAUJ,KAAKb,gBAAgBkB,KAAK,kBAWpDjB,kBAAkBK,UAAUa,cAAgB,WACxC,OAAOf,KAAKa,UAAUJ,KAAKb,gBAAgBkB,KAAK,qBAWpDjB,kBAAkBK,UAAUc,cAAgB,WACxC,OAAOhB,KAAKa,UAAUJ,KAAKb,gBAAgBkB,KAAK,qBASpDjB,kBAAkBK,UAAUe,WAAa,WACrC,OAAOjB,KAAKa,UAAUJ,KAAKb,gBAAgBkB,KAAK,kBASpDjB,kBAAkBK,UAAUgB,cAAgB,WACxC,MAAyE,QAAjElB,KAAKa,UAAUJ,KAAKb,gBAAgBkB,KAAK,sBAQrDjB,kBAAkBK,UAAUiB,uBAAyB,WAEjD/B,MAAMc,UAAUiB,uBAAuBpB,KAAKC,MAI5CoB,EAAEC,KAAKC,WAAW,mEAClBtB,KAAKuB,UAAUC,GAAGjC,YAAYkC,aAAc,WACxCzB,KAAK0B,WAAWC,KAAK,CACjBC,WAAY5B,KAAKe,gBACjBc,QAAS7B,KAAKY,aACdkB,WAAY9B,KAAKgB,kBAEpBF,KAAK,YAAa,SACnBpB,aAAaqC,eAAe/B,KAAK0B,YACjCN,EAAEC,KAAKW,YAAY,kEACvB,EAAEC,KAAKjC,OAEPhB,EAAE,QAAQwC,GAAG/B,eAAeyC,QAAS,WAEjClC,KAAKmC,MACT,EAAEF,KAAKjC,OAEPb,aAAaJ,OAAOiB,KAAKM,gBAAiB,CACtCnB,aAAaiD,OAAOC,WAGxBrC,KAAKM,gBAAgBkB,GAAGrC,aAAaiD,OAAOC,SAAU,SAASC,EAAGX,MAC1D3B,KAAKkB,gBAELlC,EAAE,QAAQuD,QAAQ9C,eAAe+C,gBAAiB,CAACxC,KAAKiB,eAIxDjC,EAAE,QAAQuD,QAAQ9C,eAAegD,UAAW,CAACzC,KAAKY,eAItDZ,KAAKmC,OAGLG,EAAEI,iBACFJ,EAAEK,kBACFhB,KAAKiB,cAAcF,iBACnBf,KAAKiB,cAAcD,iBACvB,EAAEV,KAAKjC,QAKNL,aACDN,cAAcwD,SAAShD,kBAAkBI,KAAMJ,kBAAmB,qCAClEF,YAAa,GAGVE,iBACX"}
\ No newline at end of file
diff --git a/calendar/amd/build/view_manager.min.js b/calendar/amd/build/view_manager.min.js
index 797eb83e456..f19103f2b78 100644
--- a/calendar/amd/build/view_manager.min.js
+++ b/calendar/amd/build/view_manager.min.js
@@ -5,6 +5,6 @@ define("core_calendar/view_manager",["exports","jquery","core/templates","core/n
* @module core_calendar/view_manager
* @copyright 2017 Andrew Nicols
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.updateUrl=_exports.reloadCurrentUpcoming=_exports.reloadCurrentMonth=_exports.reloadCurrentDay=_exports.registerEventListenersForMonthDetailed=_exports.refreshMonthContent=_exports.refreshDayContent=_exports.init=_exports.foldDayEvents=_exports.changeMonth=_exports.changeDay=void 0,_jquery=_interopRequireDefault(_jquery),_templates=_interopRequireDefault(_templates),_notification=_interopRequireDefault(_notification),CalendarRepository=_interopRequireWildcard(CalendarRepository),_events=_interopRequireDefault(_events),CalendarSelectors=_interopRequireWildcard(CalendarSelectors),_modal_factory=_interopRequireDefault(_modal_factory),_modal_events=_interopRequireDefault(_modal_events),_summary_modal=_interopRequireDefault(_summary_modal),_custom_interaction_events=_interopRequireDefault(_custom_interaction_events),_pending=_interopRequireDefault(_pending);const foldDayEvents=()=>{const days=(0,_jquery.default)(CalendarSelectors.elements.monthDetailed).find(CalendarSelectors.day);0!==days.length&&days.each((function(){const dayContainer=(0,_jquery.default)(this),eventsSelector="".concat(CalendarSelectors.elements.dateContent," ul li[data-event-eventtype]"),filteredEventsSelector="".concat(CalendarSelectors.elements.dateContent,' ul li[data-event-filtered="true"]'),moreEventsSelector="".concat(CalendarSelectors.elements.dateContent,' [data-action="view-more-events"]'),events=dayContainer.find(eventsSelector);if(0===events.length)return;const numberOfFiltered=dayContainer.find(filteredEventsSelector).length,numberOfEvents=events.length-numberOfFiltered;let count=1;events.each((function(){const event=(0,_jquery.default)(this);"true"!==event.attr("data-event-filtered")?count>5-(5===numberOfEvents?0:1)?(event.attr("data-event-folded","true"),event.hide()):(event.attr("data-event-folded","false"),event.show(),count++):event.attr("data-event-folded","false")}));const moreEventsLink=dayContainer.find(moreEventsSelector);if(numberOfEvents>5){const numberOfHiddenEvents=numberOfEvents-5+1;moreEventsLink.show(),(0,_str.get_string)("moreevents","calendar",numberOfHiddenEvents).then((str=>{const link=moreEventsLink.find("strong a");return moreEventsLink.attr("data-event-folded","false"),link.text(str),str})).fail()}else moreEventsLink.hide()}))};_exports.foldDayEvents=foldDayEvents;const registerEventListenersForMonthDetailed=pendingId=>{const events="".concat(_events.default.viewUpdated);(0,_jquery.default)("body").on(events,(function(e){foldDayEvents()})),foldDayEvents(),(0,_jquery.default)("body").on(_events.default.filterChanged,(function(e,data){const root=(0,_jquery.default)(CalendarSelectors.elements.monthDetailed),pending=new _pending.default(pendingId),target=root.find(CalendarSelectors.eventType[data.type]),transitionPromise=_jquery.default.Deferred();data.hidden?transitionPromise.then((function(){return target.attr("data-event-filtered","true"),target.hide().promise()})).fail():transitionPromise.then((function(){return target.attr("data-event-filtered","false"),target.show().promise()})).fail(),transitionPromise.then((function(){foldDayEvents()})).always(pending.resolve).fail(),transitionPromise.resolve()}))};_exports.registerEventListenersForMonthDetailed=registerEventListenersForMonthDetailed;const refreshMonthContent=function(root,year,month,courseId,categoryId){let target=arguments.length>5&&void 0!==arguments[5]?arguments[5]:null,template=arguments.length>6&&void 0!==arguments[6]?arguments[6]:"",day=arguments.length>7&&void 0!==arguments[7]?arguments[7]:1;startLoading(root),target=target||root.find(CalendarSelectors.wrapper),template=template||root.attr("data-template"),M.util.js_pending([root.get("id"),year,month,courseId].join("-"));const includenavigation=root.data("includenavigation"),mini=root.data("mini"),viewMode=target.data("view");return CalendarRepository.getCalendarMonthData(year,month,courseId,categoryId,includenavigation,mini,day,viewMode).then((context=>_templates.default.render(template,context))).then(((html,js)=>_templates.default.replaceNode(target,html,js))).then((()=>{document.querySelector("body").dispatchEvent(new CustomEvent(_events.default.viewUpdated))})).always((()=>(M.util.js_complete([root.get("id"),year,month,courseId].join("-")),stopLoading(root)))).fail(_notification.default.exception)};_exports.refreshMonthContent=refreshMonthContent;const changeMonth=function(root,url,year,month,courseId,categoryId){let day=arguments.length>6&&void 0!==arguments[6]?arguments[6]:1;return refreshMonthContent(root,year,month,courseId,categoryId,null,"",day).then((function(){url.length&&"#"!==url&&updateUrl(url);for(var _len=arguments.length,args=new Array(_len),_key=0;_key<_len;_key++)args[_key]=arguments[_key];return args})).then((function(){(0,_jquery.default)("body").trigger(_events.default.monthChanged,[year,month,courseId,categoryId]);for(var _len2=arguments.length,args=new Array(_len2),_key2=0;_key2<_len2;_key2++)args[_key2]=arguments[_key2];return args}))};_exports.changeMonth=changeMonth;_exports.reloadCurrentMonth=function(root){let courseId=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0,categoryId=arguments.length>2&&void 0!==arguments[2]?arguments[2]:0;const year=root.find(CalendarSelectors.wrapper).data("year"),month=root.find(CalendarSelectors.wrapper).data("month"),day=root.find(CalendarSelectors.wrapper).data("day");return courseId=courseId||root.find(CalendarSelectors.wrapper).data("courseid"),categoryId=categoryId||root.find(CalendarSelectors.wrapper).data("categoryid"),refreshMonthContent(root,year,month,courseId,categoryId,null,"",day).then((function(){(0,_jquery.default)("body").trigger(_events.default.courseChanged,[year,month,courseId,categoryId]);for(var _len3=arguments.length,args=new Array(_len3),_key3=0;_key3<_len3;_key3++)args[_key3]=arguments[_key3];return args}))};const refreshDayContent=function(root,year,month,day,courseId,categoryId){let target=arguments.length>6&&void 0!==arguments[6]?arguments[6]:null,template=arguments.length>7&&void 0!==arguments[7]?arguments[7]:"";startLoading(root),target&&0!=target.length||(target=root.find(CalendarSelectors.wrapper)),template=template||root.attr("data-template"),M.util.js_pending([root.get("id"),year,month,day,courseId,categoryId].join("-"));const includenavigation=root.data("includenavigation");return CalendarRepository.getCalendarDayData(year,month,day,courseId,categoryId,includenavigation).then((context=>(context.viewingday=!0,context.showviewselector=!0,_templates.default.render(template,context)))).then(((html,js)=>_templates.default.replaceNode(target,html,js))).then((()=>{document.querySelector("body").dispatchEvent(new CustomEvent(_events.default.viewUpdated))})).always((()=>(M.util.js_complete([root.get("id"),year,month,day,courseId,categoryId].join("-")),stopLoading(root)))).fail(_notification.default.exception)};_exports.refreshDayContent=refreshDayContent;_exports.reloadCurrentDay=function(root){let courseId=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0,categoryId=arguments.length>2&&void 0!==arguments[2]?arguments[2]:0;const wrapper=root.find(CalendarSelectors.wrapper),year=wrapper.data("year"),month=wrapper.data("month"),day=wrapper.data("day");return courseId=courseId||root.find(CalendarSelectors.wrapper).data("courseid"),categoryId=categoryId||root.find(CalendarSelectors.wrapper).data("categoryid"),refreshDayContent(root,year,month,day,courseId,categoryId)};const changeDay=(root,url,year,month,day,courseId,categoryId)=>refreshDayContent(root,year,month,day,courseId,categoryId).then((function(){url.length&&"#"!==url&&updateUrl(url);for(var _len4=arguments.length,args=new Array(_len4),_key4=0;_key4<_len4;_key4++)args[_key4]=arguments[_key4];return args})).then((function(){(0,_jquery.default)("body").trigger(_events.default.dayChanged,[year,month,courseId,categoryId]);for(var _len5=arguments.length,args=new Array(_len5),_key5=0;_key5<_len5;_key5++)args[_key5]=arguments[_key5];return args}));_exports.changeDay=changeDay;const updateUrl=url=>{document.getElementById(CalendarSelectors.fullCalendarView)&&window.history.pushState({},"",url)};_exports.updateUrl=updateUrl;const startLoading=root=>{root.find(CalendarSelectors.containers.loadingIcon).removeClass("hidden")},stopLoading=root=>{root.find(CalendarSelectors.containers.loadingIcon).addClass("hidden")},reloadCurrentUpcoming=function(root){let courseId=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0,categoryId=arguments.length>2&&void 0!==arguments[2]?arguments[2]:0,target=arguments.length>3&&void 0!==arguments[3]?arguments[3]:null,template=arguments.length>4&&void 0!==arguments[4]?arguments[4]:"";return startLoading(root),target=target||root.find(CalendarSelectors.wrapper),template=template||root.attr("data-template"),courseId=courseId||root.find(CalendarSelectors.wrapper).data("courseid"),categoryId=categoryId||root.find(CalendarSelectors.wrapper).data("categoryid"),CalendarRepository.getCalendarUpcomingData(courseId,categoryId).then((context=>(context.viewingupcoming=!0,context.showviewselector=!0,_templates.default.render(template,context)))).then(((html,js)=>_templates.default.replaceNode(target,html,js))).then((()=>{document.querySelector("body").dispatchEvent(new CustomEvent(_events.default.viewUpdated))})).always((function(){return stopLoading(root)})).fail(_notification.default.exception)};_exports.reloadCurrentUpcoming=reloadCurrentUpcoming;const renderEventSummaryModal=eventId=>{const pendingPromise=new _pending.default("core_calendar/view_manager:renderEventSummaryModal");return CalendarRepository.getEventById(eventId).then((getEventResponse=>{if(!getEventResponse.event)throw new Error("Error encountered while trying to fetch calendar event with ID: "+eventId);return getEventResponse.event})).then((eventData=>{const modalParams={title:eventData.name,type:_summary_modal.default.TYPE,body:_templates.default.render("core_calendar/event_summary_body",eventData),templateContext:{canedit:eventData.canedit,candelete:eventData.candelete,headerclasses:(eventType=eventData.normalisedeventtype,"calendar_event_"+eventType),isactionevent:eventData.isactionevent,url:eventData.url,action:eventData.action}};var eventType;return _modal_factory.default.create(modalParams)})).then((modal=>(modal.getRoot().on(_modal_events.default.hidden,(function(){modal.destroy()})),modal.show(),modal))).then((modal=>(pendingPromise.resolve(),modal))).catch(_notification.default.exception)};_exports.init=(root,view)=>{(0,_prefetch.prefetchStrings)("calendar",["moreevents"]),foldDayEvents(),(root=>{(root=(0,_jquery.default)(root)).on("click",CalendarSelectors.links.eventLink,(e=>{const target=e.target;let eventLink=null,eventId=null;const pendingPromise=new _pending.default("core_calendar/view_manager:eventLink:click");eventLink=target.matches(CalendarSelectors.actions.viewEvent)?target:target.closest(CalendarSelectors.actions.viewEvent),eventId=eventLink?eventLink.dataset.eventId:target.querySelector(CalendarSelectors.actions.viewEvent).dataset.eventId,eventId?(e.preventDefault(),e.stopPropagation(),renderEventSummaryModal(eventId).then(pendingPromise.resolve).catch()):pendingPromise.resolve()})),root.on("click",CalendarSelectors.links.navLink,(e=>{const wrapper=root.find(CalendarSelectors.wrapper),view=wrapper.data("view"),courseId=wrapper.data("courseid"),categoryId=wrapper.data("categoryid"),link=e.currentTarget;"month"===view||"monthblock"===view?(changeMonth(root,link.href,link.dataset.year,link.dataset.month,courseId,categoryId,link.dataset.day),e.preventDefault()):"day"===view&&(changeDay(root,link.href,link.dataset.year,link.dataset.month,link.dataset.day,courseId,categoryId),e.preventDefault())}));const viewSelector=root.find(CalendarSelectors.viewSelector);_custom_interaction_events.default.define(viewSelector,[_custom_interaction_events.default.events.activate]),viewSelector.on(_custom_interaction_events.default.events.activate,(e=>{e.preventDefault();const option=e.target;if(option.classList.contains("active"))return;const view=option.dataset.view,year=option.dataset.year,month=option.dataset.month,day=option.dataset.day,courseId=option.dataset.courseid,categoryId=option.dataset.categoryid;"month"==view?refreshMonthContent(root,year,month,courseId,categoryId,root,"core_calendar/calendar_month",day).then((()=>{updateUrl("?view=month")})).fail(_notification.default.exception):"day"==view?refreshDayContent(root,year,month,day,courseId,categoryId,root,"core_calendar/calendar_day").then((()=>{updateUrl("?view=day")})).fail(_notification.default.exception):"upcoming"==view&&reloadCurrentUpcoming(root,courseId,categoryId,root,"core_calendar/calendar_upcoming").then((()=>{updateUrl("?view=upcoming")})).fail(_notification.default.exception)}))})(root);const calendarTable=root.find(CalendarSelectors.elements.monthDetailed);if(calendarTable.length){"month-detailed-".concat(calendarTable.id,"-filterChanged");registerEventListenersForMonthDetailed(calendarTable)}}}));
+ */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.updateUrl=_exports.reloadCurrentUpcoming=_exports.reloadCurrentMonth=_exports.reloadCurrentDay=_exports.registerEventListenersForMonthDetailed=_exports.refreshMonthContent=_exports.refreshDayContent=_exports.init=_exports.foldDayEvents=_exports.changeMonth=_exports.changeDay=void 0,_jquery=_interopRequireDefault(_jquery),_templates=_interopRequireDefault(_templates),_notification=_interopRequireDefault(_notification),CalendarRepository=_interopRequireWildcard(CalendarRepository),_events=_interopRequireDefault(_events),CalendarSelectors=_interopRequireWildcard(CalendarSelectors),_modal_factory=_interopRequireDefault(_modal_factory),_modal_events=_interopRequireDefault(_modal_events),_summary_modal=_interopRequireDefault(_summary_modal),_custom_interaction_events=_interopRequireDefault(_custom_interaction_events),_pending=_interopRequireDefault(_pending);const foldDayEvents=()=>{const days=(0,_jquery.default)(CalendarSelectors.elements.monthDetailed).find(CalendarSelectors.day);0!==days.length&&days.each((function(){const dayContainer=(0,_jquery.default)(this),eventsSelector="".concat(CalendarSelectors.elements.dateContent," ul li[data-event-eventtype]"),filteredEventsSelector="".concat(CalendarSelectors.elements.dateContent,' ul li[data-event-filtered="true"]'),moreEventsSelector="".concat(CalendarSelectors.elements.dateContent,' [data-action="view-more-events"]'),events=dayContainer.find(eventsSelector);if(0===events.length)return;const numberOfFiltered=dayContainer.find(filteredEventsSelector).length,numberOfEvents=events.length-numberOfFiltered;let count=1;events.each((function(){const event=(0,_jquery.default)(this);"true"!==event.attr("data-event-filtered")?count>5-(5===numberOfEvents?0:1)?(event.attr("data-event-folded","true"),event.hide()):(event.attr("data-event-folded","false"),event.show(),count++):event.attr("data-event-folded","false")}));const moreEventsLink=dayContainer.find(moreEventsSelector);if(numberOfEvents>5){const numberOfHiddenEvents=numberOfEvents-5+1;moreEventsLink.show(),(0,_str.get_string)("moreevents","calendar",numberOfHiddenEvents).then((str=>{const link=moreEventsLink.find("strong a");return moreEventsLink.attr("data-event-folded","false"),link.text(str),str})).fail()}else moreEventsLink.hide()}))};_exports.foldDayEvents=foldDayEvents;const registerEventListenersForMonthDetailed=pendingId=>{const events="".concat(_events.default.viewUpdated);(0,_jquery.default)("body").on(events,(function(e){foldDayEvents()})),foldDayEvents(),(0,_jquery.default)("body").on(_events.default.filterChanged,(function(e,data){const root=(0,_jquery.default)(CalendarSelectors.elements.monthDetailed),pending=new _pending.default(pendingId),target=root.find(CalendarSelectors.eventType[data.type]),transitionPromise=_jquery.default.Deferred();data.hidden?transitionPromise.then((function(){return target.attr("data-event-filtered","true"),target.hide().promise()})).fail():transitionPromise.then((function(){return target.attr("data-event-filtered","false"),target.show().promise()})).fail(),transitionPromise.then((function(){foldDayEvents()})).always(pending.resolve).fail(),transitionPromise.resolve()}))};_exports.registerEventListenersForMonthDetailed=registerEventListenersForMonthDetailed;const refreshMonthContent=function(root,year,month,courseId,categoryId){let target=arguments.length>5&&void 0!==arguments[5]?arguments[5]:null,template=arguments.length>6&&void 0!==arguments[6]?arguments[6]:"",day=arguments.length>7&&void 0!==arguments[7]?arguments[7]:1;startLoading(root),target=target||root.find(CalendarSelectors.wrapper),template=template||root.attr("data-template"),M.util.js_pending([root.get("id"),year,month,courseId].join("-"));const includenavigation=root.data("includenavigation"),mini=root.data("mini"),viewMode=target.data("view");return CalendarRepository.getCalendarMonthData(year,month,courseId,categoryId,includenavigation,mini,day,viewMode).then((context=>_templates.default.render(template,context))).then(((html,js)=>_templates.default.replaceNode(target,html,js))).then((()=>{document.querySelector("body").dispatchEvent(new CustomEvent(_events.default.viewUpdated))})).always((()=>(M.util.js_complete([root.get("id"),year,month,courseId].join("-")),stopLoading(root)))).fail(_notification.default.exception)};_exports.refreshMonthContent=refreshMonthContent;const changeMonth=function(root,url,year,month,courseId,categoryId){return refreshMonthContent(root,year,month,courseId,categoryId,null,"",arguments.length>6&&void 0!==arguments[6]?arguments[6]:1).then((function(){url.length&&"#"!==url&&updateUrl(url);for(var _len=arguments.length,args=new Array(_len),_key=0;_key<_len;_key++)args[_key]=arguments[_key];return args})).then((function(){(0,_jquery.default)("body").trigger(_events.default.monthChanged,[year,month,courseId,categoryId]);for(var _len2=arguments.length,args=new Array(_len2),_key2=0;_key2<_len2;_key2++)args[_key2]=arguments[_key2];return args}))};_exports.changeMonth=changeMonth;_exports.reloadCurrentMonth=function(root){let courseId=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0,categoryId=arguments.length>2&&void 0!==arguments[2]?arguments[2]:0;const year=root.find(CalendarSelectors.wrapper).data("year"),month=root.find(CalendarSelectors.wrapper).data("month"),day=root.find(CalendarSelectors.wrapper).data("day");return courseId=courseId||root.find(CalendarSelectors.wrapper).data("courseid"),categoryId=categoryId||root.find(CalendarSelectors.wrapper).data("categoryid"),refreshMonthContent(root,year,month,courseId,categoryId,null,"",day).then((function(){(0,_jquery.default)("body").trigger(_events.default.courseChanged,[year,month,courseId,categoryId]);for(var _len3=arguments.length,args=new Array(_len3),_key3=0;_key3<_len3;_key3++)args[_key3]=arguments[_key3];return args}))};const refreshDayContent=function(root,year,month,day,courseId,categoryId){let target=arguments.length>6&&void 0!==arguments[6]?arguments[6]:null,template=arguments.length>7&&void 0!==arguments[7]?arguments[7]:"";startLoading(root),target&&0!=target.length||(target=root.find(CalendarSelectors.wrapper)),template=template||root.attr("data-template"),M.util.js_pending([root.get("id"),year,month,day,courseId,categoryId].join("-"));const includenavigation=root.data("includenavigation");return CalendarRepository.getCalendarDayData(year,month,day,courseId,categoryId,includenavigation).then((context=>(context.viewingday=!0,context.showviewselector=!0,_templates.default.render(template,context)))).then(((html,js)=>_templates.default.replaceNode(target,html,js))).then((()=>{document.querySelector("body").dispatchEvent(new CustomEvent(_events.default.viewUpdated))})).always((()=>(M.util.js_complete([root.get("id"),year,month,day,courseId,categoryId].join("-")),stopLoading(root)))).fail(_notification.default.exception)};_exports.refreshDayContent=refreshDayContent;_exports.reloadCurrentDay=function(root){let courseId=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0,categoryId=arguments.length>2&&void 0!==arguments[2]?arguments[2]:0;const wrapper=root.find(CalendarSelectors.wrapper),year=wrapper.data("year"),month=wrapper.data("month"),day=wrapper.data("day");return courseId=courseId||root.find(CalendarSelectors.wrapper).data("courseid"),categoryId=categoryId||root.find(CalendarSelectors.wrapper).data("categoryid"),refreshDayContent(root,year,month,day,courseId,categoryId)};const changeDay=(root,url,year,month,day,courseId,categoryId)=>refreshDayContent(root,year,month,day,courseId,categoryId).then((function(){url.length&&"#"!==url&&updateUrl(url);for(var _len4=arguments.length,args=new Array(_len4),_key4=0;_key4<_len4;_key4++)args[_key4]=arguments[_key4];return args})).then((function(){(0,_jquery.default)("body").trigger(_events.default.dayChanged,[year,month,courseId,categoryId]);for(var _len5=arguments.length,args=new Array(_len5),_key5=0;_key5<_len5;_key5++)args[_key5]=arguments[_key5];return args}));_exports.changeDay=changeDay;const updateUrl=url=>{document.getElementById(CalendarSelectors.fullCalendarView)&&window.history.pushState({},"",url)};_exports.updateUrl=updateUrl;const startLoading=root=>{root.find(CalendarSelectors.containers.loadingIcon).removeClass("hidden")},stopLoading=root=>{root.find(CalendarSelectors.containers.loadingIcon).addClass("hidden")},reloadCurrentUpcoming=function(root){let courseId=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0,categoryId=arguments.length>2&&void 0!==arguments[2]?arguments[2]:0,target=arguments.length>3&&void 0!==arguments[3]?arguments[3]:null,template=arguments.length>4&&void 0!==arguments[4]?arguments[4]:"";return startLoading(root),target=target||root.find(CalendarSelectors.wrapper),template=template||root.attr("data-template"),courseId=courseId||root.find(CalendarSelectors.wrapper).data("courseid"),categoryId=categoryId||root.find(CalendarSelectors.wrapper).data("categoryid"),CalendarRepository.getCalendarUpcomingData(courseId,categoryId).then((context=>(context.viewingupcoming=!0,context.showviewselector=!0,_templates.default.render(template,context)))).then(((html,js)=>_templates.default.replaceNode(target,html,js))).then((()=>{document.querySelector("body").dispatchEvent(new CustomEvent(_events.default.viewUpdated))})).always((function(){return stopLoading(root)})).fail(_notification.default.exception)};_exports.reloadCurrentUpcoming=reloadCurrentUpcoming;const renderEventSummaryModal=eventId=>{const pendingPromise=new _pending.default("core_calendar/view_manager:renderEventSummaryModal");return CalendarRepository.getEventById(eventId).then((getEventResponse=>{if(!getEventResponse.event)throw new Error("Error encountered while trying to fetch calendar event with ID: "+eventId);return getEventResponse.event})).then((eventData=>{const modalParams={title:eventData.name,type:_summary_modal.default.TYPE,body:_templates.default.render("core_calendar/event_summary_body",eventData),templateContext:{canedit:eventData.canedit,candelete:eventData.candelete,headerclasses:(eventType=eventData.normalisedeventtype,"calendar_event_"+eventType),isactionevent:eventData.isactionevent,url:eventData.url,action:eventData.action}};var eventType;return _modal_factory.default.create(modalParams)})).then((modal=>(modal.getRoot().on(_modal_events.default.hidden,(function(){modal.destroy()})),modal.show(),modal))).then((modal=>(pendingPromise.resolve(),modal))).catch(_notification.default.exception)};_exports.init=(root,view)=>{(0,_prefetch.prefetchStrings)("calendar",["moreevents"]),foldDayEvents(),(root=>{(root=(0,_jquery.default)(root)).on("click",CalendarSelectors.links.eventLink,(e=>{const target=e.target;let eventLink=null,eventId=null;const pendingPromise=new _pending.default("core_calendar/view_manager:eventLink:click");eventLink=target.matches(CalendarSelectors.actions.viewEvent)?target:target.closest(CalendarSelectors.actions.viewEvent),eventId=eventLink?eventLink.dataset.eventId:target.querySelector(CalendarSelectors.actions.viewEvent).dataset.eventId,eventId?(e.preventDefault(),e.stopPropagation(),renderEventSummaryModal(eventId).then(pendingPromise.resolve).catch()):pendingPromise.resolve()})),root.on("click",CalendarSelectors.links.navLink,(e=>{const wrapper=root.find(CalendarSelectors.wrapper),view=wrapper.data("view"),courseId=wrapper.data("courseid"),categoryId=wrapper.data("categoryid"),link=e.currentTarget;"month"===view||"monthblock"===view?(changeMonth(root,link.href,link.dataset.year,link.dataset.month,courseId,categoryId,link.dataset.day),e.preventDefault()):"day"===view&&(changeDay(root,link.href,link.dataset.year,link.dataset.month,link.dataset.day,courseId,categoryId),e.preventDefault())}));const viewSelector=root.find(CalendarSelectors.viewSelector);_custom_interaction_events.default.define(viewSelector,[_custom_interaction_events.default.events.activate]),viewSelector.on(_custom_interaction_events.default.events.activate,(e=>{e.preventDefault();const option=e.target;if(option.classList.contains("active"))return;const view=option.dataset.view,year=option.dataset.year,month=option.dataset.month,day=option.dataset.day,courseId=option.dataset.courseid,categoryId=option.dataset.categoryid;"month"==view?refreshMonthContent(root,year,month,courseId,categoryId,root,"core_calendar/calendar_month",day).then((()=>{updateUrl("?view=month")})).fail(_notification.default.exception):"day"==view?refreshDayContent(root,year,month,day,courseId,categoryId,root,"core_calendar/calendar_day").then((()=>{updateUrl("?view=day")})).fail(_notification.default.exception):"upcoming"==view&&reloadCurrentUpcoming(root,courseId,categoryId,root,"core_calendar/calendar_upcoming").then((()=>{updateUrl("?view=upcoming")})).fail(_notification.default.exception)}))})(root);const calendarTable=root.find(CalendarSelectors.elements.monthDetailed);if(calendarTable.length){"month-detailed-".concat(calendarTable.id,"-filterChanged");registerEventListenersForMonthDetailed(calendarTable)}}}));
//# sourceMappingURL=view_manager.min.js.map
\ No newline at end of file
diff --git a/calendar/amd/build/view_manager.min.js.map b/calendar/amd/build/view_manager.min.js.map
index 87f2fd128be..360a312256f 100644
--- a/calendar/amd/build/view_manager.min.js.map
+++ b/calendar/amd/build/view_manager.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"view_manager.min.js","sources":["../src/view_manager.js"],"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 * A javascript module to handler calendar view changes.\n *\n * @module core_calendar/view_manager\n * @copyright 2017 Andrew Nicols \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport $ from 'jquery';\nimport Templates from 'core/templates';\nimport Notification from 'core/notification';\nimport * as CalendarRepository from 'core_calendar/repository';\nimport CalendarEvents from 'core_calendar/events';\nimport * as CalendarSelectors from 'core_calendar/selectors';\nimport ModalFactory from 'core/modal_factory';\nimport ModalEvents from 'core/modal_events';\nimport SummaryModal from 'core_calendar/summary_modal';\nimport CustomEvents from 'core/custom_interaction_events';\nimport {get_string as getString} from 'core/str';\nimport Pending from 'core/pending';\nimport {prefetchStrings} from 'core/prefetch';\n\n/**\n * Limit number of events per day\n *\n */\nconst LIMIT_DAY_EVENTS = 5;\n\n/**\n * Hide day events if more than 5.\n *\n */\nexport const foldDayEvents = () => {\n const root = $(CalendarSelectors.elements.monthDetailed);\n const days = root.find(CalendarSelectors.day);\n if (days.length === 0) {\n return;\n }\n days.each(function() {\n const dayContainer = $(this);\n const eventsSelector = `${CalendarSelectors.elements.dateContent} ul li[data-event-eventtype]`;\n const filteredEventsSelector = `${CalendarSelectors.elements.dateContent} ul li[data-event-filtered=\"true\"]`;\n const moreEventsSelector = `${CalendarSelectors.elements.dateContent} [data-action=\"view-more-events\"]`;\n const events = dayContainer.find(eventsSelector);\n if (events.length === 0) {\n return;\n }\n\n const filteredEvents = dayContainer.find(filteredEventsSelector);\n const numberOfFiltered = filteredEvents.length;\n const numberOfEvents = events.length - numberOfFiltered;\n\n let count = 1;\n events.each(function() {\n const event = $(this);\n const isNotFiltered = event.attr('data-event-filtered') !== 'true';\n const offset = (numberOfEvents === LIMIT_DAY_EVENTS) ? 0 : 1;\n if (isNotFiltered) {\n if (count > LIMIT_DAY_EVENTS - offset) {\n event.attr('data-event-folded', 'true');\n event.hide();\n } else {\n event.attr('data-event-folded', 'false');\n event.show();\n count++;\n }\n } else {\n // It's being filtered out.\n event.attr('data-event-folded', 'false');\n }\n });\n\n const moreEventsLink = dayContainer.find(moreEventsSelector);\n if (numberOfEvents > LIMIT_DAY_EVENTS) {\n const numberOfHiddenEvents = numberOfEvents - LIMIT_DAY_EVENTS + 1;\n moreEventsLink.show();\n getString('moreevents', 'calendar', numberOfHiddenEvents).then(str => {\n const link = moreEventsLink.find('strong a');\n moreEventsLink.attr('data-event-folded', 'false');\n link.text(str);\n return str;\n }).fail();\n } else {\n moreEventsLink.hide();\n }\n });\n};\n\n/**\n * Register and handle month calendar events.\n *\n * @param {string} pendingId pending id.\n */\nexport const registerEventListenersForMonthDetailed = (pendingId) => {\n const events = `${CalendarEvents.viewUpdated}`;\n $('body').on(events, function(e) {\n foldDayEvents(e);\n });\n foldDayEvents();\n $('body').on(CalendarEvents.filterChanged, function(e, data) {\n const root = $(CalendarSelectors.elements.monthDetailed);\n const pending = new Pending(pendingId);\n const target = root.find(CalendarSelectors.eventType[data.type]);\n const transitionPromise = $.Deferred();\n if (data.hidden) {\n transitionPromise.then(function() {\n target.attr('data-event-filtered', 'true');\n return target.hide().promise();\n }).fail();\n } else {\n transitionPromise.then(function() {\n target.attr('data-event-filtered', 'false');\n return target.show().promise();\n }).fail();\n }\n\n transitionPromise.then(function() {\n foldDayEvents();\n return;\n })\n .always(pending.resolve)\n .fail();\n\n transitionPromise.resolve();\n });\n};\n\n/**\n * Register event listeners for the module.\n *\n * @param {object} root The root element.\n */\nconst registerEventListeners = (root) => {\n root = $(root);\n\n // Bind click events to event links.\n root.on('click', CalendarSelectors.links.eventLink, (e) => {\n const target = e.target;\n let eventLink = null;\n let eventId = null;\n const pendingPromise = new Pending('core_calendar/view_manager:eventLink:click');\n\n if (target.matches(CalendarSelectors.actions.viewEvent)) {\n eventLink = target;\n } else {\n eventLink = target.closest(CalendarSelectors.actions.viewEvent);\n }\n\n if (eventLink) {\n eventId = eventLink.dataset.eventId;\n } else {\n eventId = target.querySelector(CalendarSelectors.actions.viewEvent).dataset.eventId;\n }\n\n if (eventId) {\n // A link was found. Show the modal.\n\n e.preventDefault();\n // We've handled the event so stop it from bubbling\n // and causing the day click handler to fire.\n e.stopPropagation();\n\n renderEventSummaryModal(eventId)\n .then(pendingPromise.resolve)\n .catch();\n } else {\n pendingPromise.resolve();\n }\n });\n\n root.on('click', CalendarSelectors.links.navLink, (e) => {\n const wrapper = root.find(CalendarSelectors.wrapper);\n const view = wrapper.data('view');\n const courseId = wrapper.data('courseid');\n const categoryId = wrapper.data('categoryid');\n const link = e.currentTarget;\n\n if (view === 'month' || view === 'monthblock') {\n changeMonth(root, link.href, link.dataset.year, link.dataset.month, courseId, categoryId, link.dataset.day);\n e.preventDefault();\n } else if (view === 'day') {\n changeDay(root, link.href, link.dataset.year, link.dataset.month, link.dataset.day, courseId, categoryId);\n e.preventDefault();\n }\n });\n\n const viewSelector = root.find(CalendarSelectors.viewSelector);\n CustomEvents.define(viewSelector, [CustomEvents.events.activate]);\n viewSelector.on(\n CustomEvents.events.activate,\n (e) => {\n e.preventDefault();\n\n const option = e.target;\n if (option.classList.contains('active')) {\n return;\n }\n\n const view = option.dataset.view,\n year = option.dataset.year,\n month = option.dataset.month,\n day = option.dataset.day,\n courseId = option.dataset.courseid,\n categoryId = option.dataset.categoryid;\n\n if (view == 'month') {\n refreshMonthContent(root, year, month, courseId, categoryId, root, 'core_calendar/calendar_month', day)\n .then(() => {\n updateUrl('?view=month');\n }).fail(Notification.exception);\n } else if (view == 'day') {\n refreshDayContent(root, year, month, day, courseId, categoryId, root, 'core_calendar/calendar_day')\n .then(() => {\n updateUrl('?view=day');\n }).fail(Notification.exception);\n } else if (view == 'upcoming') {\n reloadCurrentUpcoming(root, courseId, categoryId, root, 'core_calendar/calendar_upcoming')\n .then(() => {\n updateUrl('?view=upcoming');\n }).fail(Notification.exception);\n }\n }\n );\n};\n\n/**\n * Refresh the month content.\n *\n * @param {object} root The root element.\n * @param {number} year Year\n * @param {number} month Month\n * @param {number} courseId The id of the course whose events are shown\n * @param {number} categoryId The id of the category whose events are shown\n * @param {object} target The element being replaced. If not specified, the calendarwrapper is used.\n * @param {string} template The template to be rendered.\n * @param {number} day Day (optional)\n * @return {promise}\n */\nexport const refreshMonthContent = (root, year, month, courseId, categoryId, target = null, template = '', day = 1) => {\n startLoading(root);\n\n target = target || root.find(CalendarSelectors.wrapper);\n template = template || root.attr('data-template');\n M.util.js_pending([root.get('id'), year, month, courseId].join('-'));\n const includenavigation = root.data('includenavigation');\n const mini = root.data('mini');\n const viewMode = target.data('view');\n return CalendarRepository.getCalendarMonthData(year, month, courseId, categoryId, includenavigation, mini, day, viewMode)\n .then(context => {\n return Templates.render(template, context);\n })\n .then((html, js) => {\n return Templates.replaceNode(target, html, js);\n })\n .then(() => {\n document.querySelector('body').dispatchEvent(new CustomEvent(CalendarEvents.viewUpdated));\n return;\n })\n .always(() => {\n M.util.js_complete([root.get('id'), year, month, courseId].join('-'));\n return stopLoading(root);\n })\n .fail(Notification.exception);\n};\n\n/**\n * Handle changes to the current calendar view.\n *\n * @param {object} root The container element\n * @param {string} url The calendar url to be shown\n * @param {number} year Year\n * @param {number} month Month\n * @param {number} courseId The id of the course whose events are shown\n * @param {number} categoryId The id of the category whose events are shown\n * @param {number} day Day (optional)\n * @return {promise}\n */\nexport const changeMonth = (root, url, year, month, courseId, categoryId, day = 1) => {\n return refreshMonthContent(root, year, month, courseId, categoryId, null, '', day)\n .then((...args) => {\n if (url.length && url !== '#') {\n updateUrl(url);\n }\n return args;\n })\n .then((...args) => {\n $('body').trigger(CalendarEvents.monthChanged, [year, month, courseId, categoryId]);\n return args;\n });\n};\n\n/**\n * Reload the current month view data.\n *\n * @param {object} root The container element.\n * @param {number} courseId The course id.\n * @param {number} categoryId The id of the category whose events are shown\n * @return {promise}\n */\nexport const reloadCurrentMonth = (root, courseId = 0, categoryId = 0) => {\n const year = root.find(CalendarSelectors.wrapper).data('year');\n const month = root.find(CalendarSelectors.wrapper).data('month');\n const day = root.find(CalendarSelectors.wrapper).data('day');\n\n courseId = courseId || root.find(CalendarSelectors.wrapper).data('courseid');\n categoryId = categoryId || root.find(CalendarSelectors.wrapper).data('categoryid');\n\n return refreshMonthContent(root, year, month, courseId, categoryId, null, '', day).\n then((...args) => {\n $('body').trigger(CalendarEvents.courseChanged, [year, month, courseId, categoryId]);\n return args;\n });\n};\n\n\n/**\n * Refresh the day content.\n *\n * @param {object} root The root element.\n * @param {number} year Year\n * @param {number} month Month\n * @param {number} day Day\n * @param {number} courseId The id of the course whose events are shown\n * @param {number} categoryId The id of the category whose events are shown\n * @param {object} target The element being replaced. If not specified, the calendarwrapper is used.\n * @param {string} template The template to be rendered.\n *\n * @return {promise}\n */\nexport const refreshDayContent = (root, year, month, day, courseId, categoryId, target = null, template = '') => {\n startLoading(root);\n\n if (!target || target.length == 0){\n target = root.find(CalendarSelectors.wrapper);\n }\n template = template || root.attr('data-template');\n M.util.js_pending([root.get('id'), year, month, day, courseId, categoryId].join('-'));\n const includenavigation = root.data('includenavigation');\n return CalendarRepository.getCalendarDayData(year, month, day, courseId, categoryId, includenavigation)\n .then((context) => {\n context.viewingday = true;\n context.showviewselector = true;\n return Templates.render(template, context);\n })\n .then((html, js) => {\n return Templates.replaceNode(target, html, js);\n })\n .then(() => {\n document.querySelector('body').dispatchEvent(new CustomEvent(CalendarEvents.viewUpdated));\n return;\n })\n .always(() => {\n M.util.js_complete([root.get('id'), year, month, day, courseId, categoryId].join('-'));\n return stopLoading(root);\n })\n .fail(Notification.exception);\n};\n\n/**\n * Reload the current day view data.\n *\n * @param {object} root The container element.\n * @param {number} courseId The course id.\n * @param {number} categoryId The id of the category whose events are shown\n * @return {promise}\n */\nexport const reloadCurrentDay = (root, courseId = 0, categoryId = 0) => {\n const wrapper = root.find(CalendarSelectors.wrapper);\n const year = wrapper.data('year');\n const month = wrapper.data('month');\n const day = wrapper.data('day');\n\n courseId = courseId || root.find(CalendarSelectors.wrapper).data('courseid');\n categoryId = categoryId || root.find(CalendarSelectors.wrapper).data('categoryid');\n\n return refreshDayContent(root, year, month, day, courseId, categoryId);\n};\n\n/**\n * Handle changes to the current calendar view.\n *\n * @param {object} root The root element.\n * @param {String} url The calendar url to be shown\n * @param {Number} year Year\n * @param {Number} month Month\n * @param {Number} day Day\n * @param {Number} courseId The id of the course whose events are shown\n * @param {Number} categoryId The id of the category whose events are shown\n * @return {promise}\n */\nexport const changeDay = (root, url, year, month, day, courseId, categoryId) => {\n return refreshDayContent(root, year, month, day, courseId, categoryId)\n .then((...args) => {\n if (url.length && url !== '#') {\n updateUrl(url);\n }\n return args;\n })\n .then((...args) => {\n $('body').trigger(CalendarEvents.dayChanged, [year, month, courseId, categoryId]);\n return args;\n });\n};\n\n/**\n * Update calendar URL.\n *\n * @param {String} url The calendar url to be updated.\n */\nexport const updateUrl = (url) => {\n const viewingFullCalendar = document.getElementById(CalendarSelectors.fullCalendarView);\n\n // We want to update the url only if the user is viewing the full calendar.\n if (viewingFullCalendar) {\n window.history.pushState({}, '', url);\n }\n};\n\n/**\n * Set the element state to loading.\n *\n * @param {object} root The container element\n * @method startLoading\n */\nconst startLoading = (root) => {\n const loadingIconContainer = root.find(CalendarSelectors.containers.loadingIcon);\n\n loadingIconContainer.removeClass('hidden');\n};\n\n/**\n * Remove the loading state from the element.\n *\n * @param {object} root The container element\n * @method stopLoading\n */\nconst stopLoading = (root) => {\n const loadingIconContainer = root.find(CalendarSelectors.containers.loadingIcon);\n\n loadingIconContainer.addClass('hidden');\n};\n\n/**\n * Reload the current month view data.\n *\n * @param {object} root The container element.\n * @param {number} courseId The course id.\n * @param {number} categoryId The id of the category whose events are shown\n * @param {object} target The element being replaced. If not specified, the calendarwrapper is used.\n * @param {string} template The template to be rendered.\n * @return {promise}\n */\nexport const reloadCurrentUpcoming = (root, courseId = 0, categoryId = 0, target = null, template = '') => {\n startLoading(root);\n\n target = target || root.find(CalendarSelectors.wrapper);\n template = template || root.attr('data-template');\n courseId = courseId || root.find(CalendarSelectors.wrapper).data('courseid');\n categoryId = categoryId || root.find(CalendarSelectors.wrapper).data('categoryid');\n\n return CalendarRepository.getCalendarUpcomingData(courseId, categoryId)\n .then((context) => {\n context.viewingupcoming = true;\n context.showviewselector = true;\n return Templates.render(template, context);\n })\n .then((html, js) => {\n return Templates.replaceNode(target, html, js);\n })\n .then(() => {\n document.querySelector('body').dispatchEvent(new CustomEvent(CalendarEvents.viewUpdated));\n return;\n })\n .always(function() {\n return stopLoading(root);\n })\n .fail(Notification.exception);\n};\n\n/**\n * Get the CSS class to apply for the given event type.\n *\n * @param {string} eventType The calendar event type\n * @return {string}\n */\nconst getEventTypeClassFromType = (eventType) => {\n return 'calendar_event_' + eventType;\n};\n\n/**\n * Render the event summary modal.\n *\n * @param {Number} eventId The calendar event id.\n * @returns {Promise}\n */\nconst renderEventSummaryModal = (eventId) => {\n const pendingPromise = new Pending('core_calendar/view_manager:renderEventSummaryModal');\n\n // Calendar repository promise.\n return CalendarRepository.getEventById(eventId)\n .then((getEventResponse) => {\n if (!getEventResponse.event) {\n throw new Error('Error encountered while trying to fetch calendar event with ID: ' + eventId);\n }\n\n return getEventResponse.event;\n })\n .then(eventData => {\n // Build the modal parameters from the event data.\n const modalParams = {\n title: eventData.name,\n type: SummaryModal.TYPE,\n body: Templates.render('core_calendar/event_summary_body', eventData),\n templateContext: {\n canedit: eventData.canedit,\n candelete: eventData.candelete,\n headerclasses: getEventTypeClassFromType(eventData.normalisedeventtype),\n isactionevent: eventData.isactionevent,\n url: eventData.url,\n action: eventData.action\n }\n };\n\n // Create the modal.\n return ModalFactory.create(modalParams);\n })\n .then(modal => {\n // Handle hidden event.\n modal.getRoot().on(ModalEvents.hidden, function() {\n // Destroy when hidden.\n modal.destroy();\n });\n\n // Finally, render the modal!\n modal.show();\n\n return modal;\n })\n .then(modal => {\n pendingPromise.resolve();\n\n return modal;\n })\n .catch(Notification.exception);\n};\n\nexport const init = (root, view) => {\n prefetchStrings('calendar', ['moreevents']);\n foldDayEvents();\n registerEventListeners(root, view);\n const calendarTable = root.find(CalendarSelectors.elements.monthDetailed);\n if (calendarTable.length) {\n const pendingId = `month-detailed-${calendarTable.id}-filterChanged`;\n registerEventListenersForMonthDetailed(calendarTable, pendingId);\n }\n};\n"],"names":["foldDayEvents","days","CalendarSelectors","elements","monthDetailed","find","day","length","each","dayContainer","this","eventsSelector","dateContent","filteredEventsSelector","moreEventsSelector","events","numberOfFiltered","numberOfEvents","count","event","attr","hide","show","moreEventsLink","numberOfHiddenEvents","then","str","link","text","fail","registerEventListenersForMonthDetailed","pendingId","CalendarEvents","viewUpdated","on","e","filterChanged","data","root","pending","Pending","target","eventType","type","transitionPromise","$","Deferred","hidden","promise","always","resolve","refreshMonthContent","year","month","courseId","categoryId","template","startLoading","wrapper","M","util","js_pending","get","join","includenavigation","mini","viewMode","CalendarRepository","getCalendarMonthData","context","Templates","render","html","js","replaceNode","document","querySelector","dispatchEvent","CustomEvent","js_complete","stopLoading","Notification","exception","changeMonth","url","updateUrl","args","trigger","monthChanged","courseChanged","refreshDayContent","getCalendarDayData","viewingday","showviewselector","changeDay","dayChanged","getElementById","fullCalendarView","window","history","pushState","containers","loadingIcon","removeClass","addClass","reloadCurrentUpcoming","getCalendarUpcomingData","viewingupcoming","renderEventSummaryModal","eventId","pendingPromise","getEventById","getEventResponse","Error","eventData","modalParams","title","name","SummaryModal","TYPE","body","templateContext","canedit","candelete","headerclasses","normalisedeventtype","isactionevent","action","ModalFactory","create","modal","getRoot","ModalEvents","destroy","catch","view","links","eventLink","matches","actions","viewEvent","closest","dataset","preventDefault","stopPropagation","navLink","currentTarget","href","viewSelector","define","CustomEvents","activate","option","classList","contains","courseid","categoryid","registerEventListeners","calendarTable","id"],"mappings":"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"}
\ No newline at end of file
+{"version":3,"file":"view_manager.min.js","sources":["../src/view_manager.js"],"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 * A javascript module to handler calendar view changes.\n *\n * @module core_calendar/view_manager\n * @copyright 2017 Andrew Nicols \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport $ from 'jquery';\nimport Templates from 'core/templates';\nimport Notification from 'core/notification';\nimport * as CalendarRepository from 'core_calendar/repository';\nimport CalendarEvents from 'core_calendar/events';\nimport * as CalendarSelectors from 'core_calendar/selectors';\nimport ModalFactory from 'core/modal_factory';\nimport ModalEvents from 'core/modal_events';\nimport SummaryModal from 'core_calendar/summary_modal';\nimport CustomEvents from 'core/custom_interaction_events';\nimport {get_string as getString} from 'core/str';\nimport Pending from 'core/pending';\nimport {prefetchStrings} from 'core/prefetch';\n\n/**\n * Limit number of events per day\n *\n */\nconst LIMIT_DAY_EVENTS = 5;\n\n/**\n * Hide day events if more than 5.\n *\n */\nexport const foldDayEvents = () => {\n const root = $(CalendarSelectors.elements.monthDetailed);\n const days = root.find(CalendarSelectors.day);\n if (days.length === 0) {\n return;\n }\n days.each(function() {\n const dayContainer = $(this);\n const eventsSelector = `${CalendarSelectors.elements.dateContent} ul li[data-event-eventtype]`;\n const filteredEventsSelector = `${CalendarSelectors.elements.dateContent} ul li[data-event-filtered=\"true\"]`;\n const moreEventsSelector = `${CalendarSelectors.elements.dateContent} [data-action=\"view-more-events\"]`;\n const events = dayContainer.find(eventsSelector);\n if (events.length === 0) {\n return;\n }\n\n const filteredEvents = dayContainer.find(filteredEventsSelector);\n const numberOfFiltered = filteredEvents.length;\n const numberOfEvents = events.length - numberOfFiltered;\n\n let count = 1;\n events.each(function() {\n const event = $(this);\n const isNotFiltered = event.attr('data-event-filtered') !== 'true';\n const offset = (numberOfEvents === LIMIT_DAY_EVENTS) ? 0 : 1;\n if (isNotFiltered) {\n if (count > LIMIT_DAY_EVENTS - offset) {\n event.attr('data-event-folded', 'true');\n event.hide();\n } else {\n event.attr('data-event-folded', 'false');\n event.show();\n count++;\n }\n } else {\n // It's being filtered out.\n event.attr('data-event-folded', 'false');\n }\n });\n\n const moreEventsLink = dayContainer.find(moreEventsSelector);\n if (numberOfEvents > LIMIT_DAY_EVENTS) {\n const numberOfHiddenEvents = numberOfEvents - LIMIT_DAY_EVENTS + 1;\n moreEventsLink.show();\n getString('moreevents', 'calendar', numberOfHiddenEvents).then(str => {\n const link = moreEventsLink.find('strong a');\n moreEventsLink.attr('data-event-folded', 'false');\n link.text(str);\n return str;\n }).fail();\n } else {\n moreEventsLink.hide();\n }\n });\n};\n\n/**\n * Register and handle month calendar events.\n *\n * @param {string} pendingId pending id.\n */\nexport const registerEventListenersForMonthDetailed = (pendingId) => {\n const events = `${CalendarEvents.viewUpdated}`;\n $('body').on(events, function(e) {\n foldDayEvents(e);\n });\n foldDayEvents();\n $('body').on(CalendarEvents.filterChanged, function(e, data) {\n const root = $(CalendarSelectors.elements.monthDetailed);\n const pending = new Pending(pendingId);\n const target = root.find(CalendarSelectors.eventType[data.type]);\n const transitionPromise = $.Deferred();\n if (data.hidden) {\n transitionPromise.then(function() {\n target.attr('data-event-filtered', 'true');\n return target.hide().promise();\n }).fail();\n } else {\n transitionPromise.then(function() {\n target.attr('data-event-filtered', 'false');\n return target.show().promise();\n }).fail();\n }\n\n transitionPromise.then(function() {\n foldDayEvents();\n return;\n })\n .always(pending.resolve)\n .fail();\n\n transitionPromise.resolve();\n });\n};\n\n/**\n * Register event listeners for the module.\n *\n * @param {object} root The root element.\n */\nconst registerEventListeners = (root) => {\n root = $(root);\n\n // Bind click events to event links.\n root.on('click', CalendarSelectors.links.eventLink, (e) => {\n const target = e.target;\n let eventLink = null;\n let eventId = null;\n const pendingPromise = new Pending('core_calendar/view_manager:eventLink:click');\n\n if (target.matches(CalendarSelectors.actions.viewEvent)) {\n eventLink = target;\n } else {\n eventLink = target.closest(CalendarSelectors.actions.viewEvent);\n }\n\n if (eventLink) {\n eventId = eventLink.dataset.eventId;\n } else {\n eventId = target.querySelector(CalendarSelectors.actions.viewEvent).dataset.eventId;\n }\n\n if (eventId) {\n // A link was found. Show the modal.\n\n e.preventDefault();\n // We've handled the event so stop it from bubbling\n // and causing the day click handler to fire.\n e.stopPropagation();\n\n renderEventSummaryModal(eventId)\n .then(pendingPromise.resolve)\n .catch();\n } else {\n pendingPromise.resolve();\n }\n });\n\n root.on('click', CalendarSelectors.links.navLink, (e) => {\n const wrapper = root.find(CalendarSelectors.wrapper);\n const view = wrapper.data('view');\n const courseId = wrapper.data('courseid');\n const categoryId = wrapper.data('categoryid');\n const link = e.currentTarget;\n\n if (view === 'month' || view === 'monthblock') {\n changeMonth(root, link.href, link.dataset.year, link.dataset.month, courseId, categoryId, link.dataset.day);\n e.preventDefault();\n } else if (view === 'day') {\n changeDay(root, link.href, link.dataset.year, link.dataset.month, link.dataset.day, courseId, categoryId);\n e.preventDefault();\n }\n });\n\n const viewSelector = root.find(CalendarSelectors.viewSelector);\n CustomEvents.define(viewSelector, [CustomEvents.events.activate]);\n viewSelector.on(\n CustomEvents.events.activate,\n (e) => {\n e.preventDefault();\n\n const option = e.target;\n if (option.classList.contains('active')) {\n return;\n }\n\n const view = option.dataset.view,\n year = option.dataset.year,\n month = option.dataset.month,\n day = option.dataset.day,\n courseId = option.dataset.courseid,\n categoryId = option.dataset.categoryid;\n\n if (view == 'month') {\n refreshMonthContent(root, year, month, courseId, categoryId, root, 'core_calendar/calendar_month', day)\n .then(() => {\n updateUrl('?view=month');\n }).fail(Notification.exception);\n } else if (view == 'day') {\n refreshDayContent(root, year, month, day, courseId, categoryId, root, 'core_calendar/calendar_day')\n .then(() => {\n updateUrl('?view=day');\n }).fail(Notification.exception);\n } else if (view == 'upcoming') {\n reloadCurrentUpcoming(root, courseId, categoryId, root, 'core_calendar/calendar_upcoming')\n .then(() => {\n updateUrl('?view=upcoming');\n }).fail(Notification.exception);\n }\n }\n );\n};\n\n/**\n * Refresh the month content.\n *\n * @param {object} root The root element.\n * @param {number} year Year\n * @param {number} month Month\n * @param {number} courseId The id of the course whose events are shown\n * @param {number} categoryId The id of the category whose events are shown\n * @param {object} target The element being replaced. If not specified, the calendarwrapper is used.\n * @param {string} template The template to be rendered.\n * @param {number} day Day (optional)\n * @return {promise}\n */\nexport const refreshMonthContent = (root, year, month, courseId, categoryId, target = null, template = '', day = 1) => {\n startLoading(root);\n\n target = target || root.find(CalendarSelectors.wrapper);\n template = template || root.attr('data-template');\n M.util.js_pending([root.get('id'), year, month, courseId].join('-'));\n const includenavigation = root.data('includenavigation');\n const mini = root.data('mini');\n const viewMode = target.data('view');\n return CalendarRepository.getCalendarMonthData(year, month, courseId, categoryId, includenavigation, mini, day, viewMode)\n .then(context => {\n return Templates.render(template, context);\n })\n .then((html, js) => {\n return Templates.replaceNode(target, html, js);\n })\n .then(() => {\n document.querySelector('body').dispatchEvent(new CustomEvent(CalendarEvents.viewUpdated));\n return;\n })\n .always(() => {\n M.util.js_complete([root.get('id'), year, month, courseId].join('-'));\n return stopLoading(root);\n })\n .fail(Notification.exception);\n};\n\n/**\n * Handle changes to the current calendar view.\n *\n * @param {object} root The container element\n * @param {string} url The calendar url to be shown\n * @param {number} year Year\n * @param {number} month Month\n * @param {number} courseId The id of the course whose events are shown\n * @param {number} categoryId The id of the category whose events are shown\n * @param {number} day Day (optional)\n * @return {promise}\n */\nexport const changeMonth = (root, url, year, month, courseId, categoryId, day = 1) => {\n return refreshMonthContent(root, year, month, courseId, categoryId, null, '', day)\n .then((...args) => {\n if (url.length && url !== '#') {\n updateUrl(url);\n }\n return args;\n })\n .then((...args) => {\n $('body').trigger(CalendarEvents.monthChanged, [year, month, courseId, categoryId]);\n return args;\n });\n};\n\n/**\n * Reload the current month view data.\n *\n * @param {object} root The container element.\n * @param {number} courseId The course id.\n * @param {number} categoryId The id of the category whose events are shown\n * @return {promise}\n */\nexport const reloadCurrentMonth = (root, courseId = 0, categoryId = 0) => {\n const year = root.find(CalendarSelectors.wrapper).data('year');\n const month = root.find(CalendarSelectors.wrapper).data('month');\n const day = root.find(CalendarSelectors.wrapper).data('day');\n\n courseId = courseId || root.find(CalendarSelectors.wrapper).data('courseid');\n categoryId = categoryId || root.find(CalendarSelectors.wrapper).data('categoryid');\n\n return refreshMonthContent(root, year, month, courseId, categoryId, null, '', day).\n then((...args) => {\n $('body').trigger(CalendarEvents.courseChanged, [year, month, courseId, categoryId]);\n return args;\n });\n};\n\n\n/**\n * Refresh the day content.\n *\n * @param {object} root The root element.\n * @param {number} year Year\n * @param {number} month Month\n * @param {number} day Day\n * @param {number} courseId The id of the course whose events are shown\n * @param {number} categoryId The id of the category whose events are shown\n * @param {object} target The element being replaced. If not specified, the calendarwrapper is used.\n * @param {string} template The template to be rendered.\n *\n * @return {promise}\n */\nexport const refreshDayContent = (root, year, month, day, courseId, categoryId, target = null, template = '') => {\n startLoading(root);\n\n if (!target || target.length == 0){\n target = root.find(CalendarSelectors.wrapper);\n }\n template = template || root.attr('data-template');\n M.util.js_pending([root.get('id'), year, month, day, courseId, categoryId].join('-'));\n const includenavigation = root.data('includenavigation');\n return CalendarRepository.getCalendarDayData(year, month, day, courseId, categoryId, includenavigation)\n .then((context) => {\n context.viewingday = true;\n context.showviewselector = true;\n return Templates.render(template, context);\n })\n .then((html, js) => {\n return Templates.replaceNode(target, html, js);\n })\n .then(() => {\n document.querySelector('body').dispatchEvent(new CustomEvent(CalendarEvents.viewUpdated));\n return;\n })\n .always(() => {\n M.util.js_complete([root.get('id'), year, month, day, courseId, categoryId].join('-'));\n return stopLoading(root);\n })\n .fail(Notification.exception);\n};\n\n/**\n * Reload the current day view data.\n *\n * @param {object} root The container element.\n * @param {number} courseId The course id.\n * @param {number} categoryId The id of the category whose events are shown\n * @return {promise}\n */\nexport const reloadCurrentDay = (root, courseId = 0, categoryId = 0) => {\n const wrapper = root.find(CalendarSelectors.wrapper);\n const year = wrapper.data('year');\n const month = wrapper.data('month');\n const day = wrapper.data('day');\n\n courseId = courseId || root.find(CalendarSelectors.wrapper).data('courseid');\n categoryId = categoryId || root.find(CalendarSelectors.wrapper).data('categoryid');\n\n return refreshDayContent(root, year, month, day, courseId, categoryId);\n};\n\n/**\n * Handle changes to the current calendar view.\n *\n * @param {object} root The root element.\n * @param {String} url The calendar url to be shown\n * @param {Number} year Year\n * @param {Number} month Month\n * @param {Number} day Day\n * @param {Number} courseId The id of the course whose events are shown\n * @param {Number} categoryId The id of the category whose events are shown\n * @return {promise}\n */\nexport const changeDay = (root, url, year, month, day, courseId, categoryId) => {\n return refreshDayContent(root, year, month, day, courseId, categoryId)\n .then((...args) => {\n if (url.length && url !== '#') {\n updateUrl(url);\n }\n return args;\n })\n .then((...args) => {\n $('body').trigger(CalendarEvents.dayChanged, [year, month, courseId, categoryId]);\n return args;\n });\n};\n\n/**\n * Update calendar URL.\n *\n * @param {String} url The calendar url to be updated.\n */\nexport const updateUrl = (url) => {\n const viewingFullCalendar = document.getElementById(CalendarSelectors.fullCalendarView);\n\n // We want to update the url only if the user is viewing the full calendar.\n if (viewingFullCalendar) {\n window.history.pushState({}, '', url);\n }\n};\n\n/**\n * Set the element state to loading.\n *\n * @param {object} root The container element\n * @method startLoading\n */\nconst startLoading = (root) => {\n const loadingIconContainer = root.find(CalendarSelectors.containers.loadingIcon);\n\n loadingIconContainer.removeClass('hidden');\n};\n\n/**\n * Remove the loading state from the element.\n *\n * @param {object} root The container element\n * @method stopLoading\n */\nconst stopLoading = (root) => {\n const loadingIconContainer = root.find(CalendarSelectors.containers.loadingIcon);\n\n loadingIconContainer.addClass('hidden');\n};\n\n/**\n * Reload the current month view data.\n *\n * @param {object} root The container element.\n * @param {number} courseId The course id.\n * @param {number} categoryId The id of the category whose events are shown\n * @param {object} target The element being replaced. If not specified, the calendarwrapper is used.\n * @param {string} template The template to be rendered.\n * @return {promise}\n */\nexport const reloadCurrentUpcoming = (root, courseId = 0, categoryId = 0, target = null, template = '') => {\n startLoading(root);\n\n target = target || root.find(CalendarSelectors.wrapper);\n template = template || root.attr('data-template');\n courseId = courseId || root.find(CalendarSelectors.wrapper).data('courseid');\n categoryId = categoryId || root.find(CalendarSelectors.wrapper).data('categoryid');\n\n return CalendarRepository.getCalendarUpcomingData(courseId, categoryId)\n .then((context) => {\n context.viewingupcoming = true;\n context.showviewselector = true;\n return Templates.render(template, context);\n })\n .then((html, js) => {\n return Templates.replaceNode(target, html, js);\n })\n .then(() => {\n document.querySelector('body').dispatchEvent(new CustomEvent(CalendarEvents.viewUpdated));\n return;\n })\n .always(function() {\n return stopLoading(root);\n })\n .fail(Notification.exception);\n};\n\n/**\n * Get the CSS class to apply for the given event type.\n *\n * @param {string} eventType The calendar event type\n * @return {string}\n */\nconst getEventTypeClassFromType = (eventType) => {\n return 'calendar_event_' + eventType;\n};\n\n/**\n * Render the event summary modal.\n *\n * @param {Number} eventId The calendar event id.\n * @returns {Promise}\n */\nconst renderEventSummaryModal = (eventId) => {\n const pendingPromise = new Pending('core_calendar/view_manager:renderEventSummaryModal');\n\n // Calendar repository promise.\n return CalendarRepository.getEventById(eventId)\n .then((getEventResponse) => {\n if (!getEventResponse.event) {\n throw new Error('Error encountered while trying to fetch calendar event with ID: ' + eventId);\n }\n\n return getEventResponse.event;\n })\n .then(eventData => {\n // Build the modal parameters from the event data.\n const modalParams = {\n title: eventData.name,\n type: SummaryModal.TYPE,\n body: Templates.render('core_calendar/event_summary_body', eventData),\n templateContext: {\n canedit: eventData.canedit,\n candelete: eventData.candelete,\n headerclasses: getEventTypeClassFromType(eventData.normalisedeventtype),\n isactionevent: eventData.isactionevent,\n url: eventData.url,\n action: eventData.action\n }\n };\n\n // Create the modal.\n return ModalFactory.create(modalParams);\n })\n .then(modal => {\n // Handle hidden event.\n modal.getRoot().on(ModalEvents.hidden, function() {\n // Destroy when hidden.\n modal.destroy();\n });\n\n // Finally, render the modal!\n modal.show();\n\n return modal;\n })\n .then(modal => {\n pendingPromise.resolve();\n\n return modal;\n })\n .catch(Notification.exception);\n};\n\nexport const init = (root, view) => {\n prefetchStrings('calendar', ['moreevents']);\n foldDayEvents();\n registerEventListeners(root, view);\n const calendarTable = root.find(CalendarSelectors.elements.monthDetailed);\n if (calendarTable.length) {\n const pendingId = `month-detailed-${calendarTable.id}-filterChanged`;\n registerEventListenersForMonthDetailed(calendarTable, pendingId);\n }\n};\n"],"names":["_getRequireWildcardCache","nodeInterop","WeakMap","cacheBabelInterop","cacheNodeInterop","_interopRequireWildcard","obj","__esModule","default","cache","has","get","newObj","hasPropertyDescriptor","Object","defineProperty","getOwnPropertyDescriptor","key","prototype","hasOwnProperty","call","desc","set","_interopRequireDefault","_jquery","_templates","_notification","CalendarRepository","_events","CalendarSelectors","_modal_factory","_modal_events","_summary_modal","_custom_interaction_events","_pending","foldDayEvents","days","$","elements","monthDetailed","find","day","length","each","dayContainer","this","eventsSelector","concat","dateContent","filteredEventsSelector","moreEventsSelector","events","numberOfFiltered","numberOfEvents","count","event","attr","hide","show","moreEventsLink","numberOfHiddenEvents","getString","get_string","then","str","link","text","fail","_exports","registerEventListenersForMonthDetailed","pendingId","CalendarEvents","viewUpdated","on","e","filterChanged","data","root","pending","Pending","target","eventType","type","transitionPromise","Deferred","hidden","promise","always","resolve","refreshMonthContent","year","month","courseId","categoryId","arguments","undefined","template","startLoading","wrapper","M","util","js_pending","join","includenavigation","mini","viewMode","getCalendarMonthData","context","Templates","render","html","js","replaceNode","document","querySelector","dispatchEvent","CustomEvent","js_complete","stopLoading","Notification","exception","changeMonth","url","updateUrl","_len","args","Array","_key","trigger","monthChanged","_len2","_key2","reloadCurrentMonth","courseChanged","_len3","_key3","refreshDayContent","getCalendarDayData","viewingday","showviewselector","reloadCurrentDay","changeDay","_len4","_key4","dayChanged","_len5","_key5","getElementById","fullCalendarView","window","history","pushState","containers","loadingIcon","removeClass","addClass","reloadCurrentUpcoming","getCalendarUpcomingData","viewingupcoming","renderEventSummaryModal","eventId","pendingPromise","getEventById","getEventResponse","Error","eventData","modalParams","title","name","SummaryModal","TYPE","body","templateContext","canedit","candelete","headerclasses","normalisedeventtype","isactionevent","action","ModalFactory","create","modal","getRoot","ModalEvents","destroy","catch","init","view","prefetchStrings","links","eventLink","matches","actions","viewEvent","closest","dataset","preventDefault","stopPropagation","navLink","currentTarget","href","viewSelector","CustomEvents","define","activate","option","classList","contains","courseid","categoryid","registerEventListeners","calendarTable","id"],"mappings":"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"}
\ No newline at end of file
diff --git a/comment/amd/build/admin.min.js.map b/comment/amd/build/admin.min.js.map
index b81deb29784..cc8d9ad12df 100644
--- a/comment/amd/build/admin.min.js.map
+++ b/comment/amd/build/admin.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"admin.min.js","sources":["../src/admin.js"],"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 * Comments admin management\n *\n * @module core_comment/admin\n * @copyright 2022 Paul Holden \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\n\"use strict\";\n\nimport {dispatchEvent} from 'core/event_dispatcher';\nimport Notification from 'core/notification';\nimport Pending from 'core/pending';\nimport {prefetchStrings} from 'core/prefetch';\nimport {get_string as getString} from 'core/str';\nimport {deleteComment, deleteComments} from 'core_comment/repository';\nimport * as reportEvents from 'core_reportbuilder/local/events';\nimport * as reportSelectors from 'core_reportbuilder/local/selectors';\n\nconst Selectors = {\n commentDelete: '[data-action=\"comment-delete\"]',\n commentDeleteChecked: '[data-togglegroup=\"report-select-all\"][data-toggle=\"slave\"]:checked',\n commentDeleteSelected: '[data-action=\"comment-delete-selected\"]',\n};\n\n/**\n * Initialise module\n */\nexport const init = () => {\n prefetchStrings('core_admin', [\n 'confirmdeletecomments',\n ]);\n\n prefetchStrings('core', [\n 'delete',\n 'deleteselected'\n ]);\n\n document.addEventListener('click', event => {\n const commentDelete = event.target.closest(Selectors.commentDelete);\n if (commentDelete) {\n event.preventDefault();\n\n // Use triggerElement to return focus to the action menu toggle.\n const triggerElement = commentDelete.closest('.dropdown').querySelector('.dropdown-toggle');\n Notification.saveCancelPromise(\n getString('delete', 'core'),\n getString('confirmdeletecomments', 'core_admin'),\n getString('delete', 'core'),\n {triggerElement}\n ).then(() => {\n const pendingPromise = new Pending('core_comment/comment:delete');\n const reportElement = event.target.closest(reportSelectors.regions.report);\n\n return deleteComment(commentDelete.dataset.commentId)\n .then(() => {\n dispatchEvent(reportEvents.tableReload, {preservePagination: true}, reportElement);\n return pendingPromise.resolve();\n })\n .catch(Notification.exception);\n }).catch(() => {\n return;\n });\n }\n\n const commentDeleteSelected = event.target.closest(Selectors.commentDeleteSelected);\n if (commentDeleteSelected) {\n event.preventDefault();\n\n const reportElement = document.querySelector(reportSelectors.regions.report);\n const commentDeleteChecked = reportElement.querySelectorAll(Selectors.commentDeleteChecked);\n if (commentDeleteChecked.length === 0) {\n return;\n }\n\n Notification.saveCancelPromise(\n getString('deleteselected', 'core'),\n getString('confirmdeletecomments', 'core_admin'),\n getString('delete', 'core'),\n {triggerElement: commentDeleteSelected}\n ).then(() => {\n const pendingPromise = new Pending('core_comment/comments:delete');\n const deleteCommentIds = [...commentDeleteChecked].map(check => check.value);\n\n return deleteComments(deleteCommentIds)\n .then(() => {\n dispatchEvent(reportEvents.tableReload, {preservePagination: true}, reportElement);\n return pendingPromise.resolve();\n })\n .catch(Notification.exception);\n }).catch(() => {\n return;\n });\n }\n });\n};\n"],"names":["Selectors","document","addEventListener","event","commentDelete","target","closest","preventDefault","triggerElement","querySelector","saveCancelPromise","then","pendingPromise","Pending","reportElement","reportSelectors","regions","report","dataset","commentId","reportEvents","tableReload","preservePagination","resolve","catch","Notification","exception","commentDeleteSelected","commentDeleteChecked","querySelectorAll","length","deleteCommentIds","map","check","value"],"mappings":"0nDAkCMA,wBACa,iCADbA,+BAEoB,sEAFpBA,gCAGqB,wDAMP,mCACA,aAAc,CAC1B,wDAGY,OAAQ,CACpB,SACA,mBAGJC,SAASC,iBAAiB,SAASC,cACzBC,cAAgBD,MAAME,OAAOC,QAAQN,4BACvCI,cAAe,CACfD,MAAMI,uBAGAC,eAAiBJ,cAAcE,QAAQ,aAAaG,cAAc,0CAC3DC,mBACT,mBAAU,SAAU,SACpB,mBAAU,wBAAyB,eACnC,mBAAU,SAAU,QACpB,CAACF,eAAAA,iBACHG,MAAK,WACGC,eAAiB,IAAIC,iBAAQ,+BAC7BC,cAAgBX,MAAME,OAAOC,QAAQS,gBAAgBC,QAAQC,eAE5D,6BAAcb,cAAcc,QAAQC,WACtCR,MAAK,yCACYS,aAAaC,YAAa,CAACC,oBAAoB,GAAOR,eAC7DF,eAAeW,aAEzBC,MAAMC,sBAAaC,cACzBF,OAAM,eAKPG,sBAAwBxB,MAAME,OAAOC,QAAQN,oCAC/C2B,sBAAuB,CACvBxB,MAAMI,uBAEAO,cAAgBb,SAASQ,cAAcM,gBAAgBC,QAAQC,QAC/DW,qBAAuBd,cAAce,iBAAiB7B,mCACxB,IAAhC4B,qBAAqBE,oCAIZpB,mBACT,mBAAU,iBAAkB,SAC5B,mBAAU,wBAAyB,eACnC,mBAAU,SAAU,QACpB,CAACF,eAAgBmB,wBACnBhB,MAAK,WACGC,eAAiB,IAAIC,iBAAQ,gCAC7BkB,iBAAmB,IAAIH,sBAAsBI,KAAIC,OAASA,MAAMC,eAE/D,8BAAeH,kBACjBpB,MAAK,yCACYS,aAAaC,YAAa,CAACC,oBAAoB,GAAOR,eAC7DF,eAAeW,aAEzBC,MAAMC,sBAAaC,cACzBF,OAAM"}
\ No newline at end of file
+{"version":3,"file":"admin.min.js","sources":["../src/admin.js"],"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 * Comments admin management\n *\n * @module core_comment/admin\n * @copyright 2022 Paul Holden \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\n\"use strict\";\n\nimport {dispatchEvent} from 'core/event_dispatcher';\nimport Notification from 'core/notification';\nimport Pending from 'core/pending';\nimport {prefetchStrings} from 'core/prefetch';\nimport {get_string as getString} from 'core/str';\nimport {deleteComment, deleteComments} from 'core_comment/repository';\nimport * as reportEvents from 'core_reportbuilder/local/events';\nimport * as reportSelectors from 'core_reportbuilder/local/selectors';\n\nconst Selectors = {\n commentDelete: '[data-action=\"comment-delete\"]',\n commentDeleteChecked: '[data-togglegroup=\"report-select-all\"][data-toggle=\"slave\"]:checked',\n commentDeleteSelected: '[data-action=\"comment-delete-selected\"]',\n};\n\n/**\n * Initialise module\n */\nexport const init = () => {\n prefetchStrings('core_admin', [\n 'confirmdeletecomments',\n ]);\n\n prefetchStrings('core', [\n 'delete',\n 'deleteselected'\n ]);\n\n document.addEventListener('click', event => {\n const commentDelete = event.target.closest(Selectors.commentDelete);\n if (commentDelete) {\n event.preventDefault();\n\n // Use triggerElement to return focus to the action menu toggle.\n const triggerElement = commentDelete.closest('.dropdown').querySelector('.dropdown-toggle');\n Notification.saveCancelPromise(\n getString('delete', 'core'),\n getString('confirmdeletecomments', 'core_admin'),\n getString('delete', 'core'),\n {triggerElement}\n ).then(() => {\n const pendingPromise = new Pending('core_comment/comment:delete');\n const reportElement = event.target.closest(reportSelectors.regions.report);\n\n return deleteComment(commentDelete.dataset.commentId)\n .then(() => {\n dispatchEvent(reportEvents.tableReload, {preservePagination: true}, reportElement);\n return pendingPromise.resolve();\n })\n .catch(Notification.exception);\n }).catch(() => {\n return;\n });\n }\n\n const commentDeleteSelected = event.target.closest(Selectors.commentDeleteSelected);\n if (commentDeleteSelected) {\n event.preventDefault();\n\n const reportElement = document.querySelector(reportSelectors.regions.report);\n const commentDeleteChecked = reportElement.querySelectorAll(Selectors.commentDeleteChecked);\n if (commentDeleteChecked.length === 0) {\n return;\n }\n\n Notification.saveCancelPromise(\n getString('deleteselected', 'core'),\n getString('confirmdeletecomments', 'core_admin'),\n getString('delete', 'core'),\n {triggerElement: commentDeleteSelected}\n ).then(() => {\n const pendingPromise = new Pending('core_comment/comments:delete');\n const deleteCommentIds = [...commentDeleteChecked].map(check => check.value);\n\n return deleteComments(deleteCommentIds)\n .then(() => {\n dispatchEvent(reportEvents.tableReload, {preservePagination: true}, reportElement);\n return pendingPromise.resolve();\n })\n .catch(Notification.exception);\n }).catch(() => {\n return;\n });\n }\n });\n};\n"],"names":["_getRequireWildcardCache","nodeInterop","WeakMap","cacheBabelInterop","cacheNodeInterop","_interopRequireWildcard","obj","__esModule","default","cache","has","get","newObj","hasPropertyDescriptor","Object","defineProperty","getOwnPropertyDescriptor","key","prototype","hasOwnProperty","call","desc","set","_interopRequireDefault","_exports","value","init","_notification","_pending","reportEvents","reportSelectors","Selectors","prefetchStrings","document","addEventListener","event","commentDelete","target","closest","preventDefault","triggerElement","querySelector","Notification","saveCancelPromise","getString","get_string","then","pendingPromise","Pending","reportElement","regions","report","deleteComment","dataset","commentId","dispatchEvent","tableReload","preservePagination","resolve","catch","exception","commentDeleteSelected","commentDeleteChecked","querySelectorAll","length","deleteCommentIds","map","check","deleteComments"],"mappings":"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"}
\ No newline at end of file
diff --git a/comment/amd/build/repository.min.js.map b/comment/amd/build/repository.min.js.map
index b9c93b8b188..69613f00ea2 100644
--- a/comment/amd/build/repository.min.js.map
+++ b/comment/amd/build/repository.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"repository.min.js","sources":["../src/repository.js"],"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 * Module to handle comment AJAX requests\n *\n * @module core_comment/repository\n * @copyright 2022 Paul Holden \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Ajax from 'core/ajax';\n\n/**\n * Delete single comment\n *\n * @param {Number} comment Comment ID\n * @return {Promise}\n */\nexport const deleteComment = comment => deleteComments([comment]);\n\n/**\n * Delete multiple comments\n *\n * @param {Number[]} comments Comment IDs\n * @return {Promise}\n */\nexport const deleteComments = comments => {\n const request = {\n methodname: 'core_comment_delete_comments',\n args: {comments}\n };\n\n return Ajax.call([request])[0];\n};\n"],"names":["comment","deleteComments","comments","request","methodname","args","Ajax","call"],"mappings":";;;;;;;8LA+B6BA,SAAWC,eAAe,CAACD,gBAQ3CC,eAAiBC,iBACpBC,QAAU,CACZC,WAAY,+BACZC,KAAM,CAACH,SAAAA,kBAGJI,cAAKC,KAAK,CAACJ,UAAU"}
\ No newline at end of file
+{"version":3,"file":"repository.min.js","sources":["../src/repository.js"],"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 * Module to handle comment AJAX requests\n *\n * @module core_comment/repository\n * @copyright 2022 Paul Holden \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Ajax from 'core/ajax';\n\n/**\n * Delete single comment\n *\n * @param {Number} comment Comment ID\n * @return {Promise}\n */\nexport const deleteComment = comment => deleteComments([comment]);\n\n/**\n * Delete multiple comments\n *\n * @param {Number[]} comments Comment IDs\n * @return {Promise}\n */\nexport const deleteComments = comments => {\n const request = {\n methodname: 'core_comment_delete_comments',\n args: {comments}\n };\n\n return Ajax.call([request])[0];\n};\n"],"names":["obj","_ajax","__esModule","default","_exports","deleteComment","comment","deleteComments","comments","request","methodname","args","Ajax","call"],"mappings":"mFAuB6B,IAAAA;;;;;;;mHAA7BC,OAA6BD,IAA7BC,QAA6BD,IAAAE,WAAAF,IAAAG,CAAAA,QAAAH,KAQqCI,SAAAC,cAArCC,SAAWC,eAAe,CAACD,UAQjD,MAAMC,eAAiBC,WAC1B,MAAMC,QAAU,CACZC,WAAY,+BACZC,KAAM,CAACH,oBAGX,OAAOI,MAAAA,QAAKC,KAAK,CAACJ,UAAU,EAAE,EAChCL,SAAAG,eAAAA,cAAA"}
\ No newline at end of file
diff --git a/contentbank/amd/build/actions.min.js.map b/contentbank/amd/build/actions.min.js.map
index 838fd731c85..7ab90375efa 100644
--- a/contentbank/amd/build/actions.min.js.map
+++ b/contentbank/amd/build/actions.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"actions.min.js","sources":["../src/actions.js"],"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 * Module to manage content bank actions, such as delete or rename.\n *\n * @module core_contentbank/actions\n * @copyright 2020 Sara Arjona \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine([\n 'jquery',\n 'core/ajax',\n 'core/notification',\n 'core/str',\n 'core/templates',\n 'core/url',\n 'core/modal_factory',\n 'core/modal_events'],\nfunction($, Ajax, Notification, Str, Templates, Url, ModalFactory, ModalEvents) {\n\n /**\n * List of action selectors.\n *\n * @type {{DELETE_CONTENT: string}}\n */\n var ACTIONS = {\n DELETE_CONTENT: '[data-action=\"deletecontent\"]',\n RENAME_CONTENT: '[data-action=\"renamecontent\"]',\n SET_CONTENT_VISIBILITY: '[data-action=\"setcontentvisibility\"]',\n };\n\n /**\n * Actions class.\n */\n var Actions = function() {\n this.registerEvents();\n };\n\n /**\n * Register event listeners.\n */\n Actions.prototype.registerEvents = function() {\n $(ACTIONS.DELETE_CONTENT).click(function(e) {\n e.preventDefault();\n\n var contentname = $(this).data('contentname');\n var contentuses = $(this).data('uses');\n var contentid = $(this).data('contentid');\n var contextid = $(this).data('contextid');\n\n var strings = [\n {\n key: 'deletecontent',\n component: 'core_contentbank'\n },\n {\n key: 'deletecontentconfirm',\n component: 'core_contentbank',\n param: {\n name: contentname,\n }\n },\n {\n key: 'deletecontentconfirmlinked',\n component: 'core_contentbank',\n },\n {\n key: 'delete',\n component: 'core'\n },\n ];\n\n var deleteButtonText = '';\n Str.get_strings(strings).then(function(langStrings) {\n var modalTitle = langStrings[0];\n var modalContent = langStrings[1];\n if (contentuses > 0) {\n modalContent += ' ' + langStrings[2];\n }\n deleteButtonText = langStrings[3];\n\n return ModalFactory.create({\n title: modalTitle,\n body: modalContent,\n type: ModalFactory.types.SAVE_CANCEL,\n large: true\n });\n }).done(function(modal) {\n modal.setSaveButtonText(deleteButtonText);\n modal.getRoot().on(ModalEvents.save, function() {\n // The action is now confirmed, sending an action for it.\n return deleteContent(contentid, contextid);\n });\n\n // Handle hidden event.\n modal.getRoot().on(ModalEvents.hidden, function() {\n // Destroy when hidden.\n modal.destroy();\n });\n\n // Show the modal.\n modal.show();\n\n return;\n }).catch(Notification.exception);\n });\n\n $(ACTIONS.RENAME_CONTENT).click(function(e) {\n e.preventDefault();\n\n var contentname = $(this).data('contentname');\n var contentid = $(this).data('contentid');\n\n var strings = [\n {\n key: 'renamecontent',\n component: 'core_contentbank'\n },\n {\n key: 'rename',\n component: 'core_contentbank'\n },\n ];\n\n var saveButtonText = '';\n Str.get_strings(strings).then(function(langStrings) {\n var modalTitle = langStrings[0];\n saveButtonText = langStrings[1];\n\n return ModalFactory.create({\n title: modalTitle,\n body: Templates.render('core_contentbank/renamecontent', {'contentid': contentid, 'name': contentname}),\n type: ModalFactory.types.SAVE_CANCEL\n });\n }).then(function(modal) {\n modal.setSaveButtonText(saveButtonText);\n modal.getRoot().on(ModalEvents.save, function(e) {\n // The action is now confirmed, sending an action for it.\n var newname = $(\"#newname\").val().trim();\n if (newname) {\n renameContent(contentid, newname);\n } else {\n var errorStrings = [\n {\n key: 'error',\n },\n {\n key: 'emptynamenotallowed',\n component: 'core_contentbank',\n },\n ];\n Str.get_strings(errorStrings).then(function(langStrings) {\n Notification.alert(langStrings[0], langStrings[1]);\n }).catch(Notification.exception);\n e.preventDefault();\n }\n });\n\n // Handle hidden event.\n modal.getRoot().on(ModalEvents.hidden, function() {\n // Destroy when hidden.\n modal.destroy();\n });\n\n // Show the modal.\n modal.show();\n\n return;\n }).catch(Notification.exception);\n });\n\n $(ACTIONS.SET_CONTENT_VISIBILITY).click(function(e) {\n e.preventDefault();\n\n var contentid = $(this).data('contentid');\n var visibility = $(this).data('visibility');\n\n setContentVisibility(contentid, visibility);\n });\n };\n\n /**\n * Delete content from the content bank.\n *\n * @param {int} contentid The content to delete.\n * @param {int} contextid The contextid where the content belongs.\n */\n function deleteContent(contentid, contextid) {\n var request = {\n methodname: 'core_contentbank_delete_content',\n args: {\n contentids: {contentid}\n }\n };\n\n var requestType = 'success';\n Ajax.call([request])[0].then(function(data) {\n if (data.result) {\n return 'contentdeleted';\n }\n requestType = 'error';\n return 'contentnotdeleted';\n\n }).done(function(message) {\n var params = {\n contextid: contextid\n };\n if (requestType == 'success') {\n params.statusmsg = message;\n } else {\n params.errormsg = message;\n }\n // Redirect to the main content bank page and display the message as a notification.\n window.location.href = Url.relativeUrl('contentbank/index.php', params, false);\n }).fail(Notification.exception);\n }\n\n /**\n * Rename content in the content bank.\n *\n * @param {int} contentid The content to rename.\n * @param {string} name The new name for the content.\n */\n function renameContent(contentid, name) {\n var request = {\n methodname: 'core_contentbank_rename_content',\n args: {\n contentid: contentid,\n name: name\n }\n };\n var requestType = 'success';\n Ajax.call([request])[0].then(function(data) {\n if (data.result) {\n return 'contentrenamed';\n }\n requestType = 'error';\n return data.warnings[0].message;\n\n }).then(function(message) {\n var params = null;\n if (requestType == 'success') {\n params = {\n id: contentid,\n statusmsg: message\n };\n // Redirect to the content view page and display the message as a notification.\n window.location.href = Url.relativeUrl('contentbank/view.php', params, false);\n } else {\n // Fetch error notifications.\n Notification.addNotification({\n message: message,\n type: 'error'\n });\n Notification.fetchNotifications();\n }\n return;\n }).catch(Notification.exception);\n }\n\n /**\n * Set content visibility in the content bank.\n *\n * @param {int} contentid The content to modify\n * @param {int} visibility The new visibility value\n */\n function setContentVisibility(contentid, visibility) {\n var request = {\n methodname: 'core_contentbank_set_content_visibility',\n args: {\n contentid: contentid,\n visibility: visibility\n }\n };\n var requestType = 'success';\n Ajax.call([request])[0].then(function(data) {\n if (data.result) {\n return 'contentvisibilitychanged';\n }\n requestType = 'error';\n return data.warnings[0].message;\n\n }).then(function(message) {\n var params = null;\n if (requestType == 'success') {\n params = {\n id: contentid,\n statusmsg: message\n };\n // Redirect to the content view page and display the message as a notification.\n window.location.href = Url.relativeUrl('contentbank/view.php', params, false);\n } else {\n // Fetch error notifications.\n Notification.addNotification({\n message: message,\n type: 'error'\n });\n Notification.fetchNotifications();\n }\n return;\n }).catch(Notification.exception);\n }\n\n return /** @alias module:core_contentbank/actions */ {\n // Public variables and functions.\n\n /**\n * Initialise the contentbank actions.\n *\n * @method init\n * @return {Actions}\n */\n 'init': function() {\n return new Actions();\n }\n };\n});\n"],"names":["define","$","Ajax","Notification","Str","Templates","Url","ModalFactory","ModalEvents","ACTIONS","Actions","registerEvents","prototype","click","e","preventDefault","contentname","this","data","contentuses","contentid","contextid","strings","key","component","param","name","deleteButtonText","get_strings","then","langStrings","modalTitle","modalContent","create","title","body","type","types","SAVE_CANCEL","large","done","modal","setSaveButtonText","getRoot","on","save","request","methodname","args","contentids","requestType","call","result","message","params","statusmsg","errormsg","window","location","href","relativeUrl","fail","exception","deleteContent","hidden","destroy","show","catch","saveButtonText","render","newname","val","trim","warnings","id","addNotification","fetchNotifications","renameContent","alert","visibility","setContentVisibility"],"mappings":"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"}
\ No newline at end of file
+{"version":3,"file":"actions.min.js","sources":["../src/actions.js"],"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 * Module to manage content bank actions, such as delete or rename.\n *\n * @module core_contentbank/actions\n * @copyright 2020 Sara Arjona \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine([\n 'jquery',\n 'core/ajax',\n 'core/notification',\n 'core/str',\n 'core/templates',\n 'core/url',\n 'core/modal_factory',\n 'core/modal_events'],\nfunction($, Ajax, Notification, Str, Templates, Url, ModalFactory, ModalEvents) {\n\n /**\n * List of action selectors.\n *\n * @type {{DELETE_CONTENT: string}}\n */\n var ACTIONS = {\n DELETE_CONTENT: '[data-action=\"deletecontent\"]',\n RENAME_CONTENT: '[data-action=\"renamecontent\"]',\n SET_CONTENT_VISIBILITY: '[data-action=\"setcontentvisibility\"]',\n };\n\n /**\n * Actions class.\n */\n var Actions = function() {\n this.registerEvents();\n };\n\n /**\n * Register event listeners.\n */\n Actions.prototype.registerEvents = function() {\n $(ACTIONS.DELETE_CONTENT).click(function(e) {\n e.preventDefault();\n\n var contentname = $(this).data('contentname');\n var contentuses = $(this).data('uses');\n var contentid = $(this).data('contentid');\n var contextid = $(this).data('contextid');\n\n var strings = [\n {\n key: 'deletecontent',\n component: 'core_contentbank'\n },\n {\n key: 'deletecontentconfirm',\n component: 'core_contentbank',\n param: {\n name: contentname,\n }\n },\n {\n key: 'deletecontentconfirmlinked',\n component: 'core_contentbank',\n },\n {\n key: 'delete',\n component: 'core'\n },\n ];\n\n var deleteButtonText = '';\n Str.get_strings(strings).then(function(langStrings) {\n var modalTitle = langStrings[0];\n var modalContent = langStrings[1];\n if (contentuses > 0) {\n modalContent += ' ' + langStrings[2];\n }\n deleteButtonText = langStrings[3];\n\n return ModalFactory.create({\n title: modalTitle,\n body: modalContent,\n type: ModalFactory.types.SAVE_CANCEL,\n large: true\n });\n }).done(function(modal) {\n modal.setSaveButtonText(deleteButtonText);\n modal.getRoot().on(ModalEvents.save, function() {\n // The action is now confirmed, sending an action for it.\n return deleteContent(contentid, contextid);\n });\n\n // Handle hidden event.\n modal.getRoot().on(ModalEvents.hidden, function() {\n // Destroy when hidden.\n modal.destroy();\n });\n\n // Show the modal.\n modal.show();\n\n return;\n }).catch(Notification.exception);\n });\n\n $(ACTIONS.RENAME_CONTENT).click(function(e) {\n e.preventDefault();\n\n var contentname = $(this).data('contentname');\n var contentid = $(this).data('contentid');\n\n var strings = [\n {\n key: 'renamecontent',\n component: 'core_contentbank'\n },\n {\n key: 'rename',\n component: 'core_contentbank'\n },\n ];\n\n var saveButtonText = '';\n Str.get_strings(strings).then(function(langStrings) {\n var modalTitle = langStrings[0];\n saveButtonText = langStrings[1];\n\n return ModalFactory.create({\n title: modalTitle,\n body: Templates.render('core_contentbank/renamecontent', {'contentid': contentid, 'name': contentname}),\n type: ModalFactory.types.SAVE_CANCEL\n });\n }).then(function(modal) {\n modal.setSaveButtonText(saveButtonText);\n modal.getRoot().on(ModalEvents.save, function(e) {\n // The action is now confirmed, sending an action for it.\n var newname = $(\"#newname\").val().trim();\n if (newname) {\n renameContent(contentid, newname);\n } else {\n var errorStrings = [\n {\n key: 'error',\n },\n {\n key: 'emptynamenotallowed',\n component: 'core_contentbank',\n },\n ];\n Str.get_strings(errorStrings).then(function(langStrings) {\n Notification.alert(langStrings[0], langStrings[1]);\n }).catch(Notification.exception);\n e.preventDefault();\n }\n });\n\n // Handle hidden event.\n modal.getRoot().on(ModalEvents.hidden, function() {\n // Destroy when hidden.\n modal.destroy();\n });\n\n // Show the modal.\n modal.show();\n\n return;\n }).catch(Notification.exception);\n });\n\n $(ACTIONS.SET_CONTENT_VISIBILITY).click(function(e) {\n e.preventDefault();\n\n var contentid = $(this).data('contentid');\n var visibility = $(this).data('visibility');\n\n setContentVisibility(contentid, visibility);\n });\n };\n\n /**\n * Delete content from the content bank.\n *\n * @param {int} contentid The content to delete.\n * @param {int} contextid The contextid where the content belongs.\n */\n function deleteContent(contentid, contextid) {\n var request = {\n methodname: 'core_contentbank_delete_content',\n args: {\n contentids: {contentid}\n }\n };\n\n var requestType = 'success';\n Ajax.call([request])[0].then(function(data) {\n if (data.result) {\n return 'contentdeleted';\n }\n requestType = 'error';\n return 'contentnotdeleted';\n\n }).done(function(message) {\n var params = {\n contextid: contextid\n };\n if (requestType == 'success') {\n params.statusmsg = message;\n } else {\n params.errormsg = message;\n }\n // Redirect to the main content bank page and display the message as a notification.\n window.location.href = Url.relativeUrl('contentbank/index.php', params, false);\n }).fail(Notification.exception);\n }\n\n /**\n * Rename content in the content bank.\n *\n * @param {int} contentid The content to rename.\n * @param {string} name The new name for the content.\n */\n function renameContent(contentid, name) {\n var request = {\n methodname: 'core_contentbank_rename_content',\n args: {\n contentid: contentid,\n name: name\n }\n };\n var requestType = 'success';\n Ajax.call([request])[0].then(function(data) {\n if (data.result) {\n return 'contentrenamed';\n }\n requestType = 'error';\n return data.warnings[0].message;\n\n }).then(function(message) {\n var params = null;\n if (requestType == 'success') {\n params = {\n id: contentid,\n statusmsg: message\n };\n // Redirect to the content view page and display the message as a notification.\n window.location.href = Url.relativeUrl('contentbank/view.php', params, false);\n } else {\n // Fetch error notifications.\n Notification.addNotification({\n message: message,\n type: 'error'\n });\n Notification.fetchNotifications();\n }\n return;\n }).catch(Notification.exception);\n }\n\n /**\n * Set content visibility in the content bank.\n *\n * @param {int} contentid The content to modify\n * @param {int} visibility The new visibility value\n */\n function setContentVisibility(contentid, visibility) {\n var request = {\n methodname: 'core_contentbank_set_content_visibility',\n args: {\n contentid: contentid,\n visibility: visibility\n }\n };\n var requestType = 'success';\n Ajax.call([request])[0].then(function(data) {\n if (data.result) {\n return 'contentvisibilitychanged';\n }\n requestType = 'error';\n return data.warnings[0].message;\n\n }).then(function(message) {\n var params = null;\n if (requestType == 'success') {\n params = {\n id: contentid,\n statusmsg: message\n };\n // Redirect to the content view page and display the message as a notification.\n window.location.href = Url.relativeUrl('contentbank/view.php', params, false);\n } else {\n // Fetch error notifications.\n Notification.addNotification({\n message: message,\n type: 'error'\n });\n Notification.fetchNotifications();\n }\n return;\n }).catch(Notification.exception);\n }\n\n return /** @alias module:core_contentbank/actions */ {\n // Public variables and functions.\n\n /**\n * Initialise the contentbank actions.\n *\n * @method init\n * @return {Actions}\n */\n 'init': function() {\n return new Actions();\n }\n };\n});\n"],"names":["define","$","Ajax","Notification","Str","Templates","Url","ModalFactory","ModalEvents","ACTIONS","Actions","this","registerEvents","prototype","click","e","preventDefault","contentname","data","contentuses","contentid","contextid","strings","key","component","param","name","deleteButtonText","get_strings","then","langStrings","modalTitle","modalContent","create","title","body","type","types","SAVE_CANCEL","large","done","modal","setSaveButtonText","getRoot","on","save","request","methodname","args","contentids","requestType","call","result","message","params","statusmsg","errormsg","window","location","href","relativeUrl","fail","exception","deleteContent","hidden","destroy","show","catch","saveButtonText","render","newname","val","trim","warnings","id","addNotification","fetchNotifications","renameContent","alert","visibility","setContentVisibility","init"],"mappings":"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"}
\ No newline at end of file
diff --git a/contentbank/amd/build/search.min.js.map b/contentbank/amd/build/search.min.js.map
index f1f44cb4aca..f6e62d20003 100644
--- a/contentbank/amd/build/search.min.js.map
+++ b/contentbank/amd/build/search.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"search.min.js","sources":["../src/search.js"],"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 * Search methods for finding contents in the content bank.\n *\n * @module core_contentbank/search\n * @copyright 2020 Sara Arjona \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport $ from 'jquery';\nimport selectors from 'core_contentbank/selectors';\nimport {get_string as getString} from 'core/str';\nimport Pending from 'core/pending';\nimport {debounce} from 'core/utils';\n\n/**\n * Set up the search.\n *\n * @method init\n */\nexport const init = () => {\n const pendingPromise = new Pending();\n\n const root = $(selectors.regions.contentbank);\n registerListenerEvents(root);\n\n pendingPromise.resolve();\n};\n\n/**\n * Register contentbank search related event listeners.\n *\n * @method registerListenerEvents\n * @param {Object} root The root element for the contentbank.\n */\nconst registerListenerEvents = (root) => {\n\n const searchInput = root.find(selectors.elements.searchinput)[0];\n\n root.on('click', selectors.actions.search, function(e) {\n e.preventDefault();\n toggleSearchResultsView(root, searchInput.value);\n });\n\n root.on('click', selectors.actions.clearSearch, function(e) {\n e.preventDefault();\n searchInput.value = \"\";\n searchInput.focus();\n toggleSearchResultsView(root, searchInput.value);\n });\n\n // The search input is also triggered.\n searchInput.addEventListener('input', debounce(() => {\n // Display the search results.\n toggleSearchResultsView(root, searchInput.value);\n }, 300));\n\n};\n\n/**\n * Toggle (display/hide) the search results depending on the value of the search query.\n *\n * @method toggleSearchResultsView\n * @param {HTMLElement} body The root element for the contentbank.\n * @param {String} searchQuery The search query.\n */\nconst toggleSearchResultsView = async(body, searchQuery) => {\n const clearSearchButton = body.find(selectors.actions.clearSearch)[0];\n\n const navbarBreadcrumb = body.find(selectors.elements.cbnavbarbreadcrumb)[0];\n const navbarTotal = body.find(selectors.elements.cbnavbartotalsearch)[0];\n // Update the results.\n const filteredContents = filterContents(body, searchQuery);\n if (searchQuery.length > 0) {\n // As the search query is present, search results should be displayed.\n\n // Display the \"clear\" search button in the activity chooser search bar.\n clearSearchButton.classList.remove('d-none');\n\n // Change the cb-navbar to display total items found.\n navbarBreadcrumb.classList.add('d-none');\n navbarTotal.innerHTML = await getString('itemsfound', 'core_contentbank', filteredContents.length);\n navbarTotal.classList.remove('d-none');\n } else {\n // As search query is not present, the search results should be removed.\n\n // Hide the \"clear\" search button in the activity chooser search bar.\n clearSearchButton.classList.add('d-none');\n\n // Display again the breadcrumb in the navbar.\n navbarBreadcrumb.classList.remove('d-none');\n navbarTotal.classList.add('d-none');\n }\n};\n\n/**\n * Return the list of contents which have a name that matches the given search term.\n *\n * @method filterContents\n * @param {HTMLElement} body The root element for the contentbank.\n * @param {String} searchTerm The search term to match.\n * @return {Array}\n */\nconst filterContents = (body, searchTerm) => {\n const contents = Array.from(body.find(selectors.elements.listitem));\n const searchResults = [];\n contents.forEach((content) => {\n const contentName = content.getAttribute('data-name');\n if (searchTerm === '' || contentName.toLowerCase().includes(searchTerm.toLowerCase())) {\n // The content matches the search criteria so it should be displayed and hightlighted.\n searchResults.push(content);\n const contentNameElement = content.querySelector(selectors.regions.cbcontentname);\n contentNameElement.innerHTML = highlight(contentName, searchTerm);\n content.classList.remove('d-none');\n } else {\n content.classList.add('d-none');\n }\n });\n\n return searchResults;\n};\n\n/**\n * Highlight a given string in a text.\n *\n * @method highlight\n * @param {String} text The whole text.\n * @param {String} highlightText The piece of text to highlight.\n * @return {String}\n */\nconst highlight = (text, highlightText) => {\n let result = text;\n if (highlightText !== '') {\n const pos = text.toLowerCase().indexOf(highlightText.toLowerCase());\n if (pos > -1) {\n result = text.substr(0, pos) + '' + text.substr(pos, highlightText.length) + '' +\n text.substr(pos + highlightText.length);\n }\n }\n\n return result;\n};\n"],"names":["pendingPromise","Pending","root","selectors","regions","contentbank","registerListenerEvents","resolve","searchInput","find","elements","searchinput","on","actions","search","e","preventDefault","toggleSearchResultsView","value","clearSearch","focus","addEventListener","async","body","searchQuery","clearSearchButton","navbarBreadcrumb","cbnavbarbreadcrumb","navbarTotal","cbnavbartotalsearch","filteredContents","filterContents","length","classList","remove","add","innerHTML","searchTerm","contents","Array","from","listitem","searchResults","forEach","content","contentName","getAttribute","toLowerCase","includes","push","querySelector","cbcontentname","highlight","text","highlightText","result","pos","indexOf","substr"],"mappings":";;;;;;;gOAkCoB,WACVA,eAAiB,IAAIC,iBAErBC,MAAO,mBAAEC,mBAAUC,QAAQC,aACjCC,uBAAuBJ,MAEvBF,eAAeO,iBASbD,uBAA0BJ,aAEtBM,YAAcN,KAAKO,KAAKN,mBAAUO,SAASC,aAAa,GAE9DT,KAAKU,GAAG,QAAST,mBAAUU,QAAQC,QAAQ,SAASC,GAChDA,EAAEC,iBACFC,wBAAwBf,KAAMM,YAAYU,UAG9ChB,KAAKU,GAAG,QAAST,mBAAUU,QAAQM,aAAa,SAASJ,GACrDA,EAAEC,iBACFR,YAAYU,MAAQ,GACpBV,YAAYY,QACZH,wBAAwBf,KAAMM,YAAYU,UAI9CV,YAAYa,iBAAiB,SAAS,oBAAS,KAE3CJ,wBAAwBf,KAAMM,YAAYU,SAC3C,OAWDD,wBAA0BK,MAAMC,KAAMC,qBAClCC,kBAAoBF,KAAKd,KAAKN,mBAAUU,QAAQM,aAAa,GAE7DO,iBAAmBH,KAAKd,KAAKN,mBAAUO,SAASiB,oBAAoB,GACpEC,YAAcL,KAAKd,KAAKN,mBAAUO,SAASmB,qBAAqB,GAEhEC,iBAAmBC,eAAeR,KAAMC,aAC1CA,YAAYQ,OAAS,GAIrBP,kBAAkBQ,UAAUC,OAAO,UAGnCR,iBAAiBO,UAAUE,IAAI,UAC/BP,YAAYQ,gBAAkB,mBAAU,aAAc,mBAAoBN,iBAAiBE,QAC3FJ,YAAYK,UAAUC,OAAO,YAK7BT,kBAAkBQ,UAAUE,IAAI,UAGhCT,iBAAiBO,UAAUC,OAAO,UAClCN,YAAYK,UAAUE,IAAI,YAY5BJ,eAAiB,CAACR,KAAMc,oBACpBC,SAAWC,MAAMC,KAAKjB,KAAKd,KAAKN,mBAAUO,SAAS+B,WACnDC,cAAgB,UACtBJ,SAASK,SAASC,gBACRC,YAAcD,QAAQE,aAAa,gBACtB,KAAfT,YAAqBQ,YAAYE,cAAcC,SAASX,WAAWU,eAAgB,CAEnFL,cAAcO,KAAKL,SACQA,QAAQM,cAAc/C,mBAAUC,QAAQ+C,eAChDf,UAAYgB,UAAUP,YAAaR,YACtDO,QAAQX,UAAUC,OAAO,eAEzBU,QAAQX,UAAUE,IAAI,aAIvBO,eAWLU,UAAY,CAACC,KAAMC,qBACjBC,OAASF,QACS,KAAlBC,cAAsB,OAChBE,IAAMH,KAAKN,cAAcU,QAAQH,cAAcP,eACjDS,KAAO,IACPD,OAASF,KAAKK,OAAO,EAAGF,KAAO,2BAA6BH,KAAKK,OAAOF,IAAKF,cAActB,QAAU,UACjGqB,KAAKK,OAAOF,IAAMF,cAActB,gBAIrCuB"}
\ No newline at end of file
+{"version":3,"file":"search.min.js","sources":["../src/search.js"],"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 * Search methods for finding contents in the content bank.\n *\n * @module core_contentbank/search\n * @copyright 2020 Sara Arjona \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport $ from 'jquery';\nimport selectors from 'core_contentbank/selectors';\nimport {get_string as getString} from 'core/str';\nimport Pending from 'core/pending';\nimport {debounce} from 'core/utils';\n\n/**\n * Set up the search.\n *\n * @method init\n */\nexport const init = () => {\n const pendingPromise = new Pending();\n\n const root = $(selectors.regions.contentbank);\n registerListenerEvents(root);\n\n pendingPromise.resolve();\n};\n\n/**\n * Register contentbank search related event listeners.\n *\n * @method registerListenerEvents\n * @param {Object} root The root element for the contentbank.\n */\nconst registerListenerEvents = (root) => {\n\n const searchInput = root.find(selectors.elements.searchinput)[0];\n\n root.on('click', selectors.actions.search, function(e) {\n e.preventDefault();\n toggleSearchResultsView(root, searchInput.value);\n });\n\n root.on('click', selectors.actions.clearSearch, function(e) {\n e.preventDefault();\n searchInput.value = \"\";\n searchInput.focus();\n toggleSearchResultsView(root, searchInput.value);\n });\n\n // The search input is also triggered.\n searchInput.addEventListener('input', debounce(() => {\n // Display the search results.\n toggleSearchResultsView(root, searchInput.value);\n }, 300));\n\n};\n\n/**\n * Toggle (display/hide) the search results depending on the value of the search query.\n *\n * @method toggleSearchResultsView\n * @param {HTMLElement} body The root element for the contentbank.\n * @param {String} searchQuery The search query.\n */\nconst toggleSearchResultsView = async(body, searchQuery) => {\n const clearSearchButton = body.find(selectors.actions.clearSearch)[0];\n\n const navbarBreadcrumb = body.find(selectors.elements.cbnavbarbreadcrumb)[0];\n const navbarTotal = body.find(selectors.elements.cbnavbartotalsearch)[0];\n // Update the results.\n const filteredContents = filterContents(body, searchQuery);\n if (searchQuery.length > 0) {\n // As the search query is present, search results should be displayed.\n\n // Display the \"clear\" search button in the activity chooser search bar.\n clearSearchButton.classList.remove('d-none');\n\n // Change the cb-navbar to display total items found.\n navbarBreadcrumb.classList.add('d-none');\n navbarTotal.innerHTML = await getString('itemsfound', 'core_contentbank', filteredContents.length);\n navbarTotal.classList.remove('d-none');\n } else {\n // As search query is not present, the search results should be removed.\n\n // Hide the \"clear\" search button in the activity chooser search bar.\n clearSearchButton.classList.add('d-none');\n\n // Display again the breadcrumb in the navbar.\n navbarBreadcrumb.classList.remove('d-none');\n navbarTotal.classList.add('d-none');\n }\n};\n\n/**\n * Return the list of contents which have a name that matches the given search term.\n *\n * @method filterContents\n * @param {HTMLElement} body The root element for the contentbank.\n * @param {String} searchTerm The search term to match.\n * @return {Array}\n */\nconst filterContents = (body, searchTerm) => {\n const contents = Array.from(body.find(selectors.elements.listitem));\n const searchResults = [];\n contents.forEach((content) => {\n const contentName = content.getAttribute('data-name');\n if (searchTerm === '' || contentName.toLowerCase().includes(searchTerm.toLowerCase())) {\n // The content matches the search criteria so it should be displayed and hightlighted.\n searchResults.push(content);\n const contentNameElement = content.querySelector(selectors.regions.cbcontentname);\n contentNameElement.innerHTML = highlight(contentName, searchTerm);\n content.classList.remove('d-none');\n } else {\n content.classList.add('d-none');\n }\n });\n\n return searchResults;\n};\n\n/**\n * Highlight a given string in a text.\n *\n * @method highlight\n * @param {String} text The whole text.\n * @param {String} highlightText The piece of text to highlight.\n * @return {String}\n */\nconst highlight = (text, highlightText) => {\n let result = text;\n if (highlightText !== '') {\n const pos = text.toLowerCase().indexOf(highlightText.toLowerCase());\n if (pos > -1) {\n result = text.substr(0, pos) + '' + text.substr(pos, highlightText.length) + '' +\n text.substr(pos + highlightText.length);\n }\n }\n\n return result;\n};\n"],"names":["_interopRequireDefault","obj","__esModule","default","_jquery","_selectors","_pending","_exports","init","pendingPromise","Pending","root","$","selectors","regions","contentbank","registerListenerEvents","resolve","searchInput","find","elements","searchinput","on","actions","search","e","preventDefault","toggleSearchResultsView","value","clearSearch","focus","addEventListener","debounce","async","body","searchQuery","clearSearchButton","navbarBreadcrumb","cbnavbarbreadcrumb","navbarTotal","cbnavbartotalsearch","filteredContents","filterContents","length","classList","remove","add","innerHTML","getString","get_string","searchTerm","contents","Array","from","listitem","searchResults","forEach","content","contentName","getAttribute","toLowerCase","includes","push","querySelector","cbcontentname","highlight","text","highlightText","result","pos","indexOf","substr"],"mappings":"sLA0BmC,SAAAA,uBAAAC,KAAAA,OAAAA,KAAAA,IAAAC,WAAAD,IAAAE,CAAAA,QAAAF,IAAA;;;;;;;kFAHnCG,QAAAJ,uBAAAI,SACAC,WAAAL,uBAAAK,YAEAC,SAAAN,uBAAAM,UAeEC,SAAAC,KAPkBA,KAChB,MAAMC,eAAiB,IAAIC,SAAAA,QAErBC,MAAO,EAAAC,QAACT,SAACU,mBAAUC,QAAQC,aACjCC,uBAAuBL,MAEvBF,eAAeQ,SAAS,EAS5B,MAAMD,uBAA0BL,OAE5B,MAAMO,YAAcP,KAAKQ,KAAKN,WAAAA,QAAUO,SAASC,aAAa,GAE9DV,KAAKW,GAAG,QAAST,WAAAA,QAAUU,QAAQC,QAAQ,SAASC,GAChDA,EAAEC,iBACFC,wBAAwBhB,KAAMO,YAAYU,MAC9C,IAEAjB,KAAKW,GAAG,QAAST,WAAAA,QAAUU,QAAQM,aAAa,SAASJ,GACrDA,EAAEC,iBACFR,YAAYU,MAAQ,GACpBV,YAAYY,QACZH,wBAAwBhB,KAAMO,YAAYU,MAC9C,IAGAV,YAAYa,iBAAiB,SAAS,EAAAC,OAAAA,WAAS,KAE3CL,wBAAwBhB,KAAMO,YAAYU,MAAM,GACjD,KAAK,EAWND,wBAA0BM,MAAMC,KAAMC,eACxC,MAAMC,kBAAoBF,KAAKf,KAAKN,WAAAA,QAAUU,QAAQM,aAAa,GAE7DQ,iBAAmBH,KAAKf,KAAKN,WAAAA,QAAUO,SAASkB,oBAAoB,GACpEC,YAAcL,KAAKf,KAAKN,WAAAA,QAAUO,SAASoB,qBAAqB,GAEhEC,iBAAmBC,eAAeR,KAAMC,aAC1CA,YAAYQ,OAAS,GAIrBP,kBAAkBQ,UAAUC,OAAO,UAGnCR,iBAAiBO,UAAUE,IAAI,UAC/BP,YAAYQ,gBAAkB,EAAAC,KAASC,YAAC,aAAc,mBAAoBR,iBAAiBE,QAC3FJ,YAAYK,UAAUC,OAAO,YAK7BT,kBAAkBQ,UAAUE,IAAI,UAGhCT,iBAAiBO,UAAUC,OAAO,UAClCN,YAAYK,UAAUE,IAAI,UAC9B,EAWEJ,eAAiBA,CAACR,KAAMgB,cAC1B,MAAMC,SAAWC,MAAMC,KAAKnB,KAAKf,KAAKN,mBAAUO,SAASkC,WACnDC,cAAgB,GActB,OAbAJ,SAASK,SAASC,UACd,MAAMC,YAAcD,QAAQE,aAAa,aACzC,GAAmB,KAAfT,YAAqBQ,YAAYE,cAAcC,SAASX,WAAWU,eAAgB,CAEnFL,cAAcO,KAAKL,SACQA,QAAQM,cAAclD,WAAAA,QAAUC,QAAQkD,eAChDjB,UAAYkB,UAAUP,YAAaR,YACtDO,QAAQb,UAAUC,OAAO,SAC7B,MACIY,QAAQb,UAAUE,IAAI,SAC1B,IAGGS,aAAa,EAWlBU,UAAYA,CAACC,KAAMC,iBACrB,IAAIC,OAASF,KACb,GAAsB,KAAlBC,cAAsB,CACtB,MAAME,IAAMH,KAAKN,cAAcU,QAAQH,cAAcP,eACjDS,KAAO,IACPD,OAASF,KAAKK,OAAO,EAAGF,KAAO,2BAA6BH,KAAKK,OAAOF,IAAKF,cAAcxB,QAAU,UACjGuB,KAAKK,OAAOF,IAAMF,cAAcxB,QAE5C,CAEA,OAAOyB,MAAM,CACf"}
\ No newline at end of file
diff --git a/contentbank/amd/build/selectors.min.js.map b/contentbank/amd/build/selectors.min.js.map
index 2d95b4f2a8f..387ea7d8251 100644
--- a/contentbank/amd/build/selectors.min.js.map
+++ b/contentbank/amd/build/selectors.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"selectors.min.js","sources":["../src/selectors.js"],"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 * Define all of the selectors we will be using on the contentbank interface.\n *\n * @module core_contentbank/selectors\n * @copyright 2020 Sara Arjona \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\n/**\n * A small helper function to build queryable data selectors.\n *\n * @method getDataSelector\n * @param {String} name\n * @param {String} value\n * @return {string}\n */\nconst getDataSelector = (name, value) => {\n return `[data-${name}=\"${value}\"]`;\n};\n\nexport default {\n regions: {\n cbcontentname: getDataSelector('region', 'cb-content-name'),\n contentbank: getDataSelector('region', 'contentbank'),\n filearea: getDataSelector('region', 'filearea')\n },\n actions: {\n search: getDataSelector('action', 'searchcontent'),\n clearSearch: getDataSelector('action', 'clearsearch'),\n viewgrid: getDataSelector('action', 'viewgrid'),\n viewlist: getDataSelector('action', 'viewlist'),\n sortname: getDataSelector('action', 'sortname'),\n sortuses: getDataSelector('action', 'sortuses'),\n sortdate: getDataSelector('action', 'sortdate'),\n sortsize: getDataSelector('action', 'sortsize'),\n sorttype: getDataSelector('action', 'sorttype'),\n sortauthor: getDataSelector('action', 'sortauthor'),\n },\n elements: {\n listitem: '.cb-listitem',\n heading: '.cb-heading',\n cell: '.cb-column',\n cbnavbarbreadcrumb: '.cb-navbar-breadbrumb',\n cbnavbartotalsearch: '.cb-navbar-totalsearch',\n searchinput: '[role=\"searchbox\"]',\n sortbutton: '.cb-btnsort'\n },\n};\n"],"names":["getDataSelector","name","value","regions","cbcontentname","contentbank","filearea","actions","search","clearSearch","viewgrid","viewlist","sortname","sortuses","sortdate","sortsize","sorttype","sortauthor","elements","listitem","heading","cell","cbnavbarbreadcrumb","cbnavbartotalsearch","searchinput","sortbutton"],"mappings":";;;;;;;;MA+BMA,gBAAkB,CAACC,KAAMC,wBACXD,kBAASC,yBAGd,CACXC,QAAS,CACLC,cAAeJ,gBAAgB,SAAU,mBACzCK,YAAaL,gBAAgB,SAAU,eACvCM,SAAUN,gBAAgB,SAAU,aAExCO,QAAS,CACLC,OAAQR,gBAAgB,SAAU,iBAClCS,YAAaT,gBAAgB,SAAU,eACvCU,SAAUV,gBAAgB,SAAU,YACpCW,SAAUX,gBAAgB,SAAU,YACpCY,SAAUZ,gBAAgB,SAAU,YACpCa,SAAUb,gBAAgB,SAAU,YACpCc,SAAUd,gBAAgB,SAAU,YACpCe,SAAUf,gBAAgB,SAAU,YACpCgB,SAAUhB,gBAAgB,SAAU,YACpCiB,WAAYjB,gBAAgB,SAAU,eAE1CkB,SAAU,CACNC,SAAU,eACVC,QAAS,cACTC,KAAM,aACNC,mBAAoB,wBACpBC,oBAAqB,yBACrBC,YAAa,qBACbC,WAAY"}
\ No newline at end of file
+{"version":3,"file":"selectors.min.js","sources":["../src/selectors.js"],"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 * Define all of the selectors we will be using on the contentbank interface.\n *\n * @module core_contentbank/selectors\n * @copyright 2020 Sara Arjona \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\n/**\n * A small helper function to build queryable data selectors.\n *\n * @method getDataSelector\n * @param {String} name\n * @param {String} value\n * @return {string}\n */\nconst getDataSelector = (name, value) => {\n return `[data-${name}=\"${value}\"]`;\n};\n\nexport default {\n regions: {\n cbcontentname: getDataSelector('region', 'cb-content-name'),\n contentbank: getDataSelector('region', 'contentbank'),\n filearea: getDataSelector('region', 'filearea')\n },\n actions: {\n search: getDataSelector('action', 'searchcontent'),\n clearSearch: getDataSelector('action', 'clearsearch'),\n viewgrid: getDataSelector('action', 'viewgrid'),\n viewlist: getDataSelector('action', 'viewlist'),\n sortname: getDataSelector('action', 'sortname'),\n sortuses: getDataSelector('action', 'sortuses'),\n sortdate: getDataSelector('action', 'sortdate'),\n sortsize: getDataSelector('action', 'sortsize'),\n sorttype: getDataSelector('action', 'sorttype'),\n sortauthor: getDataSelector('action', 'sortauthor'),\n },\n elements: {\n listitem: '.cb-listitem',\n heading: '.cb-heading',\n cell: '.cb-column',\n cbnavbarbreadcrumb: '.cb-navbar-breadbrumb',\n cbnavbartotalsearch: '.cb-navbar-totalsearch',\n searchinput: '[role=\"searchbox\"]',\n sortbutton: '.cb-btnsort'\n },\n};\n"],"names":["getDataSelector","name","value","concat","_default","regions","cbcontentname","contentbank","filearea","actions","search","clearSearch","viewgrid","viewlist","sortname","sortuses","sortdate","sortsize","sorttype","sortauthor","elements","listitem","heading","cell","cbnavbarbreadcrumb","cbnavbartotalsearch","searchinput","sortbutton","_exports","default"],"mappings":";;;;;;;;AA+BA,MAAMA,gBAAkBA,CAACC,KAAMC,QAC3B,SAAAC,OAAgBF,KAAIE,MAAAA,OAAKD,MAAK,MAChC,IAAAE,SAEa,CACXC,QAAS,CACLC,cAAeN,gBAAgB,SAAU,mBACzCO,YAAaP,gBAAgB,SAAU,eACvCQ,SAAUR,gBAAgB,SAAU,aAExCS,QAAS,CACLC,OAAQV,gBAAgB,SAAU,iBAClCW,YAAaX,gBAAgB,SAAU,eACvCY,SAAUZ,gBAAgB,SAAU,YACpCa,SAAUb,gBAAgB,SAAU,YACpCc,SAAUd,gBAAgB,SAAU,YACpCe,SAAUf,gBAAgB,SAAU,YACpCgB,SAAUhB,gBAAgB,SAAU,YACpCiB,SAAUjB,gBAAgB,SAAU,YACpCkB,SAAUlB,gBAAgB,SAAU,YACpCmB,WAAYnB,gBAAgB,SAAU,eAE1CoB,SAAU,CACNC,SAAU,eACVC,QAAS,cACTC,KAAM,aACNC,mBAAoB,wBACpBC,oBAAqB,yBACrBC,YAAa,qBACbC,WAAY,gBAEnB,OAAAC,SAAAC,QAAAzB,SAAAwB,SAAAC,OAAA"}
\ No newline at end of file
diff --git a/contentbank/amd/build/sort.min.js.map b/contentbank/amd/build/sort.min.js.map
index 2bd1546a85b..d5c1fb0095d 100644
--- a/contentbank/amd/build/sort.min.js.map
+++ b/contentbank/amd/build/sort.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"sort.min.js","sources":["../src/sort.js"],"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 * Content bank UI actions.\n *\n * @module core_contentbank/sort\n * @copyright 2020 Bas Brands \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport selectors from './selectors';\nimport {get_string as getString} from 'core/str';\nimport Prefetch from 'core/prefetch';\nimport Ajax from 'core/ajax';\nimport Notification from 'core/notification';\n\n/**\n * Set up the contentbank views.\n *\n * @method init\n */\nexport const init = () => {\n const contentBank = document.querySelector(selectors.regions.contentbank);\n Prefetch.prefetchStrings('contentbank', ['contentname', 'uses', 'lastmodified', 'size', 'type', 'author']);\n Prefetch.prefetchStrings('moodle', ['sortbyx', 'sortbyxreverse']);\n registerListenerEvents(contentBank);\n};\n\n/**\n * Register contentbank related event listeners.\n *\n * @method registerListenerEvents\n * @param {HTMLElement} contentBank The DOM node of the content bank\n */\nconst registerListenerEvents = (contentBank) => {\n\n contentBank.addEventListener('click', e => {\n const viewList = contentBank.querySelector(selectors.actions.viewlist);\n const viewGrid = contentBank.querySelector(selectors.actions.viewgrid);\n const fileArea = contentBank.querySelector(selectors.regions.filearea);\n const shownItems = fileArea.querySelectorAll(selectors.elements.listitem);\n\n // View as Grid button.\n if (e.target.closest(selectors.actions.viewgrid)) {\n contentBank.classList.remove('view-list');\n contentBank.classList.add('view-grid');\n if (fileArea && shownItems) {\n fileArea.setAttribute('role', 'list');\n shownItems.forEach(listItem => {\n listItem.setAttribute('role', 'listitem');\n listItem.querySelectorAll(selectors.elements.cell).forEach(cell => cell.removeAttribute('role'));\n });\n\n const heading = fileArea.querySelector(selectors.elements.heading);\n heading.removeAttribute('role');\n heading.querySelectorAll(selectors.elements.cell).forEach(cell => cell.removeAttribute('role'));\n }\n viewGrid.classList.add('active');\n viewList.classList.remove('active');\n setViewListPreference(false);\n\n return;\n }\n\n // View as List button.\n if (e.target.closest(selectors.actions.viewlist)) {\n contentBank.classList.remove('view-grid');\n contentBank.classList.add('view-list');\n if (fileArea && shownItems) {\n fileArea.setAttribute('role', 'table');\n shownItems.forEach(listItem => {\n listItem.setAttribute('role', 'row');\n listItem.querySelectorAll(selectors.elements.cell).forEach(cell => cell.setAttribute('role', 'cell'));\n });\n\n const heading = fileArea.querySelector(selectors.elements.heading);\n heading.setAttribute('role', 'row');\n heading.querySelectorAll(selectors.elements.cell).forEach(cell => cell.setAttribute('role', 'columnheader'));\n }\n viewList.classList.add('active');\n viewGrid.classList.remove('active');\n setViewListPreference(true);\n\n return;\n }\n\n if (fileArea && shownItems) {\n\n // Sort by file name alphabetical\n const sortByName = e.target.closest(selectors.actions.sortname);\n if (sortByName) {\n const ascending = updateSortButtons(contentBank, sortByName);\n updateSortOrder(fileArea, shownItems, 'data-file', ascending);\n return;\n }\n\n // Sort by uses.\n const sortByUses = e.target.closest(selectors.actions.sortuses);\n if (sortByUses) {\n const ascending = updateSortButtons(contentBank, sortByUses);\n updateSortOrder(fileArea, shownItems, 'data-uses', ascending);\n return;\n }\n\n // Sort by date.\n const sortByDate = e.target.closest(selectors.actions.sortdate);\n if (sortByDate) {\n const ascending = updateSortButtons(contentBank, sortByDate);\n updateSortOrder(fileArea, shownItems, 'data-timemodified', ascending);\n return;\n }\n\n // Sort by size.\n const sortBySize = e.target.closest(selectors.actions.sortsize);\n if (sortBySize) {\n const ascending = updateSortButtons(contentBank, sortBySize);\n updateSortOrder(fileArea, shownItems, 'data-bytes', ascending);\n return;\n }\n\n // Sort by type.\n const sortByType = e.target.closest(selectors.actions.sorttype);\n if (sortByType) {\n const ascending = updateSortButtons(contentBank, sortByType);\n updateSortOrder(fileArea, shownItems, 'data-type', ascending);\n return;\n }\n\n // Sort by author.\n const sortByAuthor = e.target.closest(selectors.actions.sortauthor);\n if (sortByAuthor) {\n const ascending = updateSortButtons(contentBank, sortByAuthor);\n updateSortOrder(fileArea, shownItems, 'data-author', ascending);\n }\n return;\n }\n });\n};\n\n\n/**\n * Set the contentbank user preference in list view\n *\n * @param {Bool} viewList view ContentBank as list.\n * @return {Promise} Repository promise.\n */\nconst setViewListPreference = function(viewList) {\n\n // If the given status is not hidden, the preference has to be deleted with a null value.\n if (viewList === false) {\n viewList = null;\n }\n\n const request = {\n methodname: 'core_user_update_user_preferences',\n args: {\n preferences: [\n {\n type: 'core_contentbank_view_list',\n value: viewList\n }\n ]\n }\n };\n\n return Ajax.call([request])[0].catch(Notification.exception);\n};\n\n/**\n * Update the sort button view.\n *\n * @method updateSortButtons\n * @param {HTMLElement} contentBank The DOM node of the contentbank button\n * @param {HTMLElement} sortButton The DOM node of the sort button\n * @return {Bool} sort ascending\n */\nconst updateSortButtons = (contentBank, sortButton) => {\n const sortButtons = contentBank.querySelectorAll(selectors.elements.sortbutton);\n\n sortButtons.forEach((button) => {\n if (button !== sortButton) {\n button.classList.remove('dir-asc');\n button.classList.remove('dir-desc');\n button.classList.add('dir-none');\n\n button.closest(selectors.elements.cell).setAttribute('aria-sort', 'none');\n\n updateButtonTitle(button, false);\n }\n });\n\n let ascending = true;\n\n if (sortButton.classList.contains('dir-none')) {\n sortButton.classList.remove('dir-none');\n sortButton.classList.add('dir-asc');\n sortButton.closest(selectors.elements.cell).setAttribute('aria-sort', 'ascending');\n } else if (sortButton.classList.contains('dir-asc')) {\n sortButton.classList.remove('dir-asc');\n sortButton.classList.add('dir-desc');\n sortButton.closest(selectors.elements.cell).setAttribute('aria-sort', 'descending');\n ascending = false;\n } else if (sortButton.classList.contains('dir-desc')) {\n sortButton.classList.remove('dir-desc');\n sortButton.classList.add('dir-asc');\n sortButton.closest(selectors.elements.cell).setAttribute('aria-sort', 'ascending');\n }\n\n updateButtonTitle(sortButton, ascending);\n\n return ascending;\n};\n\n/**\n * Update the button title.\n *\n * @method updateButtonTitle\n * @param {HTMLElement} button Button to update\n * @param {Bool} ascending Sort direction\n * @return {Promise} string promise\n */\nconst updateButtonTitle = (button, ascending) => {\n\n const sortString = (ascending ? 'sortbyxreverse' : 'sortbyx');\n\n return getString(button.dataset.string, 'contentbank')\n .then(columnName => {\n return getString(sortString, 'core', columnName);\n })\n .then(sortByString => {\n button.setAttribute('title', sortByString);\n return sortByString;\n })\n .catch();\n};\n\n/**\n * Update the sort order of the itemlist and update the DOM\n *\n * @method updateSortOrder\n * @param {HTMLElement} fileArea the Dom container for the itemlist\n * @param {Array} itemList Nodelist of Dom elements\n * @param {String} attribute the attribut to sort on\n * @param {Bool} ascending Sort Ascending\n */\nconst updateSortOrder = (fileArea, itemList, attribute, ascending) => {\n const sortList = [].slice.call(itemList).sort(function(a, b) {\n\n let aa = a.getAttribute(attribute);\n let bb = b.getAttribute(attribute);\n if (!isNaN(aa)) {\n aa = parseInt(aa);\n bb = parseInt(bb);\n }\n\n if (ascending) {\n return aa > bb ? 1 : -1;\n } else {\n return aa < bb ? 1 : -1;\n }\n });\n sortList.forEach(listItem => fileArea.appendChild(listItem));\n};\n"],"names":["contentBank","document","querySelector","selectors","regions","contentbank","prefetchStrings","registerListenerEvents","addEventListener","e","viewList","actions","viewlist","viewGrid","viewgrid","fileArea","filearea","shownItems","querySelectorAll","elements","listitem","target","closest","classList","remove","add","setAttribute","forEach","listItem","cell","removeAttribute","heading","setViewListPreference","sortByName","sortname","ascending","updateSortButtons","updateSortOrder","sortByUses","sortuses","sortByDate","sortdate","sortBySize","sortsize","sortByType","sorttype","sortByAuthor","sortauthor","request","methodname","args","preferences","type","value","Ajax","call","catch","Notification","exception","sortButton","sortbutton","button","updateButtonTitle","contains","sortString","dataset","string","then","columnName","sortByString","itemList","attribute","slice","sort","a","b","aa","getAttribute","bb","isNaN","parseInt","appendChild"],"mappings":"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"}
\ No newline at end of file
+{"version":3,"file":"sort.min.js","sources":["../src/sort.js"],"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 * Content bank UI actions.\n *\n * @module core_contentbank/sort\n * @copyright 2020 Bas Brands \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport selectors from './selectors';\nimport {get_string as getString} from 'core/str';\nimport Prefetch from 'core/prefetch';\nimport Ajax from 'core/ajax';\nimport Notification from 'core/notification';\n\n/**\n * Set up the contentbank views.\n *\n * @method init\n */\nexport const init = () => {\n const contentBank = document.querySelector(selectors.regions.contentbank);\n Prefetch.prefetchStrings('contentbank', ['contentname', 'uses', 'lastmodified', 'size', 'type', 'author']);\n Prefetch.prefetchStrings('moodle', ['sortbyx', 'sortbyxreverse']);\n registerListenerEvents(contentBank);\n};\n\n/**\n * Register contentbank related event listeners.\n *\n * @method registerListenerEvents\n * @param {HTMLElement} contentBank The DOM node of the content bank\n */\nconst registerListenerEvents = (contentBank) => {\n\n contentBank.addEventListener('click', e => {\n const viewList = contentBank.querySelector(selectors.actions.viewlist);\n const viewGrid = contentBank.querySelector(selectors.actions.viewgrid);\n const fileArea = contentBank.querySelector(selectors.regions.filearea);\n const shownItems = fileArea.querySelectorAll(selectors.elements.listitem);\n\n // View as Grid button.\n if (e.target.closest(selectors.actions.viewgrid)) {\n contentBank.classList.remove('view-list');\n contentBank.classList.add('view-grid');\n if (fileArea && shownItems) {\n fileArea.setAttribute('role', 'list');\n shownItems.forEach(listItem => {\n listItem.setAttribute('role', 'listitem');\n listItem.querySelectorAll(selectors.elements.cell).forEach(cell => cell.removeAttribute('role'));\n });\n\n const heading = fileArea.querySelector(selectors.elements.heading);\n heading.removeAttribute('role');\n heading.querySelectorAll(selectors.elements.cell).forEach(cell => cell.removeAttribute('role'));\n }\n viewGrid.classList.add('active');\n viewList.classList.remove('active');\n setViewListPreference(false);\n\n return;\n }\n\n // View as List button.\n if (e.target.closest(selectors.actions.viewlist)) {\n contentBank.classList.remove('view-grid');\n contentBank.classList.add('view-list');\n if (fileArea && shownItems) {\n fileArea.setAttribute('role', 'table');\n shownItems.forEach(listItem => {\n listItem.setAttribute('role', 'row');\n listItem.querySelectorAll(selectors.elements.cell).forEach(cell => cell.setAttribute('role', 'cell'));\n });\n\n const heading = fileArea.querySelector(selectors.elements.heading);\n heading.setAttribute('role', 'row');\n heading.querySelectorAll(selectors.elements.cell).forEach(cell => cell.setAttribute('role', 'columnheader'));\n }\n viewList.classList.add('active');\n viewGrid.classList.remove('active');\n setViewListPreference(true);\n\n return;\n }\n\n if (fileArea && shownItems) {\n\n // Sort by file name alphabetical\n const sortByName = e.target.closest(selectors.actions.sortname);\n if (sortByName) {\n const ascending = updateSortButtons(contentBank, sortByName);\n updateSortOrder(fileArea, shownItems, 'data-file', ascending);\n return;\n }\n\n // Sort by uses.\n const sortByUses = e.target.closest(selectors.actions.sortuses);\n if (sortByUses) {\n const ascending = updateSortButtons(contentBank, sortByUses);\n updateSortOrder(fileArea, shownItems, 'data-uses', ascending);\n return;\n }\n\n // Sort by date.\n const sortByDate = e.target.closest(selectors.actions.sortdate);\n if (sortByDate) {\n const ascending = updateSortButtons(contentBank, sortByDate);\n updateSortOrder(fileArea, shownItems, 'data-timemodified', ascending);\n return;\n }\n\n // Sort by size.\n const sortBySize = e.target.closest(selectors.actions.sortsize);\n if (sortBySize) {\n const ascending = updateSortButtons(contentBank, sortBySize);\n updateSortOrder(fileArea, shownItems, 'data-bytes', ascending);\n return;\n }\n\n // Sort by type.\n const sortByType = e.target.closest(selectors.actions.sorttype);\n if (sortByType) {\n const ascending = updateSortButtons(contentBank, sortByType);\n updateSortOrder(fileArea, shownItems, 'data-type', ascending);\n return;\n }\n\n // Sort by author.\n const sortByAuthor = e.target.closest(selectors.actions.sortauthor);\n if (sortByAuthor) {\n const ascending = updateSortButtons(contentBank, sortByAuthor);\n updateSortOrder(fileArea, shownItems, 'data-author', ascending);\n }\n return;\n }\n });\n};\n\n\n/**\n * Set the contentbank user preference in list view\n *\n * @param {Bool} viewList view ContentBank as list.\n * @return {Promise} Repository promise.\n */\nconst setViewListPreference = function(viewList) {\n\n // If the given status is not hidden, the preference has to be deleted with a null value.\n if (viewList === false) {\n viewList = null;\n }\n\n const request = {\n methodname: 'core_user_update_user_preferences',\n args: {\n preferences: [\n {\n type: 'core_contentbank_view_list',\n value: viewList\n }\n ]\n }\n };\n\n return Ajax.call([request])[0].catch(Notification.exception);\n};\n\n/**\n * Update the sort button view.\n *\n * @method updateSortButtons\n * @param {HTMLElement} contentBank The DOM node of the contentbank button\n * @param {HTMLElement} sortButton The DOM node of the sort button\n * @return {Bool} sort ascending\n */\nconst updateSortButtons = (contentBank, sortButton) => {\n const sortButtons = contentBank.querySelectorAll(selectors.elements.sortbutton);\n\n sortButtons.forEach((button) => {\n if (button !== sortButton) {\n button.classList.remove('dir-asc');\n button.classList.remove('dir-desc');\n button.classList.add('dir-none');\n\n button.closest(selectors.elements.cell).setAttribute('aria-sort', 'none');\n\n updateButtonTitle(button, false);\n }\n });\n\n let ascending = true;\n\n if (sortButton.classList.contains('dir-none')) {\n sortButton.classList.remove('dir-none');\n sortButton.classList.add('dir-asc');\n sortButton.closest(selectors.elements.cell).setAttribute('aria-sort', 'ascending');\n } else if (sortButton.classList.contains('dir-asc')) {\n sortButton.classList.remove('dir-asc');\n sortButton.classList.add('dir-desc');\n sortButton.closest(selectors.elements.cell).setAttribute('aria-sort', 'descending');\n ascending = false;\n } else if (sortButton.classList.contains('dir-desc')) {\n sortButton.classList.remove('dir-desc');\n sortButton.classList.add('dir-asc');\n sortButton.closest(selectors.elements.cell).setAttribute('aria-sort', 'ascending');\n }\n\n updateButtonTitle(sortButton, ascending);\n\n return ascending;\n};\n\n/**\n * Update the button title.\n *\n * @method updateButtonTitle\n * @param {HTMLElement} button Button to update\n * @param {Bool} ascending Sort direction\n * @return {Promise} string promise\n */\nconst updateButtonTitle = (button, ascending) => {\n\n const sortString = (ascending ? 'sortbyxreverse' : 'sortbyx');\n\n return getString(button.dataset.string, 'contentbank')\n .then(columnName => {\n return getString(sortString, 'core', columnName);\n })\n .then(sortByString => {\n button.setAttribute('title', sortByString);\n return sortByString;\n })\n .catch();\n};\n\n/**\n * Update the sort order of the itemlist and update the DOM\n *\n * @method updateSortOrder\n * @param {HTMLElement} fileArea the Dom container for the itemlist\n * @param {Array} itemList Nodelist of Dom elements\n * @param {String} attribute the attribut to sort on\n * @param {Bool} ascending Sort Ascending\n */\nconst updateSortOrder = (fileArea, itemList, attribute, ascending) => {\n const sortList = [].slice.call(itemList).sort(function(a, b) {\n\n let aa = a.getAttribute(attribute);\n let bb = b.getAttribute(attribute);\n if (!isNaN(aa)) {\n aa = parseInt(aa);\n bb = parseInt(bb);\n }\n\n if (ascending) {\n return aa > bb ? 1 : -1;\n } else {\n return aa < bb ? 1 : -1;\n }\n });\n sortList.forEach(listItem => fileArea.appendChild(listItem));\n};\n"],"names":["_interopRequireDefault","obj","__esModule","default","_selectors","_prefetch","_ajax","_notification","_exports","init","contentBank","document","querySelector","selectors","regions","contentbank","Prefetch","prefetchStrings","registerListenerEvents","addEventListener","e","viewList","actions","viewlist","viewGrid","viewgrid","fileArea","filearea","shownItems","querySelectorAll","elements","listitem","target","closest","classList","remove","add","setAttribute","forEach","listItem","cell","removeAttribute","heading","setViewListPreference","sortByName","sortname","ascending","updateSortButtons","updateSortOrder","sortByUses","sortuses","sortByDate","sortdate","sortBySize","sortsize","sortByType","sorttype","sortByAuthor","sortauthor","request","methodname","args","preferences","type","value","Ajax","call","catch","Notification","exception","sortButton","sortbutton","button","updateButtonTitle","contains","sortString","getString","dataset","string","then","columnName","sortByString","itemList","attribute","slice","sort","a","b","aa","getAttribute","bb","isNaN","parseInt","appendChild"],"mappings":"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"}
\ No newline at end of file
diff --git a/contentbank/amd/build/upload.min.js.map b/contentbank/amd/build/upload.min.js.map
index 6d9dd313580..f7a18aba42c 100644
--- a/contentbank/amd/build/upload.min.js.map
+++ b/contentbank/amd/build/upload.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"upload.min.js","sources":["../src/upload.js"],"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 * Module to handle AJAX interactions with content bank upload files.\n *\n * @module core_contentbank/upload\n * @copyright 2021 Sara Arjona \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport ModalForm from 'core_form/modalform';\nimport {get_string as getString} from 'core/str';\n\n/**\n * Initialize upload files to the content bank form as Modal form.\n *\n * @param {String} elementSelector\n * @param {String} formClass\n * @param {Integer} contextId\n * @param {Integer} contentId\n */\nexport const initModal = (elementSelector, formClass, contextId, contentId) => {\n const element = document.querySelector(elementSelector);\n element.addEventListener('click', function(e) {\n e.preventDefault();\n const form = new ModalForm({\n formClass,\n args: {\n contextid: contextId,\n id: contentId,\n },\n modalConfig: {title: getString('upload', 'contentbank')},\n returnFocus: e.target,\n });\n form.addEventListener(form.events.FORM_SUBMITTED, (event) => {\n document.location = event.detail.returnurl;\n });\n form.show();\n });\n};\n"],"names":["elementSelector","formClass","contextId","contentId","document","querySelector","addEventListener","e","preventDefault","form","ModalForm","args","contextid","id","modalConfig","title","returnFocus","target","events","FORM_SUBMITTED","event","location","detail","returnurl","show"],"mappings":";;;;;;;wKAiCyB,CAACA,gBAAiBC,UAAWC,UAAWC,aAC7CC,SAASC,cAAcL,iBAC/BM,iBAAiB,SAAS,SAASC,GACvCA,EAAEC,uBACIC,KAAO,IAAIC,mBAAU,CACvBT,UAAAA,UACAU,KAAM,CACFC,UAAWV,UACXW,GAAIV,WAERW,YAAa,CAACC,OAAO,mBAAU,SAAU,gBACzCC,YAAaT,EAAEU,SAEnBR,KAAKH,iBAAiBG,KAAKS,OAAOC,gBAAiBC,QAC/ChB,SAASiB,SAAWD,MAAME,OAAOC,aAErCd,KAAKe"}
\ No newline at end of file
+{"version":3,"file":"upload.min.js","sources":["../src/upload.js"],"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 * Module to handle AJAX interactions with content bank upload files.\n *\n * @module core_contentbank/upload\n * @copyright 2021 Sara Arjona \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport ModalForm from 'core_form/modalform';\nimport {get_string as getString} from 'core/str';\n\n/**\n * Initialize upload files to the content bank form as Modal form.\n *\n * @param {String} elementSelector\n * @param {String} formClass\n * @param {Integer} contextId\n * @param {Integer} contentId\n */\nexport const initModal = (elementSelector, formClass, contextId, contentId) => {\n const element = document.querySelector(elementSelector);\n element.addEventListener('click', function(e) {\n e.preventDefault();\n const form = new ModalForm({\n formClass,\n args: {\n contextid: contextId,\n id: contentId,\n },\n modalConfig: {title: getString('upload', 'contentbank')},\n returnFocus: e.target,\n });\n form.addEventListener(form.events.FORM_SUBMITTED, (event) => {\n document.location = event.detail.returnurl;\n });\n form.show();\n });\n};\n"],"names":["obj","_modalform","__esModule","default","_exports","initModal","elementSelector","formClass","contextId","contentId","document","querySelector","addEventListener","e","preventDefault","form","ModalForm","args","contextid","id","modalConfig","title","getString","returnFocus","target","events","FORM_SUBMITTED","event","location","detail","returnurl","show"],"mappings":"kHAsB4C,IAAAA;;;;;;;uFAA5CC,YAA4CD,IAA5CC,aAA4CD,IAAAE,WAAAF,IAAAG,CAAAA,QAAAH,KA6B1CI,SAAAC,UAlBuBA,CAACC,gBAAiBC,UAAWC,UAAWC,aAC7CC,SAASC,cAAcL,iBAC/BM,iBAAiB,SAAS,SAASC,GACvCA,EAAEC,iBACF,MAAMC,KAAO,IAAIC,WAAAA,QAAU,CACvBT,oBACAU,KAAM,CACFC,UAAWV,UACXW,GAAIV,WAERW,YAAa,CAACC,OAAO,EAAAC,KAAAA,YAAU,SAAU,gBACzCC,YAAaV,EAAEW,SAEnBT,KAAKH,iBAAiBG,KAAKU,OAAOC,gBAAiBC,QAC/CjB,SAASkB,SAAWD,MAAME,OAAOC,SAAS,IAE9Cf,KAAKgB,MACT,GAAE,CACJ"}
\ No newline at end of file
diff --git a/course/amd/build/actions.min.js.map b/course/amd/build/actions.min.js.map
index bc8e6a55407..8eb64ab3d86 100644
--- a/course/amd/build/actions.min.js.map
+++ b/course/amd/build/actions.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"actions.min.js","sources":["../src/actions.js"],"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 * Various actions on modules and sections in the editing mode - hiding, duplicating, deleting, etc.\n *\n * @module core_course/actions\n * @copyright 2016 Marina Glancy\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @since 3.3\n */\ndefine(\n [\n 'jquery',\n 'core/ajax',\n 'core/templates',\n 'core/notification',\n 'core/str',\n 'core/url',\n 'core/yui',\n 'core/modal_copy_to_clipboard',\n 'core/modal_factory',\n 'core/modal_events',\n 'core/key_codes',\n 'core/log',\n 'core_courseformat/courseeditor',\n 'core/event_dispatcher',\n 'core_course/events'\n ],\n function(\n $,\n ajax,\n templates,\n notification,\n str,\n url,\n Y,\n ModalCopyToClipboard,\n ModalFactory,\n ModalEvents,\n KeyCodes,\n log,\n editor,\n EventDispatcher,\n CourseEvents\n ) {\n\n // Eventually, core_courseformat/local/content/actions will handle all actions for\n // component compatible formats and the default actions.js won't be necessary anymore.\n // Meanwhile, we filter the migrated actions.\n const componentActions = [\n 'moveSection', 'moveCm', 'addSection', 'deleteSection', 'cmDelete', 'cmDuplicate', 'sectionHide', 'sectionShow',\n 'cmHide', 'cmShow', 'cmStealth', 'sectionHighlight', 'sectionUnhighlight',\n ];\n\n // The course reactive instance.\n const courseeditor = editor.getCurrentCourseEditor();\n\n // The current course format name (loaded on init).\n let formatname;\n\n var CSS = {\n EDITINPROGRESS: 'editinprogress',\n SECTIONDRAGGABLE: 'sectiondraggable',\n EDITINGMOVE: 'editing_move'\n };\n var SELECTOR = {\n ACTIVITYLI: 'li.activity',\n ACTIONAREA: '.actions',\n ACTIVITYACTION: 'a.cm-edit-action',\n MENU: '.moodle-actionmenu[data-enhance=moodle-core-actionmenu]',\n TOGGLE: '.toggle-display,.dropdown-toggle',\n SECTIONLI: 'li.section',\n SECTIONACTIONMENU: '.section_action_menu',\n ADDSECTIONS: '.changenumsections [data-add-sections]',\n SECTIONBADGES: '[data-region=\"sectionbadges\"]',\n };\n\n Y.use('moodle-course-coursebase', function() {\n var courseformatselector = M.course.format.get_section_selector();\n if (courseformatselector) {\n SELECTOR.SECTIONLI = courseformatselector;\n }\n });\n\n /**\n * Dispatch event wrapper.\n *\n * Old jQuery events will be replaced by native events gradually.\n *\n * @method dispatchEvent\n * @param {String} eventName The name of the event\n * @param {Object} detail Any additional details to pass into the eveent\n * @param {Node|HTMLElement} container The point at which to dispatch the event\n * @param {Object} options\n * @param {Boolean} options.bubbles Whether to bubble up the DOM\n * @param {Boolean} options.cancelable Whether preventDefault() can be called\n * @param {Boolean} options.composed Whether the event can bubble across the ShadowDOM boundary\n * @returns {CustomEvent}\n */\n const dispatchEvent = function(eventName, detail, container, options) {\n // Most actions still uses jQuery node instead of regular HTMLElement.\n if (!(container instanceof Element) && container.get !== undefined) {\n container = container.get(0);\n }\n return EventDispatcher.dispatchEvent(eventName, detail, container, options);\n };\n\n /**\n * Wrapper for Y.Moodle.core_course.util.cm.getId\n *\n * @param {JQuery} element\n * @returns {Integer}\n */\n var getModuleId = function(element) {\n // Check if we have a data-id first.\n const item = element.get(0);\n if (item.dataset.id) {\n return item.dataset.id;\n }\n // Use YUI way if data-id is not present.\n let id;\n Y.use('moodle-course-util', function(Y) {\n id = Y.Moodle.core_course.util.cm.getId(Y.Node(item));\n });\n return id;\n };\n\n /**\n * Wrapper for Y.Moodle.core_course.util.cm.getName\n *\n * @param {JQuery} element\n * @returns {String}\n */\n var getModuleName = function(element) {\n var name;\n Y.use('moodle-course-util', function(Y) {\n name = Y.Moodle.core_course.util.cm.getName(Y.Node(element.get(0)));\n });\n // Check if we have the name in the course state.\n const state = courseeditor.state;\n const cmid = getModuleId(element);\n if (!name && state && cmid) {\n name = state.cm.get(cmid)?.name;\n }\n return name;\n };\n\n /**\n * Wrapper for M.util.add_spinner for an activity\n *\n * @param {JQuery} activity\n * @returns {Node}\n */\n var addActivitySpinner = function(activity) {\n activity.addClass(CSS.EDITINPROGRESS);\n var actionarea = activity.find(SELECTOR.ACTIONAREA).get(0);\n if (actionarea) {\n var spinner = M.util.add_spinner(Y, Y.Node(actionarea));\n spinner.show();\n // Lock the activity state element.\n if (activity.data('id') !== undefined) {\n courseeditor.dispatch('cmLock', [activity.data('id')], true);\n }\n return spinner;\n }\n return null;\n };\n\n /**\n * Wrapper for M.util.add_spinner for a section\n *\n * @param {JQuery} sectionelement\n * @returns {Node}\n */\n var addSectionSpinner = function(sectionelement) {\n sectionelement.addClass(CSS.EDITINPROGRESS);\n var actionarea = sectionelement.find(SELECTOR.SECTIONACTIONMENU).get(0);\n if (actionarea) {\n var spinner = M.util.add_spinner(Y, Y.Node(actionarea));\n spinner.show();\n // Lock the section state element.\n if (sectionelement.data('id') !== undefined) {\n courseeditor.dispatch('sectionLock', [sectionelement.data('id')], true);\n }\n return spinner;\n }\n return null;\n };\n\n /**\n * Wrapper for M.util.add_lightbox\n *\n * @param {JQuery} sectionelement\n * @returns {Node}\n */\n var addSectionLightbox = function(sectionelement) {\n const item = sectionelement.get(0);\n var lightbox = M.util.add_lightbox(Y, Y.Node(item));\n if (item.dataset.for == 'section' && item.dataset.id) {\n courseeditor.dispatch('sectionLock', [item.dataset.id], true);\n lightbox.setAttribute('data-state', 'section');\n lightbox.setAttribute('data-state-id', item.dataset.id);\n }\n lightbox.show();\n return lightbox;\n };\n\n /**\n * Removes the spinner element\n *\n * @param {JQuery} element\n * @param {Node} spinner\n * @param {Number} delay\n */\n var removeSpinner = function(element, spinner, delay) {\n window.setTimeout(function() {\n element.removeClass(CSS.EDITINPROGRESS);\n if (spinner) {\n spinner.hide();\n }\n // Unlock the state element.\n if (element.data('id') !== undefined) {\n const mutation = (element.data('for') === 'section') ? 'sectionLock' : 'cmLock';\n courseeditor.dispatch(mutation, [element.data('id')], false);\n }\n }, delay);\n };\n\n /**\n * Removes the lightbox element\n *\n * @param {Node} lightbox lighbox YUI element returned by addSectionLightbox\n * @param {Number} delay\n */\n var removeLightbox = function(lightbox, delay) {\n if (lightbox) {\n window.setTimeout(function() {\n lightbox.hide();\n // Unlock state if necessary.\n if (lightbox.getAttribute('data-state')) {\n courseeditor.dispatch(\n `${lightbox.getAttribute('data-state')}Lock`,\n [lightbox.getAttribute('data-state-id')],\n false\n );\n }\n }, delay);\n }\n };\n\n /**\n * Initialise action menu for the element (section or module)\n *\n * @param {String} elementid CSS id attribute of the element\n */\n var initActionMenu = function(elementid) {\n // Initialise action menu in the new activity.\n Y.use('moodle-course-coursebase', function() {\n M.course.coursebase.invoke_function('setup_for_resource', '#' + elementid);\n });\n if (M.core.actionmenu && M.core.actionmenu.newDOMNode) {\n M.core.actionmenu.newDOMNode(Y.one('#' + elementid));\n }\n };\n\n /**\n * Returns focus to the element that was clicked or \"Edit\" link if element is no longer visible.\n *\n * @param {String} elementId CSS id attribute of the element\n * @param {String} action data-action property of the element that was clicked\n */\n var focusActionItem = function(elementId, action) {\n var mainelement = $('#' + elementId);\n var selector = '[data-action=' + action + ']';\n if (action === 'groupsseparate' || action === 'groupsvisible' || action === 'groupsnone') {\n // New element will have different data-action.\n selector = '[data-action=groupsseparate],[data-action=groupsvisible],[data-action=groupsnone]';\n }\n if (mainelement.find(selector).is(':visible')) {\n mainelement.find(selector).focus();\n } else {\n // Element not visible, focus the \"Edit\" link.\n mainelement.find(SELECTOR.MENU).find(SELECTOR.TOGGLE).focus();\n }\n };\n\n /**\n * Find next after the element\n *\n * @param {JQuery} mainElement element that is about to be deleted\n * @returns {JQuery}\n */\n var findNextFocusable = function(mainElement) {\n var tabables = $(\"a:visible\");\n var isInside = false;\n var foundElement = null;\n tabables.each(function() {\n if ($.contains(mainElement[0], this)) {\n isInside = true;\n } else if (isInside) {\n foundElement = this;\n return false; // Returning false in .each() is equivalent to \"break;\" inside the loop in php.\n }\n return true;\n });\n return foundElement;\n };\n\n /**\n * Performs an action on a module (moving, deleting, duplicating, hiding, etc.)\n *\n * @param {JQuery} moduleElement activity element we perform action on\n * @param {Number} cmid\n * @param {JQuery} target the element (menu item) that was clicked\n */\n var editModule = function(moduleElement, cmid, target) {\n var action = target.attr('data-action');\n var spinner = addActivitySpinner(moduleElement);\n var promises = ajax.call([{\n methodname: 'core_course_edit_module',\n args: {id: cmid,\n action: action,\n sectionreturn: target.attr('data-sectionreturn') ? target.attr('data-sectionreturn') : 0\n }\n }], true);\n\n var lightbox;\n if (action === 'duplicate') {\n lightbox = addSectionLightbox(target.closest(SELECTOR.SECTIONLI));\n }\n $.when.apply($, promises)\n .done(function(data) {\n var elementToFocus = findNextFocusable(moduleElement);\n moduleElement.replaceWith(data);\n let affectedids = [];\n // Initialise action menu for activity(ies) added as a result of this.\n $('