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