##// END OF EJS Templates
Massive work on the notebook document format....
Brian E. Granger -
Show More
@@ -0,0 +1,195 b''
1 #-----------------------------------------------------------------------------
2 # Copyright (C) 2011 The IPython Development Team
3 #
4 # Distributed under the terms of the BSD License. The full license is in
5 # the file COPYING.txt, distributed as part of this software.
6 #-----------------------------------------------------------------------------
7
8 #-----------------------------------------------------------------------------
9 # Imports
10 #-----------------------------------------------------------------------------
11
12 import datetime
13 import os
14 import uuid
15
16 from tornado import web
17
18 from IPython.config.configurable import Configurable
19 from IPython.nbformat import current
20 from IPython.utils.traitlets import Unicode, List, Dict
21
22
23 #-----------------------------------------------------------------------------
24 # Code
25 #-----------------------------------------------------------------------------
26
27
28 class NotebookManager(Configurable):
29
30 notebook_dir = Unicode(os.getcwd())
31 filename_ext = Unicode(u'.ipynb')
32 allowed_formats = List([u'json',u'xml',u'py'])
33
34 # Map notebook_ids to notebook names
35 mapping = Dict()
36 # Map notebook names to notebook_ids
37 rev_mapping = Dict()
38
39 def list_notebooks(self):
40 """List all notebooks in the notebook dir.
41
42 This returns a list of dicts of the form::
43
44 dict(notebook_id=notebook,name=name)
45 """
46 names = os.listdir(self.notebook_dir)
47 names = [name.strip(self.filename_ext)\
48 for name in names if name.endswith(self.filename_ext)]
49 data = []
50 for name in names:
51 if name not in self.rev_mapping:
52 notebook_id = self.new_notebook_id(name)
53 else:
54 notebook_id = self.rev_mapping[name]
55 data.append(dict(notebook_id=notebook_id,name=name))
56 return data
57
58 def new_notebook_id(self, name):
59 """Generate a new notebook_id for a name and store its mappings."""
60 notebook_id = unicode(uuid.uuid4())
61 self.mapping[notebook_id] = name
62 self.rev_mapping[name] = notebook_id
63 return notebook_id
64
65 def delete_notebook_id(self, notebook_id):
66 """Delete a notebook's id only. This doesn't delete the actual notebook."""
67 name = self.mapping[notebook_id]
68 del self.mapping[notebook_id]
69 del self.rev_mapping[name]
70
71 def notebook_exists(self, notebook_id):
72 """Does a notebook exist?"""
73 if notebook_id not in self.mapping:
74 return False
75 path = self.get_path_by_name(self.mapping[notebook_id])
76 if not os.path.isfile(path):
77 return False
78 return True
79
80 def find_path(self, notebook_id):
81 """Return a full path to a notebook given its notebook_id."""
82 try:
83 name = self.mapping[notebook_id]
84 except KeyError:
85 raise web.HTTPError(404)
86 return self.get_path_by_name(name)
87
88 def get_path_by_name(self, name):
89 """Return a full path to a notebook given its name."""
90 filename = name + self.filename_ext
91 path = os.path.join(self.notebook_dir, filename)
92 return path
93
94 def get_notebook(self, notebook_id, format=u'json'):
95 """Get the representation of a notebook in format by notebook_id."""
96 format = unicode(format)
97 if format not in self.allowed_formats:
98 raise web.HTTPError(415)
99 last_modified, nb = self.get_notebook_object(notebook_id)
100 data = current.writes(nb, format)
101 name = nb.get('name','notebook')
102 return last_modified, name, data
103
104 def get_notebook_object(self, notebook_id):
105 """Get the NotebookNode representation of a notebook by notebook_id."""
106 path = self.find_path(notebook_id)
107 if not os.path.isfile(path):
108 raise web.HTTPError(404)
109 info = os.stat(path)
110 last_modified = datetime.datetime.utcfromtimestamp(info.st_mtime)
111 try:
112 with open(path,'r') as f:
113 s = f.read()
114 try:
115 # v2 and later have xml in the .ipynb files
116 nb = current.reads(s, 'xml')
117 except:
118 # v1 had json in the .ipynb files
119 nb = current.reads(s, 'json')
120 except:
121 raise web.HTTPError(404)
122 return last_modified, nb
123
124 def save_new_notebook(self, data, format=u'json'):
125 """Save a new notebook and return its notebook_id."""
126 if format not in self.allowed_formats:
127 raise web.HTTPError(415)
128 try:
129 nb = current.reads(data, format)
130 except:
131 raise web.HTTPError(400)
132 try:
133 name = nb.name
134 except AttributeError:
135 raise web.HTTPError(400)
136 notebook_id = self.new_notebook_id(name)
137 self.save_notebook_object(notebook_id, nb)
138 return notebook_id
139
140 def save_notebook(self, notebook_id, data, format=u'json'):
141 """Save an existing notebook by notebook_id."""
142 if format not in self.allowed_formats:
143 raise web.HTTPError(415)
144 try:
145 nb = current.reads(data, format)
146 except:
147 raise web.HTTPError(400)
148 self.save_notebook_object(notebook_id, nb)
149
150 def save_notebook_object(self, notebook_id, nb):
151 """Save an existing notebook object by notebook_id."""
152 if notebook_id not in self.mapping:
153 raise web.HTTPError(404)
154 old_name = self.mapping[notebook_id]
155 try:
156 new_name = nb.name
157 except AttributeError:
158 raise web.HTTPError(400)
159 path = self.get_path_by_name(new_name)
160 try:
161 with open(path,'w') as f:
162 current.write(nb, f, u'xml')
163 except:
164 raise web.HTTPError(400)
165 if old_name != new_name:
166 old_path = self.get_path_by_name(old_name)
167 if os.path.isfile(old_path):
168 os.unlink(old_path)
169 self.mapping[notebook_id] = new_name
170 self.rev_mapping[new_name] = notebook_id
171
172 def delete_notebook(self, notebook_id):
173 """Delete notebook by notebook_id."""
174 path = self.find_path(notebook_id)
175 if not os.path.isfile(path):
176 raise web.HTTPError(404)
177 os.unlink(path)
178 self.delete_notebook_id(notebook_id)
179
180 def new_notebook(self):
181 """Create a new notebook and returns its notebook_id."""
182 i = 0
183 while True:
184 name = u'Untitled%i' % i
185 path = self.get_path_by_name(name)
186 if not os.path.isfile(path):
187 break
188 else:
189 i = i+1
190 notebook_id = self.new_notebook_id(name)
191 nb = current.new_notebook(name=name, id=notebook_id)
192 with open(path,'w') as f:
193 current.write(nb, f, u'xml')
194 return notebook_id
195
@@ -1,116 +1,125 b''
1 """Tornado handlers for the notebook."""
1 """Tornado handlers for the notebook."""
2
2
3 #-----------------------------------------------------------------------------
3 #-----------------------------------------------------------------------------
4 # Imports
4 # Imports
5 #-----------------------------------------------------------------------------
5 #-----------------------------------------------------------------------------
6
6
7 import datetime
8 import json
7 import json
9 import logging
8 import logging
10 import os
11 import urllib
9 import urllib
12
10
13 from tornado import web
11 from tornado import web
14 from tornado import websocket
12 from tornado import websocket
15
13
14
16 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
17 # Handlers
16 # Handlers
18 #-----------------------------------------------------------------------------
17 #-----------------------------------------------------------------------------
19
18
20
19
21 class MainHandler(web.RequestHandler):
20 class MainHandler(web.RequestHandler):
22 def get(self):
21 def get(self):
23 self.render('notebook.html')
22 notebook_id = self.application.notebook_manager.new_notebook()
23 self.render('notebook.html', notebook_id=notebook_id)
24
25
26 class NamedNotebookHandler(web.RequestHandler):
27 def get(self, notebook_id):
28 nbm = self.application.notebook_manager
29 if not nbm.notebook_exists(notebook_id):
30 raise web.HTTPError(404)
31 self.render('notebook.html', notebook_id=notebook_id)
24
32
25
33
26 class KernelHandler(web.RequestHandler):
34 class KernelHandler(web.RequestHandler):
27
35
28 def get(self):
36 def get(self):
29 self.write(json.dumps(self.application.kernel_ids))
37 self.write(json.dumps(self.application.kernel_ids))
30
38
31 def post(self):
39 def post(self):
32 kernel_id = self.application.start_kernel()
40 kernel_id = self.application.start_kernel()
41 self.set_header('Location', '/'+kernel_id)
33 self.write(json.dumps(kernel_id))
42 self.write(json.dumps(kernel_id))
34
43
35
44
36 class KernelActionHandler(web.RequestHandler):
45 class KernelActionHandler(web.RequestHandler):
37
46
38 def post(self, kernel_id, action):
47 def post(self, kernel_id, action):
39 # TODO: figure out a better way of handling RPC style calls.
48 # TODO: figure out a better way of handling RPC style calls.
40 if action == 'interrupt':
49 if action == 'interrupt':
41 self.application.interrupt_kernel(kernel_id)
50 self.application.interrupt_kernel(kernel_id)
42 if action == 'restart':
51 if action == 'restart':
43 new_kernel_id = self.application.restart_kernel(kernel_id)
52 new_kernel_id = self.application.restart_kernel(kernel_id)
44 self.write(json.dumps(new_kernel_id))
53 self.write(json.dumps(new_kernel_id))
45
54
46
55
47 class ZMQStreamHandler(websocket.WebSocketHandler):
56 class ZMQStreamHandler(websocket.WebSocketHandler):
48
57
49 def initialize(self, stream_name):
58 def initialize(self, stream_name):
50 self.stream_name = stream_name
59 self.stream_name = stream_name
51
60
52 def open(self, kernel_id):
61 def open(self, kernel_id):
53 self.router = self.application.get_router(kernel_id, self.stream_name)
62 self.router = self.application.get_router(kernel_id, self.stream_name)
54 self.client_id = self.router.register_client(self)
63 self.client_id = self.router.register_client(self)
55 logging.info("Connection open: %s, %s" % (kernel_id, self.client_id))
64 logging.info("Connection open: %s, %s" % (kernel_id, self.client_id))
56
65
57 def on_message(self, msg):
66 def on_message(self, msg):
58 self.router.forward_msg(self.client_id, msg)
67 self.router.forward_msg(self.client_id, msg)
59
68
60 def on_close(self):
69 def on_close(self):
61 self.router.unregister_client(self.client_id)
70 self.router.unregister_client(self.client_id)
62 logging.info("Connection closed: %s" % self.client_id)
71 logging.info("Connection closed: %s" % self.client_id)
63
72
64
73
65 class NotebookRootHandler(web.RequestHandler):
74 class NotebookRootHandler(web.RequestHandler):
66
75
67 def get(self):
76 def get(self):
68 files = os.listdir(os.getcwd())
77 nbm = self.application.notebook_manager
69 files = [file for file in files if file.endswith(".ipynb")]
78 files = nbm.list_notebooks()
70 self.write(json.dumps(files))
79 self.write(json.dumps(files))
71
80
81 def post(self):
82 nbm = self.application.notebook_manager
83 body = self.request.body.strip()
84 format = self.get_argument('format', default='json')
85 if body:
86 notebook_id = nbm.save_new_notebook(body, format)
87 else:
88 notebook_id = nbm.new_notebook()
89 self.set_header('Location', '/'+notebook_id)
90 self.write(json.dumps(notebook_id))
72
91
73 class NotebookHandler(web.RequestHandler):
74
75 SUPPORTED_METHODS = ("GET", "DELETE", "PUT")
76
92
77 def find_path(self, filename):
93 class NotebookHandler(web.RequestHandler):
78 filename = urllib.unquote(filename)
79 if not filename.endswith('.ipynb'):
80 raise web.HTTPError(400)
81 path = os.path.join(os.getcwd(), filename)
82 return path
83
94
84 def get(self, filename):
95 SUPPORTED_METHODS = ('GET', 'PUT', 'DELETE')
85 path = self.find_path(filename)
96
86 if not os.path.isfile(path):
97 def get(self, notebook_id):
87 raise web.HTTPError(404)
98 nbm = self.application.notebook_manager
88 info = os.stat(path)
99 format = self.get_argument('format', default='json')
89 self.set_header("Content-Type", "application/unknown")
100 last_mod, name, data = nbm.get_notebook(notebook_id, format)
90 self.set_header("Last-Modified", datetime.datetime.utcfromtimestamp(
101 if format == u'json':
91 info.st_mtime))
102 self.set_header('Content-Type', 'application/json')
92 f = open(path, "r")
103 self.set_header('Content-Disposition','attachment; filename=%s.json' % name)
93 try:
104 elif format == u'xml':
94 self.finish(f.read())
105 self.set_header('Content-Type', 'text/xml')
95 finally:
106 self.set_header('Content-Disposition','attachment; filename=%s.ipynb' % name)
96 f.close()
107 elif format == u'py':
97
108 self.set_header('Content-Type', 'text/plain')
98 def put(self, filename):
109 self.set_header('Content-Disposition','attachment; filename=%s.py' % name)
99 path = self.find_path(filename)
110 self.set_header('Last-Modified', last_mod)
100 f = open(path, "w")
111 self.finish(data)
101 f.write(self.request.body)
112
102 f.close()
113 def put(self, notebook_id):
114 nbm = self.application.notebook_manager
115 format = self.get_argument('format', default='json')
116 nbm.save_notebook(notebook_id, self.request.body, format)
117 self.set_status(204)
103 self.finish()
118 self.finish()
104
119
105 def delete(self, filename):
120 def delete(self, notebook_id):
106 path = self.find_path(filename)
121 nbm = self.application.notebook_manager
107 if not os.path.isfile(path):
122 nbm.delete_notebook(notebook_id)
108 raise web.HTTPError(404)
109 os.unlink(path)
110 self.set_status(204)
123 self.set_status(204)
111 self.finish()
124 self.finish()
112
125
113
114
115
116
@@ -1,264 +1,269 b''
1 """A tornado based IPython notebook server."""
1 """A tornado based IPython notebook server."""
2
2
3 #-----------------------------------------------------------------------------
3 #-----------------------------------------------------------------------------
4 # Copyright (C) 2011 The IPython Development Team
4 # Copyright (C) 2011 The IPython Development Team
5 #
5 #
6 # Distributed under the terms of the BSD License. The full license is in
6 # Distributed under the terms of the BSD License. The full license is in
7 # the file COPYING.txt, distributed as part of this software.
7 # the file COPYING.txt, distributed as part of this software.
8 #-----------------------------------------------------------------------------
8 #-----------------------------------------------------------------------------
9
9
10 #-----------------------------------------------------------------------------
10 #-----------------------------------------------------------------------------
11 # Imports
11 # Imports
12 #-----------------------------------------------------------------------------
12 #-----------------------------------------------------------------------------
13
13
14 import logging
14 import logging
15 import os
15 import os
16 import signal
16 import signal
17 import sys
17 import sys
18
18
19 import zmq
19 import zmq
20
20
21 # Install the pyzmq ioloop. This has to be done before anything else from
21 # Install the pyzmq ioloop. This has to be done before anything else from
22 # tornado is imported.
22 # tornado is imported.
23 from zmq.eventloop import ioloop
23 from zmq.eventloop import ioloop
24 import tornado.ioloop
24 import tornado.ioloop
25 tornado.ioloop = ioloop
25 tornado.ioloop = ioloop
26
26
27 from tornado import httpserver
27 from tornado import httpserver
28 from tornado import web
28 from tornado import web
29
29
30 from kernelmanager import KernelManager
30 from .kernelmanager import KernelManager
31 from sessionmanager import SessionManager
31 from .sessionmanager import SessionManager
32 from handlers import (
32 from .handlers import (
33 MainHandler, KernelHandler, KernelActionHandler, ZMQStreamHandler,
33 MainHandler, NamedNotebookHandler,
34 KernelHandler, KernelActionHandler, ZMQStreamHandler,
34 NotebookRootHandler, NotebookHandler
35 NotebookRootHandler, NotebookHandler
35 )
36 )
36 from routers import IOPubStreamRouter, ShellStreamRouter
37 from .routers import IOPubStreamRouter, ShellStreamRouter
38 from .notebookmanager import NotebookManager
37
39
38 from IPython.core.application import BaseIPythonApplication
40 from IPython.core.application import BaseIPythonApplication
39 from IPython.core.profiledir import ProfileDir
41 from IPython.core.profiledir import ProfileDir
40 from IPython.frontend.qt.console.rich_ipython_widget import RichIPythonWidget
42 from IPython.frontend.qt.console.rich_ipython_widget import RichIPythonWidget
41 from IPython.zmq.session import Session
43 from IPython.zmq.session import Session
42 from IPython.zmq.zmqshell import ZMQInteractiveShell
44 from IPython.zmq.zmqshell import ZMQInteractiveShell
43 from IPython.zmq.ipkernel import (
45 from IPython.zmq.ipkernel import (
44 flags as ipkernel_flags,
46 flags as ipkernel_flags,
45 aliases as ipkernel_aliases,
47 aliases as ipkernel_aliases,
46 IPKernelApp
48 IPKernelApp
47 )
49 )
48 from IPython.utils.traitlets import Dict, Unicode, Int, Any, List, Enum
50 from IPython.utils.traitlets import Dict, Unicode, Int, Any, List, Enum
49
51
50 #-----------------------------------------------------------------------------
52 #-----------------------------------------------------------------------------
51 # Module globals
53 # Module globals
52 #-----------------------------------------------------------------------------
54 #-----------------------------------------------------------------------------
53
55
54 _kernel_id_regex = r"(?P<kernel_id>\w+-\w+-\w+-\w+-\w+)"
56 _kernel_id_regex = r"(?P<kernel_id>\w+-\w+-\w+-\w+-\w+)"
55 _kernel_action_regex = r"(?P<action>restart|interrupt)"
57 _kernel_action_regex = r"(?P<action>restart|interrupt)"
58 _notebook_id_regex = r"(?P<notebook_id>\w+-\w+-\w+-\w+-\w+)"
56
59
57 LOCALHOST = '127.0.0.1'
60 LOCALHOST = '127.0.0.1'
58
61
59 #-----------------------------------------------------------------------------
62 #-----------------------------------------------------------------------------
60 # The Tornado web application
63 # The Tornado web application
61 #-----------------------------------------------------------------------------
64 #-----------------------------------------------------------------------------
62
65
63 class NotebookWebApplication(web.Application):
66 class NotebookWebApplication(web.Application):
64
67
65 def __init__(self, kernel_manager, log, kernel_argv, config):
68 def __init__(self, kernel_manager, log, kernel_argv, config):
66 handlers = [
69 handlers = [
67 (r"/", MainHandler),
70 (r"/", MainHandler),
71 (r"/%s" % _notebook_id_regex, NamedNotebookHandler),
68 (r"/kernels", KernelHandler),
72 (r"/kernels", KernelHandler),
69 (r"/kernels/%s/%s" % (_kernel_id_regex, _kernel_action_regex), KernelActionHandler),
73 (r"/kernels/%s/%s" % (_kernel_id_regex, _kernel_action_regex), KernelActionHandler),
70 (r"/kernels/%s/iopub" % _kernel_id_regex, ZMQStreamHandler, dict(stream_name='iopub')),
74 (r"/kernels/%s/iopub" % _kernel_id_regex, ZMQStreamHandler, dict(stream_name='iopub')),
71 (r"/kernels/%s/shell" % _kernel_id_regex, ZMQStreamHandler, dict(stream_name='shell')),
75 (r"/kernels/%s/shell" % _kernel_id_regex, ZMQStreamHandler, dict(stream_name='shell')),
72 (r"/notebooks", NotebookRootHandler),
76 (r"/notebooks", NotebookRootHandler),
73 (r"/notebooks/([^/]+)", NotebookHandler)
77 (r"/notebooks/%s" % _notebook_id_regex, NotebookHandler)
74 ]
78 ]
75 settings = dict(
79 settings = dict(
76 template_path=os.path.join(os.path.dirname(__file__), "templates"),
80 template_path=os.path.join(os.path.dirname(__file__), "templates"),
77 static_path=os.path.join(os.path.dirname(__file__), "static"),
81 static_path=os.path.join(os.path.dirname(__file__), "static"),
78 )
82 )
79 web.Application.__init__(self, handlers, **settings)
83 web.Application.__init__(self, handlers, **settings)
80
84
81 self.kernel_manager = kernel_manager
85 self.kernel_manager = kernel_manager
82 self.log = log
86 self.log = log
83 self.kernel_argv = kernel_argv
87 self.kernel_argv = kernel_argv
84 self.config = config
88 self.config = config
85 self._routers = {}
89 self._routers = {}
86 self._session_dict = {}
90 self._session_dict = {}
91 self.notebook_manager = NotebookManager(config=self.config)
87
92
88 #-------------------------------------------------------------------------
93 #-------------------------------------------------------------------------
89 # Methods for managing kernels and sessions
94 # Methods for managing kernels and sessions
90 #-------------------------------------------------------------------------
95 #-------------------------------------------------------------------------
91
96
92 @property
97 @property
93 def kernel_ids(self):
98 def kernel_ids(self):
94 return self.kernel_manager.kernel_ids
99 return self.kernel_manager.kernel_ids
95
100
96 def start_kernel(self):
101 def start_kernel(self):
97 kwargs = dict()
102 kwargs = dict()
98 kwargs['extra_arguments'] = self.kernel_argv
103 kwargs['extra_arguments'] = self.kernel_argv
99 kernel_id = self.kernel_manager.start_kernel(**kwargs)
104 kernel_id = self.kernel_manager.start_kernel(**kwargs)
100 self.log.info("Kernel started: %s" % kernel_id)
105 self.log.info("Kernel started: %s" % kernel_id)
101 self.log.debug("Kernel args: %r" % kwargs)
106 self.log.debug("Kernel args: %r" % kwargs)
102 self.start_session_manager(kernel_id)
107 self.start_session_manager(kernel_id)
103 return kernel_id
108 return kernel_id
104
109
105 def start_session_manager(self, kernel_id):
110 def start_session_manager(self, kernel_id):
106 sm = self.kernel_manager.create_session_manager(kernel_id)
111 sm = self.kernel_manager.create_session_manager(kernel_id)
107 self._session_dict[kernel_id] = sm
112 self._session_dict[kernel_id] = sm
108 iopub_stream = sm.get_iopub_stream()
113 iopub_stream = sm.get_iopub_stream()
109 shell_stream = sm.get_shell_stream()
114 shell_stream = sm.get_shell_stream()
110 iopub_router = IOPubStreamRouter(
115 iopub_router = IOPubStreamRouter(
111 zmq_stream=iopub_stream, session=sm.session, config=self.config
116 zmq_stream=iopub_stream, session=sm.session, config=self.config
112 )
117 )
113 shell_router = ShellStreamRouter(
118 shell_router = ShellStreamRouter(
114 zmq_stream=shell_stream, session=sm.session, config=self.config
119 zmq_stream=shell_stream, session=sm.session, config=self.config
115 )
120 )
116 self._routers[(kernel_id, 'iopub')] = iopub_router
121 self._routers[(kernel_id, 'iopub')] = iopub_router
117 self._routers[(kernel_id, 'shell')] = shell_router
122 self._routers[(kernel_id, 'shell')] = shell_router
118
123
119 def kill_kernel(self, kernel_id):
124 def kill_kernel(self, kernel_id):
120 sm = self._session_dict.pop(kernel_id)
125 sm = self._session_dict.pop(kernel_id)
121 sm.stop()
126 sm.stop()
122 self.kernel_manager.kill_kernel(kernel_id)
127 self.kernel_manager.kill_kernel(kernel_id)
123 self.log.info("Kernel killed: %s" % kernel_id)
128 self.log.info("Kernel killed: %s" % kernel_id)
124
129
125 def interrupt_kernel(self, kernel_id):
130 def interrupt_kernel(self, kernel_id):
126 self.kernel_manager.interrupt_kernel(kernel_id)
131 self.kernel_manager.interrupt_kernel(kernel_id)
127 self.log.debug("Kernel interrupted: %s" % kernel_id)
132 self.log.debug("Kernel interrupted: %s" % kernel_id)
128
133
129 def restart_kernel(self, kernel_id):
134 def restart_kernel(self, kernel_id):
130 # Create the new kernel first so we can move the clients over.
135 # Create the new kernel first so we can move the clients over.
131 new_kernel_id = self.start_kernel()
136 new_kernel_id = self.start_kernel()
132
137
133 # Copy the clients over to the new routers.
138 # Copy the clients over to the new routers.
134 old_iopub_router = self.get_router(kernel_id, 'iopub')
139 old_iopub_router = self.get_router(kernel_id, 'iopub')
135 old_shell_router = self.get_router(kernel_id, 'shell')
140 old_shell_router = self.get_router(kernel_id, 'shell')
136 new_iopub_router = self.get_router(new_kernel_id, 'iopub')
141 new_iopub_router = self.get_router(new_kernel_id, 'iopub')
137 new_shell_router = self.get_router(new_kernel_id, 'shell')
142 new_shell_router = self.get_router(new_kernel_id, 'shell')
138 new_iopub_router.copy_clients(old_iopub_router)
143 new_iopub_router.copy_clients(old_iopub_router)
139 new_shell_router.copy_clients(old_shell_router)
144 new_shell_router.copy_clients(old_shell_router)
140
145
141 # Now shutdown the old session and the kernel.
146 # Now shutdown the old session and the kernel.
142 # TODO: This causes a hard crash in ZMQStream.close, which sets
147 # TODO: This causes a hard crash in ZMQStream.close, which sets
143 # self.socket to None to hastily. We will need to fix this in PyZMQ
148 # self.socket to None to hastily. We will need to fix this in PyZMQ
144 # itself. For now, we just leave the old kernel running :(
149 # itself. For now, we just leave the old kernel running :(
145 # Maybe this is fixed now, but nothing was changed really.
150 # Maybe this is fixed now, but nothing was changed really.
146 self.kill_kernel(kernel_id)
151 self.kill_kernel(kernel_id)
147
152
148 self.log.debug("Kernel restarted: %s -> %s" % (kernel_id, new_kernel_id))
153 self.log.debug("Kernel restarted: %s -> %s" % (kernel_id, new_kernel_id))
149 return new_kernel_id
154 return new_kernel_id
150
155
151 def get_router(self, kernel_id, stream_name):
156 def get_router(self, kernel_id, stream_name):
152 router = self._routers[(kernel_id, stream_name)]
157 router = self._routers[(kernel_id, stream_name)]
153 return router
158 return router
154
159
155
160
156
161
157 #-----------------------------------------------------------------------------
162 #-----------------------------------------------------------------------------
158 # Aliases and Flags
163 # Aliases and Flags
159 #-----------------------------------------------------------------------------
164 #-----------------------------------------------------------------------------
160
165
161 flags = dict(ipkernel_flags)
166 flags = dict(ipkernel_flags)
162
167
163 # the flags that are specific to the frontend
168 # the flags that are specific to the frontend
164 # these must be scrubbed before being passed to the kernel,
169 # these must be scrubbed before being passed to the kernel,
165 # or it will raise an error on unrecognized flags
170 # or it will raise an error on unrecognized flags
166 notebook_flags = []
171 notebook_flags = []
167
172
168 aliases = dict(ipkernel_aliases)
173 aliases = dict(ipkernel_aliases)
169
174
170 aliases.update(dict(
175 aliases.update(dict(
171 ip = 'IPythonNotebookApp.ip',
176 ip = 'IPythonNotebookApp.ip',
172 port = 'IPythonNotebookApp.port',
177 port = 'IPythonNotebookApp.port',
173 colors = 'ZMQInteractiveShell.colors',
178 colors = 'ZMQInteractiveShell.colors',
174 editor = 'RichIPythonWidget.editor',
179 editor = 'RichIPythonWidget.editor',
175 ))
180 ))
176
181
177 #-----------------------------------------------------------------------------
182 #-----------------------------------------------------------------------------
178 # IPythonNotebookApp
183 # IPythonNotebookApp
179 #-----------------------------------------------------------------------------
184 #-----------------------------------------------------------------------------
180
185
181 class IPythonNotebookApp(BaseIPythonApplication):
186 class IPythonNotebookApp(BaseIPythonApplication):
182 name = 'ipython-notebook'
187 name = 'ipython-notebook'
183 default_config_file_name='ipython_notebook_config.py'
188 default_config_file_name='ipython_notebook_config.py'
184
189
185 description = """
190 description = """
186 The IPython HTML Notebook.
191 The IPython HTML Notebook.
187
192
188 This launches a Tornado based HTML Notebook Server that serves up an
193 This launches a Tornado based HTML Notebook Server that serves up an
189 HTML5/Javascript Notebook client.
194 HTML5/Javascript Notebook client.
190 """
195 """
191
196
192 classes = [IPKernelApp, ZMQInteractiveShell, ProfileDir, Session,
197 classes = [IPKernelApp, ZMQInteractiveShell, ProfileDir, Session,
193 KernelManager, SessionManager, RichIPythonWidget]
198 KernelManager, SessionManager, RichIPythonWidget]
194 flags = Dict(flags)
199 flags = Dict(flags)
195 aliases = Dict(aliases)
200 aliases = Dict(aliases)
196
201
197 kernel_argv = List(Unicode)
202 kernel_argv = List(Unicode)
198
203
199 log_level = Enum((0,10,20,30,40,50,'DEBUG','INFO','WARN','ERROR','CRITICAL'),
204 log_level = Enum((0,10,20,30,40,50,'DEBUG','INFO','WARN','ERROR','CRITICAL'),
200 default_value=logging.INFO,
205 default_value=logging.INFO,
201 config=True,
206 config=True,
202 help="Set the log level by value or name.")
207 help="Set the log level by value or name.")
203
208
204 # connection info:
209 # connection info:
205 ip = Unicode(LOCALHOST, config=True,
210 ip = Unicode(LOCALHOST, config=True,
206 help="The IP address the notebook server will listen on."
211 help="The IP address the notebook server will listen on."
207 )
212 )
208
213
209 port = Int(8888, config=True,
214 port = Int(8888, config=True,
210 help="The port the notebook server will listen on."
215 help="The port the notebook server will listen on."
211 )
216 )
212
217
213 # the factory for creating a widget
218 # the factory for creating a widget
214 widget_factory = Any(RichIPythonWidget)
219 widget_factory = Any(RichIPythonWidget)
215
220
216 def parse_command_line(self, argv=None):
221 def parse_command_line(self, argv=None):
217 super(IPythonNotebookApp, self).parse_command_line(argv)
222 super(IPythonNotebookApp, self).parse_command_line(argv)
218 if argv is None:
223 if argv is None:
219 argv = sys.argv[1:]
224 argv = sys.argv[1:]
220
225
221 self.kernel_argv = list(argv) # copy
226 self.kernel_argv = list(argv) # copy
222 # kernel should inherit default config file from frontend
227 # kernel should inherit default config file from frontend
223 self.kernel_argv.append("--KernelApp.parent_appname='%s'"%self.name)
228 self.kernel_argv.append("--KernelApp.parent_appname='%s'"%self.name)
224 # scrub frontend-specific flags
229 # scrub frontend-specific flags
225 for a in argv:
230 for a in argv:
226 if a.startswith('-') and a.lstrip('-') in notebook_flags:
231 if a.startswith('-') and a.lstrip('-') in notebook_flags:
227 self.kernel_argv.remove(a)
232 self.kernel_argv.remove(a)
228
233
229 def init_kernel_manager(self):
234 def init_kernel_manager(self):
230 # Don't let Qt or ZMQ swallow KeyboardInterupts.
235 # Don't let Qt or ZMQ swallow KeyboardInterupts.
231 signal.signal(signal.SIGINT, signal.SIG_DFL)
236 signal.signal(signal.SIGINT, signal.SIG_DFL)
232
237
233 # Create a KernelManager and start a kernel.
238 # Create a KernelManager and start a kernel.
234 self.kernel_manager = KernelManager(config=self.config, log=self.log)
239 self.kernel_manager = KernelManager(config=self.config, log=self.log)
235
240
236 def init_logging(self):
241 def init_logging(self):
237 super(IPythonNotebookApp, self).init_logging()
242 super(IPythonNotebookApp, self).init_logging()
238 # This prevents double log messages because tornado use a root logger that
243 # This prevents double log messages because tornado use a root logger that
239 # self.log is a child of. The logging module dipatches log messages to a log
244 # self.log is a child of. The logging module dipatches log messages to a log
240 # and all of its ancenstors until propagate is set to False.
245 # and all of its ancenstors until propagate is set to False.
241 self.log.propagate = False
246 self.log.propagate = False
242
247
243 def initialize(self, argv=None):
248 def initialize(self, argv=None):
244 super(IPythonNotebookApp, self).initialize(argv)
249 super(IPythonNotebookApp, self).initialize(argv)
245 self.init_kernel_manager()
250 self.init_kernel_manager()
246 self.web_app = NotebookWebApplication(
251 self.web_app = NotebookWebApplication(
247 self.kernel_manager, self.log, self.kernel_argv, self.config
252 self.kernel_manager, self.log, self.kernel_argv, self.config
248 )
253 )
249 self.http_server = httpserver.HTTPServer(self.web_app)
254 self.http_server = httpserver.HTTPServer(self.web_app)
250 self.http_server.listen(self.port)
255 self.http_server.listen(self.port)
251
256
252 def start(self):
257 def start(self):
253 self.log.info("The IPython Notebook is running at: http://%s:%i" % (self.ip, self.port))
258 self.log.info("The IPython Notebook is running at: http://%s:%i" % (self.ip, self.port))
254 ioloop.IOLoop.instance().start()
259 ioloop.IOLoop.instance().start()
255
260
256 #-----------------------------------------------------------------------------
261 #-----------------------------------------------------------------------------
257 # Main entry point
262 # Main entry point
258 #-----------------------------------------------------------------------------
263 #-----------------------------------------------------------------------------
259
264
260 def launch_new_instance():
265 def launch_new_instance():
261 app = IPythonNotebookApp()
266 app = IPythonNotebookApp()
262 app.initialize()
267 app.initialize()
263 app.start()
268 app.start()
264
269
@@ -1,118 +1,118 b''
1 """Routers that connect WebSockets to ZMQ sockets."""
1 """Routers that connect WebSockets to ZMQ sockets."""
2
2
3 #-----------------------------------------------------------------------------
3 #-----------------------------------------------------------------------------
4 # Copyright (C) 2011 The IPython Development Team
4 # Copyright (C) 2011 The IPython Development Team
5 #
5 #
6 # Distributed under the terms of the BSD License. The full license is in
6 # Distributed under the terms of the BSD License. The full license is in
7 # the file COPYING.txt, distributed as part of this software.
7 # the file COPYING.txt, distributed as part of this software.
8 #-----------------------------------------------------------------------------
8 #-----------------------------------------------------------------------------
9
9
10 #-----------------------------------------------------------------------------
10 #-----------------------------------------------------------------------------
11 # Imports
11 # Imports
12 #-----------------------------------------------------------------------------
12 #-----------------------------------------------------------------------------
13
13
14 import uuid
14 import uuid
15 from Queue import Queue
15 from Queue import Queue
16 import json
16 import json
17
17
18 from IPython.config.configurable import Configurable
18 from IPython.config.configurable import Configurable
19 from IPython.utils.traitlets import Instance, Int, Dict
19 from IPython.utils.traitlets import Instance, Int, Dict
20
20
21 #-----------------------------------------------------------------------------
21 #-----------------------------------------------------------------------------
22 # Classes
22 # Classes
23 #-----------------------------------------------------------------------------
23 #-----------------------------------------------------------------------------
24
24
25 class ZMQStreamRouter(Configurable):
25 class ZMQStreamRouter(Configurable):
26
26
27 zmq_stream = Instance('zmq.eventloop.zmqstream.ZMQStream')
27 zmq_stream = Instance('zmq.eventloop.zmqstream.ZMQStream')
28 session = Instance('IPython.zmq.session.Session')
28 session = Instance('IPython.zmq.session.Session')
29 max_msg_size = Int(2048, config=True, help="""
29 max_msg_size = Int(2048, config=True, help="""
30 The max raw message size accepted from the browser
30 The max raw message size accepted from the browser
31 over a WebSocket connection.
31 over a WebSocket connection.
32 """)
32 """)
33
33
34 _clients = Dict()
34 _clients = Dict()
35
35
36 def __init__(self, **kwargs):
36 def __init__(self, **kwargs):
37 super(ZMQStreamRouter,self).__init__(**kwargs)
37 super(ZMQStreamRouter,self).__init__(**kwargs)
38 self.zmq_stream.on_recv(self._on_zmq_reply)
38 self.zmq_stream.on_recv(self._on_zmq_reply)
39
39
40 def register_client(self, client):
40 def register_client(self, client):
41 """Register a client, returning a client uuid."""
41 """Register a client, returning a client uuid."""
42 client_id = uuid.uuid4()
42 client_id = uuid.uuid4()
43 self._clients[client_id] = client
43 self._clients[client_id] = client
44 return client_id
44 return client_id
45
45
46 def unregister_client(self, client_id):
46 def unregister_client(self, client_id):
47 """Unregister a client by its client uuid."""
47 """Unregister a client by its client uuid."""
48 del self._clients[client_id]
48 del self._clients[client_id]
49
49
50 def copy_clients(self, router):
50 def copy_clients(self, router):
51 """Copy the clients of another router to this one.
51 """Copy the clients of another router to this one.
52
52
53 This is used to enable the backend zeromq stream to disconnect
53 This is used to enable the backend zeromq stream to disconnect
54 and reconnect while the WebSocket connections to browsers
54 and reconnect while the WebSocket connections to browsers
55 remain, such as when a kernel is restarted.
55 remain, such as when a kernel is restarted.
56 """
56 """
57 for client_id, client in router._clients.items():
57 for client_id, client in router._clients.items():
58 client.router = self
58 client.router = self
59 self._clients[client_id] = client
59 self._clients[client_id] = client
60
60
61 def forward_msg(self, client_id, msg):
61 def forward_msg(self, client_id, msg):
62 """Forward a msg to a client by its id.
62 """Forward a msg to a client by its id.
63
63
64 The default implementation of this will fail silently if a message
64 The default implementation of this will fail silently if a message
65 arrives on a socket that doesn't support it. This method should
65 arrives on a socket that doesn't support it. This method should
66 use max_msg_size to check and silently discard message that are too
66 use max_msg_size to check and silently discard message that are too
67 long."""
67 long."""
68 pass
68 pass
69
69
70 def _on_zmq_reply(self, msg_list):
70 def _on_zmq_reply(self, msg_list):
71 """Handle a message the ZMQ stream sends to the router.
71 """Handle a message the ZMQ stream sends to the router.
72
72
73 Usually, this is where the return message will be written to
73 Usually, this is where the return message will be written to
74 clients that need it using client.write_message().
74 clients that need it using client.write_message().
75 """
75 """
76 pass
76 pass
77
77
78 def _reserialize_reply(self, msg_list):
78 def _reserialize_reply(self, msg_list):
79 """Reserialize a reply message using JSON.
79 """Reserialize a reply message using JSON.
80
80
81 This takes the msg list from the ZMQ socket, unserializes it using
81 This takes the msg list from the ZMQ socket, unserializes it using
82 self.session and then serializes the result using JSON. This method
82 self.session and then serializes the result using JSON. This method
83 should be used by self._on_zmq_reply to build messages that can
83 should be used by self._on_zmq_reply to build messages that can
84 be sent back to the browser.
84 be sent back to the browser.
85 """
85 """
86 idents, msg_list = self.session.feed_identities(msg_list)
86 idents, msg_list = self.session.feed_identities(msg_list)
87 msg = self.session.unserialize(msg_list)
87 msg = self.session.unserialize(msg_list)
88 msg['header'].pop('date')
88 msg['header'].pop('date')
89 msg.pop('buffers')
89 msg.pop('buffers')
90 return json.dumps(msg)
90 return json.dumps(msg)
91
91
92
92
93 class IOPubStreamRouter(ZMQStreamRouter):
93 class IOPubStreamRouter(ZMQStreamRouter):
94
94
95 def _on_zmq_reply(self, msg_list):
95 def _on_zmq_reply(self, msg_list):
96 msg = self._reserialize_reply(msg_list)
96 msg = self._reserialize_reply(msg_list)
97 for client_id, client in self._clients.items():
97 for client_id, client in self._clients.items():
98 client.write_message(msg)
98 client.write_message(msg)
99
99
100
100
101 class ShellStreamRouter(ZMQStreamRouter):
101 class ShellStreamRouter(ZMQStreamRouter):
102
102
103 _request_queue = Instance(Queue,(),{})
103 _request_queue = Instance(Queue,(),{})
104
104
105 def _on_zmq_reply(self, msg_list):
105 def _on_zmq_reply(self, msg_list):
106 msg = self._reserialize_reply(msg_list)
106 msg = self._reserialize_reply(msg_list)
107 client_id = self._request_queue.get(block=False)
107 client_id = self._request_queue.get(block=False)
108 client = self._clients.get(client_id)
108 client = self._clients.get(client_id)
109 if client is not None:
109 if client is not None:
110 client.write_message(msg)
110 client.write_message(msg)
111
111
112 def forward_msg(self, client_id, msg):
112 def forward_msg(self, client_id, msg):
113 if len(msg) < self.max_msg_size:
113 if len(msg) < self.max_msg_size:
114 msg = json.loads(msg)
114 msg = json.loads(msg)
115 to_send = self.session.serialize(msg)
115 # to_send = self.session.serialize(msg)
116 self._request_queue.put(client_id)
116 self._request_queue.put(client_id)
117 self.session.send(self.zmq_stream, msg)
117 self.session.send(self.zmq_stream, msg)
118
118
@@ -1,378 +1,385 b''
1 /**
1 /**
2 * HTML5 ✰ Boilerplate
2 * HTML5 ✰ Boilerplate
3 *
3 *
4 * style.css contains a reset, font normalization and some base styles.
4 * style.css contains a reset, font normalization and some base styles.
5 *
5 *
6 * Credit is left where credit is due.
6 * Credit is left where credit is due.
7 * Much inspiration was taken from these projects:
7 * Much inspiration was taken from these projects:
8 * - yui.yahooapis.com/2.8.1/build/base/base.css
8 * - yui.yahooapis.com/2.8.1/build/base/base.css
9 * - camendesign.com/design/
9 * - camendesign.com/design/
10 * - praegnanz.de/weblog/htmlcssjs-kickstart
10 * - praegnanz.de/weblog/htmlcssjs-kickstart
11 */
11 */
12
12
13
13
14 /**
14 /**
15 * html5doctor.com Reset Stylesheet (Eric Meyer's Reset Reloaded + HTML5 baseline)
15 * html5doctor.com Reset Stylesheet (Eric Meyer's Reset Reloaded + HTML5 baseline)
16 * v1.6.1 2010-09-17 | Authors: Eric Meyer & Richard Clark
16 * v1.6.1 2010-09-17 | Authors: Eric Meyer & Richard Clark
17 * html5doctor.com/html-5-reset-stylesheet/
17 * html5doctor.com/html-5-reset-stylesheet/
18 */
18 */
19
19
20 html, body, div, span, object, iframe,
20 html, body, div, span, object, iframe,
21 h1, h2, h3, h4, h5, h6, p, blockquote, pre,
21 h1, h2, h3, h4, h5, h6, p, blockquote, pre,
22 abbr, address, cite, code, del, dfn, em, img, ins, kbd, q, samp,
22 abbr, address, cite, code, del, dfn, em, img, ins, kbd, q, samp,
23 small, strong, sub, sup, var, b, i, dl, dt, dd, ol, ul, li,
23 small, strong, sub, sup, var, b, i, dl, dt, dd, ol, ul, li,
24 fieldset, form, label, legend,
24 fieldset, form, label, legend,
25 table, caption, tbody, tfoot, thead, tr, th, td,
25 table, caption, tbody, tfoot, thead, tr, th, td,
26 article, aside, canvas, details, figcaption, figure,
26 article, aside, canvas, details, figcaption, figure,
27 footer, header, hgroup, menu, nav, section, summary,
27 footer, header, hgroup, menu, nav, section, summary,
28 time, mark, audio, video {
28 time, mark, audio, video {
29 margin: 0;
29 margin: 0;
30 padding: 0;
30 padding: 0;
31 border: 0;
31 border: 0;
32 font-size: 100%;
32 font-size: 100%;
33 font: inherit;
33 font: inherit;
34 vertical-align: baseline;
34 vertical-align: baseline;
35 }
35 }
36
36
37 article, aside, details, figcaption, figure,
37 article, aside, details, figcaption, figure,
38 footer, header, hgroup, menu, nav, section {
38 footer, header, hgroup, menu, nav, section {
39 display: block;
39 display: block;
40 }
40 }
41
41
42 blockquote, q { quotes: none; }
42 blockquote, q { quotes: none; }
43
43
44 blockquote:before, blockquote:after,
44 blockquote:before, blockquote:after,
45 q:before, q:after { content: ""; content: none; }
45 q:before, q:after { content: ""; content: none; }
46
46
47 ins { background-color: #ff9; color: #000; text-decoration: none; }
47 ins { background-color: #ff9; color: #000; text-decoration: none; }
48
48
49 mark { background-color: #ff9; color: #000; font-style: italic; font-weight: bold; }
49 mark { background-color: #ff9; color: #000; font-style: italic; font-weight: bold; }
50
50
51 del { text-decoration: line-through; }
51 del { text-decoration: line-through; }
52
52
53 abbr[title], dfn[title] { border-bottom: 1px dotted; cursor: help; }
53 abbr[title], dfn[title] { border-bottom: 1px dotted; cursor: help; }
54
54
55 table { border-collapse: collapse; border-spacing: 0; }
55 table { border-collapse: collapse; border-spacing: 0; }
56
56
57 hr { display: block; height: 1px; border: 0; border-top: 1px solid #ccc; margin: 1em 0; padding: 0; }
57 hr { display: block; height: 1px; border: 0; border-top: 1px solid #ccc; margin: 1em 0; padding: 0; }
58
58
59 input, select { vertical-align: middle; }
59 input, select { vertical-align: middle; }
60
60
61
61
62 /**
62 /**
63 * Font normalization inspired by YUI Library's fonts.css: developer.yahoo.com/yui/
63 * Font normalization inspired by YUI Library's fonts.css: developer.yahoo.com/yui/
64 */
64 */
65
65
66 body { font:13px/1.231 sans-serif; *font-size:small; } /* Hack retained to preserve specificity */
66 body { font:13px/1.231 sans-serif; *font-size:small; } /* Hack retained to preserve specificity */
67 select, input, textarea, button { font:99% sans-serif; }
67 select, input, textarea, button { font:99% sans-serif; }
68
68
69 /* Normalize monospace sizing:
69 /* Normalize monospace sizing:
70 en.wikipedia.org/wiki/MediaWiki_talk:Common.css/Archive_11#Teletype_style_fix_for_Chrome */
70 en.wikipedia.org/wiki/MediaWiki_talk:Common.css/Archive_11#Teletype_style_fix_for_Chrome */
71 pre, code, kbd, samp { font-family: monospace, sans-serif; }
71 pre, code, kbd, samp { font-family: monospace, sans-serif; }
72
72
73
73
74 /**
74 /**
75 * Primary styles
75 * Primary styles
76 *
76 *
77 * Author: IPython Development Team
77 * Author: IPython Development Team
78 */
78 */
79
79
80
80
81 body {
81 body {
82 background-color: white;
82 background-color: white;
83 /* This makes sure that the body covers the entire window and needs to
83 /* This makes sure that the body covers the entire window and needs to
84 be in a different element than the display: box in wrapper below */
84 be in a different element than the display: box in wrapper below */
85 position: absolute;
85 position: absolute;
86 left: 0px;
86 left: 0px;
87 right: 0px;
87 right: 0px;
88 top: 0px;
88 top: 0px;
89 bottom: 0px;
89 bottom: 0px;
90 overflow: hidden;
90 overflow: hidden;
91 }
91 }
92
92
93
93
94 div#header {
94 div#header {
95 /* Initially hidden to prevent FLOUC */
95 /* Initially hidden to prevent FLOUC */
96 display: none;
96 display: none;
97 position: relative;
97 position: relative;
98 height: 45px;
98 height: 45px;
99 padding: 5px;
99 padding: 5px;
100 margin: 0px;
100 margin: 0px;
101 width: 100%;
101 width: 100%;
102 }
102 }
103
103
104 span#ipython_notebook {
104 span#ipython_notebook {
105 position: absolute;
105 position: absolute;
106 padding: 2px;
106 padding: 2px;
107 }
107 }
108
108
109 span#ipython_notebook h1 {
109 span#ipython_notebook h1 {
110 font-family: Verdana, "Helvetica Neue", Arial, Helvetica, Geneva, sans-serif;
110 font-family: Verdana, "Helvetica Neue", Arial, Helvetica, Geneva, sans-serif;
111 font-size: 197%;
111 font-size: 197%;
112 display: inline;
112 display: inline;
113 }
113 }
114
114
115 span#save_widget {
115 span#save_widget {
116 position: absolute;
116 position: absolute;
117 left: 0px;
117 left: 0px;
118 padding: 5px 0px;
118 padding: 5px 0px;
119 margin: 0px 0px 0px 0px;
119 margin: 0px 0px 0px 0px;
120 }
120 }
121
121
122 input#notebook_name {
122 input#notebook_name {
123 height: 1em;
123 height: 1em;
124 line-height: 1em;
124 line-height: 1em;
125 padding: 5px;
125 padding: 5px;
126 }
126 }
127
127
128 span#kernel_status {
128 span#kernel_status {
129 position: absolute;
129 position: absolute;
130 padding: 8px 5px 5px 5px;
130 padding: 8px 5px 5px 5px;
131 right: 10px;
131 right: 10px;
132 font-weight: bold;
132 font-weight: bold;
133 }
133 }
134
134
135 .status_idle {
135 .status_idle {
136 color: gray;
136 color: gray;
137 }
137 }
138
138
139 .status_busy {
139 .status_busy {
140 color: red;
140 color: red;
141 }
141 }
142
142
143 .status_restarting {
143 .status_restarting {
144 color: black;
144 color: black;
145 }
145 }
146
146
147 div#notebook_app {
147 div#notebook_app {
148 /* Initially hidden to prevent FLOUC */
148 /* Initially hidden to prevent FLOUC */
149 display: none;
149 display: none;
150 width: 100%;
150 width: 100%;
151 position: relative;
151 position: relative;
152 }
152 }
153
153
154 div#left_panel {
154 div#left_panel {
155 overflow-y: auto;
155 overflow-y: auto;
156 top: 0px;
156 top: 0px;
157 left: 0px;
157 left: 0px;
158 margin: 0px;
158 margin: 0px;
159 padding: 0px;
159 padding: 0px;
160 position: absolute;
160 position: absolute;
161 }
161 }
162
162
163 h3.section_header {
163 h3.section_header {
164 padding: 5px;
164 padding: 5px;
165 }
165 }
166
166
167 div.section_content {
167 div.section_content {
168 padding: 5px;
168 padding: 5px;
169 }
169 }
170
170
171
171
172 span.section_row_buttons > button {
172 span.section_row_buttons > button {
173 width: 60px;
173 width: 60px;
174 }
174 }
175
175
176 .section_row {
176 .section_row {
177 margin: 5px 0px;
177 margin: 5px 0px;
178 }
178 }
179
179
180 .section_row_buttons {
180 .section_row_buttons {
181 float: right;
181 float: right;
182 }
182 }
183
183
184 .section_row_header {
184 .section_row_header {
185 float: left;
185 float: left;
186 font-size: 85%;
186 font-size: 85%;
187 padding: 0.2em 0em;
187 padding: 0.2em 0em;
188 font-weight: bold;
188 font-weight: bold;
189 }
189 }
190
190
191 span.button_label {
191 span.button_label {
192 padding: 0.2em 1em;
192 padding: 0.2em 1em;
193 font-size: 77%;
193 font-size: 77%;
194 float: right;
194 float: right;
195 }
195 }
196
196
197 /* This is needed because FF was adding a 2px margin top and bottom. */
197 /* This is needed because FF was adding a 2px margin top and bottom. */
198 .section_row .ui-button {
198 .section_row .ui-button {
199 margin-top: 0px;
199 margin-top: 0px;
200 margin-bottom: 0px;
200 margin-bottom: 0px;
201 }
201 }
202
202
203
203
204 .ui-button .ui-button-text {
204 .ui-button .ui-button-text {
205 padding: 0.2em 0.8em;
205 padding: 0.2em 0.8em;
206 font-size: 77%;
206 font-size: 77%;
207 }
207 }
208
208
209 #download_format {
210 float: right;
211 font-size: 85%;
212 width: 60px;
213 margin: 1px 5px;
214 }
215
209 div#left_panel_splitter {
216 div#left_panel_splitter {
210 width: 8px;
217 width: 8px;
211 top: 0px;
218 top: 0px;
212 left: 202px;
219 left: 202px;
213 margin: 0px;
220 margin: 0px;
214 padding: 0px;
221 padding: 0px;
215 position: absolute;
222 position: absolute;
216 }
223 }
217
224
218 div#notebook_panel {
225 div#notebook_panel {
219 /* The L margin will be set in the Javascript code*/
226 /* The L margin will be set in the Javascript code*/
220 margin: 0px 0px 0px 0px;
227 margin: 0px 0px 0px 0px;
221 padding: 0px;
228 padding: 0px;
222 }
229 }
223
230
224 div#notebook {
231 div#notebook {
225 overflow-y: scroll;
232 overflow-y: scroll;
226 overflow-x: auto;
233 overflow-x: auto;
227 width: 100%;
234 width: 100%;
228 padding: 0px 15px 0px 15px;
235 padding: 0px 15px 0px 15px;
229 margin: 0px
236 margin: 0px
230 background-color: white;
237 background-color: white;
231 }
238 }
232
239
233 div#pager_splitter {
240 div#pager_splitter {
234 height: 8px;
241 height: 8px;
235 }
242 }
236
243
237 div#pager {
244 div#pager {
238 padding: 15px;
245 padding: 15px;
239 overflow: auto;
246 overflow: auto;
240 }
247 }
241
248
242 div.cell {
249 div.cell {
243 width: 100%;
250 width: 100%;
244 padding: 5px;
251 padding: 5px;
245 /* This acts as a spacer between cells, that is outside the border */
252 /* This acts as a spacer between cells, that is outside the border */
246 margin: 15px 0px 15px 0px;
253 margin: 15px 0px 15px 0px;
247 }
254 }
248
255
249 div.code_cell {
256 div.code_cell {
250 background-color: white;
257 background-color: white;
251 }
258 }
252
259
253 div.prompt {
260 div.prompt {
254 width: 80px;
261 width: 80px;
255 padding: 0.4em;
262 padding: 0.4em;
256 margin: 0px;
263 margin: 0px;
257 font-family: monospace;
264 font-family: monospace;
258 }
265 }
259
266
260 div.input_prompt {
267 div.input_prompt {
261 color: navy;
268 color: navy;
262 }
269 }
263
270
264 div.output {
271 div.output {
265 /* This is a spacer between the input and output of each cell */
272 /* This is a spacer between the input and output of each cell */
266 margin-top: 15px;
273 margin-top: 15px;
267 }
274 }
268
275
269 div.output_prompt {
276 div.output_prompt {
270 color: darkred;
277 color: darkred;
271 }
278 }
272
279
273 div.output_area {
280 div.output_area {
274 text-align: left;
281 text-align: left;
275 color: black;
282 color: black;
276 font-family: monospace;
283 font-family: monospace;
277 }
284 }
278
285
279 div.output_stream {
286 div.output_stream {
280 padding: 0.4em;
287 padding: 0.4em;
281 }
288 }
282
289
283 div.output_latex {
290 div.output_latex {
284 /* Slightly bigger than the rest of the notebook */
291 /* Slightly bigger than the rest of the notebook */
285 font-size: 116%;
292 font-size: 116%;
286 }
293 }
287
294
288 div.output_html {
295 div.output_html {
289 }
296 }
290
297
291 div.output_png {
298 div.output_png {
292 }
299 }
293
300
294 div.text_cell {
301 div.text_cell {
295 background-color: white;
302 background-color: white;
296 }
303 }
297
304
298 textarea.text_cell_input {
305 textarea.text_cell_input {
299 /* Slightly bigger than the rest of the notebook */
306 /* Slightly bigger than the rest of the notebook */
300 font-size: 116%;
307 font-size: 116%;
301 font-family: monospace;
308 font-family: monospace;
302 outline: none;
309 outline: none;
303 resize: none;
310 resize: none;
304 width: inherit;
311 width: inherit;
305 border-style: none;
312 border-style: none;
306 padding: 0px;
313 padding: 0px;
307 margin: 0px;
314 margin: 0px;
308 color: black;
315 color: black;
309 }
316 }
310
317
311 div.text_cell_render {
318 div.text_cell_render {
312 font-family: "Helvetica Neue", Arial, Helvetica, Geneva, sans-serif;
319 font-family: "Helvetica Neue", Arial, Helvetica, Geneva, sans-serif;
313 /* Slightly bigger than the rest of the notebook */
320 /* Slightly bigger than the rest of the notebook */
314 font-size: 116%;
321 font-size: 116%;
315 outline: none;
322 outline: none;
316 resize: none;
323 resize: none;
317 width: inherit;
324 width: inherit;
318 border-style: none;
325 border-style: none;
319 padding: 5px;
326 padding: 5px;
320 color: black;
327 color: black;
321 }
328 }
322
329
323 div.text_cell_render em {font-style: italic;}
330 div.text_cell_render em {font-style: italic;}
324 div.text_cell_render strong {font-weight: bold;}
331 div.text_cell_render strong {font-weight: bold;}
325 div.text_cell_render u {text-decoration: underline;}
332 div.text_cell_render u {text-decoration: underline;}
326 div.text_cell_render :link { text-decoration: underline }
333 div.text_cell_render :link { text-decoration: underline }
327 div.text_cell_render :visited { text-decoration: underline }
334 div.text_cell_render :visited { text-decoration: underline }
328 div.text_cell_render h1 {font-size: 197%; margin: .67em 0; font-weight: bold;}
335 div.text_cell_render h1 {font-size: 197%; margin: .67em 0; font-weight: bold;}
329 div.text_cell_render h2 {font-size: 153.9%; margin: .75em 0; font-weight: bold;}
336 div.text_cell_render h2 {font-size: 153.9%; margin: .75em 0; font-weight: bold;}
330 div.text_cell_render h3 {font-size: 116%; margin: .83em 0; font-weight: bold;}
337 div.text_cell_render h3 {font-size: 116%; margin: .83em 0; font-weight: bold;}
331 div.text_cell_render h4 {margin: 1.12em 0; font-weight: bold;}
338 div.text_cell_render h4 {margin: 1.12em 0; font-weight: bold;}
332 div.text_cell_render h5 {font-size: 85%.; margin: 1.5em 0; font-weight: bold;}
339 div.text_cell_render h5 {font-size: 85%.; margin: 1.5em 0; font-weight: bold;}
333 div.text_cell_render h6 {font-size: 77%; margin: 1.67em 0; font-weight: bold;}
340 div.text_cell_render h6 {font-size: 77%; margin: 1.67em 0; font-weight: bold;}
334 div.text_cell_render ul {list-style:disc; margin-left: 40px;}
341 div.text_cell_render ul {list-style:disc; margin-left: 40px;}
335 div.text_cell_render ul ul {list-style:square; margin-left: 40px;}
342 div.text_cell_render ul ul {list-style:square; margin-left: 40px;}
336 div.text_cell_render ul ul ul {list-style:circle; margin-left: 40px;}
343 div.text_cell_render ul ul ul {list-style:circle; margin-left: 40px;}
337 div.text_cell_render ol {list-style:upper-roman; margin-left: 40px;}
344 div.text_cell_render ol {list-style:upper-roman; margin-left: 40px;}
338 div.text_cell_render ol ol {list-style:upper-alpha;}
345 div.text_cell_render ol ol {list-style:upper-alpha;}
339 div.text_cell_render ol ol ol {list-style:decimal;}
346 div.text_cell_render ol ol ol {list-style:decimal;}
340 div.text_cell_render ol ol ol ol {list-style:lower-alpha;}
347 div.text_cell_render ol ol ol ol {list-style:lower-alpha;}
341 div.text_cell_render ol ol ol ol ol {list-style:lower-roman;}
348 div.text_cell_render ol ol ol ol ol {list-style:lower-roman;}
342
349
343
350
344 .CodeMirror {
351 .CodeMirror {
345 overflow: hidden; /* Changed from auto to remove scrollbar */
352 overflow: hidden; /* Changed from auto to remove scrollbar */
346 height: auto; /* Changed to auto to autogrow */
353 height: auto; /* Changed to auto to autogrow */
347 line-height: 1.231; /* Changed from 1em to our global default */
354 line-height: 1.231; /* Changed from 1em to our global default */
348 }
355 }
349
356
350 /* CSS font colors for translated ANSI colors. */
357 /* CSS font colors for translated ANSI colors. */
351
358
352
359
353 .ansiblack {color: black;}
360 .ansiblack {color: black;}
354 .ansired {color: darkred;}
361 .ansired {color: darkred;}
355 .ansigreen {color: darkgreen;}
362 .ansigreen {color: darkgreen;}
356 .ansiyellow {color: brown;}
363 .ansiyellow {color: brown;}
357 .ansiblue {color: darkblue;}
364 .ansiblue {color: darkblue;}
358 .ansipurple {color: darkviolet;}
365 .ansipurple {color: darkviolet;}
359 .ansicyan {color: steelblue;}
366 .ansicyan {color: steelblue;}
360 .ansigrey {color: grey;}
367 .ansigrey {color: grey;}
361 .ansibold {font-weight: bold;}
368 .ansibold {font-weight: bold;}
362
369
363 .completions {
370 .completions {
364 position: absolute;
371 position: absolute;
365 z-index: 10;
372 z-index: 10;
366 overflow: auto;
373 overflow: auto;
367 border: 1px solid black;
374 border: 1px solid black;
368 }
375 }
369
376
370 .completions select {
377 .completions select {
371 background: white;
378 background: white;
372 outline: none;
379 outline: none;
373 border: none;
380 border: none;
374 padding: 0px;
381 padding: 0px;
375 margin: 0px;
382 margin: 0px;
376 font-family: monospace;
383 font-family: monospace;
377 }
384 }
378
385
@@ -1,323 +1,333 b''
1
1
2 //============================================================================
2 //============================================================================
3 // CodeCell
3 // CodeCell
4 //============================================================================
4 //============================================================================
5
5
6 var IPython = (function (IPython) {
6 var IPython = (function (IPython) {
7
7
8 var utils = IPython.utils;
8 var utils = IPython.utils;
9
9
10 var CodeCell = function (notebook) {
10 var CodeCell = function (notebook) {
11 this.code_mirror = null;
11 this.code_mirror = null;
12 this.input_prompt_number = ' ';
12 this.input_prompt_number = ' ';
13 this.is_completing = false;
13 this.is_completing = false;
14 this.completion_cursor = null;
14 this.completion_cursor = null;
15 IPython.Cell.apply(this, arguments);
15 IPython.Cell.apply(this, arguments);
16 };
16 };
17
17
18
18
19 CodeCell.prototype = new IPython.Cell();
19 CodeCell.prototype = new IPython.Cell();
20
20
21
21
22 CodeCell.prototype.create_element = function () {
22 CodeCell.prototype.create_element = function () {
23 var cell = $('<div></div>').addClass('cell border-box-sizing code_cell vbox');
23 var cell = $('<div></div>').addClass('cell border-box-sizing code_cell vbox');
24 var input = $('<div></div>').addClass('input hbox');
24 var input = $('<div></div>').addClass('input hbox');
25 input.append($('<div/>').addClass('prompt input_prompt'));
25 input.append($('<div/>').addClass('prompt input_prompt'));
26 var input_area = $('<div/>').addClass('input_area box-flex1');
26 var input_area = $('<div/>').addClass('input_area box-flex1');
27 this.code_mirror = CodeMirror(input_area.get(0), {
27 this.code_mirror = CodeMirror(input_area.get(0), {
28 indentUnit : 4,
28 indentUnit : 4,
29 enterMode : 'flat',
29 enterMode : 'flat',
30 tabMode: 'shift',
30 tabMode: 'shift',
31 onKeyEvent: $.proxy(this.handle_codemirror_keyevent,this)
31 onKeyEvent: $.proxy(this.handle_codemirror_keyevent,this)
32 });
32 });
33 input.append(input_area);
33 input.append(input_area);
34 var output = $('<div></div>').addClass('output vbox');
34 var output = $('<div></div>').addClass('output vbox');
35 cell.append(input).append(output);
35 cell.append(input).append(output);
36 this.element = cell;
36 this.element = cell;
37 this.collapse()
37 this.collapse()
38 };
38 };
39
39
40
40
41 CodeCell.prototype.handle_codemirror_keyevent = function (editor, event) {
41 CodeCell.prototype.handle_codemirror_keyevent = function (editor, event) {
42 // This method gets called in CodeMirror's onKeyDown/onKeyPress handlers and
42 // This method gets called in CodeMirror's onKeyDown/onKeyPress handlers and
43 // is used to provide custom key handling. Its return value is used to determine
43 // is used to provide custom key handling. Its return value is used to determine
44 // if CodeMirror should ignore the event: true = ignore, false = don't ignore.
44 // if CodeMirror should ignore the event: true = ignore, false = don't ignore.
45 if (event.keyCode === 13 && event.shiftKey) {
45 if (event.keyCode === 13 && event.shiftKey) {
46 // Always ignore shift-enter in CodeMirror as we handle it.
46 // Always ignore shift-enter in CodeMirror as we handle it.
47 return true;
47 return true;
48 // } else if (event.keyCode == 32 && (event.ctrlKey || event.metaKey) && !event.altKey) {
48 // } else if (event.keyCode == 32 && (event.ctrlKey || event.metaKey) && !event.altKey) {
49 } else if (event.keyCode == 9) {
49 } else if (event.keyCode == 9) {
50 var cur = editor.getCursor();
50 var cur = editor.getCursor();
51 var pre_cursor = editor.getRange({line:cur.line,ch:0},cur).trim();
51 var pre_cursor = editor.getRange({line:cur.line,ch:0},cur).trim();
52 if (pre_cursor === "") {
52 if (pre_cursor === "") {
53 // Don't autocomplete if the part of the line before the cursor is empty.
53 // Don't autocomplete if the part of the line before the cursor is empty.
54 // In this case, let CodeMirror handle indentation.
54 // In this case, let CodeMirror handle indentation.
55 return false;
55 return false;
56 } else {
56 } else {
57 // Autocomplete the current line.
57 // Autocomplete the current line.
58 event.stop();
58 event.stop();
59 var line = editor.getLine(cur.line);
59 var line = editor.getLine(cur.line);
60 this.is_completing = true;
60 this.is_completing = true;
61 this.completion_cursor = cur;
61 this.completion_cursor = cur;
62 IPython.notebook.complete_cell(this, line, cur.ch);
62 IPython.notebook.complete_cell(this, line, cur.ch);
63 return true;
63 return true;
64 }
64 }
65 } else {
65 } else {
66 if (this.is_completing && this.completion_cursor !== editor.getCursor()) {
66 if (this.is_completing && this.completion_cursor !== editor.getCursor()) {
67 this.is_completing = false;
67 this.is_completing = false;
68 this.completion_cursor = null;
68 this.completion_cursor = null;
69 }
69 }
70 return false;
70 return false;
71 };
71 };
72 };
72 };
73
73
74
74
75 CodeCell.prototype.finish_completing = function (matched_text, matches) {
75 CodeCell.prototype.finish_completing = function (matched_text, matches) {
76 if (!this.is_completing || matches.length === 0) {return;}
76 if (!this.is_completing || matches.length === 0) {return;}
77 // console.log("Got matches", matched_text, matches);
77 // console.log("Got matches", matched_text, matches);
78
78
79 var that = this;
79 var that = this;
80 var cur = this.completion_cursor;
80 var cur = this.completion_cursor;
81 var complete = $('<div/>').addClass('completions');
81 var complete = $('<div/>').addClass('completions');
82 var select = $('<select/>').attr('multiple','true');
82 var select = $('<select/>').attr('multiple','true');
83 for (var i=0; i<matches.length; ++i) {
83 for (var i=0; i<matches.length; ++i) {
84 select.append($('<option/>').text(matches[i]));
84 select.append($('<option/>').text(matches[i]));
85 }
85 }
86 select.children().first().attr('selected','true');
86 select.children().first().attr('selected','true');
87 select.attr('size',Math.min(10,matches.length));
87 select.attr('size',Math.min(10,matches.length));
88 var pos = this.code_mirror.cursorCoords();
88 var pos = this.code_mirror.cursorCoords();
89 complete.css('left',pos.x+'px');
89 complete.css('left',pos.x+'px');
90 complete.css('top',pos.yBot+'px');
90 complete.css('top',pos.yBot+'px');
91 complete.append(select);
91 complete.append(select);
92
92
93 $('body').append(complete);
93 $('body').append(complete);
94 var done = false;
94 var done = false;
95
95
96 var insert = function (selected_text) {
96 var insert = function (selected_text) {
97 that.code_mirror.replaceRange(
97 that.code_mirror.replaceRange(
98 selected_text,
98 selected_text,
99 {line: cur.line, ch: (cur.ch-matched_text.length)},
99 {line: cur.line, ch: (cur.ch-matched_text.length)},
100 {line: cur.line, ch: cur.ch}
100 {line: cur.line, ch: cur.ch}
101 );
101 );
102 };
102 };
103
103
104 var close = function () {
104 var close = function () {
105 if (done) return;
105 if (done) return;
106 done = true;
106 done = true;
107 complete.remove();
107 complete.remove();
108 that.is_completing = false;
108 that.is_completing = false;
109 that.completion_cursor = null;
109 that.completion_cursor = null;
110 };
110 };
111
111
112 var pick = function () {
112 var pick = function () {
113 insert(select.val()[0]);
113 insert(select.val()[0]);
114 close();
114 close();
115 setTimeout(function(){that.code_mirror.focus();}, 50);
115 setTimeout(function(){that.code_mirror.focus();}, 50);
116 };
116 };
117
117
118 select.blur(close);
118 select.blur(close);
119 select.keydown(function (event) {
119 select.keydown(function (event) {
120 var code = event.which;
120 var code = event.which;
121 if (code === 13 || code === 32) {
121 if (code === 13 || code === 32) {
122 // Pressing SPACE or ENTER will cause a pick
122 // Pressing SPACE or ENTER will cause a pick
123 event.stopPropagation();
123 event.stopPropagation();
124 event.preventDefault();
124 event.preventDefault();
125 pick();
125 pick();
126 } else if (code === 38 || code === 40) {
126 } else if (code === 38 || code === 40) {
127 // We don't want the document keydown handler to handle UP/DOWN,
127 // We don't want the document keydown handler to handle UP/DOWN,
128 // but we want the default action.
128 // but we want the default action.
129 event.stopPropagation();
129 event.stopPropagation();
130 } else {
130 } else {
131 // All other key presses simple exit completion.
131 // All other key presses simple exit completion.
132 event.stopPropagation();
132 event.stopPropagation();
133 event.preventDefault();
133 event.preventDefault();
134 close();
134 close();
135 that.code_mirror.focus();
135 that.code_mirror.focus();
136 }
136 }
137 });
137 });
138 // Double click also causes a pick.
138 // Double click also causes a pick.
139 select.dblclick(pick);
139 select.dblclick(pick);
140 select.focus();
140 select.focus();
141 };
141 };
142
142
143
143
144 CodeCell.prototype.select = function () {
144 CodeCell.prototype.select = function () {
145 IPython.Cell.prototype.select.apply(this);
145 IPython.Cell.prototype.select.apply(this);
146 this.code_mirror.focus();
146 this.code_mirror.focus();
147 };
147 };
148
148
149
149
150 CodeCell.prototype.append_pyout = function (data, n) {
150 CodeCell.prototype.append_pyout = function (data, n) {
151 var toinsert = $("<div/>").addClass("output_area output_pyout hbox");
151 var toinsert = $("<div/>").addClass("output_area output_pyout hbox");
152 toinsert.append($('<div/>').
152 toinsert.append($('<div/>').
153 addClass('prompt output_prompt').
153 addClass('prompt output_prompt').
154 html('Out[' + n + ']:')
154 html('Out[' + n + ']:')
155 );
155 );
156 this.append_display_data(data, toinsert);
156 this.append_display_data(data, toinsert);
157 toinsert.children().last().addClass("box_flex1");
157 toinsert.children().last().addClass("box_flex1");
158 this.element.find("div.output").append(toinsert);
158 this.element.find("div.output").append(toinsert);
159 // If we just output latex, typeset it.
159 // If we just output latex, typeset it.
160 if (data["text/latex"] !== undefined) {
160 if (data["text/latex"] !== undefined) {
161 MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
161 MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
162 };
162 };
163 };
163 };
164
164
165
165
166 CodeCell.prototype.append_pyerr = function (ename, evalue, tb) {
166 CodeCell.prototype.append_pyerr = function (ename, evalue, tb) {
167 var s = '';
167 var s = '';
168 var len = tb.length;
168 var len = tb.length;
169 for (var i=0; i<len; i++) {
169 for (var i=0; i<len; i++) {
170 s = s + tb[i] + '\n';
170 s = s + tb[i] + '\n';
171 }
171 }
172 s = s + '\n';
172 s = s + '\n';
173 this.append_stream(s);
173 this.append_stream(s);
174 };
174 };
175
175
176
176
177 CodeCell.prototype.append_display_data = function (data, element) {
177 CodeCell.prototype.append_display_data = function (data, element) {
178 if (data["text/html"] !== undefined) {
178 if (data["text/html"] !== undefined) {
179 this.append_html(data["text/html"], element);
179 this.append_html(data["text/html"], element);
180 } else if (data["text/latex"] !== undefined) {
180 } else if (data["text/latex"] !== undefined) {
181 this.append_latex(data["text/latex"], element);
181 this.append_latex(data["text/latex"], element);
182 // If it is undefined, then we just appended to div.output, which
182 // If it is undefined, then we just appended to div.output, which
183 // makes the latex visible and we can typeset it. The typesetting
183 // makes the latex visible and we can typeset it. The typesetting
184 // has to be done after the latex is on the page.
184 // has to be done after the latex is on the page.
185 if (element === undefined) {
185 if (element === undefined) {
186 MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
186 MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
187 };
187 };
188 } else if (data["image/svg+xml"] !== undefined) {
188 } else if (data["image/svg+xml"] !== undefined) {
189 this.append_svg(data["image/svg+xml"], element);
189 this.append_svg(data["image/svg+xml"], element);
190 } else if (data["image/png"] !== undefined) {
190 } else if (data["image/png"] !== undefined) {
191 this.append_png(data["image/png"], element);
191 this.append_png(data["image/png"], element);
192 } else if (data["text/plain"] !== undefined) {
192 } else if (data["text/plain"] !== undefined) {
193 this.append_stream(data["text/plain"], element);
193 this.append_stream(data["text/plain"], element);
194 };
194 };
195 return element;
195 return element;
196 };
196 };
197
197
198
198
199 CodeCell.prototype.append_html = function (html, element) {
199 CodeCell.prototype.append_html = function (html, element) {
200 element = element || this.element.find("div.output");
200 element = element || this.element.find("div.output");
201 var toinsert = $("<div/>").addClass("output_area output_html");
201 var toinsert = $("<div/>").addClass("output_area output_html");
202 toinsert.append(html);
202 toinsert.append(html);
203 element.append(toinsert);
203 element.append(toinsert);
204 return element;
204 return element;
205 }
205 }
206
206
207
207
208 CodeCell.prototype.append_stream = function (data, element) {
208 CodeCell.prototype.append_stream = function (data, element) {
209 element = element || this.element.find("div.output");
209 element = element || this.element.find("div.output");
210 var toinsert = $("<div/>").addClass("output_area output_stream");
210 var toinsert = $("<div/>").addClass("output_area output_stream");
211 toinsert.append($("<pre/>").html(utils.fixConsole(data)));
211 toinsert.append($("<pre/>").html(utils.fixConsole(data)));
212 element.append(toinsert);
212 element.append(toinsert);
213 return element;
213 return element;
214 };
214 };
215
215
216
216
217 CodeCell.prototype.append_svg = function (svg, element) {
217 CodeCell.prototype.append_svg = function (svg, element) {
218 element = element || this.element.find("div.output");
218 element = element || this.element.find("div.output");
219 var toinsert = $("<div/>").addClass("output_area output_svg");
219 var toinsert = $("<div/>").addClass("output_area output_svg");
220 toinsert.append(svg);
220 toinsert.append(svg);
221 element.append(toinsert);
221 element.append(toinsert);
222 return element;
222 return element;
223 };
223 };
224
224
225
225
226 CodeCell.prototype.append_png = function (png, element) {
226 CodeCell.prototype.append_png = function (png, element) {
227 element = element || this.element.find("div.output");
227 element = element || this.element.find("div.output");
228 var toinsert = $("<div/>").addClass("output_area output_png");
228 var toinsert = $("<div/>").addClass("output_area output_png");
229 toinsert.append($("<img/>").attr('src','data:image/png;base64,'+png));
229 toinsert.append($("<img/>").attr('src','data:image/png;base64,'+png));
230 element.append(toinsert);
230 element.append(toinsert);
231 return element;
231 return element;
232 };
232 };
233
233
234
234
235 CodeCell.prototype.append_latex = function (latex, element) {
235 CodeCell.prototype.append_latex = function (latex, element) {
236 // This method cannot do the typesetting because the latex first has to
236 // This method cannot do the typesetting because the latex first has to
237 // be on the page.
237 // be on the page.
238 element = element || this.element.find("div.output");
238 element = element || this.element.find("div.output");
239 var toinsert = $("<div/>").addClass("output_area output_latex");
239 var toinsert = $("<div/>").addClass("output_area output_latex");
240 toinsert.append(latex);
240 toinsert.append(latex);
241 element.append(toinsert);
241 element.append(toinsert);
242 return element;
242 return element;
243 }
243 }
244
244
245
245
246 CodeCell.prototype.clear_output = function () {
246 CodeCell.prototype.clear_output = function () {
247 this.element.find("div.output").html("");
247 this.element.find("div.output").html("");
248 };
248 };
249
249
250
250
251 CodeCell.prototype.clear_input = function () {
251 CodeCell.prototype.clear_input = function () {
252 this.code_mirror.setValue('');
252 this.code_mirror.setValue('');
253 };
253 };
254
254
255
255
256 CodeCell.prototype.collapse = function () {
256 CodeCell.prototype.collapse = function () {
257 this.element.find('div.output').hide();
257 this.element.find('div.output').hide();
258 };
258 };
259
259
260
260
261 CodeCell.prototype.expand = function () {
261 CodeCell.prototype.expand = function () {
262 this.element.find('div.output').show();
262 this.element.find('div.output').show();
263 };
263 };
264
264
265
265
266 CodeCell.prototype.set_input_prompt = function (number) {
266 CodeCell.prototype.set_input_prompt = function (number) {
267 var n = number || ' ';
267 var n = number || ' ';
268 this.input_prompt_number = n
268 this.input_prompt_number = n
269 this.element.find('div.input_prompt').html('In&nbsp;[' + n + ']:');
269 this.element.find('div.input_prompt').html('In&nbsp;[' + n + ']:');
270 };
270 };
271
271
272
272
273 CodeCell.prototype.get_code = function () {
273 CodeCell.prototype.get_code = function () {
274 return this.code_mirror.getValue();
274 return this.code_mirror.getValue();
275 };
275 };
276
276
277
277
278 CodeCell.prototype.set_code = function (code) {
278 CodeCell.prototype.set_code = function (code) {
279 return this.code_mirror.setValue(code);
279 return this.code_mirror.setValue(code);
280 };
280 };
281
281
282
282
283 CodeCell.prototype.at_top = function () {
283 CodeCell.prototype.at_top = function () {
284 var cursor = this.code_mirror.getCursor();
284 var cursor = this.code_mirror.getCursor();
285 if (cursor.line === 0) {
285 if (cursor.line === 0) {
286 return true;
286 return true;
287 } else {
287 } else {
288 return false;
288 return false;
289 }
289 }
290 };
290 };
291
291
292
292
293 CodeCell.prototype.at_bottom = function () {
293 CodeCell.prototype.at_bottom = function () {
294 var cursor = this.code_mirror.getCursor();
294 var cursor = this.code_mirror.getCursor();
295 if (cursor.line === (this.code_mirror.lineCount()-1)) {
295 if (cursor.line === (this.code_mirror.lineCount()-1)) {
296 return true;
296 return true;
297 } else {
297 } else {
298 return false;
298 return false;
299 }
299 }
300 };
300 };
301
301
302
302
303 CodeCell.prototype.fromJSON = function (data) {
303 CodeCell.prototype.fromJSON = function (data) {
304 if (data.cell_type === 'code') {
304 if (data.cell_type === 'code') {
305 this.set_code(data.code);
305 if (data.input !== undefined) {
306 this.set_input_prompt(data.prompt_number);
306 this.set_code(data.input);
307 }
308 if (data.prompt_number !== undefined) {
309 this.set_input_prompt(data.prompt_number);
310 } else {
311 this.set_input_prompt();
312 };
307 };
313 };
308 };
314 };
309
315
310
316
311 CodeCell.prototype.toJSON = function () {
317 CodeCell.prototype.toJSON = function () {
312 return {
318 var data = {}
313 code : this.get_code(),
319 data.input = this.get_code();
314 cell_type : 'code',
320 data.cell_type = 'code';
315 prompt_number : this.input_prompt_number
321 if (this.input_prompt_number !== ' ') {
322 data.prompt_number = this.input_prompt_number
316 };
323 };
324 data.outputs = [];
325 data.language = 'python';
326 return data;
317 };
327 };
318
328
319 IPython.CodeCell = CodeCell;
329 IPython.CodeCell = CodeCell;
320
330
321 return IPython;
331 return IPython;
322 }(IPython));
332 }(IPython));
323
333
@@ -1,624 +1,606 b''
1
1
2 //============================================================================
2 //============================================================================
3 // Notebook
3 // Notebook
4 //============================================================================
4 //============================================================================
5
5
6 var IPython = (function (IPython) {
6 var IPython = (function (IPython) {
7
7
8 var utils = IPython.utils;
8 var utils = IPython.utils;
9
9
10 var Notebook = function (selector) {
10 var Notebook = function (selector) {
11 this.element = $(selector);
11 this.element = $(selector);
12 this.element.scroll();
12 this.element.scroll();
13 this.element.data("notebook", this);
13 this.element.data("notebook", this);
14 this.next_prompt_number = 1;
14 this.next_prompt_number = 1;
15 this.kernel = null;
15 this.kernel = null;
16 this.msg_cell_map = {};
16 this.msg_cell_map = {};
17 this.filename = null;
18 this.notebook_load_re = /%notebook load/
19 this.notebook_save_re = /%notebook save/
20 this.notebook_filename_re = /(\w)+.ipynb/
21 this.style();
17 this.style();
22 this.create_elements();
18 this.create_elements();
23 this.bind_events();
19 this.bind_events();
24 this.start_kernel();
25 };
20 };
26
21
27
22
28 Notebook.prototype.style = function () {
23 Notebook.prototype.style = function () {
29 $('div#notebook').addClass('border-box-sizing');
24 $('div#notebook').addClass('border-box-sizing');
30 };
25 };
31
26
32
27
33 Notebook.prototype.create_elements = function () {
28 Notebook.prototype.create_elements = function () {
34 // We add this end_space div to the end of the notebook div to:
29 // We add this end_space div to the end of the notebook div to:
35 // i) provide a margin between the last cell and the end of the notebook
30 // i) provide a margin between the last cell and the end of the notebook
36 // ii) to prevent the div from scrolling up when the last cell is being
31 // ii) to prevent the div from scrolling up when the last cell is being
37 // edited, but is too low on the page, which browsers will do automatically.
32 // edited, but is too low on the page, which browsers will do automatically.
38 this.element.append($('<div class="end_space"></div>').height(50));
33 this.element.append($('<div class="end_space"></div>').height(50));
39 $('div#notebook').addClass('border-box-sizing');
34 $('div#notebook').addClass('border-box-sizing');
40 };
35 };
41
36
42
37
43 Notebook.prototype.bind_events = function () {
38 Notebook.prototype.bind_events = function () {
44 var that = this;
39 var that = this;
45 $(document).keydown(function (event) {
40 $(document).keydown(function (event) {
46 // console.log(event);
41 // console.log(event);
47 if (event.which === 38) {
42 if (event.which === 38) {
48 var cell = that.selected_cell();
43 var cell = that.selected_cell();
49 if (cell.at_top()) {
44 if (cell.at_top()) {
50 event.preventDefault();
45 event.preventDefault();
51 that.select_prev();
46 that.select_prev();
52 };
47 };
53 } else if (event.which === 40) {
48 } else if (event.which === 40) {
54 var cell = that.selected_cell();
49 var cell = that.selected_cell();
55 if (cell.at_bottom()) {
50 if (cell.at_bottom()) {
56 event.preventDefault();
51 event.preventDefault();
57 that.select_next();
52 that.select_next();
58 };
53 };
59 } else if (event.which === 13 && event.shiftKey) {
54 } else if (event.which === 13 && event.shiftKey) {
60 that.execute_selected_cell();
55 that.execute_selected_cell();
61 return false;
56 return false;
62 } else if (event.which === 13 && event.ctrlKey) {
57 } else if (event.which === 13 && event.ctrlKey) {
63 that.execute_selected_cell({terminal:true});
58 that.execute_selected_cell({terminal:true});
64 return false;
59 return false;
65 };
60 };
66 });
61 });
67
62
68 this.element.bind('collapse_pager', function () {
63 this.element.bind('collapse_pager', function () {
69 var app_height = $('div#notebook_app').height(); // content height
64 var app_height = $('div#notebook_app').height(); // content height
70 var splitter_height = $('div#pager_splitter').outerHeight(true);
65 var splitter_height = $('div#pager_splitter').outerHeight(true);
71 var new_height = app_height - splitter_height;
66 var new_height = app_height - splitter_height;
72 that.element.animate({height : new_height + 'px'}, 'fast');
67 that.element.animate({height : new_height + 'px'}, 'fast');
73 });
68 });
74
69
75 this.element.bind('expand_pager', function () {
70 this.element.bind('expand_pager', function () {
76 var app_height = $('div#notebook_app').height(); // content height
71 var app_height = $('div#notebook_app').height(); // content height
77 var splitter_height = $('div#pager_splitter').outerHeight(true);
72 var splitter_height = $('div#pager_splitter').outerHeight(true);
78 var pager_height = $('div#pager').outerHeight(true);
73 var pager_height = $('div#pager').outerHeight(true);
79 var new_height = app_height - pager_height - splitter_height;
74 var new_height = app_height - pager_height - splitter_height;
80 that.element.animate({height : new_height + 'px'}, 'fast');
75 that.element.animate({height : new_height + 'px'}, 'fast');
81 });
76 });
82
77
83 this.element.bind('collapse_left_panel', function () {
78 this.element.bind('collapse_left_panel', function () {
84 var splitter_width = $('div#left_panel_splitter').outerWidth(true);
79 var splitter_width = $('div#left_panel_splitter').outerWidth(true);
85 var new_margin = splitter_width;
80 var new_margin = splitter_width;
86 $('div#notebook_panel').animate({marginLeft : new_margin + 'px'}, 'fast');
81 $('div#notebook_panel').animate({marginLeft : new_margin + 'px'}, 'fast');
87 });
82 });
88
83
89 this.element.bind('expand_left_panel', function () {
84 this.element.bind('expand_left_panel', function () {
90 var splitter_width = $('div#left_panel_splitter').outerWidth(true);
85 var splitter_width = $('div#left_panel_splitter').outerWidth(true);
91 var left_panel_width = IPython.left_panel.width;
86 var left_panel_width = IPython.left_panel.width;
92 var new_margin = splitter_width + left_panel_width;
87 var new_margin = splitter_width + left_panel_width;
93 $('div#notebook_panel').animate({marginLeft : new_margin + 'px'}, 'fast');
88 $('div#notebook_panel').animate({marginLeft : new_margin + 'px'}, 'fast');
94 });
89 });
95 };
90 };
96
91
97
92
98 Notebook.prototype.scroll_to_bottom = function () {
93 Notebook.prototype.scroll_to_bottom = function () {
99 this.element.animate({scrollTop:this.element.get(0).scrollHeight}, 0);
94 this.element.animate({scrollTop:this.element.get(0).scrollHeight}, 0);
100 };
95 };
101
96
102 // Cell indexing, retrieval, etc.
97 // Cell indexing, retrieval, etc.
103
98
104
99
105 Notebook.prototype.cell_elements = function () {
100 Notebook.prototype.cell_elements = function () {
106 return this.element.children("div.cell");
101 return this.element.children("div.cell");
107 }
102 }
108
103
109
104
110 Notebook.prototype.ncells = function (cell) {
105 Notebook.prototype.ncells = function (cell) {
111 return this.cell_elements().length;
106 return this.cell_elements().length;
112 }
107 }
113
108
114
109
115 // TODO: we are often calling cells as cells()[i], which we should optimize
110 // TODO: we are often calling cells as cells()[i], which we should optimize
116 // to cells(i) or a new method.
111 // to cells(i) or a new method.
117 Notebook.prototype.cells = function () {
112 Notebook.prototype.cells = function () {
118 return this.cell_elements().toArray().map(function (e) {
113 return this.cell_elements().toArray().map(function (e) {
119 return $(e).data("cell");
114 return $(e).data("cell");
120 });
115 });
121 }
116 }
122
117
123
118
124 Notebook.prototype.find_cell_index = function (cell) {
119 Notebook.prototype.find_cell_index = function (cell) {
125 var result = null;
120 var result = null;
126 this.cell_elements().filter(function (index) {
121 this.cell_elements().filter(function (index) {
127 if ($(this).data("cell") === cell) {
122 if ($(this).data("cell") === cell) {
128 result = index;
123 result = index;
129 };
124 };
130 });
125 });
131 return result;
126 return result;
132 };
127 };
133
128
134
129
135 Notebook.prototype.index_or_selected = function (index) {
130 Notebook.prototype.index_or_selected = function (index) {
136 return index || this.selected_index() || 0;
131 return index || this.selected_index() || 0;
137 }
132 }
138
133
139
134
140 Notebook.prototype.select = function (index) {
135 Notebook.prototype.select = function (index) {
141 if (index !== undefined && index >= 0 && index < this.ncells()) {
136 if (index !== undefined && index >= 0 && index < this.ncells()) {
142 if (this.selected_index() !== null) {
137 if (this.selected_index() !== null) {
143 this.selected_cell().unselect();
138 this.selected_cell().unselect();
144 };
139 };
145 this.cells()[index].select();
140 this.cells()[index].select();
146 if (index === (this.ncells()-1)) {
141 if (index === (this.ncells()-1)) {
147 this.scroll_to_bottom();
142 this.scroll_to_bottom();
148 };
143 };
149 };
144 };
150 return this;
145 return this;
151 };
146 };
152
147
153
148
154 Notebook.prototype.select_next = function () {
149 Notebook.prototype.select_next = function () {
155 var index = this.selected_index();
150 var index = this.selected_index();
156 if (index !== null && index >= 0 && (index+1) < this.ncells()) {
151 if (index !== null && index >= 0 && (index+1) < this.ncells()) {
157 this.select(index+1);
152 this.select(index+1);
158 };
153 };
159 return this;
154 return this;
160 };
155 };
161
156
162
157
163 Notebook.prototype.select_prev = function () {
158 Notebook.prototype.select_prev = function () {
164 var index = this.selected_index();
159 var index = this.selected_index();
165 if (index !== null && index >= 0 && (index-1) < this.ncells()) {
160 if (index !== null && index >= 0 && (index-1) < this.ncells()) {
166 this.select(index-1);
161 this.select(index-1);
167 };
162 };
168 return this;
163 return this;
169 };
164 };
170
165
171
166
172 Notebook.prototype.selected_index = function () {
167 Notebook.prototype.selected_index = function () {
173 var result = null;
168 var result = null;
174 this.cell_elements().filter(function (index) {
169 this.cell_elements().filter(function (index) {
175 if ($(this).data("cell").selected === true) {
170 if ($(this).data("cell").selected === true) {
176 result = index;
171 result = index;
177 };
172 };
178 });
173 });
179 return result;
174 return result;
180 };
175 };
181
176
182
177
183 Notebook.prototype.cell_for_msg = function (msg_id) {
178 Notebook.prototype.cell_for_msg = function (msg_id) {
184 var cell_id = this.msg_cell_map[msg_id];
179 var cell_id = this.msg_cell_map[msg_id];
185 var result = null;
180 var result = null;
186 this.cell_elements().filter(function (index) {
181 this.cell_elements().filter(function (index) {
187 cell = $(this).data("cell");
182 cell = $(this).data("cell");
188 if (cell.cell_id === cell_id) {
183 if (cell.cell_id === cell_id) {
189 result = cell;
184 result = cell;
190 };
185 };
191 });
186 });
192 return result;
187 return result;
193 };
188 };
194
189
195
190
196 Notebook.prototype.selected_cell = function () {
191 Notebook.prototype.selected_cell = function () {
197 return this.cell_elements().eq(this.selected_index()).data("cell");
192 return this.cell_elements().eq(this.selected_index()).data("cell");
198 }
193 }
199
194
200
195
201 // Cell insertion, deletion and moving.
196 // Cell insertion, deletion and moving.
202
197
203
198
204 Notebook.prototype.delete_cell = function (index) {
199 Notebook.prototype.delete_cell = function (index) {
205 var i = index || this.selected_index();
200 var i = index || this.selected_index();
206 if (i !== null && i >= 0 && i < this.ncells()) {
201 if (i !== null && i >= 0 && i < this.ncells()) {
207 this.cell_elements().eq(i).remove();
202 this.cell_elements().eq(i).remove();
208 if (i === (this.ncells())) {
203 if (i === (this.ncells())) {
209 this.select(i-1);
204 this.select(i-1);
210 } else {
205 } else {
211 this.select(i);
206 this.select(i);
212 };
207 };
213 };
208 };
214 return this;
209 return this;
215 };
210 };
216
211
217
212
218 Notebook.prototype.append_cell = function (cell) {
213 Notebook.prototype.append_cell = function (cell) {
219 this.element.find('div.end_space').before(cell.element);
214 this.element.find('div.end_space').before(cell.element);
220 return this;
215 return this;
221 };
216 };
222
217
223
218
224 Notebook.prototype.insert_cell_after = function (cell, index) {
219 Notebook.prototype.insert_cell_after = function (cell, index) {
225 var ncells = this.ncells();
220 var ncells = this.ncells();
226 if (ncells === 0) {
221 if (ncells === 0) {
227 this.append_cell(cell);
222 this.append_cell(cell);
228 return this;
223 return this;
229 };
224 };
230 if (index >= 0 && index < ncells) {
225 if (index >= 0 && index < ncells) {
231 this.cell_elements().eq(index).after(cell.element);
226 this.cell_elements().eq(index).after(cell.element);
232 };
227 };
233 return this
228 return this
234 };
229 };
235
230
236
231
237 Notebook.prototype.insert_cell_before = function (cell, index) {
232 Notebook.prototype.insert_cell_before = function (cell, index) {
238 var ncells = this.ncells();
233 var ncells = this.ncells();
239 if (ncells === 0) {
234 if (ncells === 0) {
240 this.append_cell(cell);
235 this.append_cell(cell);
241 return this;
236 return this;
242 };
237 };
243 if (index >= 0 && index < ncells) {
238 if (index >= 0 && index < ncells) {
244 this.cell_elements().eq(index).before(cell.element);
239 this.cell_elements().eq(index).before(cell.element);
245 };
240 };
246 return this;
241 return this;
247 };
242 };
248
243
249
244
250 Notebook.prototype.move_cell_up = function (index) {
245 Notebook.prototype.move_cell_up = function (index) {
251 var i = index || this.selected_index();
246 var i = index || this.selected_index();
252 if (i !== null && i < this.ncells() && i > 0) {
247 if (i !== null && i < this.ncells() && i > 0) {
253 var pivot = this.cell_elements().eq(i-1);
248 var pivot = this.cell_elements().eq(i-1);
254 var tomove = this.cell_elements().eq(i);
249 var tomove = this.cell_elements().eq(i);
255 if (pivot !== null && tomove !== null) {
250 if (pivot !== null && tomove !== null) {
256 tomove.detach();
251 tomove.detach();
257 pivot.before(tomove);
252 pivot.before(tomove);
258 this.select(i-1);
253 this.select(i-1);
259 };
254 };
260 };
255 };
261 return this;
256 return this;
262 }
257 }
263
258
264
259
265 Notebook.prototype.move_cell_down = function (index) {
260 Notebook.prototype.move_cell_down = function (index) {
266 var i = index || this.selected_index();
261 var i = index || this.selected_index();
267 if (i !== null && i < (this.ncells()-1) && i >= 0) {
262 if (i !== null && i < (this.ncells()-1) && i >= 0) {
268 var pivot = this.cell_elements().eq(i+1)
263 var pivot = this.cell_elements().eq(i+1)
269 var tomove = this.cell_elements().eq(i)
264 var tomove = this.cell_elements().eq(i)
270 if (pivot !== null && tomove !== null) {
265 if (pivot !== null && tomove !== null) {
271 tomove.detach();
266 tomove.detach();
272 pivot.after(tomove);
267 pivot.after(tomove);
273 this.select(i+1);
268 this.select(i+1);
274 };
269 };
275 };
270 };
276 return this;
271 return this;
277 }
272 }
278
273
279
274
280 Notebook.prototype.sort_cells = function () {
275 Notebook.prototype.sort_cells = function () {
281 var ncells = this.ncells();
276 var ncells = this.ncells();
282 var sindex = this.selected_index();
277 var sindex = this.selected_index();
283 var swapped;
278 var swapped;
284 do {
279 do {
285 swapped = false
280 swapped = false
286 for (var i=1; i<ncells; i++) {
281 for (var i=1; i<ncells; i++) {
287 current = this.cell_elements().eq(i).data("cell");
282 current = this.cell_elements().eq(i).data("cell");
288 previous = this.cell_elements().eq(i-1).data("cell");
283 previous = this.cell_elements().eq(i-1).data("cell");
289 if (previous.input_prompt_number > current.input_prompt_number) {
284 if (previous.input_prompt_number > current.input_prompt_number) {
290 this.move_cell_up(i);
285 this.move_cell_up(i);
291 swapped = true;
286 swapped = true;
292 };
287 };
293 };
288 };
294 } while (swapped);
289 } while (swapped);
295 this.select(sindex);
290 this.select(sindex);
296 return this;
291 return this;
297 };
292 };
298
293
299
294
300 Notebook.prototype.insert_code_cell_before = function (index) {
295 Notebook.prototype.insert_code_cell_before = function (index) {
301 // TODO: Bounds check for i
296 // TODO: Bounds check for i
302 var i = this.index_or_selected(index);
297 var i = this.index_or_selected(index);
303 var cell = new IPython.CodeCell(this);
298 var cell = new IPython.CodeCell(this);
304 // cell.set_input_prompt(this.next_prompt_number);
299 // cell.set_input_prompt(this.next_prompt_number);
305 cell.set_input_prompt();
300 cell.set_input_prompt();
306 this.next_prompt_number = this.next_prompt_number + 1;
301 this.next_prompt_number = this.next_prompt_number + 1;
307 this.insert_cell_before(cell, i);
302 this.insert_cell_before(cell, i);
308 this.select(this.find_cell_index(cell));
303 this.select(this.find_cell_index(cell));
309 return this;
304 return this;
310 }
305 }
311
306
312
307
313 Notebook.prototype.insert_code_cell_after = function (index) {
308 Notebook.prototype.insert_code_cell_after = function (index) {
314 // TODO: Bounds check for i
309 // TODO: Bounds check for i
315 var i = this.index_or_selected(index);
310 var i = this.index_or_selected(index);
316 var cell = new IPython.CodeCell(this);
311 var cell = new IPython.CodeCell(this);
317 // cell.set_input_prompt(this.next_prompt_number);
312 // cell.set_input_prompt(this.next_prompt_number);
318 cell.set_input_prompt();
313 cell.set_input_prompt();
319 this.next_prompt_number = this.next_prompt_number + 1;
314 this.next_prompt_number = this.next_prompt_number + 1;
320 this.insert_cell_after(cell, i);
315 this.insert_cell_after(cell, i);
321 this.select(this.find_cell_index(cell));
316 this.select(this.find_cell_index(cell));
322 return this;
317 return this;
323 }
318 }
324
319
325
320
326 Notebook.prototype.insert_text_cell_before = function (index) {
321 Notebook.prototype.insert_text_cell_before = function (index) {
327 // TODO: Bounds check for i
322 // TODO: Bounds check for i
328 var i = this.index_or_selected(index);
323 var i = this.index_or_selected(index);
329 var cell = new IPython.TextCell(this);
324 var cell = new IPython.TextCell(this);
330 cell.config_mathjax();
325 cell.config_mathjax();
331 this.insert_cell_before(cell, i);
326 this.insert_cell_before(cell, i);
332 this.select(this.find_cell_index(cell));
327 this.select(this.find_cell_index(cell));
333 return this;
328 return this;
334 }
329 }
335
330
336
331
337 Notebook.prototype.insert_text_cell_after = function (index) {
332 Notebook.prototype.insert_text_cell_after = function (index) {
338 // TODO: Bounds check for i
333 // TODO: Bounds check for i
339 var i = this.index_or_selected(index);
334 var i = this.index_or_selected(index);
340 var cell = new IPython.TextCell(this);
335 var cell = new IPython.TextCell(this);
341 cell.config_mathjax();
336 cell.config_mathjax();
342 this.insert_cell_after(cell, i);
337 this.insert_cell_after(cell, i);
343 this.select(this.find_cell_index(cell));
338 this.select(this.find_cell_index(cell));
344 return this;
339 return this;
345 }
340 }
346
341
347
342
348 Notebook.prototype.text_to_code = function (index) {
343 Notebook.prototype.text_to_code = function (index) {
349 // TODO: Bounds check for i
344 // TODO: Bounds check for i
350 var i = this.index_or_selected(index);
345 var i = this.index_or_selected(index);
351 var source_element = this.cell_elements().eq(i);
346 var source_element = this.cell_elements().eq(i);
352 var source_cell = source_element.data("cell");
347 var source_cell = source_element.data("cell");
353 if (source_cell instanceof IPython.TextCell) {
348 if (source_cell instanceof IPython.TextCell) {
354 this.insert_code_cell_after(i);
349 this.insert_code_cell_after(i);
355 var target_cell = this.cells()[i+1];
350 var target_cell = this.cells()[i+1];
356 target_cell.set_code(source_cell.get_text());
351 target_cell.set_code(source_cell.get_text());
357 source_element.remove();
352 source_element.remove();
358 };
353 };
359 };
354 };
360
355
361
356
362 Notebook.prototype.code_to_text = function (index) {
357 Notebook.prototype.code_to_text = function (index) {
363 // TODO: Bounds check for i
358 // TODO: Bounds check for i
364 var i = this.index_or_selected(index);
359 var i = this.index_or_selected(index);
365 var source_element = this.cell_elements().eq(i);
360 var source_element = this.cell_elements().eq(i);
366 var source_cell = source_element.data("cell");
361 var source_cell = source_element.data("cell");
367 if (source_cell instanceof IPython.CodeCell) {
362 if (source_cell instanceof IPython.CodeCell) {
368 this.insert_text_cell_after(i);
363 this.insert_text_cell_after(i);
369 var target_cell = this.cells()[i+1];
364 var target_cell = this.cells()[i+1];
370 var text = source_cell.get_code();
365 var text = source_cell.get_code();
371 if (text === "") {text = target_cell.placeholder;};
366 if (text === "") {text = target_cell.placeholder;};
372 target_cell.set_text(text);
367 target_cell.set_text(text);
373 source_element.remove();
368 source_element.remove();
374 target_cell.edit();
369 target_cell.edit();
375 };
370 };
376 };
371 };
377
372
378
373
379 // Cell collapsing
374 // Cell collapsing
380
375
381 Notebook.prototype.collapse = function (index) {
376 Notebook.prototype.collapse = function (index) {
382 var i = this.index_or_selected(index);
377 var i = this.index_or_selected(index);
383 this.cells()[i].collapse();
378 this.cells()[i].collapse();
384 };
379 };
385
380
386
381
387 Notebook.prototype.expand = function (index) {
382 Notebook.prototype.expand = function (index) {
388 var i = this.index_or_selected(index);
383 var i = this.index_or_selected(index);
389 this.cells()[i].expand();
384 this.cells()[i].expand();
390 };
385 };
391
386
392
387
393 // Kernel related things
388 // Kernel related things
394
389
395 Notebook.prototype.start_kernel = function () {
390 Notebook.prototype.start_kernel = function () {
396 this.kernel = new IPython.Kernel();
391 this.kernel = new IPython.Kernel();
397 this.kernel.start_kernel($.proxy(this.kernel_started, this));
392 this.kernel.start_kernel($.proxy(this.kernel_started, this));
398 };
393 };
399
394
400
395
401 Notebook.prototype.handle_shell_reply = function (e) {
396 Notebook.prototype.handle_shell_reply = function (e) {
402 reply = $.parseJSON(e.data);
397 reply = $.parseJSON(e.data);
403 var header = reply.header;
398 var header = reply.header;
404 var content = reply.content;
399 var content = reply.content;
405 var msg_type = header.msg_type;
400 var msg_type = header.msg_type;
406 // console.log(reply);
401 // console.log(reply);
407 var cell = this.cell_for_msg(reply.parent_header.msg_id);
402 var cell = this.cell_for_msg(reply.parent_header.msg_id);
408 if (msg_type === "execute_reply") {
403 if (msg_type === "execute_reply") {
409 cell.set_input_prompt(content.execution_count);
404 cell.set_input_prompt(content.execution_count);
410 } else if (msg_type === "complete_reply") {
405 } else if (msg_type === "complete_reply") {
411 cell.finish_completing(content.matched_text, content.matches);
406 cell.finish_completing(content.matched_text, content.matches);
412 };
407 };
413 var payload = content.payload || [];
408 var payload = content.payload || [];
414 this.handle_payload(payload);
409 this.handle_payload(payload);
415 };
410 };
416
411
417
412
418 Notebook.prototype.handle_payload = function (payload) {
413 Notebook.prototype.handle_payload = function (payload) {
419 var l = payload.length;
414 var l = payload.length;
420 if (l > 0) {
415 if (l > 0) {
421 IPython.pager.clear();
416 IPython.pager.clear();
422 IPython.pager.expand();
417 IPython.pager.expand();
423 };
418 };
424 for (var i=0; i<l; i++) {
419 for (var i=0; i<l; i++) {
425 IPython.pager.append_text(payload[i].text);
420 IPython.pager.append_text(payload[i].text);
426 };
421 };
427 };
422 };
428
423
429
424
430 Notebook.prototype.handle_iopub_reply = function (e) {
425 Notebook.prototype.handle_iopub_reply = function (e) {
431 reply = $.parseJSON(e.data);
426 reply = $.parseJSON(e.data);
432 var content = reply.content;
427 var content = reply.content;
433 // console.log(reply);
428 // console.log(reply);
434 var msg_type = reply.header.msg_type;
429 var msg_type = reply.header.msg_type;
435 var cell = this.cell_for_msg(reply.parent_header.msg_id);
430 var cell = this.cell_for_msg(reply.parent_header.msg_id);
436 if (msg_type === "stream") {
431 if (msg_type === "stream") {
437 cell.expand();
432 cell.expand();
438 cell.append_stream(content.data + "\n");
433 cell.append_stream(content.data + "\n");
439 } else if (msg_type === "display_data") {
434 } else if (msg_type === "display_data") {
440 cell.expand();
435 cell.expand();
441 cell.append_display_data(content.data);
436 cell.append_display_data(content.data);
442 } else if (msg_type === "pyout") {
437 } else if (msg_type === "pyout") {
443 cell.expand();
438 cell.expand();
444 cell.append_pyout(content.data, content.execution_count)
439 cell.append_pyout(content.data, content.execution_count)
445 } else if (msg_type === "pyerr") {
440 } else if (msg_type === "pyerr") {
446 cell.expand();
441 cell.expand();
447 cell.append_pyerr(content.ename, content.evalue, content.traceback);
442 cell.append_pyerr(content.ename, content.evalue, content.traceback);
448 } else if (msg_type === "status") {
443 } else if (msg_type === "status") {
449 if (content.execution_state === "busy") {
444 if (content.execution_state === "busy") {
450 IPython.kernel_status_widget.status_busy();
445 IPython.kernel_status_widget.status_busy();
451 } else if (content.execution_state === "idle") {
446 } else if (content.execution_state === "idle") {
452 IPython.kernel_status_widget.status_idle();
447 IPython.kernel_status_widget.status_idle();
453 };
448 };
454 }
449 }
455 };
450 };
456
451
457
452
458 Notebook.prototype.kernel_started = function () {
453 Notebook.prototype.kernel_started = function () {
459 console.log("Kernel started: ", this.kernel.kernel_id);
454 console.log("Kernel started: ", this.kernel.kernel_id);
460 this.kernel.shell_channel.onmessage = $.proxy(this.handle_shell_reply,this);
455 this.kernel.shell_channel.onmessage = $.proxy(this.handle_shell_reply,this);
461 this.kernel.iopub_channel.onmessage = $.proxy(this.handle_iopub_reply,this);
456 this.kernel.iopub_channel.onmessage = $.proxy(this.handle_iopub_reply,this);
462 };
457 };
463
458
464
459
465 Notebook.prototype.execute_selected_cell = function (options) {
460 Notebook.prototype.execute_selected_cell = function (options) {
466 // add_new: should a new cell be added if we are at the end of the nb
461 // add_new: should a new cell be added if we are at the end of the nb
467 // terminal: execute in terminal mode, which stays in the current cell
462 // terminal: execute in terminal mode, which stays in the current cell
468 default_options = {terminal: false, add_new: true}
463 default_options = {terminal: false, add_new: true}
469 $.extend(default_options, options)
464 $.extend(default_options, options)
470 var that = this;
465 var that = this;
471 var cell = that.selected_cell();
466 var cell = that.selected_cell();
472 var cell_index = that.find_cell_index(cell);
467 var cell_index = that.find_cell_index(cell);
473 if (cell instanceof IPython.CodeCell) {
468 if (cell instanceof IPython.CodeCell) {
474 cell.clear_output();
469 cell.clear_output();
475 var code = cell.get_code();
470 var code = cell.get_code();
476 if (that.notebook_load_re.test(code)) {
471 var msg_id = that.kernel.execute(cell.get_code());
477 // %notebook load
472 that.msg_cell_map[msg_id] = cell.cell_id;
478 var code_parts = code.split(' ');
479 if (code_parts.length === 3) {
480 that.load_notebook(code_parts[2]);
481 };
482 } else if (that.notebook_save_re.test(code)) {
483 // %notebook save
484 var code_parts = code.split(' ');
485 if (code_parts.length === 3) {
486 that.save_notebook(code_parts[2]);
487 } else {
488 that.save_notebook()
489 };
490 } else {
491 var msg_id = that.kernel.execute(cell.get_code());
492 that.msg_cell_map[msg_id] = cell.cell_id;
493 };
494 } else if (cell instanceof IPython.TextCell) {
473 } else if (cell instanceof IPython.TextCell) {
495 cell.render();
474 cell.render();
496 }
475 }
497 if (default_options.terminal) {
476 if (default_options.terminal) {
498 cell.clear_input();
477 cell.clear_input();
499 } else {
478 } else {
500 if ((cell_index === (that.ncells()-1)) && default_options.add_new) {
479 if ((cell_index === (that.ncells()-1)) && default_options.add_new) {
501 that.insert_code_cell_after();
480 that.insert_code_cell_after();
502 // If we are adding a new cell at the end, scroll down to show it.
481 // If we are adding a new cell at the end, scroll down to show it.
503 that.scroll_to_bottom();
482 that.scroll_to_bottom();
504 } else {
483 } else {
505 that.select(cell_index+1);
484 that.select(cell_index+1);
506 };
485 };
507 };
486 };
508 };
487 };
509
488
510
489
511 Notebook.prototype.execute_all_cells = function () {
490 Notebook.prototype.execute_all_cells = function () {
512 var ncells = this.ncells();
491 var ncells = this.ncells();
513 for (var i=0; i<ncells; i++) {
492 for (var i=0; i<ncells; i++) {
514 this.select(i);
493 this.select(i);
515 this.execute_selected_cell({add_new:false});
494 this.execute_selected_cell({add_new:false});
516 };
495 };
517 this.scroll_to_bottom();
496 this.scroll_to_bottom();
518 };
497 };
519
498
520
499
521 Notebook.prototype.complete_cell = function (cell, line, cursor_pos) {
500 Notebook.prototype.complete_cell = function (cell, line, cursor_pos) {
522 var msg_id = this.kernel.complete(line, cursor_pos);
501 var msg_id = this.kernel.complete(line, cursor_pos);
523 this.msg_cell_map[msg_id] = cell.cell_id;
502 this.msg_cell_map[msg_id] = cell.cell_id;
524 };
503 };
525
504
526 // Persistance and loading
505 // Persistance and loading
527
506
528
507
529 Notebook.prototype.fromJSON = function (data) {
508 Notebook.prototype.fromJSON = function (data) {
530 var ncells = this.ncells();
509 var ncells = this.ncells();
531 for (var i=0; i<ncells; i++) {
510 for (var i=0; i<ncells; i++) {
532 // Always delete cell 0 as they get renumbered as they are deleted.
511 // Always delete cell 0 as they get renumbered as they are deleted.
533 this.delete_cell(0);
512 this.delete_cell(0);
534 };
513 };
535 var new_cells = data.cells;
514 // Only handle 1 worksheet for now.
536 ncells = new_cells.length;
515 var worksheet = data.worksheets[0];
537 var cell_data = null;
516 if (worksheet !== undefined) {
538 for (var i=0; i<ncells; i++) {
517 var new_cells = worksheet.cells;
539 cell_data = new_cells[i];
518 ncells = new_cells.length;
540 if (cell_data.cell_type == 'code') {
519 var cell_data = null;
541 this.insert_code_cell_after();
520 for (var i=0; i<ncells; i++) {
542 this.selected_cell().fromJSON(cell_data);
521 cell_data = new_cells[i];
543 } else if (cell_data.cell_type === 'text') {
522 if (cell_data.cell_type == 'code') {
544 this.insert_text_cell_after();
523 this.insert_code_cell_after();
545 this.selected_cell().fromJSON(cell_data);
524 this.selected_cell().fromJSON(cell_data);
546 };
525 } else if (cell_data.cell_type === 'text') {
526 this.insert_text_cell_after();
527 this.selected_cell().fromJSON(cell_data);
528 };
529 };
547 };
530 };
548 };
531 };
549
532
550
533
551 Notebook.prototype.toJSON = function () {
534 Notebook.prototype.toJSON = function () {
552 var cells = this.cells();
535 var cells = this.cells();
553 var ncells = cells.length;
536 var ncells = cells.length;
554 cell_array = new Array(ncells);
537 cell_array = new Array(ncells);
555 for (var i=0; i<ncells; i++) {
538 for (var i=0; i<ncells; i++) {
556 cell_array[i] = cells[i].toJSON();
539 cell_array[i] = cells[i].toJSON();
557 };
540 };
558 json = {
541 data = {
559 cells : cell_array
542 // Only handle 1 worksheet for now.
543 worksheets : [{cells:cell_array}]
544 }
545 return data
546 };
547
548 Notebook.prototype.save_notebook = function () {
549 if (IPython.save_widget.test_notebook_name()) {
550 var notebook_id = IPython.save_widget.get_notebook_id();
551 var nbname = IPython.save_widget.get_notebook_name();
552 // We may want to move the name/id/nbformat logic inside toJSON?
553 var data = this.toJSON();
554 data.name = nbname;
555 data.nbformat = 2;
556 data.id = notebook_id
557 // We do the call with settings so we can set cache to false.
558 var settings = {
559 processData : false,
560 cache : false,
561 type : "PUT",
562 data : JSON.stringify(data),
563 success : $.proxy(this.notebook_saved,this)
564 };
565 IPython.save_widget.status_saving();
566 $.ajax("/notebooks/" + notebook_id, settings);
560 };
567 };
561 return json
562 };
568 };
563
569
564
570
565 Notebook.prototype.test_filename = function (filename) {
571 Notebook.prototype.notebook_saved = function (data, status, xhr) {
566 if (this.notebook_filename_re.test(filename)) {
572 IPython.save_widget.status_save();
567 return true;
573 }
568 } else {
569 var bad_filename = $('<div/>');
570 bad_filename.html(
571 "The filename you entered (" + filename + ") is not valid. Notebook filenames must have the following form: foo.ipynb"
572 );
573 bad_filename.dialog({title: 'Invalid filename', modal: true});
574 return false;
575 };
576 };
577
578 Notebook.prototype.save_notebook = function (filename) {
579 this.filename = filename || this.filename || '';
580 if (this.filename === '') {
581 var no_filename = $('<div/>');
582 no_filename.html(
583 "This notebook has no filename, please specify a filename of the form: foo.ipynb"
584 );
585 no_filename.dialog({title: 'Missing filename', modal: true});
586 return;
587 }
588 if (!this.test_filename(this.filename)) {return;}
589 var thedata = this.toJSON();
590 var settings = {
591 processData : false,
592 cache : false,
593 type : "PUT",
594 data : JSON.stringify(thedata),
595 success : function (data, status, xhr) {console.log(data);}
596 };
597 $.ajax("/notebooks/" + this.filename, settings);
598 };
599
574
600
575
601 Notebook.prototype.load_notebook = function (filename) {
576 Notebook.prototype.load_notebook = function () {
602 if (!this.test_filename(filename)) {return;}
577 var notebook_id = IPython.save_widget.get_notebook_id();
603 var that = this;
604 // We do the call with settings so we can set cache to false.
578 // We do the call with settings so we can set cache to false.
605 var settings = {
579 var settings = {
606 processData : false,
580 processData : false,
607 cache : false,
581 cache : false,
608 type : "GET",
582 type : "GET",
609 dataType : "json",
583 dataType : "json",
610 success : function (data, status, xhr) {
584 success : $.proxy(this.notebook_loaded,this)
611 that.fromJSON(data);
612 that.filename = filename;
613 that.kernel.restart();
614 }
615 };
585 };
616 $.ajax("/notebooks/" + filename, settings);
586 IPython.save_widget.status_loading();
587 $.ajax("/notebooks/" + notebook_id, settings);
617 }
588 }
618
589
590
591 Notebook.prototype.notebook_loaded = function (data, status, xhr) {
592 this.fromJSON(data);
593 if (this.ncells() === 0) {
594 this.insert_code_cell_after();
595 };
596 IPython.save_widget.status_save();
597 IPython.save_widget.set_notebook_name(data.name);
598 this.start_kernel();
599 };
600
619 IPython.Notebook = Notebook;
601 IPython.Notebook = Notebook;
620
602
621 return IPython;
603 return IPython;
622
604
623 }(IPython));
605 }(IPython));
624
606
@@ -1,43 +1,47 b''
1
1
2 //============================================================================
2 //============================================================================
3 // On document ready
3 // On document ready
4 //============================================================================
4 //============================================================================
5
5
6
6
7 $(document).ready(function () {
7 $(document).ready(function () {
8
8
9 MathJax.Hub.Config({
9 MathJax.Hub.Config({
10 tex2jax: {
10 tex2jax: {
11 inlineMath: [ ['$','$'], ["\\(","\\)"] ],
11 inlineMath: [ ['$','$'], ["\\(","\\)"] ],
12 displayMath: [ ['$$','$$'], ["\\[","\\]"] ],
12 displayMath: [ ['$$','$$'], ["\\[","\\]"] ],
13 },
13 },
14 displayAlign: 'left', // Change this to 'center' to center equations.
14 displayAlign: 'left', // Change this to 'center' to center equations.
15 "HTML-CSS": {
15 "HTML-CSS": {
16 styles: {'.MathJax_Display': {"margin": 0}}
16 styles: {'.MathJax_Display': {"margin": 0}}
17 }
17 }
18 });
18 });
19
19
20 $('div#header').addClass('border-box-sizing');
20 $('div#header').addClass('border-box-sizing');
21 $('div#notebook_app').addClass('border-box-sizing ui-widget ui-widget-content');
21 $('div#notebook_app').addClass('border-box-sizing ui-widget ui-widget-content');
22 $('div#notebook_panel').addClass('border-box-sizing ui-widget');
22 $('div#notebook_panel').addClass('border-box-sizing ui-widget');
23
23
24 IPython.layout_manager = new IPython.LayoutManager();
24 IPython.layout_manager = new IPython.LayoutManager();
25 IPython.pager = new IPython.Pager('div#pager', 'div#pager_splitter');
25 IPython.pager = new IPython.Pager('div#pager', 'div#pager_splitter');
26 IPython.left_panel = new IPython.LeftPanel('div#left_panel', 'div#left_panel_splitter');
26 IPython.left_panel = new IPython.LeftPanel('div#left_panel', 'div#left_panel_splitter');
27 IPython.save_widget = new IPython.SaveWidget('span#save_widget');
27 IPython.save_widget = new IPython.SaveWidget('span#save_widget');
28 IPython.notebook = new IPython.Notebook('div#notebook');
28 IPython.notebook = new IPython.Notebook('div#notebook');
29 IPython.kernel_status_widget = new IPython.KernelStatusWidget('#kernel_status');
29 IPython.kernel_status_widget = new IPython.KernelStatusWidget('#kernel_status');
30 IPython.kernel_status_widget.status_idle();
30 IPython.kernel_status_widget.status_idle();
31
31
32 IPython.layout_manager.do_resize();
32 IPython.layout_manager.do_resize();
33 IPython.notebook.insert_code_cell_after();
34 IPython.layout_manager.do_resize();
35
33
36 // These have display: none in the css file and are made visible here to prevent FLOUC.
34 // These have display: none in the css file and are made visible here to prevent FLOUC.
37 $('div#header').css('display','block');
35 $('div#header').css('display','block');
38 $('div#notebook_app').css('display','block');
36 $('div#notebook_app').css('display','block');
39 IPython.layout_manager.do_resize();
37
40 IPython.pager.collapse();
38 IPython.notebook.load_notebook();
41 IPython.layout_manager.do_resize();
39
40 // Perform these actions after the notebook has been loaded.
41 setTimeout(function () {
42 IPython.save_widget.update_url();
43 IPython.layout_manager.do_resize();
44 IPython.pager.collapse();
45 }, 100);
42 });
46 });
43
47
@@ -1,225 +1,236 b''
1
1
2 //============================================================================
2 //============================================================================
3 // Cell
3 // Cell
4 //============================================================================
4 //============================================================================
5
5
6 var IPython = (function (IPython) {
6 var IPython = (function (IPython) {
7
7
8 var utils = IPython.utils;
8 var utils = IPython.utils;
9
9
10 // Base PanelSection class
10 // Base PanelSection class
11
11
12 var PanelSection = function (selector) {
12 var PanelSection = function (selector) {
13 this.selector = selector;
13 this.selector = selector;
14 if (this.selector !== undefined) {
14 if (this.selector !== undefined) {
15 this.element = $(selector);
15 this.element = $(selector);
16 this.header = this.element.find('h3.section_header');
16 this.header = this.element.find('h3.section_header');
17 this.content = this.element.find('div.section_content');
17 this.content = this.element.find('div.section_content');
18 this.style();
18 this.style();
19 this.bind_events();
19 this.bind_events();
20 }
20 }
21 this.expanded = true;
21 this.expanded = true;
22 };
22 };
23
23
24
24
25 PanelSection.prototype.style = function () {
25 PanelSection.prototype.style = function () {
26 this.header.addClass('ui-widget ui-state-default');
26 this.header.addClass('ui-widget ui-state-default');
27 this.content.addClass('ui-widget section_content');
27 this.content.addClass('ui-widget section_content');
28 };
28 };
29
29
30
30
31 PanelSection.prototype.bind_events = function () {
31 PanelSection.prototype.bind_events = function () {
32 var that = this;
32 var that = this;
33 this.header.click(function () {
33 this.header.click(function () {
34 that.toggle();
34 that.toggle();
35 });
35 });
36 this.header.hover(function () {
36 this.header.hover(function () {
37 that.header.toggleClass('ui-state-hover');
37 that.header.toggleClass('ui-state-hover');
38 });
38 });
39 };
39 };
40
40
41
41
42 PanelSection.prototype.expand = function () {
42 PanelSection.prototype.expand = function () {
43 if (!this.expanded) {
43 if (!this.expanded) {
44 this.content.slideDown('fast');
44 this.content.slideDown('fast');
45 this.expanded = true;
45 this.expanded = true;
46 };
46 };
47 };
47 };
48
48
49
49
50 PanelSection.prototype.collapse = function () {
50 PanelSection.prototype.collapse = function () {
51 if (this.expanded) {
51 if (this.expanded) {
52 this.content.slideUp('fast');
52 this.content.slideUp('fast');
53 this.expanded = false;
53 this.expanded = false;
54 };
54 };
55 };
55 };
56
56
57
57
58 PanelSection.prototype.toggle = function () {
58 PanelSection.prototype.toggle = function () {
59 if (this.expanded === true) {
59 if (this.expanded === true) {
60 this.collapse();
60 this.collapse();
61 } else {
61 } else {
62 this.expand();
62 this.expand();
63 };
63 };
64 };
64 };
65
65
66
66
67 PanelSection.prototype.create_children = function () {};
67 PanelSection.prototype.create_children = function () {};
68
68
69
69
70 // NotebookSection
70 // NotebookSection
71
71
72 var NotebookSection = function () {
72 var NotebookSection = function () {
73 PanelSection.apply(this, arguments);
73 PanelSection.apply(this, arguments);
74 };
74 };
75
75
76
76
77 NotebookSection.prototype = new PanelSection();
77 NotebookSection.prototype = new PanelSection();
78
78
79
79
80 NotebookSection.prototype.style = function () {
80 NotebookSection.prototype.style = function () {
81 PanelSection.prototype.style.apply(this);
81 PanelSection.prototype.style.apply(this);
82 this.content.addClass('ui-helper-clearfix');
82 this.content.addClass('ui-helper-clearfix');
83 this.content.find('div.section_row').addClass('ui-helper-clearfix');
83 this.content.find('div.section_row').addClass('ui-helper-clearfix');
84 this.content.find('#new_open').buttonset();
84 this.content.find('#new_open').buttonset();
85 this.content.find('#download_notebook').button();
86 this.content.find('#upload_notebook').button();
87 this.content.find('#download_format').addClass('ui-widget ui-widget-content');
88 this.content.find('#download_format option').addClass('ui-widget ui-widget-content');
85 };
89 };
86
90
87
91
88 NotebookSection.prototype.bind_events = function () {
92 NotebookSection.prototype.bind_events = function () {
89 PanelSection.prototype.bind_events.apply(this);
93 PanelSection.prototype.bind_events.apply(this);
94 var that = this;
90 this.content.find('#new_notebook').click(function () {
95 this.content.find('#new_notebook').click(function () {
91 alert('Not Implemented');
96 console.log('click!')
97 window.open('/');
92 });
98 });
93 this.content.find('#open_notebook').click(function () {
99 this.content.find('#open_notebook').click(function () {
94 alert('Not Implemented');
100 alert('Not Implemented');
95 });
101 });
102 this.content.find('#download_notebook').click(function () {
103 var format = that.content.find('#download_format').val();
104 var notebook_id = IPython.save_widget.get_notebook_id();
105 var url = '/notebooks/' + notebook_id + '?format=' + format;
106 window.open(url,'_newtab');
107 });
96 };
108 };
97
109
98
99 // CellSection
110 // CellSection
100
111
101 var CellSection = function () {
112 var CellSection = function () {
102 PanelSection.apply(this, arguments);
113 PanelSection.apply(this, arguments);
103 };
114 };
104
115
105
116
106 CellSection.prototype = new PanelSection();
117 CellSection.prototype = new PanelSection();
107
118
108
119
109 CellSection.prototype.style = function () {
120 CellSection.prototype.style = function () {
110 PanelSection.prototype.style.apply(this);
121 PanelSection.prototype.style.apply(this);
111 this.content.addClass('ui-helper-clearfix');
122 this.content.addClass('ui-helper-clearfix');
112 this.content.find('div.section_row').addClass('ui-helper-clearfix');
123 this.content.find('div.section_row').addClass('ui-helper-clearfix');
113 this.content.find('#delete_cell').button();
124 this.content.find('#delete_cell').button();
114 this.content.find('#insert').buttonset();
125 this.content.find('#insert').buttonset();
115 this.content.find('#move').buttonset();
126 this.content.find('#move').buttonset();
116 this.content.find('#cell_type').buttonset();
127 this.content.find('#cell_type').buttonset();
117 this.content.find('#toggle_output').buttonset();
128 this.content.find('#toggle_output').buttonset();
118 this.content.find('#run_cells').buttonset();
129 this.content.find('#run_cells').buttonset();
119 };
130 };
120
131
121
132
122 CellSection.prototype.bind_events = function () {
133 CellSection.prototype.bind_events = function () {
123 PanelSection.prototype.bind_events.apply(this);
134 PanelSection.prototype.bind_events.apply(this);
124 this.content.find('#collapse_cell').click(function () {
135 this.content.find('#collapse_cell').click(function () {
125 IPython.notebook.collapse();
136 IPython.notebook.collapse();
126 });
137 });
127 this.content.find('#expand_cell').click(function () {
138 this.content.find('#expand_cell').click(function () {
128 IPython.notebook.expand();
139 IPython.notebook.expand();
129 });
140 });
130 this.content.find('#delete_cell').click(function () {
141 this.content.find('#delete_cell').click(function () {
131 IPython.notebook.delete_cell();
142 IPython.notebook.delete_cell();
132 });
143 });
133 this.content.find('#insert_cell_above').click(function () {
144 this.content.find('#insert_cell_above').click(function () {
134 IPython.notebook.insert_code_cell_before();
145 IPython.notebook.insert_code_cell_before();
135 });
146 });
136 this.content.find('#insert_cell_below').click(function () {
147 this.content.find('#insert_cell_below').click(function () {
137 IPython.notebook.insert_code_cell_after();
148 IPython.notebook.insert_code_cell_after();
138 });
149 });
139 this.content.find('#move_cell_up').click(function () {
150 this.content.find('#move_cell_up').click(function () {
140 IPython.notebook.move_cell_up();
151 IPython.notebook.move_cell_up();
141 });
152 });
142 this.content.find('#move_cell_down').click(function () {
153 this.content.find('#move_cell_down').click(function () {
143 IPython.notebook.move_cell_down();
154 IPython.notebook.move_cell_down();
144 });
155 });
145 this.content.find('#to_code').click(function () {
156 this.content.find('#to_code').click(function () {
146 IPython.notebook.text_to_code();
157 IPython.notebook.text_to_code();
147 });
158 });
148 this.content.find('#to_text').click(function () {
159 this.content.find('#to_text').click(function () {
149 IPython.notebook.code_to_text();
160 IPython.notebook.code_to_text();
150 });
161 });
151 this.content.find('#run_selected_cell').click(function () {
162 this.content.find('#run_selected_cell').click(function () {
152 IPython.notebook.execute_selected_cell();
163 IPython.notebook.execute_selected_cell();
153 });
164 });
154 this.content.find('#run_all_cells').click(function () {
165 this.content.find('#run_all_cells').click(function () {
155 IPython.notebook.execute_all_cells();
166 IPython.notebook.execute_all_cells();
156 });
167 });
157 };
168 };
158
169
159
170
160 // KernelSection
171 // KernelSection
161
172
162 var KernelSection = function () {
173 var KernelSection = function () {
163 PanelSection.apply(this, arguments);
174 PanelSection.apply(this, arguments);
164 };
175 };
165
176
166
177
167 KernelSection.prototype = new PanelSection();
178 KernelSection.prototype = new PanelSection();
168
179
169
180
170 KernelSection.prototype.style = function () {
181 KernelSection.prototype.style = function () {
171 PanelSection.prototype.style.apply(this);
182 PanelSection.prototype.style.apply(this);
172 this.content.addClass('ui-helper-clearfix');
183 this.content.addClass('ui-helper-clearfix');
173 this.content.find('div.section_row').addClass('ui-helper-clearfix');
184 this.content.find('div.section_row').addClass('ui-helper-clearfix');
174 this.content.find('#int_restart').buttonset();
185 this.content.find('#int_restart').buttonset();
175 };
186 };
176
187
177
188
178 KernelSection.prototype.bind_events = function () {
189 KernelSection.prototype.bind_events = function () {
179 PanelSection.prototype.bind_events.apply(this);
190 PanelSection.prototype.bind_events.apply(this);
180 this.content.find('#restart_kernel').click(function () {
191 this.content.find('#restart_kernel').click(function () {
181 IPython.notebook.kernel.restart();
192 IPython.notebook.kernel.restart();
182 });
193 });
183 this.content.find('#int_kernel').click(function () {
194 this.content.find('#int_kernel').click(function () {
184 IPython.notebook.kernel.interrupt();
195 IPython.notebook.kernel.interrupt();
185 });
196 });
186 };
197 };
187
198
188
199
189 // HelpSection
200 // HelpSection
190
201
191 var HelpSection = function () {
202 var HelpSection = function () {
192 PanelSection.apply(this, arguments);
203 PanelSection.apply(this, arguments);
193 };
204 };
194
205
195
206
196 HelpSection.prototype = new PanelSection();
207 HelpSection.prototype = new PanelSection();
197
208
198
209
199 HelpSection.prototype.style = function () {
210 HelpSection.prototype.style = function () {
200 PanelSection.prototype.style.apply(this);
211 PanelSection.prototype.style.apply(this);
201 PanelSection.prototype.style.apply(this);
212 PanelSection.prototype.style.apply(this);
202 this.content.addClass('ui-helper-clearfix');
213 this.content.addClass('ui-helper-clearfix');
203 this.content.find('div.section_row').addClass('ui-helper-clearfix');
214 this.content.find('div.section_row').addClass('ui-helper-clearfix');
204 this.content.find('#help_buttons0').buttonset();
215 this.content.find('#help_buttons0').buttonset();
205 this.content.find('#help_buttons1').buttonset();
216 this.content.find('#help_buttons1').buttonset();
206 };
217 };
207
218
208
219
209 HelpSection.prototype.bind_events = function () {
220 HelpSection.prototype.bind_events = function () {
210 PanelSection.prototype.bind_events.apply(this);
221 PanelSection.prototype.bind_events.apply(this);
211 };
222 };
212
223
213
224
214 // Set module variables
225 // Set module variables
215
226
216 IPython.PanelSection = PanelSection;
227 IPython.PanelSection = PanelSection;
217 IPython.NotebookSection = NotebookSection;
228 IPython.NotebookSection = NotebookSection;
218 IPython.CellSection = CellSection;
229 IPython.CellSection = CellSection;
219 IPython.KernelSection = KernelSection;
230 IPython.KernelSection = KernelSection;
220 IPython.HelpSection = HelpSection;
231 IPython.HelpSection = HelpSection;
221
232
222 return IPython;
233 return IPython;
223
234
224 }(IPython));
235 }(IPython));
225
236
@@ -1,52 +1,101 b''
1
1
2 //============================================================================
2 //============================================================================
3 // Cell
3 // Cell
4 //============================================================================
4 //============================================================================
5
5
6 var IPython = (function (IPython) {
6 var IPython = (function (IPython) {
7
7
8 var utils = IPython.utils;
8 var utils = IPython.utils;
9
9
10 var SaveWidget = function (selector) {
10 var SaveWidget = function (selector) {
11 this.selector = selector;
11 this.selector = selector;
12 this.notebook_name_re = /[^/\\]+/
12 if (this.selector !== undefined) {
13 if (this.selector !== undefined) {
13 this.element = $(selector);
14 this.element = $(selector);
14 this.style();
15 this.style();
15 this.bind_events();
16 this.bind_events();
16 }
17 }
17 };
18 };
18
19
19
20
20 SaveWidget.prototype.style = function () {
21 SaveWidget.prototype.style = function () {
21 this.element.find('input#notebook_name').addClass('ui-widget ui-widget-content');
22 this.element.find('input#notebook_name').addClass('ui-widget ui-widget-content');
22 this.element.find('button#save_notebook').button();
23 this.element.find('button#save_notebook').button();
23 var left_panel_width = $('div#left_panel').outerWidth();
24 var left_panel_width = $('div#left_panel').outerWidth();
24 var left_panel_splitter_width = $('div#left_panel_splitter').outerWidth();
25 var left_panel_splitter_width = $('div#left_panel_splitter').outerWidth();
25 $('span#save_widget').css({marginLeft:left_panel_width+left_panel_splitter_width});
26 $('span#save_widget').css({marginLeft:left_panel_width+left_panel_splitter_width});
26 };
27 };
27
28
28
29
29 SaveWidget.prototype.bind_events = function () {
30 SaveWidget.prototype.bind_events = function () {
30 var that = this;
31 var that = this;
31 this.element.find('button#save_notebook').click(function () {
32 this.element.find('button#save_notebook').click(function () {
32 IPython.notebook.save_notebook(that.get_notebook_name());
33 IPython.notebook.save_notebook();
33 });
34 });
34 };
35 };
35
36
36
37
37 SaveWidget.prototype.get_notebook_name = function () {
38 SaveWidget.prototype.get_notebook_name = function () {
38 return this.element.find('input#notebook_name').attr('value');
39 return this.element.find('input#notebook_name').attr('value');
39 }
40 }
40
41
41
42
42 SaveWidget.prototype.set_notebook_name = function (name) {
43 SaveWidget.prototype.set_notebook_name = function (nbname) {
43 this.element.find('input#notebook_name').attr('value',name);
44 this.element.find('input#notebook_name').attr('value',nbname);
44 }
45 }
45
46
46
47
48 SaveWidget.prototype.get_notebook_id = function () {
49 return this.element.find('span#notebook_id').text()
50 };
51
52
53 SaveWidget.prototype.update_url = function () {
54 var notebook_id = this.get_notebook_id();
55 if (notebook_id !== '') {
56 window.history.replaceState({}, '', notebook_id);
57 };
58 };
59
60
61 SaveWidget.prototype.test_notebook_name = function () {
62 var nbname = this.get_notebook_name();
63 if (this.notebook_name_re.test(nbname)) {
64 return true;
65 } else {
66 var bad_name = $('<div/>');
67 bad_name.html(
68 "The notebook name you entered (" +
69 nbname +
70 ") is not valid. Notebook names can contain any characters except / and \\"
71 );
72 bad_name.dialog({title: 'Invalid name', modal: true});
73 return false;
74 };
75 };
76
77
78 SaveWidget.prototype.status_save = function () {
79 this.element.find('span.ui-button-text').text('Save');
80 this.element.find('button#save_notebook').button('enable');
81 };
82
83
84 SaveWidget.prototype.status_saving = function () {
85 this.element.find('span.ui-button-text').text('Saving');
86 this.element.find('button#save_notebook').button('disable');
87 };
88
89
90 SaveWidget.prototype.status_loading = function () {
91 this.element.find('span.ui-button-text').text('Loading');
92 this.element.find('button#save_notebook').button('disable');
93 };
94
95
47 IPython.SaveWidget = SaveWidget;
96 IPython.SaveWidget = SaveWidget;
48
97
49 return IPython;
98 return IPython;
50
99
51 }(IPython));
100 }(IPython));
52
101
@@ -1,150 +1,152 b''
1
1
2 //============================================================================
2 //============================================================================
3 // TextCell
3 // TextCell
4 //============================================================================
4 //============================================================================
5
5
6 var IPython = (function (IPython) {
6 var IPython = (function (IPython) {
7
7
8 var TextCell = function (notebook) {
8 var TextCell = function (notebook) {
9 IPython.Cell.apply(this, arguments);
9 IPython.Cell.apply(this, arguments);
10 this.placeholder = "Type <strong>HTML</strong> and LaTeX: $\\alpha^2$"
10 this.placeholder = "Type <strong>HTML</strong> and LaTeX: $\\alpha^2$"
11 this.rendered = false;
11 this.rendered = false;
12 };
12 };
13
13
14
14
15 TextCell.prototype = new IPython.Cell();
15 TextCell.prototype = new IPython.Cell();
16
16
17
17
18 TextCell.prototype.create_element = function () {
18 TextCell.prototype.create_element = function () {
19 var cell = $("<div>").addClass('cell text_cell border-box-sizing').
19 var cell = $("<div>").addClass('cell text_cell border-box-sizing').
20 append(
20 append(
21 $("<textarea>" + this.placeholder + "</textarea>").
21 $("<textarea>" + this.placeholder + "</textarea>").
22 addClass('text_cell_input').
22 addClass('text_cell_input').
23 attr('rows',1).
23 attr('rows',1).
24 attr('cols',80).
24 attr('cols',80).
25 autogrow()
25 autogrow()
26 ).append(
26 ).append(
27 // The tabindex=-1 makes this div focusable.
27 // The tabindex=-1 makes this div focusable.
28 $('<div></div>').addClass('text_cell_render').attr('tabindex','-1')
28 $('<div></div>').addClass('text_cell_render').attr('tabindex','-1')
29 )
29 )
30 this.element = cell;
30 this.element = cell;
31 };
31 };
32
32
33
33
34 TextCell.prototype.bind_events = function () {
34 TextCell.prototype.bind_events = function () {
35 IPython.Cell.prototype.bind_events.apply(this);
35 IPython.Cell.prototype.bind_events.apply(this);
36 var that = this;
36 var that = this;
37 this.element.keydown(function (event) {
37 this.element.keydown(function (event) {
38 if (event.which === 13) {
38 if (event.which === 13) {
39 if (that.rendered) {
39 if (that.rendered) {
40 that.edit();
40 that.edit();
41 event.preventDefault();
41 event.preventDefault();
42 };
42 };
43 };
43 };
44 });
44 });
45 };
45 };
46
46
47
47
48 TextCell.prototype.select = function () {
48 TextCell.prototype.select = function () {
49 IPython.Cell.prototype.select.apply(this);
49 IPython.Cell.prototype.select.apply(this);
50 var output = this.element.find("div.text_cell_render");
50 var output = this.element.find("div.text_cell_render");
51 output.trigger('focus');
51 output.trigger('focus');
52 };
52 };
53
53
54
54
55 TextCell.prototype.edit = function () {
55 TextCell.prototype.edit = function () {
56 if (this.rendered === true) {
56 if (this.rendered === true) {
57 var text_cell = this.element;
57 var text_cell = this.element;
58 var input = text_cell.find("textarea.text_cell_input");
58 var input = text_cell.find("textarea.text_cell_input");
59 var output = text_cell.find("div.text_cell_render");
59 var output = text_cell.find("div.text_cell_render");
60 output.hide();
60 output.hide();
61 input.show().trigger('focus');
61 input.show().trigger('focus');
62 this.rendered = false;
62 this.rendered = false;
63 };
63 };
64 };
64 };
65
65
66
66
67 TextCell.prototype.render = function () {
67 TextCell.prototype.render = function () {
68 if (this.rendered === false) {
68 if (this.rendered === false) {
69 var text_cell = this.element;
69 var text_cell = this.element;
70 var input = text_cell.find("textarea.text_cell_input");
70 var input = text_cell.find("textarea.text_cell_input");
71 var output = text_cell.find("div.text_cell_render");
71 var output = text_cell.find("div.text_cell_render");
72 var text = input.val();
72 var text = input.val();
73 if (text === "") {
73 if (text === "") {
74 text = this.placeholder;
74 text = this.placeholder;
75 input.val(text);
75 input.val(text);
76 };
76 };
77 output.html(text)
77 output.html(text)
78 input.html(text);
78 input.html(text);
79 MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
79 MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
80 input.hide();
80 input.hide();
81 output.show();
81 output.show();
82 this.rendered = true;
82 this.rendered = true;
83 };
83 };
84 };
84 };
85
85
86
86
87 TextCell.prototype.config_mathjax = function () {
87 TextCell.prototype.config_mathjax = function () {
88 var text_cell = this.element;
88 var text_cell = this.element;
89 var that = this;
89 var that = this;
90 text_cell.click(function () {
90 text_cell.click(function () {
91 that.edit();
91 that.edit();
92 }).focusout(function () {
92 }).focusout(function () {
93 that.render();
93 that.render();
94 });
94 });
95
95
96 text_cell.trigger("focusout");
96 text_cell.trigger("focusout");
97 };
97 };
98
98
99
99
100 TextCell.prototype.get_text = function() {
100 TextCell.prototype.get_text = function() {
101 return this.element.find("textarea.text_cell_input").val();
101 return this.element.find("textarea.text_cell_input").val();
102 };
102 };
103
103
104
104
105 TextCell.prototype.set_text = function(text) {
105 TextCell.prototype.set_text = function(text) {
106 this.element.find("textarea.text_cell_input").val(text);
106 this.element.find("textarea.text_cell_input").val(text);
107 this.element.find("textarea.text_cell_input").html(text);
107 this.element.find("textarea.text_cell_input").html(text);
108 this.element.find("div.text_cell_render").html(text);
108 this.element.find("div.text_cell_render").html(text);
109 };
109 };
110
110
111
111
112 TextCell.prototype.at_top = function () {
112 TextCell.prototype.at_top = function () {
113 if (this.rendered) {
113 if (this.rendered) {
114 return true;
114 return true;
115 } else {
115 } else {
116 return false;
116 return false;
117 }
117 }
118 };
118 };
119
119
120
120
121 TextCell.prototype.at_bottom = function () {
121 TextCell.prototype.at_bottom = function () {
122 if (this.rendered) {
122 if (this.rendered) {
123 return true;
123 return true;
124 } else {
124 } else {
125 return false;
125 return false;
126 }
126 }
127 };
127 };
128
128
129
129
130 TextCell.prototype.fromJSON = function (data) {
130 TextCell.prototype.fromJSON = function (data) {
131 if (data.cell_type === 'text') {
131 if (data.cell_type === 'text') {
132 this.set_text(data.text);
132 if (data.text !== undefined) {
133 this.grow(this.element.find("textarea.text_cell_input"));
133 this.set_text(data.text);
134 this.grow(this.element.find("textarea.text_cell_input"));
135 };
134 };
136 };
135 }
137 }
136
138
137
139
138 TextCell.prototype.toJSON = function () {
140 TextCell.prototype.toJSON = function () {
139 return {
141 var data = {}
140 cell_type : 'text',
142 data.cell_type = 'text';
141 text : this.get_text(),
143 data.text = this.get_text();
142 };
144 return data;
143 };
145 };
144
146
145 IPython.TextCell = TextCell;
147 IPython.TextCell = TextCell;
146
148
147 return IPython;
149 return IPython;
148
150
149 }(IPython));
151 }(IPython));
150
152
@@ -1,173 +1,186 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>IPython Notebook</title>
7 <title>IPython Notebook</title>
8
8
9 <link rel="stylesheet" href="static/jquery/css/themes/aristo/jquery-wijmo.css" type="text/css" />
9 <link rel="stylesheet" href="static/jquery/css/themes/aristo/jquery-wijmo.css" type="text/css" />
10 <!-- <link rel="stylesheet" href="static/jquery/css/themes/rocket/jquery-wijmo.css" type="text/css" /> -->
10 <!-- <link rel="stylesheet" href="static/jquery/css/themes/rocket/jquery-wijmo.css" type="text/css" /> -->
11 <!-- <link rel="stylesheet" href="static/jquery/css/themes/smoothness/jquery-ui-1.8.14.custom.css" type="text/css" />-->
11 <!-- <link rel="stylesheet" href="static/jquery/css/themes/smoothness/jquery-ui-1.8.14.custom.css" type="text/css" />-->
12
12
13 <script type="text/javascript" src="http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS_HTML" charset="utf-8"></script>
13 <script type="text/javascript" src="http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS_HTML" charset="utf-8"></script>
14 <!-- <script type='text/javascript' src='static/mathjax/MathJax.js?config=TeX-AMS_HTML' charset='utf-8'></script> -->
14 <!-- <script type='text/javascript' src='static/mathjax/MathJax.js?config=TeX-AMS_HTML' charset='utf-8'></script> -->
15 <script type="text/javascript">
15 <script type="text/javascript">
16 if (typeof MathJax == 'undefined') {
16 if (typeof MathJax == 'undefined') {
17 console.log("Trying to load local copy of MathJax");
17 console.log("Trying to load local copy of MathJax");
18 document.write(unescape("%3Cscript type='text/javascript' src='static/mathjax/MathJax.js%3Fconfig=TeX-AMS_HTML' charset='utf-8'%3E%3C/script%3E"));
18 document.write(unescape("%3Cscript type='text/javascript' src='static/mathjax/MathJax.js%3Fconfig=TeX-AMS_HTML' charset='utf-8'%3E%3C/script%3E"));
19 }
19 }
20 </script>
20 </script>
21
21
22 <link rel="stylesheet" href="static/codemirror2/lib/codemirror.css">
22 <link rel="stylesheet" href="static/codemirror2/lib/codemirror.css">
23 <link rel="stylesheet" href="static/codemirror2/mode/python/python.css">
23 <link rel="stylesheet" href="static/codemirror2/mode/python/python.css">
24
24
25 <link rel="stylesheet" href="static/css/layout.css" type="text/css" />
25 <link rel="stylesheet" href="static/css/layout.css" type="text/css" />
26 <link rel="stylesheet" href="static/css/notebook.css" type="text/css" />
26 <link rel="stylesheet" href="static/css/notebook.css" type="text/css" />
27
27
28 </head>
28 </head>
29
29
30 <body>
30 <body>
31
31
32 <div id="header">
32 <div id="header">
33 <span id="ipython_notebook"><h1>IPython Notebook</h1></span>
33 <span id="ipython_notebook"><h1>IPython Notebook</h1></span>
34 <span id="save_widget">
34 <span id="save_widget">
35 <input type="text" id="notebook_name" size="20"></textarea>
35 <input type="text" id="notebook_name" size="20"></textarea>
36 <span id="notebook_id" style="display:none">{{notebook_id}}</span>
36 <button id="save_notebook">Save</button>
37 <button id="save_notebook">Save</button>
37 </span>
38 </span>
38 <span id="kernel_status">Idle</span>
39 <span id="kernel_status">Idle</span>
39 </div>
40 </div>
40
41
41 <div id="notebook_app">
42 <div id="notebook_app">
42
43
43 <div id="left_panel">
44 <div id="left_panel">
44
45
45 <div id="notebook_section">
46 <div id="notebook_section">
46 <h3 class="section_header">Notebook</h3>
47 <h3 class="section_header">Notebook</h3>
47 <div class="section_content">
48 <div class="section_content">
48 <div class="section_row">
49 <div class="section_row">
49 <span id="new_open" class="section_row_buttons">
50 <span id="new_open" class="section_row_buttons">
50 <button id="new_notebook">New</button>
51 <button id="new_notebook">New</button>
51 <button id="open_notebook">Open</button>
52 <button id="open_notebook">Open</button>
52 </span>
53 </span>
53 <span class="section_row_header">Actions</span>
54 <span class="section_row_header">Actions</span>
54 </div>
55 </div>
56 <div class="section_row">
57 <span class="section_row_buttons">
58 <button id="download_notebook">Export</button>
59 </span>
60 <span>
61 <select id="download_format">
62 <option value="xml">xml</option>
63 <option value="json">json</option>
64 <option value="py">py</option>
65 </select>
66 </span>
67 </div>
55 </div>
68 </div>
56 </div>
69 </div>
57
70
58 <div id="cell_section">
71 <div id="cell_section">
59 <h3 class="section_header">Cell</h3>
72 <h3 class="section_header">Cell</h3>
60 <div class="section_content">
73 <div class="section_content">
61 <div class="section_row">
74 <div class="section_row">
62 <span class="section_row_buttons">
75 <span class="section_row_buttons">
63 <button id="delete_cell">Delete</button>
76 <button id="delete_cell">Delete</button>
64 </span>
77 </span>
65 <span class="section_row_header">Actions</span>
78 <span class="section_row_header">Actions</span>
66 </div>
79 </div>
67 <div class="section_row">
80 <div class="section_row">
68 <span id="insert" class="section_row_buttons">
81 <span id="insert" class="section_row_buttons">
69 <button id="insert_cell_above">Above</button>
82 <button id="insert_cell_above">Above</button>
70 <button id="insert_cell_below">Below</button>
83 <button id="insert_cell_below">Below</button>
71 </span>
84 </span>
72 <span class="button_label">Insert</span>
85 <span class="button_label">Insert</span>
73 </div>
86 </div>
74 <div class="section_row">
87 <div class="section_row">
75 <span id="move" class="section_row_buttons">
88 <span id="move" class="section_row_buttons">
76 <button id="move_cell_up">Up</button>
89 <button id="move_cell_up">Up</button>
77 <button id="move_cell_down">Down</button>
90 <button id="move_cell_down">Down</button>
78 </span>
91 </span>
79 <span class="button_label">Move</span>
92 <span class="button_label">Move</span>
80 </div>
93 </div>
81 <div class="section_row">
94 <div class="section_row">
82 <span id="cell_type" class="section_row_buttons">
95 <span id="cell_type" class="section_row_buttons">
83 <button id="to_code">Code</button>
96 <button id="to_code">Code</button>
84 <button id="to_text">Text</button>
97 <button id="to_text">Text</button>
85 </span>
98 </span>
86 <span class="button_label">Cell Type</span>
99 <span class="button_label">Cell Type</span>
87 </div>
100 </div>
88 <div class="section_row">
101 <div class="section_row">
89 <span id="toggle_output" class="section_row_buttons">
102 <span id="toggle_output" class="section_row_buttons">
90 <button id="collapse_cell">Collapse</button>
103 <button id="collapse_cell">Collapse</button>
91 <button id="expand_cell">Expand</button>
104 <button id="expand_cell">Expand</button>
92 </span>
105 </span>
93 <span class="button_label">Output</span>
106 <span class="button_label">Output</span>
94 </div>
107 </div>
95 <div class="section_row">
108 <div class="section_row">
96 <span id="run_cells" class="section_row_buttons">
109 <span id="run_cells" class="section_row_buttons">
97 <button id="run_selected_cell">Selected</button>
110 <button id="run_selected_cell">Selected</button>
98 <button id="run_all_cells">All</button>
111 <button id="run_all_cells">All</button>
99 </span>
112 </span>
100 <span class="button_label">Run</span>
113 <span class="button_label">Run</span>
101 </div>
114 </div>
102 </div>
115 </div>
103 </div>
116 </div>
104
117
105 <div id="kernel_section">
118 <div id="kernel_section">
106 <h3 class="section_header">Kernel</h3>
119 <h3 class="section_header">Kernel</h3>
107 <div class="section_content">
120 <div class="section_content">
108 <div class="section_row">
121 <div class="section_row">
109 <span id="int_restart" class="section_row_buttons">
122 <span id="int_restart" class="section_row_buttons">
110 <button id="int_kernel">Interrupt</button>
123 <button id="int_kernel">Interrupt</button>
111 <button id="restart_kernel">Restart</button>
124 <button id="restart_kernel">Restart</button>
112 </span>
125 </span>
113 <span class="section_row_header">Actions</span>
126 <span class="section_row_header">Actions</span>
114 </div>
127 </div>
115 </div>
128 </div>
116 </div>
129 </div>
117
130
118 <div id="help_section">
131 <div id="help_section">
119 <h3 class="section_header">Help</h3>
132 <h3 class="section_header">Help</h3>
120 <div class="section_content">
133 <div class="section_content">
121 <div class="section_row">
134 <div class="section_row">
122 <span id="help_buttons0" class="section_row_buttons">
135 <span id="help_buttons0" class="section_row_buttons">
123 <button id="python_help"><a href="http://docs.python.org" target="_blank">Python</a></button>
136 <button id="python_help"><a href="http://docs.python.org" target="_blank">Python</a></button>
124 <button id="ipython_help"><a href="http://ipython.org/documentation.html" target="_blank">IPython</a></button>
137 <button id="ipython_help"><a href="http://ipython.org/documentation.html" target="_blank">IPython</a></button>
125 <button id="numpy_help"><a href="http://docs.scipy.org/doc/numpy/reference/" target="_blank">NumPy</a></button>
138 <button id="numpy_help"><a href="http://docs.scipy.org/doc/numpy/reference/" target="_blank">NumPy</a></button>
126 </span>
139 </span>
127 <span class="section_row_header">Links</span>
140 <span class="section_row_header">Links</span>
128 </div>
141 </div>
129 <div class="section_row">
142 <div class="section_row">
130 <span id="help_buttons1" class="section_row_buttons">
143 <span id="help_buttons1" class="section_row_buttons">
131 <button id="matplotlib_help"><a href="http://matplotlib.sourceforge.net/" target="_blank">MPL</a></button>
144 <button id="matplotlib_help"><a href="http://matplotlib.sourceforge.net/" target="_blank">MPL</a></button>
132 <button id="scipy_help"><a href="http://docs.scipy.org/doc/scipy/reference/" target="_blank">SciPy</a></button>
145 <button id="scipy_help"><a href="http://docs.scipy.org/doc/scipy/reference/" target="_blank">SciPy</a></button>
133 <button id="sympy_help"><a href="http://docs.sympy.org/dev/index.html" target="_blank">SymPy</a></button>
146 <button id="sympy_help"><a href="http://docs.sympy.org/dev/index.html" target="_blank">SymPy</a></button>
134 </span>
147 </span>
135 </div>
148 </div>
136 </div>
149 </div>
137 </div>
150 </div>
138
151
139 </div>
152 </div>
140 <div id="left_panel_splitter"></div>
153 <div id="left_panel_splitter"></div>
141 <div id="notebook_panel">
154 <div id="notebook_panel">
142 <div id="notebook"></div>
155 <div id="notebook"></div>
143 <div id="pager_splitter"></div>
156 <div id="pager_splitter"></div>
144 <div id="pager"></div>
157 <div id="pager"></div>
145 </div>
158 </div>
146
159
147 </div>
160 </div>
148
161
149 <script src="static/jquery/js/jquery-1.6.2.min.js" type="text/javascript" charset="utf-8"></script>
162 <script src="static/jquery/js/jquery-1.6.2.min.js" type="text/javascript" charset="utf-8"></script>
150 <script src="static/jquery/js/jquery-ui-1.8.14.custom.min.js" type="text/javascript" charset="utf-8"></script>
163 <script src="static/jquery/js/jquery-ui-1.8.14.custom.min.js" type="text/javascript" charset="utf-8"></script>
151 <script src="static/jquery/js/jquery.autogrow.js" type="text/javascript" charset="utf-8"></script>
164 <script src="static/jquery/js/jquery.autogrow.js" type="text/javascript" charset="utf-8"></script>
152 <script src="static/js/namespace.js" type="text/javascript" charset="utf-8"></script>
165 <script src="static/js/namespace.js" type="text/javascript" charset="utf-8"></script>
153 <script src="static/js/utils.js" type="text/javascript" charset="utf-8"></script>
166 <script src="static/js/utils.js" type="text/javascript" charset="utf-8"></script>
154 <script src="static/js/cell.js" type="text/javascript" charset="utf-8"></script>
167 <script src="static/js/cell.js" type="text/javascript" charset="utf-8"></script>
155 <script src="static/js/codecell.js" type="text/javascript" charset="utf-8"></script>
168 <script src="static/js/codecell.js" type="text/javascript" charset="utf-8"></script>
156 <script src="static/js/textcell.js" type="text/javascript" charset="utf-8"></script>
169 <script src="static/js/textcell.js" type="text/javascript" charset="utf-8"></script>
157 <script src="static/js/kernel.js" type="text/javascript" charset="utf-8"></script>
170 <script src="static/js/kernel.js" type="text/javascript" charset="utf-8"></script>
158 <script src="static/js/kernelstatus.js" type="text/javascript" charset="utf-8"></script>
171 <script src="static/js/kernelstatus.js" type="text/javascript" charset="utf-8"></script>
159 <script src="static/js/layout.js" type="text/javascript" charset="utf-8"></script>
172 <script src="static/js/layout.js" type="text/javascript" charset="utf-8"></script>
160 <script src="static/js/savewidget.js" type="text/javascript" charset="utf-8"></script>
173 <script src="static/js/savewidget.js" type="text/javascript" charset="utf-8"></script>
161 <script src="static/js/pager.js" type="text/javascript" charset="utf-8"></script>
174 <script src="static/js/pager.js" type="text/javascript" charset="utf-8"></script>
162 <script src="static/js/panelsection.js" type="text/javascript" charset="utf-8"></script>
175 <script src="static/js/panelsection.js" type="text/javascript" charset="utf-8"></script>
163 <script src="static/js/leftpanel.js" type="text/javascript" charset="utf-8"></script>
176 <script src="static/js/leftpanel.js" type="text/javascript" charset="utf-8"></script>
164 <script src="static/js/notebook.js" type="text/javascript" charset="utf-8"></script>
177 <script src="static/js/notebook.js" type="text/javascript" charset="utf-8"></script>
165 <script src="static/js/notebook_main.js" type="text/javascript" charset="utf-8"></script>
178 <script src="static/js/notebook_main.js" type="text/javascript" charset="utf-8"></script>
166 <script src="static/codemirror2/lib/codemirror.js"></script>
179 <script src="static/codemirror2/lib/codemirror.js"></script>
167 <script src="static/codemirror2/mode/python/python.js"></script>
180 <script src="static/codemirror2/mode/python/python.js"></script>
168
181
169 </body>
182 </body>
170
183
171 </html>
184 </html>
172
185
173
186
@@ -1,190 +1,195 b''
1 import json
1 import json
2 from xml.etree import ElementTree as ET
2 from xml.etree import ElementTree as ET
3 import re
3 import re
4
4
5 from IPython.nbformat import v2
5 from IPython.nbformat import v2
6 from IPython.nbformat import v1
6 from IPython.nbformat import v1
7
7
8 from IPython.nbformat.v2 import (
9 NotebookNode,
10 new_code_cell, new_text_cell, new_notebook, new_output, new_worksheet
11 )
12
8
13
9 current_nbformat = 2
14 current_nbformat = 2
10
15
11
16
12 class NBFormatError(Exception):
17 class NBFormatError(Exception):
13 pass
18 pass
14
19
15
20
16 def parse_json(s, **kwargs):
21 def parse_json(s, **kwargs):
17 """Parse a string into a (nbformat, dict) tuple."""
22 """Parse a string into a (nbformat, dict) tuple."""
18 d = json.loads(s, **kwargs)
23 d = json.loads(s, **kwargs)
19 nbformat = d.get('nbformat',1)
24 nbformat = d.get('nbformat',1)
20 return nbformat, d
25 return nbformat, d
21
26
22
27
23 def parse_xml(s, **kwargs):
28 def parse_xml(s, **kwargs):
24 """Parse a string into a (nbformat, etree) tuple."""
29 """Parse a string into a (nbformat, etree) tuple."""
25 root = ET.fromstring(s)
30 root = ET.fromstring(s)
26 nbformat_e = root.find('nbformat')
31 nbformat_e = root.find('nbformat')
27 if nbformat_e is not None:
32 if nbformat_e is not None:
28 nbformat = int(nbformat_e.text)
33 nbformat = int(nbformat_e.text)
29 else:
34 else:
30 raise NBFormatError('No nbformat version found')
35 raise NBFormatError('No nbformat version found')
31 return nbformat, root
36 return nbformat, root
32
37
33
38
34 def parse_py(s, **kwargs):
39 def parse_py(s, **kwargs):
35 """Parse a string into a (nbformat, string) tuple."""
40 """Parse a string into a (nbformat, string) tuple."""
36 pattern = r'# <nbformat>(?P<nbformat>\d+)</nbformat>'
41 pattern = r'# <nbformat>(?P<nbformat>\d+)</nbformat>'
37 m = re.search(pattern,s)
42 m = re.search(pattern,s)
38 if m is not None:
43 if m is not None:
39 nbformat = int(m.group('nbformat'))
44 nbformat = int(m.group('nbformat'))
40 else:
45 else:
41 raise NBFormatError('No nbformat version found')
46 raise NBFormatError('No nbformat version found')
42 return nbformat, s
47 return nbformat, s
43
48
44
49
45 def reads_json(s, **kwargs):
50 def reads_json(s, **kwargs):
46 """Read a JSON notebook from a string and return the NotebookNode object."""
51 """Read a JSON notebook from a string and return the NotebookNode object."""
47 nbformat, d = parse_json(s, **kwargs)
52 nbformat, d = parse_json(s, **kwargs)
48 if nbformat == 1:
53 if nbformat == 1:
49 nb = v1.to_notebook_json(d, **kwargs)
54 nb = v1.to_notebook_json(d, **kwargs)
50 nb = v2.convert_to_this_nbformat(nb, orig_version=1)
55 nb = v2.convert_to_this_nbformat(nb, orig_version=1)
51 elif nbformat == 2:
56 elif nbformat == 2:
52 nb = v2.to_notebook_json(d, **kwargs)
57 nb = v2.to_notebook_json(d, **kwargs)
53 else:
58 else:
54 raise NBFormatError('Unsupported JSON nbformat version: %i' % nbformat)
59 raise NBFormatError('Unsupported JSON nbformat version: %i' % nbformat)
55 return nb
60 return nb
56
61
57
62
58 def writes_json(nb, **kwargs):
63 def writes_json(nb, **kwargs):
59 return v2.writes_json(nb, **kwargs)
64 return v2.writes_json(nb, **kwargs)
60
65
61
66
62 def reads_xml(s, **kwargs):
67 def reads_xml(s, **kwargs):
63 """Read an XML notebook from a string and return the NotebookNode object."""
68 """Read an XML notebook from a string and return the NotebookNode object."""
64 nbformat, root = parse_xml(s, **kwargs)
69 nbformat, root = parse_xml(s, **kwargs)
65 if nbformat == 2:
70 if nbformat == 2:
66 nb = v2.to_notebook_xml(root, **kwargs)
71 nb = v2.to_notebook_xml(root, **kwargs)
67 else:
72 else:
68 raise NBFormatError('Unsupported XML nbformat version: %i' % nbformat)
73 raise NBFormatError('Unsupported XML nbformat version: %i' % nbformat)
69 return nb
74 return nb
70
75
71
76
72 def writes_xml(nb, **kwargs):
77 def writes_xml(nb, **kwargs):
73 return v2.writes_xml(nb, **kwargs)
78 return v2.writes_xml(nb, **kwargs)
74
79
75
80
76 def reads_py(s, **kwargs):
81 def reads_py(s, **kwargs):
77 """Read a .py notebook from a string and return the NotebookNode object."""
82 """Read a .py notebook from a string and return the NotebookNode object."""
78 nbformat, s = parse_py(s, **kwargs)
83 nbformat, s = parse_py(s, **kwargs)
79 if nbformat == 2:
84 if nbformat == 2:
80 nb = v2.to_notebook_py(s, **kwargs)
85 nb = v2.to_notebook_py(s, **kwargs)
81 else:
86 else:
82 raise NBFormatError('Unsupported PY nbformat version: %i' % nbformat)
87 raise NBFormatError('Unsupported PY nbformat version: %i' % nbformat)
83 return nb
88 return nb
84
89
85
90
86 def writes_py(nb, **kwargs):
91 def writes_py(nb, **kwargs):
87 return v2.writes_py(nb, **kwargs)
92 return v2.writes_py(nb, **kwargs)
88
93
89
94
90 # High level API
95 # High level API
91
96
92
97
93 def reads(s, format, **kwargs):
98 def reads(s, format, **kwargs):
94 """Read a notebook from a string and return the NotebookNode object.
99 """Read a notebook from a string and return the NotebookNode object.
95
100
96 This function properly handles notebooks of any version. The notebook
101 This function properly handles notebooks of any version. The notebook
97 returned will always be in the current version's format.
102 returned will always be in the current version's format.
98
103
99 Parameters
104 Parameters
100 ----------
105 ----------
101 s : str
106 s : str
102 The raw string to read the notebook from.
107 The raw string to read the notebook from.
103 format : ('xml','json','py')
108 format : ('xml','json','py')
104 The format that the string is in.
109 The format that the string is in.
105
110
106 Returns
111 Returns
107 -------
112 -------
108 nb : NotebookNode
113 nb : NotebookNode
109 The notebook that was read.
114 The notebook that was read.
110 """
115 """
111 if format == 'xml':
116 if format == 'xml':
112 return reads_xml(s, **kwargs)
117 return reads_xml(s, **kwargs)
113 elif format == 'json':
118 elif format == 'json':
114 return reads_json(s, **kwargs)
119 return reads_json(s, **kwargs)
115 elif format == 'py':
120 elif format == 'py':
116 return reads_py(s, **kwargs)
121 return reads_py(s, **kwargs)
117 else:
122 else:
118 raise NBFormatError('Unsupported format: %s' % format)
123 raise NBFormatError('Unsupported format: %s' % format)
119
124
120
125
121 def writes(nb, format, **kwargs):
126 def writes(nb, format, **kwargs):
122 """Write a notebook to a string in a given format in the current nbformat version.
127 """Write a notebook to a string in a given format in the current nbformat version.
123
128
124 This function always writes the notebook in the current nbformat version.
129 This function always writes the notebook in the current nbformat version.
125
130
126 Parameters
131 Parameters
127 ----------
132 ----------
128 nb : NotebookNode
133 nb : NotebookNode
129 The notebook to write.
134 The notebook to write.
130 format : ('xml','json','py')
135 format : ('xml','json','py')
131 The format to write the notebook in.
136 The format to write the notebook in.
132
137
133 Returns
138 Returns
134 -------
139 -------
135 s : str
140 s : str
136 The notebook string.
141 The notebook string.
137 """
142 """
138 if format == 'xml':
143 if format == 'xml':
139 return writes_xml(nb, **kwargs)
144 return writes_xml(nb, **kwargs)
140 elif format == 'json':
145 elif format == 'json':
141 return writes_json(nb, **kwargs)
146 return writes_json(nb, **kwargs)
142 elif format == 'py':
147 elif format == 'py':
143 return writes_py(nb, **kwargs)
148 return writes_py(nb, **kwargs)
144 else:
149 else:
145 raise NBFormatError('Unsupported format: %s' % format)
150 raise NBFormatError('Unsupported format: %s' % format)
146
151
147
152
148 def read(fp, format, **kwargs):
153 def read(fp, format, **kwargs):
149 """Read a notebook from a file and return the NotebookNode object.
154 """Read a notebook from a file and return the NotebookNode object.
150
155
151 This function properly handles notebooks of any version. The notebook
156 This function properly handles notebooks of any version. The notebook
152 returned will always be in the current version's format.
157 returned will always be in the current version's format.
153
158
154 Parameters
159 Parameters
155 ----------
160 ----------
156 fp : file
161 fp : file
157 Any file-like object with a read method.
162 Any file-like object with a read method.
158 format : ('xml','json','py')
163 format : ('xml','json','py')
159 The format that the string is in.
164 The format that the string is in.
160
165
161 Returns
166 Returns
162 -------
167 -------
163 nb : NotebookNode
168 nb : NotebookNode
164 The notebook that was read.
169 The notebook that was read.
165 """
170 """
166 return reads(fp.read(), format, **kwargs)
171 return reads(fp.read(), format, **kwargs)
167
172
168
173
169 def write(nb, fp, format, **kwargs):
174 def write(nb, fp, format, **kwargs):
170 """Write a notebook to a file in a given format in the current nbformat version.
175 """Write a notebook to a file in a given format in the current nbformat version.
171
176
172 This function always writes the notebook in the current nbformat version.
177 This function always writes the notebook in the current nbformat version.
173
178
174 Parameters
179 Parameters
175 ----------
180 ----------
176 nb : NotebookNode
181 nb : NotebookNode
177 The notebook to write.
182 The notebook to write.
178 fp : file
183 fp : file
179 Any file-like object with a write method.
184 Any file-like object with a write method.
180 format : ('xml','json','py')
185 format : ('xml','json','py')
181 The format to write the notebook in.
186 The format to write the notebook in.
182
187
183 Returns
188 Returns
184 -------
189 -------
185 s : str
190 s : str
186 The notebook string.
191 The notebook string.
187 """
192 """
188 return fp.write(writes(nb, format, **kwargs))
193 return fp.write(writes(nb, format, **kwargs))
189
194
190
195
@@ -1,55 +1,56 b''
1 """Read and write notebooks as regular .py files."""
1 """Read and write notebooks as regular .py files."""
2
2
3 from .rwbase import NotebookReader, NotebookWriter
3 from .rwbase import NotebookReader, NotebookWriter
4 from .nbbase import new_code_cell, new_worksheet, new_notebook
4 from .nbbase import new_code_cell, new_worksheet, new_notebook
5
5
6
6
7 class PyReader(NotebookReader):
7 class PyReader(NotebookReader):
8
8
9 def reads(self, s, **kwargs):
9 def reads(self, s, **kwargs):
10 return self.to_notebook(s,**kwargs)
10 return self.to_notebook(s,**kwargs)
11
11
12 def to_notebook(self, s, **kwargs):
12 def to_notebook(self, s, **kwargs):
13 lines = s.splitlines()
13 lines = s.splitlines()
14 cells = []
14 cells = []
15 cell_lines = []
15 cell_lines = []
16 for line in lines:
16 for line in lines:
17 if line.startswith(u'# <codecell>'):
17 if line.startswith(u'# <codecell>'):
18 cell_lines = []
18 cell_lines = []
19 if line.startswith(u'# </codecell>'):
19 if line.startswith(u'# </codecell>'):
20 code = u'\n'.join(cell_lines)
20 code = u'\n'.join(cell_lines)
21 code = code.strip(u'\n')
21 code = code.strip(u'\n')
22 if code:
22 if code:
23 cells.append(new_code_cell(input=code))
23 cells.append(new_code_cell(input=code))
24 else:
24 else:
25 cell_lines.append(line)
25 cell_lines.append(line)
26 ws = new_worksheet(cells=cells)
26 ws = new_worksheet(cells=cells)
27 nb = new_notebook(worksheets=[ws])
27 nb = new_notebook(worksheets=[ws])
28 return nb
28 return nb
29
29
30
30
31 class PyWriter(NotebookWriter):
31 class PyWriter(NotebookWriter):
32
32
33 def writes(self, nb, **kwargs):
33 def writes(self, nb, **kwargs):
34 lines = []
34 lines = []
35 lines.extend(['# <nbformat>2</nbformat>',''])
35 lines.extend(['# <nbformat>2</nbformat>',''])
36 for ws in nb.worksheets:
36 for ws in nb.worksheets:
37 for cell in ws.cells:
37 for cell in ws.cells:
38 if cell.cell_type == 'code':
38 if cell.cell_type == 'code':
39 input = cell.input
39 input = cell.get('input')
40 lines.extend([u'# <codecell>',u''])
40 if input is not None:
41 lines.extend(input.splitlines())
41 lines.extend([u'# <codecell>',u''])
42 lines.extend([u'',u'# </codecell>'])
42 lines.extend(input.splitlines())
43 lines.extend([u'',u'# </codecell>'])
43 lines.append('')
44 lines.append('')
44 return unicode('\n'.join(lines))
45 return unicode('\n'.join(lines))
45
46
46
47
47 _reader = PyReader()
48 _reader = PyReader()
48 _writer = PyWriter()
49 _writer = PyWriter()
49
50
50 reads = _reader.reads
51 reads = _reader.reads
51 read = _reader.read
52 read = _reader.read
52 to_notebook = _reader.to_notebook
53 to_notebook = _reader.to_notebook
53 write = _writer.write
54 write = _writer.write
54 writes = _writer.writes
55 writes = _writer.writes
55
56
General Comments 0
You need to be logged in to leave comments. Login now