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