##// END OF EJS Templates
Replace trigering tooltip for cross platform indep...
Matthias BUSSONNIER -
Show More
@@ -1,600 +1,601
1 //----------------------------------------------------------------------------
1 //----------------------------------------------------------------------------
2 // Copyright (C) 2008-2011 The IPython Development Team
2 // Copyright (C) 2008-2011 The IPython Development Team
3 //
3 //
4 // Distributed under the terms of the BSD License. The full license is in
4 // Distributed under the terms of the BSD License. The full license is in
5 // the file COPYING, distributed as part of this software.
5 // the file COPYING, distributed as part of this software.
6 //----------------------------------------------------------------------------
6 //----------------------------------------------------------------------------
7
7
8 //============================================================================
8 //============================================================================
9 // CodeCell
9 // CodeCell
10 //============================================================================
10 //============================================================================
11
11
12 var IPython = (function (IPython) {
12 var IPython = (function (IPython) {
13
13
14 var utils = IPython.utils;
14 var utils = IPython.utils;
15
15
16 var CodeCell = function (notebook) {
16 var CodeCell = function (notebook) {
17 this.code_mirror = null;
17 this.code_mirror = null;
18 this.input_prompt_number = ' ';
18 this.input_prompt_number = ' ';
19 this.is_completing = false;
19 this.is_completing = false;
20 this.completion_cursor = null;
20 this.completion_cursor = null;
21 this.outputs = [];
21 this.outputs = [];
22 this.collapsed = false;
22 this.collapsed = false;
23 IPython.Cell.apply(this, arguments);
23 IPython.Cell.apply(this, arguments);
24 };
24 };
25
25
26
26
27 CodeCell.prototype = new IPython.Cell();
27 CodeCell.prototype = new IPython.Cell();
28
28
29
29
30 CodeCell.prototype.create_element = function () {
30 CodeCell.prototype.create_element = function () {
31 var cell = $('<div></div>').addClass('cell border-box-sizing code_cell vbox');
31 var cell = $('<div></div>').addClass('cell border-box-sizing code_cell vbox');
32 cell.attr('tabindex','2');
32 cell.attr('tabindex','2');
33 var input = $('<div></div>').addClass('input hbox');
33 var input = $('<div></div>').addClass('input hbox');
34 input.append($('<div/>').addClass('prompt input_prompt'));
34 input.append($('<div/>').addClass('prompt input_prompt'));
35 var input_area = $('<div/>').addClass('input_area box-flex1');
35 var input_area = $('<div/>').addClass('input_area box-flex1');
36 this.code_mirror = CodeMirror(input_area.get(0), {
36 this.code_mirror = CodeMirror(input_area.get(0), {
37 indentUnit : 4,
37 indentUnit : 4,
38 mode: 'python',
38 mode: 'python',
39 theme: 'ipython',
39 theme: 'ipython',
40 readOnly: this.read_only,
40 readOnly: this.read_only,
41 onKeyEvent: $.proxy(this.handle_codemirror_keyevent,this)
41 onKeyEvent: $.proxy(this.handle_codemirror_keyevent,this)
42 });
42 });
43 input.append(input_area);
43 input.append(input_area);
44 var output = $('<div></div>').addClass('output vbox');
44 var output = $('<div></div>').addClass('output vbox');
45 cell.append(input).append(output);
45 cell.append(input).append(output);
46 this.element = cell;
46 this.element = cell;
47 this.collapse()
47 this.collapse()
48 };
48 };
49
49
50 //TODO, try to diminish the number of parameters.
50 //TODO, try to diminish the number of parameters.
51 CodeCell.prototype.request_tooltip_after_time = function (pre_cursor,time,that){
51 CodeCell.prototype.request_tooltip_after_time = function (pre_cursor,time,that){
52 if (pre_cursor === "" || pre_cursor === "(" ) {
52 if (pre_cursor === "" || pre_cursor === "(" ) {
53 // don't do anything if line beggin with '(' or is empty
53 // don't do anything if line beggin with '(' or is empty
54 } else {
54 } else {
55 // Will set a timer to request tooltip in `time`
55 // Will set a timer to request tooltip in `time`
56 that.tooltip_timeout = setTimeout(function(){
56 that.tooltip_timeout = setTimeout(function(){
57 IPython.notebook.request_tool_tip(that, pre_cursor)
57 IPython.notebook.request_tool_tip(that, pre_cursor)
58 },time);
58 },time);
59 }
59 }
60 };
60 };
61
61
62 CodeCell.prototype.handle_codemirror_keyevent = function (editor, event) {
62 CodeCell.prototype.handle_codemirror_keyevent = function (editor, event) {
63 // This method gets called in CodeMirror's onKeyDown/onKeyPress
63 // This method gets called in CodeMirror's onKeyDown/onKeyPress
64 // handlers and is used to provide custom key handling. Its return
64 // handlers and is used to provide custom key handling. Its return
65 // value is used to determine if CodeMirror should ignore the event:
65 // value is used to determine if CodeMirror should ignore the event:
66 // true = ignore, false = don't ignore.
66 // true = ignore, false = don't ignore.
67
67
68 // note that we are comparing and setting the time to wait at each key press.
68 // note that we are comparing and setting the time to wait at each key press.
69 // a better wqy might be to generate a new function on each time change and
69 // a better wqy might be to generate a new function on each time change and
70 // assign it to CodeCell.prototype.request_tooltip_after_time
70 // assign it to CodeCell.prototype.request_tooltip_after_time
71 tooltip_wait_time = this.notebook.time_before_tooltip;
71 tooltip_wait_time = this.notebook.time_before_tooltip;
72 tooltip_on_tab = this.notebook.tooltip_on_tab;
72 tooltip_on_tab = this.notebook.tooltip_on_tab;
73 var that = this;
73 var that = this;
74
75 // whatever key is pressed, first, cancel the tooltip request before
74 // whatever key is pressed, first, cancel the tooltip request before
76 // they are sent, and remove tooltip if any
75 // they are sent, and remove tooltip if any
77 if(event.type === 'keydown' && this.tooltip_timeout != null){
76 if(event.type === 'keydown' && this.tooltip_timeout != null){
78 CodeCell.prototype.remove_and_cancell_tooltip(that.tooltip_timeout);
77 CodeCell.prototype.remove_and_cancell_tooltip(that.tooltip_timeout);
79 that.tooltip_timeout=null;
78 that.tooltip_timeout=null;
80 }
79 }
81
80
82 if (event.keyCode === 13 && (event.shiftKey || event.ctrlKey)) {
81 if (event.keyCode === 13 && (event.shiftKey || event.ctrlKey)) {
83 // Always ignore shift-enter in CodeMirror as we handle it.
82 // Always ignore shift-enter in CodeMirror as we handle it.
84 return true;
83 return true;
85 }else if (event.keyCode === 53 && event.type === 'keydown' && tooltip_wait_time >= 0) {
84 }else if (event.which === 40 && event.type === 'keypress' && tooltip_wait_time >= 0) {
85 // triger aon keypress (!) otherwise inconsistent event.which depending on plateform
86 // browser and keyboard layout !
86 // Pressing '(' , request tooltip, don't forget to reappend it
87 // Pressing '(' , request tooltip, don't forget to reappend it
87 var cursor = editor.getCursor();
88 var cursor = editor.getCursor();
88 var pre_cursor = editor.getRange({line:cursor.line,ch:0},cursor).trim()+'(';
89 var pre_cursor = editor.getRange({line:cursor.line,ch:0},cursor).trim()+'(';
89 CodeCell.prototype.request_tooltip_after_time(pre_cursor,tooltip_wait_time,that);
90 CodeCell.prototype.request_tooltip_after_time(pre_cursor,tooltip_wait_time,that);
90 } else if (event.keyCode === 9 && event.type == 'keydown') {
91 } else if (event.keyCode === 9 && event.type == 'keydown') {
91 // Tab completion.
92 // Tab completion.
92 var cur = editor.getCursor();
93 var cur = editor.getCursor();
93 //Do not trim here because of tooltip
94 //Do not trim here because of tooltip
94 var pre_cursor = editor.getRange({line:cur.line,ch:0},cur);
95 var pre_cursor = editor.getRange({line:cur.line,ch:0},cur);
95 if (pre_cursor.trim() === "") {
96 if (pre_cursor.trim() === "") {
96 // Don't autocomplete if the part of the line before the cursor
97 // Don't autocomplete if the part of the line before the cursor
97 // is empty. In this case, let CodeMirror handle indentation.
98 // is empty. In this case, let CodeMirror handle indentation.
98 return false;
99 return false;
99 } else if ((pre_cursor.substr(-1) === "("|| pre_cursor.substr(-1) === " ") && tooltip_on_tab ) {
100 } else if ((pre_cursor.substr(-1) === "("|| pre_cursor.substr(-1) === " ") && tooltip_on_tab ) {
100 CodeCell.prototype.request_tooltip_after_time(pre_cursor,0,that);
101 CodeCell.prototype.request_tooltip_after_time(pre_cursor,0,that);
101 } else {
102 } else {
102 pre_cursor.trim();
103 pre_cursor.trim();
103 // Autocomplete the current line.
104 // Autocomplete the current line.
104 event.stop();
105 event.stop();
105 var line = editor.getLine(cur.line);
106 var line = editor.getLine(cur.line);
106 this.is_completing = true;
107 this.is_completing = true;
107 this.completion_cursor = cur;
108 this.completion_cursor = cur;
108 IPython.notebook.complete_cell(this, line, cur.ch);
109 IPython.notebook.complete_cell(this, line, cur.ch);
109 return true;
110 return true;
110 }
111 }
111 } else if (event.keyCode === 8 && event.type == 'keydown') {
112 } else if (event.keyCode === 8 && event.type == 'keydown') {
112 // If backspace and the line ends with 4 spaces, remove them.
113 // If backspace and the line ends with 4 spaces, remove them.
113 var cur = editor.getCursor();
114 var cur = editor.getCursor();
114 var line = editor.getLine(cur.line);
115 var line = editor.getLine(cur.line);
115 var ending = line.slice(-4);
116 var ending = line.slice(-4);
116 if (ending === ' ') {
117 if (ending === ' ') {
117 editor.replaceRange('',
118 editor.replaceRange('',
118 {line: cur.line, ch: cur.ch-4},
119 {line: cur.line, ch: cur.ch-4},
119 {line: cur.line, ch: cur.ch}
120 {line: cur.line, ch: cur.ch}
120 );
121 );
121 event.stop();
122 event.stop();
122 return true;
123 return true;
123 } else {
124 } else {
124 return false;
125 return false;
125 };
126 };
126 } else if (event.keyCode === 76 && event.ctrlKey && event.shiftKey
127 } else if (event.keyCode === 76 && event.ctrlKey && event.shiftKey
127 && event.type == 'keydown') {
128 && event.type == 'keydown') {
128 // toggle line numbers with Ctrl-Shift-L
129 // toggle line numbers with Ctrl-Shift-L
129 this.toggle_line_numbers();
130 this.toggle_line_numbers();
130 }
131 }
131 else {
132 else {
132 // keypress/keyup also trigger on TAB press, and we don't want to
133 // keypress/keyup also trigger on TAB press, and we don't want to
133 // use those to disable tab completion.
134 // use those to disable tab completion.
134 if (this.is_completing && event.keyCode !== 9) {
135 if (this.is_completing && event.keyCode !== 9) {
135 var ed_cur = editor.getCursor();
136 var ed_cur = editor.getCursor();
136 var cc_cur = this.completion_cursor;
137 var cc_cur = this.completion_cursor;
137 if (ed_cur.line !== cc_cur.line || ed_cur.ch !== cc_cur.ch) {
138 if (ed_cur.line !== cc_cur.line || ed_cur.ch !== cc_cur.ch) {
138 this.is_completing = false;
139 this.is_completing = false;
139 this.completion_cursor = null;
140 this.completion_cursor = null;
140 };
141 };
141 };
142 };
142 return false;
143 return false;
143 };
144 };
144 };
145 };
145
146
146 CodeCell.prototype.remove_and_cancell_tooltip = function(timeout)
147 CodeCell.prototype.remove_and_cancell_tooltip = function(timeout)
147 {
148 {
148 // note that we don't handle closing directly inside the calltip
149 // note that we don't handle closing directly inside the calltip
149 // as in the completer, because it is not focusable, so won't
150 // as in the completer, because it is not focusable, so won't
150 // get the event.
151 // get the event.
151 clearTimeout(timeout);
152 clearTimeout(timeout);
152 $('#tooltip').remove();
153 $('#tooltip').remove();
153 }
154 }
154
155
155 CodeCell.prototype.finish_tooltip = function (defstring,docstring) {
156 CodeCell.prototype.finish_tooltip = function (defstring,docstring) {
156 shortened = function(string){
157 shortened = function(string){
157 if(string.length > 200){
158 if(string.length > 200){
158 return string.trim().substring(0,197)+'...';
159 return string.trim().substring(0,197)+'...';
159 } else { return string.trim() }
160 } else { return string.trim() }
160 }
161 }
161
162
162 var that = this;
163 var that = this;
163 var tooltip = $('<div/>').attr('id', 'tooltip').addClass('tooltip');
164 var tooltip = $('<div/>').attr('id', 'tooltip').addClass('tooltip');
164 if(defstring){
165 if(defstring){
165 defstring_html= $('<pre/>').html(utils.fixConsole(defstring));
166 defstring_html= $('<pre/>').html(utils.fixConsole(defstring));
166 tooltip.append(defstring_html);
167 tooltip.append(defstring_html);
167 }
168 }
168 tooltip.append($('<pre/>').html(utils.fixConsole(shortened(docstring))));
169 tooltip.append($('<pre/>').html(utils.fixConsole(shortened(docstring))));
169 var pos = this.code_mirror.cursorCoords();
170 var pos = this.code_mirror.cursorCoords();
170 tooltip.css('left',pos.x+'px');
171 tooltip.css('left',pos.x+'px');
171 tooltip.css('top',pos.yBot+'px');
172 tooltip.css('top',pos.yBot+'px');
172 $('body').append(tooltip);
173 $('body').append(tooltip);
173
174
174 // issues with cross-closing if multiple tooltip in less than 5sec
175 // issues with cross-closing if multiple tooltip in less than 5sec
175 // keep it comented for now
176 // keep it comented for now
176 // setTimeout(CodeCell.prototype.remove_and_cancell_tooltip, 5000);
177 // setTimeout(CodeCell.prototype.remove_and_cancell_tooltip, 5000);
177 };
178 };
178
179
179
180
180 CodeCell.prototype.finish_completing = function (matched_text, matches) {
181 CodeCell.prototype.finish_completing = function (matched_text, matches) {
181 // console.log("Got matches", matched_text, matches);
182 // console.log("Got matches", matched_text, matches);
182 var newm = new Array();
183 var newm = new Array();
183 if(this.notebook.smart_completer)
184 if(this.notebook.smart_completer)
184 {
185 {
185 kwargs = new Array();
186 kwargs = new Array();
186 other = new Array();
187 other = new Array();
187 for(var i=0;i<matches.length; ++i){
188 for(var i=0;i<matches.length; ++i){
188 if(matches[i].substr(-1) === '='){
189 if(matches[i].substr(-1) === '='){
189 kwargs.push(matches[i]);
190 kwargs.push(matches[i]);
190 }else{other.push(matches[i]);}
191 }else{other.push(matches[i]);}
191 }
192 }
192 newm = kwargs.concat(other);
193 newm = kwargs.concat(other);
193 matches=newm;
194 matches=newm;
194 }
195 }
195 if (!this.is_completing || matches.length === 0) {return;}
196 if (!this.is_completing || matches.length === 0) {return;}
196
197
197 var that = this;
198 var that = this;
198 var cur = this.completion_cursor;
199 var cur = this.completion_cursor;
199
200
200 var insert = function (selected_text) {
201 var insert = function (selected_text) {
201 that.code_mirror.replaceRange(
202 that.code_mirror.replaceRange(
202 selected_text,
203 selected_text,
203 {line: cur.line, ch: (cur.ch-matched_text.length)},
204 {line: cur.line, ch: (cur.ch-matched_text.length)},
204 {line: cur.line, ch: cur.ch}
205 {line: cur.line, ch: cur.ch}
205 );
206 );
206 };
207 };
207
208
208 if (matches.length === 1) {
209 if (matches.length === 1) {
209 insert(matches[0]);
210 insert(matches[0]);
210 setTimeout(function(){that.code_mirror.focus();}, 50);
211 setTimeout(function(){that.code_mirror.focus();}, 50);
211 return;
212 return;
212 };
213 };
213
214
214 var complete = $('<div/>').addClass('completions');
215 var complete = $('<div/>').addClass('completions');
215 var select = $('<select/>').attr('multiple','true');
216 var select = $('<select/>').attr('multiple','true');
216 for (var i=0; i<matches.length; ++i) {
217 for (var i=0; i<matches.length; ++i) {
217 select.append($('<option/>').text(matches[i]));
218 select.append($('<option/>').text(matches[i]));
218 }
219 }
219 select.children().first().attr('selected','true');
220 select.children().first().attr('selected','true');
220 select.attr('size',Math.min(10,matches.length));
221 select.attr('size',Math.min(10,matches.length));
221 var pos = this.code_mirror.cursorCoords();
222 var pos = this.code_mirror.cursorCoords();
222 complete.css('left',pos.x+'px');
223 complete.css('left',pos.x+'px');
223 complete.css('top',pos.yBot+'px');
224 complete.css('top',pos.yBot+'px');
224 complete.append(select);
225 complete.append(select);
225
226
226 $('body').append(complete);
227 $('body').append(complete);
227 var done = false;
228 var done = false;
228
229
229 var close = function () {
230 var close = function () {
230 if (done) return;
231 if (done) return;
231 done = true;
232 done = true;
232 complete.remove();
233 complete.remove();
233 that.is_completing = false;
234 that.is_completing = false;
234 that.completion_cursor = null;
235 that.completion_cursor = null;
235 };
236 };
236
237
237 var pick = function () {
238 var pick = function () {
238 insert(select.val()[0]);
239 insert(select.val()[0]);
239 close();
240 close();
240 setTimeout(function(){that.code_mirror.focus();}, 50);
241 setTimeout(function(){that.code_mirror.focus();}, 50);
241 };
242 };
242
243
243 select.blur(close);
244 select.blur(close);
244 select.keydown(function (event) {
245 select.keydown(function (event) {
245 var code = event.which;
246 var code = event.which;
246 if (code === 13 || code === 32) {
247 if (code === 13 || code === 32) {
247 // Pressing SPACE or ENTER will cause a pick
248 // Pressing SPACE or ENTER will cause a pick
248 event.stopPropagation();
249 event.stopPropagation();
249 event.preventDefault();
250 event.preventDefault();
250 pick();
251 pick();
251 } else if (code === 38 || code === 40) {
252 } else if (code === 38 || code === 40) {
252 // We don't want the document keydown handler to handle UP/DOWN,
253 // We don't want the document keydown handler to handle UP/DOWN,
253 // but we want the default action.
254 // but we want the default action.
254 event.stopPropagation();
255 event.stopPropagation();
255 } else {
256 } else {
256 // All other key presses exit completion.
257 // All other key presses exit completion.
257 event.stopPropagation();
258 event.stopPropagation();
258 event.preventDefault();
259 event.preventDefault();
259 close();
260 close();
260 that.code_mirror.focus();
261 that.code_mirror.focus();
261 }
262 }
262 });
263 });
263 // Double click also causes a pick.
264 // Double click also causes a pick.
264 select.dblclick(pick);
265 select.dblclick(pick);
265 select.focus();
266 select.focus();
266 };
267 };
267
268
268 CodeCell.prototype.toggle_line_numbers = function () {
269 CodeCell.prototype.toggle_line_numbers = function () {
269 if (this.code_mirror.getOption('lineNumbers') == false) {
270 if (this.code_mirror.getOption('lineNumbers') == false) {
270 this.code_mirror.setOption('lineNumbers', true);
271 this.code_mirror.setOption('lineNumbers', true);
271 } else {
272 } else {
272 this.code_mirror.setOption('lineNumbers', false);
273 this.code_mirror.setOption('lineNumbers', false);
273 }
274 }
274 this.code_mirror.refresh()
275 this.code_mirror.refresh()
275 };
276 };
276
277
277 CodeCell.prototype.select = function () {
278 CodeCell.prototype.select = function () {
278 IPython.Cell.prototype.select.apply(this);
279 IPython.Cell.prototype.select.apply(this);
279 // Todo: this dance is needed because as of CodeMirror 2.12, focus is
280 // Todo: this dance is needed because as of CodeMirror 2.12, focus is
280 // not causing the cursor to blink if the editor is empty initially.
281 // not causing the cursor to blink if the editor is empty initially.
281 // While this seems to fix the issue, this should be fixed
282 // While this seems to fix the issue, this should be fixed
282 // in CodeMirror proper.
283 // in CodeMirror proper.
283 var s = this.code_mirror.getValue();
284 var s = this.code_mirror.getValue();
284 this.code_mirror.focus();
285 this.code_mirror.focus();
285 if (s === '') this.code_mirror.setValue('');
286 if (s === '') this.code_mirror.setValue('');
286 };
287 };
287
288
288
289
289 CodeCell.prototype.select_all = function () {
290 CodeCell.prototype.select_all = function () {
290 var start = {line: 0, ch: 0};
291 var start = {line: 0, ch: 0};
291 var nlines = this.code_mirror.lineCount();
292 var nlines = this.code_mirror.lineCount();
292 var last_line = this.code_mirror.getLine(nlines-1);
293 var last_line = this.code_mirror.getLine(nlines-1);
293 var end = {line: nlines-1, ch: last_line.length};
294 var end = {line: nlines-1, ch: last_line.length};
294 this.code_mirror.setSelection(start, end);
295 this.code_mirror.setSelection(start, end);
295 };
296 };
296
297
297
298
298 CodeCell.prototype.append_output = function (json) {
299 CodeCell.prototype.append_output = function (json) {
299 this.expand();
300 this.expand();
300 if (json.output_type === 'pyout') {
301 if (json.output_type === 'pyout') {
301 this.append_pyout(json);
302 this.append_pyout(json);
302 } else if (json.output_type === 'pyerr') {
303 } else if (json.output_type === 'pyerr') {
303 this.append_pyerr(json);
304 this.append_pyerr(json);
304 } else if (json.output_type === 'display_data') {
305 } else if (json.output_type === 'display_data') {
305 this.append_display_data(json);
306 this.append_display_data(json);
306 } else if (json.output_type === 'stream') {
307 } else if (json.output_type === 'stream') {
307 this.append_stream(json);
308 this.append_stream(json);
308 };
309 };
309 this.outputs.push(json);
310 this.outputs.push(json);
310 };
311 };
311
312
312
313
313 CodeCell.prototype.create_output_area = function () {
314 CodeCell.prototype.create_output_area = function () {
314 var oa = $("<div/>").addClass("hbox output_area");
315 var oa = $("<div/>").addClass("hbox output_area");
315 oa.append($('<div/>').addClass('prompt'));
316 oa.append($('<div/>').addClass('prompt'));
316 return oa;
317 return oa;
317 };
318 };
318
319
319
320
320 CodeCell.prototype.append_pyout = function (json) {
321 CodeCell.prototype.append_pyout = function (json) {
321 n = json.prompt_number || ' ';
322 n = json.prompt_number || ' ';
322 var toinsert = this.create_output_area();
323 var toinsert = this.create_output_area();
323 toinsert.find('div.prompt').addClass('output_prompt').html('Out[' + n + ']:');
324 toinsert.find('div.prompt').addClass('output_prompt').html('Out[' + n + ']:');
324 this.append_mime_type(json, toinsert);
325 this.append_mime_type(json, toinsert);
325 this.element.find('div.output').append(toinsert);
326 this.element.find('div.output').append(toinsert);
326 // If we just output latex, typeset it.
327 // If we just output latex, typeset it.
327 if ((json.latex !== undefined) || (json.html !== undefined)) {
328 if ((json.latex !== undefined) || (json.html !== undefined)) {
328 MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
329 MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
329 };
330 };
330 };
331 };
331
332
332
333
333 CodeCell.prototype.append_pyerr = function (json) {
334 CodeCell.prototype.append_pyerr = function (json) {
334 var tb = json.traceback;
335 var tb = json.traceback;
335 if (tb !== undefined && tb.length > 0) {
336 if (tb !== undefined && tb.length > 0) {
336 var s = '';
337 var s = '';
337 var len = tb.length;
338 var len = tb.length;
338 for (var i=0; i<len; i++) {
339 for (var i=0; i<len; i++) {
339 s = s + tb[i] + '\n';
340 s = s + tb[i] + '\n';
340 }
341 }
341 s = s + '\n';
342 s = s + '\n';
342 var toinsert = this.create_output_area();
343 var toinsert = this.create_output_area();
343 this.append_text(s, toinsert);
344 this.append_text(s, toinsert);
344 this.element.find('div.output').append(toinsert);
345 this.element.find('div.output').append(toinsert);
345 };
346 };
346 };
347 };
347
348
348
349
349 CodeCell.prototype.append_stream = function (json) {
350 CodeCell.prototype.append_stream = function (json) {
350 // temporary fix: if stream undefined (json file written prior to this patch),
351 // temporary fix: if stream undefined (json file written prior to this patch),
351 // default to most likely stdout:
352 // default to most likely stdout:
352 if (json.stream == undefined){
353 if (json.stream == undefined){
353 json.stream = 'stdout';
354 json.stream = 'stdout';
354 }
355 }
355 var subclass = "output_"+json.stream;
356 var subclass = "output_"+json.stream;
356 if (this.outputs.length > 0){
357 if (this.outputs.length > 0){
357 // have at least one output to consider
358 // have at least one output to consider
358 var last = this.outputs[this.outputs.length-1];
359 var last = this.outputs[this.outputs.length-1];
359 if (last.output_type == 'stream' && json.stream == last.stream){
360 if (last.output_type == 'stream' && json.stream == last.stream){
360 // latest output was in the same stream,
361 // latest output was in the same stream,
361 // so append directly into its pre tag
362 // so append directly into its pre tag
362 this.element.find('div.'+subclass).last().find('pre').append(json.text);
363 this.element.find('div.'+subclass).last().find('pre').append(json.text);
363 return;
364 return;
364 }
365 }
365 }
366 }
366
367
367 // If we got here, attach a new div
368 // If we got here, attach a new div
368 var toinsert = this.create_output_area();
369 var toinsert = this.create_output_area();
369 this.append_text(json.text, toinsert, "output_stream "+subclass);
370 this.append_text(json.text, toinsert, "output_stream "+subclass);
370 this.element.find('div.output').append(toinsert);
371 this.element.find('div.output').append(toinsert);
371 };
372 };
372
373
373
374
374 CodeCell.prototype.append_display_data = function (json) {
375 CodeCell.prototype.append_display_data = function (json) {
375 var toinsert = this.create_output_area();
376 var toinsert = this.create_output_area();
376 this.append_mime_type(json, toinsert)
377 this.append_mime_type(json, toinsert)
377 this.element.find('div.output').append(toinsert);
378 this.element.find('div.output').append(toinsert);
378 // If we just output latex, typeset it.
379 // If we just output latex, typeset it.
379 if ( (json.latex !== undefined) || (json.html !== undefined) ) {
380 if ( (json.latex !== undefined) || (json.html !== undefined) ) {
380 MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
381 MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
381 };
382 };
382 };
383 };
383
384
384
385
385 CodeCell.prototype.append_mime_type = function (json, element) {
386 CodeCell.prototype.append_mime_type = function (json, element) {
386 if (json.html !== undefined) {
387 if (json.html !== undefined) {
387 this.append_html(json.html, element);
388 this.append_html(json.html, element);
388 } else if (json.latex !== undefined) {
389 } else if (json.latex !== undefined) {
389 this.append_latex(json.latex, element);
390 this.append_latex(json.latex, element);
390 } else if (json.svg !== undefined) {
391 } else if (json.svg !== undefined) {
391 this.append_svg(json.svg, element);
392 this.append_svg(json.svg, element);
392 } else if (json.png !== undefined) {
393 } else if (json.png !== undefined) {
393 this.append_png(json.png, element);
394 this.append_png(json.png, element);
394 } else if (json.jpeg !== undefined) {
395 } else if (json.jpeg !== undefined) {
395 this.append_jpeg(json.jpeg, element);
396 this.append_jpeg(json.jpeg, element);
396 } else if (json.text !== undefined) {
397 } else if (json.text !== undefined) {
397 this.append_text(json.text, element);
398 this.append_text(json.text, element);
398 };
399 };
399 };
400 };
400
401
401
402
402 CodeCell.prototype.append_html = function (html, element) {
403 CodeCell.prototype.append_html = function (html, element) {
403 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_html rendered_html");
404 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_html rendered_html");
404 toinsert.append(html);
405 toinsert.append(html);
405 element.append(toinsert);
406 element.append(toinsert);
406 }
407 }
407
408
408
409
409 CodeCell.prototype.append_text = function (data, element, extra_class) {
410 CodeCell.prototype.append_text = function (data, element, extra_class) {
410 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_text");
411 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_text");
411 if (extra_class){
412 if (extra_class){
412 toinsert.addClass(extra_class);
413 toinsert.addClass(extra_class);
413 }
414 }
414 toinsert.append($("<pre/>").html(data));
415 toinsert.append($("<pre/>").html(data));
415 element.append(toinsert);
416 element.append(toinsert);
416 };
417 };
417
418
418
419
419 CodeCell.prototype.append_svg = function (svg, element) {
420 CodeCell.prototype.append_svg = function (svg, element) {
420 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_svg");
421 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_svg");
421 toinsert.append(svg);
422 toinsert.append(svg);
422 element.append(toinsert);
423 element.append(toinsert);
423 };
424 };
424
425
425
426
426 CodeCell.prototype.append_png = function (png, element) {
427 CodeCell.prototype.append_png = function (png, element) {
427 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_png");
428 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_png");
428 toinsert.append($("<img/>").attr('src','data:image/png;base64,'+png));
429 toinsert.append($("<img/>").attr('src','data:image/png;base64,'+png));
429 element.append(toinsert);
430 element.append(toinsert);
430 };
431 };
431
432
432
433
433 CodeCell.prototype.append_jpeg = function (jpeg, element) {
434 CodeCell.prototype.append_jpeg = function (jpeg, element) {
434 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_jpeg");
435 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_jpeg");
435 toinsert.append($("<img/>").attr('src','data:image/jpeg;base64,'+jpeg));
436 toinsert.append($("<img/>").attr('src','data:image/jpeg;base64,'+jpeg));
436 element.append(toinsert);
437 element.append(toinsert);
437 };
438 };
438
439
439
440
440 CodeCell.prototype.append_latex = function (latex, element) {
441 CodeCell.prototype.append_latex = function (latex, element) {
441 // This method cannot do the typesetting because the latex first has to
442 // This method cannot do the typesetting because the latex first has to
442 // be on the page.
443 // be on the page.
443 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_latex");
444 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_latex");
444 toinsert.append(latex);
445 toinsert.append(latex);
445 element.append(toinsert);
446 element.append(toinsert);
446 }
447 }
447
448
448
449
449 CodeCell.prototype.clear_output = function (stdout, stderr, other) {
450 CodeCell.prototype.clear_output = function (stdout, stderr, other) {
450 var output_div = this.element.find("div.output");
451 var output_div = this.element.find("div.output");
451 if (stdout && stderr && other){
452 if (stdout && stderr && other){
452 // clear all, no need for logic
453 // clear all, no need for logic
453 output_div.html("");
454 output_div.html("");
454 this.outputs = [];
455 this.outputs = [];
455 return;
456 return;
456 }
457 }
457 // remove html output
458 // remove html output
458 // each output_subarea that has an identifying class is in an output_area
459 // each output_subarea that has an identifying class is in an output_area
459 // which is the element to be removed.
460 // which is the element to be removed.
460 if (stdout){
461 if (stdout){
461 output_div.find("div.output_stdout").parent().remove();
462 output_div.find("div.output_stdout").parent().remove();
462 }
463 }
463 if (stderr){
464 if (stderr){
464 output_div.find("div.output_stderr").parent().remove();
465 output_div.find("div.output_stderr").parent().remove();
465 }
466 }
466 if (other){
467 if (other){
467 output_div.find("div.output_subarea").not("div.output_stderr").not("div.output_stdout").parent().remove();
468 output_div.find("div.output_subarea").not("div.output_stderr").not("div.output_stdout").parent().remove();
468 }
469 }
469
470
470 // remove cleared outputs from JSON list:
471 // remove cleared outputs from JSON list:
471 for (var i = this.outputs.length - 1; i >= 0; i--){
472 for (var i = this.outputs.length - 1; i >= 0; i--){
472 var out = this.outputs[i];
473 var out = this.outputs[i];
473 var output_type = out.output_type;
474 var output_type = out.output_type;
474 if (output_type == "display_data" && other){
475 if (output_type == "display_data" && other){
475 this.outputs.splice(i,1);
476 this.outputs.splice(i,1);
476 }else if (output_type == "stream"){
477 }else if (output_type == "stream"){
477 if (stdout && out.stream == "stdout"){
478 if (stdout && out.stream == "stdout"){
478 this.outputs.splice(i,1);
479 this.outputs.splice(i,1);
479 }else if (stderr && out.stream == "stderr"){
480 }else if (stderr && out.stream == "stderr"){
480 this.outputs.splice(i,1);
481 this.outputs.splice(i,1);
481 }
482 }
482 }
483 }
483 }
484 }
484 };
485 };
485
486
486
487
487 CodeCell.prototype.clear_input = function () {
488 CodeCell.prototype.clear_input = function () {
488 this.code_mirror.setValue('');
489 this.code_mirror.setValue('');
489 };
490 };
490
491
491
492
492 CodeCell.prototype.collapse = function () {
493 CodeCell.prototype.collapse = function () {
493 if (!this.collapsed) {
494 if (!this.collapsed) {
494 this.element.find('div.output').hide();
495 this.element.find('div.output').hide();
495 this.collapsed = true;
496 this.collapsed = true;
496 };
497 };
497 };
498 };
498
499
499
500
500 CodeCell.prototype.expand = function () {
501 CodeCell.prototype.expand = function () {
501 if (this.collapsed) {
502 if (this.collapsed) {
502 this.element.find('div.output').show();
503 this.element.find('div.output').show();
503 this.collapsed = false;
504 this.collapsed = false;
504 };
505 };
505 };
506 };
506
507
507
508
508 CodeCell.prototype.toggle_output = function () {
509 CodeCell.prototype.toggle_output = function () {
509 if (this.collapsed) {
510 if (this.collapsed) {
510 this.expand();
511 this.expand();
511 } else {
512 } else {
512 this.collapse();
513 this.collapse();
513 };
514 };
514 };
515 };
515
516
516 CodeCell.prototype.set_input_prompt = function (number) {
517 CodeCell.prototype.set_input_prompt = function (number) {
517 var n = number || ' ';
518 var n = number || ' ';
518 this.input_prompt_number = n
519 this.input_prompt_number = n
519 this.element.find('div.input_prompt').html('In&nbsp;[' + n + ']:');
520 this.element.find('div.input_prompt').html('In&nbsp;[' + n + ']:');
520 };
521 };
521
522
522
523
523 CodeCell.prototype.get_code = function () {
524 CodeCell.prototype.get_code = function () {
524 return this.code_mirror.getValue();
525 return this.code_mirror.getValue();
525 };
526 };
526
527
527
528
528 CodeCell.prototype.set_code = function (code) {
529 CodeCell.prototype.set_code = function (code) {
529 return this.code_mirror.setValue(code);
530 return this.code_mirror.setValue(code);
530 };
531 };
531
532
532
533
533 CodeCell.prototype.at_top = function () {
534 CodeCell.prototype.at_top = function () {
534 var cursor = this.code_mirror.getCursor();
535 var cursor = this.code_mirror.getCursor();
535 if (cursor.line === 0) {
536 if (cursor.line === 0) {
536 return true;
537 return true;
537 } else {
538 } else {
538 return false;
539 return false;
539 }
540 }
540 };
541 };
541
542
542
543
543 CodeCell.prototype.at_bottom = function () {
544 CodeCell.prototype.at_bottom = function () {
544 var cursor = this.code_mirror.getCursor();
545 var cursor = this.code_mirror.getCursor();
545 if (cursor.line === (this.code_mirror.lineCount()-1)) {
546 if (cursor.line === (this.code_mirror.lineCount()-1)) {
546 return true;
547 return true;
547 } else {
548 } else {
548 return false;
549 return false;
549 }
550 }
550 };
551 };
551
552
552
553
553 CodeCell.prototype.fromJSON = function (data) {
554 CodeCell.prototype.fromJSON = function (data) {
554 console.log('Import from JSON:', data);
555 console.log('Import from JSON:', data);
555 if (data.cell_type === 'code') {
556 if (data.cell_type === 'code') {
556 if (data.input !== undefined) {
557 if (data.input !== undefined) {
557 this.set_code(data.input);
558 this.set_code(data.input);
558 }
559 }
559 if (data.prompt_number !== undefined) {
560 if (data.prompt_number !== undefined) {
560 this.set_input_prompt(data.prompt_number);
561 this.set_input_prompt(data.prompt_number);
561 } else {
562 } else {
562 this.set_input_prompt();
563 this.set_input_prompt();
563 };
564 };
564 var len = data.outputs.length;
565 var len = data.outputs.length;
565 for (var i=0; i<len; i++) {
566 for (var i=0; i<len; i++) {
566 this.append_output(data.outputs[i]);
567 this.append_output(data.outputs[i]);
567 };
568 };
568 if (data.collapsed !== undefined) {
569 if (data.collapsed !== undefined) {
569 if (data.collapsed) {
570 if (data.collapsed) {
570 this.collapse();
571 this.collapse();
571 };
572 };
572 };
573 };
573 };
574 };
574 };
575 };
575
576
576
577
577 CodeCell.prototype.toJSON = function () {
578 CodeCell.prototype.toJSON = function () {
578 var data = {};
579 var data = {};
579 data.input = this.get_code();
580 data.input = this.get_code();
580 data.cell_type = 'code';
581 data.cell_type = 'code';
581 if (this.input_prompt_number !== ' ') {
582 if (this.input_prompt_number !== ' ') {
582 data.prompt_number = this.input_prompt_number
583 data.prompt_number = this.input_prompt_number
583 };
584 };
584 var outputs = [];
585 var outputs = [];
585 var len = this.outputs.length;
586 var len = this.outputs.length;
586 for (var i=0; i<len; i++) {
587 for (var i=0; i<len; i++) {
587 outputs[i] = this.outputs[i];
588 outputs[i] = this.outputs[i];
588 };
589 };
589 data.outputs = outputs;
590 data.outputs = outputs;
590 data.language = 'python';
591 data.language = 'python';
591 data.collapsed = this.collapsed;
592 data.collapsed = this.collapsed;
592 // console.log('Export to JSON:',data);
593 // console.log('Export to JSON:',data);
593 return data;
594 return data;
594 };
595 };
595
596
596
597
597 IPython.CodeCell = CodeCell;
598 IPython.CodeCell = CodeCell;
598
599
599 return IPython;
600 return IPython;
600 }(IPython));
601 }(IPython));
General Comments 0
You need to be logged in to leave comments. Login now