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