##// END OF EJS Templates
Work on the server side of the html notebook.
Brian Granger -
Show More
@@ -0,0 +1,98 b''
1 import signal
2 import sys
3 import uuid
4
5 from IPython.zmq.ipkernel import launch_kernel
6 from session import SessionManager
7
8
9 class KernelManager(object):
10
11 ip = '127.0.0.1'
12
13 def __init__(self, context):
14 self.context = context
15 self._kernels = {}
16
17 @property
18 def kernel_ids(self):
19 return self._kernels.keys()
20
21 def __len__(self):
22 return len(self.kernel_ids)
23
24 def __contains__(self, kernel_id):
25 if kernel_id in self.kernel_ids:
26 return True
27 else:
28 return False
29
30 def start_kernel(self):
31 kid = str(uuid.uuid4())
32 (process, shell_port, iopub_port, stdin_port, hb_port) = launch_kernel()
33 d = dict(
34 process = process,
35 stdin_port = stdin_port,
36 iopub_port = iopub_port,
37 shell_port = shell_port,
38 hb_port = hb_port,
39 session_manager = SessionManager(self, kid, self.context)
40 )
41 self._kernels[kid] = d
42 return kid
43
44 def kill_kernel(self, kernel_id):
45 kernel_process = self.get_kernel_process(kernel_id)
46 if kernel_process is not None:
47 # Attempt to kill the kernel.
48 try:
49 kernel_process.kill()
50 except OSError, e:
51 # In Windows, we will get an Access Denied error if the process
52 # has already terminated. Ignore it.
53 if not (sys.platform == 'win32' and e.winerror == 5):
54 raise
55 del self._kernels[kernel_id]
56
57 def interrupt_kernel(self, kernel_id):
58 kernel_process = self.get_kernel_process(kernel_id)
59 if kernel_process is not None:
60 if sys.platform == 'win32':
61 from parentpoller import ParentPollerWindows as Poller
62 Poller.send_interrupt(kernel_process.win32_interrupt_event)
63 else:
64 kernel_process.send_signal(signal.SIGINT)
65
66 def signal_kernel(self, kernel_id, signum):
67 """ Sends a signal to the kernel. Note that since only SIGTERM is
68 supported on Windows, this function is only useful on Unix systems.
69 """
70 kernel_process = self.get_kernel_process(kernel_id)
71 if kernel_process is not None:
72 kernel_process.send_signal(signum)
73
74 def get_kernel_process(self, kernel_id):
75 d = self._kernels.get(kernel_id)
76 if d is not None:
77 return d['process']
78 else:
79 raise KeyError("Kernel with id not found: %s" % kernel_id)
80
81 def get_kernel_ports(self, kernel_id):
82 d = self._kernels.get(kernel_id)
83 if d is not None:
84 dcopy = d.copy()
85 dcopy.pop('process')
86 return dcopy
87 else:
88 raise KeyError("Kernel with id not found: %s" % kernel_id)
89
90 def get_session_manager(self, kernel_id):
91 d = self._kernels.get(kernel_id)
92 if d is not None:
93 return d['session_manager']
94 else:
95 raise KeyError("Kernel with id not found: %s" % kernel_id)
96
97
98
@@ -0,0 +1,56 b''
1 import logging
2
3 import zmq
4 from zmq.eventloop.zmqstream import ZMQStream
5
6
7 class SessionManager(object):
8
9 def __init__(self, kernel_manager, kernel_id, context):
10 self.context = context
11 self.kernel_manager = kernel_manager
12 self.kernel_id = kernel_id
13 self._sessions = {}
14
15 def __del__(self):
16 self.stop_all()
17
18 def start_session(self, session_id):
19 ports = self.kernel_manager.get_kernel_ports(self.kernel_id)
20 iopub_stream = self.create_connected_stream(ports['iopub_port'], zmq.SUB)
21 shell_stream = self.create_connected_stream(ports['shell_port'], zmq.XREQ)
22 self._sessions[session_id] = dict(
23 iopub_stream = iopub_stream,
24 shell_stream = shell_stream
25 )
26
27 def stop_session(self, session_id):
28 session_dict = self._sessions.get(session_id)
29 if session_dict is not None:
30 for name, stream in session_dict.items():
31 stream.close()
32 del self._sessions[session_id]
33
34 def stop_all(self):
35 for session_id in self._sessions.keys():
36 self.stop_session(session_id)
37
38 def create_connected_stream(self, port, socket_type):
39 sock = self.context.socket(socket_type)
40 addr = "tcp://%s:%i" % (self.kernel_manager.ip, port)
41 logging.info("Connecting to: %s" % addr)
42 sock.connect(addr)
43 return ZMQStream(sock)
44
45 def get_stream(self, session_id, stream_name):
46 session_dict = self._sessions.get(session_id)
47 if session_dict is not None:
48 return session_dict[stream_name]
49 else:
50 raise KeyError("Session with id not found: %s" % session_id)
51
52 def get_iopub_stream(self, session_id):
53 return self.get_stream(session_id, 'iopub_stream')
54
55 def get_shell_stream(self, session_id):
56 return self.get_stream(session_id, 'shell_stream')
@@ -0,0 +1,80 b''
1 import json
2
3 from tornado import web
4
5 import logging
6
7
8 class ZMQHandler(web.RequestHandler):
9
10 def get_stream(self):
11 """Get the ZMQStream for this request."""
12 raise NotImplementedError('Implement get_stream() in a subclass.')
13
14 def _save_method_args(self, *args, **kwargs):
15 """Save the args and kwargs to get/post/put/delete for future use.
16
17 These arguments are not saved in the request or handler objects, but
18 are often needed by methods such as get_stream().
19 """
20 self._method_args = args
21 self._method_kwargs = kwargs
22
23 def _handle_msgs(self, msg):
24 msgs = [msg]
25 stream = self.get_stream()
26 stream.on_recv(lambda m: msgs.append(json.loads(m)))
27 stream.flush()
28 stream.stop_on_recv()
29 logging.info("Reply: %r" % msgs)
30 self.write(json.dumps(msgs))
31 self.finish()
32
33
34 class ZMQPubHandler(ZMQHandler):
35
36 SUPPORTED_METHODS = ("POST",)
37
38 def post(self, *args, **kwargs):
39 self._save_method_args(*args, **kwargs)
40 try:
41 msg = json.loads(self.request.body)
42 except:
43 self.send_error(status_code=415)
44 else:
45 logging.info("Request: %r" % msg)
46 self.get_stream().send_json(msg)
47
48
49 class ZMQSubHandler(ZMQHandler):
50
51 SUPPORTED_METHODS = ("GET",)
52
53 @web.asynchronous
54 def get(self, *args, **kwargs):
55 self._save_method_args(*args, **kwargs)
56 self.get_stream().on_recv(self._handle_msgs)
57
58
59 class ZMQXReqHandler(ZMQHandler):
60
61 SUPPORTED_METHODS = ("POST",)
62
63 @web.asynchronous
64 def post(self, *args, **kwargs):
65 self._save_method_args(*args, **kwargs)
66 logging.info("request: %r" % self.request)
67 try:
68 msg = json.loads(self.request.body)
69 except:
70 self.send_error(status_code=415)
71 else:
72 logging.info("Reply: %r" % msg)
73 stream = self.get_stream()
74 stream.send_json(msg)
75 stream.on_recv(self._handle_msgs)
76
77
78
79
80 No newline at end of file
@@ -0,0 +1,23 b''
1 """A simple WebSocket to ZMQ forwarder."""
2
3 from tornado import websocket
4
5 class ZMQWebSocketBridge(websocket.WebSocketHandler):
6 """A handler to forward between a WebSocket at ZMQ socket."""
7
8 def open(self):
9 self.stream
10
11 @property
12 def stream(self):
13 raise NotImplementedError("stream property must be implemented in a subclass")
14
15 def on_message(self, message):
16 self.stream.send(message)
17
18 def on_zmq_reply(self, reply_list):
19 for part in reply_list:
20 self.write_message(part)
21
22 def on_close(self):
23 print "WebSocket closed"
@@ -1,40 +1,124 b''
1
1 import json
2 import logging
2 3 import os
4 import uuid
5
6 import zmq
3 7
4 import tornado.httpserver
8 # Install the pyzmq ioloop. This has to be done before anything else from
9 # tornado is imported.
10 from zmq.eventloop.zmqstream import ZMQStream
11 from zmq.eventloop import ioloop
5 12 import tornado.ioloop
6 import tornado.options
7 import tornado.web
13 tornado.ioloop = ioloop
14
15 from tornado import httpserver
16 from tornado import options
17 from tornado import web
18 from tornado import websocket
19
20 from kernelmanager import KernelManager
8 21
9 from tornado.options import define, options
22 options.define("port", default=8888, help="run on the given port", type=int)
10 23
11 define("port", default=8888, help="run on the given port", type=int)
24 _kernel_id_regex = r"(?P<kernel_id>\w+-\w+-\w+-\w+-\w+)"
25 _session_id_regex = r"(?P<session_id>\w+)"
12 26
13 class MainHandler(tornado.web.RequestHandler):
27
28 class MainHandler(web.RequestHandler):
14 29 def get(self):
15 30 self.render('notebook.html')
16 31
17 32
18 class NotebookApplication(tornado.web.Application):
33 class KernelHandler(web.RequestHandler):
34
35 def get(self):
36 self.write(json.dumps(self.application.kernel_manager.kernel_ids))
37
38 def post(self):
39 kid = self.application.kernel_manager.start_kernel()
40 logging.info("Starting kernel: %s" % kid)
41 self.write(json.dumps(kid))
42
43
44 class SessionHandler(web.RequestHandler):
45
46 def post(self, *args, **kwargs):
47 kernel_id = kwargs['kernel_id']
48 session_id = kwargs['session_id']
49 logging.info("Starting session: %s, %s" % (kernel_id,session_id))
50 km = self.application.kernel_manager
51 sm = km.get_session_manager(kernel_id)
52 sm.start_session(session_id)
53 self.finish()
54
55
56 class ZMQStreamHandler(websocket.WebSocketHandler):
57
58 stream_name = ''
59
60 def open(self, *args, **kwargs):
61 kernel_id = kwargs['kernel_id']
62 session_id = kwargs['session_id']
63 logging.info("Connection open: %s, %s" % (kernel_id,session_id))
64 sm = self.application.kernel_manager.get_session_manager(kernel_id)
65 method_name = "get_%s_stream" % self.stream_name
66 method = getattr(sm, method_name)
67 self.zmq_stream = method(session_id)
68 self.zmq_stream.on_recv(self._on_zmq_reply)
69 self.session_manager = sm
70 self.session_id = session_id
71
72 def on_message(self, msg):
73 logging.info("Message received: %r" % msg)
74 self.zmq_stream.send(msg)
75
76 def on_close(self):
77 logging.info("Connection closed: %s, %s" % (kernel_id,session_id))
78 self.zmq_stream.close()
79
80 def _on_zmq_reply(self, msg):
81 logging.info("Message reply: %r" % msg)
82 self.write_message(msg)
83
84
85 class IOPubStreamHandler(ZMQStreamHandler):
86
87 stream_name = 'iopub'
88
89
90 class ShellStreamHandler(ZMQStreamHandler):
91
92 stream_name = 'shell'
93
94
95 class NotebookApplication(web.Application):
96
19 97 def __init__(self):
20 98 handlers = [
21 (r"/", MainHandler)
99 (r"/", MainHandler),
100 (r"/kernels", KernelHandler),
101 (r"/kernels/%s/sessions/%s" % (_kernel_id_regex,_session_id_regex), SessionHandler),
102 (r"/kernels/%s/sessions/%s/iopub" % (_kernel_id_regex,_session_id_regex), IOPubStreamHandler),
103 (r"/kernels/%s/sessions/%s/shell" % (_kernel_id_regex,_session_id_regex), ShellStreamHandler),
22 104 ]
23 105 settings = dict(
24 106 template_path=os.path.join(os.path.dirname(__file__), "templates"),
25 107 static_path=os.path.join(os.path.dirname(__file__), "static"),
26 108 )
27 tornado.web.Application.__init__(self, handlers, **settings)
109 web.Application.__init__(self, handlers, **settings)
110 self.context = zmq.Context()
111 self.kernel_manager = KernelManager(self.context)
28 112
29 113
30 114 def main():
31 tornado.options.parse_command_line()
115 options.parse_command_line()
32 116 application = NotebookApplication()
33 http_server = tornado.httpserver.HTTPServer(application)
34 http_server.listen(options.port)
35 tornado.ioloop.IOLoop.instance().start()
117 http_server = httpserver.HTTPServer(application)
118 http_server.listen(options.options.port)
119 ioloop.IOLoop.instance().start()
36 120
37 121
38 122 if __name__ == "__main__":
39 123 main()
40 124
@@ -1,539 +1,582 b''
1 1 var IPYTHON = {};
2 2
3 3
4 4 //============================================================================
5 5 // Notebook
6 6 //============================================================================
7 7
8 8
9 9 var Notebook = function (selector) {
10 10 this.element = $(selector);
11 this.element.scroll();
11 12 this.element.data("notebook", this);
12 13 this.next_prompt_number = 1;
13 14 this.bind_events();
14 }
15 };
15 16
16 17
17 18 Notebook.prototype.bind_events = function () {
18 19 var that = this;
19 20 $(document).keydown(function (event) {
20 21 console.log(event);
21 22 if (event.which == 38 && event.shiftKey) {
22 23 event.preventDefault();
23 24 that.select_prev();
24 25 } else if (event.which == 40 && event.shiftKey) {
25 26 event.preventDefault();
26 27 that.select_next();
27 28 } else if (event.which == 13 && event.shiftKey) {
28 29 // The focus is not quite working here.
29 30 event.preventDefault();
30 31 that.insert_code_cell_after();
31 32 }
32 33 });
33 34 };
34 35
35 36
36 37 // Cell indexing, retrieval, etc.
37 38
38 39
39 40 Notebook.prototype.cell_elements = function () {
40 41 return this.element.children("div.cell");
41 42 }
42 43
43 44
44 45 Notebook.prototype.ncells = function (cell) {
45 46 return this.cell_elements().length;
46 47 }
47 48
48 49
49 50 // TODO: we are often calling cells as cells()[i], which we should optimize
50 51 // to cells(i) or a new method.
51 52 Notebook.prototype.cells = function () {
52 53 return this.cell_elements().toArray().map(function (e) {
53 54 return $(e).data("cell");
54 55 });
55 56 }
56 57
57 58
58 59 Notebook.prototype.find_cell_index = function (cell) {
59 60 var result = null;
60 61 this.cell_elements().filter(function (index) {
61 62 if ($(this).data("cell") === cell) {
62 63 result = index;
63 64 };
64 65 });
65 66 return result;
66 67 };
67 68
68 69
69 70 Notebook.prototype.index_or_selected = function (index) {
70 71 return index || this.selected_index() || 0;
71 72 }
72 73
73 74
74 75 Notebook.prototype.select = function (index) {
75 76 if (index !== undefined && index >= 0 && index < this.ncells()) {
76 77 if (this.selected_index() !== null) {
77 78 this.selected_cell().unselect();
78 79 };
79 80 this.cells()[index].select();
80 81 };
81 82 return this;
82 83 };
83 84
84 85
85 86 Notebook.prototype.select_next = function () {
86 87 var index = this.selected_index();
87 88 if (index !== null && index >= 0 && (index+1) < this.ncells()) {
88 89 this.select(index+1);
89 90 };
90 91 return this;
91 92 };
92 93
93 94
94 95 Notebook.prototype.select_prev = function () {
95 96 var index = this.selected_index();
96 97 if (index !== null && index >= 0 && (index-1) < this.ncells()) {
97 98 this.select(index-1);
98 99 };
99 100 return this;
100 101 };
101 102
102 103
103 104 Notebook.prototype.selected_index = function () {
104 105 var result = null;
105 106 this.cell_elements().filter(function (index) {
106 107 if ($(this).data("cell").selected === true) {
107 108 result = index;
108 109 };
109 110 });
110 111 return result;
111 112 };
112 113
113 114
114 115 Notebook.prototype.selected_cell = function () {
115 116 return this.cell_elements().eq(this.selected_index()).data("cell");
116 117 }
117 118
118 119
119 120 // Cell insertion, deletion and moving.
120 121
121 122
122 123 Notebook.prototype.delete_cell = function (index) {
123 124 var i = index || this.selected_index();
124 125 if (i !== null && i >= 0 && i < this.ncells()) {
125 126 this.cell_elements().eq(i).remove();
126 127 if (i === (this.ncells())) {
127 128 this.select(i-1);
128 129 } else {
129 130 this.select(i);
130 131 };
131 132 };
132 133 return this;
133 134 };
134 135
135 136
136 137 Notebook.prototype.append_cell = function (cell) {
137 138 this.element.append(cell.element);
138 139 return this;
139 140 };
140 141
141 142
142 143 Notebook.prototype.insert_cell_after = function (cell, index) {
143 144 var ncells = this.ncells();
144 145 if (ncells === 0) {
145 146 this.append_cell(cell);
146 147 return this;
147 148 };
148 149 if (index >= 0 && index < ncells) {
149 150 this.cell_elements().eq(index).after(cell.element);
150 151 };
151 152 return this
152 153 };
153 154
154 155
155 156 Notebook.prototype.insert_cell_before = function (cell, index) {
156 157 var ncells = this.ncells();
157 158 if (ncells === 0) {
158 159 this.append_cell(cell);
159 160 return this;
160 161 };
161 162 if (index >= 0 && index < ncells) {
162 163 this.cell_elements().eq(index).before(cell.element);
163 164 };
164 165 return this;
165 166 };
166 167
167 168
168 169 Notebook.prototype.move_cell_up = function (index) {
169 170 var i = index || this.selected_index();
170 171 if (i !== null && i < this.ncells() && i > 0) {
171 172 var pivot = this.cell_elements().eq(i-1);
172 173 var tomove = this.cell_elements().eq(i);
173 174 if (pivot !== null && tomove !== null) {
174 175 tomove.detach();
175 176 pivot.before(tomove);
176 177 this.select(i-1);
177 178 };
178 179 };
179 180 return this;
180 181 }
181 182
182 183
183 184 Notebook.prototype.move_cell_down = function (index) {
184 185 var i = index || this.selected_index();
185 186 if (i !== null && i < (this.ncells()-1) && i >= 0) {
186 187 var pivot = this.cell_elements().eq(i+1)
187 188 var tomove = this.cell_elements().eq(i)
188 189 if (pivot !== null && tomove !== null) {
189 190 tomove.detach();
190 191 pivot.after(tomove);
191 192 this.select(i+1);
192 193 };
193 194 };
194 195 return this;
195 196 }
196 197
197 198
198 199 Notebook.prototype.sort_cells = function () {
199 200 var ncells = this.ncells();
201 var sindex = this.selected_index();
200 202 var swapped;
201 203 do {
202 204 swapped = false
203 205 for (var i=1; i<ncells; i++) {
204 206 current = this.cell_elements().eq(i).data("cell");
205 207 previous = this.cell_elements().eq(i-1).data("cell");
206 208 if (previous.input_prompt_number > current.input_prompt_number) {
207 209 this.move_cell_up(i);
208 210 swapped = true;
209 211 };
210 212 };
211 213 } while (swapped);
214 this.select(sindex);
212 215 return this;
213 216 };
214 217
215 218
216 219 Notebook.prototype.insert_code_cell_before = function (index) {
217 220 // TODO: Bounds check for i
218 221 var i = this.index_or_selected(index);
219 222 var cell = new CodeCell(this);
220 223 cell.set_input_prompt(this.next_prompt_number);
221 224 this.next_prompt_number = this.next_prompt_number + 1;
222 225 this.insert_cell_before(cell, i);
223 226 this.select(this.find_cell_index(cell));
224 227 return this;
225 228 }
226 229
227 230
228 231 Notebook.prototype.insert_code_cell_after = function (index) {
229 232 // TODO: Bounds check for i
230 233 var i = this.index_or_selected(index);
231 234 var cell = new CodeCell(this);
232 235 cell.set_input_prompt(this.next_prompt_number);
233 236 this.next_prompt_number = this.next_prompt_number + 1;
234 237 this.insert_cell_after(cell, i);
235 238 this.select(this.find_cell_index(cell));
236 239 return this;
237 240 }
238 241
239 242
240 243 Notebook.prototype.insert_text_cell_before = function (index) {
241 244 // TODO: Bounds check for i
242 245 var i = this.index_or_selected(index);
243 246 var cell = new TextCell(this);
244 247 cell.config_mathjax();
245 248 this.insert_cell_before(cell, i);
246 249 this.select(this.find_cell_index(cell));
247 250 return this;
248 251 }
249 252
250 253
251 254 Notebook.prototype.insert_text_cell_after = function (index) {
252 255 // TODO: Bounds check for i
253 256 var i = this.index_or_selected(index);
254 257 var cell = new TextCell(this);
255 258 cell.config_mathjax();
256 259 this.insert_cell_after(cell, i);
257 260 this.select(this.find_cell_index(cell));
258 261 return this;
259 262 }
260 263
261 264
262 265 Notebook.prototype.text_to_code = function (index) {
263 266 // TODO: Bounds check for i
264 267 var i = this.index_or_selected(index);
265 268 var source_element = this.cell_elements().eq(i);
266 269 var source_cell = source_element.data("cell");
267 270 if (source_cell instanceof TextCell) {
268 271 this.insert_code_cell_after(i);
269 272 var target_cell = this.cells()[i+1];
270 273 var text = source_element.find("textarea.text_cell_input").val();
271 274 target_cell.element.find("textarea.input_area").val(text);
272 275 source_element.remove();
273 276 };
274 277 };
275 278
276 279
277 280 Notebook.prototype.code_to_text = function (index) {
278 281 // TODO: Bounds check for i
279 282 var i = this.index_or_selected(index);
280 283 var source_element = this.cell_elements().eq(i);
281 284 var source_cell = source_element.data("cell");
282 285 if (source_cell instanceof CodeCell) {
283 286 this.insert_text_cell_after(i);
284 287 var target_cell = this.cells()[i+1];
285 288 var text = source_element.find("textarea.input_area").val();
286 289 if (text === "") {text = target_cell.placeholder;};
287 290 target_cell.element.find("textarea.text_cell_input").val(text);
288 291 target_cell.element.find("textarea.text_cell_input").html(text);
289 292 target_cell.element.find("div.text_cell_render").html(text);
290 293
291 294 source_element.remove();
292 295 };
293 296 };
294 297
295 298
296 299 // Cell collapsing
297 300
298 301 Notebook.prototype.collapse = function (index) {
299 302 var i = this.index_or_selected(index);
300 303 this.cells()[i].collapse();
301 304 }
302 305
303 306
304 307 Notebook.prototype.expand = function (index) {
305 308 var i = this.index_or_selected(index);
306 309 this.cells()[i].expand();
307 310 }
308 311
309 312
310 313 //============================================================================
311 314 // Cell
312 315 //============================================================================
313 316
314 317
315 318 var Cell = function (notebook) {
316 319 this.notebook = notebook;
317 320 this.selected = false;
318 321 this.element;
319 322 this.create_element();
320 323 if (this.element !== undefined) {
321 324 this.element.data("cell", this);
322 325 this.bind_events();
323 326 }
324 327 };
325 328
326 329
327 330 Cell.prototype.select = function () {
328 331 this.element.addClass('ui-widget-content ui-corner-all');
329 332 this.selected = true;
330 333 // TODO: we need t test across browsers to see if both of these are needed.
331 334 // In the meantime, there should not be any harm in having them both.
332 335 this.element.find('textarea').trigger('focusin');
333 336 this.element.find('textarea').trigger('focus');
334 337 };
335 338
336 339
337 340 Cell.prototype.unselect = function () {
338 341 this.element.removeClass('ui-widget-content ui-corner-all');
339 342 this.selected = false;
340 343 };
341 344
342 345
343 346 Cell.prototype.bind_events = function () {
344 347 var that = this;
345 348 var nb = that.notebook
346 349 that.element.click(function (event) {
347 350 if (that.selected === false) {
348 351 nb.select(nb.find_cell_index(that));
349 352 };
350 353 });
351 354 that.element.focusin(function (event) {
352 355 if (that.selected === false) {
353 356 nb.select(nb.find_cell_index(that));
354 357 };
355 358 });
356 359 };
357 360
358 361
359 362 // Subclasses must implement create_element.
360 363 Cell.prototype.create_element = function () {};
361 364
362 365
363 366 //============================================================================
364 367 // CodeCell
365 368 //============================================================================
366 369
367 370
368 371 var CodeCell = function (notebook) {
369 372 Cell.apply(this, arguments);
370 373 this.input_prompt_number = ' ';
371 374 this.output_prompt_number = ' ';
372 375 };
373 376
374 377
375 378 CodeCell.prototype = new Cell();
376 379
377 380
378 381 CodeCell.prototype.create_element = function () {
379 382 var cell = $('<div></div>').addClass('cell code_cell')
380 383 var input = $('<div></div>').addClass('input').append(
381 384 $('<div/>').addClass('prompt input_prompt')
382 385 ).append(
383 386 $('<textarea/>').addClass('input_area').
384 387 attr('rows',1).
385 388 attr('cols',80).
386 389 attr('wrap','hard').
387 390 autoGrow()
388 391 );
389 392 var output = $('<div></div>').addClass('output').append(
390 393 $('<div/>').addClass('prompt output_prompt')
391 394 ).append(
392 395 $('<div/>').addClass('output_area')
393 396 );
394 397 output.hide();
395 398 cell.append(input).append(output);
396 399 this.element = cell;
397 400 };
398 401
399 402
400 403 CodeCell.prototype.collapse = function () {
401 404 this.element.find('div.output').hide();
402 405 };
403 406
404 407
405 408 CodeCell.prototype.expand = function () {
406 409 this.element.find('div.output').show();
407 410 };
408 411
409 412
410 413 CodeCell.prototype.set_prompt = function (number) {
411 414 this.set_input_prompt(number);
412 415 this.set_output_prompt(number);
413 416 };
414 417
415 418 CodeCell.prototype.set_input_prompt = function (number) {
416 419 var n = number || ' ';
417 420 this.input_prompt_number = n
418 421 this.element.find('div.input_prompt').html('In&nbsp;[' + n + ']:');
419 422 };
420 423
421 424
422 425 CodeCell.prototype.set_output_prompt = function (number) {
423 426 var n = number || ' ';
424 427 this.output_prompt_number = n
425 428 this.element.find('div.output_prompt').html('Out[' + n + ']:');
426 429 };
427 430
428 431
429 432 //============================================================================
430 433 // TextCell
431 434 //============================================================================
432 435
433 436
434 437 var TextCell = function (notebook) {
435 438 Cell.apply(this, arguments);
436 439 this.placeholder = "Type <strong>HTML</strong> and LaTeX: $\\alpha^2$"
437 440 };
438 441
439 442
440 443 TextCell.prototype = new Cell();
441 444
442 445
443 446 TextCell.prototype.create_element = function () {
444 447 var cell = $("<div>").addClass('cell text_cell').
445 448 append(
446 449 $("<textarea>" + this.placeholder + "</textarea>").
447 450 addClass('text_cell_input').
448 451 attr('rows',1).
449 452 attr('cols',80).
450 453 autoGrow()
451 454 ).append(
452 455 $('<div></div>').addClass('text_cell_render')
453 456 )
454 457 this.element = cell;
455 458 };
456 459
457 460
458 461 TextCell.prototype.select = function () {
459 462 this.edit();
460 463 Cell.prototype.select.apply(this);
461 464 };
462 465
463 466
464 467 TextCell.prototype.edit = function () {
465 468 var text_cell = this.element;
466 469 var input = text_cell.find("textarea.text_cell_input");
467 var output = text_cell.find("div.text_cell_render");
470 var output = text_cell.find("div.text_cell_render");
468 471 output.hide();
469 472 input.show().trigger('focus');
470 473 };
471 474
472 475
473 476 TextCell.prototype.render = function () {
474 477 var text_cell = this.element;
475 478 var input = text_cell.find("textarea.text_cell_input");
476 479 var output = text_cell.find("div.text_cell_render");
477 480 var text = input.val();
481 if (text === "") {
482 text = this.placeholder;
483 input.val(text);
484 };
478 485 output.html(text)
479 486 input.html(text);
480 487 MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
481 488 input.hide();
482 489 output.show();
483 490 };
484 491
485 492
486 493 TextCell.prototype.config_mathjax = function () {
487 494 var text_cell = this.element;
488 495 var that = this;
489 496 text_cell.click(function () {
490 497 that.edit();
491 498 }).focusout(function () {
492 499 that.render();
493 500 });
494 501
495 502 text_cell.trigger("focusout");
496 503 };
497 504
498 505
499 506 //============================================================================
500 507 // On document ready
501 508 //============================================================================
502 509
503 510
511 var KernelManager = function () {
512 this.kernelid = null;
513 this.baseurl = "/kernels";
514 };
515
516
517 KernelManager.prototype.create_kernel = function () {
518 var that = this;
519 $.post(this.baseurl, function (data) {
520 that.kernelid = data;
521 }, 'json');
522 }
523
524
525 KernelManager.prototype.execute = function (code, callback) {
526 var msg = {
527 header : {msg_id : 0, username : "bgranger", session: 0},
528 msg_type : "execute_request",
529 content : {code : code}
530 };
531 var settings = {
532 data : JSON.stringify(msg),
533 processData : false,
534 contentType : "application/json",
535 success : callback,
536 type : "POST"
537 }
538 var url = this.baseurl + "/" + this.kernelid + "/" + ""
539 }
540
541
542 //============================================================================
543 // On document ready
544 //============================================================================
545
546
504 547 $(document).ready(function () {
505 548
506 549 MathJax.Hub.Config({
507 550 tex2jax: {
508 551 inlineMath: [ ['$','$'], ["\\(","\\)"] ],
509 552 displayMath: [ ['$$','$$'], ["\\[","\\]"] ],
510 553 }
511 554 });
512 555
513 556 $("ul#main_menu").wijmenu({animation:{animated: "slide", duration: 100, easing: null}});
514 557 IPYTHON.notebook = new Notebook('div.notebook');
515 558 IPYTHON.notebook.insert_code_cell_after();
516 559
517 560 $("#move_cell").buttonset();
518 561 $("#move_up").button("option", "icons", {primary:"ui-icon-arrowthick-1-n"});
519 562 $("#move_up").button("option", "text", false);
520 563 $("#move_up").click(function () {IPYTHON.notebook.move_cell_up();});
521 564 $("#move_down").button("option", "icons", {primary:"ui-icon-arrowthick-1-s"});
522 565 $("#move_down").button("option", "text", false);
523 566 $("#move_down").click(function () {IPYTHON.notebook.move_cell_down();});
524 567
525 568 $("#insert_delete").buttonset();
526 569 $("#insert_cell_before").click(function () {IPYTHON.notebook.insert_code_cell_before();});
527 570 $("#insert_cell_after").click(function () {IPYTHON.notebook.insert_code_cell_after();});
528 571 $("#delete_cell").button("option", "icons", {primary:"ui-icon-closethick"});
529 572 $("#delete_cell").button("option", "text", false);
530 573 $("#delete_cell").click(function () {IPYTHON.notebook.delete_cell();});
531 574
532 575 $("#cell_type").buttonset();
533 576 $("#to_code").click(function () {IPYTHON.notebook.text_to_code();});
534 577 $("#to_text").click(function () {IPYTHON.notebook.code_to_text();});
535 578
536 579 $("#sort").buttonset();
537 580 $("#sort_cells").click(function () {IPYTHON.notebook.sort_cells();});
538 581
539 582 }); No newline at end of file
General Comments 0
You need to be logged in to leave comments. Login now