%PDF- %PDF-
Direktori : /home/vacivi36/ava/lib/amd/src/ |
Current File : /home/vacivi36/ava/lib/amd/src/notification.js |
// This file is part of Moodle - http://moodle.org/ // // Moodle is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // Moodle is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with Moodle. If not, see <http://www.gnu.org/licenses/>. /** * Notification manager for in-page notifications in Moodle. * * @module core/notification * @copyright 2015 Damyon Wiese <damyon@moodle.com> * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @since 2.9 */ import Pending from 'core/pending'; import Log from 'core/log'; let currentContextId = M.cfg.contextid; const notificationTypes = { success: 'core/notification_success', info: 'core/notification_info', warning: 'core/notification_warning', error: 'core/notification_error', }; const notificationRegionId = 'user-notifications'; const Selectors = { notificationRegion: `#${notificationRegionId}`, fallbackRegionParents: [ '#region-main', '[role="main"]', 'body', ], }; const setupTargetRegion = () => { let targetRegion = getNotificationRegion(); if (targetRegion) { return false; } const newRegion = document.createElement('span'); newRegion.id = notificationRegionId; return Selectors.fallbackRegionParents.some(selector => { const targetRegion = document.querySelector(selector); if (targetRegion) { targetRegion.prepend(newRegion); return true; } return false; }); }; /** * A notification object displayed to a user. * * @typedef {Object} Notification * @property {string} message The body of the notification * @property {string} type The type of notification to add (error, warning, info, success). * @property {Boolean} closebutton Whether to show the close button. * @property {Boolean} announce Whether to announce to screen readers. */ /** * Poll the server for any new notifications. * * @method * @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. * * @method * @param {Notification[]} 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. * * @method * @param {Notification} notification The notification to add. * @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, }; 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; } return renderNotification(template, notification) .then(pendingPromise.resolve); }; const renderNotification = async(template, variables) => { if (typeof variables.message === 'undefined' || !variables.message) { Log.debug('Notification received without content. Skipping.'); return; } 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. * * @method * @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(); pendingPromise.resolve(); return modal; }); }; /** * The confirm has now been replaced with a save and cancel dialogue. * * @method * @param {String|Promise} title * @param {String|Promise} question * @param {String|Promise} saveLabel * @param {String|Promise} noLabel * @param {String|Promise} saveCallback * @param {String|Promise} cancelCallback * @returns {Promise} */ export const confirm = (title, question, saveLabel, noLabel, saveCallback, cancelCallback) => saveCancel(title, question, saveLabel, saveCallback, cancelCallback); /** * The Save and Cancel dialogue helper. * * @method * @param {String|Promise} title * @param {String|Promise} question * @param {String|Promise} saveLabel * @param {String|Promise} saveCallback * @param {String|Promise} cancelCallback * @param {Object} options * @param {HTMLElement} [options.triggerElement=null] The element that triggered the modal (will receive the focus after hidden) * @returns {Promise} */ export const saveCancel = async(title, question, saveLabel, saveCallback, cancelCallback, { triggerElement = null, } = {}) => { const pendingPromise = new Pending('core/notification:confirm'); const [ ModalFactory, ModalEvents, ] = await Promise.all([ import('core/modal_factory'), import('core/modal_events'), ]); 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(); modal.getRoot().on(ModalEvents.save, saveCallback); modal.getRoot().on(ModalEvents.cancel, cancelCallback); modal.getRoot().on(ModalEvents.hidden, () => triggerElement?.focus()); pendingPromise.resolve(); return modal; }); }; /** * 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 * @param {Object} options * @param {HTMLElement} [options.triggerElement=null] The element that triggered the modal (will receive the focus after hidden) * @return {Promise} */ export const saveCancelPromise = (title, question, saveLabel, { triggerElement = null, } = {}) => new Promise((resolve, reject) => { saveCancel(title, question, saveLabel, resolve, reject, {triggerElement}); }); /** * Wrap M.core.exception. * * @method * @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. * * @method * @param {Number} contextId * @param {Notification[]} 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); }; // To maintain backwards compatability we export default here. export default { init, fetchNotifications, addNotification, alert, confirm, saveCancel, saveCancelPromise, exception, };