| 1 |
/* |
| 2 |
* Photonic Tooltip |
| 3 |
* License: MIT |
| 4 |
*/ |
| 5 |
|
| 6 |
export const Tooltip = function (selector, tooltip_element) { |
| 7 |
let tooltip, tooltipClass, elemEdges, tooltipElems; |
| 8 |
|
| 9 |
// From https://locutus.io/php/strings/strip_tags/, or https://stackoverflow.com/questions/5601903/jquery-almost-equivalent-of-phps-strip-tags/46483672#46483672 |
| 10 |
function strip_tags(input, allowed) { |
| 11 |
allowed = (((allowed || '') + '').toLowerCase().match(/<[a-z][a-z0-9]*>/g) || []).join('') |
| 12 |
const tags = /<\/?([a-z][a-z0-9]*)\b[^>]*>/gi |
| 13 |
const commentsAndPhpTags = /<!--[\s\S]*?-->|<\?(?:php)?[\s\S]*?\?>/gi |
| 14 |
return input.replace(commentsAndPhpTags, '').replace(tags, function ($0, $1) { |
| 15 |
return allowed.indexOf('<' + $1.toLowerCase() + '>') > -1 ? $0 : '' |
| 16 |
}); |
| 17 |
} |
| 18 |
|
| 19 |
function create(tooltip, elm) { |
| 20 |
const tooltipText = elm.getAttribute('data-photonic-tooltip'); |
| 21 |
if (tooltipText !== '') { |
| 22 |
elm.setAttribute('title', ''); // Blank out the regular title |
| 23 |
|
| 24 |
// elemEdges relative to the viewport. |
| 25 |
elemEdges = elm.getBoundingClientRect(); |
| 26 |
|
| 27 |
const tooltipTextNode = document.createTextNode(tooltipText); |
| 28 |
tooltip.innerHTML = ''; // Reset, or upon refresh the node gets repeated |
| 29 |
tooltip.appendChild(tooltipTextNode); |
| 30 |
|
| 31 |
// Remove no-display + set the correct classname based on the position |
| 32 |
// of the elm. |
| 33 |
if (elemEdges.left > window.innerWidth - 100) { |
| 34 |
tooltip.className = 'photonic-tooltip-container tooltip-left'; |
| 35 |
} |
| 36 |
else if ((elemEdges.left + (elemEdges.width / 2)) < 100) { |
| 37 |
tooltip.className = 'photonic-tooltip-container tooltip-right'; |
| 38 |
} |
| 39 |
else { |
| 40 |
tooltip.className = 'photonic-tooltip-container tooltip-center'; |
| 41 |
} |
| 42 |
} |
| 43 |
} |
| 44 |
|
| 45 |
function position(tooltip, elm) { |
| 46 |
const tooltipText = elm.getAttribute('data-photonic-tooltip'); |
| 47 |
if (tooltipText !== '') { |
| 48 |
if (elemEdges === undefined) { |
| 49 |
elemEdges = elm.getBoundingClientRect(); |
| 50 |
} |
| 51 |
|
| 52 |
// 10 = arrow height |
| 53 |
const elm_top = elemEdges.top + elemEdges.height + window.scrollY; |
| 54 |
const viewport_edges = window.innerWidth - 100; |
| 55 |
|
| 56 |
// Position tooltip on the left side of the elm if the elm touches |
| 57 |
// the viewports right edge and elm width is < 50px. |
| 58 |
if (elemEdges.left + window.scrollX > viewport_edges && elemEdges.width < 50) { |
| 59 |
tooltip.style.left = (elemEdges.left + window.scrollX - (tooltip.offsetWidth + elemEdges.width)) + 'px'; |
| 60 |
tooltip.style.top = elm.offsetTop + 'px'; |
| 61 |
// Position tooltip on the left side of the elm if the elm touches |
| 62 |
// the viewports right edge and elm width is > 50px. |
| 63 |
} |
| 64 |
else if (elemEdges.left + window.scrollX > viewport_edges && elemEdges.width > 50) { |
| 65 |
tooltip.style.left = (elemEdges.left + window.scrollX - tooltip.offsetWidth - 20) + 'px'; |
| 66 |
tooltip.style.top = elm.offsetTop + 'px'; |
| 67 |
} |
| 68 |
else if ((elemEdges.left + window.scrollX + (elemEdges.width / 2)) < 100) { |
| 69 |
// position tooltip on the right side of the elm. |
| 70 |
tooltip.style.left = (elemEdges.left + window.scrollX + elemEdges.width + 20) + 'px'; |
| 71 |
tooltip.style.top = elm.offsetTop + 'px'; |
| 72 |
} |
| 73 |
else { |
| 74 |
// Position the toolbox in the center of the elm. |
| 75 |
const centered = (elemEdges.left + window.scrollX + (elemEdges.width / 2)) - (tooltip.offsetWidth / 2); |
| 76 |
tooltip.style.left = centered + 'px'; |
| 77 |
tooltip.style.top = elm_top + 'px'; |
| 78 |
} |
| 79 |
} |
| 80 |
} |
| 81 |
|
| 82 |
function show(evt) { |
| 83 |
create(tooltip, evt.currentTarget); |
| 84 |
position(tooltip, evt.currentTarget); |
| 85 |
} |
| 86 |
|
| 87 |
function hide(evt) { |
| 88 |
tooltip.className = tooltipClass + ' no-display'; |
| 89 |
if (tooltip.innerText !== '') { |
| 90 |
tooltip.removeChild(tooltip.firstChild); |
| 91 |
tooltip.removeAttribute('style'); |
| 92 |
const element = evt.currentTarget; |
| 93 |
element.setAttribute('title', element.getAttribute('data-photonic-tooltip')); |
| 94 |
} |
| 95 |
} |
| 96 |
|
| 97 |
function init() { |
| 98 |
tooltipElems = document.documentElement.querySelectorAll(selector); |
| 99 |
tooltip = document.documentElement.querySelector(tooltip_element); |
| 100 |
tooltipClass = tooltip_element.replace(/^\.+/g, ''); |
| 101 |
|
| 102 |
if (tooltip === null || tooltip.length === 0) { |
| 103 |
tooltip = document.createElement('div'); |
| 104 |
tooltip.className = tooltipClass + ' no-display'; |
| 105 |
document.body.appendChild(tooltip); |
| 106 |
} |
| 107 |
|
| 108 |
tooltipElems.forEach(elm => { |
| 109 |
elm.removeEventListener('mouseenter', show); |
| 110 |
elm.removeEventListener('mouseleave', hide); |
| 111 |
|
| 112 |
elm.addEventListener('mouseenter', show, false); |
| 113 |
elm.addEventListener('mouseleave', hide, false); |
| 114 |
}); |
| 115 |
} |
| 116 |
|
| 117 |
init(); |
| 118 |
}; |
| 119 |
|
| 120 |
|