Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Avatar for KuCalc : devops.
Download
50654 views
1
// Copyright (c) IPython Development Team.
2
// Distributed under the terms of the Modified BSD License.
3
4
define([
5
'base/js/namespace',
6
'jqueryui',
7
'base/js/utils',
8
'base/js/security',
9
'base/js/keyboard',
10
'notebook/js/mathjaxutils',
11
'components/marked/lib/marked',
12
], function(IPython, $, utils, security, keyboard, mathjaxutils, marked) {
13
"use strict";
14
15
/**
16
* @class OutputArea
17
*
18
* @constructor
19
*/
20
21
var OutputArea = function (options) {
22
this.selector = options.selector;
23
this.events = options.events;
24
this.keyboard_manager = options.keyboard_manager;
25
this.wrapper = $(options.selector);
26
this.outputs = [];
27
this.collapsed = false;
28
this.scrolled = false;
29
this.scroll_state = 'auto';
30
this.trusted = true;
31
this.clear_queued = null;
32
if (options.prompt_area === undefined) {
33
this.prompt_area = true;
34
} else {
35
this.prompt_area = options.prompt_area;
36
}
37
this.create_elements();
38
this.style();
39
this.bind_events();
40
};
41
42
43
/**
44
* Class prototypes
45
**/
46
47
OutputArea.prototype.create_elements = function () {
48
this.element = $("<div/>");
49
this.collapse_button = $("<div/>");
50
this.prompt_overlay = $("<div/>");
51
this.wrapper.append(this.prompt_overlay);
52
this.wrapper.append(this.element);
53
this.wrapper.append(this.collapse_button);
54
};
55
56
57
OutputArea.prototype.style = function () {
58
this.collapse_button.hide();
59
this.prompt_overlay.hide();
60
61
this.wrapper.addClass('output_wrapper');
62
this.element.addClass('output');
63
64
this.collapse_button.addClass("btn btn-default output_collapsed");
65
this.collapse_button.attr('title', 'click to expand output');
66
this.collapse_button.text('. . .');
67
68
this.prompt_overlay.addClass('out_prompt_overlay prompt');
69
this.prompt_overlay.attr('title', 'click to expand output; double click to hide output');
70
71
this.collapse();
72
};
73
74
/**
75
* Should the OutputArea scroll?
76
* Returns whether the height (in lines) exceeds the current threshold.
77
* Threshold will be OutputArea.minimum_scroll_threshold if scroll_state=true (manually requested)
78
* or OutputArea.auto_scroll_threshold if scroll_state='auto'.
79
* This will always return false if scroll_state=false (scroll disabled).
80
*
81
*/
82
OutputArea.prototype._should_scroll = function () {
83
var threshold;
84
if (this.scroll_state === false) {
85
return false;
86
} else if (this.scroll_state === true) {
87
threshold = OutputArea.minimum_scroll_threshold;
88
} else {
89
threshold = OutputArea.auto_scroll_threshold;
90
}
91
if (threshold <=0) {
92
return false;
93
}
94
// line-height from http://stackoverflow.com/questions/1185151
95
var fontSize = this.element.css('font-size');
96
var lineHeight = Math.floor(parseInt(fontSize.replace('px','')) * 1.5);
97
return (this.element.height() > threshold * lineHeight);
98
};
99
100
101
OutputArea.prototype.bind_events = function () {
102
var that = this;
103
this.prompt_overlay.dblclick(function () { that.toggle_output(); });
104
this.prompt_overlay.click(function () { that.toggle_scroll(); });
105
106
this.element.resize(function () {
107
// FIXME: Firefox on Linux misbehaves, so automatic scrolling is disabled
108
if ( utils.browser[0] === "Firefox" ) {
109
return;
110
}
111
// maybe scroll output,
112
// if it's grown large enough and hasn't already been scrolled.
113
if (!that.scrolled && that._should_scroll()) {
114
that.scroll_area();
115
}
116
});
117
this.collapse_button.click(function () {
118
that.expand();
119
});
120
};
121
122
123
OutputArea.prototype.collapse = function () {
124
if (!this.collapsed) {
125
this.element.hide();
126
this.prompt_overlay.hide();
127
if (this.element.html()){
128
this.collapse_button.show();
129
}
130
this.collapsed = true;
131
// collapsing output clears scroll state
132
this.scroll_state = 'auto';
133
}
134
};
135
136
137
OutputArea.prototype.expand = function () {
138
if (this.collapsed) {
139
this.collapse_button.hide();
140
this.element.show();
141
if (this.prompt_area) {
142
this.prompt_overlay.show();
143
}
144
this.collapsed = false;
145
this.scroll_if_long();
146
}
147
};
148
149
150
OutputArea.prototype.toggle_output = function () {
151
if (this.collapsed) {
152
this.expand();
153
} else {
154
this.collapse();
155
}
156
};
157
158
159
OutputArea.prototype.scroll_area = function () {
160
this.element.addClass('output_scroll');
161
this.prompt_overlay.attr('title', 'click to unscroll output; double click to hide');
162
this.scrolled = true;
163
};
164
165
166
OutputArea.prototype.unscroll_area = function () {
167
this.element.removeClass('output_scroll');
168
this.prompt_overlay.attr('title', 'click to scroll output; double click to hide');
169
this.scrolled = false;
170
};
171
172
/**
173
* Scroll OutputArea if height exceeds a threshold.
174
*
175
* Threshold is OutputArea.minimum_scroll_threshold if scroll_state = true,
176
* OutputArea.auto_scroll_threshold if scroll_state='auto'.
177
*
178
**/
179
OutputArea.prototype.scroll_if_long = function () {
180
var should_scroll = this._should_scroll();
181
if (!this.scrolled && should_scroll) {
182
// only allow scrolling long-enough output
183
this.scroll_area();
184
} else if (this.scrolled && !should_scroll) {
185
// scrolled and shouldn't be
186
this.unscroll_area();
187
}
188
};
189
190
191
OutputArea.prototype.toggle_scroll = function () {
192
if (this.scroll_state == 'auto') {
193
this.scroll_state = !this.scrolled;
194
} else {
195
this.scroll_state = !this.scroll_state;
196
}
197
if (this.scrolled) {
198
this.unscroll_area();
199
} else {
200
// only allow scrolling long-enough output
201
this.scroll_if_long();
202
}
203
};
204
205
206
// typeset with MathJax if MathJax is available
207
OutputArea.prototype.typeset = function () {
208
utils.typeset(this.element);
209
};
210
211
212
OutputArea.prototype.handle_output = function (msg) {
213
var json = {};
214
var msg_type = json.output_type = msg.header.msg_type;
215
var content = msg.content;
216
if (msg_type === "stream") {
217
json.text = content.text;
218
json.name = content.name;
219
} else if (msg_type === "display_data") {
220
json.data = content.data;
221
json.metadata = content.metadata;
222
} else if (msg_type === "execute_result") {
223
json.data = content.data;
224
json.metadata = content.metadata;
225
json.execution_count = content.execution_count;
226
} else if (msg_type === "error") {
227
json.ename = content.ename;
228
json.evalue = content.evalue;
229
json.traceback = content.traceback;
230
} else {
231
console.log("unhandled output message", msg);
232
return;
233
}
234
this.append_output(json);
235
};
236
237
238
OutputArea.output_types = [
239
'application/javascript',
240
'text/html',
241
'text/markdown',
242
'text/latex',
243
'image/svg+xml',
244
'image/png',
245
'image/jpeg',
246
'application/pdf',
247
'text/plain'
248
];
249
250
OutputArea.prototype.validate_mimebundle = function (bundle) {
251
/** scrub invalid outputs */
252
if (typeof bundle.data !== 'object') {
253
console.warn("mimebundle missing data", bundle);
254
bundle.data = {};
255
}
256
if (typeof bundle.metadata !== 'object') {
257
console.warn("mimebundle missing metadata", bundle);
258
bundle.metadata = {};
259
}
260
var data = bundle.data;
261
$.map(OutputArea.output_types, function(key){
262
if (key !== 'application/json' &&
263
data[key] !== undefined &&
264
typeof data[key] !== 'string'
265
) {
266
console.log("Invalid type for " + key, data[key]);
267
delete data[key];
268
}
269
});
270
return bundle;
271
};
272
273
OutputArea.prototype.append_output = function (json) {
274
this.expand();
275
276
// Clear the output if clear is queued.
277
var needs_height_reset = false;
278
if (this.clear_queued) {
279
this.clear_output(false);
280
needs_height_reset = true;
281
}
282
283
var record_output = true;
284
switch(json.output_type) {
285
case 'execute_result':
286
json = this.validate_mimebundle(json);
287
this.append_execute_result(json);
288
break;
289
case 'stream':
290
// append_stream might have merged the output with earlier stream output
291
record_output = this.append_stream(json);
292
break;
293
case 'error':
294
this.append_error(json);
295
break;
296
case 'display_data':
297
// append handled below
298
json = this.validate_mimebundle(json);
299
break;
300
default:
301
console.log("unrecognized output type: " + json.output_type);
302
this.append_unrecognized(json);
303
}
304
305
// We must release the animation fixed height in a callback since Gecko
306
// (FireFox) doesn't render the image immediately as the data is
307
// available.
308
var that = this;
309
var handle_appended = function ($el) {
310
/**
311
* Only reset the height to automatic if the height is currently
312
* fixed (done by wait=True flag on clear_output).
313
*/
314
if (needs_height_reset) {
315
that.element.height('');
316
}
317
that.element.trigger('resize');
318
};
319
if (json.output_type === 'display_data') {
320
this.append_display_data(json, handle_appended);
321
} else {
322
handle_appended();
323
}
324
325
if (record_output) {
326
this.outputs.push(json);
327
}
328
};
329
330
331
OutputArea.prototype.create_output_area = function () {
332
var oa = $("<div/>").addClass("output_area");
333
if (this.prompt_area) {
334
oa.append($('<div/>').addClass('prompt'));
335
}
336
return oa;
337
};
338
339
340
function _get_metadata_key(metadata, key, mime) {
341
var mime_md = metadata[mime];
342
// mime-specific higher priority
343
if (mime_md && mime_md[key] !== undefined) {
344
return mime_md[key];
345
}
346
// fallback on global
347
return metadata[key];
348
}
349
350
OutputArea.prototype.create_output_subarea = function(md, classes, mime) {
351
var subarea = $('<div/>').addClass('output_subarea').addClass(classes);
352
if (_get_metadata_key(md, 'isolated', mime)) {
353
// Create an iframe to isolate the subarea from the rest of the
354
// document
355
var iframe = $('<iframe/>').addClass('box-flex1');
356
iframe.css({'height':1, 'width':'100%', 'display':'block'});
357
iframe.attr('frameborder', 0);
358
iframe.attr('scrolling', 'auto');
359
360
// Once the iframe is loaded, the subarea is dynamically inserted
361
iframe.on('load', function() {
362
// Workaround needed by Firefox, to properly render svg inside
363
// iframes, see http://stackoverflow.com/questions/10177190/
364
// svg-dynamically-added-to-iframe-does-not-render-correctly
365
this.contentDocument.open();
366
367
// Insert the subarea into the iframe
368
// We must directly write the html. When using Jquery's append
369
// method, javascript is evaluated in the parent document and
370
// not in the iframe document. At this point, subarea doesn't
371
// contain any user content.
372
this.contentDocument.write(subarea.html());
373
374
this.contentDocument.close();
375
376
var body = this.contentDocument.body;
377
// Adjust the iframe height automatically
378
iframe.height(body.scrollHeight + 'px');
379
});
380
381
// Elements should be appended to the inner subarea and not to the
382
// iframe
383
iframe.append = function(that) {
384
subarea.append(that);
385
};
386
387
return iframe;
388
} else {
389
return subarea;
390
}
391
};
392
393
394
OutputArea.prototype._append_javascript_error = function (err, element) {
395
/**
396
* display a message when a javascript error occurs in display output
397
*/
398
var msg = "Javascript error adding output!";
399
if ( element === undefined ) return;
400
element
401
.append($('<div/>').text(msg).addClass('js-error'))
402
.append($('<div/>').text(err.toString()).addClass('js-error'))
403
.append($('<div/>').text('See your browser Javascript console for more details.').addClass('js-error'));
404
};
405
406
OutputArea.prototype._safe_append = function (toinsert) {
407
/**
408
* safely append an item to the document
409
* this is an object created by user code,
410
* and may have errors, which should not be raised
411
* under any circumstances.
412
*/
413
try {
414
this.element.append(toinsert);
415
} catch(err) {
416
console.log(err);
417
// Create an actual output_area and output_subarea, which creates
418
// the prompt area and the proper indentation.
419
var toinsert = this.create_output_area();
420
var subarea = $('<div/>').addClass('output_subarea');
421
toinsert.append(subarea);
422
this._append_javascript_error(err, subarea);
423
this.element.append(toinsert);
424
}
425
426
// Notify others of changes.
427
this.element.trigger('changed');
428
};
429
430
431
OutputArea.prototype.append_execute_result = function (json) {
432
var n = json.execution_count || ' ';
433
var toinsert = this.create_output_area();
434
if (this.prompt_area) {
435
toinsert.find('div.prompt').addClass('output_prompt').text('Out[' + n + ']:');
436
}
437
var inserted = this.append_mime_type(json, toinsert);
438
if (inserted) {
439
inserted.addClass('output_result');
440
}
441
this._safe_append(toinsert);
442
// If we just output latex, typeset it.
443
if ((json.data['text/latex'] !== undefined) ||
444
(json.data['text/html'] !== undefined) ||
445
(json.data['text/markdown'] !== undefined)) {
446
this.typeset();
447
}
448
};
449
450
451
OutputArea.prototype.append_error = function (json) {
452
var tb = json.traceback;
453
if (tb !== undefined && tb.length > 0) {
454
var s = '';
455
var len = tb.length;
456
for (var i=0; i<len; i++) {
457
s = s + tb[i] + '\n';
458
}
459
s = s + '\n';
460
var toinsert = this.create_output_area();
461
var append_text = OutputArea.append_map['text/plain'];
462
if (append_text) {
463
append_text.apply(this, [s, {}, toinsert]).addClass('output_error');
464
}
465
this._safe_append(toinsert);
466
}
467
};
468
469
470
OutputArea.prototype.append_stream = function (json) {
471
var text = json.text;
472
if (typeof text !== 'string') {
473
console.error("Stream output is invalid (missing text)", json);
474
return false;
475
}
476
var subclass = "output_"+json.name;
477
if (this.outputs.length > 0){
478
// have at least one output to consider
479
var last = this.outputs[this.outputs.length-1];
480
if (last.output_type == 'stream' && json.name == last.name){
481
// latest output was in the same stream,
482
// so append directly into its pre tag
483
// escape ANSI & HTML specials:
484
last.text = utils.fixCarriageReturn(last.text + json.text);
485
var pre = this.element.find('div.'+subclass).last().find('pre');
486
var html = utils.fixConsole(last.text);
487
html = utils.autoLinkUrls(html);
488
// The only user content injected with this HTML call is
489
// escaped by the fixConsole() method.
490
pre.html(html);
491
// return false signals that we merged this output with the previous one,
492
// and the new output shouldn't be recorded.
493
return false;
494
}
495
}
496
497
if (!text.replace("\r", "")) {
498
// text is nothing (empty string, \r, etc.)
499
// so don't append any elements, which might add undesirable space
500
// return true to indicate the output should be recorded.
501
return true;
502
}
503
504
// If we got here, attach a new div
505
var toinsert = this.create_output_area();
506
var append_text = OutputArea.append_map['text/plain'];
507
if (append_text) {
508
append_text.apply(this, [text, {}, toinsert]).addClass("output_stream " + subclass);
509
}
510
this._safe_append(toinsert);
511
return true;
512
};
513
514
515
OutputArea.prototype.append_unrecognized = function (json) {
516
var that = this;
517
var toinsert = this.create_output_area();
518
var subarea = $('<div/>').addClass('output_subarea output_unrecognized');
519
toinsert.append(subarea);
520
subarea.append(
521
$("<a>")
522
.attr("href", "#")
523
.text("Unrecognized output: " + json.output_type)
524
.click(function () {
525
that.events.trigger('unrecognized_output.OutputArea', {output: json});
526
})
527
);
528
this._safe_append(toinsert);
529
};
530
531
532
OutputArea.prototype.append_display_data = function (json, handle_inserted) {
533
var toinsert = this.create_output_area();
534
if (this.append_mime_type(json, toinsert, handle_inserted)) {
535
this._safe_append(toinsert);
536
// If we just output latex, typeset it.
537
if ((json.data['text/latex'] !== undefined) ||
538
(json.data['text/html'] !== undefined) ||
539
(json.data['text/markdown'] !== undefined)) {
540
this.typeset();
541
}
542
}
543
};
544
545
546
OutputArea.safe_outputs = {
547
'text/plain' : true,
548
'text/latex' : true,
549
'image/png' : true,
550
'image/jpeg' : true
551
};
552
553
OutputArea.prototype.append_mime_type = function (json, element, handle_inserted) {
554
for (var i=0; i < OutputArea.display_order.length; i++) {
555
var type = OutputArea.display_order[i];
556
var append = OutputArea.append_map[type];
557
if ((json.data[type] !== undefined) && append) {
558
var value = json.data[type];
559
if (!this.trusted && !OutputArea.safe_outputs[type]) {
560
// not trusted, sanitize HTML
561
if (type==='text/html' || type==='text/svg') {
562
value = security.sanitize_html(value);
563
} else {
564
// don't display if we don't know how to sanitize it
565
console.log("Ignoring untrusted " + type + " output.");
566
continue;
567
}
568
}
569
var md = json.metadata || {};
570
var toinsert = append.apply(this, [value, md, element, handle_inserted]);
571
// Since only the png and jpeg mime types call the inserted
572
// callback, if the mime type is something other we must call the
573
// inserted callback only when the element is actually inserted
574
// into the DOM. Use a timeout of 0 to do this.
575
if (['image/png', 'image/jpeg'].indexOf(type) < 0 && handle_inserted !== undefined) {
576
setTimeout(handle_inserted, 0);
577
}
578
this.events.trigger('output_appended.OutputArea', [type, value, md, toinsert]);
579
return toinsert;
580
}
581
}
582
return null;
583
};
584
585
586
var append_html = function (html, md, element) {
587
var type = 'text/html';
588
var toinsert = this.create_output_subarea(md, "output_html rendered_html", type);
589
this.keyboard_manager.register_events(toinsert);
590
toinsert.append(html);
591
dblclick_to_reset_size(toinsert.find('img'));
592
element.append(toinsert);
593
return toinsert;
594
};
595
596
597
var append_markdown = function(markdown, md, element) {
598
var type = 'text/markdown';
599
var toinsert = this.create_output_subarea(md, "output_markdown", type);
600
var text_and_math = mathjaxutils.remove_math(markdown);
601
var text = text_and_math[0];
602
var math = text_and_math[1];
603
marked(text, function (err, html) {
604
html = mathjaxutils.replace_math(html, math);
605
toinsert.append(html);
606
});
607
dblclick_to_reset_size(toinsert.find('img'));
608
element.append(toinsert);
609
return toinsert;
610
};
611
612
613
var append_javascript = function (js, md, element) {
614
/**
615
* We just eval the JS code, element appears in the local scope.
616
*/
617
var type = 'application/javascript';
618
var toinsert = this.create_output_subarea(md, "output_javascript", type);
619
this.keyboard_manager.register_events(toinsert);
620
element.append(toinsert);
621
622
// Fix for ipython/issues/5293, make sure `element` is the area which
623
// output can be inserted into at the time of JS execution.
624
element = toinsert;
625
try {
626
eval(js);
627
} catch(err) {
628
console.log(err);
629
this._append_javascript_error(err, toinsert);
630
}
631
return toinsert;
632
};
633
634
635
var append_text = function (data, md, element) {
636
var type = 'text/plain';
637
var toinsert = this.create_output_subarea(md, "output_text", type);
638
// escape ANSI & HTML specials in plaintext:
639
data = utils.fixConsole(data);
640
data = utils.fixCarriageReturn(data);
641
data = utils.autoLinkUrls(data);
642
// The only user content injected with this HTML call is
643
// escaped by the fixConsole() method.
644
toinsert.append($("<pre/>").html(data));
645
element.append(toinsert);
646
return toinsert;
647
};
648
649
650
var append_svg = function (svg_html, md, element) {
651
var type = 'image/svg+xml';
652
var toinsert = this.create_output_subarea(md, "output_svg", type);
653
654
// Get the svg element from within the HTML.
655
var svg = $('<div />').html(svg_html).find('svg');
656
var svg_area = $('<div />');
657
var width = svg.attr('width');
658
var height = svg.attr('height');
659
svg
660
.width('100%')
661
.height('100%');
662
svg_area
663
.width(width)
664
.height(height);
665
666
svg_area.append(svg);
667
toinsert.append(svg_area);
668
element.append(toinsert);
669
670
return toinsert;
671
};
672
673
function dblclick_to_reset_size (img) {
674
/**
675
* Double-click on an image toggles confinement to notebook width
676
*
677
* img: jQuery element
678
*/
679
680
img.dblclick(function () {
681
// dblclick toggles *raw* size, disabling max-width confinement.
682
if (img.hasClass('unconfined')) {
683
img.removeClass('unconfined');
684
} else {
685
img.addClass('unconfined');
686
}
687
});
688
};
689
690
var set_width_height = function (img, md, mime) {
691
/**
692
* set width and height of an img element from metadata
693
*/
694
var height = _get_metadata_key(md, 'height', mime);
695
if (height !== undefined) img.attr('height', height);
696
var width = _get_metadata_key(md, 'width', mime);
697
if (width !== undefined) img.attr('width', width);
698
if (_get_metadata_key(md, 'unconfined', mime)) {
699
img.addClass('unconfined');
700
}
701
};
702
703
var append_png = function (png, md, element, handle_inserted) {
704
var type = 'image/png';
705
var toinsert = this.create_output_subarea(md, "output_png", type);
706
var img = $("<img/>");
707
if (handle_inserted !== undefined) {
708
img.on('load', function(){
709
handle_inserted(img);
710
});
711
}
712
img[0].src = 'data:image/png;base64,'+ png;
713
set_width_height(img, md, 'image/png');
714
dblclick_to_reset_size(img);
715
toinsert.append(img);
716
element.append(toinsert);
717
return toinsert;
718
};
719
720
721
var append_jpeg = function (jpeg, md, element, handle_inserted) {
722
var type = 'image/jpeg';
723
var toinsert = this.create_output_subarea(md, "output_jpeg", type);
724
var img = $("<img/>");
725
if (handle_inserted !== undefined) {
726
img.on('load', function(){
727
handle_inserted(img);
728
});
729
}
730
img[0].src = 'data:image/jpeg;base64,'+ jpeg;
731
set_width_height(img, md, 'image/jpeg');
732
dblclick_to_reset_size(img);
733
toinsert.append(img);
734
element.append(toinsert);
735
return toinsert;
736
};
737
738
739
var append_pdf = function (pdf, md, element) {
740
var type = 'application/pdf';
741
var toinsert = this.create_output_subarea(md, "output_pdf", type);
742
var a = $('<a/>').attr('href', 'data:application/pdf;base64,'+pdf);
743
a.attr('target', '_blank');
744
a.text('View PDF');
745
toinsert.append(a);
746
element.append(toinsert);
747
return toinsert;
748
};
749
750
var append_latex = function (latex, md, element) {
751
/**
752
* This method cannot do the typesetting because the latex first has to
753
* be on the page.
754
*/
755
var type = 'text/latex';
756
var toinsert = this.create_output_subarea(md, "output_latex", type);
757
toinsert.append(latex);
758
element.append(toinsert);
759
return toinsert;
760
};
761
762
763
OutputArea.prototype.append_raw_input = function (msg) {
764
var that = this;
765
this.expand();
766
var content = msg.content;
767
var area = this.create_output_area();
768
769
// disable any other raw_inputs, if they are left around
770
$("div.output_subarea.raw_input_container").remove();
771
772
var input_type = content.password ? 'password' : 'text';
773
774
area.append(
775
$("<div/>")
776
.addClass("box-flex1 output_subarea raw_input_container")
777
.append(
778
$("<span/>")
779
.addClass("raw_input_prompt")
780
.text(content.prompt)
781
)
782
.append(
783
$("<input/>")
784
.addClass("raw_input")
785
.attr('type', input_type)
786
.attr("size", 47)
787
.keydown(function (event, ui) {
788
// make sure we submit on enter,
789
// and don't re-execute the *cell* on shift-enter
790
if (event.which === keyboard.keycodes.enter) {
791
that._submit_raw_input();
792
return false;
793
}
794
})
795
)
796
);
797
798
this.element.append(area);
799
var raw_input = area.find('input.raw_input');
800
// Register events that enable/disable the keyboard manager while raw
801
// input is focused.
802
this.keyboard_manager.register_events(raw_input);
803
// Note, the following line used to read raw_input.focus().focus().
804
// This seemed to be needed otherwise only the cell would be focused.
805
// But with the modal UI, this seems to work fine with one call to focus().
806
raw_input.focus();
807
};
808
809
OutputArea.prototype._submit_raw_input = function (evt) {
810
var container = this.element.find("div.raw_input_container");
811
var theprompt = container.find("span.raw_input_prompt");
812
var theinput = container.find("input.raw_input");
813
var value = theinput.val();
814
var echo = value;
815
// don't echo if it's a password
816
if (theinput.attr('type') == 'password') {
817
echo = '········';
818
}
819
var content = {
820
output_type : 'stream',
821
name : 'stdout',
822
text : theprompt.text() + echo + '\n'
823
};
824
// remove form container
825
container.parent().remove();
826
// replace with plaintext version in stdout
827
this.append_output(content, false);
828
this.events.trigger('send_input_reply.Kernel', value);
829
};
830
831
832
OutputArea.prototype.handle_clear_output = function (msg) {
833
/**
834
* msg spec v4 had stdout, stderr, display keys
835
* v4.1 replaced these with just wait
836
* The default behavior is the same (stdout=stderr=display=True, wait=False),
837
* so v4 messages will still be properly handled,
838
* except for the rarely used clearing less than all output.
839
*/
840
this.clear_output(msg.content.wait || false);
841
};
842
843
844
OutputArea.prototype.clear_output = function(wait, ignore_que) {
845
if (wait) {
846
847
// If a clear is queued, clear before adding another to the queue.
848
if (this.clear_queued) {
849
this.clear_output(false);
850
}
851
852
this.clear_queued = true;
853
} else {
854
855
// Fix the output div's height if the clear_output is waiting for
856
// new output (it is being used in an animation).
857
if (!ignore_que && this.clear_queued) {
858
var height = this.element.height();
859
this.element.height(height);
860
this.clear_queued = false;
861
}
862
863
// Clear all
864
// Remove load event handlers from img tags because we don't want
865
// them to fire if the image is never added to the page.
866
this.element.find('img').off('load');
867
this.element.html("");
868
869
// Notify others of changes.
870
this.element.trigger('changed');
871
872
this.outputs = [];
873
this.trusted = true;
874
this.unscroll_area();
875
return;
876
}
877
};
878
879
880
// JSON serialization
881
882
OutputArea.prototype.fromJSON = function (outputs, metadata) {
883
var len = outputs.length;
884
metadata = metadata || {};
885
886
for (var i=0; i<len; i++) {
887
this.append_output(outputs[i]);
888
}
889
if (metadata.collapsed !== undefined) {
890
if (metadata.collapsed) {
891
this.collapse();
892
} else {
893
this.expand();
894
}
895
}
896
if (metadata.scrolled !== undefined) {
897
this.scroll_state = metadata.scrolled;
898
if (metadata.scrolled) {
899
this.scroll_if_long();
900
} else {
901
this.unscroll_area();
902
}
903
}
904
};
905
906
907
OutputArea.prototype.toJSON = function () {
908
return this.outputs;
909
};
910
911
/**
912
* Class properties
913
**/
914
915
/**
916
* Threshold to trigger autoscroll when the OutputArea is resized,
917
* typically when new outputs are added.
918
*
919
* Behavior is undefined if autoscroll is lower than minimum_scroll_threshold,
920
* unless it is < 0, in which case autoscroll will never be triggered
921
*
922
* @property auto_scroll_threshold
923
* @type Number
924
* @default 100
925
*
926
**/
927
OutputArea.auto_scroll_threshold = 100;
928
929
/**
930
* Lower limit (in lines) for OutputArea to be made scrollable. OutputAreas
931
* shorter than this are never scrolled.
932
*
933
* @property minimum_scroll_threshold
934
* @type Number
935
* @default 20
936
*
937
**/
938
OutputArea.minimum_scroll_threshold = 20;
939
940
941
OutputArea.display_order = [
942
'application/javascript',
943
'text/html',
944
'text/markdown',
945
'text/latex',
946
'image/svg+xml',
947
'image/png',
948
'image/jpeg',
949
'application/pdf',
950
'text/plain'
951
];
952
953
OutputArea.append_map = {
954
"text/plain" : append_text,
955
"text/html" : append_html,
956
"text/markdown": append_markdown,
957
"image/svg+xml" : append_svg,
958
"image/png" : append_png,
959
"image/jpeg" : append_jpeg,
960
"text/latex" : append_latex,
961
"application/javascript" : append_javascript,
962
"application/pdf" : append_pdf
963
};
964
965
// For backwards compatability.
966
IPython.OutputArea = OutputArea;
967
968
return {'OutputArea': OutputArea};
969
});
970
971