blob: b5ffdfe704943d87de4e2a16f2963fcd83b6192d [file] [edit]
#!/usr/bin/env node
/*
* This content is licensed according to the W3C Software License at
* https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document
*
* File: coverage-report.js
*/
const fs = require('fs');
const path = require('path');
const glob = require('glob');
const cheerio = require('cheerio');
const HTMLParser = require('node-html-parser');
const joinPaths = (...segments) => {
// Normalize paths to avoid backslash-based paths on Windows
return path.join(...segments).replace(/\\/g, '/');
};
const coverageReportPath = joinPaths(
__dirname,
'../content/about/coverage-and-quality/coverage-and-quality-report.html'
);
const templatePath = joinPaths(__dirname, 'coverage-report.template');
const csvRoleFilePath = joinPaths(
__dirname,
'../content/about/coverage-and-quality/role-coverage.csv'
);
const csvPropFilePath = joinPaths(
__dirname,
'../content/about/coverage-and-quality/prop-coverage.csv'
);
let output = fs.readFileSync(templatePath, function (err) {
console.log('Error reading aria index:', err);
});
const $ = cheerio.load(output);
const ariaRoles = [
'alert',
'alertdialog',
'application',
'article',
'banner',
'button',
'caption',
'cell',
'checkbox',
'code',
'columnheader',
'combobox',
'complementary',
'contentinfo',
'definition',
'deletion',
'dialog',
'directory',
'document',
'emphasis',
'feed',
'figure',
'form',
'generic',
'grid',
'gridcell',
'group',
'heading',
'img',
'input',
'insertion',
'link',
'list',
'listbox',
'listitem',
'log',
'main',
'marquee',
'math',
'menu',
'menubar',
'menuitem',
'menuitemcheckbox',
'menuitemradio',
'meter',
'navigation',
'none',
'note',
'option',
'paragraph',
'presentation',
'progressbar',
'radio',
'radiogroup',
'region',
'row',
'rowgroup',
'rowheader',
'scrollbar',
'search',
'searchbox',
'separator',
'slider',
'spinbutton',
'status',
'switch',
'tab',
'table',
'tablist',
'tabpanel',
'term',
'textbox',
'timer',
'toolbar',
'tooltip',
'tree',
'treegrid',
'treeitem',
];
const ariaPropertiesAndStates = [
'aria-activedescendant',
'aria-atomic',
'aria-autocomplete',
'aria-busy',
'aria-checked',
'aria-colcount',
'aria-colindex',
'aria-colspan',
'aria-controls',
'aria-current',
'aria-describedby',
'aria-details',
'aria-disabled',
'aria-dropeffect',
'aria-errormessage',
'aria-expanded',
'aria-flowto',
'aria-grabbed',
'aria-haspopup',
'aria-hidden',
'aria-invalid',
'aria-keyshortcuts',
'aria-label',
'aria-labelledby',
'aria-level',
'aria-live',
'aria-modal',
'aria-multiline',
'aria-multiselectable',
'aria-orientation',
'aria-owns',
'aria-placeholder',
'aria-posinset',
'aria-pressed',
'aria-readonly',
'aria-relevant',
'aria-required',
'aria-roledescription',
'aria-rowcount',
'aria-rowindex',
'aria-rowspan',
'aria-selected',
'aria-setsize',
'aria-sort',
'aria-valuemax',
'aria-valuemin',
'aria-valuenow',
'aria-valuetext',
];
let indexOfRolesInExamples = {};
ariaRoles.forEach(function (role) {
indexOfRolesInExamples[role] = [];
});
let indexOfRolesInGuidance = {};
ariaRoles.forEach(function (role) {
indexOfRolesInGuidance[role] = [];
});
let indexOfPropertiesAndStatesInExamples = {};
ariaPropertiesAndStates.forEach(function (prop) {
indexOfPropertiesAndStatesInExamples[prop] = [];
});
let indexOfPropertiesAndStatesInGuidance = {};
ariaPropertiesAndStates.forEach(function (prop) {
indexOfPropertiesAndStatesInGuidance[prop] = [];
});
let indexOfExamples = [];
console.log('Generating index...');
function getRoles(html) {
let roles = [];
let exampleRoles = html.querySelectorAll(
'table.data.attributes tbody tr > th:first-child code'
);
for (let i = 0; i < exampleRoles.length; i++) {
let code = exampleRoles[i].textContent.toLowerCase().trim();
for (let j = 0; j < ariaRoles.length; j++) {
const hasRole = RegExp('\\b' + ariaRoles[j] + '\\b');
if (hasRole.test(code) && roles.indexOf(ariaRoles[j]) < 0) {
console.log(' [role]: ' + code);
roles.push(ariaRoles[j]);
}
}
}
return roles;
}
function getPropertiesAndStates(html) {
let propertiesAndStates = [];
let exampleProps = html.querySelectorAll(
'table.data.attributes tbody tr > th:nth-child(2) code'
);
for (let i = 0; i < exampleProps.length; i++) {
let code = exampleProps[i].textContent.toLowerCase().trim().split('=')[0];
for (let j = 0; j < ariaPropertiesAndStates.length; j++) {
const hasPropOrState = RegExp('\\b' + ariaPropertiesAndStates[j] + '\\b');
if (
hasPropOrState.test(code) &&
propertiesAndStates.indexOf(ariaPropertiesAndStates[j]) < 0
) {
console.log(' [propertyOrState]: ' + code);
propertiesAndStates.push(ariaPropertiesAndStates[j]);
}
}
}
return propertiesAndStates;
}
function addExampleToRoles(roles, example) {
let items = [];
for (let i = 0; i < roles.length; i++) {
let role = roles[i];
if (role === '') {
continue;
}
if (!indexOfRolesInExamples[role]) {
indexOfRolesInExamples[role] = [];
}
indexOfRolesInExamples[role].push(example);
items.push(role);
}
return items;
}
function addExampleToPropertiesAndStates(props, example) {
let items = [];
for (let i = 0; i < props.length; i++) {
let prop = props[i];
if (prop === '') {
continue;
}
if (!indexOfPropertiesAndStatesInExamples[prop]) {
indexOfPropertiesAndStatesInExamples[prop] = [];
}
indexOfPropertiesAndStatesInExamples[prop].push(example);
items.push(prop);
}
return items;
}
function addLandmarkRole(landmark, hasLabel, title, ref) {
let example = {
title: title,
ref: ref,
};
addExampleToRoles(landmark, example);
if (hasLabel) {
addExampleToPropertiesAndStates(['aria-labelledby'], example);
}
}
function getNumberOfReferences(data, target, toLower) {
if (typeof toLower === 'boolean' && toLower) {
data = data.toLowerCase();
target = target.toLowerCase();
}
const hasTarget = RegExp('\\b' + target + '\\b', 'g');
let count = 0;
while (hasTarget.test(data)) {
count += 1;
hasTarget.lastIndex;
}
return count;
}
function getUniqueRolesInExample(html, dataJS) {
let roles = [];
ariaRoles.forEach((role) => {
let items = html.querySelectorAll('#ex1 [role=' + role + ']');
if (items.length) {
roles.push(role);
} else {
let items = html.querySelectorAll('#ex2 [role=' + role + ']');
if (items.length) {
roles.push(role);
} else {
let items = html.querySelectorAll('#ex3 [role=' + role + ']');
if (items.length) {
roles.push(role);
} else {
let id = getExampleCodeId(html);
items = html.querySelectorAll('#' + id + ' [role=' + role + ']');
if (items.length) {
roles.push(role);
} else {
// Check Javascript
const hasRole1 = RegExp(".role = '" + role, 'g');
const hasRole2 = RegExp('.role = "' + role, 'g');
if (hasRole1.test(dataJS) || hasRole2.test(dataJS)) {
roles.push(role);
} else {
// Check for elements with default landmark roles
switch (role) {
case 'banner':
items = html.querySelectorAll('#' + id + ' header');
if (items.length) {
roles.push(role);
}
break;
case 'complementary':
items = html.querySelectorAll('#' + id + ' aside');
if (items.length) {
roles.push(role);
}
break;
case 'contentinfo':
items = html.querySelectorAll('#' + id + ' footer');
if (items.length) {
roles.push(role);
}
break;
case 'navigation':
items = html.querySelectorAll('#' + id + ' nav');
if (items.length) {
roles.push(role);
}
break;
case 'region':
items = html.querySelectorAll(
'#' + id + ' section[aria-label]'
);
if (items.length) {
roles.push(role);
}
items = html.querySelectorAll(
'#' + id + ' section[aria-labelledby]'
);
if (items.length) {
roles.push(role);
}
items = html.querySelectorAll('#' + id + ' section[title]');
if (items.length) {
roles.push(role);
}
break;
default:
break;
}
}
}
}
}
}
});
// roles.forEach((role) => console.log(' [Example role]: ' + role));
console.log(' [Example Roles]: ' + roles.length);
return roles;
}
function getUniqueAriaAttributeInExample(html, dataJS) {
let attributes = [];
ariaPropertiesAndStates.forEach(function (attribute) {
let items = html.querySelectorAll('#ex1 [' + attribute + ']');
if (items.length) {
attributes.push(attribute);
} else {
items = html.querySelectorAll('#ex2 [' + attribute + ']');
if (items.length) {
attributes.push(attribute);
} else {
items = html.querySelectorAll('#ex3 [' + attribute + ']');
if (items.length) {
attributes.push(attribute);
} else {
let id = getExampleCodeId(html);
items = html.querySelectorAll('#' + id + ' [' + attribute + ']');
if (items.length) {
attributes.push(attribute);
} else {
const hasAttribute1 = RegExp(attribute, 'g');
let parts = attribute.split('-');
let attribute2 =
'.' +
parts[0] +
parts[1][0].toUpperCase() +
parts[1].substring(1, parts[1].length - 1);
const hasAttribute2 = RegExp(attribute2, 'g');
if (hasAttribute1.test(dataJS) || hasAttribute2.test(dataJS)) {
attributes.push(attribute);
}
}
}
}
}
});
attributes.forEach((attribute) =>
console.log(' [Example aria-* Attribute]: ' + attribute)
);
console.log(' [Example aria-* Attributes]: ' + attributes.length);
return attributes;
}
function getExampleCodeId(html) {
let startSeparator = html.querySelector('[role="separator"]');
if (startSeparator && startSeparator.nextElementSibling.id) {
return startSeparator.nextElementSibling.id;
}
return 'not found';
}
// Index roles, properties and states used in examples
glob
.sync('content/patterns/!(landmarks)/examples/**/!(index).html', {
cwd: joinPaths(__dirname, '..'),
nodir: true,
})
.forEach(function (file) {
console.log('[file]: ' + file);
let dir = path.dirname(file);
console.log('[ dir]: ' + dir);
if (file.toLowerCase().indexOf('deprecated') >= 0) {
console.log(' [ignored]');
return;
}
let data = fs.readFileSync(file, 'utf8');
let html = HTMLParser.parse(data);
let dataJS = '';
let scripts = html.querySelectorAll('script[src]');
for (let i = 0; i < scripts.length; i++) {
let src = scripts[i].getAttribute('src');
if (
src.indexOf('examples.js') < 0 &&
src.indexOf('highlight.pack.js') < 0 &&
src.indexOf('app.js') < 0 &&
src.indexOf('skipto.js') < 0
) {
console.log(' [script]: ' + src);
dataJS += fs.readFileSync(joinPaths(dir, src), 'utf8');
}
dataJS += ' ';
}
let dataCSS = '';
let cssFiles = html.querySelectorAll('link[href]');
for (let i = 0; i < cssFiles.length; i++) {
let href = cssFiles[i].getAttribute('href');
if (
href.indexOf('base.css') < 0 &&
href.indexOf('core.css') < 0 &&
href.indexOf('all.css') < 0
) {
console.log(' [link]: ' + href);
dataCSS += fs.readFileSync(joinPaths(dir, href), 'utf8');
}
dataCSS += ' ';
}
let ref = joinPaths('../../..', file);
let title = html
.querySelector('title')
.textContent.split('|')[0]
.replace('Examples', '')
.replace('Example of', '')
.replace('Example', '')
.trim();
let example = {
title: title,
ref: ref,
codeId: getExampleCodeId(html),
exampleRoles: getUniqueRolesInExample(html, dataJS),
exampleAttributes: getUniqueAriaAttributeInExample(html, dataJS),
highContrast: data.toLowerCase().indexOf('high contrast') > 0,
svgHTML: html.querySelectorAll('svg').length,
svgCSS: getNumberOfReferences(dataCSS, 'svg', true),
forcedColors: getNumberOfReferences(dataCSS, 'forced-colors'),
currentColor: getNumberOfReferences(dataCSS, 'currentColor', true),
svgJS: getNumberOfReferences(dataJS, 'svg', true),
classJS: getNumberOfReferences(dataJS, 'constructor\\('),
prototypeJS: getNumberOfReferences(dataJS, '.prototype.'),
keyCodeJS: getNumberOfReferences(dataJS, '.keyCode'),
whichJS: getNumberOfReferences(dataJS, '.which'),
hasExternalJS: dataJS.length > 0,
mouseDown: getNumberOfReferences(dataJS, 'mousedown', true),
mouseEnter: getNumberOfReferences(dataJS, 'mouseenter', true),
mouseLeave: getNumberOfReferences(dataJS, 'mouseleave', true),
mouseMove: getNumberOfReferences(dataJS, 'mousemove', true),
mouseOut: getNumberOfReferences(dataJS, 'mouseout', true),
mouseOver: getNumberOfReferences(dataJS, 'mouseover', true),
mouseUp: getNumberOfReferences(dataJS, 'mouseup', true),
pointerDown: getNumberOfReferences(dataJS, 'pointerdown', true),
pointerEnter: getNumberOfReferences(dataJS, 'pointerenter', true),
pointerLeave: getNumberOfReferences(dataJS, 'pointerleave', true),
pointerMove: getNumberOfReferences(dataJS, 'pointermove', true),
pointerOut: getNumberOfReferences(dataJS, 'pointerout', true),
pointerOver: getNumberOfReferences(dataJS, 'pointerover', true),
pointerUp: getNumberOfReferences(dataJS, 'pointerup', true),
};
(example.documentationRoles = addExampleToRoles(getRoles(html), example)),
console.log(
' [Documentation Roles]: ' + example.documentationRoles.length
);
(example.documentationAttributes = addExampleToPropertiesAndStates(
getPropertiesAndStates(html),
example
)),
console.log(
' [Documentation aria-* Attributes]: ' +
example.documentationAttributes.length
);
indexOfExamples.push(example);
});
// Index roles, properties and states used in guidance
function getClosestId(node) {
let id = '';
node = node.parentNode;
while (node) {
if (node.id) {
return node.id;
}
node = node.parentNode;
}
return id;
}
function addGuidanceToRole(role, url, label, id) {
let r = {};
r.title = label;
r.ref = url + '#' + id;
if (!indexOfRolesInGuidance[role]) {
indexOfRolesInGuidance[role] = [];
}
indexOfRolesInGuidance[role].push(r);
}
function addGuidanceToPropertyOrState(prop, url, label, id) {
let r = {};
r.title = label;
r.ref = url + '#' + id;
if (!indexOfPropertiesAndStatesInGuidance[prop]) {
indexOfPropertiesAndStatesInGuidance[prop] = [];
}
indexOfPropertiesAndStatesInGuidance[prop].push(r);
}
function getRolesPropertiesAndStatesFromHeaders(html, url) {
let dataHeadings = html.querySelectorAll('h1, h2, h3, h4, h4, h5, h6');
for (let i = 0; i < dataHeadings.length; i++) {
let dataHeading = dataHeadings[i];
let tagName = dataHeading.tagName;
let content = dataHeading.textContent;
let contentItems = content.toLowerCase().split(' ');
ariaRoles.forEach(function (role) {
if (contentItems.indexOf(role) >= 0) {
console.log(tagName + ': ' + role + ', ' + content);
addGuidanceToRole(role, url, content, getClosestId(dataHeading));
}
});
ariaPropertiesAndStates.forEach(function (prop) {
if (contentItems.indexOf(prop) >= 0) {
console.log(tagName + ': ' + prop + ', ' + content);
addGuidanceToPropertyOrState(
prop,
url,
content,
getClosestId(dataHeading)
);
}
});
}
}
function getRolesFromDataAttributesOnHeaders(html, url) {
let dataRoles = html.querySelectorAll('[data-aria-roles]');
for (let i = 0; i < dataRoles.length; i++) {
let dataRole = dataRoles[i];
let roles = dataRole.textContent.split(' ');
let content = dataRole.textContent;
ariaRoles.forEach(function (role) {
if (roles.indexOf(role) >= 0) {
console.log('data: ' + role + ', ' + content);
addGuidanceToRole(role, url, content + ' (D)', getClosestId(dataRole));
}
});
}
}
function getPropertiesAndStatesFromDataAttributesOnHeaders(html, url) {
let dataProps = html.querySelectorAll('[data-aria-props]');
for (let i = 0; i < dataProps.length; i++) {
let dataProp = dataProps[i];
let props = dataProp.textContent.split(' ');
let content = dataProp.textContent;
ariaPropertiesAndStates.forEach(function (prop) {
if (props.indexOf(prop) >= 0) {
console.log('data: ' + prop + ', ' + content);
addGuidanceToPropertyOrState(
prop,
url,
content + ' (D)',
getClosestId(dataProp)
);
}
});
}
}
function getRolesPropertiesAndStatesFromGuidance(html, url) {
getRolesPropertiesAndStatesFromHeaders(html, url);
getRolesFromDataAttributesOnHeaders(html, url);
getPropertiesAndStatesFromDataAttributesOnHeaders(html, url);
}
const patternFiles = glob.sync('content/patterns/!(landmarks)/*-pattern.html', {
cwd: joinPaths(__dirname, '..'),
});
const practiceFiles = glob.sync('content/practices/*/*-practice.html', {
cwd: joinPaths(__dirname, '..'),
});
const guidanceFiles = [...patternFiles, ...practiceFiles];
guidanceFiles.forEach(function (file) {
let data = fs.readFileSync(file, 'utf8');
let html = HTMLParser.parse(data);
getRolesPropertiesAndStatesFromGuidance(html, joinPaths('../../../', file));
});
// Add landmark examples, since they are a different format
addLandmarkRole(
['banner'],
false,
'Banner Landmark',
'../../../content/patterns/landmarks/examples/banner.html'
);
addLandmarkRole(
['complementary'],
true,
'Complementary Landmark',
'../../../content/patterns/landmarks/examples/complementary.html'
);
addLandmarkRole(
['contentinfo'],
false,
'Contentinfo Landmark',
'../../../content/patterns/landmarks/examples/contentinfo.html'
);
addLandmarkRole(
['form'],
true,
'Form Landmark',
'../../../content/patterns/landmarks/examples/form.html'
);
addLandmarkRole(
['main'],
true,
'Main Landmark',
'../../../content/patterns/landmarks/examples/main.html'
);
addLandmarkRole(
['navigation'],
true,
'Navigation Landmark',
'../../../content/patterns/landmarks/examples/navigation.html'
);
addLandmarkRole(
['region'],
true,
'Region Landmark',
'../../../content/patterns/landmarks/examples/region.html'
);
addLandmarkRole(
['search'],
true,
'Search Landmark',
'../../../content/patterns/landmarks/examples/search.html'
);
function getListItem(item) {
let highContrast = '';
if (item.highContrast) {
highContrast = ' (<abbr title="High Contrast Support">HC</abbr>)';
}
return `
<li><a href="${item.ref}">${item.title}</a>${highContrast}</li>`;
}
function getListHTML(list) {
let html = '';
if (list.length === 1) {
html = `<a href="${list[0].ref}">${list[0].title}</a>\n`;
} else {
if (list.length > 1) {
html = `<ul>${list.map(getListItem).join('')}
</ul>\n`;
}
}
return html;
}
let sortedRoles = Object.getOwnPropertyNames(indexOfRolesInExamples).sort();
let countNoExamples = 0;
let countOneExample = 0;
let countMoreThanOneExample = 0;
let RoleWithNoExamples = sortedRoles.reduce(function (set, role) {
let examples = indexOfRolesInExamples[role];
let guidance = indexOfRolesInGuidance[role];
if (examples.length === 0 && guidance.length == 0) {
countNoExamples += 1;
return `${set}
<li><code>${role}</code></li>`;
}
return `${set}`;
}, '');
$('#roles_with_no_examples_ul').html(RoleWithNoExamples);
let RoleWithOneExample = sortedRoles.reduce(function (set, role) {
let examples = indexOfRolesInExamples[role];
let guidance = indexOfRolesInGuidance[role];
if (
(examples.length === 1 && guidance.length === 0) ||
(examples.length === 0 && guidance.length === 1) ||
(examples.length === 1 && guidance.length === 1)
) {
countOneExample += 1;
return `${set}
<tr>
<td><code>${role}</code></td>
<td>${getListHTML(guidance)}</td>
<td>${getListHTML(examples)}</td>
</tr>`;
}
return `${set}`;
}, '');
$('#roles_with_one_example_tbody').html(RoleWithOneExample);
let RoleWithMoreThanOneExample = sortedRoles.reduce(function (set, role) {
let examples = indexOfRolesInExamples[role];
let guidance = indexOfRolesInGuidance[role];
if (examples.length > 1 || guidance.length > 1) {
countMoreThanOneExample += 1;
return `${set}
<tr>
<td><code>${role}</code></td>
<td>${getListHTML(guidance)}</td>
<td>${getListHTML(examples)}</td>
</tr>`;
}
return `${set}`;
}, '');
$('#roles_with_more_than_one_tbody').html(RoleWithMoreThanOneExample);
$('.roles_with_no_examples_count').html(countNoExamples.toString());
$('.roles_with_one_example_count').html(countOneExample.toString());
$('.roles_with_more_than_one_examples_count').html(
countMoreThanOneExample.toString()
);
// Properties and States
let sortedPropertiesAndStates = Object.getOwnPropertyNames(
indexOfPropertiesAndStatesInExamples
).sort();
countNoExamples = 0;
countOneExample = 0;
countMoreThanOneExample = 0;
let PropsWithNoExamples = sortedPropertiesAndStates.reduce(function (
set,
prop
) {
let examples = indexOfPropertiesAndStatesInExamples[prop];
let guidance = indexOfPropertiesAndStatesInGuidance[prop];
if (examples.length === 0 && guidance.length === 0) {
countNoExamples += 1;
return `${set}
<li><code>${prop}</code></li>`;
}
return `${set}`;
}, '');
$('#props_with_no_examples_ul').html(PropsWithNoExamples);
$('.props_with_no_examples_count').html(countNoExamples.toString());
let PropsWithOneExample = sortedPropertiesAndStates.reduce(function (
set,
prop
) {
let examples = indexOfPropertiesAndStatesInExamples[prop];
let guidance = indexOfPropertiesAndStatesInGuidance[prop];
if (
(examples.length === 1 && guidance.length === 0) ||
(examples.length === 0 && guidance.length === 1) ||
(examples.length === 1 && guidance.length === 1)
) {
countOneExample += 1;
return `${set}
<tr>
<td><code>${prop}</code></td>
<td>${getListHTML(guidance)}</td>
<td>${getListHTML(examples)}</td>
</tr>`;
}
return `${set}`;
}, '');
$('#props_with_one_example_tbody').html(PropsWithOneExample);
$('.props_with_one_example_count').html(countOneExample.toString());
let PropsWithMoreThanOneExample = sortedPropertiesAndStates.reduce(function (
set,
prop
) {
let examples = indexOfPropertiesAndStatesInExamples[prop];
let guidance = indexOfPropertiesAndStatesInGuidance[prop];
if (examples.length > 1 || guidance.length > 1) {
countMoreThanOneExample += 1;
return `${set}
<tr>
<td><code>${prop}</code></td>
<td>${getListHTML(guidance)}</td>
<td>${getListHTML(examples)}</td>
</tr>`;
}
return `${set}`;
}, '');
$('#props_with_more_than_one_tbody').html(PropsWithMoreThanOneExample);
$('.props_with_more_than_one_examples_count').html(
countMoreThanOneExample.toString()
);
// Example Coding Practices
function htmlYesOrNo(flag) {
return flag ? 'Yes' : '';
}
let IndexOfExampleCodingPractices = indexOfExamples.reduce(function (
set,
example
) {
function getDifference(a1, a2) {
let diff = [];
a1.forEach((item) => {
if (!a2.includes(item)) {
diff.push(item);
}
});
a2.forEach((item) => {
if (!a1.includes(item)) {
diff.push(item);
}
});
return diff;
}
let using = '';
if (example.hasExternalJS) {
if (example.classJS) {
using += 'class';
}
if (example.prototypeJS) {
if (example.classJS) {
using += ', ';
}
using += 'prototype';
}
}
let checkDocumentation = [];
let rolesDiff = getDifference(
example.exampleRoles,
example.documentationRoles
);
let attributesDiff = getDifference(
example.exampleAttributes,
example.documentationAttributes
);
if (rolesDiff.length) {
checkDocumentation.push(rolesDiff);
}
if (attributesDiff.length) {
checkDocumentation.push(attributesDiff);
}
return `${set}
<tr>
<td><a href="${example.ref}">${example.title}</code></td>
<td>${using}</td>
<td>${htmlYesOrNo(example.keyCodeJS)}</td>
<td>${htmlYesOrNo(example.whichJS)}</td>
<td>${htmlYesOrNo(example.highContrast)}</td>
<td>${example.codeId}</td>
<td>${example.exampleRoles.length}</td>
<td>${example.documentationRoles.length}</td>
<td>${example.exampleAttributes.length}</td>
<td>${example.documentationAttributes.length}</td>
<td>${checkDocumentation}</td>
</tr>`;
}, '');
let IndexOfExampleGraphics = indexOfExamples.reduce(function (set, example) {
let count = example.svgHTML;
count += example.svgCSS;
count += example.svgJS;
count += example.forcedColors;
count += example.currentColor;
if (count === 0) {
return `${set}`;
}
return `${set}
<tr>
<td><a href="${example.ref}">${example.title}</code></td>
<td>${htmlYesOrNo(example.svgHTML)}</td>
<td>${htmlYesOrNo(example.svgCSS)}</td>
<td>${htmlYesOrNo(example.svgJS)}</td>
<td>${htmlYesOrNo(example.forcedColors)}</td>
<td>${htmlYesOrNo(example.currentColor)}</td>
</tr>`;
}, '');
let IndexOfExampleMousePointer = indexOfExamples.reduce(function (
set,
example
) {
let mouseCount = example.mouseDown;
mouseCount += example.mouseEnter;
mouseCount += example.mouseLeave;
mouseCount += example.mouseMove;
mouseCount += example.mouseOut;
mouseCount += example.mouseOver;
mouseCount += example.mouseUp;
let pointerCount = example.pointerDown;
pointerCount += example.pointerEnter;
pointerCount += example.pointerLeave;
pointerCount += example.pointerMove;
pointerCount += example.pointerOut;
pointerCount += example.pointerOver;
pointerCount += example.pointerUp;
if (mouseCount === 0 && pointerCount === 0) {
return `${set}`;
}
return `${set}
<tr>
<td><a href="${example.ref}">${example.title}</code></td>
<td>${htmlYesOrNo(mouseCount)}</td>
<td>${htmlYesOrNo(pointerCount)}</td>
</tr>`;
}, '');
let countClass = indexOfExamples.reduce(function (set, example) {
return set + (example.classJS ? 1 : 0);
}, 0);
let countPrototype = indexOfExamples.reduce(function (set, example) {
return set + (example.prototypeJS ? 1 : 0);
}, 0);
let countHighContrast = indexOfExamples.reduce(function (set, example) {
return set + (example.highContrast ? 1 : 0);
}, 0);
let countKeyCode = indexOfExamples.reduce(function (set, example) {
return set + (example.keyCodeJS ? 1 : 0);
}, 0);
let countWhich = indexOfExamples.reduce(function (set, example) {
return set + (example.whichJS ? 1 : 0);
}, 0);
let countSVG = indexOfExamples.reduce(function (set, example) {
let svg = example.svgHTML ? 1 : 0;
if (!svg && (example.svgCSS || example.svgJS)) {
svg = 1;
}
return set + svg;
}, 0);
let countMouse = indexOfExamples.reduce(function (set, example) {
let count = example.mouseDown;
count += example.mouseEnter;
count += example.mouseLeave;
count += example.mouseMove;
count += example.mouseOut;
count += example.mouseOver;
count += example.mouseUp;
return set + (count ? 1 : 0);
}, 0);
let countPointer = indexOfExamples.reduce(function (set, example) {
let count = example.pointerDown;
count += example.pointerEnter;
count += example.pointerLeave;
count += example.pointerMove;
count += example.pointerOut;
count += example.pointerOver;
count += example.pointerUp;
return set + (count ? 1 : 0);
}, 0);
/*
let countForcedColorAdjust = indexOfExamples.reduce(function (set, example) {
return set + (example.forcedColorAdjust ? 1 : 0);
}, 0);
*/
let countForcedColors = indexOfExamples.reduce(function (set, example) {
return set + (example.forcedColors ? 1 : 0);
}, 0);
let countCurrentColor = indexOfExamples.reduce(function (set, example) {
return set + (example.currentColor ? 1 : 0);
}, 0);
$('#example_coding_practices_tbody').html(IndexOfExampleCodingPractices);
$('#example_graphics_techniques_tbody').html(IndexOfExampleGraphics);
$('#example_mouse_pointer_tbody').html(IndexOfExampleMousePointer);
$('#example_summary_total').html(indexOfExamples.length);
$('#example_summary_hc').html(countHighContrast);
$('#example_summary_svg').html(countSVG);
$('#example_summary_force_colors').html(countForcedColors);
$('#example_summary_current_color').html(countCurrentColor);
$('#example_summary_keycode').html(countKeyCode);
$('#example_summary_which').html(countWhich);
$('#example_summary_class').html(countClass);
$('#example_summary_prototype').html(countPrototype);
$('#example_summary_mouse').html(countMouse);
$('#example_summary_pointer').html(countPointer);
// cheerio seems to fold the doctype lines despite the template
const result = $.html()
.replace('<!DOCTYPE html>', '<!DOCTYPE html>\n')
.replace(
'<html xmlns="http://www.w3.org/1999/xhtml" lang="en-US" xml:lang="en-US">',
'<html xmlns="http://www.w3.org/1999/xhtml" lang="en-US" xml:lang="en-US">\n'
);
fs.writeFile(coverageReportPath, result, function (err) {
if (err) {
console.log('Error saving updated aria practices:', err);
}
});
// Output CSV files
let roles = '"Role","Guidance","Examples","References"\n';
roles += sortedRoles.reduce(function (line, role) {
let examples = indexOfRolesInExamples[role];
let guidance = indexOfRolesInGuidance[role];
let csvExampleTitles = examples.reduce(function (set, e) {
return `${set},"Example: ${e.title}"`;
}, '');
let csvGuidanceTitles = guidance.reduce(function (set, g) {
return `${set},"Guidance: ${g.title}"`;
}, '');
return `${line}"${role}","${guidance.length}","${examples.length}"${csvGuidanceTitles}${csvExampleTitles}\n`;
}, '');
fs.writeFile(csvRoleFilePath, roles, (err) => {
if (err) {
console.error(err);
return;
}
//file written successfully
});
let props = '"Property or State","Guidance","Examples","References"\n';
props += sortedPropertiesAndStates.reduce(function (line, prop) {
let examples = indexOfPropertiesAndStatesInExamples[prop];
let guidance = indexOfPropertiesAndStatesInGuidance[prop];
let csvExampleTitles = examples.reduce(function (set, e) {
return `${set},"Example: ${e.title}"`;
}, '');
let csvGuidanceTitles = guidance.reduce(function (set, g) {
return `${set},"Guidance: ${g.title}"`;
}, '');
return `${line}"${prop}","${guidance.length}","${examples.length}"${csvGuidanceTitles}${csvExampleTitles}\n`;
}, '');
fs.writeFile(csvPropFilePath, props, (err) => {
if (err) {
console.error(err);
return;
}
//file written successfully
});