##// END OF EJS Templates
Merge pull request #893 from minrk/clearoutput...
Fernando Perez -
r5099:c99b9fd7 merge
parent child Browse files
Show More
@@ -0,0 +1,109 b''
1 {
2 "worksheets": [
3 {
4 "cells": [
5 {
6 "source": "A demonstration of the ability to clear the output of a cell during execution.",
7 "cell_type": "markdown"
8 },
9 {
10 "cell_type": "code",
11 "language": "python",
12 "outputs": [],
13 "collapsed": true,
14 "prompt_number": 8,
15 "input": "from IPython.core.display import clear_output, display"
16 },
17 {
18 "cell_type": "code",
19 "language": "python",
20 "outputs": [],
21 "collapsed": true,
22 "prompt_number": 4,
23 "input": "import time"
24 },
25 {
26 "source": "First we show how this works with ``display``:",
27 "cell_type": "markdown"
28 },
29 {
30 "cell_type": "code",
31 "language": "python",
32 "outputs": [
33 {
34 "output_type": "stream",
35 "stream": "stdout",
36 "text": "\nTime step: 9"
37 },
38 {
39 "output_type": "stream",
40 "stream": "stdout",
41 "text": "\n"
42 }
43 ],
44 "collapsed": false,
45 "prompt_number": 20,
46 "input": "for i in range(10):\n clear_output()\n print \"Time step: %i\" % i\n time.sleep(0.5)\n"
47 },
48 {
49 "source": "Next, we show that ``clear_output`` can also be used to create a primitive form of animation using\nmatplotlib:",
50 "cell_type": "markdown"
51 },
52 {
53 "cell_type": "code",
54 "language": "python",
55 "outputs": [
56 {
57 "output_type": "display_data",
58 "png": "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\n8P8BdhDzr6dIpNsAAAAASUVORK5CYII=\n"
59 }
60 ],
61 "collapsed": false,
62 "prompt_number": 18,
63 "input": "for i in range(10):\n figure()\n plot(rand(100))\n clear_output()\n show()\n time.sleep(0.25)\n"
64 },
65 {
66 "source": "And we can even selectively clear only a subset of stdout/stderr/other display such as figures\n(all are cleared by default).",
67 "cell_type": "markdown"
68 },
69 {
70 "cell_type": "code",
71 "language": "python",
72 "outputs": [
73 {
74 "output_type": "stream",
75 "stream": "stdout",
76 "text": "Time step: 0\n"
77 },
78 {
79 "output_type": "stream",
80 "stream": "stdout",
81 "text": "Time step: 1\n"
82 },
83 {
84 "output_type": "stream",
85 "stream": "stdout",
86 "text": "Time step: 2\n"
87 },
88 {
89 "output_type": "stream",
90 "stream": "stdout",
91 "text": "Time step: 3\n"
92 },
93 {
94 "output_type": "display_data",
95 "png": "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\nZjzrn0OUXq0AAAAASUVORK5CYII=\n"
96 }
97 ],
98 "collapsed": false,
99 "prompt_number": 19,
100 "input": "for i in range(4):\n print \"Time step: %i\" % i\n figure()\n plot(rand(100))\n # clear plots, but not stdout:\n clear_output(stdout=False)\n show()\n time.sleep(0.25)\n"
101 }
102 ]
103 }
104 ],
105 "metadata": {
106 "name": "clear_output"
107 },
108 "nbformat": 2
109 } No newline at end of file
@@ -1,383 +1,407 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """Top-level display functions for displaying object in different formats.
2 """Top-level display functions for displaying object in different formats.
3
3
4 Authors:
4 Authors:
5
5
6 * Brian Granger
6 * Brian Granger
7 """
7 """
8
8
9 #-----------------------------------------------------------------------------
9 #-----------------------------------------------------------------------------
10 # Copyright (C) 2008-2010 The IPython Development Team
10 # Copyright (C) 2008-2010 The IPython Development Team
11 #
11 #
12 # Distributed under the terms of the BSD License. The full license is in
12 # Distributed under the terms of the BSD License. The full license is in
13 # the file COPYING, distributed as part of this software.
13 # the file COPYING, distributed as part of this software.
14 #-----------------------------------------------------------------------------
14 #-----------------------------------------------------------------------------
15
15
16 #-----------------------------------------------------------------------------
16 #-----------------------------------------------------------------------------
17 # Imports
17 # Imports
18 #-----------------------------------------------------------------------------
18 #-----------------------------------------------------------------------------
19
19
20 from .displaypub import (
20 from .displaypub import (
21 publish_pretty, publish_html,
21 publish_pretty, publish_html,
22 publish_latex, publish_svg,
22 publish_latex, publish_svg,
23 publish_png, publish_json,
23 publish_png, publish_json,
24 publish_javascript, publish_jpeg
24 publish_javascript, publish_jpeg
25 )
25 )
26
26
27 #-----------------------------------------------------------------------------
27 #-----------------------------------------------------------------------------
28 # Main functions
28 # Main functions
29 #-----------------------------------------------------------------------------
29 #-----------------------------------------------------------------------------
30
30
31 def display(*objs, **kwargs):
31 def display(*objs, **kwargs):
32 """Display a Python object in all frontends.
32 """Display a Python object in all frontends.
33
33
34 By default all representations will be computed and sent to the frontends.
34 By default all representations will be computed and sent to the frontends.
35 Frontends can decide which representation is used and how.
35 Frontends can decide which representation is used and how.
36
36
37 Parameters
37 Parameters
38 ----------
38 ----------
39 objs : tuple of objects
39 objs : tuple of objects
40 The Python objects to display.
40 The Python objects to display.
41 include : list or tuple, optional
41 include : list or tuple, optional
42 A list of format type strings (MIME types) to include in the
42 A list of format type strings (MIME types) to include in the
43 format data dict. If this is set *only* the format types included
43 format data dict. If this is set *only* the format types included
44 in this list will be computed.
44 in this list will be computed.
45 exclude : list or tuple, optional
45 exclude : list or tuple, optional
46 A list of format type string (MIME types) to exclue in the format
46 A list of format type string (MIME types) to exclue in the format
47 data dict. If this is set all format types will be computed,
47 data dict. If this is set all format types will be computed,
48 except for those included in this argument.
48 except for those included in this argument.
49 """
49 """
50 include = kwargs.get('include')
50 include = kwargs.get('include')
51 exclude = kwargs.get('exclude')
51 exclude = kwargs.get('exclude')
52
52
53 from IPython.core.interactiveshell import InteractiveShell
53 from IPython.core.interactiveshell import InteractiveShell
54 inst = InteractiveShell.instance()
54 inst = InteractiveShell.instance()
55 format = inst.display_formatter.format
55 format = inst.display_formatter.format
56 publish = inst.display_pub.publish
56 publish = inst.display_pub.publish
57
57
58 for obj in objs:
58 for obj in objs:
59 format_dict = format(obj, include=include, exclude=exclude)
59 format_dict = format(obj, include=include, exclude=exclude)
60 publish('IPython.core.display.display', format_dict)
60 publish('IPython.core.display.display', format_dict)
61
61
62
62
63 def display_pretty(*objs, **kwargs):
63 def display_pretty(*objs, **kwargs):
64 """Display the pretty (default) representation of an object.
64 """Display the pretty (default) representation of an object.
65
65
66 Parameters
66 Parameters
67 ----------
67 ----------
68 objs : tuple of objects
68 objs : tuple of objects
69 The Python objects to display, or if raw=True raw text data to
69 The Python objects to display, or if raw=True raw text data to
70 display.
70 display.
71 raw : bool
71 raw : bool
72 Are the data objects raw data or Python objects that need to be
72 Are the data objects raw data or Python objects that need to be
73 formatted before display? [default: False]
73 formatted before display? [default: False]
74 """
74 """
75 raw = kwargs.pop('raw',False)
75 raw = kwargs.pop('raw',False)
76 if raw:
76 if raw:
77 for obj in objs:
77 for obj in objs:
78 publish_pretty(obj)
78 publish_pretty(obj)
79 else:
79 else:
80 display(*objs, include=['text/plain'])
80 display(*objs, include=['text/plain'])
81
81
82
82
83 def display_html(*objs, **kwargs):
83 def display_html(*objs, **kwargs):
84 """Display the HTML representation of an object.
84 """Display the HTML representation of an object.
85
85
86 Parameters
86 Parameters
87 ----------
87 ----------
88 objs : tuple of objects
88 objs : tuple of objects
89 The Python objects to display, or if raw=True raw HTML data to
89 The Python objects to display, or if raw=True raw HTML data to
90 display.
90 display.
91 raw : bool
91 raw : bool
92 Are the data objects raw data or Python objects that need to be
92 Are the data objects raw data or Python objects that need to be
93 formatted before display? [default: False]
93 formatted before display? [default: False]
94 """
94 """
95 raw = kwargs.pop('raw',False)
95 raw = kwargs.pop('raw',False)
96 if raw:
96 if raw:
97 for obj in objs:
97 for obj in objs:
98 publish_html(obj)
98 publish_html(obj)
99 else:
99 else:
100 display(*objs, include=['text/plain','text/html'])
100 display(*objs, include=['text/plain','text/html'])
101
101
102
102
103 def display_svg(*objs, **kwargs):
103 def display_svg(*objs, **kwargs):
104 """Display the SVG representation of an object.
104 """Display the SVG representation of an object.
105
105
106 Parameters
106 Parameters
107 ----------
107 ----------
108 objs : tuple of objects
108 objs : tuple of objects
109 The Python objects to display, or if raw=True raw svg data to
109 The Python objects to display, or if raw=True raw svg data to
110 display.
110 display.
111 raw : bool
111 raw : bool
112 Are the data objects raw data or Python objects that need to be
112 Are the data objects raw data or Python objects that need to be
113 formatted before display? [default: False]
113 formatted before display? [default: False]
114 """
114 """
115 raw = kwargs.pop('raw',False)
115 raw = kwargs.pop('raw',False)
116 if raw:
116 if raw:
117 for obj in objs:
117 for obj in objs:
118 publish_svg(obj)
118 publish_svg(obj)
119 else:
119 else:
120 display(*objs, include=['text/plain','image/svg+xml'])
120 display(*objs, include=['text/plain','image/svg+xml'])
121
121
122
122
123 def display_png(*objs, **kwargs):
123 def display_png(*objs, **kwargs):
124 """Display the PNG representation of an object.
124 """Display the PNG representation of an object.
125
125
126 Parameters
126 Parameters
127 ----------
127 ----------
128 objs : tuple of objects
128 objs : tuple of objects
129 The Python objects to display, or if raw=True raw png data to
129 The Python objects to display, or if raw=True raw png data to
130 display.
130 display.
131 raw : bool
131 raw : bool
132 Are the data objects raw data or Python objects that need to be
132 Are the data objects raw data or Python objects that need to be
133 formatted before display? [default: False]
133 formatted before display? [default: False]
134 """
134 """
135 raw = kwargs.pop('raw',False)
135 raw = kwargs.pop('raw',False)
136 if raw:
136 if raw:
137 for obj in objs:
137 for obj in objs:
138 publish_png(obj)
138 publish_png(obj)
139 else:
139 else:
140 display(*objs, include=['text/plain','image/png'])
140 display(*objs, include=['text/plain','image/png'])
141
141
142
142
143 def display_jpeg(*objs, **kwargs):
143 def display_jpeg(*objs, **kwargs):
144 """Display the JPEG representation of an object.
144 """Display the JPEG representation of an object.
145
145
146 Parameters
146 Parameters
147 ----------
147 ----------
148 objs : tuple of objects
148 objs : tuple of objects
149 The Python objects to display, or if raw=True raw JPEG data to
149 The Python objects to display, or if raw=True raw JPEG data to
150 display.
150 display.
151 raw : bool
151 raw : bool
152 Are the data objects raw data or Python objects that need to be
152 Are the data objects raw data or Python objects that need to be
153 formatted before display? [default: False]
153 formatted before display? [default: False]
154 """
154 """
155 raw = kwargs.pop('raw',False)
155 raw = kwargs.pop('raw',False)
156 if raw:
156 if raw:
157 for obj in objs:
157 for obj in objs:
158 publish_jpeg(obj)
158 publish_jpeg(obj)
159 else:
159 else:
160 display(*objs, include=['text/plain','image/jpeg'])
160 display(*objs, include=['text/plain','image/jpeg'])
161
161
162
162
163 def display_latex(*objs, **kwargs):
163 def display_latex(*objs, **kwargs):
164 """Display the LaTeX representation of an object.
164 """Display the LaTeX representation of an object.
165
165
166 Parameters
166 Parameters
167 ----------
167 ----------
168 objs : tuple of objects
168 objs : tuple of objects
169 The Python objects to display, or if raw=True raw latex data to
169 The Python objects to display, or if raw=True raw latex data to
170 display.
170 display.
171 raw : bool
171 raw : bool
172 Are the data objects raw data or Python objects that need to be
172 Are the data objects raw data or Python objects that need to be
173 formatted before display? [default: False]
173 formatted before display? [default: False]
174 """
174 """
175 raw = kwargs.pop('raw',False)
175 raw = kwargs.pop('raw',False)
176 if raw:
176 if raw:
177 for obj in objs:
177 for obj in objs:
178 publish_latex(obj)
178 publish_latex(obj)
179 else:
179 else:
180 display(*objs, include=['text/plain','text/latex'])
180 display(*objs, include=['text/plain','text/latex'])
181
181
182
182
183 def display_json(*objs, **kwargs):
183 def display_json(*objs, **kwargs):
184 """Display the JSON representation of an object.
184 """Display the JSON representation of an object.
185
185
186 Parameters
186 Parameters
187 ----------
187 ----------
188 objs : tuple of objects
188 objs : tuple of objects
189 The Python objects to display, or if raw=True raw json data to
189 The Python objects to display, or if raw=True raw json data to
190 display.
190 display.
191 raw : bool
191 raw : bool
192 Are the data objects raw data or Python objects that need to be
192 Are the data objects raw data or Python objects that need to be
193 formatted before display? [default: False]
193 formatted before display? [default: False]
194 """
194 """
195 raw = kwargs.pop('raw',False)
195 raw = kwargs.pop('raw',False)
196 if raw:
196 if raw:
197 for obj in objs:
197 for obj in objs:
198 publish_json(obj)
198 publish_json(obj)
199 else:
199 else:
200 display(*objs, include=['text/plain','application/json'])
200 display(*objs, include=['text/plain','application/json'])
201
201
202
202
203 def display_javascript(*objs, **kwargs):
203 def display_javascript(*objs, **kwargs):
204 """Display the Javascript representation of an object.
204 """Display the Javascript representation of an object.
205
205
206 Parameters
206 Parameters
207 ----------
207 ----------
208 objs : tuple of objects
208 objs : tuple of objects
209 The Python objects to display, or if raw=True raw javascript data to
209 The Python objects to display, or if raw=True raw javascript data to
210 display.
210 display.
211 raw : bool
211 raw : bool
212 Are the data objects raw data or Python objects that need to be
212 Are the data objects raw data or Python objects that need to be
213 formatted before display? [default: False]
213 formatted before display? [default: False]
214 """
214 """
215 raw = kwargs.pop('raw',False)
215 raw = kwargs.pop('raw',False)
216 if raw:
216 if raw:
217 for obj in objs:
217 for obj in objs:
218 publish_javascript(obj)
218 publish_javascript(obj)
219 else:
219 else:
220 display(*objs, include=['text/plain','application/javascript'])
220 display(*objs, include=['text/plain','application/javascript'])
221
221
222 #-----------------------------------------------------------------------------
222 #-----------------------------------------------------------------------------
223 # Smart classes
223 # Smart classes
224 #-----------------------------------------------------------------------------
224 #-----------------------------------------------------------------------------
225
225
226
226
227 class DisplayObject(object):
227 class DisplayObject(object):
228 """An object that wraps data to be displayed."""
228 """An object that wraps data to be displayed."""
229
229
230 _read_flags = 'r'
230 _read_flags = 'r'
231
231
232 def __init__(self, data=None, url=None, filename=None):
232 def __init__(self, data=None, url=None, filename=None):
233 """Create a display object given raw data.
233 """Create a display object given raw data.
234
234
235 When this object is returned by an expression or passed to the
235 When this object is returned by an expression or passed to the
236 display function, it will result in the data being displayed
236 display function, it will result in the data being displayed
237 in the frontend. The MIME type of the data should match the
237 in the frontend. The MIME type of the data should match the
238 subclasses used, so the Png subclass should be used for 'image/png'
238 subclasses used, so the Png subclass should be used for 'image/png'
239 data. If the data is a URL, the data will first be downloaded
239 data. If the data is a URL, the data will first be downloaded
240 and then displayed. If
240 and then displayed. If
241
241
242 Parameters
242 Parameters
243 ----------
243 ----------
244 data : unicode, str or bytes
244 data : unicode, str or bytes
245 The raw data or a URL to download the data from.
245 The raw data or a URL to download the data from.
246 url : unicode
246 url : unicode
247 A URL to download the data from.
247 A URL to download the data from.
248 filename : unicode
248 filename : unicode
249 Path to a local file to load the data from.
249 Path to a local file to load the data from.
250 """
250 """
251 if data is not None and data.startswith('http'):
251 if data is not None and data.startswith('http'):
252 self.url = data
252 self.url = data
253 self.filename = None
253 self.filename = None
254 self.data = None
254 self.data = None
255 else:
255 else:
256 self.data = data
256 self.data = data
257 self.url = url
257 self.url = url
258 self.filename = None if filename is None else unicode(filename)
258 self.filename = None if filename is None else unicode(filename)
259 self.reload()
259 self.reload()
260
260
261 def reload(self):
261 def reload(self):
262 """Reload the raw data from file or URL."""
262 """Reload the raw data from file or URL."""
263 if self.filename is not None:
263 if self.filename is not None:
264 with open(self.filename, self._read_flags) as f:
264 with open(self.filename, self._read_flags) as f:
265 self.data = f.read()
265 self.data = f.read()
266 elif self.url is not None:
266 elif self.url is not None:
267 try:
267 try:
268 import urllib2
268 import urllib2
269 response = urllib2.urlopen(self.url)
269 response = urllib2.urlopen(self.url)
270 self.data = response.read()
270 self.data = response.read()
271 # extract encoding from header, if there is one:
271 # extract encoding from header, if there is one:
272 encoding = None
272 encoding = None
273 for sub in response.headers['content-type'].split(';'):
273 for sub in response.headers['content-type'].split(';'):
274 sub = sub.strip()
274 sub = sub.strip()
275 if sub.startswith('charset'):
275 if sub.startswith('charset'):
276 encoding = sub.split('=')[-1].strip()
276 encoding = sub.split('=')[-1].strip()
277 break
277 break
278 # decode data, if an encoding was specified
278 # decode data, if an encoding was specified
279 if encoding:
279 if encoding:
280 self.data = self.data.decode(encoding, 'replace')
280 self.data = self.data.decode(encoding, 'replace')
281 except:
281 except:
282 self.data = None
282 self.data = None
283
283
284 class Pretty(DisplayObject):
284 class Pretty(DisplayObject):
285
285
286 def _repr_pretty_(self):
286 def _repr_pretty_(self):
287 return self.data
287 return self.data
288
288
289
289
290 class HTML(DisplayObject):
290 class HTML(DisplayObject):
291
291
292 def _repr_html_(self):
292 def _repr_html_(self):
293 return self.data
293 return self.data
294
294
295
295
296 class Math(DisplayObject):
296 class Math(DisplayObject):
297
297
298 def _repr_latex_(self):
298 def _repr_latex_(self):
299 return self.data
299 return self.data
300
300
301
301
302 class SVG(DisplayObject):
302 class SVG(DisplayObject):
303
303
304 def _repr_svg_(self):
304 def _repr_svg_(self):
305 return self.data
305 return self.data
306
306
307
307
308 class JSON(DisplayObject):
308 class JSON(DisplayObject):
309
309
310 def _repr_json_(self):
310 def _repr_json_(self):
311 return self.data
311 return self.data
312
312
313
313
314 class Javascript(DisplayObject):
314 class Javascript(DisplayObject):
315
315
316 def _repr_javascript_(self):
316 def _repr_javascript_(self):
317 return self.data
317 return self.data
318
318
319
319
320 class Image(DisplayObject):
320 class Image(DisplayObject):
321
321
322 _read_flags = 'rb'
322 _read_flags = 'rb'
323
323
324 def __init__(self, data=None, url=None, filename=None, format=u'png', embed=False):
324 def __init__(self, data=None, url=None, filename=None, format=u'png', embed=False):
325 """Create a display an PNG/JPEG image given raw data.
325 """Create a display an PNG/JPEG image given raw data.
326
326
327 When this object is returned by an expression or passed to the
327 When this object is returned by an expression or passed to the
328 display function, it will result in the image being displayed
328 display function, it will result in the image being displayed
329 in the frontend.
329 in the frontend.
330
330
331 Parameters
331 Parameters
332 ----------
332 ----------
333 data : unicode, str or bytes
333 data : unicode, str or bytes
334 The raw data or a URL to download the data from.
334 The raw data or a URL to download the data from.
335 url : unicode
335 url : unicode
336 A URL to download the data from.
336 A URL to download the data from.
337 filename : unicode
337 filename : unicode
338 Path to a local file to load the data from.
338 Path to a local file to load the data from.
339 format : unicode
339 format : unicode
340 The format of the image data (png/jpeg/jpg). If a filename or URL is given
340 The format of the image data (png/jpeg/jpg). If a filename or URL is given
341 for format will be inferred from the filename extension.
341 for format will be inferred from the filename extension.
342 embed : bool
342 embed : bool
343 Should the image data be embedded in the notebook using a data URI (True)
343 Should the image data be embedded in the notebook using a data URI (True)
344 or be loaded using an <img> tag. Set this to True if you want the image
344 or be loaded using an <img> tag. Set this to True if you want the image
345 to be viewable later with no internet connection. If a filename is given
345 to be viewable later with no internet connection. If a filename is given
346 embed is always set to True.
346 embed is always set to True.
347 """
347 """
348 if filename is not None:
348 if filename is not None:
349 ext = self._find_ext(filename)
349 ext = self._find_ext(filename)
350 elif url is not None:
350 elif url is not None:
351 ext = self._find_ext(url)
351 ext = self._find_ext(url)
352 elif data.startswith('http'):
352 elif data.startswith('http'):
353 ext = self._find_ext(data)
353 ext = self._find_ext(data)
354 else:
354 else:
355 ext = None
355 ext = None
356 if ext is not None:
356 if ext is not None:
357 if ext == u'jpg' or ext == u'jpeg':
357 if ext == u'jpg' or ext == u'jpeg':
358 format = u'jpeg'
358 format = u'jpeg'
359 if ext == u'png':
359 if ext == u'png':
360 format = u'png'
360 format = u'png'
361 self.format = unicode(format).lower()
361 self.format = unicode(format).lower()
362 self.embed = True if filename is not None else embed
362 self.embed = True if filename is not None else embed
363 super(Image, self).__init__(data=data, url=url, filename=filename)
363 super(Image, self).__init__(data=data, url=url, filename=filename)
364
364
365 def reload(self):
365 def reload(self):
366 """Reload the raw data from file or URL."""
366 """Reload the raw data from file or URL."""
367 if self.embed:
367 if self.embed:
368 super(Image,self).reload()
368 super(Image,self).reload()
369
369
370 def _repr_html_(self):
370 def _repr_html_(self):
371 if not self.embed:
371 if not self.embed:
372 return u'<img src="%s" />' % self.url
372 return u'<img src="%s" />' % self.url
373
373
374 def _repr_png_(self):
374 def _repr_png_(self):
375 if self.embed and self.format == u'png':
375 if self.embed and self.format == u'png':
376 return self.data
376 return self.data
377
377
378 def _repr_jpeg_(self):
378 def _repr_jpeg_(self):
379 if self.embed and (self.format == u'jpeg' or self.format == u'jpg'):
379 if self.embed and (self.format == u'jpeg' or self.format == u'jpg'):
380 return self.data
380 return self.data
381
381
382 def _find_ext(self, s):
382 def _find_ext(self, s):
383 return unicode(s.split('.')[-1].lower())
383 return unicode(s.split('.')[-1].lower())
384
385
386 def clear_output(stdout=True, stderr=True, other=True):
387 """Clear the output of the current cell receiving output.
388
389 Optionally, each of stdout/stderr or other non-stream data (e.g. anything
390 produced by display()) can be excluded from the clear event.
391
392 By default, everything is cleared.
393
394 Parameters
395 ----------
396 stdout : bool [default: True]
397 Whether to clear stdout.
398 stderr : bool [default: True]
399 Whether to clear stderr.
400 other : bool [default: True]
401 Whether to clear everything else that is not stdout/stderr
402 (e.g. figures,images,HTML, any result of display()).
403 """
404 from IPython.core.interactiveshell import InteractiveShell
405 InteractiveShell.instance().display_pub.clear_output(
406 stdout=stdout, stderr=stderr, other=other,
407 )
@@ -1,298 +1,302 b''
1 """An interface for publishing rich data to frontends.
1 """An interface for publishing rich data to frontends.
2
2
3 There are two components of the display system:
3 There are two components of the display system:
4
4
5 * Display formatters, which take a Python object and compute the
5 * Display formatters, which take a Python object and compute the
6 representation of the object in various formats (text, HTML, SVg, etc.).
6 representation of the object in various formats (text, HTML, SVg, etc.).
7 * The display publisher that is used to send the representation data to the
7 * The display publisher that is used to send the representation data to the
8 various frontends.
8 various frontends.
9
9
10 This module defines the logic display publishing. The display publisher uses
10 This module defines the logic display publishing. The display publisher uses
11 the ``display_data`` message type that is defined in the IPython messaging
11 the ``display_data`` message type that is defined in the IPython messaging
12 spec.
12 spec.
13
13
14 Authors:
14 Authors:
15
15
16 * Brian Granger
16 * Brian Granger
17 """
17 """
18
18
19 #-----------------------------------------------------------------------------
19 #-----------------------------------------------------------------------------
20 # Copyright (C) 2008-2010 The IPython Development Team
20 # Copyright (C) 2008-2010 The IPython Development Team
21 #
21 #
22 # Distributed under the terms of the BSD License. The full license is in
22 # Distributed under the terms of the BSD License. The full license is in
23 # the file COPYING, distributed as part of this software.
23 # the file COPYING, distributed as part of this software.
24 #-----------------------------------------------------------------------------
24 #-----------------------------------------------------------------------------
25
25
26 #-----------------------------------------------------------------------------
26 #-----------------------------------------------------------------------------
27 # Imports
27 # Imports
28 #-----------------------------------------------------------------------------
28 #-----------------------------------------------------------------------------
29
29
30 from __future__ import print_function
30 from __future__ import print_function
31
31
32 from IPython.config.configurable import Configurable
32 from IPython.config.configurable import Configurable
33
33
34 #-----------------------------------------------------------------------------
34 #-----------------------------------------------------------------------------
35 # Main payload class
35 # Main payload class
36 #-----------------------------------------------------------------------------
36 #-----------------------------------------------------------------------------
37
37
38 class DisplayPublisher(Configurable):
38 class DisplayPublisher(Configurable):
39 """A traited class that publishes display data to frontends.
39 """A traited class that publishes display data to frontends.
40
40
41 Instances of this class are created by the main IPython object and should
41 Instances of this class are created by the main IPython object and should
42 be accessed there.
42 be accessed there.
43 """
43 """
44
44
45 def _validate_data(self, source, data, metadata=None):
45 def _validate_data(self, source, data, metadata=None):
46 """Validate the display data.
46 """Validate the display data.
47
47
48 Parameters
48 Parameters
49 ----------
49 ----------
50 source : str
50 source : str
51 The fully dotted name of the callable that created the data, like
51 The fully dotted name of the callable that created the data, like
52 :func:`foo.bar.my_formatter`.
52 :func:`foo.bar.my_formatter`.
53 data : dict
53 data : dict
54 The formata data dictionary.
54 The formata data dictionary.
55 metadata : dict
55 metadata : dict
56 Any metadata for the data.
56 Any metadata for the data.
57 """
57 """
58
58
59 if not isinstance(source, basestring):
59 if not isinstance(source, basestring):
60 raise TypeError('source must be a str, got: %r' % source)
60 raise TypeError('source must be a str, got: %r' % source)
61 if not isinstance(data, dict):
61 if not isinstance(data, dict):
62 raise TypeError('data must be a dict, got: %r' % data)
62 raise TypeError('data must be a dict, got: %r' % data)
63 if metadata is not None:
63 if metadata is not None:
64 if not isinstance(metadata, dict):
64 if not isinstance(metadata, dict):
65 raise TypeError('metadata must be a dict, got: %r' % data)
65 raise TypeError('metadata must be a dict, got: %r' % data)
66
66
67 def publish(self, source, data, metadata=None):
67 def publish(self, source, data, metadata=None):
68 """Publish data and metadata to all frontends.
68 """Publish data and metadata to all frontends.
69
69
70 See the ``display_data`` message in the messaging documentation for
70 See the ``display_data`` message in the messaging documentation for
71 more details about this message type.
71 more details about this message type.
72
72
73 The following MIME types are currently implemented:
73 The following MIME types are currently implemented:
74
74
75 * text/plain
75 * text/plain
76 * text/html
76 * text/html
77 * text/latex
77 * text/latex
78 * application/json
78 * application/json
79 * application/javascript
79 * application/javascript
80 * image/png
80 * image/png
81 * image/jpeg
81 * image/jpeg
82 * image/svg+xml
82 * image/svg+xml
83
83
84 Parameters
84 Parameters
85 ----------
85 ----------
86 source : str
86 source : str
87 A string that give the function or method that created the data,
87 A string that give the function or method that created the data,
88 such as 'IPython.core.page'.
88 such as 'IPython.core.page'.
89 data : dict
89 data : dict
90 A dictionary having keys that are valid MIME types (like
90 A dictionary having keys that are valid MIME types (like
91 'text/plain' or 'image/svg+xml') and values that are the data for
91 'text/plain' or 'image/svg+xml') and values that are the data for
92 that MIME type. The data itself must be a JSON'able data
92 that MIME type. The data itself must be a JSON'able data
93 structure. Minimally all data should have the 'text/plain' data,
93 structure. Minimally all data should have the 'text/plain' data,
94 which can be displayed by all frontends. If more than the plain
94 which can be displayed by all frontends. If more than the plain
95 text is given, it is up to the frontend to decide which
95 text is given, it is up to the frontend to decide which
96 representation to use.
96 representation to use.
97 metadata : dict
97 metadata : dict
98 A dictionary for metadata related to the data. This can contain
98 A dictionary for metadata related to the data. This can contain
99 arbitrary key, value pairs that frontends can use to interpret
99 arbitrary key, value pairs that frontends can use to interpret
100 the data.
100 the data.
101 """
101 """
102 from IPython.utils import io
102 from IPython.utils import io
103 # The default is to simply write the plain text data using io.stdout.
103 # The default is to simply write the plain text data using io.stdout.
104 if data.has_key('text/plain'):
104 if data.has_key('text/plain'):
105 print(data['text/plain'], file=io.stdout)
105 print(data['text/plain'], file=io.stdout)
106
106
107 def clear_output(self, stdout=True, stderr=True, other=True):
108 """Clear the output of the cell receiving output."""
109 pass
110
107
111
108 def publish_display_data(source, data, metadata=None):
112 def publish_display_data(source, data, metadata=None):
109 """Publish data and metadata to all frontends.
113 """Publish data and metadata to all frontends.
110
114
111 See the ``display_data`` message in the messaging documentation for
115 See the ``display_data`` message in the messaging documentation for
112 more details about this message type.
116 more details about this message type.
113
117
114 The following MIME types are currently implemented:
118 The following MIME types are currently implemented:
115
119
116 * text/plain
120 * text/plain
117 * text/html
121 * text/html
118 * text/latex
122 * text/latex
119 * application/json
123 * application/json
120 * application/javascript
124 * application/javascript
121 * image/png
125 * image/png
122 * image/jpeg
126 * image/jpeg
123 * image/svg+xml
127 * image/svg+xml
124
128
125 Parameters
129 Parameters
126 ----------
130 ----------
127 source : str
131 source : str
128 A string that give the function or method that created the data,
132 A string that give the function or method that created the data,
129 such as 'IPython.core.page'.
133 such as 'IPython.core.page'.
130 data : dict
134 data : dict
131 A dictionary having keys that are valid MIME types (like
135 A dictionary having keys that are valid MIME types (like
132 'text/plain' or 'image/svg+xml') and values that are the data for
136 'text/plain' or 'image/svg+xml') and values that are the data for
133 that MIME type. The data itself must be a JSON'able data
137 that MIME type. The data itself must be a JSON'able data
134 structure. Minimally all data should have the 'text/plain' data,
138 structure. Minimally all data should have the 'text/plain' data,
135 which can be displayed by all frontends. If more than the plain
139 which can be displayed by all frontends. If more than the plain
136 text is given, it is up to the frontend to decide which
140 text is given, it is up to the frontend to decide which
137 representation to use.
141 representation to use.
138 metadata : dict
142 metadata : dict
139 A dictionary for metadata related to the data. This can contain
143 A dictionary for metadata related to the data. This can contain
140 arbitrary key, value pairs that frontends can use to interpret
144 arbitrary key, value pairs that frontends can use to interpret
141 the data.
145 the data.
142 """
146 """
143 from IPython.core.interactiveshell import InteractiveShell
147 from IPython.core.interactiveshell import InteractiveShell
144 InteractiveShell.instance().display_pub.publish(
148 InteractiveShell.instance().display_pub.publish(
145 source,
149 source,
146 data,
150 data,
147 metadata
151 metadata
148 )
152 )
149
153
150
154
151 def publish_pretty(data, metadata=None):
155 def publish_pretty(data, metadata=None):
152 """Publish raw text data to all frontends.
156 """Publish raw text data to all frontends.
153
157
154 Parameters
158 Parameters
155 ----------
159 ----------
156 data : unicode
160 data : unicode
157 The raw text data to publish.
161 The raw text data to publish.
158 metadata : dict
162 metadata : dict
159 A dictionary for metadata related to the data. This can contain
163 A dictionary for metadata related to the data. This can contain
160 arbitrary key, value pairs that frontends can use to interpret
164 arbitrary key, value pairs that frontends can use to interpret
161 the data.
165 the data.
162 """
166 """
163 publish_display_data(
167 publish_display_data(
164 u'IPython.core.displaypub.publish_pretty',
168 u'IPython.core.displaypub.publish_pretty',
165 {'text/plain':data},
169 {'text/plain':data},
166 metadata=metadata
170 metadata=metadata
167 )
171 )
168
172
169
173
170 def publish_html(data, metadata=None):
174 def publish_html(data, metadata=None):
171 """Publish raw HTML data to all frontends.
175 """Publish raw HTML data to all frontends.
172
176
173 Parameters
177 Parameters
174 ----------
178 ----------
175 data : unicode
179 data : unicode
176 The raw HTML data to publish.
180 The raw HTML data to publish.
177 metadata : dict
181 metadata : dict
178 A dictionary for metadata related to the data. This can contain
182 A dictionary for metadata related to the data. This can contain
179 arbitrary key, value pairs that frontends can use to interpret
183 arbitrary key, value pairs that frontends can use to interpret
180 the data.
184 the data.
181 """
185 """
182 publish_display_data(
186 publish_display_data(
183 u'IPython.core.displaypub.publish_html',
187 u'IPython.core.displaypub.publish_html',
184 {'text/html':data},
188 {'text/html':data},
185 metadata=metadata
189 metadata=metadata
186 )
190 )
187
191
188
192
189 def publish_latex(data, metadata=None):
193 def publish_latex(data, metadata=None):
190 """Publish raw LaTeX data to all frontends.
194 """Publish raw LaTeX data to all frontends.
191
195
192 Parameters
196 Parameters
193 ----------
197 ----------
194 data : unicode
198 data : unicode
195 The raw LaTeX data to publish.
199 The raw LaTeX data to publish.
196 metadata : dict
200 metadata : dict
197 A dictionary for metadata related to the data. This can contain
201 A dictionary for metadata related to the data. This can contain
198 arbitrary key, value pairs that frontends can use to interpret
202 arbitrary key, value pairs that frontends can use to interpret
199 the data.
203 the data.
200 """
204 """
201 publish_display_data(
205 publish_display_data(
202 u'IPython.core.displaypub.publish_latex',
206 u'IPython.core.displaypub.publish_latex',
203 {'text/latex':data},
207 {'text/latex':data},
204 metadata=metadata
208 metadata=metadata
205 )
209 )
206
210
207 def publish_png(data, metadata=None):
211 def publish_png(data, metadata=None):
208 """Publish raw binary PNG data to all frontends.
212 """Publish raw binary PNG data to all frontends.
209
213
210 Parameters
214 Parameters
211 ----------
215 ----------
212 data : str/bytes
216 data : str/bytes
213 The raw binary PNG data to publish.
217 The raw binary PNG data to publish.
214 metadata : dict
218 metadata : dict
215 A dictionary for metadata related to the data. This can contain
219 A dictionary for metadata related to the data. This can contain
216 arbitrary key, value pairs that frontends can use to interpret
220 arbitrary key, value pairs that frontends can use to interpret
217 the data.
221 the data.
218 """
222 """
219 publish_display_data(
223 publish_display_data(
220 u'IPython.core.displaypub.publish_png',
224 u'IPython.core.displaypub.publish_png',
221 {'image/png':data},
225 {'image/png':data},
222 metadata=metadata
226 metadata=metadata
223 )
227 )
224
228
225
229
226 def publish_jpeg(data, metadata=None):
230 def publish_jpeg(data, metadata=None):
227 """Publish raw binary JPEG data to all frontends.
231 """Publish raw binary JPEG data to all frontends.
228
232
229 Parameters
233 Parameters
230 ----------
234 ----------
231 data : str/bytes
235 data : str/bytes
232 The raw binary JPEG data to publish.
236 The raw binary JPEG data to publish.
233 metadata : dict
237 metadata : dict
234 A dictionary for metadata related to the data. This can contain
238 A dictionary for metadata related to the data. This can contain
235 arbitrary key, value pairs that frontends can use to interpret
239 arbitrary key, value pairs that frontends can use to interpret
236 the data.
240 the data.
237 """
241 """
238 publish_display_data(
242 publish_display_data(
239 u'IPython.core.displaypub.publish_jpeg',
243 u'IPython.core.displaypub.publish_jpeg',
240 {'image/jpeg':data},
244 {'image/jpeg':data},
241 metadata=metadata
245 metadata=metadata
242 )
246 )
243
247
244
248
245 def publish_svg(data, metadata=None):
249 def publish_svg(data, metadata=None):
246 """Publish raw SVG data to all frontends.
250 """Publish raw SVG data to all frontends.
247
251
248 Parameters
252 Parameters
249 ----------
253 ----------
250 data : unicode
254 data : unicode
251 The raw SVG data to publish.
255 The raw SVG data to publish.
252 metadata : dict
256 metadata : dict
253 A dictionary for metadata related to the data. This can contain
257 A dictionary for metadata related to the data. This can contain
254 arbitrary key, value pairs that frontends can use to interpret
258 arbitrary key, value pairs that frontends can use to interpret
255 the data.
259 the data.
256 """
260 """
257 publish_display_data(
261 publish_display_data(
258 u'IPython.core.displaypub.publish_svg',
262 u'IPython.core.displaypub.publish_svg',
259 {'image/svg+xml':data},
263 {'image/svg+xml':data},
260 metadata=metadata
264 metadata=metadata
261 )
265 )
262
266
263 def publish_json(data, metadata=None):
267 def publish_json(data, metadata=None):
264 """Publish raw JSON data to all frontends.
268 """Publish raw JSON data to all frontends.
265
269
266 Parameters
270 Parameters
267 ----------
271 ----------
268 data : unicode
272 data : unicode
269 The raw JSON data to publish.
273 The raw JSON data to publish.
270 metadata : dict
274 metadata : dict
271 A dictionary for metadata related to the data. This can contain
275 A dictionary for metadata related to the data. This can contain
272 arbitrary key, value pairs that frontends can use to interpret
276 arbitrary key, value pairs that frontends can use to interpret
273 the data.
277 the data.
274 """
278 """
275 publish_display_data(
279 publish_display_data(
276 u'IPython.core.displaypub.publish_json',
280 u'IPython.core.displaypub.publish_json',
277 {'application/json':data},
281 {'application/json':data},
278 metadata=metadata
282 metadata=metadata
279 )
283 )
280
284
281 def publish_javascript(data, metadata=None):
285 def publish_javascript(data, metadata=None):
282 """Publish raw Javascript data to all frontends.
286 """Publish raw Javascript data to all frontends.
283
287
284 Parameters
288 Parameters
285 ----------
289 ----------
286 data : unicode
290 data : unicode
287 The raw Javascript data to publish.
291 The raw Javascript data to publish.
288 metadata : dict
292 metadata : dict
289 A dictionary for metadata related to the data. This can contain
293 A dictionary for metadata related to the data. This can contain
290 arbitrary key, value pairs that frontends can use to interpret
294 arbitrary key, value pairs that frontends can use to interpret
291 the data.
295 the data.
292 """
296 """
293 publish_display_data(
297 publish_display_data(
294 u'IPython.core.displaypub.publish_javascript',
298 u'IPython.core.displaypub.publish_javascript',
295 {'application/javascript':data},
299 {'application/javascript':data},
296 metadata=metadata
300 metadata=metadata
297 )
301 )
298
302
@@ -1,486 +1,518 b''
1 //----------------------------------------------------------------------------
1 //----------------------------------------------------------------------------
2 // Copyright (C) 2008-2011 The IPython Development Team
2 // Copyright (C) 2008-2011 The IPython Development Team
3 //
3 //
4 // Distributed under the terms of the BSD License. The full license is in
4 // Distributed under the terms of the BSD License. The full license is in
5 // the file COPYING, distributed as part of this software.
5 // the file COPYING, distributed as part of this software.
6 //----------------------------------------------------------------------------
6 //----------------------------------------------------------------------------
7
7
8 //============================================================================
8 //============================================================================
9 // CodeCell
9 // CodeCell
10 //============================================================================
10 //============================================================================
11
11
12 var IPython = (function (IPython) {
12 var IPython = (function (IPython) {
13
13
14 var utils = IPython.utils;
14 var utils = IPython.utils;
15
15
16 var CodeCell = function (notebook) {
16 var CodeCell = function (notebook) {
17 this.code_mirror = null;
17 this.code_mirror = null;
18 this.input_prompt_number = ' ';
18 this.input_prompt_number = ' ';
19 this.is_completing = false;
19 this.is_completing = false;
20 this.completion_cursor = null;
20 this.completion_cursor = null;
21 this.outputs = [];
21 this.outputs = [];
22 this.collapsed = false;
22 this.collapsed = false;
23 IPython.Cell.apply(this, arguments);
23 IPython.Cell.apply(this, arguments);
24 };
24 };
25
25
26
26
27 CodeCell.prototype = new IPython.Cell();
27 CodeCell.prototype = new IPython.Cell();
28
28
29
29
30 CodeCell.prototype.create_element = function () {
30 CodeCell.prototype.create_element = function () {
31 var cell = $('<div></div>').addClass('cell border-box-sizing code_cell vbox');
31 var cell = $('<div></div>').addClass('cell border-box-sizing code_cell vbox');
32 cell.attr('tabindex','2');
32 cell.attr('tabindex','2');
33 var input = $('<div></div>').addClass('input hbox');
33 var input = $('<div></div>').addClass('input hbox');
34 input.append($('<div/>').addClass('prompt input_prompt'));
34 input.append($('<div/>').addClass('prompt input_prompt'));
35 var input_area = $('<div/>').addClass('input_area box-flex1');
35 var input_area = $('<div/>').addClass('input_area box-flex1');
36 this.code_mirror = CodeMirror(input_area.get(0), {
36 this.code_mirror = CodeMirror(input_area.get(0), {
37 indentUnit : 4,
37 indentUnit : 4,
38 mode: 'python',
38 mode: 'python',
39 theme: 'ipython',
39 theme: 'ipython',
40 onKeyEvent: $.proxy(this.handle_codemirror_keyevent,this)
40 onKeyEvent: $.proxy(this.handle_codemirror_keyevent,this)
41 });
41 });
42 input.append(input_area);
42 input.append(input_area);
43 var output = $('<div></div>').addClass('output vbox');
43 var output = $('<div></div>').addClass('output vbox');
44 cell.append(input).append(output);
44 cell.append(input).append(output);
45 this.element = cell;
45 this.element = cell;
46 this.collapse()
46 this.collapse()
47 };
47 };
48
48
49
49
50 CodeCell.prototype.handle_codemirror_keyevent = function (editor, event) {
50 CodeCell.prototype.handle_codemirror_keyevent = function (editor, event) {
51 // This method gets called in CodeMirror's onKeyDown/onKeyPress
51 // This method gets called in CodeMirror's onKeyDown/onKeyPress
52 // handlers and is used to provide custom key handling. Its return
52 // handlers and is used to provide custom key handling. Its return
53 // value is used to determine if CodeMirror should ignore the event:
53 // value is used to determine if CodeMirror should ignore the event:
54 // true = ignore, false = don't ignore.
54 // true = ignore, false = don't ignore.
55 if (event.keyCode === 13 && (event.shiftKey || event.ctrlKey)) {
55 if (event.keyCode === 13 && (event.shiftKey || event.ctrlKey)) {
56 // Always ignore shift-enter in CodeMirror as we handle it.
56 // Always ignore shift-enter in CodeMirror as we handle it.
57 return true;
57 return true;
58 } else if (event.keyCode === 9 && event.type == 'keydown') {
58 } else if (event.keyCode === 9 && event.type == 'keydown') {
59 // Tab completion.
59 // Tab completion.
60 var cur = editor.getCursor();
60 var cur = editor.getCursor();
61 var pre_cursor = editor.getRange({line:cur.line,ch:0},cur).trim();
61 var pre_cursor = editor.getRange({line:cur.line,ch:0},cur).trim();
62 if (pre_cursor === "") {
62 if (pre_cursor === "") {
63 // Don't autocomplete if the part of the line before the cursor
63 // Don't autocomplete if the part of the line before the cursor
64 // is empty. In this case, let CodeMirror handle indentation.
64 // is empty. In this case, let CodeMirror handle indentation.
65 return false;
65 return false;
66 } else {
66 } else {
67 // Autocomplete the current line.
67 // Autocomplete the current line.
68 event.stop();
68 event.stop();
69 var line = editor.getLine(cur.line);
69 var line = editor.getLine(cur.line);
70 this.is_completing = true;
70 this.is_completing = true;
71 this.completion_cursor = cur;
71 this.completion_cursor = cur;
72 IPython.notebook.complete_cell(this, line, cur.ch);
72 IPython.notebook.complete_cell(this, line, cur.ch);
73 return true;
73 return true;
74 }
74 }
75 } else if (event.keyCode === 8 && event.type == 'keydown') {
75 } else if (event.keyCode === 8 && event.type == 'keydown') {
76 // If backspace and the line ends with 4 spaces, remove them.
76 // If backspace and the line ends with 4 spaces, remove them.
77 var cur = editor.getCursor();
77 var cur = editor.getCursor();
78 var line = editor.getLine(cur.line);
78 var line = editor.getLine(cur.line);
79 var ending = line.slice(-4);
79 var ending = line.slice(-4);
80 if (ending === ' ') {
80 if (ending === ' ') {
81 editor.replaceRange('',
81 editor.replaceRange('',
82 {line: cur.line, ch: cur.ch-4},
82 {line: cur.line, ch: cur.ch-4},
83 {line: cur.line, ch: cur.ch}
83 {line: cur.line, ch: cur.ch}
84 );
84 );
85 event.stop();
85 event.stop();
86 return true;
86 return true;
87 } else {
87 } else {
88 return false;
88 return false;
89 };
89 };
90 } else if (event.keyCode === 76 && event.ctrlKey && event.shiftKey
90 } else if (event.keyCode === 76 && event.ctrlKey && event.shiftKey
91 && event.type == 'keydown') {
91 && event.type == 'keydown') {
92 // toggle line numbers with Ctrl-Shift-L
92 // toggle line numbers with Ctrl-Shift-L
93 this.toggle_line_numbers();
93 this.toggle_line_numbers();
94 }
94 }
95 else {
95 else {
96 // keypress/keyup also trigger on TAB press, and we don't want to
96 // keypress/keyup also trigger on TAB press, and we don't want to
97 // use those to disable tab completion.
97 // use those to disable tab completion.
98 if (this.is_completing && event.keyCode !== 9) {
98 if (this.is_completing && event.keyCode !== 9) {
99 var ed_cur = editor.getCursor();
99 var ed_cur = editor.getCursor();
100 var cc_cur = this.completion_cursor;
100 var cc_cur = this.completion_cursor;
101 if (ed_cur.line !== cc_cur.line || ed_cur.ch !== cc_cur.ch) {
101 if (ed_cur.line !== cc_cur.line || ed_cur.ch !== cc_cur.ch) {
102 this.is_completing = false;
102 this.is_completing = false;
103 this.completion_cursor = null;
103 this.completion_cursor = null;
104 };
104 };
105 };
105 };
106 return false;
106 return false;
107 };
107 };
108 };
108 };
109
109
110
110
111 CodeCell.prototype.finish_completing = function (matched_text, matches) {
111 CodeCell.prototype.finish_completing = function (matched_text, matches) {
112 // console.log("Got matches", matched_text, matches);
112 // console.log("Got matches", matched_text, matches);
113 if (!this.is_completing || matches.length === 0) {return;}
113 if (!this.is_completing || matches.length === 0) {return;}
114
114
115 var that = this;
115 var that = this;
116 var cur = this.completion_cursor;
116 var cur = this.completion_cursor;
117
117
118 var insert = function (selected_text) {
118 var insert = function (selected_text) {
119 that.code_mirror.replaceRange(
119 that.code_mirror.replaceRange(
120 selected_text,
120 selected_text,
121 {line: cur.line, ch: (cur.ch-matched_text.length)},
121 {line: cur.line, ch: (cur.ch-matched_text.length)},
122 {line: cur.line, ch: cur.ch}
122 {line: cur.line, ch: cur.ch}
123 );
123 );
124 };
124 };
125
125
126 if (matches.length === 1) {
126 if (matches.length === 1) {
127 insert(matches[0]);
127 insert(matches[0]);
128 setTimeout(function(){that.code_mirror.focus();}, 50);
128 setTimeout(function(){that.code_mirror.focus();}, 50);
129 return;
129 return;
130 };
130 };
131
131
132 var complete = $('<div/>').addClass('completions');
132 var complete = $('<div/>').addClass('completions');
133 var select = $('<select/>').attr('multiple','true');
133 var select = $('<select/>').attr('multiple','true');
134 for (var i=0; i<matches.length; ++i) {
134 for (var i=0; i<matches.length; ++i) {
135 select.append($('<option/>').text(matches[i]));
135 select.append($('<option/>').text(matches[i]));
136 }
136 }
137 select.children().first().attr('selected','true');
137 select.children().first().attr('selected','true');
138 select.attr('size',Math.min(10,matches.length));
138 select.attr('size',Math.min(10,matches.length));
139 var pos = this.code_mirror.cursorCoords();
139 var pos = this.code_mirror.cursorCoords();
140 complete.css('left',pos.x+'px');
140 complete.css('left',pos.x+'px');
141 complete.css('top',pos.yBot+'px');
141 complete.css('top',pos.yBot+'px');
142 complete.append(select);
142 complete.append(select);
143
143
144 $('body').append(complete);
144 $('body').append(complete);
145 var done = false;
145 var done = false;
146
146
147 var close = function () {
147 var close = function () {
148 if (done) return;
148 if (done) return;
149 done = true;
149 done = true;
150 complete.remove();
150 complete.remove();
151 that.is_completing = false;
151 that.is_completing = false;
152 that.completion_cursor = null;
152 that.completion_cursor = null;
153 };
153 };
154
154
155 var pick = function () {
155 var pick = function () {
156 insert(select.val()[0]);
156 insert(select.val()[0]);
157 close();
157 close();
158 setTimeout(function(){that.code_mirror.focus();}, 50);
158 setTimeout(function(){that.code_mirror.focus();}, 50);
159 };
159 };
160
160
161 select.blur(close);
161 select.blur(close);
162 select.keydown(function (event) {
162 select.keydown(function (event) {
163 var code = event.which;
163 var code = event.which;
164 if (code === 13 || code === 32) {
164 if (code === 13 || code === 32) {
165 // Pressing SPACE or ENTER will cause a pick
165 // Pressing SPACE or ENTER will cause a pick
166 event.stopPropagation();
166 event.stopPropagation();
167 event.preventDefault();
167 event.preventDefault();
168 pick();
168 pick();
169 } else if (code === 38 || code === 40) {
169 } else if (code === 38 || code === 40) {
170 // We don't want the document keydown handler to handle UP/DOWN,
170 // We don't want the document keydown handler to handle UP/DOWN,
171 // but we want the default action.
171 // but we want the default action.
172 event.stopPropagation();
172 event.stopPropagation();
173 } else {
173 } else {
174 // All other key presses exit completion.
174 // All other key presses exit completion.
175 event.stopPropagation();
175 event.stopPropagation();
176 event.preventDefault();
176 event.preventDefault();
177 close();
177 close();
178 that.code_mirror.focus();
178 that.code_mirror.focus();
179 }
179 }
180 });
180 });
181 // Double click also causes a pick.
181 // Double click also causes a pick.
182 select.dblclick(pick);
182 select.dblclick(pick);
183 select.focus();
183 select.focus();
184 };
184 };
185
185
186 CodeCell.prototype.toggle_line_numbers = function () {
186 CodeCell.prototype.toggle_line_numbers = function () {
187 if (this.code_mirror.getOption('lineNumbers') == false) {
187 if (this.code_mirror.getOption('lineNumbers') == false) {
188 this.code_mirror.setOption('lineNumbers', true);
188 this.code_mirror.setOption('lineNumbers', true);
189 } else {
189 } else {
190 this.code_mirror.setOption('lineNumbers', false);
190 this.code_mirror.setOption('lineNumbers', false);
191 }
191 }
192 this.code_mirror.refresh()
192 this.code_mirror.refresh()
193 };
193 };
194
194
195 CodeCell.prototype.select = function () {
195 CodeCell.prototype.select = function () {
196 IPython.Cell.prototype.select.apply(this);
196 IPython.Cell.prototype.select.apply(this);
197 // Todo: this dance is needed because as of CodeMirror 2.12, focus is
197 // Todo: this dance is needed because as of CodeMirror 2.12, focus is
198 // not causing the cursor to blink if the editor is empty initially.
198 // not causing the cursor to blink if the editor is empty initially.
199 // While this seems to fix the issue, this should be fixed
199 // While this seems to fix the issue, this should be fixed
200 // in CodeMirror proper.
200 // in CodeMirror proper.
201 var s = this.code_mirror.getValue();
201 var s = this.code_mirror.getValue();
202 this.code_mirror.focus();
202 this.code_mirror.focus();
203 if (s === '') this.code_mirror.setValue('');
203 if (s === '') this.code_mirror.setValue('');
204 };
204 };
205
205
206
206
207 CodeCell.prototype.select_all = function () {
207 CodeCell.prototype.select_all = function () {
208 var start = {line: 0, ch: 0};
208 var start = {line: 0, ch: 0};
209 var nlines = this.code_mirror.lineCount();
209 var nlines = this.code_mirror.lineCount();
210 var last_line = this.code_mirror.getLine(nlines-1);
210 var last_line = this.code_mirror.getLine(nlines-1);
211 var end = {line: nlines-1, ch: last_line.length};
211 var end = {line: nlines-1, ch: last_line.length};
212 this.code_mirror.setSelection(start, end);
212 this.code_mirror.setSelection(start, end);
213 };
213 };
214
214
215
215
216 CodeCell.prototype.append_output = function (json) {
216 CodeCell.prototype.append_output = function (json) {
217 this.expand();
217 this.expand();
218 if (json.output_type === 'pyout') {
218 if (json.output_type === 'pyout') {
219 this.append_pyout(json);
219 this.append_pyout(json);
220 } else if (json.output_type === 'pyerr') {
220 } else if (json.output_type === 'pyerr') {
221 this.append_pyerr(json);
221 this.append_pyerr(json);
222 } else if (json.output_type === 'display_data') {
222 } else if (json.output_type === 'display_data') {
223 this.append_display_data(json);
223 this.append_display_data(json);
224 } else if (json.output_type === 'stream') {
224 } else if (json.output_type === 'stream') {
225 this.append_stream(json);
225 this.append_stream(json);
226 };
226 };
227 this.outputs.push(json);
227 this.outputs.push(json);
228 };
228 };
229
229
230
230
231 CodeCell.prototype.create_output_area = function () {
231 CodeCell.prototype.create_output_area = function () {
232 var oa = $("<div/>").addClass("hbox output_area");
232 var oa = $("<div/>").addClass("hbox output_area");
233 oa.append($('<div/>').addClass('prompt'));
233 oa.append($('<div/>').addClass('prompt'));
234 return oa;
234 return oa;
235 };
235 };
236
236
237
237
238 CodeCell.prototype.append_pyout = function (json) {
238 CodeCell.prototype.append_pyout = function (json) {
239 n = json.prompt_number || ' ';
239 n = json.prompt_number || ' ';
240 var toinsert = this.create_output_area();
240 var toinsert = this.create_output_area();
241 toinsert.find('div.prompt').addClass('output_prompt').html('Out[' + n + ']:');
241 toinsert.find('div.prompt').addClass('output_prompt').html('Out[' + n + ']:');
242 this.append_mime_type(json, toinsert);
242 this.append_mime_type(json, toinsert);
243 this.element.find('div.output').append(toinsert);
243 this.element.find('div.output').append(toinsert);
244 // If we just output latex, typeset it.
244 // If we just output latex, typeset it.
245 if (json.latex !== undefined) {
245 if (json.latex !== undefined) {
246 MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
246 MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
247 };
247 };
248 };
248 };
249
249
250
250
251 CodeCell.prototype.append_pyerr = function (json) {
251 CodeCell.prototype.append_pyerr = function (json) {
252 var tb = json.traceback;
252 var tb = json.traceback;
253 if (tb !== undefined && tb.length > 0) {
253 if (tb !== undefined && tb.length > 0) {
254 var s = '';
254 var s = '';
255 var len = tb.length;
255 var len = tb.length;
256 for (var i=0; i<len; i++) {
256 for (var i=0; i<len; i++) {
257 s = s + tb[i] + '\n';
257 s = s + tb[i] + '\n';
258 }
258 }
259 s = s + '\n';
259 s = s + '\n';
260 var toinsert = this.create_output_area();
260 var toinsert = this.create_output_area();
261 this.append_text(s, toinsert);
261 this.append_text(s, toinsert);
262 this.element.find('div.output').append(toinsert);
262 this.element.find('div.output').append(toinsert);
263 };
263 };
264 };
264 };
265
265
266
266
267 CodeCell.prototype.append_stream = function (json) {
267 CodeCell.prototype.append_stream = function (json) {
268 // temporary fix: if stream undefined (json file written prior to this patch),
268 // temporary fix: if stream undefined (json file written prior to this patch),
269 // default to most likely stdout:
269 // default to most likely stdout:
270 if (json.stream == undefined){
270 if (json.stream == undefined){
271 json.stream = 'stdout';
271 json.stream = 'stdout';
272 }
272 }
273 var subclass = "output_"+json.stream;
273 var subclass = "output_"+json.stream;
274 if (this.outputs.length > 0){
274 if (this.outputs.length > 0){
275 // have at least one output to consider
275 // have at least one output to consider
276 var last = this.outputs[this.outputs.length-1];
276 var last = this.outputs[this.outputs.length-1];
277 if (last.output_type == 'stream' && json.stream == last.stream){
277 if (last.output_type == 'stream' && json.stream == last.stream){
278 // latest output was in the same stream,
278 // latest output was in the same stream,
279 // so append directly into its pre tag
279 // so append directly into its pre tag
280 this.element.find('div.'+subclass).last().find('pre').append(json.text);
280 this.element.find('div.'+subclass).last().find('pre').append(json.text);
281 return;
281 return;
282 }
282 }
283 }
283 }
284
284
285 // If we got here, attach a new div
285 // If we got here, attach a new div
286 var toinsert = this.create_output_area();
286 var toinsert = this.create_output_area();
287 this.append_text(json.text, toinsert, "output_stream "+subclass);
287 this.append_text(json.text, toinsert, "output_stream "+subclass);
288 this.element.find('div.output').append(toinsert);
288 this.element.find('div.output').append(toinsert);
289 };
289 };
290
290
291
291
292 CodeCell.prototype.append_display_data = function (json) {
292 CodeCell.prototype.append_display_data = function (json) {
293 var toinsert = this.create_output_area();
293 var toinsert = this.create_output_area();
294 this.append_mime_type(json, toinsert)
294 this.append_mime_type(json, toinsert)
295 this.element.find('div.output').append(toinsert);
295 this.element.find('div.output').append(toinsert);
296 // If we just output latex, typeset it.
296 // If we just output latex, typeset it.
297 if (json.latex !== undefined) {
297 if (json.latex !== undefined) {
298 MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
298 MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
299 };
299 };
300 };
300 };
301
301
302
302
303 CodeCell.prototype.append_mime_type = function (json, element) {
303 CodeCell.prototype.append_mime_type = function (json, element) {
304 if (json.html !== undefined) {
304 if (json.html !== undefined) {
305 this.append_html(json.html, element);
305 this.append_html(json.html, element);
306 } else if (json.latex !== undefined) {
306 } else if (json.latex !== undefined) {
307 this.append_latex(json.latex, element);
307 this.append_latex(json.latex, element);
308 } else if (json.svg !== undefined) {
308 } else if (json.svg !== undefined) {
309 this.append_svg(json.svg, element);
309 this.append_svg(json.svg, element);
310 } else if (json.png !== undefined) {
310 } else if (json.png !== undefined) {
311 this.append_png(json.png, element);
311 this.append_png(json.png, element);
312 } else if (json.jpeg !== undefined) {
312 } else if (json.jpeg !== undefined) {
313 this.append_jpeg(json.jpeg, element);
313 this.append_jpeg(json.jpeg, element);
314 } else if (json.text !== undefined) {
314 } else if (json.text !== undefined) {
315 this.append_text(json.text, element);
315 this.append_text(json.text, element);
316 };
316 };
317 };
317 };
318
318
319
319
320 CodeCell.prototype.append_html = function (html, element) {
320 CodeCell.prototype.append_html = function (html, element) {
321 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_html rendered_html");
321 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_html rendered_html");
322 toinsert.append(html);
322 toinsert.append(html);
323 element.append(toinsert);
323 element.append(toinsert);
324 }
324 }
325
325
326
326
327 CodeCell.prototype.append_text = function (data, element, extra_class) {
327 CodeCell.prototype.append_text = function (data, element, extra_class) {
328 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_text");
328 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_text");
329 if (extra_class){
329 if (extra_class){
330 toinsert.addClass(extra_class);
330 toinsert.addClass(extra_class);
331 }
331 }
332 toinsert.append($("<pre/>").html(data));
332 toinsert.append($("<pre/>").html(data));
333 element.append(toinsert);
333 element.append(toinsert);
334 };
334 };
335
335
336
336
337 CodeCell.prototype.append_svg = function (svg, element) {
337 CodeCell.prototype.append_svg = function (svg, element) {
338 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_svg");
338 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_svg");
339 toinsert.append(svg);
339 toinsert.append(svg);
340 element.append(toinsert);
340 element.append(toinsert);
341 };
341 };
342
342
343
343
344 CodeCell.prototype.append_png = function (png, element) {
344 CodeCell.prototype.append_png = function (png, element) {
345 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_png");
345 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_png");
346 toinsert.append($("<img/>").attr('src','data:image/png;base64,'+png));
346 toinsert.append($("<img/>").attr('src','data:image/png;base64,'+png));
347 element.append(toinsert);
347 element.append(toinsert);
348 };
348 };
349
349
350
350
351 CodeCell.prototype.append_jpeg = function (jpeg, element) {
351 CodeCell.prototype.append_jpeg = function (jpeg, element) {
352 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_jpeg");
352 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_jpeg");
353 toinsert.append($("<img/>").attr('src','data:image/jpeg;base64,'+jpeg));
353 toinsert.append($("<img/>").attr('src','data:image/jpeg;base64,'+jpeg));
354 element.append(toinsert);
354 element.append(toinsert);
355 };
355 };
356
356
357
357
358 CodeCell.prototype.append_latex = function (latex, element) {
358 CodeCell.prototype.append_latex = function (latex, element) {
359 // This method cannot do the typesetting because the latex first has to
359 // This method cannot do the typesetting because the latex first has to
360 // be on the page.
360 // be on the page.
361 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_latex");
361 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_latex");
362 toinsert.append(latex);
362 toinsert.append(latex);
363 element.append(toinsert);
363 element.append(toinsert);
364 }
364 }
365
365
366
366
367 CodeCell.prototype.clear_output = function () {
367 CodeCell.prototype.clear_output = function (stdout, stderr, other) {
368 this.element.find("div.output").html("");
368 var output_div = this.element.find("div.output");
369 this.outputs = [];
369 if (stdout && stderr && other){
370 // clear all, no need for logic
371 output_div.html("");
372 this.outputs = [];
373 return;
374 }
375 // remove html output
376 // each output_subarea that has an identifying class is in an output_area
377 // which is the element to be removed.
378 if (stdout){
379 output_div.find("div.output_stdout").parent().remove();
380 }
381 if (stderr){
382 output_div.find("div.output_stderr").parent().remove();
383 }
384 if (other){
385 output_div.find("div.output_subarea").not("div.output_stderr").not("div.output_stdout").parent().remove();
386 }
387
388 // remove cleared outputs from JSON list:
389 for (var i = this.outputs.length - 1; i >= 0; i--){
390 var out = this.outputs[i];
391 var output_type = out.output_type;
392 if (output_type == "display_data" && other){
393 this.outputs.splice(i,1);
394 }else if (output_type == "stream"){
395 if (stdout && out.stream == "stdout"){
396 this.outputs.splice(i,1);
397 }else if (stderr && out.stream == "stderr"){
398 this.outputs.splice(i,1);
399 }
400 }
401 }
370 };
402 };
371
403
372
404
373 CodeCell.prototype.clear_input = function () {
405 CodeCell.prototype.clear_input = function () {
374 this.code_mirror.setValue('');
406 this.code_mirror.setValue('');
375 };
407 };
376
408
377
409
378 CodeCell.prototype.collapse = function () {
410 CodeCell.prototype.collapse = function () {
379 if (!this.collapsed) {
411 if (!this.collapsed) {
380 this.element.find('div.output').hide();
412 this.element.find('div.output').hide();
381 this.collapsed = true;
413 this.collapsed = true;
382 };
414 };
383 };
415 };
384
416
385
417
386 CodeCell.prototype.expand = function () {
418 CodeCell.prototype.expand = function () {
387 if (this.collapsed) {
419 if (this.collapsed) {
388 this.element.find('div.output').show();
420 this.element.find('div.output').show();
389 this.collapsed = false;
421 this.collapsed = false;
390 };
422 };
391 };
423 };
392
424
393
425
394 CodeCell.prototype.toggle_output = function () {
426 CodeCell.prototype.toggle_output = function () {
395 if (this.collapsed) {
427 if (this.collapsed) {
396 this.expand();
428 this.expand();
397 } else {
429 } else {
398 this.collapse();
430 this.collapse();
399 };
431 };
400 };
432 };
401
433
402 CodeCell.prototype.set_input_prompt = function (number) {
434 CodeCell.prototype.set_input_prompt = function (number) {
403 var n = number || ' ';
435 var n = number || ' ';
404 this.input_prompt_number = n
436 this.input_prompt_number = n
405 this.element.find('div.input_prompt').html('In&nbsp;[' + n + ']:');
437 this.element.find('div.input_prompt').html('In&nbsp;[' + n + ']:');
406 };
438 };
407
439
408
440
409 CodeCell.prototype.get_code = function () {
441 CodeCell.prototype.get_code = function () {
410 return this.code_mirror.getValue();
442 return this.code_mirror.getValue();
411 };
443 };
412
444
413
445
414 CodeCell.prototype.set_code = function (code) {
446 CodeCell.prototype.set_code = function (code) {
415 return this.code_mirror.setValue(code);
447 return this.code_mirror.setValue(code);
416 };
448 };
417
449
418
450
419 CodeCell.prototype.at_top = function () {
451 CodeCell.prototype.at_top = function () {
420 var cursor = this.code_mirror.getCursor();
452 var cursor = this.code_mirror.getCursor();
421 if (cursor.line === 0) {
453 if (cursor.line === 0) {
422 return true;
454 return true;
423 } else {
455 } else {
424 return false;
456 return false;
425 }
457 }
426 };
458 };
427
459
428
460
429 CodeCell.prototype.at_bottom = function () {
461 CodeCell.prototype.at_bottom = function () {
430 var cursor = this.code_mirror.getCursor();
462 var cursor = this.code_mirror.getCursor();
431 if (cursor.line === (this.code_mirror.lineCount()-1)) {
463 if (cursor.line === (this.code_mirror.lineCount()-1)) {
432 return true;
464 return true;
433 } else {
465 } else {
434 return false;
466 return false;
435 }
467 }
436 };
468 };
437
469
438
470
439 CodeCell.prototype.fromJSON = function (data) {
471 CodeCell.prototype.fromJSON = function (data) {
440 console.log('Import from JSON:', data);
472 console.log('Import from JSON:', data);
441 if (data.cell_type === 'code') {
473 if (data.cell_type === 'code') {
442 if (data.input !== undefined) {
474 if (data.input !== undefined) {
443 this.set_code(data.input);
475 this.set_code(data.input);
444 }
476 }
445 if (data.prompt_number !== undefined) {
477 if (data.prompt_number !== undefined) {
446 this.set_input_prompt(data.prompt_number);
478 this.set_input_prompt(data.prompt_number);
447 } else {
479 } else {
448 this.set_input_prompt();
480 this.set_input_prompt();
449 };
481 };
450 var len = data.outputs.length;
482 var len = data.outputs.length;
451 for (var i=0; i<len; i++) {
483 for (var i=0; i<len; i++) {
452 this.append_output(data.outputs[i]);
484 this.append_output(data.outputs[i]);
453 };
485 };
454 if (data.collapsed !== undefined) {
486 if (data.collapsed !== undefined) {
455 if (data.collapsed) {
487 if (data.collapsed) {
456 this.collapse();
488 this.collapse();
457 };
489 };
458 };
490 };
459 };
491 };
460 };
492 };
461
493
462
494
463 CodeCell.prototype.toJSON = function () {
495 CodeCell.prototype.toJSON = function () {
464 var data = {};
496 var data = {};
465 data.input = this.get_code();
497 data.input = this.get_code();
466 data.cell_type = 'code';
498 data.cell_type = 'code';
467 if (this.input_prompt_number !== ' ') {
499 if (this.input_prompt_number !== ' ') {
468 data.prompt_number = this.input_prompt_number
500 data.prompt_number = this.input_prompt_number
469 };
501 };
470 var outputs = [];
502 var outputs = [];
471 var len = this.outputs.length;
503 var len = this.outputs.length;
472 for (var i=0; i<len; i++) {
504 for (var i=0; i<len; i++) {
473 outputs[i] = this.outputs[i];
505 outputs[i] = this.outputs[i];
474 };
506 };
475 data.outputs = outputs;
507 data.outputs = outputs;
476 data.language = 'python';
508 data.language = 'python';
477 data.collapsed = this.collapsed;
509 data.collapsed = this.collapsed;
478 // console.log('Export to JSON:',data);
510 // console.log('Export to JSON:',data);
479 return data;
511 return data;
480 };
512 };
481
513
482
514
483 IPython.CodeCell = CodeCell;
515 IPython.CodeCell = CodeCell;
484
516
485 return IPython;
517 return IPython;
486 }(IPython));
518 }(IPython));
@@ -1,995 +1,997 b''
1 //----------------------------------------------------------------------------
1 //----------------------------------------------------------------------------
2 // Copyright (C) 2008-2011 The IPython Development Team
2 // Copyright (C) 2008-2011 The IPython Development Team
3 //
3 //
4 // Distributed under the terms of the BSD License. The full license is in
4 // Distributed under the terms of the BSD License. The full license is in
5 // the file COPYING, distributed as part of this software.
5 // the file COPYING, distributed as part of this software.
6 //----------------------------------------------------------------------------
6 //----------------------------------------------------------------------------
7
7
8 //============================================================================
8 //============================================================================
9 // Notebook
9 // Notebook
10 //============================================================================
10 //============================================================================
11
11
12 var IPython = (function (IPython) {
12 var IPython = (function (IPython) {
13
13
14 var utils = IPython.utils;
14 var utils = IPython.utils;
15
15
16 var Notebook = function (selector) {
16 var Notebook = function (selector) {
17 this.element = $(selector);
17 this.element = $(selector);
18 this.element.scroll();
18 this.element.scroll();
19 this.element.data("notebook", this);
19 this.element.data("notebook", this);
20 this.next_prompt_number = 1;
20 this.next_prompt_number = 1;
21 this.kernel = null;
21 this.kernel = null;
22 this.dirty = false;
22 this.dirty = false;
23 this.msg_cell_map = {};
23 this.msg_cell_map = {};
24 this.metadata = {};
24 this.metadata = {};
25 this.control_key_active = false;
25 this.control_key_active = false;
26 this.style();
26 this.style();
27 this.create_elements();
27 this.create_elements();
28 this.bind_events();
28 this.bind_events();
29 };
29 };
30
30
31
31
32 Notebook.prototype.style = function () {
32 Notebook.prototype.style = function () {
33 $('div#notebook').addClass('border-box-sizing');
33 $('div#notebook').addClass('border-box-sizing');
34 };
34 };
35
35
36
36
37 Notebook.prototype.create_elements = function () {
37 Notebook.prototype.create_elements = function () {
38 // We add this end_space div to the end of the notebook div to:
38 // We add this end_space div to the end of the notebook div to:
39 // i) provide a margin between the last cell and the end of the notebook
39 // i) provide a margin between the last cell and the end of the notebook
40 // ii) to prevent the div from scrolling up when the last cell is being
40 // ii) to prevent the div from scrolling up when the last cell is being
41 // edited, but is too low on the page, which browsers will do automatically.
41 // edited, but is too low on the page, which browsers will do automatically.
42 var that = this;
42 var that = this;
43 var end_space = $('<div class="end_space"></div>').height(150);
43 var end_space = $('<div class="end_space"></div>').height(150);
44 end_space.dblclick(function (e) {
44 end_space.dblclick(function (e) {
45 var ncells = that.ncells();
45 var ncells = that.ncells();
46 that.insert_code_cell_below(ncells-1);
46 that.insert_code_cell_below(ncells-1);
47 });
47 });
48 this.element.append(end_space);
48 this.element.append(end_space);
49 $('div#notebook').addClass('border-box-sizing');
49 $('div#notebook').addClass('border-box-sizing');
50 };
50 };
51
51
52
52
53 Notebook.prototype.bind_events = function () {
53 Notebook.prototype.bind_events = function () {
54 var that = this;
54 var that = this;
55 $(document).keydown(function (event) {
55 $(document).keydown(function (event) {
56 // console.log(event);
56 // console.log(event);
57 if (event.which === 38) {
57 if (event.which === 38) {
58 var cell = that.selected_cell();
58 var cell = that.selected_cell();
59 if (cell.at_top()) {
59 if (cell.at_top()) {
60 event.preventDefault();
60 event.preventDefault();
61 that.select_prev();
61 that.select_prev();
62 };
62 };
63 } else if (event.which === 40) {
63 } else if (event.which === 40) {
64 var cell = that.selected_cell();
64 var cell = that.selected_cell();
65 if (cell.at_bottom()) {
65 if (cell.at_bottom()) {
66 event.preventDefault();
66 event.preventDefault();
67 that.select_next();
67 that.select_next();
68 };
68 };
69 } else if (event.which === 13 && event.shiftKey) {
69 } else if (event.which === 13 && event.shiftKey) {
70 that.execute_selected_cell();
70 that.execute_selected_cell();
71 return false;
71 return false;
72 } else if (event.which === 13 && event.ctrlKey) {
72 } else if (event.which === 13 && event.ctrlKey) {
73 that.execute_selected_cell({terminal:true});
73 that.execute_selected_cell({terminal:true});
74 return false;
74 return false;
75 } else if (event.which === 77 && event.ctrlKey) {
75 } else if (event.which === 77 && event.ctrlKey) {
76 that.control_key_active = true;
76 that.control_key_active = true;
77 return false;
77 return false;
78 } else if (event.which === 68 && that.control_key_active) {
78 } else if (event.which === 68 && that.control_key_active) {
79 // Delete selected cell = d
79 // Delete selected cell = d
80 that.delete_cell();
80 that.delete_cell();
81 that.control_key_active = false;
81 that.control_key_active = false;
82 return false;
82 return false;
83 } else if (event.which === 65 && that.control_key_active) {
83 } else if (event.which === 65 && that.control_key_active) {
84 // Insert code cell above selected = a
84 // Insert code cell above selected = a
85 that.insert_code_cell_above();
85 that.insert_code_cell_above();
86 that.control_key_active = false;
86 that.control_key_active = false;
87 return false;
87 return false;
88 } else if (event.which === 66 && that.control_key_active) {
88 } else if (event.which === 66 && that.control_key_active) {
89 // Insert code cell below selected = b
89 // Insert code cell below selected = b
90 that.insert_code_cell_below();
90 that.insert_code_cell_below();
91 that.control_key_active = false;
91 that.control_key_active = false;
92 return false;
92 return false;
93 } else if (event.which === 67 && that.control_key_active) {
93 } else if (event.which === 67 && that.control_key_active) {
94 // To code = c
94 // To code = c
95 that.to_code();
95 that.to_code();
96 that.control_key_active = false;
96 that.control_key_active = false;
97 return false;
97 return false;
98 } else if (event.which === 77 && that.control_key_active) {
98 } else if (event.which === 77 && that.control_key_active) {
99 // To markdown = m
99 // To markdown = m
100 that.to_markdown();
100 that.to_markdown();
101 that.control_key_active = false;
101 that.control_key_active = false;
102 return false;
102 return false;
103 } else if (event.which === 84 && that.control_key_active) {
103 } else if (event.which === 84 && that.control_key_active) {
104 // Toggle output = t
104 // Toggle output = t
105 that.toggle_output();
105 that.toggle_output();
106 that.control_key_active = false;
106 that.control_key_active = false;
107 return false;
107 return false;
108 } else if (event.which === 83 && that.control_key_active) {
108 } else if (event.which === 83 && that.control_key_active) {
109 // Save notebook = s
109 // Save notebook = s
110 IPython.save_widget.save_notebook();
110 IPython.save_widget.save_notebook();
111 that.control_key_active = false;
111 that.control_key_active = false;
112 return false;
112 return false;
113 } else if (event.which === 74 && that.control_key_active) {
113 } else if (event.which === 74 && that.control_key_active) {
114 // Move cell down = j
114 // Move cell down = j
115 that.move_cell_down();
115 that.move_cell_down();
116 that.control_key_active = false;
116 that.control_key_active = false;
117 return false;
117 return false;
118 } else if (event.which === 75 && that.control_key_active) {
118 } else if (event.which === 75 && that.control_key_active) {
119 // Move cell up = k
119 // Move cell up = k
120 that.move_cell_up();
120 that.move_cell_up();
121 that.control_key_active = false;
121 that.control_key_active = false;
122 return false;
122 return false;
123 } else if (event.which === 80 && that.control_key_active) {
123 } else if (event.which === 80 && that.control_key_active) {
124 // Select previous = p
124 // Select previous = p
125 that.select_prev();
125 that.select_prev();
126 that.control_key_active = false;
126 that.control_key_active = false;
127 return false;
127 return false;
128 } else if (event.which === 78 && that.control_key_active) {
128 } else if (event.which === 78 && that.control_key_active) {
129 // Select next = n
129 // Select next = n
130 that.select_next();
130 that.select_next();
131 that.control_key_active = false;
131 that.control_key_active = false;
132 return false;
132 return false;
133 } else if (event.which === 76 && that.control_key_active) {
133 } else if (event.which === 76 && that.control_key_active) {
134 // Toggle line numbers = l
134 // Toggle line numbers = l
135 that.cell_toggle_line_numbers();
135 that.cell_toggle_line_numbers();
136 that.control_key_active = false;
136 that.control_key_active = false;
137 return false;
137 return false;
138 } else if (event.which === 73 && that.control_key_active) {
138 } else if (event.which === 73 && that.control_key_active) {
139 // Interrupt kernel = i
139 // Interrupt kernel = i
140 IPython.notebook.kernel.interrupt();
140 IPython.notebook.kernel.interrupt();
141 that.control_key_active = false;
141 that.control_key_active = false;
142 return false;
142 return false;
143 } else if (event.which === 190 && that.control_key_active) {
143 } else if (event.which === 190 && that.control_key_active) {
144 // Restart kernel = . # matches qt console
144 // Restart kernel = . # matches qt console
145 IPython.notebook.restart_kernel();
145 IPython.notebook.restart_kernel();
146 that.control_key_active = false;
146 that.control_key_active = false;
147 return false;
147 return false;
148 } else if (event.which === 72 && that.control_key_active) {
148 } else if (event.which === 72 && that.control_key_active) {
149 // Show keyboard shortcuts = h
149 // Show keyboard shortcuts = h
150 that.toggle_keyboard_shortcuts();
150 that.toggle_keyboard_shortcuts();
151 that.control_key_active = false;
151 that.control_key_active = false;
152 return false;
152 return false;
153 } else if (that.control_key_active) {
153 } else if (that.control_key_active) {
154 that.control_key_active = false;
154 that.control_key_active = false;
155 return true;
155 return true;
156 };
156 };
157 });
157 });
158
158
159 this.element.bind('collapse_pager', function () {
159 this.element.bind('collapse_pager', function () {
160 var app_height = $('div#main_app').height(); // content height
160 var app_height = $('div#main_app').height(); // content height
161 var splitter_height = $('div#pager_splitter').outerHeight(true);
161 var splitter_height = $('div#pager_splitter').outerHeight(true);
162 var new_height = app_height - splitter_height;
162 var new_height = app_height - splitter_height;
163 that.element.animate({height : new_height + 'px'}, 'fast');
163 that.element.animate({height : new_height + 'px'}, 'fast');
164 });
164 });
165
165
166 this.element.bind('expand_pager', function () {
166 this.element.bind('expand_pager', function () {
167 var app_height = $('div#main_app').height(); // content height
167 var app_height = $('div#main_app').height(); // content height
168 var splitter_height = $('div#pager_splitter').outerHeight(true);
168 var splitter_height = $('div#pager_splitter').outerHeight(true);
169 var pager_height = $('div#pager').outerHeight(true);
169 var pager_height = $('div#pager').outerHeight(true);
170 var new_height = app_height - pager_height - splitter_height;
170 var new_height = app_height - pager_height - splitter_height;
171 that.element.animate({height : new_height + 'px'}, 'fast');
171 that.element.animate({height : new_height + 'px'}, 'fast');
172 });
172 });
173
173
174 this.element.bind('collapse_left_panel', function () {
174 this.element.bind('collapse_left_panel', function () {
175 var splitter_width = $('div#left_panel_splitter').outerWidth(true);
175 var splitter_width = $('div#left_panel_splitter').outerWidth(true);
176 var new_margin = splitter_width;
176 var new_margin = splitter_width;
177 $('div#notebook_panel').animate({marginLeft : new_margin + 'px'}, 'fast');
177 $('div#notebook_panel').animate({marginLeft : new_margin + 'px'}, 'fast');
178 });
178 });
179
179
180 this.element.bind('expand_left_panel', function () {
180 this.element.bind('expand_left_panel', function () {
181 var splitter_width = $('div#left_panel_splitter').outerWidth(true);
181 var splitter_width = $('div#left_panel_splitter').outerWidth(true);
182 var left_panel_width = IPython.left_panel.width;
182 var left_panel_width = IPython.left_panel.width;
183 var new_margin = splitter_width + left_panel_width;
183 var new_margin = splitter_width + left_panel_width;
184 $('div#notebook_panel').animate({marginLeft : new_margin + 'px'}, 'fast');
184 $('div#notebook_panel').animate({marginLeft : new_margin + 'px'}, 'fast');
185 });
185 });
186
186
187 $(window).bind('beforeunload', function () {
187 $(window).bind('beforeunload', function () {
188 var kill_kernel = $('#kill_kernel').prop('checked');
188 var kill_kernel = $('#kill_kernel').prop('checked');
189 if (kill_kernel) {
189 if (kill_kernel) {
190 that.kernel.kill();
190 that.kernel.kill();
191 }
191 }
192 if (that.dirty) {
192 if (that.dirty) {
193 return "You have unsaved changes that will be lost if you leave this page.";
193 return "You have unsaved changes that will be lost if you leave this page.";
194 };
194 };
195 });
195 });
196 };
196 };
197
197
198
198
199 Notebook.prototype.toggle_keyboard_shortcuts = function () {
199 Notebook.prototype.toggle_keyboard_shortcuts = function () {
200 // toggles display of keyboard shortcut dialog
200 // toggles display of keyboard shortcut dialog
201 var that = this;
201 var that = this;
202 if ( this.shortcut_dialog ){
202 if ( this.shortcut_dialog ){
203 // if dialog is already shown, close it
203 // if dialog is already shown, close it
204 this.shortcut_dialog.dialog("close");
204 this.shortcut_dialog.dialog("close");
205 this.shortcut_dialog = null;
205 this.shortcut_dialog = null;
206 return;
206 return;
207 }
207 }
208 var dialog = $('<div/>');
208 var dialog = $('<div/>');
209 this.shortcut_dialog = dialog;
209 this.shortcut_dialog = dialog;
210 var shortcuts = [
210 var shortcuts = [
211 {key: 'Shift-Enter', help: 'run cell'},
211 {key: 'Shift-Enter', help: 'run cell'},
212 {key: 'Ctrl-Enter', help: 'run cell in-place'},
212 {key: 'Ctrl-Enter', help: 'run cell in-place'},
213 {key: 'Ctrl-m d', help: 'delete cell'},
213 {key: 'Ctrl-m d', help: 'delete cell'},
214 {key: 'Ctrl-m a', help: 'insert cell above'},
214 {key: 'Ctrl-m a', help: 'insert cell above'},
215 {key: 'Ctrl-m b', help: 'insert cell below'},
215 {key: 'Ctrl-m b', help: 'insert cell below'},
216 {key: 'Ctrl-m t', help: 'toggle output'},
216 {key: 'Ctrl-m t', help: 'toggle output'},
217 {key: 'Ctrl-m l', help: 'toggle line numbers'},
217 {key: 'Ctrl-m l', help: 'toggle line numbers'},
218 {key: 'Ctrl-m s', help: 'save notebook'},
218 {key: 'Ctrl-m s', help: 'save notebook'},
219 {key: 'Ctrl-m j', help: 'move cell down'},
219 {key: 'Ctrl-m j', help: 'move cell down'},
220 {key: 'Ctrl-m k', help: 'move cell up'},
220 {key: 'Ctrl-m k', help: 'move cell up'},
221 {key: 'Ctrl-m c', help: 'code cell'},
221 {key: 'Ctrl-m c', help: 'code cell'},
222 {key: 'Ctrl-m m', help: 'markdown cell'},
222 {key: 'Ctrl-m m', help: 'markdown cell'},
223 {key: 'Ctrl-m p', help: 'select previous'},
223 {key: 'Ctrl-m p', help: 'select previous'},
224 {key: 'Ctrl-m n', help: 'select next'},
224 {key: 'Ctrl-m n', help: 'select next'},
225 {key: 'Ctrl-m i', help: 'interrupt kernel'},
225 {key: 'Ctrl-m i', help: 'interrupt kernel'},
226 {key: 'Ctrl-m .', help: 'restart kernel'},
226 {key: 'Ctrl-m .', help: 'restart kernel'},
227 {key: 'Ctrl-m h', help: 'show keyboard shortcuts'}
227 {key: 'Ctrl-m h', help: 'show keyboard shortcuts'}
228 ];
228 ];
229 for (var i=0; i<shortcuts.length; i++) {
229 for (var i=0; i<shortcuts.length; i++) {
230 dialog.append($('<div>').
230 dialog.append($('<div>').
231 append($('<span/>').addClass('shortcut_key').html(shortcuts[i].key)).
231 append($('<span/>').addClass('shortcut_key').html(shortcuts[i].key)).
232 append($('<span/>').addClass('shortcut_descr').html(' : ' + shortcuts[i].help))
232 append($('<span/>').addClass('shortcut_descr').html(' : ' + shortcuts[i].help))
233 );
233 );
234 };
234 };
235 dialog.bind('dialogclose', function(event) {
235 dialog.bind('dialogclose', function(event) {
236 // dialog has been closed, allow it to be drawn again.
236 // dialog has been closed, allow it to be drawn again.
237 that.shortcut_dialog = null;
237 that.shortcut_dialog = null;
238 });
238 });
239 dialog.dialog({title: 'Keyboard shortcuts'});
239 dialog.dialog({title: 'Keyboard shortcuts'});
240 };
240 };
241
241
242
242
243 Notebook.prototype.scroll_to_bottom = function () {
243 Notebook.prototype.scroll_to_bottom = function () {
244 this.element.animate({scrollTop:this.element.get(0).scrollHeight}, 0);
244 this.element.animate({scrollTop:this.element.get(0).scrollHeight}, 0);
245 };
245 };
246
246
247
247
248 Notebook.prototype.scroll_to_top = function () {
248 Notebook.prototype.scroll_to_top = function () {
249 this.element.animate({scrollTop:0}, 0);
249 this.element.animate({scrollTop:0}, 0);
250 };
250 };
251
251
252
252
253 // Cell indexing, retrieval, etc.
253 // Cell indexing, retrieval, etc.
254
254
255
255
256 Notebook.prototype.cell_elements = function () {
256 Notebook.prototype.cell_elements = function () {
257 return this.element.children("div.cell");
257 return this.element.children("div.cell");
258 }
258 }
259
259
260
260
261 Notebook.prototype.ncells = function (cell) {
261 Notebook.prototype.ncells = function (cell) {
262 return this.cell_elements().length;
262 return this.cell_elements().length;
263 }
263 }
264
264
265
265
266 // TODO: we are often calling cells as cells()[i], which we should optimize
266 // TODO: we are often calling cells as cells()[i], which we should optimize
267 // to cells(i) or a new method.
267 // to cells(i) or a new method.
268 Notebook.prototype.cells = function () {
268 Notebook.prototype.cells = function () {
269 return this.cell_elements().toArray().map(function (e) {
269 return this.cell_elements().toArray().map(function (e) {
270 return $(e).data("cell");
270 return $(e).data("cell");
271 });
271 });
272 }
272 }
273
273
274
274
275 Notebook.prototype.find_cell_index = function (cell) {
275 Notebook.prototype.find_cell_index = function (cell) {
276 var result = null;
276 var result = null;
277 this.cell_elements().filter(function (index) {
277 this.cell_elements().filter(function (index) {
278 if ($(this).data("cell") === cell) {
278 if ($(this).data("cell") === cell) {
279 result = index;
279 result = index;
280 };
280 };
281 });
281 });
282 return result;
282 return result;
283 };
283 };
284
284
285
285
286 Notebook.prototype.index_or_selected = function (index) {
286 Notebook.prototype.index_or_selected = function (index) {
287 return index || this.selected_index() || 0;
287 return index || this.selected_index() || 0;
288 }
288 }
289
289
290
290
291 Notebook.prototype.select = function (index) {
291 Notebook.prototype.select = function (index) {
292 if (index !== undefined && index >= 0 && index < this.ncells()) {
292 if (index !== undefined && index >= 0 && index < this.ncells()) {
293 if (this.selected_index() !== null) {
293 if (this.selected_index() !== null) {
294 this.selected_cell().unselect();
294 this.selected_cell().unselect();
295 };
295 };
296 this.cells()[index].select();
296 this.cells()[index].select();
297 };
297 };
298 return this;
298 return this;
299 };
299 };
300
300
301
301
302 Notebook.prototype.select_next = function () {
302 Notebook.prototype.select_next = function () {
303 var index = this.selected_index();
303 var index = this.selected_index();
304 if (index !== null && index >= 0 && (index+1) < this.ncells()) {
304 if (index !== null && index >= 0 && (index+1) < this.ncells()) {
305 this.select(index+1);
305 this.select(index+1);
306 };
306 };
307 return this;
307 return this;
308 };
308 };
309
309
310
310
311 Notebook.prototype.select_prev = function () {
311 Notebook.prototype.select_prev = function () {
312 var index = this.selected_index();
312 var index = this.selected_index();
313 if (index !== null && index >= 0 && (index-1) < this.ncells()) {
313 if (index !== null && index >= 0 && (index-1) < this.ncells()) {
314 this.select(index-1);
314 this.select(index-1);
315 };
315 };
316 return this;
316 return this;
317 };
317 };
318
318
319
319
320 Notebook.prototype.selected_index = function () {
320 Notebook.prototype.selected_index = function () {
321 var result = null;
321 var result = null;
322 this.cell_elements().filter(function (index) {
322 this.cell_elements().filter(function (index) {
323 if ($(this).data("cell").selected === true) {
323 if ($(this).data("cell").selected === true) {
324 result = index;
324 result = index;
325 };
325 };
326 });
326 });
327 return result;
327 return result;
328 };
328 };
329
329
330
330
331 Notebook.prototype.cell_for_msg = function (msg_id) {
331 Notebook.prototype.cell_for_msg = function (msg_id) {
332 var cell_id = this.msg_cell_map[msg_id];
332 var cell_id = this.msg_cell_map[msg_id];
333 var result = null;
333 var result = null;
334 this.cell_elements().filter(function (index) {
334 this.cell_elements().filter(function (index) {
335 cell = $(this).data("cell");
335 cell = $(this).data("cell");
336 if (cell.cell_id === cell_id) {
336 if (cell.cell_id === cell_id) {
337 result = cell;
337 result = cell;
338 };
338 };
339 });
339 });
340 return result;
340 return result;
341 };
341 };
342
342
343
343
344 Notebook.prototype.selected_cell = function () {
344 Notebook.prototype.selected_cell = function () {
345 return this.cell_elements().eq(this.selected_index()).data("cell");
345 return this.cell_elements().eq(this.selected_index()).data("cell");
346 }
346 }
347
347
348
348
349 // Cell insertion, deletion and moving.
349 // Cell insertion, deletion and moving.
350
350
351
351
352 Notebook.prototype.delete_cell = function (index) {
352 Notebook.prototype.delete_cell = function (index) {
353 var i = index || this.selected_index();
353 var i = index || this.selected_index();
354 if (i !== null && i >= 0 && i < this.ncells()) {
354 if (i !== null && i >= 0 && i < this.ncells()) {
355 this.cell_elements().eq(i).remove();
355 this.cell_elements().eq(i).remove();
356 if (i === (this.ncells())) {
356 if (i === (this.ncells())) {
357 this.select(i-1);
357 this.select(i-1);
358 } else {
358 } else {
359 this.select(i);
359 this.select(i);
360 };
360 };
361 };
361 };
362 this.dirty = true;
362 this.dirty = true;
363 return this;
363 return this;
364 };
364 };
365
365
366
366
367 Notebook.prototype.append_cell = function (cell) {
367 Notebook.prototype.append_cell = function (cell) {
368 this.element.find('div.end_space').before(cell.element);
368 this.element.find('div.end_space').before(cell.element);
369 this.dirty = true;
369 this.dirty = true;
370 return this;
370 return this;
371 };
371 };
372
372
373
373
374 Notebook.prototype.insert_cell_below = function (cell, index) {
374 Notebook.prototype.insert_cell_below = function (cell, index) {
375 var ncells = this.ncells();
375 var ncells = this.ncells();
376 if (ncells === 0) {
376 if (ncells === 0) {
377 this.append_cell(cell);
377 this.append_cell(cell);
378 return this;
378 return this;
379 };
379 };
380 if (index >= 0 && index < ncells) {
380 if (index >= 0 && index < ncells) {
381 this.cell_elements().eq(index).after(cell.element);
381 this.cell_elements().eq(index).after(cell.element);
382 };
382 };
383 this.dirty = true;
383 this.dirty = true;
384 return this
384 return this
385 };
385 };
386
386
387
387
388 Notebook.prototype.insert_cell_above = function (cell, index) {
388 Notebook.prototype.insert_cell_above = function (cell, index) {
389 var ncells = this.ncells();
389 var ncells = this.ncells();
390 if (ncells === 0) {
390 if (ncells === 0) {
391 this.append_cell(cell);
391 this.append_cell(cell);
392 return this;
392 return this;
393 };
393 };
394 if (index >= 0 && index < ncells) {
394 if (index >= 0 && index < ncells) {
395 this.cell_elements().eq(index).before(cell.element);
395 this.cell_elements().eq(index).before(cell.element);
396 };
396 };
397 this.dirty = true;
397 this.dirty = true;
398 return this;
398 return this;
399 };
399 };
400
400
401
401
402 Notebook.prototype.move_cell_up = function (index) {
402 Notebook.prototype.move_cell_up = function (index) {
403 var i = index || this.selected_index();
403 var i = index || this.selected_index();
404 if (i !== null && i < this.ncells() && i > 0) {
404 if (i !== null && i < this.ncells() && i > 0) {
405 var pivot = this.cell_elements().eq(i-1);
405 var pivot = this.cell_elements().eq(i-1);
406 var tomove = this.cell_elements().eq(i);
406 var tomove = this.cell_elements().eq(i);
407 if (pivot !== null && tomove !== null) {
407 if (pivot !== null && tomove !== null) {
408 tomove.detach();
408 tomove.detach();
409 pivot.before(tomove);
409 pivot.before(tomove);
410 this.select(i-1);
410 this.select(i-1);
411 };
411 };
412 };
412 };
413 this.dirty = true;
413 this.dirty = true;
414 return this;
414 return this;
415 }
415 }
416
416
417
417
418 Notebook.prototype.move_cell_down = function (index) {
418 Notebook.prototype.move_cell_down = function (index) {
419 var i = index || this.selected_index();
419 var i = index || this.selected_index();
420 if (i !== null && i < (this.ncells()-1) && i >= 0) {
420 if (i !== null && i < (this.ncells()-1) && i >= 0) {
421 var pivot = this.cell_elements().eq(i+1)
421 var pivot = this.cell_elements().eq(i+1)
422 var tomove = this.cell_elements().eq(i)
422 var tomove = this.cell_elements().eq(i)
423 if (pivot !== null && tomove !== null) {
423 if (pivot !== null && tomove !== null) {
424 tomove.detach();
424 tomove.detach();
425 pivot.after(tomove);
425 pivot.after(tomove);
426 this.select(i+1);
426 this.select(i+1);
427 };
427 };
428 };
428 };
429 this.dirty = true;
429 this.dirty = true;
430 return this;
430 return this;
431 }
431 }
432
432
433
433
434 Notebook.prototype.sort_cells = function () {
434 Notebook.prototype.sort_cells = function () {
435 var ncells = this.ncells();
435 var ncells = this.ncells();
436 var sindex = this.selected_index();
436 var sindex = this.selected_index();
437 var swapped;
437 var swapped;
438 do {
438 do {
439 swapped = false
439 swapped = false
440 for (var i=1; i<ncells; i++) {
440 for (var i=1; i<ncells; i++) {
441 current = this.cell_elements().eq(i).data("cell");
441 current = this.cell_elements().eq(i).data("cell");
442 previous = this.cell_elements().eq(i-1).data("cell");
442 previous = this.cell_elements().eq(i-1).data("cell");
443 if (previous.input_prompt_number > current.input_prompt_number) {
443 if (previous.input_prompt_number > current.input_prompt_number) {
444 this.move_cell_up(i);
444 this.move_cell_up(i);
445 swapped = true;
445 swapped = true;
446 };
446 };
447 };
447 };
448 } while (swapped);
448 } while (swapped);
449 this.select(sindex);
449 this.select(sindex);
450 return this;
450 return this;
451 };
451 };
452
452
453
453
454 Notebook.prototype.insert_code_cell_above = function (index) {
454 Notebook.prototype.insert_code_cell_above = function (index) {
455 // TODO: Bounds check for i
455 // TODO: Bounds check for i
456 var i = this.index_or_selected(index);
456 var i = this.index_or_selected(index);
457 var cell = new IPython.CodeCell(this);
457 var cell = new IPython.CodeCell(this);
458 cell.set_input_prompt();
458 cell.set_input_prompt();
459 this.insert_cell_above(cell, i);
459 this.insert_cell_above(cell, i);
460 this.select(this.find_cell_index(cell));
460 this.select(this.find_cell_index(cell));
461 return cell;
461 return cell;
462 }
462 }
463
463
464
464
465 Notebook.prototype.insert_code_cell_below = function (index) {
465 Notebook.prototype.insert_code_cell_below = function (index) {
466 // TODO: Bounds check for i
466 // TODO: Bounds check for i
467 var i = this.index_or_selected(index);
467 var i = this.index_or_selected(index);
468 var cell = new IPython.CodeCell(this);
468 var cell = new IPython.CodeCell(this);
469 cell.set_input_prompt();
469 cell.set_input_prompt();
470 this.insert_cell_below(cell, i);
470 this.insert_cell_below(cell, i);
471 this.select(this.find_cell_index(cell));
471 this.select(this.find_cell_index(cell));
472 return cell;
472 return cell;
473 }
473 }
474
474
475
475
476 Notebook.prototype.insert_html_cell_above = function (index) {
476 Notebook.prototype.insert_html_cell_above = function (index) {
477 // TODO: Bounds check for i
477 // TODO: Bounds check for i
478 var i = this.index_or_selected(index);
478 var i = this.index_or_selected(index);
479 var cell = new IPython.HTMLCell(this);
479 var cell = new IPython.HTMLCell(this);
480 cell.config_mathjax();
480 cell.config_mathjax();
481 this.insert_cell_above(cell, i);
481 this.insert_cell_above(cell, i);
482 this.select(this.find_cell_index(cell));
482 this.select(this.find_cell_index(cell));
483 return cell;
483 return cell;
484 }
484 }
485
485
486
486
487 Notebook.prototype.insert_html_cell_below = function (index) {
487 Notebook.prototype.insert_html_cell_below = function (index) {
488 // TODO: Bounds check for i
488 // TODO: Bounds check for i
489 var i = this.index_or_selected(index);
489 var i = this.index_or_selected(index);
490 var cell = new IPython.HTMLCell(this);
490 var cell = new IPython.HTMLCell(this);
491 cell.config_mathjax();
491 cell.config_mathjax();
492 this.insert_cell_below(cell, i);
492 this.insert_cell_below(cell, i);
493 this.select(this.find_cell_index(cell));
493 this.select(this.find_cell_index(cell));
494 return cell;
494 return cell;
495 }
495 }
496
496
497
497
498 Notebook.prototype.insert_markdown_cell_above = function (index) {
498 Notebook.prototype.insert_markdown_cell_above = function (index) {
499 // TODO: Bounds check for i
499 // TODO: Bounds check for i
500 var i = this.index_or_selected(index);
500 var i = this.index_or_selected(index);
501 var cell = new IPython.MarkdownCell(this);
501 var cell = new IPython.MarkdownCell(this);
502 cell.config_mathjax();
502 cell.config_mathjax();
503 this.insert_cell_above(cell, i);
503 this.insert_cell_above(cell, i);
504 this.select(this.find_cell_index(cell));
504 this.select(this.find_cell_index(cell));
505 return cell;
505 return cell;
506 }
506 }
507
507
508
508
509 Notebook.prototype.insert_markdown_cell_below = function (index) {
509 Notebook.prototype.insert_markdown_cell_below = function (index) {
510 // TODO: Bounds check for i
510 // TODO: Bounds check for i
511 var i = this.index_or_selected(index);
511 var i = this.index_or_selected(index);
512 var cell = new IPython.MarkdownCell(this);
512 var cell = new IPython.MarkdownCell(this);
513 cell.config_mathjax();
513 cell.config_mathjax();
514 this.insert_cell_below(cell, i);
514 this.insert_cell_below(cell, i);
515 this.select(this.find_cell_index(cell));
515 this.select(this.find_cell_index(cell));
516 return cell;
516 return cell;
517 }
517 }
518
518
519
519
520 Notebook.prototype.to_code = function (index) {
520 Notebook.prototype.to_code = function (index) {
521 // TODO: Bounds check for i
521 // TODO: Bounds check for i
522 var i = this.index_or_selected(index);
522 var i = this.index_or_selected(index);
523 var source_element = this.cell_elements().eq(i);
523 var source_element = this.cell_elements().eq(i);
524 var source_cell = source_element.data("cell");
524 var source_cell = source_element.data("cell");
525 if (source_cell instanceof IPython.HTMLCell ||
525 if (source_cell instanceof IPython.HTMLCell ||
526 source_cell instanceof IPython.MarkdownCell) {
526 source_cell instanceof IPython.MarkdownCell) {
527 this.insert_code_cell_below(i);
527 this.insert_code_cell_below(i);
528 var target_cell = this.cells()[i+1];
528 var target_cell = this.cells()[i+1];
529 target_cell.set_code(source_cell.get_source());
529 target_cell.set_code(source_cell.get_source());
530 source_element.remove();
530 source_element.remove();
531 target_cell.select();
531 target_cell.select();
532 };
532 };
533 this.dirty = true;
533 this.dirty = true;
534 };
534 };
535
535
536
536
537 Notebook.prototype.to_markdown = function (index) {
537 Notebook.prototype.to_markdown = function (index) {
538 // TODO: Bounds check for i
538 // TODO: Bounds check for i
539 var i = this.index_or_selected(index);
539 var i = this.index_or_selected(index);
540 var source_element = this.cell_elements().eq(i);
540 var source_element = this.cell_elements().eq(i);
541 var source_cell = source_element.data("cell");
541 var source_cell = source_element.data("cell");
542 var target_cell = null;
542 var target_cell = null;
543 if (source_cell instanceof IPython.CodeCell) {
543 if (source_cell instanceof IPython.CodeCell) {
544 this.insert_markdown_cell_below(i);
544 this.insert_markdown_cell_below(i);
545 var target_cell = this.cells()[i+1];
545 var target_cell = this.cells()[i+1];
546 var text = source_cell.get_code();
546 var text = source_cell.get_code();
547 } else if (source_cell instanceof IPython.HTMLCell) {
547 } else if (source_cell instanceof IPython.HTMLCell) {
548 this.insert_markdown_cell_below(i);
548 this.insert_markdown_cell_below(i);
549 var target_cell = this.cells()[i+1];
549 var target_cell = this.cells()[i+1];
550 var text = source_cell.get_source();
550 var text = source_cell.get_source();
551 if (text === source_cell.placeholder) {
551 if (text === source_cell.placeholder) {
552 text = target_cell.placeholder;
552 text = target_cell.placeholder;
553 }
553 }
554 }
554 }
555 if (target_cell !== null) {
555 if (target_cell !== null) {
556 if (text === "") {text = target_cell.placeholder;};
556 if (text === "") {text = target_cell.placeholder;};
557 target_cell.set_source(text);
557 target_cell.set_source(text);
558 source_element.remove();
558 source_element.remove();
559 target_cell.edit();
559 target_cell.edit();
560 }
560 }
561 this.dirty = true;
561 this.dirty = true;
562 };
562 };
563
563
564
564
565 Notebook.prototype.to_html = function (index) {
565 Notebook.prototype.to_html = function (index) {
566 // TODO: Bounds check for i
566 // TODO: Bounds check for i
567 var i = this.index_or_selected(index);
567 var i = this.index_or_selected(index);
568 var source_element = this.cell_elements().eq(i);
568 var source_element = this.cell_elements().eq(i);
569 var source_cell = source_element.data("cell");
569 var source_cell = source_element.data("cell");
570 var target_cell = null;
570 var target_cell = null;
571 if (source_cell instanceof IPython.CodeCell) {
571 if (source_cell instanceof IPython.CodeCell) {
572 this.insert_html_cell_below(i);
572 this.insert_html_cell_below(i);
573 var target_cell = this.cells()[i+1];
573 var target_cell = this.cells()[i+1];
574 var text = source_cell.get_code();
574 var text = source_cell.get_code();
575 } else if (source_cell instanceof IPython.MarkdownCell) {
575 } else if (source_cell instanceof IPython.MarkdownCell) {
576 this.insert_html_cell_below(i);
576 this.insert_html_cell_below(i);
577 var target_cell = this.cells()[i+1];
577 var target_cell = this.cells()[i+1];
578 var text = source_cell.get_source();
578 var text = source_cell.get_source();
579 if (text === source_cell.placeholder) {
579 if (text === source_cell.placeholder) {
580 text = target_cell.placeholder;
580 text = target_cell.placeholder;
581 }
581 }
582 }
582 }
583 if (target_cell !== null) {
583 if (target_cell !== null) {
584 if (text === "") {text = target_cell.placeholder;};
584 if (text === "") {text = target_cell.placeholder;};
585 target_cell.set_source(text);
585 target_cell.set_source(text);
586 source_element.remove();
586 source_element.remove();
587 target_cell.edit();
587 target_cell.edit();
588 }
588 }
589 this.dirty = true;
589 this.dirty = true;
590 };
590 };
591
591
592
592
593 // Cell collapsing and output clearing
593 // Cell collapsing and output clearing
594
594
595 Notebook.prototype.collapse = function (index) {
595 Notebook.prototype.collapse = function (index) {
596 var i = this.index_or_selected(index);
596 var i = this.index_or_selected(index);
597 this.cells()[i].collapse();
597 this.cells()[i].collapse();
598 this.dirty = true;
598 this.dirty = true;
599 };
599 };
600
600
601
601
602 Notebook.prototype.expand = function (index) {
602 Notebook.prototype.expand = function (index) {
603 var i = this.index_or_selected(index);
603 var i = this.index_or_selected(index);
604 this.cells()[i].expand();
604 this.cells()[i].expand();
605 this.dirty = true;
605 this.dirty = true;
606 };
606 };
607
607
608
608
609 Notebook.prototype.toggle_output = function (index) {
609 Notebook.prototype.toggle_output = function (index) {
610 var i = this.index_or_selected(index);
610 var i = this.index_or_selected(index);
611 this.cells()[i].toggle_output();
611 this.cells()[i].toggle_output();
612 this.dirty = true;
612 this.dirty = true;
613 };
613 };
614
614
615
615
616 Notebook.prototype.set_autoindent = function (state) {
616 Notebook.prototype.set_autoindent = function (state) {
617 var cells = this.cells();
617 var cells = this.cells();
618 len = cells.length;
618 len = cells.length;
619 for (var i=0; i<len; i++) {
619 for (var i=0; i<len; i++) {
620 cells[i].set_autoindent(state)
620 cells[i].set_autoindent(state)
621 };
621 };
622 };
622 };
623
623
624
624
625 Notebook.prototype.clear_all_output = function () {
625 Notebook.prototype.clear_all_output = function () {
626 var ncells = this.ncells();
626 var ncells = this.ncells();
627 var cells = this.cells();
627 var cells = this.cells();
628 for (var i=0; i<ncells; i++) {
628 for (var i=0; i<ncells; i++) {
629 if (cells[i] instanceof IPython.CodeCell) {
629 if (cells[i] instanceof IPython.CodeCell) {
630 cells[i].clear_output();
630 cells[i].clear_output(true,true,true);
631 }
631 }
632 };
632 };
633 this.dirty = true;
633 this.dirty = true;
634 };
634 };
635
635
636 // Other cell functions: line numbers, ...
636 // Other cell functions: line numbers, ...
637
637
638 Notebook.prototype.cell_toggle_line_numbers = function() {
638 Notebook.prototype.cell_toggle_line_numbers = function() {
639 this.selected_cell().toggle_line_numbers()
639 this.selected_cell().toggle_line_numbers()
640 };
640 };
641
641
642 // Kernel related things
642 // Kernel related things
643
643
644 Notebook.prototype.start_kernel = function () {
644 Notebook.prototype.start_kernel = function () {
645 this.kernel = new IPython.Kernel();
645 this.kernel = new IPython.Kernel();
646 var notebook_id = IPython.save_widget.get_notebook_id();
646 var notebook_id = IPython.save_widget.get_notebook_id();
647 this.kernel.start(notebook_id, $.proxy(this.kernel_started, this));
647 this.kernel.start(notebook_id, $.proxy(this.kernel_started, this));
648 };
648 };
649
649
650
650
651 Notebook.prototype.restart_kernel = function () {
651 Notebook.prototype.restart_kernel = function () {
652 var that = this;
652 var that = this;
653 var notebook_id = IPython.save_widget.get_notebook_id();
653 var notebook_id = IPython.save_widget.get_notebook_id();
654
654
655 var dialog = $('<div/>');
655 var dialog = $('<div/>');
656 dialog.html('Do you want to restart the current kernel? You will lose all variables defined in it.');
656 dialog.html('Do you want to restart the current kernel? You will lose all variables defined in it.');
657 $(document).append(dialog);
657 $(document).append(dialog);
658 dialog.dialog({
658 dialog.dialog({
659 resizable: false,
659 resizable: false,
660 modal: true,
660 modal: true,
661 title: "Restart kernel or continue running?",
661 title: "Restart kernel or continue running?",
662 buttons : {
662 buttons : {
663 "Restart": function () {
663 "Restart": function () {
664 that.kernel.restart($.proxy(that.kernel_started, that));
664 that.kernel.restart($.proxy(that.kernel_started, that));
665 $(this).dialog('close');
665 $(this).dialog('close');
666 },
666 },
667 "Continue running": function () {
667 "Continue running": function () {
668 $(this).dialog('close');
668 $(this).dialog('close');
669 }
669 }
670 }
670 }
671 });
671 });
672 };
672 };
673
673
674
674
675 Notebook.prototype.kernel_started = function () {
675 Notebook.prototype.kernel_started = function () {
676 console.log("Kernel started: ", this.kernel.kernel_id);
676 console.log("Kernel started: ", this.kernel.kernel_id);
677 this.kernel.shell_channel.onmessage = $.proxy(this.handle_shell_reply,this);
677 this.kernel.shell_channel.onmessage = $.proxy(this.handle_shell_reply,this);
678 this.kernel.iopub_channel.onmessage = $.proxy(this.handle_iopub_reply,this);
678 this.kernel.iopub_channel.onmessage = $.proxy(this.handle_iopub_reply,this);
679 };
679 };
680
680
681
681
682 Notebook.prototype.handle_shell_reply = function (e) {
682 Notebook.prototype.handle_shell_reply = function (e) {
683 reply = $.parseJSON(e.data);
683 reply = $.parseJSON(e.data);
684 var header = reply.header;
684 var header = reply.header;
685 var content = reply.content;
685 var content = reply.content;
686 var msg_type = header.msg_type;
686 var msg_type = header.msg_type;
687 // console.log(reply);
687 // console.log(reply);
688 var cell = this.cell_for_msg(reply.parent_header.msg_id);
688 var cell = this.cell_for_msg(reply.parent_header.msg_id);
689 if (msg_type === "execute_reply") {
689 if (msg_type === "execute_reply") {
690 cell.set_input_prompt(content.execution_count);
690 cell.set_input_prompt(content.execution_count);
691 this.dirty = true;
691 this.dirty = true;
692 } else if (msg_type === "complete_reply") {
692 } else if (msg_type === "complete_reply") {
693 cell.finish_completing(content.matched_text, content.matches);
693 cell.finish_completing(content.matched_text, content.matches);
694 };
694 };
695 var payload = content.payload || [];
695 var payload = content.payload || [];
696 this.handle_payload(cell, payload);
696 this.handle_payload(cell, payload);
697 };
697 };
698
698
699
699
700 Notebook.prototype.handle_payload = function (cell, payload) {
700 Notebook.prototype.handle_payload = function (cell, payload) {
701 var l = payload.length;
701 var l = payload.length;
702 for (var i=0; i<l; i++) {
702 for (var i=0; i<l; i++) {
703 if (payload[i].source === 'IPython.zmq.page.page') {
703 if (payload[i].source === 'IPython.zmq.page.page') {
704 if (payload[i].text.trim() !== '') {
704 if (payload[i].text.trim() !== '') {
705 IPython.pager.clear();
705 IPython.pager.clear();
706 IPython.pager.expand();
706 IPython.pager.expand();
707 IPython.pager.append_text(payload[i].text);
707 IPython.pager.append_text(payload[i].text);
708 }
708 }
709 } else if (payload[i].source === 'IPython.zmq.zmqshell.ZMQInteractiveShell.set_next_input') {
709 } else if (payload[i].source === 'IPython.zmq.zmqshell.ZMQInteractiveShell.set_next_input') {
710 var index = this.find_cell_index(cell);
710 var index = this.find_cell_index(cell);
711 var new_cell = this.insert_code_cell_below(index);
711 var new_cell = this.insert_code_cell_below(index);
712 new_cell.set_code(payload[i].text);
712 new_cell.set_code(payload[i].text);
713 this.dirty = true;
713 this.dirty = true;
714 }
714 }
715 };
715 };
716 };
716 };
717
717
718
718
719 Notebook.prototype.handle_iopub_reply = function (e) {
719 Notebook.prototype.handle_iopub_reply = function (e) {
720 reply = $.parseJSON(e.data);
720 reply = $.parseJSON(e.data);
721 var content = reply.content;
721 var content = reply.content;
722 // console.log(reply);
722 // console.log(reply);
723 var msg_type = reply.header.msg_type;
723 var msg_type = reply.header.msg_type;
724 var cell = this.cell_for_msg(reply.parent_header.msg_id);
724 var cell = this.cell_for_msg(reply.parent_header.msg_id);
725 var output_types = ['stream','display_data','pyout','pyerr'];
725 var output_types = ['stream','display_data','pyout','pyerr'];
726 if (output_types.indexOf(msg_type) >= 0) {
726 if (output_types.indexOf(msg_type) >= 0) {
727 this.handle_output(cell, msg_type, content);
727 this.handle_output(cell, msg_type, content);
728 } else if (msg_type === 'status') {
728 } else if (msg_type === 'status') {
729 if (content.execution_state === 'busy') {
729 if (content.execution_state === 'busy') {
730 IPython.kernel_status_widget.status_busy();
730 IPython.kernel_status_widget.status_busy();
731 } else if (content.execution_state === 'idle') {
731 } else if (content.execution_state === 'idle') {
732 IPython.kernel_status_widget.status_idle();
732 IPython.kernel_status_widget.status_idle();
733 } else if (content.execution_state === 'dead') {
733 } else if (content.execution_state === 'dead') {
734 this.handle_status_dead();
734 this.handle_status_dead();
735 };
735 };
736 }
736 } else if (msg_type === 'clear_output') {
737 cell.clear_output(content.stdout, content.stderr, content.other);
738 };
737 };
739 };
738
740
739
741
740 Notebook.prototype.handle_status_dead = function () {
742 Notebook.prototype.handle_status_dead = function () {
741 var that = this;
743 var that = this;
742 this.kernel.stop_channels();
744 this.kernel.stop_channels();
743 var dialog = $('<div/>');
745 var dialog = $('<div/>');
744 dialog.html('The kernel has died, would you like to restart it? If you do not restart the kernel, you will be able to save the notebook, but running code will not work until the notebook is reopened.');
746 dialog.html('The kernel has died, would you like to restart it? If you do not restart the kernel, you will be able to save the notebook, but running code will not work until the notebook is reopened.');
745 $(document).append(dialog);
747 $(document).append(dialog);
746 dialog.dialog({
748 dialog.dialog({
747 resizable: false,
749 resizable: false,
748 modal: true,
750 modal: true,
749 title: "Dead kernel",
751 title: "Dead kernel",
750 buttons : {
752 buttons : {
751 "Restart": function () {
753 "Restart": function () {
752 that.start_kernel();
754 that.start_kernel();
753 $(this).dialog('close');
755 $(this).dialog('close');
754 },
756 },
755 "Continue running": function () {
757 "Continue running": function () {
756 $(this).dialog('close');
758 $(this).dialog('close');
757 }
759 }
758 }
760 }
759 });
761 });
760 };
762 };
761
763
762
764
763 Notebook.prototype.handle_output = function (cell, msg_type, content) {
765 Notebook.prototype.handle_output = function (cell, msg_type, content) {
764 var json = {};
766 var json = {};
765 json.output_type = msg_type;
767 json.output_type = msg_type;
766 if (msg_type === "stream") {
768 if (msg_type === "stream") {
767 json.text = utils.fixConsole(content.data);
769 json.text = utils.fixConsole(content.data);
768 json.stream = content.name;
770 json.stream = content.name;
769 } else if (msg_type === "display_data") {
771 } else if (msg_type === "display_data") {
770 json = this.convert_mime_types(json, content.data);
772 json = this.convert_mime_types(json, content.data);
771 } else if (msg_type === "pyout") {
773 } else if (msg_type === "pyout") {
772 json.prompt_number = content.execution_count;
774 json.prompt_number = content.execution_count;
773 json = this.convert_mime_types(json, content.data);
775 json = this.convert_mime_types(json, content.data);
774 } else if (msg_type === "pyerr") {
776 } else if (msg_type === "pyerr") {
775 json.ename = content.ename;
777 json.ename = content.ename;
776 json.evalue = content.evalue;
778 json.evalue = content.evalue;
777 var traceback = [];
779 var traceback = [];
778 for (var i=0; i<content.traceback.length; i++) {
780 for (var i=0; i<content.traceback.length; i++) {
779 traceback.push(utils.fixConsole(content.traceback[i]));
781 traceback.push(utils.fixConsole(content.traceback[i]));
780 }
782 }
781 json.traceback = traceback;
783 json.traceback = traceback;
782 };
784 };
783 cell.append_output(json);
785 cell.append_output(json);
784 this.dirty = true;
786 this.dirty = true;
785 };
787 };
786
788
787
789
788 Notebook.prototype.convert_mime_types = function (json, data) {
790 Notebook.prototype.convert_mime_types = function (json, data) {
789 if (data['text/plain'] !== undefined) {
791 if (data['text/plain'] !== undefined) {
790 json.text = utils.fixConsole(data['text/plain']);
792 json.text = utils.fixConsole(data['text/plain']);
791 };
793 };
792 if (data['text/html'] !== undefined) {
794 if (data['text/html'] !== undefined) {
793 json.html = data['text/html'];
795 json.html = data['text/html'];
794 };
796 };
795 if (data['image/svg+xml'] !== undefined) {
797 if (data['image/svg+xml'] !== undefined) {
796 json.svg = data['image/svg+xml'];
798 json.svg = data['image/svg+xml'];
797 };
799 };
798 if (data['image/png'] !== undefined) {
800 if (data['image/png'] !== undefined) {
799 json.png = data['image/png'];
801 json.png = data['image/png'];
800 };
802 };
801 if (data['image/jpeg'] !== undefined) {
803 if (data['image/jpeg'] !== undefined) {
802 json.jpeg = data['image/jpeg'];
804 json.jpeg = data['image/jpeg'];
803 };
805 };
804 if (data['text/latex'] !== undefined) {
806 if (data['text/latex'] !== undefined) {
805 json.latex = data['text/latex'];
807 json.latex = data['text/latex'];
806 };
808 };
807 if (data['application/json'] !== undefined) {
809 if (data['application/json'] !== undefined) {
808 json.json = data['application/json'];
810 json.json = data['application/json'];
809 };
811 };
810 if (data['application/javascript'] !== undefined) {
812 if (data['application/javascript'] !== undefined) {
811 json.javascript = data['application/javascript'];
813 json.javascript = data['application/javascript'];
812 }
814 }
813 return json;
815 return json;
814 };
816 };
815
817
816
818
817 Notebook.prototype.execute_selected_cell = function (options) {
819 Notebook.prototype.execute_selected_cell = function (options) {
818 // add_new: should a new cell be added if we are at the end of the nb
820 // add_new: should a new cell be added if we are at the end of the nb
819 // terminal: execute in terminal mode, which stays in the current cell
821 // terminal: execute in terminal mode, which stays in the current cell
820 default_options = {terminal: false, add_new: true}
822 default_options = {terminal: false, add_new: true}
821 $.extend(default_options, options)
823 $.extend(default_options, options)
822 var that = this;
824 var that = this;
823 var cell = that.selected_cell();
825 var cell = that.selected_cell();
824 var cell_index = that.find_cell_index(cell);
826 var cell_index = that.find_cell_index(cell);
825 if (cell instanceof IPython.CodeCell) {
827 if (cell instanceof IPython.CodeCell) {
826 cell.clear_output();
828 cell.clear_output(true, true, true);
827 var code = cell.get_code();
829 var code = cell.get_code();
828 var msg_id = that.kernel.execute(cell.get_code());
830 var msg_id = that.kernel.execute(cell.get_code());
829 that.msg_cell_map[msg_id] = cell.cell_id;
831 that.msg_cell_map[msg_id] = cell.cell_id;
830 } else if (cell instanceof IPython.HTMLCell) {
832 } else if (cell instanceof IPython.HTMLCell) {
831 cell.render();
833 cell.render();
832 }
834 }
833 if (default_options.terminal) {
835 if (default_options.terminal) {
834 cell.select_all();
836 cell.select_all();
835 } else {
837 } else {
836 if ((cell_index === (that.ncells()-1)) && default_options.add_new) {
838 if ((cell_index === (that.ncells()-1)) && default_options.add_new) {
837 that.insert_code_cell_below();
839 that.insert_code_cell_below();
838 // If we are adding a new cell at the end, scroll down to show it.
840 // If we are adding a new cell at the end, scroll down to show it.
839 that.scroll_to_bottom();
841 that.scroll_to_bottom();
840 } else {
842 } else {
841 that.select(cell_index+1);
843 that.select(cell_index+1);
842 };
844 };
843 };
845 };
844 this.dirty = true;
846 this.dirty = true;
845 };
847 };
846
848
847
849
848 Notebook.prototype.execute_all_cells = function () {
850 Notebook.prototype.execute_all_cells = function () {
849 var ncells = this.ncells();
851 var ncells = this.ncells();
850 for (var i=0; i<ncells; i++) {
852 for (var i=0; i<ncells; i++) {
851 this.select(i);
853 this.select(i);
852 this.execute_selected_cell({add_new:false});
854 this.execute_selected_cell({add_new:false});
853 };
855 };
854 this.scroll_to_bottom();
856 this.scroll_to_bottom();
855 };
857 };
856
858
857
859
858 Notebook.prototype.complete_cell = function (cell, line, cursor_pos) {
860 Notebook.prototype.complete_cell = function (cell, line, cursor_pos) {
859 var msg_id = this.kernel.complete(line, cursor_pos);
861 var msg_id = this.kernel.complete(line, cursor_pos);
860 this.msg_cell_map[msg_id] = cell.cell_id;
862 this.msg_cell_map[msg_id] = cell.cell_id;
861 };
863 };
862
864
863 // Persistance and loading
865 // Persistance and loading
864
866
865
867
866 Notebook.prototype.fromJSON = function (data) {
868 Notebook.prototype.fromJSON = function (data) {
867 var ncells = this.ncells();
869 var ncells = this.ncells();
868 for (var i=0; i<ncells; i++) {
870 for (var i=0; i<ncells; i++) {
869 // Always delete cell 0 as they get renumbered as they are deleted.
871 // Always delete cell 0 as they get renumbered as they are deleted.
870 this.delete_cell(0);
872 this.delete_cell(0);
871 };
873 };
872 // Save the metadata
874 // Save the metadata
873 this.metadata = data.metadata;
875 this.metadata = data.metadata;
874 // Only handle 1 worksheet for now.
876 // Only handle 1 worksheet for now.
875 var worksheet = data.worksheets[0];
877 var worksheet = data.worksheets[0];
876 if (worksheet !== undefined) {
878 if (worksheet !== undefined) {
877 var new_cells = worksheet.cells;
879 var new_cells = worksheet.cells;
878 ncells = new_cells.length;
880 ncells = new_cells.length;
879 var cell_data = null;
881 var cell_data = null;
880 var new_cell = null;
882 var new_cell = null;
881 for (var i=0; i<ncells; i++) {
883 for (var i=0; i<ncells; i++) {
882 cell_data = new_cells[i];
884 cell_data = new_cells[i];
883 if (cell_data.cell_type == 'code') {
885 if (cell_data.cell_type == 'code') {
884 new_cell = this.insert_code_cell_below();
886 new_cell = this.insert_code_cell_below();
885 new_cell.fromJSON(cell_data);
887 new_cell.fromJSON(cell_data);
886 } else if (cell_data.cell_type === 'html') {
888 } else if (cell_data.cell_type === 'html') {
887 new_cell = this.insert_html_cell_below();
889 new_cell = this.insert_html_cell_below();
888 new_cell.fromJSON(cell_data);
890 new_cell.fromJSON(cell_data);
889 } else if (cell_data.cell_type === 'markdown') {
891 } else if (cell_data.cell_type === 'markdown') {
890 new_cell = this.insert_markdown_cell_below();
892 new_cell = this.insert_markdown_cell_below();
891 new_cell.fromJSON(cell_data);
893 new_cell.fromJSON(cell_data);
892 };
894 };
893 };
895 };
894 };
896 };
895 };
897 };
896
898
897
899
898 Notebook.prototype.toJSON = function () {
900 Notebook.prototype.toJSON = function () {
899 var cells = this.cells();
901 var cells = this.cells();
900 var ncells = cells.length;
902 var ncells = cells.length;
901 cell_array = new Array(ncells);
903 cell_array = new Array(ncells);
902 for (var i=0; i<ncells; i++) {
904 for (var i=0; i<ncells; i++) {
903 cell_array[i] = cells[i].toJSON();
905 cell_array[i] = cells[i].toJSON();
904 };
906 };
905 data = {
907 data = {
906 // Only handle 1 worksheet for now.
908 // Only handle 1 worksheet for now.
907 worksheets : [{cells:cell_array}],
909 worksheets : [{cells:cell_array}],
908 metadata : this.metadata
910 metadata : this.metadata
909 }
911 }
910 return data
912 return data
911 };
913 };
912
914
913 Notebook.prototype.save_notebook = function () {
915 Notebook.prototype.save_notebook = function () {
914 if (IPython.save_widget.test_notebook_name()) {
916 if (IPython.save_widget.test_notebook_name()) {
915 var notebook_id = IPython.save_widget.get_notebook_id();
917 var notebook_id = IPython.save_widget.get_notebook_id();
916 var nbname = IPython.save_widget.get_notebook_name();
918 var nbname = IPython.save_widget.get_notebook_name();
917 // We may want to move the name/id/nbformat logic inside toJSON?
919 // We may want to move the name/id/nbformat logic inside toJSON?
918 var data = this.toJSON();
920 var data = this.toJSON();
919 data.metadata.name = nbname;
921 data.metadata.name = nbname;
920 data.nbformat = 2;
922 data.nbformat = 2;
921 // We do the call with settings so we can set cache to false.
923 // We do the call with settings so we can set cache to false.
922 var settings = {
924 var settings = {
923 processData : false,
925 processData : false,
924 cache : false,
926 cache : false,
925 type : "PUT",
927 type : "PUT",
926 data : JSON.stringify(data),
928 data : JSON.stringify(data),
927 headers : {'Content-Type': 'application/json'},
929 headers : {'Content-Type': 'application/json'},
928 success : $.proxy(this.notebook_saved,this),
930 success : $.proxy(this.notebook_saved,this),
929 error : $.proxy(this.notebook_save_failed,this)
931 error : $.proxy(this.notebook_save_failed,this)
930 };
932 };
931 IPython.save_widget.status_saving();
933 IPython.save_widget.status_saving();
932 $.ajax("/notebooks/" + notebook_id, settings);
934 $.ajax("/notebooks/" + notebook_id, settings);
933 };
935 };
934 };
936 };
935
937
936
938
937 Notebook.prototype.notebook_saved = function (data, status, xhr) {
939 Notebook.prototype.notebook_saved = function (data, status, xhr) {
938 this.dirty = false;
940 this.dirty = false;
939 IPython.save_widget.notebook_saved();
941 IPython.save_widget.notebook_saved();
940 setTimeout($.proxy(IPython.save_widget.status_save,IPython.save_widget),500);
942 setTimeout($.proxy(IPython.save_widget.status_save,IPython.save_widget),500);
941 }
943 }
942
944
943
945
944 Notebook.prototype.notebook_save_failed = function (xhr, status, error_msg) {
946 Notebook.prototype.notebook_save_failed = function (xhr, status, error_msg) {
945 // Notify the user and reset the save button
947 // Notify the user and reset the save button
946 // TODO: Handle different types of errors (timeout etc.)
948 // TODO: Handle different types of errors (timeout etc.)
947 alert('An unexpected error occured while saving the notebook.');
949 alert('An unexpected error occured while saving the notebook.');
948 setTimeout($.proxy(IPython.save_widget.reset_status,IPython.save_widget),500);
950 setTimeout($.proxy(IPython.save_widget.reset_status,IPython.save_widget),500);
949 }
951 }
950
952
951
953
952 Notebook.prototype.load_notebook = function (callback) {
954 Notebook.prototype.load_notebook = function (callback) {
953 var that = this;
955 var that = this;
954 var notebook_id = IPython.save_widget.get_notebook_id();
956 var notebook_id = IPython.save_widget.get_notebook_id();
955 // We do the call with settings so we can set cache to false.
957 // We do the call with settings so we can set cache to false.
956 var settings = {
958 var settings = {
957 processData : false,
959 processData : false,
958 cache : false,
960 cache : false,
959 type : "GET",
961 type : "GET",
960 dataType : "json",
962 dataType : "json",
961 success : function (data, status, xhr) {
963 success : function (data, status, xhr) {
962 that.notebook_loaded(data, status, xhr);
964 that.notebook_loaded(data, status, xhr);
963 if (callback !== undefined) {
965 if (callback !== undefined) {
964 callback();
966 callback();
965 };
967 };
966 }
968 }
967 };
969 };
968 IPython.save_widget.status_loading();
970 IPython.save_widget.status_loading();
969 $.ajax("/notebooks/" + notebook_id, settings);
971 $.ajax("/notebooks/" + notebook_id, settings);
970 }
972 }
971
973
972
974
973 Notebook.prototype.notebook_loaded = function (data, status, xhr) {
975 Notebook.prototype.notebook_loaded = function (data, status, xhr) {
974 this.fromJSON(data);
976 this.fromJSON(data);
975 if (this.ncells() === 0) {
977 if (this.ncells() === 0) {
976 this.insert_code_cell_below();
978 this.insert_code_cell_below();
977 };
979 };
978 IPython.save_widget.status_save();
980 IPython.save_widget.status_save();
979 IPython.save_widget.set_notebook_name(data.metadata.name);
981 IPython.save_widget.set_notebook_name(data.metadata.name);
980 this.start_kernel();
982 this.start_kernel();
981 this.dirty = false;
983 this.dirty = false;
982 // fromJSON always selects the last cell inserted. We need to wait
984 // fromJSON always selects the last cell inserted. We need to wait
983 // until that is done before scrolling to the top.
985 // until that is done before scrolling to the top.
984 setTimeout(function () {
986 setTimeout(function () {
985 IPython.notebook.select(0);
987 IPython.notebook.select(0);
986 IPython.notebook.scroll_to_top();
988 IPython.notebook.scroll_to_top();
987 }, 50);
989 }, 50);
988 };
990 };
989
991
990 IPython.Notebook = Notebook;
992 IPython.Notebook = Notebook;
991
993
992 return IPython;
994 return IPython;
993
995
994 }(IPython));
996 }(IPython));
995
997
@@ -1,489 +1,495 b''
1 """A ZMQ-based subclass of InteractiveShell.
1 """A ZMQ-based subclass of InteractiveShell.
2
2
3 This code is meant to ease the refactoring of the base InteractiveShell into
3 This code is meant to ease the refactoring of the base InteractiveShell into
4 something with a cleaner architecture for 2-process use, without actually
4 something with a cleaner architecture for 2-process use, without actually
5 breaking InteractiveShell itself. So we're doing something a bit ugly, where
5 breaking InteractiveShell itself. So we're doing something a bit ugly, where
6 we subclass and override what we want to fix. Once this is working well, we
6 we subclass and override what we want to fix. Once this is working well, we
7 can go back to the base class and refactor the code for a cleaner inheritance
7 can go back to the base class and refactor the code for a cleaner inheritance
8 implementation that doesn't rely on so much monkeypatching.
8 implementation that doesn't rely on so much monkeypatching.
9
9
10 But this lets us maintain a fully working IPython as we develop the new
10 But this lets us maintain a fully working IPython as we develop the new
11 machinery. This should thus be thought of as scaffolding.
11 machinery. This should thus be thought of as scaffolding.
12 """
12 """
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14 # Imports
14 # Imports
15 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
16 from __future__ import print_function
16 from __future__ import print_function
17
17
18 # Stdlib
18 # Stdlib
19 import inspect
19 import inspect
20 import os
20 import os
21 import sys
21 import sys
22 from subprocess import Popen, PIPE
22 from subprocess import Popen, PIPE
23
23
24 # Our own
24 # Our own
25 from IPython.core.interactiveshell import (
25 from IPython.core.interactiveshell import (
26 InteractiveShell, InteractiveShellABC
26 InteractiveShell, InteractiveShellABC
27 )
27 )
28 from IPython.core import page
28 from IPython.core import page
29 from IPython.core.autocall import ZMQExitAutocall
29 from IPython.core.autocall import ZMQExitAutocall
30 from IPython.core.displaypub import DisplayPublisher
30 from IPython.core.displaypub import DisplayPublisher
31 from IPython.core.macro import Macro
31 from IPython.core.macro import Macro
32 from IPython.core.magic import MacroToEdit
32 from IPython.core.magic import MacroToEdit
33 from IPython.core.payloadpage import install_payload_page
33 from IPython.core.payloadpage import install_payload_page
34 from IPython.lib.kernel import (
34 from IPython.lib.kernel import (
35 get_connection_file, get_connection_info, connect_qtconsole
35 get_connection_file, get_connection_info, connect_qtconsole
36 )
36 )
37 from IPython.utils import io
37 from IPython.utils import io
38 from IPython.utils.jsonutil import json_clean
38 from IPython.utils.jsonutil import json_clean
39 from IPython.utils.path import get_py_filename
39 from IPython.utils.path import get_py_filename
40 from IPython.utils.process import arg_split
40 from IPython.utils.process import arg_split
41 from IPython.utils.traitlets import Instance, Type, Dict, CBool
41 from IPython.utils.traitlets import Instance, Type, Dict, CBool
42 from IPython.utils.warn import warn, error
42 from IPython.utils.warn import warn, error
43 from IPython.zmq.displayhook import ZMQShellDisplayHook, _encode_binary
43 from IPython.zmq.displayhook import ZMQShellDisplayHook, _encode_binary
44 from IPython.zmq.session import extract_header
44 from IPython.zmq.session import extract_header
45 from session import Session
45 from session import Session
46
46
47 #-----------------------------------------------------------------------------
47 #-----------------------------------------------------------------------------
48 # Globals and side-effects
48 # Globals and side-effects
49 #-----------------------------------------------------------------------------
49 #-----------------------------------------------------------------------------
50
50
51 # Install the payload version of page.
51 # Install the payload version of page.
52 install_payload_page()
52 install_payload_page()
53
53
54 #-----------------------------------------------------------------------------
54 #-----------------------------------------------------------------------------
55 # Functions and classes
55 # Functions and classes
56 #-----------------------------------------------------------------------------
56 #-----------------------------------------------------------------------------
57
57
58 class ZMQDisplayPublisher(DisplayPublisher):
58 class ZMQDisplayPublisher(DisplayPublisher):
59 """A display publisher that publishes data using a ZeroMQ PUB socket."""
59 """A display publisher that publishes data using a ZeroMQ PUB socket."""
60
60
61 session = Instance(Session)
61 session = Instance(Session)
62 pub_socket = Instance('zmq.Socket')
62 pub_socket = Instance('zmq.Socket')
63 parent_header = Dict({})
63 parent_header = Dict({})
64
64
65 def set_parent(self, parent):
65 def set_parent(self, parent):
66 """Set the parent for outbound messages."""
66 """Set the parent for outbound messages."""
67 self.parent_header = extract_header(parent)
67 self.parent_header = extract_header(parent)
68
68
69 def publish(self, source, data, metadata=None):
69 def publish(self, source, data, metadata=None):
70 if metadata is None:
70 if metadata is None:
71 metadata = {}
71 metadata = {}
72 self._validate_data(source, data, metadata)
72 self._validate_data(source, data, metadata)
73 content = {}
73 content = {}
74 content['source'] = source
74 content['source'] = source
75 _encode_binary(data)
75 _encode_binary(data)
76 content['data'] = data
76 content['data'] = data
77 content['metadata'] = metadata
77 content['metadata'] = metadata
78 self.session.send(
78 self.session.send(
79 self.pub_socket, u'display_data', json_clean(content),
79 self.pub_socket, u'display_data', json_clean(content),
80 parent=self.parent_header
80 parent=self.parent_header
81 )
81 )
82
82
83 def clear_output(self, stdout=True, stderr=True, other=True):
84 content = dict(stdout=stdout, stderr=stderr, other=other)
85 self.session.send(
86 self.pub_socket, u'clear_output', content,
87 parent=self.parent_header
88 )
83
89
84 class ZMQInteractiveShell(InteractiveShell):
90 class ZMQInteractiveShell(InteractiveShell):
85 """A subclass of InteractiveShell for ZMQ."""
91 """A subclass of InteractiveShell for ZMQ."""
86
92
87 displayhook_class = Type(ZMQShellDisplayHook)
93 displayhook_class = Type(ZMQShellDisplayHook)
88 display_pub_class = Type(ZMQDisplayPublisher)
94 display_pub_class = Type(ZMQDisplayPublisher)
89
95
90 # Override the traitlet in the parent class, because there's no point using
96 # Override the traitlet in the parent class, because there's no point using
91 # readline for the kernel. Can be removed when the readline code is moved
97 # readline for the kernel. Can be removed when the readline code is moved
92 # to the terminal frontend.
98 # to the terminal frontend.
93 colors_force = CBool(True)
99 colors_force = CBool(True)
94 readline_use = CBool(False)
100 readline_use = CBool(False)
95 # autoindent has no meaning in a zmqshell, and attempting to enable it
101 # autoindent has no meaning in a zmqshell, and attempting to enable it
96 # will print a warning in the absence of readline.
102 # will print a warning in the absence of readline.
97 autoindent = CBool(False)
103 autoindent = CBool(False)
98
104
99 exiter = Instance(ZMQExitAutocall)
105 exiter = Instance(ZMQExitAutocall)
100 def _exiter_default(self):
106 def _exiter_default(self):
101 return ZMQExitAutocall(self)
107 return ZMQExitAutocall(self)
102
108
103 keepkernel_on_exit = None
109 keepkernel_on_exit = None
104
110
105 def init_environment(self):
111 def init_environment(self):
106 """Configure the user's environment.
112 """Configure the user's environment.
107
113
108 """
114 """
109 env = os.environ
115 env = os.environ
110 # These two ensure 'ls' produces nice coloring on BSD-derived systems
116 # These two ensure 'ls' produces nice coloring on BSD-derived systems
111 env['TERM'] = 'xterm-color'
117 env['TERM'] = 'xterm-color'
112 env['CLICOLOR'] = '1'
118 env['CLICOLOR'] = '1'
113 # Since normal pagers don't work at all (over pexpect we don't have
119 # Since normal pagers don't work at all (over pexpect we don't have
114 # single-key control of the subprocess), try to disable paging in
120 # single-key control of the subprocess), try to disable paging in
115 # subprocesses as much as possible.
121 # subprocesses as much as possible.
116 env['PAGER'] = 'cat'
122 env['PAGER'] = 'cat'
117 env['GIT_PAGER'] = 'cat'
123 env['GIT_PAGER'] = 'cat'
118
124
119 def auto_rewrite_input(self, cmd):
125 def auto_rewrite_input(self, cmd):
120 """Called to show the auto-rewritten input for autocall and friends.
126 """Called to show the auto-rewritten input for autocall and friends.
121
127
122 FIXME: this payload is currently not correctly processed by the
128 FIXME: this payload is currently not correctly processed by the
123 frontend.
129 frontend.
124 """
130 """
125 new = self.displayhook.prompt1.auto_rewrite() + cmd
131 new = self.displayhook.prompt1.auto_rewrite() + cmd
126 payload = dict(
132 payload = dict(
127 source='IPython.zmq.zmqshell.ZMQInteractiveShell.auto_rewrite_input',
133 source='IPython.zmq.zmqshell.ZMQInteractiveShell.auto_rewrite_input',
128 transformed_input=new,
134 transformed_input=new,
129 )
135 )
130 self.payload_manager.write_payload(payload)
136 self.payload_manager.write_payload(payload)
131
137
132 def ask_exit(self):
138 def ask_exit(self):
133 """Engage the exit actions."""
139 """Engage the exit actions."""
134 payload = dict(
140 payload = dict(
135 source='IPython.zmq.zmqshell.ZMQInteractiveShell.ask_exit',
141 source='IPython.zmq.zmqshell.ZMQInteractiveShell.ask_exit',
136 exit=True,
142 exit=True,
137 keepkernel=self.keepkernel_on_exit,
143 keepkernel=self.keepkernel_on_exit,
138 )
144 )
139 self.payload_manager.write_payload(payload)
145 self.payload_manager.write_payload(payload)
140
146
141 def _showtraceback(self, etype, evalue, stb):
147 def _showtraceback(self, etype, evalue, stb):
142
148
143 exc_content = {
149 exc_content = {
144 u'traceback' : stb,
150 u'traceback' : stb,
145 u'ename' : unicode(etype.__name__),
151 u'ename' : unicode(etype.__name__),
146 u'evalue' : unicode(evalue)
152 u'evalue' : unicode(evalue)
147 }
153 }
148
154
149 dh = self.displayhook
155 dh = self.displayhook
150 # Send exception info over pub socket for other clients than the caller
156 # Send exception info over pub socket for other clients than the caller
151 # to pick up
157 # to pick up
152 exc_msg = dh.session.send(dh.pub_socket, u'pyerr', json_clean(exc_content), dh.parent_header)
158 exc_msg = dh.session.send(dh.pub_socket, u'pyerr', json_clean(exc_content), dh.parent_header)
153
159
154 # FIXME - Hack: store exception info in shell object. Right now, the
160 # FIXME - Hack: store exception info in shell object. Right now, the
155 # caller is reading this info after the fact, we need to fix this logic
161 # caller is reading this info after the fact, we need to fix this logic
156 # to remove this hack. Even uglier, we need to store the error status
162 # to remove this hack. Even uglier, we need to store the error status
157 # here, because in the main loop, the logic that sets it is being
163 # here, because in the main loop, the logic that sets it is being
158 # skipped because runlines swallows the exceptions.
164 # skipped because runlines swallows the exceptions.
159 exc_content[u'status'] = u'error'
165 exc_content[u'status'] = u'error'
160 self._reply_content = exc_content
166 self._reply_content = exc_content
161 # /FIXME
167 # /FIXME
162
168
163 return exc_content
169 return exc_content
164
170
165 #------------------------------------------------------------------------
171 #------------------------------------------------------------------------
166 # Magic overrides
172 # Magic overrides
167 #------------------------------------------------------------------------
173 #------------------------------------------------------------------------
168 # Once the base class stops inheriting from magic, this code needs to be
174 # Once the base class stops inheriting from magic, this code needs to be
169 # moved into a separate machinery as well. For now, at least isolate here
175 # moved into a separate machinery as well. For now, at least isolate here
170 # the magics which this class needs to implement differently from the base
176 # the magics which this class needs to implement differently from the base
171 # class, or that are unique to it.
177 # class, or that are unique to it.
172
178
173 def magic_doctest_mode(self,parameter_s=''):
179 def magic_doctest_mode(self,parameter_s=''):
174 """Toggle doctest mode on and off.
180 """Toggle doctest mode on and off.
175
181
176 This mode is intended to make IPython behave as much as possible like a
182 This mode is intended to make IPython behave as much as possible like a
177 plain Python shell, from the perspective of how its prompts, exceptions
183 plain Python shell, from the perspective of how its prompts, exceptions
178 and output look. This makes it easy to copy and paste parts of a
184 and output look. This makes it easy to copy and paste parts of a
179 session into doctests. It does so by:
185 session into doctests. It does so by:
180
186
181 - Changing the prompts to the classic ``>>>`` ones.
187 - Changing the prompts to the classic ``>>>`` ones.
182 - Changing the exception reporting mode to 'Plain'.
188 - Changing the exception reporting mode to 'Plain'.
183 - Disabling pretty-printing of output.
189 - Disabling pretty-printing of output.
184
190
185 Note that IPython also supports the pasting of code snippets that have
191 Note that IPython also supports the pasting of code snippets that have
186 leading '>>>' and '...' prompts in them. This means that you can paste
192 leading '>>>' and '...' prompts in them. This means that you can paste
187 doctests from files or docstrings (even if they have leading
193 doctests from files or docstrings (even if they have leading
188 whitespace), and the code will execute correctly. You can then use
194 whitespace), and the code will execute correctly. You can then use
189 '%history -t' to see the translated history; this will give you the
195 '%history -t' to see the translated history; this will give you the
190 input after removal of all the leading prompts and whitespace, which
196 input after removal of all the leading prompts and whitespace, which
191 can be pasted back into an editor.
197 can be pasted back into an editor.
192
198
193 With these features, you can switch into this mode easily whenever you
199 With these features, you can switch into this mode easily whenever you
194 need to do testing and changes to doctests, without having to leave
200 need to do testing and changes to doctests, without having to leave
195 your existing IPython session.
201 your existing IPython session.
196 """
202 """
197
203
198 from IPython.utils.ipstruct import Struct
204 from IPython.utils.ipstruct import Struct
199
205
200 # Shorthands
206 # Shorthands
201 shell = self.shell
207 shell = self.shell
202 disp_formatter = self.shell.display_formatter
208 disp_formatter = self.shell.display_formatter
203 ptformatter = disp_formatter.formatters['text/plain']
209 ptformatter = disp_formatter.formatters['text/plain']
204 # dstore is a data store kept in the instance metadata bag to track any
210 # dstore is a data store kept in the instance metadata bag to track any
205 # changes we make, so we can undo them later.
211 # changes we make, so we can undo them later.
206 dstore = shell.meta.setdefault('doctest_mode', Struct())
212 dstore = shell.meta.setdefault('doctest_mode', Struct())
207 save_dstore = dstore.setdefault
213 save_dstore = dstore.setdefault
208
214
209 # save a few values we'll need to recover later
215 # save a few values we'll need to recover later
210 mode = save_dstore('mode', False)
216 mode = save_dstore('mode', False)
211 save_dstore('rc_pprint', ptformatter.pprint)
217 save_dstore('rc_pprint', ptformatter.pprint)
212 save_dstore('rc_plain_text_only',disp_formatter.plain_text_only)
218 save_dstore('rc_plain_text_only',disp_formatter.plain_text_only)
213 save_dstore('xmode', shell.InteractiveTB.mode)
219 save_dstore('xmode', shell.InteractiveTB.mode)
214
220
215 if mode == False:
221 if mode == False:
216 # turn on
222 # turn on
217 ptformatter.pprint = False
223 ptformatter.pprint = False
218 disp_formatter.plain_text_only = True
224 disp_formatter.plain_text_only = True
219 shell.magic_xmode('Plain')
225 shell.magic_xmode('Plain')
220 else:
226 else:
221 # turn off
227 # turn off
222 ptformatter.pprint = dstore.rc_pprint
228 ptformatter.pprint = dstore.rc_pprint
223 disp_formatter.plain_text_only = dstore.rc_plain_text_only
229 disp_formatter.plain_text_only = dstore.rc_plain_text_only
224 shell.magic_xmode(dstore.xmode)
230 shell.magic_xmode(dstore.xmode)
225
231
226 # Store new mode and inform on console
232 # Store new mode and inform on console
227 dstore.mode = bool(1-int(mode))
233 dstore.mode = bool(1-int(mode))
228 mode_label = ['OFF','ON'][dstore.mode]
234 mode_label = ['OFF','ON'][dstore.mode]
229 print('Doctest mode is:', mode_label)
235 print('Doctest mode is:', mode_label)
230
236
231 # Send the payload back so that clients can modify their prompt display
237 # Send the payload back so that clients can modify their prompt display
232 payload = dict(
238 payload = dict(
233 source='IPython.zmq.zmqshell.ZMQInteractiveShell.magic_doctest_mode',
239 source='IPython.zmq.zmqshell.ZMQInteractiveShell.magic_doctest_mode',
234 mode=dstore.mode)
240 mode=dstore.mode)
235 self.payload_manager.write_payload(payload)
241 self.payload_manager.write_payload(payload)
236
242
237 def magic_edit(self,parameter_s='',last_call=['','']):
243 def magic_edit(self,parameter_s='',last_call=['','']):
238 """Bring up an editor and execute the resulting code.
244 """Bring up an editor and execute the resulting code.
239
245
240 Usage:
246 Usage:
241 %edit [options] [args]
247 %edit [options] [args]
242
248
243 %edit runs an external text editor. You will need to set the command for
249 %edit runs an external text editor. You will need to set the command for
244 this editor via the ``TerminalInteractiveShell.editor`` option in your
250 this editor via the ``TerminalInteractiveShell.editor`` option in your
245 configuration file before it will work.
251 configuration file before it will work.
246
252
247 This command allows you to conveniently edit multi-line code right in
253 This command allows you to conveniently edit multi-line code right in
248 your IPython session.
254 your IPython session.
249
255
250 If called without arguments, %edit opens up an empty editor with a
256 If called without arguments, %edit opens up an empty editor with a
251 temporary file and will execute the contents of this file when you
257 temporary file and will execute the contents of this file when you
252 close it (don't forget to save it!).
258 close it (don't forget to save it!).
253
259
254
260
255 Options:
261 Options:
256
262
257 -n <number>: open the editor at a specified line number. By default,
263 -n <number>: open the editor at a specified line number. By default,
258 the IPython editor hook uses the unix syntax 'editor +N filename', but
264 the IPython editor hook uses the unix syntax 'editor +N filename', but
259 you can configure this by providing your own modified hook if your
265 you can configure this by providing your own modified hook if your
260 favorite editor supports line-number specifications with a different
266 favorite editor supports line-number specifications with a different
261 syntax.
267 syntax.
262
268
263 -p: this will call the editor with the same data as the previous time
269 -p: this will call the editor with the same data as the previous time
264 it was used, regardless of how long ago (in your current session) it
270 it was used, regardless of how long ago (in your current session) it
265 was.
271 was.
266
272
267 -r: use 'raw' input. This option only applies to input taken from the
273 -r: use 'raw' input. This option only applies to input taken from the
268 user's history. By default, the 'processed' history is used, so that
274 user's history. By default, the 'processed' history is used, so that
269 magics are loaded in their transformed version to valid Python. If
275 magics are loaded in their transformed version to valid Python. If
270 this option is given, the raw input as typed as the command line is
276 this option is given, the raw input as typed as the command line is
271 used instead. When you exit the editor, it will be executed by
277 used instead. When you exit the editor, it will be executed by
272 IPython's own processor.
278 IPython's own processor.
273
279
274 -x: do not execute the edited code immediately upon exit. This is
280 -x: do not execute the edited code immediately upon exit. This is
275 mainly useful if you are editing programs which need to be called with
281 mainly useful if you are editing programs which need to be called with
276 command line arguments, which you can then do using %run.
282 command line arguments, which you can then do using %run.
277
283
278
284
279 Arguments:
285 Arguments:
280
286
281 If arguments are given, the following possibilites exist:
287 If arguments are given, the following possibilites exist:
282
288
283 - The arguments are numbers or pairs of colon-separated numbers (like
289 - The arguments are numbers or pairs of colon-separated numbers (like
284 1 4:8 9). These are interpreted as lines of previous input to be
290 1 4:8 9). These are interpreted as lines of previous input to be
285 loaded into the editor. The syntax is the same of the %macro command.
291 loaded into the editor. The syntax is the same of the %macro command.
286
292
287 - If the argument doesn't start with a number, it is evaluated as a
293 - If the argument doesn't start with a number, it is evaluated as a
288 variable and its contents loaded into the editor. You can thus edit
294 variable and its contents loaded into the editor. You can thus edit
289 any string which contains python code (including the result of
295 any string which contains python code (including the result of
290 previous edits).
296 previous edits).
291
297
292 - If the argument is the name of an object (other than a string),
298 - If the argument is the name of an object (other than a string),
293 IPython will try to locate the file where it was defined and open the
299 IPython will try to locate the file where it was defined and open the
294 editor at the point where it is defined. You can use `%edit function`
300 editor at the point where it is defined. You can use `%edit function`
295 to load an editor exactly at the point where 'function' is defined,
301 to load an editor exactly at the point where 'function' is defined,
296 edit it and have the file be executed automatically.
302 edit it and have the file be executed automatically.
297
303
298 If the object is a macro (see %macro for details), this opens up your
304 If the object is a macro (see %macro for details), this opens up your
299 specified editor with a temporary file containing the macro's data.
305 specified editor with a temporary file containing the macro's data.
300 Upon exit, the macro is reloaded with the contents of the file.
306 Upon exit, the macro is reloaded with the contents of the file.
301
307
302 Note: opening at an exact line is only supported under Unix, and some
308 Note: opening at an exact line is only supported under Unix, and some
303 editors (like kedit and gedit up to Gnome 2.8) do not understand the
309 editors (like kedit and gedit up to Gnome 2.8) do not understand the
304 '+NUMBER' parameter necessary for this feature. Good editors like
310 '+NUMBER' parameter necessary for this feature. Good editors like
305 (X)Emacs, vi, jed, pico and joe all do.
311 (X)Emacs, vi, jed, pico and joe all do.
306
312
307 - If the argument is not found as a variable, IPython will look for a
313 - If the argument is not found as a variable, IPython will look for a
308 file with that name (adding .py if necessary) and load it into the
314 file with that name (adding .py if necessary) and load it into the
309 editor. It will execute its contents with execfile() when you exit,
315 editor. It will execute its contents with execfile() when you exit,
310 loading any code in the file into your interactive namespace.
316 loading any code in the file into your interactive namespace.
311
317
312 After executing your code, %edit will return as output the code you
318 After executing your code, %edit will return as output the code you
313 typed in the editor (except when it was an existing file). This way
319 typed in the editor (except when it was an existing file). This way
314 you can reload the code in further invocations of %edit as a variable,
320 you can reload the code in further invocations of %edit as a variable,
315 via _<NUMBER> or Out[<NUMBER>], where <NUMBER> is the prompt number of
321 via _<NUMBER> or Out[<NUMBER>], where <NUMBER> is the prompt number of
316 the output.
322 the output.
317
323
318 Note that %edit is also available through the alias %ed.
324 Note that %edit is also available through the alias %ed.
319
325
320 This is an example of creating a simple function inside the editor and
326 This is an example of creating a simple function inside the editor and
321 then modifying it. First, start up the editor:
327 then modifying it. First, start up the editor:
322
328
323 In [1]: ed
329 In [1]: ed
324 Editing... done. Executing edited code...
330 Editing... done. Executing edited code...
325 Out[1]: 'def foo():n print "foo() was defined in an editing session"n'
331 Out[1]: 'def foo():n print "foo() was defined in an editing session"n'
326
332
327 We can then call the function foo():
333 We can then call the function foo():
328
334
329 In [2]: foo()
335 In [2]: foo()
330 foo() was defined in an editing session
336 foo() was defined in an editing session
331
337
332 Now we edit foo. IPython automatically loads the editor with the
338 Now we edit foo. IPython automatically loads the editor with the
333 (temporary) file where foo() was previously defined:
339 (temporary) file where foo() was previously defined:
334
340
335 In [3]: ed foo
341 In [3]: ed foo
336 Editing... done. Executing edited code...
342 Editing... done. Executing edited code...
337
343
338 And if we call foo() again we get the modified version:
344 And if we call foo() again we get the modified version:
339
345
340 In [4]: foo()
346 In [4]: foo()
341 foo() has now been changed!
347 foo() has now been changed!
342
348
343 Here is an example of how to edit a code snippet successive
349 Here is an example of how to edit a code snippet successive
344 times. First we call the editor:
350 times. First we call the editor:
345
351
346 In [5]: ed
352 In [5]: ed
347 Editing... done. Executing edited code...
353 Editing... done. Executing edited code...
348 hello
354 hello
349 Out[5]: "print 'hello'n"
355 Out[5]: "print 'hello'n"
350
356
351 Now we call it again with the previous output (stored in _):
357 Now we call it again with the previous output (stored in _):
352
358
353 In [6]: ed _
359 In [6]: ed _
354 Editing... done. Executing edited code...
360 Editing... done. Executing edited code...
355 hello world
361 hello world
356 Out[6]: "print 'hello world'n"
362 Out[6]: "print 'hello world'n"
357
363
358 Now we call it with the output #8 (stored in _8, also as Out[8]):
364 Now we call it with the output #8 (stored in _8, also as Out[8]):
359
365
360 In [7]: ed _8
366 In [7]: ed _8
361 Editing... done. Executing edited code...
367 Editing... done. Executing edited code...
362 hello again
368 hello again
363 Out[7]: "print 'hello again'n"
369 Out[7]: "print 'hello again'n"
364 """
370 """
365
371
366 opts,args = self.parse_options(parameter_s,'prn:')
372 opts,args = self.parse_options(parameter_s,'prn:')
367
373
368 try:
374 try:
369 filename, lineno, _ = self._find_edit_target(args, opts, last_call)
375 filename, lineno, _ = self._find_edit_target(args, opts, last_call)
370 except MacroToEdit as e:
376 except MacroToEdit as e:
371 # TODO: Implement macro editing over 2 processes.
377 # TODO: Implement macro editing over 2 processes.
372 print("Macro editing not yet implemented in 2-process model.")
378 print("Macro editing not yet implemented in 2-process model.")
373 return
379 return
374
380
375 # Make sure we send to the client an absolute path, in case the working
381 # Make sure we send to the client an absolute path, in case the working
376 # directory of client and kernel don't match
382 # directory of client and kernel don't match
377 filename = os.path.abspath(filename)
383 filename = os.path.abspath(filename)
378
384
379 payload = {
385 payload = {
380 'source' : 'IPython.zmq.zmqshell.ZMQInteractiveShell.edit_magic',
386 'source' : 'IPython.zmq.zmqshell.ZMQInteractiveShell.edit_magic',
381 'filename' : filename,
387 'filename' : filename,
382 'line_number' : lineno
388 'line_number' : lineno
383 }
389 }
384 self.payload_manager.write_payload(payload)
390 self.payload_manager.write_payload(payload)
385
391
386 def magic_gui(self, *args, **kwargs):
392 def magic_gui(self, *args, **kwargs):
387 raise NotImplementedError(
393 raise NotImplementedError(
388 'Kernel GUI support is not implemented yet, except for --pylab.')
394 'Kernel GUI support is not implemented yet, except for --pylab.')
389
395
390 def magic_pylab(self, *args, **kwargs):
396 def magic_pylab(self, *args, **kwargs):
391 raise NotImplementedError(
397 raise NotImplementedError(
392 'pylab support must be enabled in command line options.')
398 'pylab support must be enabled in command line options.')
393
399
394 # A few magics that are adapted to the specifics of using pexpect and a
400 # A few magics that are adapted to the specifics of using pexpect and a
395 # remote terminal
401 # remote terminal
396
402
397 def magic_clear(self, arg_s):
403 def magic_clear(self, arg_s):
398 """Clear the terminal."""
404 """Clear the terminal."""
399 if os.name == 'posix':
405 if os.name == 'posix':
400 self.shell.system("clear")
406 self.shell.system("clear")
401 else:
407 else:
402 self.shell.system("cls")
408 self.shell.system("cls")
403
409
404 if os.name == 'nt':
410 if os.name == 'nt':
405 # This is the usual name in windows
411 # This is the usual name in windows
406 magic_cls = magic_clear
412 magic_cls = magic_clear
407
413
408 # Terminal pagers won't work over pexpect, but we do have our own pager
414 # Terminal pagers won't work over pexpect, but we do have our own pager
409
415
410 def magic_less(self, arg_s):
416 def magic_less(self, arg_s):
411 """Show a file through the pager.
417 """Show a file through the pager.
412
418
413 Files ending in .py are syntax-highlighted."""
419 Files ending in .py are syntax-highlighted."""
414 cont = open(arg_s).read()
420 cont = open(arg_s).read()
415 if arg_s.endswith('.py'):
421 if arg_s.endswith('.py'):
416 cont = self.shell.pycolorize(cont)
422 cont = self.shell.pycolorize(cont)
417 page.page(cont)
423 page.page(cont)
418
424
419 magic_more = magic_less
425 magic_more = magic_less
420
426
421 # Man calls a pager, so we also need to redefine it
427 # Man calls a pager, so we also need to redefine it
422 if os.name == 'posix':
428 if os.name == 'posix':
423 def magic_man(self, arg_s):
429 def magic_man(self, arg_s):
424 """Find the man page for the given command and display in pager."""
430 """Find the man page for the given command and display in pager."""
425 page.page(self.shell.getoutput('man %s | col -b' % arg_s,
431 page.page(self.shell.getoutput('man %s | col -b' % arg_s,
426 split=False))
432 split=False))
427
433
428 # FIXME: this is specific to the GUI, so we should let the gui app load
434 # FIXME: this is specific to the GUI, so we should let the gui app load
429 # magics at startup that are only for the gui. Once the gui app has proper
435 # magics at startup that are only for the gui. Once the gui app has proper
430 # profile and configuration management, we can have it initialize a kernel
436 # profile and configuration management, we can have it initialize a kernel
431 # with a special config file that provides these.
437 # with a special config file that provides these.
432 def magic_guiref(self, arg_s):
438 def magic_guiref(self, arg_s):
433 """Show a basic reference about the GUI console."""
439 """Show a basic reference about the GUI console."""
434 from IPython.core.usage import gui_reference
440 from IPython.core.usage import gui_reference
435 page.page(gui_reference, auto_html=True)
441 page.page(gui_reference, auto_html=True)
436
442
437 def magic_connect_info(self, arg_s):
443 def magic_connect_info(self, arg_s):
438 """Print information for connecting other clients to this kernel
444 """Print information for connecting other clients to this kernel
439
445
440 It will print the contents of this session's connection file, as well as
446 It will print the contents of this session's connection file, as well as
441 shortcuts for local clients.
447 shortcuts for local clients.
442
448
443 In the simplest case, when called from the most recently launched kernel,
449 In the simplest case, when called from the most recently launched kernel,
444 secondary clients can be connected, simply with:
450 secondary clients can be connected, simply with:
445
451
446 $> ipython <app> --existing
452 $> ipython <app> --existing
447
453
448 """
454 """
449 try:
455 try:
450 connection_file = get_connection_file()
456 connection_file = get_connection_file()
451 info = get_connection_info(unpack=False)
457 info = get_connection_info(unpack=False)
452 except Exception as e:
458 except Exception as e:
453 error("Could not get connection info: %r" % e)
459 error("Could not get connection info: %r" % e)
454 return
460 return
455
461
456 print (info + '\n')
462 print (info + '\n')
457 print ("Paste the above JSON into a file, and connect with:\n"
463 print ("Paste the above JSON into a file, and connect with:\n"
458 " $> ipython <app> --existing <file>\n"
464 " $> ipython <app> --existing <file>\n"
459 "or, if you are local, you can connect with just:\n"
465 "or, if you are local, you can connect with just:\n"
460 " $> ipython <app> --existing %s\n"
466 " $> ipython <app> --existing %s\n"
461 "or even just:\n"
467 "or even just:\n"
462 " $> ipython <app> --existing\n"
468 " $> ipython <app> --existing\n"
463 "if this is the most recent IPython session you have started."
469 "if this is the most recent IPython session you have started."
464 % os.path.basename(connection_file)
470 % os.path.basename(connection_file)
465 )
471 )
466
472
467 def magic_qtconsole(self, arg_s):
473 def magic_qtconsole(self, arg_s):
468 """Open a qtconsole connected to this kernel.
474 """Open a qtconsole connected to this kernel.
469
475
470 Useful for connecting a qtconsole to running notebooks, for better
476 Useful for connecting a qtconsole to running notebooks, for better
471 debugging.
477 debugging.
472 """
478 """
473 try:
479 try:
474 p = connect_qtconsole(argv=arg_split(arg_s, os.name=='posix'))
480 p = connect_qtconsole(argv=arg_split(arg_s, os.name=='posix'))
475 except Exception as e:
481 except Exception as e:
476 error("Could not start qtconsole: %r" % e)
482 error("Could not start qtconsole: %r" % e)
477 return
483 return
478
484
479
485
480 def set_next_input(self, text):
486 def set_next_input(self, text):
481 """Send the specified text to the frontend to be presented at the next
487 """Send the specified text to the frontend to be presented at the next
482 input cell."""
488 input cell."""
483 payload = dict(
489 payload = dict(
484 source='IPython.zmq.zmqshell.ZMQInteractiveShell.set_next_input',
490 source='IPython.zmq.zmqshell.ZMQInteractiveShell.set_next_input',
485 text=text
491 text=text
486 )
492 )
487 self.payload_manager.write_payload(payload)
493 self.payload_manager.write_payload(payload)
488
494
489 InteractiveShellABC.register(ZMQInteractiveShell)
495 InteractiveShellABC.register(ZMQInteractiveShell)
General Comments 0
You need to be logged in to leave comments. Login now