'use strict';
/**
* Provide the notification classes.
*
* @module views
* @submodule views.notifications
*/
YUI.add('juju-notifications', function(Y) {
var views = Y.namespace('juju.views'),
widgets = Y.namespace('juju.widgets'),
Templates = views.Templates;
/**
* Abstract base class used to view a ModelList of notifications.
*
* @class NotificationsBaseView
*/
var NotificationsBaseView = Y.Base.create('NotificationsBaseView',
Y.View, [views.JujuBaseView], {
initializer: function() {
NotificationsView.superclass.constructor.apply(this, arguments);
var notifications = this.get('notifications'),
env = this.get('env');
// Bind view to model list in a number of ways
notifications.addTarget(this);
// Re-render the model list changes
notifications.after('add', this.slowRender, this);
notifications.after('create', this.slowRender, this);
notifications.after('remove', this.slowRender, this);
notifications.after('reset', this.slowRender, this);
// Bind new notifications to the notifier widget.
notifications.after('add', this.addNotifier, this);
// Env connection state watcher
env.on('connectedChange', this.slowRender, this);
},
/**
* Create and display a notifier widget when a notification is added.
* The notifier is created only if:
* - the notifier box exists in the DOM;
* - the notification is a local one (not related to the delta stream);
* - the notification is an error.
*
* @method addNotifier
* @param {Object} ev An event object (with a "model" attribute).
* @return {undefined} Mutates only.
*/
addNotifier: function(ev) {
var notification = ev.model,
notifierBox = Y.one('#notifier-box');
// Show error notifications only if the DOM contain the notifier box.
if (notifierBox &&
!notification.get('isDelta') &&
notification.get('level') === 'error') {
new widgets.Notifier({
title: notification.get('title'),
message: notification.get('message')
}).render(notifierBox);
}
},
/**
* Event handler for clicking the notification icon.
*
* @method notifyToggle
*/
notifyToggle: function(evt) {
var container = this.get('container'),
notifications = this.get('notifications'),
target = evt.target.getAttribute('data-target'),
el = container.one('#' + target),
parent = el.ancestor();
if (notifications.size() === 0) {
return;
}
if (parent && parent.hasClass('open')) {
el.hide(true);
}
else {
el.show(true);
}
if (parent) {
parent.toggleClass('open');
}
el.toggleClass('active');
},
/**
* Select/click on a notice. Currently this just removes it from the
* model_list.
*
* @method notificationSelect
*/
notificationSelect: function(evt) {
var notifications = this.get('notifications'),
target = evt.target,
model;
if (!target) {
return;
}
if (target.get('tagName') !== 'LI') {
target = target.ancestor('li');
}
model = notifications.getByClientId(target.get('id'));
if (this.selection.seen) {
model.set('seen', true);
}
if (this.selection.hide) {
target.hide(true);
}
this.slowRender();
},
/**
* A flow of events can trigger many renders, from the event system
* we debounce render requests with this method.
*
* @method slowRender
*/
slowRender: function() {
var self = this,
container = self.get('container');
clearTimeout(this._renderTimeout);
this._renderTimeout = setTimeout(function() {
if (!container) {
return;
}
self.render();
}, 200);
},
render: function() {
var container = this.get('container'),
env = this.get('env'),
connected = env.get('connected'),
notifications = this.get('notifications'),
state,
open = '',
btngroup = container.one('.btn-group');
// Honor the current active state if the view is already
// rendered
if (btngroup && btngroup.hasClass('open')) {
open = 'open';
}
// However if the size of the message list is now
// zero we can close the dialog
if (notifications.size() === 0) {
open = '';
}
var showable = this.getShowable(),
show_count = showable.length || 0;
if (!connected) {
state = 'btn-warning';
}
else if (show_count > 0) {
state = 'btn-danger';
} else {
state = 'btn-info';
}
container.setHTML(this.template({
notifications: showable,
count: show_count,
state: state,
open: open,
viewAllUri: this.get('nsRouter').url({ gui: '/notifications' })
}));
return this;
}
}, {
ATTRS: {
/**
Applications router utility methods
@attribute nsRouter
*/
nsRouter: {}
}
});
/**
* The view associated with the notifications indicator.
*
* @class NotificationsView
*/
var NotificationsView = Y.Base.create('NotificationsView',
NotificationsBaseView, [], {
template: Templates.notifications,
/*
* Actions associated with events. In this case selection events
* represent policy flags inside the 'notificationSelect' callback.
*
* :hide: should the selected element be hidden on selection
*/
selection: {
hide: false,
seen: false
},
events: {
'#notify-indicator': {
click: 'notifyToggle'
},
'li.notice': {
click: 'notificationSelect'
},
'#notify-list li.header a': {
click: 'close'
}
},
/**
* @method getShowable
*/
getShowable: function() {
var notifications = this.get('notifications');
return notifications.filter(function(n) {
return n.get('level') === 'error' && n.get('seen') === false;
}).map(function(n) {
return n.getAttrs();
});
},
close: function() {
var container = this.get('container');
if (!container) {
return;
}
var indicator = container.one('#notify-indicator'),
list = container.one('#notify-list');
if (!indicator) {
return;
}
var parent = indicator.ancestor();
if (parent && parent.hasClass('open')) {
indicator.ancestor().removeClass('open');
list.hide();
indicator.removeClass('active');
}
},
render: function() {
NotificationsView.superclass.render.apply(this, arguments);
this.get('container').on('clickoutside', this.close, this);
return this;
}
});
views.NotificationsView = NotificationsView;
/**
* The 'View All Notifications' view.
*
* @class NotificationsOverview
*/
var NotificationsOverview = Y.Base.create('NotificationsOverview',
NotificationsBaseView, [], {
template: Templates.notifications_overview,
events: {
'li.notice': {
click: 'notificationSelect'
}
},
// Actions for selecting a notice
selection: {hide: false},
/**
* The overview shows all events by default when real filtering
* is present this will have to take options.
*
* @method getShowable
*/
getShowable: function() {
var notifications = this.get('notifications');
return notifications.map(function(n) {
return n.getAttrs();
});
}
});
views.NotificationsOverview = NotificationsOverview;
}, '0.1.0', {
requires: [
'view',
'juju-view-utils',
'node',
'handlebars',
'notifier'
]
});