##// END OF EJS Templates
Add about dialog in Notebook Help Menu....
Matthias Bussonnier -
Show More
@@ -0,0 +1,38 b''
1 // Copyright (c) IPython Development Team.
2 // Distributed under the terms of the Modified BSD License.
3 require([
4 'jquery',
5 'base/js/dialog',
6 'underscore',
7 'base/js/namespace'
8 ], function ($, dialog, _, IPython) {
9 'use strict';
10 $('#notebook_about').click(function () {
11 // use underscore template to auto html escape
12 var text = 'You are using IPython notebook.<br/><br/>';
13 text = text + 'The version of the notebook server is ';
14 text = text + _.template('<b><%- version %></b>')({ version: sys_info.ipython_version });
15 if (sys_info.commit_hash) {
16 text = text + _.template('-<%- hash %>')({ hash: sys_info.commit_hash });
17 }
18 text = text + _.template(' and is running on:<br/><pre>Python <%- pyver %></pre>')({ pyver: sys_info.sys_version });
19 var kinfo = $('<div/>').attr('id', '#about-kinfo').text('Waiting for kernel to be available...');
20 var body = $('<div/>');
21 body.append($('<h4/>').text('Server Information:'));
22 body.append($('<p/>').html(text));
23 body.append($('<h4/>').text('Current Kernel Information:'));
24 body.append(kinfo);
25 dialog.modal({
26 title: 'About IPython Notebook',
27 body: body,
28 buttons: { 'OK': {} }
29 });
30 try {
31 IPython.notebook.session.kernel.kernel_info(function (data) {
32 kinfo.html($('<pre/>').text(data.content.banner));
33 });
34 } catch (e) {
35 kinfo.html($('<p/>').text('unable to contact kernel'));
36 }
37 });
38 });
@@ -1,470 +1,472 b''
1 1 """Base Tornado handlers for the notebook server."""
2 2
3 3 # Copyright (c) IPython Development Team.
4 4 # Distributed under the terms of the Modified BSD License.
5 5
6 6 import functools
7 7 import json
8 8 import logging
9 9 import os
10 10 import re
11 11 import sys
12 12 import traceback
13 13 try:
14 14 # py3
15 15 from http.client import responses
16 16 except ImportError:
17 17 from httplib import responses
18 18
19 19 from jinja2 import TemplateNotFound
20 20 from tornado import web
21 21
22 22 try:
23 23 from tornado.log import app_log
24 24 except ImportError:
25 25 app_log = logging.getLogger()
26 26
27 27 import IPython
28 from IPython.utils.sysinfo import get_sys_info
28 29
29 30 from IPython.config import Application
30 31 from IPython.utils.path import filefind
31 32 from IPython.utils.py3compat import string_types
32 33 from IPython.html.utils import is_hidden, url_path_join, url_escape
33 34
34 35 #-----------------------------------------------------------------------------
35 36 # Top-level handlers
36 37 #-----------------------------------------------------------------------------
37 38 non_alphanum = re.compile(r'[^A-Za-z0-9]')
38 39
39 40 class AuthenticatedHandler(web.RequestHandler):
40 41 """A RequestHandler with an authenticated user."""
41 42
42 43 def set_default_headers(self):
43 44 headers = self.settings.get('headers', {})
44 45
45 46 if "X-Frame-Options" not in headers:
46 47 headers["X-Frame-Options"] = "SAMEORIGIN"
47 48
48 49 for header_name,value in headers.items() :
49 50 try:
50 51 self.set_header(header_name, value)
51 52 except Exception:
52 53 # tornado raise Exception (not a subclass)
53 54 # if method is unsupported (websocket and Access-Control-Allow-Origin
54 55 # for example, so just ignore)
55 56 pass
56 57
57 58 def clear_login_cookie(self):
58 59 self.clear_cookie(self.cookie_name)
59 60
60 61 def get_current_user(self):
61 62 user_id = self.get_secure_cookie(self.cookie_name)
62 63 # For now the user_id should not return empty, but it could eventually
63 64 if user_id == '':
64 65 user_id = 'anonymous'
65 66 if user_id is None:
66 67 # prevent extra Invalid cookie sig warnings:
67 68 self.clear_login_cookie()
68 69 if not self.login_available:
69 70 user_id = 'anonymous'
70 71 return user_id
71 72
72 73 @property
73 74 def cookie_name(self):
74 75 default_cookie_name = non_alphanum.sub('-', 'username-{}'.format(
75 76 self.request.host
76 77 ))
77 78 return self.settings.get('cookie_name', default_cookie_name)
78 79
79 80 @property
80 81 def password(self):
81 82 """our password"""
82 83 return self.settings.get('password', '')
83 84
84 85 @property
85 86 def logged_in(self):
86 87 """Is a user currently logged in?
87 88
88 89 """
89 90 user = self.get_current_user()
90 91 return (user and not user == 'anonymous')
91 92
92 93 @property
93 94 def login_available(self):
94 95 """May a user proceed to log in?
95 96
96 97 This returns True if login capability is available, irrespective of
97 98 whether the user is already logged in or not.
98 99
99 100 """
100 101 return bool(self.settings.get('password', ''))
101 102
102 103
103 104 class IPythonHandler(AuthenticatedHandler):
104 105 """IPython-specific extensions to authenticated handling
105 106
106 107 Mostly property shortcuts to IPython-specific settings.
107 108 """
108 109
109 110 @property
110 111 def config(self):
111 112 return self.settings.get('config', None)
112 113
113 114 @property
114 115 def log(self):
115 116 """use the IPython log by default, falling back on tornado's logger"""
116 117 if Application.initialized():
117 118 return Application.instance().log
118 119 else:
119 120 return app_log
120 121
121 122 #---------------------------------------------------------------
122 123 # URLs
123 124 #---------------------------------------------------------------
124 125
125 126 @property
126 127 def mathjax_url(self):
127 128 return self.settings.get('mathjax_url', '')
128 129
129 130 @property
130 131 def base_url(self):
131 132 return self.settings.get('base_url', '/')
132 133
133 134 @property
134 135 def ws_url(self):
135 136 return self.settings.get('websocket_url', '')
136 137
137 138 #---------------------------------------------------------------
138 139 # Manager objects
139 140 #---------------------------------------------------------------
140 141
141 142 @property
142 143 def kernel_manager(self):
143 144 return self.settings['kernel_manager']
144 145
145 146 @property
146 147 def contents_manager(self):
147 148 return self.settings['contents_manager']
148 149
149 150 @property
150 151 def cluster_manager(self):
151 152 return self.settings['cluster_manager']
152 153
153 154 @property
154 155 def session_manager(self):
155 156 return self.settings['session_manager']
156 157
157 158 @property
158 159 def kernel_spec_manager(self):
159 160 return self.settings['kernel_spec_manager']
160 161
161 162 #---------------------------------------------------------------
162 163 # CORS
163 164 #---------------------------------------------------------------
164 165
165 166 @property
166 167 def allow_origin(self):
167 168 """Normal Access-Control-Allow-Origin"""
168 169 return self.settings.get('allow_origin', '')
169 170
170 171 @property
171 172 def allow_origin_pat(self):
172 173 """Regular expression version of allow_origin"""
173 174 return self.settings.get('allow_origin_pat', None)
174 175
175 176 @property
176 177 def allow_credentials(self):
177 178 """Whether to set Access-Control-Allow-Credentials"""
178 179 return self.settings.get('allow_credentials', False)
179 180
180 181 def set_default_headers(self):
181 182 """Add CORS headers, if defined"""
182 183 super(IPythonHandler, self).set_default_headers()
183 184 if self.allow_origin:
184 185 self.set_header("Access-Control-Allow-Origin", self.allow_origin)
185 186 elif self.allow_origin_pat:
186 187 origin = self.get_origin()
187 188 if origin and self.allow_origin_pat.match(origin):
188 189 self.set_header("Access-Control-Allow-Origin", origin)
189 190 if self.allow_credentials:
190 191 self.set_header("Access-Control-Allow-Credentials", 'true')
191 192
192 193 def get_origin(self):
193 194 # Handle WebSocket Origin naming convention differences
194 195 # The difference between version 8 and 13 is that in 8 the
195 196 # client sends a "Sec-Websocket-Origin" header and in 13 it's
196 197 # simply "Origin".
197 198 if "Origin" in self.request.headers:
198 199 origin = self.request.headers.get("Origin")
199 200 else:
200 201 origin = self.request.headers.get("Sec-Websocket-Origin", None)
201 202 return origin
202 203
203 204 #---------------------------------------------------------------
204 205 # template rendering
205 206 #---------------------------------------------------------------
206 207
207 208 def get_template(self, name):
208 209 """Return the jinja template object for a given name"""
209 210 return self.settings['jinja2_env'].get_template(name)
210 211
211 212 def render_template(self, name, **ns):
212 213 ns.update(self.template_namespace)
213 214 template = self.get_template(name)
214 215 return template.render(**ns)
215 216
216 217 @property
217 218 def template_namespace(self):
218 219 return dict(
219 220 base_url=self.base_url,
220 221 ws_url=self.ws_url,
221 222 logged_in=self.logged_in,
222 223 login_available=self.login_available,
223 224 static_url=self.static_url,
225 sys_info=json.dumps(get_sys_info())
224 226 )
225 227
226 228 def get_json_body(self):
227 229 """Return the body of the request as JSON data."""
228 230 if not self.request.body:
229 231 return None
230 232 # Do we need to call body.decode('utf-8') here?
231 233 body = self.request.body.strip().decode(u'utf-8')
232 234 try:
233 235 model = json.loads(body)
234 236 except Exception:
235 237 self.log.debug("Bad JSON: %r", body)
236 238 self.log.error("Couldn't parse JSON", exc_info=True)
237 239 raise web.HTTPError(400, u'Invalid JSON in body of request')
238 240 return model
239 241
240 242 def write_error(self, status_code, **kwargs):
241 243 """render custom error pages"""
242 244 exc_info = kwargs.get('exc_info')
243 245 message = ''
244 246 status_message = responses.get(status_code, 'Unknown HTTP Error')
245 247 if exc_info:
246 248 exception = exc_info[1]
247 249 # get the custom message, if defined
248 250 try:
249 251 message = exception.log_message % exception.args
250 252 except Exception:
251 253 pass
252 254
253 255 # construct the custom reason, if defined
254 256 reason = getattr(exception, 'reason', '')
255 257 if reason:
256 258 status_message = reason
257 259
258 260 # build template namespace
259 261 ns = dict(
260 262 status_code=status_code,
261 263 status_message=status_message,
262 264 message=message,
263 265 exception=exception,
264 266 )
265 267
266 268 self.set_header('Content-Type', 'text/html')
267 269 # render the template
268 270 try:
269 271 html = self.render_template('%s.html' % status_code, **ns)
270 272 except TemplateNotFound:
271 273 self.log.debug("No template for %d", status_code)
272 274 html = self.render_template('error.html', **ns)
273 275
274 276 self.write(html)
275 277
276 278
277 279
278 280 class Template404(IPythonHandler):
279 281 """Render our 404 template"""
280 282 def prepare(self):
281 283 raise web.HTTPError(404)
282 284
283 285
284 286 class AuthenticatedFileHandler(IPythonHandler, web.StaticFileHandler):
285 287 """static files should only be accessible when logged in"""
286 288
287 289 @web.authenticated
288 290 def get(self, path):
289 291 if os.path.splitext(path)[1] == '.ipynb':
290 292 name = os.path.basename(path)
291 293 self.set_header('Content-Type', 'application/json')
292 294 self.set_header('Content-Disposition','attachment; filename="%s"' % name)
293 295
294 296 return web.StaticFileHandler.get(self, path)
295 297
296 298 def compute_etag(self):
297 299 return None
298 300
299 301 def validate_absolute_path(self, root, absolute_path):
300 302 """Validate and return the absolute path.
301 303
302 304 Requires tornado 3.1
303 305
304 306 Adding to tornado's own handling, forbids the serving of hidden files.
305 307 """
306 308 abs_path = super(AuthenticatedFileHandler, self).validate_absolute_path(root, absolute_path)
307 309 abs_root = os.path.abspath(root)
308 310 if is_hidden(abs_path, abs_root):
309 311 self.log.info("Refusing to serve hidden file, via 404 Error")
310 312 raise web.HTTPError(404)
311 313 return abs_path
312 314
313 315
314 316 def json_errors(method):
315 317 """Decorate methods with this to return GitHub style JSON errors.
316 318
317 319 This should be used on any JSON API on any handler method that can raise HTTPErrors.
318 320
319 321 This will grab the latest HTTPError exception using sys.exc_info
320 322 and then:
321 323
322 324 1. Set the HTTP status code based on the HTTPError
323 325 2. Create and return a JSON body with a message field describing
324 326 the error in a human readable form.
325 327 """
326 328 @functools.wraps(method)
327 329 def wrapper(self, *args, **kwargs):
328 330 try:
329 331 result = method(self, *args, **kwargs)
330 332 except web.HTTPError as e:
331 333 status = e.status_code
332 334 message = e.log_message
333 335 self.log.warn(message)
334 336 self.set_status(e.status_code)
335 337 self.finish(json.dumps(dict(message=message)))
336 338 except Exception:
337 339 self.log.error("Unhandled error in API request", exc_info=True)
338 340 status = 500
339 341 message = "Unknown server error"
340 342 t, value, tb = sys.exc_info()
341 343 self.set_status(status)
342 344 tb_text = ''.join(traceback.format_exception(t, value, tb))
343 345 reply = dict(message=message, traceback=tb_text)
344 346 self.finish(json.dumps(reply))
345 347 else:
346 348 return result
347 349 return wrapper
348 350
349 351
350 352
351 353 #-----------------------------------------------------------------------------
352 354 # File handler
353 355 #-----------------------------------------------------------------------------
354 356
355 357 # to minimize subclass changes:
356 358 HTTPError = web.HTTPError
357 359
358 360 class FileFindHandler(web.StaticFileHandler):
359 361 """subclass of StaticFileHandler for serving files from a search path"""
360 362
361 363 # cache search results, don't search for files more than once
362 364 _static_paths = {}
363 365
364 366 def initialize(self, path, default_filename=None):
365 367 if isinstance(path, string_types):
366 368 path = [path]
367 369
368 370 self.root = tuple(
369 371 os.path.abspath(os.path.expanduser(p)) + os.sep for p in path
370 372 )
371 373 self.default_filename = default_filename
372 374
373 375 def compute_etag(self):
374 376 return None
375 377
376 378 @classmethod
377 379 def get_absolute_path(cls, roots, path):
378 380 """locate a file to serve on our static file search path"""
379 381 with cls._lock:
380 382 if path in cls._static_paths:
381 383 return cls._static_paths[path]
382 384 try:
383 385 abspath = os.path.abspath(filefind(path, roots))
384 386 except IOError:
385 387 # IOError means not found
386 388 return ''
387 389
388 390 cls._static_paths[path] = abspath
389 391 return abspath
390 392
391 393 def validate_absolute_path(self, root, absolute_path):
392 394 """check if the file should be served (raises 404, 403, etc.)"""
393 395 if absolute_path == '':
394 396 raise web.HTTPError(404)
395 397
396 398 for root in self.root:
397 399 if (absolute_path + os.sep).startswith(root):
398 400 break
399 401
400 402 return super(FileFindHandler, self).validate_absolute_path(root, absolute_path)
401 403
402 404
403 405 class ApiVersionHandler(IPythonHandler):
404 406
405 407 @json_errors
406 408 def get(self):
407 409 # not authenticated, so give as few info as possible
408 410 self.finish(json.dumps({"version":IPython.__version__}))
409 411
410 412 class TrailingSlashHandler(web.RequestHandler):
411 413 """Simple redirect handler that strips trailing slashes
412 414
413 415 This should be the first, highest priority handler.
414 416 """
415 417
416 418 SUPPORTED_METHODS = ['GET']
417 419
418 420 def get(self):
419 421 self.redirect(self.request.uri.rstrip('/'))
420 422
421 423
422 424 class FilesRedirectHandler(IPythonHandler):
423 425 """Handler for redirecting relative URLs to the /files/ handler"""
424 426 def get(self, path=''):
425 427 cm = self.contents_manager
426 428 if cm.path_exists(path):
427 429 # it's a *directory*, redirect to /tree
428 430 url = url_path_join(self.base_url, 'tree', path)
429 431 else:
430 432 orig_path = path
431 433 # otherwise, redirect to /files
432 434 parts = path.split('/')
433 435 path = '/'.join(parts[:-1])
434 436 name = parts[-1]
435 437
436 438 if not cm.file_exists(name=name, path=path) and 'files' in parts:
437 439 # redirect without files/ iff it would 404
438 440 # this preserves pre-2.0-style 'files/' links
439 441 self.log.warn("Deprecated files/ URL: %s", orig_path)
440 442 parts.remove('files')
441 443 path = '/'.join(parts[:-1])
442 444
443 445 if not cm.file_exists(name=name, path=path):
444 446 raise web.HTTPError(404)
445 447
446 448 url = url_path_join(self.base_url, 'files', path, name)
447 449 url = url_escape(url)
448 450 self.log.debug("Redirecting %s to %s", self.request.path, url)
449 451 self.redirect(url)
450 452
451 453
452 454 #-----------------------------------------------------------------------------
453 455 # URL pattern fragments for re-use
454 456 #-----------------------------------------------------------------------------
455 457
456 458 path_regex = r"(?P<path>(?:/.*)*)"
457 459 notebook_name_regex = r"(?P<name>[^/]+\.ipynb)"
458 460 notebook_path_regex = "%s/%s" % (path_regex, notebook_name_regex)
459 461 file_name_regex = r"(?P<name>[^/]+)"
460 462 file_path_regex = "%s/%s" % (path_regex, file_name_regex)
461 463
462 464 #-----------------------------------------------------------------------------
463 465 # URL to handler mappings
464 466 #-----------------------------------------------------------------------------
465 467
466 468
467 469 default_handlers = [
468 470 (r".*/", TrailingSlashHandler),
469 471 (r"api", ApiVersionHandler)
470 472 ]
@@ -1,146 +1,148 b''
1 1 // Copyright (c) IPython Development Team.
2 2 // Distributed under the terms of the Modified BSD License.
3 3
4 4 require([
5 5 'base/js/namespace',
6 6 'jquery',
7 7 'notebook/js/notebook',
8 8 'base/js/utils',
9 9 'base/js/page',
10 10 'notebook/js/layoutmanager',
11 11 'base/js/events',
12 12 'auth/js/loginwidget',
13 13 'notebook/js/maintoolbar',
14 14 'notebook/js/pager',
15 15 'notebook/js/quickhelp',
16 16 'notebook/js/menubar',
17 17 'notebook/js/notificationarea',
18 18 'notebook/js/savewidget',
19 19 'notebook/js/keyboardmanager',
20 20 'notebook/js/config',
21 21 'notebook/js/kernelselector',
22 22 'codemirror/lib/codemirror',
23 'notebook/js/about',
23 24 // only loaded, not used, please keep sure this is loaded last
24 25 'custom/custom'
25 26 ], function(
26 27 IPython,
27 28 $,
28 29 notebook,
29 30 utils,
30 31 page,
31 32 layoutmanager,
32 33 events,
33 34 loginwidget,
34 35 maintoolbar,
35 36 pager,
36 37 quickhelp,
37 38 menubar,
38 39 notificationarea,
39 40 savewidget,
40 41 keyboardmanager,
41 42 config,
42 43 kernelselector,
43 44 CodeMirror,
45 about,
44 46 // please keep sure that even if not used, this is loaded last
45 47 custom
46 48 ) {
47 49 "use strict";
48 50
49 51 // compat with old IPython, remove for IPython > 3.0
50 52 window.CodeMirror = CodeMirror;
51 53
52 54 var common_options = {
53 55 ws_url : utils.get_body_data("wsUrl"),
54 56 base_url : utils.get_body_data("baseUrl"),
55 57 notebook_path : utils.get_body_data("notebookPath"),
56 58 notebook_name : utils.get_body_data('notebookName')
57 59 };
58 60
59 61 var user_config = $.extend({}, config.default_config);
60 62 var page = new page.Page();
61 63 var layout_manager = new layoutmanager.LayoutManager();
62 64 var pager = new pager.Pager('div#pager', 'div#pager_splitter', {
63 65 layout_manager: layout_manager,
64 66 events: events});
65 67 var keyboard_manager = new keyboardmanager.KeyboardManager({
66 68 pager: pager,
67 69 events: events});
68 70 var save_widget = new savewidget.SaveWidget('span#save_widget', {
69 71 events: events,
70 72 keyboard_manager: keyboard_manager});
71 73 var notebook = new notebook.Notebook('div#notebook', $.extend({
72 74 events: events,
73 75 keyboard_manager: keyboard_manager,
74 76 save_widget: save_widget,
75 77 config: user_config},
76 78 common_options));
77 79 var login_widget = new loginwidget.LoginWidget('span#login_widget', common_options);
78 80 var toolbar = new maintoolbar.MainToolBar('#maintoolbar-container', {
79 81 notebook: notebook,
80 82 events: events});
81 83 var quick_help = new quickhelp.QuickHelp({
82 84 keyboard_manager: keyboard_manager,
83 85 events: events,
84 86 notebook: notebook});
85 87 var menubar = new menubar.MenuBar('#menubar', $.extend({
86 88 notebook: notebook,
87 89 layout_manager: layout_manager,
88 90 events: events,
89 91 save_widget: save_widget,
90 92 quick_help: quick_help},
91 93 common_options));
92 94 var notification_area = new notificationarea.NotificationArea(
93 95 '#notification_area', {
94 96 events: events,
95 97 save_widget: save_widget,
96 98 notebook: notebook,
97 99 keyboard_manager: keyboard_manager});
98 100 notification_area.init_notification_widgets();
99 101 var kernel_selector = new kernelselector.KernelSelector(
100 102 '#kernel_selector_widget', notebook);
101 103
102 104 $('body').append('<div id="fonttest"><pre><span id="test1">x</span>'+
103 105 '<span id="test2" style="font-weight: bold;">x</span>'+
104 106 '<span id="test3" style="font-style: italic;">x</span></pre></div>');
105 107 var nh = $('#test1').innerHeight();
106 108 var bh = $('#test2').innerHeight();
107 109 var ih = $('#test3').innerHeight();
108 110 if(nh != bh || nh != ih) {
109 111 $('head').append('<style>.CodeMirror span { vertical-align: bottom; }</style>');
110 112 }
111 113 $('#fonttest').remove();
112 114
113 115 page.show();
114 116
115 117 layout_manager.do_resize();
116 118 var first_load = function () {
117 119 layout_manager.do_resize();
118 120 var hash = document.location.hash;
119 121 if (hash) {
120 122 document.location.hash = '';
121 123 document.location.hash = hash;
122 124 }
123 125 notebook.set_autosave_interval(notebook.minimum_autosave_interval);
124 126 // only do this once
125 127 events.off('notebook_loaded.Notebook', first_load);
126 128 };
127 129 events.on('notebook_loaded.Notebook', first_load);
128 130
129 131 IPython.page = page;
130 132 IPython.layout_manager = layout_manager;
131 133 IPython.notebook = notebook;
132 134 IPython.pager = pager;
133 135 IPython.quick_help = quick_help;
134 136 IPython.login_widget = login_widget;
135 137 IPython.menubar = menubar;
136 138 IPython.toolbar = toolbar;
137 139 IPython.notification_area = notification_area;
138 140 IPython.keyboard_manager = keyboard_manager;
139 141 IPython.save_widget = save_widget;
140 142 IPython.config = user_config;
141 143 IPython.tooltip = notebook.tooltip;
142 144
143 145 events.trigger('app_initialized.NotebookApp');
144 146 notebook.load_notebook(common_options.notebook_name, common_options.notebook_path);
145 147
146 148 });
@@ -1,317 +1,321 b''
1 1 {% extends "page.html" %}
2 2
3 3 {% block stylesheet %}
4 4
5 5 {% if mathjax_url %}
6 6 <script type="text/javascript" src="{{mathjax_url}}?config=TeX-AMS_HTML-full&delayStartupUntil=configured" charset="utf-8"></script>
7 7 {% endif %}
8 8 <script type="text/javascript">
9 9 // MathJax disabled, set as null to distingish from *missing* MathJax,
10 10 // where it will be undefined, and should prompt a dialog later.
11 11 window.mathjax_url = "{{mathjax_url}}";
12 12 </script>
13 13
14 14 <link rel="stylesheet" href="{{ static_url("components/bootstrap-tour/build/css/bootstrap-tour.min.css") }}" type="text/css" />
15 15 <link rel="stylesheet" href="{{ static_url("components/codemirror/lib/codemirror.css") }}">
16 16
17 17 {{super()}}
18 18
19 19 <link rel="stylesheet" href="{{ static_url("notebook/css/override.css") }}" type="text/css" />
20 20
21 21 {% endblock %}
22 22
23 23 {% block params %}
24 24
25 25 data-project="{{project}}"
26 26 data-base-url="{{base_url}}"
27 27 data-ws-url="{{ws_url}}"
28 28 data-notebook-name="{{notebook_name}}"
29 29 data-notebook-path="{{notebook_path}}"
30 30 class="notebook_app"
31 31
32 32 {% endblock %}
33 33
34 34
35 35 {% block header %}
36 36
37 37
38 38 <span id="save_widget" class="nav pull-left">
39 39 <span id="notebook_name"></span>
40 40 <span id="checkpoint_status"></span>
41 41 <span id="autosave_status"></span>
42 42 </span>
43 43
44 44 <span id="kernel_selector_widget" class="pull-right dropdown">
45 45 <button class="dropdown-toggle" data-toggle="dropdown" type='button' id="current_kernel_spec">
46 46 <span class='kernel_name'>Python</span>
47 47 <span class="caret"></span>
48 48 </button>
49 49 <ul id="kernel_selector" class="dropdown-menu">
50 50 </ul>
51 51 </span>
52 52
53 53 {% endblock %}
54 54
55 55
56 56 {% block site %}
57 57
58 58 <div id="menubar-container" class="container">
59 59 <div id="menubar">
60 60 <div id="menus" class="navbar navbar-default" role="navigation">
61 61 <div class="container-fluid">
62 62 <ul class="nav navbar-nav">
63 63 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">File</a>
64 64 <ul id="file_menu" class="dropdown-menu">
65 65 <li id="new_notebook"
66 66 title="Make a new notebook (Opens a new window)">
67 67 <a href="#">New</a></li>
68 68 <li id="open_notebook"
69 69 title="Opens a new window with the Dashboard view">
70 70 <a href="#">Open...</a></li>
71 71 <!-- <hr/> -->
72 72 <li class="divider"></li>
73 73 <li id="copy_notebook"
74 74 title="Open a copy of this notebook's contents and start a new kernel">
75 75 <a href="#">Make a Copy...</a></li>
76 76 <li id="rename_notebook"><a href="#">Rename...</a></li>
77 77 <li id="save_checkpoint"><a href="#">Save and Checkpoint</a></li>
78 78 <!-- <hr/> -->
79 79 <li class="divider"></li>
80 80 <li id="restore_checkpoint" class="dropdown-submenu"><a href="#">Revert to Checkpoint</a>
81 81 <ul class="dropdown-menu">
82 82 <li><a href="#"></a></li>
83 83 <li><a href="#"></a></li>
84 84 <li><a href="#"></a></li>
85 85 <li><a href="#"></a></li>
86 86 <li><a href="#"></a></li>
87 87 </ul>
88 88 </li>
89 89 <li class="divider"></li>
90 90 <li id="print_preview"><a href="#">Print Preview</a></li>
91 91 <li class="dropdown-submenu"><a href="#">Download as</a>
92 92 <ul class="dropdown-menu">
93 93 <li id="download_ipynb"><a href="#">IPython Notebook (.ipynb)</a></li>
94 94 <li id="download_py"><a href="#">Python (.py)</a></li>
95 95 <li id="download_html"><a href="#">HTML (.html)</a></li>
96 96 <li id="download_rst"><a href="#">reST (.rst)</a></li>
97 97 <li id="download_pdf"><a href="#">PDF (.pdf)</a></li>
98 98 </ul>
99 99 </li>
100 100 <li class="divider"></li>
101 101 <li id="trust_notebook"
102 102 title="Trust the output of this notebook">
103 103 <a href="#" >Trust Notebook</a></li>
104 104 <li class="divider"></li>
105 105 <li id="kill_and_exit"
106 106 title="Shutdown this notebook's kernel, and close this window">
107 107 <a href="#" >Close and halt</a></li>
108 108 </ul>
109 109 </li>
110 110 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">Edit</a>
111 111 <ul id="edit_menu" class="dropdown-menu">
112 112 <li id="cut_cell"><a href="#">Cut Cell</a></li>
113 113 <li id="copy_cell"><a href="#">Copy Cell</a></li>
114 114 <li id="paste_cell_above" class="disabled"><a href="#">Paste Cell Above</a></li>
115 115 <li id="paste_cell_below" class="disabled"><a href="#">Paste Cell Below</a></li>
116 116 <li id="paste_cell_replace" class="disabled"><a href="#">Paste Cell &amp; Replace</a></li>
117 117 <li id="delete_cell"><a href="#">Delete Cell</a></li>
118 118 <li id="undelete_cell" class="disabled"><a href="#">Undo Delete Cell</a></li>
119 119 <li class="divider"></li>
120 120 <li id="split_cell"><a href="#">Split Cell</a></li>
121 121 <li id="merge_cell_above"><a href="#">Merge Cell Above</a></li>
122 122 <li id="merge_cell_below"><a href="#">Merge Cell Below</a></li>
123 123 <li class="divider"></li>
124 124 <li id="move_cell_up"><a href="#">Move Cell Up</a></li>
125 125 <li id="move_cell_down"><a href="#">Move Cell Down</a></li>
126 126 <li class="divider"></li>
127 127 <li id="edit_nb_metadata"><a href="#">Edit Notebook Metadata</a></li>
128 128 </ul>
129 129 </li>
130 130 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">View</a>
131 131 <ul id="view_menu" class="dropdown-menu">
132 132 <li id="toggle_header"
133 133 title="Show/Hide the IPython Notebook logo and notebook title (above menu bar)">
134 134 <a href="#">Toggle Header</a></li>
135 135 <li id="toggle_toolbar"
136 136 title="Show/Hide the action icons (below menu bar)">
137 137 <a href="#">Toggle Toolbar</a></li>
138 138 </ul>
139 139 </li>
140 140 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">Insert</a>
141 141 <ul id="insert_menu" class="dropdown-menu">
142 142 <li id="insert_cell_above"
143 143 title="Insert an empty Code cell above the currently active cell">
144 144 <a href="#">Insert Cell Above</a></li>
145 145 <li id="insert_cell_below"
146 146 title="Insert an empty Code cell below the currently active cell">
147 147 <a href="#">Insert Cell Below</a></li>
148 148 </ul>
149 149 </li>
150 150 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">Cell</a>
151 151 <ul id="cell_menu" class="dropdown-menu">
152 152 <li id="run_cell" title="Run this cell, and move cursor to the next one">
153 153 <a href="#">Run</a></li>
154 154 <li id="run_cell_select_below" title="Run this cell, select below">
155 155 <a href="#">Run and Select Below</a></li>
156 156 <li id="run_cell_insert_below" title="Run this cell, insert below">
157 157 <a href="#">Run and Insert Below</a></li>
158 158 <li id="run_all_cells" title="Run all cells in the notebook">
159 159 <a href="#">Run All</a></li>
160 160 <li id="run_all_cells_above" title="Run all cells above (but not including) this cell">
161 161 <a href="#">Run All Above</a></li>
162 162 <li id="run_all_cells_below" title="Run this cell and all cells below it">
163 163 <a href="#">Run All Below</a></li>
164 164 <li class="divider"></li>
165 165 <li id="change_cell_type" class="dropdown-submenu"
166 166 title="All cells in the notebook have a cell type. By default, new cells are created as 'Code' cells">
167 167 <a href="#">Cell Type</a>
168 168 <ul class="dropdown-menu">
169 169 <li id="to_code"
170 170 title="Contents will be sent to the kernel for execution, and output will display in the footer of cell">
171 171 <a href="#">Code</a></li>
172 172 <li id="to_markdown"
173 173 title="Contents will be rendered as HTML and serve as explanatory text">
174 174 <a href="#">Markdown</a></li>
175 175 <li id="to_raw"
176 176 title="Contents will pass through nbconvert unmodified">
177 177 <a href="#">Raw NBConvert</a></li>
178 178 <li id="to_heading1"><a href="#">Heading 1</a></li>
179 179 <li id="to_heading2"><a href="#">Heading 2</a></li>
180 180 <li id="to_heading3"><a href="#">Heading 3</a></li>
181 181 <li id="to_heading4"><a href="#">Heading 4</a></li>
182 182 <li id="to_heading5"><a href="#">Heading 5</a></li>
183 183 <li id="to_heading6"><a href="#">Heading 6</a></li>
184 184 </ul>
185 185 </li>
186 186 <li class="divider"></li>
187 187 <li id="current_outputs" class="dropdown-submenu"><a href="#">Current Output</a>
188 188 <ul class="dropdown-menu">
189 189 <li id="toggle_current_output"
190 190 title="Hide/Show the output of the current cell">
191 191 <a href="#">Toggle</a>
192 192 </li>
193 193 <li id="toggle_current_output_scroll"
194 194 title="Scroll the output of the current cell">
195 195 <a href="#">Toggle Scrolling</a>
196 196 </li>
197 197 <li id="clear_current_output"
198 198 title="Clear the output of the current cell">
199 199 <a href="#">Clear</a>
200 200 </li>
201 201 </ul>
202 202 </li>
203 203 <li id="all_outputs" class="dropdown-submenu"><a href="#">All Output</a>
204 204 <ul class="dropdown-menu">
205 205 <li id="toggle_all_output"
206 206 title="Hide/Show the output of all cells">
207 207 <a href="#">Toggle</a>
208 208 </li>
209 209 <li id="toggle_all_output_scroll"
210 210 title="Scroll the output of all cells">
211 211 <a href="#">Toggle Scrolling</a>
212 212 </li>
213 213 <li id="clear_all_output"
214 214 title="Clear the output of all cells">
215 215 <a href="#">Clear</a>
216 216 </li>
217 217 </ul>
218 218 </li>
219 219 </ul>
220 220 </li>
221 221 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">Kernel</a>
222 222 <ul id="kernel_menu" class="dropdown-menu">
223 223 <li id="int_kernel"
224 224 title="Send KeyboardInterrupt (CTRL-C) to the Kernel">
225 225 <a href="#">Interrupt</a></li>
226 226 <li id="restart_kernel"
227 227 title="Restart the Kernel">
228 228 <a href="#">Restart</a></li>
229 229 <li class="divider"></li>
230 230 <li id="menu-change-kernel" class="dropdown-submenu">
231 231 <a href="#">Change kernel</a>
232 232 <ul class="dropdown-menu" id="menu-change-kernel-submenu"></ul>
233 233 </li>
234 234 </ul>
235 235 </li>
236 236 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">Help</a>
237 237 <ul id="help_menu" class="dropdown-menu">
238 238 <li id="notebook_tour" title="A quick tour of the notebook user interface"><a href="#">User Interface Tour</a></li>
239 239 <li id="keyboard_shortcuts" title="Opens a tooltip with all keyboard shortcuts"><a href="#">Keyboard Shortcuts</a></li>
240 240 <li class="divider"></li>
241 241 {% set
242 242 sections = (
243 243 (
244 244 ("http://ipython.org/documentation.html","IPython Help",True),
245 245 ("http://nbviewer.ipython.org/github/ipython/ipython/tree/2.x/examples/Index.ipynb", "Notebook Help", True),
246 246 ),(
247 247 ("http://docs.python.org","Python",True),
248 248 ("http://help.github.com/articles/github-flavored-markdown","Markdown",True),
249 249 ("http://docs.scipy.org/doc/numpy/reference/","NumPy",True),
250 250 ("http://docs.scipy.org/doc/scipy/reference/","SciPy",True),
251 251 ("http://matplotlib.org/contents.html","Matplotlib",True),
252 252 ("http://docs.sympy.org/latest/index.html","SymPy",True),
253 253 ("http://pandas.pydata.org/pandas-docs/stable/","pandas", True)
254 254 )
255 255 )
256 256 %}
257 257
258 258 {% for helplinks in sections %}
259 259 {% for link in helplinks %}
260 260 <li><a href="{{link[0]}}" {{'target="_blank" title="Opens in a new window"' if link[2]}}>
261 261 {{'<i class="fa fa-external-link menu-icon pull-right"></i>' if link[2]}}
262 262 {{link[1]}}
263 263 </a></li>
264 264 {% endfor %}
265 265 {% if not loop.last %}
266 266 <li class="divider"></li>
267 267 {% endif %}
268 268 {% endfor %}
269 </li>
269 <li class="divider"></li>
270 <li title="About IPython Notebook"><a id="notebook_about" href="#">About</a></li>
270 271 </ul>
271 272 </li>
272 273 </ul>
273 274 <ul class="nav navbar-nav navbar-right">
274 275 <li id="kernel_indicator">
275 276 <i id="kernel_indicator_icon"></i>
276 277 </li>
277 278 <li id="modal_indicator">
278 279 <i id="modal_indicator_icon"></i>
279 280 </li>
280 281 <li id="notification_area"></li>
281 282 </ul>
282 283 </div>
283 284 </div>
284 285 </div>
285 286 <div id="maintoolbar" class="navbar">
286 287 <div class="toolbar-inner navbar-inner navbar-nobg">
287 288 <div id="maintoolbar-container" class="container"></div>
288 289 </div>
289 290 </div>
290 291 </div>
291 292
292 293 <div id="ipython-main-app">
293 294
294 295 <div id="notebook_panel">
295 296 <div id="notebook"></div>
296 297 <div id="pager_splitter"></div>
297 298 <div id="pager">
298 299 <div id='pager_button_area'>
299 300 </div>
300 301 <div id="pager-container" class="container"></div>
301 302 </div>
302 303 </div>
303 304
304 305 </div>
305 306 <div id='tooltip' class='ipython_tooltip' style='display:none'></div>
306 307
307 308
308 309 {% endblock %}
309 310
310 311
311 312 {% block script %}
312 313 {{super()}}
314 <script type="text/javascript">
315 sys_info = {{sys_info}};
316 </script>
313 317
314 318
315 319 <script src="{{ static_url("notebook/js/main.js") }}" charset="utf-8"></script>
316 320
317 321 {% endblock %}
General Comments 0
You need to be logged in to leave comments. Login now