PageSourceSearch

https://annetiits.eu/haridustehnoloog/wp-content/plugins/h5p/h5p-php-library/js/h5p-tooltip.js?ver=1.16.2

js annetiits.eu collected 2026-10-08 14:59:19 UTC 7,149 bytes, 217 lines download raw bytes

1/*global H5P*/
2H5P.Tooltip = (function () {
3  'use strict';
4
5  /**
6   * Create an accessible tooltip
7   *
8   * @param {HTMLElement} triggeringElement The element that should trigger the tooltip
9   * @param {Object} options Options for tooltip
10   * @param {String} options.text The text to be displayed in the tooltip
11   *  If not set, will attempt to set text = aria-label of triggeringElement
12   * @param {String[]} options.classes Extra css classes for the tooltip
13   * @param {Boolean} options.ariaHidden Whether the hover should be read by screen readers or not (default: true)
14   * @param {String} options.position Where the tooltip should appear in relation to the
15   *  triggeringElement. Accepted positions are "top" (default), "left", "right" and "bottom"
16   *
17   * @constructor
18   */
19  function Tooltip(triggeringElement, options) {
20
21    // Make sure tooltips have unique id
22    H5P.Tooltip.uniqueId += 1;
23    const tooltipId = 'h5p-tooltip-' + H5P.Tooltip.uniqueId;
24
25    // Default options
26    options = options || {};
27    options.classes = options.classes || [];
28    options.ariaHidden = options.ariaHidden || true;
29
30    // Initiate state
31    let hover = false;
32    let focus = false;
33
34    // Function used by the escape listener
35    const escapeFunction = function (e) {
36      if (e.key === 'Escape') {
37        tooltip.classList.remove('h5p-tooltip-visible');
38      }
39    }
40
41    // Create element
42    const tooltip = document.createElement('div');
43
44    tooltip.classList.add('h5p-tooltip');
45    tooltip.id = tooltipId;
46    tooltip.role = 'tooltip';
47    tooltip.innerHTML = options.text || triggeringElement.getAttribute('aria-label') || '';
48    tooltip.setAttribute('aria-hidden', options.ariaHidden);
49    tooltip.classList.add(...options.classes);
50
51    triggeringElement.appendChild(tooltip);
52
53    // Set the initial position based on options.position
54    switch (options.position) {
55      case 'left':
56        tooltip.classList.add('h5p-tooltip-left');
57        break;
58      case 'right':
59        tooltip.classList.add('h5p-tooltip-right');
60        break;
61      case 'bottom':
62        tooltip.classList.add('h5p-tooltip-bottom');
63        break;
64      default:
65        options.position = 'top';
66    }
67
68    // Aria-describedby will override aria-hidden
69    if (!options.ariaHidden) {
70      triggeringElement.setAttribute('aria-describedby', tooltipId);
71    }
72
73    // Add event listeners to triggeringElement
74    triggeringElement.addEventListener('mouseenter', function () {
75      showTooltip(true);
76    });
77    triggeringElement.addEventListener('mouseleave', function () {
78      hideTooltip(true);
79    });
80    triggeringElement.addEventListener('focusin', function () {
81      showTooltip(false);
82    });
83    triggeringElement.addEventListener('focusout', function () {
84      hideTooltip(false);
85    });
86
87    // Prevent clicks on the tooltip from triggering onClick listeners on the triggeringElement
88    tooltip.addEventListener('click', function (event) {
89      event.stopPropagation();
90    });
91
92    // Use a mutation observer to listen for aria-label being
93    // changed for the triggering element. If so, update the tooltip.
94    // Mutation observer will be used even if the original elements
95    // doesn't have any aria-label.
96    new MutationObserver(function (mutations) {
97      const ariaLabel = mutations[0].target.getAttribute('aria-label');
98      if (ariaLabel) {
99        tooltip.innerHTML = options.text || ariaLabel;
100      }
101    }).observe(triggeringElement, {
102      attributes: true,
103      attributeFilter: ['aria-label'],
104    });
105
106    // Use intersection observer to adjust the tooltip if it is not completely visible
107    new IntersectionObserver(function (entries) {
108      entries.forEach((entry) => {
109        const target = entry.target;
110        const positionClass = 'h5p-tooltip-' + options.position;
111
112        // Stop adjusting when hidden (to prevent a false positive next time)
113        if (entry.intersectionRatio === 0) {
114          ['h5p-tooltip-down', 'h5p-tooltip-left', 'h5p-tooltip-right']
115            .forEach(function (adjustmentClass) {
116              if (adjustmentClass !== positionClass) {
117                target.classList.remove(adjustmentClass);
118              }
119            });
120        }        
121        // Adjust if not completely visible when meant to be
122        else if (entry.intersectionRatio < 1 && (hover || focus)) {
123          const targetRect = entry.boundingClientRect;
124          const intersectionRect = entry.intersectionRect;
125
126          // Going out of screen on left side
127          if (intersectionRect.left > targetRect.left) {
128            target.classList.add('h5p-tooltip-right');
129            target.classList.remove(positionClass);
130          }
131          // Going out of screen on right side
132          else if (intersectionRect.right < targetRect.right) {
133            target.classList.add('h5p-tooltip-left');
134            target.classList.remove(positionClass);
135          }
136
137          // going out of top of screen
138          if (intersectionRect.top > targetRect.top) {
139            target.classList.add('h5p-tooltip-down');
140            target.classList.remove(positionClass);
141          }
142          // going out of bottom of screen
143          else if (intersectionRect.bottom < targetRect.bottom) {
144            target.classList.add('h5p-tooltip-up');
145            target.classList.remove(positionClass);
146          }
147        }
148      });
149    }).observe(tooltip);
150
151    /**
152     * Makes the tooltip visible and activates it's functionality
153     *
154     * @param {Boolean} triggeredByHover True if triggered by mouse, false if triggered by focu
154s
155     */
156    const showTooltip = function (triggeredByHover) {
157      if (triggeredByHover) {
158        hover = true;
159      }
160      else {
161        focus = true;
162      }
163
164      tooltip.classList.add('h5p-tooltip-visible');
165
166      // Add listener to iframe body, as esc keypress would not be detected otherwise
167      document.body.addEventListener('keydown', escapeFunction, true);
168    }
169
170    /**
171     * Hides the tooltip and removes listeners
172     *
173     * @param {Boolean} triggeredByHover True if triggered by mouse, false if triggered by focus
174     */
175     const hideTooltip = function (triggeredByHover) {
176      if (triggeredByHover) {
177        hover = false;
178      }
179      else {
180        focus = false;
181      }
182
183      // Only hide tooltip if neither hovered nor focused
184      if (!hover && !focus) {
185        tooltip.classList.remove('h5p-tooltip-visible');
186
187        // Remove iframe body listener
188        document.body.removeEventListener('keydown', escapeFunction, true);
189      }
190    }
191
192    /**
193     * Change the text displayed by the tooltip
194     *
195     * @param {String} text The new text to be displayed
196     *  Set to null to use aria-label of triggeringElement instead
197     */
198    this.setText = function (text) {
199      options.text = text;
200      tooltip.innerHTML = options.text || triggeringElement.getAttribute('aria-label') || '';
201    };
202
203    /**
204     * Retrieve tooltip
205     *
206     * @return {HTMLElement}
207     */
208    this.getElement = function () {
209      return tooltip;
210    };
211  }
212
213  return Tooltip;
214
215})();
216
217H5P.Tooltip.uniqueId = -1;

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.