diff --git a/course/amd/build/actions.min.js b/course/amd/build/actions.min.js
index b6a2862d863..318611df3cf 100644
--- a/course/amd/build/actions.min.js
+++ b/course/amd/build/actions.min.js
@@ -6,6 +6,6 @@
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  * @since      3.3
  */
-define("core_course/actions",["jquery","core/ajax","core/templates","core/notification","core/str","core/url","core/yui","core/modal_copy_to_clipboard","core/modal_save_cancel","core/modal_events","core/key_codes","core/log","core_courseformat/courseeditor","core/event_dispatcher","core_course/events"],(function($,ajax,templates,notification,str,url,Y,ModalCopyToClipboard,ModalSaveCancel,ModalEvents,KeyCodes,log,editor,EventDispatcher,CourseEvents){const componentActions=["moveSection","moveCm","addSection","deleteSection","cmDelete","cmDuplicate","sectionHide","sectionShow","cmHide","cmShow","cmStealth","sectionHighlight","sectionUnhighlight","cmMoveRight","cmMoveLeft","cmNoGroups","cmVisibleGroups","cmSeparateGroups"],courseeditor=editor.getCurrentCourseEditor();let formatname;var CSS_EDITINPROGRESS="editinprogress",CSS_EDITINGMOVE="editing_move",SELECTOR={ACTIVITYLI:"li.activity",ACTIONAREA:".actions",ACTIVITYACTION:"a.cm-edit-action",MENU:".moodle-actionmenu[data-enhance=moodle-core-actionmenu]",TOGGLE:".toggle-display,.dropdown-toggle",SECTIONLI:"li.section",SECTIONACTIONMENU:".section_action_menu",SECTIONACTIONMENUTRIGGER:".section-actions",SECTIONITEM:'[data-for="section_title"]',ADDSECTIONS:".changenumsections [data-add-sections]",SECTIONBADGES:'[data-region="sectionbadges"]'};Y.use("moodle-course-coursebase",(function(){var courseformatselector=M.course.format.get_section_selector();courseformatselector&&(SELECTOR.SECTIONLI=courseformatselector)}));const dispatchEvent=function(eventName,detail,container,options){return container instanceof Element||void 0===container.get||(container=container.get(0)),EventDispatcher.dispatchEvent(eventName,detail,container,options)};var getModuleId=function(element){const item=element.get(0);if(item.dataset.id)return item.dataset.id;let id;return Y.use("moodle-course-util",(function(Y){id=Y.Moodle.core_course.util.cm.getId(Y.Node(item))})),id},addActivitySpinner=function(activity){activity.addClass(CSS_EDITINPROGRESS);var actionarea=activity.find(SELECTOR.ACTIONAREA).get(0);if(actionarea){var spinner=M.util.add_spinner(Y,Y.Node(actionarea));return spinner.show(),void 0!==activity.data("id")&&courseeditor.dispatch("cmLock",[activity.data("id")],!0),spinner}return null},addSectionSpinner=function(sectionelement){sectionelement.addClass(CSS_EDITINPROGRESS);var actionarea=sectionelement.find(SELECTOR.SECTIONACTIONMENU).get(0);if(actionarea){var spinner=M.util.add_spinner(Y,Y.Node(actionarea));return spinner.show(),void 0!==sectionelement.data("id")&&courseeditor.dispatch("sectionLock",[sectionelement.data("id")],!0),spinner}return null},addSectionLightbox=function(sectionelement){const item=sectionelement.get(0);var lightbox=M.util.add_lightbox(Y,Y.Node(item));return"section"==item.dataset.for&&item.dataset.id&&(courseeditor.dispatch("sectionLock",[item.dataset.id],!0),lightbox.setAttribute("data-state","section"),lightbox.setAttribute("data-state-id",item.dataset.id)),lightbox.show(),lightbox},removeSpinner=function(element,spinner,delay){window.setTimeout((function(){if(element.removeClass(CSS_EDITINPROGRESS),spinner&&spinner.hide(),void 0!==element.data("id")){const mutation="section"===element.data("for")?"sectionLock":"cmLock";courseeditor.dispatch(mutation,[element.data("id")],!1)}}),delay)},removeLightbox=function(lightbox,delay){lightbox&&window.setTimeout((function(){lightbox.hide(),lightbox.getAttribute("data-state")&&courseeditor.dispatch("".concat(lightbox.getAttribute("data-state"),"Lock"),[lightbox.getAttribute("data-state-id")],!1)}),delay)},initActionMenu=function(elementid){Y.use("moodle-course-coursebase",(function(){M.course.coursebase.invoke_function("setup_for_resource","#"+elementid)})),M.core.actionmenu&&M.core.actionmenu.newDOMNode&&M.core.actionmenu.newDOMNode(Y.one("#"+elementid))},editModule=function(moduleElement,cmid,target){var lightbox,action=target.attr("data-action"),spinner=addActivitySpinner(moduleElement),promises=ajax.call([{methodname:"core_course_edit_module",args:{id:cmid,action:action,sectionreturn:target.attr("data-sectionreturn")?target.attr("data-sectionreturn"):null}}],!0);"duplicate"===action&&(lightbox=addSectionLightbox(target.closest(SELECTOR.SECTIONLI))),$.when.apply($,promises).done((function(data){var mainElement,tabables,isInside,foundElement,elementToFocus=(mainElement=moduleElement,tabables=$("a:visible"),isInside=!1,foundElement=null,tabables.each((function(){if($.contains(mainElement[0],this))isInside=!0;else if(isInside)return foundElement=this,!1;return!0})),foundElement);moduleElement.replaceWith(data);let affectedids=[];$("<div>"+data+"</div>").find(SELECTOR.ACTIVITYLI).each((function(index){initActionMenu($(this).attr("id")),0===index&&(!function(elementId,action){var mainelement=$("#"+elementId),selector="[data-action="+action+"]";"groupsseparate"!==action&&"groupsvisible"!==action&&"groupsnone"!==action||(selector="[data-action=groupsseparate],[data-action=groupsvisible],[data-action=groupsnone]"),mainelement.find(selector).is(":visible")?mainelement.find(selector).focus():mainelement.find(SELECTOR.MENU).find(SELECTOR.TOGGLE).focus()}($(this).attr("id"),action),elementToFocus=null),affectedids.push(getModuleId($(this)))})),elementToFocus&&elementToFocus.focus(),removeSpinner(moduleElement,spinner,400),removeLightbox(lightbox,400),moduleElement.trigger($.Event("coursemoduleedited",{ajaxreturn:data,action:action})),courseeditor.dispatch("legacyActivityAction",action,cmid,affectedids)})).fail((function(ex){removeSpinner(moduleElement,spinner),removeLightbox(lightbox);var e=$.Event("coursemoduleeditfailed",{exception:ex,action:action});moduleElement.trigger(e),e.isDefaultPrevented()||notification.exception(ex)}))},refreshModule=function(element,cmid,sectionreturn){void 0===sectionreturn&&(sectionreturn=courseeditor.sectionReturn);const activityElement=$(element);var spinner=addActivitySpinner(activityElement),promises=ajax.call([{methodname:"core_course_get_module",args:{id:cmid,sectionreturn:sectionreturn}}],!0);return new Promise(((resolve,reject)=>{$.when.apply($,promises).done((function(data){removeSpinner(activityElement,spinner,400),replaceActivityHtmlWith(data),resolve(data)})).fail((function(){removeSpinner(activityElement,spinner),reject()}))}))},confirmDeleteModule=function(mainelement,onconfirm){var modtypename=mainelement.attr("class").match(/modtype_([^\s]*)/)[1],modulename=function(element){var name;Y.use("moodle-course-util",(function(Y){name=Y.Moodle.core_course.util.cm.getName(Y.Node(element.get(0)))}));const state=courseeditor.state,cmid=getModuleId(element);var _state$cm$get;return!name&&state&&cmid&&(name=null===(_state$cm$get=state.cm.get(cmid))||void 0===_state$cm$get?void 0:_state$cm$get.name),name}(mainelement);str.get_string("pluginname",modtypename).done((function(pluginname){var plugindata={type:pluginname,name:modulename};str.get_strings([{key:"confirm",component:"core"},{key:null===modulename?"deletechecktype":"deletechecktypename",param:plugindata},{key:"yes"},{key:"no"}]).done((function(s){notification.confirm(s[0],s[1],s[2],s[3],onconfirm)}))}))},replaceActionItem=function(actionitem,image,stringname,stringcomponent,newaction){var stringRequests=[{key:stringname,component:stringcomponent}];return str.get_strings(stringRequests).then((function(strings){return actionitem.find("span.menu-action-text").html(strings[0]),templates.renderPix(image,"core")})).then((function(pixhtml){actionitem.find(".icon").replaceWith(pixhtml),actionitem.attr("data-action",newaction)})).catch(notification.exception)},defaultEditSectionHandler=function(sectionElement,actionItem,data,courseformat,sectionid){var action=actionItem.attr("data-action");if("hide"===action||"show"===action){if("hide"===action?(sectionElement.addClass("hidden"),setSectionBadge(sectionElement[0],"hiddenfromstudents",!0,!1),replaceActionItem(actionItem,"i/show","showfromothers","format_"+courseformat,"show")):(setSectionBadge(sectionElement[0],"hiddenfromstudents",!1,!1),sectionElement.removeClass("hidden"),replaceActionItem(actionItem,"i/hide","hidefromothers","format_"+courseformat,"hide")),void 0!==data.modules)for(var i in data.modules)replaceActivityHtmlWith(data.modules[i]);void 0!==data.section_availability&&sectionElement.find(".section_availability").first().replaceWith(data.section_availability);void 0!==courseeditor.state.section.get(sectionid)&&courseeditor.dispatch("sectionState",[sectionid])}else if("setmarker"===action){var oldmarker=$(SELECTOR.SECTIONLI+".current"),oldActionItem=oldmarker.find(SELECTOR.SECTIONACTIONMENU+" a[data-action=removemarker]");oldmarker.removeClass("current"),replaceActionItem(oldActionItem,"i/marker","highlight","core","setmarker"),sectionElement.addClass("current"),replaceActionItem(actionItem,"i/marked","highlightoff","core","removemarker"),courseeditor.dispatch("legacySectionAction",action,sectionid),setSectionBadge(sectionElement[0],"iscurrent",!0,!0)}else"removemarker"===action&&(sectionElement.removeClass("current"),replaceActionItem(actionItem,"i/marker","highlight","core","setmarker"),courseeditor.dispatch("legacySectionAction",action,sectionid),setSectionBadge(sectionElement[0],"iscurrent",!1,!0))};var replaceActivityHtmlWith=function(activityHTML){$("<div>"+activityHTML+"</div>").find(SELECTOR.ACTIVITYLI).each((function(){var id=$(this).attr("id");let focusedPath=function(id){const element=document.getElementById(id);if(element&&element.contains(document.activeElement))return element.querySelector(SELECTOR.ACTIONAREA).contains(document.activeElement)?"".concat(SELECTOR.ACTIONAREA,' [tabindex="0"]'):document.activeElement.id?"#".concat(document.activeElement.id):void 0}(id);if($(SELECTOR.ACTIVITYLI+"#"+id).replaceWith(activityHTML),initActionMenu(id),focusedPath){var _newItem$querySelecto;null===(_newItem$querySelecto=document.getElementById(id).querySelector(focusedPath))||void 0===_newItem$querySelecto||_newItem$querySelecto.focus()}}))},editSection=function(sectionElement,sectionid,target,courseformat){var action=target.attr("data-action"),sectionreturn=target.attr("data-sectionreturn")?target.attr("data-sectionreturn"):null;if(courseeditor.supportComponents&&componentActions.includes(action))return!1;var spinner=addSectionSpinner(sectionElement),promises=ajax.call([{methodname:"core_course_edit_section",args:{id:sectionid,action:action,sectionreturn:sectionreturn}}],!0),lightbox=addSectionLightbox(sectionElement);return $.when.apply($,promises).done((function(dataencoded){var data=$.parseJSON(dataencoded);removeSpinner(sectionElement,spinner),removeLightbox(lightbox),sectionElement.find(SELECTOR.SECTIONACTIONMENU).find(SELECTOR.TOGGLE).focus();var e=$.Event("coursesectionedited",{ajaxreturn:data,action:action});sectionElement.trigger(e),e.isDefaultPrevented()||defaultEditSectionHandler(sectionElement,target,data,courseformat,sectionid)})).fail((function(ex){removeSpinner(sectionElement,spinner),removeLightbox(lightbox);var e=$.Event("coursesectioneditfailed",{exception:ex,action:action});sectionElement.trigger(e),e.isDefaultPrevented()||notification.exception(ex)})),!0},setSectionBadge=function(sectionElement,badgetype,add,removeOther){const sectionbadges=sectionElement.querySelector(SELECTOR.SECTIONBADGES);if(!sectionbadges)return;const badge=sectionbadges.querySelector('[data-type="'+badgetype+'"]');badge&&(add?(removeOther&&document.querySelectorAll('[data-type="'+badgetype+'"]').forEach((b=>{b.classList.add("d-none")})),badge.classList.remove("d-none")):badge.classList.add("d-none"))};return Y.use("moodle-course-coursebase",(function(){M.course.coursebase.register_module({set_visibility_resource_ui:function(args){var mainelement=$(args.element.getDOMNode()),cmid=getModuleId(mainelement);if(cmid){var sectionreturn=mainelement.find("."+CSS_EDITINGMOVE).attr("data-sectionreturn");refreshModule(mainelement,cmid,sectionreturn)}},updateMovedCmState:params=>{const cm=courseeditor.state.cm.get(params.cmid);void 0!==cm&&courseeditor.dispatch("sectionState",[cm.sectionid]),courseeditor.dispatch("cmState",[params.cmid])},updateMovedSectionState:()=>{courseeditor.dispatch("courseState")}})})),courseeditor.addMutations({legacyActivityAction:function(statemanager,action,cmid,affectedids){const state=statemanager.state,cm=state.cm.get(cmid);if(void 0===cm)return;const section=state.section.get(cm.sectionid);if(void 0!==section){switch(courseeditor.dispatch("cmLock",[cm.id],!0),statemanager.setReadOnly(!1),cm.locked=!1,action){case"delete":section.cmlist=section.cmlist.reduce(((cmlist,current)=>(current!=cmid&&cmlist.push(current),cmlist)),[]),state.cm.delete(cmid);break;case"hide":case"show":case"duplicate":courseeditor.dispatch("cmState",affectedids)}statemanager.setReadOnly(!0)}},legacySectionAction:function(statemanager,action,sectionid){const state=statemanager.state,section=state.section.get(sectionid);if(void 0!==section){switch(statemanager.setReadOnly(!1),section.locked=!0,statemanager.setReadOnly(!0),statemanager.setReadOnly(!1),section.locked=!1,action){case"setmarker":state.section.forEach((current=>{current.id!=sectionid&&(current.current=!1)})),section.current=!0;break;case"removemarker":section.current=!1}statemanager.setReadOnly(!0)}}}),{initCoursePage:function(courseformat){if(formatname=courseformat,$("body").on("click keypress",SELECTOR.ACTIVITYLI+" "+SELECTOR.ACTIVITYACTION+"[data-action]",(function(e){if("keypress"!==e.type||13===e.keyCode){var actionItem=$(this),moduleElement=actionItem.closest(SELECTOR.ACTIVITYLI),action=actionItem.attr("data-action"),moduleId=getModuleId(moduleElement);switch(action){case"moveleft":case"moveright":case"delete":case"duplicate":case"hide":case"stealth":case"show":case"groupsseparate":case"groupsvisible":case"groupsnone":break;default:return}moduleId&&(e.preventDefault(),"delete"===action?confirmDeleteModule(moduleElement,(function(){editModule(moduleElement,moduleId,actionItem)})):editModule(moduleElement,moduleId,actionItem))}})),$("body").on("click keypress",SELECTOR.SECTIONACTIONMENUTRIGGER+"[data-sectionid] a[data-action]",(function(e){if("keypress"===e.type&&13!==e.keyCode)return;var actionItem=$(this),sectionElement=actionItem.closest(SELECTOR.SECTIONLI),sectionId=actionItem.closest(SELECTOR.SECTIONACTIONMENUTRIGGER).attr("data-sectionid");if("permalink"===actionItem.attr("data-action"))return e.preventDefault(),void ModalCopyToClipboard.create({text:actionItem.attr("href")},str.get_string("sectionlink","course"));let isExecuted=!0;var message,onconfirm;actionItem.attr("data-confirm")?(message=actionItem.attr("data-confirm"),onconfirm=function(){isExecuted=editSection(sectionElement,sectionId,actionItem,courseformat)},str.get_strings([{key:"confirm"},{key:"yes"},{key:"no"}]).done((function(s){notification.confirm(s[0],message,s[1],s[2],onconfirm)}))):isExecuted=editSection(sectionElement,sectionId,actionItem,courseformat),isExecuted&&e.preventDefault()})),$("body").on("updated","".concat(SELECTOR.SECTIONITEM," [data-inplaceeditable]"),(function(e){if(e.ajaxreturn&&e.ajaxreturn.itemid){void 0!==courseeditor.state.section.get(e.ajaxreturn.itemid)&&courseeditor.dispatch("sectionState",[e.ajaxreturn.itemid])}})),$("body").on("updated","".concat(SELECTOR.ACTIVITYLI,' [data-itemtype="activityname"][data-inplaceeditable]'),(function(e){e.ajaxreturn&&e.ajaxreturn.itemid&&courseeditor.dispatch("cmState",[e.ajaxreturn.itemid])})),courseeditor.supportComponents&&componentActions.includes("addSection"))return;const trigger=$(SELECTOR.ADDSECTIONS),modalTitle=trigger.attr("data-add-sections"),newSections=trigger.attr("data-new-sections");str.get_string("numberweeks").then((function(strNumberSections){var modalBody=$('<div><label for="add_section_numsections"></label> <input id="add_section_numsections" type="number" min="1" max="'+newSections+'" value="1"></div>');return modalBody.find("label").html(strNumberSections),modalBody.html()})).then((body=>ModalSaveCancel.create({body:body,title:modalTitle}))).then((function(modal){var numSections=$(modal.getBody()).find("#add_section_numsections"),addSections=function(){""+parseInt(numSections.val())===numSections.val()&&parseInt(numSections.val())>=1&&(document.location=trigger.attr("href")+"&numsections="+parseInt(numSections.val()))};return modal.setSaveButtonText(modalTitle),modal.getRoot().on(ModalEvents.shown,(function(){numSections.focus().select().on("keydown",(function(e){e.keyCode===KeyCodes.enter&&addSections()}))})),modal.getRoot().on(ModalEvents.save,(function(e){e.preventDefault(),addSections()})),trigger.on("click",(e=>{e.preventDefault(),modal.show()})),modal})).catch(notification.exception)},replaceSectionActionItem:function(sectionelement,selector,image,stringname,stringcomponent,newaction){log.debug("replaceSectionActionItem() is deprecated and will be removed.");var actionitem=sectionelement.find(SELECTOR.SECTIONACTIONMENU+" "+selector);replaceActionItem(actionitem,image,stringname,stringcomponent,newaction)},refreshModule:refreshModule,refreshSection:function(element,sectionid,sectionreturn){void 0===sectionreturn&&(sectionreturn=courseeditor.sectionReturn);const sectionElement=$(element),promises=ajax.call([{methodname:"core_course_edit_section",args:{id:sectionid,action:"refresh",sectionreturn:sectionreturn}}],!0);var spinner=addSectionSpinner(sectionElement);return new Promise(((resolve,reject)=>{$.when.apply($,promises).done((dataencoded=>{removeSpinner(sectionElement,spinner);const data=$.parseJSON(dataencoded),newSectionElement=$(data.content);sectionElement.replaceWith(newSectionElement),$("".concat(SELECTOR.SECTIONLI,"#").concat(sectionid," ").concat(SELECTOR.ACTIVITYLI)).each(((index,activity)=>{initActionMenu(activity.data("id"))}));dispatchEvent(CourseEvents.sectionRefreshed,{ajaxreturn:data,action:"refresh",newSectionElement:newSectionElement.get(0)},newSectionElement).defaultPrevented||defaultEditSectionHandler(newSectionElement,$(SELECTOR.SECTIONLI+"#"+sectionid),data,formatname,sectionid),resolve(data)})).fail((ex=>{dispatchEvent("coursesectionrefreshfailed",{exception:ex,action:"refresh"},sectionElement).defaultPrevented||notification.exception(ex),reject()}))}))}}}));
+define("core_course/actions",["jquery","core/ajax","core/templates","core/notification","core/str","core/url","core/yui","core/modal_copy_to_clipboard","core/modal_save_cancel","core/modal_events","core/key_codes","core/log","core_courseformat/courseeditor","core/event_dispatcher","core/local/inplace_editable/events","core_course/events"],(function($,ajax,templates,notification,str,url,Y,ModalCopyToClipboard,ModalSaveCancel,ModalEvents,KeyCodes,log,editor,EventDispatcher,InplaceEditableEvents,CourseEvents){const componentActions=["moveSection","moveCm","addSection","deleteSection","cmDelete","cmDuplicate","sectionHide","sectionShow","cmHide","cmShow","cmStealth","sectionHighlight","sectionUnhighlight","cmMoveRight","cmMoveLeft","cmNoGroups","cmVisibleGroups","cmSeparateGroups"],courseeditor=editor.getCurrentCourseEditor();let formatname;var CSS_EDITINPROGRESS="editinprogress",CSS_EDITINGMOVE="editing_move",SELECTOR={ACTIVITYLI:"li.activity",ACTIONAREA:".actions",ACTIVITYACTION:"a.cm-edit-action",MENU:".moodle-actionmenu[data-enhance=moodle-core-actionmenu]",TOGGLE:".toggle-display,.dropdown-toggle",SECTIONLI:"li.section",SECTIONACTIONMENU:".section_action_menu",SECTIONACTIONMENUTRIGGER:".section-actions",SECTIONITEM:'[data-for="section_title"]',ADDSECTIONS:".changenumsections [data-add-sections]",SECTIONBADGES:'[data-region="sectionbadges"]'};Y.use("moodle-course-coursebase",(function(){var courseformatselector=M.course.format.get_section_selector();courseformatselector&&(SELECTOR.SECTIONLI=courseformatselector)}));const dispatchEvent=function(eventName,detail,container,options){return container instanceof Element||void 0===container.get||(container=container.get(0)),EventDispatcher.dispatchEvent(eventName,detail,container,options)};var getModuleId=function(element){const item=element.get(0);if(item.dataset.id)return item.dataset.id;let id;return Y.use("moodle-course-util",(function(Y){id=Y.Moodle.core_course.util.cm.getId(Y.Node(item))})),id},addActivitySpinner=function(activity){activity.addClass(CSS_EDITINPROGRESS);var actionarea=activity.find(SELECTOR.ACTIONAREA).get(0);if(actionarea){var spinner=M.util.add_spinner(Y,Y.Node(actionarea));return spinner.show(),void 0!==activity.data("id")&&courseeditor.dispatch("cmLock",[activity.data("id")],!0),spinner}return null},addSectionSpinner=function(sectionelement){sectionelement.addClass(CSS_EDITINPROGRESS);var actionarea=sectionelement.find(SELECTOR.SECTIONACTIONMENU).get(0);if(actionarea){var spinner=M.util.add_spinner(Y,Y.Node(actionarea));return spinner.show(),void 0!==sectionelement.data("id")&&courseeditor.dispatch("sectionLock",[sectionelement.data("id")],!0),spinner}return null},addSectionLightbox=function(sectionelement){const item=sectionelement.get(0);var lightbox=M.util.add_lightbox(Y,Y.Node(item));return"section"==item.dataset.for&&item.dataset.id&&(courseeditor.dispatch("sectionLock",[item.dataset.id],!0),lightbox.setAttribute("data-state","section"),lightbox.setAttribute("data-state-id",item.dataset.id)),lightbox.show(),lightbox},removeSpinner=function(element,spinner,delay){window.setTimeout((function(){if(element.removeClass(CSS_EDITINPROGRESS),spinner&&spinner.hide(),void 0!==element.data("id")){const mutation="section"===element.data("for")?"sectionLock":"cmLock";courseeditor.dispatch(mutation,[element.data("id")],!1)}}),delay)},removeLightbox=function(lightbox,delay){lightbox&&window.setTimeout((function(){lightbox.hide(),lightbox.getAttribute("data-state")&&courseeditor.dispatch("".concat(lightbox.getAttribute("data-state"),"Lock"),[lightbox.getAttribute("data-state-id")],!1)}),delay)},initActionMenu=function(elementid){Y.use("moodle-course-coursebase",(function(){M.course.coursebase.invoke_function("setup_for_resource","#"+elementid)})),M.core.actionmenu&&M.core.actionmenu.newDOMNode&&M.core.actionmenu.newDOMNode(Y.one("#"+elementid))},editModule=function(moduleElement,cmid,target){var lightbox,action=target.attr("data-action"),spinner=addActivitySpinner(moduleElement),promises=ajax.call([{methodname:"core_course_edit_module",args:{id:cmid,action:action,sectionreturn:target.attr("data-sectionreturn")?target.attr("data-sectionreturn"):null}}],!0);"duplicate"===action&&(lightbox=addSectionLightbox(target.closest(SELECTOR.SECTIONLI))),$.when.apply($,promises).done((function(data){var mainElement,tabables,isInside,foundElement,elementToFocus=(mainElement=moduleElement,tabables=$("a:visible"),isInside=!1,foundElement=null,tabables.each((function(){if($.contains(mainElement[0],this))isInside=!0;else if(isInside)return foundElement=this,!1;return!0})),foundElement);moduleElement.replaceWith(data);let affectedids=[];$("<div>"+data+"</div>").find(SELECTOR.ACTIVITYLI).each((function(index){initActionMenu($(this).attr("id")),0===index&&(!function(elementId,action){var mainelement=$("#"+elementId),selector="[data-action="+action+"]";"groupsseparate"!==action&&"groupsvisible"!==action&&"groupsnone"!==action||(selector="[data-action=groupsseparate],[data-action=groupsvisible],[data-action=groupsnone]"),mainelement.find(selector).is(":visible")?mainelement.find(selector).focus():mainelement.find(SELECTOR.MENU).find(SELECTOR.TOGGLE).focus()}($(this).attr("id"),action),elementToFocus=null),affectedids.push(getModuleId($(this)))})),elementToFocus&&elementToFocus.focus(),removeSpinner(moduleElement,spinner,400),removeLightbox(lightbox,400),moduleElement.trigger($.Event("coursemoduleedited",{ajaxreturn:data,action:action})),courseeditor.dispatch("legacyActivityAction",action,cmid,affectedids)})).fail((function(ex){removeSpinner(moduleElement,spinner),removeLightbox(lightbox);var e=$.Event("coursemoduleeditfailed",{exception:ex,action:action});moduleElement.trigger(e),e.isDefaultPrevented()||notification.exception(ex)}))},refreshModule=function(element,cmid,sectionreturn){void 0===sectionreturn&&(sectionreturn=courseeditor.sectionReturn);const activityElement=$(element);var spinner=addActivitySpinner(activityElement),promises=ajax.call([{methodname:"core_course_get_module",args:{id:cmid,sectionreturn:sectionreturn}}],!0);return new Promise(((resolve,reject)=>{$.when.apply($,promises).done((function(data){removeSpinner(activityElement,spinner,400),replaceActivityHtmlWith(data),resolve(data)})).fail((function(){removeSpinner(activityElement,spinner),reject()}))}))},confirmDeleteModule=function(mainelement,onconfirm){var modtypename=mainelement.attr("class").match(/modtype_([^\s]*)/)[1],modulename=function(element){var name;Y.use("moodle-course-util",(function(Y){name=Y.Moodle.core_course.util.cm.getName(Y.Node(element.get(0)))}));const state=courseeditor.state,cmid=getModuleId(element);var _state$cm$get;return!name&&state&&cmid&&(name=null===(_state$cm$get=state.cm.get(cmid))||void 0===_state$cm$get?void 0:_state$cm$get.name),name}(mainelement);str.get_string("pluginname",modtypename).done((function(pluginname){var plugindata={type:pluginname,name:modulename};str.get_strings([{key:"confirm",component:"core"},{key:null===modulename?"deletechecktype":"deletechecktypename",param:plugindata},{key:"yes"},{key:"no"}]).done((function(s){notification.confirm(s[0],s[1],s[2],s[3],onconfirm)}))}))},replaceActionItem=function(actionitem,image,stringname,stringcomponent,newaction){var stringRequests=[{key:stringname,component:stringcomponent}];return str.get_strings(stringRequests).then((function(strings){return actionitem.find("span.menu-action-text").html(strings[0]),templates.renderPix(image,"core")})).then((function(pixhtml){actionitem.find(".icon").replaceWith(pixhtml),actionitem.attr("data-action",newaction)})).catch(notification.exception)},defaultEditSectionHandler=function(sectionElement,actionItem,data,courseformat,sectionid){var action=actionItem.attr("data-action");if("hide"===action||"show"===action){if("hide"===action?(sectionElement.addClass("hidden"),setSectionBadge(sectionElement[0],"hiddenfromstudents",!0,!1),replaceActionItem(actionItem,"i/show","showfromothers","format_"+courseformat,"show")):(setSectionBadge(sectionElement[0],"hiddenfromstudents",!1,!1),sectionElement.removeClass("hidden"),replaceActionItem(actionItem,"i/hide","hidefromothers","format_"+courseformat,"hide")),void 0!==data.modules)for(var i in data.modules)replaceActivityHtmlWith(data.modules[i]);void 0!==data.section_availability&&sectionElement.find(".section_availability").first().replaceWith(data.section_availability);void 0!==courseeditor.state.section.get(sectionid)&&courseeditor.dispatch("sectionState",[sectionid])}else if("setmarker"===action){var oldmarker=$(SELECTOR.SECTIONLI+".current"),oldActionItem=oldmarker.find(SELECTOR.SECTIONACTIONMENU+" a[data-action=removemarker]");oldmarker.removeClass("current"),replaceActionItem(oldActionItem,"i/marker","highlight","core","setmarker"),sectionElement.addClass("current"),replaceActionItem(actionItem,"i/marked","highlightoff","core","removemarker"),courseeditor.dispatch("legacySectionAction",action,sectionid),setSectionBadge(sectionElement[0],"iscurrent",!0,!0)}else"removemarker"===action&&(sectionElement.removeClass("current"),replaceActionItem(actionItem,"i/marker","highlight","core","setmarker"),courseeditor.dispatch("legacySectionAction",action,sectionid),setSectionBadge(sectionElement[0],"iscurrent",!1,!0))};var replaceActivityHtmlWith=function(activityHTML){$("<div>"+activityHTML+"</div>").find(SELECTOR.ACTIVITYLI).each((function(){var id=$(this).attr("id");let focusedPath=function(id){const element=document.getElementById(id);if(element&&element.contains(document.activeElement))return element.querySelector(SELECTOR.ACTIONAREA).contains(document.activeElement)?"".concat(SELECTOR.ACTIONAREA,' [tabindex="0"]'):document.activeElement.id?"#".concat(document.activeElement.id):void 0}(id);if($(SELECTOR.ACTIVITYLI+"#"+id).replaceWith(activityHTML),initActionMenu(id),focusedPath){var _newItem$querySelecto;null===(_newItem$querySelecto=document.getElementById(id).querySelector(focusedPath))||void 0===_newItem$querySelecto||_newItem$querySelecto.focus()}}))},editSection=function(sectionElement,sectionid,target,courseformat){var action=target.attr("data-action"),sectionreturn=target.attr("data-sectionreturn")?target.attr("data-sectionreturn"):null;if(courseeditor.supportComponents&&componentActions.includes(action))return!1;var spinner=addSectionSpinner(sectionElement),promises=ajax.call([{methodname:"core_course_edit_section",args:{id:sectionid,action:action,sectionreturn:sectionreturn}}],!0),lightbox=addSectionLightbox(sectionElement);return $.when.apply($,promises).done((function(dataencoded){var data=$.parseJSON(dataencoded);removeSpinner(sectionElement,spinner),removeLightbox(lightbox),sectionElement.find(SELECTOR.SECTIONACTIONMENU).find(SELECTOR.TOGGLE).focus();var e=$.Event("coursesectionedited",{ajaxreturn:data,action:action});sectionElement.trigger(e),e.isDefaultPrevented()||defaultEditSectionHandler(sectionElement,target,data,courseformat,sectionid)})).fail((function(ex){removeSpinner(sectionElement,spinner),removeLightbox(lightbox);var e=$.Event("coursesectioneditfailed",{exception:ex,action:action});sectionElement.trigger(e),e.isDefaultPrevented()||notification.exception(ex)})),!0},setSectionBadge=function(sectionElement,badgetype,add,removeOther){const sectionbadges=sectionElement.querySelector(SELECTOR.SECTIONBADGES);if(!sectionbadges)return;const badge=sectionbadges.querySelector('[data-type="'+badgetype+'"]');badge&&(add?(removeOther&&document.querySelectorAll('[data-type="'+badgetype+'"]').forEach((b=>{b.classList.add("d-none")})),badge.classList.remove("d-none")):badge.classList.add("d-none"))};return Y.use("moodle-course-coursebase",(function(){M.course.coursebase.register_module({set_visibility_resource_ui:function(args){var mainelement=$(args.element.getDOMNode()),cmid=getModuleId(mainelement);if(cmid){var sectionreturn=mainelement.find("."+CSS_EDITINGMOVE).attr("data-sectionreturn");refreshModule(mainelement,cmid,sectionreturn)}},updateMovedCmState:params=>{const cm=courseeditor.state.cm.get(params.cmid);void 0!==cm&&courseeditor.dispatch("sectionState",[cm.sectionid]),courseeditor.dispatch("cmState",[params.cmid])},updateMovedSectionState:()=>{courseeditor.dispatch("courseState")}})})),courseeditor.addMutations({legacyActivityAction:function(statemanager,action,cmid,affectedids){const state=statemanager.state,cm=state.cm.get(cmid);if(void 0===cm)return;const section=state.section.get(cm.sectionid);if(void 0!==section){switch(courseeditor.dispatch("cmLock",[cm.id],!0),statemanager.setReadOnly(!1),cm.locked=!1,action){case"delete":section.cmlist=section.cmlist.reduce(((cmlist,current)=>(current!=cmid&&cmlist.push(current),cmlist)),[]),state.cm.delete(cmid);break;case"hide":case"show":case"duplicate":courseeditor.dispatch("cmState",affectedids)}statemanager.setReadOnly(!0)}},legacySectionAction:function(statemanager,action,sectionid){const state=statemanager.state,section=state.section.get(sectionid);if(void 0!==section){switch(statemanager.setReadOnly(!1),section.locked=!0,statemanager.setReadOnly(!0),statemanager.setReadOnly(!1),section.locked=!1,action){case"setmarker":state.section.forEach((current=>{current.id!=sectionid&&(current.current=!1)})),section.current=!0;break;case"removemarker":section.current=!1}statemanager.setReadOnly(!0)}}}),{initCoursePage:function(courseformat){if(formatname=courseformat,$("body").on("click keypress",SELECTOR.ACTIVITYLI+" "+SELECTOR.ACTIVITYACTION+"[data-action]",(function(e){if("keypress"!==e.type||13===e.keyCode){var actionItem=$(this),moduleElement=actionItem.closest(SELECTOR.ACTIVITYLI),action=actionItem.attr("data-action"),moduleId=getModuleId(moduleElement);switch(action){case"moveleft":case"moveright":case"delete":case"duplicate":case"hide":case"stealth":case"show":case"groupsseparate":case"groupsvisible":case"groupsnone":break;default:return}moduleId&&(e.preventDefault(),"delete"===action?confirmDeleteModule(moduleElement,(function(){editModule(moduleElement,moduleId,actionItem)})):editModule(moduleElement,moduleId,actionItem))}})),$("body").on("click keypress",SELECTOR.SECTIONACTIONMENUTRIGGER+"[data-sectionid] a[data-action]",(function(e){if("keypress"===e.type&&13!==e.keyCode)return;var actionItem=$(this),sectionElement=actionItem.closest(SELECTOR.SECTIONLI),sectionId=actionItem.closest(SELECTOR.SECTIONACTIONMENUTRIGGER).attr("data-sectionid");if("permalink"===actionItem.attr("data-action"))return e.preventDefault(),void ModalCopyToClipboard.create({text:actionItem.attr("href")},str.get_string("sectionlink","course"));let isExecuted=!0;var message,onconfirm;actionItem.attr("data-confirm")?(message=actionItem.attr("data-confirm"),onconfirm=function(){isExecuted=editSection(sectionElement,sectionId,actionItem,courseformat)},str.get_strings([{key:"confirm"},{key:"yes"},{key:"no"}]).done((function(s){notification.confirm(s[0],message,s[1],s[2],onconfirm)}))):isExecuted=editSection(sectionElement,sectionId,actionItem,courseformat),isExecuted&&e.preventDefault()})),$("body").on(InplaceEditableEvents.eventTypes.elementUpdated,"".concat(SELECTOR.SECTIONITEM," [data-inplaceeditable]"),(function(e){if(e.detail.ajaxreturn.itemid){void 0!==courseeditor.state.section.get(e.detail.ajaxreturn.itemid)&&courseeditor.dispatch("sectionState",[e.detail.ajaxreturn.itemid])}})),$("body").on(InplaceEditableEvents.eventTypes.elementUpdated,"".concat(SELECTOR.ACTIVITYLI,' [data-itemtype="activityname"][data-inplaceeditable]'),(function(e){e.detail.ajaxreturn.itemid&&courseeditor.dispatch("cmState",[e.detail.ajaxreturn.itemid])})),courseeditor.supportComponents&&componentActions.includes("addSection"))return;const trigger=$(SELECTOR.ADDSECTIONS),modalTitle=trigger.attr("data-add-sections"),newSections=trigger.attr("data-new-sections");str.get_string("numberweeks").then((function(strNumberSections){var modalBody=$('<div><label for="add_section_numsections"></label> <input id="add_section_numsections" type="number" min="1" max="'+newSections+'" value="1"></div>');return modalBody.find("label").html(strNumberSections),modalBody.html()})).then((body=>ModalSaveCancel.create({body:body,title:modalTitle}))).then((function(modal){var numSections=$(modal.getBody()).find("#add_section_numsections"),addSections=function(){""+parseInt(numSections.val())===numSections.val()&&parseInt(numSections.val())>=1&&(document.location=trigger.attr("href")+"&numsections="+parseInt(numSections.val()))};return modal.setSaveButtonText(modalTitle),modal.getRoot().on(ModalEvents.shown,(function(){numSections.focus().select().on("keydown",(function(e){e.keyCode===KeyCodes.enter&&addSections()}))})),modal.getRoot().on(ModalEvents.save,(function(e){e.preventDefault(),addSections()})),trigger.on("click",(e=>{e.preventDefault(),modal.show()})),modal})).catch(notification.exception)},replaceSectionActionItem:function(sectionelement,selector,image,stringname,stringcomponent,newaction){log.debug("replaceSectionActionItem() is deprecated and will be removed.");var actionitem=sectionelement.find(SELECTOR.SECTIONACTIONMENU+" "+selector);replaceActionItem(actionitem,image,stringname,stringcomponent,newaction)},refreshModule:refreshModule,refreshSection:function(element,sectionid,sectionreturn){void 0===sectionreturn&&(sectionreturn=courseeditor.sectionReturn);const sectionElement=$(element),promises=ajax.call([{methodname:"core_course_edit_section",args:{id:sectionid,action:"refresh",sectionreturn:sectionreturn}}],!0);var spinner=addSectionSpinner(sectionElement);return new Promise(((resolve,reject)=>{$.when.apply($,promises).done((dataencoded=>{removeSpinner(sectionElement,spinner);const data=$.parseJSON(dataencoded),newSectionElement=$(data.content);sectionElement.replaceWith(newSectionElement),$("".concat(SELECTOR.SECTIONLI,"#").concat(sectionid," ").concat(SELECTOR.ACTIVITYLI)).each(((index,activity)=>{initActionMenu(activity.data("id"))}));dispatchEvent(CourseEvents.sectionRefreshed,{ajaxreturn:data,action:"refresh",newSectionElement:newSectionElement.get(0)},newSectionElement).defaultPrevented||defaultEditSectionHandler(newSectionElement,$(SELECTOR.SECTIONLI+"#"+sectionid),data,formatname,sectionid),resolve(data)})).fail((ex=>{dispatchEvent("coursesectionrefreshfailed",{exception:ex,action:"refresh"},sectionElement).defaultPrevented||notification.exception(ex),reject()}))}))}}}));
 
 //# sourceMappingURL=actions.min.js.map
