symphony/assets/js/src/symphony.notify.js
/**
* @package assets
*/
(function($, Symphony) {
'use strict';
/**
* Notify combines multiple system messages to an interface that focusses
* on a single message at a time and offers a navigation to move between message.
*
* @name $.symphonyNotify
* @class
*
* @param {Object} options An object specifying containing the attributes specified below
* @param {String} [options.items='p.notice'] Selector to find messages
* @param {String} [options.storage='symphony.notify.root'] Namespace used for local storage
*
* @example
$('#messages').symphonyNotify();
*/
$.fn.symphonyNotify = function(options) {
var objects = this,
settings = {
items: 'p.notice',
storage: 'symphony.notify.' + Symphony.Context.get('root').replace('http://', '')
};
$.extend(settings, options);
/*-----------------------------------------------------------------------*/
Symphony.Language.add({
'Ignore?': false,
'next': false,
'at': false
});
/*-------------------------------------------------------------------------
Events
-------------------------------------------------------------------------*/
// Attach message
objects.on('attach.notify', function attachMessage(event, message, type) {
var object = $(this),
notifier = object.find('div.notifier'),
items = notifier.find(settings.items),
item, storage;
notifier.trigger('attachstart.notify');
// Create item
item = $('<p />', {
'class': type
}).html(message.replace(Symphony.Language.get('at') + ' ', '')).addClass('notice active').symphonyTimeAgo();
// Add ignore link to notices)
if(!item.is('.error') && !item.is('.success') && !item.is('.protected')) {
item.html(item.html() + ' <a class="ignore">' + Symphony.Language.get('Ignore?') + '</a>');
}
// Add navigator
$('<nav />', {
text: Symphony.Language.get('next')
}).hide().appendTo(item);
// Load exclusion rules
if(Symphony.Support.localStorage === true) {
storage = (window.localStorage[settings.storage]) ? $.parseJSON(window.localStorage[settings.storage]) : [];
}
// Dimmed success transition
if (!!type && !!~type.indexOf('success')) {
setTimeout(function () {
item.addClass('dimmed');
}, 10000);
}
// Prepend item
if($.inArray(item.text(), storage) == -1) {
items.removeClass('active');
item.addClass('active').prependTo(notifier);
notifier.scrollTop(0);
notifier.trigger('attachstop.notify', [item]);
}
else {
notifier.trigger('attachcancel.notify', [item]);
}
});
// Detach message
objects.on('detach.notify', settings.items, function detachMessage() {
var item = $(this),
notifier = item.parents('div.notifier');
notifier.trigger('detachstart.notify', [item]);
// Prepare item removal
notifier.one('movestop.notify', function() {
var notifier = $(this),
offset = notifier.scrollTop();
// Adjust offset
if(offset > 0) {
notifier.scrollTop(offset - item.outerHeight());
}
// Remove item
item.remove();
notifier.trigger('detachstop.notify', [item]);
});
// Fade item
item.animate({
opacity: 0
}, 'normal', function removeItem() {
// No other items
if(item.siblings().length == 0) {
notifier.trigger('resize.notify', [$('<div />')]);
}
// More item
else {
notifier.trigger('move.notify');
}
// Remove item
item.remove();
notifier.trigger('detachstop.notify', [item]);
});
});
// Resize notifier
objects.on('resize.notify attachstop.notify', 'div.notifier', function resizeNotifer(event, item) {
var notifier = $(this);
// Adjust height
if(!notifier.hasClass('constructing')) {
var active = item || notifier.find('.active:not(:animated)');
notifier.show().animate({
height: active.innerHeight() || 0
}, 100);
}
});
// Count messages
objects.on('attachstop.notify detachstop.notify', 'div.notifier', function toggleNavigator() {
var notifier = $(this),
items = notifier.find(settings.items);
// Hide navigator
if(items.length == 1) {
items.find('nav').hide();
}
// Show navigator
else {
items.find('nav').show();
}
});
// Next message
objects.on('click', 'nav', function switchMessage() {
var notifier = $(this).closest('div.notifier');
// Move messages
notifier.trigger('move.notify');
});
// Move messages
objects.on('move.notify', 'div.notifier', function moveMessage() {
var notifier = $(this),
current = notifier.find('.active'),
next = current.next(settings.items),
from = current.outerHeight(),
offset;
notifier.trigger('movestart.notify');
// Deactivate current message
current.removeClass('active');
// Activate next message and get offset
if(next.length > 0) {
next.addClass('active');
offset = notifier.scrollTop() + from;
}
else {
next = notifier.find(settings.items).first().addClass('active');
offset = 0;
}
// If next's height is not the same, resize first
if(next.outerHeight() !== from) {
notifier.trigger('resize.notify');
}
// Move to next message
notifier.animate({
scrollTop: offset
}, 'fast', function stopMovingMessage() {
notifier.trigger('movestop.notify');
});
});
// Ignore message
objects.on('click', 'a.ignore', function ignoreMessage() {
var ignore = $(this),
item = ignore.parents(settings.items),
text = item.text(),
storage;
// Store exclusion rule
if(Symphony.Support.localStorage === true) {
// Put in a try/catch incase we exceed storage space
try {
storage = (window.localStorage[settings.storage]) ? $.parseJSON(window.localStorage[settings.storage]) : [];
storage.push(text);
window.localStorage[settings.storage] = JSON.stringify(storage);
}
catch(e) {
window.onerror(e.message);
}
}
// Remove item
item.trigger('detach.notify');
});
/*-------------------------------------------------------------------------
Initialisation
-------------------------------------------------------------------------*/
// Build interface
objects.each(function initNotify() {
var object = $(this),
notifier = $('<div class="notifier" />').hide().prependTo(object),
items = $(object.find(settings.items).get().reverse());
// Construct notifier
notifier.addClass('constructing');
notifier.height(items.last().innerHeight());
items.each(function buildMessages() {
var item = $(this).remove(),
message = item.html(),
type = item.attr('class');
object.trigger('attach.notify', [message, type]);
});
// Resize notifier
if(notifier.find(settings.items).length > 0) {
notifier.removeClass('constructing').trigger('resize.notify');
}
notifier.removeClass('constructing');
// Update relative times in system messages
setInterval(function updateRelativeTimes() {
$('header p.notice').symphonyTimeAgo();
}, 60000);
});
/*-----------------------------------------------------------------------*/
return objects;
};
})(window.jQuery, window.Symphony);