Skip to content

Commit 3b74c45

Browse files
authored
Merge pull request #1053 from brenns10/master
Add sound, sticky options to notify plugin
2 parents 3a497b0 + 42c28ac commit 3b74c45

File tree

4 files changed

+77
-18
lines changed

4 files changed

+77
-18
lines changed

src/jupyter_contrib_nbextensions/nbextensions/notify/notify.js

Lines changed: 55 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,19 @@ Add this file to $(ipython locate)/nbextensions/
88
99
*/
1010

11-
define(["require"], function (require) {
11+
define([
12+
"jquery",
13+
"base/js/namespace",
14+
"require",
15+
], function ($, Jupyter, require) {
1216
"use strict";
1317

18+
var params = {
19+
sticky: false,
20+
play_sound: false
21+
};
22+
var audio_file = "./notify.mp3";
23+
1424
var current_time = function() {
1525
return new Date().getTime() / 1000;
1626
};
@@ -54,7 +64,7 @@ define(["require"], function (require) {
5464

5565
var add_permissions_button = function () {
5666
if ($("#permissions-button").length === 0) {
57-
IPython.toolbar.add_buttons_group([
67+
Jupyter.toolbar.add_buttons_group([
5868
{
5969
'label' : 'Grant Notification Permissions',
6070
'icon' : 'fa-check',
@@ -92,10 +102,35 @@ define(["require"], function (require) {
92102
}
93103
};
94104

105+
var play_notification_sound = function(opts) {
106+
/**
107+
* NB: the Web Notification API specifies a mechanism for playing sound
108+
* with notifications. As of 2017-08-22, it is unsupported in all browsers.
109+
* This is a workaround. It should be updated to an implementation like
110+
* this when browser support is available:
111+
*
112+
* opts["sound"] = require.toUrl(audio_file);
113+
*/
114+
try {
115+
var audio = new Audio(require.toUrl(audio_file));
116+
audio.play();
117+
} catch(e) {
118+
console.log('HTML5 Audio not supported in browser.');
119+
}
120+
};
121+
95122
var notify = function () {
96123
var elapsed_time = current_time() - start_time;
97124
if (enabled && !first_start && !busy_kernel && elapsed_time >= min_time) {
98-
var n = new Notification(IPython.notebook.notebook_name, {body: "Kernel is now idle\n(ran for " + Math.round(elapsed_time) + " secs)"});
125+
var opts = {
126+
body: "Kernel is now idle\n(ran for " + Math.round(elapsed_time) + " secs)",
127+
icon: Jupyter.notebook.base_url + "static/base/images/favicon.ico",
128+
requireInteraction: params.sticky
129+
};
130+
if (params.play_sound) {
131+
play_notification_sound(opts);
132+
}
133+
var n = new Notification(Jupyter.notebook.notebook_name, opts);
99134
n.onclick = function(event){ window.focus(); }
100135
}
101136
if (first_start) {
@@ -104,24 +139,24 @@ define(["require"], function (require) {
104139
};
105140

106141
var load_state = function () {
107-
if (!IPython.notebook) return;
142+
if (!Jupyter.notebook) return;
108143

109-
if ("notify_time" in IPython.notebook.metadata) {
110-
min_time = IPython.notebook.metadata.notify_time;
144+
if ("notify_time" in Jupyter.notebook.metadata) {
145+
min_time = Jupyter.notebook.metadata.notify_time;
111146
enabled = true;
112147
}
113148
};
114149

115150
var save_state = function () {
116151
if (enabled) {
117-
if (IPython.notebook.metadata.notify_time !== min_time) {
118-
IPython.notebook.metadata.notify_time = min_time;
119-
IPython.notebook.set_dirty();
152+
if (Jupyter.notebook.metadata.notify_time !== min_time) {
153+
Jupyter.notebook.metadata.notify_time = min_time;
154+
Jupyter.notebook.set_dirty();
120155
}
121156
} else {
122-
if (IPython.notebook.metadata.hasOwnProperty('notify_time')) {
123-
delete IPython.notebook.metadata.notify_time;
124-
IPython.notebook.set_dirty();
157+
if (Jupyter.notebook.metadata.hasOwnProperty('notify_time')) {
158+
delete Jupyter.notebook.metadata.notify_time;
159+
Jupyter.notebook.set_dirty();
125160
}
126161
}
127162
};
@@ -139,24 +174,27 @@ define(["require"], function (require) {
139174
};
140175

141176
var setup_notifier = function () {
142-
$([IPython.events]).on('kernel_starting.Kernel',function () {
177+
$([Jupyter.events]).on('kernel_starting.Kernel',function () {
143178
first_start = true; // reset first_start status when restarting the kernel
144179
});
145180

146-
$([IPython.events]).on('kernel_busy.Kernel',function () {
181+
$([Jupyter.events]).on('kernel_busy.Kernel',function () {
147182
busy_kernel = true;
148183
start_time = current_time(); // reset the timer
149184
});
150185

151-
$([IPython.events]).on('kernel_idle.Kernel',function () {
186+
$([Jupyter.events]).on('kernel_idle.Kernel',function () {
152187
busy_kernel = false; // Used to make sure that kernel doesn't go busy again within the timeout set below.
153188
setTimeout(notify, 500);
154189
});
155190
};
156191

157192
var load_ipython_extension = function () {
158-
ensure_permission();
159-
setup_notifier();
193+
return Jupyter.notebook.config.loaded.then(function() {
194+
$.extend(true, params, Jupyter.notebook.config.data.notify);
195+
ensure_permission();
196+
setup_notifier();
197+
});
160198
};
161199

162200
return {
Binary file not shown.

src/jupyter_contrib_nbextensions/nbextensions/notify/notify.yaml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,12 @@ Link: readme.md
77
Icon: notification.png
88
Main: notify.js
99
Compatibility: 4.x, 5.x
10+
Parameters:
11+
- name: notify.sticky
12+
description: Require interactions on notifications to dismiss them. (Chrome only)
13+
input_type: checkbox
14+
default: false
15+
- name: notify.play_sound
16+
description: Play notification sound.
17+
input_type: checkbox
18+
default: false

src/jupyter_contrib_nbextensions/nbextensions/notify/readme.md

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,24 @@ trigger a notification (e.g. if selecting 5, a notification will only be shown
1616
if the kernel was busy for more than 5 seconds). The selection is saved in the
1717
notebook's metadata and restored when the notebook is re-opened.
1818

19+
You may configure the plugin so that notifications require manual dismissal
20+
before disappearing. Browser support is limited, see
21+
[here](https://developer.mozilla.org/en-US/docs/Web/API/notification/requireInteraction)
22+
to check if your browser supports this. You may also configure the plugin so
23+
that notifications play a sound.
24+
1925
![notification](notification.png "notification")
2026

2127

2228
## Original Source
2329
This extension originally comes from [@sjpfenniger](https://github.com/sjpfenninger)'s [GitHub repository](https://github.com/sjpfenninger/ipython-extensions).
2430

31+
## Credits
32+
33+
This extension contains sounds created by RSilveira_88 on fresound.org, licensed
34+
under the CC-BY 3.0 License. Modifications by morrisjim. You may find the
35+
modified version [here](http://freesound.org/people/morrisjm/sounds/268756/) and
36+
the original [here](http://freesound.org/people/RSilveira_88/sounds/216306/).
2537

2638
## License
2739

@@ -33,4 +45,4 @@ Permission is hereby granted, free of charge, to any person obtaining a copy of
3345

3446
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
3547

36-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
48+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

0 commit comments

Comments
 (0)