0-hero commited on
Commit
fa0f006
Β·
verified Β·
1 Parent(s): f67f72f

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. .local/share/jupyter/nbextensions/code_prettify/README_2to3.md +139 -0
  2. .local/share/jupyter/nbextensions/code_prettify/README_code_prettify.md +300 -0
  3. .local/share/jupyter/nbextensions/code_prettify/demo-py.gif +0 -0
  4. .local/share/jupyter/nbextensions/codefolding/codefolding_firstline_folded.png +0 -0
  5. .local/share/jupyter/nbextensions/codefolding/codefolding_firstline_unfolded.png +0 -0
  6. .local/share/jupyter/nbextensions/codefolding/codefolding_indent_folded_1.png +0 -0
  7. .local/share/jupyter/nbextensions/codefolding/edit.js +4 -0
  8. .local/share/jupyter/nbextensions/codefolding/foldgutter.css +5 -0
  9. .local/share/jupyter/nbextensions/codemirror_mode_extensions/codemirror_mode_extensions.yaml +7 -0
  10. .local/share/jupyter/nbextensions/collapsible_headings/icon.png +0 -0
  11. .local/share/jupyter/nbextensions/collapsible_headings/main.css +130 -0
  12. .local/share/jupyter/nbextensions/collapsible_headings/main.js +1092 -0
  13. .local/share/jupyter/nbextensions/datestamper/icon.png +0 -0
  14. .local/share/jupyter/nbextensions/equation-numbering/info.yaml +7 -0
  15. .local/share/jupyter/nbextensions/execute_time/ExecuteTime.js +349 -0
  16. .local/share/jupyter/nbextensions/execute_time/execution-timings-menu.png +0 -0
  17. .local/share/jupyter/nbextensions/execute_time/icon.png +0 -0
  18. .local/share/jupyter/nbextensions/execute_time/readme.md +206 -0
  19. .local/share/jupyter/nbextensions/exercise/history.md +26 -0
  20. .local/share/jupyter/nbextensions/exercise/main.js +169 -0
  21. .local/share/jupyter/nbextensions/help_panel/help_panel_ext.png +0 -0
  22. .local/share/jupyter/nbextensions/help_panel/icon.png +0 -0
  23. .local/share/jupyter/nbextensions/hide_header/hide_header.yaml +12 -0
  24. .local/share/jupyter/nbextensions/hide_input_all/hide_input_all.yaml +7 -0
  25. .local/share/jupyter/nbextensions/hide_input_all/hide_input_all_show.png +0 -0
  26. .local/share/jupyter/nbextensions/hide_input_all/icon.png +0 -0
  27. .local/share/jupyter/nbextensions/hide_input_all/main.js +59 -0
  28. .local/share/jupyter/nbextensions/hide_input_all/readme.md +44 -0
  29. .local/share/jupyter/nbextensions/highlighter/demo_highlighter.ipynb +96 -0
  30. .local/share/jupyter/nbextensions/highlighter/export_highlights.pdf +0 -0
  31. .local/share/jupyter/nbextensions/highlighter/export_highlights.tex +457 -0
  32. .local/share/jupyter/nbextensions/highlighter/highlighter.css +98 -0
  33. .local/share/jupyter/nbextensions/highlighter/highlighter.js +378 -0
  34. .local/share/jupyter/nbextensions/highlighter/highlighter.yaml +7 -0
  35. .local/share/jupyter/nbextensions/highlighter/icon.png +0 -0
  36. .local/share/jupyter/nbextensions/highlighter/readme.md +47 -0
  37. .local/share/jupyter/nbextensions/highlighter/tst_highlights.ipynb +53 -0
  38. .local/share/jupyter/nbextensions/hinterland/README.md +49 -0
  39. .local/share/jupyter/nbextensions/hinterland/hinterland.yaml +73 -0
  40. .local/share/jupyter/nbextensions/init_cell/README.md +44 -0
  41. .local/share/jupyter/nbextensions/init_cell/cell_toolbar_menu.png +0 -0
  42. .local/share/jupyter/nbextensions/init_cell/icon.png +0 -0
  43. .local/share/jupyter/nbextensions/init_cell/init_cell.yaml +17 -0
  44. .local/share/jupyter/nbextensions/keyboard_shortcut_editor/README.md +130 -0
  45. .local/share/jupyter/nbextensions/keyboard_shortcut_editor/icon.png +0 -0
  46. .local/share/jupyter/nbextensions/keyboard_shortcut_editor/kse_components.js +364 -0
  47. .local/share/jupyter/nbextensions/keyboard_shortcut_editor/main.js +778 -0
  48. .local/share/jupyter/nbextensions/keyboard_shortcut_editor/quickhelp_shim.js +207 -0
  49. .local/share/jupyter/nbextensions/keyboard_shortcut_editor/readme_add_new_link.png +0 -0
  50. .local/share/jupyter/nbextensions/keyboard_shortcut_editor/readme_conflict.png +0 -0
.local/share/jupyter/nbextensions/code_prettify/README_2to3.md ADDED
@@ -0,0 +1,139 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ A 2to3 converter
2
+ ================
3
+
4
+ This nbextension converts python2 code in notebook code cells to python3 code.
5
+
6
+ Under the hood, it uses a call to the current notebook kernel to reformat the
7
+ code.
8
+ The conversion run by the kernel uses Python's standard-library [lib2to3]
9
+ module.
10
+
11
+ The nbextension provides
12
+
13
+ - a toolbar button (configurable to be added or not)
14
+
15
+ - a keyboard shortcut for reformatting the current code-cell (default shortcut
16
+ is `Ctrl-M`, can also be configured not to add the keyboard shortcut).
17
+
18
+ - a keyboard shortcut for reformatting the whole notebook (default shortcut
19
+ is `Ctrl-Shift-M`, can also be configured not to add the keyboard shortcut).
20
+
21
+ Syntax needs to be correct, but the nbextension may be able to point out basic
22
+ syntax errors.
23
+
24
+ ![](demo_2to3.gif)
25
+
26
+
27
+ Options
28
+ -------
29
+
30
+ All options are provided by the [KerneExecOnCells library] - see the
31
+ [internals] section below for details.
32
+ There are a few nbextension-wide options, configurable using the
33
+ [jupyter_nbextensions_configurator] or by editing the `notebook` section config
34
+ file directly.
35
+ The options are as follows:
36
+
37
+ - `2to3.add_toolbar_button`:
38
+ Whether to add a toolbar button to transform the selected cell(s).
39
+ Defaults to `true`.
40
+
41
+ - `2to3.button_icon`:
42
+ A font-awesome class defining the icon used for the toolbar button and
43
+ actions. See [fontawesome] for available icon classes.
44
+ Defaults to `fa-legal`.
45
+
46
+ - `2to3.button_label`:
47
+ Toolbar button label text. Also used in the actions' help text.
48
+ Defaults to `Convert Python 2 to 3`.
49
+
50
+ - `2to3.register_hotkey`:
51
+ Whether to register hotkeys to transform the selected cell(s)/whole notebook.
52
+ Defaults to `true`.
53
+
54
+ - `2to3.hotkeys.process_all`:
55
+ Hotkey to use to transform all the code cells in the notebook.
56
+ Defaults to `Ctrl-Shift-L`.
57
+
58
+ - `2to3.hotkeys.process_selected`:
59
+ Hotkey to use to transform the selected cell(s).
60
+ Defaults to `Ctrl-L`.
61
+
62
+ - `2to3.show_alerts_for_not_supported_kernel`:
63
+ Whether to show alerts if the kernel is not supported.
64
+ Defaults to `false`.
65
+
66
+ - `2to3.show_alerts_for_errors`:
67
+ Whether to show alerts for errors in the kernel calls.
68
+ Defaults to `true`.
69
+
70
+ - `2to3.kernel_config_map_json`:
71
+ The value of this key is a string which can be parsed into a json object
72
+ giving the config for each kernel language.
73
+
74
+ The following give the per-kernel options of the parsed json, using the
75
+ language key `python `:
76
+
77
+ * `2to3.kernel_config_map_json.python.library`:
78
+ String to execute in the kernel in order to load any necessary kernel
79
+ libraries.
80
+
81
+ * `2to3.kernel_config_map_json.python.replacements_json_to_kernel`:
82
+ a list of pairs of strings, used as arguments to javascript's
83
+ `String.replace(from, to)` to translate from a json string into a valid
84
+ representation of the same string in the kernel language. Since json
85
+ strings are particularly simple, this can often (as with the python
86
+ language) be left as the default, an empty list.
87
+
88
+ * `2to3.kernel_config_map_json.python.prefix` and
89
+ `2to3.kernel_config_map_json.python.postfix`:
90
+ Strings added as bookends to the kernel string (translated from the json
91
+ string using the replacements above) to make up the kernel prettifier call
92
+ kernel's prettifier libraries.
93
+
94
+ * `2to3.kernel_config_map_json.python.trim_formatted_text`:
95
+ Whether to trim whitespace from the transformed cell text. Since jupyter
96
+ cells don't usually have leading or trailing whitespace, the default
97
+ behaviour is to trim the transformed text, in order to prevent the
98
+ transform adding extra newlines at the end (a common behaviour for source
99
+ files, where having a trailing newline is often considered good practice).
100
+
101
+
102
+ Internals
103
+ ---------
104
+
105
+ Under the hood, this nbextension uses the [KerneExecOnCells library], a shared
106
+ library for creating Jupyter nbextensions which transform code cell text using
107
+ calls to the active kernel.
108
+
109
+ See the [shared README] for the internal model used by the nbextension.
110
+
111
+ History
112
+ -------
113
+
114
+ The project was forked by [@EWouters] from [@jfbercher]'s [code_prettify],
115
+ retaining most of the code.
116
+
117
+ It has since been altered to use the [KerneExecOnCells library], a shared
118
+ library for creating Jupyter nbextensions which transform code cell text using
119
+ calls to the active kernel.
120
+
121
+ The 2to3 conversion's kernel-side python code is based on [2to3_nb.py] by
122
+ [@takluyver] and [@fperez].
123
+
124
+ It could be extended to use the [futurize] functions so it can convert both
125
+ ways.
126
+
127
+ [2to3_nb.py]: https://gist.github.com/takluyver/c8839593c615bb2f6e80
128
+ [@EWouters]: https://github.com/EWouters
129
+ [@fperez]: https://github.com/fperez
130
+ [@jfbercher]: https://github.com/jfbercher
131
+ [@takluyver]: https://github.com/takluyver
132
+ [code_prettify]: https://github.com/jfbercher/code_prettify
133
+ [futurize]: http://python-future.org/automatic_conversion.html
134
+ [fontawesome]: https://fontawesome.com/icons
135
+ [internals]: #Internals
136
+ [jupyter_nbextensions_configurator]: https://github.com/Jupyter-contrib/jupyter_nbextensions_configurator
137
+ [KerneExecOnCells library]: README.md
138
+ [lib2to3]: https://docs.python.org/3/library/2to3.html#module-lib2to3
139
+ [shared README]: README.md
.local/share/jupyter/nbextensions/code_prettify/README_code_prettify.md ADDED
@@ -0,0 +1,300 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ A Code Prettifier
2
+ =================
3
+
4
+ This nbextension reformats/prettifies code in notebook code cells.
5
+
6
+ Under the hood, it uses a call to the current notebook kernel to reformat the
7
+ code.
8
+ Thus the actual prettifier package has to be callable from the current kernel
9
+ language.
10
+
11
+ With an appropriately-configured prettifier for the kernel in use, the
12
+ nbextension provides
13
+
14
+ - a toolbar button (configurable to be added or not)
15
+
16
+ - a keyboard shortcut for reformatting the current code-cell (default shortcut
17
+ is `Ctrl-L`, can also be configured not to add the keyboard shortcut).
18
+
19
+ - a keyboard shortcut for reformatting the whole notebook (default shortcut
20
+ is `Ctrl-Shift-L`, can also be configured not to add the keyboard shortcut).
21
+
22
+ Syntax shall be correct. The nbextension may also point out basic syntax errors.
23
+
24
+ ![](demo-py.gif)
25
+ ![](demo-R.gif)
26
+ ![](demo-jv.gif)
27
+
28
+
29
+ Compatible Kernels
30
+ ------------------
31
+
32
+ Example implementations are provided for prettifiers for ipython, ir and
33
+ ijavascript kernels which should work out of the box (assuming availability of
34
+ the relevant kernel-specific [prerequisites] mentioned below), but the
35
+ kernel-specific prettifier calls are configurable, so the model is applicable
36
+ to essentially any kernel language and prettifier library.
37
+
38
+ Other languages may be added as defaults in the future, but given that there
39
+ are more than 50 [kernels] available for Jupyter, it is not easily possible to
40
+ support all of them out of the box, unless people with experience in the
41
+ relevant kernels have the time to contribute code. For information on how the
42
+ reformatting takes place, and how to adapt it for your particular
43
+ kernel/prettifier, see the [options] and [internals] sections below.
44
+ If you implement a language that isn't yet provided by default, please submit a
45
+ PR or let us know to add it to the repo :)
46
+
47
+ Under the hood, this nbextension's functionality is provided by the
48
+ [KerneExecOnCells library], a shared library for creating Jupyter nbextensions
49
+ which transform code cell text using calls to the active kernel.
50
+
51
+
52
+ Prerequisites
53
+ -------------
54
+
55
+ Of course, you must have the necessary kernel-specific packages installed for
56
+ the prettifier call to work:
57
+
58
+ - for the default python implementation, the [yapf] module is required:
59
+
60
+ pip install yapf
61
+
62
+ Others you might consider using include [autopep8] - see [README_autopep8.md].
63
+
64
+ - for R, the default implementation uses the [formatR] and [jsonlite] packages:
65
+
66
+ ```r
67
+ install.packages(c("formatR", "jsonlite"), repos="http://cran.rstudio.com")
68
+ ```
69
+
70
+ - for [ijavascript], the [js-beautify] package is used:
71
+ (*Under linux, in the root of your user tree = ~*)
72
+
73
+ npm install js-beautify
74
+
75
+ Under Windows, you may then need to set the `NODE_PATH` environment variable
76
+ (see [this question on stackoverflow]) to it to `%AppData%\npm\node_modules`
77
+ (Windows 7/8/10).
78
+ To be done with it once and for all, add this as a System variable in the
79
+ Advanced tab of the System Properties dialog.
80
+
81
+
82
+ Options
83
+ -------
84
+
85
+ All options are provided by the [KerneExecOnCells library]. - see the
86
+ [internals] section below for details.
87
+ There are a few nbextension-wide options, configurable using the
88
+ [jupyter_nbextensions_configurator] or by editing the `notebook` section config
89
+ file directly.
90
+ The options are as follows:
91
+
92
+ - `code_prettify.add_toolbar_button`:
93
+ Whether to add a toolbar button to transform the selected cell(s).
94
+ Defaults to `true`.
95
+
96
+ - `code_prettify.button_icon`:
97
+ A font-awesome class defining the icon used for the toolbar button and
98
+ actions. See [fontawesome] for available icon classes.
99
+ Defaults to `fa-legal`.
100
+
101
+ - `code_prettify.button_label`:
102
+ Toolbar button label text. Also used in the actions' help text.
103
+ Defaults to `Code prettify`.
104
+
105
+ - `code_prettify.register_hotkey`:
106
+ Whether to register hotkeys to transform the selected cell(s)/whole notebook.
107
+ Defaults to `true`.
108
+
109
+ - `code_prettify.hotkeys.process_all`:
110
+ Hotkey to use to transform all the code cells in the notebook.
111
+ Defaults to `Ctrl-Shift-L`.
112
+
113
+ - `code_prettify.hotkeys.process_selected`:
114
+ Hotkey to use to transform the selected cell(s).
115
+ Defaults to `Ctrl-L`.
116
+
117
+ - `code_prettify.show_alerts_for_not_supported_kernel`:
118
+ Whether to show alerts if the kernel is not supported.
119
+ Defaults to `false`.
120
+
121
+ - `code_prettify.show_alerts_for_errors`:
122
+ Whether to show alerts for errors in the kernel calls.
123
+ Defaults to `true`.
124
+
125
+ - `code_prettify.kernel_config_map_json`:
126
+ The value of this key is a string which can be parsed into a json object
127
+ giving the config for each kernel language.
128
+
129
+ The following give the per-kernel options of the parsed json, using the
130
+ language key `python `:
131
+
132
+ * `code_prettify.kernel_config_map_json.python.library`:
133
+ String to execute in the kernel in order to load any necessary kernel
134
+ libraries.
135
+
136
+ * `code_prettify.kernel_config_map_json.python.replacements_json_to_kernel`:
137
+ a list of pairs of strings, used as arguments to javascript's
138
+ `String.replace(from, to)` to translate from a json string into a valid
139
+ representation of the same string in the kernel language. Since json
140
+ strings are particularly simple, this can often (as with the python
141
+ language) be left as the default, an empty list.
142
+
143
+ * `code_prettify.kernel_config_map_json.python.prefix` and
144
+ `code_prettify.kernel_config_map_json.python.postfix`:
145
+ Strings added as bookends to the kernel string (translated from the json
146
+ string using the replacements above) to make up the kernel prettifier call
147
+ kernel's prettifier libraries.
148
+
149
+ * `code_prettify.kernel_config_map_json.python.trim_formatted_text`:
150
+ Whether to trim whitespace from the transformed cell text. Since jupyter
151
+ cells don't usually have leading or trailing whitespace, the default
152
+ behaviour is to trim the transformed text, in order to prevent the
153
+ transform adding extra newlines at the end (a common behaviour for source
154
+ files, where having a trailing newline is often considered good practice).
155
+
156
+
157
+ Internals
158
+ ---------
159
+
160
+ Under the hood, this nbextension uses the [KerneExecOnCells library], a shared
161
+ library for creating Jupyter nbextensions which transform code cell text using
162
+ calls to the active kernel.
163
+
164
+ The model is essentially:
165
+
166
+ 1. The cell text is grabbed by client-side javascript, then turned into a json
167
+ string using javascript `JSON.stringify`. Since json-compatible strings are
168
+ a particularly simple string format, which is compatible with many other
169
+ programming languages without much modification (e.g. a valid json string
170
+ is also a valid string in python 3, and also in python 2 when prefixed with
171
+ a `u`), and easily converted for use in others (because of its simplicity).
172
+
173
+ 2. Optional regex replacements are used to translate the json-format string
174
+ into a valid kernel string. Python, R and javascript don't require this
175
+ step, but other languages may do, so it's implemented for flexibility
176
+ using the per-kernel config key `replacements_json_to_kernel`, which is a
177
+ list of pairs of arguments to javascript `String.replace`.
178
+
179
+ 3. The kernel-specific prettifier call is then composed from
180
+ `kernel_config.prefix` + `kernel_text_string` + `kernel_config.postfix` and
181
+ sent to the kernel for execution. This kernel call is expected to get the
182
+ formatted cell text _printed_ as a json-compatible string. Since most
183
+ kernel languages have json packages, this should hopefully be easy to
184
+ arrange. The reason for the printing text rather than simply displaying it,
185
+ is that it prevents us having to translate from a kernel string
186
+ representing a json string.
187
+
188
+ 4. The callback for the kernel execution in client-side javascript parses the
189
+ printed json-format string, optionally trims trailing whitespace according
190
+ to the `trim_formatted_text` key (which defaults to `true`) in the
191
+ per-kernel config, and then sets the cell text using the result.
192
+
193
+ The process is probably best illustrated using an example for the python
194
+ implementation:
195
+
196
+ 1. **At nbextension load**, the `code_prettify.kernel_config_map_json` config
197
+ option is parsed to give the json object
198
+
199
+ ```json
200
+ {
201
+ "python": {
202
+ "library": "import json\nimport yapf.yapflib.yapf_api",
203
+ "prefix": "print(json.dumps(yapf.yapflib.yapf_api.FormatCode(u",
204
+ "postfix": ")[0]))"
205
+ }
206
+ }
207
+ ```
208
+
209
+ (other kernel languages are omitted for clarity).
210
+
211
+ 2. **On kernel becoming ready**, the nbextension looks up the config for the
212
+ kernel's language (in our example, this is the `python` key of the kernel
213
+ config json object above). It then sends the kernel config's `library`
214
+ string to the kernel for execution. Thus the python implementation above
215
+ executes
216
+
217
+ ```python
218
+ import json
219
+ import yapf.yapflib.yapf_api
220
+ ```
221
+
222
+ 3. **On requesting a cell be prettified** which can happen by clicking the
223
+ toolbar, or with a (configurable) hotkey, the following happens:
224
+
225
+ Say the cell to be formatted contains the following ugly python code:
226
+
227
+ ```python
228
+ msg= 'hello '+"world"
229
+ print (
230
+ msg )
231
+ ```
232
+
233
+ Then the result of the `JSON.stringify` call will be a string containing
234
+
235
+ ```json
236
+ "msg= 'hello '+\"world\"\nprint (\n msg )"
237
+ ```
238
+
239
+ (note the opening and closing quotes). Concatenating this with the prefix &
240
+ postfix strings from the python kernel config above, gives us the kernel
241
+ code to execute. The call sent to the python kernel is therefore
242
+
243
+ ```python
244
+ print(json.dumps(yapf.yapflib.yapf_api.FormatCode(u"msg= 'hello '+\"world\"\nprint (\n msg )")[0]))
245
+ ```
246
+
247
+ 4. What gets 'printed' by the kernel (i.e. returned to the javascript stream
248
+ callback) is the following json-format string:
249
+
250
+ ```json
251
+ "msg = 'hello ' + \"world\"\nprint(msg)\n"
252
+ ```
253
+
254
+ The default is to trim whitepace from the returned prettified text, which
255
+ results in the final prettified python code for the cell:
256
+
257
+ ```python
258
+ msg = 'hello ' + "world"
259
+ print(msg)
260
+ ```
261
+
262
+
263
+ History
264
+ -------
265
+
266
+ - [@jfbercher], august 14, 2016, first version, named `yapf_ext`
267
+ - [@jfbercher], august 19, 2016, second version `code_prettify`
268
+ - introduced support for R and javascript.
269
+ - changed extension name from `yapf_ext` to `code_prettify`
270
+ - [@jcb91], december 2016
271
+ - made addition of toolbar button & hotkey configurable
272
+ - reworked to avoid regex replacements for conversion to/from kernel string
273
+ formats, in favour of json-string interchange
274
+ - made kernel-specific prettifier calls configurable, allowing support for
275
+ different prettifiers & arbitrary kernels
276
+ - improved documentation
277
+ - [@jfbercher], december 2016-january 2017
278
+ - added a configurable shortkey to reflow the whole notebook
279
+ - extracted most of the code to build a general library of functions,
280
+ `kernel_exec_on_cell.js`, which can be used for all nbextensions which
281
+ needs to exec some code (via the current kernel) on the text from cells.
282
+
283
+
284
+ [@jcb91]: https://github.com/jcb91
285
+ [@jfbercher]: https://github.com/jfbercher
286
+ [autopep8]: https://github.com/hhatto/autopep8
287
+ [formatR]: https://yihui.name/formatr
288
+ [fontawesome]: https://fontawesome.com/icons
289
+ [ijavascript]: https://n-riesco.github.io/ijavascript
290
+ [internals]: #Internals
291
+ [js-beautify]: https://github.com/beautify-web/js-beautify
292
+ [jsonlite]: https://github.com/jeroen/jsonlite
293
+ [jupyter_nbextensions_configurator]: https://github.com/Jupyter-contrib/jupyter_nbextensions_configurator
294
+ [KerneExecOnCells library]: README.md
295
+ [kernels]: https://github.com/ipython/ipython/wiki/IPython-kernels-for-other-languages
296
+ [options]: #Options
297
+ [prerequisites]: #Prerequisites
298
+ [README_autopep8.md]: README_autopep8.md
299
+ [this question on stackoverflow]: https://stackoverflow.com/questions/9587665/nodejs-cannot-find-installed-module-on-windows
300
+ [yapf]: https://github.com/google/yapf
.local/share/jupyter/nbextensions/code_prettify/demo-py.gif ADDED
.local/share/jupyter/nbextensions/codefolding/codefolding_firstline_folded.png ADDED
.local/share/jupyter/nbextensions/codefolding/codefolding_firstline_unfolded.png ADDED
.local/share/jupyter/nbextensions/codefolding/codefolding_indent_folded_1.png ADDED
.local/share/jupyter/nbextensions/codefolding/edit.js ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ define(['./main'], function (codefolding) {
2
+ "use strict";
3
+ return codefolding;
4
+ });
.local/share/jupyter/nbextensions/codefolding/foldgutter.css ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ .CodeMirror-foldgutter {
2
+ width: .9em;
3
+ }
4
+
5
+
.local/share/jupyter/nbextensions/codemirror_mode_extensions/codemirror_mode_extensions.yaml ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ Type: Jupyter Notebook Extension
2
+ Name: CodeMirror mode extensions
3
+ Description: |
4
+ Extends some CodeMirror modes with extra features. Currently just adds
5
+ support for comment/uncomment and folding for octave/MATLAB mode.
6
+ Main: main.js
7
+ Compatibility: 4.x
.local/share/jupyter/nbextensions/collapsible_headings/icon.png ADDED
.local/share/jupyter/nbextensions/collapsible_headings/main.css ADDED
@@ -0,0 +1,130 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .collapsible_headings_toggle .h1 {
2
+ font-size: 185.7%;
3
+ margin: 0.538em 0 0 0;
4
+ line-height: 1.0;
5
+ }
6
+ .collapsible_headings_toggle .h2 {
7
+ font-size: 157.1%;
8
+ margin: 0.636em 0 0 0;
9
+ line-height: 1.0;
10
+ }
11
+ .collapsible_headings_toggle .h3 {
12
+ font-size: 128.6%;
13
+ margin: 0.777em 0 0 0;
14
+ line-height: 1.0;
15
+ }
16
+ .collapsible_headings_toggle .h4,
17
+ .collapsible_headings_toggle .h5,
18
+ .collapsible_headings_toggle .h6 {
19
+ font-size: 100%;
20
+ margin: 1em 0 0 0;
21
+ line-height: 1.0;
22
+ }
23
+
24
+ .collapsible_headings_toggle.btn .h1,
25
+ .collapsible_headings_toggle.btn .h2,
26
+ .collapsible_headings_toggle.btn .h3,
27
+ .collapsible_headings_toggle.btn .h4,
28
+ .collapsible_headings_toggle.btn .h5,
29
+ .collapsible_headings_toggle.btn .h6 {
30
+ margin-top: 0;
31
+ }
32
+
33
+ .collapsible_headings_toggle .fa {
34
+ transition: transform 400ms;
35
+
36
+ /* don't support IE filter, since can't rotate 360 */
37
+ -webkit-transform: rotate(360deg);
38
+ -moz-transform: rotate(360deg);
39
+ -ms-transform: rotate(360deg);
40
+ -o-transform: rotate(360deg);
41
+ transform: rotate(360deg);
42
+ }
43
+
44
+ .collapsible_headings_collapsed .fa {
45
+ -webkit-transform: none;
46
+ -moz-transform: none;
47
+ -ms-transform: none;
48
+ -o-transform: none;
49
+ transform: none;
50
+ }
51
+
52
+ /* bracket rules */
53
+
54
+ div.cell {
55
+ position: relative;
56
+ }
57
+
58
+ .chb {
59
+ position: absolute;
60
+ top: -1px;
61
+ bottom: -1px;
62
+ left: calc(100% + 3px);
63
+ display: flex;
64
+ flex-direction: row-reverse;
65
+ justify-content: flex-start;
66
+ align-items: stretch;
67
+ }
68
+
69
+ .chb div {
70
+ margin-left: 2px;
71
+ width: 5px;
72
+ border-color: #aaa;
73
+ border-left-color: transparent;
74
+ border-style: solid;
75
+ border-width: 0 2px 0 2px;
76
+ }
77
+
78
+ .collapsible_headings_collapsed .chb .chb-start {
79
+ border-width: 5px 2px 2px 4px;
80
+ }
81
+
82
+ .chb div:hover,
83
+ .chb .chb-hover,
84
+ .jupyter-soft-selected .chb div{
85
+ border-color: #42A5F5;
86
+ border-left-color: transparent;
87
+ border-width: 0 3px 0 0;
88
+ }
89
+
90
+ .chb .chb-start {
91
+ border-top-width: 1px;
92
+ margin-top: 2px;
93
+ }
94
+
95
+ .chb .chb-end {
96
+ border-bottom-width: 1px;
97
+ margin-bottom: 2px;
98
+ }
99
+
100
+ .chb-start div:hover, .chb .chb-start.chb-hover, .jupyter-soft-selected .chb .chb-start {
101
+ border-top-width: 2px;
102
+ }
103
+
104
+ .chb-end div:hover, .chb .chb-end.chb-hover, .jupyter-soft-selected .chb .chb-end {
105
+ border-bottom-width: 2px;
106
+ }
107
+
108
+ /* ellipsis rules */
109
+ .collapsible_headings_ellipsis .rendered_html h1,
110
+ .collapsible_headings_ellipsis .rendered_html h2,
111
+ .collapsible_headings_ellipsis .rendered_html h3,
112
+ .collapsible_headings_ellipsis .rendered_html h4,
113
+ .collapsible_headings_ellipsis .rendered_html h5,
114
+ .collapsible_headings_ellipsis .rendered_html h6 {
115
+ position: relative;
116
+ padding-right: 2em;
117
+ }
118
+
119
+ .collapsible_headings_collapsed.collapsible_headings_ellipsis .rendered_html h1:after,
120
+ .collapsible_headings_collapsed.collapsible_headings_ellipsis .rendered_html h2:after,
121
+ .collapsible_headings_collapsed.collapsible_headings_ellipsis .rendered_html h3:after,
122
+ .collapsible_headings_collapsed.collapsible_headings_ellipsis .rendered_html h4:after,
123
+ .collapsible_headings_collapsed.collapsible_headings_ellipsis .rendered_html h5:after,
124
+ .collapsible_headings_collapsed.collapsible_headings_ellipsis .rendered_html h6:after {
125
+ position: absolute;
126
+ right: 0;
127
+ bottom: 0;
128
+ content: "[\002026]";
129
+ color: #aaa;
130
+ }
.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/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/execute_time/ExecuteTime.js ADDED
@@ -0,0 +1,349 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ // Copyright (C) 2014 Jean-Christophe Jaskula
3
+ // 2015 [email protected]
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/execution-timings-menu.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/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/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/help_panel/help_panel_ext.png ADDED
.local/share/jupyter/nbextensions/help_panel/icon.png ADDED
.local/share/jupyter/nbextensions/hide_header/hide_header.yaml ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Type: IPython Notebook Extension
2
+ Name: Hide Header
3
+ Link: README.md
4
+ Description: Toggle visibility of all of header, menubar, toolbar using a hotkey
5
+ Main: main.js
6
+ Compatibility: 4.x, 5.x
7
+ Parameters:
8
+ - name: header_toggle
9
+ description: keybinding for toggling header visibility
10
+ input_type: hotkey
11
+ default: ctrl-H
12
+
.local/share/jupyter/nbextensions/hide_input_all/hide_input_all.yaml ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ Type: IPython Notebook Extension
2
+ Compatibility: 3.x 4.x 5.x
3
+ Main: main.js
4
+ Name: Hide input all
5
+ Description: "toggle display of all code cells' inputs"
6
+ Icon: icon.png
7
+ Link: readme.md
.local/share/jupyter/nbextensions/hide_input_all/hide_input_all_show.png ADDED
.local/share/jupyter/nbextensions/hide_input_all/icon.png ADDED
.local/share/jupyter/nbextensions/hide_input_all/main.js ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 set_input_visible(show) {
15
+ Jupyter.notebook.metadata.hide_input = !show;
16
+
17
+ if (show) $('div.input').show('slow');
18
+ else $('div.input').hide('slow');
19
+
20
+ var btn = $('#toggle_codecells');
21
+ btn.toggleClass('active', !show);
22
+
23
+ var icon = btn.find('i');
24
+ icon.toggleClass('fa-eye', show);
25
+ icon.toggleClass('fa-eye-slash', !show);
26
+ $('#toggle_codecells').attr(
27
+ 'title', (show ? 'Hide' : 'Show') + ' codecell inputs');
28
+ }
29
+
30
+ function toggle() {
31
+ set_input_visible($('#toggle_codecells').hasClass('active'));
32
+ }
33
+
34
+ function initialize () {
35
+ set_input_visible(Jupyter.notebook.metadata.hide_input !== true);
36
+ }
37
+
38
+ var load_ipython_extension = function() {
39
+ $(Jupyter.toolbar.add_buttons_group([
40
+ Jupyter.keyboard_manager.actions.register({
41
+ help : 'Hide codecell inputs',
42
+ icon : 'fa-eye',
43
+ handler: function() {
44
+ toggle();
45
+ setTimeout(function() { $('#toggle_codecells').blur(); }, 500);
46
+ }
47
+ }, 'hide-codecell-inputs', 'hide_input_all'),
48
+ ])).find('.btn').attr('id', 'toggle_codecells');
49
+ if (Jupyter.notebook !== undefined && Jupyter.notebook._fully_loaded) {
50
+ // notebook_loaded.Notebook event has already happened
51
+ initialize();
52
+ }
53
+ events.on('notebook_loaded.Notebook', initialize);
54
+ };
55
+
56
+ return {
57
+ load_ipython_extension : load_ipython_extension
58
+ };
59
+ });
.local/share/jupyter/nbextensions/hide_input_all/readme.md ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Hide all Input
2
+ ==============
3
+ This extension allows hiding all codecells of a notebook. This can be achieved by clicking on the button toolbar:
4
+
5
+ ![](icon.png)
6
+
7
+ Typically, all codecells are shown with their corresponding output:
8
+
9
+ ![](hide_input_all_show.png)
10
+
11
+ Clicking on the "Toggle codecell display" toolbar button hides all codecells:
12
+
13
+ ![](hide_input_all_hide.png)
14
+
15
+
16
+ Internals
17
+ ---------
18
+
19
+ The codecell hiding state is stored in the metadata `IPython.notebook.metadata.hide_input`.
20
+ If it is set to `true`, all codecells will be hidden on reload.
21
+
22
+ The `nbextensions.tpl` template is provided in the
23
+ `jupyter_contrib_nbextensions.nbconvert_support` templates directory (see the
24
+ docs mentioned above for how to find it)
25
+
26
+ To use, add the template to your `nbconvert` call:
27
+
28
+ jupyter nbconvert --template=nbextensions --to=html my_notebook.ipynb
29
+
30
+ The nbextensions template will respect the `nb.metadata.hide_input` flag, and
31
+ filter the cell's output prompt (the bit that looks like `Out[27]:`).
32
+ The filter is only used for html output, not for PDF or LaTeX output.
33
+
34
+ If you want to _keep_ the cell output prompt, you will have to remove the lines
35
+
36
+ {% block output_group -%}
37
+ {%- if cell.metadata.hide_output or nb.metadata.hide_input -%}
38
+ {%- else -%}
39
+ {{ super() }}
40
+ {%- endif -%}
41
+ {% endblock output_group %}
42
+
43
+ in the `nbextensions.tpl` file.
44
+
.local/share/jupyter/nbextensions/highlighter/demo_highlighter.ipynb ADDED
@@ -0,0 +1,96 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "cells": [
3
+ {
4
+ "cell_type": "markdown",
5
+ "metadata": {},
6
+ "source": [
7
+ "\n",
8
+ "## The highlighter extension:\n",
9
+ "\n",
10
+ "- Firstable, the extension provides <span class=\"mark\">several toolbar buttons</span> for highlighting a selected text _within a markdown cell_. Three different \\`color schemes' are provided, which can be easily customized in the \\textit{stylesheet} `highlighter.css`. The last button enables to remove all highlightings in the current cell. \n",
11
+ "- This works both <span class=\"burk\">when the cell is _rendered_ and when the cell is in edit mode</span>; \n",
12
+ "- In both modes, it is possible to highlight formatted portions of text (In rendered mode, since the selected text loose its formatting, an heuristic is applied to find the best alignment with the actual text)\n",
13
+ "- When no text is selected, the whole cell is highlighted; \n",
14
+ "- The extension also provides two keyboard shortcuts (Alt-G and Alt-H) which fire the highlighting of the selected text. \n",
15
+ "- Highlights can be preserved when exporting to html or to LaTeX -- details are provided in [export_highlights](export_highlights.ipynb)\n",
16
+ "\n",
17
+ "\n",
18
+ "![](image.gif)\n",
19
+ "\n",
20
+ "## Installation:\n",
21
+ "\n",
22
+ "The extension can be installed with the nice UI available on jupyter_contrib_nbextensions website, which also allows to enable/disable the extension. \n",
23
+ "\n",
24
+ "You may also install the extension from the original repo: issue\n",
25
+ "```bash\n",
26
+ "jupyter nbextension install https://rawgit.com/jfbercher/small_nbextensions/master/highlighter.zip --user\n",
27
+ "\n",
28
+ "```\n",
29
+ "at the command line.\n",
30
+ "\n",
31
+ "### Testing: \n",
32
+ "\n",
33
+ "Use a code cell with\n",
34
+ "```javascript\n",
35
+ "%%javascript\n",
36
+ "require(\"base/js/utils\").load_extensions(\"highlighter/highlighter\")\n",
37
+ "```\n",
38
+ "\n",
39
+ "### Automatic load\n",
40
+ "You may also automatically load the extension for any notebook via\n",
41
+ "```bash\n",
42
+ "jupyter nbextension enable highlighter/highlighter\t\n",
43
+ "```\n"
44
+ ]
45
+ },
46
+ {
47
+ "cell_type": "code",
48
+ "execution_count": 2,
49
+ "metadata": {
50
+ "collapsed": false
51
+ },
52
+ "outputs": [
53
+ {
54
+ "data": {
55
+ "application/javascript": [
56
+ "require(\"base/js/utils\").load_extensions(\"highlighter/highlighter\")"
57
+ ],
58
+ "text/plain": [
59
+ "<IPython.core.display.Javascript object>"
60
+ ]
61
+ },
62
+ "metadata": {},
63
+ "output_type": "display_data"
64
+ }
65
+ ],
66
+ "source": [
67
+ "%%javascript\n",
68
+ "require(\"base/js/utils\").load_extensions(\"highlighter/highlighter\")"
69
+ ]
70
+ }
71
+ ],
72
+ "metadata": {
73
+ "interactive_sols": {
74
+ "cbx_id": 1
75
+ },
76
+ "kernelspec": {
77
+ "display_name": "Python 3",
78
+ "language": "python",
79
+ "name": "python3"
80
+ },
81
+ "language_info": {
82
+ "codemirror_mode": {
83
+ "name": "ipython",
84
+ "version": 3
85
+ },
86
+ "file_extension": ".py",
87
+ "mimetype": "text/x-python",
88
+ "name": "python",
89
+ "nbconvert_exporter": "python",
90
+ "pygments_lexer": "ipython3",
91
+ "version": "3.4.3+"
92
+ },
93
+ },
94
+ "nbformat": 4,
95
+ "nbformat_minor": 0
96
+ }
.local/share/jupyter/nbextensions/highlighter/export_highlights.pdf ADDED
Binary file (116 kB). View file
 
