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