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