##// END OF EJS Templates
document initially hidden javascript container
MinRK -
Show More
@@ -1,513 +1,518
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-2011 The IPython Development Team
10 # Copyright (C) 2008-2011 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 __future__ import print_function
20 from __future__ import print_function
21
21
22 from xml.dom import minidom
22 from xml.dom import minidom
23
23
24 from .displaypub import (
24 from .displaypub import (
25 publish_pretty, publish_html,
25 publish_pretty, publish_html,
26 publish_latex, publish_svg,
26 publish_latex, publish_svg,
27 publish_png, publish_json,
27 publish_png, publish_json,
28 publish_javascript, publish_jpeg
28 publish_javascript, publish_jpeg
29 )
29 )
30
30
31 #-----------------------------------------------------------------------------
31 #-----------------------------------------------------------------------------
32 # Main functions
32 # Main functions
33 #-----------------------------------------------------------------------------
33 #-----------------------------------------------------------------------------
34
34
35 def display(*objs, **kwargs):
35 def display(*objs, **kwargs):
36 """Display a Python object in all frontends.
36 """Display a Python object in all frontends.
37
37
38 By default all representations will be computed and sent to the frontends.
38 By default all representations will be computed and sent to the frontends.
39 Frontends can decide which representation is used and how.
39 Frontends can decide which representation is used and how.
40
40
41 Parameters
41 Parameters
42 ----------
42 ----------
43 objs : tuple of objects
43 objs : tuple of objects
44 The Python objects to display.
44 The Python objects to display.
45 include : list or tuple, optional
45 include : list or tuple, optional
46 A list of format type strings (MIME types) to include in the
46 A list of format type strings (MIME types) to include in the
47 format data dict. If this is set *only* the format types included
47 format data dict. If this is set *only* the format types included
48 in this list will be computed.
48 in this list will be computed.
49 exclude : list or tuple, optional
49 exclude : list or tuple, optional
50 A list of format type string (MIME types) to exclue in the format
50 A list of format type string (MIME types) to exclue in the format
51 data dict. If this is set all format types will be computed,
51 data dict. If this is set all format types will be computed,
52 except for those included in this argument.
52 except for those included in this argument.
53 """
53 """
54 include = kwargs.get('include')
54 include = kwargs.get('include')
55 exclude = kwargs.get('exclude')
55 exclude = kwargs.get('exclude')
56
56
57 from IPython.core.interactiveshell import InteractiveShell
57 from IPython.core.interactiveshell import InteractiveShell
58 inst = InteractiveShell.instance()
58 inst = InteractiveShell.instance()
59 format = inst.display_formatter.format
59 format = inst.display_formatter.format
60 publish = inst.display_pub.publish
60 publish = inst.display_pub.publish
61
61
62 for obj in objs:
62 for obj in objs:
63 format_dict = format(obj, include=include, exclude=exclude)
63 format_dict = format(obj, include=include, exclude=exclude)
64 publish('IPython.core.display.display', format_dict)
64 publish('IPython.core.display.display', format_dict)
65
65
66
66
67 def display_pretty(*objs, **kwargs):
67 def display_pretty(*objs, **kwargs):
68 """Display the pretty (default) representation of an object.
68 """Display the pretty (default) representation of an object.
69
69
70 Parameters
70 Parameters
71 ----------
71 ----------
72 objs : tuple of objects
72 objs : tuple of objects
73 The Python objects to display, or if raw=True raw text data to
73 The Python objects to display, or if raw=True raw text data to
74 display.
74 display.
75 raw : bool
75 raw : bool
76 Are the data objects raw data or Python objects that need to be
76 Are the data objects raw data or Python objects that need to be
77 formatted before display? [default: False]
77 formatted before display? [default: False]
78 """
78 """
79 raw = kwargs.pop('raw',False)
79 raw = kwargs.pop('raw',False)
80 if raw:
80 if raw:
81 for obj in objs:
81 for obj in objs:
82 publish_pretty(obj)
82 publish_pretty(obj)
83 else:
83 else:
84 display(*objs, include=['text/plain'])
84 display(*objs, include=['text/plain'])
85
85
86
86
87 def display_html(*objs, **kwargs):
87 def display_html(*objs, **kwargs):
88 """Display the HTML representation of an object.
88 """Display the HTML representation of an object.
89
89
90 Parameters
90 Parameters
91 ----------
91 ----------
92 objs : tuple of objects
92 objs : tuple of objects
93 The Python objects to display, or if raw=True raw HTML data to
93 The Python objects to display, or if raw=True raw HTML data to
94 display.
94 display.
95 raw : bool
95 raw : bool
96 Are the data objects raw data or Python objects that need to be
96 Are the data objects raw data or Python objects that need to be
97 formatted before display? [default: False]
97 formatted before display? [default: False]
98 """
98 """
99 raw = kwargs.pop('raw',False)
99 raw = kwargs.pop('raw',False)
100 if raw:
100 if raw:
101 for obj in objs:
101 for obj in objs:
102 publish_html(obj)
102 publish_html(obj)
103 else:
103 else:
104 display(*objs, include=['text/plain','text/html'])
104 display(*objs, include=['text/plain','text/html'])
105
105
106
106
107 def display_svg(*objs, **kwargs):
107 def display_svg(*objs, **kwargs):
108 """Display the SVG representation of an object.
108 """Display the SVG representation of an object.
109
109
110 Parameters
110 Parameters
111 ----------
111 ----------
112 objs : tuple of objects
112 objs : tuple of objects
113 The Python objects to display, or if raw=True raw svg data to
113 The Python objects to display, or if raw=True raw svg data to
114 display.
114 display.
115 raw : bool
115 raw : bool
116 Are the data objects raw data or Python objects that need to be
116 Are the data objects raw data or Python objects that need to be
117 formatted before display? [default: False]
117 formatted before display? [default: False]
118 """
118 """
119 raw = kwargs.pop('raw',False)
119 raw = kwargs.pop('raw',False)
120 if raw:
120 if raw:
121 for obj in objs:
121 for obj in objs:
122 publish_svg(obj)
122 publish_svg(obj)
123 else:
123 else:
124 display(*objs, include=['text/plain','image/svg+xml'])
124 display(*objs, include=['text/plain','image/svg+xml'])
125
125
126
126
127 def display_png(*objs, **kwargs):
127 def display_png(*objs, **kwargs):
128 """Display the PNG representation of an object.
128 """Display the PNG representation of an object.
129
129
130 Parameters
130 Parameters
131 ----------
131 ----------
132 objs : tuple of objects
132 objs : tuple of objects
133 The Python objects to display, or if raw=True raw png data to
133 The Python objects to display, or if raw=True raw png data to
134 display.
134 display.
135 raw : bool
135 raw : bool
136 Are the data objects raw data or Python objects that need to be
136 Are the data objects raw data or Python objects that need to be
137 formatted before display? [default: False]
137 formatted before display? [default: False]
138 """
138 """
139 raw = kwargs.pop('raw',False)
139 raw = kwargs.pop('raw',False)
140 if raw:
140 if raw:
141 for obj in objs:
141 for obj in objs:
142 publish_png(obj)
142 publish_png(obj)
143 else:
143 else:
144 display(*objs, include=['text/plain','image/png'])
144 display(*objs, include=['text/plain','image/png'])
145
145
146
146
147 def display_jpeg(*objs, **kwargs):
147 def display_jpeg(*objs, **kwargs):
148 """Display the JPEG representation of an object.
148 """Display the JPEG representation of an object.
149
149
150 Parameters
150 Parameters
151 ----------
151 ----------
152 objs : tuple of objects
152 objs : tuple of objects
153 The Python objects to display, or if raw=True raw JPEG data to
153 The Python objects to display, or if raw=True raw JPEG data to
154 display.
154 display.
155 raw : bool
155 raw : bool
156 Are the data objects raw data or Python objects that need to be
156 Are the data objects raw data or Python objects that need to be
157 formatted before display? [default: False]
157 formatted before display? [default: False]
158 """
158 """
159 raw = kwargs.pop('raw',False)
159 raw = kwargs.pop('raw',False)
160 if raw:
160 if raw:
161 for obj in objs:
161 for obj in objs:
162 publish_jpeg(obj)
162 publish_jpeg(obj)
163 else:
163 else:
164 display(*objs, include=['text/plain','image/jpeg'])
164 display(*objs, include=['text/plain','image/jpeg'])
165
165
166
166
167 def display_latex(*objs, **kwargs):
167 def display_latex(*objs, **kwargs):
168 """Display the LaTeX representation of an object.
168 """Display the LaTeX representation of an object.
169
169
170 Parameters
170 Parameters
171 ----------
171 ----------
172 objs : tuple of objects
172 objs : tuple of objects
173 The Python objects to display, or if raw=True raw latex data to
173 The Python objects to display, or if raw=True raw latex data to
174 display.
174 display.
175 raw : bool
175 raw : bool
176 Are the data objects raw data or Python objects that need to be
176 Are the data objects raw data or Python objects that need to be
177 formatted before display? [default: False]
177 formatted before display? [default: False]
178 """
178 """
179 raw = kwargs.pop('raw',False)
179 raw = kwargs.pop('raw',False)
180 if raw:
180 if raw:
181 for obj in objs:
181 for obj in objs:
182 publish_latex(obj)
182 publish_latex(obj)
183 else:
183 else:
184 display(*objs, include=['text/plain','text/latex'])
184 display(*objs, include=['text/plain','text/latex'])
185
185
186
186
187 def display_json(*objs, **kwargs):
187 def display_json(*objs, **kwargs):
188 """Display the JSON representation of an object.
188 """Display the JSON representation of an object.
189
189
190 Parameters
190 Parameters
191 ----------
191 ----------
192 objs : tuple of objects
192 objs : tuple of objects
193 The Python objects to display, or if raw=True raw json data to
193 The Python objects to display, or if raw=True raw json data to
194 display.
194 display.
195 raw : bool
195 raw : bool
196 Are the data objects raw data or Python objects that need to be
196 Are the data objects raw data or Python objects that need to be
197 formatted before display? [default: False]
197 formatted before display? [default: False]
198 """
198 """
199 raw = kwargs.pop('raw',False)
199 raw = kwargs.pop('raw',False)
200 if raw:
200 if raw:
201 for obj in objs:
201 for obj in objs:
202 publish_json(obj)
202 publish_json(obj)
203 else:
203 else:
204 display(*objs, include=['text/plain','application/json'])
204 display(*objs, include=['text/plain','application/json'])
205
205
206
206
207 def display_javascript(*objs, **kwargs):
207 def display_javascript(*objs, **kwargs):
208 """Display the Javascript representation of an object.
208 """Display the Javascript representation of an object.
209
209
210 Parameters
210 Parameters
211 ----------
211 ----------
212 objs : tuple of objects
212 objs : tuple of objects
213 The Python objects to display, or if raw=True raw javascript data to
213 The Python objects to display, or if raw=True raw javascript data to
214 display.
214 display.
215 raw : bool
215 raw : bool
216 Are the data objects raw data or Python objects that need to be
216 Are the data objects raw data or Python objects that need to be
217 formatted before display? [default: False]
217 formatted before display? [default: False]
218 """
218 """
219 raw = kwargs.pop('raw',False)
219 raw = kwargs.pop('raw',False)
220 if raw:
220 if raw:
221 for obj in objs:
221 for obj in objs:
222 publish_javascript(obj)
222 publish_javascript(obj)
223 else:
223 else:
224 display(*objs, include=['text/plain','application/javascript'])
224 display(*objs, include=['text/plain','application/javascript'])
225
225
226 #-----------------------------------------------------------------------------
226 #-----------------------------------------------------------------------------
227 # Smart classes
227 # Smart classes
228 #-----------------------------------------------------------------------------
228 #-----------------------------------------------------------------------------
229
229
230
230
231 class DisplayObject(object):
231 class DisplayObject(object):
232 """An object that wraps data to be displayed."""
232 """An object that wraps data to be displayed."""
233
233
234 _read_flags = 'r'
234 _read_flags = 'r'
235
235
236 def __init__(self, data=None, url=None, filename=None):
236 def __init__(self, data=None, url=None, filename=None):
237 """Create a display object given raw data.
237 """Create a display object given raw data.
238
238
239 When this object is returned by an expression or passed to the
239 When this object is returned by an expression or passed to the
240 display function, it will result in the data being displayed
240 display function, it will result in the data being displayed
241 in the frontend. The MIME type of the data should match the
241 in the frontend. The MIME type of the data should match the
242 subclasses used, so the Png subclass should be used for 'image/png'
242 subclasses used, so the Png subclass should be used for 'image/png'
243 data. If the data is a URL, the data will first be downloaded
243 data. If the data is a URL, the data will first be downloaded
244 and then displayed. If
244 and then displayed. If
245
245
246 Parameters
246 Parameters
247 ----------
247 ----------
248 data : unicode, str or bytes
248 data : unicode, str or bytes
249 The raw data or a URL to download the data from.
249 The raw data or a URL to download the data from.
250 url : unicode
250 url : unicode
251 A URL to download the data from.
251 A URL to download the data from.
252 filename : unicode
252 filename : unicode
253 Path to a local file to load the data from.
253 Path to a local file to load the data from.
254 """
254 """
255 if data is not None and data.startswith('http'):
255 if data is not None and data.startswith('http'):
256 self.url = data
256 self.url = data
257 self.filename = None
257 self.filename = None
258 self.data = None
258 self.data = None
259 else:
259 else:
260 self.data = data
260 self.data = data
261 self.url = url
261 self.url = url
262 self.filename = None if filename is None else unicode(filename)
262 self.filename = None if filename is None else unicode(filename)
263 self.reload()
263 self.reload()
264
264
265 def reload(self):
265 def reload(self):
266 """Reload the raw data from file or URL."""
266 """Reload the raw data from file or URL."""
267 if self.filename is not None:
267 if self.filename is not None:
268 with open(self.filename, self._read_flags) as f:
268 with open(self.filename, self._read_flags) as f:
269 self.data = f.read()
269 self.data = f.read()
270 elif self.url is not None:
270 elif self.url is not None:
271 try:
271 try:
272 import urllib2
272 import urllib2
273 response = urllib2.urlopen(self.url)
273 response = urllib2.urlopen(self.url)
274 self.data = response.read()
274 self.data = response.read()
275 # extract encoding from header, if there is one:
275 # extract encoding from header, if there is one:
276 encoding = None
276 encoding = None
277 for sub in response.headers['content-type'].split(';'):
277 for sub in response.headers['content-type'].split(';'):
278 sub = sub.strip()
278 sub = sub.strip()
279 if sub.startswith('charset'):
279 if sub.startswith('charset'):
280 encoding = sub.split('=')[-1].strip()
280 encoding = sub.split('=')[-1].strip()
281 break
281 break
282 # decode data, if an encoding was specified
282 # decode data, if an encoding was specified
283 if encoding:
283 if encoding:
284 self.data = self.data.decode(encoding, 'replace')
284 self.data = self.data.decode(encoding, 'replace')
285 except:
285 except:
286 self.data = None
286 self.data = None
287
287
288 class Pretty(DisplayObject):
288 class Pretty(DisplayObject):
289
289
290 def _repr_pretty_(self):
290 def _repr_pretty_(self):
291 return self.data
291 return self.data
292
292
293
293
294 class HTML(DisplayObject):
294 class HTML(DisplayObject):
295
295
296 def _repr_html_(self):
296 def _repr_html_(self):
297 return self.data
297 return self.data
298
298
299
299
300 class Math(DisplayObject):
300 class Math(DisplayObject):
301
301
302 def _repr_latex_(self):
302 def _repr_latex_(self):
303 s = self.data.strip('$')
303 s = self.data.strip('$')
304 return "$$%s$$" % s
304 return "$$%s$$" % s
305
305
306
306
307 class Latex(DisplayObject):
307 class Latex(DisplayObject):
308
308
309 def _repr_latex_(self):
309 def _repr_latex_(self):
310 return self.data
310 return self.data
311
311
312
312
313 class SVG(DisplayObject):
313 class SVG(DisplayObject):
314
314
315 # wrap data in a property, which extracts the <svg> tag, discarding
315 # wrap data in a property, which extracts the <svg> tag, discarding
316 # document headers
316 # document headers
317 _data = None
317 _data = None
318
318
319 @property
319 @property
320 def data(self):
320 def data(self):
321 return self._data
321 return self._data
322
322
323 @data.setter
323 @data.setter
324 def data(self, svg):
324 def data(self, svg):
325 if svg is None:
325 if svg is None:
326 self._data = None
326 self._data = None
327 return
327 return
328 # parse into dom object
328 # parse into dom object
329 x = minidom.parseString(svg)
329 x = minidom.parseString(svg)
330 # get svg tag (should be 1)
330 # get svg tag (should be 1)
331 found_svg = x.getElementsByTagName('svg')
331 found_svg = x.getElementsByTagName('svg')
332 if found_svg:
332 if found_svg:
333 svg = found_svg[0].toxml()
333 svg = found_svg[0].toxml()
334 else:
334 else:
335 # fallback on the input, trust the user
335 # fallback on the input, trust the user
336 # but this is probably an error.
336 # but this is probably an error.
337 pass
337 pass
338 self._data = svg
338 self._data = svg
339
339
340 def _repr_svg_(self):
340 def _repr_svg_(self):
341 return self.data
341 return self.data
342
342
343
343
344 class JSON(DisplayObject):
344 class JSON(DisplayObject):
345
345
346 def _repr_json_(self):
346 def _repr_json_(self):
347 return self.data
347 return self.data
348
348
349 css_t = """$("head").append($("<link/>").attr({
349 css_t = """$("head").append($("<link/>").attr({
350 rel: "stylesheet",
350 rel: "stylesheet",
351 type: "text/css",
351 type: "text/css",
352 href: "%s"
352 href: "%s"
353 }));
353 }));
354 """
354 """
355
355
356 lib_t1 = """$.getScript("%s", function () {
356 lib_t1 = """$.getScript("%s", function () {
357 """
357 """
358 lib_t2 = """});
358 lib_t2 = """});
359 """
359 """
360
360
361 class Javascript(DisplayObject):
361 class Javascript(DisplayObject):
362
362
363 def __init__(self, data=None, url=None, filename=None, lib=None, css=None):
363 def __init__(self, data=None, url=None, filename=None, lib=None, css=None):
364 """Create a Javascript display object given raw data.
364 """Create a Javascript display object given raw data.
365
365
366 When this object is returned by an expression or passed to the
366 When this object is returned by an expression or passed to the
367 display function, it will result in the data being displayed
367 display function, it will result in the data being displayed
368 in the frontend. If the data is a URL, the data will first be
368 in the frontend. If the data is a URL, the data will first be
369 downloaded and then displayed.
369 downloaded and then displayed.
370
370
371 In the Notebook, the containing element will be available as `element`,
372 and jQuery will be available. The output area starts hidden, so if
373 the js appends content to `element` that should be visible, then
374 it must call `container.show()` to unhide the area.
375
371 Parameters
376 Parameters
372 ----------
377 ----------
373 data : unicode, str or bytes
378 data : unicode, str or bytes
374 The Javascript source code or a URL to download it from.
379 The Javascript source code or a URL to download it from.
375 url : unicode
380 url : unicode
376 A URL to download the data from.
381 A URL to download the data from.
377 filename : unicode
382 filename : unicode
378 Path to a local file to load the data from.
383 Path to a local file to load the data from.
379 lib : list or str
384 lib : list or str
380 A sequence of Javascript library URLs to load asynchronously before
385 A sequence of Javascript library URLs to load asynchronously before
381 running the source code. The full URLs of the libraries should
386 running the source code. The full URLs of the libraries should
382 be given. A single Javascript library URL can also be given as a
387 be given. A single Javascript library URL can also be given as a
383 string.
388 string.
384 css: : list or str
389 css: : list or str
385 A sequence of css files to load before running the source code.
390 A sequence of css files to load before running the source code.
386 The full URLs of the css files should be give. A single css URL
391 The full URLs of the css files should be give. A single css URL
387 can also be given as a string.
392 can also be given as a string.
388 """
393 """
389 if isinstance(lib, basestring):
394 if isinstance(lib, basestring):
390 lib = [lib]
395 lib = [lib]
391 elif lib is None:
396 elif lib is None:
392 lib = []
397 lib = []
393 if isinstance(css, basestring):
398 if isinstance(css, basestring):
394 css = [css]
399 css = [css]
395 elif css is None:
400 elif css is None:
396 css = []
401 css = []
397 if not isinstance(lib, (list,tuple)):
402 if not isinstance(lib, (list,tuple)):
398 raise TypeError('expected sequence, got: %r' % lib)
403 raise TypeError('expected sequence, got: %r' % lib)
399 if not isinstance(css, (list,tuple)):
404 if not isinstance(css, (list,tuple)):
400 raise TypeError('expected sequence, got: %r' % css)
405 raise TypeError('expected sequence, got: %r' % css)
401 self.lib = lib
406 self.lib = lib
402 self.css = css
407 self.css = css
403 super(Javascript, self).__init__(data=data, url=url, filename=filename)
408 super(Javascript, self).__init__(data=data, url=url, filename=filename)
404
409
405 def _repr_javascript_(self):
410 def _repr_javascript_(self):
406 r = ''
411 r = ''
407 for c in self.css:
412 for c in self.css:
408 r += css_t % c
413 r += css_t % c
409 for l in self.lib:
414 for l in self.lib:
410 r += lib_t1 % l
415 r += lib_t1 % l
411 r += self.data
416 r += self.data
412 r += lib_t2*len(self.lib)
417 r += lib_t2*len(self.lib)
413 return r
418 return r
414
419
415
420
416 class Image(DisplayObject):
421 class Image(DisplayObject):
417
422
418 _read_flags = 'rb'
423 _read_flags = 'rb'
419
424
420 def __init__(self, data=None, url=None, filename=None, format=u'png', embed=False):
425 def __init__(self, data=None, url=None, filename=None, format=u'png', embed=False):
421 """Create a display an PNG/JPEG image given raw data.
426 """Create a display an PNG/JPEG image given raw data.
422
427
423 When this object is returned by an expression or passed to the
428 When this object is returned by an expression or passed to the
424 display function, it will result in the image being displayed
429 display function, it will result in the image being displayed
425 in the frontend.
430 in the frontend.
426
431
427 Parameters
432 Parameters
428 ----------
433 ----------
429 data : unicode, str or bytes
434 data : unicode, str or bytes
430 The raw data or a URL to download the data from.
435 The raw data or a URL to download the data from.
431 url : unicode
436 url : unicode
432 A URL to download the data from.
437 A URL to download the data from.
433 filename : unicode
438 filename : unicode
434 Path to a local file to load the data from.
439 Path to a local file to load the data from.
435 format : unicode
440 format : unicode
436 The format of the image data (png/jpeg/jpg). If a filename or URL is given
441 The format of the image data (png/jpeg/jpg). If a filename or URL is given
437 for format will be inferred from the filename extension.
442 for format will be inferred from the filename extension.
438 embed : bool
443 embed : bool
439 Should the image data be embedded in the notebook using a data URI (True)
444 Should the image data be embedded in the notebook using a data URI (True)
440 or be loaded using an <img> tag. Set this to True if you want the image
445 or be loaded using an <img> tag. Set this to True if you want the image
441 to be viewable later with no internet connection. If a filename is given
446 to be viewable later with no internet connection. If a filename is given
442 embed is always set to True.
447 embed is always set to True.
443 """
448 """
444 if filename is not None:
449 if filename is not None:
445 ext = self._find_ext(filename)
450 ext = self._find_ext(filename)
446 elif url is not None:
451 elif url is not None:
447 ext = self._find_ext(url)
452 ext = self._find_ext(url)
448 elif data.startswith('http'):
453 elif data.startswith('http'):
449 ext = self._find_ext(data)
454 ext = self._find_ext(data)
450 else:
455 else:
451 ext = None
456 ext = None
452 if ext is not None:
457 if ext is not None:
453 if ext == u'jpg' or ext == u'jpeg':
458 if ext == u'jpg' or ext == u'jpeg':
454 format = u'jpeg'
459 format = u'jpeg'
455 if ext == u'png':
460 if ext == u'png':
456 format = u'png'
461 format = u'png'
457 self.format = unicode(format).lower()
462 self.format = unicode(format).lower()
458 self.embed = True if filename is not None else embed
463 self.embed = True if filename is not None else embed
459 super(Image, self).__init__(data=data, url=url, filename=filename)
464 super(Image, self).__init__(data=data, url=url, filename=filename)
460
465
461 def reload(self):
466 def reload(self):
462 """Reload the raw data from file or URL."""
467 """Reload the raw data from file or URL."""
463 if self.embed:
468 if self.embed:
464 super(Image,self).reload()
469 super(Image,self).reload()
465
470
466 def _repr_html_(self):
471 def _repr_html_(self):
467 if not self.embed:
472 if not self.embed:
468 return u'<img src="%s" />' % self.url
473 return u'<img src="%s" />' % self.url
469
474
470 def _repr_png_(self):
475 def _repr_png_(self):
471 if self.embed and self.format == u'png':
476 if self.embed and self.format == u'png':
472 return self.data
477 return self.data
473
478
474 def _repr_jpeg_(self):
479 def _repr_jpeg_(self):
475 if self.embed and (self.format == u'jpeg' or self.format == u'jpg'):
480 if self.embed and (self.format == u'jpeg' or self.format == u'jpg'):
476 return self.data
481 return self.data
477
482
478 def _find_ext(self, s):
483 def _find_ext(self, s):
479 return unicode(s.split('.')[-1].lower())
484 return unicode(s.split('.')[-1].lower())
480
485
481
486
482 def clear_output(stdout=True, stderr=True, other=True):
487 def clear_output(stdout=True, stderr=True, other=True):
483 """Clear the output of the current cell receiving output.
488 """Clear the output of the current cell receiving output.
484
489
485 Optionally, each of stdout/stderr or other non-stream data (e.g. anything
490 Optionally, each of stdout/stderr or other non-stream data (e.g. anything
486 produced by display()) can be excluded from the clear event.
491 produced by display()) can be excluded from the clear event.
487
492
488 By default, everything is cleared.
493 By default, everything is cleared.
489
494
490 Parameters
495 Parameters
491 ----------
496 ----------
492 stdout : bool [default: True]
497 stdout : bool [default: True]
493 Whether to clear stdout.
498 Whether to clear stdout.
494 stderr : bool [default: True]
499 stderr : bool [default: True]
495 Whether to clear stderr.
500 Whether to clear stderr.
496 other : bool [default: True]
501 other : bool [default: True]
497 Whether to clear everything else that is not stdout/stderr
502 Whether to clear everything else that is not stdout/stderr
498 (e.g. figures,images,HTML, any result of display()).
503 (e.g. figures,images,HTML, any result of display()).
499 """
504 """
500 from IPython.core.interactiveshell import InteractiveShell
505 from IPython.core.interactiveshell import InteractiveShell
501 if InteractiveShell.initialized():
506 if InteractiveShell.initialized():
502 InteractiveShell.instance().display_pub.clear_output(
507 InteractiveShell.instance().display_pub.clear_output(
503 stdout=stdout, stderr=stderr, other=other,
508 stdout=stdout, stderr=stderr, other=other,
504 )
509 )
505 else:
510 else:
506 from IPython.utils import io
511 from IPython.utils import io
507 if stdout:
512 if stdout:
508 print('\033[2K\r', file=io.stdout, end='')
513 print('\033[2K\r', file=io.stdout, end='')
509 io.stdout.flush()
514 io.stdout.flush()
510 if stderr:
515 if stderr:
511 print('\033[2K\r', file=io.stderr, end='')
516 print('\033[2K\r', file=io.stderr, end='')
512 io.stderr.flush()
517 io.stderr.flush()
513
518
@@ -1,926 +1,927
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 = null;
18 this.input_prompt_number = null;
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 this.tooltip_timeout = null;
23 this.tooltip_timeout = null;
24 this.clear_out_timeout = null;
24 this.clear_out_timeout = null;
25 IPython.Cell.apply(this, arguments);
25 IPython.Cell.apply(this, arguments);
26 };
26 };
27
27
28
28
29 CodeCell.prototype = new IPython.Cell();
29 CodeCell.prototype = new IPython.Cell();
30
30
31
31
32 CodeCell.prototype.create_element = function () {
32 CodeCell.prototype.create_element = function () {
33 var cell = $('<div></div>').addClass('cell border-box-sizing code_cell vbox');
33 var cell = $('<div></div>').addClass('cell border-box-sizing code_cell vbox');
34 cell.attr('tabindex','2');
34 cell.attr('tabindex','2');
35 var input = $('<div></div>').addClass('input hbox');
35 var input = $('<div></div>').addClass('input hbox');
36 input.append($('<div/>').addClass('prompt input_prompt'));
36 input.append($('<div/>').addClass('prompt input_prompt'));
37 var input_area = $('<div/>').addClass('input_area box-flex1');
37 var input_area = $('<div/>').addClass('input_area box-flex1');
38 this.code_mirror = CodeMirror(input_area.get(0), {
38 this.code_mirror = CodeMirror(input_area.get(0), {
39 indentUnit : 4,
39 indentUnit : 4,
40 mode: 'python',
40 mode: 'python',
41 theme: 'ipython',
41 theme: 'ipython',
42 readOnly: this.read_only,
42 readOnly: this.read_only,
43 onKeyEvent: $.proxy(this.handle_codemirror_keyevent,this)
43 onKeyEvent: $.proxy(this.handle_codemirror_keyevent,this)
44 });
44 });
45 input.append(input_area);
45 input.append(input_area);
46 var output = $('<div></div>').addClass('output vbox');
46 var output = $('<div></div>').addClass('output vbox');
47 cell.append(input).append(output);
47 cell.append(input).append(output);
48 this.element = cell;
48 this.element = cell;
49 this.collapse();
49 this.collapse();
50 };
50 };
51
51
52 //TODO, try to diminish the number of parameters.
52 //TODO, try to diminish the number of parameters.
53 CodeCell.prototype.request_tooltip_after_time = function (pre_cursor,time){
53 CodeCell.prototype.request_tooltip_after_time = function (pre_cursor,time){
54 var that = this;
54 var that = this;
55 if (pre_cursor === "" || pre_cursor === "(" ) {
55 if (pre_cursor === "" || pre_cursor === "(" ) {
56 // don't do anything if line beggin with '(' or is empty
56 // don't do anything if line beggin with '(' or is empty
57 } else {
57 } else {
58 // Will set a timer to request tooltip in `time`
58 // Will set a timer to request tooltip in `time`
59 that.tooltip_timeout = setTimeout(function(){
59 that.tooltip_timeout = setTimeout(function(){
60 IPython.notebook.request_tool_tip(that, pre_cursor)
60 IPython.notebook.request_tool_tip(that, pre_cursor)
61 },time);
61 },time);
62 }
62 }
63 };
63 };
64
64
65 CodeCell.prototype.handle_codemirror_keyevent = function (editor, event) {
65 CodeCell.prototype.handle_codemirror_keyevent = function (editor, event) {
66 // This method gets called in CodeMirror's onKeyDown/onKeyPress
66 // This method gets called in CodeMirror's onKeyDown/onKeyPress
67 // handlers and is used to provide custom key handling. Its return
67 // handlers and is used to provide custom key handling. Its return
68 // value is used to determine if CodeMirror should ignore the event:
68 // value is used to determine if CodeMirror should ignore the event:
69 // true = ignore, false = don't ignore.
69 // true = ignore, false = don't ignore.
70
70
71 if (this.read_only){
71 if (this.read_only){
72 return false;
72 return false;
73 }
73 }
74
74
75 // note that we are comparing and setting the time to wait at each key press.
75 // note that we are comparing and setting the time to wait at each key press.
76 // a better wqy might be to generate a new function on each time change and
76 // a better wqy might be to generate a new function on each time change and
77 // assign it to CodeCell.prototype.request_tooltip_after_time
77 // assign it to CodeCell.prototype.request_tooltip_after_time
78 tooltip_wait_time = this.notebook.time_before_tooltip;
78 tooltip_wait_time = this.notebook.time_before_tooltip;
79 tooltip_on_tab = this.notebook.tooltip_on_tab;
79 tooltip_on_tab = this.notebook.tooltip_on_tab;
80 var that = this;
80 var that = this;
81 // whatever key is pressed, first, cancel the tooltip request before
81 // whatever key is pressed, first, cancel the tooltip request before
82 // they are sent, and remove tooltip if any
82 // they are sent, and remove tooltip if any
83 if(event.type === 'keydown' ) {
83 if(event.type === 'keydown' ) {
84 that.remove_and_cancel_tooltip();
84 that.remove_and_cancel_tooltip();
85 };
85 };
86
86
87
87
88 if (event.keyCode === 13 && (event.shiftKey || event.ctrlKey)) {
88 if (event.keyCode === 13 && (event.shiftKey || event.ctrlKey)) {
89 // Always ignore shift-enter in CodeMirror as we handle it.
89 // Always ignore shift-enter in CodeMirror as we handle it.
90 return true;
90 return true;
91 } else if (event.which === 40 && event.type === 'keypress' && tooltip_wait_time >= 0) {
91 } else if (event.which === 40 && event.type === 'keypress' && tooltip_wait_time >= 0) {
92 // triger aon keypress (!) otherwise inconsistent event.which depending on plateform
92 // triger aon keypress (!) otherwise inconsistent event.which depending on plateform
93 // browser and keyboard layout !
93 // browser and keyboard layout !
94 // Pressing '(' , request tooltip, don't forget to reappend it
94 // Pressing '(' , request tooltip, don't forget to reappend it
95 var cursor = editor.getCursor();
95 var cursor = editor.getCursor();
96 var pre_cursor = editor.getRange({line:cursor.line,ch:0},cursor).trim()+'(';
96 var pre_cursor = editor.getRange({line:cursor.line,ch:0},cursor).trim()+'(';
97 that.request_tooltip_after_time(pre_cursor,tooltip_wait_time);
97 that.request_tooltip_after_time(pre_cursor,tooltip_wait_time);
98 } else if (event.which === 38) {
98 } else if (event.which === 38) {
99 // If we are not at the top, let CM handle the up arrow and
99 // If we are not at the top, let CM handle the up arrow and
100 // prevent the global keydown handler from handling it.
100 // prevent the global keydown handler from handling it.
101 if (!that.at_top()) {
101 if (!that.at_top()) {
102 event.stop();
102 event.stop();
103 return false;
103 return false;
104 } else {
104 } else {
105 return true;
105 return true;
106 };
106 };
107 } else if (event.which === 40) {
107 } else if (event.which === 40) {
108 // If we are not at the bottom, let CM handle the down arrow and
108 // If we are not at the bottom, let CM handle the down arrow and
109 // prevent the global keydown handler from handling it.
109 // prevent the global keydown handler from handling it.
110 if (!that.at_bottom()) {
110 if (!that.at_bottom()) {
111 event.stop();
111 event.stop();
112 return false;
112 return false;
113 } else {
113 } else {
114 return true;
114 return true;
115 };
115 };
116 } else if (event.keyCode === 9 && event.type == 'keydown') {
116 } else if (event.keyCode === 9 && event.type == 'keydown') {
117 // Tab completion.
117 // Tab completion.
118 var cur = editor.getCursor();
118 var cur = editor.getCursor();
119 //Do not trim here because of tooltip
119 //Do not trim here because of tooltip
120 var pre_cursor = editor.getRange({line:cur.line,ch:0},cur);
120 var pre_cursor = editor.getRange({line:cur.line,ch:0},cur);
121 if (pre_cursor.trim() === "") {
121 if (pre_cursor.trim() === "") {
122 // Don't autocomplete if the part of the line before the cursor
122 // Don't autocomplete if the part of the line before the cursor
123 // is empty. In this case, let CodeMirror handle indentation.
123 // is empty. In this case, let CodeMirror handle indentation.
124 return false;
124 return false;
125 } else if ((pre_cursor.substr(-1) === "("|| pre_cursor.substr(-1) === " ") && tooltip_on_tab ) {
125 } else if ((pre_cursor.substr(-1) === "("|| pre_cursor.substr(-1) === " ") && tooltip_on_tab ) {
126 that.request_tooltip_after_time(pre_cursor,0);
126 that.request_tooltip_after_time(pre_cursor,0);
127 // Prevent the event from bubbling up.
127 // Prevent the event from bubbling up.
128 event.stop();
128 event.stop();
129 // Prevent CodeMirror from handling the tab.
129 // Prevent CodeMirror from handling the tab.
130 return true;
130 return true;
131 } else {
131 } else {
132 pre_cursor.trim();
132 pre_cursor.trim();
133 // Autocomplete the current line.
133 // Autocomplete the current line.
134 event.stop();
134 event.stop();
135 var line = editor.getLine(cur.line);
135 var line = editor.getLine(cur.line);
136 this.is_completing = true;
136 this.is_completing = true;
137 this.completion_cursor = cur;
137 this.completion_cursor = cur;
138 IPython.notebook.complete_cell(this, line, cur.ch);
138 IPython.notebook.complete_cell(this, line, cur.ch);
139 return true;
139 return true;
140 };
140 };
141 } else if (event.keyCode === 8 && event.type == 'keydown') {
141 } else if (event.keyCode === 8 && event.type == 'keydown') {
142 // If backspace and the line ends with 4 spaces, remove them.
142 // If backspace and the line ends with 4 spaces, remove them.
143 var cur = editor.getCursor();
143 var cur = editor.getCursor();
144 var line = editor.getLine(cur.line);
144 var line = editor.getLine(cur.line);
145 var ending = line.slice(-4);
145 var ending = line.slice(-4);
146 if (ending === ' ') {
146 if (ending === ' ') {
147 editor.replaceRange('',
147 editor.replaceRange('',
148 {line: cur.line, ch: cur.ch-4},
148 {line: cur.line, ch: cur.ch-4},
149 {line: cur.line, ch: cur.ch}
149 {line: cur.line, ch: cur.ch}
150 );
150 );
151 event.stop();
151 event.stop();
152 return true;
152 return true;
153 } else {
153 } else {
154 return false;
154 return false;
155 };
155 };
156 } else {
156 } else {
157 // keypress/keyup also trigger on TAB press, and we don't want to
157 // keypress/keyup also trigger on TAB press, and we don't want to
158 // use those to disable tab completion.
158 // use those to disable tab completion.
159 if (this.is_completing && event.keyCode !== 9) {
159 if (this.is_completing && event.keyCode !== 9) {
160 var ed_cur = editor.getCursor();
160 var ed_cur = editor.getCursor();
161 var cc_cur = this.completion_cursor;
161 var cc_cur = this.completion_cursor;
162 if (ed_cur.line !== cc_cur.line || ed_cur.ch !== cc_cur.ch) {
162 if (ed_cur.line !== cc_cur.line || ed_cur.ch !== cc_cur.ch) {
163 this.is_completing = false;
163 this.is_completing = false;
164 this.completion_cursor = null;
164 this.completion_cursor = null;
165 };
165 };
166 };
166 };
167 return false;
167 return false;
168 };
168 };
169 return false;
169 return false;
170 };
170 };
171
171
172 CodeCell.prototype.remove_and_cancel_tooltip = function() {
172 CodeCell.prototype.remove_and_cancel_tooltip = function() {
173 // note that we don't handle closing directly inside the calltip
173 // note that we don't handle closing directly inside the calltip
174 // as in the completer, because it is not focusable, so won't
174 // as in the completer, because it is not focusable, so won't
175 // get the event.
175 // get the event.
176 if (this.tooltip_timeout != null){
176 if (this.tooltip_timeout != null){
177 clearTimeout(this.tooltip_timeout);
177 clearTimeout(this.tooltip_timeout);
178 $('#tooltip').remove();
178 $('#tooltip').remove();
179 this.tooltip_timeout = null;
179 this.tooltip_timeout = null;
180 }
180 }
181 }
181 }
182
182
183 CodeCell.prototype.finish_tooltip = function (reply) {
183 CodeCell.prototype.finish_tooltip = function (reply) {
184 // Extract call tip data; the priority is call, init, main.
184 // Extract call tip data; the priority is call, init, main.
185 defstring = reply.call_def;
185 defstring = reply.call_def;
186 if (defstring == null) { defstring = reply.init_definition; }
186 if (defstring == null) { defstring = reply.init_definition; }
187 if (defstring == null) { defstring = reply.definition; }
187 if (defstring == null) { defstring = reply.definition; }
188
188
189 docstring = reply.call_docstring;
189 docstring = reply.call_docstring;
190 if (docstring == null) { docstring = reply.init_docstring; }
190 if (docstring == null) { docstring = reply.init_docstring; }
191 if (docstring == null) { docstring = reply.docstring; }
191 if (docstring == null) { docstring = reply.docstring; }
192 if (docstring == null) { docstring = "<empty docstring>"; }
192 if (docstring == null) { docstring = "<empty docstring>"; }
193
193
194 name=reply.name;
194 name=reply.name;
195
195
196 var that = this;
196 var that = this;
197 var tooltip = $('<div/>').attr('id', 'tooltip').addClass('tooltip');
197 var tooltip = $('<div/>').attr('id', 'tooltip').addClass('tooltip');
198 // remove to have the tooltip not Limited in X and Y
198 // remove to have the tooltip not Limited in X and Y
199 tooltip.addClass('smalltooltip');
199 tooltip.addClass('smalltooltip');
200 var pre=$('<pre/>').html(utils.fixConsole(docstring));
200 var pre=$('<pre/>').html(utils.fixConsole(docstring));
201 var expandlink=$('<a/>').attr('href',"#");
201 var expandlink=$('<a/>').attr('href',"#");
202 expandlink.addClass("ui-corner-all"); //rounded corner
202 expandlink.addClass("ui-corner-all"); //rounded corner
203 expandlink.attr('role',"button");
203 expandlink.attr('role',"button");
204 //expandlink.addClass('ui-button');
204 //expandlink.addClass('ui-button');
205 //expandlink.addClass('ui-state-default');
205 //expandlink.addClass('ui-state-default');
206 var expandspan=$('<span/>').text('Expand');
206 var expandspan=$('<span/>').text('Expand');
207 expandspan.addClass('ui-icon');
207 expandspan.addClass('ui-icon');
208 expandspan.addClass('ui-icon-plus');
208 expandspan.addClass('ui-icon-plus');
209 expandlink.append(expandspan);
209 expandlink.append(expandspan);
210 expandlink.attr('id','expanbutton');
210 expandlink.attr('id','expanbutton');
211 expandlink.click(function(){
211 expandlink.click(function(){
212 tooltip.removeClass('smalltooltip');
212 tooltip.removeClass('smalltooltip');
213 tooltip.addClass('bigtooltip');
213 tooltip.addClass('bigtooltip');
214 $('#expanbutton').remove();
214 $('#expanbutton').remove();
215 setTimeout(function(){that.code_mirror.focus();}, 50);
215 setTimeout(function(){that.code_mirror.focus();}, 50);
216 });
216 });
217 var morelink=$('<a/>').attr('href',"#");
217 var morelink=$('<a/>').attr('href',"#");
218 morelink.attr('role',"button");
218 morelink.attr('role',"button");
219 morelink.addClass('ui-button');
219 morelink.addClass('ui-button');
220 //morelink.addClass("ui-corner-all"); //rounded corner
220 //morelink.addClass("ui-corner-all"); //rounded corner
221 //morelink.addClass('ui-state-default');
221 //morelink.addClass('ui-state-default');
222 var morespan=$('<span/>').text('Open in Pager');
222 var morespan=$('<span/>').text('Open in Pager');
223 morespan.addClass('ui-icon');
223 morespan.addClass('ui-icon');
224 morespan.addClass('ui-icon-arrowstop-l-n');
224 morespan.addClass('ui-icon-arrowstop-l-n');
225 morelink.append(morespan);
225 morelink.append(morespan);
226 morelink.click(function(){
226 morelink.click(function(){
227 var msg_id = IPython.notebook.kernel.execute(name+"?");
227 var msg_id = IPython.notebook.kernel.execute(name+"?");
228 IPython.notebook.msg_cell_map[msg_id] = IPython.notebook.get_selected_cell().cell_id;
228 IPython.notebook.msg_cell_map[msg_id] = IPython.notebook.get_selected_cell().cell_id;
229 that.remove_and_cancel_tooltip();
229 that.remove_and_cancel_tooltip();
230 setTimeout(function(){that.code_mirror.focus();}, 50);
230 setTimeout(function(){that.code_mirror.focus();}, 50);
231 });
231 });
232
232
233 var closelink=$('<a/>').attr('href',"#");
233 var closelink=$('<a/>').attr('href',"#");
234 closelink.attr('role',"button");
234 closelink.attr('role',"button");
235 closelink.addClass('ui-button');
235 closelink.addClass('ui-button');
236 //closelink.addClass("ui-corner-all"); //rounded corner
236 //closelink.addClass("ui-corner-all"); //rounded corner
237 //closelink.adClass('ui-state-default'); // grey background and blue cross
237 //closelink.adClass('ui-state-default'); // grey background and blue cross
238 var closespan=$('<span/>').text('Close');
238 var closespan=$('<span/>').text('Close');
239 closespan.addClass('ui-icon');
239 closespan.addClass('ui-icon');
240 closespan.addClass('ui-icon-close');
240 closespan.addClass('ui-icon-close');
241 closelink.append(closespan);
241 closelink.append(closespan);
242 closelink.click(function(){
242 closelink.click(function(){
243 that.remove_and_cancel_tooltip();
243 that.remove_and_cancel_tooltip();
244 setTimeout(function(){that.code_mirror.focus();}, 50);
244 setTimeout(function(){that.code_mirror.focus();}, 50);
245 });
245 });
246 //construct the tooltip
246 //construct the tooltip
247 tooltip.append(closelink);
247 tooltip.append(closelink);
248 tooltip.append(expandlink);
248 tooltip.append(expandlink);
249 tooltip.append(morelink);
249 tooltip.append(morelink);
250 if(defstring){
250 if(defstring){
251 defstring_html = $('<pre/>').html(utils.fixConsole(defstring));
251 defstring_html = $('<pre/>').html(utils.fixConsole(defstring));
252 tooltip.append(defstring_html);
252 tooltip.append(defstring_html);
253 }
253 }
254 tooltip.append(pre);
254 tooltip.append(pre);
255 var pos = this.code_mirror.cursorCoords();
255 var pos = this.code_mirror.cursorCoords();
256 tooltip.css('left',pos.x+'px');
256 tooltip.css('left',pos.x+'px');
257 tooltip.css('top',pos.yBot+'px');
257 tooltip.css('top',pos.yBot+'px');
258 $('body').append(tooltip);
258 $('body').append(tooltip);
259
259
260 // issues with cross-closing if multiple tooltip in less than 5sec
260 // issues with cross-closing if multiple tooltip in less than 5sec
261 // keep it comented for now
261 // keep it comented for now
262 // setTimeout(that.remove_and_cancel_tooltip, 5000);
262 // setTimeout(that.remove_and_cancel_tooltip, 5000);
263 };
263 };
264
264
265 // As you type completer
265 // As you type completer
266 CodeCell.prototype.finish_completing = function (matched_text, matches) {
266 CodeCell.prototype.finish_completing = function (matched_text, matches) {
267 if(matched_text[0]=='%'){
267 if(matched_text[0]=='%'){
268 completing_from_magic = true;
268 completing_from_magic = true;
269 completing_to_magic = false;
269 completing_to_magic = false;
270 } else {
270 } else {
271 completing_from_magic = false;
271 completing_from_magic = false;
272 completing_to_magic = false;
272 completing_to_magic = false;
273 }
273 }
274 //return if not completing or nothing to complete
274 //return if not completing or nothing to complete
275 if (!this.is_completing || matches.length === 0) {return;}
275 if (!this.is_completing || matches.length === 0) {return;}
276
276
277 // for later readability
277 // for later readability
278 var key = { tab:9,
278 var key = { tab:9,
279 esc:27,
279 esc:27,
280 backspace:8,
280 backspace:8,
281 space:32,
281 space:32,
282 shift:16,
282 shift:16,
283 enter:13,
283 enter:13,
284 // _ is 95
284 // _ is 95
285 isCompSymbol : function (code)
285 isCompSymbol : function (code)
286 {
286 {
287 return (code > 64 && code <= 90)
287 return (code > 64 && code <= 90)
288 || (code >= 97 && code <= 122)
288 || (code >= 97 && code <= 122)
289 || (code == 95)
289 || (code == 95)
290 },
290 },
291 dismissAndAppend : function (code)
291 dismissAndAppend : function (code)
292 {
292 {
293 chararr = '()[]+-/\\. ,=*'.split("");
293 chararr = '()[]+-/\\. ,=*'.split("");
294 codearr = chararr.map(function(x){return x.charCodeAt(0)});
294 codearr = chararr.map(function(x){return x.charCodeAt(0)});
295 return jQuery.inArray(code, codearr) != -1;
295 return jQuery.inArray(code, codearr) != -1;
296 }
296 }
297
297
298 }
298 }
299
299
300 // smart completion, sort kwarg ending with '='
300 // smart completion, sort kwarg ending with '='
301 var newm = new Array();
301 var newm = new Array();
302 if(this.notebook.smart_completer)
302 if(this.notebook.smart_completer)
303 {
303 {
304 kwargs = new Array();
304 kwargs = new Array();
305 other = new Array();
305 other = new Array();
306 for(var i = 0 ; i<matches.length ; ++i){
306 for(var i = 0 ; i<matches.length ; ++i){
307 if(matches[i].substr(-1) === '='){
307 if(matches[i].substr(-1) === '='){
308 kwargs.push(matches[i]);
308 kwargs.push(matches[i]);
309 }else{other.push(matches[i]);}
309 }else{other.push(matches[i]);}
310 }
310 }
311 newm = kwargs.concat(other);
311 newm = kwargs.concat(other);
312 matches = newm;
312 matches = newm;
313 }
313 }
314 // end sort kwargs
314 // end sort kwargs
315
315
316 // give common prefix of a array of string
316 // give common prefix of a array of string
317 function sharedStart(A){
317 function sharedStart(A){
318 shared='';
318 shared='';
319 if(A.length == 1){shared=A[0]}
319 if(A.length == 1){shared=A[0]}
320 if(A.length > 1 ){
320 if(A.length > 1 ){
321 var tem1, tem2, s, A = A.slice(0).sort();
321 var tem1, tem2, s, A = A.slice(0).sort();
322 tem1 = A[0];
322 tem1 = A[0];
323 s = tem1.length;
323 s = tem1.length;
324 tem2 = A.pop();
324 tem2 = A.pop();
325 while(s && tem2.indexOf(tem1) == -1){
325 while(s && tem2.indexOf(tem1) == -1){
326 tem1 = tem1.substring(0, --s);
326 tem1 = tem1.substring(0, --s);
327 }
327 }
328 shared = tem1;
328 shared = tem1;
329 }
329 }
330 if (shared[0] == '%' && !completing_from_magic)
330 if (shared[0] == '%' && !completing_from_magic)
331 {
331 {
332 shared = shared.substr(1);
332 shared = shared.substr(1);
333 return [shared, true];
333 return [shared, true];
334 } else {
334 } else {
335 return [shared, false];
335 return [shared, false];
336 }
336 }
337 }
337 }
338
338
339
339
340 //try to check if the user is typing tab at least twice after a word
340 //try to check if the user is typing tab at least twice after a word
341 // and completion is "done"
341 // and completion is "done"
342 fallback_on_tooltip_after = 2
342 fallback_on_tooltip_after = 2
343 if(matches.length == 1 && matched_text === matches[0])
343 if(matches.length == 1 && matched_text === matches[0])
344 {
344 {
345 if(this.npressed >fallback_on_tooltip_after && this.prevmatch==matched_text)
345 if(this.npressed >fallback_on_tooltip_after && this.prevmatch==matched_text)
346 {
346 {
347 this.request_tooltip_after_time(matched_text+'(',0);
347 this.request_tooltip_after_time(matched_text+'(',0);
348 return;
348 return;
349 }
349 }
350 this.prevmatch = matched_text
350 this.prevmatch = matched_text
351 this.npressed = this.npressed+1;
351 this.npressed = this.npressed+1;
352 }
352 }
353 else
353 else
354 {
354 {
355 this.prevmatch = "";
355 this.prevmatch = "";
356 this.npressed = 0;
356 this.npressed = 0;
357 }
357 }
358 // end fallback on tooltip
358 // end fallback on tooltip
359 //==================================
359 //==================================
360 // Real completion logic start here
360 // Real completion logic start here
361 var that = this;
361 var that = this;
362 var cur = this.completion_cursor;
362 var cur = this.completion_cursor;
363 var done = false;
363 var done = false;
364
364
365 // call to dismmiss the completer
365 // call to dismmiss the completer
366 var close = function () {
366 var close = function () {
367 if (done) return;
367 if (done) return;
368 done = true;
368 done = true;
369 if (complete != undefined)
369 if (complete != undefined)
370 {complete.remove();}
370 {complete.remove();}
371 that.is_completing = false;
371 that.is_completing = false;
372 that.completion_cursor = null;
372 that.completion_cursor = null;
373 };
373 };
374
374
375 // update codemirror with the typed text
375 // update codemirror with the typed text
376 prev = matched_text
376 prev = matched_text
377 var update = function (inserted_text, event) {
377 var update = function (inserted_text, event) {
378 that.code_mirror.replaceRange(
378 that.code_mirror.replaceRange(
379 inserted_text,
379 inserted_text,
380 {line: cur.line, ch: (cur.ch-matched_text.length)},
380 {line: cur.line, ch: (cur.ch-matched_text.length)},
381 {line: cur.line, ch: (cur.ch+prev.length-matched_text.length)}
381 {line: cur.line, ch: (cur.ch+prev.length-matched_text.length)}
382 );
382 );
383 prev = inserted_text
383 prev = inserted_text
384 if(event != null){
384 if(event != null){
385 event.stopPropagation();
385 event.stopPropagation();
386 event.preventDefault();
386 event.preventDefault();
387 }
387 }
388 };
388 };
389 // insert the given text and exit the completer
389 // insert the given text and exit the completer
390 var insert = function (selected_text, event) {
390 var insert = function (selected_text, event) {
391 update(selected_text)
391 update(selected_text)
392 close();
392 close();
393 setTimeout(function(){that.code_mirror.focus();}, 50);
393 setTimeout(function(){that.code_mirror.focus();}, 50);
394 };
394 };
395
395
396 // insert the curent highlited selection and exit
396 // insert the curent highlited selection and exit
397 var pick = function () {
397 var pick = function () {
398 insert(select.val()[0],null);
398 insert(select.val()[0],null);
399 };
399 };
400
400
401
401
402 // Define function to clear the completer, refill it with the new
402 // Define function to clear the completer, refill it with the new
403 // matches, update the pseuso typing field. autopick insert match if
403 // matches, update the pseuso typing field. autopick insert match if
404 // only one left, in no matches (anymore) dismiss itself by pasting
404 // only one left, in no matches (anymore) dismiss itself by pasting
405 // what the user have typed until then
405 // what the user have typed until then
406 var complete_with = function(matches,typed_text,autopick,event)
406 var complete_with = function(matches,typed_text,autopick,event)
407 {
407 {
408 // If autopick an only one match, past.
408 // If autopick an only one match, past.
409 // Used to 'pick' when pressing tab
409 // Used to 'pick' when pressing tab
410 var prefix = '';
410 var prefix = '';
411 if(completing_to_magic && !completing_from_magic)
411 if(completing_to_magic && !completing_from_magic)
412 {
412 {
413 prefix='%';
413 prefix='%';
414 }
414 }
415 if (matches.length < 1) {
415 if (matches.length < 1) {
416 insert(prefix+typed_text,event);
416 insert(prefix+typed_text,event);
417 if(event != null){
417 if(event != null){
418 event.stopPropagation();
418 event.stopPropagation();
419 event.preventDefault();
419 event.preventDefault();
420 }
420 }
421 } else if (autopick && matches.length == 1) {
421 } else if (autopick && matches.length == 1) {
422 insert(matches[0],event);
422 insert(matches[0],event);
423 if(event != null){
423 if(event != null){
424 event.stopPropagation();
424 event.stopPropagation();
425 event.preventDefault();
425 event.preventDefault();
426 }
426 }
427 return;
427 return;
428 }
428 }
429 //clear the previous completion if any
429 //clear the previous completion if any
430 update(prefix+typed_text,event);
430 update(prefix+typed_text,event);
431 complete.children().children().remove();
431 complete.children().children().remove();
432 $('#asyoutype').html("<b>"+prefix+matched_text+"</b>"+typed_text.substr(matched_text.length));
432 $('#asyoutype').html("<b>"+prefix+matched_text+"</b>"+typed_text.substr(matched_text.length));
433 select = $('#asyoutypeselect');
433 select = $('#asyoutypeselect');
434 for (var i = 0; i<matches.length; ++i) {
434 for (var i = 0; i<matches.length; ++i) {
435 select.append($('<option/>').html(matches[i]));
435 select.append($('<option/>').html(matches[i]));
436 }
436 }
437 select.children().first().attr('selected','true');
437 select.children().first().attr('selected','true');
438 }
438 }
439
439
440 // create html for completer
440 // create html for completer
441 var complete = $('<div/>').addClass('completions');
441 var complete = $('<div/>').addClass('completions');
442 complete.attr('id','complete');
442 complete.attr('id','complete');
443 complete.append($('<p/>').attr('id', 'asyoutype').html('<b>fixed part</b>user part'));//pseudo input field
443 complete.append($('<p/>').attr('id', 'asyoutype').html('<b>fixed part</b>user part'));//pseudo input field
444
444
445 var select = $('<select/>').attr('multiple','true');
445 var select = $('<select/>').attr('multiple','true');
446 select.attr('id', 'asyoutypeselect')
446 select.attr('id', 'asyoutypeselect')
447 select.attr('size',Math.min(10,matches.length));
447 select.attr('size',Math.min(10,matches.length));
448 var pos = this.code_mirror.cursorCoords();
448 var pos = this.code_mirror.cursorCoords();
449
449
450 // TODO: I propose to remove enough horizontal pixel
450 // TODO: I propose to remove enough horizontal pixel
451 // to align the text later
451 // to align the text later
452 complete.css('left',pos.x+'px');
452 complete.css('left',pos.x+'px');
453 complete.css('top',pos.yBot+'px');
453 complete.css('top',pos.yBot+'px');
454 complete.append(select);
454 complete.append(select);
455
455
456 $('body').append(complete);
456 $('body').append(complete);
457
457
458 // So a first actual completion. see if all the completion start wit
458 // So a first actual completion. see if all the completion start wit
459 // the same letter and complete if necessary
459 // the same letter and complete if necessary
460 ff = sharedStart(matches)
460 ff = sharedStart(matches)
461 fastForward = ff[0];
461 fastForward = ff[0];
462 completing_to_magic = ff[1];
462 completing_to_magic = ff[1];
463 typed_characters = fastForward.substr(matched_text.length);
463 typed_characters = fastForward.substr(matched_text.length);
464 complete_with(matches,matched_text+typed_characters,true,null);
464 complete_with(matches,matched_text+typed_characters,true,null);
465 filterd = matches;
465 filterd = matches;
466 // Give focus to select, and make it filter the match as the user type
466 // Give focus to select, and make it filter the match as the user type
467 // by filtering the previous matches. Called by .keypress and .keydown
467 // by filtering the previous matches. Called by .keypress and .keydown
468 var downandpress = function (event,press_or_down) {
468 var downandpress = function (event,press_or_down) {
469 var code = event.which;
469 var code = event.which;
470 var autopick = false; // auto 'pick' if only one match
470 var autopick = false; // auto 'pick' if only one match
471 if (press_or_down === 0){
471 if (press_or_down === 0){
472 press = true; down = false; //Are we called from keypress or keydown
472 press = true; down = false; //Are we called from keypress or keydown
473 } else if (press_or_down == 1){
473 } else if (press_or_down == 1){
474 press = false; down = true;
474 press = false; down = true;
475 }
475 }
476 if (code === key.shift) {
476 if (code === key.shift) {
477 // nothing on Shift
477 // nothing on Shift
478 return;
478 return;
479 }
479 }
480 if (key.dismissAndAppend(code) && press) {
480 if (key.dismissAndAppend(code) && press) {
481 var newchar = String.fromCharCode(code);
481 var newchar = String.fromCharCode(code);
482 typed_characters = typed_characters+newchar;
482 typed_characters = typed_characters+newchar;
483 insert(matched_text+typed_characters,event);
483 insert(matched_text+typed_characters,event);
484 return
484 return
485 }
485 }
486 if (code === key.enter) {
486 if (code === key.enter) {
487 // Pressing ENTER will cause a pick
487 // Pressing ENTER will cause a pick
488 event.stopPropagation();
488 event.stopPropagation();
489 event.preventDefault();
489 event.preventDefault();
490 pick();
490 pick();
491 } else if (code === 38 || code === 40) {
491 } else if (code === 38 || code === 40) {
492 // We don't want the document keydown handler to handle UP/DOWN,
492 // We don't want the document keydown handler to handle UP/DOWN,
493 // but we want the default action.
493 // but we want the default action.
494 event.stopPropagation();
494 event.stopPropagation();
495 } else if ( (code == key.backspace)||(code == key.tab && down) || press || key.isCompSymbol(code)){
495 } else if ( (code == key.backspace)||(code == key.tab && down) || press || key.isCompSymbol(code)){
496 if( key.isCompSymbol(code) && press)
496 if( key.isCompSymbol(code) && press)
497 {
497 {
498 var newchar = String.fromCharCode(code);
498 var newchar = String.fromCharCode(code);
499 typed_characters = typed_characters+newchar;
499 typed_characters = typed_characters+newchar;
500 } else if (code == key.tab) {
500 } else if (code == key.tab) {
501 ff = sharedStart(matches)
501 ff = sharedStart(matches)
502 fastForward = ff[0];
502 fastForward = ff[0];
503 completing_to_magic = ff[1];
503 completing_to_magic = ff[1];
504 ffsub = fastForward.substr(matched_text.length+typed_characters.length);
504 ffsub = fastForward.substr(matched_text.length+typed_characters.length);
505 typed_characters = typed_characters+ffsub;
505 typed_characters = typed_characters+ffsub;
506 autopick = true;
506 autopick = true;
507 } else if (code == key.backspace && down) {
507 } else if (code == key.backspace && down) {
508 // cancel if user have erase everything, otherwise decrease
508 // cancel if user have erase everything, otherwise decrease
509 // what we filter with
509 // what we filter with
510 event.preventDefault();
510 event.preventDefault();
511 if (typed_characters.length <= 0)
511 if (typed_characters.length <= 0)
512 {
512 {
513 insert(matched_text,event)
513 insert(matched_text,event)
514 return
514 return
515 }
515 }
516 typed_characters = typed_characters.substr(0,typed_characters.length-1);
516 typed_characters = typed_characters.substr(0,typed_characters.length-1);
517 } else if (press && code != key.backspace && code != key.tab && code != 0){
517 } else if (press && code != key.backspace && code != key.tab && code != 0){
518 insert(matched_text+typed_characters,event);
518 insert(matched_text+typed_characters,event);
519 return
519 return
520 } else {
520 } else {
521 return
521 return
522 }
522 }
523 re = new RegExp("^"+"\%?"+matched_text+typed_characters,"");
523 re = new RegExp("^"+"\%?"+matched_text+typed_characters,"");
524 filterd = matches.filter(function(x){return re.test(x)});
524 filterd = matches.filter(function(x){return re.test(x)});
525 ff = sharedStart(filterd);
525 ff = sharedStart(filterd);
526 completing_to_magic = ff[1];
526 completing_to_magic = ff[1];
527 complete_with(filterd,matched_text+typed_characters,autopick,event);
527 complete_with(filterd,matched_text+typed_characters,autopick,event);
528 } else if (code == key.esc) {
528 } else if (code == key.esc) {
529 // dismiss the completer and go back to before invoking it
529 // dismiss the completer and go back to before invoking it
530 insert(matched_text,event);
530 insert(matched_text,event);
531 } else if (press) { // abort only on .keypress or esc
531 } else if (press) { // abort only on .keypress or esc
532 }
532 }
533 }
533 }
534 select.keydown(function (event) {
534 select.keydown(function (event) {
535 downandpress(event,1)
535 downandpress(event,1)
536 });
536 });
537 select.keypress(function (event) {
537 select.keypress(function (event) {
538 downandpress(event,0)
538 downandpress(event,0)
539 });
539 });
540 // Double click also causes a pick.
540 // Double click also causes a pick.
541 // and bind the last actions.
541 // and bind the last actions.
542 select.dblclick(pick);
542 select.dblclick(pick);
543 select.blur(close);
543 select.blur(close);
544 select.focus();
544 select.focus();
545 };
545 };
546
546
547
547
548 CodeCell.prototype.select = function () {
548 CodeCell.prototype.select = function () {
549 IPython.Cell.prototype.select.apply(this);
549 IPython.Cell.prototype.select.apply(this);
550 this.code_mirror.refresh();
550 this.code_mirror.refresh();
551 this.code_mirror.focus();
551 this.code_mirror.focus();
552 // We used to need an additional refresh() after the focus, but
552 // We used to need an additional refresh() after the focus, but
553 // it appears that this has been fixed in CM. This bug would show
553 // it appears that this has been fixed in CM. This bug would show
554 // up on FF when a newly loaded markdown cell was edited.
554 // up on FF when a newly loaded markdown cell was edited.
555 };
555 };
556
556
557
557
558 CodeCell.prototype.select_all = function () {
558 CodeCell.prototype.select_all = function () {
559 var start = {line: 0, ch: 0};
559 var start = {line: 0, ch: 0};
560 var nlines = this.code_mirror.lineCount();
560 var nlines = this.code_mirror.lineCount();
561 var last_line = this.code_mirror.getLine(nlines-1);
561 var last_line = this.code_mirror.getLine(nlines-1);
562 var end = {line: nlines-1, ch: last_line.length};
562 var end = {line: nlines-1, ch: last_line.length};
563 this.code_mirror.setSelection(start, end);
563 this.code_mirror.setSelection(start, end);
564 };
564 };
565
565
566
566
567 CodeCell.prototype.append_output = function (json, dynamic) {
567 CodeCell.prototype.append_output = function (json, dynamic) {
568 // If dynamic is true, javascript output will be eval'd.
568 // If dynamic is true, javascript output will be eval'd.
569 this.expand();
569 this.expand();
570 this.flush_clear_timeout();
570 this.flush_clear_timeout();
571 if (json.output_type === 'pyout') {
571 if (json.output_type === 'pyout') {
572 this.append_pyout(json, dynamic);
572 this.append_pyout(json, dynamic);
573 } else if (json.output_type === 'pyerr') {
573 } else if (json.output_type === 'pyerr') {
574 this.append_pyerr(json);
574 this.append_pyerr(json);
575 } else if (json.output_type === 'display_data') {
575 } else if (json.output_type === 'display_data') {
576 this.append_display_data(json, dynamic);
576 this.append_display_data(json, dynamic);
577 } else if (json.output_type === 'stream') {
577 } else if (json.output_type === 'stream') {
578 this.append_stream(json);
578 this.append_stream(json);
579 };
579 };
580 this.outputs.push(json);
580 this.outputs.push(json);
581 };
581 };
582
582
583
583
584 CodeCell.prototype.create_output_area = function () {
584 CodeCell.prototype.create_output_area = function () {
585 var oa = $("<div/>").addClass("hbox output_area");
585 var oa = $("<div/>").addClass("hbox output_area");
586 oa.append($('<div/>').addClass('prompt'));
586 oa.append($('<div/>').addClass('prompt'));
587 return oa;
587 return oa;
588 };
588 };
589
589
590
590
591 CodeCell.prototype.append_pyout = function (json, dynamic) {
591 CodeCell.prototype.append_pyout = function (json, dynamic) {
592 n = json.prompt_number || ' ';
592 n = json.prompt_number || ' ';
593 var toinsert = this.create_output_area();
593 var toinsert = this.create_output_area();
594 toinsert.find('div.prompt').addClass('output_prompt').html('Out[' + n + ']:');
594 toinsert.find('div.prompt').addClass('output_prompt').html('Out[' + n + ']:');
595 this.append_mime_type(json, toinsert, dynamic);
595 this.append_mime_type(json, toinsert, dynamic);
596 this.element.find('div.output').append(toinsert);
596 this.element.find('div.output').append(toinsert);
597 // If we just output latex, typeset it.
597 // If we just output latex, typeset it.
598 if ((json.latex !== undefined) || (json.html !== undefined)) {
598 if ((json.latex !== undefined) || (json.html !== undefined)) {
599 this.typeset();
599 this.typeset();
600 };
600 };
601 };
601 };
602
602
603
603
604 CodeCell.prototype.append_pyerr = function (json) {
604 CodeCell.prototype.append_pyerr = function (json) {
605 var tb = json.traceback;
605 var tb = json.traceback;
606 if (tb !== undefined && tb.length > 0) {
606 if (tb !== undefined && tb.length > 0) {
607 var s = '';
607 var s = '';
608 var len = tb.length;
608 var len = tb.length;
609 for (var i=0; i<len; i++) {
609 for (var i=0; i<len; i++) {
610 s = s + tb[i] + '\n';
610 s = s + tb[i] + '\n';
611 }
611 }
612 s = s + '\n';
612 s = s + '\n';
613 var toinsert = this.create_output_area();
613 var toinsert = this.create_output_area();
614 this.append_text(s, toinsert);
614 this.append_text(s, toinsert);
615 this.element.find('div.output').append(toinsert);
615 this.element.find('div.output').append(toinsert);
616 };
616 };
617 };
617 };
618
618
619
619
620 CodeCell.prototype.append_stream = function (json) {
620 CodeCell.prototype.append_stream = function (json) {
621 // temporary fix: if stream undefined (json file written prior to this patch),
621 // temporary fix: if stream undefined (json file written prior to this patch),
622 // default to most likely stdout:
622 // default to most likely stdout:
623 if (json.stream == undefined){
623 if (json.stream == undefined){
624 json.stream = 'stdout';
624 json.stream = 'stdout';
625 }
625 }
626 if (!utils.fixConsole(json.text)){
626 if (!utils.fixConsole(json.text)){
627 // fixConsole gives nothing (empty string, \r, etc.)
627 // fixConsole gives nothing (empty string, \r, etc.)
628 // so don't append any elements, which might add undesirable space
628 // so don't append any elements, which might add undesirable space
629 return;
629 return;
630 }
630 }
631 var subclass = "output_"+json.stream;
631 var subclass = "output_"+json.stream;
632 if (this.outputs.length > 0){
632 if (this.outputs.length > 0){
633 // have at least one output to consider
633 // have at least one output to consider
634 var last = this.outputs[this.outputs.length-1];
634 var last = this.outputs[this.outputs.length-1];
635 if (last.output_type == 'stream' && json.stream == last.stream){
635 if (last.output_type == 'stream' && json.stream == last.stream){
636 // latest output was in the same stream,
636 // latest output was in the same stream,
637 // so append directly into its pre tag
637 // so append directly into its pre tag
638 // escape ANSI & HTML specials:
638 // escape ANSI & HTML specials:
639 var text = utils.fixConsole(json.text);
639 var text = utils.fixConsole(json.text);
640 this.element.find('div.'+subclass).last().find('pre').append(text);
640 this.element.find('div.'+subclass).last().find('pre').append(text);
641 return;
641 return;
642 }
642 }
643 }
643 }
644
644
645 // If we got here, attach a new div
645 // If we got here, attach a new div
646 var toinsert = this.create_output_area();
646 var toinsert = this.create_output_area();
647 this.append_text(json.text, toinsert, "output_stream "+subclass);
647 this.append_text(json.text, toinsert, "output_stream "+subclass);
648 this.element.find('div.output').append(toinsert);
648 this.element.find('div.output').append(toinsert);
649 };
649 };
650
650
651
651
652 CodeCell.prototype.append_display_data = function (json, dynamic) {
652 CodeCell.prototype.append_display_data = function (json, dynamic) {
653 var toinsert = this.create_output_area();
653 var toinsert = this.create_output_area();
654 this.append_mime_type(json, toinsert, dynamic);
654 this.append_mime_type(json, toinsert, dynamic);
655 this.element.find('div.output').append(toinsert);
655 this.element.find('div.output').append(toinsert);
656 // If we just output latex, typeset it.
656 // If we just output latex, typeset it.
657 if ( (json.latex !== undefined) || (json.html !== undefined) ) {
657 if ( (json.latex !== undefined) || (json.html !== undefined) ) {
658 this.typeset();
658 this.typeset();
659 };
659 };
660 };
660 };
661
661
662
662
663 CodeCell.prototype.append_mime_type = function (json, element, dynamic) {
663 CodeCell.prototype.append_mime_type = function (json, element, dynamic) {
664 if (json.javascript !== undefined && dynamic) {
664 if (json.javascript !== undefined && dynamic) {
665 this.append_javascript(json.javascript, element, dynamic);
665 this.append_javascript(json.javascript, element, dynamic);
666 } else if (json.html !== undefined) {
666 } else if (json.html !== undefined) {
667 this.append_html(json.html, element);
667 this.append_html(json.html, element);
668 } else if (json.latex !== undefined) {
668 } else if (json.latex !== undefined) {
669 this.append_latex(json.latex, element);
669 this.append_latex(json.latex, element);
670 } else if (json.svg !== undefined) {
670 } else if (json.svg !== undefined) {
671 this.append_svg(json.svg, element);
671 this.append_svg(json.svg, element);
672 } else if (json.png !== undefined) {
672 } else if (json.png !== undefined) {
673 this.append_png(json.png, element);
673 this.append_png(json.png, element);
674 } else if (json.jpeg !== undefined) {
674 } else if (json.jpeg !== undefined) {
675 this.append_jpeg(json.jpeg, element);
675 this.append_jpeg(json.jpeg, element);
676 } else if (json.text !== undefined) {
676 } else if (json.text !== undefined) {
677 this.append_text(json.text, element);
677 this.append_text(json.text, element);
678 };
678 };
679 };
679 };
680
680
681
681
682 CodeCell.prototype.append_html = function (html, element) {
682 CodeCell.prototype.append_html = function (html, element) {
683 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_html rendered_html");
683 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_html rendered_html");
684 toinsert.append(html);
684 toinsert.append(html);
685 element.append(toinsert);
685 element.append(toinsert);
686 };
686 };
687
687
688
688
689 CodeCell.prototype.append_javascript = function (js, e) {
689 CodeCell.prototype.append_javascript = function (js, container) {
690 // We just eval the JS code, element appears in the local scope.
690 // We just eval the JS code, element appears in the local scope.
691 var element = $("<div/>").addClass("box_flex1 output_subarea");
691 var element = $("<div/>").addClass("box_flex1 output_subarea");
692 e.append(element);
692 container.append(element);
693 // Div for js shouldn't be drawn, as it will add empty height to the area.
693 // Div for js shouldn't be drawn, as it will add empty height to the area.
694 e.hide();
694 container.hide();
695
695 // If the Javascript appends content to `element` that should be drawn, then
696 // it must also call `container.show()`.
696 eval(js);
697 eval(js);
697 }
698 }
698
699
699
700
700 CodeCell.prototype.append_text = function (data, element, extra_class) {
701 CodeCell.prototype.append_text = function (data, element, extra_class) {
701 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_text");
702 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_text");
702 // escape ANSI & HTML specials in plaintext:
703 // escape ANSI & HTML specials in plaintext:
703 data = utils.fixConsole(data);
704 data = utils.fixConsole(data);
704 if (extra_class){
705 if (extra_class){
705 toinsert.addClass(extra_class);
706 toinsert.addClass(extra_class);
706 }
707 }
707 toinsert.append($("<pre/>").html(data));
708 toinsert.append($("<pre/>").html(data));
708 element.append(toinsert);
709 element.append(toinsert);
709 };
710 };
710
711
711
712
712 CodeCell.prototype.append_svg = function (svg, element) {
713 CodeCell.prototype.append_svg = function (svg, element) {
713 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_svg");
714 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_svg");
714 toinsert.append(svg);
715 toinsert.append(svg);
715 element.append(toinsert);
716 element.append(toinsert);
716 };
717 };
717
718
718
719
719 CodeCell.prototype.append_png = function (png, element) {
720 CodeCell.prototype.append_png = function (png, element) {
720 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_png");
721 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_png");
721 toinsert.append($("<img/>").attr('src','data:image/png;base64,'+png));
722 toinsert.append($("<img/>").attr('src','data:image/png;base64,'+png));
722 element.append(toinsert);
723 element.append(toinsert);
723 };
724 };
724
725
725
726
726 CodeCell.prototype.append_jpeg = function (jpeg, element) {
727 CodeCell.prototype.append_jpeg = function (jpeg, element) {
727 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_jpeg");
728 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_jpeg");
728 toinsert.append($("<img/>").attr('src','data:image/jpeg;base64,'+jpeg));
729 toinsert.append($("<img/>").attr('src','data:image/jpeg;base64,'+jpeg));
729 element.append(toinsert);
730 element.append(toinsert);
730 };
731 };
731
732
732
733
733 CodeCell.prototype.append_latex = function (latex, element) {
734 CodeCell.prototype.append_latex = function (latex, element) {
734 // This method cannot do the typesetting because the latex first has to
735 // This method cannot do the typesetting because the latex first has to
735 // be on the page.
736 // be on the page.
736 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_latex");
737 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_latex");
737 toinsert.append(latex);
738 toinsert.append(latex);
738 element.append(toinsert);
739 element.append(toinsert);
739 };
740 };
740
741
741
742
742 CodeCell.prototype.clear_output = function (stdout, stderr, other) {
743 CodeCell.prototype.clear_output = function (stdout, stderr, other) {
743 var that = this;
744 var that = this;
744 if (this.clear_out_timeout != null){
745 if (this.clear_out_timeout != null){
745 // fire previous pending clear *immediately*
746 // fire previous pending clear *immediately*
746 clearTimeout(this.clear_out_timeout);
747 clearTimeout(this.clear_out_timeout);
747 this.clear_out_timeout = null;
748 this.clear_out_timeout = null;
748 this.clear_output_callback(this._clear_stdout, this._clear_stderr, this._clear_other);
749 this.clear_output_callback(this._clear_stdout, this._clear_stderr, this._clear_other);
749 }
750 }
750 // store flags for flushing the timeout
751 // store flags for flushing the timeout
751 this._clear_stdout = stdout;
752 this._clear_stdout = stdout;
752 this._clear_stderr = stderr;
753 this._clear_stderr = stderr;
753 this._clear_other = other;
754 this._clear_other = other;
754 this.clear_out_timeout = setTimeout(function(){
755 this.clear_out_timeout = setTimeout(function(){
755 // really clear timeout only after a short delay
756 // really clear timeout only after a short delay
756 // this reduces flicker in 'clear_output; print' cases
757 // this reduces flicker in 'clear_output; print' cases
757 that.clear_out_timeout = null;
758 that.clear_out_timeout = null;
758 that._clear_stdout = that._clear_stderr = that._clear_other = null;
759 that._clear_stdout = that._clear_stderr = that._clear_other = null;
759 that.clear_output_callback(stdout, stderr, other);
760 that.clear_output_callback(stdout, stderr, other);
760 }, 500
761 }, 500
761 );
762 );
762 };
763 };
763
764
764 CodeCell.prototype.clear_output_callback = function (stdout, stderr, other) {
765 CodeCell.prototype.clear_output_callback = function (stdout, stderr, other) {
765 var output_div = this.element.find("div.output");
766 var output_div = this.element.find("div.output");
766
767
767 if (stdout && stderr && other){
768 if (stdout && stderr && other){
768 // clear all, no need for logic
769 // clear all, no need for logic
769 output_div.html("");
770 output_div.html("");
770 this.outputs = [];
771 this.outputs = [];
771 return;
772 return;
772 }
773 }
773 // remove html output
774 // remove html output
774 // each output_subarea that has an identifying class is in an output_area
775 // each output_subarea that has an identifying class is in an output_area
775 // which is the element to be removed.
776 // which is the element to be removed.
776 if (stdout){
777 if (stdout){
777 output_div.find("div.output_stdout").parent().remove();
778 output_div.find("div.output_stdout").parent().remove();
778 }
779 }
779 if (stderr){
780 if (stderr){
780 output_div.find("div.output_stderr").parent().remove();
781 output_div.find("div.output_stderr").parent().remove();
781 }
782 }
782 if (other){
783 if (other){
783 output_div.find("div.output_subarea").not("div.output_stderr").not("div.output_stdout").parent().remove();
784 output_div.find("div.output_subarea").not("div.output_stderr").not("div.output_stdout").parent().remove();
784 }
785 }
785
786
786 // remove cleared outputs from JSON list:
787 // remove cleared outputs from JSON list:
787 for (var i = this.outputs.length - 1; i >= 0; i--){
788 for (var i = this.outputs.length - 1; i >= 0; i--){
788 var out = this.outputs[i];
789 var out = this.outputs[i];
789 var output_type = out.output_type;
790 var output_type = out.output_type;
790 if (output_type == "display_data" && other){
791 if (output_type == "display_data" && other){
791 this.outputs.splice(i,1);
792 this.outputs.splice(i,1);
792 }else if (output_type == "stream"){
793 }else if (output_type == "stream"){
793 if (stdout && out.stream == "stdout"){
794 if (stdout && out.stream == "stdout"){
794 this.outputs.splice(i,1);
795 this.outputs.splice(i,1);
795 }else if (stderr && out.stream == "stderr"){
796 }else if (stderr && out.stream == "stderr"){
796 this.outputs.splice(i,1);
797 this.outputs.splice(i,1);
797 }
798 }
798 }
799 }
799 }
800 }
800 };
801 };
801
802
802
803
803 CodeCell.prototype.clear_input = function () {
804 CodeCell.prototype.clear_input = function () {
804 this.code_mirror.setValue('');
805 this.code_mirror.setValue('');
805 };
806 };
806
807
807 CodeCell.prototype.flush_clear_timeout = function() {
808 CodeCell.prototype.flush_clear_timeout = function() {
808 var output_div = this.element.find('div.output');
809 var output_div = this.element.find('div.output');
809 if (this.clear_out_timeout){
810 if (this.clear_out_timeout){
810 clearTimeout(this.clear_out_timeout);
811 clearTimeout(this.clear_out_timeout);
811 this.clear_out_timeout = null;
812 this.clear_out_timeout = null;
812 this.clear_output_callback(this._clear_stdout, this._clear_stderr, this._clear_other);
813 this.clear_output_callback(this._clear_stdout, this._clear_stderr, this._clear_other);
813 };
814 };
814 }
815 }
815
816
816
817
817 CodeCell.prototype.collapse = function () {
818 CodeCell.prototype.collapse = function () {
818 if (!this.collapsed) {
819 if (!this.collapsed) {
819 this.element.find('div.output').hide();
820 this.element.find('div.output').hide();
820 this.collapsed = true;
821 this.collapsed = true;
821 };
822 };
822 };
823 };
823
824
824
825
825 CodeCell.prototype.expand = function () {
826 CodeCell.prototype.expand = function () {
826 if (this.collapsed) {
827 if (this.collapsed) {
827 this.element.find('div.output').show();
828 this.element.find('div.output').show();
828 this.collapsed = false;
829 this.collapsed = false;
829 };
830 };
830 };
831 };
831
832
832
833
833 CodeCell.prototype.toggle_output = function () {
834 CodeCell.prototype.toggle_output = function () {
834 if (this.collapsed) {
835 if (this.collapsed) {
835 this.expand();
836 this.expand();
836 } else {
837 } else {
837 this.collapse();
838 this.collapse();
838 };
839 };
839 };
840 };
840
841
841 CodeCell.prototype.set_input_prompt = function (number) {
842 CodeCell.prototype.set_input_prompt = function (number) {
842 this.input_prompt_number = number;
843 this.input_prompt_number = number;
843 var ns = number || "&nbsp;";
844 var ns = number || "&nbsp;";
844 this.element.find('div.input_prompt').html('In&nbsp;[' + ns + ']:');
845 this.element.find('div.input_prompt').html('In&nbsp;[' + ns + ']:');
845 };
846 };
846
847
847
848
848 CodeCell.prototype.get_text = function () {
849 CodeCell.prototype.get_text = function () {
849 return this.code_mirror.getValue();
850 return this.code_mirror.getValue();
850 };
851 };
851
852
852
853
853 CodeCell.prototype.set_text = function (code) {
854 CodeCell.prototype.set_text = function (code) {
854 return this.code_mirror.setValue(code);
855 return this.code_mirror.setValue(code);
855 };
856 };
856
857
857
858
858 CodeCell.prototype.at_top = function () {
859 CodeCell.prototype.at_top = function () {
859 var cursor = this.code_mirror.getCursor();
860 var cursor = this.code_mirror.getCursor();
860 if (cursor.line === 0) {
861 if (cursor.line === 0) {
861 return true;
862 return true;
862 } else {
863 } else {
863 return false;
864 return false;
864 }
865 }
865 };
866 };
866
867
867
868
868 CodeCell.prototype.at_bottom = function () {
869 CodeCell.prototype.at_bottom = function () {
869 var cursor = this.code_mirror.getCursor();
870 var cursor = this.code_mirror.getCursor();
870 if (cursor.line === (this.code_mirror.lineCount()-1)) {
871 if (cursor.line === (this.code_mirror.lineCount()-1)) {
871 return true;
872 return true;
872 } else {
873 } else {
873 return false;
874 return false;
874 }
875 }
875 };
876 };
876
877
877
878
878 CodeCell.prototype.fromJSON = function (data) {
879 CodeCell.prototype.fromJSON = function (data) {
879 if (data.cell_type === 'code') {
880 if (data.cell_type === 'code') {
880 if (data.input !== undefined) {
881 if (data.input !== undefined) {
881 this.set_text(data.input);
882 this.set_text(data.input);
882 }
883 }
883 if (data.prompt_number !== undefined) {
884 if (data.prompt_number !== undefined) {
884 this.set_input_prompt(data.prompt_number);
885 this.set_input_prompt(data.prompt_number);
885 } else {
886 } else {
886 this.set_input_prompt();
887 this.set_input_prompt();
887 };
888 };
888 var len = data.outputs.length;
889 var len = data.outputs.length;
889 for (var i=0; i<len; i++) {
890 for (var i=0; i<len; i++) {
890 // append with dynamic=false.
891 // append with dynamic=false.
891 this.append_output(data.outputs[i], false);
892 this.append_output(data.outputs[i], false);
892 };
893 };
893 if (data.collapsed !== undefined) {
894 if (data.collapsed !== undefined) {
894 if (data.collapsed) {
895 if (data.collapsed) {
895 this.collapse();
896 this.collapse();
896 } else {
897 } else {
897 this.expand();
898 this.expand();
898 };
899 };
899 };
900 };
900 };
901 };
901 };
902 };
902
903
903
904
904 CodeCell.prototype.toJSON = function () {
905 CodeCell.prototype.toJSON = function () {
905 var data = {};
906 var data = {};
906 data.input = this.get_text();
907 data.input = this.get_text();
907 data.cell_type = 'code';
908 data.cell_type = 'code';
908 if (this.input_prompt_number) {
909 if (this.input_prompt_number) {
909 data.prompt_number = this.input_prompt_number;
910 data.prompt_number = this.input_prompt_number;
910 };
911 };
911 var outputs = [];
912 var outputs = [];
912 var len = this.outputs.length;
913 var len = this.outputs.length;
913 for (var i=0; i<len; i++) {
914 for (var i=0; i<len; i++) {
914 outputs[i] = this.outputs[i];
915 outputs[i] = this.outputs[i];
915 };
916 };
916 data.outputs = outputs;
917 data.outputs = outputs;
917 data.language = 'python';
918 data.language = 'python';
918 data.collapsed = this.collapsed;
919 data.collapsed = this.collapsed;
919 return data;
920 return data;
920 };
921 };
921
922
922
923
923 IPython.CodeCell = CodeCell;
924 IPython.CodeCell = CodeCell;
924
925
925 return IPython;
926 return IPython;
926 }(IPython));
927 }(IPython));
General Comments 0
You need to be logged in to leave comments. Login now