0-hero commited on
Commit
01cd082
1 Parent(s): 23baf50

Add files using upload-large-folder tool

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. .cache/pip/http-v2/0/4/1/8/c/0418c83b80f7f7bfaec2738bfbbee53d2c1562196c0781702f6eddc8 +0 -0
  2. .cache/pip/http-v2/0/4/1/8/c/0418c83b80f7f7bfaec2738bfbbee53d2c1562196c0781702f6eddc8.body +0 -0
  3. .cache/pip/http-v2/0/7/5/9/3/07593bb905dded4b84aacb1d96c1e64704669d6bab658dcaeab79c36 +0 -0
  4. .cache/pip/http-v2/0/c/2/1/8/0c218d526767991766d9b365a4f3e4449ce3579fd8b0f96a707d5a52 +0 -0
  5. .cache/pip/http-v2/0/c/2/1/8/0c218d526767991766d9b365a4f3e4449ce3579fd8b0f96a707d5a52.body +0 -0
  6. .cache/pip/http-v2/0/c/f/6/e/0cf6e817e2c5554000c735ecab0f3cf492f7d33b50d5a474a801ba24 +0 -0
  7. .cache/pip/http-v2/0/c/f/6/e/0cf6e817e2c5554000c735ecab0f3cf492f7d33b50d5a474a801ba24.body +0 -0
  8. .cache/pip/http-v2/6/1/4/f/4/614f46c6d1c16fa5b0800dfd0497e41c5b320e16ee8c9d943d4dd341.body +0 -0
  9. .cache/pip/http-v2/6/1/6/7/8/61678d682a1ea716fb4acccebc1350da197d2251a96e4b9220061051 +0 -0
  10. .cache/pip/http-v2/8/7/7/1/9/87719c54152648e1f5dd42370f81c27a1523da9b52543b783f02ec41 +0 -0
  11. .cache/pip/http-v2/8/7/7/1/9/87719c54152648e1f5dd42370f81c27a1523da9b52543b783f02ec41.body +0 -0
  12. .cache/pip/http-v2/8/9/8/0/7/8980772ed68d7fbcfe2ef2b3f1911b9bd45462ad1becf93269532743.body +0 -0
  13. .local/share/jupyter/nbextensions/codefolding/codefolding_firstline_unfolded.png +0 -0
  14. .local/share/jupyter/nbextensions/collapsible_headings/main.js +1092 -0
  15. .local/share/jupyter/nbextensions/collapsible_headings/readme.md +125 -0
  16. .local/share/jupyter/nbextensions/comment-uncomment/icon.png +0 -0
  17. .local/share/jupyter/nbextensions/comment-uncomment/readme.md +11 -0
  18. .local/share/jupyter/nbextensions/contrib_nbextensions_help_item/contrib_nbextensions_help_item.yaml +7 -0
  19. .local/share/jupyter/nbextensions/datestamper/icon.png +0 -0
  20. .local/share/jupyter/nbextensions/equation-numbering/info.yaml +7 -0
  21. .local/share/jupyter/nbextensions/equation-numbering/main.js +39 -0
  22. .local/share/jupyter/nbextensions/execute_time/ExecuteTime.css +6 -0
  23. .local/share/jupyter/nbextensions/execute_time/ExecuteTime.js +349 -0
  24. .local/share/jupyter/nbextensions/execute_time/ExecuteTime.yaml +88 -0
  25. .local/share/jupyter/nbextensions/execute_time/execution-timings-box.png +0 -0
  26. .local/share/jupyter/nbextensions/execute_time/icon.png +0 -0
  27. .local/share/jupyter/nbextensions/execute_time/readme.md +206 -0
  28. .local/share/jupyter/nbextensions/execution_dependencies/README.md +43 -0
  29. .local/share/jupyter/nbextensions/execution_dependencies/execution_dependencies.js +139 -0
  30. .local/share/jupyter/nbextensions/exercise/history.md +26 -0
  31. .local/share/jupyter/nbextensions/exercise/icon.png +0 -0
  32. .local/share/jupyter/nbextensions/exercise/image.gif +0 -0
  33. .local/share/jupyter/nbextensions/exercise/main.js +169 -0
  34. .local/share/jupyter/nbextensions/exercise2/icon.png +0 -0
  35. .local/share/jupyter/nbextensions/exercise2/image.gif +0 -0
  36. .local/share/jupyter/nbextensions/exercise2/main.css +60 -0
  37. .local/share/jupyter/nbextensions/exercise2/main.js +169 -0
  38. .local/share/jupyter/nbextensions/export_embedded/export_embedded.yaml +7 -0
  39. .local/share/jupyter/nbextensions/export_embedded/main.js +56 -0
  40. .local/share/jupyter/nbextensions/export_embedded/readme.md +7 -0
  41. .local/share/jupyter/nbextensions/freeze/config.yaml +20 -0
  42. .local/share/jupyter/nbextensions/freeze/icon.png +0 -0
  43. .local/share/jupyter/nbextensions/freeze/main.js +205 -0
  44. .local/share/jupyter/nbextensions/freeze/readme.md +24 -0
  45. .local/share/jupyter/nbextensions/gist_it/gist_it.yaml +18 -0
  46. .local/share/jupyter/nbextensions/gist_it/icon.png +0 -0
  47. .local/share/jupyter/nbextensions/gist_it/main.js +481 -0
  48. .local/share/jupyter/nbextensions/go_to_current_running_cell/README.md +22 -0
  49. .local/share/jupyter/nbextensions/go_to_current_running_cell/eye.png +0 -0
  50. .local/share/jupyter/nbextensions/go_to_current_running_cell/go_to_current_running_cell.yaml +26 -0
.cache/pip/http-v2/0/4/1/8/c/0418c83b80f7f7bfaec2738bfbbee53d2c1562196c0781702f6eddc8 ADDED
Binary file (1.82 kB). View file
 
.cache/pip/http-v2/0/4/1/8/c/0418c83b80f7f7bfaec2738bfbbee53d2c1562196c0781702f6eddc8.body ADDED
Binary file (147 kB). View file
 
.cache/pip/http-v2/0/7/5/9/3/07593bb905dded4b84aacb1d96c1e64704669d6bab658dcaeab79c36 ADDED
Binary file (1.81 kB). View file
 
.cache/pip/http-v2/0/c/2/1/8/0c218d526767991766d9b365a4f3e4449ce3579fd8b0f96a707d5a52 ADDED
Binary file (1.82 kB). View file
 
.cache/pip/http-v2/0/c/2/1/8/0c218d526767991766d9b365a4f3e4449ce3579fd8b0f96a707d5a52.body ADDED
Binary file (43.7 kB). View file
 
.cache/pip/http-v2/0/c/f/6/e/0cf6e817e2c5554000c735ecab0f3cf492f7d33b50d5a474a801ba24 ADDED
Binary file (1.81 kB). View file
 
.cache/pip/http-v2/0/c/f/6/e/0cf6e817e2c5554000c735ecab0f3cf492f7d33b50d5a474a801ba24.body ADDED
Binary file (7.87 kB). View file
 
.cache/pip/http-v2/6/1/4/f/4/614f46c6d1c16fa5b0800dfd0497e41c5b320e16ee8c9d943d4dd341.body ADDED
Binary file (32.2 kB). View file
 
.cache/pip/http-v2/6/1/6/7/8/61678d682a1ea716fb4acccebc1350da197d2251a96e4b9220061051 ADDED
Binary file (1.81 kB). View file
 
.cache/pip/http-v2/8/7/7/1/9/87719c54152648e1f5dd42370f81c27a1523da9b52543b783f02ec41 ADDED
Binary file (1.81 kB). View file
 
.cache/pip/http-v2/8/7/7/1/9/87719c54152648e1f5dd42370f81c27a1523da9b52543b783f02ec41.body ADDED
Binary file (3.42 kB). View file
 
.cache/pip/http-v2/8/9/8/0/7/8980772ed68d7fbcfe2ef2b3f1911b9bd45462ad1becf93269532743.body ADDED
Binary file (530 kB). View file
 