\ 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 9a7d8152d76..d2cae6c8885 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 <http://www.gnu.org/licenses/>.\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_save_cancel',\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        ModalSaveCancel,\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', 'cmMoveRight', 'cmMoveLeft',\n            'cmNoGroups', 'cmVisibleGroups', 'cmSeparateGroups',\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            SECTIONACTIONMENUTRIGGER: '.section-actions',\n            SECTIONITEM: '[data-for=\"section_title\"]',\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 <a> 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') : null\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                    $('<div>' + data + '</div>').find(SELECTOR.ACTIVITYLI).each(function(index) {\n                        initActionMenu($(this).attr('id'));\n                        if (index === 0) {\n                            focusActionItem($(this).attr('id'), action);\n                            elementToFocus = null;\n                        }\n                        // Save any activity id in cmids.\n                        affectedids.push(getModuleId($(this)));\n                    });\n                    // In case of activity deletion focus the next focusable element.\n                    if (elementToFocus) {\n                        elementToFocus.focus();\n                    }\n                    // Remove spinner and lightbox with a delay.\n                    removeSpinner(moduleElement, spinner, 400);\n                    removeLightbox(lightbox, 400);\n                    // Trigger event that can be observed by course formats.\n                    moduleElement.trigger($.Event('coursemoduleedited', {ajaxreturn: data, action: action}));\n\n                    // Modify cm state.\n                    courseeditor.dispatch('legacyActivityAction', action, cmid, affectedids);\n\n                }).fail(function(ex) {\n                    // Remove spinner and lightbox.\n                    removeSpinner(moduleElement, spinner);\n                    removeLightbox(lightbox);\n                    // Trigger event that can be observed by course formats.\n                    var e = $.Event('coursemoduleeditfailed', {exception: ex, action: action});\n                    moduleElement.trigger(e);\n                    if (!e.isDefaultPrevented()) {\n                        notification.exception(ex);\n                    }\n                });\n        };\n\n        /**\n         * Requests html for the module via WS core_course_get_module and updates the module on the course page\n         *\n         * Used after d&d of the module to another section\n         *\n         * @param {JQuery|Element} element\n         * @param {Number} cmid\n         * @param {Number} sectionreturn\n         * @return {Promise} the refresh promise\n         */\n        var refreshModule = function(element, cmid, sectionreturn) {\n\n            if (sectionreturn === undefined) {\n                sectionreturn = courseeditor.sectionReturn;\n            }\n\n            const activityElement = $(element);\n            var spinner = addActivitySpinner(activityElement);\n            var promises = ajax.call([{\n                methodname: 'core_course_get_module',\n                args: {id: cmid, sectionreturn: sectionreturn}\n            }], true);\n\n            return new Promise((resolve, reject) => {\n                $.when.apply($, promises)\n                    .done(function(data) {\n                        removeSpinner(activityElement, spinner, 400);\n                        replaceActivityHtmlWith(data);\n                        resolve(data);\n                    }).fail(function() {\n                        removeSpinner(activityElement, spinner);\n                        reject();\n                    });\n            });\n        };\n\n        /**\n         * Requests html for the section via WS core_course_edit_section and updates the section on the course page\n         *\n         * @param {JQuery|Element} element\n         * @param {Number} sectionid\n         * @param {Number} sectionreturn\n         * @return {Promise} the refresh promise\n         */\n        var refreshSection = function(element, sectionid, sectionreturn) {\n\n            if (sectionreturn === undefined) {\n                sectionreturn = courseeditor.sectionReturn;\n            }\n\n            const sectionElement = $(element);\n            const action = 'refresh';\n            const promises = ajax.call([{\n                methodname: 'core_course_edit_section',\n                args: {id: sectionid, action, sectionreturn},\n            }], true);\n\n            var spinner = addSectionSpinner(sectionElement);\n            return new Promise((resolve, reject) => {\n                $.when.apply($, promises)\n                    .done(dataencoded => {\n\n                        removeSpinner(sectionElement, spinner);\n                        const data = $.parseJSON(dataencoded);\n\n                        const newSectionElement = $(data.content);\n                        sectionElement.replaceWith(newSectionElement);\n\n                        // Init modules menus.\n                        $(`${SELECTOR.SECTIONLI}#${sectionid} ${SELECTOR.ACTIVITYLI}`).each(\n                            (index, activity) => {\n                                initActionMenu(activity.data('id'));\n                            }\n                        );\n\n                        // Trigger event that can be observed by course formats.\n                        const event = dispatchEvent(\n                            CourseEvents.sectionRefreshed,\n                            {\n                                ajaxreturn: data,\n                                action: action,\n                                newSectionElement: newSectionElement.get(0),\n                            },\n                            newSectionElement\n                        );\n\n                        if (!event.defaultPrevented) {\n                            defaultEditSectionHandler(\n                                newSectionElement, $(SELECTOR.SECTIONLI + '#' + sectionid),\n                                data,\n                                formatname,\n                                sectionid\n                            );\n                        }\n                        resolve(data);\n                    }).fail(ex => {\n                        // Trigger event that can be observed by course formats.\n                        const event = dispatchEvent(\n                            'coursesectionrefreshfailed',\n                            {exception: ex, action: action},\n                            sectionElement\n                        );\n                        if (!event.defaultPrevented) {\n                            notification.exception(ex);\n                        }\n                        reject();\n                    });\n            });\n        };\n\n        /**\n         * Displays the delete confirmation to delete a module\n         *\n         * @param {JQuery} mainelement activity element we perform action on\n         * @param {function} onconfirm function to execute on confirm\n         */\n        var confirmDeleteModule = function(mainelement, onconfirm) {\n            var modtypename = mainelement.attr('class').match(/modtype_([^\\s]*)/)[1];\n            var modulename = getModuleName(mainelement);\n\n            str.get_string('pluginname', modtypename).done(function(pluginname) {\n                var plugindata = {\n                    type: pluginname,\n                    name: modulename\n                };\n                str.get_strings([\n                    {key: 'confirm', component: 'core'},\n                    {key: modulename === null ? 'deletechecktype' : 'deletechecktypename', param: plugindata},\n                    {key: 'yes'},\n                    {key: 'no'}\n                ]).done(function(s) {\n                        notification.confirm(s[0], s[1], s[2], s[3], onconfirm);\n                    }\n                );\n            });\n        };\n\n        /**\n         * Displays the delete confirmation to delete a section\n         *\n         * @param {String} message confirmation message\n         * @param {function} onconfirm function to execute on confirm\n         */\n        var confirmEditSection = function(message, onconfirm) {\n            str.get_strings([\n                {key: 'confirm'}, // TODO link text\n                {key: 'yes'},\n                {key: 'no'}\n            ]).done(function(s) {\n                    notification.confirm(s[0], message, s[1], s[2], onconfirm);\n                }\n            );\n        };\n\n        /**\n         * Replaces an action menu item with another one (for example Show->Hide, Set marker->Remove marker)\n         *\n         * @param {JQuery} actionitem\n         * @param {String} image new image name (\"i/show\", \"i/hide\", etc.)\n         * @param {String} stringname new string for the action menu item\n         * @param {String} stringcomponent\n         * @param {String} newaction new value for data-action attribute of the link\n         * @return {Promise} promise which is resolved when the replacement has completed\n         */\n        var replaceActionItem = function(actionitem, image, stringname,\n                                           stringcomponent, newaction) {\n\n            var stringRequests = [{key: stringname, component: stringcomponent}];\n            // Do not provide an icon with duplicate, different text to the menu item.\n\n            return str.get_strings(stringRequests).then(function(strings) {\n                actionitem.find('span.menu-action-text').html(strings[0]);\n\n                return templates.renderPix(image, 'core');\n            }).then(function(pixhtml) {\n                actionitem.find('.icon').replaceWith(pixhtml);\n                actionitem.attr('data-action', newaction);\n                return;\n            }).catch(notification.exception);\n        };\n\n        /**\n         * Default post-processing for section AJAX edit actions.\n         *\n         * This can be overridden in course formats by listening to event coursesectionedited:\n         *\n         * $('body').on('coursesectionedited', 'li.section', function(e) {\n         *     var action = e.action,\n         *         sectionElement = $(e.target),\n         *         data = e.ajaxreturn;\n         *     // ... Do some processing here.\n         *     e.preventDefault(); // Prevent default handler.\n         * });\n         *\n         * @param {JQuery} sectionElement\n         * @param {JQuery} actionItem\n         * @param {Object} data\n         * @param {String} courseformat\n         * @param {Number} sectionid\n         */\n        var defaultEditSectionHandler = function(sectionElement, actionItem, data, courseformat, sectionid) {\n            var action = actionItem.attr('data-action');\n            if (action === 'hide' || action === 'show') {\n                if (action === 'hide') {\n                    sectionElement.addClass('hidden');\n                    setSectionBadge(sectionElement[0], 'hiddenfromstudents', true, false);\n                    replaceActionItem(actionItem, 'i/show',\n                        'showfromothers', 'format_' + courseformat, 'show');\n                } else {\n                    setSectionBadge(sectionElement[0], 'hiddenfromstudents', false, false);\n                    sectionElement.removeClass('hidden');\n                    replaceActionItem(actionItem, 'i/hide',\n                        'hidefromothers', 'format_' + courseformat, 'hide');\n                }\n                // Replace the modules with new html (that indicates that they are now hidden or not hidden).\n                if (data.modules !== undefined) {\n                    for (var i in data.modules) {\n                        replaceActivityHtmlWith(data.modules[i]);\n                    }\n                }\n                // Replace the section availability information.\n                if (data.section_availability !== undefined) {\n                    sectionElement.find('.section_availability').first().replaceWith(data.section_availability);\n                }\n                // Modify course state.\n                const section = courseeditor.state.section.get(sectionid);\n                if (section !== undefined) {\n                    courseeditor.dispatch('sectionState', [sectionid]);\n                }\n            } else if (action === 'setmarker') {\n                var oldmarker = $(SELECTOR.SECTIONLI + '.current'),\n                    oldActionItem = oldmarker.find(SELECTOR.SECTIONACTIONMENU + ' ' + 'a[data-action=removemarker]');\n                oldmarker.removeClass('current');\n                replaceActionItem(oldActionItem, 'i/marker',\n                    'highlight', 'core', 'setmarker');\n                sectionElement.addClass('current');\n                replaceActionItem(actionItem, 'i/marked',\n                    'highlightoff', 'core', 'removemarker');\n                courseeditor.dispatch('legacySectionAction', action, sectionid);\n                setSectionBadge(sectionElement[0], 'iscurrent', true, true);\n            } else if (action === 'removemarker') {\n                sectionElement.removeClass('current');\n                replaceActionItem(actionItem, 'i/marker',\n                    'highlight', 'core', 'setmarker');\n                courseeditor.dispatch('legacySectionAction', action, sectionid);\n                setSectionBadge(sectionElement[0], 'iscurrent', false, true);\n            }\n        };\n\n        /**\n         * Get the focused element path in an activity if any.\n         *\n         * This method is used to restore focus when the activity HTML is refreshed.\n         * Only the main course editor elements can be refocused as they are always present\n         * even if the activity content changes.\n         *\n         * @param {String} id the element id the activity element\n         * @return {String|undefined} the inner path of the focused element or undefined\n         */\n        const getActivityFocusedElement = function(id) {\n            const element = document.getElementById(id);\n            if (!element || !element.contains(document.activeElement)) {\n                return undefined;\n            }\n            // Check if the actions menu toggler is focused.\n            if (element.querySelector(SELECTOR.ACTIONAREA).contains(document.activeElement)) {\n                return `${SELECTOR.ACTIONAREA} [tabindex=\"0\"]`;\n            }\n            // Return the current element id if any.\n            if (document.activeElement.id) {\n                return `#${document.activeElement.id}`;\n            }\n            return undefined;\n        };\n\n        /**\n         * Replaces the course module with the new html (used to update module after it was edited or its visibility was changed).\n         *\n         * @param {String} activityHTML\n         */\n        var replaceActivityHtmlWith = function(activityHTML) {\n            $('<div>' + activityHTML + '</div>').find(SELECTOR.ACTIVITYLI).each(function() {\n                // Extract id from the new activity html.\n                var id = $(this).attr('id');\n                // Check if the current focused element is inside the activity.\n                let focusedPath = getActivityFocusedElement(id);\n                // Find the existing element with the same id and replace its contents with new html.\n                $(SELECTOR.ACTIVITYLI + '#' + id).replaceWith(activityHTML);\n                // Initialise action menu.\n                initActionMenu(id);\n                // Re-focus the previous elements.\n                if (focusedPath) {\n                    const newItem = document.getElementById(id);\n                    newItem.querySelector(focusedPath)?.focus();\n                }\n\n            });\n        };\n\n        /**\n         * Performs an action on a module (moving, deleting, duplicating, hiding, etc.)\n         *\n         * @param {JQuery} sectionElement section element we perform action on\n         * @param {Nunmber} sectionid\n         * @param {JQuery} target the element (menu item) that was clicked\n         * @param {String} courseformat\n         * @return {boolean} true the action call is sent to the server or false if it is ignored.\n         */\n        var editSection = function(sectionElement, sectionid, target, courseformat) {\n            var action = target.attr('data-action'),\n                sectionreturn = target.attr('data-sectionreturn') ? target.attr('data-sectionreturn') : null;\n\n            // Filter direct component handled actions.\n            if (courseeditor.supportComponents && componentActions.includes(action)) {\n                return false;\n            }\n\n            var spinner = addSectionSpinner(sectionElement);\n            var promises = ajax.call([{\n                methodname: 'core_course_edit_section',\n                args: {id: sectionid, action: action, sectionreturn: sectionreturn}\n            }], true);\n\n            var lightbox = addSectionLightbox(sectionElement);\n            $.when.apply($, promises)\n                .done(function(dataencoded) {\n                    var data = $.parseJSON(dataencoded);\n                    removeSpinner(sectionElement, spinner);\n                    removeLightbox(lightbox);\n                    sectionElement.find(SELECTOR.SECTIONACTIONMENU).find(SELECTOR.TOGGLE).focus();\n                    // Trigger event that can be observed by course formats.\n                    var e = $.Event('coursesectionedited', {ajaxreturn: data, action: action});\n                    sectionElement.trigger(e);\n                    if (!e.isDefaultPrevented()) {\n                        defaultEditSectionHandler(sectionElement, target, data, courseformat, sectionid);\n                    }\n                }).fail(function(ex) {\n                    // Remove spinner and lightbox.\n                    removeSpinner(sectionElement, spinner);\n                    removeLightbox(lightbox);\n                    // Trigger event that can be observed by course formats.\n                    var e = $.Event('coursesectioneditfailed', {exception: ex, action: action});\n                    sectionElement.trigger(e);\n                    if (!e.isDefaultPrevented()) {\n                        notification.exception(ex);\n                    }\n                });\n            return true;\n        };\n\n        /**\n         * Sets the section badge in the section header.\n         *\n         * @param {JQuery} sectionElement section element we perform action on\n         * @param {String} badgetype the type of badge this is for\n         * @param {bool} add true to add, false to remove\n         * @param {boolean} removeOther in case of adding a badge, whether to remove all other.\n         */\n        var setSectionBadge = function(sectionElement, badgetype, add, removeOther) {\n            const sectionbadges = sectionElement.querySelector(SELECTOR.SECTIONBADGES);\n            if (!sectionbadges) {\n                return;\n            }\n            const badge = sectionbadges.querySelector('[data-type=\"' + badgetype + '\"]');\n            if (!badge) {\n                return;\n            }\n            if (add) {\n                if (removeOther) {\n                    document.querySelectorAll('[data-type=\"' + badgetype + '\"]').forEach((b) => {\n                        b.classList.add('d-none');\n                    });\n                }\n                badge.classList.remove('d-none');\n            } else {\n                badge.classList.add('d-none');\n            }\n        };\n\n        // Register a function to be executed after D&D of an activity.\n        Y.use('moodle-course-coursebase', function() {\n            M.course.coursebase.register_module({\n                // Ignore camelcase eslint rule for the next line because it is an expected name of the callback.\n                // eslint-disable-next-line camelcase\n                set_visibility_resource_ui: function(args) {\n                    var mainelement = $(args.element.getDOMNode());\n                    var cmid = getModuleId(mainelement);\n                    if (cmid) {\n                        var sectionreturn = mainelement.find('.' + CSS.EDITINGMOVE).attr('data-sectionreturn');\n                        refreshModule(mainelement, cmid, sectionreturn);\n                    }\n                },\n                /**\n                 * Update the course state when some cm is moved via YUI.\n                 * @param {*} params\n                 */\n                updateMovedCmState: (params) => {\n                    const state = courseeditor.state;\n\n                    // Update old section.\n                    const cm = state.cm.get(params.cmid);\n                    if (cm !== undefined) {\n                        courseeditor.dispatch('sectionState', [cm.sectionid]);\n                    }\n                    // Update cm state.\n                    courseeditor.dispatch('cmState', [params.cmid]);\n                },\n                /**\n                 * Update the course state when some section is moved via YUI.\n                 */\n                updateMovedSectionState: () => {\n                    courseeditor.dispatch('courseState');\n                },\n            });\n        });\n\n        // From Moodle 4.0 all edit actions are being re-implemented as state mutation.\n        // This means all method from this \"actions\" module will be deprecated when all the course\n        // interface is migrated to reactive components.\n        // Most legacy actions did not provide enough information to regenarate the course so they\n        // use the mutations courseState, sectionState and cmState to get the updated state from\n        // the server. However, some activity actions where we can prevent an extra webservice\n        // call by implementing an adhoc mutation.\n        courseeditor.addMutations({\n            /**\n             * Compatibility function to update Moodle 4.0 course state using legacy actions.\n             *\n             * This method only updates some actions which does not require to use cmState mutation\n             * to get updated data form the server.\n             *\n             * @param {Object} statemanager the current state in read write mode\n             * @param {String} action the performed action\n             * @param {Number} cmid the affected course module id\n             * @param {Array} affectedids all affected cm ids (for duplicate action)\n             */\n            legacyActivityAction: function(statemanager, action, cmid, affectedids) {\n\n                const state = statemanager.state;\n                const cm = state.cm.get(cmid);\n                if (cm === undefined) {\n                    return;\n                }\n                const section = state.section.get(cm.sectionid);\n                if (section === undefined) {\n                    return;\n                }\n\n                // Send the element is locked.\n                courseeditor.dispatch('cmLock', [cm.id], true);\n\n                // Now we do the real mutation.\n                statemanager.setReadOnly(false);\n\n                // This unlocked will take effect when the read only is restored.\n                cm.locked = false;\n\n                switch (action) {\n                    case 'delete':\n                        // Remove from section.\n                        section.cmlist = section.cmlist.reduce(\n                            (cmlist, current) => {\n                                if (current != cmid) {\n                                    cmlist.push(current);\n                                }\n                                return cmlist;\n                            },\n                            []\n                        );\n                        // Delete form list.\n                        state.cm.delete(cmid);\n                        break;\n\n                    case 'hide':\n                    case 'show':\n                    case 'duplicate':\n                        courseeditor.dispatch('cmState', affectedids);\n                        break;\n                }\n                statemanager.setReadOnly(true);\n            },\n            legacySectionAction: function(statemanager, action, sectionid) {\n\n                const state = statemanager.state;\n                const section = state.section.get(sectionid);\n                if (section === undefined) {\n                    return;\n                }\n\n                // Send the element is locked. Reactive events are only triggered when the state\n                // read only mode is restored. We want to notify the interface the element is\n                // locked so we need to do a quick lock operation before performing the rest\n                // of the mutation.\n                statemanager.setReadOnly(false);\n                section.locked = true;\n                statemanager.setReadOnly(true);\n\n                // Now we do the real mutation.\n                statemanager.setReadOnly(false);\n\n                // This locked will take effect when the read only is restored.\n                section.locked = false;\n\n                switch (action) {\n                    case 'setmarker':\n                        // Remove previous marker.\n                        state.section.forEach((current) => {\n                            if (current.id != sectionid) {\n                                current.current = false;\n                            }\n                        });\n                        section.current = true;\n                        break;\n\n                    case 'removemarker':\n                        section.current = false;\n                        break;\n                }\n                statemanager.setReadOnly(true);\n            },\n        });\n\n        return /** @alias module:core_course/actions */ {\n\n            /**\n             * Initialises course page\n             *\n             * @method init\n             * @param {String} courseformat name of the current course format (for fetching strings)\n             */\n            initCoursePage: function(courseformat) {\n\n                formatname = courseformat;\n\n                // Add a handler for course module actions.\n                $('body').on('click keypress', SELECTOR.ACTIVITYLI + ' ' +\n                        SELECTOR.ACTIVITYACTION + '[data-action]', function(e) {\n                    if (e.type === 'keypress' && e.keyCode !== 13) {\n                        return;\n                    }\n                    var actionItem = $(this),\n                        moduleElement = actionItem.closest(SELECTOR.ACTIVITYLI),\n                        action = actionItem.attr('data-action'),\n                        moduleId = getModuleId(moduleElement);\n                    switch (action) {\n                        case 'moveleft':\n                        case 'moveright':\n                        case 'delete':\n                        case 'duplicate':\n                        case 'hide':\n                        case 'stealth':\n                        case 'show':\n                        case 'groupsseparate':\n                        case 'groupsvisible':\n                        case 'groupsnone':\n                            break;\n                        default:\n                            // Nothing to do here!\n                            return;\n                    }\n                    if (!moduleId) {\n                        return;\n                    }\n                    e.preventDefault();\n                    if (action === 'delete') {\n                        // Deleting requires confirmation.\n                        confirmDeleteModule(moduleElement, function() {\n                            editModule(moduleElement, moduleId, actionItem);\n                        });\n                    } else {\n                        editModule(moduleElement, moduleId, actionItem);\n                    }\n                });\n\n                // Add a handler for section action menu.\n                $('body').on('click keypress',\n                            SELECTOR.SECTIONACTIONMENUTRIGGER + '[data-sectionid] ' +\n                            'a[data-action]', function(e) {\n                    if (e.type === 'keypress' && e.keyCode !== 13) {\n                        return;\n                    }\n                    var actionItem = $(this),\n                        sectionElement = actionItem.closest(SELECTOR.SECTIONLI),\n                        sectionId = actionItem.closest(SELECTOR.SECTIONACTIONMENUTRIGGER).attr('data-sectionid');\n\n                    if (actionItem.attr('data-action') === 'permalink') {\n                        e.preventDefault();\n                        ModalCopyToClipboard.create({\n                            text: actionItem.attr('href'),\n                        }, str.get_string('sectionlink', 'course')\n                        );\n                        return;\n                    }\n\n                    let isExecuted = true;\n                    if (actionItem.attr('data-confirm')) {\n                        // Action requires confirmation.\n                        confirmEditSection(actionItem.attr('data-confirm'), function() {\n                            isExecuted = editSection(sectionElement, sectionId, actionItem, courseformat);\n                        });\n                    } else {\n                        isExecuted = editSection(sectionElement, sectionId, actionItem, courseformat);\n                    }\n                    // Prevent any other module from capturing the action if it is already in execution.\n                    if (isExecuted) {\n                        e.preventDefault();\n                    }\n                });\n\n                // The section and activity names are edited using inplace editable.\n                // The \"update\" jQuery event must be captured in order to update the course state.\n                $('body').on('updated', `${SELECTOR.SECTIONITEM} [data-inplaceeditable]`, function(e) {\n                    if (e.ajaxreturn && e.ajaxreturn.itemid) {\n                        const state = courseeditor.state;\n                        const section = state.section.get(e.ajaxreturn.itemid);\n                        if (section !== undefined) {\n                            courseeditor.dispatch('sectionState', [e.ajaxreturn.itemid]);\n                        }\n                    }\n                });\n                $('body').on('updated', `${SELECTOR.ACTIVITYLI} [data-itemtype=\"activityname\"][data-inplaceeditable]`, function(e) {\n                    if (e.ajaxreturn && e.ajaxreturn.itemid) {\n                        courseeditor.dispatch('cmState', [e.ajaxreturn.itemid]);\n                    }\n                });\n\n                // Component-based formats don't use modals to create sections.\n                if (courseeditor.supportComponents && componentActions.includes('addSection')) {\n                    return;\n                }\n\n                // Add a handler for \"Add sections\" link to ask for a number of sections to add.\n                const trigger = $(SELECTOR.ADDSECTIONS);\n                const modalTitle = trigger.attr('data-add-sections');\n                const newSections = trigger.attr('data-new-sections');\n                str.get_string('numberweeks')\n                .then(function(strNumberSections) {\n                    var modalBody = $('<div><label for=\"add_section_numsections\"></label> ' +\n                        '<input id=\"add_section_numsections\" type=\"number\" min=\"1\" max=\"' + newSections + '\" value=\"1\"></div>');\n                    modalBody.find('label').html(strNumberSections);\n\n                    return modalBody.html();\n                })\n                .then((body) => ModalSaveCancel.create({\n                    body,\n                    title: modalTitle,\n                }))\n                .then(function(modal) {\n                    var numSections = $(modal.getBody()).find('#add_section_numsections'),\n                    addSections = function() {\n                        // Check if value of the \"Number of sections\" is a valid positive integer and redirect\n                        // to adding a section script.\n                        if ('' + parseInt(numSections.val()) === numSections.val() && parseInt(numSections.val()) >= 1) {\n                            document.location = trigger.attr('href') + '&numsections=' + parseInt(numSections.val());\n                        }\n                    };\n                    modal.setSaveButtonText(modalTitle);\n                    modal.getRoot().on(ModalEvents.shown, function() {\n                        // When modal is shown focus and select the input and add a listener to keypress of \"Enter\".\n                        numSections.focus().select().on('keydown', function(e) {\n                            if (e.keyCode === KeyCodes.enter) {\n                                addSections();\n                            }\n                        });\n                    });\n                    modal.getRoot().on(ModalEvents.save, function(e) {\n                        // When modal \"Add\" button is pressed.\n                        e.preventDefault();\n                        addSections();\n                    });\n\n                    trigger.on('click', (e) => {\n                        e.preventDefault();\n                        modal.show();\n                    });\n\n                    return modal;\n                })\n                .catch(notification.exception);\n            },\n\n            /**\n             * Replaces a section action menu item with another one (for example Show->Hide, Set marker->Remove marker)\n             *\n             * This method can be used by course formats in their listener to the coursesectionedited event\n             *\n             * @deprecated since Moodle 3.9\n             * @param {JQuery} sectionelement\n             * @param {String} selector CSS selector inside the section element, for example \"a[data-action=show]\"\n             * @param {String} image new image name (\"i/show\", \"i/hide\", etc.)\n             * @param {String} stringname new string for the action menu item\n             * @param {String} stringcomponent\n             * @param {String} newaction new value for data-action attribute of the link\n             */\n            replaceSectionActionItem: function(sectionelement, selector, image, stringname,\n                                                    stringcomponent, newaction) {\n                log.debug('replaceSectionActionItem() is deprecated and will be removed.');\n                var actionitem = sectionelement.find(SELECTOR.SECTIONACTIONMENU + ' ' + selector);\n                replaceActionItem(actionitem, image, stringname, stringcomponent, newaction);\n            },\n            // Method to refresh a module.\n            refreshModule,\n            refreshSection,\n        };\n    });\n"],"names":["define","$","ajax","templates","notification","str","url","Y","ModalCopyToClipboard","ModalSaveCancel","ModalEvents","KeyCodes","log","editor","EventDispatcher","CourseEvents","componentActions","courseeditor","getCurrentCourseEditor","formatname","CSS","SELECTOR","ACTIVITYLI","ACTIONAREA","ACTIVITYACTION","MENU","TOGGLE","SECTIONLI","SECTIONACTIONMENU","SECTIONACTIONMENUTRIGGER","SECTIONITEM","ADDSECTIONS","SECTIONBADGES","use","courseformatselector","M","course","format","get_section_selector","dispatchEvent","eventName","detail","container","options","Element","undefined","get","getModuleId","element","item","dataset","id","Moodle","core_course","util","cm","getId","Node","addActivitySpinner","activity","addClass","actionarea","find","spinner","add_spinner","show","data","dispatch","addSectionSpinner","sectionelement","addSectionLightbox","lightbox","add_lightbox","for","setAttribute","removeSpinner","delay","window","setTimeout","removeClass","hide","mutation","removeLightbox","getAttribute","initActionMenu","elementid","coursebase","invoke_function","core","actionmenu","newDOMNode","one","editModule","moduleElement","cmid","target","action","attr","promises","call","methodname","args","sectionreturn","closest","when","apply","done","mainElement","tabables","isInside","foundElement","elementToFocus","each","contains","this","replaceWith","affectedids","index","elementId","mainelement","selector","is","focus","focusActionItem","push","trigger","Event","ajaxreturn","fail","ex","e","exception","isDefaultPrevented","refreshModule","sectionReturn","activityElement","Promise","resolve","reject","replaceActivityHtmlWith","confirmDeleteModule","onconfirm","modtypename","match","modulename","name","getName","state","_state$cm$get","getModuleName","get_string","pluginname","plugindata","type","get_strings","key","component","param","s","confirm","replaceActionItem","actionitem","image","stringname","stringcomponent","newaction","stringRequests","then","strings","html","renderPix","pixhtml","catch","defaultEditSectionHandler","sectionElement","actionItem","courseformat","sectionid","setSectionBadge","modules","i","section_availability","first","section","oldmarker","oldActionItem","activityHTML","focusedPath","document","getElementById","activeElement","querySelector","getActivityFocusedElement","editSection","supportComponents","includes","dataencoded","parseJSON","badgetype","add","removeOther","sectionbadges","badge","querySelectorAll","forEach","b","classList","remove","register_module","set_visibility_resource_ui","getDOMNode","updateMovedCmState","params","updateMovedSectionState","addMutations","legacyActivityAction","statemanager","setReadOnly","locked","cmlist","reduce","current","delete","legacySectionAction","initCoursePage","on","keyCode","moduleId","preventDefault","sectionId","create","text","isExecuted","message","itemid","modalTitle","newSections","strNumberSections","modalBody","body","title","modal","numSections","getBody","addSections","parseInt","val","location","setSaveButtonText","getRoot","shown","select","enter","save","replaceSectionActionItem","debug","refreshSection","newSectionElement","content","sectionRefreshed","defaultPrevented"],"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 <http://www.gnu.org/licenses/>.\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_save_cancel',\n        'core/modal_events',\n        'core/key_codes',\n        'core/log',\n        'core_courseformat/courseeditor',\n        'core/event_dispatcher',\n        'core/local/inplace_editable/events',\n        'core_course/events'\n    ],\n    function(\n        $,\n        ajax,\n        templates,\n        notification,\n        str,\n        url,\n        Y,\n        ModalCopyToClipboard,\n        ModalSaveCancel,\n        ModalEvents,\n        KeyCodes,\n        log,\n        editor,\n        EventDispatcher,\n        InplaceEditableEvents,\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', 'cmMoveRight', 'cmMoveLeft',\n            'cmNoGroups', 'cmVisibleGroups', 'cmSeparateGroups',\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            SECTIONACTIONMENUTRIGGER: '.section-actions',\n            SECTIONITEM: '[data-for=\"section_title\"]',\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 <a> 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') : null\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                    $('<div>' + data + '</div>').find(SELECTOR.ACTIVITYLI).each(function(index) {\n                        initActionMenu($(this).attr('id'));\n                        if (index === 0) {\n                            focusActionItem($(this).attr('id'), action);\n                            elementToFocus = null;\n                        }\n                        // Save any activity id in cmids.\n                        affectedids.push(getModuleId($(this)));\n                    });\n                    // In case of activity deletion focus the next focusable element.\n                    if (elementToFocus) {\n                        elementToFocus.focus();\n                    }\n                    // Remove spinner and lightbox with a delay.\n                    removeSpinner(moduleElement, spinner, 400);\n                    removeLightbox(lightbox, 400);\n                    // Trigger event that can be observed by course formats.\n                    moduleElement.trigger($.Event('coursemoduleedited', {ajaxreturn: data, action: action}));\n\n                    // Modify cm state.\n                    courseeditor.dispatch('legacyActivityAction', action, cmid, affectedids);\n\n                }).fail(function(ex) {\n                    // Remove spinner and lightbox.\n                    removeSpinner(moduleElement, spinner);\n                    removeLightbox(lightbox);\n                    // Trigger event that can be observed by course formats.\n                    var e = $.Event('coursemoduleeditfailed', {exception: ex, action: action});\n                    moduleElement.trigger(e);\n                    if (!e.isDefaultPrevented()) {\n                        notification.exception(ex);\n                    }\n                });\n        };\n\n        /**\n         * Requests html for the module via WS core_course_get_module and updates the module on the course page\n         *\n         * Used after d&d of the module to another section\n         *\n         * @param {JQuery|Element} element\n         * @param {Number} cmid\n         * @param {Number} sectionreturn\n         * @return {Promise} the refresh promise\n         */\n        var refreshModule = function(element, cmid, sectionreturn) {\n\n            if (sectionreturn === undefined) {\n                sectionreturn = courseeditor.sectionReturn;\n            }\n\n            const activityElement = $(element);\n            var spinner = addActivitySpinner(activityElement);\n            var promises = ajax.call([{\n                methodname: 'core_course_get_module',\n                args: {id: cmid, sectionreturn: sectionreturn}\n            }], true);\n\n            return new Promise((resolve, reject) => {\n                $.when.apply($, promises)\n                    .done(function(data) {\n                        removeSpinner(activityElement, spinner, 400);\n                        replaceActivityHtmlWith(data);\n                        resolve(data);\n                    }).fail(function() {\n                        removeSpinner(activityElement, spinner);\n                        reject();\n                    });\n            });\n        };\n\n        /**\n         * Requests html for the section via WS core_course_edit_section and updates the section on the course page\n         *\n         * @param {JQuery|Element} element\n         * @param {Number} sectionid\n         * @param {Number} sectionreturn\n         * @return {Promise} the refresh promise\n         */\n        var refreshSection = function(element, sectionid, sectionreturn) {\n\n            if (sectionreturn === undefined) {\n                sectionreturn = courseeditor.sectionReturn;\n            }\n\n            const sectionElement = $(element);\n            const action = 'refresh';\n            const promises = ajax.call([{\n                methodname: 'core_course_edit_section',\n                args: {id: sectionid, action, sectionreturn},\n            }], true);\n\n            var spinner = addSectionSpinner(sectionElement);\n            return new Promise((resolve, reject) => {\n                $.when.apply($, promises)\n                    .done(dataencoded => {\n\n                        removeSpinner(sectionElement, spinner);\n                        const data = $.parseJSON(dataencoded);\n\n                        const newSectionElement = $(data.content);\n                        sectionElement.replaceWith(newSectionElement);\n\n                        // Init modules menus.\n                        $(`${SELECTOR.SECTIONLI}#${sectionid} ${SELECTOR.ACTIVITYLI}`).each(\n                            (index, activity) => {\n                                initActionMenu(activity.data('id'));\n                            }\n                        );\n\n                        // Trigger event that can be observed by course formats.\n                        const event = dispatchEvent(\n                            CourseEvents.sectionRefreshed,\n                            {\n                                ajaxreturn: data,\n                                action: action,\n                                newSectionElement: newSectionElement.get(0),\n                            },\n                            newSectionElement\n                        );\n\n                        if (!event.defaultPrevented) {\n                            defaultEditSectionHandler(\n                                newSectionElement, $(SELECTOR.SECTIONLI + '#' + sectionid),\n                                data,\n                                formatname,\n                                sectionid\n                            );\n                        }\n                        resolve(data);\n                    }).fail(ex => {\n                        // Trigger event that can be observed by course formats.\n                        const event = dispatchEvent(\n                            'coursesectionrefreshfailed',\n                            {exception: ex, action: action},\n                            sectionElement\n                        );\n                        if (!event.defaultPrevented) {\n                            notification.exception(ex);\n                        }\n                        reject();\n                    });\n            });\n        };\n\n        /**\n         * Displays the delete confirmation to delete a module\n         *\n         * @param {JQuery} mainelement activity element we perform action on\n         * @param {function} onconfirm function to execute on confirm\n         */\n        var confirmDeleteModule = function(mainelement, onconfirm) {\n            var modtypename = mainelement.attr('class').match(/modtype_([^\\s]*)/)[1];\n            var modulename = getModuleName(mainelement);\n\n            str.get_string('pluginname', modtypename).done(function(pluginname) {\n                var plugindata = {\n                    type: pluginname,\n                    name: modulename\n                };\n                str.get_strings([\n                    {key: 'confirm', component: 'core'},\n                    {key: modulename === null ? 'deletechecktype' : 'deletechecktypename', param: plugindata},\n                    {key: 'yes'},\n                    {key: 'no'}\n                ]).done(function(s) {\n                        notification.confirm(s[0], s[1], s[2], s[3], onconfirm);\n                    }\n                );\n            });\n        };\n\n        /**\n         * Displays the delete confirmation to delete a section\n         *\n         * @param {String} message confirmation message\n         * @param {function} onconfirm function to execute on confirm\n         */\n        var confirmEditSection = function(message, onconfirm) {\n            str.get_strings([\n                {key: 'confirm'}, // TODO link text\n                {key: 'yes'},\n                {key: 'no'}\n            ]).done(function(s) {\n                    notification.confirm(s[0], message, s[1], s[2], onconfirm);\n                }\n            );\n        };\n\n        /**\n         * Replaces an action menu item with another one (for example Show->Hide, Set marker->Remove marker)\n         *\n         * @param {JQuery} actionitem\n         * @param {String} image new image name (\"i/show\", \"i/hide\", etc.)\n         * @param {String} stringname new string for the action menu item\n         * @param {String} stringcomponent\n         * @param {String} newaction new value for data-action attribute of the link\n         * @return {Promise} promise which is resolved when the replacement has completed\n         */\n        var replaceActionItem = function(actionitem, image, stringname,\n                                           stringcomponent, newaction) {\n\n            var stringRequests = [{key: stringname, component: stringcomponent}];\n            // Do not provide an icon with duplicate, different text to the menu item.\n\n            return str.get_strings(stringRequests).then(function(strings) {\n                actionitem.find('span.menu-action-text').html(strings[0]);\n\n                return templates.renderPix(image, 'core');\n            }).then(function(pixhtml) {\n                actionitem.find('.icon').replaceWith(pixhtml);\n                actionitem.attr('data-action', newaction);\n                return;\n            }).catch(notification.exception);\n        };\n\n        /**\n         * Default post-processing for section AJAX edit actions.\n         *\n         * This can be overridden in course formats by listening to event coursesectionedited:\n         *\n         * $('body').on('coursesectionedited', 'li.section', function(e) {\n         *     var action = e.action,\n         *         sectionElement = $(e.target),\n         *         data = e.ajaxreturn;\n         *     // ... Do some processing here.\n         *     e.preventDefault(); // Prevent default handler.\n         * });\n         *\n         * @param {JQuery} sectionElement\n         * @param {JQuery} actionItem\n         * @param {Object} data\n         * @param {String} courseformat\n         * @param {Number} sectionid\n         */\n        var defaultEditSectionHandler = function(sectionElement, actionItem, data, courseformat, sectionid) {\n            var action = actionItem.attr('data-action');\n            if (action === 'hide' || action === 'show') {\n                if (action === 'hide') {\n                    sectionElement.addClass('hidden');\n                    setSectionBadge(sectionElement[0], 'hiddenfromstudents', true, false);\n                    replaceActionItem(actionItem, 'i/show',\n                        'showfromothers', 'format_' + courseformat, 'show');\n                } else {\n                    setSectionBadge(sectionElement[0], 'hiddenfromstudents', false, false);\n                    sectionElement.removeClass('hidden');\n                    replaceActionItem(actionItem, 'i/hide',\n                        'hidefromothers', 'format_' + courseformat, 'hide');\n                }\n                // Replace the modules with new html (that indicates that they are now hidden or not hidden).\n                if (data.modules !== undefined) {\n                    for (var i in data.modules) {\n                        replaceActivityHtmlWith(data.modules[i]);\n                    }\n                }\n                // Replace the section availability information.\n                if (data.section_availability !== undefined) {\n                    sectionElement.find('.section_availability').first().replaceWith(data.section_availability);\n                }\n                // Modify course state.\n                const section = courseeditor.state.section.get(sectionid);\n                if (section !== undefined) {\n                    courseeditor.dispatch('sectionState', [sectionid]);\n                }\n            } else if (action === 'setmarker') {\n                var oldmarker = $(SELECTOR.SECTIONLI + '.current'),\n                    oldActionItem = oldmarker.find(SELECTOR.SECTIONACTIONMENU + ' ' + 'a[data-action=removemarker]');\n                oldmarker.removeClass('current');\n                replaceActionItem(oldActionItem, 'i/marker',\n                    'highlight', 'core', 'setmarker');\n                sectionElement.addClass('current');\n                replaceActionItem(actionItem, 'i/marked',\n                    'highlightoff', 'core', 'removemarker');\n                courseeditor.dispatch('legacySectionAction', action, sectionid);\n                setSectionBadge(sectionElement[0], 'iscurrent', true, true);\n            } else if (action === 'removemarker') {\n                sectionElement.removeClass('current');\n                replaceActionItem(actionItem, 'i/marker',\n                    'highlight', 'core', 'setmarker');\n                courseeditor.dispatch('legacySectionAction', action, sectionid);\n                setSectionBadge(sectionElement[0], 'iscurrent', false, true);\n            }\n        };\n\n        /**\n         * Get the focused element path in an activity if any.\n         *\n         * This method is used to restore focus when the activity HTML is refreshed.\n         * Only the main course editor elements can be refocused as they are always present\n         * even if the activity content changes.\n         *\n         * @param {String} id the element id the activity element\n         * @return {String|undefined} the inner path of the focused element or undefined\n         */\n        const getActivityFocusedElement = function(id) {\n            const element = document.getElementById(id);\n            if (!element || !element.contains(document.activeElement)) {\n                return undefined;\n            }\n            // Check if the actions menu toggler is focused.\n            if (element.querySelector(SELECTOR.ACTIONAREA).contains(document.activeElement)) {\n                return `${SELECTOR.ACTIONAREA} [tabindex=\"0\"]`;\n            }\n            // Return the current element id if any.\n            if (document.activeElement.id) {\n                return `#${document.activeElement.id}`;\n            }\n            return undefined;\n        };\n\n        /**\n         * Replaces the course module with the new html (used to update module after it was edited or its visibility was changed).\n         *\n         * @param {String} activityHTML\n         */\n        var replaceActivityHtmlWith = function(activityHTML) {\n            $('<div>' + activityHTML + '</div>').find(SELECTOR.ACTIVITYLI).each(function() {\n                // Extract id from the new activity html.\n                var id = $(this).attr('id');\n                // Check if the current focused element is inside the activity.\n                let focusedPath = getActivityFocusedElement(id);\n                // Find the existing element with the same id and replace its contents with new html.\n                $(SELECTOR.ACTIVITYLI + '#' + id).replaceWith(activityHTML);\n                // Initialise action menu.\n                initActionMenu(id);\n                // Re-focus the previous elements.\n                if (focusedPath) {\n                    const newItem = document.getElementById(id);\n                    newItem.querySelector(focusedPath)?.focus();\n                }\n\n            });\n        };\n\n        /**\n         * Performs an action on a module (moving, deleting, duplicating, hiding, etc.)\n         *\n         * @param {JQuery} sectionElement section element we perform action on\n         * @param {Nunmber} sectionid\n         * @param {JQuery} target the element (menu item) that was clicked\n         * @param {String} courseformat\n         * @return {boolean} true the action call is sent to the server or false if it is ignored.\n         */\n        var editSection = function(sectionElement, sectionid, target, courseformat) {\n            var action = target.attr('data-action'),\n                sectionreturn = target.attr('data-sectionreturn') ? target.attr('data-sectionreturn') : null;\n\n            // Filter direct component handled actions.\n            if (courseeditor.supportComponents && componentActions.includes(action)) {\n                return false;\n            }\n\n            var spinner = addSectionSpinner(sectionElement);\n            var promises = ajax.call([{\n                methodname: 'core_course_edit_section',\n                args: {id: sectionid, action: action, sectionreturn: sectionreturn}\n            }], true);\n\n            var lightbox = addSectionLightbox(sectionElement);\n            $.when.apply($, promises)\n                .done(function(dataencoded) {\n                    var data = $.parseJSON(dataencoded);\n                    removeSpinner(sectionElement, spinner);\n                    removeLightbox(lightbox);\n                    sectionElement.find(SELECTOR.SECTIONACTIONMENU).find(SELECTOR.TOGGLE).focus();\n                    // Trigger event that can be observed by course formats.\n                    var e = $.Event('coursesectionedited', {ajaxreturn: data, action: action});\n                    sectionElement.trigger(e);\n                    if (!e.isDefaultPrevented()) {\n                        defaultEditSectionHandler(sectionElement, target, data, courseformat, sectionid);\n                    }\n                }).fail(function(ex) {\n                    // Remove spinner and lightbox.\n                    removeSpinner(sectionElement, spinner);\n                    removeLightbox(lightbox);\n                    // Trigger event that can be observed by course formats.\n                    var e = $.Event('coursesectioneditfailed', {exception: ex, action: action});\n                    sectionElement.trigger(e);\n                    if (!e.isDefaultPrevented()) {\n                        notification.exception(ex);\n                    }\n                });\n            return true;\n        };\n\n        /**\n         * Sets the section badge in the section header.\n         *\n         * @param {JQuery} sectionElement section element we perform action on\n         * @param {String} badgetype the type of badge this is for\n         * @param {bool} add true to add, false to remove\n         * @param {boolean} removeOther in case of adding a badge, whether to remove all other.\n         */\n        var setSectionBadge = function(sectionElement, badgetype, add, removeOther) {\n            const sectionbadges = sectionElement.querySelector(SELECTOR.SECTIONBADGES);\n            if (!sectionbadges) {\n                return;\n            }\n            const badge = sectionbadges.querySelector('[data-type=\"' + badgetype + '\"]');\n            if (!badge) {\n                return;\n            }\n            if (add) {\n                if (removeOther) {\n                    document.querySelectorAll('[data-type=\"' + badgetype + '\"]').forEach((b) => {\n                        b.classList.add('d-none');\n                    });\n                }\n                badge.classList.remove('d-none');\n            } else {\n                badge.classList.add('d-none');\n            }\n        };\n\n        // Register a function to be executed after D&D of an activity.\n        Y.use('moodle-course-coursebase', function() {\n            M.course.coursebase.register_module({\n                // Ignore camelcase eslint rule for the next line because it is an expected name of the callback.\n                // eslint-disable-next-line camelcase\n                set_visibility_resource_ui: function(args) {\n                    var mainelement = $(args.element.getDOMNode());\n                    var cmid = getModuleId(mainelement);\n                    if (cmid) {\n                        var sectionreturn = mainelement.find('.' + CSS.EDITINGMOVE).attr('data-sectionreturn');\n                        refreshModule(mainelement, cmid, sectionreturn);\n                    }\n                },\n                /**\n                 * Update the course state when some cm is moved via YUI.\n                 * @param {*} params\n                 */\n                updateMovedCmState: (params) => {\n                    const state = courseeditor.state;\n\n                    // Update old section.\n                    const cm = state.cm.get(params.cmid);\n                    if (cm !== undefined) {\n                        courseeditor.dispatch('sectionState', [cm.sectionid]);\n                    }\n                    // Update cm state.\n                    courseeditor.dispatch('cmState', [params.cmid]);\n                },\n                /**\n                 * Update the course state when some section is moved via YUI.\n                 */\n                updateMovedSectionState: () => {\n                    courseeditor.dispatch('courseState');\n                },\n            });\n        });\n\n        // From Moodle 4.0 all edit actions are being re-implemented as state mutation.\n        // This means all method from this \"actions\" module will be deprecated when all the course\n        // interface is migrated to reactive components.\n        // Most legacy actions did not provide enough information to regenarate the course so they\n        // use the mutations courseState, sectionState and cmState to get the updated state from\n        // the server. However, some activity actions where we can prevent an extra webservice\n        // call by implementing an adhoc mutation.\n        courseeditor.addMutations({\n            /**\n             * Compatibility function to update Moodle 4.0 course state using legacy actions.\n             *\n             * This method only updates some actions which does not require to use cmState mutation\n             * to get updated data form the server.\n             *\n             * @param {Object} statemanager the current state in read write mode\n             * @param {String} action the performed action\n             * @param {Number} cmid the affected course module id\n             * @param {Array} affectedids all affected cm ids (for duplicate action)\n             */\n            legacyActivityAction: function(statemanager, action, cmid, affectedids) {\n\n                const state = statemanager.state;\n                const cm = state.cm.get(cmid);\n                if (cm === undefined) {\n                    return;\n                }\n                const section = state.section.get(cm.sectionid);\n                if (section === undefined) {\n                    return;\n                }\n\n                // Send the element is locked.\n                courseeditor.dispatch('cmLock', [cm.id], true);\n\n                // Now we do the real mutation.\n                statemanager.setReadOnly(false);\n\n                // This unlocked will take effect when the read only is restored.\n                cm.locked = false;\n\n                switch (action) {\n                    case 'delete':\n                        // Remove from section.\n                        section.cmlist = section.cmlist.reduce(\n                            (cmlist, current) => {\n                                if (current != cmid) {\n                                    cmlist.push(current);\n                                }\n                                return cmlist;\n                            },\n                            []\n                        );\n                        // Delete form list.\n                        state.cm.delete(cmid);\n                        break;\n\n                    case 'hide':\n                    case 'show':\n                    case 'duplicate':\n                        courseeditor.dispatch('cmState', affectedids);\n                        break;\n                }\n                statemanager.setReadOnly(true);\n            },\n            legacySectionAction: function(statemanager, action, sectionid) {\n\n                const state = statemanager.state;\n                const section = state.section.get(sectionid);\n                if (section === undefined) {\n                    return;\n                }\n\n                // Send the element is locked. Reactive events are only triggered when the state\n                // read only mode is restored. We want to notify the interface the element is\n                // locked so we need to do a quick lock operation before performing the rest\n                // of the mutation.\n                statemanager.setReadOnly(false);\n                section.locked = true;\n                statemanager.setReadOnly(true);\n\n                // Now we do the real mutation.\n                statemanager.setReadOnly(false);\n\n                // This locked will take effect when the read only is restored.\n                section.locked = false;\n\n                switch (action) {\n                    case 'setmarker':\n                        // Remove previous marker.\n                        state.section.forEach((current) => {\n                            if (current.id != sectionid) {\n                                current.current = false;\n                            }\n                        });\n                        section.current = true;\n                        break;\n\n                    case 'removemarker':\n                        section.current = false;\n                        break;\n                }\n                statemanager.setReadOnly(true);\n            },\n        });\n\n        return /** @alias module:core_course/actions */ {\n\n            /**\n             * Initialises course page\n             *\n             * @method init\n             * @param {String} courseformat name of the current course format (for fetching strings)\n             */\n            initCoursePage: function(courseformat) {\n\n                formatname = courseformat;\n\n                // Add a handler for course module actions.\n                $('body').on('click keypress', SELECTOR.ACTIVITYLI + ' ' +\n                        SELECTOR.ACTIVITYACTION + '[data-action]', function(e) {\n                    if (e.type === 'keypress' && e.keyCode !== 13) {\n                        return;\n                    }\n                    var actionItem = $(this),\n                        moduleElement = actionItem.closest(SELECTOR.ACTIVITYLI),\n                        action = actionItem.attr('data-action'),\n                        moduleId = getModuleId(moduleElement);\n                    switch (action) {\n                        case 'moveleft':\n                        case 'moveright':\n                        case 'delete':\n                        case 'duplicate':\n                        case 'hide':\n                        case 'stealth':\n                        case 'show':\n                        case 'groupsseparate':\n                        case 'groupsvisible':\n                        case 'groupsnone':\n                            break;\n                        default:\n                            // Nothing to do here!\n                            return;\n                    }\n                    if (!moduleId) {\n                        return;\n                    }\n                    e.preventDefault();\n                    if (action === 'delete') {\n                        // Deleting requires confirmation.\n                        confirmDeleteModule(moduleElement, function() {\n                            editModule(moduleElement, moduleId, actionItem);\n                        });\n                    } else {\n                        editModule(moduleElement, moduleId, actionItem);\n                    }\n                });\n\n                // Add a handler for section action menu.\n                $('body').on('click keypress',\n                            SELECTOR.SECTIONACTIONMENUTRIGGER + '[data-sectionid] ' +\n                            'a[data-action]', function(e) {\n                    if (e.type === 'keypress' && e.keyCode !== 13) {\n                        return;\n                    }\n                    var actionItem = $(this),\n                        sectionElement = actionItem.closest(SELECTOR.SECTIONLI),\n                        sectionId = actionItem.closest(SELECTOR.SECTIONACTIONMENUTRIGGER).attr('data-sectionid');\n\n                    if (actionItem.attr('data-action') === 'permalink') {\n                        e.preventDefault();\n                        ModalCopyToClipboard.create({\n                            text: actionItem.attr('href'),\n                        }, str.get_string('sectionlink', 'course')\n                        );\n                        return;\n                    }\n\n                    let isExecuted = true;\n                    if (actionItem.attr('data-confirm')) {\n                        // Action requires confirmation.\n                        confirmEditSection(actionItem.attr('data-confirm'), function() {\n                            isExecuted = editSection(sectionElement, sectionId, actionItem, courseformat);\n                        });\n                    } else {\n                        isExecuted = editSection(sectionElement, sectionId, actionItem, courseformat);\n                    }\n                    // Prevent any other module from capturing the action if it is already in execution.\n                    if (isExecuted) {\n                        e.preventDefault();\n                    }\n                });\n\n                // The section and activity names are edited using inplace editable.\n                // The \"update\" jQuery event must be captured in order to update the course state.\n                $('body').on(InplaceEditableEvents.eventTypes.elementUpdated,\n                        `${SELECTOR.SECTIONITEM} [data-inplaceeditable]`, function(e) {\n                    if (e.detail.ajaxreturn.itemid) {\n                        const state = courseeditor.state;\n                        const section = state.section.get(e.detail.ajaxreturn.itemid);\n                        if (section !== undefined) {\n                            courseeditor.dispatch('sectionState', [e.detail.ajaxreturn.itemid]);\n                        }\n                    }\n                });\n\n                $('body').on(InplaceEditableEvents.eventTypes.elementUpdated,\n                        `${SELECTOR.ACTIVITYLI} [data-itemtype=\"activityname\"][data-inplaceeditable]`, function(e) {\n                    if (e.detail.ajaxreturn.itemid) {\n                        courseeditor.dispatch('cmState', [e.detail.ajaxreturn.itemid]);\n                    }\n                });\n\n                // Component-based formats don't use modals to create sections.\n                if (courseeditor.supportComponents && componentActions.includes('addSection')) {\n                    return;\n                }\n\n                // Add a handler for \"Add sections\" link to ask for a number of sections to add.\n                const trigger = $(SELECTOR.ADDSECTIONS);\n                const modalTitle = trigger.attr('data-add-sections');\n                const newSections = trigger.attr('data-new-sections');\n                str.get_string('numberweeks')\n                .then(function(strNumberSections) {\n                    var modalBody = $('<div><label for=\"add_section_numsections\"></label> ' +\n                        '<input id=\"add_section_numsections\" type=\"number\" min=\"1\" max=\"' + newSections + '\" value=\"1\"></div>');\n                    modalBody.find('label').html(strNumberSections);\n\n                    return modalBody.html();\n                })\n                .then((body) => ModalSaveCancel.create({\n                    body,\n                    title: modalTitle,\n                }))\n                .then(function(modal) {\n                    var numSections = $(modal.getBody()).find('#add_section_numsections'),\n                    addSections = function() {\n                        // Check if value of the \"Number of sections\" is a valid positive integer and redirect\n                        // to adding a section script.\n                        if ('' + parseInt(numSections.val()) === numSections.val() && parseInt(numSections.val()) >= 1) {\n                            document.location = trigger.attr('href') + '&numsections=' + parseInt(numSections.val());\n                        }\n                    };\n                    modal.setSaveButtonText(modalTitle);\n                    modal.getRoot().on(ModalEvents.shown, function() {\n                        // When modal is shown focus and select the input and add a listener to keypress of \"Enter\".\n                        numSections.focus().select().on('keydown', function(e) {\n                            if (e.keyCode === KeyCodes.enter) {\n                                addSections();\n                            }\n                        });\n                    });\n                    modal.getRoot().on(ModalEvents.save, function(e) {\n                        // When modal \"Add\" button is pressed.\n                        e.preventDefault();\n                        addSections();\n                    });\n\n                    trigger.on('click', (e) => {\n                        e.preventDefault();\n                        modal.show();\n                    });\n\n                    return modal;\n                })\n                .catch(notification.exception);\n            },\n\n            /**\n             * Replaces a section action menu item with another one (for example Show->Hide, Set marker->Remove marker)\n             *\n             * This method can be used by course formats in their listener to the coursesectionedited event\n             *\n             * @deprecated since Moodle 3.9\n             * @param {JQuery} sectionelement\n             * @param {String} selector CSS selector inside the section element, for example \"a[data-action=show]\"\n             * @param {String} image new image name (\"i/show\", \"i/hide\", etc.)\n             * @param {String} stringname new string for the action menu item\n             * @param {String} stringcomponent\n             * @param {String} newaction new value for data-action attribute of the link\n             */\n            replaceSectionActionItem: function(sectionelement, selector, image, stringname,\n                                                    stringcomponent, newaction) {\n                log.debug('replaceSectionActionItem() is deprecated and will be removed.');\n                var actionitem = sectionelement.find(SELECTOR.SECTIONACTIONMENU + ' ' + selector);\n                replaceActionItem(actionitem, image, stringname, stringcomponent, newaction);\n            },\n            // Method to refresh a module.\n            refreshModule,\n            refreshSection,\n        };\n    });\n"],"names":["define","$","ajax","templates","notification","str","url","Y","ModalCopyToClipboard","ModalSaveCancel","ModalEvents","KeyCodes","log","editor","EventDispatcher","InplaceEditableEvents","CourseEvents","componentActions","courseeditor","getCurrentCourseEditor","formatname","CSS","SELECTOR","ACTIVITYLI","ACTIONAREA","ACTIVITYACTION","MENU","TOGGLE","SECTIONLI","SECTIONACTIONMENU","SECTIONACTIONMENUTRIGGER","SECTIONITEM","ADDSECTIONS","SECTIONBADGES","use","courseformatselector","M","course","format","get_section_selector","dispatchEvent","eventName","detail","container","options","Element","undefined","get","getModuleId","element","item","dataset","id","Moodle","core_course","util","cm","getId","Node","addActivitySpinner","activity","addClass","actionarea","find","spinner","add_spinner","show","data","dispatch","addSectionSpinner","sectionelement","addSectionLightbox","lightbox","add_lightbox","for","setAttribute","removeSpinner","delay","window","setTimeout","removeClass","hide","mutation","removeLightbox","getAttribute","initActionMenu","elementid","coursebase","invoke_function","core","actionmenu","newDOMNode","one","editModule","moduleElement","cmid","target","action","attr","promises","call","methodname","args","sectionreturn","closest","when","apply","done","mainElement","tabables","isInside","foundElement","elementToFocus","each","contains","this","replaceWith","affectedids","index","elementId","mainelement","selector","is","focus","focusActionItem","push","trigger","Event","ajaxreturn","fail","ex","e","exception","isDefaultPrevented","refreshModule","sectionReturn","activityElement","Promise","resolve","reject","replaceActivityHtmlWith","confirmDeleteModule","onconfirm","modtypename","match","modulename","name","getName","state","_state$cm$get","getModuleName","get_string","pluginname","plugindata","type","get_strings","key","component","param","s","confirm","replaceActionItem","actionitem","image","stringname","stringcomponent","newaction","stringRequests","then","strings","html","renderPix","pixhtml","catch","defaultEditSectionHandler","sectionElement","actionItem","courseformat","sectionid","setSectionBadge","modules","i","section_availability","first","section","oldmarker","oldActionItem","activityHTML","focusedPath","document","getElementById","activeElement","querySelector","getActivityFocusedElement","editSection","supportComponents","includes","dataencoded","parseJSON","badgetype","add","removeOther","sectionbadges","badge","querySelectorAll","forEach","b","classList","remove","register_module","set_visibility_resource_ui","getDOMNode","updateMovedCmState","params","updateMovedSectionState","addMutations","legacyActivityAction","statemanager","setReadOnly","locked","cmlist","reduce","current","delete","legacySectionAction","initCoursePage","on","keyCode","moduleId","preventDefault","sectionId","create","text","isExecuted","message","eventTypes","elementUpdated","itemid","modalTitle","newSections","strNumberSections","modalBody","body","title","modal","numSections","getBody","addSections","parseInt","val","location","setSaveButtonText","getRoot","shown","select","enter","save","replaceSectionActionItem","debug","refreshSection","newSectionElement","content","sectionRefreshed","defaultPrevented"],"mappings":";;;;;;;;AAuBAA,6BACI,CACI,SACA,YACA,iBACA,oBACA,WACA,WACA,WACA,+BACA,yBACA,oBACA,iBACA,WACA,iCACA,wBACA,qCACA,uBAEJ,SACIC,EACAC,KACAC,UACAC,aACAC,IACAC,IACAC,EACAC,qBACAC,gBACAC,YACAC,SACAC,IACAC,OACAC,gBACAC,sBACAC,oBAMMC,iBAAmB,CACrB,cAAe,SAAU,aAAc,gBAAiB,WAAY,cAAe,cAAe,cAClG,SAAU,SAAU,YAAa,mBAAoB,qBAAsB,cAAe,aAC1F,aAAc,kBAAmB,oBAI/BC,aAAeL,OAAOM,6BAGxBC,eAEAC,mBACgB,iBADhBA,gBAGa,eAEbC,SAAW,CACXC,WAAY,cACZC,WAAY,WACZC,eAAgB,mBAChBC,KAAM,0DACNC,OAAQ,mCACRC,UAAW,aACXC,kBAAmB,uBACnBC,yBAA0B,mBAC1BC,YAAa,6BACbC,YAAa,yCACbC,cAAe,iCAGnB1B,EAAE2B,IAAI,4BAA4B,eAC1BC,qBAAuBC,EAAEC,OAAOC,OAAOC,uBACvCJ,uBACAb,SAASM,UAAYO,+BAmBvBK,cAAgB,SAASC,UAAWC,OAAQC,UAAWC,gBAEnDD,qBAAqBE,cAA8BC,IAAlBH,UAAUI,MAC7CJ,UAAYA,UAAUI,IAAI,IAEvBjC,gBAAgB0B,cAAcC,UAAWC,OAAQC,UAAWC,cASnEI,YAAc,SAASC,eAEjBC,KAAOD,QAAQF,IAAI,MACrBG,KAAKC,QAAQC,UACNF,KAAKC,QAAQC,OAGpBA,UACJ7C,EAAE2B,IAAI,sBAAsB,SAAS3B,GACjC6C,GAAK7C,EAAE8C,OAAOC,YAAYC,KAAKC,GAAGC,MAAMlD,EAAEmD,KAAKR,UAE5CE,IA6BPO,mBAAqB,SAASC,UAC9BA,SAASC,SAASxC,wBACdyC,WAAaF,SAASG,KAAKzC,SAASE,YAAYuB,IAAI,MACpDe,WAAY,KACRE,QAAU5B,EAAEmB,KAAKU,YAAY1D,EAAGA,EAAEmD,KAAKI,oBAC3CE,QAAQE,YAEoBpB,IAAxBc,SAASO,KAAK,OACdjD,aAAakD,SAAS,SAAU,CAACR,SAASO,KAAK,QAAQ,GAEpDH,eAEJ,MASPK,kBAAoB,SAASC,gBAC7BA,eAAeT,SAASxC,wBACpByC,WAAaQ,eAAeP,KAAKzC,SAASO,mBAAmBkB,IAAI,MACjEe,WAAY,KACRE,QAAU5B,EAAEmB,KAAKU,YAAY1D,EAAGA,EAAEmD,KAAKI,oBAC3CE,QAAQE,YAE0BpB,IAA9BwB,eAAeH,KAAK,OACpBjD,aAAakD,SAAS,cAAe,CAACE,eAAeH,KAAK,QAAQ,GAE/DH,eAEJ,MASPO,mBAAqB,SAASD,sBACxBpB,KAAOoB,eAAevB,IAAI,OAC5ByB,SAAWpC,EAAEmB,KAAKkB,aAAalE,EAAGA,EAAEmD,KAAKR,aACrB,WAApBA,KAAKC,QAAQuB,KAAoBxB,KAAKC,QAAQC,KAC9ClC,aAAakD,SAAS,cAAe,CAAClB,KAAKC,QAAQC,KAAK,GACxDoB,SAASG,aAAa,aAAc,WACpCH,SAASG,aAAa,gBAAiBzB,KAAKC,QAAQC,KAExDoB,SAASN,OACFM,UAUPI,cAAgB,SAAS3B,QAASe,QAASa,OAC3CC,OAAOC,YAAW,cACd9B,QAAQ+B,YAAY3D,oBAChB2C,SACAA,QAAQiB,YAGenC,IAAvBG,QAAQkB,KAAK,MAAqB,OAC5Be,SAAoC,YAAxBjC,QAAQkB,KAAK,OAAwB,cAAgB,SACvEjD,aAAakD,SAASc,SAAU,CAACjC,QAAQkB,KAAK,QAAQ,MAE3DU,QASHM,eAAiB,SAASX,SAAUK,OAChCL,UACAM,OAAOC,YAAW,WACdP,SAASS,OAELT,SAASY,aAAa,eACtBlE,aAAakD,mBACNI,SAASY,aAAa,sBACzB,CAACZ,SAASY,aAAa,mBACvB,KAGTP,QASPQ,eAAiB,SAASC,WAE1B/E,EAAE2B,IAAI,4BAA4B,WAC9BE,EAAEC,OAAOkD,WAAWC,gBAAgB,qBAAsB,IAAMF,cAEhElD,EAAEqD,KAAKC,YAActD,EAAEqD,KAAKC,WAAWC,YACvCvD,EAAEqD,KAAKC,WAAWC,WAAWpF,EAAEqF,IAAI,IAAMN,aAsD7CO,WAAa,SAASC,cAAeC,KAAMC,YAWvCxB,SAVAyB,OAASD,OAAOE,KAAK,eACrBlC,QAAUL,mBAAmBmC,eAC7BK,SAAWjG,KAAKkG,KAAK,CAAC,CACtBC,WAAY,0BACZC,KAAM,CAAClD,GAAI2C,KACPE,OAAQA,OACRM,cAAeP,OAAOE,KAAK,sBAAwBF,OAAOE,KAAK,sBAAwB,SAE3F,GAGW,cAAXD,SACAzB,SAAWD,mBAAmByB,OAAOQ,QAAQlF,SAASM,aAE1D3B,EAAEwG,KAAKC,MAAMzG,EAAGkG,UACXQ,MAAK,SAASxC,UAvCUyC,YACzBC,SACAC,SACAC,aAqCQC,gBAxCiBJ,YAwCkBd,cAvC3Ce,SAAW5G,EAAE,aACb6G,UAAW,EACXC,aAAe,KACnBF,SAASI,MAAK,cACNhH,EAAEiH,SAASN,YAAY,GAAIO,MAC3BL,UAAW,OACR,GAAIA,gBACPC,aAAeI,MACR,SAEJ,KAEJJ,cA4BCjB,cAAcsB,YAAYjD,UACtBkD,YAAc,GAElBpH,EAAE,QAAUkE,KAAO,UAAUJ,KAAKzC,SAASC,YAAY0F,MAAK,SAASK,OACjEjC,eAAepF,EAAEkH,MAAMjB,KAAK,OACd,IAAVoB,SAnEE,SAASC,UAAWtB,YAClCuB,YAAcvH,EAAE,IAAMsH,WACtBE,SAAW,gBAAkBxB,OAAS,IAC3B,mBAAXA,QAA0C,kBAAXA,QAAyC,eAAXA,SAE7DwB,SAAW,qFAEXD,YAAYzD,KAAK0D,UAAUC,GAAG,YAC9BF,YAAYzD,KAAK0D,UAAUE,QAG3BH,YAAYzD,KAAKzC,SAASI,MAAMqC,KAAKzC,SAASK,QAAQgG,QAyD1CC,CAAgB3H,EAAEkH,MAAMjB,KAAK,MAAOD,QACpCe,eAAiB,MAGrBK,YAAYQ,KAAK7E,YAAY/C,EAAEkH,WAG/BH,gBACAA,eAAeW,QAGnB/C,cAAckB,cAAe9B,QAAS,KACtCmB,eAAeX,SAAU,KAEzBsB,cAAcgC,QAAQ7H,EAAE8H,MAAM,qBAAsB,CAACC,WAAY7D,KAAM8B,OAAQA,UAG/E/E,aAAakD,SAAS,uBAAwB6B,OAAQF,KAAMsB,gBAE7DY,MAAK,SAASC,IAEbtD,cAAckB,cAAe9B,SAC7BmB,eAAeX,cAEX2D,EAAIlI,EAAE8H,MAAM,yBAA0B,CAACK,UAAWF,GAAIjC,OAAQA,SAClEH,cAAcgC,QAAQK,GACjBA,EAAEE,sBACHjI,aAAagI,UAAUF,QAenCI,cAAgB,SAASrF,QAAS8C,KAAMQ,oBAElBzD,IAAlByD,gBACAA,cAAgBrF,aAAaqH,qBAG3BC,gBAAkBvI,EAAEgD,aACtBe,QAAUL,mBAAmB6E,iBAC7BrC,SAAWjG,KAAKkG,KAAK,CAAC,CACtBC,WAAY,yBACZC,KAAM,CAAClD,GAAI2C,KAAMQ,cAAeA,kBAChC,UAEG,IAAIkC,SAAQ,CAACC,QAASC,UACzB1I,EAAEwG,KAAKC,MAAMzG,EAAGkG,UACXQ,MAAK,SAASxC,MACXS,cAAc4D,gBAAiBxE,QAAS,KACxC4E,wBAAwBzE,MACxBuE,QAAQvE,SACT8D,MAAK,WACJrD,cAAc4D,gBAAiBxE,SAC/B2E,gBAqFZE,oBAAsB,SAASrB,YAAasB,eACxCC,YAAcvB,YAAYtB,KAAK,SAAS8C,MAAM,oBAAoB,GAClEC,WApWY,SAAShG,aACrBiG,KACJ3I,EAAE2B,IAAI,sBAAsB,SAAS3B,GACjC2I,KAAO3I,EAAE8C,OAAOC,YAAYC,KAAKC,GAAG2F,QAAQ5I,EAAEmD,KAAKT,QAAQF,IAAI,cAG7DqG,MAAQlI,aAAakI,MACrBrD,KAAO/C,YAAYC,kCACpBiG,MAAQE,OAASrD,OAClBmD,2BAAOE,MAAM5F,GAAGT,IAAIgD,sCAAbsD,cAAoBH,MAExBA,KAyVUI,CAAc9B,aAE/BnH,IAAIkJ,WAAW,aAAcR,aAAapC,MAAK,SAAS6C,gBAChDC,WAAa,CACbC,KAAMF,WACNN,KAAMD,YAEV5I,IAAIsJ,YAAY,CACZ,CAACC,IAAK,UAAWC,UAAW,QAC5B,CAACD,IAAoB,OAAfX,WAAsB,kBAAoB,sBAAuBa,MAAOL,YAC9E,CAACG,IAAK,OACN,CAACA,IAAK,QACPjD,MAAK,SAASoD,GACT3J,aAAa4J,QAAQD,EAAE,GAAIA,EAAE,GAAIA,EAAE,GAAIA,EAAE,GAAIjB,kBAiCzDmB,kBAAoB,SAASC,WAAYC,MAAOC,WACjBC,gBAAiBC,eAE5CC,eAAiB,CAAC,CAACX,IAAKQ,WAAYP,UAAWQ,yBAG5ChK,IAAIsJ,YAAYY,gBAAgBC,MAAK,SAASC,gBACjDP,WAAWnG,KAAK,yBAAyB2G,KAAKD,QAAQ,IAE/CtK,UAAUwK,UAAUR,MAAO,WACnCK,MAAK,SAASI,SACbV,WAAWnG,KAAK,SAASqD,YAAYwD,SACrCV,WAAWhE,KAAK,cAAeoE,cAEhCO,MAAMzK,aAAagI,YAsBtB0C,0BAA4B,SAASC,eAAgBC,WAAY7G,KAAM8G,aAAcC,eACjFjF,OAAS+E,WAAW9E,KAAK,kBACd,SAAXD,QAAgC,SAAXA,OAAmB,IACzB,SAAXA,QACA8E,eAAelH,SAAS,UACxBsH,gBAAgBJ,eAAe,GAAI,sBAAsB,GAAM,GAC/Dd,kBAAkBe,WAAY,SAC1B,iBAAkB,UAAYC,aAAc,UAEhDE,gBAAgBJ,eAAe,GAAI,sBAAsB,GAAO,GAChEA,eAAe/F,YAAY,UAC3BiF,kBAAkBe,WAAY,SAC1B,iBAAkB,UAAYC,aAAc,cAG/BnI,IAAjBqB,KAAKiH,YACA,IAAIC,KAAKlH,KAAKiH,QACfxC,wBAAwBzE,KAAKiH,QAAQC,SAIXvI,IAA9BqB,KAAKmH,sBACLP,eAAehH,KAAK,yBAAyBwH,QAAQnE,YAAYjD,KAAKmH,2BAI1DxI,IADA5B,aAAakI,MAAMoC,QAAQzI,IAAImI,YAE3ChK,aAAakD,SAAS,eAAgB,CAAC8G,iBAExC,GAAe,cAAXjF,OAAwB,KAC3BwF,UAAYxL,EAAEqB,SAASM,UAAY,YACnC8J,cAAgBD,UAAU1H,KAAKzC,SAASO,kBAATP,gCACnCmK,UAAUzG,YAAY,WACtBiF,kBAAkByB,cAAe,WAC7B,YAAa,OAAQ,aACzBX,eAAelH,SAAS,WACxBoG,kBAAkBe,WAAY,WAC1B,eAAgB,OAAQ,gBAC5B9J,aAAakD,SAAS,sBAAuB6B,OAAQiF,WACrDC,gBAAgBJ,eAAe,GAAI,aAAa,GAAM,OACpC,iBAAX9E,SACP8E,eAAe/F,YAAY,WAC3BiF,kBAAkBe,WAAY,WAC1B,YAAa,OAAQ,aACzB9J,aAAakD,SAAS,sBAAuB6B,OAAQiF,WACrDC,gBAAgBJ,eAAe,GAAI,aAAa,GAAO,SAmC3DnC,wBAA0B,SAAS+C,cACnC1L,EAAE,QAAU0L,aAAe,UAAU5H,KAAKzC,SAASC,YAAY0F,MAAK,eAE5D7D,GAAKnD,EAAEkH,MAAMjB,KAAK,UAElB0F,YA1BsB,SAASxI,UACjCH,QAAU4I,SAASC,eAAe1I,OACnCH,SAAYA,QAAQiE,SAAS2E,SAASE,sBAIvC9I,QAAQ+I,cAAc1K,SAASE,YAAY0F,SAAS2E,SAASE,yBACnDzK,SAASE,8BAGnBqK,SAASE,cAAc3I,cACZyI,SAASE,cAAc3I,WAehB6I,CAA0B7I,OAE5CnD,EAAEqB,SAASC,WAAa,IAAM6B,IAAIgE,YAAYuE,cAE9CtG,eAAejC,IAEXwI,YAAa,yDACGC,SAASC,eAAe1I,IAChC4I,cAAcJ,qEAAcjE,aAe5CuE,YAAc,SAASnB,eAAgBG,UAAWlF,OAAQiF,kBACtDhF,OAASD,OAAOE,KAAK,eACrBK,cAAgBP,OAAOE,KAAK,sBAAwBF,OAAOE,KAAK,sBAAwB,QAGxFhF,aAAaiL,mBAAqBlL,iBAAiBmL,SAASnG,eACrD,MAGPjC,QAAUK,kBAAkB0G,gBAC5B5E,SAAWjG,KAAKkG,KAAK,CAAC,CACtBC,WAAY,2BACZC,KAAM,CAAClD,GAAI8H,UAAWjF,OAAQA,OAAQM,cAAeA,kBACrD,GAEA/B,SAAWD,mBAAmBwG,uBAClC9K,EAAEwG,KAAKC,MAAMzG,EAAGkG,UACXQ,MAAK,SAAS0F,iBACPlI,KAAOlE,EAAEqM,UAAUD,aACvBzH,cAAcmG,eAAgB/G,SAC9BmB,eAAeX,UACfuG,eAAehH,KAAKzC,SAASO,mBAAmBkC,KAAKzC,SAASK,QAAQgG,YAElEQ,EAAIlI,EAAE8H,MAAM,sBAAuB,CAACC,WAAY7D,KAAM8B,OAAQA,SAClE8E,eAAejD,QAAQK,GAClBA,EAAEE,sBACHyC,0BAA0BC,eAAgB/E,OAAQ7B,KAAM8G,aAAcC,cAE3EjD,MAAK,SAASC,IAEbtD,cAAcmG,eAAgB/G,SAC9BmB,eAAeX,cAEX2D,EAAIlI,EAAE8H,MAAM,0BAA2B,CAACK,UAAWF,GAAIjC,OAAQA,SACnE8E,eAAejD,QAAQK,GAClBA,EAAEE,sBACHjI,aAAagI,UAAUF,QAG5B,GAWPiD,gBAAkB,SAASJ,eAAgBwB,UAAWC,IAAKC,mBACrDC,cAAgB3B,eAAeiB,cAAc1K,SAASW,mBACvDyK,2BAGCC,MAAQD,cAAcV,cAAc,eAAiBO,UAAY,MAClEI,QAGDH,KACIC,aACAZ,SAASe,iBAAiB,eAAiBL,UAAY,MAAMM,SAASC,IAClEA,EAAEC,UAAUP,IAAI,aAGxBG,MAAMI,UAAUC,OAAO,WAEvBL,MAAMI,UAAUP,IAAI,mBAK5BjM,EAAE2B,IAAI,4BAA4B,WAC9BE,EAAEC,OAAOkD,WAAW0H,gBAAgB,CAGhCC,2BAA4B,SAAS5G,UAC7BkB,YAAcvH,EAAEqG,KAAKrD,QAAQkK,cAC7BpH,KAAO/C,YAAYwE,gBACnBzB,KAAM,KACFQ,cAAgBiB,YAAYzD,KAAK,IAAM1C,iBAAiB6E,KAAK,sBACjEoC,cAAcd,YAAazB,KAAMQ,iBAOzC6G,mBAAqBC,eAIX7J,GAHQtC,aAAakI,MAGV5F,GAAGT,IAAIsK,OAAOtH,WACpBjD,IAAPU,IACAtC,aAAakD,SAAS,eAAgB,CAACZ,GAAG0H,YAG9ChK,aAAakD,SAAS,UAAW,CAACiJ,OAAOtH,QAK7CuH,wBAAyB,KACrBpM,aAAakD,SAAS,qBAYlClD,aAAaqM,aAAa,CAYtBC,qBAAsB,SAASC,aAAcxH,OAAQF,KAAMsB,mBAEjD+B,MAAQqE,aAAarE,MACrB5F,GAAK4F,MAAM5F,GAAGT,IAAIgD,cACbjD,IAAPU,gBAGEgI,QAAUpC,MAAMoC,QAAQzI,IAAIS,GAAG0H,mBACrBpI,IAAZ0I,gBAKJtK,aAAakD,SAAS,SAAU,CAACZ,GAAGJ,KAAK,GAGzCqK,aAAaC,aAAY,GAGzBlK,GAAGmK,QAAS,EAEJ1H,YACC,SAEDuF,QAAQoC,OAASpC,QAAQoC,OAAOC,QAC5B,CAACD,OAAQE,WACDA,SAAW/H,MACX6H,OAAO/F,KAAKiG,SAETF,SAEX,IAGJxE,MAAM5F,GAAGuK,OAAOhI,gBAGf,WACA,WACA,YACD7E,aAAakD,SAAS,UAAWiD,aAGzCoG,aAAaC,aAAY,KAE7BM,oBAAqB,SAASP,aAAcxH,OAAQiF,iBAE1C9B,MAAQqE,aAAarE,MACrBoC,QAAUpC,MAAMoC,QAAQzI,IAAImI,mBAClBpI,IAAZ0I,gBAQJiC,aAAaC,aAAY,GACzBlC,QAAQmC,QAAS,EACjBF,aAAaC,aAAY,GAGzBD,aAAaC,aAAY,GAGzBlC,QAAQmC,QAAS,EAET1H,YACC,YAEDmD,MAAMoC,QAAQqB,SAASiB,UACfA,QAAQ1K,IAAM8H,YACd4C,QAAQA,SAAU,MAG1BtC,QAAQsC,SAAU,YAGjB,eACDtC,QAAQsC,SAAU,EAG1BL,aAAaC,aAAY,OAIe,CAQ5CO,eAAgB,SAAShD,iBAErB7J,WAAa6J,aAGbhL,EAAE,QAAQiO,GAAG,iBAAkB5M,SAASC,WAAa,IAC7CD,SAASG,eAAiB,iBAAiB,SAAS0G,MACzC,aAAXA,EAAEuB,MAAqC,KAAdvB,EAAEgG,aAG3BnD,WAAa/K,EAAEkH,MACfrB,cAAgBkF,WAAWxE,QAAQlF,SAASC,YAC5C0E,OAAS+E,WAAW9E,KAAK,eACzBkI,SAAWpL,YAAY8C,sBACnBG,YACC,eACA,gBACA,aACA,gBACA,WACA,cACA,WACA,qBACA,oBACA,kCAMJmI,WAGLjG,EAAEkG,iBACa,WAAXpI,OAEA4C,oBAAoB/C,eAAe,WAC/BD,WAAWC,cAAesI,SAAUpD,eAGxCnF,WAAWC,cAAesI,SAAUpD,iBAK5C/K,EAAE,QAAQiO,GAAG,iBACD5M,SAASQ,yBAATR,mCACkB,SAAS6G,MACpB,aAAXA,EAAEuB,MAAqC,KAAdvB,EAAEgG,mBAG3BnD,WAAa/K,EAAEkH,MACf4D,eAAiBC,WAAWxE,QAAQlF,SAASM,WAC7C0M,UAAYtD,WAAWxE,QAAQlF,SAASQ,0BAA0BoE,KAAK,qBAEpC,cAAnC8E,WAAW9E,KAAK,sBAChBiC,EAAEkG,sBACF7N,qBAAqB+N,OAAO,CACxBC,KAAMxD,WAAW9E,KAAK,SACvB7F,IAAIkJ,WAAW,cAAe,eAKjCkF,YAAa,EAlcJ,IAASC,QAAS5F,UAmc3BkC,WAAW9E,KAAK,iBAncEwI,QAqcC1D,WAAW9E,KAAK,gBArcR4C,UAqcyB,WAChD2F,WAAavC,YAAYnB,eAAgBuD,UAAWtD,WAAYC,eArchF5K,IAAIsJ,YAAY,CACZ,CAACC,IAAK,WACN,CAACA,IAAK,OACN,CAACA,IAAK,QACPjD,MAAK,SAASoD,GACT3J,aAAa4J,QAAQD,EAAE,GAAI2E,QAAS3E,EAAE,GAAIA,EAAE,GAAIjB,eAmc5C2F,WAAavC,YAAYnB,eAAgBuD,UAAWtD,WAAYC,cAGhEwD,YACAtG,EAAEkG,oBAMVpO,EAAE,QAAQiO,GAAGnN,sBAAsB4N,WAAWC,yBACnCtN,SAASS,wCAAsC,SAASoG,MAC3DA,EAAEzF,OAAOsF,WAAW6G,OAAQ,MAGZ/L,IAFF5B,aAAakI,MACLoC,QAAQzI,IAAIoF,EAAEzF,OAAOsF,WAAW6G,SAElD3N,aAAakD,SAAS,eAAgB,CAAC+D,EAAEzF,OAAOsF,WAAW6G,aAKvE5O,EAAE,QAAQiO,GAAGnN,sBAAsB4N,WAAWC,yBACnCtN,SAASC,qEAAmE,SAAS4G,GACxFA,EAAEzF,OAAOsF,WAAW6G,QACpB3N,aAAakD,SAAS,UAAW,CAAC+D,EAAEzF,OAAOsF,WAAW6G,YAK1D3N,aAAaiL,mBAAqBlL,iBAAiBmL,SAAS,2BAK1DtE,QAAU7H,EAAEqB,SAASU,aACrB8M,WAAahH,QAAQ5B,KAAK,qBAC1B6I,YAAcjH,QAAQ5B,KAAK,qBACjC7F,IAAIkJ,WAAW,eACdiB,MAAK,SAASwE,uBACPC,UAAYhP,EAAE,qHACsD8O,YAAc,6BACtFE,UAAUlL,KAAK,SAAS2G,KAAKsE,mBAEtBC,UAAUvE,UAEpBF,MAAM0E,MAASzO,gBAAgB8N,OAAO,CACnCW,KAAAA,KACAC,MAAOL,eAEVtE,MAAK,SAAS4E,WACPC,YAAcpP,EAAEmP,MAAME,WAAWvL,KAAK,4BAC1CwL,YAAc,WAGN,GAAKC,SAASH,YAAYI,SAAWJ,YAAYI,OAASD,SAASH,YAAYI,QAAU,IACzF5D,SAAS6D,SAAW5H,QAAQ5B,KAAK,QAAU,gBAAkBsJ,SAASH,YAAYI,gBAG1FL,MAAMO,kBAAkBb,YACxBM,MAAMQ,UAAU1B,GAAGxN,YAAYmP,OAAO,WAElCR,YAAY1H,QAAQmI,SAAS5B,GAAG,WAAW,SAAS/F,GAC5CA,EAAEgG,UAAYxN,SAASoP,OACvBR,oBAIZH,MAAMQ,UAAU1B,GAAGxN,YAAYsP,MAAM,SAAS7H,GAE1CA,EAAEkG,iBACFkB,iBAGJzH,QAAQoG,GAAG,SAAU/F,IACjBA,EAAEkG,iBACFe,MAAMlL,UAGHkL,SAEVvE,MAAMzK,aAAagI,YAgBxB6H,yBAA0B,SAAS3L,eAAgBmD,SAAU0C,MAAOC,WAC5BC,gBAAiBC,WACrD1J,IAAIsP,MAAM,qEACNhG,WAAa5F,eAAeP,KAAKzC,SAASO,kBAAoB,IAAM4F,UACxEwC,kBAAkBC,WAAYC,MAAOC,WAAYC,gBAAiBC,YAGtEhC,cAAAA,cACA6H,eAppBiB,SAASlN,QAASiI,UAAW3E,oBAExBzD,IAAlByD,gBACAA,cAAgBrF,aAAaqH,qBAG3BwC,eAAiB9K,EAAEgD,SAEnBkD,SAAWjG,KAAKkG,KAAK,CAAC,CACxBC,WAAY,2BACZC,KAAM,CAAClD,GAAI8H,UAAWjF,OAHX,UAGmBM,cAAAA,kBAC9B,OAEAvC,QAAUK,kBAAkB0G,uBACzB,IAAItC,SAAQ,CAACC,QAASC,UACzB1I,EAAEwG,KAAKC,MAAMzG,EAAGkG,UACXQ,MAAK0F,cAEFzH,cAAcmG,eAAgB/G,eACxBG,KAAOlE,EAAEqM,UAAUD,aAEnB+D,kBAAoBnQ,EAAEkE,KAAKkM,SACjCtF,eAAe3D,YAAYgJ,mBAG3BnQ,YAAKqB,SAASM,sBAAasJ,sBAAa5J,SAASC,aAAc0F,MAC3D,CAACK,MAAO1D,YACJyB,eAAezB,SAASO,KAAK,UAKvB3B,cACVxB,aAAasP,iBACb,CACItI,WAAY7D,KACZ8B,OA7BL,UA8BKmK,kBAAmBA,kBAAkBrN,IAAI,IAE7CqN,mBAGOG,kBACPzF,0BACIsF,kBAAmBnQ,EAAEqB,SAASM,UAAY,IAAMsJ,WAChD/G,KACA/C,WACA8J,WAGRxC,QAAQvE,SACT8D,MAAKC,KAEU1F,cACV,6BACA,CAAC4F,UAAWF,GAAIjC,OAhDjB,WAiDC8E,gBAEOwF,kBACPnQ,aAAagI,UAAUF,IAE3BS"}
\ No newline at end of file
diff --git a/course/amd/src/actions.js b/course/amd/src/actions.js
index 7a6b77bd13d..648137a5f41 100644
--- a/course/amd/src/actions.js
+++ b/course/amd/src/actions.js
@@ -37,6 +37,7 @@ define(
         'core/log',
         'core_courseformat/courseeditor',
         'core/event_dispatcher',
+        'core/local/inplace_editable/events',
         'core_course/events'
     ],
     function(
@@ -54,6 +55,7 @@ define(
         log,
         editor,
         EventDispatcher,
+        InplaceEditableEvents,
         CourseEvents
     ) {
 
@@ -995,18 +997,21 @@ define(
 
                 // The section and activity names are edited using inplace editable.
                 // The "update" jQuery event must be captured in order to update the course state.
-                $('body').on('updated', `${SELECTOR.SECTIONITEM} [data-inplaceeditable]`, function(e) {
-                    if (e.ajaxreturn && e.ajaxreturn.itemid) {
+                $('body').on(InplaceEditableEvents.eventTypes.elementUpdated,
+                        `${SELECTOR.SECTIONITEM} [data-inplaceeditable]`, function(e) {
+                    if (e.detail.ajaxreturn.itemid) {
                         const state = courseeditor.state;
-                        const section = state.section.get(e.ajaxreturn.itemid);
+                        const section = state.section.get(e.detail.ajaxreturn.itemid);
                         if (section !== undefined) {
-                            courseeditor.dispatch('sectionState', [e.ajaxreturn.itemid]);
+                            courseeditor.dispatch('sectionState', [e.detail.ajaxreturn.itemid]);
                         }
                     }
                 });
-                $('body').on('updated', `${SELECTOR.ACTIVITYLI} [data-itemtype="activityname"][data-inplaceeditable]`, function(e) {
-                    if (e.ajaxreturn && e.ajaxreturn.itemid) {
-                        courseeditor.dispatch('cmState', [e.ajaxreturn.itemid]);
+
+                $('body').on(InplaceEditableEvents.eventTypes.elementUpdated,
+                        `${SELECTOR.ACTIVITYLI} [data-itemtype="activityname"][data-inplaceeditable]`, function(e) {
+                    if (e.detail.ajaxreturn.itemid) {
+                        courseeditor.dispatch('cmState', [e.detail.ajaxreturn.itemid]);
                     }
                 });
 
diff --git a/lib/amd/build/local/inplace_editable/events.min.js b/lib/amd/build/local/inplace_editable/events.min.js
index 9a67cd10904..ecbeb9ea986 100644
--- a/lib/amd/build/local/inplace_editable/events.min.js
+++ b/lib/amd/build/local/inplace_editable/events.min.js
@@ -1,10 +1,11 @@
-define("core/local/inplace_editable/events",["exports","jquery","core/event_dispatcher"],(function(_exports,_jquery,_event_dispatcher){var obj;
+define("core/local/inplace_editable/events",["exports","core/event_dispatcher"],(function(_exports,_event_dispatcher){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.notifyElementUpdated=_exports.notifyElementUpdateFailed=_exports.eventTypes=void 0;
 /**
    * Inplace editable module events
    *
    * @module      core/local/inplace_editable/events
    * @copyright   2021 Paul Holden <paulh@moodle.com>
    * @license     http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
-   */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.notifyElementUpdated=_exports.notifyElementUpdateFailed=_exports.eventTypes=void 0,_jquery=(obj=_jquery)&&obj.__esModule?obj:{default:obj};const eventTypes={elementUpdated:"core/inplace_editable:updated",elementUpdateFailed:"core/inplace_editable:updateFailed"};_exports.eventTypes=eventTypes;_exports.notifyElementUpdated=(element,ajaxreturn,oldvalue)=>(0,_event_dispatcher.dispatchEvent)(eventTypes.elementUpdated,{ajaxreturn:ajaxreturn,oldvalue:oldvalue},element);_exports.notifyElementUpdateFailed=(element,exception,newvalue)=>(0,_event_dispatcher.dispatchEvent)(eventTypes.elementUpdateFailed,{exception:exception,newvalue:newvalue},element,{cancelable:!0});let legacyEventsRegistered=!1;legacyEventsRegistered||(document.addEventListener(eventTypes.elementUpdated,(event=>{const legacyEvent=_jquery.default.Event("updated",event.detail);(0,_jquery.default)(event.target).trigger(legacyEvent)})),document.addEventListener(eventTypes.elementUpdateFailed,(event=>{const legacyEvent=_jquery.default.Event("updatefailed",event.detail);(0,_jquery.default)(event.target).trigger(legacyEvent),legacyEvent.isDefaultPrevented()&&event.preventDefault()})),legacyEventsRegistered=!0)}));
+   */
+const eventTypes={elementUpdated:"core/inplace_editable:updated",elementUpdateFailed:"core/inplace_editable:updateFailed"};_exports.eventTypes=eventTypes;_exports.notifyElementUpdated=(element,ajaxreturn,oldvalue)=>(0,_event_dispatcher.dispatchEvent)(eventTypes.elementUpdated,{ajaxreturn:ajaxreturn,oldvalue:oldvalue},element);_exports.notifyElementUpdateFailed=(element,exception,newvalue)=>(0,_event_dispatcher.dispatchEvent)(eventTypes.elementUpdateFailed,{exception:exception,newvalue:newvalue},element,{cancelable:!0})}));
 
 //# sourceMappingURL=events.min.js.map
\ No newline at end of file
diff --git a/lib/amd/build/local/inplace_editable/events.min.js.map b/lib/amd/build/local/inplace_editable/events.min.js.map
index 3b9c441e1f0..9ffecedc683 100644
--- a/lib/amd/build/local/inplace_editable/events.min.js.map
+++ b/lib/amd/build/local/inplace_editable/events.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"events.min.js","sources":["../../../src/local/inplace_editable/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 <http://www.gnu.org/licenses/>.\n\n/**\n * Inplace editable module events\n *\n * @module      core/local/inplace_editable/events\n * @copyright   2021 Paul Holden <paulh@moodle.com>\n * @license     http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport $ from 'jquery';\nimport {dispatchEvent} from 'core/event_dispatcher';\n\n/**\n * Module events\n *\n * @constant\n * @property {String} elementUpdated See {@link event:core/inplace_editable:updated}\n * @property {String} elementUpdateFailed See {@link event:core/inplace_editable:updateFailed}\n */\nexport const eventTypes = {\n\n    /**\n     * Event triggered when an element has been updated\n     *\n     * @event core/inplace_editable:updated\n     * @type {CustomEvent}\n     * @property {HTMLElement} target The element that was updated\n     * @property {Object} detail\n     * @property {Object} detail.ajaxreturn The data returned from the update AJAX request\n     * @property {String} detail.oldvalue The previous value of the element\n     */\n    elementUpdated: 'core/inplace_editable:updated',\n\n    /**\n     * Event triggered when an element update has failed\n     *\n     * @event core/inplace_editable:updateFailed\n     * @type {CustomEvent}\n     * @property {HTMLElement} target The element that failed to update\n     * @property {Object} detail\n     * @property {Object} detail.exception The raised exception\n     * @property {String} detail.newvalue The intended value of the element\n     */\n    elementUpdateFailed: 'core/inplace_editable:updateFailed',\n};\n\n/**\n * Notify element of successful update\n *\n * @method\n * @param {HTMLElement} element The element that was updated\n * @param {Object} ajaxreturn The data returned from the update AJAX request\n * @param {String} oldvalue The previous value of the element\n * @returns {CustomEvent}\n * @fires event:core/inplace_editable:updated\n */\nexport const notifyElementUpdated = (element, ajaxreturn, oldvalue) => dispatchEvent(\n    eventTypes.elementUpdated,\n    {\n        ajaxreturn,\n        oldvalue,\n    },\n    element\n);\n\n/**\n * Notify element of failed update\n *\n * @method\n * @param {HTMLElement} element The element that failed to update\n * @param {Object} exception The raised exception\n * @param {String} newvalue The intended value of the element\n * @returns {CustomEvent}\n * @fires event:core/inplace_editable:updateFailed\n */\nexport const notifyElementUpdateFailed = (element, exception, newvalue) => dispatchEvent(\n    eventTypes.elementUpdateFailed,\n    {\n        exception,\n        newvalue,\n    },\n    element,\n    {\n        cancelable: true\n    }\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    // Listen for the new native elementUpdated event, and trigger the legacy jQuery event.\n    document.addEventListener(eventTypes.elementUpdated, event => {\n        const legacyEvent = $.Event('updated', event.detail);\n        $(event.target).trigger(legacyEvent);\n    });\n\n    // Listen for the new native elementUpdateFailed event, and trigger the legacy jQuery event.\n    document.addEventListener(eventTypes.elementUpdateFailed, event => {\n        const legacyEvent = $.Event('updatefailed', event.detail);\n        $(event.target).trigger(legacyEvent);\n\n        // If the legacy event is cancelled, so should the native event.\n        if (legacyEvent.isDefaultPrevented()) {\n            event.preventDefault();\n        }\n    });\n\n    legacyEventsRegistered = true;\n}\n"],"names":["eventTypes","elementUpdated","elementUpdateFailed","element","ajaxreturn","oldvalue","exception","newvalue","cancelable","legacyEventsRegistered","document","addEventListener","event","legacyEvent","$","Event","detail","target","trigger","isDefaultPrevented","preventDefault"],"mappings":";;;;;;;uNAiCaA,WAAa,CAYtBC,eAAgB,gCAYhBC,oBAAqB,mGAaW,CAACC,QAASC,WAAYC,YAAa,mCACnEL,WAAWC,eACX,CACIG,WAAAA,WACAC,SAAAA,UAEJF,4CAaqC,CAACA,QAASG,UAAWC,YAAa,mCACvEP,WAAWE,oBACX,CACII,UAAAA,UACAC,SAAAA,UAEJJ,QACA,CACIK,YAAY,QAIhBC,wBAAyB,EACxBA,yBAMDC,SAASC,iBAAiBX,WAAWC,gBAAgBW,cAC3CC,YAAcC,gBAAEC,MAAM,UAAWH,MAAMI,4BAC3CJ,MAAMK,QAAQC,QAAQL,gBAI5BH,SAASC,iBAAiBX,WAAWE,qBAAqBU,cAChDC,YAAcC,gBAAEC,MAAM,eAAgBH,MAAMI,4BAChDJ,MAAMK,QAAQC,QAAQL,aAGpBA,YAAYM,sBACZP,MAAMQ,oBAIdX,wBAAyB"}
\ No newline at end of file
+{"version":3,"file":"events.min.js","sources":["../../../src/local/inplace_editable/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 <http://www.gnu.org/licenses/>.\n\n/**\n * Inplace editable module events\n *\n * @module      core/local/inplace_editable/events\n * @copyright   2021 Paul Holden <paulh@moodle.com>\n * @license     http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {dispatchEvent} from 'core/event_dispatcher';\n\n/**\n * Module events\n *\n * @constant\n * @property {String} elementUpdated See {@link event:core/inplace_editable:updated}\n * @property {String} elementUpdateFailed See {@link event:core/inplace_editable:updateFailed}\n */\nexport const eventTypes = {\n\n    /**\n     * Event triggered when an element has been updated\n     *\n     * @event core/inplace_editable:updated\n     * @type {CustomEvent}\n     * @property {HTMLElement} target The element that was updated\n     * @property {Object} detail\n     * @property {Object} detail.ajaxreturn The data returned from the update AJAX request\n     * @property {String} detail.oldvalue The previous value of the element\n     */\n    elementUpdated: 'core/inplace_editable:updated',\n\n    /**\n     * Event triggered when an element update has failed\n     *\n     * @event core/inplace_editable:updateFailed\n     * @type {CustomEvent}\n     * @property {HTMLElement} target The element that failed to update\n     * @property {Object} detail\n     * @property {Object} detail.exception The raised exception\n     * @property {String} detail.newvalue The intended value of the element\n     */\n    elementUpdateFailed: 'core/inplace_editable:updateFailed',\n};\n\n/**\n * Notify element of successful update\n *\n * @method\n * @param {HTMLElement} element The element that was updated\n * @param {Object} ajaxreturn The data returned from the update AJAX request\n * @param {String} oldvalue The previous value of the element\n * @returns {CustomEvent}\n * @fires event:core/inplace_editable:updated\n */\nexport const notifyElementUpdated = (element, ajaxreturn, oldvalue) => dispatchEvent(\n    eventTypes.elementUpdated,\n    {\n        ajaxreturn,\n        oldvalue,\n    },\n    element\n);\n\n/**\n * Notify element of failed update\n *\n * @method\n * @param {HTMLElement} element The element that failed to update\n * @param {Object} exception The raised exception\n * @param {String} newvalue The intended value of the element\n * @returns {CustomEvent}\n * @fires event:core/inplace_editable:updateFailed\n */\nexport const notifyElementUpdateFailed = (element, exception, newvalue) => dispatchEvent(\n    eventTypes.elementUpdateFailed,\n    {\n        exception,\n        newvalue,\n    },\n    element,\n    {\n        cancelable: true\n    }\n);\n"],"names":["eventTypes","elementUpdated","elementUpdateFailed","element","ajaxreturn","oldvalue","exception","newvalue","cancelable"],"mappings":";;;;;;;;MAgCaA,WAAa,CAYtBC,eAAgB,gCAYhBC,oBAAqB,mGAaW,CAACC,QAASC,WAAYC,YAAa,mCACnEL,WAAWC,eACX,CACIG,WAAAA,WACAC,SAAAA,UAEJF,4CAaqC,CAACA,QAASG,UAAWC,YAAa,mCACvEP,WAAWE,oBACX,CACII,UAAAA,UACAC,SAAAA,UAEJJ,QACA,CACIK,YAAY"}
\ No newline at end of file
diff --git a/lib/amd/build/tag.min.js b/lib/amd/build/tag.min.js
index 49cbc9865a1..379899c5b3a 100644
--- a/lib/amd/build/tag.min.js
+++ b/lib/amd/build/tag.min.js
@@ -1,4 +1,4 @@
-define("core/tag",["exports","jquery","core/ajax","core/notification","core/templates","core/str","core/modal_events","core/pending","core/modal_save_cancel","core/config","core_reportbuilder/local/selectors"],(function(_exports,_jquery,_ajax,Notification,Templates,_str,ModalEvents,_pending,_modal_save_cancel,_config,reportSelectors){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireWildcard(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]}return newObj.default=obj,cache&&cache.set(obj,newObj),newObj}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}
+define("core/tag",["exports","jquery","core/ajax","core/notification","core/templates","core/str","core/modal_events","core/pending","core/modal_save_cancel","core/config","core/local/inplace_editable/events","core_reportbuilder/local/selectors"],(function(_exports,_jquery,_ajax,Notification,Templates,_str,ModalEvents,_pending,_modal_save_cancel,_config,_events,reportSelectors){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireWildcard(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]}return newObj.default=obj,cache&&cache.set(obj,newObj),newObj}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}
 /**
    * AJAX helper for the tag management page.
    *
@@ -6,6 +6,6 @@ define("core/tag",["exports","jquery","core/ajax","core/notification","core/temp
    * @copyright  2015 Marina Glancy
    * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
    * @since      3.0
-   */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.initTagindexPage=_exports.initManagePage=_exports.initManageCollectionsPage=void 0,_jquery=_interopRequireDefault(_jquery),Notification=_interopRequireWildcard(Notification),Templates=_interopRequireWildcard(Templates),ModalEvents=_interopRequireWildcard(ModalEvents),_pending=_interopRequireDefault(_pending),_modal_save_cancel=_interopRequireDefault(_modal_save_cancel),_config=_interopRequireDefault(_config),reportSelectors=_interopRequireWildcard(reportSelectors);const getCheckedTags=root=>root.querySelectorAll('[data-togglegroup="report-select-all"][data-toggle="slave"]:checked');_exports.initTagindexPage=async()=>{document.addEventListener("click",(async e=>{const targetArea=e.target.closest('a[data-quickload="1"]');if(!targetArea)return;const tagArea=targetArea.closest(".tagarea[data-ta]");if(!tagArea)return;e.preventDefault();const pendingPromise=new _pending.default("core/tag:initTagindexPage"),query=targetArea.search.replace(/^\?/,""),params=Object.fromEntries(new URLSearchParams(query).entries());try{const data=await(tagindex=params,(0,_ajax.call)([{methodname:"core_tag_get_tagindex",args:{tagindex:tagindex}}])[0]),{html:html,js:js}=await Templates.renderForPromise("core_tag/index",data);Templates.replaceNode(tagArea,html,js)}catch(error){Notification.exception(error)}var tagindex;pendingPromise.resolve()}))};_exports.initManagePage=()=>{(0,_jquery.default)("body").on("updated","[data-inplaceeditable][data-itemtype=tagflag]",(function(e){(0,_jquery.default)(e.target).closest("tr").toggleClass("table-warning","1"===e.ajaxreturn.value)})),document.addEventListener("click",(async e=>{const tagManagementCombine=e.target.closest("#tag-management-combine");tagManagementCombine&&(e.preventDefault(),(async tagManagementCombine=>{const pendingPromise=new _pending.default("core/tag:tag-management-combine"),form=tagManagementCombine.closest("form"),reportElement=document.querySelector(reportSelectors.regions.report),checkedTags=getCheckedTags(reportElement);if(checkedTags.length<=1)return void Notification.alert((0,_str.getString)("combineselected","tag"),(0,_str.getString)("selectmultipletags","tag"),(0,_str.getString)("ok"));const tags=Array.from(checkedTags.values()).map((tag=>{const namedElement=document.querySelector('.inplaceeditable[data-itemtype=tagname][data-itemid="'.concat(tag.value,'"]'));return{id:tag.value,name:namedElement.dataset.value}})),modal=await _modal_save_cancel.default.create({title:(0,_str.getString)("combineselected","tag"),buttons:{save:(0,_str.getString)("continue","core")},body:Templates.render("core_tag/combine_tags",{tags:tags}),show:!0,removeOnClose:!0});modal.getRoot().on(ModalEvents.save,(e=>{e.preventDefault();const tempElement=document.createElement("input");tempElement.hidden=!0,tempElement.name=tagManagementCombine.name,form.append(tempElement);const tagsElement=document.createElement("input");tagsElement.hidden=!0,tagsElement.name="tagschecked",tagsElement.value=[...checkedTags].map((check=>check.value)).join(","),form.append(tagsElement);var maintag=(0,_jquery.default)("input[name=maintag]:checked","#combinetags_form").val();(0,_jquery.default)("<input type='hidden'/>").attr("name","maintag").attr("value",maintag).appendTo(form),form.submit()})),await modal.getBodyPromise();const firstOption=document.querySelector("#combinetags_form input[type=radio]");firstOption.focus(),firstOption.checked=!0,pendingPromise.resolve()})(tagManagementCombine)),e.target.closest('[data-action="addstandardtag"]')&&(e.preventDefault(),(async()=>{var pendingPromise=new _pending.default("core/tag:addstandardtag");const modal=await _modal_save_cancel.default.create({title:(0,_str.getString)("addotags","tag"),body:Templates.render("core_tag/add_tags",{actionurl:window.location.href,sesskey:M.cfg.sesskey}),buttons:{save:(0,_str.getString)("continue","core")},removeOnClose:!0,show:!0});modal.getRoot().on(ModalEvents.save,(e=>{var tagsInput=(0,_jquery.default)(e.currentTarget).find("#id_tagslist"),name=tagsInput.val().trim();tagsInput.val(name);var tagsForm=(0,_jquery.default)("#addtags_form");return tagsForm.on("submit",(function(e){var form=(0,_jquery.default)("#addtags_form");!1===form[0].checkValidity()&&(e.preventDefault(),e.stopPropagation()),form.addClass("was-validated"),(0,_jquery.default)('[data-region="tagslistinput"]').addClass("error");var errorMessage=(0,_jquery.default)("#id_tagslist_error_message");errorMessage.removeAttr("hidden"),errorMessage.addClass("help-block")})),tagsForm.submit(),!1})),await modal.getBodyPromise(),pendingPromise.resolve()})());const bulkActionDeleteButton=e.target.closest("#tag-management-delete");bulkActionDeleteButton&&(e.preventDefault(),(async bulkActionDeleteButton=>{const form=bulkActionDeleteButton.closest("form"),reportElement=document.querySelector(reportSelectors.regions.report),checkedTags=getCheckedTags(reportElement);if(checkedTags.length)try{await Notification.saveCancelPromise((0,_str.getString)("delete"),(0,_str.getString)("confirmdeletetags","tag"),(0,_str.getString)("yes"),(0,_str.getString)("no"));const tempElement=document.createElement("input");tempElement.hidden=!0,tempElement.name=bulkActionDeleteButton.name,form.append(tempElement);const tagsElement=document.createElement("input");tagsElement.hidden=!0,tagsElement.name="tagschecked",tagsElement.value=[...checkedTags].map((check=>check.value)).join(","),form.append(tagsElement),form.submit()}catch{return}})(bulkActionDeleteButton));const rowDeleteButton=e.target.closest(".tagdelete");rowDeleteButton&&(e.preventDefault(),(async button=>{try{await Notification.saveCancelPromise((0,_str.getString)("delete"),(0,_str.getString)("confirmdeletetag","tag"),(0,_str.getString)("yes"),(0,_str.getString)("no")),window.location.href=button.href}catch{return}})(rowDeleteButton))})),(0,_jquery.default)("body").on("updatefailed","[data-inplaceeditable][data-itemtype=tagname]",(async e=>{var exception=e.exception,newvalue=e.newvalue,tagid=(0,_jquery.default)(e.target).attr("data-itemid");if("namesalreadybeeingused"===exception.errorcode){e.preventDefault();try{await Notification.saveCancelPromise((0,_str.getString)("confirm"),(0,_str.getString)("nameuseddocombine","tag"),(0,_str.getString)("yes"),(0,_str.getString)("cancel"));const redirectTarget=new URL(window.location);redirectTarget.searchParams.set("newname",newvalue),redirectTarget.searchParams.set("tagid",tagid),redirectTarget.searchParams.set("action","renamecombine"),redirectTarget.searchParams.set("sesskey",_config.default.sesskey),window.location.href=redirectTarget}catch{return}}}))};_exports.initManageCollectionsPage=()=>{(0,_jquery.default)("body").on("updated","[data-inplaceeditable]",(function(e){var areaid,collid,pendingPromise=new _pending.default("core/tag:initManageCollectionsPage-updated"),ajaxreturn=e.ajaxreturn;"core_tag"===ajaxreturn.component&&"tagareaenable"===ajaxreturn.itemtype&&(areaid=(0,_jquery.default)(this).attr("data-itemid"),(0,_jquery.default)(".tag-collections-table ul[data-collectionid] li[data-areaid="+areaid+"]").hide(),"1"===ajaxreturn.value?((0,_jquery.default)(this).closest("tr").removeClass("dimmed_text"),collid=(0,_jquery.default)(this).closest("tr").find('[data-itemtype="tagareacollection"]').attr("data-value"),(0,_jquery.default)(".tag-collections-table ul[data-collectionid="+collid+"] li[data-areaid="+areaid+"]").show()):(0,_jquery.default)(this).closest("tr").addClass("dimmed_text")),"core_tag"===ajaxreturn.component&&"tagareacollection"===ajaxreturn.itemtype&&(areaid=(0,_jquery.default)(this).attr("data-itemid"),(0,_jquery.default)(".tag-collections-table ul[data-collectionid] li[data-areaid="+areaid+"]").hide(),collid=(0,_jquery.default)(this).attr("data-value"),"1"===(0,_jquery.default)(this).closest("tr").find('[data-itemtype="tagareaenable"]').attr("data-value")&&(0,_jquery.default)(".tag-collections-table ul[data-collectionid="+collid+"] li[data-areaid="+areaid+"]").show()),pendingPromise.resolve()})),document.addEventListener("click",(async e=>{const addTagCollectionNode=e.target.closest(".addtagcoll > a");if(addTagCollectionNode)return e.preventDefault(),void(async link=>{const pendingPromise=new _pending.default("core/tag:initManageCollectionsPage-addtagcoll"),href=link.dataset.url;(await _modal_save_cancel.default.create({title:(0,_str.getString)("addtagcoll","tag"),buttons:{save:(0,_str.getString)("create","core")},body:Templates.render("core_tag/add_tag_collection",{actionurl:href,sesskey:M.cfg.sesskey}),removeOnClose:!0,show:!0})).getRoot().on(ModalEvents.save,(e=>{const collectionInput=(0,_jquery.default)(e.currentTarget).find("#addtagcoll_name"),name=collectionInput.val().trim();collectionInput.val(name);const form=(0,_jquery.default)("#addtagcoll_form");return form.on("submit",(function(e){!1===form[0].checkValidity()&&(e.preventDefault(),e.stopPropagation()),form.addClass("was-validated"),(0,_jquery.default)('[data-region="addtagcoll_nameinput"]').addClass("error");const errorMessage=(0,_jquery.default)("#id_addtagcoll_name_error_message");errorMessage.removeAttr("hidden"),errorMessage.addClass("help-block")})),form.submit(),!1})),pendingPromise.resolve()})(addTagCollectionNode);const deleteCollectionButton=e.target.closest(".tag-collections-table .action_delete");deleteCollectionButton&&(e.preventDefault(),(async button=>{try{await Notification.saveCancelPromise((0,_str.getString)("delete"),(0,_str.getString)("suredeletecoll","tag",button.dataset.collname),(0,_str.getString)("yes"),(0,_str.getString)("no"));const redirectTarget=new URL(button.dataset.url);redirectTarget.searchParams.set("sesskey",_config.default.sesskey),window.location.href=redirectTarget}catch{return}})(deleteCollectionButton))}))}}));
+   */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.initTagindexPage=_exports.initManagePage=_exports.initManageCollectionsPage=void 0,_jquery=_interopRequireDefault(_jquery),Notification=_interopRequireWildcard(Notification),Templates=_interopRequireWildcard(Templates),ModalEvents=_interopRequireWildcard(ModalEvents),_pending=_interopRequireDefault(_pending),_modal_save_cancel=_interopRequireDefault(_modal_save_cancel),_config=_interopRequireDefault(_config),reportSelectors=_interopRequireWildcard(reportSelectors);const getCheckedTags=root=>root.querySelectorAll('[data-togglegroup="report-select-all"][data-toggle="slave"]:checked');_exports.initTagindexPage=async()=>{document.addEventListener("click",(async e=>{const targetArea=e.target.closest('a[data-quickload="1"]');if(!targetArea)return;const tagArea=targetArea.closest(".tagarea[data-ta]");if(!tagArea)return;e.preventDefault();const pendingPromise=new _pending.default("core/tag:initTagindexPage"),query=targetArea.search.replace(/^\?/,""),params=Object.fromEntries(new URLSearchParams(query).entries());try{const data=await(tagindex=params,(0,_ajax.call)([{methodname:"core_tag_get_tagindex",args:{tagindex:tagindex}}])[0]),{html:html,js:js}=await Templates.renderForPromise("core_tag/index",data);Templates.replaceNode(tagArea,html,js)}catch(error){Notification.exception(error)}var tagindex;pendingPromise.resolve()}))};_exports.initManagePage=()=>{(0,_jquery.default)("body").on(_events.eventTypes.elementUpdated,"[data-inplaceeditable][data-itemtype=tagflag]",(function(e){(0,_jquery.default)(e.target).closest("tr").toggleClass("table-warning","1"===e.detail.ajaxreturn.value)})),document.addEventListener("click",(async e=>{const tagManagementCombine=e.target.closest("#tag-management-combine");tagManagementCombine&&(e.preventDefault(),(async tagManagementCombine=>{const pendingPromise=new _pending.default("core/tag:tag-management-combine"),form=tagManagementCombine.closest("form"),reportElement=document.querySelector(reportSelectors.regions.report),checkedTags=getCheckedTags(reportElement);if(checkedTags.length<=1)return void Notification.alert((0,_str.getString)("combineselected","tag"),(0,_str.getString)("selectmultipletags","tag"),(0,_str.getString)("ok"));const tags=Array.from(checkedTags.values()).map((tag=>{const namedElement=document.querySelector('.inplaceeditable[data-itemtype=tagname][data-itemid="'.concat(tag.value,'"]'));return{id:tag.value,name:namedElement.dataset.value}})),modal=await _modal_save_cancel.default.create({title:(0,_str.getString)("combineselected","tag"),buttons:{save:(0,_str.getString)("continue","core")},body:Templates.render("core_tag/combine_tags",{tags:tags}),show:!0,removeOnClose:!0});modal.getRoot().on(ModalEvents.save,(e=>{e.preventDefault();const tempElement=document.createElement("input");tempElement.hidden=!0,tempElement.name=tagManagementCombine.name,form.append(tempElement);const tagsElement=document.createElement("input");tagsElement.hidden=!0,tagsElement.name="tagschecked",tagsElement.value=[...checkedTags].map((check=>check.value)).join(","),form.append(tagsElement);var maintag=(0,_jquery.default)("input[name=maintag]:checked","#combinetags_form").val();(0,_jquery.default)("<input type='hidden'/>").attr("name","maintag").attr("value",maintag).appendTo(form),form.submit()})),await modal.getBodyPromise();const firstOption=document.querySelector("#combinetags_form input[type=radio]");firstOption.focus(),firstOption.checked=!0,pendingPromise.resolve()})(tagManagementCombine)),e.target.closest('[data-action="addstandardtag"]')&&(e.preventDefault(),(async()=>{var pendingPromise=new _pending.default("core/tag:addstandardtag");const modal=await _modal_save_cancel.default.create({title:(0,_str.getString)("addotags","tag"),body:Templates.render("core_tag/add_tags",{actionurl:window.location.href,sesskey:M.cfg.sesskey}),buttons:{save:(0,_str.getString)("continue","core")},removeOnClose:!0,show:!0});modal.getRoot().on(ModalEvents.save,(e=>{var tagsInput=(0,_jquery.default)(e.currentTarget).find("#id_tagslist"),name=tagsInput.val().trim();tagsInput.val(name);var tagsForm=(0,_jquery.default)("#addtags_form");return tagsForm.on("submit",(function(e){var form=(0,_jquery.default)("#addtags_form");!1===form[0].checkValidity()&&(e.preventDefault(),e.stopPropagation()),form.addClass("was-validated"),(0,_jquery.default)('[data-region="tagslistinput"]').addClass("error");var errorMessage=(0,_jquery.default)("#id_tagslist_error_message");errorMessage.removeAttr("hidden"),errorMessage.addClass("help-block")})),tagsForm.submit(),!1})),await modal.getBodyPromise(),pendingPromise.resolve()})());const bulkActionDeleteButton=e.target.closest("#tag-management-delete");bulkActionDeleteButton&&(e.preventDefault(),(async bulkActionDeleteButton=>{const form=bulkActionDeleteButton.closest("form"),reportElement=document.querySelector(reportSelectors.regions.report),checkedTags=getCheckedTags(reportElement);if(checkedTags.length)try{await Notification.saveCancelPromise((0,_str.getString)("delete"),(0,_str.getString)("confirmdeletetags","tag"),(0,_str.getString)("yes"),(0,_str.getString)("no"));const tempElement=document.createElement("input");tempElement.hidden=!0,tempElement.name=bulkActionDeleteButton.name,form.append(tempElement);const tagsElement=document.createElement("input");tagsElement.hidden=!0,tagsElement.name="tagschecked",tagsElement.value=[...checkedTags].map((check=>check.value)).join(","),form.append(tagsElement),form.submit()}catch{return}})(bulkActionDeleteButton));const rowDeleteButton=e.target.closest(".tagdelete");rowDeleteButton&&(e.preventDefault(),(async button=>{try{await Notification.saveCancelPromise((0,_str.getString)("delete"),(0,_str.getString)("confirmdeletetag","tag"),(0,_str.getString)("yes"),(0,_str.getString)("no")),window.location.href=button.href}catch{return}})(rowDeleteButton))})),(0,_jquery.default)("body").on(_events.eventTypes.elementUpdateFailed,"[data-inplaceeditable][data-itemtype=tagname]",(async e=>{var exception=e.detail.exception,newvalue=e.detail.newvalue,tagid=(0,_jquery.default)(e.target).attr("data-itemid");if("namesalreadybeeingused"===exception.errorcode){e.preventDefault();try{await Notification.saveCancelPromise((0,_str.getString)("confirm"),(0,_str.getString)("nameuseddocombine","tag"),(0,_str.getString)("yes"),(0,_str.getString)("cancel"));const redirectTarget=new URL(window.location);redirectTarget.searchParams.set("newname",newvalue),redirectTarget.searchParams.set("tagid",tagid),redirectTarget.searchParams.set("action","renamecombine"),redirectTarget.searchParams.set("sesskey",_config.default.sesskey),window.location.href=redirectTarget}catch{return}}}))};_exports.initManageCollectionsPage=()=>{(0,_jquery.default)("body").on(_events.eventTypes.elementUpdated,"[data-inplaceeditable]",(function(e){var areaid,collid,pendingPromise=new _pending.default("core/tag:initManageCollectionsPage-updated"),ajaxreturn=e.detail.ajaxreturn;"core_tag"===ajaxreturn.component&&"tagareaenable"===ajaxreturn.itemtype&&(areaid=(0,_jquery.default)(this).attr("data-itemid"),(0,_jquery.default)(".tag-collections-table ul[data-collectionid] li[data-areaid="+areaid+"]").hide(),"1"===ajaxreturn.value?((0,_jquery.default)(this).closest("tr").removeClass("dimmed_text"),collid=(0,_jquery.default)(this).closest("tr").find('[data-itemtype="tagareacollection"]').attr("data-value"),(0,_jquery.default)(".tag-collections-table ul[data-collectionid="+collid+"] li[data-areaid="+areaid+"]").show()):(0,_jquery.default)(this).closest("tr").addClass("dimmed_text")),"core_tag"===ajaxreturn.component&&"tagareacollection"===ajaxreturn.itemtype&&(areaid=(0,_jquery.default)(this).attr("data-itemid"),(0,_jquery.default)(".tag-collections-table ul[data-collectionid] li[data-areaid="+areaid+"]").hide(),collid=(0,_jquery.default)(this).attr("data-value"),"1"===(0,_jquery.default)(this).closest("tr").find('[data-itemtype="tagareaenable"]').attr("data-value")&&(0,_jquery.default)(".tag-collections-table ul[data-collectionid="+collid+"] li[data-areaid="+areaid+"]").show()),pendingPromise.resolve()})),document.addEventListener("click",(async e=>{const addTagCollectionNode=e.target.closest(".addtagcoll > a");if(addTagCollectionNode)return e.preventDefault(),void(async link=>{const pendingPromise=new _pending.default("core/tag:initManageCollectionsPage-addtagcoll"),href=link.dataset.url;(await _modal_save_cancel.default.create({title:(0,_str.getString)("addtagcoll","tag"),buttons:{save:(0,_str.getString)("create","core")},body:Templates.render("core_tag/add_tag_collection",{actionurl:href,sesskey:M.cfg.sesskey}),removeOnClose:!0,show:!0})).getRoot().on(ModalEvents.save,(e=>{const collectionInput=(0,_jquery.default)(e.currentTarget).find("#addtagcoll_name"),name=collectionInput.val().trim();collectionInput.val(name);const form=(0,_jquery.default)("#addtagcoll_form");return form.on("submit",(function(e){!1===form[0].checkValidity()&&(e.preventDefault(),e.stopPropagation()),form.addClass("was-validated"),(0,_jquery.default)('[data-region="addtagcoll_nameinput"]').addClass("error");const errorMessage=(0,_jquery.default)("#id_addtagcoll_name_error_message");errorMessage.removeAttr("hidden"),errorMessage.addClass("help-block")})),form.submit(),!1})),pendingPromise.resolve()})(addTagCollectionNode);const deleteCollectionButton=e.target.closest(".tag-collections-table .action_delete");deleteCollectionButton&&(e.preventDefault(),(async button=>{try{await Notification.saveCancelPromise((0,_str.getString)("delete"),(0,_str.getString)("suredeletecoll","tag",button.dataset.collname),(0,_str.getString)("yes"),(0,_str.getString)("no"));const redirectTarget=new URL(button.dataset.url);redirectTarget.searchParams.set("sesskey",_config.default.sesskey),window.location.href=redirectTarget}catch{return}})(deleteCollectionButton))}))}}));
 
 //# sourceMappingURL=tag.min.js.map
\ No newline at end of file
diff --git a/lib/amd/build/tag.min.js.map b/lib/amd/build/tag.min.js.map
index 24719af21f2..ea0fba18f2e 100644
--- a/lib/amd/build/tag.min.js.map
+++ b/lib/amd/build/tag.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"tag.min.js","sources":["../src/tag.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 <http://www.gnu.org/licenses/>.\n\n/**\n * AJAX helper for the tag management page.\n *\n * @module     core/tag\n * @copyright  2015 Marina Glancy\n * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @since      3.0\n */\n\nimport $ from 'jquery';\nimport {call as fetchMany} from 'core/ajax';\nimport * as Notification from 'core/notification';\nimport * as Templates from 'core/templates';\nimport {getString} from 'core/str';\nimport * as ModalEvents from 'core/modal_events';\nimport Pending from 'core/pending';\nimport SaveCancelModal from 'core/modal_save_cancel';\nimport Config from 'core/config';\nimport * as reportSelectors from 'core_reportbuilder/local/selectors';\n\nconst getTagIndex = (tagindex) => fetchMany([{\n    methodname: 'core_tag_get_tagindex',\n    args: {tagindex}\n}])[0];\n\nconst getCheckedTags = (root) => root.querySelectorAll('[data-togglegroup=\"report-select-all\"][data-toggle=\"slave\"]:checked');\n\nconst handleCombineRequest = async(tagManagementCombine) => {\n    const pendingPromise = new Pending('core/tag:tag-management-combine');\n    const form = tagManagementCombine.closest('form');\n\n    const reportElement = document.querySelector(reportSelectors.regions.report);\n    const checkedTags = getCheckedTags(reportElement);\n\n    if (checkedTags.length <= 1) {\n        // We need at least 2 tags to combine them.\n        Notification.alert(\n            getString('combineselected', 'tag'),\n            getString('selectmultipletags', 'tag'),\n            getString('ok'),\n        );\n\n        return;\n    }\n\n    const tags = Array.from(checkedTags.values()).map((tag) => {\n        const namedElement = document.querySelector(`.inplaceeditable[data-itemtype=tagname][data-itemid=\"${tag.value}\"]`);\n        return {\n            id: tag.value,\n            name: namedElement.dataset.value,\n        };\n    });\n\n    const modal = await SaveCancelModal.create({\n        title: getString('combineselected', 'tag'),\n        buttons: {\n            save: getString('continue', 'core'),\n        },\n        body: Templates.render('core_tag/combine_tags', {tags}),\n        show: true,\n        removeOnClose: true,\n    });\n\n    // Handle save event.\n    modal.getRoot().on(ModalEvents.save, (e) => {\n        e.preventDefault();\n\n        // Append this temp element in the form in the tags list, not the form in the modal. Confusing, right?!?\n        const tempElement = document.createElement('input');\n        tempElement.hidden = true;\n        tempElement.name = tagManagementCombine.name;\n        form.append(tempElement);\n\n        // Append selected tags element.\n        const tagsElement = document.createElement('input');\n        tagsElement.hidden = true;\n        tagsElement.name = 'tagschecked';\n        tagsElement.value = [...checkedTags].map(check => check.value).join(',');\n        form.append(tagsElement);\n\n        // Get the selected tag from the modal.\n        var maintag = $('input[name=maintag]:checked', '#combinetags_form').val();\n        // Append this in the tags list form.\n        $(\"<input type='hidden'/>\").attr('name', 'maintag').attr('value', maintag).appendTo(form);\n        // Submit the tags list form.\n        form.submit();\n    });\n\n    await modal.getBodyPromise();\n    // Tick the first option.\n    const firstOption = document.querySelector('#combinetags_form input[type=radio]');\n    firstOption.focus();\n    firstOption.checked = true;\n\n    pendingPromise.resolve();\n\n    return;\n};\n\nconst addStandardTags = async() => {\n    var pendingPromise = new Pending('core/tag:addstandardtag');\n\n    const modal = await SaveCancelModal.create({\n        title: getString('addotags', 'tag'),\n        body: Templates.render('core_tag/add_tags', {\n            actionurl: window.location.href,\n            sesskey: M.cfg.sesskey,\n        }),\n        buttons: {\n            save: getString('continue', 'core'),\n        },\n        removeOnClose: true,\n        show: true,\n    });\n\n    // Handle save event.\n    modal.getRoot().on(ModalEvents.save, (e) => {\n        var tagsInput = $(e.currentTarget).find('#id_tagslist');\n        var name = tagsInput.val().trim();\n\n        // Set the text field's value to the trimmed value.\n        tagsInput.val(name);\n\n        // Add submit event listener to the form.\n        var tagsForm = $('#addtags_form');\n        tagsForm.on('submit', function(e) {\n            // Validate the form.\n            var form = $('#addtags_form');\n            if (form[0].checkValidity() === false) {\n                e.preventDefault();\n                e.stopPropagation();\n            }\n            form.addClass('was-validated');\n\n            // BS2 compatibility.\n            $('[data-region=\"tagslistinput\"]').addClass('error');\n            var errorMessage = $('#id_tagslist_error_message');\n            errorMessage.removeAttr('hidden');\n            errorMessage.addClass('help-block');\n        });\n\n        // Try to submit the form.\n        tagsForm.submit();\n\n        return false;\n    });\n\n    await modal.getBodyPromise();\n    pendingPromise.resolve();\n};\n\nconst deleteSelectedTags = async(bulkActionDeleteButton) => {\n    const form = bulkActionDeleteButton.closest('form');\n\n    const reportElement = document.querySelector(reportSelectors.regions.report);\n    const checkedTags = getCheckedTags(reportElement);\n\n    if (!checkedTags.length) {\n        return;\n    }\n\n    try {\n        await Notification.saveCancelPromise(\n            getString('delete'),\n            getString('confirmdeletetags', 'tag'),\n            getString('yes'),\n            getString('no'),\n        );\n\n        // Append this temp element in the form in the tags list, not the form in the modal. Confusing, right?!?\n        const tempElement = document.createElement('input');\n        tempElement.hidden = true;\n        tempElement.name = bulkActionDeleteButton.name;\n        form.append(tempElement);\n\n        // Append selected tags element.\n        const tagsElement = document.createElement('input');\n        tagsElement.hidden = true;\n        tagsElement.name = 'tagschecked';\n        tagsElement.value = [...checkedTags].map(check => check.value).join(',');\n        form.append(tagsElement);\n\n        form.submit();\n    } catch {\n        return;\n    }\n};\n\nconst deleteSelectedTag = async(button) => {\n    try {\n        await Notification.saveCancelPromise(\n            getString('delete'),\n            getString('confirmdeletetag', 'tag'),\n            getString('yes'),\n            getString('no'),\n        );\n\n        window.location.href = button.href;\n    } catch {\n        return;\n    }\n};\n\nconst deleteSelectedCollection = async(button) => {\n    try {\n        await Notification.saveCancelPromise(\n            getString('delete'),\n            getString('suredeletecoll', 'tag', button.dataset.collname),\n            getString('yes'),\n            getString('no'),\n        );\n\n        const redirectTarget = new URL(button.dataset.url);\n        redirectTarget.searchParams.set('sesskey', Config.sesskey);\n        window.location.href = redirectTarget;\n    } catch {\n        return;\n    }\n};\n\nconst addTagCollection = async(link) => {\n    const pendingPromise = new Pending('core/tag:initManageCollectionsPage-addtagcoll');\n    const href = link.dataset.url;\n\n    const modal = await SaveCancelModal.create({\n        title: getString('addtagcoll', 'tag'),\n        buttons: {\n            save: getString('create', 'core'),\n        },\n        body: Templates.render('core_tag/add_tag_collection', {\n            actionurl: href,\n            sesskey: M.cfg.sesskey,\n        }),\n        removeOnClose: true,\n        show: true,\n    });\n\n    // Handle save event.\n    modal.getRoot().on(ModalEvents.save, (e) => {\n        const collectionInput = $(e.currentTarget).find('#addtagcoll_name');\n        const name = collectionInput.val().trim();\n        // Set the text field's value to the trimmed value.\n        collectionInput.val(name);\n\n        // Add submit event listener to the form.\n        const form = $('#addtagcoll_form');\n        form.on('submit', function(e) {\n            // Validate the form.\n            if (form[0].checkValidity() === false) {\n                e.preventDefault();\n                e.stopPropagation();\n            }\n            form.addClass('was-validated');\n\n            // BS2 compatibility.\n            $('[data-region=\"addtagcoll_nameinput\"]').addClass('error');\n            const errorMessage = $('#id_addtagcoll_name_error_message');\n            errorMessage.removeAttr('hidden');\n            errorMessage.addClass('help-block');\n        });\n\n        // Try to submit the form.\n        form.submit();\n\n        return false;\n    });\n\n    pendingPromise.resolve();\n};\n\n/**\n * Initialises tag index page.\n *\n * @method initTagindexPage\n */\nexport const initTagindexPage = async() => {\n    document.addEventListener('click', async(e) => {\n        const targetArea = e.target.closest('a[data-quickload=\"1\"]');\n        if (!targetArea) {\n            return;\n        }\n        const tagArea = targetArea.closest('.tagarea[data-ta]');\n        if (!tagArea) {\n            return;\n        }\n\n        e.preventDefault();\n        const pendingPromise = new Pending('core/tag:initTagindexPage');\n\n        const query = targetArea.search.replace(/^\\?/, '');\n        const params = Object.fromEntries((new URLSearchParams(query)).entries());\n\n        try {\n            const data = await getTagIndex(params);\n            const {html, js} = await Templates.renderForPromise('core_tag/index', data);\n            Templates.replaceNode(tagArea, html, js);\n        } catch (error) {\n            Notification.exception(error);\n        }\n        pendingPromise.resolve();\n    });\n};\n\n/**\n * Initialises tag management page.\n *\n * @method initManagePage\n */\nexport const initManagePage = () => {\n    // Toggle row class when updating flag.\n    $('body').on('updated', '[data-inplaceeditable][data-itemtype=tagflag]', function(e) {\n        var row = $(e.target).closest('tr');\n        row.toggleClass('table-warning', e.ajaxreturn.value === '1');\n    });\n\n    // Confirmation for bulk tag combine button.\n    document.addEventListener('click', async(e) => {\n        const tagManagementCombine = e.target.closest('#tag-management-combine');\n        if (tagManagementCombine) {\n            e.preventDefault();\n            handleCombineRequest(tagManagementCombine);\n        }\n\n        if (e.target.closest('[data-action=\"addstandardtag\"]')) {\n            e.preventDefault();\n            addStandardTags();\n        }\n\n        const bulkActionDeleteButton = e.target.closest('#tag-management-delete');\n        if (bulkActionDeleteButton) {\n            e.preventDefault();\n            deleteSelectedTags(bulkActionDeleteButton);\n        }\n\n        const rowDeleteButton = e.target.closest('.tagdelete');\n        if (rowDeleteButton) {\n            e.preventDefault();\n            deleteSelectedTag(rowDeleteButton);\n        }\n    });\n\n    // When user changes tag name to some name that already exists suggest to combine the tags.\n    $('body').on('updatefailed', '[data-inplaceeditable][data-itemtype=tagname]', async(e) => {\n        var exception = e.exception; // The exception object returned by the callback.\n        var newvalue = e.newvalue; // The value that user tried to udpated the element to.\n        var tagid = $(e.target).attr('data-itemid');\n        if (exception.errorcode !== 'namesalreadybeeingused') {\n            return;\n        }\n        e.preventDefault(); // This will prevent default error dialogue.\n\n        try {\n            await Notification.saveCancelPromise(\n                getString('confirm'),\n                getString('nameuseddocombine', 'tag'),\n                getString('yes'),\n                getString('cancel'),\n            );\n\n            // The Promise will resolve on 'Yes' button, and reject on 'Cancel' button.\n            const redirectTarget = new URL(window.location);\n            redirectTarget.searchParams.set('newname', newvalue);\n            redirectTarget.searchParams.set('tagid', tagid);\n            redirectTarget.searchParams.set('action', 'renamecombine');\n            redirectTarget.searchParams.set('sesskey', Config.sesskey);\n\n            window.location.href = redirectTarget;\n        } catch {\n            return;\n        }\n    });\n};\n\n/**\n * Initialises tag collection management page.\n *\n * @method initManageCollectionsPage\n */\nexport const initManageCollectionsPage = () => {\n    $('body').on('updated', '[data-inplaceeditable]', function(e) {\n        var pendingPromise = new Pending('core/tag:initManageCollectionsPage-updated');\n\n        var ajaxreturn = e.ajaxreturn,\n            areaid, collid, isenabled;\n        if (ajaxreturn.component === 'core_tag' && ajaxreturn.itemtype === 'tagareaenable') {\n            areaid = $(this).attr('data-itemid');\n            $(\".tag-collections-table ul[data-collectionid] li[data-areaid=\" + areaid + \"]\").hide();\n            isenabled = ajaxreturn.value;\n            if (isenabled === '1') {\n                $(this).closest('tr').removeClass('dimmed_text');\n                collid = $(this).closest('tr').find('[data-itemtype=\"tagareacollection\"]').attr(\"data-value\");\n                $(\".tag-collections-table ul[data-collectionid=\" + collid + \"] li[data-areaid=\" + areaid + \"]\").show();\n            } else {\n                $(this).closest('tr').addClass('dimmed_text');\n            }\n        }\n        if (ajaxreturn.component === 'core_tag' && ajaxreturn.itemtype === 'tagareacollection') {\n            areaid = $(this).attr('data-itemid');\n            $(\".tag-collections-table ul[data-collectionid] li[data-areaid=\" + areaid + \"]\").hide();\n            collid = $(this).attr('data-value');\n            isenabled = $(this).closest('tr').find('[data-itemtype=\"tagareaenable\"]').attr(\"data-value\");\n            if (isenabled === \"1\") {\n                $(\".tag-collections-table ul[data-collectionid=\" + collid + \"] li[data-areaid=\" + areaid + \"]\").show();\n            }\n        }\n\n        pendingPromise.resolve();\n    });\n\n    document.addEventListener('click', async(e) => {\n        const addTagCollectionNode = e.target.closest('.addtagcoll > a');\n        if (addTagCollectionNode) {\n            e.preventDefault();\n            addTagCollection(addTagCollectionNode);\n            return;\n        }\n\n        const deleteCollectionButton = e.target.closest('.tag-collections-table .action_delete');\n        if (deleteCollectionButton) {\n            e.preventDefault();\n            deleteSelectedCollection(deleteCollectionButton);\n        }\n    });\n};\n"],"names":["getCheckedTags","root","querySelectorAll","async","document","addEventListener","targetArea","e","target","closest","tagArea","preventDefault","pendingPromise","Pending","query","search","replace","params","Object","fromEntries","URLSearchParams","entries","data","tagindex","methodname","args","html","js","Templates","renderForPromise","replaceNode","error","Notification","exception","resolve","on","toggleClass","ajaxreturn","value","tagManagementCombine","form","reportElement","querySelector","reportSelectors","regions","report","checkedTags","length","alert","tags","Array","from","values","map","tag","namedElement","id","name","dataset","modal","SaveCancelModal","create","title","buttons","save","body","render","show","removeOnClose","getRoot","ModalEvents","tempElement","createElement","hidden","append","tagsElement","check","join","maintag","val","attr","appendTo","submit","getBodyPromise","firstOption","focus","checked","handleCombineRequest","actionurl","window","location","href","sesskey","M","cfg","tagsInput","currentTarget","find","trim","tagsForm","checkValidity","stopPropagation","addClass","errorMessage","removeAttr","addStandardTags","bulkActionDeleteButton","saveCancelPromise","deleteSelectedTags","rowDeleteButton","button","deleteSelectedTag","newvalue","tagid","errorcode","redirectTarget","URL","searchParams","set","Config","areaid","collid","component","itemtype","this","hide","removeClass","addTagCollectionNode","link","url","collectionInput","addTagCollection","deleteCollectionButton","collname","deleteSelectedCollection"],"mappings":"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"}
\ No newline at end of file
+{"version":3,"file":"tag.min.js","sources":["../src/tag.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 <http://www.gnu.org/licenses/>.\n\n/**\n * AJAX helper for the tag management page.\n *\n * @module     core/tag\n * @copyright  2015 Marina Glancy\n * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @since      3.0\n */\n\nimport $ from 'jquery';\nimport {call as fetchMany} from 'core/ajax';\nimport * as Notification from 'core/notification';\nimport * as Templates from 'core/templates';\nimport {getString} from 'core/str';\nimport * as ModalEvents from 'core/modal_events';\nimport Pending from 'core/pending';\nimport SaveCancelModal from 'core/modal_save_cancel';\nimport Config from 'core/config';\nimport {eventTypes as inplaceEditableEvents} from 'core/local/inplace_editable/events';\nimport * as reportSelectors from 'core_reportbuilder/local/selectors';\n\nconst getTagIndex = (tagindex) => fetchMany([{\n    methodname: 'core_tag_get_tagindex',\n    args: {tagindex}\n}])[0];\n\nconst getCheckedTags = (root) => root.querySelectorAll('[data-togglegroup=\"report-select-all\"][data-toggle=\"slave\"]:checked');\n\nconst handleCombineRequest = async(tagManagementCombine) => {\n    const pendingPromise = new Pending('core/tag:tag-management-combine');\n    const form = tagManagementCombine.closest('form');\n\n    const reportElement = document.querySelector(reportSelectors.regions.report);\n    const checkedTags = getCheckedTags(reportElement);\n\n    if (checkedTags.length <= 1) {\n        // We need at least 2 tags to combine them.\n        Notification.alert(\n            getString('combineselected', 'tag'),\n            getString('selectmultipletags', 'tag'),\n            getString('ok'),\n        );\n\n        return;\n    }\n\n    const tags = Array.from(checkedTags.values()).map((tag) => {\n        const namedElement = document.querySelector(`.inplaceeditable[data-itemtype=tagname][data-itemid=\"${tag.value}\"]`);\n        return {\n            id: tag.value,\n            name: namedElement.dataset.value,\n        };\n    });\n\n    const modal = await SaveCancelModal.create({\n        title: getString('combineselected', 'tag'),\n        buttons: {\n            save: getString('continue', 'core'),\n        },\n        body: Templates.render('core_tag/combine_tags', {tags}),\n        show: true,\n        removeOnClose: true,\n    });\n\n    // Handle save event.\n    modal.getRoot().on(ModalEvents.save, (e) => {\n        e.preventDefault();\n\n        // Append this temp element in the form in the tags list, not the form in the modal. Confusing, right?!?\n        const tempElement = document.createElement('input');\n        tempElement.hidden = true;\n        tempElement.name = tagManagementCombine.name;\n        form.append(tempElement);\n\n        // Append selected tags element.\n        const tagsElement = document.createElement('input');\n        tagsElement.hidden = true;\n        tagsElement.name = 'tagschecked';\n        tagsElement.value = [...checkedTags].map(check => check.value).join(',');\n        form.append(tagsElement);\n\n        // Get the selected tag from the modal.\n        var maintag = $('input[name=maintag]:checked', '#combinetags_form').val();\n        // Append this in the tags list form.\n        $(\"<input type='hidden'/>\").attr('name', 'maintag').attr('value', maintag).appendTo(form);\n        // Submit the tags list form.\n        form.submit();\n    });\n\n    await modal.getBodyPromise();\n    // Tick the first option.\n    const firstOption = document.querySelector('#combinetags_form input[type=radio]');\n    firstOption.focus();\n    firstOption.checked = true;\n\n    pendingPromise.resolve();\n\n    return;\n};\n\nconst addStandardTags = async() => {\n    var pendingPromise = new Pending('core/tag:addstandardtag');\n\n    const modal = await SaveCancelModal.create({\n        title: getString('addotags', 'tag'),\n        body: Templates.render('core_tag/add_tags', {\n            actionurl: window.location.href,\n            sesskey: M.cfg.sesskey,\n        }),\n        buttons: {\n            save: getString('continue', 'core'),\n        },\n        removeOnClose: true,\n        show: true,\n    });\n\n    // Handle save event.\n    modal.getRoot().on(ModalEvents.save, (e) => {\n        var tagsInput = $(e.currentTarget).find('#id_tagslist');\n        var name = tagsInput.val().trim();\n\n        // Set the text field's value to the trimmed value.\n        tagsInput.val(name);\n\n        // Add submit event listener to the form.\n        var tagsForm = $('#addtags_form');\n        tagsForm.on('submit', function(e) {\n            // Validate the form.\n            var form = $('#addtags_form');\n            if (form[0].checkValidity() === false) {\n                e.preventDefault();\n                e.stopPropagation();\n            }\n            form.addClass('was-validated');\n\n            // BS2 compatibility.\n            $('[data-region=\"tagslistinput\"]').addClass('error');\n            var errorMessage = $('#id_tagslist_error_message');\n            errorMessage.removeAttr('hidden');\n            errorMessage.addClass('help-block');\n        });\n\n        // Try to submit the form.\n        tagsForm.submit();\n\n        return false;\n    });\n\n    await modal.getBodyPromise();\n    pendingPromise.resolve();\n};\n\nconst deleteSelectedTags = async(bulkActionDeleteButton) => {\n    const form = bulkActionDeleteButton.closest('form');\n\n    const reportElement = document.querySelector(reportSelectors.regions.report);\n    const checkedTags = getCheckedTags(reportElement);\n\n    if (!checkedTags.length) {\n        return;\n    }\n\n    try {\n        await Notification.saveCancelPromise(\n            getString('delete'),\n            getString('confirmdeletetags', 'tag'),\n            getString('yes'),\n            getString('no'),\n        );\n\n        // Append this temp element in the form in the tags list, not the form in the modal. Confusing, right?!?\n        const tempElement = document.createElement('input');\n        tempElement.hidden = true;\n        tempElement.name = bulkActionDeleteButton.name;\n        form.append(tempElement);\n\n        // Append selected tags element.\n        const tagsElement = document.createElement('input');\n        tagsElement.hidden = true;\n        tagsElement.name = 'tagschecked';\n        tagsElement.value = [...checkedTags].map(check => check.value).join(',');\n        form.append(tagsElement);\n\n        form.submit();\n    } catch {\n        return;\n    }\n};\n\nconst deleteSelectedTag = async(button) => {\n    try {\n        await Notification.saveCancelPromise(\n            getString('delete'),\n            getString('confirmdeletetag', 'tag'),\n            getString('yes'),\n            getString('no'),\n        );\n\n        window.location.href = button.href;\n    } catch {\n        return;\n    }\n};\n\nconst deleteSelectedCollection = async(button) => {\n    try {\n        await Notification.saveCancelPromise(\n            getString('delete'),\n            getString('suredeletecoll', 'tag', button.dataset.collname),\n            getString('yes'),\n            getString('no'),\n        );\n\n        const redirectTarget = new URL(button.dataset.url);\n        redirectTarget.searchParams.set('sesskey', Config.sesskey);\n        window.location.href = redirectTarget;\n    } catch {\n        return;\n    }\n};\n\nconst addTagCollection = async(link) => {\n    const pendingPromise = new Pending('core/tag:initManageCollectionsPage-addtagcoll');\n    const href = link.dataset.url;\n\n    const modal = await SaveCancelModal.create({\n        title: getString('addtagcoll', 'tag'),\n        buttons: {\n            save: getString('create', 'core'),\n        },\n        body: Templates.render('core_tag/add_tag_collection', {\n            actionurl: href,\n            sesskey: M.cfg.sesskey,\n        }),\n        removeOnClose: true,\n        show: true,\n    });\n\n    // Handle save event.\n    modal.getRoot().on(ModalEvents.save, (e) => {\n        const collectionInput = $(e.currentTarget).find('#addtagcoll_name');\n        const name = collectionInput.val().trim();\n        // Set the text field's value to the trimmed value.\n        collectionInput.val(name);\n\n        // Add submit event listener to the form.\n        const form = $('#addtagcoll_form');\n        form.on('submit', function(e) {\n            // Validate the form.\n            if (form[0].checkValidity() === false) {\n                e.preventDefault();\n                e.stopPropagation();\n            }\n            form.addClass('was-validated');\n\n            // BS2 compatibility.\n            $('[data-region=\"addtagcoll_nameinput\"]').addClass('error');\n            const errorMessage = $('#id_addtagcoll_name_error_message');\n            errorMessage.removeAttr('hidden');\n            errorMessage.addClass('help-block');\n        });\n\n        // Try to submit the form.\n        form.submit();\n\n        return false;\n    });\n\n    pendingPromise.resolve();\n};\n\n/**\n * Initialises tag index page.\n *\n * @method initTagindexPage\n */\nexport const initTagindexPage = async() => {\n    document.addEventListener('click', async(e) => {\n        const targetArea = e.target.closest('a[data-quickload=\"1\"]');\n        if (!targetArea) {\n            return;\n        }\n        const tagArea = targetArea.closest('.tagarea[data-ta]');\n        if (!tagArea) {\n            return;\n        }\n\n        e.preventDefault();\n        const pendingPromise = new Pending('core/tag:initTagindexPage');\n\n        const query = targetArea.search.replace(/^\\?/, '');\n        const params = Object.fromEntries((new URLSearchParams(query)).entries());\n\n        try {\n            const data = await getTagIndex(params);\n            const {html, js} = await Templates.renderForPromise('core_tag/index', data);\n            Templates.replaceNode(tagArea, html, js);\n        } catch (error) {\n            Notification.exception(error);\n        }\n        pendingPromise.resolve();\n    });\n};\n\n/**\n * Initialises tag management page.\n *\n * @method initManagePage\n */\nexport const initManagePage = () => {\n    // Toggle row class when updating flag.\n    $('body').on(inplaceEditableEvents.elementUpdated, '[data-inplaceeditable][data-itemtype=tagflag]', function(e) {\n        var row = $(e.target).closest('tr');\n        row.toggleClass('table-warning', e.detail.ajaxreturn.value === '1');\n    });\n\n    // Confirmation for bulk tag combine button.\n    document.addEventListener('click', async(e) => {\n        const tagManagementCombine = e.target.closest('#tag-management-combine');\n        if (tagManagementCombine) {\n            e.preventDefault();\n            handleCombineRequest(tagManagementCombine);\n        }\n\n        if (e.target.closest('[data-action=\"addstandardtag\"]')) {\n            e.preventDefault();\n            addStandardTags();\n        }\n\n        const bulkActionDeleteButton = e.target.closest('#tag-management-delete');\n        if (bulkActionDeleteButton) {\n            e.preventDefault();\n            deleteSelectedTags(bulkActionDeleteButton);\n        }\n\n        const rowDeleteButton = e.target.closest('.tagdelete');\n        if (rowDeleteButton) {\n            e.preventDefault();\n            deleteSelectedTag(rowDeleteButton);\n        }\n    });\n\n    // When user changes tag name to some name that already exists suggest to combine the tags.\n    $('body').on(inplaceEditableEvents.elementUpdateFailed, '[data-inplaceeditable][data-itemtype=tagname]', async(e) => {\n        var exception = e.detail.exception; // The exception object returned by the callback.\n        var newvalue = e.detail.newvalue; // The value that user tried to udpated the element to.\n        var tagid = $(e.target).attr('data-itemid');\n        if (exception.errorcode !== 'namesalreadybeeingused') {\n            return;\n        }\n        e.preventDefault(); // This will prevent default error dialogue.\n\n        try {\n            await Notification.saveCancelPromise(\n                getString('confirm'),\n                getString('nameuseddocombine', 'tag'),\n                getString('yes'),\n                getString('cancel'),\n            );\n\n            // The Promise will resolve on 'Yes' button, and reject on 'Cancel' button.\n            const redirectTarget = new URL(window.location);\n            redirectTarget.searchParams.set('newname', newvalue);\n            redirectTarget.searchParams.set('tagid', tagid);\n            redirectTarget.searchParams.set('action', 'renamecombine');\n            redirectTarget.searchParams.set('sesskey', Config.sesskey);\n\n            window.location.href = redirectTarget;\n        } catch {\n            return;\n        }\n    });\n};\n\n/**\n * Initialises tag collection management page.\n *\n * @method initManageCollectionsPage\n */\nexport const initManageCollectionsPage = () => {\n    $('body').on(inplaceEditableEvents.elementUpdated, '[data-inplaceeditable]', function(e) {\n        var pendingPromise = new Pending('core/tag:initManageCollectionsPage-updated');\n\n        var ajaxreturn = e.detail.ajaxreturn,\n            areaid, collid, isenabled;\n        if (ajaxreturn.component === 'core_tag' && ajaxreturn.itemtype === 'tagareaenable') {\n            areaid = $(this).attr('data-itemid');\n            $(\".tag-collections-table ul[data-collectionid] li[data-areaid=\" + areaid + \"]\").hide();\n            isenabled = ajaxreturn.value;\n            if (isenabled === '1') {\n                $(this).closest('tr').removeClass('dimmed_text');\n                collid = $(this).closest('tr').find('[data-itemtype=\"tagareacollection\"]').attr(\"data-value\");\n                $(\".tag-collections-table ul[data-collectionid=\" + collid + \"] li[data-areaid=\" + areaid + \"]\").show();\n            } else {\n                $(this).closest('tr').addClass('dimmed_text');\n            }\n        }\n        if (ajaxreturn.component === 'core_tag' && ajaxreturn.itemtype === 'tagareacollection') {\n            areaid = $(this).attr('data-itemid');\n            $(\".tag-collections-table ul[data-collectionid] li[data-areaid=\" + areaid + \"]\").hide();\n            collid = $(this).attr('data-value');\n            isenabled = $(this).closest('tr').find('[data-itemtype=\"tagareaenable\"]').attr(\"data-value\");\n            if (isenabled === \"1\") {\n                $(\".tag-collections-table ul[data-collectionid=\" + collid + \"] li[data-areaid=\" + areaid + \"]\").show();\n            }\n        }\n\n        pendingPromise.resolve();\n    });\n\n    document.addEventListener('click', async(e) => {\n        const addTagCollectionNode = e.target.closest('.addtagcoll > a');\n        if (addTagCollectionNode) {\n            e.preventDefault();\n            addTagCollection(addTagCollectionNode);\n            return;\n        }\n\n        const deleteCollectionButton = e.target.closest('.tag-collections-table .action_delete');\n        if (deleteCollectionButton) {\n            e.preventDefault();\n            deleteSelectedCollection(deleteCollectionButton);\n        }\n    });\n};\n"],"names":["getCheckedTags","root","querySelectorAll","async","document","addEventListener","targetArea","e","target","closest","tagArea","preventDefault","pendingPromise","Pending","query","search","replace","params","Object","fromEntries","URLSearchParams","entries","data","tagindex","methodname","args","html","js","Templates","renderForPromise","replaceNode","error","Notification","exception","resolve","on","inplaceEditableEvents","elementUpdated","toggleClass","detail","ajaxreturn","value","tagManagementCombine","form","reportElement","querySelector","reportSelectors","regions","report","checkedTags","length","alert","tags","Array","from","values","map","tag","namedElement","id","name","dataset","modal","SaveCancelModal","create","title","buttons","save","body","render","show","removeOnClose","getRoot","ModalEvents","tempElement","createElement","hidden","append","tagsElement","check","join","maintag","val","attr","appendTo","submit","getBodyPromise","firstOption","focus","checked","handleCombineRequest","actionurl","window","location","href","sesskey","M","cfg","tagsInput","currentTarget","find","trim","tagsForm","checkValidity","stopPropagation","addClass","errorMessage","removeAttr","addStandardTags","bulkActionDeleteButton","saveCancelPromise","deleteSelectedTags","rowDeleteButton","button","deleteSelectedTag","elementUpdateFailed","newvalue","tagid","errorcode","redirectTarget","URL","searchParams","set","Config","areaid","collid","component","itemtype","this","hide","removeClass","addTagCollectionNode","link","url","collectionInput","addTagCollection","deleteCollectionButton","collname","deleteSelectedCollection"],"mappings":"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"}
\ No newline at end of file
diff --git a/lib/amd/src/local/inplace_editable/events.js b/lib/amd/src/local/inplace_editable/events.js
index f4ebd6df0cd..af443858b03 100644
--- a/lib/amd/src/local/inplace_editable/events.js
+++ b/lib/amd/src/local/inplace_editable/events.js
@@ -21,7 +21,6 @@
  * @license     http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 
-import $ from 'jquery';
 import {dispatchEvent} from 'core/event_dispatcher';
 
 /**
@@ -98,29 +97,3 @@ export const notifyElementUpdateFailed = (element, exception, newvalue) => dispa
         cancelable: true
     }
 );
-
-let legacyEventsRegistered = false;
-if (!legacyEventsRegistered) {
-    // The following event triggers are legacy and will be removed in the future.
-    // The following approach provides a backwards-compatability layer for the new events.
-    // Code should be updated to make use of native events.
-
-    // Listen for the new native elementUpdated event, and trigger the legacy jQuery event.
-    document.addEventListener(eventTypes.elementUpdated, event => {
-        const legacyEvent = $.Event('updated', event.detail);
-        $(event.target).trigger(legacyEvent);
-    });
-
-    // Listen for the new native elementUpdateFailed event, and trigger the legacy jQuery event.
-    document.addEventListener(eventTypes.elementUpdateFailed, event => {
-        const legacyEvent = $.Event('updatefailed', event.detail);
-        $(event.target).trigger(legacyEvent);
-
-        // If the legacy event is cancelled, so should the native event.
-        if (legacyEvent.isDefaultPrevented()) {
-            event.preventDefault();
-        }
-    });
-
-    legacyEventsRegistered = true;
-}
diff --git a/lib/amd/src/tag.js b/lib/amd/src/tag.js
index e8c6d98c6d0..58116c91b24 100644
--- a/lib/amd/src/tag.js
+++ b/lib/amd/src/tag.js
@@ -31,6 +31,7 @@ import * as ModalEvents from 'core/modal_events';
 import Pending from 'core/pending';
 import SaveCancelModal from 'core/modal_save_cancel';
 import Config from 'core/config';
+import {eventTypes as inplaceEditableEvents} from 'core/local/inplace_editable/events';
 import * as reportSelectors from 'core_reportbuilder/local/selectors';
 
 const getTagIndex = (tagindex) => fetchMany([{
@@ -323,9 +324,9 @@ export const initTagindexPage = async() => {
  */
 export const initManagePage = () => {
     // Toggle row class when updating flag.
-    $('body').on('updated', '[data-inplaceeditable][data-itemtype=tagflag]', function(e) {
+    $('body').on(inplaceEditableEvents.elementUpdated, '[data-inplaceeditable][data-itemtype=tagflag]', function(e) {
         var row = $(e.target).closest('tr');
-        row.toggleClass('table-warning', e.ajaxreturn.value === '1');
+        row.toggleClass('table-warning', e.detail.ajaxreturn.value === '1');
     });
 
     // Confirmation for bulk tag combine button.
@@ -355,9 +356,9 @@ export const initManagePage = () => {
     });
 
     // When user changes tag name to some name that already exists suggest to combine the tags.
-    $('body').on('updatefailed', '[data-inplaceeditable][data-itemtype=tagname]', async(e) => {
-        var exception = e.exception; // The exception object returned by the callback.
-        var newvalue = e.newvalue; // The value that user tried to udpated the element to.
+    $('body').on(inplaceEditableEvents.elementUpdateFailed, '[data-inplaceeditable][data-itemtype=tagname]', async(e) => {
+        var exception = e.detail.exception; // The exception object returned by the callback.
+        var newvalue = e.detail.newvalue; // The value that user tried to udpated the element to.
         var tagid = $(e.target).attr('data-itemid');
         if (exception.errorcode !== 'namesalreadybeeingused') {
             return;
@@ -392,10 +393,10 @@ export const initManagePage = () => {
  * @method initManageCollectionsPage
  */
 export const initManageCollectionsPage = () => {
-    $('body').on('updated', '[data-inplaceeditable]', function(e) {
+    $('body').on(inplaceEditableEvents.elementUpdated, '[data-inplaceeditable]', function(e) {
         var pendingPromise = new Pending('core/tag:initManageCollectionsPage-updated');
 
-        var ajaxreturn = e.ajaxreturn,
+        var ajaxreturn = e.detail.ajaxreturn,
             areaid, collid, isenabled;
         if (ajaxreturn.component === 'core_tag' && ajaxreturn.itemtype === 'tagareaenable') {
             areaid = $(this).attr('data-itemid');