/** * @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);