.local/share/jupyter/nbextensions/codefolding/codefolding_firstline_unfolded.png ADDED
.local/share/jupyter/nbextensions/collapsible_headings/main.js ADDED
@@ -0,0 +1,1092 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ (requirejs.specified('base/js/namespace') ? define : function (deps, callback) {
2
+ // if here, the Jupyter namespace hasn't been specified to be loaded.
3
+ // This means that we're probably embedded in a page, so we need to make
4
+ // our definition with a specific module name
5
+ "use strict";
6
+ return define('nbextensions/collapsible_headings/main', deps, callback);
7
+ })(['jquery', 'require'], function ($, requirejs) {
8
+ "use strict";
9
+
10
+ var mod_name = 'collapsible_headings';
11
+ var log_prefix = '[' + mod_name + ']';
12
+ var action_names = { // set on registration
13
+ insert_above: '',
14
+ insert_below: '',
15
+ collapse: '',
16
+ uncollapse: '',
17
+ select: ''
18
+ };
19
+ var select_reveals = true; // used as a flag to prevent selecting a heading section from also opening it
20
+
21
+ // define default values for config parameters
22
+ var params = {
23
+ add_button : false,
24
+ add_all_cells_button: false,
25
+ add_insert_header_buttons: false,
26
+ use_toggle_controls : true,
27
+ make_toggle_controls_buttons : false,
28
+ size_toggle_controls_by_level : true,
29
+ toggle_open_icon : 'fa-caret-down',
30
+ toggle_closed_icon : 'fa-caret-right',
31
+ toggle_color : '#aaaaaa',
32
+ use_shortcuts : true,
33
+ shortcuts: {
34
+ collapse: 'left',
35
+ collapse_all: 'ctrl-shift-left',
36
+ uncollapse: 'right',
37
+ uncollapse_all: 'ctrl-shift-right',
38
+ select: 'shift-right',
39
+ insert_above: 'shift-a',
40
+ insert_below: 'shift-b',
41
+ },
42
+ show_section_brackets : false,
43
+ section_bracket_width : 10,
44
+ show_ellipsis : true,
45
+ select_reveals : true,
46
+ collapse_to_match_toc: false,
47
+ indent_px: 8,
48
+ };
49
+
50
+ // ------------------------------------------------------------------------
51
+ // Jupyter is used when we're in a live notebook, but in non-live notebook
52
+ // settings, it remains undefined.
53
+ // It is declared here to allow us to keep logic for live/nonlive functions
54
+ // together.
55
+ var Jupyter;
56
+ // similarly, in a live notebook, events is the Jupyter global events
57
+ // object, but in a non-live notebook, we must construct our own version
58
+ var events;
59
+ try {
60
+ events = requirejs('base/js/events');
61
+ }
62
+ catch (err) {
63
+ // in non-live notebook, there's no events structure, so we make our own
64
+ if (window.events === undefined) {
65
+ var Events = function () {};
66
+ window.events = $([new Events()]);
67
+ }
68
+ events = window.events;
69
+ }
70
+
71
+ // global flag denoting whether we're in a live notebook or exported html.
72
+ // In a live notebook we operate on Cell instances, in exported html we
73
+ // operate on jQuery collections of '.cell' elements
74
+ var live_notebook = false;
75
+
76
+
77
+ // Some functions providing things akin to Jupyter.notebook methods, but
78
+ // which can work using jQuery collections in place of Cell instances.
79
+
80
+ /**
81
+ * Return all cells in the notebook (or cell elements if notebook not live)
82
+ */
83
+ function _get_cells () {
84
+ return live_notebook ? Jupyter.notebook.get_cells() : $('#notebook-container > .cell');
85
+ }
86
+
87
+ /**
88
+ * Return cell at index index (or cell element if notebook not live)
89
+ */
90
+ function _get_cell_at_index (index) {
91
+ return live_notebook ? Jupyter.notebook.get_cell(index) : $('.cell').eq(index);
92
+ }
93
+
94
+ /**
95
+ * Return the index of the given cell (or cell element if notebook not live)
96
+ */
97
+ function _find_cell_index (cell) {
98
+ return live_notebook ? Jupyter.notebook.find_cell_index(cell) : $(cell).index();
99
+ }
100
+
101
+ // ------------------------------------------------------------------------
102
+
103
+ /**
104
+ * Return the level of nbcell.
105
+ * The cell level is an integer in the range 1-7 inclusive
106
+ *
107
+ * @param {Object} cell Cell instance or jQuery collection of '.cell' elements
108
+ * @return {Integer} cell level
109
+ */
110
+ function get_cell_level (cell) {
111
+ // headings can have a level up to 6, so 7 is used for a non-heading
112
+ var level = 7;
113
+ if (cell === undefined) {
114
+ return level;
115
+ }
116
+ if (live_notebook) {
117
+ if ((typeof(cell) === 'object') && (cell.cell_type === 'markdown')) {
118
+ level = cell.get_text().match(/^#*/)[0].length || level;
119
+ }
120
+ }
121
+ else {
122
+ // the jQuery pseudo-selector :header is useful for us, but is
123
+ // implemented in javascript rather than standard css selectors,
124
+ // which get implemented in native browser code.
125
+ // So we get best performance by using css-native first, then filtering
126
+ var only_child_header = $(cell).find(
127
+ '.inner_cell > .rendered_html > :only-child'
128
+ ).filter(':header');
129
+ if (only_child_header.length > 0) {
130
+ level = Number(only_child_header[0].tagName.substring(1));
131
+ }
132
+ }
133
+ return Math.min(level, 7); // we rely on 7 being max
134
+ }
135
+
136
+ /**
137
+ * Check if a cell is a heading cell.
138
+ *
139
+ * @param {Object} cell Cell instance or jQuery collection of '.cell' elements
140
+ * @return {Boolean}
141
+ */
142
+ function is_heading (cell) {
143
+ return get_cell_level(cell) < 7;
144
+ }
145
+
146
+ /**
147
+ * Check if a heading cell is collapsed.
148
+ *
149
+ * Should in general return false on non-heading cells, but this is
150
+ * dependent on metadata/css classes, so don't rely on it.
151
+ *
152
+ * @param {Object} cell Cell instance or jQuery collection of '.cell' elements
153
+ * @return {Boolean}
154
+ */
155
+ function _is_collapsed (heading_cell) {
156
+ if (live_notebook) {
157
+ return heading_cell.metadata.heading_collapsed === true;
158
+ }
159
+ return $(heading_cell).hasClass('collapsible_headings_collapsed');
160
+ }
161
+
162
+ /**
163
+ * Alter cell so that _is_collapsed called on it will return set_collapsed
164
+ */
165
+ function _set_collapsed (heading_cell, set_collapsed) {
166
+ set_collapsed = set_collapsed !== undefined ? set_collapsed : true;
167
+ if (live_notebook) {
168
+ if (set_collapsed) {
169
+ heading_cell.metadata.heading_collapsed = true;
170
+ }
171
+ else {
172
+ delete heading_cell.metadata.heading_collapsed;
173
+ }
174
+ }
175
+ else {
176
+ $(heading_cell).toggleClass('collapsible_headings_collapsed', set_collapsed);
177
+ }
178
+ return set_collapsed;
179
+ }
180
+
181
+ /**
182
+ * Check if a cell is a collapsed heading cell.
183
+ *
184
+ * @param {Object} cell Cell instance or jQuery collection of '.cell' elements
185
+ * @return {Boolean}
186
+ */
187
+ function is_collapsed_heading (cell) {
188
+ return is_heading(cell) && _is_collapsed(cell);
189
+ }
190
+
191
+ /**
192
+ * Uncollapse any headings which are hiding the cell at index
193
+ *
194
+ * @param {Integer} index - index of cell to reveal
195
+ */
196
+ function reveal_cell_by_index (index) {
197
+ // Restrict the search to cells that are of the same level and lower
198
+ // than the currently selected cell by index.
199
+ var ref_cell = _get_cell_at_index(index);
200
+ // ref_cell may be null, if we've attempted to extend selection beyond
201
+ // the existing cells
202
+ if (!ref_cell) {
203
+ return;
204
+ }
205
+ var pivot_level = get_cell_level(ref_cell);
206
+ var cells = _get_cells();
207
+ while (index > 0 && pivot_level > 1) {
208
+ index--;
209
+ var cell = cells[index];
210
+ var cell_level = get_cell_level(cell);
211
+ if (cell_level < pivot_level) {
212
+ if (is_collapsed_heading(cell)) {
213
+ toggle_heading(cell);
214
+ }
215
+ pivot_level = cell_level;
216
+ }
217
+ }
218
+ }
219
+
220
+ /**
221
+ * Add or remove collapsed/uncollapsed classes & metadata to match the
222
+ * cell's status as a non-heading or collapsed/uncollapsed heading
223
+ *
224
+ * @param {Object} cell Cell instance or jQuery collection of '.cell' elements
225
+ * @return {undefined}
226
+ */
227
+ function update_heading_cell_status (cell) {
228
+ var level = get_cell_level(cell);
229
+ var cell_is_heading = level < 7;
230
+ var cell_elt = live_notebook ? cell.element : $(cell);
231
+ var cht = cell_elt.find('.input_prompt > .collapsible_headings_toggle');
232
+ if (cell_is_heading) {
233
+ var collapsed = _is_collapsed(cell);
234
+ cell_elt.toggleClass('collapsible_headings_collapsed', collapsed);
235
+ cell_elt.toggleClass('collapsible_headings_ellipsis', params.show_ellipsis);
236
+ if (params.use_toggle_controls) {
237
+ if (cht.length < 1) {
238
+ cht = $('<div/>')
239
+ .addClass('collapsible_headings_toggle')
240
+ .css('color', params.toggle_color)
241
+ .append('<div><i class="fa fa-fw"></i></div>')
242
+ .appendTo(cell_elt.find('.input_prompt'));
243
+ var clickable = cht.find('i');
244
+ if (params.make_toggle_controls_buttons) {
245
+ cht.addClass('btn btn-default');
246
+ clickable = cht;
247
+ }
248
+ if (live_notebook) {
249
+ clickable.on('click', function () { toggle_heading(cell); });
250
+ }
251
+ else {
252
+ // in non-live notebook, cell isn;t editable, so make it clickable also
253
+ var only_child_header = cell_elt.find(
254
+ '.inner_cell > .rendered_html > :only-child'
255
+ ).filter(':header');
256
+ clickable.add(only_child_header)
257
+ .css('cursor', 'pointer')
258
+ .on('click', function (evt) {
259
+ // evt.target is what was clicked, not what the handler was attached to
260
+ if (!$(evt.target).hasClass('anchor-link')) {
261
+ toggle_heading(cell);
262
+ }
263
+ });
264
+ }
265
+ }
266
+ // Update the cell's toggle control classes
267
+ var hwrap = cht.children();
268
+ hwrap.find('.fa')
269
+ .toggleClass(params.toggle_closed_icon, collapsed)
270
+ .toggleClass(params.toggle_open_icon, !collapsed);
271
+ if (params.size_toggle_controls_by_level) {
272
+ for (var hh = 1; hh < 7; hh++) {
273
+ hwrap.toggleClass('h' + hh, hh == level);
274
+ }
275
+ }
276
+ }
277
+ }
278
+ else {
279
+ _set_collapsed(cell, false);
280
+ cell_elt.removeClass('collapsible_headings_collapsed');
281
+ cht.remove();
282
+ }
283
+ }
284
+
285
+ /**
286
+ * find the closest header cell to input cell
287
+ *
288
+ * @param {Object} cell Cell instance or jQuery collection of '.cell' elements
289
+ * @param {Function} a function to filter which header cells can be
290
+ * returned. Should take a notebook cell/jquer element as
291
+ * input (depending on whether we're in a live notebook),
292
+ * and return true if the given cell is acceptable.
293
+ * @return {Object | undefined}
294
+ */
295
+ function find_header_cell (cell, test_func) {
296
+ var index = _find_cell_index(cell);
297
+ for (; index >= 0; index--) {
298
+ cell = _get_cell_at_index(index);
299
+ if (is_heading(cell) && (test_func === undefined || test_func(cell))) {
300
+ return cell;
301
+ }
302
+ }
303
+ return undefined;
304
+ }
305
+
306
+ /**
307
+ * Select the section enclosed by the given heading cell.
308
+ *
309
+ * Only callable from a live notebook, so require no special cell handling
310
+ *
311
+ * @param {Object} head_cell Cell instance or jQuery collection of '.cell' elements
312
+ * @return {undefined}
313
+ */
314
+ function select_heading_section(head_cell, extend) {
315
+ var head_lvl = get_cell_level(head_cell);
316
+ var ncells = Jupyter.notebook.ncells();
317
+ var head_ind = _find_cell_index(head_cell);
318
+ var tail_ind;
319
+ for (tail_ind = head_ind; tail_ind + 1 < ncells; tail_ind++) {
320
+ if (get_cell_level(_get_cell_at_index(tail_ind + 1)) <= head_lvl) {
321
+ break;
322
+ }
323
+ }
324
+ select_reveals = params.select_reveals;
325
+ if (extend) {
326
+ var ank_ind = Jupyter.notebook.get_anchor_index();
327
+ if (ank_ind <= head_ind) {
328
+ // keep current anchor, extend to head
329
+ Jupyter.notebook.select(tail_ind, false);
330
+ select_reveals = true;
331
+ return;
332
+ }
333
+ else if (ank_ind >= tail_ind) {
334
+ // keep current anchor, extend to tail
335
+ Jupyter.notebook.select(head_ind, false);
336
+ select_reveals = true;
337
+ return;
338
+ }
339
+ // head_ind < ank_ind < tail_ind i.e. anchor is inside section
340
+ }
341
+ // move_anchor to header cell
342
+ Jupyter.notebook.select(head_ind, true);
343
+ // don't move anchor, i.e. extend, to tail cell
344
+ Jupyter.notebook.select(tail_ind, false);
345
+ select_reveals = true;
346
+ }
347
+
348
+ /**
349
+ * Return all of the cell _elements _which are part of the section headed by
350
+ * the given cell
351
+ *
352
+ * @param {Object} head_cell Cell instance or jQuery collection of '.cell' elements
353
+ */
354
+ function get_jquery_bracket_section (head_cell) {
355
+ var head_lvl = get_cell_level(head_cell);
356
+ var cells = _get_cells();
357
+ var cell_elements = $(live_notebook ? head_cell.element : head_cell);
358
+ for (var ii = _find_cell_index(head_cell); ii < cells.length; ii++) {
359
+ var cell = live_notebook ? cells[ii] : cells.eq(ii);
360
+
361
+ if (get_cell_level(cell) <= head_lvl) {
362
+ break;
363
+ }
364
+ cell_elements = cell_elements.add(live_notebook ? cell.element : cell);
365
+ }
366
+ return cell_elements;
367
+ }
368
+
369
+ /**
370
+ * Callback function attached to the bracket-containing div, should toggle
371
+ * the relevant heading
372
+ */
373
+ var bracket_callback_timeout_id;
374
+ function bracket_callback (evt) {
375
+ // prevent bubbling, otherwise when closing a section, the cell gets
376
+ // selected & re-revealed after being hidden
377
+ evt.preventDefault();
378
+ evt.stopPropagation();
379
+ // evt.target is what was clicked, not what the handler was attached to
380
+ var bracket = $(evt.target);
381
+ var bracket_level = Number(bracket.attr('data-bracket-level'));
382
+ if (bracket_level) {
383
+ var bracket_cell = live_notebook ? bracket.closest('.cell').data('cell') : bracket.closest('.cell');
384
+ var header_cell = find_header_cell(bracket_cell, function (cell) {
385
+ return get_cell_level(cell) == bracket_level;
386
+ });
387
+ switch (evt.type) {
388
+ case 'dblclick':
389
+ clearTimeout(bracket_callback_timeout_id);
390
+ bracket_callback_timeout_id = undefined;
391
+ toggle_heading(header_cell);
392
+ break;
393
+ case 'click':
394
+ if (live_notebook && (bracket_callback_timeout_id === undefined)) {
395
+ bracket_callback_timeout_id = setTimeout(function () {
396
+ select_heading_section(header_cell, evt.shiftKey);
397
+ bracket_callback_timeout_id = undefined;
398
+ }, 300);
399
+ }
400
+ break;
401
+ case 'mouseenter':
402
+ case 'mouseleave':
403
+ var in_section = get_jquery_bracket_section(header_cell)
404
+ .find('.chb div[data-bracket-level=' + bracket_level + ']');
405
+ $('.chb div').not(in_section).removeClass('chb-hover');
406
+ in_section.toggleClass('chb-hover', evt.type === 'mouseenter');
407
+ break;
408
+ }
409
+ }
410
+ return false;
411
+ }
412
+
413
+ /**
414
+ * Update the hidden/collapsed status of all the cells under
415
+ * - the notebook, if param cell === undefined
416
+ * - the heading which contains the specified cell (if cell !== undefined,
417
+ * but is also not a heading)
418
+ * - the specified heading cell (if specified cell is a heading)
419
+ *
420
+ * @param {Object} cell Cell instance or jQuery collection of '.cell' elements
421
+ * @return {undefined}
422
+ */
423
+ function update_collapsed_headings (cell) {
424
+ var index = 0;
425
+ var section_level = 0;
426
+ var show = true;
427
+ if (cell !== undefined && (cell = find_header_cell(cell)) !== undefined) {
428
+ index = _find_cell_index(cell) + 1;
429
+ section_level = get_cell_level(cell);
430
+ show = !_is_collapsed(cell);
431
+ }
432
+ var hide_above = 7;
433
+ var brackets_open = {};
434
+ var max_open = 0; // count max number open at one time to calc padding
435
+ for (var cells = _get_cells(); index < cells.length; index++) {
436
+ cell = cells[index];
437
+ var cell_elt = live_notebook ? cell.element : $(cell);
438
+ var level = get_cell_level(cell);
439
+ if (level <= section_level) {
440
+ break;
441
+ }
442
+ if (show && level <= hide_above) {
443
+ cell_elt.slideDown('fast');
444
+ hide_above = is_collapsed_heading(cell) ? level : 7;
445
+ if (live_notebook) {
446
+ delete cell.metadata.hidden;
447
+ }
448
+ }
449
+ else {
450
+ cell_elt.slideUp('fast');
451
+ if (live_notebook) {
452
+ cell.metadata.hidden = true;
453
+ }
454
+ continue;
455
+ }
456
+
457
+ if (params.show_section_brackets) {
458
+ var chb = cell_elt.find('.chb').empty();
459
+ if (chb.length < 1) {
460
+ chb = $('<div/>')
461
+ .addClass('chb')
462
+ .on('click dblclick', bracket_callback)
463
+ .appendTo(cell_elt);
464
+ }
465
+ var num_open = 0; // count number of brackets currently open
466
+ for (var jj = 1; jj < 7; jj++) {
467
+ if (brackets_open[jj] && level <= jj) {
468
+ brackets_open[jj].addClass('chb-end'); // closing, add class
469
+ delete brackets_open[jj]; // closed
470
+ }
471
+ var opening = level == jj;
472
+ if (brackets_open[jj] || opening) {
473
+ num_open++;
474
+ brackets_open[jj] = $('<div/>')
475
+ .on('mouseenter mouseleave', bracket_callback)
476
+ .attr('data-bracket-level', jj)
477
+ .appendTo(chb); // add bracket element
478
+ if (opening) { // opening, add class
479
+ brackets_open[jj].addClass('chb-start');
480
+ }
481
+ }
482
+ }
483
+ max_open = Math.max(num_open, max_open);
484
+ }
485
+ }
486
+ if (params.show_section_brackets) {
487
+ // close any remaining
488
+ for (var ii in brackets_open) {
489
+ brackets_open[ii].addClass('chb-end');
490
+ }
491
+ // adjust padding to fit in brackets
492
+ var bwidth = params.section_bracket_width;
493
+ var dwidth = max_open * (2 + bwidth);
494
+ $('#notebook-container').css('padding-right', (16 + dwidth) + 'px');
495
+ $('.chb')
496
+ .css('right', '-' + (3 + dwidth) + 'px')
497
+ .find('div')
498
+ .css('width', bwidth);
499
+ }
500
+ }
501
+
502
+ /**
503
+ * Hide/reveal all cells in the section headed by cell.
504
+ *
505
+ * @param {Object} cell Cell instance or jQuery collection of '.cell' elements
506
+ */
507
+ function toggle_heading (cell, set_collapsed, trigger_event) {
508
+ if (is_heading(cell)) {
509
+ if (set_collapsed === undefined) {
510
+ set_collapsed = !_is_collapsed(cell);
511
+ }
512
+ _set_collapsed(cell, set_collapsed);
513
+ update_heading_cell_status(cell);
514
+ update_collapsed_headings(params.show_section_brackets ? undefined : cell);
515
+ console.log(log_prefix, set_collapsed ? 'collapsed' : 'expanded', 'cell', _find_cell_index(cell));
516
+ if (trigger_event !== false) {
517
+ events.trigger((set_collapsed ? '' : 'un') + 'collapse.CollapsibleHeading', {cell: cell});
518
+ }
519
+ }
520
+ }
521
+
522
+ /**
523
+ * Return a promise which resolves when the Notebook class methods have
524
+ * been appropriately patched.
525
+ * Patches methods
526
+ * - Notebook.select
527
+ * - Notebook.undelete
528
+ *
529
+ * @return {Promise}
530
+ */
531
+ function patch_Notebook () {
532
+ return new Promise(function (resolve, reject) {
533
+ requirejs(['notebook/js/notebook'], function on_success (notebook) {
534
+ console.debug(log_prefix, 'patching Notebook.protoype');
535
+
536
+ // we have to patch select, since the select.Cell event is only fired
537
+ // by cell click events, not by the notebook select method
538
+ var orig_notebook_select = notebook.Notebook.prototype.select;
539
+ notebook.Notebook.prototype.select = function (index, moveanchor) {
540
+ if (select_reveals) {
541
+ reveal_cell_by_index(index);
542
+ }
543
+ return orig_notebook_select.apply(this, arguments);
544
+ };
545
+ resolve();
546
+ }, reject);
547
+ }).catch(function on_reject (reason) {
548
+ console.warn(log_prefix, 'error patching Notebook.protoype:', reason);
549
+ });
550
+ }
551
+
552
+ /**
553
+ * Return a promise which resolves when the TextCell class methods have
554
+ * been appropriately patched.
555
+ *
556
+ * Patches TextCell.set_text to update headings.
557
+ * This is useful for undelete and copy/paste of cells, which don't fire
558
+ * markdown.
559
+ *
560
+ * @return {Promise}
561
+ */
562
+ function patch_TextCell () {
563
+ return new Promise(function (resolve, reject) {
564
+ requirejs(['notebook/js/textcell'], function on_success (textcell) {
565
+ console.debug(log_prefix, 'patching TextCell.protoype');
566
+ var orig_set_text = textcell.TextCell.prototype.set_text;
567
+ textcell.TextCell.prototype.set_text = function (text) {
568
+ var ret = orig_set_text.apply(this, arguments);
569
+ if (Jupyter.notebook._fully_loaded) {
570
+ update_heading_cell_status(this);
571
+ update_collapsed_headings();
572
+ }
573
+ return ret;
574
+ };
575
+ resolve();
576
+ }, reject);
577
+ }).catch(function on_reject (reason) {
578
+ console.warn(log_prefix, 'error patching TextCell.protoype:', reason);
579
+ });
580
+ }
581
+
582
+ /**
583
+ * Return a promise which resolves when the Tooltip class methods have
584
+ * been appropriately patched.
585
+ *
586
+ * For notebook 4.x, cells had css position:static, and changing them to
587
+ * relative to get heading brackets working broke the tooltip position
588
+ * calculation. In order to fix this, we patch the 4.x Tooltip._show
589
+ * method to temporarily reapply position:static while the tooltip
590
+ * position is calculated & the animation queued, before revertign to the
591
+ * css-appled position:relative.
592
+ * For notebook 5.x, cells are already position:relative, so the patch is
593
+ * unecessary.
594
+ *
595
+ * @return {Promise}
596
+ */
597
+ function patch_Tooltip () {
598
+ if (Number(Jupyter.version[0]) >= 5) {
599
+ return Promise.resolve();
600
+ }
601
+ return new Promise(function (resolve, reject) {
602
+ requirejs(['notebook/js/tooltip'], function on_success (tooltip) {
603
+ console.debug(log_prefix, 'patching Tooltip.prototype');
604
+
605
+ var orig_tooltip__show = tooltip.Tooltip.prototype._show;
606
+ tooltip.Tooltip.prototype._show = function (reply) {
607
+ var $cell = $(this.code_mirror.getWrapperElement()).closest('.cell');
608
+ $cell.css('position', 'static');
609
+ var ret = orig_tooltip__show.apply(this, arguments);
610
+ $cell.css('position', '');
611
+ return ret;
612
+ };
613
+
614
+ resolve();
615
+ }, reject);
616
+ }).catch(function on_reject (reason) {
617
+ console.warn(log_prefix, 'error patching Tooltip.prototype:', reason);
618
+ });
619
+ }
620
+
621
+ /**
622
+ * Return a promise which resolves when the appropriate Jupyter actions
623
+ * have been patched correctly.
624
+ *
625
+ * We patch the up/down arrow actions to skip selecting cells which are
626
+ * hidden by a collapsed heading
627
+ *
628
+ * @return {Promise}
629
+ */
630
+ function patch_actions () {
631
+ return new Promise(function (resolve, reject) {
632
+ requirejs(['notebook/js/tooltip'], function on_success (tooltip) {
633
+ console.debug(log_prefix, 'patching Jupyter up/down actions');
634
+
635
+ var kbm = Jupyter.keyboard_manager;
636
+
637
+ var action_up = kbm.actions.get("jupyter-notebook:select-previous-cell");
638
+ var orig_up_handler = action_up.handler;
639
+ action_up.handler = function (env) {
640
+ for (var index = env.notebook.get_selected_index() - 1; (index !== null) && (index >= 0); index--) {
641
+ if (env.notebook.get_cell(index).element.is(':visible')) {
642
+ env.notebook.select(index);
643
+ env.notebook.focus_cell();
644
+ return;
645
+ }
646
+ }
647
+ return orig_up_handler.apply(this, arguments);
648
+ };
649
+
650
+ var action_down = kbm.actions.get("jupyter-notebook:select-next-cell");
651
+ var orig_down_handler = action_down.handler;
652
+ action_down.handler = function (env) {
653
+ var ncells = env.notebook.ncells();
654
+ for (var index = env.notebook.get_selected_index() + 1; (index !== null) && (index < ncells); index++) {
655
+ if (env.notebook.get_cell(index).element.is(':visible')) {
656
+ env.notebook.select(index);
657
+ env.notebook.focus_cell();
658
+ return;
659
+ }
660
+ }
661
+ return orig_down_handler.apply(this, arguments);
662
+ };
663
+
664
+ resolve();
665
+ }, reject);
666
+ }).catch(function on_reject (reason) {
667
+ console.warn(log_prefix, 'error patching Jupyter up/down actions:', reason);
668
+ });
669
+ }
670
+
671
+ /**
672
+ * register actions to collapse and uncollapse the selected heading cell
673
+ */
674
+ function register_new_actions () {
675
+ action_names.collapse = Jupyter.keyboard_manager.actions.register({
676
+ handler : function (env) {
677
+ var cell = env.notebook.get_selected_cell();
678
+ var is_h = is_heading(cell);
679
+ if (is_h && !_is_collapsed(cell)) {
680
+ toggle_heading(cell, true);
681
+ return;
682
+ }
683
+ var filter_func;
684
+ if (is_h) {
685
+ var lvl = get_cell_level(cell);
686
+ filter_func = function (c) { return get_cell_level(c) < lvl; };
687
+ }
688
+ cell = find_header_cell(cell, filter_func);
689
+ if (cell !== undefined) {
690
+ Jupyter.notebook.select(Jupyter.notebook.find_cell_index(cell));
691
+ cell.focus_cell();
692
+ }
693
+ },
694
+ help : "Collapse the selected heading cell's section",
695
+ icon : params.toggle_closed_icon,
696
+ help_index: 'c1'
697
+ },
698
+ 'collapse_heading', mod_name
699
+ );
700
+
701
+ action_names.collapse_all = Jupyter.keyboard_manager.actions.register({
702
+ handler : function (env) {
703
+ env.notebook.get_cells().forEach(function (c, idx, arr) {
704
+ toggle_heading(c, true);
705
+ });
706
+ var cell = env.notebook.get_selected_cell();
707
+ if (cell.element.is(':hidden')) {
708
+ cell = find_header_cell(cell, function (c) { return c.element.is(':visible'); });
709
+ if (cell !== undefined) {
710
+ Jupyter.notebook.select(Jupyter.notebook.find_cell_index(cell));
711
+ cell.focus_cell();
712
+ }
713
+ }
714
+ },
715
+ help : "Collapse all heading cells' sections",
716
+ icon : params.toggle_closed_icon,
717
+ help_index: 'c2'
718
+ },
719
+ 'collapse_all_headings', mod_name
720
+ );
721
+
722
+ action_names.uncollapse = Jupyter.keyboard_manager.actions.register({
723
+ handler : function (env) {
724
+ var cell = env.notebook.get_selected_cell();
725
+ if (is_heading(cell)) {
726
+ toggle_heading(cell, false);
727
+ }
728
+ else {
729
+ var ncells = env.notebook.ncells();
730
+ for (var ii = env.notebook.find_cell_index(cell); ii < ncells; ii++) {
731
+ cell = env.notebook.get_cell(ii);
732
+ if (is_heading(cell)) {
733
+ env.notebook.select(ii);
734
+ cell.focus_cell();
735
+ break;
736
+ }
737
+ }
738
+ }
739
+ },
740
+ help : "Un-collapse (expand) the selected heading cell's section",
741
+ icon : params.toggle_open_icon,
742
+ help_index: 'c3'
743
+ },
744
+ 'uncollapse_heading', mod_name
745
+ );
746
+
747
+ action_names.uncollapse_all = Jupyter.keyboard_manager.actions.register({
748
+ handler : function (env) {
749
+ env.notebook.get_cells().forEach(function (c, idx, arr) {
750
+ toggle_heading(c, false);
751
+ });
752
+ env.notebook.get_selected_cell().focus_cell();
753
+ },
754
+ help : "Un-collapse (expand) all heading cells' sections",
755
+ icon : params.toggle_open_icon,
756
+ help_index: 'c4'
757
+ },
758
+ 'uncollapse_all_headings', mod_name
759
+ );
760
+
761
+ action_names.toggle = Jupyter.keyboard_manager.actions.register ({
762
+ handler: function () {
763
+ var heading_cell = find_header_cell(Jupyter.notebook.get_selected_cell(), function (cell) {
764
+ return cell.element.is(':visible') && !_is_collapsed(cell);
765
+ });
766
+ if (is_heading(heading_cell)) {
767
+ toggle_heading(heading_cell, true);
768
+ Jupyter.notebook.select(Jupyter.notebook.find_cell_index(heading_cell));
769
+ }
770
+ },
771
+ help : "Toggle closest heading's collapsed status",
772
+ icon : 'fa-angle-double-up',
773
+ },
774
+ 'toggle_collapse_heading', mod_name
775
+ );
776
+
777
+ action_names.toggle_all = Jupyter.keyboard_manager.actions.register ({
778
+ handler: function () {
779
+ var cells = Jupyter.notebook.get_cells();
780
+ for (var ii = 0; ii < cells.length; ii++) {
781
+ if (is_heading(cells[ii])) {
782
+ Jupyter.keyboard_manager.actions.call(action_names[
783
+ is_collapsed_heading(cells[ii]) ? 'uncollapse_all' : 'collapse_all']);
784
+ return;
785
+ }
786
+ }
787
+ },
788
+ help : 'Collapse/uncollapse all headings based on the status of the first',
789
+ icon : 'fa-angle-double-up',
790
+ },
791
+ 'toggle_collapse_all_headings', mod_name
792
+ );
793
+
794
+ action_names.select = Jupyter.keyboard_manager.actions.register({
795
+ handler : function (env) {
796
+ var cell = env.notebook.get_selected_cell();
797
+ if (is_heading(cell)) {
798
+ select_heading_section(cell, true);
799
+ }
800
+ },
801
+ help : "Select all cells in the selected heading cell's section",
802
+ help_index: 'c3'
803
+ },
804
+ 'select_heading_section', mod_name
805
+ );
806
+
807
+ action_names.insert_above = Jupyter.keyboard_manager.actions.register({
808
+ handler : function (env) { insert_heading_cell(true); },
809
+ help : "Insert a heading cell above the selected cell",
810
+ help_index: 'c4',
811
+ icon: 'fa-caret-up'
812
+ },
813
+ 'insert_heading_above', mod_name
814
+ );
815
+
816
+ action_names.insert_below = Jupyter.keyboard_manager.actions.register({
817
+ handler : function (env) { insert_heading_cell(false); },
818
+ help : "Insert a heading cell below the selected cell's section",
819
+ help_index: 'c5',
820
+ icon: 'fa-caret-down'
821
+ },
822
+ 'insert_heading_below', mod_name
823
+ );
824
+ }
825
+
826
+ function imitate_hash_click ($element) {
827
+ var site = $('#site');
828
+ var adjust = $element.offset().top - site.offset().top;
829
+ site.animate({scrollTop: site.scrollTop() + adjust});
830
+ }
831
+
832
+ /**
833
+ * Insert a new heading cell either above or below the current section.
834
+ * only works in a live notebook.
835
+ */
836
+ function insert_heading_cell (above) {
837
+ var selected_cell = Jupyter.notebook.get_selected_cell();
838
+ var ref_cell = find_header_cell(selected_cell) || selected_cell;
839
+ var level = get_cell_level(ref_cell);
840
+ level = (level == 7) ? 1 : level; // default to biggest level (1)
841
+ if (above) {
842
+ // if above, insert just above selected cell, but keep ref_cell's level
843
+ ref_cell = selected_cell;
844
+ }
845
+ var index = ref_cell.element.index();
846
+ if (!above) {
847
+ // below requires special handling, as we really want to put it
848
+ // below the currently selected heading's *content*
849
+ var cells = _get_cells();
850
+ for (index=index + 1; index < cells.length; index++) {
851
+ if (get_cell_level(cells[index]) <= level) {
852
+ break;
853
+ }
854
+ }
855
+ // if we make it here, index will be == cells.length, which is ok
856
+ // as it gets the new cell inserted at the bottom of the notebook
857
+ }
858
+ // we don't want our newly-inserted cell to trigger opening of headings
859
+ var cached_select_reveals = select_reveals;
860
+ select_reveals = false;
861
+ var new_cell = Jupyter.notebook.insert_cell_above('markdown', index);
862
+ var new_text = 'New heading';
863
+ new_cell.set_text(new_text);
864
+ new_cell.set_heading_level(level);
865
+ new_cell.code_mirror.setSelection({line:0, ch: level + 1}, {line:0, ch: level + 1 + new_text.length});
866
+ Jupyter.notebook.select(index, true);
867
+ // restore cached setting
868
+ select_reveals = cached_select_reveals;
869
+ Jupyter.notebook.focus_cell();
870
+ Jupyter.notebook.edit_mode();
871
+ }
872
+
873
+ function refresh_all_headings () {
874
+ var cells = _get_cells();
875
+ for (var ii=0; ii < cells.length; ii++) {
876
+ update_heading_cell_status(cells[ii]);
877
+ }
878
+ update_collapsed_headings();
879
+ }
880
+
881
+ function set_collapsible_headings_options (options) {
882
+ // options may be undefined here, but it's still handled ok by $.extend
883
+ $.extend(true, params, options);
884
+ // bind/unbind toc-collapse handler
885
+ events[params.collapse_to_match_toc ? 'on' : 'off']('collapse.Toc uncollapse.Toc', callback_toc_collapse);
886
+ // add css for indents
887
+ if (params.indent_px !== 0) {
888
+ var lines = [];
889
+ for (var hh = 1; hh <= 6; hh++) {
890
+ lines.push(
891
+ '.collapsible_headings_toggle .h' + hh +
892
+ ' { margin-right: ' + ((6 - hh) * params.indent_px) + 'px; }'
893
+ );
894
+ }
895
+ $('<style id="collapsible_headings_indent_css"/>')
896
+ .html(lines.join('\n'))
897
+ .appendTo('head');
898
+ }
899
+ return params;
900
+ }
901
+
902
+ function add_buttons_and_shortcuts () {
903
+ // (Maybe) add buttons to the toolbar
904
+ if (params.add_button) {
905
+ Jupyter.toolbar.add_buttons_group([action_names.toggle]);
906
+ }
907
+ if (params.add_all_cells_button) {
908
+ Jupyter.toolbar.add_buttons_group([action_names.toggle_all]);
909
+ }
910
+ if (params.add_insert_header_buttons) {
911
+ Jupyter.toolbar.add_buttons_group([
912
+ action_names.insert_above, action_names.insert_below
913
+ ],'insert_heading_cell_btns');
914
+ }
915
+ // add hashes
916
+ $('#insert_heading_cell_btns .btn').prepend('# ');
917
+
918
+ // (Maybe) register keyboard shortcuts
919
+ if (params.use_shortcuts) {
920
+ var cmd_shrts = Jupyter.keyboard_manager.command_shortcuts;
921
+ for (var act in action_names) {
922
+ if (action_names.hasOwnProperty(act) && params.shortcuts[act]) {
923
+ cmd_shrts.add_shortcut(params.shortcuts[act], action_names[act]);
924
+ }
925
+ }
926
+ }
927
+ }
928
+
929
+ var callback_toc_collapse = function (evt, data) {
930
+ // use trigger_event false to avoid re-triggering toc2
931
+ toggle_heading(data.cell, evt.type.indexOf('un') < 0, false);
932
+ }
933
+
934
+ /**
935
+ * Return a promise which resolves once event handlers have been bound
936
+ *
937
+ * @return {Promise}
938
+ */
939
+ function bind_events () {
940
+
941
+ // Callbacks bound to the create.Cell event can execute before the cell
942
+ // data has been loaded from JSON.
943
+ // So, we rely on rendered.MarkdownCell event to catch headings from
944
+ // JSON, and the only reason we use create.Cell is to update brackets
945
+ function callback_create_cell (evt, data) {
946
+ if (params.show_section_brackets) {
947
+ update_collapsed_headings();
948
+ }
949
+ }
950
+
951
+ function callback_delete_cell(evt, data) {
952
+ update_collapsed_headings();
953
+ }
954
+
955
+ function callback_markdown_rendered (evt, data) {
956
+ update_heading_cell_status(data.cell);
957
+ // we update all headings to avoid pasted headings ending up hidden
958
+ // by other pre-existing collapsed headings - see
959
+ // https://github.com/ipython-contrib/jupyter_contrib_nbextensions/issues/1082
960
+ // for details
961
+ update_collapsed_headings();
962
+ }
963
+
964
+ return new Promise (function (resolve, reject) {
965
+ requirejs(['base/js/events'], function on_success (events) {
966
+
967
+ // ensure events are detached while notebook loads, in order to
968
+ // speed up loading (otherwise headings are updated for every
969
+ // new cell in the notebook), then reattached when load is
970
+ // complete
971
+ function events_attach () {
972
+ refresh_all_headings();
973
+ events.on('create.Cell', callback_create_cell);
974
+ events.on('delete.Cell', callback_delete_cell);
975
+ events.on('rendered.MarkdownCell', callback_markdown_rendered);
976
+ }
977
+ function events_detach () {
978
+ events.off('create.Cell', callback_create_cell);
979
+ events.off('delete.Cell', callback_delete_cell);
980
+ events.off('rendered.MarkdownCell', callback_markdown_rendered);
981
+ }
982
+
983
+ if (Jupyter.notebook._fully_loaded) {
984
+ events_attach();
985
+ }
986
+ events.on('notebook_loaded.Notebook', events_attach);
987
+ events.on('notebook_loading.Notebook', events_detach);
988
+
989
+ resolve();
990
+ }, reject);
991
+ }).catch(function on_reject (reason) {
992
+ console.warn(log_prefix, 'error binding events:', reason);
993
+ });
994
+ }
995
+
996
+ /**
997
+ * Return a menu list item with a link that calls the specified action
998
+ * name.
999
+ *
1000
+ * @param {String} action_name the name of the action which the menu item
1001
+ * should call
1002
+ * @param {String} menu_item_html the html to use as the link's content
1003
+ * @return {jQuery}
1004
+ */
1005
+ function make_action_menu_item (action_name, menu_item_html) {
1006
+ var act = Jupyter.menubar.actions.get(action_name);
1007
+ var menu_item = $('<li/>');
1008
+ $('<a/>')
1009
+ .html(menu_item_html)
1010
+ .attr({'title' : act.help, 'href' : '#'})
1011
+ .on('click', function (evt) {
1012
+ Jupyter.menubar.actions.call(action_name, evt);
1013
+ })
1014
+ .appendTo(menu_item);
1015
+ return menu_item;
1016
+ }
1017
+
1018
+ /**
1019
+ * Add any new items to the notebook menu
1020
+ */
1021
+ function insert_menu_items () {
1022
+ $('#insert_menu')
1023
+ .append('<li class="divider"/>')
1024
+ .append(make_action_menu_item(action_names.insert_above, 'Insert Heading Above'))
1025
+ .append(make_action_menu_item(action_names.insert_below, 'Insert Heading Below'));
1026
+ }
1027
+
1028
+ /**
1029
+ * Initialize the extension.
1030
+ */
1031
+ function load_jupyter_extension () {
1032
+ // Load css first
1033
+ $('<link/>')
1034
+ .attr({
1035
+ id: 'collapsible_headings_css',
1036
+ rel: 'stylesheet',
1037
+ type: 'text/css',
1038
+ href: requirejs.toUrl('./main.css')
1039
+ })
1040
+ .appendTo('head');
1041
+
1042
+ // ensure Jupyter module is defined before proceeding further
1043
+ new Promise(function (resolve, reject) {
1044
+ requirejs(['base/js/namespace'], function (Jupyter_mod) {
1045
+ live_notebook = true;
1046
+ Jupyter = Jupyter_mod;
1047
+ resolve(Jupyter);
1048
+ }, reject);
1049
+ })
1050
+
1051
+ // load config & update params
1052
+ .then(function (Jupyter) {
1053
+ return Jupyter.notebook.config.loaded.catch(function on_err (reason) {
1054
+ console.warn(log_prefix, 'error loading config:', reason);
1055
+ }).then(function () {
1056
+ // may be undefined, but that's ok.
1057
+ return Jupyter.notebook.config.data.collapsible_headings;
1058
+ });
1059
+ })
1060
+ // set values using resolution val of previous .then
1061
+ .then(set_collapsible_headings_options)
1062
+
1063
+ // apply all promisory things in arbitrary order
1064
+ .then(patch_actions)
1065
+ .then(patch_Notebook)
1066
+ .then(patch_TextCell)
1067
+ .then(patch_Tooltip)
1068
+ .then(bind_events)
1069
+ // finally add user-interaction stuff
1070
+ .then(function () {
1071
+ register_new_actions();
1072
+ insert_menu_items();
1073
+ add_buttons_and_shortcuts();
1074
+ })
1075
+ .catch(function on_reject (reason) {
1076
+ console.error(log_prefix, 'error:', reason);
1077
+ });
1078
+ }
1079
+
1080
+ /**
1081
+ * Export things
1082
+ */
1083
+ return {
1084
+ get_cell_level : get_cell_level,
1085
+ reveal_cell_by_index : reveal_cell_by_index,
1086
+ update_collapsed_headings : update_collapsed_headings,
1087
+ set_collapsible_headings_options : set_collapsible_headings_options,
1088
+ refresh_all_headings: refresh_all_headings,
1089
+ load_jupyter_extension : load_jupyter_extension,
1090
+ load_ipython_extension : load_jupyter_extension
1091
+ };
1092
+ });
.local/share/jupyter/nbextensions/collapsible_headings/readme.md ADDED
@@ -0,0 +1,125 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Collapsible Headings
2
+ ====================
3
+
4
+ Allows notebook to have collapsible sections, separated by headings.
5
+
6
+ Any markdown heading cell (that is, one which begins with 1-6 `#` characters),
7
+ becomes collapsible once rendered.
8
+
9
+ The collapsed/expanded status of the headings is stored in the cell metadata,
10
+ and reloaded on notebook load.
11
+
12
+ ![screenshot](screenshot.png)
13
+
14
+
15
+ Options
16
+ -------
17
+
18
+ The extension offers a few options for how to display and toggle the collapsed
19
+ status of headings, each of which can be enabled, disabled or configured from
20
+ the nbextensions config page:
21
+
22
+ * Command-mode keyboard shortcuts, (enabled by default, and set to left and
23
+ right arrow keys to collapse/expand sections, or go to the previous/next
24
+ heading, plus shift-right to select a heading cell's section, shift-a/b to
25
+ insert a heading above/below the current cell, ctrl-shift-left and
26
+ ctrl-shift-right to collapse/uncollapse all headings).
27
+ Bindings are also configurable from the config page
28
+ * A toggle control in the input prompt area of each heading cell (as seen in
29
+ the screenshot below, enabled by default)
30
+ * Configurable icons and icon color for the toggle control (by default, grey
31
+ right/down carets are used)
32
+ * The option to make the toggle control into a button (by default it's just a
33
+ clickable icon)
34
+ * Mathematica-style grouping brackets around each collapsible section on the
35
+ right of the notebook. Single-clicking a bracket will select all cells in the
36
+ section (hold shift to extend existing selection), while double-clicking the
37
+ bracket toggles the section's collpased/expanded status (disabled by default)
38
+ * Bracket width is configurable, defaults to 10 (px)
39
+ * A gray bracketed ellipsis added to the end of each collapsed heading,
40
+ indicating hidden content (disabled by default)
41
+ * A toolbar button to collapse the nearest heading to the curently selected
42
+ cell (disabled by default)
43
+ * Collapse/uncollapse sections when ToC2 sections are collapsed/uncollapsed
44
+ * A toolbar button to collapse/uncollapse all headings (disabled by default)
45
+ * Shift more-significant headings' collapse controls further to the left
46
+
47
+
48
+ css
49
+ ---
50
+
51
+ The extension add the css class `collapsible_headings_collapsed` to each
52
+ collapsed heading cell, which you could use for custom css rules, such as
53
+ adding a bottom border to collapsed headings, to visually distinguish them a
54
+ bit more.
55
+
56
+ The toggle controls' icons currently spin by 360 degrees when the heading gets
57
+ collapsed or uncollapsed, via a css transition property (not in IE).
58
+ If this annoys you,
59
+ you could turn it off using the following rule in your `custom.css`:
60
+
61
+ ```css
62
+ .cell .collapsible_headings_toggle .fa {
63
+ transition: transform 0s;
64
+ }
65
+ ```
66
+
67
+
68
+ Internals
69
+ ---------
70
+
71
+ Heading cells which are collapsed have a value set in the cell metadata, so
72
+ that
73
+
74
+ ```javascript
75
+ cell.metadata.heading_collapsed = true
76
+ ```
77
+
78
+ The extension patches some Jupyter methods:
79
+ * `TextCell.prototype.execute` is patched to add/remove the toggle buttons,
80
+ as well as update the visibility of any cells below the new one.
81
+ * `Notebook.prototype.select` is patched to make sure any collapsed headings
82
+ which would be hiding the new selection get uncollapsed (expanded).
83
+ * `Notebook.prototype.undelete` and `Notebook.prototype.delete_cells` are
84
+ patched to trigger an update of which cells should be visible or hidden.
85
+ * `Tooltip._show` is patched to toggle the `div.cell { position:relative; }`
86
+ css rule of while the tooltip displays, as otherwise it interferes with the
87
+ tooltip's position-determining logic. Since this method is not part of the
88
+ public API (leading underscore), this may break in future, but it should
89
+ degrade in a non-catastrophic manner, with the result that the tooltip will
90
+ appear at the top of the notebook document, rather than where the cursor is
91
+ currently.
92
+
93
+ The extension also patches two existing Jupyter actions: those triggered in
94
+ command mode by the up/down arrow keys. Ordinarily, these select the cell
95
+ above/below the current selection. Once patched by `collapsible_headings`, they
96
+ have the same behaviour, but skip over any cells which have been hidden (by a
97
+ collapsed heading, or, in fact, by any other mechanism).
98
+
99
+ Finally, `collapsible_headings` registers two new actions, namely
100
+ `collapsible_headings:collapse_heading` and
101
+ `collapsible_headings:uncollapse_heading`, which are used by the keyboard
102
+ shortcuts (if used), and can be called as with any other action.
103
+
104
+ The previously-provided preprocessor has been retired in favour of an exporter
105
+ which embeds functionality correctly. See the [exporting section] for details.
106
+ If you have questions, comments, or would like alterations (particularly for
107
+ nbconvert support, of which I don't have much experience), get in touch
108
+ ([@jcb91](https://github.com/jcb91))
109
+ and I'll see what I can do :)
110
+
111
+
112
+ Exporting
113
+ ---------
114
+
115
+ It is possible to export most of the features of collapsible_headings to html.
116
+ The process is to embed the relevant css & js files into the html output, with
117
+ suitable functionality for a non-live notebook.
118
+
119
+ This is accomplished through use of the `ExporterInliner` class and its
120
+ associated `inliner.tpl` template, provided as part of the
121
+ `jupyter_contrib_nbextensions.nbconvert_support` module.
122
+ To convert to html embedding collapsible headings functionality, use `html_ch`
123
+ exporter, with a command like
124
+
125
+ jupyter nbconvert --to html_ch FILE.ipynb
.local/share/jupyter/nbextensions/comment-uncomment/icon.png ADDED
.local/share/jupyter/nbextensions/comment-uncomment/readme.md ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Comment/Uncomment
2
+ =================
3
+
4
+ Adds a new configurable hotkey binding to toggle comments on/off.
5
+
6
+ Options
7
+ -------
8
+
9
+ **comment_uncomment_keybinding**: keybinding for toggling comments (default: Alt-c)
10
+
11
+ ***comment_uncomment_indent***: add comment at current indent level instead of at beginning of line
.local/share/jupyter/nbextensions/contrib_nbextensions_help_item/contrib_nbextensions_help_item.yaml ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ Type: Jupyter Notebook Extension
2
+ Name: contrib_nbextensions_help_item
3
+ Description: The contrib_nbextensions_help_item is a tiny extension that just adds an item in the notebook's help menu, pointing to the jupyter_contrib_nbextensions at readthedocs.
4
+ Link: README.md
5
+ Icon: icon.png
6
+ Main: main.js
7
+ Compatibility: 4.x, 5.x
.local/share/jupyter/nbextensions/datestamper/icon.png ADDED
.local/share/jupyter/nbextensions/equation-numbering/info.yaml ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ Type: IPython Notebook Extension
2
+ Name: Equation Auto Numbering
3
+ Description: This extension enables equation autonumbering and resetting the equation count.
4
+ Link: readme.md
5
+ Icon: icon.png
6
+ Main: main.js
7
+ Compatibility: 4.x, 5.x
.local/share/jupyter/nbextensions/equation-numbering/main.js ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright (c) IPython-Contrib Team.
2
+ // Distributed under the terms of the Modified BSD License.
3
+
4
+ define([
5
+ 'base/js/namespace',
6
+ 'jquery',
7
+ 'require',
8
+ 'notebook/js/textcell',
9
+ 'base/js/utils',
10
+ ], function(Jupyter, $, requirejs, textcell, utils) {
11
+ "use strict";
12
+
13
+ var MathJax = window.MathJax;
14
+
15
+ var load_ipython_extension = function() {
16
+ var btn_grp = Jupyter.toolbar.add_buttons_group([
17
+ Jupyter.keyboard_manager.actions.register ({
18
+ help : 'Reset equation numbering',
19
+ icon : 'fa-sort-numeric-asc',
20
+ handler: function () {
21
+ MathJax.Hub.Queue(
22
+ ["resetEquationNumbers", MathJax.InputJax.TeX],
23
+ ["PreProcess", MathJax.Hub],
24
+ ["Reprocess", MathJax.Hub]
25
+ );
26
+ $('#reset_numbering').blur();
27
+ }
28
+ }, 'reset-numbering', 'equation_numbering')
29
+ ]);
30
+ $(btn_grp).find('.btn').attr('id', 'reset_numbering');
31
+ MathJax.Hub.Config({
32
+ TeX: { equationNumbers: { autoNumber: "AMS" } }
33
+ });
34
+ };
35
+
36
+ return {
37
+ load_ipython_extension : load_ipython_extension
38
+ };
39
+ });
.local/share/jupyter/nbextensions/execute_time/ExecuteTime.css ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ .timing_area {
2
+ padding: 0 5px;
3
+ border: none;
4
+ border-top: 1px solid #CFCFCF;
5
+ font-size: 80%;
6
+ }
.local/share/jupyter/nbextensions/execute_time/ExecuteTime.js ADDED
@@ -0,0 +1,349 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ // Copyright (C) 2014 Jean-Christophe Jaskula
3
+ // 2015 joshuacookebarnes@gmail.com
4
+ //
5
+ // Distributed under the terms of the BSD License.
6
+ // ---------------------------------------------------------------------------
7
+ //
8
+ // Execution timings:
9
+ // display when a cell was last executed, and how long it took to run
10
+ // A double click on the timing box makes it disappear
11
+
12
+ define([
13
+ 'require',
14
+ 'jquery',
15
+ 'moment',
16
+ 'base/js/namespace',
17
+ 'base/js/events',
18
+ 'notebook/js/codecell'
19
+ ], function (
20
+ requirejs,
21
+ $,
22
+ moment,
23
+ Jupyter,
24
+ events,
25
+ codecell
26
+ ) {
27
+ 'use strict';
28
+
29
+ var mod_name = 'ExecuteTime';
30
+ var log_prefix = '[' + mod_name + ']';
31
+
32
+ var CodeCell = codecell.CodeCell;
33
+
34
+ // defaults, overridden by server's config
35
+ var options = {
36
+ clear_timings_on_clear_output: false,
37
+ clear_timings_on_kernel_restart: false,
38
+ default_kernel_to_utc: true,
39
+ display_absolute_format: 'HH:mm:ss YYYY-MM-DD',
40
+ display_absolute_timings: true,
41
+ display_in_utc: false,
42
+ display_right_aligned: false,
43
+ highlight: {
44
+ use: true,
45
+ color: '#00bb00',
46
+ },
47
+ relative_timing_update_period: 10,
48
+ template: {
49
+ executed: 'executed in ${duration}, finished ${end_time}',
50
+ queued: 'execution queued ${start_time}',
51
+ },
52
+ };
53
+
54
+ function patch_CodeCell_get_callbacks () {
55
+ console.log(log_prefix, 'patching CodeCell.prototype.get_callbacks');
56
+ var old_get_callbacks = CodeCell.prototype.get_callbacks;
57
+ CodeCell.prototype.get_callbacks = function () {
58
+ var callbacks = old_get_callbacks.apply(this, arguments);
59
+
60
+ var cell = this;
61
+ var prev_reply_callback = callbacks.shell.reply;
62
+ callbacks.shell.reply = function (msg) {
63
+ if (msg.msg_type === 'execute_reply') {
64
+ $.extend(true, cell.metadata, {
65
+ ExecuteTime: {
66
+ start_time: add_utc_offset(msg.metadata.started),
67
+ end_time: add_utc_offset(msg.header.date),
68
+ }
69
+ });
70
+ var timing_area = update_timing_area(cell);
71
+ if ($.ui !== undefined && options.highlight.use) {
72
+ timing_area.stop(true, true).show(0).effect('highlight', {color: options.highlight.color});
73
+ }
74
+ }
75
+ else {
76
+ console.log('msg_type', msg.msg_type);
77
+ }
78
+ return prev_reply_callback(msg);
79
+ };
80
+ return callbacks;
81
+ };
82
+ }
83
+
84
+ function patch_CodeCell_clear_output () {
85
+ console.log(log_prefix, 'Patching CodeCell.prototype.clear_output to clear timings also.');
86
+ var orig_clear_output = CodeCell.prototype.clear_output;
87
+ CodeCell.prototype.clear_output = function () {
88
+ var ret = orig_clear_output.apply(this, arguments);
89
+ clear_timing_data([this]);
90
+ return ret;
91
+ };
92
+ }
93
+
94
+ function toggle_timing_display (cells, vis) {
95
+ for (var i = 0; i < cells.length; i++) {
96
+ var cell = cells[i];
97
+ if (cell instanceof CodeCell) {
98
+ var ce = cell.element;
99
+ var timing_area = ce.find('.timing_area');
100
+ if (timing_area.length > 0) {
101
+ if (vis === undefined) {
102
+ vis = !timing_area.is(':visible');
103
+ }
104
+ timing_area.toggle(vis);
105
+ }
106
+ }
107
+ }
108
+ return vis;
109
+ }
110
+
111
+ function clear_timing_data (cells) {
112
+ cells.forEach(function (cell, idx, arr) {
113
+ delete cell.metadata.ExecuteTime;
114
+ cell.element.find('.timing_area').remove();
115
+ });
116
+ events.trigger('set_dirty.Notebook', {value: true});
117
+ }
118
+
119
+ function clear_timing_data_all () {
120
+ console.log(log_prefix, 'Clearing all timing data');
121
+ clear_timing_data(Jupyter.notebook.get_cells());
122
+ }
123
+
124
+ function create_menu () {
125
+ var timings_menu_item = $('<li/>')
126
+ .addClass('dropdown-submenu')
127
+ .append(
128
+ $('<a href="#">')
129
+ .text('Execution Timings')
130
+ .on('click', function (evt) { evt.preventDefault(); })
131
+ )
132
+ .appendTo($('#cell_menu'));
133
+
134
+ var timings_submenu = $('<ul/>')
135
+ .addClass('dropdown-menu')
136
+ .appendTo(timings_menu_item);
137
+
138
+ $('<li/>')
139
+ .attr('title', 'Toggle the timing box for the selected cell(s)')
140
+ .append(
141
+ $('<a href="#">')
142
+ .text('Toggle visibility (selected)')
143
+ .on('click', function (evt) {
144
+ evt.preventDefault();
145
+ toggle_timing_display(Jupyter.notebook.get_selected_cells());
146
+ })
147
+ )
148
+ .appendTo(timings_submenu);
149
+
150
+ $('<li/>')
151
+ .attr('title', 'Toggle the timing box for all cells')
152
+ .append(
153
+ $('<a href="#">')
154
+ .text('Toggle visibility (all)')
155
+ .on('click', function (evt) {
156
+ evt.preventDefault();
157
+ toggle_timing_display(Jupyter.notebook.get_cells());
158
+ })
159
+ )
160
+ .appendTo(timings_submenu);
161
+
162
+ $('<li/>')
163
+ .attr('title', 'Clear the selected cell(s) timing data')
164
+ .append(
165
+ $('<a href="#">')
166
+ .text('Clear (selected)')
167
+ .on('click', function (evt) {
168
+ evt.preventDefault();
169
+ clear_timing_data(Jupyter.notebook.get_selected_cells());
170
+ })
171
+ )
172
+ .appendTo(timings_submenu);
173
+
174
+ $('<li/>')
175
+ .attr('title', 'Clear the timing data from all cells')
176
+ .append(
177
+ $('<a href="#">')
178
+ .text('Clear (all)')
179
+ .on('click', function (evt) {
180
+ evt.preventDefault();
181
+ clear_timing_data(Jupyter.notebook.get_cells());
182
+ })
183
+ )
184
+ .appendTo(timings_submenu);
185
+ }
186
+
187
+ function excute_codecell_callback (evt, data) {
188
+ var cell = data.cell;
189
+ cell.metadata.ExecuteTime = {start_time: moment().toISOString()};
190
+
191
+ update_timing_area(cell);
192
+ }
193
+
194
+ function humanized_duration (duration_ms, item_count) {
195
+ if (duration_ms < 1000) { // < 1s, show ms directly
196
+ return Math.round(duration_ms) + 'ms';
197
+ }
198
+
199
+ var humanized = '';
200
+
201
+ var days = Math.floor(duration_ms / 86400000);
202
+ if (days) {
203
+ humanized += days + 'd ';
204
+ }
205
+ duration_ms %= 86400000;
206
+
207
+ var hours = Math.floor(duration_ms / 3600000);
208
+ if (days || hours) {
209
+ humanized += hours + 'h ';
210
+ }
211
+ duration_ms %= 3600000;
212
+
213
+ var mins = Math.floor(duration_ms / 60000);
214
+ if (days || hours || mins) {
215
+ humanized += mins + 'm';
216
+ }
217
+ duration_ms %= 60000;
218
+
219
+ var secs = duration_ms / 1000; // don't round!
220
+ if (!days) {
221
+ var decimals = (hours || mins > 1) ? 0 : (secs > 10 ? 1 : 2);
222
+ humanized += (humanized ? ' ' : '') + secs.toFixed(decimals) + 's';
223
+ }
224
+
225
+ return humanized;
226
+ }
227
+
228
+ // ISO8601 UTC offset is in format ±[hh]:[mm], ±[hh][mm], or ±[hh]
229
+ var rgx_has_timezone = new RegExp('Z|[\\-+\u2212]\\d\\d(?::?\\d\\d)?$');
230
+ function add_utc_offset (timestamp) {
231
+ if (options.default_kernel_to_utc && timestamp !== undefined && !rgx_has_timezone.test(timestamp)) {
232
+ return timestamp + 'Z';
233
+ }
234
+ return timestamp;
235
+ }
236
+
237
+ function format_moment (when) {
238
+ if (options.display_in_utc) {
239
+ when.utc();
240
+ }
241
+ if (options.display_absolute_timings) {
242
+ return when.format(options.display_absolute_format);
243
+ }
244
+ return when.fromNow();
245
+ }
246
+
247
+ function update_timing_area (cell) {
248
+ if (! (cell instanceof CodeCell) ||
249
+ !cell.metadata.ExecuteTime ||
250
+ !cell.metadata.ExecuteTime.start_time) {
251
+ return $();
252
+ }
253
+
254
+ var timing_area = cell.element.find('.timing_area');
255
+ if (timing_area.length < 1) {
256
+ timing_area = $('<div/>')
257
+ .addClass('timing_area' + (options.display_right_aligned ? ' text-right' : ''))
258
+ .on('dblclick', function (evt) { toggle_timing_display([cell]); })
259
+ .appendTo(cell.element.find('.input_area'));
260
+ }
261
+
262
+ var start_time = moment(cell.metadata.ExecuteTime.start_time),
263
+ end_time = cell.metadata.ExecuteTime.end_time;
264
+ var msg = options.template[end_time ? 'executed' : 'queued'];
265
+ msg = msg.replace('${start_time}', format_moment(start_time));
266
+ if (end_time) {
267
+ end_time = moment(end_time);
268
+ msg = msg.replace('${end_time}', format_moment(end_time));
269
+ var exec_time = -start_time.diff(end_time);
270
+ msg = msg.replace('${duration}', humanized_duration(exec_time));
271
+ }
272
+ timing_area.text(msg);
273
+ return timing_area;
274
+ }
275
+
276
+ function _update_all_timing_areas () {
277
+ Jupyter.notebook.get_cells().forEach(update_timing_area);
278
+ }
279
+
280
+ function update_all_timing_areas () {
281
+ console.debug(log_prefix, 'updating all timing areas');
282
+ _update_all_timing_areas();
283
+ }
284
+
285
+ function add_css(url) {
286
+ $('<link/>')
287
+ .attr({
288
+ rel: 'stylesheet',
289
+ href: requirejs.toUrl(url),
290
+ type: 'text/css'
291
+ })
292
+ .appendTo('head');
293
+ }
294
+
295
+ function load_jupyter_extension () {
296
+ // try to load jquery-ui
297
+ if ($.ui === undefined && options.highlight.use) {
298
+ requirejs(['jquery-ui'], function ($) {}, function (err) {
299
+ // try to load using the older, non-standard name (without hyphen)
300
+ requirejs(['jqueryui'], function ($) {}, function (err) {
301
+ console.log(log_prefix, 'couldn\'t find jquery-ui, so no animations');
302
+ });
303
+ });
304
+ }
305
+
306
+ add_css('./ExecuteTime.css');
307
+
308
+ Jupyter.notebook.config.loaded.then(function on_config_loaded () {
309
+ $.extend(true, options, Jupyter.notebook.config.data[mod_name]);
310
+ }, function on_config_load_error (reason) {
311
+ console.warn(log_prefix, 'Using defaults after error loading config:', reason);
312
+ }).then(function do_stuff_with_config () {
313
+
314
+ patch_CodeCell_get_callbacks();
315
+ events.on('execute.CodeCell', excute_codecell_callback);
316
+
317
+ create_menu();
318
+
319
+ // add any existing timing info
320
+ events.on("notebook_loaded.Notebook", update_all_timing_areas);
321
+ if (Jupyter.notebook !== undefined && Jupyter.notebook._fully_loaded) {
322
+ // notebook already loaded, so we missed the event, so update all
323
+ update_all_timing_areas();
324
+ }
325
+
326
+ // setup optional clear-data calls
327
+ if (options.clear_timings_on_clear_output) {
328
+ patch_CodeCell_clear_output();
329
+ }
330
+ if (options.clear_timings_on_kernel_restart) {
331
+ console.log(log_prefix, 'Binding kernel_restarting.Kernel event to clear timings.');
332
+ events.on('kernel_restarting.Kernel', clear_timing_data_all);
333
+ }
334
+
335
+ // if displaying relative times, update them at intervals
336
+ if (!options.display_absolute_timings) {
337
+ var period_ms = 1000 * Math.max(1, options.relative_timing_update_period);
338
+ setInterval(_update_all_timing_areas, period_ms);
339
+ }
340
+ }).catch(function on_error (reason) {
341
+ console.error(log_prefix, 'Error:', reason);
342
+ });
343
+ }
344
+
345
+ return {
346
+ load_jupyter_extension : load_jupyter_extension,
347
+ load_ipython_extension : load_jupyter_extension
348
+ };
349
+ });
.local/share/jupyter/nbextensions/execute_time/ExecuteTime.yaml ADDED
@@ -0,0 +1,88 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Type: IPython Notebook Extension
2
+ Name: ExecuteTime
3
+ Description: Display when each cell has been executed and how long it took
4
+ Link: readme.md
5
+ Icon: icon.png
6
+ Main: ExecuteTime.js
7
+ Compatibility: 4.x, 5.x
8
+ Parameters:
9
+
10
+ - name: ExecuteTime.clear_timings_on_clear_output
11
+ description: |
12
+ When cells' outputs are cleared, also clear their timing data, e.g. when
13
+ using the "Kernel > Restart & Clear Output" menu item
14
+ input_type: checkbox
15
+ default: false
16
+
17
+ - name: ExecuteTime.clear_timings_on_kernel_restart
18
+ description: |
19
+ Clear all cells' execution timing data on any kernel restart event
20
+ input_type: checkbox
21
+ default: false
22
+
23
+ - name: ExecuteTime.display_absolute_timings
24
+ description: |
25
+ Display absolute timings for the start time of execution.
26
+ Setting false will display a relative timestamp like 'a few seconds ago'
27
+ default: true
28
+ input_type: checkbox
29
+
30
+ - name: ExecuteTime.display_absolute_format
31
+ description: |
32
+ The format to use when displaying absolute timings (see above)
33
+ default: 'YYYY-MM-DD HH:mm:ss'
34
+ input_type: text
35
+
36
+ - name: ExecuteTime.relative_timing_update_period
37
+ description: |
38
+ Seconds to wait between updating the relative timestamps, if using them
39
+ (see above)
40
+ default: 10
41
+ input_type: number
42
+ step: 1
43
+ min: 1
44
+ max: 600
45
+
46
+ - name: ExecuteTime.display_in_utc
47
+ description: |
48
+ Display times in UTC, rather than in the local timezone set by the browser
49
+ default: false
50
+ input_type: checkbox
51
+
52
+ - name: ExecuteTime.default_kernel_to_utc
53
+ description: |
54
+ For kernel timestamps which do not specify a timezone, assume UTC
55
+ default: true
56
+ input_type: checkbox
57
+
58
+ - name: ExecuteTime.display_right_aligned
59
+ description: |
60
+ Right-align the text in the timing area under each cell
61
+ default: false
62
+ input_type: checkbox
63
+
64
+ - name: ExecuteTime.highlight.use
65
+ description: |
66
+ Highlight the displayed execution time on completion of execution
67
+ default: true
68
+ input_type: checkbox
69
+
70
+ - name: ExecuteTime.highlight.color
71
+ description: |
72
+ Color to use for highlighting the displayed execution time
73
+ default: '#00BB00'
74
+ input_type: color
75
+
76
+ - name: ExecuteTime.template.executed
77
+ description: |
78
+ Template for the timing message for executed cells. See readme for
79
+ replacement tokens.
80
+ default: 'executed in ${duration}, finished ${end_time}'
81
+ input_type: text
82
+
83
+ - name: ExecuteTime.template.queued
84
+ description: |
85
+ Template for the timing message for queued cells. See readme for
86
+ replacement tokens.
87
+ default: 'execution queued ${start_time}'
88
+ input_type: text
.local/share/jupyter/nbextensions/execute_time/execution-timings-box.png ADDED
.local/share/jupyter/nbextensions/execute_time/icon.png ADDED
.local/share/jupyter/nbextensions/execute_time/readme.md ADDED
@@ -0,0 +1,206 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Execute Time
2
+ ============
3
+
4
+ This extension displays when the last execution of a code cell occurred, and
5
+ how long it took.
6
+
7
+ Every executed code cell is extended with a new area, attached at the bottom of
8
+ the input area, that displays the time at which the user sent the cell to the
9
+ kernel for execution.
10
+ When the kernel finishes executing the cell, the area is updated with the
11
+ duration of the execution.
12
+ The timing information is stored in the cell metadata, and restored on notebook
13
+ load.
14
+
15
+ ![](execution-timings-box.png)
16
+
17
+
18
+ Toggling display
19
+ ----------------
20
+
21
+ The timing area can be hidden by double clicking on it, or using the
22
+ `Cell -> Toggle timings -> Selected`
23
+ menu item.
24
+ The menu item
25
+ `Cell -> Toggle timings -> All`
26
+ hides (shows) all the timing areas in the notebook, if the first cell is
27
+ currently shown (hidden).
28
+
29
+ ![](execution-timings-menu.png)
30
+
31
+
32
+ Options
33
+ -------
34
+
35
+ The nbextension offers a few options for how to display and interpret
36
+ timestamps.
37
+ Options are stored in the `notebook` section of the server's nbconfig, under
38
+ the key `ExecuteTime`.
39
+ The easiest way to configure these is using the
40
+ [jupyter_nbextensions_configurator](https://github.com/Jupyter-contrib/jupyter_nbextensions_configurator),
41
+ which if you got this nbextension in the usual way from
42
+ [jupyter_contrib_nbextensions](https://github.com/ipython-contrib/jupyter_contrib_nbextensions),
43
+ should also have been installed.
44
+
45
+ Alternatively, you can also configure them directly with a few lines of python.
46
+ For example, to alter the displayed message, use relative timestamps,
47
+ and set them to update every 5 seconds, we can use the following python
48
+ snippet:
49
+
50
+ ```python
51
+ from notebook.services.config import ConfigManager
52
+ ConfigManager().update('notebook', {'ExecuteTime': {
53
+ 'display_absolute_timestamps': False,
54
+ 'relative_timing_update_period': 5,
55
+ 'template': {
56
+ 'executed': 'started ${start_time}, finished in ${duration}',
57
+ }
58
+ }})
59
+ ```
60
+
61
+ The available options are:
62
+
63
+ * `ExecuteTime.clear_timings_on_clear_output`: When cells' outputs are cleared,
64
+ also clear their timing data, e.g. when using the
65
+ `Kernel > Restart & Clear Output` menu item
66
+
67
+ * `ExecuteTime.clear_timings_on_kernel_restart`: Clear all cells' execution
68
+ timing data on any kernel restart event
69
+
70
+ * `ExecuteTime.display_absolute_timings`: Display absolute timings for the
71
+ start/end time of execution. Setting this `false` will result in the display
72
+ of a relative timestamp like 'a few seconds ago' (see the moment.js function
73
+ [fromNow](https://momentjs.com/docs/#/displaying/fromnow/)
74
+ for details). Defaults to `true`.
75
+
76
+ * `ExecuteTime.display_absolute_format`: The format to use when displaying
77
+ absolute timings (see `ExecuteTime.display_absolute_timings`, above).
78
+ See the moment.js function
79
+ [format](https://momentjs.com/docs/#/displaying/format/)
80
+ for details of the template tokens available.
81
+ Defaults to `'YYYY-MM-DD HH:mm:ss'`.
82
+
83
+ * `ExecuteTime.relative_timing_update_period`: Seconds to wait between updating
84
+ the relative timestamps, if using them (see
85
+ `ExecuteTime.display_absolute_timings`, above).
86
+ Defaults to `10`.
87
+
88
+ * `ExecuteTime.display_in_utc`: Display times in UTC, rather than in the local
89
+ timezone set by the browser.
90
+ Defaults to `false`.
91
+
92
+ * `ExecuteTime.default_kernel_to_utc`: For kernel timestamps which do not
93
+ specify a timezone, assume UTC.
94
+ Defaults to `true`.
95
+
96
+ * `ExecuteTime.display_right_aligned`: Right-align the text in the timing area
97
+ under each cell.
98
+ Defaults to `false`.
99
+
100
+ * `ExecuteTime.highlight.use`: Highlight the displayed execution time on
101
+ completion of execution.
102
+ Defaults to `true`.
103
+
104
+ * `ExecuteTime.highlight.color`: Color to use for highlighting the displayed
105
+ execution time.
106
+ Defaults to `'#00BB00'`.
107
+
108
+ * `ExecuteTime.template.executed`: Template for the timing message for executed
109
+ cells. See readme for replacement tokens.
110
+ Defaults to `'executed in ${duration}, finished ${end_time}'`.
111
+
112
+ * `ExecuteTime.template.queued`: Template for the timing message for queued
113
+ cells. The template uses an ES2015-like syntax, but replaces only the exact
114
+ strings `${start_time}`, plus (if defined) `${end_time}` and `${duration}`.
115
+ Defaults to `'execution queued ${start_time}'`.
116
+
117
+
118
+
119
+ Limitations
120
+ -----------
121
+
122
+
123
+ ### timezones
124
+
125
+ As discussed in
126
+ [ipython-contrib/jupyter_contrib_nbextensions#549](https://github.com/ipython-contrib/jupyter_contrib_nbextensions/issues/549),
127
+ [ipython-contrib/jupyter_contrib_nbextensions#904](https://github.com/ipython-contrib/jupyter_contrib_nbextensions/issues/904),
128
+ and
129
+ [jupyter/jupyter_client#143](https://github.com/jupyter/jupyter_client/issues/143),
130
+ although they are (now) supposed to, Jupyter kernels don't always specify a
131
+ timezone for their timestamps, which can cause problems when the
132
+ [moment.js](https://momentjs.com/)
133
+ library assumes the local timezone, rather than UTC, which is what most kernels
134
+ are actually using.
135
+ To help to address this, see the [options](#Options) above, which can be used
136
+ to assume UTC for unzoned timestamps.
137
+
138
+
139
+ ### execution queues
140
+
141
+ For a reason I don't understand, when multiple cells are queued for execution,
142
+ the kernel doesn't send a reply immediately after finishing executing each
143
+ cell.
144
+ Some replies are delayed, and sent at the same time as later replies, meaning
145
+ that the output of a cell can be updated with its finished value, before the
146
+ notebook recieves the kernel execution reply.
147
+ For the same reason, you can see this in the fact that the star for an
148
+ executing cell can remain next to two cells at once, if several are queued to
149
+ execute together.
150
+ Since this extension uses the times in the kernel message (see internals,
151
+ below), and these remain correct, the timings displayed are still accurate,
152
+ but they may get displayed later due to this kernel issue.
153
+
154
+
155
+ Installation
156
+ ------------
157
+
158
+ Install the master version of the jupyter_contrib_nbextensions repository as
159
+ explained in the docs at
160
+ [jupyter-contrib-nbextensions.readthedocs.io](https://jupyter-contrib-nbextensions.readthedocs.io/en/latest/install.html).
161
+
162
+ Then you can use the
163
+ [jupyter_nbextensions_configurator](https://github.com/Jupyter-contrib/jupyter_nbextensions_configurator)
164
+ to enable/disable this extension for all notebooks.
165
+
166
+ Internals
167
+ ---------
168
+
169
+ The execution start and end times are stored in the cell metadata as ISO8601
170
+ strings, for example:
171
+
172
+ ```json
173
+ {
174
+ "ExecuteTime": {
175
+ "start_time": "2016-02-11T18:51:18.536796",
176
+ "end_time": "2016-02-11T18:51:35.806119"
177
+ }
178
+ }
179
+ ```
180
+
181
+ The times in the timing areas are formatted using the
182
+ [moment.js](https://momentjs.com/) library (already included as part of
183
+ Jupyter), but the durations use a custom formatting function, as
184
+ I ([@jcb91](https://github.com/jcb91))
185
+ couldn't find an existing one that I liked.
186
+
187
+ The event `execute.CodeCell` is caught in order to create a start time, and add
188
+ the timing area with its 'Execution queued at' message.
189
+ The extension again uses [moment.js](https://momentjs.com/) for formatting this
190
+ as an ISO string time.
191
+
192
+ To determine the execution time, the extension patches the Jupyter class
193
+ prototype `CodeCell.prototype.get_callbacks` from `notebook/js/codecell.js`.
194
+ This patch then patches the `callbacks.shell.reply` function returned by the
195
+ original `CodeCell.prototype.get_callbacks`, wrapping it in a function which
196
+ reads the `msg.header.date` value from the kernel message, to provide the
197
+ execution end time.
198
+ This is more accurate than creating a new time, which can be affected by
199
+ client-side variability.
200
+ In addition, for accurate timings, the start time is also revised using
201
+ the `msg.metadata.started` value supplied in the callback, which can be very
202
+ different from the time the cell was queued for execution (as a result of
203
+ other cells already being executed).
204
+ The kernel reply message times are already ISO8601 strings, so no conversion is
205
+ necessary, although again, [moment.js](https://momentjs.com/) is used for
206
+ parsing and diff'ing them.
.local/share/jupyter/nbextensions/execution_dependencies/README.md ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ execution_dependencies
2
+ ======================
3
+
4
+ Writing extensive notebooks can become very complicated since many cells act as stepping stones to produce intermediate results for later cells. Thus, it becomes tedious to
5
+ keep track of the cells that have to be run in order to run a certain cell. This extension simplifies handling the execution dependencies by introducing tag annotations to
6
+ identify each cell and indicate a dependency on others. This improves on the current state which requires remembering all dependencies by heart or annotating the cells in the comments.
7
+
8
+ If a cell with dependencies is run, the extension checks recursively for all dependencies of the cell, then executes them before executing the cell after all the dependencies have finished.
9
+ Dependencies are definitely executed and not only once per kernel session.
10
+
11
+ The two annotations are added to the tags of a cell and are as follows:
12
+
13
+ * add a hashmark (#) and an identification tag to the tags to identify a cell (e.g. #initializer-cell). The #identifiers must be unique among all cells.
14
+ * add an arrow (=>) and an identification tag to the tags to add a dependency on a certain cell (e.g. =>initializer-cell).
15
+
16
+ Based on these dependencies, the kernel will now execute the dependencies before the cell that depends on them. If the cell's dependencies have further dependencies, these will in turn
17
+ be executed before them. In conclusion, the kernel looks through the tree of dependencies of the cell executed by the user and executes its dependencies in their appropriate order,
18
+ then executes the cell.
19
+
20
+ A more extensive example is described below:
21
+
22
+ A cell A has the identifier #A.
23
+
24
+ | Cell A [tags: #A] |
25
+ | ------------- |
26
+ | Content Cell |
27
+ | Content Cell |
28
+
29
+
30
+ A cell B has the identifier #B and depends on A (=>A).
31
+
32
+
33
+ | Cell B [tags: #B, =>A] |
34
+ | ------------- |
35
+ | Content Cell |
36
+ | Content Cell |
37
+
38
+ If the user runs A, only A is executed, since it has no dependencies. On the other hand, if the user runs B, the kernel finds the dependency on A, and thus first runs A and then runs B.
39
+
40
+ Running a cell C that is dependent on B and on A as well, the kernel then first runs A and then runs B before running C, avoiding to run cell A twice.
41
+
42
+
43
+ If you are missing anything, open up an issue at the repository prepending [execute_dependencies] to the title.
.local/share/jupyter/nbextensions/execution_dependencies/execution_dependencies.js ADDED
@@ -0,0 +1,139 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * execution_dependencies.js
3
+ * Introduce tag annotations to identify each cell and indicate a dependency on others.
4
+ * Upon running a cell, its dependencies are run first to prepare all dependencies.
5
+ * Then the cell triggered by the user is run as soon as all its dependencies are met.
6
+ *
7
+ *
8
+ * @version 0.1.0
9
+ * @author Benjamin Ellenberger, https://github.com/benelot
10
+ * @updated 2018-01-31
11
+ *
12
+ *
13
+ */
14
+ define([
15
+ 'jquery',
16
+ 'base/js/dialog',
17
+ 'base/js/namespace',
18
+ 'notebook/js/codecell'
19
+ ], function (
20
+ $,
21
+ dialog,
22
+ Jupyter,
23
+ codecell
24
+ ) {
25
+ "use strict";
26
+
27
+ var CodeCell = codecell.CodeCell;
28
+
29
+ return {
30
+ load_ipython_extension: function () {
31
+ console.log('[execution_dependencies] patching CodeCell.execute');
32
+ var orig_execute = codecell.CodeCell.prototype.execute; // keep original cell execute function
33
+ CodeCell.prototype.execute = function (stop_on_error) {
34
+ var root_tags = this.metadata.tags || []; // get tags of the cell executed by the user (root cell)
35
+ if(root_tags.some(tag => /=>.*/.test(tag))) { // if the root cell contains any dependencies, resolve dependency tree
36
+ var root_cell = this;
37
+ var root_cell_id = root_cell.cell_id;
38
+ var cells_with_id = Jupyter.notebook.get_cells().filter(function (cell, idx, cells) { // ...get all cells which have at least one id (these are the only ones we could have in deps)
39
+ var tags = cell.metadata.tags || [];
40
+ return (cell === root_cell || tags.some(tag => /#.*/.test(tag)));
41
+ });
42
+
43
+ console.log('[execution_dependencies] collecting ids and dependencies...');
44
+ var cell_map = {}
45
+ var dep_graph = {}
46
+ cells_with_id.forEach(function (cell) { // ...get all identified cells (the ones that have at least one #tag)
47
+ var tags = cell.metadata.tags || [];
48
+ var cell_ids = tags.filter(tag => /#.*/.test(tag)).map(tag => tag.substring(1)); // ...get all identifiers of the current cell and drop the #
49
+ if(cell === root_cell){
50
+ if(cell_ids.length < 1) {
51
+ cell_ids.push(root_cell.cell_id); // ...use internal root cell id for internal usage
52
+ }
53
+ else {
54
+ root_cell_id = cell_ids[0]; // get any of the root cell ids
55
+ }
56
+ }
57
+
58
+ var dep_ids = tags.filter(tag => /=>.*/.test(tag)).map(tag => tag.substring(2)); // ...get all dependencies and drop the =>
59
+
60
+ cell_ids.forEach(function (id) {
61
+ //console.log('ID:', id, 'deps: ', dep_ids.toString())
62
+ cell_map[id] = cell;
63
+ dep_graph[id] = dep_ids;
64
+
65
+ });
66
+ });
67
+
68
+ if(dep_graph[root_cell_id].length > 0) {
69
+ console.log('[execution_dependencies] collecting depdendency graph in-degrees...');
70
+ var processing_queue = [root_cell_id];
71
+ var processed_nodes = 0;
72
+ var in_degree = {}; // ...collect in-degrees of nodes
73
+ while(processing_queue.length > 0 && processed_nodes < Object.keys(dep_graph).length) {// ...stay processing deps while the queue contains nodes and the processed nodes are below total node quantity
74
+ var id = processing_queue.shift(); // .....pop front of queue and front-push it to the processing order
75
+ //console.log("ID: ", id);
76
+ for(var i=0, dep_qty=dep_graph[id].length; i < dep_qty; i++) {
77
+ var dep = dep_graph[id][i];
78
+ // console.log(' dep: ', dep);
79
+ in_degree[id] = in_degree[id] || 0;
80
+ in_degree[dep] = in_degree[dep] === undefined ? 1 : ++in_degree[dep];
81
+ processing_queue.unshift(dep);
82
+ }
83
+ processed_nodes++;
84
+ }
85
+
86
+ console.log('[execution_dependencies] starting topological sort...');
87
+ processing_queue = [root_cell_id]; // ...add root node with in-degree 0 to queue (this excludes all disconnected subgraphs)
88
+ processed_nodes = 0; // ...number of processed nodes (to detect circular dependencies)
89
+ var processing_order = [];
90
+ while(processing_queue.length > 0 && processed_nodes < Object.keys(dep_graph).length) {// ...stay processing deps while the queue contains nodes and the processed nodes are below total node quantity
91
+ var id = processing_queue.shift(); // .....pop front of queue and front-push it to the processing order
92
+ processing_order.unshift(id);
93
+ //console.log("ID: ", id);
94
+ for(var i=0, dep_qty=dep_graph[id].length; i < dep_qty; i++) { // ......iterate over dependent nodes of current id and decrease their in-degree by 1
95
+ var dep = dep_graph[id][i];
96
+ // console.log(' dep: ', dep);
97
+ in_degree[dep]--;
98
+ if(in_degree[dep] == 0) { // ......queue dependency if in-degree is 0
99
+ processing_queue.unshift(dep);
100
+ }
101
+ }
102
+ processed_nodes++;
103
+ }
104
+
105
+ console.log('[execution_dependencies] checking for circular dependencies...');
106
+ if(processed_nodes > Object.keys(dep_graph).length) { // ...if more nodes where processed than the number of graph nodes, there is a circular dependency
107
+ dialog.modal({
108
+ title : 'Circular dependencies in the execute dependencies of this cell',
109
+ body : 'There is a circular dependency in this cell\'s execute dependencies. The cell will be run without dependencies. If this does not work, fix the dependencies and rerun the cell.',
110
+ buttons: {'OK': {'class' : 'btn-primary'}},
111
+ notebook: Jupyter.notebook,
112
+ keyboard_manager: Jupyter.keyboard_manager,
113
+ });
114
+ }
115
+ else if(!Jupyter.notebook.trusted) { // ...if the notebook is not trusted, we do not execute dependencies, but only print them out to the user
116
+ dialog.modal({
117
+ title : 'Execute dependencies in untrusted notebook',
118
+ body : 'This notebook is not trusted, so execute dependencies will not be automatically run. You can still run them manually, though. Run in order (the last one is the cell you wanted to execute): ' + processing_order,
119
+ buttons: {'OK': {'class' : 'btn-primary'}},
120
+ notebook: Jupyter.notebook,
121
+ keyboard_manager: Jupyter.keyboard_manager,
122
+ });
123
+ }
124
+ else{
125
+ processing_order.pop()
126
+ console.log('[execution_dependencies] executing dependency cells in order ', processing_order ,'...');
127
+ var dependency_cells = processing_order.map(id =>cell_map[id]); // ...get dependent cells by their id
128
+ //console.log("Execute cells..", dependency_cells)
129
+ dependency_cells.forEach(cell => orig_execute.call(cell, stop_on_error)); // ...execute all dependent cells in sequence using the original execute method
130
+ }
131
+ }
132
+ }
133
+ console.log('[execution_dependencies] executing requested cell...');
134
+ orig_execute.call(this, stop_on_error); // execute original cell execute function
135
+ };
136
+ console.log('[execution_dependencies] loaded');
137
+ }
138
+ };
139
+ });
.local/share/jupyter/nbextensions/exercise/history.md ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Exercise nbextension history
2
+ ----------------------------
3
+
4
+ Update december 30, 2015:
5
+ (@jfbercher) Updated to jupyter notebook 4.1.x
6
+
7
+ Update december 22, 2015:
8
+ (@jfbercher)
9
+ Added the metadata solution_first to mark the beginning of an exercise. It is now possible to have several consecutive exercises.
10
+
11
+ October 21-27,2015:
12
+ (@jfbercher)
13
+
14
+ 1- the extension now works with the multicell API, that is
15
+ - several cells can be selected either via the rubberband extension
16
+ - or via Shift-J (select next) or Shift-K (select previous) keyboard shortcuts
17
+ (probably Shit-up and down will work in a near future)
18
+ Note: previously, the extension required the selected cells to be marked with a "selected" key in metadata. This is no more necessary with the new API.
19
+ Then clicking on the toolbar button turns these cells into a "solution" which is hidden by default ** Do not forget to keep the Shift key pressed down while clicking on the menu button (otherwise selected cells will be lost)**
20
+ 2- the "state" of solutions, hidden or shown, is saved and restored at reload/restart. We use the "solution" metadata to store the current state.
21
+ 3- A small issue (infinite loop when a solution was defined at the bottom edge of the notebook have been corrected)
22
+ 4- Added a keyboard shortcut (Alt-S with S for solution]
23
+
24
+ October-November 2014 (?):
25
+
26
+ Several versions (@juhasch)
.local/share/jupyter/nbextensions/exercise/icon.png ADDED
.local/share/jupyter/nbextensions/exercise/image.gif ADDED
.local/share/jupyter/nbextensions/exercise/main.js ADDED
@@ -0,0 +1,169 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright (c) IPython-Contrib Team.
2
+ // Distributed under the terms of the Modified BSD License.
3
+
4
+ // Hide or display solutions in a notebook
5
+
6
+ /*
7
+ December 6, 2017 @jcb91: use bootstrap 'hidden' class to play nicely with collapsible_headings
8
+ December 30, 2015: update to 4.1
9
+ Update december 22, 2015:
10
+ Added the metadata solution_first to mark the beginning of an exercise. It is now possible to have several consecutive exercises.
11
+ Update october 21-27,2015:
12
+ 1- the extension now works with the multicell API, that is
13
+ - several cells can be selected either via the rubberband extension
14
+ - or via Shift-J (select next) or Shift-K (select previous) keyboard shortcuts
15
+ (probably Shit-up and down will work in a near future)
16
+ Note: previously, the extension required the selected cells to be marked with a "selected" key in metadata. This is no more necessary with the new API.
17
+ Then clicking on the toolbar button transforms these cells into a "solution" which is hidden by default
18
+ ** Do not forget to keep the Shift key pressed down while clicking on the menu button
19
+ (otherwise selected cells will be lost)**
20
+ 2- the "state" of solutions, hidden or shown, is saved and restored at reload/restart. We use the "solution" metadata to store the current state.
21
+ 3- A small issue (infinite loop when a solution was defined at the bottom edge of the notebook have been corrected)
22
+ 4- Added a keyboard shortcut (Alt-S) [S for solution]
23
+ */
24
+
25
+ define([
26
+ 'base/js/namespace',
27
+ 'jquery',
28
+ 'require',
29
+ 'base/js/events',
30
+ ], function(IPython, $, requirejs, events) {
31
+ "use strict";
32
+
33
+ var cfg = {
34
+ add_button: true,
35
+ use_hotkey: true,
36
+ hotkey: 'Alt-S',
37
+ };
38
+
39
+ /**
40
+ * handle click event
41
+ *
42
+ * @method click_solution_lock
43
+ * @param evt {Event} jquery event
44
+ */
45
+ function click_solution_lock(evt) {
46
+ var cell = IPython.notebook.get_selected_cell();
47
+ var is_locked = cell.metadata.solution === 'hidden';
48
+ cell.metadata.solution = is_locked ? 'shown' : 'hidden';
49
+ element_set_locked(cell, !is_locked);
50
+ cell = IPython.notebook.get_next_cell(cell);
51
+ while (cell !== null && cell.metadata.solution !== undefined && !cell.metadata.solution_first) {
52
+ cell.element.toggleClass('hidden', !is_locked);
53
+ cell.metadata.solution = is_locked ? 'shown' : 'hidden';
54
+ cell = IPython.notebook.get_next_cell(cell);
55
+ }
56
+ }
57
+
58
+ /**
59
+ * Create or Remove an exercise in selected cells
60
+ *
61
+ * @method create_remove_exercise
62
+ *
63
+ */
64
+ function create_remove_exercise() {
65
+ var lcells = IPython.notebook.get_selected_cells();
66
+ // It is possible that no cell is selected
67
+ if (lcells.length < 1) {
68
+ alert("Exercise extension: \nPlease select some cells...");
69
+ return;
70
+ }
71
+
72
+ var cell = lcells[0];
73
+ if (cell.metadata.solution_first) {
74
+ remove_element(cell);
75
+ delete cell.metadata.solution_first;
76
+ while (cell !== null && cell.metadata.solution !== undefined && !cell.metadata.solution_first) {
77
+ delete cell.metadata.solution;
78
+ cell.element.removeClass('hidden');
79
+ cell = IPython.notebook.get_next_cell(cell);
80
+ }
81
+ }
82
+ else {
83
+ cell.metadata.solution_first = true;
84
+ cell.metadata.solution = 'hidden';
85
+ add_element(cell);
86
+ for (var k = 1; k < lcells.length; k++) {
87
+ cell = lcells[k];
88
+ cell.element.addClass('hidden');
89
+ cell.metadata.solution = 'hidden';
90
+ }
91
+ }
92
+ }
93
+
94
+ /**
95
+ * Add a lock control to the given cell
96
+ */
97
+ function add_element(cell) {
98
+ var ctrl = cell.element.find('.exercise');
99
+ if (ctrl.length > 0) return ctrl;
100
+ var locked = cell.metadata.solution === 'hidden';
101
+ ctrl = $('<div class="exercise fa">')
102
+ .prependTo(cell.element)
103
+ .on('click', click_solution_lock);
104
+ element_set_locked(cell, locked);
105
+ return ctrl;
106
+ }
107
+
108
+ function remove_element(cell) {
109
+ cell.element.find('.exercise').remove();
110
+ }
111
+
112
+ function element_set_locked(cell, locked) {
113
+ return cell.element.find('.exercise')
114
+ .toggleClass('fa-plus-square-o', locked)
115
+ .toggleClass('fa-minus-square-o', !locked);
116
+ }
117
+
118
+ function refresh_exercises() {
119
+ var in_exercise = false;
120
+ IPython.notebook.get_cells().forEach(function(cell) {
121
+ if (in_exercise && cell.metadata.solution !== undefined && !cell.metadata.solution_first) {
122
+ cell.element.toggleClass('hidden', cell.metadata.solution === 'hidden');
123
+ } else {
124
+ in_exercise = false;
125
+ }
126
+ if (!in_exercise && cell.metadata.solution !== undefined) {
127
+ in_exercise = true;
128
+ add_element(cell);
129
+ }
130
+ });
131
+ }
132
+
133
+ function load_ipython_extension() {
134
+ // add css
135
+ $('<link rel="stylesheet" type="text/css">')
136
+ .attr('href', requirejs.toUrl('./main.css'))
137
+ .appendTo('head');
138
+
139
+ // Hide/display existing solutions at startup
140
+ events.on('notebook_loaded.Notebook', refresh_exercises);
141
+ if (IPython.notebook._fully_loaded) refresh_exercises();
142
+
143
+ var action_name = IPython.keyboard_manager.actions.register({
144
+ help : 'Exercise: Create/Remove exercise',
145
+ help_index: 'ht',
146
+ icon : 'fa-mortar-board',
147
+ handler : create_remove_exercise
148
+ }, 'create_remove_exercise', 'exercise');
149
+
150
+ IPython.notebook.config.loaded.then(function() {
151
+ $.extend(true, cfg, IPython.notebook.config.data);
152
+
153
+ if (cfg.add_button) {
154
+ IPython.toolbar.add_buttons_group([action_name]);
155
+ }
156
+ if (cfg.use_hotkey && cfg.hotkey) {
157
+ var cmd_shrts = {};
158
+ cmd_shrts[cfg.hotkey] = action_name;
159
+ IPython.keyboard_manager.command_shortcuts.add_shortcuts(cmd_shrts);
160
+ }
161
+ }).catch(function(err) {
162
+ console.warn('[exercise] error:', err);
163
+ });
164
+ }
165
+
166
+ return {
167
+ load_ipython_extension: load_ipython_extension,
168
+ };
169
+ });
.local/share/jupyter/nbextensions/exercise2/icon.png ADDED
.local/share/jupyter/nbextensions/exercise2/image.gif ADDED
.local/share/jupyter/nbextensions/exercise2/main.css ADDED
@@ -0,0 +1,60 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .exercise2 {
2
+ display: flex;
3
+ width: 100%;
4
+ flex-direction: row;
5
+ align-content: flex-end;
6
+ }
7
+
8
+ .onoffswitch {
9
+ display: inline;
10
+ position: relative; width: 167px;
11
+ margin-top:8px;
12
+ -webkit-user-select:none; -moz-user-select:none; -ms-user-select: none;
13
+ }
14
+ .onoffswitch-checkbox {
15
+ display: none;
16
+ }
17
+ .onoffswitch-label {
18
+ display: block; overflow: hidden; cursor: pointer;
19
+ border: 2px solid #999999; border-radius: 20px;
20
+ margin:0;
21
+ }
22
+ .onoffswitch-inner {
23
+ display: block; width: 200%; margin-left: -100%;
24
+ transition: margin 0.3s ease-in 0s;
25
+ }
26
+ .onoffswitch-inner:before, .onoffswitch-inner:after {
27
+ display: block; float: left; width: 50%; height: 30px; padding: 0; line-height: 30px;
28
+ font-size: 15px; color: white; font-family: Trebuchet, Arial, sans-serif; font-weight: bold;
29
+ box-sizing: border-box;
30
+ }
31
+ .onoffswitch-inner:before {
32
+ content: "Hide Solution";
33
+ padding-left: 10px;
34
+ background-color: #34A7C1; color: #FFFFFF;
35
+ }
36
+
37
+ .onoffswitch-inner:after {
38
+ content: "Show Solution";
39
+ padding-right: 10px;
40
+ background-color: #73FA7E; color: #999999;
41
+ text-align: right;
42
+ }
43
+ .onoffswitch-switch {
44
+ display: block; width: 14px; margin: 6px;
45
+ padding-top: 0px;
46
+ background: #FFFFFF;
47
+ position: absolute; top: 0; bottom: 0;
48
+ text-align: center;
49
+ vertical-align: middle;
50
+ right: 133px;
51
+ border: 2px solid #999999; border-radius: 20px;
52
+ transition: all 0.25s ease-in 0s;
53
+ }
54
+ .onoffswitch-checkbox:checked + .onoffswitch-label .onoffswitch-inner {
55
+ margin-left: 0;
56
+ }
57
+ .onoffswitch-checkbox:checked + .onoffswitch-label .onoffswitch-switch {
58
+ right: 0px;
59
+ }
60
+
.local/share/jupyter/nbextensions/exercise2/main.js ADDED
@@ -0,0 +1,169 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright (c) IPython-Contrib Team.
2
+ // Distributed under the terms of the Modified BSD License.
3
+
4
+ // Hide or display solutions in a notebook
5
+
6
+ // dec 6, 2017 @jcb91: use bootstrap 'hidden' class to play nicely with collapsible_headings
7
+ // december 30, 2015: update to notebook 4.1.x
8
+ // updated on december 22, 2015 to allow consecutive exercises
9
+ // exercise2: built by @jfbercher from an earlier work by @junasch october 2015) - see readme.md
10
+
11
+ define([
12
+ 'base/js/namespace',
13
+ 'jquery',
14
+ 'require',
15
+ 'base/js/events',
16
+ ], function(IPython, $, requirejs, events) {
17
+ "use strict";
18
+
19
+ var cfg = {
20
+ add_button: true,
21
+ use_hotkey: true,
22
+ hotkey: 'Alt-D',
23
+ };
24
+
25
+ /**
26
+ * handle click event
27
+ *
28
+ * @method click_solution_lock
29
+ * @param evt {Event} jquery event
30
+ */
31
+ function click_solution_lock(evt) {
32
+ var cell = IPython.notebook.get_selected_cell();
33
+ var is_locked = cell.metadata.solution2 === 'hidden';
34
+ cell.metadata.solution2 = is_locked ? 'shown' : 'hidden';
35
+ element_set_locked(cell, !is_locked);
36
+ cell = IPython.notebook.get_next_cell(cell);
37
+ while (cell !== null && cell.metadata.solution2 !== undefined && !cell.metadata.solution2_first) {
38
+ cell.element.toggleClass('hidden', !is_locked);
39
+ cell.metadata.solution2 = is_locked ? 'shown' : 'hidden';
40
+ cell = IPython.notebook.get_next_cell(cell);
41
+ }
42
+ }
43
+
44
+ /**
45
+ * Create or Remove an exercise in selected cells
46
+ *
47
+ * @method create_remove_exercise
48
+ *
49
+ */
50
+ function create_remove_exercise() {
51
+ var lcells = IPython.notebook.get_selected_cells();
52
+ // It is possible that no cell is selected
53
+ if (lcells.length < 1) {
54
+ alert("Exercise extension: \nPlease select some cells...");
55
+ return;
56
+ }
57
+
58
+ var cell = lcells[0];
59
+ if (cell.metadata.solution2_first) {
60
+ remove_element(cell);
61
+ delete cell.metadata.solution2_first;
62
+ while (cell !== null && cell.metadata.solution2 !== undefined && !cell.metadata.solution2_first) {
63
+ delete cell.metadata.solution2;
64
+ cell.element.removeClass('hidden');
65
+ cell = IPython.notebook.get_next_cell(cell);
66
+ }
67
+ }
68
+ else {
69
+ cell.metadata.solution2_first = true;
70
+ cell.metadata.solution2 = 'hidden';
71
+ add_element(cell);
72
+ for (var k = 1; k < lcells.length; k++) {
73
+ cell = lcells[k];
74
+ cell.element.addClass('hidden');
75
+ cell.metadata.solution2 = 'hidden';
76
+ }
77
+ }
78
+ }
79
+
80
+ /**
81
+ * Add a lock control to the given cell
82
+ */
83
+ var cbx = 0;
84
+ function add_element(cell) {
85
+ var ctrl = cell.element.find('.exercise');
86
+ if (ctrl.length > 0) return ctrl;
87
+ var locked = cell.metadata.solution2 === 'hidden';
88
+ cell.element.css('flex-wrap', 'wrap');
89
+ cbx += 1;
90
+ ctrl = $([
91
+ '<div class="exercise exercise2">',
92
+ ' <div class="prompt"></div>',
93
+ ' <div class="onoffswitch">',
94
+ ' <input class="onoffswitch-checkbox" type="checkbox" id="myCheck' + cbx + '">',
95
+ ' <label class="onoffswitch-label" for="myCheck' + cbx + '">',
96
+ ' <div class="onoffswitch-inner"></div>',
97
+ ' <div class="onoffswitch-switch"></div>',
98
+ ' </label>',
99
+ ' </div>',
100
+ '</div>'
101
+ ].join('\n'))
102
+ .appendTo(cell.element);
103
+ ctrl.find('input')
104
+ .on('click', click_solution_lock);
105
+ element_set_locked(cell, locked);
106
+ return ctrl;
107
+ }
108
+
109
+ function remove_element(cell) {
110
+ cell.element.find('.exercise').remove();
111
+ }
112
+
113
+ function element_set_locked(cell, locked) {
114
+ return cell.element.find('.exercise')
115
+ .prop('checked', !locked);
116
+ }
117
+
118
+ function refresh_exercises() {
119
+ var in_exercise = false;
120
+ IPython.notebook.get_cells().forEach(function(cell) {
121
+ if (in_exercise && cell.metadata.solution2 !== undefined && !cell.metadata.solution2_first) {
122
+ cell.element.toggleClass('hidden', cell.metadata.solution2 === 'hidden');
123
+ } else {
124
+ in_exercise = false;
125
+ }
126
+ if (!in_exercise && cell.metadata.solution2 !== undefined) {
127
+ in_exercise = true;
128
+ add_element(cell);
129
+ }
130
+ });
131
+ }
132
+
133
+ function load_ipython_extension() {
134
+ // add css
135
+ $('<link rel="stylesheet" type="text/css">')
136
+ .attr('href', requirejs.toUrl('./main.css'))
137
+ .appendTo('head');
138
+
139
+ // Hide/display existing solutions at startup
140
+ events.on('notebook_loaded.Notebook', refresh_exercises);
141
+ if (IPython.notebook._fully_loaded) refresh_exercises();
142
+
143
+ var action_name = IPython.keyboard_manager.actions.register({
144
+ help : 'Exercise2: Create/Remove exercise',
145
+ help_index: 'ht',
146
+ icon : 'fa-toggle-on',
147
+ handler : create_remove_exercise,
148
+ }, 'create_remove_exercise', 'exercise2');
149
+
150
+ return IPython.notebook.config.loaded.then(function() {
151
+ $.extend(true, cfg, IPython.notebook.config.data.exercise2);
152
+
153
+ if (cfg.add_button) {
154
+ IPython.toolbar.add_buttons_group([action_name]);
155
+ }
156
+ if (cfg.use_hotkey && cfg.hotkey) {
157
+ var cmd_shrts = {};
158
+ cmd_shrts[cfg.hotkey] = action_name;
159
+ IPython.keyboard_manager.command_shortcuts.add_shortcuts(cmd_shrts);
160
+ }
161
+ }).catch(function(err) {
162
+ console.warn('[exercise2] error:', err);
163
+ });
164
+ }
165
+
166
+ return {
167
+ load_ipython_extension: load_ipython_extension,
168
+ };
169
+ });
.local/share/jupyter/nbextensions/export_embedded/export_embedded.yaml ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ Type: Jupyter Notebook Extension
2
+ Compatibility: 5.x
3
+ Main: main.js
4
+ Name: Export Embedded HTML
5
+ Description: Export to HTML with images embedded
6
+ Icon: icon.png
7
+ Link: readme.md
.local/share/jupyter/nbextensions/export_embedded/main.js ADDED
@@ -0,0 +1,56 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // toggle display of all code cells' inputs
2
+
3
+ define([
4
+ 'jquery',
5
+ 'base/js/namespace',
6
+ 'base/js/events'
7
+ ], function(
8
+ $,
9
+ Jupyter,
10
+ events
11
+ ) {
12
+ "use strict";
13
+
14
+ function initialize () {
15
+ }
16
+
17
+ var load_ipython_extension = function() {
18
+
19
+ var v = Jupyter.version.split(".")
20
+ if(Number(v[0])*10+ Number(v[1]) < 51)
21
+ {
22
+ console.log('Notebook version 5.1.0 or higher required for this extension')
23
+ return
24
+ }
25
+
26
+ /* Add an entry in the download menu */
27
+ var dwm = $("#download_menu")
28
+ var downloadEntry = $('<li id="download_html_embed"><a href="#">HTML Embedded (.html)</a></li>')
29
+ dwm.append(downloadEntry)
30
+ downloadEntry.click(function () {
31
+ Jupyter.menubar._nbconvert('html_embed', true);
32
+ });
33
+
34
+ /* Add also a Button, currently disabled */
35
+ /*
36
+ Jupyter.toolbar.add_buttons_group([
37
+ Jupyter.keyboard_manager.actions.register ({
38
+ help : 'Embedded HTML Export',
39
+ icon : 'fa-save',
40
+ handler: function() {
41
+ Jupyter.menubar._nbconvert('html_embed', true);
42
+ }
43
+ }, 'export-embedded-html', 'export_embedded')
44
+ ]);
45
+ */
46
+ if (Jupyter.notebook !== undefined && Jupyter.notebook._fully_loaded) {
47
+ // notebook_loaded.Notebook event has already happened
48
+ initialize();
49
+ }
50
+ events.on('notebook_loaded.Notebook', initialize);
51
+ };
52
+
53
+ return {
54
+ load_ipython_extension : load_ipython_extension
55
+ };
56
+ });
.local/share/jupyter/nbextensions/export_embedded/readme.md ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ Export HTML With Embedded Images
2
+ ================================
3
+ This extension allows exporting an embedded HTML by an additional download option in File -> Download -> HTML Embedded, (works like: jupyter nbconvert --to html_embed notebook.ipynb)
4
+
5
+ **Note**: This extension can so far only successfully read relative images paths in the markdown cells (e.g. `![](graphics/pic.png)`) when jupyter is started in the same folder (working directory) where the relative paths can be resolved!
6
+
7
+
.local/share/jupyter/nbextensions/freeze/config.yaml ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Type: IPython Notebook Extension
2
+ Name: Freeze
3
+ Description: Freeze cells (forbid editing and executing) or make them read-only
4
+ Link: readme.md
5
+ Icon: icon.png
6
+ Main: main.js
7
+ Compatibility: 4.x, 5.x
8
+ Parameters:
9
+ - name: Freeze.readonly_color
10
+ description: |
11
+ Color to use for read-only cell
12
+ default: '#fffef0'
13
+ input_type: color
14
+
15
+ - name: Freeze.frozen_color
16
+ description: |
17
+ Color to use for frozen cell
18
+ default: '#f0feff'
19
+ input_type: color
20
+
.local/share/jupyter/nbextensions/freeze/icon.png ADDED
.local/share/jupyter/nbextensions/freeze/main.js ADDED
@@ -0,0 +1,205 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ define([
2
+ 'base/js/namespace',
3
+ 'base/js/events',
4
+ 'notebook/js/codecell',
5
+ 'notebook/js/textcell',
6
+ 'jquery'
7
+ ], function (
8
+ Jupyter,
9
+ events,
10
+ codecell,
11
+ textcell,
12
+ $
13
+ ) {
14
+ 'use strict';
15
+
16
+ var CodeCell = codecell.CodeCell;
17
+ var MarkdownCell = textcell.MarkdownCell;
18
+
19
+ var mod_name = 'Freeze';
20
+ var log_prefix = '[' + mod_name + ']';
21
+
22
+ // defaults, overridden by server's config
23
+ var options = {
24
+ readonly_color: '#fffef0',
25
+ frozen_color: '#f0feff'
26
+ };
27
+
28
+ function patch_MarkdownCell_unrender () {
29
+ console.log('[Freeze] patching MarkdownCell.prototype.unrender');
30
+ var old_unrender = MarkdownCell.prototype.unrender;
31
+
32
+ MarkdownCell.prototype.unrender = function () {
33
+ // console.log('[Freeze] patched unrender applied');
34
+ if (this.metadata.run_control === undefined ||
35
+ !this.metadata.run_control.frozen
36
+ ) {
37
+ old_unrender.apply(this, arguments);
38
+ }
39
+ };
40
+ }
41
+
42
+ function patch_CodeCell_execute () {
43
+ console.log('[Freeze] patching CodeCell.prototype.execute');
44
+ var old_execute = CodeCell.prototype.execute;
45
+
46
+ CodeCell.prototype.execute = function () {
47
+ if (this.metadata.run_control === undefined ||
48
+ !this.metadata.run_control.frozen
49
+ ) {
50
+ old_execute.apply(this, arguments);
51
+ }
52
+ };
53
+ }
54
+
55
+ // Migrate old metadata format to new notebook-defined metadata.editable
56
+ function migrate_state (cell) {
57
+ if (cell.metadata.run_control !== undefined) {
58
+ if (cell instanceof CodeCell || cell instanceof MarkdownCell) {
59
+ if (cell.metadata.run_control.read_only === true) {
60
+ cell.metadata.editable = false;
61
+ }
62
+ }
63
+ else {
64
+ // remove metadata irrelevant to non-code/markdown cells
65
+ delete cell.metadata.run_control.frozen;
66
+ }
67
+ // remove old key replaced by metadata.editable
68
+ delete cell.metadata.run_control.read_only;
69
+ // remove whole object if it's now empty
70
+ if (Object.keys(cell.metadata.run_control).length === 0) {
71
+ delete cell.metadata.run_control;
72
+ }
73
+ }
74
+ }
75
+
76
+ function get_state (cell) {
77
+ if (cell.metadata.editable === false && (cell instanceof CodeCell || cell instanceof MarkdownCell)) {
78
+ if (cell.metadata.run_control !== undefined && cell.metadata.run_control.frozen) {
79
+ return 'frozen';
80
+ }
81
+ return 'readonly';
82
+ }
83
+ return 'normal';
84
+ }
85
+
86
+ function set_state(cell, state) {
87
+ if (!(cell instanceof CodeCell || cell instanceof MarkdownCell)) {
88
+ return;
89
+ }
90
+
91
+ state = state || 'normal';
92
+ var bg;
93
+ switch (state) {
94
+ case 'normal':
95
+ cell.metadata.editable = true;
96
+ cell.metadata.deletable = true;
97
+ if (cell.metadata.run_control !== undefined) {
98
+ delete cell.metadata.run_control.frozen;
99
+ }
100
+ bg = "";
101
+ break;
102
+ case 'read_only':
103
+ case 'readonly':
104
+ cell.metadata.editable = false;
105
+ cell.metadata.deletable = false;
106
+ if (cell.metadata.run_control !== undefined) {
107
+ delete cell.metadata.run_control.frozen;
108
+ }
109
+ bg = options.readonly_color;
110
+ break;
111
+ case 'frozen':
112
+ cell.metadata.editable = false;
113
+ cell.metadata.deletable = false;
114
+ $.extend(true, cell.metadata, {run_control: {frozen: true}});
115
+ bg = options.frozen_color;
116
+ break;
117
+ }
118
+ // remove whole object if it's now empty
119
+ if (cell.metadata.run_control !== undefined && Object.keys(cell.metadata.run_control).length === 0) {
120
+ delete cell.metadata.run_control;
121
+ }
122
+ cell.code_mirror.setOption('readOnly', !cell.metadata.editable);
123
+ var prompt = cell.element.find('div.input_area');
124
+ prompt.css("background-color", bg);
125
+ }
126
+
127
+ function set_state_selected (state) {
128
+ var cells = Jupyter.notebook.get_selected_cells();
129
+ for (var i = 0; i < cells.length; i++) {
130
+ set_state(cells[i], state);
131
+ }
132
+ }
133
+
134
+ function button_callback(state) {
135
+ set_state_selected(state);
136
+ var dirty_state = {value: true};
137
+ events.trigger("set_dirty.Notebook", dirty_state);
138
+ }
139
+
140
+ function make_normal_selected () {
141
+ button_callback('normal');
142
+ }
143
+
144
+ function make_read_only_selected () {
145
+ button_callback('read_only');
146
+ }
147
+
148
+ function make_frozen_selected () {
149
+ button_callback('frozen');
150
+ }
151
+
152
+ function initialize_states () {
153
+ var cells = Jupyter.notebook.get_cells();
154
+ for (var i = 0; i < cells.length; i++) {
155
+ var cell = cells[i];
156
+ migrate_state(cell);
157
+ var state = get_state(cell);
158
+ set_state(cell, state);
159
+ }
160
+ }
161
+
162
+ function load_extension () {
163
+ Jupyter.toolbar.add_buttons_group([
164
+ Jupyter.keyboard_manager.actions.register ({
165
+ help : 'lift restrictions from selected cells',
166
+ icon : 'fa-unlock-alt',
167
+ handler : make_normal_selected
168
+ }, 'make-cells-normal', mod_name),
169
+ Jupyter.keyboard_manager.actions.register({
170
+ help : 'make selected cells read-only',
171
+ icon: 'fa-lock',
172
+ handler : make_read_only_selected
173
+ }, 'make-cells-read-only', mod_name),
174
+ Jupyter.keyboard_manager.actions.register({
175
+ help : 'freeze selected cells',
176
+ icon : 'fa-asterisk',
177
+ handler : make_frozen_selected
178
+ }, 'freeze-cells', mod_name)
179
+ ]);
180
+
181
+ patch_CodeCell_execute();
182
+ patch_MarkdownCell_unrender();
183
+
184
+ Jupyter.notebook.config.loaded.then(function on_config_loaded () {
185
+ $.extend(true, options, Jupyter.notebook.config.data[mod_name]);
186
+ }, function on_config_load_error (reason) {
187
+ console.warn(log_prefix, 'Using defaults after error loading config:', reason);
188
+ }).then(function do_stuff_with_config () {
189
+ events.on("notebook_loaded.Notebook", initialize_states);
190
+ if (Jupyter.notebook !== undefined && Jupyter.notebook._fully_loaded) {
191
+ // notebook already loaded, so we missed the event, so update all
192
+ initialize_states();
193
+ }
194
+ }).catch(function on_error (reason) {
195
+ console.error(log_prefix, 'Error:', reason);
196
+ });
197
+ }
198
+
199
+ return {
200
+ get_state : get_state,
201
+ set_state : set_state,
202
+ load_jupyter_extension : load_extension,
203
+ load_ipython_extension : load_extension
204
+ };
205
+ });
.local/share/jupyter/nbextensions/freeze/readme.md ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Freeze
2
+
3
+ This extension allows to make cells read-only or frozen. It provides three buttons:
4
+ * unlock
5
+ * read-only
6
+ * frozen
7
+
8
+
9
+ For **code-cells**:<br>
10
+ _read-only_: it can be executed, but its input cannot be changed.<br>
11
+ _frozen_: It cannot be either altered or executed.
12
+
13
+ For **markdown-cells**:<br>
14
+ _read-only_: It's input can be viewed by double-clicking on it, but cannot be changed.<br>
15
+ _frozen_: Input cannot be viewed by double-clicking.
16
+
17
+ To change the state of a selected cell, press the corresponding button.
18
+
19
+ The individual cell's state is stored in its metadata and is applied to the cell if the extension is loaded.
20
+
21
+ ## Internals
22
+
23
+ The _read-only_ state is stored in the `cell.metadata.editable` attribute. Cells are editable by default.
24
+ The _frozen_ state is stored in the `cell.metadata.run_control.frozen`attribute.
.local/share/jupyter/nbextensions/gist_it/gist_it.yaml ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Type: IPython Notebook Extension
2
+ Compatibility: 3.x, 4.x, 5.x
3
+ Name: Gist-it
4
+ Main: main.js
5
+ Description: Adds a button to publish the current notebook as a gist. See the readme for description of the authentication options and relevant parameters.
6
+ Link: readme.md
7
+ Icon: icon.png
8
+ Parameters:
9
+ - name: gist_it_personal_access_token
10
+ description: Github personal access token. To write gists on a user's behalf, you need a token with the <a href="https://developer.github.com/apps/building-oauth-apps/understanding-scopes-for-oauth-apps/">gist OAuth scope</a>.
11
+ input_type: text
12
+ - name: gist_it_default_to_public
13
+ description: Gists default to public. If using a personal access token, gists will default to private. Set this to have them default to being public instead.
14
+ input_type: checkbox
15
+ - name: github_endpoint
16
+ description: Github endpoint. Defaults to 'github.com'. Change this to publish to your enterprise github endpoint.
17
+ input_type: text
18
+ default: 'github.com'
.local/share/jupyter/nbextensions/gist_it/icon.png ADDED
.local/share/jupyter/nbextensions/gist_it/main.js ADDED
@@ -0,0 +1,481 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ *
3
+ // Avoid server side code :
4
+ // https://github.com/ipython/ipython/issues/2780
5
+ *
6
+ * This essentially boils down to the following:
7
+ * Github authentication requires some server-side code for any 'app' which
8
+ * wants to authenticate over the Github API.
9
+ * When registering an app with Github, Github provides the app with what they
10
+ * call a 'client secret'.
11
+ * The client secret is then incorporated into the app, and the app sends it to
12
+ * Github as part of the authentication process, thus proving to Github's
13
+ * servers that the communicating app was written by someone with appropriate
14
+ * credentials.
15
+ *
16
+ * The issue with writing a single Github app for Gist-ing notebooks, is that
17
+ * it would need to include such a client secret. Since this would be part of
18
+ * the extension source code, anyone could use the client secret, potentially
19
+ * gaining the permissions that any given user has granted to the app.
20
+ *
21
+ * As a result, we only support:
22
+ * - anonymous (non-authenticated) API usage
23
+ * - client-side authentication using a personal access token
24
+ * (see https://github.com/settings/tokens)
25
+ */
26
+
27
+ define([
28
+ 'jquery',
29
+ 'base/js/namespace',
30
+ 'base/js/dialog'
31
+ ], function (
32
+ $,
33
+ Jupyter,
34
+ dialog
35
+ ) {
36
+ "use strict";
37
+
38
+ // define default values for config parameters
39
+ var params = {
40
+ gist_it_default_to_public: false,
41
+ gist_it_personal_access_token: '',
42
+ github_endpoint: 'github.com'
43
+ };
44
+
45
+ var initialize = function () {
46
+ update_params();
47
+ Jupyter.toolbar.add_buttons_group([
48
+ Jupyter.keyboard_manager.actions.register ({
49
+ help : 'Create/Edit Gist of Notebook',
50
+ icon : 'fa-github',
51
+ handler: show_gist_editor_modal
52
+ }, 'create-gist-from-notebook', 'gist_it')
53
+ ]);
54
+ };
55
+
56
+ // update params with any specified in the server's config file
57
+ var update_params = function() {
58
+ var config = Jupyter.notebook.config;
59
+ for (var key in params) {
60
+ if (config.data.hasOwnProperty(key))
61
+ params[key] = config.data[key];
62
+ }
63
+ default_metadata.data.public = Boolean(config.data.gist_it_default_to_public);
64
+ };
65
+
66
+ var default_metadata = {
67
+ id: '',
68
+ data: {
69
+ description: Jupyter.notebook.notebook_path,
70
+ public: false
71
+ }
72
+ };
73
+
74
+ function ensure_default_metadata () {
75
+ Jupyter.notebook.metadata.gist = $.extend(
76
+ true, // deep-copy
77
+ default_metadata, //defaults
78
+ Jupyter.notebook.metadata.gist // overrides
79
+ );
80
+ }
81
+
82
+ var add_auth_token = function add_auth_token (xhr) {
83
+ var token = '';
84
+ if (params.gist_it_personal_access_token !== '') {
85
+ token = params.gist_it_personal_access_token;
86
+ }
87
+ if (token !== '') {
88
+ xhr.setRequestHeader("Authorization", "token " + token);
89
+ }
90
+ };
91
+
92
+ function build_alert(alert_class) {
93
+ return $('<div/>')
94
+ .addClass('alert alert-dismissable')
95
+ .addClass(alert_class)
96
+ .append(
97
+ $('<button class="close" type="button" data-dismiss="alert" aria-label="Close"/>')
98
+ .append($('<span aria-hidden="true"/>').html('&times;'))
99
+ );
100
+ }
101
+
102
+ function gist_error (jqXHR, textStatus, errorThrown) {
103
+ console.log('github ajax error:', jqXHR, textStatus, errorThrown);
104
+ var alert = build_alert('alert-danger')
105
+ .hide()
106
+ .append(
107
+ $('<p/>').text('The ajax request to Github went wrong:')
108
+ )
109
+ .append(
110
+ $('<pre/>').text(jqXHR.responseJSON ? JSON.stringify(jqXHR.responseJSON, null, 2) : errorThrown)
111
+ );
112
+ $('#gist_modal').find('.modal-body').append(alert);
113
+ alert.slideDown('fast');
114
+ }
115
+
116
+ function gist_success (response, textStatus, jqXHR) {
117
+ // if (Jupyter.notebook.metadata.gist.id === response.id) return;
118
+
119
+ Jupyter.notebook.metadata.gist.id = response.id;
120
+ Jupyter.notebook.metadata._draft = $.extend(
121
+ true, // deep copy
122
+ Jupyter.notebook.metadata._draft, // defaults
123
+ {nbviewer_url: response.html_url} // overrides
124
+ );
125
+
126
+ var d = new Date();
127
+ var msg_head = d.toLocaleString() + ': Gist ';
128
+ var msg_tail = response.history.length === 1 ? ' published' : ' updated to revision ' + response.history.length;
129
+ var alert = build_alert('alert-success')
130
+ .hide()
131
+ .append(msg_head)
132
+ .append(
133
+ $('<a/>')
134
+ .attr('href', response.html_url)
135
+ .attr('target', '_blank')
136
+ .text(response.id)
137
+ )
138
+ .append(msg_tail);
139
+ $('#gist_modal').find('.modal-body').append(alert);
140
+ alert.slideDown('fast');
141
+ }
142
+
143
+ function get_github_endpoint() {
144
+ return params.github_endpoint !== '' ? params.github_endpoint : 'github.com';
145
+ }
146
+
147
+ function get_api_endpoint() {
148
+ const github_endpoint = get_github_endpoint();
149
+ if (github_endpoint === 'github.com') {
150
+ return 'https://api.'+ github_endpoint;
151
+ } else {
152
+ // Github Enterprise
153
+ // https://developer.github.com/enterprise/2.18/v3/enterprise-admin/#endpoint-urls
154
+ return 'https://' + github_endpoint + '/api/v3'
155
+ }
156
+ }
157
+
158
+ function gist_id_updated_callback(gist_editor) {
159
+ if (gist_editor === undefined) gist_editor = $('#gist_editor');
160
+
161
+ var id_input = gist_editor.find('#gist_id');
162
+ var id = id_input.val();
163
+
164
+ var help_block = gist_editor.find('#gist_id ~ .help-block');
165
+ var help_block_base_text = 'Set the gist id to update an existing gist, ' +
166
+ 'or leave blank to create a new one.';
167
+
168
+ var gist_it_button = $('#gist_modal').find('.btn-primary');
169
+
170
+ id_input.parent()
171
+ .removeClass('has-success has-error has-warning')
172
+ .find('#gist_id ~ .form-control-feedback > i.fa')
173
+ .removeClass('fa-pencil-square fa-exclamation-circle fa-question-circle');
174
+
175
+ if (id === '') {
176
+ $('#gist_id ~ .form-control-feedback > i.fa')
177
+ .addClass('fa-plus-circle');
178
+ help_block.html(
179
+ '<p>' + help_block_base_text + '</p>' +
180
+ '<p><i class="fa fa-plus-circle"></i> a new gist will be created</p>'
181
+ );
182
+ gist_it_button.prop('disabled', false);
183
+ }
184
+ else {
185
+ $('#gist_id ~ .form-control-feedback > i.fa')
186
+ .addClass('fa-circle-o-notch fa-spin');
187
+ // List commits as a way of checking whether the gist exists.
188
+ // Listing commits appears to give the most concise response.
189
+
190
+ $.ajax({
191
+ url: get_api_endpoint() +'/gists/' + id + '/commits',
192
+ dataType: 'json',
193
+ beforeSend: add_auth_token,
194
+ error: function(jqXHR, textStatus, errorThrown) {
195
+ jqXHR.errorThrown = errorThrown;
196
+ },
197
+ complete: function(jqXHR, textStatus) {
198
+ var success = textStatus === 'success';
199
+ var error = !success && jqXHR.status === 404 && jqXHR.responseJSON !== undefined;
200
+ var warning = !success && !error;
201
+
202
+ var help_block_html = '<p>' + help_block_base_text + '</p>';
203
+
204
+ gist_it_button.prop('disabled', error);
205
+ if (success) {
206
+ var single = (jqXHR.responseJSON.length === 1);
207
+ help_block_html += '<p>' +
208
+ '<i class="fa fa-pencil-square"></i>' +
209
+ ' gist ' +
210
+ '<a href="https://'+ get_github_endpoint() + '/gist/' + id +
211
+ '" target="_blank">' + id + '</a> will be updated' +
212
+ ' (' + jqXHR.responseJSON.length +
213
+ ' revision' + (single ? '' : 's') +
214
+ ' exist' + (single ? 's' : '') + ' so far)' +
215
+ '</p>';
216
+ }
217
+ else if (error) {
218
+ help_block_html += '<p>' +
219
+ '<i class="fa fa-exclamation-circle"></i>' +
220
+ ' no gist exists with the specified id (given current access token)'+
221
+ '</p>';
222
+ }
223
+ else {
224
+ help_block_html += '<p>' +
225
+ '<i class="fa fa-question-circle"></i>' +
226
+ ' can\'t list commits for the specified gist id - you may have problems updating it!' +
227
+ '</p>';
228
+ help_block_html += '<p>The ajax request to Github went wrong:<p/>' +
229
+ '<pre>';
230
+ if (jqXHR.responseJSON) {
231
+ help_block_html += JSON.stringify(jqXHR.responseJSON, null, 2);
232
+ }
233
+ else {
234
+ help_block_html += jqXHR.errorThrown || textStatus;
235
+ }
236
+ help_block_html += '</pre>';
237
+ console.log('non-404 github ajax error:', jqXHR, textStatus);
238
+ }
239
+ help_block.html(help_block_html);
240
+
241
+ id_input.parent()
242
+ .toggleClass('has-success', success)
243
+ .toggleClass('has-error', error)
244
+ .toggleClass('has-warning', warning)
245
+ .find('#gist_id ~ .form-control-feedback > i.fa')
246
+ .removeClass('fa-circle-o-notch fa-spin')
247
+ .toggleClass('fa-pencil-square', success)
248
+ .toggleClass('fa-exclamation-circle', error)
249
+ .toggleClass('fa-question-circle', warning);
250
+ }
251
+ });
252
+ }
253
+ }
254
+
255
+ function update_gist_editor (gist_editor) {
256
+ if (gist_editor === undefined) gist_editor = $('#gist_editor');
257
+
258
+ var id_input = gist_editor.find('#gist_id');
259
+
260
+ var have_auth = params.gist_it_personal_access_token !== '';
261
+ var id = '';
262
+ var is_public = true;
263
+ if (have_auth) {
264
+ id = Jupyter.notebook.metadata.gist.id;
265
+ is_public = Jupyter.notebook.metadata.gist.data.public;
266
+ id_input.val(id);
267
+ }
268
+ id_input.closest('.form-group').toggle(have_auth);
269
+
270
+ gist_editor.find('#gist_public')
271
+ .prop('checked', is_public)
272
+ .prop('readonly', !have_auth);
273
+
274
+ gist_editor.find('#gist_description')
275
+ .val(Jupyter.notebook.metadata.gist.data.description);
276
+
277
+ if (have_auth) {
278
+ gist_id_updated_callback(gist_editor);
279
+ }
280
+ }
281
+
282
+ function build_gist_editor () {
283
+ ensure_default_metadata();
284
+
285
+ var gist_editor = $('#gist_editor');
286
+
287
+ if (gist_editor.length > 0) return gist_editor;
288
+
289
+ gist_editor = $('<div/>').attr('id', 'gist_editor').append(controls);
290
+
291
+ var id = params.gist_it_personal_access_token !== '' ? Jupyter.notebook.metadata.gist.id : '';
292
+ var controls = $('<form/>')
293
+ .appendTo(gist_editor)
294
+ .addClass('form-horizontal');
295
+
296
+ $('<div/>')
297
+ .addClass('has-feedback')
298
+ .hide()
299
+ .appendTo(controls)
300
+ .append(
301
+ $('<label/>')
302
+ .attr('for', 'gist_id')
303
+ .text('Gist id')
304
+ )
305
+ .append(
306
+ $('<input/>')
307
+ .addClass('form-control')
308
+ .attr('id', 'gist_id')
309
+ .val(Jupyter.notebook.metadata.gist.id)
310
+ )
311
+ .append(
312
+ $('<span/>')
313
+ .addClass('form-control-feedback')
314
+ .append(
315
+ $('<i/>')
316
+ .addClass('fa fa-lg')
317
+ )
318
+ )
319
+ .append(
320
+ $('<span/>')
321
+ .addClass('help-block')
322
+ );
323
+ $('<div/>')
324
+ .appendTo(controls)
325
+ .append(
326
+ $('<div/>')
327
+ .addClass('checkbox')
328
+ .append(
329
+ $('<label>')
330
+ .text('Make the gist public')
331
+ .prepend(
332
+ $('<input/>')
333
+ .attr('type', 'checkbox')
334
+ .attr('id', 'gist_public')
335
+ .prop('checked', Jupyter.notebook.metadata.gist.data.public)
336
+ .prop('readonly', id === '')
337
+ )
338
+ )
339
+ )
340
+ .append(
341
+ $('<label/>')
342
+ .attr('for', 'gist_public')
343
+ .text('public')
344
+ );
345
+ $('<div/>')
346
+ .appendTo(controls)
347
+ .append(
348
+ $('<label/>')
349
+ .attr('for', 'gist_description')
350
+ .text('description')
351
+ )
352
+ .append(
353
+ $('<input/>')
354
+ .addClass('form-control')
355
+ .attr('id', 'gist_description')
356
+ .attr('type', 'textarea')
357
+ .val(Jupyter.notebook.metadata.gist.data.description)
358
+ );
359
+
360
+ var form_groups = controls.children('div').addClass('form-group');
361
+ form_groups
362
+ .children('label')
363
+ .addClass('col-sm-2 control-label')
364
+ .css('padding-right', '1em');
365
+ form_groups
366
+ .each(function (index, elem) {
367
+ $('<div/>')
368
+ .appendTo(elem)
369
+ .addClass('col-sm-10')
370
+ .append($(elem).children(':not(label)'));
371
+ });
372
+
373
+ update_gist_editor(gist_editor);
374
+
375
+ // bind events for id changing
376
+ var id_input = gist_editor.find('#gist_id');
377
+ // Save current value of element
378
+ id_input.data('oldVal', id_input.val());
379
+ // Look for changes in the value
380
+ id_input.bind("change click keyup input paste", function(event) {
381
+ // If value has changed...
382
+ if (id_input.data('oldVal') !== id_input.val()) {
383
+ // Updated stored value
384
+ id_input.data('oldVal', id_input.val());
385
+ // Do action
386
+ gist_id_updated_callback(gist_editor);
387
+ }
388
+ });
389
+
390
+ return gist_editor;
391
+ }
392
+
393
+ function show_gist_editor_modal () {
394
+ var modal;
395
+ modal = dialog.modal({
396
+ show: false,
397
+ title: 'Share on Github',
398
+ notebook: Jupyter.notebook,
399
+ keyboard_manager: Jupyter.notebook.keyboard_manager,
400
+ body: build_gist_editor(),
401
+ buttons: {
402
+ ' Gist it!': {
403
+ class : 'btn-primary',
404
+ click: function() {
405
+ modal.find('.btn').prop('disabled', true);
406
+ var new_data = {
407
+ public: $('#gist_public').prop('checked'),
408
+ description: $('#gist_description').val()
409
+ };
410
+ $.extend(
411
+ true,
412
+ Jupyter.notebook.metadata.gist.data,
413
+ new_data
414
+ );
415
+ // prevent the modal from closing. See github.com/twbs/bootstrap/issues/1202
416
+ modal.data('bs.modal').isShown = false;
417
+ var spinner = modal.find('.btn-primary .fa-github').addClass('fa-spin');
418
+ make_gist(function (jqXHR, textStatus) {
419
+ modal.find('.btn').prop('disabled', false);
420
+ // allow the modal to close again. See github.com/twbs/bootstrap/issues/1202
421
+ modal.data('bs.modal').isShown = true;
422
+ spinner.removeClass('fa-spin');
423
+ });
424
+ }
425
+ },
426
+ done: {}
427
+ }
428
+ })
429
+ .attr('id', 'gist_modal')
430
+ .on('shown.bs.modal', function (evt) {
431
+ var err = modal.find('#gist_id').parent().hasClass('has-error');
432
+ modal.find('.btn-primary').prop('disabled', err);
433
+ });
434
+
435
+ modal.find('.btn-primary').prepend(
436
+ $('<i/>')
437
+ .addClass('fa fa-lg fa-github')
438
+ );
439
+
440
+ modal.modal('show');
441
+ }
442
+
443
+ var make_gist = function make_gist (complete_callback) {
444
+ ensure_default_metadata();
445
+
446
+ var data = $.extend(
447
+ true, // deep-copy
448
+ { files: {} }, // defaults
449
+ Jupyter.notebook.metadata.gist.data // overrides
450
+ );
451
+ var filename = Jupyter.notebook.notebook_name;
452
+ data.files[filename] = {
453
+ content: JSON.stringify(Jupyter.notebook.toJSON(), null, 2)
454
+ };
455
+
456
+ var id_input = $('#gist_id');
457
+ var id = params.gist_it_personal_access_token !== '' ? id_input.val() : '';
458
+ var method = id ? 'PATCH' : 'POST';
459
+
460
+ // Create/edit the Gist
461
+ $.ajax({
462
+ url: get_api_endpoint() +'/gists' + (id ? '/' + id : ''),
463
+ type: method,
464
+ dataType: 'json',
465
+ data: JSON.stringify(data),
466
+ beforeSend: add_auth_token,
467
+ success: gist_success,
468
+ error: gist_error,
469
+ complete: complete_callback
470
+ });
471
+ };
472
+
473
+ function load_jupyter_extension () {
474
+ return Jupyter.notebook.config.loaded.then(initialize);
475
+ }
476
+
477
+ return {
478
+ load_jupyter_extension: load_jupyter_extension,
479
+ load_ipython_extension: load_jupyter_extension
480
+ };
481
+ });
.local/share/jupyter/nbextensions/go_to_current_running_cell/README.md ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Go to Running Cell
2
+ ==================
3
+
4
+ This is an extension allows you to jump to the current running cell. You can also activate this functionality automatically, i.e., your view is always scolling to the current cell.
5
+
6
+ Button: A button with eye icon that you can go to the first running cell.
7
+ ![button](anchor.png)
8
+
9
+ Keyboard shortcuts:
10
+ -------------------
11
+ __*Alt-I*__ (Jump to first running cell)
12
+ __*Meta-[*__ (Follow executing cell On)
13
+ __*Meta-]*__(Follow executing cell Off)
14
+
15
+ Demo
16
+ ----
17
+ ### Jump to first running cell
18
+ ![button](jump_to_cell.gif)
19
+
20
+ ### Follow executing cell
21
+
22
+ ![button](auto_focus.gif)
.local/share/jupyter/nbextensions/go_to_current_running_cell/eye.png ADDED
.local/share/jupyter/nbextensions/go_to_current_running_cell/go_to_current_running_cell.yaml ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Type: Jupyter Notebook Extension
2
+ Name: Go to Current Running Cells
3
+ Description: Go to Running cell and always scroll into current running cell view
4
+ Link: README.md
5
+ Main: main.js
6
+ Compatibility: 4.x, 5.x
7
+
8
+ Parameters:
9
+ - name: is_follow_cell
10
+ description: Activate follow executing cells, default behavior is false.
11
+ input_type: checkbox
12
+ - name: go_to_running_cell_shortcut
13
+ description: Go to first running cell
14
+ input_type: input
15
+ default: Alt-I
16
+ - name: follow_cell_on_shortcut
17
+ description: Enable following running cell
18
+ input_type: input
19
+ default: Alt-;
20
+ - name: follow_cell_off_shortcut
21
+ description: Disable following running cell
22
+ input_type: input
23
+ default: Alt-'
24
+ - name: button_icon
25
+ description: Button for go to first running cell
26
+ default: fa-anchor