Skip to content

Instantly share code, notes, and snippets.

@akornmeier
Last active May 2, 2017 17:55
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save akornmeier/67c0423f21f8b04edb5e55a711b263c0 to your computer and use it in GitHub Desktop.
Save akornmeier/67c0423f21f8b04edb5e55a711b263c0 to your computer and use it in GitHub Desktop.
Choice Hotels Custom Rules
{
"rules": [
{
"id": "accesskeys",
"selector": "[accesskey]",
"excludeHidden": false,
"enabled": true,
"pageLevel": false,
"any": [],
"all": [],
"none": [
"accesskeys"
],
"tags": [
"wcag2a",
"wcag211"
]
},
{
"id": "area-alt",
"selector": "map area[href]",
"excludeHidden": false,
"enabled": true,
"pageLevel": false,
"any": [
"non-empty-alt",
"non-empty-title",
"aria-label",
"aria-labelledby"
],
"all": [],
"none": [],
"tags": [
"wcag2a",
"wcag111",
"section508",
"section508.22.a"
]
},
{
"id": "aria-allowed-attr",
"selector": "*",
"excludeHidden": true,
"enabled": true,
"pageLevel": false,
"any": [
"aria-allowed-attr"
],
"all": [],
"none": [],
"tags": [
"wcag2a",
"wcag411",
"wcag412"
],
"matches": "function matches(node) {\n var role = node.getAttribute('role');\n if (!role) {\n role = axe.commons.aria.implicitRole(node);\n }\n var allowed = axe.commons.aria.allowedAttr(role);\n if (role && allowed) {\n var aria = /^aria-/;\n if (node.hasAttributes()) {\n var attrs = node.attributes;\n for (var i = 0, l = attrs.length; i < l; i++) {\n if (aria.test(attrs[i].name)) {\n return true;\n }\n }\n }\n }\n return false;\n }"
},
{
"id": "aria-required-attr",
"selector": "[role]",
"excludeHidden": true,
"enabled": true,
"pageLevel": false,
"any": [
"aria-required-attr"
],
"all": [],
"none": [],
"tags": [
"wcag2a",
"wcag411",
"wcag412"
]
},
{
"id": "aria-required-children",
"selector": "[role]",
"excludeHidden": true,
"enabled": true,
"pageLevel": false,
"any": [
"aria-required-children"
],
"all": [],
"none": [],
"tags": [
"wcag2a",
"wcag131"
]
},
{
"id": "aria-required-parent",
"selector": "[role]",
"excludeHidden": true,
"enabled": true,
"pageLevel": false,
"any": [
"aria-required-parent"
],
"all": [],
"none": [],
"tags": [
"wcag2a",
"wcag131"
]
},
{
"id": "aria-roles",
"selector": "[role]",
"excludeHidden": true,
"enabled": true,
"pageLevel": false,
"any": [],
"all": [],
"none": [
"invalidrole",
"abstractrole"
],
"tags": [
"wcag2a",
"wcag131",
"wcag411",
"wcag412"
]
},
{
"id": "aria-valid-attr-value",
"selector": "*",
"excludeHidden": true,
"enabled": true,
"pageLevel": false,
"any": [
{
"options": [],
"id": "aria-valid-attr-value"
}
],
"all": [],
"none": [],
"tags": [
"wcag2a",
"wcag131",
"wcag411",
"wcag412"
],
"matches": "function matches(node) {\n var aria = /^aria-/;\n if (node.hasAttributes()) {\n var attrs = node.attributes;\n for (var i = 0, l = attrs.length; i < l; i++) {\n if (aria.test(attrs[i].name)) {\n return true;\n }\n }\n }\n return false;\n }"
},
{
"id": "aria-valid-attr",
"selector": "*",
"excludeHidden": true,
"enabled": true,
"pageLevel": false,
"any": [
{
"options": [],
"id": "aria-valid-attr"
}
],
"all": [],
"none": [],
"tags": [
"wcag2a",
"wcag411"
],
"matches": "function matches(node) {\n var aria = /^aria-/;\n if (node.hasAttributes()) {\n var attrs = node.attributes;\n for (var i = 0, l = attrs.length; i < l; i++) {\n if (aria.test(attrs[i].name)) {\n return true;\n }\n }\n }\n return false;\n }"
},
{
"id": "audio-caption",
"selector": "audio",
"excludeHidden": false,
"enabled": true,
"pageLevel": false,
"any": [],
"all": [],
"none": [
"caption"
],
"tags": [
"wcag2a",
"wcag122",
"section508",
"section508.22.a"
]
},
{
"id": "blink",
"selector": "blink",
"excludeHidden": false,
"enabled": true,
"pageLevel": false,
"any": [],
"all": [],
"none": [
"is-on-screen"
],
"tags": [
"wcag2a",
"wcag222",
"section508",
"section508.22.j"
]
},
{
"id": "button-name",
"selector": "button, [role=\"button\"], input[type=\"button\"], input[type=\"submit\"], input[type=\"reset\"]",
"excludeHidden": true,
"enabled": true,
"pageLevel": false,
"any": [
"non-empty-if-present",
"non-empty-value",
"button-has-visible-text",
"aria-label",
"aria-labelledby",
"role-presentation",
"role-none"
],
"all": [],
"none": [
"focusable-no-name"
],
"tags": [
"wcag2a",
"wcag412",
"section508",
"section508.22.a"
]
},
{
"id": "bypass",
"selector": "html",
"excludeHidden": true,
"enabled": true,
"pageLevel": true,
"any": [
"internal-link-present",
"header-present",
"landmark"
],
"all": [],
"none": [],
"tags": [
"wcag2a",
"wcag241",
"section508",
"section508.22.o"
],
"matches": "function matches(node) {\n return !!node.querySelector('a[href]');\n }"
},
{
"id": "color-contrast",
"selector": "*",
"excludeHidden": false,
"enabled": true,
"pageLevel": false,
"any": [
"color-contrast"
],
"all": [],
"none": [],
"tags": [
"wcag2aa",
"wcag143"
],
"matches": "function matches(node) {\n /* global document */\n var nodeName = node.nodeName.toUpperCase(), nodeType = node.type, doc = document;\n if (node.getAttribute('aria-disabled') === 'true') {\n return false;\n }\n if (nodeName === 'INPUT') {\n return [ 'hidden', 'range', 'color', 'checkbox', 'radio', 'image' ].indexOf(nodeType) === -1 && !node.disabled;\n }\n if (nodeName === 'SELECT') {\n return !!node.options.length && !node.disabled;\n }\n if (nodeName === 'TEXTAREA') {\n return !node.disabled;\n }\n if (nodeName === 'OPTION') {\n return false;\n }\n if (nodeName === 'BUTTON' && node.disabled) {\n return false;\n }\n // check if the element is a label for a disabled control\n if (nodeName === 'LABEL') {\n // explicit label of disabled input\n var candidate = node.htmlFor && doc.getElementById(node.htmlFor);\n if (candidate && candidate.disabled) {\n return false;\n }\n var candidate = node.querySelector('input:not([type=\"hidden\"]):not([type=\"image\"])' + ':not([type=\"button\"]):not([type=\"submit\"]):not([type=\"reset\"]), select, textarea');\n if (candidate && candidate.disabled) {\n return false;\n }\n }\n // label of disabled control associated w/ aria-labelledby\n if (node.id) {\n var candidate = doc.querySelector('[aria-labelledby~=' + axe.commons.utils.escapeSelector(node.id) + ']');\n if (candidate && candidate.disabled) {\n return false;\n }\n }\n if (axe.commons.text.visible(node, false, true) === '') {\n return false;\n }\n var range = document.createRange(), childNodes = node.childNodes, length = childNodes.length, child, index;\n for (index = 0; index < length; index++) {\n child = childNodes[index];\n if (child.nodeType === 3 && axe.commons.text.sanitize(child.nodeValue) !== '') {\n range.selectNodeContents(child);\n }\n }\n var rects = range.getClientRects();\n length = rects.length;\n for (index = 0; index < length; index++) {\n //check to see if the rectangle impinges\n if (axe.commons.dom.visuallyOverlaps(rects[index], node)) {\n return true;\n }\n }\n return false;\n }"
},
{
"id": "definition-list",
"selector": "dl:not([role])",
"excludeHidden": true,
"enabled": true,
"pageLevel": false,
"any": [],
"all": [],
"none": [
"structured-dlitems",
"only-dlitems"
],
"tags": [
"wcag2a",
"wcag131"
]
},
{
"id": "dlitem",
"selector": "dd:not([role]), dt:not([role])",
"excludeHidden": true,
"enabled": true,
"pageLevel": false,
"any": [
"dlitem"
],
"all": [],
"none": [],
"tags": [
"wcag2a",
"wcag131"
]
},
{
"id": "document-title",
"selector": "html",
"excludeHidden": true,
"enabled": true,
"pageLevel": false,
"any": [
"doc-has-title"
],
"all": [],
"none": [],
"tags": [
"wcag2a",
"wcag242"
],
"matches": "function matches(node) {\n /* global window */\n return window.self === window.top;\n }"
},
{
"id": "duplicate-id",
"selector": "[id]",
"excludeHidden": false,
"enabled": true,
"pageLevel": false,
"any": [
"duplicate-id"
],
"all": [],
"none": [],
"tags": [
"wcag2a",
"wcag411"
]
},
{
"id": "frame-title",
"selector": "frame, iframe",
"excludeHidden": true,
"enabled": true,
"pageLevel": false,
"any": [
"aria-label",
"aria-labelledby",
"non-empty-title",
"role-presentation",
"role-none"
],
"all": [],
"none": [],
"tags": [
"wcag2a",
"wcag241",
"section508",
"section508.22.i"
]
},
{
"id": "html-has-lang",
"selector": "html",
"excludeHidden": true,
"enabled": true,
"pageLevel": false,
"any": [
"has-lang"
],
"all": [],
"none": [],
"tags": [
"wcag2a",
"wcag311"
]
},
{
"id": "html-lang-valid",
"selector": "html[lang]",
"excludeHidden": true,
"enabled": true,
"pageLevel": false,
"any": [],
"all": [],
"none": [
{
"options": [
"aa",
"ab",
"ae",
"af",
"ak",
"am",
"an",
"ar",
"as",
"av",
"ay",
"az",
"ba",
"be",
"bg",
"bh",
"bi",
"bm",
"bn",
"bo",
"br",
"bs",
"ca",
"ce",
"ch",
"co",
"cr",
"cs",
"cu",
"cv",
"cy",
"da",
"de",
"dv",
"dz",
"ee",
"el",
"en",
"eo",
"es",
"et",
"eu",
"fa",
"ff",
"fi",
"fj",
"fo",
"fr",
"fy",
"ga",
"gd",
"gl",
"gn",
"gu",
"gv",
"ha",
"he",
"hi",
"ho",
"hr",
"ht",
"hu",
"hy",
"hz",
"ia",
"id",
"ie",
"ig",
"ii",
"ik",
"in",
"io",
"is",
"it",
"iu",
"iw",
"ja",
"ji",
"jv",
"jw",
"ka",
"kg",
"ki",
"kj",
"kk",
"kl",
"km",
"kn",
"ko",
"kr",
"ks",
"ku",
"kv",
"kw",
"ky",
"la",
"lb",
"lg",
"li",
"ln",
"lo",
"lt",
"lu",
"lv",
"mg",
"mh",
"mi",
"mk",
"ml",
"mn",
"mo",
"mr",
"ms",
"mt",
"my",
"na",
"nb",
"nd",
"ne",
"ng",
"nl",
"nn",
"no",
"nr",
"nv",
"ny",
"oc",
"oj",
"om",
"or",
"os",
"pa",
"pi",
"pl",
"ps",
"pt",
"qu",
"rm",
"rn",
"ro",
"ru",
"rw",
"sa",
"sc",
"sd",
"se",
"sg",
"sh",
"si",
"sk",
"sl",
"sm",
"sn",
"so",
"sq",
"sr",
"ss",
"st",
"su",
"sv",
"sw",
"ta",
"te",
"tg",
"th",
"ti",
"tk",
"tl",
"tn",
"to",
"tr",
"ts",
"tt",
"tw",
"ty",
"ug",
"uk",
"ur",
"uz",
"ve",
"vi",
"vo",
"wa",
"wo",
"xh",
"yi",
"yo",
"za",
"zh",
"zu"
],
"id": "valid-lang"
}
],
"tags": [
"wcag2a",
"wcag311"
]
},
{
"id": "image-alt",
"selector": "img, [role='img']",
"excludeHidden": true,
"enabled": true,
"pageLevel": false,
"any": [
"has-alt",
"aria-label",
"aria-labelledby",
"non-empty-title",
"role-presentation",
"role-none"
],
"all": [],
"none": [],
"tags": [
"wcag2a",
"wcag111",
"section508",
"section508.22.a"
],
"matches": "function matches(node) {\n var nodeName = node.nodeName.toUpperCase(),\n clientRect = node.getBoundingClientRect();\n\n if(nodeName === 'IMG') {\n return (node.width > 2 && node.height > 2);\n } else {\n return (clientRect.width > 2 && clientRect.height > 2);\n }\n}",
"metadata": {
"description": "Ensures <img> elements larger than 2px width/height have alternate text or a role of none or presentation",
"help": "Images must have alternate text"
}
},
{
"id": "input-image-alt",
"selector": "input[type=\"image\"]",
"excludeHidden": true,
"enabled": true,
"pageLevel": false,
"any": [
"non-empty-alt",
"aria-label",
"aria-labelledby",
"non-empty-title"
],
"all": [],
"none": [],
"tags": [
"wcag2a",
"wcag111",
"section508",
"section508.22.a"
]
},
{
"id": "label",
"selector": "input:not([type='hidden']):not([type='image']):not([type='button']):not([type='submit']):not([type='reset']), select, textarea",
"excludeHidden": true,
"enabled": true,
"pageLevel": false,
"any": [
"aria-label",
"aria-labelledby",
"implicit-label",
"explicit-label",
"non-empty-title"
],
"all": [],
"none": [
"help-same-as-label",
"multiple-label"
],
"tags": [
"wcag2a",
"wcag332",
"wcag131",
"section508",
"section508.22.n"
]
},
{
"id": "layout-table",
"selector": "table",
"excludeHidden": true,
"enabled": true,
"pageLevel": false,
"any": [],
"all": [],
"none": [
"has-th",
"has-caption",
"has-summary"
],
"tags": [
"wcag2a",
"wcag131"
],
"matches": "function matches(node) {\n return !axe.commons.table.isDataTable(node);\n }"
},
{
"id": "link-in-text-block",
"selector": "a[href]:not([role]), *[role=link]",
"excludeHidden": false,
"enabled": false,
"pageLevel": false,
"any": [],
"all": [
"link-in-text-block"
],
"none": [],
"tags": [
"experimental",
"wcag2a",
"wcag141"
],
"matches": "function matches(node) {\n var text = axe.commons.text.sanitize(node.textContent);\n if (!text) {\n return false;\n }\n if (!axe.commons.dom.isVisible(node, false)) {\n return false;\n }\n return axe.commons.dom.isInTextBlock(node);\n }"
},
{
"id": "link-name",
"selector": "a[href]:not([role=\"button\"]), [role=link][href]",
"excludeHidden": true,
"enabled": true,
"pageLevel": false,
"any": [
"has-visible-text",
"aria-label",
"aria-labelledby",
"role-presentation",
"role-none"
],
"all": [],
"none": [
"focusable-no-name"
],
"tags": [
"wcag2a",
"wcag111",
"wcag412",
"section508",
"section508.22.a"
]
},
{
"id": "list",
"selector": "ul:not([role]), ol:not([role])",
"excludeHidden": true,
"enabled": true,
"pageLevel": false,
"any": [],
"all": [],
"none": [
"only-listitems"
],
"tags": [
"wcag2a",
"wcag131"
]
},
{
"id": "listitem",
"selector": "li:not([role])",
"excludeHidden": true,
"enabled": true,
"pageLevel": false,
"any": [
"listitem"
],
"all": [],
"none": [],
"tags": [
"wcag2a",
"wcag131"
]
},
{
"id": "marquee",
"selector": "marquee",
"excludeHidden": false,
"enabled": true,
"pageLevel": false,
"any": [],
"all": [],
"none": [
"is-on-screen"
],
"tags": [
"wcag2a",
"wcag222"
]
},
{
"id": "meta-refresh",
"selector": "meta[http-equiv=\"refresh\"]",
"excludeHidden": false,
"enabled": true,
"pageLevel": false,
"any": [
"meta-refresh"
],
"all": [],
"none": [],
"tags": [
"wcag2a",
"wcag2aaa",
"wcag221",
"wcag224",
"wcag325"
]
},
{
"id": "meta-viewport",
"selector": "meta[name=\"viewport\"]",
"excludeHidden": false,
"enabled": true,
"pageLevel": false,
"any": [
{
"options": {
"scaleMinimum": 2
},
"id": "meta-viewport"
}
],
"all": [],
"none": [],
"tags": [
"wcag2aa",
"wcag144"
]
},
{
"id": "object-alt",
"selector": "object",
"excludeHidden": true,
"enabled": true,
"pageLevel": false,
"any": [
"has-visible-text",
"aria-label",
"aria-labelledby",
"non-empty-title"
],
"all": [],
"none": [],
"tags": [
"wcag2a",
"wcag111",
"section508",
"section508.22.a"
]
},
{
"id": "server-side-image-map",
"selector": "img[ismap]",
"excludeHidden": true,
"enabled": true,
"pageLevel": false,
"any": [],
"all": [],
"none": [
"exists"
],
"tags": [
"wcag2a",
"wcag211",
"section508",
"section508.22.f"
]
},
{
"id": "table-fake-caption",
"selector": "table",
"excludeHidden": true,
"enabled": true,
"pageLevel": false,
"any": [],
"all": [
"caption-faked"
],
"none": [],
"tags": [
"experimental",
"wcag2a",
"wcag131",
"section508",
"section508.22.g"
],
"matches": "function matches(node) {\n return axe.commons.table.isDataTable(node);\n }"
},
{
"id": "td-has-header",
"selector": "table",
"excludeHidden": true,
"enabled": true,
"pageLevel": false,
"any": [],
"all": [
"td-has-header"
],
"none": [],
"tags": [
"experimental",
"wcag2a",
"wcag131",
"section508",
"section508.22.g"
],
"matches": "function matches(node) {\n if (axe.commons.table.isDataTable(node)) {\n var tableArray = axe.commons.table.toArray(node);\n return tableArray.length >= 3 && tableArray[0].length >= 3 && tableArray[1].length >= 3 && tableArray[2].length >= 3;\n }\n return false;\n }"
},
{
"id": "td-headers-attr",
"selector": "table",
"excludeHidden": true,
"enabled": true,
"pageLevel": false,
"any": [],
"all": [
"td-headers-attr"
],
"none": [],
"tags": [
"wcag2a",
"wcag131",
"section508",
"section508.22.g"
]
},
{
"id": "th-has-data-cells",
"selector": "table",
"excludeHidden": true,
"enabled": true,
"pageLevel": false,
"any": [],
"all": [
"th-has-data-cells"
],
"none": [],
"tags": [
"wcag2a",
"wcag131",
"section508",
"section508.22.g"
],
"matches": "function matches(node) {\n return axe.commons.table.isDataTable(node);\n }"
},
{
"id": "valid-lang",
"selector": "[lang]:not(html), [xml\\:lang]:not(html)",
"excludeHidden": true,
"enabled": true,
"pageLevel": false,
"any": [],
"all": [],
"none": [
{
"options": [
"aa",
"ab",
"ae",
"af",
"ak",
"am",
"an",
"ar",
"as",
"av",
"ay",
"az",
"ba",
"be",
"bg",
"bh",
"bi",
"bm",
"bn",
"bo",
"br",
"bs",
"ca",
"ce",
"ch",
"co",
"cr",
"cs",
"cu",
"cv",
"cy",
"da",
"de",
"dv",
"dz",
"ee",
"el",
"en",
"eo",
"es",
"et",
"eu",
"fa",
"ff",
"fi",
"fj",
"fo",
"fr",
"fy",
"ga",
"gd",
"gl",
"gn",
"gu",
"gv",
"ha",
"he",
"hi",
"ho",
"hr",
"ht",
"hu",
"hy",
"hz",
"ia",
"id",
"ie",
"ig",
"ii",
"ik",
"in",
"io",
"is",
"it",
"iu",
"iw",
"ja",
"ji",
"jv",
"jw",
"ka",
"kg",
"ki",
"kj",
"kk",
"kl",
"km",
"kn",
"ko",
"kr",
"ks",
"ku",
"kv",
"kw",
"ky",
"la",
"lb",
"lg",
"li",
"ln",
"lo",
"lt",
"lu",
"lv",
"mg",
"mh",
"mi",
"mk",
"ml",
"mn",
"mo",
"mr",
"ms",
"mt",
"my",
"na",
"nb",
"nd",
"ne",
"ng",
"nl",
"nn",
"no",
"nr",
"nv",
"ny",
"oc",
"oj",
"om",
"or",
"os",
"pa",
"pi",
"pl",
"ps",
"pt",
"qu",
"rm",
"rn",
"ro",
"ru",
"rw",
"sa",
"sc",
"sd",
"se",
"sg",
"sh",
"si",
"sk",
"sl",
"sm",
"sn",
"so",
"sq",
"sr",
"ss",
"st",
"su",
"sv",
"sw",
"ta",
"te",
"tg",
"th",
"ti",
"tk",
"tl",
"tn",
"to",
"tr",
"ts",
"tt",
"tw",
"ty",
"ug",
"uk",
"ur",
"uz",
"ve",
"vi",
"vo",
"wa",
"wo",
"xh",
"yi",
"yo",
"za",
"zh",
"zu"
],
"id": "valid-lang"
}
],
"tags": [
"wcag2aa",
"wcag312"
]
},
{
"id": "video-caption",
"selector": "video",
"excludeHidden": false,
"enabled": true,
"pageLevel": false,
"any": [],
"all": [],
"none": [
"caption"
],
"tags": [
"wcag2a",
"wcag122",
"wcag123",
"section508",
"section508.22.a"
]
},
{
"id": "video-description",
"selector": "video",
"excludeHidden": false,
"enabled": true,
"pageLevel": false,
"any": [],
"all": [],
"none": [
"description"
],
"tags": [
"wcag2aa",
"wcag125",
"section508",
"section508.22.b"
]
},
{
"id": "checkboxgroup",
"enabled": false
},
{
"id": "empty-heading",
"enabled": false
},
{
"id": "frame-title-unique",
"enabled": false
},
{
"id": "heading-order",
"enabled": false
},
{
"id": "href-no-hash",
"enabled": false
},
{
"id": "image-redundant-alt",
"enabled": false
},
{
"id": "label-title-only",
"enabled": false
},
{
"id": "meta-viewport-large",
"enabled": false
},
{
"id": "radiogroup",
"enabled": false
},
{
"id": "region",
"enabled": false
},
{
"id": "scope-attr-valid",
"enabled": false
},
{
"id": "skip-link",
"enabled": false
},
{
"id": "tabindex",
"enabled": false
},
{
"id": "table-duplicate-name",
"enabled": false
}
],
"checks": [
{
"id": "accesskeys",
"evaluate": "function evaluate(node, options) {\n if (axe.commons.dom.isVisible(node, false)) {\n this.data(node.getAttribute('accesskey'));\n this.relatedNodes([ node ]);\n }\n return true;\n }",
"after": "function after(results, options) {\n var seen = {};\n return results.filter(function(r) {\n if (!r.data) {\n return false;\n }\n var key = r.data.toUpperCase();\n if (!seen[key]) {\n seen[key] = r;\n r.relatedNodes = [];\n return true;\n }\n seen[key].relatedNodes.push(r.relatedNodes[0]);\n return false;\n }).map(function(r) {\n r.result = !!r.relatedNodes.length;\n return r;\n });\n }",
"enabled": true
},
{
"id": "non-empty-alt",
"evaluate": "function evaluate(node, options) {\n var label = node.getAttribute('alt');\n return !!(label ? axe.commons.text.sanitize(label).trim() : '');\n }",
"enabled": true
},
{
"id": "non-empty-title",
"evaluate": "function evaluate(node, options) {\n var title = node.getAttribute('title');\n return !!(title ? axe.commons.text.sanitize(title).trim() : '');\n }",
"enabled": true
},
{
"id": "aria-label",
"evaluate": "function evaluate(node, options) {\n var label = node.getAttribute('aria-label');\n return !!(label ? axe.commons.text.sanitize(label).trim() : '');\n }",
"enabled": true
},
{
"id": "aria-labelledby",
"evaluate": "function evaluate(node, options) {\n var getIdRefs = axe.commons.dom.idrefs;\n return getIdRefs(node, 'aria-labelledby').some(function(elm) {\n return elm && axe.commons.text.accessibleText(elm, true);\n });\n }",
"enabled": true
},
{
"id": "aria-allowed-attr",
"evaluate": "function evaluate(node, options) {\n var invalid = [];\n var attr, attrName, allowed, role = node.getAttribute('role'), attrs = node.attributes;\n if (!role) {\n role = axe.commons.aria.implicitRole(node);\n }\n allowed = axe.commons.aria.allowedAttr(role);\n if (role && allowed) {\n for (var i = 0, l = attrs.length; i < l; i++) {\n attr = attrs[i];\n attrName = attr.name;\n if (axe.commons.aria.validateAttr(attrName) && allowed.indexOf(attrName) === -1) {\n invalid.push(attrName + '=\"' + attr.nodeValue + '\"');\n }\n }\n }\n if (invalid.length) {\n this.data(invalid);\n return false;\n }\n return true;\n }",
"enabled": true
},
{
"id": "aria-required-attr",
"evaluate": "function evaluate(node, options) {\n var missing = [];\n if (node.hasAttributes()) {\n var attr, role = node.getAttribute('role'), required = axe.commons.aria.requiredAttr(role);\n if (role && required) {\n for (var i = 0, l = required.length; i < l; i++) {\n attr = required[i];\n if (!node.getAttribute(attr)) {\n missing.push(attr);\n }\n }\n }\n }\n if (missing.length) {\n this.data(missing);\n return false;\n }\n return true;\n }",
"enabled": true
},
{
"id": "aria-required-children",
"evaluate": "function evaluate(node, options) {\n var requiredOwned = axe.commons.aria.requiredOwned, implicitNodes = axe.commons.aria.implicitNodes, matchesSelector = axe.commons.utils.matchesSelector, idrefs = axe.commons.dom.idrefs;\n function owns(node, role, ariaOwned) {\n if (node === null) {\n return false;\n }\n var implicit = implicitNodes(role), selector = [ '[role=\"' + role + '\"]' ];\n if (implicit) {\n selector = selector.concat(implicit);\n }\n selector = selector.join(',');\n return ariaOwned ? matchesSelector(node, selector) || !!node.querySelector(selector) : !!node.querySelector(selector);\n }\n function ariaOwns(nodes, role) {\n var index, length;\n for (index = 0, length = nodes.length; index < length; index++) {\n if (nodes[index] === null) {\n continue;\n }\n if (owns(nodes[index], role, true)) {\n return true;\n }\n }\n return false;\n }\n function missingRequiredChildren(node, childRoles, all) {\n var i, l = childRoles.length, missing = [], ownedElements = idrefs(node, 'aria-owns');\n for (i = 0; i < l; i++) {\n var r = childRoles[i];\n if (owns(node, r) || ariaOwns(ownedElements, r)) {\n if (!all) {\n return null;\n }\n } else {\n if (all) {\n missing.push(r);\n }\n }\n }\n if (missing.length) {\n return missing;\n }\n if (!all && childRoles.length) {\n return childRoles;\n }\n return null;\n }\n var role = node.getAttribute('role');\n var required = requiredOwned(role);\n if (!required) {\n return true;\n }\n var all = false;\n var childRoles = required.one;\n if (!childRoles) {\n var all = true;\n childRoles = required.all;\n }\n var missing = missingRequiredChildren(node, childRoles, all);\n if (!missing) {\n return true;\n }\n this.data(missing);\n return false;\n }",
"enabled": true
},
{
"id": "aria-required-parent",
"evaluate": "function evaluate(node, options) {\n function getSelector(role) {\n var impliedNative = axe.commons.aria.implicitNodes(role) || [];\n return impliedNative.concat('[role=\"' + role + '\"]').join(',');\n }\n function getMissingContext(element, requiredContext, includeElement) {\n var index, length, role = element.getAttribute('role'), missing = [];\n if (!requiredContext) {\n requiredContext = axe.commons.aria.requiredContext(role);\n }\n if (!requiredContext) {\n return null;\n }\n for (index = 0, length = requiredContext.length; index < length; index++) {\n if (includeElement && axe.utils.matchesSelector(element, getSelector(requiredContext[index]))) {\n return null;\n }\n if (axe.commons.dom.findUp(element, getSelector(requiredContext[index]))) {\n //if one matches, it passes\n return null;\n } else {\n missing.push(requiredContext[index]);\n }\n }\n return missing;\n }\n function getAriaOwners(element) {\n var owners = [], o = null;\n while (element) {\n if (element.id) {\n o = document.querySelector('[aria-owns~=' + axe.commons.utils.escapeSelector(element.id) + ']');\n if (o) {\n owners.push(o);\n }\n }\n element = element.parentNode;\n }\n return owners.length ? owners : null;\n }\n var missingParents = getMissingContext(node);\n if (!missingParents) {\n return true;\n }\n var owners = getAriaOwners(node);\n if (owners) {\n for (var i = 0, l = owners.length; i < l; i++) {\n missingParents = getMissingContext(owners[i], missingParents, true);\n if (!missingParents) {\n return true;\n }\n }\n }\n this.data(missingParents);\n return false;\n }",
"enabled": true
},
{
"id": "invalidrole",
"evaluate": "function evaluate(node, options) {\n return !axe.commons.aria.isValidRole(node.getAttribute('role'));\n }",
"enabled": true
},
{
"id": "abstractrole",
"evaluate": "function evaluate(node, options) {\n return axe.commons.aria.getRoleType(node.getAttribute('role')) === 'abstract';\n }",
"enabled": true
},
{
"id": "aria-valid-attr-value",
"options": [],
"evaluate": "function evaluate(node, options) {\n options = Array.isArray(options) ? options : [];\n var invalid = [], aria = /^aria-/;\n var attr, attrName, attrs = node.attributes;\n for (var i = 0, l = attrs.length; i < l; i++) {\n attr = attrs[i];\n attrName = attr.name;\n if (options.indexOf(attrName) === -1 && aria.test(attrName) && !axe.commons.aria.validateAttrValue(node, attrName)) {\n invalid.push(attrName + '=\"' + attr.nodeValue + '\"');\n }\n }\n if (invalid.length) {\n this.data(invalid);\n return false;\n }\n return true;\n }",
"enabled": true
},
{
"id": "aria-valid-attr",
"options": [],
"evaluate": "function evaluate(node, options) {\n options = Array.isArray(options) ? options : [];\n var invalid = [], aria = /^aria-/;\n var attr, attrs = node.attributes;\n for (var i = 0, l = attrs.length; i < l; i++) {\n attr = attrs[i].name;\n if (options.indexOf(attr) === -1 && aria.test(attr) && !axe.commons.aria.validateAttr(attr)) {\n invalid.push(attr);\n }\n }\n if (invalid.length) {\n this.data(invalid);\n return false;\n }\n return true;\n }",
"enabled": true
},
{
"id": "caption",
"evaluate": "function evaluate(node, options) {\n return !node.querySelector('track[kind=captions]');\n }",
"enabled": true
},
{
"id": "is-on-screen",
"evaluate": "function evaluate(node, options) {\n // From a visual perspective\n return axe.commons.dom.isVisible(node, false) && !axe.commons.dom.isOffscreen(node);\n }",
"enabled": true
},
{
"id": "non-empty-if-present",
"evaluate": "function evaluate(node, options) {\n // Check for 'default' names, which are given to reset and submit buttons\n var nodeName = node.nodeName.toUpperCase();\n var type = (node.getAttribute('type') || '').toLowerCase();\n var label = node.getAttribute('value');\n this.data(label);\n if (nodeName === 'INPUT' && [ 'submit', 'reset' ].indexOf(type) !== -1) {\n return label === null;\n }\n return false;\n }",
"enabled": true
},
{
"id": "non-empty-value",
"evaluate": "function evaluate(node, options) {\n var label = node.getAttribute('value');\n return !!(label ? axe.commons.text.sanitize(label).trim() : '');\n }",
"enabled": true
},
{
"id": "button-has-visible-text",
"evaluate": "function evaluate(node, options) {\n var nodeName = node.nodeName.toUpperCase();\n var role = node.getAttribute('role');\n var label = void 0;\n if (nodeName === 'BUTTON' || role === 'button' && nodeName !== 'INPUT') {\n label = axe.commons.text.accessibleText(node);\n this.data(label);\n return !!label;\n } else {\n return false;\n }\n }",
"enabled": true
},
{
"id": "role-presentation",
"evaluate": "function evaluate(node, options) {\n return node.getAttribute('role') === 'presentation';\n }",
"enabled": true
},
{
"id": "role-none",
"evaluate": "function evaluate(node, options) {\n return node.getAttribute('role') === 'none';\n }",
"enabled": true
},
{
"id": "focusable-no-name",
"evaluate": "function evaluate(node, options) {\n var tabIndex = node.getAttribute('tabindex'), isFocusable = axe.commons.dom.isFocusable(node) && tabIndex > -1;\n if (!isFocusable) {\n return false;\n }\n return !axe.commons.text.accessibleText(node);\n }",
"enabled": true
},
{
"id": "internal-link-present",
"evaluate": "function evaluate(node, options) {\n return !!node.querySelector('a[href^=\"#\"]');\n }",
"enabled": true
},
{
"id": "header-present",
"evaluate": "function evaluate(node, options) {\n return !!node.querySelector('h1, h2, h3, h4, h5, h6, [role=\"heading\"]');\n }",
"enabled": true
},
{
"id": "landmark",
"evaluate": "function evaluate(node, options) {\n return node.getElementsByTagName('main').length > 0 || !!node.querySelector('[role=\"main\"]');\n }",
"enabled": true
},
{
"id": "color-contrast",
"evaluate": "function evaluate(node, options) {\n if (!axe.commons.dom.isVisible(node, false)) {\n return true;\n }\n var noScroll = !!(options || {}).noScroll;\n var bgNodes = [], bgColor = axe.commons.color.getBackgroundColor(node, bgNodes, noScroll), fgColor = axe.commons.color.getForegroundColor(node, noScroll);\n //We don't know, so we'll pass it provisionally\n if (fgColor === null || bgColor === null) {\n return undefined;\n }\n var nodeStyle = window.getComputedStyle(node);\n var fontSize = parseFloat(nodeStyle.getPropertyValue('font-size'));\n var fontWeight = nodeStyle.getPropertyValue('font-weight');\n var bold = [ 'bold', 'bolder', '600', '700', '800', '900' ].indexOf(fontWeight) !== -1;\n var cr = axe.commons.color.hasValidContrastRatio(bgColor, fgColor, fontSize, bold);\n this.data({\n fgColor: fgColor.toHexString(),\n bgColor: bgColor.toHexString(),\n contrastRatio: cr.contrastRatio.toFixed(2),\n fontSize: (fontSize * 72 / 96).toFixed(1) + 'pt',\n fontWeight: bold ? 'bold' : 'normal'\n });\n if (!cr.isValid) {\n this.relatedNodes(bgNodes);\n }\n return cr.isValid;\n }",
"enabled": true
},
{
"id": "structured-dlitems",
"evaluate": "function evaluate(node, options) {\n var children = node.children;\n if (!children || !children.length) {\n return false;\n }\n var hasDt = false, hasDd = false, nodeName;\n for (var i = 0; i < children.length; i++) {\n nodeName = children[i].nodeName.toUpperCase();\n if (nodeName === 'DT') {\n hasDt = true;\n }\n if (hasDt && nodeName === 'DD') {\n return false;\n }\n if (nodeName === 'DD') {\n hasDd = true;\n }\n }\n return hasDt || hasDd;\n }",
"enabled": true
},
{
"id": "only-dlitems",
"evaluate": "function evaluate(node, options) {\n var child, nodeName, bad = [], children = node.childNodes, permitted = [ 'STYLE', 'META', 'LINK', 'MAP', 'AREA', 'SCRIPT', 'DATALIST', 'TEMPLATE' ], hasNonEmptyTextNode = false;\n for (var i = 0; i < children.length; i++) {\n child = children[i];\n var nodeName = child.nodeName.toUpperCase();\n if (child.nodeType === 1 && nodeName !== 'DT' && nodeName !== 'DD' && permitted.indexOf(nodeName) === -1) {\n bad.push(child);\n } else {\n if (child.nodeType === 3 && child.nodeValue.trim() !== '') {\n hasNonEmptyTextNode = true;\n }\n }\n }\n if (bad.length) {\n this.relatedNodes(bad);\n }\n var retVal = !!bad.length || hasNonEmptyTextNode;\n return retVal;\n }",
"enabled": true
},
{
"id": "dlitem",
"evaluate": "function evaluate(node, options) {\n return node.parentNode.tagName === 'DL';\n }",
"enabled": true
},
{
"id": "doc-has-title",
"evaluate": "function evaluate(node, options) {\n var title = document.title;\n return !!(title ? axe.commons.text.sanitize(title).trim() : '');\n }",
"enabled": true
},
{
"id": "duplicate-id",
"evaluate": "function evaluate(node, options) {\n // Since empty ID's are not meaningful and are ignored by Edge, we'll\n // let those pass.\n if (!node.id.trim()) {\n return true;\n }\n var matchingNodes = document.querySelectorAll('[id=\"' + axe.commons.utils.escapeSelector(node.id) + '\"]');\n var related = [];\n for (var i = 0; i < matchingNodes.length; i++) {\n if (matchingNodes[i] !== node) {\n related.push(matchingNodes[i]);\n }\n }\n if (related.length) {\n this.relatedNodes(related);\n }\n this.data(node.getAttribute('id'));\n return matchingNodes.length <= 1;\n }",
"after": "function after(results, options) {\n var uniqueIds = [];\n return results.filter(function(r) {\n if (uniqueIds.indexOf(r.data) === -1) {\n uniqueIds.push(r.data);\n return true;\n }\n return false;\n });\n }",
"enabled": true
},
{
"id": "has-lang",
"evaluate": "function evaluate(node, options) {\n return !!(node.getAttribute('lang') || node.getAttribute('xml:lang') || '').trim();\n }",
"enabled": true
},
{
"id": "valid-lang",
"options": [
"aa",
"ab",
"ae",
"af",
"ak",
"am",
"an",
"ar",
"as",
"av",
"ay",
"az",
"ba",
"be",
"bg",
"bh",
"bi",
"bm",
"bn",
"bo",
"br",
"bs",
"ca",
"ce",
"ch",
"co",
"cr",
"cs",
"cu",
"cv",
"cy",
"da",
"de",
"dv",
"dz",
"ee",
"el",
"en",
"eo",
"es",
"et",
"eu",
"fa",
"ff",
"fi",
"fj",
"fo",
"fr",
"fy",
"ga",
"gd",
"gl",
"gn",
"gu",
"gv",
"ha",
"he",
"hi",
"ho",
"hr",
"ht",
"hu",
"hy",
"hz",
"ia",
"id",
"ie",
"ig",
"ii",
"ik",
"in",
"io",
"is",
"it",
"iu",
"iw",
"ja",
"ji",
"jv",
"jw",
"ka",
"kg",
"ki",
"kj",
"kk",
"kl",
"km",
"kn",
"ko",
"kr",
"ks",
"ku",
"kv",
"kw",
"ky",
"la",
"lb",
"lg",
"li",
"ln",
"lo",
"lt",
"lu",
"lv",
"mg",
"mh",
"mi",
"mk",
"ml",
"mn",
"mo",
"mr",
"ms",
"mt",
"my",
"na",
"nb",
"nd",
"ne",
"ng",
"nl",
"nn",
"no",
"nr",
"nv",
"ny",
"oc",
"oj",
"om",
"or",
"os",
"pa",
"pi",
"pl",
"ps",
"pt",
"qu",
"rm",
"rn",
"ro",
"ru",
"rw",
"sa",
"sc",
"sd",
"se",
"sg",
"sh",
"si",
"sk",
"sl",
"sm",
"sn",
"so",
"sq",
"sr",
"ss",
"st",
"su",
"sv",
"sw",
"ta",
"te",
"tg",
"th",
"ti",
"tk",
"tl",
"tn",
"to",
"tr",
"ts",
"tt",
"tw",
"ty",
"ug",
"uk",
"ur",
"uz",
"ve",
"vi",
"vo",
"wa",
"wo",
"xh",
"yi",
"yo",
"za",
"zh",
"zu"
],
"evaluate": "function evaluate(node, options) {\n function getBaseLang(lang) {\n return lang.trim().split('-')[0].toLowerCase();\n }\n var langs, invalid;\n langs = (options || []).map(getBaseLang);\n invalid = [ 'lang', 'xml:lang' ].reduce(function(invalid, langAttr) {\n var langVal = node.getAttribute(langAttr);\n if (typeof langVal !== 'string') {\n return invalid;\n }\n var baselangVal = getBaseLang(langVal);\n // Edge sets lang to an empty string when xml:lang is set\n // so we need to ignore empty strings here\n if (baselangVal !== '' && langs.indexOf(baselangVal) === -1) {\n invalid.push(langAttr + '=\"' + node.getAttribute(langAttr) + '\"');\n }\n return invalid;\n }, []);\n if (invalid.length) {\n this.data(invalid);\n return true;\n }\n return false;\n }",
"enabled": true
},
{
"id": "has-alt",
"evaluate": "function evaluate(node, options) {\n return node.hasAttribute('alt');\n }",
"enabled": true
},
{
"id": "implicit-label",
"evaluate": "function evaluate(node, options) {\n var label = axe.commons.dom.findUp(node, 'label');\n if (label) {\n return !!axe.commons.text.accessibleText(label);\n }\n return false;\n }",
"enabled": true
},
{
"id": "explicit-label",
"evaluate": "function evaluate(node, options) {\n if (node.id) {\n var label = document.querySelector('label[for=\"' + axe.commons.utils.escapeSelector(node.id) + '\"]');\n if (label) {\n return !!axe.commons.text.accessibleText(label);\n }\n }\n return false;\n }",
"enabled": true
},
{
"id": "help-same-as-label",
"enabled": false,
"evaluate": "function evaluate(node, options) {\n var labelText = axe.commons.text.label(node), check = node.getAttribute('title');\n if (!labelText) {\n return false;\n }\n if (!check) {\n check = '';\n if (node.getAttribute('aria-describedby')) {\n var ref = axe.commons.dom.idrefs(node, 'aria-describedby');\n check = ref.map(function(thing) {\n return thing ? axe.commons.text.accessibleText(thing) : '';\n }).join('');\n }\n }\n return axe.commons.text.sanitize(check) === axe.commons.text.sanitize(labelText);\n }"
},
{
"id": "multiple-label",
"evaluate": "function evaluate(node, options) {\n var labels = [].slice.call(document.querySelectorAll('label[for=\"' + axe.commons.utils.escapeSelector(node.id) + '\"]')), parent = node.parentNode;\n while (parent) {\n if (parent.tagName === 'LABEL' && labels.indexOf(parent) === -1) {\n labels.push(parent);\n }\n parent = parent.parentNode;\n }\n this.relatedNodes(labels);\n return labels.length > 1;\n }",
"enabled": true
},
{
"id": "has-th",
"evaluate": "function evaluate(node, options) {\n var row, cell, badCells = [];\n for (var rowIndex = 0, rowLength = node.rows.length; rowIndex < rowLength; rowIndex++) {\n row = node.rows[rowIndex];\n for (var cellIndex = 0, cellLength = row.cells.length; cellIndex < cellLength; cellIndex++) {\n cell = row.cells[cellIndex];\n if (cell.nodeName.toUpperCase() === 'TH' || [ 'rowheader', 'columnheader' ].indexOf(cell.getAttribute('role')) !== -1) {\n badCells.push(cell);\n }\n }\n }\n if (badCells.length) {\n this.relatedNodes(badCells);\n return true;\n }\n return false;\n }",
"enabled": true
},
{
"id": "has-caption",
"evaluate": "function evaluate(node, options) {\n return !!node.caption;\n }",
"enabled": true
},
{
"id": "has-summary",
"evaluate": "function evaluate(node, options) {\n return !!node.summary;\n }",
"enabled": true
},
{
"id": "link-in-text-block",
"evaluate": "function evaluate(node, options) {\n /* global axe*/\n var color = axe.commons.color;\n function getContrast(color1, color2) {\n var c1lum = color1.getRelativeLuminance();\n var c2lum = color2.getRelativeLuminance();\n return (Math.max(c1lum, c2lum) + .05) / (Math.min(c1lum, c2lum) + .05);\n }\n var blockLike = [ 'block', 'list-item', 'table', 'flex', 'grid', 'inline-block' ];\n function isBlock(elm) {\n var display = window.getComputedStyle(elm).getPropertyValue('display');\n return blockLike.indexOf(display) !== -1 || display.substr(0, 6) === 'table-';\n }\n if (isBlock(node)) {\n return false;\n }\n var parentBlock = node.parentNode;\n while (parentBlock.nodeType === 1 && !isBlock(parentBlock)) {\n parentBlock = parentBlock.parentNode;\n }\n // TODO: Check the :visited state of the link\n if (color.elementIsDistinct(node, parentBlock)) {\n return true;\n } else {\n // Check if contrast of foreground is sufficient\n var nodeColor, parentColor;\n nodeColor = color.getForegroundColor(node);\n parentColor = color.getForegroundColor(parentBlock);\n if (!nodeColor || !parentColor) {\n return undefined;\n }\n var contrast = getContrast(nodeColor, parentColor);\n if (contrast === 1) {\n return true;\n } else {\n if (contrast >= 3) {\n return undefined;\n }\n }\n // Check if contrast of background is sufficient\n nodeColor = color.getBackgroundColor(node);\n parentColor = color.getBackgroundColor(parentBlock);\n if (!nodeColor || !parentColor || getContrast(nodeColor, parentColor) >= 3) {\n return undefined;\n }\n }\n // TODO: We should check the focus / hover style too\n return false;\n }",
"enabled": true
},
{
"id": "has-visible-text",
"evaluate": "function evaluate(node, options) {\n return axe.commons.text.accessibleText(node).length > 0;\n }",
"enabled": true
},
{
"id": "only-listitems",
"evaluate": "function evaluate(node, options) {\n var child, nodeName, bad = [], children = node.childNodes, permitted = [ 'STYLE', 'META', 'LINK', 'MAP', 'AREA', 'SCRIPT', 'DATALIST', 'TEMPLATE' ], hasNonEmptyTextNode = false;\n for (var i = 0; i < children.length; i++) {\n child = children[i];\n nodeName = child.nodeName.toUpperCase();\n if (child.nodeType === 1 && nodeName !== 'LI' && permitted.indexOf(nodeName) === -1) {\n bad.push(child);\n } else {\n if (child.nodeType === 3 && child.nodeValue.trim() !== '') {\n hasNonEmptyTextNode = true;\n }\n }\n }\n if (bad.length) {\n this.relatedNodes(bad);\n }\n return !!bad.length || hasNonEmptyTextNode;\n }",
"enabled": true
},
{
"id": "listitem",
"evaluate": "function evaluate(node, options) {\n if ([ 'UL', 'OL' ].indexOf(node.parentNode.nodeName.toUpperCase()) !== -1) {\n return true;\n }\n return node.parentNode.getAttribute('role') === 'list';\n }",
"enabled": true
},
{
"id": "meta-refresh",
"evaluate": "function evaluate(node, options) {\n var content = node.getAttribute('content') || '', parsedParams = content.split(/[;,]/);\n return content === '' || parsedParams[0] === '0';\n }",
"enabled": true
},
{
"id": "meta-viewport",
"options": {
"scaleMinimum": 2
},
"evaluate": "function evaluate(node, options) {\n options = options || {};\n var params, content = node.getAttribute('content') || '', parsedParams = content.split(/[;,]/), result = {}, minimum = options.scaleMinimum || 2, lowerBound = options.lowerBound || false;\n for (var i = 0, l = parsedParams.length; i < l; i++) {\n params = parsedParams[i].split('=');\n var key = params.shift().toLowerCase();\n if (key && params.length) {\n result[key.trim()] = params.shift().trim().toLowerCase();\n }\n }\n if (lowerBound && result['maximum-scale'] && parseFloat(result['maximum-scale']) < lowerBound) {\n return true;\n }\n if (!lowerBound && result['user-scalable'] === 'no') {\n return false;\n }\n if (result['maximum-scale'] && parseFloat(result['maximum-scale']) < minimum) {\n return false;\n }\n return true;\n }",
"enabled": true
},
{
"id": "exists",
"evaluate": "function evaluate(node, options) {\n return true;\n }",
"enabled": true
},
{
"id": "caption-faked",
"evaluate": "function evaluate(node, options) {\n var table = axe.commons.table.toGrid(node);\n var firstRow = table[0];\n if (table.length <= 1 || firstRow.length <= 1 || node.rows.length <= 1) {\n return true;\n }\n return firstRow.reduce(function(out, curr, i) {\n return out || curr !== firstRow[i + 1] && firstRow[i + 1] !== undefined;\n }, false);\n }",
"enabled": true
},
{
"id": "td-has-header",
"evaluate": "function evaluate(node, options) {\n var tableUtils = axe.commons.table;\n var badCells = [];\n var cells = tableUtils.getAllCells(node);\n cells.forEach(function(cell) {\n // For each non-empty data cell that doesn't have an aria label\n if (cell.textContent.trim() !== '' && tableUtils.isDataCell(cell) && !axe.commons.aria.label(cell)) {\n // Check if it has any headers\n var hasHeaders = tableUtils.getHeaders(cell);\n hasHeaders = hasHeaders.reduce(function(hasHeaders, header) {\n return hasHeaders || header !== null && !!header.textContent.trim();\n }, false);\n // If no headers, put it on the naughty list\n if (!hasHeaders) {\n badCells.push(cell);\n }\n }\n });\n if (badCells.length) {\n this.relatedNodes(badCells);\n return false;\n }\n return true;\n }",
"enabled": true
},
{
"id": "td-headers-attr",
"evaluate": "function evaluate(node, options) {\n var cells = [];\n for (var rowIndex = 0, rowLength = node.rows.length; rowIndex < rowLength; rowIndex++) {\n var row = node.rows[rowIndex];\n for (var cellIndex = 0, cellLength = row.cells.length; cellIndex < cellLength; cellIndex++) {\n cells.push(row.cells[cellIndex]);\n }\n }\n var ids = cells.reduce(function(ids, cell) {\n if (cell.id) {\n ids.push(cell.id);\n }\n return ids;\n }, []);\n var badCells = cells.reduce(function(badCells, cell) {\n var isSelf, notOfTable;\n // Get a list all the values of the headers attribute\n var headers = (cell.getAttribute('headers') || '').split(/\\s/).reduce(function(headers, header) {\n header = header.trim();\n if (header) {\n headers.push(header);\n }\n return headers;\n }, []);\n if (headers.length !== 0) {\n // Check if the cell's id is in this list\n if (cell.id) {\n isSelf = headers.indexOf(cell.id.trim()) !== -1;\n }\n // Check if the headers are of cells inside the table\n notOfTable = headers.reduce(function(fail, header) {\n return fail || ids.indexOf(header) === -1;\n }, false);\n if (isSelf || notOfTable) {\n badCells.push(cell);\n }\n }\n return badCells;\n }, []);\n if (badCells.length > 0) {\n this.relatedNodes(badCells);\n return false;\n } else {\n return true;\n }\n }",
"enabled": true
},
{
"id": "th-has-data-cells",
"evaluate": "function evaluate(node, options) {\n var tableUtils = axe.commons.table;\n var cells = tableUtils.getAllCells(node);\n var checkResult = this;\n // Get a list of all headers reffed to in this rule\n var reffedHeaders = [];\n cells.forEach(function(cell) {\n var headers = cell.getAttribute('headers');\n if (headers) {\n reffedHeaders = reffedHeaders.concat(headers.split(/\\s+/));\n }\n var ariaLabel = cell.getAttribute('aria-labelledby');\n if (ariaLabel) {\n reffedHeaders = reffedHeaders.concat(ariaLabel.split(/\\s+/));\n }\n });\n // Get all the headers\n var headers = cells.filter(function(cell) {\n if (axe.commons.text.sanitize(cell.textContent) === '') {\n return false;\n }\n return cell.nodeName.toUpperCase() === 'TH' || [ 'rowheader', 'columnheader' ].indexOf(cell.getAttribute('role')) !== -1;\n });\n var tableGrid = tableUtils.toGrid(node);\n // Look for all the bad headers\n return headers.reduce(function(res, header) {\n if (header.id && reffedHeaders.indexOf(header.id) !== -1) {\n return !res ? res : true;\n }\n var hasCell = false;\n var pos = tableUtils.getCellPosition(header, tableGrid);\n // Look for any data cells or row headers that this might refer to\n if (tableUtils.isColumnHeader(header)) {\n hasCell = tableUtils.traverse('down', pos, tableGrid).reduce(function(out, cell) {\n return out || cell.textContent.trim() !== '' && !tableUtils.isColumnHeader(cell);\n }, false);\n }\n // Look for any data cells or column headers that this might refer to\n if (!hasCell && tableUtils.isRowHeader(header)) {\n hasCell = tableUtils.traverse('right', pos, tableGrid).reduce(function(out, cell) {\n return out || cell.textContent.trim() !== '' && !tableUtils.isRowHeader(cell);\n }, false);\n }\n // report the node as having failed\n if (!hasCell) {\n checkResult.relatedNodes(header);\n }\n return res && hasCell;\n }, true);\n }",
"enabled": true
},
{
"id": "description",
"evaluate": "function evaluate(node, options) {\n return !node.querySelector('track[kind=descriptions]');\n }",
"enabled": true
}
],
"data": {
"rules": {
"accesskeys": {
"description": "Ensures every accesskey attribute value is unique",
"help": "accesskey attribute value must be unique"
},
"area-alt": {
"description": "Ensures <area> elements of image maps have alternate text",
"help": "Active <area> elements must have alternate text",
"mapping": {
"ruleId": "010109-A",
"SC": "1.1.1",
"weight": 3,
"description": "Missing text equivalent for area."
}
},
"aria-allowed-attr": {
"description": "Ensures ARIA attributes are allowed for an element's role",
"help": "Elements must only use allowed ARIA attributes",
"mapping": {
"ruleId": "ARIA_ATTRIBUTE_VALID",
"SC": "4.1.1",
"weight": 0,
"description": "Checks all attributes that start with 'aria-' to ensure that they are all official WAI-ARIA attributes"
}
},
"aria-required-attr": {
"description": "Ensures elements with ARIA roles have all required ARIA attributes",
"help": "Required ARIA attributes must be provided",
"mapping": {
"ruleId": "ARIA_ROLE_REQUIRED_ATTRIBUTES",
"SC": "4.1.1",
"weight": 0,
"description": "Checks all elements that contain WAI-ARIA roles to ensure that all required aria- attributes are present"
}
},
"aria-required-children": {
"description": "Ensures elements with an ARIA role that require child roles contain them",
"help": "Certain ARIA roles must contain particular children",
"mapping": {
"ruleId": "ARIA_ROLE_REQUIRED_CHILDREN",
"SC": "4.1.1",
"weight": 0,
"description": "Checks all elements that contain a WAI-ARIA role to ensure that all required children roles are present"
}
},
"aria-required-parent": {
"description": "Ensures elements with an ARIA role that require parent roles are contained by them",
"help": "Certain ARIA roles must be contained by particular parents",
"mapping": {
"ruleId": "ARIA_ROLE_REQUIRED_PARENTS",
"SC": "4.1.1",
"weight": 0,
"description": "Checks all elements that contain a WAI-ARIA role to ensure that all required parent roles are present"
}
},
"aria-roles": {
"description": "Ensures all elements with a role attribute use a valid value",
"help": "ARIA roles used must conform to valid values",
"mapping": {
"ruleId": "ARIA_ROLE_VALID",
"SC": "4.1.1",
"weight": 0,
"description": "Checks all elements that contain the WAI-ARIA role attribute to ensure that the role value is valid"
}
},
"aria-valid-attr-value": {
"description": "Ensures all ARIA attributes have valid values",
"help": "ARIA attributes must conform to valid values",
"mapping": {
"ruleId": "ARIA_ATTRIBUTE_VALUES",
"SC": "4.1.1",
"weight": 0,
"description": "Checks all elements that contain WAI-ARIA atributes to ensure that the values of the attributes are valid"
}
},
"aria-valid-attr": {
"description": "Ensures attributes that begin with aria- are valid ARIA attributes",
"help": "ARIA attributes must conform to valid names",
"mapping": {
"ruleId": "ARIA_ATTRIBUTE_VALID",
"SC": "4.1.1",
"weight": 0,
"description": "Checks all elements that contain WAI-ARIA attributes to ensure that the attributes are valid attributes"
}
},
"audio-caption": {
"description": "Ensures <audio> elements have captions",
"help": "<audio> elements must have a captions track",
"mapping": {
"ruleId": "010402-D",
"SC": "1.2.2",
"weight": 0,
"description": "Checks the use of all <audio> element to ensure that the element contains a <caption> element"
}
},
"blink": {
"description": "Ensures <blink> elements are not used",
"help": "<blink> elements are deprecated and must not be used",
"mapping": {
"ruleId": "070201-A",
"SC": "2.2.2",
"weight": 0,
"description": "Checks to make sure that the <blink> tag is never used"
}
},
"button-name": {
"description": "Ensures buttons have discernible text",
"help": "Buttons must have discernible text",
"mapping": {
"ruleId": "SC412BTN",
"SC": "1.1.1",
"weight": 3,
"description": "Checks all <button> elements to ensure that they have a discernable accessible name"
}
},
"bypass": {
"description": "Ensures each page has at least one mechanism for a user to bypass navigation and jump straight to the content",
"help": "Page must have means to bypass repeated blocks",
"mapping": {
"ruleId": "N130601-A",
"SC": "2.4.1",
"weight": 3,
"description": "There is no mechanism to bypass navigation links."
}
},
"color-contrast": {
"description": "Ensures the contrast between foreground and background colors meets WCAG 2 AA contrast ratio thresholds",
"help": "Elements must have sufficient color contrast",
"mapping": {
"ruleId": "020201-A",
"SC": "1.4.3",
"weight": 2,
"description": "Checks all elements to ensure that the contrast between the foreground and the background meets the WCAG 2 AA contrast ratio thresholds."
}
},
"definition-list": {
"description": "Ensures <dl> elements are structured correctly",
"help": "<dl> elements must only directly contain properly-ordered <dt> and <dd> groups, <script> or <template> elements",
"mapping": {
"ruleId": "SC411VAL",
"SC": "4.1.1",
"weight": 0,
"description": "Ensures that all <dl> elements are structured correctly"
}
},
"dlitem": {
"description": "Ensures <dt> and <dd> elements are contained by a <dl>",
"help": "<dt> and <dd> elements must be contained by a <dl>",
"mapping": {
"ruleId": "SC411VAL",
"SC": "4.1.1",
"weight": 0,
"description": "Ensures that all <dd> and <dt> elements have a <dl> as a parent"
}
},
"document-title": {
"description": "Ensures each HTML document contains a non-empty <title> element",
"help": "Documents must have <title> element to aid in navigation",
"mapping": {
"ruleId": "130201-C",
"SC": "2.4.2",
"weight": 0,
"description": "Supply a valid title element for this document that is non-empty and useful."
}
},
"duplicate-id": {
"description": "Ensures every id attribute value is unique",
"help": "id attribute value must be unique",
"mapping": {
"ruleId": "SC411NID",
"SC": "4.1.1",
"weight": 0,
"description": "Ensures that each element on the page with an ID attribute has a unique ID attribute value"
}
},
"frame-title": {
"description": "Ensures <iframe> and <frame> elements contain a non-empty title attribute",
"help": "Frames must have title attribute",
"mapping": {
"ruleId": "120101-A",
"SC": "4.1.2",
"weight": 2,
"description": "Ensure that all iframe and frame elements contain a unique and non-empty title attribute"
}
},
"html-has-lang": {
"description": "Ensures every HTML document has a lang attribute",
"help": "<html> element must have a lang attribute"
},
"html-lang-valid": {
"description": "Ensures the lang attribute of the <html> element has a valid value",
"help": "<html> element must have a valid value for the lang attribute"
},
"image-alt": {
"description": "Ensures <img> elements have alternate text or a role of none or presentation",
"help": "Images must have alternate text",
"mapping": {
"ruleId": "IMG_NO_ALT",
"SC": "1.1.1",
"weight": 0,
"description": "Ensures that every <img> element has an alternative text or is marked as presentational"
}
},
"input-image-alt": {
"description": "Ensures <input type=\"image\"> elements have alternate text",
"help": "Image buttons must have alternate text",
"mapping": {
"ruleId": "010103-A",
"SC": "1.1.1",
"weight": 0,
"description": "Ensures that every <input> that represents an image button has an accessible name"
}
},
"label": {
"description": "Ensures every form element has a label",
"help": "Form elements must have labels",
"mapping": {
"ruleId": "120401-E",
"SC": "1.3.1",
"weight": 0,
"description": "Ensures that every input element that requires a label, has an appropriate label"
}
},
"layout-table": {
"description": "Ensures presentational <table> elements do not use <th>, <caption> elements or the summary attribute",
"help": "Layout tables must not use data table elements",
"mapping": {
"ruleId": "050401-A",
"SC": "1.3.1",
"weight": 1,
"description": "Ensures that <table> elements that are being used for layout do not contain markup only relevant for data tables"
}
},
"link-in-text-block": {
"description": "Links can be distinguished without relying on color",
"help": "Links must be distinguished from surrounding text in a way that does not rely on color"
},
"link-name": {
"description": "Ensures links have discernible text",
"help": "Links must have discernible text",
"mapping": {
"ruleId": "SC412LNKN",
"SC": "4.1.2",
"weight": 1,
"description": "Ensures that every link has an accessible name"
}
},
"list": {
"description": "Ensures that lists are structured correctly",
"help": "<ul> and <ol> must only directly contain <li>, <script> or <template> elements",
"mapping": {
"ruleId": "030601-C",
"SC": "1.3.1",
"weight": 1,
"description": "Ensures that lists are structured correctly"
}
},
"listitem": {
"description": "Ensures <li> elements are used semantically",
"help": "<li> elements must be contained in a <ul> or <ol>",
"mapping": {
"ruleId": "030601-A",
"SC": "1.3.1",
"weight": 1,
"description": "Ensures that every list item is used semantically"
}
},
"marquee": {
"description": "Ensures <marquee> elements are not used",
"help": "<marquee> elements are deprecated and must not be used",
"mapping": {
"ruleId": "070301-A",
"SC": "2.2.2",
"weight": 0,
"description": "Ensures that the deprecated <marquee> tag is not used"
}
},
"meta-refresh": {
"description": "Ensures <meta http-equiv=\"refresh\"> is not used",
"help": "Timed refresh must not exist",
"mapping": {
"ruleId": "070401-A",
"SC": "2.2.4",
"weight": 1,
"description": "Ensures that <meta> refresh is not used"
}
},
"meta-viewport": {
"description": "Ensures <meta name=\"viewport\"> does not disable text scaling and zooming",
"help": "Zooming and scaling must not be disabled",
"mapping": {}
},
"object-alt": {
"description": "Ensures <object> elements have alternate text",
"help": "<object> elements must have alternate text",
"mapping": {
"ruleId": "010105-A",
"SC": "1.1.1",
"weight": 0,
"description": "Ensures that every <object> element has a text alternative"
}
},
"server-side-image-map": {
"description": "Ensures that server-side image maps are not used",
"help": "Server-side image maps must not be used",
"mapping": {
"ruleId": "090101-A",
"SC": "2.1.1",
"weight": 0,
"description": "Ensures that server-side image maps are never used"
}
},
"table-fake-caption": {
"description": "Ensure that tables with a caption use the <caption> element.",
"help": "Data or header cells should not be used to give caption to a data table.",
"mapping": {}
},
"td-has-header": {
"description": "Ensure that each non-empty data cell in a large table has one or more table headers",
"help": "All non-empty td element in table larger than 3 by 3 must have an associated table header",
"mapping": {}
},
"td-headers-attr": {
"description": "Ensure that each cell in a table using the headers refers to another cell in that table",
"help": "All cells in a table element that use the headers attribute must only refer to other cells of that same table"
},
"th-has-data-cells": {
"description": "Ensure that each table header in a data table refers to data cells",
"help": "All th element and elements with role=columnheader/rowheader must data cells which it describes",
"mapping": {}
},
"valid-lang": {
"description": "Ensures lang attributes have valid values",
"help": "lang attribute must have a valid value",
"mapping": {
"ruleId": "040101-B",
"SC": "3.1.2",
"weight": 1,
"description": "Ensures that when the 'lang' attribute is used, it has a valid value"
}
},
"video-caption": {
"description": "Ensures <video> elements have captions",
"help": "<video> elements must have captions",
"mapping": {
"ruleId": "010402-D",
"SC": "1.2.2",
"weight": 1,
"description": "Ensures that the HTML5 <video> tag is captioned"
}
},
"video-description": {
"description": "Ensures <video> elements have audio descriptions",
"help": "<video> elements must have an audio description track",
"mapping": {
"ruleId": "010301-Q1",
"SC": "1.2.5",
"weight": 1,
"description": "Ensures that every <video> tag has an audio description"
}
}
},
"checks": {
"accesskeys": {
"impact": "critical"
},
"non-empty-alt": {
"impact": "critical"
},
"non-empty-title": {
"impact": "critical"
},
"aria-label": {
"impact": "critical"
},
"aria-labelledby": {
"impact": "critical"
},
"aria-allowed-attr": {
"impact": "critical"
},
"aria-required-attr": {
"impact": "critical"
},
"aria-required-children": {
"impact": "critical"
},
"aria-required-parent": {
"impact": "critical"
},
"invalidrole": {
"impact": "critical"
},
"abstractrole": {
"impact": "serious"
},
"aria-valid-attr-value": {
"impact": "critical"
},
"aria-valid-attr": {
"impact": "critical"
},
"caption": {
"impact": "critical"
},
"is-on-screen": {
"impact": "minor"
},
"non-empty-if-present": {
"impact": "critical"
},
"non-empty-value": {
"impact": "critical"
},
"button-has-visible-text": {
"impact": "critical"
},
"role-presentation": {
"impact": "moderate"
},
"role-none": {
"impact": "moderate"
},
"focusable-no-name": {
"impact": "serious"
},
"internal-link-present": {
"impact": "critical"
},
"header-present": {
"impact": "moderate"
},
"landmark": {
"impact": "serious"
},
"color-contrast": {
"impact": "critical"
},
"structured-dlitems": {
"impact": "serious"
},
"only-dlitems": {
"impact": "serious"
},
"dlitem": {
"impact": "serious"
},
"doc-has-title": {
"impact": "moderate"
},
"duplicate-id": {
"impact": "critical"
},
"has-lang": {
"impact": "serious"
},
"valid-lang": {
"impact": "serious"
},
"has-alt": {
"impact": "critical"
},
"implicit-label": {
"impact": "critical"
},
"explicit-label": {
"impact": "critical"
},
"help-same-as-label": {
"impact": "minor"
},
"multiple-label": {
"impact": "serious"
},
"has-th": {
"impact": "serious"
},
"has-caption": {
"impact": "serious"
},
"has-summary": {
"impact": "serious"
},
"link-in-text-block": {
"impact": "critical"
},
"has-visible-text": {
"impact": "critical"
},
"only-listitems": {
"impact": "serious"
},
"listitem": {
"impact": "critical"
},
"meta-refresh": {
"impact": "critical"
},
"meta-viewport": {
"impact": "critical"
},
"exists": {
"impact": "minor"
},
"caption-faked": {
"impact": "critical"
},
"td-has-header": {
"impact": "critical"
},
"td-headers-attr": {
"impact": "serious"
},
"th-has-data-cells": {
"impact": "critical"
},
"description": {
"impact": "serious"
}
}
}
}
{
"name": "attest-rules",
"version": "1.0.0",
"author": "Tony Kornmeier"
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment