diff --git a/lib/amd/build/modal.min.js b/lib/amd/build/modal.min.js index 4071cc230fac8..d3d16251ce845 100644 --- a/lib/amd/build/modal.min.js +++ b/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"],(function(_exports,_jquery,Templates,Notification,KeyCodes,_modal_backdrop,_modal_events,ModalRegistry,_pending,CustomEvents,FilterEvents,FocusLock,Aria,Fullscreen,_toast){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.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}=await Templates.renderForPromise(templateName,templateContext),modal=new this(html);return 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.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),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"),this.root.trigger(_modal_events.default.shown,this)})).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"));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-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")}}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/modal_registry","core/pending","core/custom_interaction_events","core_filters/events","core/local/aria/focuslock","core/aria","core/fullscreen","./toast"],(function(_exports,_jquery,Templates,Notification,KeyCodes,_modal_backdrop,_modal_events,ModalRegistry,_pending,CustomEvents,FilterEvents,FocusLock,Aria,Fullscreen,_toast){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),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"),this.root.trigger(_modal_events.default.shown,this)})).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"));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-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})); //# sourceMappingURL=modal.min.js.map \ No newline at end of file diff --git a/lib/amd/build/modal.min.js.map b/lib/amd/build/modal.min.js.map index 8748a6811e770..2d2be5aea8001 100644 --- a/lib/amd/build/modal.min.js.map +++ b/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';\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\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} = await Templates.renderForPromise(templateName, templateContext);\n\n const modal = new this(html);\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.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 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 this.root.trigger(ModalEvents.shown, this);\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 }\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-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"],"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","is","Notification","exception","message","length","registerEventListeners","TYPE","Error","TEMPLATE","ModalRegistry","register","modalConfig","pendingModalPromise","Pending","type","templateName","_getTemplateName","templateContext","html","Templates","renderForPromise","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","js","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","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 * 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';\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 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 this.root.trigger(ModalEvents.shown, this);\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 }\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-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","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 diff --git a/lib/amd/src/modal.js b/lib/amd/src/modal.js index 25d826d50d8ba..0b3886a2d796b 100644 --- a/lib/amd/src/modal.js +++ b/lib/amd/src/modal.js @@ -131,6 +131,7 @@ export default class Modal { this.attachmentPoint = document.createElement('div'); document.body.append(this.attachmentPoint); this.focusOnClose = null; + this.templateJS = null; if (!this.root.is(SELECTORS.CONTAINER)) { Notification.exception({message: 'Element is not a modal container'}); @@ -194,9 +195,12 @@ export default class Modal { const templateName = this._getTemplateName(modalConfig); const templateContext = modalConfig.templateContext || {}; - const {html} = await Templates.renderForPromise(templateName, templateContext); + const {html, js} = await Templates.renderForPromise(templateName, templateContext); const modal = new this(html); + if (js) { + modal.setTemplateJS(js); + } modal.configure(modalConfig); pendingModalPromise.resolve(); @@ -303,6 +307,11 @@ export default class Modal { // If we'd cached any JS then we can run it how that the modal is // attached to the DOM. + if (this.templateJS) { + Templates.runTemplateJS(this.templateJS); + this.templateJS = null; + } + if (this.bodyJS) { Templates.runTemplateJS(this.bodyJS); this.bodyJS = null; @@ -1212,4 +1221,12 @@ export default class Modal { button.removeAttr('disabled'); } } + + /** + * Set the template JS for this modal. + * @param {String} js The JavaScript to run when the modal is attached to the DOM. + */ + setTemplateJS(js) { + this.templateJS = js; + } }