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