"use strict";
/**
* @class TooltipJS
* @description Une librairie qui permet d'afficher un TooltipJS au survol d'un élément HTML. L'instance de la classe est créée automatiquement lorsque la librairie est chargée dans la page.
* @author TIMMatane
* @version 1.0.0 (2022)
*/
let TooltipJS = class {
constructor() {
document.addEventListener("DOMContentLoaded", function () {
setTimeout(function () {
_checkForInlineTooltips();
}, api.initDelay);
});
const api = {};
/**
* @memberof TooltipJS
* @alias TooltipJS.initDelay
* @type {Number}
* @static
* @default 100
* @description Le délai en milisecondes avant de commencer à vérifier, dans le DOM, les balises html necéssitant un TooltipJS
* @example <caption>Attendre une demi-seconde avant de détecter, dans le DOM, les balise necéssitant un TooltipJS</caption>
* TooltipJS.initDelay = 500;
*/
api.initDelay = 100;
/**
*@alias TooltipJS.tooltipStyle
*@type {Object}
*@static
*@description Le style css affecté au conteneur HTML d'un TooltipJS. Il aussi est possible d'ajouter n'importe quelle propriété CSS n'étant pas définie par défaut.
*<br><em><b>Note</b>: Aucun fichier CSS nécessaire puisque le style sera affecté par JavaScript. Il est aussi possible de spécifier un style CSS sur mesure pour un TooltipJS particulier voir {@link TooltipJS.addTooltip}</em>
*@member {Object}
*@property {Object} BASE Propriétés du style CSS de base pour tout TooltipJS
*@property {String} BASE.display="none" Type d'affiche du TooltipJS
*@property {String} BASE.position="fixed" Type de positionnement du TooltipJS
*@property {String} BASE.top="0" Position verticale du TooltipJS
*@property {String} BASE.left="0" Position horizontale du TooltipJS
*@property {String} BASE.pointerEvents="none" Le TooltipJS ne bloque pas les événements de la souris
*@property {String} BASE.opacity="0" Opacité de départ du TooltipJS
*@property {String} BASE.zIndex="1000" Index de profondeur du TooltipJS
*@property {Object} DEFAULT Propriétés du style CSS par défaut pour le TooltipJS par défaut
*@property {String} [DEFAULT.width="auto"] Largeur par défaut du TooltipJS
*@property {String} [DEFAULT.height="auto"] Hauteur par défaut du TooltipJS
*@property {String} [DEFAULT.backgroundColor="#ffffcc"] par défaut Couleur de fond du TooltipJS
*@property {String} [DEFAULT.color="#000"] Couleur par défaut du texte du TooltipJS
*@property {String} [DEFAULT.margin="0"] Marges par défaut du TooltipJS
*@property {String} [DEFAULT.padding="0.2rem"] Padding par défaut du TooltipJS
*@property {String} [DEFAULT.fontFamily="Arial"] Police par défaut du TooltipJS
*@property {String} [DEFAULT.fontSize="0.8rem"] Grosseur par défaut du texte du TooltipJS
*@property {String} [DEFAULT.border="1px solid #000"] Le type de bordure par défaut du TooltipJS
*@property {String} [DEFAULT.boxSizing="border-box"] Les bordures sont calculées comme étant intérieures au TooltipJS
*@property {String} [DEFAULT.boxShadow="3px 3px 5px rgba(0,0,0,0.5)"] Le style d'ombrage par défaut du TooltipJS
*@example <caption>Changer le style par defaut du conteneur du TooltipJS.</caption>
*TooltipJS.tooltipStyle.DEFAULT.fontFamily = "Verdana";
* //Ajout d'une propriété qui n'existe pas à la base'
*TooltipJS.tooltipStyle.DEFAULT.borderBottom = "5px solid #fff";
*/
api.tooltipStyle = {
BASE: {
display: "none",
position: "fixed",
top: "0",
left: "0",
pointerEvents: "none",
opacity: "0",
zIndex: "10000",
},
DEFAULT: {
width: "auto",
height: "auto",
backgroundColor: "#ffffcc",
color: "#000",
margin: "0",
padding: "0.2rem",
fontFamily: "Arial",
fontSize: "0.8rem",
border: "1px solid #000",
boxSizing: "border-box",
boxShadow: "3px 3px 5px rgba(0,0,0,0.5)",
},
};
/**
* @description Lance la détection de balises HTML nécessitant un TooltipJS.
* Exécuté automatiquement au chargement de la page, il est possible que les éléments ne soient pas encore créés.
* Il suffit d'appeler cette fonction au moment opportun.
* @alias TooltipJS.scanForInlineTooltips
* @static
* @returns {undefined} Ne retourne aucune valeur
* @example <caption>Lancer la détection</caption>
* TooltipJS.scanForInlineTooltips();
*/
api.scanForInlineTooltips = function () {
_checkForInlineTooltips();
};
/**
* @description Association d'un TooltipJS pour un élément HTML donné
* <br><em><b>Note</b>: Il est aussi possible d'associer un TooltipJS à un élément sans appel JavaScript direct.</em>
* @alias TooltipJS.addTooltip
* @static
* @param {HTMLElement} elem La référence à l'élément HTML pour lequel associer le TooltipJS
* @param {String} toolTipText Le texte à afficher dans le TooltipJS
* @param {Number} [fadeDuration = 250] La durée en millisecondes de l'animation d'apparition du TooltipJS
* @param {String} [customStyle = ""] Le nom d'une classe CSS existante à utiliser pour affecter l'apparence du TooltipJS
* @param {Object} [positionMargins = {}] Le postionnement du TooltipJS en pixels en fonction de la position du curseur de la souris
* @param {number} [positionMargins.x = 15] Position en pixels du TooltipJS en x en rapport avec la position du curseur de la souris
* @param {number} [positionMargins.y = -15] Position en pixels du TooltipJS en y en rapport avec la position du curseur de la souris
* @param {Object} [boundsPadding = {}] Le padding à prendre en compte pour la fenêtre du navigateur et les rebords du TooltipJS
* @param {Number} [boundsPadding.top = 15] Le padding en pixels à prendre en compte en haut de la fenêtre
* @param {Number} [boundsPadding.right = 15] Le padding en pixels à prendre en compte à droite de la fenêtre
* @param {Number} [boundsPadding.bottom = 15] Le padding en pixels à prendre en compte en bas de la fenêtre
* @param {Number} [boundsPadding.left = 15] Le padding en pixels à prendre en compte à gauche de la fenêtre
* @param {Boolean} [fixed = false] Si l'on souhaite que le TooltipJS soit positionné en fonction du coin supérieur droit de l'élément de survol plutôt que la position de la souris
* @param {Boolean} [hideOnClick = false] Si l'on souhaite cacher le tooltip lors du clic sur l'élément
* @param {Number} [hideDelay = 0] Délai avant de cacher la tooltip (0 = toujours visible)
* @param {HTMLElement} [htmlTemplate = undefined] La référence à un conteneur HTML devant servir de tooltip. La classe "tooltip-active" est automatiquement ajoutée sur le conteneur une fois ce dernier actif et complètement visible.
* @returns {undefined} Ne retourne aucune valeur
* @example <caption>Associer un TooltipJS par code.</caption>
* TooltipJS.addTooltip(document.querySelector("#monElementId"), "Ceci est le texte du TooltipJS");
* //Utiliser les paramètres optionnels
* TooltipJS.addTooltip(document.querySelector("#monAutreElementId"), "Ceci est le texte d'un autre TooltipJS", 1000, "maClasse", {x:25, y:-30}, {top:0, right:0, bottom:10, left:0});
* @example <caption>Associer un TooltipJS directement dans le DOM.</caption>
* <input type="text" data-toolTipText="Veuillez inscrire votre identifiant" data-fadeDuration="1000" data-customStyle="maClasse" data-positionMargins="25,-30" data-boundsPadding="0,0,10,0" data-fixed name="login" size="100" placeholder="Ceci est un champs de formulaire"/>
* @example <caption>Associer un TooltipJS avec conteneur HTML directement dans le DOM. Comme il est impossible de passer une référence directe à un element HTML, il faut passer le selecteur qui permet de récupérer le conteneur HTML voulu.</caption>
* <select type="text" data-toolTipText="Ceci est un texte dynamique" data-htmlTemplate=".tip"></select>
*/
api.addTooltip = function (elem, toolTipText, fadeDuration, customStyle, positionMargins, boundsPadding, fixed, hideOnClick, hideDelay, htmlTemplate) {
if (elem == null) {
console.error("L'élément fourni n'existe pas");
return;
}
//not working on mobile for now
if (navigator.userAgent.match(/Mobi/)) {
return;
}
if (typeof _tipContainer === "undefined") {
_createTooltip(api.tooltipStyle);
}
if (typeof toolTipText === "undefined") {
throw new Error("Le texte du TooltipJS doit être défini pour l'élément");
} else if (toolTipText === "") {
throw new Error("Le texte du TooltipJS ne peut être vide pour l'élément");
}
if (typeof fixed != typeof true) {
fixed = false;
}
let defaultValues = {
fadeDuration: fadeDuration,
customStyle: customStyle,
positionMargins: positionMargins,
boundsPadding: boundsPadding,
};
_applyDefaultValue(defaultValues);
let tipO = {
elem: elem,
toolTipText: toolTipText,
onmouseover: elem.onmouseover,
onmousemove: elem.onmousemove,
onmouseout: elem.onmouseout,
fadeDuration: defaultValues.fadeDuration,
customStyle: defaultValues.customStyle,
positionMargins: defaultValues.positionMargins,
boundsPadding: defaultValues.boundsPadding,
fixed: fixed,
hideOnClick: hideOnClick == undefined ? false : hideOnClick,
hideDelay: hideDelay,
htmlTemplate: htmlTemplate,
};
elem.onmouseover = function (e) {
if (tipO.onmouseover !== null) {
tipO.onmouseover(e);
}
if (tipO.fixed) {
tipO.elemFixedRect = tipO.elem.getBoundingClientRect();
}
if (!_currentlyShowing) {
_showTooltip(e);
}
};
elem.onmousemove = function (e) {
let tipO = _allTooltips.get(e.currentTarget);
/* if (tipO.onmouseout !== null) {
tipO.onmouseout(e);
}*/
if (!tipO.fixed) {
_moveTooltip(e);
}
};
elem.onmouseout = function (e) {
let tipO = _allTooltips.get(e.currentTarget);
/*if (tipO.onmouseout !== null) {
tipO.onmouseout(e);
}*/
if (!e.currentTarget.contains(e.relatedTarget)) {
_hideTooltip(e);
}
};
if (tipO.hideOnClick) {
//hide TooltipJS on elemtent click
elem.addEventListener(
"click",
function (e) {
_hideTooltip(e);
},
{ capture: true }
);
}
if (tipO.htmlTemplate) {
const label = tipO.htmlTemplate.querySelector(".tooltip-label");
if (label) {
label.innerText = tipO.toolTipText;
}
}
//setting global TooltipJS value for this element reference
_allTooltips.set(elem, tipO);
};
/**
* @type {Function}
* @description Permet de mettre à jour le TooltipJS préalablement associé à un élément HTML dans la page
* <br><em><b>Note</b>: Il n'est pas possible de mettre à jour un TooltipJS sans appel JavaScript direct.</em>
* @alias TooltipJS.updateTooltip
* @static
* @param {HTMLElement} elem La référence à l'élément HTML pour lequel mettre à jour le TooltipJS
* @param {String} toolTipText Le texte à afficher dans le TooltipJS
* @param {Number} [fadeDuration = 250] La durée en millisecondes de l'animation d'apparition du TooltipJS
* @param {String} [customStyle = ""] Le nom d'une classe CSS existante à utiliser pour affecter l'apparence du TooltipJS
* @param {Object} [positionMargins = {}] Le postionnement du TooltipJS en pixels en fonction de la position du curseur de la souris
* @param {number} [positionMargins.x = 15] Position en pixels du TooltipJS en x en rapport avec la position du curseur de la souris
* @param {number} [positionMargins.y = -15] Position en pixels du TooltipJS en y en rapport avec la position du curseur de la souris
* @param {Object} [boundsPadding = {}] Le padding à prendre en compte pour la fenêtre du navigateur et les rebords du TooltipJS
* @param {Number} [boundsPadding.top = 15] Le padding en pixels à prendre en compte en haut de la fenêtre
* @param {Number} [boundsPadding.right = 15] Le padding en pixels à prendre en compte à droite de la fenêtre
* @param {Number} [boundsPadding.bottom = 15] Le padding en pixels à prendre en compte en bas de la fenêtre
* @param {Number} [boundsPadding.left = 15] Le padding en pixels à prendre en compte à gauche de la fenêtre
* @param {Boolean} [fixed = false] Si l'on souhaite que le TooltipJS soit positionné en fonction du coin supérieur droit de l'élément de survol plutôt que la position de la souris
* @param {Boolean} [hideOnClick = false] Si l'on souhaite cacher le tooltip lors du clic sur l'élément
* @param {Number} [hideDelay = 0] Délai avant de cacher la tooltip (0 = toujours visible)
* @param {HTMLElement} [htmlTemplate = undefined] La référence à un conteneur HTML devant servir de tooltip
* @returns {undefined} Ne retourne aucune valeur
* @example <caption>Mettre à jour un TooltipJS par code.</caption>
* TooltipJS.updateTooltip(document.querySelector("#monElementId"), "Ceci est le texte du TooltipJS mis à jour");
* //Utiliser les paramètres optionnels
* TooltipJS.updateTooltip(document.querySelector("#monAutreElementId"), "Ceci est le texte d'un autre TooltipJS mis à jour", 1000, "maClasse", {x:25, y:-30}, {top:0, right:0, bottom:10, left:0}, true);
*/
api.updateTooltip = function (elem, toolTipText, fadeDuration, customStyle, positionMargins, boundsPadding, fixed, hideOnClick, hideDelay) {
//not working on mobile for now
if (navigator.userAgent.match(/Mobi/)) {
return;
}
let tipO = _allTooltips.get(elem);
if (typeof tipO !== "undefined") {
if (_currentlyShowing && _currentlyShownElement === elem) {
_hideTooltip(elem, true);
}
if (typeof fixed != typeof true) {
fixed = false;
}
let defaultValues = {
fadeDuration: fadeDuration || tipO.fadeDuration,
customStyle: customStyle || tipO.customStyle,
positionMargins: positionMargins || tipO.positionMargins,
boundsPadding: boundsPadding || tipO.boundsPadding,
};
_applyDefaultValue(defaultValues);
tipO.toolTipText = toolTipText;
tipO.fadeDuration = defaultValues.fadeDuration;
tipO.customStyle = defaultValues.customStyle;
tipO.positionMargins = defaultValues.positionMargins;
tipO.boundsPadding = defaultValues.boundsPadding;
tipO.fixed = fixed || tipO.fixed;
tipO.hideOnClick = hideOnClick || tipO.hideOnClick;
tipO.hideDelay = hideDelay || tipO.hideDelay;
if (fixed) {
elem.onmousemove = tipO.mousemove;
tipO.elemFixedRect = elem.getBoundingClientRect();
} else {
elem.onmousemove = function (e) {
if (tipO.onmouseout !== null) {
tipO.onmouseout(e);
}
if (!tipO.fixed) {
_moveTooltip(e);
}
};
tipO.onmousemove = elem.onmousemove;
tipO.elemFixedRect = undefined;
}
_allTooltips.set(elem, tipO);
if (_currentlyShowing && _currentlyShownElement === elem) {
_showTooltip(tipO.elem, true, true);
}
} else {
throw new Error("L'élément choisi ne fait partie des éléments associé à un TooltipJS");
}
};
/**
* @description Détruit toutes les associations de TooltipJS précédemment créées
* @alias TooltipJS.removeAllTooltips
* @static
* @returns {undefined} Ne retourne aucune valeur
* @example <caption>Enlever toutes les associations de TooltipJS</caption>
* TooltipJS.removeAllTooltips();
*/
api.removeAllTooltips = function () {
_allTooltips.forEach(function (value, key, map) {
let tipO = _allTooltips.get(value.elem);
if (tipO.onmouseover !== null) {
value.elem.onmouseover = tipO.onmouseover;
} else value.elem.onmouseover = null;
if (tipO.onmouseout !== null) {
value.elem.onmouseout = tipO.onmouseout;
} else value.elem.onmouseout = null;
if (tipO.onmousemove !== null) {
value.elem.onmousemove = tipO.onmousemove;
} else value.elem.onmousemove = null;
});
_allTooltips.clear();
};
/**
* @description Prepares and show the current TooltipJS
* @param e MouseOver event
* @private
*/
let _showTooltip = function (e, checkRect, fadeBypass) {
clearTimeout(_hideDelayTimeoutID);
checkRect = typeof checkRect === "undefined" ? true : checkRect;
fadeBypass = typeof fadeBypass === "undefined" ? false : fadeBypass;
let tipO = _allTooltips.get(e.currentTarget || e);
if (tipO.htmlTemplate) {
_switchToHTMLTemplate(tipO.htmlTemplate);
} else _switchToDefaultTemplate();
if (tipO.htmlTemplate == (null || undefined)) {
_tipContainer.innerText = tipO.toolTipText;
}
_currentPositionMargins = tipO.positionMargins;
_currentBoundsPadding = tipO.boundsPadding;
if (tipO.customStyle !== null && tipO.htmlTemplate == (null || undefined)) {
_tipContainer.style.cssText = ""; //ie11 et edge n'aime pas style = "" (read only)
_applyStyle(api.tooltipStyle.BASE);
_tipContainer.classList.toggle(tipO.customStyle);
}
_tipContainer.setAttribute("data-fadeduration", tipO.fadeDuration);
_tipContainer.style.display = "block";
_currentTooltipRect = _tipContainer.getBoundingClientRect();
if (checkRect) {
_checkRectBoundsBeforePositioning(e);
}
if (tipO.elemFixedRect) {
_checkRectBoundsBeforeFixedPositioning(tipO.elemFixedRect);
}
_fadeInAnimation(fadeBypass ? 0 : tipO.fadeDuration, tipO.htmlTemplate);
_currentlyShowing = true;
_currentlyShownElement = e.currentTarget || e;
if (tipO.hideDelay > 0) {
_hideDelayTimeoutID = setTimeout(() => {
_hideTooltip(_currentlyShownElement);
}, tipO.hideDelay);
}
};
/**
* @description Moves the current TooltipJS
* @param e MouseMove event
* @private
*/
let _moveTooltip = function (e) {
_checkRectBoundsBeforePositioning(e);
};
/**
* @description Prepares and hides the current TooltipJS
* @param e MouseOut event
* @param codeTriggered Was the call triggered by user or code?
* @private
*/
let _hideTooltip = function (e, codeTriggered) {
codeTriggered = typeof codeTriggered === "undefined" ? false : codeTriggered;
clearTimeout(_hideDelayTimeoutID);
window.cancelAnimationFrame(_currentAnimationFrameID);
let tipO = _allTooltips.get(e.currentTarget || e);
if (tipO.customStyle !== null && tipO.htmlTemplate == (null || undefined)) {
_tipContainer.classList.remove(tipO.customStyle);
_applyStyle(api.tooltipStyle.DEFAULT);
}
if (tipO.htmlTemplate) {
_tipContainer.classList.remove("tooltip-active");
}
_tipContainer.style.display = "none";
_tipContainer.style.opacity = 0;
if (!codeTriggered) {
_currentlyShowing = false;
_currentlyShownElement = undefined;
}
};
/**
* Will apply default values to the corresponding parameters
* @param defaultValues
* @private
*/
let _applyDefaultValue = function (defaultValues) {
defaultValues.fadeDuration = typeof defaultValues.fadeDuration === "undefined" ? _fadeDuration : defaultValues.fadeDuration;
defaultValues.customStyle = typeof defaultValues.customStyle === "undefined" ? null : defaultValues.customStyle;
defaultValues.positionMargins = typeof defaultValues.positionMargins === "undefined" ? _tipPositionMargins : defaultValues.positionMargins;
defaultValues.positionMargins.x = typeof defaultValues.positionMargins.x === "undefined" ? _tipPositionMargins.x : defaultValues.positionMargins.x;
defaultValues.positionMargins.y = typeof defaultValues.positionMargins.y === "undefined" ? _tipPositionMargins.y : defaultValues.positionMargins.y;
defaultValues.boundsPadding = typeof defaultValues.boundsPadding === "undefined" ? _tipBoundsPadding : defaultValues.boundsPadding;
defaultValues.boundsPadding.top = typeof defaultValues.boundsPadding.top === "undefined" ? _tipBoundsPadding.top : defaultValues.boundsPadding.top;
defaultValues.boundsPadding.right = typeof defaultValues.boundsPadding.right === "undefined" ? _tipBoundsPadding.right : defaultValues.boundsPadding.right;
defaultValues.boundsPadding.bottom = typeof defaultValues.boundsPadding.bottom === "undefined" ? _tipBoundsPadding.bottom : defaultValues.boundsPadding.bottom;
defaultValues.boundsPadding.left = typeof defaultValues.boundsPadding.left === "undefined" ? _tipBoundsPadding.left : defaultValues.boundsPadding.left;
};
/**
* @description Sets the top and left properties of the TooltipJS HTMLElement
* @param x The position from the left
* @param y The position from the top
* @private
*/
let _setTooltipPosition = function (x, y) {
if (isNaN(x)) {
throw new Error(234);
}
_tipContainer.style.left = x + _currentPositionMargins.x + "px";
_tipContainer.style.top = y + _currentPositionMargins.y + "px";
};
/**
* @description Creates the TooltipJS container HTMLElement that will be used for all tooltips
* @param {Object} style The object from with to extract the style properties and values
* @returns {undefined}
* @private
*/
let _createTooltip = function (style) {
_tipContainer = document.createElement("div");
_originalTipContainer = _tipContainer;
//basic styling
_applyStyle(style.BASE);
//visual styling
_applyStyle(style.DEFAULT);
_tipContainer.setAttribute("id", "tipContainer");
document.body.appendChild(_tipContainer);
};
/**
* @description Switch the current tooltipContainer to user the htmlTemplate
* @private
* @param {HTMLElement} templateElement HTML container to use
*/
let _switchToHTMLTemplate = function (templateElement) {
_tipContainer = templateElement;
};
/**
* @description Switch the current tooltipContainer to user the default container
* @private
*/
let _switchToDefaultTemplate = function () {
_tipContainer = _originalTipContainer;
};
/**
* @description Will check if any HTMLElement in the DOM is setting TooltipJS usage with the data- attributes. If so, a TooltipJS is prepared from each one found
* @private
* @return {undefined}
*/
let _checkForInlineTooltips = function () {
let tts = document.querySelectorAll("[data-tooltiptext]");
for (let i = 0; i < tts.length; i++) {
let posMarginsInline = tts[i].getAttribute("data-positionMargins") || _tipPositionMargins;
let posMargins = {};
posMargins.x = typeof posMarginsInline === "string" ? parseInt(tts[i].getAttribute("data-positionMargins").split(",")[0]) : _tipPositionMargins.x;
posMargins.y = typeof posMarginsInline === "string" ? parseInt(tts[i].getAttribute("data-positionMargins").split(",")[1]) : _tipPositionMargins.y;
let boundsPaddingInline = tts[i].getAttribute("data-boundsPadding") || _tipBoundsPadding;
let boundsPadding = {};
boundsPadding.top = typeof boundsPaddingInline === "string" ? parseInt(tts[i].getAttribute("data-boundsPadding").split(",")[0]) : _tipBoundsPadding.top;
boundsPadding.right = typeof boundsPaddingInline === "string" ? parseInt(tts[i].getAttribute("data-boundsPadding").split(",")[1]) : _tipBoundsPadding.right;
boundsPadding.bottom = typeof boundsPaddingInline === "string" ? parseInt(tts[i].getAttribute("data-boundsPadding").split(",")[2]) : _tipBoundsPadding.bottom;
boundsPadding.left = typeof boundsPaddingInline === "string" ? parseInt(tts[i].getAttribute("data-boundsPadding").split(",")[3]) : _tipBoundsPadding.left;
let fixed = tts[i].hasAttribute("data-fixed");
let hideOnClick = tts[i].hasAttribute("data-hideOnClick");
let hideDelay = tts[i].hasAttribute("data-hideOnClick") ? tts[i].getAttribute("data-hideDelay") : 0;
let htmlTemplate = tts[i].hasAttribute("data-htmlTemplate") ? document.querySelector(tts[i].getAttribute("data-htmlTemplate")) : null;
api.addTooltip(tts[i], tts[i].getAttribute("data-tooltiptext"), tts[i].getAttribute("data-fadeDuration") || _fadeDuration, tts[i].getAttribute("data-customStyle") || null, posMargins, boundsPadding, fixed, hideOnClick, hideDelay, htmlTemplate);
}
};
/**
* @description Will apply style from an object to the tip container HTMLElement
* @param style The object from with to extract the style properties and values
* @private
* @return {undefined}
*/
let _applyStyle = function (style) {
//basic styling
for (let prop in style) {
_tipContainer.style[prop] = style[prop];
}
};
/**
* @description Will start a fade in animation on the tip container HTMLElement
* @param duration The duration in ms of the fade in animation
* @private
* @return {undefined}
*/
let _fadeInAnimation = function (duration, htmlTemplate) {
let end = +new Date() + parseInt(duration);
let fadeInStep = function () {
let current = +new Date();
let remaining = end - current;
if (remaining < 60) {
_tipContainer.style.opacity = 1;
if (htmlTemplate) {
_tipContainer.classList.add("tooltip-active");
}
return;
} else {
let rate = 1 - remaining / duration;
_tipContainer.style.opacity = rate;
}
_currentAnimationFrameID = window.requestAnimationFrame(fadeInStep);
};
fadeInStep();
};
/**
* @description Will try to guess if the TooltipJS will be hidden by the browser window's sides
* @param e MouseMove event reference
* @private
*/
let _checkRectBoundsBeforePositioning = function (e) {
let rect = {};
let html = document.documentElement;
rect.left = (e.clientX || _lastKnowMousePosition.clientX) + _currentPositionMargins.x;
rect.top = (e.clientY || _lastKnowMousePosition.clientY) + _currentPositionMargins.y;
rect.width = _currentTooltipRect.width;
rect.height = _currentTooltipRect.height;
rect.right = rect.left + _currentTooltipRect.width;
rect.bottom = rect.top + _currentTooltipRect.height;
let inViewResults = _isRectInView(rect, true, true, {
left: 0,
right: 0,
top: 0,
bottom: 0,
});
let buffer = { left: 0, top: 0 };
if (!inViewResults.inview) {
if (!inViewResults.leftIsVisible) {
buffer.left = rect.left + _currentPositionMargins.x - _currentBoundsPadding.left;
} else if (!inViewResults.rightIsVisible) {
buffer.left = rect.right - (html.clientWidth || window.innerWidth) + _currentPositionMargins.x + _currentBoundsPadding.right;
}
if (!inViewResults.topIsVisible) {
buffer.top = rect.top + _currentPositionMargins.y - _currentBoundsPadding.top;
} else if (!inViewResults.bottomIsVisible) {
buffer.top = rect.bottom - (html.clientHeight || window.innerHeight) + _currentPositionMargins.y + _currentBoundsPadding.bottom;
}
_setTooltipPosition(rect.left - buffer.left, rect.top - buffer.top);
} else {
_setTooltipPosition(rect.left, rect.top);
}
_lastKnowMousePosition.clientX = e.clientX || _lastKnowMousePosition.clientX;
_lastKnowMousePosition.clientY = e.clientY || _lastKnowMousePosition.clientY;
};
/**
* @description Will position the TooltipJS in relation the the fixed element it is tied to
* @param rect The fixed element rect
* @private
*/
let _checkRectBoundsBeforeFixedPositioning = function (rect) {
let buffer = {
left: _currentPositionMargins.x,
top: _currentPositionMargins.y,
};
_setTooltipPosition(rect.left + rect.width + buffer.left, rect.top + buffer.top);
};
/**
* @description Checks if the TooltipJS rect is fully visible or partially visible
* @param rect The TooltipJS rect
* @param fullyVisibleX Should we check for full visibility on X axis?
* @param fullVisibleY Should we check for full visibility on Y axis?
* @param offset An object describing offsets to use for top, right, bottom and left of the TooltipJS
* @return {{inview: boolean, leftIsVisible: boolean, topIsVisible: boolean, rightIsVisible: boolean, bottomIsVisible: boolean, isFullyVisibleX: boolean, isFullyVisibleY: boolean}}
* @private
*/
let _isRectInView = function (rect, fullyVisibleX, fullVisibleY, offset) {
let html = document.documentElement;
let isPartiallyVisibleX = rect.right >= offset.left && rect.left <= (window.innerWidth || html.clientWidth) - offset.right;
let isPartiallyVisibleY = rect.bottom >= offset.top && rect.top <= (window.innerHeight || html.clientHeight) - offset.bottom;
let leftIsVisible = rect.left + _currentPositionMargins.x - _currentBoundsPadding.left >= offset.left;
let rightIsVisible = rect.right <= (html.clientWidth || window.innerWidth) - (offset.right + _currentPositionMargins.x + _currentBoundsPadding.right);
let isFullyVisibleX = leftIsVisible && rightIsVisible && rect.left + rect.width <= (window.innerWidth || html.clientWidth) - offset.right;
let topIsVisible = rect.top + _currentPositionMargins.y - _currentBoundsPadding.top >= offset.top;
let bottomIsVisible = rect.bottom <= (html.clientHeight || window.innerHeight) - (offset.bottom + _currentPositionMargins.y + _currentBoundsPadding.bottom);
let isFullyVisibleY = topIsVisible && bottomIsVisible && rect.top + rect.height <= (window.innerHeight || html.clientHeight) - offset.bottom;
let result = fullyVisibleX && fullVisibleY ? isFullyVisibleX && isFullyVisibleY : fullyVisibleX ? isFullyVisibleX && isPartiallyVisibleY : fullVisibleY ? isFullyVisibleY && isPartiallyVisibleX : isPartiallyVisibleX && isPartiallyVisibleY;
return {
inview: result,
leftIsVisible: leftIsVisible,
topIsVisible: topIsVisible,
rightIsVisible: rightIsVisible,
bottomIsVisible: bottomIsVisible,
isFullyVisibleX: isFullyVisibleX,
isFullyVisibleY: isFullyVisibleY,
};
};
/**
* @description Reference to the TooltipJS container HTMLElement
* @private
*/
let _tipContainer;
/**
* @description Is the TooltipJS container currently showed?
* @private
*/
let _currentlyShowing = false;
/**
* @description The last known mouse position
* @private
*/
let _lastKnowMousePosition = { clientX: 0, clientY: 0 };
let _currentlyShownElement;
/**
* @description Default values to use as position margins from the mouse cursor's position
* @private
*/
let _tipPositionMargins = { x: 15, y: -15 };
/**
* @description Default values to use as bounds padding from the browser window's rect
* @private
*/
let _tipBoundsPadding = { left: 15, right: 15, top: 15, bottom: 15 };
/**
* @description A dictionary that reference all tooltips currently existing inside the page
* @private
*/
let _allTooltips = new Map();
/**
* @description Default value to use for fade in animation duration
* @private
*/
let _fadeDuration = 250;
/**
* @description Reference to the TooltipJS container HTMLElement current rect
* @private
*/
let _currentTooltipRect;
/**
* @description Reference to the TooltipJS container HTMLElement current position margins
* @private
*/
let _currentPositionMargins;
/**
* @description Reference to the TooltipJS container HTMLElement current bounds padding
* @private
*/
let _currentBoundsPadding;
/**
* @description Reference to the current fadeIn animation frame
* @private
*/
let _currentAnimationFrameID = 0;
/**
* @description Reference to the current hide timeout
* @private
*/
let _hideDelayTimeoutID = 0;
let _originalTipContainer;
//set the api object to be public
return api;
}
};
export default new TooltipJS();