.local/share/jupyter/nbextensions/highlighter/export_highlights.tex ADDED
@@ -0,0 +1,457 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ % Default to the notebook output style
3
+
4
+
5
+ % Inherit from the specified cell style.
6
+
7
+
8
+
9
+
10
+
11
+
12
+ \documentclass{article}
13
+
14
+
15
+
16
+
17
+ \usepackage{graphicx} % Used to insert images
18
+ \usepackage{adjustbox} % Used to constrain images to a maximum size
19
+ \usepackage{color} % Allow colors to be defined
20
+ \usepackage{enumerate} % Needed for markdown enumerations to work
21
+ \usepackage{geometry} % Used to adjust the document margins
22
+ \usepackage{amsmath} % Equations
23
+ \usepackage{amssymb} % Equations
24
+ \usepackage{eurosym} % defines \euro
25
+ \usepackage[mathletters]{ucs} % Extended unicode (utf-8) support
26
+ \usepackage[utf8x]{inputenc} % Allow utf-8 characters in the tex document
27
+ \usepackage{fancyvrb} % verbatim replacement that allows latex
28
+ \usepackage{grffile} % extends the file name processing of package graphics
29
+ % to support a larger range
30
+ % The hyperref package gives us a pdf with properly built
31
+ % internal navigation ('pdf bookmarks' for the table of contents,
32
+ % internal cross-reference links, web links for URLs, etc.)
33
+ \usepackage{hyperref}
34
+ \usepackage{longtable} % longtable support required by pandoc >1.10
35
+ \usepackage{booktabs} % table support for pandoc > 1.12.2
36
+
37
+ \usepackage{color}
38
+ \usepackage{soul}
39
+ \usepackage[framemethod=tikz]{mdframed}
40
+
41
+
42
+
43
+
44
+ \definecolor{orange}{cmyk}{0,0.4,0.8,0.2}
45
+ \definecolor{darkorange}{rgb}{.71,0.21,0.01}
46
+ \definecolor{darkgreen}{rgb}{.12,.54,.11}
47
+ \definecolor{myteal}{rgb}{.26, .44, .56}
48
+ \definecolor{gray}{gray}{0.45}
49
+ \definecolor{lightgray}{gray}{.95}
50
+ \definecolor{mediumgray}{gray}{.8}
51
+ \definecolor{inputbackground}{rgb}{.95, .95, .85}
52
+ \definecolor{outputbackground}{rgb}{.95, .95, .95}
53
+ \definecolor{traceback}{rgb}{1, .95, .95}
54
+ % ansi colors
55
+ \definecolor{red}{rgb}{.6,0,0}
56
+ \definecolor{green}{rgb}{0,.65,0}
57
+ \definecolor{brown}{rgb}{0.6,0.6,0}
58
+ \definecolor{blue}{rgb}{0,.145,.698}
59
+ \definecolor{purple}{rgb}{.698,.145,.698}
60
+ \definecolor{cyan}{rgb}{0,.698,.698}
61
+ \definecolor{lightgray}{gray}{0.5}
62
+
63
+ % bright ansi colors
64
+ \definecolor{darkgray}{gray}{0.25}
65
+ \definecolor{lightred}{rgb}{1.0,0.39,0.28}
66
+ \definecolor{lightgreen}{rgb}{0.48,0.99,0.0}
67
+ \definecolor{lightblue}{rgb}{0.53,0.81,0.92}
68
+ \definecolor{lightpurple}{rgb}{0.87,0.63,0.87}
69
+ \definecolor{lightcyan}{rgb}{0.5,1.0,0.83}
70
+
71
+ % commands and environments needed by pandoc snippets
72
+ % extracted from the output of `pandoc -s`
73
+ \providecommand{\tightlist}{%
74
+ \setlength{\itemsep}{0pt}\setlength{\parskip}{0pt}}
75
+ \DefineVerbatimEnvironment{Highlighting}{Verbatim}{commandchars=\\\{\}}
76
+ % Add ',fontsize=\small' for more characters per line
77
+ \newenvironment{Shaded}{}{}
78
+ \newcommand{\KeywordTok}[1]{\textcolor[rgb]{0.00,0.44,0.13}{\textbf{{#1}}}}
79
+ \newcommand{\DataTypeTok}[1]{\textcolor[rgb]{0.56,0.13,0.00}{{#1}}}
80
+ \newcommand{\DecValTok}[1]{\textcolor[rgb]{0.25,0.63,0.44}{{#1}}}
81
+ \newcommand{\BaseNTok}[1]{\textcolor[rgb]{0.25,0.63,0.44}{{#1}}}
82
+ \newcommand{\FloatTok}[1]{\textcolor[rgb]{0.25,0.63,0.44}{{#1}}}
83
+ \newcommand{\CharTok}[1]{\textcolor[rgb]{0.25,0.44,0.63}{{#1}}}
84
+ \newcommand{\StringTok}[1]{\textcolor[rgb]{0.25,0.44,0.63}{{#1}}}
85
+ \newcommand{\CommentTok}[1]{\textcolor[rgb]{0.38,0.63,0.69}{\textit{{#1}}}}
86
+ \newcommand{\OtherTok}[1]{\textcolor[rgb]{0.00,0.44,0.13}{{#1}}}
87
+ \newcommand{\AlertTok}[1]{\textcolor[rgb]{1.00,0.00,0.00}{\textbf{{#1}}}}
88
+ \newcommand{\FunctionTok}[1]{\textcolor[rgb]{0.02,0.16,0.49}{{#1}}}
89
+ \newcommand{\RegionMarkerTok}[1]{{#1}}
90
+ \newcommand{\ErrorTok}[1]{\textcolor[rgb]{1.00,0.00,0.00}{\textbf{{#1}}}}
91
+ \newcommand{\NormalTok}[1]{{#1}}
92
+
93
+ % Define a nice break command that doesn't care if a line doesn't already
94
+ % exist.
95
+ \def\br{\hspace*{\fill} \\* }
96
+ % Math Jax compatability definitions
97
+ \def\gt{>}
98
+ \def\lt{<}
99
+ % Document parameters
100
+ \title{export\_highlights}
101
+
102
+
103
+ \author{}
104
+
105
+
106
+
107
+ % Pygments definitions
108
+
109
+ \makeatletter
110
+ \def\PY@reset{\let\PY@it=\relax \let\PY@bf=\relax%
111
+ \let\PY@ul=\relax \let\PY@tc=\relax%
112
+ \let\PY@bc=\relax \let\PY@ff=\relax}
113
+ \def\PY@tok#1{\csname PY@tok@#1\endcsname}
114
+ \def\PY@toks#1+{\ifx\relax#1\empty\else%
115
+ \PY@tok{#1}\expandafter\PY@toks\fi}
116
+ \def\PY@do#1{\PY@bc{\PY@tc{\PY@ul{%
117
+ \PY@it{\PY@bf{\PY@ff{#1}}}}}}}
118
+ \def\PY#1#2{\PY@reset\PY@toks#1+\relax+\PY@do{#2}}
119
+
120
+ \expandafter\def\csname PY@tok@gp\endcsname{\let\PY@bf=\textbf\def\PY@tc##1{\textcolor[rgb]{0.00,0.00,0.50}{##1}}}
121
+ \expandafter\def\csname PY@tok@no\endcsname{\def\PY@tc##1{\textcolor[rgb]{0.53,0.00,0.00}{##1}}}
122
+ \expandafter\def\csname PY@tok@il\endcsname{\def\PY@tc##1{\textcolor[rgb]{0.40,0.40,0.40}{##1}}}
123
+ \expandafter\def\csname PY@tok@sx\endcsname{\def\PY@tc##1{\textcolor[rgb]{0.00,0.50,0.00}{##1}}}
124
+ \expandafter\def\csname PY@tok@kt\endcsname{\def\PY@tc##1{\textcolor[rgb]{0.69,0.00,0.25}{##1}}}
125
+ \expandafter\def\csname PY@tok@mo\endcsname{\def\PY@tc##1{\textcolor[rgb]{0.40,0.40,0.40}{##1}}}
126
+ \expandafter\def\csname PY@tok@sh\endcsname{\def\PY@tc##1{\textcolor[rgb]{0.73,0.13,0.13}{##1}}}
127
+ \expandafter\def\csname PY@tok@bp\endcsname{\def\PY@tc##1{\textcolor[rgb]{0.00,0.50,0.00}{##1}}}
128
+ \expandafter\def\csname PY@tok@gd\endcsname{\def\PY@tc##1{\textcolor[rgb]{0.63,0.00,0.00}{##1}}}
129
+ \expandafter\def\csname PY@tok@sb\endcsname{\def\PY@tc##1{\textcolor[rgb]{0.73,0.13,0.13}{##1}}}
130
+ \expandafter\def\csname PY@tok@err\endcsname{\def\PY@bc##1{\setlength{\fboxsep}{0pt}\fcolorbox[rgb]{1.00,0.00,0.00}{1,1,1}{\strut ##1}}}
131
+ \expandafter\def\csname PY@tok@nd\endcsname{\def\PY@tc##1{\textcolor[rgb]{0.67,0.13,1.00}{##1}}}
132
+ \expandafter\def\csname PY@tok@gr\endcsname{\def\PY@tc##1{\textcolor[rgb]{1.00,0.00,0.00}{##1}}}
133
+ \expandafter\def\csname PY@tok@kd\endcsname{\let\PY@bf=\textbf\def\PY@tc##1{\textcolor[rgb]{0.00,0.50,0.00}{##1}}}
134
+ \expandafter\def\csname PY@tok@s\endcsname{\def\PY@tc##1{\textcolor[rgb]{0.73,0.13,0.13}{##1}}}
135
+ \expandafter\def\csname PY@tok@cs\endcsname{\let\PY@it=\textit\def\PY@tc##1{\textcolor[rgb]{0.25,0.50,0.50}{##1}}}
136
+ \expandafter\def\csname PY@tok@sd\endcsname{\let\PY@it=\textit\def\PY@tc##1{\textcolor[rgb]{0.73,0.13,0.13}{##1}}}
137
+ \expandafter\def\csname PY@tok@ss\endcsname{\def\PY@tc##1{\textcolor[rgb]{0.10,0.09,0.49}{##1}}}
138
+ \expandafter\def\csname PY@tok@nn\endcsname{\let\PY@bf=\textbf\def\PY@tc##1{\textcolor[rgb]{0.00,0.00,1.00}{##1}}}
139
+ \expandafter\def\csname PY@tok@w\endcsname{\def\PY@tc##1{\textcolor[rgb]{0.73,0.73,0.73}{##1}}}
140
+ \expandafter\def\csname PY@tok@kn\endcsname{\let\PY@bf=\textbf\def\PY@tc##1{\textcolor[rgb]{0.00,0.50,0.00}{##1}}}
141
+ \expandafter\def\csname PY@tok@sc\endcsname{\def\PY@tc##1{\textcolor[rgb]{0.73,0.13,0.13}{##1}}}
142
+ \expandafter\def\csname PY@tok@s1\endcsname{\def\PY@tc##1{\textcolor[rgb]{0.73,0.13,0.13}{##1}}}
143
+ \expandafter\def\csname PY@tok@ge\endcsname{\let\PY@it=\textit}
144
+ \expandafter\def\csname PY@tok@cp\endcsname{\def\PY@tc##1{\textcolor[rgb]{0.74,0.48,0.00}{##1}}}
145
+ \expandafter\def\csname PY@tok@gh\endcsname{\let\PY@bf=\textbf\def\PY@tc##1{\textcolor[rgb]{0.00,0.00,0.50}{##1}}}
146
+ \expandafter\def\csname PY@tok@gi\endcsname{\def\PY@tc##1{\textcolor[rgb]{0.00,0.63,0.00}{##1}}}
147
+ \expandafter\def\csname PY@tok@vc\endcsname{\def\PY@tc##1{\textcolor[rgb]{0.10,0.09,0.49}{##1}}}
148
+ \expandafter\def\csname PY@tok@si\endcsname{\let\PY@bf=\textbf\def\PY@tc##1{\textcolor[rgb]{0.73,0.40,0.53}{##1}}}
149
+ \expandafter\def\csname PY@tok@ow\endcsname{\let\PY@bf=\textbf\def\PY@tc##1{\textcolor[rgb]{0.67,0.13,1.00}{##1}}}
150
+ \expandafter\def\csname PY@tok@vg\endcsname{\def\PY@tc##1{\textcolor[rgb]{0.10,0.09,0.49}{##1}}}
151
+ \expandafter\def\csname PY@tok@sr\endcsname{\def\PY@tc##1{\textcolor[rgb]{0.73,0.40,0.53}{##1}}}
152
+ \expandafter\def\csname PY@tok@cm\endcsname{\let\PY@it=\textit\def\PY@tc##1{\textcolor[rgb]{0.25,0.50,0.50}{##1}}}
153
+ \expandafter\def\csname PY@tok@c\endcsname{\let\PY@it=\textit\def\PY@tc##1{\textcolor[rgb]{0.25,0.50,0.50}{##1}}}
154
+ \expandafter\def\csname PY@tok@mi\endcsname{\def\PY@tc##1{\textcolor[rgb]{0.40,0.40,0.40}{##1}}}
155
+ \expandafter\def\csname PY@tok@kc\endcsname{\let\PY@bf=\textbf\def\PY@tc##1{\textcolor[rgb]{0.00,0.50,0.00}{##1}}}
156
+ \expandafter\def\csname PY@tok@ne\endcsname{\let\PY@bf=\textbf\def\PY@tc##1{\textcolor[rgb]{0.82,0.25,0.23}{##1}}}
157
+ \expandafter\def\csname PY@tok@nf\endcsname{\def\PY@tc##1{\textcolor[rgb]{0.00,0.00,1.00}{##1}}}
158
+ \expandafter\def\csname PY@tok@go\endcsname{\def\PY@tc##1{\textcolor[rgb]{0.53,0.53,0.53}{##1}}}
159
+ \expandafter\def\csname PY@tok@m\endcsname{\def\PY@tc##1{\textcolor[rgb]{0.40,0.40,0.40}{##1}}}
160
+ \expandafter\def\csname PY@tok@mh\endcsname{\def\PY@tc##1{\textcolor[rgb]{0.40,0.40,0.40}{##1}}}
161
+ \expandafter\def\csname PY@tok@nc\endcsname{\let\PY@bf=\textbf\def\PY@tc##1{\textcolor[rgb]{0.00,0.00,1.00}{##1}}}
162
+ \expandafter\def\csname PY@tok@mb\endcsname{\def\PY@tc##1{\textcolor[rgb]{0.40,0.40,0.40}{##1}}}
163
+ \expandafter\def\csname PY@tok@se\endcsname{\let\PY@bf=\textbf\def\PY@tc##1{\textcolor[rgb]{0.73,0.40,0.13}{##1}}}
164
+ \expandafter\def\csname PY@tok@gt\endcsname{\def\PY@tc##1{\textcolor[rgb]{0.00,0.27,0.87}{##1}}}
165
+ \expandafter\def\csname PY@tok@nv\endcsname{\def\PY@tc##1{\textcolor[rgb]{0.10,0.09,0.49}{##1}}}
166
+ \expandafter\def\csname PY@tok@c1\endcsname{\let\PY@it=\textit\def\PY@tc##1{\textcolor[rgb]{0.25,0.50,0.50}{##1}}}
167
+ \expandafter\def\csname PY@tok@kp\endcsname{\def\PY@tc##1{\textcolor[rgb]{0.00,0.50,0.00}{##1}}}
168
+ \expandafter\def\csname PY@tok@s2\endcsname{\def\PY@tc##1{\textcolor[rgb]{0.73,0.13,0.13}{##1}}}
169
+ \expandafter\def\csname PY@tok@nb\endcsname{\def\PY@tc##1{\textcolor[rgb]{0.00,0.50,0.00}{##1}}}
170
+ \expandafter\def\csname PY@tok@ni\endcsname{\let\PY@bf=\textbf\def\PY@tc##1{\textcolor[rgb]{0.60,0.60,0.60}{##1}}}
171
+ \expandafter\def\csname PY@tok@k\endcsname{\let\PY@bf=\textbf\def\PY@tc##1{\textcolor[rgb]{0.00,0.50,0.00}{##1}}}
172
+ \expandafter\def\csname PY@tok@na\endcsname{\def\PY@tc##1{\textcolor[rgb]{0.49,0.56,0.16}{##1}}}
173
+ \expandafter\def\csname PY@tok@o\endcsname{\def\PY@tc##1{\textcolor[rgb]{0.40,0.40,0.40}{##1}}}
174
+ \expandafter\def\csname PY@tok@mf\endcsname{\def\PY@tc##1{\textcolor[rgb]{0.40,0.40,0.40}{##1}}}
175
+ \expandafter\def\csname PY@tok@nl\endcsname{\def\PY@tc##1{\textcolor[rgb]{0.63,0.63,0.00}{##1}}}
176
+ \expandafter\def\csname PY@tok@nt\endcsname{\let\PY@bf=\textbf\def\PY@tc##1{\textcolor[rgb]{0.00,0.50,0.00}{##1}}}
177
+ \expandafter\def\csname PY@tok@gs\endcsname{\let\PY@bf=\textbf}
178
+ \expandafter\def\csname PY@tok@vi\endcsname{\def\PY@tc##1{\textcolor[rgb]{0.10,0.09,0.49}{##1}}}
179
+ \expandafter\def\csname PY@tok@gu\endcsname{\let\PY@bf=\textbf\def\PY@tc##1{\textcolor[rgb]{0.50,0.00,0.50}{##1}}}
180
+ \expandafter\def\csname PY@tok@kr\endcsname{\let\PY@bf=\textbf\def\PY@tc##1{\textcolor[rgb]{0.00,0.50,0.00}{##1}}}
181
+
182
+ \def\PYZbs{\char`\\}
183
+ \def\PYZus{\char`\_}
184
+ \def\PYZob{\char`\{}
185
+ \def\PYZcb{\char`\}}
186
+ \def\PYZca{\char`\^}
187
+ \def\PYZam{\char`\&}
188
+ \def\PYZlt{\char`\<}
189
+ \def\PYZgt{\char`\>}
190
+ \def\PYZsh{\char`\#}
191
+ \def\PYZpc{\char`\%}
192
+ \def\PYZdl{\char`\$}
193
+ \def\PYZhy{\char`\-}
194
+ \def\PYZsq{\char`\'}
195
+ \def\PYZdq{\char`\"}
196
+ \def\PYZti{\char`\~}
197
+ % for compatibility with earlier versions
198
+ \def\PYZat{@}
199
+ \def\PYZlb{[}
200
+ \def\PYZrb{]}
201
+ \makeatother
202
+
203
+
204
+
205
+
206
+
207
+
208
+ % Prevent overflowing lines due to hard-to-break entities
209
+ \sloppy
210
+ % Setup hyperref package
211
+ \hypersetup{
212
+ breaklinks=true, % so long urls are correctly broken across lines
213
+ colorlinks=true,
214
+ urlcolor=blue,
215
+ linkcolor=darkorange,
216
+ citecolor=darkgreen,
217
+ }
218
+ % Slightly bigger margins than the latex defaults
219
+
220
+ \geometry{verbose,tmargin=1in,bmargin=1in,lmargin=1in,rmargin=1in}
221
+
222
+
223
+ \newcommand{\highlighta}[1]{{\sethlcolor{yellow} \textcolor{red}{\hl{#1}}}}
224
+ \newcommand{\highlightb}[1]{{\sethlcolor{red} \textcolor{yellow}{\hl{#1}}}}
225
+ \newcommand{\highlightc}[1]{{\sethlcolor{green} \textcolor{yellow}{\hl{#1}}}}
226
+ \newenvironment{highlightA}{\begin{mdframed}[hidealllines=true,backgroundcolor=yellow!20]}{\end{mdframed}}
227
+ \newenvironment{highlightB}{\begin{mdframed}[hidealllines=true,backgroundcolor=red!20]}{\end{mdframed}}
228
+ \newenvironment{highlightC}{\begin{mdframed}[hidealllines=true,backgroundcolor=green!20]}{\end{mdframed}}
229
+
230
+
231
+ %\usepackage{foo}
232
+
233
+ \begin{document}
234
+
235
+
236
+ \maketitle
237
+
238
+
239
+
240
+
241
+ \section{Exporting the notebook}\label{exporting-the-notebook}
242
+
243
+ As suggested by @juhasch, it is interesting to keep the highlights when
244
+ exporting the notebook to another format. We give and explain below some
245
+ possibilities:
246
+
247
+ \subsection{Short version}\label{short-version}
248
+
249
+ \begin{itemize}
250
+ \item
251
+ Html export:
252
+
253
+ \begin{Shaded}
254
+ \begin{Highlighting}[]
255
+ \KeywordTok{jupyter} \NormalTok{nbconvert FILE --config JUPYTER_DATA_DIR/extensions/highlight_html_cfg.py }
256
+ \end{Highlighting}
257
+ \end{Shaded}
258
+ \item
259
+ LaTeX export:
260
+
261
+ \begin{Shaded}
262
+ \begin{Highlighting}[]
263
+ \KeywordTok{jupyter} \NormalTok{nbconvert FILE --config JUPYTER_DATA_DIR/extensions/highlight_latex_cfg.py }
264
+ \end{Highlighting}
265
+ \end{Shaded}
266
+
267
+ where JUPYTER\_DATA\_DIR can be found from the output of
268
+
269
+ \begin{Shaded}
270
+ \begin{Highlighting}[]
271
+ \KeywordTok{jupyter} \NormalTok{--paths}
272
+ \end{Highlighting}
273
+ \end{Shaded}
274
+
275
+ eg \texttt{\textasciitilde{}/.local/share/jupyter} in my case. Seems
276
+ to be
277
+ \texttt{c:\textbackslash{}users\textbackslash{}NAME\textbackslash{}AppData\textbackslash{}Roaming\textbackslash{}jupyter}
278
+ under Windows.
279
+ \end{itemize}
280
+
281
+ Examples can be found here: \href{tst_highlights.ipynb}{initial
282
+ notebook}, \href{tst_highlights.html}{html version},
283
+ \href{tst_highlights.pdf}{pdf version} (after an additional LaTeX
284
+ \(\rightarrow\) pdf compilation).
285
+
286
+ \subsection{Html export}\label{html-export}
287
+
288
+ This is quite easy. Actually, highlight formatting embedded in markdown
289
+ cells is preserved while converting with the standard
290
+
291
+ \begin{Shaded}
292
+ \begin{Highlighting}[]
293
+ \KeywordTok{jupyter} \NormalTok{nbconvert file.ipynb}
294
+ \end{Highlighting}
295
+ \end{Shaded}
296
+
297
+ However, the css file is missing and must be added. Here we have several
298
+ possibilities
299
+
300
+ \begin{itemize}
301
+ \item
302
+ Embed the css \emph{within} the notebook. For that, consider the last
303
+ cell of the present notebook. This code reads the css file
304
+ \texttt{highlighter.css} in the extension directory and displays the
305
+ corresponding style. So doing the
306
+ \texttt{\textless{}style\textgreater{}\ ...\textless{}/style\textgreater{}}
307
+ section will be present in the cell output and interpreted by the web
308
+ browser. Drawbacks of this solution is that user still have to execute
309
+ this cell and that the this is not language agnostic.
310
+ \item
311
+ Use a \textbf{template file} to link or include the css file during
312
+ conversion. Such a file is provided as
313
+ \texttt{templates/highlighter.tpl}. It was choosen here to
314
+ \emph{include} the css content in the produced html file rather than
315
+ linking it. This avoids the necessity to keep the css file with the
316
+ html files.
317
+ \item
318
+ This works directly if the css resides in the same directory as the
319
+ file the user is attempting to convert --thus requires the user to
320
+ copy \texttt{highlighter.css} in the current directory. Then the
321
+ conversion is simply
322
+
323
+ \begin{Shaded}
324
+ \begin{Highlighting}[]
325
+ \KeywordTok{jupyter} \NormalTok{nbconvert file.ipynb --template highlighter}
326
+ \end{Highlighting}
327
+ \end{Shaded}
328
+ \item
329
+ It still remains two problems with this approach. First, it can be
330
+ annoying to have to systematically copy the css file in the current
331
+ directory. Second, the data within the html tags is not converted (and
332
+ thus markdown remains unmodified). A solution is to use a pair of
333
+ preprocessor/postprocessor that modify the html tags and enable the
334
+ subsequent markdown to html converter to operate on the included data.
335
+ Also, a config file is provided which redefines the template path to
336
+ enable direct inclusion of the css file in the extension directory.
337
+ Unfortunately, \highlighta{it seems that the \emph{full path} to
338
+ the config file has to be provided}. This file resides in the
339
+ extensions subdirectory of the jupyter\_data\_dir. The path can be
340
+ found by looking at the output of
341
+
342
+ \begin{Shaded}
343
+ \begin{Highlighting}[]
344
+ \KeywordTok{jupyter} \NormalTok{--paths}
345
+ \end{Highlighting}
346
+ \end{Shaded}
347
+
348
+ Then the command to issue for converting the notebook to html is
349
+
350
+ \begin{Shaded}
351
+ \begin{Highlighting}[]
352
+ \KeywordTok{jupyter} \NormalTok{nbconvert FILE --config JUPYTER_DATA_DIR/extensions/highlight_html_cfg.py }
353
+ \end{Highlighting}
354
+ \end{Shaded}
355
+ \end{itemize}
356
+
357
+ For instance
358
+
359
+ \begin{Shaded}
360
+ \begin{Highlighting}[]
361
+ \KeywordTok{jupyter} \NormalTok{nbconvert tst_highlights.ipynb --config ~/.local/share/jupyter/extensions/highlight_html_cfg.py }
362
+ \end{Highlighting}
363
+ \end{Shaded}
364
+
365
+ \subsection{LaTeX export}\label{latex-export}
366
+
367
+ This is a bit more complicated since the direct conversion removes all
368
+ html formatting present in markdown cells. Thus use again a
369
+ \textbf{preprocessor} which runs before the markdown \(\rightarrow\)
370
+ LaTeX conversion. In turn, it appears that we also need to postprocess
371
+ the result.
372
+
373
+ Three LaTeX commands, namely \emph{highlighta, highlightb, highlightc},
374
+ and three environments \emph{highlightA, highlightB, highlightC} are
375
+ defined. Highlighting html markup is then transformed into the
376
+ corresponding LaTeX commands and the text for completely highlighted
377
+ cells is put in the adequate LaTeX environment.
378
+
379
+ Pre and PostProcessor classes are defined in the file
380
+ \texttt{pp\_highlighter.py} located in the \texttt{extensions}
381
+ directory. A LaTeX template, that includes the necessary packages and
382
+ the definitions of commands/environments is provides as
383
+ \texttt{highlighter.tplx} in the template directory. The template
384
+ inherits from \texttt{article.ltx}. For more complex scenarios,
385
+ typically if the latex template file has be customized, the user shall
386
+ modify its template or inherit from his base template rather than from
387
+ article.
388
+
389
+ Finally, a config file fixes the different options for the conversion.
390
+ Then the command to issue is simply
391
+
392
+ \begin{Shaded}
393
+ \begin{Highlighting}[]
394
+ \KeywordTok{jupyter} \NormalTok{nbconvert FILE --config JUPYTER_DATA_DIR/extensions/highlight_latex_cfg.py }
395
+ \end{Highlighting}
396
+ \end{Shaded}
397
+
398
+ e.g.
399
+
400
+ \begin{Shaded}
401
+ \begin{Highlighting}[]
402
+ \KeywordTok{jupyter} \NormalTok{nbconvert tst_highlights.ipynb --config ~/.local/share/jupyter/extensions/highlight_latex_cfg.py }
403
+ \end{Highlighting}
404
+ \end{Shaded}
405
+
406
+ \subsection{Configuring paths}\label{configuring-paths}
407
+
408
+ \highlighta{For those who do not have taken the extension from the
409
+ \texttt{jupyter_contrib_nbextensions} repository or have not configured
410
+ extensions via its \texttt{setup.py} utility,} a file
411
+ \texttt{set\_paths.py} is present in the extension directory (it is
412
+ merely a verbatim copy of the relevant parts in setup.py). This file
413
+ configure the paths to the \texttt{templates} and \texttt{extension}
414
+ directories. It should be executed by something like
415
+
416
+ \begin{Shaded}
417
+ \begin{Highlighting}[]
418
+ \KeywordTok{python3} \NormalTok{set_paths.py}
419
+ \end{Highlighting}
420
+ \end{Shaded}
421
+
422
+ Additionaly, you may also have to execute \texttt{mv\_paths.py} if you
423
+ installed from the original repo via
424
+ \texttt{jupyter\ nbextension\ install\ ..}
425
+
426
+ \begin{Shaded}
427
+ \begin{Highlighting}[]
428
+ \KeywordTok{python3} \NormalTok{mv_paths.py}
429
+ \end{Highlighting}
430
+ \end{Shaded}
431
+
432
+ \subsection{Example for embedding the css within the notebook before
433
+ conversion}\label{example-for-embedding-the-css-within-the-notebook-before-conversion}
434
+
435
+ \begin{Verbatim}[commandchars=\\\{\}]
436
+ >>> \PY{k+kn}{from} \PY{n+nn}{IPython}\PY{n+nn}{.}\PY{n+nn}{core}\PY{n+nn}{.}\PY{n+nn}{display} \PY{k}{import} \PY{n}{display}\PY{p}{,} \PY{n}{HTML}
437
+ ... \PY{k+kn}{from} \PY{n+nn}{jupyter\PYZus{}core}\PY{n+nn}{.}\PY{n+nn}{paths} \PY{k}{import} \PY{n}{jupyter\PYZus{}config\PYZus{}dir}\PY{p}{,} \PY{n}{jupyter\PYZus{}data\PYZus{}dir}
438
+ ... \PY{k+kn}{import} \PY{n+nn}{os}
439
+ ... \PY{n}{csspath}\PY{o}{=}\PY{n}{os}\PY{o}{.}\PY{n}{path}\PY{o}{.}\PY{n}{join}\PY{p}{(}\PY{n}{jupyter\PYZus{}data\PYZus{}dir}\PY{p}{(}\PY{p}{)}\PY{p}{,}\PY{l+s}{\PYZsq{}}\PY{l+s}{nbextensions}\PY{l+s}{\PYZsq{}}\PY{p}{,}
440
+ ... \PY{l+s}{\PYZsq{}}\PY{l+s}{highlighter}\PY{l+s}{\PYZsq{}}\PY{p}{,}\PY{l+s}{\PYZsq{}}\PY{l+s}{highlighter.css}\PY{l+s}{\PYZsq{}}\PY{p}{)}
441
+ ... \PY{n}{HTML}\PY{p}{(}\PY{l+s}{\PYZsq{}}\PY{l+s}{\PYZlt{}style\PYZgt{}}\PY{l+s}{\PYZsq{}}\PY{o}{+}\PY{n+nb}{open}\PY{p}{(}\PY{n}{csspath}\PY{p}{,} \PY{l+s}{\PYZdq{}}\PY{l+s}{r}\PY{l+s}{\PYZdq{}}\PY{p}{)}\PY{o}{.}\PY{n}{read}\PY{p}{(}\PY{p}{)}\PY{o}{+}\PY{l+s}{\PYZsq{}}\PY{l+s}{\PYZlt{}/style\PYZgt{}}\PY{l+s}{\PYZsq{}}\PY{p}{)}
442
+ \end{Verbatim}
443
+
444
+
445
+
446
+ \begin{verbatim}
447
+ <IPython.core.display.HTML object>
448
+ \end{verbatim}
449
+
450
+
451
+
452
+
453
+ % Add a bibliography block to the postdoc
454
+
455
+
456
+
457
+ \end{document}
.local/share/jupyter/nbextensions/highlighter/highlighter.css ADDED
@@ -0,0 +1,98 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .mark {
2
+ background-color: #f2ee97;
3
+ color: black;
4
+ display: inline;
5
+ }
6
+
7
+ .burk {
8
+ background-color: #ff9292;
9
+ color: #f9f9f9;
10
+ display: inline;
11
+ }
12
+
13
+ .girk {
14
+ background-color: #afe5ad;
15
+ color: black;
16
+ display: inline;
17
+ }
18
+
19
+ .birk {
20
+ background-color: #83b8f4;
21
+ color: #fff;
22
+ display: inline;
23
+ }
24
+
25
+ .pirk {
26
+ background-color: #ecb3d2;
27
+ color: #000;
28
+ display: inline;
29
+ }
30
+
31
+ /**/
32
+
33
+ button.mark {
34
+ background-image: url("data:image/svg+xml,%3C%3Fxml version='1.0' encoding='iso-8859-1'%3F%3E%3C!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --%3E%3Csvg version='1.1' id='Layer_1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px' viewBox='0 0 297 297' style='enable-background:new 0 0 297 297;' xml:space='preserve'%3E%3Cg%3E%3Cg id='XMLID_48_'%3E%3Cg%3E%3Cpath style='fill:%23fff;' d='M63.648,169.162l71.18,71.19l-61.09,12.21c-0.93,0.19-1.78,0.65-2.45,1.32l-9.74,9.74 l-21.17-21.18l9.74-9.73c0.66-0.67,1.12-1.53,1.31-2.45L63.648,169.162z'/%3E%3Cpath style='fill:%23000;' d='M230.278,81.592c2.81,2.81,2.81,7.38,0,10.19l-73.19,73.2c-2.81,2.81-7.38,2.81-10.19,0 l-7.62-7.62c-2.81-2.81-2.81-7.38,0-10.19l73.19-73.2c1.36-1.36,3.17-2.11,5.1-2.11c1.92,0,3.73,0.75,5.09,2.11L230.278,81.592z' /%3E%3Cpolygon style='fill:%23f2ee97;' points='53.918,271.242 45.428,279.732 13.668,269.152 32.748,250.072 '/%3E%3Cpath style='fill:%23f2ee97;' d='M283.918,54.022c4.41,4.41,4.71,11.58,0.68,16.34l-140.34,165.86l-76.48-76.49l165.85-140.34 c4.76-4.02,11.94-3.73,16.34,0.68L283.918,54.022z M241.968,86.682c0-4.3-1.64-8.6-4.91-11.87l-7.61-7.62 c-3.18-3.17-7.39-4.92-11.88-4.92s-8.71,1.75-11.88,4.92l-73.19,73.2c-3.17,3.17-4.92,7.39-4.92,11.87 c0,4.49,1.75,8.71,4.92,11.88l7.62,7.62c3.27,3.27,7.57,4.91,11.87,4.91c4.3,0,8.61-1.64,11.88-4.91l73.19-73.2 C240.328,95.282,241.968,90.982,241.968,86.682z'/%3E%3Cpath d='M290.698,47.242c7.91,7.9,8.44,20.78,1.22,29.31c0,0-143.78,169.92-143.83,169.95c-0.66,0.74-1.55,1.28-2.59,1.49 l-68.46,13.69l-26.92,26.93c-0.92,0.91-2.14,1.4-3.4,1.4c-0.5,0-1.01-0.08-1.51-0.24l-41.93-13.98 c-1.58-0.53-2.77-1.83-3.15-3.45c-0.38-1.61,0.1-3.31,1.28-4.49l40.9-40.9l13.69-68.45c0.22-1.09,0.79-1.99,1.57-2.67 c0.01-0.01,169.87-143.76,169.87-143.76c8.53-7.22,21.41-6.69,29.31,1.22L290.698,47.242z M284.598,70.362 c4.03-4.76,3.73-11.93-0.68-16.34l-33.95-33.95c-4.4-4.41-11.58-4.7-16.34-0.68l-165.85,140.34l76.48,76.49L284.598,70.362z M134.828,240.352l-71.18-71.19l-12.22,61.1c-0.19,0.92-0.65,1.78-1.31,2.45l-9.74,9.73l21.17,21.18l9.74-9.74 c0.67-0.67,1.52-1.13,2.45-1.32L134.828,240.352z M45.428,279.732l8.49-8.49l-21.17-21.17l-19.08,19.08L45.428,279.732z'/%3E%3C/g%3E%3C/g%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3C/svg%3E%0A");
35
+ background-repeat: no-repeat;
36
+ }
37
+
38
+ button.highlighter-btn {
39
+ height: 24px;
40
+ width: 29px;
41
+ background-size: 21px;
42
+ background-position: center;
43
+ border: none;
44
+ }
45
+
46
+ button.burk {
47
+ background-image: url("data:image/svg+xml,%3C%3Fxml version='1.0' encoding='iso-8859-1'%3F%3E%3C!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --%3E%3Csvg version='1.1' id='Layer_1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px' viewBox='0 0 297 297' style='enable-background:new 0 0 297 297;' xml:space='preserve'%3E%3Cg%3E%3Cg id='XMLID_48_'%3E%3Cg%3E%3Cpath style='fill:%23fff;' d='M63.648,169.162l71.18,71.19l-61.09,12.21c-0.93,0.19-1.78,0.65-2.45,1.32l-9.74,9.74 l-21.17-21.18l9.74-9.73c0.66-0.67,1.12-1.53,1.31-2.45L63.648,169.162z'/%3E%3Cpath style='fill:%23000;' d='M230.278,81.592c2.81,2.81,2.81,7.38,0,10.19l-73.19,73.2c-2.81,2.81-7.38,2.81-10.19,0 l-7.62-7.62c-2.81-2.81-2.81-7.38,0-10.19l73.19-73.2c1.36-1.36,3.17-2.11,5.1-2.11c1.92,0,3.73,0.75,5.09,2.11L230.278,81.592z' /%3E%3Cpolygon style='fill:%23ff9292;' points='53.918,271.242 45.428,279.732 13.668,269.152 32.748,250.072 '/%3E%3Cpath style='fill:%23ff9292;' d='M283.918,54.022c4.41,4.41,4.71,11.58,0.68,16.34l-140.34,165.86l-76.48-76.49l165.85-140.34 c4.76-4.02,11.94-3.73,16.34,0.68L283.918,54.022z M241.968,86.682c0-4.3-1.64-8.6-4.91-11.87l-7.61-7.62 c-3.18-3.17-7.39-4.92-11.88-4.92s-8.71,1.75-11.88,4.92l-73.19,73.2c-3.17,3.17-4.92,7.39-4.92,11.87 c0,4.49,1.75,8.71,4.92,11.88l7.62,7.62c3.27,3.27,7.57,4.91,11.87,4.91c4.3,0,8.61-1.64,11.88-4.91l73.19-73.2 C240.328,95.282,241.968,90.982,241.968,86.682z'/%3E%3Cpath d='M290.698,47.242c7.91,7.9,8.44,20.78,1.22,29.31c0,0-143.78,169.92-143.83,169.95c-0.66,0.74-1.55,1.28-2.59,1.49 l-68.46,13.69l-26.92,26.93c-0.92,0.91-2.14,1.4-3.4,1.4c-0.5,0-1.01-0.08-1.51-0.24l-41.93-13.98 c-1.58-0.53-2.77-1.83-3.15-3.45c-0.38-1.61,0.1-3.31,1.28-4.49l40.9-40.9l13.69-68.45c0.22-1.09,0.79-1.99,1.57-2.67 c0.01-0.01,169.87-143.76,169.87-143.76c8.53-7.22,21.41-6.69,29.31,1.22L290.698,47.242z M284.598,70.362 c4.03-4.76,3.73-11.93-0.68-16.34l-33.95-33.95c-4.4-4.41-11.58-4.7-16.34-0.68l-165.85,140.34l76.48,76.49L284.598,70.362z M134.828,240.352l-71.18-71.19l-12.22,61.1c-0.19,0.92-0.65,1.78-1.31,2.45l-9.74,9.73l21.17,21.18l9.74-9.74 c0.67-0.67,1.52-1.13,2.45-1.32L134.828,240.352z M45.428,279.732l8.49-8.49l-21.17-21.17l-19.08,19.08L45.428,279.732z'/%3E%3C/g%3E%3C/g%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3C/svg%3E%0A");
48
+ background-repeat: no-repeat;
49
+ }
50
+
51
+ button.girk {
52
+ background-image: url("data:image/svg+xml,%3C%3Fxml version='1.0' encoding='iso-8859-1'%3F%3E%3C!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --%3E%3Csvg version='1.1' id='Layer_1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px' viewBox='0 0 297 297' style='enable-background:new 0 0 297 297;' xml:space='preserve'%3E%3Cg%3E%3Cg id='XMLID_48_'%3E%3Cg%3E%3Cpath style='fill:%23fff;' d='M63.648,169.162l71.18,71.19l-61.09,12.21c-0.93,0.19-1.78,0.65-2.45,1.32l-9.74,9.74 l-21.17-21.18l9.74-9.73c0.66-0.67,1.12-1.53,1.31-2.45L63.648,169.162z'/%3E%3Cpath style='fill:%23000;' d='M230.278,81.592c2.81,2.81,2.81,7.38,0,10.19l-73.19,73.2c-2.81,2.81-7.38,2.81-10.19,0 l-7.62-7.62c-2.81-2.81-2.81-7.38,0-10.19l73.19-73.2c1.36-1.36,3.17-2.11,5.1-2.11c1.92,0,3.73,0.75,5.09,2.11L230.278,81.592z' /%3E%3Cpolygon style='fill:%23afe5ad;' points='53.918,271.242 45.428,279.732 13.668,269.152 32.748,250.072 '/%3E%3Cpath style='fill:%23afe5ad;' d='M283.918,54.022c4.41,4.41,4.71,11.58,0.68,16.34l-140.34,165.86l-76.48-76.49l165.85-140.34 c4.76-4.02,11.94-3.73,16.34,0.68L283.918,54.022z M241.968,86.682c0-4.3-1.64-8.6-4.91-11.87l-7.61-7.62 c-3.18-3.17-7.39-4.92-11.88-4.92s-8.71,1.75-11.88,4.92l-73.19,73.2c-3.17,3.17-4.92,7.39-4.92,11.87 c0,4.49,1.75,8.71,4.92,11.88l7.62,7.62c3.27,3.27,7.57,4.91,11.87,4.91c4.3,0,8.61-1.64,11.88-4.91l73.19-73.2 C240.328,95.282,241.968,90.982,241.968,86.682z'/%3E%3Cpath d='M290.698,47.242c7.91,7.9,8.44,20.78,1.22,29.31c0,0-143.78,169.92-143.83,169.95c-0.66,0.74-1.55,1.28-2.59,1.49 l-68.46,13.69l-26.92,26.93c-0.92,0.91-2.14,1.4-3.4,1.4c-0.5,0-1.01-0.08-1.51-0.24l-41.93-13.98 c-1.58-0.53-2.77-1.83-3.15-3.45c-0.38-1.61,0.1-3.31,1.28-4.49l40.9-40.9l13.69-68.45c0.22-1.09,0.79-1.99,1.57-2.67 c0.01-0.01,169.87-143.76,169.87-143.76c8.53-7.22,21.41-6.69,29.31,1.22L290.698,47.242z M284.598,70.362 c4.03-4.76,3.73-11.93-0.68-16.34l-33.95-33.95c-4.4-4.41-11.58-4.7-16.34-0.68l-165.85,140.34l76.48,76.49L284.598,70.362z M134.828,240.352l-71.18-71.19l-12.22,61.1c-0.19,0.92-0.65,1.78-1.31,2.45l-9.74,9.73l21.17,21.18l9.74-9.74 c0.67-0.67,1.52-1.13,2.45-1.32L134.828,240.352z M45.428,279.732l8.49-8.49l-21.17-21.17l-19.08,19.08L45.428,279.732z'/%3E%3C/g%3E%3C/g%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3C/svg%3E%0A");
53
+ background-repeat: no-repeat;
54
+ }
55
+
56
+ button.birk {
57
+ background-image: url("data:image/svg+xml,%3C%3Fxml version='1.0' encoding='iso-8859-1'%3F%3E%3C!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --%3E%3Csvg version='1.1' id='Layer_1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px' viewBox='0 0 297 297' style='enable-background:new 0 0 297 297;' xml:space='preserve'%3E%3Cg%3E%3Cg id='XMLID_48_'%3E%3Cg%3E%3Cpath style='fill:%23fff;' d='M63.648,169.162l71.18,71.19l-61.09,12.21c-0.93,0.19-1.78,0.65-2.45,1.32l-9.74,9.74 l-21.17-21.18l9.74-9.73c0.66-0.67,1.12-1.53,1.31-2.45L63.648,169.162z'/%3E%3Cpath style='fill:%23000;' d='M230.278,81.592c2.81,2.81,2.81,7.38,0,10.19l-73.19,73.2c-2.81,2.81-7.38,2.81-10.19,0 l-7.62-7.62c-2.81-2.81-2.81-7.38,0-10.19l73.19-73.2c1.36-1.36,3.17-2.11,5.1-2.11c1.92,0,3.73,0.75,5.09,2.11L230.278,81.592z' /%3E%3Cpolygon style='fill:%2383b8f4;' points='53.918,271.242 45.428,279.732 13.668,269.152 32.748,250.072 '/%3E%3Cpath style='fill:%2383b8f4;' d='M283.918,54.022c4.41,4.41,4.71,11.58,0.68,16.34l-140.34,165.86l-76.48-76.49l165.85-140.34 c4.76-4.02,11.94-3.73,16.34,0.68L283.918,54.022z M241.968,86.682c0-4.3-1.64-8.6-4.91-11.87l-7.61-7.62 c-3.18-3.17-7.39-4.92-11.88-4.92s-8.71,1.75-11.88,4.92l-73.19,73.2c-3.17,3.17-4.92,7.39-4.92,11.87 c0,4.49,1.75,8.71,4.92,11.88l7.62,7.62c3.27,3.27,7.57,4.91,11.87,4.91c4.3,0,8.61-1.64,11.88-4.91l73.19-73.2 C240.328,95.282,241.968,90.982,241.968,86.682z'/%3E%3Cpath d='M290.698,47.242c7.91,7.9,8.44,20.78,1.22,29.31c0,0-143.78,169.92-143.83,169.95c-0.66,0.74-1.55,1.28-2.59,1.49 l-68.46,13.69l-26.92,26.93c-0.92,0.91-2.14,1.4-3.4,1.4c-0.5,0-1.01-0.08-1.51-0.24l-41.93-13.98 c-1.58-0.53-2.77-1.83-3.15-3.45c-0.38-1.61,0.1-3.31,1.28-4.49l40.9-40.9l13.69-68.45c0.22-1.09,0.79-1.99,1.57-2.67 c0.01-0.01,169.87-143.76,169.87-143.76c8.53-7.22,21.41-6.69,29.31,1.22L290.698,47.242z M284.598,70.362 c4.03-4.76,3.73-11.93-0.68-16.34l-33.95-33.95c-4.4-4.41-11.58-4.7-16.34-0.68l-165.85,140.34l76.48,76.49L284.598,70.362z M134.828,240.352l-71.18-71.19l-12.22,61.1c-0.19,0.92-0.65,1.78-1.31,2.45l-9.74,9.73l21.17,21.18l9.74-9.74 c0.67-0.67,1.52-1.13,2.45-1.32L134.828,240.352z M45.428,279.732l8.49-8.49l-21.17-21.17l-19.08,19.08L45.428,279.732z'/%3E%3C/g%3E%3C/g%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3C/svg%3E%0A");
58
+ background-repeat: no-repeat;
59
+ }
60
+
61
+ button.pirk {
62
+ background-image: url("data:image/svg+xml,%3C%3Fxml version='1.0' encoding='iso-8859-1'%3F%3E%3C!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --%3E%3Csvg version='1.1' id='Layer_1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px' viewBox='0 0 297 297' style='enable-background:new 0 0 297 297;' xml:space='preserve'%3E%3Cg%3E%3Cg id='XMLID_48_'%3E%3Cg%3E%3Cpath style='fill:%23fff;' d='M63.648,169.162l71.18,71.19l-61.09,12.21c-0.93,0.19-1.78,0.65-2.45,1.32l-9.74,9.74 l-21.17-21.18l9.74-9.73c0.66-0.67,1.12-1.53,1.31-2.45L63.648,169.162z'/%3E%3Cpath style='fill:%23000;' d='M230.278,81.592c2.81,2.81,2.81,7.38,0,10.19l-73.19,73.2c-2.81,2.81-7.38,2.81-10.19,0 l-7.62-7.62c-2.81-2.81-2.81-7.38,0-10.19l73.19-73.2c1.36-1.36,3.17-2.11,5.1-2.11c1.92,0,3.73,0.75,5.09,2.11L230.278,81.592z' /%3E%3Cpolygon style='fill:%23ecb3d2;' points='53.918,271.242 45.428,279.732 13.668,269.152 32.748,250.072 '/%3E%3Cpath style='fill:%23ecb3d2;' d='M283.918,54.022c4.41,4.41,4.71,11.58,0.68,16.34l-140.34,165.86l-76.48-76.49l165.85-140.34 c4.76-4.02,11.94-3.73,16.34,0.68L283.918,54.022z M241.968,86.682c0-4.3-1.64-8.6-4.91-11.87l-7.61-7.62 c-3.18-3.17-7.39-4.92-11.88-4.92s-8.71,1.75-11.88,4.92l-73.19,73.2c-3.17,3.17-4.92,7.39-4.92,11.87 c0,4.49,1.75,8.71,4.92,11.88l7.62,7.62c3.27,3.27,7.57,4.91,11.87,4.91c4.3,0,8.61-1.64,11.88-4.91l73.19-73.2 C240.328,95.282,241.968,90.982,241.968,86.682z'/%3E%3Cpath d='M290.698,47.242c7.91,7.9,8.44,20.78,1.22,29.31c0,0-143.78,169.92-143.83,169.95c-0.66,0.74-1.55,1.28-2.59,1.49 l-68.46,13.69l-26.92,26.93c-0.92,0.91-2.14,1.4-3.4,1.4c-0.5,0-1.01-0.08-1.51-0.24l-41.93-13.98 c-1.58-0.53-2.77-1.83-3.15-3.45c-0.38-1.61,0.1-3.31,1.28-4.49l40.9-40.9l13.69-68.45c0.22-1.09,0.79-1.99,1.57-2.67 c0.01-0.01,169.87-143.76,169.87-143.76c8.53-7.22,21.41-6.69,29.31,1.22L290.698,47.242z M284.598,70.362 c4.03-4.76,3.73-11.93-0.68-16.34l-33.95-33.95c-4.4-4.41-11.58-4.7-16.34-0.68l-165.85,140.34l76.48,76.49L284.598,70.362z M134.828,240.352l-71.18-71.19l-12.22,61.1c-0.19,0.92-0.65,1.78-1.31,2.45l-9.74,9.73l21.17,21.18l9.74-9.74 c0.67-0.67,1.52-1.13,2.45-1.32L134.828,240.352z M45.428,279.732l8.49-8.49l-21.17-21.17l-19.08,19.08L45.428,279.732z'/%3E%3C/g%3E%3C/g%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3C/svg%3E%0A");
63
+ background-repeat: no-repeat;
64
+ }
65
+
66
+ i#menu-hgl, i.highlighter-close {
67
+ padding-left: 20px;
68
+ }
69
+
70
+ #higlighter_menu.btn:focus, #higlighter_menu.btn:active:focus, #higlighter_menu.btn.active:focus, #higlighter_menu.btn.focus, #higlighter_menu.btn:active.focus, #higlighter_menu.btn.active.focus :focus {
71
+ outline: 0 !important;
72
+ }
73
+
74
+ #higlighter_menu {
75
+ background-image: url("data:image/svg+xml,%3C%3Fxml version='1.0' encoding='iso-8859-1'%3F%3E%3C!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --%3E%3Csvg version='1.1' id='Capa_1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px' viewBox='0 0 512.001 512.001' style='enable-background:new 0 0 512.001 512.001;' xml:space='preserve'%3E%3Cpolygon style='fill:%23FFABCD;' points='387.375,480.658 383.919,157.034 503.991,157.034 500.535,480.658 '/%3E%3Cpolygon style='fill:%23FF6BA6;' points='382.637,161.05 419.416,90.494 468.484,90.494 505.272,161.05 '/%3E%3Cpolygon style='fill:%23FFABCD;' points='418.626,94.51 418.626,43.148 469.274,30.795 469.274,94.51 '/%3E%3Cg%3E%3Cpolygon style='fill:%23FBDF97;' points='230.672,94.51 230.672,43.148 281.33,30.795 281.33,94.51 '/%3E%3Cpolygon style='fill:%23FBDF97;' points='199.422,480.658 195.965,157.034 316.036,157.034 312.58,480.658 '/%3E%3C/g%3E%3Cpolygon style='fill:%23FBC640;' points='194.683,161.05 231.462,90.494 280.539,90.494 317.318,161.05 '/%3E%3Cg%3E%3Cpolygon style='fill:%2344C973;' points='11.466,480.658 8.011,157.034 128.083,157.034 124.626,480.658 '/%3E%3Cpolygon style='fill:%2344C973;' points='42.728,94.51 42.728,43.148 93.375,30.795 93.375,94.51 '/%3E%3C/g%3E%3Cpolygon style='fill:%23248A48;' points='6.729,161.05 43.518,90.494 92.585,90.494 129.364,161.05 '/%3E%3Cpath d='M443.734,375.969c-12.919,0-12.941,20.078,0,20.078C456.653,396.048,456.675,375.969,443.734,375.969z'/%3E%3Cpath d='M13.453,488.689H122.64c5.502,0,9.98-4.43,10.039-9.932l3.413-319.608c0.004-0.354-0.012-0.705-0.044-1.05 c-0.001-0.014-0.004-0.027-0.006-0.041c-0.037-0.382-0.1-0.758-0.18-1.127c-0.02-0.093-0.045-0.185-0.068-0.277 c-0.074-0.305-0.163-0.604-0.264-0.898c-0.031-0.09-0.061-0.181-0.095-0.27c-0.138-0.361-0.289-0.715-0.466-1.055 c-0.005-0.01-0.008-0.02-0.013-0.029l-0.008-0.015c-0.01-0.02-0.021-0.04-0.031-0.06l-33.51-64.284V33.351 c0-3.081-1.416-5.992-3.838-7.895c-2.423-1.903-5.584-2.588-8.58-1.858L42.357,34.972c-4.497,1.097-7.66,5.125-7.66,9.753v45.316 L1.176,154.325c-0.011,0.02-0.021,0.04-0.031,0.06l-0.008,0.015c-0.005,0.009-0.008,0.019-0.012,0.028 c-0.177,0.34-0.328,0.694-0.466,1.055c-0.034,0.09-0.064,0.181-0.096,0.272c-0.101,0.293-0.19,0.592-0.264,0.898 c-0.023,0.092-0.048,0.184-0.068,0.277c-0.079,0.369-0.143,0.744-0.18,1.126c-0.001,0.014-0.005,0.028-0.006,0.042 c-0.032,0.345-0.048,0.697-0.044,1.051l3.413,319.608C3.473,484.259,7.951,488.689,13.453,488.689z M23.386,468.611l-3.198-299.53 h95.719l-3.198,299.53H23.386z M54.775,52.61l26.554-6.476v36.329H54.775V52.61z M50.823,102.541h34.456l24.22,46.461H26.596 L50.823,102.541z'/%3E%3Cpath d='M68.046,215.121c-5.545,0-10.039,4.495-10.039,10.039v160.366c0,5.545,4.495,10.039,10.039,10.039 s10.039-4.495,10.039-10.039V225.16C78.086,219.615,73.591,215.121,68.046,215.121z'/%3E%3Cpath d='M188.518,155.756c-0.102,0.293-0.19,0.592-0.264,0.898c-0.023,0.092-0.048,0.184-0.068,0.277 c-0.079,0.369-0.143,0.744-0.18,1.127c-0.001,0.014-0.005,0.027-0.006,0.041c-0.032,0.345-0.048,0.696-0.044,1.05l3.413,319.608 c0.059,5.501,4.537,9.932,10.039,9.932h109.187c5.502,0,9.98-4.43,10.039-9.932l3.413-319.608c0.004-0.354-0.012-0.705-0.044-1.05 c-0.001-0.014-0.004-0.027-0.006-0.041c-0.037-0.382-0.1-0.758-0.18-1.127c-0.02-0.093-0.045-0.185-0.068-0.277 c-0.074-0.305-0.163-0.604-0.264-0.898c-0.031-0.09-0.061-0.181-0.095-0.27c-0.138-0.361-0.289-0.715-0.466-1.055 c-0.005-0.01-0.008-0.02-0.013-0.029l-0.008-0.015c-0.01-0.02-0.021-0.04-0.031-0.06l-33.511-64.284V33.351 c0-3.081-1.415-5.991-3.838-7.895c-2.422-1.903-5.585-2.588-8.579-1.858l-46.642,11.374c-4.497,1.096-7.661,5.125-7.661,9.753 v45.317l-33.509,64.283c-0.011,0.02-0.021,0.04-0.031,0.06l-0.008,0.015c-0.005,0.009-0.008,0.02-0.013,0.029 c-0.177,0.34-0.328,0.694-0.466,1.055C188.579,155.575,188.549,155.665,188.518,155.756z M211.34,468.611l-3.198-299.53h95.719 l-3.199,299.53H211.34z M242.719,52.61l26.564-6.478v36.331h-26.564L242.719,52.61L242.719,52.61z M238.768,102.541h34.466 l24.22,46.461H214.55L238.768,102.541z'/%3E%3Cpath d='M256.001,215.121c-5.545,0-10.039,4.495-10.039,10.039v160.366c0,5.545,4.495,10.039,10.039,10.039 c5.545,0,10.039-4.495,10.039-10.039V225.16C266.04,219.615,261.545,215.121,256.001,215.121z'/%3E%3Cpath d='M508.587,478.758l3.413-319.608c0.004-0.355-0.012-0.706-0.044-1.051c-0.001-0.014-0.005-0.028-0.006-0.042 c-0.037-0.382-0.1-0.758-0.18-1.126c-0.02-0.092-0.045-0.185-0.068-0.277c-0.074-0.305-0.163-0.604-0.264-0.898 c-0.031-0.091-0.062-0.182-0.096-0.272c-0.138-0.361-0.289-0.715-0.466-1.055c-0.005-0.009-0.008-0.019-0.012-0.028l-0.008-0.015 c-0.01-0.02-0.02-0.04-0.031-0.06l-33.52-64.283v-56.69c0-3.081-1.416-5.992-3.838-7.895c-2.423-1.903-5.585-2.588-8.579-1.858 l-46.632,11.374c-4.497,1.097-7.66,5.125-7.66,9.753v45.317l-33.51,64.283c-0.011,0.02-0.021,0.04-0.031,0.06l-0.008,0.015 c-0.005,0.009-0.008,0.02-0.013,0.029c-0.177,0.34-0.328,0.694-0.466,1.055c-0.034,0.089-0.064,0.18-0.095,0.27 c-0.102,0.293-0.19,0.592-0.264,0.898c-0.023,0.092-0.048,0.184-0.068,0.277c-0.079,0.369-0.143,0.744-0.18,1.127 c-0.001,0.014-0.005,0.027-0.006,0.041c-0.032,0.345-0.048,0.696-0.044,1.05l3.413,319.608c0.059,5.501,4.537,9.932,10.039,9.932 h109.186C504.051,488.689,508.528,484.259,508.587,478.758z M430.673,52.61l26.554-6.476v36.329h-26.554V52.61z M426.723,102.541 h34.456l24.227,46.461h-82.902L426.723,102.541z M399.293,468.611l-3.198-299.53h95.719l-3.199,299.53H399.293z'/%3E%3Cpath d='M443.955,215.121c-5.545,0-10.039,4.495-10.039,10.039v128.221c0,5.545,4.495,10.039,10.039,10.039 s10.039-4.495,10.039-10.039V225.16C453.994,219.615,449.5,215.121,443.955,215.121z'/%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3C/svg%3E%0A");
76
+ background-repeat: no-repeat;
77
+ background-position-x: 6px;
78
+ padding-right: 2px !important;
79
+ background-size: 18px;
80
+ background-position-y: center;
81
+ }
82
+
83
+ .toolbar .btn-group {
84
+ margin-top: 3px !important;
85
+ }
86
+
87
+ div#hgl {
88
+ padding-top: 2px;
89
+ }
90
+
91
+ #remove_highlights {
92
+ background-image: url("data:image/svg+xml,%3C%3Fxml version='1.0' encoding='iso-8859-1'%3F%3E%3C!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --%3E%3Csvg version='1.1' id='Layer_1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px' viewBox='0 0 297 297' style='enable-background:new 0 0 297 297;' xml:space='preserve'%3E%3Cg%3E%3Cg id='XMLID_48_'%3E%3Cg%3E%3Cpath style='fill:%23fff;' d='M63.648,169.162l71.18,71.19l-61.09,12.21c-0.93,0.19-1.78,0.65-2.45,1.32l-9.74,9.74 l-21.17-21.18l9.74-9.73c0.66-0.67,1.12-1.53,1.31-2.45L63.648,169.162z'/%3E%3Cpath style='fill:%23000;' d='M230.278,81.592c2.81,2.81,2.81,7.38,0,10.19l-73.19,73.2c-2.81,2.81-7.38,2.81-10.19,0 l-7.62-7.62c-2.81-2.81-2.81-7.38,0-10.19l73.19-73.2c1.36-1.36,3.17-2.11,5.1-2.11c1.92,0,3.73,0.75,5.09,2.11L230.278,81.592z' /%3E%3Cpolygon style='fill:%23fff;' points='53.918,271.242 45.428,279.732 13.668,269.152 32.748,250.072 '/%3E%3Cpath style='fill:%23fff;' d='M283.918,54.022c4.41,4.41,4.71,11.58,0.68,16.34l-140.34,165.86l-76.48-76.49l165.85-140.34 c4.76-4.02,11.94-3.73,16.34,0.68L283.918,54.022z M241.968,86.682c0-4.3-1.64-8.6-4.91-11.87l-7.61-7.62 c-3.18-3.17-7.39-4.92-11.88-4.92s-8.71,1.75-11.88,4.92l-73.19,73.2c-3.17,3.17-4.92,7.39-4.92,11.87 c0,4.49,1.75,8.71,4.92,11.88l7.62,7.62c3.27,3.27,7.57,4.91,11.87,4.91c4.3,0,8.61-1.64,11.88-4.91l73.19-73.2 C240.328,95.282,241.968,90.982,241.968,86.682z'/%3E%3Cpath d='M290.698,47.242c7.91,7.9,8.44,20.78,1.22,29.31c0,0-143.78,169.92-143.83,169.95c-0.66,0.74-1.55,1.28-2.59,1.49 l-68.46,13.69l-26.92,26.93c-0.92,0.91-2.14,1.4-3.4,1.4c-0.5,0-1.01-0.08-1.51-0.24l-41.93-13.98 c-1.58-0.53-2.77-1.83-3.15-3.45c-0.38-1.61,0.1-3.31,1.28-4.49l40.9-40.9l13.69-68.45c0.22-1.09,0.79-1.99,1.57-2.67 c0.01-0.01,169.87-143.76,169.87-143.76c8.53-7.22,21.41-6.69,29.31,1.22L290.698,47.242z M284.598,70.362 c4.03-4.76,3.73-11.93-0.68-16.34l-33.95-33.95c-4.4-4.41-11.58-4.7-16.34-0.68l-165.85,140.34l76.48,76.49L284.598,70.362z M134.828,240.352l-71.18-71.19l-12.22,61.1c-0.19,0.92-0.65,1.78-1.31,2.45l-9.74,9.73l21.17,21.18l9.74-9.74 c0.67-0.67,1.52-1.13,2.45-1.32L134.828,240.352z M45.428,279.732l8.49-8.49l-21.17-21.17l-19.08,19.08L45.428,279.732z'/%3E%3C/g%3E%3C/g%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3Cg%3E%3C/g%3E%3C/svg%3E%0A");
93
+ background-repeat: no-repeat;
94
+ background-position-x: 6px;
95
+ padding-right: 2px !important;
96
+ background-size: 18px;
97
+ background-position-y: center;
98
+ }
.local/share/jupyter/nbextensions/highlighter/highlighter.js ADDED
@@ -0,0 +1,378 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ Three different highlighting schemes "mark"|"burk"|"girk"|"birk"|"pirk" are defined in the css highlighter.css
3
+ The following functions highlight the selected text, according to the scheme chosen by a menu button. More precisely, they replace selected text, both in edit or command mode, by
4
+ a span tag with a given class and the selected text as data.
5
+ if no text is selected, then the whole cell is highlighted (using a div tag and a class corresponding to the chosen scheme). A function to remove all hihlightings is also provided.
6
+ */
7
+
8
+ function removeFullCellHighlight(cell_text) {
9
+ cell_text = cell_text.replace(/<div class=(?:"mark"|"burk"|"girk"|"birk"|"pirk")>\n([\s\S]*?)<\/div><i class="fa fa-lightbulb-o "><\/i>/g, function (w, g) {
10
+ return g
11
+ })
12
+ return cell_text
13
+ }
14
+
15
+ function fullCellHighlight(cell_text, scheme) {
16
+ cell_text = removeFullCellHighlight(cell_text);
17
+ return '<div class=' + '"' + scheme + '"' + '>\n' + cell_text + '</div><i class="fa fa-lightbulb-o "><\/i>'
18
+ }
19
+
20
+ function highlight(text, scheme) {
21
+ var scheme = scheme;
22
+ // replace by a span, wile preserving leading and trailing spaces
23
+ var rep = text.replace(/(\S[\S\s]*\S)/, function (w, internal_text) {
24
+ return '<span class=' + '"' + scheme + '"' + '>' + internal_text + '</span>'
25
+ })
26
+ return rep
27
+ //return '<span class='+'"'+scheme+'"'+'>'+text+'</span>'
28
+ }
29
+
30
+
31
+ function add_div(text) {
32
+ if (text.match(/^<div>([\S\s]*)<\/div>$/) == null) {
33
+ return '<div>' + text + '</div>'
34
+ } else {
35
+ return text
36
+ }
37
+ }
38
+
39
+ function rem_div(text) {
40
+ return text.replace(/^<div>([\S\s]*)<\/div>$/, function (w, g) {
41
+ return g
42
+ })
43
+ }
44
+
45
+ function highlightInCmdMode(event, scheme) {
46
+ var cell = IPython.notebook.get_selected_cell()
47
+ var cm = IPython.notebook.get_selected_cell().code_mirror
48
+ var selectedText = window.getSelection().toString();
49
+ var cell_text = cell.get_text();
50
+ if (selectedText.length == 0) {
51
+ cell_text = fullCellHighlight(cell_text, scheme);
52
+ } else {
53
+ var identifiedText = align(selectedText, cell_text);
54
+ cell_text = cell_text.replace(identifiedText, highlight(identifiedText, scheme));
55
+ }
56
+ cell.set_text(cell_text);
57
+ cell.render();
58
+ return false;
59
+ }
60
+
61
+ function highlightInEditMode(event, scheme) {
62
+ var cell = IPython.notebook.get_selected_cell()
63
+ var cm = cell.code_mirror
64
+ var selectedText = cm.getSelection()
65
+ if (selectedText.length == 0) {
66
+ var cell_text = cell.get_text();
67
+ cell_text = fullCellHighlight(cell_text, scheme);
68
+ cell.set_text(cell_text);
69
+ } else {
70
+ cm.replaceSelection(highlight(selectedText, scheme))
71
+ }
72
+ return false;
73
+ }
74
+
75
+ function removeHighlights() {
76
+ var cell = IPython.notebook.get_selected_cell();
77
+ var cell_text = removeFullCellHighlight(cell.get_text());
78
+ cell_text = cell_text.replace(/<span class=(?:"mark"|"burk"|"girk"|"birk"|"pirk")>([\s\S]*?)<\/span>/g,
79
+ function (w, g) {
80
+ return g
81
+ }
82
+ )
83
+ cell.set_text(cell_text)
84
+ cell.render();
85
+ }
86
+
87
+ //*****************************************************************************************
88
+ // Utilitary functions for finding a candidate corresponding text from an unformatted selection
89
+
90
+ /* In case of text selection in rendered cells, the returned text retains no formatting
91
+ therefore, when looking for this text in the actual formatted text, we need to do a
92
+ kind of "fuzzy" alignment. Though there exists specialized libraries for such task,
93
+ we have developed here a simple heuristics that should work 90% of the time,
94
+ but the problem cannot get a perfect solution.
95
+ A first point is to replace special characters that could be interpreded with
96
+ a special meaning in regular expressions. Then the idea is to find the exact matches
97
+ on the longest substring from the beginning of text, then the longest substring
98
+ from the end of the text. Finally, given the locations of the two substring,
99
+ we extract the corresponding global match in the original text.
100
+ */
101
+ function escapeRegExp(str) {
102
+ return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "#");
103
+ // return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
104
+ return str
105
+ }
106
+
107
+ // Extract the longest matching substring from the beginning of the text
108
+ function exsub_up(sub, text) {
109
+ for (k = 0; k <= sub.length; k++) {
110
+ if (text.match(sub.substr(0, k)) == null) {
111
+ k = k - 2
112
+ break
113
+ }
114
+ }
115
+ return text.match(sub.substr(0, k + 1))
116
+ }
117
+
118
+ // Extract the longest matching substring from the end of the text
119
+ function exsub_down(sub, text) {
120
+ var L = sub.length
121
+ try {
122
+ for (k = 0; k <= sub.length; k++) {
123
+ tst = sub.substr(L - k - 1, L);
124
+ if (text.match(tst) == null) {
125
+ // console.log(tst)
126
+ k = k - 1
127
+ break
128
+ }
129
+ }
130
+ return text.match(sub.substr(L - k - 1, L))
131
+ } catch (e) {
132
+ console.log('Error', e)
133
+ return ""
134
+ }
135
+
136
+ }
137
+
138
+ // Function that tries to find the best match of the unformatted
139
+ // text in the formatted one.
140
+
141
+ function align(tofind, text) {
142
+
143
+ sub = escapeRegExp(tofind)
144
+ textModified = escapeRegExp(text)
145
+ //console.log(textModified.match(sub))
146
+ if (textModified.match(sub) == null) {
147
+ a = exsub_up(sub, textModified)
148
+ b = exsub_down(sub, textModified)
149
+ return text.substr(a.index, b.index + b[0].length - a.index)
150
+ } else {
151
+ var tmpMatch = textModified.match(sub)
152
+ return text.substr(tmpMatch.index, tmpMatch[0].length)
153
+ }
154
+ }
155
+
156
+
157
+
158
+ // ***************** Keyboard shortcuts ******************************
159
+
160
+ var add_cmd_shortcuts = {
161
+ 'Alt-g': {
162
+ help: 'highlight selected text',
163
+ help_index: 'ht',
164
+ handler: function (event) {
165
+ highlightInCmdMode("", mark);
166
+ return false;
167
+ }
168
+ },
169
+ 'Alt-h': {
170
+ help: 'highlight selected text',
171
+ help_index: 'ht',
172
+ handler: function (event) {
173
+ highlightInCmdMode("", burk);
174
+ return false;
175
+ }
176
+ },
177
+ };
178
+
179
+
180
+ var add_edit_shortcuts = {
181
+ 'Alt-g': {
182
+ help: 'highlight selected text',
183
+ help_index: 'ht',
184
+ handler: function (event) {
185
+ var highlight = mark;
186
+ highlightInEditMode("", mark);
187
+ return false;
188
+ }
189
+ },
190
+ 'Alt-h': {
191
+ help: 'highlight selected text',
192
+ help_index: 'ht',
193
+ handler: function (event) {
194
+ var highlight = burk;
195
+ highlightInEditMode("", burk);
196
+ return false;
197
+ }
198
+ },
199
+ };
200
+
201
+
202
+ //******Toolbar buttons *************************************************
203
+
204
+ function highlightText(scheme) {
205
+ var cell = IPython.notebook.get_selected_cell();
206
+ var rendered = cell.rendered;
207
+ if (rendered) highlightInCmdMode("", scheme);
208
+ else highlightInEditMode("", scheme);
209
+ }
210
+
211
+
212
+ function build_toolbar() {
213
+ var test = ' <div id="hgl" class="toolbar btn-group" role="toolbar"> \
214
+ <button type="button" class="btn btn-default btn-group" id="higlighter_menu" href="#">\
215
+ <i id="menu-hgl" class="fa fa-caret-right"></i></button>\
216
+ <div id="submenu" class="btn-group" style="font-weight:bold;margin-left:0" > \
217
+ <button type="button" class="btn btn-default highlighter-btn burk" style="font-weight:bold;margin-left:0" href="#" id="b1"></button>\
218
+ <button type="button" class="btn btn-default highlighter-btn mark" style="font-weight:bold;margin-left:0" href="#" id="b2"></button>\
219
+ <button type="button" class="btn btn-default highlighter-btn girk" style="font-weight:bold;margin-left:0" href="#" id="b3"></button>\
220
+ <button type="button" class="btn btn-default highlighter-btn birk" style="font-weight:bold;margin-left:0" href="#" id="b4"></button>\
221
+ <button type="button" class="btn btn-default highlighter-btn pirk" style="font-weight:bold;margin-left:0" href="#" id="b5"></button>\
222
+ <button type="button" class="btn btn-default" style="font-weight:bold;margin-left:0"\
223
+ href="#" id="remove_highlights">\<i class="fa fa-times highlighter-close"></i> </button></div>\
224
+ </div>'
225
+
226
+
227
+ $("#maintoolbar-container").append(test);
228
+ $("#test").css({
229
+ 'padding': '5px'
230
+ });
231
+
232
+ $("#submenu").hide(); // initially hide the submenu
233
+
234
+ //buttons initial css -- shall check if this is really necessary
235
+ // $("#higlighter_menu").css({
236
+ // 'padding': '2px 8px',
237
+ // 'display': 'inline-block',
238
+ // 'border': '1px solid',
239
+ // 'border-color': '#cccccc',
240
+ // 'font-weight': 'bold',
241
+ // 'text-align': 'center',
242
+ // 'vertical-align': 'middle',
243
+ // 'margin-left': '0px',
244
+ // 'margin-right': '0px'
245
+ // })
246
+
247
+
248
+ //Actions
249
+
250
+
251
+ $("#higlighter_menu")
252
+ .on('click', function () {
253
+ $("#submenu").toggle();
254
+ $("#menu-hgl").toggleClass("fa-caret-right")
255
+ $("#menu-hgl").toggleClass("fa-caret-left")
256
+ })
257
+ .attr('title', 'Highlight Selected Text');
258
+
259
+
260
+ $("#b1")
261
+ .on('click', function () {
262
+ highlightText("burk")
263
+ })
264
+ .on('mouseover', function () {
265
+ $("#b1").removeClass("btn btn-default").addClass("btn burk")
266
+ //.addClass("burk");
267
+ }) //!!
268
+ .on('mouseout', function () {
269
+ $("#b1").addClass("btn btn-default")
270
+ })
271
+
272
+
273
+ $("#b2")
274
+ .on('click', function () {
275
+ highlightText("mark")
276
+ })
277
+ .on('mouseover', function () {
278
+ $("#b2").removeClass("btn btn-default").addClass("btn mark")
279
+ }) //!!
280
+ .on('mouseout', function () {
281
+ $("#b2").addClass("btn btn-default")
282
+ })
283
+
284
+ $("#b3")
285
+ .on('click', function () {
286
+ highlightText("girk")
287
+ })
288
+ .on('mouseover', function () {
289
+ $(this).removeClass("btn btn-default").addClass("btn girk")
290
+ }) //!!
291
+ .on('mouseout', function () {
292
+ $(this).addClass("btn btn-default")
293
+ })
294
+
295
+ $("#b4")
296
+ .on('click', function () {
297
+ highlightText("birk")
298
+ })
299
+ .on('mouseover', function () {
300
+ $(this).removeClass("btn btn-default").addClass("btn birk")
301
+ }) //!!
302
+ .on('mouseout', function () {
303
+ $(this).addClass("btn btn-default")
304
+ })
305
+
306
+ $("#b5")
307
+ .on('click', function () {
308
+ highlightText("pirk")
309
+ })
310
+ .on('mouseover', function () {
311
+ $(this).removeClass("btn btn-default").addClass("btn pirk")
312
+ }) //!!
313
+ .on('mouseout', function () {
314
+ $(this).addClass("btn btn-default")
315
+ })
316
+
317
+
318
+ $("#remove_highlights")
319
+ .on('click', function () {
320
+ removeHighlights()
321
+ })
322
+ .attr('title', 'Remove highlightings in selected cell');
323
+ } // end build_toolbar
324
+
325
+ //******************************* MAIN FUNCTION **************************
326
+
327
+ define(["require",
328
+ 'base/js/namespace'
329
+ ], function (requirejs, Jupyter) {
330
+
331
+ var security = requirejs("base/js/security")
332
+
333
+ var load_css = function (name) {
334
+ var link = document.createElement("link");
335
+ link.type = "text/css";
336
+ link.rel = "stylesheet";
337
+ link.href = requirejs.toUrl(name);
338
+ document.getElementsByTagName("head")[0].appendChild(link);
339
+
340
+ };
341
+
342
+ //Load_ipython_extension
343
+ var load_ipython_extension = requirejs(['base/js/namespace'], function (Jupyter) {
344
+ "use strict";
345
+ if (Jupyter.version[0] < 3) {
346
+ console.log("This extension requires Jupyter or IPython >= 3.x")
347
+ return
348
+ }
349
+
350
+ console.log("[highlighter] Loading highlighter.css");
351
+ load_css('./highlighter.css')
352
+
353
+ IPython.keyboard_manager.edit_shortcuts.add_shortcuts(add_edit_shortcuts);
354
+ IPython.keyboard_manager.command_shortcuts.add_shortcuts(add_cmd_shortcuts);
355
+
356
+ build_toolbar();
357
+
358
+ var _on_reload = true; /* make sure cells render on reload */
359
+
360
+ //highlighter_init_cells(); /* initialize cells */
361
+
362
+
363
+ /* on reload */
364
+ $([Jupyter.events]).on('status_started.Kernel', function () {
365
+
366
+ //highlighter_init_cells();
367
+ console.log("[highlighter] reload...");
368
+ _on_reload = false;
369
+ })
370
+
371
+ }); //end of load_ipython_extension function
372
+
373
+ return {
374
+ load_ipython_extension: load_ipython_extension,
375
+ };
376
+ }); //End of main function
377
+
378
+ console.log("Loading ./highlighter.js");
.local/share/jupyter/nbextensions/highlighter/highlighter.yaml ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ Type: IPython Notebook Extension
2
+ Name: highlighter
3
+ Description: Enable to highlight select text in a markdown cell
4
+ Link: readme.md
5
+ Icon: icon.png
6
+ Main: highlighter.js
7
+ Compatibility: 3.x, 4.x, 5.x
.local/share/jupyter/nbextensions/highlighter/icon.png ADDED
.local/share/jupyter/nbextensions/highlighter/readme.md ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Highlighter
2
+ ===========
3
+
4
+ - Firstable, the extension provides <span class="mark">several toolbar buttons</span> for highlighting a selected text _within a markdown cell_. Three different \`color schemes' are provided, which can be easily customized in the stylesheet `highlighter.css`. The last button enables to remove all highlightings in the current cell.
5
+ - This works both <span class="burk">when the cell is _rendered_ and when the cell is in edit mode</span>;
6
+ - In both modes, it is possible to highlight formatted portions of text (In rendered mode, since the selected text loose its formatting, an heuristic is applied to find the best alignment with the actual text)
7
+ - When no text is selected, the whole cell is highlighted;
8
+ - The extension also provides two keyboard shortcuts (Alt-G and Alt-H) which fire the highlighting of the selected text.
9
+ - Highlights can be preserved when exporting to html or to LaTeX -- details are provided in [export_highlights](https://rawgit.com/jfbercher/small_nbextensions/master/usability/highlighter/export_highlights.html)
10
+
11
+
12
+ ![](image.gif)
13
+
14
+
15
+ Installation
16
+ ------------
17
+
18
+ The extension can be installed with the nice UI available on jupyter_nbextensions_configurator website, which also allows to enable/disable the extension.
19
+
20
+ You may also install the extension from the original repo: issue
21
+
22
+ ```bash
23
+ jupyter nbextension install https://rawgit.com/jfbercher/small_nbextensions/master/highlighter.zip --user
24
+ ```
25
+ at the command line.
26
+
27
+
28
+ Testing
29
+ -------
30
+
31
+ Use a code cell with
32
+
33
+ ```python
34
+ %%javascript
35
+ require("base/js/utils").load_extensions("highlighter/highlighter")
36
+ ```
37
+
38
+
39
+ Automatic load
40
+ --------------
41
+
42
+ You may also automatically load the extension for any notebook via
43
+
44
+ ```bash
45
+ jupyter nbextension enable highlighter/highlighter
46
+ ```
47
+
.local/share/jupyter/nbextensions/highlighter/tst_highlights.ipynb ADDED
@@ -0,0 +1,53 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "cells": [
3
+ {
4
+ "cell_type": "markdown",
5
+ "metadata": {},
6
+ "source": [
7
+ "# First cell\n",
8
+ "\n",
9
+ "<span class=\"mark\">In the first cell, we highlight *some words* using the different schemes provided.</span> \n",
10
+ "\n",
11
+ "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus id lacus mauris. Etiam in dictum mauris. <span class=\"burk\">Morbi pharetra, **mauris a feugiat consequat**, est purus vulputate mauris, quis feugiat leo metus eu risus. Sed non luctus arcu.</span> Donec eu ipsum justo. Praesent sit amet euismod orci. Nam eu turpis quis enim pulvinar blandit in eu justo. Vivamus nec libero ipsum. Nunc tempus, mi at vestibulum congue, lacus ante faucibus dolor, quis varius elit felis id ipsum. <span class=\"girk\">Vivamus at mi lorem.</span> Integer quam massa, viverra et fermentum et, cursus faucibus nisl. Vestibulum sed est lacus. Morbi sit amet laoreet odio.\n",
12
+ "\n"
13
+ ]
14
+ },
15
+ {
16
+ "cell_type": "markdown",
17
+ "metadata": {},
18
+ "source": [
19
+ "<div class=\"mark\">\n",
20
+ "# Second cell\n",
21
+ "\n",
22
+ "The second cell is completely highlighted. \n",
23
+ "\n",
24
+ "\n",
25
+ "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus id lacus mauris. Etiam in dictum mauris. Morbi pharetra, mauris a feugiat consequat, <span class=\"burk\">est purus vulputate mauris,</span> quis feugiat leo metus eu risus. Sed non luctus arcu. Donec eu ipsum justo. Praesent sit amet euismod orci. Nam eu turpis quis enim pulvinar blandit in eu justo. Vivamus nec libero ipsum. Nunc tempus, mi at vestibulum congue, <span class=\"girk\">lacus ante faucibus dolor</span>, quis varius elit felis id ipsum. Vivamus at mi lorem. Integer quam massa, viverra et fermentum et, cursus faucibus nisl. Vestibulum sed est lacus. Morbi sit amet laoreet odio.</div><i class=\"fa fa-lightbulb-o \"></i>"
26
+ ]
27
+ }
28
+ ],
29
+ "metadata": {
30
+ "interactive_sols": {
31
+ "cbx_id": 1
32
+ },
33
+ "kernelspec": {
34
+ "display_name": "Python 3",
35
+ "language": "python",
36
+ "name": "python3"
37
+ },
38
+ "language_info": {
39
+ "codemirror_mode": {
40
+ "name": "ipython",
41
+ "version": 3
42
+ },
43
+ "file_extension": ".py",
44
+ "mimetype": "text/x-python",
45
+ "name": "python",
46
+ "nbconvert_exporter": "python",
47
+ "pygments_lexer": "ipython3",
48
+ "version": "3.4.3+"
49
+ },
50
+ },
51
+ "nbformat": 4,
52
+ "nbformat_minor": 0
53
+ }
.local/share/jupyter/nbextensions/hinterland/README.md ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Hinterland
2
+ ==========
3
+
4
+ Enable code autocompletion menu for every keypress in a code cell, instead of
5
+ only calling it with tab.
6
+
7
+ The nbextension adds an item to the help menu to turn auto-hinting on and off,
8
+ and offers some options for configuration:
9
+
10
+
11
+ Options
12
+ -------
13
+
14
+ * `hinterland.hint_delay`:
15
+ delay in milliseconds between keypress & hint request. This is used to help
16
+ ensure that the character from the keypress is added to the CodeMirror editor
17
+ *before* the hint request checks the character preceding the cursor against
18
+ the regexes below.
19
+
20
+ * `hinterland.enable_at_start`:
21
+ Whether to enable hinterland's continuous hinting when notebook is first
22
+ opened, or if false, only when selected from the help-menu item.
23
+
24
+ * `hinterland.hint_inside_comments`:
25
+ Whether to request hints while typing code comments. Defaults to false.
26
+
27
+ * `hinterland.exclude_regexp`:
28
+ A regular expression tested against the character before the cursor, which,
29
+ if a match occurs, prevents autocompletion from being triggered. This is
30
+ useful, for example, to prevent triggering autocomplete on a colon, which is
31
+ included by the default Completer.reinvoke pattern. If blank, no test is
32
+ performed. Note that the regex will be created without any flags, making it
33
+ case sensitive.
34
+
35
+ * `hinterland.include_regexp`:
36
+ A regular expression tested against the character before the cursor, which
37
+ must match in order for autocompletion to be triggered. If left blank, the
38
+ value of the notebook's `Completer.reinvoke_re` parameter is used, which can
39
+ be modified by kernels, but defaults to `/[%0-9a-z._/\\:~-]/i`. Note that
40
+ although the `Completer.reinvoke_re` default is case insensitive by virtue of
41
+ its `/i` flag, any regex specified by the user will be created without any
42
+ flags, making it case sensitive.
43
+
44
+ * `hinterland.tooltip_regexp`:
45
+ A regular expression tested against the character before the cursor, which if
46
+ it matches, causes a tooltip to be triggered, instead of regular
47
+ autocompletion. For python, this is useful for example for function calls, so
48
+ the default regex matches opening parentheses. Note that the regex will be
49
+ created without any flags, making it case sensitive.
.local/share/jupyter/nbextensions/hinterland/hinterland.yaml ADDED
@@ -0,0 +1,73 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Type: Jupyter Notebook Extension
2
+ Main: hinterland.js
3
+ Name: Hinterland
4
+ Link: README.md
5
+ Description: |
6
+ Enable code autocompletion menu for every keypress in a code cell, instead of
7
+ only enabling it with tab
8
+ Compatibility: 4.x, 5.x
9
+ Parameters:
10
+ - name: hinterland.hint_delay
11
+ description: |
12
+ delay in milliseconds between keypress & hint request. This is used to help
13
+ ensure that the character from the keypress is added to the CodeMirror
14
+ editor *before* the hint request checks the character preceding the cursor
15
+ against the regexes below.
16
+ input_type: number
17
+ min: 1
18
+ step: 1
19
+ default: 20
20
+ - name: hinterland.enable_at_start
21
+ description: |
22
+ Enable hinterland's continuous hinting when notebook is first opened, or
23
+ if false, only when selected from the help-menu item.
24
+ input_type: checkbox
25
+ default: true
26
+ - name: hinterland.hint_inside_comments
27
+ description: |
28
+ Whether to request hints while typing code comments.
29
+ input_type: checkbox
30
+ default: false
31
+ - name: hinterland.exclude_regexp
32
+ description: |
33
+ exclude_regexp:
34
+ A regular expression tested against the character before the cursor, which,
35
+ if a match occurs, prevents autocompletion from being triggered.
36
+ This is useful, for example, to prevent triggering autocomplete on a colon,
37
+ which is included by the default Completer.reinvoke pattern.
38
+ If blank, no test is performed.
39
+ Note that the regex will be created without any flags, making it case
40
+ sensitive.
41
+ input_type: text
42
+ # note that the YAML single-quoted string allows us to use the \ character
43
+ # without escaping it, similar to python's raw string syntax
44
+ default: ':'
45
+ - name: hinterland.include_regexp
46
+ description: |
47
+ include_regexp:
48
+ A regular expression tested against the character before the cursor, which
49
+ must match in order for autocompletion to be triggered.
50
+ If left blank, the value of the notebook's Completer.reinvoke_re parameter
51
+ is used, which can be modified by kernels, but defaults to
52
+ /[%0-9a-z._/\\:~-]/i.
53
+ Note that although the Completer.reinvoke_re default is case insensitive by
54
+ virtue of its /i flag, any regex specified by the user will be created
55
+ without any flags, making it case sensitive.
56
+ input_type: text
57
+ # note that the YAML single-quoted string allows us to use the \ character
58
+ # without escaping it, similar to python's raw string syntax
59
+ default: ''
60
+ - name: hinterland.tooltip_regexp
61
+ description: |
62
+ tooltip_regexp:
63
+ A regular expression tested against the character before the cursor, which
64
+ if it matches, causes a tooltip to be triggered, instead of regular
65
+ autocompletion.
66
+ For python, this is useful for example for function calls, so the default
67
+ regex matches opening parentheses.
68
+ Note that the regex will be created without any flags, making it case
69
+ sensitive.
70
+ input_type: text
71
+ # note that the YAML single-quoted string allows us to use the \ character
72
+ # without escaping it, similar to python's raw string syntax
73
+ default: '\('
.local/share/jupyter/nbextensions/init_cell/README.md ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ init_cell
2
+ =========
3
+
4
+ Add a cell toolbar selector to mark cells as 'initialization' cells .
5
+ Such initialization cells are run:
6
+
7
+ * on clicking the provided button in the main toolbar
8
+ ![main toolabr button](icon.png)
9
+ * by default, on kernel ready notification for trusted notebooks.
10
+ This is configurable (see options section).
11
+ In untrusted notebooks, a warning is displayed if the cells would otherwise
12
+ have been run.
13
+
14
+
15
+ Options
16
+ -------
17
+
18
+ This nbextension provides option configurable using the
19
+ [jupyter_nbextensions_configurator](https://github.com/Jupyter-contrib/jupyter_nbextensions_configurator).
20
+
21
+ Once the extension is enabled, turn on the cell toolbar within your Notebook using
22
+ the "View > Cell Toolbar > Initialization Cell" menu
23
+ ![Cell Toobar Menu](cell_toolbar_menu.png)
24
+
25
+
26
+ The running of initialization cells on kernel ready notification can be
27
+ frustrating if your kernel is attached to multiple frontends, or is persistent
28
+ between frontend reloads (e.g. reloading the notebook browser page without
29
+ killing the kernel).
30
+ As such, the option `init_cell.run_on_kernel_ready` in the notebook config
31
+ section controls whether this behaviour occurs.
32
+ The server's config value can also be overridden on a per-notebook basis by
33
+ setting `notebook.metadata.init_cell.run_on_kernel_ready`.
34
+
35
+
36
+ Internals
37
+ ---------
38
+
39
+ Cells are marked as initialization cells in their metadata, as
40
+
41
+ cell.metadata.init_cell = true
42
+
43
+ The running of initialization cells on kernel ready is bound to the Jupyter
44
+ event `kernel_ready.Kernel`.
.local/share/jupyter/nbextensions/init_cell/cell_toolbar_menu.png ADDED
.local/share/jupyter/nbextensions/init_cell/icon.png ADDED
.local/share/jupyter/nbextensions/init_cell/init_cell.yaml ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Type: Jupyter Notebook Extension
2
+ Compatibility: 3.x, 4.x, 5.x
3
+ Name: Initialization cells
4
+ Main: main.js
5
+ Icon: icon.png
6
+ Link: README.md
7
+ Description: |
8
+ Add a cell toolbar selector to mark cells as 'initialization' cells. Such
9
+ initialization cells can be run by on clicking the provided button in the
10
+ main toolbar, or configurably, run automatically on notebook load.
11
+ Parameters:
12
+ - name: init_cell.run_on_kernel_ready
13
+ description: |
14
+ Run input cells whenever a kernel_ready.Kernel event is fired. See readme
15
+ for further details.
16
+ input_type: checkbox
17
+ default: true
.local/share/jupyter/nbextensions/keyboard_shortcut_editor/README.md ADDED
@@ -0,0 +1,130 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Keyboard shortcut editor
2
+ ========================
3
+
4
+ This extension allows you to edit or remove the default notebook keyboard
5
+ shortcuts, or create your own new ones.
6
+
7
+ Currently, this extension supports the editing of any shortcut provided by
8
+ Jupyter, with the exception of those provided by the CodeMirror editors, since
9
+ they use a different API.
10
+
11
+ To edit your keyboard shortcuts, open the keyboard shortcuts help dialog,
12
+ either by pressing <kbd>h</kbd> in command mode, or by selecting
13
+ `help > keyboard shortcuts` from the menu:
14
+
15
+ ![keyboard shortcuts menu item](readme_menu_item.png)
16
+
17
+ When the extension has been loaded, each shortcut in the dialog will show a
18
+ small dropdown menu next to it, with items to remove or edit the shortcut:
19
+
20
+ ![keyboard shortcut edit dropdown](readme_dropdown.png)
21
+
22
+ Clicking the edit item opens a second modal dialog, with a text input. While
23
+ the input has focus, you can press keys to form your combination. The reset
24
+ button (the curly arrow to the left hand side) allows you to clear any keys you
25
+ may input accidentally.
26
+
27
+ ![the shortcut editor dialog](readme_shortcut_editor_blank.png)
28
+ ![the shortcut editor dialog](readme_shortcut_editor_success.png)
29
+
30
+ If you'd like to disable an existing shortcut, you can click the 'Disable'
31
+ button on the dropdown. This will move the shortcut into a new section on the
32
+ dialog headed 'disabled'. You can click the reset button next to disabled
33
+ shortcuts to re-enable them:
34
+
35
+ ![re-enabling a disabled shortcut](readme_reset_disabled.png)
36
+
37
+ You can create new custom keyboard shortcuts using the link at the base of the
38
+ shortcut list for each mode:
39
+
40
+ ![add a new keyboard shortcut](readme_add_new_link.png)
41
+
42
+ This opens a dialog similar to the editor, with the addition of a select box
43
+ from which you can select the action which will be called:
44
+
45
+ ![selecting an action for a new keyboard shortcut](readme_add_new_select_action.png)
46
+
47
+
48
+ Limitations: problem shortcuts
49
+ ------------------------------
50
+
51
+ Since this editor uses the same key-identification method as the notebook,
52
+ anything you can get it to recognise should (?!) work as a notebook shortcut,
53
+ even if it gets represented by the editor differently to the letters on your
54
+ actual, physical, keyboard. However, bear in mind that key identification is
55
+ not perfect, (this is a problem on the web in general), so it's possible that
56
+ some combinations may not be identified by Jupyter at all. In such cases, the
57
+ editor should notify you:
58
+
59
+ ![an unrecognised keypress](readme_undefined_key.png)
60
+
61
+ In addition, the handling of shortcuts including commas is currently
62
+ compromised to the extent that they don't really work properly, so the editor
63
+ also won't accept anything with a comma in it:
64
+
65
+ ![commas don't work properly!](readme_comma.png)
66
+
67
+ The dialog will also not accept a shortcut that would conflict with one which
68
+ already exists:
69
+
70
+ ![conflicting keyboard shortcuts are not accepted](readme_conflict.png)
71
+
72
+ If the conflicting shortcut is provided by Jupyter rather than CodeMirror, you
73
+ can of course disable it to prevent the conflict occurring.
74
+
75
+
76
+ Internals
77
+ ---------
78
+
79
+ The extension stores a record of the edits in use in the config, as a list
80
+ of objects for each mode. Those without a `to` key denote shortcuts to disable,
81
+ while those without a `from` key denote new shortcuts. For example:
82
+
83
+ ```javascript
84
+ // the config object with section name 'notebook' at the base URL
85
+ {
86
+ "kse_rebinds": {
87
+ // command-mode rebindings
88
+ 'command': [
89
+ { // disable the default 'space' shortcut, which used to scroll the notebook down
90
+ from: "space",
91
+ action_name: "jupyter-notebook:scroll-notebook-down"
92
+ },
93
+ { // create a new shortcut 't,t' to trust the notebook
94
+ action_name: "jupyter-notebook:trust-notebook",
95
+ to: "t,t"
96
+ },
97
+ { // change the default save-notebook shortcut from 's' to 'shift-s'
98
+ action_name: "jupyter-notebook:save-notebook",
99
+ to: "shift-s",
100
+ from: "s"
101
+ }
102
+ ],
103
+ // edit-mode rebindings:
104
+ "edit": [
105
+ { // disable the default edit-mode binding which switches to command mode
106
+ action_name: "jupyter-notebook:enter-command-mode",
107
+ from: "ctrl-m"
108
+ }
109
+ ]
110
+ },
111
+ // other config keys may be present in this file!
112
+ }
113
+ ```
114
+
115
+ The extension applies the shortcut edits when it is loaded, and in addition to
116
+ any shortcut registered subsequently, as detailed below.
117
+
118
+
119
+ Patches
120
+ -------
121
+
122
+ The extension applies patches to two Jupyter class prototypes.
123
+ The method `ShortcutManager.prototype.add_shortcut` from `base/js/keyboard`,
124
+ is patched to ensure any appropriate edits are applied to any shortcuts which
125
+ get registered after the extension is loaded, for example by other notebook
126
+ extensions.
127
+
128
+ The `QuickHelp.prototype.build_command_help` and
129
+ `QuickHelp.prototype.build_edit_help` methods from `notebook/js/quickhelp`, are
130
+ patched to insert the dropdown menus, disabled shortcuts and other links.
.local/share/jupyter/nbextensions/keyboard_shortcut_editor/icon.png ADDED
.local/share/jupyter/nbextensions/keyboard_shortcut_editor/kse_components.js ADDED
@@ -0,0 +1,364 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ define([
2
+ 'bootstrap', // for modals
3
+ 'jquery',
4
+ 'base/js/dialog',
5
+ 'base/js/utils',
6
+ 'base/js/keyboard',
7
+ 'notebook/js/quickhelp',
8
+ './quickhelp_shim'
9
+ ], function(
10
+ bs,
11
+ $,
12
+ dialog,
13
+ utils,
14
+ keyboard,
15
+ quickhelp,
16
+ quickhelp_shim
17
+ ){
18
+ "use strict";
19
+
20
+ function only_modifier_event (event) {
21
+ // adapted from base/js/keyboard
22
+ /**
23
+ * Return `true` if the event only contains modifiers keys, false
24
+ * otherwise
25
+ */
26
+ var key = keyboard.inv_keycodes[event.which];
27
+ return ((event.altKey || event.ctrlKey || event.metaKey || event.shiftKey) &&
28
+ (key === 'alt'|| key === 'ctrl'|| key === 'meta'|| key === 'shift'));
29
+ }
30
+
31
+ function editor_build () {
32
+ var editor = $('#kse-editor');
33
+ if (editor.length > 0) {
34
+ return editor;
35
+ }
36
+
37
+ editor = $('<div/>')
38
+ .addClass('kse-editor')
39
+ .attr('id', 'kse-editor')
40
+ .data({
41
+ 'kse_sequence': [],
42
+ 'kse_info': {},
43
+ 'kse_mode': 'command',
44
+ 'kse_undefined_key': false
45
+ });
46
+
47
+ var form = $('<form/>')
48
+ .addClass('form')
49
+ .appendTo(editor);
50
+
51
+ $('<div/>')
52
+ .addClass('form-group')
53
+ .appendTo(form);
54
+
55
+ var form_group = $('<div/>')
56
+ .addClass('form-group has-feedback')
57
+ .appendTo(form);
58
+
59
+ var input_group = $('<div/>')
60
+ .addClass('input-group')
61
+ .addClass('kse-input-group')
62
+ .appendTo(form_group);
63
+
64
+ // reset button
65
+ var btn = $('<a/>')
66
+ .addClass('btn btn-default')
67
+ .addClass('kse-input-group-reset')
68
+ .attr({
69
+ 'title': 'Restart',
70
+ 'type': 'button'
71
+ })
72
+ .append(
73
+ $('<i/>')
74
+ .addClass('fa fa-repeat')
75
+ )
76
+ .on('click', function () {
77
+ editor.data({
78
+ 'kse_sequence': [],
79
+ 'kse_undefined_key': false
80
+ });
81
+ editor_update_input_group(editor);
82
+ $(this).blur();
83
+ textcontrol.focus();
84
+ });
85
+ $('<div/>')
86
+ .addClass('input-group-btn')
87
+ .append(btn)
88
+ .appendTo(input_group);
89
+
90
+ // pretty-displayed shortcut
91
+ $('<div/>')
92
+ .addClass('input-group-addon')
93
+ .addClass('kse-input-group-pretty')
94
+ .addClass('kse-editor-to')
95
+ .appendTo(input_group);
96
+
97
+ var textcontrol = $('<input/>')
98
+ .addClass('form-control')
99
+ .addClass('kse-input-group-input')
100
+ .attr({
101
+ 'type': 'text',
102
+ 'placeholder': 'click here to edit the shortcut'
103
+ })
104
+ .on('keydown', editor_handle_shortcut_keydown)
105
+ .on('focus', function (evt) {
106
+ $(this).attr('placeholder', 'press keys to add to the shortcut');
107
+ })
108
+ .on('blur', function (evt) {
109
+ $(this).attr('placeholder', 'click here to edit the shortcut');
110
+ })
111
+ .appendTo(input_group);
112
+
113
+ // feedback icon
114
+ var form_fdbck = $('<i/>')
115
+ .addClass('fa fa-lg');
116
+ $('<span/>')
117
+ .addClass('form-control-feedback')
118
+ .append(form_fdbck)
119
+ .appendTo(form_group);
120
+
121
+ // help for input group
122
+ $('<span/>')
123
+ .addClass('help-block')
124
+ .appendTo(form_group);
125
+
126
+ return editor;
127
+ }
128
+
129
+ function editor_update_input_group (editor, seq) {
130
+ seq = seq || editor.data('kse_sequence');
131
+ var shortcut = seq.join(',');
132
+ var mode = editor.data('kse_mode');
133
+ var have_seq = seq.length > 0;
134
+ var valid = have_seq;
135
+
136
+ // empty help block
137
+ var feedback = editor.find('.form-group.has-feedback:first');
138
+ var help_block = feedback.find('.help-block');
139
+ help_block.empty();
140
+
141
+ var ii;
142
+ var has_comma = false;
143
+ for (ii = 0; !has_comma && (ii < seq.length); ii++) {
144
+ has_comma = seq[ii].indexOf(',') >= 0;
145
+ }
146
+
147
+ if (has_comma) {
148
+ valid = false;
149
+ // use HTML Unicode escape for a comma, to get it to look right in the pretty version
150
+ shortcut = $.map(seq, function (elem, idx) {
151
+ return elem.replace(',', '&#44;');
152
+ }).join(',');
153
+
154
+ $('<p/>')
155
+ .html(
156
+ 'Unfortunately, Jupyter\'s handling of shortcuts containing ' +
157
+ 'commas (<kbd>,</kbd>) is fundamentally flawed, ' +
158
+ 'as the comma is used as the key-separator character &#9785;. ' +
159
+ 'Please try something else for your rebind!'
160
+ )
161
+ .appendTo(help_block);
162
+ }
163
+ else if (have_seq) {
164
+ var conflicts = {};
165
+ var tree;
166
+
167
+ // get existing shortcuts
168
+ if (Jupyter.keyboard_manager !== undefined) {
169
+ var startkey = seq.slice(0, 1)[0];
170
+ if (mode === 'command') {
171
+ tree = Jupyter.keyboard_manager.command_shortcuts.get_shortcut(startkey);
172
+ }
173
+ else {
174
+ tree = Jupyter.keyboard_manager.edit_shortcuts.get_shortcut(startkey);
175
+ // deal with codemirror shortcuts specially, since they're not included in kbm
176
+ for (var jj = 0; jj < quickhelp.cm_shortcuts.length; jj++) {
177
+ var cm_shrt = quickhelp.cm_shortcuts[jj];
178
+ if (keyboard.normalize_shortcut(cm_shrt.shortcut) === startkey) {
179
+ tree = cm_shrt.help;
180
+ break;
181
+ }
182
+ }
183
+ }
184
+ }
185
+
186
+ // check for conflicting shortcuts.
187
+ // Start at 1 because we got tree from startkey
188
+ for (ii = 1; (ii < seq.length) && (tree !== undefined); ii++) {
189
+ // check for exsiting definitions at current specificity
190
+ if (typeof(tree) === 'string') {
191
+ valid = false;
192
+ conflicts[seq.slice(0, ii).join(',')] = tree;
193
+ break;
194
+ }
195
+ tree = tree[seq[ii]];
196
+ }
197
+
198
+ // check whether any more-specific shortcuts were defined
199
+ if ((ii === seq.length) && (tree !== undefined)) {
200
+ valid = false;
201
+ var flatten_conflict_tree = function flatten_conflict_tree (obj, key) {
202
+ if (typeof(obj) === 'string') {
203
+ conflicts[key] = obj;
204
+ }
205
+ else for (var subkey in obj) {
206
+ if (obj.hasOwnProperty(subkey)) {
207
+ flatten_conflict_tree(obj[key], [key, subkey].join(','));
208
+ }
209
+ }
210
+ };
211
+ flatten_conflict_tree(tree, seq.join(','));
212
+ }
213
+
214
+ if (!valid) {
215
+ var plural = Object.keys(conflicts).length != 1;
216
+ $('<p/>')
217
+ .append(quickhelp.humanize_sequence(seq.join(',')))
218
+ .append(
219
+ ' conflicts with the' + (plural ? ' following' : '') +
220
+ ' existing shortcut' + (plural ? 's' : '') + ':'
221
+ )
222
+ .appendTo(help_block);
223
+
224
+ for (var conflicting_shortcut in conflicts) {
225
+ if (conflicts.hasOwnProperty(conflicting_shortcut)) {
226
+ $('<p/>')
227
+ .append(quickhelp.humanize_sequence(conflicting_shortcut))
228
+ .append($('<code/>').text(conflicts[conflicting_shortcut]))
229
+ .appendTo(help_block);
230
+ }
231
+ }
232
+ }
233
+ }
234
+
235
+ if (editor.data('kse_undefined_key')) {
236
+ var warning = $('<span/>')
237
+ .addClass('form-group has-feedback has-warning kse-undefined')
238
+ .append(
239
+ $('<span/>')
240
+ .addClass('help-block')
241
+ .append(
242
+ $('<p/>').text('Unrecognised key! (code ' + editor.data('kse_undefined_key' ) + ')')
243
+ )
244
+ );
245
+
246
+ var existing = editor.find('.kse-undefined');
247
+ if (existing.length > 0) {
248
+ existing.replaceWith(warning);
249
+ }
250
+ else {
251
+ warning.insertAfter(feedback);
252
+ }
253
+ setTimeout(function () {
254
+ warning.remove();
255
+ }, 2000);
256
+ }
257
+
258
+ // disable reset button if no sequence
259
+ editor.find('.kse-input-group-reset')
260
+ .toggleClass('disabled', !have_seq);
261
+
262
+ editor.find('.kse-input-group-pretty')
263
+ .html(shortcut ? quickhelp.humanize_sequence(shortcut) : '&lt;new shortcut&gt;');
264
+
265
+ feedback
266
+ .toggleClass('has-error', !valid && have_seq)
267
+ .toggleClass('has-success', valid && have_seq)
268
+ .find('.form-control-feedback .fa')
269
+ .toggleClass('fa-remove', !valid && have_seq)
270
+ .toggleClass('fa-check', valid && have_seq);
271
+ }
272
+
273
+ function editor_handle_shortcut_keydown (evt) {
274
+ var elem = $(evt.delegateTarget);
275
+ if (!only_modifier_event(evt)) {
276
+ var shortcut = keyboard.normalize_shortcut(keyboard.event_to_shortcut(evt));
277
+ var editor = elem.closest('#kse-editor');
278
+ var seq = editor.data('kse_sequence');
279
+ var has_undefined_key = (shortcut.toLowerCase().indexOf('undefined') !== -1);
280
+ editor.data('kse_undefined_key', has_undefined_key);
281
+ if (has_undefined_key) {
282
+ // deal with things like ~ appearing on apple alt-n, or Β¨ on alt-u
283
+ editor.find('.kse-input-group-input').val('');
284
+ editor.data('kse_undefined_key', evt.which || true);
285
+ }
286
+ else {
287
+ seq.push(shortcut);
288
+ }
289
+ editor_update_input_group(editor, seq);
290
+ }
291
+ }
292
+
293
+ function modal_build (editor, modal_options) {
294
+ var modal = $('#kse-editor-modal');
295
+ if (modal.length > 0) {
296
+ return modal;
297
+ }
298
+
299
+ var default_modal_options = {
300
+ 'destroy': false,
301
+ 'show': false,
302
+ 'title': 'Edit keyboard shortcut',
303
+ 'body': editor,
304
+ 'buttons': {
305
+ 'OK': {'class': 'btn-primary'},
306
+ 'Cancel': {}
307
+ },
308
+ 'open': function (evt) {
309
+ $(this).find('.kse-input-group-input').focus();
310
+ }
311
+ };
312
+ if (Jupyter.notebook !== undefined) {
313
+ default_modal_options.notebook = Jupyter.notebook;
314
+ }
315
+ if (Jupyter.keyboard_manager !== undefined) {
316
+ default_modal_options.keyboard_manager= Jupyter.keyboard_manager;
317
+ }
318
+ modal_options = $.extend({}, default_modal_options, modal_options);
319
+
320
+ modal = dialog.modal(modal_options);
321
+
322
+ modal
323
+ .addClass('modal_stretch')
324
+ .attr('id', 'kse-editor-modal');
325
+
326
+ // Add a data-target attribute to ensure buttons only target the editor modal
327
+ modal.find('.close,.modal-footer button')
328
+ .attr('data-target', '#kse-editor-modal');
329
+
330
+ return modal;
331
+ }
332
+
333
+ /**
334
+ * Pass it an option dictionary with any of the bootstrap or base/js/dialog
335
+ * modal options, plus the following optional properties:
336
+ * - description: html for the form group preceding the editor group,
337
+ * useful as a description
338
+ */
339
+ function KSE_modal (modal_options) {
340
+ var editor = editor_build();
341
+ editor.data({'kse_sequence': [], 'kse_undefined_key': false});
342
+ editor_update_input_group(editor);
343
+ var modal = modal_build(editor, modal_options);
344
+
345
+ editor.on('keydown', '.kse-input-group-input', function (evt) {
346
+ event.preventDefault();
347
+ event.stopPropagation();
348
+ return false;
349
+ });
350
+
351
+ if (modal_options.description) {
352
+ modal.find('.modal-body .form-group:first').html(modal_options.description);
353
+ }
354
+
355
+ return modal;
356
+ }
357
+
358
+ return {
359
+ editor_build : editor_build,
360
+ editor_update_input_group: editor_update_input_group,
361
+ modal_build : modal_build,
362
+ KSE_modal : KSE_modal
363
+ };
364
+ });
.local/share/jupyter/nbextensions/keyboard_shortcut_editor/main.js ADDED
@@ -0,0 +1,778 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ define([
2
+ 'jquery',
3
+ 'require',
4
+ 'base/js/namespace',
5
+ 'base/js/dialog',
6
+ 'base/js/events',
7
+ 'base/js/keyboard',
8
+ 'notebook/js/quickhelp',
9
+ './quickhelp_shim',
10
+ './kse_components',
11
+ ], function (
12
+ $,
13
+ requirejs,
14
+ Jupyter,
15
+ dialog,
16
+ events,
17
+ keyboard,
18
+ quickhelp,
19
+ qh_shim,
20
+ kse_comp
21
+ ) {
22
+ "use strict";
23
+
24
+ var mod_name = 'keyboard_shortcut_editor';
25
+
26
+ // define default values for config parameters
27
+ var params = {
28
+ 'kse_show_rebinds': true,
29
+ // mode, action name, new combo
30
+ 'kse_rebinds': {
31
+ // command-mode rebindings
32
+ 'command': [
33
+ // { // disable the default 'space' shortcut, which used to scroll the notebook down
34
+ // from: "space",
35
+ // action_name: "jupyter-notebook:scroll-notebook-down"
36
+ // },
37
+ // { // create a new shortcut 't,t' to trust the notebook
38
+ // action_name: "jupyter-notebook:trust-notebook",
39
+ // to: "t,t"
40
+ // },
41
+ // { // change the default save-notebook shortcut from 's' to 'shift-s'
42
+ // action_name: "jupyter-notebook:save-notebook",
43
+ // to: "shift-s",
44
+ // from: "s"
45
+ // }
46
+ ],
47
+ // edit-mode rebindings:
48
+ "edit": [
49
+ // { // disable the default edit-mode binding which switches to command mode
50
+ // action_name: "jupyter-notebook:enter-command-mode",
51
+ // from: "ctrl-m"
52
+ // }
53
+ ]
54
+ }
55
+ };
56
+ // function to update params with any specified in the server's config file
57
+ function update_params () {
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
+ }
64
+ }
65
+
66
+ function add_css (url) {
67
+ $('<link/>')
68
+ .attr({
69
+ 'rel': 'stylesheet',
70
+ 'type': 'text/css',
71
+ 'href': requirejs.toUrl(url)
72
+ })
73
+ .appendTo($('head'));
74
+ }
75
+
76
+ var kbm = Jupyter.keyboard_manager;
77
+ var deleted_shortcuts = {
78
+ 'command': new keyboard.ShortcutManager(undefined, kbm.command_shortcuts.events, kbm.actions, kbm.env),
79
+ 'edit': new keyboard.ShortcutManager(undefined, kbm.edit_shortcuts.events, kbm.actions, kbm.env)
80
+ };
81
+
82
+ var patched_quickhelp_prototype = false;
83
+ var patched_shortcut_manager_prototype = false;
84
+
85
+ function patch_shortcut_manager_prototype () {
86
+ if (!patched_shortcut_manager_prototype) {
87
+ var orig_add_shortcut = keyboard.ShortcutManager.prototype.add_shortcut;
88
+ keyboard.ShortcutManager.prototype.add_shortcut = function add_shortcut (shortcut, data, suppress_help_update, called_by_rebinder) {
89
+ if (!called_by_rebinder) {
90
+ var this_mode;
91
+ if (this === kbm.edit_shortcuts) {
92
+ this_mode = 'edit';
93
+ }
94
+ else if (this === kbm.command_shortcuts) {
95
+ this_mode = 'command';
96
+ }
97
+ if (this_mode) {
98
+ var rebind_specs = params.kse_rebinds[this_mode];
99
+ for (var ii = 0; ii < rebind_specs.length; ii++) {
100
+ var spec = rebind_specs[ii];
101
+ if (spec.from === shortcut) {
102
+ if (!spec.to) {
103
+ return;
104
+ }
105
+ shortcut = spec.to;
106
+ }
107
+ }
108
+ }
109
+ }
110
+ return orig_add_shortcut.call(this, shortcut, data, suppress_help_update);
111
+ };
112
+ console.log('[' + mod_name + '] patched ShortcutManager.prototype.add_shortcut');
113
+ patched_shortcut_manager_prototype = true;
114
+ }
115
+ }
116
+
117
+ function patch_quickhelp_prototype () {
118
+ if (!patched_quickhelp_prototype) {
119
+ var orig_build_command_help = quickhelp.QuickHelp.prototype.build_command_help;
120
+ quickhelp.QuickHelp.prototype.build_command_help = function () {
121
+ var div = orig_build_command_help.call(this);
122
+ return quickhelp_div_add_rebind_controls(div, 'command');
123
+ };
124
+ console.log('[' + mod_name + '] patched QuickHelp.prototype.build_command_help');
125
+
126
+ var orig_build_edit_help = quickhelp.QuickHelp.prototype.build_edit_help;
127
+ quickhelp.QuickHelp.prototype.build_edit_help = function (cm_shortcuts) {
128
+ var div = orig_build_edit_help.call(this, cm_shortcuts);
129
+ return quickhelp_div_add_rebind_controls(div, 'edit');
130
+ };
131
+ console.log('[' + mod_name + '] patched QuickHelp.prototype.build_edit_help');
132
+
133
+ patched_quickhelp_prototype = true;
134
+ }
135
+ }
136
+
137
+ function load_jupyter_extension () {
138
+ add_css('./main.css');
139
+ patch_shortcut_manager_prototype();
140
+ patch_quickhelp_prototype();
141
+ Jupyter.notebook.config.loaded.then(initialize);
142
+ }
143
+
144
+ function get_mode_shortcuts (mode, deleted) {
145
+ if (deleted) {
146
+ return deleted_shortcuts[mode];
147
+ }
148
+ else if (mode === 'command') {
149
+ return kbm.command_shortcuts;
150
+ }
151
+ else if (mode === 'edit') {
152
+ return kbm.edit_shortcuts;
153
+ }
154
+ return undefined;
155
+ }
156
+
157
+ function rebind (mode, spec, suppress_help_update) {
158
+ var shortcuts = get_mode_shortcuts(mode);
159
+ if (spec.action_name === undefined) {
160
+ spec.action_name = shortcuts.get_shortcut(spec.from);
161
+ }
162
+ if (!shortcuts.actions.exists(spec.action_name)) {
163
+ console.warn(
164
+ '[' + mod_name + '] ' +
165
+ 'rebind specified for unrecognised action "' +
166
+ spec.action_name + '"' +
167
+ (spec.from ? ' from ' + spec.from : '') +
168
+ (spec.to ? ' to ' + spec.to : '')
169
+ );
170
+ }
171
+ else {
172
+ console.log(
173
+ '[' + mod_name + '] ' +
174
+ (spec.from ? (spec.to ? 're' : 'un') : '') + 'bound ' +
175
+ spec.action_name +
176
+ (spec.from ? ' from ' + spec.from : '') +
177
+ (spec.to ? ' to ' + spec.to : '')
178
+ );
179
+
180
+ if (spec.from) {
181
+ if (!spec.to) {
182
+ deleted_shortcuts[mode].add_shortcut(spec.from, spec.action_name, true, true);
183
+ }
184
+ shortcuts.remove_shortcut(spec.from);
185
+ }
186
+ if (spec.to) {
187
+ return shortcuts.add_shortcut(spec.to, spec.action_name, suppress_help_update, true);
188
+ }
189
+ }
190
+ }
191
+
192
+ function apply_config_rebinds () {
193
+ var modes = ['command', 'edit'];
194
+ for (var mm = 0; mm < modes.length; mm++) {
195
+ var mode = modes[mm];
196
+ if (params.kse_rebinds.hasOwnProperty(mode)) {
197
+ var rebind_specs = params.kse_rebinds[mode];
198
+ for (var ii = 0; ii < rebind_specs.length; ii++) {
199
+ rebind(mode, rebind_specs[ii], true);
200
+ }
201
+ }
202
+ }
203
+ events.trigger('rebuild.QuickHelp');
204
+ }
205
+
206
+ var initialize = function () {
207
+ update_params();
208
+ apply_config_rebinds();
209
+ var title = $('#keyboard_shortcuts').attr('title');
210
+ $('#keyboard_shortcuts').attr('title', title + ' & controls to edit them');
211
+ };
212
+
213
+ function reverse_spec (spec) {
214
+ var new_spec = {action_name: spec.action_name};
215
+ if (spec.from) {
216
+ new_spec.to = spec.from;
217
+ }
218
+ if (spec.to) {
219
+ new_spec.from = spec.to;
220
+ }
221
+ return new_spec;
222
+ }
223
+
224
+ function find_rebinding (rebinds, partial_spec, index_only) {
225
+ for (var ii = 0; ii < rebinds.length; ii++) {
226
+ if (((partial_spec.to === undefined) ||
227
+ (partial_spec.to === rebinds[ii].to)) &&
228
+ ((partial_spec.from === undefined) ||
229
+ (partial_spec.from === rebinds[ii].from)) &&
230
+ ((partial_spec.action_name === undefined) ||
231
+ (partial_spec.action_name === rebinds[ii].action_name))) {
232
+ return index_only ? ii : rebinds[ii];
233
+ }
234
+ }
235
+ return undefined;
236
+ }
237
+
238
+ function register_rebinding (mode, spec) {
239
+ var rebinds = params.kse_rebinds[mode];
240
+ rebinds.push($.extend({}, spec));
241
+ // write our private copy to the config:
242
+ Jupyter.notebook.config.update(params);
243
+ console.log('[' + mod_name + '] rebinding added:', spec);
244
+ }
245
+
246
+ function deregister_rebinding (mode, partial_spec) {
247
+ var rebinds = params.kse_rebinds[mode];
248
+ var idx = find_rebinding(rebinds, partial_spec, true);
249
+ if (idx === undefined) {
250
+ console.warn('[' + mod_name + '] attempted to delete non-exsitent shortcut:', partial_spec);
251
+ return undefined;
252
+ }
253
+ var deleted = rebinds.splice(idx, 1)[0];
254
+ // write our private copy to the config:
255
+ Jupyter.notebook.config.update(params);
256
+ console.log('[' + mod_name + '] rebinding removed:', deleted);
257
+ return deleted;
258
+ }
259
+
260
+ function action_selector (default_pair) {
261
+ var select = $('<select/>')
262
+ .append(
263
+ $('<option/>')
264
+ .attr('value', '')
265
+ .text('select an action')
266
+ )
267
+ .addClass('form-control select-xs');
268
+
269
+ var action_names = [];
270
+ $.each(kbm.actions._actions, function (key, val) {
271
+ if (key !== 'ignore') {
272
+ action_names.push(key);
273
+ }
274
+ });
275
+ action_names.sort();
276
+
277
+ for (var ii = 0; ii < action_names.length; ii++) {
278
+ select.append(
279
+ $('<option/>')
280
+ .attr('value', action_names[ii])
281
+ .append(
282
+ $('<code/>')
283
+ .text(action_names[ii])
284
+ )
285
+ // .text(kbm.actions.get(action_name).help)
286
+ );
287
+ }
288
+ return select;
289
+ }
290
+
291
+ function quickhelp_rebuild_mode_div (div) {
292
+ if ((div === undefined) || (div.data('kse_mode') === 'command')) {
293
+ div.replaceWith(Jupyter.quick_help.build_command_help());
294
+ }
295
+ if ((div === undefined) || (div.data('kse_mode') === 'edit')) {
296
+ div.replaceWith(Jupyter.quick_help.build_edit_help(quickhelp.cm_shortcuts));
297
+ }
298
+ return div;
299
+ }
300
+
301
+ function modal_update_ok_disable_status (evt) {
302
+ var editor = $(evt.delegateTarget);
303
+ if (!editor.is('#kse-editor')) {
304
+ editor = editor.closest('#kse-editor');
305
+ }
306
+ var feedback = editor.find('.has-feedback:first');
307
+ var valid = feedback.hasClass('has-success');
308
+ if (valid) {
309
+ valid = valid && editor.data('kse_sequence').length > 0;
310
+ }
311
+ var modal = editor.closest('#kse-editor-modal');
312
+ if (valid) {
313
+ var select = modal.find('select');
314
+ if (select.length > 0) {
315
+ valid = valid && select.val();
316
+ }
317
+ }
318
+ modal.find('.modal-footer button:first').prop('disabled', !valid);
319
+ }
320
+
321
+
322
+ function modal_ok_click_callback (evt) {
323
+ var editor = $('#kse-editor');
324
+ var new_shortcut = editor.data('kse_sequence').join(',');
325
+ var info = editor.data('kse_info');
326
+ var mode = editor.data('kse_mode');
327
+ var new_spec = {
328
+ 'action_name': info.action_name,
329
+ 'to': new_shortcut
330
+ };
331
+ if (info.rebound) {
332
+ // editing an existing rebinding
333
+ deregister_rebinding(mode, info.spec);
334
+ // rebind directly
335
+ rebind(mode, $.extend({'from': info.spec.to}, new_spec), true);
336
+ // get registration correct
337
+ new_spec.from = info.spec.from;
338
+ }
339
+ else {
340
+ if (info.spec.to) {
341
+ // editing an existing binding, so ensure there's a from
342
+ new_spec.from = info.spec.to;
343
+ }
344
+ rebind(mode, new_spec, true);
345
+ }
346
+ if (new_spec.from !== new_spec.to) {
347
+ register_rebinding(mode, new_spec);
348
+ }
349
+ quickhelp_rebuild_mode_div(info.div);
350
+ }
351
+
352
+ var modal_options_for_edit = {
353
+ backdrop: false,
354
+ buttons: {
355
+ OK: {
356
+ 'class':'btn-primary',
357
+ 'click': modal_ok_click_callback
358
+ },
359
+ Cancel: {}
360
+ }
361
+ };
362
+
363
+ function modal_prepare_editor(modal, editor, info) {
364
+ // ensure events are bound
365
+ if (!editor.data('kse_modal_events_bound')) {
366
+ editor.on('keydown', '.kse-input-group-input', function (evt) {
367
+ evt.preventDefault();
368
+ evt.stopPropagation();
369
+ modal_update_ok_disable_status(evt);
370
+ return false;
371
+ });
372
+ editor.on('click', '.kse-input-group-reset', modal_update_ok_disable_status);
373
+ editor.on('change', 'select', modal_update_ok_disable_status);
374
+ editor.data('kse_modal_events_bound', true);
375
+ }
376
+ // reset data
377
+ editor.data('kse_sequence', []);
378
+ editor.data('kse_info', info);
379
+ editor.data('kse_mode', info.mode);
380
+ editor.find('.kse-input-group-reset').click();
381
+
382
+ // add description
383
+ var descript_div = editor.find('.form-group:first').empty();
384
+ if (info.action_name) { // this is a rebind
385
+ descript_div
386
+ .append('Rebinding ')
387
+ .append(
388
+ $('<code/>')
389
+ .addClass('kse-action')
390
+ .text(info.spec.action_name)
391
+ )
392
+ .append(' from ')
393
+ .append(
394
+ $('<span/>')
395
+ .addClass('kse-from')
396
+ .html(quickhelp.humanize_sequence(info.spec.to || ''))
397
+ )
398
+ .append(' to:');
399
+ }
400
+ else {
401
+ // this is a nubind
402
+ var select = action_selector();
403
+ select.on('change', function (event) {
404
+ var action_name = $(this).val();
405
+ $.extend(true, info, {'action_name': action_name, 'spec': {'action_name': action_name}});
406
+ });
407
+ descript_div
408
+ .append('Bind ')
409
+ .append(select)
410
+ .append(' to:');
411
+ }
412
+ kse_comp.editor_update_input_group(editor);
413
+ }
414
+
415
+ function modal_show_above_quickhelp_modal(modal) {
416
+ // add a custom backdrop which covers the quickhelp modal.
417
+ // We do this every time, as bootstrap destroys it every time.
418
+ var backdrop = modal.data('bs.modal').$backdrop = $('<div/>')
419
+ .attr('id', 'kse-modal-backdrop')
420
+ .addClass('kse-modal-backdrop')
421
+ .addClass('modal-backdrop fade')
422
+ .appendTo(Jupyter.quick_help.shortcut_dialog.find('.modal-content'));
423
+
424
+ // get offsetWidth to force reflow, otherwise animation doesn't show
425
+ var tmp = backdrop[0].offsetWidth;
426
+ // now trigger animation by adding class
427
+ backdrop
428
+ .addClass('in');
429
+ // show the modal once the (backdrop) transition is complete
430
+ backdrop
431
+ .one('bsTransitionEnd', function () {
432
+ modal.modal('show');
433
+ })
434
+ .emulateTransitionEnd(150);
435
+ }
436
+
437
+ function build_rebind_rep_list (mode) {
438
+ var rep_list = $('<div/>')
439
+ .addClass('kse-rep-list');
440
+
441
+ var shortcuts = get_mode_shortcuts(mode);
442
+ var rebind_specs = params.kse_rebinds[mode];
443
+
444
+ for (var ii = 0; ii < rebind_specs.length; ii++) {
445
+ var spec = rebind_specs[ii];
446
+ var verb = (spec.from ? (spec.to ? 're' : 'un') : '') + 'bound';
447
+ var rep = $('<div/>')
448
+ .append(
449
+ $('<code/>')
450
+ .text(spec.action_name)
451
+ )
452
+ .append(' ' + verb)
453
+ .appendTo(rep_list);
454
+ if (spec.from) {
455
+ rep.append(' from ' + quickhelp.humanize_sequence(spec.from));
456
+ }
457
+ if (spec.to) {
458
+ rep.append(' to ' + quickhelp.humanize_sequence(spec.to));
459
+ }
460
+ }
461
+
462
+ return rep_list;
463
+ }
464
+
465
+ function btn_get_info (btn) {
466
+ /**
467
+ * get the shortcut info associated with a quickhelp button element
468
+ */
469
+ var info = {};
470
+ btn = info.btn = $(btn);
471
+ var qh_div = info.qh_div = btn.closest('.quickhelp');
472
+ var div = info.div = qh_div.closest('.kse-div');
473
+
474
+ var mode = info.mode = div.data('kse_mode');
475
+ var unbound = info.unbound = qh_div.hasClass('kse-unbound');
476
+ var rebound = info.rebound = qh_div.hasClass('kse-rebound');
477
+ info.nubound = qh_div.hasClass('kse-nubound');
478
+
479
+ var shortcuts = info.shortcuts = get_mode_shortcuts(mode, unbound);
480
+ // Make sure we remove jupyter-notebook:ignore shortcuts.
481
+ var shortcut_help_arr = info.shortcut_help_arr = shortcuts.help().filter(
482
+ function (shortcut) {
483
+ return (shortcut.help !== 'ignore');
484
+ }
485
+ );
486
+
487
+ var idx = info.idx = div.find('.quickhelp:not(.kse-links,.kse-codemirror)')[unbound ? 'filter' : 'not']('.kse-unbound').index(qh_div);
488
+
489
+ var keycombo = info.keycombo = shortcut_help_arr[idx].shortcut;
490
+ var action_name = info.action_name = shortcuts.get_shortcut(keycombo);
491
+ var spec = info.spec = {
492
+ 'to': keycombo,
493
+ 'action_name': action_name
494
+ };
495
+ if (rebound) {
496
+ spec.from = find_rebinding(params.kse_rebinds[mode], spec).from;
497
+ }
498
+ return info;
499
+ }
500
+
501
+ function btn_del_callback (evt) {
502
+ evt.preventDefault(); // ignore #
503
+
504
+ var info = btn_get_info(evt.delegateTarget);
505
+ var spec = info.spec;
506
+ var rev_spec = reverse_spec(spec);
507
+ rebind(info.mode, rev_spec, true);
508
+
509
+ if (info.nubound) {
510
+ // don't want deleted new binds to show up in deleted shortcuts
511
+ deleted_shortcuts[info.mode].remove_shortcut(spec.to);
512
+ deregister_rebinding(info.mode, spec);
513
+ }
514
+ else if (info.rebound) {
515
+ // get rid of existing rebinding
516
+ deregister_rebinding(info.mode, spec);
517
+ // add a new rebinding for the deletion
518
+ delete spec.to;
519
+ rebind(info.mode, spec, true);
520
+ register_rebinding(info.mode, spec);
521
+ }
522
+ else {
523
+ register_rebinding(info.mode, rev_spec);
524
+ }
525
+ quickhelp_rebuild_mode_div(info.div);
526
+ }
527
+
528
+ function btn_rst_callback (evt) {
529
+ evt.preventDefault(); // ignore #
530
+
531
+ var info = btn_get_info(evt.delegateTarget);
532
+ var spec = info.spec;
533
+ if (info.unbound) {
534
+ spec = reverse_spec(spec);
535
+ deleted_shortcuts[info.mode].remove_shortcut(spec.from);
536
+ }
537
+ rebind(info.mode, reverse_spec(spec), true);
538
+ deregister_rebinding(info.mode, spec);
539
+
540
+ quickhelp_rebuild_mode_div(info.div);
541
+ }
542
+
543
+ function btn_edt_callback (evt) {
544
+ evt.preventDefault(); // ignore #
545
+
546
+ var info = btn_get_info(evt.delegateTarget);
547
+ var spec = reverse_spec(info.spec);
548
+ // see if the shortcut was already rebound...
549
+ var rebinds = params.kse_rebinds[info.mode];
550
+ for (var ii = 0; ii < rebinds.length; ii++) {
551
+ if (rebinds[ii].to === spec.from) {
552
+ spec = rebinds[ii];
553
+ break;
554
+ }
555
+ }
556
+
557
+ var editor = kse_comp.editor_build();
558
+ var modal = kse_comp.modal_build(editor, modal_options_for_edit);
559
+
560
+ modal_prepare_editor(modal, editor, info);
561
+ modal_show_above_quickhelp_modal(modal);
562
+ }
563
+
564
+ function btn_add_callback (evt) {
565
+ evt.preventDefault(); // don't use #
566
+
567
+ var div = $(evt.delegateTarget).closest('.kse-div');
568
+ var mode = div.data('kse_mode');
569
+ var info = {
570
+ 'div': div,
571
+ 'spec': {},
572
+ 'shortcuts': get_mode_shortcuts(mode),
573
+ 'keycombo': '',
574
+ 'action_name': '',
575
+ 'mode': mode
576
+ };
577
+
578
+ var editor = kse_comp.editor_build();
579
+ var modal = kse_comp.modal_build(editor, modal_options_for_edit);
580
+
581
+ modal_prepare_editor(modal, editor, info);
582
+ modal_show_above_quickhelp_modal(modal, editor, info);
583
+ }
584
+
585
+ function btn_view_callback (evt) {
586
+ evt.preventDefault(); // don't use #
587
+
588
+ var div = $(evt.delegateTarget).closest('.kse-div');
589
+ var mode = div.data('kse_mode');
590
+ var modal = dialog.modal({
591
+ backdrop: false, // a custom one gets added by modal_show_above_quickhelp_modal
592
+ show: false,
593
+ title : mode.substring(0, 1).toUpperCase() + mode.substring(1) + '-mode keyboard shortcut edits',
594
+ body : build_rebind_rep_list(mode),
595
+ buttons: {'OK': {}},
596
+ notebook: Jupyter.notebook,
597
+ keyboard_manager: Jupyter.keyboard_manager
598
+ });
599
+
600
+ modal
601
+ .addClass('modal_stretch')
602
+ .attr('id', 'kse-view-modal');
603
+
604
+ // Add a data-target attribute to ensure buttons only target this modal
605
+ modal.find('.close,.modal-footer button')
606
+ .attr('data-target', '#kse-view-modal');
607
+ modal_show_above_quickhelp_modal(modal);
608
+ }
609
+
610
+ function build_button_menu (idx, qh_div) {
611
+ qh_div = $(qh_div);
612
+
613
+ var grp = $('<div/>')
614
+ .addClass('btn-group btn-group-xs')
615
+ .addClass('kse-dropdown')
616
+ .addClass('hidden-print')
617
+ .appendTo(qh_div);
618
+
619
+ var btn = $('<button/>')
620
+ .addClass('btn btn-default')
621
+ .attr('type', 'button')
622
+ .appendTo(grp);
623
+
624
+ if (qh_div.hasClass('kse-codemirror')) {
625
+ btn
626
+ .addClass('disabled')
627
+ .attr('title', 'Editing Codemirror shortcuts is not supported')
628
+ .html('<i class="fa">cm</i>');
629
+ }
630
+ else if (qh_div.hasClass('kse-unbound')) {
631
+ btn
632
+ .on('click', btn_rst_callback)
633
+ .attr('title', 'Re-enable shortcut')
634
+ .html('<i class="fa fa-repeat"/>');
635
+ }
636
+ else if (qh_div.hasClass('kse-nubound')) {
637
+ btn
638
+ .on('click', btn_del_callback)
639
+ .attr('title', 'Delete custom shortcut')
640
+ .html('<i class="fa fa-trash"/>');
641
+ }
642
+ else {
643
+ btn
644
+ .attr({
645
+ 'title': 'Edit',
646
+ 'data-toggle': 'dropdown',
647
+ 'aria-haspopup': 'true',
648
+ 'aria-expanded': 'false'
649
+ })
650
+ .addClass('dropdown-toggle')
651
+ .html('<i class="fa fa-pencil"/> <i class="fa fa-caret-down"/>');
652
+
653
+ var mnu = $('<ul/>')
654
+ .addClass('dropdown-menu dropdown-menu-right')
655
+ .appendTo(grp);
656
+ $('<li/>')
657
+ .html('<a href="#" title="Edit shortcut"><i class="fa fa-pencil"></i> Edit</a>')
658
+ .on('click', btn_edt_callback)
659
+ .appendTo(mnu);
660
+ if (qh_div.hasClass('kse-rebound')) {
661
+ $('<li/>')
662
+ .on('click', btn_rst_callback)
663
+ .html('<a href="#" title="Reset shortcut to default key(s)"><i class="fa fa-repeat"></i> Reset</a>')
664
+ .appendTo(mnu);
665
+ }
666
+ $('<li/>')
667
+ .on('click', btn_del_callback)
668
+ .html('<a href="#" title="Disable shortcut"><i class="fa fa-ban"></i> Disable</a>')
669
+ .appendTo(mnu);
670
+ }
671
+
672
+ return grp;
673
+ }
674
+
675
+ function quickhelp_div_add_rebind_controls (div, mode) {
676
+ if (!params.kse_show_rebinds) {
677
+ return div;
678
+ }
679
+
680
+ div
681
+ .data('kse_mode', mode)
682
+ .addClass('kse-div');
683
+
684
+ var nubound_shortcuts = [];
685
+ var rebound_shortcuts = [];
686
+ for (var ii = 0; ii < params.kse_rebinds[mode].length; ii++) {
687
+ var spec = params.kse_rebinds[mode][ii];
688
+ if (spec.to) {
689
+ (spec.from ? rebound_shortcuts : nubound_shortcuts).push(spec.to);
690
+ }
691
+ }
692
+
693
+ var shortcuts = get_mode_shortcuts(mode);
694
+ var shortcut_help_arr = shortcuts.help();
695
+ // add codemirror shortcuts in edit mode
696
+ if (mode === 'edit') {
697
+ shortcut_help_arr = $.merge($.merge([], quickhelp.cm_shortcuts), shortcut_help_arr);
698
+ }
699
+ // Remove jupyter-notebook:ignore shortcuts.
700
+ shortcut_help_arr = shortcut_help_arr.filter(function(shortcut) {
701
+ return (shortcut.help !== 'ignore');
702
+ });
703
+
704
+ // label quickhelp divs with classes
705
+ div.find('.quickhelp').each(function (idx, elem) {
706
+ var keycombo = shortcut_help_arr[idx].shortcut;
707
+ var action_name = shortcuts.get_shortcut(keycombo);
708
+ if (mode === 'edit' && action_name === undefined) {
709
+ $(elem).addClass('kse-codemirror');
710
+ }
711
+ else if (nubound_shortcuts.indexOf(keycombo) >= 0) {
712
+ $(elem).addClass('kse-nubound');
713
+ }
714
+ else if (rebound_shortcuts.indexOf(keycombo) >= 0) {
715
+ $(elem).addClass('kse-rebound');
716
+ }
717
+ });
718
+
719
+ // (maybe) add a set of rebinds and deleted shortcuts
720
+ div.find('.kse-rep-list').remove();
721
+ var cont = div.find('.container-fluid:first');
722
+
723
+ var del_div = quickhelp.build_div('<h5>Disabled:<h5/>', deleted_shortcuts[mode].help());
724
+ del_div
725
+ .addClass('hidden-print')
726
+ .addClass('text-danger hidden-print');
727
+ if (del_div.find('.quickhelp').addClass('kse-unbound').length > 0) {
728
+ del_div
729
+ .insertAfter(cont);
730
+ }
731
+
732
+ // add button menus
733
+ div.find('.quickhelp').each(build_button_menu);
734
+
735
+ var link_new = $('<a/>')
736
+ .attr('href', '#')
737
+ .on('click', btn_add_callback);
738
+
739
+ $('<span/>')
740
+ .addClass('shortcut_key')
741
+ .html('<i class="fa fa-plus"></i>')
742
+ .appendTo(link_new);
743
+
744
+ $('<span/>')
745
+ .addClass('shortcut_descr')
746
+ .text(': Add a new ' + mode + '-mode shortcut')
747
+ .appendTo(link_new);
748
+
749
+ $('<div/>')
750
+ .addClass('col-xs-12 hidden-print quickhelp kse-links')
751
+ .append(link_new)
752
+ .appendTo(cont);
753
+
754
+ // add the view-rebinds and add-a-new links
755
+ if (params.kse_rebinds[mode].length > 0) {
756
+ var link_view = link_new.clone()
757
+ .off('click')
758
+ .on('click', btn_view_callback)
759
+ .appendTo(cont);
760
+ link_view.find('.shortcut_key')
761
+ .html('<i class="fa fa-eye"></i>');
762
+ link_view.find('.shortcut_descr')
763
+ .text(': View active ' + mode + '-mode shortcut edits');
764
+
765
+ $('<div/>')
766
+ .addClass('col-xs-12 hidden-print quickhelp kse-links')
767
+ .append(link_view)
768
+ .appendTo(cont);
769
+ }
770
+
771
+ return div;
772
+ }
773
+
774
+ return {
775
+ 'load_jupyter_extension': load_jupyter_extension,
776
+ 'load_ipython_extension': load_jupyter_extension
777
+ };
778
+ });
.local/share/jupyter/nbextensions/keyboard_shortcut_editor/quickhelp_shim.js ADDED
@@ -0,0 +1,207 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ define([
2
+ 'underscore',
3
+ 'jquery',
4
+ 'base/js/utils',
5
+ 'notebook/js/quickhelp',
6
+ 'codemirror/lib/codemirror'
7
+ ], function(
8
+ _,
9
+ $,
10
+ utils,
11
+ quickhelp,
12
+ CodeMirror
13
+ ){
14
+ "use strict";
15
+ // This is essentially a duplicate of the quickhelp module, used to
16
+ // patch the existing quickhelp with definitions which aren't exported.
17
+
18
+ var platform = utils.platform;
19
+
20
+ var cmd_ctrl = 'Ctrl-';
21
+ var platform_specific;
22
+
23
+ if (platform === 'MacOS') {
24
+ // Mac OS X specific
25
+ cmd_ctrl = 'Cmd-';
26
+ platform_specific = [
27
+ { shortcut: "Cmd-Up", help:"go to cell start" },
28
+ { shortcut: "Cmd-Down", help:"go to cell end" },
29
+ { shortcut: "Alt-Left", help:"go one word left" },
30
+ { shortcut: "Alt-Right", help:"go one word right" },
31
+ { shortcut: "Alt-Backspace", help:"delete word before" },
32
+ { shortcut: "Alt-Delete", help:"delete word after" },
33
+ ];
34
+ } else {
35
+ // PC specific
36
+ platform_specific = [
37
+ { shortcut: "Ctrl-Home", help:"go to cell start" },
38
+ { shortcut: "Ctrl-Up", help:"go to cell start" },
39
+ { shortcut: "Ctrl-End", help:"go to cell end" },
40
+ { shortcut: "Ctrl-Down", help:"go to cell end" },
41
+ { shortcut: "Ctrl-Left", help:"go one word left" },
42
+ { shortcut: "Ctrl-Right", help:"go one word right" },
43
+ { shortcut: "Ctrl-Backspace", help:"delete word before" },
44
+ { shortcut: "Ctrl-Delete", help:"delete word after" },
45
+ ];
46
+ }
47
+
48
+ var cm_shortcuts = [
49
+ { shortcut:"Tab", help:"code completion or indent" },
50
+ { shortcut:"Shift-Tab", help:"tooltip" },
51
+ { shortcut: cmd_ctrl + "]", help:"indent" },
52
+ { shortcut: cmd_ctrl + "[", help:"dedent" },
53
+ { shortcut: cmd_ctrl + "a", help:"select all" },
54
+ { shortcut: cmd_ctrl + "z", help:"undo" },
55
+ { shortcut: cmd_ctrl + "Shift-z", help:"redo" },
56
+ { shortcut: cmd_ctrl + "y", help:"redo" },
57
+ ].concat( platform_specific );
58
+
59
+ var mac_humanize_map = {
60
+ // all these are unicode, will probably display badly on anything except macs.
61
+ // these are the standard symbol that are used in MacOS native menus
62
+ // cf http://apple.stackexchange.com/questions/55727/
63
+ // for htmlentities and/or unicode value
64
+ 'cmd':'⌘',
65
+ 'shift':'⇧',
66
+ 'alt':'βŒ₯',
67
+ 'up':'↑',
68
+ 'down':'↓',
69
+ 'left':'←',
70
+ 'right':'β†’',
71
+ 'eject':'⏏',
72
+ 'tab':'β‡₯',
73
+ 'backtab':'⇀',
74
+ 'capslock':'β‡ͺ',
75
+ 'esc':'esc',
76
+ 'ctrl':'βŒƒ',
77
+ 'enter':'↩',
78
+ 'pageup':'β‡ž',
79
+ 'pagedown':'β‡Ÿ',
80
+ 'home':'β†–',
81
+ 'end':'β†˜',
82
+ 'altenter':'⌀',
83
+ 'space':'␣',
84
+ 'delete':'⌦',
85
+ 'backspace':'⌫',
86
+ 'apple':'ο£Ώ',
87
+ };
88
+
89
+ var default_humanize_map = {
90
+ 'shift':'Shift',
91
+ 'alt':'Alt',
92
+ 'up':'Up',
93
+ 'down':'Down',
94
+ 'left':'Left',
95
+ 'right':'Right',
96
+ 'tab':'Tab',
97
+ 'capslock':'Caps Lock',
98
+ 'esc':'Esc',
99
+ 'ctrl':'Ctrl',
100
+ 'enter':'Enter',
101
+ 'pageup':'Page Up',
102
+ 'pagedown':'Page Down',
103
+ 'home':'Home',
104
+ 'end':'End',
105
+ 'space':'Space',
106
+ 'backspace':'Backspace',
107
+ };
108
+
109
+ var humanize_map;
110
+
111
+ if (platform === 'MacOS'){
112
+ humanize_map = mac_humanize_map;
113
+ } else {
114
+ humanize_map = default_humanize_map;
115
+ }
116
+
117
+ var special_case = { pageup: "PageUp", pagedown: "Page Down", 'minus': '-' };
118
+
119
+ function humanize_key(key){
120
+ if (key.length === 1){
121
+ return key.toUpperCase();
122
+ }
123
+
124
+ key = humanize_map[key.toLowerCase()]||key;
125
+
126
+ if (key.indexOf(',') === -1){
127
+ return ( special_case[key] ? special_case[key] : key.charAt(0).toUpperCase() + key.slice(1) );
128
+ }
129
+ }
130
+
131
+ // return an **html** string of the keyboard shortcut
132
+ // for human eyes consumption.
133
+ // the sequence is a string, comma sepparated linkt of shortcut,
134
+ // where the shortcut is a list of dash-joined keys.
135
+ // Each shortcut will be wrapped in <kbd> tag, and joined by comma is in a
136
+ // sequence.
137
+ //
138
+ // Depending on the platform each shortcut will be normalized, with or without dashes.
139
+ // and replace with the corresponding unicode symbol for modifier if necessary.
140
+ function humanize_sequence(sequence){
141
+ var joinchar = ',';
142
+ var hum = _.map(sequence.replace(/meta/g, 'cmd').split(','), humanize_shortcut).join(joinchar);
143
+ return hum;
144
+ }
145
+
146
+ function humanize_shortcut(shortcut){
147
+ var joinchar = '-';
148
+ if (platform === 'MacOS'){
149
+ joinchar = '';
150
+ }
151
+ var sh = _.map(shortcut.split('-'), humanize_key ).join(joinchar);
152
+ return '<kbd>'+sh+'</kbd>';
153
+ }
154
+
155
+ var build_one = function (s) {
156
+ var help = s.help;
157
+ var shortcut = '';
158
+ if(s.shortcut){
159
+ shortcut = humanize_sequence(s.shortcut);
160
+ }
161
+ return $('<div>').addClass('quickhelp').
162
+ append($('<span/>').addClass('shortcut_key').append($(shortcut))).
163
+ append($('<span/>').addClass('shortcut_descr').text(' : ' + help));
164
+
165
+ };
166
+
167
+ var build_div = function (title, shortcuts) {
168
+
169
+ // Remove jupyter-notebook:ignore shortcuts.
170
+ shortcuts = shortcuts.filter(function(shortcut) {
171
+ if (shortcut.help === 'ignore') {
172
+ return false;
173
+ } else {
174
+ return true;
175
+ }
176
+ });
177
+
178
+ var i, half, n;
179
+ var div = $('<div/>').append($(title));
180
+ var sub_div = $('<div/>').addClass('container-fluid');
181
+ var col1 = $('<div/>').addClass('col-md-6');
182
+ var col2 = $('<div/>').addClass('col-md-6');
183
+ n = shortcuts.length;
184
+ half = ~~(n/2); // Truncate :)
185
+ for (i=0; i<half; i++) { col1.append( build_one(shortcuts[i]) ); }
186
+ for (i=half; i<n; i++) { col2.append( build_one(shortcuts[i]) ); }
187
+ sub_div.append(col1).append(col2);
188
+ div.append(sub_div);
189
+ return div;
190
+ };
191
+
192
+ var quickhelp_shiv = {
193
+ cmd_ctrl : cmd_ctrl,
194
+ platform_specific : platform_specific,
195
+ cm_shortcuts : cm_shortcuts,
196
+ mac_humanize_map : mac_humanize_map,
197
+ default_humanize_map : default_humanize_map,
198
+ humanize_map : humanize_map,
199
+ special_case : special_case,
200
+ humanize_key : humanize_key,
201
+ humanize_sequence : humanize_sequence,
202
+ humanize_shortcut : humanize_shortcut,
203
+ build_one : build_one,
204
+ build_div : build_div
205
+ };
206
+ _.defaults(quickhelp, quickhelp_shiv);
207
+ });
.local/share/jupyter/nbextensions/keyboard_shortcut_editor/readme_add_new_link.png ADDED
.local/share/jupyter/nbextensions/keyboard_shortcut_editor/readme_conflict.png ADDED