a11y-outline

web extension to navigate document outlines easily
git clone https://git.ce9e.org/a11y-outline.git

commit
e4896639171b2ad883613850227620dcedf93e43
parent
116dcf9339b2c8f68b74c6b42e701c4f8a78e09d
Author
Tobias Bengfort <tobias.bengfort@posteo.de>
Date
2023-01-12 19:00
gather data async

Diffstat

M outline.js 95 +++++++++++++++++++++++++++++++++----------------------------
M treeview.js 28 ++++++++++++++++++++--------

2 files changed, 71 insertions, 52 deletions


diff --git a/outline.js b/outline.js

@@ -1,4 +1,4 @@
    1    -1 /* global chrome, aria, treeview */
   -1     1 /* global chrome, aria, treeview, updateTree */
    2     2 
    3     3 var DIALOG_ID = 'a11y-outline';
    4     4 
@@ -80,53 +80,57 @@ var buildTree = function(matches) {
   80    80 };
   81    81 
   82    82 var renderTree = function(role, dialog) {
   83    -1 	var matches = getMatches(role);
   84    -1 	var tree = buildTree(matches);
   -1    83 	var ul = treeview([], dialog.id + '-' + role);
   85    84 
   86    -1 	var ul = treeview(tree, dialog.id + '-' + role);
   -1    85 	dialog.appendChild(ul);
   87    86 
   88    -1 	var getTarget = function(a) {
   89    -1 		var href = a.getAttribute('href');
   90    -1 		var i = parseInt(href.substr(1), 10);
   91    -1 		return matches[i];
   92    -1 	};
   -1    87 	setTimeout(() => {
   -1    88 		var matches = getMatches(role);
   -1    89 		var tree = buildTree(matches);
   93    90 
   94    -1 	ul.addEventListener('click', function(event) {
   95    -1 		if (event.target.matches('a')) {
   96    -1 			event.preventDefault();
   97    -1 			dialog.close();
   98    -1 			focus(getTarget(event.target));
   99    -1 		}
  100    -1 	});
   -1    91 		updateTree(ul, tree, ul.id);
  101    92 
  102    -1 	var targetSelected = function() {
  103    -1 		var target = null;
   -1    93 		var getTarget = function(a) {
   -1    94 			var href = a.getAttribute('href');
   -1    95 			var i = parseInt(href.substr(1), 10);
   -1    96 			return matches[i];
   -1    97 		};
  104    98 
  105    -1 		if (ul === document.activeElement) {
  106    -1 			var selected = ul.querySelector('[aria-selected="true"] a');
  107    -1 			target = getTarget(selected);
  108    -1 		}
   -1    99 		ul.addEventListener('click', function(event) {
   -1   100 			if (event.target.matches('a')) {
   -1   101 				event.preventDefault();
   -1   102 				dialog.close();
   -1   103 				focus(getTarget(event.target));
   -1   104 			}
   -1   105 		});
  109   106 
  110    -1 		setTarget(target);
  111    -1 	};
   -1   107 		var targetSelected = function() {
   -1   108 			var target = null;
   -1   109 
   -1   110 			if (ul === document.activeElement) {
   -1   111 				var selected = ul.querySelector('[aria-selected="true"] a');
   -1   112 				target = getTarget(selected);
   -1   113 			}
  112   114 
  113    -1 	var mouseoutTimeoutId = null;
  114    -1 	ul.addEventListener('mouseover', event => {
  115    -1 		if (event.target.matches('a')) {
  116    -1 			clearTimeout(mouseoutTimeoutId);
  117    -1 			var target = getTarget(event.target);
  118   115 			setTarget(target);
  119    -1 		}
  120    -1 	});
  121    -1 	ul.addEventListener('mouseout', () => {
  122    -1 		clearTimeout(mouseoutTimeoutId);
  123    -1 		mouseoutTimeoutId = setTimeout(targetSelected, 100);
   -1   116 		};
   -1   117 
   -1   118 		var mouseoutTimeoutId = null;
   -1   119 		ul.addEventListener('mouseover', event => {
   -1   120 			if (event.target.matches('a')) {
   -1   121 				clearTimeout(mouseoutTimeoutId);
   -1   122 				var target = getTarget(event.target);
   -1   123 				setTarget(target);
   -1   124 			}
   -1   125 		});
   -1   126 		ul.addEventListener('mouseout', () => {
   -1   127 			clearTimeout(mouseoutTimeoutId);
   -1   128 			mouseoutTimeoutId = setTimeout(targetSelected, 100);
   -1   129 		});
   -1   130 		ul.addEventListener('focus', targetSelected);
   -1   131 		ul.addEventListener('select', targetSelected);
   -1   132 		ul.addEventListener('blur', targetSelected);
  124   133 	});
  125    -1 	ul.addEventListener('focus', targetSelected);
  126    -1 	ul.addEventListener('select', targetSelected);
  127    -1 	ul.addEventListener('blur', targetSelected);
  128    -1 
  129    -1 	dialog.appendChild(ul);
  130   134 };
  131   135 
  132   136 var updateVisiblePane = function(select, dialog) {
@@ -179,15 +183,18 @@ var quickNav = function() {
  179   183 };
  180   184 
  181   185 var _walk = function(root, fn) {
  182    -1 	fn(root);
  183    -1 	aria.getChildNodes(root).forEach(function(child) {
  184    -1 		_walk(child, fn);
  185    -1 	});
   -1   186 	var owners = document.querySelectorAll('[aria-owns]');
   -1   187 	var queue = [root];
   -1   188 	while (queue.length) {
   -1   189 		var item = queue.shift();
   -1   190 		fn(item);
   -1   191 		queue = aria.getChildNodes(item, owners).concat(queue);
   -1   192 	}
  186   193 };
  187   194 
  188   195 var walk = function(root, fn) {
  189   196 	try {
  190    -1 		_walk(document, function(node) {
   -1   197 		_walk(root, function(node) {
  191   198 			if (node.nodeType === node.ELEMENT_NODE) {
  192   199 				fn(node);
  193   200 			}

diff --git a/treeview.js b/treeview.js

@@ -170,12 +170,17 @@ var buildItem = function(data, id) {
  170   170 	return item;
  171   171 };
  172   172 
  173    -1 var buildList = function(data, id) {
  174    -1 	var list = document.createElement('ul');
   -1   173 var updateList = function(list, data, id) {
   -1   174 	list.innerHTML = '';
  175   175 	data.forEach(function(child, i) {
  176   176 		var item = buildItem(child, id + '.' + i);
  177   177 		list.appendChild(item);
  178   178 	});
   -1   179 };
   -1   180 
   -1   181 var buildList = function(data, id) {
   -1   182 	var list = document.createElement('ul');
   -1   183 	updateList(list, data, id);
  179   184 	return list;
  180   185 };
  181   186 
@@ -185,8 +190,16 @@ var buildGroup = function(data, id) {
  185   190 	return group;
  186   191 };
  187   192 
   -1   193 var updateTree = function(tree, data, id) {
   -1   194 	tree.innerHTML = '';
   -1   195 	updateList(tree, data, id);
   -1   196 
   -1   197 	var first = tree.querySelector('[role="treeitem"]');
   -1   198 	select(first);
   -1   199 };
   -1   200 
  188   201 var buildTree = function(data, id) {
  189    -1 	var tree = buildList(data, id);
   -1   202 	var tree = document.createElement('ul');
  190   203 	tree.setAttribute('role', 'tree');
  191   204 	tree.tabIndex = 0;
  192   205 	tree.id = id;
@@ -202,10 +215,9 @@ var buildTree = function(data, id) {
  202   215 		this.focus();
  203   216 	});
  204   217 
  205    -1 	var first = tree.querySelector('[role="treeitem"]');
  206    -1 	select(first);
  207    -1 
   -1   218 	updateTree(tree, data, id);
  208   219 	return tree;
  209   220 };
  210   221 
  211    -1 window.treeview = buildTree
  211    -1 
\ No newline at end of file
   -1   222 window.treeview = buildTree;
   -1   223 window.updateTree = updateTree
   -1   223 
\ No newline at end of file