@surea11y/core 1.4.0 → 1.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +41 -0
- package/bin/surea11y-core.js +0 -0
- package/docs/ARIA_DEPRECATION.md +95 -0
- package/docs/RULE_CATALOG.md +6 -6
- package/package.json +3 -1
- package/src/checks/automatic/aria-allowed-attr.js +648 -83
- package/src/checks/automatic/aria-deprecated-role.js +105 -37
- package/src/checks/automatic/aria-roles-valid.js +31 -6
- package/src/checks/automatic/autocomplete-valid.js +37 -1
- package/src/checks/automatic/avoid-inline-spacing.js +179 -19
- package/src/checks/automatic/binary-control-name-present.js +11 -3
- package/src/checks/automatic/button-name-present.js +50 -17
- package/src/checks/automatic/canvas-text-alternative-present.js +15 -8
- package/src/checks/automatic/combobox-name-present.js +8 -1
- package/src/checks/automatic/dialog-name-present.js +8 -1
- package/src/checks/automatic/form-control-programmatic-label-present.js +48 -4
- package/src/checks/automatic/form-control-single-label.js +107 -39
- package/src/checks/automatic/img-alt-present.js +16 -9
- package/src/checks/automatic/input-image-alt-present.js +99 -48
- package/src/checks/automatic/label-in-name.js +82 -8
- package/src/checks/automatic/language-page-present.js +5 -1
- package/src/checks/automatic/link-name-present.js +50 -17
- package/src/checks/automatic/listbox-name-present.js +8 -1
- package/src/checks/automatic/menuitem-name-present.js +8 -1
- package/src/checks/automatic/meta-refresh-no-exceptions.js +29 -0
- package/src/checks/automatic/meta-refresh-timing-absent.js +30 -4
- package/src/checks/automatic/meta-viewport-zoom-enabled.js +37 -15
- package/src/checks/automatic/meter-name-present.js +8 -1
- package/src/checks/automatic/nested-interactive-controls-absent.js +161 -31
- package/src/checks/automatic/object-text-alternative-present.js +14 -7
- package/src/checks/automatic/option-name-present.js +8 -1
- package/src/checks/automatic/progressbar-name-present.js +8 -1
- package/src/checks/automatic/searchbox-name-present.js +8 -1
- package/src/checks/automatic/slider-name-present.js +11 -2
- package/src/checks/automatic/spinbutton-name-present.js +8 -1
- package/src/checks/automatic/summary-name-present.js +8 -1
- package/src/checks/automatic/tab-name-present.js +8 -1
- package/src/checks/automatic/table-th-has-data-cells.js +67 -6
- package/src/checks/automatic/textbox-name-present.js +8 -1
- package/src/checks/automatic/tooltip-name-present.js +8 -1
- package/src/checks/automatic/treeitem-name-present.js +8 -1
- package/src/checks/automatic/valid-lang.js +16 -3
- package/src/checks/{automatic/bypass-blocks-present.js → manual/bypass-blocks-present-manual.js} +97 -35
- package/src/checks/manual/input-image-alt-decorative-manual.js +24 -0
- package/src/checks/manual/landmark-banner-is-top-level-manual.js +11 -22
- package/src/checks/manual/landmark-contentinfo-is-top-level-manual.js +7 -10
- package/src/core.js +4853 -964
- package/src/sarif.js +2 -2
- package/surea11y.browser.js +2256 -433
package/surea11y.browser.js
CHANGED
|
@@ -505,8 +505,8 @@ function runa11yCoreInPage(pageUrl, contextSelector, engineOptions, runOnly) {
|
|
|
505
505
|
},
|
|
506
506
|
{
|
|
507
507
|
"ruleId": "aria-deprecated-role",
|
|
508
|
-
"title": "role attribute
|
|
509
|
-
"description": "Checks that an explicit role=\"\" attribute does not use a role deprecated by the WAI-ARIA specification, or one reserved for user-agent-internal use
|
|
508
|
+
"title": "role attribute should not use a deprecated or author-discouraged ARIA role",
|
|
509
|
+
"description": "Checks that an explicit role=\"\" attribute does not use a role deprecated by the WAI-ARIA specification, or one reserved for user-agent-internal use (e.g. role=\"generic\").",
|
|
510
510
|
"i18n": {
|
|
511
511
|
"titleKey": "ariaDeprecatedRole_title",
|
|
512
512
|
"descriptionKey": "ariaDeprecatedRole_description"
|
|
@@ -1278,8 +1278,8 @@ function runa11yCoreInPage(pageUrl, contextSelector, engineOptions, runOnly) {
|
|
|
1278
1278
|
},
|
|
1279
1279
|
{
|
|
1280
1280
|
"ruleId": "avoid-inline-spacing",
|
|
1281
|
-
"title": "Inline style must not force text spacing
|
|
1282
|
-
"description": "Checks that inline style
|
|
1281
|
+
"title": "Inline style must not force text spacing below the WCAG metric",
|
|
1282
|
+
"description": "Checks that where inline style forces line-height, letter-spacing or word-spacing with !important, the value already meets WCAG 1.4.12, so the user has nothing left to override.",
|
|
1283
1283
|
"i18n": {
|
|
1284
1284
|
"titleKey": "avoidInlineSpacing_title",
|
|
1285
1285
|
"descriptionKey": "avoidInlineSpacing_description"
|
|
@@ -1460,7 +1460,7 @@ function runa11yCoreInPage(pageUrl, contextSelector, engineOptions, runOnly) {
|
|
|
1460
1460
|
"wcag241",
|
|
1461
1461
|
"navigation",
|
|
1462
1462
|
"atomic",
|
|
1463
|
-
"
|
|
1463
|
+
"manual",
|
|
1464
1464
|
"a11ycore"
|
|
1465
1465
|
],
|
|
1466
1466
|
"wcagSc": [
|
|
@@ -1475,9 +1475,9 @@ function runa11yCoreInPage(pageUrl, contextSelector, engineOptions, runOnly) {
|
|
|
1475
1475
|
"conformanceLevel": "A"
|
|
1476
1476
|
}
|
|
1477
1477
|
],
|
|
1478
|
-
"defaultSeverity": "
|
|
1478
|
+
"defaultSeverity": "moderate",
|
|
1479
1479
|
"defaultConfidence": "medium",
|
|
1480
|
-
"type": "
|
|
1480
|
+
"type": "manual",
|
|
1481
1481
|
"coverage": {
|
|
1482
1482
|
"facetsBySc": {
|
|
1483
1483
|
"2.4.1": [
|
|
@@ -7838,6 +7838,7 @@ function runa11yCoreInPage(pageUrl, contextSelector, engineOptions, runOnly) {
|
|
|
7838
7838
|
// Declared inside runInPage (rather than at module scope) because the
|
|
7839
7839
|
// build inlines only this function's own source text — see
|
|
7840
7840
|
// scripts/build-core.js header ("runInPage MUST be self-contained").
|
|
7841
|
+
// <generated:aria-global-attrs>
|
|
7841
7842
|
const GLOBAL_ATTRS = [
|
|
7842
7843
|
'aria-atomic',
|
|
7843
7844
|
'aria-braillelabel',
|
|
@@ -7848,14 +7849,10 @@ function runa11yCoreInPage(pageUrl, contextSelector, engineOptions, runOnly) {
|
|
|
7848
7849
|
'aria-describedby',
|
|
7849
7850
|
'aria-description',
|
|
7850
7851
|
'aria-details',
|
|
7851
|
-
'aria-disabled',
|
|
7852
7852
|
'aria-dropeffect',
|
|
7853
|
-
'aria-errormessage',
|
|
7854
7853
|
'aria-flowto',
|
|
7855
7854
|
'aria-grabbed',
|
|
7856
|
-
'aria-haspopup',
|
|
7857
7855
|
'aria-hidden',
|
|
7858
|
-
'aria-invalid',
|
|
7859
7856
|
'aria-keyshortcuts',
|
|
7860
7857
|
'aria-label',
|
|
7861
7858
|
'aria-labelledby',
|
|
@@ -7864,194 +7861,736 @@ function runa11yCoreInPage(pageUrl, contextSelector, engineOptions, runOnly) {
|
|
|
7864
7861
|
'aria-relevant',
|
|
7865
7862
|
'aria-roledescription'
|
|
7866
7863
|
];
|
|
7864
|
+
// </generated:aria-global-attrs>
|
|
7867
7865
|
|
|
7868
7866
|
// Per-role supported (non-global) states/properties. Deliberately
|
|
7869
7867
|
// conservative — see src/core/aria-helpers.js file header for the same
|
|
7870
7868
|
// confidence-scoping rationale; only well-established, unambiguous
|
|
7871
7869
|
// role/attribute pairings from the WAI-ARIA role definitions are listed.
|
|
7870
|
+
// <generated:aria-implicit-roles>
|
|
7871
|
+
const IMPLICIT_ROLE_BY_ELEMENT = {
|
|
7872
|
+
article: 'article',
|
|
7873
|
+
blockquote: 'blockquote',
|
|
7874
|
+
button: 'button',
|
|
7875
|
+
caption: 'caption',
|
|
7876
|
+
code: 'code',
|
|
7877
|
+
dd: 'definition',
|
|
7878
|
+
del: 'deletion',
|
|
7879
|
+
details: 'group',
|
|
7880
|
+
dfn: 'term',
|
|
7881
|
+
dialog: 'dialog',
|
|
7882
|
+
dt: 'term',
|
|
7883
|
+
em: 'emphasis',
|
|
7884
|
+
fieldset: 'group',
|
|
7885
|
+
figure: 'figure',
|
|
7886
|
+
h1: 'heading',
|
|
7887
|
+
h2: 'heading',
|
|
7888
|
+
h3: 'heading',
|
|
7889
|
+
h4: 'heading',
|
|
7890
|
+
h5: 'heading',
|
|
7891
|
+
h6: 'heading',
|
|
7892
|
+
hr: 'separator',
|
|
7893
|
+
ins: 'insertion',
|
|
7894
|
+
main: 'main',
|
|
7895
|
+
mark: 'mark',
|
|
7896
|
+
menu: 'list',
|
|
7897
|
+
meter: 'meter',
|
|
7898
|
+
nav: 'navigation',
|
|
7899
|
+
ol: 'list',
|
|
7900
|
+
optgroup: 'group',
|
|
7901
|
+
option: 'option',
|
|
7902
|
+
output: 'status',
|
|
7903
|
+
p: 'paragraph',
|
|
7904
|
+
progress: 'progressbar',
|
|
7905
|
+
strong: 'strong',
|
|
7906
|
+
sub: 'subscript',
|
|
7907
|
+
sup: 'superscript',
|
|
7908
|
+
textarea: 'textbox',
|
|
7909
|
+
time: 'time',
|
|
7910
|
+
ul: 'list',
|
|
7911
|
+
'input[type=text]': 'textbox',
|
|
7912
|
+
'input[type=tel]': 'textbox',
|
|
7913
|
+
'input[type=url]': 'textbox',
|
|
7914
|
+
'input[type=email]': 'textbox',
|
|
7915
|
+
'input[type=password]': 'textbox',
|
|
7916
|
+
'input[type=search]': 'searchbox',
|
|
7917
|
+
'input[type=number]': 'spinbutton',
|
|
7918
|
+
'input[type=range]': 'slider',
|
|
7919
|
+
'input[type=checkbox]': 'checkbox',
|
|
7920
|
+
'input[type=radio]': 'radio',
|
|
7921
|
+
'input[type=button]': 'button',
|
|
7922
|
+
'input[type=submit]': 'button',
|
|
7923
|
+
'input[type=reset]': 'button',
|
|
7924
|
+
'input[type=image]': 'button'
|
|
7925
|
+
};
|
|
7926
|
+
const NON_GLOBAL_ARIA_ATTR_SELECTOR =
|
|
7927
|
+
'[aria-activedescendant], [aria-autocomplete], [aria-checked], [aria-colcount], [aria-colindex], [aria-colspan], [aria-disabled], [aria-errormessage], [aria-expanded], [aria-haspopup], [aria-invalid], [aria-level], [aria-modal], [aria-multiline], [aria-multiselectable], [aria-orientation], [aria-placeholder], [aria-posinset], [aria-pressed], [aria-readonly], [aria-required], [aria-rowcount], [aria-rowindex], [aria-rowspan], [aria-selected], [aria-setsize], [aria-sort], [aria-valuemax], [aria-valuemin], [aria-valuenow], [aria-valuetext]';
|
|
7928
|
+
// </generated:aria-implicit-roles>
|
|
7929
|
+
|
|
7930
|
+
// <generated:aria-role-attrs>
|
|
7872
7931
|
const SUPPORTED_ATTRS_BY_ROLE = {
|
|
7932
|
+
alert: [],
|
|
7873
7933
|
alertdialog: ['aria-modal'],
|
|
7874
|
-
|
|
7934
|
+
application: [
|
|
7935
|
+
'aria-activedescendant',
|
|
7936
|
+
'aria-disabled',
|
|
7937
|
+
'aria-errormessage',
|
|
7938
|
+
'aria-expanded',
|
|
7939
|
+
'aria-haspopup',
|
|
7940
|
+
'aria-invalid'
|
|
7941
|
+
],
|
|
7942
|
+
article: ['aria-posinset', 'aria-setsize'],
|
|
7943
|
+
banner: [],
|
|
7944
|
+
blockquote: [],
|
|
7945
|
+
button: ['aria-disabled', 'aria-expanded', 'aria-haspopup', 'aria-pressed'],
|
|
7946
|
+
caption: [],
|
|
7947
|
+
cell: ['aria-colindex', 'aria-colspan', 'aria-rowindex', 'aria-rowspan'],
|
|
7948
|
+
checkbox: [
|
|
7949
|
+
'aria-checked',
|
|
7950
|
+
'aria-disabled',
|
|
7951
|
+
'aria-errormessage',
|
|
7952
|
+
'aria-expanded',
|
|
7953
|
+
'aria-invalid',
|
|
7954
|
+
'aria-readonly',
|
|
7955
|
+
'aria-required'
|
|
7956
|
+
],
|
|
7957
|
+
code: [],
|
|
7875
7958
|
columnheader: [
|
|
7876
|
-
'aria-sort',
|
|
7877
7959
|
'aria-colindex',
|
|
7878
7960
|
'aria-colspan',
|
|
7961
|
+
'aria-disabled',
|
|
7962
|
+
'aria-errormessage',
|
|
7963
|
+
'aria-expanded',
|
|
7964
|
+
'aria-haspopup',
|
|
7965
|
+
'aria-invalid',
|
|
7879
7966
|
'aria-readonly',
|
|
7880
7967
|
'aria-required',
|
|
7881
7968
|
'aria-rowindex',
|
|
7882
7969
|
'aria-rowspan',
|
|
7883
7970
|
'aria-selected',
|
|
7884
|
-
'aria-
|
|
7971
|
+
'aria-sort'
|
|
7885
7972
|
],
|
|
7886
7973
|
combobox: [
|
|
7887
|
-
'aria-
|
|
7974
|
+
'aria-activedescendant',
|
|
7888
7975
|
'aria-autocomplete',
|
|
7976
|
+
'aria-disabled',
|
|
7977
|
+
'aria-errormessage',
|
|
7978
|
+
'aria-expanded',
|
|
7979
|
+
'aria-haspopup',
|
|
7980
|
+
'aria-invalid',
|
|
7889
7981
|
'aria-readonly',
|
|
7890
|
-
'aria-required'
|
|
7891
|
-
'aria-activedescendant'
|
|
7982
|
+
'aria-required'
|
|
7892
7983
|
],
|
|
7984
|
+
complementary: [],
|
|
7985
|
+
contentinfo: [],
|
|
7986
|
+
definition: [],
|
|
7987
|
+
deletion: [],
|
|
7893
7988
|
dialog: ['aria-modal'],
|
|
7989
|
+
directory: [],
|
|
7990
|
+
'doc-abstract': [
|
|
7991
|
+
'aria-disabled',
|
|
7992
|
+
'aria-errormessage',
|
|
7993
|
+
'aria-expanded',
|
|
7994
|
+
'aria-haspopup',
|
|
7995
|
+
'aria-invalid'
|
|
7996
|
+
],
|
|
7997
|
+
'doc-acknowledgments': [
|
|
7998
|
+
'aria-disabled',
|
|
7999
|
+
'aria-errormessage',
|
|
8000
|
+
'aria-expanded',
|
|
8001
|
+
'aria-haspopup',
|
|
8002
|
+
'aria-invalid'
|
|
8003
|
+
],
|
|
8004
|
+
'doc-afterword': [
|
|
8005
|
+
'aria-disabled',
|
|
8006
|
+
'aria-errormessage',
|
|
8007
|
+
'aria-expanded',
|
|
8008
|
+
'aria-haspopup',
|
|
8009
|
+
'aria-invalid'
|
|
8010
|
+
],
|
|
8011
|
+
'doc-appendix': [
|
|
8012
|
+
'aria-disabled',
|
|
8013
|
+
'aria-errormessage',
|
|
8014
|
+
'aria-expanded',
|
|
8015
|
+
'aria-haspopup',
|
|
8016
|
+
'aria-invalid'
|
|
8017
|
+
],
|
|
8018
|
+
'doc-backlink': [
|
|
8019
|
+
'aria-disabled',
|
|
8020
|
+
'aria-errormessage',
|
|
8021
|
+
'aria-expanded',
|
|
8022
|
+
'aria-haspopup',
|
|
8023
|
+
'aria-invalid'
|
|
8024
|
+
],
|
|
8025
|
+
'doc-biblioentry': [
|
|
8026
|
+
'aria-disabled',
|
|
8027
|
+
'aria-errormessage',
|
|
8028
|
+
'aria-expanded',
|
|
8029
|
+
'aria-haspopup',
|
|
8030
|
+
'aria-invalid',
|
|
8031
|
+
'aria-level',
|
|
8032
|
+
'aria-posinset',
|
|
8033
|
+
'aria-setsize'
|
|
8034
|
+
],
|
|
8035
|
+
'doc-bibliography': [
|
|
8036
|
+
'aria-disabled',
|
|
8037
|
+
'aria-errormessage',
|
|
8038
|
+
'aria-expanded',
|
|
8039
|
+
'aria-haspopup',
|
|
8040
|
+
'aria-invalid'
|
|
8041
|
+
],
|
|
8042
|
+
'doc-biblioref': [
|
|
8043
|
+
'aria-disabled',
|
|
8044
|
+
'aria-errormessage',
|
|
8045
|
+
'aria-expanded',
|
|
8046
|
+
'aria-haspopup',
|
|
8047
|
+
'aria-invalid'
|
|
8048
|
+
],
|
|
8049
|
+
'doc-chapter': [
|
|
8050
|
+
'aria-disabled',
|
|
8051
|
+
'aria-errormessage',
|
|
8052
|
+
'aria-expanded',
|
|
8053
|
+
'aria-haspopup',
|
|
8054
|
+
'aria-invalid'
|
|
8055
|
+
],
|
|
8056
|
+
'doc-colophon': [
|
|
8057
|
+
'aria-disabled',
|
|
8058
|
+
'aria-errormessage',
|
|
8059
|
+
'aria-expanded',
|
|
8060
|
+
'aria-haspopup',
|
|
8061
|
+
'aria-invalid'
|
|
8062
|
+
],
|
|
8063
|
+
'doc-conclusion': [
|
|
8064
|
+
'aria-disabled',
|
|
8065
|
+
'aria-errormessage',
|
|
8066
|
+
'aria-expanded',
|
|
8067
|
+
'aria-haspopup',
|
|
8068
|
+
'aria-invalid'
|
|
8069
|
+
],
|
|
8070
|
+
'doc-cover': [
|
|
8071
|
+
'aria-disabled',
|
|
8072
|
+
'aria-errormessage',
|
|
8073
|
+
'aria-expanded',
|
|
8074
|
+
'aria-haspopup',
|
|
8075
|
+
'aria-invalid'
|
|
8076
|
+
],
|
|
8077
|
+
'doc-credit': [
|
|
8078
|
+
'aria-disabled',
|
|
8079
|
+
'aria-errormessage',
|
|
8080
|
+
'aria-expanded',
|
|
8081
|
+
'aria-haspopup',
|
|
8082
|
+
'aria-invalid'
|
|
8083
|
+
],
|
|
8084
|
+
'doc-credits': [
|
|
8085
|
+
'aria-disabled',
|
|
8086
|
+
'aria-errormessage',
|
|
8087
|
+
'aria-expanded',
|
|
8088
|
+
'aria-haspopup',
|
|
8089
|
+
'aria-invalid'
|
|
8090
|
+
],
|
|
8091
|
+
'doc-dedication': [
|
|
8092
|
+
'aria-disabled',
|
|
8093
|
+
'aria-errormessage',
|
|
8094
|
+
'aria-expanded',
|
|
8095
|
+
'aria-haspopup',
|
|
8096
|
+
'aria-invalid'
|
|
8097
|
+
],
|
|
8098
|
+
'doc-endnote': [
|
|
8099
|
+
'aria-disabled',
|
|
8100
|
+
'aria-errormessage',
|
|
8101
|
+
'aria-expanded',
|
|
8102
|
+
'aria-haspopup',
|
|
8103
|
+
'aria-invalid',
|
|
8104
|
+
'aria-level',
|
|
8105
|
+
'aria-posinset',
|
|
8106
|
+
'aria-setsize'
|
|
8107
|
+
],
|
|
8108
|
+
'doc-endnotes': [
|
|
8109
|
+
'aria-disabled',
|
|
8110
|
+
'aria-errormessage',
|
|
8111
|
+
'aria-expanded',
|
|
8112
|
+
'aria-haspopup',
|
|
8113
|
+
'aria-invalid'
|
|
8114
|
+
],
|
|
8115
|
+
'doc-epigraph': [
|
|
8116
|
+
'aria-disabled',
|
|
8117
|
+
'aria-errormessage',
|
|
8118
|
+
'aria-expanded',
|
|
8119
|
+
'aria-haspopup',
|
|
8120
|
+
'aria-invalid'
|
|
8121
|
+
],
|
|
8122
|
+
'doc-epilogue': [
|
|
8123
|
+
'aria-disabled',
|
|
8124
|
+
'aria-errormessage',
|
|
8125
|
+
'aria-expanded',
|
|
8126
|
+
'aria-haspopup',
|
|
8127
|
+
'aria-invalid'
|
|
8128
|
+
],
|
|
8129
|
+
'doc-errata': [
|
|
8130
|
+
'aria-disabled',
|
|
8131
|
+
'aria-errormessage',
|
|
8132
|
+
'aria-expanded',
|
|
8133
|
+
'aria-haspopup',
|
|
8134
|
+
'aria-invalid'
|
|
8135
|
+
],
|
|
8136
|
+
'doc-example': [
|
|
8137
|
+
'aria-disabled',
|
|
8138
|
+
'aria-errormessage',
|
|
8139
|
+
'aria-expanded',
|
|
8140
|
+
'aria-haspopup',
|
|
8141
|
+
'aria-invalid'
|
|
8142
|
+
],
|
|
8143
|
+
'doc-footnote': [
|
|
8144
|
+
'aria-disabled',
|
|
8145
|
+
'aria-errormessage',
|
|
8146
|
+
'aria-expanded',
|
|
8147
|
+
'aria-haspopup',
|
|
8148
|
+
'aria-invalid'
|
|
8149
|
+
],
|
|
8150
|
+
'doc-foreword': [
|
|
8151
|
+
'aria-disabled',
|
|
8152
|
+
'aria-errormessage',
|
|
8153
|
+
'aria-expanded',
|
|
8154
|
+
'aria-haspopup',
|
|
8155
|
+
'aria-invalid'
|
|
8156
|
+
],
|
|
8157
|
+
'doc-glossary': [
|
|
8158
|
+
'aria-disabled',
|
|
8159
|
+
'aria-errormessage',
|
|
8160
|
+
'aria-expanded',
|
|
8161
|
+
'aria-haspopup',
|
|
8162
|
+
'aria-invalid'
|
|
8163
|
+
],
|
|
8164
|
+
'doc-glossref': [
|
|
8165
|
+
'aria-disabled',
|
|
8166
|
+
'aria-errormessage',
|
|
8167
|
+
'aria-expanded',
|
|
8168
|
+
'aria-haspopup',
|
|
8169
|
+
'aria-invalid'
|
|
8170
|
+
],
|
|
8171
|
+
'doc-index': [
|
|
8172
|
+
'aria-disabled',
|
|
8173
|
+
'aria-errormessage',
|
|
8174
|
+
'aria-expanded',
|
|
8175
|
+
'aria-haspopup',
|
|
8176
|
+
'aria-invalid'
|
|
8177
|
+
],
|
|
8178
|
+
'doc-introduction': [
|
|
8179
|
+
'aria-disabled',
|
|
8180
|
+
'aria-errormessage',
|
|
8181
|
+
'aria-expanded',
|
|
8182
|
+
'aria-haspopup',
|
|
8183
|
+
'aria-invalid'
|
|
8184
|
+
],
|
|
8185
|
+
'doc-noteref': [
|
|
8186
|
+
'aria-disabled',
|
|
8187
|
+
'aria-errormessage',
|
|
8188
|
+
'aria-expanded',
|
|
8189
|
+
'aria-haspopup',
|
|
8190
|
+
'aria-invalid'
|
|
8191
|
+
],
|
|
8192
|
+
'doc-notice': [
|
|
8193
|
+
'aria-disabled',
|
|
8194
|
+
'aria-errormessage',
|
|
8195
|
+
'aria-expanded',
|
|
8196
|
+
'aria-haspopup',
|
|
8197
|
+
'aria-invalid'
|
|
8198
|
+
],
|
|
8199
|
+
'doc-pagebreak': [
|
|
8200
|
+
'aria-disabled',
|
|
8201
|
+
'aria-errormessage',
|
|
8202
|
+
'aria-expanded',
|
|
8203
|
+
'aria-haspopup',
|
|
8204
|
+
'aria-invalid',
|
|
8205
|
+
'aria-orientation',
|
|
8206
|
+
'aria-valuemax',
|
|
8207
|
+
'aria-valuemin',
|
|
8208
|
+
'aria-valuenow',
|
|
8209
|
+
'aria-valuetext'
|
|
8210
|
+
],
|
|
8211
|
+
'doc-pagefooter': ['aria-disabled', 'aria-errormessage', 'aria-haspopup', 'aria-invalid'],
|
|
8212
|
+
'doc-pageheader': ['aria-disabled', 'aria-errormessage', 'aria-haspopup', 'aria-invalid'],
|
|
8213
|
+
'doc-pagelist': [
|
|
8214
|
+
'aria-disabled',
|
|
8215
|
+
'aria-errormessage',
|
|
8216
|
+
'aria-expanded',
|
|
8217
|
+
'aria-haspopup',
|
|
8218
|
+
'aria-invalid'
|
|
8219
|
+
],
|
|
8220
|
+
'doc-part': [
|
|
8221
|
+
'aria-disabled',
|
|
8222
|
+
'aria-errormessage',
|
|
8223
|
+
'aria-expanded',
|
|
8224
|
+
'aria-haspopup',
|
|
8225
|
+
'aria-invalid'
|
|
8226
|
+
],
|
|
8227
|
+
'doc-preface': [
|
|
8228
|
+
'aria-disabled',
|
|
8229
|
+
'aria-errormessage',
|
|
8230
|
+
'aria-expanded',
|
|
8231
|
+
'aria-haspopup',
|
|
8232
|
+
'aria-invalid'
|
|
8233
|
+
],
|
|
8234
|
+
'doc-prologue': [
|
|
8235
|
+
'aria-disabled',
|
|
8236
|
+
'aria-errormessage',
|
|
8237
|
+
'aria-expanded',
|
|
8238
|
+
'aria-haspopup',
|
|
8239
|
+
'aria-invalid'
|
|
8240
|
+
],
|
|
8241
|
+
'doc-pullquote': [],
|
|
8242
|
+
'doc-qna': [
|
|
8243
|
+
'aria-disabled',
|
|
8244
|
+
'aria-errormessage',
|
|
8245
|
+
'aria-expanded',
|
|
8246
|
+
'aria-haspopup',
|
|
8247
|
+
'aria-invalid'
|
|
8248
|
+
],
|
|
8249
|
+
'doc-subtitle': [
|
|
8250
|
+
'aria-disabled',
|
|
8251
|
+
'aria-errormessage',
|
|
8252
|
+
'aria-expanded',
|
|
8253
|
+
'aria-haspopup',
|
|
8254
|
+
'aria-invalid'
|
|
8255
|
+
],
|
|
8256
|
+
'doc-tip': [
|
|
8257
|
+
'aria-disabled',
|
|
8258
|
+
'aria-errormessage',
|
|
8259
|
+
'aria-expanded',
|
|
8260
|
+
'aria-haspopup',
|
|
8261
|
+
'aria-invalid'
|
|
8262
|
+
],
|
|
8263
|
+
'doc-toc': [
|
|
8264
|
+
'aria-disabled',
|
|
8265
|
+
'aria-errormessage',
|
|
8266
|
+
'aria-expanded',
|
|
8267
|
+
'aria-haspopup',
|
|
8268
|
+
'aria-invalid'
|
|
8269
|
+
],
|
|
8270
|
+
document: [],
|
|
8271
|
+
emphasis: [],
|
|
8272
|
+
feed: [],
|
|
8273
|
+
figure: [],
|
|
8274
|
+
form: [],
|
|
8275
|
+
generic: [],
|
|
8276
|
+
'graphics-document': [
|
|
8277
|
+
'aria-disabled',
|
|
8278
|
+
'aria-errormessage',
|
|
8279
|
+
'aria-expanded',
|
|
8280
|
+
'aria-haspopup',
|
|
8281
|
+
'aria-invalid'
|
|
8282
|
+
],
|
|
8283
|
+
'graphics-object': [
|
|
8284
|
+
'aria-activedescendant',
|
|
8285
|
+
'aria-disabled',
|
|
8286
|
+
'aria-errormessage',
|
|
8287
|
+
'aria-expanded',
|
|
8288
|
+
'aria-haspopup',
|
|
8289
|
+
'aria-invalid'
|
|
8290
|
+
],
|
|
8291
|
+
'graphics-symbol': [
|
|
8292
|
+
'aria-disabled',
|
|
8293
|
+
'aria-errormessage',
|
|
8294
|
+
'aria-expanded',
|
|
8295
|
+
'aria-haspopup',
|
|
8296
|
+
'aria-invalid'
|
|
8297
|
+
],
|
|
7894
8298
|
grid: [
|
|
8299
|
+
'aria-activedescendant',
|
|
8300
|
+
'aria-colcount',
|
|
8301
|
+
'aria-disabled',
|
|
7895
8302
|
'aria-multiselectable',
|
|
7896
8303
|
'aria-readonly',
|
|
7897
|
-
'aria-
|
|
7898
|
-
'aria-rowcount',
|
|
7899
|
-
'aria-activedescendant'
|
|
8304
|
+
'aria-rowcount'
|
|
7900
8305
|
],
|
|
7901
8306
|
gridcell: [
|
|
7902
|
-
'aria-selected',
|
|
7903
|
-
'aria-readonly',
|
|
7904
|
-
'aria-required',
|
|
7905
8307
|
'aria-colindex',
|
|
7906
8308
|
'aria-colspan',
|
|
8309
|
+
'aria-disabled',
|
|
8310
|
+
'aria-errormessage',
|
|
8311
|
+
'aria-expanded',
|
|
8312
|
+
'aria-haspopup',
|
|
8313
|
+
'aria-invalid',
|
|
8314
|
+
'aria-readonly',
|
|
8315
|
+
'aria-required',
|
|
7907
8316
|
'aria-rowindex',
|
|
7908
8317
|
'aria-rowspan',
|
|
7909
|
-
'aria-
|
|
8318
|
+
'aria-selected'
|
|
7910
8319
|
],
|
|
8320
|
+
group: ['aria-activedescendant', 'aria-disabled'],
|
|
7911
8321
|
heading: ['aria-level'],
|
|
8322
|
+
img: [],
|
|
8323
|
+
insertion: [],
|
|
8324
|
+
link: ['aria-disabled', 'aria-expanded', 'aria-haspopup'],
|
|
8325
|
+
list: [],
|
|
7912
8326
|
listbox: [
|
|
8327
|
+
'aria-activedescendant',
|
|
8328
|
+
'aria-disabled',
|
|
8329
|
+
'aria-errormessage',
|
|
8330
|
+
'aria-expanded',
|
|
8331
|
+
'aria-invalid',
|
|
7913
8332
|
'aria-multiselectable',
|
|
7914
|
-
'aria-readonly',
|
|
7915
|
-
'aria-required',
|
|
7916
8333
|
'aria-orientation',
|
|
7917
|
-
'aria-
|
|
7918
|
-
'aria-
|
|
8334
|
+
'aria-readonly',
|
|
8335
|
+
'aria-required'
|
|
7919
8336
|
],
|
|
7920
8337
|
listitem: ['aria-level', 'aria-posinset', 'aria-setsize'],
|
|
7921
|
-
|
|
7922
|
-
|
|
8338
|
+
log: [],
|
|
8339
|
+
main: [],
|
|
8340
|
+
mark: [],
|
|
8341
|
+
marquee: [],
|
|
8342
|
+
math: [],
|
|
8343
|
+
menu: ['aria-activedescendant', 'aria-disabled', 'aria-orientation'],
|
|
8344
|
+
menubar: ['aria-activedescendant', 'aria-disabled', 'aria-orientation'],
|
|
8345
|
+
menuitem: ['aria-disabled', 'aria-expanded', 'aria-haspopup', 'aria-posinset', 'aria-setsize'],
|
|
7923
8346
|
menuitemcheckbox: [
|
|
7924
8347
|
'aria-checked',
|
|
8348
|
+
'aria-disabled',
|
|
8349
|
+
'aria-errormessage',
|
|
7925
8350
|
'aria-expanded',
|
|
8351
|
+
'aria-haspopup',
|
|
8352
|
+
'aria-invalid',
|
|
8353
|
+
'aria-posinset',
|
|
7926
8354
|
'aria-readonly',
|
|
7927
8355
|
'aria-required',
|
|
7928
|
-
'aria-posinset',
|
|
7929
8356
|
'aria-setsize'
|
|
7930
8357
|
],
|
|
7931
8358
|
menuitemradio: [
|
|
7932
8359
|
'aria-checked',
|
|
8360
|
+
'aria-disabled',
|
|
8361
|
+
'aria-errormessage',
|
|
7933
8362
|
'aria-expanded',
|
|
8363
|
+
'aria-haspopup',
|
|
8364
|
+
'aria-invalid',
|
|
8365
|
+
'aria-posinset',
|
|
7934
8366
|
'aria-readonly',
|
|
7935
8367
|
'aria-required',
|
|
7936
|
-
'aria-posinset',
|
|
7937
8368
|
'aria-setsize'
|
|
7938
8369
|
],
|
|
7939
|
-
meter: ['aria-
|
|
7940
|
-
|
|
7941
|
-
|
|
7942
|
-
|
|
7943
|
-
|
|
8370
|
+
meter: ['aria-valuemax', 'aria-valuemin', 'aria-valuenow', 'aria-valuetext'],
|
|
8371
|
+
navigation: [],
|
|
8372
|
+
none: [],
|
|
8373
|
+
note: [],
|
|
8374
|
+
option: ['aria-checked', 'aria-disabled', 'aria-posinset', 'aria-selected', 'aria-setsize'],
|
|
8375
|
+
paragraph: [],
|
|
8376
|
+
presentation: [],
|
|
8377
|
+
progressbar: ['aria-valuemax', 'aria-valuemin', 'aria-valuenow', 'aria-valuetext'],
|
|
8378
|
+
radio: ['aria-checked', 'aria-disabled', 'aria-posinset', 'aria-setsize'],
|
|
8379
|
+
radiogroup: [
|
|
8380
|
+
'aria-activedescendant',
|
|
8381
|
+
'aria-disabled',
|
|
8382
|
+
'aria-errormessage',
|
|
8383
|
+
'aria-invalid',
|
|
8384
|
+
'aria-orientation',
|
|
8385
|
+
'aria-readonly',
|
|
8386
|
+
'aria-required'
|
|
8387
|
+
],
|
|
8388
|
+
region: [],
|
|
7944
8389
|
row: [
|
|
7945
|
-
'aria-
|
|
8390
|
+
'aria-activedescendant',
|
|
8391
|
+
'aria-colindex',
|
|
8392
|
+
'aria-disabled',
|
|
8393
|
+
'aria-expanded',
|
|
7946
8394
|
'aria-level',
|
|
7947
8395
|
'aria-posinset',
|
|
7948
|
-
'aria-setsize',
|
|
7949
|
-
'aria-colindex',
|
|
7950
8396
|
'aria-rowindex',
|
|
7951
|
-
'aria-
|
|
7952
|
-
'aria-
|
|
8397
|
+
'aria-selected',
|
|
8398
|
+
'aria-setsize'
|
|
7953
8399
|
],
|
|
8400
|
+
rowgroup: [],
|
|
7954
8401
|
rowheader: [
|
|
7955
|
-
'aria-sort',
|
|
7956
8402
|
'aria-colindex',
|
|
7957
8403
|
'aria-colspan',
|
|
8404
|
+
'aria-disabled',
|
|
8405
|
+
'aria-errormessage',
|
|
8406
|
+
'aria-expanded',
|
|
8407
|
+
'aria-haspopup',
|
|
8408
|
+
'aria-invalid',
|
|
7958
8409
|
'aria-readonly',
|
|
7959
8410
|
'aria-required',
|
|
7960
8411
|
'aria-rowindex',
|
|
7961
8412
|
'aria-rowspan',
|
|
7962
8413
|
'aria-selected',
|
|
7963
|
-
'aria-
|
|
8414
|
+
'aria-sort'
|
|
7964
8415
|
],
|
|
7965
8416
|
scrollbar: [
|
|
7966
|
-
'aria-
|
|
7967
|
-
'aria-valuemin',
|
|
7968
|
-
'aria-valuemax',
|
|
7969
|
-
'aria-valuetext',
|
|
8417
|
+
'aria-disabled',
|
|
7970
8418
|
'aria-orientation',
|
|
7971
|
-
'aria-
|
|
8419
|
+
'aria-valuemax',
|
|
8420
|
+
'aria-valuemin',
|
|
8421
|
+
'aria-valuenow',
|
|
8422
|
+
'aria-valuetext'
|
|
7972
8423
|
],
|
|
8424
|
+
search: [],
|
|
7973
8425
|
searchbox: [
|
|
7974
8426
|
'aria-activedescendant',
|
|
7975
8427
|
'aria-autocomplete',
|
|
8428
|
+
'aria-disabled',
|
|
8429
|
+
'aria-errormessage',
|
|
8430
|
+
'aria-haspopup',
|
|
8431
|
+
'aria-invalid',
|
|
7976
8432
|
'aria-multiline',
|
|
7977
8433
|
'aria-placeholder',
|
|
7978
8434
|
'aria-readonly',
|
|
7979
8435
|
'aria-required'
|
|
7980
8436
|
],
|
|
7981
8437
|
separator: [
|
|
7982
|
-
'aria-
|
|
7983
|
-
'aria-
|
|
8438
|
+
'aria-disabled',
|
|
8439
|
+
'aria-orientation',
|
|
7984
8440
|
'aria-valuemax',
|
|
7985
|
-
'aria-
|
|
7986
|
-
'aria-
|
|
8441
|
+
'aria-valuemin',
|
|
8442
|
+
'aria-valuenow',
|
|
8443
|
+
'aria-valuetext'
|
|
7987
8444
|
],
|
|
7988
8445
|
slider: [
|
|
7989
|
-
'aria-
|
|
7990
|
-
'aria-
|
|
7991
|
-
'aria-
|
|
7992
|
-
'aria-
|
|
8446
|
+
'aria-disabled',
|
|
8447
|
+
'aria-errormessage',
|
|
8448
|
+
'aria-haspopup',
|
|
8449
|
+
'aria-invalid',
|
|
7993
8450
|
'aria-orientation',
|
|
7994
|
-
'aria-readonly'
|
|
8451
|
+
'aria-readonly',
|
|
8452
|
+
'aria-valuemax',
|
|
8453
|
+
'aria-valuemin',
|
|
8454
|
+
'aria-valuenow',
|
|
8455
|
+
'aria-valuetext'
|
|
7995
8456
|
],
|
|
7996
8457
|
spinbutton: [
|
|
7997
|
-
'aria-
|
|
7998
|
-
'aria-
|
|
7999
|
-
'aria-
|
|
8000
|
-
'aria-
|
|
8458
|
+
'aria-activedescendant',
|
|
8459
|
+
'aria-disabled',
|
|
8460
|
+
'aria-errormessage',
|
|
8461
|
+
'aria-invalid',
|
|
8001
8462
|
'aria-readonly',
|
|
8002
8463
|
'aria-required',
|
|
8003
|
-
'aria-
|
|
8464
|
+
'aria-valuemax',
|
|
8465
|
+
'aria-valuemin',
|
|
8466
|
+
'aria-valuenow',
|
|
8467
|
+
'aria-valuetext'
|
|
8468
|
+
],
|
|
8469
|
+
status: [],
|
|
8470
|
+
strong: [],
|
|
8471
|
+
subscript: [],
|
|
8472
|
+
superscript: [],
|
|
8473
|
+
switch: [
|
|
8474
|
+
'aria-checked',
|
|
8475
|
+
'aria-disabled',
|
|
8476
|
+
'aria-errormessage',
|
|
8477
|
+
'aria-expanded',
|
|
8478
|
+
'aria-invalid',
|
|
8479
|
+
'aria-readonly',
|
|
8480
|
+
'aria-required'
|
|
8481
|
+
],
|
|
8482
|
+
tab: [
|
|
8483
|
+
'aria-disabled',
|
|
8484
|
+
'aria-expanded',
|
|
8485
|
+
'aria-haspopup',
|
|
8486
|
+
'aria-posinset',
|
|
8487
|
+
'aria-selected',
|
|
8488
|
+
'aria-setsize'
|
|
8004
8489
|
],
|
|
8005
|
-
switch: ['aria-checked', 'aria-expanded', 'aria-readonly', 'aria-required'],
|
|
8006
|
-
tab: ['aria-selected', 'aria-expanded', 'aria-posinset', 'aria-setsize'],
|
|
8007
8490
|
table: ['aria-colcount', 'aria-rowcount'],
|
|
8008
|
-
tablist: [
|
|
8491
|
+
tablist: [
|
|
8492
|
+
'aria-activedescendant',
|
|
8493
|
+
'aria-disabled',
|
|
8494
|
+
'aria-level',
|
|
8495
|
+
'aria-multiselectable',
|
|
8496
|
+
'aria-orientation'
|
|
8497
|
+
],
|
|
8498
|
+
tabpanel: [],
|
|
8499
|
+
term: [],
|
|
8009
8500
|
textbox: [
|
|
8010
8501
|
'aria-activedescendant',
|
|
8011
8502
|
'aria-autocomplete',
|
|
8503
|
+
'aria-disabled',
|
|
8504
|
+
'aria-errormessage',
|
|
8505
|
+
'aria-haspopup',
|
|
8506
|
+
'aria-invalid',
|
|
8012
8507
|
'aria-multiline',
|
|
8013
8508
|
'aria-placeholder',
|
|
8014
8509
|
'aria-readonly',
|
|
8015
8510
|
'aria-required'
|
|
8016
8511
|
],
|
|
8017
|
-
|
|
8018
|
-
|
|
8512
|
+
time: [],
|
|
8513
|
+
timer: [],
|
|
8514
|
+
toolbar: ['aria-activedescendant', 'aria-disabled', 'aria-orientation'],
|
|
8515
|
+
tooltip: [],
|
|
8516
|
+
tree: [
|
|
8517
|
+
'aria-activedescendant',
|
|
8518
|
+
'aria-disabled',
|
|
8519
|
+
'aria-errormessage',
|
|
8520
|
+
'aria-invalid',
|
|
8521
|
+
'aria-multiselectable',
|
|
8522
|
+
'aria-orientation',
|
|
8523
|
+
'aria-required'
|
|
8524
|
+
],
|
|
8019
8525
|
treegrid: [
|
|
8526
|
+
'aria-activedescendant',
|
|
8527
|
+
'aria-colcount',
|
|
8528
|
+
'aria-disabled',
|
|
8529
|
+
'aria-errormessage',
|
|
8530
|
+
'aria-invalid',
|
|
8020
8531
|
'aria-multiselectable',
|
|
8532
|
+
'aria-orientation',
|
|
8021
8533
|
'aria-readonly',
|
|
8022
8534
|
'aria-required',
|
|
8023
|
-
'aria-
|
|
8024
|
-
'aria-colcount',
|
|
8025
|
-
'aria-rowcount',
|
|
8026
|
-
'aria-activedescendant'
|
|
8535
|
+
'aria-rowcount'
|
|
8027
8536
|
],
|
|
8028
8537
|
treeitem: [
|
|
8029
8538
|
'aria-checked',
|
|
8030
|
-
'aria-
|
|
8539
|
+
'aria-disabled',
|
|
8031
8540
|
'aria-expanded',
|
|
8541
|
+
'aria-haspopup',
|
|
8032
8542
|
'aria-level',
|
|
8033
8543
|
'aria-posinset',
|
|
8544
|
+
'aria-selected',
|
|
8034
8545
|
'aria-setsize'
|
|
8035
8546
|
]
|
|
8036
8547
|
};
|
|
8548
|
+
// </generated:aria-role-attrs>
|
|
8037
8549
|
|
|
8038
8550
|
const globalSet = new Set(GLOBAL_ATTRS);
|
|
8551
|
+
// [role] keeps the explicit-role path; the attribute selector brings in
|
|
8552
|
+
// elements judged by their implicit role. Only non-global attributes can be
|
|
8553
|
+
// disallowed, so nothing else needs visiting.
|
|
8554
|
+
const selector = '[role], ' + NON_GLOBAL_ARIA_ATTR_SELECTOR;
|
|
8039
8555
|
const nodes = helpers.queryAllSmart
|
|
8040
|
-
? helpers.queryAllSmart(
|
|
8041
|
-
: helpers.queryAll(
|
|
8556
|
+
? helpers.queryAllSmart(selector)
|
|
8557
|
+
: helpers.queryAll(selector);
|
|
8042
8558
|
|
|
8043
|
-
const
|
|
8559
|
+
const failOccurrences = [];
|
|
8560
|
+
const cantTellOccurrences = [];
|
|
8044
8561
|
let applicableCount = 0;
|
|
8045
8562
|
|
|
8046
8563
|
for (const el of nodes) {
|
|
8047
8564
|
if (!el || !el.attributes) continue;
|
|
8048
8565
|
|
|
8049
|
-
|
|
8566
|
+
// ACT 5c01ea scopes the rule to any element carrying an ARIA attribute, so
|
|
8567
|
+
// an element with no role attribute is judged against its implicit role.
|
|
8568
|
+
// Only elements whose implicit role is context-free are covered; the
|
|
8569
|
+
// generator lists what is excluded and why.
|
|
8570
|
+
const explicitRole = ariaHelpers.getExplicitRole(el);
|
|
8571
|
+
let role = explicitRole;
|
|
8572
|
+
if (!role) {
|
|
8573
|
+
const tag = String(el.tagName || '').toLowerCase();
|
|
8574
|
+
const key =
|
|
8575
|
+
tag === 'input'
|
|
8576
|
+
? 'input[type=' + String(el.getAttribute('type') || 'text').toLowerCase() + ']'
|
|
8577
|
+
: tag;
|
|
8578
|
+
role = Object.prototype.hasOwnProperty.call(IMPLICIT_ROLE_BY_ELEMENT, key)
|
|
8579
|
+
? IMPLICIT_ROLE_BY_ELEMENT[key]
|
|
8580
|
+
: '';
|
|
8581
|
+
}
|
|
8050
8582
|
if (!role || !ariaHelpers.isValidConcreteRole(role)) continue; // aria-roles-valid's concern
|
|
8051
8583
|
|
|
8052
|
-
//
|
|
8053
|
-
//
|
|
8054
|
-
//
|
|
8584
|
+
// Presentational role conflict resolution drops role="none"/"presentation"
|
|
8585
|
+
// when the element is focusable or carries global ARIA, so the implicit
|
|
8586
|
+
// role decides which attributes are supported. Judging against the
|
|
8587
|
+
// presentational role would flag valid markup such as
|
|
8588
|
+
// <button role="none" aria-pressed="false">; presentation-role-conflict
|
|
8589
|
+
// owns this case.
|
|
8590
|
+
if (role === 'none' || role === 'presentation') continue;
|
|
8591
|
+
|
|
8592
|
+
// Roles absent from the generated table are unknown to ARIA, so there is
|
|
8593
|
+
// nothing to judge them against.
|
|
8055
8594
|
const roleSupported = SUPPORTED_ATTRS_BY_ROLE[role];
|
|
8056
8595
|
if (!roleSupported) continue;
|
|
8057
8596
|
const roleSupportedSet = new Set(roleSupported);
|
|
@@ -8077,11 +8616,36 @@ function runa11yCoreInPage(pageUrl, contextSelector, engineOptions, runOnly) {
|
|
|
8077
8616
|
const html = helpers.getOuterHtmlSnippet ? helpers.getOuterHtmlSnippet(el) : el.outerHTML || '';
|
|
8078
8617
|
|
|
8079
8618
|
for (const name of disallowed) {
|
|
8080
|
-
|
|
8619
|
+
// A property ARIA deprecated (rather than prohibited) on this role is
|
|
8620
|
+
// still allowed — surfaced as cantTell for the author to decide, not a
|
|
8621
|
+
// not-allowed fail.
|
|
8622
|
+
const deprecated =
|
|
8623
|
+
typeof ariaHelpers.isDeprecatedAttr === 'function' &&
|
|
8624
|
+
ariaHelpers.isDeprecatedAttr(name, role);
|
|
8625
|
+
if (deprecated) {
|
|
8626
|
+
cantTellOccurrences.push({
|
|
8627
|
+
selector: stableSelector,
|
|
8628
|
+
html,
|
|
8629
|
+
summary: 'This ARIA attribute is deprecated for this element’s role.',
|
|
8630
|
+
hint: 'It is still allowed but discouraged; remove it or use a role that supports it, as a future ARIA version may disallow it.',
|
|
8631
|
+
occurrenceOutcome: 'cantTell',
|
|
8632
|
+
i18n: {
|
|
8633
|
+
summaryKey: 'ariaAllowedAttr_summary_cantTell',
|
|
8634
|
+
hintKey: 'ariaAllowedAttr_hint_cantTell',
|
|
8635
|
+
params: { attr: name, role }
|
|
8636
|
+
},
|
|
8637
|
+
data: {
|
|
8638
|
+
details: { reasonCode: 'ARIA_ATTR_DEPRECATED', attr: name, role }
|
|
8639
|
+
}
|
|
8640
|
+
});
|
|
8641
|
+
continue;
|
|
8642
|
+
}
|
|
8643
|
+
failOccurrences.push({
|
|
8081
8644
|
selector: stableSelector,
|
|
8082
8645
|
html,
|
|
8083
8646
|
summary: 'This ARIA attribute is not permitted for this element’s role.',
|
|
8084
8647
|
hint: 'Remove this attribute, or use a role that supports it.',
|
|
8648
|
+
occurrenceOutcome: 'fail',
|
|
8085
8649
|
i18n: {
|
|
8086
8650
|
summaryKey: 'ariaAllowedAttr_summary_fail',
|
|
8087
8651
|
hintKey: 'ariaAllowedAttr_hint_fail',
|
|
@@ -8097,15 +8661,13 @@ function runa11yCoreInPage(pageUrl, contextSelector, engineOptions, runOnly) {
|
|
|
8097
8661
|
if (applicableCount === 0) {
|
|
8098
8662
|
return { ruleId: rule.ruleId, outcome: 'notApplicable', severity: 'minor', occurrences: [] };
|
|
8099
8663
|
}
|
|
8100
|
-
|
|
8101
|
-
|
|
8102
|
-
|
|
8103
|
-
|
|
8104
|
-
|
|
8105
|
-
|
|
8106
|
-
|
|
8107
|
-
}
|
|
8108
|
-
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
8664
|
+
|
|
8665
|
+
const resolved = helpers.resolveTieredOutcome(
|
|
8666
|
+
failOccurrences,
|
|
8667
|
+
cantTellOccurrences,
|
|
8668
|
+
rule.defaultSeverity || 'moderate'
|
|
8669
|
+
);
|
|
8670
|
+
return { ruleId: rule.ruleId, ...resolved };
|
|
8109
8671
|
}), applicability: null },
|
|
8110
8672
|
"aria-allowed-role": { run: (function runInPage(ctx) {
|
|
8111
8673
|
const { helpers, rule } = ctx;
|
|
@@ -8427,12 +8989,31 @@ function runa11yCoreInPage(pageUrl, contextSelector, engineOptions, runOnly) {
|
|
|
8427
8989
|
? helpers.queryAllSmart('[role]')
|
|
8428
8990
|
: helpers.queryAll('[role]');
|
|
8429
8991
|
|
|
8430
|
-
const
|
|
8992
|
+
const failOccurrences = [];
|
|
8993
|
+
const cantTellOccurrences = [];
|
|
8431
8994
|
let applicableCount = 0;
|
|
8432
8995
|
|
|
8996
|
+
// A role on an element hidden from assistive technology has no effect, so
|
|
8997
|
+
// ACT 674b10 does not apply to it.
|
|
8998
|
+
function isHidden(el) {
|
|
8999
|
+
try {
|
|
9000
|
+
if (typeof helpers.isDomVisibleEligible === 'function') {
|
|
9001
|
+
if (!helpers.isDomVisibleEligible(el, ctx)) return true;
|
|
9002
|
+
}
|
|
9003
|
+
for (let n = el; n && n.getAttribute; n = n.parentElement) {
|
|
9004
|
+
if (String(n.getAttribute('aria-hidden') || '').toLowerCase() === 'true') return true;
|
|
9005
|
+
}
|
|
9006
|
+
} catch {
|
|
9007
|
+
return false;
|
|
9008
|
+
}
|
|
9009
|
+
return false;
|
|
9010
|
+
}
|
|
9011
|
+
|
|
8433
9012
|
for (const el of nodes) {
|
|
8434
9013
|
if (!el || !el.getAttribute) continue;
|
|
8435
9014
|
|
|
9015
|
+
if (isHidden(el)) continue;
|
|
9016
|
+
|
|
8436
9017
|
const role = ariaHelpers.getExplicitRole(el);
|
|
8437
9018
|
if (!role) continue;
|
|
8438
9019
|
|
|
@@ -8442,7 +9023,14 @@ function runa11yCoreInPage(pageUrl, contextSelector, engineOptions, runOnly) {
|
|
|
8442
9023
|
|
|
8443
9024
|
applicableCount += 1;
|
|
8444
9025
|
|
|
8445
|
-
|
|
9026
|
+
const deprecated = ariaHelpers.isDeprecatedRole(role);
|
|
9027
|
+
const discouraged =
|
|
9028
|
+
typeof ariaHelpers.isAuthorDiscouragedRole === 'function' &&
|
|
9029
|
+
ariaHelpers.isAuthorDiscouragedRole(role);
|
|
9030
|
+
const prohibited =
|
|
9031
|
+
typeof ariaHelpers.isAuthorProhibitedRole === 'function' &&
|
|
9032
|
+
ariaHelpers.isAuthorProhibitedRole(role);
|
|
9033
|
+
if (!deprecated && !discouraged && !prohibited) continue;
|
|
8446
9034
|
|
|
8447
9035
|
const stableSelector = helpers.buildSelector ? helpers.buildSelector(el) : 'html';
|
|
8448
9036
|
const html = helpers.getOuterHtmlSnippet ? helpers.getOuterHtmlSnippet(el) : el.outerHTML || '';
|
|
@@ -8450,34 +9038,70 @@ function runa11yCoreInPage(pageUrl, contextSelector, engineOptions, runOnly) {
|
|
|
8450
9038
|
? ariaHelpers.getDeprecatedRoleGuidance(role)
|
|
8451
9039
|
: 'Replace the deprecated role with its recommended replacement.';
|
|
8452
9040
|
|
|
8453
|
-
|
|
8454
|
-
|
|
8455
|
-
|
|
8456
|
-
|
|
8457
|
-
|
|
8458
|
-
|
|
8459
|
-
|
|
8460
|
-
|
|
8461
|
-
|
|
8462
|
-
|
|
8463
|
-
|
|
8464
|
-
|
|
8465
|
-
|
|
8466
|
-
|
|
9041
|
+
if (prohibited) {
|
|
9042
|
+
// Author MUST NOT: the usage is non-conforming, not merely discouraged.
|
|
9043
|
+
failOccurrences.push({
|
|
9044
|
+
selector: stableSelector,
|
|
9045
|
+
html,
|
|
9046
|
+
summary: `This element uses role="${role}", which authors must not explicitly declare.`,
|
|
9047
|
+
hint: guidance,
|
|
9048
|
+
occurrenceOutcome: 'fail',
|
|
9049
|
+
i18n: {
|
|
9050
|
+
summaryKey: 'ariaDeprecatedRole_summary_fail',
|
|
9051
|
+
hintKey: 'ariaDeprecatedRole_hint_fail',
|
|
9052
|
+
params: { role, guidance }
|
|
9053
|
+
},
|
|
9054
|
+
data: {
|
|
9055
|
+
details: { reasonCode: 'ARIA_ROLE_AUTHOR_PROHIBITED', role, guidance }
|
|
9056
|
+
}
|
|
9057
|
+
});
|
|
9058
|
+
} else if (discouraged) {
|
|
9059
|
+
// Reserved for user-agent-internal use, at SHOULD NOT strength.
|
|
9060
|
+
cantTellOccurrences.push({
|
|
9061
|
+
selector: stableSelector,
|
|
9062
|
+
html,
|
|
9063
|
+
summary: `This element uses role="${role}", which is reserved for user agents (still valid, but discouraged).`,
|
|
9064
|
+
hint: guidance,
|
|
9065
|
+
occurrenceOutcome: 'cantTell',
|
|
9066
|
+
i18n: {
|
|
9067
|
+
summaryKey: 'ariaDeprecatedRole_summary_cantTell_discouraged',
|
|
9068
|
+
hintKey: 'ariaDeprecatedRole_hint_cantTell',
|
|
9069
|
+
params: { role, guidance }
|
|
9070
|
+
},
|
|
9071
|
+
data: {
|
|
9072
|
+
details: { reasonCode: 'ARIA_ROLE_AUTHOR_DISCOURAGED', role, guidance }
|
|
9073
|
+
}
|
|
9074
|
+
});
|
|
9075
|
+
} else {
|
|
9076
|
+
// Deprecated but still valid: surfaced for the author to decide.
|
|
9077
|
+
cantTellOccurrences.push({
|
|
9078
|
+
selector: stableSelector,
|
|
9079
|
+
html,
|
|
9080
|
+
summary: `This element uses role="${role}", which is deprecated in WAI-ARIA.`,
|
|
9081
|
+
hint: guidance,
|
|
9082
|
+
occurrenceOutcome: 'cantTell',
|
|
9083
|
+
i18n: {
|
|
9084
|
+
summaryKey: 'ariaDeprecatedRole_summary_cantTell',
|
|
9085
|
+
hintKey: 'ariaDeprecatedRole_hint_cantTell',
|
|
9086
|
+
params: { role, guidance }
|
|
9087
|
+
},
|
|
9088
|
+
data: {
|
|
9089
|
+
details: { reasonCode: 'ARIA_ROLE_DEPRECATED', role, guidance }
|
|
9090
|
+
}
|
|
9091
|
+
});
|
|
9092
|
+
}
|
|
8467
9093
|
}
|
|
8468
9094
|
|
|
8469
9095
|
if (applicableCount === 0) {
|
|
8470
9096
|
return { ruleId: rule.ruleId, outcome: 'notApplicable', severity: 'minor', occurrences: [] };
|
|
8471
9097
|
}
|
|
8472
|
-
|
|
8473
|
-
|
|
8474
|
-
|
|
8475
|
-
|
|
8476
|
-
|
|
8477
|
-
|
|
8478
|
-
|
|
8479
|
-
}
|
|
8480
|
-
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
9098
|
+
|
|
9099
|
+
const resolved = helpers.resolveTieredOutcome(
|
|
9100
|
+
failOccurrences,
|
|
9101
|
+
cantTellOccurrences,
|
|
9102
|
+
rule.defaultSeverity || 'moderate'
|
|
9103
|
+
);
|
|
9104
|
+
return { ruleId: rule.ruleId, ...resolved };
|
|
8481
9105
|
}), applicability: null },
|
|
8482
9106
|
"aria-hidden-body": { run: (function runInPage(ctx) {
|
|
8483
9107
|
const { document, helpers, rule } = ctx;
|
|
@@ -10483,19 +11107,44 @@ function runa11yCoreInPage(pageUrl, contextSelector, engineOptions, runOnly) {
|
|
|
10483
11107
|
const occurrences = [];
|
|
10484
11108
|
let applicableCount = 0;
|
|
10485
11109
|
|
|
11110
|
+
// Programmatically hidden per the ACT glossary: display:none, visibility not
|
|
11111
|
+
// visible, or aria-hidden on the element or an ancestor.
|
|
11112
|
+
function isHidden(el) {
|
|
11113
|
+
try {
|
|
11114
|
+
if (typeof helpers.isDomVisibleEligible === 'function') {
|
|
11115
|
+
if (!helpers.isDomVisibleEligible(el, ctx)) return true;
|
|
11116
|
+
}
|
|
11117
|
+
for (let n = el; n && n.getAttribute; n = n.parentElement) {
|
|
11118
|
+
if (String(n.getAttribute('aria-hidden') || '').toLowerCase() === 'true') return true;
|
|
11119
|
+
}
|
|
11120
|
+
} catch {
|
|
11121
|
+
return false;
|
|
11122
|
+
}
|
|
11123
|
+
return false;
|
|
11124
|
+
}
|
|
11125
|
+
|
|
10486
11126
|
for (const el of nodes) {
|
|
10487
11127
|
if (!el || !el.getAttribute) continue;
|
|
10488
11128
|
|
|
10489
|
-
|
|
10490
|
-
if (
|
|
11129
|
+
// ACT 674b10 is not applicable to a programmatically hidden element.
|
|
11130
|
+
if (isHidden(el)) continue;
|
|
10491
11131
|
|
|
10492
|
-
|
|
11132
|
+
// role takes a fallback list and the first token the browser recognises
|
|
11133
|
+
// wins, so role="searchfield searchbox" resolves to searchbox. The rule
|
|
11134
|
+
// fails only when no token names a concrete role.
|
|
11135
|
+
const tokens =
|
|
11136
|
+
typeof ariaHelpers.getAllRoleTokens === 'function'
|
|
11137
|
+
? ariaHelpers.getAllRoleTokens(el)
|
|
11138
|
+
: [ariaHelpers.getExplicitRole(el)].filter(Boolean);
|
|
11139
|
+
if (!tokens.length) continue; // role="" or whitespace-only: not this rule's concern
|
|
10493
11140
|
|
|
10494
|
-
|
|
10495
|
-
const isKnown = ariaHelpers.isKnownRole(role);
|
|
11141
|
+
applicableCount += 1;
|
|
10496
11142
|
|
|
10497
|
-
|
|
11143
|
+
const usable = tokens.find((t) => ariaHelpers.isKnownRole(t) && !ariaHelpers.isAbstractRole(t));
|
|
11144
|
+
if (usable) continue;
|
|
10498
11145
|
|
|
11146
|
+
const role = tokens[0];
|
|
11147
|
+
const isKnown = tokens.some((t) => ariaHelpers.isKnownRole(t));
|
|
10499
11148
|
const reasonCode = !isKnown ? 'ARIA_ROLE_INVALID' : 'ARIA_ROLE_ABSTRACT';
|
|
10500
11149
|
const stableSelector = helpers.buildSelector ? helpers.buildSelector(el) : 'html';
|
|
10501
11150
|
const html = helpers.getOuterHtmlSnippet ? helpers.getOuterHtmlSnippet(el) : el.outerHTML || '';
|
|
@@ -10813,7 +11462,15 @@ function runa11yCoreInPage(pageUrl, contextSelector, engineOptions, runOnly) {
|
|
|
10813
11462
|
if (tokens[i] && tokens[i].startsWith('section-') && tokens[i].length > 'section-'.length)
|
|
10814
11463
|
i += 1;
|
|
10815
11464
|
if (tokens[i] === 'shipping' || tokens[i] === 'billing') i += 1;
|
|
10816
|
-
|
|
11465
|
+
// A contact modality token is only allowed when the field that follows is
|
|
11466
|
+
// a contact field, so "work photo" is invalid while "work email" is not.
|
|
11467
|
+
if (CONTACT_MODALITY.has(tokens[i])) {
|
|
11468
|
+
const next = tokens[i + 1];
|
|
11469
|
+
const isContactField =
|
|
11470
|
+
next === 'email' || next === 'impp' || next === 'tel' || (next || '').startsWith('tel-');
|
|
11471
|
+
if (!isContactField) return false;
|
|
11472
|
+
i += 1;
|
|
11473
|
+
}
|
|
10817
11474
|
|
|
10818
11475
|
let end = tokens.length;
|
|
10819
11476
|
if (tokens[end - 1] === 'webauthn') end -= 1;
|
|
@@ -10830,11 +11487,39 @@ function runa11yCoreInPage(pageUrl, contextSelector, engineOptions, runOnly) {
|
|
|
10830
11487
|
const occurrences = [];
|
|
10831
11488
|
let applicableCount = 0;
|
|
10832
11489
|
|
|
11490
|
+
// ACT 73f2c2 exempts controls where the attribute cannot describe an input
|
|
11491
|
+
// purpose: the on/off toggle, disabled controls, input types with a fixed
|
|
11492
|
+
// value, and controls that take no input.
|
|
11493
|
+
const FIXED_VALUE_TYPES = new Set([
|
|
11494
|
+
'button',
|
|
11495
|
+
'checkbox',
|
|
11496
|
+
'file',
|
|
11497
|
+
'image',
|
|
11498
|
+
'radio',
|
|
11499
|
+
'reset',
|
|
11500
|
+
'submit'
|
|
11501
|
+
]);
|
|
11502
|
+
|
|
11503
|
+
function isExempt(el) {
|
|
11504
|
+
const tag = String(el.tagName || '').toLowerCase();
|
|
11505
|
+
if (tag === 'input') {
|
|
11506
|
+
const type = String(el.getAttribute('type') || 'text').toLowerCase();
|
|
11507
|
+
if (FIXED_VALUE_TYPES.has(type)) return true;
|
|
11508
|
+
}
|
|
11509
|
+
if (el.hasAttribute && el.hasAttribute('disabled')) return true;
|
|
11510
|
+
if (String(el.getAttribute('aria-disabled') || '').toLowerCase() === 'true') return true;
|
|
11511
|
+
return false;
|
|
11512
|
+
}
|
|
11513
|
+
|
|
10833
11514
|
for (const el of nodes) {
|
|
10834
11515
|
if (!el || !el.getAttribute) continue;
|
|
10835
11516
|
const raw = String(el.getAttribute('autocomplete') || '').trim();
|
|
10836
11517
|
if (!raw) continue;
|
|
10837
11518
|
|
|
11519
|
+
const tokens = raw.toLowerCase().split(/\s+/).filter(Boolean);
|
|
11520
|
+
if (tokens.length === 1 && (tokens[0] === 'on' || tokens[0] === 'off')) continue;
|
|
11521
|
+
if (isExempt(el)) continue;
|
|
11522
|
+
|
|
10838
11523
|
applicableCount += 1;
|
|
10839
11524
|
|
|
10840
11525
|
if (isValidAutocomplete(raw)) continue;
|
|
@@ -10876,6 +11561,20 @@ function runa11yCoreInPage(pageUrl, contextSelector, engineOptions, runOnly) {
|
|
|
10876
11561
|
const { helpers, rule } = ctx;
|
|
10877
11562
|
|
|
10878
11563
|
const SPACING_PROPS = ['line-height', 'letter-spacing', 'word-spacing'];
|
|
11564
|
+
// WCAG 1.4.12's own metrics, as multiples of the font size.
|
|
11565
|
+
const MIN_RATIO = { 'line-height': 1.5, 'letter-spacing': 0.12, 'word-spacing': 0.16 };
|
|
11566
|
+
// Only these two take the value from the parent, leaving this declaration
|
|
11567
|
+
// specifying no spacing of its own. `initial` and `revert` resolve to a
|
|
11568
|
+
// concrete value (`normal` for all three properties), so they stay in scope.
|
|
11569
|
+
const INHERITED_KEYWORDS = ['inherit', 'unset'];
|
|
11570
|
+
// No user agent's `normal` line height reaches 1.5, so a forced `normal`
|
|
11571
|
+
// always falls short of the metric.
|
|
11572
|
+
const NORMAL_LINE_HEIGHT_RATIO = 1.2;
|
|
11573
|
+
const CAMEL = {
|
|
11574
|
+
'line-height': 'lineHeight',
|
|
11575
|
+
'letter-spacing': 'letterSpacing',
|
|
11576
|
+
'word-spacing': 'wordSpacing'
|
|
11577
|
+
};
|
|
10879
11578
|
|
|
10880
11579
|
const nodes = helpers.queryAllSmart
|
|
10881
11580
|
? helpers.queryAllSmart('[style]')
|
|
@@ -10884,6 +11583,136 @@ function runa11yCoreInPage(pageUrl, contextSelector, engineOptions, runOnly) {
|
|
|
10884
11583
|
const occurrences = [];
|
|
10885
11584
|
let applicableCount = 0;
|
|
10886
11585
|
|
|
11586
|
+
// Within one declaration block, importance wins over order, so the last
|
|
11587
|
+
// important declaration is the one that takes effect. Passed Example 5 of ACT
|
|
11588
|
+
// 78fd32 turns on this and Passed Example 6 on the non-important half.
|
|
11589
|
+
function effectiveDeclaration(raw, prop) {
|
|
11590
|
+
const re = new RegExp('(?:^|;)\\s*' + prop + '\\s*:\\s*([^;]*)', 'gi');
|
|
11591
|
+
let chosen = null;
|
|
11592
|
+
let m;
|
|
11593
|
+
while ((m = re.exec(raw))) {
|
|
11594
|
+
const value = String(m[1] || '').trim();
|
|
11595
|
+
const important = /!\s*important\s*$/i.test(value);
|
|
11596
|
+
const clean = value.replace(/!\s*important\s*$/i, '').trim();
|
|
11597
|
+
if (!clean) continue;
|
|
11598
|
+
if (!chosen || important || !chosen.important) {
|
|
11599
|
+
if (chosen && chosen.important && !important) continue;
|
|
11600
|
+
chosen = { value: clean, important };
|
|
11601
|
+
}
|
|
11602
|
+
}
|
|
11603
|
+
return chosen;
|
|
11604
|
+
}
|
|
11605
|
+
|
|
11606
|
+
function hasVisibleTextChild(el) {
|
|
11607
|
+
let kids;
|
|
11608
|
+
try {
|
|
11609
|
+
kids = el.childNodes ? Array.from(el.childNodes) : [];
|
|
11610
|
+
} catch {
|
|
11611
|
+
return false;
|
|
11612
|
+
}
|
|
11613
|
+
return kids.some((n) => n && n.nodeType === 3 && String(n.nodeValue || '').trim() !== '');
|
|
11614
|
+
}
|
|
11615
|
+
|
|
11616
|
+
function isRendered(el) {
|
|
11617
|
+
if (helpers.isDomVisibleEligible) {
|
|
11618
|
+
try {
|
|
11619
|
+
return !!helpers.isDomVisibleEligible(el, ctx, { targetSet: 'dom' }).eligible;
|
|
11620
|
+
} catch {
|
|
11621
|
+
return true;
|
|
11622
|
+
}
|
|
11623
|
+
}
|
|
11624
|
+
return true;
|
|
11625
|
+
}
|
|
11626
|
+
|
|
11627
|
+
const px = (v) => {
|
|
11628
|
+
const n = parseFloat(v);
|
|
11629
|
+
return Number.isFinite(n) && /px\s*$/.test(String(v)) ? n : null;
|
|
11630
|
+
};
|
|
11631
|
+
|
|
11632
|
+
function computedStyleOf(el) {
|
|
11633
|
+
if (helpers && typeof helpers.computedStyle === 'function') {
|
|
11634
|
+
try {
|
|
11635
|
+
const cs = helpers.computedStyle(el);
|
|
11636
|
+
if (cs) return cs;
|
|
11637
|
+
} catch {
|
|
11638
|
+
// fall through to the realm's own view
|
|
11639
|
+
}
|
|
11640
|
+
}
|
|
11641
|
+
try {
|
|
11642
|
+
const view = el.ownerDocument && el.ownerDocument.defaultView;
|
|
11643
|
+
if (view && typeof view.getComputedStyle === 'function') return view.getComputedStyle(el);
|
|
11644
|
+
} catch {
|
|
11645
|
+
// no computed style available
|
|
11646
|
+
}
|
|
11647
|
+
return null;
|
|
11648
|
+
}
|
|
11649
|
+
|
|
11650
|
+
// ACT scopes these rules to text visible on screen, and text pushed far off
|
|
11651
|
+
// canvas is the one hidden shape the shared eligibility check keeps eligible.
|
|
11652
|
+
function isOffScreen(el) {
|
|
11653
|
+
if (!helpers.getVisibilityHintsInfo) return false;
|
|
11654
|
+
try {
|
|
11655
|
+
const info = helpers.getVisibilityHintsInfo(el, ctx, {});
|
|
11656
|
+
return !!(info && Array.isArray(info.hints) && info.hints.indexOf('offscreen') !== -1);
|
|
11657
|
+
} catch {
|
|
11658
|
+
return false;
|
|
11659
|
+
}
|
|
11660
|
+
}
|
|
11661
|
+
|
|
11662
|
+
// A realm that does not lay the document out reports font-size as the CSS
|
|
11663
|
+
// absolute-size keyword rather than a length. Resolving those keeps px-valued
|
|
11664
|
+
// spacing checkable there, and cannot manufacture a failure: a font size
|
|
11665
|
+
// larger than assumed only makes a px ratio smaller.
|
|
11666
|
+
const ABSOLUTE_FONT_SIZES = {
|
|
11667
|
+
'xx-small': 9,
|
|
11668
|
+
'x-small': 10,
|
|
11669
|
+
small: 13,
|
|
11670
|
+
medium: 16,
|
|
11671
|
+
large: 18,
|
|
11672
|
+
'x-large': 24,
|
|
11673
|
+
'xx-large': 32
|
|
11674
|
+
};
|
|
11675
|
+
|
|
11676
|
+
function fontSizeOf(cs) {
|
|
11677
|
+
if (!cs) return null;
|
|
11678
|
+
const direct = px(cs.fontSize);
|
|
11679
|
+
if (direct !== null) return direct;
|
|
11680
|
+
const keyword = String(cs.fontSize || '')
|
|
11681
|
+
.trim()
|
|
11682
|
+
.toLowerCase();
|
|
11683
|
+
return ABSOLUTE_FONT_SIZES[keyword] || null;
|
|
11684
|
+
}
|
|
11685
|
+
|
|
11686
|
+
/**
|
|
11687
|
+
* The spacing as a multiple of the font size, or null when it cannot be
|
|
11688
|
+
* resolved. Computed style is preferred because it already applies the
|
|
11689
|
+
* cascade and unit resolution; the declared value is only a fallback for
|
|
11690
|
+
* environments that do not lay the document out.
|
|
11691
|
+
*/
|
|
11692
|
+
function spacingRatio(el, prop, declared) {
|
|
11693
|
+
const cs = computedStyleOf(el);
|
|
11694
|
+
const fontSize = fontSizeOf(cs);
|
|
11695
|
+
if (cs && fontSize) {
|
|
11696
|
+
const used = px(cs[CAMEL[prop]]);
|
|
11697
|
+
if (used !== null) return used / fontSize;
|
|
11698
|
+
}
|
|
11699
|
+
|
|
11700
|
+
const v = String(declared || '')
|
|
11701
|
+
.trim()
|
|
11702
|
+
.toLowerCase();
|
|
11703
|
+
// `normal`, and the keywords that resolve to it, add nothing for the two
|
|
11704
|
+
// spacing properties and stay under the metric for line height.
|
|
11705
|
+
if (v === 'normal' || v === 'initial' || v === 'revert' || v === 'revert-layer') {
|
|
11706
|
+
return prop === 'line-height' ? NORMAL_LINE_HEIGHT_RATIO : 0;
|
|
11707
|
+
}
|
|
11708
|
+
if (/^[0-9.]+$/.test(v)) return parseFloat(v);
|
|
11709
|
+
if (/em$/.test(v)) return parseFloat(v);
|
|
11710
|
+
if (/%$/.test(v)) return parseFloat(v) / 100;
|
|
11711
|
+
const asPx = px(v);
|
|
11712
|
+
if (asPx !== null && fontSize) return asPx / fontSize;
|
|
11713
|
+
return null;
|
|
11714
|
+
}
|
|
11715
|
+
|
|
10887
11716
|
for (const el of nodes) {
|
|
10888
11717
|
if (!el || !el.getAttribute) continue;
|
|
10889
11718
|
const raw = String(el.getAttribute('style') || '');
|
|
@@ -10893,14 +11722,25 @@ function runa11yCoreInPage(pageUrl, contextSelector, engineOptions, runOnly) {
|
|
|
10893
11722
|
const hasAnySpacingProp = SPACING_PROPS.some((p) => lower.includes(p));
|
|
10894
11723
|
if (!hasAnySpacingProp) continue;
|
|
10895
11724
|
|
|
10896
|
-
|
|
11725
|
+
// The rule is about text the user needs to re-space, so an element with no
|
|
11726
|
+
// text of its own, or none that renders, is not a target.
|
|
11727
|
+
if (!hasVisibleTextChild(el) || !isRendered(el) || isOffScreen(el)) continue;
|
|
10897
11728
|
|
|
10898
11729
|
const flagged = [];
|
|
11730
|
+
let inScope = false;
|
|
10899
11731
|
for (const prop of SPACING_PROPS) {
|
|
10900
|
-
const
|
|
10901
|
-
if (
|
|
10902
|
-
|
|
10903
|
-
|
|
11732
|
+
const decl = effectiveDeclaration(raw, prop);
|
|
11733
|
+
if (!decl || !decl.important) continue;
|
|
11734
|
+
if (INHERITED_KEYWORDS.indexOf(decl.value.toLowerCase()) !== -1) continue;
|
|
11735
|
+
inScope = true;
|
|
11736
|
+
const ratio = spacingRatio(el, prop, decl.value);
|
|
11737
|
+
// Unresolvable spacing is left alone: this engine reserves fail for
|
|
11738
|
+
// high-confidence violations.
|
|
11739
|
+
if (ratio === null) continue;
|
|
11740
|
+
if (ratio < MIN_RATIO[prop]) flagged.push(prop);
|
|
11741
|
+
}
|
|
11742
|
+
|
|
11743
|
+
if (inScope) applicableCount += 1;
|
|
10904
11744
|
if (!flagged.length) continue;
|
|
10905
11745
|
|
|
10906
11746
|
const tag = el.tagName.toLowerCase();
|
|
@@ -10910,8 +11750,8 @@ function runa11yCoreInPage(pageUrl, contextSelector, engineOptions, runOnly) {
|
|
|
10910
11750
|
occurrences.push({
|
|
10911
11751
|
selector: stableSelector,
|
|
10912
11752
|
html,
|
|
10913
|
-
summary: `This element's inline style forces ${flagged.join(', ')} with !important
|
|
10914
|
-
hint: 'Remove !important from line-height/letter-spacing/word-spacing in inline styles
|
|
11753
|
+
summary: `This element's inline style forces ${flagged.join(', ')} with !important below the WCAG text-spacing metric, so the user cannot raise it.`,
|
|
11754
|
+
hint: 'Remove !important from line-height/letter-spacing/word-spacing in inline styles, or set a value that already meets the metric (line-height 1.5, letter-spacing 0.12em, word-spacing 0.16em).',
|
|
10915
11755
|
i18n: {
|
|
10916
11756
|
summaryKey: 'avoidInlineSpacing_summary_fail',
|
|
10917
11757
|
hintKey: 'avoidInlineSpacing_hint_fail',
|
|
@@ -11030,9 +11870,16 @@ function runa11yCoreInPage(pageUrl, contextSelector, engineOptions, runOnly) {
|
|
|
11030
11870
|
return '';
|
|
11031
11871
|
}
|
|
11032
11872
|
|
|
11873
|
+
// Naming rules apply only to elements included in the accessibility tree
|
|
11874
|
+
// (ACT c487ae and siblings), which excludes focusable aria-hidden content.
|
|
11875
|
+
// aria-hidden-focus (ACT 6cfa84) covers that markup instead.
|
|
11033
11876
|
function isEligibleAcc(helpers, el, ctx) {
|
|
11034
11877
|
const fn =
|
|
11035
|
-
helpers && typeof helpers.
|
|
11878
|
+
helpers && typeof helpers.isIncludedInAccessibilityTree === 'function'
|
|
11879
|
+
? helpers.isIncludedInAccessibilityTree
|
|
11880
|
+
: helpers && typeof helpers.isAccTreeEligible === 'function'
|
|
11881
|
+
? helpers.isAccTreeEligible
|
|
11882
|
+
: null;
|
|
11036
11883
|
if (!fn) return true;
|
|
11037
11884
|
try {
|
|
11038
11885
|
const r = fn(el, ctx);
|
|
@@ -11046,8 +11893,9 @@ function runa11yCoreInPage(pageUrl, contextSelector, engineOptions, runOnly) {
|
|
|
11046
11893
|
const occurrences = [];
|
|
11047
11894
|
let applicableCount = 0;
|
|
11048
11895
|
|
|
11049
|
-
|
|
11050
|
-
|
|
11896
|
+
// Native checkbox/radio without an explicit role belongs to
|
|
11897
|
+
// form-control-programmatic-label-present.
|
|
11898
|
+
const selector = '[role="checkbox"], [role="radio"], [role="switch"]';
|
|
11051
11899
|
const nodes = helpers.queryAllSmart
|
|
11052
11900
|
? helpers.queryAllSmart(selector)
|
|
11053
11901
|
: helpers.queryAll(selector);
|
|
@@ -11240,7 +12088,14 @@ function runa11yCoreInPage(pageUrl, contextSelector, engineOptions, runOnly) {
|
|
|
11240
12088
|
|
|
11241
12089
|
for (const el of nodes) {
|
|
11242
12090
|
// isAccTreeEligible returns { eligible, reasons }, not a boolean.
|
|
11243
|
-
|
|
12091
|
+
// Naming rules apply only to elements included in the accessibility tree
|
|
12092
|
+
// (ACT c487ae), which excludes focusable aria-hidden content;
|
|
12093
|
+
// aria-hidden-focus (ACT 6cfa84) covers that markup instead.
|
|
12094
|
+
const eligResult = helpers.isIncludedInAccessibilityTree
|
|
12095
|
+
? helpers.isIncludedInAccessibilityTree(el, ctx)
|
|
12096
|
+
: helpers.isAccTreeEligible
|
|
12097
|
+
? helpers.isAccTreeEligible(el, ctx)
|
|
12098
|
+
: true;
|
|
11244
12099
|
const eligible =
|
|
11245
12100
|
typeof eligResult === 'boolean' ? eligResult : !!(eligResult && eligResult.eligible);
|
|
11246
12101
|
if (!eligible) continue;
|
|
@@ -11266,24 +12121,50 @@ function runa11yCoreInPage(pageUrl, contextSelector, engineOptions, runOnly) {
|
|
|
11266
12121
|
inputValueName = getInputButtonValueName(el);
|
|
11267
12122
|
}
|
|
11268
12123
|
|
|
11269
|
-
//
|
|
11270
|
-
//
|
|
11271
|
-
//
|
|
11272
|
-
// name-from-
|
|
11273
|
-
|
|
11274
|
-
|
|
11275
|
-
|
|
11276
|
-
|
|
11277
|
-
'
|
|
11278
|
-
'
|
|
11279
|
-
'
|
|
11280
|
-
'
|
|
11281
|
-
'
|
|
11282
|
-
'
|
|
11283
|
-
'
|
|
12124
|
+
// ARIA 1.2 "Name From: author, contents". Every other known role is
|
|
12125
|
+
// name-from-author-only: <button role="combobox">List</button> exposes a
|
|
12126
|
+
// value, not a label. An unknown role falls back to the implicit role.
|
|
12127
|
+
// <generated:aria-name-from-content>
|
|
12128
|
+
const NAME_FROM_CONTENT_ROLES = [
|
|
12129
|
+
'button',
|
|
12130
|
+
'cell',
|
|
12131
|
+
'checkbox',
|
|
12132
|
+
'columnheader',
|
|
12133
|
+
'doc-backlink',
|
|
12134
|
+
'doc-biblioref',
|
|
12135
|
+
'doc-glossref',
|
|
12136
|
+
'doc-noteref',
|
|
12137
|
+
'graphics-object',
|
|
12138
|
+
'gridcell',
|
|
12139
|
+
'heading',
|
|
12140
|
+
'link',
|
|
12141
|
+
'menuitem',
|
|
12142
|
+
'menuitemcheckbox',
|
|
12143
|
+
'menuitemradio',
|
|
12144
|
+
'option',
|
|
12145
|
+
'radio',
|
|
12146
|
+
'row',
|
|
12147
|
+
'rowgroup',
|
|
12148
|
+
'rowheader',
|
|
12149
|
+
'switch',
|
|
12150
|
+
'tab',
|
|
12151
|
+
'tooltip',
|
|
12152
|
+
'treeitem'
|
|
11284
12153
|
];
|
|
12154
|
+
// </generated:aria-name-from-content>
|
|
12155
|
+
const isKnownRoleToken =
|
|
12156
|
+
helpers && helpers.aria && typeof helpers.aria.isKnownRole === 'function'
|
|
12157
|
+
? (() => {
|
|
12158
|
+
try {
|
|
12159
|
+
return !!helpers.aria.isKnownRole(roleNorm);
|
|
12160
|
+
} catch {
|
|
12161
|
+
return false;
|
|
12162
|
+
}
|
|
12163
|
+
})()
|
|
12164
|
+
: false;
|
|
11285
12165
|
const isContentNameCandidate =
|
|
11286
|
-
(tag === 'button' || role === 'button') &&
|
|
12166
|
+
(tag === 'button' || role === 'button') &&
|
|
12167
|
+
(!roleNorm || !isKnownRoleToken || NAME_FROM_CONTENT_ROLES.includes(roleNorm));
|
|
11287
12168
|
const contentName =
|
|
11288
12169
|
!trustedProgrammaticName && !inputValueName && isContentNameCandidate
|
|
11289
12170
|
? getConservativeSubtreeText(el)
|
|
@@ -11382,7 +12263,8 @@ function runa11yCoreInPage(pageUrl, contextSelector, engineOptions, runOnly) {
|
|
|
11382
12263
|
// (e.g. a page whose only <h1> sits inside a display:none ancestor,
|
|
11383
12264
|
// unreachable by sighted and screen reader users alike). A fully
|
|
11384
12265
|
// non-rendered <main>/heading must not be credited here, since that would
|
|
11385
|
-
// wrongly
|
|
12266
|
+
// wrongly treat a page with zero currently-exposed bypass mechanisms as
|
|
12267
|
+
// having one.
|
|
11386
12268
|
function hasMainLandmark() {
|
|
11387
12269
|
for (const el of queryAll('main, [role="main"]')) {
|
|
11388
12270
|
if (el && isExposedToAt(el)) return true;
|
|
@@ -11390,10 +12272,41 @@ function runa11yCoreInPage(pageUrl, contextSelector, engineOptions, runOnly) {
|
|
|
11390
12272
|
return false;
|
|
11391
12273
|
}
|
|
11392
12274
|
|
|
12275
|
+
// Resolve a fragment id (or legacy <a name>) inside a specific root node
|
|
12276
|
+
// (a Document or a ShadowRoot). Both expose getElementById; querySelector
|
|
12277
|
+
// is used for the legacy anchor-name fallback.
|
|
12278
|
+
function resolveInRoot(root, fragment) {
|
|
12279
|
+
if (!root) return null;
|
|
12280
|
+
let target;
|
|
12281
|
+
try {
|
|
12282
|
+
target = typeof root.getElementById === 'function' ? root.getElementById(fragment) : null;
|
|
12283
|
+
} catch {
|
|
12284
|
+
target = null;
|
|
12285
|
+
}
|
|
12286
|
+
if (target) return target;
|
|
12287
|
+
try {
|
|
12288
|
+
target =
|
|
12289
|
+
typeof root.querySelector === 'function'
|
|
12290
|
+
? root.querySelector('a[name="' + fragment.replace(/"/g, '\\"') + '"]')
|
|
12291
|
+
: null;
|
|
12292
|
+
} catch {
|
|
12293
|
+
target = null;
|
|
12294
|
+
}
|
|
12295
|
+
return target;
|
|
12296
|
+
}
|
|
12297
|
+
|
|
12298
|
+
// Shadow-DOM-aware: gather anchors via queryAllSmart (pierces shadow roots
|
|
12299
|
+
// when includeShadowDom is enabled, and drops hard-hidden links), and
|
|
12300
|
+
// resolve each fragment in the link's own root before falling back to the
|
|
12301
|
+
// document. This credits a skip link encapsulated in a web component the
|
|
12302
|
+
// same way as one authored in the light DOM.
|
|
11393
12303
|
function hasWorkingAnchorLink() {
|
|
11394
12304
|
let links;
|
|
11395
12305
|
try {
|
|
11396
|
-
links =
|
|
12306
|
+
links =
|
|
12307
|
+
helpers && typeof helpers.queryAllSmart === 'function'
|
|
12308
|
+
? helpers.queryAllSmart('a[href]')
|
|
12309
|
+
: document.querySelectorAll('a[href]');
|
|
11397
12310
|
} catch {
|
|
11398
12311
|
links = [];
|
|
11399
12312
|
}
|
|
@@ -11410,18 +12323,19 @@ function runa11yCoreInPage(pageUrl, contextSelector, engineOptions, runOnly) {
|
|
|
11410
12323
|
fragment = fragment.trim();
|
|
11411
12324
|
if (!fragment) continue;
|
|
11412
12325
|
|
|
11413
|
-
let
|
|
12326
|
+
let root = document;
|
|
11414
12327
|
try {
|
|
11415
|
-
|
|
12328
|
+
if (typeof a.getRootNode === 'function') {
|
|
12329
|
+
const r = a.getRootNode();
|
|
12330
|
+
if (r) root = r;
|
|
12331
|
+
}
|
|
11416
12332
|
} catch {
|
|
11417
|
-
|
|
12333
|
+
root = document;
|
|
11418
12334
|
}
|
|
11419
|
-
|
|
11420
|
-
|
|
11421
|
-
|
|
11422
|
-
|
|
11423
|
-
target = null;
|
|
11424
|
-
}
|
|
12335
|
+
|
|
12336
|
+
let target = resolveInRoot(root, fragment);
|
|
12337
|
+
if (!target && root !== document) {
|
|
12338
|
+
target = resolveInRoot(document, fragment);
|
|
11425
12339
|
}
|
|
11426
12340
|
if (target) return true;
|
|
11427
12341
|
}
|
|
@@ -11439,8 +12353,9 @@ function runa11yCoreInPage(pageUrl, contextSelector, engineOptions, runOnly) {
|
|
|
11439
12353
|
const anchorLink = mainLandmark ? false : hasWorkingAnchorLink();
|
|
11440
12354
|
const heading = mainLandmark || anchorLink ? false : hasHeading();
|
|
11441
12355
|
|
|
12356
|
+
// A recognized mechanism is present -> nothing to review on this page.
|
|
11442
12357
|
if (mainLandmark || anchorLink || heading) {
|
|
11443
|
-
return { ruleId: rule.ruleId, outcome: '
|
|
12358
|
+
return { ruleId: rule.ruleId, outcome: 'notApplicable', severity: 'minor', occurrences: [] };
|
|
11444
12359
|
}
|
|
11445
12360
|
|
|
11446
12361
|
const stableSelector = helpers.buildSelector ? helpers.buildSelector(body) : 'body';
|
|
@@ -11452,11 +12367,12 @@ function runa11yCoreInPage(pageUrl, contextSelector, engineOptions, runOnly) {
|
|
|
11452
12367
|
{
|
|
11453
12368
|
selector: stableSelector,
|
|
11454
12369
|
html,
|
|
11455
|
-
summary:
|
|
11456
|
-
|
|
12370
|
+
summary:
|
|
12371
|
+
'No recognized way to bypass repeated blocks of content was detected on this page — verify a bypass mechanism exists.',
|
|
12372
|
+
hint: 'Confirm the page offers a bypass mechanism: a main landmark (<main> or role="main"), a working "skip to content" link, or heading elements that assistive technology can use to jump past repeated content. (A mechanism may be temporarily hidden — e.g. while a modal dialog makes the page inert — or provided on a per-site basis; this needs human confirmation.)',
|
|
11457
12373
|
i18n: {
|
|
11458
|
-
summaryKey: '
|
|
11459
|
-
hintKey: '
|
|
12374
|
+
summaryKey: 'bypassBlocksPresent_summary_cantTell',
|
|
12375
|
+
hintKey: 'bypassBlocksPresent_hint_cantTell',
|
|
11460
12376
|
params: {}
|
|
11461
12377
|
},
|
|
11462
12378
|
data: {
|
|
@@ -11468,8 +12384,8 @@ function runa11yCoreInPage(pageUrl, contextSelector, engineOptions, runOnly) {
|
|
|
11468
12384
|
|
|
11469
12385
|
return {
|
|
11470
12386
|
ruleId: rule.ruleId,
|
|
11471
|
-
outcome: '
|
|
11472
|
-
severity: rule.defaultSeverity || '
|
|
12387
|
+
outcome: 'cantTell',
|
|
12388
|
+
severity: rule.defaultSeverity || 'moderate',
|
|
11473
12389
|
occurrences
|
|
11474
12390
|
};
|
|
11475
12391
|
}), applicability: (function applicability(ctx) {
|
|
@@ -11497,8 +12413,15 @@ function runa11yCoreInPage(pageUrl, contextSelector, engineOptions, runOnly) {
|
|
|
11497
12413
|
const getEligibilityInfo =
|
|
11498
12414
|
helpers && typeof helpers.getEligibilityInfo === 'function' ? helpers.getEligibilityInfo : null;
|
|
11499
12415
|
|
|
11500
|
-
|
|
11501
|
-
|
|
12416
|
+
// aria-hidden removes the canvas from the accessibility tree, so a text
|
|
12417
|
+
// alternative on it cannot reach anyone; aria-hidden-focus reports a
|
|
12418
|
+
// focusable one. Matches the other non-text-content rules.
|
|
12419
|
+
const isEligibleHelper =
|
|
12420
|
+
helpers && typeof helpers.isIncludedInAccessibilityTree === 'function'
|
|
12421
|
+
? helpers.isIncludedInAccessibilityTree
|
|
12422
|
+
: helpers && typeof helpers.isAccTreeEligible === 'function'
|
|
12423
|
+
? helpers.isAccTreeEligible
|
|
12424
|
+
: null;
|
|
11502
12425
|
|
|
11503
12426
|
const getTextAlternativeInfo =
|
|
11504
12427
|
helpers && typeof helpers.getTextAlternativeInfo === 'function'
|
|
@@ -11523,15 +12446,16 @@ function runa11yCoreInPage(pageUrl, contextSelector, engineOptions, runOnly) {
|
|
|
11523
12446
|
for (const el of canvases) {
|
|
11524
12447
|
if (!el) continue;
|
|
11525
12448
|
|
|
11526
|
-
// Applicability:
|
|
11527
|
-
if (
|
|
12449
|
+
// Applicability: included in the accessibility tree.
|
|
12450
|
+
if (isEligibleHelper) {
|
|
11528
12451
|
const elig = (() => {
|
|
11529
12452
|
try {
|
|
11530
|
-
return
|
|
12453
|
+
return isEligibleHelper(el, ctx);
|
|
11531
12454
|
} catch {
|
|
11532
12455
|
return { eligible: true, reasons: [] };
|
|
11533
12456
|
}
|
|
11534
12457
|
})();
|
|
12458
|
+
if (elig === false) continue;
|
|
11535
12459
|
if (elig && elig.eligible === false) continue;
|
|
11536
12460
|
}
|
|
11537
12461
|
|
|
@@ -11846,9 +12770,16 @@ function runa11yCoreInPage(pageUrl, contextSelector, engineOptions, runOnly) {
|
|
|
11846
12770
|
return '';
|
|
11847
12771
|
}
|
|
11848
12772
|
|
|
12773
|
+
// Naming rules apply only to elements included in the accessibility tree
|
|
12774
|
+
// (ACT c487ae and siblings), which excludes focusable aria-hidden content.
|
|
12775
|
+
// aria-hidden-focus (ACT 6cfa84) covers that markup instead.
|
|
11849
12776
|
function isEligibleAcc(helpers, el, ctx) {
|
|
11850
12777
|
const fn =
|
|
11851
|
-
helpers && typeof helpers.
|
|
12778
|
+
helpers && typeof helpers.isIncludedInAccessibilityTree === 'function'
|
|
12779
|
+
? helpers.isIncludedInAccessibilityTree
|
|
12780
|
+
: helpers && typeof helpers.isAccTreeEligible === 'function'
|
|
12781
|
+
? helpers.isAccTreeEligible
|
|
12782
|
+
: null;
|
|
11852
12783
|
if (!fn) return true;
|
|
11853
12784
|
try {
|
|
11854
12785
|
const r = fn(el, ctx);
|
|
@@ -14075,9 +15006,16 @@ function runa11yCoreInPage(pageUrl, contextSelector, engineOptions, runOnly) {
|
|
|
14075
15006
|
return '';
|
|
14076
15007
|
}
|
|
14077
15008
|
|
|
15009
|
+
// Naming rules apply only to elements included in the accessibility tree
|
|
15010
|
+
// (ACT c487ae and siblings), which excludes focusable aria-hidden content.
|
|
15011
|
+
// aria-hidden-focus (ACT 6cfa84) covers that markup instead.
|
|
14078
15012
|
function isEligibleAcc(helpers, el, ctx) {
|
|
14079
15013
|
const fn =
|
|
14080
|
-
helpers && typeof helpers.
|
|
15014
|
+
helpers && typeof helpers.isIncludedInAccessibilityTree === 'function'
|
|
15015
|
+
? helpers.isIncludedInAccessibilityTree
|
|
15016
|
+
: helpers && typeof helpers.isAccTreeEligible === 'function'
|
|
15017
|
+
? helpers.isAccTreeEligible
|
|
15018
|
+
: null;
|
|
14081
15019
|
if (!fn) return true;
|
|
14082
15020
|
try {
|
|
14083
15021
|
const r = fn(el, ctx);
|
|
@@ -15044,8 +15982,15 @@ function runa11yCoreInPage(pageUrl, contextSelector, engineOptions, runOnly) {
|
|
|
15044
15982
|
"form-control-programmatic-label-present": { run: (function runInPage(ctx) {
|
|
15045
15983
|
const { helpers, rule } = ctx;
|
|
15046
15984
|
|
|
15047
|
-
|
|
15048
|
-
|
|
15985
|
+
// ACT e086e5 applies only to controls included in the accessibility tree, so
|
|
15986
|
+
// a focusable control inside aria-hidden is out of scope; aria-hidden-focus
|
|
15987
|
+
// reports that markup instead.
|
|
15988
|
+
const isEligibleHelper =
|
|
15989
|
+
helpers && typeof helpers.isIncludedInAccessibilityTree === 'function'
|
|
15990
|
+
? helpers.isIncludedInAccessibilityTree
|
|
15991
|
+
: helpers && typeof helpers.isAccTreeEligible === 'function'
|
|
15992
|
+
? helpers.isAccTreeEligible
|
|
15993
|
+
: null;
|
|
15049
15994
|
const getEligibilityInfo =
|
|
15050
15995
|
helpers && typeof helpers.getEligibilityInfo === 'function' ? helpers.getEligibilityInfo : null;
|
|
15051
15996
|
|
|
@@ -15056,6 +16001,41 @@ function runa11yCoreInPage(pageUrl, contextSelector, engineOptions, runOnly) {
|
|
|
15056
16001
|
|
|
15057
16002
|
const trim = (v) => (v == null ? '' : String(v)).trim();
|
|
15058
16003
|
|
|
16004
|
+
// Roles with their own *-name-present rule; a control carrying one
|
|
16005
|
+
// explicitly is skipped here so it is reported once.
|
|
16006
|
+
const ROLE_OWNED_ELSEWHERE = [
|
|
16007
|
+
'alertdialog',
|
|
16008
|
+
'button',
|
|
16009
|
+
'checkbox',
|
|
16010
|
+
'combobox',
|
|
16011
|
+
'dialog',
|
|
16012
|
+
'grid',
|
|
16013
|
+
'link',
|
|
16014
|
+
'listbox',
|
|
16015
|
+
'menu',
|
|
16016
|
+
'menubar',
|
|
16017
|
+
'menuitem',
|
|
16018
|
+
'menuitemcheckbox',
|
|
16019
|
+
'menuitemradio',
|
|
16020
|
+
'meter',
|
|
16021
|
+
'option',
|
|
16022
|
+
'progressbar',
|
|
16023
|
+
'radio',
|
|
16024
|
+
'radiogroup',
|
|
16025
|
+
'scrollbar',
|
|
16026
|
+
'searchbox',
|
|
16027
|
+
'slider',
|
|
16028
|
+
'spinbutton',
|
|
16029
|
+
'switch',
|
|
16030
|
+
'tab',
|
|
16031
|
+
'tablist',
|
|
16032
|
+
'textbox',
|
|
16033
|
+
'toolbar',
|
|
16034
|
+
'tooltip',
|
|
16035
|
+
'tree',
|
|
16036
|
+
'treeitem'
|
|
16037
|
+
];
|
|
16038
|
+
|
|
15059
16039
|
const metrics = {
|
|
15060
16040
|
applicableCount: 0,
|
|
15061
16041
|
passCount: 0,
|
|
@@ -15079,9 +16059,9 @@ function runa11yCoreInPage(pageUrl, contextSelector, engineOptions, runOnly) {
|
|
|
15079
16059
|
}
|
|
15080
16060
|
|
|
15081
16061
|
function isEligibleAcc(el) {
|
|
15082
|
-
if (!
|
|
16062
|
+
if (!isEligibleHelper) return true;
|
|
15083
16063
|
try {
|
|
15084
|
-
const r =
|
|
16064
|
+
const r = isEligibleHelper(el, ctx);
|
|
15085
16065
|
if (typeof r === 'boolean') return r;
|
|
15086
16066
|
return !!(r && r.eligible);
|
|
15087
16067
|
} catch {
|
|
@@ -15164,6 +16144,8 @@ function runa11yCoreInPage(pageUrl, contextSelector, engineOptions, runOnly) {
|
|
|
15164
16144
|
role = '';
|
|
15165
16145
|
}
|
|
15166
16146
|
|
|
16147
|
+
if (role && ROLE_OWNED_ELSEWHERE.indexOf(role) !== -1) continue;
|
|
16148
|
+
|
|
15167
16149
|
if (role === 'presentation' || role === 'none') {
|
|
15168
16150
|
const fi = getFocusableInfo
|
|
15169
16151
|
? (() => {
|
|
@@ -15459,6 +16441,12 @@ function runa11yCoreInPage(pageUrl, contextSelector, engineOptions, runOnly) {
|
|
|
15459
16441
|
|
|
15460
16442
|
const isAccTreeEligible =
|
|
15461
16443
|
helpers && typeof helpers.isAccTreeEligible === 'function' ? helpers.isAccTreeEligible : null;
|
|
16444
|
+
const getAriaNameInfo =
|
|
16445
|
+
helpers && typeof helpers.getAriaNameInfo === 'function' ? helpers.getAriaNameInfo : null;
|
|
16446
|
+
const labelContributesName =
|
|
16447
|
+
helpers && typeof helpers.labelContributesAccessibleName === 'function'
|
|
16448
|
+
? helpers.labelContributesAccessibleName
|
|
16449
|
+
: null;
|
|
15462
16450
|
|
|
15463
16451
|
const selector =
|
|
15464
16452
|
'input:not([type="hidden"]):not([type="submit"]):not([type="reset"]):not([type="button"]):not([type="image"]),select,textarea';
|
|
@@ -15479,7 +16467,8 @@ function runa11yCoreInPage(pageUrl, contextSelector, engineOptions, runOnly) {
|
|
|
15479
16467
|
labelsByFor.get(forValue).push(lab);
|
|
15480
16468
|
}
|
|
15481
16469
|
|
|
15482
|
-
const
|
|
16470
|
+
const failOccurrences = [];
|
|
16471
|
+
const cantTellOccurrences = [];
|
|
15483
16472
|
let applicableCount = 0;
|
|
15484
16473
|
|
|
15485
16474
|
for (const el of nodes) {
|
|
@@ -15508,42 +16497,94 @@ function runa11yCoreInPage(pageUrl, contextSelector, engineOptions, runOnly) {
|
|
|
15508
16497
|
|
|
15509
16498
|
if (eligibleLabels.size <= 1) continue;
|
|
15510
16499
|
|
|
16500
|
+
// An override (aria-labelledby / aria-label) supersedes every native
|
|
16501
|
+
// <label>, so the labels contribute nothing to the accessible name and
|
|
16502
|
+
// cannot compete.
|
|
16503
|
+
const override = getAriaNameInfo ? getAriaNameInfo(el, ctx) : null;
|
|
16504
|
+
if (override && override.present && override.value) continue;
|
|
16505
|
+
|
|
16506
|
+
// Without an override the labels feed the name; only labels with their
|
|
16507
|
+
// own text compete for it.
|
|
16508
|
+
const contributing = labelContributesName
|
|
16509
|
+
? [...eligibleLabels].filter((lab) => labelContributesName(lab))
|
|
16510
|
+
: [...eligibleLabels];
|
|
16511
|
+
|
|
15511
16512
|
const tag = el.tagName.toLowerCase();
|
|
15512
16513
|
const stableSelector = helpers.buildSelector ? helpers.buildSelector(el) : 'html';
|
|
15513
16514
|
const html = helpers.getOuterHtmlSnippet ? helpers.getOuterHtmlSnippet(el) : el.outerHTML || '';
|
|
15514
16515
|
|
|
15515
|
-
|
|
15516
|
-
|
|
15517
|
-
|
|
15518
|
-
|
|
15519
|
-
|
|
15520
|
-
|
|
15521
|
-
|
|
15522
|
-
|
|
15523
|
-
|
|
15524
|
-
|
|
15525
|
-
|
|
15526
|
-
|
|
15527
|
-
|
|
15528
|
-
|
|
15529
|
-
|
|
16516
|
+
if (contributing.length >= 2) {
|
|
16517
|
+
failOccurrences.push({
|
|
16518
|
+
selector: stableSelector,
|
|
16519
|
+
html,
|
|
16520
|
+
summary: 'This form control is associated with more than one <label>.',
|
|
16521
|
+
hint: 'Keep only one <label> per form control (either wrapping it or referencing it via for/id).',
|
|
16522
|
+
occurrenceOutcome: 'fail',
|
|
16523
|
+
i18n: {
|
|
16524
|
+
summaryKey: 'formControlSingleLabel_summary_fail',
|
|
16525
|
+
hintKey: 'formControlSingleLabel_hint_fail',
|
|
16526
|
+
params: { element: tag, labelCount: String(contributing.length) }
|
|
16527
|
+
},
|
|
16528
|
+
data: {
|
|
16529
|
+
details: {
|
|
16530
|
+
reasonCode: 'FORM_FIELD_MULTIPLE_LABELS',
|
|
16531
|
+
element: tag,
|
|
16532
|
+
labelCount: contributing.length
|
|
16533
|
+
}
|
|
15530
16534
|
}
|
|
15531
|
-
}
|
|
15532
|
-
})
|
|
16535
|
+
});
|
|
16536
|
+
} else if (contributing.length === 1) {
|
|
16537
|
+
cantTellOccurrences.push({
|
|
16538
|
+
selector: stableSelector,
|
|
16539
|
+
html,
|
|
16540
|
+
summary:
|
|
16541
|
+
'This form control has one labelling <label> plus an extra empty <label> association.',
|
|
16542
|
+
hint: 'Remove the redundant empty <label> so exactly one <label> is associated with the control.',
|
|
16543
|
+
occurrenceOutcome: 'cantTell',
|
|
16544
|
+
i18n: {
|
|
16545
|
+
summaryKey: 'formControlSingleLabel_summary_cantTell',
|
|
16546
|
+
hintKey: 'formControlSingleLabel_hint_cantTell',
|
|
16547
|
+
params: { element: tag, labelCount: String(eligibleLabels.size) }
|
|
16548
|
+
},
|
|
16549
|
+
data: {
|
|
16550
|
+
details: {
|
|
16551
|
+
reasonCode: 'FORM_FIELD_EXTRA_EMPTY_LABEL',
|
|
16552
|
+
element: tag,
|
|
16553
|
+
labelCount: eligibleLabels.size,
|
|
16554
|
+
contributingLabelCount: contributing.length
|
|
16555
|
+
}
|
|
16556
|
+
}
|
|
16557
|
+
});
|
|
16558
|
+
}
|
|
16559
|
+
// contributing.length === 0: no label carries text, so nothing competes
|
|
16560
|
+
// for the name. A control left unnamed is form-control-programmatic-
|
|
16561
|
+
// label-present's concern.
|
|
15533
16562
|
}
|
|
15534
16563
|
|
|
15535
16564
|
if (applicableCount === 0) {
|
|
15536
16565
|
return { ruleId: rule.ruleId, outcome: 'notApplicable', severity: 'minor', occurrences: [] };
|
|
15537
16566
|
}
|
|
15538
|
-
|
|
15539
|
-
|
|
15540
|
-
|
|
15541
|
-
|
|
15542
|
-
|
|
15543
|
-
|
|
15544
|
-
|
|
15545
|
-
|
|
15546
|
-
|
|
16567
|
+
|
|
16568
|
+
const resolved = helpers.resolveTieredOutcome
|
|
16569
|
+
? helpers.resolveTieredOutcome(
|
|
16570
|
+
failOccurrences,
|
|
16571
|
+
cantTellOccurrences,
|
|
16572
|
+
rule.defaultSeverity || 'moderate'
|
|
16573
|
+
)
|
|
16574
|
+
: failOccurrences.length
|
|
16575
|
+
? {
|
|
16576
|
+
outcome: 'fail',
|
|
16577
|
+
severity: rule.defaultSeverity || 'moderate',
|
|
16578
|
+
occurrences: failOccurrences
|
|
16579
|
+
}
|
|
16580
|
+
: cantTellOccurrences.length
|
|
16581
|
+
? {
|
|
16582
|
+
outcome: 'cantTell',
|
|
16583
|
+
severity: rule.defaultSeverity || 'moderate',
|
|
16584
|
+
occurrences: cantTellOccurrences
|
|
16585
|
+
}
|
|
16586
|
+
: { outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
16587
|
+
return { ruleId: rule.ruleId, ...resolved };
|
|
15547
16588
|
}), applicability: null },
|
|
15548
16589
|
"heading-order": { run: (function runInPage(ctx) {
|
|
15549
16590
|
const { helpers, rule } = ctx;
|
|
@@ -15731,7 +16772,11 @@ function runa11yCoreInPage(pageUrl, contextSelector, engineOptions, runOnly) {
|
|
|
15731
16772
|
}
|
|
15732
16773
|
|
|
15733
16774
|
// Minimal BCP47 primary subtag check
|
|
15734
|
-
|
|
16775
|
+
const isValidTag =
|
|
16776
|
+
helpers && typeof helpers.isValidLanguageTag === 'function'
|
|
16777
|
+
? helpers.isValidLanguageTag
|
|
16778
|
+
: (v) => /^[a-zA-Z]{2,3}(-[a-zA-Z0-9]{2,8})*$/.test(String(v || ''));
|
|
16779
|
+
if (!isValidTag(lang)) {
|
|
15735
16780
|
return {
|
|
15736
16781
|
ruleId: rule.ruleId,
|
|
15737
16782
|
outcome: 'fail',
|
|
@@ -16603,8 +17648,15 @@ function runa11yCoreInPage(pageUrl, contextSelector, engineOptions, runOnly) {
|
|
|
16603
17648
|
const getEligibilityInfo =
|
|
16604
17649
|
helpers && typeof helpers.getEligibilityInfo === 'function' ? helpers.getEligibilityInfo : null;
|
|
16605
17650
|
|
|
16606
|
-
|
|
16607
|
-
|
|
17651
|
+
// ACT 23a2a8 exempts programmatically hidden images, and that glossary term
|
|
17652
|
+
// has no focusability carve-out, so an aria-hidden image stays out of scope
|
|
17653
|
+
// even when tabbable; aria-hidden-focus reports that markup instead.
|
|
17654
|
+
const isEligibleHelper =
|
|
17655
|
+
helpers && typeof helpers.isIncludedInAccessibilityTree === 'function'
|
|
17656
|
+
? helpers.isIncludedInAccessibilityTree
|
|
17657
|
+
: helpers && typeof helpers.isAccTreeEligible === 'function'
|
|
17658
|
+
? helpers.isAccTreeEligible
|
|
17659
|
+
: null;
|
|
16608
17660
|
const getFocusableInfo =
|
|
16609
17661
|
helpers && typeof helpers.getFocusableInfo === 'function' ? helpers.getFocusableInfo : null;
|
|
16610
17662
|
const getAriaNameInfo =
|
|
@@ -16656,11 +17708,11 @@ function runa11yCoreInPage(pageUrl, contextSelector, engineOptions, runOnly) {
|
|
|
16656
17708
|
const el = imgs[i];
|
|
16657
17709
|
if (!el || !el.getAttribute) continue;
|
|
16658
17710
|
|
|
16659
|
-
// Eligibility: only imgs exposed to assistive tech
|
|
16660
|
-
if (
|
|
17711
|
+
// Eligibility: only imgs exposed to assistive tech.
|
|
17712
|
+
if (isEligibleHelper) {
|
|
16661
17713
|
let elig;
|
|
16662
17714
|
try {
|
|
16663
|
-
elig =
|
|
17715
|
+
elig = isEligibleHelper(el, ctx);
|
|
16664
17716
|
} catch {
|
|
16665
17717
|
elig = null;
|
|
16666
17718
|
}
|
|
@@ -16973,6 +18025,9 @@ function runa11yCoreInPage(pageUrl, contextSelector, engineOptions, runOnly) {
|
|
|
16973
18025
|
const isAccTreeEligible =
|
|
16974
18026
|
helpers && typeof helpers.isAccTreeEligible === 'function' ? helpers.isAccTreeEligible : null;
|
|
16975
18027
|
|
|
18028
|
+
const getAriaNameInfo =
|
|
18029
|
+
helpers && typeof helpers.getAriaNameInfo === 'function' ? helpers.getAriaNameInfo : null;
|
|
18030
|
+
|
|
16976
18031
|
const getFocusableInfo =
|
|
16977
18032
|
helpers && typeof helpers.getFocusableInfo === 'function' ? helpers.getFocusableInfo : null;
|
|
16978
18033
|
|
|
@@ -17009,6 +18064,26 @@ function runa11yCoreInPage(pageUrl, contextSelector, engineOptions, runOnly) {
|
|
|
17009
18064
|
return !focusable;
|
|
17010
18065
|
}
|
|
17011
18066
|
|
|
18067
|
+
// alt="" plus a name from aria-label/aria-labelledby/title is the judgement
|
|
18068
|
+
// call this rule reviews. alt="" with no other source leaves the control
|
|
18069
|
+
// unnamed, which input-image-alt-present fails outright.
|
|
18070
|
+
function hasNameFromOtherSource(el) {
|
|
18071
|
+
if (getAriaNameInfo) {
|
|
18072
|
+
try {
|
|
18073
|
+
const aria = getAriaNameInfo(el, ctx);
|
|
18074
|
+
if (aria && aria.present && String(aria.value || '').trim()) return true;
|
|
18075
|
+
} catch {
|
|
18076
|
+
// fall through to title
|
|
18077
|
+
}
|
|
18078
|
+
}
|
|
18079
|
+
try {
|
|
18080
|
+
const title = el.getAttribute('title');
|
|
18081
|
+
return title != null && String(title).trim() !== '';
|
|
18082
|
+
} catch {
|
|
18083
|
+
return false;
|
|
18084
|
+
}
|
|
18085
|
+
}
|
|
18086
|
+
|
|
17012
18087
|
const els = (() => {
|
|
17013
18088
|
try {
|
|
17014
18089
|
return Array.from(
|
|
@@ -17045,6 +18120,7 @@ function runa11yCoreInPage(pageUrl, contextSelector, engineOptions, runOnly) {
|
|
|
17045
18120
|
|
|
17046
18121
|
// Rule-specific applicability (only elements that already have a text alternative mechanism)
|
|
17047
18122
|
if (!(el.getAttribute('alt') != null && String(el.getAttribute('alt')).trim() === '')) continue;
|
|
18123
|
+
if (!hasNameFromOtherSource(el)) continue;
|
|
17048
18124
|
|
|
17049
18125
|
applicableCount += 1;
|
|
17050
18126
|
|
|
@@ -17099,8 +18175,14 @@ function runa11yCoreInPage(pageUrl, contextSelector, engineOptions, runOnly) {
|
|
|
17099
18175
|
const getEligibilityInfo =
|
|
17100
18176
|
helpers && typeof helpers.getEligibilityInfo === 'function' ? helpers.getEligibilityInfo : null;
|
|
17101
18177
|
|
|
17102
|
-
|
|
17103
|
-
|
|
18178
|
+
// ACT 59796f applies to image buttons included in the accessibility tree,
|
|
18179
|
+
// which excludes focusable content inside aria-hidden.
|
|
18180
|
+
const isEligibleHelper =
|
|
18181
|
+
helpers && typeof helpers.isIncludedInAccessibilityTree === 'function'
|
|
18182
|
+
? helpers.isIncludedInAccessibilityTree
|
|
18183
|
+
: helpers && typeof helpers.isAccTreeEligible === 'function'
|
|
18184
|
+
? helpers.isAccTreeEligible
|
|
18185
|
+
: null;
|
|
17104
18186
|
|
|
17105
18187
|
const getAriaNameInfo =
|
|
17106
18188
|
helpers && typeof helpers.getAriaNameInfo === 'function' ? helpers.getAriaNameInfo : null;
|
|
@@ -17126,63 +18208,109 @@ function runa11yCoreInPage(pageUrl, contextSelector, engineOptions, runOnly) {
|
|
|
17126
18208
|
for (const el of inputs) {
|
|
17127
18209
|
if (!el || !el.getAttribute) continue;
|
|
17128
18210
|
|
|
17129
|
-
|
|
17130
|
-
if (isAccTreeEligible) {
|
|
18211
|
+
if (isEligibleHelper) {
|
|
17131
18212
|
const elig = (() => {
|
|
17132
18213
|
try {
|
|
17133
|
-
return
|
|
18214
|
+
return isEligibleHelper(el, ctx);
|
|
17134
18215
|
} catch {
|
|
17135
|
-
return
|
|
18216
|
+
return true;
|
|
17136
18217
|
}
|
|
17137
18218
|
})();
|
|
17138
|
-
|
|
18219
|
+
const eligible = typeof elig === 'boolean' ? elig : !(elig && elig.eligible === false);
|
|
18220
|
+
if (!eligible) continue;
|
|
17139
18221
|
}
|
|
17140
18222
|
|
|
17141
18223
|
applicableCount += 1;
|
|
17142
18224
|
|
|
17143
|
-
|
|
17144
|
-
|
|
17145
|
-
|
|
17146
|
-
//
|
|
17147
|
-
|
|
17148
|
-
|
|
17149
|
-
|
|
17150
|
-
|
|
17151
|
-
|
|
17152
|
-
|
|
17153
|
-
|
|
17154
|
-
|
|
17155
|
-
|
|
18225
|
+
// The browser's own fallback name for an image button carries no
|
|
18226
|
+
// information, so an author-supplied name equal to it is treated as no
|
|
18227
|
+
// name at all (ACT 59796f). Only the English defaults are recognised:
|
|
18228
|
+
// "Submit Query" from HTML-AAM, "Submit" from Chrome.
|
|
18229
|
+
const effectiveName = (() => {
|
|
18230
|
+
let v = '';
|
|
18231
|
+
if (getAriaNameInfo) {
|
|
18232
|
+
try {
|
|
18233
|
+
const aria = getAriaNameInfo(el, ctx);
|
|
18234
|
+
if (aria && aria.present && aria.value) v = String(aria.value);
|
|
18235
|
+
} catch {
|
|
18236
|
+
v = '';
|
|
18237
|
+
}
|
|
17156
18238
|
}
|
|
17157
|
-
if (
|
|
17158
|
-
|
|
17159
|
-
|
|
17160
|
-
|
|
17161
|
-
|
|
17162
|
-
|
|
17163
|
-
|
|
17164
|
-
try {
|
|
17165
|
-
return el.getAttribute('title');
|
|
17166
|
-
} catch {
|
|
17167
|
-
return null;
|
|
18239
|
+
if (!v) {
|
|
18240
|
+
const alt = el.getAttribute('alt');
|
|
18241
|
+
if (alt != null && String(alt).trim()) v = String(alt);
|
|
18242
|
+
}
|
|
18243
|
+
if (!v) {
|
|
18244
|
+
const t = el.getAttribute('title');
|
|
18245
|
+
if (t != null && String(t).trim()) v = String(t);
|
|
17168
18246
|
}
|
|
18247
|
+
return v.trim().toLowerCase();
|
|
17169
18248
|
})();
|
|
17170
|
-
|
|
18249
|
+
|
|
18250
|
+
if (effectiveName === 'submit query' || effectiveName === 'submit') {
|
|
18251
|
+
const eligInfoDefault = getEligibilityInfo
|
|
18252
|
+
? getEligibilityInfo(el, ctx, { targetSet: 'acc' })
|
|
18253
|
+
: null;
|
|
18254
|
+
const defaultNameOccurrence = {
|
|
18255
|
+
summary:
|
|
18256
|
+
'Accessible name is the browser default for an image button, which conveys nothing.',
|
|
18257
|
+
hint: 'Replace it with text describing what the button does, for example "Search".',
|
|
18258
|
+
i18n: {
|
|
18259
|
+
summaryKey: 'inputImage_altPresent_summary_defaultName',
|
|
18260
|
+
hintKey: 'inputImage_altPresent_hint_defaultName',
|
|
18261
|
+
params: { element: 'input[type=image]' }
|
|
18262
|
+
},
|
|
18263
|
+
data: {
|
|
18264
|
+
visibilityFilter: eligInfoDefault || { targetSet: 'acc', accEligible: null, reasons: [] },
|
|
18265
|
+
details: { reasonCode: 'default_name' }
|
|
18266
|
+
}
|
|
18267
|
+
};
|
|
18268
|
+
occurrences.push(
|
|
18269
|
+
helpers && typeof helpers.reportOccurrence === 'function'
|
|
18270
|
+
? helpers.reportOccurrence(el, defaultNameOccurrence)
|
|
18271
|
+
: { selector: '', html: '', ...defaultNameOccurrence }
|
|
18272
|
+
);
|
|
18273
|
+
continue;
|
|
18274
|
+
}
|
|
18275
|
+
|
|
18276
|
+
// effectiveName already covers the naming sources HTML-AAM allows here,
|
|
18277
|
+
// in order: aria-label/aria-labelledby, then alt, then title.
|
|
18278
|
+
if (effectiveName) continue;
|
|
18279
|
+
|
|
18280
|
+
// An image button is a control, so an empty name fails whether alt is
|
|
18281
|
+
// absent or present-but-empty. alt="" marks a decorative image, and an
|
|
18282
|
+
// image button is never decorative.
|
|
18283
|
+
const emptyAlt = el.getAttribute('alt') !== null;
|
|
17171
18284
|
|
|
17172
18285
|
const eligInfo = getEligibilityInfo ? getEligibilityInfo(el, ctx, { targetSet: 'acc' }) : null;
|
|
17173
18286
|
|
|
17174
|
-
const baseOccurrence =
|
|
17175
|
-
|
|
17176
|
-
|
|
17177
|
-
|
|
17178
|
-
|
|
17179
|
-
|
|
17180
|
-
|
|
17181
|
-
|
|
17182
|
-
|
|
17183
|
-
|
|
17184
|
-
|
|
17185
|
-
|
|
18287
|
+
const baseOccurrence = emptyAlt
|
|
18288
|
+
? {
|
|
18289
|
+
summary: 'Empty alt="" on <input type="image"> leaves the control unnamed.',
|
|
18290
|
+
hint: 'Describe the action in alt, or name the control with aria-label or aria-labelledby.',
|
|
18291
|
+
i18n: {
|
|
18292
|
+
summaryKey: 'inputImage_altPresent_summary_emptyAlt',
|
|
18293
|
+
hintKey: 'inputImage_altPresent_hint_emptyAlt',
|
|
18294
|
+
params: { element: 'input[type=image]' }
|
|
18295
|
+
},
|
|
18296
|
+
data: {
|
|
18297
|
+
visibilityFilter: eligInfo || { targetSet: 'acc', accEligible: null, reasons: [] },
|
|
18298
|
+
details: { reasonCode: 'empty_alt' }
|
|
18299
|
+
}
|
|
18300
|
+
}
|
|
18301
|
+
: {
|
|
18302
|
+
summary: 'Missing alt attribute on <input type="image">.',
|
|
18303
|
+
hint: 'Add an alt attribute (use alt="" only when a separate accessible name is provided).',
|
|
18304
|
+
i18n: {
|
|
18305
|
+
summaryKey: 'inputImage_altPresent_summary_fail',
|
|
18306
|
+
hintKey: 'inputImage_altPresent_hint_fail',
|
|
18307
|
+
params: { element: 'input[type=image]' }
|
|
18308
|
+
},
|
|
18309
|
+
data: {
|
|
18310
|
+
visibilityFilter: eligInfo || { targetSet: 'acc', accEligible: null, reasons: [] },
|
|
18311
|
+
details: { reasonCode: 'missing_alt' }
|
|
18312
|
+
}
|
|
18313
|
+
};
|
|
17186
18314
|
|
|
17187
18315
|
if (helpers && typeof helpers.reportOccurrence === 'function') {
|
|
17188
18316
|
occurrences.push(helpers.reportOccurrence(el, baseOccurrence));
|
|
@@ -17357,6 +18485,56 @@ function runa11yCoreInPage(pageUrl, contextSelector, engineOptions, runOnly) {
|
|
|
17357
18485
|
return v.replace(/\s+/g, ' ').trim().toLowerCase();
|
|
17358
18486
|
}
|
|
17359
18487
|
|
|
18488
|
+
// WCAG 2.5.3's label-in-name comparison is over words, not characters: drop
|
|
18489
|
+
// parenthesised text, case-fold and NFKD-normalise, then reduce every
|
|
18490
|
+
// non-letter/digit to a space. `hyphensJoin` deletes hyphens instead of
|
|
18491
|
+
// splitting on them, which distinguishes a real mismatch from one that is
|
|
18492
|
+
// only a hyphenation difference.
|
|
18493
|
+
function tokenize(s, hyphensJoin) {
|
|
18494
|
+
let v = (s == null ? '' : String(s)).replace(/\([^)]*\)/g, ' ').toLowerCase();
|
|
18495
|
+
try {
|
|
18496
|
+
v = v.normalize('NFKD');
|
|
18497
|
+
} catch {
|
|
18498
|
+
// Realm without String#normalize: the word comparison below still holds.
|
|
18499
|
+
}
|
|
18500
|
+
if (hyphensJoin) v = v.replace(/[-‐-―−]/g, '');
|
|
18501
|
+
return v
|
|
18502
|
+
.replace(/[^\p{L}\p{N}]+/gu, ' ')
|
|
18503
|
+
.split(' ')
|
|
18504
|
+
.filter(Boolean);
|
|
18505
|
+
}
|
|
18506
|
+
|
|
18507
|
+
// The label's words must appear adjacent and in order inside the name, so a
|
|
18508
|
+
// scattered subsequence does not count. Words in `prefixable` may match a
|
|
18509
|
+
// longer name word they start.
|
|
18510
|
+
function containsWordRun(needle, hay, prefixable) {
|
|
18511
|
+
if (!needle.length) return true;
|
|
18512
|
+
for (let i = 0; i + needle.length <= hay.length; i++) {
|
|
18513
|
+
let ok = true;
|
|
18514
|
+
for (let j = 0; j < needle.length; j++) {
|
|
18515
|
+
const want = needle[j];
|
|
18516
|
+
const got = hay[i + j];
|
|
18517
|
+
if (got === want) continue;
|
|
18518
|
+
if (prefixable && prefixable.has(want) && got.indexOf(want) === 0) continue;
|
|
18519
|
+
ok = false;
|
|
18520
|
+
break;
|
|
18521
|
+
}
|
|
18522
|
+
if (ok) return true;
|
|
18523
|
+
}
|
|
18524
|
+
return false;
|
|
18525
|
+
}
|
|
18526
|
+
|
|
18527
|
+
// A trailing period marks a word the author may have shortened ("Ave." for
|
|
18528
|
+
// "Avenue"). Tokenizing removes the period, so collect these beforehand.
|
|
18529
|
+
function abbreviatedWords(s) {
|
|
18530
|
+
const out = new Set();
|
|
18531
|
+
for (const w of (s == null ? '' : String(s)).split(/\s+/)) {
|
|
18532
|
+
const m = /^([\p{L}\p{N}]+)\.$/u.exec(w);
|
|
18533
|
+
if (m) out.add(m[1].toLowerCase());
|
|
18534
|
+
}
|
|
18535
|
+
return out;
|
|
18536
|
+
}
|
|
18537
|
+
|
|
17360
18538
|
function getElementDescriptor(el) {
|
|
17361
18539
|
const tag = el && el.tagName ? String(el.tagName).toLowerCase() : 'element';
|
|
17362
18540
|
let role;
|
|
@@ -17602,7 +18780,23 @@ function runa11yCoreInPage(pageUrl, contextSelector, engineOptions, runOnly) {
|
|
|
17602
18780
|
const accName = acc && acc.value != null ? String(acc.value) : '';
|
|
17603
18781
|
const accNorm = norm(accName);
|
|
17604
18782
|
|
|
17605
|
-
const
|
|
18783
|
+
const labelTokens = tokenize(visibleLabel, false);
|
|
18784
|
+
const nameTokens = tokenize(accName, false);
|
|
18785
|
+
const contains = containsWordRun(labelTokens, nameTokens, null);
|
|
18786
|
+
|
|
18787
|
+
// An abbreviation, or a word hyphenated differently in the two places, is
|
|
18788
|
+
// not something markup settles: the author may have meant either. Report
|
|
18789
|
+
// without asserting a defect instead of failing or staying silent.
|
|
18790
|
+
let uncertainty = '';
|
|
18791
|
+
if (!contains) {
|
|
18792
|
+
if (containsWordRun(tokenize(visibleLabel, true), tokenize(accName, true), null)) {
|
|
18793
|
+
uncertainty = 'HYPHENATION_DIFFERS';
|
|
18794
|
+
} else {
|
|
18795
|
+
const abbreviated = abbreviatedWords(visibleLabel);
|
|
18796
|
+
if (abbreviated.size && containsWordRun(labelTokens, nameTokens, abbreviated))
|
|
18797
|
+
uncertainty = 'POSSIBLE_ABBREVIATION';
|
|
18798
|
+
}
|
|
18799
|
+
}
|
|
17606
18800
|
|
|
17607
18801
|
if (!contains) {
|
|
17608
18802
|
const selectorOut = helpers.buildSelector ? helpers.buildSelector(el) : 'html';
|
|
@@ -17613,11 +18807,16 @@ function runa11yCoreInPage(pageUrl, contextSelector, engineOptions, runOnly) {
|
|
|
17613
18807
|
occurrences.push({
|
|
17614
18808
|
selector: selectorOut,
|
|
17615
18809
|
html,
|
|
17616
|
-
|
|
17617
|
-
|
|
18810
|
+
...(uncertainty ? { outcome: 'cantTell' } : null),
|
|
18811
|
+
summary: uncertainty
|
|
18812
|
+
? 'Accessible name may not contain the visible label text.'
|
|
18813
|
+
: 'Accessible name does not contain the visible label text.',
|
|
18814
|
+
hint: uncertainty
|
|
18815
|
+
? 'Check by hand: the two differ only by an abbreviation or by hyphenation, which markup cannot settle.'
|
|
18816
|
+
: 'Ensure the accessible name includes the visible text label (e.g., update aria-label/aria-labelledby to include the visible wording).',
|
|
17618
18817
|
i18n: {
|
|
17619
|
-
summaryKey: 'labelInName_summary_fail',
|
|
17620
|
-
hintKey: 'labelInName_hint_fail',
|
|
18818
|
+
summaryKey: uncertainty ? 'labelInName_summary_cantTell' : 'labelInName_summary_fail',
|
|
18819
|
+
hintKey: uncertainty ? 'labelInName_hint_cantTell' : 'labelInName_hint_fail',
|
|
17621
18820
|
params: {
|
|
17622
18821
|
element: getElementDescriptor(el),
|
|
17623
18822
|
visibleLabel: clipForSummary(visibleLabel),
|
|
@@ -17627,10 +18826,11 @@ function runa11yCoreInPage(pageUrl, contextSelector, engineOptions, runOnly) {
|
|
|
17627
18826
|
},
|
|
17628
18827
|
data: {
|
|
17629
18828
|
details: {
|
|
17630
|
-
reasonCode: 'VISIBLE_LABEL_NOT_IN_ACCESSIBLE_NAME',
|
|
18829
|
+
reasonCode: uncertainty || 'VISIBLE_LABEL_NOT_IN_ACCESSIBLE_NAME',
|
|
17631
18830
|
visibleLabel,
|
|
17632
18831
|
accessibleName: accName,
|
|
17633
18832
|
normalized: { visibleLabel: visibleNorm, accessibleName: accNorm },
|
|
18833
|
+
tokenized: { visibleLabel: labelTokens, accessibleName: nameTokens },
|
|
17634
18834
|
labelSource: labelInfo && labelInfo.source ? labelInfo.source : 'none',
|
|
17635
18835
|
nameMechanism: acc && acc.mechanism ? acc.mechanism : 'none'
|
|
17636
18836
|
}
|
|
@@ -17641,13 +18841,15 @@ function runa11yCoreInPage(pageUrl, contextSelector, engineOptions, runOnly) {
|
|
|
17641
18841
|
|
|
17642
18842
|
if (applicableCount === 0)
|
|
17643
18843
|
return { ruleId: rule.ruleId, outcome: 'notApplicable', severity: 'minor', occurrences: [] };
|
|
17644
|
-
if (occurrences.length)
|
|
18844
|
+
if (occurrences.length) {
|
|
18845
|
+
const anyFail = occurrences.some((o) => o.outcome !== 'cantTell');
|
|
17645
18846
|
return {
|
|
17646
18847
|
ruleId: rule.ruleId,
|
|
17647
|
-
outcome: 'fail',
|
|
18848
|
+
outcome: anyFail ? 'fail' : 'cantTell',
|
|
17648
18849
|
severity: rule.defaultSeverity || 'minor',
|
|
17649
18850
|
occurrences
|
|
17650
18851
|
};
|
|
18852
|
+
}
|
|
17651
18853
|
return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
|
|
17652
18854
|
}), applicability: null },
|
|
17653
18855
|
"label-title-only": { run: (function runInPage(ctx) {
|
|
@@ -17810,16 +19012,11 @@ function runa11yCoreInPage(pageUrl, contextSelector, engineOptions, runOnly) {
|
|
|
17810
19012
|
return getImplicitLandmarkRole(el);
|
|
17811
19013
|
}
|
|
17812
19014
|
|
|
17813
|
-
//
|
|
17814
|
-
//
|
|
17815
|
-
//
|
|
17816
|
-
// of whether sectioning-ancestor nesting would currently suppress its
|
|
17817
|
-
// implicit role; an explicit role="banner" is always a candidate too.
|
|
19015
|
+
// A candidate must actually have the banner role. Per HTML-AAM a <header>
|
|
19016
|
+
// descended from article/aside/main/nav/section is not a banner at all, so
|
|
19017
|
+
// flagging it as a nested banner reports a landmark that does not exist.
|
|
17818
19018
|
function isBannerCandidate(el) {
|
|
17819
|
-
|
|
17820
|
-
const explicit = getExplicitRoleToken(el);
|
|
17821
|
-
if (explicit) return explicit === 'banner';
|
|
17822
|
-
return !!(el.tagName && el.tagName.toLowerCase() === 'header');
|
|
19019
|
+
return getLandmarkRole(el) === 'banner';
|
|
17823
19020
|
}
|
|
17824
19021
|
|
|
17825
19022
|
function hasLandmarkAncestor(el) {
|
|
@@ -17998,11 +19195,11 @@ function runa11yCoreInPage(pageUrl, contextSelector, engineOptions, runOnly) {
|
|
|
17998
19195
|
// a candidate purely by tag + absence of any role attribute, independent
|
|
17999
19196
|
// of whether sectioning-ancestor nesting would currently suppress its
|
|
18000
19197
|
// implicit role; an explicit role="contentinfo" is always a candidate too.
|
|
19198
|
+
// A candidate must actually have the contentinfo role — a <footer> inside
|
|
19199
|
+
// article/aside/main/nav/section is not one, so flagging it as nested
|
|
19200
|
+
// would report a landmark that does not exist.
|
|
18001
19201
|
function isContentinfoCandidate(el) {
|
|
18002
|
-
|
|
18003
|
-
const explicit = getExplicitRoleToken(el);
|
|
18004
|
-
if (explicit) return explicit === 'contentinfo';
|
|
18005
|
-
return !!(el.tagName && el.tagName.toLowerCase() === 'footer');
|
|
19202
|
+
return getLandmarkRole(el) === 'contentinfo';
|
|
18006
19203
|
}
|
|
18007
19204
|
|
|
18008
19205
|
function hasLandmarkAncestor(el) {
|
|
@@ -19151,7 +20348,14 @@ function runa11yCoreInPage(pageUrl, contextSelector, engineOptions, runOnly) {
|
|
|
19151
20348
|
|
|
19152
20349
|
for (const el of nodes) {
|
|
19153
20350
|
// isAccTreeEligible returns { eligible, reasons }, not a boolean.
|
|
19154
|
-
|
|
20351
|
+
// Naming rules apply only to elements included in the accessibility tree
|
|
20352
|
+
// (ACT c487ae), which excludes focusable aria-hidden content;
|
|
20353
|
+
// aria-hidden-focus (ACT 6cfa84) covers that markup instead.
|
|
20354
|
+
const eligResult = helpers.isIncludedInAccessibilityTree
|
|
20355
|
+
? helpers.isIncludedInAccessibilityTree(el, ctx)
|
|
20356
|
+
: helpers.isAccTreeEligible
|
|
20357
|
+
? helpers.isAccTreeEligible(el, ctx)
|
|
20358
|
+
: true;
|
|
19155
20359
|
const eligible =
|
|
19156
20360
|
typeof eligResult === 'boolean' ? eligResult : !!(eligResult && eligResult.eligible);
|
|
19157
20361
|
if (!eligible) continue;
|
|
@@ -19161,33 +20365,59 @@ function runa11yCoreInPage(pageUrl, contextSelector, engineOptions, runOnly) {
|
|
|
19161
20365
|
const nameInfo = helpers.getAccessibleNameInfo ? helpers.getAccessibleNameInfo(el, ctx) : null;
|
|
19162
20366
|
const programmaticName = nameInfo && typeof nameInfo.value === 'string' ? nameInfo.value : '';
|
|
19163
20367
|
|
|
19164
|
-
// A native <a href>/<area href> (or [role="link"]) whose role has been
|
|
19165
|
-
// overridden to one of these roles is no longer semantically a link —
|
|
19166
|
-
// per the WAI-ARIA Accessible Name and Description Computation spec
|
|
19167
|
-
// these roles are name-from-author-only, and their rendered content
|
|
19168
|
-
// represents a VALUE, not a NAME (see button-name-present.js's identical
|
|
19169
|
-
// fix for a real-world example — <button role="combobox">List</button> —
|
|
19170
|
-
// of the same class of bug on a different host tag).
|
|
19171
20368
|
const role = el.getAttribute ? el.getAttribute('role') : null;
|
|
19172
20369
|
const roleNorm = String(role || '')
|
|
19173
20370
|
.replace(/\s+/g, ' ')
|
|
19174
20371
|
.trim()
|
|
19175
20372
|
.toLowerCase();
|
|
19176
|
-
|
|
19177
|
-
|
|
19178
|
-
|
|
19179
|
-
|
|
19180
|
-
'
|
|
19181
|
-
'
|
|
19182
|
-
'
|
|
19183
|
-
'
|
|
20373
|
+
// ARIA 1.2 "Name From: author, contents". Every other known role is
|
|
20374
|
+
// name-from-author-only. An unknown role falls back to the implicit role.
|
|
20375
|
+
// <generated:aria-name-from-content>
|
|
20376
|
+
const NAME_FROM_CONTENT_ROLES = [
|
|
20377
|
+
'button',
|
|
20378
|
+
'cell',
|
|
20379
|
+
'checkbox',
|
|
20380
|
+
'columnheader',
|
|
20381
|
+
'doc-backlink',
|
|
20382
|
+
'doc-biblioref',
|
|
20383
|
+
'doc-glossref',
|
|
20384
|
+
'doc-noteref',
|
|
20385
|
+
'graphics-object',
|
|
20386
|
+
'gridcell',
|
|
20387
|
+
'heading',
|
|
20388
|
+
'link',
|
|
20389
|
+
'menuitem',
|
|
20390
|
+
'menuitemcheckbox',
|
|
20391
|
+
'menuitemradio',
|
|
20392
|
+
'option',
|
|
20393
|
+
'radio',
|
|
20394
|
+
'row',
|
|
20395
|
+
'rowgroup',
|
|
20396
|
+
'rowheader',
|
|
20397
|
+
'switch',
|
|
20398
|
+
'tab',
|
|
20399
|
+
'tooltip',
|
|
20400
|
+
'treeitem'
|
|
19184
20401
|
];
|
|
19185
|
-
|
|
20402
|
+
// </generated:aria-name-from-content>
|
|
20403
|
+
const isKnownRoleToken =
|
|
20404
|
+
helpers && helpers.aria && typeof helpers.aria.isKnownRole === 'function'
|
|
20405
|
+
? (() => {
|
|
20406
|
+
try {
|
|
20407
|
+
return !!helpers.aria.isKnownRole(roleNorm);
|
|
20408
|
+
} catch {
|
|
20409
|
+
return false;
|
|
20410
|
+
}
|
|
20411
|
+
})()
|
|
20412
|
+
: false;
|
|
20413
|
+
const isContentNameCandidate =
|
|
20414
|
+
!roleNorm || !isKnownRoleToken || NAME_FROM_CONTENT_ROLES.includes(roleNorm);
|
|
19186
20415
|
|
|
19187
20416
|
const contentName =
|
|
19188
20417
|
programmaticName.trim().length === 0 && isContentNameCandidate
|
|
19189
20418
|
? getConservativeSubtreeText(el)
|
|
19190
20419
|
: '';
|
|
20420
|
+
|
|
19191
20421
|
const finalName = (programmaticName.trim().length ? programmaticName : contentName).trim();
|
|
19192
20422
|
|
|
19193
20423
|
if (finalName.length === 0) {
|
|
@@ -19516,9 +20746,16 @@ function runa11yCoreInPage(pageUrl, contextSelector, engineOptions, runOnly) {
|
|
|
19516
20746
|
return '';
|
|
19517
20747
|
}
|
|
19518
20748
|
|
|
20749
|
+
// Naming rules apply only to elements included in the accessibility tree
|
|
20750
|
+
// (ACT c487ae and siblings), which excludes focusable aria-hidden content.
|
|
20751
|
+
// aria-hidden-focus (ACT 6cfa84) covers that markup instead.
|
|
19519
20752
|
function isEligibleAcc(helpers, el, ctx) {
|
|
19520
20753
|
const fn =
|
|
19521
|
-
helpers && typeof helpers.
|
|
20754
|
+
helpers && typeof helpers.isIncludedInAccessibilityTree === 'function'
|
|
20755
|
+
? helpers.isIncludedInAccessibilityTree
|
|
20756
|
+
: helpers && typeof helpers.isAccTreeEligible === 'function'
|
|
20757
|
+
? helpers.isAccTreeEligible
|
|
20758
|
+
: null;
|
|
19522
20759
|
if (!fn) return true;
|
|
19523
20760
|
try {
|
|
19524
20761
|
const r = fn(el, ctx);
|
|
@@ -20178,9 +21415,16 @@ function runa11yCoreInPage(pageUrl, contextSelector, engineOptions, runOnly) {
|
|
|
20178
21415
|
return '';
|
|
20179
21416
|
}
|
|
20180
21417
|
|
|
21418
|
+
// Naming rules apply only to elements included in the accessibility tree
|
|
21419
|
+
// (ACT c487ae and siblings), which excludes focusable aria-hidden content.
|
|
21420
|
+
// aria-hidden-focus (ACT 6cfa84) covers that markup instead.
|
|
20181
21421
|
function isEligibleAcc(helpers, el, ctx) {
|
|
20182
21422
|
const fn =
|
|
20183
|
-
helpers && typeof helpers.
|
|
21423
|
+
helpers && typeof helpers.isIncludedInAccessibilityTree === 'function'
|
|
21424
|
+
? helpers.isIncludedInAccessibilityTree
|
|
21425
|
+
: helpers && typeof helpers.isAccTreeEligible === 'function'
|
|
21426
|
+
? helpers.isAccTreeEligible
|
|
21427
|
+
: null;
|
|
20184
21428
|
if (!fn) return true;
|
|
20185
21429
|
try {
|
|
20186
21430
|
const r = fn(el, ctx);
|
|
@@ -20278,6 +21522,34 @@ function runa11yCoreInPage(pageUrl, contextSelector, engineOptions, runOnly) {
|
|
|
20278
21522
|
: [];
|
|
20279
21523
|
|
|
20280
21524
|
const occurrences = [];
|
|
21525
|
+
// HTML's shared declarative refresh steps. Returns the delay in seconds, or
|
|
21526
|
+
// null when the value is not a valid refresh directive, in which case the
|
|
21527
|
+
// browser never refreshes and there is nothing to report. Rejects a leading
|
|
21528
|
+
// sign ("+72001"), a non-numeric time ("foo"), and a separator other than
|
|
21529
|
+
// "," or ";" ("0:1").
|
|
21530
|
+
function parseRefreshDelay(input) {
|
|
21531
|
+
const s = String(input == null ? '' : input);
|
|
21532
|
+
const isSpace = (c) => c === ' ' || c === '\t' || c === '\n' || c === '\f' || c === '\r';
|
|
21533
|
+
let i = 0;
|
|
21534
|
+
while (i < s.length && isSpace(s[i])) i++;
|
|
21535
|
+
let digits = '';
|
|
21536
|
+
while (i < s.length && s[i] >= '0' && s[i] <= '9') digits += s[i++];
|
|
21537
|
+
if (!digits) return null;
|
|
21538
|
+
if (s[i] === '.') {
|
|
21539
|
+
i++;
|
|
21540
|
+
while (i < s.length && s[i] >= '0' && s[i] <= '9') i++;
|
|
21541
|
+
}
|
|
21542
|
+
if (i >= s.length) return parseInt(digits, 10);
|
|
21543
|
+
let sawSpace = false;
|
|
21544
|
+
while (i < s.length && isSpace(s[i])) {
|
|
21545
|
+
sawSpace = true;
|
|
21546
|
+
i++;
|
|
21547
|
+
}
|
|
21548
|
+
if (i >= s.length) return parseInt(digits, 10);
|
|
21549
|
+
if (s[i] === ';' || s[i] === ',' || sawSpace) return parseInt(digits, 10);
|
|
21550
|
+
return null;
|
|
21551
|
+
}
|
|
21552
|
+
|
|
20281
21553
|
let applicableCount = 0;
|
|
20282
21554
|
|
|
20283
21555
|
for (const el of nodes) {
|
|
@@ -20285,6 +21557,7 @@ function runa11yCoreInPage(pageUrl, contextSelector, engineOptions, runOnly) {
|
|
|
20285
21557
|
if (el.closest && el.closest('noscript')) continue; // never applies with scripting enabled
|
|
20286
21558
|
const raw = String(el.getAttribute('content') || '').trim();
|
|
20287
21559
|
if (!raw) continue;
|
|
21560
|
+
if (parseRefreshDelay(raw) === null) continue;
|
|
20288
21561
|
|
|
20289
21562
|
applicableCount += 1;
|
|
20290
21563
|
|
|
@@ -20336,16 +21609,42 @@ function runa11yCoreInPage(pageUrl, contextSelector, engineOptions, runOnly) {
|
|
|
20336
21609
|
const occurrences = [];
|
|
20337
21610
|
let applicableCount = 0;
|
|
20338
21611
|
|
|
21612
|
+
// HTML's shared declarative refresh steps. Returns the delay in seconds, or
|
|
21613
|
+
// null when the value is not a valid refresh directive, in which case the
|
|
21614
|
+
// browser never refreshes and there is nothing to report. Rejects a leading
|
|
21615
|
+
// sign ("+72001"), a non-numeric time ("foo"), and a separator other than
|
|
21616
|
+
// "," or ";" ("0:1").
|
|
21617
|
+
function parseRefreshDelay(input) {
|
|
21618
|
+
const s = String(input == null ? '' : input);
|
|
21619
|
+
const isSpace = (c) => c === ' ' || c === '\t' || c === '\n' || c === '\f' || c === '\r';
|
|
21620
|
+
let i = 0;
|
|
21621
|
+
while (i < s.length && isSpace(s[i])) i++;
|
|
21622
|
+
let digits = '';
|
|
21623
|
+
while (i < s.length && s[i] >= '0' && s[i] <= '9') digits += s[i++];
|
|
21624
|
+
if (!digits) return null;
|
|
21625
|
+
if (s[i] === '.') {
|
|
21626
|
+
i++;
|
|
21627
|
+
while (i < s.length && s[i] >= '0' && s[i] <= '9') i++;
|
|
21628
|
+
}
|
|
21629
|
+
if (i >= s.length) return parseInt(digits, 10);
|
|
21630
|
+
let sawSpace = false;
|
|
21631
|
+
while (i < s.length && isSpace(s[i])) {
|
|
21632
|
+
sawSpace = true;
|
|
21633
|
+
i++;
|
|
21634
|
+
}
|
|
21635
|
+
if (i >= s.length) return parseInt(digits, 10);
|
|
21636
|
+
if (s[i] === ';' || s[i] === ',' || sawSpace) return parseInt(digits, 10);
|
|
21637
|
+
return null;
|
|
21638
|
+
}
|
|
21639
|
+
|
|
20339
21640
|
for (const el of nodes) {
|
|
20340
21641
|
if (!el || !el.getAttribute) continue;
|
|
20341
21642
|
if (el.closest && el.closest('noscript')) continue; // never applies with scripting enabled — see meta-refresh-no-exceptions.js's header comment
|
|
20342
21643
|
const raw = String(el.getAttribute('content') || '').trim();
|
|
20343
21644
|
if (!raw) continue;
|
|
20344
21645
|
|
|
20345
|
-
const
|
|
20346
|
-
if (
|
|
20347
|
-
const delay = parseFloat(match[1]);
|
|
20348
|
-
if (Number.isNaN(delay)) continue;
|
|
21646
|
+
const delay = parseRefreshDelay(raw);
|
|
21647
|
+
if (delay === null) continue;
|
|
20349
21648
|
|
|
20350
21649
|
applicableCount += 1;
|
|
20351
21650
|
|
|
@@ -20500,20 +21799,40 @@ function runa11yCoreInPage(pageUrl, contextSelector, engineOptions, runOnly) {
|
|
|
20500
21799
|
const raw = String(el.getAttribute('content') || '').trim();
|
|
20501
21800
|
if (!raw) continue;
|
|
20502
21801
|
|
|
20503
|
-
applicableCount += 1;
|
|
20504
|
-
|
|
20505
21802
|
const parsed = parseContent(raw);
|
|
21803
|
+
|
|
21804
|
+
// ACT b4f0c3 applies only when content carries maximum-scale or
|
|
21805
|
+
// user-scalable; content that sets neither cannot restrict zoom.
|
|
21806
|
+
if (parsed['user-scalable'] === undefined && parsed['maximum-scale'] === undefined) continue;
|
|
21807
|
+
|
|
21808
|
+
applicableCount += 1;
|
|
20506
21809
|
const reasons = [];
|
|
20507
21810
|
|
|
21811
|
+
// CSS Device Adaptation translates an unparseable value to 0, so
|
|
21812
|
+
// user-scalable=invalid and maximum-scale=yes disable zoom just as
|
|
21813
|
+
// user-scalable=no does. A negative maximum-scale is out of range and
|
|
21814
|
+
// dropped instead, which is why it does not restrict anything.
|
|
20508
21815
|
const userScalable = parsed['user-scalable'];
|
|
20509
|
-
if (
|
|
20510
|
-
|
|
21816
|
+
if (
|
|
21817
|
+
userScalable !== undefined &&
|
|
21818
|
+
userScalable !== 'yes' &&
|
|
21819
|
+
userScalable !== 'device-width' &&
|
|
21820
|
+
userScalable !== 'device-height'
|
|
21821
|
+
) {
|
|
21822
|
+
const scale = parseFloat(userScalable);
|
|
21823
|
+
if (Number.isNaN(scale) || (scale > -1 && scale < 1)) {
|
|
21824
|
+
reasons.push('user-scalable=' + userScalable);
|
|
21825
|
+
}
|
|
20511
21826
|
}
|
|
20512
21827
|
|
|
20513
21828
|
const maxScaleRaw = parsed['maximum-scale'];
|
|
20514
|
-
if (
|
|
21829
|
+
if (
|
|
21830
|
+
maxScaleRaw !== undefined &&
|
|
21831
|
+
maxScaleRaw !== 'device-width' &&
|
|
21832
|
+
maxScaleRaw !== 'device-height'
|
|
21833
|
+
) {
|
|
20515
21834
|
const maxScale = parseFloat(maxScaleRaw);
|
|
20516
|
-
if (
|
|
21835
|
+
if (Number.isNaN(maxScale) || (maxScale >= 0 && maxScale < 2)) {
|
|
20517
21836
|
reasons.push('maximum-scale=' + maxScaleRaw);
|
|
20518
21837
|
}
|
|
20519
21838
|
}
|
|
@@ -20591,9 +21910,16 @@ function runa11yCoreInPage(pageUrl, contextSelector, engineOptions, runOnly) {
|
|
|
20591
21910
|
return '';
|
|
20592
21911
|
}
|
|
20593
21912
|
|
|
21913
|
+
// Naming rules apply only to elements included in the accessibility tree
|
|
21914
|
+
// (ACT c487ae and siblings), which excludes focusable aria-hidden content.
|
|
21915
|
+
// aria-hidden-focus (ACT 6cfa84) covers that markup instead.
|
|
20594
21916
|
function isEligibleAcc(helpers, el, ctx) {
|
|
20595
21917
|
const fn =
|
|
20596
|
-
helpers && typeof helpers.
|
|
21918
|
+
helpers && typeof helpers.isIncludedInAccessibilityTree === 'function'
|
|
21919
|
+
? helpers.isIncludedInAccessibilityTree
|
|
21920
|
+
: helpers && typeof helpers.isAccTreeEligible === 'function'
|
|
21921
|
+
? helpers.isAccTreeEligible
|
|
21922
|
+
: null;
|
|
20597
21923
|
if (!fn) return true;
|
|
20598
21924
|
try {
|
|
20599
21925
|
const r = fn(el, ctx);
|
|
@@ -20799,6 +22125,8 @@ function runa11yCoreInPage(pageUrl, contextSelector, engineOptions, runOnly) {
|
|
|
20799
22125
|
|
|
20800
22126
|
const isAccTreeEligible =
|
|
20801
22127
|
helpers && typeof helpers.isAccTreeEligible === 'function' ? helpers.isAccTreeEligible : null;
|
|
22128
|
+
const getFocusableInfo =
|
|
22129
|
+
helpers && typeof helpers.getFocusableInfo === 'function' ? helpers.getFocusableInfo : null;
|
|
20802
22130
|
|
|
20803
22131
|
function isEligible(node) {
|
|
20804
22132
|
if (!isAccTreeEligible) return true;
|
|
@@ -20810,6 +22138,122 @@ function runa11yCoreInPage(pageUrl, contextSelector, engineOptions, runOnly) {
|
|
|
20810
22138
|
}
|
|
20811
22139
|
}
|
|
20812
22140
|
|
|
22141
|
+
function matchesInteractive(node) {
|
|
22142
|
+
return !!(
|
|
22143
|
+
node &&
|
|
22144
|
+
node.nodeType === 1 &&
|
|
22145
|
+
typeof node.matches === 'function' &&
|
|
22146
|
+
node.matches(INTERACTIVE_SELECTOR)
|
|
22147
|
+
);
|
|
22148
|
+
}
|
|
22149
|
+
|
|
22150
|
+
// A composite widget owns children of a fixed role and drives their focus
|
|
22151
|
+
// itself (via roving tabindex or aria-activedescendant). The roving-tabindex
|
|
22152
|
+
// model gives the active child tabindex="0" and the rest tabindex="-1", so
|
|
22153
|
+
// the owned children ARE platform-focusable even though the container, not
|
|
22154
|
+
// the child, is the operable unit. Such a child is part of the composite,
|
|
22155
|
+
// not a control nested inside another control, so it must not be reported
|
|
22156
|
+
// regardless of its tabindex. The map is keyed by the child role and lists
|
|
22157
|
+
// the container roles that legitimately own it.
|
|
22158
|
+
const COMPOSITE_CHILD_CONTAINERS = {
|
|
22159
|
+
option: ['listbox', 'combobox'],
|
|
22160
|
+
tab: ['tablist'],
|
|
22161
|
+
treeitem: ['tree'],
|
|
22162
|
+
menuitem: ['menu', 'menubar'],
|
|
22163
|
+
menuitemcheckbox: ['menu', 'menubar'],
|
|
22164
|
+
menuitemradio: ['menu', 'menubar'],
|
|
22165
|
+
radio: ['radiogroup']
|
|
22166
|
+
};
|
|
22167
|
+
|
|
22168
|
+
function getExplicitRole(node) {
|
|
22169
|
+
if (!node || node.nodeType !== 1 || typeof node.getAttribute !== 'function') return '';
|
|
22170
|
+
const raw = node.getAttribute('role');
|
|
22171
|
+
if (!raw) return '';
|
|
22172
|
+
// role accepts a space-separated fallback list; the first token wins.
|
|
22173
|
+
const first = raw.trim().split(/\s+/)[0];
|
|
22174
|
+
return first ? first.toLowerCase() : '';
|
|
22175
|
+
}
|
|
22176
|
+
|
|
22177
|
+
function parentElementOf(node) {
|
|
22178
|
+
if (!node) return null;
|
|
22179
|
+
if (node.parentElement) return node.parentElement;
|
|
22180
|
+
const p = node.parentNode;
|
|
22181
|
+
return p && p.nodeType === 1 ? p : null;
|
|
22182
|
+
}
|
|
22183
|
+
|
|
22184
|
+
// True when `node` is an owned child of a composite widget: its role is a
|
|
22185
|
+
// composite-child role and a matching container role is present on one of
|
|
22186
|
+
// its ancestors. The roving tab stop (tabindex="0") such a container places
|
|
22187
|
+
// on its active child does not make that child an independently nested
|
|
22188
|
+
// control.
|
|
22189
|
+
function isManagedCompositeChild(node) {
|
|
22190
|
+
const role = getExplicitRole(node);
|
|
22191
|
+
const containers = COMPOSITE_CHILD_CONTAINERS[role];
|
|
22192
|
+
if (!containers) return false;
|
|
22193
|
+
let p = parentElementOf(node);
|
|
22194
|
+
while (p && p.nodeType === 1) {
|
|
22195
|
+
if (containers.indexOf(getExplicitRole(p)) !== -1) return true;
|
|
22196
|
+
p = parentElementOf(p);
|
|
22197
|
+
}
|
|
22198
|
+
return false;
|
|
22199
|
+
}
|
|
22200
|
+
|
|
22201
|
+
// Operable = a native-interactive / ARIA-widget element that is exposed to
|
|
22202
|
+
// the accessibility tree and platform-focusable. A widget-role element with
|
|
22203
|
+
// no independent focus (e.g. a role="option" with no tabindex, driven by
|
|
22204
|
+
// its container via roving tabindex or aria-activedescendant) is not
|
|
22205
|
+
// operable and does not nest an interactive control. When no focus model is
|
|
22206
|
+
// available, role membership alone qualifies. (Composite-owned children are
|
|
22207
|
+
// handled earlier as attribution boundaries in collectNestedOperable, so
|
|
22208
|
+
// they never reach here.)
|
|
22209
|
+
function isOperableInteractive(node) {
|
|
22210
|
+
if (!matchesInteractive(node)) return false;
|
|
22211
|
+
if (!isEligible(node)) return false;
|
|
22212
|
+
if (!getFocusableInfo) return true;
|
|
22213
|
+
try {
|
|
22214
|
+
const info = getFocusableInfo(node, ctx);
|
|
22215
|
+
return !!(info && info.focusable);
|
|
22216
|
+
} catch {
|
|
22217
|
+
return true;
|
|
22218
|
+
}
|
|
22219
|
+
}
|
|
22220
|
+
|
|
22221
|
+
// Shallowest operable interactive descendants of `root`. Traversal stops at
|
|
22222
|
+
// each counted node instead of descending into it, so a control nested
|
|
22223
|
+
// inside another operable control is attributed to its nearest operable
|
|
22224
|
+
// ancestor rather than to every enclosing container. Eligibility is applied
|
|
22225
|
+
// per node during the walk, so hidden or aria-hidden subtrees drop out.
|
|
22226
|
+
function collectNestedOperable(root) {
|
|
22227
|
+
const out = [];
|
|
22228
|
+
const top = root && root.children;
|
|
22229
|
+
if (!top || !top.length) return out;
|
|
22230
|
+
const stack = [];
|
|
22231
|
+
for (let i = top.length - 1; i >= 0; i--) stack.push(top[i]);
|
|
22232
|
+
while (stack.length) {
|
|
22233
|
+
const node = stack.pop();
|
|
22234
|
+
if (node && node.nodeType === 1) {
|
|
22235
|
+
// A composite-owned child (option in a listbox/combobox, tab in a
|
|
22236
|
+
// tablist, ...) is not a nested interactive control: its container
|
|
22237
|
+
// owns it and drives its focus (roving tabindex or
|
|
22238
|
+
// aria-activedescendant). Treat it as an attribution boundary — do not
|
|
22239
|
+
// count it, and do not descend past it. Any control genuinely nested
|
|
22240
|
+
// inside it is attributed to the child itself (examined as its own
|
|
22241
|
+
// container in the main loop), keeping the report at the nearest
|
|
22242
|
+
// interactive ancestor rather than bubbling up to the composite.
|
|
22243
|
+
if (isManagedCompositeChild(node)) continue;
|
|
22244
|
+
if (isOperableInteractive(node)) {
|
|
22245
|
+
out.push(node);
|
|
22246
|
+
continue; // do not descend into a counted control
|
|
22247
|
+
}
|
|
22248
|
+
}
|
|
22249
|
+
const kids = node && node.children;
|
|
22250
|
+
if (kids && kids.length) {
|
|
22251
|
+
for (let i = kids.length - 1; i >= 0; i--) stack.push(kids[i]);
|
|
22252
|
+
}
|
|
22253
|
+
}
|
|
22254
|
+
return out;
|
|
22255
|
+
}
|
|
22256
|
+
|
|
20813
22257
|
const nodes = helpers.queryAllSmart
|
|
20814
22258
|
? helpers.queryAllSmart(INTERACTIVE_SELECTOR)
|
|
20815
22259
|
: helpers.queryAll(INTERACTIVE_SELECTOR);
|
|
@@ -20818,30 +22262,16 @@ function runa11yCoreInPage(pageUrl, contextSelector, engineOptions, runOnly) {
|
|
|
20818
22262
|
let applicableCount = 0;
|
|
20819
22263
|
|
|
20820
22264
|
for (const el of nodes) {
|
|
20821
|
-
if (!el ||
|
|
22265
|
+
if (!el || el.nodeType !== 1) continue;
|
|
20822
22266
|
if (!isEligible(el)) continue;
|
|
20823
22267
|
|
|
20824
22268
|
applicableCount += 1;
|
|
20825
22269
|
|
|
20826
|
-
|
|
20827
|
-
// helpers.queryAllSmart), so it isn't subject to that helper's
|
|
20828
|
-
// default hidden-content policy at all -- not even hard CSS-based
|
|
20829
|
-
// hiding, let alone aria-hidden. A nested descendant that is never
|
|
20830
|
-
// actually rendered or exposed to AT (display:none, aria-hidden,
|
|
20831
|
-
// etc.) creates no real ambiguity for ANY user, since it isn't there
|
|
20832
|
-
// to be confused with the outer control.
|
|
20833
|
-
let nested;
|
|
20834
|
-
try {
|
|
20835
|
-
nested = Array.from(el.querySelectorAll(INTERACTIVE_SELECTOR)).filter(isEligible);
|
|
20836
|
-
} catch {
|
|
20837
|
-
nested = [];
|
|
20838
|
-
}
|
|
22270
|
+
const nested = collectNestedOperable(el);
|
|
20839
22271
|
if (!nested.length) continue;
|
|
20840
22272
|
|
|
20841
22273
|
const nestedTags = nested.map((n) => (n && n.tagName ? n.tagName.toLowerCase() : 'unknown'));
|
|
20842
|
-
const dedupedNestedTags = [
|
|
20843
|
-
...new Set(nested.map((n) => (n && n.tagName ? n.tagName.toLowerCase() : 'unknown')))
|
|
20844
|
-
];
|
|
22274
|
+
const dedupedNestedTags = [...new Set(nestedTags)];
|
|
20845
22275
|
|
|
20846
22276
|
const tag = el.tagName.toLowerCase();
|
|
20847
22277
|
const stableSelector = helpers.buildSelector ? helpers.buildSelector(el) : 'html';
|
|
@@ -20964,8 +22394,15 @@ function runa11yCoreInPage(pageUrl, contextSelector, engineOptions, runOnly) {
|
|
|
20964
22394
|
const getEligibilityInfo =
|
|
20965
22395
|
helpers && typeof helpers.getEligibilityInfo === 'function' ? helpers.getEligibilityInfo : null;
|
|
20966
22396
|
|
|
20967
|
-
|
|
20968
|
-
|
|
22397
|
+
// ACT 8fc3b6 applies only to objects included in the accessibility tree, so a
|
|
22398
|
+
// focusable object inside aria-hidden is out of scope; aria-hidden-focus
|
|
22399
|
+
// reports that markup instead.
|
|
22400
|
+
const isEligibleHelper =
|
|
22401
|
+
helpers && typeof helpers.isIncludedInAccessibilityTree === 'function'
|
|
22402
|
+
? helpers.isIncludedInAccessibilityTree
|
|
22403
|
+
: helpers && typeof helpers.isAccTreeEligible === 'function'
|
|
22404
|
+
? helpers.isAccTreeEligible
|
|
22405
|
+
: null;
|
|
20969
22406
|
|
|
20970
22407
|
const getFocusableInfo =
|
|
20971
22408
|
helpers && typeof helpers.getFocusableInfo === 'function' ? helpers.getFocusableInfo : null;
|
|
@@ -21041,14 +22478,15 @@ function runa11yCoreInPage(pageUrl, contextSelector, engineOptions, runOnly) {
|
|
|
21041
22478
|
for (const el of objects) {
|
|
21042
22479
|
if (!el || !el.getAttribute) continue;
|
|
21043
22480
|
|
|
21044
|
-
if (
|
|
22481
|
+
if (isEligibleHelper) {
|
|
21045
22482
|
const elig = (() => {
|
|
21046
22483
|
try {
|
|
21047
|
-
return
|
|
22484
|
+
return isEligibleHelper(el, ctx);
|
|
21048
22485
|
} catch {
|
|
21049
22486
|
return { eligible: true, reasons: [] };
|
|
21050
22487
|
}
|
|
21051
22488
|
})();
|
|
22489
|
+
if (elig === false) continue;
|
|
21052
22490
|
if (elig && elig.eligible === false) continue;
|
|
21053
22491
|
}
|
|
21054
22492
|
|
|
@@ -21356,9 +22794,16 @@ function runa11yCoreInPage(pageUrl, contextSelector, engineOptions, runOnly) {
|
|
|
21356
22794
|
return '';
|
|
21357
22795
|
}
|
|
21358
22796
|
|
|
22797
|
+
// Naming rules apply only to elements included in the accessibility tree
|
|
22798
|
+
// (ACT c487ae and siblings), which excludes focusable aria-hidden content.
|
|
22799
|
+
// aria-hidden-focus (ACT 6cfa84) covers that markup instead.
|
|
21359
22800
|
function isEligibleAcc(helpers, el, ctx) {
|
|
21360
22801
|
const fn =
|
|
21361
|
-
helpers && typeof helpers.
|
|
22802
|
+
helpers && typeof helpers.isIncludedInAccessibilityTree === 'function'
|
|
22803
|
+
? helpers.isIncludedInAccessibilityTree
|
|
22804
|
+
: helpers && typeof helpers.isAccTreeEligible === 'function'
|
|
22805
|
+
? helpers.isAccTreeEligible
|
|
22806
|
+
: null;
|
|
21362
22807
|
if (!fn) return true;
|
|
21363
22808
|
try {
|
|
21364
22809
|
const r = fn(el, ctx);
|
|
@@ -22147,9 +23592,16 @@ function runa11yCoreInPage(pageUrl, contextSelector, engineOptions, runOnly) {
|
|
|
22147
23592
|
return '';
|
|
22148
23593
|
}
|
|
22149
23594
|
|
|
23595
|
+
// Naming rules apply only to elements included in the accessibility tree
|
|
23596
|
+
// (ACT c487ae and siblings), which excludes focusable aria-hidden content.
|
|
23597
|
+
// aria-hidden-focus (ACT 6cfa84) covers that markup instead.
|
|
22150
23598
|
function isEligibleAcc(helpers, el, ctx) {
|
|
22151
23599
|
const fn =
|
|
22152
|
-
helpers && typeof helpers.
|
|
23600
|
+
helpers && typeof helpers.isIncludedInAccessibilityTree === 'function'
|
|
23601
|
+
? helpers.isIncludedInAccessibilityTree
|
|
23602
|
+
: helpers && typeof helpers.isAccTreeEligible === 'function'
|
|
23603
|
+
? helpers.isAccTreeEligible
|
|
23604
|
+
: null;
|
|
22153
23605
|
if (!fn) return true;
|
|
22154
23606
|
try {
|
|
22155
23607
|
const r = fn(el, ctx);
|
|
@@ -22980,9 +24432,16 @@ function runa11yCoreInPage(pageUrl, contextSelector, engineOptions, runOnly) {
|
|
|
22980
24432
|
return '';
|
|
22981
24433
|
}
|
|
22982
24434
|
|
|
24435
|
+
// Naming rules apply only to elements included in the accessibility tree
|
|
24436
|
+
// (ACT c487ae and siblings), which excludes focusable aria-hidden content.
|
|
24437
|
+
// aria-hidden-focus (ACT 6cfa84) covers that markup instead.
|
|
22983
24438
|
function isEligibleAcc(helpers, el, ctx) {
|
|
22984
24439
|
const fn =
|
|
22985
|
-
helpers && typeof helpers.
|
|
24440
|
+
helpers && typeof helpers.isIncludedInAccessibilityTree === 'function'
|
|
24441
|
+
? helpers.isIncludedInAccessibilityTree
|
|
24442
|
+
: helpers && typeof helpers.isAccTreeEligible === 'function'
|
|
24443
|
+
? helpers.isAccTreeEligible
|
|
24444
|
+
: null;
|
|
22986
24445
|
if (!fn) return true;
|
|
22987
24446
|
try {
|
|
22988
24447
|
const r = fn(el, ctx);
|
|
@@ -23463,9 +24922,16 @@ function runa11yCoreInPage(pageUrl, contextSelector, engineOptions, runOnly) {
|
|
|
23463
24922
|
return '';
|
|
23464
24923
|
}
|
|
23465
24924
|
|
|
24925
|
+
// Naming rules apply only to elements included in the accessibility tree
|
|
24926
|
+
// (ACT c487ae and siblings), which excludes focusable aria-hidden content.
|
|
24927
|
+
// aria-hidden-focus (ACT 6cfa84) covers that markup instead.
|
|
23466
24928
|
function isEligibleAcc(helpers, el, ctx) {
|
|
23467
24929
|
const fn =
|
|
23468
|
-
helpers && typeof helpers.
|
|
24930
|
+
helpers && typeof helpers.isIncludedInAccessibilityTree === 'function'
|
|
24931
|
+
? helpers.isIncludedInAccessibilityTree
|
|
24932
|
+
: helpers && typeof helpers.isAccTreeEligible === 'function'
|
|
24933
|
+
? helpers.isAccTreeEligible
|
|
24934
|
+
: null;
|
|
23469
24935
|
if (!fn) return true;
|
|
23470
24936
|
try {
|
|
23471
24937
|
const r = fn(el, ctx);
|
|
@@ -23479,7 +24945,9 @@ function runa11yCoreInPage(pageUrl, contextSelector, engineOptions, runOnly) {
|
|
|
23479
24945
|
const occurrences = [];
|
|
23480
24946
|
let applicableCount = 0;
|
|
23481
24947
|
|
|
23482
|
-
|
|
24948
|
+
// Native input[type=range] belongs to
|
|
24949
|
+
// form-control-programmatic-label-present.
|
|
24950
|
+
const selector = '[role="slider"]';
|
|
23483
24951
|
const nodes = helpers.queryAllSmart
|
|
23484
24952
|
? helpers.queryAllSmart(selector)
|
|
23485
24953
|
: helpers.queryAll(selector);
|
|
@@ -23683,9 +25151,16 @@ function runa11yCoreInPage(pageUrl, contextSelector, engineOptions, runOnly) {
|
|
|
23683
25151
|
return '';
|
|
23684
25152
|
}
|
|
23685
25153
|
|
|
25154
|
+
// Naming rules apply only to elements included in the accessibility tree
|
|
25155
|
+
// (ACT c487ae and siblings), which excludes focusable aria-hidden content.
|
|
25156
|
+
// aria-hidden-focus (ACT 6cfa84) covers that markup instead.
|
|
23686
25157
|
function isEligibleAcc(helpers, el, ctx) {
|
|
23687
25158
|
const fn =
|
|
23688
|
-
helpers && typeof helpers.
|
|
25159
|
+
helpers && typeof helpers.isIncludedInAccessibilityTree === 'function'
|
|
25160
|
+
? helpers.isIncludedInAccessibilityTree
|
|
25161
|
+
: helpers && typeof helpers.isAccTreeEligible === 'function'
|
|
25162
|
+
? helpers.isAccTreeEligible
|
|
25163
|
+
: null;
|
|
23689
25164
|
if (!fn) return true;
|
|
23690
25165
|
try {
|
|
23691
25166
|
const r = fn(el, ctx);
|
|
@@ -23880,9 +25355,16 @@ function runa11yCoreInPage(pageUrl, contextSelector, engineOptions, runOnly) {
|
|
|
23880
25355
|
return '';
|
|
23881
25356
|
}
|
|
23882
25357
|
|
|
25358
|
+
// Naming rules apply only to elements included in the accessibility tree
|
|
25359
|
+
// (ACT c487ae and siblings), which excludes focusable aria-hidden content.
|
|
25360
|
+
// aria-hidden-focus (ACT 6cfa84) covers that markup instead.
|
|
23883
25361
|
function isEligibleAcc(helpers, el, ctx) {
|
|
23884
25362
|
const fn =
|
|
23885
|
-
helpers && typeof helpers.
|
|
25363
|
+
helpers && typeof helpers.isIncludedInAccessibilityTree === 'function'
|
|
25364
|
+
? helpers.isIncludedInAccessibilityTree
|
|
25365
|
+
: helpers && typeof helpers.isAccTreeEligible === 'function'
|
|
25366
|
+
? helpers.isAccTreeEligible
|
|
25367
|
+
: null;
|
|
23886
25368
|
if (!fn) return true;
|
|
23887
25369
|
try {
|
|
23888
25370
|
const r = fn(el, ctx);
|
|
@@ -24712,9 +26194,16 @@ function runa11yCoreInPage(pageUrl, contextSelector, engineOptions, runOnly) {
|
|
|
24712
26194
|
return '';
|
|
24713
26195
|
}
|
|
24714
26196
|
|
|
26197
|
+
// Naming rules apply only to elements included in the accessibility tree
|
|
26198
|
+
// (ACT c487ae and siblings), which excludes focusable aria-hidden content.
|
|
26199
|
+
// aria-hidden-focus (ACT 6cfa84) covers that markup instead.
|
|
24715
26200
|
function isEligibleAcc(helpers, el, ctx) {
|
|
24716
26201
|
const fn =
|
|
24717
|
-
helpers && typeof helpers.
|
|
26202
|
+
helpers && typeof helpers.isIncludedInAccessibilityTree === 'function'
|
|
26203
|
+
? helpers.isIncludedInAccessibilityTree
|
|
26204
|
+
: helpers && typeof helpers.isAccTreeEligible === 'function'
|
|
26205
|
+
? helpers.isAccTreeEligible
|
|
26206
|
+
: null;
|
|
24718
26207
|
if (!fn) return true;
|
|
24719
26208
|
try {
|
|
24720
26209
|
const r = fn(el, ctx);
|
|
@@ -25106,16 +26595,73 @@ function runa11yCoreInPage(pageUrl, contextSelector, engineOptions, runOnly) {
|
|
|
25106
26595
|
const occurrences = [];
|
|
25107
26596
|
let applicableCount = 0;
|
|
25108
26597
|
|
|
26598
|
+
function explicitRole(el) {
|
|
26599
|
+
try {
|
|
26600
|
+
return String((el && el.getAttribute && el.getAttribute('role')) || '')
|
|
26601
|
+
.trim()
|
|
26602
|
+
.toLowerCase()
|
|
26603
|
+
.split(/\s+/)[0];
|
|
26604
|
+
} catch {
|
|
26605
|
+
return '';
|
|
26606
|
+
}
|
|
26607
|
+
}
|
|
26608
|
+
|
|
26609
|
+
function isIncludedInTree(el) {
|
|
26610
|
+
const fn =
|
|
26611
|
+
helpers && typeof helpers.isIncludedInAccessibilityTree === 'function'
|
|
26612
|
+
? helpers.isIncludedInAccessibilityTree
|
|
26613
|
+
: null;
|
|
26614
|
+
if (!fn) return true;
|
|
26615
|
+
try {
|
|
26616
|
+
const r = fn(el, ctx);
|
|
26617
|
+
return typeof r === 'boolean' ? r : !!(r && r.eligible);
|
|
26618
|
+
} catch {
|
|
26619
|
+
return true;
|
|
26620
|
+
}
|
|
26621
|
+
}
|
|
26622
|
+
|
|
26623
|
+
// role="presentation"/"none" removes the table role, and with it every
|
|
26624
|
+
// header cell the role would have implied. A table is not focusable, so
|
|
26625
|
+
// presentational role conflict resolution does not restore it.
|
|
26626
|
+
function hasTableSemantics(table) {
|
|
26627
|
+
const role = explicitRole(table);
|
|
26628
|
+
if (role === 'presentation' || role === 'none') return false;
|
|
26629
|
+
if (role && role !== 'table' && role !== 'grid' && role !== 'treegrid') return false;
|
|
26630
|
+
return isIncludedInTree(table);
|
|
26631
|
+
}
|
|
26632
|
+
|
|
26633
|
+
// A <th> only carries a rowheader/columnheader role while nothing overrides
|
|
26634
|
+
// it, and a hidden header describes nothing to a screen reader either way.
|
|
26635
|
+
function isHeaderCellInScope(th) {
|
|
26636
|
+
if (!th) return false;
|
|
26637
|
+
const role = explicitRole(th);
|
|
26638
|
+
if (role && role !== 'rowheader' && role !== 'columnheader') return false;
|
|
26639
|
+
if (helpers.isDomVisibleEligible) {
|
|
26640
|
+
try {
|
|
26641
|
+
if (!helpers.isDomVisibleEligible(th, ctx, { targetSet: 'dom' }).eligible) return false;
|
|
26642
|
+
} catch {
|
|
26643
|
+
// fall through to the tree check
|
|
26644
|
+
}
|
|
26645
|
+
}
|
|
26646
|
+
return isIncludedInTree(th);
|
|
26647
|
+
}
|
|
26648
|
+
|
|
25109
26649
|
for (const table of tables) {
|
|
25110
26650
|
if (!table || !table.querySelectorAll) continue;
|
|
25111
26651
|
|
|
26652
|
+
// A table stripped of its semantics has no header cells to describe
|
|
26653
|
+
// anything, so nothing in it is in scope.
|
|
26654
|
+
if (!hasTableSemantics(table)) continue;
|
|
26655
|
+
|
|
25112
26656
|
let ths;
|
|
25113
26657
|
try {
|
|
25114
26658
|
ths = table.querySelectorAll('th');
|
|
25115
26659
|
} catch {
|
|
25116
26660
|
ths = [];
|
|
25117
26661
|
}
|
|
25118
|
-
|
|
26662
|
+
|
|
26663
|
+
const headers = Array.from(ths).filter(isHeaderCellInScope);
|
|
26664
|
+
if (!headers.length) continue;
|
|
25119
26665
|
|
|
25120
26666
|
applicableCount += 1;
|
|
25121
26667
|
|
|
@@ -25127,7 +26673,7 @@ function runa11yCoreInPage(pageUrl, contextSelector, engineOptions, runOnly) {
|
|
|
25127
26673
|
}
|
|
25128
26674
|
if (hasDataCell) continue;
|
|
25129
26675
|
|
|
25130
|
-
for (const th of
|
|
26676
|
+
for (const th of headers) {
|
|
25131
26677
|
if (!th) continue;
|
|
25132
26678
|
const stableSelector = helpers.buildSelector ? helpers.buildSelector(th) : 'html';
|
|
25133
26679
|
const html = helpers.getOuterHtmlSnippet
|
|
@@ -25933,9 +27479,16 @@ function runa11yCoreInPage(pageUrl, contextSelector, engineOptions, runOnly) {
|
|
|
25933
27479
|
return '';
|
|
25934
27480
|
}
|
|
25935
27481
|
|
|
27482
|
+
// Naming rules apply only to elements included in the accessibility tree
|
|
27483
|
+
// (ACT c487ae and siblings), which excludes focusable aria-hidden content.
|
|
27484
|
+
// aria-hidden-focus (ACT 6cfa84) covers that markup instead.
|
|
25936
27485
|
function isEligibleAcc(helpers, el, ctx) {
|
|
25937
27486
|
const fn =
|
|
25938
|
-
helpers && typeof helpers.
|
|
27487
|
+
helpers && typeof helpers.isIncludedInAccessibilityTree === 'function'
|
|
27488
|
+
? helpers.isIncludedInAccessibilityTree
|
|
27489
|
+
: helpers && typeof helpers.isAccTreeEligible === 'function'
|
|
27490
|
+
? helpers.isAccTreeEligible
|
|
27491
|
+
: null;
|
|
25939
27492
|
if (!fn) return true;
|
|
25940
27493
|
try {
|
|
25941
27494
|
const r = fn(el, ctx);
|
|
@@ -26130,9 +27683,16 @@ function runa11yCoreInPage(pageUrl, contextSelector, engineOptions, runOnly) {
|
|
|
26130
27683
|
return '';
|
|
26131
27684
|
}
|
|
26132
27685
|
|
|
27686
|
+
// Naming rules apply only to elements included in the accessibility tree
|
|
27687
|
+
// (ACT c487ae and siblings), which excludes focusable aria-hidden content.
|
|
27688
|
+
// aria-hidden-focus (ACT 6cfa84) covers that markup instead.
|
|
26133
27689
|
function isEligibleAcc(helpers, el, ctx) {
|
|
26134
27690
|
const fn =
|
|
26135
|
-
helpers && typeof helpers.
|
|
27691
|
+
helpers && typeof helpers.isIncludedInAccessibilityTree === 'function'
|
|
27692
|
+
? helpers.isIncludedInAccessibilityTree
|
|
27693
|
+
: helpers && typeof helpers.isAccTreeEligible === 'function'
|
|
27694
|
+
? helpers.isAccTreeEligible
|
|
27695
|
+
: null;
|
|
26136
27696
|
if (!fn) return true;
|
|
26137
27697
|
try {
|
|
26138
27698
|
const r = fn(el, ctx);
|
|
@@ -26274,9 +27834,16 @@ function runa11yCoreInPage(pageUrl, contextSelector, engineOptions, runOnly) {
|
|
|
26274
27834
|
return '';
|
|
26275
27835
|
}
|
|
26276
27836
|
|
|
27837
|
+
// Naming rules apply only to elements included in the accessibility tree
|
|
27838
|
+
// (ACT c487ae and siblings), which excludes focusable aria-hidden content.
|
|
27839
|
+
// aria-hidden-focus (ACT 6cfa84) covers that markup instead.
|
|
26277
27840
|
function isEligibleAcc(helpers, el, ctx) {
|
|
26278
27841
|
const fn =
|
|
26279
|
-
helpers && typeof helpers.
|
|
27842
|
+
helpers && typeof helpers.isIncludedInAccessibilityTree === 'function'
|
|
27843
|
+
? helpers.isIncludedInAccessibilityTree
|
|
27844
|
+
: helpers && typeof helpers.isAccTreeEligible === 'function'
|
|
27845
|
+
? helpers.isAccTreeEligible
|
|
27846
|
+
: null;
|
|
26280
27847
|
if (!fn) return true;
|
|
26281
27848
|
try {
|
|
26282
27849
|
const r = fn(el, ctx);
|
|
@@ -26365,7 +27932,13 @@ function runa11yCoreInPage(pageUrl, contextSelector, engineOptions, runOnly) {
|
|
|
26365
27932
|
"valid-lang": { run: (function runInPage(ctx) {
|
|
26366
27933
|
const { helpers, rule } = ctx;
|
|
26367
27934
|
|
|
27935
|
+
// Shape alone accepts unregistered tags such as "eng" and "em-US", so the
|
|
27936
|
+
// primary subtag is checked against the IANA registry via the shared helper.
|
|
26368
27937
|
const BCP47_RE = /^[a-zA-Z]{2,3}(-[a-zA-Z0-9]{2,8})*$/;
|
|
27938
|
+
const isValidTag =
|
|
27939
|
+
typeof helpers.isValidLanguageTag === 'function'
|
|
27940
|
+
? helpers.isValidLanguageTag
|
|
27941
|
+
: (v) => BCP47_RE.test(String(v || ''));
|
|
26369
27942
|
|
|
26370
27943
|
const nodes = helpers.queryAllSmart
|
|
26371
27944
|
? helpers.queryAllSmart('[lang]')
|
|
@@ -26378,12 +27951,19 @@ function runa11yCoreInPage(pageUrl, contextSelector, engineOptions, runOnly) {
|
|
|
26378
27951
|
if (!el || !el.getAttribute) continue;
|
|
26379
27952
|
if (el.tagName && el.tagName.toLowerCase() === 'html') continue;
|
|
26380
27953
|
|
|
26381
|
-
const
|
|
26382
|
-
if (
|
|
27954
|
+
const rawAttr = el.getAttribute('lang');
|
|
27955
|
+
if (rawAttr === null || rawAttr === '') continue; // ACT de46e4: empty is out of scope
|
|
27956
|
+
|
|
27957
|
+
// The rule applies only where text actually inherits the language, so an
|
|
27958
|
+
// element with no text, or whose text reaches no one, has nothing to
|
|
27959
|
+
// declare a language for.
|
|
27960
|
+
if (!String(el.textContent || '').trim()) continue;
|
|
26383
27961
|
|
|
26384
27962
|
applicableCount += 1;
|
|
26385
27963
|
|
|
26386
|
-
|
|
27964
|
+
// A whitespace-only value is in scope and has no primary language tag.
|
|
27965
|
+
const raw = String(rawAttr).trim();
|
|
27966
|
+
if (isValidTag(raw)) continue;
|
|
26387
27967
|
|
|
26388
27968
|
const tag = el.tagName.toLowerCase();
|
|
26389
27969
|
const stableSelector = helpers.buildSelector ? helpers.buildSelector(el) : 'html';
|
|
@@ -26685,6 +28265,10 @@ const I18N = {
|
|
|
26685
28265
|
"inputImage_altPresent_description": "Prüft, ob <input type=\"image\">-Elemente ein alt-Attribut bereitstellen, um einen Mechanismus für eine Textalternative zu unterstützen.",
|
|
26686
28266
|
"inputImage_altPresent_summary_fail": "Fehlendes alt-Attribut auf <input type=\"image\">.",
|
|
26687
28267
|
"inputImage_altPresent_hint_fail": "Fügen Sie ein alt-Attribut hinzu (verwenden Sie alt=\"\" nur, wenn ein separater zugänglicher Name bereitgestellt wird).",
|
|
28268
|
+
"inputImage_altPresent_summary_defaultName": "Der zugaengliche Name ist der Browser-Standard fuer eine Bildschaltflaeche und sagt nichts aus.",
|
|
28269
|
+
"inputImage_altPresent_hint_defaultName": "Ersetzen Sie ihn durch Text, der die Aktion beschreibt, zum Beispiel \"Suchen\".",
|
|
28270
|
+
"inputImage_altPresent_summary_emptyAlt": "Ein leeres alt=\"\" auf <input type=\"image\"> laesst das Steuerelement ohne Namen.",
|
|
28271
|
+
"inputImage_altPresent_hint_emptyAlt": "Beschreiben Sie die Aktion in alt, oder benennen Sie das Steuerelement mit aria-label oder aria-labelledby.",
|
|
26688
28272
|
"ariaHidden_programmaticFocus_review_title": "Programmatischen Fokus bei aria-hidden überprüfen",
|
|
26689
28273
|
"ariaHidden_programmaticFocus_review_description": "Markiert Elemente, die aria-hidden sind, aber aufgrund von programmatischem Fokus (z. B. tabindex < 0) als geeignet gelten. Überprüfen Sie die beabsichtigte Fokusverwaltung und die Sichtbarkeit für assistive Technologien.",
|
|
26690
28274
|
"ariaHidden_programmaticFocus_review_summary": "Überprüfung: Ein aria-hidden-Element ist programmatisch fokussierbar.",
|
|
@@ -26966,16 +28550,21 @@ const I18N = {
|
|
|
26966
28550
|
"labelInName_description": "Prüft, ob bei einem Formularelement mit sichtbarer Textbeschriftung der zugängliche Name diesen sichtbaren Beschriftungstext enthält (WCAG 2.5.3).",
|
|
26967
28551
|
"labelInName_summary_fail": "{{element}}: Die sichtbare Beschriftung „{{visibleLabel}}“ (aus {{labelSource}}) ist nicht im zugänglichen Namen enthalten (aus {{nameMechanism}}).",
|
|
26968
28552
|
"labelInName_hint_fail": "Aktualisieren Sie aria-label/aria-labelledby (oder den sichtbaren Beschriftungstext), damit der zugängliche Name den Wortlaut der sichtbaren Beschriftung enthält.",
|
|
28553
|
+
"labelInName_summary_cantTell": "{{element}}: Die sichtbare Beschriftung „{{visibleLabel}}“ (aus {{labelSource}}) unterscheidet sich vom zugänglichen Namen (aus {{nameMechanism}}) nur durch eine Abkürzung oder eine Bindestrichschreibung.",
|
|
28554
|
+
"labelInName_hint_cantTell": "Prüfen Sie manuell, ob beide Formulierungen übereinstimmen: Aus dem Markup lässt sich eine beabsichtigte Abkürzung nicht von einer Abweichung unterscheiden.",
|
|
26969
28555
|
"ariaRolesValid_title": "Das role-Attribut muss eine gültige, nicht abstrakte ARIA-Rolle sein",
|
|
26970
28556
|
"ariaRolesValid_description": "Prüft, ob ein explizites role=\"\"-Attribut zu einer echten, nicht abstrakten WAI-ARIA-Rolle aufgelöst wird.",
|
|
26971
28557
|
"ariaRolesValid_summary_invalid": "role=\"{{role}}\" ist keine erkannte ARIA-Rolle.",
|
|
26972
28558
|
"ariaRolesValid_hint_invalid": "Verwenden Sie einen gültigen ARIA-Rollenwert, oder entfernen Sie das role-Attribut, wenn keine Rolle zutrifft.",
|
|
26973
28559
|
"ariaRolesValid_summary_abstract": "role=\"{{role}}\" ist eine abstrakte ARIA-Rolle, die nicht direkt verwendet werden darf.",
|
|
26974
28560
|
"ariaRolesValid_hint_abstract": "Ersetzen Sie diese abstrakte Rolle durch eine konkrete Rolle, die zu dem Widget/der Struktur passt.",
|
|
26975
|
-
"ariaDeprecatedRole_title": "Das role-Attribut
|
|
26976
|
-
"ariaDeprecatedRole_description": "Prüft, ob ein explizites role=\"\"-Attribut keine durch die WAI-ARIA-Spezifikation als veraltet markierte Rolle verwendet, und keine Rolle, die
|
|
28561
|
+
"ariaDeprecatedRole_title": "Das role-Attribut sollte keine veraltete oder für Autoren nicht empfohlene ARIA-Rolle verwenden",
|
|
28562
|
+
"ariaDeprecatedRole_description": "Prüft, ob ein explizites role=\"\"-Attribut keine durch die WAI-ARIA-Spezifikation als veraltet markierte Rolle verwendet, und keine Rolle, die für die interne Verwendung durch den User-Agent reserviert ist (z. B. role=\"generic\").",
|
|
26977
28563
|
"ariaDeprecatedRole_summary_fail": "Dieses Element verwendet role=\"{{role}}\", was Autoren nicht explizit deklarieren dürfen.",
|
|
26978
28564
|
"ariaDeprecatedRole_hint_fail": "{{guidance}}",
|
|
28565
|
+
"ariaDeprecatedRole_summary_cantTell": "Dieses Element verwendet role=\"{{role}}\", die in WAI-ARIA veraltet ist (weiterhin gültig, aber nicht empfohlen).",
|
|
28566
|
+
"ariaDeprecatedRole_summary_cantTell_discouraged": "Dieses Element verwendet role=\"{{role}}\", die User-Agents vorbehalten ist (weiterhin gültig, aber nicht empfohlen).",
|
|
28567
|
+
"ariaDeprecatedRole_hint_cantTell": "{{guidance}}",
|
|
26979
28568
|
"ariaValidAttr_title": "aria-*-Attribute müssen echte, definierte ARIA-Attribute sein",
|
|
26980
28569
|
"ariaValidAttr_description": "Prüft, ob jeder im DOM vorhandene aria-*-Attributname ein echtes, durch die WAI-ARIA-Spezifikation definiertes Attribut ist.",
|
|
26981
28570
|
"ariaValidAttr_summary_fail": "{{attr}} ist kein erkanntes ARIA-Attribut.",
|
|
@@ -26988,6 +28577,8 @@ const I18N = {
|
|
|
26988
28577
|
"ariaAllowedAttr_description": "Prüft, ob jedes erkannte aria-*-Attribut auf einem Element mit expliziter Rolle entweder global unterstützt wird oder von dieser Rolle unterstützt wird.",
|
|
26989
28578
|
"ariaAllowedAttr_summary_fail": "{{attr}} ist bei role=\"{{role}}\" nicht zulässig.",
|
|
26990
28579
|
"ariaAllowedAttr_hint_fail": "Entfernen Sie dieses Attribut, oder verwenden Sie eine Rolle, die es unterstützt.",
|
|
28580
|
+
"ariaAllowedAttr_summary_cantTell": "{{attr}} ist bei role=\"{{role}}\" veraltet (weiterhin zulässig, aber nicht empfohlen).",
|
|
28581
|
+
"ariaAllowedAttr_hint_cantTell": "Dieses Attribut wurde in ARIA 1.2 aus dem globalen Satz entfernt; entfernen Sie es oder verwenden Sie eine Rolle, die es unterstützt, da eine künftige ARIA-Version es möglicherweise nicht mehr zulässt.",
|
|
26991
28582
|
"ariaProhibitedAttr_title": "ARIA-Benennungsattribute dürfen nicht bei Rollen verwendet werden, die sie untersagen",
|
|
26992
28583
|
"ariaProhibitedAttr_description": "Prüft, ob aria-label/aria-labelledby nicht bei WAI-ARIA-Rollen vorhanden sind, deren Spezifikation die ARIA-Benennung ausdrücklich untersagt (z. B. generic, emphasis, strong, paragraph).",
|
|
26993
28584
|
"ariaProhibitedAttr_summary_fail": "{{attr}} ist bei role=\"{{role}}\" untersagt.",
|
|
@@ -27102,14 +28693,16 @@ const I18N = {
|
|
|
27102
28693
|
"formControlSingleLabel_description": "Prüft, ob ein Formularelement mit höchstens einem <label> verknüpft ist (durch Umschließen oder durch label[for]).",
|
|
27103
28694
|
"formControlSingleLabel_summary_fail": "Dieses <{{element}}> ist mit {{labelCount}} Beschriftungen verknüpft.",
|
|
27104
28695
|
"formControlSingleLabel_hint_fail": "Behalten Sie nur ein <label> pro Formularelement (entweder umschließend oder über for/id referenziert).",
|
|
28696
|
+
"formControlSingleLabel_summary_cantTell": "Dieses <{{element}}> hat ein beschriftendes <label> und zusätzlich eine leere <label>-Verknüpfung; prüfen Sie, wie es angesagt wird.",
|
|
28697
|
+
"formControlSingleLabel_hint_cantTell": "Entfernen Sie das überflüssige leere <label>, sodass genau ein <label> mit dem Element verknüpft ist.",
|
|
27105
28698
|
"nestedInteractiveControlsAbsent_title": "Interaktive Formularelemente dürfen nicht verschachtelt sein",
|
|
27106
28699
|
"nestedInteractiveControlsAbsent_description": "Prüft, ob ein interaktives Element (Link, Schaltfläche, Formularelement oder ARIA-Widget-Rolle) kein weiteres interaktives Element enthält.",
|
|
27107
28700
|
"nestedInteractiveControlsAbsent_summary_fail": "Dieses <{{element}}> enthält ein oder mehrere verschachtelte interaktive Elemente: {{nestedElements}}.",
|
|
27108
28701
|
"nestedInteractiveControlsAbsent_hint_fail": "Verschieben Sie die verschachtelten interaktiven Elemente außerhalb dieses Elements; verschachtelte interaktive Elemente sind über assistive Technologien nicht zuverlässig bedienbar.",
|
|
27109
28702
|
"bypassBlocksPresent_title": "Die Seite muss eine Möglichkeit bieten, wiederkehrende Blöcke zu überspringen",
|
|
27110
28703
|
"bypassBlocksPresent_description": "Prüft, ob die Seite mindestens einen anerkannten Mechanismus nach WCAG 2.4.1 zum Überspringen wiederkehrender Blöcke hat: eine main-Landmarke, einen funktionierenden Anker-Link auf derselben Seite oder eine Überschrift.",
|
|
27111
|
-
"
|
|
27112
|
-
"
|
|
28704
|
+
"bypassBlocksPresent_summary_cantTell": "Auf dieser Seite wurde keine anerkannte Möglichkeit erkannt, wiederkehrende Inhaltsblöcke zu überspringen – prüfen Sie, ob ein Überspring-Mechanismus vorhanden ist.",
|
|
28705
|
+
"bypassBlocksPresent_hint_cantTell": "Bestätigen Sie, dass die Seite einen Überspring-Mechanismus bietet: eine main-Landmarke (<main> oder role=\"main\"), einen funktionierenden „Zum Inhalt springen“-Link oder Überschriften-Elemente, die assistive Technologien nutzen können, um wiederkehrende Inhalte zu überspringen. (Ein Mechanismus kann vorübergehend verborgen sein – z. B. während ein modaler Dialog die Seite inert macht – oder seitenweit bereitgestellt werden; dies erfordert eine menschliche Bestätigung.)",
|
|
27113
28706
|
"landmarkBannerIsTopLevel_title": "Die banner-Landmarke muss auf oberster Ebene liegen",
|
|
27114
28707
|
"landmarkBannerIsTopLevel_description": "Prüft, ob die banner-Landmarke (role=\"banner\" oder ein nicht verschachtelter <header>) nicht innerhalb einer anderen Landmarke verschachtelt ist.",
|
|
27115
28708
|
"landmarkBannerIsTopLevel_summary_cantTell": "Diese banner-Landmarke ist innerhalb einer anderen Landmarke verschachtelt.",
|
|
@@ -27211,8 +28804,8 @@ const I18N = {
|
|
|
27211
28804
|
"htmlXmlLangMismatch_description": "Prüft, ob die Attribute lang und xml:lang des <html>-Elements dieselbe Hauptsprache deklarieren, wenn beide vorhanden sind.",
|
|
27212
28805
|
"htmlXmlLangMismatch_summary_fail": "Die Attribute lang („{{lang}}“) und xml:lang („{{xmlLang}}“) deklarieren unterschiedliche Sprachen.",
|
|
27213
28806
|
"htmlXmlLangMismatch_hint_fail": "Lassen Sie lang und xml:lang dieselbe Hauptsprache deklarieren, oder entfernen Sie das veraltete Attribut xml:lang.",
|
|
27214
|
-
"avoidInlineSpacing_title": "Inline-Stile dürfen den Textabstand nicht
|
|
27215
|
-
"avoidInlineSpacing_description": "Prüft,
|
|
28807
|
+
"avoidInlineSpacing_title": "Inline-Stile dürfen den Textabstand nicht unter den WCAG-Wert erzwingen",
|
|
28808
|
+
"avoidInlineSpacing_description": "Prüft, dass ein per !important erzwungener Wert für line-height, letter-spacing oder word-spacing WCAG 1.4.12 bereits erfüllt, sodass dem Nutzer nichts zu überschreiben bleibt.",
|
|
27216
28809
|
"avoidInlineSpacing_summary_fail": "Der Inline-Stil dieses Elements erzwingt {{properties}} mit !important und blockiert damit Überschreibungen des Textabstands durch den Nutzer.",
|
|
27217
28810
|
"avoidInlineSpacing_hint_fail": "Entfernen Sie !important von line-height/letter-spacing/word-spacing in Inline-Stilen, damit Nutzer den Textabstand überschreiben können.",
|
|
27218
28811
|
"metaRefreshNoExceptions_title": "Die Seite darf überhaupt keinen Meta-Refresh verwenden (AAA)",
|
|
@@ -27301,6 +28894,10 @@ const I18N = {
|
|
|
27301
28894
|
"inputImage_altPresent_description": "Checks that <input type=\"image\"> elements provide an alt attribute to support a text alternative mechanism.",
|
|
27302
28895
|
"inputImage_altPresent_summary_fail": "Missing alt attribute on <input type=\"image\">.",
|
|
27303
28896
|
"inputImage_altPresent_hint_fail": "Add an alt attribute (use alt=\"\" only when a separate accessible name is provided).",
|
|
28897
|
+
"inputImage_altPresent_summary_defaultName": "Accessible name is the browser default for an image button, which conveys nothing.",
|
|
28898
|
+
"inputImage_altPresent_hint_defaultName": "Replace it with text describing what the button does, for example \"Search\".",
|
|
28899
|
+
"inputImage_altPresent_summary_emptyAlt": "Empty alt=\"\" on <input type=\"image\"> leaves the control unnamed.",
|
|
28900
|
+
"inputImage_altPresent_hint_emptyAlt": "Describe the action in alt, or name the control with aria-label or aria-labelledby.",
|
|
27304
28901
|
"ariaHidden_programmaticFocus_review_title": "Review aria-hidden programmatic focus",
|
|
27305
28902
|
"ariaHidden_programmaticFocus_review_description": "Flags elements that are aria-hidden but considered eligible due to programmatic focus (e.g., tabindex < 0). Verify intended focus management and assistive technology exposure.",
|
|
27306
28903
|
"ariaHidden_programmaticFocus_review_summary": "Review: aria-hidden element is programmatically focusable.",
|
|
@@ -27582,16 +29179,21 @@ const I18N = {
|
|
|
27582
29179
|
"labelInName_description": "Checks that when a control has a visible text label, the accessible name contains that visible label text (WCAG 2.5.3).",
|
|
27583
29180
|
"labelInName_summary_fail": "{{element}}: visible label \"{{visibleLabel}}\" (from {{labelSource}}) is not included in the accessible name (from {{nameMechanism}}).",
|
|
27584
29181
|
"labelInName_hint_fail": "Update aria-label/aria-labelledby (or the visible label text) so the accessible name includes the visible label wording.",
|
|
29182
|
+
"labelInName_summary_cantTell": "{{element}}: visible label \"{{visibleLabel}}\" (from {{labelSource}}) differs from the accessible name (from {{nameMechanism}}) only by an abbreviation or by hyphenation.",
|
|
29183
|
+
"labelInName_hint_cantTell": "Check by hand whether the two wordings match: markup cannot tell an intended abbreviation from a mismatch.",
|
|
27585
29184
|
"ariaRolesValid_title": "role attribute must be a valid, non-abstract ARIA role",
|
|
27586
29185
|
"ariaRolesValid_description": "Checks that an explicit role=\"\" attribute resolves to a real, non-abstract WAI-ARIA role.",
|
|
27587
29186
|
"ariaRolesValid_summary_invalid": "role=\"{{role}}\" is not a recognized ARIA role.",
|
|
27588
29187
|
"ariaRolesValid_hint_invalid": "Use a valid ARIA role token, or remove the role attribute if none applies.",
|
|
27589
29188
|
"ariaRolesValid_summary_abstract": "role=\"{{role}}\" is an abstract ARIA role, which must not be used directly.",
|
|
27590
29189
|
"ariaRolesValid_hint_abstract": "Replace this abstract role with a concrete role appropriate for the widget/structure.",
|
|
27591
|
-
"ariaDeprecatedRole_title": "role attribute
|
|
27592
|
-
"ariaDeprecatedRole_description": "Checks that an explicit role=\"\" attribute does not use a role deprecated by the WAI-ARIA specification, or one reserved for user-agent-internal use
|
|
29190
|
+
"ariaDeprecatedRole_title": "role attribute should not use a deprecated or author-discouraged ARIA role",
|
|
29191
|
+
"ariaDeprecatedRole_description": "Checks that an explicit role=\"\" attribute does not use a role deprecated by the WAI-ARIA specification, or one reserved for user-agent-internal use (e.g. role=\"generic\").",
|
|
27593
29192
|
"ariaDeprecatedRole_summary_fail": "This element uses role=\"{{role}}\", which authors must not explicitly declare.",
|
|
27594
29193
|
"ariaDeprecatedRole_hint_fail": "{{guidance}}",
|
|
29194
|
+
"ariaDeprecatedRole_summary_cantTell": "This element uses role=\"{{role}}\", which is deprecated in WAI-ARIA (still valid, but discouraged).",
|
|
29195
|
+
"ariaDeprecatedRole_summary_cantTell_discouraged": "This element uses role=\"{{role}}\", which is reserved for user agents (still valid, but discouraged).",
|
|
29196
|
+
"ariaDeprecatedRole_hint_cantTell": "{{guidance}}",
|
|
27595
29197
|
"ariaValidAttr_title": "aria-* attributes must be real, defined ARIA attributes",
|
|
27596
29198
|
"ariaValidAttr_description": "Checks that every aria-* attribute name present in the DOM is a real attribute defined by the WAI-ARIA specification.",
|
|
27597
29199
|
"ariaValidAttr_summary_fail": "{{attr}} is not a recognized ARIA attribute.",
|
|
@@ -27604,6 +29206,8 @@ const I18N = {
|
|
|
27604
29206
|
"ariaAllowedAttr_description": "Checks that every recognized aria-* attribute present on an element with an explicit role is either globally supported or supported by that role.",
|
|
27605
29207
|
"ariaAllowedAttr_summary_fail": "{{attr}} is not permitted on role=\"{{role}}\".",
|
|
27606
29208
|
"ariaAllowedAttr_hint_fail": "Remove this attribute, or use a role that supports it.",
|
|
29209
|
+
"ariaAllowedAttr_summary_cantTell": "{{attr}} is deprecated on role=\"{{role}}\" (still allowed, but discouraged).",
|
|
29210
|
+
"ariaAllowedAttr_hint_cantTell": "This attribute was removed from the ARIA global set in 1.2; remove it or use a role that supports it, as a future ARIA version may disallow it.",
|
|
27607
29211
|
"ariaProhibitedAttr_title": "ARIA naming attributes must not be used on roles that prohibit them",
|
|
27608
29212
|
"ariaProhibitedAttr_description": "Checks that aria-label/aria-labelledby are not present on WAI-ARIA roles whose specification explicitly prohibits ARIA naming (e.g. generic, emphasis, strong, paragraph).",
|
|
27609
29213
|
"ariaProhibitedAttr_summary_fail": "{{attr}} is prohibited on role=\"{{role}}\".",
|
|
@@ -27718,14 +29322,16 @@ const I18N = {
|
|
|
27718
29322
|
"formControlSingleLabel_description": "Checks that a form control is associated with at most one <label> (by wrapping or by label[for]).",
|
|
27719
29323
|
"formControlSingleLabel_summary_fail": "This <{{element}}> is associated with {{labelCount}} labels.",
|
|
27720
29324
|
"formControlSingleLabel_hint_fail": "Keep only one <label> per form control (either wrapping it or referencing it via for/id).",
|
|
29325
|
+
"formControlSingleLabel_summary_cantTell": "This <{{element}}> has one labelling <label> plus an extra empty <label> association; verify how it is announced.",
|
|
29326
|
+
"formControlSingleLabel_hint_cantTell": "Remove the redundant empty <label> so exactly one <label> is associated with the control.",
|
|
27721
29327
|
"nestedInteractiveControlsAbsent_title": "Interactive controls must not be nested",
|
|
27722
29328
|
"nestedInteractiveControlsAbsent_description": "Checks that an interactive control (link, button, form control, or ARIA widget role) does not contain another interactive control.",
|
|
27723
29329
|
"nestedInteractiveControlsAbsent_summary_fail": "This <{{element}}> contains one or more nested interactive controls: {{nestedElements}}.",
|
|
27724
29330
|
"nestedInteractiveControlsAbsent_hint_fail": "Move the nested interactive control(s) outside this element; nested interactive controls are not reliably operable via assistive technology.",
|
|
27725
29331
|
"bypassBlocksPresent_title": "Page must provide a way to bypass repeated blocks",
|
|
27726
29332
|
"bypassBlocksPresent_description": "Checks that the page has at least one recognized WCAG 2.4.1 bypass-blocks mechanism: a main landmark, a working same-page anchor link, or a heading.",
|
|
27727
|
-
"
|
|
27728
|
-
"
|
|
29333
|
+
"bypassBlocksPresent_summary_cantTell": "No recognized way to bypass repeated blocks of content was detected on this page — verify a bypass mechanism exists.",
|
|
29334
|
+
"bypassBlocksPresent_hint_cantTell": "Confirm the page offers a bypass mechanism: a main landmark (<main> or role=\"main\"), a working \"skip to content\" link, or heading elements that assistive technology can use to jump past repeated content. (A mechanism may be temporarily hidden — e.g. while a modal dialog makes the page inert — or provided on a per-site basis; this needs human confirmation.)",
|
|
27729
29335
|
"landmarkBannerIsTopLevel_title": "Banner landmark must be top-level",
|
|
27730
29336
|
"landmarkBannerIsTopLevel_description": "Checks that the banner landmark (role=\"banner\" or a non-nested <header>) is not nested inside another landmark region.",
|
|
27731
29337
|
"landmarkBannerIsTopLevel_summary_cantTell": "This banner landmark is nested inside another landmark region.",
|
|
@@ -27827,8 +29433,8 @@ const I18N = {
|
|
|
27827
29433
|
"htmlXmlLangMismatch_description": "Checks that the <html> element's lang and xml:lang attributes declare the same primary language, when both are present.",
|
|
27828
29434
|
"htmlXmlLangMismatch_summary_fail": "The lang (\"{{lang}}\") and xml:lang (\"{{xmlLang}}\") attributes declare different languages.",
|
|
27829
29435
|
"htmlXmlLangMismatch_hint_fail": "Make lang and xml:lang declare the same primary language, or remove the deprecated xml:lang attribute.",
|
|
27830
|
-
"avoidInlineSpacing_title": "Inline style must not force text spacing
|
|
27831
|
-
"avoidInlineSpacing_description": "Checks that inline style
|
|
29436
|
+
"avoidInlineSpacing_title": "Inline style must not force text spacing below the WCAG metric",
|
|
29437
|
+
"avoidInlineSpacing_description": "Checks that where inline style forces line-height, letter-spacing or word-spacing with !important, the value already meets WCAG 1.4.12, so the user has nothing left to override.",
|
|
27832
29438
|
"avoidInlineSpacing_summary_fail": "This element's inline style forces {{properties}} with !important, blocking user text-spacing overrides.",
|
|
27833
29439
|
"avoidInlineSpacing_hint_fail": "Remove !important from line-height/letter-spacing/word-spacing in inline styles so users can override text spacing.",
|
|
27834
29440
|
"metaRefreshNoExceptions_title": "Page must not use a meta refresh at all (AAA)",
|
|
@@ -27917,6 +29523,10 @@ const I18N = {
|
|
|
27917
29523
|
"inputImage_altPresent_description": "Comprueba que los elementos <input type=\"image\"> incluyan un atributo alt para ofrecer un mecanismo de alternativa textual.",
|
|
27918
29524
|
"inputImage_altPresent_summary_fail": "Falta el atributo alt en <input type=\"image\">.",
|
|
27919
29525
|
"inputImage_altPresent_hint_fail": "Agregar un atributo alt (usar alt=\"\" solo cuando se proporcione un nombre accesible por separado).",
|
|
29526
|
+
"inputImage_altPresent_summary_defaultName": "El nombre accesible es el predeterminado del navegador para un boton de imagen y no aporta informacion.",
|
|
29527
|
+
"inputImage_altPresent_hint_defaultName": "Sustituyelo por un texto que describa la accion del boton, por ejemplo \"Buscar\".",
|
|
29528
|
+
"inputImage_altPresent_summary_emptyAlt": "Un alt=\"\" vacio en <input type=\"image\"> deja el control sin nombre.",
|
|
29529
|
+
"inputImage_altPresent_hint_emptyAlt": "Describe la accion en alt, o nombra el control con aria-label o aria-labelledby.",
|
|
27920
29530
|
"ariaHidden_programmaticFocus_review_title": "Revisar el foco programático en aria-hidden",
|
|
27921
29531
|
"ariaHidden_programmaticFocus_review_description": "Señala elementos aria-hidden considerados elegibles debido a un foco programático (por ejemplo, tabindex < 0). Verificar que la gestión del foco es intencionada y la exposición a las tecnologías de asistencia.",
|
|
27922
29532
|
"ariaHidden_programmaticFocus_review_summary": "Revisión: un elemento aria-hidden es enfocable de forma programática.",
|
|
@@ -28198,16 +29808,21 @@ const I18N = {
|
|
|
28198
29808
|
"labelInName_description": "Comprueba que, cuando un control tiene una etiqueta de texto visible, el nombre accesible contenga ese texto de etiqueta visible (WCAG 2.5.3).",
|
|
28199
29809
|
"labelInName_summary_fail": "{{element}}: la etiqueta visible \"{{visibleLabel}}\" (de {{labelSource}}) no está incluida en el nombre accesible (de {{nameMechanism}}).",
|
|
28200
29810
|
"labelInName_hint_fail": "Actualizar aria-label/aria-labelledby (o el texto de la etiqueta visible) para que el nombre accesible incluya el texto de la etiqueta visible.",
|
|
29811
|
+
"labelInName_summary_cantTell": "{{element}}: la etiqueta visible \"{{visibleLabel}}\" (de {{labelSource}}) se diferencia del nombre accesible (de {{nameMechanism}}) solo por una abreviatura o por la separación con guion.",
|
|
29812
|
+
"labelInName_hint_cantTell": "Comprobar manualmente si ambas redacciones coinciden: el marcado no permite distinguir una abreviatura intencionada de una discrepancia.",
|
|
28201
29813
|
"ariaRolesValid_title": "El atributo role debe ser un rol ARIA válido y no abstracto",
|
|
28202
29814
|
"ariaRolesValid_description": "Comprueba que un atributo role=\"\" explícito se resuelva en un rol WAI-ARIA real y no abstracto.",
|
|
28203
29815
|
"ariaRolesValid_summary_invalid": "role=\"{{role}}\" no es un rol ARIA reconocido.",
|
|
28204
29816
|
"ariaRolesValid_hint_invalid": "Usar un token de rol ARIA válido, o eliminar el atributo role si no aplica ninguno.",
|
|
28205
29817
|
"ariaRolesValid_summary_abstract": "role=\"{{role}}\" es un rol ARIA abstracto, que no debe usarse directamente.",
|
|
28206
29818
|
"ariaRolesValid_hint_abstract": "Reemplazar este rol abstracto por un rol concreto adecuado para el widget o la estructura.",
|
|
28207
|
-
"ariaDeprecatedRole_title": "El atributo role no
|
|
28208
|
-
"ariaDeprecatedRole_description": "Comprueba que un atributo role=\"\" explícito no use un rol obsoleto según la especificación WAI-ARIA, ni uno reservado
|
|
29819
|
+
"ariaDeprecatedRole_title": "El atributo role no debería usar un rol ARIA obsoleto o desaconsejado para autores",
|
|
29820
|
+
"ariaDeprecatedRole_description": "Comprueba que un atributo role=\"\" explícito no use un rol obsoleto según la especificación WAI-ARIA, ni uno reservado para uso interno del agente de usuario (por ejemplo, role=\"generic\").",
|
|
28209
29821
|
"ariaDeprecatedRole_summary_fail": "Este elemento usa role=\"{{role}}\", que los autores no deben declarar explícitamente.",
|
|
28210
29822
|
"ariaDeprecatedRole_hint_fail": "{{guidance}}",
|
|
29823
|
+
"ariaDeprecatedRole_summary_cantTell": "Este elemento usa role=\"{{role}}\", que está obsoleto en WAI-ARIA (todavía válido, pero desaconsejado).",
|
|
29824
|
+
"ariaDeprecatedRole_summary_cantTell_discouraged": "Este elemento usa role=\"{{role}}\", que está reservado para los agentes de usuario (todavía válido, pero desaconsejado).",
|
|
29825
|
+
"ariaDeprecatedRole_hint_cantTell": "{{guidance}}",
|
|
28211
29826
|
"ariaValidAttr_title": "Los atributos aria-* deben ser atributos ARIA reales y definidos",
|
|
28212
29827
|
"ariaValidAttr_description": "Comprueba que cada nombre de atributo aria-* presente en el DOM sea un atributo real definido por la especificación WAI-ARIA.",
|
|
28213
29828
|
"ariaValidAttr_summary_fail": "{{attr}} no es un atributo ARIA reconocido.",
|
|
@@ -28220,6 +29835,8 @@ const I18N = {
|
|
|
28220
29835
|
"ariaAllowedAttr_description": "Comprueba que cada atributo aria-* reconocido presente en un elemento con un rol explícito esté admitido globalmente o admitido por ese rol.",
|
|
28221
29836
|
"ariaAllowedAttr_summary_fail": "{{attr}} no está permitido en role=\"{{role}}\".",
|
|
28222
29837
|
"ariaAllowedAttr_hint_fail": "Eliminar este atributo, o usar un rol que lo admita.",
|
|
29838
|
+
"ariaAllowedAttr_summary_cantTell": "{{attr}} está obsoleto en role=\"{{role}}\" (todavía permitido, pero desaconsejado).",
|
|
29839
|
+
"ariaAllowedAttr_hint_cantTell": "Este atributo se eliminó del conjunto global de ARIA en 1.2; elimínelo o use un rol que lo admita, ya que una versión futura de ARIA podría no permitirlo.",
|
|
28223
29840
|
"ariaProhibitedAttr_title": "Los atributos de nombrado ARIA no deben usarse en roles que los prohíban",
|
|
28224
29841
|
"ariaProhibitedAttr_description": "Comprueba que aria-label/aria-labelledby no estén presentes en roles WAI-ARIA cuya especificación prohíbe explícitamente el nombrado ARIA (por ejemplo, generic, emphasis, strong, paragraph).",
|
|
28225
29842
|
"ariaProhibitedAttr_summary_fail": "{{attr}} está prohibido en role=\"{{role}}\".",
|
|
@@ -28334,14 +29951,16 @@ const I18N = {
|
|
|
28334
29951
|
"formControlSingleLabel_description": "Comprueba que un control de formulario esté asociado a como máximo una <label> (por envoltura o mediante label[for]).",
|
|
28335
29952
|
"formControlSingleLabel_summary_fail": "Este <{{element}}> está asociado a {{labelCount}} etiquetas.",
|
|
28336
29953
|
"formControlSingleLabel_hint_fail": "Mantener solo una <label> por control de formulario (ya sea envolviéndolo o referenciándolo mediante for/id).",
|
|
29954
|
+
"formControlSingleLabel_summary_cantTell": "Este <{{element}}> tiene una <label> que lo etiqueta más una asociación de <label> vacía adicional; verifique cómo se anuncia.",
|
|
29955
|
+
"formControlSingleLabel_hint_cantTell": "Elimine la <label> vacía redundante para que solo una <label> quede asociada al control.",
|
|
28337
29956
|
"nestedInteractiveControlsAbsent_title": "Los controles interactivos no deben estar anidados",
|
|
28338
29957
|
"nestedInteractiveControlsAbsent_description": "Comprueba que un control interactivo (enlace, botón, control de formulario o rol de widget ARIA) no contenga otro control interactivo.",
|
|
28339
29958
|
"nestedInteractiveControlsAbsent_summary_fail": "Este <{{element}}> contiene uno o más controles interactivos anidados: {{nestedElements}}.",
|
|
28340
29959
|
"nestedInteractiveControlsAbsent_hint_fail": "Mover el/los control(es) interactivo(s) anidado(s) fuera de este elemento; los controles interactivos anidados no son operables de forma fiable mediante tecnología de asistencia.",
|
|
28341
29960
|
"bypassBlocksPresent_title": "La página debe proporcionar una forma de omitir bloques repetidos",
|
|
28342
29961
|
"bypassBlocksPresent_description": "Comprueba que la página tenga al menos un mecanismo reconocido de omisión de bloques del criterio de éxito 2.4.1 de WCAG: un landmark main, un enlace de anclaje funcional dentro de la misma página, o un encabezado.",
|
|
28343
|
-
"
|
|
28344
|
-
"
|
|
29962
|
+
"bypassBlocksPresent_summary_cantTell": "No se detectó ninguna forma reconocida de omitir bloques de contenido repetidos en esta página; verifique que exista un mecanismo de omisión.",
|
|
29963
|
+
"bypassBlocksPresent_hint_cantTell": "Confirme que la página ofrece un mecanismo de omisión: un landmark main (<main> o role=\"main\"), un enlace funcional de \"saltar al contenido\", o elementos de encabezado que las tecnologías de asistencia puedan usar para saltar el contenido repetido. (Un mecanismo puede estar oculto temporalmente —por ejemplo, mientras un diálogo modal deja la página inerte— o proporcionarse a nivel de sitio; esto requiere confirmación humana.)",
|
|
28345
29964
|
"landmarkBannerIsTopLevel_title": "El landmark banner debe ser de nivel superior",
|
|
28346
29965
|
"landmarkBannerIsTopLevel_description": "Comprueba que el landmark banner (role=\"banner\" o un <header> no anidado) no esté anidado dentro de otra región landmark.",
|
|
28347
29966
|
"landmarkBannerIsTopLevel_summary_cantTell": "Este landmark banner está anidado dentro de otra región landmark.",
|
|
@@ -28443,8 +30062,8 @@ const I18N = {
|
|
|
28443
30062
|
"htmlXmlLangMismatch_description": "Comprueba que los atributos lang y xml:lang del elemento <html> declaren el mismo idioma principal, cuando ambos están presentes.",
|
|
28444
30063
|
"htmlXmlLangMismatch_summary_fail": "Los atributos lang (\"{{lang}}\") y xml:lang (\"{{xmlLang}}\") declaran idiomas diferentes.",
|
|
28445
30064
|
"htmlXmlLangMismatch_hint_fail": "Hacer que lang y xml:lang declaren el mismo idioma principal, o eliminar el atributo obsoleto xml:lang.",
|
|
28446
|
-
"avoidInlineSpacing_title": "El estilo en línea no debe forzar el espaciado de texto
|
|
28447
|
-
"avoidInlineSpacing_description": "Comprueba que el estilo en línea
|
|
30065
|
+
"avoidInlineSpacing_title": "El estilo en línea no debe forzar el espaciado de texto por debajo del umbral WCAG",
|
|
30066
|
+
"avoidInlineSpacing_description": "Comprueba que cuando el estilo en línea fuerza line-height, letter-spacing o word-spacing con !important, el valor ya cumple WCAG 1.4.12, por lo que al usuario no le queda nada que anular.",
|
|
28448
30067
|
"avoidInlineSpacing_summary_fail": "El estilo en línea de este elemento fuerza {{properties}} con !important, bloqueando las anulaciones de espaciado de texto del usuario.",
|
|
28449
30068
|
"avoidInlineSpacing_hint_fail": "Eliminar !important de line-height/letter-spacing/word-spacing en los estilos en línea para que los usuarios puedan anular el espaciado de texto.",
|
|
28450
30069
|
"metaRefreshNoExceptions_title": "La página no debe usar un meta refresh en absoluto (AAA)",
|
|
@@ -28533,6 +30152,10 @@ const I18N = {
|
|
|
28533
30152
|
"inputImage_altPresent_description": "Vérifie que les éléments <input type=\"image\"> fournissent un attribut alt afin de proposer un mécanisme d’alternative textuelle.",
|
|
28534
30153
|
"inputImage_altPresent_summary_fail": "Attribut alt manquant sur <input type=\"image\">.",
|
|
28535
30154
|
"inputImage_altPresent_hint_fail": "Ajoutez un attribut alt (utilisez alt=\"\" uniquement lorsqu’un nom accessible séparé est fourni).",
|
|
30155
|
+
"inputImage_altPresent_summary_defaultName": "Le nom accessible est celui par defaut du navigateur pour un bouton image et n'apporte aucune information.",
|
|
30156
|
+
"inputImage_altPresent_hint_defaultName": "Remplacez-le par un texte decrivant l action du bouton, par exemple \"Rechercher\".",
|
|
30157
|
+
"inputImage_altPresent_summary_emptyAlt": "Un alt=\"\" vide sur <input type=\"image\"> laisse le controle sans nom.",
|
|
30158
|
+
"inputImage_altPresent_hint_emptyAlt": "Decrivez l action dans alt, ou nommez le controle avec aria-label ou aria-labelledby.",
|
|
28536
30159
|
"ariaHidden_programmaticFocus_review_title": "Vérifier le focus programmatique avec aria-hidden",
|
|
28537
30160
|
"ariaHidden_programmaticFocus_review_description": "Signale les éléments aria-hidden considérés comme éligibles uniquement via un focus programmatique (ex. tabindex < 0). Vérifiez l’intention de gestion du focus et l’exposition aux technologies d’assistance.",
|
|
28538
30161
|
"ariaHidden_programmaticFocus_review_summary": "Vérification : un élément aria-hidden est focusable de façon programmatique.",
|
|
@@ -28814,16 +30437,21 @@ const I18N = {
|
|
|
28814
30437
|
"labelInName_description": "Vérifie que lorsqu’un composant possède un libellé textuel visible, le nom accessible contient ce libellé visible (WCAG 2.5.3).",
|
|
28815
30438
|
"labelInName_summary_fail": "{{element}} : le libellé visible « {{visibleLabel}} » (source : {{labelSource}}) n’est pas inclus dans le nom accessible (source : {{nameMechanism}}).",
|
|
28816
30439
|
"labelInName_hint_fail": "Modifiez aria-label ou aria-labelledby (ou le texte du libellé visible) afin que le nom accessible inclue le libellé visible.",
|
|
30440
|
+
"labelInName_summary_cantTell": "{{element}} : le libellé visible « {{visibleLabel}} » (source : {{labelSource}}) ne diffère du nom accessible (source : {{nameMechanism}}) que par une abréviation ou une césure.",
|
|
30441
|
+
"labelInName_hint_cantTell": "Vérifiez manuellement que les deux formulations correspondent : le balisage ne permet pas de distinguer une abréviation volontaire d’une incohérence.",
|
|
28817
30442
|
"ariaRolesValid_title": "L’attribut role doit être un rôle ARIA valide et non abstrait",
|
|
28818
30443
|
"ariaRolesValid_description": "Vérifie qu’un attribut role=\"\" explicite correspond à un rôle WAI-ARIA réel et non abstrait.",
|
|
28819
30444
|
"ariaRolesValid_summary_invalid": "role=\"{{role}}\" n’est pas un rôle ARIA reconnu.",
|
|
28820
30445
|
"ariaRolesValid_hint_invalid": "Utilisez un rôle ARIA valide, ou retirez l’attribut role si aucun ne s’applique.",
|
|
28821
30446
|
"ariaRolesValid_summary_abstract": "role=\"{{role}}\" est un rôle ARIA abstrait, qui ne doit pas être utilisé directement.",
|
|
28822
30447
|
"ariaRolesValid_hint_abstract": "Remplacez ce rôle abstrait par un rôle concret adapté au composant/à la structure.",
|
|
28823
|
-
"ariaDeprecatedRole_title": "L’attribut role ne
|
|
30448
|
+
"ariaDeprecatedRole_title": "L’attribut role ne devrait pas utiliser un rôle ARIA obsolète ou déconseillé aux auteurs",
|
|
28824
30449
|
"ariaDeprecatedRole_description": "Vérifie qu’un attribut role=\"\" explicite n’utilise pas un rôle rendu obsolète par la spécification WAI-ARIA, ni un rôle réservé à un usage interne à l’agent utilisateur (ex. role=\"generic\").",
|
|
28825
30450
|
"ariaDeprecatedRole_summary_fail": "Cet élément utilise role=\"{{role}}\", que les auteurs ne doivent pas déclarer explicitement.",
|
|
28826
30451
|
"ariaDeprecatedRole_hint_fail": "{{guidance}}",
|
|
30452
|
+
"ariaDeprecatedRole_summary_cantTell": "Cet élément utilise role=\"{{role}}\", qui est obsolète dans WAI-ARIA (toujours valide, mais déconseillé).",
|
|
30453
|
+
"ariaDeprecatedRole_summary_cantTell_discouraged": "Cet élément utilise role=\"{{role}}\", qui est réservé aux agents utilisateurs (toujours valide, mais déconseillé).",
|
|
30454
|
+
"ariaDeprecatedRole_hint_cantTell": "{{guidance}}",
|
|
28827
30455
|
"ariaValidAttr_title": "Les attributs aria-* doivent être des attributs ARIA réels et définis",
|
|
28828
30456
|
"ariaValidAttr_description": "Vérifie que chaque nom d’attribut aria-* présent dans le DOM est un attribut réel défini par la spécification WAI-ARIA.",
|
|
28829
30457
|
"ariaValidAttr_summary_fail": "{{attr}} n’est pas un attribut ARIA reconnu.",
|
|
@@ -28836,6 +30464,8 @@ const I18N = {
|
|
|
28836
30464
|
"ariaAllowedAttr_description": "Vérifie que chaque attribut aria-* reconnu présent sur un élément ayant un rôle explicite est soit globalement pris en charge, soit pris en charge par ce rôle.",
|
|
28837
30465
|
"ariaAllowedAttr_summary_fail": "{{attr}} n’est pas autorisé sur role=\"{{role}}\".",
|
|
28838
30466
|
"ariaAllowedAttr_hint_fail": "Retirez cet attribut, ou utilisez un rôle qui le prend en charge.",
|
|
30467
|
+
"ariaAllowedAttr_summary_cantTell": "{{attr}} est obsolète sur role=\"{{role}}\" (toujours autorisé, mais déconseillé).",
|
|
30468
|
+
"ariaAllowedAttr_hint_cantTell": "Cet attribut a été retiré de l’ensemble global d’ARIA en 1.2 ; retirez-le ou utilisez un rôle qui le prend en charge, car une future version d’ARIA pourrait l’interdire.",
|
|
28839
30469
|
"ariaProhibitedAttr_title": "Les attributs de nommage ARIA ne doivent pas être utilisés sur des rôles qui les interdisent",
|
|
28840
30470
|
"ariaProhibitedAttr_description": "Vérifie que aria-label/aria-labelledby ne sont pas présents sur des rôles WAI-ARIA dont la spécification interdit explicitement le nommage ARIA (ex. generic, emphasis, strong, paragraph).",
|
|
28841
30471
|
"ariaProhibitedAttr_summary_fail": "{{attr}} est interdit sur role=\"{{role}}\".",
|
|
@@ -28950,14 +30580,16 @@ const I18N = {
|
|
|
28950
30580
|
"formControlSingleLabel_description": "Vérifie qu’un contrôle de formulaire est associé à au plus un <label> (par imbrication ou par label[for]).",
|
|
28951
30581
|
"formControlSingleLabel_summary_fail": "Ce <{{element}}> est associé à {{labelCount}} étiquettes.",
|
|
28952
30582
|
"formControlSingleLabel_hint_fail": "Conservez une seule <label> par contrôle de formulaire (soit en l’enveloppant, soit en la référençant via for/id).",
|
|
30583
|
+
"formControlSingleLabel_summary_cantTell": "Ce <{{element}}> a une <label> qui l’étiquette plus une association de <label> vide supplémentaire ; vérifiez comment il est annoncé.",
|
|
30584
|
+
"formControlSingleLabel_hint_cantTell": "Supprimez la <label> vide redondante afin qu’une seule <label> soit associée au contrôle.",
|
|
28953
30585
|
"nestedInteractiveControlsAbsent_title": "Les contrôles interactifs ne doivent pas être imbriqués",
|
|
28954
30586
|
"nestedInteractiveControlsAbsent_description": "Vérifie qu’un contrôle interactif (lien, bouton, contrôle de formulaire, ou rôle de widget ARIA) ne contient pas un autre contrôle interactif.",
|
|
28955
30587
|
"nestedInteractiveControlsAbsent_summary_fail": "Ce <{{element}}> contient un ou plusieurs contrôles interactifs imbriqués : {{nestedElements}}.",
|
|
28956
30588
|
"nestedInteractiveControlsAbsent_hint_fail": "Déplacez le(s) contrôle(s) interactif(s) imbriqué(s) hors de cet élément ; les contrôles interactifs imbriqués ne sont pas utilisables de façon fiable via les technologies d’assistance.",
|
|
28957
30589
|
"bypassBlocksPresent_title": "La page doit proposer un moyen de contourner les blocs répétés",
|
|
28958
30590
|
"bypassBlocksPresent_description": "Vérifie que la page dispose d’au moins un mécanisme reconnu de contournement des blocs répétés (WCAG 2.4.1) : un point de repère main, un lien d’ancrage fonctionnel vers la même page, ou un titre.",
|
|
28959
|
-
"
|
|
28960
|
-
"
|
|
30591
|
+
"bypassBlocksPresent_summary_cantTell": "Aucun moyen reconnu de contourner les blocs de contenu répétés n’a été détecté sur cette page — vérifiez qu’un mécanisme de contournement existe.",
|
|
30592
|
+
"bypassBlocksPresent_hint_cantTell": "Confirmez que la page propose un mécanisme de contournement : un point de repère main (<main> ou role=\"main\"), un lien « aller au contenu » fonctionnel, ou des éléments de titre que les technologies d’assistance peuvent utiliser pour passer le contenu répété. (Un mécanisme peut être temporairement masqué — par exemple pendant qu’une boîte de dialogue modale rend la page inerte — ou fourni à l’échelle du site ; cela nécessite une confirmation humaine.)",
|
|
28961
30593
|
"landmarkBannerIsTopLevel_title": "Le point de repère banner doit être de premier niveau",
|
|
28962
30594
|
"landmarkBannerIsTopLevel_description": "Vérifie que le point de repère banner (role=\"banner\" ou un <header> non imbriqué) n’est pas imbriqué dans un autre point de repère.",
|
|
28963
30595
|
"landmarkBannerIsTopLevel_summary_cantTell": "Ce point de repère banner est imbriqué dans un autre point de repère.",
|
|
@@ -29059,8 +30691,8 @@ const I18N = {
|
|
|
29059
30691
|
"htmlXmlLangMismatch_description": "Vérifie que les attributs lang et xml:lang de l’élément <html> déclarent la même langue principale, lorsque les deux sont présents.",
|
|
29060
30692
|
"htmlXmlLangMismatch_summary_fail": "Les attributs lang (« {{lang}} ») et xml:lang (« {{xmlLang}} ») déclarent des langues différentes.",
|
|
29061
30693
|
"htmlXmlLangMismatch_hint_fail": "Faites en sorte que lang et xml:lang déclarent la même langue principale, ou retirez l’attribut obsolète xml:lang.",
|
|
29062
|
-
"avoidInlineSpacing_title": "Un style en ligne ne doit pas forcer l’espacement du texte
|
|
29063
|
-
"avoidInlineSpacing_description": "Vérifie
|
|
30694
|
+
"avoidInlineSpacing_title": "Un style en ligne ne doit pas forcer l’espacement du texte en dessous du seuil WCAG",
|
|
30695
|
+
"avoidInlineSpacing_description": "Vérifie que lorsqu’un style en ligne force line-height, letter-spacing ou word-spacing avec !important, la valeur respecte déjà WCAG 1.4.12, ne laissant rien à surcharger à l’utilisateur.",
|
|
29064
30696
|
"avoidInlineSpacing_summary_fail": "Le style en ligne de cet élément force {{properties}} avec !important, bloquant les surcharges d’espacement de texte par l’utilisateur.",
|
|
29065
30697
|
"avoidInlineSpacing_hint_fail": "Retirez !important de line-height/letter-spacing/word-spacing dans les styles en ligne afin que les utilisateurs puissent surcharger l’espacement du texte.",
|
|
29066
30698
|
"metaRefreshNoExceptions_title": "La page ne doit utiliser aucun rafraîchissement meta (AAA)",
|
|
@@ -30073,6 +31705,35 @@ const createContrastHelpers = (function createContrastHelpers(opts, shared) {
|
|
|
30073
31705
|
|
|
30074
31706
|
const eligCache = new WeakMap();
|
|
30075
31707
|
const inactiveCache = new WeakMap();
|
|
31708
|
+
const clipHiddenCache = new WeakMap();
|
|
31709
|
+
|
|
31710
|
+
// clip:rect(0,0,0,0) / clip-path:inset(50%+) (the sr-only technique) has
|
|
31711
|
+
// no visually-presented color, so contrast rules exempt it even though
|
|
31712
|
+
// isDomVisibleEligible itself keeps it eligible for other callers
|
|
31713
|
+
// (aria-hidden-focus etc. need to find clipped-but-focusable elements).
|
|
31714
|
+
const isClipHidden = (el) => {
|
|
31715
|
+
if (!helpers || typeof helpers.getVisibilityHintsInfo !== 'function') return false;
|
|
31716
|
+
if (clipHiddenCache.has(el)) return clipHiddenCache.get(el);
|
|
31717
|
+
|
|
31718
|
+
let hidden = false;
|
|
31719
|
+
try {
|
|
31720
|
+
let cur = el;
|
|
31721
|
+
let guard = 0;
|
|
31722
|
+
while (cur && cur.nodeType === 1 && guard++ < 100) {
|
|
31723
|
+
const info = helpers.getVisibilityHintsInfo(cur, ctx, {});
|
|
31724
|
+
if (info && Array.isArray(info.hints) && info.hints.indexOf('clipped') !== -1) {
|
|
31725
|
+
hidden = true;
|
|
31726
|
+
break;
|
|
31727
|
+
}
|
|
31728
|
+
cur = composedParent ? composedParent(cur) : cur.parentElement;
|
|
31729
|
+
}
|
|
31730
|
+
} catch {
|
|
31731
|
+
hidden = false;
|
|
31732
|
+
}
|
|
31733
|
+
|
|
31734
|
+
clipHiddenCache.set(el, hidden);
|
|
31735
|
+
return hidden;
|
|
31736
|
+
};
|
|
30076
31737
|
|
|
30077
31738
|
const isVisibleEligible = (el) => {
|
|
30078
31739
|
if (!helpers || typeof helpers.isDomVisibleEligible !== 'function') return true;
|
|
@@ -30082,6 +31743,7 @@ const createContrastHelpers = (function createContrastHelpers(opts, shared) {
|
|
|
30082
31743
|
try {
|
|
30083
31744
|
const r = helpers.isDomVisibleEligible(el, ctx, { visibilityMode });
|
|
30084
31745
|
ok = __asEligibilityBool(r);
|
|
31746
|
+
if (ok && isClipHidden(el)) ok = false;
|
|
30085
31747
|
} catch {
|
|
30086
31748
|
ok = false;
|
|
30087
31749
|
}
|
|
@@ -31196,6 +32858,7 @@ const createAriaHelpers = (function createAriaHelpers(opts, shared) {
|
|
|
31196
32858
|
// -------------------------------------------------------------------
|
|
31197
32859
|
// A) Abstract roles — MUST NOT be used directly in a role="" attribute.
|
|
31198
32860
|
// -------------------------------------------------------------------
|
|
32861
|
+
// <generated:aria-abstract-roles>
|
|
31199
32862
|
const ABSTRACT_ROLES = new Set([
|
|
31200
32863
|
'command',
|
|
31201
32864
|
'composite',
|
|
@@ -31210,23 +32873,44 @@ const createAriaHelpers = (function createAriaHelpers(opts, shared) {
|
|
|
31210
32873
|
'widget',
|
|
31211
32874
|
'window'
|
|
31212
32875
|
]);
|
|
32876
|
+
// </generated:aria-abstract-roles>
|
|
31213
32877
|
|
|
31214
32878
|
// -------------------------------------------------------------------
|
|
31215
|
-
// B) Valid, concrete (non-abstract) roles
|
|
31216
|
-
//
|
|
31217
|
-
//
|
|
31218
|
-
// user-agent-internal use
|
|
31219
|
-
//
|
|
31220
|
-
//
|
|
31221
|
-
//
|
|
31222
|
-
// per-role, reason-accurate messaging.
|
|
32879
|
+
// B) Valid, concrete (non-abstract) roles authors should not explicitly
|
|
32880
|
+
// declare — either because WAI-ARIA has deprecated them (a direct
|
|
32881
|
+
// replacement exists) or because they are reserved for
|
|
32882
|
+
// user-agent-internal use. Flagged by aria-deprecated-role, not
|
|
32883
|
+
// aria-roles-valid (which only checks existence/abstractness) — see
|
|
32884
|
+
// DEPRECATED_ROLE_GUIDANCE below for per-role, reason-accurate
|
|
32885
|
+
// messaging.
|
|
31223
32886
|
// -------------------------------------------------------------------
|
|
32887
|
+
// Deprecated but still VALID roles (SHOULD NOT, still conforming). Reported
|
|
32888
|
+
// as cantTell so the author decides whether it matters to them.
|
|
31224
32889
|
const DEPRECATED_ROLES = new Set([
|
|
31225
|
-
'directory'
|
|
31226
|
-
|
|
31227
|
-
|
|
31228
|
-
|
|
31229
|
-
|
|
32890
|
+
'directory' // superseded by role="list"
|
|
32891
|
+
]);
|
|
32892
|
+
|
|
32893
|
+
// Valid roles reserved for user-agent-internal use, which ARIA states at
|
|
32894
|
+
// SHOULD NOT strength — conforming, so reported as cantTell.
|
|
32895
|
+
const AUTHOR_DISCOURAGED_ROLES = new Set([
|
|
32896
|
+
'generic' // "primarily for implementors of user agents"
|
|
32897
|
+
]);
|
|
32898
|
+
|
|
32899
|
+
// Roles carrying an author MUST NOT, reported as fail. Empty under ARIA 1.2
|
|
32900
|
+
// and 1.3, whose only author MUST NOT covers abstract roles — the concern of
|
|
32901
|
+
// aria-roles-valid.
|
|
32902
|
+
const AUTHOR_PROHIBITED_ROLES = new Set([]);
|
|
32903
|
+
|
|
32904
|
+
// Deprecated but still ALLOWED states/properties (SHOULD NOT, still
|
|
32905
|
+
// conforming): the four ARIA 1.2 keeps in the global set as deprecated and
|
|
32906
|
+
// marks "deprecated on this role" wherever a role does not support them.
|
|
32907
|
+
// Reported as cantTell, not a not-allowed fail. Flat rather than per-role
|
|
32908
|
+
// because the deprecation is uniform and no role prohibits any of the four.
|
|
32909
|
+
const DEPRECATED_ATTRS = new Set([
|
|
32910
|
+
'aria-disabled',
|
|
32911
|
+
'aria-errormessage',
|
|
32912
|
+
'aria-haspopup',
|
|
32913
|
+
'aria-invalid'
|
|
31230
32914
|
]);
|
|
31231
32915
|
|
|
31232
32916
|
const DEPRECATED_ROLE_GUIDANCE = {
|
|
@@ -31252,103 +32936,140 @@ const createAriaHelpers = (function createAriaHelpers(opts, shared) {
|
|
|
31252
32936
|
// Digital Publishing WAI-ARIA (doc-abstract etc.) is a separate
|
|
31253
32937
|
// module, deliberately left out of scope for now.
|
|
31254
32938
|
// -------------------------------------------------------------------
|
|
32939
|
+
// <generated:aria-concrete-roles>
|
|
31255
32940
|
const CONCRETE_ROLES = new Set([
|
|
31256
|
-
// WAI-ARIA Graphics Module 1.0 (see comment above)
|
|
31257
|
-
'graphics-document',
|
|
31258
|
-
'graphics-object',
|
|
31259
|
-
'graphics-symbol',
|
|
31260
|
-
// Live region / window roles
|
|
31261
32941
|
'alert',
|
|
31262
32942
|
'alertdialog',
|
|
31263
|
-
'dialog',
|
|
31264
|
-
'log',
|
|
31265
|
-
'marquee',
|
|
31266
|
-
'status',
|
|
31267
|
-
'timer',
|
|
31268
|
-
// Landmark roles
|
|
31269
|
-
'banner',
|
|
31270
|
-
'complementary',
|
|
31271
|
-
'contentinfo',
|
|
31272
|
-
'form',
|
|
31273
|
-
'main',
|
|
31274
|
-
'navigation',
|
|
31275
|
-
'region',
|
|
31276
|
-
'search',
|
|
31277
|
-
// Widget roles (leaf)
|
|
31278
|
-
'button',
|
|
31279
|
-
'checkbox',
|
|
31280
|
-
'gridcell',
|
|
31281
|
-
'link',
|
|
31282
|
-
'menuitem',
|
|
31283
|
-
'menuitemcheckbox',
|
|
31284
|
-
'menuitemradio',
|
|
31285
|
-
'option',
|
|
31286
|
-
'progressbar',
|
|
31287
|
-
'radio',
|
|
31288
|
-
'scrollbar',
|
|
31289
|
-
'searchbox',
|
|
31290
|
-
'separator',
|
|
31291
|
-
'slider',
|
|
31292
|
-
'spinbutton',
|
|
31293
|
-
'switch',
|
|
31294
|
-
'tab',
|
|
31295
|
-
'tabpanel',
|
|
31296
|
-
'textbox',
|
|
31297
|
-
'treeitem',
|
|
31298
|
-
'tooltip',
|
|
31299
|
-
// Composite widget roles
|
|
31300
|
-
'combobox',
|
|
31301
|
-
'grid',
|
|
31302
|
-
'listbox',
|
|
31303
|
-
'menu',
|
|
31304
|
-
'menubar',
|
|
31305
|
-
'radiogroup',
|
|
31306
|
-
'tablist',
|
|
31307
|
-
'tree',
|
|
31308
|
-
'treegrid',
|
|
31309
|
-
// Document structure roles
|
|
31310
32943
|
'application',
|
|
31311
32944
|
'article',
|
|
32945
|
+
'banner',
|
|
31312
32946
|
'blockquote',
|
|
32947
|
+
'button',
|
|
31313
32948
|
'caption',
|
|
31314
32949
|
'cell',
|
|
32950
|
+
'checkbox',
|
|
31315
32951
|
'code',
|
|
31316
32952
|
'columnheader',
|
|
32953
|
+
'combobox',
|
|
31317
32954
|
'comment',
|
|
32955
|
+
'complementary',
|
|
32956
|
+
'contentinfo',
|
|
31318
32957
|
'definition',
|
|
31319
32958
|
'deletion',
|
|
32959
|
+
'dialog',
|
|
31320
32960
|
'directory',
|
|
32961
|
+
'doc-abstract',
|
|
32962
|
+
'doc-acknowledgments',
|
|
32963
|
+
'doc-afterword',
|
|
32964
|
+
'doc-appendix',
|
|
32965
|
+
'doc-backlink',
|
|
32966
|
+
'doc-biblioentry',
|
|
32967
|
+
'doc-bibliography',
|
|
32968
|
+
'doc-biblioref',
|
|
32969
|
+
'doc-chapter',
|
|
32970
|
+
'doc-colophon',
|
|
32971
|
+
'doc-conclusion',
|
|
32972
|
+
'doc-cover',
|
|
32973
|
+
'doc-credit',
|
|
32974
|
+
'doc-credits',
|
|
32975
|
+
'doc-dedication',
|
|
32976
|
+
'doc-endnote',
|
|
32977
|
+
'doc-endnotes',
|
|
32978
|
+
'doc-epigraph',
|
|
32979
|
+
'doc-epilogue',
|
|
32980
|
+
'doc-errata',
|
|
32981
|
+
'doc-example',
|
|
32982
|
+
'doc-footnote',
|
|
32983
|
+
'doc-foreword',
|
|
32984
|
+
'doc-glossary',
|
|
32985
|
+
'doc-glossref',
|
|
32986
|
+
'doc-index',
|
|
32987
|
+
'doc-introduction',
|
|
32988
|
+
'doc-noteref',
|
|
32989
|
+
'doc-notice',
|
|
32990
|
+
'doc-pagebreak',
|
|
32991
|
+
'doc-pagefooter',
|
|
32992
|
+
'doc-pageheader',
|
|
32993
|
+
'doc-pagelist',
|
|
32994
|
+
'doc-part',
|
|
32995
|
+
'doc-preface',
|
|
32996
|
+
'doc-prologue',
|
|
32997
|
+
'doc-pullquote',
|
|
32998
|
+
'doc-qna',
|
|
32999
|
+
'doc-subtitle',
|
|
33000
|
+
'doc-tip',
|
|
33001
|
+
'doc-toc',
|
|
31321
33002
|
'document',
|
|
31322
33003
|
'emphasis',
|
|
31323
33004
|
'feed',
|
|
31324
33005
|
'figure',
|
|
33006
|
+
'form',
|
|
31325
33007
|
'generic',
|
|
33008
|
+
'graphics-document',
|
|
33009
|
+
'graphics-object',
|
|
33010
|
+
'graphics-symbol',
|
|
33011
|
+
'grid',
|
|
33012
|
+
'gridcell',
|
|
31326
33013
|
'group',
|
|
31327
33014
|
'heading',
|
|
31328
33015
|
'img',
|
|
31329
33016
|
'insertion',
|
|
33017
|
+
'link',
|
|
31330
33018
|
'list',
|
|
33019
|
+
'listbox',
|
|
31331
33020
|
'listitem',
|
|
33021
|
+
'log',
|
|
33022
|
+
'main',
|
|
31332
33023
|
'mark',
|
|
33024
|
+
'marquee',
|
|
31333
33025
|
'math',
|
|
33026
|
+
'menu',
|
|
33027
|
+
'menubar',
|
|
33028
|
+
'menuitem',
|
|
33029
|
+
'menuitemcheckbox',
|
|
33030
|
+
'menuitemradio',
|
|
31334
33031
|
'meter',
|
|
33032
|
+
'navigation',
|
|
31335
33033
|
'none',
|
|
31336
33034
|
'note',
|
|
33035
|
+
'option',
|
|
31337
33036
|
'paragraph',
|
|
31338
33037
|
'presentation',
|
|
33038
|
+
'progressbar',
|
|
33039
|
+
'radio',
|
|
33040
|
+
'radiogroup',
|
|
33041
|
+
'region',
|
|
31339
33042
|
'row',
|
|
31340
33043
|
'rowgroup',
|
|
31341
33044
|
'rowheader',
|
|
33045
|
+
'scrollbar',
|
|
33046
|
+
'search',
|
|
33047
|
+
'searchbox',
|
|
33048
|
+
'separator',
|
|
33049
|
+
'slider',
|
|
33050
|
+
'spinbutton',
|
|
33051
|
+
'status',
|
|
31342
33052
|
'strong',
|
|
31343
33053
|
'subscript',
|
|
31344
33054
|
'suggestion',
|
|
31345
33055
|
'superscript',
|
|
33056
|
+
'switch',
|
|
33057
|
+
'tab',
|
|
31346
33058
|
'table',
|
|
33059
|
+
'tablist',
|
|
33060
|
+
'tabpanel',
|
|
31347
33061
|
'term',
|
|
31348
33062
|
'text',
|
|
33063
|
+
'textbox',
|
|
31349
33064
|
'time',
|
|
31350
|
-
'
|
|
33065
|
+
'timer',
|
|
33066
|
+
'toolbar',
|
|
33067
|
+
'tooltip',
|
|
33068
|
+
'tree',
|
|
33069
|
+
'treegrid',
|
|
33070
|
+
'treeitem'
|
|
31351
33071
|
]);
|
|
33072
|
+
// </generated:aria-concrete-roles>
|
|
31352
33073
|
|
|
31353
33074
|
// -------------------------------------------------------------------
|
|
31354
33075
|
// D) ARIA attribute value types.
|
|
@@ -31864,6 +33585,18 @@ const createAriaHelpers = (function createAriaHelpers(opts, shared) {
|
|
|
31864
33585
|
return DEPRECATED_ROLES.has(lower(role));
|
|
31865
33586
|
}
|
|
31866
33587
|
|
|
33588
|
+
function isAuthorDiscouragedRole(role) {
|
|
33589
|
+
return AUTHOR_DISCOURAGED_ROLES.has(lower(role));
|
|
33590
|
+
}
|
|
33591
|
+
|
|
33592
|
+
function isAuthorProhibitedRole(role) {
|
|
33593
|
+
return AUTHOR_PROHIBITED_ROLES.has(lower(role));
|
|
33594
|
+
}
|
|
33595
|
+
|
|
33596
|
+
function isDeprecatedAttr(attr /* , role */) {
|
|
33597
|
+
return DEPRECATED_ATTRS.has(lower(attr));
|
|
33598
|
+
}
|
|
33599
|
+
|
|
31867
33600
|
function isKnownRole(role) {
|
|
31868
33601
|
const r = lower(role);
|
|
31869
33602
|
return ABSTRACT_ROLES.has(r) || CONCRETE_ROLES.has(r);
|
|
@@ -32139,6 +33872,9 @@ const createAriaHelpers = (function createAriaHelpers(opts, shared) {
|
|
|
32139
33872
|
getAllRoleTokens,
|
|
32140
33873
|
isAbstractRole,
|
|
32141
33874
|
isDeprecatedRole,
|
|
33875
|
+
isAuthorDiscouragedRole,
|
|
33876
|
+
isAuthorProhibitedRole,
|
|
33877
|
+
isDeprecatedAttr,
|
|
32142
33878
|
getDeprecatedRoleGuidance,
|
|
32143
33879
|
isKnownRole,
|
|
32144
33880
|
isValidConcreteRole,
|
|
@@ -32226,6 +33962,28 @@ const resolveContextRoots = (function resolveContextRoots(document, contextSelec
|
|
|
32226
33962
|
return { ctxSelector, roots };
|
|
32227
33963
|
});
|
|
32228
33964
|
const createDomHelpers = (function createDomHelpers(opts) {
|
|
33965
|
+
// <generated:language-subtags>
|
|
33966
|
+
const LANGUAGE_SUBTAGS =
|
|
33967
|
+
'aa aaa aab aac aad aae aaf aag aah aai aak aal aam aan aao aap aaq aas aat aau aav aaw aax aaz ab aba abb abc abd abe abf abg abh abi abj abl abm abn abo abp abq abr abs abt abu abv abw abx aby abz aca acb acd ace acf ach aci ack acl acm acn acp acq acr acs act acu acv acw acx acy acz ada adb add ade adf adg adh adi adj adl adn ado adp adq adr ads adt adu adw adx ady adz ae aea aeb aec aed aee aek ael aem aen aeq aer aes aeu aew aey aez af afa afb afd afe afg afh afi afk afn afo afp afs aft afu afz aga agb agc agd age agf agg agh agi agj agk agl agm agn ago agp agq agr ags agt agu agv agw agx agy agz aha ahb ahg ahh ahi ahk ahl ahm ahn aho ahp ahr ahs aht aia aib aic aid aie aif aig aih aii aij aik ail aim ain aio aip aiq air ais ait aiw aix aiy aja ajg aji ajn ajp ajs ajt aju ajw ajz ak akb akc akd ake akf akg akh aki akj akk akl akm ako akp akq akr aks akt aku akv akw akx aky akz ala alc ald ale alf alg alh ali alj alk all alm aln alo alp alq alr als alt alu alv alw alx aly alz am ama amb amc ame amf amg ami amj amk aml amm amn amo amp amq amr ams amt amu amv amw amx amy amz an ana anb anc and ane anf ang anh ani anj ank anl anm ann ano anp anq anr ans ant anu anv anw anx any anz aoa aob aoc aod aoe aof aog aoh aoi aoj aok aol aom aon aor aos aot aou aox aoz apa apb apc apd ape apf apg aph api apj apk apl apm apn apo app apq apr aps apt apu apv apw apx apy apz aqa aqc aqd aqg aqk aql aqm aqn aqp aqr aqt aqz ar arb arc ard are arh ari arj ark arl arn aro arp arq arr ars art aru arv arw arx ary arz as asa asb asc asd ase asf asg ash asi asj ask asl asn aso asp asq asr ass ast asu asv asw asx asy asz ata atb atc atd ate atg ath ati atj atk atl atm atn ato atp atq atr ats att atu atv atw atx aty atz aua aub auc aud aue auf aug auh aui auj auk aul aum aun auo aup auq aur aus aut auu auw aux auy auz av avb avd avi avk avl avm avn avo avs avt avu avv awa awb awc awd awe awg awh awi awk awm awn awo awr aws awt awu awv aww awx awy axb axe axg axk axl axm axx ay aya ayb ayc ayd aye ayg ayh ayi ayk ayl ayn ayo ayp ayq ayr ays ayt ayu ayx ayy ayz az aza azb azc azd azg azj azm azn azo azt azz ba baa bab bac bad bae baf bag bah bai baj bal ban bao bap bar bas bat bau bav baw bax bay baz bba bbb bbc bbd bbe bbf bbg bbh bbi bbj bbk bbl bbm bbn bbo bbp bbq bbr bbs bbt bbu bbv bbw bbx bby bbz bca bcb bcc bcd bce bcf bcg bch bci bcj bck bcl bcm bcn bco bcp bcq bcr bcs bct bcu bcv bcw bcy bcz bda bdb bdc bdd bde bdf bdg bdh bdi bdj bdk bdl bdm bdn bdo bdp bdq bdr bds bdt bdu bdv bdw bdx bdy bdz be bea beb bec bed bee bef beg beh bei bej bek bem beo bep beq ber bes bet beu bev bew bex bey bez bfa bfb bfc bfd bfe bff bfg bfh bfi bfj bfk bfl bfm bfn bfo bfp bfq bfr bfs bft bfu bfw bfx bfy bfz bg bga bgb bgc bgd bge bgf bgg bgi bgj bgk bgl bgm bgn bgo bgp bgq bgr bgs bgt bgu bgv bgw bgx bgy bgz bh bha bhb bhc bhd bhe bhf bhg bhh bhi bhj bhk bhl bhm bhn bho bhp bhq bhr bhs bht bhu bhv bhw bhx bhy bhz bi bia bib bic bid bie bif big bij bik bil bim bin bio bip biq bir bit biu biv biw bix biy biz bja bjb bjc bjd bje bjf bjg bjh bji bjj bjk bjl bjm bjn bjo bjp bjq bjr bjs bjt bju bjv bjw bjx bjy bjz bka bkb bkc bkd bkf bkg bkh bki bkj bkk bkl bkm bkn bko bkp bkq bkr bks bkt bku bkv bkw bkx bky bkz bla blb blc bld ble blf blg blh bli blj blk bll blm bln blo blp blq blr bls blt blv blw blx bly blz bm bma bmb bmc bmd bme bmf bmg bmh bmi bmj bmk bml bmm bmn bmo bmp bmq bmr bms bmt bmu bmv bmw bmx bmy bmz bn bna bnb bnc bnd bne bnf bng bni bnj bnk bnl bnm bnn bno bnp bnq bnr bns bnt bnu bnv bnw bnx bny bnz bo boa bob boe bof bog boh boi boj bok bol bom bon boo bop boq bor bot bou bov bow box boy boz bpa bpb bpc bpd bpe bpg bph bpi bpj bpk bpl bpm bpn bpo bpp bpq bpr bps bpt bpu bpv bpw bpx bpy bpz bqa bqb bqc bqd bqf bqg bqh bqi bqj bqk bql bqm bqn bqo bqp bqq bqr bqs bqt bqu bqv bqw bqx bqy bqz br bra brb brc brd brf brg brh bri brj brk brl brm brn bro brp brq brr brs brt bru brv brw brx bry brz bs bsa bsb bsc bse bsf bsg bsh bsi bsj bsk bsl bsm bsn bso bsp bsq bsr bss bst bsu bsv bsw bsx bsy bta btb btc btd bte btf btg bth bti btj btk btl btm btn bto btp btq btr bts btt btu btv btw btx bty btz bua bub buc bud bue buf bug buh bui buj buk bum bun buo bup buq bus but buu buv buw bux buy buz bva bvb bvc bvd bve bvf bvg bvh bvi bvj bvk bvl bvm bvn bvo bvp bvq bvr bvt bvu bvv bvw bvx bvy bvz bwa bwb bwc bwd bwe bwf bwg bwh bwi bwj bwk bwl bwm bwn bwo bwp bwq bwr bws bwt bwu bww bwx bwy bwz bxa bxb bxc bxd bxe bxf bxg bxh bxi bxj bxk bxl bxm bxn bxo bxp bxq bxr bxs bxu bxv bxw bxx bxz bya byb byc byd bye byf byg byh byi byj byk byl bym byn byo byp byq byr bys byt byv byw byx byy byz bza bzb bzc bzd bze bzf bzg bzh bzi bzj bzk bzl bzm bzn bzo bzp bzq bzr bzs bzt bzu bzv bzw bzx bzy bzz ca caa cab cac cad cae caf cag cah cai caj cak cal cam can cao cap caq car cas cau cav caw cax cay caz cba cbb cbc cbd cbe cbg cbh cbi cbj cbk cbl cbn cbo cbq cbr cbs cbt cbu cbv cbw cby cca ccc ccd cce ccg cch ccj ccl ccm ccn cco ccp ccq ccr ccs cda cdc cdd cde cdf cdg cdh cdi cdj cdm cdn cdo cdr cds cdy cdz ce cea ceb ceg cek cel cen cet cey cfa cfd cfg cfm cga cgc cgg cgk ch chb chc chd chf chg chh chj chk chl chm chn cho chp chq chr cht chw chx chy chz cia cib cic cid cie cih cik cim cin cip cir ciw ciy cja cje cjh cji cjk cjm cjn cjo cjp cjr cjs cjv cjy cka ckb ckh ckl ckm ckn cko ckq ckr cks ckt cku ckv ckx cky ckz cla clc cld cle clh cli clj clk cll clm clo cls clt clu clw cly cma cmc cme cmg cmi cmk cml cmm cmn cmo cmr cms cmt cna cnb cnc cng cnh cni cnk cnl cno cnp cnq cnr cns cnt cnu cnw cnx co coa cob coc cod coe cof cog coh coj cok col com con coo cop coq cot cou cov cow cox coy coz cpa cpb cpc cpe cpf cpg cpi cpn cpo cpp cps cpu cpx cpy cqd cqu cr cra crb crc crd crf crg crh cri crj crk crl crm crn cro crp crq crr crs crt crv crw crx cry crz cs csa csb csc csd cse csf csg csh csi csj csk csl csm csn cso csp csq csr css cst csu csv csw csx csy csz cta ctc ctd cte ctg cth ctl ctm ctn cto ctp cts ctt ctu cty ctz cu cua cub cuc cug cuh cui cuj cuk cul cum cuo cup cuq cur cus cut cuu cuv cuw cux cuy cv cvg cvn cwa cwb cwd cwe cwg cwt cxh cy cya cyb cyo czh czk czn czo czt da daa dac dad dae daf dag dah dai daj dak dal dam dao dap daq dar das dau dav daw dax day daz dba dbb dbd dbe dbf dbg dbi dbj dbl dbm dbn dbo dbp dbq dbr dbt dbu dbv dbw dby dcc dcr dda ddd dde ddg ddi ddj ddn ddo ddr dds ddw de dec ded dee def deg deh dei dek del dem den dep deq der des dev dez dga dgb dgc dgd dge dgg dgh dgi dgk dgl dgn dgo dgr dgs dgt dgu dgw dgx dgz dha dhd dhg dhi dhl dhm dhn dho dhr dhs dhu dhv dhw dhx dia dib dic did dif dig dih dii dij dik dil dim din dio dip diq dir dis dit diu diw dix diy diz dja djb djc djd dje djf dji djj djk djl djm djn djo djr dju djw dka dkg dkk dkl dkr dks dkx dlg dlk dlm dln dma dmb dmc dmd dme dmf dmg dmk dml dmm dmn dmo dmr dms dmu dmv dmw dmx dmy dna dnd dne dng dni dnj dnk dnn dno dnr dnt dnu dnv dnw dny doa dob doc doe dof doh doi dok dol don doo dop doq dor dos dot dov dow dox doy doz dpp dra drb drc drd dre drg drh dri drl drn dro drq drr drs drt dru drw dry dsb dse dsh dsi dsk dsl dsn dso dsq dsz dta dtb dtd dth dti dtk dtm dtn dto dtp dtr dts dtt dtu dty dua dub duc dud due duf dug duh dui duj duk dul dum dun duo dup duq dur dus duu duv duw dux duy duz dv dva dwa dwk dwl dwr dws dwu dww dwy dwz dya dyb dyd dyg dyi dym dyn dyo dyr dyu dyy dz dza dzd dze dzg dzl dzn eaa ebc ebg ebk ebo ebr ebu ecr ecs ecy ee eee efa efe efi ega egl egm ego egx egy ehs ehu eip eit eiv eja eka ekc eke ekg eki ekk ekl ekm eko ekp ekr eky el ele elh eli elk elm elo elp elu elx ema emb eme emg emi emk emm emn emo emp emq ems emu emw emx emy emz en ena enb enc end enf enh enl enm enn eno enq enr enu env enw enx eo eot epi era erg erh eri erk ero err ers ert erw es ese esg esh esi esk esl esm esn eso esq ess esu esx esy et etb etc eth etn eto etr ets ett etu etx etz eu eud euq eve evh evn ewo ext eya eyo eza eze fa faa fab fad faf fag fah fai faj fak fal fam fan fap far fat fau fax fay faz fbl fcs fer ff ffi ffm fgr fi fia fie fif fil fip fir fit fiu fiw fj fkk fkv fla flh fli fll fln flr fly fmp fmu fnb fng fni fo fod foi fom fon for fos fox fpe fqs fr frc frd frk frm fro frp frq frr frs frt fse fsl fss fub fuc fud fue fuf fuh fui fuj fum fun fuq fur fut fuu fuv fuy fvr fwa fwe fy ga gaa gab gac gad gae gaf gag gah gai gaj gak gal gam gan gao gap gaq gar gas gat gau gav gaw gax gay gaz gba gbb gbc gbd gbe gbf gbg gbh gbi gbj gbk gbl gbm gbn gbo gbp gbq gbr gbs gbu gbv gbw gbx gby gbz gcc gcd gce gcf gcl gcn gcr gct gd gda gdb gdc gdd gde gdf gdg gdh gdi gdj gdk gdl gdm gdn gdo gdq gdr gds gdt gdu gdx gea geb gec ged gef geg geh gei gej gek gel gem geq ges gev gew gex gey gez gfk gft gfx gga ggb ggd gge ggg ggk ggl ggn ggo ggr ggt ggu ggw gha ghc ghe ghh ghk ghl ghn gho ghr ghs ght gia gib gic gid gie gig gih gii gil gim gin gio gip giq gir gis git giu giw gix giy giz gji gjk gjm gjn gjr gju gka gkd gke gkn gko gkp gku gl glb glc gld glh gli glj glk gll glo glr glu glw gly gma gmb gmd gme gmg gmh gml gmm gmn gmq gmr gmu gmv gmw gmx gmy gmz gn gna gnb gnc gnd gne gng gnh gni gnj gnk gnl gnm gnn gno gnq gnr gnt gnu gnw gnz goa gob goc god goe gof gog goh goi goj gok gol gom gon goo gop goq gor gos got gou gov gow gox goy goz gpa gpe gpn gqa gqi gqn gqr gqu gra grb grc grd grg grh gri grj grk grm gro grq grr grs grt gru grv grw grx gry grz gse gsg gsl gsm gsn gso gsp gss gsw gta gti gtu gu gua gub guc gud gue guf gug guh gui guk gul gum gun guo gup guq gur gus gut guu guv guw gux guz gv gva gvc gve gvf gvj gvl gvm gvn gvo gvp gvr gvs gvy gwa gwb gwc gwd gwe gwf gwg gwi gwj gwm gwn gwr gwt gwu gww gwx gxx gya gyb gyd gye gyf gyg gyi gyl gym gyn gyo gyr gyy gyz gza gzi gzn ha haa hab hac had hae haf hag hah hai haj hak hal ham han hao hap haq har has hav haw hax hay haz hba hbb hbn hbo hbu hca hch hdn hds hdy he hea hed heg heh hei hem hgm hgw hhi hhr hhy hi hia hib hid hif hig hih hii hij hik hil him hio hir hit hiw hix hji hka hke hkh hkk hkn hks hla hlb hld hle hlt hlu hma hmb hmc hmd hme hmf hmg hmh hmi hmj hmk hml hmm hmn hmp hmq hmr hms hmt hmu hmv hmw hmx hmy hmz hna hnd hne hng hnh hni hnj hnm hnn hno hns hnu ho hoa hob hoc hod hoe hoh hoi hoj hok hol hom hoo hop hor hos hot hov how hoy hoz hpo hps hr hra hrc hre hrk hrm hro hrp hrr hrt hru hrw hrx hrz hsb hsh hsl hsn hss ht hti hto hts htu htx hu hub huc hud hue huf hug huh hui huj huk hul hum huo hup huq hur hus hut huu huv huw hux huy huz hvc hve hvk hvn hvv hwa hwc hwo hy hya hyw hyx hz ia iai ian iap iar iba ibb ibd ibe ibg ibh ibi ibl ibm ibn ibr ibu iby ica ich icl icr id ida idb idc idd ide idi idr ids idt idu ie ifa ifb ife iff ifk ifm ifu ify ig igb ige igg igl igm ign igo igs igw ihb ihi ihp ihw ii iin iir ijc ije ijj ijn ijo ijs ik ike ikh iki ikk ikl iko ikp ikr iks ikt ikv ikw ikx ikz ila ilb ilg ili ilk ill ilm ilo ilp ils ilu ilv ilw ima ime imi iml imn imo imr ims imt imy in inb inc ine ing inh inj inl inm inn ino inp ins int inz io ior iou iow ipi ipo iqu iqw ira ire irh iri irk irn iro irr iru irx iry is isa isc isd ise isg ish isi isk ism isn iso isr ist isu isv it itb itc itd ite iti itk itl itm ito itr its itt itv itw itx ity itz iu ium ivb ivv iw iwk iwm iwo iws ixc ixl iya iyo iyx izh izi izm izr izz ja jaa jab jac jad jae jaf jah jaj jak jal jam jan jao jaq jar jas jat jau jax jay jaz jbe jbi jbj jbk jbm jbn jbo jbr jbt jbu jbw jcs jct jda jdg jdt jeb jee jeg jeh jei jek jel jen jer jet jeu jgb jge jgk jgo jhi jhs ji jia jib jic jid jie jig jih jii jil jim jio jiq jit jiu jiv jiy jje jjr jka jkm jko jkp jkr jks jku jle jls jma jmb jmc jmd jmi jml jmn jmr jms jmw jmx jna jnd jng jni jnj jnl jns job jod jog jor jos jow jpa jpr jpx jqr jra jrb jrr jrt jru jsl jua jub juc jud juh jui juk jul jum jun juo jup jur jus jut juu juw juy jv jvd jvn jw jwi jya jye jyy ka kaa kab kac kad kae kaf kag kah kai kaj kak kam kao kap kaq kar kav kaw kax kay kba kbb kbc kbd kbe kbf kbg kbh kbi kbj kbk kbl kbm kbn kbo kbp kbq kbr kbs kbt kbu kbv kbw kbx kby kbz kca kcb kcc kcd kce kcf kcg kch kci kcj kck kcl kcm kcn kco kcp kcq kcr kcs kct kcu kcv kcw kcx kcy kcz kda kdc kdd kde kdf kdg kdh kdi kdj kdk kdl kdm kdn kdo kdp kdq kdr kdt kdu kdv kdw kdx kdy kdz kea keb kec ked kee kef keg keh kei kej kek kel kem ken keo kep keq ker kes ket keu kev kew kex key kez kfa kfb kfc kfd kfe kff kfg kfh kfi kfj kfk kfl kfm kfn kfo kfp kfq kfr kfs kft kfu kfv kfw kfx kfy kfz kg kga kgb kgc kgd kge kgf kgg kgh kgi kgj kgk kgl kgm kgn kgo kgp kgq kgr kgs kgt kgu kgv kgw kgx kgy kha khb khc khd khe khf khg khh khi khj khk khl khn kho khp khq khr khs kht khu khv khw khx khy khz ki kia kib kic kid kie kif kig kih kii kij kil kim kio kip kiq kis kit kiu kiv kiw kix kiy kiz kj kja kjb kjc kjd kje kjf kjg kjh kji kjj kjk kjl kjm kjn kjo kjp kjq kjr kjs kjt kju kjv kjx kjy kjz kk kka kkb kkc kkd kke kkf kkg kkh kki kkj kkk kkl kkm kkn kko kkp kkq kkr kks kkt kku kkv kkw kkx kky kkz kl kla klb klc kld kle klf klg klh kli klj klk kll klm kln klo klp klq klr kls klt klu klv klw klx kly klz km kma kmb kmc kmd kme kmf kmg kmh kmi kmj kmk kml kmm kmn kmo kmp kmq kmr kms kmt kmu kmv kmw kmx kmy kmz kn kna knb knc knd kne knf kng kni knj knk knl knm knn kno knp knq knr kns knt knu knv knw knx kny knz ko koa koc kod koe kof kog koh koi koj kok kol koo kop koq kos kot kou kov kow kox koy koz kpa kpb kpc kpd kpe kpf kpg kph kpi kpj kpk kpl kpm kpn kpo kpp kpq kpr kps kpt kpu kpv kpw kpx kpy kpz kqa kqb kqc kqd kqe kqf kqg kqh kqi kqj kqk kql kqm kqn kqo kqp kqq kqr kqs kqt kqu kqv kqw kqx kqy kqz kr kra krb krc krd kre krf krh kri krj krk krl krm krn kro krp krr krs krt kru krv krw krx kry krz ks ksa ksb ksc ksd kse ksf ksg ksh ksi ksj ksk ksl ksm ksn kso ksp ksq ksr kss kst ksu ksv ksw ksx ksy ksz kta ktb ktc ktd kte ktf ktg kth kti ktj ktk ktl ktm ktn kto ktp ktq ktr kts ktt ktu ktv ktw ktx kty ktz ku kub kuc kud kue kuf kug kuh kui kuj kuk kul kum kun kuo kup kuq kus kut kuu kuv kuw kux kuy kuz kv kva kvb kvc kvd kve kvf kvg kvh kvi kvj kvk kvl kvm kvn kvo kvp kvq kvr kvs kvt kvu kvv kvw kvx kvy kvz kw kwa kwb kwc kwd kwe kwf kwg kwh kwi kwj kwk kwl kwm kwn kwo kwp kwq kwr kws kwt kwu kwv kww kwx kwy kwz kxa kxb kxc kxd kxe kxf kxh kxi kxj kxk kxl kxm kxn kxo kxp kxq kxr kxs kxt kxu kxv kxw kxx kxy kxz ky kya kyb kyc kyd kye kyf kyg kyh kyi kyj kyk kyl kym kyn kyo kyp kyq kyr kys kyt kyu kyv kyw kyx kyy kyz kza kzb kzc kzd kze kzf kzg kzh kzi kzj kzk kzl kzm kzn kzo kzp kzq kzr kzs kzt kzu kzv kzw kzx kzy kzz la laa lab lac lad lae laf lag lah lai laj lak lal lam lan lap laq lar las lau law lax lay laz lb lba lbb lbc lbe lbf lbg lbi lbj lbk lbl lbm lbn lbo lbq lbr lbs lbt lbu lbv lbw lbx lby lbz lcc lcd lce lcf lch lcl lcm lcp lcq lcs lda ldb ldd ldg ldh ldi ldj ldk ldl ldm ldn ldo ldp ldq lea leb lec led lee lef leg leh lei lej lek lel lem len leo lep leq ler les let leu lev lew lex ley lez lfa lfn lg lga lgb lgg lgh lgi lgk lgl lgm lgn lgo lgq lgr lgs lgt lgu lgz lha lhh lhi lhl lhm lhn lhp lhs lht lhu li lia lib lic lid lie lif lig lih lii lij lik lil lio lip liq lir lis liu liv liw lix liy liz lja lje lji ljl ljp ljw ljx lka lkb lkc lkd lke lkh lki lkj lkl lkm lkn lko lkr lks lkt lku lky lla llb llc lld lle llf llg llh lli llj llk lll llm lln llo llp llq lls llu llx lma lmb lmc lmd lme lmf lmg lmh lmi lmj lmk lml lmm lmn lmo lmp lmq lmr lmu lmv lmw lmx lmy lmz ln lna lnb lnd lng lnh lni lnj lnl lnm lnn lno lns lnu lnw lnz lo loa lob loc loe lof log loh loi loj lok lol lom lon loo lop loq lor los lot lou lov low lox loy loz lpa lpe lpn lpo lpx lqr lra lrc lre lrg lri lrk lrl lrm lrn lro lrr lrt lrv lrz lsa lsb lsc lsd lse lsg lsh lsi lsl lsm lsn lso lsp lsr lss lst lsv lsw lsy lt ltc ltg lth lti ltn lto lts ltu lu lua luc lud lue luf luh lui luj luk lul lum lun luo lup luq lur lus lut luu luv luw luy luz lv lva lvi lvk lvl lvs lvu lwa lwe lwg lwh lwl lwm lwo lws lwt lwu lww lxm lya lyg lyn lzh lzl lzn lzz maa mab mad mae maf mag mai maj mak mam man map maq mas mat mau mav maw max maz mba mbb mbc mbd mbe mbf mbh mbi mbj mbk mbl mbm mbn mbo mbp mbq mbr mbs mbt mbu mbv mbw mbx mby mbz mca mcb mcc mcd mce mcf mcg mch mci mcj mck mcl mcm mcn mco mcp mcq mcr mcs mct mcu mcv mcw mcx mcy mcz mda mdb mdc mdd mde mdf mdg mdh mdi mdj mdk mdl mdm mdn mdp mdq mdr mds mdt mdu mdv mdw mdx mdy mdz mea meb mec med mee mef meg meh mei mej mek mel mem men meo mep meq mer mes met meu mev mew mey mez mfa mfb mfc mfd mfe mff mfg mfh mfi mfj mfk mfl mfm mfn mfo mfp mfq mfr mfs mft mfu mfv mfw mfx mfy mfz mg mga mgb mgc mgd mge mgf mgg mgh mgi mgj mgk mgl mgm mgn mgo mgp mgq mgr mgs mgt mgu mgv mgw mgx mgy mgz mh mha mhb mhc mhd mhe mhf mhg mhh mhi mhj mhk mhl mhm mhn mho mhp mhq mhr mhs mht mhu mhw mhx mhy mhz mi mia mib mic mid mie mif mig mih mii mij mik mil mim min mio mip miq mir mis mit miu miw mix miy miz mja mjb mjc mjd mje mjg mjh mji mjj mjk mjl mjm mjn mjo mjp mjq mjr mjs mjt mju mjv mjw mjx mjy mjz mk mka mkb mkc mke mkf mkg mkh mki mkj mkk mkl mkm mkn mko mkp mkq mkr mks mkt mku mkv mkw mkx mky mkz ml mla mlb mlc mld mle mlf mlh mli mlj mlk mll mlm mln mlo mlp mlq mlr mls mlu mlv mlw mlx mlz mma mmb mmc mmd mme mmf mmg mmh mmi mmj mmk mml mmm mmn mmo mmp mmq mmr mmt mmu mmv mmw mmx mmy mmz mn mna mnb mnc mnd mne mnf mng mnh mni mnj mnk mnl mnm mnn mno mnp mnq mnr mns mnt mnu mnv mnw mnx mny mnz mo moa moc mod moe mof mog moh moi moj mok mom moo mop moq mor mos mot mou mov mow mox moy moz mpa mpb mpc mpd mpe mpg mph mpi mpj mpk mpl mpm mpn mpo mpp mpq mpr mps mpt mpu mpv mpw mpx mpy mpz mqa mqb mqc mqe mqf mqg mqh mqi mqj mqk mql mqm mqn mqo mqp mqq mqr mqs mqt mqu mqv mqw mqx mqy mqz mr mra mrb mrc mrd mre mrf mrg mrh mrj mrk mrl mrm mrn mro mrp mrq mrr mrs mrt mru mrv mrw mrx mry mrz ms msb msc msd mse msf msg msh msi msj msk msl msm msn mso msp msq msr mss mst msu msv msw msx msy msz mt mta mtb mtc mtd mte mtf mtg mth mti mtj mtk mtl mtm mtn mto mtp mtq mtr mts mtt mtu mtv mtw mtx mty mua mub muc mud mue mug muh mui muj muk mul mum mun muo mup muq mur mus mut muu muv mux muy muz mva mvb mvd mve mvf mvg mvh mvi mvk mvl mvm mvn mvo mvp mvq mvr mvs mvt mvu mvv mvw mvx mvy mvz mwa mwb mwc mwd mwe mwf mwg mwh mwi mwj mwk mwl mwm mwn mwo mwp mwq mwr mws mwt mwu mwv mww mwx mwy mwz mxa mxb mxc mxd mxe mxf mxg mxh mxi mxj mxk mxl mxm mxn mxo mxp mxq mxr mxs mxt mxu mxv mxw mxx mxy mxz my myb myc myd mye myf myg myh myi myj myk myl mym myn myo myp myq myr mys myt myu myv myw myx myy myz mza mzb mzc mzd mze mzg mzh mzi mzj mzk mzl mzm mzn mzo mzp mzq mzr mzs mzt mzu mzv mzw mzx mzy mzz na naa nab nac nad nae naf nag nah nai naj nak nal nam nan nao nap naq nar nas nat naw nax nay naz nb nba nbb nbc nbd nbe nbf nbg nbh nbi nbj nbk nbm nbn nbo nbp nbq nbr nbs nbt nbu nbv nbw nbx nby nca ncb ncc ncd nce ncf ncg nch nci ncj nck ncl ncm ncn nco ncp ncq ncr ncs nct ncu ncx ncz nd nda ndb ndc ndd ndf ndg ndh ndi ndj ndk ndl ndm ndn ndp ndq ndr nds ndt ndu ndv ndw ndx ndy ndz ne nea neb nec ned nee nef neg neh nei nej nek nem nen neo neq ner nes net neu nev new nex ney nez nfa nfd nfl nfr nfu ng nga ngb ngc ngd nge ngf ngg ngh ngi ngj ngk ngl ngm ngn ngo ngp ngq ngr ngs ngt ngu ngv ngw ngx ngy ngz nha nhb nhc nhd nhe nhf nhg nhh nhi nhk nhm nhn nho nhp nhq nhr nht nhu nhv nhw nhx nhy nhz nia nib nic nid nie nif nig nih nii nij nik nil nim nin nio niq nir nis nit niu niv niw nix niy niz nja njb njd njh nji njj njl njm njn njo njr njs njt nju njx njy njz nka nkb nkc nkd nke nkf nkg nkh nki nkj nkk nkm nkn nko nkp nkq nkr nks nkt nku nkv nkw nkx nkz nl nla nlc nle nlg nli nlj nlk nll nlm nln nlo nlq nlr nlu nlv nlw nlx nly nlz nma nmb nmc nmd nme nmf nmg nmh nmi nmj nmk nml nmm nmn nmo nmp nmq nmr nms nmt nmu nmv nmw nmx nmy nmz nn nna nnb nnc nnd nne nnf nng nnh nni nnj nnk nnl nnm nnn nnp nnq nnr nns nnt nnu nnv nnw nnx nny nnz no noa noc nod noe nof nog noh noi noj nok nol nom non noo nop noq nos not nou nov now noy noz npa npb npg nph npi npl npn npo nps npu npx npy nqg nqk nql nqm nqn nqo nqq nqt nqy nr nra nrb nrc nre nrf nrg nri nrk nrl nrm nrn nrp nrr nrt nru nrx nrz nsa nsb nsc nsd nse nsf nsg nsh nsi nsk nsl nsm nsn nso nsp nsq nsr nss nst nsu nsv nsw nsx nsy nsz ntd nte ntg nti ntj ntk ntm nto ntp ntr nts ntu ntw ntx nty ntz nua nub nuc nud nue nuf nug nuh nui nuj nuk nul num nun nuo nup nuq nur nus nut nuu nuv nuw nux nuy nuz nv nvh nvm nvo nwa nwb nwc nwe nwg nwi nwm nwo nwr nww nwx nwy nxa nxd nxe nxg nxi nxk nxl nxm nxn nxo nxq nxr nxu nxx ny nyb nyc nyd nye nyf nyg nyh nyi nyj nyk nyl nym nyn nyo nyp nyq nyr nys nyt nyu nyv nyw nyx nyy nza nzb nzd nzi nzk nzm nzr nzs nzu nzy nzz oaa oac oak oar oav obi obk obl obm obo obr obt obu oc oca och ocm oco ocu oda odk odt odu ofo ofs ofu ogb ogc oge ogg ogo ogu oht ohu oia oie oin oj ojb ojc ojg ojp ojs ojv ojw oka okb okc okd oke okg okh oki okj okk okl okm okn oko okr oks oku okv okx okz ola old ole olk olm olo olr olt olu om oma omb omc ome omg omi omk oml omn omo omp omq omr omt omu omv omw omx omy ona onb one ong oni onj onk onn ono onp onr ons ont onu onw onx ood oog oon oor oos opa opk opm opo opt opy or ora orc ore org orh orn oro orr ors ort oru orv orw orx ory orz os osa osc osi osn oso osp ost osu osx ota otb otd ote oti otk otl otm otn oto otq otr ots ott otu otw otx oty otz oua oub oue oui oum oun ovd owi owl oyb oyd oym oyy ozm pa paa pab pac pad pae paf pag pah pai pak pal pam pao pap paq par pas pat pau pav paw pax pay paz pbb pbc pbe pbf pbg pbh pbi pbl pbm pbn pbo pbp pbr pbs pbt pbu pbv pby pbz pca pcb pcc pcd pce pcf pcg pch pci pcj pck pcl pcm pcn pcp pcr pcw pda pdc pdi pdn pdo pdt pdu pea peb ped pee pef peg peh pei pej pek pel pem peo pep peq pes pev pex pey pez pfa pfe pfl pga pgd pgg pgi pgk pgl pgn pgs pgu pgy pgz pha phd phg phh phi phj phk phl phm phn pho phq phr pht phu phv phw pi pia pib pic pid pie pif pig pih pii pij pil pim pin pio pip pir pis pit piu piv piw pix piy piz pjt pka pkb pkc pkg pkh pkn pko pkp pkr pks pkt pku pl pla plb plc pld ple plf plg plh plj plk pll pln plo plp plq plr pls plt plu plv plw ply plz pma pmb pmc pmd pme pmf pmh pmi pmj pmk pml pmm pmn pmo pmq pmr pms pmt pmu pmw pmx pmy pmz pna pnb pnc pnd pne png pnh pni pnj pnk pnl pnm pnn pno pnp pnq pnr pns pnt pnu pnv pnw pnx pny pnz poc pod poe pof pog poh poi pok pom pon poo pop poq pos pot pov pow pox poy poz ppa ppe ppi ppk ppl ppm ppn ppo ppp ppq ppr pps ppt ppu pqa pqe pqm pqw pra prb prc prd pre prf prg prh pri prk prl prm prn pro prp prq prr prs prt pru prw prx pry prz ps psa psc psd pse psg psh psi psl psm psn pso psp psq psr pss pst psu psw psy pt pta pth pti ptn pto ptp ptq ptr ptt ptu ptv ptw pty pua pub puc pud pue puf pug pui puj puk pum puo pup puq pur put puu puw pux puy puz pwa pwb pwg pwi pwm pwn pwo pwr pww pxm pye pym pyn pys pyu pyx pyy pze pzh pzn qaa..qtz qu qua qub quc qud quf qug quh qui quk qul qum qun qup quq qur qus quv quw qux quy quz qva qvc qve qvh qvi qvj qvl qvm qvn qvo qvp qvs qvw qvy qvz qwa qwc qwe qwh qwm qws qwt qxa qxc qxh qxl qxn qxo qxp qxq qxr qxs qxt qxu qxw qya qyp raa rab rac rad raf rag rah rai raj rak ral ram ran rao rap raq rar ras rat rau rav raw rax ray raz rbb rbk rbl rbp rcf rdb rea reb ree reg rei rej rel rem ren rer res ret rey rga rge rgk rgn rgr rgs rgu rhg rhp ria rib rie rif ril rim rin rir rit riu rjg rji rjs rka rkb rkh rki rkm rkt rkw rm rma rmb rmc rmd rme rmf rmg rmh rmi rmk rml rmm rmn rmo rmp rmq rmr rms rmt rmu rmv rmw rmx rmy rmz rn rna rnb rnd rng rnl rnn rnp rnr rnw ro roa rob roc rod roe rof rog rol rom roo rop ror rou row rpn rpt rri rrm rro rrt rsb rsi rsk rsl rsm rsn rsw rtc rth rtm rts rtw ru rub ruc rue ruf rug ruh rui ruk ruo rup ruq rut ruu ruy ruz rw rwa rwk rwl rwm rwo rwr rxd rxw ryn rys ryu rzh sa saa sab sac sad sae saf sah sai saj sak sal sam sao sap saq sar sas sat sau sav saw sax say saz sba sbb sbc sbd sbe sbf sbg sbh sbi sbj sbk sbl sbm sbn sbo sbp sbq sbr sbs sbt sbu sbv sbw sbx sby sbz sc sca scb sce scf scg sch sci sck scl scn sco scp scq scs sct scu scv scw scx sd sda sdb sdc sde sdf sdg sdh sdj sdk sdl sdm sdn sdo sdp sdq sdr sds sdt sdu sdv sdx sdz se sea seb sec sed see sef seg seh sei sej sek sel sem sen seo sep seq ser ses set seu sev sew sey sez sfb sfe sfm sfs sfw sg sga sgb sgc sgd sge sgg sgh sgi sgj sgk sgl sgm sgn sgo sgp sgr sgs sgt sgu sgw sgx sgy sgz sh sha shb shc shd she shg shh shi shj shk shl shm shn sho shp shq shr shs sht shu shv shw shx shy shz si sia sib sid sie sif sig sih sii sij sik sil sim sio sip siq sir sis sit siu siv siw six siy siz sja sjb sjc sjd sje sjg sjk sjl sjm sjn sjo sjp sjr sjs sjt sju sjw sk ska skb skc skd ske skf skg skh ski skj skk skm skn sko skp skq skr sks skt sku skv skw skx sky skz sl sla slc sld sle slf slg slh sli slj sll slm sln slp slq slr sls slt slu slw slx sly slz sm sma smb smc smd smf smg smh smi smj smk sml smm smn smp smq smr sms smt smu smv smw smx smy smz sn snb snc sne snf sng snh sni snj snk snl snm snn sno snp snq snr sns snu snv snw snx sny snz so soa sob soc sod soe sog soh soi soj sok sol son soo sop soq sor sos sou sov sow sox soy soz spb spc spd spe spg spi spk spl spm spn spo spp spq spr sps spt spu spv spx spy sq sqa sqh sqj sqk sqm sqn sqo sqq sqr sqs sqt squ sqx sr sra srb src sre srf srg srh sri srk srl srm srn sro srq srr srs srt sru srv srw srx sry srz ss ssa ssb ssc ssd sse ssf ssg ssh ssi ssj ssk ssl ssm ssn sso ssp ssq ssr sss sst ssu ssv ssx ssy ssz st sta stb std ste stf stg sth sti stj stk stl stm stn sto stp stq str sts stt stu stv stw sty su sua sub suc sue sug sui suj suk sul sum suo suq sur sus sut suv suw sux suy suz sv sva svb svc sve svk svm svr svs svx sw swb swc swf swg swh swi swj swk swl swm swn swo swp swq swr sws swt swu swv sww swx swy sxb sxc sxe sxg sxk sxl sxm sxn sxo sxr sxs sxu sxw sya syb syc syd syi syk syl sym syn syo syr sys syw syx syy sza szb szc szd sze szg szl szn szp szs szv szw szy ta taa tab tac tad tae taf tag tai taj tak tal tan tao tap taq tar tas tau tav taw tax tay taz tba tbb tbc tbd tbe tbf tbg tbh tbi tbj tbk tbl tbm tbn tbo tbp tbq tbr tbs tbt tbu tbv tbw tbx tby tbz tca tcb tcc tcd tce tcf tcg tch tci tck tcl tcm tcn tco tcp tcq tcs tct tcu tcw tcx tcy tcz tda tdb tdc tdd tde tdf tdg tdh tdi tdj tdk tdl tdm tdn tdo tdq tdr tds tdt tdu tdv tdx tdy te tea teb tec ted tee tef teg teh tei tek tem ten teo tep teq ter tes tet teu tev tew tex tey tez tfi tfn tfo tfr tft tg tga tgb tgc tgd tge tgf tgg tgh tgi tgj tgn tgo tgp tgq tgr tgs tgt tgu tgv tgw tgx tgy tgz th thc thd the thf thh thi thk thl thm thn thp thq thr ths tht thu thv thw thx thy thz ti tia tic tid tie tif tig tih tii tij tik til tim tin tio tip tiq tis tit tiu tiv tiw tix tiy tiz tja tjg tji tjj tjl tjm tjn tjo tjp tjs tju tjw tk tka tkb tkd tke tkf tkg tkk tkl tkm tkn tkp tkq tkr tks tkt tku tkv tkw tkx tkz tl tla tlb tlc tld tlf tlg tlh tli tlj tlk tll tlm tln tlo tlp tlq tlr tls tlt tlu tlv tlw tlx tly tma tmb tmc tmd tme tmf tmg tmh tmi tmj tmk tml tmm tmn tmo tmp tmq tmr tms tmt tmu tmv tmw tmy tmz tn tna tnb tnc tnd tne tnf tng tnh tni tnk tnl tnm tnn tno tnp tnq tnr tns tnt tnu tnv tnw tnx tny tnz to tob toc tod toe tof tog toh toi toj tok tol tom too top toq tor tos tou tov tow tox toy toz tpa tpc tpe tpf tpg tpi tpj tpk tpl tpm tpn tpo tpp tpq tpr tpt tpu tpv tpw tpx tpy tpz tqb tql tqm tqn tqo tqp tqq tqr tqt tqu tqw tr tra trb trc trd tre trf trg trh tri trj trk trl trm trn tro trp trq trr trs trt tru trv trw trx try trz ts tsa tsb tsc tsd tse tsf tsg tsh tsi tsj tsk tsl tsm tsp tsq tsr tss tst tsu tsv tsw tsx tsy tsz tt tta ttb ttc ttd tte ttf ttg tth tti ttj ttk ttl ttm ttn tto ttp ttq ttr tts ttt ttu ttv ttw tty ttz tua tub tuc tud tue tuf tug tuh tui tuj tul tum tun tuo tup tuq tus tut tuu tuv tuw tux tuy tuz tva tvd tve tvi tvk tvl tvm tvn tvo tvs tvt tvu tvw tvx tvy tw twa twb twc twd twe twf twg twh twl twm twn two twp twq twr twt twu tww twx twy txa txb txc txe txg txh txi txj txm txn txo txq txr txs txt txu txx txy ty tya tye tyh tyi tyj tyl tyn typ tyr tys tyt tyu tyv tyx tyy tyz tza tzh tzj tzl tzm tzn tzo tzx uam uan uar uba ubi ubl ubr ubu uby uda ude udg udi udj udl udm udu ues ufi ug uga ugb uge ugh ugn ugo ugy uha uhn uis uiv uji uk uka ukg ukh uki ukk ukl ukp ukq uks uku ukv ukw uky ula ulb ulc ule ulf uli ulk ull ulm uln ulu ulw uly uma umb umc umd umg umi umm umn umo ump umr ums umu una und une ung uni unk unm unn unp unr unu unx unz uok uon upi upv ur ura urb urc ure urf urg urh uri urj urk url urm urn uro urp urr urt uru urv urw urx ury urz usa ush usi usk usp uss usu uta ute uth utp utr utu uum uun uur uuu uve uvh uvl uwa uya uz uzn uzs vaa vae vaf vag vah vai vaj val vam van vao vap var vas vau vav vay vbb vbk ve vec ved vel vem veo vep ver vgr vgt vi vic vid vif vig vil vin vis vit viv vjk vka vki vkj vkk vkl vkm vkn vko vkp vkt vku vkz vlp vls vma vmb vmc vmd vme vmf vmg vmh vmi vmj vmk vml vmm vmp vmq vmr vms vmu vmv vmw vmx vmy vmz vnk vnm vnp vo vor vot vra vro vrs vrt vsi vsl vsn vsv vto vum vun vut vwa wa waa wab wac wad wae waf wag wah wai waj wak wal wam wan wao wap waq war was wat wau wav waw wax way waz wba wbb wbe wbf wbh wbi wbj wbk wbl wbm wbp wbq wbr wbs wbt wbv wbw wca wci wdd wdg wdj wdk wdt wdu wdy wea wec wed weg weh wei wem wen weo wep wer wes wet weu wew wfg wga wgb wgg wgi wgo wgu wgw wgy wha whg whk whu wib wic wie wif wig wih wii wij wik wil wim win wir wit wiu wiv wiw wiy wja wji wka wkb wkd wkl wkr wku wkw wky wla wlc wle wlg wlh wli wlk wll wlm wlo wlr wls wlu wlv wlw wlx wly wma wmb wmc wmd wme wmg wmh wmi wmm wmn wmo wms wmt wmw wmx wnb wnc wnd wne wng wni wnk wnm wnn wno wnp wnu wnw wny wo woa wob woc wod woe wof wog woi wok wom won woo wor wos wow woy wpc wra wrb wrd wrg wrh wri wrk wrl wrm wrn wro wrp wrr wrs wru wrv wrw wrx wry wrz wsa wsg wsi wsk wsr wss wsu wsv wtb wtf wth wti wtk wtm wtw wua wub wud wuh wul wum wun wur wut wuu wuv wux wuy wwa wwb wwo wwr www wxa wxw wya wyb wyi wym wyn wyr wyy xaa xab xac xad xae xag xai xaj xak xal xam xan xao xap xaq xar xas xat xau xav xaw xay xba xbb xbc xbd xbe xbg xbi xbj xbm xbn xbo xbp xbr xbw xbx xby xcb xcc xce xcg xch xcl xcm xcn xco xcr xct xcu xcv xcw xcy xda xdc xdk xdm xdo xdq xdy xeb xed xeg xel xem xep xer xes xet xeu xfa xga xgb xgd xgf xgg xgi xgl xgm xgn xgr xgu xgw xh xha xhc xhd xhe xhm xhr xht xhu xhv xia xib xii xil xin xip xir xis xiv xiy xjb xjt xka xkb xkc xkd xke xkf xkg xkh xki xkj xkk xkl xkn xko xkp xkq xkr xks xkt xku xkv xkw xkx xky xkz xla xlb xlc xld xle xlg xli xln xlo xlp xls xlu xly xma xmb xmc xmd xme xmf xmg xmh xmj xmk xml xmm xmn xmo xmp xmq xmr xms xmt xmu xmv xmw xmx xmy xmz xna xnb xnd xng xnh xni xnj xnk xnm xnn xno xnq xnr xns xnt xnu xny xnz xoc xod xog xoi xok xom xon xoo xop xor xow xpa xpb xpc xpd xpe xpf xpg xph xpi xpj xpk xpl xpm xpn xpo xpp xpq xpr xps xpt xpu xpv xpw xpx xpy xpz xqa xqt xra xrb xrd xre xrg xri xrm xrn xrq xrr xrt xru xrw xsa xsb xsc xsd xse xsh xsi xsj xsl xsm xsn xso xsp xsq xsr xss xsu xsv xsy xta xtb xtc xtd xte xtg xth xti xtj xtl xtm xtn xto xtp xtq xtr xts xtt xtu xtv xtw xty xtz xua xub xud xug xuj xul xum xun xuo xup xur xut xuu xve xvi xvn xvo xvs xwa xwc xwd xwe xwg xwj xwk xwl xwo xwr xwt xww xxb xxk xxm xxr xxt xya xyb xyj xyk xyl xyt xyy xzh xzm xzp yaa yab yac yad yae yaf yag yah yai yaj yak yal yam yan yao yap yaq yar yas yat yau yav yaw yax yay yaz yba ybb ybd ybe ybh ybi ybj ybk ybl ybm ybn ybo ybx yby ych ycl ycn ycp ycr yda ydd yde ydg ydk yds yea yec yee yei yej yel yen yer yes yet yeu yev yey yga ygi ygl ygm ygp ygr ygs ygu ygw yha yhd yhl yhs yi yia yif yig yih yii yij yik yil yim yin yip yiq yir yis yit yiu yiv yix yiy yiz yka ykg ykh yki ykk ykl ykm ykn yko ykr ykt yku yky yla ylb yle ylg yli yll ylm yln ylo ylr ylu yly yma ymb ymc ymd yme ymg ymh ymi ymk yml ymm ymn ymo ymp ymq ymr yms ymt ymx ymz yna ynb ynd yne yng ynh ynk ynl ynn yno ynq yns ynu yo yob yog yoi yok yol yom yon yos yot yox yoy ypa ypb ypg yph ypk ypm ypn ypo ypp ypz yra yrb yre yri yrk yrl yrm yrn yro yrs yrw yry ysc ysd ysg ysl ysm ysn yso ysp ysr yss ysy yta ytl ytp ytw yty yua yub yuc yud yue yuf yug yui yuj yuk yul yum yun yup yuq yur yut yuu yuw yux yuy yuz yva yvt ywa ywg ywl ywn ywq ywr ywt ywu yww yxa yxg yxl yxm yxu yxy yyr yyu yyz yzg yzk za zaa zab zac zad zae zaf zag zah zai zaj zak zal zam zao zap zaq zar zas zat zau zav zaw zax zay zaz zba zbc zbe zbl zbt zbu zbw zca zcd zch zdj zea zeg zeh zem zen zga zgb zgh zgm zgn zgr zh zhb zhd zhi zhn zhw zhx zia zib zik zil zim zin zir ziw ziz zka zkb zkd zkg zkh zkk zkn zko zkp zkr zkt zku zkv zkz zla zle zlj zlm zln zlq zls zlu zlw zma zmb zmc zmd zme zmf zmg zmh zmi zmj zmk zml zmm zmn zmo zmp zmq zmr zms zmt zmu zmv zmw zmx zmy zmz zna znd zne zng znk zns zoc zoh zom zoo zoq zor zos zpa zpb zpc zpd zpe zpf zpg zph zpi zpj zpk zpl zpm zpn zpo zpp zpq zpr zps zpt zpu zpv zpw zpx zpy zpz zqe zra zrg zrn zro zrp zrs zsa zsk zsl zsm zsr zsu zte ztg ztl ztm ztn ztp ztq zts ztt ztu ztx zty zu zua zuh zum zun zuy zwa zxx zyb zyg zyj zyn zyp zza zzj';
|
|
33968
|
+
// </generated:language-subtags>
|
|
33969
|
+
|
|
33970
|
+
// BCP 47 well-formedness plus a registry check on the primary subtag. Shape
|
|
33971
|
+
// alone accepts "eng" and "em-US", which look like language tags but are not
|
|
33972
|
+
// registered: the IANA registry lists a three-letter subtag only when no
|
|
33973
|
+
// two-letter one exists, so "en" is registered and "eng" is not.
|
|
33974
|
+
let __languageSubtagSet = null;
|
|
33975
|
+
function isRegisteredLanguageSubtag(subtag) {
|
|
33976
|
+
if (!__languageSubtagSet) __languageSubtagSet = new Set(LANGUAGE_SUBTAGS.split(' '));
|
|
33977
|
+
return __languageSubtagSet.has(String(subtag || '').toLowerCase());
|
|
33978
|
+
}
|
|
33979
|
+
|
|
33980
|
+
function isValidLanguageTag(value) {
|
|
33981
|
+
const raw = String(value == null ? '' : value).trim();
|
|
33982
|
+
if (!raw) return false;
|
|
33983
|
+
if (!/^[a-zA-Z]{2,3}(-[a-zA-Z0-9]{1,8})*$/.test(raw)) return false;
|
|
33984
|
+
return isRegisteredLanguageSubtag(raw.split('-')[0]);
|
|
33985
|
+
}
|
|
33986
|
+
|
|
32229
33987
|
const document = opts && opts.document ? opts.document : null;
|
|
32230
33988
|
const window = opts && opts.window ? opts.window : null;
|
|
32231
33989
|
// Some engine paths may not pass opts.window; recover it from document when possible.
|
|
@@ -34934,6 +36692,23 @@ const createDomHelpers = (function createDomHelpers(opts) {
|
|
|
34934
36692
|
return { present: !!value, value, mechanism: 'label', flags: value ? [] : ['empty'] };
|
|
34935
36693
|
}
|
|
34936
36694
|
|
|
36695
|
+
// ACT scopes every accessible-name rule to elements "included in the
|
|
36696
|
+
// accessibility tree" (c487ae link-name, 97a4e1 button-name and siblings),
|
|
36697
|
+
// and its glossary puts focusable aria-hidden content outside that set:
|
|
36698
|
+
// "Because they are hidden, these elements are considered not included in
|
|
36699
|
+
// the accessibility tree", even where a browser leaves them in it. The
|
|
36700
|
+
// defect they do represent, aria-hidden over content in the focus order,
|
|
36701
|
+
// belongs to aria-hidden-focus (ACT 6cfa84, WCAG 4.1.2).
|
|
36702
|
+
//
|
|
36703
|
+
// isAccTreeEligible keeps them eligible on purpose so aria-hidden-focus can
|
|
36704
|
+
// reach them, so naming rules need this narrower question instead.
|
|
36705
|
+
function isIncludedInAccessibilityTree(el) {
|
|
36706
|
+
const r = isAccTreeEligible(el);
|
|
36707
|
+
if (!r || !r.eligible) return false;
|
|
36708
|
+
const reasons = Array.isArray(r.reasons) ? r.reasons : [];
|
|
36709
|
+
return !reasons.some((x) => typeof x === 'string' && x.indexOf('ariaHiddenOverridden') === 0);
|
|
36710
|
+
}
|
|
36711
|
+
|
|
34937
36712
|
function getAccessibleNameInfo(el, _ctx, opts) {
|
|
34938
36713
|
const flags = [];
|
|
34939
36714
|
if (!isElement(el))
|
|
@@ -35481,13 +37256,51 @@ const createDomHelpers = (function createDomHelpers(opts) {
|
|
|
35481
37256
|
}
|
|
35482
37257
|
|
|
35483
37258
|
const ownName = getAccessibleNameInfo(node, _ctx, opts);
|
|
35484
|
-
|
|
37259
|
+
// A descendant's own name outranks its content for real naming
|
|
37260
|
+
// mechanisms (aria-label/aria-labelledby, a native <label>), but not for
|
|
37261
|
+
// a bare title: accname orders name-from-content ahead of the title
|
|
37262
|
+
// fallback, so a descendant's title may only speak for it when its
|
|
37263
|
+
// content is empty. Same precedence the image-like branch above applies
|
|
37264
|
+
// to alt; without it, a tooltip silently replaces the visible text of
|
|
37265
|
+
// anything named from its content.
|
|
37266
|
+
const titleOnlyName = !!(
|
|
37267
|
+
ownName &&
|
|
37268
|
+
ownName.present &&
|
|
37269
|
+
ownName.value &&
|
|
37270
|
+
ownName.mechanism === 'title'
|
|
37271
|
+
);
|
|
37272
|
+
|
|
37273
|
+
if (ownName && ownName.present && ownName.value && !titleOnlyName) {
|
|
35485
37274
|
parts.push(ownName.value);
|
|
35486
37275
|
const tag = `descendant-name-used:${ownName.mechanism || 'unknown'}`;
|
|
35487
37276
|
if (flags.indexOf(tag) === -1) flags.push(tag);
|
|
35488
37277
|
return; // this descendant speaks for itself; don't also use its content
|
|
35489
37278
|
}
|
|
35490
37279
|
|
|
37280
|
+
if (titleOnlyName) {
|
|
37281
|
+
// Content first; keep the title only if the content contributed
|
|
37282
|
+
// nothing. Compared on the trimmed join rather than parts.length,
|
|
37283
|
+
// since a whitespace-only text node pushes a part but no name text.
|
|
37284
|
+
const before = parts.length;
|
|
37285
|
+
walkChildren(node, parts);
|
|
37286
|
+
if (!trim(parts.slice(before).join(' '))) {
|
|
37287
|
+
parts.length = before;
|
|
37288
|
+
parts.push(ownName.value);
|
|
37289
|
+
if (flags.indexOf('descendant-name-used:title-fallback') === -1)
|
|
37290
|
+
flags.push('descendant-name-used:title-fallback');
|
|
37291
|
+
} else if (flags.indexOf('descendant-title-superseded-by-content') === -1) {
|
|
37292
|
+
flags.push('descendant-title-superseded-by-content');
|
|
37293
|
+
}
|
|
37294
|
+
return;
|
|
37295
|
+
}
|
|
37296
|
+
|
|
37297
|
+
walkChildren(node, parts);
|
|
37298
|
+
}
|
|
37299
|
+
|
|
37300
|
+
// Extracted from collect() so the title-only branch above can walk a
|
|
37301
|
+
// descendant's children and then decide whether the title was needed,
|
|
37302
|
+
// without duplicating the <slot> handling.
|
|
37303
|
+
function walkChildren(node, parts) {
|
|
35491
37304
|
// A <slot>'s own childNodes are its FALLBACK content only —
|
|
35492
37305
|
// rendered solely when nothing is assigned to it. When real content
|
|
35493
37306
|
// IS distributed into it, that's what's exposed to the accessibility
|
|
@@ -36534,6 +38347,9 @@ const createDomHelpers = (function createDomHelpers(opts) {
|
|
|
36534
38347
|
}
|
|
36535
38348
|
|
|
36536
38349
|
return {
|
|
38350
|
+
isValidLanguageTag,
|
|
38351
|
+
isRegisteredLanguageSubtag,
|
|
38352
|
+
|
|
36537
38353
|
// Existing query/snippet utilities
|
|
36538
38354
|
queryAll,
|
|
36539
38355
|
queryAllDeep,
|
|
@@ -36547,6 +38363,7 @@ const createDomHelpers = (function createDomHelpers(opts) {
|
|
|
36547
38363
|
hasAccessibleName,
|
|
36548
38364
|
isExcluded,
|
|
36549
38365
|
isAccTreeEligible,
|
|
38366
|
+
isIncludedInAccessibilityTree,
|
|
36550
38367
|
isDomVisibleEligible,
|
|
36551
38368
|
isWholeDocumentScope,
|
|
36552
38369
|
|
|
@@ -36603,6 +38420,12 @@ const createDomHelpers = (function createDomHelpers(opts) {
|
|
|
36603
38420
|
getLabelMethod,
|
|
36604
38421
|
getLabelStrength,
|
|
36605
38422
|
|
|
38423
|
+
// Whether a <label> carries text that names its associated control
|
|
38424
|
+
// (own aria-name, else rendered content, else title). Shared so
|
|
38425
|
+
// form-control-single-label and form-control-programmatic-label-present
|
|
38426
|
+
// agree on what a label is worth.
|
|
38427
|
+
labelContributesAccessibleName,
|
|
38428
|
+
|
|
36606
38429
|
// Flat-tree ancestor walk (assignedSlot-aware, then shadow host) —
|
|
36607
38430
|
// see this function's own definition above for why assignedSlot
|
|
36608
38431
|
// must win over parentNode.
|