##// END OF EJS Templates
Refactoring templates and top level js/css organization.
Brian Granger -
Show More
@@ -0,0 +1,6 b''
1
2 #main_app {
3 height: 100px;
4 width: 350px;
5 margin: 50px auto;
6 }
@@ -0,0 +1,7 b''
1
2 #main_app {
3 height: 100px;
4 width: 200px;
5 margin: 50px auto;
6 }
7
@@ -0,0 +1,20 b''
1 //----------------------------------------------------------------------------
2 // Copyright (C) 2008-2011 The IPython Development Team
3 //
4 // Distributed under the terms of the BSD License. The full license is in
5 // the file COPYING, distributed as part of this software.
6 //----------------------------------------------------------------------------
7
8 //============================================================================
9 // On document ready
10 //============================================================================
11
12
13 $(document).ready(function () {
14
15 IPython.page = new IPython.Page();
16 $('div#main_app').addClass('border-box-sizing ui-widget');
17 IPython.page.show();
18
19 });
20
@@ -0,0 +1,44 b''
1 //----------------------------------------------------------------------------
2 // Copyright (C) 2008-2011 The IPython Development Team
3 //
4 // Distributed under the terms of the BSD License. The full license is in
5 // the file COPYING, distributed as part of this software.
6 //----------------------------------------------------------------------------
7
8 //============================================================================
9 // Global header/site setup.
10 //============================================================================
11
12 var IPython = (function (IPython) {
13
14 var Page = function () {
15 this.style();
16 this.bind_events();
17 };
18
19 Page.prototype.style = function () {
20 $('div#header').addClass('border-box-sizing').
21 addClass('ui-widget ui-widget-content').
22 css('border-top-style','none').
23 css('border-left-style','none').
24 css('border-right-style','none');
25 $('div#site').addClass('border-box-sizing')
26 };
27
28
29 Page.prototype.bind_events = function () {
30 };
31
32
33 Page.prototype.show = function () {
34 // The header and site divs start out hidden to prevent FLOUC.
35 // Main scripts should call this method after styling everything.
36 $('div#header').css('display','block');
37 $('div#site').css('display','block');
38 };
39
40 IPython.Page = Page;
41
42 return IPython;
43
44 }(IPython));
@@ -0,0 +1,19 b''
1 //----------------------------------------------------------------------------
2 // Copyright (C) 2008-2011 The IPython Development Team
3 //
4 // Distributed under the terms of the BSD License. The full license is in
5 // the file COPYING, distributed as part of this software.
6 //----------------------------------------------------------------------------
7
8 //============================================================================
9 // On document ready
10 //============================================================================
11
12
13 $(document).ready(function () {
14
15 IPython.page = new IPython.Page();
16 IPython.page.show();
17
18 });
19
@@ -1,728 +1,730 b''
1 """Tornado handlers for the notebook.
1 """Tornado handlers for the notebook.
2
2
3 Authors:
3 Authors:
4
4
5 * Brian Granger
5 * Brian Granger
6 """
6 """
7
7
8 #-----------------------------------------------------------------------------
8 #-----------------------------------------------------------------------------
9 # Copyright (C) 2008-2011 The IPython Development Team
9 # Copyright (C) 2008-2011 The IPython Development Team
10 #
10 #
11 # Distributed under the terms of the BSD License. The full license is in
11 # Distributed under the terms of the BSD License. The full license is in
12 # the file COPYING, distributed as part of this software.
12 # the file COPYING, distributed as part of this software.
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14
14
15 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
16 # Imports
16 # Imports
17 #-----------------------------------------------------------------------------
17 #-----------------------------------------------------------------------------
18
18
19 import logging
19 import logging
20 import Cookie
20 import Cookie
21 import time
21 import time
22 import uuid
22 import uuid
23
23
24 from tornado import web
24 from tornado import web
25 from tornado import websocket
25 from tornado import websocket
26
26
27 from zmq.eventloop import ioloop
27 from zmq.eventloop import ioloop
28 from zmq.utils import jsonapi
28 from zmq.utils import jsonapi
29
29
30 from IPython.external.decorator import decorator
30 from IPython.external.decorator import decorator
31 from IPython.zmq.session import Session
31 from IPython.zmq.session import Session
32 from IPython.lib.security import passwd_check
32 from IPython.lib.security import passwd_check
33
33
34 try:
34 try:
35 from docutils.core import publish_string
35 from docutils.core import publish_string
36 except ImportError:
36 except ImportError:
37 publish_string = None
37 publish_string = None
38
38
39 #-----------------------------------------------------------------------------
39 #-----------------------------------------------------------------------------
40 # Monkeypatch for Tornado <= 2.1.1 - Remove when no longer necessary!
40 # Monkeypatch for Tornado <= 2.1.1 - Remove when no longer necessary!
41 #-----------------------------------------------------------------------------
41 #-----------------------------------------------------------------------------
42
42
43 # Google Chrome, as of release 16, changed its websocket protocol number. The
43 # Google Chrome, as of release 16, changed its websocket protocol number. The
44 # parts tornado cares about haven't really changed, so it's OK to continue
44 # parts tornado cares about haven't really changed, so it's OK to continue
45 # accepting Chrome connections, but as of Tornado 2.1.1 (the currently released
45 # accepting Chrome connections, but as of Tornado 2.1.1 (the currently released
46 # version as of Oct 30/2011) the version check fails, see the issue report:
46 # version as of Oct 30/2011) the version check fails, see the issue report:
47
47
48 # https://github.com/facebook/tornado/issues/385
48 # https://github.com/facebook/tornado/issues/385
49
49
50 # This issue has been fixed in Tornado post 2.1.1:
50 # This issue has been fixed in Tornado post 2.1.1:
51
51
52 # https://github.com/facebook/tornado/commit/84d7b458f956727c3b0d6710
52 # https://github.com/facebook/tornado/commit/84d7b458f956727c3b0d6710
53
53
54 # Here we manually apply the same patch as above so that users of IPython can
54 # Here we manually apply the same patch as above so that users of IPython can
55 # continue to work with an officially released Tornado. We make the
55 # continue to work with an officially released Tornado. We make the
56 # monkeypatch version check as narrow as possible to limit its effects; once
56 # monkeypatch version check as narrow as possible to limit its effects; once
57 # Tornado 2.1.1 is no longer found in the wild we'll delete this code.
57 # Tornado 2.1.1 is no longer found in the wild we'll delete this code.
58
58
59 import tornado
59 import tornado
60
60
61 if tornado.version_info <= (2,1,1):
61 if tornado.version_info <= (2,1,1):
62
62
63 def _execute(self, transforms, *args, **kwargs):
63 def _execute(self, transforms, *args, **kwargs):
64 from tornado.websocket import WebSocketProtocol8, WebSocketProtocol76
64 from tornado.websocket import WebSocketProtocol8, WebSocketProtocol76
65
65
66 self.open_args = args
66 self.open_args = args
67 self.open_kwargs = kwargs
67 self.open_kwargs = kwargs
68
68
69 # The difference between version 8 and 13 is that in 8 the
69 # The difference between version 8 and 13 is that in 8 the
70 # client sends a "Sec-Websocket-Origin" header and in 13 it's
70 # client sends a "Sec-Websocket-Origin" header and in 13 it's
71 # simply "Origin".
71 # simply "Origin".
72 if self.request.headers.get("Sec-WebSocket-Version") in ("7", "8", "13"):
72 if self.request.headers.get("Sec-WebSocket-Version") in ("7", "8", "13"):
73 self.ws_connection = WebSocketProtocol8(self)
73 self.ws_connection = WebSocketProtocol8(self)
74 self.ws_connection.accept_connection()
74 self.ws_connection.accept_connection()
75
75
76 elif self.request.headers.get("Sec-WebSocket-Version"):
76 elif self.request.headers.get("Sec-WebSocket-Version"):
77 self.stream.write(tornado.escape.utf8(
77 self.stream.write(tornado.escape.utf8(
78 "HTTP/1.1 426 Upgrade Required\r\n"
78 "HTTP/1.1 426 Upgrade Required\r\n"
79 "Sec-WebSocket-Version: 8\r\n\r\n"))
79 "Sec-WebSocket-Version: 8\r\n\r\n"))
80 self.stream.close()
80 self.stream.close()
81
81
82 else:
82 else:
83 self.ws_connection = WebSocketProtocol76(self)
83 self.ws_connection = WebSocketProtocol76(self)
84 self.ws_connection.accept_connection()
84 self.ws_connection.accept_connection()
85
85
86 websocket.WebSocketHandler._execute = _execute
86 websocket.WebSocketHandler._execute = _execute
87 del _execute
87 del _execute
88
88
89 #-----------------------------------------------------------------------------
89 #-----------------------------------------------------------------------------
90 # Decorator for disabling read-only handlers
90 # Decorator for disabling read-only handlers
91 #-----------------------------------------------------------------------------
91 #-----------------------------------------------------------------------------
92
92
93 @decorator
93 @decorator
94 def not_if_readonly(f, self, *args, **kwargs):
94 def not_if_readonly(f, self, *args, **kwargs):
95 if self.application.read_only:
95 if self.application.read_only:
96 raise web.HTTPError(403, "Notebook server is read-only")
96 raise web.HTTPError(403, "Notebook server is read-only")
97 else:
97 else:
98 return f(self, *args, **kwargs)
98 return f(self, *args, **kwargs)
99
99
100 @decorator
100 @decorator
101 def authenticate_unless_readonly(f, self, *args, **kwargs):
101 def authenticate_unless_readonly(f, self, *args, **kwargs):
102 """authenticate this page *unless* readonly view is active.
102 """authenticate this page *unless* readonly view is active.
103
103
104 In read-only mode, the notebook list and print view should
104 In read-only mode, the notebook list and print view should
105 be accessible without authentication.
105 be accessible without authentication.
106 """
106 """
107
107
108 @web.authenticated
108 @web.authenticated
109 def auth_f(self, *args, **kwargs):
109 def auth_f(self, *args, **kwargs):
110 return f(self, *args, **kwargs)
110 return f(self, *args, **kwargs)
111
111
112 if self.application.read_only:
112 if self.application.read_only:
113 return f(self, *args, **kwargs)
113 return f(self, *args, **kwargs)
114 else:
114 else:
115 return auth_f(self, *args, **kwargs)
115 return auth_f(self, *args, **kwargs)
116
116
117 #-----------------------------------------------------------------------------
117 #-----------------------------------------------------------------------------
118 # Top-level handlers
118 # Top-level handlers
119 #-----------------------------------------------------------------------------
119 #-----------------------------------------------------------------------------
120
120
121 class RequestHandler(web.RequestHandler):
121 class RequestHandler(web.RequestHandler):
122 """RequestHandler with default variable setting."""
122 """RequestHandler with default variable setting."""
123
123
124 def render(*args, **kwargs):
124 def render(*args, **kwargs):
125 kwargs.setdefault('message', '')
125 kwargs.setdefault('message', '')
126 return web.RequestHandler.render(*args, **kwargs)
126 return web.RequestHandler.render(*args, **kwargs)
127
127
128 class AuthenticatedHandler(RequestHandler):
128 class AuthenticatedHandler(RequestHandler):
129 """A RequestHandler with an authenticated user."""
129 """A RequestHandler with an authenticated user."""
130
130
131 def get_current_user(self):
131 def get_current_user(self):
132 user_id = self.get_secure_cookie("username")
132 user_id = self.get_secure_cookie("username")
133 # For now the user_id should not return empty, but it could eventually
133 # For now the user_id should not return empty, but it could eventually
134 if user_id == '':
134 if user_id == '':
135 user_id = 'anonymous'
135 user_id = 'anonymous'
136 if user_id is None:
136 if user_id is None:
137 # prevent extra Invalid cookie sig warnings:
137 # prevent extra Invalid cookie sig warnings:
138 self.clear_cookie('username')
138 self.clear_cookie('username')
139 if not self.application.password and not self.application.read_only:
139 if not self.application.password and not self.application.read_only:
140 user_id = 'anonymous'
140 user_id = 'anonymous'
141 return user_id
141 return user_id
142
142
143 @property
143 @property
144 def logged_in(self):
144 def logged_in(self):
145 """Is a user currently logged in?
145 """Is a user currently logged in?
146
146
147 """
147 """
148 user = self.get_current_user()
148 user = self.get_current_user()
149 return (user and not user == 'anonymous')
149 return (user and not user == 'anonymous')
150
150
151 @property
151 @property
152 def login_available(self):
152 def login_available(self):
153 """May a user proceed to log in?
153 """May a user proceed to log in?
154
154
155 This returns True if login capability is available, irrespective of
155 This returns True if login capability is available, irrespective of
156 whether the user is already logged in or not.
156 whether the user is already logged in or not.
157
157
158 """
158 """
159 return bool(self.application.password)
159 return bool(self.application.password)
160
160
161 @property
161 @property
162 def read_only(self):
162 def read_only(self):
163 """Is the notebook read-only?
163 """Is the notebook read-only?
164
164
165 """
165 """
166 return self.application.read_only
166 return self.application.read_only
167
167
168 @property
168 @property
169 def ws_url(self):
169 def ws_url(self):
170 """websocket url matching the current request
170 """websocket url matching the current request
171
171
172 turns http[s]://host[:port] into
172 turns http[s]://host[:port] into
173 ws[s]://host[:port]
173 ws[s]://host[:port]
174 """
174 """
175 proto = self.request.protocol.replace('http', 'ws')
175 proto = self.request.protocol.replace('http', 'ws')
176 host = self.application.ipython_app.websocket_host # default to config value
176 host = self.application.ipython_app.websocket_host # default to config value
177 if host == '':
177 if host == '':
178 host = self.request.host # get from request
178 host = self.request.host # get from request
179 return "%s://%s" % (proto, host)
179 return "%s://%s" % (proto, host)
180
180
181
181
182 class AuthenticatedFileHandler(AuthenticatedHandler, web.StaticFileHandler):
182 class AuthenticatedFileHandler(AuthenticatedHandler, web.StaticFileHandler):
183 """static files should only be accessible when logged in"""
183 """static files should only be accessible when logged in"""
184
184
185 @authenticate_unless_readonly
185 @authenticate_unless_readonly
186 def get(self, path):
186 def get(self, path):
187 return web.StaticFileHandler.get(self, path)
187 return web.StaticFileHandler.get(self, path)
188
188
189
189
190 class ProjectDashboardHandler(AuthenticatedHandler):
190 class ProjectDashboardHandler(AuthenticatedHandler):
191
191
192 @authenticate_unless_readonly
192 @authenticate_unless_readonly
193 def get(self):
193 def get(self):
194 nbm = self.application.notebook_manager
194 nbm = self.application.notebook_manager
195 project = nbm.notebook_dir
195 project = nbm.notebook_dir
196 self.render(
196 self.render(
197 'projectdashboard.html', project=project,
197 'projectdashboard.html', project=project,
198 base_project_url=self.application.ipython_app.base_project_url,
198 base_project_url=self.application.ipython_app.base_project_url,
199 base_kernel_url=self.application.ipython_app.base_kernel_url,
199 base_kernel_url=self.application.ipython_app.base_kernel_url,
200 read_only=self.read_only,
200 read_only=self.read_only,
201 logged_in=self.logged_in,
201 logged_in=self.logged_in,
202 login_available=self.login_available
202 login_available=self.login_available
203 )
203 )
204
204
205
205
206 class LoginHandler(AuthenticatedHandler):
206 class LoginHandler(AuthenticatedHandler):
207
207
208 def _render(self, message=None):
208 def _render(self, message=None):
209 self.render('login.html',
209 self.render('login.html',
210 next=self.get_argument('next', default='/'),
210 next=self.get_argument('next', default='/'),
211 read_only=self.read_only,
211 read_only=self.read_only,
212 logged_in=self.logged_in,
212 logged_in=self.logged_in,
213 login_available=self.login_available,
213 login_available=self.login_available,
214 base_project_url=self.application.ipython_app.base_project_url,
214 message=message
215 message=message
215 )
216 )
216
217
217 def get(self):
218 def get(self):
218 if self.current_user:
219 if self.current_user:
219 self.redirect(self.get_argument('next', default='/'))
220 self.redirect(self.get_argument('next', default='/'))
220 else:
221 else:
221 self._render()
222 self._render()
222
223
223 def post(self):
224 def post(self):
224 pwd = self.get_argument('password', default=u'')
225 pwd = self.get_argument('password', default=u'')
225 if self.application.password:
226 if self.application.password:
226 if passwd_check(self.application.password, pwd):
227 if passwd_check(self.application.password, pwd):
227 self.set_secure_cookie('username', str(uuid.uuid4()))
228 self.set_secure_cookie('username', str(uuid.uuid4()))
228 else:
229 else:
229 self._render(message={'error': 'Invalid password'})
230 self._render(message={'error': 'Invalid password'})
230 return
231 return
231
232
232 self.redirect(self.get_argument('next', default='/'))
233 self.redirect(self.get_argument('next', default='/'))
233
234
234
235
235 class LogoutHandler(AuthenticatedHandler):
236 class LogoutHandler(AuthenticatedHandler):
236
237
237 def get(self):
238 def get(self):
238 self.clear_cookie('username')
239 self.clear_cookie('username')
239 if self.login_available:
240 if self.login_available:
240 message = {'info': 'Successfully logged out.'}
241 message = {'info': 'Successfully logged out.'}
241 else:
242 else:
242 message = {'warning': 'Cannot log out. Notebook authentication '
243 message = {'warning': 'Cannot log out. Notebook authentication '
243 'is disabled.'}
244 'is disabled.'}
244
245
245 self.render('logout.html',
246 self.render('logout.html',
246 read_only=self.read_only,
247 read_only=self.read_only,
247 logged_in=self.logged_in,
248 logged_in=self.logged_in,
248 login_available=self.login_available,
249 login_available=self.login_available,
250 base_project_url=self.application.ipython_app.base_project_url,
249 message=message)
251 message=message)
250
252
251
253
252 class NewHandler(AuthenticatedHandler):
254 class NewHandler(AuthenticatedHandler):
253
255
254 @web.authenticated
256 @web.authenticated
255 def get(self):
257 def get(self):
256 nbm = self.application.notebook_manager
258 nbm = self.application.notebook_manager
257 project = nbm.notebook_dir
259 project = nbm.notebook_dir
258 notebook_id = nbm.new_notebook()
260 notebook_id = nbm.new_notebook()
259 self.render(
261 self.render(
260 'notebook.html', project=project,
262 'notebook.html', project=project,
261 notebook_id=notebook_id,
263 notebook_id=notebook_id,
262 base_project_url=self.application.ipython_app.base_project_url,
264 base_project_url=self.application.ipython_app.base_project_url,
263 base_kernel_url=self.application.ipython_app.base_kernel_url,
265 base_kernel_url=self.application.ipython_app.base_kernel_url,
264 kill_kernel=False,
266 kill_kernel=False,
265 read_only=False,
267 read_only=False,
266 logged_in=self.logged_in,
268 logged_in=self.logged_in,
267 login_available=self.login_available,
269 login_available=self.login_available,
268 mathjax_url=self.application.ipython_app.mathjax_url,
270 mathjax_url=self.application.ipython_app.mathjax_url,
269 )
271 )
270
272
271
273
272 class NamedNotebookHandler(AuthenticatedHandler):
274 class NamedNotebookHandler(AuthenticatedHandler):
273
275
274 @authenticate_unless_readonly
276 @authenticate_unless_readonly
275 def get(self, notebook_id):
277 def get(self, notebook_id):
276 nbm = self.application.notebook_manager
278 nbm = self.application.notebook_manager
277 project = nbm.notebook_dir
279 project = nbm.notebook_dir
278 if not nbm.notebook_exists(notebook_id):
280 if not nbm.notebook_exists(notebook_id):
279 raise web.HTTPError(404, u'Notebook does not exist: %s' % notebook_id)
281 raise web.HTTPError(404, u'Notebook does not exist: %s' % notebook_id)
280
282
281 self.render(
283 self.render(
282 'notebook.html', project=project,
284 'notebook.html', project=project,
283 notebook_id=notebook_id,
285 notebook_id=notebook_id,
284 base_project_url=self.application.ipython_app.base_project_url,
286 base_project_url=self.application.ipython_app.base_project_url,
285 base_kernel_url=self.application.ipython_app.base_kernel_url,
287 base_kernel_url=self.application.ipython_app.base_kernel_url,
286 kill_kernel=False,
288 kill_kernel=False,
287 read_only=self.read_only,
289 read_only=self.read_only,
288 logged_in=self.logged_in,
290 logged_in=self.logged_in,
289 login_available=self.login_available,
291 login_available=self.login_available,
290 mathjax_url=self.application.ipython_app.mathjax_url,
292 mathjax_url=self.application.ipython_app.mathjax_url,
291 )
293 )
292
294
293
295
294 class PrintNotebookHandler(AuthenticatedHandler):
296 class PrintNotebookHandler(AuthenticatedHandler):
295
297
296 @authenticate_unless_readonly
298 @authenticate_unless_readonly
297 def get(self, notebook_id):
299 def get(self, notebook_id):
298 nbm = self.application.notebook_manager
300 nbm = self.application.notebook_manager
299 project = nbm.notebook_dir
301 project = nbm.notebook_dir
300 if not nbm.notebook_exists(notebook_id):
302 if not nbm.notebook_exists(notebook_id):
301 raise web.HTTPError(404, u'Notebook does not exist: %s' % notebook_id)
303 raise web.HTTPError(404, u'Notebook does not exist: %s' % notebook_id)
302
304
303 self.render(
305 self.render(
304 'printnotebook.html', project=project,
306 'printnotebook.html', project=project,
305 notebook_id=notebook_id,
307 notebook_id=notebook_id,
306 base_project_url=self.application.ipython_app.base_project_url,
308 base_project_url=self.application.ipython_app.base_project_url,
307 base_kernel_url=self.application.ipython_app.base_kernel_url,
309 base_kernel_url=self.application.ipython_app.base_kernel_url,
308 kill_kernel=False,
310 kill_kernel=False,
309 read_only=self.read_only,
311 read_only=self.read_only,
310 logged_in=self.logged_in,
312 logged_in=self.logged_in,
311 login_available=self.login_available,
313 login_available=self.login_available,
312 mathjax_url=self.application.ipython_app.mathjax_url,
314 mathjax_url=self.application.ipython_app.mathjax_url,
313 )
315 )
314
316
315 #-----------------------------------------------------------------------------
317 #-----------------------------------------------------------------------------
316 # Kernel handlers
318 # Kernel handlers
317 #-----------------------------------------------------------------------------
319 #-----------------------------------------------------------------------------
318
320
319
321
320 class MainKernelHandler(AuthenticatedHandler):
322 class MainKernelHandler(AuthenticatedHandler):
321
323
322 @web.authenticated
324 @web.authenticated
323 def get(self):
325 def get(self):
324 km = self.application.kernel_manager
326 km = self.application.kernel_manager
325 self.finish(jsonapi.dumps(km.kernel_ids))
327 self.finish(jsonapi.dumps(km.kernel_ids))
326
328
327 @web.authenticated
329 @web.authenticated
328 def post(self):
330 def post(self):
329 km = self.application.kernel_manager
331 km = self.application.kernel_manager
330 notebook_id = self.get_argument('notebook', default=None)
332 notebook_id = self.get_argument('notebook', default=None)
331 kernel_id = km.start_kernel(notebook_id)
333 kernel_id = km.start_kernel(notebook_id)
332 data = {'ws_url':self.ws_url,'kernel_id':kernel_id}
334 data = {'ws_url':self.ws_url,'kernel_id':kernel_id}
333 self.set_header('Location', '/'+kernel_id)
335 self.set_header('Location', '/'+kernel_id)
334 self.finish(jsonapi.dumps(data))
336 self.finish(jsonapi.dumps(data))
335
337
336
338
337 class KernelHandler(AuthenticatedHandler):
339 class KernelHandler(AuthenticatedHandler):
338
340
339 SUPPORTED_METHODS = ('DELETE')
341 SUPPORTED_METHODS = ('DELETE')
340
342
341 @web.authenticated
343 @web.authenticated
342 def delete(self, kernel_id):
344 def delete(self, kernel_id):
343 km = self.application.kernel_manager
345 km = self.application.kernel_manager
344 km.kill_kernel(kernel_id)
346 km.kill_kernel(kernel_id)
345 self.set_status(204)
347 self.set_status(204)
346 self.finish()
348 self.finish()
347
349
348
350
349 class KernelActionHandler(AuthenticatedHandler):
351 class KernelActionHandler(AuthenticatedHandler):
350
352
351 @web.authenticated
353 @web.authenticated
352 def post(self, kernel_id, action):
354 def post(self, kernel_id, action):
353 km = self.application.kernel_manager
355 km = self.application.kernel_manager
354 if action == 'interrupt':
356 if action == 'interrupt':
355 km.interrupt_kernel(kernel_id)
357 km.interrupt_kernel(kernel_id)
356 self.set_status(204)
358 self.set_status(204)
357 if action == 'restart':
359 if action == 'restart':
358 new_kernel_id = km.restart_kernel(kernel_id)
360 new_kernel_id = km.restart_kernel(kernel_id)
359 data = {'ws_url':self.ws_url,'kernel_id':new_kernel_id}
361 data = {'ws_url':self.ws_url,'kernel_id':new_kernel_id}
360 self.set_header('Location', '/'+new_kernel_id)
362 self.set_header('Location', '/'+new_kernel_id)
361 self.write(jsonapi.dumps(data))
363 self.write(jsonapi.dumps(data))
362 self.finish()
364 self.finish()
363
365
364
366
365 class ZMQStreamHandler(websocket.WebSocketHandler):
367 class ZMQStreamHandler(websocket.WebSocketHandler):
366
368
367 def _reserialize_reply(self, msg_list):
369 def _reserialize_reply(self, msg_list):
368 """Reserialize a reply message using JSON.
370 """Reserialize a reply message using JSON.
369
371
370 This takes the msg list from the ZMQ socket, unserializes it using
372 This takes the msg list from the ZMQ socket, unserializes it using
371 self.session and then serializes the result using JSON. This method
373 self.session and then serializes the result using JSON. This method
372 should be used by self._on_zmq_reply to build messages that can
374 should be used by self._on_zmq_reply to build messages that can
373 be sent back to the browser.
375 be sent back to the browser.
374 """
376 """
375 idents, msg_list = self.session.feed_identities(msg_list)
377 idents, msg_list = self.session.feed_identities(msg_list)
376 msg = self.session.unserialize(msg_list)
378 msg = self.session.unserialize(msg_list)
377 try:
379 try:
378 msg['header'].pop('date')
380 msg['header'].pop('date')
379 except KeyError:
381 except KeyError:
380 pass
382 pass
381 try:
383 try:
382 msg['parent_header'].pop('date')
384 msg['parent_header'].pop('date')
383 except KeyError:
385 except KeyError:
384 pass
386 pass
385 msg.pop('buffers')
387 msg.pop('buffers')
386 return jsonapi.dumps(msg)
388 return jsonapi.dumps(msg)
387
389
388 def _on_zmq_reply(self, msg_list):
390 def _on_zmq_reply(self, msg_list):
389 try:
391 try:
390 msg = self._reserialize_reply(msg_list)
392 msg = self._reserialize_reply(msg_list)
391 except:
393 except:
392 self.application.log.critical("Malformed message: %r" % msg_list)
394 self.application.log.critical("Malformed message: %r" % msg_list)
393 else:
395 else:
394 self.write_message(msg)
396 self.write_message(msg)
395
397
396 def allow_draft76(self):
398 def allow_draft76(self):
397 """Allow draft 76, until browsers such as Safari update to RFC 6455.
399 """Allow draft 76, until browsers such as Safari update to RFC 6455.
398
400
399 This has been disabled by default in tornado in release 2.2.0, and
401 This has been disabled by default in tornado in release 2.2.0, and
400 support will be removed in later versions.
402 support will be removed in later versions.
401 """
403 """
402 return True
404 return True
403
405
404
406
405 class AuthenticatedZMQStreamHandler(ZMQStreamHandler):
407 class AuthenticatedZMQStreamHandler(ZMQStreamHandler):
406
408
407 def open(self, kernel_id):
409 def open(self, kernel_id):
408 self.kernel_id = kernel_id.decode('ascii')
410 self.kernel_id = kernel_id.decode('ascii')
409 try:
411 try:
410 cfg = self.application.ipython_app.config
412 cfg = self.application.ipython_app.config
411 except AttributeError:
413 except AttributeError:
412 # protect from the case where this is run from something other than
414 # protect from the case where this is run from something other than
413 # the notebook app:
415 # the notebook app:
414 cfg = None
416 cfg = None
415 self.session = Session(config=cfg)
417 self.session = Session(config=cfg)
416 self.save_on_message = self.on_message
418 self.save_on_message = self.on_message
417 self.on_message = self.on_first_message
419 self.on_message = self.on_first_message
418
420
419 def get_current_user(self):
421 def get_current_user(self):
420 user_id = self.get_secure_cookie("username")
422 user_id = self.get_secure_cookie("username")
421 if user_id == '' or (user_id is None and not self.application.password):
423 if user_id == '' or (user_id is None and not self.application.password):
422 user_id = 'anonymous'
424 user_id = 'anonymous'
423 return user_id
425 return user_id
424
426
425 def _inject_cookie_message(self, msg):
427 def _inject_cookie_message(self, msg):
426 """Inject the first message, which is the document cookie,
428 """Inject the first message, which is the document cookie,
427 for authentication."""
429 for authentication."""
428 if isinstance(msg, unicode):
430 if isinstance(msg, unicode):
429 # Cookie can't constructor doesn't accept unicode strings for some reason
431 # Cookie can't constructor doesn't accept unicode strings for some reason
430 msg = msg.encode('utf8', 'replace')
432 msg = msg.encode('utf8', 'replace')
431 try:
433 try:
432 self.request._cookies = Cookie.SimpleCookie(msg)
434 self.request._cookies = Cookie.SimpleCookie(msg)
433 except:
435 except:
434 logging.warn("couldn't parse cookie string: %s",msg, exc_info=True)
436 logging.warn("couldn't parse cookie string: %s",msg, exc_info=True)
435
437
436 def on_first_message(self, msg):
438 def on_first_message(self, msg):
437 self._inject_cookie_message(msg)
439 self._inject_cookie_message(msg)
438 if self.get_current_user() is None:
440 if self.get_current_user() is None:
439 logging.warn("Couldn't authenticate WebSocket connection")
441 logging.warn("Couldn't authenticate WebSocket connection")
440 raise web.HTTPError(403)
442 raise web.HTTPError(403)
441 self.on_message = self.save_on_message
443 self.on_message = self.save_on_message
442
444
443
445
444 class IOPubHandler(AuthenticatedZMQStreamHandler):
446 class IOPubHandler(AuthenticatedZMQStreamHandler):
445
447
446 def initialize(self, *args, **kwargs):
448 def initialize(self, *args, **kwargs):
447 self._kernel_alive = True
449 self._kernel_alive = True
448 self._beating = False
450 self._beating = False
449 self.iopub_stream = None
451 self.iopub_stream = None
450 self.hb_stream = None
452 self.hb_stream = None
451
453
452 def on_first_message(self, msg):
454 def on_first_message(self, msg):
453 try:
455 try:
454 super(IOPubHandler, self).on_first_message(msg)
456 super(IOPubHandler, self).on_first_message(msg)
455 except web.HTTPError:
457 except web.HTTPError:
456 self.close()
458 self.close()
457 return
459 return
458 km = self.application.kernel_manager
460 km = self.application.kernel_manager
459 self.time_to_dead = km.time_to_dead
461 self.time_to_dead = km.time_to_dead
460 self.first_beat = km.first_beat
462 self.first_beat = km.first_beat
461 kernel_id = self.kernel_id
463 kernel_id = self.kernel_id
462 try:
464 try:
463 self.iopub_stream = km.create_iopub_stream(kernel_id)
465 self.iopub_stream = km.create_iopub_stream(kernel_id)
464 self.hb_stream = km.create_hb_stream(kernel_id)
466 self.hb_stream = km.create_hb_stream(kernel_id)
465 except web.HTTPError:
467 except web.HTTPError:
466 # WebSockets don't response to traditional error codes so we
468 # WebSockets don't response to traditional error codes so we
467 # close the connection.
469 # close the connection.
468 if not self.stream.closed():
470 if not self.stream.closed():
469 self.stream.close()
471 self.stream.close()
470 self.close()
472 self.close()
471 else:
473 else:
472 self.iopub_stream.on_recv(self._on_zmq_reply)
474 self.iopub_stream.on_recv(self._on_zmq_reply)
473 self.start_hb(self.kernel_died)
475 self.start_hb(self.kernel_died)
474
476
475 def on_message(self, msg):
477 def on_message(self, msg):
476 pass
478 pass
477
479
478 def on_close(self):
480 def on_close(self):
479 # This method can be called twice, once by self.kernel_died and once
481 # This method can be called twice, once by self.kernel_died and once
480 # from the WebSocket close event. If the WebSocket connection is
482 # from the WebSocket close event. If the WebSocket connection is
481 # closed before the ZMQ streams are setup, they could be None.
483 # closed before the ZMQ streams are setup, they could be None.
482 self.stop_hb()
484 self.stop_hb()
483 if self.iopub_stream is not None and not self.iopub_stream.closed():
485 if self.iopub_stream is not None and not self.iopub_stream.closed():
484 self.iopub_stream.on_recv(None)
486 self.iopub_stream.on_recv(None)
485 self.iopub_stream.close()
487 self.iopub_stream.close()
486 if self.hb_stream is not None and not self.hb_stream.closed():
488 if self.hb_stream is not None and not self.hb_stream.closed():
487 self.hb_stream.close()
489 self.hb_stream.close()
488
490
489 def start_hb(self, callback):
491 def start_hb(self, callback):
490 """Start the heartbeating and call the callback if the kernel dies."""
492 """Start the heartbeating and call the callback if the kernel dies."""
491 if not self._beating:
493 if not self._beating:
492 self._kernel_alive = True
494 self._kernel_alive = True
493
495
494 def ping_or_dead():
496 def ping_or_dead():
495 self.hb_stream.flush()
497 self.hb_stream.flush()
496 if self._kernel_alive:
498 if self._kernel_alive:
497 self._kernel_alive = False
499 self._kernel_alive = False
498 self.hb_stream.send(b'ping')
500 self.hb_stream.send(b'ping')
499 # flush stream to force immediate socket send
501 # flush stream to force immediate socket send
500 self.hb_stream.flush()
502 self.hb_stream.flush()
501 else:
503 else:
502 try:
504 try:
503 callback()
505 callback()
504 except:
506 except:
505 pass
507 pass
506 finally:
508 finally:
507 self.stop_hb()
509 self.stop_hb()
508
510
509 def beat_received(msg):
511 def beat_received(msg):
510 self._kernel_alive = True
512 self._kernel_alive = True
511
513
512 self.hb_stream.on_recv(beat_received)
514 self.hb_stream.on_recv(beat_received)
513 loop = ioloop.IOLoop.instance()
515 loop = ioloop.IOLoop.instance()
514 self._hb_periodic_callback = ioloop.PeriodicCallback(ping_or_dead, self.time_to_dead*1000, loop)
516 self._hb_periodic_callback = ioloop.PeriodicCallback(ping_or_dead, self.time_to_dead*1000, loop)
515 loop.add_timeout(time.time()+self.first_beat, self._really_start_hb)
517 loop.add_timeout(time.time()+self.first_beat, self._really_start_hb)
516 self._beating= True
518 self._beating= True
517
519
518 def _really_start_hb(self):
520 def _really_start_hb(self):
519 """callback for delayed heartbeat start
521 """callback for delayed heartbeat start
520
522
521 Only start the hb loop if we haven't been closed during the wait.
523 Only start the hb loop if we haven't been closed during the wait.
522 """
524 """
523 if self._beating and not self.hb_stream.closed():
525 if self._beating and not self.hb_stream.closed():
524 self._hb_periodic_callback.start()
526 self._hb_periodic_callback.start()
525
527
526 def stop_hb(self):
528 def stop_hb(self):
527 """Stop the heartbeating and cancel all related callbacks."""
529 """Stop the heartbeating and cancel all related callbacks."""
528 if self._beating:
530 if self._beating:
529 self._beating = False
531 self._beating = False
530 self._hb_periodic_callback.stop()
532 self._hb_periodic_callback.stop()
531 if not self.hb_stream.closed():
533 if not self.hb_stream.closed():
532 self.hb_stream.on_recv(None)
534 self.hb_stream.on_recv(None)
533
535
534 def kernel_died(self):
536 def kernel_died(self):
535 self.application.kernel_manager.delete_mapping_for_kernel(self.kernel_id)
537 self.application.kernel_manager.delete_mapping_for_kernel(self.kernel_id)
536 self.application.log.error("Kernel %s failed to respond to heartbeat", self.kernel_id)
538 self.application.log.error("Kernel %s failed to respond to heartbeat", self.kernel_id)
537 self.write_message(
539 self.write_message(
538 {'header': {'msg_type': 'status'},
540 {'header': {'msg_type': 'status'},
539 'parent_header': {},
541 'parent_header': {},
540 'content': {'execution_state':'dead'}
542 'content': {'execution_state':'dead'}
541 }
543 }
542 )
544 )
543 self.on_close()
545 self.on_close()
544
546
545
547
546 class ShellHandler(AuthenticatedZMQStreamHandler):
548 class ShellHandler(AuthenticatedZMQStreamHandler):
547
549
548 def initialize(self, *args, **kwargs):
550 def initialize(self, *args, **kwargs):
549 self.shell_stream = None
551 self.shell_stream = None
550
552
551 def on_first_message(self, msg):
553 def on_first_message(self, msg):
552 try:
554 try:
553 super(ShellHandler, self).on_first_message(msg)
555 super(ShellHandler, self).on_first_message(msg)
554 except web.HTTPError:
556 except web.HTTPError:
555 self.close()
557 self.close()
556 return
558 return
557 km = self.application.kernel_manager
559 km = self.application.kernel_manager
558 self.max_msg_size = km.max_msg_size
560 self.max_msg_size = km.max_msg_size
559 kernel_id = self.kernel_id
561 kernel_id = self.kernel_id
560 try:
562 try:
561 self.shell_stream = km.create_shell_stream(kernel_id)
563 self.shell_stream = km.create_shell_stream(kernel_id)
562 except web.HTTPError:
564 except web.HTTPError:
563 # WebSockets don't response to traditional error codes so we
565 # WebSockets don't response to traditional error codes so we
564 # close the connection.
566 # close the connection.
565 if not self.stream.closed():
567 if not self.stream.closed():
566 self.stream.close()
568 self.stream.close()
567 self.close()
569 self.close()
568 else:
570 else:
569 self.shell_stream.on_recv(self._on_zmq_reply)
571 self.shell_stream.on_recv(self._on_zmq_reply)
570
572
571 def on_message(self, msg):
573 def on_message(self, msg):
572 if len(msg) < self.max_msg_size:
574 if len(msg) < self.max_msg_size:
573 msg = jsonapi.loads(msg)
575 msg = jsonapi.loads(msg)
574 self.session.send(self.shell_stream, msg)
576 self.session.send(self.shell_stream, msg)
575
577
576 def on_close(self):
578 def on_close(self):
577 # Make sure the stream exists and is not already closed.
579 # Make sure the stream exists and is not already closed.
578 if self.shell_stream is not None and not self.shell_stream.closed():
580 if self.shell_stream is not None and not self.shell_stream.closed():
579 self.shell_stream.close()
581 self.shell_stream.close()
580
582
581
583
582 #-----------------------------------------------------------------------------
584 #-----------------------------------------------------------------------------
583 # Notebook web service handlers
585 # Notebook web service handlers
584 #-----------------------------------------------------------------------------
586 #-----------------------------------------------------------------------------
585
587
586 class NotebookRootHandler(AuthenticatedHandler):
588 class NotebookRootHandler(AuthenticatedHandler):
587
589
588 @authenticate_unless_readonly
590 @authenticate_unless_readonly
589 def get(self):
591 def get(self):
590 nbm = self.application.notebook_manager
592 nbm = self.application.notebook_manager
591 files = nbm.list_notebooks()
593 files = nbm.list_notebooks()
592 self.finish(jsonapi.dumps(files))
594 self.finish(jsonapi.dumps(files))
593
595
594 @web.authenticated
596 @web.authenticated
595 def post(self):
597 def post(self):
596 nbm = self.application.notebook_manager
598 nbm = self.application.notebook_manager
597 body = self.request.body.strip()
599 body = self.request.body.strip()
598 format = self.get_argument('format', default='json')
600 format = self.get_argument('format', default='json')
599 name = self.get_argument('name', default=None)
601 name = self.get_argument('name', default=None)
600 if body:
602 if body:
601 notebook_id = nbm.save_new_notebook(body, name=name, format=format)
603 notebook_id = nbm.save_new_notebook(body, name=name, format=format)
602 else:
604 else:
603 notebook_id = nbm.new_notebook()
605 notebook_id = nbm.new_notebook()
604 self.set_header('Location', '/'+notebook_id)
606 self.set_header('Location', '/'+notebook_id)
605 self.finish(jsonapi.dumps(notebook_id))
607 self.finish(jsonapi.dumps(notebook_id))
606
608
607
609
608 class NotebookHandler(AuthenticatedHandler):
610 class NotebookHandler(AuthenticatedHandler):
609
611
610 SUPPORTED_METHODS = ('GET', 'PUT', 'DELETE')
612 SUPPORTED_METHODS = ('GET', 'PUT', 'DELETE')
611
613
612 @authenticate_unless_readonly
614 @authenticate_unless_readonly
613 def get(self, notebook_id):
615 def get(self, notebook_id):
614 nbm = self.application.notebook_manager
616 nbm = self.application.notebook_manager
615 format = self.get_argument('format', default='json')
617 format = self.get_argument('format', default='json')
616 last_mod, name, data = nbm.get_notebook(notebook_id, format)
618 last_mod, name, data = nbm.get_notebook(notebook_id, format)
617
619
618 if format == u'json':
620 if format == u'json':
619 self.set_header('Content-Type', 'application/json')
621 self.set_header('Content-Type', 'application/json')
620 self.set_header('Content-Disposition','attachment; filename="%s.ipynb"' % name)
622 self.set_header('Content-Disposition','attachment; filename="%s.ipynb"' % name)
621 elif format == u'py':
623 elif format == u'py':
622 self.set_header('Content-Type', 'application/x-python')
624 self.set_header('Content-Type', 'application/x-python')
623 self.set_header('Content-Disposition','attachment; filename="%s.py"' % name)
625 self.set_header('Content-Disposition','attachment; filename="%s.py"' % name)
624 self.set_header('Last-Modified', last_mod)
626 self.set_header('Last-Modified', last_mod)
625 self.finish(data)
627 self.finish(data)
626
628
627 @web.authenticated
629 @web.authenticated
628 def put(self, notebook_id):
630 def put(self, notebook_id):
629 nbm = self.application.notebook_manager
631 nbm = self.application.notebook_manager
630 format = self.get_argument('format', default='json')
632 format = self.get_argument('format', default='json')
631 name = self.get_argument('name', default=None)
633 name = self.get_argument('name', default=None)
632 nbm.save_notebook(notebook_id, self.request.body, name=name, format=format)
634 nbm.save_notebook(notebook_id, self.request.body, name=name, format=format)
633 self.set_status(204)
635 self.set_status(204)
634 self.finish()
636 self.finish()
635
637
636 @web.authenticated
638 @web.authenticated
637 def delete(self, notebook_id):
639 def delete(self, notebook_id):
638 nbm = self.application.notebook_manager
640 nbm = self.application.notebook_manager
639 nbm.delete_notebook(notebook_id)
641 nbm.delete_notebook(notebook_id)
640 self.set_status(204)
642 self.set_status(204)
641 self.finish()
643 self.finish()
642
644
643
645
644 class NotebookCopyHandler(AuthenticatedHandler):
646 class NotebookCopyHandler(AuthenticatedHandler):
645
647
646 @web.authenticated
648 @web.authenticated
647 def get(self, notebook_id):
649 def get(self, notebook_id):
648 nbm = self.application.notebook_manager
650 nbm = self.application.notebook_manager
649 project = nbm.notebook_dir
651 project = nbm.notebook_dir
650 notebook_id = nbm.copy_notebook(notebook_id)
652 notebook_id = nbm.copy_notebook(notebook_id)
651 self.render(
653 self.render(
652 'notebook.html', project=project,
654 'notebook.html', project=project,
653 notebook_id=notebook_id,
655 notebook_id=notebook_id,
654 base_project_url=self.application.ipython_app.base_project_url,
656 base_project_url=self.application.ipython_app.base_project_url,
655 base_kernel_url=self.application.ipython_app.base_kernel_url,
657 base_kernel_url=self.application.ipython_app.base_kernel_url,
656 kill_kernel=False,
658 kill_kernel=False,
657 read_only=False,
659 read_only=False,
658 logged_in=self.logged_in,
660 logged_in=self.logged_in,
659 login_available=self.login_available,
661 login_available=self.login_available,
660 mathjax_url=self.application.ipython_app.mathjax_url,
662 mathjax_url=self.application.ipython_app.mathjax_url,
661 )
663 )
662
664
663
665
664 #-----------------------------------------------------------------------------
666 #-----------------------------------------------------------------------------
665 # Cluster handlers
667 # Cluster handlers
666 #-----------------------------------------------------------------------------
668 #-----------------------------------------------------------------------------
667
669
668
670
669 class MainClusterHandler(AuthenticatedHandler):
671 class MainClusterHandler(AuthenticatedHandler):
670
672
671 @web.authenticated
673 @web.authenticated
672 def get(self):
674 def get(self):
673 cm = self.application.cluster_manager
675 cm = self.application.cluster_manager
674 self.finish(jsonapi.dumps(cm.list_profiles()))
676 self.finish(jsonapi.dumps(cm.list_profiles()))
675
677
676
678
677 class ClusterProfileHandler(AuthenticatedHandler):
679 class ClusterProfileHandler(AuthenticatedHandler):
678
680
679 @web.authenticated
681 @web.authenticated
680 def get(self, profile):
682 def get(self, profile):
681 cm = self.application.cluster_manager
683 cm = self.application.cluster_manager
682 self.finish(jsonapi.dumps(cm.profile_info(profile)))
684 self.finish(jsonapi.dumps(cm.profile_info(profile)))
683
685
684
686
685 class ClusterActionHandler(AuthenticatedHandler):
687 class ClusterActionHandler(AuthenticatedHandler):
686
688
687 @web.authenticated
689 @web.authenticated
688 def post(self, profile, action):
690 def post(self, profile, action):
689 cm = self.application.cluster_manager
691 cm = self.application.cluster_manager
690 if action == 'start':
692 if action == 'start':
691 n = int(self.get_argument('n', default=4))
693 n = int(self.get_argument('n', default=4))
692 cm.start_cluster(profile, n)
694 cm.start_cluster(profile, n)
693 if action == 'stop':
695 if action == 'stop':
694 cm.stop_cluster(profile)
696 cm.stop_cluster(profile)
695 self.finish()
697 self.finish()
696
698
697
699
698 #-----------------------------------------------------------------------------
700 #-----------------------------------------------------------------------------
699 # RST web service handlers
701 # RST web service handlers
700 #-----------------------------------------------------------------------------
702 #-----------------------------------------------------------------------------
701
703
702
704
703 class RSTHandler(AuthenticatedHandler):
705 class RSTHandler(AuthenticatedHandler):
704
706
705 @web.authenticated
707 @web.authenticated
706 def post(self):
708 def post(self):
707 if publish_string is None:
709 if publish_string is None:
708 raise web.HTTPError(503, u'docutils not available')
710 raise web.HTTPError(503, u'docutils not available')
709 body = self.request.body.strip()
711 body = self.request.body.strip()
710 source = body
712 source = body
711 # template_path=os.path.join(os.path.dirname(__file__), u'templates', u'rst_template.html')
713 # template_path=os.path.join(os.path.dirname(__file__), u'templates', u'rst_template.html')
712 defaults = {'file_insertion_enabled': 0,
714 defaults = {'file_insertion_enabled': 0,
713 'raw_enabled': 0,
715 'raw_enabled': 0,
714 '_disable_config': 1,
716 '_disable_config': 1,
715 'stylesheet_path': 0
717 'stylesheet_path': 0
716 # 'template': template_path
718 # 'template': template_path
717 }
719 }
718 try:
720 try:
719 html = publish_string(source, writer_name='html',
721 html = publish_string(source, writer_name='html',
720 settings_overrides=defaults
722 settings_overrides=defaults
721 )
723 )
722 except:
724 except:
723 raise web.HTTPError(400, u'Invalid RST')
725 raise web.HTTPError(400, u'Invalid RST')
724 print html
726 print html
725 self.set_header('Content-Type', 'text/html')
727 self.set_header('Content-Type', 'text/html')
726 self.finish(html)
728 self.finish(html)
727
729
728
730
@@ -1,130 +1,97 b''
1
1
2 .border-box-sizing {
3 box-sizing: border-box;
4 -moz-box-sizing: border-box;
5 -webkit-box-sizing: border-box;
6 }
7
8 /* Flexible box model classes */
2 /* Flexible box model classes */
9 /* Taken from Alex Russell http://infrequently.org/2009/08/css-3-progress/ */
3 /* Taken from Alex Russell http://infrequently.org/2009/08/css-3-progress/ */
10
4
11 .hbox {
5 .hbox {
12 display: -webkit-box;
6 display: -webkit-box;
13 -webkit-box-orient: horizontal;
7 -webkit-box-orient: horizontal;
14 -webkit-box-align: stretch;
8 -webkit-box-align: stretch;
15
9
16 display: -moz-box;
10 display: -moz-box;
17 -moz-box-orient: horizontal;
11 -moz-box-orient: horizontal;
18 -moz-box-align: stretch;
12 -moz-box-align: stretch;
19
13
20 display: box;
14 display: box;
21 box-orient: horizontal;
15 box-orient: horizontal;
22 box-align: stretch;
16 box-align: stretch;
23 }
17 }
24
18
25 .hbox > * {
19 .hbox > * {
26 -webkit-box-flex: 0;
20 -webkit-box-flex: 0;
27 -moz-box-flex: 0;
21 -moz-box-flex: 0;
28 box-flex: 0;
22 box-flex: 0;
29 }
23 }
30
24
31 .vbox {
25 .vbox {
32 display: -webkit-box;
26 display: -webkit-box;
33 -webkit-box-orient: vertical;
27 -webkit-box-orient: vertical;
34 -webkit-box-align: stretch;
28 -webkit-box-align: stretch;
35
29
36 display: -moz-box;
30 display: -moz-box;
37 -moz-box-orient: vertical;
31 -moz-box-orient: vertical;
38 -moz-box-align: stretch;
32 -moz-box-align: stretch;
39
33
40 display: box;
34 display: box;
41 box-orient: vertical;
35 box-orient: vertical;
42 box-align: stretch;
36 box-align: stretch;
43 }
37 }
44
38
45 .vbox > * {
39 .vbox > * {
46 -webkit-box-flex: 0;
40 -webkit-box-flex: 0;
47 -moz-box-flex: 0;
41 -moz-box-flex: 0;
48 box-flex: 0;
42 box-flex: 0;
49 }
43 }
50
44
51 .reverse {
45 .reverse {
52 -webkit-box-direction: reverse;
46 -webkit-box-direction: reverse;
53 -moz-box-direction: reverse;
47 -moz-box-direction: reverse;
54 box-direction: reverse;
48 box-direction: reverse;
55 }
49 }
56
50
57 .box-flex0 {
51 .box-flex0 {
58 -webkit-box-flex: 0;
52 -webkit-box-flex: 0;
59 -moz-box-flex: 0;
53 -moz-box-flex: 0;
60 box-flex: 0;
54 box-flex: 0;
61 }
55 }
62
56
63 .box-flex1, .box-flex {
57 .box-flex1, .box-flex {
64 -webkit-box-flex: 1;
58 -webkit-box-flex: 1;
65 -moz-box-flex: 1;
59 -moz-box-flex: 1;
66 box-flex: 1;
60 box-flex: 1;
67 }
61 }
68
62
69 .box-flex2 {
63 .box-flex2 {
70 -webkit-box-flex: 2;
64 -webkit-box-flex: 2;
71 -moz-box-flex: 2;
65 -moz-box-flex: 2;
72 box-flex: 2;
66 box-flex: 2;
73 }
67 }
74
68
75 .box-group1 {
69 .box-group1 {
76 -webkit-box-flex-group: 1;
70 -webkit-box-flex-group: 1;
77 -moz-box-flex-group: 1;
71 -moz-box-flex-group: 1;
78 box-flex-group: 1;
72 box-flex-group: 1;
79 }
73 }
80
74
81 .box-group2 {
75 .box-group2 {
82 -webkit-box-flex-group: 2;
76 -webkit-box-flex-group: 2;
83 -moz-box-flex-group: 2;
77 -moz-box-flex-group: 2;
84 box-flex-group: 2;
78 box-flex-group: 2;
85 }
79 }
86
80
87 .start {
81 .start {
88 -webkit-box-pack: start;
82 -webkit-box-pack: start;
89 -moz-box-pack: start;
83 -moz-box-pack: start;
90 box-pack: start;
84 box-pack: start;
91 }
85 }
92
86
93 .end {
87 .end {
94 -webkit-box-pack: end;
88 -webkit-box-pack: end;
95 -moz-box-pack: end;
89 -moz-box-pack: end;
96 box-pack: end;
90 box-pack: end;
97 }
91 }
98
92
99 .center {
93 .center {
100 -webkit-box-pack: center;
94 -webkit-box-pack: center;
101 -moz-box-pack: center;
95 -moz-box-pack: center;
102 box-pack: center;
96 box-pack: center;
103 }
97 }
104
105 .message {
106 border-width: 1px;
107 border-style: solid;
108 text-align: center;
109 padding: 0.5em;
110 margin: 0.5em 0;
111 }
112
113 .message.error {
114 background-color: #FFD3D1;
115 border-color: red;
116 }
117
118 .message.warning {
119 background-color: #FFD09E;
120 border-color: orange;
121 }
122
123 .message.info {
124 background-color: #CBFFBA;
125 border-color: green;
126 }
127
128 #content_panel {
129 margin: 0.5em;
130 } No newline at end of file
@@ -1,381 +1,389 b''
1 /**
1 /**
2 * Primary styles
2 * Primary styles
3 *
3 *
4 * Author: IPython Development Team
4 * Author: IPython Development Team
5 */
5 */
6
6
7
7
8 body {
8 body {
9 background-color: white;
9 background-color: white;
10 /* This makes sure that the body covers the entire window and needs to
10 /* This makes sure that the body covers the entire window and needs to
11 be in a different element than the display: box in wrapper below */
11 be in a different element than the display: box in wrapper below */
12 position: absolute;
12 position: absolute;
13 left: 0px;
13 left: 0px;
14 right: 0px;
14 right: 0px;
15 top: 0px;
15 top: 0px;
16 bottom: 0px;
16 bottom: 0px;
17 overflow: hidden;
17 overflow: hidden;
18 }
18 }
19
19
20 span#save_widget {
20 span#save_widget {
21 padding: 5px;
21 padding: 5px;
22 margin: 0px 0px 0px 300px;
22 margin: 0px 0px 0px 300px;
23 display:inline-block;
23 display:inline-block;
24 }
24 }
25
25
26 span#notebook_name {
26 span#notebook_name {
27 height: 1em;
27 height: 1em;
28 line-height: 1em;
28 line-height: 1em;
29 padding: 3px;
29 padding: 3px;
30 border: none;
30 border: none;
31 font-size: 146.5%;
31 font-size: 146.5%;
32 }
32 }
33
33
34 #menubar {
34 #menubar {
35 /* Initially hidden to prevent FLOUC */
35 /* Initially hidden to prevent FLOUC */
36 display: none;
36 display: none;
37 }
37 }
38
38
39 .ui-menubar-item .ui-button .ui-button-text {
39 .ui-menubar-item .ui-button .ui-button-text {
40 padding: 0.4em 1.0em;
40 padding: 0.4em 1.0em;
41 font-size: 100%;
41 font-size: 100%;
42 }
42 }
43
43
44 .ui-menu {
44 .ui-menu {
45 -moz-box-shadow: 0px 6px 10px -1px #adadad;
45 -moz-box-shadow: 0px 6px 10px -1px #adadad;
46 -webkit-box-shadow: 0px 6px 10px -1px #adadad;
46 -webkit-box-shadow: 0px 6px 10px -1px #adadad;
47 box-shadow: 0px 6px 10px -1px #adadad;
47 box-shadow: 0px 6px 10px -1px #adadad;
48 }
48 }
49
49
50 .ui-menu .ui-menu-item a {
50 .ui-menu .ui-menu-item a {
51 padding: 2px 1.6em;
51 padding: 2px 1.6em;
52 }
52 }
53
53
54 .ui-menu hr {
54 .ui-menu hr {
55 margin: 0.3em 0;
55 margin: 0.3em 0;
56 }
56 }
57
57
58 #menubar_container {
58 #menubar_container {
59 position: relative;
59 position: relative;
60 }
60 }
61
61
62 #notification {
62 #notification {
63 position: absolute;
63 position: absolute;
64 right: 3px;
64 right: 3px;
65 top: 3px;
65 top: 3px;
66 height: 25px;
66 height: 25px;
67 padding: 3px 6px;
67 padding: 3px 6px;
68 z-index: 10;
68 z-index: 10;
69 }
69 }
70
70
71 #toolbar {
71 #toolbar {
72 /* Initially hidden to prevent FLOUC */
72 /* Initially hidden to prevent FLOUC */
73 display: none;
73 display: none;
74 padding: 3px 15px;
74 padding: 3px 15px;
75 }
75 }
76
76
77 #cell_type {
77 #cell_type {
78 font-size: 85%;
78 font-size: 85%;
79 }
79 }
80
80
81
82 div#main_app {
83 /* Initially hidden to prevent FLOUC */
84 display: none;
85 width: 100%;
86 position: relative;
87 }
88
81 span#quick_help_area {
89 span#quick_help_area {
82 position: static;
90 position: static;
83 padding: 5px 0px;
91 padding: 5px 0px;
84 margin: 0px 0px 0px 0px;
92 margin: 0px 0px 0px 0px;
85 }
93 }
86
94
87 .help_string {
95 .help_string {
88 float: right;
96 float: right;
89 width: 170px;
97 width: 170px;
90 padding: 0px 5px;
98 padding: 0px 5px;
91 text-align: left;
99 text-align: left;
92 font-size: 85%;
100 font-size: 85%;
93 }
101 }
94
102
95 .help_string_label {
103 .help_string_label {
96 float: right;
104 float: right;
97 font-size: 85%;
105 font-size: 85%;
98 }
106 }
99
107
100 div#notebook_panel {
108 div#notebook_panel {
101 margin: 0px 0px 0px 0px;
109 margin: 0px 0px 0px 0px;
102 padding: 0px;
110 padding: 0px;
103 }
111 }
104
112
105 div#notebook {
113 div#notebook {
106 overflow-y: scroll;
114 overflow-y: scroll;
107 overflow-x: auto;
115 overflow-x: auto;
108 width: 100%;
116 width: 100%;
109 /* This spaces the cell away from the edge of the notebook area */
117 /* This spaces the cell away from the edge of the notebook area */
110 padding: 5px 5px 15px 5px;
118 padding: 5px 5px 15px 5px;
111 margin: 0px
119 margin: 0px
112 background-color: white;
120 background-color: white;
113 }
121 }
114
122
115 div#pager_splitter {
123 div#pager_splitter {
116 height: 8px;
124 height: 8px;
117 }
125 }
118
126
119 div#pager {
127 div#pager {
120 padding: 15px;
128 padding: 15px;
121 overflow: auto;
129 overflow: auto;
122 display: none;
130 display: none;
123 }
131 }
124
132
125 div.cell {
133 div.cell {
126 width: 100%;
134 width: 100%;
127 padding: 5px 5px 5px 0px;
135 padding: 5px 5px 5px 0px;
128 /* This acts as a spacer between cells, that is outside the border */
136 /* This acts as a spacer between cells, that is outside the border */
129 margin: 2px 0px 2px 0px;
137 margin: 2px 0px 2px 0px;
130 }
138 }
131
139
132 div.code_cell {
140 div.code_cell {
133 background-color: white;
141 background-color: white;
134 }
142 }
135 /* any special styling for code cells that are currently running goes here */
143 /* any special styling for code cells that are currently running goes here */
136 div.code_cell.running {
144 div.code_cell.running {
137 }
145 }
138
146
139 div.prompt {
147 div.prompt {
140 /* This needs to be wide enough for 3 digit prompt numbers: In[100]: */
148 /* This needs to be wide enough for 3 digit prompt numbers: In[100]: */
141 width: 11ex;
149 width: 11ex;
142 /* This 0.4em is tuned to match the padding on the CodeMirror editor. */
150 /* This 0.4em is tuned to match the padding on the CodeMirror editor. */
143 padding: 0.4em;
151 padding: 0.4em;
144 margin: 0px;
152 margin: 0px;
145 font-family: monospace;
153 font-family: monospace;
146 text-align:right;
154 text-align:right;
147 }
155 }
148
156
149 div.input {
157 div.input {
150 page-break-inside: avoid;
158 page-break-inside: avoid;
151 }
159 }
152
160
153 /* input_area and input_prompt must match in top border and margin for alignment */
161 /* input_area and input_prompt must match in top border and margin for alignment */
154 div.input_area {
162 div.input_area {
155 color: black;
163 color: black;
156 border: 1px solid #ddd;
164 border: 1px solid #ddd;
157 border-radius: 3px;
165 border-radius: 3px;
158 background: #f7f7f7;
166 background: #f7f7f7;
159 }
167 }
160
168
161 div.input_prompt {
169 div.input_prompt {
162 color: navy;
170 color: navy;
163 border-top: 1px solid transparent;
171 border-top: 1px solid transparent;
164 }
172 }
165
173
166 div.output {
174 div.output {
167 /* This is a spacer between the input and output of each cell */
175 /* This is a spacer between the input and output of each cell */
168 margin-top: 5px;
176 margin-top: 5px;
169 }
177 }
170
178
171 div.output_prompt {
179 div.output_prompt {
172 color: darkred;
180 color: darkred;
173 }
181 }
174
182
175 /* This class is the outer container of all output sections. */
183 /* This class is the outer container of all output sections. */
176 div.output_area {
184 div.output_area {
177 padding: 0px;
185 padding: 0px;
178 page-break-inside: avoid;
186 page-break-inside: avoid;
179 }
187 }
180
188
181 /* This class is for the output subarea inside the output_area and after
189 /* This class is for the output subarea inside the output_area and after
182 the prompt div. */
190 the prompt div. */
183 div.output_subarea {
191 div.output_subarea {
184 padding: 0.4em 6.1em 0.4em 0.4em;
192 padding: 0.4em 6.1em 0.4em 0.4em;
185 }
193 }
186
194
187 /* The rest of the output_* classes are for special styling of the different
195 /* The rest of the output_* classes are for special styling of the different
188 output types */
196 output types */
189
197
190 /* all text output has this class: */
198 /* all text output has this class: */
191 div.output_text {
199 div.output_text {
192 text-align: left;
200 text-align: left;
193 color: black;
201 color: black;
194 font-family: monospace;
202 font-family: monospace;
195 }
203 }
196
204
197 /* stdout/stderr are 'text' as well as 'stream', but pyout/pyerr are *not* streams */
205 /* stdout/stderr are 'text' as well as 'stream', but pyout/pyerr are *not* streams */
198 div.output_stream {
206 div.output_stream {
199 padding-top: 0.0em;
207 padding-top: 0.0em;
200 padding-bottom: 0.0em;
208 padding-bottom: 0.0em;
201 }
209 }
202 div.output_stdout {
210 div.output_stdout {
203 }
211 }
204 div.output_stderr {
212 div.output_stderr {
205 background: #fdd; /* very light red background for stderr */
213 background: #fdd; /* very light red background for stderr */
206 }
214 }
207
215
208 div.output_latex {
216 div.output_latex {
209 text-align: left;
217 text-align: left;
210 color: black;
218 color: black;
211 }
219 }
212
220
213 div.output_html {
221 div.output_html {
214 }
222 }
215
223
216 div.output_png {
224 div.output_png {
217 }
225 }
218
226
219 div.output_jpeg {
227 div.output_jpeg {
220 }
228 }
221
229
222 div.text_cell {
230 div.text_cell {
223 background-color: white;
231 background-color: white;
224 padding: 5px 5px 5px 5px;
232 padding: 5px 5px 5px 5px;
225 }
233 }
226
234
227 div.text_cell_input {
235 div.text_cell_input {
228 color: black;
236 color: black;
229 border: 1px solid #ddd;
237 border: 1px solid #ddd;
230 border-radius: 3px;
238 border-radius: 3px;
231 background: #f7f7f7;
239 background: #f7f7f7;
232 }
240 }
233
241
234 div.text_cell_render {
242 div.text_cell_render {
235 font-family: "Helvetica Neue", Arial, Helvetica, Geneva, sans-serif;
243 font-family: "Helvetica Neue", Arial, Helvetica, Geneva, sans-serif;
236 outline: none;
244 outline: none;
237 resize: none;
245 resize: none;
238 width: inherit;
246 width: inherit;
239 border-style: none;
247 border-style: none;
240 padding: 5px;
248 padding: 5px;
241 color: black;
249 color: black;
242 }
250 }
243
251
244 .CodeMirror {
252 .CodeMirror {
245 line-height: 1.231; /* Changed from 1em to our global default */
253 line-height: 1.231; /* Changed from 1em to our global default */
246 }
254 }
247
255
248 .CodeMirror-scroll {
256 .CodeMirror-scroll {
249 height: auto; /* Changed to auto to autogrow */
257 height: auto; /* Changed to auto to autogrow */
250 /* The CodeMirror docs are a bit fuzzy on if overflow-y should be hidden or visible.*/
258 /* The CodeMirror docs are a bit fuzzy on if overflow-y should be hidden or visible.*/
251 /* We have found that if it is visible, vertical scrollbars appear with font size changes.*/
259 /* We have found that if it is visible, vertical scrollbars appear with font size changes.*/
252 overflow-y: hidden;
260 overflow-y: hidden;
253 overflow-x: auto; /* Changed from auto to remove scrollbar */
261 overflow-x: auto; /* Changed from auto to remove scrollbar */
254 }
262 }
255
263
256 /* CSS font colors for translated ANSI colors. */
264 /* CSS font colors for translated ANSI colors. */
257
265
258
266
259 .ansiblack {color: black;}
267 .ansiblack {color: black;}
260 .ansired {color: darkred;}
268 .ansired {color: darkred;}
261 .ansigreen {color: darkgreen;}
269 .ansigreen {color: darkgreen;}
262 .ansiyellow {color: brown;}
270 .ansiyellow {color: brown;}
263 .ansiblue {color: darkblue;}
271 .ansiblue {color: darkblue;}
264 .ansipurple {color: darkviolet;}
272 .ansipurple {color: darkviolet;}
265 .ansicyan {color: steelblue;}
273 .ansicyan {color: steelblue;}
266 .ansigrey {color: grey;}
274 .ansigrey {color: grey;}
267 .ansibold {font-weight: bold;}
275 .ansibold {font-weight: bold;}
268
276
269 .completions , .tooltip {
277 .completions , .tooltip {
270 position: absolute;
278 position: absolute;
271 z-index: 10;
279 z-index: 10;
272 overflow: auto;
280 overflow: auto;
273 border: 1px solid black;
281 border: 1px solid black;
274 }
282 }
275
283
276 .completions select {
284 .completions select {
277 background: white;
285 background: white;
278 outline: none;
286 outline: none;
279 border: none;
287 border: none;
280 padding: 0px;
288 padding: 0px;
281 margin: 0px;
289 margin: 0px;
282 font-family: monospace;
290 font-family: monospace;
283 }
291 }
284
292
285 @-moz-keyframes fadeIn {
293 @-moz-keyframes fadeIn {
286 from {opacity:0;}
294 from {opacity:0;}
287 to {opacity:1;}
295 to {opacity:1;}
288 }
296 }
289
297
290 @-webkit-keyframes fadeIn {
298 @-webkit-keyframes fadeIn {
291 from {opacity:0;}
299 from {opacity:0;}
292 to {opacity:1;}
300 to {opacity:1;}
293 }
301 }
294
302
295 @keyframes fadeIn {
303 @keyframes fadeIn {
296 from {opacity:0;}
304 from {opacity:0;}
297 to {opacity:1;}
305 to {opacity:1;}
298 }
306 }
299
307
300 /*"close" "expand" and "Open in pager button" of
308 /*"close" "expand" and "Open in pager button" of
301 /* the tooltip*/
309 /* the tooltip*/
302 .tooltip a {
310 .tooltip a {
303 float:right;
311 float:right;
304 }
312 }
305
313
306 /*properties of tooltip after "expand"*/
314 /*properties of tooltip after "expand"*/
307 .bigtooltip {
315 .bigtooltip {
308 height:30%;
316 height:30%;
309 }
317 }
310
318
311 /*properties of tooltip before "expand"*/
319 /*properties of tooltip before "expand"*/
312 .smalltooltip {
320 .smalltooltip {
313 text-overflow: ellipsis;
321 text-overflow: ellipsis;
314 overflow: hidden;
322 overflow: hidden;
315 height:15%;
323 height:15%;
316 }
324 }
317
325
318 .tooltip {
326 .tooltip {
319 /*transition when "expand"ing tooltip */
327 /*transition when "expand"ing tooltip */
320 -webkit-transition-property: height;
328 -webkit-transition-property: height;
321 -webkit-transition-duration: 1s;
329 -webkit-transition-duration: 1s;
322 -moz-transition-property: height;
330 -moz-transition-property: height;
323 -moz-transition-duration: 1s;
331 -moz-transition-duration: 1s;
324 transition-property: height;
332 transition-property: height;
325 transition-duration: 1s;
333 transition-duration: 1s;
326 max-width:700px;
334 max-width:700px;
327 border-radius: 0px 10px 10px 10px;
335 border-radius: 0px 10px 10px 10px;
328 box-shadow: 3px 3px 5px #999;
336 box-shadow: 3px 3px 5px #999;
329 /*fade-in animation when inserted*/
337 /*fade-in animation when inserted*/
330 -webkit-animation: fadeIn 200ms;
338 -webkit-animation: fadeIn 200ms;
331 -moz-animation: fadeIn 200ms;
339 -moz-animation: fadeIn 200ms;
332 animation: fadeIn 200ms;
340 animation: fadeIn 200ms;
333 vertical-align: middle;
341 vertical-align: middle;
334 background: #FDFDD8;
342 background: #FDFDD8;
335 outline: none;
343 outline: none;
336 padding: 3px;
344 padding: 3px;
337 margin: 0px;
345 margin: 0px;
338 font-family: monospace;
346 font-family: monospace;
339 min-height:50px;
347 min-height:50px;
340 }
348 }
341
349
342 /*fixed part of the completion*/
350 /*fixed part of the completion*/
343 .completions p b {
351 .completions p b {
344 font-weight:bold;
352 font-weight:bold;
345 }
353 }
346
354
347 .completions p {
355 .completions p {
348 background: #DDF;
356 background: #DDF;
349 /*outline: none;
357 /*outline: none;
350 padding: 0px;*/
358 padding: 0px;*/
351 border-bottom: black solid 1px;
359 border-bottom: black solid 1px;
352 padding: 1px;
360 padding: 1px;
353 font-family: monospace;
361 font-family: monospace;
354 }
362 }
355
363
356 pre.dialog {
364 pre.dialog {
357 background-color: #f7f7f7;
365 background-color: #f7f7f7;
358 border: 1px solid #ddd;
366 border: 1px solid #ddd;
359 border-radius: 3px;
367 border-radius: 3px;
360 padding: 0.4em;
368 padding: 0.4em;
361 padding-left: 2em;
369 padding-left: 2em;
362 }
370 }
363
371
364 p.dialog {
372 p.dialog {
365 padding : 0.2em;
373 padding : 0.2em;
366 }
374 }
367
375
368 .shortcut_key {
376 .shortcut_key {
369 display: inline-block;
377 display: inline-block;
370 width: 15ex;
378 width: 15ex;
371 text-align: right;
379 text-align: right;
372 font-family: monospace;
380 font-family: monospace;
373 }
381 }
374
382
375 .shortcut_descr {
383 .shortcut_descr {
376 }
384 }
377
385
378 /* Word-wrap output correctly. This is the CSS3 spelling, though Firefox seems
386 /* Word-wrap output correctly. This is the CSS3 spelling, though Firefox seems
379 to not honor it correctly. Webkit browsers (Chrome, rekonq, Safari) do.
387 to not honor it correctly. Webkit browsers (Chrome, rekonq, Safari) do.
380 */
388 */
381 pre, code, kbd, samp { white-space: pre-wrap; }
389 pre, code, kbd, samp { white-space: pre-wrap; }
@@ -1,73 +1,77 b''
1 /**
1 /**
2 * Primary styles
2 * Primary styles
3 *
3 *
4 * Author: IPython Development Team
4 * Author: IPython Development Team
5 */
5 */
6
6
7
7
8 body {
8 body {
9 background-color: white;
9 background-color: white;
10 /* This makes sure that the body covers the entire window and needs to
10 /* This makes sure that the body covers the entire window and needs to
11 be in a different element than the display: box in wrapper below */
11 be in a different element than the display: box in wrapper below */
12 position: absolute;
12 position: absolute;
13 left: 0px;
13 left: 0px;
14 right: 0px;
14 right: 0px;
15 top: 0px;
15 top: 0px;
16 bottom: 0px;
16 bottom: 0px;
17 overflow: hidden;
17 overflow: visible;
18 }
18 }
19
19
20
20
21 div#header {
21 div#header {
22 /* Initially hidden to prevent FLOUC */
22 /* Initially hidden to prevent FLOUC */
23 display: none;
23 display: none;
24 position: relative;
24 position: relative;
25 height: 40px;
25 height: 40px;
26 padding: 5px;
26 padding: 5px;
27 margin: 0px;
27 margin: 0px;
28 width: 100%;
28 width: 100%;
29 }
29 }
30
30
31 span#ipython_notebook {
31 span#ipython_notebook {
32 position: absolute;
32 position: absolute;
33 padding: 2px 2px 2px 5px;
33 padding: 2px 2px 2px 5px;
34 }
34 }
35
35
36 span#ipython_notebook h1 img {
36 span#ipython_notebook h1 img {
37 font-family: Verdana, "Helvetica Neue", Arial, Helvetica, Geneva, sans-serif;
37 font-family: Verdana, "Helvetica Neue", Arial, Helvetica, Geneva, sans-serif;
38 height: 24px;
38 height: 24px;
39 text-decoration:none;
39 text-decoration:none;
40 display: inline;
40 display: inline;
41 color: black;
41 color: black;
42 }
42 }
43
43
44 div#main_app {
44 #site {
45 /* Initially hidden to prevent FLOUC */
45 width: 100%
46 display: none;
46 display: none;
47 width: 100%;
48 position: relative;
49 }
47 }
50
48
51 /* We set the fonts by hand here to override the values in the theme */
49 /* We set the fonts by hand here to override the values in the theme */
52 .ui-widget {
50 .ui-widget {
53 font-family: "Lucinda Grande", "Lucinda Sans Unicode", Helvetica, Arial, Verdana, sans-serif;
51 font-family: "Lucinda Grande", "Lucinda Sans Unicode", Helvetica, Arial, Verdana, sans-serif;
54 }
52 }
55
53
56 .ui-widget input, .ui-widget select, .ui-widget textarea, .ui-widget button {
54 .ui-widget input, .ui-widget select, .ui-widget textarea, .ui-widget button {
57 font-family: "Lucinda Grande", "Lucinda Sans Unicode", Helvetica, Arial, Verdana, sans-serif;
55 font-family: "Lucinda Grande", "Lucinda Sans Unicode", Helvetica, Arial, Verdana, sans-serif;
58 }
56 }
59
57
60 /* Smaller buttons */
58 /* Smaller buttons */
61 .ui-button .ui-button-text {
59 .ui-button .ui-button-text {
62 padding: 0.2em 0.8em;
60 padding: 0.2em 0.8em;
63 font-size: 77%;
61 font-size: 77%;
64 }
62 }
65
63
64 input.ui-button {
65 padding: 0.3em 0.9em;
66 }
67
66 span#login_widget {
68 span#login_widget {
67 float: right;
69 float: right;
68 }
70 }
69
71
70 /* generic class for hidden objects */
72 .border-box-sizing {
71 .hidden {
73 box-sizing: border-box;
72 display: none;
74 -moz-box-sizing: border-box;
75 -webkit-box-sizing: border-box;
73 }
76 }
77
@@ -1,82 +1,53 b''
1
1
2 /**
2 /**
3 * Primary styles
3 * Primary styles
4 *
4 *
5 * Author: IPython Development Team
5 * Author: IPython Development Team
6 */
6 */
7
7
8
8 #main_app {
9 body {
9 width: 920px;
10 background-color: white;
10 margin: auto;
11 /* This makes sure that the body covers the entire window and needs to
12 be in a different element than the display: box in wrapper below */
13 position: absolute;
14 left: 0px;
15 right: 0px;
16 top: 0px;
17 bottom: 0px;
18 overflow: auto;
19 }
20
21 #left_panel {
22 }
11 }
23
12
24 #drop_zone {
13 #notebooks_toolbar {
25 height: 200px;
26 width: 200px
27 }
28
29 #content_panel {
30 width: 600px;
31 }
32
33 #content_toolbar {
34 padding: 5px;
14 padding: 5px;
35 height: 25px;
15 height: 25px;
36 line-height: 25px;
16 line-height: 25px;
37 }
17 }
38
18
39 #header_border {
40 width: 100%;
41 height: 2px;
42 }
43
44 #app_hbox {
45 width: 100%;
46 }
47
48 #drag_info {
19 #drag_info {
49 float: left;
20 float: left;
50 }
21 }
51
22
52 #notebooks_buttons {
23 #notebooks_buttons {
53 float: right;
24 float: right;
54 }
25 }
55
26
56 #project_name {
27 #project_name {
57 height: 25px;
28 height: 25px;
58 line-height: 25px;
29 line-height: 25px;
59 padding: 3px;
30 padding: 3px;
60 }
31 }
61
32
62 .notebook_item {
33 .notebook_item {
63 height: 25px;
34 height: 25px;
64 line-height: 25px;
35 line-height: 25px;
65 padding: 3px;
36 padding: 3px;
66 }
37 }
67
38
68 .notebook_item a {
39 .notebook_item a {
69 text-decoration: none;
40 text-decoration: none;
70 }
41 }
71
42
72 .item_buttons {
43 .item_buttons {
73 float: right;
44 float: right;
74 }
45 }
75
46
76 .item_buttons .upload_button {
47 .item_buttons .upload_button {
77 color: darkred;
48 color: darkred;
78 }
49 }
79
50
80 .highlight_text {
51 .highlight_text {
81 color: blue;
52 color: blue;
82 }
53 }
1 NO CONTENT: file renamed from IPython/frontend/html/notebook/static/js/layout.js to IPython/frontend/html/notebook/static/js/layoutmanager.js
NO CONTENT: file renamed from IPython/frontend/html/notebook/static/js/layout.js to IPython/frontend/html/notebook/static/js/layoutmanager.js
@@ -1,30 +1,22 b''
1 //----------------------------------------------------------------------------
1 //----------------------------------------------------------------------------
2 // Copyright (C) 2008-2011 The IPython Development Team
2 // Copyright (C) 2008-2011 The IPython Development Team
3 //
3 //
4 // Distributed under the terms of the BSD License. The full license is in
4 // Distributed under the terms of the BSD License. The full license is in
5 // the file COPYING, distributed as part of this software.
5 // the file COPYING, distributed as part of this software.
6 //----------------------------------------------------------------------------
6 //----------------------------------------------------------------------------
7
7
8 //============================================================================
8 //============================================================================
9 // On document ready
9 // On document ready
10 //============================================================================
10 //============================================================================
11
11
12
12
13 $(document).ready(function () {
13 $(document).ready(function () {
14
14
15 $('div#header').addClass('border-box-sizing');
15 IPython.page = new IPython.Page();
16 $('div#header_border').addClass('border-box-sizing ui-widget ui-widget-content');
16 $('input#login_submit').button();
17
18 $('div#main_app').addClass('border-box-sizing ui-widget');
17 $('div#main_app').addClass('border-box-sizing ui-widget');
19 $('div#app_hbox').addClass('hbox');
18 IPython.page.show();
20
19 $('input#password_input').focus();
21 $('div#left_panel').addClass('box-flex');
22 $('div#right_panel').addClass('box-flex');
23 $('input#signin').button();
24
25 // These have display: none in the css file and are made visible here to prevent FLOUC.
26 $('div#header').css('display','block');
27 $('div#main_app').css('display','block');
28
20
29 });
21 });
30
22
@@ -1,42 +1,44 b''
1 //----------------------------------------------------------------------------
1 //----------------------------------------------------------------------------
2 // Copyright (C) 2008-2011 The IPython Development Team
2 // Copyright (C) 2008-2011 The IPython Development Team
3 //
3 //
4 // Distributed under the terms of the BSD License. The full license is in
4 // Distributed under the terms of the BSD License. The full license is in
5 // the file COPYING, distributed as part of this software.
5 // the file COPYING, distributed as part of this software.
6 //----------------------------------------------------------------------------
6 //----------------------------------------------------------------------------
7
7
8 //============================================================================
8 //============================================================================
9 // Login button
9 // Login button
10 //============================================================================
10 //============================================================================
11
11
12 var IPython = (function (IPython) {
12 var IPython = (function (IPython) {
13
13
14 var LoginWidget = function (selector) {
14 var LoginWidget = function (selector) {
15 this.selector = selector;
15 this.selector = selector;
16 if (this.selector !== undefined) {
16 if (this.selector !== undefined) {
17 this.element = $(selector);
17 this.element = $(selector);
18 this.style();
18 this.style();
19 this.bind_events();
19 this.bind_events();
20 }
20 }
21 };
21 };
22
22
23 LoginWidget.prototype.style = function () {
23 LoginWidget.prototype.style = function () {
24 this.element.find('button#logout').button();
24 this.element.find('button#logout').button();
25 this.element.find('button#login').button();
25 this.element.find('button#login').button();
26 };
26 };
27
28
27 LoginWidget.prototype.bind_events = function () {
29 LoginWidget.prototype.bind_events = function () {
28 var that = this;
30 var that = this;
29 this.element.find("button#logout").click(function () {
31 this.element.find("button#logout").click(function () {
30 window.location = "/logout";
32 window.location = "/logout";
31 });
33 });
32 this.element.find("button#login").click(function () {
34 this.element.find("button#login").click(function () {
33 window.location = "/login";
35 window.location = "/login";
34 });
36 });
35 };
37 };
36
38
37 // Set module variables
39 // Set module variables
38 IPython.LoginWidget = LoginWidget;
40 IPython.LoginWidget = LoginWidget;
39
41
40 return IPython;
42 return IPython;
41
43
42 }(IPython));
44 }(IPython));
@@ -1,251 +1,250 b''
1 //----------------------------------------------------------------------------
1 //----------------------------------------------------------------------------
2 // Copyright (C) 2008-2011 The IPython Development Team
2 // Copyright (C) 2008-2011 The IPython Development Team
3 //
3 //
4 // Distributed under the terms of the BSD License. The full license is in
4 // Distributed under the terms of the BSD License. The full license is in
5 // the file COPYING, distributed as part of this software.
5 // the file COPYING, distributed as part of this software.
6 //----------------------------------------------------------------------------
6 //----------------------------------------------------------------------------
7
7
8 //============================================================================
8 //============================================================================
9 // NotebookList
9 // NotebookList
10 //============================================================================
10 //============================================================================
11
11
12 var IPython = (function (IPython) {
12 var IPython = (function (IPython) {
13
13
14 var NotebookList = function (selector) {
14 var NotebookList = function (selector) {
15 this.selector = selector;
15 this.selector = selector;
16 if (this.selector !== undefined) {
16 if (this.selector !== undefined) {
17 this.element = $(selector);
17 this.element = $(selector);
18 this.style();
18 this.style();
19 this.bind_events();
19 this.bind_events();
20 }
20 }
21 };
21 };
22
22
23 NotebookList.prototype.style = function () {
23 NotebookList.prototype.style = function () {
24 this.element.addClass('ui-widget ui-widget-content');
25 $('div#project_name').addClass('ui-widget ui-widget-header');
24 $('div#project_name').addClass('ui-widget ui-widget-header');
26 };
25 };
27
26
28
27
29 NotebookList.prototype.bind_events = function () {
28 NotebookList.prototype.bind_events = function () {
30 if (IPython.read_only){
29 if (IPython.read_only){
31 return;
30 return;
32 }
31 }
33 var that = this;
32 var that = this;
34 this.element.bind('dragover', function () {
33 this.element.bind('dragover', function () {
35 return false;
34 return false;
36 });
35 });
37 this.element.bind('drop', function (event) {
36 this.element.bind('drop', function (event) {
38 var files = event.originalEvent.dataTransfer.files;
37 var files = event.originalEvent.dataTransfer.files;
39 for (var i = 0, f; f = files[i]; i++) {
38 for (var i = 0, f; f = files[i]; i++) {
40 var reader = new FileReader();
39 var reader = new FileReader();
41 reader.readAsText(f);
40 reader.readAsText(f);
42 var fname = f.name.split('.');
41 var fname = f.name.split('.');
43 var nbname = fname.slice(0,-1).join('.');
42 var nbname = fname.slice(0,-1).join('.');
44 var nbformat = fname.slice(-1)[0];
43 var nbformat = fname.slice(-1)[0];
45 if (nbformat === 'ipynb') {nbformat = 'json';};
44 if (nbformat === 'ipynb') {nbformat = 'json';};
46 if (nbformat === 'py' || nbformat === 'json') {
45 if (nbformat === 'py' || nbformat === 'json') {
47 var item = that.new_notebook_item(0);
46 var item = that.new_notebook_item(0);
48 that.add_name_input(nbname, item);
47 that.add_name_input(nbname, item);
49 item.data('nbformat', nbformat);
48 item.data('nbformat', nbformat);
50 // Store the notebook item in the reader so we can use it later
49 // Store the notebook item in the reader so we can use it later
51 // to know which item it belongs to.
50 // to know which item it belongs to.
52 $(reader).data('item', item);
51 $(reader).data('item', item);
53 reader.onload = function (event) {
52 reader.onload = function (event) {
54 var nbitem = $(event.target).data('item');
53 var nbitem = $(event.target).data('item');
55 that.add_notebook_data(event.target.result, nbitem);
54 that.add_notebook_data(event.target.result, nbitem);
56 that.add_upload_button(nbitem);
55 that.add_upload_button(nbitem);
57 };
56 };
58 };
57 };
59 }
58 }
60 return false;
59 return false;
61 });
60 });
62 };
61 };
63
62
64
63
65 NotebookList.prototype.load_list = function () {
64 NotebookList.prototype.load_list = function () {
66 var settings = {
65 var settings = {
67 processData : false,
66 processData : false,
68 cache : false,
67 cache : false,
69 type : "GET",
68 type : "GET",
70 dataType : "json",
69 dataType : "json",
71 success : $.proxy(this.list_loaded, this)
70 success : $.proxy(this.list_loaded, this)
72 };
71 };
73 var url = $('body').data('baseProjectUrl') + 'notebooks';
72 var url = $('body').data('baseProjectUrl') + 'notebooks';
74 $.ajax(url, settings);
73 $.ajax(url, settings);
75 };
74 };
76
75
77
76
78 NotebookList.prototype.list_loaded = function (data, status, xhr) {
77 NotebookList.prototype.list_loaded = function (data, status, xhr) {
79 var len = data.length;
78 var len = data.length;
80 // Todo: remove old children
79 // Todo: remove old children
81 for (var i=0; i<len; i++) {
80 for (var i=0; i<len; i++) {
82 var notebook_id = data[i].notebook_id;
81 var notebook_id = data[i].notebook_id;
83 var nbname = data[i].name;
82 var nbname = data[i].name;
84 var item = this.new_notebook_item(i);
83 var item = this.new_notebook_item(i);
85 this.add_link(notebook_id, nbname, item);
84 this.add_link(notebook_id, nbname, item);
86 if (!IPython.read_only){
85 if (!IPython.read_only){
87 // hide delete buttons when readonly
86 // hide delete buttons when readonly
88 this.add_delete_button(item);
87 this.add_delete_button(item);
89 }
88 }
90 };
89 };
91 };
90 };
92
91
93
92
94 NotebookList.prototype.new_notebook_item = function (index) {
93 NotebookList.prototype.new_notebook_item = function (index) {
95 var item = $('<div/>');
94 var item = $('<div/>');
96 item.addClass('notebook_item ui-widget ui-widget-content ui-helper-clearfix');
95 item.addClass('notebook_item ui-widget ui-widget-content ui-helper-clearfix');
97 var item_name = $('<span/>').addClass('item_name');
96 var item_name = $('<span/>').addClass('item_name');
98
97
99 item.append(item_name);
98 item.append(item_name);
100 if (index === -1) {
99 if (index === -1) {
101 this.element.append(item);
100 this.element.append(item);
102 } else {
101 } else {
103 this.element.children().eq(index).after(item);
102 this.element.children().eq(index).after(item);
104 }
103 }
105 return item;
104 return item;
106 };
105 };
107
106
108
107
109 NotebookList.prototype.add_link = function (notebook_id, nbname, item) {
108 NotebookList.prototype.add_link = function (notebook_id, nbname, item) {
110 item.data('nbname', nbname);
109 item.data('nbname', nbname);
111 item.data('notebook_id', notebook_id);
110 item.data('notebook_id', notebook_id);
112 var new_item_name = $('<span/>').addClass('item_name');
111 var new_item_name = $('<span/>').addClass('item_name');
113 new_item_name.append(
112 new_item_name.append(
114 $('<a/>').
113 $('<a/>').
115 attr('href', $('body').data('baseProjectUrl')+notebook_id).
114 attr('href', $('body').data('baseProjectUrl')+notebook_id).
116 attr('target','_blank').
115 attr('target','_blank').
117 text(nbname)
116 text(nbname)
118 );
117 );
119 var e = item.find('.item_name');
118 var e = item.find('.item_name');
120 if (e.length === 0) {
119 if (e.length === 0) {
121 item.append(new_item_name);
120 item.append(new_item_name);
122 } else {
121 } else {
123 e.replaceWith(new_item_name);
122 e.replaceWith(new_item_name);
124 };
123 };
125 };
124 };
126
125
127
126
128 NotebookList.prototype.add_name_input = function (nbname, item) {
127 NotebookList.prototype.add_name_input = function (nbname, item) {
129 item.data('nbname', nbname);
128 item.data('nbname', nbname);
130 var new_item_name = $('<span/>').addClass('item_name');
129 var new_item_name = $('<span/>').addClass('item_name');
131 new_item_name.append(
130 new_item_name.append(
132 $('<input/>').addClass('ui-widget ui-widget-content').
131 $('<input/>').addClass('ui-widget ui-widget-content').
133 attr('value', nbname).
132 attr('value', nbname).
134 attr('size', '30').
133 attr('size', '30').
135 attr('type', 'text')
134 attr('type', 'text')
136 );
135 );
137 var e = item.find('.item_name');
136 var e = item.find('.item_name');
138 if (e.length === 0) {
137 if (e.length === 0) {
139 item.append(new_item_name);
138 item.append(new_item_name);
140 } else {
139 } else {
141 e.replaceWith(new_item_name);
140 e.replaceWith(new_item_name);
142 };
141 };
143 };
142 };
144
143
145
144
146 NotebookList.prototype.add_notebook_data = function (data, item) {
145 NotebookList.prototype.add_notebook_data = function (data, item) {
147 item.data('nbdata',data);
146 item.data('nbdata',data);
148 };
147 };
149
148
150
149
151 NotebookList.prototype.add_delete_button = function (item) {
150 NotebookList.prototype.add_delete_button = function (item) {
152 var new_buttons = $('<span/>').addClass('item_buttons');
151 var new_buttons = $('<span/>').addClass('item_buttons');
153 var delete_button = $('<button>Delete</button>').button().
152 var delete_button = $('<button>Delete</button>').button().
154 click(function (e) {
153 click(function (e) {
155 // $(this) is the button that was clicked.
154 // $(this) is the button that was clicked.
156 var that = $(this);
155 var that = $(this);
157 // We use the nbname and notebook_id from the parent notebook_item element's
156 // We use the nbname and notebook_id from the parent notebook_item element's
158 // data because the outer scopes values change as we iterate through the loop.
157 // data because the outer scopes values change as we iterate through the loop.
159 var parent_item = that.parents('div.notebook_item');
158 var parent_item = that.parents('div.notebook_item');
160 var nbname = parent_item.data('nbname');
159 var nbname = parent_item.data('nbname');
161 var notebook_id = parent_item.data('notebook_id');
160 var notebook_id = parent_item.data('notebook_id');
162 var dialog = $('<div/>');
161 var dialog = $('<div/>');
163 dialog.html('Are you sure you want to permanently delete the notebook: ' + nbname + '?');
162 dialog.html('Are you sure you want to permanently delete the notebook: ' + nbname + '?');
164 parent_item.append(dialog);
163 parent_item.append(dialog);
165 dialog.dialog({
164 dialog.dialog({
166 resizable: false,
165 resizable: false,
167 modal: true,
166 modal: true,
168 title: "Delete notebook",
167 title: "Delete notebook",
169 buttons : {
168 buttons : {
170 "Delete": function () {
169 "Delete": function () {
171 var settings = {
170 var settings = {
172 processData : false,
171 processData : false,
173 cache : false,
172 cache : false,
174 type : "DELETE",
173 type : "DELETE",
175 dataType : "json",
174 dataType : "json",
176 success : function (data, status, xhr) {
175 success : function (data, status, xhr) {
177 parent_item.remove();
176 parent_item.remove();
178 }
177 }
179 };
178 };
180 var url = $('body').data('baseProjectUrl') + 'notebooks/' + notebook_id;
179 var url = $('body').data('baseProjectUrl') + 'notebooks/' + notebook_id;
181 $.ajax(url, settings);
180 $.ajax(url, settings);
182 $(this).dialog('close');
181 $(this).dialog('close');
183 },
182 },
184 "Cancel": function () {
183 "Cancel": function () {
185 $(this).dialog('close');
184 $(this).dialog('close');
186 }
185 }
187 }
186 }
188 });
187 });
189 });
188 });
190 new_buttons.append(delete_button);
189 new_buttons.append(delete_button);
191 var e = item.find('.item_buttons');
190 var e = item.find('.item_buttons');
192 if (e.length === 0) {
191 if (e.length === 0) {
193 item.append(new_buttons);
192 item.append(new_buttons);
194 } else {
193 } else {
195 e.replaceWith(new_buttons);
194 e.replaceWith(new_buttons);
196 };
195 };
197 };
196 };
198
197
199
198
200 NotebookList.prototype.add_upload_button = function (item) {
199 NotebookList.prototype.add_upload_button = function (item) {
201 var that = this;
200 var that = this;
202 var new_buttons = $('<span/>').addClass('item_buttons');
201 var new_buttons = $('<span/>').addClass('item_buttons');
203 var upload_button = $('<button>Upload</button>').button().
202 var upload_button = $('<button>Upload</button>').button().
204 click(function (e) {
203 click(function (e) {
205 var nbname = item.find('.item_name > input').attr('value');
204 var nbname = item.find('.item_name > input').attr('value');
206 var nbformat = item.data('nbformat');
205 var nbformat = item.data('nbformat');
207 var nbdata = item.data('nbdata');
206 var nbdata = item.data('nbdata');
208 var content_type = 'text/plain';
207 var content_type = 'text/plain';
209 if (nbformat === 'json') {
208 if (nbformat === 'json') {
210 content_type = 'application/json';
209 content_type = 'application/json';
211 } else if (nbformat === 'py') {
210 } else if (nbformat === 'py') {
212 content_type = 'application/x-python';
211 content_type = 'application/x-python';
213 };
212 };
214 var settings = {
213 var settings = {
215 processData : false,
214 processData : false,
216 cache : false,
215 cache : false,
217 type : 'POST',
216 type : 'POST',
218 dataType : 'json',
217 dataType : 'json',
219 data : nbdata,
218 data : nbdata,
220 headers : {'Content-Type': content_type},
219 headers : {'Content-Type': content_type},
221 success : function (data, status, xhr) {
220 success : function (data, status, xhr) {
222 that.add_link(data, nbname, item);
221 that.add_link(data, nbname, item);
223 that.add_delete_button(item);
222 that.add_delete_button(item);
224 }
223 }
225 };
224 };
226
225
227 var qs = $.param({name:nbname, format:nbformat});
226 var qs = $.param({name:nbname, format:nbformat});
228 var url = $('body').data('baseProjectUrl') + 'notebooks?' + qs;
227 var url = $('body').data('baseProjectUrl') + 'notebooks?' + qs;
229 $.ajax(url, settings);
228 $.ajax(url, settings);
230 });
229 });
231 var cancel_button = $('<button>Cancel</button>').button().
230 var cancel_button = $('<button>Cancel</button>').button().
232 click(function (e) {
231 click(function (e) {
233 item.remove();
232 item.remove();
234 });
233 });
235 upload_button.addClass('upload_button');
234 upload_button.addClass('upload_button');
236 new_buttons.append(upload_button).append(cancel_button);
235 new_buttons.append(upload_button).append(cancel_button);
237 var e = item.find('.item_buttons');
236 var e = item.find('.item_buttons');
238 if (e.length === 0) {
237 if (e.length === 0) {
239 item.append(new_buttons);
238 item.append(new_buttons);
240 } else {
239 } else {
241 e.replaceWith(new_buttons);
240 e.replaceWith(new_buttons);
242 };
241 };
243 };
242 };
244
243
245
244
246 IPython.NotebookList = NotebookList;
245 IPython.NotebookList = NotebookList;
247
246
248 return IPython;
247 return IPython;
249
248
250 }(IPython));
249 }(IPython));
251
250
@@ -1,120 +1,119 b''
1 //----------------------------------------------------------------------------
1 //----------------------------------------------------------------------------
2 // Copyright (C) 2008-2011 The IPython Development Team
2 // Copyright (C) 2008-2011 The IPython Development Team
3 //
3 //
4 // Distributed under the terms of the BSD License. The full license is in
4 // Distributed under the terms of the BSD License. The full license is in
5 // the file COPYING, distributed as part of this software.
5 // the file COPYING, distributed as part of this software.
6 //----------------------------------------------------------------------------
6 //----------------------------------------------------------------------------
7
7
8 //============================================================================
8 //============================================================================
9 // On document ready
9 // On document ready
10 //============================================================================
10 //============================================================================
11
11
12
12
13 $(document).ready(function () {
13 $(document).ready(function () {
14 if (window.MathJax){
14 if (window.MathJax){
15 // MathJax loaded
15 // MathJax loaded
16 MathJax.Hub.Config({
16 MathJax.Hub.Config({
17 tex2jax: {
17 tex2jax: {
18 inlineMath: [ ['$','$'], ["\\(","\\)"] ],
18 inlineMath: [ ['$','$'], ["\\(","\\)"] ],
19 displayMath: [ ['$$','$$'], ["\\[","\\]"] ]
19 displayMath: [ ['$$','$$'], ["\\[","\\]"] ]
20 },
20 },
21 displayAlign: 'left', // Change this to 'center' to center equations.
21 displayAlign: 'left', // Change this to 'center' to center equations.
22 "HTML-CSS": {
22 "HTML-CSS": {
23 styles: {'.MathJax_Display': {"margin": 0}}
23 styles: {'.MathJax_Display': {"margin": 0}}
24 }
24 }
25 });
25 });
26 }else if (window.mathjax_url != ""){
26 }else if (window.mathjax_url != ""){
27 // Don't have MathJax, but should. Show dialog.
27 // Don't have MathJax, but should. Show dialog.
28 var dialog = $('<div></div>')
28 var dialog = $('<div></div>')
29 .append(
29 .append(
30 $("<p></p>").addClass('dialog').html(
30 $("<p></p>").addClass('dialog').html(
31 "Math/LaTeX rendering will be disabled."
31 "Math/LaTeX rendering will be disabled."
32 )
32 )
33 ).append(
33 ).append(
34 $("<p></p>").addClass('dialog').html(
34 $("<p></p>").addClass('dialog').html(
35 "If you have administrative access to the notebook server and" +
35 "If you have administrative access to the notebook server and" +
36 " a working internet connection, you can install a local copy" +
36 " a working internet connection, you can install a local copy" +
37 " of MathJax for offline use with the following command on the server" +
37 " of MathJax for offline use with the following command on the server" +
38 " at a Python or IPython prompt:"
38 " at a Python or IPython prompt:"
39 )
39 )
40 ).append(
40 ).append(
41 $("<pre></pre>").addClass('dialog').html(
41 $("<pre></pre>").addClass('dialog').html(
42 ">>> from IPython.external import mathjax; mathjax.install_mathjax()"
42 ">>> from IPython.external import mathjax; mathjax.install_mathjax()"
43 )
43 )
44 ).append(
44 ).append(
45 $("<p></p>").addClass('dialog').html(
45 $("<p></p>").addClass('dialog').html(
46 "This will try to install MathJax into the IPython source directory."
46 "This will try to install MathJax into the IPython source directory."
47 )
47 )
48 ).append(
48 ).append(
49 $("<p></p>").addClass('dialog').html(
49 $("<p></p>").addClass('dialog').html(
50 "If IPython is installed to a location that requires" +
50 "If IPython is installed to a location that requires" +
51 " administrative privileges to write, you will need to make this call as" +
51 " administrative privileges to write, you will need to make this call as" +
52 " an administrator, via 'sudo'."
52 " an administrator, via 'sudo'."
53 )
53 )
54 ).append(
54 ).append(
55 $("<p></p>").addClass('dialog').html(
55 $("<p></p>").addClass('dialog').html(
56 "When you start the notebook server, you can instruct it to disable MathJax support altogether:"
56 "When you start the notebook server, you can instruct it to disable MathJax support altogether:"
57 )
57 )
58 ).append(
58 ).append(
59 $("<pre></pre>").addClass('dialog').html(
59 $("<pre></pre>").addClass('dialog').html(
60 "$ ipython notebook --no-mathjax"
60 "$ ipython notebook --no-mathjax"
61 )
61 )
62 ).append(
62 ).append(
63 $("<p></p>").addClass('dialog').html(
63 $("<p></p>").addClass('dialog').html(
64 "which will prevent this dialog from appearing."
64 "which will prevent this dialog from appearing."
65 )
65 )
66 ).dialog({
66 ).dialog({
67 title: "Failed to retrieve MathJax from '" + window.mathjax_url + "'",
67 title: "Failed to retrieve MathJax from '" + window.mathjax_url + "'",
68 width: "70%",
68 width: "70%",
69 modal: true,
69 modal: true,
70 })
70 })
71 }else{
71 }else{
72 // No MathJax, but none expected. No dialog.
72 // No MathJax, but none expected. No dialog.
73 }
73 }
74
74
75
75
76 IPython.markdown_converter = new Markdown.Converter();
76 IPython.markdown_converter = new Markdown.Converter();
77 IPython.read_only = $('meta[name=read_only]').attr("content") == 'True';
77 IPython.read_only = $('meta[name=read_only]').attr("content") == 'True';
78
78
79 $('div#header').addClass('border-box-sizing');
79 $('div#header').addClass('border-box-sizing');
80 $('div#main_app').addClass('border-box-sizing ui-widget ui-widget-content');
80 $('div#main_app').addClass('border-box-sizing ui-widget ui-widget-content');
81 $('div#notebook_panel').addClass('border-box-sizing ui-widget');
81 $('div#notebook_panel').addClass('border-box-sizing ui-widget');
82
82
83 IPython.layout_manager = new IPython.LayoutManager();
83 IPython.layout_manager = new IPython.LayoutManager();
84 IPython.pager = new IPython.Pager('div#pager', 'div#pager_splitter');
84 IPython.pager = new IPython.Pager('div#pager', 'div#pager_splitter');
85 IPython.quick_help = new IPython.QuickHelp('span#quick_help_area');
85 IPython.quick_help = new IPython.QuickHelp('span#quick_help_area');
86 IPython.login_widget = new IPython.LoginWidget('span#login_widget');
86 IPython.login_widget = new IPython.LoginWidget('span#login_widget');
87 IPython.notebook = new IPython.Notebook('div#notebook');
87 IPython.notebook = new IPython.Notebook('div#notebook');
88 IPython.save_widget = new IPython.SaveWidget('span#save_widget');
88 IPython.save_widget = new IPython.SaveWidget('span#save_widget');
89 IPython.menubar = new IPython.MenuBar('#menubar')
89 IPython.menubar = new IPython.MenuBar('#menubar')
90 IPython.toolbar = new IPython.ToolBar('#toolbar')
90 IPython.toolbar = new IPython.ToolBar('#toolbar')
91 IPython.notification_widget = new IPython.NotificationWidget('#notification')
91 IPython.notification_widget = new IPython.NotificationWidget('#notification')
92
92
93 IPython.layout_manager.do_resize();
93 IPython.layout_manager.do_resize();
94
94
95 // These have display: none in the css file and are made visible here to prevent FLOUC.
95 // These have display: none in the css file and are made visible here to prevent FLOUC.
96 $('div#header').css('display','block');
96 $('div#header').css('display','block');
97
97
98 if(IPython.read_only){
98 if(IPython.read_only){
99 // hide various elements from read-only view
99 // hide various elements from read-only view
100 $('div#pager').remove();
100 $('div#pager').remove();
101 $('div#pager_splitter').remove();
101 $('div#pager_splitter').remove();
102 $('span#login_widget').removeClass('hidden');
103
102
104 // set the notebook name field as not modifiable
103 // set the notebook name field as not modifiable
105 $('#notebook_name').attr('disabled','disabled')
104 $('#notebook_name').attr('disabled','disabled')
106 }
105 }
107
106
108 $('div#menubar').css('display','block');
107 $('div#menubar').css('display','block');
109 $('div#toolbar').css('display','block');
108 $('div#toolbar').css('display','block');
110 $('div#main_app').css('display','block');
109 $('div#main_app').css('display','block');
111
110
112 IPython.layout_manager.do_resize();
111 IPython.layout_manager.do_resize();
113 $([IPython.events]).on('notebook_loaded.Notebook', function () {
112 $([IPython.events]).on('notebook_loaded.Notebook', function () {
114 IPython.layout_manager.do_resize();
113 IPython.layout_manager.do_resize();
115 IPython.save_widget.update_url();
114 IPython.save_widget.update_url();
116 })
115 })
117 IPython.notebook.load_notebook($('body').data('notebookId'));
116 IPython.notebook.load_notebook($('body').data('notebookId'));
118
117
119 });
118 });
120
119
@@ -1,42 +1,32 b''
1 //----------------------------------------------------------------------------
1 //----------------------------------------------------------------------------
2 // Copyright (C) 2008-2011 The IPython Development Team
2 // Copyright (C) 2008-2011 The IPython Development Team
3 //
3 //
4 // Distributed under the terms of the BSD License. The full license is in
4 // Distributed under the terms of the BSD License. The full license is in
5 // the file COPYING, distributed as part of this software.
5 // the file COPYING, distributed as part of this software.
6 //----------------------------------------------------------------------------
6 //----------------------------------------------------------------------------
7
7
8 //============================================================================
8 //============================================================================
9 // On document ready
9 // On document ready
10 //============================================================================
10 //============================================================================
11
11
12
12
13 $(document).ready(function () {
13 $(document).ready(function () {
14
14
15 $('div#header').addClass('border-box-sizing');
15 IPython.page = new IPython.Page();
16 $('div#header_border').addClass('border-box-sizing ui-widget ui-widget-content');
17
16
18 $('div#main_app').addClass('border-box-sizing ui-widget');
17 $('div#main_app').addClass('border-box-sizing ui-widget');
19 $('div#app_hbox').addClass('hbox');
18 $('div#notebooks_toolbar').addClass('ui-widget ui-helper-clearfix');
20
21 $('div#content_toolbar').addClass('ui-widget ui-helper-clearfix');
22
23 $('#new_notebook').button().click(function (e) {
19 $('#new_notebook').button().click(function (e) {
24 window.open($('body').data('baseProjectUrl')+'new');
20 window.open($('body').data('baseProjectUrl')+'new');
25 });
21 });
26
22
27 $('div#left_panel').addClass('box-flex');
23 IPython.read_only = $('body').data('readOnly') === 'True';
28 $('div#right_panel').addClass('box-flex');
29
30 IPython.read_only = $('meta[name=read_only]').attr("content") == 'True';
31 IPython.notebook_list = new IPython.NotebookList('div#notebook_list');
24 IPython.notebook_list = new IPython.NotebookList('div#notebook_list');
32 IPython.login_widget = new IPython.LoginWidget('span#login_widget');
25 IPython.login_widget = new IPython.LoginWidget('span#login_widget');
33
26
34 IPython.notebook_list.load_list();
27 IPython.notebook_list.load_list();
35
28
36 // These have display: none in the css file and are made visible here to prevent FLOUC.
29 IPython.page.show();
37 $('div#header').css('display','block');
38 $('div#main_app').css('display','block');
39
40
30
41 });
31 });
42
32
@@ -1,26 +1,42 b''
1 {% extends layout.html %}
1 {% extends page.html %}
2
2
3 {% block content_panel %}
3 {% block stylesheet %}
4
4
5 {% if login_available %}
5 <link rel="stylesheet" href="{{static_url("css/login.css") }}" type="text/css"/>
6
7 {% end %}
8
9
10 {% block login_widget %}
11 {% end %}
12
13
14 {% block site %}
6
15
16 <div id="main_app">
17
18 {% if login_available %}
7 <form action="/login?next={{url_escape(next)}}" method="post">
19 <form action="/login?next={{url_escape(next)}}" method="post">
8 Password: <input type="password" name="password" id="focus">
20 Password: <input type="password" name="password" id="password_input">
9 <input type="submit" value="Sign in" id="signin">
21 <input type="submit" value="Log in" id="login_submit">
10 </form>
22 </form>
23 {% end %}
11
24
25 {% if message %}
26 {% for key in message %}
27 <div class="message {{key}}">
28 {{message[key]}}
29 </div>
30 {% end %}
12 {% end %}
31 {% end %}
13
32
14 {% end %}
33 <div/>
15
34
16 {% block login_widget %}
17 {% end %}
35 {% end %}
18
36
37
19 {% block script %}
38 {% block script %}
20 <script type="text/javascript">
39
21 $(document).ready(function() {
40 <script src="{{static_url("js/loginmain.js") }}" type="text/javascript" charset="utf-8"></script>
22 IPython.login_widget = new IPython.LoginWidget('span#login_widget');
41
23 $('#focus').focus();
24 });
25 </script>
26 {% end %}
42 {% end %}
@@ -1,28 +1,40 b''
1 {% extends layout.html %}
1 {% extends page.html %}
2
2
3 {% block content_panel %}
3 {% block stylesheet %}
4 <ul>
5 {% if read_only or not login_available %}
6
4
7 Proceed to the <a href="/">list of notebooks</a>.</li>
5 <link rel="stylesheet" href="{{static_url("css/logout.css") }}" type="text/css"/>
8
6
9 {% else %}
7 {% end %}
8
9
10 {% block login_widget %}
11 {% end %}
12
13 {% block site %}
10
14
11 Proceed to the <a href="/login">login page</a>.</li>
15 <div id="main_app">
12
16
17 {% if message %}
18 {% for key in message %}
19 <div class="message {{key}}">
20 {{message[key]}}
21 </div>
22 {% end %}
13 {% end %}
23 {% end %}
14
24
15 </ul>
25 {% if read_only or not login_available %}
26 Proceed to the <a href="/">dashboard</a>.
27 {% else %}
28 Proceed to the <a href="/login">login page</a>.
29 {% end %}
16
30
17 {% end %}
18
31
19 {% block login_widget %}
32 <div/>
33
20 {% end %}
34 {% end %}
21
35
22 {% block script %}
36 {% block script %}
23 <script type="text/javascript">
37
24 $(document).ready(function() {
38 <script src="{{static_url("js/logoutmain.js") }}" type="text/javascript" charset="utf-8"></script>
25 IPython.login_widget = new IPython.LoginWidget('span#login_widget');
39
26 });
27 </script>
28 {% end %}
40 {% end %}
@@ -1,86 +1,58 b''
1 <!DOCTYPE HTML>
1 <!DOCTYPE HTML>
2 <html>
2 <html>
3
3
4 <head>
4 <head>
5 <meta charset="utf-8">
5 <meta charset="utf-8">
6
6
7 <title>{% block title %}IPython Notebook{% end %}</title>
7 <title>{% block title %}IPython Notebook{% end %}</title>
8
8
9 <link rel="stylesheet" href="{{static_url("jquery/css/themes/base/jquery-ui.min.css") }}" type="text/css" />
9 <link rel="stylesheet" href="{{static_url("jquery/css/themes/base/jquery-ui.min.css") }}" type="text/css" />
10 <link rel="stylesheet" href="{{static_url("css/boilerplate.css") }}" type="text/css" />
10 <link rel="stylesheet" href="{{static_url("css/boilerplate.css") }}" type="text/css" />
11 <link rel="stylesheet" href="{{static_url("css/layout.css") }}" type="text/css" />
11 <link rel="stylesheet" href="{{static_url("css/fbm.css") }}" type="text/css" />
12 <link rel="stylesheet" href="{{static_url("css/base.css") }}" type="text/css"/>
12 <link rel="stylesheet" href="{{static_url("css/page.css") }}" type="text/css"/>
13 {% block stylesheet %}
13 {% block stylesheet %}
14 {% end %}
14 {% end %}
15
15
16 {% block meta %}
16 {% block meta %}
17 {% end %}
17 {% end %}
18
18
19 </head>
19 </head>
20
20
21 <body {% block params %}{% end %}>
21 <body {% block params %}{% end %}>
22
22
23 <div id="header">
23 <div id="header">
24 <span id="ipython_notebook"><h1><img src='{{static_url("ipynblogo.png") }}' alt='IPython Notebook'/></h1></span>
24 <span id="ipython_notebook"><h1><a href={{base_project_url}} alt='dashboard'><img src='{{static_url("ipynblogo.png") }}' alt='IPython Notebook'/></a></h1></span>
25
25
26 {% block login_widget %}
26 {% block login_widget %}
27
27
28 <span id="login_widget">
28 <span id="login_widget">
29 {% if logged_in %}
29 {% if logged_in %}
30 <button id="logout">Logout</button>
30 <button id="logout">Logout</button>
31 {% elif login_available and not logged_in %}
31 {% elif login_available and not logged_in %}
32 <button id="login">Login</button>
32 <button id="login">Login</button>
33 {% end %}
33 {% end %}
34 </span>
34 </span>
35
35
36 {% end %}
36 {% end %}
37
37
38 {% block header %}
38 {% block header %}
39 {% end %}
39 {% end %}
40 </div>
40 </div>
41
41
42 <div id="header_border"></div>
42 <div id="site">
43
43 {% block site %}
44 <div id="main_app">
44 {% end %}
45
46 <div id="app_hbox">
47
48 <div id="left_panel">
49 {% block left_panel %}
50 {% end %}
51 </div>
52
53 <div id="content_panel">
54 {% if message %}
55
56 {% for key in message %}
57 <div class="message {{key}}">
58 {{message[key]}}
59 </div>
60 {% end %}
61 {% end %}
62
63 {% block content_panel %}
64 {% end %}
65 </div>
66 <div id="right_panel">
67 {% block right_panel %}
68 {% end %}
69 </div>
70
71 </div>
72
73 </div>
45 </div>
74
46
75 <script src="{{static_url("jquery/js/jquery-1.7.1.min.js") }}" type="text/javascript" charset="utf-8"></script>
47 <script src="{{static_url("jquery/js/jquery-1.7.1.min.js") }}" type="text/javascript" charset="utf-8"></script>
76 <script src="{{static_url("jquery/js/jquery-ui.min.js") }}" type="text/javascript" charset="utf-8"></script>
48 <script src="{{static_url("jquery/js/jquery-ui.min.js") }}" type="text/javascript" charset="utf-8"></script>
77 <script src="{{static_url("js/namespace.js") }}" type="text/javascript" charset="utf-8"></script>
49 <script src="{{static_url("js/namespace.js") }}" type="text/javascript" charset="utf-8"></script>
78 <script src="{{static_url("js/loginmain.js") }}" type="text/javascript" charset="utf-8"></script>
50 <script src="{{static_url("js/page.js") }}" type="text/javascript" charset="utf-8"></script>
79 <script src="{{static_url("js/loginwidget.js") }}" type="text/javascript" charset="utf-8"></script>
51 <script src="{{static_url("js/loginwidget.js") }}" type="text/javascript" charset="utf-8"></script>
80
52
81 {% block script %}
53 {% block script %}
82 {% end %}
54 {% end %}
83
55
84 </body>
56 </body>
85
57
86 </html>
58 </html>
@@ -1,43 +1,45 b''
1 {% extends layout.html %}
1 {% extends page.html %}
2
2
3 {% block title %}
3 {% block title %}IPython Dashboard{% end %}
4 IPython Dashboard
5 {% end %}
6
4
7 {% block stylesheet %}
5 {% block stylesheet %}
8 <link rel="stylesheet" href="{{static_url("css/projectdashboard.css") }}" type="text/css" />
6 <link rel="stylesheet" href="{{static_url("css/projectdashboard.css") }}" type="text/css" />
9 {% end %}
7 {% end %}
10
8
11 {% block meta %}
12 <meta name="read_only" content="{{read_only}}"/>
13 {% end %}
14
9
15 {% block params %}
10 {% block params %}
16 data-project={{project}}
11 data-project={{project}}
17 data-base-project-url={{base_project_url}}
12 data-base-project-url={{base_project_url}}
18 data-base-kernel-url={{base_kernel_url}}
13 data-base-kernel-url={{base_kernel_url}}
14 data-read-only={{read_only}}
19 {% end %}
15 {% end %}
20
16
21 {% block content_panel %}
17 {% block site %}
18
19 <div id="main_app">
20
22 {% if logged_in or not read_only %}
21 {% if logged_in or not read_only %}
23
22
24 <div id="content_toolbar">
23 <div id="notebooks_toolbar">
25 <span id="drag_info">Drag files onto the list to import
24 <span id="drag_info">Drag files onto the list to import
26 notebooks.</span>
25 notebooks.</span>
27
26
28 <span id="notebooks_buttons">
27 <span id="notebooks_buttons">
29 <button id="new_notebook">New Notebook</button>
28 <button id="new_notebook">New Notebook</button>
30 </span>
29 </span>
31 </div>
30 </div>
32
31
33 {% end %}
32 {% end %}
34
33
35 <div id="notebook_list">
34 <div id="notebook_list">
36 <div id="project_name"><h2>{{project}}</h2></div>
35 <div id="project_name"><h2>{{project}}</h2></div>
37 </div>
36 </div>
37
38 </div>
39
38 {% end %}
40 {% end %}
39
41
40 {% block script %}
42 {% block script %}
41 <script src="{{static_url("js/notebooklist.js") }}" type="text/javascript" charset="utf-8"></script>
43 <script src="{{static_url("js/notebooklist.js") }}" type="text/javascript" charset="utf-8"></script>
42 <script src="{{static_url("js/projectdashboardmain.js") }}" type="text/javascript" charset="utf-8"></script>
44 <script src="{{static_url("js/projectdashboardmain.js") }}" type="text/javascript" charset="utf-8"></script>
43 {% end %}
45 {% end %}
General Comments 0
You need to be logged in to leave comments. Login now