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