Skip to content

Commit c70c66d

Browse files
authored
Merge pull request #21 from manics/buttons
Attempt to make buttons clearer
2 parents d873b9f + 36341de commit c70c66d

File tree

5 files changed

+113
-77
lines changed

5 files changed

+113
-77
lines changed

README.md

Lines changed: 25 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,29 @@ This should automatically enable the extension. If it is not listed in `jupyter
2121
jupyter serverextension enable --py jupyter_offlinenotebook --sys-prefix
2222

2323

24+
Usage
25+
-----
26+
27+
![Offline notebook buttons](./offline-notebook-buttons.png)
28+
29+
You should see up to five new buttons depending on your configuration and where you are running the notebook:
30+
- download the in-memory (browser) state of the notebook
31+
- save the in-memory state of the notebook to local-storage
32+
- load a notebook from local-storage
33+
- open the permanent URL of the repository containing this notebook
34+
- copy the permanent mybinder URL to share this repository
35+
36+
Saving and loading uses the repository ID and the path of the current notebook.
37+
38+
You should always see the `Download` button.
39+
If you are running this on mybinder all buttons should be visible.
40+
See the configuration section below to enable the other buttons on other systems.
41+
42+
If you don't see the buttons check the Javascript console log.
43+
44+
See [example.ipynb](./example.ipynb)
45+
46+
2447
Configuration
2548
-------------
2649

@@ -36,23 +59,8 @@ This extension can be configured in `jupyter_notebook_config.py` by setting the
3659
A callable that returns the repository reference URL.
3760
Default is the values of the `BINDER_LAUNCH_HOST` and
3861
`BINDER_PERSISTENT_REQUEST` environment variables.
39-
40-
41-
42-
Usage
43-
-----
44-
45-
![Offline notebook buttons](./offline-notebook-buttons.png)
46-
47-
There are three new icons to:
48-
- download the in-memory (browser) state of the notebook
49-
- save the in-memory state of the notebook to local-storage
50-
- load a notebook from local-storage
51-
52-
Saving and loading uses the repository ID and the path of the current notebook.
53-
If you don't see the buttons check the Javascritp console log, it may mean no repository ID was found.
54-
55-
See [example.ipynb](./example.ipynb)
62+
- `binder_repo_label`:
63+
A callable that returns the label used to link to the repository.
5664

5765

5866
**WARNING**

example.ipynb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,10 @@
1616
"source": [
1717
"1. Make some changes to this notebook (or run it to update the output).\n",
1818
"2. Do not save the notebook. You can even disconnect from the Jupyter server or your network.\n",
19-
"3. Click the first button (`first aid kit`). This should prompt you to download the notebook.\n",
20-
"4. Click the middle button (`download`). This should save the current notebook into your browser's [local-storage](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage).\n",
19+
"3. Click the first button (`Download`). This should prompt you to download the notebook.\n",
20+
"4. Click the second button (`cloud download`). This should save the current notebook into your browser's [local-storage](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage).\n",
2121
"5. Start a new instance of Jupyter, and open the original version of this notebook.\n",
22-
"6. Click the third button (`upload`). This should restore the copy of the notebook from your browser's local-storage."
22+
"6. Click the third button (`cloud upload`). This should restore the copy of the notebook from your browser's local-storage."
2323
]
2424
},
2525
{

jupyter_offlinenotebook/__init__.py

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,19 +34,29 @@ async def get(self):
3434
to work if the user subsequently goes offline
3535
"""
3636
config = self.settings['offline_notebook_config']
37-
repoid = config.repository_id()
38-
binder_ref_url = config.repository_ref_url()
39-
binder_persistent_url = config.binder_persistent_url()
4037
jcfg = json.dumps({
41-
'repoid': repoid,
42-
'binder_ref_url': binder_ref_url,
43-
'binder_persistent_url': binder_persistent_url,
38+
'repoid': config.repository_id(),
39+
'binder_repo_label': config.repository_label(),
40+
'binder_ref_url': config.repository_ref_url(),
41+
'binder_persistent_url': config.binder_persistent_url(),
4442
})
4543
self.log.debug('OfflineNotebook config:%s ', jcfg)
4644
self.set_header('Content-Type', 'application/json')
4745
self.write(jcfg)
4846

4947

48+
def _repo_label_from_binder_request():
49+
try:
50+
repotype = os.getenv('BINDER_PERSISTENT_REQUEST', '').split('/')[1]
51+
except IndexError:
52+
return ''
53+
if repotype == 'gh':
54+
return 'GitHub'
55+
if repotype == 'gl':
56+
return 'GitLab'
57+
return repotype.capitalize()
58+
59+
5060
class OfflineNotebookConfig(Configurable):
5161
"""
5262
Holds server-side configuration
@@ -61,6 +71,15 @@ class OfflineNotebookConfig(Configurable):
6171
"""
6272
).tag(config=True)
6373

74+
repository_label = Callable(
75+
default_value=_repo_label_from_binder_request,
76+
help="""
77+
A callable that returns the repository label.
78+
Default is to parse the `BINDER_PERSISTENT_REQUEST` environment
79+
variable.
80+
"""
81+
).tag(config=True)
82+
6483
repository_ref_url = Callable(
6584
default_value=lambda: os.getenv('BINDER_REF_URL', ''),
6685
help="""

jupyter_offlinenotebook/static/main.js

Lines changed: 60 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -5,23 +5,26 @@ define([
55
'base/js/dialog',
66
'./dexie',
77
'jquery'
8-
],
9-
function(Jupyter, events, utils, dialog, dexie, $) {
8+
],
9+
function (Jupyter, events, utils, dialog, dexie, $) {
1010
var repoid = null;
11+
var repoLabel = null;
1112
var bindeRefUrl = null;
1213
var binderPersistentUrl = null
1314
var db = null;
1415
var dbname = 'jupyter-offlinenotebook';
1516

16-
var initialise = function() {
17-
$.getJSON(utils.get_body_data('baseUrl') + 'offlinenotebook/config', function(data) {
17+
var initialise = function () {
18+
$.getJSON(utils.get_body_data('baseUrl') + 'offlinenotebook/config', function (data) {
1819
repoid = data['repoid'];
1920
if (repoid) {
2021
console.log('offline-notebook repoid: ' + repoid);
2122
}
2223
else {
2324
console.log('offline-notebook repoid not found, disabled');
2425
}
26+
repoLabel = data['binder_repo_label'] || 'Repo'
27+
console.log('offline-notebook repoLabel: ' + repoLabel);
2528
bindeRefUrl = data['binder_ref_url'];
2629
console.log('offline-notebook bindeRefUrl: ' + bindeRefUrl);
2730
binderPersistentUrl = data['binder_persistent_url']
@@ -30,63 +33,69 @@ define([
3033
});
3134
}
3235

33-
var getDb = function() {
36+
var getDb = function () {
3437
if (!db) {
3538
db = new dexie(dbname);
3639
// Only define indexed fields. pk: primary key
37-
db.version(1).stores({'offlinenotebook': 'pk,repoid,name,type'});
40+
db.version(1).stores({ 'offlinenotebook': 'pk,repoid,name,type' });
3841
console.log('offline-notebook: Opened IndexedDB');
3942
}
4043
return db;
4144
}
4245

43-
var addButtons = function() {
44-
var downloadAction = Jupyter.actions.register({
46+
var addButtons = function () {
47+
Jupyter.actions.register({
4548
'help': 'Download visible',
46-
'icon' : 'fa-medkit',
49+
'icon': 'fa-download',
4750
'handler': downloadNotebookFromBrowser
4851
}, 'offline-notebook-download', 'offlinenotebook');
49-
var saveAction = Jupyter.actions.register({
52+
Jupyter.actions.register({
5053
'help': 'Save to browser storage',
51-
'icon' : 'fa-download',
54+
'icon': 'fa-cloud-download',
5255
'handler': localstoreSaveNotebook
5356
}, 'offline-notebook-save', 'offlinenotebook');
54-
var loadAction = Jupyter.actions.register({
55-
'help': 'Load from browser storage',
56-
'icon' : 'fa-upload',
57+
Jupyter.actions.register({
58+
'help': 'Restore from browser storage',
59+
'icon': 'fa-cloud-upload',
5760
'handler': localstoreLoadNotebook
5861
}, 'offline-notebook-load', 'offlinenotebook');
5962

60-
var showRepoAction = Jupyter.actions.register({
63+
var repoIcons = {
64+
'GitHub': 'fa-github',
65+
'GitLab': 'fa-gitlab',
66+
'Git': 'fa-git'
67+
}
68+
Jupyter.actions.register({
6169
'help': 'Visit Binder repository',
62-
'icon' : 'fa-external-link',
70+
'icon': repoIcons[repoLabel] || 'fa-external-link',
6371
'handler': openBinderRepo
6472
}, 'offline-notebook-binderrepo', 'offlinenotebook');
65-
var showBinderAction = Jupyter.actions.register({
73+
Jupyter.actions.register({
6674
'help': 'Link to this Binder',
67-
'icon' : 'fa-external-link',
75+
'icon': 'fa-link',
6876
'handler': showBinderLink
6977
}, 'offline-notebook-binderlink', 'offlinenotebook');
7078

71-
var buttons = [
72-
downloadAction
73-
];
79+
var buttons = [{
80+
'action': 'offlinenotebook:offline-notebook-download',
81+
'label': 'Download'
82+
}];
7483
if (repoid) {
75-
buttons.push(saveAction);
76-
buttons.push(loadAction);
84+
buttons.push('offlinenotebook:offline-notebook-save');
85+
buttons.push('offlinenotebook:offline-notebook-load');
7786
}
7887
Jupyter.toolbar.add_buttons_group(buttons);
7988

8089
var binderButtons = []
8190
if (bindeRefUrl) {
8291
binderButtons.push({
83-
'action': showRepoAction,
84-
'label': 'Repo'
92+
'action': 'offlinenotebook:offline-notebook-binderrepo',
93+
'label': repoLabel
8594
});
8695
}
8796
if (binderPersistentUrl) {
8897
binderButtons.push({
89-
'action': showBinderAction,
98+
'action': 'offlinenotebook:offline-notebook-binderlink',
9099
'label': 'Binder'
91100
})
92101
}
@@ -101,7 +110,7 @@ define([
101110
}
102111
if (!buttons) {
103112
buttons = {
104-
OK: {'class': 'btn-primary'}
113+
OK: { 'class': 'btn-primary' }
105114
};
106115
}
107116
dialog.modal({
@@ -116,18 +125,18 @@ define([
116125
$('<span/>', {
117126
'text': 'repoid: '
118127
}).append(
119-
$('<b/>', {
120-
'text': repoid
121-
})
122-
));
128+
$('<b/>', {
129+
'text': repoid
130+
})
131+
));
123132
var displayPath = $('<div/>').append(
124133
$('<span/>', {
125134
'text': 'path: '
126135
}).append(
127-
$('<b/>', {
128-
'text': path
129-
})
130-
));
136+
$('<b/>', {
137+
'text': path
138+
})
139+
));
131140
return displayRepoid.append(displayPath);
132141
}
133142

@@ -148,12 +157,12 @@ define([
148157
'format': 'json',
149158
'type': 'notebook',
150159
'content': nb
151-
}).then(function(key) {
160+
}).then(function (key) {
152161
console.log('offline-notebook saved: ', key);
153162
modalDialog(
154163
'Notebook saved to browser storage',
155164
repopathDisplay);
156-
}).catch(function(e) {
165+
}).catch(function (e) {
157166
var body = repopathDisplay.append(
158167
$('<div/>', {
159168
'text': e
@@ -162,14 +171,14 @@ define([
162171
'Local storage IndexedDB error',
163172
body,
164173
'alert alert-danger');
165-
throw(e);
174+
throw (e);
166175
});
167176
}
168177

169178
function localstoreLoadNotebook() {
170179
var path = Jupyter.notebook.notebook_path;
171180
var primaryKey = 'repoid:' + repoid + ' path:' + path;
172-
getDb().offlinenotebook.get(primaryKey).then(function(nb) {
181+
getDb().offlinenotebook.get(primaryKey).then(function (nb) {
173182
var repopathDisplay = formatRepoPathforDialog(repoid, path);
174183
if (nb) {
175184
console.log('offline-notebook found ' + primaryKey);
@@ -196,27 +205,27 @@ define([
196205
repopathDisplay,
197206
'alert alert-danger');
198207
}
199-
}).catch(function(e) {
208+
}).catch(function (e) {
200209
var body = $('<div/>').append(
201210
$('<div/>', {
202211
'text': primaryKey
203212
})).append(
204-
$('<div/>', {
205-
'text': e
206-
}));
213+
$('<div/>', {
214+
'text': e
215+
}));
207216
modalDialog(
208217
'Local storage IndexedDB error',
209218
body,
210219
'alert alert-danger');
211-
throw(e);
220+
throw (e);
212221
});
213222
}
214223

215224
// Download https://jsfiddle.net/koldev/cW7W5/
216225
function downloadNotebookFromBrowser() {
217226
var name = Jupyter.notebook.notebook_name;
218227
var nb = getNotebookFromBrowser();
219-
var blob = new Blob([JSON.stringify(nb)], {type: 'application/json'});
228+
var blob = new Blob([JSON.stringify(nb)], { type: 'application/json' });
220229
var url = window.URL.createObjectURL(blob);
221230
var a = document.createElement('a');
222231
document.body.appendChild(a);
@@ -258,11 +267,11 @@ define([
258267
'style': 'flex-grow: 1; margin: 0;'
259268
}));
260269
var button = $('<button/>', {
261-
'title': 'Copy binder link to clipboard',
262-
'data-url': binderUrl
263-
}).click(function() {
264-
copy_link_into_clipboard(this);
265-
})
270+
'title': 'Copy binder link to clipboard',
271+
'data-url': binderUrl
272+
}).click(function () {
273+
copy_link_into_clipboard(this);
274+
})
266275
button.append(
267276
$('<i/>', {
268277
'class': 'fa fa-clipboard'
@@ -281,4 +290,4 @@ define([
281290
return {
282291
load_ipython_extension: load_ipython_extension
283292
};
284-
});
293+
});

offline-notebook-buttons.png

6.73 KB
Loading

0 commit comments

Comments
 (0)