Add files using upload-large-folder tool
Browse filesThis view is limited to 50 files because it contains too many changes. Β
See raw diff
- .local/share/jupyter/nbextensions/code_prettify/README_2to3.md +139 -0
- .local/share/jupyter/nbextensions/code_prettify/README_code_prettify.md +300 -0
- .local/share/jupyter/nbextensions/code_prettify/demo-py.gif +0 -0
- .local/share/jupyter/nbextensions/codefolding/codefolding_firstline_folded.png +0 -0
- .local/share/jupyter/nbextensions/codefolding/codefolding_firstline_unfolded.png +0 -0
- .local/share/jupyter/nbextensions/codefolding/codefolding_indent_folded_1.png +0 -0
- .local/share/jupyter/nbextensions/codefolding/edit.js +4 -0
- .local/share/jupyter/nbextensions/codefolding/foldgutter.css +5 -0
- .local/share/jupyter/nbextensions/codemirror_mode_extensions/codemirror_mode_extensions.yaml +7 -0
- .local/share/jupyter/nbextensions/collapsible_headings/icon.png +0 -0
- .local/share/jupyter/nbextensions/collapsible_headings/main.css +130 -0
- .local/share/jupyter/nbextensions/collapsible_headings/main.js +1092 -0
- .local/share/jupyter/nbextensions/datestamper/icon.png +0 -0
- .local/share/jupyter/nbextensions/equation-numbering/info.yaml +7 -0
- .local/share/jupyter/nbextensions/execute_time/ExecuteTime.js +349 -0
- .local/share/jupyter/nbextensions/execute_time/execution-timings-menu.png +0 -0
- .local/share/jupyter/nbextensions/execute_time/icon.png +0 -0
- .local/share/jupyter/nbextensions/execute_time/readme.md +206 -0
- .local/share/jupyter/nbextensions/exercise/history.md +26 -0
- .local/share/jupyter/nbextensions/exercise/main.js +169 -0
- .local/share/jupyter/nbextensions/help_panel/help_panel_ext.png +0 -0
- .local/share/jupyter/nbextensions/help_panel/icon.png +0 -0
- .local/share/jupyter/nbextensions/hide_header/hide_header.yaml +12 -0
- .local/share/jupyter/nbextensions/hide_input_all/hide_input_all.yaml +7 -0
- .local/share/jupyter/nbextensions/hide_input_all/hide_input_all_show.png +0 -0
- .local/share/jupyter/nbextensions/hide_input_all/icon.png +0 -0
- .local/share/jupyter/nbextensions/hide_input_all/main.js +59 -0
- .local/share/jupyter/nbextensions/hide_input_all/readme.md +44 -0
- .local/share/jupyter/nbextensions/highlighter/demo_highlighter.ipynb +96 -0
- .local/share/jupyter/nbextensions/highlighter/export_highlights.pdf +0 -0
- .local/share/jupyter/nbextensions/highlighter/export_highlights.tex +457 -0
- .local/share/jupyter/nbextensions/highlighter/highlighter.css +98 -0
- .local/share/jupyter/nbextensions/highlighter/highlighter.js +378 -0
- .local/share/jupyter/nbextensions/highlighter/highlighter.yaml +7 -0
- .local/share/jupyter/nbextensions/highlighter/icon.png +0 -0
- .local/share/jupyter/nbextensions/highlighter/readme.md +47 -0
- .local/share/jupyter/nbextensions/highlighter/tst_highlights.ipynb +53 -0
- .local/share/jupyter/nbextensions/hinterland/README.md +49 -0
- .local/share/jupyter/nbextensions/hinterland/hinterland.yaml +73 -0
- .local/share/jupyter/nbextensions/init_cell/README.md +44 -0
- .local/share/jupyter/nbextensions/init_cell/cell_toolbar_menu.png +0 -0
- .local/share/jupyter/nbextensions/init_cell/icon.png +0 -0
- .local/share/jupyter/nbextensions/init_cell/init_cell.yaml +17 -0
- .local/share/jupyter/nbextensions/keyboard_shortcut_editor/README.md +130 -0
- .local/share/jupyter/nbextensions/keyboard_shortcut_editor/icon.png +0 -0
- .local/share/jupyter/nbextensions/keyboard_shortcut_editor/kse_components.js +364 -0
- .local/share/jupyter/nbextensions/keyboard_shortcut_editor/main.js +778 -0
- .local/share/jupyter/nbextensions/keyboard_shortcut_editor/quickhelp_shim.js +207 -0
- .local/share/jupyter/nbextensions/keyboard_shortcut_editor/readme_add_new_link.png +0 -0
- .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 |
+

|
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 |
+

|
25 |
+

|
26 |
+

|
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 |
+

|
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 |
+

|
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 |
+

|
6 |
+
|
7 |
+
Typically, all codecells are shown with their corresponding output:
|
8 |
+
|
9 |
+

|
10 |
+
|
11 |
+
Clicking on the "Toggle codecell display" toolbar button hides all codecells:
|
12 |
+
|
13 |
+

|
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 |
+
"\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 |
+

|
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 |
+

|
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 |
+

|
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 |
+

|
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 |
+

|
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 |
+

|
28 |
+

|
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 |
+

|
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 |
+

|
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 |
+

|
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 |
+

|
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 |
+

|
66 |
+
|
67 |
+
The dialog will also not accept a shortcut that would conflict with one which
|
68 |
+
already exists:
|
69 |
+
|
70 |
+

|
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(',', ',');
|
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 ☹. ' +
|
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) : '<new shortcut>');
|
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
![]() |