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