diff --git a/lib/amd/build/notification.min.js b/lib/amd/build/notification.min.js index 4e559a2f810..2729ff19edc 100644 --- a/lib/amd/build/notification.min.js +++ b/lib/amd/build/notification.min.js @@ -1,2 +1,2 @@ -define ("core/notification",["core/yui","jquery","core/log","core/pending"],function(a,b,c,d){var e={types:{success:"core/notification_success",info:"core/notification_info",warning:"core/notification_warning",error:"core/notification_error"},fieldName:"user-notifications",fetchNotifications:function fetchNotifications(){var a=new d("core/notification:fetchNotifications");require(["core/ajax"],function(b){var c=b.call([{methodname:"core_fetch_notifications",args:{contextid:e.contextid}}]);c[0].then(e.addNotifications).always(a.resolve)})},addNotifications:function addNotifications(a){var c=new d("core/notification:addNotifications");if(!a){a=[]}b.each(a,function(a,b){e.renderNotification(b.template,b.variables)});c.resolve()},setupTargetRegion:function setupTargetRegion(){var a=b("#"+e.fieldName);if(a.length){return!1}var c=b("").attr("id",e.fieldName);a=b("#region-main");if(a.length){return a.prepend(c)}a=b("[role=\"main\"]");if(a.length){return a.prepend(c)}a=b("body");return a.prepend(c)},addNotification:function addNotification(a){var c=new d("core/notification:addNotifications"),f=e.types.error;a=b.extend({closebutton:!0,announce:!0,type:"error"},a);if(a.template){f=a.template;delete a.template}else if(a.type){if("undefined"!=typeof e.types[a.type]){f=e.types[a.type]}delete a.type}c.resolve();return e.renderNotification(f,a)},renderNotification:function renderNotification(a,f){if("undefined"==typeof f.message||!f.message){c.debug("Notification received without content. Skipping.");return}var g=new d("core/notification:renderNotification");require(["core/templates"],function(c){c.render(a,f).then(function(a,d){b("#"+e.fieldName).prepend(a);c.runTemplateJS(d)}).always(g.resolve).catch(e.exception)})},alert:function alert(b,c,e){var f=new d("core/notification:alert");a.use("moodle-core-notification-alert",function(){var a=new M.core.alert({title:b,message:c,yesLabel:e});a.show();f.resolve()})},confirm:function confirm(b,c,e,f,g,h){var i=new d("core/notification:confirm");a.use("moodle-core-notification-confirm",function(){var a=new M.core.confirm({title:b,question:c,yesLabel:e,noLabel:f});a.on("complete-yes",function(){g()});if(h){a.on("complete-no",function(){h()})}a.show();i.resolve()})},exception:function exception(b){var c=new d("core/notification:addNotifications");if("undefined"==typeof b.stack){b.stack=""}if(b.debuginfo){b.stack+=b.debuginfo+"\n"}if(!b.backtrace&&b.stacktrace){b.backtrace=b.stacktrace}if(b.backtrace){b.stack+=b.backtrace;var e=b.backtrace.match(/line ([^ ]*) of/),f=b.backtrace.match(/ of ([^:]*): /);if(e&&e[1]){b.lineNumber=e[1]}if(f&&f[1]){b.fileName=f[1];if(30.\n\n/**\n * A system for displaying notifications to users from the session.\n *\n * Wrapper for the YUI M.core.notification class. Allows us to\n * use the YUI version in AMD code until it is replaced.\n *\n * @module core/notification\n * @class notification\n * @package core\n * @copyright 2015 Damyon Wiese \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @since 2.9\n */\ndefine(['core/yui', 'jquery', 'core/log', 'core/pending'],\nfunction(Y, $, log, Pending) {\n var notificationModule = {\n types: {\n 'success': 'core/notification_success',\n 'info': 'core/notification_info',\n 'warning': 'core/notification_warning',\n 'error': 'core/notification_error',\n },\n\n fieldName: 'user-notifications',\n\n fetchNotifications: function() {\n var pendingPromise = new Pending('core/notification:fetchNotifications');\n\n require(['core/ajax'], function(ajax) {\n var promises = ajax.call([{\n methodname: 'core_fetch_notifications',\n args: {\n contextid: notificationModule.contextid\n }\n }]);\n\n // This currently fails when not logged in.\n // eslint-disable-next-line promise/catch-or-return\n promises[0]\n .then(notificationModule.addNotifications)\n .always(pendingPromise.resolve);\n });\n },\n\n addNotifications: function(notifications) {\n var pendingPromise = new Pending('core/notification:addNotifications');\n\n if (!notifications) {\n notifications = [];\n }\n\n $.each(notifications, function(i, notification) {\n notificationModule.renderNotification(notification.template, notification.variables);\n });\n\n pendingPromise.resolve();\n },\n\n setupTargetRegion: function() {\n var targetRegion = $('#' + notificationModule.fieldName);\n if (targetRegion.length) {\n return false;\n }\n\n var newRegion = $('').attr('id', notificationModule.fieldName);\n\n targetRegion = $('#region-main');\n if (targetRegion.length) {\n return targetRegion.prepend(newRegion);\n }\n\n targetRegion = $('[role=\"main\"]');\n if (targetRegion.length) {\n return targetRegion.prepend(newRegion);\n }\n\n targetRegion = $('body');\n return targetRegion.prepend(newRegion);\n },\n\n addNotification: function(notification) {\n var pendingPromise = new Pending('core/notification:addNotifications');\n\n var template = notificationModule.types.error;\n\n notification = $.extend({\n closebutton: true,\n announce: true,\n type: 'error'\n }, notification);\n\n if (notification.template) {\n template = notification.template;\n delete notification.template;\n } else if (notification.type) {\n if (typeof notificationModule.types[notification.type] !== 'undefined') {\n template = notificationModule.types[notification.type];\n }\n delete notification.type;\n }\n\n pendingPromise.resolve();\n\n return notificationModule.renderNotification(template, notification);\n },\n\n renderNotification: function(template, variables) {\n if (typeof variables.message === 'undefined' || !variables.message) {\n log.debug('Notification received without content. Skipping.');\n return;\n }\n\n var pendingPromise = new Pending('core/notification:renderNotification');\n\n require(['core/templates'], function(templates) {\n templates.render(template, variables)\n .then(function(html, js) {\n $('#' + notificationModule.fieldName).prepend(html);\n templates.runTemplateJS(js);\n\n return;\n })\n .always(pendingPromise.resolve)\n .catch(notificationModule.exception);\n });\n },\n\n alert: function(title, message, yesLabel) {\n var pendingPromise = new Pending('core/notification:alert');\n\n // Here we are wrapping YUI. This allows us to start transitioning, but\n // wait for a good alternative without having inconsistent dialogues.\n Y.use('moodle-core-notification-alert', function() {\n var alert = new M.core.alert({\n title: title,\n message: message,\n yesLabel: yesLabel\n });\n\n alert.show();\n\n pendingPromise.resolve();\n });\n },\n\n confirm: function(title, question, yesLabel, noLabel, yesCallback, noCallback) {\n var pendingPromise = new Pending('core/notification:confirm');\n\n // Here we are wrapping YUI. This allows us to start transitioning, but\n // wait for a good alternative without having inconsistent dialogues.\n Y.use('moodle-core-notification-confirm', function() {\n var modal = new M.core.confirm({\n title: title,\n question: question,\n yesLabel: yesLabel,\n noLabel: noLabel\n });\n\n modal.on('complete-yes', function() {\n yesCallback();\n });\n if (noCallback) {\n modal.on('complete-no', function() {\n noCallback();\n });\n }\n modal.show();\n\n pendingPromise.resolve();\n });\n },\n\n exception: function(ex) {\n var pendingPromise = new Pending('core/notification:addNotifications');\n\n // Fudge some parameters.\n if (typeof ex.stack == 'undefined') {\n ex.stack = '';\n }\n if (ex.debuginfo) {\n ex.stack += ex.debuginfo + '\\n';\n }\n if (!ex.backtrace && ex.stacktrace) {\n ex.backtrace = ex.stacktrace;\n }\n if (ex.backtrace) {\n ex.stack += ex.backtrace;\n var ln = ex.backtrace.match(/line ([^ ]*) of/);\n var 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 if (typeof ex.name == 'undefined' && ex.errorcode) {\n ex.name = ex.errorcode;\n }\n\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 return /** @alias module:core/notification */{\n init: function(contextid, notifications) {\n notificationModule.contextid = contextid;\n\n // Setup the message target region if it isn't setup already\n notificationModule.setupTargetRegion();\n\n // Add provided notifications.\n notificationModule.addNotifications(notifications);\n\n // Poll for any new notifications.\n notificationModule.fetchNotifications();\n },\n\n /**\n * Poll the server for any new notifications.\n *\n * @method fetchNotifications\n */\n fetchNotifications: notificationModule.fetchNotifications,\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 addNotification\n * @param {Object} notification The notification to add.\n * @param {string} notification.message The body of the notification\n * @param {string} notification.type The type of notification to add (error, warning, info, success).\n * @param {Boolean} notification.closebutton Whether to show the close button.\n * @param {Boolean} notification.announce Whether to announce to screen readers.\n */\n addNotification: notificationModule.addNotification,\n\n /**\n * Wrap M.core.alert.\n *\n * @method alert\n * @param {string} title\n * @param {string} message\n * @param {string} yesLabel\n */\n alert: notificationModule.alert,\n\n /**\n * Wrap M.core.confirm.\n *\n * @method confirm\n * @param {string} title\n * @param {string} question\n * @param {string} yesLabel\n * @param {string} noLabel\n * @param {function} yesCallback\n * @param {function} noCallback Optional parameter to be called if the user presses cancel.\n */\n confirm: notificationModule.confirm,\n\n /**\n * Wrap M.core.exception.\n *\n * @method exception\n * @param {Error} ex\n */\n exception: notificationModule.exception\n };\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","saveCancel","question","saveLabel","saveCallback","cancelCallback","all","ModalEvents","SAVE_CANCEL","save","getRoot","on","ex","stack","debuginfo","backtrace","stacktrace","ln","match","fn","lineNumber","fileName","substr","name","errorcode","Y","use","core","init","contextId","notificationList","confirm"],"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\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/**\n * Poll the server for any new notifications.\n *\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 * @param {Array} 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 * @param {Object} notification The notification to add.\n * @param {string} notification.message The body of the notification\n * @param {string} notification.type The type of notification to add (error, warning, info, success).\n * @param {Boolean} notification.closebutton Whether to show the close button.\n * @param {Boolean} notification.announce Whether to announce to screen readers.\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 * @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 * @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 * @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 * @param {Number} contextId\n * @param {Array} 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 // Perform an initial poll for any new notifications.\n fetchNotifications();\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 diff --git a/lib/amd/src/notification.js b/lib/amd/src/notification.js index 3bbe59f3094..8abb41aeeee 100644 --- a/lib/amd/src/notification.js +++ b/lib/amd/src/notification.js @@ -13,283 +13,292 @@ // You should have received a copy of the GNU General Public License // along with Moodle. If not, see . -/** - * A system for displaying notifications to users from the session. - * - * Wrapper for the YUI M.core.notification class. Allows us to - * use the YUI version in AMD code until it is replaced. - * - * @module core/notification - * @class notification - * @package core - * @copyright 2015 Damyon Wiese - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - * @since 2.9 - */ -define(['core/yui', 'jquery', 'core/log', 'core/pending'], -function(Y, $, log, Pending) { - var notificationModule = { - types: { - 'success': 'core/notification_success', - 'info': 'core/notification_info', - 'warning': 'core/notification_warning', - 'error': 'core/notification_error', - }, +import Pending from 'core/pending'; +import Log from 'core/log'; - fieldName: 'user-notifications', +let currentContextId = M.cfg.contextid; - fetchNotifications: function() { - var pendingPromise = new Pending('core/notification:fetchNotifications'); +const notificationTypes = { + success: 'core/notification_success', + info: 'core/notification_info', + warning: 'core/notification_warning', + error: 'core/notification_error', +}; - require(['core/ajax'], function(ajax) { - var promises = ajax.call([{ - methodname: 'core_fetch_notifications', - args: { - contextid: notificationModule.contextid - } - }]); +const notificationRegionId = 'user-notifications'; - // This currently fails when not logged in. - // eslint-disable-next-line promise/catch-or-return - promises[0] - .then(notificationModule.addNotifications) - .always(pendingPromise.resolve); - }); - }, +const Selectors = { + notificationRegion: `#${notificationRegionId}`, + fallbackRegionParents: [ + '#region-main', + '[role="main"]', + 'body', + ], +}; - addNotifications: function(notifications) { - var pendingPromise = new Pending('core/notification:addNotifications'); +const setupTargetRegion = () => { + let targetRegion = getNotificationRegion(); + if (targetRegion) { + return false; + } - if (!notifications) { - notifications = []; - } + const newRegion = document.createElement('span'); + newRegion.id = notificationRegionId; - $.each(notifications, function(i, notification) { - notificationModule.renderNotification(notification.template, notification.variables); - }); + return Selectors.fallbackRegionParents.some(selector => { + const targetRegion = document.querySelector(selector); - pendingPromise.resolve(); - }, - - setupTargetRegion: function() { - var targetRegion = $('#' + notificationModule.fieldName); - if (targetRegion.length) { - return false; - } - - var newRegion = $('').attr('id', notificationModule.fieldName); - - targetRegion = $('#region-main'); - if (targetRegion.length) { - return targetRegion.prepend(newRegion); - } - - targetRegion = $('[role="main"]'); - if (targetRegion.length) { - return targetRegion.prepend(newRegion); - } - - targetRegion = $('body'); - return targetRegion.prepend(newRegion); - }, - - addNotification: function(notification) { - var pendingPromise = new Pending('core/notification:addNotifications'); - - var template = notificationModule.types.error; - - notification = $.extend({ - closebutton: true, - announce: true, - type: 'error' - }, notification); - - if (notification.template) { - template = notification.template; - delete notification.template; - } else if (notification.type) { - if (typeof notificationModule.types[notification.type] !== 'undefined') { - template = notificationModule.types[notification.type]; - } - delete notification.type; - } - - pendingPromise.resolve(); - - return notificationModule.renderNotification(template, notification); - }, - - renderNotification: function(template, variables) { - if (typeof variables.message === 'undefined' || !variables.message) { - log.debug('Notification received without content. Skipping.'); - return; - } - - var pendingPromise = new Pending('core/notification:renderNotification'); - - require(['core/templates'], function(templates) { - templates.render(template, variables) - .then(function(html, js) { - $('#' + notificationModule.fieldName).prepend(html); - templates.runTemplateJS(js); - - return; - }) - .always(pendingPromise.resolve) - .catch(notificationModule.exception); - }); - }, - - alert: function(title, message, yesLabel) { - var pendingPromise = new Pending('core/notification:alert'); - - // Here we are wrapping YUI. This allows us to start transitioning, but - // wait for a good alternative without having inconsistent dialogues. - Y.use('moodle-core-notification-alert', function() { - var alert = new M.core.alert({ - title: title, - message: message, - yesLabel: yesLabel - }); - - alert.show(); - - pendingPromise.resolve(); - }); - }, - - confirm: function(title, question, yesLabel, noLabel, yesCallback, noCallback) { - var pendingPromise = new Pending('core/notification:confirm'); - - // Here we are wrapping YUI. This allows us to start transitioning, but - // wait for a good alternative without having inconsistent dialogues. - Y.use('moodle-core-notification-confirm', function() { - var modal = new M.core.confirm({ - title: title, - question: question, - yesLabel: yesLabel, - noLabel: noLabel - }); - - modal.on('complete-yes', function() { - yesCallback(); - }); - if (noCallback) { - modal.on('complete-no', function() { - noCallback(); - }); - } - modal.show(); - - pendingPromise.resolve(); - }); - }, - - exception: function(ex) { - var pendingPromise = new Pending('core/notification:addNotifications'); - - // Fudge some parameters. - if (typeof ex.stack == 'undefined') { - ex.stack = ''; - } - if (ex.debuginfo) { - ex.stack += ex.debuginfo + '\n'; - } - if (!ex.backtrace && ex.stacktrace) { - ex.backtrace = ex.stacktrace; - } - if (ex.backtrace) { - ex.stack += ex.backtrace; - var ln = ex.backtrace.match(/line ([^ ]*) of/); - var fn = ex.backtrace.match(/ of ([^:]*): /); - if (ln && ln[1]) { - ex.lineNumber = ln[1]; - } - if (fn && fn[1]) { - ex.fileName = fn[1]; - if (ex.fileName.length > 30) { - ex.fileName = '...' + ex.fileName.substr(ex.fileName.length - 27); - } - } - } - if (typeof ex.name == 'undefined' && ex.errorcode) { - ex.name = ex.errorcode; - } - - Y.use('moodle-core-notification-exception', function() { - var modal = new M.core.exception(ex); - - modal.show(); - - pendingPromise.resolve(); - }); + if (targetRegion) { + targetRegion.prepend(newRegion); + return true; } + + return false; + }); +}; + + +/** + * Poll the server for any new notifications. + * + * @returns {Promise} + */ +export const fetchNotifications = async() => { + const Ajax = await import('core/ajax'); + + return Ajax.call([{ + methodname: 'core_fetch_notifications', + args: { + contextid: currentContextId + } + }])[0] + .then(addNotifications); +}; + +/** + * Add all of the supplied notifications. + * + * @param {Array} notifications The list of notificaitons + * @returns {Promise} + */ +const addNotifications = notifications => { + if (!notifications.length) { + return Promise.resolve(); + } + + const pendingPromise = new Pending('core/notification:addNotifications'); + notifications.forEach(notification => renderNotification(notification.template, notification.variables)); + + return pendingPromise.resolve(); +}; + +/** + * Add a notification to the page. + * + * Note: This does not cause the notification to be added to the session. + * + * @param {Object} notification The notification to add. + * @param {string} notification.message The body of the notification + * @param {string} notification.type The type of notification to add (error, warning, info, success). + * @param {Boolean} notification.closebutton Whether to show the close button. + * @param {Boolean} notification.announce Whether to announce to screen readers. + * @returns {Promise} + */ +export const addNotification = notification => { + const pendingPromise = new Pending('core/notification:addNotifications'); + + let template = notificationTypes.error; + + notification = { + closebutton: true, + announce: true, + type: 'error', + ...notification, }; - return /** @alias module:core/notification */{ - init: function(contextid, notifications) { - notificationModule.contextid = contextid; + if (notification.template) { + template = notification.template; + delete notification.template; + } else if (notification.type) { + if (typeof notificationTypes[notification.type] !== 'undefined') { + template = notificationTypes[notification.type]; + } + delete notification.type; + } - // Setup the message target region if it isn't setup already - notificationModule.setupTargetRegion(); + return renderNotification(template, notification) + .then(pendingPromise.resolve); +}; - // Add provided notifications. - notificationModule.addNotifications(notifications); +const renderNotification = async(template, variables) => { + if (typeof variables.message === 'undefined' || !variables.message) { + Log.debug('Notification received without content. Skipping.'); + return; + } - // Poll for any new notifications. - notificationModule.fetchNotifications(); + const pendingPromise = new Pending('core/notification:renderNotification'); + const Templates = await import('core/templates'); + + Templates.renderForPromise(template, variables) + .then(({html, js = ''}) => { + Templates.prependNodeContents(getNotificationRegion(), html, js); + + return; + }) + .then(pendingPromise.resolve) + .catch(exception); +}; + +const getNotificationRegion = () => document.querySelector(Selectors.notificationRegion); + +/** + * Alert dialogue. + * + * @param {String|Promise} title + * @param {String|Promise} message + * @param {String|Promise} cancelText + * @returns {Promise} + */ +export const alert = async(title, message, cancelText) => { + var pendingPromise = new Pending('core/notification:alert'); + + const ModalFactory = await import('core/modal_factory'); + + return ModalFactory.create({ + type: ModalFactory.types.ALERT, + body: message, + title: title, + buttons: { + cancel: cancelText, }, + removeOnClose: true, + }) + .then(function(modal) { + modal.show(); - /** - * Poll the server for any new notifications. - * - * @method fetchNotifications - */ - fetchNotifications: notificationModule.fetchNotifications, + pendingPromise.resolve(); + return modal; + }); +}; - /** - * Add a notification to the page. - * - * Note: This does not cause the notification to be added to the session. - * - * @method addNotification - * @param {Object} notification The notification to add. - * @param {string} notification.message The body of the notification - * @param {string} notification.type The type of notification to add (error, warning, info, success). - * @param {Boolean} notification.closebutton Whether to show the close button. - * @param {Boolean} notification.announce Whether to announce to screen readers. - */ - addNotification: notificationModule.addNotification, +/** + * The confirm has now been replaced with a save and cancel dialogue. + * + * @param {String|Promise} title + * @param {String|Promise} question + * @param {String|Promise} saveLabel + * @param {String|Promise} saveCallback + * @param {String|Promise} cancelCallback + * @returns {Promise} + */ +export const saveCancel = async(title, question, saveLabel, saveCallback, cancelCallback) => { + const pendingPromise = new Pending('core/notification:confirm'); - /** - * Wrap M.core.alert. - * - * @method alert - * @param {string} title - * @param {string} message - * @param {string} yesLabel - */ - alert: notificationModule.alert, + const [ + ModalFactory, + ModalEvents, + ] = await Promise.all([ + import('core/modal_factory'), + import('core/modal_events'), + ]); - /** - * Wrap M.core.confirm. - * - * @method confirm - * @param {string} title - * @param {string} question - * @param {string} yesLabel - * @param {string} noLabel - * @param {function} yesCallback - * @param {function} noCallback Optional parameter to be called if the user presses cancel. - */ - confirm: notificationModule.confirm, + return ModalFactory.create({ + type: ModalFactory.types.SAVE_CANCEL, + title: title, + body: question, + buttons: { + // Note: The noLabel is no longer supported. + save: saveLabel, + }, + removeOnClose: true, + }) + .then(function(modal) { + modal.show(); - /** - * Wrap M.core.exception. - * - * @method exception - * @param {Error} ex - */ - exception: notificationModule.exception - }; -}); + modal.getRoot().on(ModalEvents.save, saveCallback); + modal.getRoot().on(ModalEvents.cancel, cancelCallback); + pendingPromise.resolve(); + + return modal; + }); +}; + +/** + * Wrap M.core.exception. + * + * @param {Error} ex + */ +export const exception = async ex => { + const pendingPromise = new Pending('core/notification:displayException'); + + // Fudge some parameters. + if (!ex.stack) { + ex.stack = ''; + } + + if (ex.debuginfo) { + ex.stack += ex.debuginfo + '\n'; + } + + if (!ex.backtrace && ex.stacktrace) { + ex.backtrace = ex.stacktrace; + } + + if (ex.backtrace) { + ex.stack += ex.backtrace; + const ln = ex.backtrace.match(/line ([^ ]*) of/); + const fn = ex.backtrace.match(/ of ([^:]*): /); + if (ln && ln[1]) { + ex.lineNumber = ln[1]; + } + if (fn && fn[1]) { + ex.fileName = fn[1]; + if (ex.fileName.length > 30) { + ex.fileName = '...' + ex.fileName.substr(ex.fileName.length - 27); + } + } + } + + if (typeof ex.name === 'undefined' && ex.errorcode) { + ex.name = ex.errorcode; + } + + const Y = await import('core/yui'); + Y.use('moodle-core-notification-exception', function() { + var modal = new M.core.exception(ex); + + modal.show(); + + pendingPromise.resolve(); + }); +}; + +/** + * Initialise the page for the suppled context, and displaying the supplied notifications. + * + * @param {Number} contextId + * @param {Array} notificationList + */ +export const init = (contextId, notificationList) => { + currentContextId = contextId; + + // Setup the message target region if it isn't setup already + setupTargetRegion(); + + // Add provided notifications. + addNotifications(notificationList); + + // Perform an initial poll for any new notifications. + fetchNotifications(); +}; + +// To maintain backwards compatability we export default here. +export default { + init, + fetchNotifications, + addNotification, + alert, + confirm, + saveCancel, + exception, +};