##// END OF EJS Templates
only show prompt overlay if there's a prompt...
only show prompt overlay if there's a prompt on which to overlay

File last commit:

r20226:fa4b962d
r20299:6fb4c32f
Show More
tooltip.js
326 lines | 11.3 KiB | application/javascript | JavascriptLexer
// Copyright (c) IPython Development Team.
// Distributed under the terms of the Modified BSD License.
define([
'base/js/namespace',
'jquery',
'base/js/utils',
], function(IPython, $, utils) {
"use strict";
// tooltip constructor
var Tooltip = function (events) {
var that = this;
this.events = events;
this.time_before_tooltip = 1200;
// handle to html
this.tooltip = $('#tooltip');
this._hidden = true;
// variable for consecutive call
this._old_cell = null;
this._old_request = null;
this._consecutive_counter = 0;
// 'sticky ?'
this._sticky = false;
// display tooltip if the docstring is empty?
this._hide_if_no_docstring = false;
// contain the button in the upper right corner
this.buttons = $('<div/>').addClass('tooltipbuttons');
// will contain the docstring
this.text = $('<div/>').addClass('tooltiptext').addClass('smalltooltip');
// build the buttons menu on the upper right
// expand the tooltip to see more
var expandlink = $('<a/>').attr('href', "#").addClass("ui-corner-all") //rounded corner
.attr('role', "button").attr('id', 'expanbutton').attr('title', 'Grow the tooltip vertically (press shift-tab twice)').click(function () {
that.expand();
event.preventDefault();
}).append(
$('<span/>').text('Expand').addClass('ui-icon').addClass('ui-icon-plus'));
// open in pager
var morelink = $('<a/>').attr('href', "#").attr('role', "button").addClass('ui-button').attr('title', 'show the current docstring in pager (press shift-tab 4 times)');
var morespan = $('<span/>').text('Open in Pager').addClass('ui-icon').addClass('ui-icon-arrowstop-l-n');
morelink.append(morespan);
morelink.click(function () {
that.showInPager(that._old_cell);
event.preventDefault();
});
// close the tooltip
var closelink = $('<a/>').attr('href', "#").attr('role', "button").addClass('ui-button');
var closespan = $('<span/>').text('Close').addClass('ui-icon').addClass('ui-icon-close');
closelink.append(closespan);
closelink.click(function () {
that.remove_and_cancel_tooltip(true);
event.preventDefault();
});
this._clocklink = $('<a/>').attr('href', "#");
this._clocklink.attr('role', "button");
this._clocklink.addClass('ui-button');
this._clocklink.attr('title', 'Tooltip is not dismissed while typing for 10 seconds');
var clockspan = $('<span/>').text('Close');
clockspan.addClass('ui-icon');
clockspan.addClass('ui-icon-clock');
this._clocklink.append(clockspan);
this._clocklink.click(function () {
that.cancel_stick();
event.preventDefault();
});
//construct the tooltip
// add in the reverse order you want them to appear
this.buttons.append(closelink);
this.buttons.append(expandlink);
this.buttons.append(morelink);
this.buttons.append(this._clocklink);
this._clocklink.hide();
// we need a phony element to make the small arrow
// of the tooltip in css
// we will move the arrow later
this.arrow = $('<div/>').addClass('pretooltiparrow');
this.tooltip.append(this.buttons);
this.tooltip.append(this.arrow);
this.tooltip.append(this.text);
// function that will be called if you press tab 1, 2, 3... times in a row
this.tabs_functions = [function (cell, text, cursor) {
that._request_tooltip(cell, text, cursor);
}, function () {
that.expand();
}, function () {
that.stick();
}, function (cell) {
that.cancel_stick();
that.showInPager(cell);
}];
// call after all the tabs function above have bee call to clean their effects
// if necessary
this.reset_tabs_function = function (cell, text) {
this._old_cell = (cell) ? cell : null;
this._old_request = (text) ? text : null;
this._consecutive_counter = 0;
};
};
Tooltip.prototype.is_visible = function () {
return !this._hidden;
};
Tooltip.prototype.showInPager = function (cell) {
/**
* reexecute last call in pager by appending ? to show back in pager
*/
this.events.trigger('open_with_text.Pager', this._reply.content);
this.remove_and_cancel_tooltip();
};
// grow the tooltip verticaly
Tooltip.prototype.expand = function () {
this.text.removeClass('smalltooltip');
this.text.addClass('bigtooltip');
$('#expanbutton').hide('slow');
};
// deal with all the logic of hiding the tooltip
// and reset it's status
Tooltip.prototype._hide = function () {
this._hidden = true;
this.tooltip.fadeOut('fast');
$('#expanbutton').show('slow');
this.text.removeClass('bigtooltip');
this.text.addClass('smalltooltip');
// keep scroll top to be sure to always see the first line
this.text.scrollTop(0);
this.code_mirror = null;
};
// return true on successfully removing a visible tooltip; otherwise return
// false.
Tooltip.prototype.remove_and_cancel_tooltip = function (force) {
/**
* note that we don't handle closing directly inside the calltip
* as in the completer, because it is not focusable, so won't
* get the event.
*/
this.cancel_pending();
if (!this._hidden) {
if (force || !this._sticky) {
this.cancel_stick();
this._hide();
}
this.reset_tabs_function();
return true;
} else {
return false;
}
};
// cancel autocall done after '(' for example.
Tooltip.prototype.cancel_pending = function () {
if (this._tooltip_timeout !== null) {
clearTimeout(this._tooltip_timeout);
this._tooltip_timeout = null;
}
};
// will trigger tooltip after timeout
Tooltip.prototype.pending = function (cell, hide_if_no_docstring) {
var that = this;
this._tooltip_timeout = setTimeout(function () {
that.request(cell, hide_if_no_docstring);
}, that.time_before_tooltip);
};
// easy access for julia monkey patching.
Tooltip.last_token_re = /[a-z_][0-9a-z._]*$/gi;
Tooltip.prototype._request_tooltip = function (cell, text, cursor_pos) {
var callbacks = $.proxy(this._show, this);
var msg_id = cell.kernel.inspect(text, cursor_pos, callbacks);
};
// make an immediate completion request
Tooltip.prototype.request = function (cell, hide_if_no_docstring) {
/**
* request(codecell)
* Deal with extracting the text from the cell and counting
* call in a row
*/
this.cancel_pending();
var editor = cell.code_mirror;
var cursor = editor.getCursor();
var cursor_pos = utils.to_absolute_cursor_pos(editor, cursor);
var text = cell.get_text();
this._hide_if_no_docstring = hide_if_no_docstring;
if(editor.somethingSelected()){
// get only the most recent selection.
text = editor.getSelection();
}
// need a permanent handle to code_mirror for future auto recall
this.code_mirror = editor;
// now we treat the different number of keypress
// first if same cell, same text, increment counter by 1
if (this._old_cell == cell && this._old_request == text && this._hidden === false) {
this._consecutive_counter++;
} else {
// else reset
this.cancel_stick();
this.reset_tabs_function (cell, text);
}
this.tabs_functions[this._consecutive_counter](cell, text, cursor_pos);
// then if we are at the end of list function, reset
if (this._consecutive_counter == this.tabs_functions.length) {
this.reset_tabs_function (cell, text, cursor);
}
return;
};
// cancel the option of having the tooltip to stick
Tooltip.prototype.cancel_stick = function () {
clearTimeout(this._stick_timeout);
this._stick_timeout = null;
this._clocklink.hide('slow');
this._sticky = false;
};
// put the tooltip in a sicky state for 10 seconds
// it won't be removed by remove_and_cancell() unless you called with
// the first parameter set to true.
// remove_and_cancell_tooltip(true)
Tooltip.prototype.stick = function (time) {
time = (time !== undefined) ? time : 10;
var that = this;
this._sticky = true;
this._clocklink.show('slow');
this._stick_timeout = setTimeout(function () {
that._sticky = false;
that._clocklink.hide('slow');
}, time * 1000);
};
// should be called with the kernel reply to actually show the tooltip
Tooltip.prototype._show = function (reply) {
/**
* move the bubble if it is not hidden
* otherwise fade it
*/
this._reply = reply;
var content = reply.content;
if (!content.found) {
// object not found, nothing to show
return;
}
this.name = content.name;
// do some math to have the tooltip arrow on more or less on left or right
// position of the editor
var cm_pos = $(this.code_mirror.getWrapperElement()).position();
// anchor and head positions are local within CodeMirror element
var anchor = this.code_mirror.cursorCoords(false, 'local');
var head = this.code_mirror.cursorCoords(true, 'local');
// locate the target at the center of anchor, head
var center_left = (head.left + anchor.left) / 2;
// locate the left edge of the tooltip, at most 450 px left of the arrow
var edge_left = Math.max(center_left - 450, 0);
// locate the arrow at the cursor. A 24 px offset seems necessary.
var arrow_left = center_left - edge_left - 24;
// locate left, top within container element
var left = (cm_pos.left + edge_left) + 'px';
var top = (cm_pos.top + head.bottom + 10) + 'px';
if (this._hidden === false) {
this.tooltip.animate({
left: left,
top: top
});
} else {
this.tooltip.css({
left: left
});
this.tooltip.css({
top: top
});
}
this.arrow.animate({
'left': arrow_left + 'px'
});
this._hidden = false;
this.tooltip.fadeIn('fast');
this.text.children().remove();
// This should support rich data types, but only text/plain for now
// Any HTML within the docstring is escaped by the fixConsole() method.
var pre = $('<pre/>').html(utils.fixConsole(content.data['text/plain']));
this.text.append(pre);
// keep scroll top to be sure to always see the first line
this.text.scrollTop(0);
};
// Backwards compatibility.
IPython.Tooltip = Tooltip;
return {'Tooltip': Tooltip};
});