1(function () { 2 'use strict'; 3 4 var VERSION = (window.TuriTopTooltipsConfig && window.TuriTopTooltipsConfig.version) || '3.0.0'; 5 var ANCHOR_SELECTOR = '.turitop-tooltip-anchor'; 6 var overlays = []; 7 var tooltip = null; 8 var activeOverlay = null; 9 var raf = null; 10 11 window.turitopTooltipsVersion = VERSION; 12 13 function ready(fn) { 14 if (document.readyState === 'loading') { 15 document.addEventListener('DOMContentLoaded', fn, { once: true }); 16 } else { 17 fn(); 18 } 19 } 20 21 function getTooltip() { 22 if (tooltip && document.body.contains(tooltip)) { 23 return tooltip; 24 } 25 26 tooltip = document.createElement('div'); 27 tooltip.className = 'turitop-tooltip-floating'; 28 tooltip.setAttribute('role', 'tooltip'); 29 tooltip.setAttribute('aria-hidden', 'true'); 30 document.body.appendChild(tooltip); 31 return tooltip; 32 } 33 34 function getText(anchor) { 35 return (anchor.getAttribute('data-turitop-tooltip') || anchor.getAttribute('aria-label') || '').trim(); 36 } 37 38 function createOverlay(anchor, index) { 39 if (anchor._turitopOverlay) { 40 return anchor._turitopOverlay; 41 } 42 43 var text = getText(anchor); 44 var label = (anchor.textContent || '?').trim() || '?'; 45 46 var button = document.createElement('button'); 47 button.type = 'button'; 48 button.className = 'turitop-tooltip-overlay-button'; 49 button.textContent = label; 50 button.setAttribute('aria-label', text); 51 button.setAttribute('data-turitop-tooltip', text); 52 button.setAttribute('data-turitop-index', String(index)); 53 54 button._turitopAnchor = anchor; 55 anchor._turitopOverlay = button; 56 anchor.classList.add('turitop-tooltip-anchor-enhanced'); 57 58 button.addEventListener('mouseenter', function () { 59 show(button); 60 }); 61 62 button.addEventListener('mouseleave', function () { 63 hide(); 64 }); 65 66 button.addEventListener('focus', function () { 67 show(button); 68 }); 69 70 button.addEventListener('blur', function () { 71 hide(); 72 }); 73 74 button.addEventListener('click', function (event) { 75 event.preventDefault(); 76 event.stopPropagation(); 77 78 if (activeOverlay === button && tooltip && tooltip.classList.contains('is-visible')) { 79 hide(); 80 } else { 81 show(button); 82 } 83 }); 84 85 document.body.appendChild(button); 86 overlays.push(button); 87 return button; 88 } 89 90 function updateOverlayPosition(button) { 91 var anchor = button._turitopAnchor; 92 93 if (!anchor || !document.body.contains(anchor)) { 94 button.style.display = 'none'; 95 return; 96 } 97 98 var rect = anchor.getBoundingClientRect(); 99 var viewportWidth = window.innerWidth || document.documentElement.clientWidth; 100 var viewportHeight = window.innerHeight || document.documentElement.clientHeight; 101 102 if ( 103 rect.width <= 0 || 104 rect.height <= 0 || 105 rect.bottom < 0 || 106 rect.right < 0 || 107 rect.top > viewportHeight || 108 rect.left > viewportWidth 109 ) { 110 button.style.display = 'none'; 111 return; 112 } 113 114 button.style.display = 'inline-flex'; 115 button.style.left = Math.round(rect.left) + 'px'; 116 button.style.top = Math.round(rect.top) + 'px'; 117 button.style.width = Math.max(18, Math.round(rect.width)) + 'px'; 118 button.style.height = Math.max(18, Math.round(rect.height)) + 'px'; 119 } 120 121 function updateAllOverlayPositions() { 122 overlays.forEach(updateOverlayPosition); 123 124 if (activeOverlay && tooltip && tooltip.classList.contains('is-visible')) { 125 positionTooltip(activeOverlay, tooltip); 126 } 127 } 128 129 function scheduleUpdate() { 130 if (raf) { 131 return; 132 } 133 134 raf = window.requestAnimationFrame(function () { 135 raf = null; 136 updateAllOverlayPositions(); 137 }); 138 } 139 140 function positionTooltip(button, tip) { 141 var rect = button.getBoundingClientRect(); 142 var padding = 8; 143 var gap = 10; 144 145 tip.style.left = '-9999px'; 146 tip.style.top = '-9999px'; 147 tip.classList.add('is-visible'); 148 149 var tipRect = tip.getBoundingClientRect(); 150 var viewportWidth = window.innerWidth || document.documentElement.clientWidth;
151 var viewportHeight = window.innerHeight || document.documentElement.clientHeight; 152 153 var left = rect.left + (rect.width / 2) - (tipRect.width / 2); 154 var top = rect.top - tipRect.height - gap; 155 var placement = 'top'; 156 157 if (top < padding) { 158 top = rect.bottom + gap; 159 placement = 'bottom'; 160 } 161 162 if (left < padding) { 163 left = padding; 164 } 165 166 if (left + tipRect.width > viewportWidth - padding) { 167 left = viewportWidth - tipRect.width - padding; 168 } 169 170 if (top + tipRect.height > viewportHeight - padding) { 171 top = Math.max(padding, viewportHeight - tipRect.height - padding); 172 } 173 174 var arrowLeft = (rect.left + rect.width / 2) - left; 175 if (arrowLeft < 14) arrowLeft = 14; 176 if (arrowLeft > tipRect.width - 14) arrowLeft = tipRect.width - 14; 177 178 tip.dataset.placement = placement; 179 tip.style.setProperty('--turitop-arrow-left', arrowLeft + 'px'); 180 tip.style.left = Math.round(left) + 'px'; 181 tip.style.top = Math.round(top) + 'px'; 182 } 183 184 function show(button) { 185 if (!button) { 186 return false; 187 } 188 189 updateOverlayPosition(button); 190 191 var text = (button.getAttribute('data-turitop-tooltip') || button.getAttribute('aria-label') || '').trim(); 192 if (!text) { 193 return false; 194 } 195 196 var tip = getTooltip(); 197 tip.textContent = text; 198 tip.setAttribute('aria-hidden', 'false'); 199 200 if (activeOverlay && activeOverlay !== button) { 201 activeOverlay.classList.remove('is-active'); 202 } 203 204 activeOverlay = button; 205 activeOverlay.classList.add('is-active'); 206 207 positionTooltip(button, tip); 208 return true; 209 } 210 211 function hide() { 212 if (activeOverlay) { 213 activeOverlay.classList.remove('is-active'); 214 } 215 216 activeOverlay = null; 217 218 if (tooltip) { 219 tooltip.classList.remove('is-visible'); 220 tooltip.setAttribute('aria-hidden', 'true'); 221 } 222 } 223 224 function enhance() { 225 var anchors = document.querySelectorAll(ANCHOR_SELECTOR); 226 227 anchors.forEach(function (anchor, index) { 228 createOverlay(anchor, index); 229 }); 230 231 updateAllOverlayPositions(); 232 } 233 234 function init() { 235 enhance(); 236 237 window.addEventListener('scroll', scheduleUpdate, true); 238 window.addEventListener('resize', scheduleUpdate); 239 window.addEventListener('orientationchange', scheduleUpdate); 240 241 document.addEventListener('click', function (event) { 242 if (activeOverlay && !event.target.closest('.turitop-tooltip-overlay-button')) { 243 hide(); 244 } 245 }, true); 246 247 document.addEventListener('keydown', function (event) { 248 if (event.key === 'Escape') { 249 hide(); 250 } 251 }, true); 252 253 if (window.MutationObserver) { 254 var observer = new MutationObserver(function () { 255 enhance(); 256 scheduleUpdate(); 257 }); 258 259 observer.observe(document.body, { 260 childList: true, 261 subtree: true 262 }); 263 } 264 265 /* Layout can shift after fonts/images/cards finish loading. */ 266 window.setTimeout(updateAllOverlayPositions, 250); 267 window.setTimeout(updateAllOverlayPositions, 750); 268 window.setTimeout(updateAllOverlayPositions, 1500); 269 window.setTimeout(updateAllOverlayPositions, 3000); 270 } 271 272 ready(init); 273 274 window.TuriTopTooltips = { 275 version: VERSION, 276 enhance: enhance, 277 update: updateAllOverlayPositions, 278 showFirst: function () { 279 enhance(); 280 return show(overlays[0]); 281 }, 282 debug: function () { 283 var firstAnchor = document.querySelector(ANCHOR_SELECTOR); 284 var firstOverlay = overlays[0] || null; 285 286 return { 287 version: VERSION, 288 anchors: document.querySelectorAll(ANCHOR_SELECTOR).length, 289 overlays: overlays.length, 290 firstAnchor: firstAnchor, 291 firstAnchorRect: firstAnchor ? firstAnchor.getBoundingClientRect() : null, 292 firstOverlay: firstOverlay, 293 firstOverlayRect: firstOverlay ? firstOverlay.getBoundingClientRect() : null, 294 elementAtOverlayCenter: firstOverlay ? document.elementFromPoint( 295 firstOverlay.getBoundingClientRect().left + firstOverlay.getBoundingClientRect().width / 2, 296 firstOverlay.getBoundingClientRect().top + firstOverlay.getBoundingClientRect().height / 2 297 ) : null, 298 tooltip: tooltip 299 }; 300 } 301 }; 302})();
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.