Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / third_party / WebKit / Source / devtools / front_end / ui / Tooltip.js
blobc01b30088460e02baa347c9aa551484f97a8d3af
1 // Copyright (c) 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 /**
6 * @constructor
7 * @param {!Document} doc
8 */
9 WebInspector.Tooltip = function(doc)
11 this.element = doc.body.createChild("div");
12 this._shadowRoot = WebInspector.createShadowRootWithCoreStyles(this.element);
13 this._shadowRoot.appendChild(WebInspector.Widget.createStyleElement("ui/tooltip.css"));
15 this._tooltipElement = this._shadowRoot.createChild("div", "tooltip");
16 doc.addEventListener("mousemove", this._mouseMove.bind(this), true);
17 doc.addEventListener("mousedown", this._hide.bind(this, true), true);
18 doc.addEventListener("mouseout", this._hide.bind(this, true), true);
19 doc.addEventListener("keydown", this._hide.bind(this, true), true);
22 WebInspector.Tooltip.Timing = {
23 // Max time between tooltips showing that no opening delay is required.
24 "InstantThreshold": 300,
25 // Wait time before opening a tooltip.
26 "OpeningDelay": 600
29 WebInspector.Tooltip.prototype = {
30 /**
31 * @param {!Event} event
33 _mouseMove: function(event)
35 var path = event.deepPath ? event.deepPath : event.path;
36 if (!path || event.buttons !== 0)
37 return;
39 if (this._anchorElement && path.indexOf(this._anchorElement) === -1)
40 this._hide();
42 for (var element of path) {
43 if (element === this._anchorElement) {
44 return;
45 } else if (element[WebInspector.Tooltip._symbol]) {
46 this._show(element, event);
47 return;
52 /**
53 * @param {!Element} anchorElement
54 * @param {!Event} event
56 _show: function(anchorElement, event)
58 var tooltip = anchorElement[WebInspector.Tooltip._symbol];
59 this._anchorElement = anchorElement;
60 this._tooltipElement.removeChildren();
62 // Check if native tooltips should be used.
63 for (var element of WebInspector.Tooltip._nativeOverrideContainer) {
64 if (this._anchorElement.isSelfOrDescendant(element)) {
65 Object.defineProperty(this._anchorElement, "title", WebInspector.Tooltip._nativeTitle);
66 this._anchorElement.title = tooltip.content;
67 return;
71 if (typeof tooltip.content === "string")
72 this._tooltipElement.textContent = tooltip.content;
73 else
74 this._tooltipElement.appendChild(tooltip.content);
76 if (tooltip.actionId) {
77 var shortcuts = WebInspector.shortcutRegistry.shortcutDescriptorsForAction(tooltip.actionId);
78 for (var shortcut of shortcuts) {
79 var shortcutElement = this._tooltipElement.createChild("div", "tooltip-shortcut");
80 shortcutElement.textContent = shortcut.name;
84 this._tooltipElement.classList.add("shown");
85 // Reposition to ensure text doesn't overflow unnecessarily.
86 this._tooltipElement.positionAt(0, 0);
88 // Show tooltip instantly if a tooltip was shown recently.
89 var now = Date.now();
90 var instant = (this._tooltipLastClosed && now - this._tooltipLastClosed < WebInspector.Tooltip.Timing.InstantThreshold);
91 this._tooltipElement.classList.toggle("instant", instant);
92 this._tooltipLastOpened = instant ? now : now + WebInspector.Tooltip.Timing.OpeningDelay;
94 // Get container element.
95 var container = WebInspector.Dialog.modalHostView().element;
96 if (!anchorElement.isDescendant(container))
97 container = this.element.parentElement;
99 // Posititon tooltip based on the anchor element.
100 var containerOffset = container.offsetRelativeToWindow(this.element.window());
101 var containerOffsetWidth = container.offsetWidth;
102 var containerOffsetHeight = container.offsetHeight;
103 var anchorBox = this._anchorElement.boxInWindow(this.element.window());
104 const anchorOffset = 2;
105 const pageMargin = 2;
106 var cursorOffset = 10;
107 this._tooltipElement.style.maxWidth = (containerOffsetWidth - pageMargin * 2) + "px";
108 var tooltipWidth = this._tooltipElement.offsetWidth;
109 var tooltipHeight = this._tooltipElement.offsetHeight;
110 var anchorTooltipAtElement = this._anchorElement.nodeName === "BUTTON" || this._anchorElement.nodeName === "LABEL";
111 var tooltipX = anchorTooltipAtElement ? anchorBox.x : event.x + cursorOffset;
112 tooltipX = Number.constrain(tooltipX,
113 containerOffset.x + pageMargin,
114 containerOffset.x + containerOffsetWidth - tooltipWidth - pageMargin);
115 var tooltipY;
116 if (!anchorTooltipAtElement) {
117 tooltipY = event.y + cursorOffset + tooltipHeight < containerOffset.y + containerOffsetHeight ? event.y + cursorOffset : event.y - tooltipHeight;
118 } else {
119 var onBottom = anchorBox.y + anchorOffset + anchorBox.height + tooltipHeight < containerOffset.y + containerOffsetHeight;
120 tooltipY = onBottom ? anchorBox.y + anchorBox.height + anchorOffset : anchorBox.y - tooltipHeight - anchorOffset;
122 this._tooltipElement.positionAt(tooltipX, tooltipY);
126 * @param {boolean=} removeInstant
128 _hide: function(removeInstant)
130 delete this._anchorElement;
131 this._tooltipElement.classList.remove("shown");
132 if (Date.now() > this._tooltipLastOpened)
133 this._tooltipLastClosed = Date.now();
134 if (removeInstant)
135 delete this._tooltipLastClosed;
139 WebInspector.Tooltip._symbol = Symbol("Tooltip");
142 * @param {!Document} doc
144 WebInspector.Tooltip.installHandler = function(doc)
146 new WebInspector.Tooltip(doc);
150 * @param {!Element} element
151 * @param {!Element|string} tooltipContent
152 * @param {string=} actionId
153 * @param {!Object=} options
155 WebInspector.Tooltip.install = function(element, tooltipContent, actionId, options)
157 if (typeof tooltipContent === "string" && tooltipContent === "") {
158 delete element[WebInspector.Tooltip._symbol];
159 return;
161 element[WebInspector.Tooltip._symbol] = { content: tooltipContent, actionId: actionId, options: options || {} };
165 * @param {!Element} element
167 WebInspector.Tooltip.addNativeOverrideContainer = function(element)
169 WebInspector.Tooltip._nativeOverrideContainer.push(element);
172 /** @type {!Array.<!Element>} */
173 WebInspector.Tooltip._nativeOverrideContainer = [];
174 WebInspector.Tooltip._nativeTitle = /** @type {!ObjectPropertyDescriptor} */(Object.getOwnPropertyDescriptor(HTMLElement.prototype, "title"));
176 Object.defineProperty(HTMLElement.prototype, "title", {
178 * @return {!Element|string}
179 * @this {!Element}
181 get: function()
183 var tooltip = this[WebInspector.Tooltip._symbol];
184 return tooltip ? tooltip.content : "";
188 * @param {!Element|string} x
189 * @this {!Element}
191 set: function(x)
193 WebInspector.Tooltip.install(this, x);