From 56a06676cdd7fc54869de257dd8a06ce75688990 2014-09-29 07:00:43 From: Matthias Bussonnier Date: 2014-09-29 07:00:43 Subject: [PATCH] Merge pull request #6544 from jhamrick/notification-widget Clean up notification widget --- diff --git a/IPython/html/static/notebook/js/notificationarea.js b/IPython/html/static/notebook/js/notificationarea.js index 8c406ad..81f650b 100644 --- a/IPython/html/static/notebook/js/notificationarea.js +++ b/IPython/html/static/notebook/js/notificationarea.js @@ -11,16 +11,22 @@ define([ ], function(IPython, $, utils, dialog, notificationwidget, moment) { "use strict"; + // store reference to the NotificationWidget class + var NotificationWidget = notificationwidget.NotificationWidget; + + /** + * Construct the NotificationArea object. Options are: + * events: $(Events) instance + * save_widget: SaveWidget instance + * notebook: Notebook instance + * keyboard_manager: KeyboardManager instance + * + * @constructor + * @param {string} selector - a jQuery selector string for the + * notification area element + * @param {Object} [options] - a dictionary of keyword arguments. + */ var NotificationArea = function (selector, options) { - // Constructor - // - // Parameters: - // selector: string - // options: dictionary - // Dictionary of keyword arguments. - // notebook: Notebook instance - // events: $(Events) instance - // save_widget: SaveWidget instance this.selector = selector; this.events = options.events; this.save_widget = options.save_widget; @@ -32,47 +38,70 @@ define([ this.widget_dict = {}; }; - NotificationArea.prototype.temp_message = function (msg, timeout, css_class) { - var tdiv = $('
') - .addClass('notification_widget') - .addClass(css_class) - .hide() - .text(msg); - - $(this.selector).append(tdiv); - var tmout = Math.max(1500,(timeout||1500)); - tdiv.fadeIn(100); - - setTimeout(function () { - tdiv.fadeOut(100, function () {tdiv.remove();}); - }, tmout); - }; - - NotificationArea.prototype.widget = function(name) { - if(this.widget_dict[name] === undefined) { + /** + * Get a widget by name, creating it if it doesn't exist. + * + * @method widget + * @param {string} name - the widget name + */ + NotificationArea.prototype.widget = function (name) { + if (this.widget_dict[name] === undefined) { return this.new_notification_widget(name); } return this.get_widget(name); }; - NotificationArea.prototype.get_widget = function(name) { + /** + * Get a widget by name, throwing an error if it doesn't exist. + * + * @method get_widget + * @param {string} name - the widget name + */ + NotificationArea.prototype.get_widget = function (name) { if(this.widget_dict[name] === undefined) { throw('no widgets with this name'); } return this.widget_dict[name]; }; - NotificationArea.prototype.new_notification_widget = function(name) { - if(this.widget_dict[name] !== undefined) { - throw('widget with that name already exists ! '); + /** + * Create a new notification widget with the given name. The + * widget must not already exist. + * + * @method new_notification_widget + * @param {string} name - the widget name + */ + NotificationArea.prototype.new_notification_widget = function (name) { + if (this.widget_dict[name] !== undefined) { + throw('widget with that name already exists!'); } - var div = $('
').attr('id','notification_'+name); + + // create the element for the notification widget and add it + // to the notification aread element + var div = $('
').attr('id', 'notification_' + name); $(this.selector).append(div); - this.widget_dict[name] = new notificationwidget.NotificationWidget('#notification_'+name); + + // create the widget object and return it + this.widget_dict[name] = new NotificationWidget('#notification_' + name); return this.widget_dict[name]; }; - NotificationArea.prototype.init_notification_widgets = function() { + /** + * Initialize the default set of notification widgets. + * + * @method init_notification_widgets + */ + NotificationArea.prototype.init_notification_widgets = function () { + this.init_kernel_notification_widget(); + this.init_notebook_notification_widget(); + }; + + /** + * Initialize the notification widget for kernel status messages. + * + * @method init_kernel_notification_widget + */ + NotificationArea.prototype.init_kernel_notification_widget = function () { var that = this; var knw = this.new_notification_widget('kernel'); var $kernel_ind_icon = $("#kernel_indicator_icon"); @@ -194,8 +223,14 @@ define([ } }); }); + }; - + /** + * Initialize the notification widget for notebook status messages. + * + * @method init_notebook_notification_widget + */ + NotificationArea.prototype.init_notebook_notification_widget = function () { var nnw = this.new_notification_widget('notebook'); // Notebook events @@ -247,7 +282,6 @@ define([ this.events.on('autosave_enabled.Notebook', function (evt, interval) { nnw.set_message("Saving every " + interval / 1000 + "s", 1000); }); - }; IPython.NotificationArea = NotificationArea; diff --git a/IPython/html/static/notebook/js/notificationwidget.js b/IPython/html/static/notebook/js/notificationwidget.js index d4ed891..0c7f672 100644 --- a/IPython/html/static/notebook/js/notificationwidget.js +++ b/IPython/html/static/notebook/js/notificationwidget.js @@ -7,6 +7,13 @@ define([ ], function(IPython, $) { "use strict"; + /** + * Construct a NotificationWidget object. + * + * @constructor + * @param {string} selector - a jQuery selector string for the + * notification widget element + */ var NotificationWidget = function (selector) { this.selector = selector; this.timeout = null; @@ -16,27 +23,41 @@ define([ this.style(); } this.element.hide(); - var that = this; - this.inner = $(''); this.element.append(this.inner); - }; + /** + * Add the 'notification_widget' CSS class to the widget element. + * + * @method style + */ NotificationWidget.prototype.style = function () { this.element.addClass('notification_widget'); }; - // msg : message to display - // timeout : time in ms before diseapearing - // - // if timeout <= 0 - // click_callback : function called if user click on notification - // could return false to prevent the notification to be dismissed + /** + * Set the notification widget message to display for a certain + * amount of time (timeout). The widget will be shown forever if + * timeout is <= 0 or undefined. If the widget is clicked while it + * is still displayed, execute an optional callback + * (click_callback). If the callback returns false, it will + * prevent the notification from being dismissed. + * + * Options: + * class - CSS class name for styling + * icon - CSS class name for the widget icon + * title - HTML title attribute for the widget + * + * @method set_message + * @param {string} msg - The notification to display + * @param {integer} [timeout] - The amount of time in milliseconds to display the widget + * @param {function} [click_callback] - The function to run when the widget is clicked + * @param {Object} [options] - Additional options + */ NotificationWidget.prototype.set_message = function (msg, timeout, click_callback, options) { - var options = options || {}; - var callback = click_callback || function() {return true;}; - var that = this; + options = options || {}; + // unbind potential previous callback this.element.unbind('click'); this.inner.attr('class', options.icon); @@ -47,52 +68,87 @@ define([ // reset previous set style this.element.removeClass(); this.style(); - if (options.class){ - - this.element.addClass(options.class) + if (options.class) { + this.element.addClass(options.class); } + + // clear previous timer if (this.timeout !== null) { clearTimeout(this.timeout); this.timeout = null; } - if (timeout !== undefined && timeout >=0) { + + // set the timer if a timeout is given + var that = this; + if (timeout !== undefined && timeout >= 0) { this.timeout = setTimeout(function () { that.element.fadeOut(100, function () {that.inner.text('');}); + that.element.unbind('click'); that.timeout = null; }, timeout); - } else { - this.element.click(function() { - if( callback() !== false ) { + } + + // bind the click callback if it is given + if (click_callback !== undefined) { + this.element.click(function () { + if (click_callback() !== false) { that.element.fadeOut(100, function () {that.inner.text('');}); - that.element.unbind('click'); } - if (that.timeout !== undefined) { - that.timeout = undefined; + that.element.unbind('click'); + if (that.timeout !== null) { clearTimeout(that.timeout); + that.timeout = null; } }); } }; - + /** + * Display an information message (styled with the 'info' + * class). Arguments are the same as in set_message. Default + * timeout is 3500 milliseconds. + * + * @method info + */ NotificationWidget.prototype.info = function (msg, timeout, click_callback, options) { - var options = options || {}; - options.class = options.class +' info'; - var timeout = timeout || 3500; + options = options || {}; + options.class = options.class + ' info'; + timeout = timeout || 3500; this.set_message(msg, timeout, click_callback, options); - } + }; + + /** + * Display a warning message (styled with the 'warning' + * class). Arguments are the same as in set_message. Messages are + * sticky by default. + * + * @method warning + */ NotificationWidget.prototype.warning = function (msg, timeout, click_callback, options) { - var options = options || {}; - options.class = options.class +' warning'; + options = options || {}; + options.class = options.class + ' warning'; this.set_message(msg, timeout, click_callback, options); - } + }; + + /** + * Display a danger message (styled with the 'danger' + * class). Arguments are the same as in set_message. Messages are + * sticky by default. + * + * @method danger + */ NotificationWidget.prototype.danger = function (msg, timeout, click_callback, options) { - var options = options || {}; - options.class = options.class +' danger'; + options = options || {}; + options.class = options.class + ' danger'; this.set_message(msg, timeout, click_callback, options); - } - + }; + /** + * Get the text of the widget message. + * + * @method get_message + * @return {string} - the message text + */ NotificationWidget.prototype.get_message = function () { return this.inner.html(); }; diff --git a/IPython/html/tests/notebook/notifications.js b/IPython/html/tests/notebook/notifications.js new file mode 100644 index 0000000..7366930 --- /dev/null +++ b/IPython/html/tests/notebook/notifications.js @@ -0,0 +1,116 @@ +// Test the notification area and widgets + +casper.notebook_test(function () { + var that = this; + var widget = function (name) { + return that.evaluate(function (name) { + return (IPython.notification_area.widget(name) !== undefined); + }, name); + }; + + var get_widget = function (name) { + return that.evaluate(function (name) { + return (IPython.notification_area.get_widget(name) !== undefined); + }, name); + }; + + var new_notification_widget = function (name) { + return that.evaluate(function (name) { + return (IPython.notification_area.new_notification_widget(name) !== undefined); + }, name); + }; + + var widget_has_class = function (name, class_name) { + return that.evaluate(function (name, class_name) { + var w = IPython.notification_area.get_widget(name); + return w.element.hasClass(class_name); + }, name, class_name); + }; + + var widget_message = function (name) { + return that.evaluate(function (name) { + var w = IPython.notification_area.get_widget(name); + return w.get_message(); + }, name); + }; + + this.then(function () { + // check that existing widgets are there + this.test.assert(get_widget('kernel') && widget('kernel'), 'The kernel notification widget exists'); + this.test.assert(get_widget('notebook') && widget('notbook'), 'The notebook notification widget exists'); + + // try getting a non-existant widget + this.test.assertRaises(get_widget, 'foo', 'get_widget: error is thrown'); + + // try creating a non-existant widget + this.test.assert(widget('bar'), 'widget: new widget is created'); + + // try creating a widget that already exists + this.test.assertRaises(new_notification_widget, 'kernel', 'new_notification_widget: error is thrown'); + }); + + // test creating 'info' messages + this.thenEvaluate(function () { + var tnw = IPython.notification_area.widget('test'); + tnw.info('test info'); + }); + this.waitUntilVisible('#notification_test', function () { + this.test.assert(widget_has_class('test', 'info'), 'info: class is correct'); + this.test.assertEquals(widget_message('test'), 'test info', 'info: message is correct'); + }); + + // test creating 'warning' messages + this.thenEvaluate(function () { + var tnw = IPython.notification_area.widget('test'); + tnw.warning('test warning'); + }); + this.waitUntilVisible('#notification_test', function () { + this.test.assert(widget_has_class('test', 'warning'), 'warning: class is correct'); + this.test.assertEquals(widget_message('test'), 'test warning', 'warning: message is correct'); + }); + + // test creating 'danger' messages + this.thenEvaluate(function () { + var tnw = IPython.notification_area.widget('test'); + tnw.danger('test danger'); + }); + this.waitUntilVisible('#notification_test', function () { + this.test.assert(widget_has_class('test', 'danger'), 'danger: class is correct'); + this.test.assertEquals(widget_message('test'), 'test danger', 'danger: message is correct'); + }); + + // test message timeout + this.thenEvaluate(function () { + var tnw = IPython.notification_area.widget('test'); + tnw.set_message('test timeout', 1000); + }); + this.waitUntilVisible('#notification_test', function () { + this.test.assertEquals(widget_message('test'), 'test timeout', 'timeout: message is correct'); + }); + this.waitWhileVisible('#notification_test', function () { + this.test.assertEquals(widget_message('test'), '', 'timeout: message was cleared'); + }); + + // test click callback + this.thenEvaluate(function () { + var tnw = IPython.notification_area.widget('test'); + tnw._clicked = false; + tnw.set_message('test click', undefined, function () { + tnw._clicked = true; + return true; + }); + }); + this.waitUntilVisible('#notification_test', function () { + this.test.assertEquals(widget_message('test'), 'test click', 'callback: message is correct'); + this.click('#notification_test'); + }); + this.waitFor(function () { + return this.evaluate(function () { + return IPython.notification_area.widget('test')._clicked; + }); + }, function () { + this.waitWhileVisible('#notification_test', function () { + this.test.assertEquals(widget_message('test'), '', 'callback: message was cleared'); + }); + }); +});