1!(function() { 2 'use strict'; 3 4 function callback() { 5 var ua = navigator.userAgent, 6 hasTouch = 'ontouchstart' in window && !(/chrome/i.exec(ua) && !/android/i.exec(ua)), 7 lastEl = null, 8 lastEventType = null, 9 lastInteractive = false, 10 lastTooltip = null, 11 preventMouseOver = false, 12 prevTarget = null, 13 stack = [], 14 startEvent = hasTouch ? 'touchstart' : 'mousedown', 15 stopEvent = hasTouch ? 'touchend' : 'mouseup', 16 style = document.documentElement.style, 17 transition = style.transition != undefined ? {event: 'transitionend', prefix: ''} 18 : style.WebkitTransition != undefined ? {event: 'webkitTransitionEnd', prefix: '-webkit-'} : null; 19 20 /** 21 * classList polyfill 22 */ 23 if (!('classList' in document.documentElement) && Object.defineProperty && typeof HTMLElement !== 'undefined') { 24 Object.defineProperty(HTMLElement.prototype, 'classList', { 25 get: function() { 26 var self = this; 27 function update(fn) { 28 return function(value) { 29 var classes = self.className.split(/\s+/), 30 index = classes.indexOf(value); 31 32 fn(classes, index, value); 33 self.className = classes.join(' '); 34 } 35 } 36 37 var ret = { 38 add: update(function(classes, index, value) { 39 ~index || classes.push(value); 40 }), 41 42 remove: update(function(classes, index) { 43 ~index && classes.splice(index, 1); 44 }), 45 46 toggle: update(function(classes, index, value) { 47 ~index ? classes.splice(index, 1) : classes.push(value); 48 }), 49 50 contains: function(value) { 51 return !!~self.className.split(/\s+/).indexOf(value); 52 }, 53 54 item: function(i) { 55 return self.className.split(/\s+/)[i] || null; 56 } 57 }; 58 59 Object.defineProperty(ret, 'length', { 60 get: function() { 61 return self.className.split(/\s+/).length; 62 } 63 }); 64 65 return ret; 66 } 67 }); 68 } 69 70 /** 71 * closest pollyfill 72 */ 73 if (window.Element && !Element.prototype.closest) { 74 Element.prototype.closest = 75 function(s) { 76 var matches = (this.document || this.ownerDocument).querySelectorAll(s), 77 i, 78 el = this; 79 do { 80 i = matches.length; 81 while (--i >= 0 && matches.item(i) !== el) {}; 82 } while ((i < 0) && (el = el.parentElement)); 83 return el; 84 }; 85 } 86 87 /** 88 * Close all tooltips 89 */ 90 var closeAllTooltips = function() { 91 for (var i = stack.length - 1; i >= 0; i--) { 92 var item = stack[i]; 93 94 if (lastEventType == 'mouseover' && item.getOptions().event == 'click') { 95 continue; 96 } 97 98 item.close(); 99 } 100 }; 101 102 var body = document.body; 103 var matchesSelector = body.matches || body.webkitMatchesSelector || body.mozMatchesSelector || body.msMatchesSelector; 104 105 /** 106 * Prevent close tooltip 107 * 108 * @param {MouseEvent} e - Event. 109 */ 110 function preventClosing(e) { 111 if (!lastInteractive || !lastTooltip || !lastEl) { 112 return false; 113 } 114 115 var elRect = lastEl.getBoundingClientRect(), 116 tRect = lastTooltip.getBoundingClientRect(), 117 vertical = (e.clientY >= tRect.bottom && e.clientY <= elRect.top) || (e.clientY >= elRect.bottom && e.clientY <= tRect.top), 118 horizontal = e.clientX >= Math.max(elRect.left, tRect.left) && e.clientX <= Math.min(elRect.right, tRect.right); 119 120 return vertical && horizontal; 121 } 122 123 /** 124 * Transition event callback 125 * 126 * @param {HTMLElement} el - Element. 127 * @param {Function} callback - Callback. 128 */ 129 var transitionCallback = function(el, callback) {
130 var delay = transition && el.classList.contains('open') 131 && Math.max.apply(null, getComputedStyle(el)[transition.prefix + 'transition-duration'].match(/\d+(\.\d+)?/g) || [0]); 132 133 if (delay) { 134 el.addEventListener(transition.event, function transitionListener(e) { 135 e.target.removeEventListener(transition.event, transitionListener); 136 137 if (e.target === e.currentTarget) { 138 callback(); 139 } else { 140 transitionCallback(el, callback); 141 } 142 }); 143 } else { 144 callback(); 145 } 146 }; 147 148 /** 149 * Tooltip 150 * 151 * @param {HTMLElement} target - Target element. 152 * @param {Object} options - Tooltip options. 153 * @returns {Tooltip} 154 */ 155 var Tooltip = function(target, options) { 156 var autoCloseTimer, closeTimer, openTimer, transTimer, tooltip, that = this; 157 158 // repos tooltip on image load or load error 159 function imgCallback() { 160 that.pos(); 161 this.removeEventListener('load', imgCallback); 162 this.removeEventListener('error', imgCallback); 163 } 164 165 /** 166 * Sets attributes 167 */ 168 function setAttrs(el, attrs) { 169 for (var prop in attrs) { 170 if (attrs.hasOwnProperty(prop)) { 171 var value = attrs[prop]; 172 173 if (value == null) { 174 continue; 175 } 176 177 if ('transform transition'.indexOf(prop) > -1) { 178 transition && (el.style[transition.prefix + prop] = value); 179 } else { 180 var key = prop == '_class' ? 'className' : prop; 181 182 if (el[key] == undefined && el.style[key] != undefined) { 183 el.style[key] = value; 184 } else { 185 el[key] = value; 186 } 187 } 188 } 189 } 190 return el; 191 } 192 193 /** 194 * Create tooltip 195 */ 196 that.create = function() { 197 if (!tooltip) { 198 tooltip = setAttrs(document.createElement('span'), { 199 _class: 's4yt' + (options.effect ? ' s4yt-' + options.effect : '') + (options._class ? ' ' + options._class : ''), 200 innerHTML: options.content.replace(/^\s*[\r\n]/gm, '<span class="s4yt-br"></span>').replace(/\n/g, '<br>') + '<span class="s4yt-arrow"></span>', 201 transform: 'none', 202 transition: 'none' 203 }); 204 205 Array.prototype.forEach.call(tooltip.querySelectorAll('img'), function(img) { 206 img.addEventListener('load', imgCallback); 207 img.addEventListener('error', imgCallback); 208 }); 209 210 // /^\s*[\r\n]/gm - regex for replace empty new lines for span 211 document.body.appendChild(tooltip); 212 } 213 214 that.pos(); 215 lastInteractive = options.interactive && options.event != 'click'; 216 217 transTimer = setTimeout(function() { 218 setAttrs(tooltip, {transition: ''}); 219 tooltip.classList.add('open'); 220 }, 30); 221 }; 222 223 /** 224 * Close tooltip 225 */ 226 that.close = function() { 227 that.reset(); 228 229 closeTimer = setTimeout(function() { 230 var index = stack.indexOf(that); 231 232 if (index > -1) { 233 stack.splice(index, 1); 234 } 235 236 if (!tooltip) { 237 return; 238 } 239 240 transitionCallback(tooltip, function() { 241 tooltip = !tooltip.parentNode.removeChild(tooltip); 242 }); 243 tooltip && tooltip.classList.remove('open'); 244 }, lastEventType == 'mouseover' ? parseInt(options.closeDelay) || 0 : 0); 245 }; 246 247 /** 248 * Equals target element with entered element 249 * 250 * @param compareEl 251 * @returns {boolean} 252 */ 253 that.equals = function(compareEl) { 254 return compareEl === target; 255 }; 256 257 /** 258 * Open tooltip 259 */ 260 that.open = function() { 261 that.reset(); 262 stack.push(that); 263 264 openTimer = setTimeout(function() { 265 that.create(); 266 autoCloseTimer = options.autoClose 267 && setTimeout(function() { that.close(); }, parseInt(options.autoClose) || 0); 268 }, lastEventType == 'mouseover' ? parseInt(options.openDelay) || 0 : 0); 269 }; 270 271 /** 272 * Sets tooltip position 273 */ 274 that.pos = function() { 275 if (!tooltip) { 276 return; 277 } 278 279 var arrow = tooltip.querySelector('.s4yt-arrow'), 280 arrowHeight = arrow.offsetHeight, 281 arrowWidth = arrow.offsetWidth, 282 clientHeight = document.documentElement.clientHeight, 283 clientWidth = document.documentElement.clientWidth, 284 scrollYPos = window.pageYOffset, 285 pos = target.getBoundingClientRect(),
286 tPos = {left: 0, top: 0}; 287 288 tooltip.style.maxWidth = Math.min(clientWidth - arrowWidth * 2, parseInt(getComputedStyle(tooltip).maxWidth)) + 'px'; 289 tPos.left = pos.left + (pos.width - tooltip.offsetWidth) * .5; 290 291 var tHeight = tooltip.offsetHeight + arrowWidth, 292 toDown = options.position == 'bottom' ? pos.bottom + tHeight < clientHeight : tHeight > pos.top; 293 294 if (toDown) { 295 tPos.top = scrollYPos + pos.bottom + arrowHeight; 296 tooltip.classList.add('top'); 297 } else { 298 tPos.top = scrollYPos + pos.top - tooltip.offsetHeight - arrowHeight; 299 tooltip.classList.remove('top'); 300 } 301 302 if (tPos.left - arrowWidth < 0) { 303 tPos.left = arrowWidth; 304 tPos.maxWidth = clientWidth - arrowWidth * 2; 305 } else if (tPos.left + tooltip.offsetWidth + arrowWidth > clientWidth) { 306 tPos.left = clientWidth - tooltip.offsetWidth - arrowWidth; 307 } 308 309 arrow.style.left = (pos.left < tPos.left ? tooltip.offsetWidth * .5 - arrowWidth * .5 : pos.left - tPos.left + pos.width * .5 - arrowWidth * .5) + 'px'; 310 311 setAttrs(tooltip, { 312 left: Math.round(tPos.left) + 'px', 313 top: Math.round(tPos.top) + 'px', 314 transform: '' 315 }); 316 317 318 lastTooltip = tooltip; 319 }; 320 321 /** 322 * Reset timeouts 323 */ 324 that.reset = function() { 325 // clearTimeout(autoCloseTimer); na mobilech se spustà mouseover 326 clearTimeout(closeTimer); 327 clearTimeout(openTimer); 328 clearTimeout(transTimer); 329 }; 330 331 /** 332 * Get tooltip options 333 * 334 * @returns {Object} 335 */ 336 that.getOptions = function() { 337 return options; 338 }; 339 340 /** 341 * Get target element 342 * 343 * @returns {HTMLElement} 344 */ 345 that.getTarget = function() { 346 return target; 347 }; 348 349 /** 350 * Get tooltip element 351 * 352 * @returns {*} 353 */ 354 that.getTooltip = function() { 355 return tooltip; 356 }; 357 358 that.open(); 359 return this; 360 }; 361 362 /** 363 * Tooltip plugin 364 */ 365 window.smart4yTooltip = function(el, options) { 366 if (!el.parentNode || !options || typeof options !== 'object' || (typeof options === 'object' && !options.content)) { 367 return; 368 } 369 370 var clickable = el.classList.contains('s4yt-click'); 371 closeAllTooltips(); 372 lastEl = el; 373 374 // extend options 375 for (var i in smart4yTooltipDefaults) { 376 if (options[i] == null && smart4yTooltipDefaults.hasOwnProperty(i)) { 377 options[i] = i == 'event' && clickable ? 'click' : smart4yTooltipDefaults[i]; 378 } 379 } 380 381 return new Tooltip(el, options); 382 }; 383 384 /** 385 * Start event handler 386 * 387 * @param {Event} e - Event. 388 */ 389 function startHandler(e) { 390 if (e.which > 1) { 391 return; 392 } 393 394 prevTarget = e.target; 395 } 396 397 /** 398 * Stop event handler 399 * 400 * @param {Event} e - Event. 401 */ 402 function stopHandler(e) { 403 var currentTarget = e.type == 'mouseup' ? e.target : document.elementFromPoint(e.changedTouches[0].clientX, e.changedTouches[0].clientY); 404 405 if (currentTarget === prevTarget) { 406 baseHandler(e); 407 } 408 } 409 410 /** 411 * Base event handler 412 * 413 * @param {Event} e - Event. 414 */ 415 function baseHandler(e) { 416 var target = e.target.nodeType != 9 && e.target.closest('.s4yt, .s4yt-click, .s4yt-hover'), 417 clickable = target && target.classList.contains('s4yt-click'), 418 existingTooltip = stack.filter(function(item) { return item.getTarget() === target; }), 419 hoverable = target && target.classList.contains('s4yt-hover'), 420 tooltip = target && target.classList.contains('s4yt'), 421 wasClicked = 'mouseup touchend'.indexOf(lastEventType = e.type) > -1; 422 423 if ((clickable && wasClicked) || (hasTouch && hoverable)) { 424 // click 425 preventMouseOver = true; 426 // prevent execute mouseover event on mobile devices that produce unwanted closure of tooltip 427 setTimeout(function() { preventMouseOver = false; }, 0); 428 429 if (!existingTooltip.length) { 430 return smart4yTooltip(target, JSON.parse(target.getAttribute('data-s4yt'))); 431 } 432 } 433 434 if (!wasClicked) { 435 // hover 436 if (preventMouseOver || preventClosing(e) || tooltip) { 437 return; 438 } 439 440 if (hoverable) { 441 return existingTooltip.length ? 442 existingTooltip[0].reset() : smart4yTooltip(target, JSON.parse(target.getAttribute('data-s4yt'))); 443 } 444 } 445 446 closeAllTooltips(); 447 } 448 449 // attach events 450 document.addEventListener(startEvent, startHandler); 451 document.addEventListener(stopEvent, stopHandler); 452 document.addEventListener('mouseleave', closeAllTooltips); 453 454 if (!/iemobile/i.test(ua.toLowerCase())) { 455 // exclude ie phones - mouseover is calling before touchstart 456 document.addEventListener('mouseover', baseHandler); 457 } 458 459 window.addEventListener('resize', function() { 460 for (var i = stack.length - 1; i >= 0; i--) { 461 stack[i].pos(); 462 } 463 }); 464 } 465 466 if (document.readyState != 'loading') { 467 callback(); 468 } else { 469 document.addEventListener('DOMContentLoaded', callback); 470 } 471})();
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.