Mercurial > hg > dml-open-vis
view src/DML/MainVisBundle/Resources/assets/marionette/modules/NotificationsModule.js @ 1:f38015048f48 tip
Added GPL
author | Daniel Wolff |
---|---|
date | Sat, 13 Feb 2016 20:43:38 +0100 |
parents | 493bcb69166c |
children |
line wrap: on
line source
"use strict"; /** * Notification options: * * id * null or string * if the same as before, the old notification with the same id is replaced with the current one * * modifiers * [string1, string2] * bem modifiers (define look and feel) * availabilities: * no-padding * type_warning * type_error * ttl_ever * ttl_10 * ttl_11 * ttl_20 * ... * * content * text / jquery object * what to show in the notification * * onClose * function(options) * what to do when the notification is manually closed * * onExpire * function(options) * what to do when the notification is removed automatically * * keepContentInMemoryAfterRemoval * does not destroy the content on notification removal (it retains DOM nodes and event listeners, which can be reused later) * */ App.module("NotificationsModule", function(NotificationsModule, App, Backbone, Marionette, $, _, Logger) { // Prevent auto start NotificationsModule.startWithParent = false; // Define options var defaultModuleOptions = { defaultTTL: 10, // time to live minTTLOnUnfreeze: 6 // time to live after the notification is unfrozen (mouse went out) }; var moduleOptions; // Define private variables var logger = null; var $notifications = null; var $notificationStack = null; // Define private methods var addNewNotification = null; var removeNotification = null; var updateNotification = null; var notificationTimeoutHandler = null; /* * Structure of "$notification" object * .notification_body * .notification_content * .notification_closer * data * options * ... */ /** * Initialization checker */ var assertModuleIsInitialized = function() { if (!$notifications) { throw "NotificationsModule has not been initialized"; } }; /** * Module initializer * */ NotificationsModule.addInitializer(function(options){ moduleOptions = _.extend(defaultModuleOptions, options); logger = Logger.get("NotificationsModule"); //logger.setLevel(Logger.DEBUG); logger.debug("Begin init"); $notifications = $(".app__notifications") .addClass("notifications"); $notificationStack = $.bem.generateElement("notifications", "stack") .appendTo($notifications); // Sets up the internals of the notification var setupNotification = function($notification, notificationOptions) { // Content var $notificationContent = $notification.children().eq(0).children().eq(0); if (!$notificationContent.hasClass("notification__content")) { $notificationContent = $notificationContent.children().eq(0); } $notificationContent.children().detach(); if (notificationOptions.content instanceof $) { $notificationContent.append(notificationOptions.content); } else { $notificationContent.html(notificationOptions.content); } $notification.attr("data-id", notificationOptions.id); $notification.data("options", notificationOptions); $notification.data("$content", $notificationContent); // Update css modifiers var modifiersAsArray = null; if (_.isString(notificationOptions.modifiers)) { modifiersAsArray = notificationOptions.modifiers.split(" "); } if (_.isArray(notificationOptions.modifiers)){ modifiersAsArray = notificationOptions.modifiers; } var state = $notification.getMod("notification", "state"); $notification.attr("class", "notification notification_state_" + state); if (_.isArray(modifiersAsArray) && modifiersAsArray.length) { $notification.addClass("notification_" + modifiersAsArray.join(" notification_")); } // Set ttl (time to live) if needed if (!$notification.getMod("notification", "ttl")) { $notification.setMod("notification", "ttl", moduleOptions.defaultTTL); } }; var freezeTTL = function () { logger.debug("Freeze ttl", this); $(this).addClass("notification_ttl-frozen"); }; var unfreezeTTL = function () { logger.debug("Unfreeze ttl", this); var $this = $(this); var ttl = $this.getMod("notification", "ttl"); if (ttl * 1 == ttl && ttl < moduleOptions.minTTLOnUnfreeze) { $this.setMod("notification", "ttl", moduleOptions.minTTLOnUnfreeze); } $this.removeClass("notification_ttl-frozen"); }; var stopTTL = function () { logger.debug("Stop ttl", this); $(this).setMod("notification", "ttl", "ever"); }; // A function that adds a new notification addNewNotification = function(notificationOptions) { logger.info("Adding new notificaton", notificationOptions); var $notificationContent = $.bem.generateElement("notification", "content"); var $notificationCloser = $.bem.generateElement("notification", "closer"); var $notificationBody = $.bem.generateElement("notification", "body") .append($notificationContent, $notificationCloser); var $notification = $.bem.generateBlock("notification") .append($notificationBody); var closeFunction = function() { removeNotification($notification); var onClose = notificationOptions.onClose; if (_.isFunction(onClose)) { try { onClose($notification); } catch (e) { logger.error("Error when handling onClose", e, $notification, onClose); } } }; $notificationCloser.click(closeFunction); $notification.mouseover(freezeTTL); $notification.mouseout(unfreezeTTL); $notification.click(stopTTL); setupNotification($notification, notificationOptions); $notification.setMod("notification", "state", "pre-shown"); $notification.appendTo($notificationStack); var notificationBodyHeight = $notificationBody.outerHeight(); $notification.height(notificationBodyHeight); $notification.hide(); $notificationBody.hide(); // First show the container, and then slide the body $notification.show("blind", { direction: "up", //easing: "easeInOutQuart" }, $notificationStack.children().length == 1 ? 1 : 100 + notificationBodyHeight / 10 * 30, function() { $notificationBody.show("slide", { direction: "right", easing: "easeOutQuart" }, 500, function() { $notification.setMod("notification", "state", "shown"); }); }); }; // A function that replaces the contents of the given notification updateNotification = function($notification, notificationOptions) { logger.info("Updating notification", $notification, "with new options", notificationOptions); if ($notification.length !== 1) { logger.error(_.str.sprintf("Can’t replace a notification, this can be done only when $notification contains a single instance.", $notification)); return; } // Set up the notification again setupNotification($notification, notificationOptions); // Pay user's attention to this notification if (!$notification.is(":animated") && $notification.hasClass("notification_state_shown")) { $notification.effect("shake", { direction: "up", distance: 5, times: 2 }); } }; // Removes the notification (hides it and removes) removeNotification = function($notification) { logger.debug("Begin remove notification", $notification); if ($notification.hasMod("notification", "state_pre-hidden")) { logger.debug("Notification", $notification, "is already being hidden by another handler"); return; }; $notification.setMod("notification", "state", "pre-hidden"); // First fade the body and then blind the container var $notificationBody = $notification.children().eq(0); var notificationBodyHeight = $notificationBody.outerHeight(); $notification.children().eq(0).fadeOut( 200, function() { $notification.hide("blind", { direction: "up", }, 100 + notificationBodyHeight / 10 * 30, function() { if ($notification.data("options").keepContentInMemoryAfterRemoval) { $notification.data("$content").detach(); } $notification.remove(); }); }); }; // Every second decrease ttl (time to live) for each notification notificationTimeoutHandler = setInterval(function() { logger.debug("Notification lifetime interval triggered"); $notificationStack .children() .filter(".notification_state_shown") .not(".notification_ttl-frozen") // this class is set to notification when it is hovered over with a mouse .each(function() { var $thisNotification = $(this); var ttl = $thisNotification.getMod("notification", "ttl"); if (ttl * 1 != ttl) { return; } ttl = ttl - 1; if (ttl < 0) { removeNotification($thisNotification); var onExpire = $thisNotification.data("options").onExpire; if (_.isFunction(onExpire)) { try { onExpire($thisNotification); } catch (e) { logger.error("Error when handling onExpire", e, $thisNotification, onExpire); } } } else { $thisNotification.setMod("notification", "ttl", ttl); } }); }, 1000); logger.debug("End init"); }); /** * Show a notification */ NotificationsModule.show = function(notificationOptions) { assertModuleIsInitialized(); logger.debug("Begin showing a new notification", notificationOptions); var extendedNotificationOptions = _.extend({ id: null, modifiers: null, content: "", onClose: null, onExpire: null, keepContentInMemoryAfterRemoval: false }, notificationOptions); // Attempt to find and update notification with the same id (if it has been defined) // Or to create a new notification if (extendedNotificationOptions.id) { var $notificationWithTheSameId = $notificationStack.find(_.str.sprintf(".notification[data-id='%s']", extendedNotificationOptions.id)).not(".notification_state_pre-hidden"); logger.debug("Detection of a notification witht the same id", extendedNotificationOptions.id, $notificationWithTheSameId, $notificationStack.children()); if ($notificationWithTheSameId.length && ($notificationWithTheSameId.hasMod("notification", "state_shown") || $notificationWithTheSameId.hasMod("notification", "state_pre-shown"))) { if ($notificationWithTheSameId.offset().top < 0) { removeNotification($notificationWithTheSameId); addNewNotification(extendedNotificationOptions); } else { updateNotification($notificationWithTheSameId, extendedNotificationOptions); } } else { addNewNotification(extendedNotificationOptions); } } else { addNewNotification(extendedNotificationOptions); } logger.debug("End showing a new notification", notificationOptions); }; /** * Hide a notification */ NotificationsModule.hide = function(notificationId) { var $notification = $notificationStack.find(_.str.sprintf(".notification[data-id='%s']", notificationId)).not(".notification_state_pre-hidden"); if ($notification.length) { removeNotification($notification); } }; }, Logger);