
These tags are preserved by the Closure compiler and vulcanize in order to serve the license notices embedded in the outputs. In a standalone Gerrit server, these license are also covered in the LICENSES.txt served with the documentation. When serving PG assets from a CDN, it's less obvious what the corresponding LICENSES.txt file is, since the CDN is not directly linked to a running Gerrit server. Safer to embed the licenses in the assets themselves. Change-Id: Id1add1451fad1baa7916882a6bda02c326ccc988
148 lines
4.5 KiB
JavaScript
148 lines
4.5 KiB
JavaScript
/**
|
||
* @license
|
||
* Copyright (C) 2016 The Android Open Source Project
|
||
*
|
||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||
* you may not use this file except in compliance with the License.
|
||
* You may obtain a copy of the License at
|
||
*
|
||
* http://www.apache.org/licenses/LICENSE-2.0
|
||
*
|
||
* Unless required by applicable law or agreed to in writing, software
|
||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
* See the License for the specific language governing permissions and
|
||
* limitations under the License.
|
||
*/
|
||
(function(window) {
|
||
'use strict';
|
||
|
||
const BOTTOM_OFFSET = 7.2; // Height of the arrow in tooltip.
|
||
|
||
window.Gerrit = window.Gerrit || {};
|
||
|
||
/** @polymerBehavior Gerrit.TooltipBehavior */
|
||
Gerrit.TooltipBehavior = {
|
||
|
||
properties: {
|
||
hasTooltip: {
|
||
type: Boolean,
|
||
observer: '_setupTooltipListeners',
|
||
},
|
||
positionBelow: {
|
||
type: Boolean,
|
||
value: false,
|
||
reflectToAttribute: true,
|
||
},
|
||
|
||
_isTouchDevice: {
|
||
type: Boolean,
|
||
value() {
|
||
return 'ontouchstart' in document.documentElement;
|
||
},
|
||
},
|
||
_tooltip: Object,
|
||
_titleText: String,
|
||
_hasSetupTooltipListeners: {
|
||
type: Boolean,
|
||
value: false,
|
||
},
|
||
},
|
||
|
||
detached() {
|
||
this._handleHideTooltip();
|
||
this.unlisten(window, 'scroll', '_handleWindowScroll');
|
||
},
|
||
|
||
_setupTooltipListeners() {
|
||
if (this._hasSetupTooltipListeners || !this.hasTooltip) { return; }
|
||
this._hasSetupTooltipListeners = true;
|
||
|
||
this.addEventListener('mouseenter', this._handleShowTooltip.bind(this));
|
||
this.addEventListener('mouseleave', this._handleHideTooltip.bind(this));
|
||
this.addEventListener('tap', this._handleHideTooltip.bind(this));
|
||
this.listen(window, 'scroll', '_handleWindowScroll');
|
||
},
|
||
|
||
_handleShowTooltip(e) {
|
||
if (this._isTouchDevice) { return; }
|
||
|
||
if (!this.hasAttribute('title') ||
|
||
this.getAttribute('title') === '' ||
|
||
this._tooltip) {
|
||
return;
|
||
}
|
||
|
||
// Store the title attribute text then set it to an empty string to
|
||
// prevent it from showing natively.
|
||
this._titleText = this.getAttribute('title');
|
||
this.setAttribute('title', '');
|
||
|
||
const tooltip = document.createElement('gr-tooltip');
|
||
tooltip.text = this._titleText;
|
||
tooltip.maxWidth = this.getAttribute('max-width');
|
||
tooltip.positionBelow = this.getAttribute('position-below');
|
||
|
||
// Set visibility to hidden before appending to the DOM so that
|
||
// calculations can be made based on the element’s size.
|
||
tooltip.style.visibility = 'hidden';
|
||
Gerrit.getRootElement().appendChild(tooltip);
|
||
this._positionTooltip(tooltip);
|
||
tooltip.style.visibility = null;
|
||
|
||
this._tooltip = tooltip;
|
||
},
|
||
|
||
_handleHideTooltip(e) {
|
||
if (this._isTouchDevice) { return; }
|
||
if (!this.hasAttribute('title') ||
|
||
this._titleText == null) {
|
||
return;
|
||
}
|
||
|
||
this.setAttribute('title', this._titleText);
|
||
if (this._tooltip && this._tooltip.parentNode) {
|
||
this._tooltip.parentNode.removeChild(this._tooltip);
|
||
}
|
||
this._tooltip = null;
|
||
},
|
||
|
||
_handleWindowScroll(e) {
|
||
if (!this._tooltip) { return; }
|
||
|
||
this._positionTooltip(this._tooltip);
|
||
},
|
||
|
||
_positionTooltip(tooltip) {
|
||
// This flush is needed for tooltips to be positioned correctly in Firefox
|
||
// and Safari.
|
||
Polymer.dom.flush();
|
||
const rect = this.getBoundingClientRect();
|
||
const boxRect = tooltip.getBoundingClientRect();
|
||
const parentRect = tooltip.parentElement.getBoundingClientRect();
|
||
const top = rect.top - parentRect.top;
|
||
const left =
|
||
rect.left - parentRect.left + (rect.width - boxRect.width) / 2;
|
||
const right = parentRect.width - left - boxRect.width;
|
||
if (left < 0) {
|
||
tooltip.updateStyles({
|
||
'--gr-tooltip-arrow-center-offset': left + 'px',
|
||
});
|
||
} else if (right < 0) {
|
||
tooltip.updateStyles({
|
||
'--gr-tooltip-arrow-center-offset': (-0.5 * right) + 'px',
|
||
});
|
||
}
|
||
tooltip.style.left = Math.max(0, left) + 'px';
|
||
|
||
if (!this.positionBelow) {
|
||
tooltip.style.top = Math.max(0, top) + 'px';
|
||
tooltip.style.transform = 'translateY(calc(-100% - ' + BOTTOM_OFFSET +
|
||
'px))';
|
||
} else {
|
||
tooltip.style.top = top + rect.height + BOTTOM_OFFSET + 'px';
|
||
}
|
||
},
|
||
};
|
||
})(window);
|