Skip to content

Commit f4021df

Browse files
committed
Very early script for Jira.
1 parent 1a62e89 commit f4021df

File tree

1 file changed

+154
-0
lines changed

1 file changed

+154
-0
lines changed

JiraA11yFixes.user.js

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
// ==UserScript==
2+
// @name Jira Accessibility Fixes
3+
// @namespace http://axSgrease.nvaccess.org/
4+
// @description Improves the accessibility of Jira.
5+
// @author James Teh <jteh@mozilla.com>
6+
// @copyright 2019-2021 Mozilla Corporation, Derek Riemer
7+
// @license Mozilla Public License version 2.0
8+
// @version 2021.1
9+
// @include https://*.atlassian.net/browse/*
10+
// ==/UserScript==
11+
12+
/*** Functions for common tweaks. ***/
13+
14+
function makeHeading(el, level) {
15+
el.setAttribute("role", "heading");
16+
el.setAttribute("aria-level", level);
17+
}
18+
19+
function makeRegion(el, label) {
20+
el.setAttribute("role", "region");
21+
el.setAttribute("aria-label", label);
22+
}
23+
24+
function makeButton(el, label) {
25+
el.setAttribute("role", "button");
26+
if (label) {
27+
el.setAttribute("aria-label", label);
28+
}
29+
}
30+
31+
function makePresentational(el) {
32+
el.setAttribute("role", "presentation");
33+
}
34+
35+
function setLabel(el, label) {
36+
el.setAttribute("aria-label", label);
37+
}
38+
39+
function makeHidden(el) {
40+
el.setAttribute("aria-hidden", "true");
41+
}
42+
43+
function setExpanded(el, expanded) {
44+
el.setAttribute("aria-expanded", expanded ? "true" : "false");
45+
}
46+
47+
var idCounter = 0;
48+
// Get a node's id. If it doesn't have one, make and set one first.
49+
function setAriaIdIfNecessary(elem) {
50+
if (!elem.id) {
51+
elem.setAttribute("id", "axsg-" + idCounter++);
52+
}
53+
return elem.id;
54+
}
55+
56+
function makeElementOwn(parentElement, listOfNodes){
57+
ids = [];
58+
for(let node of listOfNodes){
59+
ids.push(setAriaIdIfNecessary(node));
60+
}
61+
parentElement.setAttribute("aria-owns", ids.join(" "));
62+
}
63+
64+
// Focus something even if it wasn't made focusable by the author.
65+
function forceFocus(el) {
66+
let focusable = el.hasAttribute("tabindex");
67+
if (focusable) {
68+
el.focus();
69+
return;
70+
}
71+
el.setAttribute("tabindex", "-1");
72+
el.focus();
73+
}
74+
75+
/*** Code to apply the tweaks when appropriate. ***/
76+
77+
function applyTweak(el, tweak) {
78+
if (Array.isArray(tweak.tweak)) {
79+
let [func, ...args] = tweak.tweak;
80+
func(el, ...args);
81+
} else {
82+
tweak.tweak(el);
83+
}
84+
}
85+
86+
function applyTweaks(root, tweaks, checkRoot) {
87+
for (let tweak of tweaks) {
88+
for (let el of root.querySelectorAll(tweak.selector)) {
89+
try {
90+
applyTweak(el, tweak);
91+
} catch (e) {
92+
console.log("Exception while applying tweak for '" + tweak.selector + "': " + e);
93+
}
94+
}
95+
if (checkRoot && root.matches(tweak.selector)) {
96+
try {
97+
applyTweak(root, tweak);
98+
} catch (e) {
99+
console.log("Exception while applying tweak for '" + tweak.selector + "': " + e);
100+
}
101+
}
102+
}
103+
}
104+
105+
let observer = new MutationObserver(function(mutations) {
106+
for (let mutation of mutations) {
107+
try {
108+
if (mutation.type === "childList") {
109+
for (let node of mutation.addedNodes) {
110+
if (node.nodeType != Node.ELEMENT_NODE) {
111+
continue;
112+
}
113+
applyTweaks(node, DYNAMIC_TWEAKS, true);
114+
}
115+
} else if (mutation.type === "attributes") {
116+
applyTweaks(mutation.target, DYNAMIC_TWEAKS, true);
117+
}
118+
} catch (e) {
119+
// Catch exceptions for individual mutations so other mutations are still handled.
120+
console.log("Exception while handling mutation: " + e);
121+
}
122+
}
123+
});
124+
125+
function init() {
126+
applyTweaks(document, LOAD_TWEAKS, false);
127+
applyTweaks(document, DYNAMIC_TWEAKS, false);
128+
options = {childList: true, subtree: true};
129+
if (DYNAMIC_TWEAK_ATTRIBS.length > 0) {
130+
options.attributes = true;
131+
options.attributeFilter = DYNAMIC_TWEAK_ATTRIBS;
132+
}
133+
observer.observe(document, options);
134+
}
135+
136+
/*** Define the actual tweaks. ***/
137+
138+
// Tweaks that only need to be applied on load.
139+
const LOAD_TWEAKS = [
140+
];
141+
142+
// Attributes that should be watched for changes and cause dynamic tweaks to be
143+
// applied.
144+
const DYNAMIC_TWEAK_ATTRIBS = [];
145+
146+
// Tweaks that must be applied whenever an element is added/changed.
147+
const DYNAMIC_TWEAKS = [
148+
// Make comments into headings.
149+
{selector: '.ezs3xpg1',
150+
tweak: [makeHeading, 3]},
151+
];
152+
153+
/*** Lights, camera, action! ***/
154+
init();

0 commit comments

Comments
 (0)