##// END OF EJS Templates
update description of sticky tooltip...
Min RK -
Show More
@@ -1,326 +1,326
1 // Copyright (c) IPython Development Team.
1 // Copyright (c) IPython Development Team.
2 // Distributed under the terms of the Modified BSD License.
2 // Distributed under the terms of the Modified BSD License.
3
3
4 define([
4 define([
5 'base/js/namespace',
5 'base/js/namespace',
6 'jquery',
6 'jquery',
7 'base/js/utils',
7 'base/js/utils',
8 ], function(IPython, $, utils) {
8 ], function(IPython, $, utils) {
9 "use strict";
9 "use strict";
10
10
11 // tooltip constructor
11 // tooltip constructor
12 var Tooltip = function (events) {
12 var Tooltip = function (events) {
13 var that = this;
13 var that = this;
14 this.events = events;
14 this.events = events;
15 this.time_before_tooltip = 1200;
15 this.time_before_tooltip = 1200;
16
16
17 // handle to html
17 // handle to html
18 this.tooltip = $('#tooltip');
18 this.tooltip = $('#tooltip');
19 this._hidden = true;
19 this._hidden = true;
20
20
21 // variable for consecutive call
21 // variable for consecutive call
22 this._old_cell = null;
22 this._old_cell = null;
23 this._old_request = null;
23 this._old_request = null;
24 this._consecutive_counter = 0;
24 this._consecutive_counter = 0;
25
25
26 // 'sticky ?'
26 // 'sticky ?'
27 this._sticky = false;
27 this._sticky = false;
28
28
29 // display tooltip if the docstring is empty?
29 // display tooltip if the docstring is empty?
30 this._hide_if_no_docstring = false;
30 this._hide_if_no_docstring = false;
31
31
32 // contain the button in the upper right corner
32 // contain the button in the upper right corner
33 this.buttons = $('<div/>').addClass('tooltipbuttons');
33 this.buttons = $('<div/>').addClass('tooltipbuttons');
34
34
35 // will contain the docstring
35 // will contain the docstring
36 this.text = $('<div/>').addClass('tooltiptext').addClass('smalltooltip');
36 this.text = $('<div/>').addClass('tooltiptext').addClass('smalltooltip');
37
37
38 // build the buttons menu on the upper right
38 // build the buttons menu on the upper right
39 // expand the tooltip to see more
39 // expand the tooltip to see more
40 var expandlink = $('<a/>').attr('href', "#").addClass("ui-corner-all") //rounded corner
40 var expandlink = $('<a/>').attr('href', "#").addClass("ui-corner-all") //rounded corner
41 .attr('role', "button").attr('id', 'expanbutton').attr('title', 'Grow the tooltip vertically (press shift-tab twice)').click(function () {
41 .attr('role', "button").attr('id', 'expanbutton').attr('title', 'Grow the tooltip vertically (press shift-tab twice)').click(function () {
42 that.expand();
42 that.expand();
43 event.preventDefault();
43 event.preventDefault();
44 }).append(
44 }).append(
45 $('<span/>').text('Expand').addClass('ui-icon').addClass('ui-icon-plus'));
45 $('<span/>').text('Expand').addClass('ui-icon').addClass('ui-icon-plus'));
46
46
47 // open in pager
47 // open in pager
48 var morelink = $('<a/>').attr('href', "#").attr('role', "button").addClass('ui-button').attr('title', 'show the current docstring in pager (press shift-tab 4 times)');
48 var morelink = $('<a/>').attr('href', "#").attr('role', "button").addClass('ui-button').attr('title', 'show the current docstring in pager (press shift-tab 4 times)');
49 var morespan = $('<span/>').text('Open in Pager').addClass('ui-icon').addClass('ui-icon-arrowstop-l-n');
49 var morespan = $('<span/>').text('Open in Pager').addClass('ui-icon').addClass('ui-icon-arrowstop-l-n');
50 morelink.append(morespan);
50 morelink.append(morespan);
51 morelink.click(function () {
51 morelink.click(function () {
52 that.showInPager(that._old_cell);
52 that.showInPager(that._old_cell);
53 event.preventDefault();
53 event.preventDefault();
54 });
54 });
55
55
56 // close the tooltip
56 // close the tooltip
57 var closelink = $('<a/>').attr('href', "#").attr('role', "button").addClass('ui-button');
57 var closelink = $('<a/>').attr('href', "#").attr('role', "button").addClass('ui-button');
58 var closespan = $('<span/>').text('Close').addClass('ui-icon').addClass('ui-icon-close');
58 var closespan = $('<span/>').text('Close').addClass('ui-icon').addClass('ui-icon-close');
59 closelink.append(closespan);
59 closelink.append(closespan);
60 closelink.click(function () {
60 closelink.click(function () {
61 that.remove_and_cancel_tooltip(true);
61 that.remove_and_cancel_tooltip(true);
62 event.preventDefault();
62 event.preventDefault();
63 });
63 });
64
64
65 this._clocklink = $('<a/>').attr('href', "#");
65 this._clocklink = $('<a/>').attr('href', "#");
66 this._clocklink.attr('role', "button");
66 this._clocklink.attr('role', "button");
67 this._clocklink.addClass('ui-button');
67 this._clocklink.addClass('ui-button');
68 this._clocklink.attr('title', 'Tooltip is not dismissed while typing for 10 seconds');
68 this._clocklink.attr('title', 'Tooltip will linger for 10 seconds while you type');
69 var clockspan = $('<span/>').text('Close');
69 var clockspan = $('<span/>').text('Close');
70 clockspan.addClass('ui-icon');
70 clockspan.addClass('ui-icon');
71 clockspan.addClass('ui-icon-clock');
71 clockspan.addClass('ui-icon-clock');
72 this._clocklink.append(clockspan);
72 this._clocklink.append(clockspan);
73 this._clocklink.click(function () {
73 this._clocklink.click(function () {
74 that.cancel_stick();
74 that.cancel_stick();
75 event.preventDefault();
75 event.preventDefault();
76 });
76 });
77
77
78
78
79
79
80
80
81 //construct the tooltip
81 //construct the tooltip
82 // add in the reverse order you want them to appear
82 // add in the reverse order you want them to appear
83 this.buttons.append(closelink);
83 this.buttons.append(closelink);
84 this.buttons.append(expandlink);
84 this.buttons.append(expandlink);
85 this.buttons.append(morelink);
85 this.buttons.append(morelink);
86 this.buttons.append(this._clocklink);
86 this.buttons.append(this._clocklink);
87 this._clocklink.hide();
87 this._clocklink.hide();
88
88
89
89
90 // we need a phony element to make the small arrow
90 // we need a phony element to make the small arrow
91 // of the tooltip in css
91 // of the tooltip in css
92 // we will move the arrow later
92 // we will move the arrow later
93 this.arrow = $('<div/>').addClass('pretooltiparrow');
93 this.arrow = $('<div/>').addClass('pretooltiparrow');
94 this.tooltip.append(this.buttons);
94 this.tooltip.append(this.buttons);
95 this.tooltip.append(this.arrow);
95 this.tooltip.append(this.arrow);
96 this.tooltip.append(this.text);
96 this.tooltip.append(this.text);
97
97
98 // function that will be called if you press tab 1, 2, 3... times in a row
98 // function that will be called if you press tab 1, 2, 3... times in a row
99 this.tabs_functions = [function (cell, text, cursor) {
99 this.tabs_functions = [function (cell, text, cursor) {
100 that._request_tooltip(cell, text, cursor);
100 that._request_tooltip(cell, text, cursor);
101 }, function () {
101 }, function () {
102 that.expand();
102 that.expand();
103 }, function () {
103 }, function () {
104 that.stick();
104 that.stick();
105 }, function (cell) {
105 }, function (cell) {
106 that.cancel_stick();
106 that.cancel_stick();
107 that.showInPager(cell);
107 that.showInPager(cell);
108 }];
108 }];
109 // call after all the tabs function above have bee call to clean their effects
109 // call after all the tabs function above have bee call to clean their effects
110 // if necessary
110 // if necessary
111 this.reset_tabs_function = function (cell, text) {
111 this.reset_tabs_function = function (cell, text) {
112 this._old_cell = (cell) ? cell : null;
112 this._old_cell = (cell) ? cell : null;
113 this._old_request = (text) ? text : null;
113 this._old_request = (text) ? text : null;
114 this._consecutive_counter = 0;
114 this._consecutive_counter = 0;
115 };
115 };
116 };
116 };
117
117
118 Tooltip.prototype.is_visible = function () {
118 Tooltip.prototype.is_visible = function () {
119 return !this._hidden;
119 return !this._hidden;
120 };
120 };
121
121
122 Tooltip.prototype.showInPager = function (cell) {
122 Tooltip.prototype.showInPager = function (cell) {
123 /**
123 /**
124 * reexecute last call in pager by appending ? to show back in pager
124 * reexecute last call in pager by appending ? to show back in pager
125 */
125 */
126 this.events.trigger('open_with_text.Pager', this._reply.content);
126 this.events.trigger('open_with_text.Pager', this._reply.content);
127 this.remove_and_cancel_tooltip();
127 this.remove_and_cancel_tooltip();
128 };
128 };
129
129
130 // grow the tooltip verticaly
130 // grow the tooltip verticaly
131 Tooltip.prototype.expand = function () {
131 Tooltip.prototype.expand = function () {
132 this.text.removeClass('smalltooltip');
132 this.text.removeClass('smalltooltip');
133 this.text.addClass('bigtooltip');
133 this.text.addClass('bigtooltip');
134 $('#expanbutton').hide('slow');
134 $('#expanbutton').hide('slow');
135 };
135 };
136
136
137 // deal with all the logic of hiding the tooltip
137 // deal with all the logic of hiding the tooltip
138 // and reset it's status
138 // and reset it's status
139 Tooltip.prototype._hide = function () {
139 Tooltip.prototype._hide = function () {
140 this._hidden = true;
140 this._hidden = true;
141 this.tooltip.fadeOut('fast');
141 this.tooltip.fadeOut('fast');
142 $('#expanbutton').show('slow');
142 $('#expanbutton').show('slow');
143 this.text.removeClass('bigtooltip');
143 this.text.removeClass('bigtooltip');
144 this.text.addClass('smalltooltip');
144 this.text.addClass('smalltooltip');
145 // keep scroll top to be sure to always see the first line
145 // keep scroll top to be sure to always see the first line
146 this.text.scrollTop(0);
146 this.text.scrollTop(0);
147 this.code_mirror = null;
147 this.code_mirror = null;
148 };
148 };
149
149
150 // return true on successfully removing a visible tooltip; otherwise return
150 // return true on successfully removing a visible tooltip; otherwise return
151 // false.
151 // false.
152 Tooltip.prototype.remove_and_cancel_tooltip = function (force) {
152 Tooltip.prototype.remove_and_cancel_tooltip = function (force) {
153 /**
153 /**
154 * note that we don't handle closing directly inside the calltip
154 * note that we don't handle closing directly inside the calltip
155 * as in the completer, because it is not focusable, so won't
155 * as in the completer, because it is not focusable, so won't
156 * get the event.
156 * get the event.
157 */
157 */
158 this.cancel_pending();
158 this.cancel_pending();
159 if (!this._hidden) {
159 if (!this._hidden) {
160 if (force || !this._sticky) {
160 if (force || !this._sticky) {
161 this.cancel_stick();
161 this.cancel_stick();
162 this._hide();
162 this._hide();
163 }
163 }
164 this.reset_tabs_function();
164 this.reset_tabs_function();
165 return true;
165 return true;
166 } else {
166 } else {
167 return false;
167 return false;
168 }
168 }
169 };
169 };
170
170
171 // cancel autocall done after '(' for example.
171 // cancel autocall done after '(' for example.
172 Tooltip.prototype.cancel_pending = function () {
172 Tooltip.prototype.cancel_pending = function () {
173 if (this._tooltip_timeout !== null) {
173 if (this._tooltip_timeout !== null) {
174 clearTimeout(this._tooltip_timeout);
174 clearTimeout(this._tooltip_timeout);
175 this._tooltip_timeout = null;
175 this._tooltip_timeout = null;
176 }
176 }
177 };
177 };
178
178
179 // will trigger tooltip after timeout
179 // will trigger tooltip after timeout
180 Tooltip.prototype.pending = function (cell, hide_if_no_docstring) {
180 Tooltip.prototype.pending = function (cell, hide_if_no_docstring) {
181 var that = this;
181 var that = this;
182 this._tooltip_timeout = setTimeout(function () {
182 this._tooltip_timeout = setTimeout(function () {
183 that.request(cell, hide_if_no_docstring);
183 that.request(cell, hide_if_no_docstring);
184 }, that.time_before_tooltip);
184 }, that.time_before_tooltip);
185 };
185 };
186
186
187 // easy access for julia monkey patching.
187 // easy access for julia monkey patching.
188 Tooltip.last_token_re = /[a-z_][0-9a-z._]*$/gi;
188 Tooltip.last_token_re = /[a-z_][0-9a-z._]*$/gi;
189
189
190 Tooltip.prototype._request_tooltip = function (cell, text, cursor_pos) {
190 Tooltip.prototype._request_tooltip = function (cell, text, cursor_pos) {
191 var callbacks = $.proxy(this._show, this);
191 var callbacks = $.proxy(this._show, this);
192 var msg_id = cell.kernel.inspect(text, cursor_pos, callbacks);
192 var msg_id = cell.kernel.inspect(text, cursor_pos, callbacks);
193 };
193 };
194
194
195 // make an immediate completion request
195 // make an immediate completion request
196 Tooltip.prototype.request = function (cell, hide_if_no_docstring) {
196 Tooltip.prototype.request = function (cell, hide_if_no_docstring) {
197 /**
197 /**
198 * request(codecell)
198 * request(codecell)
199 * Deal with extracting the text from the cell and counting
199 * Deal with extracting the text from the cell and counting
200 * call in a row
200 * call in a row
201 */
201 */
202 this.cancel_pending();
202 this.cancel_pending();
203 var editor = cell.code_mirror;
203 var editor = cell.code_mirror;
204 var cursor = editor.getCursor();
204 var cursor = editor.getCursor();
205 var cursor_pos = utils.to_absolute_cursor_pos(editor, cursor);
205 var cursor_pos = utils.to_absolute_cursor_pos(editor, cursor);
206 var text = cell.get_text();
206 var text = cell.get_text();
207
207
208 this._hide_if_no_docstring = hide_if_no_docstring;
208 this._hide_if_no_docstring = hide_if_no_docstring;
209
209
210 if(editor.somethingSelected()){
210 if(editor.somethingSelected()){
211 // get only the most recent selection.
211 // get only the most recent selection.
212 text = editor.getSelection();
212 text = editor.getSelection();
213 }
213 }
214
214
215 // need a permanent handle to code_mirror for future auto recall
215 // need a permanent handle to code_mirror for future auto recall
216 this.code_mirror = editor;
216 this.code_mirror = editor;
217
217
218 // now we treat the different number of keypress
218 // now we treat the different number of keypress
219 // first if same cell, same text, increment counter by 1
219 // first if same cell, same text, increment counter by 1
220 if (this._old_cell == cell && this._old_request == text && this._hidden === false) {
220 if (this._old_cell == cell && this._old_request == text && this._hidden === false) {
221 this._consecutive_counter++;
221 this._consecutive_counter++;
222 } else {
222 } else {
223 // else reset
223 // else reset
224 this.cancel_stick();
224 this.cancel_stick();
225 this.reset_tabs_function (cell, text);
225 this.reset_tabs_function (cell, text);
226 }
226 }
227
227
228 this.tabs_functions[this._consecutive_counter](cell, text, cursor_pos);
228 this.tabs_functions[this._consecutive_counter](cell, text, cursor_pos);
229
229
230 // then if we are at the end of list function, reset
230 // then if we are at the end of list function, reset
231 if (this._consecutive_counter == this.tabs_functions.length) {
231 if (this._consecutive_counter == this.tabs_functions.length) {
232 this.reset_tabs_function (cell, text, cursor);
232 this.reset_tabs_function (cell, text, cursor);
233 }
233 }
234
234
235 return;
235 return;
236 };
236 };
237
237
238 // cancel the option of having the tooltip to stick
238 // cancel the option of having the tooltip to stick
239 Tooltip.prototype.cancel_stick = function () {
239 Tooltip.prototype.cancel_stick = function () {
240 clearTimeout(this._stick_timeout);
240 clearTimeout(this._stick_timeout);
241 this._stick_timeout = null;
241 this._stick_timeout = null;
242 this._clocklink.hide('slow');
242 this._clocklink.hide('slow');
243 this._sticky = false;
243 this._sticky = false;
244 };
244 };
245
245
246 // put the tooltip in a sicky state for 10 seconds
246 // put the tooltip in a sicky state for 10 seconds
247 // it won't be removed by remove_and_cancell() unless you called with
247 // it won't be removed by remove_and_cancell() unless you called with
248 // the first parameter set to true.
248 // the first parameter set to true.
249 // remove_and_cancell_tooltip(true)
249 // remove_and_cancell_tooltip(true)
250 Tooltip.prototype.stick = function (time) {
250 Tooltip.prototype.stick = function (time) {
251 time = (time !== undefined) ? time : 10;
251 time = (time !== undefined) ? time : 10;
252 var that = this;
252 var that = this;
253 this._sticky = true;
253 this._sticky = true;
254 this._clocklink.show('slow');
254 this._clocklink.show('slow');
255 this._stick_timeout = setTimeout(function () {
255 this._stick_timeout = setTimeout(function () {
256 that._sticky = false;
256 that._sticky = false;
257 that._clocklink.hide('slow');
257 that._clocklink.hide('slow');
258 }, time * 1000);
258 }, time * 1000);
259 };
259 };
260
260
261 // should be called with the kernel reply to actually show the tooltip
261 // should be called with the kernel reply to actually show the tooltip
262 Tooltip.prototype._show = function (reply) {
262 Tooltip.prototype._show = function (reply) {
263 /**
263 /**
264 * move the bubble if it is not hidden
264 * move the bubble if it is not hidden
265 * otherwise fade it
265 * otherwise fade it
266 */
266 */
267 this._reply = reply;
267 this._reply = reply;
268 var content = reply.content;
268 var content = reply.content;
269 if (!content.found) {
269 if (!content.found) {
270 // object not found, nothing to show
270 // object not found, nothing to show
271 return;
271 return;
272 }
272 }
273 this.name = content.name;
273 this.name = content.name;
274
274
275 // do some math to have the tooltip arrow on more or less on left or right
275 // do some math to have the tooltip arrow on more or less on left or right
276 // position of the editor
276 // position of the editor
277 var cm_pos = $(this.code_mirror.getWrapperElement()).position();
277 var cm_pos = $(this.code_mirror.getWrapperElement()).position();
278
278
279 // anchor and head positions are local within CodeMirror element
279 // anchor and head positions are local within CodeMirror element
280 var anchor = this.code_mirror.cursorCoords(false, 'local');
280 var anchor = this.code_mirror.cursorCoords(false, 'local');
281 var head = this.code_mirror.cursorCoords(true, 'local');
281 var head = this.code_mirror.cursorCoords(true, 'local');
282 // locate the target at the center of anchor, head
282 // locate the target at the center of anchor, head
283 var center_left = (head.left + anchor.left) / 2;
283 var center_left = (head.left + anchor.left) / 2;
284 // locate the left edge of the tooltip, at most 450 px left of the arrow
284 // locate the left edge of the tooltip, at most 450 px left of the arrow
285 var edge_left = Math.max(center_left - 450, 0);
285 var edge_left = Math.max(center_left - 450, 0);
286 // locate the arrow at the cursor. A 24 px offset seems necessary.
286 // locate the arrow at the cursor. A 24 px offset seems necessary.
287 var arrow_left = center_left - edge_left - 24;
287 var arrow_left = center_left - edge_left - 24;
288
288
289 // locate left, top within container element
289 // locate left, top within container element
290 var left = (cm_pos.left + edge_left) + 'px';
290 var left = (cm_pos.left + edge_left) + 'px';
291 var top = (cm_pos.top + head.bottom + 10) + 'px';
291 var top = (cm_pos.top + head.bottom + 10) + 'px';
292
292
293 if (this._hidden === false) {
293 if (this._hidden === false) {
294 this.tooltip.animate({
294 this.tooltip.animate({
295 left: left,
295 left: left,
296 top: top
296 top: top
297 });
297 });
298 } else {
298 } else {
299 this.tooltip.css({
299 this.tooltip.css({
300 left: left
300 left: left
301 });
301 });
302 this.tooltip.css({
302 this.tooltip.css({
303 top: top
303 top: top
304 });
304 });
305 }
305 }
306 this.arrow.animate({
306 this.arrow.animate({
307 'left': arrow_left + 'px'
307 'left': arrow_left + 'px'
308 });
308 });
309
309
310 this._hidden = false;
310 this._hidden = false;
311 this.tooltip.fadeIn('fast');
311 this.tooltip.fadeIn('fast');
312 this.text.children().remove();
312 this.text.children().remove();
313
313
314 // This should support rich data types, but only text/plain for now
314 // This should support rich data types, but only text/plain for now
315 // Any HTML within the docstring is escaped by the fixConsole() method.
315 // Any HTML within the docstring is escaped by the fixConsole() method.
316 var pre = $('<pre/>').html(utils.fixConsole(content.data['text/plain']));
316 var pre = $('<pre/>').html(utils.fixConsole(content.data['text/plain']));
317 this.text.append(pre);
317 this.text.append(pre);
318 // keep scroll top to be sure to always see the first line
318 // keep scroll top to be sure to always see the first line
319 this.text.scrollTop(0);
319 this.text.scrollTop(0);
320 };
320 };
321
321
322 // Backwards compatibility.
322 // Backwards compatibility.
323 IPython.Tooltip = Tooltip;
323 IPython.Tooltip = Tooltip;
324
324
325 return {'Tooltip': Tooltip};
325 return {'Tooltip': Tooltip};
326 });
326 });
General Comments 0
You need to be logged in to leave comments. Login now