File size: 6,341 Bytes
fdaf774
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
/*

*************************
Display Web Notifications
*************************

Add this file to $(ipython locate)/nbextensions/

*/

define([
  "jquery",
  "base/js/namespace",
  "require",
], function ($, Jupyter, requirejs) {
  "use strict";

  var params = {
    sticky: false,
    play_sound: false
  };
  var audio_file = "./notify.mp3";

  var current_time = function() {
    return new Date().getTime() / 1000;
  };

  var start_time = current_time();
  var min_time = 0;
  var enabled = false;
  var first_start = true;
  var busy_kernel = true;

  var add_permissions_list = function () {
    var ipython_toolbar = $('#maintoolbar-container');
    var label = $('<span/>').addClass("navbar-text permissions-list").text('Notify:');
    var select = $('<select/>')
                  .attr('id', 'permissions-select')
                  .attr('class', 'permissions-list form-control select-xs')
                  .append($('<option/>')
                  .attr('value', 'Disabled')
                  .text('Disabled'));
    ipython_toolbar.append(label).append(select);
    select.change(function() {
      var val = $(this).val();
      if (val == 'Disabled') {
        enabled = false;
      } else {
        enabled = true;
        min_time = val;
      }
      save_state();
    });
    // Add options in addition to the default, 'Disabled'
    // Options give the minimum kernel busy time in seconds after which a notification is displayed
    var presets = [0, 5, 10, 30];
    for (var i=0; i<presets.length; i++) {
      var name = presets[i];
      select.append($('<option/>').attr('value', name).text(name));
    }
    // Finally, restore the selected option if it was saved in notebook metadata
    restore_state();
  };

  var add_permissions_button = function () {
    if ($("#permissions-button").length === 0) {
      $(Jupyter.toolbar.add_buttons_group([
        Jupyter.keyboard_manager.actions.register ({
          'help'   : 'Grant Notification Permissions',
          'icon'   : 'fa-check',
          'handler': ask_permission,
        },'grant-notifications-permission', 'notify')
      ])).find('.btn').attr('id', 'permissions-button');
    }
  };

  var ensure_permission = function () {
    ask_permission();  // Asks for permission on notebook load, doesn't work in Chrome
    // If don't have permission now, add a button to the toolbar to let user request permission
    if (Notification && Notification.permission !== "granted") {
      add_permissions_button();
      add_permissions_list();
      $(".permissions-list").hide();
    } else if (Notification && Notification.permission === "granted") {
      add_permissions_list();
    }
  };

  var ask_permission = function () {
    if (Notification && Notification.permission !== "granted") {
      Notification.requestPermission(function (status) {
        if (Notification.permission !== status) {
          Notification.permission = status;
        }
        // Wait for permission to be granted, then remove the permissions-button and show permissions-list
        if (Notification && Notification.permission === "granted" && $("#permissions-button").length > 0) {
          $("#permissions-button").remove();
          $(".permissions-list").show();
        }
      });
    }
  };

  var play_notification_sound = function(opts) {
    /**
     * NB: the Web Notification API specifies a mechanism for playing sound
     * with notifications. As of 2017-08-22, it is unsupported in all browsers.
     * This is a workaround. It should be updated to an implementation like
     * this when browser support is available:
     *
     *   opts["sound"] = requirejs.toUrl(audio_file);
     */
    try {
      var audio = new Audio(requirejs.toUrl(audio_file));
      audio.play();
    } catch(e) {
      console.log('HTML5 Audio not supported in browser.');
    }
  };

  var notify = function () {
    var elapsed_time = current_time() - start_time;
    if (enabled && !first_start && !busy_kernel && elapsed_time >= min_time) {
      var opts = {
        body: "Kernel is now idle\n(ran for " + Math.round(elapsed_time) + " secs)",
        icon: Jupyter.notebook.base_url + "static/base/images/favicon.ico",
        requireInteraction: params.sticky
      };
      if (params.play_sound) {
        play_notification_sound(opts);
      }
      var n = new Notification(Jupyter.notebook.notebook_name, opts);
      n.onclick = function(event){ window.focus(); }
    }
    if (first_start) {
      first_start = false;
    }
  };

  var load_state = function () {
    if (!Jupyter.notebook) return;

    if ("notify_time" in Jupyter.notebook.metadata) {
      min_time = Jupyter.notebook.metadata.notify_time;
      enabled = true;
    }
  };

  var save_state = function () {
    if (enabled) {
      if (Jupyter.notebook.metadata.notify_time !== min_time) {
        Jupyter.notebook.metadata.notify_time = min_time;
        Jupyter.notebook.set_dirty();
      }
    } else {
      if (Jupyter.notebook.metadata.hasOwnProperty('notify_time')) {
        delete Jupyter.notebook.metadata.notify_time;
        Jupyter.notebook.set_dirty();
      }
    }
  };

  var restore_state = function () {
    load_state();
    // Only proceed if the permissions selector is being shown
    if ($("#permissions-select").length > 0) {
      if (!enabled) {
        $("#permissions-select").val("Disabled");
      } else {
        $("#permissions-select").val(min_time);
      }
    }
  };

  var setup_notifier = function () {
    $([Jupyter.events]).on('kernel_starting.Kernel',function () {
      first_start = true;  // reset first_start status when restarting the kernel
    });

    $([Jupyter.events]).on('kernel_busy.Kernel',function () {
      busy_kernel = true;
      start_time = current_time();  // reset the timer
    });

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

  var load_ipython_extension = function () {
    return Jupyter.notebook.config.loaded.then(function() {
      $.extend(true, params, Jupyter.notebook.config.data.notify);
      ensure_permission();
      setup_notifier();
    });
  };

  return {
    load_ipython_extension : load_ipython_extension
  };

});