diff --git a/.upgradenotes/MDL-79182-2025112106564582.yml b/.upgradenotes/MDL-79182-2025112106564582.yml
new file mode 100644
index 00000000000..02ddd0134cc
--- /dev/null
+++ b/.upgradenotes/MDL-79182-2025112106564582.yml
@@ -0,0 +1,7 @@
+issueNumber: MDL-79182
+notes:
+ core:
+ - message:
+ - Removed core/modal_registry for the final deprecation due to no longer being required.
+ - Removed core/modal_factory for the final deprecation due to no longer being required.
+ type: removed
diff --git a/public/lib/amd/build/modal.min.js b/public/lib/amd/build/modal.min.js
index 1df82979b72..1f3ddfa1719 100644
--- a/public/lib/amd/build/modal.min.js
+++ b/public/lib/amd/build/modal.min.js
@@ -1,3 +1,3 @@
-define("core/modal",["exports","jquery","core/templates","core/notification","core/key_codes","core/modal_backdrop","core/modal_events","core/modal_registry","core/pending","core/custom_interaction_events","core_filters/events","core/local/aria/focuslock","core/aria","core/fullscreen","./toast","core/event_dispatcher"],(function(_exports,_jquery,Templates,Notification,KeyCodes,_modal_backdrop,_modal_events,ModalRegistry,_pending,CustomEvents,FilterEvents,FocusLock,Aria,Fullscreen,_toast,_event_dispatcher){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}}function _defineProperty(obj,key,value){return key in obj?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value,obj}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_jquery=_interopRequireDefault(_jquery),Templates=_interopRequireWildcard(Templates),Notification=_interopRequireWildcard(Notification),KeyCodes=_interopRequireWildcard(KeyCodes),_modal_backdrop=_interopRequireDefault(_modal_backdrop),_modal_events=_interopRequireDefault(_modal_events),ModalRegistry=_interopRequireWildcard(ModalRegistry),_pending=_interopRequireDefault(_pending),CustomEvents=_interopRequireWildcard(CustomEvents),FilterEvents=_interopRequireWildcard(FilterEvents),FocusLock=_interopRequireWildcard(FocusLock),Aria=_interopRequireWildcard(Aria),Fullscreen=_interopRequireWildcard(Fullscreen);const SELECTORS_CONTAINER='[data-region="modal-container"]',SELECTORS_MODAL='[data-region="modal"]',SELECTORS_HEADER='[data-region="header"]',SELECTORS_TITLE='[data-region="title"]',SELECTORS_BODY='[data-region="body"]',SELECTORS_FOOTER='[data-region="footer"]',SELECTORS_HIDE='[data-action="hide"]',SELECTORS_DIALOG="[role=dialog]",SELECTORS_FORM="form",SELECTORS_MENU_BAR="[role=menubar]",SELECTORS_HAS_Z_INDEX=".moodle-has-zindex",TEMPLATES_LOADING="core/loading",TEMPLATES_BACKDROP="core/modal_backdrop";class Modal{get root(){return(0,_jquery.default)(this._root.filter(SELECTORS_CONTAINER))}set root(root){this._root=root}constructor(root){this.root=(0,_jquery.default)(root),this.modal=this.root.find(SELECTORS_MODAL),this.header=this.modal.find(SELECTORS_HEADER),this.headerPromise=_jquery.default.Deferred(),this.title=this.header.find(SELECTORS_TITLE),this.titlePromise=_jquery.default.Deferred(),this.body=this.modal.find(SELECTORS_BODY),this.bodyPromise=_jquery.default.Deferred(),this.footer=this.modal.find(SELECTORS_FOOTER),this.footerPromise=_jquery.default.Deferred(),this.hiddenSiblings=[],this.isAttached=!1,this.bodyJS=null,this.footerJS=null,this.modalCount=Modal.modalCounter++,this.attachmentPoint=document.createElement("div"),document.body.append(this.attachmentPoint),this.focusOnClose=null,this.templateJS=null,this.root.is(SELECTORS_CONTAINER)||Notification.exception({message:"Element is not a modal container"}),this.modal.length||Notification.exception({message:"Container does not contain a modal"}),this.header.length||Notification.exception({message:"Modal is missing a header region"}),this.title.length||Notification.exception({message:"Modal header is missing a title region"}),this.body.length||Notification.exception({message:"Modal is missing a body region"}),this.footer.length||Notification.exception({message:"Modal is missing a footer region"}),this.registerEventListeners()}static registerModalType(){if(!this.TYPE)throw new Error("Unknown modal type",this);if(!this.TEMPLATE)throw new Error("Unknown modal template",this);ModalRegistry.register(this.TYPE,this,this.TEMPLATE)}static async create(){let modalConfig=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};const pendingModalPromise=new _pending.default("core/modal_factory:create");modalConfig.type=this.TYPE;const templateName=this._getTemplateName(modalConfig),templateContext=modalConfig.templateContext||{},{html:html,js:js}=await Templates.renderForPromise(templateName,templateContext),modal=new this(html);return js&&modal.setTemplateJS(js),modal.configure(modalConfig),pendingModalPromise.resolve(),modal}static _getTemplateName(modalConfig){if(modalConfig.template)return modalConfig.template;if(this.TEMPLATE)return this.TEMPLATE;if(ModalRegistry.has(this.TYPE)){window.console.warning("Use of core/modal_registry is deprecated. Please define your modal template in a new static TEMPLATE property on your modal class.");return ModalRegistry.get(this.TYPE).template}throw new Error("Unable to determine template name for modal ".concat(this.TYPE))}configure(){let{show:show=!1,large:large=!1,isVerticallyCentered:isVerticallyCentered=!1,removeOnClose:removeOnClose=!1,scrollable:scrollable=!0,returnElement:returnElement,title:title,body:body,footer:footer,buttons:buttons={}}=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};large&&this.setLarge(),isVerticallyCentered&&this.setVerticallyCentered(),this.setRemoveOnClose(removeOnClose),this.setReturnElement(returnElement),this.setScrollable(scrollable),void 0!==title&&this.setTitle(title),void 0!==body&&this.setBody(body),void 0!==footer&&this.setFooter(footer),Object.entries(buttons).forEach((_ref=>{let[key,value]=_ref;return this.setButtonText(key,value)})),show&&this.show()}attachToDOM(){this.getAttachmentPoint().append(this._root),this.isAttached||(FocusLock.trapFocus(this.root[0]),this.templateJS&&(Templates.runTemplateJS(this.templateJS),this.templateJS=null),this.bodyJS&&(Templates.runTemplateJS(this.bodyJS),this.bodyJS=null),this.footerJS&&(Templates.runTemplateJS(this.footerJS),this.footerJS=null),this.isAttached=!0)}countOtherVisibleModals(){let count=0;return(0,_jquery.default)("body").find(SELECTORS_CONTAINER).each(((index,element)=>{element=(0,_jquery.default)(element),!this.root.is(element)&&element.hasClass("show")&&count++})),count}getBackdrop(){return Modal.backdropPromise||(Modal.backdropPromise=Templates.render(TEMPLATES_BACKDROP,{}).then((html=>new _modal_backdrop.default((0,_jquery.default)(html)))).catch(Notification.exception)),Modal.backdropPromise}getRoot(){return this.root}getModal(){return this.modal}getTitle(){return this.title}getBody(){return this.body}getFooter(){return this.footer}getTitlePromise(){return this.titlePromise}getBodyPromise(){return this.bodyPromise}getFooterPromise(){return this.footerPromise}getModalCount(){return this.modalCount}setTitle(value){const title=this.getTitle();this.titlePromise=_jquery.default.Deferred(),this.asyncSet(value,title.html.bind(title)).then((()=>{this.titlePromise.resolve(title)})).catch(Notification.exception)}setBody(value){this.bodyPromise=_jquery.default.Deferred();const body=this.getBody();if("string"==typeof value)body.html(value),FilterEvents.notifyFilterContentUpdated(body),this.getRoot().trigger(_modal_events.default.bodyRendered,this),this.bodyPromise.resolve(body);else{const modalPromise=new _pending.default("amd-modal-js-pending-id-".concat(this.getModalCount()));let contentPromise=null;if(body.css("overflow","hidden"),"pending"==(value=_jquery.default.when(value)).state()){let height=body.innerHeight();height<100&&(height=100),body.animate({height:"".concat(height,"px")},150),body.html(""),contentPromise=Templates.render(TEMPLATES_LOADING,{}).then((html=>{const loadingIcon=(0,_jquery.default)(html).hide();return body.html(loadingIcon),loadingIcon.fadeIn(150),_jquery.default.when(loadingIcon.promise(),value)})).then((loadingIcon=>loadingIcon.fadeOut(100).promise())).then((()=>value))}else contentPromise=value;contentPromise.then(((html,js)=>{let result=null;if(this.isVisible()){body.css("opacity",0);const currentHeight=body.innerHeight();body.html(html),body.css("height","");const newHeight=body.innerHeight();body.css("height","".concat(currentHeight,"px")),result=body.animate({height:"".concat(newHeight,"px"),opacity:1},{duration:150,queue:!1}).promise()}else body.html(html);return js&&(this.isAttached?Templates.runTemplateJS(js):this.bodyJS=js),result})).then((result=>(FilterEvents.notifyFilterContentUpdated(body),this.getRoot().trigger(_modal_events.default.bodyRendered,this),(0,_event_dispatcher.dispatchEvent)("core/modal:bodyRendered",this,this.modal[0]),result))).then((()=>{this.bodyPromise.resolve(body)})).catch(Notification.exception).always((()=>{body.css("height",""),body.css("overflow",""),body.css("opacity",""),modalPromise.resolve()}))}}setBodyContent(promise){return promise.then((_ref2=>{let{html:html,js:js}=_ref2;return this.setBody(_jquery.default.when(html,js))})).catch((exception=>{throw this.hide(),exception}))}setFooter(value){this.showFooter(),this.footerPromise=_jquery.default.Deferred();const footer=this.getFooter();"string"==typeof value?(footer.html(value),this.footerPromise.resolve(footer)):Templates.render(TEMPLATES_LOADING,{}).then((html=>(footer.html(html),value))).then(((html,js)=>(footer.html(html),js&&(this.isAttached?Templates.runTemplateJS(js):this.footerJS=js),footer))).then((footer=>{this.footerPromise.resolve(footer),this.showFooter()})).catch(Notification.exception)}hasFooterContent(){return!!this.getFooter().children().length}hideFooter(){this.getFooter().addClass("hidden")}showFooter(){this.getFooter().removeClass("hidden")}setLarge(){this.isLarge()||this.getModal().addClass("modal-lg")}setVerticallyCentered(){this.isVerticallyCentered()||this.getModal().addClass("modal-dialog-centered")}isLarge(){return this.getModal().hasClass("modal-lg")}isVerticallyCentered(){return this.getModal().hasClass("modal-dialog-centered")}setSmall(){this.isSmall()||this.getModal().removeClass("modal-lg")}isSmall(){return!this.getModal().hasClass("modal-lg")}setScrollable(value){value?this.getModal()[0].classList.add("modal-dialog-scrollable"):this.getModal()[0].classList.remove("modal-dialog-scrollable")}calculateZIndex(){const items=(0,_jquery.default)("".concat(SELECTORS_DIALOG,", ").concat(SELECTORS_MENU_BAR,", ").concat(SELECTORS_HAS_Z_INDEX));let zIndex=parseInt(this.root.css("z-index"));return items.each(((index,item)=>{if(!(item=(0,_jquery.default)(item)).is(":visible"))return;const itemZIndex=item.css("z-index")?parseInt(item.css("z-index")):0;itemZIndex>zIndex&&(zIndex=itemZIndex)})),zIndex}isVisible(){return this.root.hasClass("show")}hasFocus(){const target=(0,_jquery.default)(document.activeElement);return this.root.is(target)||this.root.has(target).length}hasTransitions(){return this.getRoot().hasClass("fade")}getAttachmentPoint(){return(0,_jquery.default)(Fullscreen.getElement()||this.attachmentPoint)}show(){if(this.isVisible())return _jquery.default.Deferred().resolve();const pendingPromise=new _pending.default("core/modal:show");return this.hasFooterContent()?this.showFooter():this.hideFooter(),this.attachToDOM(),!this.focusOnClose&&document.activeElement&&(this.focusOnClose=document.activeElement),this.getBackdrop().then((backdrop=>{const newIndex=this.calculateZIndex()+2,newBackdropIndex=newIndex-1;this.root.css("z-index",newIndex),backdrop.setZIndex(newBackdropIndex),backdrop.show(),this.root.removeClass("hide").addClass("show"),this.accessibilityShow(),this.getModal().focus(),(0,_jquery.default)("body").addClass("modal-open");const scrollbarWidth=window.innerWidth-document.documentElement.clientWidth;(0,_jquery.default)("body").css({overflow:"hidden",paddingRight:"".concat(scrollbarWidth,"px")}),this.root.trigger(_modal_events.default.shown,this),(0,_event_dispatcher.dispatchEvent)("core/modal:shown",this,this.modal[0])})).then(pendingPromise.resolve)}hideIfNotForm(){0==this.modal.find(SELECTORS_FORM).length&&this.hide()}hide(){this.getBackdrop().done((backdrop=>{FocusLock.untrapFocus(),this.countOtherVisibleModals()||(backdrop.hide(),(0,_jquery.default)("body").removeClass("modal-open"),(0,_jquery.default)("body").css({overflow:"",paddingRight:""}));const currentIndex=parseInt(this.root.css("z-index"));this.root.css("z-index",""),backdrop.setZIndex(currentIndex-3),this.accessibilityHide(),this.hasTransitions()?this.getRoot().one("transitionend webkitTransitionEnd oTransitionEnd",(()=>{this.getRoot().removeClass("show").addClass("hide")})):this.getRoot().removeClass("show").addClass("hide"),(0,_jquery.default)(document.body).find(this.getRoot()).length&&(0,_jquery.default)(document.body).append(this.getRoot()),this.getRoot().find('[data-bs-toggle="popover"]').each((function(){var _document$getElementB;null===(_document$getElementB=document.getElementById(this.getAttribute("aria-describedby")))||void 0===_document$getElementB||_document$getElementB.remove()})),this.root.trigger(_modal_events.default.hidden,this)}))}destroy(){this.hide(),(0,_toast.removeToastRegion)(this.getBody().get(0)),this.root.remove(),this.root.trigger(_modal_events.default.destroyed,this),this.attachmentPoint.remove()}accessibilityShow(){Aria.unhide(this.root.get()),Aria.hideSiblings(this.root.get()[0])}accessibilityHide(){Aria.unhideSiblings(this.root.get()[0]),Aria.hide(this.root.get())}registerEventListeners(){this.getRoot().on("keydown",(e=>{this.isVisible()&&e.keyCode==KeyCodes.escape&&(this.removeOnClose?this.destroy():this.hide())})),this.getRoot().click((e=>{if(!(0,_jquery.default)(e.target).closest(SELECTORS_MODAL).length&&(0,_jquery.default)(e.target).closest(SELECTORS_CONTAINER).length){const outsideClickEvent=_jquery.default.Event(_modal_events.default.outsideClick);this.getRoot().trigger(outsideClickEvent,this),outsideClickEvent.isDefaultPrevented()||this.hideIfNotForm()}})),CustomEvents.define(this.getModal(),[CustomEvents.events.activate]),this.getModal().on(CustomEvents.events.activate,SELECTORS_HIDE,((e,data)=>{this.removeOnClose?this.destroy():this.hide(),data.originalEvent.preventDefault()})),this.getRoot().on(_modal_events.default.hidden,(()=>{this.focusOnClose&&this.focusOnClose.focus()}))}registerCloseOnCancel(){this.getModal().on(CustomEvents.events.activate,this.getActionSelector("cancel"),((e,data)=>{const cancelEvent=_jquery.default.Event(_modal_events.default.cancel);this.getRoot().trigger(cancelEvent,this),cancelEvent.isDefaultPrevented()||(data.originalEvent.preventDefault(),this.removeOnClose?this.destroy():this.hide())}))}registerCloseOnSave(){this.getModal().on(CustomEvents.events.activate,this.getActionSelector("save"),((e,data)=>{const saveEvent=_jquery.default.Event(_modal_events.default.save);this.getRoot().trigger(saveEvent,this),saveEvent.isDefaultPrevented()||(data.originalEvent.preventDefault(),this.removeOnClose?this.destroy():this.hide())}))}registerCloseOnDelete(){this.getModal().on(CustomEvents.events.activate,this.getActionSelector("delete"),((e,data)=>{const deleteEvent=_jquery.default.Event(_modal_events.default.delete);this.getRoot().trigger(deleteEvent,this),deleteEvent.isDefaultPrevented()||(data.originalEvent.preventDefault(),this.removeOnClose?this.destroy():this.hide())}))}asyncSet(value,setFunction){return(value=>value instanceof Promise?_jquery.default.when(value):"object"==typeof value&&value.hasOwnProperty("then")?value:_jquery.default.Deferred().resolve(value))(value).then((content=>setFunction(content))).catch(Notification.exception)}setButtonText(action,value){const button=this.getFooter().find(this.getActionSelector(action));if(!button)throw new Error("Unable to find the '"+action+"' button");return this.asyncSet(value,button.text.bind(button))}getActionSelector(action){return"[data-action='"+action+"']"}setRemoveOnClose(remove){this.removeOnClose=remove}setReturnElement(element){this.focusOnClose=element}setButtonDisabled(action,disabled){const button=this.getFooter().find(this.getActionSelector(action));if(!button)throw new Error("Unable to find the '"+action+"' button");disabled?button.attr("disabled",""):button.removeAttr("disabled")}setTemplateJS(js){this.templateJS=js}}return _exports.default=Modal,_defineProperty(Modal,"TYPE","default"),_defineProperty(Modal,"TEMPLATE","core/modal"),_defineProperty(Modal,"backdropPromise",null),_defineProperty(Modal,"modalCounter",0),_exports.default}));
+define("core/modal",["exports","jquery","core/templates","core/notification","core/key_codes","core/modal_backdrop","core/modal_events","core/pending","core/custom_interaction_events","core_filters/events","core/local/aria/focuslock","core/aria","core/fullscreen","./toast","core/event_dispatcher","core/prefetch"],(function(_exports,_jquery,Templates,Notification,KeyCodes,_modal_backdrop,_modal_events,_pending,CustomEvents,FilterEvents,FocusLock,Aria,Fullscreen,_toast,_event_dispatcher,Prefetch){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}}function _defineProperty(obj,key,value){return key in obj?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value,obj}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_jquery=_interopRequireDefault(_jquery),Templates=_interopRequireWildcard(Templates),Notification=_interopRequireWildcard(Notification),KeyCodes=_interopRequireWildcard(KeyCodes),_modal_backdrop=_interopRequireDefault(_modal_backdrop),_modal_events=_interopRequireDefault(_modal_events),_pending=_interopRequireDefault(_pending),CustomEvents=_interopRequireWildcard(CustomEvents),FilterEvents=_interopRequireWildcard(FilterEvents),FocusLock=_interopRequireWildcard(FocusLock),Aria=_interopRequireWildcard(Aria),Fullscreen=_interopRequireWildcard(Fullscreen),Prefetch=_interopRequireWildcard(Prefetch);const SELECTORS_CONTAINER='[data-region="modal-container"]',SELECTORS_MODAL='[data-region="modal"]',SELECTORS_HEADER='[data-region="header"]',SELECTORS_TITLE='[data-region="title"]',SELECTORS_BODY='[data-region="body"]',SELECTORS_FOOTER='[data-region="footer"]',SELECTORS_HIDE='[data-action="hide"]',SELECTORS_DIALOG="[role=dialog]",SELECTORS_FORM="form",SELECTORS_MENU_BAR="[role=menubar]",SELECTORS_HAS_Z_INDEX=".moodle-has-zindex",TEMPLATES_LOADING="core/loading",TEMPLATES_BACKDROP="core/modal_backdrop";class Modal{get root(){return(0,_jquery.default)(this._root.filter(SELECTORS_CONTAINER))}set root(root){this._root=root}constructor(root){this.root=(0,_jquery.default)(root),this.modal=this.root.find(SELECTORS_MODAL),this.header=this.modal.find(SELECTORS_HEADER),this.headerPromise=_jquery.default.Deferred(),this.title=this.header.find(SELECTORS_TITLE),this.titlePromise=_jquery.default.Deferred(),this.body=this.modal.find(SELECTORS_BODY),this.bodyPromise=_jquery.default.Deferred(),this.footer=this.modal.find(SELECTORS_FOOTER),this.footerPromise=_jquery.default.Deferred(),this.hiddenSiblings=[],this.isAttached=!1,this.bodyJS=null,this.footerJS=null,this.modalCount=Modal.modalCounter++,this.attachmentPoint=document.createElement("div"),document.body.append(this.attachmentPoint),this.focusOnClose=null,this.templateJS=null,this.root.is(SELECTORS_CONTAINER)||Notification.exception({message:"Element is not a modal container"}),this.modal.length||Notification.exception({message:"Container does not contain a modal"}),this.header.length||Notification.exception({message:"Modal is missing a header region"}),this.title.length||Notification.exception({message:"Modal header is missing a title region"}),this.body.length||Notification.exception({message:"Modal is missing a body region"}),this.footer.length||Notification.exception({message:"Modal is missing a footer region"}),this.registerEventListeners()}static registerModalType(){if(!this.TYPE)throw new Error("Unknown modal type",this);if(!this.TEMPLATE)throw new Error("Unknown modal template",this);this.register(this.TYPE,this,this.TEMPLATE)}static async create(){let modalConfig=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};const pendingModalPromise=new _pending.default("core/modal:create");modalConfig.type=this.TYPE;const templateName=this._getTemplateName(modalConfig),templateContext=modalConfig.templateContext||{},{html:html,js:js}=await Templates.renderForPromise(templateName,templateContext),modal=new this(html);return js&&modal.setTemplateJS(js),modal.configure(modalConfig),pendingModalPromise.resolve(),modal}static _getTemplateName(modalConfig){if(modalConfig.template)return modalConfig.template;if(this.TEMPLATE)return this.TEMPLATE;if(this.registry.has(this.TYPE)){return this.registry.get(this.TYPE).template}throw new Error("Unable to determine template name for modal ".concat(this.TYPE))}configure(){let{show:show=!1,large:large=!1,isVerticallyCentered:isVerticallyCentered=!1,removeOnClose:removeOnClose=!1,scrollable:scrollable=!0,returnElement:returnElement,title:title,body:body,footer:footer,buttons:buttons={}}=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};large&&this.setLarge(),isVerticallyCentered&&this.setVerticallyCentered(),this.setRemoveOnClose(removeOnClose),this.setReturnElement(returnElement),this.setScrollable(scrollable),void 0!==title&&this.setTitle(title),void 0!==body&&this.setBody(body),void 0!==footer&&this.setFooter(footer),Object.entries(buttons).forEach((_ref=>{let[key,value]=_ref;return this.setButtonText(key,value)})),show&&this.show()}attachToDOM(){this.getAttachmentPoint().append(this._root),this.isAttached||(FocusLock.trapFocus(this.root[0]),this.templateJS&&(Templates.runTemplateJS(this.templateJS),this.templateJS=null),this.bodyJS&&(Templates.runTemplateJS(this.bodyJS),this.bodyJS=null),this.footerJS&&(Templates.runTemplateJS(this.footerJS),this.footerJS=null),this.isAttached=!0)}countOtherVisibleModals(){let count=0;return(0,_jquery.default)("body").find(SELECTORS_CONTAINER).each(((index,element)=>{element=(0,_jquery.default)(element),!this.root.is(element)&&element.hasClass("show")&&count++})),count}getBackdrop(){return Modal.backdropPromise||(Modal.backdropPromise=Templates.render(TEMPLATES_BACKDROP,{}).then((html=>new _modal_backdrop.default((0,_jquery.default)(html)))).catch(Notification.exception)),Modal.backdropPromise}getRoot(){return this.root}getModal(){return this.modal}getTitle(){return this.title}getBody(){return this.body}getFooter(){return this.footer}getTitlePromise(){return this.titlePromise}getBodyPromise(){return this.bodyPromise}getFooterPromise(){return this.footerPromise}getModalCount(){return this.modalCount}setTitle(value){const title=this.getTitle();this.titlePromise=_jquery.default.Deferred(),this.asyncSet(value,title.html.bind(title)).then((()=>{this.titlePromise.resolve(title)})).catch(Notification.exception)}setBody(value){this.bodyPromise=_jquery.default.Deferred();const body=this.getBody();if("string"==typeof value)body.html(value),FilterEvents.notifyFilterContentUpdated(body),this.getRoot().trigger(_modal_events.default.bodyRendered,this),this.bodyPromise.resolve(body);else{const modalPromise=new _pending.default("amd-modal-js-pending-id-".concat(this.getModalCount()));let contentPromise=null;if(body.css("overflow","hidden"),"pending"==(value=_jquery.default.when(value)).state()){let height=body.innerHeight();height<100&&(height=100),body.animate({height:"".concat(height,"px")},150),body.html(""),contentPromise=Templates.render(TEMPLATES_LOADING,{}).then((html=>{const loadingIcon=(0,_jquery.default)(html).hide();return body.html(loadingIcon),loadingIcon.fadeIn(150),_jquery.default.when(loadingIcon.promise(),value)})).then((loadingIcon=>loadingIcon.fadeOut(100).promise())).then((()=>value))}else contentPromise=value;contentPromise.then(((html,js)=>{let result=null;if(this.isVisible()){body.css("opacity",0);const currentHeight=body.innerHeight();body.html(html),body.css("height","");const newHeight=body.innerHeight();body.css("height","".concat(currentHeight,"px")),result=body.animate({height:"".concat(newHeight,"px"),opacity:1},{duration:150,queue:!1}).promise()}else body.html(html);return js&&(this.isAttached?Templates.runTemplateJS(js):this.bodyJS=js),result})).then((result=>(FilterEvents.notifyFilterContentUpdated(body),this.getRoot().trigger(_modal_events.default.bodyRendered,this),(0,_event_dispatcher.dispatchEvent)("core/modal:bodyRendered",this,this.modal[0]),result))).then((()=>{this.bodyPromise.resolve(body)})).catch(Notification.exception).always((()=>{body.css("height",""),body.css("overflow",""),body.css("opacity",""),modalPromise.resolve()}))}}setBodyContent(promise){return promise.then((_ref2=>{let{html:html,js:js}=_ref2;return this.setBody(_jquery.default.when(html,js))})).catch((exception=>{throw this.hide(),exception}))}setFooter(value){this.showFooter(),this.footerPromise=_jquery.default.Deferred();const footer=this.getFooter();"string"==typeof value?(footer.html(value),this.footerPromise.resolve(footer)):Templates.render(TEMPLATES_LOADING,{}).then((html=>(footer.html(html),value))).then(((html,js)=>(footer.html(html),js&&(this.isAttached?Templates.runTemplateJS(js):this.footerJS=js),footer))).then((footer=>{this.footerPromise.resolve(footer),this.showFooter()})).catch(Notification.exception)}hasFooterContent(){return!!this.getFooter().children().length}hideFooter(){this.getFooter().addClass("hidden")}showFooter(){this.getFooter().removeClass("hidden")}setLarge(){this.isLarge()||this.getModal().addClass("modal-lg")}setVerticallyCentered(){this.isVerticallyCentered()||this.getModal().addClass("modal-dialog-centered")}isLarge(){return this.getModal().hasClass("modal-lg")}isVerticallyCentered(){return this.getModal().hasClass("modal-dialog-centered")}setSmall(){this.isSmall()||this.getModal().removeClass("modal-lg")}isSmall(){return!this.getModal().hasClass("modal-lg")}setScrollable(value){value?this.getModal()[0].classList.add("modal-dialog-scrollable"):this.getModal()[0].classList.remove("modal-dialog-scrollable")}calculateZIndex(){const items=(0,_jquery.default)("".concat(SELECTORS_DIALOG,", ").concat(SELECTORS_MENU_BAR,", ").concat(SELECTORS_HAS_Z_INDEX));let zIndex=parseInt(this.root.css("z-index"));return items.each(((index,item)=>{if(!(item=(0,_jquery.default)(item)).is(":visible"))return;const itemZIndex=item.css("z-index")?parseInt(item.css("z-index")):0;itemZIndex>zIndex&&(zIndex=itemZIndex)})),zIndex}isVisible(){return this.root.hasClass("show")}hasFocus(){const target=(0,_jquery.default)(document.activeElement);return this.root.is(target)||this.root.has(target).length}hasTransitions(){return this.getRoot().hasClass("fade")}getAttachmentPoint(){return(0,_jquery.default)(Fullscreen.getElement()||this.attachmentPoint)}show(){if(this.isVisible())return _jquery.default.Deferred().resolve();const pendingPromise=new _pending.default("core/modal:show");return this.hasFooterContent()?this.showFooter():this.hideFooter(),this.attachToDOM(),!this.focusOnClose&&document.activeElement&&(this.focusOnClose=document.activeElement),this.getBackdrop().then((backdrop=>{const newIndex=this.calculateZIndex()+2,newBackdropIndex=newIndex-1;this.root.css("z-index",newIndex),backdrop.setZIndex(newBackdropIndex),backdrop.show(),this.root.removeClass("hide").addClass("show"),this.accessibilityShow(),this.getModal().focus(),(0,_jquery.default)("body").addClass("modal-open");const scrollbarWidth=window.innerWidth-document.documentElement.clientWidth;(0,_jquery.default)("body").css({overflow:"hidden",paddingRight:"".concat(scrollbarWidth,"px")}),this.root.trigger(_modal_events.default.shown,this),(0,_event_dispatcher.dispatchEvent)("core/modal:shown",this,this.modal[0])})).then(pendingPromise.resolve)}hideIfNotForm(){0==this.modal.find(SELECTORS_FORM).length&&this.hide()}hide(){this.getBackdrop().done((backdrop=>{FocusLock.untrapFocus(),this.countOtherVisibleModals()||(backdrop.hide(),(0,_jquery.default)("body").removeClass("modal-open"),(0,_jquery.default)("body").css({overflow:"",paddingRight:""}));const currentIndex=parseInt(this.root.css("z-index"));this.root.css("z-index",""),backdrop.setZIndex(currentIndex-3),this.accessibilityHide(),this.hasTransitions()?this.getRoot().one("transitionend webkitTransitionEnd oTransitionEnd",(()=>{this.getRoot().removeClass("show").addClass("hide")})):this.getRoot().removeClass("show").addClass("hide"),(0,_jquery.default)(document.body).find(this.getRoot()).length&&(0,_jquery.default)(document.body).append(this.getRoot()),this.getRoot().find('[data-bs-toggle="popover"]').each((function(){var _document$getElementB;null===(_document$getElementB=document.getElementById(this.getAttribute("aria-describedby")))||void 0===_document$getElementB||_document$getElementB.remove()})),this.root.trigger(_modal_events.default.hidden,this)}))}destroy(){this.hide(),(0,_toast.removeToastRegion)(this.getBody().get(0)),this.root.remove(),this.root.trigger(_modal_events.default.destroyed,this),this.attachmentPoint.remove()}accessibilityShow(){Aria.unhide(this.root.get()),Aria.hideSiblings(this.root.get()[0])}accessibilityHide(){Aria.unhideSiblings(this.root.get()[0]),Aria.hide(this.root.get())}registerEventListeners(){this.getRoot().on("keydown",(e=>{this.isVisible()&&e.keyCode==KeyCodes.escape&&(this.removeOnClose?this.destroy():this.hide())})),this.getRoot().click((e=>{if(!(0,_jquery.default)(e.target).closest(SELECTORS_MODAL).length&&(0,_jquery.default)(e.target).closest(SELECTORS_CONTAINER).length){const outsideClickEvent=_jquery.default.Event(_modal_events.default.outsideClick);this.getRoot().trigger(outsideClickEvent,this),outsideClickEvent.isDefaultPrevented()||this.hideIfNotForm()}})),CustomEvents.define(this.getModal(),[CustomEvents.events.activate]),this.getModal().on(CustomEvents.events.activate,SELECTORS_HIDE,((e,data)=>{this.removeOnClose?this.destroy():this.hide(),data.originalEvent.preventDefault()})),this.getRoot().on(_modal_events.default.hidden,(()=>{this.focusOnClose&&this.focusOnClose.focus()}))}registerCloseOnCancel(){this.getModal().on(CustomEvents.events.activate,this.getActionSelector("cancel"),((e,data)=>{const cancelEvent=_jquery.default.Event(_modal_events.default.cancel);this.getRoot().trigger(cancelEvent,this),cancelEvent.isDefaultPrevented()||(data.originalEvent.preventDefault(),this.removeOnClose?this.destroy():this.hide())}))}registerCloseOnSave(){this.getModal().on(CustomEvents.events.activate,this.getActionSelector("save"),((e,data)=>{const saveEvent=_jquery.default.Event(_modal_events.default.save);this.getRoot().trigger(saveEvent,this),saveEvent.isDefaultPrevented()||(data.originalEvent.preventDefault(),this.removeOnClose?this.destroy():this.hide())}))}registerCloseOnDelete(){this.getModal().on(CustomEvents.events.activate,this.getActionSelector("delete"),((e,data)=>{const deleteEvent=_jquery.default.Event(_modal_events.default.delete);this.getRoot().trigger(deleteEvent,this),deleteEvent.isDefaultPrevented()||(data.originalEvent.preventDefault(),this.removeOnClose?this.destroy():this.hide())}))}asyncSet(value,setFunction){return(value=>value instanceof Promise?_jquery.default.when(value):"object"==typeof value&&value.hasOwnProperty("then")?value:_jquery.default.Deferred().resolve(value))(value).then((content=>setFunction(content))).catch(Notification.exception)}setButtonText(action,value){const button=this.getFooter().find(this.getActionSelector(action));if(!button)throw new Error("Unable to find the '"+action+"' button");return this.asyncSet(value,button.text.bind(button))}getActionSelector(action){return"[data-action='"+action+"']"}setRemoveOnClose(remove){this.removeOnClose=remove}setReturnElement(element){this.focusOnClose=element}setButtonDisabled(action,disabled){const button=this.getFooter().find(this.getActionSelector(action));if(!button)throw new Error("Unable to find the '"+action+"' button");disabled?button.attr("disabled",""):button.removeAttr("disabled")}setTemplateJS(js){this.templateJS=js}}return _exports.default=Modal,_defineProperty(Modal,"TYPE","default"),_defineProperty(Modal,"TEMPLATE","core/modal"),_defineProperty(Modal,"backdropPromise",null),_defineProperty(Modal,"modalCounter",0),_defineProperty(Modal,"registry",new Map),_defineProperty(Modal,"register",((type,module,template)=>{const existing=Modal.registry.get(type);existing&&existing.module!==module&&Notification.exception({message:"Modal of type '".concat(type,"' is already registered")}),module&&"function"==typeof module||Notification.exception({message:"You must provide a modal module"}),template||Notification.exception({message:"You must provide a modal template"}),Modal.registry.set(type,{module:module,template:template}),Prefetch.prefetchTemplate(template)})),_exports.default}));
//# sourceMappingURL=modal.min.js.map
\ No newline at end of file
diff --git a/public/lib/amd/build/modal.min.js.map b/public/lib/amd/build/modal.min.js.map
index 5a131880ff5..c2e6178378b 100644
--- a/public/lib/amd/build/modal.min.js.map
+++ b/public/lib/amd/build/modal.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"modal.min.js","sources":["../src/modal.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Contain the logic for modals.\n *\n * @module core/modal\n * @copyright 2016 Ryan Wyllie \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport $ from 'jquery';\nimport * as Templates from 'core/templates';\nimport * as Notification from 'core/notification';\nimport * as KeyCodes from 'core/key_codes';\nimport ModalBackdrop from 'core/modal_backdrop';\nimport ModalEvents from 'core/modal_events';\nimport * as ModalRegistry from 'core/modal_registry';\nimport Pending from 'core/pending';\nimport * as CustomEvents from 'core/custom_interaction_events';\nimport * as FilterEvents from 'core_filters/events';\nimport * as FocusLock from 'core/local/aria/focuslock';\nimport * as Aria from 'core/aria';\nimport * as Fullscreen from 'core/fullscreen';\nimport {removeToastRegion} from './toast';\nimport {dispatchEvent} from 'core/event_dispatcher';\n\n/**\n * A configuration to provide to the modal.\n *\n * @typedef {Object} ModalConfig\n *\n * @property {string} [type] The type of modal to create.\n * @property {string|Promise} [title] The title of the modal.\n * @property {string|Promise} [body] The body of the modal.\n * @property {string|Promise} [footer] The footer of the modal.\n * @property {boolean} [show=false] Whether to show the modal immediately.\n * @property {boolean} [scrollable=true] Whether the modal should be scrollable.\n * @property {boolean} [removeOnClose=true] Whether the modal should be removed from the DOM when it is closed.\n * @property {Element|jQuery} [returnElement] The element to focus when closing the modal.\n * @property {boolean} [large=false] Whether the modal should be a large modal.\n * @property {boolean} [isVerticallyCentered=false] Whether the modal should be vertically centered.\n * @property {object} [buttons={}] The buttons to display in the footer as a key => title pair.\n */\n\nconst SELECTORS = {\n CONTAINER: '[data-region=\"modal-container\"]',\n MODAL: '[data-region=\"modal\"]',\n HEADER: '[data-region=\"header\"]',\n TITLE: '[data-region=\"title\"]',\n BODY: '[data-region=\"body\"]',\n FOOTER: '[data-region=\"footer\"]',\n HIDE: '[data-action=\"hide\"]',\n DIALOG: '[role=dialog]',\n FORM: 'form',\n MENU_BAR: '[role=menubar]',\n HAS_Z_INDEX: '.moodle-has-zindex',\n CAN_RECEIVE_FOCUS: 'input:not([type=\"hidden\"]), a[href], button, textarea, select, [tabindex]',\n};\n\nconst TEMPLATES = {\n LOADING: 'core/loading',\n BACKDROP: 'core/modal_backdrop',\n};\n\nexport default class Modal {\n /** @var {string} The type of modal */\n static TYPE = 'default';\n\n /** @var {string} The template to use for this modal */\n static TEMPLATE = 'core/modal';\n\n /** @var {Promise} Module singleton for the backdrop to be reused by all Modal instances */\n static backdropPromise = null;\n\n /**\n * @var {Number} A counter that gets incremented for each modal created.\n * This can be used to generate unique values for the modals.\n */\n static modalCounter = 0;\n\n /**\n * Getter method for .root element.\n * @return {object} jQuery object\n */\n get root() {\n return $(this._root.filter(SELECTORS.CONTAINER));\n }\n\n /**\n * Setter method for .root element.\n * @param {object} root jQuery object\n */\n set root(root) {\n this._root = root;\n }\n\n /**\n * Constructor for the Modal.\n *\n * @param {HTMLElement} root The HTMLElement at the root of the Modal content\n */\n constructor(root) {\n this.root = $(root);\n\n this.modal = this.root.find(SELECTORS.MODAL);\n this.header = this.modal.find(SELECTORS.HEADER);\n this.headerPromise = $.Deferred();\n this.title = this.header.find(SELECTORS.TITLE);\n this.titlePromise = $.Deferred();\n this.body = this.modal.find(SELECTORS.BODY);\n this.bodyPromise = $.Deferred();\n this.footer = this.modal.find(SELECTORS.FOOTER);\n this.footerPromise = $.Deferred();\n this.hiddenSiblings = [];\n this.isAttached = false;\n this.bodyJS = null;\n this.footerJS = null;\n this.modalCount = Modal.modalCounter++;\n this.attachmentPoint = document.createElement('div');\n document.body.append(this.attachmentPoint);\n this.focusOnClose = null;\n this.templateJS = null;\n\n if (!this.root.is(SELECTORS.CONTAINER)) {\n Notification.exception({message: 'Element is not a modal container'});\n }\n\n if (!this.modal.length) {\n Notification.exception({message: 'Container does not contain a modal'});\n }\n\n if (!this.header.length) {\n Notification.exception({message: 'Modal is missing a header region'});\n }\n\n if (!this.title.length) {\n Notification.exception({message: 'Modal header is missing a title region'});\n }\n\n if (!this.body.length) {\n Notification.exception({message: 'Modal is missing a body region'});\n }\n\n if (!this.footer.length) {\n Notification.exception({message: 'Modal is missing a footer region'});\n }\n\n this.registerEventListeners();\n }\n\n /**\n * Register a modal with the legacy modal registry.\n *\n * This is provided to allow backwards-compatibility with existing code that uses the legacy modal registry.\n * It is not necessary to register modals for code only present in Moodle 4.3 and later.\n */\n static registerModalType() {\n if (!this.TYPE) {\n throw new Error(`Unknown modal type`, this);\n }\n\n if (!this.TEMPLATE) {\n throw new Error(`Unknown modal template`, this);\n }\n ModalRegistry.register(\n this.TYPE,\n this,\n this.TEMPLATE,\n );\n }\n\n /**\n * Create a new modal using the ModalFactory.\n * This is a shortcut to creating the modal.\n * Create a new modal using the supplied configuration.\n *\n * @param {ModalConfig} modalConfig\n * @returns {Promise}\n */\n static async create(modalConfig = {}) {\n const pendingModalPromise = new Pending('core/modal_factory:create');\n modalConfig.type = this.TYPE;\n\n const templateName = this._getTemplateName(modalConfig);\n const templateContext = modalConfig.templateContext || {};\n const {html, js} = await Templates.renderForPromise(templateName, templateContext);\n\n const modal = new this(html);\n if (js) {\n modal.setTemplateJS(js);\n }\n modal.configure(modalConfig);\n\n pendingModalPromise.resolve();\n\n return modal;\n }\n\n /**\n * A helper to get the template name for this modal.\n *\n * @param {ModalConfig} modalConfig\n * @returns {string}\n * @protected\n */\n static _getTemplateName(modalConfig) {\n if (modalConfig.template) {\n return modalConfig.template;\n }\n\n if (this.TEMPLATE) {\n return this.TEMPLATE;\n }\n\n if (ModalRegistry.has(this.TYPE)) {\n // Note: This is provided as an interim backwards-compatability layer and will be removed four releases after 4.3.\n window.console.warning(\n 'Use of core/modal_registry is deprecated. ' +\n 'Please define your modal template in a new static TEMPLATE property on your modal class.',\n );\n const config = ModalRegistry.get(this.TYPE);\n return config.template;\n }\n\n throw new Error(`Unable to determine template name for modal ${this.TYPE}`);\n }\n\n /**\n * Configure the modal.\n *\n * @param {ModalConfig} param0 The configuration options\n */\n configure({\n show = false,\n large = false,\n isVerticallyCentered = false,\n removeOnClose = false,\n scrollable = true,\n returnElement,\n title,\n body,\n footer,\n buttons = {},\n } = {}) {\n if (large) {\n this.setLarge();\n }\n\n if (isVerticallyCentered) {\n this.setVerticallyCentered();\n }\n\n // If configured remove the modal when hiding it.\n // Ideally this should be true, but we need to identify places that this breaks first.\n this.setRemoveOnClose(removeOnClose);\n this.setReturnElement(returnElement);\n this.setScrollable(scrollable);\n\n if (title !== undefined) {\n this.setTitle(title);\n }\n\n if (body !== undefined) {\n this.setBody(body);\n }\n\n if (footer !== undefined) {\n this.setFooter(footer);\n }\n\n Object.entries(buttons).forEach(([key, value]) => this.setButtonText(key, value));\n\n // If configured show the modal.\n if (show) {\n this.show();\n }\n }\n\n /**\n * Attach the modal to the correct part of the page.\n *\n * If it hasn't already been added it runs any\n * javascript that has been cached until now.\n *\n * @method attachToDOM\n */\n attachToDOM() {\n this.getAttachmentPoint().append(this._root);\n\n if (this.isAttached) {\n return;\n }\n\n FocusLock.trapFocus(this.root[0]);\n\n // If we'd cached any JS then we can run it how that the modal is\n // attached to the DOM.\n if (this.templateJS) {\n Templates.runTemplateJS(this.templateJS);\n this.templateJS = null;\n }\n\n if (this.bodyJS) {\n Templates.runTemplateJS(this.bodyJS);\n this.bodyJS = null;\n }\n\n if (this.footerJS) {\n Templates.runTemplateJS(this.footerJS);\n this.footerJS = null;\n }\n\n this.isAttached = true;\n }\n\n /**\n * Count the number of other visible modals (not including this one).\n *\n * @method countOtherVisibleModals\n * @return {int}\n */\n countOtherVisibleModals() {\n let count = 0;\n $('body').find(SELECTORS.CONTAINER).each((index, element) => {\n element = $(element);\n\n // If we haven't found ourself and the element is visible.\n if (!this.root.is(element) && element.hasClass('show')) {\n count++;\n }\n });\n\n return count;\n }\n\n /**\n * Get the modal backdrop.\n *\n * @method getBackdrop\n * @return {object} jQuery promise\n */\n getBackdrop() {\n if (!Modal.backdropPromise) {\n Modal.backdropPromise = Templates.render(TEMPLATES.BACKDROP, {})\n .then((html) => new ModalBackdrop($(html)))\n .catch(Notification.exception);\n }\n\n return Modal.backdropPromise;\n }\n\n /**\n * Get the root element of this modal.\n *\n * @method getRoot\n * @return {object} jQuery object\n */\n getRoot() {\n return this.root;\n }\n\n /**\n * Get the modal element of this modal.\n *\n * @method getModal\n * @return {object} jQuery object\n */\n getModal() {\n return this.modal;\n }\n\n /**\n * Get the modal title element.\n *\n * @method getTitle\n * @return {object} jQuery object\n */\n getTitle() {\n return this.title;\n }\n\n /**\n * Get the modal body element.\n *\n * @method getBody\n * @return {object} jQuery object\n */\n getBody() {\n return this.body;\n }\n\n /**\n * Get the modal footer element.\n *\n * @method getFooter\n * @return {object} jQuery object\n */\n getFooter() {\n return this.footer;\n }\n\n /**\n * Get a promise resolving to the title region.\n *\n * @method getTitlePromise\n * @return {Promise}\n */\n getTitlePromise() {\n return this.titlePromise;\n }\n\n /**\n * Get a promise resolving to the body region.\n *\n * @method getBodyPromise\n * @return {object} jQuery object\n */\n getBodyPromise() {\n return this.bodyPromise;\n }\n\n /**\n * Get a promise resolving to the footer region.\n *\n * @method getFooterPromise\n * @return {object} jQuery object\n */\n getFooterPromise() {\n return this.footerPromise;\n }\n\n /**\n * Get the unique modal count.\n *\n * @method getModalCount\n * @return {int}\n */\n getModalCount() {\n return this.modalCount;\n }\n\n /**\n * Set the modal title element.\n *\n * This method is overloaded to take either a string value for the title or a jQuery promise that is resolved with\n * HTML most commonly from a Str.get_string call.\n *\n * @method setTitle\n * @param {(string|object)} value The title string or jQuery promise which resolves to the title.\n */\n setTitle(value) {\n const title = this.getTitle();\n this.titlePromise = $.Deferred();\n\n this.asyncSet(value, title.html.bind(title))\n .then(() => {\n this.titlePromise.resolve(title);\n return;\n })\n .catch(Notification.exception);\n }\n\n /**\n * Set the modal body element.\n *\n * This method is overloaded to take either a string value for the body or a jQuery promise that is resolved with\n * HTML and Javascript most commonly from a Templates.render call.\n *\n * @method setBody\n * @param {(string|object)} value The body string or jQuery promise which resolves to the body.\n * @fires event:filterContentUpdated\n */\n setBody(value) {\n this.bodyPromise = $.Deferred();\n\n const body = this.getBody();\n\n if (typeof value === 'string') {\n // Just set the value if it's a string.\n body.html(value);\n FilterEvents.notifyFilterContentUpdated(body);\n this.getRoot().trigger(ModalEvents.bodyRendered, this);\n this.bodyPromise.resolve(body);\n } else {\n const modalPromise = new Pending(`amd-modal-js-pending-id-${this.getModalCount()}`);\n // Otherwise we assume it's a promise to be resolved with\n // html and javascript.\n let contentPromise = null;\n body.css('overflow', 'hidden');\n\n // Ensure that the `value` is a jQuery Promise.\n value = $.when(value);\n\n if (value.state() == 'pending') {\n // We're still waiting for the body promise to resolve so\n // let's show a loading icon.\n let height = body.innerHeight();\n if (height < 100) {\n height = 100;\n }\n\n body.animate({height: `${height}px`}, 150);\n\n body.html('');\n contentPromise = Templates.render(TEMPLATES.LOADING, {})\n .then((html) => {\n const loadingIcon = $(html).hide();\n body.html(loadingIcon);\n loadingIcon.fadeIn(150);\n\n // We only want the loading icon to fade out\n // when the content for the body has finished\n // loading.\n return $.when(loadingIcon.promise(), value);\n })\n .then((loadingIcon) => {\n // Once the content has finished loading and\n // the loading icon has been shown then we can\n // fade the icon away to reveal the content.\n return loadingIcon.fadeOut(100).promise();\n })\n .then(() => {\n return value;\n });\n } else {\n // The content is already loaded so let's just display\n // it to the user. No need for a loading icon.\n contentPromise = value;\n }\n\n // Now we can actually display the content.\n contentPromise.then((html, js) => {\n let result = null;\n\n if (this.isVisible()) {\n // If the modal is visible then we should display\n // the content gracefully for the user.\n body.css('opacity', 0);\n const currentHeight = body.innerHeight();\n body.html(html);\n // We need to clear any height values we've set here\n // in order to measure the height of the content being\n // added. This then allows us to animate the height\n // transition.\n body.css('height', '');\n const newHeight = body.innerHeight();\n body.css('height', `${currentHeight}px`);\n result = body.animate(\n {height: `${newHeight}px`, opacity: 1},\n {duration: 150, queue: false}\n ).promise();\n } else {\n // Since the modal isn't visible we can just immediately\n // set the content. No need to animate it.\n body.html(html);\n }\n\n if (js) {\n if (this.isAttached) {\n // If we're in the DOM then run the JS immediately.\n Templates.runTemplateJS(js);\n } else {\n // Otherwise cache it to be run when we're attached.\n this.bodyJS = js;\n }\n }\n\n return result;\n })\n .then((result) => {\n FilterEvents.notifyFilterContentUpdated(body);\n this.getRoot().trigger(ModalEvents.bodyRendered, this);\n dispatchEvent('core/modal:bodyRendered', this, this.modal[0]);\n return result;\n })\n .then(() => {\n this.bodyPromise.resolve(body);\n return;\n })\n .catch(Notification.exception)\n .always(() => {\n // When we're done displaying all of the content we need\n // to clear the custom values we've set here.\n body.css('height', '');\n body.css('overflow', '');\n body.css('opacity', '');\n modalPromise.resolve();\n\n return;\n });\n }\n }\n\n /**\n * Alternative to setBody() that can be used from non-Jquery modules\n *\n * @param {Promise} promise promise that returns {html, js} object\n * @return {Promise}\n */\n setBodyContent(promise) {\n // Call the leegacy API for now and pass it a jQuery Promise.\n // This is a non-spec feature of jQuery and cannot be produced with spec promises.\n // We can encourage people to migrate to this approach, and in future we can swap\n // it so that setBody() calls setBodyPromise().\n return promise.then(({html, js}) => this.setBody($.when(html, js)))\n .catch(exception => {\n this.hide();\n throw exception;\n });\n }\n\n /**\n * Set the modal footer element. The footer element is made visible, if it\n * isn't already.\n *\n * This method is overloaded to take either a string\n * value for the body or a jQuery promise that is resolved with HTML and Javascript\n * most commonly from a Templates.render call.\n *\n * @method setFooter\n * @param {(string|object)} value The footer string or jQuery promise\n */\n setFooter(value) {\n // Make sure the footer is visible.\n this.showFooter();\n this.footerPromise = $.Deferred();\n\n const footer = this.getFooter();\n\n if (typeof value === 'string') {\n // Just set the value if it's a string.\n footer.html(value);\n this.footerPromise.resolve(footer);\n } else {\n // Otherwise we assume it's a promise to be resolved with\n // html and javascript.\n Templates.render(TEMPLATES.LOADING, {})\n .then((html) => {\n footer.html(html);\n\n return value;\n })\n .then((html, js) => {\n footer.html(html);\n\n if (js) {\n if (this.isAttached) {\n // If we're in the DOM then run the JS immediately.\n Templates.runTemplateJS(js);\n } else {\n // Otherwise cache it to be run when we're attached.\n this.footerJS = js;\n }\n }\n\n return footer;\n })\n .then((footer) => {\n this.footerPromise.resolve(footer);\n this.showFooter();\n return;\n })\n .catch(Notification.exception);\n }\n }\n\n /**\n * Check if the footer has any content in it.\n *\n * @method hasFooterContent\n * @return {bool}\n */\n hasFooterContent() {\n return this.getFooter().children().length ? true : false;\n }\n\n /**\n * Hide the footer element.\n *\n * @method hideFooter\n */\n hideFooter() {\n this.getFooter().addClass('hidden');\n }\n\n /**\n * Show the footer element.\n *\n * @method showFooter\n */\n showFooter() {\n this.getFooter().removeClass('hidden');\n }\n\n /**\n * Mark the modal as a large modal.\n *\n * @method setLarge\n */\n setLarge() {\n if (this.isLarge()) {\n return;\n }\n\n this.getModal().addClass('modal-lg');\n }\n\n /**\n * Mark the modal as a centered modal.\n *\n * @method setVerticallyCentered\n */\n setVerticallyCentered() {\n if (this.isVerticallyCentered()) {\n return;\n }\n this.getModal().addClass('modal-dialog-centered');\n }\n\n /**\n * Check if the modal is a large modal.\n *\n * @method isLarge\n * @return {bool}\n */\n isLarge() {\n return this.getModal().hasClass('modal-lg');\n }\n\n /**\n * Check if the modal is vertically centered.\n *\n * @method isVerticallyCentered\n * @return {bool}\n */\n isVerticallyCentered() {\n return this.getModal().hasClass('modal-dialog-centered');\n }\n\n /**\n * Mark the modal as a small modal.\n *\n * @method setSmall\n */\n setSmall() {\n if (this.isSmall()) {\n return;\n }\n\n this.getModal().removeClass('modal-lg');\n }\n\n /**\n * Check if the modal is a small modal.\n *\n * @method isSmall\n * @return {bool}\n */\n isSmall() {\n return !this.getModal().hasClass('modal-lg');\n }\n\n /**\n * Set this modal to be scrollable or not.\n *\n * @method setScrollable\n * @param {bool} value Whether the modal is scrollable or not\n */\n setScrollable(value) {\n if (!value) {\n this.getModal()[0].classList.remove('modal-dialog-scrollable');\n return;\n }\n\n this.getModal()[0].classList.add('modal-dialog-scrollable');\n }\n\n\n /**\n * Determine the highest z-index value currently on the page.\n *\n * @method calculateZIndex\n * @return {int}\n */\n calculateZIndex() {\n const items = $(`${SELECTORS.DIALOG}, ${SELECTORS.MENU_BAR}, ${SELECTORS.HAS_Z_INDEX}`);\n let zIndex = parseInt(this.root.css('z-index'));\n\n items.each((index, item) => {\n item = $(item);\n if (!item.is(':visible')) {\n // Do not include items which are not visible in the z-index calculation.\n // This is important because some dialogues are not removed from the DOM.\n return;\n }\n // Note that webkit browsers won't return the z-index value from the CSS stylesheet\n // if the element doesn't have a position specified. Instead it'll return \"auto\".\n const itemZIndex = item.css('z-index') ? parseInt(item.css('z-index')) : 0;\n\n if (itemZIndex > zIndex) {\n zIndex = itemZIndex;\n }\n });\n\n return zIndex;\n }\n\n /**\n * Check if this modal is visible.\n *\n * @method isVisible\n * @return {bool}\n */\n isVisible() {\n return this.root.hasClass('show');\n }\n\n /**\n * Check if this modal has focus.\n *\n * @method hasFocus\n * @return {bool}\n */\n hasFocus() {\n const target = $(document.activeElement);\n return this.root.is(target) || this.root.has(target).length;\n }\n\n /**\n * Check if this modal has CSS transitions applied.\n *\n * @method hasTransitions\n * @return {bool}\n */\n hasTransitions() {\n return this.getRoot().hasClass('fade');\n }\n\n /**\n * Gets the jQuery wrapped node that the Modal should be attached to.\n *\n * @returns {jQuery}\n */\n getAttachmentPoint() {\n return $(Fullscreen.getElement() || this.attachmentPoint);\n }\n\n /**\n * Display this modal. The modal will be attached to the DOM if it hasn't\n * already been.\n *\n * @method show\n * @returns {Promise}\n */\n show() {\n if (this.isVisible()) {\n return $.Deferred().resolve();\n }\n\n const pendingPromise = new Pending('core/modal:show');\n\n if (this.hasFooterContent()) {\n this.showFooter();\n } else {\n this.hideFooter();\n }\n\n this.attachToDOM();\n\n // If the focusOnClose was not set. Set the focus back to triggered element.\n if (!this.focusOnClose && document.activeElement) {\n this.focusOnClose = document.activeElement;\n }\n\n return this.getBackdrop()\n .then((backdrop) => {\n const currentIndex = this.calculateZIndex();\n const newIndex = currentIndex + 2;\n const newBackdropIndex = newIndex - 1;\n this.root.css('z-index', newIndex);\n backdrop.setZIndex(newBackdropIndex);\n backdrop.show();\n\n this.root.removeClass('hide').addClass('show');\n this.accessibilityShow();\n this.getModal().focus();\n $('body').addClass('modal-open');\n const scrollbarWidth = window.innerWidth - document.documentElement.clientWidth;\n $('body').css({overflow: \"hidden\", paddingRight: `${scrollbarWidth}px`});\n this.root.trigger(ModalEvents.shown, this);\n dispatchEvent('core/modal:shown', this, this.modal[0]);\n\n return;\n })\n .then(pendingPromise.resolve);\n }\n\n /**\n * Hide this modal if it does not contain a form.\n *\n * @method hideIfNotForm\n */\n hideIfNotForm() {\n const formElement = this.modal.find(SELECTORS.FORM);\n if (formElement.length == 0) {\n this.hide();\n }\n }\n\n /**\n * Hide this modal.\n *\n * @method hide\n */\n hide() {\n this.getBackdrop().done((backdrop) => {\n FocusLock.untrapFocus();\n\n if (!this.countOtherVisibleModals()) {\n // Hide the backdrop if we're the last open modal.\n backdrop.hide();\n $('body').removeClass('modal-open');\n $('body').css({overflow: \"\", paddingRight: \"\"});\n }\n\n const currentIndex = parseInt(this.root.css('z-index'));\n this.root.css('z-index', '');\n backdrop.setZIndex(currentIndex - 3);\n\n this.accessibilityHide();\n\n if (this.hasTransitions()) {\n // Wait for CSS transitions to complete before hiding the element.\n this.getRoot().one('transitionend webkitTransitionEnd oTransitionEnd', () => {\n this.getRoot().removeClass('show').addClass('hide');\n });\n } else {\n this.getRoot().removeClass('show').addClass('hide');\n }\n\n // Ensure the modal is moved onto the body node if it is still attached to the DOM.\n if ($(document.body).find(this.getRoot()).length) {\n $(document.body).append(this.getRoot());\n }\n\n // Closes popover elements that are inside the modal at the time the modal is closed.\n this.getRoot().find('[data-bs-toggle=\"popover\"]').each(function() {\n document.getElementById(this.getAttribute('aria-describedby'))?.remove();\n });\n\n this.root.trigger(ModalEvents.hidden, this);\n });\n }\n\n /**\n * Remove this modal from the DOM.\n *\n * @method destroy\n */\n destroy() {\n this.hide();\n removeToastRegion(this.getBody().get(0));\n this.root.remove();\n this.root.trigger(ModalEvents.destroyed, this);\n this.attachmentPoint.remove();\n }\n\n /**\n * Sets the appropriate aria attributes on this dialogue and the other\n * elements in the DOM to ensure that screen readers are able to navigate\n * the dialogue popup correctly.\n *\n * @method accessibilityShow\n */\n accessibilityShow() {\n // Make us visible to screen readers.\n Aria.unhide(this.root.get());\n\n // Hide siblings.\n Aria.hideSiblings(this.root.get()[0]);\n }\n\n /**\n * Restores the aria visibility on the DOM elements changed when displaying\n * the dialogue popup and makes the dialogue aria hidden to allow screen\n * readers to navigate the main page correctly when the dialogue is closed.\n *\n * @method accessibilityHide\n */\n accessibilityHide() {\n // Unhide siblings.\n Aria.unhideSiblings(this.root.get()[0]);\n\n // Hide this modal.\n Aria.hide(this.root.get());\n }\n\n /**\n * Set up all of the event handling for the modal.\n *\n * @method registerEventListeners\n */\n registerEventListeners() {\n this.getRoot().on('keydown', (e) => {\n if (!this.isVisible()) {\n return;\n }\n\n if (e.keyCode == KeyCodes.escape) {\n if (this.removeOnClose) {\n this.destroy();\n } else {\n this.hide();\n }\n }\n });\n\n // Listen for clicks on the modal container.\n this.getRoot().click((e) => {\n // If the click wasn't inside the modal element then we should\n // hide the modal.\n if (!$(e.target).closest(SELECTORS.MODAL).length) {\n // The check above fails to detect the click was inside the modal when the DOM tree is already changed.\n // So, we check if we can still find the container element or not. If not, then the DOM tree is changed.\n // It's best not to hide the modal in that case.\n if ($(e.target).closest(SELECTORS.CONTAINER).length) {\n const outsideClickEvent = $.Event(ModalEvents.outsideClick);\n this.getRoot().trigger(outsideClickEvent, this);\n\n if (!outsideClickEvent.isDefaultPrevented()) {\n this.hideIfNotForm();\n }\n }\n }\n });\n\n CustomEvents.define(this.getModal(), [CustomEvents.events.activate]);\n this.getModal().on(CustomEvents.events.activate, SELECTORS.HIDE, (e, data) => {\n if (this.removeOnClose) {\n this.destroy();\n } else {\n this.hide();\n }\n data.originalEvent.preventDefault();\n });\n\n this.getRoot().on(ModalEvents.hidden, () => {\n if (this.focusOnClose) {\n // Focus on the element that actually triggers the modal.\n this.focusOnClose.focus();\n }\n });\n }\n\n /**\n * Register a listener to close the dialogue when the cancel button is pressed.\n *\n * @method registerCloseOnCancel\n */\n registerCloseOnCancel() {\n // Handle the clicking of the Cancel button.\n this.getModal().on(CustomEvents.events.activate, this.getActionSelector('cancel'), (e, data) => {\n const cancelEvent = $.Event(ModalEvents.cancel);\n this.getRoot().trigger(cancelEvent, this);\n\n if (!cancelEvent.isDefaultPrevented()) {\n data.originalEvent.preventDefault();\n\n if (this.removeOnClose) {\n this.destroy();\n } else {\n this.hide();\n }\n }\n });\n }\n\n /**\n * Register a listener to close the dialogue when the save button is pressed.\n *\n * @method registerCloseOnSave\n */\n registerCloseOnSave() {\n // Handle the clicking of the Cancel button.\n this.getModal().on(CustomEvents.events.activate, this.getActionSelector('save'), (e, data) => {\n const saveEvent = $.Event(ModalEvents.save);\n this.getRoot().trigger(saveEvent, this);\n\n if (!saveEvent.isDefaultPrevented()) {\n data.originalEvent.preventDefault();\n\n if (this.removeOnClose) {\n this.destroy();\n } else {\n this.hide();\n }\n }\n });\n }\n\n\n /**\n * Register a listener to close the dialogue when the delete button is pressed.\n *\n * @method registerCloseOnDelete\n */\n registerCloseOnDelete() {\n // Handle the clicking of the Cancel button.\n this.getModal().on(CustomEvents.events.activate, this.getActionSelector('delete'), (e, data) => {\n const deleteEvent = $.Event(ModalEvents.delete);\n this.getRoot().trigger(deleteEvent, this);\n\n if (!deleteEvent.isDefaultPrevented()) {\n data.originalEvent.preventDefault();\n\n if (this.removeOnClose) {\n this.destroy();\n } else {\n this.hide();\n }\n }\n });\n }\n\n /**\n * Set or resolve and set the value using the function.\n *\n * @method asyncSet\n * @param {(string|object)} value The string or jQuery promise.\n * @param {function} setFunction The setter\n * @return {Promise}\n */\n asyncSet(value, setFunction) {\n const getWrappedValue = (value) => {\n if (value instanceof Promise) {\n return $.when(value);\n }\n\n if (typeof value !== 'object' || !value.hasOwnProperty('then')) {\n return $.Deferred().resolve(value);\n }\n\n return value;\n };\n\n return getWrappedValue(value)\n .then((content) => setFunction(content))\n .catch(Notification.exception);\n }\n\n /**\n * Set the title text of a button.\n *\n * This method is overloaded to take either a string value for the button title or a jQuery promise that is resolved with\n * text most commonly from a Str.get_string call.\n *\n * @param {DOMString} action The action of the button\n * @param {(String|object)} value The button text, or a promise which will resolve to it\n * @returns {Promise}\n */\n setButtonText(action, value) {\n const button = this.getFooter().find(this.getActionSelector(action));\n\n if (!button) {\n throw new Error(\"Unable to find the '\" + action + \"' button\");\n }\n\n return this.asyncSet(value, button.text.bind(button));\n }\n\n /**\n * Get the Selector for an action.\n *\n * @param {String} action\n * @returns {DOMString}\n */\n getActionSelector(action) {\n return \"[data-action='\" + action + \"']\";\n }\n\n /**\n * Set the flag to remove the modal from the DOM on close.\n *\n * @param {Boolean} remove\n */\n setRemoveOnClose(remove) {\n this.removeOnClose = remove;\n }\n\n /**\n * Set the return element for the modal.\n *\n * @param {Element|jQuery} element Element to focus when the modal is closed\n */\n setReturnElement(element) {\n this.focusOnClose = element;\n }\n\n /**\n * Set the a button enabled or disabled.\n *\n * @param {DOMString} action The action of the button\n * @param {Boolean} disabled the new disabled value\n */\n setButtonDisabled(action, disabled) {\n const button = this.getFooter().find(this.getActionSelector(action));\n\n if (!button) {\n throw new Error(\"Unable to find the '\" + action + \"' button\");\n }\n if (disabled) {\n button.attr('disabled', '');\n } else {\n button.removeAttr('disabled');\n }\n }\n\n /**\n * Set the template JS for this modal.\n * @param {String} js The JavaScript to run when the modal is attached to the DOM.\n */\n setTemplateJS(js) {\n this.templateJS = js;\n }\n}\n"],"names":["SELECTORS","TEMPLATES","Modal","root","this","_root","filter","constructor","modal","find","header","headerPromise","$","Deferred","title","titlePromise","body","bodyPromise","footer","footerPromise","hiddenSiblings","isAttached","bodyJS","footerJS","modalCount","modalCounter","attachmentPoint","document","createElement","append","focusOnClose","templateJS","is","Notification","exception","message","length","registerEventListeners","TYPE","Error","TEMPLATE","ModalRegistry","register","modalConfig","pendingModalPromise","Pending","type","templateName","_getTemplateName","templateContext","html","js","Templates","renderForPromise","setTemplateJS","configure","resolve","template","has","window","console","warning","get","show","large","isVerticallyCentered","removeOnClose","scrollable","returnElement","buttons","setLarge","setVerticallyCentered","setRemoveOnClose","setReturnElement","setScrollable","undefined","setTitle","setBody","setFooter","Object","entries","forEach","_ref","key","value","setButtonText","attachToDOM","getAttachmentPoint","FocusLock","trapFocus","runTemplateJS","countOtherVisibleModals","count","each","index","element","hasClass","getBackdrop","backdropPromise","render","then","ModalBackdrop","catch","getRoot","getModal","getTitle","getBody","getFooter","getTitlePromise","getBodyPromise","getFooterPromise","getModalCount","asyncSet","bind","FilterEvents","notifyFilterContentUpdated","trigger","ModalEvents","bodyRendered","modalPromise","contentPromise","css","when","state","height","innerHeight","animate","loadingIcon","hide","fadeIn","promise","fadeOut","result","isVisible","currentHeight","newHeight","opacity","duration","queue","always","setBodyContent","_ref2","showFooter","hasFooterContent","children","hideFooter","addClass","removeClass","isLarge","setSmall","isSmall","classList","add","remove","calculateZIndex","items","zIndex","parseInt","item","itemZIndex","hasFocus","target","activeElement","hasTransitions","Fullscreen","getElement","pendingPromise","backdrop","newIndex","newBackdropIndex","setZIndex","accessibilityShow","focus","scrollbarWidth","innerWidth","documentElement","clientWidth","overflow","paddingRight","shown","hideIfNotForm","done","untrapFocus","currentIndex","accessibilityHide","one","getElementById","getAttribute","hidden","destroy","destroyed","Aria","unhide","hideSiblings","unhideSiblings","on","e","keyCode","KeyCodes","escape","click","closest","outsideClickEvent","Event","outsideClick","isDefaultPrevented","CustomEvents","define","events","activate","data","originalEvent","preventDefault","registerCloseOnCancel","getActionSelector","cancelEvent","cancel","registerCloseOnSave","saveEvent","save","registerCloseOnDelete","deleteEvent","delete","setFunction","Promise","hasOwnProperty","getWrappedValue","content","action","button","text","setButtonDisabled","disabled","attr","removeAttr"],"mappings":"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"}
\ No newline at end of file
+{"version":3,"file":"modal.min.js","sources":["../src/modal.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Contain the logic for modals.\n *\n * @module core/modal\n * @copyright 2016 Ryan Wyllie \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport $ from 'jquery';\nimport * as Templates from 'core/templates';\nimport * as Notification from 'core/notification';\nimport * as KeyCodes from 'core/key_codes';\nimport ModalBackdrop from 'core/modal_backdrop';\nimport ModalEvents from 'core/modal_events';\nimport Pending from 'core/pending';\nimport * as CustomEvents from 'core/custom_interaction_events';\nimport * as FilterEvents from 'core_filters/events';\nimport * as FocusLock from 'core/local/aria/focuslock';\nimport * as Aria from 'core/aria';\nimport * as Fullscreen from 'core/fullscreen';\nimport {removeToastRegion} from './toast';\nimport {dispatchEvent} from 'core/event_dispatcher';\nimport * as Prefetch from 'core/prefetch';\n\n/**\n * A configuration to provide to the modal.\n *\n * @typedef {Object} ModalConfig\n *\n * @property {string} [type] The type of modal to create.\n * @property {string|Promise} [title] The title of the modal.\n * @property {string|Promise} [body] The body of the modal.\n * @property {string|Promise} [footer] The footer of the modal.\n * @property {boolean} [show=false] Whether to show the modal immediately.\n * @property {boolean} [scrollable=true] Whether the modal should be scrollable.\n * @property {boolean} [removeOnClose=true] Whether the modal should be removed from the DOM when it is closed.\n * @property {Element|jQuery} [returnElement] The element to focus when closing the modal.\n * @property {boolean} [large=false] Whether the modal should be a large modal.\n * @property {boolean} [isVerticallyCentered=false] Whether the modal should be vertically centered.\n * @property {object} [buttons={}] The buttons to display in the footer as a key => title pair.\n */\n\nconst SELECTORS = {\n CONTAINER: '[data-region=\"modal-container\"]',\n MODAL: '[data-region=\"modal\"]',\n HEADER: '[data-region=\"header\"]',\n TITLE: '[data-region=\"title\"]',\n BODY: '[data-region=\"body\"]',\n FOOTER: '[data-region=\"footer\"]',\n HIDE: '[data-action=\"hide\"]',\n DIALOG: '[role=dialog]',\n FORM: 'form',\n MENU_BAR: '[role=menubar]',\n HAS_Z_INDEX: '.moodle-has-zindex',\n CAN_RECEIVE_FOCUS: 'input:not([type=\"hidden\"]), a[href], button, textarea, select, [tabindex]',\n};\n\nconst TEMPLATES = {\n LOADING: 'core/loading',\n BACKDROP: 'core/modal_backdrop',\n};\n\nexport default class Modal {\n /** @var {string} The type of modal */\n static TYPE = 'default';\n\n /** @var {string} The template to use for this modal */\n static TEMPLATE = 'core/modal';\n\n /** @var {Promise} Module singleton for the backdrop to be reused by all Modal instances */\n static backdropPromise = null;\n\n /**\n * @var {Number} A counter that gets incremented for each modal created.\n * This can be used to generate unique values for the modals.\n */\n static modalCounter = 0;\n\n /**\n * @var {Number} A singleton registry for all modules to access. Allows types to be\n * added at runtime.\n */\n static registry = new Map();\n\n /**\n * Getter method for .root element.\n * @return {object} jQuery object\n */\n get root() {\n return $(this._root.filter(SELECTORS.CONTAINER));\n }\n\n /**\n * Setter method for .root element.\n * @param {object} root jQuery object\n */\n set root(root) {\n this._root = root;\n }\n\n /**\n * Constructor for the Modal.\n *\n * @param {HTMLElement} root The HTMLElement at the root of the Modal content\n */\n constructor(root) {\n this.root = $(root);\n\n this.modal = this.root.find(SELECTORS.MODAL);\n this.header = this.modal.find(SELECTORS.HEADER);\n this.headerPromise = $.Deferred();\n this.title = this.header.find(SELECTORS.TITLE);\n this.titlePromise = $.Deferred();\n this.body = this.modal.find(SELECTORS.BODY);\n this.bodyPromise = $.Deferred();\n this.footer = this.modal.find(SELECTORS.FOOTER);\n this.footerPromise = $.Deferred();\n this.hiddenSiblings = [];\n this.isAttached = false;\n this.bodyJS = null;\n this.footerJS = null;\n this.modalCount = Modal.modalCounter++;\n this.attachmentPoint = document.createElement('div');\n document.body.append(this.attachmentPoint);\n this.focusOnClose = null;\n this.templateJS = null;\n\n if (!this.root.is(SELECTORS.CONTAINER)) {\n Notification.exception({message: 'Element is not a modal container'});\n }\n\n if (!this.modal.length) {\n Notification.exception({message: 'Container does not contain a modal'});\n }\n\n if (!this.header.length) {\n Notification.exception({message: 'Modal is missing a header region'});\n }\n\n if (!this.title.length) {\n Notification.exception({message: 'Modal header is missing a title region'});\n }\n\n if (!this.body.length) {\n Notification.exception({message: 'Modal is missing a body region'});\n }\n\n if (!this.footer.length) {\n Notification.exception({message: 'Modal is missing a footer region'});\n }\n\n this.registerEventListeners();\n }\n\n /**\n * Register a modal with the legacy modal registry.\n *\n * This is provided to allow backwards-compatibility with existing code that uses the legacy modal registry.\n * It is not necessary to register modals for code only present in Moodle 4.3 and later.\n */\n static registerModalType() {\n if (!this.TYPE) {\n throw new Error(`Unknown modal type`, this);\n }\n\n if (!this.TEMPLATE) {\n throw new Error(`Unknown modal template`, this);\n }\n\n this.register(\n this.TYPE,\n this,\n this.TEMPLATE,\n );\n }\n\n /**\n * Register a modal.\n *\n * @param {string} type The type of modal (must be unique)\n * @param {function} module The modal module (must be a constructor function of type core/modal)\n * @param {string} template The template name of the modal\n */\n static register = (type, module, template) => {\n const existing = this.registry.get(type);\n if (existing && existing.module !== module) {\n Notification.exception({\n message: `Modal of type '${type}' is already registered`,\n });\n }\n\n if (!module || typeof module !== 'function') {\n Notification.exception({message: \"You must provide a modal module\"});\n }\n\n if (!template) {\n Notification.exception({message: \"You must provide a modal template\"});\n }\n\n this.registry.set(type, {module, template});\n\n // Prefetch the template.\n Prefetch.prefetchTemplate(template);\n };\n\n /**\n * Create a new modal using the ModalFactory.\n * This is a shortcut to creating the modal.\n * Create a new modal using the supplied configuration.\n *\n * @param {ModalConfig} modalConfig\n * @returns {Promise}\n */\n static async create(modalConfig = {}) {\n const pendingModalPromise = new Pending('core/modal:create');\n modalConfig.type = this.TYPE;\n\n const templateName = this._getTemplateName(modalConfig);\n const templateContext = modalConfig.templateContext || {};\n const {html, js} = await Templates.renderForPromise(templateName, templateContext);\n\n const modal = new this(html);\n if (js) {\n modal.setTemplateJS(js);\n }\n modal.configure(modalConfig);\n\n pendingModalPromise.resolve();\n\n return modal;\n }\n\n /**\n * A helper to get the template name for this modal.\n *\n * @param {ModalConfig} modalConfig\n * @returns {string}\n * @protected\n */\n static _getTemplateName(modalConfig) {\n if (modalConfig.template) {\n return modalConfig.template;\n }\n\n if (this.TEMPLATE) {\n return this.TEMPLATE;\n }\n\n if (this.registry.has(this.TYPE)) {\n const config = this.registry.get(this.TYPE);\n return config.template;\n }\n\n throw new Error(`Unable to determine template name for modal ${this.TYPE}`);\n }\n\n /**\n * Configure the modal.\n *\n * @param {ModalConfig} param0 The configuration options\n */\n configure({\n show = false,\n large = false,\n isVerticallyCentered = false,\n removeOnClose = false,\n scrollable = true,\n returnElement,\n title,\n body,\n footer,\n buttons = {},\n } = {}) {\n if (large) {\n this.setLarge();\n }\n\n if (isVerticallyCentered) {\n this.setVerticallyCentered();\n }\n\n // If configured remove the modal when hiding it.\n // Ideally this should be true, but we need to identify places that this breaks first.\n this.setRemoveOnClose(removeOnClose);\n this.setReturnElement(returnElement);\n this.setScrollable(scrollable);\n\n if (title !== undefined) {\n this.setTitle(title);\n }\n\n if (body !== undefined) {\n this.setBody(body);\n }\n\n if (footer !== undefined) {\n this.setFooter(footer);\n }\n\n Object.entries(buttons).forEach(([key, value]) => this.setButtonText(key, value));\n\n // If configured show the modal.\n if (show) {\n this.show();\n }\n }\n\n /**\n * Attach the modal to the correct part of the page.\n *\n * If it hasn't already been added it runs any\n * javascript that has been cached until now.\n *\n * @method attachToDOM\n */\n attachToDOM() {\n this.getAttachmentPoint().append(this._root);\n\n if (this.isAttached) {\n return;\n }\n\n FocusLock.trapFocus(this.root[0]);\n\n // If we'd cached any JS then we can run it how that the modal is\n // attached to the DOM.\n if (this.templateJS) {\n Templates.runTemplateJS(this.templateJS);\n this.templateJS = null;\n }\n\n if (this.bodyJS) {\n Templates.runTemplateJS(this.bodyJS);\n this.bodyJS = null;\n }\n\n if (this.footerJS) {\n Templates.runTemplateJS(this.footerJS);\n this.footerJS = null;\n }\n\n this.isAttached = true;\n }\n\n /**\n * Count the number of other visible modals (not including this one).\n *\n * @method countOtherVisibleModals\n * @return {int}\n */\n countOtherVisibleModals() {\n let count = 0;\n $('body').find(SELECTORS.CONTAINER).each((index, element) => {\n element = $(element);\n\n // If we haven't found ourself and the element is visible.\n if (!this.root.is(element) && element.hasClass('show')) {\n count++;\n }\n });\n\n return count;\n }\n\n /**\n * Get the modal backdrop.\n *\n * @method getBackdrop\n * @return {object} jQuery promise\n */\n getBackdrop() {\n if (!Modal.backdropPromise) {\n Modal.backdropPromise = Templates.render(TEMPLATES.BACKDROP, {})\n .then((html) => new ModalBackdrop($(html)))\n .catch(Notification.exception);\n }\n\n return Modal.backdropPromise;\n }\n\n /**\n * Get the root element of this modal.\n *\n * @method getRoot\n * @return {object} jQuery object\n */\n getRoot() {\n return this.root;\n }\n\n /**\n * Get the modal element of this modal.\n *\n * @method getModal\n * @return {object} jQuery object\n */\n getModal() {\n return this.modal;\n }\n\n /**\n * Get the modal title element.\n *\n * @method getTitle\n * @return {object} jQuery object\n */\n getTitle() {\n return this.title;\n }\n\n /**\n * Get the modal body element.\n *\n * @method getBody\n * @return {object} jQuery object\n */\n getBody() {\n return this.body;\n }\n\n /**\n * Get the modal footer element.\n *\n * @method getFooter\n * @return {object} jQuery object\n */\n getFooter() {\n return this.footer;\n }\n\n /**\n * Get a promise resolving to the title region.\n *\n * @method getTitlePromise\n * @return {Promise}\n */\n getTitlePromise() {\n return this.titlePromise;\n }\n\n /**\n * Get a promise resolving to the body region.\n *\n * @method getBodyPromise\n * @return {object} jQuery object\n */\n getBodyPromise() {\n return this.bodyPromise;\n }\n\n /**\n * Get a promise resolving to the footer region.\n *\n * @method getFooterPromise\n * @return {object} jQuery object\n */\n getFooterPromise() {\n return this.footerPromise;\n }\n\n /**\n * Get the unique modal count.\n *\n * @method getModalCount\n * @return {int}\n */\n getModalCount() {\n return this.modalCount;\n }\n\n /**\n * Set the modal title element.\n *\n * This method is overloaded to take either a string value for the title or a jQuery promise that is resolved with\n * HTML most commonly from a Str.get_string call.\n *\n * @method setTitle\n * @param {(string|object)} value The title string or jQuery promise which resolves to the title.\n */\n setTitle(value) {\n const title = this.getTitle();\n this.titlePromise = $.Deferred();\n\n this.asyncSet(value, title.html.bind(title))\n .then(() => {\n this.titlePromise.resolve(title);\n return;\n })\n .catch(Notification.exception);\n }\n\n /**\n * Set the modal body element.\n *\n * This method is overloaded to take either a string value for the body or a jQuery promise that is resolved with\n * HTML and Javascript most commonly from a Templates.render call.\n *\n * @method setBody\n * @param {(string|object)} value The body string or jQuery promise which resolves to the body.\n * @fires event:filterContentUpdated\n */\n setBody(value) {\n this.bodyPromise = $.Deferred();\n\n const body = this.getBody();\n\n if (typeof value === 'string') {\n // Just set the value if it's a string.\n body.html(value);\n FilterEvents.notifyFilterContentUpdated(body);\n this.getRoot().trigger(ModalEvents.bodyRendered, this);\n this.bodyPromise.resolve(body);\n } else {\n const modalPromise = new Pending(`amd-modal-js-pending-id-${this.getModalCount()}`);\n // Otherwise we assume it's a promise to be resolved with\n // html and javascript.\n let contentPromise = null;\n body.css('overflow', 'hidden');\n\n // Ensure that the `value` is a jQuery Promise.\n value = $.when(value);\n\n if (value.state() == 'pending') {\n // We're still waiting for the body promise to resolve so\n // let's show a loading icon.\n let height = body.innerHeight();\n if (height < 100) {\n height = 100;\n }\n\n body.animate({height: `${height}px`}, 150);\n\n body.html('');\n contentPromise = Templates.render(TEMPLATES.LOADING, {})\n .then((html) => {\n const loadingIcon = $(html).hide();\n body.html(loadingIcon);\n loadingIcon.fadeIn(150);\n\n // We only want the loading icon to fade out\n // when the content for the body has finished\n // loading.\n return $.when(loadingIcon.promise(), value);\n })\n .then((loadingIcon) => {\n // Once the content has finished loading and\n // the loading icon has been shown then we can\n // fade the icon away to reveal the content.\n return loadingIcon.fadeOut(100).promise();\n })\n .then(() => {\n return value;\n });\n } else {\n // The content is already loaded so let's just display\n // it to the user. No need for a loading icon.\n contentPromise = value;\n }\n\n // Now we can actually display the content.\n contentPromise.then((html, js) => {\n let result = null;\n\n if (this.isVisible()) {\n // If the modal is visible then we should display\n // the content gracefully for the user.\n body.css('opacity', 0);\n const currentHeight = body.innerHeight();\n body.html(html);\n // We need to clear any height values we've set here\n // in order to measure the height of the content being\n // added. This then allows us to animate the height\n // transition.\n body.css('height', '');\n const newHeight = body.innerHeight();\n body.css('height', `${currentHeight}px`);\n result = body.animate(\n {height: `${newHeight}px`, opacity: 1},\n {duration: 150, queue: false}\n ).promise();\n } else {\n // Since the modal isn't visible we can just immediately\n // set the content. No need to animate it.\n body.html(html);\n }\n\n if (js) {\n if (this.isAttached) {\n // If we're in the DOM then run the JS immediately.\n Templates.runTemplateJS(js);\n } else {\n // Otherwise cache it to be run when we're attached.\n this.bodyJS = js;\n }\n }\n\n return result;\n })\n .then((result) => {\n FilterEvents.notifyFilterContentUpdated(body);\n this.getRoot().trigger(ModalEvents.bodyRendered, this);\n dispatchEvent('core/modal:bodyRendered', this, this.modal[0]);\n return result;\n })\n .then(() => {\n this.bodyPromise.resolve(body);\n return;\n })\n .catch(Notification.exception)\n .always(() => {\n // When we're done displaying all of the content we need\n // to clear the custom values we've set here.\n body.css('height', '');\n body.css('overflow', '');\n body.css('opacity', '');\n modalPromise.resolve();\n\n return;\n });\n }\n }\n\n /**\n * Alternative to setBody() that can be used from non-Jquery modules\n *\n * @param {Promise} promise promise that returns {html, js} object\n * @return {Promise}\n */\n setBodyContent(promise) {\n // Call the leegacy API for now and pass it a jQuery Promise.\n // This is a non-spec feature of jQuery and cannot be produced with spec promises.\n // We can encourage people to migrate to this approach, and in future we can swap\n // it so that setBody() calls setBodyPromise().\n return promise.then(({html, js}) => this.setBody($.when(html, js)))\n .catch(exception => {\n this.hide();\n throw exception;\n });\n }\n\n /**\n * Set the modal footer element. The footer element is made visible, if it\n * isn't already.\n *\n * This method is overloaded to take either a string\n * value for the body or a jQuery promise that is resolved with HTML and Javascript\n * most commonly from a Templates.render call.\n *\n * @method setFooter\n * @param {(string|object)} value The footer string or jQuery promise\n */\n setFooter(value) {\n // Make sure the footer is visible.\n this.showFooter();\n this.footerPromise = $.Deferred();\n\n const footer = this.getFooter();\n\n if (typeof value === 'string') {\n // Just set the value if it's a string.\n footer.html(value);\n this.footerPromise.resolve(footer);\n } else {\n // Otherwise we assume it's a promise to be resolved with\n // html and javascript.\n Templates.render(TEMPLATES.LOADING, {})\n .then((html) => {\n footer.html(html);\n\n return value;\n })\n .then((html, js) => {\n footer.html(html);\n\n if (js) {\n if (this.isAttached) {\n // If we're in the DOM then run the JS immediately.\n Templates.runTemplateJS(js);\n } else {\n // Otherwise cache it to be run when we're attached.\n this.footerJS = js;\n }\n }\n\n return footer;\n })\n .then((footer) => {\n this.footerPromise.resolve(footer);\n this.showFooter();\n return;\n })\n .catch(Notification.exception);\n }\n }\n\n /**\n * Check if the footer has any content in it.\n *\n * @method hasFooterContent\n * @return {bool}\n */\n hasFooterContent() {\n return this.getFooter().children().length ? true : false;\n }\n\n /**\n * Hide the footer element.\n *\n * @method hideFooter\n */\n hideFooter() {\n this.getFooter().addClass('hidden');\n }\n\n /**\n * Show the footer element.\n *\n * @method showFooter\n */\n showFooter() {\n this.getFooter().removeClass('hidden');\n }\n\n /**\n * Mark the modal as a large modal.\n *\n * @method setLarge\n */\n setLarge() {\n if (this.isLarge()) {\n return;\n }\n\n this.getModal().addClass('modal-lg');\n }\n\n /**\n * Mark the modal as a centered modal.\n *\n * @method setVerticallyCentered\n */\n setVerticallyCentered() {\n if (this.isVerticallyCentered()) {\n return;\n }\n this.getModal().addClass('modal-dialog-centered');\n }\n\n /**\n * Check if the modal is a large modal.\n *\n * @method isLarge\n * @return {bool}\n */\n isLarge() {\n return this.getModal().hasClass('modal-lg');\n }\n\n /**\n * Check if the modal is vertically centered.\n *\n * @method isVerticallyCentered\n * @return {bool}\n */\n isVerticallyCentered() {\n return this.getModal().hasClass('modal-dialog-centered');\n }\n\n /**\n * Mark the modal as a small modal.\n *\n * @method setSmall\n */\n setSmall() {\n if (this.isSmall()) {\n return;\n }\n\n this.getModal().removeClass('modal-lg');\n }\n\n /**\n * Check if the modal is a small modal.\n *\n * @method isSmall\n * @return {bool}\n */\n isSmall() {\n return !this.getModal().hasClass('modal-lg');\n }\n\n /**\n * Set this modal to be scrollable or not.\n *\n * @method setScrollable\n * @param {bool} value Whether the modal is scrollable or not\n */\n setScrollable(value) {\n if (!value) {\n this.getModal()[0].classList.remove('modal-dialog-scrollable');\n return;\n }\n\n this.getModal()[0].classList.add('modal-dialog-scrollable');\n }\n\n\n /**\n * Determine the highest z-index value currently on the page.\n *\n * @method calculateZIndex\n * @return {int}\n */\n calculateZIndex() {\n const items = $(`${SELECTORS.DIALOG}, ${SELECTORS.MENU_BAR}, ${SELECTORS.HAS_Z_INDEX}`);\n let zIndex = parseInt(this.root.css('z-index'));\n\n items.each((index, item) => {\n item = $(item);\n if (!item.is(':visible')) {\n // Do not include items which are not visible in the z-index calculation.\n // This is important because some dialogues are not removed from the DOM.\n return;\n }\n // Note that webkit browsers won't return the z-index value from the CSS stylesheet\n // if the element doesn't have a position specified. Instead it'll return \"auto\".\n const itemZIndex = item.css('z-index') ? parseInt(item.css('z-index')) : 0;\n\n if (itemZIndex > zIndex) {\n zIndex = itemZIndex;\n }\n });\n\n return zIndex;\n }\n\n /**\n * Check if this modal is visible.\n *\n * @method isVisible\n * @return {bool}\n */\n isVisible() {\n return this.root.hasClass('show');\n }\n\n /**\n * Check if this modal has focus.\n *\n * @method hasFocus\n * @return {bool}\n */\n hasFocus() {\n const target = $(document.activeElement);\n return this.root.is(target) || this.root.has(target).length;\n }\n\n /**\n * Check if this modal has CSS transitions applied.\n *\n * @method hasTransitions\n * @return {bool}\n */\n hasTransitions() {\n return this.getRoot().hasClass('fade');\n }\n\n /**\n * Gets the jQuery wrapped node that the Modal should be attached to.\n *\n * @returns {jQuery}\n */\n getAttachmentPoint() {\n return $(Fullscreen.getElement() || this.attachmentPoint);\n }\n\n /**\n * Display this modal. The modal will be attached to the DOM if it hasn't\n * already been.\n *\n * @method show\n * @returns {Promise}\n */\n show() {\n if (this.isVisible()) {\n return $.Deferred().resolve();\n }\n\n const pendingPromise = new Pending('core/modal:show');\n\n if (this.hasFooterContent()) {\n this.showFooter();\n } else {\n this.hideFooter();\n }\n\n this.attachToDOM();\n\n // If the focusOnClose was not set. Set the focus back to triggered element.\n if (!this.focusOnClose && document.activeElement) {\n this.focusOnClose = document.activeElement;\n }\n\n return this.getBackdrop()\n .then((backdrop) => {\n const currentIndex = this.calculateZIndex();\n const newIndex = currentIndex + 2;\n const newBackdropIndex = newIndex - 1;\n this.root.css('z-index', newIndex);\n backdrop.setZIndex(newBackdropIndex);\n backdrop.show();\n\n this.root.removeClass('hide').addClass('show');\n this.accessibilityShow();\n this.getModal().focus();\n $('body').addClass('modal-open');\n const scrollbarWidth = window.innerWidth - document.documentElement.clientWidth;\n $('body').css({overflow: \"hidden\", paddingRight: `${scrollbarWidth}px`});\n this.root.trigger(ModalEvents.shown, this);\n dispatchEvent('core/modal:shown', this, this.modal[0]);\n\n return;\n })\n .then(pendingPromise.resolve);\n }\n\n /**\n * Hide this modal if it does not contain a form.\n *\n * @method hideIfNotForm\n */\n hideIfNotForm() {\n const formElement = this.modal.find(SELECTORS.FORM);\n if (formElement.length == 0) {\n this.hide();\n }\n }\n\n /**\n * Hide this modal.\n *\n * @method hide\n */\n hide() {\n this.getBackdrop().done((backdrop) => {\n FocusLock.untrapFocus();\n\n if (!this.countOtherVisibleModals()) {\n // Hide the backdrop if we're the last open modal.\n backdrop.hide();\n $('body').removeClass('modal-open');\n $('body').css({overflow: \"\", paddingRight: \"\"});\n }\n\n const currentIndex = parseInt(this.root.css('z-index'));\n this.root.css('z-index', '');\n backdrop.setZIndex(currentIndex - 3);\n\n this.accessibilityHide();\n\n if (this.hasTransitions()) {\n // Wait for CSS transitions to complete before hiding the element.\n this.getRoot().one('transitionend webkitTransitionEnd oTransitionEnd', () => {\n this.getRoot().removeClass('show').addClass('hide');\n });\n } else {\n this.getRoot().removeClass('show').addClass('hide');\n }\n\n // Ensure the modal is moved onto the body node if it is still attached to the DOM.\n if ($(document.body).find(this.getRoot()).length) {\n $(document.body).append(this.getRoot());\n }\n\n // Closes popover elements that are inside the modal at the time the modal is closed.\n this.getRoot().find('[data-bs-toggle=\"popover\"]').each(function() {\n document.getElementById(this.getAttribute('aria-describedby'))?.remove();\n });\n\n this.root.trigger(ModalEvents.hidden, this);\n });\n }\n\n /**\n * Remove this modal from the DOM.\n *\n * @method destroy\n */\n destroy() {\n this.hide();\n removeToastRegion(this.getBody().get(0));\n this.root.remove();\n this.root.trigger(ModalEvents.destroyed, this);\n this.attachmentPoint.remove();\n }\n\n /**\n * Sets the appropriate aria attributes on this dialogue and the other\n * elements in the DOM to ensure that screen readers are able to navigate\n * the dialogue popup correctly.\n *\n * @method accessibilityShow\n */\n accessibilityShow() {\n // Make us visible to screen readers.\n Aria.unhide(this.root.get());\n\n // Hide siblings.\n Aria.hideSiblings(this.root.get()[0]);\n }\n\n /**\n * Restores the aria visibility on the DOM elements changed when displaying\n * the dialogue popup and makes the dialogue aria hidden to allow screen\n * readers to navigate the main page correctly when the dialogue is closed.\n *\n * @method accessibilityHide\n */\n accessibilityHide() {\n // Unhide siblings.\n Aria.unhideSiblings(this.root.get()[0]);\n\n // Hide this modal.\n Aria.hide(this.root.get());\n }\n\n /**\n * Set up all of the event handling for the modal.\n *\n * @method registerEventListeners\n */\n registerEventListeners() {\n this.getRoot().on('keydown', (e) => {\n if (!this.isVisible()) {\n return;\n }\n\n if (e.keyCode == KeyCodes.escape) {\n if (this.removeOnClose) {\n this.destroy();\n } else {\n this.hide();\n }\n }\n });\n\n // Listen for clicks on the modal container.\n this.getRoot().click((e) => {\n // If the click wasn't inside the modal element then we should\n // hide the modal.\n if (!$(e.target).closest(SELECTORS.MODAL).length) {\n // The check above fails to detect the click was inside the modal when the DOM tree is already changed.\n // So, we check if we can still find the container element or not. If not, then the DOM tree is changed.\n // It's best not to hide the modal in that case.\n if ($(e.target).closest(SELECTORS.CONTAINER).length) {\n const outsideClickEvent = $.Event(ModalEvents.outsideClick);\n this.getRoot().trigger(outsideClickEvent, this);\n\n if (!outsideClickEvent.isDefaultPrevented()) {\n this.hideIfNotForm();\n }\n }\n }\n });\n\n CustomEvents.define(this.getModal(), [CustomEvents.events.activate]);\n this.getModal().on(CustomEvents.events.activate, SELECTORS.HIDE, (e, data) => {\n if (this.removeOnClose) {\n this.destroy();\n } else {\n this.hide();\n }\n data.originalEvent.preventDefault();\n });\n\n this.getRoot().on(ModalEvents.hidden, () => {\n if (this.focusOnClose) {\n // Focus on the element that actually triggers the modal.\n this.focusOnClose.focus();\n }\n });\n }\n\n /**\n * Register a listener to close the dialogue when the cancel button is pressed.\n *\n * @method registerCloseOnCancel\n */\n registerCloseOnCancel() {\n // Handle the clicking of the Cancel button.\n this.getModal().on(CustomEvents.events.activate, this.getActionSelector('cancel'), (e, data) => {\n const cancelEvent = $.Event(ModalEvents.cancel);\n this.getRoot().trigger(cancelEvent, this);\n\n if (!cancelEvent.isDefaultPrevented()) {\n data.originalEvent.preventDefault();\n\n if (this.removeOnClose) {\n this.destroy();\n } else {\n this.hide();\n }\n }\n });\n }\n\n /**\n * Register a listener to close the dialogue when the save button is pressed.\n *\n * @method registerCloseOnSave\n */\n registerCloseOnSave() {\n // Handle the clicking of the Cancel button.\n this.getModal().on(CustomEvents.events.activate, this.getActionSelector('save'), (e, data) => {\n const saveEvent = $.Event(ModalEvents.save);\n this.getRoot().trigger(saveEvent, this);\n\n if (!saveEvent.isDefaultPrevented()) {\n data.originalEvent.preventDefault();\n\n if (this.removeOnClose) {\n this.destroy();\n } else {\n this.hide();\n }\n }\n });\n }\n\n\n /**\n * Register a listener to close the dialogue when the delete button is pressed.\n *\n * @method registerCloseOnDelete\n */\n registerCloseOnDelete() {\n // Handle the clicking of the Cancel button.\n this.getModal().on(CustomEvents.events.activate, this.getActionSelector('delete'), (e, data) => {\n const deleteEvent = $.Event(ModalEvents.delete);\n this.getRoot().trigger(deleteEvent, this);\n\n if (!deleteEvent.isDefaultPrevented()) {\n data.originalEvent.preventDefault();\n\n if (this.removeOnClose) {\n this.destroy();\n } else {\n this.hide();\n }\n }\n });\n }\n\n /**\n * Set or resolve and set the value using the function.\n *\n * @method asyncSet\n * @param {(string|object)} value The string or jQuery promise.\n * @param {function} setFunction The setter\n * @return {Promise}\n */\n asyncSet(value, setFunction) {\n const getWrappedValue = (value) => {\n if (value instanceof Promise) {\n return $.when(value);\n }\n\n if (typeof value !== 'object' || !value.hasOwnProperty('then')) {\n return $.Deferred().resolve(value);\n }\n\n return value;\n };\n\n return getWrappedValue(value)\n .then((content) => setFunction(content))\n .catch(Notification.exception);\n }\n\n /**\n * Set the title text of a button.\n *\n * This method is overloaded to take either a string value for the button title or a jQuery promise that is resolved with\n * text most commonly from a Str.get_string call.\n *\n * @param {DOMString} action The action of the button\n * @param {(String|object)} value The button text, or a promise which will resolve to it\n * @returns {Promise}\n */\n setButtonText(action, value) {\n const button = this.getFooter().find(this.getActionSelector(action));\n\n if (!button) {\n throw new Error(\"Unable to find the '\" + action + \"' button\");\n }\n\n return this.asyncSet(value, button.text.bind(button));\n }\n\n /**\n * Get the Selector for an action.\n *\n * @param {String} action\n * @returns {DOMString}\n */\n getActionSelector(action) {\n return \"[data-action='\" + action + \"']\";\n }\n\n /**\n * Set the flag to remove the modal from the DOM on close.\n *\n * @param {Boolean} remove\n */\n setRemoveOnClose(remove) {\n this.removeOnClose = remove;\n }\n\n /**\n * Set the return element for the modal.\n *\n * @param {Element|jQuery} element Element to focus when the modal is closed\n */\n setReturnElement(element) {\n this.focusOnClose = element;\n }\n\n /**\n * Set the a button enabled or disabled.\n *\n * @param {DOMString} action The action of the button\n * @param {Boolean} disabled the new disabled value\n */\n setButtonDisabled(action, disabled) {\n const button = this.getFooter().find(this.getActionSelector(action));\n\n if (!button) {\n throw new Error(\"Unable to find the '\" + action + \"' button\");\n }\n if (disabled) {\n button.attr('disabled', '');\n } else {\n button.removeAttr('disabled');\n }\n }\n\n /**\n * Set the template JS for this modal.\n * @param {String} js The JavaScript to run when the modal is attached to the DOM.\n */\n setTemplateJS(js) {\n this.templateJS = js;\n }\n}\n"],"names":["SELECTORS","TEMPLATES","Modal","root","this","_root","filter","constructor","modal","find","header","headerPromise","$","Deferred","title","titlePromise","body","bodyPromise","footer","footerPromise","hiddenSiblings","isAttached","bodyJS","footerJS","modalCount","modalCounter","attachmentPoint","document","createElement","append","focusOnClose","templateJS","is","Notification","exception","message","length","registerEventListeners","TYPE","Error","TEMPLATE","register","modalConfig","pendingModalPromise","Pending","type","templateName","_getTemplateName","templateContext","html","js","Templates","renderForPromise","setTemplateJS","configure","resolve","template","registry","has","get","show","large","isVerticallyCentered","removeOnClose","scrollable","returnElement","buttons","setLarge","setVerticallyCentered","setRemoveOnClose","setReturnElement","setScrollable","undefined","setTitle","setBody","setFooter","Object","entries","forEach","_ref","key","value","setButtonText","attachToDOM","getAttachmentPoint","FocusLock","trapFocus","runTemplateJS","countOtherVisibleModals","count","each","index","element","hasClass","getBackdrop","backdropPromise","render","then","ModalBackdrop","catch","getRoot","getModal","getTitle","getBody","getFooter","getTitlePromise","getBodyPromise","getFooterPromise","getModalCount","asyncSet","bind","FilterEvents","notifyFilterContentUpdated","trigger","ModalEvents","bodyRendered","modalPromise","contentPromise","css","when","state","height","innerHeight","animate","loadingIcon","hide","fadeIn","promise","fadeOut","result","isVisible","currentHeight","newHeight","opacity","duration","queue","always","setBodyContent","_ref2","showFooter","hasFooterContent","children","hideFooter","addClass","removeClass","isLarge","setSmall","isSmall","classList","add","remove","calculateZIndex","items","zIndex","parseInt","item","itemZIndex","hasFocus","target","activeElement","hasTransitions","Fullscreen","getElement","pendingPromise","backdrop","newIndex","newBackdropIndex","setZIndex","accessibilityShow","focus","scrollbarWidth","window","innerWidth","documentElement","clientWidth","overflow","paddingRight","shown","hideIfNotForm","done","untrapFocus","currentIndex","accessibilityHide","one","getElementById","getAttribute","hidden","destroy","destroyed","Aria","unhide","hideSiblings","unhideSiblings","on","e","keyCode","KeyCodes","escape","click","closest","outsideClickEvent","Event","outsideClick","isDefaultPrevented","CustomEvents","define","events","activate","data","originalEvent","preventDefault","registerCloseOnCancel","getActionSelector","cancelEvent","cancel","registerCloseOnSave","saveEvent","save","registerCloseOnDelete","deleteEvent","delete","setFunction","Promise","hasOwnProperty","getWrappedValue","content","action","button","text","setButtonDisabled","disabled","attr","removeAttr","Map","module","existing","set","Prefetch","prefetchTemplate"],"mappings":"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"}
\ No newline at end of file
diff --git a/public/lib/amd/build/modal_factory.min.js b/public/lib/amd/build/modal_factory.min.js
deleted file mode 100644
index fd0a0fb3b48..00000000000
--- a/public/lib/amd/build/modal_factory.min.js
+++ /dev/null
@@ -1,12 +0,0 @@
-define("core/modal_factory",["exports","jquery","core/modal_events","core/modal_registry","core/modal","core/modal_save_cancel","core/modal_delete_cancel","core/modal_cancel","core/local/modal/alert","core/notification","core/custom_interaction_events","core/pending"],(function(_exports,_jquery,_modal_events,ModalRegistry,_modal,_modal_save_cancel,_modal_delete_cancel,_modal_cancel,_alert,Notification,CustomEvents,_pending){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}}
-/**
- * Create a modal.
- *
- * @module core/modal_factory
- * @copyright 2016 Ryan Wyllie
- * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- * @deprecated since Moodle 4.3
- * @todo Final deprecation in Moodle 4.7/5.2. See MDL-79128/
- */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.types=_exports.default=_exports.create=void 0,_jquery=_interopRequireDefault(_jquery),_modal_events=_interopRequireDefault(_modal_events),ModalRegistry=_interopRequireWildcard(ModalRegistry),_modal=_interopRequireDefault(_modal),_modal_save_cancel=_interopRequireDefault(_modal_save_cancel),_modal_delete_cancel=_interopRequireDefault(_modal_delete_cancel),_modal_cancel=_interopRequireDefault(_modal_cancel),_alert=_interopRequireDefault(_alert),Notification=_interopRequireWildcard(Notification),CustomEvents=_interopRequireWildcard(CustomEvents),_pending=_interopRequireDefault(_pending);const types={DEFAULT:"DEFAULT",SAVE_CANCEL:_modal_save_cancel.default.TYPE,DELETE_CANCEL:_modal_delete_cancel.default.TYPE,CANCEL:_modal_cancel.default.TYPE,ALERT:_alert.default.TYPE};_exports.types=types,ModalRegistry.register(types.DEFAULT,_modal.default,_modal.default.TEMPLATE);const create=(modalConfig,triggerElement)=>{window.console.warn("The modal_factory has been deprecated since Moodle 4.3. Please use the create method on your target modal type instead.");const type=modalConfig.type||types.DEFAULT,registryConf=ModalRegistry.get(type);registryConf||Notification.exception({message:"Unable to find modal of type: ".concat(type)});const modal=registryConf.module.create(modalConfig);return triggerElement&&(window.console.warn("The triggerElement feature of the modal_factory has been deprecated. Please use event listeners instead."),((modalPromise,triggerElement,modalConfig)=>{let actualTriggerElement=null;const hasPreShowCallback="function"==typeof modalConfig.preShowCallback,triggeredCallback=(e,data)=>{const pendingPromise=new _pending.default("core/modal_factory:setUpTrigger:triggeredCallback");actualTriggerElement=(0,_jquery.default)(e.currentTarget),modalPromise.then((function(modal){return hasPreShowCallback&&modalConfig.preShowCallback(actualTriggerElement,modal),modal.show(),modal})).then(pendingPromise.resolve),data.originalEvent.preventDefault()};if(Array.isArray(triggerElement)){const selector=triggerElement[1];triggerElement=triggerElement[0],CustomEvents.define(triggerElement,[CustomEvents.events.activate]),triggerElement.on(CustomEvents.events.activate,selector,triggeredCallback)}else CustomEvents.define(triggerElement,[CustomEvents.events.activate]),triggerElement.on(CustomEvents.events.activate,triggeredCallback);modalPromise.then((function(modal){return modal.getRoot().on(_modal_events.default.hidden,(function(){null!==actualTriggerElement&&actualTriggerElement.focus()})),modal}))})(modal,triggerElement,modalConfig)),_jquery.default.when(new Promise(((resolve,reject)=>{modal.then(resolve).catch(reject)})))};_exports.create=create;var _default={create:create,types:types};return _exports.default=_default,_exports.default}));
-
-//# sourceMappingURL=modal_factory.min.js.map
\ No newline at end of file
diff --git a/public/lib/amd/build/modal_factory.min.js.map b/public/lib/amd/build/modal_factory.min.js.map
deleted file mode 100644
index 97d8aa064aa..00000000000
--- a/public/lib/amd/build/modal_factory.min.js.map
+++ /dev/null
@@ -1 +0,0 @@
-{"version":3,"file":"modal_factory.min.js","sources":["../src/modal_factory.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Create a modal.\n *\n * @module core/modal_factory\n * @copyright 2016 Ryan Wyllie \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @deprecated since Moodle 4.3\n * @todo Final deprecation in Moodle 4.7/5.2. See MDL-79128/\n */\n\nimport $ from 'jquery';\nimport ModalEvents from 'core/modal_events';\nimport * as ModalRegistry from 'core/modal_registry';\nimport Modal from 'core/modal';\nimport ModalSaveCancel from 'core/modal_save_cancel';\nimport ModalDeleteCancel from 'core/modal_delete_cancel';\nimport ModalCancel from 'core/modal_cancel';\nimport ModalAlert from 'core/local/modal/alert';\nimport * as Notification from 'core/notification';\nimport * as CustomEvents from 'core/custom_interaction_events';\nimport Pending from 'core/pending';\n\n/**\n * The available standard modals.\n *\n * @property {String} DEFAULT The default modal\n * @property {String} SAVE_CANCEL A modal which can be used to either save, or cancel.\n * @property {String} DELETE_CANCEL A modal which can be used to either delete, or cancel.\n * @property {String} CANCEL A modal which displayed a cancel button\n * @property {String} ALERT An information modal which only displays information.\n */\nexport const types = {\n DEFAULT: 'DEFAULT',\n SAVE_CANCEL: ModalSaveCancel.TYPE,\n DELETE_CANCEL: ModalDeleteCancel.TYPE,\n CANCEL: ModalCancel.TYPE,\n ALERT: ModalAlert.TYPE,\n};\n\n// Most modals are self-registering.\n// We do not self-register the base Modal because we do not want to define a default TYPE\n// on the class that every other modal extends.\nModalRegistry.register(types.DEFAULT, Modal, Modal.TEMPLATE);\n\n/**\n * Set up the events required to show the modal and return focus when the modal\n * is closed.\n *\n * @method setUpTrigger\n * @private\n * @param {Promise} modalPromise The modal instance\n * @param {object} triggerElement The jQuery element to open the modal\n * @param {object} modalConfig The modal configuration given to the factory\n */\nconst setUpTrigger = (modalPromise, triggerElement, modalConfig) => {\n // The element that actually shows the modal.\n let actualTriggerElement = null;\n // Check if the client has provided a callback function to be called\n // before the modal is displayed.\n const hasPreShowCallback = (typeof modalConfig.preShowCallback == 'function');\n // Function to handle the trigger element being activated.\n const triggeredCallback = (e, data) => {\n const pendingPromise = new Pending('core/modal_factory:setUpTrigger:triggeredCallback');\n actualTriggerElement = $(e.currentTarget);\n\n // eslint-disable-next-line promise/catch-or-return\n modalPromise.then(function(modal) {\n if (hasPreShowCallback) {\n // If the client provided a pre-show callback then execute\n // it now before showing the modal.\n modalConfig.preShowCallback(actualTriggerElement, modal);\n }\n\n modal.show();\n\n return modal;\n })\n .then(pendingPromise.resolve);\n data.originalEvent.preventDefault();\n };\n\n // The trigger element can either be a single element or it can be an\n // element + selector pair to create a delegated event handler to trigger\n // the modal.\n if (Array.isArray(triggerElement)) {\n const selector = triggerElement[1];\n triggerElement = triggerElement[0];\n\n CustomEvents.define(triggerElement, [CustomEvents.events.activate]);\n triggerElement.on(CustomEvents.events.activate, selector, triggeredCallback);\n } else {\n CustomEvents.define(triggerElement, [CustomEvents.events.activate]);\n triggerElement.on(CustomEvents.events.activate, triggeredCallback);\n }\n\n // eslint-disable-next-line promise/catch-or-return\n modalPromise.then(function(modal) {\n modal.getRoot().on(ModalEvents.hidden, function() {\n // Focus on the trigger element that actually launched the modal.\n if (actualTriggerElement !== null) {\n actualTriggerElement.focus();\n }\n });\n\n return modal;\n });\n};\n\n/**\n * Create a Modal instance.\n *\n * @method create\n * @param {object} modalConfig The configuration to create the modal instance\n * @param {object} triggerElement The trigger HTML jQuery object\n * @return {promise} Resolved with a Modal instance\n */\nexport const create = (modalConfig, triggerElement) => {\n window.console.warn(\n 'The modal_factory has been deprecated since Moodle 4.3. Please use the create method on your target modal type instead.',\n );\n // Use of the triggerElement has been deprecated.\n const type = modalConfig.type || types.DEFAULT;\n\n const registryConf = ModalRegistry.get(type);\n if (!registryConf) {\n Notification.exception({message: `Unable to find modal of type: ${type}`});\n }\n\n const modal = registryConf.module.create(modalConfig);\n\n if (triggerElement) {\n window.console.warn(\n 'The triggerElement feature of the modal_factory has been deprecated. Please use event listeners instead.',\n );\n setUpTrigger(modal, triggerElement, modalConfig);\n }\n\n return $.when(new Promise((resolve, reject) => {\n modal\n .then(resolve)\n .catch(reject);\n }));\n};\n\nexport default {\n create,\n types,\n};\n"],"names":["types","DEFAULT","SAVE_CANCEL","ModalSaveCancel","TYPE","DELETE_CANCEL","ModalDeleteCancel","CANCEL","ModalCancel","ALERT","ModalAlert","ModalRegistry","register","Modal","TEMPLATE","create","modalConfig","triggerElement","window","console","warn","type","registryConf","get","Notification","exception","message","modal","module","modalPromise","actualTriggerElement","hasPreShowCallback","preShowCallback","triggeredCallback","e","data","pendingPromise","Pending","currentTarget","then","show","resolve","originalEvent","preventDefault","Array","isArray","selector","CustomEvents","define","events","activate","on","getRoot","ModalEvents","hidden","focus","setUpTrigger","$","when","Promise","reject","catch"],"mappings":";;;;;;;;;2pBA8CaA,MAAQ,CACjBC,QAAS,UACTC,YAAaC,2BAAgBC,KAC7BC,cAAeC,6BAAkBF,KACjCG,OAAQC,sBAAYJ,KACpBK,MAAOC,eAAWN,2BAMtBO,cAAcC,SAASZ,MAAMC,QAASY,eAAOA,eAAMC,gBA0EtCC,OAAS,CAACC,YAAaC,kBAChCC,OAAOC,QAAQC,KACX,iIAGEC,KAAOL,YAAYK,MAAQrB,MAAMC,QAEjCqB,aAAeX,cAAcY,IAAIF,MAClCC,cACDE,aAAaC,UAAU,CAACC,gDAA0CL,cAGhEM,MAAQL,aAAaM,OAAOb,OAAOC,oBAErCC,iBACAC,OAAOC,QAAQC,KACX,4GA9ES,EAACS,aAAcZ,eAAgBD,mBAE5Cc,qBAAuB,WAGrBC,mBAA4D,mBAA/Bf,YAAYgB,gBAEzCC,kBAAoB,CAACC,EAAGC,cACpBC,eAAiB,IAAIC,iBAAQ,qDACnCP,sBAAuB,mBAAEI,EAAEI,eAG3BT,aAAaU,MAAK,SAASZ,cACnBI,oBAGAf,YAAYgB,gBAAgBF,qBAAsBH,OAGtDA,MAAMa,OAECb,SAEVY,KAAKH,eAAeK,SACrBN,KAAKO,cAAcC,qBAMnBC,MAAMC,QAAQ5B,gBAAiB,OACzB6B,SAAW7B,eAAe,GAChCA,eAAiBA,eAAe,GAEhC8B,aAAaC,OAAO/B,eAAgB,CAAC8B,aAAaE,OAAOC,WACzDjC,eAAekC,GAAGJ,aAAaE,OAAOC,SAAUJ,SAAUb,wBAE1Dc,aAAaC,OAAO/B,eAAgB,CAAC8B,aAAaE,OAAOC,WACzDjC,eAAekC,GAAGJ,aAAaE,OAAOC,SAAUjB,mBAIpDJ,aAAaU,MAAK,SAASZ,cACvBA,MAAMyB,UAAUD,GAAGE,sBAAYC,QAAQ,WAEN,OAAzBxB,sBACAA,qBAAqByB,WAItB5B,UA8BP6B,CAAa7B,MAAOV,eAAgBD,cAGjCyC,gBAAEC,KAAK,IAAIC,SAAQ,CAAClB,QAASmB,UAChCjC,MACKY,KAAKE,SACLoB,MAAMD,iDAIJ,CACX7C,OAAAA,OACAf,MAAAA"}
\ No newline at end of file
diff --git a/public/lib/amd/build/modal_registry.min.js b/public/lib/amd/build/modal_registry.min.js
deleted file mode 100644
index ace36ba2edd..00000000000
--- a/public/lib/amd/build/modal_registry.min.js
+++ /dev/null
@@ -1,11 +0,0 @@
-define("core/modal_registry",["exports","core/notification","core/prefetch"],(function(_exports,Notification,Prefetch){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}
-/**
- * A registry for the different types of modal.
- *
- * @module core/modal_registry
- * @class modal_registry
- * @copyright 2016 Ryan Wyllie
- * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.register=_exports.get=_exports.default=void 0,Notification=_interopRequireWildcard(Notification),Prefetch=_interopRequireWildcard(Prefetch);const registry=new Map,get=type=>registry.get(type);_exports.get=get;const register=(type,module,template)=>{const existing=get(type);existing&&existing.module!==module&&Notification.exception({message:"Modal of type '".concat(type,"' is already registered")}),module&&"function"==typeof module||Notification.exception({message:"You must provide a modal module"}),template||Notification.exception({message:"You must provide a modal template"}),registry.set(type,{module:module,template:template}),Prefetch.prefetchTemplate(template)};_exports.register=register;var _default={register:register,get:get};return _exports.default=_default,_exports.default}));
-
-//# sourceMappingURL=modal_registry.min.js.map
\ No newline at end of file
diff --git a/public/lib/amd/build/modal_registry.min.js.map b/public/lib/amd/build/modal_registry.min.js.map
deleted file mode 100644
index ea733aba80a..00000000000
--- a/public/lib/amd/build/modal_registry.min.js.map
+++ /dev/null
@@ -1 +0,0 @@
-{"version":3,"file":"modal_registry.min.js","sources":["../src/modal_registry.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * A registry for the different types of modal.\n *\n * @module core/modal_registry\n * @class modal_registry\n * @copyright 2016 Ryan Wyllie \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport * as Notification from 'core/notification';\nimport * as Prefetch from 'core/prefetch';\n\n// A singleton registry for all modules to access. Allows types to be\n// added at runtime.\nconst registry = new Map();\n\n/**\n * Get a registered type of modal.\n *\n * @method get\n * @param {string} type The type of modal to get\n * @return {object} The registered config for the modal\n */\nexport const get = (type) => registry.get(type);\n\n/**\n * Register a modal with the registry.\n *\n * @method register\n * @param {string} type The type of modal (must be unique)\n * @param {function} module The modal module (must be a constructor function of type core/modal)\n * @param {string} template The template name of the modal\n */\nexport const register = (type, module, template) => {\n const existing = get(type);\n if (existing && existing.module !== module) {\n Notification.exception({\n message: `Modal of type '${type}' is already registered`,\n });\n }\n\n if (!module || typeof module !== 'function') {\n Notification.exception({message: \"You must provide a modal module\"});\n }\n\n if (!template) {\n Notification.exception({message: \"You must provide a modal template\"});\n }\n\n registry.set(type, {module, template});\n\n // Prefetch the template.\n Prefetch.prefetchTemplate(template);\n};\n\nexport default {\n register,\n get,\n};\n"],"names":["registry","Map","get","type","register","module","template","existing","Notification","exception","message","set","Prefetch","prefetchTemplate"],"mappings":";;;;;;;;wNA4BMA,SAAW,IAAIC,IASRC,IAAOC,MAASH,SAASE,IAAIC,6BAU7BC,SAAW,CAACD,KAAME,OAAQC,kBAC7BC,SAAWL,IAAIC,MACjBI,UAAYA,SAASF,SAAWA,QAChCG,aAAaC,UAAU,CACnBC,kCAA4BP,kCAI/BE,QAA4B,mBAAXA,QAClBG,aAAaC,UAAU,CAACC,QAAS,oCAGhCJ,UACDE,aAAaC,UAAU,CAACC,QAAS,sCAGrCV,SAASW,IAAIR,KAAM,CAACE,OAAAA,OAAQC,SAAAA,WAG5BM,SAASC,iBAAiBP,mDAGf,CACXF,SAAAA,SACAF,IAAAA"}
\ No newline at end of file
diff --git a/public/lib/amd/src/modal.js b/public/lib/amd/src/modal.js
index a48b2799be7..700b9ed9e3d 100644
--- a/public/lib/amd/src/modal.js
+++ b/public/lib/amd/src/modal.js
@@ -27,7 +27,6 @@ import * as Notification from 'core/notification';
import * as KeyCodes from 'core/key_codes';
import ModalBackdrop from 'core/modal_backdrop';
import ModalEvents from 'core/modal_events';
-import * as ModalRegistry from 'core/modal_registry';
import Pending from 'core/pending';
import * as CustomEvents from 'core/custom_interaction_events';
import * as FilterEvents from 'core_filters/events';
@@ -36,6 +35,7 @@ import * as Aria from 'core/aria';
import * as Fullscreen from 'core/fullscreen';
import {removeToastRegion} from './toast';
import {dispatchEvent} from 'core/event_dispatcher';
+import * as Prefetch from 'core/prefetch';
/**
* A configuration to provide to the modal.
@@ -91,6 +91,12 @@ export default class Modal {
*/
static modalCounter = 0;
+ /**
+ * @var {Number} A singleton registry for all modules to access. Allows types to be
+ * added at runtime.
+ */
+ static registry = new Map();
+
/**
* Getter method for .root element.
* @return {object} jQuery object
@@ -175,13 +181,43 @@ export default class Modal {
if (!this.TEMPLATE) {
throw new Error(`Unknown modal template`, this);
}
- ModalRegistry.register(
+
+ this.register(
this.TYPE,
this,
this.TEMPLATE,
);
}
+ /**
+ * Register a modal.
+ *
+ * @param {string} type The type of modal (must be unique)
+ * @param {function} module The modal module (must be a constructor function of type core/modal)
+ * @param {string} template The template name of the modal
+ */
+ static register = (type, module, template) => {
+ const existing = this.registry.get(type);
+ if (existing && existing.module !== module) {
+ Notification.exception({
+ message: `Modal of type '${type}' is already registered`,
+ });
+ }
+
+ if (!module || typeof module !== 'function') {
+ Notification.exception({message: "You must provide a modal module"});
+ }
+
+ if (!template) {
+ Notification.exception({message: "You must provide a modal template"});
+ }
+
+ this.registry.set(type, {module, template});
+
+ // Prefetch the template.
+ Prefetch.prefetchTemplate(template);
+ };
+
/**
* Create a new modal using the ModalFactory.
* This is a shortcut to creating the modal.
@@ -191,7 +227,7 @@ export default class Modal {
* @returns {Promise}
*/
static async create(modalConfig = {}) {
- const pendingModalPromise = new Pending('core/modal_factory:create');
+ const pendingModalPromise = new Pending('core/modal:create');
modalConfig.type = this.TYPE;
const templateName = this._getTemplateName(modalConfig);
@@ -225,13 +261,8 @@ export default class Modal {
return this.TEMPLATE;
}
- if (ModalRegistry.has(this.TYPE)) {
- // Note: This is provided as an interim backwards-compatability layer and will be removed four releases after 4.3.
- window.console.warning(
- 'Use of core/modal_registry is deprecated. ' +
- 'Please define your modal template in a new static TEMPLATE property on your modal class.',
- );
- const config = ModalRegistry.get(this.TYPE);
+ if (this.registry.has(this.TYPE)) {
+ const config = this.registry.get(this.TYPE);
return config.template;
}
diff --git a/public/lib/amd/src/modal_factory.js b/public/lib/amd/src/modal_factory.js
deleted file mode 100644
index 460083c7a9d..00000000000
--- a/public/lib/amd/src/modal_factory.js
+++ /dev/null
@@ -1,163 +0,0 @@
-// This file is part of Moodle - http://moodle.org/
-//
-// Moodle is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// Moodle is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with Moodle. If not, see .
-
-/**
- * Create a modal.
- *
- * @module core/modal_factory
- * @copyright 2016 Ryan Wyllie
- * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- * @deprecated since Moodle 4.3
- * @todo Final deprecation in Moodle 4.7/5.2. See MDL-79128/
- */
-
-import $ from 'jquery';
-import ModalEvents from 'core/modal_events';
-import * as ModalRegistry from 'core/modal_registry';
-import Modal from 'core/modal';
-import ModalSaveCancel from 'core/modal_save_cancel';
-import ModalDeleteCancel from 'core/modal_delete_cancel';
-import ModalCancel from 'core/modal_cancel';
-import ModalAlert from 'core/local/modal/alert';
-import * as Notification from 'core/notification';
-import * as CustomEvents from 'core/custom_interaction_events';
-import Pending from 'core/pending';
-
-/**
- * The available standard modals.
- *
- * @property {String} DEFAULT The default modal
- * @property {String} SAVE_CANCEL A modal which can be used to either save, or cancel.
- * @property {String} DELETE_CANCEL A modal which can be used to either delete, or cancel.
- * @property {String} CANCEL A modal which displayed a cancel button
- * @property {String} ALERT An information modal which only displays information.
- */
-export const types = {
- DEFAULT: 'DEFAULT',
- SAVE_CANCEL: ModalSaveCancel.TYPE,
- DELETE_CANCEL: ModalDeleteCancel.TYPE,
- CANCEL: ModalCancel.TYPE,
- ALERT: ModalAlert.TYPE,
-};
-
-// Most modals are self-registering.
-// We do not self-register the base Modal because we do not want to define a default TYPE
-// on the class that every other modal extends.
-ModalRegistry.register(types.DEFAULT, Modal, Modal.TEMPLATE);
-
-/**
- * Set up the events required to show the modal and return focus when the modal
- * is closed.
- *
- * @method setUpTrigger
- * @private
- * @param {Promise} modalPromise The modal instance
- * @param {object} triggerElement The jQuery element to open the modal
- * @param {object} modalConfig The modal configuration given to the factory
- */
-const setUpTrigger = (modalPromise, triggerElement, modalConfig) => {
- // The element that actually shows the modal.
- let actualTriggerElement = null;
- // Check if the client has provided a callback function to be called
- // before the modal is displayed.
- const hasPreShowCallback = (typeof modalConfig.preShowCallback == 'function');
- // Function to handle the trigger element being activated.
- const triggeredCallback = (e, data) => {
- const pendingPromise = new Pending('core/modal_factory:setUpTrigger:triggeredCallback');
- actualTriggerElement = $(e.currentTarget);
-
- // eslint-disable-next-line promise/catch-or-return
- modalPromise.then(function(modal) {
- if (hasPreShowCallback) {
- // If the client provided a pre-show callback then execute
- // it now before showing the modal.
- modalConfig.preShowCallback(actualTriggerElement, modal);
- }
-
- modal.show();
-
- return modal;
- })
- .then(pendingPromise.resolve);
- data.originalEvent.preventDefault();
- };
-
- // The trigger element can either be a single element or it can be an
- // element + selector pair to create a delegated event handler to trigger
- // the modal.
- if (Array.isArray(triggerElement)) {
- const selector = triggerElement[1];
- triggerElement = triggerElement[0];
-
- CustomEvents.define(triggerElement, [CustomEvents.events.activate]);
- triggerElement.on(CustomEvents.events.activate, selector, triggeredCallback);
- } else {
- CustomEvents.define(triggerElement, [CustomEvents.events.activate]);
- triggerElement.on(CustomEvents.events.activate, triggeredCallback);
- }
-
- // eslint-disable-next-line promise/catch-or-return
- modalPromise.then(function(modal) {
- modal.getRoot().on(ModalEvents.hidden, function() {
- // Focus on the trigger element that actually launched the modal.
- if (actualTriggerElement !== null) {
- actualTriggerElement.focus();
- }
- });
-
- return modal;
- });
-};
-
-/**
- * Create a Modal instance.
- *
- * @method create
- * @param {object} modalConfig The configuration to create the modal instance
- * @param {object} triggerElement The trigger HTML jQuery object
- * @return {promise} Resolved with a Modal instance
- */
-export const create = (modalConfig, triggerElement) => {
- window.console.warn(
- 'The modal_factory has been deprecated since Moodle 4.3. Please use the create method on your target modal type instead.',
- );
- // Use of the triggerElement has been deprecated.
- const type = modalConfig.type || types.DEFAULT;
-
- const registryConf = ModalRegistry.get(type);
- if (!registryConf) {
- Notification.exception({message: `Unable to find modal of type: ${type}`});
- }
-
- const modal = registryConf.module.create(modalConfig);
-
- if (triggerElement) {
- window.console.warn(
- 'The triggerElement feature of the modal_factory has been deprecated. Please use event listeners instead.',
- );
- setUpTrigger(modal, triggerElement, modalConfig);
- }
-
- return $.when(new Promise((resolve, reject) => {
- modal
- .then(resolve)
- .catch(reject);
- }));
-};
-
-export default {
- create,
- types,
-};
diff --git a/public/lib/amd/src/modal_registry.js b/public/lib/amd/src/modal_registry.js
deleted file mode 100644
index 4814eab51a8..00000000000
--- a/public/lib/amd/src/modal_registry.js
+++ /dev/null
@@ -1,73 +0,0 @@
-// This file is part of Moodle - http://moodle.org/
-//
-// Moodle is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// Moodle is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with Moodle. If not, see .
-
-/**
- * A registry for the different types of modal.
- *
- * @module core/modal_registry
- * @class modal_registry
- * @copyright 2016 Ryan Wyllie
- * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-import * as Notification from 'core/notification';
-import * as Prefetch from 'core/prefetch';
-
-// A singleton registry for all modules to access. Allows types to be
-// added at runtime.
-const registry = new Map();
-
-/**
- * Get a registered type of modal.
- *
- * @method get
- * @param {string} type The type of modal to get
- * @return {object} The registered config for the modal
- */
-export const get = (type) => registry.get(type);
-
-/**
- * Register a modal with the registry.
- *
- * @method register
- * @param {string} type The type of modal (must be unique)
- * @param {function} module The modal module (must be a constructor function of type core/modal)
- * @param {string} template The template name of the modal
- */
-export const register = (type, module, template) => {
- const existing = get(type);
- if (existing && existing.module !== module) {
- Notification.exception({
- message: `Modal of type '${type}' is already registered`,
- });
- }
-
- if (!module || typeof module !== 'function') {
- Notification.exception({message: "You must provide a modal module"});
- }
-
- if (!template) {
- Notification.exception({message: "You must provide a modal template"});
- }
-
- registry.set(type, {module, template});
-
- // Prefetch the template.
- Prefetch.prefetchTemplate(template);
-};
-
-export default {
- register,
- get,
-};
diff --git a/public/lib/form/amd/build/modalform.min.js b/public/lib/form/amd/build/modalform.min.js
index ac8bc5e6a43..9d14bc5ac77 100644
--- a/public/lib/form/amd/build/modalform.min.js
+++ b/public/lib/form/amd/build/modalform.min.js
@@ -1,3 +1,3 @@
-define("core_form/modalform",["exports","core/ajax","core_form/changechecker","core_form/events","core/fragment","core/modal_events","core/notification","core/pending","./util"],(function(_exports,_ajax,FormChangeChecker,FormEvents,_fragment,_modal_events,_notification,_pending,_util){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_ajax=_interopRequireDefault(_ajax),FormChangeChecker=_interopRequireWildcard(FormChangeChecker),FormEvents=_interopRequireWildcard(FormEvents),_fragment=_interopRequireDefault(_fragment),_modal_events=_interopRequireDefault(_modal_events),_notification=_interopRequireDefault(_notification),_pending=_interopRequireDefault(_pending);var _systemImportTransformerGlobalIdentifier="undefined"!=typeof window?window:"undefined"!=typeof self?self:"undefined"!=typeof global?global:{};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}}return _exports.default=class{constructor(config){var obj,key,value;value={FORM_SUBMITTED:"core_form_modalform_formsubmitted",FORM_CANCELLED:"core_form_modalform_formcancelled",CLIENT_VALIDATION_ERROR:"core_form_modalform_clientvalidationerror",SERVER_VALIDATION_ERROR:"core_form_modalform_validationerror",ERROR:"core_form_modalform_error",NOSUBMIT_BUTTON_PRESSED:"core_form_modalform_nosubmitbutton",SUBMIT_BUTTON_PRESSED:"core_form_modalform_submitbutton",CANCEL_BUTTON_PRESSED:"core_form_modalform_cancelbutton",LOADED:"core_form_modalform_loaded"},(key="events")in(obj=this)?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value,this.modal=null,this.config=config,this.config.modalConfig={removeOnClose:!0,large:!0,...this.config.modalConfig||{}},this.config.args=this.config.args||{},this.futureListeners=[]}getModalModule(){if(!this.config.moduleName&&this.config.modalConfig.type&&"SAVE_CANCEL"!==this.config.modalConfig.type)return window.console.warn("Passing config.modalConfig.type to ModalForm has been deprecated since Moodle 4.3. Please pass config.modalName instead with the full module name."),("function"==typeof _systemImportTransformerGlobalIdentifier.define&&_systemImportTransformerGlobalIdentifier.define.amd?new Promise((function(resolve,reject){_systemImportTransformerGlobalIdentifier.require(["core/modal_factory"],resolve,reject)})):"undefined"!=typeof module&&module.exports&&"undefined"!=typeof require||"undefined"!=typeof module&&module.component&&_systemImportTransformerGlobalIdentifier.require&&"component"===_systemImportTransformerGlobalIdentifier.require.loader?Promise.resolve(require("core/modal_factory")):Promise.resolve(_systemImportTransformerGlobalIdentifier["core/modal_factory"])).then((ModalFactory=>ModalFactory.create(this.config.modalConfig)));{var _this$config$moduleNa;const moduleName=null!==(_this$config$moduleNa=this.config.moduleName)&&void 0!==_this$config$moduleNa?_this$config$moduleNa:"core/modal_save_cancel";return("function"==typeof _systemImportTransformerGlobalIdentifier.define&&_systemImportTransformerGlobalIdentifier.define.amd?new Promise((function(resolve,reject){_systemImportTransformerGlobalIdentifier.require([moduleName],resolve,reject)})):"undefined"!=typeof module&&module.exports&&"undefined"!=typeof require||"undefined"!=typeof module&&module.component&&_systemImportTransformerGlobalIdentifier.require&&"component"===_systemImportTransformerGlobalIdentifier.require.loader?Promise.resolve(require(moduleName)):Promise.resolve(_systemImportTransformerGlobalIdentifier[moduleName])).then((module=>module.create(this.config.modalConfig)))}}show(){const pendingPromise=new _pending.default("core_form/modalform:init");return this.getModalModule().then((modal=>{this.modal=modal;const formParams=(0,_util.serialize)(this.config.args||{}),bodyContent=this.getBody(formParams);return this.modal.setBodyContent(bodyContent),bodyContent.catch(_notification.default.exception),this.modal.getRoot().on(_modal_events.default.hidden,(()=>{this.notifyResetFormChanges(),this.modal.destroy(),this.config.returnFocus&&this.config.returnFocus.focus()})),this.modal.getModal().addClass("modal-form-dialogue"),this.modal.getRoot().on("click","form input[type=submit][data-no-submit]",(e=>{e.preventDefault();this.trigger(this.events.NOSUBMIT_BUTTON_PRESSED,e.target).defaultPrevented||this.processNoSubmitButton(e.target)})),this.modal.getRoot().on("submit","form",(e=>{e.preventDefault();this.trigger(this.events.SUBMIT_BUTTON_PRESSED).defaultPrevented||this.submitFormAjax()})),void 0!==this.config.saveButtonText&&void 0!==this.modal.setSaveButtonText&&this.modal.setSaveButtonText(this.config.saveButtonText),void 0!==this.config.saveButtonClasses&&this.setSaveButtonClasses(this.config.saveButtonClasses),this.modal.getRoot().on(_modal_events.default.save,(e=>{e.preventDefault(),this.modal.getRoot().find("form").submit()})),this.modal.getRoot().on(_modal_events.default.cancel,(e=>{this.trigger(this.events.CANCEL_BUTTON_PRESSED).defaultPrevented&&e.preventDefault()})),this.futureListeners.forEach((args=>this.modal.getRoot()[0].addEventListener(...args))),this.futureListeners=[],this.trigger(this.events.LOADED,null,!1),this.modal.show()})).then(pendingPromise.resolve)}trigger(eventName){const e=new CustomEvent(eventName,{detail:arguments.length>1&&void 0!==arguments[1]?arguments[1]:null,cancelable:!(arguments.length>2&&void 0!==arguments[2])||arguments[2]});return this.modal.getRoot()[0].dispatchEvent(e),e}addEventListener(){for(var _len=arguments.length,args=new Array(_len),_key=0;_key<_len;_key++)args[_key]=arguments[_key];this.modal?this.modal.getRoot()[0].addEventListener(...args):this.futureListeners.push(args)}getBody(formDataString){const params={formdata:formDataString,form:this.config.formClass},pendingPromise=new _pending.default("core_form/modalform:form_body");return _ajax.default.call([{methodname:"core_form_dynamic_form",args:params}])[0].then((response=>(pendingPromise.resolve(),{html:response.html,js:_fragment.default.processCollectedJavascript(response.javascript)}))).catch((exception=>this.onSubmitError(exception)))}onSubmitError(exception){this.trigger(this.events.ERROR,exception).defaultPrevented||_notification.default.exception(exception)}notifyResetFormChanges(){const form=this.getFormNode();form&&(FormEvents.notifyFormSubmittedByJavascript(form,!0),FormChangeChecker.resetFormDirtyState(form))}getFormNode(){return this.modal.getRoot().find("form")[0]}processNoSubmitButton(button){const form=this.getFormNode();if(!form)return;FormEvents.notifyFormSubmittedByJavascript(form,!0);let formData=this.modal.getRoot().find("form").serialize();formData=formData+"&"+encodeURIComponent(button.getAttribute("name"))+"="+encodeURIComponent(button.getAttribute("value")),this.disableButtons();const bodyContent=this.getBody(formData);this.modal.setBodyContent(bodyContent),bodyContent.then((()=>this.enableButtons())).catch(_notification.default.exception)}validateElements(){FormEvents.notifyFormSubmittedByJavascript(this.getFormNode());const invalid=this.modal.getRoot().find('[aria-invalid="true"], .error');return!invalid.length||(invalid.first().focus(),!1)}disableButtons(){this.modal.getModal()[0].querySelectorAll('input[type="submit"], [data-action]').forEach((el=>el.setAttribute("disabled",!0)))}enableButtons(){this.modal.getModal()[0].querySelectorAll('input[type="submit"], [data-action]').forEach((el=>el.removeAttribute("disabled")))}async submitFormAjax(){if(!this.validateElements())return void this.trigger(this.events.CLIENT_VALIDATION_ERROR,null,!1);this.disableButtons();const form=this.modal.getRoot().find("form"),formData=form.serialize();_ajax.default.call([{methodname:"core_form_dynamic_form",args:{formdata:formData,form:this.config.formClass}}])[0].then((response=>{if(response.submitted){const data=JSON.parse(response.data);FormChangeChecker.markFormSubmitted(form[0]);this.trigger(this.events.FORM_SUBMITTED,data).defaultPrevented||this.modal.hide()}else{const promise=new Promise((resolve=>resolve({html:response.html,js:_fragment.default.processCollectedJavascript(response.javascript)})));this.modal.setBodyContent(promise),this.enableButtons(),this.trigger(this.events.SERVER_VALIDATION_ERROR)}return null})).catch((exception=>{this.enableButtons(),this.onSubmitError(exception)}))}setSaveButtonClasses(value){const button=this.modal.getFooter().find("[data-action='save']");if(!button)throw new Error("Unable to find the 'save' button");button.removeClass().addClass(value)}},_exports.default}));
+define("core_form/modalform",["exports","core/ajax","core_form/changechecker","core_form/events","core/fragment","core/modal_events","core/notification","core/pending","./util"],(function(_exports,_ajax,FormChangeChecker,FormEvents,_fragment,_modal_events,_notification,_pending,_util){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_ajax=_interopRequireDefault(_ajax),FormChangeChecker=_interopRequireWildcard(FormChangeChecker),FormEvents=_interopRequireWildcard(FormEvents),_fragment=_interopRequireDefault(_fragment),_modal_events=_interopRequireDefault(_modal_events),_notification=_interopRequireDefault(_notification),_pending=_interopRequireDefault(_pending);var _systemImportTransformerGlobalIdentifier="undefined"!=typeof window?window:"undefined"!=typeof self?self:"undefined"!=typeof global?global:{};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}}return _exports.default=class{constructor(config){var obj,key,value;value={FORM_SUBMITTED:"core_form_modalform_formsubmitted",FORM_CANCELLED:"core_form_modalform_formcancelled",CLIENT_VALIDATION_ERROR:"core_form_modalform_clientvalidationerror",SERVER_VALIDATION_ERROR:"core_form_modalform_validationerror",ERROR:"core_form_modalform_error",NOSUBMIT_BUTTON_PRESSED:"core_form_modalform_nosubmitbutton",SUBMIT_BUTTON_PRESSED:"core_form_modalform_submitbutton",CANCEL_BUTTON_PRESSED:"core_form_modalform_cancelbutton",LOADED:"core_form_modalform_loaded"},(key="events")in(obj=this)?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value,this.modal=null,this.config=config,this.config.modalConfig={removeOnClose:!0,large:!0,...this.config.modalConfig||{}},this.config.args=this.config.args||{},this.futureListeners=[]}getModalModule(){if(!this.config.moduleName&&this.config.modalConfig.type&&"SAVE_CANCEL"!==this.config.modalConfig.type)return window.console.warn("Passing config.modalConfig.type to ModalForm has been deprecated since Moodle 4.3. Please pass config.modalName instead with the full module name."),("function"==typeof _systemImportTransformerGlobalIdentifier.define&&_systemImportTransformerGlobalIdentifier.define.amd?new Promise((function(resolve,reject){_systemImportTransformerGlobalIdentifier.require(["core/modal"],resolve,reject)})):"undefined"!=typeof module&&module.exports&&"undefined"!=typeof require||"undefined"!=typeof module&&module.component&&_systemImportTransformerGlobalIdentifier.require&&"component"===_systemImportTransformerGlobalIdentifier.require.loader?Promise.resolve(require("core/modal")):Promise.resolve(_systemImportTransformerGlobalIdentifier["core/modal"])).then((Modal=>Modal.create(this.config.modalConfig)));{var _this$config$moduleNa;const moduleName=null!==(_this$config$moduleNa=this.config.moduleName)&&void 0!==_this$config$moduleNa?_this$config$moduleNa:"core/modal_save_cancel";return("function"==typeof _systemImportTransformerGlobalIdentifier.define&&_systemImportTransformerGlobalIdentifier.define.amd?new Promise((function(resolve,reject){_systemImportTransformerGlobalIdentifier.require([moduleName],resolve,reject)})):"undefined"!=typeof module&&module.exports&&"undefined"!=typeof require||"undefined"!=typeof module&&module.component&&_systemImportTransformerGlobalIdentifier.require&&"component"===_systemImportTransformerGlobalIdentifier.require.loader?Promise.resolve(require(moduleName)):Promise.resolve(_systemImportTransformerGlobalIdentifier[moduleName])).then((module=>module.create(this.config.modalConfig)))}}show(){const pendingPromise=new _pending.default("core_form/modalform:init");return this.getModalModule().then((modal=>{this.modal=modal;const formParams=(0,_util.serialize)(this.config.args||{}),bodyContent=this.getBody(formParams);return this.modal.setBodyContent(bodyContent),bodyContent.catch(_notification.default.exception),this.modal.getRoot().on(_modal_events.default.hidden,(()=>{this.notifyResetFormChanges(),this.modal.destroy(),this.config.returnFocus&&this.config.returnFocus.focus()})),this.modal.getModal().addClass("modal-form-dialogue"),this.modal.getRoot().on("click","form input[type=submit][data-no-submit]",(e=>{e.preventDefault();this.trigger(this.events.NOSUBMIT_BUTTON_PRESSED,e.target).defaultPrevented||this.processNoSubmitButton(e.target)})),this.modal.getRoot().on("submit","form",(e=>{e.preventDefault();this.trigger(this.events.SUBMIT_BUTTON_PRESSED).defaultPrevented||this.submitFormAjax()})),void 0!==this.config.saveButtonText&&void 0!==this.modal.setSaveButtonText&&this.modal.setSaveButtonText(this.config.saveButtonText),void 0!==this.config.saveButtonClasses&&this.setSaveButtonClasses(this.config.saveButtonClasses),this.modal.getRoot().on(_modal_events.default.save,(e=>{e.preventDefault(),this.modal.getRoot().find("form").submit()})),this.modal.getRoot().on(_modal_events.default.cancel,(e=>{this.trigger(this.events.CANCEL_BUTTON_PRESSED).defaultPrevented&&e.preventDefault()})),this.futureListeners.forEach((args=>this.modal.getRoot()[0].addEventListener(...args))),this.futureListeners=[],this.trigger(this.events.LOADED,null,!1),this.modal.show()})).then(pendingPromise.resolve)}trigger(eventName){const e=new CustomEvent(eventName,{detail:arguments.length>1&&void 0!==arguments[1]?arguments[1]:null,cancelable:!(arguments.length>2&&void 0!==arguments[2])||arguments[2]});return this.modal.getRoot()[0].dispatchEvent(e),e}addEventListener(){for(var _len=arguments.length,args=new Array(_len),_key=0;_key<_len;_key++)args[_key]=arguments[_key];this.modal?this.modal.getRoot()[0].addEventListener(...args):this.futureListeners.push(args)}getBody(formDataString){const params={formdata:formDataString,form:this.config.formClass},pendingPromise=new _pending.default("core_form/modalform:form_body");return _ajax.default.call([{methodname:"core_form_dynamic_form",args:params}])[0].then((response=>(pendingPromise.resolve(),{html:response.html,js:_fragment.default.processCollectedJavascript(response.javascript)}))).catch((exception=>this.onSubmitError(exception)))}onSubmitError(exception){this.trigger(this.events.ERROR,exception).defaultPrevented||_notification.default.exception(exception)}notifyResetFormChanges(){const form=this.getFormNode();form&&(FormEvents.notifyFormSubmittedByJavascript(form,!0),FormChangeChecker.resetFormDirtyState(form))}getFormNode(){return this.modal.getRoot().find("form")[0]}processNoSubmitButton(button){const form=this.getFormNode();if(!form)return;FormEvents.notifyFormSubmittedByJavascript(form,!0);let formData=this.modal.getRoot().find("form").serialize();formData=formData+"&"+encodeURIComponent(button.getAttribute("name"))+"="+encodeURIComponent(button.getAttribute("value")),this.disableButtons();const bodyContent=this.getBody(formData);this.modal.setBodyContent(bodyContent),bodyContent.then((()=>this.enableButtons())).catch(_notification.default.exception)}validateElements(){FormEvents.notifyFormSubmittedByJavascript(this.getFormNode());const invalid=this.modal.getRoot().find('[aria-invalid="true"], .error');return!invalid.length||(invalid.first().focus(),!1)}disableButtons(){this.modal.getModal()[0].querySelectorAll('input[type="submit"], [data-action]').forEach((el=>el.setAttribute("disabled",!0)))}enableButtons(){this.modal.getModal()[0].querySelectorAll('input[type="submit"], [data-action]').forEach((el=>el.removeAttribute("disabled")))}async submitFormAjax(){if(!this.validateElements())return void this.trigger(this.events.CLIENT_VALIDATION_ERROR,null,!1);this.disableButtons();const form=this.modal.getRoot().find("form"),formData=form.serialize();_ajax.default.call([{methodname:"core_form_dynamic_form",args:{formdata:formData,form:this.config.formClass}}])[0].then((response=>{if(response.submitted){const data=JSON.parse(response.data);FormChangeChecker.markFormSubmitted(form[0]);this.trigger(this.events.FORM_SUBMITTED,data).defaultPrevented||this.modal.hide()}else{const promise=new Promise((resolve=>resolve({html:response.html,js:_fragment.default.processCollectedJavascript(response.javascript)})));this.modal.setBodyContent(promise),this.enableButtons(),this.trigger(this.events.SERVER_VALIDATION_ERROR)}return null})).catch((exception=>{this.enableButtons(),this.onSubmitError(exception)}))}setSaveButtonClasses(value){const button=this.modal.getFooter().find("[data-action='save']");if(!button)throw new Error("Unable to find the 'save' button");button.removeClass().addClass(value)}},_exports.default}));
//# sourceMappingURL=modalform.min.js.map
\ No newline at end of file
diff --git a/public/lib/form/amd/build/modalform.min.js.map b/public/lib/form/amd/build/modalform.min.js.map
index e5d55264455..051840d5642 100644
--- a/public/lib/form/amd/build/modalform.min.js.map
+++ b/public/lib/form/amd/build/modalform.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"modalform.min.js","sources":["../src/modalform.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Display a form in a modal dialogue\n *\n * Example:\n * import ModalForm from 'core_form/modalform';\n *\n * const modalForm = new ModalForm({\n * formClass: 'pluginname\\\\form\\\\formname',\n * modalConfig: {title: 'Here comes the title'},\n * args: {categoryid: 123},\n * returnFocus: e.target,\n * });\n * modalForm.addEventListener(modalForm.events.FORM_SUBMITTED, (c) => window.console.log(c.detail));\n * modalForm.show();\n *\n * See also https://docs.moodle.org/dev/Modal_and_AJAX_forms\n *\n * @module core_form/modalform\n * @copyright 2018 Mitxel Moriana \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Ajax from 'core/ajax';\nimport * as FormChangeChecker from 'core_form/changechecker';\nimport * as FormEvents from 'core_form/events';\nimport Fragment from 'core/fragment';\nimport ModalEvents from 'core/modal_events';\nimport Notification from 'core/notification';\nimport Pending from 'core/pending';\nimport {serialize} from './util';\n\nexport default class ModalForm {\n\n /**\n * Various events that can be observed.\n *\n * @type {Object}\n */\n events = {\n // Form was successfully submitted - the response is passed to the event listener.\n // Cancellable (but it's hardly ever needed to cancel this event).\n FORM_SUBMITTED: 'core_form_modalform_formsubmitted',\n // Cancel button was pressed.\n // Cancellable (but it's hardly ever needed to cancel this event).\n FORM_CANCELLED: 'core_form_modalform_formcancelled',\n // User attempted to submit the form but there was client-side validation error.\n CLIENT_VALIDATION_ERROR: 'core_form_modalform_clientvalidationerror',\n // User attempted to submit the form but server returned validation error.\n SERVER_VALIDATION_ERROR: 'core_form_modalform_validationerror',\n // Error occurred while performing request to the server.\n // Cancellable (by default calls Notification.exception).\n ERROR: 'core_form_modalform_error',\n // Right after user pressed no-submit button,\n // listen to this event if you want to add JS validation or processing for no-submit button.\n // Cancellable.\n NOSUBMIT_BUTTON_PRESSED: 'core_form_modalform_nosubmitbutton',\n // Right after user pressed submit button,\n // listen to this event if you want to add additional JS validation or confirmation dialog.\n // Cancellable.\n SUBMIT_BUTTON_PRESSED: 'core_form_modalform_submitbutton',\n // Right after user pressed cancel button,\n // listen to this event if you want to add confirmation dialog.\n // Cancellable.\n CANCEL_BUTTON_PRESSED: 'core_form_modalform_cancelbutton',\n // Modal was loaded and this.modal is available (but the form content may not be loaded yet).\n LOADED: 'core_form_modalform_loaded',\n };\n\n /**\n * Constructor\n *\n * Shows the required form inside a modal dialogue\n *\n * @param {Object} config parameters for the form and modal dialogue:\n * @paramy {String} config.formClass PHP class name that handles the form (should extend \\core_form\\modal )\n * @paramy {String} config.moduleName module name to use if different to core/modal_save_cancel (optional)\n * @paramy {Object} config.modalConfig modal config - title, header, footer, etc.\n * Default: {removeOnClose: true, large: true}\n * @paramy {Object} config.args Arguments for the initial form rendering (for example, id of the edited entity)\n * @paramy {String} config.saveButtonText the text to display on the Modal \"Save\" button (optional)\n * @paramy {String} config.saveButtonClasses additional CSS classes for the Modal \"Save\" button\n * @paramy {HTMLElement} config.returnFocus element to return focus to after the dialogue is closed\n */\n constructor(config) {\n this.modal = null;\n this.config = config;\n this.config.modalConfig = {\n removeOnClose: true,\n large: true,\n ...(this.config.modalConfig || {}),\n };\n this.config.args = this.config.args || {};\n this.futureListeners = [];\n }\n\n /**\n * Loads the modal module and creates an instance\n *\n * @returns {Promise}\n */\n getModalModule() {\n if (!this.config.moduleName && this.config.modalConfig.type && this.config.modalConfig.type !== 'SAVE_CANCEL') {\n // Legacy loader for plugins that were not updated with Moodle 4.3 changes.\n window.console.warn(\n 'Passing config.modalConfig.type to ModalForm has been deprecated since Moodle 4.3. ' +\n 'Please pass config.modalName instead with the full module name.',\n );\n return import('core/modal_factory')\n .then((ModalFactory) => ModalFactory.create(this.config.modalConfig));\n } else {\n // New loader for Moodle 4.3 and above.\n const moduleName = this.config.moduleName ?? 'core/modal_save_cancel';\n return import(moduleName)\n .then((module) => module.create(this.config.modalConfig));\n }\n }\n\n /**\n * Initialise the modal and shows it\n *\n * @return {Promise}\n */\n show() {\n const pendingPromise = new Pending('core_form/modalform:init');\n\n return this.getModalModule()\n .then((modal) => {\n this.modal = modal;\n\n // Retrieve the form and set the modal body. We can not set the body in the modalConfig,\n // we need to make sure that the modal already exists when we render the form. Some form elements\n // such as date_selector inspect the existing elements on the page to find the highest z-index.\n const formParams = serialize(this.config.args || {});\n const bodyContent = this.getBody(formParams);\n this.modal.setBodyContent(bodyContent);\n bodyContent.catch(Notification.exception);\n\n // After successfull submit, when we press \"Cancel\" or close the dialogue by clicking on X in the top right corner.\n this.modal.getRoot().on(ModalEvents.hidden, () => {\n this.notifyResetFormChanges();\n this.modal.destroy();\n // Focus on the element that actually launched the modal.\n if (this.config.returnFocus) {\n this.config.returnFocus.focus();\n }\n });\n\n // Add the class to the modal dialogue.\n this.modal.getModal().addClass('modal-form-dialogue');\n\n // We catch the press on submit buttons in the forms.\n this.modal.getRoot().on('click', 'form input[type=submit][data-no-submit]',\n (e) => {\n e.preventDefault();\n const event = this.trigger(this.events.NOSUBMIT_BUTTON_PRESSED, e.target);\n if (!event.defaultPrevented) {\n this.processNoSubmitButton(e.target);\n }\n });\n\n // We catch the form submit event and use it to submit the form with ajax.\n this.modal.getRoot().on('submit', 'form', (e) => {\n e.preventDefault();\n const event = this.trigger(this.events.SUBMIT_BUTTON_PRESSED);\n if (!event.defaultPrevented) {\n this.submitFormAjax();\n }\n });\n\n // Change the text for the save button.\n if (typeof this.config.saveButtonText !== 'undefined' &&\n typeof this.modal.setSaveButtonText !== 'undefined') {\n this.modal.setSaveButtonText(this.config.saveButtonText);\n }\n // Set classes for the save button.\n if (typeof this.config.saveButtonClasses !== 'undefined') {\n this.setSaveButtonClasses(this.config.saveButtonClasses);\n }\n // When Save button is pressed - submit the form.\n this.modal.getRoot().on(ModalEvents.save, (e) => {\n e.preventDefault();\n this.modal.getRoot().find('form').submit();\n });\n\n // When Cancel button is pressed - allow to intercept.\n this.modal.getRoot().on(ModalEvents.cancel, (e) => {\n const event = this.trigger(this.events.CANCEL_BUTTON_PRESSED);\n if (event.defaultPrevented) {\n e.preventDefault();\n }\n });\n this.futureListeners.forEach(args => this.modal.getRoot()[0].addEventListener(...args));\n this.futureListeners = [];\n this.trigger(this.events.LOADED, null, false);\n return this.modal.show();\n })\n .then(pendingPromise.resolve);\n }\n\n /**\n * Triggers a custom event\n *\n * @private\n * @param {String} eventName\n * @param {*} detail\n * @param {Boolean} cancelable\n * @return {CustomEvent}\n */\n trigger(eventName, detail = null, cancelable = true) {\n const e = new CustomEvent(eventName, {detail, cancelable});\n this.modal.getRoot()[0].dispatchEvent(e);\n return e;\n }\n\n /**\n * Add listener for an event\n *\n * @param {array} args\n * @example:\n * const modalForm = new ModalForm(...);\n * dynamicForm.addEventListener(modalForm.events.FORM_SUBMITTED, e => {\n * window.console.log(e.detail);\n * });\n */\n addEventListener(...args) {\n if (!this.modal) {\n this.futureListeners.push(args);\n } else {\n this.modal.getRoot()[0].addEventListener(...args);\n }\n }\n\n /**\n * Get form contents (to be used in ModalForm.setBodyContent())\n *\n * @param {String} formDataString form data in format of a query string\n * @method getBody\n * @private\n * @return {Promise}\n */\n getBody(formDataString) {\n const params = {\n formdata: formDataString,\n form: this.config.formClass\n };\n const pendingPromise = new Pending('core_form/modalform:form_body');\n return Ajax.call([{\n methodname: 'core_form_dynamic_form',\n args: params\n }])[0]\n .then(response => {\n pendingPromise.resolve();\n return {html: response.html, js: Fragment.processCollectedJavascript(response.javascript)};\n })\n .catch(exception => this.onSubmitError(exception));\n }\n\n /**\n * On exception during form processing or initial rendering. Caller may override.\n *\n * @param {Object} exception\n */\n onSubmitError(exception) {\n const event = this.trigger(this.events.ERROR, exception);\n if (event.defaultPrevented) {\n return;\n }\n\n Notification.exception(exception);\n }\n\n /**\n * Notifies listeners that form dirty state should be reset.\n *\n * @fires event:formSubmittedByJavascript\n */\n notifyResetFormChanges() {\n const form = this.getFormNode();\n if (!form) {\n return;\n }\n\n FormEvents.notifyFormSubmittedByJavascript(form, true);\n\n FormChangeChecker.resetFormDirtyState(form);\n }\n\n /**\n * Get the form node from the Dialogue.\n *\n * @returns {HTMLFormElement}\n */\n getFormNode() {\n return this.modal.getRoot().find('form')[0];\n }\n\n /**\n * Click on a \"submit\" button that is marked in the form as registerNoSubmitButton()\n *\n * @param {Element} button button that was pressed\n * @fires event:formSubmittedByJavascript\n */\n processNoSubmitButton(button) {\n const form = this.getFormNode();\n if (!form) {\n return;\n }\n\n FormEvents.notifyFormSubmittedByJavascript(form, true);\n\n // Add the button name to the form data and submit it.\n let formData = this.modal.getRoot().find('form').serialize();\n formData = formData + '&' + encodeURIComponent(button.getAttribute('name')) + '=' +\n encodeURIComponent(button.getAttribute('value'));\n\n this.disableButtons();\n\n const bodyContent = this.getBody(formData);\n this.modal.setBodyContent(bodyContent);\n bodyContent\n .then(() => this.enableButtons())\n .catch(Notification.exception);\n }\n\n /**\n * Validate form elements\n * @return {Boolean} Whether client-side validation has passed, false if there are errors\n * @fires event:formSubmittedByJavascript\n */\n validateElements() {\n FormEvents.notifyFormSubmittedByJavascript(this.getFormNode());\n\n // Now the change events have run, see if there are any \"invalid\" form fields.\n /** @var {jQuery} list of elements with errors */\n const invalid = this.modal.getRoot().find('[aria-invalid=\"true\"], .error');\n\n // If we found invalid fields, focus on the first one and do not submit via ajax.\n if (invalid.length) {\n invalid.first().focus();\n return false;\n }\n\n return true;\n }\n\n /**\n * Disable buttons during form submission\n */\n disableButtons() {\n this.modal.getModal()[0].querySelectorAll('input[type=\"submit\"], [data-action]')\n .forEach(el => el.setAttribute('disabled', true));\n }\n\n /**\n * Enable buttons after form submission (on validation error)\n */\n enableButtons() {\n this.modal.getModal()[0].querySelectorAll('input[type=\"submit\"], [data-action]')\n .forEach(el => el.removeAttribute('disabled'));\n }\n\n /**\n * Submit the form via AJAX call to the core_form_dynamic_form WS\n */\n async submitFormAjax() {\n // If we found invalid fields, focus on the first one and do not submit via ajax.\n if (!this.validateElements()) {\n this.trigger(this.events.CLIENT_VALIDATION_ERROR, null, false);\n return;\n }\n this.disableButtons();\n\n // Convert all the form elements values to a serialised string.\n const form = this.modal.getRoot().find('form');\n const formData = form.serialize();\n\n // Now we can continue...\n Ajax.call([{\n methodname: 'core_form_dynamic_form',\n args: {\n formdata: formData,\n form: this.config.formClass\n }\n }])[0]\n .then((response) => {\n if (!response.submitted) {\n // Form was not submitted because validation failed.\n const promise = new Promise(\n resolve => resolve({html: response.html, js: Fragment.processCollectedJavascript(response.javascript)}));\n this.modal.setBodyContent(promise);\n this.enableButtons();\n this.trigger(this.events.SERVER_VALIDATION_ERROR);\n } else {\n // Form was submitted properly. Hide the modal and execute callback.\n const data = JSON.parse(response.data);\n FormChangeChecker.markFormSubmitted(form[0]);\n const event = this.trigger(this.events.FORM_SUBMITTED, data);\n if (!event.defaultPrevented) {\n this.modal.hide();\n }\n }\n return null;\n })\n .catch(exception => {\n this.enableButtons();\n this.onSubmitError(exception);\n });\n }\n\n /**\n * Set the classes for the 'save' button.\n *\n * @method setSaveButtonClasses\n * @param {(String)} value The 'save' button classes.\n */\n setSaveButtonClasses(value) {\n const button = this.modal.getFooter().find(\"[data-action='save']\");\n if (!button) {\n throw new Error(\"Unable to find the 'save' button\");\n }\n button.removeClass().addClass(value);\n }\n}\n"],"names":["constructor","config","FORM_SUBMITTED","FORM_CANCELLED","CLIENT_VALIDATION_ERROR","SERVER_VALIDATION_ERROR","ERROR","NOSUBMIT_BUTTON_PRESSED","SUBMIT_BUTTON_PRESSED","CANCEL_BUTTON_PRESSED","LOADED","modal","modalConfig","removeOnClose","large","this","args","futureListeners","getModalModule","moduleName","type","window","console","warn","then","ModalFactory","create","module","show","pendingPromise","Pending","formParams","bodyContent","getBody","setBodyContent","catch","Notification","exception","getRoot","on","ModalEvents","hidden","notifyResetFormChanges","destroy","returnFocus","focus","getModal","addClass","e","preventDefault","trigger","events","target","defaultPrevented","processNoSubmitButton","submitFormAjax","saveButtonText","setSaveButtonText","saveButtonClasses","setSaveButtonClasses","save","find","submit","cancel","forEach","addEventListener","resolve","eventName","CustomEvent","detail","cancelable","dispatchEvent","push","formDataString","params","formdata","form","formClass","Ajax","call","methodname","response","html","js","Fragment","processCollectedJavascript","javascript","onSubmitError","getFormNode","FormEvents","notifyFormSubmittedByJavascript","FormChangeChecker","resetFormDirtyState","button","formData","serialize","encodeURIComponent","getAttribute","disableButtons","enableButtons","validateElements","invalid","length","first","querySelectorAll","el","setAttribute","removeAttribute","submitted","data","JSON","parse","markFormSubmitted","hide","promise","Promise","value","getFooter","Error","removeClass"],"mappings":"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"}
\ No newline at end of file
+{"version":3,"file":"modalform.min.js","sources":["../src/modalform.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Display a form in a modal dialogue\n *\n * Example:\n * import ModalForm from 'core_form/modalform';\n *\n * const modalForm = new ModalForm({\n * formClass: 'pluginname\\\\form\\\\formname',\n * modalConfig: {title: 'Here comes the title'},\n * args: {categoryid: 123},\n * returnFocus: e.target,\n * });\n * modalForm.addEventListener(modalForm.events.FORM_SUBMITTED, (c) => window.console.log(c.detail));\n * modalForm.show();\n *\n * See also https://docs.moodle.org/dev/Modal_and_AJAX_forms\n *\n * @module core_form/modalform\n * @copyright 2018 Mitxel Moriana \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Ajax from 'core/ajax';\nimport * as FormChangeChecker from 'core_form/changechecker';\nimport * as FormEvents from 'core_form/events';\nimport Fragment from 'core/fragment';\nimport ModalEvents from 'core/modal_events';\nimport Notification from 'core/notification';\nimport Pending from 'core/pending';\nimport {serialize} from './util';\n\nexport default class ModalForm {\n\n /**\n * Various events that can be observed.\n *\n * @type {Object}\n */\n events = {\n // Form was successfully submitted - the response is passed to the event listener.\n // Cancellable (but it's hardly ever needed to cancel this event).\n FORM_SUBMITTED: 'core_form_modalform_formsubmitted',\n // Cancel button was pressed.\n // Cancellable (but it's hardly ever needed to cancel this event).\n FORM_CANCELLED: 'core_form_modalform_formcancelled',\n // User attempted to submit the form but there was client-side validation error.\n CLIENT_VALIDATION_ERROR: 'core_form_modalform_clientvalidationerror',\n // User attempted to submit the form but server returned validation error.\n SERVER_VALIDATION_ERROR: 'core_form_modalform_validationerror',\n // Error occurred while performing request to the server.\n // Cancellable (by default calls Notification.exception).\n ERROR: 'core_form_modalform_error',\n // Right after user pressed no-submit button,\n // listen to this event if you want to add JS validation or processing for no-submit button.\n // Cancellable.\n NOSUBMIT_BUTTON_PRESSED: 'core_form_modalform_nosubmitbutton',\n // Right after user pressed submit button,\n // listen to this event if you want to add additional JS validation or confirmation dialog.\n // Cancellable.\n SUBMIT_BUTTON_PRESSED: 'core_form_modalform_submitbutton',\n // Right after user pressed cancel button,\n // listen to this event if you want to add confirmation dialog.\n // Cancellable.\n CANCEL_BUTTON_PRESSED: 'core_form_modalform_cancelbutton',\n // Modal was loaded and this.modal is available (but the form content may not be loaded yet).\n LOADED: 'core_form_modalform_loaded',\n };\n\n /**\n * Constructor\n *\n * Shows the required form inside a modal dialogue\n *\n * @param {Object} config parameters for the form and modal dialogue:\n * @paramy {String} config.formClass PHP class name that handles the form (should extend \\core_form\\modal )\n * @paramy {String} config.moduleName module name to use if different to core/modal_save_cancel (optional)\n * @paramy {Object} config.modalConfig modal config - title, header, footer, etc.\n * Default: {removeOnClose: true, large: true}\n * @paramy {Object} config.args Arguments for the initial form rendering (for example, id of the edited entity)\n * @paramy {String} config.saveButtonText the text to display on the Modal \"Save\" button (optional)\n * @paramy {String} config.saveButtonClasses additional CSS classes for the Modal \"Save\" button\n * @paramy {HTMLElement} config.returnFocus element to return focus to after the dialogue is closed\n */\n constructor(config) {\n this.modal = null;\n this.config = config;\n this.config.modalConfig = {\n removeOnClose: true,\n large: true,\n ...(this.config.modalConfig || {}),\n };\n this.config.args = this.config.args || {};\n this.futureListeners = [];\n }\n\n /**\n * Loads the modal module and creates an instance\n *\n * @returns {Promise}\n */\n getModalModule() {\n if (!this.config.moduleName && this.config.modalConfig.type && this.config.modalConfig.type !== 'SAVE_CANCEL') {\n // Legacy loader for plugins that were not updated with Moodle 4.3 changes.\n window.console.warn(\n 'Passing config.modalConfig.type to ModalForm has been deprecated since Moodle 4.3. ' +\n 'Please pass config.modalName instead with the full module name.',\n );\n return import('core/modal')\n .then((Modal) => Modal.create(this.config.modalConfig));\n } else {\n // New loader for Moodle 4.3 and above.\n const moduleName = this.config.moduleName ?? 'core/modal_save_cancel';\n return import(moduleName)\n .then((module) => module.create(this.config.modalConfig));\n }\n }\n\n /**\n * Initialise the modal and shows it\n *\n * @return {Promise}\n */\n show() {\n const pendingPromise = new Pending('core_form/modalform:init');\n\n return this.getModalModule()\n .then((modal) => {\n this.modal = modal;\n\n // Retrieve the form and set the modal body. We can not set the body in the modalConfig,\n // we need to make sure that the modal already exists when we render the form. Some form elements\n // such as date_selector inspect the existing elements on the page to find the highest z-index.\n const formParams = serialize(this.config.args || {});\n const bodyContent = this.getBody(formParams);\n this.modal.setBodyContent(bodyContent);\n bodyContent.catch(Notification.exception);\n\n // After successfull submit, when we press \"Cancel\" or close the dialogue by clicking on X in the top right corner.\n this.modal.getRoot().on(ModalEvents.hidden, () => {\n this.notifyResetFormChanges();\n this.modal.destroy();\n // Focus on the element that actually launched the modal.\n if (this.config.returnFocus) {\n this.config.returnFocus.focus();\n }\n });\n\n // Add the class to the modal dialogue.\n this.modal.getModal().addClass('modal-form-dialogue');\n\n // We catch the press on submit buttons in the forms.\n this.modal.getRoot().on('click', 'form input[type=submit][data-no-submit]',\n (e) => {\n e.preventDefault();\n const event = this.trigger(this.events.NOSUBMIT_BUTTON_PRESSED, e.target);\n if (!event.defaultPrevented) {\n this.processNoSubmitButton(e.target);\n }\n });\n\n // We catch the form submit event and use it to submit the form with ajax.\n this.modal.getRoot().on('submit', 'form', (e) => {\n e.preventDefault();\n const event = this.trigger(this.events.SUBMIT_BUTTON_PRESSED);\n if (!event.defaultPrevented) {\n this.submitFormAjax();\n }\n });\n\n // Change the text for the save button.\n if (typeof this.config.saveButtonText !== 'undefined' &&\n typeof this.modal.setSaveButtonText !== 'undefined') {\n this.modal.setSaveButtonText(this.config.saveButtonText);\n }\n // Set classes for the save button.\n if (typeof this.config.saveButtonClasses !== 'undefined') {\n this.setSaveButtonClasses(this.config.saveButtonClasses);\n }\n // When Save button is pressed - submit the form.\n this.modal.getRoot().on(ModalEvents.save, (e) => {\n e.preventDefault();\n this.modal.getRoot().find('form').submit();\n });\n\n // When Cancel button is pressed - allow to intercept.\n this.modal.getRoot().on(ModalEvents.cancel, (e) => {\n const event = this.trigger(this.events.CANCEL_BUTTON_PRESSED);\n if (event.defaultPrevented) {\n e.preventDefault();\n }\n });\n this.futureListeners.forEach(args => this.modal.getRoot()[0].addEventListener(...args));\n this.futureListeners = [];\n this.trigger(this.events.LOADED, null, false);\n return this.modal.show();\n })\n .then(pendingPromise.resolve);\n }\n\n /**\n * Triggers a custom event\n *\n * @private\n * @param {String} eventName\n * @param {*} detail\n * @param {Boolean} cancelable\n * @return {CustomEvent}\n */\n trigger(eventName, detail = null, cancelable = true) {\n const e = new CustomEvent(eventName, {detail, cancelable});\n this.modal.getRoot()[0].dispatchEvent(e);\n return e;\n }\n\n /**\n * Add listener for an event\n *\n * @param {array} args\n * @example:\n * const modalForm = new ModalForm(...);\n * dynamicForm.addEventListener(modalForm.events.FORM_SUBMITTED, e => {\n * window.console.log(e.detail);\n * });\n */\n addEventListener(...args) {\n if (!this.modal) {\n this.futureListeners.push(args);\n } else {\n this.modal.getRoot()[0].addEventListener(...args);\n }\n }\n\n /**\n * Get form contents (to be used in ModalForm.setBodyContent())\n *\n * @param {String} formDataString form data in format of a query string\n * @method getBody\n * @private\n * @return {Promise}\n */\n getBody(formDataString) {\n const params = {\n formdata: formDataString,\n form: this.config.formClass\n };\n const pendingPromise = new Pending('core_form/modalform:form_body');\n return Ajax.call([{\n methodname: 'core_form_dynamic_form',\n args: params\n }])[0]\n .then(response => {\n pendingPromise.resolve();\n return {html: response.html, js: Fragment.processCollectedJavascript(response.javascript)};\n })\n .catch(exception => this.onSubmitError(exception));\n }\n\n /**\n * On exception during form processing or initial rendering. Caller may override.\n *\n * @param {Object} exception\n */\n onSubmitError(exception) {\n const event = this.trigger(this.events.ERROR, exception);\n if (event.defaultPrevented) {\n return;\n }\n\n Notification.exception(exception);\n }\n\n /**\n * Notifies listeners that form dirty state should be reset.\n *\n * @fires event:formSubmittedByJavascript\n */\n notifyResetFormChanges() {\n const form = this.getFormNode();\n if (!form) {\n return;\n }\n\n FormEvents.notifyFormSubmittedByJavascript(form, true);\n\n FormChangeChecker.resetFormDirtyState(form);\n }\n\n /**\n * Get the form node from the Dialogue.\n *\n * @returns {HTMLFormElement}\n */\n getFormNode() {\n return this.modal.getRoot().find('form')[0];\n }\n\n /**\n * Click on a \"submit\" button that is marked in the form as registerNoSubmitButton()\n *\n * @param {Element} button button that was pressed\n * @fires event:formSubmittedByJavascript\n */\n processNoSubmitButton(button) {\n const form = this.getFormNode();\n if (!form) {\n return;\n }\n\n FormEvents.notifyFormSubmittedByJavascript(form, true);\n\n // Add the button name to the form data and submit it.\n let formData = this.modal.getRoot().find('form').serialize();\n formData = formData + '&' + encodeURIComponent(button.getAttribute('name')) + '=' +\n encodeURIComponent(button.getAttribute('value'));\n\n this.disableButtons();\n\n const bodyContent = this.getBody(formData);\n this.modal.setBodyContent(bodyContent);\n bodyContent\n .then(() => this.enableButtons())\n .catch(Notification.exception);\n }\n\n /**\n * Validate form elements\n * @return {Boolean} Whether client-side validation has passed, false if there are errors\n * @fires event:formSubmittedByJavascript\n */\n validateElements() {\n FormEvents.notifyFormSubmittedByJavascript(this.getFormNode());\n\n // Now the change events have run, see if there are any \"invalid\" form fields.\n /** @var {jQuery} list of elements with errors */\n const invalid = this.modal.getRoot().find('[aria-invalid=\"true\"], .error');\n\n // If we found invalid fields, focus on the first one and do not submit via ajax.\n if (invalid.length) {\n invalid.first().focus();\n return false;\n }\n\n return true;\n }\n\n /**\n * Disable buttons during form submission\n */\n disableButtons() {\n this.modal.getModal()[0].querySelectorAll('input[type=\"submit\"], [data-action]')\n .forEach(el => el.setAttribute('disabled', true));\n }\n\n /**\n * Enable buttons after form submission (on validation error)\n */\n enableButtons() {\n this.modal.getModal()[0].querySelectorAll('input[type=\"submit\"], [data-action]')\n .forEach(el => el.removeAttribute('disabled'));\n }\n\n /**\n * Submit the form via AJAX call to the core_form_dynamic_form WS\n */\n async submitFormAjax() {\n // If we found invalid fields, focus on the first one and do not submit via ajax.\n if (!this.validateElements()) {\n this.trigger(this.events.CLIENT_VALIDATION_ERROR, null, false);\n return;\n }\n this.disableButtons();\n\n // Convert all the form elements values to a serialised string.\n const form = this.modal.getRoot().find('form');\n const formData = form.serialize();\n\n // Now we can continue...\n Ajax.call([{\n methodname: 'core_form_dynamic_form',\n args: {\n formdata: formData,\n form: this.config.formClass\n }\n }])[0]\n .then((response) => {\n if (!response.submitted) {\n // Form was not submitted because validation failed.\n const promise = new Promise(\n resolve => resolve({html: response.html, js: Fragment.processCollectedJavascript(response.javascript)}));\n this.modal.setBodyContent(promise);\n this.enableButtons();\n this.trigger(this.events.SERVER_VALIDATION_ERROR);\n } else {\n // Form was submitted properly. Hide the modal and execute callback.\n const data = JSON.parse(response.data);\n FormChangeChecker.markFormSubmitted(form[0]);\n const event = this.trigger(this.events.FORM_SUBMITTED, data);\n if (!event.defaultPrevented) {\n this.modal.hide();\n }\n }\n return null;\n })\n .catch(exception => {\n this.enableButtons();\n this.onSubmitError(exception);\n });\n }\n\n /**\n * Set the classes for the 'save' button.\n *\n * @method setSaveButtonClasses\n * @param {(String)} value The 'save' button classes.\n */\n setSaveButtonClasses(value) {\n const button = this.modal.getFooter().find(\"[data-action='save']\");\n if (!button) {\n throw new Error(\"Unable to find the 'save' button\");\n }\n button.removeClass().addClass(value);\n }\n}\n"],"names":["constructor","config","FORM_SUBMITTED","FORM_CANCELLED","CLIENT_VALIDATION_ERROR","SERVER_VALIDATION_ERROR","ERROR","NOSUBMIT_BUTTON_PRESSED","SUBMIT_BUTTON_PRESSED","CANCEL_BUTTON_PRESSED","LOADED","modal","modalConfig","removeOnClose","large","this","args","futureListeners","getModalModule","moduleName","type","window","console","warn","then","Modal","create","module","show","pendingPromise","Pending","formParams","bodyContent","getBody","setBodyContent","catch","Notification","exception","getRoot","on","ModalEvents","hidden","notifyResetFormChanges","destroy","returnFocus","focus","getModal","addClass","e","preventDefault","trigger","events","target","defaultPrevented","processNoSubmitButton","submitFormAjax","saveButtonText","setSaveButtonText","saveButtonClasses","setSaveButtonClasses","save","find","submit","cancel","forEach","addEventListener","resolve","eventName","CustomEvent","detail","cancelable","dispatchEvent","push","formDataString","params","formdata","form","formClass","Ajax","call","methodname","response","html","js","Fragment","processCollectedJavascript","javascript","onSubmitError","getFormNode","FormEvents","notifyFormSubmittedByJavascript","FormChangeChecker","resetFormDirtyState","button","formData","serialize","encodeURIComponent","getAttribute","disableButtons","enableButtons","validateElements","invalid","length","first","querySelectorAll","el","setAttribute","removeAttribute","submitted","data","JSON","parse","markFormSubmitted","hide","promise","Promise","value","getFooter","Error","removeClass"],"mappings":"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"}
\ No newline at end of file
diff --git a/public/lib/form/amd/src/modalform.js b/public/lib/form/amd/src/modalform.js
index eb8174c0330..87395924ddd 100644
--- a/public/lib/form/amd/src/modalform.js
+++ b/public/lib/form/amd/src/modalform.js
@@ -120,8 +120,8 @@ export default class ModalForm {
'Passing config.modalConfig.type to ModalForm has been deprecated since Moodle 4.3. ' +
'Please pass config.modalName instead with the full module name.',
);
- return import('core/modal_factory')
- .then((ModalFactory) => ModalFactory.create(this.config.modalConfig));
+ return import('core/modal')
+ .then((Modal) => Modal.create(this.config.modalConfig));
} else {
// New loader for Moodle 4.3 and above.
const moduleName = this.config.moduleName ?? 'core/modal_save_cancel';