| 1 | efrain | 1 | // This file is part of Moodle - http://moodle.org/
 | 
        
           |  |  | 2 | //
 | 
        
           |  |  | 3 | // Moodle is free software: you can redistribute it and/or modify
 | 
        
           |  |  | 4 | // it under the terms of the GNU General Public License as published by
 | 
        
           |  |  | 5 | // the Free Software Foundation, either version 3 of the License, or
 | 
        
           |  |  | 6 | // (at your option) any later version.
 | 
        
           |  |  | 7 | //
 | 
        
           |  |  | 8 | // Moodle is distributed in the hope that it will be useful,
 | 
        
           |  |  | 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
        
           |  |  | 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
        
           |  |  | 11 | // GNU General Public License for more details.
 | 
        
           |  |  | 12 | //
 | 
        
           |  |  | 13 | // You should have received a copy of the GNU General Public License
 | 
        
           |  |  | 14 | // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 | 
        
           |  |  | 15 |   | 
        
           |  |  | 16 | /**
 | 
        
           |  |  | 17 |  * Controls the notification popover in the nav bar.
 | 
        
           |  |  | 18 |  *
 | 
        
           |  |  | 19 |  * See template: message_popup/notification_popover
 | 
        
           |  |  | 20 |  *
 | 
        
           |  |  | 21 |  * @module     message_popup/notification_popover_controller
 | 
        
           |  |  | 22 |  * @class      notification_popover_controller
 | 
        
           |  |  | 23 |  * @copyright  2016 Ryan Wyllie <ryan@moodle.com>
 | 
        
           |  |  | 24 |  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 | 
        
           |  |  | 25 |  */
 | 
        
           |  |  | 26 | define(['jquery', 'core/ajax', 'core/templates', 'core/str', 'core/url',
 | 
        
           |  |  | 27 |             'core/notification', 'core/custom_interaction_events', 'core/popover_region_controller',
 | 
        
           | 1441 | ariadna | 28 |             'message_popup/notification_repository', 'message_popup/notification_area_events',
 | 
        
           |  |  | 29 |             'core/local/aria/focuslock',
 | 
        
           |  |  | 30 |         ],
 | 
        
           | 1 | efrain | 31 |         function($, Ajax, Templates, Str, URL, DebugNotification, CustomEvents,
 | 
        
           | 1441 | ariadna | 32 |             PopoverController, NotificationRepo, NotificationAreaEvents, FocusLock) {
 | 
        
           | 1 | efrain | 33 |   | 
        
           |  |  | 34 |     var SELECTORS = {
 | 
        
           |  |  | 35 |         MARK_ALL_READ_BUTTON: '[data-action="mark-all-read"]',
 | 
        
           |  |  | 36 |         ALL_NOTIFICATIONS_CONTAINER: '[data-region="all-notifications"]',
 | 
        
           |  |  | 37 |         NOTIFICATION: '[data-region="notification-content-item-container"]',
 | 
        
           |  |  | 38 |         UNREAD_NOTIFICATION: '[data-region="notification-content-item-container"].unread',
 | 
        
           |  |  | 39 |         NOTIFICATION_LINK: '[data-action="content-item-link"]',
 | 
        
           |  |  | 40 |         EMPTY_MESSAGE: '[data-region="empty-message"]',
 | 
        
           |  |  | 41 |         COUNT_CONTAINER: '[data-region="count-container"]',
 | 
        
           | 1441 | ariadna | 42 |         NOTIFICATION_READ_FEEDBACK: '[data-region="notification-read-feedback"]',
 | 
        
           |  |  | 43 |         CLOSE_NOTIFICATION_POPOVER: '[data-action="close-notification-popover"]',
 | 
        
           | 1 | efrain | 44 |     };
 | 
        
           |  |  | 45 |   | 
        
           |  |  | 46 |     /**
 | 
        
           |  |  | 47 |      * Constructor for the NotificationPopoverController.
 | 
        
           |  |  | 48 |      * Extends PopoverRegionController.
 | 
        
           |  |  | 49 |      *
 | 
        
           |  |  | 50 |      * @param {object} element jQuery object root element of the popover
 | 
        
           |  |  | 51 |      */
 | 
        
           |  |  | 52 |     var NotificationPopoverController = function(element) {
 | 
        
           |  |  | 53 |         // Initialise base class.
 | 
        
           |  |  | 54 |         PopoverController.call(this, element);
 | 
        
           |  |  | 55 |   | 
        
           |  |  | 56 |         this.markAllReadButton = this.root.find(SELECTORS.MARK_ALL_READ_BUTTON);
 | 
        
           |  |  | 57 |         this.unreadCount = 0;
 | 
        
           |  |  | 58 |         this.lastQueried = 0;
 | 
        
           |  |  | 59 |         this.userId = this.root.attr('data-userid');
 | 
        
           |  |  | 60 |         this.container = this.root.find(SELECTORS.ALL_NOTIFICATIONS_CONTAINER);
 | 
        
           |  |  | 61 |         this.limit = 20;
 | 
        
           |  |  | 62 |         this.offset = 0;
 | 
        
           |  |  | 63 |         this.loadedAll = false;
 | 
        
           |  |  | 64 |         this.initialLoad = false;
 | 
        
           |  |  | 65 |   | 
        
           |  |  | 66 |         // Let's find out how many unread notifications there are.
 | 
        
           |  |  | 67 |         this.unreadCount = this.root.find(SELECTORS.COUNT_CONTAINER).html();
 | 
        
           |  |  | 68 |     };
 | 
        
           |  |  | 69 |   | 
        
           |  |  | 70 |     /**
 | 
        
           |  |  | 71 |      * Clone the parent prototype.
 | 
        
           |  |  | 72 |      */
 | 
        
           |  |  | 73 |     NotificationPopoverController.prototype = Object.create(PopoverController.prototype);
 | 
        
           |  |  | 74 |   | 
        
           |  |  | 75 |     /**
 | 
        
           |  |  | 76 |      * Make sure the constructor is set correctly.
 | 
        
           |  |  | 77 |      */
 | 
        
           |  |  | 78 |     NotificationPopoverController.prototype.constructor = NotificationPopoverController;
 | 
        
           |  |  | 79 |   | 
        
           |  |  | 80 |     /**
 | 
        
           |  |  | 81 |      * Set the correct aria label on the menu toggle button to be read out by screen
 | 
        
           |  |  | 82 |      * readers. The message will indicate the state of the unread notifications.
 | 
        
           |  |  | 83 |      *
 | 
        
           |  |  | 84 |      * @method updateButtonAriaLabel
 | 
        
           |  |  | 85 |      */
 | 
        
           |  |  | 86 |     NotificationPopoverController.prototype.updateButtonAriaLabel = function() {
 | 
        
           |  |  | 87 |         if (this.isMenuOpen()) {
 | 
        
           |  |  | 88 |             Str.get_string('hidenotificationwindow', 'message').done(function(string) {
 | 
        
           |  |  | 89 |                 this.menuToggle.attr('aria-label', string);
 | 
        
           |  |  | 90 |             }.bind(this));
 | 
        
           |  |  | 91 |         } else {
 | 
        
           |  |  | 92 |             if (this.unreadCount) {
 | 
        
           |  |  | 93 |                 Str.get_string('shownotificationwindowwithcount', 'message', this.unreadCount).done(function(string) {
 | 
        
           |  |  | 94 |                     this.menuToggle.attr('aria-label', string);
 | 
        
           |  |  | 95 |                 }.bind(this));
 | 
        
           |  |  | 96 |             } else {
 | 
        
           |  |  | 97 |                 Str.get_string('shownotificationwindownonew', 'message').done(function(string) {
 | 
        
           |  |  | 98 |                     this.menuToggle.attr('aria-label', string);
 | 
        
           |  |  | 99 |                 }.bind(this));
 | 
        
           |  |  | 100 |             }
 | 
        
           |  |  | 101 |         }
 | 
        
           |  |  | 102 |     };
 | 
        
           |  |  | 103 |   | 
        
           |  |  | 104 |     /**
 | 
        
           |  |  | 105 |      * Return the jQuery element with the content. This will return either
 | 
        
           |  |  | 106 |      * the unread notification container or the all notification container
 | 
        
           |  |  | 107 |      * depending on which is currently visible.
 | 
        
           |  |  | 108 |      *
 | 
        
           |  |  | 109 |      * @method getContent
 | 
        
           |  |  | 110 |      * @return {object} jQuery object currently visible content contianer
 | 
        
           |  |  | 111 |      */
 | 
        
           |  |  | 112 |     NotificationPopoverController.prototype.getContent = function() {
 | 
        
           |  |  | 113 |         return this.container;
 | 
        
           |  |  | 114 |     };
 | 
        
           |  |  | 115 |   | 
        
           |  |  | 116 |     /**
 | 
        
           |  |  | 117 |      * Get the offset value for the current state of the popover in order
 | 
        
           |  |  | 118 |      * to sent to the backend to correctly paginate the notifications.
 | 
        
           |  |  | 119 |      *
 | 
        
           |  |  | 120 |      * @method getOffset
 | 
        
           |  |  | 121 |      * @return {int} current offset
 | 
        
           |  |  | 122 |      */
 | 
        
           |  |  | 123 |     NotificationPopoverController.prototype.getOffset = function() {
 | 
        
           |  |  | 124 |         return this.offset;
 | 
        
           |  |  | 125 |     };
 | 
        
           |  |  | 126 |   | 
        
           |  |  | 127 |     /**
 | 
        
           |  |  | 128 |      * Increment the offset for the current state, if required.
 | 
        
           |  |  | 129 |      *
 | 
        
           |  |  | 130 |      * @method incrementOffset
 | 
        
           |  |  | 131 |      */
 | 
        
           |  |  | 132 |     NotificationPopoverController.prototype.incrementOffset = function() {
 | 
        
           |  |  | 133 |         this.offset += this.limit;
 | 
        
           |  |  | 134 |     };
 | 
        
           |  |  | 135 |   | 
        
           |  |  | 136 |     /**
 | 
        
           |  |  | 137 |      * Check if the first load of notification has been triggered for the current
 | 
        
           |  |  | 138 |      * state of the popover.
 | 
        
           |  |  | 139 |      *
 | 
        
           |  |  | 140 |      * @method hasDoneInitialLoad
 | 
        
           |  |  | 141 |      * @return {bool} true if first notification loaded, false otherwise
 | 
        
           |  |  | 142 |      */
 | 
        
           |  |  | 143 |     NotificationPopoverController.prototype.hasDoneInitialLoad = function() {
 | 
        
           |  |  | 144 |         return this.initialLoad;
 | 
        
           |  |  | 145 |     };
 | 
        
           |  |  | 146 |   | 
        
           |  |  | 147 |     /**
 | 
        
           |  |  | 148 |      * Check if we've loaded all of the notifications for the current popover
 | 
        
           |  |  | 149 |      * state.
 | 
        
           |  |  | 150 |      *
 | 
        
           |  |  | 151 |      * @method hasLoadedAllContent
 | 
        
           |  |  | 152 |      * @return {bool} true if all notifications loaded, false otherwise
 | 
        
           |  |  | 153 |      */
 | 
        
           |  |  | 154 |     NotificationPopoverController.prototype.hasLoadedAllContent = function() {
 | 
        
           |  |  | 155 |         return this.loadedAll;
 | 
        
           |  |  | 156 |     };
 | 
        
           |  |  | 157 |   | 
        
           |  |  | 158 |     /**
 | 
        
           |  |  | 159 |      * Set the state of the loaded all content property for the current state
 | 
        
           |  |  | 160 |      * of the popover.
 | 
        
           |  |  | 161 |      *
 | 
        
           |  |  | 162 |      * @method setLoadedAllContent
 | 
        
           |  |  | 163 |      * @param {bool} val True if all content is loaded, false otherwise
 | 
        
           |  |  | 164 |      */
 | 
        
           |  |  | 165 |     NotificationPopoverController.prototype.setLoadedAllContent = function(val) {
 | 
        
           |  |  | 166 |         this.loadedAll = val;
 | 
        
           |  |  | 167 |     };
 | 
        
           |  |  | 168 |   | 
        
           |  |  | 169 |     /**
 | 
        
           |  |  | 170 |      * Show the unread notification count badge on the menu toggle if there
 | 
        
           |  |  | 171 |      * are unread notifications, otherwise hide it.
 | 
        
           |  |  | 172 |      *
 | 
        
           |  |  | 173 |      * @method renderUnreadCount
 | 
        
           |  |  | 174 |      */
 | 
        
           |  |  | 175 |     NotificationPopoverController.prototype.renderUnreadCount = function() {
 | 
        
           |  |  | 176 |         var element = this.root.find(SELECTORS.COUNT_CONTAINER);
 | 
        
           |  |  | 177 |   | 
        
           |  |  | 178 |         if (this.unreadCount) {
 | 
        
           |  |  | 179 |             element.text(this.unreadCount);
 | 
        
           |  |  | 180 |             element.removeClass('hidden');
 | 
        
           |  |  | 181 |         } else {
 | 
        
           |  |  | 182 |             element.addClass('hidden');
 | 
        
           |  |  | 183 |         }
 | 
        
           |  |  | 184 |     };
 | 
        
           |  |  | 185 |   | 
        
           |  |  | 186 |     /**
 | 
        
           |  |  | 187 |      * Hide the unread notification count badge on the menu toggle.
 | 
        
           |  |  | 188 |      *
 | 
        
           |  |  | 189 |      * @method hideUnreadCount
 | 
        
           |  |  | 190 |      */
 | 
        
           |  |  | 191 |     NotificationPopoverController.prototype.hideUnreadCount = function() {
 | 
        
           |  |  | 192 |         this.root.find(SELECTORS.COUNT_CONTAINER).addClass('hidden');
 | 
        
           |  |  | 193 |     };
 | 
        
           |  |  | 194 |   | 
        
           |  |  | 195 |     /**
 | 
        
           |  |  | 196 |      * Find the notification element for the given id.
 | 
        
           |  |  | 197 |      *
 | 
        
           |  |  | 198 |      * @param {int} id
 | 
        
           |  |  | 199 |      * @method getNotificationElement
 | 
        
           |  |  | 200 |      * @return {object|null} The notification element
 | 
        
           |  |  | 201 |      */
 | 
        
           |  |  | 202 |     NotificationPopoverController.prototype.getNotificationElement = function(id) {
 | 
        
           |  |  | 203 |         var element = this.root.find(SELECTORS.NOTIFICATION + '[data-id="' + id + '"]');
 | 
        
           |  |  | 204 |         return element.length == 1 ? element : null;
 | 
        
           |  |  | 205 |     };
 | 
        
           |  |  | 206 |   | 
        
           |  |  | 207 |     /**
 | 
        
           |  |  | 208 |      * Render the notification data with the appropriate template and add it to the DOM.
 | 
        
           |  |  | 209 |      *
 | 
        
           |  |  | 210 |      * @method renderNotifications
 | 
        
           |  |  | 211 |      * @param {array} notifications Notification data
 | 
        
           |  |  | 212 |      * @param {object} container jQuery object the container to append the rendered notifications
 | 
        
           |  |  | 213 |      * @return {object} jQuery promise that is resolved when all notifications have been
 | 
        
           |  |  | 214 |      *                  rendered and added to the DOM
 | 
        
           |  |  | 215 |      */
 | 
        
           |  |  | 216 |     NotificationPopoverController.prototype.renderNotifications = function(notifications, container) {
 | 
        
           |  |  | 217 |         var promises = [];
 | 
        
           |  |  | 218 |   | 
        
           |  |  | 219 |         $.each(notifications, function(index, notification) {
 | 
        
           |  |  | 220 |             // Determine what the offset was when loading this notification.
 | 
        
           |  |  | 221 |             var offset = this.getOffset() - this.limit;
 | 
        
           |  |  | 222 |             // Update the view more url to contain the offset to allow the notifications
 | 
        
           |  |  | 223 |             // page to load to the correct position in the list of notifications.
 | 
        
           |  |  | 224 |             notification.viewmoreurl = URL.relativeUrl('/message/output/popup/notifications.php', {
 | 
        
           |  |  | 225 |                 notificationid: notification.id,
 | 
        
           |  |  | 226 |                 offset: offset,
 | 
        
           |  |  | 227 |             });
 | 
        
           |  |  | 228 |   | 
        
           |  |  | 229 |             // Link to mark read page before loading the actual link.
 | 
        
           |  |  | 230 |             var notificationurlparams = {
 | 
        
           |  |  | 231 |                 notificationid: notification.id
 | 
        
           |  |  | 232 |             };
 | 
        
           |  |  | 233 |   | 
        
           |  |  | 234 |             notification.contexturl = URL.relativeUrl('message/output/popup/mark_notification_read.php', notificationurlparams);
 | 
        
           |  |  | 235 |   | 
        
           |  |  | 236 |             var promise = Templates.render('message_popup/notification_content_item', notification)
 | 
        
           |  |  | 237 |             .then(function(html, js) {
 | 
        
           |  |  | 238 |                 return {html: html, js: js};
 | 
        
           |  |  | 239 |             });
 | 
        
           |  |  | 240 |             promises.push(promise);
 | 
        
           |  |  | 241 |         }.bind(this));
 | 
        
           |  |  | 242 |   | 
        
           |  |  | 243 |         return $.when.apply($, promises).then(function() {
 | 
        
           |  |  | 244 |             // Each of the promises in the when will pass its results as an argument to the function.
 | 
        
           |  |  | 245 |             // The order of the arguments will be the order that the promises are passed to when()
 | 
        
           |  |  | 246 |             // i.e. the first promise's results will be in the first argument.
 | 
        
           |  |  | 247 |             $.each(arguments, function(index, argument) {
 | 
        
           |  |  | 248 |                 container.append(argument.html);
 | 
        
           |  |  | 249 |                 Templates.runTemplateJS(argument.js);
 | 
        
           |  |  | 250 |             });
 | 
        
           |  |  | 251 |             return;
 | 
        
           |  |  | 252 |         });
 | 
        
           |  |  | 253 |     };
 | 
        
           |  |  | 254 |   | 
        
           |  |  | 255 |     /**
 | 
        
           |  |  | 256 |      * Send a request for more notifications from the server, if we aren't already
 | 
        
           |  |  | 257 |      * loading some and haven't already loaded all of them.
 | 
        
           |  |  | 258 |      *
 | 
        
           |  |  | 259 |      * Takes into account the current mode of the popover and will request only
 | 
        
           |  |  | 260 |      * unread notifications if required.
 | 
        
           |  |  | 261 |      *
 | 
        
           |  |  | 262 |      * All notifications are marked as read by the server when they are returned.
 | 
        
           |  |  | 263 |      *
 | 
        
           |  |  | 264 |      * @method loadMoreNotifications
 | 
        
           |  |  | 265 |      * @return {object} jQuery promise that is resolved when notifications have been
 | 
        
           |  |  | 266 |      *                        retrieved and added to the DOM
 | 
        
           |  |  | 267 |      */
 | 
        
           |  |  | 268 |     NotificationPopoverController.prototype.loadMoreNotifications = function() {
 | 
        
           |  |  | 269 |         if (this.isLoading || this.hasLoadedAllContent()) {
 | 
        
           |  |  | 270 |             return $.Deferred().resolve();
 | 
        
           |  |  | 271 |         }
 | 
        
           |  |  | 272 |   | 
        
           |  |  | 273 |         this.startLoading();
 | 
        
           |  |  | 274 |         var request = {
 | 
        
           |  |  | 275 |             limit: this.limit,
 | 
        
           |  |  | 276 |             offset: this.getOffset(),
 | 
        
           |  |  | 277 |             useridto: this.userId,
 | 
        
           |  |  | 278 |         };
 | 
        
           |  |  | 279 |   | 
        
           |  |  | 280 |         var container = this.getContent();
 | 
        
           |  |  | 281 |         return NotificationRepo.query(request).then(function(result) {
 | 
        
           |  |  | 282 |             var notifications = result.notifications;
 | 
        
           |  |  | 283 |             this.unreadCount = result.unreadcount;
 | 
        
           |  |  | 284 |             this.lastQueried = Math.floor(new Date().getTime() / 1000);
 | 
        
           |  |  | 285 |             this.setLoadedAllContent(!notifications.length || notifications.length < this.limit);
 | 
        
           |  |  | 286 |             this.initialLoad = true;
 | 
        
           |  |  | 287 |             this.updateButtonAriaLabel();
 | 
        
           |  |  | 288 |   | 
        
           |  |  | 289 |             if (notifications.length) {
 | 
        
           |  |  | 290 |                 this.incrementOffset();
 | 
        
           |  |  | 291 |                 return this.renderNotifications(notifications, container);
 | 
        
           |  |  | 292 |             }
 | 
        
           |  |  | 293 |   | 
        
           |  |  | 294 |             return false;
 | 
        
           |  |  | 295 |         }.bind(this))
 | 
        
           |  |  | 296 |         .always(function() {
 | 
        
           |  |  | 297 |             this.stopLoading();
 | 
        
           |  |  | 298 |         }.bind(this));
 | 
        
           |  |  | 299 |     };
 | 
        
           |  |  | 300 |   | 
        
           |  |  | 301 |     /**
 | 
        
           |  |  | 302 |      * Send a request to the server to mark all unread notifications as read and update
 | 
        
           |  |  | 303 |      * the unread count and unread notification elements appropriately.
 | 
        
           |  |  | 304 |      *
 | 
        
           |  |  | 305 |      * @return {Promise}
 | 
        
           |  |  | 306 |      * @method markAllAsRead
 | 
        
           |  |  | 307 |      */
 | 
        
           |  |  | 308 |     NotificationPopoverController.prototype.markAllAsRead = function() {
 | 
        
           |  |  | 309 |         this.markAllReadButton.addClass('loading');
 | 
        
           |  |  | 310 |   | 
        
           |  |  | 311 |         var request = {
 | 
        
           |  |  | 312 |             useridto: this.userId,
 | 
        
           |  |  | 313 |             timecreatedto: this.lastQueried,
 | 
        
           |  |  | 314 |         };
 | 
        
           |  |  | 315 |   | 
        
           |  |  | 316 |         return NotificationRepo.markAllAsRead(request)
 | 
        
           |  |  | 317 |             .then(function() {
 | 
        
           |  |  | 318 |                 this.unreadCount = 0;
 | 
        
           |  |  | 319 |                 this.root.find(SELECTORS.UNREAD_NOTIFICATION).removeClass('unread');
 | 
        
           | 1441 | ariadna | 320 |   | 
        
           |  |  | 321 |                 // Set the ARIA live region's contents with the feedback.
 | 
        
           |  |  | 322 |                 const readFeedback = this.root.get(0).querySelector(SELECTORS.NOTIFICATION_READ_FEEDBACK);
 | 
        
           |  |  | 323 |                 Str.get_string('notificationsmarkedasread', 'message').done((notificationsmarkedasread) => {
 | 
        
           |  |  | 324 |                     readFeedback.innerHTML = notificationsmarkedasread;
 | 
        
           |  |  | 325 |                 });
 | 
        
           | 1 | efrain | 326 |             }.bind(this))
 | 
        
           |  |  | 327 |             .always(function() {
 | 
        
           |  |  | 328 |                 this.markAllReadButton.removeClass('loading');
 | 
        
           |  |  | 329 |             }.bind(this));
 | 
        
           |  |  | 330 |     };
 | 
        
           |  |  | 331 |   | 
        
           |  |  | 332 |     /**
 | 
        
           |  |  | 333 |      * Add all of the required event listeners for this notification popover.
 | 
        
           |  |  | 334 |      *
 | 
        
           |  |  | 335 |      * @method registerEventListeners
 | 
        
           |  |  | 336 |      */
 | 
        
           |  |  | 337 |     NotificationPopoverController.prototype.registerEventListeners = function() {
 | 
        
           |  |  | 338 |         CustomEvents.define(this.root, [
 | 
        
           |  |  | 339 |             CustomEvents.events.activate,
 | 
        
           |  |  | 340 |         ]);
 | 
        
           |  |  | 341 |   | 
        
           |  |  | 342 |         // Mark all notifications read if the user activates the mark all as read button.
 | 
        
           |  |  | 343 |         this.root.on(CustomEvents.events.activate, SELECTORS.MARK_ALL_READ_BUTTON, function(e, data) {
 | 
        
           | 1441 | ariadna | 344 |             if (this.unreadCount > 0) {
 | 
        
           |  |  | 345 |                 this.markAllAsRead();
 | 
        
           |  |  | 346 |             }
 | 
        
           |  |  | 347 |   | 
        
           | 1 | efrain | 348 |             e.stopPropagation();
 | 
        
           |  |  | 349 |             data.originalEvent.preventDefault();
 | 
        
           |  |  | 350 |         }.bind(this));
 | 
        
           |  |  | 351 |   | 
        
           |  |  | 352 |         // Mark individual notification read if the user activates it.
 | 
        
           |  |  | 353 |         this.root.on(CustomEvents.events.activate, SELECTORS.NOTIFICATION_LINK, function(e) {
 | 
        
           |  |  | 354 |             var element = $(e.target).closest(SELECTORS.NOTIFICATION);
 | 
        
           |  |  | 355 |   | 
        
           |  |  | 356 |             if (element.hasClass('unread')) {
 | 
        
           |  |  | 357 |                 this.unreadCount--;
 | 
        
           |  |  | 358 |                 element.removeClass('unread');
 | 
        
           |  |  | 359 |             }
 | 
        
           |  |  | 360 |   | 
        
           |  |  | 361 |             e.stopPropagation();
 | 
        
           |  |  | 362 |         }.bind(this));
 | 
        
           |  |  | 363 |   | 
        
           | 1441 | ariadna | 364 |         this.root.on(CustomEvents.events.activate, SELECTORS.CLOSE_NOTIFICATION_POPOVER, function(e) {
 | 
        
           |  |  | 365 |             e.preventDefault();
 | 
        
           |  |  | 366 |             $(this.root).trigger(CustomEvents.events.escape);
 | 
        
           |  |  | 367 |             e.stopPropagation();
 | 
        
           |  |  | 368 |         }.bind(this));
 | 
        
           |  |  | 369 |   | 
        
           | 1 | efrain | 370 |         // Update the notification information when the menu is opened.
 | 
        
           |  |  | 371 |         this.root.on(this.events().menuOpened, function() {
 | 
        
           |  |  | 372 |             this.hideUnreadCount();
 | 
        
           |  |  | 373 |             this.updateButtonAriaLabel();
 | 
        
           |  |  | 374 |   | 
        
           |  |  | 375 |             if (!this.hasDoneInitialLoad()) {
 | 
        
           |  |  | 376 |                 this.loadMoreNotifications();
 | 
        
           |  |  | 377 |             }
 | 
        
           | 1441 | ariadna | 378 |   | 
        
           |  |  | 379 |             // Lock focus to the popover when it is opened, it is the parent of the container.
 | 
        
           |  |  | 380 |             const contentContainer = this.getContentContainer()[0].parentNode;
 | 
        
           |  |  | 381 |             FocusLock.trapFocus(contentContainer);
 | 
        
           |  |  | 382 |   | 
        
           | 1 | efrain | 383 |         }.bind(this));
 | 
        
           |  |  | 384 |   | 
        
           |  |  | 385 |         // Update the unread notification count when the menu is closed.
 | 
        
           |  |  | 386 |         this.root.on(this.events().menuClosed, function() {
 | 
        
           |  |  | 387 |             this.renderUnreadCount();
 | 
        
           |  |  | 388 |             this.updateButtonAriaLabel();
 | 
        
           | 1441 | ariadna | 389 |             // Lock focus to the popover when it is opened, it is the parent of the container.
 | 
        
           |  |  | 390 |             FocusLock.untrapFocus();
 | 
        
           | 1 | efrain | 391 |         }.bind(this));
 | 
        
           |  |  | 392 |   | 
        
           |  |  | 393 |         // Set aria attributes when popover is loading.
 | 
        
           |  |  | 394 |         this.root.on(this.events().startLoading, function() {
 | 
        
           |  |  | 395 |             this.getContent().attr('aria-busy', 'true');
 | 
        
           |  |  | 396 |         }.bind(this));
 | 
        
           |  |  | 397 |   | 
        
           |  |  | 398 |         // Set aria attributes when popover is finished loading.
 | 
        
           |  |  | 399 |         this.root.on(this.events().stopLoading, function() {
 | 
        
           |  |  | 400 |             this.getContent().attr('aria-busy', 'false');
 | 
        
           |  |  | 401 |         }.bind(this));
 | 
        
           |  |  | 402 |   | 
        
           |  |  | 403 |         // Load more notifications if the user has scrolled to the end of content
 | 
        
           |  |  | 404 |         // item list.
 | 
        
           |  |  | 405 |         this.getContentContainer().on(CustomEvents.events.scrollBottom, function() {
 | 
        
           |  |  | 406 |             if (!this.isLoading && !this.hasLoadedAllContent()) {
 | 
        
           |  |  | 407 |                 this.loadMoreNotifications();
 | 
        
           |  |  | 408 |             }
 | 
        
           |  |  | 409 |         }.bind(this));
 | 
        
           |  |  | 410 |   | 
        
           |  |  | 411 |         // Stop mouse scroll from propagating to the window element and
 | 
        
           |  |  | 412 |         // scrolling the page.
 | 
        
           |  |  | 413 |         CustomEvents.define(this.getContentContainer(), [
 | 
        
           |  |  | 414 |             CustomEvents.events.scrollLock
 | 
        
           |  |  | 415 |         ]);
 | 
        
           |  |  | 416 |   | 
        
           |  |  | 417 |         // Listen for when a notification is shown in the notifications page and mark
 | 
        
           |  |  | 418 |         // it as read, if it's unread.
 | 
        
           |  |  | 419 |         $(document).on(NotificationAreaEvents.notificationShown, function(e, notification) {
 | 
        
           |  |  | 420 |             if (!notification.read) {
 | 
        
           |  |  | 421 |                 var element = this.getNotificationElement(notification.id);
 | 
        
           |  |  | 422 |   | 
        
           |  |  | 423 |                 if (element) {
 | 
        
           |  |  | 424 |                     element.removeClass('unread');
 | 
        
           |  |  | 425 |                 }
 | 
        
           |  |  | 426 |   | 
        
           |  |  | 427 |                 this.unreadCount--;
 | 
        
           |  |  | 428 |                 this.renderUnreadCount();
 | 
        
           |  |  | 429 |             }
 | 
        
           |  |  | 430 |         }.bind(this));
 | 
        
           |  |  | 431 |     };
 | 
        
           |  |  | 432 |   | 
        
           |  |  | 433 |     return NotificationPopoverController;
 | 
        
           |  |  | 434 | });
 |