##// END OF EJS Templates
fix firefox (windows) break line on empty prompt number
Bussonnier Matthias -
Show More
@@ -1,674 +1,674 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
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 // whatever key is pressed, first, cancel the tooltip request before
74 // whatever key is pressed, first, cancel the tooltip request before
75 // they are sent, and remove tooltip if any
75 // they are sent, and remove tooltip if any
76 if(event.type === 'keydown' && this.tooltip_timeout != null){
76 if(event.type === 'keydown' && this.tooltip_timeout != null){
77 CodeCell.prototype.remove_and_cancell_tooltip(that.tooltip_timeout);
77 CodeCell.prototype.remove_and_cancell_tooltip(that.tooltip_timeout);
78 that.tooltip_timeout=null;
78 that.tooltip_timeout=null;
79 }
79 }
80
80
81 if (event.keyCode === 13 && (event.shiftKey || event.ctrlKey)) {
81 if (event.keyCode === 13 && (event.shiftKey || event.ctrlKey)) {
82 // Always ignore shift-enter in CodeMirror as we handle it.
82 // Always ignore shift-enter in CodeMirror as we handle it.
83 return true;
83 return true;
84 }else if (event.which === 40 && event.type === 'keypress' && 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
85 // triger aon keypress (!) otherwise inconsistent event.which depending on plateform
86 // browser and keyboard layout !
86 // browser and keyboard layout !
87 // Pressing '(' , request tooltip, don't forget to reappend it
87 // Pressing '(' , request tooltip, don't forget to reappend it
88 var cursor = editor.getCursor();
88 var cursor = editor.getCursor();
89 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()+'(';
90 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);
91 } else if (event.keyCode === 9 && event.type == 'keydown') {
91 } else if (event.keyCode === 9 && event.type == 'keydown') {
92 // Tab completion.
92 // Tab completion.
93 var cur = editor.getCursor();
93 var cur = editor.getCursor();
94 //Do not trim here because of tooltip
94 //Do not trim here because of tooltip
95 var pre_cursor = editor.getRange({line:cur.line,ch:0},cur);
95 var pre_cursor = editor.getRange({line:cur.line,ch:0},cur);
96 if (pre_cursor.trim() === "") {
96 if (pre_cursor.trim() === "") {
97 // 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
98 // is empty. In this case, let CodeMirror handle indentation.
98 // is empty. In this case, let CodeMirror handle indentation.
99 return false;
99 return false;
100 } 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 ) {
101 CodeCell.prototype.request_tooltip_after_time(pre_cursor,0,that);
101 CodeCell.prototype.request_tooltip_after_time(pre_cursor,0,that);
102 } else {
102 } else {
103 pre_cursor.trim();
103 pre_cursor.trim();
104 // Autocomplete the current line.
104 // Autocomplete the current line.
105 event.stop();
105 event.stop();
106 var line = editor.getLine(cur.line);
106 var line = editor.getLine(cur.line);
107 this.is_completing = true;
107 this.is_completing = true;
108 this.completion_cursor = cur;
108 this.completion_cursor = cur;
109 IPython.notebook.complete_cell(this, line, cur.ch);
109 IPython.notebook.complete_cell(this, line, cur.ch);
110 return true;
110 return true;
111 }
111 }
112 } else if (event.keyCode === 8 && event.type == 'keydown') {
112 } else if (event.keyCode === 8 && event.type == 'keydown') {
113 // If backspace and the line ends with 4 spaces, remove them.
113 // If backspace and the line ends with 4 spaces, remove them.
114 var cur = editor.getCursor();
114 var cur = editor.getCursor();
115 var line = editor.getLine(cur.line);
115 var line = editor.getLine(cur.line);
116 var ending = line.slice(-4);
116 var ending = line.slice(-4);
117 if (ending === ' ') {
117 if (ending === ' ') {
118 editor.replaceRange('',
118 editor.replaceRange('',
119 {line: cur.line, ch: cur.ch-4},
119 {line: cur.line, ch: cur.ch-4},
120 {line: cur.line, ch: cur.ch}
120 {line: cur.line, ch: cur.ch}
121 );
121 );
122 event.stop();
122 event.stop();
123 return true;
123 return true;
124 } else {
124 } else {
125 return false;
125 return false;
126 };
126 };
127 } else if (event.keyCode === 76 && event.ctrlKey && event.shiftKey
127 } else if (event.keyCode === 76 && event.ctrlKey && event.shiftKey
128 && event.type == 'keydown') {
128 && event.type == 'keydown') {
129 // toggle line numbers with Ctrl-Shift-L
129 // toggle line numbers with Ctrl-Shift-L
130 this.toggle_line_numbers();
130 this.toggle_line_numbers();
131 }
131 }
132 else {
132 else {
133 // 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
134 // use those to disable tab completion.
134 // use those to disable tab completion.
135 if (this.is_completing && event.keyCode !== 9) {
135 if (this.is_completing && event.keyCode !== 9) {
136 var ed_cur = editor.getCursor();
136 var ed_cur = editor.getCursor();
137 var cc_cur = this.completion_cursor;
137 var cc_cur = this.completion_cursor;
138 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) {
139 this.is_completing = false;
139 this.is_completing = false;
140 this.completion_cursor = null;
140 this.completion_cursor = null;
141 };
141 };
142 };
142 };
143 return false;
143 return false;
144 };
144 };
145 };
145 };
146
146
147 CodeCell.prototype.remove_and_cancell_tooltip = function(timeout)
147 CodeCell.prototype.remove_and_cancell_tooltip = function(timeout)
148 {
148 {
149 // note that we don't handle closing directly inside the calltip
149 // note that we don't handle closing directly inside the calltip
150 // 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
151 // get the event.
151 // get the event.
152 clearTimeout(timeout);
152 clearTimeout(timeout);
153 $('#tooltip').remove();
153 $('#tooltip').remove();
154 }
154 }
155
155
156 CodeCell.prototype.finish_tooltip = function (reply) {
156 CodeCell.prototype.finish_tooltip = function (reply) {
157 defstring=reply.definition;
157 defstring=reply.definition;
158 docstring=reply.docstring;
158 docstring=reply.docstring;
159 if(docstring == null){docstring="<empty docstring>"};
159 if(docstring == null){docstring="<empty docstring>"};
160 name=reply.name;
160 name=reply.name;
161
161
162 var that = this;
162 var that = this;
163 var tooltip = $('<div/>').attr('id', 'tooltip').addClass('tooltip');
163 var tooltip = $('<div/>').attr('id', 'tooltip').addClass('tooltip');
164 // remove to have the tooltip not Limited in X and Y
164 // remove to have the tooltip not Limited in X and Y
165 tooltip.addClass('smalltooltip');
165 tooltip.addClass('smalltooltip');
166 var pre=$('<pre/>').html(utils.fixConsole(docstring));
166 var pre=$('<pre/>').html(utils.fixConsole(docstring));
167 var expandlink=$('<a/>').attr('href',"#");
167 var expandlink=$('<a/>').attr('href',"#");
168 expandlink.addClass("ui-corner-all"); //rounded corner
168 expandlink.addClass("ui-corner-all"); //rounded corner
169 expandlink.attr('role',"button");
169 expandlink.attr('role',"button");
170 //expandlink.addClass('ui-button');
170 //expandlink.addClass('ui-button');
171 //expandlink.addClass('ui-state-default');
171 //expandlink.addClass('ui-state-default');
172 var expandspan=$('<span/>').text('Expand');
172 var expandspan=$('<span/>').text('Expand');
173 expandspan.addClass('ui-icon');
173 expandspan.addClass('ui-icon');
174 expandspan.addClass('ui-icon-plus');
174 expandspan.addClass('ui-icon-plus');
175 expandlink.append(expandspan);
175 expandlink.append(expandspan);
176 expandlink.attr('id','expanbutton');
176 expandlink.attr('id','expanbutton');
177 expandlink.click(function(){
177 expandlink.click(function(){
178 tooltip.removeClass('smalltooltip');
178 tooltip.removeClass('smalltooltip');
179 tooltip.addClass('bigtooltip');
179 tooltip.addClass('bigtooltip');
180 $('#expanbutton').remove();
180 $('#expanbutton').remove();
181 setTimeout(function(){that.code_mirror.focus();}, 50);
181 setTimeout(function(){that.code_mirror.focus();}, 50);
182 });
182 });
183 var morelink=$('<a/>').attr('href',"#");
183 var morelink=$('<a/>').attr('href',"#");
184 morelink.attr('role',"button");
184 morelink.attr('role',"button");
185 morelink.addClass('ui-button');
185 morelink.addClass('ui-button');
186 //morelink.addClass("ui-corner-all"); //rounded corner
186 //morelink.addClass("ui-corner-all"); //rounded corner
187 //morelink.addClass('ui-state-default');
187 //morelink.addClass('ui-state-default');
188 var morespan=$('<span/>').text('Open in Pager');
188 var morespan=$('<span/>').text('Open in Pager');
189 morespan.addClass('ui-icon');
189 morespan.addClass('ui-icon');
190 morespan.addClass('ui-icon-arrowstop-l-n');
190 morespan.addClass('ui-icon-arrowstop-l-n');
191 morelink.append(morespan);
191 morelink.append(morespan);
192 morelink.click(function(){
192 morelink.click(function(){
193 var msg_id = IPython.notebook.kernel.execute(name+"?");
193 var msg_id = IPython.notebook.kernel.execute(name+"?");
194 IPython.notebook.msg_cell_map[msg_id] = IPython.notebook.selected_cell().cell_id;
194 IPython.notebook.msg_cell_map[msg_id] = IPython.notebook.selected_cell().cell_id;
195 CodeCell.prototype.remove_and_cancell_tooltip(that.tooltip_timeout);
195 CodeCell.prototype.remove_and_cancell_tooltip(that.tooltip_timeout);
196 setTimeout(function(){that.code_mirror.focus();}, 50);
196 setTimeout(function(){that.code_mirror.focus();}, 50);
197 });
197 });
198
198
199 var closelink=$('<a/>').attr('href',"#");
199 var closelink=$('<a/>').attr('href',"#");
200 closelink.attr('role',"button");
200 closelink.attr('role',"button");
201 closelink.addClass('ui-button');
201 closelink.addClass('ui-button');
202 //closelink.addClass("ui-corner-all"); //rounded corner
202 //closelink.addClass("ui-corner-all"); //rounded corner
203 //closelink.adClass('ui-state-default'); // grey background and blue cross
203 //closelink.adClass('ui-state-default'); // grey background and blue cross
204 var closespan=$('<span/>').text('Close');
204 var closespan=$('<span/>').text('Close');
205 closespan.addClass('ui-icon');
205 closespan.addClass('ui-icon');
206 closespan.addClass('ui-icon-close');
206 closespan.addClass('ui-icon-close');
207 closelink.append(closespan);
207 closelink.append(closespan);
208 closelink.click(function(){
208 closelink.click(function(){
209 CodeCell.prototype.remove_and_cancell_tooltip(that.tooltip_timeout);
209 CodeCell.prototype.remove_and_cancell_tooltip(that.tooltip_timeout);
210 setTimeout(function(){that.code_mirror.focus();}, 50);
210 setTimeout(function(){that.code_mirror.focus();}, 50);
211 });
211 });
212 //construct the tooltip
212 //construct the tooltip
213 tooltip.append(closelink);
213 tooltip.append(closelink);
214 tooltip.append(expandlink);
214 tooltip.append(expandlink);
215 tooltip.append(morelink);
215 tooltip.append(morelink);
216 if(defstring){
216 if(defstring){
217 defstring_html= $('<pre/>').html(utils.fixConsole(defstring));
217 defstring_html= $('<pre/>').html(utils.fixConsole(defstring));
218 tooltip.append(defstring_html);
218 tooltip.append(defstring_html);
219 }
219 }
220 tooltip.append(pre);
220 tooltip.append(pre);
221 var pos = this.code_mirror.cursorCoords();
221 var pos = this.code_mirror.cursorCoords();
222 tooltip.css('left',pos.x+'px');
222 tooltip.css('left',pos.x+'px');
223 tooltip.css('top',pos.yBot+'px');
223 tooltip.css('top',pos.yBot+'px');
224 $('body').append(tooltip);
224 $('body').append(tooltip);
225
225
226 // issues with cross-closing if multiple tooltip in less than 5sec
226 // issues with cross-closing if multiple tooltip in less than 5sec
227 // keep it comented for now
227 // keep it comented for now
228 // setTimeout(CodeCell.prototype.remove_and_cancell_tooltip, 5000);
228 // setTimeout(CodeCell.prototype.remove_and_cancell_tooltip, 5000);
229 };
229 };
230
230
231
231
232 CodeCell.prototype.finish_completing = function (matched_text, matches) {
232 CodeCell.prototype.finish_completing = function (matched_text, matches) {
233 // console.log("Got matches", matched_text, matches);
233 // console.log("Got matches", matched_text, matches);
234 var newm = new Array();
234 var newm = new Array();
235 if(this.notebook.smart_completer)
235 if(this.notebook.smart_completer)
236 {
236 {
237 kwargs = new Array();
237 kwargs = new Array();
238 other = new Array();
238 other = new Array();
239 for(var i=0;i<matches.length; ++i){
239 for(var i=0;i<matches.length; ++i){
240 if(matches[i].substr(-1) === '='){
240 if(matches[i].substr(-1) === '='){
241 kwargs.push(matches[i]);
241 kwargs.push(matches[i]);
242 }else{other.push(matches[i]);}
242 }else{other.push(matches[i]);}
243 }
243 }
244 newm = kwargs.concat(other);
244 newm = kwargs.concat(other);
245 matches=newm;
245 matches=newm;
246 }
246 }
247 if (!this.is_completing || matches.length === 0) {return;}
247 if (!this.is_completing || matches.length === 0) {return;}
248
248
249 //try to check if the user is typing tab at least twice after a word
249 //try to check if the user is typing tab at least twice after a word
250 // and completion is "done"
250 // and completion is "done"
251 fallback_on_tooltip_after=2
251 fallback_on_tooltip_after=2
252 if(matches.length==1 && matched_text === matches[0])
252 if(matches.length==1 && matched_text === matches[0])
253 {
253 {
254 if(this.npressed >fallback_on_tooltip_after && this.prevmatch==matched_text)
254 if(this.npressed >fallback_on_tooltip_after && this.prevmatch==matched_text)
255 {
255 {
256 console.log('Ok, you really want to complete after pressing tab '+this.npressed+' times !');
256 console.log('Ok, you really want to complete after pressing tab '+this.npressed+' times !');
257 console.log('You should understand that there is no (more) completion for that !');
257 console.log('You should understand that there is no (more) completion for that !');
258 console.log("I'll show you the tooltip, will you stop bothering me ?");
258 console.log("I'll show you the tooltip, will you stop bothering me ?");
259 this.request_tooltip_after_time(matched_text+'(',0,this);
259 this.request_tooltip_after_time(matched_text+'(',0,this);
260 return;
260 return;
261 }
261 }
262 this.prevmatch=matched_text
262 this.prevmatch=matched_text
263 this.npressed=this.npressed+1;
263 this.npressed=this.npressed+1;
264 }
264 }
265 else
265 else
266 {
266 {
267 this.prevmatch="";
267 this.prevmatch="";
268 this.npressed=0;
268 this.npressed=0;
269 }
269 }
270
270
271 var that = this;
271 var that = this;
272 var cur = this.completion_cursor;
272 var cur = this.completion_cursor;
273
273
274 var insert = function (selected_text) {
274 var insert = function (selected_text) {
275 that.code_mirror.replaceRange(
275 that.code_mirror.replaceRange(
276 selected_text,
276 selected_text,
277 {line: cur.line, ch: (cur.ch-matched_text.length)},
277 {line: cur.line, ch: (cur.ch-matched_text.length)},
278 {line: cur.line, ch: cur.ch}
278 {line: cur.line, ch: cur.ch}
279 );
279 );
280 };
280 };
281
281
282 if (matches.length === 1) {
282 if (matches.length === 1) {
283 insert(matches[0]);
283 insert(matches[0]);
284 setTimeout(function(){that.code_mirror.focus();}, 50);
284 setTimeout(function(){that.code_mirror.focus();}, 50);
285 return;
285 return;
286 };
286 };
287
287
288 var complete = $('<div/>').addClass('completions');
288 var complete = $('<div/>').addClass('completions');
289 var select = $('<select/>').attr('multiple','true');
289 var select = $('<select/>').attr('multiple','true');
290 for (var i=0; i<matches.length; ++i) {
290 for (var i=0; i<matches.length; ++i) {
291 select.append($('<option/>').text(matches[i]));
291 select.append($('<option/>').text(matches[i]));
292 }
292 }
293 select.children().first().attr('selected','true');
293 select.children().first().attr('selected','true');
294 select.attr('size',Math.min(10,matches.length));
294 select.attr('size',Math.min(10,matches.length));
295 var pos = this.code_mirror.cursorCoords();
295 var pos = this.code_mirror.cursorCoords();
296 complete.css('left',pos.x+'px');
296 complete.css('left',pos.x+'px');
297 complete.css('top',pos.yBot+'px');
297 complete.css('top',pos.yBot+'px');
298 complete.append(select);
298 complete.append(select);
299
299
300 $('body').append(complete);
300 $('body').append(complete);
301 var done = false;
301 var done = false;
302
302
303 var close = function () {
303 var close = function () {
304 if (done) return;
304 if (done) return;
305 done = true;
305 done = true;
306 complete.remove();
306 complete.remove();
307 that.is_completing = false;
307 that.is_completing = false;
308 that.completion_cursor = null;
308 that.completion_cursor = null;
309 };
309 };
310
310
311 var pick = function () {
311 var pick = function () {
312 insert(select.val()[0]);
312 insert(select.val()[0]);
313 close();
313 close();
314 setTimeout(function(){that.code_mirror.focus();}, 50);
314 setTimeout(function(){that.code_mirror.focus();}, 50);
315 };
315 };
316
316
317 select.blur(close);
317 select.blur(close);
318 select.keydown(function (event) {
318 select.keydown(function (event) {
319 var code = event.which;
319 var code = event.which;
320 if (code === 13 || code === 32) {
320 if (code === 13 || code === 32) {
321 // Pressing SPACE or ENTER will cause a pick
321 // Pressing SPACE or ENTER will cause a pick
322 event.stopPropagation();
322 event.stopPropagation();
323 event.preventDefault();
323 event.preventDefault();
324 pick();
324 pick();
325 } else if (code === 38 || code === 40) {
325 } else if (code === 38 || code === 40) {
326 // We don't want the document keydown handler to handle UP/DOWN,
326 // We don't want the document keydown handler to handle UP/DOWN,
327 // but we want the default action.
327 // but we want the default action.
328 event.stopPropagation();
328 event.stopPropagation();
329 } else {
329 } else {
330 // All other key presses exit completion.
330 // All other key presses exit completion.
331 event.stopPropagation();
331 event.stopPropagation();
332 event.preventDefault();
332 event.preventDefault();
333 close();
333 close();
334 that.code_mirror.focus();
334 that.code_mirror.focus();
335 }
335 }
336 });
336 });
337 // Double click also causes a pick.
337 // Double click also causes a pick.
338 select.dblclick(pick);
338 select.dblclick(pick);
339 select.focus();
339 select.focus();
340 };
340 };
341
341
342 CodeCell.prototype.toggle_line_numbers = function () {
342 CodeCell.prototype.toggle_line_numbers = function () {
343 if (this.code_mirror.getOption('lineNumbers') == false) {
343 if (this.code_mirror.getOption('lineNumbers') == false) {
344 this.code_mirror.setOption('lineNumbers', true);
344 this.code_mirror.setOption('lineNumbers', true);
345 } else {
345 } else {
346 this.code_mirror.setOption('lineNumbers', false);
346 this.code_mirror.setOption('lineNumbers', false);
347 }
347 }
348 this.code_mirror.refresh()
348 this.code_mirror.refresh()
349 };
349 };
350
350
351 CodeCell.prototype.select = function () {
351 CodeCell.prototype.select = function () {
352 IPython.Cell.prototype.select.apply(this);
352 IPython.Cell.prototype.select.apply(this);
353 // Todo: this dance is needed because as of CodeMirror 2.12, focus is
353 // Todo: this dance is needed because as of CodeMirror 2.12, focus is
354 // not causing the cursor to blink if the editor is empty initially.
354 // not causing the cursor to blink if the editor is empty initially.
355 // While this seems to fix the issue, this should be fixed
355 // While this seems to fix the issue, this should be fixed
356 // in CodeMirror proper.
356 // in CodeMirror proper.
357 var s = this.code_mirror.getValue();
357 var s = this.code_mirror.getValue();
358 this.code_mirror.focus();
358 this.code_mirror.focus();
359 if (s === '') this.code_mirror.setValue('');
359 if (s === '') this.code_mirror.setValue('');
360 };
360 };
361
361
362
362
363 CodeCell.prototype.select_all = function () {
363 CodeCell.prototype.select_all = function () {
364 var start = {line: 0, ch: 0};
364 var start = {line: 0, ch: 0};
365 var nlines = this.code_mirror.lineCount();
365 var nlines = this.code_mirror.lineCount();
366 var last_line = this.code_mirror.getLine(nlines-1);
366 var last_line = this.code_mirror.getLine(nlines-1);
367 var end = {line: nlines-1, ch: last_line.length};
367 var end = {line: nlines-1, ch: last_line.length};
368 this.code_mirror.setSelection(start, end);
368 this.code_mirror.setSelection(start, end);
369 };
369 };
370
370
371
371
372 CodeCell.prototype.append_output = function (json) {
372 CodeCell.prototype.append_output = function (json) {
373 this.expand();
373 this.expand();
374 if (json.output_type === 'pyout') {
374 if (json.output_type === 'pyout') {
375 this.append_pyout(json);
375 this.append_pyout(json);
376 } else if (json.output_type === 'pyerr') {
376 } else if (json.output_type === 'pyerr') {
377 this.append_pyerr(json);
377 this.append_pyerr(json);
378 } else if (json.output_type === 'display_data') {
378 } else if (json.output_type === 'display_data') {
379 this.append_display_data(json);
379 this.append_display_data(json);
380 } else if (json.output_type === 'stream') {
380 } else if (json.output_type === 'stream') {
381 this.append_stream(json);
381 this.append_stream(json);
382 };
382 };
383 this.outputs.push(json);
383 this.outputs.push(json);
384 };
384 };
385
385
386
386
387 CodeCell.prototype.create_output_area = function () {
387 CodeCell.prototype.create_output_area = function () {
388 var oa = $("<div/>").addClass("hbox output_area");
388 var oa = $("<div/>").addClass("hbox output_area");
389 oa.append($('<div/>').addClass('prompt'));
389 oa.append($('<div/>').addClass('prompt'));
390 return oa;
390 return oa;
391 };
391 };
392
392
393
393
394 CodeCell.prototype.append_pyout = function (json) {
394 CodeCell.prototype.append_pyout = function (json) {
395 n = json.prompt_number || ' ';
395 n = json.prompt_number || ' ';
396 var toinsert = this.create_output_area();
396 var toinsert = this.create_output_area();
397 toinsert.find('div.prompt').addClass('output_prompt').html('Out[' + n + ']:');
397 toinsert.find('div.prompt').addClass('output_prompt').html('Out[' + n + ']:');
398 this.append_mime_type(json, toinsert);
398 this.append_mime_type(json, toinsert);
399 this.element.find('div.output').append(toinsert);
399 this.element.find('div.output').append(toinsert);
400 // If we just output latex, typeset it.
400 // If we just output latex, typeset it.
401 if ((json.latex !== undefined) || (json.html !== undefined)) {
401 if ((json.latex !== undefined) || (json.html !== undefined)) {
402 MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
402 MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
403 };
403 };
404 };
404 };
405
405
406
406
407 CodeCell.prototype.append_pyerr = function (json) {
407 CodeCell.prototype.append_pyerr = function (json) {
408 var tb = json.traceback;
408 var tb = json.traceback;
409 if (tb !== undefined && tb.length > 0) {
409 if (tb !== undefined && tb.length > 0) {
410 var s = '';
410 var s = '';
411 var len = tb.length;
411 var len = tb.length;
412 for (var i=0; i<len; i++) {
412 for (var i=0; i<len; i++) {
413 s = s + tb[i] + '\n';
413 s = s + tb[i] + '\n';
414 }
414 }
415 s = s + '\n';
415 s = s + '\n';
416 var toinsert = this.create_output_area();
416 var toinsert = this.create_output_area();
417 this.append_text(s, toinsert);
417 this.append_text(s, toinsert);
418 this.element.find('div.output').append(toinsert);
418 this.element.find('div.output').append(toinsert);
419 };
419 };
420 };
420 };
421
421
422
422
423 CodeCell.prototype.append_stream = function (json) {
423 CodeCell.prototype.append_stream = function (json) {
424 // temporary fix: if stream undefined (json file written prior to this patch),
424 // temporary fix: if stream undefined (json file written prior to this patch),
425 // default to most likely stdout:
425 // default to most likely stdout:
426 if (json.stream == undefined){
426 if (json.stream == undefined){
427 json.stream = 'stdout';
427 json.stream = 'stdout';
428 }
428 }
429 var subclass = "output_"+json.stream;
429 var subclass = "output_"+json.stream;
430 if (this.outputs.length > 0){
430 if (this.outputs.length > 0){
431 // have at least one output to consider
431 // have at least one output to consider
432 var last = this.outputs[this.outputs.length-1];
432 var last = this.outputs[this.outputs.length-1];
433 if (last.output_type == 'stream' && json.stream == last.stream){
433 if (last.output_type == 'stream' && json.stream == last.stream){
434 // latest output was in the same stream,
434 // latest output was in the same stream,
435 // so append directly into its pre tag
435 // so append directly into its pre tag
436 this.element.find('div.'+subclass).last().find('pre').append(json.text);
436 this.element.find('div.'+subclass).last().find('pre').append(json.text);
437 return;
437 return;
438 }
438 }
439 }
439 }
440
440
441 // If we got here, attach a new div
441 // If we got here, attach a new div
442 var toinsert = this.create_output_area();
442 var toinsert = this.create_output_area();
443 this.append_text(json.text, toinsert, "output_stream "+subclass);
443 this.append_text(json.text, toinsert, "output_stream "+subclass);
444 this.element.find('div.output').append(toinsert);
444 this.element.find('div.output').append(toinsert);
445 };
445 };
446
446
447
447
448 CodeCell.prototype.append_display_data = function (json) {
448 CodeCell.prototype.append_display_data = function (json) {
449 var toinsert = this.create_output_area();
449 var toinsert = this.create_output_area();
450 this.append_mime_type(json, toinsert)
450 this.append_mime_type(json, toinsert)
451 this.element.find('div.output').append(toinsert);
451 this.element.find('div.output').append(toinsert);
452 // If we just output latex, typeset it.
452 // If we just output latex, typeset it.
453 if ( (json.latex !== undefined) || (json.html !== undefined) ) {
453 if ( (json.latex !== undefined) || (json.html !== undefined) ) {
454 MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
454 MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
455 };
455 };
456 };
456 };
457
457
458
458
459 CodeCell.prototype.append_mime_type = function (json, element) {
459 CodeCell.prototype.append_mime_type = function (json, element) {
460 if (json.html !== undefined) {
460 if (json.html !== undefined) {
461 this.append_html(json.html, element);
461 this.append_html(json.html, element);
462 } else if (json.latex !== undefined) {
462 } else if (json.latex !== undefined) {
463 this.append_latex(json.latex, element);
463 this.append_latex(json.latex, element);
464 } else if (json.svg !== undefined) {
464 } else if (json.svg !== undefined) {
465 this.append_svg(json.svg, element);
465 this.append_svg(json.svg, element);
466 } else if (json.png !== undefined) {
466 } else if (json.png !== undefined) {
467 this.append_png(json.png, element);
467 this.append_png(json.png, element);
468 } else if (json.jpeg !== undefined) {
468 } else if (json.jpeg !== undefined) {
469 this.append_jpeg(json.jpeg, element);
469 this.append_jpeg(json.jpeg, element);
470 } else if (json.text !== undefined) {
470 } else if (json.text !== undefined) {
471 this.append_text(json.text, element);
471 this.append_text(json.text, element);
472 };
472 };
473 };
473 };
474
474
475
475
476 CodeCell.prototype.append_html = function (html, element) {
476 CodeCell.prototype.append_html = function (html, element) {
477 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_html rendered_html");
477 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_html rendered_html");
478 toinsert.append(html);
478 toinsert.append(html);
479 element.append(toinsert);
479 element.append(toinsert);
480 }
480 }
481
481
482
482
483 CodeCell.prototype.append_text = function (data, element, extra_class) {
483 CodeCell.prototype.append_text = function (data, element, extra_class) {
484 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_text");
484 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_text");
485 if (extra_class){
485 if (extra_class){
486 toinsert.addClass(extra_class);
486 toinsert.addClass(extra_class);
487 }
487 }
488 toinsert.append($("<pre/>").html(data));
488 toinsert.append($("<pre/>").html(data));
489 element.append(toinsert);
489 element.append(toinsert);
490 };
490 };
491
491
492
492
493 CodeCell.prototype.append_svg = function (svg, element) {
493 CodeCell.prototype.append_svg = function (svg, element) {
494 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_svg");
494 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_svg");
495 toinsert.append(svg);
495 toinsert.append(svg);
496 element.append(toinsert);
496 element.append(toinsert);
497 };
497 };
498
498
499
499
500 CodeCell.prototype.append_png = function (png, element) {
500 CodeCell.prototype.append_png = function (png, element) {
501 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_png");
501 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_png");
502 toinsert.append($("<img/>").attr('src','data:image/png;base64,'+png));
502 toinsert.append($("<img/>").attr('src','data:image/png;base64,'+png));
503 element.append(toinsert);
503 element.append(toinsert);
504 };
504 };
505
505
506
506
507 CodeCell.prototype.append_jpeg = function (jpeg, element) {
507 CodeCell.prototype.append_jpeg = function (jpeg, element) {
508 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_jpeg");
508 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_jpeg");
509 toinsert.append($("<img/>").attr('src','data:image/jpeg;base64,'+jpeg));
509 toinsert.append($("<img/>").attr('src','data:image/jpeg;base64,'+jpeg));
510 element.append(toinsert);
510 element.append(toinsert);
511 };
511 };
512
512
513
513
514 CodeCell.prototype.append_latex = function (latex, element) {
514 CodeCell.prototype.append_latex = function (latex, element) {
515 // This method cannot do the typesetting because the latex first has to
515 // This method cannot do the typesetting because the latex first has to
516 // be on the page.
516 // be on the page.
517 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_latex");
517 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_latex");
518 toinsert.append(latex);
518 toinsert.append(latex);
519 element.append(toinsert);
519 element.append(toinsert);
520 }
520 }
521
521
522
522
523 CodeCell.prototype.clear_output = function (stdout, stderr, other) {
523 CodeCell.prototype.clear_output = function (stdout, stderr, other) {
524 var output_div = this.element.find("div.output");
524 var output_div = this.element.find("div.output");
525 if (stdout && stderr && other){
525 if (stdout && stderr && other){
526 // clear all, no need for logic
526 // clear all, no need for logic
527 output_div.html("");
527 output_div.html("");
528 this.outputs = [];
528 this.outputs = [];
529 return;
529 return;
530 }
530 }
531 // remove html output
531 // remove html output
532 // each output_subarea that has an identifying class is in an output_area
532 // each output_subarea that has an identifying class is in an output_area
533 // which is the element to be removed.
533 // which is the element to be removed.
534 if (stdout){
534 if (stdout){
535 output_div.find("div.output_stdout").parent().remove();
535 output_div.find("div.output_stdout").parent().remove();
536 }
536 }
537 if (stderr){
537 if (stderr){
538 output_div.find("div.output_stderr").parent().remove();
538 output_div.find("div.output_stderr").parent().remove();
539 }
539 }
540 if (other){
540 if (other){
541 output_div.find("div.output_subarea").not("div.output_stderr").not("div.output_stdout").parent().remove();
541 output_div.find("div.output_subarea").not("div.output_stderr").not("div.output_stdout").parent().remove();
542 }
542 }
543
543
544 // remove cleared outputs from JSON list:
544 // remove cleared outputs from JSON list:
545 for (var i = this.outputs.length - 1; i >= 0; i--){
545 for (var i = this.outputs.length - 1; i >= 0; i--){
546 var out = this.outputs[i];
546 var out = this.outputs[i];
547 var output_type = out.output_type;
547 var output_type = out.output_type;
548 if (output_type == "display_data" && other){
548 if (output_type == "display_data" && other){
549 this.outputs.splice(i,1);
549 this.outputs.splice(i,1);
550 }else if (output_type == "stream"){
550 }else if (output_type == "stream"){
551 if (stdout && out.stream == "stdout"){
551 if (stdout && out.stream == "stdout"){
552 this.outputs.splice(i,1);
552 this.outputs.splice(i,1);
553 }else if (stderr && out.stream == "stderr"){
553 }else if (stderr && out.stream == "stderr"){
554 this.outputs.splice(i,1);
554 this.outputs.splice(i,1);
555 }
555 }
556 }
556 }
557 }
557 }
558 };
558 };
559
559
560
560
561 CodeCell.prototype.clear_input = function () {
561 CodeCell.prototype.clear_input = function () {
562 this.code_mirror.setValue('');
562 this.code_mirror.setValue('');
563 };
563 };
564
564
565
565
566 CodeCell.prototype.collapse = function () {
566 CodeCell.prototype.collapse = function () {
567 if (!this.collapsed) {
567 if (!this.collapsed) {
568 this.element.find('div.output').hide();
568 this.element.find('div.output').hide();
569 this.collapsed = true;
569 this.collapsed = true;
570 };
570 };
571 };
571 };
572
572
573
573
574 CodeCell.prototype.expand = function () {
574 CodeCell.prototype.expand = function () {
575 if (this.collapsed) {
575 if (this.collapsed) {
576 this.element.find('div.output').show();
576 this.element.find('div.output').show();
577 this.collapsed = false;
577 this.collapsed = false;
578 };
578 };
579 };
579 };
580
580
581
581
582 CodeCell.prototype.toggle_output = function () {
582 CodeCell.prototype.toggle_output = function () {
583 if (this.collapsed) {
583 if (this.collapsed) {
584 this.expand();
584 this.expand();
585 } else {
585 } else {
586 this.collapse();
586 this.collapse();
587 };
587 };
588 };
588 };
589
589
590 CodeCell.prototype.set_input_prompt = function (number) {
590 CodeCell.prototype.set_input_prompt = function (number) {
591 var n = number || ' ';
591 var n = number || '&nbsp;';
592 this.input_prompt_number = n
592 this.input_prompt_number = n
593 this.element.find('div.input_prompt').html('In&nbsp;[' + n + ']:');
593 this.element.find('div.input_prompt').html('In&nbsp;[' + n + ']:');
594 };
594 };
595
595
596
596
597 CodeCell.prototype.get_code = function () {
597 CodeCell.prototype.get_code = function () {
598 return this.code_mirror.getValue();
598 return this.code_mirror.getValue();
599 };
599 };
600
600
601
601
602 CodeCell.prototype.set_code = function (code) {
602 CodeCell.prototype.set_code = function (code) {
603 return this.code_mirror.setValue(code);
603 return this.code_mirror.setValue(code);
604 };
604 };
605
605
606
606
607 CodeCell.prototype.at_top = function () {
607 CodeCell.prototype.at_top = function () {
608 var cursor = this.code_mirror.getCursor();
608 var cursor = this.code_mirror.getCursor();
609 if (cursor.line === 0) {
609 if (cursor.line === 0) {
610 return true;
610 return true;
611 } else {
611 } else {
612 return false;
612 return false;
613 }
613 }
614 };
614 };
615
615
616
616
617 CodeCell.prototype.at_bottom = function () {
617 CodeCell.prototype.at_bottom = function () {
618 var cursor = this.code_mirror.getCursor();
618 var cursor = this.code_mirror.getCursor();
619 if (cursor.line === (this.code_mirror.lineCount()-1)) {
619 if (cursor.line === (this.code_mirror.lineCount()-1)) {
620 return true;
620 return true;
621 } else {
621 } else {
622 return false;
622 return false;
623 }
623 }
624 };
624 };
625
625
626
626
627 CodeCell.prototype.fromJSON = function (data) {
627 CodeCell.prototype.fromJSON = function (data) {
628 console.log('Import from JSON:', data);
628 console.log('Import from JSON:', data);
629 if (data.cell_type === 'code') {
629 if (data.cell_type === 'code') {
630 if (data.input !== undefined) {
630 if (data.input !== undefined) {
631 this.set_code(data.input);
631 this.set_code(data.input);
632 }
632 }
633 if (data.prompt_number !== undefined) {
633 if (data.prompt_number !== undefined) {
634 this.set_input_prompt(data.prompt_number);
634 this.set_input_prompt(data.prompt_number);
635 } else {
635 } else {
636 this.set_input_prompt();
636 this.set_input_prompt();
637 };
637 };
638 var len = data.outputs.length;
638 var len = data.outputs.length;
639 for (var i=0; i<len; i++) {
639 for (var i=0; i<len; i++) {
640 this.append_output(data.outputs[i]);
640 this.append_output(data.outputs[i]);
641 };
641 };
642 if (data.collapsed !== undefined) {
642 if (data.collapsed !== undefined) {
643 if (data.collapsed) {
643 if (data.collapsed) {
644 this.collapse();
644 this.collapse();
645 };
645 };
646 };
646 };
647 };
647 };
648 };
648 };
649
649
650
650
651 CodeCell.prototype.toJSON = function () {
651 CodeCell.prototype.toJSON = function () {
652 var data = {};
652 var data = {};
653 data.input = this.get_code();
653 data.input = this.get_code();
654 data.cell_type = 'code';
654 data.cell_type = 'code';
655 if (this.input_prompt_number !== ' ') {
655 if (this.input_prompt_number !== ' ') {
656 data.prompt_number = this.input_prompt_number
656 data.prompt_number = this.input_prompt_number
657 };
657 };
658 var outputs = [];
658 var outputs = [];
659 var len = this.outputs.length;
659 var len = this.outputs.length;
660 for (var i=0; i<len; i++) {
660 for (var i=0; i<len; i++) {
661 outputs[i] = this.outputs[i];
661 outputs[i] = this.outputs[i];
662 };
662 };
663 data.outputs = outputs;
663 data.outputs = outputs;
664 data.language = 'python';
664 data.language = 'python';
665 data.collapsed = this.collapsed;
665 data.collapsed = this.collapsed;
666 // console.log('Export to JSON:',data);
666 // console.log('Export to JSON:',data);
667 return data;
667 return data;
668 };
668 };
669
669
670
670
671 IPython.CodeCell = CodeCell;
671 IPython.CodeCell = CodeCell;
672
672
673 return IPython;
673 return IPython;
674 }(IPython));
674 }(IPython));
General Comments 0
You need to be logged in to leave comments. Login now