##// END OF EJS Templates
disable auto-scroll on mozilla...
MinRK -
Show More
@@ -1,541 +1,545
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 // OutputArea
10 10 //============================================================================
11 11
12 12 var IPython = (function (IPython) {
13 13 "use strict";
14 14
15 15 var utils = IPython.utils;
16 16
17 17 var OutputArea = function (selector, prompt_area) {
18 18 this.selector = selector;
19 19 this.wrapper = $(selector);
20 20 this.outputs = [];
21 21 this.collapsed = false;
22 22 this.scrolled = false;
23 23 this.clear_out_timeout = null;
24 24 if (prompt_area === undefined) {
25 25 this.prompt_area = true;
26 26 } else {
27 27 this.prompt_area = prompt_area;
28 28 };
29 29 this.create_elements();
30 30 this.style();
31 31 this.bind_events();
32 32 };
33 33
34 34 OutputArea.prototype.create_elements = function () {
35 35 this.element = $("<div/>");
36 36 this.collapse_button = $("<div/>");
37 37 this.prompt_overlay = $("<div/>");
38 38 this.wrapper.append(this.prompt_overlay);
39 39 this.wrapper.append(this.element);
40 40 this.wrapper.append(this.collapse_button);
41 41 };
42 42
43 43
44 44 OutputArea.prototype.style = function () {
45 45 this.collapse_button.hide();
46 46 this.prompt_overlay.hide();
47 47
48 48 this.wrapper.addClass('output_wrapper');
49 49 this.element.addClass('output vbox');
50 50
51 51 this.collapse_button.button();
52 52 this.collapse_button.addClass('output_collapsed vbox');
53 53 this.collapse_button.attr('title', 'click to expand outout');
54 54 this.collapse_button.html('. . .');
55 55
56 56 this.prompt_overlay.addClass('out_prompt_overlay prompt');
57 57 this.prompt_overlay.attr('title', 'click to expand outout; double click to hide output');
58 58
59 59 this.collapse();
60 60 };
61 61
62 62
63 63 OutputArea.prototype._should_scroll = function (lines) {
64 64 if (!lines) {
65 65 lines = 50;
66 66 }
67 67 // line-height from http://stackoverflow.com/questions/1185151
68 68 var fontSize = this.element.css('font-size');
69 69 var lineHeight = Math.floor(parseInt(fontSize.replace('px','')) * 1.5);
70 70
71 71 return (this.element.height() > lines * lineHeight);
72 72 };
73 73
74 74
75 75 OutputArea.prototype.bind_events = function () {
76 76 var that = this;
77 77 this.prompt_overlay.dblclick(function () { that.toggle_output(); });
78 78 this.prompt_overlay.click(function () { that.toggle_scroll(); });
79 79
80 80 this.element.resize(function () {
81 // FIXME: Firefox on Linux misbehaves, so automatic scrolling is disabled
82 if ( $.browser.mozilla ) {
83 return;
84 }
81 85 // maybe scroll output,
82 86 // if it's grown large enough and hasn't already been scrolled.
83 87 if ( !that.scrolled && that._should_scroll()) {
84 88 that.scroll_area();
85 89 }
86 90 });
87 91 this.collapse_button.click(function () {
88 92 that.expand();
89 93 });
90 94 this.collapse_button.hover(function () {
91 95 $(this).addClass("ui-state-hover");
92 96 }, function () {
93 97 $(this).removeClass("ui-state-hover");
94 98 });
95 99 };
96 100
97 101
98 102 OutputArea.prototype.collapse = function () {
99 103 if (!this.collapsed) {
100 104 this.element.hide();
101 105 this.prompt_overlay.hide();
102 106 if (this.element.html()){
103 107 this.collapse_button.show();
104 108 }
105 109 this.collapsed = true;
106 110 };
107 111 };
108 112
109 113
110 114 OutputArea.prototype.expand = function () {
111 115 if (this.collapsed) {
112 116 this.collapse_button.hide();
113 117 this.element.show();
114 118 this.prompt_overlay.show();
115 119 this.collapsed = false;
116 120 };
117 121 };
118 122
119 123
120 124 OutputArea.prototype.toggle_output = function () {
121 125 if (this.collapsed) {
122 126 this.expand();
123 127 } else {
124 128 this.collapse();
125 129 };
126 130 };
127 131
128 132
129 133 OutputArea.prototype.scroll_area = function () {
130 134 this.element.addClass('output_scroll');
131 135 this.prompt_overlay.attr('title', 'click to unscroll output; double click to hide');
132 136 this.scrolled = true;
133 137 };
134 138
135 139
136 140 OutputArea.prototype.unscroll_area = function () {
137 141 this.element.removeClass('output_scroll');
138 142 this.prompt_overlay.attr('title', 'click to scroll output; double click to hide');
139 143 this.scrolled = false;
140 144 };
141 145
142 146
143 147 OutputArea.prototype.scroll_if_long = function (lines) {
144 148 if (this._should_scroll(lines)) {
145 149 // only allow scrolling long-enough output
146 150 this.scroll_area();
147 151 };
148 152 };
149 153
150 154
151 155 OutputArea.prototype.toggle_scroll = function () {
152 156 if (this.scrolled) {
153 157 this.unscroll_area();
154 158 } else {
155 159 // only allow scrolling long-enough output
156 160 this.scroll_if_long(20);
157 161 };
158 162 };
159 163
160 164
161 165 // typeset with MathJax if MathJax is available
162 166 OutputArea.prototype.typeset = function () {
163 167 if (window.MathJax){
164 168 MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
165 169 }
166 170 };
167 171
168 172
169 173 OutputArea.prototype.handle_output = function (msg_type, content) {
170 174 var json = {};
171 175 json.output_type = msg_type;
172 176 if (msg_type === "stream") {
173 177 json.text = content.data;
174 178 json.stream = content.name;
175 179 } else if (msg_type === "display_data") {
176 180 json = this.convert_mime_types(json, content.data);
177 181 } else if (msg_type === "pyout") {
178 182 json.prompt_number = content.execution_count;
179 183 json = this.convert_mime_types(json, content.data);
180 184 } else if (msg_type === "pyerr") {
181 185 json.ename = content.ename;
182 186 json.evalue = content.evalue;
183 187 json.traceback = content.traceback;
184 188 };
185 189 // append with dynamic=true
186 190 this.append_output(json, true);
187 191 };
188 192
189 193
190 194 OutputArea.prototype.convert_mime_types = function (json, data) {
191 195 if (data['text/plain'] !== undefined) {
192 196 json.text = data['text/plain'];
193 197 };
194 198 if (data['text/html'] !== undefined) {
195 199 json.html = data['text/html'];
196 200 };
197 201 if (data['image/svg+xml'] !== undefined) {
198 202 json.svg = data['image/svg+xml'];
199 203 };
200 204 if (data['image/png'] !== undefined) {
201 205 json.png = data['image/png'];
202 206 };
203 207 if (data['image/jpeg'] !== undefined) {
204 208 json.jpeg = data['image/jpeg'];
205 209 };
206 210 if (data['text/latex'] !== undefined) {
207 211 json.latex = data['text/latex'];
208 212 };
209 213 if (data['application/json'] !== undefined) {
210 214 json.json = data['application/json'];
211 215 };
212 216 if (data['application/javascript'] !== undefined) {
213 217 json.javascript = data['application/javascript'];
214 218 }
215 219 return json;
216 220 };
217 221
218 222
219 223 OutputArea.prototype.append_output = function (json, dynamic) {
220 224 // If dynamic is true, javascript output will be eval'd.
221 225 this.expand();
222 226 this.flush_clear_timeout();
223 227 if (json.output_type === 'pyout') {
224 228 this.append_pyout(json, dynamic);
225 229 } else if (json.output_type === 'pyerr') {
226 230 this.append_pyerr(json);
227 231 } else if (json.output_type === 'display_data') {
228 232 this.append_display_data(json, dynamic);
229 233 } else if (json.output_type === 'stream') {
230 234 this.append_stream(json);
231 235 };
232 236 this.outputs.push(json);
233 237 var that = this;
234 238 setTimeout(function(){that.element.trigger('resize');}, 100);
235 239 };
236 240
237 241
238 242 OutputArea.prototype.create_output_area = function () {
239 243 var oa = $("<div/>").addClass("hbox output_area");
240 244 if (this.prompt_area) {
241 245 oa.append($('<div/>').addClass('prompt'));
242 246 }
243 247 return oa;
244 248 };
245 249
246 250
247 251 OutputArea.prototype.append_pyout = function (json, dynamic) {
248 252 var n = json.prompt_number || ' ';
249 253 var toinsert = this.create_output_area();
250 254 if (this.prompt_area) {
251 255 toinsert.find('div.prompt').addClass('output_prompt').html('Out[' + n + ']:');
252 256 }
253 257 this.append_mime_type(json, toinsert, dynamic);
254 258 this.element.append(toinsert);
255 259 // If we just output latex, typeset it.
256 260 if ((json.latex !== undefined) || (json.html !== undefined)) {
257 261 this.typeset();
258 262 };
259 263 };
260 264
261 265
262 266 OutputArea.prototype.append_pyerr = function (json) {
263 267 var tb = json.traceback;
264 268 if (tb !== undefined && tb.length > 0) {
265 269 var s = '';
266 270 var len = tb.length;
267 271 for (var i=0; i<len; i++) {
268 272 s = s + tb[i] + '\n';
269 273 }
270 274 s = s + '\n';
271 275 var toinsert = this.create_output_area();
272 276 this.append_text(s, toinsert);
273 277 this.element.append(toinsert);
274 278 };
275 279 };
276 280
277 281
278 282 OutputArea.prototype.append_stream = function (json) {
279 283 // temporary fix: if stream undefined (json file written prior to this patch),
280 284 // default to most likely stdout:
281 285 if (json.stream == undefined){
282 286 json.stream = 'stdout';
283 287 }
284 288 var text = json.text;
285 289 var subclass = "output_"+json.stream;
286 290 if (this.outputs.length > 0){
287 291 // have at least one output to consider
288 292 var last = this.outputs[this.outputs.length-1];
289 293 if (last.output_type == 'stream' && json.stream == last.stream){
290 294 // latest output was in the same stream,
291 295 // so append directly into its pre tag
292 296 // escape ANSI & HTML specials:
293 297 var pre = this.element.find('div.'+subclass).last().find('pre');
294 298 var html = utils.fixCarriageReturn(
295 299 pre.html() + utils.fixConsole(text));
296 300 pre.html(html);
297 301 return;
298 302 }
299 303 }
300 304
301 305 if (!text.replace("\r", "")) {
302 306 // text is nothing (empty string, \r, etc.)
303 307 // so don't append any elements, which might add undesirable space
304 308 return;
305 309 }
306 310
307 311 // If we got here, attach a new div
308 312 var toinsert = this.create_output_area();
309 313 this.append_text(text, toinsert, "output_stream "+subclass);
310 314 this.element.append(toinsert);
311 315 };
312 316
313 317
314 318 OutputArea.prototype.append_display_data = function (json, dynamic) {
315 319 var toinsert = this.create_output_area();
316 320 this.append_mime_type(json, toinsert, dynamic);
317 321 this.element.append(toinsert);
318 322 // If we just output latex, typeset it.
319 323 if ( (json.latex !== undefined) || (json.html !== undefined) ) {
320 324 this.typeset();
321 325 };
322 326 };
323 327
324 328
325 329 OutputArea.prototype.append_mime_type = function (json, element, dynamic) {
326 330 if (json.javascript !== undefined && dynamic) {
327 331 this.append_javascript(json.javascript, element, dynamic);
328 332 } else if (json.html !== undefined) {
329 333 this.append_html(json.html, element);
330 334 } else if (json.latex !== undefined) {
331 335 this.append_latex(json.latex, element);
332 336 } else if (json.svg !== undefined) {
333 337 this.append_svg(json.svg, element);
334 338 } else if (json.png !== undefined) {
335 339 this.append_png(json.png, element);
336 340 } else if (json.jpeg !== undefined) {
337 341 this.append_jpeg(json.jpeg, element);
338 342 } else if (json.text !== undefined) {
339 343 this.append_text(json.text, element);
340 344 };
341 345 };
342 346
343 347
344 348 OutputArea.prototype.append_html = function (html, element) {
345 349 var toinsert = $("<div/>").addClass("box-flex1 output_subarea output_html rendered_html");
346 350 toinsert.append(html);
347 351 element.append(toinsert);
348 352 };
349 353
350 354
351 355 OutputArea.prototype.append_javascript = function (js, container) {
352 356 // We just eval the JS code, element appears in the local scope.
353 357 var element = $("<div/>").addClass("box-flex1 output_subarea");
354 358 container.append(element);
355 359 // Div for js shouldn't be drawn, as it will add empty height to the area.
356 360 container.hide();
357 361 // If the Javascript appends content to `element` that should be drawn, then
358 362 // it must also call `container.show()`.
359 363 eval(js);
360 364 }
361 365
362 366
363 367 OutputArea.prototype.append_text = function (data, element, extra_class) {
364 368 var toinsert = $("<div/>").addClass("box-flex1 output_subarea output_text");
365 369 // escape ANSI & HTML specials in plaintext:
366 370 data = utils.fixConsole(data);
367 371 data = utils.fixCarriageReturn(data);
368 372 if (extra_class){
369 373 toinsert.addClass(extra_class);
370 374 }
371 375 toinsert.append($("<pre/>").html(data));
372 376 element.append(toinsert);
373 377 };
374 378
375 379
376 380 OutputArea.prototype.append_svg = function (svg, element) {
377 381 var toinsert = $("<div/>").addClass("box-flex1 output_subarea output_svg");
378 382 toinsert.append(svg);
379 383 element.append(toinsert);
380 384 };
381 385
382 386
383 387 OutputArea.prototype._dblclick_to_reset_size = function (img) {
384 388 // schedule wrapping image in resizable after a delay,
385 389 // so we don't end up calling resize on a zero-size object
386 390 var that = this;
387 391 setTimeout(function () {
388 392 var h0 = img.height();
389 393 var w0 = img.width();
390 394 if (!(h0 && w0)) {
391 395 // zero size, schedule another timeout
392 396 that._dblclick_to_reset_size(img);
393 397 return
394 398 }
395 399 img.resizable({
396 400 aspectRatio: true,
397 401 autoHide: true
398 402 });
399 403 img.dblclick(function () {
400 404 // resize wrapper & image together for some reason:
401 405 img.parent().height(h0);
402 406 img.height(h0);
403 407 img.parent().width(w0);
404 408 img.width(w0);
405 409 });
406 410 }, 250);
407 411 }
408 412
409 413
410 414 OutputArea.prototype.append_png = function (png, element) {
411 415 var toinsert = $("<div/>").addClass("box-flex1 output_subarea output_png");
412 416 var img = $("<img/>").attr('src','data:image/png;base64,'+png);
413 417 this._dblclick_to_reset_size(img);
414 418 toinsert.append(img);
415 419 element.append(toinsert);
416 420 };
417 421
418 422
419 423 OutputArea.prototype.append_jpeg = function (jpeg, element) {
420 424 var toinsert = $("<div/>").addClass("box-flex1 output_subarea output_jpeg");
421 425 var img = $("<img/>").attr('src','data:image/jpeg;base64,'+jpeg);
422 426 this._dblclick_to_reset_size(img);
423 427 toinsert.append(img);
424 428 element.append(toinsert);
425 429 };
426 430
427 431
428 432 OutputArea.prototype.append_latex = function (latex, element) {
429 433 // This method cannot do the typesetting because the latex first has to
430 434 // be on the page.
431 435 var toinsert = $("<div/>").addClass("box-flex1 output_subarea output_latex");
432 436 toinsert.append(latex);
433 437 element.append(toinsert);
434 438 };
435 439
436 440
437 441 OutputArea.prototype.handle_clear_output = function (content) {
438 442 this.clear_output(content.stdout, content.stderr, content.other);
439 443 }
440 444
441 445
442 446 OutputArea.prototype.clear_output = function (stdout, stderr, other) {
443 447 var that = this;
444 448 if (this.clear_out_timeout != null){
445 449 // fire previous pending clear *immediately*
446 450 clearTimeout(this.clear_out_timeout);
447 451 this.clear_out_timeout = null;
448 452 this.clear_output_callback(this._clear_stdout, this._clear_stderr, this._clear_other);
449 453 }
450 454 // store flags for flushing the timeout
451 455 this._clear_stdout = stdout;
452 456 this._clear_stderr = stderr;
453 457 this._clear_other = other;
454 458 this.clear_out_timeout = setTimeout(function() {
455 459 // really clear timeout only after a short delay
456 460 // this reduces flicker in 'clear_output; print' cases
457 461 that.clear_out_timeout = null;
458 462 that._clear_stdout = that._clear_stderr = that._clear_other = null;
459 463 that.clear_output_callback(stdout, stderr, other);
460 464 }, 500
461 465 );
462 466 };
463 467
464 468
465 469 OutputArea.prototype.clear_output_callback = function (stdout, stderr, other) {
466 470 var output_div = this.element;
467 471
468 472 if (stdout && stderr && other){
469 473 // clear all, no need for logic
470 474 output_div.html("");
471 475 this.outputs = [];
472 476 this.unscroll_area();
473 477 return;
474 478 }
475 479 // remove html output
476 480 // each output_subarea that has an identifying class is in an output_area
477 481 // which is the element to be removed.
478 482 if (stdout) {
479 483 output_div.find("div.output_stdout").parent().remove();
480 484 }
481 485 if (stderr) {
482 486 output_div.find("div.output_stderr").parent().remove();
483 487 }
484 488 if (other) {
485 489 output_div.find("div.output_subarea").not("div.output_stderr").not("div.output_stdout").parent().remove();
486 490 }
487 491 this.unscroll_area();
488 492
489 493 // remove cleared outputs from JSON list:
490 494 for (var i = this.outputs.length - 1; i >= 0; i--) {
491 495 var out = this.outputs[i];
492 496 var output_type = out.output_type;
493 497 if (output_type == "display_data" && other) {
494 498 this.outputs.splice(i,1);
495 499 } else if (output_type == "stream") {
496 500 if (stdout && out.stream == "stdout") {
497 501 this.outputs.splice(i,1);
498 502 } else if (stderr && out.stream == "stderr") {
499 503 this.outputs.splice(i,1);
500 504 }
501 505 }
502 506 }
503 507 };
504 508
505 509
506 510 OutputArea.prototype.flush_clear_timeout = function() {
507 511 var output_div = this.element;
508 512 if (this.clear_out_timeout){
509 513 clearTimeout(this.clear_out_timeout);
510 514 this.clear_out_timeout = null;
511 515 this.clear_output_callback(this._clear_stdout, this._clear_stderr, this._clear_other);
512 516 };
513 517 }
514 518
515 519
516 520 // JSON serialization
517 521
518 522 OutputArea.prototype.fromJSON = function (outputs) {
519 523 var len = outputs.length;
520 524 for (var i=0; i<len; i++) {
521 525 // append with dynamic=false.
522 526 this.append_output(outputs[i], false);
523 527 };
524 528 };
525 529
526 530
527 531 OutputArea.prototype.toJSON = function () {
528 532 var outputs = [];
529 533 var len = this.outputs.length;
530 534 for (var i=0; i<len; i++) {
531 535 outputs[i] = this.outputs[i];
532 536 };
533 537 return outputs;
534 538 };
535 539
536 540
537 541 IPython.OutputArea = OutputArea;
538 542
539 543 return IPython;
540 544
541 545 }(IPython));
General Comments 0
You need to be logged in to leave comments. Login now