[ Index ] |
PHP Cross Reference of Joomla 4.2.2 documentation |
[Summary view] [Print] [Text view]
1 (function () { 2 'use strict'; 3 4 function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { 5 try { 6 var info = gen[key](arg); 7 var value = info.value; 8 } catch (error) { 9 reject(error); 10 return; 11 } 12 13 if (info.done) { 14 resolve(value); 15 } else { 16 Promise.resolve(value).then(_next, _throw); 17 } 18 } 19 20 function _asyncToGenerator(fn) { 21 return function () { 22 var self = this, 23 args = arguments; 24 return new Promise(function (resolve, reject) { 25 var gen = fn.apply(self, args); 26 27 function _next(value) { 28 asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); 29 } 30 31 function _throw(err) { 32 asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); 33 } 34 35 _next(undefined); 36 }); 37 }; 38 } 39 40 var top = 'top'; 41 var bottom = 'bottom'; 42 var right = 'right'; 43 var left = 'left'; 44 var auto = 'auto'; 45 var basePlacements = [top, bottom, right, left]; 46 var start = 'start'; 47 var end = 'end'; 48 var clippingParents = 'clippingParents'; 49 var viewport = 'viewport'; 50 var popper = 'popper'; 51 var reference = 'reference'; 52 var variationPlacements = /*#__PURE__*/basePlacements.reduce(function (acc, placement) { 53 return acc.concat([placement + "-" + start, placement + "-" + end]); 54 }, []); 55 var placements = /*#__PURE__*/[].concat(basePlacements, [auto]).reduce(function (acc, placement) { 56 return acc.concat([placement, placement + "-" + start, placement + "-" + end]); 57 }, []); // modifiers that need to read the DOM 58 59 var beforeRead = 'beforeRead'; 60 var read = 'read'; 61 var afterRead = 'afterRead'; // pure-logic modifiers 62 63 var beforeMain = 'beforeMain'; 64 var main = 'main'; 65 var afterMain = 'afterMain'; // modifier with the purpose to write to the DOM (or write into a framework state) 66 67 var beforeWrite = 'beforeWrite'; 68 var write = 'write'; 69 var afterWrite = 'afterWrite'; 70 var modifierPhases = [beforeRead, read, afterRead, beforeMain, main, afterMain, beforeWrite, write, afterWrite]; 71 72 function getNodeName(element) { 73 return element ? (element.nodeName || '').toLowerCase() : null; 74 } 75 76 function getWindow(node) { 77 if (node == null) { 78 return window; 79 } 80 81 if (node.toString() !== '[object Window]') { 82 var ownerDocument = node.ownerDocument; 83 return ownerDocument ? ownerDocument.defaultView || window : window; 84 } 85 86 return node; 87 } 88 89 function isElement$1(node) { 90 var OwnElement = getWindow(node).Element; 91 return node instanceof OwnElement || node instanceof Element; 92 } 93 94 function isHTMLElement(node) { 95 var OwnElement = getWindow(node).HTMLElement; 96 return node instanceof OwnElement || node instanceof HTMLElement; 97 } 98 99 function isShadowRoot(node) { 100 // IE 11 has no ShadowRoot 101 if (typeof ShadowRoot === 'undefined') { 102 return false; 103 } 104 105 var OwnElement = getWindow(node).ShadowRoot; 106 return node instanceof OwnElement || node instanceof ShadowRoot; 107 } // and applies them to the HTMLElements such as popper and arrow 108 109 110 function applyStyles(_ref) { 111 var state = _ref.state; 112 Object.keys(state.elements).forEach(function (name) { 113 var style = state.styles[name] || {}; 114 var attributes = state.attributes[name] || {}; 115 var element = state.elements[name]; // arrow is optional + virtual elements 116 117 if (!isHTMLElement(element) || !getNodeName(element)) { 118 return; 119 } // Flow doesn't support to extend this property, but it's the most 120 // effective way to apply styles to an HTMLElement 121 // $FlowFixMe[cannot-write] 122 123 124 Object.assign(element.style, style); 125 Object.keys(attributes).forEach(function (name) { 126 var value = attributes[name]; 127 128 if (value === false) { 129 element.removeAttribute(name); 130 } else { 131 element.setAttribute(name, value === true ? '' : value); 132 } 133 }); 134 }); 135 } 136 137 function effect$2(_ref2) { 138 var state = _ref2.state; 139 var initialStyles = { 140 popper: { 141 position: state.options.strategy, 142 left: '0', 143 top: '0', 144 margin: '0' 145 }, 146 arrow: { 147 position: 'absolute' 148 }, 149 reference: {} 150 }; 151 Object.assign(state.elements.popper.style, initialStyles.popper); 152 state.styles = initialStyles; 153 154 if (state.elements.arrow) { 155 Object.assign(state.elements.arrow.style, initialStyles.arrow); 156 } 157 158 return function () { 159 Object.keys(state.elements).forEach(function (name) { 160 var element = state.elements[name]; 161 var attributes = state.attributes[name] || {}; 162 var styleProperties = Object.keys(state.styles.hasOwnProperty(name) ? state.styles[name] : initialStyles[name]); // Set all values to an empty string to unset them 163 164 var style = styleProperties.reduce(function (style, property) { 165 style[property] = ''; 166 return style; 167 }, {}); // arrow is optional + virtual elements 168 169 if (!isHTMLElement(element) || !getNodeName(element)) { 170 return; 171 } 172 173 Object.assign(element.style, style); 174 Object.keys(attributes).forEach(function (attribute) { 175 element.removeAttribute(attribute); 176 }); 177 }); 178 }; 179 } // eslint-disable-next-line import/no-unused-modules 180 181 182 var applyStyles$1 = { 183 name: 'applyStyles', 184 enabled: true, 185 phase: 'write', 186 fn: applyStyles, 187 effect: effect$2, 188 requires: ['computeStyles'] 189 }; 190 191 function getBasePlacement$1(placement) { 192 return placement.split('-')[0]; 193 } 194 195 var max = Math.max; 196 var min = Math.min; 197 var round = Math.round; 198 199 function getBoundingClientRect(element, includeScale) { 200 if (includeScale === void 0) { 201 includeScale = false; 202 } 203 204 var rect = element.getBoundingClientRect(); 205 var scaleX = 1; 206 var scaleY = 1; 207 208 if (isHTMLElement(element) && includeScale) { 209 var offsetHeight = element.offsetHeight; 210 var offsetWidth = element.offsetWidth; // Do not attempt to divide by 0, otherwise we get `Infinity` as scale 211 // Fallback to 1 in case both values are `0` 212 213 if (offsetWidth > 0) { 214 scaleX = round(rect.width) / offsetWidth || 1; 215 } 216 217 if (offsetHeight > 0) { 218 scaleY = round(rect.height) / offsetHeight || 1; 219 } 220 } 221 222 return { 223 width: rect.width / scaleX, 224 height: rect.height / scaleY, 225 top: rect.top / scaleY, 226 right: rect.right / scaleX, 227 bottom: rect.bottom / scaleY, 228 left: rect.left / scaleX, 229 x: rect.left / scaleX, 230 y: rect.top / scaleY 231 }; 232 } // means it doesn't take into account transforms. 233 234 235 function getLayoutRect(element) { 236 var clientRect = getBoundingClientRect(element); // Use the clientRect sizes if it's not been transformed. 237 // Fixes https://github.com/popperjs/popper-core/issues/1223 238 239 var width = element.offsetWidth; 240 var height = element.offsetHeight; 241 242 if (Math.abs(clientRect.width - width) <= 1) { 243 width = clientRect.width; 244 } 245 246 if (Math.abs(clientRect.height - height) <= 1) { 247 height = clientRect.height; 248 } 249 250 return { 251 x: element.offsetLeft, 252 y: element.offsetTop, 253 width: width, 254 height: height 255 }; 256 } 257 258 function contains(parent, child) { 259 var rootNode = child.getRootNode && child.getRootNode(); // First, attempt with faster native method 260 261 if (parent.contains(child)) { 262 return true; 263 } // then fallback to custom implementation with Shadow DOM support 264 else if (rootNode && isShadowRoot(rootNode)) { 265 var next = child; 266 267 do { 268 if (next && parent.isSameNode(next)) { 269 return true; 270 } // $FlowFixMe[prop-missing]: need a better way to handle this... 271 272 273 next = next.parentNode || next.host; 274 } while (next); 275 } // Give up, the result is false 276 277 278 return false; 279 } 280 281 function getComputedStyle$1(element) { 282 return getWindow(element).getComputedStyle(element); 283 } 284 285 function isTableElement(element) { 286 return ['table', 'td', 'th'].indexOf(getNodeName(element)) >= 0; 287 } 288 289 function getDocumentElement(element) { 290 // $FlowFixMe[incompatible-return]: assume body is always available 291 return ((isElement$1(element) ? element.ownerDocument : // $FlowFixMe[prop-missing] 292 element.document) || window.document).documentElement; 293 } 294 295 function getParentNode(element) { 296 if (getNodeName(element) === 'html') { 297 return element; 298 } 299 300 return (// this is a quicker (but less type safe) way to save quite some bytes from the bundle 301 // $FlowFixMe[incompatible-return] 302 // $FlowFixMe[prop-missing] 303 element.assignedSlot || // step into the shadow DOM of the parent of a slotted node 304 element.parentNode || ( // DOM Element detected 305 isShadowRoot(element) ? element.host : null) || // ShadowRoot detected 306 // $FlowFixMe[incompatible-call]: HTMLElement is a Node 307 getDocumentElement(element) // fallback 308 309 ); 310 } 311 312 function getTrueOffsetParent(element) { 313 if (!isHTMLElement(element) || // https://github.com/popperjs/popper-core/issues/837 314 getComputedStyle$1(element).position === 'fixed') { 315 return null; 316 } 317 318 return element.offsetParent; 319 } // `.offsetParent` reports `null` for fixed elements, while absolute elements 320 // return the containing block 321 322 323 function getContainingBlock(element) { 324 var isFirefox = navigator.userAgent.toLowerCase().indexOf('firefox') !== -1; 325 var isIE = navigator.userAgent.indexOf('Trident') !== -1; 326 327 if (isIE && isHTMLElement(element)) { 328 // In IE 9, 10 and 11 fixed elements containing block is always established by the viewport 329 var elementCss = getComputedStyle$1(element); 330 331 if (elementCss.position === 'fixed') { 332 return null; 333 } 334 } 335 336 var currentNode = getParentNode(element); 337 338 while (isHTMLElement(currentNode) && ['html', 'body'].indexOf(getNodeName(currentNode)) < 0) { 339 var css = getComputedStyle$1(currentNode); // This is non-exhaustive but covers the most common CSS properties that 340 // create a containing block. 341 // https://developer.mozilla.org/en-US/docs/Web/CSS/Containing_block#identifying_the_containing_block 342 343 if (css.transform !== 'none' || css.perspective !== 'none' || css.contain === 'paint' || ['transform', 'perspective'].indexOf(css.willChange) !== -1 || isFirefox && css.willChange === 'filter' || isFirefox && css.filter && css.filter !== 'none') { 344 return currentNode; 345 } else { 346 currentNode = currentNode.parentNode; 347 } 348 } 349 350 return null; 351 } // Gets the closest ancestor positioned element. Handles some edge cases, 352 // such as table ancestors and cross browser bugs. 353 354 355 function getOffsetParent(element) { 356 var window = getWindow(element); 357 var offsetParent = getTrueOffsetParent(element); 358 359 while (offsetParent && isTableElement(offsetParent) && getComputedStyle$1(offsetParent).position === 'static') { 360 offsetParent = getTrueOffsetParent(offsetParent); 361 } 362 363 if (offsetParent && (getNodeName(offsetParent) === 'html' || getNodeName(offsetParent) === 'body' && getComputedStyle$1(offsetParent).position === 'static')) { 364 return window; 365 } 366 367 return offsetParent || getContainingBlock(element) || window; 368 } 369 370 function getMainAxisFromPlacement(placement) { 371 return ['top', 'bottom'].indexOf(placement) >= 0 ? 'x' : 'y'; 372 } 373 374 function within(min$1, value, max$1) { 375 return max(min$1, min(value, max$1)); 376 } 377 378 function withinMaxClamp(min, value, max) { 379 var v = within(min, value, max); 380 return v > max ? max : v; 381 } 382 383 function getFreshSideObject() { 384 return { 385 top: 0, 386 right: 0, 387 bottom: 0, 388 left: 0 389 }; 390 } 391 392 function mergePaddingObject(paddingObject) { 393 return Object.assign({}, getFreshSideObject(), paddingObject); 394 } 395 396 function expandToHashMap(value, keys) { 397 return keys.reduce(function (hashMap, key) { 398 hashMap[key] = value; 399 return hashMap; 400 }, {}); 401 } 402 403 var toPaddingObject = function toPaddingObject(padding, state) { 404 padding = typeof padding === 'function' ? padding(Object.assign({}, state.rects, { 405 placement: state.placement 406 })) : padding; 407 return mergePaddingObject(typeof padding !== 'number' ? padding : expandToHashMap(padding, basePlacements)); 408 }; 409 410 function arrow(_ref) { 411 var _state$modifiersData$; 412 413 var state = _ref.state, 414 name = _ref.name, 415 options = _ref.options; 416 var arrowElement = state.elements.arrow; 417 var popperOffsets = state.modifiersData.popperOffsets; 418 var basePlacement = getBasePlacement$1(state.placement); 419 var axis = getMainAxisFromPlacement(basePlacement); 420 var isVertical = [left, right].indexOf(basePlacement) >= 0; 421 var len = isVertical ? 'height' : 'width'; 422 423 if (!arrowElement || !popperOffsets) { 424 return; 425 } 426 427 var paddingObject = toPaddingObject(options.padding, state); 428 var arrowRect = getLayoutRect(arrowElement); 429 var minProp = axis === 'y' ? top : left; 430 var maxProp = axis === 'y' ? bottom : right; 431 var endDiff = state.rects.reference[len] + state.rects.reference[axis] - popperOffsets[axis] - state.rects.popper[len]; 432 var startDiff = popperOffsets[axis] - state.rects.reference[axis]; 433 var arrowOffsetParent = getOffsetParent(arrowElement); 434 var clientSize = arrowOffsetParent ? axis === 'y' ? arrowOffsetParent.clientHeight || 0 : arrowOffsetParent.clientWidth || 0 : 0; 435 var centerToReference = endDiff / 2 - startDiff / 2; // Make sure the arrow doesn't overflow the popper if the center point is 436 // outside of the popper bounds 437 438 var min = paddingObject[minProp]; 439 var max = clientSize - arrowRect[len] - paddingObject[maxProp]; 440 var center = clientSize / 2 - arrowRect[len] / 2 + centerToReference; 441 var offset = within(min, center, max); // Prevents breaking syntax highlighting... 442 443 var axisProp = axis; 444 state.modifiersData[name] = (_state$modifiersData$ = {}, _state$modifiersData$[axisProp] = offset, _state$modifiersData$.centerOffset = offset - center, _state$modifiersData$); 445 } 446 447 function effect$1(_ref2) { 448 var state = _ref2.state, 449 options = _ref2.options; 450 var _options$element = options.element, 451 arrowElement = _options$element === void 0 ? '[data-popper-arrow]' : _options$element; 452 453 if (arrowElement == null) { 454 return; 455 } // CSS selector 456 457 458 if (typeof arrowElement === 'string') { 459 arrowElement = state.elements.popper.querySelector(arrowElement); 460 461 if (!arrowElement) { 462 return; 463 } 464 } 465 466 if (!contains(state.elements.popper, arrowElement)) { 467 return; 468 } 469 470 state.elements.arrow = arrowElement; 471 } // eslint-disable-next-line import/no-unused-modules 472 473 474 var arrow$1 = { 475 name: 'arrow', 476 enabled: true, 477 phase: 'main', 478 fn: arrow, 479 effect: effect$1, 480 requires: ['popperOffsets'], 481 requiresIfExists: ['preventOverflow'] 482 }; 483 484 function getVariation(placement) { 485 return placement.split('-')[1]; 486 } 487 488 var unsetSides = { 489 top: 'auto', 490 right: 'auto', 491 bottom: 'auto', 492 left: 'auto' 493 }; // Round the offsets to the nearest suitable subpixel based on the DPR. 494 // Zooming can change the DPR, but it seems to report a value that will 495 // cleanly divide the values into the appropriate subpixels. 496 497 function roundOffsetsByDPR(_ref) { 498 var x = _ref.x, 499 y = _ref.y; 500 var win = window; 501 var dpr = win.devicePixelRatio || 1; 502 return { 503 x: round(x * dpr) / dpr || 0, 504 y: round(y * dpr) / dpr || 0 505 }; 506 } 507 508 function mapToStyles(_ref2) { 509 var _Object$assign2; 510 511 var popper = _ref2.popper, 512 popperRect = _ref2.popperRect, 513 placement = _ref2.placement, 514 variation = _ref2.variation, 515 offsets = _ref2.offsets, 516 position = _ref2.position, 517 gpuAcceleration = _ref2.gpuAcceleration, 518 adaptive = _ref2.adaptive, 519 roundOffsets = _ref2.roundOffsets, 520 isFixed = _ref2.isFixed; 521 var _offsets$x = offsets.x, 522 x = _offsets$x === void 0 ? 0 : _offsets$x, 523 _offsets$y = offsets.y, 524 y = _offsets$y === void 0 ? 0 : _offsets$y; 525 526 var _ref3 = typeof roundOffsets === 'function' ? roundOffsets({ 527 x: x, 528 y: y 529 }) : { 530 x: x, 531 y: y 532 }; 533 534 x = _ref3.x; 535 y = _ref3.y; 536 var hasX = offsets.hasOwnProperty('x'); 537 var hasY = offsets.hasOwnProperty('y'); 538 var sideX = left; 539 var sideY = top; 540 var win = window; 541 542 if (adaptive) { 543 var offsetParent = getOffsetParent(popper); 544 var heightProp = 'clientHeight'; 545 var widthProp = 'clientWidth'; 546 547 if (offsetParent === getWindow(popper)) { 548 offsetParent = getDocumentElement(popper); 549 550 if (getComputedStyle$1(offsetParent).position !== 'static' && position === 'absolute') { 551 heightProp = 'scrollHeight'; 552 widthProp = 'scrollWidth'; 553 } 554 } // $FlowFixMe[incompatible-cast]: force type refinement, we compare offsetParent with window above, but Flow doesn't detect it 555 556 557 offsetParent = offsetParent; 558 559 if (placement === top || (placement === left || placement === right) && variation === end) { 560 sideY = bottom; 561 var offsetY = isFixed && win.visualViewport ? win.visualViewport.height : // $FlowFixMe[prop-missing] 562 offsetParent[heightProp]; 563 y -= offsetY - popperRect.height; 564 y *= gpuAcceleration ? 1 : -1; 565 } 566 567 if (placement === left || (placement === top || placement === bottom) && variation === end) { 568 sideX = right; 569 var offsetX = isFixed && win.visualViewport ? win.visualViewport.width : // $FlowFixMe[prop-missing] 570 offsetParent[widthProp]; 571 x -= offsetX - popperRect.width; 572 x *= gpuAcceleration ? 1 : -1; 573 } 574 } 575 576 var commonStyles = Object.assign({ 577 position: position 578 }, adaptive && unsetSides); 579 580 var _ref4 = roundOffsets === true ? roundOffsetsByDPR({ 581 x: x, 582 y: y 583 }) : { 584 x: x, 585 y: y 586 }; 587 588 x = _ref4.x; 589 y = _ref4.y; 590 591 if (gpuAcceleration) { 592 var _Object$assign; 593 594 return Object.assign({}, commonStyles, (_Object$assign = {}, _Object$assign[sideY] = hasY ? '0' : '', _Object$assign[sideX] = hasX ? '0' : '', _Object$assign.transform = (win.devicePixelRatio || 1) <= 1 ? "translate(" + x + "px, " + y + "px)" : "translate3d(" + x + "px, " + y + "px, 0)", _Object$assign)); 595 } 596 597 return Object.assign({}, commonStyles, (_Object$assign2 = {}, _Object$assign2[sideY] = hasY ? y + "px" : '', _Object$assign2[sideX] = hasX ? x + "px" : '', _Object$assign2.transform = '', _Object$assign2)); 598 } 599 600 function computeStyles(_ref5) { 601 var state = _ref5.state, 602 options = _ref5.options; 603 var _options$gpuAccelerat = options.gpuAcceleration, 604 gpuAcceleration = _options$gpuAccelerat === void 0 ? true : _options$gpuAccelerat, 605 _options$adaptive = options.adaptive, 606 adaptive = _options$adaptive === void 0 ? true : _options$adaptive, 607 _options$roundOffsets = options.roundOffsets, 608 roundOffsets = _options$roundOffsets === void 0 ? true : _options$roundOffsets; 609 var commonStyles = { 610 placement: getBasePlacement$1(state.placement), 611 variation: getVariation(state.placement), 612 popper: state.elements.popper, 613 popperRect: state.rects.popper, 614 gpuAcceleration: gpuAcceleration, 615 isFixed: state.options.strategy === 'fixed' 616 }; 617 618 if (state.modifiersData.popperOffsets != null) { 619 state.styles.popper = Object.assign({}, state.styles.popper, mapToStyles(Object.assign({}, commonStyles, { 620 offsets: state.modifiersData.popperOffsets, 621 position: state.options.strategy, 622 adaptive: adaptive, 623 roundOffsets: roundOffsets 624 }))); 625 } 626 627 if (state.modifiersData.arrow != null) { 628 state.styles.arrow = Object.assign({}, state.styles.arrow, mapToStyles(Object.assign({}, commonStyles, { 629 offsets: state.modifiersData.arrow, 630 position: 'absolute', 631 adaptive: false, 632 roundOffsets: roundOffsets 633 }))); 634 } 635 636 state.attributes.popper = Object.assign({}, state.attributes.popper, { 637 'data-popper-placement': state.placement 638 }); 639 } // eslint-disable-next-line import/no-unused-modules 640 641 642 var computeStyles$1 = { 643 name: 'computeStyles', 644 enabled: true, 645 phase: 'beforeWrite', 646 fn: computeStyles, 647 data: {} 648 }; 649 var passive = { 650 passive: true 651 }; 652 653 function effect(_ref) { 654 var state = _ref.state, 655 instance = _ref.instance, 656 options = _ref.options; 657 var _options$scroll = options.scroll, 658 scroll = _options$scroll === void 0 ? true : _options$scroll, 659 _options$resize = options.resize, 660 resize = _options$resize === void 0 ? true : _options$resize; 661 var window = getWindow(state.elements.popper); 662 var scrollParents = [].concat(state.scrollParents.reference, state.scrollParents.popper); 663 664 if (scroll) { 665 scrollParents.forEach(function (scrollParent) { 666 scrollParent.addEventListener('scroll', instance.update, passive); 667 }); 668 } 669 670 if (resize) { 671 window.addEventListener('resize', instance.update, passive); 672 } 673 674 return function () { 675 if (scroll) { 676 scrollParents.forEach(function (scrollParent) { 677 scrollParent.removeEventListener('scroll', instance.update, passive); 678 }); 679 } 680 681 if (resize) { 682 window.removeEventListener('resize', instance.update, passive); 683 } 684 }; 685 } // eslint-disable-next-line import/no-unused-modules 686 687 688 var eventListeners = { 689 name: 'eventListeners', 690 enabled: true, 691 phase: 'write', 692 fn: function fn() {}, 693 effect: effect, 694 data: {} 695 }; 696 var hash$1 = { 697 left: 'right', 698 right: 'left', 699 bottom: 'top', 700 top: 'bottom' 701 }; 702 703 function getOppositePlacement(placement) { 704 return placement.replace(/left|right|bottom|top/g, function (matched) { 705 return hash$1[matched]; 706 }); 707 } 708 709 var hash = { 710 start: 'end', 711 end: 'start' 712 }; 713 714 function getOppositeVariationPlacement(placement) { 715 return placement.replace(/start|end/g, function (matched) { 716 return hash[matched]; 717 }); 718 } 719 720 function getWindowScroll(node) { 721 var win = getWindow(node); 722 var scrollLeft = win.pageXOffset; 723 var scrollTop = win.pageYOffset; 724 return { 725 scrollLeft: scrollLeft, 726 scrollTop: scrollTop 727 }; 728 } 729 730 function getWindowScrollBarX(element) { 731 // If <html> has a CSS width greater than the viewport, then this will be 732 // incorrect for RTL. 733 // Popper 1 is broken in this case and never had a bug report so let's assume 734 // it's not an issue. I don't think anyone ever specifies width on <html> 735 // anyway. 736 // Browsers where the left scrollbar doesn't cause an issue report `0` for 737 // this (e.g. Edge 2019, IE11, Safari) 738 return getBoundingClientRect(getDocumentElement(element)).left + getWindowScroll(element).scrollLeft; 739 } 740 741 function getViewportRect(element) { 742 var win = getWindow(element); 743 var html = getDocumentElement(element); 744 var visualViewport = win.visualViewport; 745 var width = html.clientWidth; 746 var height = html.clientHeight; 747 var x = 0; 748 var y = 0; // NB: This isn't supported on iOS <= 12. If the keyboard is open, the popper 749 // can be obscured underneath it. 750 // Also, `html.clientHeight` adds the bottom bar height in Safari iOS, even 751 // if it isn't open, so if this isn't available, the popper will be detected 752 // to overflow the bottom of the screen too early. 753 754 if (visualViewport) { 755 width = visualViewport.width; 756 height = visualViewport.height; // Uses Layout Viewport (like Chrome; Safari does not currently) 757 // In Chrome, it returns a value very close to 0 (+/-) but contains rounding 758 // errors due to floating point numbers, so we need to check precision. 759 // Safari returns a number <= 0, usually < -1 when pinch-zoomed 760 // Feature detection fails in mobile emulation mode in Chrome. 761 // Math.abs(win.innerWidth / visualViewport.scale - visualViewport.width) < 762 // 0.001 763 // Fallback here: "Not Safari" userAgent 764 765 if (!/^((?!chrome|android).)*safari/i.test(navigator.userAgent)) { 766 x = visualViewport.offsetLeft; 767 y = visualViewport.offsetTop; 768 } 769 } 770 771 return { 772 width: width, 773 height: height, 774 x: x + getWindowScrollBarX(element), 775 y: y 776 }; 777 } // of the `<html>` and `<body>` rect bounds if horizontally scrollable 778 779 780 function getDocumentRect(element) { 781 var _element$ownerDocumen; 782 783 var html = getDocumentElement(element); 784 var winScroll = getWindowScroll(element); 785 var body = (_element$ownerDocumen = element.ownerDocument) == null ? void 0 : _element$ownerDocumen.body; 786 var width = max(html.scrollWidth, html.clientWidth, body ? body.scrollWidth : 0, body ? body.clientWidth : 0); 787 var height = max(html.scrollHeight, html.clientHeight, body ? body.scrollHeight : 0, body ? body.clientHeight : 0); 788 var x = -winScroll.scrollLeft + getWindowScrollBarX(element); 789 var y = -winScroll.scrollTop; 790 791 if (getComputedStyle$1(body || html).direction === 'rtl') { 792 x += max(html.clientWidth, body ? body.clientWidth : 0) - width; 793 } 794 795 return { 796 width: width, 797 height: height, 798 x: x, 799 y: y 800 }; 801 } 802 803 function isScrollParent(element) { 804 // Firefox wants us to check `-x` and `-y` variations as well 805 var _getComputedStyle = getComputedStyle$1(element), 806 overflow = _getComputedStyle.overflow, 807 overflowX = _getComputedStyle.overflowX, 808 overflowY = _getComputedStyle.overflowY; 809 810 return /auto|scroll|overlay|hidden/.test(overflow + overflowY + overflowX); 811 } 812 813 function getScrollParent(node) { 814 if (['html', 'body', '#document'].indexOf(getNodeName(node)) >= 0) { 815 // $FlowFixMe[incompatible-return]: assume body is always available 816 return node.ownerDocument.body; 817 } 818 819 if (isHTMLElement(node) && isScrollParent(node)) { 820 return node; 821 } 822 823 return getScrollParent(getParentNode(node)); 824 } 825 /* 826 given a DOM element, return the list of all scroll parents, up the list of ancesors 827 until we get to the top window object. This list is what we attach scroll listeners 828 to, because if any of these parent elements scroll, we'll need to re-calculate the 829 reference element's position. 830 */ 831 832 833 function listScrollParents(element, list) { 834 var _element$ownerDocumen; 835 836 if (list === void 0) { 837 list = []; 838 } 839 840 var scrollParent = getScrollParent(element); 841 var isBody = scrollParent === ((_element$ownerDocumen = element.ownerDocument) == null ? void 0 : _element$ownerDocumen.body); 842 var win = getWindow(scrollParent); 843 var target = isBody ? [win].concat(win.visualViewport || [], isScrollParent(scrollParent) ? scrollParent : []) : scrollParent; 844 var updatedList = list.concat(target); 845 return isBody ? updatedList : // $FlowFixMe[incompatible-call]: isBody tells us target will be an HTMLElement here 846 updatedList.concat(listScrollParents(getParentNode(target))); 847 } 848 849 function rectToClientRect(rect) { 850 return Object.assign({}, rect, { 851 left: rect.x, 852 top: rect.y, 853 right: rect.x + rect.width, 854 bottom: rect.y + rect.height 855 }); 856 } 857 858 function getInnerBoundingClientRect(element) { 859 var rect = getBoundingClientRect(element); 860 rect.top = rect.top + element.clientTop; 861 rect.left = rect.left + element.clientLeft; 862 rect.bottom = rect.top + element.clientHeight; 863 rect.right = rect.left + element.clientWidth; 864 rect.width = element.clientWidth; 865 rect.height = element.clientHeight; 866 rect.x = rect.left; 867 rect.y = rect.top; 868 return rect; 869 } 870 871 function getClientRectFromMixedType(element, clippingParent) { 872 return clippingParent === viewport ? rectToClientRect(getViewportRect(element)) : isElement$1(clippingParent) ? getInnerBoundingClientRect(clippingParent) : rectToClientRect(getDocumentRect(getDocumentElement(element))); 873 } // A "clipping parent" is an overflowable container with the characteristic of 874 // clipping (or hiding) overflowing elements with a position different from 875 // `initial` 876 877 878 function getClippingParents(element) { 879 var clippingParents = listScrollParents(getParentNode(element)); 880 var canEscapeClipping = ['absolute', 'fixed'].indexOf(getComputedStyle$1(element).position) >= 0; 881 var clipperElement = canEscapeClipping && isHTMLElement(element) ? getOffsetParent(element) : element; 882 883 if (!isElement$1(clipperElement)) { 884 return []; 885 } // $FlowFixMe[incompatible-return]: https://github.com/facebook/flow/issues/1414 886 887 888 return clippingParents.filter(function (clippingParent) { 889 return isElement$1(clippingParent) && contains(clippingParent, clipperElement) && getNodeName(clippingParent) !== 'body'; 890 }); 891 } // Gets the maximum area that the element is visible in due to any number of 892 // clipping parents 893 894 895 function getClippingRect(element, boundary, rootBoundary) { 896 var mainClippingParents = boundary === 'clippingParents' ? getClippingParents(element) : [].concat(boundary); 897 var clippingParents = [].concat(mainClippingParents, [rootBoundary]); 898 var firstClippingParent = clippingParents[0]; 899 var clippingRect = clippingParents.reduce(function (accRect, clippingParent) { 900 var rect = getClientRectFromMixedType(element, clippingParent); 901 accRect.top = max(rect.top, accRect.top); 902 accRect.right = min(rect.right, accRect.right); 903 accRect.bottom = min(rect.bottom, accRect.bottom); 904 accRect.left = max(rect.left, accRect.left); 905 return accRect; 906 }, getClientRectFromMixedType(element, firstClippingParent)); 907 clippingRect.width = clippingRect.right - clippingRect.left; 908 clippingRect.height = clippingRect.bottom - clippingRect.top; 909 clippingRect.x = clippingRect.left; 910 clippingRect.y = clippingRect.top; 911 return clippingRect; 912 } 913 914 function computeOffsets(_ref) { 915 var reference = _ref.reference, 916 element = _ref.element, 917 placement = _ref.placement; 918 var basePlacement = placement ? getBasePlacement$1(placement) : null; 919 var variation = placement ? getVariation(placement) : null; 920 var commonX = reference.x + reference.width / 2 - element.width / 2; 921 var commonY = reference.y + reference.height / 2 - element.height / 2; 922 var offsets; 923 924 switch (basePlacement) { 925 case top: 926 offsets = { 927 x: commonX, 928 y: reference.y - element.height 929 }; 930 break; 931 932 case bottom: 933 offsets = { 934 x: commonX, 935 y: reference.y + reference.height 936 }; 937 break; 938 939 case right: 940 offsets = { 941 x: reference.x + reference.width, 942 y: commonY 943 }; 944 break; 945 946 case left: 947 offsets = { 948 x: reference.x - element.width, 949 y: commonY 950 }; 951 break; 952 953 default: 954 offsets = { 955 x: reference.x, 956 y: reference.y 957 }; 958 } 959 960 var mainAxis = basePlacement ? getMainAxisFromPlacement(basePlacement) : null; 961 962 if (mainAxis != null) { 963 var len = mainAxis === 'y' ? 'height' : 'width'; 964 965 switch (variation) { 966 case start: 967 offsets[mainAxis] = offsets[mainAxis] - (reference[len] / 2 - element[len] / 2); 968 break; 969 970 case end: 971 offsets[mainAxis] = offsets[mainAxis] + (reference[len] / 2 - element[len] / 2); 972 break; 973 } 974 } 975 976 return offsets; 977 } 978 979 function detectOverflow(state, options) { 980 if (options === void 0) { 981 options = {}; 982 } 983 984 var _options = options, 985 _options$placement = _options.placement, 986 placement = _options$placement === void 0 ? state.placement : _options$placement, 987 _options$boundary = _options.boundary, 988 boundary = _options$boundary === void 0 ? clippingParents : _options$boundary, 989 _options$rootBoundary = _options.rootBoundary, 990 rootBoundary = _options$rootBoundary === void 0 ? viewport : _options$rootBoundary, 991 _options$elementConte = _options.elementContext, 992 elementContext = _options$elementConte === void 0 ? popper : _options$elementConte, 993 _options$altBoundary = _options.altBoundary, 994 altBoundary = _options$altBoundary === void 0 ? false : _options$altBoundary, 995 _options$padding = _options.padding, 996 padding = _options$padding === void 0 ? 0 : _options$padding; 997 var paddingObject = mergePaddingObject(typeof padding !== 'number' ? padding : expandToHashMap(padding, basePlacements)); 998 var altContext = elementContext === popper ? reference : popper; 999 var popperRect = state.rects.popper; 1000 var element = state.elements[altBoundary ? altContext : elementContext]; 1001 var clippingClientRect = getClippingRect(isElement$1(element) ? element : element.contextElement || getDocumentElement(state.elements.popper), boundary, rootBoundary); 1002 var referenceClientRect = getBoundingClientRect(state.elements.reference); 1003 var popperOffsets = computeOffsets({ 1004 reference: referenceClientRect, 1005 element: popperRect, 1006 strategy: 'absolute', 1007 placement: placement 1008 }); 1009 var popperClientRect = rectToClientRect(Object.assign({}, popperRect, popperOffsets)); 1010 var elementClientRect = elementContext === popper ? popperClientRect : referenceClientRect; // positive = overflowing the clipping rect 1011 // 0 or negative = within the clipping rect 1012 1013 var overflowOffsets = { 1014 top: clippingClientRect.top - elementClientRect.top + paddingObject.top, 1015 bottom: elementClientRect.bottom - clippingClientRect.bottom + paddingObject.bottom, 1016 left: clippingClientRect.left - elementClientRect.left + paddingObject.left, 1017 right: elementClientRect.right - clippingClientRect.right + paddingObject.right 1018 }; 1019 var offsetData = state.modifiersData.offset; // Offsets can be applied only to the popper element 1020 1021 if (elementContext === popper && offsetData) { 1022 var offset = offsetData[placement]; 1023 Object.keys(overflowOffsets).forEach(function (key) { 1024 var multiply = [right, bottom].indexOf(key) >= 0 ? 1 : -1; 1025 var axis = [top, bottom].indexOf(key) >= 0 ? 'y' : 'x'; 1026 overflowOffsets[key] += offset[axis] * multiply; 1027 }); 1028 } 1029 1030 return overflowOffsets; 1031 } 1032 1033 function computeAutoPlacement(state, options) { 1034 if (options === void 0) { 1035 options = {}; 1036 } 1037 1038 var _options = options, 1039 placement = _options.placement, 1040 boundary = _options.boundary, 1041 rootBoundary = _options.rootBoundary, 1042 padding = _options.padding, 1043 flipVariations = _options.flipVariations, 1044 _options$allowedAutoP = _options.allowedAutoPlacements, 1045 allowedAutoPlacements = _options$allowedAutoP === void 0 ? placements : _options$allowedAutoP; 1046 var variation = getVariation(placement); 1047 var placements$1 = variation ? flipVariations ? variationPlacements : variationPlacements.filter(function (placement) { 1048 return getVariation(placement) === variation; 1049 }) : basePlacements; 1050 var allowedPlacements = placements$1.filter(function (placement) { 1051 return allowedAutoPlacements.indexOf(placement) >= 0; 1052 }); 1053 1054 if (allowedPlacements.length === 0) { 1055 allowedPlacements = placements$1; 1056 } // $FlowFixMe[incompatible-type]: Flow seems to have problems with two array unions... 1057 1058 1059 var overflows = allowedPlacements.reduce(function (acc, placement) { 1060 acc[placement] = detectOverflow(state, { 1061 placement: placement, 1062 boundary: boundary, 1063 rootBoundary: rootBoundary, 1064 padding: padding 1065 })[getBasePlacement$1(placement)]; 1066 return acc; 1067 }, {}); 1068 return Object.keys(overflows).sort(function (a, b) { 1069 return overflows[a] - overflows[b]; 1070 }); 1071 } 1072 1073 function getExpandedFallbackPlacements(placement) { 1074 if (getBasePlacement$1(placement) === auto) { 1075 return []; 1076 } 1077 1078 var oppositePlacement = getOppositePlacement(placement); 1079 return [getOppositeVariationPlacement(placement), oppositePlacement, getOppositeVariationPlacement(oppositePlacement)]; 1080 } 1081 1082 function flip(_ref) { 1083 var state = _ref.state, 1084 options = _ref.options, 1085 name = _ref.name; 1086 1087 if (state.modifiersData[name]._skip) { 1088 return; 1089 } 1090 1091 var _options$mainAxis = options.mainAxis, 1092 checkMainAxis = _options$mainAxis === void 0 ? true : _options$mainAxis, 1093 _options$altAxis = options.altAxis, 1094 checkAltAxis = _options$altAxis === void 0 ? true : _options$altAxis, 1095 specifiedFallbackPlacements = options.fallbackPlacements, 1096 padding = options.padding, 1097 boundary = options.boundary, 1098 rootBoundary = options.rootBoundary, 1099 altBoundary = options.altBoundary, 1100 _options$flipVariatio = options.flipVariations, 1101 flipVariations = _options$flipVariatio === void 0 ? true : _options$flipVariatio, 1102 allowedAutoPlacements = options.allowedAutoPlacements; 1103 var preferredPlacement = state.options.placement; 1104 var basePlacement = getBasePlacement$1(preferredPlacement); 1105 var isBasePlacement = basePlacement === preferredPlacement; 1106 var fallbackPlacements = specifiedFallbackPlacements || (isBasePlacement || !flipVariations ? [getOppositePlacement(preferredPlacement)] : getExpandedFallbackPlacements(preferredPlacement)); 1107 var placements = [preferredPlacement].concat(fallbackPlacements).reduce(function (acc, placement) { 1108 return acc.concat(getBasePlacement$1(placement) === auto ? computeAutoPlacement(state, { 1109 placement: placement, 1110 boundary: boundary, 1111 rootBoundary: rootBoundary, 1112 padding: padding, 1113 flipVariations: flipVariations, 1114 allowedAutoPlacements: allowedAutoPlacements 1115 }) : placement); 1116 }, []); 1117 var referenceRect = state.rects.reference; 1118 var popperRect = state.rects.popper; 1119 var checksMap = new Map(); 1120 var makeFallbackChecks = true; 1121 var firstFittingPlacement = placements[0]; 1122 1123 for (var i = 0; i < placements.length; i++) { 1124 var placement = placements[i]; 1125 1126 var _basePlacement = getBasePlacement$1(placement); 1127 1128 var isStartVariation = getVariation(placement) === start; 1129 var isVertical = [top, bottom].indexOf(_basePlacement) >= 0; 1130 var len = isVertical ? 'width' : 'height'; 1131 var overflow = detectOverflow(state, { 1132 placement: placement, 1133 boundary: boundary, 1134 rootBoundary: rootBoundary, 1135 altBoundary: altBoundary, 1136 padding: padding 1137 }); 1138 var mainVariationSide = isVertical ? isStartVariation ? right : left : isStartVariation ? bottom : top; 1139 1140 if (referenceRect[len] > popperRect[len]) { 1141 mainVariationSide = getOppositePlacement(mainVariationSide); 1142 } 1143 1144 var altVariationSide = getOppositePlacement(mainVariationSide); 1145 var checks = []; 1146 1147 if (checkMainAxis) { 1148 checks.push(overflow[_basePlacement] <= 0); 1149 } 1150 1151 if (checkAltAxis) { 1152 checks.push(overflow[mainVariationSide] <= 0, overflow[altVariationSide] <= 0); 1153 } 1154 1155 if (checks.every(function (check) { 1156 return check; 1157 })) { 1158 firstFittingPlacement = placement; 1159 makeFallbackChecks = false; 1160 break; 1161 } 1162 1163 checksMap.set(placement, checks); 1164 } 1165 1166 if (makeFallbackChecks) { 1167 // `2` may be desired in some cases – research later 1168 var numberOfChecks = flipVariations ? 3 : 1; 1169 1170 var _loop = function _loop(_i) { 1171 var fittingPlacement = placements.find(function (placement) { 1172 var checks = checksMap.get(placement); 1173 1174 if (checks) { 1175 return checks.slice(0, _i).every(function (check) { 1176 return check; 1177 }); 1178 } 1179 }); 1180 1181 if (fittingPlacement) { 1182 firstFittingPlacement = fittingPlacement; 1183 return "break"; 1184 } 1185 }; 1186 1187 for (var _i = numberOfChecks; _i > 0; _i--) { 1188 var _ret = _loop(_i); 1189 1190 if (_ret === "break") break; 1191 } 1192 } 1193 1194 if (state.placement !== firstFittingPlacement) { 1195 state.modifiersData[name]._skip = true; 1196 state.placement = firstFittingPlacement; 1197 state.reset = true; 1198 } 1199 } // eslint-disable-next-line import/no-unused-modules 1200 1201 1202 var flip$1 = { 1203 name: 'flip', 1204 enabled: true, 1205 phase: 'main', 1206 fn: flip, 1207 requiresIfExists: ['offset'], 1208 data: { 1209 _skip: false 1210 } 1211 }; 1212 1213 function getSideOffsets(overflow, rect, preventedOffsets) { 1214 if (preventedOffsets === void 0) { 1215 preventedOffsets = { 1216 x: 0, 1217 y: 0 1218 }; 1219 } 1220 1221 return { 1222 top: overflow.top - rect.height - preventedOffsets.y, 1223 right: overflow.right - rect.width + preventedOffsets.x, 1224 bottom: overflow.bottom - rect.height + preventedOffsets.y, 1225 left: overflow.left - rect.width - preventedOffsets.x 1226 }; 1227 } 1228 1229 function isAnySideFullyClipped(overflow) { 1230 return [top, right, bottom, left].some(function (side) { 1231 return overflow[side] >= 0; 1232 }); 1233 } 1234 1235 function hide(_ref) { 1236 var state = _ref.state, 1237 name = _ref.name; 1238 var referenceRect = state.rects.reference; 1239 var popperRect = state.rects.popper; 1240 var preventedOffsets = state.modifiersData.preventOverflow; 1241 var referenceOverflow = detectOverflow(state, { 1242 elementContext: 'reference' 1243 }); 1244 var popperAltOverflow = detectOverflow(state, { 1245 altBoundary: true 1246 }); 1247 var referenceClippingOffsets = getSideOffsets(referenceOverflow, referenceRect); 1248 var popperEscapeOffsets = getSideOffsets(popperAltOverflow, popperRect, preventedOffsets); 1249 var isReferenceHidden = isAnySideFullyClipped(referenceClippingOffsets); 1250 var hasPopperEscaped = isAnySideFullyClipped(popperEscapeOffsets); 1251 state.modifiersData[name] = { 1252 referenceClippingOffsets: referenceClippingOffsets, 1253 popperEscapeOffsets: popperEscapeOffsets, 1254 isReferenceHidden: isReferenceHidden, 1255 hasPopperEscaped: hasPopperEscaped 1256 }; 1257 state.attributes.popper = Object.assign({}, state.attributes.popper, { 1258 'data-popper-reference-hidden': isReferenceHidden, 1259 'data-popper-escaped': hasPopperEscaped 1260 }); 1261 } // eslint-disable-next-line import/no-unused-modules 1262 1263 1264 var hide$1 = { 1265 name: 'hide', 1266 enabled: true, 1267 phase: 'main', 1268 requiresIfExists: ['preventOverflow'], 1269 fn: hide 1270 }; 1271 1272 function distanceAndSkiddingToXY(placement, rects, offset) { 1273 var basePlacement = getBasePlacement$1(placement); 1274 var invertDistance = [left, top].indexOf(basePlacement) >= 0 ? -1 : 1; 1275 1276 var _ref = typeof offset === 'function' ? offset(Object.assign({}, rects, { 1277 placement: placement 1278 })) : offset, 1279 skidding = _ref[0], 1280 distance = _ref[1]; 1281 1282 skidding = skidding || 0; 1283 distance = (distance || 0) * invertDistance; 1284 return [left, right].indexOf(basePlacement) >= 0 ? { 1285 x: distance, 1286 y: skidding 1287 } : { 1288 x: skidding, 1289 y: distance 1290 }; 1291 } 1292 1293 function offset(_ref2) { 1294 var state = _ref2.state, 1295 options = _ref2.options, 1296 name = _ref2.name; 1297 var _options$offset = options.offset, 1298 offset = _options$offset === void 0 ? [0, 0] : _options$offset; 1299 var data = placements.reduce(function (acc, placement) { 1300 acc[placement] = distanceAndSkiddingToXY(placement, state.rects, offset); 1301 return acc; 1302 }, {}); 1303 var _data$state$placement = data[state.placement], 1304 x = _data$state$placement.x, 1305 y = _data$state$placement.y; 1306 1307 if (state.modifiersData.popperOffsets != null) { 1308 state.modifiersData.popperOffsets.x += x; 1309 state.modifiersData.popperOffsets.y += y; 1310 } 1311 1312 state.modifiersData[name] = data; 1313 } // eslint-disable-next-line import/no-unused-modules 1314 1315 1316 var offset$1 = { 1317 name: 'offset', 1318 enabled: true, 1319 phase: 'main', 1320 requires: ['popperOffsets'], 1321 fn: offset 1322 }; 1323 1324 function popperOffsets(_ref) { 1325 var state = _ref.state, 1326 name = _ref.name; // Offsets are the actual position the popper needs to have to be 1327 // properly positioned near its reference element 1328 // This is the most basic placement, and will be adjusted by 1329 // the modifiers in the next step 1330 1331 state.modifiersData[name] = computeOffsets({ 1332 reference: state.rects.reference, 1333 element: state.rects.popper, 1334 strategy: 'absolute', 1335 placement: state.placement 1336 }); 1337 } // eslint-disable-next-line import/no-unused-modules 1338 1339 1340 var popperOffsets$1 = { 1341 name: 'popperOffsets', 1342 enabled: true, 1343 phase: 'read', 1344 fn: popperOffsets, 1345 data: {} 1346 }; 1347 1348 function getAltAxis(axis) { 1349 return axis === 'x' ? 'y' : 'x'; 1350 } 1351 1352 function preventOverflow(_ref) { 1353 var state = _ref.state, 1354 options = _ref.options, 1355 name = _ref.name; 1356 var _options$mainAxis = options.mainAxis, 1357 checkMainAxis = _options$mainAxis === void 0 ? true : _options$mainAxis, 1358 _options$altAxis = options.altAxis, 1359 checkAltAxis = _options$altAxis === void 0 ? false : _options$altAxis, 1360 boundary = options.boundary, 1361 rootBoundary = options.rootBoundary, 1362 altBoundary = options.altBoundary, 1363 padding = options.padding, 1364 _options$tether = options.tether, 1365 tether = _options$tether === void 0 ? true : _options$tether, 1366 _options$tetherOffset = options.tetherOffset, 1367 tetherOffset = _options$tetherOffset === void 0 ? 0 : _options$tetherOffset; 1368 var overflow = detectOverflow(state, { 1369 boundary: boundary, 1370 rootBoundary: rootBoundary, 1371 padding: padding, 1372 altBoundary: altBoundary 1373 }); 1374 var basePlacement = getBasePlacement$1(state.placement); 1375 var variation = getVariation(state.placement); 1376 var isBasePlacement = !variation; 1377 var mainAxis = getMainAxisFromPlacement(basePlacement); 1378 var altAxis = getAltAxis(mainAxis); 1379 var popperOffsets = state.modifiersData.popperOffsets; 1380 var referenceRect = state.rects.reference; 1381 var popperRect = state.rects.popper; 1382 var tetherOffsetValue = typeof tetherOffset === 'function' ? tetherOffset(Object.assign({}, state.rects, { 1383 placement: state.placement 1384 })) : tetherOffset; 1385 var normalizedTetherOffsetValue = typeof tetherOffsetValue === 'number' ? { 1386 mainAxis: tetherOffsetValue, 1387 altAxis: tetherOffsetValue 1388 } : Object.assign({ 1389 mainAxis: 0, 1390 altAxis: 0 1391 }, tetherOffsetValue); 1392 var offsetModifierState = state.modifiersData.offset ? state.modifiersData.offset[state.placement] : null; 1393 var data = { 1394 x: 0, 1395 y: 0 1396 }; 1397 1398 if (!popperOffsets) { 1399 return; 1400 } 1401 1402 if (checkMainAxis) { 1403 var _offsetModifierState$; 1404 1405 var mainSide = mainAxis === 'y' ? top : left; 1406 var altSide = mainAxis === 'y' ? bottom : right; 1407 var len = mainAxis === 'y' ? 'height' : 'width'; 1408 var offset = popperOffsets[mainAxis]; 1409 var min$1 = offset + overflow[mainSide]; 1410 var max$1 = offset - overflow[altSide]; 1411 var additive = tether ? -popperRect[len] / 2 : 0; 1412 var minLen = variation === start ? referenceRect[len] : popperRect[len]; 1413 var maxLen = variation === start ? -popperRect[len] : -referenceRect[len]; // We need to include the arrow in the calculation so the arrow doesn't go 1414 // outside the reference bounds 1415 1416 var arrowElement = state.elements.arrow; 1417 var arrowRect = tether && arrowElement ? getLayoutRect(arrowElement) : { 1418 width: 0, 1419 height: 0 1420 }; 1421 var arrowPaddingObject = state.modifiersData['arrow#persistent'] ? state.modifiersData['arrow#persistent'].padding : getFreshSideObject(); 1422 var arrowPaddingMin = arrowPaddingObject[mainSide]; 1423 var arrowPaddingMax = arrowPaddingObject[altSide]; // If the reference length is smaller than the arrow length, we don't want 1424 // to include its full size in the calculation. If the reference is small 1425 // and near the edge of a boundary, the popper can overflow even if the 1426 // reference is not overflowing as well (e.g. virtual elements with no 1427 // width or height) 1428 1429 var arrowLen = within(0, referenceRect[len], arrowRect[len]); 1430 var minOffset = isBasePlacement ? referenceRect[len] / 2 - additive - arrowLen - arrowPaddingMin - normalizedTetherOffsetValue.mainAxis : minLen - arrowLen - arrowPaddingMin - normalizedTetherOffsetValue.mainAxis; 1431 var maxOffset = isBasePlacement ? -referenceRect[len] / 2 + additive + arrowLen + arrowPaddingMax + normalizedTetherOffsetValue.mainAxis : maxLen + arrowLen + arrowPaddingMax + normalizedTetherOffsetValue.mainAxis; 1432 var arrowOffsetParent = state.elements.arrow && getOffsetParent(state.elements.arrow); 1433 var clientOffset = arrowOffsetParent ? mainAxis === 'y' ? arrowOffsetParent.clientTop || 0 : arrowOffsetParent.clientLeft || 0 : 0; 1434 var offsetModifierValue = (_offsetModifierState$ = offsetModifierState == null ? void 0 : offsetModifierState[mainAxis]) != null ? _offsetModifierState$ : 0; 1435 var tetherMin = offset + minOffset - offsetModifierValue - clientOffset; 1436 var tetherMax = offset + maxOffset - offsetModifierValue; 1437 var preventedOffset = within(tether ? min(min$1, tetherMin) : min$1, offset, tether ? max(max$1, tetherMax) : max$1); 1438 popperOffsets[mainAxis] = preventedOffset; 1439 data[mainAxis] = preventedOffset - offset; 1440 } 1441 1442 if (checkAltAxis) { 1443 var _offsetModifierState$2; 1444 1445 var _mainSide = mainAxis === 'x' ? top : left; 1446 1447 var _altSide = mainAxis === 'x' ? bottom : right; 1448 1449 var _offset = popperOffsets[altAxis]; 1450 1451 var _len = altAxis === 'y' ? 'height' : 'width'; 1452 1453 var _min = _offset + overflow[_mainSide]; 1454 1455 var _max = _offset - overflow[_altSide]; 1456 1457 var isOriginSide = [top, left].indexOf(basePlacement) !== -1; 1458 1459 var _offsetModifierValue = (_offsetModifierState$2 = offsetModifierState == null ? void 0 : offsetModifierState[altAxis]) != null ? _offsetModifierState$2 : 0; 1460 1461 var _tetherMin = isOriginSide ? _min : _offset - referenceRect[_len] - popperRect[_len] - _offsetModifierValue + normalizedTetherOffsetValue.altAxis; 1462 1463 var _tetherMax = isOriginSide ? _offset + referenceRect[_len] + popperRect[_len] - _offsetModifierValue - normalizedTetherOffsetValue.altAxis : _max; 1464 1465 var _preventedOffset = tether && isOriginSide ? withinMaxClamp(_tetherMin, _offset, _tetherMax) : within(tether ? _tetherMin : _min, _offset, tether ? _tetherMax : _max); 1466 1467 popperOffsets[altAxis] = _preventedOffset; 1468 data[altAxis] = _preventedOffset - _offset; 1469 } 1470 1471 state.modifiersData[name] = data; 1472 } // eslint-disable-next-line import/no-unused-modules 1473 1474 1475 var preventOverflow$1 = { 1476 name: 'preventOverflow', 1477 enabled: true, 1478 phase: 'main', 1479 fn: preventOverflow, 1480 requiresIfExists: ['offset'] 1481 }; 1482 1483 function getHTMLElementScroll(element) { 1484 return { 1485 scrollLeft: element.scrollLeft, 1486 scrollTop: element.scrollTop 1487 }; 1488 } 1489 1490 function getNodeScroll(node) { 1491 if (node === getWindow(node) || !isHTMLElement(node)) { 1492 return getWindowScroll(node); 1493 } else { 1494 return getHTMLElementScroll(node); 1495 } 1496 } 1497 1498 function isElementScaled(element) { 1499 var rect = element.getBoundingClientRect(); 1500 var scaleX = round(rect.width) / element.offsetWidth || 1; 1501 var scaleY = round(rect.height) / element.offsetHeight || 1; 1502 return scaleX !== 1 || scaleY !== 1; 1503 } // Returns the composite rect of an element relative to its offsetParent. 1504 // Composite means it takes into account transforms as well as layout. 1505 1506 1507 function getCompositeRect(elementOrVirtualElement, offsetParent, isFixed) { 1508 if (isFixed === void 0) { 1509 isFixed = false; 1510 } 1511 1512 var isOffsetParentAnElement = isHTMLElement(offsetParent); 1513 var offsetParentIsScaled = isHTMLElement(offsetParent) && isElementScaled(offsetParent); 1514 var documentElement = getDocumentElement(offsetParent); 1515 var rect = getBoundingClientRect(elementOrVirtualElement, offsetParentIsScaled); 1516 var scroll = { 1517 scrollLeft: 0, 1518 scrollTop: 0 1519 }; 1520 var offsets = { 1521 x: 0, 1522 y: 0 1523 }; 1524 1525 if (isOffsetParentAnElement || !isOffsetParentAnElement && !isFixed) { 1526 if (getNodeName(offsetParent) !== 'body' || // https://github.com/popperjs/popper-core/issues/1078 1527 isScrollParent(documentElement)) { 1528 scroll = getNodeScroll(offsetParent); 1529 } 1530 1531 if (isHTMLElement(offsetParent)) { 1532 offsets = getBoundingClientRect(offsetParent, true); 1533 offsets.x += offsetParent.clientLeft; 1534 offsets.y += offsetParent.clientTop; 1535 } else if (documentElement) { 1536 offsets.x = getWindowScrollBarX(documentElement); 1537 } 1538 } 1539 1540 return { 1541 x: rect.left + scroll.scrollLeft - offsets.x, 1542 y: rect.top + scroll.scrollTop - offsets.y, 1543 width: rect.width, 1544 height: rect.height 1545 }; 1546 } 1547 1548 function order(modifiers) { 1549 var map = new Map(); 1550 var visited = new Set(); 1551 var result = []; 1552 modifiers.forEach(function (modifier) { 1553 map.set(modifier.name, modifier); 1554 }); // On visiting object, check for its dependencies and visit them recursively 1555 1556 function sort(modifier) { 1557 visited.add(modifier.name); 1558 var requires = [].concat(modifier.requires || [], modifier.requiresIfExists || []); 1559 requires.forEach(function (dep) { 1560 if (!visited.has(dep)) { 1561 var depModifier = map.get(dep); 1562 1563 if (depModifier) { 1564 sort(depModifier); 1565 } 1566 } 1567 }); 1568 result.push(modifier); 1569 } 1570 1571 modifiers.forEach(function (modifier) { 1572 if (!visited.has(modifier.name)) { 1573 // check for visited object 1574 sort(modifier); 1575 } 1576 }); 1577 return result; 1578 } 1579 1580 function orderModifiers(modifiers) { 1581 // order based on dependencies 1582 var orderedModifiers = order(modifiers); // order based on phase 1583 1584 return modifierPhases.reduce(function (acc, phase) { 1585 return acc.concat(orderedModifiers.filter(function (modifier) { 1586 return modifier.phase === phase; 1587 })); 1588 }, []); 1589 } 1590 1591 function debounce$1(fn) { 1592 var pending; 1593 return function () { 1594 if (!pending) { 1595 pending = new Promise(function (resolve) { 1596 Promise.resolve().then(function () { 1597 pending = undefined; 1598 resolve(fn()); 1599 }); 1600 }); 1601 } 1602 1603 return pending; 1604 }; 1605 } 1606 1607 function mergeByName(modifiers) { 1608 var merged = modifiers.reduce(function (merged, current) { 1609 var existing = merged[current.name]; 1610 merged[current.name] = existing ? Object.assign({}, existing, current, { 1611 options: Object.assign({}, existing.options, current.options), 1612 data: Object.assign({}, existing.data, current.data) 1613 }) : current; 1614 return merged; 1615 }, {}); // IE11 does not support Object.values 1616 1617 return Object.keys(merged).map(function (key) { 1618 return merged[key]; 1619 }); 1620 } 1621 1622 var DEFAULT_OPTIONS = { 1623 placement: 'bottom', 1624 modifiers: [], 1625 strategy: 'absolute' 1626 }; 1627 1628 function areValidElements() { 1629 for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { 1630 args[_key] = arguments[_key]; 1631 } 1632 1633 return !args.some(function (element) { 1634 return !(element && typeof element.getBoundingClientRect === 'function'); 1635 }); 1636 } 1637 1638 function popperGenerator(generatorOptions) { 1639 if (generatorOptions === void 0) { 1640 generatorOptions = {}; 1641 } 1642 1643 var _generatorOptions = generatorOptions, 1644 _generatorOptions$def = _generatorOptions.defaultModifiers, 1645 defaultModifiers = _generatorOptions$def === void 0 ? [] : _generatorOptions$def, 1646 _generatorOptions$def2 = _generatorOptions.defaultOptions, 1647 defaultOptions = _generatorOptions$def2 === void 0 ? DEFAULT_OPTIONS : _generatorOptions$def2; 1648 return function createPopper(reference, popper, options) { 1649 if (options === void 0) { 1650 options = defaultOptions; 1651 } 1652 1653 var state = { 1654 placement: 'bottom', 1655 orderedModifiers: [], 1656 options: Object.assign({}, DEFAULT_OPTIONS, defaultOptions), 1657 modifiersData: {}, 1658 elements: { 1659 reference: reference, 1660 popper: popper 1661 }, 1662 attributes: {}, 1663 styles: {} 1664 }; 1665 var effectCleanupFns = []; 1666 var isDestroyed = false; 1667 var instance = { 1668 state: state, 1669 setOptions: function setOptions(setOptionsAction) { 1670 var options = typeof setOptionsAction === 'function' ? setOptionsAction(state.options) : setOptionsAction; 1671 cleanupModifierEffects(); 1672 state.options = Object.assign({}, defaultOptions, state.options, options); 1673 state.scrollParents = { 1674 reference: isElement$1(reference) ? listScrollParents(reference) : reference.contextElement ? listScrollParents(reference.contextElement) : [], 1675 popper: listScrollParents(popper) 1676 }; // Orders the modifiers based on their dependencies and `phase` 1677 // properties 1678 1679 var orderedModifiers = orderModifiers(mergeByName([].concat(defaultModifiers, state.options.modifiers))); // Strip out disabled modifiers 1680 1681 state.orderedModifiers = orderedModifiers.filter(function (m) { 1682 return m.enabled; 1683 }); // Validate the provided modifiers so that the consumer will get warned 1684 1685 runModifierEffects(); 1686 return instance.update(); 1687 }, 1688 // Sync update – it will always be executed, even if not necessary. This 1689 // is useful for low frequency updates where sync behavior simplifies the 1690 // logic. 1691 // For high frequency updates (e.g. `resize` and `scroll` events), always 1692 // prefer the async Popper#update method 1693 forceUpdate: function forceUpdate() { 1694 if (isDestroyed) { 1695 return; 1696 } 1697 1698 var _state$elements = state.elements, 1699 reference = _state$elements.reference, 1700 popper = _state$elements.popper; // Don't proceed if `reference` or `popper` are not valid elements 1701 // anymore 1702 1703 if (!areValidElements(reference, popper)) { 1704 return; 1705 } // Store the reference and popper rects to be read by modifiers 1706 1707 1708 state.rects = { 1709 reference: getCompositeRect(reference, getOffsetParent(popper), state.options.strategy === 'fixed'), 1710 popper: getLayoutRect(popper) 1711 }; // Modifiers have the ability to reset the current update cycle. The 1712 // most common use case for this is the `flip` modifier changing the 1713 // placement, which then needs to re-run all the modifiers, because the 1714 // logic was previously ran for the previous placement and is therefore 1715 // stale/incorrect 1716 1717 state.reset = false; 1718 state.placement = state.options.placement; // On each update cycle, the `modifiersData` property for each modifier 1719 // is filled with the initial data specified by the modifier. This means 1720 // it doesn't persist and is fresh on each update. 1721 // To ensure persistent data, use `${name}#persistent` 1722 1723 state.orderedModifiers.forEach(function (modifier) { 1724 return state.modifiersData[modifier.name] = Object.assign({}, modifier.data); 1725 }); 1726 1727 for (var index = 0; index < state.orderedModifiers.length; index++) { 1728 if (state.reset === true) { 1729 state.reset = false; 1730 index = -1; 1731 continue; 1732 } 1733 1734 var _state$orderedModifie = state.orderedModifiers[index], 1735 fn = _state$orderedModifie.fn, 1736 _state$orderedModifie2 = _state$orderedModifie.options, 1737 _options = _state$orderedModifie2 === void 0 ? {} : _state$orderedModifie2, 1738 name = _state$orderedModifie.name; 1739 1740 if (typeof fn === 'function') { 1741 state = fn({ 1742 state: state, 1743 options: _options, 1744 name: name, 1745 instance: instance 1746 }) || state; 1747 } 1748 } 1749 }, 1750 // Async and optimistically optimized update – it will not be executed if 1751 // not necessary (debounced to run at most once-per-tick) 1752 update: debounce$1(function () { 1753 return new Promise(function (resolve) { 1754 instance.forceUpdate(); 1755 resolve(state); 1756 }); 1757 }), 1758 destroy: function destroy() { 1759 cleanupModifierEffects(); 1760 isDestroyed = true; 1761 } 1762 }; 1763 1764 if (!areValidElements(reference, popper)) { 1765 return instance; 1766 } 1767 1768 instance.setOptions(options).then(function (state) { 1769 if (!isDestroyed && options.onFirstUpdate) { 1770 options.onFirstUpdate(state); 1771 } 1772 }); // Modifiers have the ability to execute arbitrary code before the first 1773 // update cycle runs. They will be executed in the same order as the update 1774 // cycle. This is useful when a modifier adds some persistent data that 1775 // other modifiers need to use, but the modifier is run after the dependent 1776 // one. 1777 1778 function runModifierEffects() { 1779 state.orderedModifiers.forEach(function (_ref3) { 1780 var name = _ref3.name, 1781 _ref3$options = _ref3.options, 1782 options = _ref3$options === void 0 ? {} : _ref3$options, 1783 effect = _ref3.effect; 1784 1785 if (typeof effect === 'function') { 1786 var cleanupFn = effect({ 1787 state: state, 1788 name: name, 1789 instance: instance, 1790 options: options 1791 }); 1792 1793 var noopFn = function noopFn() {}; 1794 1795 effectCleanupFns.push(cleanupFn || noopFn); 1796 } 1797 }); 1798 } 1799 1800 function cleanupModifierEffects() { 1801 effectCleanupFns.forEach(function (fn) { 1802 return fn(); 1803 }); 1804 effectCleanupFns = []; 1805 } 1806 1807 return instance; 1808 }; 1809 } 1810 1811 var defaultModifiers = [eventListeners, popperOffsets$1, computeStyles$1, applyStyles$1, offset$1, flip$1, preventOverflow$1, arrow$1, hide$1]; 1812 var createPopper = /*#__PURE__*/popperGenerator({ 1813 defaultModifiers: defaultModifiers 1814 }); // eslint-disable-next-line import/no-unused-modules 1815 1816 /**! 1817 * tippy.js v6.3.7 1818 * (c) 2017-2021 atomiks 1819 * MIT License 1820 */ 1821 1822 var BOX_CLASS = "tippy-box"; 1823 var CONTENT_CLASS = "tippy-content"; 1824 var BACKDROP_CLASS = "tippy-backdrop"; 1825 var ARROW_CLASS = "tippy-arrow"; 1826 var SVG_ARROW_CLASS = "tippy-svg-arrow"; 1827 var TOUCH_OPTIONS = { 1828 passive: true, 1829 capture: true 1830 }; 1831 1832 var TIPPY_DEFAULT_APPEND_TO = function TIPPY_DEFAULT_APPEND_TO() { 1833 return document.body; 1834 }; 1835 1836 function getValueAtIndexOrReturn(value, index, defaultValue) { 1837 if (Array.isArray(value)) { 1838 var v = value[index]; 1839 return v == null ? Array.isArray(defaultValue) ? defaultValue[index] : defaultValue : v; 1840 } 1841 1842 return value; 1843 } 1844 1845 function isType(value, type) { 1846 var str = {}.toString.call(value); 1847 return str.indexOf('[object') === 0 && str.indexOf(type + "]") > -1; 1848 } 1849 1850 function invokeWithArgsOrReturn(value, args) { 1851 return typeof value === 'function' ? value.apply(void 0, args) : value; 1852 } 1853 1854 function debounce(fn, ms) { 1855 // Avoid wrapping in `setTimeout` if ms is 0 anyway 1856 if (ms === 0) { 1857 return fn; 1858 } 1859 1860 var timeout; 1861 return function (arg) { 1862 clearTimeout(timeout); 1863 timeout = setTimeout(function () { 1864 fn(arg); 1865 }, ms); 1866 }; 1867 } 1868 1869 function splitBySpaces(value) { 1870 return value.split(/\s+/).filter(Boolean); 1871 } 1872 1873 function normalizeToArray(value) { 1874 return [].concat(value); 1875 } 1876 1877 function pushIfUnique(arr, value) { 1878 if (arr.indexOf(value) === -1) { 1879 arr.push(value); 1880 } 1881 } 1882 1883 function unique(arr) { 1884 return arr.filter(function (item, index) { 1885 return arr.indexOf(item) === index; 1886 }); 1887 } 1888 1889 function getBasePlacement(placement) { 1890 return placement.split('-')[0]; 1891 } 1892 1893 function arrayFrom(value) { 1894 return [].slice.call(value); 1895 } 1896 1897 function removeUndefinedProps(obj) { 1898 return Object.keys(obj).reduce(function (acc, key) { 1899 if (obj[key] !== undefined) { 1900 acc[key] = obj[key]; 1901 } 1902 1903 return acc; 1904 }, {}); 1905 } 1906 1907 function div() { 1908 return document.createElement('div'); 1909 } 1910 1911 function isElement(value) { 1912 return ['Element', 'Fragment'].some(function (type) { 1913 return isType(value, type); 1914 }); 1915 } 1916 1917 function isNodeList(value) { 1918 return isType(value, 'NodeList'); 1919 } 1920 1921 function isMouseEvent(value) { 1922 return isType(value, 'MouseEvent'); 1923 } 1924 1925 function isReferenceElement(value) { 1926 return !!(value && value._tippy && value._tippy.reference === value); 1927 } 1928 1929 function getArrayOfElements(value) { 1930 if (isElement(value)) { 1931 return [value]; 1932 } 1933 1934 if (isNodeList(value)) { 1935 return arrayFrom(value); 1936 } 1937 1938 if (Array.isArray(value)) { 1939 return value; 1940 } 1941 1942 return arrayFrom(document.querySelectorAll(value)); 1943 } 1944 1945 function setTransitionDuration(els, value) { 1946 els.forEach(function (el) { 1947 if (el) { 1948 el.style.transitionDuration = value + "ms"; 1949 } 1950 }); 1951 } 1952 1953 function setVisibilityState(els, state) { 1954 els.forEach(function (el) { 1955 if (el) { 1956 el.setAttribute('data-state', state); 1957 } 1958 }); 1959 } 1960 1961 function getOwnerDocument(elementOrElements) { 1962 var _element$ownerDocumen; 1963 1964 var _normalizeToArray = normalizeToArray(elementOrElements), 1965 element = _normalizeToArray[0]; // Elements created via a <template> have an ownerDocument with no reference to the body 1966 1967 1968 return element != null && (_element$ownerDocumen = element.ownerDocument) != null && _element$ownerDocumen.body ? element.ownerDocument : document; 1969 } 1970 1971 function isCursorOutsideInteractiveBorder(popperTreeData, event) { 1972 var clientX = event.clientX, 1973 clientY = event.clientY; 1974 return popperTreeData.every(function (_ref) { 1975 var popperRect = _ref.popperRect, 1976 popperState = _ref.popperState, 1977 props = _ref.props; 1978 var interactiveBorder = props.interactiveBorder; 1979 var basePlacement = getBasePlacement(popperState.placement); 1980 var offsetData = popperState.modifiersData.offset; 1981 1982 if (!offsetData) { 1983 return true; 1984 } 1985 1986 var topDistance = basePlacement === 'bottom' ? offsetData.top.y : 0; 1987 var bottomDistance = basePlacement === 'top' ? offsetData.bottom.y : 0; 1988 var leftDistance = basePlacement === 'right' ? offsetData.left.x : 0; 1989 var rightDistance = basePlacement === 'left' ? offsetData.right.x : 0; 1990 var exceedsTop = popperRect.top - clientY + topDistance > interactiveBorder; 1991 var exceedsBottom = clientY - popperRect.bottom - bottomDistance > interactiveBorder; 1992 var exceedsLeft = popperRect.left - clientX + leftDistance > interactiveBorder; 1993 var exceedsRight = clientX - popperRect.right - rightDistance > interactiveBorder; 1994 return exceedsTop || exceedsBottom || exceedsLeft || exceedsRight; 1995 }); 1996 } 1997 1998 function updateTransitionEndListener(box, action, listener) { 1999 var method = action + "EventListener"; // some browsers apparently support `transition` (unprefixed) but only fire 2000 // `webkitTransitionEnd`... 2001 2002 ['transitionend', 'webkitTransitionEnd'].forEach(function (event) { 2003 box[method](event, listener); 2004 }); 2005 } 2006 /** 2007 * Compared to xxx.contains, this function works for dom structures with shadow 2008 * dom 2009 */ 2010 2011 2012 function actualContains(parent, child) { 2013 var target = child; 2014 2015 while (target) { 2016 var _target$getRootNode; 2017 2018 if (parent.contains(target)) { 2019 return true; 2020 } 2021 2022 target = target.getRootNode == null ? void 0 : (_target$getRootNode = target.getRootNode()) == null ? void 0 : _target$getRootNode.host; 2023 } 2024 2025 return false; 2026 } 2027 2028 var currentInput = { 2029 isTouch: false 2030 }; 2031 var lastMouseMoveTime = 0; 2032 /** 2033 * When a `touchstart` event is fired, it's assumed the user is using touch 2034 * input. We'll bind a `mousemove` event listener to listen for mouse input in 2035 * the future. This way, the `isTouch` property is fully dynamic and will handle 2036 * hybrid devices that use a mix of touch + mouse input. 2037 */ 2038 2039 function onDocumentTouchStart() { 2040 if (currentInput.isTouch) { 2041 return; 2042 } 2043 2044 currentInput.isTouch = true; 2045 2046 if (window.performance) { 2047 document.addEventListener('mousemove', onDocumentMouseMove); 2048 } 2049 } 2050 /** 2051 * When two `mousemove` event are fired consecutively within 20ms, it's assumed 2052 * the user is using mouse input again. `mousemove` can fire on touch devices as 2053 * well, but very rarely that quickly. 2054 */ 2055 2056 2057 function onDocumentMouseMove() { 2058 var now = performance.now(); 2059 2060 if (now - lastMouseMoveTime < 20) { 2061 currentInput.isTouch = false; 2062 document.removeEventListener('mousemove', onDocumentMouseMove); 2063 } 2064 2065 lastMouseMoveTime = now; 2066 } 2067 /** 2068 * When an element is in focus and has a tippy, leaving the tab/window and 2069 * returning causes it to show again. For mouse users this is unexpected, but 2070 * for keyboard use it makes sense. 2071 * TODO: find a better technique to solve this problem 2072 */ 2073 2074 2075 function onWindowBlur() { 2076 var activeElement = document.activeElement; 2077 2078 if (isReferenceElement(activeElement)) { 2079 var instance = activeElement._tippy; 2080 2081 if (activeElement.blur && !instance.state.isVisible) { 2082 activeElement.blur(); 2083 } 2084 } 2085 } 2086 2087 function bindGlobalEventListeners() { 2088 document.addEventListener('touchstart', onDocumentTouchStart, TOUCH_OPTIONS); 2089 window.addEventListener('blur', onWindowBlur); 2090 } 2091 2092 var isBrowser = typeof window !== 'undefined' && typeof document !== 'undefined'; 2093 var isIE11 = isBrowser ? // @ts-ignore 2094 !!window.msCrypto : false; 2095 var pluginProps = { 2096 animateFill: false, 2097 followCursor: false, 2098 inlinePositioning: false, 2099 sticky: false 2100 }; 2101 var renderProps = { 2102 allowHTML: false, 2103 animation: 'fade', 2104 arrow: true, 2105 content: '', 2106 inertia: false, 2107 maxWidth: 350, 2108 role: 'tooltip', 2109 theme: '', 2110 zIndex: 9999 2111 }; 2112 var defaultProps = Object.assign({ 2113 appendTo: TIPPY_DEFAULT_APPEND_TO, 2114 aria: { 2115 content: 'auto', 2116 expanded: 'auto' 2117 }, 2118 delay: 0, 2119 duration: [300, 250], 2120 getReferenceClientRect: null, 2121 hideOnClick: true, 2122 ignoreAttributes: false, 2123 interactive: false, 2124 interactiveBorder: 2, 2125 interactiveDebounce: 0, 2126 moveTransition: '', 2127 offset: [0, 10], 2128 onAfterUpdate: function onAfterUpdate() {}, 2129 onBeforeUpdate: function onBeforeUpdate() {}, 2130 onCreate: function onCreate() {}, 2131 onDestroy: function onDestroy() {}, 2132 onHidden: function onHidden() {}, 2133 onHide: function onHide() {}, 2134 onMount: function onMount() {}, 2135 onShow: function onShow() {}, 2136 onShown: function onShown() {}, 2137 onTrigger: function onTrigger() {}, 2138 onUntrigger: function onUntrigger() {}, 2139 onClickOutside: function onClickOutside() {}, 2140 placement: 'top', 2141 plugins: [], 2142 popperOptions: {}, 2143 render: null, 2144 showOnCreate: false, 2145 touch: true, 2146 trigger: 'mouseenter focus', 2147 triggerTarget: null 2148 }, pluginProps, renderProps); 2149 var defaultKeys = Object.keys(defaultProps); 2150 2151 var setDefaultProps = function setDefaultProps(partialProps) { 2152 var keys = Object.keys(partialProps); 2153 keys.forEach(function (key) { 2154 defaultProps[key] = partialProps[key]; 2155 }); 2156 }; 2157 2158 function getExtendedPassedProps(passedProps) { 2159 var plugins = passedProps.plugins || []; 2160 var pluginProps = plugins.reduce(function (acc, plugin) { 2161 var name = plugin.name, 2162 defaultValue = plugin.defaultValue; 2163 2164 if (name) { 2165 var _name; 2166 2167 acc[name] = passedProps[name] !== undefined ? passedProps[name] : (_name = defaultProps[name]) != null ? _name : defaultValue; 2168 } 2169 2170 return acc; 2171 }, {}); 2172 return Object.assign({}, passedProps, pluginProps); 2173 } 2174 2175 function getDataAttributeProps(reference, plugins) { 2176 var propKeys = plugins ? Object.keys(getExtendedPassedProps(Object.assign({}, defaultProps, { 2177 plugins: plugins 2178 }))) : defaultKeys; 2179 var props = propKeys.reduce(function (acc, key) { 2180 var valueAsString = (reference.getAttribute("data-tippy-" + key) || '').trim(); 2181 2182 if (!valueAsString) { 2183 return acc; 2184 } 2185 2186 if (key === 'content') { 2187 acc[key] = valueAsString; 2188 } else { 2189 try { 2190 acc[key] = JSON.parse(valueAsString); 2191 } catch (e) { 2192 acc[key] = valueAsString; 2193 } 2194 } 2195 2196 return acc; 2197 }, {}); 2198 return props; 2199 } 2200 2201 function evaluateProps(reference, props) { 2202 var out = Object.assign({}, props, { 2203 content: invokeWithArgsOrReturn(props.content, [reference]) 2204 }, props.ignoreAttributes ? {} : getDataAttributeProps(reference, props.plugins)); 2205 out.aria = Object.assign({}, defaultProps.aria, out.aria); 2206 out.aria = { 2207 expanded: out.aria.expanded === 'auto' ? props.interactive : out.aria.expanded, 2208 content: out.aria.content === 'auto' ? props.interactive ? null : 'describedby' : out.aria.content 2209 }; 2210 return out; 2211 } 2212 2213 var innerHTML = function innerHTML() { 2214 return 'innerHTML'; 2215 }; 2216 2217 function dangerouslySetInnerHTML(element, html) { 2218 element[innerHTML()] = html; 2219 } 2220 2221 function createArrowElement(value) { 2222 var arrow = div(); 2223 2224 if (value === true) { 2225 arrow.className = ARROW_CLASS; 2226 } else { 2227 arrow.className = SVG_ARROW_CLASS; 2228 2229 if (isElement(value)) { 2230 arrow.appendChild(value); 2231 } else { 2232 dangerouslySetInnerHTML(arrow, value); 2233 } 2234 } 2235 2236 return arrow; 2237 } 2238 2239 function setContent(content, props) { 2240 if (isElement(props.content)) { 2241 dangerouslySetInnerHTML(content, ''); 2242 content.appendChild(props.content); 2243 } else if (typeof props.content !== 'function') { 2244 if (props.allowHTML) { 2245 dangerouslySetInnerHTML(content, props.content); 2246 } else { 2247 content.textContent = props.content; 2248 } 2249 } 2250 } 2251 2252 function getChildren(popper) { 2253 var box = popper.firstElementChild; 2254 var boxChildren = arrayFrom(box.children); 2255 return { 2256 box: box, 2257 content: boxChildren.find(function (node) { 2258 return node.classList.contains(CONTENT_CLASS); 2259 }), 2260 arrow: boxChildren.find(function (node) { 2261 return node.classList.contains(ARROW_CLASS) || node.classList.contains(SVG_ARROW_CLASS); 2262 }), 2263 backdrop: boxChildren.find(function (node) { 2264 return node.classList.contains(BACKDROP_CLASS); 2265 }) 2266 }; 2267 } 2268 2269 function render(instance) { 2270 var popper = div(); 2271 var box = div(); 2272 box.className = BOX_CLASS; 2273 box.setAttribute('data-state', 'hidden'); 2274 box.setAttribute('tabindex', '-1'); 2275 var content = div(); 2276 content.className = CONTENT_CLASS; 2277 content.setAttribute('data-state', 'hidden'); 2278 setContent(content, instance.props); 2279 popper.appendChild(box); 2280 box.appendChild(content); 2281 onUpdate(instance.props, instance.props); 2282 2283 function onUpdate(prevProps, nextProps) { 2284 var _getChildren = getChildren(popper), 2285 box = _getChildren.box, 2286 content = _getChildren.content, 2287 arrow = _getChildren.arrow; 2288 2289 if (nextProps.theme) { 2290 box.setAttribute('data-theme', nextProps.theme); 2291 } else { 2292 box.removeAttribute('data-theme'); 2293 } 2294 2295 if (typeof nextProps.animation === 'string') { 2296 box.setAttribute('data-animation', nextProps.animation); 2297 } else { 2298 box.removeAttribute('data-animation'); 2299 } 2300 2301 if (nextProps.inertia) { 2302 box.setAttribute('data-inertia', ''); 2303 } else { 2304 box.removeAttribute('data-inertia'); 2305 } 2306 2307 box.style.maxWidth = typeof nextProps.maxWidth === 'number' ? nextProps.maxWidth + "px" : nextProps.maxWidth; 2308 2309 if (nextProps.role) { 2310 box.setAttribute('role', nextProps.role); 2311 } else { 2312 box.removeAttribute('role'); 2313 } 2314 2315 if (prevProps.content !== nextProps.content || prevProps.allowHTML !== nextProps.allowHTML) { 2316 setContent(content, instance.props); 2317 } 2318 2319 if (nextProps.arrow) { 2320 if (!arrow) { 2321 box.appendChild(createArrowElement(nextProps.arrow)); 2322 } else if (prevProps.arrow !== nextProps.arrow) { 2323 box.removeChild(arrow); 2324 box.appendChild(createArrowElement(nextProps.arrow)); 2325 } 2326 } else if (arrow) { 2327 box.removeChild(arrow); 2328 } 2329 } 2330 2331 return { 2332 popper: popper, 2333 onUpdate: onUpdate 2334 }; 2335 } // Runtime check to identify if the render function is the default one; this 2336 // way we can apply default CSS transitions logic and it can be tree-shaken away 2337 2338 2339 render.$$tippy = true; 2340 var idCounter = 1; 2341 var mouseMoveListeners = []; // Used by `hideAll()` 2342 2343 var mountedInstances = []; 2344 2345 function createTippy(reference, passedProps) { 2346 var props = evaluateProps(reference, Object.assign({}, defaultProps, getExtendedPassedProps(removeUndefinedProps(passedProps)))); // =========================================================================== 2347 // 🔒 Private members 2348 // =========================================================================== 2349 2350 var showTimeout; 2351 var hideTimeout; 2352 var scheduleHideAnimationFrame; 2353 var isVisibleFromClick = false; 2354 var didHideDueToDocumentMouseDown = false; 2355 var didTouchMove = false; 2356 var ignoreOnFirstUpdate = false; 2357 var lastTriggerEvent; 2358 var currentTransitionEndListener; 2359 var onFirstUpdate; 2360 var listeners = []; 2361 var debouncedOnMouseMove = debounce(onMouseMove, props.interactiveDebounce); 2362 var currentTarget; // =========================================================================== 2363 // 🔑 Public members 2364 // =========================================================================== 2365 2366 var id = idCounter++; 2367 var popperInstance = null; 2368 var plugins = unique(props.plugins); 2369 var state = { 2370 // Is the instance currently enabled? 2371 isEnabled: true, 2372 // Is the tippy currently showing and not transitioning out? 2373 isVisible: false, 2374 // Has the instance been destroyed? 2375 isDestroyed: false, 2376 // Is the tippy currently mounted to the DOM? 2377 isMounted: false, 2378 // Has the tippy finished transitioning in? 2379 isShown: false 2380 }; 2381 var instance = { 2382 // properties 2383 id: id, 2384 reference: reference, 2385 popper: div(), 2386 popperInstance: popperInstance, 2387 props: props, 2388 state: state, 2389 plugins: plugins, 2390 // methods 2391 clearDelayTimeouts: clearDelayTimeouts, 2392 setProps: setProps, 2393 setContent: setContent, 2394 show: show, 2395 hide: hide, 2396 hideWithInteractivity: hideWithInteractivity, 2397 enable: enable, 2398 disable: disable, 2399 unmount: unmount, 2400 destroy: destroy 2401 }; // TODO: Investigate why this early return causes a TDZ error in the tests — 2402 // it doesn't seem to happen in the browser 2403 2404 /* istanbul ignore if */ 2405 2406 if (!props.render) { 2407 return instance; 2408 } // =========================================================================== 2409 // Initial mutations 2410 // =========================================================================== 2411 2412 2413 var _props$render = props.render(instance), 2414 popper = _props$render.popper, 2415 onUpdate = _props$render.onUpdate; 2416 2417 popper.setAttribute('data-tippy-root', ''); 2418 popper.id = "tippy-" + instance.id; 2419 instance.popper = popper; 2420 reference._tippy = instance; 2421 popper._tippy = instance; 2422 var pluginsHooks = plugins.map(function (plugin) { 2423 return plugin.fn(instance); 2424 }); 2425 var hasAriaExpanded = reference.hasAttribute('aria-expanded'); 2426 addListeners(); 2427 handleAriaExpandedAttribute(); 2428 handleStyles(); 2429 invokeHook('onCreate', [instance]); 2430 2431 if (props.showOnCreate) { 2432 scheduleShow(); 2433 } // Prevent a tippy with a delay from hiding if the cursor left then returned 2434 // before it started hiding 2435 2436 2437 popper.addEventListener('mouseenter', function () { 2438 if (instance.props.interactive && instance.state.isVisible) { 2439 instance.clearDelayTimeouts(); 2440 } 2441 }); 2442 popper.addEventListener('mouseleave', function () { 2443 if (instance.props.interactive && instance.props.trigger.indexOf('mouseenter') >= 0) { 2444 getDocument().addEventListener('mousemove', debouncedOnMouseMove); 2445 } 2446 }); 2447 return instance; // =========================================================================== 2448 // 🔒 Private methods 2449 // =========================================================================== 2450 2451 function getNormalizedTouchSettings() { 2452 var touch = instance.props.touch; 2453 return Array.isArray(touch) ? touch : [touch, 0]; 2454 } 2455 2456 function getIsCustomTouchBehavior() { 2457 return getNormalizedTouchSettings()[0] === 'hold'; 2458 } 2459 2460 function getIsDefaultRenderFn() { 2461 var _instance$props$rende; // @ts-ignore 2462 2463 2464 return !!((_instance$props$rende = instance.props.render) != null && _instance$props$rende.$$tippy); 2465 } 2466 2467 function getCurrentTarget() { 2468 return currentTarget || reference; 2469 } 2470 2471 function getDocument() { 2472 var parent = getCurrentTarget().parentNode; 2473 return parent ? getOwnerDocument(parent) : document; 2474 } 2475 2476 function getDefaultTemplateChildren() { 2477 return getChildren(popper); 2478 } 2479 2480 function getDelay(isShow) { 2481 // For touch or keyboard input, force `0` delay for UX reasons 2482 // Also if the instance is mounted but not visible (transitioning out), 2483 // ignore delay 2484 if (instance.state.isMounted && !instance.state.isVisible || currentInput.isTouch || lastTriggerEvent && lastTriggerEvent.type === 'focus') { 2485 return 0; 2486 } 2487 2488 return getValueAtIndexOrReturn(instance.props.delay, isShow ? 0 : 1, defaultProps.delay); 2489 } 2490 2491 function handleStyles(fromHide) { 2492 if (fromHide === void 0) { 2493 fromHide = false; 2494 } 2495 2496 popper.style.pointerEvents = instance.props.interactive && !fromHide ? '' : 'none'; 2497 popper.style.zIndex = "" + instance.props.zIndex; 2498 } 2499 2500 function invokeHook(hook, args, shouldInvokePropsHook) { 2501 if (shouldInvokePropsHook === void 0) { 2502 shouldInvokePropsHook = true; 2503 } 2504 2505 pluginsHooks.forEach(function (pluginHooks) { 2506 if (pluginHooks[hook]) { 2507 pluginHooks[hook].apply(pluginHooks, args); 2508 } 2509 }); 2510 2511 if (shouldInvokePropsHook) { 2512 var _instance$props; 2513 2514 (_instance$props = instance.props)[hook].apply(_instance$props, args); 2515 } 2516 } 2517 2518 function handleAriaContentAttribute() { 2519 var aria = instance.props.aria; 2520 2521 if (!aria.content) { 2522 return; 2523 } 2524 2525 var attr = "aria-" + aria.content; 2526 var id = popper.id; 2527 var nodes = normalizeToArray(instance.props.triggerTarget || reference); 2528 nodes.forEach(function (node) { 2529 var currentValue = node.getAttribute(attr); 2530 2531 if (instance.state.isVisible) { 2532 node.setAttribute(attr, currentValue ? currentValue + " " + id : id); 2533 } else { 2534 var nextValue = currentValue && currentValue.replace(id, '').trim(); 2535 2536 if (nextValue) { 2537 node.setAttribute(attr, nextValue); 2538 } else { 2539 node.removeAttribute(attr); 2540 } 2541 } 2542 }); 2543 } 2544 2545 function handleAriaExpandedAttribute() { 2546 if (hasAriaExpanded || !instance.props.aria.expanded) { 2547 return; 2548 } 2549 2550 var nodes = normalizeToArray(instance.props.triggerTarget || reference); 2551 nodes.forEach(function (node) { 2552 if (instance.props.interactive) { 2553 node.setAttribute('aria-expanded', instance.state.isVisible && node === getCurrentTarget() ? 'true' : 'false'); 2554 } else { 2555 node.removeAttribute('aria-expanded'); 2556 } 2557 }); 2558 } 2559 2560 function cleanupInteractiveMouseListeners() { 2561 getDocument().removeEventListener('mousemove', debouncedOnMouseMove); 2562 mouseMoveListeners = mouseMoveListeners.filter(function (listener) { 2563 return listener !== debouncedOnMouseMove; 2564 }); 2565 } 2566 2567 function onDocumentPress(event) { 2568 // Moved finger to scroll instead of an intentional tap outside 2569 if (currentInput.isTouch) { 2570 if (didTouchMove || event.type === 'mousedown') { 2571 return; 2572 } 2573 } 2574 2575 var actualTarget = event.composedPath && event.composedPath()[0] || event.target; // Clicked on interactive popper 2576 2577 if (instance.props.interactive && actualContains(popper, actualTarget)) { 2578 return; 2579 } // Clicked on the event listeners target 2580 2581 2582 if (normalizeToArray(instance.props.triggerTarget || reference).some(function (el) { 2583 return actualContains(el, actualTarget); 2584 })) { 2585 if (currentInput.isTouch) { 2586 return; 2587 } 2588 2589 if (instance.state.isVisible && instance.props.trigger.indexOf('click') >= 0) { 2590 return; 2591 } 2592 } else { 2593 invokeHook('onClickOutside', [instance, event]); 2594 } 2595 2596 if (instance.props.hideOnClick === true) { 2597 instance.clearDelayTimeouts(); 2598 instance.hide(); // `mousedown` event is fired right before `focus` if pressing the 2599 // currentTarget. This lets a tippy with `focus` trigger know that it 2600 // should not show 2601 2602 didHideDueToDocumentMouseDown = true; 2603 setTimeout(function () { 2604 didHideDueToDocumentMouseDown = false; 2605 }); // The listener gets added in `scheduleShow()`, but this may be hiding it 2606 // before it shows, and hide()'s early bail-out behavior can prevent it 2607 // from being cleaned up 2608 2609 if (!instance.state.isMounted) { 2610 removeDocumentPress(); 2611 } 2612 } 2613 } 2614 2615 function onTouchMove() { 2616 didTouchMove = true; 2617 } 2618 2619 function onTouchStart() { 2620 didTouchMove = false; 2621 } 2622 2623 function addDocumentPress() { 2624 var doc = getDocument(); 2625 doc.addEventListener('mousedown', onDocumentPress, true); 2626 doc.addEventListener('touchend', onDocumentPress, TOUCH_OPTIONS); 2627 doc.addEventListener('touchstart', onTouchStart, TOUCH_OPTIONS); 2628 doc.addEventListener('touchmove', onTouchMove, TOUCH_OPTIONS); 2629 } 2630 2631 function removeDocumentPress() { 2632 var doc = getDocument(); 2633 doc.removeEventListener('mousedown', onDocumentPress, true); 2634 doc.removeEventListener('touchend', onDocumentPress, TOUCH_OPTIONS); 2635 doc.removeEventListener('touchstart', onTouchStart, TOUCH_OPTIONS); 2636 doc.removeEventListener('touchmove', onTouchMove, TOUCH_OPTIONS); 2637 } 2638 2639 function onTransitionedOut(duration, callback) { 2640 onTransitionEnd(duration, function () { 2641 if (!instance.state.isVisible && popper.parentNode && popper.parentNode.contains(popper)) { 2642 callback(); 2643 } 2644 }); 2645 } 2646 2647 function onTransitionedIn(duration, callback) { 2648 onTransitionEnd(duration, callback); 2649 } 2650 2651 function onTransitionEnd(duration, callback) { 2652 var box = getDefaultTemplateChildren().box; 2653 2654 function listener(event) { 2655 if (event.target === box) { 2656 updateTransitionEndListener(box, 'remove', listener); 2657 callback(); 2658 } 2659 } // Make callback synchronous if duration is 0 2660 // `transitionend` won't fire otherwise 2661 2662 2663 if (duration === 0) { 2664 return callback(); 2665 } 2666 2667 updateTransitionEndListener(box, 'remove', currentTransitionEndListener); 2668 updateTransitionEndListener(box, 'add', listener); 2669 currentTransitionEndListener = listener; 2670 } 2671 2672 function on(eventType, handler, options) { 2673 if (options === void 0) { 2674 options = false; 2675 } 2676 2677 var nodes = normalizeToArray(instance.props.triggerTarget || reference); 2678 nodes.forEach(function (node) { 2679 node.addEventListener(eventType, handler, options); 2680 listeners.push({ 2681 node: node, 2682 eventType: eventType, 2683 handler: handler, 2684 options: options 2685 }); 2686 }); 2687 } 2688 2689 function addListeners() { 2690 if (getIsCustomTouchBehavior()) { 2691 on('touchstart', onTrigger, { 2692 passive: true 2693 }); 2694 on('touchend', onMouseLeave, { 2695 passive: true 2696 }); 2697 } 2698 2699 splitBySpaces(instance.props.trigger).forEach(function (eventType) { 2700 if (eventType === 'manual') { 2701 return; 2702 } 2703 2704 on(eventType, onTrigger); 2705 2706 switch (eventType) { 2707 case 'mouseenter': 2708 on('mouseleave', onMouseLeave); 2709 break; 2710 2711 case 'focus': 2712 on(isIE11 ? 'focusout' : 'blur', onBlurOrFocusOut); 2713 break; 2714 2715 case 'focusin': 2716 on('focusout', onBlurOrFocusOut); 2717 break; 2718 } 2719 }); 2720 } 2721 2722 function removeListeners() { 2723 listeners.forEach(function (_ref) { 2724 var node = _ref.node, 2725 eventType = _ref.eventType, 2726 handler = _ref.handler, 2727 options = _ref.options; 2728 node.removeEventListener(eventType, handler, options); 2729 }); 2730 listeners = []; 2731 } 2732 2733 function onTrigger(event) { 2734 var _lastTriggerEvent; 2735 2736 var shouldScheduleClickHide = false; 2737 2738 if (!instance.state.isEnabled || isEventListenerStopped(event) || didHideDueToDocumentMouseDown) { 2739 return; 2740 } 2741 2742 var wasFocused = ((_lastTriggerEvent = lastTriggerEvent) == null ? void 0 : _lastTriggerEvent.type) === 'focus'; 2743 lastTriggerEvent = event; 2744 currentTarget = event.currentTarget; 2745 handleAriaExpandedAttribute(); 2746 2747 if (!instance.state.isVisible && isMouseEvent(event)) { 2748 // If scrolling, `mouseenter` events can be fired if the cursor lands 2749 // over a new target, but `mousemove` events don't get fired. This 2750 // causes interactive tooltips to get stuck open until the cursor is 2751 // moved 2752 mouseMoveListeners.forEach(function (listener) { 2753 return listener(event); 2754 }); 2755 } // Toggle show/hide when clicking click-triggered tooltips 2756 2757 2758 if (event.type === 'click' && (instance.props.trigger.indexOf('mouseenter') < 0 || isVisibleFromClick) && instance.props.hideOnClick !== false && instance.state.isVisible) { 2759 shouldScheduleClickHide = true; 2760 } else { 2761 scheduleShow(event); 2762 } 2763 2764 if (event.type === 'click') { 2765 isVisibleFromClick = !shouldScheduleClickHide; 2766 } 2767 2768 if (shouldScheduleClickHide && !wasFocused) { 2769 scheduleHide(event); 2770 } 2771 } 2772 2773 function onMouseMove(event) { 2774 var target = event.target; 2775 var isCursorOverReferenceOrPopper = getCurrentTarget().contains(target) || popper.contains(target); 2776 2777 if (event.type === 'mousemove' && isCursorOverReferenceOrPopper) { 2778 return; 2779 } 2780 2781 var popperTreeData = getNestedPopperTree().concat(popper).map(function (popper) { 2782 var _instance$popperInsta; 2783 2784 var instance = popper._tippy; 2785 var state = (_instance$popperInsta = instance.popperInstance) == null ? void 0 : _instance$popperInsta.state; 2786 2787 if (state) { 2788 return { 2789 popperRect: popper.getBoundingClientRect(), 2790 popperState: state, 2791 props: props 2792 }; 2793 } 2794 2795 return null; 2796 }).filter(Boolean); 2797 2798 if (isCursorOutsideInteractiveBorder(popperTreeData, event)) { 2799 cleanupInteractiveMouseListeners(); 2800 scheduleHide(event); 2801 } 2802 } 2803 2804 function onMouseLeave(event) { 2805 var shouldBail = isEventListenerStopped(event) || instance.props.trigger.indexOf('click') >= 0 && isVisibleFromClick; 2806 2807 if (shouldBail) { 2808 return; 2809 } 2810 2811 if (instance.props.interactive) { 2812 instance.hideWithInteractivity(event); 2813 return; 2814 } 2815 2816 scheduleHide(event); 2817 } 2818 2819 function onBlurOrFocusOut(event) { 2820 if (instance.props.trigger.indexOf('focusin') < 0 && event.target !== getCurrentTarget()) { 2821 return; 2822 } // If focus was moved to within the popper 2823 2824 2825 if (instance.props.interactive && event.relatedTarget && popper.contains(event.relatedTarget)) { 2826 return; 2827 } 2828 2829 scheduleHide(event); 2830 } 2831 2832 function isEventListenerStopped(event) { 2833 return currentInput.isTouch ? getIsCustomTouchBehavior() !== event.type.indexOf('touch') >= 0 : false; 2834 } 2835 2836 function createPopperInstance() { 2837 destroyPopperInstance(); 2838 var _instance$props2 = instance.props, 2839 popperOptions = _instance$props2.popperOptions, 2840 placement = _instance$props2.placement, 2841 offset = _instance$props2.offset, 2842 getReferenceClientRect = _instance$props2.getReferenceClientRect, 2843 moveTransition = _instance$props2.moveTransition; 2844 var arrow = getIsDefaultRenderFn() ? getChildren(popper).arrow : null; 2845 var computedReference = getReferenceClientRect ? { 2846 getBoundingClientRect: getReferenceClientRect, 2847 contextElement: getReferenceClientRect.contextElement || getCurrentTarget() 2848 } : reference; 2849 var tippyModifier = { 2850 name: '$$tippy', 2851 enabled: true, 2852 phase: 'beforeWrite', 2853 requires: ['computeStyles'], 2854 fn: function fn(_ref2) { 2855 var state = _ref2.state; 2856 2857 if (getIsDefaultRenderFn()) { 2858 var _getDefaultTemplateCh = getDefaultTemplateChildren(), 2859 box = _getDefaultTemplateCh.box; 2860 2861 ['placement', 'reference-hidden', 'escaped'].forEach(function (attr) { 2862 if (attr === 'placement') { 2863 box.setAttribute('data-placement', state.placement); 2864 } else { 2865 if (state.attributes.popper["data-popper-" + attr]) { 2866 box.setAttribute("data-" + attr, ''); 2867 } else { 2868 box.removeAttribute("data-" + attr); 2869 } 2870 } 2871 }); 2872 state.attributes.popper = {}; 2873 } 2874 } 2875 }; 2876 var modifiers = [{ 2877 name: 'offset', 2878 options: { 2879 offset: offset 2880 } 2881 }, { 2882 name: 'preventOverflow', 2883 options: { 2884 padding: { 2885 top: 2, 2886 bottom: 2, 2887 left: 5, 2888 right: 5 2889 } 2890 } 2891 }, { 2892 name: 'flip', 2893 options: { 2894 padding: 5 2895 } 2896 }, { 2897 name: 'computeStyles', 2898 options: { 2899 adaptive: !moveTransition 2900 } 2901 }, tippyModifier]; 2902 2903 if (getIsDefaultRenderFn() && arrow) { 2904 modifiers.push({ 2905 name: 'arrow', 2906 options: { 2907 element: arrow, 2908 padding: 3 2909 } 2910 }); 2911 } 2912 2913 modifiers.push.apply(modifiers, (popperOptions == null ? void 0 : popperOptions.modifiers) || []); 2914 instance.popperInstance = createPopper(computedReference, popper, Object.assign({}, popperOptions, { 2915 placement: placement, 2916 onFirstUpdate: onFirstUpdate, 2917 modifiers: modifiers 2918 })); 2919 } 2920 2921 function destroyPopperInstance() { 2922 if (instance.popperInstance) { 2923 instance.popperInstance.destroy(); 2924 instance.popperInstance = null; 2925 } 2926 } 2927 2928 function mount() { 2929 var appendTo = instance.props.appendTo; 2930 var parentNode; // By default, we'll append the popper to the triggerTargets's parentNode so 2931 // it's directly after the reference element so the elements inside the 2932 // tippy can be tabbed to 2933 // If there are clipping issues, the user can specify a different appendTo 2934 // and ensure focus management is handled correctly manually 2935 2936 var node = getCurrentTarget(); 2937 2938 if (instance.props.interactive && appendTo === TIPPY_DEFAULT_APPEND_TO || appendTo === 'parent') { 2939 parentNode = node.parentNode; 2940 } else { 2941 parentNode = invokeWithArgsOrReturn(appendTo, [node]); 2942 } // The popper element needs to exist on the DOM before its position can be 2943 // updated as Popper needs to read its dimensions 2944 2945 2946 if (!parentNode.contains(popper)) { 2947 parentNode.appendChild(popper); 2948 } 2949 2950 instance.state.isMounted = true; 2951 createPopperInstance(); 2952 } 2953 2954 function getNestedPopperTree() { 2955 return arrayFrom(popper.querySelectorAll('[data-tippy-root]')); 2956 } 2957 2958 function scheduleShow(event) { 2959 instance.clearDelayTimeouts(); 2960 2961 if (event) { 2962 invokeHook('onTrigger', [instance, event]); 2963 } 2964 2965 addDocumentPress(); 2966 var delay = getDelay(true); 2967 2968 var _getNormalizedTouchSe = getNormalizedTouchSettings(), 2969 touchValue = _getNormalizedTouchSe[0], 2970 touchDelay = _getNormalizedTouchSe[1]; 2971 2972 if (currentInput.isTouch && touchValue === 'hold' && touchDelay) { 2973 delay = touchDelay; 2974 } 2975 2976 if (delay) { 2977 showTimeout = setTimeout(function () { 2978 instance.show(); 2979 }, delay); 2980 } else { 2981 instance.show(); 2982 } 2983 } 2984 2985 function scheduleHide(event) { 2986 instance.clearDelayTimeouts(); 2987 invokeHook('onUntrigger', [instance, event]); 2988 2989 if (!instance.state.isVisible) { 2990 removeDocumentPress(); 2991 return; 2992 } // For interactive tippies, scheduleHide is added to a document.body handler 2993 // from onMouseLeave so must intercept scheduled hides from mousemove/leave 2994 // events when trigger contains mouseenter and click, and the tip is 2995 // currently shown as a result of a click. 2996 2997 2998 if (instance.props.trigger.indexOf('mouseenter') >= 0 && instance.props.trigger.indexOf('click') >= 0 && ['mouseleave', 'mousemove'].indexOf(event.type) >= 0 && isVisibleFromClick) { 2999 return; 3000 } 3001 3002 var delay = getDelay(false); 3003 3004 if (delay) { 3005 hideTimeout = setTimeout(function () { 3006 if (instance.state.isVisible) { 3007 instance.hide(); 3008 } 3009 }, delay); 3010 } else { 3011 // Fixes a `transitionend` problem when it fires 1 frame too 3012 // late sometimes, we don't want hide() to be called. 3013 scheduleHideAnimationFrame = requestAnimationFrame(function () { 3014 instance.hide(); 3015 }); 3016 } 3017 } // =========================================================================== 3018 // 🔑 Public methods 3019 // =========================================================================== 3020 3021 3022 function enable() { 3023 instance.state.isEnabled = true; 3024 } 3025 3026 function disable() { 3027 // Disabling the instance should also hide it 3028 // https://github.com/atomiks/tippy.js-react/issues/106 3029 instance.hide(); 3030 instance.state.isEnabled = false; 3031 } 3032 3033 function clearDelayTimeouts() { 3034 clearTimeout(showTimeout); 3035 clearTimeout(hideTimeout); 3036 cancelAnimationFrame(scheduleHideAnimationFrame); 3037 } 3038 3039 function setProps(partialProps) { 3040 if (instance.state.isDestroyed) { 3041 return; 3042 } 3043 3044 invokeHook('onBeforeUpdate', [instance, partialProps]); 3045 removeListeners(); 3046 var prevProps = instance.props; 3047 var nextProps = evaluateProps(reference, Object.assign({}, prevProps, removeUndefinedProps(partialProps), { 3048 ignoreAttributes: true 3049 })); 3050 instance.props = nextProps; 3051 addListeners(); 3052 3053 if (prevProps.interactiveDebounce !== nextProps.interactiveDebounce) { 3054 cleanupInteractiveMouseListeners(); 3055 debouncedOnMouseMove = debounce(onMouseMove, nextProps.interactiveDebounce); 3056 } // Ensure stale aria-expanded attributes are removed 3057 3058 3059 if (prevProps.triggerTarget && !nextProps.triggerTarget) { 3060 normalizeToArray(prevProps.triggerTarget).forEach(function (node) { 3061 node.removeAttribute('aria-expanded'); 3062 }); 3063 } else if (nextProps.triggerTarget) { 3064 reference.removeAttribute('aria-expanded'); 3065 } 3066 3067 handleAriaExpandedAttribute(); 3068 handleStyles(); 3069 3070 if (onUpdate) { 3071 onUpdate(prevProps, nextProps); 3072 } 3073 3074 if (instance.popperInstance) { 3075 createPopperInstance(); // Fixes an issue with nested tippies if they are all getting re-rendered, 3076 // and the nested ones get re-rendered first. 3077 // https://github.com/atomiks/tippyjs-react/issues/177 3078 // TODO: find a cleaner / more efficient solution(!) 3079 3080 getNestedPopperTree().forEach(function (nestedPopper) { 3081 // React (and other UI libs likely) requires a rAF wrapper as it flushes 3082 // its work in one 3083 requestAnimationFrame(nestedPopper._tippy.popperInstance.forceUpdate); 3084 }); 3085 } 3086 3087 invokeHook('onAfterUpdate', [instance, partialProps]); 3088 } 3089 3090 function setContent(content) { 3091 instance.setProps({ 3092 content: content 3093 }); 3094 } 3095 3096 function show() { 3097 var isAlreadyVisible = instance.state.isVisible; 3098 var isDestroyed = instance.state.isDestroyed; 3099 var isDisabled = !instance.state.isEnabled; 3100 var isTouchAndTouchDisabled = currentInput.isTouch && !instance.props.touch; 3101 var duration = getValueAtIndexOrReturn(instance.props.duration, 0, defaultProps.duration); 3102 3103 if (isAlreadyVisible || isDestroyed || isDisabled || isTouchAndTouchDisabled) { 3104 return; 3105 } // Normalize `disabled` behavior across browsers. 3106 // Firefox allows events on disabled elements, but Chrome doesn't. 3107 // Using a wrapper element (i.e. <span>) is recommended. 3108 3109 3110 if (getCurrentTarget().hasAttribute('disabled')) { 3111 return; 3112 } 3113 3114 invokeHook('onShow', [instance], false); 3115 3116 if (instance.props.onShow(instance) === false) { 3117 return; 3118 } 3119 3120 instance.state.isVisible = true; 3121 3122 if (getIsDefaultRenderFn()) { 3123 popper.style.visibility = 'visible'; 3124 } 3125 3126 handleStyles(); 3127 addDocumentPress(); 3128 3129 if (!instance.state.isMounted) { 3130 popper.style.transition = 'none'; 3131 } // If flipping to the opposite side after hiding at least once, the 3132 // animation will use the wrong placement without resetting the duration 3133 3134 3135 if (getIsDefaultRenderFn()) { 3136 var _getDefaultTemplateCh2 = getDefaultTemplateChildren(), 3137 box = _getDefaultTemplateCh2.box, 3138 content = _getDefaultTemplateCh2.content; 3139 3140 setTransitionDuration([box, content], 0); 3141 } 3142 3143 onFirstUpdate = function onFirstUpdate() { 3144 var _instance$popperInsta2; 3145 3146 if (!instance.state.isVisible || ignoreOnFirstUpdate) { 3147 return; 3148 } 3149 3150 ignoreOnFirstUpdate = true; // reflow 3151 3152 void popper.offsetHeight; 3153 popper.style.transition = instance.props.moveTransition; 3154 3155 if (getIsDefaultRenderFn() && instance.props.animation) { 3156 var _getDefaultTemplateCh3 = getDefaultTemplateChildren(), 3157 _box = _getDefaultTemplateCh3.box, 3158 _content = _getDefaultTemplateCh3.content; 3159 3160 setTransitionDuration([_box, _content], duration); 3161 setVisibilityState([_box, _content], 'visible'); 3162 } 3163 3164 handleAriaContentAttribute(); 3165 handleAriaExpandedAttribute(); 3166 pushIfUnique(mountedInstances, instance); // certain modifiers (e.g. `maxSize`) require a second update after the 3167 // popper has been positioned for the first time 3168 3169 (_instance$popperInsta2 = instance.popperInstance) == null ? void 0 : _instance$popperInsta2.forceUpdate(); 3170 invokeHook('onMount', [instance]); 3171 3172 if (instance.props.animation && getIsDefaultRenderFn()) { 3173 onTransitionedIn(duration, function () { 3174 instance.state.isShown = true; 3175 invokeHook('onShown', [instance]); 3176 }); 3177 } 3178 }; 3179 3180 mount(); 3181 } 3182 3183 function hide() { 3184 var isAlreadyHidden = !instance.state.isVisible; 3185 var isDestroyed = instance.state.isDestroyed; 3186 var isDisabled = !instance.state.isEnabled; 3187 var duration = getValueAtIndexOrReturn(instance.props.duration, 1, defaultProps.duration); 3188 3189 if (isAlreadyHidden || isDestroyed || isDisabled) { 3190 return; 3191 } 3192 3193 invokeHook('onHide', [instance], false); 3194 3195 if (instance.props.onHide(instance) === false) { 3196 return; 3197 } 3198 3199 instance.state.isVisible = false; 3200 instance.state.isShown = false; 3201 ignoreOnFirstUpdate = false; 3202 isVisibleFromClick = false; 3203 3204 if (getIsDefaultRenderFn()) { 3205 popper.style.visibility = 'hidden'; 3206 } 3207 3208 cleanupInteractiveMouseListeners(); 3209 removeDocumentPress(); 3210 handleStyles(true); 3211 3212 if (getIsDefaultRenderFn()) { 3213 var _getDefaultTemplateCh4 = getDefaultTemplateChildren(), 3214 box = _getDefaultTemplateCh4.box, 3215 content = _getDefaultTemplateCh4.content; 3216 3217 if (instance.props.animation) { 3218 setTransitionDuration([box, content], duration); 3219 setVisibilityState([box, content], 'hidden'); 3220 } 3221 } 3222 3223 handleAriaContentAttribute(); 3224 handleAriaExpandedAttribute(); 3225 3226 if (instance.props.animation) { 3227 if (getIsDefaultRenderFn()) { 3228 onTransitionedOut(duration, instance.unmount); 3229 } 3230 } else { 3231 instance.unmount(); 3232 } 3233 } 3234 3235 function hideWithInteractivity(event) { 3236 getDocument().addEventListener('mousemove', debouncedOnMouseMove); 3237 pushIfUnique(mouseMoveListeners, debouncedOnMouseMove); 3238 debouncedOnMouseMove(event); 3239 } 3240 3241 function unmount() { 3242 if (instance.state.isVisible) { 3243 instance.hide(); 3244 } 3245 3246 if (!instance.state.isMounted) { 3247 return; 3248 } 3249 3250 destroyPopperInstance(); // If a popper is not interactive, it will be appended outside the popper 3251 // tree by default. This seems mainly for interactive tippies, but we should 3252 // find a workaround if possible 3253 3254 getNestedPopperTree().forEach(function (nestedPopper) { 3255 nestedPopper._tippy.unmount(); 3256 }); 3257 3258 if (popper.parentNode) { 3259 popper.parentNode.removeChild(popper); 3260 } 3261 3262 mountedInstances = mountedInstances.filter(function (i) { 3263 return i !== instance; 3264 }); 3265 instance.state.isMounted = false; 3266 invokeHook('onHidden', [instance]); 3267 } 3268 3269 function destroy() { 3270 if (instance.state.isDestroyed) { 3271 return; 3272 } 3273 3274 instance.clearDelayTimeouts(); 3275 instance.unmount(); 3276 removeListeners(); 3277 delete reference._tippy; 3278 instance.state.isDestroyed = true; 3279 invokeHook('onDestroy', [instance]); 3280 } 3281 } 3282 3283 function tippy(targets, optionalProps) { 3284 if (optionalProps === void 0) { 3285 optionalProps = {}; 3286 } 3287 3288 var plugins = defaultProps.plugins.concat(optionalProps.plugins || []); 3289 bindGlobalEventListeners(); 3290 var passedProps = Object.assign({}, optionalProps, { 3291 plugins: plugins 3292 }); 3293 var elements = getArrayOfElements(targets); 3294 var instances = elements.reduce(function (acc, reference) { 3295 var instance = reference && createTippy(reference, passedProps); 3296 3297 if (instance) { 3298 acc.push(instance); 3299 } 3300 3301 return acc; 3302 }, []); 3303 return isElement(targets) ? instances[0] : instances; 3304 } 3305 3306 tippy.defaultProps = defaultProps; 3307 tippy.setDefaultProps = setDefaultProps; 3308 tippy.currentInput = currentInput; // every time the popper is destroyed (i.e. a new target), removing the styles 3309 // and causing transitions to break for singletons when the console is open, but 3310 // most notably for non-transform styles being used, `gpuAcceleration: false`. 3311 3312 Object.assign({}, applyStyles$1, { 3313 effect: function effect(_ref) { 3314 var state = _ref.state; 3315 var initialStyles = { 3316 popper: { 3317 position: state.options.strategy, 3318 left: '0', 3319 top: '0', 3320 margin: '0' 3321 }, 3322 arrow: { 3323 position: 'absolute' 3324 }, 3325 reference: {} 3326 }; 3327 Object.assign(state.elements.popper.style, initialStyles.popper); 3328 state.styles = initialStyles; 3329 3330 if (state.elements.arrow) { 3331 Object.assign(state.elements.arrow.style, initialStyles.arrow); 3332 } // intentionally return no cleanup function 3333 // return () => { ... } 3334 3335 } 3336 }); 3337 tippy.setDefaultProps({ 3338 render: render 3339 }); // import 'tippy.js/dist/tippy.css'; 3340 3341 /** 3342 * Utility methods 3343 */ 3344 // Determine element visibility 3345 3346 var isElementHidden = function isElementHidden($el) { 3347 if ($el.getAttribute('hidden') || $el.offsetWidth === 0 && $el.offsetHeight === 0) { 3348 return true; 3349 } else { 3350 var compStyles = getComputedStyle($el); 3351 return compStyles.getPropertyValue('display') === 'none'; 3352 } 3353 }; // Escape HTML, encode HTML symbols 3354 3355 3356 var escapeHTML = function escapeHTML(text) { 3357 var $div = document.createElement('div'); 3358 $div.textContent = text; 3359 return $div.innerHTML.replaceAll('"', '"').replaceAll("'", ''').replaceAll("`", '`'); 3360 }; 3361 /** 3362 * Jooa11y Translation object 3363 */ 3364 3365 3366 var Lang = { 3367 langStrings: {}, 3368 addI18n: function addI18n(strings) { 3369 this.langStrings = strings; 3370 }, 3371 _: function _(string) { 3372 return this.translate(string); 3373 }, 3374 sprintf: function sprintf(string) { 3375 var transString = this._(string); 3376 3377 for (var _len2 = arguments.length, args = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) { 3378 args[_key2 - 1] = arguments[_key2]; 3379 } 3380 3381 if (args && args.length) { 3382 args.forEach(function (arg) { 3383 transString = transString.replace(/%\([a-zA-z]+\)/, arg); 3384 }); 3385 } 3386 3387 return transString; 3388 }, 3389 translate: function translate(string) { 3390 return this.langStrings[string] || string; 3391 } 3392 }; 3393 /** 3394 * Jooa11y default options 3395 */ 3396 3397 var defaultOptions = { 3398 langCode: 'en', 3399 // Target area to scan. 3400 checkRoot: 'main', 3401 // A content container 3402 // Readability configuration. 3403 readabilityRoot: 'main', 3404 readabilityLang: 'en', 3405 // Inclusions and exclusions. Use commas to seperate classes or elements. 3406 containerIgnore: '.jooa11y-ignore', 3407 // Ignore specific regions. 3408 outlineIgnore: '', 3409 // Exclude headings from outline panel. 3410 headerIgnore: '', 3411 // Ignore specific headings. E.g. "h1.jumbotron-heading" 3412 imageIgnore: '', 3413 // Ignore specific images. 3414 linkIgnore: '', 3415 // Ignore specific links. 3416 linkIgnoreSpan: 'noscript, span.sr-only-example', 3417 // Ignore specific classes within links. Example: <a href="#">learn more <span class="sr-only-example">(opens new tab)</span></a>. 3418 linksToFlag: '', 3419 // Links you don't want your content editors pointing to (e.g. development environments). 3420 // Embedded content. 3421 videoContent: "video, [src*='youtube.com'], [src*='vimeo.com'], [src*='yuja.com'], [src*='panopto.com']", 3422 audioContent: "audio, [src*='soundcloud.com'], [src*='simplecast.com'], [src*='podbean.com'], [src*='buzzsprout.com'], [src*='blubrry.com'], [src*='transistor.fm'], [src*='fusebox.fm'], [src*='libsyn.com']", 3423 embeddedContent: '', 3424 // Alt Text stop words. 3425 suspiciousAltWords: ['image', 'graphic', 'picture', 'photo'], 3426 placeholderAltStopWords: ['alt', 'image', 'photo', 'decorative', 'photo', 'placeholder', 'placeholder image', 'spacer', '.'], 3427 // Link Text stop words 3428 partialAltStopWords: ['click', 'click here', 'click here for more', 'click here to learn more', 'click here to learn more.', 'check out', 'download', 'download here', 'download here.', 'find out', 'find out more', 'find out more.', 'form', 'here', 'here.', 'info', 'information', 'link', 'learn', 'learn more', 'learn more.', 'learn to', 'more', 'page', 'paper', 'read more', 'read', 'read this', 'this', 'this page', 'this page.', 'this website', 'this website.', 'view', 'view our', 'website', '.'], 3429 warningAltWords: ['< ', ' >', 'click here'], 3430 // Link Text (Advanced) 3431 newWindowPhrases: ['external', 'new tab', 'new window', 'pop-up', 'pop up'], 3432 // Link Text (Advanced). Only some items in list would need to be translated. 3433 fileTypePhrases: ['document', 'pdf', 'doc', 'docx', 'word', 'mp3', 'ppt', 'text', 'pptx', 'powerpoint', 'txt', 'exe', 'dmg', 'rtf', 'install', 'windows', 'macos', 'spreadsheet', 'worksheet', 'csv', 'xls', 'xlsx', 'video', 'mp4', 'mov', 'avi'] 3434 }; 3435 defaultOptions.embeddedContent = defaultOptions.videoContent + ", " + defaultOptions.audioContent; 3436 /** 3437 * Load and validate options 3438 * 3439 * @param {Jooa11y} instance 3440 * @param {Object} customOptions 3441 * @returns {Object} 3442 */ 3443 3444 var loadOptions = function loadOptions(instance, customOptions) { 3445 var options = customOptions ? Object.assign(defaultOptions, customOptions) : defaultOptions; // Check required options 3446 3447 ['langCode', 'checkRoot'].forEach(function (option) { 3448 if (!options[option]) { 3449 throw new Error("Option [" + option + "] is required"); 3450 } 3451 }); 3452 3453 if (!options.readabilityRoot) { 3454 options.readabilityRoot = options.checkRoot; 3455 } // Container ignores apply to self and children. 3456 3457 3458 if (options.containerIgnore) { 3459 var containerSelectors = options.containerIgnore.split(',').map(function (el) { 3460 return el + " *, " + el; 3461 }); 3462 options.containerIgnore = '[aria-hidden="true"], #jooa11y-container *, .jooa11y-instance *, ' + containerSelectors.join(', '); 3463 } else { 3464 options.containerIgnore = '[aria-hidden="true"], #jooa11y-container *, .jooa11y-instance *'; 3465 } 3466 3467 instance.containerIgnore = options.containerIgnore; // Images ignore 3468 3469 instance.imageIgnore = instance.containerIgnore + ', [role="presentation"], [src^="https://trck.youvisit.com"]'; 3470 3471 if (options.imageIgnore) { 3472 instance.imageIgnore = options.imageIgnore + ',' + instance.imageIgnore; 3473 } // Ignore specific headings 3474 3475 3476 instance.headerIgnore = options.containerIgnore; 3477 3478 if (options.headerIgnore) { 3479 instance.headerIgnore = options.headerIgnore + ',' + instance.headerIgnore; 3480 } // Links ignore defaults plus jooa11y links. 3481 3482 3483 instance.linkIgnore = instance.containerIgnore + ', [aria-hidden="true"], .anchorjs-link'; 3484 3485 if (options.linkIgnore) { 3486 instance.linkIgnore = options.linkIgnore + ',' + instance.linkIgnore; 3487 } 3488 3489 return options; 3490 }; 3491 /** 3492 * Jooa11y class 3493 */ 3494 3495 3496 var Jooa11y = /*#__PURE__*/function () { 3497 function Jooa11y(options) { 3498 var _this = this; 3499 3500 this.checkAll = /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee() { 3501 return regeneratorRuntime.wrap(function _callee$(_context) { 3502 while (1) { 3503 switch (_context.prev = _context.next) { 3504 case 0: 3505 _this.errorCount = 0; 3506 _this.warningCount = 0; 3507 _this.$root = document.querySelector(_this.options.checkRoot); 3508 3509 _this.findElements(); //Ruleset checks 3510 3511 3512 _this.checkHeaders(); 3513 3514 _this.checkLinkText(); 3515 3516 _this.checkUnderline(); 3517 3518 _this.checkAltText(); 3519 3520 if (localStorage.getItem("jooa11y-remember-contrast") === "On") { 3521 _this.checkContrast(); 3522 } 3523 3524 if (localStorage.getItem("jooa11y-remember-labels") === "On") { 3525 _this.checkLabels(); 3526 } 3527 3528 if (localStorage.getItem("jooa11y-remember-links-advanced") === "On") { 3529 _this.checkLinksAdvanced(); 3530 } 3531 3532 if (localStorage.getItem("jooa11y-remember-readability") === "On") { 3533 _this.checkReadability(); 3534 } 3535 3536 _this.checkEmbeddedContent(); 3537 3538 _this.checkQA(); //Update panel 3539 3540 3541 if (_this.panelActive) { 3542 _this.resetAll(); 3543 } else { 3544 _this.updatePanel(); 3545 } 3546 3547 _this.initializeTooltips(); 3548 3549 _this.detectOverflow(); 3550 3551 _this.nudge(); //Don't show badge when panel is opened. 3552 3553 3554 if (!document.getElementsByClassName('jooa11y-on').length) { 3555 _this.updateBadge(); 3556 } 3557 3558 case 19: 3559 case "end": 3560 return _context.stop(); 3561 } 3562 } 3563 }, _callee); 3564 })); 3565 3566 this.nudge = function () { 3567 var jooa11yInstance = document.querySelectorAll('.jooa11y-instance, .jooa11y-instance-inline'); 3568 jooa11yInstance.forEach(function ($el) { 3569 var sibling = $el.nextElementSibling; 3570 3571 if (sibling !== null && (sibling.classList.contains("jooa11y-instance") || sibling.classList.contains("jooa11y-instance-inline"))) { 3572 sibling.querySelector("button").setAttribute("style", "margin: -10px -20px !important;"); 3573 } 3574 }); 3575 }; 3576 3577 this.buildPanel = function () { 3578 var $outlineToggle = document.getElementById("jooa11y-outline-toggle"); 3579 var $outlinePanel = document.getElementById("jooa11y-outline-panel"); 3580 var $outlineList = document.getElementById("jooa11y-outline-list"); 3581 var $settingsToggle = document.getElementById("jooa11y-settings-toggle"); 3582 var $settingsPanel = document.getElementById("jooa11y-settings-panel"); 3583 var $settingsContent = document.getElementById("jooa11y-settings-content"); 3584 var $headingAnnotations = document.querySelectorAll(".jooa11y-heading-label"); //Show outline panel 3585 3586 $outlineToggle.addEventListener('click', function () { 3587 if ($outlineToggle.getAttribute("aria-expanded") === "true") { 3588 $outlineToggle.classList.remove("jooa11y-outline-active"); 3589 $outlinePanel.classList.remove("jooa11y-active"); 3590 $outlineToggle.textContent = Lang._('SHOW_OUTLINE'); 3591 $outlineToggle.setAttribute("aria-expanded", "false"); 3592 localStorage.setItem("jooa11y-remember-outline", "Closed"); 3593 } else { 3594 $outlineToggle.classList.add("jooa11y-outline-active"); 3595 $outlinePanel.classList.add("jooa11y-active"); 3596 $outlineToggle.textContent = Lang._('HIDE_OUTLINE'); 3597 $outlineToggle.setAttribute("aria-expanded", "true"); 3598 localStorage.setItem("jooa11y-remember-outline", "Opened"); 3599 } //Set focus on Page Outline heading for accessibility. 3600 3601 3602 document.querySelector("#jooa11y-outline-header > h2").focus(); //Show heading level annotations. 3603 3604 $headingAnnotations.forEach(function ($el) { 3605 return $el.classList.toggle("jooa11y-label-visible"); 3606 }); //Close Settings panel when Show Outline is active. 3607 3608 $settingsPanel.classList.remove("jooa11y-active"); 3609 $settingsToggle.classList.remove("jooa11y-settings-active"); 3610 $settingsToggle.setAttribute("aria-expanded", "false"); 3611 $settingsToggle.textContent = Lang._('SHOW_SETTINGS'); //Keyboard accessibility fix for scrollable panel content. 3612 3613 if ($outlineList.clientHeight > 250) { 3614 $outlineList.setAttribute("tabindex", "0"); 3615 } 3616 }); //Remember to leave outline open 3617 3618 if (localStorage.getItem("jooa11y-remember-outline") === "Opened") { 3619 $outlineToggle.classList.add("jooa11y-outline-active"); 3620 $outlinePanel.classList.add("jooa11y-active"); 3621 $outlineToggle.textContent = Lang._('HIDE_OUTLINE'); 3622 $outlineToggle.setAttribute("aria-expanded", "true"); 3623 $headingAnnotations.forEach(function ($el) { 3624 return $el.classList.toggle("jooa11y-label-visible"); 3625 }); //Keyboard accessibility fix for scrollable panel content. 3626 3627 if ($outlineList.clientHeight > 250) { 3628 $outlineList.setAttribute("tabindex", "0"); 3629 } 3630 } //Show settings panel 3631 3632 3633 $settingsToggle.addEventListener('click', function () { 3634 if ($settingsToggle.getAttribute("aria-expanded") === "true") { 3635 $settingsToggle.classList.remove("jooa11y-settings-active"); 3636 $settingsPanel.classList.remove("jooa11y-active"); 3637 $settingsToggle.textContent = Lang._('SHOW_SETTINGS'); 3638 $settingsToggle.setAttribute("aria-expanded", "false"); 3639 } else { 3640 $settingsToggle.classList.add("jooa11y-settings-active"); 3641 $settingsPanel.classList.add("jooa11y-active"); 3642 $settingsToggle.textContent = Lang._('HIDE_SETTINGS'); 3643 $settingsToggle.setAttribute("aria-expanded", "true"); 3644 } //Set focus on Settings heading for accessibility. 3645 3646 3647 document.querySelector("#jooa11y-settings-header > h2").focus(); //Close Show Outline panel when Settings is active. 3648 3649 $outlinePanel.classList.remove("jooa11y-active"); 3650 $outlineToggle.classList.remove("jooa11y-outline-active"); 3651 $outlineToggle.setAttribute("aria-expanded", "false"); 3652 $outlineToggle.textContent = Lang._('SHOW_OUTLINE'); 3653 $headingAnnotations.forEach(function ($el) { 3654 return $el.classList.remove("jooa11y-label-visible"); 3655 }); 3656 localStorage.setItem("jooa11y-remember-outline", "Closed"); //Keyboard accessibility fix for scrollable panel content. 3657 3658 if ($settingsContent.clientHeight > 350) { 3659 $settingsContent.setAttribute("tabindex", "0"); 3660 } 3661 }); //Enhanced keyboard accessibility for panel. 3662 3663 document.getElementById('jooa11y-panel-controls').addEventListener('keydown', function (e) { 3664 var $tab = document.querySelectorAll('#jooa11y-outline-toggle[role=tab], #jooa11y-settings-toggle[role=tab]'); 3665 3666 if (e.key === 'ArrowRight') { 3667 for (var i = 0; i < $tab.length; i++) { 3668 if ($tab[i].getAttribute('aria-expanded') === "true" || $tab[i].getAttribute('aria-expanded') === "false") { 3669 $tab[i + 1].focus(); 3670 e.preventDefault(); 3671 break; 3672 } 3673 } 3674 } 3675 3676 if (e.key === 'ArrowDown') { 3677 for (var _i2 = 0; _i2 < $tab.length; _i2++) { 3678 if ($tab[_i2].getAttribute('aria-expanded') === "true" || $tab[_i2].getAttribute('aria-expanded') === "false") { 3679 $tab[_i2 + 1].focus(); 3680 3681 e.preventDefault(); 3682 break; 3683 } 3684 } 3685 } 3686 3687 if (e.key === 'ArrowLeft') { 3688 for (var _i3 = $tab.length - 1; _i3 > 0; _i3--) { 3689 if ($tab[_i3].getAttribute('aria-expanded') === "true" || $tab[_i3].getAttribute('aria-expanded') === "false") { 3690 $tab[_i3 - 1].focus(); 3691 3692 e.preventDefault(); 3693 break; 3694 } 3695 } 3696 } 3697 3698 if (e.key === 'ArrowUp') { 3699 for (var _i4 = $tab.length - 1; _i4 > 0; _i4--) { 3700 if ($tab[_i4].getAttribute('aria-expanded') === "true" || $tab[_i4].getAttribute('aria-expanded') === "false") { 3701 $tab[_i4 - 1].focus(); 3702 3703 e.preventDefault(); 3704 break; 3705 } 3706 } 3707 } 3708 }); 3709 var $closeAlertToggle = document.getElementById("jooa11y-close-alert"); 3710 var $alertPanel = document.getElementById("jooa11y-panel-alert"); 3711 var $alertText = document.getElementById("jooa11y-panel-alert-text"); 3712 var $jooa11ySkipBtn = document.getElementById("jooa11y-cycle-toggle"); 3713 $closeAlertToggle.addEventListener('click', function () { 3714 $alertPanel.classList.remove("jooa11y-active"); 3715 3716 while ($alertText.firstChild) { 3717 $alertText.removeChild($alertText.firstChild); 3718 } 3719 3720 document.querySelectorAll('.jooa11y-pulse-border').forEach(function (el) { 3721 return el.classList.remove('jooa11y-pulse-border'); 3722 }); 3723 $jooa11ySkipBtn.focus(); 3724 }); 3725 }; 3726 3727 this.skipToIssue = function () { 3728 /* Polyfill for scrollTo. scrollTo instead of .animate(), so Jooa11y could use jQuery slim build. Credit: https://stackoverflow.com/a/67108752 & https://github.com/iamdustan/smoothscroll */ 3729 //let reducedMotionQuery = false; 3730 //let scrollBehavior = 'smooth'; 3731 3732 /* 3733 if (!('scrollBehavior' in document.documentElement.style)) { 3734 var js = document.createElement('script'); 3735 js.src = "https://cdn.jsdelivr.net/npm/[email protected]/dist/smoothscroll.min.js"; 3736 document.head.appendChild(js); 3737 } 3738 if (!(document.documentMode)) { 3739 if (typeof window.matchMedia === "function") { 3740 reducedMotionQuery = window.matchMedia("(prefers-reduced-motion: reduce)"); 3741 } 3742 if (!reducedMotionQuery || reducedMotionQuery.matches) { 3743 scrollBehavior = "auto"; 3744 } 3745 } 3746 */ 3747 var jooa11yBtnLocation = 0; 3748 var findJooa11yBtn = document.querySelectorAll('.jooa11y-btn').length; //Jump to issue using keyboard shortcut. 3749 3750 document.addEventListener('keyup', function (e) { 3751 if (e.altKey && e.code === "Period" || e.code == "KeyS") { 3752 skipToIssueToggle(); 3753 e.preventDefault(); 3754 } 3755 }); //Jump to issue using click. 3756 3757 var $skipToggle = document.getElementById("jooa11y-cycle-toggle"); 3758 $skipToggle.addEventListener('click', function (e) { 3759 skipToIssueToggle(); 3760 e.preventDefault(); 3761 }); 3762 3763 var skipToIssueToggle = function skipToIssueToggle() { 3764 //Calculate location of both visible and hidden buttons. 3765 var $findButtons = document.querySelectorAll('.jooa11y-btn'); 3766 var $alertPanel = document.getElementById("jooa11y-panel-alert"); 3767 var $alertText = document.getElementById("jooa11y-panel-alert-text"); 3768 var $alertPanelPreview = document.getElementById("jooa11y-panel-alert-preview"); //const $closeAlertToggle = document.getElementById("jooa11y-close-alert"); 3769 //Mini function: Find visibible parent of hidden element. 3770 3771 var findVisibleParent = function findVisibleParent($el, property, value) { 3772 while ($el !== null) { 3773 var style = window.getComputedStyle($el); 3774 var propValue = style.getPropertyValue(property); 3775 3776 if (propValue === value) { 3777 return $el; 3778 } 3779 3780 $el = $el.parentElement; 3781 } 3782 3783 return null; 3784 }; //Mini function: Calculate top of element. 3785 3786 3787 var offset = function offset($el) { 3788 var rect = $el.getBoundingClientRect(), 3789 scrollTop = window.pageYOffset || document.documentElement.scrollTop; 3790 return { 3791 top: rect.top + scrollTop 3792 }; 3793 }; //'offsetTop' will always return 0 if element is hidden. We rely on offsetTop to determine if element is hidden, although we use 'getBoundingClientRect' to set the scroll position. 3794 3795 3796 var scrollPosition; 3797 var offsetTopPosition = $findButtons[jooa11yBtnLocation].offsetTop; 3798 3799 if (offsetTopPosition === 0) { 3800 var visiblePosition = findVisibleParent($findButtons[jooa11yBtnLocation], 'display', 'none'); 3801 scrollPosition = offset(visiblePosition.previousElementSibling).top - 50; 3802 } else { 3803 scrollPosition = offset($findButtons[jooa11yBtnLocation]).top - 50; 3804 } //Scroll to element if offsetTop is less than or equal to 0. 3805 3806 3807 if (offsetTopPosition >= 0) { 3808 setTimeout(function () { 3809 window.scrollTo({ 3810 top: scrollPosition, 3811 behavior: 'smooth' 3812 }); 3813 }, 1); //Add pulsing border to visible parent of hidden element. 3814 3815 $findButtons.forEach(function ($el) { 3816 var overflowing = findVisibleParent($el, 'display', 'none'); 3817 3818 if (overflowing !== null) { 3819 var hiddenparent = overflowing.previousElementSibling; 3820 hiddenparent.classList.add("jooa11y-pulse-border"); 3821 } 3822 }); 3823 $findButtons[jooa11yBtnLocation].focus(); 3824 } else { 3825 $findButtons[jooa11yBtnLocation].focus(); 3826 } //Alert if element is hidden. 3827 3828 3829 if (offsetTopPosition === 0) { 3830 $alertPanel.classList.add("jooa11y-active"); 3831 $alertText.textContent = "" + Lang._('PANEL_STATUS_HIDDEN'); 3832 $alertPanelPreview.innerHTML = $findButtons[jooa11yBtnLocation].getAttribute('data-tippy-content'); 3833 } else if (offsetTopPosition < 1) { 3834 $alertPanel.classList.remove("jooa11y-active"); 3835 document.querySelectorAll('.jooa11y-pulse-border').forEach(function ($el) { 3836 return $el.classList.remove('jooa11y-pulse-border'); 3837 }); 3838 } //Reset index so it scrolls back to top of page. 3839 3840 3841 jooa11yBtnLocation += 1; 3842 3843 if (jooa11yBtnLocation >= findJooa11yBtn) { 3844 jooa11yBtnLocation = 0; 3845 } 3846 }; 3847 }; 3848 3849 this.containerIgnore = ''; 3850 this.imageIgnore = ''; 3851 this.headerIgnore = ''; 3852 this.linkIgnore = ''; // Load options 3853 3854 this.options = loadOptions(this, options); //Icon on the main toggle. Easy to replace. 3855 3856 var MainToggleIcon = "<svg role='img' focusable='false' width='35px' height='35px' aria-hidden='true' xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'><path fill='#ffffff' d='M256 48c114.953 0 208 93.029 208 208 0 114.953-93.029 208-208 208-114.953 0-208-93.029-208-208 0-114.953 93.029-208 208-208m0-40C119.033 8 8 119.033 8 256s111.033 248 248 248 248-111.033 248-248S392.967 8 256 8zm0 56C149.961 64 64 149.961 64 256s85.961 192 192 192 192-85.961 192-192S362.039 64 256 64zm0 44c19.882 0 36 16.118 36 36s-16.118 36-36 36-36-16.118-36-36 16.118-36 36-36zm117.741 98.023c-28.712 6.779-55.511 12.748-82.14 15.807.851 101.023 12.306 123.052 25.037 155.621 3.617 9.26-.957 19.698-10.217 23.315-9.261 3.617-19.699-.957-23.316-10.217-8.705-22.308-17.086-40.636-22.261-78.549h-9.686c-5.167 37.851-13.534 56.208-22.262 78.549-3.615 9.255-14.05 13.836-23.315 10.217-9.26-3.617-13.834-14.056-10.217-23.315 12.713-32.541 24.185-54.541 25.037-155.621-26.629-3.058-53.428-9.027-82.141-15.807-8.6-2.031-13.926-10.648-11.895-19.249s10.647-13.926 19.249-11.895c96.686 22.829 124.283 22.783 220.775 0 8.599-2.03 17.218 3.294 19.249 11.895 2.029 8.601-3.297 17.219-11.897 19.249z'/></svg>"; 3857 var jooa11ycontainer = document.createElement("div"); 3858 jooa11ycontainer.setAttribute("id", "jooa11y-container"); 3859 jooa11ycontainer.setAttribute("role", "region"); 3860 jooa11ycontainer.setAttribute("lang", this.options.langCode); 3861 jooa11ycontainer.setAttribute("aria-label", Lang._('CONTAINER_LABEL')); 3862 var loadContrastPreference = localStorage.getItem("jooa11y-remember-contrast") === "On"; 3863 var loadLabelsPreference = localStorage.getItem("jooa11y-remember-labels") === "On"; 3864 var loadChangeRequestPreference = localStorage.getItem("jooa11y-remember-links-advanced") === "On"; 3865 var loadReadabilityPreference = localStorage.getItem("jooa11y-remember-readability") === "On"; 3866 jooa11ycontainer.innerHTML = //Main toggle button. 3867 "<button type=\"button\" aria-expanded=\"false\" id=\"jooa11y-toggle\" aria-describedby=\"jooa11y-notification-badge\" aria-label=\"" + Lang._('MAIN_TOGGLE_LABEL') + "\">\n " + MainToggleIcon + "\n <div id=\"jooa11y-notification-badge\">\n <span id=\"jooa11y-notification-count\"></span>\n </div>\n </button>" + //Start of main container. 3868 "<div id=\"jooa11y-panel\">" + ( //Page Outline tab. 3869 "<div id=\"jooa11y-outline-panel\" role=\"tabpanel\" aria-labelledby=\"jooa11y-outline-header\">\n <div id=\"jooa11y-outline-header\" class=\"jooa11y-header-text\">\n <h2 tabindex=\"-1\">" + Lang._('PAGE_OUTLINE') + "</h2>\n </div>\n <div id=\"jooa11y-outline-content\">\n <ul id=\"jooa11y-outline-list\"></ul>\n </div>") + ( //Readability tab. 3870 "<div id=\"jooa11y-readability-panel\">\n <div id=\"jooa11y-readability-content\">\n <h2 class=\"jooa11y-header-text-inline\">" + Lang._('READABILITY') + "</h2>\n <p id=\"jooa11y-readability-info\"></p>\n <ul id=\"jooa11y-readability-details\"></ul>\n </div>\n </div>\n </div>") + ( //End of Page Outline tab. 3871 //Settings tab. 3872 "<div id=\"jooa11y-settings-panel\" role=\"tabpanel\" aria-labelledby=\"jooa11y-settings-header\">\n <div id=\"jooa11y-settings-header\" class=\"jooa11y-header-text\">\n <h2 tabindex=\"-1\">" + Lang._('SETTINGS') + "</h2>\n </div>\n <div id=\"jooa11y-settings-content\">\n <ul id=\"jooa11y-settings-options\">\n <li>\n <label id=\"check-contrast\" for=\"jooa11y-contrast-toggle\">" + Lang._('CONTRAST') + "</label>\n <button id=\"jooa11y-contrast-toggle\"\n aria-labelledby=\"check-contrast\"\n class=\"jooa11y-settings-switch\"\n aria-pressed=\"" + (loadContrastPreference ? "true" : "false") + "\">" + (loadContrastPreference ? Lang._('ON') : Lang._('OFF')) + "</button>\n </li>\n <li>\n <label id=\"check-labels\" for=\"jooa11y-labels-toggle\">" + Lang._('FORM_LABELS') + "</label>\n <button id=\"jooa11y-labels-toggle\" aria-labelledby=\"check-labels\" class=\"jooa11y-settings-switch\"\n aria-pressed=\"" + (loadLabelsPreference ? "true" : "false") + "\">" + (loadLabelsPreference ? Lang._('ON') : Lang._('OFF')) + "</button>\n </li>\n <li>\n <label id=\"check-changerequest\" for=\"jooa11y-links-advanced-toggle\">" + Lang._('LINKS_ADVANCED') + "<span class=\"jooa11y-badge\">AAA</span></label>\n <button id=\"jooa11y-links-advanced-toggle\" aria-labelledby=\"check-changerequest\" class=\"jooa11y-settings-switch\"\n aria-pressed=\"" + (loadChangeRequestPreference ? "true" : "false") + "\">" + (loadChangeRequestPreference ? Lang._('ON') : Lang._('OFF')) + "</button>\n </li>\n <li>\n <label id=\"check-readability\" for=\"jooa11y-readability-toggle\">" + Lang._('READABILITY') + "<span class=\"jooa11y-badge\">AAA</span></label>\n <button id=\"jooa11y-readability-toggle\" aria-labelledby=\"check-readability\" class=\"jooa11y-settings-switch\"\n aria-pressed=\"" + (loadReadabilityPreference ? "true" : "false") + "\">" + (loadReadabilityPreference ? Lang._('ON') : Lang._('OFF')) + "</button>\n </li>\n <li>\n <label id=\"dark-mode\" for=\"jooa11y-theme-toggle\">" + Lang._('DARK_MODE') + "</label>\n <button id=\"jooa11y-theme-toggle\" aria-labelledby=\"dark-mode\" class=\"jooa11y-settings-switch\"></button>\n </li>\n </ul>\n </div>\n </div>") + ( //Console warning messages. 3873 "<div id=\"jooa11y-panel-alert\">\n <div class=\"jooa11y-header-text\">\n <button id=\"jooa11y-close-alert\" class=\"jooa11y-close-btn\" aria-label=\"" + Lang._('ALERT_CLOSE') + "\" aria-describedby=\"jooa11y-alert-heading jooa11y-panel-alert-text\"></button>\n <h2 id=\"jooa11y-alert-heading\">" + Lang._('ALERT_TEXT') + "</h2>\n </div>\n <p id=\"jooa11y-panel-alert-text\"></p>\n <div id=\"jooa11y-panel-alert-preview\"></div>\n </div>") + ( //Main panel that conveys state of page. 3874 "<div id=\"jooa11y-panel-content\">\n <button id=\"jooa11y-cycle-toggle\" type=\"button\" aria-label=\"" + Lang._('SHORTCUT_SR') + "\">\n <div class=\"jooa11y-panel-icon\"></div>\n </button>\n <div id=\"jooa11y-panel-text\"><p id=\"jooa11y-status\" aria-live=\"polite\"></p></div>\n </div>") + ( //Show Outline & Show Settings button. 3875 "<div id=\"jooa11y-panel-controls\" role=\"tablist\" aria-orientation=\"horizontal\">\n <button type=\"button\" role=\"tab\" aria-expanded=\"false\" id=\"jooa11y-outline-toggle\" aria-controls=\"jooa11y-outline-panel\">\n " + Lang._('SHOW_OUTLINE') + "\n </button>\n <button type=\"button\" role=\"tab\" aria-expanded=\"false\" id=\"jooa11y-settings-toggle\" aria-controls=\"jooa11y-settings-panel\">\n " + Lang._('SHOW_SETTINGS') + "\n </button>\n <div style=\"width:35px\"></div>\n </div>") + //End of main container. 3876 "</div>"; 3877 document.body.append(jooa11ycontainer); //Put before document.ready because of CSS flicker when dark mode is enabled. 3878 3879 this.settingPanelToggles(); // Preload before CheckAll function. 3880 3881 this.jooa11yMainToggle(); 3882 this.sanitizeHTMLandComputeARIA(); 3883 this.initializeJumpToIssueTooltip(); 3884 } //---------------------------------------------------------------------- 3885 // Main toggle button 3886 //---------------------------------------------------------------------- 3887 3888 3889 var _proto = Jooa11y.prototype; 3890 3891 _proto.jooa11yMainToggle = function jooa11yMainToggle() { 3892 var _this2 = this; 3893 3894 //Keeps checker active when navigating between pages until it is toggled off. 3895 var jooa11yToggle = document.getElementById("jooa11y-toggle"); 3896 jooa11yToggle.addEventListener('click', function (e) { 3897 if (localStorage.getItem("jooa11y-remember-panel") === "Opened") { 3898 localStorage.setItem("jooa11y-remember-panel", "Closed"); 3899 jooa11yToggle.classList.remove("jooa11y-on"); 3900 jooa11yToggle.setAttribute("aria-expanded", "false"); 3901 3902 _this2.resetAll(); 3903 3904 _this2.updateBadge(); 3905 3906 e.preventDefault(); 3907 } else { 3908 localStorage.setItem("jooa11y-remember-panel", "Opened"); 3909 jooa11yToggle.classList.add("jooa11y-on"); 3910 jooa11yToggle.setAttribute("aria-expanded", "true"); 3911 3912 _this2.checkAll(); //Don't show badge when panel is opened. 3913 3914 3915 document.getElementById("jooa11y-notification-badge").style.display = 'none'; 3916 e.preventDefault(); 3917 } 3918 }); //Remember to leave it open 3919 3920 if (localStorage.getItem("jooa11y-remember-panel") === "Opened") { 3921 jooa11yToggle.classList.add("jooa11y-on"); 3922 jooa11yToggle.setAttribute("aria-expanded", "true"); 3923 } //Crudely give a little time to load any other content or slow post-rendered JS, iFrames, etc. 3924 3925 3926 if (jooa11yToggle.classList.contains("jooa11y-on")) { 3927 jooa11yToggle.classList.toggle("loading-jooa11y"); 3928 jooa11yToggle.setAttribute("aria-expanded", "true"); 3929 setTimeout(this.checkAll, 800); 3930 } //Keyboard commands 3931 3932 3933 document.onkeydown = function (evt) { 3934 evt = evt || window.event; //Escape key to close accessibility checker panel 3935 3936 var isEscape = false; 3937 3938 if ("key" in evt) { 3939 isEscape = evt.key === "Escape" || evt.key === "Esc"; 3940 } else { 3941 isEscape = evt.keyCode === 27; 3942 } 3943 3944 if (isEscape && document.getElementById("jooa11y-panel").classList.contains("jooa11y-active")) { 3945 jooa11yToggle.setAttribute("aria-expanded", "false"); 3946 jooa11yToggle.classList.remove("jooa11y-on"); 3947 jooa11yToggle.click(); 3948 3949 _this2.resetAll(); 3950 } //Alt + A to open accessibility checker panel 3951 3952 3953 if (evt.altKey && evt.code == "KeyA") { 3954 var _jooa11yToggle = document.getElementById("jooa11y-toggle"); 3955 3956 _jooa11yToggle.click(); 3957 3958 _jooa11yToggle.focus(); 3959 3960 evt.preventDefault(); 3961 } 3962 }; 3963 } // ============================================================ 3964 // Helpers: Sanitize HTML and compute ARIA for hyperlinks 3965 // ============================================================ 3966 ; 3967 3968 _proto.sanitizeHTMLandComputeARIA = function sanitizeHTMLandComputeARIA() { 3969 //Helper: Compute alt text on images within a text node. 3970 this.computeTextNodeWithImage = function ($el) { 3971 var imgArray = Array.from($el.querySelectorAll("img")); 3972 var returnText = ""; //No image, has text. 3973 3974 if (imgArray.length === 0 && $el.textContent.trim().length > 1) { 3975 returnText = $el.textContent.trim(); 3976 } //Has image, no text. 3977 else if (imgArray.length && $el.textContent.trim().length === 0) { 3978 var imgalt = imgArray[0].getAttribute("alt"); 3979 3980 if (!imgalt || imgalt === " ") { 3981 returnText = " "; 3982 } else if (imgalt !== undefined) { 3983 returnText = imgalt; 3984 } 3985 } //Has image and text. 3986 //To-do: This is a hack? Any way to do this better? 3987 else if (imgArray.length && $el.textContent.trim().length) { 3988 imgArray.forEach(function (element) { 3989 element.insertAdjacentHTML("afterend", " <span class='jooa11y-clone-image-text' aria-hidden='true'>" + imgArray[0].getAttribute("alt") + "</span> "); 3990 }); 3991 returnText = $el.textContent.trim(); 3992 } 3993 3994 return returnText; 3995 }; //Helper: Handle ARIA labels for Link Text module. 3996 3997 3998 this.computeAriaLabel = function (el) { 3999 if (el.matches("[aria-label]")) { 4000 return el.getAttribute("aria-label"); 4001 } else if (el.matches("[aria-labelledby]")) { 4002 var target = el.getAttribute("aria-labelledby").split(/\s+/); 4003 4004 if (target.length > 0) { 4005 var returnText = ""; 4006 target.forEach(function (x) { 4007 if (document.querySelector("#" + x) === null) { 4008 returnText += " "; 4009 } else { 4010 returnText += document.querySelector("#" + x).firstChild.nodeValue + " "; 4011 } 4012 }); 4013 return returnText; 4014 } else { 4015 return ""; 4016 } 4017 } //Children of element. 4018 else if (Array.from(el.children).filter(function (x) { 4019 return x.matches("[aria-label]"); 4020 }).length > 0) { 4021 return Array.from(el.children)[0].getAttribute("aria-label"); 4022 } else if (Array.from(el.children).filter(function (x) { 4023 return x.matches("[title]"); 4024 }).length > 0) { 4025 return Array.from(el.children)[0].getAttribute("title"); 4026 } else if (Array.from(el.children).filter(function (x) { 4027 return x.matches("[aria-labelledby]"); 4028 }).length > 0) { 4029 var _target = Array.from(el.children)[0].getAttribute("aria-labelledby").split(/\s+/); 4030 4031 if (_target.length > 0) { 4032 var _returnText = ""; 4033 4034 _target.forEach(function (x) { 4035 if (document.querySelector("#" + x) === null) { 4036 _returnText += " "; 4037 } else { 4038 _returnText += document.querySelector("#" + x).firstChild.nodeValue + " "; 4039 } 4040 }); 4041 4042 return _returnText; 4043 } else { 4044 return ""; 4045 } 4046 } else { 4047 return "noAria"; 4048 } 4049 }; 4050 } //---------------------------------------------------------------------- 4051 // Setting's panel: Additional ruleset toggles. 4052 //---------------------------------------------------------------------- 4053 ; 4054 4055 _proto.settingPanelToggles = function settingPanelToggles() { 4056 var _this3 = this; 4057 4058 //Toggle: Contrast 4059 var $jooa11yContrastCheck = document.getElementById("jooa11y-contrast-toggle"); 4060 $jooa11yContrastCheck.onclick = /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee2() { 4061 return regeneratorRuntime.wrap(function _callee2$(_context2) { 4062 while (1) { 4063 switch (_context2.prev = _context2.next) { 4064 case 0: 4065 if (!(localStorage.getItem("jooa11y-remember-contrast") === "On")) { 4066 _context2.next = 9; 4067 break; 4068 } 4069 4070 localStorage.setItem("jooa11y-remember-contrast", "Off"); 4071 $jooa11yContrastCheck.textContent = Lang._('OFF'); 4072 $jooa11yContrastCheck.setAttribute("aria-pressed", "false"); 4073 4074 _this3.resetAll(false); 4075 4076 _context2.next = 7; 4077 return _this3.checkAll(); 4078 4079 case 7: 4080 _context2.next = 15; 4081 break; 4082 4083 case 9: 4084 localStorage.setItem("jooa11y-remember-contrast", "On"); 4085 $jooa11yContrastCheck.textContent = Lang._('ON'); 4086 $jooa11yContrastCheck.setAttribute("aria-pressed", "true"); 4087 4088 _this3.resetAll(false); 4089 4090 _context2.next = 15; 4091 return _this3.checkAll(); 4092 4093 case 15: 4094 case "end": 4095 return _context2.stop(); 4096 } 4097 } 4098 }, _callee2); 4099 })); //Toggle: Form labels 4100 4101 var $jooa11yLabelsCheck = document.getElementById("jooa11y-labels-toggle"); 4102 $jooa11yLabelsCheck.onclick = /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee3() { 4103 return regeneratorRuntime.wrap(function _callee3$(_context3) { 4104 while (1) { 4105 switch (_context3.prev = _context3.next) { 4106 case 0: 4107 if (!(localStorage.getItem("jooa11y-remember-labels") === "On")) { 4108 _context3.next = 9; 4109 break; 4110 } 4111 4112 localStorage.setItem("jooa11y-remember-labels", "Off"); 4113 $jooa11yLabelsCheck.textContent = Lang._('OFF'); 4114 $jooa11yLabelsCheck.setAttribute("aria-pressed", "false"); 4115 4116 _this3.resetAll(false); 4117 4118 _context3.next = 7; 4119 return _this3.checkAll(); 4120 4121 case 7: 4122 _context3.next = 15; 4123 break; 4124 4125 case 9: 4126 localStorage.setItem("jooa11y-remember-labels", "On"); 4127 $jooa11yLabelsCheck.textContent = Lang._('ON'); 4128 $jooa11yLabelsCheck.setAttribute("aria-pressed", "true"); 4129 4130 _this3.resetAll(false); 4131 4132 _context3.next = 15; 4133 return _this3.checkAll(); 4134 4135 case 15: 4136 case "end": 4137 return _context3.stop(); 4138 } 4139 } 4140 }, _callee3); 4141 })); //Toggle: Links (Advanced) 4142 4143 var $jooa11yChangeRequestCheck = document.getElementById("jooa11y-links-advanced-toggle"); 4144 $jooa11yChangeRequestCheck.onclick = /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee4() { 4145 return regeneratorRuntime.wrap(function _callee4$(_context4) { 4146 while (1) { 4147 switch (_context4.prev = _context4.next) { 4148 case 0: 4149 if (!(localStorage.getItem("jooa11y-remember-links-advanced") === "On")) { 4150 _context4.next = 9; 4151 break; 4152 } 4153 4154 localStorage.setItem("jooa11y-remember-links-advanced", "Off"); 4155 $jooa11yChangeRequestCheck.textContent = Lang._('OFF'); 4156 $jooa11yChangeRequestCheck.setAttribute("aria-pressed", "false"); 4157 4158 _this3.resetAll(false); 4159 4160 _context4.next = 7; 4161 return _this3.checkAll(); 4162 4163 case 7: 4164 _context4.next = 15; 4165 break; 4166 4167 case 9: 4168 localStorage.setItem("jooa11y-remember-links-advanced", "On"); 4169 $jooa11yChangeRequestCheck.textContent = Lang._('ON'); 4170 $jooa11yChangeRequestCheck.setAttribute("aria-pressed", "true"); 4171 4172 _this3.resetAll(false); 4173 4174 _context4.next = 15; 4175 return _this3.checkAll(); 4176 4177 case 15: 4178 case "end": 4179 return _context4.stop(); 4180 } 4181 } 4182 }, _callee4); 4183 })); //Toggle: Readability 4184 4185 var $jooa11yReadabilityCheck = document.getElementById("jooa11y-readability-toggle"); 4186 $jooa11yReadabilityCheck.onclick = /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee5() { 4187 return regeneratorRuntime.wrap(function _callee5$(_context5) { 4188 while (1) { 4189 switch (_context5.prev = _context5.next) { 4190 case 0: 4191 if (!(localStorage.getItem("jooa11y-remember-readability") === "On")) { 4192 _context5.next = 10; 4193 break; 4194 } 4195 4196 localStorage.setItem("jooa11y-remember-readability", "Off"); 4197 $jooa11yReadabilityCheck.textContent = Lang._('OFF'); 4198 $jooa11yReadabilityCheck.setAttribute("aria-pressed", "false"); 4199 document.getElementById("jooa11y-readability-panel").classList.remove("jooa11y-active"); 4200 4201 _this3.resetAll(false); 4202 4203 _context5.next = 8; 4204 return _this3.checkAll(); 4205 4206 case 8: 4207 _context5.next = 17; 4208 break; 4209 4210 case 10: 4211 localStorage.setItem("jooa11y-remember-readability", "On"); 4212 $jooa11yReadabilityCheck.textContent = Lang._('ON'); 4213 $jooa11yReadabilityCheck.setAttribute("aria-pressed", "true"); 4214 document.getElementById("jooa11y-readability-panel").classList.add("jooa11y-active"); 4215 4216 _this3.resetAll(false); 4217 4218 _context5.next = 17; 4219 return _this3.checkAll(); 4220 4221 case 17: 4222 case "end": 4223 return _context5.stop(); 4224 } 4225 } 4226 }, _callee5); 4227 })); 4228 4229 if (localStorage.getItem("jooa11y-remember-readability") === "On") { 4230 document.getElementById("jooa11y-readability-panel").classList.add("jooa11y-active"); 4231 } //Toggle: Dark mode. (Credits: https://derekkedziora.com/blog/dark-mode-revisited) 4232 4233 4234 var systemInitiatedDark = window.matchMedia("(prefers-color-scheme: dark)"); 4235 var $jooa11yTheme = document.getElementById("jooa11y-theme-toggle"); 4236 var html = document.querySelector("html"); 4237 var theme = localStorage.getItem("jooa11y-remember-theme"); 4238 4239 if (systemInitiatedDark.matches) { 4240 $jooa11yTheme.textContent = Lang._('ON'); 4241 $jooa11yTheme.setAttribute("aria-pressed", "true"); 4242 } else { 4243 $jooa11yTheme.textContent = Lang._('OFF'); 4244 $jooa11yTheme.setAttribute("aria-pressed", "false"); 4245 } 4246 4247 function prefersColorTest(systemInitiatedDark) { 4248 if (systemInitiatedDark.matches) { 4249 html.setAttribute("data-jooa11y-theme", "dark"); 4250 $jooa11yTheme.textContent = Lang._('ON'); 4251 $jooa11yTheme.setAttribute("aria-pressed", "true"); 4252 localStorage.setItem("jooa11y-remember-theme", ""); 4253 } else { 4254 html.setAttribute("data-jooa11y-theme", "light"); 4255 $jooa11yTheme.textContent = Lang._('OFF'); 4256 $jooa11yTheme.setAttribute("aria-pressed", "false"); 4257 localStorage.setItem("jooa11y-remember-theme", ""); 4258 } 4259 } 4260 4261 systemInitiatedDark.addEventListener('change', prefersColorTest); 4262 $jooa11yTheme.onclick = /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee6() { 4263 var theme; 4264 return regeneratorRuntime.wrap(function _callee6$(_context6) { 4265 while (1) { 4266 switch (_context6.prev = _context6.next) { 4267 case 0: 4268 theme = localStorage.getItem("jooa11y-remember-theme"); 4269 4270 if (theme === "dark") { 4271 html.setAttribute("data-jooa11y-theme", "light"); 4272 localStorage.setItem("jooa11y-remember-theme", "light"); 4273 $jooa11yTheme.textContent = Lang._('OFF'); 4274 $jooa11yTheme.setAttribute("aria-pressed", "false"); 4275 } else if (theme === "light") { 4276 html.setAttribute("data-jooa11y-theme", "dark"); 4277 localStorage.setItem("jooa11y-remember-theme", "dark"); 4278 $jooa11yTheme.textContent = Lang._('ON'); 4279 $jooa11yTheme.setAttribute("aria-pressed", "true"); 4280 } else if (systemInitiatedDark.matches) { 4281 html.setAttribute("data-jooa11y-theme", "light"); 4282 localStorage.setItem("jooa11y-remember-theme", "light"); 4283 $jooa11yTheme.textContent = Lang._('OFF'); 4284 $jooa11yTheme.setAttribute("aria-pressed", "false"); 4285 } else { 4286 html.setAttribute("data-jooa11y-theme", "dark"); 4287 localStorage.setItem("jooa11y-remember-theme", "dark"); 4288 $jooa11yTheme.textContent = Lang._('OFF'); 4289 $jooa11yTheme.setAttribute("aria-pressed", "true"); 4290 } 4291 4292 case 2: 4293 case "end": 4294 return _context6.stop(); 4295 } 4296 } 4297 }, _callee6); 4298 })); 4299 4300 if (theme === "dark") { 4301 html.setAttribute("data-jooa11y-theme", "dark"); 4302 localStorage.setItem("jooa11y-remember-theme", "dark"); 4303 $jooa11yTheme.textContent = Lang._('ON'); 4304 $jooa11yTheme.setAttribute("aria-pressed", "true"); 4305 } else if (theme === "light") { 4306 html.setAttribute("data-jooa11y-theme", "light"); 4307 localStorage.setItem("jooa11y-remember-theme", "light"); 4308 $jooa11yTheme.textContent = Lang._('OFF'); 4309 $jooa11yTheme.setAttribute("aria-pressed", "false"); 4310 } 4311 } //---------------------------------------------------------------------- 4312 // Tooltip for Jump-to-Issue button. 4313 //---------------------------------------------------------------------- 4314 ; 4315 4316 _proto.initializeJumpToIssueTooltip = function initializeJumpToIssueTooltip() { 4317 tippy('#jooa11y-cycle-toggle', { 4318 content: "<div style=\"text-align:center\">" + Lang._('SHORTCUT_TOOLTIP') + " »<br><span class=\"jooa11y-shortcut-icon\"></span></div>", 4319 allowHTML: true, 4320 delay: [900, 0], 4321 trigger: "mouseenter focusin", 4322 arrow: true, 4323 placement: 'top', 4324 theme: "jooa11y-theme", 4325 aria: { 4326 content: null, 4327 expanded: false 4328 }, 4329 appendTo: document.body 4330 }); 4331 } // ---------------------------------------------------------------------- 4332 // Do Initial check 4333 // ---------------------------------------------------------------------- 4334 ; 4335 4336 _proto.doInitialCheck = function doInitialCheck() { 4337 if (localStorage.getItem("jooa11y-remember-panel") === "Closed" || !localStorage.getItem("jooa11y-remember-panel")) { 4338 this.panelActive = true; // Prevent panel popping up after initial check 4339 4340 this.checkAll(); 4341 } 4342 } // ---------------------------------------------------------------------- 4343 // Check all 4344 // ---------------------------------------------------------------------- 4345 // ============================================================ 4346 // Reset all 4347 // ============================================================ 4348 ; 4349 4350 _proto.resetAll = function resetAll(restartPanel) { 4351 if (restartPanel === void 0) { 4352 restartPanel = true; 4353 } 4354 4355 this.panelActive = false; 4356 this.clearEverything(); //Remove eventListeners on the Show Outline and Show Panel toggles. 4357 4358 var $outlineToggle = document.getElementById("jooa11y-outline-toggle"); 4359 var resetOutline = $outlineToggle.cloneNode(true); 4360 $outlineToggle.parentNode.replaceChild(resetOutline, $outlineToggle); 4361 var $settingsToggle = document.getElementById("jooa11y-settings-toggle"); 4362 var resetSettings = $settingsToggle.cloneNode(true); 4363 $settingsToggle.parentNode.replaceChild(resetSettings, $settingsToggle); //Errors 4364 4365 document.querySelectorAll('.jooa11y-error-border').forEach(function (el) { 4366 return el.classList.remove('jooa11y-error-border'); 4367 }); 4368 document.querySelectorAll('.jooa11y-error-text').forEach(function (el) { 4369 return el.classList.remove('jooa11y-error-text'); 4370 }); //Warnings 4371 4372 document.querySelectorAll('.jooa11y-warning-border').forEach(function (el) { 4373 return el.classList.remove('jooa11y-warning-border'); 4374 }); 4375 document.querySelectorAll('.jooa11y-warning-text').forEach(function (el) { 4376 return el.classList.remove('jooa11y-warning-text'); 4377 }); 4378 document.querySelectorAll('p').forEach(function (el) { 4379 return el.classList.remove('jooa11y-fake-list'); 4380 }); 4381 var allcaps = document.querySelectorAll('.jooa11y-warning-uppercase'); 4382 allcaps.forEach(function (el) { 4383 return el.outerHTML = el.innerHTML; 4384 }); //Good 4385 4386 document.querySelectorAll('.jooa11y-good-border').forEach(function (el) { 4387 return el.classList.remove('jooa11y-good-border'); 4388 }); 4389 document.querySelectorAll('.jooa11y-good-text').forEach(function (el) { 4390 return el.classList.remove('jooa11y-good-text'); 4391 }); //Remove 4392 4393 document.querySelectorAll("\n .jooa11y-instance,\n .jooa11y-instance-inline,\n .jooa11y-heading-label,\n #jooa11y-outline-list li,\n .jooa11y-readability-period,\n #jooa11y-readability-info span,\n #jooa11y-readability-details li,\n .jooa11y-clone-image-text\n ").forEach(function (el) { 4394 return el.parentNode.removeChild(el); 4395 }); //Etc 4396 4397 document.querySelectorAll('.jooa11y-overflow').forEach(function (el) { 4398 return el.classList.remove('jooa11y-overflow'); 4399 }); 4400 document.querySelectorAll('.jooa11y-fake-heading').forEach(function (el) { 4401 return el.classList.remove('jooa11y-fake-heading'); 4402 }); 4403 document.querySelectorAll('.jooa11y-pulse-border').forEach(function (el) { 4404 return el.classList.remove('jooa11y-pulse-border'); 4405 }); 4406 document.querySelector('#jooa11y-panel-alert').classList.remove("jooa11y-active"); 4407 var empty = document.querySelector('#jooa11y-panel-alert-text'); 4408 4409 while (empty.firstChild) { 4410 empty.removeChild(empty.firstChild); 4411 } 4412 4413 var clearStatus = document.querySelector('#jooa11y-status'); 4414 4415 while (clearStatus.firstChild) { 4416 clearStatus.removeChild(clearStatus.firstChild); 4417 } 4418 4419 if (restartPanel) { 4420 document.querySelector('#jooa11y-panel').classList.remove("jooa11y-active"); 4421 } 4422 }; 4423 4424 _proto.clearEverything = function clearEverything() {} // ============================================================ 4425 // Initialize tooltips for error/warning/pass buttons: (Tippy.js) 4426 // Although you can also swap this with Bootstrap's tooltip library for example. 4427 // ============================================================ 4428 ; 4429 4430 _proto.initializeTooltips = function initializeTooltips() { 4431 tippy(".jooa11y-btn", { 4432 interactive: true, 4433 trigger: "mouseenter click focusin", 4434 //Focusin trigger to ensure "Jump to issue" button displays tooltip. 4435 arrow: true, 4436 delay: [200, 0], 4437 //Slight delay to ensure mouse doesn't quickly trigger and hide tooltip. 4438 theme: "jooa11y-theme", 4439 placement: 'bottom', 4440 allowHTML: true, 4441 aria: { 4442 content: 'describedby' 4443 }, 4444 appendTo: document.body 4445 }); 4446 } // ============================================================ 4447 // Detect parent containers that have hidden overflow. 4448 // ============================================================ 4449 ; 4450 4451 _proto.detectOverflow = function detectOverflow() { 4452 var findParentWithOverflow = function findParentWithOverflow($el, property, value) { 4453 while ($el !== null) { 4454 var style = window.getComputedStyle($el); 4455 var propValue = style.getPropertyValue(property); 4456 4457 if (propValue === value) { 4458 return $el; 4459 } 4460 4461 $el = $el.parentElement; 4462 } 4463 4464 return null; 4465 }; 4466 4467 var $findButtons = document.querySelectorAll('.jooa11y-btn'); 4468 $findButtons.forEach(function ($el) { 4469 var overflowing = findParentWithOverflow($el, 'overflow', 'hidden'); 4470 4471 if (overflowing !== null) { 4472 overflowing.classList.add('jooa11y-overflow'); 4473 } 4474 }); 4475 } // ============================================================ 4476 // Nudge buttons if they overlap. 4477 // ============================================================ 4478 // ============================================================ 4479 // Update iOS style notification badge on icon. 4480 // ============================================================ 4481 ; 4482 4483 _proto.updateBadge = function updateBadge() { 4484 var totalCount = this.errorCount + this.warningCount; 4485 var notifBadge = document.getElementById("jooa11y-notification-badge"); 4486 4487 if (totalCount === 0) { 4488 notifBadge.style.display = "none"; 4489 } else { 4490 notifBadge.style.display = "flex"; 4491 document.getElementById('jooa11y-notification-count').innerHTML = Lang.sprintf('PANEL_STATUS_ICON', totalCount); 4492 } 4493 } // ---------------------------------------------------------------------- 4494 // Main panel: Display and update panel. 4495 // ---------------------------------------------------------------------- 4496 ; 4497 4498 _proto.updatePanel = function updatePanel() { 4499 this.panelActive = true; 4500 this.errorCount + this.warningCount; 4501 this.buildPanel(); 4502 this.skipToIssue(); 4503 var $jooa11ySkipBtn = document.getElementById("jooa11y-cycle-toggle"); 4504 $jooa11ySkipBtn.disabled = false; 4505 $jooa11ySkipBtn.setAttribute("style", "cursor: pointer !important;"); 4506 var $jooa11yPanel = document.getElementById("jooa11y-panel"); 4507 $jooa11yPanel.classList.add("jooa11y-active"); 4508 var $panelContent = document.getElementById("jooa11y-panel-content"); 4509 var $jooa11yStatus = document.getElementById("jooa11y-status"); 4510 var $findButtons = document.querySelectorAll('.jooa11y-btn'); 4511 4512 if (this.errorCount > 0 && this.warningCount > 0) { 4513 $panelContent.setAttribute("class", "jooa11y-errors"); 4514 $jooa11yStatus.textContent = Lang.sprintf('PANEL_STATUS_BOTH', this.errorCount, this.warningCount); 4515 } else if (this.errorCount > 0) { 4516 $panelContent.setAttribute("class", "jooa11y-errors"); 4517 $jooa11yStatus.textContent = Lang.sprintf('PANEL_STATUS_ERRORS', this.errorCount); 4518 } else if (this.warningCount > 0) { 4519 $panelContent.setAttribute("class", "jooa11y-warnings"); 4520 $jooa11yStatus.textContent = Lang.sprintf('PANEL_STATUS_WARNINGS', this.warningCount); 4521 } else { 4522 $panelContent.setAttribute("class", "jooa11y-good"); 4523 $jooa11yStatus.textContent = Lang._('PANEL_STATUS_NONE'); 4524 4525 if ($findButtons.length === 0) { 4526 $jooa11ySkipBtn.disabled = true; 4527 $jooa11ySkipBtn.setAttribute("style", "cursor: default !important;"); 4528 } 4529 } 4530 } // ============================================================ 4531 // Finds all elements and caches them 4532 // ============================================================ 4533 ; 4534 4535 _proto.findElements = function findElements() { 4536 var _this4 = this; 4537 4538 var allHeadings = Array.from(this.$root.querySelectorAll("h1, h2, h3, h4, h5, h6, [role='heading'][aria-level]")); 4539 var allPs = Array.from(this.$root.querySelectorAll("p")); 4540 this.$containerExclusions = Array.from(document.querySelectorAll(this.containerIgnore)); 4541 this.$h = allHeadings.filter(function (heading) { 4542 return !_this4.$containerExclusions.includes(heading); 4543 }); 4544 this.$p = allPs.filter(function (p) { 4545 return !_this4.$containerExclusions.includes(p); 4546 }); 4547 } // ============================================================ 4548 // Rulesets: Check Headings 4549 // ============================================================ 4550 ; 4551 4552 _proto.checkHeaders = function checkHeaders() { 4553 var _this5 = this; 4554 4555 var prevLevel; 4556 this.$h.forEach(function (el, i) { 4557 var text = _this5.computeTextNodeWithImage(el); 4558 4559 var htext = escapeHTML(text); 4560 var level; 4561 4562 if (el.getAttribute("aria-level")) { 4563 level = +el.getAttribute("aria-level"); 4564 } else { 4565 level = +el.tagName.slice(1); 4566 } 4567 4568 var headingLength = el.textContent.trim().length; 4569 var error = null; 4570 var warning = null; 4571 4572 if (level - prevLevel > 1 && i !== 0) { 4573 error = Lang.sprintf('HEADING_NON_CONSECUTIVE_LEVEL', prevLevel, level); 4574 } else if (el.textContent.trim().length === 0) { 4575 if (el.querySelectorAll("img").length) { 4576 var imgalt = el.querySelector("img").getAttribute("alt"); 4577 4578 if (imgalt === undefined || imgalt === " " || imgalt === "") { 4579 error = Lang.sprintf('HEADING_EMPTY_WITH_IMAGE', level); 4580 el.classList.add("jooa11y-error-text"); 4581 } 4582 } else { 4583 error = Lang.sprintf('HEADING_EMPTY', level); 4584 el.classList.add("jooa11y-error-text"); 4585 } 4586 } else if (i === 0 && level !== 1 && level !== 2) { 4587 error = Lang._('HEADING_FIRST'); 4588 } else if (el.textContent.trim().length > 170) { 4589 warning = Lang._('HEADING_LONG') + " . " + Lang.sprintf('HEADING_LONG_INFO', headingLength); 4590 } 4591 4592 prevLevel = level; 4593 var li = "<li class='jooa11y-outline-" + level + "'>\n <span class='jooa11y-badge'>" + level + "</span>\n <span class='jooa11y-outline-list-item'>" + htext + "</span>\n </li>"; 4594 var liError = "<li class='jooa11y-outline-" + level + "'>\n <span class='jooa11y-badge jooa11y-error-badge'>\n <span aria-hidden='true'>✗</span>\n <span class='jooa11y-visually-hidden'>" + Lang._('ERROR') + "</span> " + level + "</span>\n <span class='jooa11y-outline-list-item jooa11y-red-text jooa11y-bold'>" + htext + "</span>\n </li>"; 4595 var liWarning = "<li class='jooa11y-outline-" + level + "'>\n <span class='jooa11y-badge jooa11y-warning-badge'>\n <span aria-hidden='true'>?</span>\n <span class='jooa11y-visually-hidden'>" + Lang._('WARNING') + "</span> " + level + "</span>\n <span class='jooa11y-outline-list-item jooa11y-yellow-text jooa11y-bold'>" + htext + "</span>\n </li>"; 4596 var ignoreArray = []; 4597 4598 if (_this5.options.outlineIgnore) { 4599 ignoreArray = Array.from(document.querySelectorAll(_this5.options.outlineIgnore)); 4600 } 4601 4602 if (!ignoreArray.includes(el)) { 4603 //Append heading labels. 4604 el.insertAdjacentHTML("beforeend", "<span class='jooa11y-heading-label'>H" + level + "</span>"); //Heading errors 4605 4606 if (error != null && el.closest("a")) { 4607 _this5.errorCount++; 4608 el.classList.add("jooa11y-error-border"); 4609 el.closest("a").insertAdjacentHTML("afterend", _this5.annotate(Lang._('ERROR'), error, true)); 4610 document.querySelector("#jooa11y-outline-list").insertAdjacentHTML("beforeend", liError); 4611 } else if (error != null) { 4612 _this5.errorCount++; 4613 el.classList.add("jooa11y-error-border"); 4614 el.insertAdjacentHTML("beforebegin", _this5.annotate(Lang._('ERROR'), error)); 4615 document.querySelector("#jooa11y-outline-list").insertAdjacentHTML("beforeend", liError); 4616 } //Heading warnings 4617 else if (warning != null && el.closest("a")) { 4618 _this5.warningCount++; 4619 el.closest("a").insertAdjacentHTML("afterend", _this5.annotate(Lang._('WARNING'), warning)); 4620 document.querySelector("#jooa11y-outline-list").insertAdjacentHTML("beforeend", liWarning); 4621 } else if (warning != null) { 4622 el.insertAdjacentHTML("beforebegin", _this5.annotate(Lang._('WARNING'), warning)); 4623 document.querySelector("#jooa11y-outline-list").insertAdjacentHTML("beforeend", liWarning); 4624 } //Not an error or warning 4625 else if (error == null || warning == null) { 4626 document.querySelector("#jooa11y-outline-list").insertAdjacentHTML("beforeend", li); 4627 } 4628 } 4629 }); //Check to see there is at least one H1 on the page. 4630 4631 var $h1 = Array.from(this.$root.querySelectorAll('h1, [role="heading"][aria-level="1"]')).filter(function ($h) { 4632 return !_this5.$containerExclusions.includes($h); 4633 }); 4634 4635 if ($h1.length === 0) { 4636 this.errorCount++; 4637 document.querySelector('#jooa11y-outline-header').insertAdjacentHTML('afterend', "<div class='jooa11y-instance jooa11y-missing-h1'>\n <span class='jooa11y-badge jooa11y-error-badge'><span aria-hidden='true'>✗</span><span class='jooa11y-visually-hidden'>" + Lang._('ERROR') + "</span></span>\n <span class='jooa11y-red-text jooa11y-bold'>" + Lang._('PANEL_HEADING_MISSING_ONE') + "</span>\n </div>"); 4638 document.querySelector("#jooa11y-container").insertAdjacentHTML('afterend', this.annotateBanner(Lang._('ERROR'), Lang._('HEADING_MISSING_ONE'))); 4639 } 4640 } // ============================================================ 4641 // Rulesets: Link text 4642 // ============================================================ 4643 ; 4644 4645 _proto.checkLinkText = function checkLinkText() { 4646 var _this6 = this; 4647 4648 var containsLinkTextStopWords = function containsLinkTextStopWords(textContent) { 4649 var urlText = ["http", ".asp", ".htm", ".php", ".edu/", ".com/", ".net/", ".org/", ".us/", ".ca/", ".de/", ".icu/", ".uk/", ".ru/", ".info/", ".top/", ".xyz/", ".tk/", ".cn/", ".ga/", ".cf/", ".nl/", ".io/"]; 4650 var hit = [null, null, null]; // Flag partial stop words. 4651 4652 _this6.options.partialAltStopWords.forEach(function (word) { 4653 if (textContent.length === word.length && textContent.toLowerCase().indexOf(word) >= 0) { 4654 hit[0] = word; 4655 return false; 4656 } 4657 }); // Other warnings we want to add. 4658 4659 4660 _this6.options.warningAltWords.forEach(function (word) { 4661 if (textContent.toLowerCase().indexOf(word) >= 0) { 4662 hit[1] = word; 4663 return false; 4664 } 4665 }); // Flag link text containing URLs. 4666 4667 4668 urlText.forEach(function (word) { 4669 if (textContent.toLowerCase().indexOf(word) >= 0) { 4670 hit[2] = word; 4671 return false; 4672 } 4673 }); 4674 return hit; 4675 }; 4676 /* Mini function if you need to exclude any text contained with a span. We created this function to ignore automatically appended sr-only text for external links and document filetypes. 4677 $.fn.ignore = function(sel){ 4678 return this.clone().find(sel||">*").remove().end(); 4679 }; 4680 $el.ignore("span.sr-only").text().trim(); 4681 Example: <a href="#">learn more <span class="sr-only">(external)</span></a> 4682 This function will ignore the text "(external)", and correctly flag this link as an error for non descript link text. */ 4683 4684 4685 var fnIgnore = function fnIgnore(element, selector) { 4686 var $clone = element.cloneNode(true); 4687 var $excluded = Array.from(selector ? $clone.querySelectorAll(selector) : $clone.children); 4688 $excluded.forEach(function ($c) { 4689 $c.parentElement.removeChild($c); 4690 }); 4691 return $clone; 4692 }; 4693 4694 var $linkIgnore = Array.from(this.$root.querySelectorAll(this.linkIgnore)); 4695 var $links = Array.from(this.$root.querySelectorAll('a[href]')).filter(function ($a) { 4696 return !$linkIgnore.includes($a); 4697 }); 4698 $links.forEach(function (el) { 4699 var linkText = _this6.computeAriaLabel(el); 4700 4701 var hasAriaLabelledBy = el.getAttribute('aria-labelledby'); 4702 var hasAriaLabel = el.getAttribute('aria-label'); 4703 var hasTitle = el.getAttribute('title'); 4704 var childAriaLabelledBy = null; 4705 var childAriaLabel = null; 4706 var childTitle = null; 4707 4708 if (el.children.length) { 4709 var $firstChild = el.children[0]; 4710 childAriaLabelledBy = $firstChild.getAttribute('aria-labelledby'); 4711 childAriaLabel = $firstChild.getAttribute('aria-label'); 4712 childTitle = $firstChild.getAttribute('title'); 4713 } 4714 4715 var error = containsLinkTextStopWords(fnIgnore(el, _this6.options.linkIgnoreSpan).textContent.trim()); 4716 4717 if (linkText === 'noAria') { 4718 linkText = el.textContent; 4719 } //Flag empty hyperlinks 4720 4721 4722 if (el.getAttribute('href') && !el.textContent.trim()) { 4723 if (el.querySelectorAll('img').length) ;else if (hasAriaLabelledBy || hasAriaLabel) { 4724 el.classList.add("jooa11y-good-border"); 4725 el.insertAdjacentHTML('beforebegin', _this6.annotate(Lang._('GOOD'), Lang.sprintf('LINK_LABEL', linkText), true)); 4726 } else if (hasTitle) { 4727 var _linkText = hasTitle; 4728 el.classList.add("jooa11y-good-border"); 4729 el.insertAdjacentHTML('beforebegin', _this6.annotate(Lang._('GOOD'), Lang.sprintf('LINK_LABEL', _linkText), true)); 4730 } else if (el.children.length) { 4731 if (childAriaLabelledBy || childAriaLabel || childTitle) { 4732 el.classList.add("jooa11y-good-border"); 4733 el.insertAdjacentHTML('beforebegin', _this6.annotate(Lang._('GOOD'), Lang.sprintf('LINK_LABEL', linkText), true)); 4734 } else { 4735 _this6.errorCount++; 4736 el.classList.add("jooa11y-error-border"); 4737 el.insertAdjacentHTML('afterend', _this6.annotate(Lang._('ERROR'), Lang.sprintf('LINK_EMPTY_LINK_NO_LABEL'), true)); 4738 } 4739 } else { 4740 _this6.errorCount++; 4741 el.classList.add("jooa11y-error-border"); 4742 el.insertAdjacentHTML('afterend', _this6.annotate(Lang._('ERROR'), Lang._('LINK_EMPTY'), true)); 4743 } 4744 } else if (error[0] !== null) { 4745 if (hasAriaLabelledBy) { 4746 el.insertAdjacentHTML('beforebegin', _this6.annotate(Lang._('GOOD'), Lang.sprintf('LINK_LABEL', linkText), true)); 4747 } else if (hasAriaLabel) { 4748 el.insertAdjacentHTML('beforebegin', _this6.annotate(Lang._('GOOD'), Lang.sprintf('LINK_LABEL', hasAriaLabel), true)); 4749 } else if (el.getAttribute('aria-hidden') === 'true' && el.getAttribute('tabindex') === '-1') ;else { 4750 _this6.errorCount++; 4751 el.classList.add("jooa11y-error-text"); 4752 el.insertAdjacentHTML('afterend', _this6.annotate(Lang._('ERROR'), Lang.sprintf('LINK_STOPWORD', error[0]) + " <hr aria-hidden=\"true\"> " + Lang._('LINK_STOPWORD_TIP'), true)); 4753 } 4754 } else if (error[1] !== null) { 4755 _this6.warningCount++; 4756 el.classList.add("jooa11y-warning-text"); 4757 el.insertAdjacentHTML('afterend', _this6.annotate(Lang._('WARNING'), Lang.sprintf('LINK_BEST_PRACTICES', error[1]) + " <hr aria-hidden=\"true\"> " + Lang._('LINK_BEST_PRACTICES_DETAILS'), true)); 4758 } else if (error[2] != null) { 4759 if (linkText.length > 40) { 4760 _this6.warningCount++; 4761 el.classList.add("jooa11y-warning-text"); 4762 el.insertAdjacentHTML('afterend', _this6.annotate(Lang._('WARNING'), Lang._('LINK_URL') + " <hr aria-hidden=\"true\"> " + Lang._('LINK_URL_TIP'), true)); 4763 } 4764 } 4765 }); 4766 } // ============================================================ 4767 // Rulesets: Links (Advanced) 4768 // ============================================================ 4769 ; 4770 4771 _proto.checkLinksAdvanced = function checkLinksAdvanced() { 4772 var _this7 = this; 4773 4774 var $linkIgnore = Array.from(this.$root.querySelectorAll(this.linkIgnore + ', #jooa11y-container a, .jooa11y-exclude')); 4775 var $linksTargetBlank = Array.from(this.$root.querySelectorAll('a[href]')).filter(function ($a) { 4776 return !$linkIgnore.includes($a); 4777 }); 4778 var seen = {}; 4779 $linksTargetBlank.forEach(function (el) { 4780 var linkText = _this7.computeAriaLabel(el); 4781 4782 if (linkText === 'noAria') { 4783 linkText = el.textContent; 4784 } 4785 4786 var fileTypeMatch = el.matches("\n a[href$='.pdf'],\n a[href$='.doc'],\n a[href$='.zip'],\n a[href$='.mp3'],\n a[href$='.txt'],\n a[href$='.exe'],\n a[href$='.dmg'],\n a[href$='.rtf'],\n a[href$='.pptx'],\n a[href$='.ppt'],\n a[href$='.xls'],\n a[href$='.xlsx'],\n a[href$='.csv'],\n a[href$='.mp4'],\n a[href$='.mov'],\n a[href$='.avi']\n "); //Links with identical accessible names have equivalent purpose. 4787 //If link has an image, process alt attribute, 4788 //To-do: Kinda hacky. Doesn't return accessible name of link in correct order. 4789 4790 var $img = el.querySelector('img'); 4791 var alt = $img ? $img.getAttribute('alt') || '' : ''; //Return link text and image's alt text. 4792 4793 var linkTextTrimmed = linkText.trim().toLowerCase() + " " + alt; 4794 var href = el.getAttribute("href"); 4795 4796 if (linkText.length !== 0) { 4797 if (seen[linkTextTrimmed] && linkTextTrimmed.length !== 0) { 4798 if (seen[href]) ;else { 4799 _this7.warningCount++; 4800 el.classList.add("jooa11y-warning-text"); 4801 el.insertAdjacentHTML('afterend', _this7.annotate(Lang._('WARNING'), Lang._('LINK_IDENTICAL_NAME') + " <hr aria-hidden=\"true\"> " + Lang.sprintf('LINK_IDENTICAL_NAME_TIP', linkText), true)); 4802 } 4803 } else { 4804 seen[linkTextTrimmed] = true; 4805 seen[href] = true; 4806 } 4807 } //New tab or new window. 4808 4809 4810 var containsNewWindowPhrases = _this7.options.newWindowPhrases.some(function (pass) { 4811 return linkText.toLowerCase().indexOf(pass) >= 0; 4812 }); //Link that points to a file type indicates that it does. 4813 4814 4815 var containsFileTypePhrases = _this7.options.fileTypePhrases.some(function (pass) { 4816 return linkText.toLowerCase().indexOf(pass) >= 0; 4817 }); 4818 4819 if (el.getAttribute("target") === "_blank" && !fileTypeMatch && !containsNewWindowPhrases) { 4820 _this7.warningCount++; 4821 el.classList.add("jooa11y-warning-text"); 4822 el.insertAdjacentHTML('afterend', _this7.annotate(Lang._('WARNING'), Lang._('NEW_TAB_WARNING') + " <hr aria-hidden=\"true\"> " + Lang._('NEW_TAB_WARNING_TIP'), true)); 4823 } 4824 4825 if (fileTypeMatch && !containsFileTypePhrases) { 4826 _this7.warningCount++; 4827 el.classList.add("jooa11y-warning-text"); 4828 el.insertAdjacentHTML('afterend', _this7.annotate(Lang._('WARNING'), Lang._('FILE_TYPE_WARNING') + " <hr aria-hidden=\"true\"> " + Lang._('FILE_TYPE_WARNING_TIP'), true)); 4829 } 4830 }); 4831 } // ============================================================ 4832 // Ruleset: Underlined text 4833 // ============================================================ 4834 // check text for <u> tags 4835 ; 4836 4837 _proto.checkUnderline = function checkUnderline() { 4838 var _this8 = this; 4839 4840 var underline = Array.from(this.$root.querySelectorAll('u')); 4841 underline.forEach(function ($el) { 4842 _this8.warningCount++; 4843 $el.insertAdjacentHTML('beforebegin', _this8.annotate(Lang._('WARNING'), Lang._('TEXT_UNDERLINE_WARNING') + " <hr aria-hidden=\"true\"> " + Lang._('TEXT_UNDERLINE_WARNING_TIP'), true)); 4844 }); // check for text-decoration-line: underline 4845 4846 var computed = Array.from(this.$root.querySelectorAll('h1, h2, h3, h4, h5, h6, p, div, span, li, blockquote')); 4847 computed.forEach(function ($el) { 4848 var style = getComputedStyle($el), 4849 decoration = style.textDecorationLine; 4850 4851 if (decoration === 'underline') { 4852 _this8.warningCount++; 4853 $el.insertAdjacentHTML('beforebegin', _this8.annotate(Lang._('WARNING'), Lang._('TEXT_UNDERLINE_WARNING') + " <hr aria-hidden=\"true\"> " + Lang._('TEXT_UNDERLINE_WARNING_TIP'), true)); 4854 } 4855 }); 4856 } // ============================================================ 4857 // Ruleset: Alternative text 4858 // ============================================================ 4859 ; 4860 4861 _proto.checkAltText = function checkAltText() { 4862 var _this9 = this; 4863 4864 var containsAltTextStopWords = function containsAltTextStopWords(alt) { 4865 var altUrl = [".png", ".jpg", ".jpeg", ".gif", ".tiff", ".svg"]; 4866 var hit = [null, null, null]; 4867 altUrl.forEach(function (word) { 4868 if (alt.toLowerCase().indexOf(word) >= 0) { 4869 hit[0] = word; 4870 } 4871 }); 4872 4873 _this9.options.suspiciousAltWords.forEach(function (word) { 4874 if (alt.toLowerCase().indexOf(word) >= 0) { 4875 hit[1] = word; 4876 } 4877 }); 4878 4879 _this9.options.placeholderAltStopWords.forEach(function (word) { 4880 if (alt.length === word.length && alt.toLowerCase().indexOf(word) >= 0) { 4881 hit[2] = word; 4882 } 4883 }); 4884 4885 return hit; 4886 }; // Stores the corresponding issue text to alternative text 4887 4888 4889 var images = Array.from(this.$root.querySelectorAll("img")); 4890 var excludeimages = Array.from(this.$root.querySelectorAll(this.imageIgnore)); 4891 var $img = images.filter(function ($el) { 4892 return !excludeimages.includes($el); 4893 }); 4894 $img.forEach(function ($el) { 4895 var alt = $el.getAttribute("alt"); 4896 4897 if (alt === null) { 4898 if ($el.closest('a[href]')) { 4899 if ($el.closest('a[href]').textContent.trim().length > 1) { 4900 $el.classList.add("jooa11y-error-border"); 4901 $el.closest('a[href]').insertAdjacentHTML('beforebegin', _this9.annotate(Lang._('ERROR'), Lang._('MISSING_ALT_LINK_BUT_HAS_TEXT_MESSAGE'), false, true)); 4902 } else if ($el.closest('a[href]').textContent.trim().length === 0) { 4903 $el.classList.add("jooa11y-error-border"); 4904 $el.closest('a[href]').insertAdjacentHTML('beforebegin', _this9.annotate(Lang._('ERROR'), Lang._('MISSING_ALT_LINK_MESSAGE'), false, true)); 4905 } 4906 } // General failure message if image is missing alt. 4907 else { 4908 $el.classList.add("jooa11y-error-border"); 4909 $el.insertAdjacentHTML('beforebegin', _this9.annotate(Lang._('ERROR'), Lang._('MISSING_ALT_MESSAGE'), false, true)); 4910 } 4911 } // If alt attribute is present, further tests are done. 4912 else { 4913 var altText = escapeHTML(alt); //Prevent tooltip from breaking. 4914 4915 var error = containsAltTextStopWords(altText); 4916 var altLength = alt.length; // Image fails if a stop word was found. 4917 4918 if (error[0] != null && $el.closest("a[href]")) { 4919 _this9.errorCount++; 4920 $el.classList.add("jooa11y-error-border"); 4921 $el.closest("a[href]").insertAdjacentHTML('beforebegin', _this9.annotate(Lang._('ERROR'), Lang.sprintf('LINK_IMAGE_BAD_ALT_MESSAGE', altText, error[0]) + " <hr aria-hidden=\"true\"> " + Lang._('LINK_IMAGE_BAD_ALT_MESSAGE_INFO'), false)); 4922 } else if (error[2] != null && $el.closest("a[href]")) { 4923 _this9.errorCount++; 4924 $el.classList.add("jooa11y-error-border"); 4925 $el.closest("a[href]").insertAdjacentHTML('beforebegin', _this9.annotate(Lang._('ERROR'), Lang.sprintf('LINK_IMAGE_PLACEHOLDER_ALT_MESSAGE', altText), false, true)); 4926 } else if (error[1] != null && $el.closest("a[href]")) { 4927 _this9.warningCount++; 4928 $el.classList.add("jooa11y-warning-border"); 4929 $el.closest("a[href]").insertAdjacentHTML('beforebegin', _this9.annotate(Lang._('WARNING'), Lang.sprintf('LINK_IMAGE_SUS_ALT_MESSAGE', altText, error[1]) + " <hr aria-hidden=\"true\"> " + Lang.sprintf('LINK_IMAGE_SUS_ALT_MESSAGE_INFO', altText), false)); 4930 } else if (error[0] != null) { 4931 _this9.errorCount++; 4932 $el.classList.add("jooa11y-error-border"); 4933 $el.insertAdjacentHTML('beforebegin', _this9.annotate(Lang._('ERROR'), Lang._('LINK_ALT_HAS_BAD_WORD_MESSAGE') + " <hr aria-hidden=\"true\"> " + Lang.sprintf('LINK_ALT_HAS_BAD_WORD_MESSAGE_INFO', error[0], altText), false)); 4934 } else if (error[2] != null) { 4935 _this9.errorCount++; 4936 $el.classList.add("jooa11y-error-border"); 4937 $el.insertAdjacentHTML('beforebegin', _this9.annotate(Lang._('ERROR'), Lang.sprintf('LINK_ALT_PLACEHOLDER_MESSAGE', altText), false)); 4938 } else if (error[1] != null) { 4939 _this9.warningCount++; 4940 $el.classList.add("jooa11y-warning-border"); 4941 $el.insertAdjacentHTML('beforebegin', _this9.annotate(Lang._('WARNING'), Lang.sprintf('LINK_ALT_HAS_SUS_WORD_MESSAGE', altText, error[1]) + " <hr aria-hidden=\"true\"> " + Lang.sprintf('LINK_ALT_HAS_SUS_WORD_MESSAGE_INFO', altText), false)); 4942 } else if ((alt === "" || alt === " ") && $el.closest("a[href]")) { 4943 if ($el.closest("a[href]").getAttribute("tabindex") === "-1" && $el.closest("a[href]").getAttribute("aria-hidden") === "true") ;else if ($el.closest("a[href]").getAttribute("aria-hidden") === "true") { 4944 _this9.errorCount++; 4945 $el.classList.add("jooa11y-error-border"); 4946 $el.closest("a[href]").insertAdjacentHTML('beforebegin', _this9.annotate(Lang._('ERROR'), Lang._('LINK_HYPERLINKED_IMAGE_ARIA_HIDDEN'), false, true)); 4947 } else if ($el.closest("a[href]").textContent.trim().length === 0) { 4948 _this9.errorCount++; 4949 $el.classList.add("jooa11y-error-border"); 4950 $el.closest("a[href]").insertAdjacentHTML('beforebegin', _this9.annotate(Lang._('ERROR'), Lang._('LINK_IMAGE_LINK_NULL_ALT_NO_TEXT_MESSAGE'), false, true)); 4951 } else { 4952 $el.closest("a[href]").insertAdjacentHTML('beforebegin', _this9.annotate(Lang._('GOOD'), Lang._('LINK_LINK_HAS_ALT_MESSAGE'), false, true)); 4953 } 4954 } //Link and contains alt text. 4955 else if (alt.length > 250 && $el.closest("a[href]")) { 4956 _this9.warningCount++; 4957 $el.classList.add("jooa11y-warning-border"); 4958 $el.closest("a[href]").insertAdjacentHTML('beforebegin', _this9.annotate(Lang._('WARNING'), Lang._('HYPERLINK_ALT_LENGTH_MESSAGE') + " <hr aria-hidden=\"true\"> " + Lang.sprintf('HYPERLINK_ALT_LENGTH_MESSAGE_INFO', altText, altLength), false)); 4959 } //Link and contains an alt text. 4960 else if (alt !== "" && $el.closest("a[href]") && $el.closest("a[href]").textContent.trim().length === 0) { 4961 _this9.warningCount++; 4962 $el.classList.add("jooa11y-warning-border"); 4963 $el.closest("a[href]").insertAdjacentHTML('beforebegin', _this9.annotate(Lang._('WARNING'), Lang._('LINK_IMAGE_LINK_ALT_TEXT_MESSAGE') + " <hr aria-hidden=\"true\"> " + Lang.sprintf('LINK_IMAGE_LINK_ALT_TEXT_MESSAGE_INFO', altText), false)); 4964 } //Contains alt text & surrounding link text. 4965 else if (alt !== "" && $el.closest("a[href]") && $el.closest("a[href]").textContent.trim().length > 1) { 4966 _this9.warningCount++; 4967 $el.classList.add("jooa11y-warning-border"); 4968 $el.closest("a[href]").insertAdjacentHTML('beforebegin', _this9.annotate(Lang._('WARNING'), Lang._('LINK_ANCHOR_LINK_AND_ALT_MESSAGE') + " <hr aria-hidden=\"true\"> " + Lang.sprintf('LINK_ANCHOR_LINK_AND_ALT_MESSAGE_INFO', altText), false)); 4969 } //Decorative alt and not a link. 4970 else if (alt === "" || alt === " ") { 4971 if ($el.closest("figure")) { 4972 var figcaption = $el.closest("figure").querySelector("figcaption"); 4973 4974 if (figcaption !== null && figcaption.textContent.trim().length >= 1) { 4975 _this9.warningCount++; 4976 $el.classList.add("jooa11y-warning-border"); 4977 $el.insertAdjacentHTML('beforebegin', _this9.annotate(Lang._('WARNING'), Lang._('IMAGE_FIGURE_DECORATIVE') + " <hr aria-hidden=\"true\"> " + Lang._('IMAGE_FIGURE_DECORATIVE_INFO'), false, true)); 4978 } 4979 } else { 4980 _this9.warningCount++; 4981 $el.classList.add("jooa11y-warning-border"); 4982 $el.insertAdjacentHTML('beforebegin', _this9.annotate(Lang._('WARNING'), Lang._('LINK_DECORATIVE_MESSAGE'), false, true)); 4983 } 4984 } else if (alt.length > 250) { 4985 _this9.warningCount++; 4986 $el.classList.add("jooa11y-warning-border"); 4987 $el.insertAdjacentHTML('beforebegin', _this9.annotate(Lang._('WARNING'), Lang._('LINK_ALT_TOO_LONG_MESSAGE') + " <hr aria-hidden=\"true\"> " + Lang.sprintf('LINK_ALT_TOO_LONG_MESSAGE_INFO', altText, altLength), false)); 4988 } else if (alt !== "") { 4989 //Figure element has same alt and caption text. 4990 if ($el.closest("figure")) { 4991 var _figcaption = $el.closest("figure").querySelector("figcaption"); 4992 4993 if (_figcaption !== null && _figcaption.textContent.trim().toLowerCase === altText.trim().toLowerCase) { 4994 _this9.warningCount++; 4995 $el.classList.add("jooa11y-warning-border"); 4996 $el.insertAdjacentHTML('beforebegin', _this9.annotate(Lang._('WARNING'), Lang.sprintf('IMAGE_FIGURE_DUPLICATE_ALT', altText) + " <hr aria-hidden=\"true\"> " + Lang._('IMAGE_FIGURE_DECORATIVE_INFO'), false, true)); 4997 } 4998 } //If image has alt text - pass! 4999 else { 5000 $el.insertAdjacentHTML('beforebegin', _this9.annotate(Lang._('GOOD'), "" + Lang.sprintf('LINK_PASS_ALT', altText), false, true)); 5001 } 5002 } 5003 } 5004 }); 5005 } // ============================================================ 5006 // Rulesets: Labels 5007 // ============================================================ 5008 ; 5009 5010 _proto.checkLabels = function checkLabels() { 5011 var _this10 = this; 5012 5013 var $inputs = Array.from(this.$root.querySelectorAll('input, select, textarea')).filter(function ($i) { 5014 return !_this10.$containerExclusions.includes($i) && !isElementHidden($i); 5015 }); 5016 $inputs.forEach(function (el) { 5017 var ariaLabel = _this10.computeAriaLabel(el); 5018 5019 var type = el.getAttribute('type'); //If button type is submit or button: pass 5020 5021 if (type === "submit" || type === "button" || type === "hidden") ; //Inputs where type="image". 5022 else if (type === "image") { 5023 var imgalt = el.getAttribute("alt"); 5024 5025 if (!imgalt || imgalt === ' ') { 5026 if (el.getAttribute("aria-label")) ;else { 5027 _this10.errorCount++; 5028 el.classList.add("jooa11y-error-border"); 5029 el.insertAdjacentHTML('afterend', _this10.annotate(Lang._('ERROR'), Lang._('LABELS_MISSING_IMAGE_INPUT_MESSAGE'), true)); 5030 } 5031 } 5032 } //Recommendation to remove reset buttons. 5033 else if (type === "reset") { 5034 _this10.warningCount++; 5035 el.classList.add("jooa11y-warning-border"); 5036 el.insertAdjacentHTML('afterend', _this10.annotate(Lang._('WARNING'), Lang._('LABELS_INPUT_RESET_MESSAGE') + " <hr aria-hidden=\"true\"> " + Lang._('LABELS_INPUT_RESET_MESSAGE_TIP'), true)); 5037 } //Uses ARIA. Warn them to ensure there's a visible label. 5038 else if (el.getAttribute("aria-label") || el.getAttribute("aria-labelledby") || el.getAttribute("title")) { 5039 if (el.getAttribute("title")) { 5040 var _ariaLabel = el.getAttribute("title"); 5041 5042 _this10.warningCount++; 5043 el.classList.add("jooa11y-warning-border"); 5044 el.insertAdjacentHTML('afterend', _this10.annotate(Lang._('WARNING'), Lang._('LABELS_ARIA_LABEL_INPUT_MESSAGE') + " <hr aria-hidden=\"true\"> " + Lang.sprintf('LABELS_ARIA_LABEL_INPUT_MESSAGE_INFO', _ariaLabel), true)); 5045 } else { 5046 _this10.warningCount++; 5047 el.classList.add("jooa11y-warning-border"); 5048 el.insertAdjacentHTML('afterend', _this10.annotate(Lang._('WARNING'), Lang._('LABELS_ARIA_LABEL_INPUT_MESSAGE') + " <hr aria-hidden=\"true\"> " + Lang.sprintf('LABELS_ARIA_LABEL_INPUT_MESSAGE_INFO', ariaLabel), true)); 5049 } 5050 } //Implicit labels. 5051 else if (el.closest('label') && el.closest('label').textContent.trim()) ; //Has an ID but doesn't have a matching FOR attribute. 5052 else if (el.getAttribute("id") && Array.from(el.parentElement.children).filter(function ($c) { 5053 return $c.nodeName === 'LABEL'; 5054 }).length) { 5055 var $labels = Array.from(el.parentElement.children).filter(function ($c) { 5056 return $c.nodeName === 'LABEL'; 5057 }); 5058 var hasFor = false; 5059 $labels.forEach(function ($l) { 5060 if (hasFor) return; 5061 5062 if ($l.getAttribute('for') === el.getAttribute('id')) { 5063 hasFor = true; 5064 } 5065 }); 5066 5067 if (!hasFor) { 5068 _this10.errorCount++; 5069 el.classList.add("jooa11y-error-border"); 5070 el.insertAdjacentHTML('afterend', _this10.annotate(Lang._('ERROR'), Lang._('LABELS_NO_FOR_ATTRIBUTE_MESSAGE') + " <hr aria-hidden=\"true\"> " + Lang.sprintf('LABELS_NO_FOR_ATTRIBUTE_MESSAGE_INFO', el.getAttribute('id')), true)); 5071 } 5072 } else { 5073 _this10.errorCount++; 5074 el.classList.add("jooa11y-error-border"); 5075 el.insertAdjacentHTML('afterend', _this10.annotate(Lang._('ERROR'), Lang._('LABELS_MISSING_LABEL_MESSAGE'), true)); 5076 } 5077 }); 5078 } // ============================================================ 5079 // Rulesets: Embedded content. 5080 // ============================================================ 5081 ; 5082 5083 _proto.checkEmbeddedContent = function checkEmbeddedContent() { 5084 var _this11 = this; 5085 5086 var $findiframes = Array.from(this.$root.querySelectorAll("iframe, audio, video")); 5087 var $iframes = $findiframes.filter(function ($el) { 5088 return !_this11.$containerExclusions.includes($el); 5089 }); //Warning: Video content. 5090 5091 var $videos = $iframes.filter(function ($el) { 5092 return $el.matches(_this11.options.videoContent); 5093 }); 5094 $videos.forEach(function ($el) { 5095 var track = $el.getElementsByTagName('TRACK'); 5096 if ($el.tagName === "VIDEO" && track.length) ;else { 5097 _this11.warningCount++; 5098 $el.classList.add("jooa11y-warning-border"); 5099 $el.insertAdjacentHTML('beforebegin', _this11.annotate(Lang._('WARNING'), Lang._('EMBED_VIDEO'))); 5100 } 5101 }); //Warning: Audio content. 5102 5103 var $audio = $iframes.filter(function ($el) { 5104 return $el.matches(_this11.options.audioContent); 5105 }); 5106 $audio.forEach(function ($el) { 5107 _this11.warningCount++; 5108 $el.classList.add("jooa11y-warning-border"); 5109 $el.insertAdjacentHTML('beforebegin', _this11.annotate(Lang._('WARNING'), Lang._('EMBED_AUDIO'))); 5110 }); //Error: iFrame is missing accessible name. 5111 5112 $iframes.forEach(function ($el) { 5113 if ($el.tagName === "VIDEO" || $el.tagName === "AUDIO" || $el.getAttribute("aria-hidden") === "true" || $el.getAttribute("hidden") !== null || $el.style.display === 'none' || $el.getAttribute("role") === "presentation") ;else if ($el.getAttribute("title") === null || $el.getAttribute("title") === '') { 5114 if ($el.getAttribute("aria-label") === null || $el.getAttribute("aria-label") === '') { 5115 if ($el.getAttribute("aria-labelledby") === null) { 5116 //Make sure red error border takes precedence 5117 if ($el.classList.contains("jooa11y-warning-border")) { 5118 $el.classList.remove("jooa11y-warning-border"); 5119 } 5120 5121 _this11.errorCount++; 5122 $el.classList.add("jooa11y-error-border"); 5123 $el.insertAdjacentHTML('beforebegin', _this11.annotate(Lang._('ERROR'), Lang._('EMBED_MISSING_TITLE'))); 5124 } 5125 } 5126 } else ; 5127 }); 5128 var $embeddedcontent = $iframes.filter(function ($el) { 5129 return !$el.matches(_this11.options.embeddedContent); 5130 }); 5131 $embeddedcontent.forEach(function ($el) { 5132 if ($el.tagName === "VIDEO" || $el.tagName === "AUDIO" || $el.getAttribute("aria-hidden") === "true" || $el.getAttribute("hidden") !== null || $el.style.display === 'none' || $el.getAttribute("role") === "presentation" || $el.getAttribute("tabindex") === "-1") ;else { 5133 _this11.warningCount++; 5134 $el.classList.add("jooa11y-warning-border"); 5135 $el.insertAdjacentHTML('beforebegin', _this11.annotate(Lang._('WARNING'), Lang._('EMBED_GENERAL_WARNING'))); 5136 } 5137 }); 5138 } // ============================================================ 5139 // Rulesets: QA 5140 // ============================================================ 5141 ; 5142 5143 _proto.checkQA = function checkQA() { 5144 var _this12 = this; 5145 5146 //Error: Find all links pointing to development environment. 5147 var $findbadDevLinks = this.options.linksToFlag ? Array.from(this.$root.querySelectorAll(this.options.linksToFlag)) : []; 5148 var $badDevLinks = $findbadDevLinks.filter(function ($el) { 5149 return !_this12.$containerExclusions.includes($el); 5150 }); 5151 $badDevLinks.forEach(function ($el) { 5152 _this12.errorCount++; 5153 $el.classList.add("jooa11y-error-text"); 5154 $el.insertAdjacentHTML('afterend', _this12.annotate(Lang._('ERROR'), Lang.sprintf('QA_BAD_LINK', $el.getAttribute('href')), true)); 5155 }); //Warning: Find all PDFs. Although only append warning icon to first PDF on page. 5156 5157 var checkPDF = Array.from(this.$root.querySelectorAll('a[href$=".pdf"]')).filter(function (p) { 5158 return !_this12.$containerExclusions.includes(p); 5159 }); 5160 var firstPDF = checkPDF[0]; 5161 var pdfCount = checkPDF.length; 5162 5163 if (checkPDF.length > 0) { 5164 this.warningCount++; 5165 checkPDF.forEach(function ($pdf) { 5166 $pdf.classList.add('jooa11y-warning-text'); 5167 5168 if ($pdf.querySelector('img')) { 5169 $pdf.classList.remove('jooa11y-warning-text'); 5170 } 5171 }); 5172 firstPDF.insertAdjacentHTML('afterend', this.annotate(Lang._('WARNING'), Lang.sprintf('QA_PDF_COUNT', pdfCount), true)); 5173 } //Warning: Detect uppercase. 5174 5175 5176 var $findallcaps = Array.from(this.$root.querySelectorAll("h1, h2, h3, h4, h5, h6, p, li:not([class^='jooa11y']), blockquote")); 5177 var $allcaps = $findallcaps.filter(function ($el) { 5178 return !_this12.$containerExclusions.includes($el); 5179 }); 5180 $allcaps.forEach(function ($el) { 5181 var uppercasePattern = /(?!<a[^>]*?>)(\b[A-Z][',!:A-Z\s]{15,}|\b[A-Z]{15,}\b)(?![^<]*?<\/a>)/g; 5182 var html = $el.innerHTML; 5183 $el.innerHTML = html.replace(uppercasePattern, "<span class='jooa11y-warning-uppercase'>$1</span>"); 5184 }); 5185 var $warningUppercase = document.querySelectorAll(".jooa11y-warning-uppercase"); 5186 $warningUppercase.forEach(function ($el) { 5187 $el.insertAdjacentHTML('afterend', _this12.annotate(Lang._('WARNING'), Lang._('QA_UPPERCASE_WARNING'), true)); 5188 }); 5189 5190 if ($warningUppercase.length > 0) { 5191 this.warningCount++; 5192 } //Tables check. 5193 5194 5195 var $findtables = Array.from(this.$root.querySelectorAll("table:not([role='presentation'])")); 5196 var $tables = $findtables.filter(function ($el) { 5197 return !_this12.$containerExclusions.includes($el); 5198 }); 5199 $tables.forEach(function ($el) { 5200 var findTHeaders = $el.querySelectorAll("th"); 5201 var findHeadingTags = $el.querySelectorAll("h1, h2, h3, h4, h5, h6"); 5202 5203 if (findTHeaders.length === 0) { 5204 _this12.errorCount++; 5205 $el.classList.add("jooa11y-error-border"); 5206 $el.insertAdjacentHTML('beforebegin', _this12.annotate(Lang._('ERROR'), Lang._('TABLES_MISSING_HEADINGS'))); 5207 } 5208 5209 if (findHeadingTags.length > 0) { 5210 _this12.errorCount++; 5211 findHeadingTags.forEach(function ($el) { 5212 $el.classList.add("jooa11y-error-border"); 5213 $el.insertAdjacentHTML('beforebegin', _this12.annotate(Lang._('ERROR'), Lang._('TABLES_SEMANTIC_HEADING') + " <hr aria-hidden=\"true\"> " + Lang._('TABLES_SEMANTIC_HEADING_INFO'))); 5214 }); 5215 } 5216 5217 findTHeaders.forEach(function ($el) { 5218 if ($el.textContent.trim().length === 0) { 5219 _this12.errorCount++; 5220 $el.classList.add("jooa11y-error-border"); 5221 $el.innerHTML = _this12.annotate(Lang._('ERROR'), Lang._('TABLES_EMPTY_HEADING') + " <hr aria-hidden=\"true\"> " + Lang._('TABLES_EMPTY_HEADING_INFO')); 5222 } 5223 }); 5224 }); //Error: Missing language tag. Lang should be at least 2 characters. 5225 5226 var lang = document.querySelector("html").getAttribute("lang"); 5227 5228 if (!lang || lang.length < 2) { 5229 this.errorCount++; 5230 var jooa11yContainer = document.getElementById("jooa11y-container"); 5231 jooa11yContainer.insertAdjacentHTML('afterend', this.annotateBanner(Lang._('ERROR'), Lang._('QA_PAGE_LANGUAGE_MESSAGE'))); 5232 } //Excessive bolding or italics. 5233 5234 5235 var $findstrongitalics = Array.from(this.$root.querySelectorAll("strong, em")); 5236 var $strongitalics = $findstrongitalics.filter(function ($el) { 5237 return !_this12.$containerExclusions.includes($el); 5238 }); 5239 $strongitalics.forEach(function ($el) { 5240 if ($el.textContent.trim().length > 200) { 5241 _this12.warningCount++; 5242 $el.insertAdjacentHTML('beforebegin', _this12.annotate(Lang._('WARNING'), Lang._('QA_BAD_ITALICS'))); 5243 } 5244 }); //Find blockquotes used as headers. 5245 5246 var $findblockquotes = Array.from(this.$root.querySelectorAll("blockquote")); 5247 var $blockquotes = $findblockquotes.filter(function ($el) { 5248 return !_this12.$containerExclusions.includes($el); 5249 }); 5250 $blockquotes.forEach(function ($el) { 5251 var bqHeadingText = $el.textContent; 5252 5253 if (bqHeadingText.trim().length < 25) { 5254 _this12.warningCount++; 5255 $el.classList.add("jooa11y-warning-border"); 5256 $el.insertAdjacentHTML('beforebegin', _this12.annotate(Lang._('WARNING'), Lang.sprintf('QA_BLOCKQUOTE_MESSAGE', bqHeadingText) + " <hr aria-hidden=\"true\"> " + Lang._('QA_BLOCKQUOTE_MESSAGE_TIP'))); 5257 } 5258 }); // Warning: Detect fake headings. 5259 5260 this.$p.forEach(function ($el) { 5261 var brAfter = $el.innerHTML.indexOf("</strong><br>"); 5262 var brBefore = $el.innerHTML.indexOf("<br></strong>"); //Check paragraphs greater than x characters. 5263 5264 if ($el && $el.textContent.trim().length >= 300) { 5265 var firstChild = $el.firstChild; //If paragraph starts with <strong> tag and ends with <br>. 5266 5267 if (firstChild.tagName === "STRONG" && (brBefore !== -1 || brAfter !== -1)) { 5268 var boldtext = firstChild.textContent; 5269 5270 if (!$el.closest("table") && boldtext.length <= 120) { 5271 firstChild.classList.add("jooa11y-fake-heading", "jooa11y-warning-border"); 5272 $el.insertAdjacentHTML('beforebegin', _this12.annotate(Lang._('WARNING'), Lang.sprintf('QA_FAKE_HEADING', boldtext) + " <hr aria-hidden=\"true\"> " + Lang._('QA_FAKE_HEADING_INFO'))); 5273 } 5274 } 5275 } // If paragraph only contains <p><strong>...</strong></p>. 5276 5277 5278 if (/^<(strong)>.+<\/\1>$/.test($el.innerHTML.trim())) { 5279 //Although only flag if it: 5280 // 1) Has less than 120 characters (typical heading length). 5281 // 2) The previous element is not a heading. 5282 var prevElement = $el.previousElementSibling; 5283 var tagName = ""; 5284 5285 if (prevElement !== null) { 5286 tagName = prevElement.tagName; 5287 } 5288 5289 if (!$el.closest("table") && $el.textContent.length <= 120 && tagName.charAt(0) !== "H") { 5290 var _boldtext = $el.textContent; 5291 $el.classList.add("jooa11y-fake-heading", "jooa11y-warning-border"); 5292 $el.firstChild.insertAdjacentHTML("afterend", _this12.annotate(Lang._('WARNING'), Lang.sprintf('QA_FAKE_HEADING', _boldtext) + " <hr aria-hidden=\"true\"> " + Lang._('QA_FAKE_HEADING_INFO'))); 5293 } 5294 } 5295 }); 5296 5297 if (this.$root.querySelectorAll(".jooa11y-fake-heading").length > 0) { 5298 this.warningCount++; 5299 } // Check duplicate ID 5300 5301 5302 var ids = this.$root.querySelectorAll('[id]'); 5303 var allIds = {}; 5304 ids.forEach(function ($el) { 5305 var id = $el.id; 5306 5307 if (id) { 5308 if (allIds[id] === undefined) { 5309 allIds[id] = 1; 5310 } else { 5311 $el.classList.add("sa11y-error-border"); 5312 $el.insertAdjacentHTML('beforebegin', _this12.annotate(Lang._('WARNING'), Lang._('QA_DUPLICATE_ID') + "\n\t\t\t\t\t\t\t\t<hr aria-hidden=\"true\">\n\t\t\t\t\t\t\t\t" + Lang.sprintf('QA_DUPLICATE_ID_TIP', id), true)); 5313 } 5314 } 5315 }); 5316 /* Thanks to John Jameson from PrincetonU for this ruleset! */ 5317 // Detect paragraphs that should be lists. 5318 5319 var activeMatch = ""; 5320 var prefixDecrement = { 5321 b: "a", 5322 B: "A", 5323 2: "1" 5324 }; 5325 var prefixMatch = /a\.|a\)|A\.|A\)|1\.|1\)|\*\s|-\s|--|•\s|→\s|✓\s|✔\s|✗\s|✖\s|✘\s|❯\s|›\s|»\s/; 5326 5327 var decrement = function decrement(el) { 5328 return el.replace(/^b|^B|^2/, function (match) { 5329 return prefixDecrement[match]; 5330 }); 5331 }; 5332 5333 this.$p.forEach(function (el) { 5334 var hit = false; // Grab first two characters. 5335 5336 var firstPrefix = el.textContent.substring(0, 2); 5337 5338 if (firstPrefix.trim().length > 0 && firstPrefix !== activeMatch && firstPrefix.match(prefixMatch)) { 5339 // We have a prefix and a possible hit 5340 // Split p by carriage return if present and compare. 5341 var hasBreak = el.innerHTML.indexOf("<br>"); 5342 5343 if (hasBreak !== -1) { 5344 var subParagraph = el.innerHTML.substring(hasBreak + 4).trim(); 5345 var subPrefix = subParagraph.substring(0, 2); 5346 5347 if (firstPrefix === decrement(subPrefix)) { 5348 hit = true; 5349 } 5350 } // Decrement the second p prefix and compare . 5351 5352 5353 if (!hit) { 5354 var $second = el.nextElementSibling.nodeName === 'P' ? el.nextElementSibling : null; 5355 5356 if ($second) { 5357 var secondPrefix = decrement(el.nextElementSibling.textContent.substring(0, 2)); 5358 5359 if (firstPrefix === secondPrefix) { 5360 hit = true; 5361 } 5362 } 5363 } 5364 5365 if (hit) { 5366 _this12.warningCount++; 5367 el.insertAdjacentHTML('beforebegin', _this12.annotate(Lang._('WARNING'), Lang.sprintf('QA_SHOULD_BE_LIST', firstPrefix) + " <hr aria-hidden=\"true\"> " + Lang._('QA_SHOULD_BE_LIST_TIP'))); 5368 el.classList.add("jooa11y-fake-list"); 5369 activeMatch = firstPrefix; 5370 } else { 5371 activeMatch = ""; 5372 } 5373 } else { 5374 activeMatch = ""; 5375 } 5376 }); 5377 5378 if (this.$root.querySelectorAll('.jooa11y-fake-list').length > 0) { 5379 this.warningCount++; 5380 } 5381 } // ============================================================ 5382 // Rulesets: Contrast 5383 // Color contrast plugin by jasonday: https://github.com/jasonday/color-contrast 5384 // ============================================================ 5385 ; 5386 5387 _proto.checkContrast = function checkContrast() { 5388 var _this13 = this; 5389 5390 var $findcontrast = Array.from(this.$root.querySelectorAll("* > :not(.jooa11y-heading-label)")); 5391 var $contrast = $findcontrast.filter(function ($el) { 5392 return !_this13.$containerExclusions.includes($el); 5393 }); 5394 var contrastErrors = { 5395 errors: [], 5396 warnings: [] 5397 }; 5398 var elements = $contrast; 5399 var contrast = { 5400 // Parse rgb(r, g, b) and rgba(r, g, b, a) strings into an array. 5401 // Adapted from https://github.com/gka/chroma.js 5402 parseRgb: function parseRgb(css) { 5403 var i, m, rgb, _i, _j; 5404 5405 if (m = css.match(/rgb\(\s*(\-?\d+),\s*(\-?\d+)\s*,\s*(\-?\d+)\s*\)/)) { 5406 rgb = m.slice(1, 4); 5407 5408 for (i = _i = 0; _i <= 2; i = ++_i) { 5409 rgb[i] = +rgb[i]; 5410 } 5411 5412 rgb[3] = 1; 5413 } else if (m = css.match(/rgba\(\s*(\-?\d+),\s*(\-?\d+)\s*,\s*(\-?\d+)\s*,\s*([01]|[01]?\.\d+)\)/)) { 5414 rgb = m.slice(1, 5); 5415 5416 for (i = _j = 0; _j <= 3; i = ++_j) { 5417 rgb[i] = +rgb[i]; 5418 } 5419 } 5420 5421 return rgb; 5422 }, 5423 // Based on http://www.w3.org/TR/WCAG20/#relativeluminancedef 5424 relativeLuminance: function relativeLuminance(c) { 5425 var lum = []; 5426 5427 for (var i = 0; i < 3; i++) { 5428 var v = c[i] / 255; 5429 lum.push(v < 0.03928 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4)); 5430 } 5431 5432 return 0.2126 * lum[0] + 0.7152 * lum[1] + 0.0722 * lum[2]; 5433 }, 5434 // Based on http://www.w3.org/TR/WCAG20/#contrast-ratiodef 5435 contrastRatio: function contrastRatio(x, y) { 5436 var l1 = contrast.relativeLuminance(contrast.parseRgb(x)); 5437 var l2 = contrast.relativeLuminance(contrast.parseRgb(y)); 5438 return (Math.max(l1, l2) + 0.05) / (Math.min(l1, l2) + 0.05); 5439 }, 5440 getBackground: function getBackground(el) { 5441 var styles = getComputedStyle(el), 5442 bgColor = styles.backgroundColor, 5443 bgImage = styles.backgroundImage, 5444 rgb = contrast.parseRgb(bgColor) + '', 5445 alpha = rgb.split(','); // if background has alpha transparency, flag manual check 5446 5447 if (alpha[3] < 1 && alpha[3] > 0) { 5448 return "alpha"; 5449 } // if element has no background image, or transparent background (alpha == 0) return bgColor 5450 5451 5452 if (bgColor !== 'rgba(0, 0, 0, 0)' && bgColor !== 'transparent' && bgImage === "none" && alpha[3] !== '0') { 5453 return bgColor; 5454 } else if (bgImage !== "none") { 5455 return "image"; 5456 } // retest if not returned above 5457 5458 5459 if (el.tagName === 'HTML') { 5460 return 'rgb(255, 255, 255)'; 5461 } else { 5462 return contrast.getBackground(el.parentNode); 5463 } 5464 }, 5465 // check visibility - based on jQuery method 5466 // isVisible: function (el) { 5467 // return !!(el.offsetWidth || el.offsetHeight || el.getClientRects().length); 5468 // }, 5469 check: function check() { 5470 // resets results 5471 contrastErrors = { 5472 errors: [], 5473 warnings: [] 5474 }; 5475 5476 for (var i = 0; i < elements.length; i++) { 5477 (function (elem) { 5478 // Test if visible. Although we want invisible too. 5479 if (contrast 5480 /* .isVisible(elem) */ 5481 ) { 5482 var style = getComputedStyle(elem), 5483 color = style.color, 5484 fill = style.fill, 5485 fontSize = parseInt(style.fontSize), 5486 pointSize = fontSize * 3 / 4, 5487 fontWeight = style.fontWeight, 5488 htmlTag = elem.tagName, 5489 background = contrast.getBackground(elem), 5490 textString = [].reduce.call(elem.childNodes, function (a, b) { 5491 return a + (b.nodeType === 3 ? b.textContent : ''); 5492 }, ''), 5493 text = textString.trim(), 5494 ratio, 5495 error, 5496 warning; 5497 5498 if (htmlTag === "SVG") { 5499 ratio = Math.round(contrast.contrastRatio(fill, background) * 100) / 100; 5500 5501 if (ratio < 3) { 5502 error = { 5503 elem: elem, 5504 ratio: ratio + ':1' 5505 }; 5506 contrastErrors.errors.push(error); 5507 } 5508 } else if (text.length || htmlTag === "INPUT" || htmlTag === "SELECT" || htmlTag === "TEXTAREA") { 5509 // does element have a background image - needs to be manually reviewed 5510 if (background === "image") { 5511 warning = { 5512 elem: elem 5513 }; 5514 contrastErrors.warnings.push(warning); 5515 } else if (background === "alpha") { 5516 warning = { 5517 elem: elem 5518 }; 5519 contrastErrors.warnings.push(warning); 5520 } else { 5521 ratio = Math.round(contrast.contrastRatio(color, background) * 100) / 100; 5522 5523 if (pointSize >= 18 || pointSize >= 14 && fontWeight >= 700) { 5524 if (ratio < 3) { 5525 error = { 5526 elem: elem, 5527 ratio: ratio + ':1' 5528 }; 5529 contrastErrors.errors.push(error); 5530 } 5531 } else { 5532 if (ratio < 4.5) { 5533 error = { 5534 elem: elem, 5535 ratio: ratio + ':1' 5536 }; 5537 contrastErrors.errors.push(error); 5538 } 5539 } 5540 } 5541 } 5542 } 5543 })(elements[i]); 5544 } 5545 5546 return contrastErrors; 5547 } 5548 }; 5549 contrast.check(); //const {errorMessage, warningMessage} = jooa11yIM["contrast"]; 5550 5551 contrastErrors.errors.forEach(function (item) { 5552 var name = item.elem; 5553 var cratio = item.ratio; 5554 var clone = name.cloneNode(true); 5555 var removeJooa11yHeadingLabel = clone.querySelectorAll('.jooa11y-heading-label'); 5556 5557 for (var i = 0; i < removeJooa11yHeadingLabel.length; i++) { 5558 clone.removeChild(removeJooa11yHeadingLabel[i]); 5559 } 5560 5561 var nodetext = clone.textContent; 5562 _this13.errorCount++; 5563 5564 if (name.tagName === "INPUT") { 5565 name.insertAdjacentHTML('beforebegin', _this13.annotate(Lang._('ERROR'), Lang._('CONTRAST_ERROR_INPUT_MESSAGE') + "\n <hr aria-hidden=\"true\">\n " + Lang.sprintf('CONTRAST_ERROR_INPUT_MESSAGE_INFO', cratio), true)); 5566 } else { 5567 name.insertAdjacentHTML('beforebegin', _this13.annotate(Lang._('ERROR'), Lang.sprintf('CONTRAST_ERROR_MESSAGE', cratio, nodetext) + "\n <hr aria-hidden=\"true\">\n " + Lang.sprintf('CONTRAST_ERROR_MESSAGE_INFO', cratio, nodetext), true)); 5568 } 5569 }); 5570 contrastErrors.warnings.forEach(function (item) { 5571 var name = item.elem; 5572 var clone = name.cloneNode(true); 5573 var removeJooa11yHeadingLabel = clone.querySelectorAll('.jooa11y-heading-label'); 5574 5575 for (var i = 0; i < removeJooa11yHeadingLabel.length; i++) { 5576 clone.removeChild(removeJooa11yHeadingLabel[i]); 5577 } 5578 5579 var nodetext = clone.textContent; 5580 _this13.warningCount++; 5581 name.insertAdjacentHTML('beforebegin', _this13.annotate(Lang._('WARNING'), Lang._('CONTRAST_WARNING_MESSAGE') + " <hr aria-hidden=\"true\"> " + Lang.sprintf('CONTRAST_WARNING_MESSAGE_INFO', nodetext), true)); 5582 }); 5583 } // ============================================================ 5584 // Rulesets: Readability 5585 // Adapted from Greg Kraus' readability script: https://accessibility.oit.ncsu.edu/it-accessibility-at-nc-state/developers/tools/readability-bookmarklet/ 5586 // ============================================================ 5587 ; 5588 5589 _proto.checkReadability = function checkReadability() { 5590 var _this14 = this; 5591 5592 var container = document.querySelector(this.options.readabilityRoot); 5593 var $findreadability = Array.from(container.querySelectorAll("p, li")); 5594 var $readability = $findreadability.filter(function ($el) { 5595 return !_this14.$containerExclusions.includes($el); 5596 }); //Crude hack to add a period to the end of list items to make a complete sentence. 5597 5598 $readability.forEach(function ($el) { 5599 var listText = $el.textContent; 5600 5601 if (listText.length >= 120) { 5602 if (listText.charAt(listText.length - 1) !== ".") { 5603 $el.insertAdjacentHTML("beforeend", "<span class='jooa11y-readability-period jooa11y-visually-hidden'>.</span>"); 5604 } 5605 } 5606 }); // Compute syllables: http://stackoverflow.com/questions/5686483/how-to-compute-number-of-syllables-in-a-word-in-javascript 5607 5608 function number_of_syllables(wordCheck) { 5609 wordCheck = wordCheck.toLowerCase().replace('.', '').replace('\n', ''); 5610 5611 if (wordCheck.length <= 3) { 5612 return 1; 5613 } 5614 5615 wordCheck = wordCheck.replace(/(?:[^laeiouy]es|ed|[^laeiouy]e)$/, ''); 5616 wordCheck = wordCheck.replace(/^y/, ''); 5617 var syllable_string = wordCheck.match(/[aeiouy]{1,2}/g); 5618 var syllables = 0; 5619 5620 if (!!syllable_string) { 5621 syllables = syllable_string.length; 5622 } 5623 5624 return syllables; 5625 } 5626 5627 var readabilityarray = []; 5628 5629 for (var i = 0; i < $readability.length; i++) { 5630 var current = $readability[i]; 5631 5632 if (current.textContent.replace(/ |\n/g, '') !== '') { 5633 readabilityarray.push(current.textContent); 5634 } 5635 } 5636 5637 var paragraphtext = readabilityarray.join(' ').trim().toString(); 5638 var words_raw = paragraphtext.replace(/[.!?-]+/g, ' ').split(' '); 5639 var words = 0; 5640 5641 for (var _i5 = 0; _i5 < words_raw.length; _i5++) { 5642 if (words_raw[_i5] != 0) { 5643 words = words + 1; 5644 } 5645 } 5646 5647 var sentences_raw = paragraphtext.split(/[.!?]+/); 5648 var sentences = 0; 5649 5650 for (var _i6 = 0; _i6 < sentences_raw.length; _i6++) { 5651 if (sentences_raw[_i6] !== '') { 5652 sentences = sentences + 1; 5653 } 5654 } 5655 5656 var total_syllables = 0; 5657 var syllables1 = 0; 5658 var syllables2 = 0; 5659 5660 for (var _i7 = 0; _i7 < words_raw.length; _i7++) { 5661 if (words_raw[_i7] != 0) { 5662 var syllable_count = number_of_syllables(words_raw[_i7]); 5663 5664 if (syllable_count === 1) { 5665 syllables1 = syllables1 + 1; 5666 } 5667 5668 if (syllable_count === 2) { 5669 syllables2 = syllables2 + 1; 5670 } 5671 5672 total_syllables = total_syllables + syllable_count; 5673 } 5674 } //var characters = paragraphtext.replace(/[.!?|\s]+/g, '').length; 5675 //Reference: https://core.ac.uk/download/pdf/6552422.pdf 5676 //Reference: https://github.com/Yoast/YoastSEO.js/issues/267 5677 5678 5679 var flesch_reading_ease; 5680 5681 if (this.options.readabilityLang === 'en') { 5682 flesch_reading_ease = 206.835 - 1.015 * words / sentences - 84.6 * total_syllables / words; 5683 } else if (this.options.readabilityLang === 'fr') { 5684 //French (Kandel & Moles) 5685 flesch_reading_ease = 207 - 1.015 * words / sentences - 73.6 * total_syllables / words; 5686 } else if (this.options.readabilityLang === 'es') { 5687 flesch_reading_ease = 206.84 - 1.02 * words / sentences - 0.60 * (100 * total_syllables / words); 5688 } 5689 5690 if (flesch_reading_ease > 100) { 5691 flesch_reading_ease = 100; 5692 } else if (flesch_reading_ease < 0) { 5693 flesch_reading_ease = 0; 5694 } 5695 5696 var $readabilityinfo = document.getElementById("jooa11y-readability-info"); 5697 5698 if (paragraphtext.length === 0) { 5699 $readabilityinfo.innerHTML = Lang._('READABILITY_NO_P_OR_LI_MESSAGE'); 5700 } else if (words > 30) { 5701 var fleschScore = flesch_reading_ease.toFixed(1); 5702 var avgWordsPerSentence = (words / sentences).toFixed(1); 5703 var complexWords = Math.round(100 * ((words - (syllables1 + syllables2)) / words)); //WCAG AAA pass if greater than 60 5704 5705 if (fleschScore >= 0 && fleschScore < 30) { 5706 $readabilityinfo.innerHTML = "<span>" + fleschScore + "</span> <span class=\"jooa11y-readability-score\">" + Lang._('VERY_DIFFICULT_READABILITY') + "</span>"; 5707 } else if (fleschScore > 31 && fleschScore < 49) { 5708 $readabilityinfo.innerHTML = "<span>" + fleschScore + "</span> <span class=\"jooa11y-readability-score\">" + Lang._('DIFFICULT_READABILITY') + "</span>"; 5709 } else if (fleschScore > 50 && fleschScore < 60) { 5710 $readabilityinfo.innerHTML = "<span>" + fleschScore + "</span> <span class=\"jooa11y-readability-score\">" + Lang._('FAIRLY_DIFFICULT_READABILITY') + "</span>"; 5711 } else { 5712 $readabilityinfo.innerHTML = "<span>" + fleschScore + "</span> <span class=\"jooa11y-readability-score\">" + Lang._('GOOD_READABILITY') + "</span>"; 5713 } 5714 5715 document.getElementById("jooa11y-readability-details").innerHTML = "<li><span class='jooa11y-bold'>" + Lang._('AVG_WORD_PER_SENTENCE') + "</span> " + avgWordsPerSentence + "</li>\n <li><span class='jooa11y-bold'>" + Lang._('COMPLEX_WORDS') + "</span> " + complexWords + "%</li>\n <li><span class='jooa11y-bold'>" + Lang._('TOTAL_WORDS') + "</span> " + words + "</li>"; 5716 } else { 5717 $readabilityinfo.textContent = Lang._('READABILITY_NOT_ENOUGH_CONTENT_MESSAGE'); 5718 } 5719 } //---------------------------------------------------------------------- 5720 // Templating for Error, Warning and Pass buttons. 5721 //---------------------------------------------------------------------- 5722 ; 5723 5724 _proto.annotate = function annotate(type, content, inline) { 5725 var _CSSName; 5726 5727 if (inline === void 0) { 5728 inline = false; 5729 } 5730 5731 var validTypes = [Lang._('ERROR'), Lang._('WARNING'), Lang._('GOOD')]; 5732 5733 if (validTypes.indexOf(type) === -1) { 5734 throw Error("Invalid type [" + type + "] for annotation"); 5735 } 5736 5737 var CSSName = (_CSSName = {}, _CSSName[validTypes[0]] = "error", _CSSName[validTypes[1]] = "warning", _CSSName[validTypes[2]] = "good", _CSSName); // Check if content is a function 5738 5739 if (content && {}.toString.call(content) === "[object Function]") { 5740 // if it is, call it and get the value. 5741 content = content(); 5742 } // Escape content, it is need because it used inside data-tippy-content="" 5743 5744 5745 content = escapeHTML(content); 5746 return "\n <div class=" + (inline ? "jooa11y-instance-inline" : "jooa11y-instance") + ">\n <button\n type=\"button\"\n aria-label=\"" + [type] + "\"\n class=\"jooa11y-btn jooa11y-" + CSSName[type] + "-btn" + (inline ? "-text" : "") + "\"\n data-tippy-content=\"<div lang='" + this.options.langCode + "'>\n <div class='jooa11y-header-text'>" + [type] + "</div>\n " + content + "\n </div>\n \">\n </button>\n </div>"; 5747 } //---------------------------------------------------------------------- 5748 // Templating for full-width banners. 5749 //---------------------------------------------------------------------- 5750 ; 5751 5752 _proto.annotateBanner = function annotateBanner(type, content) { 5753 var _CSSName2; 5754 5755 var validTypes = [Lang._('ERROR'), Lang._('WARNING'), Lang._('GOOD')]; 5756 5757 if (validTypes.indexOf(type) === -1) { 5758 throw Error("Invalid type [" + type + "] for annotation"); 5759 } 5760 5761 var CSSName = (_CSSName2 = {}, _CSSName2[validTypes[0]] = "error", _CSSName2[validTypes[1]] = "warning", _CSSName2[validTypes[2]] = "good", _CSSName2); // Check if content is a function 5762 5763 if (content && {}.toString.call(content) === "[object Function]") { 5764 // if it is, call it and get the value. 5765 content = content(); 5766 } 5767 5768 return "<div class=\"jooa11y-instance jooa11y-" + CSSName[type] + "-message-container\">\n <div role=\"region\" aria-label=\"" + [type] + "\" class=\"jooa11y-" + CSSName[type] + "-message\" lang=\"" + this.options.langCode + "\">\n " + content + "\n </div>\n </div>"; 5769 }; 5770 5771 return Jooa11y; 5772 }(); 5773 5774 if (!Joomla) { 5775 throw new Error('Joomla API is not properly initialised'); 5776 } 5777 5778 var stringPrefix = 'PLG_SYSTEM_JOOA11Y_'; 5779 5780 Lang.translate = function (string) { 5781 return Joomla.Text._(stringPrefix + string, string); 5782 }; 5783 5784 var options = Joomla.getOptions('jooa11yOptions'); 5785 window.addEventListener('load', function () { 5786 // Instantiate 5787 var checker = new Jooa11y(options); 5788 checker.doInitialCheck(); 5789 }); 5790 5791 })();
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Wed Sep 7 05:41:13 2022 | Chilli.vc Blog - For Webmaster,Blog-Writer,System Admin and Domainer |