313 lines
8.8 KiB
JavaScript
313 lines
8.8 KiB
JavaScript
/**
|
|
* @file js/controllers/NotificationHandler.js
|
|
*
|
|
* Copyright (c) 2014-2021 Simon Fraser University
|
|
* Copyright (c) 2000-2021 John Willinsky
|
|
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
|
|
*
|
|
* @class NotificationHandler
|
|
* @ingroup js_controllers
|
|
*
|
|
* @brief Handle in place notifications.
|
|
*/
|
|
(function($) {
|
|
|
|
|
|
/**
|
|
* @constructor
|
|
*
|
|
* @extends $.pkp.classes.Handler
|
|
*
|
|
* @param {jQueryObject} $notificationElement The html notification element.
|
|
* @param {Object} options Notification options.
|
|
*/
|
|
$.pkp.controllers.NotificationHandler =
|
|
function($notificationElement, options) {
|
|
this.parent($notificationElement, options);
|
|
|
|
this.options_ = options;
|
|
|
|
this.bind('notifyUser', this.fetchNotificationHandler_);
|
|
|
|
// Hide the notification element.
|
|
this.getHtmlElement().hide();
|
|
|
|
// Trigger the notify user event without bubbling up.
|
|
this.getHtmlElement().triggerHandler('notifyUser');
|
|
|
|
if (this.options_.refreshOn) {
|
|
this.bindGlobal(this.options_.refreshOn, this.fetchNotificationHandler_);
|
|
}
|
|
};
|
|
$.pkp.classes.Helper.inherits(
|
|
$.pkp.controllers.NotificationHandler,
|
|
$.pkp.classes.Handler);
|
|
|
|
|
|
//
|
|
// Private properties.
|
|
//
|
|
/**
|
|
* The options to fetch a notification.
|
|
* @private
|
|
* @type {Object?}
|
|
*/
|
|
$.pkp.controllers.NotificationHandler.prototype.options_ = null;
|
|
|
|
|
|
/**
|
|
* Time to hide trivial inplace notifications.
|
|
* @private
|
|
* @type {number?}
|
|
*/
|
|
$.pkp.controllers.NotificationHandler.prototype.trivialTimer_ = null;
|
|
|
|
|
|
//
|
|
// Private methods.
|
|
//
|
|
/**
|
|
* Handler to fetch the notification data.
|
|
* @private
|
|
*/
|
|
$.pkp.controllers.NotificationHandler.prototype.fetchNotificationHandler_ =
|
|
function() {
|
|
|
|
var requestOptions = {};
|
|
requestOptions.requestOptions = this.options_.requestOptions;
|
|
|
|
// Avoid race conditions with other notification controllers.
|
|
$.ajax({
|
|
type: 'POST',
|
|
url: this.options_.fetchNotificationUrl,
|
|
data: requestOptions,
|
|
success: this.callbackWrapper(this.showNotificationResponseHandler_),
|
|
dataType: 'json',
|
|
async: false
|
|
});
|
|
};
|
|
|
|
|
|
/**
|
|
* Callback to show the notification data in place.
|
|
*
|
|
* @param {Object} ajaxContext The AJAX request context.
|
|
* @param {Object} jsonData A parsed JSON response object.
|
|
* @private
|
|
*/
|
|
$.pkp.controllers.NotificationHandler.prototype.
|
|
showNotificationResponseHandler_ = function(ajaxContext, jsonData) {
|
|
// Delete any existing trivial notification timer.
|
|
clearTimeout(this.trivialTimer_);
|
|
|
|
var $notificationElement = this.getHtmlElement(),
|
|
workingJsonData = this.handleJson(jsonData),
|
|
inPlaceNotificationsData, newNotificationsData,
|
|
trivialNotificationsId, notificationId, i;
|
|
|
|
if (workingJsonData === false) {
|
|
return;
|
|
}
|
|
if (workingJsonData.content.inPlace) {
|
|
inPlaceNotificationsData = this.concatenateNotifications_(
|
|
workingJsonData.content.inPlace);
|
|
newNotificationsData = this.removeAlreadyShownNotifications_(
|
|
/** @type {Object} */ (workingJsonData));
|
|
|
|
this.unbindPartial($notificationElement);
|
|
$notificationElement.html(inPlaceNotificationsData);
|
|
|
|
// We need to show the notification element now so the
|
|
// visibility test can be executed.
|
|
$notificationElement.show();
|
|
|
|
trivialNotificationsId = this.getTrivialNotifications_(
|
|
workingJsonData.content.inPlace);
|
|
|
|
if (!(this.visibleWithoutScrolling_()) && newNotificationsData) {
|
|
// In place notification is not visible. Let parent widgets
|
|
// show the notification data.
|
|
$notificationElement.parent().
|
|
trigger('notifyUser', [newNotificationsData]);
|
|
|
|
// Remove in place trivial notifications.
|
|
for (i in trivialNotificationsId) {
|
|
notificationId = trivialNotificationsId[i];
|
|
$notificationElement =
|
|
$('#pkp_notification_' + notificationId,
|
|
this.getHtmlElement());
|
|
this.unbindPartial($notificationElement);
|
|
$notificationElement.remove();
|
|
}
|
|
}
|
|
|
|
// After visibility test and possible trivial notifications
|
|
// removal, we need to test if the in place notification widget
|
|
// shows any notification. If not, hide it.
|
|
if ($notificationElement.children().length === 0) {
|
|
$notificationElement.hide();
|
|
} else {
|
|
// Add a timer to any trivial notifications
|
|
// inside this widget.
|
|
this.addTimerToNotifications(trivialNotificationsId);
|
|
}
|
|
|
|
} else {
|
|
this.unbindPartial(this.getHtmlElement());
|
|
this.getHtmlElement().empty();
|
|
this.getHtmlElement().hide();
|
|
}
|
|
};
|
|
|
|
|
|
/**
|
|
* Check if the notification is inside the window are visible.
|
|
* @return {boolean} True iff the notification is visible.
|
|
* @private
|
|
*/
|
|
$.pkp.controllers.NotificationHandler.prototype.
|
|
visibleWithoutScrolling_ = function() {
|
|
var $notificationElement = this.getHtmlElement(),
|
|
notificationTop = $notificationElement.offset().top,
|
|
notificationMiddle = notificationTop + this.getHtmlElement().height() / 2,
|
|
windowScrollTop = $(window).scrollTop(),
|
|
windowBottom = windowScrollTop + $(window).height(),
|
|
// Consider modals and its own scroll functionality.
|
|
$parentModalContentWrapper = $notificationElement
|
|
.parents('.ui-dialog-content'),
|
|
modalContentTop, modalContentBottom;
|
|
|
|
if ($parentModalContentWrapper.length > 0) {
|
|
modalContentTop = $parentModalContentWrapper.offset().top;
|
|
modalContentBottom = modalContentTop +
|
|
$parentModalContentWrapper.height();
|
|
if (notificationMiddle < modalContentTop ||
|
|
notificationMiddle > modalContentBottom) {
|
|
// The element is outside of the modal content wrapper area.
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Check if the element is inside of the visible window area.
|
|
if (notificationMiddle < windowScrollTop ||
|
|
notificationMiddle > windowBottom) {
|
|
return false;
|
|
} else {
|
|
return true;
|
|
}
|
|
};
|
|
|
|
|
|
/**
|
|
* Remove notification data from object that is already on page.
|
|
* @param {Object} notificationsData The notification data to perform
|
|
* the deletion on.
|
|
* @return {Object|boolean} Notification data after deletion.
|
|
* @private
|
|
*/
|
|
$.pkp.controllers.NotificationHandler.prototype.
|
|
removeAlreadyShownNotifications_ = function(notificationsData) {
|
|
|
|
var workingNotificationsData = /** @type {{ content: {
|
|
inPlace: Array, general: Array} }} */
|
|
(notificationsData),
|
|
emptyObject = true,
|
|
levelId, notificationId, element;
|
|
for (levelId in workingNotificationsData.content.inPlace) {
|
|
for (notificationId in
|
|
workingNotificationsData.content.inPlace[levelId]) {
|
|
element = $('#pkp_notification_' + notificationId);
|
|
if (element.length > 0) {
|
|
delete workingNotificationsData.content.
|
|
inPlace[levelId][notificationId];
|
|
delete workingNotificationsData.content.
|
|
general[levelId][notificationId];
|
|
} else {
|
|
emptyObject = false;
|
|
}
|
|
}
|
|
}
|
|
if (emptyObject) {
|
|
return false;
|
|
} else {
|
|
return workingNotificationsData;
|
|
}
|
|
};
|
|
|
|
|
|
/**
|
|
* Concatenate notification data in a string variable.
|
|
* @param {Object} notificationsData The notification data to assemble
|
|
* the concatenation from.
|
|
* @return {string} The concatenated notification data.
|
|
* @private
|
|
*/
|
|
$.pkp.controllers.NotificationHandler.prototype.
|
|
concatenateNotifications_ = function(notificationsData) {
|
|
var returner = '', levelId, notificationId;
|
|
for (levelId in notificationsData) {
|
|
for (notificationId in notificationsData[levelId]) {
|
|
// Concatenate all notifications.
|
|
returner += notificationsData[levelId][notificationId];
|
|
}
|
|
}
|
|
|
|
return returner;
|
|
};
|
|
|
|
|
|
/**
|
|
* Get all trivial notifications id inside the passed notifications.
|
|
* @param {Object} notificationsData The data returned from the fetch
|
|
* notification request.
|
|
* @return {Array} The trivial notifications id.
|
|
* @private
|
|
*/
|
|
$.pkp.controllers.NotificationHandler.prototype.
|
|
getTrivialNotifications_ = function(notificationsData) {
|
|
|
|
var trivialNotificationsId = [], levelId, notificationId;
|
|
for (levelId in notificationsData) {
|
|
if (levelId == 1) { // Trivial level.
|
|
for (notificationId in notificationsData[levelId]) {
|
|
trivialNotificationsId.push(notificationId);
|
|
}
|
|
}
|
|
}
|
|
|
|
return trivialNotificationsId;
|
|
};
|
|
|
|
|
|
/**
|
|
* Add a timer for passed notifications to hide them after a time.
|
|
* @param {Object} notificationsId Array with the notifications id
|
|
* that will receive the timer.
|
|
*/
|
|
$.pkp.controllers.NotificationHandler.prototype.
|
|
addTimerToNotifications = function(notificationsId) {
|
|
|
|
var self, removeNotification;
|
|
|
|
self = this; // remember "this" reference for later use in callback
|
|
|
|
removeNotification = function() {
|
|
var $notification = $(this);
|
|
self.unbindPartial($notification);
|
|
$notification.remove();
|
|
};
|
|
|
|
if (notificationsId.length) {
|
|
this.trivialTimer_ = setTimeout(function() {
|
|
var notificationId, $notification;
|
|
for (notificationId in notificationsId) {
|
|
$notification = $('#pkp_notification_' +
|
|
notificationsId[notificationId]);
|
|
$notification.fadeOut(400, removeNotification);
|
|
}
|
|
}, 6000);
|
|
}
|
|
};
|
|
|
|
}(jQuery));
|