| 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 |
let tooltipText = elm.getAttribute('data-photonic-tooltip'); |
| 21 |
tooltipText = strip_tags(tooltipText, '<br><strong><em><b><i><p>'); |
| 22 |
|
| 23 |
if (tooltipText !== '') { |
| 24 |
elm.setAttribute('title', ''); // Blank out the regular title |
| 25 |
|
| 26 |
// elemEdges relative to the viewport. |
| 27 |
elemEdges = elm.getBoundingClientRect(); |
| 28 |
|
| 29 |
const tooltipTextNode = document.createTextNode(tooltipText); |
| 30 |
tooltip.innerHTML = tooltipText; // ''; // Reset, or upon refresh the node gets repeated |
| 31 |
// tooltip.appendChild(tooltipTextNode); |
| 32 |
|
| 33 |
// Remove no-display + set the correct classname based on the position |
| 34 |
// of the elm. |
| 35 |
if (elemEdges.left > window.innerWidth - 100) { |
| 36 |
tooltip.className = 'photonic-tooltip-container tooltip-left'; |
| 37 |
} |
| 38 |
else if ((elemEdges.left + (elemEdges.width / 2)) < 100) { |
| 39 |
tooltip.className = 'photonic-tooltip-container tooltip-right'; |
| 40 |
} |
| 41 |
else { |
| 42 |
tooltip.className = 'photonic-tooltip-container tooltip-center'; |
| 43 |
} |
| 44 |
} |
| 45 |
} |
| 46 |
|
| 47 |
function position(tooltip, elm) { |
| 48 |
const tooltipText = elm.getAttribute('data-photonic-tooltip'); |
| 49 |
if (tooltipText !== '') { |
| 50 |
if (elemEdges === undefined) { |
| 51 |
elemEdges = elm.getBoundingClientRect(); |
| 52 |
} |
| 53 |
|
| 54 |
// 10 = arrow height |
| 55 |
const elm_top = elemEdges.top + elemEdges.height + window.scrollY; |
| 56 |
const viewport_edges = window.innerWidth - 100; |
| 57 |
|
| 58 |
// Position tooltip on the left side of the elm if the elm touches |
| 59 |
// the viewports right edge and elm width is < 50px. |
| 60 |
if (elemEdges.left + window.scrollX > viewport_edges && elemEdges.width < 50) { |
| 61 |
tooltip.style.left = (elemEdges.left + window.scrollX - (tooltip.offsetWidth + elemEdges.width)) + 'px'; |
| 62 |
tooltip.style.top = elm.offsetTop + 'px'; |
| 63 |
// Position tooltip on the left side of the elm if the elm touches |
| 64 |
// the viewports right edge and elm width is > 50px. |
| 65 |
} |
| 66 |
else if (elemEdges.left + window.scrollX > viewport_edges && elemEdges.width > 50) { |
| 67 |
tooltip.style.left = (elemEdges.left + window.scrollX - tooltip.offsetWidth - 20) + 'px'; |
| 68 |
tooltip.style.top = elm.offsetTop + 'px'; |
| 69 |
} |
| 70 |
else if ((elemEdges.left + window.scrollX + (elemEdges.width / 2)) < 100) { |
| 71 |
// position tooltip on the right side of the elm. |
| 72 |
tooltip.style.left = (elemEdges.left + window.scrollX + elemEdges.width + 20) + 'px'; |
| 73 |
tooltip.style.top = elm.offsetTop + 'px'; |
| 74 |
} |
| 75 |
else { |
| 76 |
// Position the toolbox in the center of the elm. |
| 77 |
const centered = (elemEdges.left + window.scrollX + (elemEdges.width / 2)) - (tooltip.offsetWidth / 2); |
| 78 |
tooltip.style.left = centered + 'px'; |
| 79 |
tooltip.style.top = elm_top + 'px'; |
| 80 |
} |
| 81 |
} |
| 82 |
} |
| 83 |
|
| 84 |
function show(evt) { |
| 85 |
create(tooltip, evt.currentTarget); |
| 86 |
position(tooltip, evt.currentTarget); |
| 87 |
} |
| 88 |
|
| 89 |
function hide(evt) { |
| 90 |
tooltip.className = tooltipClass + ' no-display'; |
| 91 |
if (tooltip.innerText !== '') { |
| 92 |
tooltip.removeChild(tooltip.firstChild); |
| 93 |
tooltip.removeAttribute('style'); |
| 94 |
const element = evt.currentTarget; |
| 95 |
element.setAttribute('title', element.getAttribute('data-photonic-tooltip')); |
| 96 |
} |
| 97 |
} |
| 98 |
|
| 99 |
function init() { |
| 100 |
tooltipElems = document.documentElement.querySelectorAll(selector); |
| 101 |
tooltip = document.documentElement.querySelector(tooltip_element); |
| 102 |
tooltipClass = tooltip_element.replace(/^\.+/g, ''); |
| 103 |
|
| 104 |
if (tooltip === null || tooltip.length === 0) { |
| 105 |
tooltip = document.createElement('div'); |
| 106 |
tooltip.className = tooltipClass + ' no-display'; |
| 107 |
document.body.appendChild(tooltip); |
| 108 |
} |
| 109 |
|
| 110 |
tooltipElems.forEach(elm => { |
| 111 |
elm.removeEventListener('mouseenter', show); |
| 112 |
elm.removeEventListener('mouseleave', hide); |
| 113 |
|
| 114 |
elm.addEventListener('mouseenter', show, false); |
| 115 |
elm.addEventListener('mouseleave', hide, false); |
| 116 |
}); |
| 117 |
} |
| 118 |
|
| 119 |
init(); |
| 120 |
}; |
| 121 |
|
| 122 |
|