ui/sh-notifications.js

'use strict';

var _ = require('lodash'),
    StorageUtils = require('storage-utils').default,
    SHGlobals = require('sh-globals'),
    NotificationView = require('notification-view'),
    $body = $('body'),
    $window = $(window),
    $container = $('<div></div>'),
    index = 0,
    isWatchingScroll = false,
    containerTopPos = 0,

    notificationRemoved = function notificationRemoved() {
        if (!SHGlobals.isMobile && !$container.children().length && isWatchingScroll) {
            $window.off('scroll', onDesktopScroll);

            isWatchingScroll = false;
        }
    },

    onDesktopScroll = function onDesktopScroll(evt) {
        var windowTop = $window.scrollTop(),
            containerTop = (windowTop > containerTopPos) ? windowTop : containerTopPos;

        $container.css({top: containerTop});
    };

/**
 * Adds the ability to dispatch notifications from anywhere within the application. Wrap the objects to be dispatched in an array to show multiple notifications at once.
 * @module SHNotifications
 * @example
 * SHNotifications.add(
 *      {
 *          type: 'success'
 *          message: 'Success message here.'
 *      }
 * );
 */
module.exports = {
    /**
     * Adds a notification to the queue.
     * @static
     * @function add
     * @param {Object} data - The data to pass to the notification.
     * @param {String} data.message - The message to display inside the notification.
     * @param {String} [data.type] - The type of notification. ('success', 'error', 'warning')
     * @param {Integer} [data.duration=3000] - The time (in ms) to show the notification for.
     * @param {Boolean} [data.showClose=true] - Whether to show the close button.
     * @param {Boolean} [data.autoClose=true] - Whether to automatically close the notification after duration.
     * @param {Object} [data.cookie] - A cookie that, if present, only shows the notification once.
     * @param {String} data.cookie.name - The name of the cookie.
     * @param {Integer|Date} [data.cookie.expires] - The number of days (Integer) or the date (Date) that determines the lifetime of the cookie.
     * @param {Array.<Object>} [data.buttons] - Adds buttons to the notification.
     * @param {String} data.buttons.displayClass - The class to add to the button. ('Button' or 'Button-link')
     * @param {String} data.buttons.clickClass - A unique class to this notification which can differentiate this button from others.
     * @param {String} data.buttons.label - The button label.
     * @param {Function} data.buttons.callback - The method to call when the button is clicked.
     * @param {Array} [data.buttons.callbackParams] - Parameters to pass to the callback method.
     * @example
     * SHNotifications.add(
     *      {
     *          type: 'success'
     *          message: 'Success message here.'
     *          showClose: false,
     *          autoClose: false,
     *          duration: 2000,
     *          cookie: {
     *              name: 'nameOfCookie',
     *              expires: 1
     *          },
     *          buttons: [
     *              {
     *                  displayClass: 'Button',
     *                  clickClass: 'btn-label-1',
     *                  label: 'Label 1',
     *                  callback: cb,
     *                  callbackParams: [true, 'hello']
     *              },
     *              {
     *                  displayClass: 'Button-link',
     *                  clickClass: 'btn-label-2',
     *                  label: 'Label 2',
     *                  callback: cb2,
     *                  callbackParams: [this]
     *              }
     *          ]
     *      }
     * );
     * @returns {void}
     */
    add: function(data) {
        if (data.cookie) {
            if (StorageUtils.get(data.cookie.name, 'cookie')) {
                return;
            } else {
                var cookieExpires = null;

                if (!_.isUndefined(data.cookie.expires)) {
                    cookieExpires = data.cookie.expires;
                }

                StorageUtils.set(data.cookie.name, '1', 'cookie', cookieExpires);
            }
        }

        if (!$body.children('.notification-container').length) {
            var $nav = $('.navbar'),
                isSecondary = $nav.hasClass('navbar-secondary'),
                containerCSS = {
                    top: isSecondary ? $nav.outerHeight() : 0,
                    zIndex: isSecondary ? 13 : 18
                };

            containerTopPos = containerCSS.top;

            $body.append($container);
            $container
                .addClass('notification-container')
                .css(containerCSS);
        }

        this._showNotifications(data);
    },

    _showNotifications: function(data) {
        var self = this;

        if (_.isArray(data)) {
            _.each(data, function(notification, i) {
                setTimeout(function() {
                    self._addNotification(notification, i);
                }, (i * 500));
            });
        } else {
            this._addNotification(data, index++);
        }
    },

    _addNotification: function(data, zIndex) {
        data.zIndex = zIndex;

        var notificationView = new NotificationView({model: data});
        notificationView.notificationRemoved.addOnce(notificationRemoved);
        $container.prepend(notificationView.render().el);

        if (!SHGlobals.isMobile && !isWatchingScroll) {
            $window.on('scroll', onDesktopScroll);
            onDesktopScroll();

            isWatchingScroll = true;
        }
    }
};