annotate src/DML/MainVisBundle/Resources/assets/marionette/modules/NotificationsModule.js @ 0:493bcb69166c

added public content
author Daniel Wolff
date Tue, 09 Feb 2016 20:54:02 +0100
parents
children
rev   line source
Daniel@0 1 "use strict";
Daniel@0 2 /**
Daniel@0 3 * Notification options:
Daniel@0 4 *
Daniel@0 5 * id
Daniel@0 6 * null or string
Daniel@0 7 * if the same as before, the old notification with the same id is replaced with the current one
Daniel@0 8 *
Daniel@0 9 * modifiers
Daniel@0 10 * [string1, string2]
Daniel@0 11 * bem modifiers (define look and feel)
Daniel@0 12 * availabilities:
Daniel@0 13 * no-padding
Daniel@0 14 * type_warning
Daniel@0 15 * type_error
Daniel@0 16 * ttl_ever
Daniel@0 17 * ttl_10
Daniel@0 18 * ttl_11
Daniel@0 19 * ttl_20
Daniel@0 20 * ...
Daniel@0 21 *
Daniel@0 22 * content
Daniel@0 23 * text / jquery object
Daniel@0 24 * what to show in the notification
Daniel@0 25 *
Daniel@0 26 * onClose
Daniel@0 27 * function(options)
Daniel@0 28 * what to do when the notification is manually closed
Daniel@0 29 *
Daniel@0 30 * onExpire
Daniel@0 31 * function(options)
Daniel@0 32 * what to do when the notification is removed automatically
Daniel@0 33 *
Daniel@0 34 * keepContentInMemoryAfterRemoval
Daniel@0 35 * does not destroy the content on notification removal (it retains DOM nodes and event listeners, which can be reused later)
Daniel@0 36 *
Daniel@0 37 */
Daniel@0 38 App.module("NotificationsModule", function(NotificationsModule, App, Backbone, Marionette, $, _, Logger) {
Daniel@0 39 // Prevent auto start
Daniel@0 40 NotificationsModule.startWithParent = false;
Daniel@0 41
Daniel@0 42 // Define options
Daniel@0 43 var defaultModuleOptions = {
Daniel@0 44 defaultTTL: 10, // time to live
Daniel@0 45 minTTLOnUnfreeze: 6 // time to live after the notification is unfrozen (mouse went out)
Daniel@0 46 };
Daniel@0 47 var moduleOptions;
Daniel@0 48
Daniel@0 49 // Define private variables
Daniel@0 50 var logger = null;
Daniel@0 51 var $notifications = null;
Daniel@0 52 var $notificationStack = null;
Daniel@0 53
Daniel@0 54 // Define private methods
Daniel@0 55 var addNewNotification = null;
Daniel@0 56 var removeNotification = null;
Daniel@0 57 var updateNotification = null;
Daniel@0 58
Daniel@0 59 var notificationTimeoutHandler = null;
Daniel@0 60
Daniel@0 61 /*
Daniel@0 62 * Structure of "$notification" object
Daniel@0 63 * .notification_body
Daniel@0 64 * .notification_content
Daniel@0 65 * .notification_closer
Daniel@0 66 * data
Daniel@0 67 * options
Daniel@0 68 * ...
Daniel@0 69 */
Daniel@0 70
Daniel@0 71 /**
Daniel@0 72 * Initialization checker
Daniel@0 73 */
Daniel@0 74 var assertModuleIsInitialized = function() {
Daniel@0 75 if (!$notifications) {
Daniel@0 76 throw "NotificationsModule has not been initialized";
Daniel@0 77 }
Daniel@0 78 };
Daniel@0 79
Daniel@0 80 /**
Daniel@0 81 * Module initializer
Daniel@0 82 *
Daniel@0 83 */
Daniel@0 84 NotificationsModule.addInitializer(function(options){
Daniel@0 85 moduleOptions = _.extend(defaultModuleOptions, options);
Daniel@0 86
Daniel@0 87 logger = Logger.get("NotificationsModule");
Daniel@0 88 //logger.setLevel(Logger.DEBUG);
Daniel@0 89
Daniel@0 90 logger.debug("Begin init");
Daniel@0 91
Daniel@0 92 $notifications = $(".app__notifications")
Daniel@0 93 .addClass("notifications");
Daniel@0 94
Daniel@0 95 $notificationStack = $.bem.generateElement("notifications", "stack")
Daniel@0 96 .appendTo($notifications);
Daniel@0 97
Daniel@0 98 // Sets up the internals of the notification
Daniel@0 99 var setupNotification = function($notification, notificationOptions) {
Daniel@0 100 // Content
Daniel@0 101 var $notificationContent = $notification.children().eq(0).children().eq(0);
Daniel@0 102 if (!$notificationContent.hasClass("notification__content")) {
Daniel@0 103 $notificationContent = $notificationContent.children().eq(0);
Daniel@0 104 }
Daniel@0 105 $notificationContent.children().detach();
Daniel@0 106 if (notificationOptions.content instanceof $) {
Daniel@0 107 $notificationContent.append(notificationOptions.content);
Daniel@0 108 } else {
Daniel@0 109 $notificationContent.html(notificationOptions.content);
Daniel@0 110 }
Daniel@0 111
Daniel@0 112 $notification.attr("data-id", notificationOptions.id);
Daniel@0 113 $notification.data("options", notificationOptions);
Daniel@0 114 $notification.data("$content", $notificationContent);
Daniel@0 115
Daniel@0 116 // Update css modifiers
Daniel@0 117 var modifiersAsArray = null;
Daniel@0 118 if (_.isString(notificationOptions.modifiers)) {
Daniel@0 119 modifiersAsArray = notificationOptions.modifiers.split(" ");
Daniel@0 120 }
Daniel@0 121 if (_.isArray(notificationOptions.modifiers)){
Daniel@0 122 modifiersAsArray = notificationOptions.modifiers;
Daniel@0 123 }
Daniel@0 124 var state = $notification.getMod("notification", "state");
Daniel@0 125 $notification.attr("class", "notification notification_state_" + state);
Daniel@0 126 if (_.isArray(modifiersAsArray) && modifiersAsArray.length) {
Daniel@0 127 $notification.addClass("notification_" + modifiersAsArray.join(" notification_"));
Daniel@0 128 }
Daniel@0 129
Daniel@0 130 // Set ttl (time to live) if needed
Daniel@0 131 if (!$notification.getMod("notification", "ttl")) {
Daniel@0 132 $notification.setMod("notification", "ttl", moduleOptions.defaultTTL);
Daniel@0 133 }
Daniel@0 134 };
Daniel@0 135
Daniel@0 136 var freezeTTL = function () {
Daniel@0 137 logger.debug("Freeze ttl", this);
Daniel@0 138 $(this).addClass("notification_ttl-frozen");
Daniel@0 139 };
Daniel@0 140 var unfreezeTTL = function () {
Daniel@0 141 logger.debug("Unfreeze ttl", this);
Daniel@0 142 var $this = $(this);
Daniel@0 143 var ttl = $this.getMod("notification", "ttl");
Daniel@0 144 if (ttl * 1 == ttl && ttl < moduleOptions.minTTLOnUnfreeze) {
Daniel@0 145 $this.setMod("notification", "ttl", moduleOptions.minTTLOnUnfreeze);
Daniel@0 146 }
Daniel@0 147 $this.removeClass("notification_ttl-frozen");
Daniel@0 148 };
Daniel@0 149 var stopTTL = function () {
Daniel@0 150 logger.debug("Stop ttl", this);
Daniel@0 151 $(this).setMod("notification", "ttl", "ever");
Daniel@0 152 };
Daniel@0 153
Daniel@0 154 // A function that adds a new notification
Daniel@0 155 addNewNotification = function(notificationOptions) {
Daniel@0 156 logger.info("Adding new notificaton", notificationOptions);
Daniel@0 157
Daniel@0 158 var $notificationContent = $.bem.generateElement("notification", "content");
Daniel@0 159 var $notificationCloser = $.bem.generateElement("notification", "closer");
Daniel@0 160 var $notificationBody = $.bem.generateElement("notification", "body")
Daniel@0 161 .append($notificationContent, $notificationCloser);
Daniel@0 162
Daniel@0 163 var $notification = $.bem.generateBlock("notification")
Daniel@0 164 .append($notificationBody);
Daniel@0 165
Daniel@0 166 var closeFunction = function() {
Daniel@0 167 removeNotification($notification);
Daniel@0 168 var onClose = notificationOptions.onClose;
Daniel@0 169 if (_.isFunction(onClose)) {
Daniel@0 170 try {
Daniel@0 171 onClose($notification);
Daniel@0 172 } catch (e) {
Daniel@0 173 logger.error("Error when handling onClose", e, $notification, onClose);
Daniel@0 174 }
Daniel@0 175 }
Daniel@0 176 };
Daniel@0 177 $notificationCloser.click(closeFunction);
Daniel@0 178 $notification.mouseover(freezeTTL);
Daniel@0 179 $notification.mouseout(unfreezeTTL);
Daniel@0 180 $notification.click(stopTTL);
Daniel@0 181
Daniel@0 182 setupNotification($notification, notificationOptions);
Daniel@0 183 $notification.setMod("notification", "state", "pre-shown");
Daniel@0 184 $notification.appendTo($notificationStack);
Daniel@0 185
Daniel@0 186 var notificationBodyHeight = $notificationBody.outerHeight();
Daniel@0 187 $notification.height(notificationBodyHeight);
Daniel@0 188 $notification.hide();
Daniel@0 189 $notificationBody.hide();
Daniel@0 190
Daniel@0 191 // First show the container, and then slide the body
Daniel@0 192 $notification.show("blind", {
Daniel@0 193 direction: "up",
Daniel@0 194 //easing: "easeInOutQuart"
Daniel@0 195
Daniel@0 196 }, $notificationStack.children().length == 1 ? 1 : 100 + notificationBodyHeight / 10 * 30, function() {
Daniel@0 197 $notificationBody.show("slide", {
Daniel@0 198 direction: "right",
Daniel@0 199 easing: "easeOutQuart"
Daniel@0 200 }, 500, function() {
Daniel@0 201 $notification.setMod("notification", "state", "shown");
Daniel@0 202 });
Daniel@0 203 });
Daniel@0 204 };
Daniel@0 205
Daniel@0 206 // A function that replaces the contents of the given notification
Daniel@0 207 updateNotification = function($notification, notificationOptions) {
Daniel@0 208 logger.info("Updating notification", $notification, "with new options", notificationOptions);
Daniel@0 209
Daniel@0 210 if ($notification.length !== 1) {
Daniel@0 211 logger.error(_.str.sprintf("Can’t replace a notification, this can be done only when $notification contains a single instance.", $notification));
Daniel@0 212 return;
Daniel@0 213 }
Daniel@0 214
Daniel@0 215 // Set up the notification again
Daniel@0 216 setupNotification($notification, notificationOptions);
Daniel@0 217
Daniel@0 218 // Pay user's attention to this notification
Daniel@0 219 if (!$notification.is(":animated") && $notification.hasClass("notification_state_shown")) {
Daniel@0 220 $notification.effect("shake", {
Daniel@0 221 direction: "up",
Daniel@0 222 distance: 5,
Daniel@0 223 times: 2
Daniel@0 224 });
Daniel@0 225 }
Daniel@0 226 };
Daniel@0 227
Daniel@0 228 // Removes the notification (hides it and removes)
Daniel@0 229 removeNotification = function($notification) {
Daniel@0 230 logger.debug("Begin remove notification", $notification);
Daniel@0 231 if ($notification.hasMod("notification", "state_pre-hidden")) {
Daniel@0 232 logger.debug("Notification", $notification, "is already being hidden by another handler");
Daniel@0 233 return;
Daniel@0 234 };
Daniel@0 235 $notification.setMod("notification", "state", "pre-hidden");
Daniel@0 236
Daniel@0 237 // First fade the body and then blind the container
Daniel@0 238 var $notificationBody = $notification.children().eq(0);
Daniel@0 239 var notificationBodyHeight = $notificationBody.outerHeight();
Daniel@0 240 $notification.children().eq(0).fadeOut(
Daniel@0 241 200,
Daniel@0 242 function() {
Daniel@0 243 $notification.hide("blind", {
Daniel@0 244 direction: "up",
Daniel@0 245 }, 100 + notificationBodyHeight / 10 * 30, function() {
Daniel@0 246 if ($notification.data("options").keepContentInMemoryAfterRemoval) {
Daniel@0 247 $notification.data("$content").detach();
Daniel@0 248 }
Daniel@0 249 $notification.remove();
Daniel@0 250 });
Daniel@0 251 });
Daniel@0 252
Daniel@0 253 };
Daniel@0 254
Daniel@0 255 // Every second decrease ttl (time to live) for each notification
Daniel@0 256 notificationTimeoutHandler = setInterval(function() {
Daniel@0 257 logger.debug("Notification lifetime interval triggered");
Daniel@0 258 $notificationStack
Daniel@0 259 .children()
Daniel@0 260 .filter(".notification_state_shown")
Daniel@0 261 .not(".notification_ttl-frozen") // this class is set to notification when it is hovered over with a mouse
Daniel@0 262 .each(function() {
Daniel@0 263 var $thisNotification = $(this);
Daniel@0 264 var ttl = $thisNotification.getMod("notification", "ttl");
Daniel@0 265 if (ttl * 1 != ttl) {
Daniel@0 266 return;
Daniel@0 267 }
Daniel@0 268 ttl = ttl - 1;
Daniel@0 269 if (ttl < 0) {
Daniel@0 270 removeNotification($thisNotification);
Daniel@0 271 var onExpire = $thisNotification.data("options").onExpire;
Daniel@0 272 if (_.isFunction(onExpire)) {
Daniel@0 273 try {
Daniel@0 274 onExpire($thisNotification);
Daniel@0 275 } catch (e) {
Daniel@0 276 logger.error("Error when handling onExpire", e, $thisNotification, onExpire);
Daniel@0 277 }
Daniel@0 278 }
Daniel@0 279
Daniel@0 280 } else {
Daniel@0 281 $thisNotification.setMod("notification", "ttl", ttl);
Daniel@0 282 }
Daniel@0 283 });
Daniel@0 284 }, 1000);
Daniel@0 285
Daniel@0 286 logger.debug("End init");
Daniel@0 287 });
Daniel@0 288
Daniel@0 289 /**
Daniel@0 290 * Show a notification
Daniel@0 291 */
Daniel@0 292 NotificationsModule.show = function(notificationOptions) {
Daniel@0 293 assertModuleIsInitialized();
Daniel@0 294 logger.debug("Begin showing a new notification", notificationOptions);
Daniel@0 295
Daniel@0 296 var extendedNotificationOptions = _.extend({
Daniel@0 297 id: null,
Daniel@0 298 modifiers: null,
Daniel@0 299 content: "",
Daniel@0 300 onClose: null,
Daniel@0 301 onExpire: null,
Daniel@0 302 keepContentInMemoryAfterRemoval: false
Daniel@0 303 }, notificationOptions);
Daniel@0 304
Daniel@0 305 // Attempt to find and update notification with the same id (if it has been defined)
Daniel@0 306 // Or to create a new notification
Daniel@0 307 if (extendedNotificationOptions.id) {
Daniel@0 308 var $notificationWithTheSameId = $notificationStack.find(_.str.sprintf(".notification[data-id='%s']", extendedNotificationOptions.id)).not(".notification_state_pre-hidden");
Daniel@0 309 logger.debug("Detection of a notification witht the same id", extendedNotificationOptions.id, $notificationWithTheSameId, $notificationStack.children());
Daniel@0 310 if ($notificationWithTheSameId.length && ($notificationWithTheSameId.hasMod("notification", "state_shown") || $notificationWithTheSameId.hasMod("notification", "state_pre-shown"))) {
Daniel@0 311 if ($notificationWithTheSameId.offset().top < 0) {
Daniel@0 312 removeNotification($notificationWithTheSameId);
Daniel@0 313 addNewNotification(extendedNotificationOptions);
Daniel@0 314 } else {
Daniel@0 315 updateNotification($notificationWithTheSameId, extendedNotificationOptions);
Daniel@0 316 }
Daniel@0 317 } else {
Daniel@0 318 addNewNotification(extendedNotificationOptions);
Daniel@0 319 }
Daniel@0 320 } else {
Daniel@0 321 addNewNotification(extendedNotificationOptions);
Daniel@0 322 }
Daniel@0 323 logger.debug("End showing a new notification", notificationOptions);
Daniel@0 324 };
Daniel@0 325
Daniel@0 326 /**
Daniel@0 327 * Hide a notification
Daniel@0 328 */
Daniel@0 329 NotificationsModule.hide = function(notificationId) {
Daniel@0 330 var $notification = $notificationStack.find(_.str.sprintf(".notification[data-id='%s']", notificationId)).not(".notification_state_pre-hidden");
Daniel@0 331 if ($notification.length) {
Daniel@0 332 removeNotification($notification);
Daniel@0 333 }
Daniel@0 334 };
Daniel@0 335
Daniel@0 336 }, Logger);