##// END OF EJS Templates
Merge pull request #2728 from Carreau/shifttab...
Bussonnier Matthias -
r8971:99339d10 merge
parent child Browse files
Show More
@@ -1,369 +1,378
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 * An extendable module that provide base functionnality to create cell for notebook.
12 * An extendable module that provide base functionnality to create cell for notebook.
13 * @module IPython
13 * @module IPython
14 * @namespace IPython
14 * @namespace IPython
15 * @submodule CodeCell
15 * @submodule CodeCell
16 */
16 */
17
17
18 var IPython = (function (IPython) {
18 var IPython = (function (IPython) {
19 "use strict";
19 "use strict";
20
20
21 var utils = IPython.utils;
21 var utils = IPython.utils;
22 var key = IPython.utils.keycodes;
22 var key = IPython.utils.keycodes;
23 CodeMirror.modeURL = "/static/codemirror/mode/%N/%N.js";
23 CodeMirror.modeURL = "/static/codemirror/mode/%N/%N.js";
24
24
25 /**
25 /**
26 * A Cell conceived to write code.
26 * A Cell conceived to write code.
27 *
27 *
28 * The kernel doesn't have to be set at creation time, in that case
28 * The kernel doesn't have to be set at creation time, in that case
29 * it will be null and set_kernel has to be called later.
29 * it will be null and set_kernel has to be called later.
30 * @class CodeCell
30 * @class CodeCell
31 * @extends IPython.Cell
31 * @extends IPython.Cell
32 *
32 *
33 * @constructor
33 * @constructor
34 * @param {Object|null} kernel
34 * @param {Object|null} kernel
35 */
35 */
36 var CodeCell = function (kernel) {
36 var CodeCell = function (kernel) {
37 this.kernel = kernel || null;
37 this.kernel = kernel || null;
38 this.code_mirror = null;
38 this.code_mirror = null;
39 this.input_prompt_number = null;
39 this.input_prompt_number = null;
40 this.tooltip_on_tab = true;
41 this.collapsed = false;
40 this.collapsed = false;
42 this.default_mode = 'python';
41 this.default_mode = 'python';
43 IPython.Cell.apply(this, arguments);
42 IPython.Cell.apply(this, arguments);
44
43
45 var that = this;
44 var that = this;
46 this.element.focusout(
45 this.element.focusout(
47 function() { that.auto_highlight(); }
46 function() { that.auto_highlight(); }
48 );
47 );
49 };
48 };
50
49
51
52 CodeCell.prototype = new IPython.Cell();
50 CodeCell.prototype = new IPython.Cell();
53
51
54 /**
52 /**
55 * @method auto_highlight
53 * @method auto_highlight
56 */
54 */
57 CodeCell.prototype.auto_highlight = function () {
55 CodeCell.prototype.auto_highlight = function () {
58 this._auto_highlight(IPython.config.cell_magic_highlight)
56 this._auto_highlight(IPython.config.cell_magic_highlight)
59 };
57 };
60
58
61 /** @method create_element */
59 /** @method create_element */
62 CodeCell.prototype.create_element = function () {
60 CodeCell.prototype.create_element = function () {
63 var cell = $('<div></div>').addClass('cell border-box-sizing code_cell vbox');
61 var cell = $('<div></div>').addClass('cell border-box-sizing code_cell vbox');
64 cell.attr('tabindex','2');
62 cell.attr('tabindex','2');
65 var input = $('<div></div>').addClass('input hbox');
63 var input = $('<div></div>').addClass('input hbox');
66 input.append($('<div/>').addClass('prompt input_prompt'));
64 input.append($('<div/>').addClass('prompt input_prompt'));
67 var input_area = $('<div/>').addClass('input_area box-flex1');
65 var input_area = $('<div/>').addClass('input_area box-flex1');
68 this.code_mirror = CodeMirror(input_area.get(0), {
66 this.code_mirror = CodeMirror(input_area.get(0), {
69 indentUnit : 4,
67 indentUnit : 4,
70 mode: 'python',
68 mode: 'python',
71 theme: 'ipython',
69 theme: 'ipython',
72 readOnly: this.read_only,
70 readOnly: this.read_only,
73 extraKeys: {"Tab": "indentMore","Shift-Tab" : "indentLess",'Backspace':"delSpaceToPrevTabStop"},
71 extraKeys: {"Tab": "indentMore","Shift-Tab" : "indentLess",'Backspace':"delSpaceToPrevTabStop"},
74 onKeyEvent: $.proxy(this.handle_codemirror_keyevent,this),
72 onKeyEvent: $.proxy(this.handle_codemirror_keyevent,this),
75 matchBrackets: true
73 matchBrackets: true
76 });
74 });
77 input.append(input_area);
75 input.append(input_area);
78 var output = $('<div></div>');
76 var output = $('<div></div>');
79 cell.append(input).append(output);
77 cell.append(input).append(output);
80 this.element = cell;
78 this.element = cell;
81 this.output_area = new IPython.OutputArea(output, true);
79 this.output_area = new IPython.OutputArea(output, true);
82
80
83 // construct a completer only if class exist
81 // construct a completer only if class exist
84 // otherwise no print view
82 // otherwise no print view
85 if (IPython.Completer !== undefined)
83 if (IPython.Completer !== undefined)
86 {
84 {
87 this.completer = new IPython.Completer(this);
85 this.completer = new IPython.Completer(this);
88 }
86 }
89 };
87 };
90
88
91 /**
89 /**
92 * This method gets called in CodeMirror's onKeyDown/onKeyPress
90 * This method gets called in CodeMirror's onKeyDown/onKeyPress
93 * handlers and is used to provide custom key handling. Its return
91 * handlers and is used to provide custom key handling. Its return
94 * value is used to determine if CodeMirror should ignore the event:
92 * value is used to determine if CodeMirror should ignore the event:
95 * true = ignore, false = don't ignore.
93 * true = ignore, false = don't ignore.
96 * @method handle_codemirror_keyevent
94 * @method handle_codemirror_keyevent
97 */
95 */
98 CodeCell.prototype.handle_codemirror_keyevent = function (editor, event) {
96 CodeCell.prototype.handle_codemirror_keyevent = function (editor, event) {
99
97
100 if (this.read_only){
98 if (this.read_only){
101 return false;
99 return false;
102 }
100 }
103
101
104 var that = this;
102 var that = this;
105 // whatever key is pressed, first, cancel the tooltip request before
103 // whatever key is pressed, first, cancel the tooltip request before
106 // they are sent, and remove tooltip if any, except for tab again
104 // they are sent, and remove tooltip if any, except for tab again
107 if (event.type === 'keydown' && event.which != key.TAB ) {
105 if (event.type === 'keydown' && event.which != key.TAB ) {
108 IPython.tooltip.remove_and_cancel_tooltip();
106 IPython.tooltip.remove_and_cancel_tooltip();
109 };
107 };
110
108
111 var cur = editor.getCursor();
109 var cur = editor.getCursor();
112 if (event.keyCode === key.ENTER){
110 if (event.keyCode === key.ENTER){
113 this.auto_highlight();
111 this.auto_highlight();
114 }
112 }
115
113
116 if (event.keyCode === key.ENTER && (event.shiftKey || event.ctrlKey)) {
114 if (event.keyCode === key.ENTER && (event.shiftKey || event.ctrlKey)) {
117 // Always ignore shift-enter in CodeMirror as we handle it.
115 // Always ignore shift-enter in CodeMirror as we handle it.
118 return true;
116 return true;
119 } else if (event.which === 40 && event.type === 'keypress' && IPython.tooltip.time_before_tooltip >= 0) {
117 } else if (event.which === 40 && event.type === 'keypress' && IPython.tooltip.time_before_tooltip >= 0) {
120 // triger on keypress (!) otherwise inconsistent event.which depending on plateform
118 // triger on keypress (!) otherwise inconsistent event.which depending on plateform
121 // browser and keyboard layout !
119 // browser and keyboard layout !
122 // Pressing '(' , request tooltip, don't forget to reappend it
120 // Pressing '(' , request tooltip, don't forget to reappend it
123 IPython.tooltip.pending(that);
121 IPython.tooltip.pending(that);
124 } else if (event.which === key.UPARROW && event.type === 'keydown') {
122 } else if (event.which === key.UPARROW && event.type === 'keydown') {
125 // If we are not at the top, let CM handle the up arrow and
123 // If we are not at the top, let CM handle the up arrow and
126 // prevent the global keydown handler from handling it.
124 // prevent the global keydown handler from handling it.
127 if (!that.at_top()) {
125 if (!that.at_top()) {
128 event.stop();
126 event.stop();
129 return false;
127 return false;
130 } else {
128 } else {
131 return true;
129 return true;
132 };
130 };
133 } else if (event.which === key.ESC) {
131 } else if (event.which === key.ESC) {
134 IPython.tooltip.remove_and_cancel_tooltip(true);
132 IPython.tooltip.remove_and_cancel_tooltip(true);
135 return true;
133 return true;
136 } else if (event.which === key.DOWNARROW && event.type === 'keydown') {
134 } else if (event.which === key.DOWNARROW && event.type === 'keydown') {
137 // If we are not at the bottom, let CM handle the down arrow and
135 // If we are not at the bottom, let CM handle the down arrow and
138 // prevent the global keydown handler from handling it.
136 // prevent the global keydown handler from handling it.
139 if (!that.at_bottom()) {
137 if (!that.at_bottom()) {
140 event.stop();
138 event.stop();
141 return false;
139 return false;
142 } else {
140 } else {
143 return true;
141 return true;
144 };
142 };
143 } else if (event.keyCode === key.TAB && event.type == 'keydown' && event.shiftKey) {
144 if (editor.somethingSelected()){
145 var anchor = editor.getCursor("anchor");
146 var head = editor.getCursor("head");
147 if( anchor.line != head.line){
148 return false;
149 }
150 }
151 IPython.tooltip.request(that);
152 event.stop();
153 return true;
145 } else if (event.keyCode === key.TAB && event.type == 'keydown') {
154 } else if (event.keyCode === key.TAB && event.type == 'keydown') {
146 // Tab completion.
155 // Tab completion.
147 //Do not trim here because of tooltip
156 //Do not trim here because of tooltip
148 if (editor.somethingSelected()){return false}
157 if (editor.somethingSelected()){return false}
149 var pre_cursor = editor.getRange({line:cur.line,ch:0},cur);
158 var pre_cursor = editor.getRange({line:cur.line,ch:0},cur);
150 if (pre_cursor.trim() === "") {
159 if (pre_cursor.trim() === "") {
151 // Don't autocomplete if the part of the line before the cursor
160 // Don't autocomplete if the part of the line before the cursor
152 // is empty. In this case, let CodeMirror handle indentation.
161 // is empty. In this case, let CodeMirror handle indentation.
153 return false;
162 return false;
154 } else if ((pre_cursor.substr(-1) === "("|| pre_cursor.substr(-1) === " ") && that.tooltip_on_tab ) {
163 } else if ((pre_cursor.substr(-1) === "("|| pre_cursor.substr(-1) === " ") && IPython.config.tooltip_on_tab ) {
155 IPython.tooltip.request(that);
164 IPython.tooltip.request(that);
156 // Prevent the event from bubbling up.
165 // Prevent the event from bubbling up.
157 event.stop();
166 event.stop();
158 // Prevent CodeMirror from handling the tab.
167 // Prevent CodeMirror from handling the tab.
159 return true;
168 return true;
160 } else {
169 } else {
161 event.stop();
170 event.stop();
162 this.completer.startCompletion();
171 this.completer.startCompletion();
163 return true;
172 return true;
164 };
173 };
165 } else {
174 } else {
166 // keypress/keyup also trigger on TAB press, and we don't want to
175 // keypress/keyup also trigger on TAB press, and we don't want to
167 // use those to disable tab completion.
176 // use those to disable tab completion.
168 return false;
177 return false;
169 };
178 };
170 return false;
179 return false;
171 };
180 };
172
181
173
182
174 // Kernel related calls.
183 // Kernel related calls.
175
184
176 CodeCell.prototype.set_kernel = function (kernel) {
185 CodeCell.prototype.set_kernel = function (kernel) {
177 this.kernel = kernel;
186 this.kernel = kernel;
178 }
187 }
179
188
180 /**
189 /**
181 * Execute current code cell to the kernel
190 * Execute current code cell to the kernel
182 * @method execute
191 * @method execute
183 */
192 */
184 CodeCell.prototype.execute = function () {
193 CodeCell.prototype.execute = function () {
185 this.output_area.clear_output(true, true, true);
194 this.output_area.clear_output(true, true, true);
186 this.set_input_prompt('*');
195 this.set_input_prompt('*');
187 this.element.addClass("running");
196 this.element.addClass("running");
188 var callbacks = {
197 var callbacks = {
189 'execute_reply': $.proxy(this._handle_execute_reply, this),
198 'execute_reply': $.proxy(this._handle_execute_reply, this),
190 'output': $.proxy(this.output_area.handle_output, this.output_area),
199 'output': $.proxy(this.output_area.handle_output, this.output_area),
191 'clear_output': $.proxy(this.output_area.handle_clear_output, this.output_area),
200 'clear_output': $.proxy(this.output_area.handle_clear_output, this.output_area),
192 'set_next_input': $.proxy(this._handle_set_next_input, this)
201 'set_next_input': $.proxy(this._handle_set_next_input, this)
193 };
202 };
194 var msg_id = this.kernel.execute(this.get_text(), callbacks, {silent: false});
203 var msg_id = this.kernel.execute(this.get_text(), callbacks, {silent: false});
195 };
204 };
196
205
197 /**
206 /**
198 * @method _handle_execute_reply
207 * @method _handle_execute_reply
199 * @private
208 * @private
200 */
209 */
201 CodeCell.prototype._handle_execute_reply = function (content) {
210 CodeCell.prototype._handle_execute_reply = function (content) {
202 this.set_input_prompt(content.execution_count);
211 this.set_input_prompt(content.execution_count);
203 this.element.removeClass("running");
212 this.element.removeClass("running");
204 $([IPython.events]).trigger('set_dirty.Notebook', {'value': true});
213 $([IPython.events]).trigger('set_dirty.Notebook', {'value': true});
205 }
214 }
206
215
207 CodeCell.prototype._handle_set_next_input = function (text) {
216 CodeCell.prototype._handle_set_next_input = function (text) {
208 var data = {'cell': this, 'text': text}
217 var data = {'cell': this, 'text': text}
209 $([IPython.events]).trigger('set_next_input.Notebook', data);
218 $([IPython.events]).trigger('set_next_input.Notebook', data);
210 }
219 }
211
220
212 // Basic cell manipulation.
221 // Basic cell manipulation.
213
222
214 CodeCell.prototype.select = function () {
223 CodeCell.prototype.select = function () {
215 IPython.Cell.prototype.select.apply(this);
224 IPython.Cell.prototype.select.apply(this);
216 this.code_mirror.refresh();
225 this.code_mirror.refresh();
217 this.code_mirror.focus();
226 this.code_mirror.focus();
218 this.auto_highlight();
227 this.auto_highlight();
219 // We used to need an additional refresh() after the focus, but
228 // We used to need an additional refresh() after the focus, but
220 // it appears that this has been fixed in CM. This bug would show
229 // it appears that this has been fixed in CM. This bug would show
221 // up on FF when a newly loaded markdown cell was edited.
230 // up on FF when a newly loaded markdown cell was edited.
222 };
231 };
223
232
224
233
225 CodeCell.prototype.select_all = function () {
234 CodeCell.prototype.select_all = function () {
226 var start = {line: 0, ch: 0};
235 var start = {line: 0, ch: 0};
227 var nlines = this.code_mirror.lineCount();
236 var nlines = this.code_mirror.lineCount();
228 var last_line = this.code_mirror.getLine(nlines-1);
237 var last_line = this.code_mirror.getLine(nlines-1);
229 var end = {line: nlines-1, ch: last_line.length};
238 var end = {line: nlines-1, ch: last_line.length};
230 this.code_mirror.setSelection(start, end);
239 this.code_mirror.setSelection(start, end);
231 };
240 };
232
241
233
242
234 CodeCell.prototype.collapse = function () {
243 CodeCell.prototype.collapse = function () {
235 this.collapsed = true;
244 this.collapsed = true;
236 this.output_area.collapse();
245 this.output_area.collapse();
237 };
246 };
238
247
239
248
240 CodeCell.prototype.expand = function () {
249 CodeCell.prototype.expand = function () {
241 this.collapsed = false;
250 this.collapsed = false;
242 this.output_area.expand();
251 this.output_area.expand();
243 };
252 };
244
253
245
254
246 CodeCell.prototype.toggle_output = function () {
255 CodeCell.prototype.toggle_output = function () {
247 this.collapsed = Boolean(1 - this.collapsed);
256 this.collapsed = Boolean(1 - this.collapsed);
248 this.output_area.toggle_output();
257 this.output_area.toggle_output();
249 };
258 };
250
259
251
260
252 CodeCell.prototype.toggle_output_scroll = function () {
261 CodeCell.prototype.toggle_output_scroll = function () {
253 this.output_area.toggle_scroll();
262 this.output_area.toggle_scroll();
254 };
263 };
255
264
256
265
257 CodeCell.input_prompt_classical = function (prompt_value, lines_number) {
266 CodeCell.input_prompt_classical = function (prompt_value, lines_number) {
258 var ns = prompt_value || "&nbsp;";
267 var ns = prompt_value || "&nbsp;";
259 return 'In&nbsp;[' + ns + ']:'
268 return 'In&nbsp;[' + ns + ']:'
260 };
269 };
261
270
262 CodeCell.input_prompt_continuation = function (prompt_value, lines_number) {
271 CodeCell.input_prompt_continuation = function (prompt_value, lines_number) {
263 var html = [CodeCell.input_prompt_classical(prompt_value, lines_number)];
272 var html = [CodeCell.input_prompt_classical(prompt_value, lines_number)];
264 for(var i=1; i < lines_number; i++){html.push(['...:'])};
273 for(var i=1; i < lines_number; i++){html.push(['...:'])};
265 return html.join('</br>')
274 return html.join('</br>')
266 };
275 };
267
276
268 CodeCell.input_prompt_function = CodeCell.input_prompt_classical;
277 CodeCell.input_prompt_function = CodeCell.input_prompt_classical;
269
278
270
279
271 CodeCell.prototype.set_input_prompt = function (number) {
280 CodeCell.prototype.set_input_prompt = function (number) {
272 var nline = 1
281 var nline = 1
273 if( this.code_mirror != undefined) {
282 if( this.code_mirror != undefined) {
274 nline = this.code_mirror.lineCount();
283 nline = this.code_mirror.lineCount();
275 }
284 }
276 this.input_prompt_number = number;
285 this.input_prompt_number = number;
277 var prompt_html = CodeCell.input_prompt_function(this.input_prompt_number, nline);
286 var prompt_html = CodeCell.input_prompt_function(this.input_prompt_number, nline);
278 this.element.find('div.input_prompt').html(prompt_html);
287 this.element.find('div.input_prompt').html(prompt_html);
279 };
288 };
280
289
281
290
282 CodeCell.prototype.clear_input = function () {
291 CodeCell.prototype.clear_input = function () {
283 this.code_mirror.setValue('');
292 this.code_mirror.setValue('');
284 };
293 };
285
294
286
295
287 CodeCell.prototype.get_text = function () {
296 CodeCell.prototype.get_text = function () {
288 return this.code_mirror.getValue();
297 return this.code_mirror.getValue();
289 };
298 };
290
299
291
300
292 CodeCell.prototype.set_text = function (code) {
301 CodeCell.prototype.set_text = function (code) {
293 return this.code_mirror.setValue(code);
302 return this.code_mirror.setValue(code);
294 };
303 };
295
304
296
305
297 CodeCell.prototype.at_top = function () {
306 CodeCell.prototype.at_top = function () {
298 var cursor = this.code_mirror.getCursor();
307 var cursor = this.code_mirror.getCursor();
299 if (cursor.line === 0 && cursor.ch === 0) {
308 if (cursor.line === 0 && cursor.ch === 0) {
300 return true;
309 return true;
301 } else {
310 } else {
302 return false;
311 return false;
303 }
312 }
304 };
313 };
305
314
306
315
307 CodeCell.prototype.at_bottom = function () {
316 CodeCell.prototype.at_bottom = function () {
308 var cursor = this.code_mirror.getCursor();
317 var cursor = this.code_mirror.getCursor();
309 if (cursor.line === (this.code_mirror.lineCount()-1) && cursor.ch === this.code_mirror.getLine(cursor.line).length) {
318 if (cursor.line === (this.code_mirror.lineCount()-1) && cursor.ch === this.code_mirror.getLine(cursor.line).length) {
310 return true;
319 return true;
311 } else {
320 } else {
312 return false;
321 return false;
313 }
322 }
314 };
323 };
315
324
316
325
317 CodeCell.prototype.clear_output = function (stdout, stderr, other) {
326 CodeCell.prototype.clear_output = function (stdout, stderr, other) {
318 this.output_area.clear_output(stdout, stderr, other);
327 this.output_area.clear_output(stdout, stderr, other);
319 };
328 };
320
329
321
330
322 // JSON serialization
331 // JSON serialization
323
332
324 CodeCell.prototype.fromJSON = function (data) {
333 CodeCell.prototype.fromJSON = function (data) {
325 IPython.Cell.prototype.fromJSON.apply(this, arguments);
334 IPython.Cell.prototype.fromJSON.apply(this, arguments);
326 if (data.cell_type === 'code') {
335 if (data.cell_type === 'code') {
327 if (data.input !== undefined) {
336 if (data.input !== undefined) {
328 this.set_text(data.input);
337 this.set_text(data.input);
329 // make this value the starting point, so that we can only undo
338 // make this value the starting point, so that we can only undo
330 // to this state, instead of a blank cell
339 // to this state, instead of a blank cell
331 this.code_mirror.clearHistory();
340 this.code_mirror.clearHistory();
332 this.auto_highlight();
341 this.auto_highlight();
333 }
342 }
334 if (data.prompt_number !== undefined) {
343 if (data.prompt_number !== undefined) {
335 this.set_input_prompt(data.prompt_number);
344 this.set_input_prompt(data.prompt_number);
336 } else {
345 } else {
337 this.set_input_prompt();
346 this.set_input_prompt();
338 };
347 };
339 this.output_area.fromJSON(data.outputs);
348 this.output_area.fromJSON(data.outputs);
340 if (data.collapsed !== undefined) {
349 if (data.collapsed !== undefined) {
341 if (data.collapsed) {
350 if (data.collapsed) {
342 this.collapse();
351 this.collapse();
343 } else {
352 } else {
344 this.expand();
353 this.expand();
345 };
354 };
346 };
355 };
347 };
356 };
348 };
357 };
349
358
350
359
351 CodeCell.prototype.toJSON = function () {
360 CodeCell.prototype.toJSON = function () {
352 var data = IPython.Cell.prototype.toJSON.apply(this);
361 var data = IPython.Cell.prototype.toJSON.apply(this);
353 data.input = this.get_text();
362 data.input = this.get_text();
354 data.cell_type = 'code';
363 data.cell_type = 'code';
355 if (this.input_prompt_number) {
364 if (this.input_prompt_number) {
356 data.prompt_number = this.input_prompt_number;
365 data.prompt_number = this.input_prompt_number;
357 };
366 };
358 var outputs = this.output_area.toJSON();
367 var outputs = this.output_area.toJSON();
359 data.outputs = outputs;
368 data.outputs = outputs;
360 data.language = 'python';
369 data.language = 'python';
361 data.collapsed = this.collapsed;
370 data.collapsed = this.collapsed;
362 return data;
371 return data;
363 };
372 };
364
373
365
374
366 IPython.CodeCell = CodeCell;
375 IPython.CodeCell = CodeCell;
367
376
368 return IPython;
377 return IPython;
369 }(IPython));
378 }(IPython));
@@ -1,74 +1,78
1 //----------------------------------------------------------------------------
1 //----------------------------------------------------------------------------
2 // Copyright (C) 2012 The IPython Development Team
2 // Copyright (C) 2012 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 // Notebook
9 // Notebook
10 //============================================================================
10 //============================================================================
11
11
12 /**
12 /**
13 * @module IPython
13 * @module IPython
14 * @namespace IPython
14 * @namespace IPython
15 **/
15 **/
16
16
17 var IPython = (function (IPython) {
17 var IPython = (function (IPython) {
18 /**
18 /**
19 * A place where some stuff can be confugured.
19 * A place where some stuff can be confugured.
20 *
20 *
21 * @class config
21 * @class config
22 * @static
22 * @static
23 *
23 *
24 **/
24 **/
25 var config = {
25 var default_config = {
26 /**
26 /**
27 * Dictionary of object to autodetect highlight mode for code cell.
27 * Dictionary of object to autodetect highlight mode for code cell.
28 * Item of the dictionnary should take the form :
28 * Item of the dictionnary should take the form :
29 *
29 *
30 * key : {'reg':[list_of_regexp]}
30 * key : {'reg':[list_of_regexp]}
31 *
31 *
32 * where `key` will be the code mirror mode name
32 * where `key` will be the code mirror mode name
33 * and `list_of_regexp` should be a list of regext that should match
33 * and `list_of_regexp` should be a list of regext that should match
34 * the first line of the cell to trigger this mode.
34 * the first line of the cell to trigger this mode.
35 *
35 *
36 * if `key` is prefixed by the `magic_` prefix the codemirror `mode`
36 * if `key` is prefixed by the `magic_` prefix the codemirror `mode`
37 * will be applied only at the end of the first line
37 * will be applied only at the end of the first line
38 *
38 *
39 * @attribute cell_magic_highlight
39 * @attribute cell_magic_highlight
40 * @example
40 * @example
41 * This would trigger javascript mode
41 * This would trigger javascript mode
42 * from the second line if first line start with `%%javascript` or `%%jsmagic`
42 * from the second line if first line start with `%%javascript` or `%%jsmagic`
43 *
43 *
44 * cell_magic_highlight['magic_javascript'] = {'reg':[/^%%javascript/,/^%%jsmagic/]}
44 * cell_magic_highlight['magic_javascript'] = {'reg':[/^%%javascript/,/^%%jsmagic/]}
45 * @example
45 * @example
46 * This would trigger javascript mode
46 * This would trigger javascript mode
47 * from the second line if first line start with `var`
47 * from the second line if first line start with `var`
48 *
48 *
49 * cell_magic_highlight['javascript'] = {'reg':[/^var/]}
49 * cell_magic_highlight['javascript'] = {'reg':[/^var/]}
50 */
50 */
51 cell_magic_highlight : {
51 cell_magic_highlight : {
52 'magic_javascript':{'reg':[/^%%javascript/]}
52 'magic_javascript':{'reg':[/^%%javascript/]}
53 ,'magic_perl' :{'reg':[/^%%perl/]}
53 ,'magic_perl' :{'reg':[/^%%perl/]}
54 ,'magic_ruby' :{'reg':[/^%%ruby/]}
54 ,'magic_ruby' :{'reg':[/^%%ruby/]}
55 ,'magic_python' :{'reg':[/^%%python3?/]}
55 ,'magic_python' :{'reg':[/^%%python3?/]}
56 ,'magic_shell' :{'reg':[/^%%bash/]}
56 ,'magic_shell' :{'reg':[/^%%bash/]}
57 ,'magic_r' :{'reg':[/^%%R/]}
57 ,'magic_r' :{'reg':[/^%%R/]}
58 },
58 },
59
59
60 /**
60 /**
61 * same as `cell_magic_highlight` but for raw cells
61 * same as `cell_magic_highlight` but for raw cells
62 * @attribute raw_cell_highlight
62 * @attribute raw_cell_highlight
63 */
63 */
64 raw_cell_highlight : {
64 raw_cell_highlight : {
65 'diff' :{'reg':[/^diff/]}
65 'diff' :{'reg':[/^diff/]}
66 }
66 },
67
68 tooltip_on_tab : true,
67 };
69 };
68
70
69 IPython.config = config;
71 // use the same method to merge user configuration
72 IPython.config = {};
73 $.extend(IPython.config, default_config);
70
74
71 return IPython;
75 return IPython;
72
76
73 }(IPython));
77 }(IPython));
74
78
@@ -1,363 +1,375
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 // Tooltip
8 // Tooltip
9 //============================================================================
9 //============================================================================
10 //
10 //
11 // you can set the autocall time by setting `IPython.tooltip.time_before_tooltip` in ms
11 // you can set the autocall time by setting `IPython.tooltip.time_before_tooltip` in ms
12 //
12 //
13 // you can configure the differents action of pressing tab several times in a row by
13 // you can configure the differents action of pressing tab several times in a row by
14 // setting/appending different fonction in the array
14 // setting/appending different fonction in the array
15 // IPython.tooltip.tabs_functions
15 // IPython.tooltip.tabs_functions
16 //
16 //
17 // eg :
17 // eg :
18 // IPython.tooltip.tabs_functions[4] = function (){console.log('this is the action of the 4th tab pressing')}
18 // IPython.tooltip.tabs_functions[4] = function (){console.log('this is the action of the 4th tab pressing')}
19 //
19 //
20 var IPython = (function (IPython) {
20 var IPython = (function (IPython) {
21 "use strict";
21 "use strict";
22
22
23 var utils = IPython.utils;
23 var utils = IPython.utils;
24
24
25 // tooltip constructor
25 // tooltip constructor
26 var Tooltip = function () {
26 var Tooltip = function () {
27 var that = this;
27 var that = this;
28 this.time_before_tooltip = 1200;
28 this.time_before_tooltip = 1200;
29
29
30 // handle to html
30 // handle to html
31 this.tooltip = $('#tooltip');
31 this.tooltip = $('#tooltip');
32 this._hidden = true;
32 this._hidden = true;
33
33
34 // variable for consecutive call
34 // variable for consecutive call
35 this._old_cell = null;
35 this._old_cell = null;
36 this._old_request = null;
36 this._old_request = null;
37 this._consecutive_counter = 0;
37 this._consecutive_counter = 0;
38
38
39 // 'sticky ?'
39 // 'sticky ?'
40 this._sticky = false;
40 this._sticky = false;
41
41
42 // contain the button in the upper right corner
42 // contain the button in the upper right corner
43 this.buttons = $('<div/>').addClass('tooltipbuttons');
43 this.buttons = $('<div/>').addClass('tooltipbuttons');
44
44
45 // will contain the docstring
45 // will contain the docstring
46 this.text = $('<div/>').addClass('tooltiptext').addClass('smalltooltip');
46 this.text = $('<div/>').addClass('tooltiptext').addClass('smalltooltip');
47
47
48 // build the buttons menu on the upper right
48 // build the buttons menu on the upper right
49 // expand the tooltip to see more
49 // expand the tooltip to see more
50 var expandlink = $('<a/>').attr('href', "#").addClass("ui-corner-all") //rounded corner
50 var expandlink = $('<a/>').attr('href', "#").addClass("ui-corner-all") //rounded corner
51 .attr('role', "button").attr('id', 'expanbutton').attr('title', 'Grow the tooltip vertically (press tab 2 times)').click(function () {
51 .attr('role', "button").attr('id', 'expanbutton').attr('title', 'Grow the tooltip vertically (press tab 2 times)').click(function () {
52 that.expand()
52 that.expand()
53 }).append(
53 }).append(
54 $('<span/>').text('Expand').addClass('ui-icon').addClass('ui-icon-plus'));
54 $('<span/>').text('Expand').addClass('ui-icon').addClass('ui-icon-plus'));
55
55
56 // open in pager
56 // open in pager
57 var morelink = $('<a/>').attr('href', "#").attr('role', "button").addClass('ui-button').attr('title', 'show the current docstring in pager (press tab 4 times)');
57 var morelink = $('<a/>').attr('href', "#").attr('role', "button").addClass('ui-button').attr('title', 'show the current docstring in pager (press tab 4 times)');
58 var morespan = $('<span/>').text('Open in Pager').addClass('ui-icon').addClass('ui-icon-arrowstop-l-n');
58 var morespan = $('<span/>').text('Open in Pager').addClass('ui-icon').addClass('ui-icon-arrowstop-l-n');
59 morelink.append(morespan);
59 morelink.append(morespan);
60 morelink.click(function () {
60 morelink.click(function () {
61 that.showInPager();
61 that.showInPager();
62 });
62 });
63
63
64 // close the tooltip
64 // close the tooltip
65 var closelink = $('<a/>').attr('href', "#").attr('role', "button").addClass('ui-button');
65 var closelink = $('<a/>').attr('href', "#").attr('role', "button").addClass('ui-button');
66 var closespan = $('<span/>').text('Close').addClass('ui-icon').addClass('ui-icon-close');
66 var closespan = $('<span/>').text('Close').addClass('ui-icon').addClass('ui-icon-close');
67 closelink.append(closespan);
67 closelink.append(closespan);
68 closelink.click(function () {
68 closelink.click(function () {
69 that.remove_and_cancel_tooltip(true);
69 that.remove_and_cancel_tooltip(true);
70 });
70 });
71
71
72 this._clocklink = $('<a/>').attr('href', "#");
72 this._clocklink = $('<a/>').attr('href', "#");
73 this._clocklink.attr('role', "button");
73 this._clocklink.attr('role', "button");
74 this._clocklink.addClass('ui-button');
74 this._clocklink.addClass('ui-button');
75 this._clocklink.attr('title', 'Tootip is not dismissed while typing for 10 seconds');
75 this._clocklink.attr('title', 'Tootip is not dismissed while typing for 10 seconds');
76 var clockspan = $('<span/>').text('Close');
76 var clockspan = $('<span/>').text('Close');
77 clockspan.addClass('ui-icon');
77 clockspan.addClass('ui-icon');
78 clockspan.addClass('ui-icon-clock');
78 clockspan.addClass('ui-icon-clock');
79 this._clocklink.append(clockspan);
79 this._clocklink.append(clockspan);
80 this._clocklink.click(function () {
80 this._clocklink.click(function () {
81 that.cancel_stick();
81 that.cancel_stick();
82 });
82 });
83
83
84
84
85
85
86
86
87 //construct the tooltip
87 //construct the tooltip
88 // add in the reverse order you want them to appear
88 // add in the reverse order you want them to appear
89 this.buttons.append(closelink);
89 this.buttons.append(closelink);
90 this.buttons.append(expandlink);
90 this.buttons.append(expandlink);
91 this.buttons.append(morelink);
91 this.buttons.append(morelink);
92 this.buttons.append(this._clocklink);
92 this.buttons.append(this._clocklink);
93 this._clocklink.hide();
93 this._clocklink.hide();
94
94
95
95
96 // we need a phony element to make the small arrow
96 // we need a phony element to make the small arrow
97 // of the tooltip in css
97 // of the tooltip in css
98 // we will move the arrow later
98 // we will move the arrow later
99 this.arrow = $('<div/>').addClass('pretooltiparrow');
99 this.arrow = $('<div/>').addClass('pretooltiparrow');
100 this.tooltip.append(this.buttons);
100 this.tooltip.append(this.buttons);
101 this.tooltip.append(this.arrow);
101 this.tooltip.append(this.arrow);
102 this.tooltip.append(this.text);
102 this.tooltip.append(this.text);
103
103
104 // function that will be called if you press tab 1, 2, 3... times in a row
104 // function that will be called if you press tab 1, 2, 3... times in a row
105 this.tabs_functions = [function (cell, text) {
105 this.tabs_functions = [function (cell, text) {
106 that._request_tooltip(cell, text);
106 that._request_tooltip(cell, text);
107 }, function () {
107 }, function () {
108 that.expand();
108 that.expand();
109 }, function () {
109 }, function () {
110 that.stick();
110 that.stick();
111 }, function (cell) {
111 }, function (cell) {
112 that.cancel_stick();
112 that.cancel_stick();
113 that.showInPager(cell);
113 that.showInPager(cell);
114 that._cmfocus();
114 that._cmfocus();
115 }];
115 }];
116 // call after all the tabs function above have bee call to clean their effects
116 // call after all the tabs function above have bee call to clean their effects
117 // if necessary
117 // if necessary
118 this.reset_tabs_function = function (cell, text) {
118 this.reset_tabs_function = function (cell, text) {
119 this._old_cell = (cell) ? cell : null;
119 this._old_cell = (cell) ? cell : null;
120 this._old_request = (text) ? text : null;
120 this._old_request = (text) ? text : null;
121 this._consecutive_counter = 0;
121 this._consecutive_counter = 0;
122 }
122 }
123 };
123 };
124
124
125 Tooltip.prototype.showInPager = function (cell) {
125 Tooltip.prototype.showInPager = function (cell) {
126 // reexecute last call in pager by appending ? to show back in pager
126 // reexecute last call in pager by appending ? to show back in pager
127 var that = this;
127 var that = this;
128 var empty = function () {};
128 var empty = function () {};
129 IPython.notebook.kernel.execute(
129 IPython.notebook.kernel.execute(
130 that.name + '?', {
130 that.name + '?', {
131 'execute_reply': empty,
131 'execute_reply': empty,
132 'output': empty,
132 'output': empty,
133 'clear_output': empty,
133 'clear_output': empty,
134 'cell': cell
134 'cell': cell
135 }, {
135 }, {
136 'silent': false
136 'silent': false
137 });
137 });
138 this.remove_and_cancel_tooltip();
138 this.remove_and_cancel_tooltip();
139 this._cmfocus();
139 this._cmfocus();
140 }
140 }
141
141
142 // grow the tooltip verticaly
142 // grow the tooltip verticaly
143 Tooltip.prototype.expand = function () {
143 Tooltip.prototype.expand = function () {
144 this.text.removeClass('smalltooltip');
144 this.text.removeClass('smalltooltip');
145 this.text.addClass('bigtooltip');
145 this.text.addClass('bigtooltip');
146 $('#expanbutton').hide('slow');
146 $('#expanbutton').hide('slow');
147 this._cmfocus();
147 this._cmfocus();
148 }
148 }
149
149
150 // deal with all the logic of hiding the tooltip
150 // deal with all the logic of hiding the tooltip
151 // and reset it's status
151 // and reset it's status
152 Tooltip.prototype._hide = function () {
152 Tooltip.prototype._hide = function () {
153 this.tooltip.fadeOut('fast');
153 this.tooltip.fadeOut('fast');
154 $('#expanbutton').show('slow');
154 $('#expanbutton').show('slow');
155 this.text.removeClass('bigtooltip');
155 this.text.removeClass('bigtooltip');
156 this.text.addClass('smalltooltip');
156 this.text.addClass('smalltooltip');
157 // keep scroll top to be sure to always see the first line
157 // keep scroll top to be sure to always see the first line
158 this.text.scrollTop(0);
158 this.text.scrollTop(0);
159 this._hidden = true;
159 this._hidden = true;
160 this.code_mirror = null;
160 this.code_mirror = null;
161 }
161 }
162
162
163 Tooltip.prototype.remove_and_cancel_tooltip = function (force) {
163 Tooltip.prototype.remove_and_cancel_tooltip = function (force) {
164 // note that we don't handle closing directly inside the calltip
164 // note that we don't handle closing directly inside the calltip
165 // as in the completer, because it is not focusable, so won't
165 // as in the completer, because it is not focusable, so won't
166 // get the event.
166 // get the event.
167 if (this._sticky == false || force == true) {
167 if (this._sticky == false || force == true) {
168 this.cancel_stick();
168 this.cancel_stick();
169 this._hide();
169 this._hide();
170 }
170 }
171 this.cancel_pending();
171 this.cancel_pending();
172 this.reset_tabs_function();
172 this.reset_tabs_function();
173 this._cmfocus();
173 this._cmfocus();
174 }
174 }
175
175
176 // cancel autocall done after '(' for example.
176 // cancel autocall done after '(' for example.
177 Tooltip.prototype.cancel_pending = function () {
177 Tooltip.prototype.cancel_pending = function () {
178 if (this._tooltip_timeout != null) {
178 if (this._tooltip_timeout != null) {
179 clearTimeout(this._tooltip_timeout);
179 clearTimeout(this._tooltip_timeout);
180 this._tooltip_timeout = null;
180 this._tooltip_timeout = null;
181 }
181 }
182 }
182 }
183
183
184 // will trigger tooltip after timeout
184 // will trigger tooltip after timeout
185 Tooltip.prototype.pending = function (cell) {
185 Tooltip.prototype.pending = function (cell) {
186 var that = this;
186 var that = this;
187 this._tooltip_timeout = setTimeout(function () {
187 this._tooltip_timeout = setTimeout(function () {
188 that.request(cell)
188 that.request(cell)
189 }, that.time_before_tooltip);
189 }, that.time_before_tooltip);
190 }
190 }
191
191
192 Tooltip.prototype._request_tooltip = function (cell, func) {
192 Tooltip.prototype._request_tooltip = function (cell, func) {
193 // use internally just to make the request to the kernel
193 // use internally just to make the request to the kernel
194 // Feel free to shorten this logic if you are better
194 // Feel free to shorten this logic if you are better
195 // than me in regEx
195 // than me in regEx
196 // basicaly you shoul be able to get xxx.xxx.xxx from
196 // basicaly you shoul be able to get xxx.xxx.xxx from
197 // something(range(10), kwarg=smth) ; xxx.xxx.xxx( firstarg, rand(234,23), kwarg1=2,
197 // something(range(10), kwarg=smth) ; xxx.xxx.xxx( firstarg, rand(234,23), kwarg1=2,
198 // remove everything between matchin bracket (need to iterate)
198 // remove everything between matchin bracket (need to iterate)
199 var matchBracket = /\([^\(\)]+\)/g;
199 var matchBracket = /\([^\(\)]+\)/g;
200 var endBracket = /\([^\(]*$/g;
200 var endBracket = /\([^\(]*$/g;
201 var oldfunc = func;
201 var oldfunc = func;
202
202
203 func = func.replace(matchBracket, "");
203 func = func.replace(matchBracket, "");
204 while (oldfunc != func) {
204 while (oldfunc != func) {
205 oldfunc = func;
205 oldfunc = func;
206 func = func.replace(matchBracket, "");
206 func = func.replace(matchBracket, "");
207 }
207 }
208 // remove everything after last open bracket
208 // remove everything after last open bracket
209 func = func.replace(endBracket, "");
209 func = func.replace(endBracket, "");
210
210
211 var re = /[a-z_][0-9a-z._]+$/gi; // casse insensitive
211 var re = /[a-z_][0-9a-z._]+$/gi; // casse insensitive
212 var callbacks = {
212 var callbacks = {
213 'object_info_reply': $.proxy(this._show, this)
213 'object_info_reply': $.proxy(this._show, this)
214 }
214 }
215 var msg_id = IPython.notebook.kernel.object_info_request(re.exec(func), callbacks);
215 var msg_id = IPython.notebook.kernel.object_info_request(re.exec(func), callbacks);
216 }
216 }
217
217
218 // make an imediate completion request
218 // make an imediate completion request
219 Tooltip.prototype.request = function (cell) {
219 Tooltip.prototype.request = function (cell) {
220 // request(codecell)
220 // request(codecell)
221 // Deal with extracting the text from the cell and counting
221 // Deal with extracting the text from the cell and counting
222 // call in a row
222 // call in a row
223 this.cancel_pending();
223 this.cancel_pending();
224 var editor = cell.code_mirror;
224 var editor = cell.code_mirror;
225 var cursor = editor.getCursor();
225 var cursor = editor.getCursor();
226 var text = editor.getRange({
226 var text = editor.getRange({
227 line: cursor.line,
227 line: cursor.line,
228 ch: 0
228 ch: 0
229 }, cursor).trim();
229 }, cursor).trim();
230
230
231 if(editor.somethingSelected()){
232 text = editor.getSelection();
233 }
234
231 // need a permanent handel to code_mirror for future auto recall
235 // need a permanent handel to code_mirror for future auto recall
232 this.code_mirror = editor;
236 this.code_mirror = editor;
233
237
234 // now we treat the different number of keypress
238 // now we treat the different number of keypress
235 // first if same cell, same text, increment counter by 1
239 // first if same cell, same text, increment counter by 1
236 if (this._old_cell == cell && this._old_request == text && this._hidden == false) {
240 if (this._old_cell == cell && this._old_request == text && this._hidden == false) {
237 this._consecutive_counter++;
241 this._consecutive_counter++;
238 } else {
242 } else {
239 // else reset
243 // else reset
240 this.cancel_stick();
244 this.cancel_stick();
241 this.reset_tabs_function (cell, text);
245 this.reset_tabs_function (cell, text);
242 }
246 }
243
247
244 // don't do anything if line beggin with '(' or is empty
248 // don't do anything if line beggin with '(' or is empty
245 if (text === "" || text === "(") {
249 if (text === "" || text === "(") {
246 return;
250 return;
247 }
251 }
248
252
249 this.tabs_functions[this._consecutive_counter](cell, text);
253 this.tabs_functions[this._consecutive_counter](cell, text);
250
254
251 // then if we are at the end of list function, reset
255 // then if we are at the end of list function, reset
252 if (this._consecutive_counter == this.tabs_functions.length) this.reset_tabs_function (cell, text);
256 if (this._consecutive_counter == this.tabs_functions.length) this.reset_tabs_function (cell, text);
253
257
254 return;
258 return;
255 }
259 }
256
260
257 // cancel the option of having the tooltip to stick
261 // cancel the option of having the tooltip to stick
258 Tooltip.prototype.cancel_stick = function () {
262 Tooltip.prototype.cancel_stick = function () {
259 clearTimeout(this._stick_timeout);
263 clearTimeout(this._stick_timeout);
260 this._stick_timeout = null;
264 this._stick_timeout = null;
261 this._clocklink.hide('slow');
265 this._clocklink.hide('slow');
262 this._sticky = false;
266 this._sticky = false;
263 }
267 }
264
268
265 // put the tooltip in a sicky state for 10 seconds
269 // put the tooltip in a sicky state for 10 seconds
266 // it won't be removed by remove_and_cancell() unless you called with
270 // it won't be removed by remove_and_cancell() unless you called with
267 // the first parameter set to true.
271 // the first parameter set to true.
268 // remove_and_cancell_tooltip(true)
272 // remove_and_cancell_tooltip(true)
269 Tooltip.prototype.stick = function (time) {
273 Tooltip.prototype.stick = function (time) {
270 time = (time != undefined) ? time : 10;
274 time = (time != undefined) ? time : 10;
271 var that = this;
275 var that = this;
272 this._sticky = true;
276 this._sticky = true;
273 this._clocklink.show('slow');
277 this._clocklink.show('slow');
274 this._stick_timeout = setTimeout(function () {
278 this._stick_timeout = setTimeout(function () {
275 that._sticky = false;
279 that._sticky = false;
276 that._clocklink.hide('slow');
280 that._clocklink.hide('slow');
277 }, time * 1000);
281 }, time * 1000);
278 }
282 }
279
283
280 // should be called with the kernel reply to actually show the tooltip
284 // should be called with the kernel reply to actually show the tooltip
281 Tooltip.prototype._show = function (reply) {
285 Tooltip.prototype._show = function (reply) {
282 // move the bubble if it is not hidden
286 // move the bubble if it is not hidden
283 // otherwise fade it
287 // otherwise fade it
284 this.name = reply.name;
288 this.name = reply.name;
285
289
286 // do some math to have the tooltip arrow on more or less on left or right
290 // do some math to have the tooltip arrow on more or less on left or right
287 // width of the editor
291 // width of the editor
288 var w = $(this.code_mirror.getScrollerElement()).width();
292 var w = $(this.code_mirror.getScrollerElement()).width();
289 // ofset of the editor
293 // ofset of the editor
290 var o = $(this.code_mirror.getScrollerElement()).offset();
294 var o = $(this.code_mirror.getScrollerElement()).offset();
291 var pos = this.code_mirror.cursorCoords();
295
296 // whatever anchor/head order but arrow at mid x selection
297 var anchor = this.code_mirror.cursorCoords(false);
298 var head = this.code_mirror.cursorCoords(true);
299 var pos = {};
300 pos.y = head.y
301 pos.yBot = head.yBot
302 pos.x = (head.x+anchor.x)/2;
303
292 var xinit = pos.x;
304 var xinit = pos.x;
293 var xinter = o.left + (xinit - o.left) / w * (w - 450);
305 var xinter = o.left + (xinit - o.left) / w * (w - 450);
294 var posarrowleft = xinit - xinter;
306 var posarrowleft = xinit - xinter;
295
307
296
308
297 if (this._hidden == false) {
309 if (this._hidden == false) {
298 this.tooltip.animate({
310 this.tooltip.animate({
299 'left': xinter - 30 + 'px',
311 'left': xinter - 30 + 'px',
300 'top': (pos.yBot + 10) + 'px'
312 'top': (pos.yBot + 10) + 'px'
301 });
313 });
302 } else {
314 } else {
303 this.tooltip.css({
315 this.tooltip.css({
304 'left': xinter - 30 + 'px'
316 'left': xinter - 30 + 'px'
305 });
317 });
306 this.tooltip.css({
318 this.tooltip.css({
307 'top': (pos.yBot + 10) + 'px'
319 'top': (pos.yBot + 10) + 'px'
308 });
320 });
309 }
321 }
310 this.arrow.animate({
322 this.arrow.animate({
311 'left': posarrowleft + 'px'
323 'left': posarrowleft + 'px'
312 });
324 });
313 this.tooltip.fadeIn('fast');
325 this.tooltip.fadeIn('fast');
314 this._hidden = false;
326 this._hidden = false;
315
327
316 // build docstring
328 // build docstring
317 var defstring = reply.call_def;
329 var defstring = reply.call_def;
318 if (defstring == null) {
330 if (defstring == null) {
319 defstring = reply.init_definition;
331 defstring = reply.init_definition;
320 }
332 }
321 if (defstring == null) {
333 if (defstring == null) {
322 defstring = reply.definition;
334 defstring = reply.definition;
323 }
335 }
324
336
325 var docstring = reply.call_docstring;
337 var docstring = reply.call_docstring;
326 if (docstring == null) {
338 if (docstring == null) {
327 docstring = reply.init_docstring;
339 docstring = reply.init_docstring;
328 }
340 }
329 if (docstring == null) {
341 if (docstring == null) {
330 docstring = reply.docstring;
342 docstring = reply.docstring;
331 }
343 }
332 if (docstring == null) {
344 if (docstring == null) {
333 docstring = "<empty docstring>";
345 docstring = "<empty docstring>";
334 }
346 }
335
347
336 this.text.children().remove();
348 this.text.children().remove();
337
349
338 var pre = $('<pre/>').html(utils.fixConsole(docstring));
350 var pre = $('<pre/>').html(utils.fixConsole(docstring));
339 if (defstring) {
351 if (defstring) {
340 var defstring_html = $('<pre/>').html(utils.fixConsole(defstring));
352 var defstring_html = $('<pre/>').html(utils.fixConsole(defstring));
341 this.text.append(defstring_html);
353 this.text.append(defstring_html);
342 }
354 }
343 this.text.append(pre);
355 this.text.append(pre);
344 // keep scroll top to be sure to always see the first line
356 // keep scroll top to be sure to always see the first line
345 this.text.scrollTop(0);
357 this.text.scrollTop(0);
346 }
358 }
347
359
348 // convenient funciton to have the correct code_mirror back into focus
360 // convenient funciton to have the correct code_mirror back into focus
349 Tooltip.prototype._cmfocus = function () {
361 Tooltip.prototype._cmfocus = function () {
350 var cm = this.code_mirror;
362 var cm = this.code_mirror;
351 if (cm == IPython.notebook.get_selected_cell())
363 if (cm == IPython.notebook.get_selected_cell())
352 {
364 {
353 setTimeout(function () {
365 setTimeout(function () {
354 cm.focus();
366 cm.focus();
355 }, 50);
367 }, 50);
356 }
368 }
357 }
369 }
358
370
359 IPython.Tooltip = Tooltip;
371 IPython.Tooltip = Tooltip;
360
372
361 return IPython;
373 return IPython;
362
374
363 }(IPython));
375 }(IPython));
General Comments 0
You need to be logged in to leave comments. Login now