UNPKG

@spalger/kibana

Version:

Kibana is an open source (Apache Licensed), browser based analytics and search dashboard for Elasticsearch. Kibana is a snap to setup and start using. Kibana strives to be easy to get started with, while also being flexible and powerful, just like Elastic

354 lines (301 loc) 9.16 kB
define(function (require) { var _ = require('lodash'); var $ = require('jquery'); var notifs = []; var setTO = setTimeout; var clearTO = clearTimeout; var consoleGroups = ('group' in window.console) && ('groupCollapsed' in window.console) && ('groupEnd' in window.console); var fatalSplashScreen = require('ui/notify/partials/fatal_splash_screen.html'); var log = _.bindKey(console, 'log'); // used to identify the first call to fatal, set to false there var firstFatal = true; var fatalToastTemplate = (function lazyTemplate(tmpl) { var compiled; return function (vars) { return (compiled || (compiled = _.template(tmpl)))(vars); }; }(require('ui/notify/partials/fatal.html'))); function now() { if (window.performance && window.performance.now) { return window.performance.now(); } return Date.now(); } function closeNotif(cb, key) { return function () { // this === notif var i = notifs.indexOf(this); if (i !== -1) notifs.splice(i, 1); if (this.timerId) this.timerId = clearTO(this.timerId); if (typeof cb === 'function') cb(key); }; } function add(notif, cb) { if (notif.lifetime !== Infinity) { notif.timerId = setTO(function () { closeNotif(cb, 'ignore').call(notif); }, notif.lifetime); } notif.clear = closeNotif(); if (notif.actions) { notif.actions.forEach(function (action) { notif[action] = closeNotif(cb, action); }); } notif.count = (notif.count || 0) + 1; var dup = _.find(notifs, function (item) { return item.content === notif.content && item.lifetime === notif.lifetime; }); if (dup) { dup.count += 1; dup.stacks = _.union(dup.stacks, [notif.stack]); return dup; } notif.stacks = [notif.stack]; notifs.push(notif); return notif; } function formatMsg(msg, from) { var rtn = ''; if (from) { rtn += from + ': '; } if (typeof msg === 'string') { rtn += msg; } else if (msg instanceof Error) { rtn += msg.message; } return rtn; } // browsers format Error.stack differently; always include message function formatStack(err) { if (err.stack && !~err.stack.indexOf(err.message)) { return 'Error: ' + err.message + '\n' + err.stack; } return err.stack; } /** * Functionality to check that */ function Notifier(opts) { var self = this; opts = opts || {}; // label type thing to say where notifications came from self.from = opts.location; 'event lifecycle timed fatal error warning info'.split(' ').forEach(function (m) { self[m] = _.bind(self[m], self); }); } // to be notified when the first fatal error occurs, push a function into this array. Notifier.fatalCallbacks = []; // set the timer functions that all notification managers will use Notifier.setTimerFns = function (set, clear) { setTO = set; clearTO = clear; }; // simply a pointer to the global notif list Notifier.prototype._notifs = notifs; /** * Log a sometimes redundant event * @param {string} name - The name of the group * @param {boolean} success - Simple flag stating whether the event succeeded */ Notifier.prototype.event = createGroupLogger('event', { open: true }); /** * Log a major, important, event in the lifecycle of the application * @param {string} name - The name of the lifecycle event * @param {boolean} success - Simple flag stating whether the lifecycle event succeeded */ Notifier.prototype.lifecycle = createGroupLogger('lifecycle', { open: true }); /** * Wrap a function so that it's execution time gets logged. * * @param {function} fn - the function to wrap, it's .name property is * read so make sure to set it * @return {function} - the wrapped function */ Notifier.prototype.timed = function (name, fn) { var self = this; if (typeof name === 'function') { fn = name; name = fn.name; } return function WrappedNotifierFunction() { var cntx = this; var args = arguments; return self.event(name, function () { return fn.apply(cntx, args); }); }; }; /** * Kill the page, display an error, then throw the error. * Used as a last-resort error back in many promise chains * so it rethrows the error that's displayed on the page. * * @param {Error} err - The error that occured */ Notifier.prototype.fatal = function (err) { this._showFatal(err); throw err; }; /** * Display an error that destroys the entire app. Broken out so that * global error handlers can display fatal errors without throwing another * error like in #fatal() * * @param {Error} err - The fatal error that occured */ Notifier.prototype._showFatal = function (err) { if (firstFatal) { _.callEach(Notifier.fatalCallbacks); firstFatal = false; window.addEventListener('hashchange', function () { window.location.reload(); }); } var html = fatalToastTemplate({ msg: formatMsg(err, this.from), stack: formatStack(err) }); var $container = $('#fatal-splash-screen'); if (!$container.size()) { $(document.body) // in case the app has not completed boot .removeAttr('ng-cloak') .html(fatalSplashScreen); $container = $('#fatal-splash-screen'); } $container.append(html); console.error(err.stack); }; /** * Alert the user of an error that occured * @param {Error|String} err */ Notifier.prototype.error = function (err, cb) { return add({ type: 'danger', content: formatMsg(err, this.from), icon: 'warning', title: 'Error', lifetime: Infinity, actions: ['report', 'accept'], stack: formatStack(err) }, cb); }; /** * Warn the user abort something * @param {[type]} msg [description] * @return {[type]} [description] */ Notifier.prototype.warning = function (msg, cb) { return add({ type: 'warning', content: formatMsg(msg, this.from), icon: 'warning', title: 'Warning', lifetime: 10000, actions: ['accept'] }, cb); }; /** * Display a debug message * @param {String} msg [description] * @return {[type]} [description] */ Notifier.prototype.info = function (msg, cb) { return add({ type: 'info', content: formatMsg(msg, this.from), icon: 'info-circle', title: 'Debug', lifetime: 5000, actions: ['accept'] }, cb); }; if (log === _.noop) { Notifier.prototype.log = _.noop; } else { Notifier.prototype.log = function () { var args = [].slice.apply(arguments); if (this.from) args.unshift(this.from + ':'); log.apply(null, args); }; } // general functionality used by .event() and .lifecycle() function createGroupLogger(type, opts) { // Track the groups managed by this logger var groups = window[type + 'Groups'] = {}; return function logger(name, success) { var status; // status of the timer var exec; // function to execute and wrap var ret; // return value var complete = function (val) { logger(name, true); return val; }; var failure = function (err) { logger(name, false); throw err; }; if (typeof success === 'function' || success === void 0) { // start groups[name] = now(); if (success) { // success === the function to time exec = success; } else { // function that can report on the success or failure of an op, and pass their value along ret = complete; ret.failure = failure; } } else { groups[name] = now() - (groups[name] || 0); var time = ' in ' + groups[name].toFixed(2) + 'ms'; // end if (success) { status = 'complete' + time; } else { groups[name] = false; status = 'failure' + time; } } if (consoleGroups) { if (status) { console.log(status); console.groupEnd(); } else { if (opts.open) { console.group(name); } else { console.groupCollapsed(name); } } } else { log('KBN: ' + name + (status ? ' - ' + status : '')); } if (exec) { try { ret = exec(); } catch (e) { return failure(e); } if (ret && typeof ret.then === 'function') { // return a new promise that proxies the value // and logs about the promise outcome return ret.then(function (val) { complete(); return val; }, function (err) { failure(err); throw err; }); } // the function executed fine, and didn't return a promise, move along complete(); } return ret; }; } return Notifier; });