From 6b214f1c813c089a65b41b5bc46921babb12b672 Mon Sep 17 00:00:00 2001 From: Jake Dallimore Date: Mon, 18 Sep 2023 15:55:53 +0800 Subject: [PATCH] MDL-79384 core: fix modal footer logic when rendering it from template If the footer property is a Templates.render() promise, and the modal is set to show immediately (show=true), the show() logic - which contains the footer show/hide logic - can run before the footer content is set (i.e. before the promise chain is complete). In such cases, the footer will be marked as hidden and must be made visible again. --- lib/amd/build/modal.min.js | 2 +- lib/amd/build/modal.min.js.map | 2 +- lib/amd/src/modal.js | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/amd/build/modal.min.js b/lib/amd/build/modal.min.js index 1ec77fdbd36..5fe8bd5b2f2 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"],(function(_exports,_jquery,Templates,Notification,KeyCodes,_modal_backdrop,_modal_events,ModalRegistry,_pending,CustomEvents,FilterEvents,FocusLock,Aria,Fullscreen){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{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)})).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.root.trigger(_modal_events.default.hidden,this)}))}destroy(){this.hide(),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"],(function(_exports,_jquery,Templates,Notification,KeyCodes,_modal_backdrop,_modal_events,ModalRegistry,_pending,CustomEvents,FilterEvents,FocusLock,Aria,Fullscreen){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{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.root.trigger(_modal_events.default.hidden,this)}))}destroy(){this.hide(),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})); //# 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 6a6fb1f367a..65f11627b59 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';\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 * 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 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 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 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","constructor","root","modal","this","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","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';\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 * 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 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 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","constructor","root","modal","this","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","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 c0cb521de0a..02523906bc9 100644 --- a/lib/amd/src/modal.js +++ b/lib/amd/src/modal.js @@ -642,6 +642,7 @@ export default class Modal { }) .then((footer) => { this.footerPromise.resolve(footer); + this.showFooter(); return; }) .catch(Notification.exception);