diff --git a/lib/amd/build/confirm.min.js b/lib/amd/build/confirm.min.js
index 2e2f24cfdb9..d13695ad3d8 100644
--- a/lib/amd/build/confirm.min.js
+++ b/lib/amd/build/confirm.min.js
@@ -1,2 +1,2 @@
-function _typeof(a){"@babel/helpers - typeof";if("function"==typeof Symbol&&"symbol"==typeof Symbol.iterator){_typeof=function(a){return typeof a}}else{_typeof=function(a){return a&&"function"==typeof Symbol&&a.constructor===Symbol&&a!==Symbol.prototype?"symbol":typeof a}}return _typeof(a)}define ("core/confirm",["core/notification","core/str","core/toast"],function(a,b,c){"use strict";b=e(b);function d(){if("function"!=typeof WeakMap)return null;var a=new WeakMap;d=function(){return a};return a}function e(a){if(a&&a.__esModule){return a}if(null===a||"object"!==_typeof(a)&&"function"!=typeof a){return{default:a}}var b=d();if(b&&b.has(a)){return b.get(a)}var c={},e=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var f in a){if(Object.prototype.hasOwnProperty.call(a,f)){var g=e?Object.getOwnPropertyDescriptor(a,f):null;if(g&&(g.get||g.set)){Object.defineProperty(c,f,g)}else{c[f]=a[f]}}}c.default=a;if(b){b.set(a,c)}return c}var f=!1,g=function(b,c,d){return new Promise(function(e,f){(0,a.saveCancel)(b,c,d,e,f)})},h=function(a,c){if(a["confirmation".concat(c,"Str")]){return b.get_string.apply(null,JSON.parse(a["confirmation".concat(c,"Str")]))}return Promise.resolve(a["confirmation".concat(c)])},i=function(){document.addEventListener("click",function(b){var d=b.target.closest("[data-confirmation=\"modal\"]");if(d){b.preventDefault();g(h(d.dataset,"Title"),h(d.dataset,"Question"),h(d.dataset,"YesButton")).then(function(){if("true"===d.dataset.confirmationToast){var b=h(d.dataset,"ToastConfirmation");if("string"==typeof b){(0,c.add)(b)}else{b.then(function(a){return(0,c.add)(a)}).catch(function(b){return(0,a.exception)(b)})}}window.location.href=d.dataset.confirmationDestination}).catch(function(){})}})};if(!f){i();f=!0}});
+function _typeof(a){"@babel/helpers - typeof";if("function"==typeof Symbol&&"symbol"==typeof Symbol.iterator){_typeof=function(a){return typeof a}}else{_typeof=function(a){return a&&"function"==typeof Symbol&&a.constructor===Symbol&&a!==Symbol.prototype?"symbol":typeof a}}return _typeof(a)}define ("core/confirm",["core/notification","core/str","core/toast"],function(a,b,c){"use strict";b=e(b);function d(){if("function"!=typeof WeakMap)return null;var a=new WeakMap;d=function(){return a};return a}function e(a){if(a&&a.__esModule){return a}if(null===a||"object"!==_typeof(a)&&"function"!=typeof a){return{default:a}}var b=d();if(b&&b.has(a)){return b.get(a)}var c={},e=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var f in a){if(Object.prototype.hasOwnProperty.call(a,f)){var g=e?Object.getOwnPropertyDescriptor(a,f):null;if(g&&(g.get||g.set)){Object.defineProperty(c,f,g)}else{c[f]=a[f]}}}c.default=a;if(b){b.set(a,c)}return c}var f=!1,g=function(a,c){if(a["confirmation".concat(c,"Str")]){return b.get_string.apply(null,JSON.parse(a["confirmation".concat(c,"Str")]))}return Promise.resolve(a["confirmation".concat(c)])},h=function(){document.addEventListener("click",function(b){var d=b.target.closest("[data-confirmation=\"modal\"]");if(d){b.preventDefault();(0,a.saveCancelPromise)(g(d.dataset,"Title"),g(d.dataset,"Question"),g(d.dataset,"YesButton")).then(function(){if("true"===d.dataset.confirmationToast){var b=g(d.dataset,"ToastConfirmation");if("string"==typeof b){(0,c.add)(b)}else{b.then(function(a){return(0,c.add)(a)}).catch(function(b){return(0,a.exception)(b)})}}window.location.href=d.dataset.confirmationDestination}).catch(function(){})}})};if(!f){h();f=!0}});
//# sourceMappingURL=confirm.min.js.map
diff --git a/lib/amd/build/confirm.min.js.map b/lib/amd/build/confirm.min.js.map
index b2035d2212c..389788fb0a2 100644
--- a/lib/amd/build/confirm.min.js.map
+++ b/lib/amd/build/confirm.min.js.map
@@ -1 +1 @@
-{"version":3,"sources":["../src/confirm.js"],"names":["registered","confirmedPromise","title","question","saveLabel","Promise","resolve","reject","getConfirmationString","dataset","field","Str","get_string","apply","JSON","parse","registerConfirmationListeners","document","addEventListener","e","confirmRequest","target","closest","preventDefault","then","confirmationToast","stringForToast","str","catch","window","location","href","confirmationDestination"],"mappings":"qYA6CA,O,yiBAIIA,CAAAA,CAAU,G,CAYRC,CAAgB,CAAG,SAACC,CAAD,CAAQC,CAAR,CAAkBC,CAAlB,QAAgC,IAAIC,CAAAA,OAAJ,CAAY,SAACC,CAAD,CAAUC,CAAV,CAAqB,CACtF,iBAAWL,CAAX,CAAkBC,CAAlB,CAA4BC,CAA5B,CAAuCE,CAAvC,CAAgDC,CAAhD,CACH,CAFwD,CAAhC,C,CAcnBC,CAAqB,CAAG,SAACC,CAAD,CAAUC,CAAV,CAAoB,CAC9C,GAAID,CAAO,uBAAgBC,CAAhB,QAAX,CAAwC,CACpC,MAAOC,CAAAA,CAAG,CAACC,UAAJ,CAAeC,KAAf,CAAqB,IAArB,CAA2BC,IAAI,CAACC,KAAL,CAAWN,CAAO,uBAAgBC,CAAhB,QAAlB,CAA3B,CACV,CACD,MAAOL,CAAAA,OAAO,CAACC,OAAR,CAAgBG,CAAO,uBAAgBC,CAAhB,EAAvB,CACV,C,CAQKM,CAA6B,CAAG,UAAM,CACxCC,QAAQ,CAACC,gBAAT,CAA0B,OAA1B,CAAmC,SAAAC,CAAC,CAAI,CACpC,GAAMC,CAAAA,CAAc,CAAGD,CAAC,CAACE,MAAF,CAASC,OAAT,CAAiB,+BAAjB,CAAvB,CACA,GAAIF,CAAJ,CAAoB,CAChBD,CAAC,CAACI,cAAF,GACAtB,CAAgB,CACZO,CAAqB,CAACY,CAAc,CAACX,OAAhB,CAAyB,OAAzB,CADT,CAEZD,CAAqB,CAACY,CAAc,CAACX,OAAhB,CAAyB,UAAzB,CAFT,CAGZD,CAAqB,CAACY,CAAc,CAACX,OAAhB,CAAyB,WAAzB,CAHT,CAAhB,CAKCe,IALD,CAKM,UAAM,CACR,GAAiD,MAA7C,GAAAJ,CAAc,CAACX,OAAf,CAAuBgB,iBAA3B,CAAyD,CACrD,GAAMC,CAAAA,CAAc,CAAGlB,CAAqB,CAACY,CAAc,CAACX,OAAhB,CAAyB,mBAAzB,CAA5C,CACA,GAA8B,QAA1B,QAAOiB,CAAAA,CAAX,CAAwC,CACpC,UAASA,CAAT,CACH,CAFD,IAEO,CACHA,CAAc,CAACF,IAAf,CAAoB,SAAAG,CAAG,QAAI,UAASA,CAAT,CAAJ,CAAvB,EAA0CC,KAA1C,CAAgD,SAAAT,CAAC,QAAI,gBAAUA,CAAV,CAAJ,CAAjD,CACH,CACJ,CACDU,MAAM,CAACC,QAAP,CAAgBC,IAAhB,CAAuBX,CAAc,CAACX,OAAf,CAAuBuB,uBAEjD,CAhBD,EAgBGJ,KAhBH,CAgBS,UAAM,CAEd,CAlBD,CAmBH,CACJ,CAxBD,CAyBH,C,CAED,GAAI,CAAC5B,CAAL,CAAiB,CACbgB,CAA6B,GAC7BhB,CAAU,GACb,C","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 * Javascript events for the `core_confirm` modal.\n *\n * @module core/confirm\n * @copyright 2021 Andrew Nicols \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @since 4.0\n *\n * @example Calling the confirmation modal to delete a block\n *\n * // The following is an example of how to use this module via an indirect PHP call with a button.\n *\n * $controls[] = new action_menu_link_secondary(\n * $deleteactionurl,\n * new pix_icon('t/delete', $str, 'moodle', array('class' => 'iconsmall', 'title' => '')),\n * $str,\n * [\n * 'class' => 'editing_delete',\n * 'data-confirmation' => 'modal', // Needed so this module will pick it up in the click handler.\n * 'data-confirmation-title-str' => json_encode(['deletecheck_modal', 'block']),\n * 'data-confirmation-question-str' => json_encode(['deleteblockcheck', 'block', $blocktitle]),\n * 'data-confirmation-yes-button-str' => json_encode(['delete', 'core']),\n * 'data-confirmation-toast' => 'true', // Can be set to inform the user that their action was a success.\n * 'data-confirmation-toast-confirmation-str' => json_encode(['deleteblockinprogress', 'block', $blocktitle]),\n * 'data-confirmation-destination' => $deleteconfirmationurl->out(false), // Where do you want to direct the user?\n * ]\n * );\n */\n\nimport {saveCancel, exception} from 'core/notification';\nimport * as Str from 'core/str';\nimport {add as addToast} from 'core/toast';\n\n// We want to ensure that we only initialize the listeners only once.\nlet registered = false;\n\n/**\n * Add all of the supplied notifications.\n *\n * @method confirmedPromise\n * @protected\n * @param {Promise|String} title The header of the modal\n * @param {Promise|String} question What do we want the user to confirm\n * @param {Promise|String} saveLabel The modal action link text\n * @return {Promise}\n */\nconst confirmedPromise = (title, question, saveLabel) => new Promise((resolve, reject) => {\n saveCancel(title, question, saveLabel, resolve, reject);\n});\n\n/**\n * Either fetch the string or return it from the dom node.\n *\n * @method getConfirmationString\n * @private\n * @param {HTMLElement} dataset The page element to fetch dataset items in\n * @param {String} field The dataset field name to fetch the contents of\n * @return {Promise}\n *\n */\nconst getConfirmationString = (dataset, field) => {\n if (dataset[`confirmation${field}Str`]) {\n return Str.get_string.apply(null, JSON.parse(dataset[`confirmation${field}Str`]));\n }\n return Promise.resolve(dataset[`confirmation${field}`]);\n};\n\n/**\n * Set up the listeners for the confirmation modal widget within the page.\n *\n * @method registerConfirmationListeners\n * @private\n */\nconst registerConfirmationListeners = () => {\n document.addEventListener('click', e => {\n const confirmRequest = e.target.closest('[data-confirmation=\"modal\"]');\n if (confirmRequest) {\n e.preventDefault();\n confirmedPromise(\n getConfirmationString(confirmRequest.dataset, 'Title'),\n getConfirmationString(confirmRequest.dataset, 'Question'),\n getConfirmationString(confirmRequest.dataset, 'YesButton'),\n )\n .then(() => {\n if (confirmRequest.dataset.confirmationToast === 'true') {\n const stringForToast = getConfirmationString(confirmRequest.dataset, 'ToastConfirmation');\n if (typeof stringForToast === \"string\") {\n addToast(stringForToast);\n } else {\n stringForToast.then(str => addToast(str)).catch(e => exception(e));\n }\n }\n window.location.href = confirmRequest.dataset.confirmationDestination;\n return;\n }).catch(() => {\n return;\n });\n }\n });\n};\n\nif (!registered) {\n registerConfirmationListeners();\n registered = true;\n}\n"],"file":"confirm.min.js"}
\ No newline at end of file
+{"version":3,"sources":["../src/confirm.js"],"names":["registered","getConfirmationString","dataset","field","Str","get_string","apply","JSON","parse","Promise","resolve","registerConfirmationListeners","document","addEventListener","e","confirmRequest","target","closest","preventDefault","then","confirmationToast","stringForToast","str","catch","window","location","href","confirmationDestination"],"mappings":"qYA6CA,O,yiBAIIA,CAAAA,CAAU,G,CAYRC,CAAqB,CAAG,SAACC,CAAD,CAAUC,CAAV,CAAoB,CAC9C,GAAID,CAAO,uBAAgBC,CAAhB,QAAX,CAAwC,CACpC,MAAOC,CAAAA,CAAG,CAACC,UAAJ,CAAeC,KAAf,CAAqB,IAArB,CAA2BC,IAAI,CAACC,KAAL,CAAWN,CAAO,uBAAgBC,CAAhB,QAAlB,CAA3B,CACV,CACD,MAAOM,CAAAA,OAAO,CAACC,OAAR,CAAgBR,CAAO,uBAAgBC,CAAhB,EAAvB,CACV,C,CAQKQ,CAA6B,CAAG,UAAM,CACxCC,QAAQ,CAACC,gBAAT,CAA0B,OAA1B,CAAmC,SAAAC,CAAC,CAAI,CACpC,GAAMC,CAAAA,CAAc,CAAGD,CAAC,CAACE,MAAF,CAASC,OAAT,CAAiB,+BAAjB,CAAvB,CACA,GAAIF,CAAJ,CAAoB,CAChBD,CAAC,CAACI,cAAF,GACA,wBACIjB,CAAqB,CAACc,CAAc,CAACb,OAAhB,CAAyB,OAAzB,CADzB,CAEID,CAAqB,CAACc,CAAc,CAACb,OAAhB,CAAyB,UAAzB,CAFzB,CAGID,CAAqB,CAACc,CAAc,CAACb,OAAhB,CAAyB,WAAzB,CAHzB,EAKCiB,IALD,CAKM,UAAM,CACR,GAAiD,MAA7C,GAAAJ,CAAc,CAACb,OAAf,CAAuBkB,iBAA3B,CAAyD,CACrD,GAAMC,CAAAA,CAAc,CAAGpB,CAAqB,CAACc,CAAc,CAACb,OAAhB,CAAyB,mBAAzB,CAA5C,CACA,GAA8B,QAA1B,QAAOmB,CAAAA,CAAX,CAAwC,CACpC,UAASA,CAAT,CACH,CAFD,IAEO,CACHA,CAAc,CAACF,IAAf,CAAoB,SAAAG,CAAG,QAAI,UAASA,CAAT,CAAJ,CAAvB,EAA0CC,KAA1C,CAAgD,SAAAT,CAAC,QAAI,gBAAUA,CAAV,CAAJ,CAAjD,CACH,CACJ,CACDU,MAAM,CAACC,QAAP,CAAgBC,IAAhB,CAAuBX,CAAc,CAACb,OAAf,CAAuByB,uBAEjD,CAhBD,EAgBGJ,KAhBH,CAgBS,UAAM,CAEd,CAlBD,CAmBH,CACJ,CAxBD,CAyBH,C,CAED,GAAI,CAACvB,CAAL,CAAiB,CACbW,CAA6B,GAC7BX,CAAU,GACb,C","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 * Javascript events for the `core_confirm` modal.\n *\n * @module core/confirm\n * @copyright 2021 Andrew Nicols \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @since 4.0\n *\n * @example Calling the confirmation modal to delete a block\n *\n * // The following is an example of how to use this module via an indirect PHP call with a button.\n *\n * $controls[] = new action_menu_link_secondary(\n * $deleteactionurl,\n * new pix_icon('t/delete', $str, 'moodle', array('class' => 'iconsmall', 'title' => '')),\n * $str,\n * [\n * 'class' => 'editing_delete',\n * 'data-confirmation' => 'modal', // Needed so this module will pick it up in the click handler.\n * 'data-confirmation-title-str' => json_encode(['deletecheck_modal', 'block']),\n * 'data-confirmation-question-str' => json_encode(['deleteblockcheck', 'block', $blocktitle]),\n * 'data-confirmation-yes-button-str' => json_encode(['delete', 'core']),\n * 'data-confirmation-toast' => 'true', // Can be set to inform the user that their action was a success.\n * 'data-confirmation-toast-confirmation-str' => json_encode(['deleteblockinprogress', 'block', $blocktitle]),\n * 'data-confirmation-destination' => $deleteconfirmationurl->out(false), // Where do you want to direct the user?\n * ]\n * );\n */\n\nimport {saveCancelPromise, exception} from 'core/notification';\nimport * as Str from 'core/str';\nimport {add as addToast} from 'core/toast';\n\n// We want to ensure that we only initialize the listeners only once.\nlet registered = false;\n\n/**\n * Either fetch the string or return it from the dom node.\n *\n * @method getConfirmationString\n * @private\n * @param {HTMLElement} dataset The page element to fetch dataset items in\n * @param {String} field The dataset field name to fetch the contents of\n * @return {Promise}\n *\n */\nconst getConfirmationString = (dataset, field) => {\n if (dataset[`confirmation${field}Str`]) {\n return Str.get_string.apply(null, JSON.parse(dataset[`confirmation${field}Str`]));\n }\n return Promise.resolve(dataset[`confirmation${field}`]);\n};\n\n/**\n * Set up the listeners for the confirmation modal widget within the page.\n *\n * @method registerConfirmationListeners\n * @private\n */\nconst registerConfirmationListeners = () => {\n document.addEventListener('click', e => {\n const confirmRequest = e.target.closest('[data-confirmation=\"modal\"]');\n if (confirmRequest) {\n e.preventDefault();\n saveCancelPromise(\n getConfirmationString(confirmRequest.dataset, 'Title'),\n getConfirmationString(confirmRequest.dataset, 'Question'),\n getConfirmationString(confirmRequest.dataset, 'YesButton'),\n )\n .then(() => {\n if (confirmRequest.dataset.confirmationToast === 'true') {\n const stringForToast = getConfirmationString(confirmRequest.dataset, 'ToastConfirmation');\n if (typeof stringForToast === \"string\") {\n addToast(stringForToast);\n } else {\n stringForToast.then(str => addToast(str)).catch(e => exception(e));\n }\n }\n window.location.href = confirmRequest.dataset.confirmationDestination;\n return;\n }).catch(() => {\n return;\n });\n }\n });\n};\n\nif (!registered) {\n registerConfirmationListeners();\n registered = true;\n}\n"],"file":"confirm.min.js"}
\ No newline at end of file
diff --git a/lib/amd/build/notification.min.js b/lib/amd/build/notification.min.js
index 1f774b7ef3b..af3660280f5 100644
--- a/lib/amd/build/notification.min.js
+++ b/lib/amd/build/notification.min.js
@@ -1,2 +1,2 @@
-define ("core/notification",["exports","core/pending","core/log"],function(a,b,c){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.default=a.init=a.exception=a.saveCancel=a.confirm=a.alert=a.addNotification=a.fetchNotifications=void 0;b=d(b);c=d(c);var q="undefined"!=typeof window?window:"undefined"!=typeof self?self:"undefined"!=typeof global?global:{};function d(a){return a&&a.__esModule?a:{default:a}}function e(a,b){return k(a)||j(a,b)||g(a,b)||f()}function f(){throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}function g(a,b){if(!a)return;if("string"==typeof a)return h(a,b);var c=Object.prototype.toString.call(a).slice(8,-1);if("Object"===c&&a.constructor)c=a.constructor.name;if("Map"===c||"Set"===c)return Array.from(c);if("Arguments"===c||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(c))return h(a,b)}function h(a,b){if(null==b||b>a.length)b=a.length;for(var c=0,d=Array(b);ca.length)b=a.length;for(var c=0,d=Array(b);c.\n\n/**\n * Notification manager for in-page notifications in Moodle.\n *\n * @module core/notification\n * @copyright 2015 Damyon Wiese \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @since 2.9\n */\nimport Pending from 'core/pending';\nimport Log from 'core/log';\n\nlet currentContextId = M.cfg.contextid;\n\nconst notificationTypes = {\n success: 'core/notification_success',\n info: 'core/notification_info',\n warning: 'core/notification_warning',\n error: 'core/notification_error',\n};\n\nconst notificationRegionId = 'user-notifications';\n\nconst Selectors = {\n notificationRegion: `#${notificationRegionId}`,\n fallbackRegionParents: [\n '#region-main',\n '[role=\"main\"]',\n 'body',\n ],\n};\n\nconst setupTargetRegion = () => {\n let targetRegion = getNotificationRegion();\n if (targetRegion) {\n return false;\n }\n\n const newRegion = document.createElement('span');\n newRegion.id = notificationRegionId;\n\n return Selectors.fallbackRegionParents.some(selector => {\n const targetRegion = document.querySelector(selector);\n\n if (targetRegion) {\n targetRegion.prepend(newRegion);\n return true;\n }\n\n return false;\n });\n};\n\n/**\n * A notification object displayed to a user.\n *\n * @typedef {Object} Notification\n * @property {string} message The body of the notification\n * @property {string} type The type of notification to add (error, warning, info, success).\n * @property {Boolean} closebutton Whether to show the close button.\n * @property {Boolean} announce Whether to announce to screen readers.\n */\n\n/**\n * Poll the server for any new notifications.\n *\n * @method\n * @returns {Promise}\n */\nexport const fetchNotifications = async() => {\n const Ajax = await import('core/ajax');\n\n return Ajax.call([{\n methodname: 'core_fetch_notifications',\n args: {\n contextid: currentContextId\n }\n }])[0]\n .then(addNotifications);\n};\n\n/**\n * Add all of the supplied notifications.\n *\n * @method\n * @param {Notification[]} notifications The list of notificaitons\n * @returns {Promise}\n */\nconst addNotifications = notifications => {\n if (!notifications.length) {\n return Promise.resolve();\n }\n\n const pendingPromise = new Pending('core/notification:addNotifications');\n notifications.forEach(notification => renderNotification(notification.template, notification.variables));\n\n return pendingPromise.resolve();\n};\n\n/**\n * Add a notification to the page.\n *\n * Note: This does not cause the notification to be added to the session.\n *\n * @method\n * @param {Notification} notification The notification to add.\n * @returns {Promise}\n */\nexport const addNotification = notification => {\n const pendingPromise = new Pending('core/notification:addNotifications');\n\n let template = notificationTypes.error;\n\n notification = {\n closebutton: true,\n announce: true,\n type: 'error',\n ...notification,\n };\n\n if (notification.template) {\n template = notification.template;\n delete notification.template;\n } else if (notification.type) {\n if (typeof notificationTypes[notification.type] !== 'undefined') {\n template = notificationTypes[notification.type];\n }\n delete notification.type;\n }\n\n return renderNotification(template, notification)\n .then(pendingPromise.resolve);\n};\n\nconst renderNotification = async(template, variables) => {\n if (typeof variables.message === 'undefined' || !variables.message) {\n Log.debug('Notification received without content. Skipping.');\n return;\n }\n\n const pendingPromise = new Pending('core/notification:renderNotification');\n const Templates = await import('core/templates');\n\n Templates.renderForPromise(template, variables)\n .then(({html, js = ''}) => {\n Templates.prependNodeContents(getNotificationRegion(), html, js);\n\n return;\n })\n .then(pendingPromise.resolve)\n .catch(exception);\n};\n\nconst getNotificationRegion = () => document.querySelector(Selectors.notificationRegion);\n\n/**\n * Alert dialogue.\n *\n * @method\n * @param {String|Promise} title\n * @param {String|Promise} message\n * @param {String|Promise} cancelText\n * @returns {Promise}\n */\nexport const alert = async(title, message, cancelText) => {\n var pendingPromise = new Pending('core/notification:alert');\n\n const ModalFactory = await import('core/modal_factory');\n\n return ModalFactory.create({\n type: ModalFactory.types.ALERT,\n body: message,\n title: title,\n buttons: {\n cancel: cancelText,\n },\n removeOnClose: true,\n })\n .then(function(modal) {\n modal.show();\n\n pendingPromise.resolve();\n return modal;\n });\n};\n\n/**\n * The confirm has now been replaced with a save and cancel dialogue.\n *\n * @method\n * @param {String|Promise} title\n * @param {String|Promise} question\n * @param {String|Promise} saveLabel\n * @param {String|Promise} noLabel\n * @param {String|Promise} saveCallback\n * @param {String|Promise} cancelCallback\n * @returns {Promise}\n */\nexport const confirm = (title, question, saveLabel, noLabel, saveCallback, cancelCallback) =>\n saveCancel(title, question, saveLabel, saveCallback, cancelCallback);\n\n/**\n * The Save and Cancel dialogue helper.\n *\n * @method\n * @param {String|Promise} title\n * @param {String|Promise} question\n * @param {String|Promise} saveLabel\n * @param {String|Promise} saveCallback\n * @param {String|Promise} cancelCallback\n * @returns {Promise}\n */\nexport const saveCancel = async(title, question, saveLabel, saveCallback, cancelCallback) => {\n const pendingPromise = new Pending('core/notification:confirm');\n\n const [\n ModalFactory,\n ModalEvents,\n ] = await Promise.all([\n import('core/modal_factory'),\n import('core/modal_events'),\n ]);\n\n return ModalFactory.create({\n type: ModalFactory.types.SAVE_CANCEL,\n title: title,\n body: question,\n buttons: {\n // Note: The noLabel is no longer supported.\n save: saveLabel,\n },\n removeOnClose: true,\n })\n .then(function(modal) {\n modal.show();\n\n modal.getRoot().on(ModalEvents.save, saveCallback);\n modal.getRoot().on(ModalEvents.cancel, cancelCallback);\n pendingPromise.resolve();\n\n return modal;\n });\n};\n\n/**\n * Wrap M.core.exception.\n *\n * @method\n * @param {Error} ex\n */\nexport const exception = async ex => {\n const pendingPromise = new Pending('core/notification:displayException');\n\n // Fudge some parameters.\n if (!ex.stack) {\n ex.stack = '';\n }\n\n if (ex.debuginfo) {\n ex.stack += ex.debuginfo + '\\n';\n }\n\n if (!ex.backtrace && ex.stacktrace) {\n ex.backtrace = ex.stacktrace;\n }\n\n if (ex.backtrace) {\n ex.stack += ex.backtrace;\n const ln = ex.backtrace.match(/line ([^ ]*) of/);\n const fn = ex.backtrace.match(/ of ([^:]*): /);\n if (ln && ln[1]) {\n ex.lineNumber = ln[1];\n }\n if (fn && fn[1]) {\n ex.fileName = fn[1];\n if (ex.fileName.length > 30) {\n ex.fileName = '...' + ex.fileName.substr(ex.fileName.length - 27);\n }\n }\n }\n\n if (typeof ex.name === 'undefined' && ex.errorcode) {\n ex.name = ex.errorcode;\n }\n\n const Y = await import('core/yui');\n Y.use('moodle-core-notification-exception', function() {\n var modal = new M.core.exception(ex);\n\n modal.show();\n\n pendingPromise.resolve();\n });\n};\n\n/**\n * Initialise the page for the suppled context, and displaying the supplied notifications.\n *\n * @method\n * @param {Number} contextId\n * @param {Notification[]} notificationList\n */\nexport const init = (contextId, notificationList) => {\n currentContextId = contextId;\n\n // Setup the message target region if it isn't setup already\n setupTargetRegion();\n\n // Add provided notifications.\n addNotifications(notificationList);\n};\n\n// To maintain backwards compatability we export default here.\nexport default {\n init,\n fetchNotifications,\n addNotification,\n alert,\n confirm,\n saveCancel,\n exception,\n};\n"],"file":"notification.min.js"}
\ No newline at end of file
+{"version":3,"sources":["../src/notification.js"],"names":["currentContextId","M","cfg","contextid","notificationTypes","success","info","warning","error","notificationRegionId","Selectors","notificationRegion","fallbackRegionParents","setupTargetRegion","targetRegion","getNotificationRegion","newRegion","document","createElement","id","some","selector","querySelector","prepend","fetchNotifications","Ajax","call","methodname","args","then","addNotifications","notifications","length","Promise","resolve","pendingPromise","Pending","forEach","notification","renderNotification","template","variables","addNotification","closebutton","announce","type","message","Log","debug","Templates","renderForPromise","html","js","prependNodeContents","catch","exception","alert","title","cancelText","ModalFactory","create","types","ALERT","body","buttons","cancel","removeOnClose","modal","show","confirm","question","saveLabel","noLabel","saveCallback","cancelCallback","saveCancel","all","ModalEvents","SAVE_CANCEL","save","getRoot","on","saveCancelPromise","reject","ex","stack","debuginfo","backtrace","stacktrace","ln","match","fn","lineNumber","fileName","substr","name","errorcode","Y","use","core","init","contextId","notificationList"],"mappings":"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","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 * Notification manager for in-page notifications in Moodle.\n *\n * @module core/notification\n * @copyright 2015 Damyon Wiese \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @since 2.9\n */\nimport Pending from 'core/pending';\nimport Log from 'core/log';\n\nlet currentContextId = M.cfg.contextid;\n\nconst notificationTypes = {\n success: 'core/notification_success',\n info: 'core/notification_info',\n warning: 'core/notification_warning',\n error: 'core/notification_error',\n};\n\nconst notificationRegionId = 'user-notifications';\n\nconst Selectors = {\n notificationRegion: `#${notificationRegionId}`,\n fallbackRegionParents: [\n '#region-main',\n '[role=\"main\"]',\n 'body',\n ],\n};\n\nconst setupTargetRegion = () => {\n let targetRegion = getNotificationRegion();\n if (targetRegion) {\n return false;\n }\n\n const newRegion = document.createElement('span');\n newRegion.id = notificationRegionId;\n\n return Selectors.fallbackRegionParents.some(selector => {\n const targetRegion = document.querySelector(selector);\n\n if (targetRegion) {\n targetRegion.prepend(newRegion);\n return true;\n }\n\n return false;\n });\n};\n\n/**\n * A notification object displayed to a user.\n *\n * @typedef {Object} Notification\n * @property {string} message The body of the notification\n * @property {string} type The type of notification to add (error, warning, info, success).\n * @property {Boolean} closebutton Whether to show the close button.\n * @property {Boolean} announce Whether to announce to screen readers.\n */\n\n/**\n * Poll the server for any new notifications.\n *\n * @method\n * @returns {Promise}\n */\nexport const fetchNotifications = async() => {\n const Ajax = await import('core/ajax');\n\n return Ajax.call([{\n methodname: 'core_fetch_notifications',\n args: {\n contextid: currentContextId\n }\n }])[0]\n .then(addNotifications);\n};\n\n/**\n * Add all of the supplied notifications.\n *\n * @method\n * @param {Notification[]} notifications The list of notificaitons\n * @returns {Promise}\n */\nconst addNotifications = notifications => {\n if (!notifications.length) {\n return Promise.resolve();\n }\n\n const pendingPromise = new Pending('core/notification:addNotifications');\n notifications.forEach(notification => renderNotification(notification.template, notification.variables));\n\n return pendingPromise.resolve();\n};\n\n/**\n * Add a notification to the page.\n *\n * Note: This does not cause the notification to be added to the session.\n *\n * @method\n * @param {Notification} notification The notification to add.\n * @returns {Promise}\n */\nexport const addNotification = notification => {\n const pendingPromise = new Pending('core/notification:addNotifications');\n\n let template = notificationTypes.error;\n\n notification = {\n closebutton: true,\n announce: true,\n type: 'error',\n ...notification,\n };\n\n if (notification.template) {\n template = notification.template;\n delete notification.template;\n } else if (notification.type) {\n if (typeof notificationTypes[notification.type] !== 'undefined') {\n template = notificationTypes[notification.type];\n }\n delete notification.type;\n }\n\n return renderNotification(template, notification)\n .then(pendingPromise.resolve);\n};\n\nconst renderNotification = async(template, variables) => {\n if (typeof variables.message === 'undefined' || !variables.message) {\n Log.debug('Notification received without content. Skipping.');\n return;\n }\n\n const pendingPromise = new Pending('core/notification:renderNotification');\n const Templates = await import('core/templates');\n\n Templates.renderForPromise(template, variables)\n .then(({html, js = ''}) => {\n Templates.prependNodeContents(getNotificationRegion(), html, js);\n\n return;\n })\n .then(pendingPromise.resolve)\n .catch(exception);\n};\n\nconst getNotificationRegion = () => document.querySelector(Selectors.notificationRegion);\n\n/**\n * Alert dialogue.\n *\n * @method\n * @param {String|Promise} title\n * @param {String|Promise} message\n * @param {String|Promise} cancelText\n * @returns {Promise}\n */\nexport const alert = async(title, message, cancelText) => {\n var pendingPromise = new Pending('core/notification:alert');\n\n const ModalFactory = await import('core/modal_factory');\n\n return ModalFactory.create({\n type: ModalFactory.types.ALERT,\n body: message,\n title: title,\n buttons: {\n cancel: cancelText,\n },\n removeOnClose: true,\n })\n .then(function(modal) {\n modal.show();\n\n pendingPromise.resolve();\n return modal;\n });\n};\n\n/**\n * The confirm has now been replaced with a save and cancel dialogue.\n *\n * @method\n * @param {String|Promise} title\n * @param {String|Promise} question\n * @param {String|Promise} saveLabel\n * @param {String|Promise} noLabel\n * @param {String|Promise} saveCallback\n * @param {String|Promise} cancelCallback\n * @returns {Promise}\n */\nexport const confirm = (title, question, saveLabel, noLabel, saveCallback, cancelCallback) =>\n saveCancel(title, question, saveLabel, saveCallback, cancelCallback);\n\n/**\n * The Save and Cancel dialogue helper.\n *\n * @method\n * @param {String|Promise} title\n * @param {String|Promise} question\n * @param {String|Promise} saveLabel\n * @param {String|Promise} saveCallback\n * @param {String|Promise} cancelCallback\n * @returns {Promise}\n */\nexport const saveCancel = async(title, question, saveLabel, saveCallback, cancelCallback) => {\n const pendingPromise = new Pending('core/notification:confirm');\n\n const [\n ModalFactory,\n ModalEvents,\n ] = await Promise.all([\n import('core/modal_factory'),\n import('core/modal_events'),\n ]);\n\n return ModalFactory.create({\n type: ModalFactory.types.SAVE_CANCEL,\n title: title,\n body: question,\n buttons: {\n // Note: The noLabel is no longer supported.\n save: saveLabel,\n },\n removeOnClose: true,\n })\n .then(function(modal) {\n modal.show();\n\n modal.getRoot().on(ModalEvents.save, saveCallback);\n modal.getRoot().on(ModalEvents.cancel, cancelCallback);\n pendingPromise.resolve();\n\n return modal;\n });\n};\n\n/**\n * Add all of the supplied notifications.\n *\n * @param {Promise|String} title The header of the modal\n * @param {Promise|String} question What do we want the user to confirm\n * @param {Promise|String} saveLabel The modal action link text\n * @return {Promise}\n */\nexport const saveCancelPromise = (title, question, saveLabel) => new Promise((resolve, reject) => {\n saveCancel(title, question, saveLabel, resolve, reject);\n});\n\n/**\n * Wrap M.core.exception.\n *\n * @method\n * @param {Error} ex\n */\nexport const exception = async ex => {\n const pendingPromise = new Pending('core/notification:displayException');\n\n // Fudge some parameters.\n if (!ex.stack) {\n ex.stack = '';\n }\n\n if (ex.debuginfo) {\n ex.stack += ex.debuginfo + '\\n';\n }\n\n if (!ex.backtrace && ex.stacktrace) {\n ex.backtrace = ex.stacktrace;\n }\n\n if (ex.backtrace) {\n ex.stack += ex.backtrace;\n const ln = ex.backtrace.match(/line ([^ ]*) of/);\n const fn = ex.backtrace.match(/ of ([^:]*): /);\n if (ln && ln[1]) {\n ex.lineNumber = ln[1];\n }\n if (fn && fn[1]) {\n ex.fileName = fn[1];\n if (ex.fileName.length > 30) {\n ex.fileName = '...' + ex.fileName.substr(ex.fileName.length - 27);\n }\n }\n }\n\n if (typeof ex.name === 'undefined' && ex.errorcode) {\n ex.name = ex.errorcode;\n }\n\n const Y = await import('core/yui');\n Y.use('moodle-core-notification-exception', function() {\n var modal = new M.core.exception(ex);\n\n modal.show();\n\n pendingPromise.resolve();\n });\n};\n\n/**\n * Initialise the page for the suppled context, and displaying the supplied notifications.\n *\n * @method\n * @param {Number} contextId\n * @param {Notification[]} notificationList\n */\nexport const init = (contextId, notificationList) => {\n currentContextId = contextId;\n\n // Setup the message target region if it isn't setup already\n setupTargetRegion();\n\n // Add provided notifications.\n addNotifications(notificationList);\n};\n\n// To maintain backwards compatability we export default here.\nexport default {\n init,\n fetchNotifications,\n addNotification,\n alert,\n confirm,\n saveCancel,\n saveCancelPromise,\n exception,\n};\n"],"file":"notification.min.js"}
\ No newline at end of file
diff --git a/lib/amd/build/toast.min.js b/lib/amd/build/toast.min.js
index b2c63b3a4c2..e8b6549eb62 100644
--- a/lib/amd/build/toast.min.js
+++ b/lib/amd/build/toast.min.js
@@ -1,2 +1,2 @@
-define ("core/toast",["exports","core/templates","core/notification","core/pending"],function(a,b,c,d){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.add=a.addToastRegion=void 0;b=e(b);c=e(c);d=e(d);function e(a){return a&&a.__esModule?a:{default:a}}function f(a,b){var c=Object.keys(a);if(Object.getOwnPropertySymbols){var d=Object.getOwnPropertySymbols(a);if(b)d=d.filter(function(b){return Object.getOwnPropertyDescriptor(a,b).enumerable});c.push.apply(c,d)}return c}function g(a){for(var b=1,c;b.\n\n/**\n * A system for displaying small snackbar notifications to users which disappear shortly after they are shown.\n *\n * @module core/toast\n * @copyright 2019 Andrew Nicols \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport Templates from 'core/templates';\nimport Notification from 'core/notification';\nimport Pending from 'core/pending';\n\n/**\n * Add a new region to place toasts in, taking in a parent element.\n *\n * @method\n * @param {HTMLElement} parent\n */\nexport const addToastRegion = async(parent) => {\n const pendingPromise = new Pending('addToastRegion');\n\n try {\n const {html, js} = await Templates.renderForPromise('core/local/toast/wrapper', {});\n Templates.prependNodeContents(parent, html, js);\n } catch (e) {\n Notification.exception(e);\n }\n\n pendingPromise.resolve();\n};\n\n/**\n * Add a new toast or snackbar notification to the page.\n *\n * @method\n * @param {String} message\n * @param {Object} configuration\n * @param {String} [configuration.title]\n * @param {String} [configuration.subtitle]\n * @param {String} [configuration.type=info] Optional type of the toast notification ('success', 'info', 'warning' or 'danger')\n * @param {Boolean} [configuration.autohide=true]\n * @param {Boolean} [configuration.closeButton=false]\n * @param {Number} [configuration.delay=4000]\n *\n * @example\n * import {add as addToast} from 'core/toast';\n * import {get_string as getString} from 'core/str';\n *\n * getString('example', 'mod_myexample')\n * .then(str => {\n * addToast(str, {\n * type: 'warning',\n * autohide: false,\n * closeButton: true,\n * });\n * return;\n * })\n * .catch();\n */\nexport const add = async(message, configuration) => {\n const pendingPromise = new Pending('addToastRegion');\n configuration = {\n type: 'info',\n closeButton: false,\n autohide: true,\n delay: 4000,\n ...configuration,\n };\n\n const templateName = `core/local/toast/message`;\n try {\n const targetNode = await getTargetNode();\n const {html, js} = await Templates.renderForPromise(templateName, {\n message,\n ...configuration\n });\n Templates.prependNodeContents(targetNode, html, js);\n } catch (e) {\n Notification.exception(e);\n }\n\n pendingPromise.resolve();\n};\n\nconst getTargetNode = async() => {\n const regions = document.querySelectorAll('.toast-wrapper');\n\n if (regions.length) {\n return regions[regions.length - 1];\n }\n\n await addToastRegion(document.body, 'fixed-bottom');\n return getTargetNode();\n};\n"],"file":"toast.min.js"}
\ No newline at end of file
+{"version":3,"sources":["../src/toast.js"],"names":["addToastRegion","parent","pendingPromise","Pending","Templates","renderForPromise","html","js","prependNodeContents","Notification","exception","resolve","add","message","configuration","type","closeButton","autohide","delay","templateName","getTargetNode","targetNode","regions","document","querySelectorAll","length","body"],"mappings":"mMAsBA,OACA,OACA,O,6jCAQO,GAAMA,CAAAA,CAAc,4CAAG,WAAMC,CAAN,+FACpBC,CADoB,CACH,GAAIC,UAAJ,CAAY,gBAAZ,CADG,yBAIGC,WAAUC,gBAAV,CAA2B,0BAA3B,CAAuD,EAAvD,CAJH,iBAIfC,CAJe,GAIfA,IAJe,CAITC,CAJS,GAITA,EAJS,CAKtBH,UAAUI,mBAAV,CAA8BP,CAA9B,CAAsCK,CAAtC,CAA4CC,CAA5C,EALsB,qDAOtBE,UAAaC,SAAb,OAPsB,QAU1BR,CAAc,CAACS,OAAf,GAV0B,uDAAH,uDAApB,C,mBAyCA,GAAMC,CAAAA,CAAG,4CAAG,WAAMC,CAAN,CAAeC,CAAf,mGACTZ,CADS,CACQ,GAAIC,UAAJ,CAAY,gBAAZ,CADR,CAEfW,CAAa,IACTC,IAAI,CAAE,MADG,CAETC,WAAW,GAFF,CAGTC,QAAQ,GAHC,CAITC,KAAK,CAAE,GAJE,EAKNJ,CALM,CAAb,CAQMK,CAVS,oDAYcC,CAAAA,CAAa,EAZ3B,QAYLC,CAZK,aAacjB,SAbd,MAayCe,CAbzC,wBAcQN,CAAAA,CAdR,2BAcPA,OAdO,YAeJC,CAfI,gDAawBT,gBAbxB,uCAaJC,CAbI,GAaJA,IAbI,CAaEC,CAbF,GAaEA,EAbF,CAiBXH,UAAUI,mBAAV,CAA8Ba,CAA9B,CAA0Cf,CAA1C,CAAgDC,CAAhD,EAjBW,qDAmBXE,UAAaC,SAAb,OAnBW,QAsBfR,CAAc,CAACS,OAAf,GAtBe,uDAAH,uDAAT,C,QAyBP,GAAMS,CAAAA,CAAa,4CAAG,oGACZE,CADY,CACFC,QAAQ,CAACC,gBAAT,CAA0B,gBAA1B,CADE,KAGdF,CAAO,CAACG,MAHM,0CAIPH,CAAO,CAACA,CAAO,CAACG,MAAR,CAAiB,CAAlB,CAJA,wBAOZzB,CAAAA,CAAc,CAACuB,QAAQ,CAACG,IAAV,CAAgB,cAAhB,CAPF,iCAQXN,CAAa,EARF,0CAAH,uD","sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * A system for displaying small snackbar notifications to users which disappear shortly after they are shown.\n *\n * @module core/toast\n * @copyright 2019 Andrew Nicols \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport Templates from 'core/templates';\nimport Notification from 'core/notification';\nimport Pending from 'core/pending';\n\n/**\n * Add a new region to place toasts in, taking in a parent element.\n *\n * @method\n * @param {HTMLElement} parent\n */\nexport const addToastRegion = async(parent) => {\n const pendingPromise = new Pending('addToastRegion');\n\n try {\n const {html, js} = await Templates.renderForPromise('core/local/toast/wrapper', {});\n Templates.prependNodeContents(parent, html, js);\n } catch (e) {\n Notification.exception(e);\n }\n\n pendingPromise.resolve();\n};\n\n/**\n * Add a new toast or snackbar notification to the page.\n *\n * @method\n * @param {String} message\n * @param {Object} configuration\n * @param {String} [configuration.title]\n * @param {String} [configuration.subtitle]\n * @param {String} [configuration.type=info] Optional type of the toast notification ('success', 'info', 'warning' or 'danger')\n * @param {Boolean} [configuration.autohide=true]\n * @param {Boolean} [configuration.closeButton=false]\n * @param {Number} [configuration.delay=4000]\n *\n * @example\n * import {add as addToast} from 'core/toast';\n * import {get_string as getString} from 'core/str';\n *\n * getString('example', 'mod_myexample')\n * .then(str => {\n * addToast(str, {\n * type: 'warning',\n * autohide: false,\n * closeButton: true,\n * });\n * return;\n * })\n * .catch();\n */\nexport const add = async(message, configuration) => {\n const pendingPromise = new Pending('addToastRegion');\n configuration = {\n type: 'info',\n closeButton: false,\n autohide: true,\n delay: 4000,\n ...configuration,\n };\n\n const templateName = `core/local/toast/message`;\n try {\n const targetNode = await getTargetNode();\n const {html, js} = await Templates.renderForPromise(templateName, {\n message: await message,\n ...configuration\n });\n Templates.prependNodeContents(targetNode, html, js);\n } catch (e) {\n Notification.exception(e);\n }\n\n pendingPromise.resolve();\n};\n\nconst getTargetNode = async() => {\n const regions = document.querySelectorAll('.toast-wrapper');\n\n if (regions.length) {\n return regions[regions.length - 1];\n }\n\n await addToastRegion(document.body, 'fixed-bottom');\n return getTargetNode();\n};\n"],"file":"toast.min.js"}
\ No newline at end of file
diff --git a/lib/amd/src/confirm.js b/lib/amd/src/confirm.js
index 452699ea5d2..42991071234 100644
--- a/lib/amd/src/confirm.js
+++ b/lib/amd/src/confirm.js
@@ -42,27 +42,13 @@
* );
*/
-import {saveCancel, exception} from 'core/notification';
+import {saveCancelPromise, exception} from 'core/notification';
import * as Str from 'core/str';
import {add as addToast} from 'core/toast';
// We want to ensure that we only initialize the listeners only once.
let registered = false;
-/**
- * Add all of the supplied notifications.
- *
- * @method confirmedPromise
- * @protected
- * @param {Promise|String} title The header of the modal
- * @param {Promise|String} question What do we want the user to confirm
- * @param {Promise|String} saveLabel The modal action link text
- * @return {Promise}
- */
-const confirmedPromise = (title, question, saveLabel) => new Promise((resolve, reject) => {
- saveCancel(title, question, saveLabel, resolve, reject);
-});
-
/**
* Either fetch the string or return it from the dom node.
*
@@ -91,7 +77,7 @@ const registerConfirmationListeners = () => {
const confirmRequest = e.target.closest('[data-confirmation="modal"]');
if (confirmRequest) {
e.preventDefault();
- confirmedPromise(
+ saveCancelPromise(
getConfirmationString(confirmRequest.dataset, 'Title'),
getConfirmationString(confirmRequest.dataset, 'Question'),
getConfirmationString(confirmRequest.dataset, 'YesButton'),
diff --git a/lib/amd/src/notification.js b/lib/amd/src/notification.js
index be793140a3c..216bbb97661 100644
--- a/lib/amd/src/notification.js
+++ b/lib/amd/src/notification.js
@@ -256,6 +256,18 @@ export const saveCancel = async(title, question, saveLabel, saveCallback, cancel
});
};
+/**
+ * Add all of the supplied notifications.
+ *
+ * @param {Promise|String} title The header of the modal
+ * @param {Promise|String} question What do we want the user to confirm
+ * @param {Promise|String} saveLabel The modal action link text
+ * @return {Promise}
+ */
+export const saveCancelPromise = (title, question, saveLabel) => new Promise((resolve, reject) => {
+ saveCancel(title, question, saveLabel, resolve, reject);
+});
+
/**
* Wrap M.core.exception.
*
@@ -332,5 +344,6 @@ export default {
alert,
confirm,
saveCancel,
+ saveCancelPromise,
exception,
};
diff --git a/lib/amd/src/toast.js b/lib/amd/src/toast.js
index 70d16fb34f9..3349a7febf5 100644
--- a/lib/amd/src/toast.js
+++ b/lib/amd/src/toast.js
@@ -85,7 +85,7 @@ export const add = async(message, configuration) => {
try {
const targetNode = await getTargetNode();
const {html, js} = await Templates.renderForPromise(templateName, {
- message,
+ message: await message,
...configuration
});
Templates.prependNodeContents(targetNode, html, js);