##// END OF EJS Templates
as you type Completer, propagate event by hand...
Matthias BUSSONNIER -
Show More
@@ -1,785 +1,795 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' ){
76 if(event.type === 'keydown' ){
77 CodeCell.prototype.remove_and_cancel_tooltip(that.tooltip_timeout);
77 CodeCell.prototype.remove_and_cancel_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 return false;
145 return false;
146 };
146 };
147
147
148 CodeCell.prototype.remove_and_cancel_tooltip = function(timeout)
148 CodeCell.prototype.remove_and_cancel_tooltip = function(timeout)
149 {
149 {
150 // note that we don't handle closing directly inside the calltip
150 // note that we don't handle closing directly inside the calltip
151 // as in the completer, because it is not focusable, so won't
151 // as in the completer, because it is not focusable, so won't
152 // get the event.
152 // get the event.
153 if(timeout != null)
153 if(timeout != null)
154 { clearTimeout(timeout);}
154 { clearTimeout(timeout);}
155 $('#tooltip').remove();
155 $('#tooltip').remove();
156 }
156 }
157
157
158 CodeCell.prototype.finish_tooltip = function (reply) {
158 CodeCell.prototype.finish_tooltip = function (reply) {
159 defstring=reply.definition;
159 defstring=reply.definition;
160 docstring=reply.docstring;
160 docstring=reply.docstring;
161 if(docstring == null){docstring="<empty docstring>"};
161 if(docstring == null){docstring="<empty docstring>"};
162 name=reply.name;
162 name=reply.name;
163
163
164 var that = this;
164 var that = this;
165 var tooltip = $('<div/>').attr('id', 'tooltip').addClass('tooltip');
165 var tooltip = $('<div/>').attr('id', 'tooltip').addClass('tooltip');
166 // remove to have the tooltip not Limited in X and Y
166 // remove to have the tooltip not Limited in X and Y
167 tooltip.addClass('smalltooltip');
167 tooltip.addClass('smalltooltip');
168 var pre=$('<pre/>').html(utils.fixConsole(docstring));
168 var pre=$('<pre/>').html(utils.fixConsole(docstring));
169 var expandlink=$('<a/>').attr('href',"#");
169 var expandlink=$('<a/>').attr('href',"#");
170 expandlink.addClass("ui-corner-all"); //rounded corner
170 expandlink.addClass("ui-corner-all"); //rounded corner
171 expandlink.attr('role',"button");
171 expandlink.attr('role',"button");
172 //expandlink.addClass('ui-button');
172 //expandlink.addClass('ui-button');
173 //expandlink.addClass('ui-state-default');
173 //expandlink.addClass('ui-state-default');
174 var expandspan=$('<span/>').text('Expand');
174 var expandspan=$('<span/>').text('Expand');
175 expandspan.addClass('ui-icon');
175 expandspan.addClass('ui-icon');
176 expandspan.addClass('ui-icon-plus');
176 expandspan.addClass('ui-icon-plus');
177 expandlink.append(expandspan);
177 expandlink.append(expandspan);
178 expandlink.attr('id','expanbutton');
178 expandlink.attr('id','expanbutton');
179 expandlink.click(function(){
179 expandlink.click(function(){
180 tooltip.removeClass('smalltooltip');
180 tooltip.removeClass('smalltooltip');
181 tooltip.addClass('bigtooltip');
181 tooltip.addClass('bigtooltip');
182 $('#expanbutton').remove();
182 $('#expanbutton').remove();
183 setTimeout(function(){that.code_mirror.focus();}, 50);
183 setTimeout(function(){that.code_mirror.focus();}, 50);
184 });
184 });
185 var morelink=$('<a/>').attr('href',"#");
185 var morelink=$('<a/>').attr('href',"#");
186 morelink.attr('role',"button");
186 morelink.attr('role',"button");
187 morelink.addClass('ui-button');
187 morelink.addClass('ui-button');
188 //morelink.addClass("ui-corner-all"); //rounded corner
188 //morelink.addClass("ui-corner-all"); //rounded corner
189 //morelink.addClass('ui-state-default');
189 //morelink.addClass('ui-state-default');
190 var morespan=$('<span/>').text('Open in Pager');
190 var morespan=$('<span/>').text('Open in Pager');
191 morespan.addClass('ui-icon');
191 morespan.addClass('ui-icon');
192 morespan.addClass('ui-icon-arrowstop-l-n');
192 morespan.addClass('ui-icon-arrowstop-l-n');
193 morelink.append(morespan);
193 morelink.append(morespan);
194 morelink.click(function(){
194 morelink.click(function(){
195 var msg_id = IPython.notebook.kernel.execute(name+"?");
195 var msg_id = IPython.notebook.kernel.execute(name+"?");
196 IPython.notebook.msg_cell_map[msg_id] = IPython.notebook.selected_cell().cell_id;
196 IPython.notebook.msg_cell_map[msg_id] = IPython.notebook.selected_cell().cell_id;
197 CodeCell.prototype.remove_and_cancell_tooltip(that.tooltip_timeout);
197 CodeCell.prototype.remove_and_cancell_tooltip(that.tooltip_timeout);
198 setTimeout(function(){that.code_mirror.focus();}, 50);
198 setTimeout(function(){that.code_mirror.focus();}, 50);
199 });
199 });
200
200
201 var closelink=$('<a/>').attr('href',"#");
201 var closelink=$('<a/>').attr('href',"#");
202 closelink.attr('role',"button");
202 closelink.attr('role',"button");
203 closelink.addClass('ui-button');
203 closelink.addClass('ui-button');
204 //closelink.addClass("ui-corner-all"); //rounded corner
204 //closelink.addClass("ui-corner-all"); //rounded corner
205 //closelink.adClass('ui-state-default'); // grey background and blue cross
205 //closelink.adClass('ui-state-default'); // grey background and blue cross
206 var closespan=$('<span/>').text('Close');
206 var closespan=$('<span/>').text('Close');
207 closespan.addClass('ui-icon');
207 closespan.addClass('ui-icon');
208 closespan.addClass('ui-icon-close');
208 closespan.addClass('ui-icon-close');
209 closelink.append(closespan);
209 closelink.append(closespan);
210 closelink.click(function(){
210 closelink.click(function(){
211 CodeCell.prototype.remove_and_cancell_tooltip(that.tooltip_timeout);
211 CodeCell.prototype.remove_and_cancell_tooltip(that.tooltip_timeout);
212 setTimeout(function(){that.code_mirror.focus();}, 50);
212 setTimeout(function(){that.code_mirror.focus();}, 50);
213 });
213 });
214 //construct the tooltip
214 //construct the tooltip
215 tooltip.append(closelink);
215 tooltip.append(closelink);
216 tooltip.append(expandlink);
216 tooltip.append(expandlink);
217 tooltip.append(morelink);
217 tooltip.append(morelink);
218 if(defstring){
218 if(defstring){
219 defstring_html= $('<pre/>').html(utils.fixConsole(defstring));
219 defstring_html= $('<pre/>').html(utils.fixConsole(defstring));
220 tooltip.append(defstring_html);
220 tooltip.append(defstring_html);
221 }
221 }
222 tooltip.append(pre);
222 tooltip.append(pre);
223 var pos = this.code_mirror.cursorCoords();
223 var pos = this.code_mirror.cursorCoords();
224 tooltip.css('left',pos.x+'px');
224 tooltip.css('left',pos.x+'px');
225 tooltip.css('top',pos.yBot+'px');
225 tooltip.css('top',pos.yBot+'px');
226 $('body').append(tooltip);
226 $('body').append(tooltip);
227
227
228 // issues with cross-closing if multiple tooltip in less than 5sec
228 // issues with cross-closing if multiple tooltip in less than 5sec
229 // keep it comented for now
229 // keep it comented for now
230 // setTimeout(CodeCell.prototype.remove_and_cancell_tooltip, 5000);
230 // setTimeout(CodeCell.prototype.remove_and_cancell_tooltip, 5000);
231 };
231 };
232
232
233 // As you type completer
233 // As you type completer
234 CodeCell.prototype.finish_completing = function (matched_text, matches) {
234 CodeCell.prototype.finish_completing = function (matched_text, matches) {
235 //return if not completing or nothing to complete
235 //return if not completing or nothing to complete
236 if (!this.is_completing || matches.length === 0) {return;}
236 if (!this.is_completing || matches.length === 0) {return;}
237
237
238 // for later readability
238 // for later readability
239 var key = { tab:9,
239 var key = { tab:9,
240 esc:27,
240 esc:27,
241 backspace:8,
241 backspace:8,
242 space:13,
242 space:13,
243 shift:16,
243 shift:16,
244 enter:32,
244 enter:32,
245 // _ is 189
245 // _ is 189
246 isCompSymbol : function (code)
246 isCompSymbol : function (code)
247 {return ((code>64 && code <=122)|| code == 189)}
247 {return ((code>64 && code <=122)|| code == 189)}
248 }
248 }
249
249
250 // smart completion, sort kwarg ending with '='
250 // smart completion, sort kwarg ending with '='
251 var newm = new Array();
251 var newm = new Array();
252 if(this.notebook.smart_completer)
252 if(this.notebook.smart_completer)
253 {
253 {
254 kwargs = new Array();
254 kwargs = new Array();
255 other = new Array();
255 other = new Array();
256 for(var i=0;i<matches.length; ++i){
256 for(var i=0;i<matches.length; ++i){
257 if(matches[i].substr(-1) === '='){
257 if(matches[i].substr(-1) === '='){
258 kwargs.push(matches[i]);
258 kwargs.push(matches[i]);
259 }else{other.push(matches[i]);}
259 }else{other.push(matches[i]);}
260 }
260 }
261 newm = kwargs.concat(other);
261 newm = kwargs.concat(other);
262 matches=newm;
262 matches=newm;
263 }
263 }
264 // end sort kwargs
264 // end sort kwargs
265
265
266 // give common prefix of a array of string
266 // give common prefix of a array of string
267 function sharedStart(A){
267 function sharedStart(A){
268 if(A.length > 1 ){
268 if(A.length > 1 ){
269 var tem1, tem2, s, A= A.slice(0).sort();
269 var tem1, tem2, s, A= A.slice(0).sort();
270 tem1= A[0];
270 tem1= A[0];
271 s= tem1.length;
271 s= tem1.length;
272 tem2= A.pop();
272 tem2= A.pop();
273 while(s && tem2.indexOf(tem1)== -1){
273 while(s && tem2.indexOf(tem1)== -1){
274 tem1= tem1.substring(0, --s);
274 tem1= tem1.substring(0, --s);
275 }
275 }
276 return tem1;
276 return tem1;
277 }
277 }
278 return "";
278 return "";
279 }
279 }
280
280
281
281
282 //try to check if the user is typing tab at least twice after a word
282 //try to check if the user is typing tab at least twice after a word
283 // and completion is "done"
283 // and completion is "done"
284 fallback_on_tooltip_after=2
284 fallback_on_tooltip_after=2
285 if(matches.length==1 && matched_text === matches[0])
285 if(matches.length==1 && matched_text === matches[0])
286 {
286 {
287 if(this.npressed >fallback_on_tooltip_after && this.prevmatch==matched_text)
287 if(this.npressed >fallback_on_tooltip_after && this.prevmatch==matched_text)
288 {
288 {
289 console.log('Ok, you really want to complete after pressing tab '+this.npressed+' times !');
289 console.log('Ok, you really want to complete after pressing tab '+this.npressed+' times !');
290 console.log('You should understand that there is no (more) completion for that !');
290 console.log('You should understand that there is no (more) completion for that !');
291 console.log("I'll show you the tooltip, will you stop bothering me ?");
291 console.log("I'll show you the tooltip, will you stop bothering me ?");
292 this.request_tooltip_after_time(matched_text+'(',0,this);
292 this.request_tooltip_after_time(matched_text+'(',0,this);
293 return;
293 return;
294 }
294 }
295 this.prevmatch=matched_text
295 this.prevmatch=matched_text
296 this.npressed=this.npressed+1;
296 this.npressed=this.npressed+1;
297 }
297 }
298 else
298 else
299 {
299 {
300 this.prevmatch="";
300 this.prevmatch="";
301 this.npressed=0;
301 this.npressed=0;
302 }
302 }
303 // end fallback on tooltip
303 // end fallback on tooltip
304 //==================================
304 //==================================
305 // Real completion logic start here
305 // Real completion logic start here
306 var that = this;
306 var that = this;
307 var cur = this.completion_cursor;
307 var cur = this.completion_cursor;
308 var done = false;
308 var done = false;
309
309
310 // call to dismmiss the completer
310 // call to dismmiss the completer
311 var close = function () {
311 var close = function () {
312 if (done) return;
312 if (done) return;
313 done = true;
313 done = true;
314 if (complete!=undefined)
314 if (complete!=undefined)
315 {complete.remove();}
315 {complete.remove();}
316 that.is_completing = false;
316 that.is_completing = false;
317 that.completion_cursor = null;
317 that.completion_cursor = null;
318 };
318 };
319
319
320 // insert the given text and exit the completer
320 // insert the given text and exit the completer
321 var insert = function (selected_text) {
321 var insert = function (selected_text, event) {
322 that.code_mirror.replaceRange(
322 that.code_mirror.replaceRange(
323 selected_text,
323 selected_text,
324 {line: cur.line, ch: (cur.ch-matched_text.length)},
324 {line: cur.line, ch: (cur.ch-matched_text.length)},
325 {line: cur.line, ch: cur.ch}
325 {line: cur.line, ch: cur.ch}
326 );
326 );
327 event.stopPropagation();
327 if(event != null){
328 event.preventDefault();
328 event.stopPropagation();
329 event.preventDefault();
330 }
329 close();
331 close();
330 setTimeout(function(){that.code_mirror.focus();}, 50);
332 setTimeout(function(){that.code_mirror.focus();}, 50);
331 };
333 };
332
334
333 // insert the curent highlited selection and exit
335 // insert the curent highlited selection and exit
334 var pick = function () {
336 var pick = function () {
335 insert(select.val()[0]);
337 insert(select.val()[0],null);
336 };
338 };
337
339
338
340
339 // Define function to clear the completer, refill it with the new
341 // Define function to clear the completer, refill it with the new
340 // matches, update the pseuso typing field. autopick insert match if
342 // matches, update the pseuso typing field. autopick insert match if
341 // only one left, in no matches (anymore) dismiss itself by pasting
343 // only one left, in no matches (anymore) dismiss itself by pasting
342 // what the user have typed until then
344 // what the user have typed until then
343 var complete_with = function(matches,typed_text,autopick)
345 var complete_with = function(matches,typed_text,autopick,event)
344 {
346 {
345 // If autopick an only one match, past.
347 // If autopick an only one match, past.
346 // Used to 'pick' when pressing tab
348 // Used to 'pick' when pressing tab
347 if (matches.length < 1) {
349 if (matches.length < 1) {
348 insert(typed_text);
350 insert(typed_text,event);
351 if(event !=null){
352 event.stopPropagation();
353 event.preventDefault();
354 }
349 } else if (autopick && matches.length==1) {
355 } else if (autopick && matches.length==1) {
350 insert(matches[0]);
356 insert(matches[0],event);
357 if(event !=null){
358 event.stopPropagation();
359 event.preventDefault();
360 }
351 }
361 }
352 //clear the previous completion if any
362 //clear the previous completion if any
353 complete.children().children().remove();
363 complete.children().children().remove();
354 $('#asyoutype').text(typed_text);
364 $('#asyoutype').text(typed_text);
355 select=$('#asyoutypeselect');
365 select=$('#asyoutypeselect');
356 for (var i=0; i<matches.length; ++i) {
366 for (var i=0; i<matches.length; ++i) {
357 select.append($('<option/>').html(matches[i]));
367 select.append($('<option/>').html(matches[i]));
358 }
368 }
359 select.children().first().attr('selected','true');
369 select.children().first().attr('selected','true');
360 }
370 }
361
371
362 // create html for completer
372 // create html for completer
363 var complete = $('<div/>').addClass('completions');
373 var complete = $('<div/>').addClass('completions');
364 complete.attr('id','complete');
374 complete.attr('id','complete');
365 complete.append($('<p/>').attr('id', 'asyoutype').html(matched_text));//pseudo input field
375 complete.append($('<p/>').attr('id', 'asyoutype').html(matched_text));//pseudo input field
366
376
367 var select = $('<select/>').attr('multiple','true');
377 var select = $('<select/>').attr('multiple','true');
368 select.attr('id', 'asyoutypeselect')
378 select.attr('id', 'asyoutypeselect')
369 select.attr('size',Math.min(10,matches.length));
379 select.attr('size',Math.min(10,matches.length));
370 var pos = this.code_mirror.cursorCoords();
380 var pos = this.code_mirror.cursorCoords();
371
381
372 // TODO: I propose to remove enough horizontal pixel
382 // TODO: I propose to remove enough horizontal pixel
373 // to align the text later
383 // to align the text later
374 complete.css('left',pos.x+'px');
384 complete.css('left',pos.x+'px');
375 complete.css('top',pos.yBot+'px');
385 complete.css('top',pos.yBot+'px');
376 complete.append(select);
386 complete.append(select);
377
387
378 $('body').append(complete);
388 $('body').append(complete);
379
389
380 // So a first actual completion. see if all the completion start wit
390 // So a first actual completion. see if all the completion start wit
381 // the same letter and complete if necessary
391 // the same letter and complete if necessary
382 fastForward = sharedStart(matches)
392 fastForward = sharedStart(matches)
383 typed_characters= fastForward.substr(matched_text.length);
393 typed_characters= fastForward.substr(matched_text.length);
384 complete_with(matches,matched_text+typed_characters,true);
394 complete_with(matches,matched_text+typed_characters,true,null);
385 filterd=matches;
395 filterd=matches;
386 // Give focus to select, and make it filter the match as the user type
396 // Give focus to select, and make it filter the match as the user type
387 // by filtering the previous matches. Called by .keypress and .keydown
397 // by filtering the previous matches. Called by .keypress and .keydown
388 var downandpress = function (event,press_or_down) {
398 var downandpress = function (event,press_or_down) {
389 var code = event.which;
399 var code = event.which;
390 var autopick = false; // auto 'pick' if only one match
400 var autopick = false; // auto 'pick' if only one match
391 if (press_or_down === 0){
401 if (press_or_down === 0){
392 press=true; down=false; //Are we called from keypress or keydown
402 press=true; down=false; //Are we called from keypress or keydown
393 } else if (press_or_down == 1){
403 } else if (press_or_down == 1){
394 press=false; down=true;
404 press=false; down=true;
395 }
405 }
396 if (code === key.shift) {
406 if (code === key.shift) {
397 // nothing on Shift
407 // nothing on Shift
398 return;
408 return;
399 }
409 }
400 if (code === key.space || code === key.enter) {
410 if (code === key.space || code === key.enter) {
401 // Pressing SPACE or ENTER will cause a pick
411 // Pressing SPACE or ENTER will cause a pick
402 event.stopPropagation();
412 event.stopPropagation();
403 event.preventDefault();
413 event.preventDefault();
404 pick();
414 pick();
405 } else if (code === 38 || code === 40) {
415 } else if (code === 38 || code === 40) {
406 // We don't want the document keydown handler to handle UP/DOWN,
416 // We don't want the document keydown handler to handle UP/DOWN,
407 // but we want the default action.
417 // but we want the default action.
408 event.stopPropagation();
418 event.stopPropagation();
409 //} else if ( key.isCompSymbol(code)|| (code==key.backspace)||(code==key.tab && down)){
419 //} else if ( key.isCompSymbol(code)|| (code==key.backspace)||(code==key.tab && down)){
410 } else if ( (code==key.backspace)||(code==key.tab) || press || key.isCompSymbol(code)){
420 } else if ( (code==key.backspace)||(code==key.tab && down) || press || key.isCompSymbol(code)){
411 if((code != key.backspace) && (code != key.tab) && press)
421 if( key.isCompSymbol(code) && press)
412 {
422 {
413 var newchar = String.fromCharCode(code);
423 var newchar = String.fromCharCode(code);
414 typed_characters=typed_characters+newchar;
424 typed_characters=typed_characters+newchar;
415 } else if (code == key.tab) {
425 } else if (code == key.tab) {
416 fastForward = sharedStart(filterd)
426 fastForward = sharedStart(filterd)
417 ffsub = fastForward.substr(matched_text.length+typed_characters.length);
427 ffsub = fastForward.substr(matched_text.length+typed_characters.length);
418 typed_characters=typed_characters+ffsub;
428 typed_characters=typed_characters+ffsub;
419 autopick=true;
429 autopick=true;
420 event.stopPropagation();
430 event.stopPropagation();
421 event.preventDefault();
431 event.preventDefault();
422 } else if (code == key.backspace) {
432 } else if (code == key.backspace && down) {
423 // cancel if user have erase everything, otherwise decrease
433 // cancel if user have erase everything, otherwise decrease
424 // what we filter with
434 // what we filter with
425 if (typed_characters.length <= 0)
435 if (typed_characters.length <= 0)
426 {
436 {
427 insert(matched_text)
437 insert(matched_text,event)
428 }
438 }
429 typed_characters=typed_characters.substr(0,typed_characters.length-1);
439 typed_characters=typed_characters.substr(0,typed_characters.length-1);
430 }
440 }else{return}
431 re = new RegExp("^"+"\%?"+matched_text+typed_characters,"");
441 re = new RegExp("^"+"\%?"+matched_text+typed_characters,"");
432 filterd = matches.filter(function(x){return re.test(x)});
442 filterd = matches.filter(function(x){return re.test(x)});
433 complete_with(filterd,matched_text+typed_characters,autopick);
443 complete_with(filterd,matched_text+typed_characters,autopick,event);
434 } else if(down){ // abort only on .keydown
444 } else if(down){ // abort only on .keydown
435 // abort with what the user have pressed until now
445 // abort with what the user have pressed until now
436 console.log('aborting with keycode : '+code+' is down :'+down);
446 console.log('aborting with keycode : '+code+' is down :'+down);
437 insert(matched_text+typed_characters);
447 insert(matched_text+typed_characters,event);
438 }
448 }
439 }
449 }
440 select.keydown(function (event) {
450 select.keydown(function (event) {
441 downandpress(event,1)
451 downandpress(event,1)
442 });
452 });
443 select.keypress(function (event) {
453 select.keypress(function (event) {
444 downandpress(event,0)
454 downandpress(event,0)
445 });
455 });
446 // Double click also causes a pick.
456 // Double click also causes a pick.
447 // and bind the last actions.
457 // and bind the last actions.
448 select.dblclick(pick);
458 select.dblclick(pick);
449 select.blur(close);
459 select.blur(close);
450 select.focus();
460 select.focus();
451 };
461 };
452
462
453 CodeCell.prototype.toggle_line_numbers = function () {
463 CodeCell.prototype.toggle_line_numbers = function () {
454 if (this.code_mirror.getOption('lineNumbers') == false) {
464 if (this.code_mirror.getOption('lineNumbers') == false) {
455 this.code_mirror.setOption('lineNumbers', true);
465 this.code_mirror.setOption('lineNumbers', true);
456 } else {
466 } else {
457 this.code_mirror.setOption('lineNumbers', false);
467 this.code_mirror.setOption('lineNumbers', false);
458 }
468 }
459 this.code_mirror.refresh();
469 this.code_mirror.refresh();
460 };
470 };
461
471
462 CodeCell.prototype.select = function () {
472 CodeCell.prototype.select = function () {
463 IPython.Cell.prototype.select.apply(this);
473 IPython.Cell.prototype.select.apply(this);
464 // Todo: this dance is needed because as of CodeMirror 2.12, focus is
474 // Todo: this dance is needed because as of CodeMirror 2.12, focus is
465 // not causing the cursor to blink if the editor is empty initially.
475 // not causing the cursor to blink if the editor is empty initially.
466 // While this seems to fix the issue, this should be fixed
476 // While this seems to fix the issue, this should be fixed
467 // in CodeMirror proper.
477 // in CodeMirror proper.
468 var s = this.code_mirror.getValue();
478 var s = this.code_mirror.getValue();
469 this.code_mirror.focus();
479 this.code_mirror.focus();
470 if (s === '') this.code_mirror.setValue('');
480 if (s === '') this.code_mirror.setValue('');
471 };
481 };
472
482
473
483
474 CodeCell.prototype.select_all = function () {
484 CodeCell.prototype.select_all = function () {
475 var start = {line: 0, ch: 0};
485 var start = {line: 0, ch: 0};
476 var nlines = this.code_mirror.lineCount();
486 var nlines = this.code_mirror.lineCount();
477 var last_line = this.code_mirror.getLine(nlines-1);
487 var last_line = this.code_mirror.getLine(nlines-1);
478 var end = {line: nlines-1, ch: last_line.length};
488 var end = {line: nlines-1, ch: last_line.length};
479 this.code_mirror.setSelection(start, end);
489 this.code_mirror.setSelection(start, end);
480 };
490 };
481
491
482
492
483 CodeCell.prototype.append_output = function (json) {
493 CodeCell.prototype.append_output = function (json) {
484 this.expand();
494 this.expand();
485 if (json.output_type === 'pyout') {
495 if (json.output_type === 'pyout') {
486 this.append_pyout(json);
496 this.append_pyout(json);
487 } else if (json.output_type === 'pyerr') {
497 } else if (json.output_type === 'pyerr') {
488 this.append_pyerr(json);
498 this.append_pyerr(json);
489 } else if (json.output_type === 'display_data') {
499 } else if (json.output_type === 'display_data') {
490 this.append_display_data(json);
500 this.append_display_data(json);
491 } else if (json.output_type === 'stream') {
501 } else if (json.output_type === 'stream') {
492 this.append_stream(json);
502 this.append_stream(json);
493 };
503 };
494 this.outputs.push(json);
504 this.outputs.push(json);
495 };
505 };
496
506
497
507
498 CodeCell.prototype.create_output_area = function () {
508 CodeCell.prototype.create_output_area = function () {
499 var oa = $("<div/>").addClass("hbox output_area");
509 var oa = $("<div/>").addClass("hbox output_area");
500 oa.append($('<div/>').addClass('prompt'));
510 oa.append($('<div/>').addClass('prompt'));
501 return oa;
511 return oa;
502 };
512 };
503
513
504
514
505 CodeCell.prototype.append_pyout = function (json) {
515 CodeCell.prototype.append_pyout = function (json) {
506 n = json.prompt_number || ' ';
516 n = json.prompt_number || ' ';
507 var toinsert = this.create_output_area();
517 var toinsert = this.create_output_area();
508 toinsert.find('div.prompt').addClass('output_prompt').html('Out[' + n + ']:');
518 toinsert.find('div.prompt').addClass('output_prompt').html('Out[' + n + ']:');
509 this.append_mime_type(json, toinsert);
519 this.append_mime_type(json, toinsert);
510 this.element.find('div.output').append(toinsert);
520 this.element.find('div.output').append(toinsert);
511 // If we just output latex, typeset it.
521 // If we just output latex, typeset it.
512 if ((json.latex !== undefined) || (json.html !== undefined)) {
522 if ((json.latex !== undefined) || (json.html !== undefined)) {
513 MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
523 MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
514 };
524 };
515 };
525 };
516
526
517
527
518 CodeCell.prototype.append_pyerr = function (json) {
528 CodeCell.prototype.append_pyerr = function (json) {
519 var tb = json.traceback;
529 var tb = json.traceback;
520 if (tb !== undefined && tb.length > 0) {
530 if (tb !== undefined && tb.length > 0) {
521 var s = '';
531 var s = '';
522 var len = tb.length;
532 var len = tb.length;
523 for (var i=0; i<len; i++) {
533 for (var i=0; i<len; i++) {
524 s = s + tb[i] + '\n';
534 s = s + tb[i] + '\n';
525 }
535 }
526 s = s + '\n';
536 s = s + '\n';
527 var toinsert = this.create_output_area();
537 var toinsert = this.create_output_area();
528 this.append_text(s, toinsert);
538 this.append_text(s, toinsert);
529 this.element.find('div.output').append(toinsert);
539 this.element.find('div.output').append(toinsert);
530 };
540 };
531 };
541 };
532
542
533
543
534 CodeCell.prototype.append_stream = function (json) {
544 CodeCell.prototype.append_stream = function (json) {
535 // temporary fix: if stream undefined (json file written prior to this patch),
545 // temporary fix: if stream undefined (json file written prior to this patch),
536 // default to most likely stdout:
546 // default to most likely stdout:
537 if (json.stream == undefined){
547 if (json.stream == undefined){
538 json.stream = 'stdout';
548 json.stream = 'stdout';
539 }
549 }
540 var subclass = "output_"+json.stream;
550 var subclass = "output_"+json.stream;
541 if (this.outputs.length > 0){
551 if (this.outputs.length > 0){
542 // have at least one output to consider
552 // have at least one output to consider
543 var last = this.outputs[this.outputs.length-1];
553 var last = this.outputs[this.outputs.length-1];
544 if (last.output_type == 'stream' && json.stream == last.stream){
554 if (last.output_type == 'stream' && json.stream == last.stream){
545 // latest output was in the same stream,
555 // latest output was in the same stream,
546 // so append directly into its pre tag
556 // so append directly into its pre tag
547 this.element.find('div.'+subclass).last().find('pre').append(json.text);
557 this.element.find('div.'+subclass).last().find('pre').append(json.text);
548 return;
558 return;
549 }
559 }
550 }
560 }
551
561
552 // If we got here, attach a new div
562 // If we got here, attach a new div
553 var toinsert = this.create_output_area();
563 var toinsert = this.create_output_area();
554 this.append_text(json.text, toinsert, "output_stream "+subclass);
564 this.append_text(json.text, toinsert, "output_stream "+subclass);
555 this.element.find('div.output').append(toinsert);
565 this.element.find('div.output').append(toinsert);
556 };
566 };
557
567
558
568
559 CodeCell.prototype.append_display_data = function (json) {
569 CodeCell.prototype.append_display_data = function (json) {
560 var toinsert = this.create_output_area();
570 var toinsert = this.create_output_area();
561 this.append_mime_type(json, toinsert);
571 this.append_mime_type(json, toinsert);
562 this.element.find('div.output').append(toinsert);
572 this.element.find('div.output').append(toinsert);
563 // If we just output latex, typeset it.
573 // If we just output latex, typeset it.
564 if ( (json.latex !== undefined) || (json.html !== undefined) ) {
574 if ( (json.latex !== undefined) || (json.html !== undefined) ) {
565 MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
575 MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
566 };
576 };
567 };
577 };
568
578
569
579
570 CodeCell.prototype.append_mime_type = function (json, element) {
580 CodeCell.prototype.append_mime_type = function (json, element) {
571 if (json.html !== undefined) {
581 if (json.html !== undefined) {
572 this.append_html(json.html, element);
582 this.append_html(json.html, element);
573 } else if (json.latex !== undefined) {
583 } else if (json.latex !== undefined) {
574 this.append_latex(json.latex, element);
584 this.append_latex(json.latex, element);
575 } else if (json.svg !== undefined) {
585 } else if (json.svg !== undefined) {
576 this.append_svg(json.svg, element);
586 this.append_svg(json.svg, element);
577 } else if (json.png !== undefined) {
587 } else if (json.png !== undefined) {
578 this.append_png(json.png, element);
588 this.append_png(json.png, element);
579 } else if (json.jpeg !== undefined) {
589 } else if (json.jpeg !== undefined) {
580 this.append_jpeg(json.jpeg, element);
590 this.append_jpeg(json.jpeg, element);
581 } else if (json.text !== undefined) {
591 } else if (json.text !== undefined) {
582 this.append_text(json.text, element);
592 this.append_text(json.text, element);
583 };
593 };
584 };
594 };
585
595
586
596
587 CodeCell.prototype.append_html = function (html, element) {
597 CodeCell.prototype.append_html = function (html, element) {
588 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_html rendered_html");
598 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_html rendered_html");
589 toinsert.append(html);
599 toinsert.append(html);
590 element.append(toinsert);
600 element.append(toinsert);
591 };
601 };
592
602
593
603
594 CodeCell.prototype.append_text = function (data, element, extra_class) {
604 CodeCell.prototype.append_text = function (data, element, extra_class) {
595 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_text");
605 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_text");
596 if (extra_class){
606 if (extra_class){
597 toinsert.addClass(extra_class);
607 toinsert.addClass(extra_class);
598 }
608 }
599 toinsert.append($("<pre/>").html(data));
609 toinsert.append($("<pre/>").html(data));
600 element.append(toinsert);
610 element.append(toinsert);
601 };
611 };
602
612
603
613
604 CodeCell.prototype.append_svg = function (svg, element) {
614 CodeCell.prototype.append_svg = function (svg, element) {
605 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_svg");
615 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_svg");
606 toinsert.append(svg);
616 toinsert.append(svg);
607 element.append(toinsert);
617 element.append(toinsert);
608 };
618 };
609
619
610
620
611 CodeCell.prototype.append_png = function (png, element) {
621 CodeCell.prototype.append_png = function (png, element) {
612 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_png");
622 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_png");
613 toinsert.append($("<img/>").attr('src','data:image/png;base64,'+png));
623 toinsert.append($("<img/>").attr('src','data:image/png;base64,'+png));
614 element.append(toinsert);
624 element.append(toinsert);
615 };
625 };
616
626
617
627
618 CodeCell.prototype.append_jpeg = function (jpeg, element) {
628 CodeCell.prototype.append_jpeg = function (jpeg, element) {
619 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_jpeg");
629 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_jpeg");
620 toinsert.append($("<img/>").attr('src','data:image/jpeg;base64,'+jpeg));
630 toinsert.append($("<img/>").attr('src','data:image/jpeg;base64,'+jpeg));
621 element.append(toinsert);
631 element.append(toinsert);
622 };
632 };
623
633
624
634
625 CodeCell.prototype.append_latex = function (latex, element) {
635 CodeCell.prototype.append_latex = function (latex, element) {
626 // This method cannot do the typesetting because the latex first has to
636 // This method cannot do the typesetting because the latex first has to
627 // be on the page.
637 // be on the page.
628 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_latex");
638 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_latex");
629 toinsert.append(latex);
639 toinsert.append(latex);
630 element.append(toinsert);
640 element.append(toinsert);
631 };
641 };
632
642
633
643
634 CodeCell.prototype.clear_output = function (stdout, stderr, other) {
644 CodeCell.prototype.clear_output = function (stdout, stderr, other) {
635 var output_div = this.element.find("div.output");
645 var output_div = this.element.find("div.output");
636 if (stdout && stderr && other){
646 if (stdout && stderr && other){
637 // clear all, no need for logic
647 // clear all, no need for logic
638 output_div.html("");
648 output_div.html("");
639 this.outputs = [];
649 this.outputs = [];
640 return;
650 return;
641 }
651 }
642 // remove html output
652 // remove html output
643 // each output_subarea that has an identifying class is in an output_area
653 // each output_subarea that has an identifying class is in an output_area
644 // which is the element to be removed.
654 // which is the element to be removed.
645 if (stdout){
655 if (stdout){
646 output_div.find("div.output_stdout").parent().remove();
656 output_div.find("div.output_stdout").parent().remove();
647 }
657 }
648 if (stderr){
658 if (stderr){
649 output_div.find("div.output_stderr").parent().remove();
659 output_div.find("div.output_stderr").parent().remove();
650 }
660 }
651 if (other){
661 if (other){
652 output_div.find("div.output_subarea").not("div.output_stderr").not("div.output_stdout").parent().remove();
662 output_div.find("div.output_subarea").not("div.output_stderr").not("div.output_stdout").parent().remove();
653 }
663 }
654
664
655 // remove cleared outputs from JSON list:
665 // remove cleared outputs from JSON list:
656 for (var i = this.outputs.length - 1; i >= 0; i--){
666 for (var i = this.outputs.length - 1; i >= 0; i--){
657 var out = this.outputs[i];
667 var out = this.outputs[i];
658 var output_type = out.output_type;
668 var output_type = out.output_type;
659 if (output_type == "display_data" && other){
669 if (output_type == "display_data" && other){
660 this.outputs.splice(i,1);
670 this.outputs.splice(i,1);
661 }else if (output_type == "stream"){
671 }else if (output_type == "stream"){
662 if (stdout && out.stream == "stdout"){
672 if (stdout && out.stream == "stdout"){
663 this.outputs.splice(i,1);
673 this.outputs.splice(i,1);
664 }else if (stderr && out.stream == "stderr"){
674 }else if (stderr && out.stream == "stderr"){
665 this.outputs.splice(i,1);
675 this.outputs.splice(i,1);
666 }
676 }
667 }
677 }
668 }
678 }
669 };
679 };
670
680
671
681
672 CodeCell.prototype.clear_input = function () {
682 CodeCell.prototype.clear_input = function () {
673 this.code_mirror.setValue('');
683 this.code_mirror.setValue('');
674 };
684 };
675
685
676
686
677 CodeCell.prototype.collapse = function () {
687 CodeCell.prototype.collapse = function () {
678 if (!this.collapsed) {
688 if (!this.collapsed) {
679 this.element.find('div.output').hide();
689 this.element.find('div.output').hide();
680 this.collapsed = true;
690 this.collapsed = true;
681 };
691 };
682 };
692 };
683
693
684
694
685 CodeCell.prototype.expand = function () {
695 CodeCell.prototype.expand = function () {
686 if (this.collapsed) {
696 if (this.collapsed) {
687 this.element.find('div.output').show();
697 this.element.find('div.output').show();
688 this.collapsed = false;
698 this.collapsed = false;
689 };
699 };
690 };
700 };
691
701
692
702
693 CodeCell.prototype.toggle_output = function () {
703 CodeCell.prototype.toggle_output = function () {
694 if (this.collapsed) {
704 if (this.collapsed) {
695 this.expand();
705 this.expand();
696 } else {
706 } else {
697 this.collapse();
707 this.collapse();
698 };
708 };
699 };
709 };
700
710
701 CodeCell.prototype.set_input_prompt = function (number) {
711 CodeCell.prototype.set_input_prompt = function (number) {
702 var n = number || '&nbsp;';
712 var n = number || '&nbsp;';
703 this.input_prompt_number = n;
713 this.input_prompt_number = n;
704 this.element.find('div.input_prompt').html('In&nbsp;[' + n + ']:');
714 this.element.find('div.input_prompt').html('In&nbsp;[' + n + ']:');
705 };
715 };
706
716
707
717
708 CodeCell.prototype.get_code = function () {
718 CodeCell.prototype.get_code = function () {
709 return this.code_mirror.getValue();
719 return this.code_mirror.getValue();
710 };
720 };
711
721
712
722
713 CodeCell.prototype.set_code = function (code) {
723 CodeCell.prototype.set_code = function (code) {
714 return this.code_mirror.setValue(code);
724 return this.code_mirror.setValue(code);
715 };
725 };
716
726
717
727
718 CodeCell.prototype.at_top = function () {
728 CodeCell.prototype.at_top = function () {
719 var cursor = this.code_mirror.getCursor();
729 var cursor = this.code_mirror.getCursor();
720 if (cursor.line === 0) {
730 if (cursor.line === 0) {
721 return true;
731 return true;
722 } else {
732 } else {
723 return false;
733 return false;
724 }
734 }
725 };
735 };
726
736
727
737
728 CodeCell.prototype.at_bottom = function () {
738 CodeCell.prototype.at_bottom = function () {
729 var cursor = this.code_mirror.getCursor();
739 var cursor = this.code_mirror.getCursor();
730 if (cursor.line === (this.code_mirror.lineCount()-1)) {
740 if (cursor.line === (this.code_mirror.lineCount()-1)) {
731 return true;
741 return true;
732 } else {
742 } else {
733 return false;
743 return false;
734 }
744 }
735 };
745 };
736
746
737
747
738 CodeCell.prototype.fromJSON = function (data) {
748 CodeCell.prototype.fromJSON = function (data) {
739 console.log('Import from JSON:', data);
749 console.log('Import from JSON:', data);
740 if (data.cell_type === 'code') {
750 if (data.cell_type === 'code') {
741 if (data.input !== undefined) {
751 if (data.input !== undefined) {
742 this.set_code(data.input);
752 this.set_code(data.input);
743 }
753 }
744 if (data.prompt_number !== undefined) {
754 if (data.prompt_number !== undefined) {
745 this.set_input_prompt(data.prompt_number);
755 this.set_input_prompt(data.prompt_number);
746 } else {
756 } else {
747 this.set_input_prompt();
757 this.set_input_prompt();
748 };
758 };
749 var len = data.outputs.length;
759 var len = data.outputs.length;
750 for (var i=0; i<len; i++) {
760 for (var i=0; i<len; i++) {
751 this.append_output(data.outputs[i]);
761 this.append_output(data.outputs[i]);
752 };
762 };
753 if (data.collapsed !== undefined) {
763 if (data.collapsed !== undefined) {
754 if (data.collapsed) {
764 if (data.collapsed) {
755 this.collapse();
765 this.collapse();
756 };
766 };
757 };
767 };
758 };
768 };
759 };
769 };
760
770
761
771
762 CodeCell.prototype.toJSON = function () {
772 CodeCell.prototype.toJSON = function () {
763 var data = {};
773 var data = {};
764 data.input = this.get_code();
774 data.input = this.get_code();
765 data.cell_type = 'code';
775 data.cell_type = 'code';
766 if (this.input_prompt_number !== ' ') {
776 if (this.input_prompt_number !== ' ') {
767 data.prompt_number = this.input_prompt_number;
777 data.prompt_number = this.input_prompt_number;
768 };
778 };
769 var outputs = [];
779 var outputs = [];
770 var len = this.outputs.length;
780 var len = this.outputs.length;
771 for (var i=0; i<len; i++) {
781 for (var i=0; i<len; i++) {
772 outputs[i] = this.outputs[i];
782 outputs[i] = this.outputs[i];
773 };
783 };
774 data.outputs = outputs;
784 data.outputs = outputs;
775 data.language = 'python';
785 data.language = 'python';
776 data.collapsed = this.collapsed;
786 data.collapsed = this.collapsed;
777 // console.log('Export to JSON:',data);
787 // console.log('Export to JSON:',data);
778 return data;
788 return data;
779 };
789 };
780
790
781
791
782 IPython.CodeCell = CodeCell;
792 IPython.CodeCell = CodeCell;
783
793
784 return IPython;
794 return IPython;
785 }(IPython));
795 }(IPython));
General Comments 0
You need to be logged in to leave comments. Login now