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