##// END OF EJS Templates
make sticky time configurable
Matthias BUSSONNIER -
Show More
@@ -1,371 +1,372 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 // Tooltip
9 // Tooltip
10 //============================================================================
10 //============================================================================
11 //
11 //
12 // you can set the autocall time by setting `IPython.tooltip.time_before_tooltip` in ms
12 // you can set the autocall time by setting `IPython.tooltip.time_before_tooltip` in ms
13 //
13 //
14 // you can configure the differents action of pressing tab several times in a row by
14 // you can configure the differents action of pressing tab several times in a row by
15 // setting/appending different fonction in the array
15 // setting/appending different fonction in the array
16 // IPython.tooltip.tabs_functions
16 // IPython.tooltip.tabs_functions
17 //
17 //
18 // eg :
18 // eg :
19 // IPython.tooltip.tabs_functions[4] = function(){console.log('this is the action of the 4th tab pressing')}
19 // IPython.tooltip.tabs_functions[4] = function(){console.log('this is the action of the 4th tab pressing')}
20 //
20 //
21
21
22 var IPython = (function (IPython) {
22 var IPython = (function (IPython) {
23 "use strict";
23 "use strict";
24
24
25 var utils = IPython.utils;
25 var utils = IPython.utils;
26
26
27 // tooltip constructor
27 // tooltip constructor
28 var Tooltip = function () {
28 var Tooltip = function () {
29 var that = this;
29 var that = this;
30 this.time_before_tooltip = 1200;
30 this.time_before_tooltip = 1200;
31
31
32 // handle to html
32 // handle to html
33 this.tooltip = $('#tooltip');
33 this.tooltip = $('#tooltip');
34 this._hidden = true;
34 this._hidden = true;
35
35
36 // variable for consecutive call
36 // variable for consecutive call
37 this._old_cell = null ;
37 this._old_cell = null ;
38 this._old_request = null ;
38 this._old_request = null ;
39 this._consecutive_counter = 0;
39 this._consecutive_counter = 0;
40
40
41 // 'sticky ?'
41 // 'sticky ?'
42 this._sticky = false;
42 this._sticky = false;
43
43
44 // contain the button in the upper right corner
44 // contain the button in the upper right corner
45 this.buttons = $('<div/>')
45 this.buttons = $('<div/>')
46 .addClass('tooltipbuttons');
46 .addClass('tooltipbuttons');
47
47
48 // will contain the docstring
48 // will contain the docstring
49 this.text = $('<div/>')
49 this.text = $('<div/>')
50 .addClass('tooltiptext')
50 .addClass('tooltiptext')
51 .addClass('smalltooltip');
51 .addClass('smalltooltip');
52
52
53 // build the buttons menu on the upper right
53 // build the buttons menu on the upper right
54
54
55 // expand the tooltip to see more
55 // expand the tooltip to see more
56 var expandlink=$('<a/>').attr('href',"#")
56 var expandlink=$('<a/>').attr('href',"#")
57 .addClass("ui-corner-all") //rounded corner
57 .addClass("ui-corner-all") //rounded corner
58 .attr('role',"button")
58 .attr('role',"button")
59 .attr('id','expanbutton')
59 .attr('id','expanbutton')
60 .attr('title','Grow the tooltip vertically (press tab 2 times)')
60 .attr('title','Grow the tooltip vertically (press tab 2 times)')
61 .click(function(){that.expand()})
61 .click(function(){that.expand()})
62 .append(
62 .append(
63 $('<span/>').text('Expand')
63 $('<span/>').text('Expand')
64 .addClass('ui-icon')
64 .addClass('ui-icon')
65 .addClass('ui-icon-plus')
65 .addClass('ui-icon-plus')
66 );
66 );
67
67
68 // open in pager
68 // open in pager
69 var morelink=$('<a/>').attr('href',"#")
69 var morelink=$('<a/>').attr('href',"#")
70 .attr('role',"button")
70 .attr('role',"button")
71 .addClass('ui-button')
71 .addClass('ui-button')
72 .attr('title','show the current docstring in pager (press tab 4 times)');
72 .attr('title','show the current docstring in pager (press tab 4 times)');
73 var morespan=$('<span/>').text('Open in Pager')
73 var morespan=$('<span/>').text('Open in Pager')
74 .addClass('ui-icon')
74 .addClass('ui-icon')
75 .addClass('ui-icon-arrowstop-l-n');
75 .addClass('ui-icon-arrowstop-l-n');
76 morelink.append(morespan);
76 morelink.append(morespan);
77 morelink.click(function(){
77 morelink.click(function(){
78 that.showInPager();
78 that.showInPager();
79 });
79 });
80
80
81 // close the tooltip
81 // close the tooltip
82 var closelink=$('<a/>').attr('href',"#")
82 var closelink=$('<a/>').attr('href',"#")
83 .attr('role',"button")
83 .attr('role',"button")
84 .addClass('ui-button');
84 .addClass('ui-button');
85 var closespan=$('<span/>').text('Close')
85 var closespan=$('<span/>').text('Close')
86 .addClass('ui-icon')
86 .addClass('ui-icon')
87 .addClass('ui-icon-close');
87 .addClass('ui-icon-close');
88 closelink.append(closespan);
88 closelink.append(closespan);
89 closelink.click(function(){
89 closelink.click(function(){
90 that.remove_and_cancel_tooltip(true);
90 that.remove_and_cancel_tooltip(true);
91 });
91 });
92
92
93 this._clocklink=$('<a/>').attr('href',"#");
93 this._clocklink=$('<a/>').attr('href',"#");
94 this._clocklink.attr('role',"button");
94 this._clocklink.attr('role',"button");
95 this._clocklink.addClass('ui-button');
95 this._clocklink.addClass('ui-button');
96 this._clocklink.attr('title','Tootip is not dismissed while typing for 10 seconds');
96 this._clocklink.attr('title','Tootip is not dismissed while typing for 10 seconds');
97 var clockspan=$('<span/>').text('Close');
97 var clockspan=$('<span/>').text('Close');
98 clockspan.addClass('ui-icon');
98 clockspan.addClass('ui-icon');
99 clockspan.addClass('ui-icon-clock');
99 clockspan.addClass('ui-icon-clock');
100 this._clocklink.append(clockspan);
100 this._clocklink.append(clockspan);
101 this._clocklink.click(function(){
101 this._clocklink.click(function(){
102 that.cancel_stick();
102 that.cancel_stick();
103 });
103 });
104
104
105
105
106
106
107
107
108 //construct the tooltip
108 //construct the tooltip
109 // add in the reverse order you want them to appear
109 // add in the reverse order you want them to appear
110 this.buttons.append(closelink);
110 this.buttons.append(closelink);
111 this.buttons.append(expandlink);
111 this.buttons.append(expandlink);
112 this.buttons.append(morelink);
112 this.buttons.append(morelink);
113 this.buttons.append(this._clocklink);
113 this.buttons.append(this._clocklink);
114 this._clocklink.hide();
114 this._clocklink.hide();
115
115
116
116
117 // we need a phony element to make the small arrow
117 // we need a phony element to make the small arrow
118 // of the tooltip in css
118 // of the tooltip in css
119 // we will move the arrow later
119 // we will move the arrow later
120 this.arrow = $('<div/>').addClass('pretooltiparrow');
120 this.arrow = $('<div/>').addClass('pretooltiparrow');
121 this.tooltip.append(this.buttons);
121 this.tooltip.append(this.buttons);
122 this.tooltip.append(this.arrow);
122 this.tooltip.append(this.arrow);
123 this.tooltip.append(this.text);
123 this.tooltip.append(this.text);
124
124
125 // function that will be called if you press tab 1, 2, 3... times in a row
125 // function that will be called if you press tab 1, 2, 3... times in a row
126 this.tabs_functions = [ function(cell,text){
126 this.tabs_functions = [ function(cell,text){
127 that._request_tooltip(cell,text);
127 that._request_tooltip(cell,text);
128 IPython.notification_widget.set_message('tab again to expand pager',2500);
128 IPython.notification_widget.set_message('tab again to expand pager',2500);
129 },
129 },
130 function(){
130 function(){
131 that.expand();
131 that.expand();
132 IPython.notification_widget.set_message('tab again to make pager sticky for 10s',2500);
132 IPython.notification_widget.set_message('tab again to make pager sticky for 10s',2500);
133 },
133 },
134 function(){
134 function(){
135 that.stick();
135 that.stick();
136 IPython.notification_widget.set_message('tab again to open help in pager',2500);
136 IPython.notification_widget.set_message('tab again to open help in pager',2500);
137 },
137 },
138 function(cell){
138 function(cell){
139 that.cancel_stick();
139 that.cancel_stick();
140 that.showInPager(cell);
140 that.showInPager(cell);
141 that._cmfocus();
141 that._cmfocus();
142 }
142 }
143 ];
143 ];
144 // call after all the tabs function above have bee call to clean their effects
144 // call after all the tabs function above have bee call to clean their effects
145 // if necessary
145 // if necessary
146 this.reset_tabs_function = function(cell,text){
146 this.reset_tabs_function = function(cell,text){
147 this._old_cell = (cell)? cell : null ;
147 this._old_cell = (cell)? cell : null ;
148 this._old_request = (text) ? text : null ;
148 this._old_request = (text) ? text : null ;
149 this._consecutive_counter = 0;
149 this._consecutive_counter = 0;
150 }
150 }
151 };
151 };
152
152
153 Tooltip.prototype.showInPager = function(cell)
153 Tooltip.prototype.showInPager = function(cell)
154 {
154 {
155 // reexecute last call in pager by appending ? to show back in pager
155 // reexecute last call in pager by appending ? to show back in pager
156 var that = this;
156 var that = this;
157 var empty = function(){};
157 var empty = function(){};
158 IPython.notebook.kernel.execute(
158 IPython.notebook.kernel.execute(
159 that.name+'?',
159 that.name+'?',
160 {'execute_reply':empty,'output':empty,'clear_output':empty,'cell':cell},
160 {'execute_reply':empty,'output':empty,'clear_output':empty,'cell':cell},
161 {'silent':false}
161 {'silent':false}
162 );
162 );
163 this.remove_and_cancel_tooltip();
163 this.remove_and_cancel_tooltip();
164 this._cmfocus();
164 this._cmfocus();
165 }
165 }
166
166
167 // grow the tooltip verticaly
167 // grow the tooltip verticaly
168 Tooltip.prototype.expand = function(){
168 Tooltip.prototype.expand = function(){
169 this.text.removeClass('smalltooltip');
169 this.text.removeClass('smalltooltip');
170 this.text.addClass('bigtooltip');
170 this.text.addClass('bigtooltip');
171 $('#expanbutton').hide('slow');
171 $('#expanbutton').hide('slow');
172 this._cmfocus();
172 this._cmfocus();
173 }
173 }
174
174
175 // deal with all the logic of hiding the tooltip
175 // deal with all the logic of hiding the tooltip
176 // and reset it's status
176 // and reset it's status
177 Tooltip.prototype.hide = function()
177 Tooltip.prototype.hide = function()
178 {
178 {
179 this.tooltip.addClass('hide');
179 this.tooltip.addClass('hide');
180 $('#expanbutton').show('slow');
180 $('#expanbutton').show('slow');
181 this.text.removeClass('bigtooltip');
181 this.text.removeClass('bigtooltip');
182 this.text.addClass('smalltooltip');
182 this.text.addClass('smalltooltip');
183 // keep scroll top to be sure to always see the first line
183 // keep scroll top to be sure to always see the first line
184 this.text.scrollTop(0);
184 this.text.scrollTop(0);
185 this._hidden = true;
185 this._hidden = true;
186 }
186 }
187
187
188 Tooltip.prototype.remove_and_cancel_tooltip = function(force) {
188 Tooltip.prototype.remove_and_cancel_tooltip = function(force) {
189 // note that we don't handle closing directly inside the calltip
189 // note that we don't handle closing directly inside the calltip
190 // as in the completer, because it is not focusable, so won't
190 // as in the completer, because it is not focusable, so won't
191 // get the event.
191 // get the event.
192 if(this._sticky == false || force == true)
192 if(this._sticky == false || force == true)
193 {
193 {
194 this.hide();
194 this.hide();
195 }
195 }
196 this.cancel_pending();
196 this.cancel_pending();
197 this.reset_tabs_function();
197 this.reset_tabs_function();
198 }
198 }
199
199
200 // cancel autocall done after '(' for example.
200 // cancel autocall done after '(' for example.
201 Tooltip.prototype.cancel_pending = function(){
201 Tooltip.prototype.cancel_pending = function(){
202 if (this._tooltip_timeout != null){
202 if (this._tooltip_timeout != null){
203 clearTimeout(this._tooltip_timeout);
203 clearTimeout(this._tooltip_timeout);
204 this._tooltip_timeout = null;
204 this._tooltip_timeout = null;
205 }
205 }
206 }
206 }
207
207
208 // will trigger tooltip after timeout
208 // will trigger tooltip after timeout
209 Tooltip.prototype.pending = function(cell)
209 Tooltip.prototype.pending = function(cell)
210 {
210 {
211 var that = this;
211 var that = this;
212 this._tooltip_timeout = setTimeout(function(){that.request(cell)} , that.time_before_tooltip);
212 this._tooltip_timeout = setTimeout(function(){that.request(cell)} , that.time_before_tooltip);
213 }
213 }
214
214
215 Tooltip.prototype._request_tooltip = function(cell,func)
215 Tooltip.prototype._request_tooltip = function(cell,func)
216 {
216 {
217 // use internally just to make the request to the kernel
217 // use internally just to make the request to the kernel
218
218
219 // Feel free to shorten this logic if you are better
219 // Feel free to shorten this logic if you are better
220 // than me in regEx
220 // than me in regEx
221 // basicaly you shoul be able to get xxx.xxx.xxx from
221 // basicaly you shoul be able to get xxx.xxx.xxx from
222 // something(range(10), kwarg=smth) ; xxx.xxx.xxx( firstarg, rand(234,23), kwarg1=2,
222 // something(range(10), kwarg=smth) ; xxx.xxx.xxx( firstarg, rand(234,23), kwarg1=2,
223 // remove everything between matchin bracket (need to iterate)
223 // remove everything between matchin bracket (need to iterate)
224 var matchBracket = /\([^\(\)]+\)/g;
224 var matchBracket = /\([^\(\)]+\)/g;
225 var endBracket = /\([^\(]*$/g;
225 var endBracket = /\([^\(]*$/g;
226 var oldfunc = func;
226 var oldfunc = func;
227
227
228 func = func.replace(matchBracket,"");
228 func = func.replace(matchBracket,"");
229 while( oldfunc != func )
229 while( oldfunc != func )
230 {
230 {
231 oldfunc = func;
231 oldfunc = func;
232 func = func.replace(matchBracket,"");
232 func = func.replace(matchBracket,"");
233 }
233 }
234 // remove everything after last open bracket
234 // remove everything after last open bracket
235 func = func.replace(endBracket,"");
235 func = func.replace(endBracket,"");
236
236
237 var re = /[a-z_][0-9a-z._]+$/gi; // casse insensitive
237 var re = /[a-z_][0-9a-z._]+$/gi; // casse insensitive
238 var callbacks = {'object_info_reply': $.proxy(this.show,this)}
238 var callbacks = {'object_info_reply': $.proxy(this.show,this)}
239 var msg_id = IPython.notebook.kernel.object_info_request(re.exec(func), callbacks);
239 var msg_id = IPython.notebook.kernel.object_info_request(re.exec(func), callbacks);
240 }
240 }
241
241
242 // make an imediate completion request
242 // make an imediate completion request
243 Tooltip.prototype.request = function(cell)
243 Tooltip.prototype.request = function(cell)
244 {
244 {
245 // request(codecell)
245 // request(codecell)
246 // Deal with extracting the text from the cell and counting
246 // Deal with extracting the text from the cell and counting
247 // call in a row
247 // call in a row
248
248
249 this.cancel_pending();
249 this.cancel_pending();
250 var editor = cell.code_mirror;
250 var editor = cell.code_mirror;
251 var cursor = editor.getCursor();
251 var cursor = editor.getCursor();
252 var text = editor.getRange({line:cursor.line,ch:0},cursor).trim();
252 var text = editor.getRange({line:cursor.line,ch:0},cursor).trim();
253
253
254 // need a permanent handel to codemirror for future auto recall
254 // need a permanent handel to codemirror for future auto recall
255 this.code_mirror = editor;
255 this.code_mirror = editor;
256
256
257 // now we treat the different number of keypress
257 // now we treat the different number of keypress
258 // first if same cell, same text, increment counter by 1
258 // first if same cell, same text, increment counter by 1
259 if( this._old_cell == cell && this._old_request == text && this._hidden == false)
259 if( this._old_cell == cell && this._old_request == text && this._hidden == false)
260 {
260 {
261 this._consecutive_counter++;
261 this._consecutive_counter++;
262 } else {
262 } else {
263 // else reset
263 // else reset
264 this.cancel_stick();
264 this.cancel_stick();
265 this.reset_tabs_function(cell,text);
265 this.reset_tabs_function(cell,text);
266 }
266 }
267
267
268 // don't do anything if line beggin with '(' or is empty
268 // don't do anything if line beggin with '(' or is empty
269 if (text === "" || text === "(" ) {
269 if (text === "" || text === "(" ) {
270 return;
270 return;
271 }
271 }
272
272
273 this.tabs_functions[this._consecutive_counter](cell,text);
273 this.tabs_functions[this._consecutive_counter](cell,text);
274
274
275 // then if we are at the end of list function, reset
275 // then if we are at the end of list function, reset
276 if (this._consecutive_counter == this.tabs_functions.length)
276 if (this._consecutive_counter == this.tabs_functions.length)
277 this.reset_tabs_function(cell,text);
277 this.reset_tabs_function(cell,text);
278
278
279 return;
279 return;
280 }
280 }
281
281
282 // cancel the option of having the tooltip to stick
282 // cancel the option of having the tooltip to stick
283 Tooltip.prototype.cancel_stick = function()
283 Tooltip.prototype.cancel_stick = function()
284 {
284 {
285 clearTimeout(this._stick_timeout);
285 clearTimeout(this._stick_timeout);
286 this._stick_timeout = null;
286 this._stick_timeout = null;
287 this._clocklink.hide('slow');
287 this._clocklink.hide('slow');
288 this._sticky = false;
288 this._sticky = false;
289 }
289 }
290
290
291 // put the tooltip in a sicky state for 10 seconds
291 // put the tooltip in a sicky state for 10 seconds
292 // it won't be removed by remove_and_cancell() unless you called with
292 // it won't be removed by remove_and_cancell() unless you called with
293 // the first parameter set to true.
293 // the first parameter set to true.
294 // remove_and_cancell_tooltip(true)
294 // remove_and_cancell_tooltip(true)
295 Tooltip.prototype.stick = function()
295 Tooltip.prototype.stick = function(time)
296 {
296 {
297 time = (time != undefined ) ? time:10;
297 var that = this;
298 var that = this;
298 this._sticky = true;
299 this._sticky = true;
299 this._clocklink.show('slow');
300 this._clocklink.show('slow');
300 this._stick_timeout = setTimeout( function(){
301 this._stick_timeout = setTimeout( function(){
301 that._sticky = false;
302 that._sticky = false;
302 that._clocklink.hide('slow');
303 that._clocklink.hide('slow');
303 }, 10*1000
304 }, time*1000
304 );
305 );
305 }
306 }
306
307
307 // should be called with the kernel reply to actually show the tooltip
308 // should be called with the kernel reply to actually show the tooltip
308 Tooltip.prototype.show = function(reply)
309 Tooltip.prototype.show = function(reply)
309 {
310 {
310 // move the bubble if it is not hidden
311 // move the bubble if it is not hidden
311 // otherwise fade it
312 // otherwise fade it
312 this.name = reply.name;
313 this.name = reply.name;
313
314
314 // do some math to have the tooltip arrow on more or less on left or right
315 // do some math to have the tooltip arrow on more or less on left or right
315 // width of the editor
316 // width of the editor
316 var w = $(this.code_mirror.getScrollerElement()).width();
317 var w = $(this.code_mirror.getScrollerElement()).width();
317 // ofset of the editor
318 // ofset of the editor
318 var o = $(this.code_mirror.getScrollerElement()).offset();
319 var o = $(this.code_mirror.getScrollerElement()).offset();
319 var pos = this.code_mirror.cursorCoords();
320 var pos = this.code_mirror.cursorCoords();
320 var xinit = pos.x;
321 var xinit = pos.x;
321 var xinter = o.left + (xinit-o.left)/w*(w-450);
322 var xinter = o.left + (xinit-o.left)/w*(w-450);
322 var posarrowleft = xinit - xinter;
323 var posarrowleft = xinit - xinter;
323
324
324
325
325 if( this._hidden == false)
326 if( this._hidden == false)
326 {
327 {
327 this.tooltip.animate({'left' : xinter-30+'px','top' :(pos.yBot+10)+'px'});
328 this.tooltip.animate({'left' : xinter-30+'px','top' :(pos.yBot+10)+'px'});
328 } else
329 } else
329 {
330 {
330 this.tooltip.css({'left' : xinter-30+'px'});
331 this.tooltip.css({'left' : xinter-30+'px'});
331 this.tooltip.css({'top' :(pos.yBot+10)+'px'});
332 this.tooltip.css({'top' :(pos.yBot+10)+'px'});
332 }
333 }
333 this.arrow.animate({'left' : posarrowleft+'px'});
334 this.arrow.animate({'left' : posarrowleft+'px'});
334 this.tooltip.removeClass('hidden')
335 this.tooltip.removeClass('hidden')
335 this.tooltip.removeClass('hide');
336 this.tooltip.removeClass('hide');
336 this._hidden = false;
337 this._hidden = false;
337
338
338 // build docstring
339 // build docstring
339 var defstring = reply.call_def;
340 var defstring = reply.call_def;
340 if (defstring == null) { defstring = reply.init_definition; }
341 if (defstring == null) { defstring = reply.init_definition; }
341 if (defstring == null) { defstring = reply.definition; }
342 if (defstring == null) { defstring = reply.definition; }
342
343
343 var docstring = reply.call_docstring;
344 var docstring = reply.call_docstring;
344 if (docstring == null) { docstring = reply.init_docstring; }
345 if (docstring == null) { docstring = reply.init_docstring; }
345 if (docstring == null) { docstring = reply.docstring; }
346 if (docstring == null) { docstring = reply.docstring; }
346 if (docstring == null) { docstring = "<empty docstring>"; }
347 if (docstring == null) { docstring = "<empty docstring>"; }
347
348
348 this.text.children().remove();
349 this.text.children().remove();
349
350
350 var pre=$('<pre/>').html(utils.fixConsole(docstring));
351 var pre=$('<pre/>').html(utils.fixConsole(docstring));
351 if(defstring){
352 if(defstring){
352 var defstring_html = $('<pre/>').html(utils.fixConsole(defstring));
353 var defstring_html = $('<pre/>').html(utils.fixConsole(defstring));
353 this.text.append(defstring_html);
354 this.text.append(defstring_html);
354 }
355 }
355 this.text.append(pre);
356 this.text.append(pre);
356 // keep scroll top to be sure to always see the first line
357 // keep scroll top to be sure to always see the first line
357 this.text.scrollTop(0);
358 this.text.scrollTop(0);
358 }
359 }
359
360
360 // convenient funciton to have the correct codemirror back into focus
361 // convenient funciton to have the correct codemirror back into focus
361 Tooltip.prototype._cmfocus = function()
362 Tooltip.prototype._cmfocus = function()
362 {
363 {
363 var cm = this.code_mirror;
364 var cm = this.code_mirror;
364 setTimeout(function(){cm.focus();}, 50);
365 setTimeout(function(){cm.focus();}, 50);
365 }
366 }
366
367
367 IPython.Tooltip = Tooltip;
368 IPython.Tooltip = Tooltip;
368
369
369 return IPython;
370 return IPython;
370
371
371 }(IPython));
372 }(IPython));
General Comments 0
You need to be logged in to leave comments. Login now