##// END OF EJS Templates
Merge pull request #6670 from jdfreder/output_widget...
Min RK -
r18961:9288ed68 merge
parent child Browse files
Show More
@@ -0,0 +1,57 b''
1 // Copyright (c) IPython Development Team.
2 // Distributed under the terms of the Modified BSD License.
3
4 define([
5 "widgets/js/widget",
6 "jquery",
7 'notebook/js/outputarea',
8 ], function(widget, $, outputarea) {
9 'use strict';
10
11 var OutputView = widget.DOMWidgetView.extend({
12 initialize: function (parameters) {
13 // Public constructor
14 OutputView.__super__.initialize.apply(this, [parameters]);
15 this.model.on('msg:custom', this._handle_route_msg, this);
16 },
17
18 render: function(){
19 // Called when view is rendered.
20 this.output_area = new outputarea.OutputArea({
21 selector: this.$el,
22 prompt_area: false,
23 events: this.model.widget_manager.notebook.events,
24 keyboard_manager: this.model.widget_manager.keyboard_manager });
25
26 // Make output area reactive.
27 var that = this;
28 this.output_area.element.on('changed', function() {
29 that.model.set('contents', that.output_area.element.html());
30 });
31 this.model.on('change:contents', function(){
32 var html = this.model.get('contents');
33 if (this.output_area.element.html() != html) {
34 this.output_area.element.html(html);
35 }
36 }, this);
37
38 // Set initial contents.
39 this.output_area.element.html(this.model.get('contents'));
40 },
41
42 _handle_route_msg: function(content) {
43 var cell = this.options.cell;
44 if (content && cell) {
45 if (content.method == 'push') {
46 cell.push_output_area(this.output_area);
47 } else if (content.method == 'pop') {
48 cell.pop_output_area(this.output_area);
49 }
50 }
51 },
52 });
53
54 return {
55 'OutputView': OutputView,
56 };
57 });
@@ -0,0 +1,50 b''
1 """Output class.
2
3 Represents a widget that can be used to display output within the widget area.
4 """
5
6 # Copyright (c) IPython Development Team.
7 # Distributed under the terms of the Modified BSD License.
8
9 from .widget import DOMWidget
10 import sys
11 from IPython.utils.traitlets import Unicode, List
12 from IPython.display import clear_output
13 from IPython.testing.skipdoctest import skip_doctest
14
15 @skip_doctest
16 class Output(DOMWidget):
17 """Widget used as a context manager to display output.
18
19 This widget can capture and display stdout, stderr, and rich output. To use
20 it, create an instance of it and display it. Then use it as a context
21 manager. Any output produced while in it's context will be captured and
22 displayed in it instead of the standard output area.
23
24 Example
25 from IPython.html import widgets
26 from IPython.display import display
27 out = widgets.Output()
28 display(out)
29
30 print('prints to output area')
31
32 with out:
33 print('prints to output widget')"""
34 _view_name = Unicode('OutputView', sync=True)
35
36 def clear_output(self, *pargs, **kwargs):
37 with self:
38 clear_output(*pargs, **kwargs)
39
40 def __enter__(self):
41 self._flush()
42 self.send({'method': 'push'})
43
44 def __exit__(self, exception_type, exception_value, traceback):
45 self._flush()
46 self.send({'method': 'pop'})
47
48 def _flush(self):
49 sys.stdout.flush()
50 sys.stderr.flush()
@@ -1,531 +1,569 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 *
4 *
5 *
5 *
6 * @module codecell
6 * @module codecell
7 * @namespace codecell
7 * @namespace codecell
8 * @class CodeCell
8 * @class CodeCell
9 */
9 */
10
10
11
11
12 define([
12 define([
13 'base/js/namespace',
13 'base/js/namespace',
14 'jquery',
14 'jquery',
15 'base/js/utils',
15 'base/js/utils',
16 'base/js/keyboard',
16 'base/js/keyboard',
17 'notebook/js/cell',
17 'notebook/js/cell',
18 'notebook/js/outputarea',
18 'notebook/js/outputarea',
19 'notebook/js/completer',
19 'notebook/js/completer',
20 'notebook/js/celltoolbar',
20 'notebook/js/celltoolbar',
21 'codemirror/lib/codemirror',
21 'codemirror/lib/codemirror',
22 'codemirror/mode/python/python',
22 'codemirror/mode/python/python',
23 'notebook/js/codemirror-ipython'
23 'notebook/js/codemirror-ipython'
24 ], function(IPython, $, utils, keyboard, cell, outputarea, completer, celltoolbar, CodeMirror, cmpython, cmip) {
24 ], function(IPython, $, utils, keyboard, cell, outputarea, completer, celltoolbar, CodeMirror, cmpython, cmip) {
25 "use strict";
25 "use strict";
26
26 var Cell = cell.Cell;
27 var Cell = cell.Cell;
27
28
28 /* local util for codemirror */
29 /* local util for codemirror */
29 var posEq = function(a, b) {return a.line == b.line && a.ch == b.ch;};
30 var posEq = function(a, b) {return a.line == b.line && a.ch == b.ch;};
30
31
31 /**
32 /**
32 *
33 *
33 * function to delete until previous non blanking space character
34 * function to delete until previous non blanking space character
34 * or first multiple of 4 tabstop.
35 * or first multiple of 4 tabstop.
35 * @private
36 * @private
36 */
37 */
37 CodeMirror.commands.delSpaceToPrevTabStop = function(cm){
38 CodeMirror.commands.delSpaceToPrevTabStop = function(cm){
38 var from = cm.getCursor(true), to = cm.getCursor(false), sel = !posEq(from, to);
39 var from = cm.getCursor(true), to = cm.getCursor(false), sel = !posEq(from, to);
39 if (!posEq(from, to)) { cm.replaceRange("", from, to); return; }
40 if (!posEq(from, to)) { cm.replaceRange("", from, to); return; }
40 var cur = cm.getCursor(), line = cm.getLine(cur.line);
41 var cur = cm.getCursor(), line = cm.getLine(cur.line);
41 var tabsize = cm.getOption('tabSize');
42 var tabsize = cm.getOption('tabSize');
42 var chToPrevTabStop = cur.ch-(Math.ceil(cur.ch/tabsize)-1)*tabsize;
43 var chToPrevTabStop = cur.ch-(Math.ceil(cur.ch/tabsize)-1)*tabsize;
43 from = {ch:cur.ch-chToPrevTabStop,line:cur.line};
44 from = {ch:cur.ch-chToPrevTabStop,line:cur.line};
44 var select = cm.getRange(from,cur);
45 var select = cm.getRange(from,cur);
45 if( select.match(/^\ +$/) !== null){
46 if( select.match(/^\ +$/) !== null){
46 cm.replaceRange("",from,cur);
47 cm.replaceRange("",from,cur);
47 } else {
48 } else {
48 cm.deleteH(-1,"char");
49 cm.deleteH(-1,"char");
49 }
50 }
50 };
51 };
51
52
52 var keycodes = keyboard.keycodes;
53 var keycodes = keyboard.keycodes;
53
54
54 var CodeCell = function (kernel, options) {
55 var CodeCell = function (kernel, options) {
55 // Constructor
56 // Constructor
56 //
57 //
57 // A Cell conceived to write code.
58 // A Cell conceived to write code.
58 //
59 //
59 // Parameters:
60 // Parameters:
60 // kernel: Kernel instance
61 // kernel: Kernel instance
61 // The kernel doesn't have to be set at creation time, in that case
62 // The kernel doesn't have to be set at creation time, in that case
62 // it will be null and set_kernel has to be called later.
63 // it will be null and set_kernel has to be called later.
63 // options: dictionary
64 // options: dictionary
64 // Dictionary of keyword arguments.
65 // Dictionary of keyword arguments.
65 // events: $(Events) instance
66 // events: $(Events) instance
66 // config: dictionary
67 // config: dictionary
67 // keyboard_manager: KeyboardManager instance
68 // keyboard_manager: KeyboardManager instance
68 // notebook: Notebook instance
69 // notebook: Notebook instance
69 // tooltip: Tooltip instance
70 // tooltip: Tooltip instance
70 this.kernel = kernel || null;
71 this.kernel = kernel || null;
71 this.notebook = options.notebook;
72 this.notebook = options.notebook;
72 this.collapsed = false;
73 this.collapsed = false;
73 this.events = options.events;
74 this.events = options.events;
74 this.tooltip = options.tooltip;
75 this.tooltip = options.tooltip;
75 this.config = options.config;
76 this.config = options.config;
76
77
77 // create all attributed in constructor function
78 // create all attributed in constructor function
78 // even if null for V8 VM optimisation
79 // even if null for V8 VM optimisation
79 this.input_prompt_number = null;
80 this.input_prompt_number = null;
80 this.celltoolbar = null;
81 this.celltoolbar = null;
81 this.output_area = null;
82 this.output_area = null;
83 // Keep a stack of the 'active' output areas (where active means the
84 // output area that recieves output). When a user activates an output
85 // area, it gets pushed to the stack. Then, when the output area is
86 // deactivated, it's popped from the stack. When the stack is empty,
87 // the cell's output area is used.
88 this.active_output_areas = [];
89 var that = this;
90 Object.defineProperty(this, 'active_output_area', {
91 get: function() {
92 if (that.active_output_areas && that.active_output_areas.length > 0) {
93 return that.active_output_areas[that.active_output_areas.length-1];
94 } else {
95 return that.output_area;
96 }
97 },
98 });
99
82 this.last_msg_id = null;
100 this.last_msg_id = null;
83 this.completer = null;
101 this.completer = null;
84
102
85
103
86 var config = utils.mergeopt(CodeCell, this.config);
104 var config = utils.mergeopt(CodeCell, this.config);
87 Cell.apply(this,[{
105 Cell.apply(this,[{
88 config: config,
106 config: config,
89 keyboard_manager: options.keyboard_manager,
107 keyboard_manager: options.keyboard_manager,
90 events: this.events}]);
108 events: this.events}]);
91
109
92 // Attributes we want to override in this subclass.
110 // Attributes we want to override in this subclass.
93 this.cell_type = "code";
111 this.cell_type = "code";
94
95 var that = this;
96 this.element.focusout(
112 this.element.focusout(
97 function() { that.auto_highlight(); }
113 function() { that.auto_highlight(); }
98 );
114 );
99 };
115 };
100
116
101 CodeCell.options_default = {
117 CodeCell.options_default = {
102 cm_config : {
118 cm_config : {
103 extraKeys: {
119 extraKeys: {
104 "Tab" : "indentMore",
120 "Tab" : "indentMore",
105 "Shift-Tab" : "indentLess",
121 "Shift-Tab" : "indentLess",
106 "Backspace" : "delSpaceToPrevTabStop",
122 "Backspace" : "delSpaceToPrevTabStop",
107 "Cmd-/" : "toggleComment",
123 "Cmd-/" : "toggleComment",
108 "Ctrl-/" : "toggleComment"
124 "Ctrl-/" : "toggleComment"
109 },
125 },
110 mode: 'ipython',
126 mode: 'ipython',
111 theme: 'ipython',
127 theme: 'ipython',
112 matchBrackets: true
128 matchBrackets: true
113 }
129 }
114 };
130 };
115
131
116 CodeCell.msg_cells = {};
132 CodeCell.msg_cells = {};
117
133
118 CodeCell.prototype = Object.create(Cell.prototype);
134 CodeCell.prototype = Object.create(Cell.prototype);
119
135
120 /**
136 /**
137 * @method push_output_area
138 */
139 CodeCell.prototype.push_output_area = function (output_area) {
140 this.active_output_areas.push(output_area);
141 };
142
143 /**
144 * @method pop_output_area
145 */
146 CodeCell.prototype.pop_output_area = function (output_area) {
147 var index = this.active_output_areas.lastIndexOf(output_area);
148 if (index > -1) {
149 this.active_output_areas.splice(index, 1);
150 }
151 };
152
153 /**
121 * @method auto_highlight
154 * @method auto_highlight
122 */
155 */
123 CodeCell.prototype.auto_highlight = function () {
156 CodeCell.prototype.auto_highlight = function () {
124 this._auto_highlight(this.config.cell_magic_highlight);
157 this._auto_highlight(this.config.cell_magic_highlight);
125 };
158 };
126
159
127 /** @method create_element */
160 /** @method create_element */
128 CodeCell.prototype.create_element = function () {
161 CodeCell.prototype.create_element = function () {
129 Cell.prototype.create_element.apply(this, arguments);
162 Cell.prototype.create_element.apply(this, arguments);
130
163
131 var cell = $('<div></div>').addClass('cell code_cell');
164 var cell = $('<div></div>').addClass('cell code_cell');
132 cell.attr('tabindex','2');
165 cell.attr('tabindex','2');
133
166
134 var input = $('<div></div>').addClass('input');
167 var input = $('<div></div>').addClass('input');
135 var prompt = $('<div/>').addClass('prompt input_prompt');
168 var prompt = $('<div/>').addClass('prompt input_prompt');
136 var inner_cell = $('<div/>').addClass('inner_cell');
169 var inner_cell = $('<div/>').addClass('inner_cell');
137 this.celltoolbar = new celltoolbar.CellToolbar({
170 this.celltoolbar = new celltoolbar.CellToolbar({
138 cell: this,
171 cell: this,
139 notebook: this.notebook});
172 notebook: this.notebook});
140 inner_cell.append(this.celltoolbar.element);
173 inner_cell.append(this.celltoolbar.element);
141 var input_area = $('<div/>').addClass('input_area');
174 var input_area = $('<div/>').addClass('input_area');
142 this.code_mirror = new CodeMirror(input_area.get(0), this.cm_config);
175 this.code_mirror = new CodeMirror(input_area.get(0), this.cm_config);
143 this.code_mirror.on('keydown', $.proxy(this.handle_keyevent,this))
176 this.code_mirror.on('keydown', $.proxy(this.handle_keyevent,this))
144 $(this.code_mirror.getInputField()).attr("spellcheck", "false");
177 $(this.code_mirror.getInputField()).attr("spellcheck", "false");
145 inner_cell.append(input_area);
178 inner_cell.append(input_area);
146 input.append(prompt).append(inner_cell);
179 input.append(prompt).append(inner_cell);
147
180
148 var widget_area = $('<div/>')
181 var widget_area = $('<div/>')
149 .addClass('widget-area')
182 .addClass('widget-area')
150 .hide();
183 .hide();
151 this.widget_area = widget_area;
184 this.widget_area = widget_area;
152 var widget_prompt = $('<div/>')
185 var widget_prompt = $('<div/>')
153 .addClass('prompt')
186 .addClass('prompt')
154 .appendTo(widget_area);
187 .appendTo(widget_area);
155 var widget_subarea = $('<div/>')
188 var widget_subarea = $('<div/>')
156 .addClass('widget-subarea')
189 .addClass('widget-subarea')
157 .appendTo(widget_area);
190 .appendTo(widget_area);
158 this.widget_subarea = widget_subarea;
191 this.widget_subarea = widget_subarea;
159 var widget_clear_buton = $('<button />')
192 var widget_clear_buton = $('<button />')
160 .addClass('close')
193 .addClass('close')
161 .html('&times;')
194 .html('&times;')
162 .click(function() {
195 .click(function() {
163 widget_area.slideUp('', function(){ widget_subarea.html(''); });
196 widget_area.slideUp('', function(){ widget_subarea.html(''); });
164 })
197 })
165 .appendTo(widget_prompt);
198 .appendTo(widget_prompt);
166
199
167 var output = $('<div></div>');
200 var output = $('<div></div>');
168 cell.append(input).append(widget_area).append(output);
201 cell.append(input).append(widget_area).append(output);
169 this.element = cell;
202 this.element = cell;
170 this.output_area = new outputarea.OutputArea({
203 this.output_area = new outputarea.OutputArea({
171 selector: output,
204 selector: output,
172 prompt_area: true,
205 prompt_area: true,
173 events: this.events,
206 events: this.events,
174 keyboard_manager: this.keyboard_manager});
207 keyboard_manager: this.keyboard_manager});
175 this.completer = new completer.Completer(this, this.events);
208 this.completer = new completer.Completer(this, this.events);
176 };
209 };
177
210
178 /** @method bind_events */
211 /** @method bind_events */
179 CodeCell.prototype.bind_events = function () {
212 CodeCell.prototype.bind_events = function () {
180 Cell.prototype.bind_events.apply(this);
213 Cell.prototype.bind_events.apply(this);
181 var that = this;
214 var that = this;
182
215
183 this.element.focusout(
216 this.element.focusout(
184 function() { that.auto_highlight(); }
217 function() { that.auto_highlight(); }
185 );
218 );
186 };
219 };
187
220
188
221
189 /**
222 /**
190 * This method gets called in CodeMirror's onKeyDown/onKeyPress
223 * This method gets called in CodeMirror's onKeyDown/onKeyPress
191 * handlers and is used to provide custom key handling. Its return
224 * handlers and is used to provide custom key handling. Its return
192 * value is used to determine if CodeMirror should ignore the event:
225 * value is used to determine if CodeMirror should ignore the event:
193 * true = ignore, false = don't ignore.
226 * true = ignore, false = don't ignore.
194 * @method handle_codemirror_keyevent
227 * @method handle_codemirror_keyevent
195 */
228 */
196 CodeCell.prototype.handle_codemirror_keyevent = function (editor, event) {
229 CodeCell.prototype.handle_codemirror_keyevent = function (editor, event) {
197
230
198 var that = this;
231 var that = this;
199 // whatever key is pressed, first, cancel the tooltip request before
232 // whatever key is pressed, first, cancel the tooltip request before
200 // they are sent, and remove tooltip if any, except for tab again
233 // they are sent, and remove tooltip if any, except for tab again
201 var tooltip_closed = null;
234 var tooltip_closed = null;
202 if (event.type === 'keydown' && event.which != keycodes.tab ) {
235 if (event.type === 'keydown' && event.which != keycodes.tab ) {
203 tooltip_closed = this.tooltip.remove_and_cancel_tooltip();
236 tooltip_closed = this.tooltip.remove_and_cancel_tooltip();
204 }
237 }
205
238
206 var cur = editor.getCursor();
239 var cur = editor.getCursor();
207 if (event.keyCode === keycodes.enter){
240 if (event.keyCode === keycodes.enter){
208 this.auto_highlight();
241 this.auto_highlight();
209 }
242 }
210
243
211 if (event.which === keycodes.down && event.type === 'keypress' && this.tooltip.time_before_tooltip >= 0) {
244 if (event.which === keycodes.down && event.type === 'keypress' && this.tooltip.time_before_tooltip >= 0) {
212 // triger on keypress (!) otherwise inconsistent event.which depending on plateform
245 // triger on keypress (!) otherwise inconsistent event.which depending on plateform
213 // browser and keyboard layout !
246 // browser and keyboard layout !
214 // Pressing '(' , request tooltip, don't forget to reappend it
247 // Pressing '(' , request tooltip, don't forget to reappend it
215 // The second argument says to hide the tooltip if the docstring
248 // The second argument says to hide the tooltip if the docstring
216 // is actually empty
249 // is actually empty
217 this.tooltip.pending(that, true);
250 this.tooltip.pending(that, true);
218 } else if ( tooltip_closed && event.which === keycodes.esc && event.type === 'keydown') {
251 } else if ( tooltip_closed && event.which === keycodes.esc && event.type === 'keydown') {
219 // If tooltip is active, cancel it. The call to
252 // If tooltip is active, cancel it. The call to
220 // remove_and_cancel_tooltip above doesn't pass, force=true.
253 // remove_and_cancel_tooltip above doesn't pass, force=true.
221 // Because of this it won't actually close the tooltip
254 // Because of this it won't actually close the tooltip
222 // if it is in sticky mode. Thus, we have to check again if it is open
255 // if it is in sticky mode. Thus, we have to check again if it is open
223 // and close it with force=true.
256 // and close it with force=true.
224 if (!this.tooltip._hidden) {
257 if (!this.tooltip._hidden) {
225 this.tooltip.remove_and_cancel_tooltip(true);
258 this.tooltip.remove_and_cancel_tooltip(true);
226 }
259 }
227 // If we closed the tooltip, don't let CM or the global handlers
260 // If we closed the tooltip, don't let CM or the global handlers
228 // handle this event.
261 // handle this event.
229 event.codemirrorIgnore = true;
262 event.codemirrorIgnore = true;
230 event.preventDefault();
263 event.preventDefault();
231 return true;
264 return true;
232 } else if (event.keyCode === keycodes.tab && event.type === 'keydown' && event.shiftKey) {
265 } else if (event.keyCode === keycodes.tab && event.type === 'keydown' && event.shiftKey) {
233 if (editor.somethingSelected() || editor.getSelections().length !== 1){
266 if (editor.somethingSelected() || editor.getSelections().length !== 1){
234 var anchor = editor.getCursor("anchor");
267 var anchor = editor.getCursor("anchor");
235 var head = editor.getCursor("head");
268 var head = editor.getCursor("head");
236 if( anchor.line != head.line){
269 if( anchor.line != head.line){
237 return false;
270 return false;
238 }
271 }
239 }
272 }
240 this.tooltip.request(that);
273 this.tooltip.request(that);
241 event.codemirrorIgnore = true;
274 event.codemirrorIgnore = true;
242 event.preventDefault();
275 event.preventDefault();
243 return true;
276 return true;
244 } else if (event.keyCode === keycodes.tab && event.type == 'keydown') {
277 } else if (event.keyCode === keycodes.tab && event.type == 'keydown') {
245 // Tab completion.
278 // Tab completion.
246 this.tooltip.remove_and_cancel_tooltip();
279 this.tooltip.remove_and_cancel_tooltip();
247
280
248 // completion does not work on multicursor, it might be possible though in some cases
281 // completion does not work on multicursor, it might be possible though in some cases
249 if (editor.somethingSelected() || editor.getSelections().length > 1) {
282 if (editor.somethingSelected() || editor.getSelections().length > 1) {
250 return false;
283 return false;
251 }
284 }
252 var pre_cursor = editor.getRange({line:cur.line,ch:0},cur);
285 var pre_cursor = editor.getRange({line:cur.line,ch:0},cur);
253 if (pre_cursor.trim() === "") {
286 if (pre_cursor.trim() === "") {
254 // Don't autocomplete if the part of the line before the cursor
287 // Don't autocomplete if the part of the line before the cursor
255 // is empty. In this case, let CodeMirror handle indentation.
288 // is empty. In this case, let CodeMirror handle indentation.
256 return false;
289 return false;
257 } else {
290 } else {
258 event.codemirrorIgnore = true;
291 event.codemirrorIgnore = true;
259 event.preventDefault();
292 event.preventDefault();
260 this.completer.startCompletion();
293 this.completer.startCompletion();
261 return true;
294 return true;
262 }
295 }
263 }
296 }
264
297
265 // keyboard event wasn't one of those unique to code cells, let's see
298 // keyboard event wasn't one of those unique to code cells, let's see
266 // if it's one of the generic ones (i.e. check edit mode shortcuts)
299 // if it's one of the generic ones (i.e. check edit mode shortcuts)
267 return Cell.prototype.handle_codemirror_keyevent.apply(this, [editor, event]);
300 return Cell.prototype.handle_codemirror_keyevent.apply(this, [editor, event]);
268 };
301 };
269
302
270 // Kernel related calls.
303 // Kernel related calls.
271
304
272 CodeCell.prototype.set_kernel = function (kernel) {
305 CodeCell.prototype.set_kernel = function (kernel) {
273 this.kernel = kernel;
306 this.kernel = kernel;
274 };
307 };
275
308
276 /**
309 /**
277 * Execute current code cell to the kernel
310 * Execute current code cell to the kernel
278 * @method execute
311 * @method execute
279 */
312 */
280 CodeCell.prototype.execute = function () {
313 CodeCell.prototype.execute = function () {
281 if (!this.kernel || !this.kernel.is_connected()) {
314 if (!this.kernel || !this.kernel.is_connected()) {
282 console.log("Can't execute, kernel is not connected.");
315 console.log("Can't execute, kernel is not connected.");
283 return;
316 return;
284 }
317 }
285
318
286 this.output_area.clear_output();
319 this.active_output_area.clear_output();
287
320
288 // Clear widget area
321 // Clear widget area
289 this.widget_subarea.html('');
322 this.widget_subarea.html('');
290 this.widget_subarea.height('');
323 this.widget_subarea.height('');
291 this.widget_area.height('');
324 this.widget_area.height('');
292 this.widget_area.hide();
325 this.widget_area.hide();
293
326
294 this.set_input_prompt('*');
327 this.set_input_prompt('*');
295 this.element.addClass("running");
328 this.element.addClass("running");
296 if (this.last_msg_id) {
329 if (this.last_msg_id) {
297 this.kernel.clear_callbacks_for_msg(this.last_msg_id);
330 this.kernel.clear_callbacks_for_msg(this.last_msg_id);
298 }
331 }
299 var callbacks = this.get_callbacks();
332 var callbacks = this.get_callbacks();
300
333
301 var old_msg_id = this.last_msg_id;
334 var old_msg_id = this.last_msg_id;
302 this.last_msg_id = this.kernel.execute(this.get_text(), callbacks, {silent: false, store_history: true});
335 this.last_msg_id = this.kernel.execute(this.get_text(), callbacks, {silent: false, store_history: true});
303 if (old_msg_id) {
336 if (old_msg_id) {
304 delete CodeCell.msg_cells[old_msg_id];
337 delete CodeCell.msg_cells[old_msg_id];
305 }
338 }
306 CodeCell.msg_cells[this.last_msg_id] = this;
339 CodeCell.msg_cells[this.last_msg_id] = this;
307 this.render();
340 this.render();
308 };
341 };
309
342
310 /**
343 /**
311 * Construct the default callbacks for
344 * Construct the default callbacks for
312 * @method get_callbacks
345 * @method get_callbacks
313 */
346 */
314 CodeCell.prototype.get_callbacks = function () {
347 CodeCell.prototype.get_callbacks = function () {
348 var that = this;
315 return {
349 return {
316 shell : {
350 shell : {
317 reply : $.proxy(this._handle_execute_reply, this),
351 reply : $.proxy(this._handle_execute_reply, this),
318 payload : {
352 payload : {
319 set_next_input : $.proxy(this._handle_set_next_input, this),
353 set_next_input : $.proxy(this._handle_set_next_input, this),
320 page : $.proxy(this._open_with_pager, this)
354 page : $.proxy(this._open_with_pager, this)
321 }
355 }
322 },
356 },
323 iopub : {
357 iopub : {
324 output : $.proxy(this.output_area.handle_output, this.output_area),
358 output : function() {
325 clear_output : $.proxy(this.output_area.handle_clear_output, this.output_area),
359 that.active_output_area.handle_output.apply(that.active_output_area, arguments);
360 },
361 clear_output : function() {
362 that.active_output_area.handle_clear_output.apply(that.active_output_area, arguments);
363 },
326 },
364 },
327 input : $.proxy(this._handle_input_request, this)
365 input : $.proxy(this._handle_input_request, this)
328 };
366 };
329 };
367 };
330
368
331 CodeCell.prototype._open_with_pager = function (payload) {
369 CodeCell.prototype._open_with_pager = function (payload) {
332 this.events.trigger('open_with_text.Pager', payload);
370 this.events.trigger('open_with_text.Pager', payload);
333 };
371 };
334
372
335 /**
373 /**
336 * @method _handle_execute_reply
374 * @method _handle_execute_reply
337 * @private
375 * @private
338 */
376 */
339 CodeCell.prototype._handle_execute_reply = function (msg) {
377 CodeCell.prototype._handle_execute_reply = function (msg) {
340 this.set_input_prompt(msg.content.execution_count);
378 this.set_input_prompt(msg.content.execution_count);
341 this.element.removeClass("running");
379 this.element.removeClass("running");
342 this.events.trigger('set_dirty.Notebook', {value: true});
380 this.events.trigger('set_dirty.Notebook', {value: true});
343 };
381 };
344
382
345 /**
383 /**
346 * @method _handle_set_next_input
384 * @method _handle_set_next_input
347 * @private
385 * @private
348 */
386 */
349 CodeCell.prototype._handle_set_next_input = function (payload) {
387 CodeCell.prototype._handle_set_next_input = function (payload) {
350 var data = {'cell': this, 'text': payload.text};
388 var data = {'cell': this, 'text': payload.text};
351 this.events.trigger('set_next_input.Notebook', data);
389 this.events.trigger('set_next_input.Notebook', data);
352 };
390 };
353
391
354 /**
392 /**
355 * @method _handle_input_request
393 * @method _handle_input_request
356 * @private
394 * @private
357 */
395 */
358 CodeCell.prototype._handle_input_request = function (msg) {
396 CodeCell.prototype._handle_input_request = function (msg) {
359 this.output_area.append_raw_input(msg);
397 this.active_output_area.append_raw_input(msg);
360 };
398 };
361
399
362
400
363 // Basic cell manipulation.
401 // Basic cell manipulation.
364
402
365 CodeCell.prototype.select = function () {
403 CodeCell.prototype.select = function () {
366 var cont = Cell.prototype.select.apply(this);
404 var cont = Cell.prototype.select.apply(this);
367 if (cont) {
405 if (cont) {
368 this.code_mirror.refresh();
406 this.code_mirror.refresh();
369 this.auto_highlight();
407 this.auto_highlight();
370 }
408 }
371 return cont;
409 return cont;
372 };
410 };
373
411
374 CodeCell.prototype.render = function () {
412 CodeCell.prototype.render = function () {
375 var cont = Cell.prototype.render.apply(this);
413 var cont = Cell.prototype.render.apply(this);
376 // Always execute, even if we are already in the rendered state
414 // Always execute, even if we are already in the rendered state
377 return cont;
415 return cont;
378 };
416 };
379
417
380 CodeCell.prototype.select_all = function () {
418 CodeCell.prototype.select_all = function () {
381 var start = {line: 0, ch: 0};
419 var start = {line: 0, ch: 0};
382 var nlines = this.code_mirror.lineCount();
420 var nlines = this.code_mirror.lineCount();
383 var last_line = this.code_mirror.getLine(nlines-1);
421 var last_line = this.code_mirror.getLine(nlines-1);
384 var end = {line: nlines-1, ch: last_line.length};
422 var end = {line: nlines-1, ch: last_line.length};
385 this.code_mirror.setSelection(start, end);
423 this.code_mirror.setSelection(start, end);
386 };
424 };
387
425
388
426
389 CodeCell.prototype.collapse_output = function () {
427 CodeCell.prototype.collapse_output = function () {
390 this.output_area.collapse();
428 this.output_area.collapse();
391 };
429 };
392
430
393
431
394 CodeCell.prototype.expand_output = function () {
432 CodeCell.prototype.expand_output = function () {
395 this.output_area.expand();
433 this.output_area.expand();
396 this.output_area.unscroll_area();
434 this.output_area.unscroll_area();
397 };
435 };
398
436
399 CodeCell.prototype.scroll_output = function () {
437 CodeCell.prototype.scroll_output = function () {
400 this.output_area.expand();
438 this.output_area.expand();
401 this.output_area.scroll_if_long();
439 this.output_area.scroll_if_long();
402 };
440 };
403
441
404 CodeCell.prototype.toggle_output = function () {
442 CodeCell.prototype.toggle_output = function () {
405 this.output_area.toggle_output();
443 this.output_area.toggle_output();
406 };
444 };
407
445
408 CodeCell.prototype.toggle_output_scroll = function () {
446 CodeCell.prototype.toggle_output_scroll = function () {
409 this.output_area.toggle_scroll();
447 this.output_area.toggle_scroll();
410 };
448 };
411
449
412
450
413 CodeCell.input_prompt_classical = function (prompt_value, lines_number) {
451 CodeCell.input_prompt_classical = function (prompt_value, lines_number) {
414 var ns;
452 var ns;
415 if (prompt_value === undefined || prompt_value === null) {
453 if (prompt_value === undefined || prompt_value === null) {
416 ns = "&nbsp;";
454 ns = "&nbsp;";
417 } else {
455 } else {
418 ns = encodeURIComponent(prompt_value);
456 ns = encodeURIComponent(prompt_value);
419 }
457 }
420 return 'In&nbsp;[' + ns + ']:';
458 return 'In&nbsp;[' + ns + ']:';
421 };
459 };
422
460
423 CodeCell.input_prompt_continuation = function (prompt_value, lines_number) {
461 CodeCell.input_prompt_continuation = function (prompt_value, lines_number) {
424 var html = [CodeCell.input_prompt_classical(prompt_value, lines_number)];
462 var html = [CodeCell.input_prompt_classical(prompt_value, lines_number)];
425 for(var i=1; i < lines_number; i++) {
463 for(var i=1; i < lines_number; i++) {
426 html.push(['...:']);
464 html.push(['...:']);
427 }
465 }
428 return html.join('<br/>');
466 return html.join('<br/>');
429 };
467 };
430
468
431 CodeCell.input_prompt_function = CodeCell.input_prompt_classical;
469 CodeCell.input_prompt_function = CodeCell.input_prompt_classical;
432
470
433
471
434 CodeCell.prototype.set_input_prompt = function (number) {
472 CodeCell.prototype.set_input_prompt = function (number) {
435 var nline = 1;
473 var nline = 1;
436 if (this.code_mirror !== undefined) {
474 if (this.code_mirror !== undefined) {
437 nline = this.code_mirror.lineCount();
475 nline = this.code_mirror.lineCount();
438 }
476 }
439 this.input_prompt_number = number;
477 this.input_prompt_number = number;
440 var prompt_html = CodeCell.input_prompt_function(this.input_prompt_number, nline);
478 var prompt_html = CodeCell.input_prompt_function(this.input_prompt_number, nline);
441 // This HTML call is okay because the user contents are escaped.
479 // This HTML call is okay because the user contents are escaped.
442 this.element.find('div.input_prompt').html(prompt_html);
480 this.element.find('div.input_prompt').html(prompt_html);
443 };
481 };
444
482
445
483
446 CodeCell.prototype.clear_input = function () {
484 CodeCell.prototype.clear_input = function () {
447 this.code_mirror.setValue('');
485 this.code_mirror.setValue('');
448 };
486 };
449
487
450
488
451 CodeCell.prototype.get_text = function () {
489 CodeCell.prototype.get_text = function () {
452 return this.code_mirror.getValue();
490 return this.code_mirror.getValue();
453 };
491 };
454
492
455
493
456 CodeCell.prototype.set_text = function (code) {
494 CodeCell.prototype.set_text = function (code) {
457 return this.code_mirror.setValue(code);
495 return this.code_mirror.setValue(code);
458 };
496 };
459
497
460
498
461 CodeCell.prototype.clear_output = function (wait) {
499 CodeCell.prototype.clear_output = function (wait) {
462 this.output_area.clear_output(wait);
500 this.active_output_area.clear_output(wait);
463 this.set_input_prompt();
501 this.set_input_prompt();
464 };
502 };
465
503
466
504
467 // JSON serialization
505 // JSON serialization
468
506
469 CodeCell.prototype.fromJSON = function (data) {
507 CodeCell.prototype.fromJSON = function (data) {
470 Cell.prototype.fromJSON.apply(this, arguments);
508 Cell.prototype.fromJSON.apply(this, arguments);
471 if (data.cell_type === 'code') {
509 if (data.cell_type === 'code') {
472 if (data.source !== undefined) {
510 if (data.source !== undefined) {
473 this.set_text(data.source);
511 this.set_text(data.source);
474 // make this value the starting point, so that we can only undo
512 // make this value the starting point, so that we can only undo
475 // to this state, instead of a blank cell
513 // to this state, instead of a blank cell
476 this.code_mirror.clearHistory();
514 this.code_mirror.clearHistory();
477 this.auto_highlight();
515 this.auto_highlight();
478 }
516 }
479 this.set_input_prompt(data.execution_count);
517 this.set_input_prompt(data.execution_count);
480 this.output_area.trusted = data.metadata.trusted || false;
518 this.output_area.trusted = data.metadata.trusted || false;
481 this.output_area.fromJSON(data.outputs);
519 this.output_area.fromJSON(data.outputs);
482 if (data.metadata.collapsed !== undefined) {
520 if (data.metadata.collapsed !== undefined) {
483 if (data.metadata.collapsed) {
521 if (data.metadata.collapsed) {
484 this.collapse_output();
522 this.collapse_output();
485 } else {
523 } else {
486 this.expand_output();
524 this.expand_output();
487 }
525 }
488 }
526 }
489 }
527 }
490 };
528 };
491
529
492
530
493 CodeCell.prototype.toJSON = function () {
531 CodeCell.prototype.toJSON = function () {
494 var data = Cell.prototype.toJSON.apply(this);
532 var data = Cell.prototype.toJSON.apply(this);
495 data.source = this.get_text();
533 data.source = this.get_text();
496 // is finite protect against undefined and '*' value
534 // is finite protect against undefined and '*' value
497 if (isFinite(this.input_prompt_number)) {
535 if (isFinite(this.input_prompt_number)) {
498 data.execution_count = this.input_prompt_number;
536 data.execution_count = this.input_prompt_number;
499 } else {
537 } else {
500 data.execution_count = null;
538 data.execution_count = null;
501 }
539 }
502 var outputs = this.output_area.toJSON();
540 var outputs = this.output_area.toJSON();
503 data.outputs = outputs;
541 data.outputs = outputs;
504 data.metadata.trusted = this.output_area.trusted;
542 data.metadata.trusted = this.output_area.trusted;
505 data.metadata.collapsed = this.output_area.collapsed;
543 data.metadata.collapsed = this.output_area.collapsed;
506 return data;
544 return data;
507 };
545 };
508
546
509 /**
547 /**
510 * handle cell level logic when a cell is unselected
548 * handle cell level logic when a cell is unselected
511 * @method unselect
549 * @method unselect
512 * @return is the action being taken
550 * @return is the action being taken
513 */
551 */
514 CodeCell.prototype.unselect = function () {
552 CodeCell.prototype.unselect = function () {
515 var cont = Cell.prototype.unselect.apply(this);
553 var cont = Cell.prototype.unselect.apply(this);
516 if (cont) {
554 if (cont) {
517 // When a code cell is usnelected, make sure that the corresponding
555 // When a code cell is usnelected, make sure that the corresponding
518 // tooltip and completer to that cell is closed.
556 // tooltip and completer to that cell is closed.
519 this.tooltip.remove_and_cancel_tooltip(true);
557 this.tooltip.remove_and_cancel_tooltip(true);
520 if (this.completer !== null) {
558 if (this.completer !== null) {
521 this.completer.close();
559 this.completer.close();
522 }
560 }
523 }
561 }
524 return cont;
562 return cont;
525 };
563 };
526
564
527 // Backwards compatability.
565 // Backwards compatability.
528 IPython.CodeCell = CodeCell;
566 IPython.CodeCell = CodeCell;
529
567
530 return {'CodeCell': CodeCell};
568 return {'CodeCell': CodeCell};
531 });
569 });
@@ -1,932 +1,939 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 if (window.MathJax){
202 if (window.MathJax){
203 MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
203 MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
204 }
204 }
205 };
205 };
206
206
207
207
208 OutputArea.prototype.handle_output = function (msg) {
208 OutputArea.prototype.handle_output = function (msg) {
209 var json = {};
209 var json = {};
210 var msg_type = json.output_type = msg.header.msg_type;
210 var msg_type = json.output_type = msg.header.msg_type;
211 var content = msg.content;
211 var content = msg.content;
212 if (msg_type === "stream") {
212 if (msg_type === "stream") {
213 json.text = content.text;
213 json.text = content.text;
214 json.name = content.name;
214 json.name = content.name;
215 } else if (msg_type === "display_data") {
215 } else if (msg_type === "display_data") {
216 json.data = content.data;
216 json.data = content.data;
217 json.output_type = msg_type;
217 json.output_type = msg_type;
218 json.metadata = content.metadata;
218 json.metadata = content.metadata;
219 } else if (msg_type === "execute_result") {
219 } else if (msg_type === "execute_result") {
220 json.data = content.data;
220 json.data = content.data;
221 json.output_type = msg_type;
221 json.output_type = msg_type;
222 json.metadata = content.metadata;
222 json.metadata = content.metadata;
223 json.execution_count = content.execution_count;
223 json.execution_count = content.execution_count;
224 } else if (msg_type === "error") {
224 } else if (msg_type === "error") {
225 json.ename = content.ename;
225 json.ename = content.ename;
226 json.evalue = content.evalue;
226 json.evalue = content.evalue;
227 json.traceback = content.traceback;
227 json.traceback = content.traceback;
228 } else {
228 } else {
229 console.log("unhandled output message", msg);
229 console.log("unhandled output message", msg);
230 return;
230 return;
231 }
231 }
232 this.append_output(json);
232 this.append_output(json);
233 };
233 };
234
234
235
235
236 OutputArea.output_types = [
236 OutputArea.output_types = [
237 'application/javascript',
237 'application/javascript',
238 'text/html',
238 'text/html',
239 'text/markdown',
239 'text/markdown',
240 'text/latex',
240 'text/latex',
241 'image/svg+xml',
241 'image/svg+xml',
242 'image/png',
242 'image/png',
243 'image/jpeg',
243 'image/jpeg',
244 'application/pdf',
244 'application/pdf',
245 'text/plain'
245 'text/plain'
246 ];
246 ];
247
247
248 OutputArea.prototype.validate_output = function (json) {
248 OutputArea.prototype.validate_output = function (json) {
249 // scrub invalid outputs
249 // scrub invalid outputs
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 // validate output data types
266 // validate output data types
267 if (json.data) {
267 if (json.data) {
268 json = this.validate_output(json);
268 json = this.validate_output(json);
269 }
269 }
270
270
271 // Clear the output if clear is queued.
271 // Clear the output if clear is queued.
272 var needs_height_reset = false;
272 var needs_height_reset = false;
273 if (this.clear_queued) {
273 if (this.clear_queued) {
274 this.clear_output(false);
274 this.clear_output(false);
275 needs_height_reset = true;
275 needs_height_reset = true;
276 }
276 }
277
277
278 var record_output = true;
278 var record_output = true;
279
279
280 if (json.output_type === 'execute_result') {
280 if (json.output_type === 'execute_result') {
281 this.append_execute_result(json);
281 this.append_execute_result(json);
282 } else if (json.output_type === 'error') {
282 } else if (json.output_type === 'error') {
283 this.append_error(json);
283 this.append_error(json);
284 } else if (json.output_type === 'stream') {
284 } else if (json.output_type === 'stream') {
285 // append_stream might have merged the output with earlier stream output
285 // append_stream might have merged the output with earlier stream output
286 record_output = this.append_stream(json);
286 record_output = this.append_stream(json);
287 }
287 }
288
288
289 // We must release the animation fixed height in a callback since Gecko
289 // We must release the animation fixed height in a callback since Gecko
290 // (FireFox) doesn't render the image immediately as the data is
290 // (FireFox) doesn't render the image immediately as the data is
291 // available.
291 // available.
292 var that = this;
292 var that = this;
293 var handle_appended = function ($el) {
293 var handle_appended = function ($el) {
294 // Only reset the height to automatic if the height is currently
294 // Only reset the height to automatic if the height is currently
295 // fixed (done by wait=True flag on clear_output).
295 // fixed (done by wait=True flag on clear_output).
296 if (needs_height_reset) {
296 if (needs_height_reset) {
297 that.element.height('');
297 that.element.height('');
298 }
298 }
299 that.element.trigger('resize');
299 that.element.trigger('resize');
300 };
300 };
301 if (json.output_type === 'display_data') {
301 if (json.output_type === 'display_data') {
302 this.append_display_data(json, handle_appended);
302 this.append_display_data(json, handle_appended);
303 } else {
303 } else {
304 handle_appended();
304 handle_appended();
305 }
305 }
306
306
307 if (record_output) {
307 if (record_output) {
308 this.outputs.push(json);
308 this.outputs.push(json);
309 }
309 }
310 };
310 };
311
311
312
312
313 OutputArea.prototype.create_output_area = function () {
313 OutputArea.prototype.create_output_area = function () {
314 var oa = $("<div/>").addClass("output_area");
314 var oa = $("<div/>").addClass("output_area");
315 if (this.prompt_area) {
315 if (this.prompt_area) {
316 oa.append($('<div/>').addClass('prompt'));
316 oa.append($('<div/>').addClass('prompt'));
317 }
317 }
318 return oa;
318 return oa;
319 };
319 };
320
320
321
321
322 function _get_metadata_key(metadata, key, mime) {
322 function _get_metadata_key(metadata, key, mime) {
323 var mime_md = metadata[mime];
323 var mime_md = metadata[mime];
324 // mime-specific higher priority
324 // mime-specific higher priority
325 if (mime_md && mime_md[key] !== undefined) {
325 if (mime_md && mime_md[key] !== undefined) {
326 return mime_md[key];
326 return mime_md[key];
327 }
327 }
328 // fallback on global
328 // fallback on global
329 return metadata[key];
329 return metadata[key];
330 }
330 }
331
331
332 OutputArea.prototype.create_output_subarea = function(md, classes, mime) {
332 OutputArea.prototype.create_output_subarea = function(md, classes, mime) {
333 var subarea = $('<div/>').addClass('output_subarea').addClass(classes);
333 var subarea = $('<div/>').addClass('output_subarea').addClass(classes);
334 if (_get_metadata_key(md, 'isolated', mime)) {
334 if (_get_metadata_key(md, 'isolated', mime)) {
335 // Create an iframe to isolate the subarea from the rest of the
335 // Create an iframe to isolate the subarea from the rest of the
336 // document
336 // document
337 var iframe = $('<iframe/>').addClass('box-flex1');
337 var iframe = $('<iframe/>').addClass('box-flex1');
338 iframe.css({'height':1, 'width':'100%', 'display':'block'});
338 iframe.css({'height':1, 'width':'100%', 'display':'block'});
339 iframe.attr('frameborder', 0);
339 iframe.attr('frameborder', 0);
340 iframe.attr('scrolling', 'auto');
340 iframe.attr('scrolling', 'auto');
341
341
342 // Once the iframe is loaded, the subarea is dynamically inserted
342 // Once the iframe is loaded, the subarea is dynamically inserted
343 iframe.on('load', function() {
343 iframe.on('load', function() {
344 // Workaround needed by Firefox, to properly render svg inside
344 // Workaround needed by Firefox, to properly render svg inside
345 // iframes, see http://stackoverflow.com/questions/10177190/
345 // iframes, see http://stackoverflow.com/questions/10177190/
346 // svg-dynamically-added-to-iframe-does-not-render-correctly
346 // svg-dynamically-added-to-iframe-does-not-render-correctly
347 this.contentDocument.open();
347 this.contentDocument.open();
348
348
349 // Insert the subarea into the iframe
349 // Insert the subarea into the iframe
350 // We must directly write the html. When using Jquery's append
350 // We must directly write the html. When using Jquery's append
351 // method, javascript is evaluated in the parent document and
351 // method, javascript is evaluated in the parent document and
352 // not in the iframe document. At this point, subarea doesn't
352 // not in the iframe document. At this point, subarea doesn't
353 // contain any user content.
353 // contain any user content.
354 this.contentDocument.write(subarea.html());
354 this.contentDocument.write(subarea.html());
355
355
356 this.contentDocument.close();
356 this.contentDocument.close();
357
357
358 var body = this.contentDocument.body;
358 var body = this.contentDocument.body;
359 // Adjust the iframe height automatically
359 // Adjust the iframe height automatically
360 iframe.height(body.scrollHeight + 'px');
360 iframe.height(body.scrollHeight + 'px');
361 });
361 });
362
362
363 // Elements should be appended to the inner subarea and not to the
363 // Elements should be appended to the inner subarea and not to the
364 // iframe
364 // iframe
365 iframe.append = function(that) {
365 iframe.append = function(that) {
366 subarea.append(that);
366 subarea.append(that);
367 };
367 };
368
368
369 return iframe;
369 return iframe;
370 } else {
370 } else {
371 return subarea;
371 return subarea;
372 }
372 }
373 };
373 };
374
374
375
375
376 OutputArea.prototype._append_javascript_error = function (err, element) {
376 OutputArea.prototype._append_javascript_error = function (err, element) {
377 // display a message when a javascript error occurs in display output
377 // display a message when a javascript error occurs in display output
378 var msg = "Javascript error adding output!";
378 var msg = "Javascript error adding output!";
379 if ( element === undefined ) return;
379 if ( element === undefined ) return;
380 element
380 element
381 .append($('<div/>').text(msg).addClass('js-error'))
381 .append($('<div/>').text(msg).addClass('js-error'))
382 .append($('<div/>').text(err.toString()).addClass('js-error'))
382 .append($('<div/>').text(err.toString()).addClass('js-error'))
383 .append($('<div/>').text('See your browser Javascript console for more details.').addClass('js-error'));
383 .append($('<div/>').text('See your browser Javascript console for more details.').addClass('js-error'));
384 };
384 };
385
385
386 OutputArea.prototype._safe_append = function (toinsert) {
386 OutputArea.prototype._safe_append = function (toinsert) {
387 // safely append an item to the document
387 // safely append an item to the document
388 // this is an object created by user code,
388 // this is an object created by user code,
389 // and may have errors, which should not be raised
389 // and may have errors, which should not be raised
390 // under any circumstances.
390 // under any circumstances.
391 try {
391 try {
392 this.element.append(toinsert);
392 this.element.append(toinsert);
393 } catch(err) {
393 } catch(err) {
394 console.log(err);
394 console.log(err);
395 // Create an actual output_area and output_subarea, which creates
395 // Create an actual output_area and output_subarea, which creates
396 // the prompt area and the proper indentation.
396 // the prompt area and the proper indentation.
397 var toinsert = this.create_output_area();
397 var toinsert = this.create_output_area();
398 var subarea = $('<div/>').addClass('output_subarea');
398 var subarea = $('<div/>').addClass('output_subarea');
399 toinsert.append(subarea);
399 toinsert.append(subarea);
400 this._append_javascript_error(err, subarea);
400 this._append_javascript_error(err, subarea);
401 this.element.append(toinsert);
401 this.element.append(toinsert);
402 }
402 }
403
404 // Notify others of changes.
405 this.element.trigger('changed');
403 };
406 };
404
407
405
408
406 OutputArea.prototype.append_execute_result = function (json) {
409 OutputArea.prototype.append_execute_result = function (json) {
407 var n = json.execution_count || ' ';
410 var n = json.execution_count || ' ';
408 var toinsert = this.create_output_area();
411 var toinsert = this.create_output_area();
409 if (this.prompt_area) {
412 if (this.prompt_area) {
410 toinsert.find('div.prompt').addClass('output_prompt').text('Out[' + n + ']:');
413 toinsert.find('div.prompt').addClass('output_prompt').text('Out[' + n + ']:');
411 }
414 }
412 var inserted = this.append_mime_type(json, toinsert);
415 var inserted = this.append_mime_type(json, toinsert);
413 if (inserted) {
416 if (inserted) {
414 inserted.addClass('output_result');
417 inserted.addClass('output_result');
415 }
418 }
416 this._safe_append(toinsert);
419 this._safe_append(toinsert);
417 // If we just output latex, typeset it.
420 // If we just output latex, typeset it.
418 if ((json['text/latex'] !== undefined) ||
421 if ((json['text/latex'] !== undefined) ||
419 (json['text/html'] !== undefined) ||
422 (json['text/html'] !== undefined) ||
420 (json['text/markdown'] !== undefined)) {
423 (json['text/markdown'] !== undefined)) {
421 this.typeset();
424 this.typeset();
422 }
425 }
423 };
426 };
424
427
425
428
426 OutputArea.prototype.append_error = function (json) {
429 OutputArea.prototype.append_error = function (json) {
427 var tb = json.traceback;
430 var tb = json.traceback;
428 if (tb !== undefined && tb.length > 0) {
431 if (tb !== undefined && tb.length > 0) {
429 var s = '';
432 var s = '';
430 var len = tb.length;
433 var len = tb.length;
431 for (var i=0; i<len; i++) {
434 for (var i=0; i<len; i++) {
432 s = s + tb[i] + '\n';
435 s = s + tb[i] + '\n';
433 }
436 }
434 s = s + '\n';
437 s = s + '\n';
435 var toinsert = this.create_output_area();
438 var toinsert = this.create_output_area();
436 var append_text = OutputArea.append_map['text/plain'];
439 var append_text = OutputArea.append_map['text/plain'];
437 if (append_text) {
440 if (append_text) {
438 append_text.apply(this, [s, {}, toinsert]).addClass('output_error');
441 append_text.apply(this, [s, {}, toinsert]).addClass('output_error');
439 }
442 }
440 this._safe_append(toinsert);
443 this._safe_append(toinsert);
441 }
444 }
442 };
445 };
443
446
444
447
445 OutputArea.prototype.append_stream = function (json) {
448 OutputArea.prototype.append_stream = function (json) {
446 var text = json.text;
449 var text = json.text;
447 var subclass = "output_"+json.name;
450 var subclass = "output_"+json.name;
448 if (this.outputs.length > 0){
451 if (this.outputs.length > 0){
449 // have at least one output to consider
452 // have at least one output to consider
450 var last = this.outputs[this.outputs.length-1];
453 var last = this.outputs[this.outputs.length-1];
451 if (last.output_type == 'stream' && json.name == last.name){
454 if (last.output_type == 'stream' && json.name == last.name){
452 // latest output was in the same stream,
455 // latest output was in the same stream,
453 // so append directly into its pre tag
456 // so append directly into its pre tag
454 // escape ANSI & HTML specials:
457 // escape ANSI & HTML specials:
455 last.text = utils.fixCarriageReturn(last.text + json.text);
458 last.text = utils.fixCarriageReturn(last.text + json.text);
456 var pre = this.element.find('div.'+subclass).last().find('pre');
459 var pre = this.element.find('div.'+subclass).last().find('pre');
457 var html = utils.fixConsole(last.text);
460 var html = utils.fixConsole(last.text);
458 // The only user content injected with this HTML call is
461 // The only user content injected with this HTML call is
459 // escaped by the fixConsole() method.
462 // escaped by the fixConsole() method.
460 pre.html(html);
463 pre.html(html);
461 // return false signals that we merged this output with the previous one,
464 // return false signals that we merged this output with the previous one,
462 // and the new output shouldn't be recorded.
465 // and the new output shouldn't be recorded.
463 return false;
466 return false;
464 }
467 }
465 }
468 }
466
469
467 if (!text.replace("\r", "")) {
470 if (!text.replace("\r", "")) {
468 // text is nothing (empty string, \r, etc.)
471 // text is nothing (empty string, \r, etc.)
469 // so don't append any elements, which might add undesirable space
472 // so don't append any elements, which might add undesirable space
470 // return true to indicate the output should be recorded.
473 // return true to indicate the output should be recorded.
471 return true;
474 return true;
472 }
475 }
473
476
474 // If we got here, attach a new div
477 // If we got here, attach a new div
475 var toinsert = this.create_output_area();
478 var toinsert = this.create_output_area();
476 var append_text = OutputArea.append_map['text/plain'];
479 var append_text = OutputArea.append_map['text/plain'];
477 if (append_text) {
480 if (append_text) {
478 append_text.apply(this, [text, {}, toinsert]).addClass("output_stream " + subclass);
481 append_text.apply(this, [text, {}, toinsert]).addClass("output_stream " + subclass);
479 }
482 }
480 this._safe_append(toinsert);
483 this._safe_append(toinsert);
481 return true;
484 return true;
482 };
485 };
483
486
484
487
485 OutputArea.prototype.append_display_data = function (json, handle_inserted) {
488 OutputArea.prototype.append_display_data = function (json, handle_inserted) {
486 var toinsert = this.create_output_area();
489 var toinsert = this.create_output_area();
487 if (this.append_mime_type(json, toinsert, handle_inserted)) {
490 if (this.append_mime_type(json, toinsert, handle_inserted)) {
488 this._safe_append(toinsert);
491 this._safe_append(toinsert);
489 // If we just output latex, typeset it.
492 // If we just output latex, typeset it.
490 if ((json['text/latex'] !== undefined) ||
493 if ((json['text/latex'] !== undefined) ||
491 (json['text/html'] !== undefined) ||
494 (json['text/html'] !== undefined) ||
492 (json['text/markdown'] !== undefined)) {
495 (json['text/markdown'] !== undefined)) {
493 this.typeset();
496 this.typeset();
494 }
497 }
495 }
498 }
496 };
499 };
497
500
498
501
499 OutputArea.safe_outputs = {
502 OutputArea.safe_outputs = {
500 'text/plain' : true,
503 'text/plain' : true,
501 'text/latex' : true,
504 'text/latex' : true,
502 'image/png' : true,
505 'image/png' : true,
503 'image/jpeg' : true
506 'image/jpeg' : true
504 };
507 };
505
508
506 OutputArea.prototype.append_mime_type = function (json, element, handle_inserted) {
509 OutputArea.prototype.append_mime_type = function (json, element, handle_inserted) {
507 for (var i=0; i < OutputArea.display_order.length; i++) {
510 for (var i=0; i < OutputArea.display_order.length; i++) {
508 var type = OutputArea.display_order[i];
511 var type = OutputArea.display_order[i];
509 var append = OutputArea.append_map[type];
512 var append = OutputArea.append_map[type];
510 if ((json.data[type] !== undefined) && append) {
513 if ((json.data[type] !== undefined) && append) {
511 var value = json.data[type];
514 var value = json.data[type];
512 if (!this.trusted && !OutputArea.safe_outputs[type]) {
515 if (!this.trusted && !OutputArea.safe_outputs[type]) {
513 // not trusted, sanitize HTML
516 // not trusted, sanitize HTML
514 if (type==='text/html' || type==='text/svg') {
517 if (type==='text/html' || type==='text/svg') {
515 value = security.sanitize_html(value);
518 value = security.sanitize_html(value);
516 } else {
519 } else {
517 // don't display if we don't know how to sanitize it
520 // don't display if we don't know how to sanitize it
518 console.log("Ignoring untrusted " + type + " output.");
521 console.log("Ignoring untrusted " + type + " output.");
519 continue;
522 continue;
520 }
523 }
521 }
524 }
522 var md = json.metadata || {};
525 var md = json.metadata || {};
523 var toinsert = append.apply(this, [value, md, element, handle_inserted]);
526 var toinsert = append.apply(this, [value, md, element, handle_inserted]);
524 // Since only the png and jpeg mime types call the inserted
527 // Since only the png and jpeg mime types call the inserted
525 // callback, if the mime type is something other we must call the
528 // callback, if the mime type is something other we must call the
526 // inserted callback only when the element is actually inserted
529 // inserted callback only when the element is actually inserted
527 // into the DOM. Use a timeout of 0 to do this.
530 // into the DOM. Use a timeout of 0 to do this.
528 if (['image/png', 'image/jpeg'].indexOf(type) < 0 && handle_inserted !== undefined) {
531 if (['image/png', 'image/jpeg'].indexOf(type) < 0 && handle_inserted !== undefined) {
529 setTimeout(handle_inserted, 0);
532 setTimeout(handle_inserted, 0);
530 }
533 }
531 this.events.trigger('output_appended.OutputArea', [type, value, md, toinsert]);
534 this.events.trigger('output_appended.OutputArea', [type, value, md, toinsert]);
532 return toinsert;
535 return toinsert;
533 }
536 }
534 }
537 }
535 return null;
538 return null;
536 };
539 };
537
540
538
541
539 var append_html = function (html, md, element) {
542 var append_html = function (html, md, element) {
540 var type = 'text/html';
543 var type = 'text/html';
541 var toinsert = this.create_output_subarea(md, "output_html rendered_html", type);
544 var toinsert = this.create_output_subarea(md, "output_html rendered_html", type);
542 this.keyboard_manager.register_events(toinsert);
545 this.keyboard_manager.register_events(toinsert);
543 toinsert.append(html);
546 toinsert.append(html);
544 element.append(toinsert);
547 element.append(toinsert);
545 return toinsert;
548 return toinsert;
546 };
549 };
547
550
548
551
549 var append_markdown = function(markdown, md, element) {
552 var append_markdown = function(markdown, md, element) {
550 var type = 'text/markdown';
553 var type = 'text/markdown';
551 var toinsert = this.create_output_subarea(md, "output_markdown", type);
554 var toinsert = this.create_output_subarea(md, "output_markdown", type);
552 var text_and_math = mathjaxutils.remove_math(markdown);
555 var text_and_math = mathjaxutils.remove_math(markdown);
553 var text = text_and_math[0];
556 var text = text_and_math[0];
554 var math = text_and_math[1];
557 var math = text_and_math[1];
555 marked(text, function (err, html) {
558 marked(text, function (err, html) {
556 html = mathjaxutils.replace_math(html, math);
559 html = mathjaxutils.replace_math(html, math);
557 toinsert.append(html);
560 toinsert.append(html);
558 });
561 });
559 element.append(toinsert);
562 element.append(toinsert);
560 return toinsert;
563 return toinsert;
561 };
564 };
562
565
563
566
564 var append_javascript = function (js, md, element) {
567 var append_javascript = function (js, md, element) {
565 // We just eval the JS code, element appears in the local scope.
568 // We just eval the JS code, element appears in the local scope.
566 var type = 'application/javascript';
569 var type = 'application/javascript';
567 var toinsert = this.create_output_subarea(md, "output_javascript", type);
570 var toinsert = this.create_output_subarea(md, "output_javascript", type);
568 this.keyboard_manager.register_events(toinsert);
571 this.keyboard_manager.register_events(toinsert);
569 element.append(toinsert);
572 element.append(toinsert);
570
573
571 // Fix for ipython/issues/5293, make sure `element` is the area which
574 // Fix for ipython/issues/5293, make sure `element` is the area which
572 // output can be inserted into at the time of JS execution.
575 // output can be inserted into at the time of JS execution.
573 element = toinsert;
576 element = toinsert;
574 try {
577 try {
575 eval(js);
578 eval(js);
576 } catch(err) {
579 } catch(err) {
577 console.log(err);
580 console.log(err);
578 this._append_javascript_error(err, toinsert);
581 this._append_javascript_error(err, toinsert);
579 }
582 }
580 return toinsert;
583 return toinsert;
581 };
584 };
582
585
583
586
584 var append_text = function (data, md, element) {
587 var append_text = function (data, md, element) {
585 var type = 'text/plain';
588 var type = 'text/plain';
586 var toinsert = this.create_output_subarea(md, "output_text", type);
589 var toinsert = this.create_output_subarea(md, "output_text", type);
587 // escape ANSI & HTML specials in plaintext:
590 // escape ANSI & HTML specials in plaintext:
588 data = utils.fixConsole(data);
591 data = utils.fixConsole(data);
589 data = utils.fixCarriageReturn(data);
592 data = utils.fixCarriageReturn(data);
590 data = utils.autoLinkUrls(data);
593 data = utils.autoLinkUrls(data);
591 // The only user content injected with this HTML call is
594 // The only user content injected with this HTML call is
592 // escaped by the fixConsole() method.
595 // escaped by the fixConsole() method.
593 toinsert.append($("<pre/>").html(data));
596 toinsert.append($("<pre/>").html(data));
594 element.append(toinsert);
597 element.append(toinsert);
595 return toinsert;
598 return toinsert;
596 };
599 };
597
600
598
601
599 var append_svg = function (svg_html, md, element) {
602 var append_svg = function (svg_html, md, element) {
600 var type = 'image/svg+xml';
603 var type = 'image/svg+xml';
601 var toinsert = this.create_output_subarea(md, "output_svg", type);
604 var toinsert = this.create_output_subarea(md, "output_svg", type);
602
605
603 // Get the svg element from within the HTML.
606 // Get the svg element from within the HTML.
604 var svg = $('<div />').html(svg_html).find('svg');
607 var svg = $('<div />').html(svg_html).find('svg');
605 var svg_area = $('<div />');
608 var svg_area = $('<div />');
606 var width = svg.attr('width');
609 var width = svg.attr('width');
607 var height = svg.attr('height');
610 var height = svg.attr('height');
608 svg
611 svg
609 .width('100%')
612 .width('100%')
610 .height('100%');
613 .height('100%');
611 svg_area
614 svg_area
612 .width(width)
615 .width(width)
613 .height(height);
616 .height(height);
614
617
615 // The jQuery resize handlers don't seem to work on the svg element.
618 // The jQuery resize handlers don't seem to work on the svg element.
616 // When the svg renders completely, measure it's size and set the parent
619 // When the svg renders completely, measure it's size and set the parent
617 // div to that size. Then set the svg to 100% the size of the parent
620 // div to that size. Then set the svg to 100% the size of the parent
618 // div and make the parent div resizable.
621 // div and make the parent div resizable.
619 this._dblclick_to_reset_size(svg_area, true, false);
622 this._dblclick_to_reset_size(svg_area, true, false);
620
623
621 svg_area.append(svg);
624 svg_area.append(svg);
622 toinsert.append(svg_area);
625 toinsert.append(svg_area);
623 element.append(toinsert);
626 element.append(toinsert);
624
627
625 return toinsert;
628 return toinsert;
626 };
629 };
627
630
628 OutputArea.prototype._dblclick_to_reset_size = function (img, immediately, resize_parent) {
631 OutputArea.prototype._dblclick_to_reset_size = function (img, immediately, resize_parent) {
629 // Add a resize handler to an element
632 // Add a resize handler to an element
630 //
633 //
631 // img: jQuery element
634 // img: jQuery element
632 // immediately: bool=False
635 // immediately: bool=False
633 // Wait for the element to load before creating the handle.
636 // Wait for the element to load before creating the handle.
634 // resize_parent: bool=True
637 // resize_parent: bool=True
635 // Should the parent of the element be resized when the element is
638 // Should the parent of the element be resized when the element is
636 // reset (by double click).
639 // reset (by double click).
637 var callback = function (){
640 var callback = function (){
638 var h0 = img.height();
641 var h0 = img.height();
639 var w0 = img.width();
642 var w0 = img.width();
640 if (!(h0 && w0)) {
643 if (!(h0 && w0)) {
641 // zero size, don't make it resizable
644 // zero size, don't make it resizable
642 return;
645 return;
643 }
646 }
644 img.resizable({
647 img.resizable({
645 aspectRatio: true,
648 aspectRatio: true,
646 autoHide: true
649 autoHide: true
647 });
650 });
648 img.dblclick(function () {
651 img.dblclick(function () {
649 // resize wrapper & image together for some reason:
652 // resize wrapper & image together for some reason:
650 img.height(h0);
653 img.height(h0);
651 img.width(w0);
654 img.width(w0);
652 if (resize_parent === undefined || resize_parent) {
655 if (resize_parent === undefined || resize_parent) {
653 img.parent().height(h0);
656 img.parent().height(h0);
654 img.parent().width(w0);
657 img.parent().width(w0);
655 }
658 }
656 });
659 });
657 };
660 };
658
661
659 if (immediately) {
662 if (immediately) {
660 callback();
663 callback();
661 } else {
664 } else {
662 img.on("load", callback);
665 img.on("load", callback);
663 }
666 }
664 };
667 };
665
668
666 var set_width_height = function (img, md, mime) {
669 var set_width_height = function (img, md, mime) {
667 // set width and height of an img element from metadata
670 // set width and height of an img element from metadata
668 var height = _get_metadata_key(md, 'height', mime);
671 var height = _get_metadata_key(md, 'height', mime);
669 if (height !== undefined) img.attr('height', height);
672 if (height !== undefined) img.attr('height', height);
670 var width = _get_metadata_key(md, 'width', mime);
673 var width = _get_metadata_key(md, 'width', mime);
671 if (width !== undefined) img.attr('width', width);
674 if (width !== undefined) img.attr('width', width);
672 };
675 };
673
676
674 var append_png = function (png, md, element, handle_inserted) {
677 var append_png = function (png, md, element, handle_inserted) {
675 var type = 'image/png';
678 var type = 'image/png';
676 var toinsert = this.create_output_subarea(md, "output_png", type);
679 var toinsert = this.create_output_subarea(md, "output_png", type);
677 var img = $("<img/>");
680 var img = $("<img/>");
678 if (handle_inserted !== undefined) {
681 if (handle_inserted !== undefined) {
679 img.on('load', function(){
682 img.on('load', function(){
680 handle_inserted(img);
683 handle_inserted(img);
681 });
684 });
682 }
685 }
683 img[0].src = 'data:image/png;base64,'+ png;
686 img[0].src = 'data:image/png;base64,'+ png;
684 set_width_height(img, md, 'image/png');
687 set_width_height(img, md, 'image/png');
685 this._dblclick_to_reset_size(img);
688 this._dblclick_to_reset_size(img);
686 toinsert.append(img);
689 toinsert.append(img);
687 element.append(toinsert);
690 element.append(toinsert);
688 return toinsert;
691 return toinsert;
689 };
692 };
690
693
691
694
692 var append_jpeg = function (jpeg, md, element, handle_inserted) {
695 var append_jpeg = function (jpeg, md, element, handle_inserted) {
693 var type = 'image/jpeg';
696 var type = 'image/jpeg';
694 var toinsert = this.create_output_subarea(md, "output_jpeg", type);
697 var toinsert = this.create_output_subarea(md, "output_jpeg", type);
695 var img = $("<img/>");
698 var img = $("<img/>");
696 if (handle_inserted !== undefined) {
699 if (handle_inserted !== undefined) {
697 img.on('load', function(){
700 img.on('load', function(){
698 handle_inserted(img);
701 handle_inserted(img);
699 });
702 });
700 }
703 }
701 img[0].src = 'data:image/jpeg;base64,'+ jpeg;
704 img[0].src = 'data:image/jpeg;base64,'+ jpeg;
702 set_width_height(img, md, 'image/jpeg');
705 set_width_height(img, md, 'image/jpeg');
703 this._dblclick_to_reset_size(img);
706 this._dblclick_to_reset_size(img);
704 toinsert.append(img);
707 toinsert.append(img);
705 element.append(toinsert);
708 element.append(toinsert);
706 return toinsert;
709 return toinsert;
707 };
710 };
708
711
709
712
710 var append_pdf = function (pdf, md, element) {
713 var append_pdf = function (pdf, md, element) {
711 var type = 'application/pdf';
714 var type = 'application/pdf';
712 var toinsert = this.create_output_subarea(md, "output_pdf", type);
715 var toinsert = this.create_output_subarea(md, "output_pdf", type);
713 var a = $('<a/>').attr('href', 'data:application/pdf;base64,'+pdf);
716 var a = $('<a/>').attr('href', 'data:application/pdf;base64,'+pdf);
714 a.attr('target', '_blank');
717 a.attr('target', '_blank');
715 a.text('View PDF');
718 a.text('View PDF');
716 toinsert.append(a);
719 toinsert.append(a);
717 element.append(toinsert);
720 element.append(toinsert);
718 return toinsert;
721 return toinsert;
719 };
722 };
720
723
721 var append_latex = function (latex, md, element) {
724 var append_latex = function (latex, md, element) {
722 // This method cannot do the typesetting because the latex first has to
725 // This method cannot do the typesetting because the latex first has to
723 // be on the page.
726 // be on the page.
724 var type = 'text/latex';
727 var type = 'text/latex';
725 var toinsert = this.create_output_subarea(md, "output_latex", type);
728 var toinsert = this.create_output_subarea(md, "output_latex", type);
726 toinsert.append(latex);
729 toinsert.append(latex);
727 element.append(toinsert);
730 element.append(toinsert);
728 return toinsert;
731 return toinsert;
729 };
732 };
730
733
731
734
732 OutputArea.prototype.append_raw_input = function (msg) {
735 OutputArea.prototype.append_raw_input = function (msg) {
733 var that = this;
736 var that = this;
734 this.expand();
737 this.expand();
735 var content = msg.content;
738 var content = msg.content;
736 var area = this.create_output_area();
739 var area = this.create_output_area();
737
740
738 // disable any other raw_inputs, if they are left around
741 // disable any other raw_inputs, if they are left around
739 $("div.output_subarea.raw_input_container").remove();
742 $("div.output_subarea.raw_input_container").remove();
740
743
741 var input_type = content.password ? 'password' : 'text';
744 var input_type = content.password ? 'password' : 'text';
742
745
743 area.append(
746 area.append(
744 $("<div/>")
747 $("<div/>")
745 .addClass("box-flex1 output_subarea raw_input_container")
748 .addClass("box-flex1 output_subarea raw_input_container")
746 .append(
749 .append(
747 $("<span/>")
750 $("<span/>")
748 .addClass("raw_input_prompt")
751 .addClass("raw_input_prompt")
749 .text(content.prompt)
752 .text(content.prompt)
750 )
753 )
751 .append(
754 .append(
752 $("<input/>")
755 $("<input/>")
753 .addClass("raw_input")
756 .addClass("raw_input")
754 .attr('type', input_type)
757 .attr('type', input_type)
755 .attr("size", 47)
758 .attr("size", 47)
756 .keydown(function (event, ui) {
759 .keydown(function (event, ui) {
757 // make sure we submit on enter,
760 // make sure we submit on enter,
758 // and don't re-execute the *cell* on shift-enter
761 // and don't re-execute the *cell* on shift-enter
759 if (event.which === keyboard.keycodes.enter) {
762 if (event.which === keyboard.keycodes.enter) {
760 that._submit_raw_input();
763 that._submit_raw_input();
761 return false;
764 return false;
762 }
765 }
763 })
766 })
764 )
767 )
765 );
768 );
766
769
767 this.element.append(area);
770 this.element.append(area);
768 var raw_input = area.find('input.raw_input');
771 var raw_input = area.find('input.raw_input');
769 // Register events that enable/disable the keyboard manager while raw
772 // Register events that enable/disable the keyboard manager while raw
770 // input is focused.
773 // input is focused.
771 this.keyboard_manager.register_events(raw_input);
774 this.keyboard_manager.register_events(raw_input);
772 // Note, the following line used to read raw_input.focus().focus().
775 // Note, the following line used to read raw_input.focus().focus().
773 // This seemed to be needed otherwise only the cell would be focused.
776 // This seemed to be needed otherwise only the cell would be focused.
774 // But with the modal UI, this seems to work fine with one call to focus().
777 // But with the modal UI, this seems to work fine with one call to focus().
775 raw_input.focus();
778 raw_input.focus();
776 };
779 };
777
780
778 OutputArea.prototype._submit_raw_input = function (evt) {
781 OutputArea.prototype._submit_raw_input = function (evt) {
779 var container = this.element.find("div.raw_input_container");
782 var container = this.element.find("div.raw_input_container");
780 var theprompt = container.find("span.raw_input_prompt");
783 var theprompt = container.find("span.raw_input_prompt");
781 var theinput = container.find("input.raw_input");
784 var theinput = container.find("input.raw_input");
782 var value = theinput.val();
785 var value = theinput.val();
783 var echo = value;
786 var echo = value;
784 // don't echo if it's a password
787 // don't echo if it's a password
785 if (theinput.attr('type') == 'password') {
788 if (theinput.attr('type') == 'password') {
786 echo = 'Β·Β·Β·Β·Β·Β·Β·Β·';
789 echo = 'Β·Β·Β·Β·Β·Β·Β·Β·';
787 }
790 }
788 var content = {
791 var content = {
789 output_type : 'stream',
792 output_type : 'stream',
790 stream : 'stdout',
793 stream : 'stdout',
791 text : theprompt.text() + echo + '\n'
794 text : theprompt.text() + echo + '\n'
792 };
795 };
793 // remove form container
796 // remove form container
794 container.parent().remove();
797 container.parent().remove();
795 // replace with plaintext version in stdout
798 // replace with plaintext version in stdout
796 this.append_output(content, false);
799 this.append_output(content, false);
797 this.events.trigger('send_input_reply.Kernel', value);
800 this.events.trigger('send_input_reply.Kernel', value);
798 };
801 };
799
802
800
803
801 OutputArea.prototype.handle_clear_output = function (msg) {
804 OutputArea.prototype.handle_clear_output = function (msg) {
802 // msg spec v4 had stdout, stderr, display keys
805 // msg spec v4 had stdout, stderr, display keys
803 // v4.1 replaced these with just wait
806 // v4.1 replaced these with just wait
804 // The default behavior is the same (stdout=stderr=display=True, wait=False),
807 // The default behavior is the same (stdout=stderr=display=True, wait=False),
805 // so v4 messages will still be properly handled,
808 // so v4 messages will still be properly handled,
806 // except for the rarely used clearing less than all output.
809 // except for the rarely used clearing less than all output.
807 this.clear_output(msg.content.wait || false);
810 this.clear_output(msg.content.wait || false);
808 };
811 };
809
812
810
813
811 OutputArea.prototype.clear_output = function(wait) {
814 OutputArea.prototype.clear_output = function(wait) {
812 if (wait) {
815 if (wait) {
813
816
814 // If a clear is queued, clear before adding another to the queue.
817 // If a clear is queued, clear before adding another to the queue.
815 if (this.clear_queued) {
818 if (this.clear_queued) {
816 this.clear_output(false);
819 this.clear_output(false);
817 }
820 }
818
821
819 this.clear_queued = true;
822 this.clear_queued = true;
820 } else {
823 } else {
821
824
822 // Fix the output div's height if the clear_output is waiting for
825 // Fix the output div's height if the clear_output is waiting for
823 // new output (it is being used in an animation).
826 // new output (it is being used in an animation).
824 if (this.clear_queued) {
827 if (this.clear_queued) {
825 var height = this.element.height();
828 var height = this.element.height();
826 this.element.height(height);
829 this.element.height(height);
827 this.clear_queued = false;
830 this.clear_queued = false;
828 }
831 }
829
832
830 // Clear all
833 // Clear all
831 // Remove load event handlers from img tags because we don't want
834 // Remove load event handlers from img tags because we don't want
832 // them to fire if the image is never added to the page.
835 // them to fire if the image is never added to the page.
833 this.element.find('img').off('load');
836 this.element.find('img').off('load');
834 this.element.html("");
837 this.element.html("");
838
839 // Notify others of changes.
840 this.element.trigger('changed');
841
835 this.outputs = [];
842 this.outputs = [];
836 this.trusted = true;
843 this.trusted = true;
837 this.unscroll_area();
844 this.unscroll_area();
838 return;
845 return;
839 }
846 }
840 };
847 };
841
848
842
849
843 // JSON serialization
850 // JSON serialization
844
851
845 OutputArea.prototype.fromJSON = function (outputs, metadata) {
852 OutputArea.prototype.fromJSON = function (outputs, metadata) {
846 var len = outputs.length;
853 var len = outputs.length;
847 metadata = metadata || {};
854 metadata = metadata || {};
848
855
849 for (var i=0; i<len; i++) {
856 for (var i=0; i<len; i++) {
850 this.append_output(outputs[i]);
857 this.append_output(outputs[i]);
851 }
858 }
852
859
853 if (metadata.collapsed !== undefined) {
860 if (metadata.collapsed !== undefined) {
854 this.collapsed = metadata.collapsed;
861 this.collapsed = metadata.collapsed;
855 if (metadata.collapsed) {
862 if (metadata.collapsed) {
856 this.collapse_output();
863 this.collapse_output();
857 }
864 }
858 }
865 }
859 if (metadata.autoscroll !== undefined) {
866 if (metadata.autoscroll !== undefined) {
860 this.collapsed = metadata.collapsed;
867 this.collapsed = metadata.collapsed;
861 if (metadata.collapsed) {
868 if (metadata.collapsed) {
862 this.collapse_output();
869 this.collapse_output();
863 } else {
870 } else {
864 this.expand_output();
871 this.expand_output();
865 }
872 }
866 }
873 }
867 };
874 };
868
875
869
876
870 OutputArea.prototype.toJSON = function () {
877 OutputArea.prototype.toJSON = function () {
871 return this.outputs;
878 return this.outputs;
872 };
879 };
873
880
874 /**
881 /**
875 * Class properties
882 * Class properties
876 **/
883 **/
877
884
878 /**
885 /**
879 * Threshold to trigger autoscroll when the OutputArea is resized,
886 * Threshold to trigger autoscroll when the OutputArea is resized,
880 * typically when new outputs are added.
887 * typically when new outputs are added.
881 *
888 *
882 * Behavior is undefined if autoscroll is lower than minimum_scroll_threshold,
889 * Behavior is undefined if autoscroll is lower than minimum_scroll_threshold,
883 * unless it is < 0, in which case autoscroll will never be triggered
890 * unless it is < 0, in which case autoscroll will never be triggered
884 *
891 *
885 * @property auto_scroll_threshold
892 * @property auto_scroll_threshold
886 * @type Number
893 * @type Number
887 * @default 100
894 * @default 100
888 *
895 *
889 **/
896 **/
890 OutputArea.auto_scroll_threshold = 100;
897 OutputArea.auto_scroll_threshold = 100;
891
898
892 /**
899 /**
893 * Lower limit (in lines) for OutputArea to be made scrollable. OutputAreas
900 * Lower limit (in lines) for OutputArea to be made scrollable. OutputAreas
894 * shorter than this are never scrolled.
901 * shorter than this are never scrolled.
895 *
902 *
896 * @property minimum_scroll_threshold
903 * @property minimum_scroll_threshold
897 * @type Number
904 * @type Number
898 * @default 20
905 * @default 20
899 *
906 *
900 **/
907 **/
901 OutputArea.minimum_scroll_threshold = 20;
908 OutputArea.minimum_scroll_threshold = 20;
902
909
903
910
904 OutputArea.display_order = [
911 OutputArea.display_order = [
905 'application/javascript',
912 'application/javascript',
906 'text/html',
913 'text/html',
907 'text/markdown',
914 'text/markdown',
908 'text/latex',
915 'text/latex',
909 'image/svg+xml',
916 'image/svg+xml',
910 'image/png',
917 'image/png',
911 'image/jpeg',
918 'image/jpeg',
912 'application/pdf',
919 'application/pdf',
913 'text/plain'
920 'text/plain'
914 ];
921 ];
915
922
916 OutputArea.append_map = {
923 OutputArea.append_map = {
917 "text/plain" : append_text,
924 "text/plain" : append_text,
918 "text/html" : append_html,
925 "text/html" : append_html,
919 "text/markdown": append_markdown,
926 "text/markdown": append_markdown,
920 "image/svg+xml" : append_svg,
927 "image/svg+xml" : append_svg,
921 "image/png" : append_png,
928 "image/png" : append_png,
922 "image/jpeg" : append_jpeg,
929 "image/jpeg" : append_jpeg,
923 "text/latex" : append_latex,
930 "text/latex" : append_latex,
924 "application/javascript" : append_javascript,
931 "application/javascript" : append_javascript,
925 "application/pdf" : append_pdf
932 "application/pdf" : append_pdf
926 };
933 };
927
934
928 // For backwards compatability.
935 // For backwards compatability.
929 IPython.OutputArea = OutputArea;
936 IPython.OutputArea = OutputArea;
930
937
931 return {'OutputArea': OutputArea};
938 return {'OutputArea': OutputArea};
932 });
939 });
@@ -1,27 +1,28 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 "widgets/js/manager",
5 "widgets/js/manager",
6 "widgets/js/widget_bool",
6 "widgets/js/widget_bool",
7 "widgets/js/widget_button",
7 "widgets/js/widget_button",
8 "widgets/js/widget_box",
8 "widgets/js/widget_box",
9 "widgets/js/widget_float",
9 "widgets/js/widget_float",
10 "widgets/js/widget_image",
10 "widgets/js/widget_image",
11 "widgets/js/widget_int",
11 "widgets/js/widget_int",
12 "widgets/js/widget_output",
12 "widgets/js/widget_selection",
13 "widgets/js/widget_selection",
13 "widgets/js/widget_selectioncontainer",
14 "widgets/js/widget_selectioncontainer",
14 "widgets/js/widget_string",
15 "widgets/js/widget_string",
15 ], function(widgetmanager) {
16 ], function(widgetmanager) {
16
17
17 // Register all of the loaded views with the widget manager.
18 // Register all of the loaded views with the widget manager.
18 for (var i = 1; i < arguments.length; i++) {
19 for (var i = 1; i < arguments.length; i++) {
19 for (var target_name in arguments[i]) {
20 for (var target_name in arguments[i]) {
20 if (arguments[i].hasOwnProperty(target_name)) {
21 if (arguments[i].hasOwnProperty(target_name)) {
21 widgetmanager.WidgetManager.register_widget_view(target_name, arguments[i][target_name]);
22 widgetmanager.WidgetManager.register_widget_view(target_name, arguments[i][target_name]);
22 }
23 }
23 }
24 }
24 }
25 }
25
26
26 return {'WidgetManager': widgetmanager.WidgetManager};
27 return {'WidgetManager': widgetmanager.WidgetManager};
27 });
28 });
@@ -1,23 +1,24 b''
1 from .widget import Widget, DOMWidget, CallbackDispatcher, register
1 from .widget import Widget, DOMWidget, CallbackDispatcher, register
2
2
3 from .widget_bool import Checkbox, ToggleButton
3 from .widget_bool import Checkbox, ToggleButton
4 from .widget_button import Button
4 from .widget_button import Button
5 from .widget_box import Box, Popup, FlexBox, HBox, VBox
5 from .widget_box import Box, Popup, FlexBox, HBox, VBox
6 from .widget_float import FloatText, BoundedFloatText, FloatSlider, FloatProgress, FloatRangeSlider
6 from .widget_float import FloatText, BoundedFloatText, FloatSlider, FloatProgress, FloatRangeSlider
7 from .widget_image import Image
7 from .widget_image import Image
8 from .widget_int import IntText, BoundedIntText, IntSlider, IntProgress, IntRangeSlider
8 from .widget_int import IntText, BoundedIntText, IntSlider, IntProgress, IntRangeSlider
9 from .widget_output import Output
9 from .widget_selection import RadioButtons, ToggleButtons, Dropdown, Select
10 from .widget_selection import RadioButtons, ToggleButtons, Dropdown, Select
10 from .widget_selectioncontainer import Tab, Accordion
11 from .widget_selectioncontainer import Tab, Accordion
11 from .widget_string import HTML, Latex, Text, Textarea
12 from .widget_string import HTML, Latex, Text, Textarea
12 from .interaction import interact, interactive, fixed, interact_manual
13 from .interaction import interact, interactive, fixed, interact_manual
13
14
14 # Deprecated classes
15 # Deprecated classes
15 from .widget_bool import CheckboxWidget, ToggleButtonWidget
16 from .widget_bool import CheckboxWidget, ToggleButtonWidget
16 from .widget_button import ButtonWidget
17 from .widget_button import ButtonWidget
17 from .widget_box import ContainerWidget, PopupWidget
18 from .widget_box import ContainerWidget, PopupWidget
18 from .widget_float import FloatTextWidget, BoundedFloatTextWidget, FloatSliderWidget, FloatProgressWidget
19 from .widget_float import FloatTextWidget, BoundedFloatTextWidget, FloatSliderWidget, FloatProgressWidget
19 from .widget_image import ImageWidget
20 from .widget_image import ImageWidget
20 from .widget_int import IntTextWidget, BoundedIntTextWidget, IntSliderWidget, IntProgressWidget
21 from .widget_int import IntTextWidget, BoundedIntTextWidget, IntSliderWidget, IntProgressWidget
21 from .widget_selection import RadioButtonsWidget, ToggleButtonsWidget, DropdownWidget, SelectWidget
22 from .widget_selection import RadioButtonsWidget, ToggleButtonsWidget, DropdownWidget, SelectWidget
22 from .widget_selectioncontainer import TabWidget, AccordionWidget
23 from .widget_selectioncontainer import TabWidget, AccordionWidget
23 from .widget_string import HTMLWidget, LatexWidget, TextWidget, TextareaWidget
24 from .widget_string import HTMLWidget, LatexWidget, TextWidget, TextareaWidget
General Comments 0
You need to be logged in to leave comments. Login now