From f52f2bf21482f2bb2335a6a747214b06d48e72d9 Mon Sep 17 00:00:00 2001 From: Peter Krautzberger Date: Mon, 27 Nov 2023 17:31:39 +0100 Subject: [PATCH 01/93] feat: add test.sh A simplistic test to test aria.js output. Runs respec before and after copying aria.js changes. NOTE: Assumes there's a copy of w3c/aria in ../aria/ --- test.sh | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 test.sh diff --git a/test.sh b/test.sh new file mode 100644 index 0000000..6531754 --- /dev/null +++ b/test.sh @@ -0,0 +1,14 @@ +#!/bin/bash + +# NOTE: Assumes there's a copy of w3c/aria in ../aria/ + +echo "Run respec on ../aria/index.html to generate 'before.html'" +npx respec --src ../aria/index.html --out before.html +echo "Copy ./script/aria.js to ../aria/common/script/" +cp ./script/aria.js ../aria/common/script/. +echo "Run respec on ../aria/index.html to generate 'after.html'" +npx respec --src ../aria/index.html --out after.html +echo "Run diff on 'before.html' and 'after.html'" +diff before.html after.html +echo "Clean up aria spec" +git -C ../aria/ checkout ./common/script/aria.js From 110b6cf30cfd277b11bcbbd8992dad2932fc2919 Mon Sep 17 00:00:00 2001 From: Peter Krautzberger Date: Mon, 27 Nov 2023 20:24:55 +0100 Subject: [PATCH 02/93] refactor(aria.js): var => const/let A simplistic first step. Running test.sh was clean. --- script/aria.js | 208 ++++++++++++++++++++++++------------------------- 1 file changed, 104 insertions(+), 104 deletions(-) diff --git a/script/aria.js b/script/aria.js index 67d6a62..b0242ac 100644 --- a/script/aria.js +++ b/script/aria.js @@ -7,14 +7,14 @@ * localprops: local properties and states */ -var roleInfo = {}; +const roleInfo = {}; function ariaAttributeReferences() { - var propList = {}; - var globalSP = []; + const propList = {}; + const globalSP = []; - var skipIndex = 0; - var myURL = document.URL; + let skipIndex = 0; + const myURL = document.URL; if (myURL.match(/\?fast/)) { skipIndex = 1; } @@ -24,11 +24,11 @@ function ariaAttributeReferences() { Array.prototype.slice .call(document.querySelectorAll("pdef, sdef")) .forEach(function (item) { - var type = item.localName === "pdef" ? "property" : "state"; - var container = item.parentNode; - var content = item.innerHTML; - var sp = document.createElement("span"); - var title = item.getAttribute("title"); + const type = item.localName === "pdef" ? "property" : "state"; + const container = item.parentNode; + const content = item.innerHTML; + const sp = document.createElement("span"); + let title = item.getAttribute("title"); if (!title) { title = content; } @@ -41,11 +41,11 @@ function ariaAttributeReferences() { type + ""; sp.setAttribute("aria-describedby", "desc-" + title); - var dRef = item.nextElementSibling; - var desc = cloneWithoutIds(dRef.firstElementChild).innerHTML; + const dRef = item.nextElementSibling; + const desc = cloneWithoutIds(dRef.firstElementChild).innerHTML; dRef.id = "desc-" + title; dRef.setAttribute("role", "definition"); - var heading = document.createElement("h4"); + const heading = document.createElement("h4"); heading.appendChild(sp); container.replaceChild(heading, item); // add this item to the index @@ -56,7 +56,7 @@ function ariaAttributeReferences() { desc: desc, roles: [], }; - var abstract = container.querySelector( + const abstract = container.querySelector( "." + type + "-applicability" ); if ( @@ -100,14 +100,14 @@ function ariaAttributeReferences() { if (container.nodeName.toLowerCase() == "div") { // change the enclosing DIV to a section with notoc - var sec = document.createElement("section"); + const sec = document.createElement("section"); Array.prototype.slice .call(container.attributes) .forEach(function (attr) { sec.setAttribute(attr.name, attr.value); }); sec.classList.add("notoc"); - var theContents = container.innerHTML; + const theContents = container.innerHTML; sec.innerHTML = theContents; container.parentNode.replaceChild(sec, container); } @@ -116,16 +116,16 @@ function ariaAttributeReferences() { if (!skipIndex) { // we have all the properties and states - spit out the // index - var propIndex = ""; - var sortedList = []; + let propIndex = ""; + let sortedList = []; Object.keys(propList).forEach(function (key) { sortedList.push(key); }); sortedList = sortedList.sort(); - for (var i = 0; i < sortedList.length; i++) { - var item = propList[sortedList[i]]; + for (let i = 0; i < sortedList.length; i++) { + const item = propList[sortedList[i]]; propIndex += '
\n"; propIndex += "
" + item.desc + "
\n"; } - var node = document.getElementById("index_state_prop"); - var parentNode = node.parentNode; - var l = document.createElement("dl"); + let node = document.getElementById("index_state_prop"); + parentNode = node.parentNode; + let l = document.createElement("dl"); l.id = "index_state_prop"; l.className = "compact"; l.innerHTML = propIndex; parentNode.replaceChild(l, node); - var globalSPIndex = ""; + let globalSPIndex = ""; sortedList = globalSP.sort(function (a, b) { return a.name < b.name ? -1 : a.name > b.name ? 1 : 0; }); for (i = 0; i < sortedList.length; i++) { - var lItem = sortedList[i]; + const lItem = sortedList[i]; globalSPIndex += "
  • "; if (lItem.is === "state") { globalSPIndex += @@ -213,32 +213,32 @@ function ariaAttributeReferences() { // 4. grab any local states and properties so we can hand those down to the children // - var subRoles = []; - var roleIndex = ""; - var fromAuthor = ""; - var fromHeading = ""; - var fromContent = ""; - var fromProhibited = ""; + const subRoles = []; + let roleIndex = ""; + let fromAuthor = ""; + let fromHeading = ""; + let fromContent = ""; + let fromProhibited = ""; Array.prototype.slice .call(document.querySelectorAll("rdef")) .forEach(function (item) { - var container = item.parentNode; - var content = item.innerHTML; - var sp = document.createElement("h4"); - var title = item.getAttribute("title"); + const container = item.parentNode; + const content = item.innerHTML; + const sp = document.createElement("h4"); + let title = item.getAttribute("title"); if (!title) { title = content; } - var pnID = title; + const pnID = title; container.id = pnID; sp.className = "role-name"; sp.title = title; // is this a role or an abstract role - var type = "role"; - var isAbstract = false; - var abstract = container.querySelectorAll(".role-abstract"); + let type = "role"; + let isAbstract = false; + const abstract = container.querySelectorAll(".role-abstract"); if (abstract.innerText === "True") { type = "abstract role"; isAbstract = true; @@ -251,8 +251,8 @@ function ariaAttributeReferences() { ""; // sp.id = title; sp.setAttribute("aria-describedby", "desc-" + title); - var dRef = item.nextElementSibling; - var desc = cloneWithoutIds(dRef.firstElementChild).innerHTML; + const dRef = item.nextElementSibling; + const desc = cloneWithoutIds(dRef.firstElementChild).innerHTML; dRef.id = "desc-" + title; dRef.setAttribute("role", "definition"); container.replaceChild(sp, item); @@ -267,12 +267,12 @@ function ariaAttributeReferences() { roleIndex += "
    " + desc + "
    \n"; // grab info about this role // do we have a parent class? if so, put us in that parents list - var node = Array.prototype.slice.call( + const node = Array.prototype.slice.call( container.querySelectorAll(".role-parent rref") ); // s will hold the name of the parent role if any - var s = null; - var parentRoles = []; + let s = null; + const parentRoles = []; if (node.length) { node.forEach(function (roleref) { s = roleref.textContent || roleref.innerText; @@ -286,7 +286,7 @@ function ariaAttributeReferences() { }); } // are there supported states / properties in this role? - var attrs = []; + const attrs = []; Array.prototype.slice .call( container.querySelectorAll( @@ -303,20 +303,20 @@ function ariaAttributeReferences() { Array.prototype.slice .call(node.querySelectorAll("pref,sref")) .forEach(function (item) { - var name = item.getAttribute("title"); + let name = item.getAttribute("title"); if (!name) { name = item.textContent || item.innerText; } - var type = + const type = item.localName === "pref" ? "property" : "state"; - var req = node.classList.contains( + const req = node.classList.contains( "role-required-properties" ); - var dis = + const dis = node.classList.contains("role-disallowed"); - var dep = item.hasAttribute("data-deprecated"); + const dep = item.hasAttribute("data-deprecated"); attrs.push({ is: type, name: name, @@ -344,9 +344,9 @@ function ariaAttributeReferences() { Array.prototype.slice .call(container.querySelectorAll(".role-namefrom")) .forEach(function (node) { - var reqRef = + const reqRef = container.querySelector(".role-namerequired"); - var req = ""; + let req = ""; if (reqRef && reqRef.innerText === "True") { req = " (name required)"; } @@ -398,7 +398,7 @@ function ariaAttributeReferences() { } if (container.nodeName.toLowerCase() == "div") { // change the enclosing DIV to a section with notoc - var sec = document.createElement("section"); + const sec = document.createElement("section"); Array.prototype.slice .call(container.attributes) .forEach(function (attr) { @@ -406,24 +406,24 @@ function ariaAttributeReferences() { }); sec.classList.add("notoc"); - var theContents = container.innerHTML; + const theContents = container.innerHTML; sec.innerHTML = theContents; container.parentNode.replaceChild(sec, container); } }); - var getStates = function (role) { - var ref = roleInfo[role]; + const getStates = function (role) { + const ref = roleInfo[role]; if (!ref) { msg.pub("error", "No role definition for " + role); } else if (ref.allprops) { return ref.allprops; } else { - var myList = ref.localprops; + let myList = ref.localprops; Array.prototype.slice .call(ref.parentRoles) .forEach(function (item) { - var pList = getStates(item); + const pList = getStates(item); myList = myList.concat(pList); }); ref.allprops = myList; @@ -436,20 +436,20 @@ function ariaAttributeReferences() { // build up the complete inherited SP lists for each role // however, if the role already specifies an item, do not include it Object.entries(roleInfo).forEach(function (index) { - var item = index[1]; - var output = ""; - var placeholder = document.querySelector( + const item = index[1]; + let output = ""; + const placeholder = document.querySelector( "#" + item.fragID + " .role-inherited" ); if (placeholder) { - var myList = []; + let myList = []; item.parentRoles.forEach(function (role) { myList = myList.concat(getStates(role)); }); // strip out any items that we have locally if (item.localprops.length && myList.length) { - for (var j = myList.length - 1; j >= 0; j--) { + for (let j = myList.length - 1; j >= 0; j--) { item.localprops.forEach(function (x) { if (x.name == myList[j].name) { myList.splice(j, 1); @@ -458,13 +458,13 @@ function ariaAttributeReferences() { } } - var reducedList = myList.reduce((uniqueList, item) => { + const reducedList = myList.reduce((uniqueList, item) => { return uniqueList.includes(item) ? uniqueList : [...uniqueList, item]; }, []); - var sortedList = reducedList.sort((a, b) => { + const sortedList = reducedList.sort((a, b) => { if (a.name == b.name) { // Ensure deprecated false properties occur first if (a.deprecated !== b.deprecated) { @@ -474,11 +474,11 @@ function ariaAttributeReferences() { return a.name < b.name ? -1 : a.name > b.name ? 1 : 0; }, []); - var prev; - for (var k = 0; k < sortedList.length; k++) { - var property = sortedList[k]; - var req = ""; - var dep = ""; + let prev; + for (let k = 0; k < sortedList.length; k++) { + const property = sortedList[k]; + let req = ""; + let dep = ""; if (property.required) { req = " (required)"; } @@ -515,15 +515,15 @@ function ariaAttributeReferences() { }); // Update state and property role references - var getAllSubRoles = function (role) { - var ref = subRoles[role]; + const getAllSubRoles = function (role) { + const ref = subRoles[role]; if (ref && ref.length) { - var myList = []; + let myList = []; ref.forEach(function (item) { if (!myList.item) { myList[item] = 1; myList.push(item); - var childList = getAllSubRoles(item); + const childList = getAllSubRoles(item); myList = myList.concat(childList); } }); @@ -534,10 +534,10 @@ function ariaAttributeReferences() { }; Object.entries(propList).forEach(function (index) { - var output = ""; - var item = index[1]; - var section = document.querySelector("#" + item.name); - var placeholder = section.querySelector( + let output = ""; + const item = index[1]; + const section = document.querySelector("#" + item.name); + let placeholder = section.querySelector( ".state-applicability, .property-applicability" ); if ( @@ -547,9 +547,9 @@ function ariaAttributeReferences() { item.roles.length ) { // update the used in roles list - var sortedList = []; + let sortedList = []; sortedList = item.roles.sort(); - for (var j = 0; j < sortedList.length; j++) { + for (let j = 0; j < sortedList.length; j++) { output += "
  • " + sortedList[j] + "
  • \n"; } if (output !== "") { @@ -557,9 +557,9 @@ function ariaAttributeReferences() { } placeholder.innerHTML = output; // also update any inherited roles - var myList = []; + let myList = []; item.roles.forEach(function (role) { - var children = getAllSubRoles(role); + let children = getAllSubRoles(role); // Some subroles have required properties which are also required by the superclass. // Example: The checked state of radio, which is also required by superclass checkbox. // We only want to include these one time, so filter out the subroles. @@ -576,8 +576,8 @@ function ariaAttributeReferences() { if (placeholder && myList.length) { sortedList = myList.sort(); output = ""; - var last = ""; - for (j = 0; j < sortedList.length; j++) { + let last = ""; + for (let j = 0; j < sortedList.length; j++) { var sItem = sortedList[j]; if (last != sItem) { output += "
  • " + sItem + "
  • \n"; @@ -596,7 +596,7 @@ function ariaAttributeReferences() { item.roles.length ) { // update the used in roles list - var sortedList = []; + let sortedList = []; sortedList = item.roles.sort(); //remove roletype from the sorted list const index = sortedList.indexOf("roletype"); @@ -604,7 +604,7 @@ function ariaAttributeReferences() { sortedList.splice(index, 1); } - for (var j = 0; j < sortedList.length; j++) { + for (let j = 0; j < sortedList.length; j++) { output += "
  • " + sortedList[j] + "
  • \n"; } if (output !== "") { @@ -612,9 +612,9 @@ function ariaAttributeReferences() { } placeholder.innerHTML = output; // also update any inherited roles - var myList = []; + let myList = []; item.roles.forEach(function (role) { - var children = getAllSubRoles(role); + let children = getAllSubRoles(role); // Some subroles have required properties which are also required by the superclass. // Example: The checked state of radio, which is also required by superclass checkbox. // We only want to include these one time, so filter out the subroles. @@ -629,11 +629,11 @@ function ariaAttributeReferences() { ".state-descendants, .property-descendants" ); if (placeholder && myList.length) { - sortedList = myList.sort(); - output = ""; - var last = ""; + let sortedList = myList.sort(); + let output = ""; + let last = ""; for (j = 0; j < sortedList.length; j++) { - var sItem = sortedList[j]; + const sItem = sortedList[j]; if (last != sItem) { output += "
  • " + sItem + "
  • \n"; last = sItem; @@ -651,7 +651,7 @@ function ariaAttributeReferences() { item.roles.length ) { // for prohibited roles the roles list just includes those roles which are prohibited... weird I know but it is what it is - var sortedList = []; + let sortedList = []; sortedList = item.roles.sort(); //remove roletype from the sorted list const index = sortedList.indexOf("roletype"); @@ -660,7 +660,7 @@ function ariaAttributeReferences() { } output += "All elements of the base markup except for the following roles: "; - for (var j = 0; j < sortedList.length - 1; j++) { + for (let j = 0; j < sortedList.length - 1; j++) { output += "" + sortedList[j] + ", "; } output += @@ -670,9 +670,9 @@ function ariaAttributeReferences() { }); // spit out the index - var node = document.getElementById("index_role"); - var parentNode = node.parentNode; - var list = document.createElement("dl"); + let node = document.getElementById("index_role"); + let parentNode = node.parentNode; + let list = document.createElement("dl"); list.id = "index_role"; list.className = "compact"; list.innerHTML = roleIndex; @@ -719,20 +719,20 @@ function ariaAttributeReferences() { parentNode.replaceChild(list, node); } // assuming we found some parent roles, update those parents with their children - for (var i = 0; i < subRoles.length; i++) { - var item = subRoles[subRoles[i]]; - var sortedList = item.sort(function (a, b) { + for (let i = 0; i < subRoles.length; i++) { + const item = subRoles[subRoles[i]]; + const sortedList = item.sort(function (a, b) { return a < b ? -1 : a > b ? 1 : 0; }); - var output = "
    \n`; - // BREAK -- different placeholder coming up // also update any inherited roles + const placeholderInheritedRoles = section.querySelector( + ".state-descendants, .property-descendants" + ); let myList = []; item.roles.forEach(function (role) { + // TODO: can we simplify this? let children = getAllSubRoles(role); // Some subroles have required properties which are also required by the superclass. // Example: The checked state of radio, which is also required by superclass checkbox. @@ -577,25 +580,13 @@ function ariaAttributeReferences() { }); myList = myList.concat(children); }); - placeholder = section.querySelector( - ".state-descendants, .property-descendants" - ); - if (placeholder && myList.length) { - let sortedList = myList.sort(); - let output = ""; - let last = ""; - for (j = 0; j < sortedList.length; j++) { - const sItem = sortedList[j]; - if (last != sItem) { - output += "
  • " + sItem + "
  • \n"; - last = sItem; - } - } - if (output !== "") { - output = "\n"; - } - placeholder.innerHTML = output; - } + const output = [...new Set(myList)] + .sort() + .map((role) => `
  • ${role}
  • \n`) + .join(""); + + if (output !== "") + placeholderInheritedRoles.innerHTML = `\n`; } else if ( placeholder?.innerText === "All elements of the base markup except for some roles or elements that prohibit its use" && From 9dd9e0469ee1fbe723073e75b3c28e4308bf4458 Mon Sep 17 00:00:00 2001 From: Peter Krautzberger Date: Wed, 13 Dec 2023 12:05:06 +0100 Subject: [PATCH 62/93] refactor(aria.js): improve propList loop * Tightens up HTML generation for the initial case "Placeholder" * adds TODO for later test.sh run is clean. --- script/aria.js | 45 +++++++++++++++++---------------------------- 1 file changed, 17 insertions(+), 28 deletions(-) diff --git a/script/aria.js b/script/aria.js index 498cdb5..860b22e 100644 --- a/script/aria.js +++ b/script/aria.js @@ -507,20 +507,21 @@ function ariaAttributeReferences() { let placeholder = section.querySelector( ".state-applicability, .property-applicability" ); + // TODO: all three cases are near-identical. Can we do more? if (placeholder?.innerText === "Placeholder" && item.roles.length) { // update the used in roles list - let sortedList = []; - sortedList = item.roles.sort(); - for (let j = 0; j < sortedList.length; j++) { - output += "
  • " + sortedList[j] + "
  • \n"; - } - if (output !== "") { - output = "
      \n" + output + "
    \n"; - } - placeholder.innerHTML = output; + item.roles.sort(); + placeholder.innerHTML = `
      \n${item.roles + .map((role) => `
    • ${role}
    • \n`) + .join("")}
    \n`; + // also update any inherited roles + const placeholderInheritedRoles = section.querySelector( + ".state-descendants, .property-descendants" + ); let myList = []; item.roles.forEach(function (role) { + // TODO: can we simplify this? let children = getAllSubRoles(role); // Some subroles have required properties which are also required by the superclass. // Example: The checked state of radio, which is also required by superclass checkbox. @@ -532,25 +533,13 @@ function ariaAttributeReferences() { }); myList = myList.concat(children); }); - placeholder = section.querySelector( - ".state-descendants, .property-descendants" - ); - if (placeholder && myList.length) { - sortedList = myList.sort(); - output = ""; - let last = ""; - for (let j = 0; j < sortedList.length; j++) { - var sItem = sortedList[j]; - if (last != sItem) { - output += "
  • " + sItem + "
  • \n"; - last = sItem; - } - } - if (output !== "") { - output = "
      \n" + output + "
    \n"; - } - placeholder.innerHTML = output; - } + const output = [...new Set(myList)] + .sort() + .map((role) => `
  • ${role}
  • \n`) + .join(""); + + if (output !== "") + placeholderInheritedRoles.innerHTML = `
      \n${output}
    \n`; } else if ( placeholder?.innerText === "Use as a global deprecated in ARIA 1.2" && From f490ec9c37f76dc8d4669e845eb9216bfd0ec4c1 Mon Sep 17 00:00:00 2001 From: Peter Krautzberger Date: Wed, 13 Dec 2023 12:06:43 +0100 Subject: [PATCH 63/93] refactor(aria.js): improve propList loop * switch from Object.entries to .values (and adjust) * remove now unused output variable. test.sh run is clean. --- script/aria.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/script/aria.js b/script/aria.js index 860b22e..ab77c06 100644 --- a/script/aria.js +++ b/script/aria.js @@ -500,9 +500,7 @@ function ariaAttributeReferences() { } }; - Object.entries(propList).forEach(function (index) { - let output = ""; - const item = index[1]; + Object.values(propList).forEach(function (item) { const section = document.querySelector("#" + item.name); let placeholder = section.querySelector( ".state-applicability, .property-applicability" From 80bbb0783105f164ef082f2f821ad63b5d0d6268 Mon Sep 17 00:00:00 2001 From: Peter Krautzberger Date: Wed, 13 Dec 2023 12:23:09 +0100 Subject: [PATCH 64/93] refactor(aria.js): improve propList loop Remove some unnecessary if-checks; there should always be some roles and thus output. test.sh run is clean. --- script/aria.js | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/script/aria.js b/script/aria.js index ab77c06..46f332f 100644 --- a/script/aria.js +++ b/script/aria.js @@ -506,7 +506,7 @@ function ariaAttributeReferences() { ".state-applicability, .property-applicability" ); // TODO: all three cases are near-identical. Can we do more? - if (placeholder?.innerText === "Placeholder" && item.roles.length) { + if (placeholder?.innerText === "Placeholder") { // update the used in roles list item.roles.sort(); placeholder.innerHTML = `
      \n${item.roles @@ -531,17 +531,16 @@ function ariaAttributeReferences() { }); myList = myList.concat(children); }); - const output = [...new Set(myList)] + + placeholderInheritedRoles.innerHTML = `
        \n${[ + ...new Set(myList), + ] .sort() .map((role) => `
      • ${role}
      • \n`) - .join(""); - - if (output !== "") - placeholderInheritedRoles.innerHTML = `
          \n${output}
        \n`; + .join("")}
      \n`; } else if ( placeholder?.innerText === - "Use as a global deprecated in ARIA 1.2" && - item.roles.length + "Use as a global deprecated in ARIA 1.2" ) { // update roles list (sort, remove roletype) item.roles.sort().splice(item.roles.indexOf("roletype"), 1); @@ -567,17 +566,15 @@ function ariaAttributeReferences() { }); myList = myList.concat(children); }); - const output = [...new Set(myList)] + placeholderInheritedRoles.innerHTML = `
        \n${[ + ...new Set(myList), + ] .sort() .map((role) => `
      • ${role}
      • \n`) - .join(""); - - if (output !== "") - placeholderInheritedRoles.innerHTML = `
          \n${output}
        \n`; + .join("")}
      \n`; } else if ( placeholder?.innerText === - "All elements of the base markup except for some roles or elements that prohibit its use" && - item.roles.length + "All elements of the base markup except for some roles or elements that prohibit its use" ) { // for prohibited roles the roles list just includes those roles which are prohibited... weird I know but it is what it is // exclude roletype from the sorted list From 0ff45982d9296808a1c6eb7bf2d509d71144fb4e Mon Sep 17 00:00:00 2001 From: Peter Krautzberger Date: Wed, 13 Dec 2023 12:25:30 +0100 Subject: [PATCH 65/93] refactor(aria.js): improve propList loop Move tweaking of roles list to top. test.sh run is clean. --- script/aria.js | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/script/aria.js b/script/aria.js index 46f332f..2eda00f 100644 --- a/script/aria.js +++ b/script/aria.js @@ -505,10 +505,12 @@ function ariaAttributeReferences() { let placeholder = section.querySelector( ".state-applicability, .property-applicability" ); + // update roles list: sort, maybe remove roletype + item.roles.sort(); + if (placeholder.innerText !== "Placeholder") + item.roles.splice(item.roles.indexOf("roletype"), 1); // TODO: all three cases are near-identical. Can we do more? if (placeholder?.innerText === "Placeholder") { - // update the used in roles list - item.roles.sort(); placeholder.innerHTML = `
        \n${item.roles .map((role) => `
      • ${role}
      • \n`) .join("")}
      \n`; @@ -542,8 +544,6 @@ function ariaAttributeReferences() { placeholder?.innerText === "Use as a global deprecated in ARIA 1.2" ) { - // update roles list (sort, remove roletype) - item.roles.sort().splice(item.roles.indexOf("roletype"), 1); placeholder.innerHTML = `
        \n${item.roles .map((role) => `
      • ${role}
      • \n`) .join("")}
      \n`; @@ -577,8 +577,6 @@ function ariaAttributeReferences() { "All elements of the base markup except for some roles or elements that prohibit its use" ) { // for prohibited roles the roles list just includes those roles which are prohibited... weird I know but it is what it is - // exclude roletype from the sorted list - item.roles.sort().splice(item.roles.indexOf("roletype"), 1); placeholder.innerHTML = `All elements of the base markup except for the following roles: ${item.roles .map((role) => `${role}`) From 06530f4687aa07990fb89023eed8b265a9a0658c Mon Sep 17 00:00:00 2001 From: Peter Krautzberger Date: Wed, 13 Dec 2023 12:28:03 +0100 Subject: [PATCH 66/93] refactor(aria.js): improve propList loop Extract placeholder text and remove unnecessary optional chaining. test.sh run is clean. --- script/aria.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/script/aria.js b/script/aria.js index 2eda00f..3754926 100644 --- a/script/aria.js +++ b/script/aria.js @@ -505,12 +505,13 @@ function ariaAttributeReferences() { let placeholder = section.querySelector( ".state-applicability, .property-applicability" ); + const placeholderText = placeholder.innerText; // update roles list: sort, maybe remove roletype item.roles.sort(); - if (placeholder.innerText !== "Placeholder") + if (placeholderText !== "Placeholder") item.roles.splice(item.roles.indexOf("roletype"), 1); // TODO: all three cases are near-identical. Can we do more? - if (placeholder?.innerText === "Placeholder") { + if (placeholderText === "Placeholder") { placeholder.innerHTML = `
        \n${item.roles .map((role) => `
      • ${role}
      • \n`) .join("")}
      \n`; @@ -541,8 +542,7 @@ function ariaAttributeReferences() { .map((role) => `
    • ${role}
    • \n`) .join("")}
    \n`; } else if ( - placeholder?.innerText === - "Use as a global deprecated in ARIA 1.2" + placeholderText === "Use as a global deprecated in ARIA 1.2" ) { placeholder.innerHTML = `
      \n${item.roles .map((role) => `
    • ${role}
    • \n`) @@ -573,7 +573,7 @@ function ariaAttributeReferences() { .map((role) => `
    • ${role}
    • \n`) .join("")}
    \n`; } else if ( - placeholder?.innerText === + placeholderText === "All elements of the base markup except for some roles or elements that prohibit its use" ) { // for prohibited roles the roles list just includes those roles which are prohibited... weird I know but it is what it is From 4aaa99f7f7724a4f54e90034d5f8b0b6e479e00f Mon Sep 17 00:00:00 2001 From: Peter Krautzberger Date: Wed, 13 Dec 2023 12:47:10 +0100 Subject: [PATCH 67/93] refactor(aria.js): improve propList loop Re-arrange to unify two cases. * document possible placeholder values * return early on generic case * move case "partially prohibited" to top (and return early) * de-duplicate the other two cases test.sh run is clean. --- script/aria.js | 107 ++++++++++++++++++++----------------------------- 1 file changed, 44 insertions(+), 63 deletions(-) diff --git a/script/aria.js b/script/aria.js index 3754926..f87f41c 100644 --- a/script/aria.js +++ b/script/aria.js @@ -506,73 +506,23 @@ function ariaAttributeReferences() { ".state-applicability, .property-applicability" ); const placeholderText = placeholder.innerText; - // update roles list: sort, maybe remove roletype + // Current values for placeholderText: + // * "All elements of the base markup" + // * "Placeholder" + // * "Use as a global deprecated in ARIA 1.2" + // * "All elements of the base markup except for some roles or elements that prohibit its use" + // TODO: Maybe use a data attribute instead? + + // Case: nothing to od + if (placeholderText === "All elements of the base markup") return; + + // update roles list: sort & maybe remove roletype item.roles.sort(); if (placeholderText !== "Placeholder") item.roles.splice(item.roles.indexOf("roletype"), 1); - // TODO: all three cases are near-identical. Can we do more? - if (placeholderText === "Placeholder") { - placeholder.innerHTML = `
      \n${item.roles - .map((role) => `
    • ${role}
    • \n`) - .join("")}
    \n`; - - // also update any inherited roles - const placeholderInheritedRoles = section.querySelector( - ".state-descendants, .property-descendants" - ); - let myList = []; - item.roles.forEach(function (role) { - // TODO: can we simplify this? - let children = getAllSubRoles(role); - // Some subroles have required properties which are also required by the superclass. - // Example: The checked state of radio, which is also required by superclass checkbox. - // We only want to include these one time, so filter out the subroles. - children = children.filter(function (subrole) { - return ( - subrole.indexOf(propList[item.name].roles) === -1 - ); - }); - myList = myList.concat(children); - }); - placeholderInheritedRoles.innerHTML = `
      \n${[ - ...new Set(myList), - ] - .sort() - .map((role) => `
    • ${role}
    • \n`) - .join("")}
    \n`; - } else if ( - placeholderText === "Use as a global deprecated in ARIA 1.2" - ) { - placeholder.innerHTML = `
      \n${item.roles - .map((role) => `
    • ${role}
    • \n`) - .join("")}
    \n`; - - // also update any inherited roles - const placeholderInheritedRoles = section.querySelector( - ".state-descendants, .property-descendants" - ); - let myList = []; - item.roles.forEach(function (role) { - // TODO: can we simplify this? - let children = getAllSubRoles(role); - // Some subroles have required properties which are also required by the superclass. - // Example: The checked state of radio, which is also required by superclass checkbox. - // We only want to include these one time, so filter out the subroles. - children = children.filter(function (subrole) { - return ( - subrole.indexOf(propList[item.name].roles) === -1 - ); - }); - myList = myList.concat(children); - }); - placeholderInheritedRoles.innerHTML = `
      \n${[ - ...new Set(myList), - ] - .sort() - .map((role) => `
    • ${role}
    • \n`) - .join("")}
    \n`; - } else if ( + // Case: partially prohibited + if ( placeholderText === "All elements of the base markup except for some roles or elements that prohibit its use" ) { @@ -581,7 +531,38 @@ function ariaAttributeReferences() { placeholder.innerHTML = `All elements of the base markup except for the following roles: ${item.roles .map((role) => `${role}`) .join(", ")}`; + return; } + + // Otherwise, i.e., + // Cases: placeholderText "Placeholder" or "Use as a global deprecated in ARIA 1.2" + + // populate placeholder + placeholder.innerHTML = `
      \n${item.roles + .map((role) => `
    • ${role}
    • \n`) + .join("")}
    \n`; + + // also update any inherited roles + const placeholderInheritedRoles = section.querySelector( + ".state-descendants, .property-descendants" + ); + let myList = []; + item.roles.forEach(function (role) { + // TODO: can we simplify this? + let children = getAllSubRoles(role); + // Some subroles have required properties which are also required by the superclass. + // Example: The checked state of radio, which is also required by superclass checkbox. + // We only want to include these one time, so filter out the subroles. + children = children.filter(function (subrole) { + return subrole.indexOf(propList[item.name].roles) === -1; + }); + myList = myList.concat(children); + }); + + placeholderInheritedRoles.innerHTML = `
      \n${[...new Set(myList)] + .sort() + .map((role) => `
    • ${role}
    • \n`) + .join("")}
    \n`; }); // spit out the index From 2f97b038546901146a7238ffa5516679e788a608 Mon Sep 17 00:00:00 2001 From: Peter Krautzberger Date: Wed, 13 Dec 2023 13:05:43 +0100 Subject: [PATCH 68/93] refactor(aria.js): indices creation Switches to template strings and outerHTML. Adds TODO for name-from-heading test.sh run is clean. --- script/aria.js | 66 ++++++++++++++------------------------------------ 1 file changed, 18 insertions(+), 48 deletions(-) diff --git a/script/aria.js b/script/aria.js index f87f41c..50fb59a 100644 --- a/script/aria.js +++ b/script/aria.js @@ -565,55 +565,25 @@ function ariaAttributeReferences() { .join("")}\n`; }); - // spit out the index - let node = document.getElementById("index_role"); - let parentNode = node.parentNode; - let list = document.createElement("dl"); - list.id = "index_role"; - list.className = "compact"; - list.innerHTML = roleIndex; - parentNode.replaceChild(list, node); - - // and the namefrom lists - node = document.getElementById("index_fromauthor"); - if (node) { - parentNode = node.parentNode; - list = document.createElement("ul"); - list.id = "index_fromauthor"; - list.className = "compact"; - list.innerHTML = fromAuthor; - parentNode.replaceChild(list, node); - } - - node = document.getElementById("index_fromheading"); - if (node) { - parentNode = node.parentNode; - list = document.createElement("ul"); - list.id = "index_fromheading"; - list.className = "compact"; - list.innerHTML = fromHeading; - parentNode.replaceChild(list, node); - } + // spit out the indices + document.getElementById( + "index_role" + ).outerHTML = `
    ${roleIndex}
    `; + document.getElementById( + "index_fromauthor" + ).outerHTML = `
      ${fromAuthor}
    `; + document.getElementById( + "index_fromcontent" + ).outerHTML = `
      ${fromContent}
    `; + document.getElementById( + "index_fromprohibited" + ).outerHTML = `
      ${fromProhibited}
    `; + // TODO: remove if-check after w3c/aria#1860 + if (document.getElementById("index_fromheading")) + document.getElementById( + "index_fromheading" + ).outerHTML = `
      ${fromHeading}
    `; - node = document.getElementById("index_fromcontent"); - if (node) { - parentNode = node.parentNode; - list = document.createElement("ul"); - list.id = "index_fromcontent"; - list.className = "compact"; - list.innerHTML = fromContent; - parentNode.replaceChild(list, node); - } - - node = document.getElementById("index_fromprohibited"); - if (node) { - parentNode = node.parentNode; - list = document.createElement("ul"); - list.id = "index_fromprohibited"; - list.className = "compact"; - list.innerHTML = fromProhibited; - parentNode.replaceChild(list, node); - } // assuming we found some parent roles, update those parents with their children for (let i = 0; i < subRoles.length; i++) { const item = subRoles[subRoles[i]]; From e8f147242b05ab87c7194d63e7eddecb2434156b Mon Sep 17 00:00:00 2001 From: Peter Krautzberger Date: Wed, 13 Dec 2023 13:08:55 +0100 Subject: [PATCH 69/93] refactor(aria.js): child roles generation Begin refactor by dropping unnecessary filter function. test.sh run is clean. --- script/aria.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/script/aria.js b/script/aria.js index 50fb59a..84d2390 100644 --- a/script/aria.js +++ b/script/aria.js @@ -587,9 +587,7 @@ function ariaAttributeReferences() { // assuming we found some parent roles, update those parents with their children for (let i = 0; i < subRoles.length; i++) { const item = subRoles[subRoles[i]]; - const sortedList = item.sort(function (a, b) { - return a < b ? -1 : a > b ? 1 : 0; - }); + const sortedList = item.sort(); let output = "
      \n"; for (let j = 0; j < sortedList.length; j++) { output += "
    • " + sortedList[j] + "
    • \n"; From 4d5b37bb8324bca2f979ee4ee8adbee6215c7f3b Mon Sep 17 00:00:00 2001 From: Peter Krautzberger Date: Wed, 13 Dec 2023 13:10:08 +0100 Subject: [PATCH 70/93] refactor(aria.js): child roles generation Drop even more unnecessary filtering. test.sh run is clean. --- script/aria.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/aria.js b/script/aria.js index 84d2390..8166800 100644 --- a/script/aria.js +++ b/script/aria.js @@ -587,7 +587,7 @@ function ariaAttributeReferences() { // assuming we found some parent roles, update those parents with their children for (let i = 0; i < subRoles.length; i++) { const item = subRoles[subRoles[i]]; - const sortedList = item.sort(); + const sortedList = item; let output = "
        \n"; for (let j = 0; j < sortedList.length; j++) { output += "
      • " + sortedList[j] + "
      • \n"; From 22aac2ae8c8a52e8614bd4dcc394b9a0675e157e Mon Sep 17 00:00:00 2001 From: Peter Krautzberger Date: Wed, 13 Dec 2023 13:31:52 +0100 Subject: [PATCH 71/93] refactor(aria.js): child roles generation Refactor into forEach + map + template strings. test.sh run is clean. --- script/aria.js | 26 ++++++++------------------ 1 file changed, 8 insertions(+), 18 deletions(-) diff --git a/script/aria.js b/script/aria.js index 8166800..2b03021 100644 --- a/script/aria.js +++ b/script/aria.js @@ -585,24 +585,14 @@ function ariaAttributeReferences() { ).outerHTML = `
          ${fromHeading}
        `; // assuming we found some parent roles, update those parents with their children - for (let i = 0; i < subRoles.length; i++) { - const item = subRoles[subRoles[i]]; - const sortedList = item; - let output = "
          \n"; - for (let j = 0; j < sortedList.length; j++) { - output += "
        • " + sortedList[j] + "
        • \n"; - } - output += "
        \n"; - // put it somewhere - const subRolesContainer = document.querySelector("#" + subRoles[i]); - if (subRolesContainer) { - const subRolesListContainer = - subRolesContainer.querySelector(".role-children"); - if (subRolesListContainer) { - subRolesListContainer.innerHTML = output; - } - } - } + subRoles.forEach((role) => { + const item = subRoles[role]; //TODO: cf. populateSubRoles overloading + document.querySelector( + `#${role} .role-children` + ).innerHTML = `
          \n${item + .map((subrole) => `
        • ${subrole}
        • \n`) + .join("")}
        \n`; + }); } // prune out unused rows throughout the document From 72781f9fcc07cfba70a22dc629da490a6402d7ae Mon Sep 17 00:00:00 2001 From: Peter Krautzberger Date: Wed, 13 Dec 2023 16:08:31 +0100 Subject: [PATCH 72/93] refactor(aria.js): move cleanup into main scope Note: removes skipindex check during cleanup. test.sh run is clean. --- script/aria.js | 48 +++++++++++++++++++++++++++--------------------- 1 file changed, 27 insertions(+), 21 deletions(-) diff --git a/script/aria.js b/script/aria.js index 2b03021..88a4279 100644 --- a/script/aria.js +++ b/script/aria.js @@ -411,6 +411,32 @@ const buildInheritedStatesProperties = function (item) { } }; +/** + * prune out unused rows throughout the document + * + */ +const pruneUnusedRows = () => { + document + .querySelectorAll( + ".role-abstract, .role-parent, .role-base, .role-related, .role-scope, .role-mustcontain, .role-required-properties, .role-properties, .role-namefrom, .role-namerequired, .role-namerequired-inherited, .role-childpresentational, .role-presentational-inherited, .state-related, .property-related,.role-inherited, .role-children, .property-descendants, .state-descendants, .implicit-values" + ) + .forEach(function (item) { + var content = item.innerText; + if (content.length === 1 || content.length === 0) { + // there is no item - remove the row + item.parentNode.parentNode.removeChild(item.parentNode); + } else if ( + content === "Placeholder" && + (item.className === "role-inherited" || + item.className === "role-children" || + item.className === "property-descendants" || + item.className === "state-descendants") + ) { + item.parentNode.remove(); + } + }); +}; + function ariaAttributeReferences() { const propList = {}; const globalSP = []; @@ -595,27 +621,7 @@ function ariaAttributeReferences() { }); } - // prune out unused rows throughout the document - document - .querySelectorAll( - ".role-abstract, .role-parent, .role-base, .role-related, .role-scope, .role-mustcontain, .role-required-properties, .role-properties, .role-namefrom, .role-namerequired, .role-namerequired-inherited, .role-childpresentational, .role-presentational-inherited, .state-related, .property-related,.role-inherited, .role-children, .property-descendants, .state-descendants, .implicit-values" - ) - .forEach(function (item) { - var content = item.innerText; - if (content.length === 1 || content.length === 0) { - // there is no item - remove the row - item.parentNode.parentNode.removeChild(item.parentNode); - } else if ( - content === "Placeholder" && - !skipIndex && - (item.className === "role-inherited" || - item.className === "role-children" || - item.className === "property-descendants" || - item.className === "state-descendants") - ) { - item.parentNode.remove(); - } - }); + pruneUnusedRows(); updateReferences(document); } From e5e7c88e73bd97b59c57aab3c6f877a5ef890857 Mon Sep 17 00:00:00 2001 From: Peter Krautzberger Date: Wed, 13 Dec 2023 16:13:05 +0100 Subject: [PATCH 73/93] refactor(aria.js): re-arrange indices handling Prepares for more. test.sh run is clean. --- script/aria.js | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/script/aria.js b/script/aria.js index 88a4279..fa90970 100644 --- a/script/aria.js +++ b/script/aria.js @@ -497,6 +497,25 @@ function ariaAttributeReferences() { .join(""); const roleIndex = [...rdefs].map(generateHTMLRoleIndexEntry).join(""); + + // spit out the indices + document.getElementById( + "index_role" + ).outerHTML = `
        ${roleIndex}
        `; + document.getElementById( + "index_fromauthor" + ).outerHTML = `
          ${fromAuthor}
        `; + document.getElementById( + "index_fromcontent" + ).outerHTML = `
          ${fromContent}
        `; + document.getElementById( + "index_fromprohibited" + ).outerHTML = `
          ${fromProhibited}
        `; + // TODO: remove if-check after w3c/aria#1860 + if (document.getElementById("index_fromheading")) + document.getElementById( + "index_fromheading" + ).outerHTML = `
          ${fromHeading}
        `; rdefs.forEach(populateRoleInfoPropList.bind(null, roleInfo, propList)); rdefs.forEach(rewriteRdef); @@ -591,25 +610,6 @@ function ariaAttributeReferences() { .join("")}
      \n`; }); - // spit out the indices - document.getElementById( - "index_role" - ).outerHTML = `
      ${roleIndex}
      `; - document.getElementById( - "index_fromauthor" - ).outerHTML = `
        ${fromAuthor}
      `; - document.getElementById( - "index_fromcontent" - ).outerHTML = `
        ${fromContent}
      `; - document.getElementById( - "index_fromprohibited" - ).outerHTML = `
        ${fromProhibited}
      `; - // TODO: remove if-check after w3c/aria#1860 - if (document.getElementById("index_fromheading")) - document.getElementById( - "index_fromheading" - ).outerHTML = `
        ${fromHeading}
      `; - // assuming we found some parent roles, update those parents with their children subRoles.forEach((role) => { const item = subRoles[role]; //TODO: cf. populateSubRoles overloading From 485bdb595c3d5548808fb13cf2d114d6d672ab5c Mon Sep 17 00:00:00 2001 From: Peter Krautzberger Date: Wed, 13 Dec 2023 16:18:58 +0100 Subject: [PATCH 74/93] refactor(aria.js): extract HTML creation for indices Moves everything related to HTML for indices into function in global scope. test.sh run is clean. --- script/aria.js | 73 ++++++++++++++++++++++++++++---------------------- 1 file changed, 41 insertions(+), 32 deletions(-) diff --git a/script/aria.js b/script/aria.js index fa90970..fc14995 100644 --- a/script/aria.js +++ b/script/aria.js @@ -437,6 +437,46 @@ const pruneUnusedRows = () => { }); }; +/** + * Generates the HTML for various indices in the spec + * @param {NodeList} rdefs - all the rdefs + */ +const generateHTMLIndices = (rdefs) => { + let fromAuthor = [...rdefs] + .map(generateHTMLNameFromIndices.bind(null, "author")) + .join(""); + let fromHeading = [...rdefs] + .map(generateHTMLNameFromIndices.bind(null, "heading")) + .join(""); + let fromContent = [...rdefs] + .map(generateHTMLNameFromIndices.bind(null, "content")) + .join(""); + let fromProhibited = [...rdefs] + .map(generateHTMLNameFromIndices.bind(null, "prohibited")) + .join(""); + + const roleIndex = [...rdefs].map(generateHTMLRoleIndexEntry).join(""); + + // spit out the indices + document.getElementById( + "index_role" + ).outerHTML = `
      ${roleIndex}
      `; + document.getElementById( + "index_fromauthor" + ).outerHTML = `
        ${fromAuthor}
      `; + document.getElementById( + "index_fromcontent" + ).outerHTML = `
        ${fromContent}
      `; + document.getElementById( + "index_fromprohibited" + ).outerHTML = `
        ${fromProhibited}
      `; + // TODO: remove if-check after w3c/aria#1860 + if (document.getElementById("index_fromheading")) + document.getElementById( + "index_fromheading" + ).outerHTML = `
        ${fromHeading}
      `; +}; + function ariaAttributeReferences() { const propList = {}; const globalSP = []; @@ -483,39 +523,8 @@ function ariaAttributeReferences() { rdefs.forEach(populateSubRoles.bind(null, subRoles)); - let fromAuthor = [...rdefs] - .map(generateHTMLNameFromIndices.bind(null, "author")) - .join(""); - let fromHeading = [...rdefs] - .map(generateHTMLNameFromIndices.bind(null, "heading")) - .join(""); - let fromContent = [...rdefs] - .map(generateHTMLNameFromIndices.bind(null, "content")) - .join(""); - let fromProhibited = [...rdefs] - .map(generateHTMLNameFromIndices.bind(null, "prohibited")) - .join(""); - - const roleIndex = [...rdefs].map(generateHTMLRoleIndexEntry).join(""); + generateHTMLIndices(rdefs); - // spit out the indices - document.getElementById( - "index_role" - ).outerHTML = `
      ${roleIndex}
      `; - document.getElementById( - "index_fromauthor" - ).outerHTML = `
        ${fromAuthor}
      `; - document.getElementById( - "index_fromcontent" - ).outerHTML = `
        ${fromContent}
      `; - document.getElementById( - "index_fromprohibited" - ).outerHTML = `
        ${fromProhibited}
      `; - // TODO: remove if-check after w3c/aria#1860 - if (document.getElementById("index_fromheading")) - document.getElementById( - "index_fromheading" - ).outerHTML = `
        ${fromHeading}
      `; rdefs.forEach(populateRoleInfoPropList.bind(null, roleInfo, propList)); rdefs.forEach(rewriteRdef); From 44b01348d3799909f702216cb463c41d9622ef61 Mon Sep 17 00:00:00 2001 From: Peter Krautzberger Date: Wed, 13 Dec 2023 16:26:40 +0100 Subject: [PATCH 75/93] refactor(aria.js): use outerHTML more often Crude but direct. test.sh run is clean. --- script/aria.js | 30 +++++------------------------- 1 file changed, 5 insertions(+), 25 deletions(-) diff --git a/script/aria.js b/script/aria.js index fc14995..c1674ee 100644 --- a/script/aria.js +++ b/script/aria.js @@ -111,11 +111,7 @@ const rewriteRdef = function (item) { const dRef = item.nextElementSibling; dRef.id = "desc-" + title; dRef.setAttribute("role", "definition"); - item.insertAdjacentHTML( - "afterend", - `

      ${content} ${type}` - ); - item.remove(); + item.outerHTML = `

      ${content} ${type}`; }; /** @@ -130,11 +126,7 @@ const generateHTMLStatesAndProperties = function (propList, item) { dRef.id = "desc-" + title; // TODO: too much of a side-effect? dRef.setAttribute("role", "definition"); // TODO: ditto? // Replace pdef/sdef with HTML - item.insertAdjacentHTML( - "afterend", - `

      ${itemEntry.name} ${itemEntry.is}

      ` - ); - item.remove(); + item.outerHTML = `

      ${itemEntry.name} ${itemEntry.is}

      `; }; /** @@ -150,11 +142,7 @@ const generateIndexStatesAndProperties = (propList) => { `
      ${item.name}
      \n
      ${item.desc}
      \n` ) .join(""); - indexStatePropPlaceholder.insertAdjacentHTML( - "afterend", - `
      ${indexStatePropContent}
      ` - ); - indexStatePropPlaceholder.remove(); + indexStatePropPlaceholder.outerHTML = `
      ${indexStatePropContent}
      `; }; /** @@ -183,21 +171,13 @@ const generateIndexGlobalStatesAndProperties = (globalSP) => { const globalStatesPropertiesPlaceholder = document.querySelector( "#global_states .placeholder" ); - globalStatesPropertiesPlaceholder.insertAdjacentHTML( - "afterend", - `
        ${globalStatesPropertiesContent}
      ` - ); - globalStatesPropertiesPlaceholder.remove(); + globalStatesPropertiesPlaceholder.outerHTML = `
        ${globalStatesPropertiesContent}
      `; // Populate role=roletype properties with global properties const roletypePropsPlaceholder = document.querySelector( "#roletype td.role-properties span.placeholder" ); - roletypePropsPlaceholder.insertAdjacentHTML( - "afterend", - `
        ${globalStatesPropertiesContent}
      ` - ); - roletypePropsPlaceholder.remove(); + roletypePropsPlaceholder.outerHTML = `
        ${globalStatesPropertiesContent}
      `; }; /** From 7e444ad41f570ea84fbc5c4dd002880046764944 Mon Sep 17 00:00:00 2001 From: Peter Krautzberger Date: Wed, 13 Dec 2023 16:38:07 +0100 Subject: [PATCH 76/93] fix(aria.js): regressions in roleInfo output Ensures some values are Boolean. test.sh run is clean. --- script/aria.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/script/aria.js b/script/aria.js index c1674ee..fcd4414 100644 --- a/script/aria.js +++ b/script/aria.js @@ -231,8 +231,8 @@ const populateSubRoles = (subRoles, rdef) => { const extractStatesProperties = function (item) { const name = item.getAttribute("title") || item.innerText; // TODO: tests indicate both are needed but why? const type = item.localName === "pref" ? "property" : "state"; - const req = item.closest(".role-required-properties"); - const dis = item.closest(".role-disallowed"); + const req = new Boolean(item.closest(".role-required-properties")); + const dis = new Boolean(item.closest(".role-disallowed")); const dep = item.hasAttribute("data-deprecated"); return { is: type, From c619ee2d9c359ab02c30961283e66a107512f391 Mon Sep 17 00:00:00 2001 From: Peter Krautzberger Date: Wed, 13 Dec 2023 18:08:43 +0100 Subject: [PATCH 77/93] fix(aria.js): regression from fixing roleInfo Fixes the HTML output. test.sh run is clean. --- script/aria.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/script/aria.js b/script/aria.js index fcd4414..933b84f 100644 --- a/script/aria.js +++ b/script/aria.js @@ -231,8 +231,8 @@ const populateSubRoles = (subRoles, rdef) => { const extractStatesProperties = function (item) { const name = item.getAttribute("title") || item.innerText; // TODO: tests indicate both are needed but why? const type = item.localName === "pref" ? "property" : "state"; - const req = new Boolean(item.closest(".role-required-properties")); - const dis = new Boolean(item.closest(".role-disallowed")); + const req = !!item.closest(".role-required-properties"); + const dis = !!item.closest(".role-disallowed"); const dep = item.hasAttribute("data-deprecated"); return { is: type, From 2455b8893d12eb6fbd54a8a177fb91b10648da4a Mon Sep 17 00:00:00 2001 From: Peter Krautzberger Date: Wed, 13 Dec 2023 18:18:41 +0100 Subject: [PATCH 78/93] refactor(aria.js): add createDescendantRoles Adds createDescendantRoles() which encapsulates the getAllSubRoles() to allow for extraction of propList loop. test.sh run is clean. --- script/aria.js | 44 ++++++++++++++++++++++++++------------------ 1 file changed, 26 insertions(+), 18 deletions(-) diff --git a/script/aria.js b/script/aria.js index 933b84f..545d5f9 100644 --- a/script/aria.js +++ b/script/aria.js @@ -515,24 +515,32 @@ function ariaAttributeReferences() { if (!skipIndex) { Object.values(roleInfo).forEach(buildInheritedStatesProperties); - // Update state and property role references - const getAllSubRoles = function (role) { - const ref = subRoles[role]; - if (ref && ref.length) { - let myList = []; - ref.forEach(function (item) { - if (!myList.item) { - myList[item] = 1; - myList.push(item); - const childList = getAllSubRoles(item); - myList = myList.concat(childList); - } - }); - return myList; - } else { - return []; - } + const createDescendantRoles = (subRoles) => { + const descendantRoles = {}; + // Update state and property role references + const getAllSubRoles = function (role) { + const ref = subRoles[role]; + if (ref && ref.length) { + let myList = []; + ref.forEach(function (item) { + if (!myList.item) { + myList[item] = 1; + myList.push(item); + const childList = getAllSubRoles(item); + myList = myList.concat(childList); + } + }); + return myList; + } else { + return []; + } + }; + subRoles.forEach( + (item) => (descendantRoles[item] = getAllSubRoles(item)) + ); + return descendantRoles; }; + const descendantRoles = createDescendantRoles(subRoles); Object.values(propList).forEach(function (item) { const section = document.querySelector("#" + item.name); @@ -583,7 +591,7 @@ function ariaAttributeReferences() { let myList = []; item.roles.forEach(function (role) { // TODO: can we simplify this? - let children = getAllSubRoles(role); + let children = descendantRoles[role] || []; // Some subroles have required properties which are also required by the superclass. // Example: The checked state of radio, which is also required by superclass checkbox. // We only want to include these one time, so filter out the subroles. From 46b23321f45e417940c369e04645dd346b6a4918 Mon Sep 17 00:00:00 2001 From: Peter Krautzberger Date: Wed, 13 Dec 2023 18:28:40 +0100 Subject: [PATCH 79/93] refactor(aria.js): extract propList loop Extracts the loop into a function in the global scope; not pretty but hopefully better than before. test.sh run is clean. --- script/aria.js | 139 ++++++++++++++++++++++++++----------------------- 1 file changed, 75 insertions(+), 64 deletions(-) diff --git a/script/aria.js b/script/aria.js index 545d5f9..a1aec6e 100644 --- a/script/aria.js +++ b/script/aria.js @@ -457,6 +457,78 @@ const generateHTMLIndices = (rdefs) => { ).outerHTML = `
        ${fromHeading}
      `; }; +/** + * The propList loop. + * @param {Object} propList - the propList + * @param {Object} descendantRoles - the list of "descendant" roles + * @param {Object} item - value from object.values(propList) + * @returns + */ +const propListLoop = function (propList, descendantRoles, item) { + const section = document.querySelector("#" + item.name); + let placeholder = section.querySelector( + ".state-applicability, .property-applicability" + ); + const placeholderText = placeholder.innerText; + // Current values for placeholderText: + // * "All elements of the base markup" + // * "Placeholder" + // * "Use as a global deprecated in ARIA 1.2" + // * "All elements of the base markup except for some roles or elements that prohibit its use" + // TODO: Maybe use a data attribute instead? + + // Case: nothing to od + if (placeholderText === "All elements of the base markup") return; + + // update roles list: sort & maybe remove roletype + item.roles.sort(); + if (placeholderText !== "Placeholder") + item.roles.splice(item.roles.indexOf("roletype"), 1); + + // Case: partially prohibited + if ( + placeholderText === + "All elements of the base markup except for some roles or elements that prohibit its use" + ) { + // for prohibited roles the roles list just includes those roles which are prohibited... weird I know but it is what it is + + placeholder.innerHTML = `All elements of the base markup except for the following roles: ${item.roles + .map((role) => `${role}`) + .join(", ")}`; + return; + } + + // Otherwise, i.e., + // Cases: placeholderText "Placeholder" or "Use as a global deprecated in ARIA 1.2" + + // populate placeholder + placeholder.innerHTML = `
        \n${item.roles + .map((role) => `
      • ${role}
      • \n`) + .join("")}
      \n`; + + // also update any inherited roles + const placeholderInheritedRoles = section.querySelector( + ".state-descendants, .property-descendants" + ); + let myList = []; + item.roles.forEach(function (role) { + // TODO: can we simplify this? + let children = descendantRoles[role] || []; + // Some subroles have required properties which are also required by the superclass. + // Example: The checked state of radio, which is also required by superclass checkbox. + // We only want to include these one time, so filter out the subroles. + children = children.filter(function (subrole) { + return subrole.indexOf(propList[item.name].roles) === -1; + }); + myList = myList.concat(children); + }); + + placeholderInheritedRoles.innerHTML = `
        \n${[...new Set(myList)] + .sort() + .map((role) => `
      • ${role}
      • \n`) + .join("")}
      \n`; +}; + function ariaAttributeReferences() { const propList = {}; const globalSP = []; @@ -542,70 +614,9 @@ function ariaAttributeReferences() { }; const descendantRoles = createDescendantRoles(subRoles); - Object.values(propList).forEach(function (item) { - const section = document.querySelector("#" + item.name); - let placeholder = section.querySelector( - ".state-applicability, .property-applicability" - ); - const placeholderText = placeholder.innerText; - // Current values for placeholderText: - // * "All elements of the base markup" - // * "Placeholder" - // * "Use as a global deprecated in ARIA 1.2" - // * "All elements of the base markup except for some roles or elements that prohibit its use" - // TODO: Maybe use a data attribute instead? - - // Case: nothing to od - if (placeholderText === "All elements of the base markup") return; - - // update roles list: sort & maybe remove roletype - item.roles.sort(); - if (placeholderText !== "Placeholder") - item.roles.splice(item.roles.indexOf("roletype"), 1); - - // Case: partially prohibited - if ( - placeholderText === - "All elements of the base markup except for some roles or elements that prohibit its use" - ) { - // for prohibited roles the roles list just includes those roles which are prohibited... weird I know but it is what it is - - placeholder.innerHTML = `All elements of the base markup except for the following roles: ${item.roles - .map((role) => `${role}`) - .join(", ")}`; - return; - } - - // Otherwise, i.e., - // Cases: placeholderText "Placeholder" or "Use as a global deprecated in ARIA 1.2" - - // populate placeholder - placeholder.innerHTML = `
        \n${item.roles - .map((role) => `
      • ${role}
      • \n`) - .join("")}
      \n`; - - // also update any inherited roles - const placeholderInheritedRoles = section.querySelector( - ".state-descendants, .property-descendants" - ); - let myList = []; - item.roles.forEach(function (role) { - // TODO: can we simplify this? - let children = descendantRoles[role] || []; - // Some subroles have required properties which are also required by the superclass. - // Example: The checked state of radio, which is also required by superclass checkbox. - // We only want to include these one time, so filter out the subroles. - children = children.filter(function (subrole) { - return subrole.indexOf(propList[item.name].roles) === -1; - }); - myList = myList.concat(children); - }); - - placeholderInheritedRoles.innerHTML = `
        \n${[...new Set(myList)] - .sort() - .map((role) => `
      • ${role}
      • \n`) - .join("")}
      \n`; - }); + Object.values(propList).forEach( + propListLoop.bind(null, propList, descendantRoles) + ); // assuming we found some parent roles, update those parents with their children subRoles.forEach((role) => { From c95148200616f5fff827cb7637f17af879c4eb4f Mon Sep 17 00:00:00 2001 From: Peter Krautzberger Date: Wed, 13 Dec 2023 18:30:39 +0100 Subject: [PATCH 80/93] refactor(aria.js): extract createDescendantRoles() Moves it into the global scope. test.sh run is clean. --- script/aria.js | 58 +++++++++++++++++++++++++++----------------------- 1 file changed, 31 insertions(+), 27 deletions(-) diff --git a/script/aria.js b/script/aria.js index a1aec6e..b86296e 100644 --- a/script/aria.js +++ b/script/aria.js @@ -458,11 +458,40 @@ const generateHTMLIndices = (rdefs) => { }; /** - * The propList loop. + * Creates dictionary of "descendant" roles + * @param {Object} subRoles - the subroles collection + * @returns + */ +const createDescendantRoles = (subRoles) => { + const descendantRoles = {}; + // Update state and property role references + const getAllSubRoles = function (role) { + const ref = subRoles[role]; + if (ref && ref.length) { + let myList = []; + ref.forEach(function (item) { + if (!myList.item) { + myList[item] = 1; + myList.push(item); + const childList = getAllSubRoles(item); + myList = myList.concat(childList); + } + }); + return myList; + } else { + return []; + } + }; + subRoles.forEach((item) => (descendantRoles[item] = getAllSubRoles(item))); + return descendantRoles; +}; + +/** + * The propList loop. * @param {Object} propList - the propList * @param {Object} descendantRoles - the list of "descendant" roles * @param {Object} item - value from object.values(propList) - * @returns + * @returns */ const propListLoop = function (propList, descendantRoles, item) { const section = document.querySelector("#" + item.name); @@ -587,31 +616,6 @@ function ariaAttributeReferences() { if (!skipIndex) { Object.values(roleInfo).forEach(buildInheritedStatesProperties); - const createDescendantRoles = (subRoles) => { - const descendantRoles = {}; - // Update state and property role references - const getAllSubRoles = function (role) { - const ref = subRoles[role]; - if (ref && ref.length) { - let myList = []; - ref.forEach(function (item) { - if (!myList.item) { - myList[item] = 1; - myList.push(item); - const childList = getAllSubRoles(item); - myList = myList.concat(childList); - } - }); - return myList; - } else { - return []; - } - }; - subRoles.forEach( - (item) => (descendantRoles[item] = getAllSubRoles(item)) - ); - return descendantRoles; - }; const descendantRoles = createDescendantRoles(subRoles); Object.values(propList).forEach( From 90d51fd85f1d69c01eaf21f51eb5ff0c98620d2b Mon Sep 17 00:00:00 2001 From: Peter Krautzberger Date: Wed, 13 Dec 2023 18:36:40 +0100 Subject: [PATCH 81/93] refactor(aria.js): extract generateHTMLRoleChildren() Moves child role HTML generation into generateHTMLRoleChildren() in global scope. test.sh run is clean. --- script/aria.js | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/script/aria.js b/script/aria.js index b86296e..8233ba5 100644 --- a/script/aria.js +++ b/script/aria.js @@ -558,6 +558,19 @@ const propListLoop = function (propList, descendantRoles, item) { .join("")}
    \n`; }; +/** + * In forEach loop, generates HTML for child role entries + * @param {String} role - subRoles array entry + * @param {Number} index - subRoles array index + * @param {Object} subRoles - overloaded subRoles array + */ +const generateHTMLRoleChildren = (role, index, subRoles) => { + const item = subRoles[role]; //TODO: cf. populateSubRoles overloading + document.querySelector(`#${role} .role-children`).innerHTML = `
      \n${item + .map((subrole) => `
    • ${subrole}
    • \n`) + .join("")}
    \n`; +}; + function ariaAttributeReferences() { const propList = {}; const globalSP = []; @@ -623,14 +636,7 @@ function ariaAttributeReferences() { ); // assuming we found some parent roles, update those parents with their children - subRoles.forEach((role) => { - const item = subRoles[role]; //TODO: cf. populateSubRoles overloading - document.querySelector( - `#${role} .role-children` - ).innerHTML = `
      \n${item - .map((subrole) => `
    • ${subrole}
    • \n`) - .join("")}
    \n`; - }); + subRoles.forEach(generateHTMLRoleChildren); } pruneUnusedRows(); From 52c228443ed18025b02664adabee9c0647f3c0d0 Mon Sep 17 00:00:00 2001 From: Peter Krautzberger Date: Fri, 15 Dec 2023 13:33:25 +0100 Subject: [PATCH 82/93] refactor(aria.js): review and improve TODO comments --- script/aria.js | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/script/aria.js b/script/aria.js index 8233ba5..c389dc6 100644 --- a/script/aria.js +++ b/script/aria.js @@ -161,6 +161,7 @@ const generateIndexGlobalStatesAndProperties = (globalSP) => { isState ? `title="${item.name}"` : "" }>${item.name}${isState ? " (state)" : ""}${ // TODO: consider moving "(state)" out of sref/pref tag; then maybe remove title attr for sref (after checking resolveReferences interference) + // TODO: cf. extractStatesProperties() and populateRoleInfoPropList() which have extra logic for title set here) item.prohibited ? " (Except where prohibited)" : "" }${ @@ -214,7 +215,7 @@ const populateSubRoles = (subRoles, rdef) => { .querySelectorAll(".role-parent rref") .forEach(function (roleref) { const s = roleref.innerText; - // TODO: this overloading seems weird + // SUPERTODO: this overloading seems weird if (!subRoles[s]) { subRoles.push(s); subRoles[s] = []; // TODO: should this be a set? @@ -229,7 +230,7 @@ const populateSubRoles = (subRoles, rdef) => { * @returns */ const extractStatesProperties = function (item) { - const name = item.getAttribute("title") || item.innerText; // TODO: tests indicate both are needed but why? + const name = item.getAttribute("title") || item.innerText; // TODO: raw HTML doesn't have sref/pref with title attributes but generateIndexGlobalStatesAndProperties() creates them const type = item.localName === "pref" ? "property" : "state"; const req = !!item.closest(".role-required-properties"); const dis = !!item.closest(".role-disallowed"); @@ -295,7 +296,7 @@ const populateRoleInfoPropList = function (roleInfo, propList, item) { // remember that the state or property is // referenced by this role PSDefs.forEach((node) => - propList[node.getAttribute("title") || node.innerText].roles.push( + propList[node.getAttribute("title") || node.innerText].roles.push( // TODO: cf. generateIndexGlobalStatesAndProperties() TODO for simplifying title || node.innerText content ) ); @@ -341,7 +342,7 @@ const buildInheritedStatesProperties = function (item) { ); if (!placeholder) return; - // TODO: simplify (from here until sortedList) + // SUPERTODO: simplify (from here until sortedList) let myList = []; item.parentRoles.forEach(function (role) { myList = myList.concat(getStates(role)); @@ -369,7 +370,7 @@ const buildInheritedStatesProperties = function (item) { return a.name < b.name ? -1 : a.name > b.name ? 1 : 0; }, []); - let prev; //TODO: get rid of "prev" + let prev; //SUPERTODO: get rid of "prev" const output = sortedList .map((property) => { if (prev === property.name) return ""; @@ -506,7 +507,7 @@ const propListLoop = function (propList, descendantRoles, item) { // * "All elements of the base markup except for some roles or elements that prohibit its use" // TODO: Maybe use a data attribute instead? - // Case: nothing to od + // Case: nothing to do if (placeholderText === "All elements of the base markup") return; // update roles list: sort & maybe remove roletype @@ -541,7 +542,7 @@ const propListLoop = function (propList, descendantRoles, item) { ); let myList = []; item.roles.forEach(function (role) { - // TODO: can we simplify this? + // SUPERTODO: can we simplify this? let children = descendantRoles[role] || []; // Some subroles have required properties which are also required by the superclass. // Example: The checked state of radio, which is also required by superclass checkbox. From 1e29ec2edeb3bb6378bf0c82e50ed467a50f8b47 Mon Sep 17 00:00:00 2001 From: Peter Krautzberger Date: Fri, 15 Dec 2023 17:29:12 +0100 Subject: [PATCH 83/93] refactor(aria.js): change subRoles to simple object Changes subRoles to a simple object of sets (instead of an overloaded array). * createDescendantRoles(): populate subRoles with sets * createDescendantRoles(): adjust to new subRoles structure; also adds TODO * generateHTMLRoleChildren() * change signature to work in Object.entries loop * adjust to new subRoles structure * ariaAttributeReferences() * change subRoles to object * adjust loop to iterate over entries. test.sh run is clean. --- script/aria.js | 63 +++++++++++++++++++++++--------------------------- 1 file changed, 29 insertions(+), 34 deletions(-) diff --git a/script/aria.js b/script/aria.js index c389dc6..d297a9c 100644 --- a/script/aria.js +++ b/script/aria.js @@ -214,13 +214,9 @@ const populateSubRoles = (subRoles, rdef) => { rdef.parentNode .querySelectorAll(".role-parent rref") .forEach(function (roleref) { - const s = roleref.innerText; - // SUPERTODO: this overloading seems weird - if (!subRoles[s]) { - subRoles.push(s); - subRoles[s] = []; // TODO: should this be a set? - } - subRoles[s].push(title); // TODO: should this be a set? + const parentRole = roleref.innerText; + const parentChildrenRoles = (subRoles[parentRole] ??= new Set()); + parentChildrenRoles.add(title); }); }; @@ -296,7 +292,8 @@ const populateRoleInfoPropList = function (roleInfo, propList, item) { // remember that the state or property is // referenced by this role PSDefs.forEach((node) => - propList[node.getAttribute("title") || node.innerText].roles.push( // TODO: cf. generateIndexGlobalStatesAndProperties() TODO for simplifying title || node.innerText + propList[node.getAttribute("title") || node.innerText].roles.push( + // TODO: cf. generateIndexGlobalStatesAndProperties() TODO for simplifying title || node.innerText content ) ); @@ -465,25 +462,24 @@ const generateHTMLIndices = (rdefs) => { */ const createDescendantRoles = (subRoles) => { const descendantRoles = {}; - // Update state and property role references - const getAllSubRoles = function (role) { - const ref = subRoles[role]; - if (ref && ref.length) { - let myList = []; - ref.forEach(function (item) { - if (!myList.item) { - myList[item] = 1; - myList.push(item); - const childList = getAllSubRoles(item); - myList = myList.concat(childList); - } - }); - return myList; - } else { - return []; - } + const getAllSubRoles = function (key) { + if (!subRoles[key]) return []; // NOTE: recursion end + const ref = [...subRoles[key]]; + // SUPERTODO: refactor the rest here: + let myList = []; + ref.forEach(function (item) { + if (!myList.item) { + myList[item] = 1; + myList.push(item); + const childList = getAllSubRoles(item); + myList = myList.concat(childList); + } + }); + return myList; }; - subRoles.forEach((item) => (descendantRoles[item] = getAllSubRoles(item))); + Object.keys(subRoles).forEach( + (item) => (descendantRoles[item] = getAllSubRoles(item)) + ); return descendantRoles; }; @@ -560,13 +556,12 @@ const propListLoop = function (propList, descendantRoles, item) { }; /** - * In forEach loop, generates HTML for child role entries - * @param {String} role - subRoles array entry - * @param {Number} index - subRoles array index - * @param {Object} subRoles - overloaded subRoles array + * In Object.entries loop, generates HTML for child role entries + * @param {String} role - subRoles key + * @param {Object} subRolesSet - subRoles value */ -const generateHTMLRoleChildren = (role, index, subRoles) => { - const item = subRoles[role]; //TODO: cf. populateSubRoles overloading +const generateHTMLRoleChildren = ([role, subroleSet]) => { + const item = [...subroleSet]; document.querySelector(`#${role} .role-children`).innerHTML = `
      \n${item .map((subrole) => `
    • ${subrole}
    • \n`) .join("")}
    \n`; @@ -611,7 +606,7 @@ function ariaAttributeReferences() { // 4. grab any local states and properties so we can hand those down to the children // - const subRoles = []; + const subRoles = {}; const rdefs = document.querySelectorAll("rdef"); const rdefsContainer = [...rdefs].map((node) => node.parentNode); @@ -637,7 +632,7 @@ function ariaAttributeReferences() { ); // assuming we found some parent roles, update those parents with their children - subRoles.forEach(generateHTMLRoleChildren); + Object.entries(subRoles).forEach(generateHTMLRoleChildren); } pruneUnusedRows(); From 460c92d3688057b345ae098e12bc9c54c719d37a Mon Sep 17 00:00:00 2001 From: Peter Krautzberger Date: Fri, 15 Dec 2023 17:36:36 +0100 Subject: [PATCH 84/93] refactor(aria.js): replace populateSubRoles() with generateSubRoles() Instead of declaring subRoles and populating it, generateSubRoles takes rdefs and returns subRoles. * generateSubRoles(): creates subRoles object and loops over given rdefs to populate it * ariaAttributeReferences(): replace populateSubRoles() with generateSubRoles() test.sh run is clean. --- script/aria.js | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/script/aria.js b/script/aria.js index d297a9c..867da5c 100644 --- a/script/aria.js +++ b/script/aria.js @@ -206,18 +206,22 @@ const generateHTMLRoleIndexEntry = function (item) { /** * Generates subrole information - * @param {Object} subRoles - the subRoles "array" (overloaded) - * @param {HTMLement} rdef - rdef element node + * @param {NodeList} rdefs - rdefs */ -const populateSubRoles = (subRoles, rdef) => { - const title = rdef.innerHTML; - rdef.parentNode - .querySelectorAll(".role-parent rref") - .forEach(function (roleref) { - const parentRole = roleref.innerText; - const parentChildrenRoles = (subRoles[parentRole] ??= new Set()); - parentChildrenRoles.add(title); - }); +const generateSubRoles = (rdefs) => { + const subRoles = {}; + rdefs.forEach((rdef) => { + const title = rdef.innerHTML; + rdef.parentNode + .querySelectorAll(".role-parent rref") + .forEach(function (roleref) { + const parentRole = roleref.innerText; + const parentChildrenRoles = (subRoles[parentRole] ??= + new Set()); + parentChildrenRoles.add(title); + }); + }); + return subRoles; }; /** @@ -606,12 +610,10 @@ function ariaAttributeReferences() { // 4. grab any local states and properties so we can hand those down to the children // - const subRoles = {}; - const rdefs = document.querySelectorAll("rdef"); const rdefsContainer = [...rdefs].map((node) => node.parentNode); - rdefs.forEach(populateSubRoles.bind(null, subRoles)); + const subRoles = generateSubRoles(rdefs); generateHTMLIndices(rdefs); From f50851ba4fa2f50ec483f6c7ba774e4a811912e3 Mon Sep 17 00:00:00 2001 From: Peter Krautzberger Date: Fri, 15 Dec 2023 18:12:39 +0100 Subject: [PATCH 85/93] refactor(aria.js): rewrite getAllSubRoles() // descendantRoles to use sets * createDescendantRoles(): rewrite getAllSubRoles() to produce sets as values in descendantRoles * propListLoop(): adjust to new descendantRoles structure test.sh run is clean. --- script/aria.js | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/script/aria.js b/script/aria.js index 867da5c..1698f18 100644 --- a/script/aria.js +++ b/script/aria.js @@ -467,19 +467,15 @@ const generateHTMLIndices = (rdefs) => { const createDescendantRoles = (subRoles) => { const descendantRoles = {}; const getAllSubRoles = function (key) { - if (!subRoles[key]) return []; // NOTE: recursion end - const ref = [...subRoles[key]]; - // SUPERTODO: refactor the rest here: - let myList = []; - ref.forEach(function (item) { - if (!myList.item) { - myList[item] = 1; - myList.push(item); - const childList = getAllSubRoles(item); - myList = myList.concat(childList); - } + const subroleSet = new Set(); + if (!subRoles[key]) return subroleSet; // NOTE: recursion end + const childRoles = [...subRoles[key]]; + childRoles.forEach(function (childRole) { + subroleSet.add(childRole); + const descendantRolesSet = getAllSubRoles(childRole); + [...descendantRolesSet].forEach((role) => subroleSet.add(role)); }); - return myList; + return subroleSet; }; Object.keys(subRoles).forEach( (item) => (descendantRoles[item] = getAllSubRoles(item)) @@ -543,11 +539,12 @@ const propListLoop = function (propList, descendantRoles, item) { let myList = []; item.roles.forEach(function (role) { // SUPERTODO: can we simplify this? - let children = descendantRoles[role] || []; + let children = descendantRoles[role]; + if (!children) return; // Some subroles have required properties which are also required by the superclass. // Example: The checked state of radio, which is also required by superclass checkbox. // We only want to include these one time, so filter out the subroles. - children = children.filter(function (subrole) { + children = [...children].filter(function (subrole) { return subrole.indexOf(propList[item.name].roles) === -1; }); myList = myList.concat(children); From bb33ac0c7fc8324ae1b1f5546c0b89681898d995 Mon Sep 17 00:00:00 2001 From: Peter Krautzberger Date: Fri, 15 Dec 2023 19:33:27 +0100 Subject: [PATCH 86/93] refactor(aria.js): improve propListLoop's HTML generation for inheritedRoles) Hopefully simplifies this. But also a big TODO question. test.sh run is clean. --- script/aria.js | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/script/aria.js b/script/aria.js index 1698f18..ccb4791 100644 --- a/script/aria.js +++ b/script/aria.js @@ -536,21 +536,24 @@ const propListLoop = function (propList, descendantRoles, item) { const placeholderInheritedRoles = section.querySelector( ".state-descendants, .property-descendants" ); - let myList = []; + let inheritedRoles = new Set(); item.roles.forEach(function (role) { - // SUPERTODO: can we simplify this? - let children = descendantRoles[role]; - if (!children) return; // Some subroles have required properties which are also required by the superclass. // Example: The checked state of radio, which is also required by superclass checkbox. // We only want to include these one time, so filter out the subroles. - children = [...children].filter(function (subrole) { - return subrole.indexOf(propList[item.name].roles) === -1; + if (!descendantRoles[role]) return; + descendantRoles[role].forEach((subrole) => { + if (subrole.indexOf(propList[item.name].roles) === -1) + inheritedRoles.add(subrole); + // TODO: the if-check doesn't make sense + // Should it be the other way around? I.e. + // if (propList[item.name].roles.indexOf(subrole) === -1) + // inheritedRoles.add(subrole); + // But this changes the spec, adding some, removing other entries }); - myList = myList.concat(children); }); - placeholderInheritedRoles.innerHTML = `
      \n${[...new Set(myList)] + placeholderInheritedRoles.innerHTML = `
        \n${[...inheritedRoles] .sort() .map((role) => `
      • ${role}
      • \n`) .join("")}
      \n`; From 51e0fdebf795efa5cce1ea289dd34779f82d08e7 Mon Sep 17 00:00:00 2001 From: Peter Krautzberger Date: Fri, 15 Dec 2023 19:34:56 +0100 Subject: [PATCH 87/93] refactor(aria.js): improve createDescendantRoles() Remove some unnecessary spread operations. test.sh run is clean. --- script/aria.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/script/aria.js b/script/aria.js index ccb4791..9bb0b5f 100644 --- a/script/aria.js +++ b/script/aria.js @@ -469,11 +469,10 @@ const createDescendantRoles = (subRoles) => { const getAllSubRoles = function (key) { const subroleSet = new Set(); if (!subRoles[key]) return subroleSet; // NOTE: recursion end - const childRoles = [...subRoles[key]]; - childRoles.forEach(function (childRole) { + subRoles[key].forEach(function (childRole) { subroleSet.add(childRole); const descendantRolesSet = getAllSubRoles(childRole); - [...descendantRolesSet].forEach((role) => subroleSet.add(role)); + descendantRolesSet.forEach((role) => subroleSet.add(role)); }); return subroleSet; }; From e005eae1fbb6d0c80315cd1ea5b2900c055caabb Mon Sep 17 00:00:00 2001 From: Peter Krautzberger Date: Mon, 18 Dec 2023 12:04:42 +0100 Subject: [PATCH 88/93] refactor(aria.js): improve buildInheritedStatesProperties() Simplify sortedList and add TODO / note on bug. test.sh run is clean. --- script/aria.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/script/aria.js b/script/aria.js index 9bb0b5f..d804f38 100644 --- a/script/aria.js +++ b/script/aria.js @@ -363,12 +363,13 @@ const buildInheritedStatesProperties = function (item) { const sortedList = reducedList.sort((a, b) => { if (a.name == b.name) { + //TODO: BUG: deprecated states&props do not actually appear at end // Ensure deprecated false properties occur first if (a.deprecated !== b.deprecated) { return a.deprecated ? 1 : b.deprecated ? -1 : 0; } } - return a.name < b.name ? -1 : a.name > b.name ? 1 : 0; + return a.name.localeCompare(b.name); }, []); let prev; //SUPERTODO: get rid of "prev" From 35ffe779521f4d86a5961b75b74654c532a913ee Mon Sep 17 00:00:00 2001 From: Peter Krautzberger Date: Mon, 18 Dec 2023 12:12:24 +0100 Subject: [PATCH 89/93] refactor(aria.js): improve buildInheritedStatesProperties() Get rid of placeholder variable by moving the querySelector to its only use. Note: if the placeholder doesn't exist, we should fix something. test.sh run is clean. --- script/aria.js | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/script/aria.js b/script/aria.js index d804f38..38e0423 100644 --- a/script/aria.js +++ b/script/aria.js @@ -312,6 +312,7 @@ const populateRoleInfoPropList = function (roleInfo, propList, item) { /** * TODO: depends on global roleInfo object + * Generats `allprops` array for a role entry in roleInfo * @param {string} role - name of a role * @returns */ @@ -338,10 +339,6 @@ const getStates = function (role) { * @param {Object} item - value from Object.values(roleInfo) */ const buildInheritedStatesProperties = function (item) { - const placeholder = document.querySelector( - "#" + item.fragID + " .role-inherited" - ); - if (!placeholder) return; // SUPERTODO: simplify (from here until sortedList) let myList = []; @@ -390,7 +387,9 @@ const buildInheritedStatesProperties = function (item) { }) .join(""); if (output !== "") { - placeholder.innerHTML = `
        \n${output}
      \n`; + document.querySelector( + "#" + item.fragID + " .role-inherited" + ).innerHTML = `
        \n${output}
      \n`; } }; From 684b6868fd85f128eb6b08ca457fc215bc1347ed Mon Sep 17 00:00:00 2001 From: Peter Krautzberger Date: Mon, 18 Dec 2023 17:26:24 +0100 Subject: [PATCH 90/93] refactor(aria.js): improve buildInheritedStatesProperties() Get rid of the prev variable; hopefully makes it clearer what we're doing. Also adds more TODOs for additional clarifications in the future. test.sh run is clean. roleInfo exports identically. --- script/aria.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/script/aria.js b/script/aria.js index 38e0423..ec52f01 100644 --- a/script/aria.js +++ b/script/aria.js @@ -369,12 +369,12 @@ const buildInheritedStatesProperties = function (item) { return a.name.localeCompare(b.name); }, []); - let prev; //SUPERTODO: get rid of "prev" - const output = sortedList - .map((property) => { - if (prev === property.name) return ""; - prev = property.name; - + const uniquePropNames = new Set(sortedList.map(prop => prop.name)) + // NOTE: uniquePropNames is needed because sortedList can have duplicates, in particular with different deprecation states. E.g., treeitem inherits aria-disabled from option but also as deprecated-in-1.2 from listitem. + // TODO: is it just luck that the not-deprecated state is listed first? (see same comment below) + const output = [...uniquePropNames] + .map((propName) => { + const property = sortedList.find(p => p.name === propName); // TODO: is it just luck that the not-deprecated state is listed first? const isState = property.is === "state"; const suffix = isState ? " (state)" : ""; const tag = isState ? "sref" : "pref"; From e89df4d49a2f1ef7bad06635f2d5257ae1382987 Mon Sep 17 00:00:00 2001 From: Peter Krautzberger Date: Mon, 18 Dec 2023 17:30:36 +0100 Subject: [PATCH 91/93] refactor(aria.js): write up TODOs for buildInheritedStatesProperties() Replaces super-todo with several questions. Also adds a TODO to the main function just before buildInheritedStatesProperties() test.sh run is clean. roleInfo export is clean. --- script/aria.js | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/script/aria.js b/script/aria.js index ec52f01..384ae4a 100644 --- a/script/aria.js +++ b/script/aria.js @@ -317,6 +317,7 @@ const populateRoleInfoPropList = function (roleInfo, propList, item) { * @returns */ const getStates = function (role) { + // TODO: pkra would like to use sets here but allprops part of roleInfo serializaton const ref = roleInfo[role]; if (!ref) { msg.pub("error", "No role definition for " + role); @@ -340,12 +341,19 @@ const getStates = function (role) { */ const buildInheritedStatesProperties = function (item) { - // SUPERTODO: simplify (from here until sortedList) + // BEGIN TODO: why can't we do, e.g., + // 1. in the main function: Object.keys(roleInfo).forEach(role=> getStates(role)); (see also TODO: near where buildInheritedStatesProperties() is called) + // - Then: let myList = item.allprops; (instead of myList = myList.concat(getStates(role))) + // - NOTE: the HTML stays the same but the exported roleInfo isn't. + // - TODO: BUG? in the existing roleInfo allprops only occurs 30 times let myList = []; item.parentRoles.forEach(function (role) { myList = myList.concat(getStates(role)); }); + // END TODO // strip out any items that we have locally + // BEGIN TODO: why can't we do myList.filter( inherited => item.localprops.includes(local => local.name === inherited.name))? + // or do something else to simplify this if (item.localprops.length && myList.length) { for (let j = myList.length - 1; j >= 0; j--) { item.localprops.forEach(function (x) { @@ -355,12 +363,13 @@ const buildInheritedStatesProperties = function (item) { }); } } - + const reducedList = [...new Set(myList)]; const sortedList = reducedList.sort((a, b) => { if (a.name == b.name) { //TODO: BUG: deprecated states&props do not actually appear at end + // NOTE: removing if (a.deprecated !== b.deprecated) seems to fix this // Ensure deprecated false properties occur first if (a.deprecated !== b.deprecated) { return a.deprecated ? 1 : b.deprecated ? -1 : 0; @@ -624,6 +633,7 @@ function ariaAttributeReferences() { // TODO: test this on a page where `skipIndex` is truthy if (!skipIndex) { + // TODO: why not run `Object.keys(roleInfo).forEach(role=> getStates(role))` here? (cf. TODO: in buildInheritedStatesProperties ) Object.values(roleInfo).forEach(buildInheritedStatesProperties); const descendantRoles = createDescendantRoles(subRoles); From c934495e70284b6fabb88d8e18ebd9497312410f Mon Sep 17 00:00:00 2001 From: Peter Krautzberger Date: Mon, 18 Dec 2023 17:31:10 +0100 Subject: [PATCH 92/93] fix(text.sh): checkout aria.js at start --- test.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/test.sh b/test.sh index aa27850..3b2fda1 100644 --- a/test.sh +++ b/test.sh @@ -3,6 +3,7 @@ # NOTE: Assumes there's a copy of w3c/aria in ../aria/ rm before.html after.html +git -C ../aria/ checkout ./common/script/aria.js echo "Run respec on ../aria/index.html to generate 'before.html'" npx respec --src ../aria/index.html --out before.html echo "Copy ./script/aria.js to ../aria/common/script/" From 30d08944f698ac9345f55089c5e719c8c8c6075c Mon Sep 17 00:00:00 2001 From: Peter Krautzberger Date: Mon, 18 Dec 2023 18:11:11 +0100 Subject: [PATCH 93/93] chore(aria.js): appease eslint --- script/aria.js | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/script/aria.js b/script/aria.js index 384ae4a..68ad486 100644 --- a/script/aria.js +++ b/script/aria.js @@ -340,11 +340,10 @@ const getStates = function (role) { * @param {Object} item - value from Object.values(roleInfo) */ const buildInheritedStatesProperties = function (item) { - // BEGIN TODO: why can't we do, e.g., // 1. in the main function: Object.keys(roleInfo).forEach(role=> getStates(role)); (see also TODO: near where buildInheritedStatesProperties() is called) // - Then: let myList = item.allprops; (instead of myList = myList.concat(getStates(role))) - // - NOTE: the HTML stays the same but the exported roleInfo isn't. + // - NOTE: the HTML stays the same but the exported roleInfo isn't. // - TODO: BUG? in the existing roleInfo allprops only occurs 30 times let myList = []; item.parentRoles.forEach(function (role) { @@ -352,7 +351,7 @@ const buildInheritedStatesProperties = function (item) { }); // END TODO // strip out any items that we have locally - // BEGIN TODO: why can't we do myList.filter( inherited => item.localprops.includes(local => local.name === inherited.name))? + // BEGIN TODO: why can't we do myList.filter( inherited => item.localprops.includes(local => local.name === inherited.name))? // or do something else to simplify this if (item.localprops.length && myList.length) { for (let j = myList.length - 1; j >= 0; j--) { @@ -363,12 +362,12 @@ const buildInheritedStatesProperties = function (item) { }); } } - + const reducedList = [...new Set(myList)]; const sortedList = reducedList.sort((a, b) => { if (a.name == b.name) { - //TODO: BUG: deprecated states&props do not actually appear at end + //TODO: BUG: deprecated states&props do not actually appear at end // NOTE: removing if (a.deprecated !== b.deprecated) seems to fix this // Ensure deprecated false properties occur first if (a.deprecated !== b.deprecated) { @@ -378,12 +377,12 @@ const buildInheritedStatesProperties = function (item) { return a.name.localeCompare(b.name); }, []); - const uniquePropNames = new Set(sortedList.map(prop => prop.name)) + const uniquePropNames = new Set(sortedList.map((prop) => prop.name)); // NOTE: uniquePropNames is needed because sortedList can have duplicates, in particular with different deprecation states. E.g., treeitem inherits aria-disabled from option but also as deprecated-in-1.2 from listitem. // TODO: is it just luck that the not-deprecated state is listed first? (see same comment below) const output = [...uniquePropNames] .map((propName) => { - const property = sortedList.find(p => p.name === propName); // TODO: is it just luck that the not-deprecated state is listed first? + const property = sortedList.find((p) => p.name === propName); // TODO: is it just luck that the not-deprecated state is listed first? const isState = property.is === "state"; const suffix = isState ? " (state)" : ""; const tag = isState ? "sref" : "pref";