/**
* @example Notify.create('Normale Nachricht');
* @example Notify.create('Fehler Benachrichtgung', 'error', true);
*/
var Notify = function ($, PushJS) {
'use strict';
var me = {
validTypes: ['default', 'notice', 'success', 'warning', 'error', 'push'],
settings: {
storageKeyPrefix: 'notification_',
storageKeyProgressBar: 'notification_progressbar'
},
defaults: {
layout: 'topRight',
theme: 'notification',
maxVisible: 5,
timeout: 10000,
progressBar: true,
animation: {
open: {height: 'toggle'},
close: {height: 'toggle'},
easing: 'swing',
speed: 250
},
closeWith: [], // Überschreiben
template: '
'
},
init: function () {
// Notifications nicht in IFrames anzeigen
if (me.isIframe()) {
return;
}
// Eigene Default-Einstellungen in Noty-Defaults integrieren
$.noty.defaults = $.extend({}, $.noty.defaults, me.defaults);
$.noty.defaults.callback.onClose = function () {
me.closeNotificationInOtherTabs(this.options.id);
};
// Init abbrechen, wenn in Beleg-Positionen oder Positionen-Popup
var action = $('body').data('action');
if (typeof action === 'undefined' || action === 'positionen' || action === 'positioneneditpopup') {
return;
}
// Wenn Seite geladen wird > Geöffnete Benachrichtigungen aus LocalStorage wiederherstellen
me.restoreFromLocalStorage();
// Auf Änderungen im LocalStorage horchen
window.addEventListener('storage', me.storageHandler, false);
$(document).on('click', '.notification .close-icon', function () {
var notiId = $(this).parents('.noty_bar').prop('id');
me.close(notiId);
});
},
/**
* @param {string} key
*
* @return {boolean}
*/
has: function (key) {
return $.noty.get(key) !== false;
},
/**
* @param {string} key
*
* @return {object} noty-Objekt
*/
get: function (key) {
return $.noty.get(key);
},
/**
* @return {string[]}
*/
keys: function () {
return Object.keys($.noty.store);
},
/**
* @param {string|null} type [default|notice|success|warning|error|push]
* @param {string|null} title
* @param {string|null} message
* @param {boolean|null} hasPriority
* @param {object|null} options
*/
create: function (type, title, message, hasPriority, options) {
if (typeof options !== 'object' || options === null) {
options = {};
}
var data = options;
if (typeof type === 'undefined' || type === null) {
type = 'default';
}
if (me.validTypes.indexOf(type) === -1) {
type = 'default';
}
if (typeof title === 'undefined' || title === null) {
title = '';
}
if (typeof message === 'undefined' || message === null) {
message = '';
}
if (typeof hasPriority === 'undefined' || hasPriority === null) {
hasPriority = false;
}
if (title === '' && message === '') {
return;
}
if (hasPriority === true) {
me.playSound();
data.progressBar = false;
data.timeout = false; // Sticky machen
data.sticky = true;
data.force = true; // An den Anfang setzen
}
data.text = '';
if (title !== '') {
data.text += '' + title + '
';
}
if (message !== '') {
data.text += message;
}
data.type = type;
// Buttons aufbereiten
if (typeof data.buttons !== 'undefined' && typeof data.buttons === 'object') {
data.buttons.forEach(function (button) {
if (typeof button.text === 'undefined' || typeof button.link === 'undefined') {
console.warn('Could not create Notify button. Required property \'text\' oder \'link\' is missing');
return;
}
if (typeof button.addClass === 'undefined') {
button.addClass = 'btn notification-button';
} else {
button.addClass += ' btn notification-button';
}
});
}
if (data.type === 'push') {
me.createPushNotification(title, message, hasPriority);
} else {
me.createFromData(data);
}
},
/**
* Benachrichtigung erzeugen
*
* @param {object} data
*/
createFromData: function (data) {
// ID, zur Wiedererkennung über alle Tabs/Fenster, generieren und zuweisen
if (typeof data.id === 'undefined' || data.id === null) {
data.id = me.generateRandomId();
}
if (typeof data.timestamp === 'undefined' || data.timestamp === null) {
data.timestamp = Date.now();
}
if (typeof data.type === 'undefined') {
data.type = 'default';
}
switch (data.type) {
case 'default':
data.type = 'alert';
break;
case 'notice':
data.type = 'information';
break;
case 'push':
return;
}
if (me.has(data.id)) {
// Es gibt schon eine Notification mit dieser ID > Notification aktualisieren
me.updateNotificationInOwnTab(data.id, data);
me.updateNotificationInOtherTabs(data.id, data);
} else {
// Neue Notification anlegen
me.createNotificationInOwnTab(data.id, data);
me.createNotificationInOtherTabs(data.id, data);
}
},
/**
* Benachrichtigung schließen
*
* @param {string} key
*/
close: function (key) {
me.closeNotificationInOwnTab(key);
me.closeNotificationInOtherTabs(key);
},
/**
* Alle Benachrichtigungen schließen
*/
closeAll: function () {
me.closeAllNotificationsInOwnTab();
me.closeAllNotificationsInOtherTabs();
},
/* ------\/------ Private Methoden ------\/------ */
/**
* Geöffnete Benachrichtigungen wiederherstellen
*/
restoreFromLocalStorage: function () {
var restored = me.collectFromLocalStorage();
// Zeitliche Reihenfolge wiederherstellen
restored.sort(function (a, b) {
return a.timestamp - b.timestamp;
});
// Benachrichtigungen erzeugen
restored.forEach(function (data) {
me.createNotificationInOwnTab(data.id, data);
});
},
/**
* Benachrichtigungen aus LocalStorage holen
*
* @return {Array}
*/
collectFromLocalStorage: function () {
var notifications = [];
for (var key in localStorage) {
if (key === me.settings.storageKeyProgressBar) {
continue;
}
if (key.substr(0, 13) !== me.settings.storageKeyPrefix) {
continue;
}
if (localStorage.hasOwnProperty(key)) {
var store = localStorage.getItem(key);
var data = JSON.parse(store);
// Push-Benachrichtigungen nicht wiederherstellen
if (typeof data.type !== 'undefined' && data.type === 'push') {
continue;
}
notifications.push(data);
}
}
return notifications;
},
/**
* Ton abspielen
*/
playSound: function () {
try {
var bell = new Audio('./sound/pling.mp3');
bell.play();
} catch (e) {
// Sound abspielen funktioniert auf neueren Chromes nicht mehr:
// https://developers.google.com/web/updates/2017/09/autoplay-policy-changes
}
},
/**
* Benachrichtigung im eigenen Fenster/Tab erstellen
*
* @param {string} key
* @param {object} data
*/
createNotificationInOwnTab: function (key, data) {
if (typeof key === 'string' && typeof data === 'object') {
var item = noty(data);
if (data.sticky === true) {
item.$bar.addClass('sticky');
}
// Events für Fortschrittsbalken
if (item.$progressBar && item.options.progressBar) {
item.$bar.on('mouseenter', function () {
me.resetProgressBarInOtherTabs(item.options.id);
});
item.$bar.on('mouseleave', function () {
me.startProgressBarInOtherTabs(item.options.id);
});
}
// Buttons wiederherstellen
if (typeof item.options.buttons !== 'undefined' && typeof item.options.buttons === 'object') {
item.options.buttons.forEach(function (button) {
// Data-Attribute wiederherstellen
var $button = $('#' + button.id);
$.each(button, function (property, value) {
if (property.substr(0, 5) !== 'data-') {
return;
}
var dataName = property.substr(5);
$button.data(dataName, value);
});
// onClick-Methode wiederherstellen
button.onClick = function ($noty) {
var event = jQuery.Event('notification-button:clicked');
$(document).trigger(event, button);
if (!event.isDefaultPrevented()) {
$noty.close();
window.location.href = button.link;
}
};
});
}
// Custom Event feuern
$(document).trigger('notification:created', data);
}
},
/**
* Benachrichtigung in allen anderen Fenstern/Tabs erstellen
*
* @param {string} key
* @param {object} data
*/
createNotificationInOtherTabs: function (key, data) {
if (typeof key === 'string' && typeof data === 'object') {
localStorage.setItem(key, JSON.stringify(data));
}
},
/**
* Browser-Benachrichtigung erzeugen
*
* @param {string} title
* @param {string|null} message
* @param {boolean} hasPriority
*/
createPushNotification: function (title, message, hasPriority) {
if (typeof PushJS === 'undefined') {
throw 'push.js wurde nicht gefunden!';
}
if (typeof title === 'undefined' || title === null) {
message = '';
}
if (typeof message === 'undefined' || message === null) {
message = '';
}
if (typeof hasPriority === 'undefined') {
hasPriority = false;
}
if (title === '' && message === '') {
return;
}
var data = {
icon: './js/pushjs/icon.png',
onClick: function () {
window.focus();
this.close();
}
};
if (message !== '') {
data.body = message;
}
if (hasPriority === false) {
data.tag = 'default';
}
// Nicht-Prio-Nachrichten löschen
// (Prio-Nachrichten werden gestacked)
PushJS.close('default');
// Push-Nachricht erzeugen
PushJS.create(title, data);
},
/**
* Vorhandene Benachrichtigung aktualisieren
*
* @param {string} notifyId ID der Notification
* @param {object} notifyData
*/
updateNotificationInOwnTab: function (notifyId, notifyData) {
me.updateNotificationType(notifyId, notifyData.type);
me.updateNotificationText(notifyId, notifyData.text);
// @todo me.updateNotificationButtons(notifyId, notifyData.buttons);
},
/**
* Vorhandene Benachrichtigung aktualisieren
*
* @param {string} key ID der Notification
* @param {object} data
*/
updateNotificationInOtherTabs: function (key, data) {
if (typeof key === 'string' && typeof data === 'object') {
localStorage.setItem(key, JSON.stringify(data));
}
},
/**
* Benachrichtigung im eigenen Fenster/Tab schließen
*
* @param {string} key
*/
closeNotificationInOwnTab: function (key) {
if (typeof key === 'undefined') {
return;
}
$.noty.close(key);
},
/**
* Benachrichtigung in allen anderen Fenstern/Tabs schließen
*
* @param {string} key
*/
closeNotificationInOtherTabs: function (key) {
localStorage.removeItem(key);
},
/**
* Alle Benachrichtigungen im eigenen Fenster/Tab schließen
*/
closeAllNotificationsInOwnTab: function () {
$.noty.closeAll();
},
/**
* Alle Benachrichtigungen in allen anderen Fenstern/Tabs schließen
*/
closeAllNotificationsInOtherTabs: function () {
for (var key in localStorage) {
if (key.substr(0, 13) !== me.settings.storageKeyPrefix) {
continue;
}
if (localStorage.hasOwnProperty(key)) {
me.closeNotificationInOtherTabs(key);
}
}
},
/**
* Text einer vorhandenen Benachrichtigung aktualisieren
*
* @param {string} notifyId
* @param {string} text
*/
updateNotificationText: function(notifyId, text) {
var existing = me.get(notifyId);
if (existing === false) {
return;
}
existing.$message.find('.noty_text').html(text);
},
/**
* Typ einer vorhandenen Benachrichtigung aktualisieren
*
* @param {string} notifyId
* @param {string} type
*/
updateNotificationType: function(notifyId, type) {
var existing = me.get(notifyId);
if (existing === false) {
return;
}
var newOuterClassName = 'noty_container_type_' + type;
var $outer = existing.$bar;
if (!$outer.hasClass(newOuterClassName)) {
var classList = $outer.attr('class').split(/\s+/);
$.each(classList, function(index, className) {
if (className.substring(0, 20) === 'noty_container_type_') {
$outer.removeClass(className);
}
});
$outer.addClass(newOuterClassName);
}
var newInnerClassName = 'noty_type_' + type;
var $inner = existing.$bar.find('.noty_bar');
if (!$inner.hasClass(newInnerClassName)) {
var innerClasses = $inner.attr('class').split(/\s+/);
$.each(innerClasses, function (index, className) {
if (className.substring(0, 10) === 'noty_type_') {
$inner.removeClass(className);
}
});
$inner.addClass(newInnerClassName);
}
},
/**
* Fortschrittsbalken im eigenen Tab/Fenster zurücksetzen
*
* @param {string} notifyId
*/
resetProgressBarInOwnTab: function (notifyId) {
var $noty = $.noty.get(notifyId);
if (typeof $noty !== 'object') {
return;
}
// Nicht alle Benachrichtigungen haben einen Fortschrittsbalken
if ($noty.options.progressBar && $noty.$progressBar) {
$noty.dequeueClose();
}
},
/**
* Fortschrittsbalken im eigenen Tab/Fenster wieder starten
*
* @param {string} notifyId
*/
startProgressBarInOwnTab: function (notifyId) {
var $noty = $.noty.get(notifyId);
if (typeof $noty !== 'object') {
return;
}
// Nicht alle Benachrichtigungen haben einen Fortschrittsbalken
if ($noty.options.progressBar && $noty.$progressBar) {
$noty.queueClose($noty.options.timeout);
}
},
/**
* Fortschrittsbalken in anderen Tabs/Fenstern zurücksetzen
*
* @param {string} notifyId
*/
resetProgressBarInOtherTabs: function (notifyId) {
var data = {
id: notifyId,
date: Date.now(),
action: 'reset'
};
localStorage.setItem(me.settings.storageKeyProgressBar, JSON.stringify(data));
},
/**
* Fortschrittsbalken in anderen Tabs/Fenstern wieder starten
*
* @param {string} notifyId
*/
startProgressBarInOtherTabs: function (notifyId) {
var data = {
id: notifyId,
date: Date.now(),
action: 'start'
};
localStorage.setItem(me.settings.storageKeyProgressBar, JSON.stringify(data));
},
/**
* Horcht auf Änderungen im LocalStorage
*
* @param {StorageEvent} e
*/
storageHandler: function (e) {
// LocalStorage wurde komplett geleert
if (typeof e === 'undefined') {
return;
}
// Fortschrittsbalken zurücksetzen/neustarten
if (e.key === me.settings.storageKeyProgressBar) {
if (e.newValue === null) {
return;
}
var progressData = JSON.parse(e.newValue);
if (progressData.action === 'reset') {
me.resetProgressBarInOwnTab(progressData.id);
}
if (progressData.action === 'start') {
me.startProgressBarInOwnTab(progressData.id);
}
localStorage.removeItem(me.settings.storageKeyProgressBar);
return;
}
// Nur auf bestimmten Key horchen
if (e.key.substr(0, 13) !== me.settings.storageKeyPrefix) {
return;
}
// LocalStorage(-Key) wurde gelöscht > Notification schließen
if (e.newValue === null) {
me.close(e.key);
return;
}
var received = JSON.parse(e.newValue);
if (received === null) {
return;
}
// Daten wurden empfangen
if (typeof received === 'object' && typeof received.id === 'string') {
if (me.has(received.id)) {
// Vorhandene Notification aktualisieren
me.updateNotificationInOwnTab(received.id, received);
} else {
// Notification erstellen
me.createNotificationInOwnTab(received.id, received);
}
}
},
/**
* Zufällige ID generieren
*
* @return {string}
*/
generateRandomId: function () {
return me.settings.storageKeyPrefix + Math.floor(Math.random() * Math.floor(9999999999));
},
/**
* @return {boolean}
*/
isIframe: function () {
return window.location !== window.parent.location;
}
};
return {
has: me.has,
get: me.get,
keys: me.keys,
init: me.init,
create: me.create,
createFromData: me.createFromData,
close: me.close,
closeAll: me.closeAll
};
}(jQuery, Push);
$(document).ready(Notify.init);