diff --git a/amd/build/initliveupdater.min.js b/amd/build/initliveupdater.min.js
index 98333d3a..c627f2eb 100644
--- a/amd/build/initliveupdater.min.js
+++ b/amd/build/initliveupdater.min.js
@@ -6,6 +6,6 @@ define("mod_learningmap/initliveupdater",["exports","mod_learningmap/liveupdater
* @copyright 2021-2024, ISB Bayern
* @author Philipp Memmel
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_liveupdater=_interopRequireDefault(_liveupdater),_ajax=_interopRequireDefault(_ajax),_log=_interopRequireDefault(_log),_pending=_interopRequireDefault(_pending);_exports.init=async cmId=>{const initliveupdaterPendingPromise=new _pending.default("mod_learningmap/initliveupdater");try{const data=await _ajax.default.call([{methodname:"mod_learningmap_get_dependingmodules",args:{cmId:cmId}}])[0];return initliveupdaterPendingPromise.resolve(),new _liveupdater.default({element:document.getElementById(_renderer.selectors.LEARNINGMAP_RENDER_CONTAINER_PREFIX+cmId),reactive:(0,_courseeditor.getCurrentCourseEditor)(),cmId:cmId,dependingModuleIds:data.dependingModuleIds})}catch(error){return _log.default.error(error),initliveupdaterPendingPromise.reject(),!1}}}));
+ */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_liveupdater=_interopRequireDefault(_liveupdater),_ajax=_interopRequireDefault(_ajax),_log=_interopRequireDefault(_log),_pending=_interopRequireDefault(_pending);_exports.init=async function(cmId){let inmodal=arguments.length>1&&void 0!==arguments[1]&&arguments[1];const initliveupdaterPendingPromise=new _pending.default("mod_learningmap/initliveupdater");try{const data=await _ajax.default.call([{methodname:"mod_learningmap_get_dependingmodules",args:{cmId:cmId}}])[0];return initliveupdaterPendingPromise.resolve(),new _liveupdater.default({element:document.getElementById(_renderer.selectors.LEARNINGMAP_RENDER_CONTAINER_PREFIX+cmId+(inmodal?"-modal":"")),reactive:(0,_courseeditor.getCurrentCourseEditor)(),cmId:cmId,dependingModuleIds:data.dependingModuleIds})}catch(error){return _log.default.error(error),initliveupdaterPendingPromise.reject(),!1}}}));
//# sourceMappingURL=initliveupdater.min.js.map
\ No newline at end of file
diff --git a/amd/build/initliveupdater.min.js.map b/amd/build/initliveupdater.min.js.map
index 4c84100f..8a16ebf1 100644
--- a/amd/build/initliveupdater.min.js.map
+++ b/amd/build/initliveupdater.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"initliveupdater.min.js","sources":["../src/initliveupdater.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 * Main module for the massaction block.\n *\n * @module mod_learningmap/initliveupdater\n * @copyright 2021-2024, ISB Bayern\n * @author Philipp Memmel\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport LiveUpdater from 'mod_learningmap/liveupdater';\nimport {selectors} from 'mod_learningmap/renderer';\nimport {getCurrentCourseEditor} from 'core_courseformat/courseeditor';\nimport Ajax from 'core/ajax';\nimport Log from 'core/log';\nimport Pending from 'core/pending';\n\n/**\n * Renders the learningmap into the correct div.\n *\n * @param {number} cmId the course module id of the learningmap\n * @return {Component} the liveupdater component\n */\nexport const init = async(cmId) => {\n const initliveupdaterPendingPromise = new Pending('mod_learningmap/initliveupdater');\n try {\n const data = await Ajax.call(\n [\n {\n methodname: 'mod_learningmap_get_dependingmodules',\n args: {\n 'cmId': cmId\n }\n }\n ])[0];\n\n initliveupdaterPendingPromise.resolve();\n return new LiveUpdater({\n element: document.getElementById(selectors.LEARNINGMAP_RENDER_CONTAINER_PREFIX + cmId),\n reactive: getCurrentCourseEditor(),\n cmId: cmId,\n dependingModuleIds: data.dependingModuleIds\n });\n } catch (error) {\n Log.error(error);\n initliveupdaterPendingPromise.reject();\n return false;\n }\n};\n"],"names":["async","initliveupdaterPendingPromise","Pending","data","Ajax","call","methodname","args","cmId","resolve","LiveUpdater","element","document","getElementById","selectors","LEARNINGMAP_RENDER_CONTAINER_PREFIX","reactive","dependingModuleIds","error","reject"],"mappings":";;;;;;;;kQAoCoBA,MAAAA,aACVC,8BAAgC,IAAIC,iBAAQ,6CAExCC,WAAaC,cAAKC,KACpB,CACI,CACIC,WAAY,uCACZC,KAAM,MACMC,SAGjB,UAEPP,8BAA8BQ,UACvB,IAAIC,qBAAY,CACnBC,QAASC,SAASC,eAAeC,oBAAUC,oCAAsCP,MACjFQ,UAAU,0CACVR,KAAMA,KACNS,mBAAoBd,KAAKc,qBAE/B,MAAOC,2BACDA,MAAMA,OACVjB,8BAA8BkB,UACvB"}
\ No newline at end of file
+{"version":3,"file":"initliveupdater.min.js","sources":["../src/initliveupdater.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 * Main module for the massaction block.\n *\n * @module mod_learningmap/initliveupdater\n * @copyright 2021-2024, ISB Bayern\n * @author Philipp Memmel\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport LiveUpdater from 'mod_learningmap/liveupdater';\nimport {selectors} from 'mod_learningmap/renderer';\nimport {getCurrentCourseEditor} from 'core_courseformat/courseeditor';\nimport Ajax from 'core/ajax';\nimport Log from 'core/log';\nimport Pending from 'core/pending';\n\n/**\n * Renders the learningmap into the correct div.\n *\n * @param {number} cmId the course module id of the learningmap\n * @param {boolean} inmodal whether the learningmap is being rendered in a modal\n * @return {Component} the liveupdater component\n */\nexport const init = async(cmId, inmodal = false) => {\n const initliveupdaterPendingPromise = new Pending('mod_learningmap/initliveupdater');\n try {\n const data = await Ajax.call(\n [\n {\n methodname: 'mod_learningmap_get_dependingmodules',\n args: {\n 'cmId': cmId\n }\n }\n ])[0];\n\n initliveupdaterPendingPromise.resolve();\n return new LiveUpdater({\n element: document.getElementById(selectors.LEARNINGMAP_RENDER_CONTAINER_PREFIX + cmId + (inmodal ? '-modal' : '')),\n reactive: getCurrentCourseEditor(),\n cmId: cmId,\n dependingModuleIds: data.dependingModuleIds\n });\n } catch (error) {\n Log.error(error);\n initliveupdaterPendingPromise.reject();\n return false;\n }\n};\n"],"names":["async","cmId","inmodal","initliveupdaterPendingPromise","Pending","data","Ajax","call","methodname","args","resolve","LiveUpdater","element","document","getElementById","selectors","LEARNINGMAP_RENDER_CONTAINER_PREFIX","reactive","dependingModuleIds","error","reject"],"mappings":";;;;;;;;kQAqCoBA,eAAMC,UAAMC,sEACtBC,8BAAgC,IAAIC,iBAAQ,6CAExCC,WAAaC,cAAKC,KACpB,CACI,CACIC,WAAY,uCACZC,KAAM,MACMR,SAGjB,UAEPE,8BAA8BO,UACvB,IAAIC,qBAAY,CACnBC,QAASC,SAASC,eAAeC,oBAAUC,oCAAsCf,MAAQC,QAAU,SAAW,KAC9Ge,UAAU,0CACVhB,KAAMA,KACNiB,mBAAoBb,KAAKa,qBAE/B,MAAOC,2BACDA,MAAMA,OACVhB,8BAA8BiB,UACvB"}
\ No newline at end of file
diff --git a/amd/build/linkmodal.min.js b/amd/build/linkmodal.min.js
index b662f5aa..aca10384 100644
--- a/amd/build/linkmodal.min.js
+++ b/amd/build/linkmodal.min.js
@@ -5,6 +5,6 @@ define("mod_learningmap/linkmodal",["exports","core/modal","core/ajax","core_cou
* @module mod_learningmap/linkmodal
* @copyright 2025 ISB Bayern
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_modal=_interopRequireDefault(_modal),_ajax=_interopRequireDefault(_ajax),manualcompletion=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(manualcompletion),_events=_interopRequireDefault(_events),_jquery=_interopRequireDefault(_jquery);_exports.init=async learningmapcmid=>{document.getElementById("learningmap-render-container-"+learningmapcmid).addEventListener("click",(async event=>{const target=event.target.closest("a[data-cmid]");if(target&&!target.hasAttribute("xlink:href")){event.preventDefault();const cmid=target.getAttribute("data-cmid");if(cmid){const data=await _ajax.default.call([{methodname:"mod_learningmap_get_cm",args:{cmid:cmid}}])[0];let js=_jquery.default.parseHTML(data.js,null,!0).map((node=>node.innerHTML)).join("\n");const modal=await _modal.default.create({title:data.name,body:data.completion+data.html,show:!1,removeOnClose:!0,large:!0});modal.bodyJS=js,modal.show(),manualcompletion.init(),document.addEventListener(_events.default.manualCompletionToggled,(()=>{(0,_renderer.renderLearningmap)(learningmapcmid)}))}}}))}}));
+ */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_modal=_interopRequireDefault(_modal),_ajax=_interopRequireDefault(_ajax),manualcompletion=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(manualcompletion),_events=_interopRequireDefault(_events),_jquery=_interopRequireDefault(_jquery);_exports.init=async function(learningmapcmid){let inmodal=arguments.length>1&&void 0!==arguments[1]&&arguments[1];const container=document.getElementById("learningmap-render-container-"+learningmapcmid+(inmodal?"-modal":""));container&&container.addEventListener("click",(async event=>{const target=event.target.closest("a[data-cmid]");if(target&&!target.hasAttribute("xlink:href")){event.preventDefault();const cmid=target.getAttribute("data-cmid");if(cmid){const data=await _ajax.default.call([{methodname:"mod_learningmap_get_cm",args:{cmid:cmid}}])[0];let js=_jquery.default.parseHTML(data.js,null,!0).map((node=>node.innerHTML)).join("\n");const modal=await _modal.default.create({title:data.name,body:data.completion+data.html,show:!1,removeOnClose:!0,large:!0});modal.bodyJS=js,modal.show(),manualcompletion.init(),document.addEventListener(_events.default.manualCompletionToggled,(()=>{(0,_renderer.renderLearningmap)(learningmapcmid)}))}}}))}}));
//# sourceMappingURL=linkmodal.min.js.map
\ No newline at end of file
diff --git a/amd/build/linkmodal.min.js.map b/amd/build/linkmodal.min.js.map
index 1b304973..b54758a1 100644
--- a/amd/build/linkmodal.min.js.map
+++ b/amd/build/linkmodal.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"linkmodal.min.js","sources":["../src/linkmodal.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 Modal from 'core/modal';\nimport Ajax from 'core/ajax';\nimport * as manualcompletion from 'core_course/manual_completion_toggle';\nimport {renderLearningmap} from 'mod_learningmap/renderer';\nimport CourseEvents from 'core_course/events';\nimport $ from 'jquery';\n\n/**\n * Helper for opening course modules in a modal that do not have a view page.\n *\n * @module mod_learningmap/linkmodal\n * @copyright 2025 ISB Bayern\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\n/**\n * Initialize the link modal listener for a learning map.\n *\n * @param {number} learningmapcmid - The course module ID of the learning map.\n */\nexport const init = async(learningmapcmid) => {\n document.getElementById('learningmap-render-container-' + learningmapcmid).addEventListener('click', async(event) => {\n const target = event.target.closest('a[data-cmid]');\n if (target && !target.hasAttribute('xlink:href')) {\n event.preventDefault();\n const cmid = target.getAttribute('data-cmid');\n if (cmid) {\n const data = await Ajax.call([{\n methodname: 'mod_learningmap_get_cm',\n args: {\n cmid: cmid\n },\n }])[0];\n let js = $.parseHTML(data.js, null, true).map(node => node.innerHTML).join(\"\\n\");\n const modal = await Modal.create({\n title: data.name,\n body: data.completion + data.html,\n show: false,\n removeOnClose: true,\n large: true,\n });\n modal.bodyJS = js;\n modal.show();\n manualcompletion.init();\n document.addEventListener(CourseEvents.manualCompletionToggled, () => {\n renderLearningmap(learningmapcmid);\n });\n }\n }\n });\n};\n"],"names":["async","document","getElementById","learningmapcmid","addEventListener","target","event","closest","hasAttribute","preventDefault","cmid","getAttribute","data","Ajax","call","methodname","args","js","$","parseHTML","map","node","innerHTML","join","modal","Modal","create","title","name","body","completion","html","show","removeOnClose","large","bodyJS","manualcompletion","init","CourseEvents","manualCompletionToggled"],"mappings":";;;;;;;06BAmCoBA,MAAAA,kBAChBC,SAASC,eAAe,gCAAkCC,iBAAiBC,iBAAiB,SAASJ,MAAAA,cAC3FK,OAASC,MAAMD,OAAOE,QAAQ,mBAChCF,SAAWA,OAAOG,aAAa,cAAe,CAC9CF,MAAMG,uBACAC,KAAOL,OAAOM,aAAa,gBAC7BD,KAAM,OACAE,WAAaC,cAAKC,KAAK,CAAC,CAC1BC,WAAY,yBACZC,KAAM,CACFN,KAAMA,SAEV,OACAO,GAAKC,gBAAEC,UAAUP,KAAKK,GAAI,MAAM,GAAMG,KAAIC,MAAQA,KAAKC,YAAWC,KAAK,YACrEC,YAAcC,eAAMC,OAAO,CAC7BC,MAAOf,KAAKgB,KACZC,KAAMjB,KAAKkB,WAAalB,KAAKmB,KAC7BC,MAAM,EACNC,eAAe,EACfC,OAAO,IAEXV,MAAMW,OAASlB,GACfO,MAAMQ,OACNI,iBAAiBC,OACjBpC,SAASG,iBAAiBkC,gBAAaC,yBAAyB,qCAC1CpC"}
\ No newline at end of file
+{"version":3,"file":"linkmodal.min.js","sources":["../src/linkmodal.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 Modal from 'core/modal';\nimport Ajax from 'core/ajax';\nimport * as manualcompletion from 'core_course/manual_completion_toggle';\nimport {renderLearningmap} from 'mod_learningmap/renderer';\nimport CourseEvents from 'core_course/events';\nimport $ from 'jquery';\n\n/**\n * Helper for opening course modules in a modal that do not have a view page.\n *\n * @module mod_learningmap/linkmodal\n * @copyright 2025 ISB Bayern\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\n/**\n * Initialize the link modal listener for a learning map.\n *\n * @param {number} learningmapcmid - The course module ID of the learning map.\n * @param {boolean} inmodal - Whether the learning map is already in a modal.\n */\nexport const init = async(learningmapcmid, inmodal = false) => {\n const container = document.getElementById('learningmap-render-container-' + learningmapcmid + (inmodal ? '-modal' : ''));\n if (container) {\n container.addEventListener('click', async(event) => {\n const target = event.target.closest('a[data-cmid]');\n if (target && !target.hasAttribute('xlink:href')) {\n event.preventDefault();\n const cmid = target.getAttribute('data-cmid');\n if (cmid) {\n const data = await Ajax.call([{\n methodname: 'mod_learningmap_get_cm',\n args: {\n cmid: cmid\n },\n }])[0];\n let js = $.parseHTML(data.js, null, true).map(node => node.innerHTML).join(\"\\n\");\n const modal = await Modal.create({\n title: data.name,\n body: data.completion + data.html,\n show: false,\n removeOnClose: true,\n large: true,\n });\n modal.bodyJS = js;\n modal.show();\n manualcompletion.init();\n document.addEventListener(CourseEvents.manualCompletionToggled, () => {\n renderLearningmap(learningmapcmid);\n });\n }\n }\n });\n }\n};\n"],"names":["async","learningmapcmid","inmodal","container","document","getElementById","addEventListener","target","event","closest","hasAttribute","preventDefault","cmid","getAttribute","data","Ajax","call","methodname","args","js","$","parseHTML","map","node","innerHTML","join","modal","Modal","create","title","name","body","completion","html","show","removeOnClose","large","bodyJS","manualcompletion","init","CourseEvents","manualCompletionToggled"],"mappings":";;;;;;;06BAoCoBA,eAAMC,qBAAiBC,sEACjCC,UAAYC,SAASC,eAAe,gCAAkCJ,iBAAmBC,QAAU,SAAW,KAChHC,WACAA,UAAUG,iBAAiB,SAASN,MAAAA,cAC1BO,OAASC,MAAMD,OAAOE,QAAQ,mBAChCF,SAAWA,OAAOG,aAAa,cAAe,CAC9CF,MAAMG,uBACAC,KAAOL,OAAOM,aAAa,gBAC7BD,KAAM,OACAE,WAAaC,cAAKC,KAAK,CAAC,CAC1BC,WAAY,yBACZC,KAAM,CACFN,KAAMA,SAEV,OACAO,GAAKC,gBAAEC,UAAUP,KAAKK,GAAI,MAAM,GAAMG,KAAIC,MAAQA,KAAKC,YAAWC,KAAK,YACrEC,YAAcC,eAAMC,OAAO,CAC7BC,MAAOf,KAAKgB,KACZC,KAAMjB,KAAKkB,WAAalB,KAAKmB,KAC7BC,MAAM,EACNC,eAAe,EACfC,OAAO,IAEXV,MAAMW,OAASlB,GACfO,MAAMQ,OACNI,iBAAiBC,OACjBnC,SAASE,iBAAiBkC,gBAAaC,yBAAyB,qCAC1CxC"}
\ No newline at end of file
diff --git a/amd/build/renderer.min.js b/amd/build/renderer.min.js
index c7357044..cec67608 100644
--- a/amd/build/renderer.min.js
+++ b/amd/build/renderer.min.js
@@ -6,6 +6,6 @@ define("mod_learningmap/renderer",["exports","core/ajax","core/log","core/pendin
* @copyright 2021-2024, ISB Bayern
* @author Philipp Memmel
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.selectors=_exports.renderLearningmap=_exports.init=void 0,_ajax=_interopRequireDefault(_ajax),_log=_interopRequireDefault(_log),_pending=_interopRequireDefault(_pending);const selectors={LEARNINGMAP_RENDER_CONTAINER_PREFIX:"learningmap-render-container-"};_exports.selectors=selectors;_exports.init=cmId=>{const rendererPendingPromise=new _pending.default("mod_learningmap/renderer-"+cmId);renderLearningmap(cmId),rendererPendingPromise.resolve()};const renderLearningmap=cmId=>{_ajax.default.call([{methodname:"mod_learningmap_get_learningmap",args:{cmId:cmId}}])[0].then((data=>(document.getElementById(selectors.LEARNINGMAP_RENDER_CONTAINER_PREFIX+cmId).innerHTML=data.content,!0))).catch((error=>(_log.default.error(error),!1)))};_exports.renderLearningmap=renderLearningmap}));
+ */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.selectors=_exports.renderLearningmap=_exports.init=void 0,_ajax=_interopRequireDefault(_ajax),_log=_interopRequireDefault(_log),_pending=_interopRequireDefault(_pending);const selectors={LEARNINGMAP_RENDER_CONTAINER_PREFIX:"learningmap-render-container-"};_exports.selectors=selectors;_exports.init=function(cmId){let inmodal=arguments.length>1&&void 0!==arguments[1]&&arguments[1];const rendererPendingPromise=new _pending.default("mod_learningmap/renderer-"+cmId);renderLearningmap(cmId,inmodal),rendererPendingPromise.resolve()};const renderLearningmap=cmId=>{_ajax.default.call([{methodname:"mod_learningmap_get_learningmap",args:{cmId:cmId}}])[0].then((data=>{let targetDiv=document.getElementById(selectors.LEARNINGMAP_RENDER_CONTAINER_PREFIX+cmId);return targetDiv&&(targetDiv.innerHTML=data.content),targetDiv=document.getElementById(selectors.LEARNINGMAP_RENDER_CONTAINER_PREFIX+cmId+"-modal"),targetDiv&&(targetDiv.innerHTML=data.content,targetDiv.parentElement.previousElementSibling.outerHTML=data.completion),!0})).catch((error=>(_log.default.error(error),!1)))};_exports.renderLearningmap=renderLearningmap}));
//# sourceMappingURL=renderer.min.js.map
\ No newline at end of file
diff --git a/amd/build/renderer.min.js.map b/amd/build/renderer.min.js.map
index 476448e0..c05b43ac 100644
--- a/amd/build/renderer.min.js.map
+++ b/amd/build/renderer.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"renderer.min.js","sources":["../src/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 * Renderer module for the learningmap.\n *\n * @module mod_learningmap/renderer\n * @copyright 2021-2024, ISB Bayern\n * @author Philipp Memmel\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport Ajax from 'core/ajax';\nimport Log from 'core/log';\nimport Pending from 'core/pending';\n\nexport const selectors = {\n LEARNINGMAP_RENDER_CONTAINER_PREFIX: 'learningmap-render-container-'\n};\n\n/**\n * Renders the learningmap into the correct div.\n *\n * @param {number} cmId the course module id of the learningmap\n */\nexport const init = (cmId) => {\n const rendererPendingPromise = new Pending('mod_learningmap/renderer-' + cmId);\n renderLearningmap(cmId);\n rendererPendingPromise.resolve();\n};\n\n/**\n * Render the learningmap with the given cmId into the corresponding div in the DOM.\n *\n * @param {number} cmId the course module id of the learningmap\n */\nexport const renderLearningmap = (cmId) => {\n const promises = Ajax.call(\n [\n {\n methodname: 'mod_learningmap_get_learningmap',\n args: {\n 'cmId': cmId\n }\n }\n ]);\n\n promises[0].then(data => {\n const targetDiv = document.getElementById(selectors.LEARNINGMAP_RENDER_CONTAINER_PREFIX + cmId);\n targetDiv.innerHTML = data.content;\n return true;\n }).catch((error) => {\n Log.error(error);\n return false;\n });\n};\n"],"names":["selectors","LEARNINGMAP_RENDER_CONTAINER_PREFIX","cmId","rendererPendingPromise","Pending","renderLearningmap","resolve","Ajax","call","methodname","args","then","data","document","getElementById","innerHTML","content","catch","error"],"mappings":";;;;;;;;sPA2BaA,UAAY,CACrBC,oCAAqC,4EAQpBC,aACXC,uBAAyB,IAAIC,iBAAQ,4BAA8BF,MACzEG,kBAAkBH,MAClBC,uBAAuBG,iBAQdD,kBAAqBH,OACbK,cAAKC,KAClB,CACI,CACIC,WAAY,kCACZC,KAAM,MACMR,SAKf,GAAGS,MAAKC,OACKC,SAASC,eAAed,UAAUC,oCAAsCC,MAChFa,UAAYH,KAAKI,SACpB,KACRC,OAAOC,qBACFA,MAAMA,QACH"}
\ No newline at end of file
+{"version":3,"file":"renderer.min.js","sources":["../src/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 * Renderer module for the learningmap.\n *\n * @module mod_learningmap/renderer\n * @copyright 2021-2024, ISB Bayern\n * @author Philipp Memmel\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport Ajax from 'core/ajax';\nimport Log from 'core/log';\nimport Pending from 'core/pending';\n\nexport const selectors = {\n LEARNINGMAP_RENDER_CONTAINER_PREFIX: 'learningmap-render-container-'\n};\n\n/**\n * Renders the learningmap into the correct div.\n *\n * @param {number} cmId the course module id of the learningmap\n * @param {boolean} inmodal whether the learningmap is being rendered in a modal\n */\nexport const init = (cmId, inmodal = false) => {\n const rendererPendingPromise = new Pending('mod_learningmap/renderer-' + cmId);\n renderLearningmap(cmId, inmodal);\n rendererPendingPromise.resolve();\n};\n\n/**\n * Render the learningmap with the given cmId into the corresponding div in the DOM.\n *\n * @param {number} cmId the course module id of the learningmap\n */\nexport const renderLearningmap = (cmId) => {\n const promises = Ajax.call(\n [\n {\n methodname: 'mod_learningmap_get_learningmap',\n args: {\n 'cmId': cmId\n }\n }\n ]);\n\n promises[0].then(data => {\n let targetDiv = document.getElementById(selectors.LEARNINGMAP_RENDER_CONTAINER_PREFIX + cmId);\n if (targetDiv) {\n targetDiv.innerHTML = data.content;\n }\n targetDiv = document.getElementById(selectors.LEARNINGMAP_RENDER_CONTAINER_PREFIX + cmId + '-modal');\n if (targetDiv) {\n targetDiv.innerHTML = data.content;\n targetDiv.parentElement.previousElementSibling.outerHTML = data.completion;\n }\n return true;\n }).catch((error) => {\n Log.error(error);\n return false;\n });\n};\n"],"names":["selectors","LEARNINGMAP_RENDER_CONTAINER_PREFIX","cmId","inmodal","rendererPendingPromise","Pending","renderLearningmap","resolve","Ajax","call","methodname","args","then","data","targetDiv","document","getElementById","innerHTML","content","parentElement","previousElementSibling","outerHTML","completion","catch","error"],"mappings":";;;;;;;;sPA2BaA,UAAY,CACrBC,oCAAqC,4EASrB,SAACC,UAAMC,sEACjBC,uBAAyB,IAAIC,iBAAQ,4BAA8BH,MACzEI,kBAAkBJ,KAAMC,SACxBC,uBAAuBG,iBAQdD,kBAAqBJ,OACbM,cAAKC,KAClB,CACI,CACIC,WAAY,kCACZC,KAAM,MACMT,SAKf,GAAGU,MAAKC,WACTC,UAAYC,SAASC,eAAehB,UAAUC,oCAAsCC,aACpFY,YACAA,UAAUG,UAAYJ,KAAKK,SAE/BJ,UAAYC,SAASC,eAAehB,UAAUC,oCAAsCC,KAAO,UACvFY,YACAA,UAAUG,UAAYJ,KAAKK,QAC3BJ,UAAUK,cAAcC,uBAAuBC,UAAYR,KAAKS,aAE7D,KACRC,OAAOC,qBACFA,MAAMA,QACH"}
\ No newline at end of file
diff --git a/amd/src/initliveupdater.js b/amd/src/initliveupdater.js
index 81d0d5e7..677d4079 100644
--- a/amd/src/initliveupdater.js
+++ b/amd/src/initliveupdater.js
@@ -32,9 +32,10 @@ import Pending from 'core/pending';
* Renders the learningmap into the correct div.
*
* @param {number} cmId the course module id of the learningmap
+ * @param {boolean} inmodal whether the learningmap is being rendered in a modal
* @return {Component} the liveupdater component
*/
-export const init = async(cmId) => {
+export const init = async(cmId, inmodal = false) => {
const initliveupdaterPendingPromise = new Pending('mod_learningmap/initliveupdater');
try {
const data = await Ajax.call(
@@ -49,7 +50,7 @@ export const init = async(cmId) => {
initliveupdaterPendingPromise.resolve();
return new LiveUpdater({
- element: document.getElementById(selectors.LEARNINGMAP_RENDER_CONTAINER_PREFIX + cmId),
+ element: document.getElementById(selectors.LEARNINGMAP_RENDER_CONTAINER_PREFIX + cmId + (inmodal ? '-modal' : '')),
reactive: getCurrentCourseEditor(),
cmId: cmId,
dependingModuleIds: data.dependingModuleIds
diff --git a/amd/src/linkmodal.js b/amd/src/linkmodal.js
index 873b0a26..408fa97a 100644
--- a/amd/src/linkmodal.js
+++ b/amd/src/linkmodal.js
@@ -32,35 +32,39 @@ import $ from 'jquery';
* Initialize the link modal listener for a learning map.
*
* @param {number} learningmapcmid - The course module ID of the learning map.
+ * @param {boolean} inmodal - Whether the learning map is already in a modal.
*/
-export const init = async(learningmapcmid) => {
- document.getElementById('learningmap-render-container-' + learningmapcmid).addEventListener('click', async(event) => {
- const target = event.target.closest('a[data-cmid]');
- if (target && !target.hasAttribute('xlink:href')) {
- event.preventDefault();
- const cmid = target.getAttribute('data-cmid');
- if (cmid) {
- const data = await Ajax.call([{
- methodname: 'mod_learningmap_get_cm',
- args: {
- cmid: cmid
- },
- }])[0];
- let js = $.parseHTML(data.js, null, true).map(node => node.innerHTML).join("\n");
- const modal = await Modal.create({
- title: data.name,
- body: data.completion + data.html,
- show: false,
- removeOnClose: true,
- large: true,
- });
- modal.bodyJS = js;
- modal.show();
- manualcompletion.init();
- document.addEventListener(CourseEvents.manualCompletionToggled, () => {
- renderLearningmap(learningmapcmid);
- });
+export const init = async(learningmapcmid, inmodal = false) => {
+ const container = document.getElementById('learningmap-render-container-' + learningmapcmid + (inmodal ? '-modal' : ''));
+ if (container) {
+ container.addEventListener('click', async(event) => {
+ const target = event.target.closest('a[data-cmid]');
+ if (target && !target.hasAttribute('xlink:href')) {
+ event.preventDefault();
+ const cmid = target.getAttribute('data-cmid');
+ if (cmid) {
+ const data = await Ajax.call([{
+ methodname: 'mod_learningmap_get_cm',
+ args: {
+ cmid: cmid
+ },
+ }])[0];
+ let js = $.parseHTML(data.js, null, true).map(node => node.innerHTML).join("\n");
+ const modal = await Modal.create({
+ title: data.name,
+ body: data.completion + data.html,
+ show: false,
+ removeOnClose: true,
+ large: true,
+ });
+ modal.bodyJS = js;
+ modal.show();
+ manualcompletion.init();
+ document.addEventListener(CourseEvents.manualCompletionToggled, () => {
+ renderLearningmap(learningmapcmid);
+ });
+ }
}
- }
- });
+ });
+ }
};
diff --git a/amd/src/renderer.js b/amd/src/renderer.js
index 84a84d85..dc071757 100644
--- a/amd/src/renderer.js
+++ b/amd/src/renderer.js
@@ -33,10 +33,11 @@ export const selectors = {
* Renders the learningmap into the correct div.
*
* @param {number} cmId the course module id of the learningmap
+ * @param {boolean} inmodal whether the learningmap is being rendered in a modal
*/
-export const init = (cmId) => {
+export const init = (cmId, inmodal = false) => {
const rendererPendingPromise = new Pending('mod_learningmap/renderer-' + cmId);
- renderLearningmap(cmId);
+ renderLearningmap(cmId, inmodal);
rendererPendingPromise.resolve();
};
@@ -57,8 +58,15 @@ export const renderLearningmap = (cmId) => {
]);
promises[0].then(data => {
- const targetDiv = document.getElementById(selectors.LEARNINGMAP_RENDER_CONTAINER_PREFIX + cmId);
- targetDiv.innerHTML = data.content;
+ let targetDiv = document.getElementById(selectors.LEARNINGMAP_RENDER_CONTAINER_PREFIX + cmId);
+ if (targetDiv) {
+ targetDiv.innerHTML = data.content;
+ }
+ targetDiv = document.getElementById(selectors.LEARNINGMAP_RENDER_CONTAINER_PREFIX + cmId + '-modal');
+ if (targetDiv) {
+ targetDiv.innerHTML = data.content;
+ targetDiv.parentElement.previousElementSibling.outerHTML = data.completion;
+ }
return true;
}).catch((error) => {
Log.error(error);
diff --git a/classes/external/get_cm.php b/classes/external/get_cm.php
index 09ec0cb4..dc468db3 100644
--- a/classes/external/get_cm.php
+++ b/classes/external/get_cm.php
@@ -54,16 +54,15 @@ public static function execute(int $cmid): array {
define('LEARNINGMAP_NO_BACKLINK', true);
- $cm = get_coursemodule_from_id('', $cmid, 0, false, MUST_EXIST);
+ [$course, $cm] = get_course_and_cm_from_cmid($cmid);
$context = \context_module::instance($cm->id);
- $course = get_course($cm->course);
self::validate_context($context);
require_capability('mod/' . $cm->modname . ':view', $context);
$modinfo = get_fast_modinfo($course);
- if (!$cm->uservisible) {
+ if (!$cm->available || !$cm->is_stealth() && $cm->visible == 0) {
require_capability('moodle/course:viewhiddenactivities', $context);
}
@@ -75,7 +74,16 @@ public static function execute(int $cmid): array {
$completioninfo = new \completion_info($course);
$completioninfo->set_module_viewed($cm);
- $data = (array)$cm;
+ $data = [
+ 'id' => $cm->id,
+ 'course' => $cm->course,
+ 'module' => $cm->module,
+ 'instance' => $cm->instance,
+ 'section' => $cm->sectionnum,
+ 'visible' => $cm->visible,
+ 'groupmode' => groups_get_activity_groupmode($cm, $course),
+ 'groupingid' => $cm->groupingid,
+ ];
// Remove description for labels, because it is already in html.
if ($cm->modname === 'label') {
diff --git a/classes/external/get_learningmap.php b/classes/external/get_learningmap.php
index 0e8b06a5..615386e3 100644
--- a/classes/external/get_learningmap.php
+++ b/classes/external/get_learningmap.php
@@ -69,6 +69,7 @@ public static function execute_returns(): external_single_structure {
new external_single_structure(
[
'content' => new external_value(PARAM_RAW, 'Rendered learningmap'),
+ 'completion' => new external_value(PARAM_RAW, 'Activity header HTML code'),
]
);
}
@@ -85,6 +86,7 @@ public static function execute_returns(): external_single_structure {
* @throws moodle_exception
*/
public static function execute(int $cmid): array {
+ global $PAGE, $OUTPUT;
$params = self::validate_parameters(self::execute_parameters(), ['cmId' => $cmid]);
$cmid = $params['cmId'];
[$course, $cminfo] = get_course_and_cm_from_cmid($cmid);
@@ -93,8 +95,19 @@ public static function execute(int $cmid): array {
require_capability('mod/learningmap:view', $context);
$completion = new \completion_info($course);
$completion->set_module_viewed($cminfo);
+
+ $PAGE->set_context($context);
+ $PAGE->set_cm($cminfo);
+ $PAGE->set_pagelayout('embedded');
+
+ $completion = $OUTPUT->render_from_template(
+ 'core/activity_header',
+ $PAGE->activityheader->export_for_template($OUTPUT)
+ );
+
return [
'content' => learningmap_get_learningmap($cminfo),
+ 'completion' => $completion,
];
}
}
diff --git a/classes/helper.php b/classes/helper.php
index 39be7cf4..e473b4c5 100644
--- a/classes/helper.php
+++ b/classes/helper.php
@@ -111,4 +111,13 @@ public static function is_ajax_request(): bool {
!empty($_REQUEST['info']) &&
in_array($_REQUEST['info'], ['core_course_get_module', 'mod_learningmap_get_cm']);
}
+
+ /**
+ * Determines if the current request is for the get_cm web service function.
+ *
+ * @return bool True if the request is for the get_cm web service function, false otherwise.
+ */
+ public static function is_get_cm_request(): bool {
+ return !empty($_REQUEST['info']) && $_REQUEST['info'] === 'mod_learningmap_get_cm';
+ }
}
diff --git a/lib.php b/lib.php
index c925ad9f..7fadb7fd 100644
--- a/lib.php
+++ b/lib.php
@@ -285,6 +285,7 @@ function learningmap_cm_info_view(cm_info $cm): void {
'hascontentbeforemap' => $hascontentbeforemap,
'mapcontent' => $mapcontent,
'usemodal' => !empty($learningmap->usemodal) || helper::is_learningmap_format($cm),
+ 'inmodal' => helper::is_get_cm_request(),
]
);
diff --git a/templates/rendercontainer.mustache b/templates/rendercontainer.mustache
index 928cd010..dd39d0ca 100644
--- a/templates/rendercontainer.mustache
+++ b/templates/rendercontainer.mustache
@@ -37,6 +37,7 @@
"hascontentbeforemap": true,
"mapcontent": "",
"usemodal": true,
+ "inmodal": false,
}
}}