1/* 2 to show tooltip on hover add tooltip="xxx" attribute to an element 3 if element has 'text-with-ellipsis' class, the tooltip attr can be empty - 4 the whole text will be shown in this case if ellipsis is applied. 5 6 By default, tooltip is positioned above the element, aligned to its left 7 if there is no space for such a positioning, the tooltip may be shown underneath and right-aligned 8 This default can be changed to 'below by default' 9 10 On devices that support touch events, mouseenter event is fired on tap so we need 11 to check if it is a touch event or not. 12*/ 13 14const touchEvent = { 15 isActive : false, 16 reset : function() { 17 this.isActive = false; 18 }, 19 set: function() { 20 this.isActive = true; 21 setTimeout(this.reset, 30); 22 }, 23 active : function() { 24 const active = this.isActive; 25 if (active) this.reset(); 26 return active; 27 } 28} 29 30const tooltip = { 31 disabledNode : null, 32 remove : function() { 33 $('.tooltip').remove(); 34 }, 35 show : function(el, below) { 36 below = below || false; //true / false = tooltip is below / above the element, above by default 37 this.remove(); 38 if (this.disabledNode && this.disabledNode.contains(el)) { 39 return; 40 } 41 if (touchEvent.active()) { 42 return; 43 } 44 let text = el.getAttribute('tooltip'); 45 if (text === null) { 46 return; 47 } 48 text = text.trim(); 49 const $el = $(el); 50 const ellipsis = $el.hasClass('text-with-ellipsis'); 51 if (!text && ellipsis) { 52 text = el.innerText.trim(); 53 } 54 if (!text) { 55 return; 56 } 57 if (ellipsis && el.scrollHeight - el.offsetHeight <= 5) { 58 return; 59 } 60 const elBox = (el.hasAttribute('tooltip-parent') ? el.parentNode : el).getBoundingClientRect(); 61 const elOffset = (el.hasAttribute('tooltip-parent') ? $el.parent() : $el).offset(); 62 const oneLine = text.length < 40 || el.hasAttribute('tooltip-oneline'); 63 const classes = el.className.split(' ').filter(x => x.startsWith("tooltip-")).join(" ");
64 const $tooltip = $('<div>', { 65 class : 'tooltip ' + classes, 66 html: text 67 }).css({ 68 visibility: 'hidden', 69 left : elOffset.left, 70 'min-width' : oneLine ? 'auto' : Math.max(200, elBox.width), 71 'max-width' : oneLine ? 'auto' : Math.max(200, elBox.width * 1.2), 72 'white-space' : oneLine ? 'nowrap' : 'normal' 73 }); 74 75 $(document.body).append($tooltip); 76 setTimeout(() => { 77 const layoutW = getLayoutAreaSize()[0]; 78 const tooltipBox = $tooltip.get(0).getBoundingClientRect(); 79 const alignBottom = below 80 ? window.innerHeight - (elBox.bottom + tooltipBox.height) > 16 // below by default, above if not fit below 81 : elBox.top - tooltipBox.height < 16; // above by default, below if not fit above 82 const alignRight = layoutW - elBox.left - tooltipBox.width < 16; 83 $tooltip.css({ 84 "top" : alignBottom ? (elOffset.top + elBox.height) : (elOffset.top - tooltipBox.height), 85 "left" : elOffset.left - (alignRight ? tooltipBox.width - elBox.width : (alignBottom ? -el.offsetWidth : 0)), 86 visibility: 'visible' 87 }) 88 }, 0) 89 }, 90 enable : function () { 91 this.disabledNode = null; 92 }, 93 disable : function (element) { 94 // disable tooltip for descendants of the element 95 if (element) { 96 this.disabledNode = element; 97 this.remove(); 98 } 99 } 100} 101 102window.tooltip = { 103 remove : tooltip.remove, 104 enable : tooltip.enable, 105 disable : tooltip.disable 106}; 107 108const tooltipBelowSelectors = [ 109 '.article-info', // bottom tooltip for article block 110 '#bookmarks-container[mode=list]', // bookmarks 111 '.podcast-info', // podcasts search 112 '.product-info' // product pages search 113]; 114 115$(document).ready(function() { 116 $(document).on({ 117 mouseenter: function() { 118 const below = tooltipBelowSelectors.some(selector => this.closest(selector) != null); 119 tooltip.show(this, below); 120 }, 121 mouseleave: function() { tooltip.remove(); }, 122 touchend: function() { touchEvent.set(); } 123 }, '.desktop [tooltip]'); 124});
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.