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