diff --git a/ARIA/apg/about/about.md b/ARIA/apg/about/about.md index 3946650dc..e691b7841 100644 --- a/ARIA/apg/about/about.md +++ b/ARIA/apg/about/about.md @@ -94,15 +94,6 @@ lang: en Related Specifications.

-
  • -

    Assistive Technology Support Tables

    -

    - Pages that provide example implementations of APG patterns also, when available, provide a summary of assistive technology support of the ARIA used in those examples. - Learn how to interpret and use data in the - Assistive Technology Support Tables. -

    -
  • -
  • Coverage and Quality Report

    @@ -115,8 +106,5 @@ lang: en - + diff --git a/ARIA/apg/about/acknowledgements/acknowledgements.md b/ARIA/apg/about/acknowledgements/acknowledgements.md index 72015226a..ef84886e5 100644 --- a/ARIA/apg/about/acknowledgements/acknowledgements.md +++ b/ARIA/apg/about/acknowledgements/acknowledgements.md @@ -194,8 +194,5 @@ if (enableSidebar) document.body.classList.add('has-sidebar'); - + diff --git a/ARIA/apg/about/aria-basics/aria-basics.md b/ARIA/apg/about/aria-basics/aria-basics.md index a2fd13bb3..6ff71db88 100644 --- a/ARIA/apg/about/aria-basics/aria-basics.md +++ b/ARIA/apg/about/aria-basics/aria-basics.md @@ -100,8 +100,5 @@ if (enableSidebar) document.body.classList.add('has-sidebar'); - + diff --git a/ARIA/apg/about/at-support-tables/at-support-tables.md b/ARIA/apg/about/at-support-tables/at-support-tables.md deleted file mode 100644 index d8a812af8..000000000 --- a/ARIA/apg/about/at-support-tables/at-support-tables.md +++ /dev/null @@ -1,166 +0,0 @@ ---- -# This file was generated by scripts/pre-build/library/formatForJekyll.js -title: "Assistive Technology Support Tables" -ref: /ARIA/apg/about/at-support-tables/ - -github: - repository: w3c/aria-practices - branch: main - path: content/about/at-support-tables/at-support-tables.html -feedbackmail: public-aria-practices@w3.org -permalink: /ARIA/apg/about/at-support-tables/ - -sidebar: true - - - -# Context here: https://github.com/w3c/wai-aria-practices/issues/31 -type_of_guidance: APG - -lang: en ---- - - -AT Support Tables - - - - - - - - - - - - - -

    - -
    - -

    - As the ARIA and Assistive Technologies Project - makes reports on assistive technology interoperability for APG examples available, - the APG Task Force adds summaries of assistive technology support to the relevant example pages. - This page explains how to interpret and use the assistive technology support summaries. -

    - -
    -

    Purpose of AT Support Tables

    -

    - The purpose of the support tables is to provide an actionable summary of the interoperability tests performed by the ARIA-AT project.

    -
    - -
    -

    Meaning of Support Levels

    -

    - The assistive technology support tables present two percentages for each assistive technology and browser combination that have been tested: "Must-Have Behaviors" and "Should-Have Behaviors". - A behavior designated as “Must-Have" is essential; if not provided, users could be blocked from using the UI element. - Failure to provide a “Should-Have” behavior could impede users. - Learn more about ARIA-AT’s - definitions of Must and Should on the project wiki. -

    - -
    -

    Examples of Must-Have Behaviors

    -
      -
    • Convey the name of a radio button.
    • -
    • Convey the state of a checked radio button.
    • -
    -
    - -
    -

    Examples of Should-Have Behaviors

    -
      -
    • Convey the position of a radio button in a radio group, e.g., the button is 1 of 3.
    • -
    • Convey the number of radio buttons in a radio group.
    • -
    -
    - -
    -

    Important Constraints

    -
      -
    • Unless otherwise noted, all testing is done using the assistive technology vendor's default configuration of an assistive technology.
    • -
    • - ARIA-AT interoperability tests do not prescribe exactly how to satisfy a need. - For example, they do not specify exactly what a screen reader should speak. - Two different screen readers may convey the same information in different ways. -
    • -
    -
    -
    - -
    -

    Recommendations

    - -
    -

    Don’t Code to the Bugs

    -

    - ARIA-AT is working with assistive technology vendors to increase their support levels. - This means that assistive technologies that align with ARIA-AT interoperability tests will change over time. - Exercise caution when implementing a pattern where support levels are less than 100%. - Avoid modifying code to accommodate an assistive technology bug unless you are confident the modified code provides an experience that: -

    -
      -
    • Works equally well when using assistive technologies that do not exhibit the bug.
    • -
    • Will work equally well after the assistive technology bug is fixed.
    • -
    -

    - When possible, test implementations of APG patterns with an assistive technology that provides 100% support for both must-have and should-have behaviors. -

    -
    - -
    -

    Design to Mitigate Critical Support Failures

    -

    - Where feasible, avoid designing experiences that rely on features of APG patterns that have less than 100% support for must-have behaviors. - If the must-have support level is less than 100% for one example implementation of a pattern, that does not mean every other way of implementing that pattern will present assistive technology users with the same problems. - In these cases: -

    -
      -
    1. If there are multiple implementation examples of the pattern, compare support levels across examples to discover whether another method of implementation provides better support.
    2. -
    3. learn about the specific aspects of an example implementation that are not fully supported by navigating to the detailed report with the View Complete Report button.
    4. -
    5. If possible, use the guidelines of the pattern to design interactions such that they avoid the problematic features.
    6. -
    -
    - -
    -

    Perform Your Own Tests

    -

    - A primary purpose of ARIA-AT data is to help assistive technology vendors coordinate interoperable rendering of ARIA. - While the ARIA-AT summary tables on APG example pages can be used as a guide of where to prioritize testing, the data is not as a final verdict on whether a feature in a web application will work. - It is essential for all developers to test applications with multiple assistive technologies to ensure a good user experience. -

    -
    - -
    - - -
    - -
    - - diff --git a/ARIA/apg/about/change-history/change-history.md b/ARIA/apg/about/change-history/change-history.md index 53cdf5913..6a990078f 100644 --- a/ARIA/apg/about/change-history/change-history.md +++ b/ARIA/apg/about/change-history/change-history.md @@ -100,8 +100,5 @@ lang: en - + diff --git a/ARIA/apg/about/coverage-and-quality/coverage-and-quality-report.md b/ARIA/apg/about/coverage-and-quality/coverage-and-quality-report.md index f58f72eef..5a2170f5d 100644 --- a/ARIA/apg/about/coverage-and-quality/coverage-and-quality-report.md +++ b/ARIA/apg/about/coverage-and-quality/coverage-and-quality-report.md @@ -26,7 +26,6 @@ lang: en - ",rE:!0,sL:["css","xml"]}},{cN:"tag",b:"|$)",e:">",k:{name:"script"},c:[t],starts:{e:"",rE:!0,sL:["actionscript","javascript","handlebars","xml"]}},{cN:"meta",v:[{b:/<\?xml/,e:/\?>/,r:10},{b:/<\?\w+/,e:/\?>/}]},{cN:"tag",b:"",c:[{cN:"name",b:/[^\/><\s]+/,r:0},t]}]}});hljs.registerLanguage("css",function(e){var c="[a-zA-Z-][a-zA-Z0-9_-]*",t={b:/[A-Z\_\.\-]+\s*:/,rB:!0,e:";",eW:!0,c:[{cN:"attribute",b:/\S/,e:":",eE:!0,starts:{eW:!0,eE:!0,c:[{b:/[\w-]+\(/,rB:!0,c:[{cN:"built_in",b:/[\w-]+/},{b:/\(/,e:/\)/,c:[e.ASM,e.QSM]}]},e.CSSNM,e.QSM,e.ASM,e.CBCM,{cN:"number",b:"#[0-9A-Fa-f]+"},{cN:"meta",b:"!important"}]}}]};return{cI:!0,i:/[=\/|'\$]/,c:[e.CBCM,{cN:"selector-id",b:/#[A-Za-z0-9_-]+/},{cN:"selector-class",b:/\.[A-Za-z0-9_-]+/},{cN:"selector-attr",b:/\[/,e:/\]/,i:"$"},{cN:"selector-pseudo",b:/:(:)?[a-zA-Z0-9\_\-\+\(\)"'.]+/},{b:"@(font-face|page)",l:"[a-z-]+",k:"font-face page"},{b:"@",e:"[{;]",i:/:/,c:[{cN:"keyword",b:/\w+/},{b:/\s/,eW:!0,eE:!0,r:0,c:[e.ASM,e.QSM,e.CSSNM]}]},{cN:"selector-tag",b:c,r:0},{b:"{",e:"}",i:/\S/,c:[e.CBCM,t]}]}});hljs.registerLanguage("json",function(e){var i={literal:"true false null"},n=[e.QSM,e.CNM],r={e:",",eW:!0,eE:!0,c:n,k:i},t={b:"{",e:"}",c:[{cN:"attr",b:/"/,e:/"/,c:[e.BE],i:"\\n"},e.inherit(r,{b:/:/})],i:"\\S"},c={b:"\\[",e:"\\]",c:[e.inherit(r)],i:"\\S"};return n.splice(n.length,0,t,c),{c:n,k:i,i:"\\S"}});hljs.registerLanguage("javascript",function(e){var r="[A-Za-z$_][0-9A-Za-z$_]*",t={keyword:"in of if for while finally var new function do return void else break catch instanceof with throw case default try this switch continue typeof delete let yield const export super debugger as async await static import from as",literal:"true false null undefined NaN Infinity",built_in:"eval isFinite isNaN parseFloat parseInt decodeURI decodeURIComponent encodeURI encodeURIComponent escape unescape Object Function Boolean Error EvalError InternalError RangeError ReferenceError StopIteration SyntaxError TypeError URIError Number Math Date String RegExp Array Float32Array Float64Array Int16Array Int32Array Int8Array Uint16Array Uint32Array Uint8Array Uint8ClampedArray ArrayBuffer DataView JSON Intl arguments require module console window document Symbol Set Map WeakSet WeakMap Proxy Reflect Promise"},a={cN:"number",v:[{b:"\\b(0[bB][01]+)"},{b:"\\b(0[oO][0-7]+)"},{b:e.CNR}],r:0},n={cN:"subst",b:"\\$\\{",e:"\\}",k:t,c:[]},c={cN:"string",b:"`",e:"`",c:[e.BE,n]};n.c=[e.ASM,e.QSM,c,a,e.RM];var s=n.c.concat([e.CBCM,e.CLCM]);return{aliases:["js","jsx"],k:t,c:[{cN:"meta",r:10,b:/^\s*['"]use (strict|asm)['"]/},{cN:"meta",b:/^#!/,e:/$/},e.ASM,e.QSM,c,e.CLCM,e.CBCM,a,{b:/[{,]\s*/,r:0,c:[{b:r+"\\s*:",rB:!0,r:0,c:[{cN:"attr",b:r,r:0}]}]},{b:"("+e.RSR+"|\\b(case|return|throw)\\b)\\s*",k:"return throw case",c:[e.CLCM,e.CBCM,e.RM,{cN:"function",b:"(\\(.*?\\)|"+r+")\\s*=>",rB:!0,e:"\\s*=>",c:[{cN:"params",v:[{b:r},{b:/\(\s*\)/},{b:/\(/,e:/\)/,eB:!0,eE:!0,k:t,c:s}]}]},{b://,sL:"xml",c:[{b:/<\w+\s*\/>/,skip:!0},{b:/<\w+/,e:/(\/\w+|\w+\/)>/,skip:!0,c:[{b:/<\w+\s*\/>/,skip:!0},"self"]}]}],r:0},{cN:"function",bK:"function",e:/\{/,eE:!0,c:[e.inherit(e.TM,{b:r}),{cN:"params",b:/\(/,e:/\)/,eB:!0,eE:!0,c:s}],i:/\[|%/},{b:/\$[(.]/},e.METHOD_GUARD,{cN:"class",bK:"class",e:/[{;=]/,eE:!0,i:/[:"\[\]]/,c:[{bK:"extends"},e.UTM]},{bK:"constructor",e:/\{/,eE:!0}],i:/#(?!!)/}}); \ No newline at end of file diff --git a/content-assets/wai-aria-practices/shared/js/skipto.js b/content-assets/wai-aria-practices/shared/js/skipto.js index c9f081747..29134ff41 100644 --- a/content-assets/wai-aria-practices/shared/js/skipto.js +++ b/content-assets/wai-aria-practices/shared/js/skipto.js @@ -1,118 +1,117 @@ -/* ======================================================================== - * Version: 5.3.2 - * Copyright (c) 2022, 2023, 2024 Jon Gunderson; Licensed BSD - * Copyright (c) 2021 PayPal Accessibility Team and University of Illinois; Licensed BSD - * All rights reserved. - * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - * Neither the name of PayPal or any of its subsidiaries or affiliates, nor the name of the University of Illinois, nor the names of any other contributors contributors may be used to endorse or promote products derived from this software without specific prior written permission. - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * CDN: https://skipto-landmarks-headings.github.io/page-script-5/dist/skipto.min.js - * Documentation: https://skipto-landmarks-headings.github.io/page-script-5 - * Code: https://github.com/skipto-landmarks-headings/page-script-5 - * Report Issues: https://github.com/skipto-landmarks-headings/page-script-5/issues - * ======================================================================== */ - -(function () { - 'use strict'; - - /* - * debug.js - * - * Usage - * import DebugLogging from './debug.js'; - * const debug = new DebugLogging('myLabel', true); // e.g. 'myModule' - * ... - * if (debug.flag) debug.log('myMessage'); - * - * Notes - * new DebugLogging() - calling the constructor with no arguments results - * in debug.flag set to false and debug.label set to 'debug'; - * constructor accepts 0, 1 or 2 arguments in any order - * @param flag [optional] {boolean} - sets debug.flag - * @param label [optional] {string} - sets debug.label - * Properties - * debug.flag {boolean} allows you to switch debug logging on or off; - * default value is false - * debug.label {string} rendered as a prefix to each log message; - * default value is 'debug' - * Methods - * debug.log calls console.log with label prefix and message - * @param message {object} - console.log calls toString() - * @param spaceAbove [optional] {boolean} - * - * debug.tag outputs tagName and textContent of DOM element - * @param node {DOM node reference} - usually an HTMLElement - * @param spaceAbove [optional] {boolean} - * - * debug.separator outputs only debug.label and a series of hyphens - * @param spaceAbove [optional] {boolean} - */ - - class DebugLogging { - constructor (...args) { - // Default values for cases where fewer than two arguments are provided - this._flag = false; - this._label = 'debug'; - - // The constructor may be called with zero, one or two arguments. If two - // arguments, they can be in any order: one is assumed to be the boolean - // value for '_flag' and the other one the string value for '_label'. - for (const [index, arg] of args.entries()) { - if (index < 2) { - switch (typeof arg) { - case 'boolean': - this._flag = arg; - break; - case 'string': - this._label = arg; - break; - } - } - } - } - - get flag () { return this._flag; } - - set flag (value) { - if (typeof value === 'boolean') { - this._flag = value; - } - } - - get label () { return this._label; } - - set label (value) { - if (typeof value === 'string') { - this._label = value; - } - } - - log (message, spaceAbove) { - const newline = spaceAbove ? '\n' : ''; - console.log(`${newline}[${this._label}] ${message}`); - } - - tag (node, spaceAbove) { - if (node && node.tagName) { - const text = node.textContent.trim().replace(/\s+/g, ' '); - this.log(`[${node.tagName}]: ${text.substring(0, 40)}`, spaceAbove); - } - } - - separator (spaceAbove) { - this.log('-----------------------------', spaceAbove); - } - - } - +/* ======================================================================== + * Version: 5.1.6 + * Copyright (c) 2022, 2023 Jon Gunderson; Licensed BSD + * Copyright (c) 2021 PayPal Accessibility Team and University of Illinois; Licensed BSD + * All rights reserved. + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of PayPal or any of its subsidiaries or affiliates, nor the name of the University of Illinois, nor the names of any other contributors contributors may be used to endorse or promote products derived from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Documenation: https://skipto-landmarks-headings.github.io/page-script-5 + * Code: https://github.com/skipto-landmarks-headings/page-script-5 + * Report Issues: https://github.com/skipto-landmarks-headings/page-script-5/issues + * ======================================================================== */ + +(function () { + 'use strict'; + + /* + * debug.js + * + * Usage + * import DebugLogging from './debug.js'; + * const debug = new DebugLogging('myLabel', true); // e.g. 'myModule' + * ... + * if (debug.flag) debug.log('myMessage'); + * + * Notes + * new DebugLogging() - calling the constructor with no arguments results + * in debug.flag set to false and debug.label set to 'debug'; + * constructor accepts 0, 1 or 2 arguments in any order + * @param flag [optional] {boolean} - sets debug.flag + * @param label [optional] {string} - sets debug.label + * Properties + * debug.flag {boolean} allows you to switch debug logging on or off; + * default value is false + * debug.label {string} rendered as a prefix to each log message; + * default value is 'debug' + * Methods + * debug.log calls console.log with label prefix and message + * @param message {object} - console.log calls toString() + * @param spaceAbove [optional] {boolean} + * + * debug.tag outputs tagName and textContent of DOM element + * @param node {DOM node reference} - usually an HTMLElement + * @param spaceAbove [optional] {boolean} + * + * debug.separator outputs only debug.label and a series of hyphens + * @param spaceAbove [optional] {boolean} + */ + + class DebugLogging { + constructor (...args) { + // Default values for cases where fewer than two arguments are provided + this._flag = false; + this._label = 'debug'; + + // The constructor may be called with zero, one or two arguments. If two + // arguments, they can be in any order: one is assumed to be the boolean + // value for '_flag' and the other one the string value for '_label'. + for (const [index, arg] of args.entries()) { + if (index < 2) { + switch (typeof arg) { + case 'boolean': + this._flag = arg; + break; + case 'string': + this._label = arg; + break; + } + } + } + } + + get flag () { return this._flag; } + + set flag (value) { + if (typeof value === 'boolean') { + this._flag = value; + } + } + + get label () { return this._label; } + + set label (value) { + if (typeof value === 'string') { + this._label = value; + } + } + + log (message, spaceAbove) { + const newline = spaceAbove ? '\n' : ''; + console.log(`${newline}[${this._label}] ${message}`); + } + + tag (node, spaceAbove) { + if (node && node.tagName) { + const text = node.textContent.trim().replace(/\s+/g, ' '); + this.log(`[${node.tagName}]: ${text.substring(0, 40)}`, spaceAbove); + } + } + + separator (spaceAbove) { + this.log('-----------------------------', spaceAbove); + } + + } + /* style.js */ /* Constants */ - const debug$6 = new DebugLogging('style', false); - debug$6.flag = false; + const debug$5 = new DebugLogging('style', false); + debug$5.flag = false; const styleTemplate = document.createElement('template'); styleTemplate.innerHTML = ` @@ -123,19 +122,16 @@ $skipToId.popup { transition: top 0.35s ease; } -$skipToId button .skipto-text { +$skipToId button .text { padding: 6px 8px 6px 8px; display: inline-block; } -$skipToId button .skipto-small { - padding: 6px 8px 6px 8px; - display: none; -} - -$skipToId button .skipto-medium { - padding: 6px 8px 6px 8px; +$skipToId button img { + height: 24px; + padding: 2px 4px 2px 4px; display: none; + background-color: #e8e9ea; } $skipToId, @@ -169,54 +165,18 @@ $skipToId button { z-index: $zIndex !important; } -@media screen and (max-width: $smallBreakPointpx) { - $skipToId:not(.popup) button .skipto-small { - transition: top 0.35s ease; - display: inline-block; +@media screen and (max-width: $mediaBreakPointpx) { + $skipToId button img { + display: block; } - $skipToId:not(.popup) button .skipto-text, - $skipToId:not(.popup) button .skipto-medium { - transition: top 0.35s ease; - display: none; + $skipToId button { + border-color: #e8e9ea; } - $skipToId:not(.popup).focus button .skipto-text { - transition: top 0.35s ease; - display: inline-block; - } - - $skipToId:not(.popup).focus button .skipto-small, - $skipToId:not(.popup).focus button .skipto-medium { - transition: top 0.35s ease; + $skipToId button .text { display: none; } - -} - -@media screen and (min-width: $smallBreakPointpx) and (max-width: $mediumBreakPointpx) { - $skipToId:not(.popup) button .skipto-medium { - transition: top 0.35s ease; - display: inline-block; - } - - $skipToId:not(.popup) button .skipto-text, - $skipToId:not(.popup) button .skipto-small { - transition: top 0.35s ease; - display: none; - } - - $skipToId:not(.popup).focus button .skipto-text { - transition: top 0.35s ease; - display: inline-block; - } - - $skipToId:not(.popup).focus button .skipto-small, - $skipToId:not(.popup).focus button .skipto-medium { - transition: top 0.35s ease; - display: none; - } - } $skipToId.fixed { @@ -269,11 +229,10 @@ $skipToId [role="menuitem"] .label { font-size: 100%; font-weight: normal; color: $menuTextColor; - background-color: $menuBackgroundColor; display: inline-block; + background-color: $menuBackgroundColor; line-height: inherit; display: inline-block; - white-space: nowrap; } $skipToId [role="menuitem"] .level { @@ -285,6 +244,7 @@ $skipToId [role="menuitem"] .label { text-align: left; margin: 0; padding: 0; + white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } @@ -369,31 +329,37 @@ $skipToId button:hover { background-color: $menuBackgroundColor; color: $menuTextColor; outline: none; +} + +$skipToId button:focus, +$skipToId button:hover { border-width: 0px 2px 2px 2px; border-color: $focusBorderColor; } - -$skipToId button:focus .skipto-text, -$skipToId button:hover .skipto-text, -$skipToId button:focus .skipto-small, -$skipToId button:hover .skipto-small, -$skipToId button:focus .skipto-medium, -$skipToId button:hover .skipto-medium { +$skipToId button:focus .text, +$skipToId button:hover .text { padding: 6px 7px 5px 7px; } +$skipToId button:focus img, +$skipToId button:hover img { + padding: 2px 3px 4px 3px; +} + + $skipToId [role="menuitem"]:focus { padding: 1px; border-width: 2px; border-style: solid; border-color: $focusBorderColor; + background-color: $menuitemFocusBackgroundColor; + color: $menuitemFocusTextColor; outline: none; } -$skipToId [role="menuitem"].hover, -$skipToId [role="menuitem"].hover .level, -$skipToId [role="menuitem"].hover .label { +$skipToId [role="menuitem"]:focus .level, +$skipToId [role="menuitem"]:focus .label { background-color: $menuitemFocusBackgroundColor; color: $menuitemFocusTextColor; } @@ -533,8 +499,7 @@ $skipToId [role="menuitem"].hover .label { updateStyle('$fontSize', config.fontSize, theme.fontSize, defaultTheme.fontSize); updateStyle('$positionLeft', config.positionLeft, theme.positionLeft, defaultTheme.positionLeft); - updateStyle('$smallBreakPoint', config.smallBreakPoint, theme.smallBreakPoint, defaultTheme.smallBreakPoint); - updateStyle('$mediumBreakPoint', config.mediumBreakPoint, theme.mediumBreakPoint, defaultTheme.mediumBreakPoint); + updateStyle('$mediaBreakPoint', config.mediaBreakPoint, theme.mediaBreakPoint, defaultTheme.mediaBreakPoint); updateStyle('$menuTextColor', config.menuTextColor, theme.menuTextColor, defaultTheme.menuTextColor); updateStyle('$menuBackgroundColor', config.menuBackgroundColor, theme.menuBackgroundColor, defaultTheme.menuBackgroundColor); @@ -567,453 +532,453 @@ $skipToId [role="menuitem"].hover .label { styleNode.id = `${skipToId}-style`; const headNode = document.getElementsByTagName('head')[0]; headNode.appendChild(styleNode); - } - - /* utils.js */ - - /* Constants */ - const debug$5 = new DebugLogging('Utils', false); - debug$5.flag = false; - - - /* - * @function getAttributeValue - * - * @desc Return attribute value if present on element, - * otherwise return empty string. - * - * @returns {String} see @desc - */ - function getAttributeValue (element, attribute) { - let value = element.getAttribute(attribute); - return (value === null) ? '' : normalize(value); - } - - /* - * @function normalize - * - * @desc Trim leading and trailing whitespace and condense all - * internal sequences of whitespace to a single space. Adapted from - * Mozilla documentation on String.prototype.trim polyfill. Handles - * BOM and NBSP characters. - * - * @return {String} see @desc - */ - function normalize (s) { - let rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g; - return s.replace(rtrim, '').replace(/\s+/g, ' '); - } - - /** - * @fuction isNotEmptyString - * - * @desc Returns true if the string has content, otherwise false - * - * @param {Boolean} see @desc - */ - function isNotEmptyString (str) { - return (typeof str === 'string') && str.length && str.trim() && str !== " "; - } - - /** - * @fuction isVisible - * - * @desc Returns true if the element is visible in the graphical rendering - * - * @param {node} elem - DOM element node of a labelable element - */ - function isVisible (element) { - - function isDisplayNone(el) { - if (!el || (el.nodeType !== Node.ELEMENT_NODE)) { - return false; - } - - if (el.hasAttribute('hidden')) { - return true; - } - - const style = window.getComputedStyle(el, null); - const display = style.getPropertyValue("display"); - if (display === 'none') { - return true; - } - - // check ancestors for display none - if (el.parentNode) { - return isDisplayNone(el.parentNode); - } - - return false; - } - - const computedStyle = window.getComputedStyle(element); - let visibility = computedStyle.getPropertyValue('visibility'); - if ((visibility === 'hidden') || (visibility === 'collapse')) { - return false; - } - - return !isDisplayNone(element); - } - - /* - * namefrom.js - */ - - /* constants */ - - const debug$4 = new DebugLogging('nameFrom', false); - debug$4.flag = false; - - // - // LOW-LEVEL HELPER FUNCTIONS (NOT EXPORTED) - - /* - * @function isDisplayNone - * - * @desc Returns true if the element or parent element has set the CSS - * display property to none or has the hidden attribute, - * otherwise false - * - * @param {Object} node - a DOM node - * - * @returns {Boolean} see @desc - */ - - function isDisplayNone (node) { - - if (!node) { - return false; - } - - if (node.nodeType === Node.TEXT_NODE) { - node = node.parentNode; - } - - if (node.nodeType === Node.ELEMENT_NODE) { - - if (node.hasAttribute('hidden')) { - return true; - } - - // aria-hidden attribute with the value "true" is an same as - // setting the hidden attribute for name calcuation - if (node.hasAttribute('aria-hidden')) { - if (node.getAttribute('aria-hidden').toLowerCase() === 'true') { - return true; - } - } - - const style = window.getComputedStyle(node, null); - - const display = style.getPropertyValue("display"); - - if (display) { - return display === 'none'; - } - } - return false; - } - - /* - * @function isVisibilityHidden - * - * @desc Returns true if the node (or it's parrent) has the CSS visibility - * property set to "hidden" or "collapse", otherwise false - * - * @param {Object} node - DOM node - * - * @return see @desc - */ - - function isVisibilityHidden(node) { - - if (!node) { - return false; - } - - if (node.nodeType === Node.TEXT_NODE) { - node = node.parentNode; - } - - if (node.nodeType === Node.ELEMENT_NODE) { - const style = window.getComputedStyle(node, null); - - const visibility = style.getPropertyValue("visibility"); - if (visibility) { - return (visibility === 'hidden') || (visibility === 'collapse'); - } - } - return false; - } - - /* - * @function isAriaHiddenFalse - * - * @desc Returns true if the node has the aria-hidden property set to - * "false", otherwise false. - * NOTE: This function is important in the accessible namce - * calculation, since content hidden with a CSS technique - * can be included in the accessible name calculation when - * aria-hidden is set to false - * - * @param {Object} node - DOM node - * - * @return see @desc - */ - - function isAriaHIddenFalse(node) { - - if (!node) { - return false; - } - - if (node.nodeType === Node.TEXT_NODE) { - node = node.parentNode; - } - - if (node.nodeType === Node.ELEMENT_NODE) { - return (node.hasAttribute('aria-hidden') && - (node.getAttribute('aria-hidden').toLowerCase() === 'false')); - } - - return false; - } - - /* - * @function includeContentInName - * - * @desc Checks the CSS display and hidden properties, and - * the aria-hidden property to see if the content - * should be included in the accessible name - * calculation. Returns true if it should be - * included, otherwise false - * - * @param {Object} node - DOM node - * - * @return see @desc - */ - - function includeContentInName(node) { - const flag = isAriaHIddenFalse(node) || - (!isVisibilityHidden(node) && - !isDisplayNone(node)); - return flag; - } - - /* - * @function getNodeContents - * - * @desc Recursively process element and text nodes by aggregating - * their text values for an ARIA accessible name or description - * calculation. - * - * NOTE: This includes special handling of elements with 'alt' - * text and embedded controls. - * - * @param {Object} node - A DOM node - * - * @return {String} The text content for an accessible name or description - */ - function getNodeContents (node) { - let contents = ''; - let nc; - let arr = []; - - switch (node.nodeType) { - case Node.ELEMENT_NODE: - // If aria-label is present, node recursion stops and - // aria-label value is returned - if (node.hasAttribute('aria-label')) { - if (includeContentInName(node)) { - contents = node.getAttribute('aria-label'); - } - } - else { - if (node instanceof HTMLSlotElement) { - // if no slotted elements, check for default slotted content - const assignedNodes = node.assignedNodes().length ? node.assignedNodes() : node.assignedNodes({ flatten: true }); - assignedNodes.forEach( assignedNode => { - nc = getNodeContents(assignedNode); - if (nc.length) arr.push(nc); - }); - contents = (arr.length) ? arr.join(' ') : ''; - } else { - if (couldHaveAltText(node) && includeContentInName(node)) { - contents = getAttributeValue(node, 'alt'); - } - else { - if (node.hasChildNodes()) { - let children = Array.from(node.childNodes); - children.forEach( child => { - nc = getNodeContents(child); - if (nc.length) arr.push(nc); - }); - contents = (arr.length) ? arr.join(' ') : ''; - } - } - // For all branches of the ELEMENT_NODE case... - } - } - contents = addCssGeneratedContent(node, contents); - break; - - case Node.TEXT_NODE: - if (includeContentInName(node)) { - contents = normalize(node.textContent); - } - break; - } - - return contents; - } - - /* - * @function couldHaveAltText - * - * @desc Based on HTML5 specification, returns true if - * the element could have an 'alt' attribute, - * otherwise false. - * - * @param {Object} element - DOM eleemnt node - * - * @return {Boolean} see @desc - */ - function couldHaveAltText (element) { - let tagName = element.tagName.toLowerCase(); - - switch (tagName) { - case 'img': - case 'area': - return true; - case 'input': - return (element.type && element.type === 'image'); - } - - return false; - } - - /* - * @function addCssGeneratedContent - * - * @desc Adds CSS-generated content for pseudo-elements - * :before and :after. According to the CSS spec, test that content - * value is other than the default computed value of 'none'. - * - * Note: Even if an author specifies content: 'none', because browsers - * add the double-quote character to the beginning and end of - * computed string values, the result cannot and will not be - * equal to 'none'. - * - * - * @param {Object} element - DOM node element - * @param {String} contents - Text content for DOM node - * - * @returns {String} see @desc - * - */ - function addCssGeneratedContent (element, contents) { - - let result = contents, - prefix = getComputedStyle(element, ':before').content, - suffix = getComputedStyle(element, ':after').content; - - if ((prefix[0] === '"') && !prefix.toLowerCase().includes('moz-')) { - result = prefix.substring(1, (prefix.length-1)) + result; - } - - if ((suffix[0] === '"') && !suffix.toLowerCase().includes('moz-')) { - result = result + suffix.substring(1, (suffix.length-1)) ; - } - - return result; - } - - /* accName.js */ - - /* Constants */ - const debug$3 = new DebugLogging('accName', false); - debug$3.flag = false; - - /** - * @fuction getAccessibleName - * - * @desc Returns the accessible name for an heading or landamrk - * - * @paramn {Object} dom - Document of the current element - * @param {node} element - DOM element node for either a heading or - * landmark - * @param {Boolean} fromContent - if true will compute name from content - * - * @return {String} The accessible name for the landmark or heading element - */ - - function getAccessibleName (doc, element, fromContent=false) { - let accName = ''; - - accName = nameFromAttributeIdRefs(doc, element, 'aria-labelledby'); - - if (accName === '' && element.hasAttribute('aria-label')) { - accName = element.getAttribute('aria-label').trim(); - } - - if (accName === '' && fromContent) { - accName = getNodeContents(element); - } - - if (accName === '' && element.title.trim() !== '') { - accName = element.title.trim(); - } - - return accName; - } - - /* - * @function nameFromAttributeIdRefs - * - * @desc Get the value of attrName on element (a space- - * separated list of IDREFs), visit each referenced element in the order it - * appears in the list and obtain its accessible name (skipping recursive - * aria-labelledby or aria-describedby calculations), and return an object - * with name property set to a string that is a space-separated concatena- - * tion of those results if any, otherwise return empty string. - * - * @param {Object} doc - Browser document object - * @param {Object} element - DOM element node - * @param {String} attribute - Attribute name (e.g. "aria-labelledby", "aria-describedby", - * or "aria-errormessage") - * - * @returns {String} see @desc - */ - function nameFromAttributeIdRefs (doc, element, attribute) { - const value = getAttributeValue(element, attribute); - const arr = []; - - if (value.length) { - const idRefs = value.split(' '); - - for (let i = 0; i < idRefs.length; i++) { - const refElement = doc.getElementById(idRefs[i]); - if (refElement) { - const accName = getNodeContents(refElement); - if (accName && accName.length) arr.push(accName); - } - } - } - - if (arr.length) { - return arr.join(' '); - } - - return ''; - } - - /* landmarksHeadings.js */ + } + + /* utils.js */ /* Constants */ - const debug$2 = new DebugLogging('landmarksHeadings', false); + const debug$4 = new DebugLogging('Utils', false); + debug$4.flag = false; + + + /* + * @function getAttributeValue + * + * @desc Return attribute value if present on element, + * otherwise return empty string. + * + * @returns {String} see @desc + */ + function getAttributeValue (element, attribute) { + let value = element.getAttribute(attribute); + return (value === null) ? '' : normalize(value); + } + + /* + * @function normalize + * + * @desc Trim leading and trailing whitespace and condense all + * internal sequences of whitespace to a single space. Adapted from + * Mozilla documentation on String.prototype.trim polyfill. Handles + * BOM and NBSP characters. + * + * @return {String} see @desc + */ + function normalize (s) { + let rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g; + return s.replace(rtrim, '').replace(/\s+/g, ' '); + } + + /** + * @fuction isNotEmptyString + * + * @desc Returns true if the string has content, otherwise false + * + * @param {Boolean} see @desc + */ + function isNotEmptyString (str) { + return (typeof str === 'string') && str.length && str.trim() && str !== " "; + } + + /** + * @fuction isVisible + * + * @desc Returns true if the element is visible in the graphical rendering + * + * @param {node} elem - DOM element node of a labelable element + */ + function isVisible (element) { + + function isDisplayNone(el) { + if (!el || (el.nodeType !== Node.ELEMENT_NODE)) { + return false; + } + + if (el.hasAttribute('hidden')) { + return true; + } + + const style = window.getComputedStyle(el, null); + const display = style.getPropertyValue("display"); + if (display === 'none') { + return true; + } + + // check ancestors for display none + if (el.parentNode) { + return isDisplayNone(el.parentNode); + } + + return false; + } + + const computedStyle = window.getComputedStyle(element); + let visibility = computedStyle.getPropertyValue('visibility'); + if ((visibility === 'hidden') || (visibility === 'collapse')) { + return false; + } + + return !isDisplayNone(element); + } + + /* + * namefrom.js + */ + + /* constants */ + + const debug$3 = new DebugLogging('nameFrom', false); + debug$3.flag = false; + + // + // LOW-LEVEL HELPER FUNCTIONS (NOT EXPORTED) + + /* + * @function isDisplayNone + * + * @desc Returns true if the element or parent element has set the CSS + * display property to none or has the hidden attribute, + * otherwise false + * + * @param {Object} node - a DOM node + * + * @returns {Boolean} see @desc + */ + + function isDisplayNone (node) { + + if (!node) { + return false; + } + + if (node.nodeType === Node.TEXT_NODE) { + node = node.parentNode; + } + + if (node.nodeType === Node.ELEMENT_NODE) { + + if (node.hasAttribute('hidden')) { + return true; + } + + // aria-hidden attribute with the value "true" is an same as + // setting the hidden attribute for name calcuation + if (node.hasAttribute('aria-hidden')) { + if (node.getAttribute('aria-hidden').toLowerCase() === 'true') { + return true; + } + } + + const style = window.getComputedStyle(node, null); + + const display = style.getPropertyValue("display"); + + if (display) { + return display === 'none'; + } + } + return false; + } + + /* + * @function isVisibilityHidden + * + * @desc Returns true if the node (or it's parrent) has the CSS visibility + * property set to "hidden" or "collapse", otherwise false + * + * @param {Object} node - DOM node + * + * @return see @desc + */ + + function isVisibilityHidden(node) { + + if (!node) { + return false; + } + + if (node.nodeType === Node.TEXT_NODE) { + node = node.parentNode; + } + + if (node.nodeType === Node.ELEMENT_NODE) { + const style = window.getComputedStyle(node, null); + + const visibility = style.getPropertyValue("visibility"); + if (visibility) { + return (visibility === 'hidden') || (visibility === 'collapse'); + } + } + return false; + } + + /* + * @function isAriaHiddenFalse + * + * @desc Returns true if the node has the aria-hidden property set to + * "false", otherwise false. + * NOTE: This function is important in the accessible namce + * calculation, since content hidden with a CSS technique + * can be included in the accessible name calculation when + * aria-hidden is set to false + * + * @param {Object} node - DOM node + * + * @return see @desc + */ + + function isAriaHIddenFalse(node) { + + if (!node) { + return false; + } + + if (node.nodeType === Node.TEXT_NODE) { + node = node.parentNode; + } + + if (node.nodeType === Node.ELEMENT_NODE) { + return (node.hasAttribute('aria-hidden') && + (node.getAttribute('aria-hidden').toLowerCase() === 'false')); + } + + return false; + } + + /* + * @function includeContentInName + * + * @desc Checks the CSS display and hidden properties, and + * the aria-hidden property to see if the content + * should be included in the accessible name + * calculation. Returns true if it should be + * included, otherwise false + * + * @param {Object} node - DOM node + * + * @return see @desc + */ + + function includeContentInName(node) { + const flag = isAriaHIddenFalse(node) || + (!isVisibilityHidden(node) && + !isDisplayNone(node)); + return flag; + } + + /* + * @function getNodeContents + * + * @desc Recursively process element and text nodes by aggregating + * their text values for an ARIA accessible name or description + * calculation. + * + * NOTE: This includes special handling of elements with 'alt' + * text and embedded controls. + * + * @param {Object} node - A DOM node + * + * @return {String} The text content for an accessible name or description + */ + function getNodeContents (node) { + let contents = ''; + let nc; + let arr = []; + + switch (node.nodeType) { + case Node.ELEMENT_NODE: + // If aria-label is present, node recursion stops and + // aria-label value is returned + if (node.hasAttribute('aria-label')) { + if (includeContentInName(node)) { + contents = node.getAttribute('aria-label'); + } + } + else { + if (node instanceof HTMLSlotElement) { + // if no slotted elements, check for default slotted content + const assignedNodes = node.assignedNodes().length ? node.assignedNodes() : node.assignedNodes({ flatten: true }); + assignedNodes.forEach( assignedNode => { + nc = getNodeContents(assignedNode); + if (nc.length) arr.push(nc); + }); + contents = (arr.length) ? arr.join(' ') : ''; + } else { + if (couldHaveAltText(node) && includeContentInName(node)) { + contents = getAttributeValue(node, 'alt'); + } + else { + if (node.hasChildNodes()) { + let children = Array.from(node.childNodes); + children.forEach( child => { + nc = getNodeContents(child); + if (nc.length) arr.push(nc); + }); + contents = (arr.length) ? arr.join(' ') : ''; + } + } + // For all branches of the ELEMENT_NODE case... + } + } + contents = addCssGeneratedContent(node, contents); + break; + + case Node.TEXT_NODE: + if (includeContentInName(node)) { + contents = normalize(node.textContent); + } + break; + } + + return contents; + } + + /* + * @function couldHaveAltText + * + * @desc Based on HTML5 specification, returns true if + * the element could have an 'alt' attribute, + * otherwise false. + * + * @param {Object} element - DOM eleemnt node + * + * @return {Boolean} see @desc + */ + function couldHaveAltText (element) { + let tagName = element.tagName.toLowerCase(); + + switch (tagName) { + case 'img': + case 'area': + return true; + case 'input': + return (element.type && element.type === 'image'); + } + + return false; + } + + /* + * @function addCssGeneratedContent + * + * @desc Adds CSS-generated content for pseudo-elements + * :before and :after. According to the CSS spec, test that content + * value is other than the default computed value of 'none'. + * + * Note: Even if an author specifies content: 'none', because browsers + * add the double-quote character to the beginning and end of + * computed string values, the result cannot and will not be + * equal to 'none'. + * + * + * @param {Object} element - DOM node element + * @param {String} contents - Text content for DOM node + * + * @returns {String} see @desc + * + */ + function addCssGeneratedContent (element, contents) { + + let result = contents, + prefix = getComputedStyle(element, ':before').content, + suffix = getComputedStyle(element, ':after').content; + + if ((prefix[0] === '"') && !prefix.toLowerCase().includes('moz-')) { + result = prefix.substring(1, (prefix.length-1)) + result; + } + + if ((suffix[0] === '"') && !suffix.toLowerCase().includes('moz-')) { + result = result + suffix.substring(1, (suffix.length-1)) ; + } + + return result; + } + + /* accName.js */ + + /* Constants */ + const debug$2 = new DebugLogging('accName', false); debug$2.flag = false; + /** + * @fuction getAccessibleName + * + * @desc Returns the accessible name for an heading or landamrk + * + * @paramn {Object} dom - Document of the current element + * @param {node} element - DOM element node for either a heading or + * landmark + * @param {Boolean} fromContent - if true will compute name from content + * + * @return {String} The accessible name for the landmark or heading element + */ + + function getAccessibleName (doc, element, fromContent=false) { + let accName = ''; + + accName = nameFromAttributeIdRefs(doc, element, 'aria-labelledby'); + + if (accName === '' && element.hasAttribute('aria-label')) { + accName = element.getAttribute('aria-label').trim(); + } + + if (accName === '' && fromContent) { + accName = getNodeContents(element); + } + + if (accName === '' && element.title.trim() !== '') { + accName = element.title.trim(); + } + + return accName; + } + + /* + * @function nameFromAttributeIdRefs + * + * @desc Get the value of attrName on element (a space- + * separated list of IDREFs), visit each referenced element in the order it + * appears in the list and obtain its accessible name (skipping recursive + * aria-labelledby or aria-describedby calculations), and return an object + * with name property set to a string that is a space-separated concatena- + * tion of those results if any, otherwise return empty string. + * + * @param {Object} doc - Browser document object + * @param {Object} element - DOM element node + * @param {String} attribute - Attribute name (e.g. "aria-labelledby", "aria-describedby", + * or "aria-errormessage") + * + * @returns {String} see @desc + */ + function nameFromAttributeIdRefs (doc, element, attribute) { + const value = getAttributeValue(element, attribute); + const arr = []; + + if (value.length) { + const idRefs = value.split(' '); + + for (let i = 0; i < idRefs.length; i++) { + const refElement = doc.getElementById(idRefs[i]); + if (refElement) { + const accName = getNodeContents(refElement); + if (accName && accName.length) arr.push(accName); + } + } + } + + if (arr.length) { + return arr.join(' '); + } + + return ''; + } + + /* landmarksHeadings.js */ + + /* Constants */ + const debug$1 = new DebugLogging('landmarksHeadings', false); + debug$1.flag = false; + const skipableElements = [ 'base', 'content', @@ -1824,13 +1789,13 @@ $skipToId [role="menuitem"].hover .label { } } return [].concat(mainElements, searchElements, navElements, asideElements, regionElements, footerElements, otherElements); - } - + } + /* skiptoMenuButton.js */ /* Constants */ - const debug$1 = new DebugLogging('SkipToButton', false); - debug$1.flag = false; + const debug = new DebugLogging('SkipToButton', false); + debug.flag = false; /** * @class SkiptoMenuButton @@ -1886,19 +1851,14 @@ $skipToId [role="menuitem"].hover .label { this.containerNode.appendChild(this.buttonNode); this.buttonTextNode = document.createElement('span'); - this.buttonTextNode.classList.add('skipto-text'); + this.buttonTextNode.classList.add('text'); this.buttonTextNode.textContent = buttonVisibleLabel; this.buttonNode.appendChild(this.buttonTextNode); - const smallButtonNode = document.createElement('span'); - smallButtonNode.classList.add('skipto-small'); - smallButtonNode.textContent = config.smallButtonLabel; - this.buttonNode.appendChild(smallButtonNode); - - const mediumButtonNode = document.createElement('span'); - mediumButtonNode.classList.add('skipto-medium'); - mediumButtonNode.textContent = config.buttonLabel; - this.buttonNode.appendChild(mediumButtonNode); + const imageNode = document.createElement('img'); + imageNode.src = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAALEwAACxMBAJqcGAAAAVlpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IlhNUCBDb3JlIDUuNC4wIj4KICAgPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICAgICAgPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIKICAgICAgICAgICAgeG1sbnM6dGlmZj0iaHR0cDovL25zLmFkb2JlLmNvbS90aWZmLzEuMC8iPgogICAgICAgICA8dGlmZjpPcmllbnRhdGlvbj4xPC90aWZmOk9yaWVudGF0aW9uPgogICAgICA8L3JkZjpEZXNjcmlwdGlvbj4KICAgPC9yZGY6UkRGPgo8L3g6eG1wbWV0YT4KTMInWQAABP5JREFUWAm9V11MXEUUnjN7KbuwKD/L8qOp2kpbWCSFpkZN2qIxjQYWaC2JD/4kxsTE1MQHeedBY6K+mPSpJsakhcSgFaQk+qCh1DZRQmioSypVa0q1IKC1bFlw753jN5e9txfobmkBJ1nm3DPnb87fHEhkWGWVBx9QYqFCkSxk4nuFYMpA7jkilkIsMHNCGnI8m9VPl8/3/e0hcMEVAosjTc+wsl5iomcFc75LuTYA4ug7IajDZ+Qfnxw5dsMR5xoQqm0qF/OqgwXXO4cbscOQCwb5npuI9Yxq+bYBJTUHwqaZHMSNN99USjNE4jQoxuD5KeBnBSm+eZ4BYglWsQkhCzKLLWB6DHC1wwEj4j6DaiZHei8ZGmklkx+CwFZOgqalT772+qG67vb2duUwrXUPVzfVKMv6GMbsQm4ETVO8D5mHqHxXNLSQUFcFC23MdSmz9k3Fus+tVeGt+IsjrUFWc2MwokyfS5lTZiQTYndKuY5H33LlYCplv+Xmyq0Ep8PRvI+nYl0TzjngeKiqoQP63lrEzT9sIK5hIFKLhh1I76HKxjal5t4Tc17s6mEtNlQVbZse7f3A5SJx0dWn+D5DsbwmhBvqCpcQQDDb+ChuKmTrauvfy61h4qAhz0x70JJ8PYpUm5aZFaBvRElN9KGiyga2f1UN46W1rcUe+g0BkdwSiWiH1f4TqmocAGKP1oYqGPEZfGDyfN+vG6J9mVDbgNKqaMRkHkQTCtjnTBa8960U8hRyZAxljT4g44IsN1uWyVn6SRJ0dEP4xOzh5rqJTOXsZnf4kcbHlcUn0DhKl0pb2xc8mkC8R5nk11lCdE6M9sa8El0DNFLXqVCJN+GJF3GFbV7CdYK1Z45RoTw8febLWS1ziQFeJeHqlq2CrZ2WEBUkuBBt+p5M9F5eJDgJKfwot3xcBnJoB7yAB3JxoRUPEwX26r6Q1gCHeD32+yOthfNi/mVc4m0ke44tk+jIzOjJN/4XA5xLhKuj+y3FvTBkE7ww5zMKwrYBoUj0VSAr/RR450qs6y+HYSN2tOJuJHpzygtPG7oRmUl1FAhaEAndtN51FBdVNp5A/PGU3uXCs5uKtfseoCq+R14sGsBUbiAmuuxsT6Ahl3tV+SR1YAIY8eLuDObZzf6CGT1MOIsFwcOL7QTtwm/PA87h8j2nuKgvPnv97HL8ar+DlkoMDR1NZqLPaED8z+mz8FBtJgGZzvQIVV7d/OAfP/aMp6PLaABmtxdMqbakY749nuOZlGt+tzncXtjGUBiWItNNCko9RildJlvHWd19CJDbK0NAnOsMJKiIpJFNcmLBGUiY6rz3DIZDTyAJC7y4O4F1Ev52rhsDz5L1qPNFQl0lPRwc+XTwCgqjDD3bkpKfnBrtO+0QredeHGnZycocQB/IQ+EnA5RT6uvv7+dAeHsRFO0BUj/kB3NLtsfmpsbG1lN5KNIYFUp9DuW2R0lS50Ssu9NuQBjNc/5N8BBKDq9WahFdRhccQBx/JhbX8MDNI6SLHcShSbdjggFtALEuwA9zJu2DYrfJ4fBSdl7W7t9/+GLGfYz0LGguzH2GPr03ndz1wEP5KZKB551x3TXAEV5UGW0h4lfwOO3HdbMd/Fp2/fLBu1/B7Z9Mx072emWtMMA5rK9vNy5MDW8zhbUVLsxDqebil5be4VvcETQ9ExL9g3/rf3lqh/9iV1cXZpuV6z8QFu9El3GwrAAAAABJRU5ErkJggg=="; + imageNode.setAttribute('alt', ''); + this.buttonNode.appendChild(imageNode); // Create menu container @@ -1946,8 +1906,6 @@ $skipToId [role="menuitem"].hover .label { attachNode.insertBefore(this.containerNode, attachNode.firstElementChild); - this.focusMenuitem = null; - return this.containerNode; } @@ -2061,8 +2019,8 @@ $skipToId [role="menuitem"].hover .label { /* * @method updateMenuitems * - * @desc Updates the menu information with the current menu items - * used for menu navigation commands + * @desc Updates the menu information with the current manu items + * used for menu navgation commands */ updateMenuitems () { let menuitemNodes = this.menuNode.querySelectorAll('[role=menuitem'); @@ -2105,7 +2063,7 @@ $skipToId [role="menuitem"].hover .label { menuitemNode.addEventListener('keydown', this.handleMenuitemKeydown.bind(this)); menuitemNode.addEventListener('click', this.handleMenuitemClick.bind(this)); menuitemNode.addEventListener('pointerenter', this.handleMenuitemPointerenter.bind(this)); - menuitemNode.addEventListener('pointerleave', this.handleMenuitemPointerleave.bind(this)); + groupNode.appendChild(menuitemNode); // add heading level and label @@ -2214,10 +2172,7 @@ $skipToId [role="menuitem"].hover .label { */ setFocusToMenuitem(menuitem) { if (menuitem) { - this.removeHoverClass(); - menuitem.classList.add('hover'); menuitem.focus(); - this.focusMenuitem = menuitem; } } @@ -2386,18 +2341,7 @@ $skipToId [role="menuitem"].hover .label { isOpen() { return this.buttonNode.getAttribute('aria-expanded') === 'true'; } - - /* - * @method removeHoverClass - * - * @desc Removes hover class for menuitems - */ - removeHoverClass() { - this.menuitemNodes.forEach( node => { - node.classList.remove('hover'); - }); - } - + // Menu event handlers handleFocusin() { @@ -2452,51 +2396,34 @@ $skipToId [role="menuitem"].hover .label { } handleDocumentKeydown (event) { + let key = event.key, + flag = false; - const enabledInputTypes = [ - 'button', - 'checkbox', - 'color', - 'file', - 'image', - 'radio', - 'range', - 'reset', - 'submit' - ]; - - const target = event.target; - const tagName = target.tagName ? target.tagName.toLowerCase() : ''; - const type = tagName === 'input' ? target.type.toLowerCase() : ''; - - if ((tagName !== 'textarea') && - ((tagName !== 'input') || - ((tagName === 'input') && enabledInputTypes.includes(type)) - )) { - - const altPressed = - this.usesAltKey && - event.altKey && - !event.ctrlKey && - !event.shiftKey && - !event.metaKey; - - const optionPressed = - this.usesOptionKey && - event.altKey && - !event.ctrlKey && - !event.shiftKey && - !event.metaKey; - - if ((optionPressed && this.config.optionShortcut === event.key) || - (altPressed && this.config.altShortcut === event.key) || - ((optionPressed || altPressed) && (48 === event.keyCode)) - ) { - this.openPopup(); - this.setFocusToFirstMenuitem(); - event.stopPropagation(); - event.preventDefault(); - } + let altPressed = + this.usesAltKey && + event.altKey && + !event.ctrlKey && + !event.shiftKey && + !event.metaKey; + + let optionPressed = + this.usesOptionKey && + event.altKey && + !event.ctrlKey && + !event.shiftKey && + !event.metaKey; + + if ( + (optionPressed && this.config.optionShortcut === key) || + (altPressed && this.config.altShortcut === key) + ) { + this.openPopup(); + this.setFocusToFirstMenuitem(); + flag = true; + } + if (flag) { + event.stopPropagation(); + event.preventDefault(); } } @@ -2592,13 +2519,7 @@ $skipToId [role="menuitem"].hover .label { handleMenuitemPointerenter(event) { let tgt = event.currentTarget; - this.removeHoverClass(); - tgt.classList.add('hover'); - } - - handleMenuitemPointerleave(event) { - let tgt = event.currentTarget; - tgt.classList.remove('hover'); + tgt.focus(); } handleBackgroundPointerdown(event) { @@ -2609,11 +2530,7 @@ $skipToId [role="menuitem"].hover .label { } } } - } - - /* constants */ - const debug = new DebugLogging('skipto', false); - debug.flag = true; + } (function() { @@ -2637,15 +2554,14 @@ $skipToId [role="menuitem"].hover .label { altShortcut: '0', // default shortcut key is the number zero optionShortcut: 'º', // default shortcut key character associated with option+0 on mac attachElement: 'body', - displayOption: 'popup', // Line edited by pre-build script, fixed + displayOption: 'popup', // options: static (default), popup, fixed // container element, use containerClass for custom styling - containerElement: 'nav', + containerElement: 'div', containerRole: '', customClass: '', // Button labels and messages buttonLabel: 'Skip To Content', - smallButtonLabel: 'SkipTo', altLabel: 'Alt', optionLabel: 'Option', buttonShortcut: ' ($modifier+$key)', @@ -2673,12 +2589,11 @@ $skipToId [role="menuitem"].hover .label { headings: 'main h1 h2', // Place holders for configuration - colorTheme: '', + colorTheme: 'aria', fontFamily: '', fontSize: '', positionLeft: '', - smallBreakPoint: '', - mediumBreakPoint: '', + mediaBreakPoint: '', menuTextColor: '', menuBackgroundColor: '', menuitemFocusTextColor: '', @@ -2693,8 +2608,7 @@ $skipToId [role="menuitem"].hover .label { fontFamily: 'inherit', fontSize: 'inherit', positionLeft: '46%', - smallBreakPoint: '576', - mediumBreakPoint: '992', + mediaBreakPoint: '540', menuTextColor: '#1a1a1a', menuBackgroundColor: '#dcdcdc', menuitemFocusTextColor: '#eeeeee', @@ -2768,17 +2682,6 @@ $skipToId [role="menuitem"].hover .label { focusBorderColor: '#dd3444', buttonTextColor: '#fff', buttonBackgroundColor: '#036', - }, - 'openweba11y': { - hostnameSelector: 'openweba11y.com', - buttonTextColor: '#13294B', - buttonBackgroundColor: '#dddddd', - focusBorderColor: '#C5050C', - menuTextColor: '#13294B', - menuBackgroundColor: '#dddddd', - menuitemFocusTextColor: '#dddddd', - menuitemFocusBackgroundColor: '#13294B', - fontSize: '90%' } }, @@ -2791,7 +2694,7 @@ $skipToId [role="menuitem"].hover .label { * @param {object} config - Reference to configuration object * can be undefined */ - init: function(globalConfig) { + init: function(config) { let node; // Check if skipto is already loaded @@ -2803,13 +2706,9 @@ $skipToId [role="menuitem"].hover .label { document.skipToHasBeenLoaded = true; let attachElement = document.body; - - if (globalConfig) { - this.config = this.setupConfigFromGlobal(this.config, globalConfig); + if (config) { + this.setupConfig(config); } - - this.config = this.setupConfigFromDataAttribute(this.config); - if (typeof this.config.attachElement === 'string') { node = document.querySelector(this.config.attachElement); if (node && node.nodeType === Node.ELEMENT_NODE) { @@ -2823,92 +2722,45 @@ $skipToId [role="menuitem"].hover .label { }, /* - * @method setupConfigFromGlobal + * @method setupConfig * - * @desc Get configuration information from author configuration to change + * @desc Get configuration information from user configuration to change * default settings * - * @param {object} config - Javascript object with default configuration information - * @param {object} globalConfig - Javascript object with configuration information oin a global variable + * @param {object} appConfig - Javascript object with configuration information */ - setupConfigFromGlobal: function(config, globalConfig) { - let authorConfig = {}; + setupConfig: function(appConfig) { + let appConfigSettings; // Support version 4.1 configuration object structure // If found use it - if ((typeof globalConfig.settings === 'object') && - (typeof globalConfig.settings.skipTo === 'object')) { - authorConfig = globalConfig.settings.skipTo; + if ((typeof appConfig.settings === 'object') && + (typeof appConfig.settings.skipTo === 'object')) { + appConfigSettings = appConfig.settings.skipTo; } else { // Version 5.0 removes the requirement for the "settings" and "skipto" properties // to reduce the complexity of configuring skipto - if (typeof globalConfig === 'object') { - authorConfig = globalConfig; - } - } - - for (const name in authorConfig) { - //overwrite values of our local config, based on the external config - if ((typeof config[name] !== 'undefined') && - ((typeof authorConfig[name] === 'string') && - (authorConfig[name].length > 0 ) || - typeof authorConfig[name] === 'boolean') - ) { - config[name] = authorConfig[name]; - } else { - console.warn('[SkipTo]: Unsupported or deprecated configuration option in global configuration object: ' + name); + if ((typeof appConfig === 'undefined') || + (typeof appConfig !== 'object')) { + appConfigSettings = {}; } - } - - return config; - }, - - /* - * @method setupConfigFromDataAttribute - * - * @desc Get configuration information from author configuration to change - * default settings - * - * @param {object} config - Javascript object with default configuration information - */ - setupConfigFromDataAttribute: function(config) { - let dataConfig = {}; - - // Check for data-skipto attribute values for configuration - const configElem = document.querySelector('[data-skipto]'); - if (configElem) { - const dataSkiptoValue = configElem.getAttribute('data-skipto'); - if (dataSkiptoValue) { - const values = dataSkiptoValue.split(';'); - values.forEach( v => { - let [prop, value] = v.split(':'); - if (prop) { - prop = prop.trim(); - } - if (value) { - value = value.trim(); - } - if (prop && value) { - dataConfig[prop] = value; - } - }); + else { + appConfigSettings = appConfig; } } - for (const name in dataConfig) { + for (const name in appConfigSettings) { //overwrite values of our local config, based on the external config - if ((typeof config[name] !== 'undefined') && - ((typeof dataConfig[name] === 'string') && - (dataConfig[name].length > 0 ) || - typeof dataConfig[name] === 'boolean') + if ((typeof this.config[name] !== 'undefined') && + ((typeof appConfigSettings[name] === 'string') && + (appConfigSettings[name].length > 0 ) || + typeof appConfigSettings[name] === 'boolean') ) { - config[name] = dataConfig[name]; + this.config[name] = appConfigSettings[name]; } else { - console.warn('[SkipTo]: Unsupported or deprecated configuration option in data-skipto attribute: ' + name); + console.warn('[SkipTo]: Unsuported or deprecated configuration option "' + name + '".'); } } - return config; - } }; @@ -2916,6 +2768,6 @@ $skipToId [role="menuitem"].hover .label { window.addEventListener('load', function() { SkipTo.init(window.SkipToConfig); }); - })(); - -})(); + })(); + +})();