1(function (window, document) { 2 var namespace = window.EditorumUi = window.EditorumUi || {}; 3 var internals = namespace.__internals = namespace.__internals || {}; 4 var selectpickerTitlePatchInitialized = false; 5 var tooltipCompatInitialized = false; 6 var activeTooltipTrigger = null; 7 var activeTooltipElement = null; 8 var activeTooltipIsTruncation = false; 9 var suppressedTitleElement = null; 10 var suppressedTitle = null; 11 var textMeasureContext = null; 12 // Longer text is a whole section or message preview; it opens in full through its own link or expander. 13 var truncationTooltipMaxLength = 600; 14 var measurableFieldTypes = ['', 'text', 'search', 'email', 'url', 'tel', 'number']; 15 var tooltipCompatSelector = [ 16 '[data-toggle="tooltip"]', 17 '[data-tooltip-compat="true"]', 18 '[data-toggle][title]', 19 '[data-toggle][data-original-title]' 20 ].join(', '); 21 var tooltipToggleHelpSelector = [ 22 '.ds-help-icon', 23 '.for-request', 24 '.tooltip-here', 25 '.glyphicon-question-sign', 26 '.glyphicon-info-sign', 27 '[data-tooltip-action="toggle"]' 28 ].join(', '); 29 var tooltipActionSelector = [ 30 'a[href]', 31 'button', 32 'input', 33 'select', 34 'textarea', 35 'label', 36 'summary', 37 '[data-toggle="modal"]', 38 '[data-toggle="dropdown"]', 39 '[data-toggle="collapse"]', 40 '[data-toggle="tab"]' 41 ].join(', '); 42 43 function hasVisibleTextNode(node) { 44 var childNodes; 45 var index; 46 47 if (!node || !node.childNodes) { 48 return false; 49 } 50 51 childNodes = node.childNodes; 52 53 for (index = 0; index < childNodes.length; index += 1) { 54 if (childNodes[index].nodeType === 3 && childNodes[index].textContent.trim()) { 55 return true; 56 } 57 } 58 59 return false; 60 } 61 62 function isIconOnlyTrigger(trigger) { 63 var text = (trigger.textContent || '').replace(/\s+/g, ' ').trim(); 64 65 if (!trigger) { 66 return false; 67 } 68 69 if (trigger.getAttribute('data-tooltip-mode') === 'icon') { 70 return true; 71 } 72 73 if (trigger.querySelector && trigger.querySelector('.ds-icon, svg, i') && !hasVisibleTextNode(trigger)) { 74 return !text; 75 } 76 77 return !text; 78 } 79 80 function isElementTruncated(element) { 81 if (!element || element.nodeType !== 1) { 82 return false; 83 } 84 85 return element.scrollWidth > element.clientWidth + 1 86 || element.scrollHeight > element.clientHeight + 1; 87 } 88 89 // Text that CSS cuts short (text-overflow: ellipsis or -webkit-line-clamp) shows the white tooltip with 90 // its full value, without markup opting in. Elements marked data-tooltip-truncation="off" are skipped. 91 function getTruncationStyle(element) { 92 var style = window.getComputedStyle ? window.getComputedStyle(element) : null; 93 var lineClamp; 94 95 if (!style) { 96 return ''; 97 } 98 99 if (style.textOverflow === 'ellipsis' && style.overflowX !== 'visible') { 100 return 'ellipsis'; 101 } 102 103 lineClamp = style.webkitLineClamp || style.getPropertyValue('-webkit-line-clamp'); 104 105 return lineClamp && lineClamp !== 'none' ? 'line-clamp' : ''; 106 } 107 108 function isFormField(element) { 109 return element.tagName === 'INPUT' || element.tagName === 'SELECT' || element.tagName === 'TEXTAREA'; 110 } 111 112 function measureTextWidth(text, style) { 113 if (!textMeasureContext) { 114 textMeasureContext = document.createElement('canvas').getContext('2d'); 115 } 116 117 textMeasureContext.font = style.fontStyle + ' ' + style.fontWeight + ' ' + style.fontSize + ' ' + style.fontFamily; 118 119 return textMeasureContext.measureText(text).width; 120 } 121 122 // A field draws its text outside the DOM tree, so scrollWidth does not show the overflow; measure the text instead. 123 function isFieldTruncated(element) { 124 var style = window.getComputedStyle(element); 125 var available; 126 127 if (style.textOverflow !== 'ellipsis' || element.tagName === 'TEXTAREA' || element.multiple) { 128 return false;
129 } 130 131 if (element.tagName === 'INPUT' && measurableFieldTypes.indexOf((element.getAttribute('type') || '').toLowerCase()) === -1) { 132 return false; 133 } 134 135 available = element.clientWidth - parseFloat(style.paddingLeft) - parseFloat(style.paddingRight); 136 137 // A natively drawn select arrow sits inside the content box. 138 if (element.tagName === 'SELECT' && (style.appearance || style.webkitAppearance) !== 'none') { 139 available -= 20; 140 } 141 142 return measureTextWidth(readFullText(element), style) > available + 1; 143 } 144 145 function findTruncatedElement(target) { 146 var element = target && target.nodeType === 1 ? target : (target ? target.parentElement : null); 147 var depth; 148 var truncation; 149 150 for (depth = 0; element && element !== document.body && depth < 8; depth += 1) { 151 if (element.getAttribute('data-tooltip-truncation') === 'off') { 152 return null; 153 } 154 155 if (isFormField(element)) { 156 return isFieldTruncated(element) ? element : null; 157 } 158 159 truncation = getTruncationStyle(element); 160 161 if (truncation === 'ellipsis') { 162 return element.scrollWidth > element.clientWidth + 1 ? element : null; 163 } 164 165 // A clamped block also clips a single word that is wider than the box. 166 if (truncation === 'line-clamp') { 167 return element.scrollHeight > element.clientHeight + 1 || element.scrollWidth > element.clientWidth + 1 168 ? element 169 : null; 170 } 171 172 element = element.parentElement; 173 } 174 175 return null; 176 } 177 178 function readFullText(element) { 179 if (element.tagName === 'SELECT') { 180 return element.selectedIndex >= 0 ? element.options[element.selectedIndex].text.trim() : ''; 181 } 182 183 if (element.tagName === 'INPUT' || element.tagName === 'TEXTAREA') { 184 return (element.value || element.getAttribute('placeholder') || '').trim(); 185 } 186 187 return (element.innerText || element.textContent || '').replace(/\s+/g, ' ').trim(); 188 } 189 190 function suppressNativeTitle(target) { 191 var element = target && target.closest ? target.closest('[title]') : null; 192 193 if (!element || !element.getAttribute('title')) { 194 return; 195 } 196 197 suppressedTitleElement = element; 198 suppressedTitle = element.getAttribute('title'); 199 element.removeAttribute('title'); 200 } 201 202 function restoreNativeTitle() { 203 if (suppressedTitleElement && !suppressedTitleElement.hasAttribute('title')) { 204 suppressedTitleElement.setAttribute('title', suppressedTitle); 205 } 206 207 suppressedTitleElement = null; 208 suppressedTitle = null; 209 } 210 211 function showTruncationTooltip(target) { 212 var element = findTruncatedElement(target); 213 var text; 214 215 if (!element) { 216 return false; 217 } 218 219 if (element === activeTooltipTrigger) { 220 return true; 221 } 222 223 // A field being edited shows its own text; a tooltip over it would cover the caret line. 224 if ((element.tagName === 'INPUT' || element.tagName === 'TEXTAREA') && element === document.activeElement) { 225 return false; 226 } 227 228 text = readFullText(element); 229 230 if (!text || text.length > truncationTooltipMaxLength) { 231 return false; 232 } 233 234 showTooltip(element, text, 'top', 'tooltip--full-text'); 235 activeTooltipIsTruncation = true; 236 // The native title would show a second, dark tooltip over the white one. 237 suppressNativeTitle(target); 238 239 return true; 240 } 241 242 function getTooltipMeasurementTargets(trigger) { 243 var targets = []; 244 var selector = trigger && trigger.getAttribute ? trigger.getAttribute('data-tooltip-target') : ''; 245 var matched; 246 247 if (!trigger) { 248 return targets; 249 } 250 251 targets.push(trigger); 252 253 if (selector && trigger.querySelectorAll) { 254 matched = trigger.querySelectorAll(selector); 255 Array.prototype.forEach.call(matched, function (node) { 256 targets.push(node); 257 }); 258 } else if (trigger.querySelectorAll) { 259 matched = trigger.querySelectorAll('.admin-list-multiselect-option-label, .td-cell-content, .ds-text-truncate, .text-truncate, .truncate, .ellipsis'); 260 Array.prototype.forEach.call(matched, function (node) { 261 targets.push(node); 262 }); 263 } 264 265 return targets; 266 } 267 268 function isIgnoredTooltipTrigger(trigger) { 269 return !!( 270 trigger 271 && trigger.closest 272 && trigger.closest('.bootstrap-select') 273 ); 274 } 275 276 function isTooltipCandidate(trigger) { 277 return !!( 278 trigger 279 && internals.matches 280 && internals.matches(trigger, tooltipCompatSelector) 281 ); 282 } 283 284 function prepareTooltipTrigger(trigger) { 285 var title; 286 287 if (!isTooltipCandidate(trigger)) { 288 return; 289 } 290 291 if (isIgnoredTooltipTrigger(trigger)) { 292 trigger.removeAttribute('title'); 293 trigger.removeAttribute('data-original-title'); 294 return; 295 } 296 297 if (trigger.hasAttribute('data-toggle') && trigger.getAttribute('data-tooltip-compat') !== 'true') { 298 trigger.setAttribute('data-tooltip-compat', 'true'); 299 } 300 301 title = trigger.getAttribute('title'); 302 303 if (title !== null) { 304 if (title && !trigger.getAttribute('data-original-title')) { 305 trigger.setAttribute('data-original-title', title); 306 } 307 308 if (title && !trigger.getAttribute('aria-label')) { 309 trigger.setAttribute('aria-label', title); 310 } 311 312 trigger.removeAttribute('title'); 313 return; 314 } 315 316 title = trigger.getAttribute('data-original-title'); 317 318 if (title && !trigger.getAttribute('aria-label')) { 319 trigger.setAttribute('aria-label', title); 320 } 321 } 322 323 function prepareTooltipTriggers(scope) { 324 var root = scope && scope.nodeType === 1 ? scope : document; 325 var triggers = []; 326 327 if (isTooltipCandidate(root)) { 328 triggers.push(root); 329 } 330 331 triggers = triggers.concat(internals.queryAll 332 ? internals.queryAll(tooltipCompatSelector, root) 333 : Array.prototype.slice.call(root.querySelectorAll(tooltipCompatSelector))); 334 335 Array.prototype.forEach.call(triggers, prepareTooltipTrigger); 336 } 337 338 function shouldRenderTooltip(trigger) { 339 var targets; 340 var index; 341 342 if (!trigger || isIgnoredTooltipTrigger(trigger)) { 343 return false;
344 } 345 346 if (trigger.getAttribute('data-tooltip-mode') === 'overflow') { 347 targets = getTooltipMeasurementTargets(trigger); 348 349 for (index = 0; index < targets.length; index += 1) { 350 if (isElementTruncated(targets[index])) { 351 return true; 352 } 353 } 354 355 // An inline label measures 0x0; the ellipsis is drawn by the block around it. 356 return !!findTruncatedElement(trigger); 357 } 358 359 return isTooltipCandidate(trigger) || isIconOnlyTrigger(trigger); 360 } 361 362 function readTooltipText(trigger) { 363 var title = trigger.getAttribute('data-original-title'); 364 365 if (!title) { 366 title = trigger.getAttribute('data-tooltip-title'); 367 } 368 369 if (!title) { 370 title = trigger.getAttribute('title'); 371 if (title) { 372 trigger.setAttribute('data-original-title', title); 373 trigger.removeAttribute('title'); 374 } 375 } 376 377 if (!title) { 378 title = trigger.getAttribute('aria-label'); 379 } 380 381 return title; 382 } 383 384 function getTooltipPlacement(trigger) { 385 var placement = trigger.getAttribute('data-placement'); 386 var parts; 387 var index; 388 389 if (placement === 'top' || placement === 'right' || placement === 'bottom' || placement === 'left') { 390 return placement; 391 } 392 393 if (placement) { 394 parts = placement.split(/\s+/); 395 396 for (index = 0; index < parts.length; index += 1) { 397 if (parts[index] === 'top' || parts[index] === 'right' || parts[index] === 'bottom' || parts[index] === 'left') { 398 return parts[index]; 399 } 400 } 401 } 402 403 return 'top'; 404 } 405 406 function matchesMedia(query) { 407 if (!window.matchMedia) { 408 return false; 409 } 410 411 try { 412 return window.matchMedia(query).matches; 413 } catch (error) { 414 return false; 415 } 416 } 417 418 function hasTouchInput() { 419 return ('ontouchstart' in window) 420 || !!(window.navigator && window.navigator.maxTouchPoints > 0); 421 } 422 423 function canUseHoverTooltips() { 424 if (matchesMedia('(hover: hover) and (pointer: fine)')) { 425 return true; 426 } 427 428 if (matchesMedia('(any-hover: hover) and (any-pointer: fine)')) { 429 return true; 430 } 431 432 if (window.matchMedia) { 433 return false; 434 } 435 436 return !hasTouchInput(); 437 } 438 439 function isCoarsePointer() { 440 if (window.matchMedia) { 441 return matchesMedia('(hover: none)') 442 || matchesMedia('(pointer: coarse)') 443 || matchesMedia('(any-pointer: coarse)'); 444 } 445 446 return hasTouchInput(); 447 } 448 449 function isTooltipHelpTrigger(trigger) { 450 return !!trigger 451 && !!internals.matches 452 && internals.matches(trigger, tooltipToggleHelpSelector); 453 } 454 455 function isActionTooltipTrigger(trigger) { 456 return !!trigger 457 && !!internals.matches 458 && internals.matches(trigger, tooltipActionSelector); 459 } 460 461 function hasDedicatedClickHelp(trigger) { 462 return !!trigger 463 && !!internals.matches 464 && internals.matches(trigger, '.compose-info'); 465 } 466 467 function shouldUseClickTooltip(trigger) { 468 if (!trigger || !isCoarsePointer() || hasDedicatedClickHelp(trigger)) { 469 return false; 470 } 471 472 // Full text of a truncated label is a hover aid; on touch a tap must still select the option or follow the link. 473 if (trigger.getAttribute('data-tooltip-mode') === 'overflow') { 474 return false; 475 } 476 477 if (isTooltipHelpTrigger(trigger)) { 478 return true; 479 } 480 481 return !isActionTooltipTrigger(trigger); 482 } 483 484 function hasInteractiveAncestor(trigger) { 485 var current = trigger ? trigger.parentNode : null; 486 487 while (current && current !== document) { 488 if (internals.matches && internals.matches(current, tooltipActionSelector)) { 489 return true; 490 } 491 492 current = current.parentNode; 493 } 494 495 return false;
496 } 497 498 function prepareClickableTooltipTrigger(trigger) { 499 var label; 500 501 if (!trigger || !shouldUseClickTooltip(trigger)) { 502 return; 503 } 504 505 label = trigger.getAttribute('aria-label') 506 || trigger.getAttribute('data-original-title') 507 || trigger.getAttribute('title'); 508 509 if (label && !trigger.getAttribute('aria-label')) { 510 trigger.setAttribute('aria-label', label); 511 } 512 513 if (!isActionTooltipTrigger(trigger) && !trigger.hasAttribute('tabindex')) { 514 trigger.setAttribute('tabindex', '0'); 515 } 516 517 if (!trigger.getAttribute('role') && !isActionTooltipTrigger(trigger)) { 518 trigger.setAttribute('role', 'button'); 519 } 520 521 if (trigger.getAttribute('aria-hidden') === 'true') { 522 trigger.removeAttribute('aria-hidden'); 523 } 524 525 trigger.setAttribute('data-editorum-tooltip-clickable', 'true'); 526 527 if (!trigger.hasAttribute('aria-expanded')) { 528 trigger.setAttribute('aria-expanded', 'false'); 529 } 530 } 531 532 function prepareClickableTooltipTriggers(scope) { 533 var triggers; 534 535 if (!isCoarsePointer()) { 536 return; 537 } 538 539 triggers = internals.queryAll 540 ? internals.queryAll(tooltipCompatSelector, scope || document) 541 : Array.prototype.slice.call((scope || document).querySelectorAll(tooltipCompatSelector)); 542 543 Array.prototype.forEach.call(triggers, prepareClickableTooltipTrigger); 544 } 545 546 function removeActiveTooltip() { 547 if (activeTooltipElement && activeTooltipElement.parentNode) { 548 activeTooltipElement.parentNode.removeChild(activeTooltipElement); 549 } 550 551 if (activeTooltipTrigger) { 552 activeTooltipTrigger.removeAttribute('aria-describedby'); 553 554 if (activeTooltipTrigger.getAttribute('data-editorum-tooltip-clickable') === 'true') { 555 activeTooltipTrigger.setAttribute('aria-expanded', 'false'); 556 } 557 } 558 559 activeTooltipElement = null; 560 activeTooltipTrigger = null; 561 activeTooltipIsTruncation = false; 562 restoreNativeTitle(); 563 } 564 565 function positionTooltip(trigger, tooltip, placement) { 566 var triggerRect = trigger.getBoundingClientRect(); 567 var tooltipRect = tooltip.getBoundingClientRect(); 568 var scrollX = window.pageXOffset || document.documentElement.scrollLeft || 0; 569 var scrollY = window.pageYOffset || document.documentElement.scrollTop || 0; 570 var viewportW = document.documentElement.clientWidth; 571 var viewportH = document.documentElement.clientHeight; 572 var edgePad = 8; 573 var gap = 0; 574 var top = 0; 575 var left = 0; 576 var idealLeft; 577 var arrow; 578 var flipped = false; 579 var maxLeft; 580 var maxTop; 581 var triggerCenter; 582 var arrowPos; 583 var requestedPlacement = placement; 584 585 if (placement === 'top' && triggerRect.top < tooltipRect.height + edgePad 586 && triggerRect.bottom + tooltipRect.height + edgePad <= viewportH) { 587 placement = 'bottom'; 588 flipped = true; 589 } else if (placement === 'bottom' && triggerRect.bottom + tooltipRect.height + edgePad > viewportH 590 && triggerRect.top >= tooltipRect.height + edgePad) { 591 placement = 'top'; 592 flipped = true; 593 } else if (placement === 'left' && triggerRect.left < tooltipRect.width + edgePad 594 && triggerRect.right + tooltipRect.width + edgePad <= viewportW) { 595 placement = 'right'; 596 flipped = true; 597 } else if (placement === 'right' && triggerRect.right + tooltipRect.width + edgePad > viewportW 598 && triggerRect.left >= tooltipRect.width + edgePad) { 599 placement = 'left'; 600 flipped = true; 601 } 602 603 if (flipped) { 604 tooltip.classList.remove(requestedPlacement); 605 tooltip.classList.add(placement); 606 } 607 608 if (placement === 'bottom') { 609 top = triggerRect.bottom + scrollY + gap; 610 left = triggerRect.left + scrollX + (triggerRect.width / 2) - (tooltipRect.width / 2); 611 } else if (placement === 'left') { 612 top = triggerRect.top + scrollY + (triggerRect.height / 2) - (tooltipRect.height / 2); 613 left = triggerRect.left + scrollX - tooltipRect.width - gap; 614 } else if (placement === 'right') { 615 top = triggerRect.top + scrollY + (triggerRect.height / 2) - (tooltipRect.height / 2); 616 left = triggerRect.right + scrollX + gap; 617 } else { 618 top = triggerRect.top + scrollY - tooltipRect.height - gap; 619 left = triggerRect.left + scrollX + (triggerRect.width / 2) - (tooltipRect.width / 2); 620 } 621 622 idealLeft = left; 623 maxLeft = scrollX + viewportW - tooltipRect.width - edgePad; 624 left = Math.max(left, edgePad); 625 left = Math.min(left, maxLeft); 626 627 maxTop = scrollY + viewportH - tooltipRect.height - edgePad; 628 top = Math.max(top, scrollY + edgePad); 629 top = Math.min(top, maxTop); 630 631 tooltip.style.top = top + 'px'; 632 tooltip.style.left = left + 'px'; 633 634 if (left !== idealLeft && (placement === 'top' || placement === 'bottom')) { 635 arrow = tooltip.querySelector('.tooltip-arrow'); 636 if (arrow) { 637 triggerCenter = triggerRect.left + scrollX + (triggerRect.width / 2); 638 arrowPos = triggerCenter - left; 639 arrowPos = Math.max(12, Math.min(arrowPos, tooltipRect.width - 12)); 640 arrow.style.left = arrowPos + 'px'; 641 arrow.style.marginLeft = '0'; 642 } 643 } 644 } 645 646 function renderTooltip(trigger) { 647 var tooltipText; 648 649 prepareTooltipTrigger(trigger); 650 tooltipText = readTooltipText(trigger); 651 652 if (!tooltipText || !shouldRenderTooltip(trigger)) { 653 return null; 654 } 655 656 return showTooltip(trigger, tooltipText, getTooltipPlacement(trigger)); 657 } 658 659 function showTooltip(trigger, tooltipText, placement, modifierClass) { 660 var tooltip; 661 var arrow; 662 var inner; 663 var tooltipId; 664 665 removeActiveTooltip(); 666 667 tooltip = document.createElement('div'); 668 tooltip.className = 'tooltip ' + placement + ' in'; 669 670 if (modifierClass) { 671 tooltip.classList.add(modifierClass); 672 } 673 674 tooltip.setAttribute('role', 'tooltip'); 675
676 tooltipId = 'editorum-tooltip-' + Date.now(); 677 tooltip.id = tooltipId; 678 679 arrow = document.createElement('div'); 680 arrow.className = 'tooltip-arrow'; 681 682 inner = document.createElement('div'); 683 inner.className = 'tooltip-inner'; 684 inner.textContent = tooltipText; 685 686 tooltip.appendChild(arrow); 687 tooltip.appendChild(inner); 688 document.body.appendChild(tooltip); 689 690 trigger.setAttribute('aria-describedby', tooltipId); 691 if (trigger.getAttribute('data-editorum-tooltip-clickable') === 'true') { 692 trigger.setAttribute('aria-expanded', 'true'); 693 } 694 positionTooltip(trigger, tooltip, placement); 695 696 activeTooltipTrigger = trigger; 697 activeTooltipElement = tooltip; 698 699 return tooltip; 700 } 701 702 function toggleClickTooltip(trigger) { 703 prepareClickableTooltipTrigger(trigger); 704 705 if (activeTooltipTrigger === trigger) { 706 removeActiveTooltip(); 707 return; 708 } 709 710 renderTooltip(trigger); 711 } 712 713 function isSameTooltipTrigger(first, second) { 714 return !!first && !!second && first === second; 715 } 716 717 function installTooltipJQueryCompat() { 718 return false; 719 } 720 721 function prepareTooltipMutationNode(node) { 722 if (!node || node.nodeType !== 1) { 723 return; 724 } 725 726 if (isTooltipCandidate(node)) { 727 prepareTooltipTrigger(node); 728 prepareClickableTooltipTrigger(node); 729 } 730 731 if (node.querySelectorAll) { 732 prepareTooltipTriggers(node); 733 prepareClickableTooltipTriggers(node); 734 } 735 } 736 737 function initTooltipCompat(root) { 738 var scope = root || document; 739 740 prepareTooltipTriggers(scope); 741 prepareClickableTooltipTriggers(scope); 742 743 if (tooltipCompatInitialized) { 744 return true; 745 } 746 747 tooltipCompatInitialized = true; 748 749 document.addEventListener('mouseover', function (event) { 750 var trigger = internals.closest(event.target, tooltipCompatSelector); 751 var fromTrigger = internals.closest(event.relatedTarget, tooltipCompatSelector); 752 753 if (!canUseHoverTooltips()) { 754 return; 755 } 756 757 if (!trigger) { 758 showTruncationTooltip(event.target); 759 return; 760 } 761 762 if (isSameTooltipTrigger(trigger, fromTrigger)) { 763 return; 764 } 765 766 renderTooltip(trigger); 767 }); 768 769 document.addEventListener('mouseout', function (event) { 770 var trigger = internals.closest(event.target, tooltipCompatSelector); 771 var toTrigger = internals.closest(event.relatedTarget, tooltipCompatSelector); 772 773 if (activeTooltipIsTruncation) { 774 if (!event.relatedTarget || !activeTooltipTrigger.contains(event.relatedTarget)) { 775 removeActiveTooltip(); 776 } 777 return; 778 } 779 780 if (!canUseHoverTooltips() || !trigger || isSameTooltipTrigger(trigger, toTrigger)) { 781 return; 782 } 783 784 if (activeTooltipTrigger === trigger) { 785 removeActiveTooltip(); 786 } 787 }); 788 789 document.addEventListener('focusin', function (event) { 790 var trigger = internals.closest(event.target, tooltipCompatSelector); 791 792 if (!trigger) { 793 showTruncationTooltip(event.target); 794 return; 795 } 796 797 if (!shouldUseClickTooltip(trigger) && !hasDedicatedClickHelp(trigger)) { 798 renderTooltip(trigger); 799 } 800 }); 801 802 document.addEventListener('focusout', function (event) { 803 var trigger = internals.closest(event.target, tooltipCompatSelector); 804 805 if (activeTooltipIsTruncation) { 806 if (activeTooltipTrigger.contains(event.target)) { 807 removeActiveTooltip(); 808 } 809 return; 810 } 811 812 if (trigger 813 && activeTooltipTrigger === trigger 814 && trigger.getAttribute('data-editorum-tooltip-clickable') !== 'true') { 815 removeActiveTooltip(); 816 } 817 }); 818 819 document.addEventListener('click', function (event) { 820 var trigger = internals.closest(event.target, tooltipCompatSelector); 821 822 if (!shouldUseClickTooltip(trigger) || !hasInteractiveAncestor(trigger)) { 823 return; 824 } 825 826 event.preventDefault(); 827 event.stopPropagation(); 828 toggleClickTooltip(trigger); 829 }, true); 830 831 document.addEventListener('click', function (event) { 832 var trigger = internals.closest(event.target, tooltipCompatSelector); 833 834 if (shouldUseClickTooltip(trigger)) { 835 event.preventDefault(); 836 toggleClickTooltip(trigger); 837 return; 838 } 839 840 if (activeTooltipTrigger 841 && event.target !== activeTooltipTrigger 842 && !activeTooltipTrigger.contains(event.target) 843 && (!activeTooltipElement || !activeTooltipElement.contains(event.target))) { 844 removeActiveTooltip(); 845 } 846 }); 847 848 document.addEventListener('keydown', function (event) { 849 var trigger = internals.closest(event.target, tooltipCompatSelector); 850 851 if (!shouldUseClickTooltip(trigger) || (event.key !== 'Enter' && event.key !== ' ')) { 852 return; 853 } 854 855 event.preventDefault(); 856 toggleClickTooltip(trigger); 857 }); 858 859 if (window.MutationObserver) { 860 new window.MutationObserver(function (mutations) { 861 Array.prototype.forEach.call(mutations, function (mutation) { 862 if (mutation.type === 'attributes') { 863 prepareTooltipMutationNode(mutation.target); 864 return; 865 } 866 867 Array.prototype.forEach.call(mutation.addedNodes || [], prepareTooltipMutationNode); 868 }); 869 }).observe(document.documentElement, { 870 attributeFilter: ['data-original-title', 'data-toggle', 'data-tooltip-compat', 'title'], 871 attributes: true, 872 childList: true, 873 subtree: true 874 }); 875 } 876 877 window.addEventListener('scroll', removeActiveTooltip, { passive: true, capture: true }); 878 window.addEventListener('resize', removeActiveTooltip, { passive: true }); 879 880 return true; 881 } 882 883 function patchSelectpickerTitles() { 884 if (selectpickerTitlePatchInitialized) { 885 return true; 886 } 887 selectpickerTitlePatchInitialized = true; 888 889 Array.prototype.forEach.call(document.querySelectorAll('.bootstrap-select .dropdown-toggle'), function (button) { 890 button.removeAttribute('title'); 891 button.removeAttribute('data-original-title'); 892 }); 893 894 return true; 895 } 896 897 internals.findTruncatedElement = findTruncatedElement; 898 internals.readTooltipText = readTooltipText; 899 internals.getTooltipPlacement = getTooltipPlacement; 900 internals.removeActiveTooltip = removeActiveTooltip; 901 internals.renderTooltip = renderTooltip; 902 internals.prepareTooltipTrigger = prepareTooltipTrigger; 903 internals.prepareTooltipTriggers = prepareTooltipTriggers; 904 internals.installTooltipJQueryCompat = installTooltipJQueryCompat; 905 internals.initTooltipCompat = initTooltipCompat; 906 internals.patchSelectpickerTitles = patchSelectpickerTitles; 907})(window, document);
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.