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