815 lines
23 KiB
JavaScript
815 lines
23 KiB
JavaScript
|
/**
|
||
|
* Notifications for ProcessWire
|
||
|
*
|
||
|
* By Avoine and Ryan Cramer
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
var Notifications = {
|
||
|
|
||
|
options: { // options that may be passed to init(), these are the defaults:
|
||
|
ajaxURL: './', // URL to poll for ajax updates
|
||
|
version: 1, // notifications version
|
||
|
reverse: 0,
|
||
|
updateLast: 0,
|
||
|
updateDelay: 5000,
|
||
|
updateDelayFast: 1550,
|
||
|
iconMessage: 'smile-o',
|
||
|
iconWarning: 'meh-o',
|
||
|
iconError: 'frown-o',
|
||
|
iconRemove: 'times-circle',
|
||
|
classCommon: 'NoticeItem',
|
||
|
classMessage: 'NoticeMessage',
|
||
|
classWarning: 'NoticeWarning',
|
||
|
classError: 'NoticeError',
|
||
|
classDebug: 'NoticeDebug',
|
||
|
classContainer: 'container',
|
||
|
ghostDelay: 2000,
|
||
|
ghostDelayError: 4000,
|
||
|
ghostFadeSpeed: 'fast',
|
||
|
ghostOpacity: 0.9,
|
||
|
ghostLimit: 20, // max ghosts that that may be shown together
|
||
|
processKey: '', // key for session processes (NPK.ClassName.pageID.userID.windowName)
|
||
|
i18n: {
|
||
|
sec: 'sec',
|
||
|
secs: 'secs',
|
||
|
min: 'min',
|
||
|
mins: 'mins',
|
||
|
hour: 'hour',
|
||
|
hours: 'hours',
|
||
|
day: 'day',
|
||
|
days: 'days',
|
||
|
expires: 'expires',
|
||
|
now: 'now',
|
||
|
fromNow: 'from now',
|
||
|
ago: 'ago'
|
||
|
}
|
||
|
},
|
||
|
|
||
|
updateTimeout: null, // setTimeout timer for update method
|
||
|
renderTimeout: null, // setTimeout timer for render method
|
||
|
timerTimeout: null, // setTimeout timer for updateTime()
|
||
|
updating: false, // are we currently updating right now?
|
||
|
runtime: [], // notifications added by manual API add calls
|
||
|
numRender: 0, // number of times render() has been called
|
||
|
numEmptyRequests: 0, // number of empty ajax requests in a row, that had no new data
|
||
|
ghostsActive: 0, // number of ghosts currently visible
|
||
|
currentDelay: 0, // current updateDelay value
|
||
|
turbo: false, // whether there is a lot of activity (like with progress bars)
|
||
|
timeNow: 0, // current unix timestamp
|
||
|
useSession: false, // supports use of sessionStorage?
|
||
|
|
||
|
$menu: null, // <div class='NotificationMenu'>
|
||
|
$bug: null, // <div class='NotificationBug'>
|
||
|
$list: null, // <ul class='NotificationList'>
|
||
|
|
||
|
/**
|
||
|
* Given number of seconds relative to now, return relative time string
|
||
|
*
|
||
|
* @param secs
|
||
|
* @returns {string}
|
||
|
*
|
||
|
*/
|
||
|
relativeTime: function(secs) {
|
||
|
var str = '';
|
||
|
if(secs == 0) return str;
|
||
|
var i18n = Notifications.options.i18n;
|
||
|
var past = secs > 0;
|
||
|
secs = Math.abs(secs);
|
||
|
if(secs > 1 && secs < 60) {
|
||
|
str = secs + ' ' + (secs == 1 ? i18n.sec : i18n.secs);
|
||
|
} else if(secs >= 60 && secs < 3600) {
|
||
|
str = Math.floor(secs / 60);
|
||
|
str += ' ' + (str == 1 ? i18n.min : i18n.mins);
|
||
|
} else if(secs >= 3600 && secs < 86400) {
|
||
|
str = Math.floor(secs / 3600);
|
||
|
str += ' ' + (str == 1 ? i18n.hour : i18n.hours);
|
||
|
} else if(secs >= 86400) {
|
||
|
str = Math.floor(secs / 86400);
|
||
|
str += ' ' + (str == 1 ? i18n.day : i18n.days);
|
||
|
}
|
||
|
str += ' ';
|
||
|
if(past) {
|
||
|
if(secs < 3) str = i18n.now;
|
||
|
// else str += i18n.ago;
|
||
|
} else {
|
||
|
if(secs < 3) str = i18n.now;
|
||
|
// else str += i18n.fromNow;
|
||
|
}
|
||
|
return str;
|
||
|
},
|
||
|
|
||
|
setTurboMode: function(turbo) {
|
||
|
if(turbo) {
|
||
|
// console.log('setTurboMode: true');
|
||
|
if(Notifications.currentDelay != Notifications.options.updateDelayFast) {
|
||
|
Notifications.currentDelay = Notifications.options.updateDelayFast;
|
||
|
Notifications.update();
|
||
|
}
|
||
|
} else {
|
||
|
// console.log('setTurboMode: false');
|
||
|
Notifications.currentDelay = Notifications.options.updateDelay;
|
||
|
}
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Given an item, remove it if expired, otherwise update its created and expiration counters
|
||
|
*
|
||
|
* @param $li
|
||
|
*
|
||
|
*/
|
||
|
_updateItemTime: function($li, timeNow) {
|
||
|
|
||
|
var $created = $li.find('small.created');
|
||
|
var createdStr = '';
|
||
|
|
||
|
if($created.length > 0) {
|
||
|
var created = parseInt($created.attr('data-created'));
|
||
|
var secs = timeNow - created;
|
||
|
createdStr = Notifications.relativeTime(secs, true);
|
||
|
}
|
||
|
|
||
|
var expires = $li.attr('data-expires');
|
||
|
if(expires) {
|
||
|
expires = parseInt(expires);
|
||
|
if(expires > 0 && expires <= timeNow) {
|
||
|
$li.slideUp('fast', function() {
|
||
|
Notifications._remove($li);
|
||
|
});
|
||
|
} else {
|
||
|
if(createdStr.length > 0) createdStr += ' / ';
|
||
|
var expiresStr = Notifications.options.i18n.expires + ' ' + Notifications.relativeTime(timeNow - expires); // @todo i18n
|
||
|
if(Math.abs(timeNow - expires) < 10) expiresStr = '<strong>' + expiresStr + '</strong>';
|
||
|
createdStr += expiresStr;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
$created.html(createdStr);
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Update times for all items and remove expired items
|
||
|
*
|
||
|
* @param timeNow
|
||
|
*
|
||
|
*/
|
||
|
_updateTime: function() {
|
||
|
if(Notifications.timeNow == 0) return;
|
||
|
Notifications.$list.children('li').each(function() {
|
||
|
Notifications._updateItemTime($(this), Notifications.timeNow);
|
||
|
});
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Check server for new notifications
|
||
|
*
|
||
|
*/
|
||
|
update: function() {
|
||
|
|
||
|
if(Notifications.updating) {
|
||
|
// if already running, re-schedule it to run in 1 second
|
||
|
clearTimeout(Notifications.updateTimeout);
|
||
|
Notifications.updateTimeout = setTimeout('Notifications.update()', Notifications.currentDelay);
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
Notifications.updating = true;
|
||
|
|
||
|
var rm = '';
|
||
|
var $rm = Notifications.$list.find("li.removed"); // find items to be physically removed
|
||
|
|
||
|
$rm.each(function() {
|
||
|
rm += $(this).attr('id') + ',';
|
||
|
$(this).remove();
|
||
|
});
|
||
|
|
||
|
var url = "./?Notifications=update&time=" + Notifications.options.updateLast;
|
||
|
if(rm.length) url += '&rm=' + rm;
|
||
|
if(Notifications.useSession && Notifications.options.processKey.length) {
|
||
|
url += '&processKey=' + Notifications.options.processKey + "." + sessionStorage.pwWindowName;
|
||
|
}
|
||
|
|
||
|
$.getJSON(url, function(data) {
|
||
|
Notifications._update(data);
|
||
|
clearTimeout(Notifications.updateTimeout);
|
||
|
Notifications.updateTimeout = setTimeout('Notifications.update()', Notifications.currentDelay);
|
||
|
Notifications.updating = false;
|
||
|
});
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Updates .NotificationTrigger
|
||
|
*
|
||
|
* param object data Notification data from ajax request
|
||
|
* param bool Was added from runtime API? (rather than ajax)
|
||
|
*
|
||
|
*/
|
||
|
_update: function(data) {
|
||
|
|
||
|
var timeNow = parseInt(data.time);
|
||
|
var annoy = false;
|
||
|
var qty = data.notifications.length;
|
||
|
var alerts = [];
|
||
|
|
||
|
if(qty > 0) {
|
||
|
Notifications.numEmptyRequests = 0;
|
||
|
} else {
|
||
|
Notifications.numEmptyRequests++;
|
||
|
// stop turbo mode if we've had a few empty requests in a row
|
||
|
if(Notifications.numEmptyRequests > 2) Notifications.setTurboMode(false);
|
||
|
}
|
||
|
|
||
|
if(timeNow > 0) Notifications.timeNow = timeNow;
|
||
|
|
||
|
Notifications.options.updateLast = Notifications.timeNow;
|
||
|
|
||
|
for(var n = 0; n < qty; n++) {
|
||
|
var notification = data.notifications[n];
|
||
|
if(notification.flagNames.indexOf('alert') > -1) {
|
||
|
alerts[alerts.length] = notification;
|
||
|
} else {
|
||
|
Notifications._add(notification, !data.runtime);
|
||
|
if (notification.flagNames.indexOf('annoy') > -1) annoy = true;
|
||
|
}
|
||
|
if(notification.flagNames.indexOf('no-ghost') < 0) Notifications._ghost(notification, n);
|
||
|
}
|
||
|
|
||
|
// if any notifications were marked 'annoy' open the notifications menu now
|
||
|
if(annoy && !Notifications.$menu.hasClass('open')) Notifications.$bug.click();
|
||
|
if(annoy) window.scrollTo(0, 0);
|
||
|
|
||
|
// only update time if menu is closed since we already have a timer running,
|
||
|
// and that timer only runs when menu is already open
|
||
|
if(!Notifications.$menu.hasClass("open")) Notifications._updateTime();
|
||
|
|
||
|
Notifications._updateBug();
|
||
|
|
||
|
if(alerts.length) {
|
||
|
for (var n = 0; n < alerts.length; n++) {
|
||
|
ProcessWire.alert(alerts[n].title);
|
||
|
}
|
||
|
}
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Update the notification bug / quantity counter
|
||
|
*
|
||
|
* @private
|
||
|
*
|
||
|
*/
|
||
|
_updateBug: function() {
|
||
|
|
||
|
var $bug = Notifications.$bug;
|
||
|
var qtyTotal = 0;
|
||
|
var qtyError = 0;
|
||
|
var qtyWarning = 0;
|
||
|
|
||
|
Notifications.$list.children('li').each(function() {
|
||
|
var $li = $(this);
|
||
|
qtyTotal++;
|
||
|
if($li.hasClass('NoticeError')) qtyError++;
|
||
|
else if($li.hasClass('NoticeWarning')) qtyWarning++;
|
||
|
});
|
||
|
|
||
|
if(parseInt($bug.attr('data-qty')) == qtyTotal) {
|
||
|
// no change
|
||
|
} else {
|
||
|
// update count and highlight
|
||
|
Notifications._updateBugQty(qtyTotal);
|
||
|
$bug.effect('highlight', 300);
|
||
|
}
|
||
|
|
||
|
if(qtyError > 0) {
|
||
|
$bug.addClass(Notifications.options.classError, 'slow').removeClass(Notifications.options.classWarning, 'slow');
|
||
|
} else if(qtyWarning > 0) {
|
||
|
$bug.addClass(Notifications.options.classWarning, 'slow').removeClass(Notifications.options.classError, 'slow');
|
||
|
} else {
|
||
|
$bug.removeClass(Notifications.options.classWarning + ' ' + Notifications.options.classError, 'slow');
|
||
|
}
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Update the quantity shown in the bug
|
||
|
*
|
||
|
*/
|
||
|
_updateBugQty: function(qtyTotal) {
|
||
|
|
||
|
var $bug = Notifications.$bug;
|
||
|
var $bugQty = $bug.children('.qty');
|
||
|
var qtyText = qtyTotal > 99 ? '99+' : qtyTotal;
|
||
|
|
||
|
$bug.attr('class', $bug.attr('class').replace(/qty\d+\s*/g, ''));
|
||
|
$bug.addClass('qty' + qtyTotal);
|
||
|
$bugQty.text(qtyText);
|
||
|
|
||
|
if(qtyTotal == 0) {
|
||
|
$bug.fadeOut();
|
||
|
} else {
|
||
|
if(!$bug.is(":visible")) $bug.fadeIn();
|
||
|
}
|
||
|
|
||
|
$bug.attr('data-qty', qtyTotal);
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Add a notification (external/public API use)
|
||
|
*
|
||
|
* param object Notification to add containing these properties
|
||
|
*
|
||
|
*/
|
||
|
add: function(notification) {
|
||
|
var qty = Notifications.runtime.length;
|
||
|
notification.addClass = 'runtime';
|
||
|
notification.runtime = true;
|
||
|
Notifications.runtime[qty] = notification;
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Render any add()'d notifications now (for public API)
|
||
|
*
|
||
|
*/
|
||
|
render: function() {
|
||
|
Notifications.renderTimeout = setTimeout(function() {
|
||
|
|
||
|
var qtyError = 0;
|
||
|
var qtyWarning = 0;
|
||
|
var qtyMessage = 0;
|
||
|
|
||
|
$(Notifications.runtime).each(function(n, notification) {
|
||
|
if(notification.flagNames.indexOf('error') != -1) {
|
||
|
qtyError++;
|
||
|
} else if(notification.flagNames.indexOf('warning') != -1) {
|
||
|
qtyWarning++;
|
||
|
} else {
|
||
|
qtyMessage++;
|
||
|
}
|
||
|
});
|
||
|
|
||
|
var data = {
|
||
|
qty: Notifications.runtime.length,
|
||
|
qtyNew: 0,
|
||
|
qtyMessage: qtyMessage,
|
||
|
qtyWarning: qtyWarning,
|
||
|
qtyError: qtyError,
|
||
|
notifications: Notifications.runtime,
|
||
|
runtime: true
|
||
|
};
|
||
|
|
||
|
Notifications._update(data, true);
|
||
|
|
||
|
if(Notifications.$list.find(".NotificationProgress").length > 0) {
|
||
|
Notifications.setTurboMode(true);
|
||
|
}
|
||
|
|
||
|
}, 250);
|
||
|
Notifications.numRender++;
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Add a notification (internal use)
|
||
|
*
|
||
|
* @param object Notification to add
|
||
|
* @param bool highlight Whether to highlight the notification (use true for new notifications)
|
||
|
*
|
||
|
*/
|
||
|
_add: function(notification, highlight) {
|
||
|
|
||
|
var exists = false;
|
||
|
var open = false;
|
||
|
var $li = Notifications.$list.children("#" + notification.id);
|
||
|
var progressNext = parseInt(notification.progress);
|
||
|
var progressPrev = 0;
|
||
|
var $createdPrev = null;
|
||
|
|
||
|
if($li.length > 0) {
|
||
|
exists = true;
|
||
|
highlight = false;
|
||
|
open = $li.hasClass('open');
|
||
|
var $progress = $li.find(".NotificationProgress");
|
||
|
if($progress.length > 0) {
|
||
|
progressPrev = parseInt($progress.text());
|
||
|
}
|
||
|
$createdPrev = $li.find("small.created");
|
||
|
$li.empty(); // clear it out
|
||
|
|
||
|
} else {
|
||
|
$li = $("<li></li>");
|
||
|
}
|
||
|
|
||
|
$li.attr('id', notification.id).addClass(Notifications.options.classCommon);
|
||
|
if(notification.expires > 0) {
|
||
|
$li.attr('data-expires', notification.expires);
|
||
|
}
|
||
|
|
||
|
var $icon = $("<i></i>").addClass('fa fa-fw fa-' + notification.icon);
|
||
|
var $title = $("<span></span>").addClass('NotificationTitle').html(notification.title);
|
||
|
var $p = $("<p></p>").append($title).prepend(' ').prepend($icon);
|
||
|
var $div = $("<div></div>").addClass(Notifications.options.classContainer).append($p);
|
||
|
var $text = $("<div></div>").addClass('NotificationText');
|
||
|
var $rm = $("<i class='NotificationRemove fa fa-" + Notifications.options.iconRemove + "'></i>");
|
||
|
var addClass = '';
|
||
|
|
||
|
if(progressNext > 0) {
|
||
|
$li.prepend(Notifications._progress($title, progressNext, progressPrev));
|
||
|
if(progressNext < 100) {
|
||
|
$li.addClass('NotificationHasProgress', 'normal');
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if('addClass' in notification && notification.addClass.length > 0) {
|
||
|
addClass = notification.addClass;
|
||
|
$li.addClass(addClass);
|
||
|
}
|
||
|
|
||
|
if($createdPrev !== null && $createdPrev.length) {
|
||
|
// keep existing created time
|
||
|
$title.append(' ').append($createdPrev);
|
||
|
} else if('when' in notification && notification.created > 0) {
|
||
|
// insert created time
|
||
|
$title.append(" <small data-created='" + notification.created + "' class='created'>" + notification.when + "</small>");
|
||
|
}
|
||
|
|
||
|
if(notification.flagNames.indexOf('debug') != -1) $li.addClass(Notifications.options.classDebug);
|
||
|
if(notification.flagNames.indexOf('error') != -1) $li.addClass(Notifications.options.classError);
|
||
|
else if(notification.flagNames.indexOf('warning') != -1) $li.addClass(Notifications.options.classWarning);
|
||
|
else if(notification.flagNames.indexOf('message') != -1) $li.addClass(Notifications.options.classMessage);
|
||
|
|
||
|
|
||
|
if(notification.html.length > 0) {
|
||
|
|
||
|
$text.html(notification.html);
|
||
|
var $chevron = $("<i class='fa fa-chevron-circle-right'></i>");
|
||
|
$title.append(" ").append($chevron);
|
||
|
|
||
|
$title.click(function() {
|
||
|
if($li.hasClass('open')) {
|
||
|
$li.removeClass('open');
|
||
|
$text.slideUp('fast').removeClass('open');
|
||
|
$chevron.removeClass('fa-chevron-circle-down').addClass('fa-chevron-circle-right');
|
||
|
} else {
|
||
|
$text.slideDown('fast').addClass('open');
|
||
|
$li.addClass('open');
|
||
|
$chevron.removeClass('fa-chevron-circle-right').addClass('fa-chevron-circle-down');
|
||
|
}
|
||
|
});
|
||
|
|
||
|
$div.append($text);
|
||
|
|
||
|
if(open || notification.flagNames.indexOf('open') != -1) {
|
||
|
if(!open) {
|
||
|
$title.click();
|
||
|
/*
|
||
|
setTimeout(function() {
|
||
|
$text.fadeIn('slow', function() {
|
||
|
$li.addClass('open');
|
||
|
$text.addClass('open');
|
||
|
});
|
||
|
}, 500);
|
||
|
*/
|
||
|
} else {
|
||
|
$li.addClass('open');
|
||
|
$text.show().addClass('open');
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// click event for 'remove' link
|
||
|
$rm.on('click', function() {
|
||
|
Notifications._remove($li);
|
||
|
});
|
||
|
|
||
|
$p.prepend($rm);
|
||
|
$li.append($div)
|
||
|
|
||
|
if(highlight) {
|
||
|
$li.hide();
|
||
|
if(Notifications.options.reverse) {
|
||
|
Notifications.$list.append($li);
|
||
|
} else {
|
||
|
Notifications.$list.prepend($li);
|
||
|
}
|
||
|
$li.slideDown().effect('highlight', 500);
|
||
|
} else if(exists) {
|
||
|
$li.show();
|
||
|
} else {
|
||
|
Notifications.$list.append($li);
|
||
|
}
|
||
|
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Remove the notification item $li
|
||
|
*
|
||
|
* @param $li
|
||
|
* @private
|
||
|
*
|
||
|
*/
|
||
|
_remove: function($li) {
|
||
|
// mark it as removed. it will be physically removed by the next update()
|
||
|
$li.addClass('removed').hide();
|
||
|
|
||
|
var qtyTotal = $li.siblings(":visible").length;
|
||
|
|
||
|
// update the quantity shown in the bug
|
||
|
Notifications._updateBugQty(qtyTotal);
|
||
|
|
||
|
// tell the update timer to run in 1 second, so that items are removed from the server too
|
||
|
clearTimeout(Notifications.updateTimeout);
|
||
|
Notifications.updateTimeout = setTimeout('Notifications.update()', 1000);
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Get progress bar
|
||
|
*
|
||
|
* @param $title
|
||
|
* @param progressNext
|
||
|
* @param progressPrev
|
||
|
* @returns {*|jQuery}
|
||
|
* @private
|
||
|
*
|
||
|
*/
|
||
|
_progress: function($title, progressNext, progressPrev) {
|
||
|
|
||
|
var $progress = $("<div></div>").addClass('NotificationProgress')
|
||
|
.html("<span>" + progressNext + '%</span>').css('width', progressPrev + '%').hide();
|
||
|
|
||
|
if(progressNext > progressPrev) {
|
||
|
|
||
|
var duration = progressPrev == 0 ? Notifications.currentDelay / 1.4 : 1750;
|
||
|
var easing = 'linear';
|
||
|
|
||
|
if(progressNext == 100) {
|
||
|
duration = 750;
|
||
|
easing = 'swing';
|
||
|
} else if(progressPrev == 0) {
|
||
|
easing = 'swing';
|
||
|
}
|
||
|
|
||
|
if(progressNext > 0 && progressNext <= 100) {
|
||
|
$progress.show().animate({
|
||
|
width: progressNext + '%'
|
||
|
}, {
|
||
|
duration: duration,
|
||
|
easing: easing,
|
||
|
complete: function() {
|
||
|
if(progressNext >= 100) {
|
||
|
$progress.fadeOut('slow', function() {
|
||
|
$title.parents(".NotificationHasProgress").removeClass('NotificationHasProgress', 'slow');
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
});
|
||
|
|
||
|
} else if(progressNext >= 100) {
|
||
|
// don't show
|
||
|
if(Notifications.$list.find(".NotificationHasProgress").length == 0) Notifications.setTurboMode(false);
|
||
|
|
||
|
} else {
|
||
|
$progress.css('width', progressNext + '%').show();
|
||
|
}
|
||
|
|
||
|
$progress.height('100%');
|
||
|
}
|
||
|
|
||
|
$title.append(" <strong class='NotificationProgressPercent'>" + progressNext + '%</strong>');
|
||
|
return $progress;
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Add a notification ghost (subtle notification that appears than disappears)
|
||
|
*
|
||
|
* param object notification Notification to ghost
|
||
|
* param int n Index of the notification, affects when it is shown so that multiple don't appear and disappear as a group.
|
||
|
*
|
||
|
*/
|
||
|
_ghost: function(notification, n) {
|
||
|
|
||
|
if(notification.progress > 0 && notification.progress < 100) return;
|
||
|
if(Notifications.$menu.hasClass('open')) return;
|
||
|
|
||
|
var $icon = $('<i class="fa fa-fw fa-' + notification.icon + '"></i>');
|
||
|
var $ghost = $("<div class='NotificationGhost'></div>").append($icon).append(' ' + $("<span>" + notification.title + "</span>").text());
|
||
|
var $li = $("<li></li>").append($ghost);
|
||
|
var delay = Notifications.options.ghostDelay;
|
||
|
|
||
|
if(notification.flagNames.indexOf('error') > -1) {
|
||
|
$ghost.addClass(Notifications.options.classError);
|
||
|
delay = Notifications.options.ghostDelayError;
|
||
|
|
||
|
} else if(notification.flagNames.indexOf('warning') > -1) {
|
||
|
$ghost.addClass(Notifications.options.classWarning);
|
||
|
delay = Notifications.options.ghostDelayError;
|
||
|
|
||
|
} else {
|
||
|
$ghost.addClass(Notifications.options.classMessage);
|
||
|
}
|
||
|
|
||
|
Notifications.$ghosts.append($li.hide());
|
||
|
Notifications.ghostsActive++;
|
||
|
|
||
|
var fadeSpeed = Notifications.options.ghostFadeSpeed;
|
||
|
var opacity = Notifications.options.ghostOpacity;
|
||
|
var interval = 100 * n;
|
||
|
var windowHeight = $(window).height();
|
||
|
|
||
|
if(fadeSpeed.length == 0) interval = 200 * n;
|
||
|
|
||
|
setTimeout(function() {
|
||
|
|
||
|
if(fadeSpeed.length > 0) {
|
||
|
$li.fadeTo(fadeSpeed, opacity);
|
||
|
} else {
|
||
|
$li.show().css('opacity', opacity);
|
||
|
}
|
||
|
|
||
|
var y = $li.offset().top;
|
||
|
var h = $li.height();
|
||
|
if(y + h > (windowHeight / 2)) {
|
||
|
Notifications.$ghosts.animate({ top: "-=" + (h+3) }, 'fast');
|
||
|
}
|
||
|
|
||
|
setTimeout(function() {
|
||
|
var ghostDone = function() {
|
||
|
$li.addClass('removed');
|
||
|
Notifications.ghostsActive--;
|
||
|
if(Notifications.ghostsActive == 0) {
|
||
|
Notifications.$ghosts.children('li').remove();
|
||
|
}
|
||
|
};
|
||
|
if(fadeSpeed.length > 0) {
|
||
|
$li.fadeTo(fadeSpeed, 0.01, ghostDone);
|
||
|
} else {
|
||
|
$li.css('opacity', 0.01);
|
||
|
ghostDone();
|
||
|
}
|
||
|
}, delay);
|
||
|
}, interval);
|
||
|
|
||
|
notification.ghostShown = true;
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Click event for notification bug element (small red counter)
|
||
|
*
|
||
|
*/
|
||
|
clickBug: function() {
|
||
|
|
||
|
var $menu = Notifications.$menu;
|
||
|
|
||
|
if($menu.hasClass('open')) {
|
||
|
|
||
|
$menu.slideUp('fast', function() {
|
||
|
$menu.removeClass('open');
|
||
|
Notifications.$bug.removeClass('open'); // css only
|
||
|
clearTimeout(Notifications.timerTimeout);
|
||
|
});
|
||
|
|
||
|
} else {
|
||
|
|
||
|
if(!$menu.hasClass('init')) {
|
||
|
$menu.prependTo($('body'));
|
||
|
$menu.addClass('init');
|
||
|
}
|
||
|
|
||
|
$menu.slideDown('fast', function() {
|
||
|
$menu.addClass('open');
|
||
|
Notifications.$bug.addClass('open'); // css only
|
||
|
Notifications._startTimeUpdater();
|
||
|
});
|
||
|
|
||
|
Notifications.$ghosts.find('li').hide();
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Updates notification item times (created and expire)
|
||
|
*
|
||
|
* @private
|
||
|
*/
|
||
|
_startTimeUpdater: function() {
|
||
|
if(Notifications.timeNow > 0) Notifications.timeNow += 1; // increment by 1 seconds
|
||
|
Notifications._updateTime();
|
||
|
Notifications.timerTimeout = setTimeout('Notifications._startTimeUpdater()', 1000);
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Show a notification right now (runtime, internal)
|
||
|
*
|
||
|
*/
|
||
|
_show: function(type, title, html, icon, href) {
|
||
|
|
||
|
var notification = {
|
||
|
id: 0,
|
||
|
title: title,
|
||
|
from: '',
|
||
|
created: 0,
|
||
|
modified: 0,
|
||
|
when: 'now',
|
||
|
href: href,
|
||
|
icon: icon,
|
||
|
flags: 0,
|
||
|
flagNames: type + ' notice',
|
||
|
progress: 0,
|
||
|
html: html,
|
||
|
qty: 1
|
||
|
};
|
||
|
|
||
|
Notifications.add(notification);
|
||
|
|
||
|
// if notifications have already been rendered, we can show this one now
|
||
|
if(Notifications.numRender > 0) {
|
||
|
Notifications.render();
|
||
|
}
|
||
|
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Show a runtime message notification right now
|
||
|
*
|
||
|
*/
|
||
|
message: function(title, html, icon, href) {
|
||
|
if(typeof html == "undefined") html = '';
|
||
|
if(typeof icon == "undefined") icon = Notifications.options.iconMessage;
|
||
|
if(typeof href == "undefined") href = '';
|
||
|
Notifications._show('message', title, html, icon, href);
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Show a runtime warning notification right now
|
||
|
*
|
||
|
*/
|
||
|
warning: function(title, html, icon, href) {
|
||
|
if(typeof html == "undefined") html = '';
|
||
|
if(typeof icon == "undefined") icon = Notifications.options.iconWarning;
|
||
|
if(typeof href == "undefined") href = '';
|
||
|
Notifications._show('warning', title, html, icon, href);
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Show a runtime error notification right now
|
||
|
*
|
||
|
*/
|
||
|
error: function(title, html, icon, href) {
|
||
|
if(typeof html == "undefined") html = '';
|
||
|
if(typeof icon == "undefined") icon = Notifications.options.iconError;
|
||
|
if(typeof href == "undefined") href = '';
|
||
|
Notifications._show('error', title, html, icon, href);
|
||
|
},
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Initialize notifications, to be called at document.ready
|
||
|
*
|
||
|
*/
|
||
|
init: function(options) {
|
||
|
|
||
|
$.extend(Notifications.options, options);
|
||
|
|
||
|
Notifications.currentDelay = Notifications.options.updateDelay;
|
||
|
|
||
|
Notifications.$menu = $("#NotificationMenu");
|
||
|
Notifications.$bug = $("#NotificationBug");
|
||
|
Notifications.$list = $("#NotificationList");
|
||
|
Notifications.$ghosts = $("#NotificationGhosts");
|
||
|
|
||
|
if(!Notifications.$bug.length) return;
|
||
|
|
||
|
Notifications.$menu.hide();
|
||
|
Notifications.$bug.click(Notifications.clickBug);
|
||
|
Notifications.useSession = typeof sessionStorage != "undefined";
|
||
|
|
||
|
// start polling for new notifications
|
||
|
Notifications.updateTimeout = setTimeout(Notifications.update, Notifications.currentDelay);
|
||
|
|
||
|
$("#ProcessPageSearchForm input").dblclick(function(e) {
|
||
|
Notifications.message(
|
||
|
"ProcessWire Notifications v" + Notifications.options.version,
|
||
|
"Grab a coffee and come and visit us at the <a target='_blank' href='https://processwire.com/talk/'>ProcessWire support forums</a>.</p>",
|
||
|
'coffee fa-spin');
|
||
|
return false;
|
||
|
});
|
||
|
|
||
|
// hide the notifications bug and menu when modal window opened
|
||
|
$(document).on('pw-modal-opened', function() {
|
||
|
if(Notifications.$bug.is(":visible")) Notifications.$bug.fadeOut().addClass('hidden-for-modal');
|
||
|
if(Notifications.$menu.is(":visible")) Notifications.$menu.hide().addClass('hidden-for-modal');
|
||
|
}).on('pw-modal-closed', function() {
|
||
|
if(Notifications.$bug.hasClass('hidden-for-modal')) Notifications.$bug.fadeIn().removeClass('hidden-for-modal');
|
||
|
if(Notifications.$menu.hasClass('hidden-for-modal')) Notifications.$menu.slideDown().removeClass('hidden-for-modal');
|
||
|
});
|
||
|
|
||
|
if(Notifications.useSession && Notifications.options.processKey.length) {
|
||
|
if(typeof sessionStorage.pwWindowName == "undefined" || sessionStorage.pwWindowName.indexOf('PW') !== 0) {
|
||
|
sessionStorage.pwWindowName = 'PW' +
|
||
|
Math.floor(Math.random() * 0x10000).toString() +
|
||
|
Math.floor(Math.random() * 0x10000).toString();
|
||
|
}
|
||
|
// for debugging:
|
||
|
// $("#ProcessPageSearchQuery").val(sessionStorage.pwWindowName);
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
|