blob: aae9b7bb39524570dab42b951472aff7e10fc259 [file] [edit]
// ariaChild.js - script for aria child specifications to use
// when integrating content. Requires a roleInfo.js file in the
// same directory that contains the roleInfo data structure.
//
var localRoleInfo = {};
// NOTE: this was taken from https://github.com/w3c/respec/blob/develop/src/core/utils.js
/**
* Creates and sets an ID to an element (elem)
* using a specific prefix if provided, and a specific text if given.
* @param {HTMLElement} elem element
* @param {String} pfx prefix
* @param {String} txt text
* @param {Boolean} noLC do not convert to lowercase
* @returns {String} generated (or existing) id for element
*/
function addId(elem, pfx = "", txt = "", noLC = false) {
if (elem.id) {
return elem.id;
}
if (!txt) {
txt = (elem.title ? elem.title : elem.textContent).trim();
}
let id = noLC ? txt : txt.toLowerCase();
id = id
.trim()
.normalize("NFD")
.replace(/[\u0300-\u036f]/g, "")
.replace(/\W+/gim, "-")
.replace(/^-+/, "")
.replace(/-+$/, "");
if (!id) {
id = "generatedID";
} else if (/\.$/.test(id) || !/^[a-z]/i.test(pfx || id)) {
id = `x${id}`; // trailing . doesn't play well with jQuery
}
if (pfx) {
id = `${pfx}-${id}`;
}
if (elem.ownerDocument.getElementById(id)) {
let i = 0;
let nextId = `${id}-${i}`;
while (elem.ownerDocument.getElementById(nextId)) {
i += 1;
nextId = `${id}-${i}`;
}
id = nextId;
}
elem.id = id;
return id;
}
function ariaAttributeReferences() {
{
var propList = {};
var globalSP = [];
var skipIndex = 0;
var myURL = document.URL;
if (myURL.match(/\?fast/)) {
skipIndex = 1;
}
// process the document before anything else is done
// first get the properties
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");
if (!title) {
title = content;
}
sp.className = type + "-name";
sp.title = title;
sp.innerHTML =
"<code>" +
content +
'</code> <span class="type-indicator">(' +
type +
")</span>";
sp.setAttribute("aria-describedby", "desc-" + title);
var dRef = item.nextElementSibling;
var desc = dRef.firstElementChild.innerHTML;
dRef.id = "desc-" + title;
dRef.setAttribute("role", "definition");
var heading = document.createElement("h3");
heading.appendChild(sp);
container.replaceChild(heading, item);
// add this item to the index
propList[title] = {
is: type,
title: title,
name: content,
desc: desc,
roles: [],
};
var abstract = container.querySelector(
"." + type + "-applicability"
);
if (
(abstract.textContent || abstract.innerText) ===
"All elements of the base markup"
) {
globalSP.push({
is: type,
title: title,
name: content,
desc: desc,
});
}
// the pdef/sdef is gone. if we are in a div, convert that div to a section
if (container.nodeName.toLowerCase() == "div") {
// change the enclosing DIV to a section with notoc
var 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;
sec.innerHTML = theContents;
container.parentNode.replaceChild(sec, container);
}
});
// what about roles?
//
// we need to do a few things here:
// 1. expand the rdef elements.
// 2. accumulate the roles into a table for the indices
// 3. grab the parent role reference so we can build up the tree
// 4. grab any local states and properties so we can hand those down to the children
//
var subRoles = [];
var roleIndex = "";
Array.prototype.slice
.call(document.querySelectorAll("rdef"))
.forEach(function (item) {
var container = item.parentNode;
var content = item.innerHTML;
var sp = document.createElement("h3");
var title = item.getAttribute("title");
if (!title) {
title = content;
}
var pnID = addId(container, "", title);
sp.className = "role-name";
sp.title = title;
// is this a role or an abstract role
var type = "role";
var abstract = container.querySelectorAll(".role-abstract");
if (abstract.innerText === "True") {
type = "abstract role";
}
sp.innerHTML =
"<code>" +
content +
'</code> <span class="type-indicator">(' +
type +
")</span>";
// sp.id = title;
sp.setAttribute("aria-describedby", "desc-" + title);
var dRef = item.nextElementSibling;
var desc = dRef.firstElementChild.innerHTML;
dRef.id = "desc-" + title;
dRef.setAttribute("role", "definition");
container.replaceChild(sp, item);
roleIndex +=
'<dt><a href="#' +
pnID +
'" class="role-reference">' +
content +
"</a></dt>\n";
roleIndex += "<dd>" + desc + "</dd>\n";
// grab info about this role
// do we have a parent class? if so, put us in that parents list
var node = container.querySelectorAll(".role-parent rref");
// s will hold the name of the parent role if any
var s = null;
var parentRoles = [];
if (node) {
Array.prototype.slice
.call(node)
.forEach(function (roleref) {
s = roleref.textContent || roleref.innerText;
if (!subRoles[s]) {
subRoles.push(s);
subRoles[s] = [];
}
subRoles[s].push(title);
parentRoles.push(s);
});
}
// are there supported states / properties in this role?
var attrs = [];
Array.prototype.slice
.call(
container.querySelectorAll(
".role-properties, .role-required-properties"
)
)
.forEach(function (node) {
if (
node &&
((node.textContent &&
node.textContent.length !== 1) ||
(node.innerText && node.innerText.length !== 1))
) {
// looks like we do
Array.prototype.slice
.call(node.querySelectorAll("pref,sref"))
.forEach(function (item) {
var name = item.getAttribute("title");
if (!name) {
name =
item.textContent || item.innerText;
}
var type =
item.localName === "pref"
? "property"
: "state";
var req = node.classList.contains(
"role-required-properties"
);
attrs.push({
is: type,
name: name,
required: req,
});
// remember that the state or property is
// referenced by this role
propList[name].roles.push(title);
});
}
});
localRoleInfo[title] = {
name: title,
fragID: pnID,
parentRoles: parentRoles,
localprops: attrs,
};
if (container.nodeName.toLowerCase() == "div") {
// change the enclosing DIV to a section with notoc
var 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;
sec.innerHTML = theContents;
container.parentNode.replaceChild(sec, container);
}
});
var getStates = function (role) {
var ref = localRoleInfo[role];
if (!ref) {
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.slice();
ref.parentRoles.forEach(function (item) {
var pList = getStates(item);
pList.forEach(function (item) {
myList.push(item);
});
});
ref.allprops = myList;
return myList;
}
};
if (!skipIndex) {
// build up the complete inherited SP lists for each role
Object.entries(localRoleInfo).forEach(function (index) {
item = index[1];
var output = "";
var placeholder = document.querySelector(
"#" + item.fragID + " .role-inherited"
);
if (placeholder) {
var myList = [];
item.parentRoles.forEach(function (role) {
myList.push(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--) {
item.localprops.forEach(function (x) {
if (x.name == myList[j].name) {
myList.splice(j, 1);
}
});
}
}
var sortedList = [];
sortedList = myList.sort(function (a, b) {
return a.name < b.name ? -1 : a.name > b.name ? 1 : 0;
});
var prev;
sortedList = sortedList[0];
if (sortedList) {
for (var k = 0; k < sortedList.length; k++) {
var role = sortedList[k];
var req = "";
if (role.required) {
req = " <strong>(required)</strong>";
}
if (prev != role.name) {
output += "<li>";
if (role.is === "state") {
output +=
"<sref " +
(role.prohibited
? "data-prohibited "
: "") +
(role.deprecated
? "data-deprecated "
: "") +
'title="' +
role.name +
'">' +
role.name +
" (state)</sref>" +
req;
} else {
output +=
"<pref " +
(role.prohibited
? "data-prohibited "
: "") +
(role.deprecated
? "data-deprecated "
: "") +
">" +
role.name +
"</pref>" +
req;
}
if (role.prohibited) {
output += " (Except where prohibited)";
}
if (role.deprecated) {
output +=
" (Global use deprecated in ARIA 1.2)";
}
output += "</li>\n";
prev = role.name;
}
}
}
if (output !== "") {
output = "<ul>\n" + output + "</ul>\n";
placeholder.innerHTML = output;
}
}
});
// Update state and property role references
var getAllSubRoles = function (role) {
var ref = subRoles[role];
if (ref && ref.length) {
var myList = [];
ref.forEach(function (item) {
if (!myList.item) {
myList[item] = 1;
myList.push(item);
var childList = getAllSubRoles(item);
childList.forEach(function (item) {
myList.push(childList);
});
}
});
return myList;
} else {
return [];
}
};
Object.values(propList).forEach(function (item) {
var output = "";
var section = document.querySelector("#" + item.name);
var placeholder = section.querySelector(
".state-applicability, .property-applicability"
);
if (
placeholder &&
(placeholder.textContent || placeholder.innerText) ===
"Placeholder" &&
item.roles.length
) {
// update the used in roles list
var sortedList = [];
sortedList = item.roles.sort();
for (var j = 0; j < sortedList.length; j++) {
output +=
"<li><rref>" + sortedList[j] + "</rref></li>\n";
}
if (output !== "") {
output = "<ul>\n" + output + "</ul>\n";
}
placeholder.innerHTML = output;
// also update any inherited roles
var myList = [];
item.roles.forEach(function (role) {
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.
.filter(function (subrole) {
return (
propList[item.name].roles.indexOf(
subrole
) === -1
);
})
.forEach(function (subrole) {
myList.push(chlidren);
});
});
placeholder = section.querySelector(
".state-descendants, .property-descendants"
);
if (placeholder && myList.length) {
sortedList = myList.sort();
output = "";
var last = "";
for (var k = 0; k < sortedList.length; k++) {
var lItem = sortedList[k];
if (last != lItem) {
output +=
"<li><rref>" + lItem + "</rref></li>\n";
last = lItem;
}
}
if (output !== "") {
output = "<ul>\n" + output + "</ul>\n";
}
placeholder.innerHTML = output;
}
}
});
// spit out the index
var node = document.getElementById("index_role");
var parentNode = node.parentNode;
var list = document.createElement("dl");
list.id = "index_role";
list.className = "compact";
list.innerHTML = roleIndex;
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) {
return a < b ? -1 : a > b ? 1 : 0;
});
var output = "<ul>\n";
for (var j = 0; j < sortedList.length; j++) {
output += "<li><rref>" + sortedList[j] + "</rref></li>\n";
}
output += "</ul>\n";
// put it somewhere
var subRolesContainer = document.querySelector(
"#" + subRoles[i]
);
if (subRolesContainer) {
var subRolesListContainer =
subRolesContainer.querySelector(".role-children");
if (subRolesListContainer) {
subRolesListContainer.innerHTML = output;
}
}
}
}
updateReferences(document);
// prune out unused rows throughout the document
Array.prototype.slice
.call(
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();
}
});
}
}
require(["core/pubsubhub"], function (respecEvents) {
respecEvents.sub("end", function (msg) {
if (msg == "w3c/conformance") {
ariaAttributeReferences();
}
});
});