##// END OF EJS Templates
comment more
Matthias BUSSONNIER -
Show More
@@ -1,684 +1,691 b''
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 "use strict";
13 "use strict";
14
14
15 var utils = IPython.utils;
15 var utils = IPython.utils;
16
16
17 var CodeCell = function (notebook) {
17 var CodeCell = function (notebook) {
18 this.code_mirror = null;
18 this.code_mirror = null;
19 this.input_prompt_number = null;
19 this.input_prompt_number = null;
20 this.is_completing = false;
20 this.is_completing = false;
21 this.completion_cursor = null;
21 this.completion_cursor = null;
22 this.outputs = [];
22 this.outputs = [];
23 this.collapsed = false;
23 this.collapsed = false;
24 this.tooltip_timeout = null;
24 this.tooltip_timeout = null;
25 this.clear_out_timeout = null;
25 this.clear_out_timeout = null;
26 IPython.Cell.apply(this, arguments);
26 IPython.Cell.apply(this, arguments);
27 };
27 };
28
28
29
29
30 CodeCell.prototype = new IPython.Cell();
30 CodeCell.prototype = new IPython.Cell();
31
31
32
32
33 CodeCell.prototype.create_element = function () {
33 CodeCell.prototype.create_element = function () {
34 var cell = $('<div></div>').addClass('cell border-box-sizing code_cell vbox');
34 var cell = $('<div></div>').addClass('cell border-box-sizing code_cell vbox');
35 cell.attr('tabindex','2');
35 cell.attr('tabindex','2');
36 var input = $('<div></div>').addClass('input hbox');
36 var input = $('<div></div>').addClass('input hbox');
37 input.append($('<div/>').addClass('prompt input_prompt'));
37 input.append($('<div/>').addClass('prompt input_prompt'));
38 var input_area = $('<div/>').addClass('input_area box-flex1');
38 var input_area = $('<div/>').addClass('input_area box-flex1');
39 this.code_mirror = CodeMirror(input_area.get(0), {
39 this.code_mirror = CodeMirror(input_area.get(0), {
40 indentUnit : 4,
40 indentUnit : 4,
41 mode: 'python',
41 mode: 'python',
42 theme: 'ipython',
42 theme: 'ipython',
43 readOnly: this.read_only,
43 readOnly: this.read_only,
44 onKeyEvent: $.proxy(this.handle_codemirror_keyevent,this)
44 onKeyEvent: $.proxy(this.handle_codemirror_keyevent,this)
45 });
45 });
46 input.append(input_area);
46 input.append(input_area);
47 var output = $('<div></div>').addClass('output vbox');
47 var output = $('<div></div>').addClass('output vbox');
48 cell.append(input).append(output);
48 cell.append(input).append(output);
49 this.element = cell;
49 this.element = cell;
50 this.collapse();
50 this.collapse();
51
51
52 // construct a completer
52 // construct a completer
53 // And give it the function to call to get the completion list
53 // And give it the function to call to get the completion list
54 var that = this;
54 var that = this;
55 this.completer = new IPython.Completer(this.code_mirror,function(callback){that.requestCompletion(callback)});
55 this.completer = new IPython.Completer(this.code_mirror,function(callback){that.requestCompletion(callback)});
56 };
56 };
57
57
58 //TODO, try to diminish the number of parameters.
58 //TODO, try to diminish the number of parameters.
59 CodeCell.prototype.request_tooltip_after_time = function (pre_cursor,time){
59 CodeCell.prototype.request_tooltip_after_time = function (pre_cursor,time){
60 var that = this;
60 var that = this;
61 if (pre_cursor === "" || pre_cursor === "(" ) {
61 if (pre_cursor === "" || pre_cursor === "(" ) {
62 // don't do anything if line beggin with '(' or is empty
62 // don't do anything if line beggin with '(' or is empty
63 } else {
63 } else {
64 // Will set a timer to request tooltip in `time`
64 // Will set a timer to request tooltip in `time`
65 that.tooltip_timeout = setTimeout(function(){
65 that.tooltip_timeout = setTimeout(function(){
66 IPython.notebook.request_tool_tip(that, pre_cursor)
66 IPython.notebook.request_tool_tip(that, pre_cursor)
67 },time);
67 },time);
68 }
68 }
69 };
69 };
70
70
71 CodeCell.prototype.handle_codemirror_keyevent = function (editor, event) {
71 CodeCell.prototype.handle_codemirror_keyevent = function (editor, event) {
72 // This method gets called in CodeMirror's onKeyDown/onKeyPress
72 // This method gets called in CodeMirror's onKeyDown/onKeyPress
73 // handlers and is used to provide custom key handling. Its return
73 // handlers and is used to provide custom key handling. Its return
74 // value is used to determine if CodeMirror should ignore the event:
74 // value is used to determine if CodeMirror should ignore the event:
75 // true = ignore, false = don't ignore.
75 // true = ignore, false = don't ignore.
76
76
77 if (this.read_only){
77 if (this.read_only){
78 return false;
78 return false;
79 }
79 }
80
80
81 // note that we are comparing and setting the time to wait at each key press.
81 // note that we are comparing and setting the time to wait at each key press.
82 // a better wqy might be to generate a new function on each time change and
82 // a better wqy might be to generate a new function on each time change and
83 // assign it to CodeCell.prototype.request_tooltip_after_time
83 // assign it to CodeCell.prototype.request_tooltip_after_time
84 var tooltip_wait_time = this.notebook.time_before_tooltip;
84 var tooltip_wait_time = this.notebook.time_before_tooltip;
85 var tooltip_on_tab = this.notebook.tooltip_on_tab;
85 var tooltip_on_tab = this.notebook.tooltip_on_tab;
86 var that = this;
86 var that = this;
87 // whatever key is pressed, first, cancel the tooltip request before
87 // whatever key is pressed, first, cancel the tooltip request before
88 // they are sent, and remove tooltip if any
88 // they are sent, and remove tooltip if any
89 if(event.type === 'keydown' ) {
89 if(event.type === 'keydown' ) {
90 that.remove_and_cancel_tooltip();
90 that.remove_and_cancel_tooltip();
91 };
91 };
92
92
93
93
94 if (event.keyCode === 13 && (event.shiftKey || event.ctrlKey)) {
94 if (event.keyCode === 13 && (event.shiftKey || event.ctrlKey)) {
95 // Always ignore shift-enter in CodeMirror as we handle it.
95 // Always ignore shift-enter in CodeMirror as we handle it.
96 return true;
96 return true;
97 } else if (event.which === 40 && event.type === 'keypress' && tooltip_wait_time >= 0) {
97 } else if (event.which === 40 && event.type === 'keypress' && tooltip_wait_time >= 0) {
98 // triger aon keypress (!) otherwise inconsistent event.which depending on plateform
98 // triger aon keypress (!) otherwise inconsistent event.which depending on plateform
99 // browser and keyboard layout !
99 // browser and keyboard layout !
100 // Pressing '(' , request tooltip, don't forget to reappend it
100 // Pressing '(' , request tooltip, don't forget to reappend it
101 var cursor = editor.getCursor();
101 var cursor = editor.getCursor();
102 var pre_cursor = editor.getRange({line:cursor.line,ch:0},cursor).trim()+'(';
102 var pre_cursor = editor.getRange({line:cursor.line,ch:0},cursor).trim()+'(';
103 that.request_tooltip_after_time(pre_cursor,tooltip_wait_time);
103 that.request_tooltip_after_time(pre_cursor,tooltip_wait_time);
104 } else if (event.which === 38) {
104 } else if (event.which === 38) {
105 // If we are not at the top, let CM handle the up arrow and
105 // If we are not at the top, let CM handle the up arrow and
106 // prevent the global keydown handler from handling it.
106 // prevent the global keydown handler from handling it.
107 if (!that.at_top()) {
107 if (!that.at_top()) {
108 event.stop();
108 event.stop();
109 return false;
109 return false;
110 } else {
110 } else {
111 return true;
111 return true;
112 };
112 };
113 } else if (event.which === 40) {
113 } else if (event.which === 40) {
114 // If we are not at the bottom, let CM handle the down arrow and
114 // If we are not at the bottom, let CM handle the down arrow and
115 // prevent the global keydown handler from handling it.
115 // prevent the global keydown handler from handling it.
116 if (!that.at_bottom()) {
116 if (!that.at_bottom()) {
117 event.stop();
117 event.stop();
118 return false;
118 return false;
119 } else {
119 } else {
120 return true;
120 return true;
121 };
121 };
122 } else if (event.keyCode === 9 && event.type == 'keydown') {
122 } else if (event.keyCode === 9 && event.type == 'keydown') {
123 // Tab completion.
123 // Tab completion.
124 var cur = editor.getCursor();
124 var cur = editor.getCursor();
125 //Do not trim here because of tooltip
125 //Do not trim here because of tooltip
126 var pre_cursor = editor.getRange({line:cur.line,ch:0},cur);
126 var pre_cursor = editor.getRange({line:cur.line,ch:0},cur);
127 if (pre_cursor.trim() === "") {
127 if (pre_cursor.trim() === "") {
128 // Don't autocomplete if the part of the line before the cursor
128 // Don't autocomplete if the part of the line before the cursor
129 // is empty. In this case, let CodeMirror handle indentation.
129 // is empty. In this case, let CodeMirror handle indentation.
130 return false;
130 return false;
131 } else if ((pre_cursor.substr(-1) === "("|| pre_cursor.substr(-1) === " ") && tooltip_on_tab ) {
131 } else if ((pre_cursor.substr(-1) === "("|| pre_cursor.substr(-1) === " ") && tooltip_on_tab ) {
132 that.request_tooltip_after_time(pre_cursor,0);
132 that.request_tooltip_after_time(pre_cursor,0);
133 // Prevent the event from bubbling up.
133 // Prevent the event from bubbling up.
134 event.stop();
134 event.stop();
135 // Prevent CodeMirror from handling the tab.
135 // Prevent CodeMirror from handling the tab.
136 return true;
136 return true;
137 } else {
137 } else {
138 event.stop();
138 event.stop();
139 this.completer.startCompletion();
139 this.completer.startCompletion();
140 return true;
140 return true;
141 };
141 };
142 } else if (event.keyCode === 8 && event.type == 'keydown') {
142 } else if (event.keyCode === 8 && event.type == 'keydown') {
143 // If backspace and the line ends with 4 spaces, remove them.
143 // If backspace and the line ends with 4 spaces, remove them.
144 var cur = editor.getCursor();
144 var cur = editor.getCursor();
145 var line = editor.getLine(cur.line);
145 var line = editor.getLine(cur.line);
146 var ending = line.slice(-4);
146 var ending = line.slice(-4);
147 if (ending === ' ') {
147 if (ending === ' ') {
148 editor.replaceRange('',
148 editor.replaceRange('',
149 {line: cur.line, ch: cur.ch-4},
149 {line: cur.line, ch: cur.ch-4},
150 {line: cur.line, ch: cur.ch}
150 {line: cur.line, ch: cur.ch}
151 );
151 );
152 event.stop();
152 event.stop();
153 return true;
153 return true;
154 } else {
154 } else {
155 return false;
155 return false;
156 };
156 };
157 } else {
157 } else {
158 // keypress/keyup also trigger on TAB press, and we don't want to
158 // keypress/keyup also trigger on TAB press, and we don't want to
159 // use those to disable tab completion.
159 // use those to disable tab completion.
160 if (this.is_completing && event.keyCode !== 9) {
160 if (this.is_completing && event.keyCode !== 9) {
161 var ed_cur = editor.getCursor();
161 var ed_cur = editor.getCursor();
162 var cc_cur = this.completion_cursor;
162 var cc_cur = this.completion_cursor;
163 if (ed_cur.line !== cc_cur.line || ed_cur.ch !== cc_cur.ch) {
163 if (ed_cur.line !== cc_cur.line || ed_cur.ch !== cc_cur.ch) {
164 this.is_completing = false;
164 this.is_completing = false;
165 this.completion_cursor = null;
165 this.completion_cursor = null;
166 };
166 };
167 };
167 };
168 return false;
168 return false;
169 };
169 };
170 return false;
170 return false;
171 };
171 };
172
172
173 CodeCell.prototype.remove_and_cancel_tooltip = function() {
173 CodeCell.prototype.remove_and_cancel_tooltip = function() {
174 // note that we don't handle closing directly inside the calltip
174 // note that we don't handle closing directly inside the calltip
175 // as in the completer, because it is not focusable, so won't
175 // as in the completer, because it is not focusable, so won't
176 // get the event.
176 // get the event.
177 if (this.tooltip_timeout != null){
177 if (this.tooltip_timeout != null){
178 clearTimeout(this.tooltip_timeout);
178 clearTimeout(this.tooltip_timeout);
179 $('#tooltip').remove();
179 $('#tooltip').remove();
180 this.tooltip_timeout = null;
180 this.tooltip_timeout = null;
181 }
181 }
182 }
182 }
183
183
184 CodeCell.prototype.finish_tooltip = function (reply) {
184 CodeCell.prototype.finish_tooltip = function (reply) {
185 // Extract call tip data; the priority is call, init, main.
185 // Extract call tip data; the priority is call, init, main.
186 defstring = reply.call_def;
186 defstring = reply.call_def;
187 if (defstring == null) { defstring = reply.init_definition; }
187 if (defstring == null) { defstring = reply.init_definition; }
188 if (defstring == null) { defstring = reply.definition; }
188 if (defstring == null) { defstring = reply.definition; }
189
189
190 docstring = reply.call_docstring;
190 docstring = reply.call_docstring;
191 if (docstring == null) { docstring = reply.init_docstring; }
191 if (docstring == null) { docstring = reply.init_docstring; }
192 if (docstring == null) { docstring = reply.docstring; }
192 if (docstring == null) { docstring = reply.docstring; }
193 if (docstring == null) { docstring = "<empty docstring>"; }
193 if (docstring == null) { docstring = "<empty docstring>"; }
194
194
195 name=reply.name;
195 name=reply.name;
196
196
197 var that = this;
197 var that = this;
198 var tooltip = $('<div/>').attr('id', 'tooltip').addClass('tooltip');
198 var tooltip = $('<div/>').attr('id', 'tooltip').addClass('tooltip');
199 // remove to have the tooltip not Limited in X and Y
199 // remove to have the tooltip not Limited in X and Y
200 tooltip.addClass('smalltooltip');
200 tooltip.addClass('smalltooltip');
201 var pre=$('<pre/>').html(utils.fixConsole(docstring));
201 var pre=$('<pre/>').html(utils.fixConsole(docstring));
202 var expandlink=$('<a/>').attr('href',"#");
202 var expandlink=$('<a/>').attr('href',"#");
203 expandlink.addClass("ui-corner-all"); //rounded corner
203 expandlink.addClass("ui-corner-all"); //rounded corner
204 expandlink.attr('role',"button");
204 expandlink.attr('role',"button");
205 //expandlink.addClass('ui-button');
205 //expandlink.addClass('ui-button');
206 //expandlink.addClass('ui-state-default');
206 //expandlink.addClass('ui-state-default');
207 var expandspan=$('<span/>').text('Expand');
207 var expandspan=$('<span/>').text('Expand');
208 expandspan.addClass('ui-icon');
208 expandspan.addClass('ui-icon');
209 expandspan.addClass('ui-icon-plus');
209 expandspan.addClass('ui-icon-plus');
210 expandlink.append(expandspan);
210 expandlink.append(expandspan);
211 expandlink.attr('id','expanbutton');
211 expandlink.attr('id','expanbutton');
212 expandlink.click(function(){
212 expandlink.click(function(){
213 tooltip.removeClass('smalltooltip');
213 tooltip.removeClass('smalltooltip');
214 tooltip.addClass('bigtooltip');
214 tooltip.addClass('bigtooltip');
215 $('#expanbutton').remove();
215 $('#expanbutton').remove();
216 setTimeout(function(){that.code_mirror.focus();}, 50);
216 setTimeout(function(){that.code_mirror.focus();}, 50);
217 });
217 });
218 var morelink=$('<a/>').attr('href',"#");
218 var morelink=$('<a/>').attr('href',"#");
219 morelink.attr('role',"button");
219 morelink.attr('role',"button");
220 morelink.addClass('ui-button');
220 morelink.addClass('ui-button');
221 //morelink.addClass("ui-corner-all"); //rounded corner
221 //morelink.addClass("ui-corner-all"); //rounded corner
222 //morelink.addClass('ui-state-default');
222 //morelink.addClass('ui-state-default');
223 var morespan=$('<span/>').text('Open in Pager');
223 var morespan=$('<span/>').text('Open in Pager');
224 morespan.addClass('ui-icon');
224 morespan.addClass('ui-icon');
225 morespan.addClass('ui-icon-arrowstop-l-n');
225 morespan.addClass('ui-icon-arrowstop-l-n');
226 morelink.append(morespan);
226 morelink.append(morespan);
227 morelink.click(function(){
227 morelink.click(function(){
228 var msg_id = IPython.notebook.kernel.execute(name+"?");
228 var msg_id = IPython.notebook.kernel.execute(name+"?");
229 IPython.notebook.msg_cell_map[msg_id] = IPython.notebook.get_selected_cell().cell_id;
229 IPython.notebook.msg_cell_map[msg_id] = IPython.notebook.get_selected_cell().cell_id;
230 that.remove_and_cancel_tooltip();
230 that.remove_and_cancel_tooltip();
231 setTimeout(function(){that.code_mirror.focus();}, 50);
231 setTimeout(function(){that.code_mirror.focus();}, 50);
232 });
232 });
233
233
234 var closelink=$('<a/>').attr('href',"#");
234 var closelink=$('<a/>').attr('href',"#");
235 closelink.attr('role',"button");
235 closelink.attr('role',"button");
236 closelink.addClass('ui-button');
236 closelink.addClass('ui-button');
237 //closelink.addClass("ui-corner-all"); //rounded corner
237 //closelink.addClass("ui-corner-all"); //rounded corner
238 //closelink.adClass('ui-state-default'); // grey background and blue cross
238 //closelink.adClass('ui-state-default'); // grey background and blue cross
239 var closespan=$('<span/>').text('Close');
239 var closespan=$('<span/>').text('Close');
240 closespan.addClass('ui-icon');
240 closespan.addClass('ui-icon');
241 closespan.addClass('ui-icon-close');
241 closespan.addClass('ui-icon-close');
242 closelink.append(closespan);
242 closelink.append(closespan);
243 closelink.click(function(){
243 closelink.click(function(){
244 that.remove_and_cancel_tooltip();
244 that.remove_and_cancel_tooltip();
245 setTimeout(function(){that.code_mirror.focus();}, 50);
245 setTimeout(function(){that.code_mirror.focus();}, 50);
246 });
246 });
247 //construct the tooltip
247 //construct the tooltip
248 tooltip.append(closelink);
248 tooltip.append(closelink);
249 tooltip.append(expandlink);
249 tooltip.append(expandlink);
250 tooltip.append(morelink);
250 tooltip.append(morelink);
251 if(defstring){
251 if(defstring){
252 defstring_html = $('<pre/>').html(utils.fixConsole(defstring));
252 defstring_html = $('<pre/>').html(utils.fixConsole(defstring));
253 tooltip.append(defstring_html);
253 tooltip.append(defstring_html);
254 }
254 }
255 tooltip.append(pre);
255 tooltip.append(pre);
256 var pos = this.code_mirror.cursorCoords();
256 var pos = this.code_mirror.cursorCoords();
257 tooltip.css('left',pos.x+'px');
257 tooltip.css('left',pos.x+'px');
258 tooltip.css('top',pos.yBot+'px');
258 tooltip.css('top',pos.yBot+'px');
259 $('body').append(tooltip);
259 $('body').append(tooltip);
260
260
261 // issues with cross-closing if multiple tooltip in less than 5sec
261 // issues with cross-closing if multiple tooltip in less than 5sec
262 // keep it comented for now
262 // keep it comented for now
263 // setTimeout(that.remove_and_cancel_tooltip, 5000);
263 // setTimeout(that.remove_and_cancel_tooltip, 5000);
264 };
264 };
265
265
266 // As you type completer
266 // As you type completer
267 // this should be called by the completer, that in return will
268 // be reclled by finish_completing
267 CodeCell.prototype.requestCompletion= function(callback)
269 CodeCell.prototype.requestCompletion= function(callback)
268 {
270 {
269 this._compcallback = callback;
271 this._compcallback = callback;
270 var cur = this.code_mirror.getCursor();
272 var cur = this.code_mirror.getCursor();
271 var pre_cursor = this.code_mirror.getRange({line:cur.line,ch:0},cur);
273 var pre_cursor = this.code_mirror.getRange({line:cur.line,ch:0},cur);
272 pre_cursor.trim();
274 pre_cursor.trim();
273 // Autocomplete the current line.
275 // Autocomplete the current line.
274 var line = this.code_mirror.getLine(cur.line);
276 var line = this.code_mirror.getLine(cur.line);
275 this.is_completing = true;
277 this.is_completing = true;
276 this.completion_cursor = cur;
278 this.completion_cursor = cur;
279 // one could fork here and directly call finish completing
280 // if kernel is busy
277 IPython.notebook.complete_cell(this, line, cur.ch);
281 IPython.notebook.complete_cell(this, line, cur.ch);
278 }
282 }
279
283
284 // called when completion came back from the kernel. this will inspect the
285 // curent cell for (more) completion merge the resuults with the ones
286 // comming from the kernel and forward it to the completer
280 CodeCell.prototype.finish_completing = function (matched_text, matches) {
287 CodeCell.prototype.finish_completing = function (matched_text, matches) {
281 // let's build a function that wrap all that stuff into what is needed for the
288 // let's build a function that wrap all that stuff into what is needed for the
282 // new completer:
289 // new completer:
283 //
290 //
284 var cur = this.code_mirror.getCursor();
291 var cur = this.code_mirror.getCursor();
285 var res = CodeMirror.contextHint(this.code_mirror);
292 var res = CodeMirror.contextHint(this.code_mirror);
286
293
287 // append the introspection result, in order, at
294 // append the introspection result, in order, at
288 // at the beginning of the table and compute the replacement rance
295 // at the beginning of the table and compute the replacement rance
289 // from current cursor positon and matched_text length.
296 // from current cursor positon and matched_text length.
290 for(var i= matches.length-1; i>=0 ;--i)
297 for(var i= matches.length-1; i>=0 ;--i)
291 {
298 {
292 res.unshift(
299 res.unshift(
293 {
300 {
294 str : matches[i],
301 str : matches[i],
295 type : "introspection",
302 type : "introspection",
296 from : {line: cur.line, ch: cur.ch-matched_text.length},
303 from : {line: cur.line, ch: cur.ch-matched_text.length},
297 to : {line: cur.line, ch: cur.ch}
304 to : {line: cur.line, ch: cur.ch}
298 }
305 }
299 )
306 )
300 }
307 }
301 this._compcallback(res);
308 this._compcallback(res);
302 };
309 };
303
310
304
311
305 CodeCell.prototype.select = function () {
312 CodeCell.prototype.select = function () {
306 IPython.Cell.prototype.select.apply(this);
313 IPython.Cell.prototype.select.apply(this);
307 this.code_mirror.refresh();
314 this.code_mirror.refresh();
308 this.code_mirror.focus();
315 this.code_mirror.focus();
309 // We used to need an additional refresh() after the focus, but
316 // We used to need an additional refresh() after the focus, but
310 // it appears that this has been fixed in CM. This bug would show
317 // it appears that this has been fixed in CM. This bug would show
311 // up on FF when a newly loaded markdown cell was edited.
318 // up on FF when a newly loaded markdown cell was edited.
312 };
319 };
313
320
314
321
315 CodeCell.prototype.select_all = function () {
322 CodeCell.prototype.select_all = function () {
316 var start = {line: 0, ch: 0};
323 var start = {line: 0, ch: 0};
317 var nlines = this.code_mirror.lineCount();
324 var nlines = this.code_mirror.lineCount();
318 var last_line = this.code_mirror.getLine(nlines-1);
325 var last_line = this.code_mirror.getLine(nlines-1);
319 var end = {line: nlines-1, ch: last_line.length};
326 var end = {line: nlines-1, ch: last_line.length};
320 this.code_mirror.setSelection(start, end);
327 this.code_mirror.setSelection(start, end);
321 };
328 };
322
329
323
330
324 CodeCell.prototype.append_output = function (json, dynamic) {
331 CodeCell.prototype.append_output = function (json, dynamic) {
325 // If dynamic is true, javascript output will be eval'd.
332 // If dynamic is true, javascript output will be eval'd.
326 this.expand();
333 this.expand();
327 this.flush_clear_timeout();
334 this.flush_clear_timeout();
328 if (json.output_type === 'pyout') {
335 if (json.output_type === 'pyout') {
329 this.append_pyout(json, dynamic);
336 this.append_pyout(json, dynamic);
330 } else if (json.output_type === 'pyerr') {
337 } else if (json.output_type === 'pyerr') {
331 this.append_pyerr(json);
338 this.append_pyerr(json);
332 } else if (json.output_type === 'display_data') {
339 } else if (json.output_type === 'display_data') {
333 this.append_display_data(json, dynamic);
340 this.append_display_data(json, dynamic);
334 } else if (json.output_type === 'stream') {
341 } else if (json.output_type === 'stream') {
335 this.append_stream(json);
342 this.append_stream(json);
336 };
343 };
337 this.outputs.push(json);
344 this.outputs.push(json);
338 };
345 };
339
346
340
347
341 CodeCell.prototype.create_output_area = function () {
348 CodeCell.prototype.create_output_area = function () {
342 var oa = $("<div/>").addClass("hbox output_area");
349 var oa = $("<div/>").addClass("hbox output_area");
343 oa.append($('<div/>').addClass('prompt'));
350 oa.append($('<div/>').addClass('prompt'));
344 return oa;
351 return oa;
345 };
352 };
346
353
347
354
348 CodeCell.prototype.append_pyout = function (json, dynamic) {
355 CodeCell.prototype.append_pyout = function (json, dynamic) {
349 var n = json.prompt_number || ' ';
356 var n = json.prompt_number || ' ';
350 var toinsert = this.create_output_area();
357 var toinsert = this.create_output_area();
351 toinsert.find('div.prompt').addClass('output_prompt').html('Out[' + n + ']:');
358 toinsert.find('div.prompt').addClass('output_prompt').html('Out[' + n + ']:');
352 this.append_mime_type(json, toinsert, dynamic);
359 this.append_mime_type(json, toinsert, dynamic);
353 this.element.find('div.output').append(toinsert);
360 this.element.find('div.output').append(toinsert);
354 // If we just output latex, typeset it.
361 // If we just output latex, typeset it.
355 if ((json.latex !== undefined) || (json.html !== undefined)) {
362 if ((json.latex !== undefined) || (json.html !== undefined)) {
356 this.typeset();
363 this.typeset();
357 };
364 };
358 };
365 };
359
366
360
367
361 CodeCell.prototype.append_pyerr = function (json) {
368 CodeCell.prototype.append_pyerr = function (json) {
362 var tb = json.traceback;
369 var tb = json.traceback;
363 if (tb !== undefined && tb.length > 0) {
370 if (tb !== undefined && tb.length > 0) {
364 var s = '';
371 var s = '';
365 var len = tb.length;
372 var len = tb.length;
366 for (var i=0; i<len; i++) {
373 for (var i=0; i<len; i++) {
367 s = s + tb[i] + '\n';
374 s = s + tb[i] + '\n';
368 }
375 }
369 s = s + '\n';
376 s = s + '\n';
370 var toinsert = this.create_output_area();
377 var toinsert = this.create_output_area();
371 this.append_text(s, toinsert);
378 this.append_text(s, toinsert);
372 this.element.find('div.output').append(toinsert);
379 this.element.find('div.output').append(toinsert);
373 };
380 };
374 };
381 };
375
382
376
383
377 CodeCell.prototype.append_stream = function (json) {
384 CodeCell.prototype.append_stream = function (json) {
378 // temporary fix: if stream undefined (json file written prior to this patch),
385 // temporary fix: if stream undefined (json file written prior to this patch),
379 // default to most likely stdout:
386 // default to most likely stdout:
380 if (json.stream == undefined){
387 if (json.stream == undefined){
381 json.stream = 'stdout';
388 json.stream = 'stdout';
382 }
389 }
383 if (!utils.fixConsole(json.text)){
390 if (!utils.fixConsole(json.text)){
384 // fixConsole gives nothing (empty string, \r, etc.)
391 // fixConsole gives nothing (empty string, \r, etc.)
385 // so don't append any elements, which might add undesirable space
392 // so don't append any elements, which might add undesirable space
386 return;
393 return;
387 }
394 }
388 var subclass = "output_"+json.stream;
395 var subclass = "output_"+json.stream;
389 if (this.outputs.length > 0){
396 if (this.outputs.length > 0){
390 // have at least one output to consider
397 // have at least one output to consider
391 var last = this.outputs[this.outputs.length-1];
398 var last = this.outputs[this.outputs.length-1];
392 if (last.output_type == 'stream' && json.stream == last.stream){
399 if (last.output_type == 'stream' && json.stream == last.stream){
393 // latest output was in the same stream,
400 // latest output was in the same stream,
394 // so append directly into its pre tag
401 // so append directly into its pre tag
395 // escape ANSI & HTML specials:
402 // escape ANSI & HTML specials:
396 var text = utils.fixConsole(json.text);
403 var text = utils.fixConsole(json.text);
397 this.element.find('div.'+subclass).last().find('pre').append(text);
404 this.element.find('div.'+subclass).last().find('pre').append(text);
398 return;
405 return;
399 }
406 }
400 }
407 }
401
408
402 // If we got here, attach a new div
409 // If we got here, attach a new div
403 var toinsert = this.create_output_area();
410 var toinsert = this.create_output_area();
404 this.append_text(json.text, toinsert, "output_stream "+subclass);
411 this.append_text(json.text, toinsert, "output_stream "+subclass);
405 this.element.find('div.output').append(toinsert);
412 this.element.find('div.output').append(toinsert);
406 };
413 };
407
414
408
415
409 CodeCell.prototype.append_display_data = function (json, dynamic) {
416 CodeCell.prototype.append_display_data = function (json, dynamic) {
410 var toinsert = this.create_output_area();
417 var toinsert = this.create_output_area();
411 this.append_mime_type(json, toinsert, dynamic);
418 this.append_mime_type(json, toinsert, dynamic);
412 this.element.find('div.output').append(toinsert);
419 this.element.find('div.output').append(toinsert);
413 // If we just output latex, typeset it.
420 // If we just output latex, typeset it.
414 if ( (json.latex !== undefined) || (json.html !== undefined) ) {
421 if ( (json.latex !== undefined) || (json.html !== undefined) ) {
415 this.typeset();
422 this.typeset();
416 };
423 };
417 };
424 };
418
425
419
426
420 CodeCell.prototype.append_mime_type = function (json, element, dynamic) {
427 CodeCell.prototype.append_mime_type = function (json, element, dynamic) {
421 if (json.javascript !== undefined && dynamic) {
428 if (json.javascript !== undefined && dynamic) {
422 this.append_javascript(json.javascript, element, dynamic);
429 this.append_javascript(json.javascript, element, dynamic);
423 } else if (json.html !== undefined) {
430 } else if (json.html !== undefined) {
424 this.append_html(json.html, element);
431 this.append_html(json.html, element);
425 } else if (json.latex !== undefined) {
432 } else if (json.latex !== undefined) {
426 this.append_latex(json.latex, element);
433 this.append_latex(json.latex, element);
427 } else if (json.svg !== undefined) {
434 } else if (json.svg !== undefined) {
428 this.append_svg(json.svg, element);
435 this.append_svg(json.svg, element);
429 } else if (json.png !== undefined) {
436 } else if (json.png !== undefined) {
430 this.append_png(json.png, element);
437 this.append_png(json.png, element);
431 } else if (json.jpeg !== undefined) {
438 } else if (json.jpeg !== undefined) {
432 this.append_jpeg(json.jpeg, element);
439 this.append_jpeg(json.jpeg, element);
433 } else if (json.text !== undefined) {
440 } else if (json.text !== undefined) {
434 this.append_text(json.text, element);
441 this.append_text(json.text, element);
435 };
442 };
436 };
443 };
437
444
438
445
439 CodeCell.prototype.append_html = function (html, element) {
446 CodeCell.prototype.append_html = function (html, element) {
440 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_html rendered_html");
447 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_html rendered_html");
441 toinsert.append(html);
448 toinsert.append(html);
442 element.append(toinsert);
449 element.append(toinsert);
443 };
450 };
444
451
445
452
446 CodeCell.prototype.append_javascript = function (js, container) {
453 CodeCell.prototype.append_javascript = function (js, container) {
447 // We just eval the JS code, element appears in the local scope.
454 // We just eval the JS code, element appears in the local scope.
448 var element = $("<div/>").addClass("box_flex1 output_subarea");
455 var element = $("<div/>").addClass("box_flex1 output_subarea");
449 container.append(element);
456 container.append(element);
450 // Div for js shouldn't be drawn, as it will add empty height to the area.
457 // Div for js shouldn't be drawn, as it will add empty height to the area.
451 container.hide();
458 container.hide();
452 // If the Javascript appends content to `element` that should be drawn, then
459 // If the Javascript appends content to `element` that should be drawn, then
453 // it must also call `container.show()`.
460 // it must also call `container.show()`.
454 eval(js);
461 eval(js);
455 }
462 }
456
463
457
464
458 CodeCell.prototype.append_text = function (data, element, extra_class) {
465 CodeCell.prototype.append_text = function (data, element, extra_class) {
459 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_text");
466 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_text");
460 // escape ANSI & HTML specials in plaintext:
467 // escape ANSI & HTML specials in plaintext:
461 data = utils.fixConsole(data);
468 data = utils.fixConsole(data);
462 if (extra_class){
469 if (extra_class){
463 toinsert.addClass(extra_class);
470 toinsert.addClass(extra_class);
464 }
471 }
465 toinsert.append($("<pre/>").html(data));
472 toinsert.append($("<pre/>").html(data));
466 element.append(toinsert);
473 element.append(toinsert);
467 };
474 };
468
475
469
476
470 CodeCell.prototype.append_svg = function (svg, element) {
477 CodeCell.prototype.append_svg = function (svg, element) {
471 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_svg");
478 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_svg");
472 toinsert.append(svg);
479 toinsert.append(svg);
473 element.append(toinsert);
480 element.append(toinsert);
474 };
481 };
475
482
476
483
477 CodeCell.prototype.append_png = function (png, element) {
484 CodeCell.prototype.append_png = function (png, element) {
478 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_png");
485 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_png");
479 toinsert.append($("<img/>").attr('src','data:image/png;base64,'+png));
486 toinsert.append($("<img/>").attr('src','data:image/png;base64,'+png));
480 element.append(toinsert);
487 element.append(toinsert);
481 };
488 };
482
489
483
490
484 CodeCell.prototype.append_jpeg = function (jpeg, element) {
491 CodeCell.prototype.append_jpeg = function (jpeg, element) {
485 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_jpeg");
492 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_jpeg");
486 toinsert.append($("<img/>").attr('src','data:image/jpeg;base64,'+jpeg));
493 toinsert.append($("<img/>").attr('src','data:image/jpeg;base64,'+jpeg));
487 element.append(toinsert);
494 element.append(toinsert);
488 };
495 };
489
496
490
497
491 CodeCell.prototype.append_latex = function (latex, element) {
498 CodeCell.prototype.append_latex = function (latex, element) {
492 // This method cannot do the typesetting because the latex first has to
499 // This method cannot do the typesetting because the latex first has to
493 // be on the page.
500 // be on the page.
494 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_latex");
501 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_latex");
495 toinsert.append(latex);
502 toinsert.append(latex);
496 element.append(toinsert);
503 element.append(toinsert);
497 };
504 };
498
505
499
506
500 CodeCell.prototype.clear_output = function (stdout, stderr, other) {
507 CodeCell.prototype.clear_output = function (stdout, stderr, other) {
501 var that = this;
508 var that = this;
502 if (this.clear_out_timeout != null){
509 if (this.clear_out_timeout != null){
503 // fire previous pending clear *immediately*
510 // fire previous pending clear *immediately*
504 clearTimeout(this.clear_out_timeout);
511 clearTimeout(this.clear_out_timeout);
505 this.clear_out_timeout = null;
512 this.clear_out_timeout = null;
506 this.clear_output_callback(this._clear_stdout, this._clear_stderr, this._clear_other);
513 this.clear_output_callback(this._clear_stdout, this._clear_stderr, this._clear_other);
507 }
514 }
508 // store flags for flushing the timeout
515 // store flags for flushing the timeout
509 this._clear_stdout = stdout;
516 this._clear_stdout = stdout;
510 this._clear_stderr = stderr;
517 this._clear_stderr = stderr;
511 this._clear_other = other;
518 this._clear_other = other;
512 this.clear_out_timeout = setTimeout(function(){
519 this.clear_out_timeout = setTimeout(function(){
513 // really clear timeout only after a short delay
520 // really clear timeout only after a short delay
514 // this reduces flicker in 'clear_output; print' cases
521 // this reduces flicker in 'clear_output; print' cases
515 that.clear_out_timeout = null;
522 that.clear_out_timeout = null;
516 that._clear_stdout = that._clear_stderr = that._clear_other = null;
523 that._clear_stdout = that._clear_stderr = that._clear_other = null;
517 that.clear_output_callback(stdout, stderr, other);
524 that.clear_output_callback(stdout, stderr, other);
518 }, 500
525 }, 500
519 );
526 );
520 };
527 };
521
528
522 CodeCell.prototype.clear_output_callback = function (stdout, stderr, other) {
529 CodeCell.prototype.clear_output_callback = function (stdout, stderr, other) {
523 var output_div = this.element.find("div.output");
530 var output_div = this.element.find("div.output");
524
531
525 if (stdout && stderr && other){
532 if (stdout && stderr && other){
526 // clear all, no need for logic
533 // clear all, no need for logic
527 output_div.html("");
534 output_div.html("");
528 this.outputs = [];
535 this.outputs = [];
529 return;
536 return;
530 }
537 }
531 // remove html output
538 // remove html output
532 // each output_subarea that has an identifying class is in an output_area
539 // each output_subarea that has an identifying class is in an output_area
533 // which is the element to be removed.
540 // which is the element to be removed.
534 if (stdout){
541 if (stdout){
535 output_div.find("div.output_stdout").parent().remove();
542 output_div.find("div.output_stdout").parent().remove();
536 }
543 }
537 if (stderr){
544 if (stderr){
538 output_div.find("div.output_stderr").parent().remove();
545 output_div.find("div.output_stderr").parent().remove();
539 }
546 }
540 if (other){
547 if (other){
541 output_div.find("div.output_subarea").not("div.output_stderr").not("div.output_stdout").parent().remove();
548 output_div.find("div.output_subarea").not("div.output_stderr").not("div.output_stdout").parent().remove();
542 }
549 }
543
550
544 // remove cleared outputs from JSON list:
551 // remove cleared outputs from JSON list:
545 for (var i = this.outputs.length - 1; i >= 0; i--){
552 for (var i = this.outputs.length - 1; i >= 0; i--){
546 var out = this.outputs[i];
553 var out = this.outputs[i];
547 var output_type = out.output_type;
554 var output_type = out.output_type;
548 if (output_type == "display_data" && other){
555 if (output_type == "display_data" && other){
549 this.outputs.splice(i,1);
556 this.outputs.splice(i,1);
550 }else if (output_type == "stream"){
557 }else if (output_type == "stream"){
551 if (stdout && out.stream == "stdout"){
558 if (stdout && out.stream == "stdout"){
552 this.outputs.splice(i,1);
559 this.outputs.splice(i,1);
553 }else if (stderr && out.stream == "stderr"){
560 }else if (stderr && out.stream == "stderr"){
554 this.outputs.splice(i,1);
561 this.outputs.splice(i,1);
555 }
562 }
556 }
563 }
557 }
564 }
558 };
565 };
559
566
560
567
561 CodeCell.prototype.clear_input = function () {
568 CodeCell.prototype.clear_input = function () {
562 this.code_mirror.setValue('');
569 this.code_mirror.setValue('');
563 };
570 };
564
571
565 CodeCell.prototype.flush_clear_timeout = function() {
572 CodeCell.prototype.flush_clear_timeout = function() {
566 var output_div = this.element.find('div.output');
573 var output_div = this.element.find('div.output');
567 if (this.clear_out_timeout){
574 if (this.clear_out_timeout){
568 clearTimeout(this.clear_out_timeout);
575 clearTimeout(this.clear_out_timeout);
569 this.clear_out_timeout = null;
576 this.clear_out_timeout = null;
570 this.clear_output_callback(this._clear_stdout, this._clear_stderr, this._clear_other);
577 this.clear_output_callback(this._clear_stdout, this._clear_stderr, this._clear_other);
571 };
578 };
572 }
579 }
573
580
574
581
575 CodeCell.prototype.collapse = function () {
582 CodeCell.prototype.collapse = function () {
576 if (!this.collapsed) {
583 if (!this.collapsed) {
577 this.element.find('div.output').hide();
584 this.element.find('div.output').hide();
578 this.collapsed = true;
585 this.collapsed = true;
579 };
586 };
580 };
587 };
581
588
582
589
583 CodeCell.prototype.expand = function () {
590 CodeCell.prototype.expand = function () {
584 if (this.collapsed) {
591 if (this.collapsed) {
585 this.element.find('div.output').show();
592 this.element.find('div.output').show();
586 this.collapsed = false;
593 this.collapsed = false;
587 };
594 };
588 };
595 };
589
596
590
597
591 CodeCell.prototype.toggle_output = function () {
598 CodeCell.prototype.toggle_output = function () {
592 if (this.collapsed) {
599 if (this.collapsed) {
593 this.expand();
600 this.expand();
594 } else {
601 } else {
595 this.collapse();
602 this.collapse();
596 };
603 };
597 };
604 };
598
605
599 CodeCell.prototype.set_input_prompt = function (number) {
606 CodeCell.prototype.set_input_prompt = function (number) {
600 this.input_prompt_number = number;
607 this.input_prompt_number = number;
601 var ns = number || "&nbsp;";
608 var ns = number || "&nbsp;";
602 this.element.find('div.input_prompt').html('In&nbsp;[' + ns + ']:');
609 this.element.find('div.input_prompt').html('In&nbsp;[' + ns + ']:');
603 };
610 };
604
611
605
612
606 CodeCell.prototype.get_text = function () {
613 CodeCell.prototype.get_text = function () {
607 return this.code_mirror.getValue();
614 return this.code_mirror.getValue();
608 };
615 };
609
616
610
617
611 CodeCell.prototype.set_text = function (code) {
618 CodeCell.prototype.set_text = function (code) {
612 return this.code_mirror.setValue(code);
619 return this.code_mirror.setValue(code);
613 };
620 };
614
621
615
622
616 CodeCell.prototype.at_top = function () {
623 CodeCell.prototype.at_top = function () {
617 var cursor = this.code_mirror.getCursor();
624 var cursor = this.code_mirror.getCursor();
618 if (cursor.line === 0) {
625 if (cursor.line === 0) {
619 return true;
626 return true;
620 } else {
627 } else {
621 return false;
628 return false;
622 }
629 }
623 };
630 };
624
631
625
632
626 CodeCell.prototype.at_bottom = function () {
633 CodeCell.prototype.at_bottom = function () {
627 var cursor = this.code_mirror.getCursor();
634 var cursor = this.code_mirror.getCursor();
628 if (cursor.line === (this.code_mirror.lineCount()-1)) {
635 if (cursor.line === (this.code_mirror.lineCount()-1)) {
629 return true;
636 return true;
630 } else {
637 } else {
631 return false;
638 return false;
632 }
639 }
633 };
640 };
634
641
635
642
636 CodeCell.prototype.fromJSON = function (data) {
643 CodeCell.prototype.fromJSON = function (data) {
637 if (data.cell_type === 'code') {
644 if (data.cell_type === 'code') {
638 if (data.input !== undefined) {
645 if (data.input !== undefined) {
639 this.set_text(data.input);
646 this.set_text(data.input);
640 }
647 }
641 if (data.prompt_number !== undefined) {
648 if (data.prompt_number !== undefined) {
642 this.set_input_prompt(data.prompt_number);
649 this.set_input_prompt(data.prompt_number);
643 } else {
650 } else {
644 this.set_input_prompt();
651 this.set_input_prompt();
645 };
652 };
646 var len = data.outputs.length;
653 var len = data.outputs.length;
647 for (var i=0; i<len; i++) {
654 for (var i=0; i<len; i++) {
648 // append with dynamic=false.
655 // append with dynamic=false.
649 this.append_output(data.outputs[i], false);
656 this.append_output(data.outputs[i], false);
650 };
657 };
651 if (data.collapsed !== undefined) {
658 if (data.collapsed !== undefined) {
652 if (data.collapsed) {
659 if (data.collapsed) {
653 this.collapse();
660 this.collapse();
654 } else {
661 } else {
655 this.expand();
662 this.expand();
656 };
663 };
657 };
664 };
658 };
665 };
659 };
666 };
660
667
661
668
662 CodeCell.prototype.toJSON = function () {
669 CodeCell.prototype.toJSON = function () {
663 var data = {};
670 var data = {};
664 data.input = this.get_text();
671 data.input = this.get_text();
665 data.cell_type = 'code';
672 data.cell_type = 'code';
666 if (this.input_prompt_number) {
673 if (this.input_prompt_number) {
667 data.prompt_number = this.input_prompt_number;
674 data.prompt_number = this.input_prompt_number;
668 };
675 };
669 var outputs = [];
676 var outputs = [];
670 var len = this.outputs.length;
677 var len = this.outputs.length;
671 for (var i=0; i<len; i++) {
678 for (var i=0; i<len; i++) {
672 outputs[i] = this.outputs[i];
679 outputs[i] = this.outputs[i];
673 };
680 };
674 data.outputs = outputs;
681 data.outputs = outputs;
675 data.language = 'python';
682 data.language = 'python';
676 data.collapsed = this.collapsed;
683 data.collapsed = this.collapsed;
677 return data;
684 return data;
678 };
685 };
679
686
680
687
681 IPython.CodeCell = CodeCell;
688 IPython.CodeCell = CodeCell;
682
689
683 return IPython;
690 return IPython;
684 }(IPython));
691 }(IPython));
General Comments 0
You need to be logged in to leave comments. Login now