Plugin Directory

Changeset 3248595


Ignore:
Timestamp:
02/28/2025 04:11:27 PM (12 months ago)
Author:
stevejonesdev
Message:

release v1.1.0

Location:
accessibility-new-window-warnings
Files:
8 edited
1 copied

Legend:

Unmodified
Added
Removed
  • accessibility-new-window-warnings/tags/1.1.0/README.txt

    r3174459 r3248595  
    33Tags: accessibility, accessible, wcag, ada, a11y, section 508, links, open new window, open new tab
    44Requires at least: 6.4.0
    5 Tested up to: 6.6.2
    6 Stable tag: 1.0.10
     5Tested up to: 6.7.2
     6Stable tag: 1.1.0
    77License: GPLv2 or later
    88License URI: http://www.gnu.org/licenses/gpl-2.0.html
     
    7575== Changelog ==
    7676
     77= 1.1.0 =
     78* Fixed - tooltips can now be dismissed using the Escape key, ensuring better accessibility
     79* Fixed - tooltip content is now hoverable, preventing accidental dismissal
     80* Refactored - code for better performance and maintainability using modern JavaScript practices
     81
    7782= 1.0.10 =
    7883* Added - detection and processing of window.open() links with consistent tooltip/ARIA label handling for both target="_blank" and window.open()
  • accessibility-new-window-warnings/tags/1.1.0/accessibility-new-window-warnings.php

    r3174459 r3248595  
    44 * Plugin URI:  https://a11ychecker.com
    55 * Description: Make links that open in a new window accessible by adding a warning.
    6  * Version:     1.0.10
     6 * Version:     1.1.0
    77 * Author:      Equalize Digital
    88 * Author URI:  https://equalizedigital.com
     
    1414 */
    1515
    16 define( 'ANWW_VERSION', '1.0.10' );
     16define( 'ANWW_VERSION', '1.1.0' );
    1717define( 'ANWW_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
    1818define( 'ANWW_PLUGIN_URL', plugin_dir_url( __FILE__ ) );
  • accessibility-new-window-warnings/tags/1.1.0/assets/js/accessibility-new-window-warnings-min.js

    r3174459 r3248595  
    1 !function($){"use strict";var n;function e(){var e="";$(".anww-external-link-icon").remove(),$("a").each((function(){var t=$(this).attr("onclick");if("_blank"===$(this).attr("target")&&(o($(this)),l($(this)),d($(this)),!0),t&&t.includes("window.open")){var a=t.match(/window\.open\([^,]+,\s*['"]([^'"]+)['"]/),i=a?a[1]:"";"_blank"!==i&&""!==i||(o($(this)),l($(this)),d($(this)),!0)}function o(n){$(":header",n).length?$(":header",n).append('<i class="anww-external-link-icon" aria-hidden="true"></i>'):n.append('<i class="anww-external-link-icon" aria-hidden="true"></i>')}function l(n){n.attr("aria-label")?e=n.attr("aria-label"):$("img",n).length?e=n.find("img").attr("alt"):n.text()&&(e=n.text()),e?(e=e.trimEnd(),e+=", "+anww_localized.opens_a_new_window):e+=anww_localized.opens_a_new_window,n.attr("aria-label",e)}function d(e){e.mousemove((function(e){n.css({top:e.pageY+10+"px",left:e.pageX+10+"px"})})).hover((function(){n.show().html(anww_localized.opens_a_new_window)}),(function(){n.hide()})),e.on({focusin:function(){var t=e.offset();n.css({top:t.top+e.outerHeight()+"px",left:t.left+"px"}),n.show().html(anww_localized.opens_a_new_window)},focusout:function(){n.hide()}})}}))}$(window).on("load",(function(){n=$("<div/>").css({position:"absolute",background:"white",color:"#1e1e1e",fontSize:"16px",border:"1px solid black",padding:"5px 10px",zIndex:9999,display:"none"}).addClass("anww-tooltip").appendTo("body"),e()})),$(document).on("facetwp-loaded",e)}(jQuery);
     1!function(){let e,t;const n=()=>{document.querySelectorAll(".anww-external-link-icon").forEach((e=>e.remove())),document.querySelectorAll("a").forEach((e=>{let t=!1;const n=e.getAttribute("onclick");if("_blank"===e.getAttribute("target")&&(o(e),i(e),a(e),t=!0),n&&n.includes("window.open")){const d=n.match(/window\.open\([^,]+,\s*['"]([^'"]+)['"]/),l=d?d[1]:"";"_blank"!==l&&""!==l||(o(e),i(e),a(e),t=!0)}}))},o=e=>{const t=document.createElement("i");t.classList.add("anww-external-link-icon"),t.setAttribute("aria-hidden","true"),e.appendChild(t)},i=e=>{let t="";e.hasAttribute("aria-label")?t=e.getAttribute("aria-label"):e.querySelector("img")?t=e.querySelector("img").getAttribute("alt"):e.textContent&&(t=e.textContent.trim()),t=t?`${t}, ${anww_localized.opens_a_new_window}`:anww_localized.opens_a_new_window,e.setAttribute("aria-label",t)},a=e=>{e.addEventListener("mouseenter",(t=>{d(e,t.pageX,t.pageY)})),e.addEventListener("focusin",(t=>{const n=e.getBoundingClientRect();d(e,n.left+window.scrollX,n.top+n.height+window.scrollY)})),e.addEventListener("mouseleave",l),e.addEventListener("focusout",l)},d=(n,o,i)=>{clearTimeout(t),e.textContent=anww_localized.opens_a_new_window,e.style.display="block";const a=e.offsetWidth,d=e.offsetHeight;o+a+10>window.innerWidth&&(o-=a+20),i+d+10>window.innerHeight+window.scrollY&&(i-=d+20),e.style.top=`${i+10}px`,e.style.left=`${o+10}px`},l=()=>{t=setTimeout(s,300)},s=()=>{e.style.display="none"};window.addEventListener("load",(()=>{e=document.createElement("div"),e.setAttribute("role","tooltip"),e.classList.add("anww-tooltip"),Object.assign(e.style,{position:"absolute",background:"white",color:"#1e1e1e",fontSize:"16px",border:"1px solid black",padding:"5px 10px",zIndex:9999,display:"none",pointerEvents:"auto",boxShadow:"0px 4px 6px rgba(0,0,0,0.1)",maxWidth:"200px",whiteSpace:"nowrap"}),document.body.appendChild(e),document.addEventListener("click",(e=>{e.target.closest(".anww-tooltip, a[target='_blank']")||s()})),document.addEventListener("keydown",(e=>{"Escape"===e.key&&s()})),e.addEventListener("mouseenter",(()=>{clearTimeout(t)})),e.addEventListener("mouseleave",(()=>{s()})),n()})),document.addEventListener("facetwp-loaded",n)}();
  • accessibility-new-window-warnings/tags/1.1.0/assets/js/accessibility-new-window-warnings.js

    r3174459 r3248595  
    1 (function ($) {
    2     "use strict";
     1/* global anww_localized */
    32
    4     var anww_link_tooltip;
     3(function () {
    54
    6     function initializeTooltip() {
    7         anww_link_tooltip = $('<div/>').css({
    8             position: 'absolute',
    9             background: 'white',
    10             color: '#1e1e1e',
    11             fontSize: '16px',
    12             border: '1px solid black',
    13             padding: '5px 10px',
     5    let anwwLinkTooltip;
     6    let tooltipTimeout;
     7
     8    /**
     9     * Initializes the tooltip element and event listeners.
     10     */
     11    const initializeTooltip = () => {
     12        anwwLinkTooltip = document.createElement("div");
     13        anwwLinkTooltip.setAttribute("role", "tooltip");
     14        anwwLinkTooltip.classList.add("anww-tooltip");
     15        Object.assign(anwwLinkTooltip.style, {
     16            position: "absolute",
     17            background: "white",
     18            color: "#1e1e1e",
     19            fontSize: "16px",
     20            border: "1px solid black",
     21            padding: "5px 10px",
    1422            zIndex: 9999,
    15             display: 'none'
    16         }).addClass('anww-tooltip').appendTo('body');
    17     }
     23            display: "none",
     24            pointerEvents: "auto",
     25            boxShadow: "0px 4px 6px rgba(0,0,0,0.1)",
     26            maxWidth: "200px",
     27            whiteSpace: "nowrap"
     28        });
     29        document.body.appendChild(anwwLinkTooltip);
    1830
    19     function processLinks() {
    20         var anww_label = '';
     31        // Hide tooltip when clicking outside or pressing Escape
     32        document.addEventListener("click", (event) => {
     33            if (!event.target.closest(".anww-tooltip, a[target='_blank']")) {
     34                hideTooltip();
     35            }
     36        });
    2137
     38        document.addEventListener("keydown", (event) => {
     39            if (event.key === "Escape") {
     40                hideTooltip();
     41            }
     42        });
     43
     44        // Keep tooltip visible when hovered
     45        anwwLinkTooltip.addEventListener("mouseenter", () => {
     46            clearTimeout(tooltipTimeout);
     47        });
     48
     49        anwwLinkTooltip.addEventListener("mouseleave", () => {
     50            hideTooltip();
     51        });
     52    };
     53
     54    /**
     55     * Processes all anchor links and applies necessary accessibility enhancements.
     56     */
     57    const processLinks = () => {
    2258        // Remove previously appended icons to avoid duplication
    23         $(".anww-external-link-icon").remove();
     59        document.querySelectorAll(".anww-external-link-icon").forEach(icon => icon.remove());
    2460
    25         $("a").each(function () {
    26             var hasIcon = false;
    27             var onclickAttr = $(this).attr("onclick");
     61        document.querySelectorAll("a").forEach((link) => {
     62            let hasIcon = false;
     63            const onclickAttr = link.getAttribute("onclick");
    2864
    2965            // Check if the link opens a new window using target="_blank"
    30             if ($(this).attr("target") === "_blank") {
    31                 addExternalLinkIcon($(this));
    32                 updateAriaLabel($(this));
    33                 addTooltipHandlers($(this));
     66            if (link.getAttribute("target") === "_blank") {
     67                addExternalLinkIcon(link);
     68                updateAriaLabel(link);
     69                addTooltipHandlers(link);
    3470                hasIcon = true;
    3571            }
     
    3773            // Check if the link uses window.open in the onclick attribute
    3874            if (onclickAttr && onclickAttr.includes("window.open")) {
    39                 // Extract window.open arguments
    40                 var windowOpenMatch = onclickAttr.match(/window\.open\([^,]+,\s*['"]([^'"]+)['"]/);
    41                 var targetWindow = windowOpenMatch ? windowOpenMatch[1] : '';
     75                const windowOpenMatch = onclickAttr.match(/window\.open\([^,]+,\s*['"]([^'"]+)['"]/);
     76                const targetWindow = windowOpenMatch ? windowOpenMatch[1] : "";
    4277
    43                 // Ensure window.open is opening a new window (i.e., '_blank')
    44                 if (targetWindow === '_blank' || targetWindow === '') {
    45                     addExternalLinkIcon($(this));
    46                     updateAriaLabel($(this));
    47                     addTooltipHandlers($(this));
     78                if (targetWindow === "_blank" || targetWindow === "") {
     79                    addExternalLinkIcon(link);
     80                    updateAriaLabel(link);
     81                    addTooltipHandlers(link);
    4882                    hasIcon = true;
    4983                }
    5084            }
    5185
    52             // If no external link behavior detected, do not proceed with tooltip
    53             if (!hasIcon) {
    54                 return;
    55             }
     86            if (!hasIcon) return;
     87        });
     88    };
    5689
    57             function addExternalLinkIcon(link) {
    58                 // Add icon to link
    59                 if ($(':header', link).length) {
    60                     $(':header', link).append('<i class="anww-external-link-icon" aria-hidden="true"></i>');
    61                 } else {
    62                     link.append('<i class="anww-external-link-icon" aria-hidden="true"></i>');
    63                 }
    64             }
     90    /**
     91     * Adds an external link icon to the specified link.
     92     * @param {HTMLElement} link - The link element to modify.
     93     */
     94    const addExternalLinkIcon = (link) => {
     95        const icon = document.createElement("i");
     96        icon.classList.add("anww-external-link-icon");
     97        icon.setAttribute("aria-hidden", "true");
    6598
    66             function updateAriaLabel(link) {
    67                 // Get aria label text
    68                 if (link.attr("aria-label")) {
    69                     anww_label = link.attr("aria-label");
    70                 } else if ($('img', link).length) {
    71                     anww_label = link.find("img").attr("alt");
    72                 } else if (link.text()) {
    73                     anww_label = link.text();
    74                 }
     99        link.appendChild(icon);
     100    };
    75101
    76                 // Add warning label
    77                 if (anww_label) {
    78                     anww_label = anww_label.trimEnd();
    79                     anww_label += ", " + anww_localized.opens_a_new_window;
    80                 } else {
    81                     anww_label += anww_localized.opens_a_new_window;
    82                 }
     102    /**
     103     * Updates the aria-label of the specified link.
     104     * @param {HTMLElement} link - The link element to modify.
     105     */
     106    const updateAriaLabel = (link) => {
     107        let anwwLabel = "";
    83108
    84                 // Add aria-label to link
    85                 link.attr("aria-label", anww_label);
    86             }
     109        if (link.hasAttribute("aria-label")) {
     110            anwwLabel = link.getAttribute("aria-label");
     111        } else if (link.querySelector("img")) {
     112            anwwLabel = link.querySelector("img").getAttribute("alt");
     113        } else if (link.textContent) {
     114            anwwLabel = link.textContent.trim();
     115        }
    87116
    88             function addTooltipHandlers(link) {
    89                 // Position and show link_tooltip on hover
    90                 link.mousemove(function (e) {
    91                     anww_link_tooltip.css({
    92                         top: e.pageY + 10 + 'px',
    93                         left: e.pageX + 10 + 'px'
    94                     });
    95                 })
    96                 .hover(function () {
    97                     anww_link_tooltip.show().html(anww_localized.opens_a_new_window);
    98                 }, function () {
    99                     anww_link_tooltip.hide();
    100                 });
     117        anwwLabel = anwwLabel ? `${anwwLabel}, ${anww_localized.opens_a_new_window}` : anww_localized.opens_a_new_window;
     118        link.setAttribute("aria-label", anwwLabel);
     119    };
    101120
    102                 // Position and show link_tooltip on focus
    103                 link.on({
    104                     focusin: function () {
    105                         var position = link.offset();
    106                         anww_link_tooltip.css({
    107                             top: position.top + link.outerHeight() + 'px',
    108                             left: position.left + 'px'
    109                         });
     121    /**
     122     * Adds tooltip event handlers to the specified link.
     123     * @param {HTMLElement} link - The link element to modify.
     124     */
     125    const addTooltipHandlers = (link) => {
     126        link.addEventListener("mouseenter", (e) => {
     127            showTooltip(link, e.pageX, e.pageY);
     128        });
    110129
    111                         anww_link_tooltip.show().html(anww_localized.opens_a_new_window);
    112                     },
    113                     focusout: function () {
    114                         anww_link_tooltip.hide();
    115                     }
    116                 });
    117             }
     130        link.addEventListener("focusin", (e) => {
     131            const rect = link.getBoundingClientRect();
     132            showTooltip(link, rect.left + window.scrollX, rect.top + rect.height + window.scrollY);
    118133        });
    119     }
    120134
    121     $(window).on('load', function () {
     135        link.addEventListener("mouseleave", hideTooltipWithDelay);
     136        link.addEventListener("focusout", hideTooltipWithDelay);
     137    };
     138
     139    /**
     140     * Displays the tooltip near the specified coordinates, adjusting if it overflows.
     141     * @param {HTMLElement} link - The link triggering the tooltip.
     142     * @param {number} x - The x-coordinate.
     143     * @param {number} y - The y-coordinate.
     144     */
     145    const showTooltip = (link, x, y) => {
     146        clearTimeout(tooltipTimeout);
     147
     148        anwwLinkTooltip.textContent = anww_localized.opens_a_new_window;
     149        anwwLinkTooltip.style.display = "block";
     150
     151        const tooltipWidth = anwwLinkTooltip.offsetWidth;
     152        const tooltipHeight = anwwLinkTooltip.offsetHeight;
     153        const windowWidth = window.innerWidth;
     154        const windowHeight = window.innerHeight;
     155        const scrollTop = window.scrollY;
     156
     157        // Adjust X if the tooltip overflows the right edge
     158        if (x + tooltipWidth + 10 > windowWidth) {
     159            x -= (tooltipWidth + 20);
     160        }
     161
     162        // Adjust Y if the tooltip overflows the bottom edge
     163        if (y + tooltipHeight + 10 > windowHeight + scrollTop) {
     164            y -= (tooltipHeight + 20);
     165        }
     166
     167        anwwLinkTooltip.style.top = `${y + 10}px`;
     168        anwwLinkTooltip.style.left = `${x + 10}px`;
     169    };
     170
     171    /**
     172     * Delays hiding the tooltip to prevent flickering.
     173     */
     174    const hideTooltipWithDelay = () => {
     175        tooltipTimeout = setTimeout(hideTooltip, 300);
     176    };
     177
     178    /**
     179     * Hides the tooltip.
     180     */
     181    const hideTooltip = () => {
     182        anwwLinkTooltip.style.display = "none";
     183    };
     184
     185    // Initialize tooltip and process links on page load
     186    window.addEventListener("load", () => {
    122187        initializeTooltip();
    123188        processLinks();
     
    125190
    126191    // Support for FacetWP: Re-run the processLinks function when FacetWP refreshes the page
    127     $(document).on('facetwp-loaded', processLinks);
    128 
    129 })(jQuery);
     192    document.addEventListener("facetwp-loaded", processLinks);
     193})();
  • accessibility-new-window-warnings/trunk/README.txt

    r3174459 r3248595  
    33Tags: accessibility, accessible, wcag, ada, a11y, section 508, links, open new window, open new tab
    44Requires at least: 6.4.0
    5 Tested up to: 6.6.2
    6 Stable tag: 1.0.10
     5Tested up to: 6.7.2
     6Stable tag: 1.1.0
    77License: GPLv2 or later
    88License URI: http://www.gnu.org/licenses/gpl-2.0.html
     
    7575== Changelog ==
    7676
     77= 1.1.0 =
     78* Fixed - tooltips can now be dismissed using the Escape key, ensuring better accessibility
     79* Fixed - tooltip content is now hoverable, preventing accidental dismissal
     80* Refactored - code for better performance and maintainability using modern JavaScript practices
     81
    7782= 1.0.10 =
    7883* Added - detection and processing of window.open() links with consistent tooltip/ARIA label handling for both target="_blank" and window.open()
  • accessibility-new-window-warnings/trunk/accessibility-new-window-warnings.php

    r3174459 r3248595  
    44 * Plugin URI:  https://a11ychecker.com
    55 * Description: Make links that open in a new window accessible by adding a warning.
    6  * Version:     1.0.10
     6 * Version:     1.1.0
    77 * Author:      Equalize Digital
    88 * Author URI:  https://equalizedigital.com
     
    1414 */
    1515
    16 define( 'ANWW_VERSION', '1.0.10' );
     16define( 'ANWW_VERSION', '1.1.0' );
    1717define( 'ANWW_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
    1818define( 'ANWW_PLUGIN_URL', plugin_dir_url( __FILE__ ) );
  • accessibility-new-window-warnings/trunk/assets/js/accessibility-new-window-warnings-min.js

    r3174459 r3248595  
    1 !function($){"use strict";var n;function e(){var e="";$(".anww-external-link-icon").remove(),$("a").each((function(){var t=$(this).attr("onclick");if("_blank"===$(this).attr("target")&&(o($(this)),l($(this)),d($(this)),!0),t&&t.includes("window.open")){var a=t.match(/window\.open\([^,]+,\s*['"]([^'"]+)['"]/),i=a?a[1]:"";"_blank"!==i&&""!==i||(o($(this)),l($(this)),d($(this)),!0)}function o(n){$(":header",n).length?$(":header",n).append('<i class="anww-external-link-icon" aria-hidden="true"></i>'):n.append('<i class="anww-external-link-icon" aria-hidden="true"></i>')}function l(n){n.attr("aria-label")?e=n.attr("aria-label"):$("img",n).length?e=n.find("img").attr("alt"):n.text()&&(e=n.text()),e?(e=e.trimEnd(),e+=", "+anww_localized.opens_a_new_window):e+=anww_localized.opens_a_new_window,n.attr("aria-label",e)}function d(e){e.mousemove((function(e){n.css({top:e.pageY+10+"px",left:e.pageX+10+"px"})})).hover((function(){n.show().html(anww_localized.opens_a_new_window)}),(function(){n.hide()})),e.on({focusin:function(){var t=e.offset();n.css({top:t.top+e.outerHeight()+"px",left:t.left+"px"}),n.show().html(anww_localized.opens_a_new_window)},focusout:function(){n.hide()}})}}))}$(window).on("load",(function(){n=$("<div/>").css({position:"absolute",background:"white",color:"#1e1e1e",fontSize:"16px",border:"1px solid black",padding:"5px 10px",zIndex:9999,display:"none"}).addClass("anww-tooltip").appendTo("body"),e()})),$(document).on("facetwp-loaded",e)}(jQuery);
     1!function(){let e,t;const n=()=>{document.querySelectorAll(".anww-external-link-icon").forEach((e=>e.remove())),document.querySelectorAll("a").forEach((e=>{let t=!1;const n=e.getAttribute("onclick");if("_blank"===e.getAttribute("target")&&(o(e),i(e),a(e),t=!0),n&&n.includes("window.open")){const d=n.match(/window\.open\([^,]+,\s*['"]([^'"]+)['"]/),l=d?d[1]:"";"_blank"!==l&&""!==l||(o(e),i(e),a(e),t=!0)}}))},o=e=>{const t=document.createElement("i");t.classList.add("anww-external-link-icon"),t.setAttribute("aria-hidden","true"),e.appendChild(t)},i=e=>{let t="";e.hasAttribute("aria-label")?t=e.getAttribute("aria-label"):e.querySelector("img")?t=e.querySelector("img").getAttribute("alt"):e.textContent&&(t=e.textContent.trim()),t=t?`${t}, ${anww_localized.opens_a_new_window}`:anww_localized.opens_a_new_window,e.setAttribute("aria-label",t)},a=e=>{e.addEventListener("mouseenter",(t=>{d(e,t.pageX,t.pageY)})),e.addEventListener("focusin",(t=>{const n=e.getBoundingClientRect();d(e,n.left+window.scrollX,n.top+n.height+window.scrollY)})),e.addEventListener("mouseleave",l),e.addEventListener("focusout",l)},d=(n,o,i)=>{clearTimeout(t),e.textContent=anww_localized.opens_a_new_window,e.style.display="block";const a=e.offsetWidth,d=e.offsetHeight;o+a+10>window.innerWidth&&(o-=a+20),i+d+10>window.innerHeight+window.scrollY&&(i-=d+20),e.style.top=`${i+10}px`,e.style.left=`${o+10}px`},l=()=>{t=setTimeout(s,300)},s=()=>{e.style.display="none"};window.addEventListener("load",(()=>{e=document.createElement("div"),e.setAttribute("role","tooltip"),e.classList.add("anww-tooltip"),Object.assign(e.style,{position:"absolute",background:"white",color:"#1e1e1e",fontSize:"16px",border:"1px solid black",padding:"5px 10px",zIndex:9999,display:"none",pointerEvents:"auto",boxShadow:"0px 4px 6px rgba(0,0,0,0.1)",maxWidth:"200px",whiteSpace:"nowrap"}),document.body.appendChild(e),document.addEventListener("click",(e=>{e.target.closest(".anww-tooltip, a[target='_blank']")||s()})),document.addEventListener("keydown",(e=>{"Escape"===e.key&&s()})),e.addEventListener("mouseenter",(()=>{clearTimeout(t)})),e.addEventListener("mouseleave",(()=>{s()})),n()})),document.addEventListener("facetwp-loaded",n)}();
  • accessibility-new-window-warnings/trunk/assets/js/accessibility-new-window-warnings.js

    r3174459 r3248595  
    1 (function ($) {
    2     "use strict";
     1/* global anww_localized */
    32
    4     var anww_link_tooltip;
     3(function () {
    54
    6     function initializeTooltip() {
    7         anww_link_tooltip = $('<div/>').css({
    8             position: 'absolute',
    9             background: 'white',
    10             color: '#1e1e1e',
    11             fontSize: '16px',
    12             border: '1px solid black',
    13             padding: '5px 10px',
     5    let anwwLinkTooltip;
     6    let tooltipTimeout;
     7
     8    /**
     9     * Initializes the tooltip element and event listeners.
     10     */
     11    const initializeTooltip = () => {
     12        anwwLinkTooltip = document.createElement("div");
     13        anwwLinkTooltip.setAttribute("role", "tooltip");
     14        anwwLinkTooltip.classList.add("anww-tooltip");
     15        Object.assign(anwwLinkTooltip.style, {
     16            position: "absolute",
     17            background: "white",
     18            color: "#1e1e1e",
     19            fontSize: "16px",
     20            border: "1px solid black",
     21            padding: "5px 10px",
    1422            zIndex: 9999,
    15             display: 'none'
    16         }).addClass('anww-tooltip').appendTo('body');
    17     }
     23            display: "none",
     24            pointerEvents: "auto",
     25            boxShadow: "0px 4px 6px rgba(0,0,0,0.1)",
     26            maxWidth: "200px",
     27            whiteSpace: "nowrap"
     28        });
     29        document.body.appendChild(anwwLinkTooltip);
    1830
    19     function processLinks() {
    20         var anww_label = '';
     31        // Hide tooltip when clicking outside or pressing Escape
     32        document.addEventListener("click", (event) => {
     33            if (!event.target.closest(".anww-tooltip, a[target='_blank']")) {
     34                hideTooltip();
     35            }
     36        });
    2137
     38        document.addEventListener("keydown", (event) => {
     39            if (event.key === "Escape") {
     40                hideTooltip();
     41            }
     42        });
     43
     44        // Keep tooltip visible when hovered
     45        anwwLinkTooltip.addEventListener("mouseenter", () => {
     46            clearTimeout(tooltipTimeout);
     47        });
     48
     49        anwwLinkTooltip.addEventListener("mouseleave", () => {
     50            hideTooltip();
     51        });
     52    };
     53
     54    /**
     55     * Processes all anchor links and applies necessary accessibility enhancements.
     56     */
     57    const processLinks = () => {
    2258        // Remove previously appended icons to avoid duplication
    23         $(".anww-external-link-icon").remove();
     59        document.querySelectorAll(".anww-external-link-icon").forEach(icon => icon.remove());
    2460
    25         $("a").each(function () {
    26             var hasIcon = false;
    27             var onclickAttr = $(this).attr("onclick");
     61        document.querySelectorAll("a").forEach((link) => {
     62            let hasIcon = false;
     63            const onclickAttr = link.getAttribute("onclick");
    2864
    2965            // Check if the link opens a new window using target="_blank"
    30             if ($(this).attr("target") === "_blank") {
    31                 addExternalLinkIcon($(this));
    32                 updateAriaLabel($(this));
    33                 addTooltipHandlers($(this));
     66            if (link.getAttribute("target") === "_blank") {
     67                addExternalLinkIcon(link);
     68                updateAriaLabel(link);
     69                addTooltipHandlers(link);
    3470                hasIcon = true;
    3571            }
     
    3773            // Check if the link uses window.open in the onclick attribute
    3874            if (onclickAttr && onclickAttr.includes("window.open")) {
    39                 // Extract window.open arguments
    40                 var windowOpenMatch = onclickAttr.match(/window\.open\([^,]+,\s*['"]([^'"]+)['"]/);
    41                 var targetWindow = windowOpenMatch ? windowOpenMatch[1] : '';
     75                const windowOpenMatch = onclickAttr.match(/window\.open\([^,]+,\s*['"]([^'"]+)['"]/);
     76                const targetWindow = windowOpenMatch ? windowOpenMatch[1] : "";
    4277
    43                 // Ensure window.open is opening a new window (i.e., '_blank')
    44                 if (targetWindow === '_blank' || targetWindow === '') {
    45                     addExternalLinkIcon($(this));
    46                     updateAriaLabel($(this));
    47                     addTooltipHandlers($(this));
     78                if (targetWindow === "_blank" || targetWindow === "") {
     79                    addExternalLinkIcon(link);
     80                    updateAriaLabel(link);
     81                    addTooltipHandlers(link);
    4882                    hasIcon = true;
    4983                }
    5084            }
    5185
    52             // If no external link behavior detected, do not proceed with tooltip
    53             if (!hasIcon) {
    54                 return;
    55             }
     86            if (!hasIcon) return;
     87        });
     88    };
    5689
    57             function addExternalLinkIcon(link) {
    58                 // Add icon to link
    59                 if ($(':header', link).length) {
    60                     $(':header', link).append('<i class="anww-external-link-icon" aria-hidden="true"></i>');
    61                 } else {
    62                     link.append('<i class="anww-external-link-icon" aria-hidden="true"></i>');
    63                 }
    64             }
     90    /**
     91     * Adds an external link icon to the specified link.
     92     * @param {HTMLElement} link - The link element to modify.
     93     */
     94    const addExternalLinkIcon = (link) => {
     95        const icon = document.createElement("i");
     96        icon.classList.add("anww-external-link-icon");
     97        icon.setAttribute("aria-hidden", "true");
    6598
    66             function updateAriaLabel(link) {
    67                 // Get aria label text
    68                 if (link.attr("aria-label")) {
    69                     anww_label = link.attr("aria-label");
    70                 } else if ($('img', link).length) {
    71                     anww_label = link.find("img").attr("alt");
    72                 } else if (link.text()) {
    73                     anww_label = link.text();
    74                 }
     99        link.appendChild(icon);
     100    };
    75101
    76                 // Add warning label
    77                 if (anww_label) {
    78                     anww_label = anww_label.trimEnd();
    79                     anww_label += ", " + anww_localized.opens_a_new_window;
    80                 } else {
    81                     anww_label += anww_localized.opens_a_new_window;
    82                 }
     102    /**
     103     * Updates the aria-label of the specified link.
     104     * @param {HTMLElement} link - The link element to modify.
     105     */
     106    const updateAriaLabel = (link) => {
     107        let anwwLabel = "";
    83108
    84                 // Add aria-label to link
    85                 link.attr("aria-label", anww_label);
    86             }
     109        if (link.hasAttribute("aria-label")) {
     110            anwwLabel = link.getAttribute("aria-label");
     111        } else if (link.querySelector("img")) {
     112            anwwLabel = link.querySelector("img").getAttribute("alt");
     113        } else if (link.textContent) {
     114            anwwLabel = link.textContent.trim();
     115        }
    87116
    88             function addTooltipHandlers(link) {
    89                 // Position and show link_tooltip on hover
    90                 link.mousemove(function (e) {
    91                     anww_link_tooltip.css({
    92                         top: e.pageY + 10 + 'px',
    93                         left: e.pageX + 10 + 'px'
    94                     });
    95                 })
    96                 .hover(function () {
    97                     anww_link_tooltip.show().html(anww_localized.opens_a_new_window);
    98                 }, function () {
    99                     anww_link_tooltip.hide();
    100                 });
     117        anwwLabel = anwwLabel ? `${anwwLabel}, ${anww_localized.opens_a_new_window}` : anww_localized.opens_a_new_window;
     118        link.setAttribute("aria-label", anwwLabel);
     119    };
    101120
    102                 // Position and show link_tooltip on focus
    103                 link.on({
    104                     focusin: function () {
    105                         var position = link.offset();
    106                         anww_link_tooltip.css({
    107                             top: position.top + link.outerHeight() + 'px',
    108                             left: position.left + 'px'
    109                         });
     121    /**
     122     * Adds tooltip event handlers to the specified link.
     123     * @param {HTMLElement} link - The link element to modify.
     124     */
     125    const addTooltipHandlers = (link) => {
     126        link.addEventListener("mouseenter", (e) => {
     127            showTooltip(link, e.pageX, e.pageY);
     128        });
    110129
    111                         anww_link_tooltip.show().html(anww_localized.opens_a_new_window);
    112                     },
    113                     focusout: function () {
    114                         anww_link_tooltip.hide();
    115                     }
    116                 });
    117             }
     130        link.addEventListener("focusin", (e) => {
     131            const rect = link.getBoundingClientRect();
     132            showTooltip(link, rect.left + window.scrollX, rect.top + rect.height + window.scrollY);
    118133        });
    119     }
    120134
    121     $(window).on('load', function () {
     135        link.addEventListener("mouseleave", hideTooltipWithDelay);
     136        link.addEventListener("focusout", hideTooltipWithDelay);
     137    };
     138
     139    /**
     140     * Displays the tooltip near the specified coordinates, adjusting if it overflows.
     141     * @param {HTMLElement} link - The link triggering the tooltip.
     142     * @param {number} x - The x-coordinate.
     143     * @param {number} y - The y-coordinate.
     144     */
     145    const showTooltip = (link, x, y) => {
     146        clearTimeout(tooltipTimeout);
     147
     148        anwwLinkTooltip.textContent = anww_localized.opens_a_new_window;
     149        anwwLinkTooltip.style.display = "block";
     150
     151        const tooltipWidth = anwwLinkTooltip.offsetWidth;
     152        const tooltipHeight = anwwLinkTooltip.offsetHeight;
     153        const windowWidth = window.innerWidth;
     154        const windowHeight = window.innerHeight;
     155        const scrollTop = window.scrollY;
     156
     157        // Adjust X if the tooltip overflows the right edge
     158        if (x + tooltipWidth + 10 > windowWidth) {
     159            x -= (tooltipWidth + 20);
     160        }
     161
     162        // Adjust Y if the tooltip overflows the bottom edge
     163        if (y + tooltipHeight + 10 > windowHeight + scrollTop) {
     164            y -= (tooltipHeight + 20);
     165        }
     166
     167        anwwLinkTooltip.style.top = `${y + 10}px`;
     168        anwwLinkTooltip.style.left = `${x + 10}px`;
     169    };
     170
     171    /**
     172     * Delays hiding the tooltip to prevent flickering.
     173     */
     174    const hideTooltipWithDelay = () => {
     175        tooltipTimeout = setTimeout(hideTooltip, 300);
     176    };
     177
     178    /**
     179     * Hides the tooltip.
     180     */
     181    const hideTooltip = () => {
     182        anwwLinkTooltip.style.display = "none";
     183    };
     184
     185    // Initialize tooltip and process links on page load
     186    window.addEventListener("load", () => {
    122187        initializeTooltip();
    123188        processLinks();
     
    125190
    126191    // Support for FacetWP: Re-run the processLinks function when FacetWP refreshes the page
    127     $(document).on('facetwp-loaded', processLinks);
    128 
    129 })(jQuery);
     192    document.addEventListener("facetwp-loaded", processLinks);
     193})();
Note: See TracChangeset for help on using the changeset viewer.