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');
+ });
+ });
+});