百度网盘无障碍优化

百度网盘无障碍优化脚本

  1. // ==UserScript==
  2. // @name 百度网盘无障碍优化
  3. // @namespace https://accjs.org/
  4. // @version 0.1
  5. // @description 百度网盘无障碍优化脚本
  6. // @description alt加x切换文件,alt加z切换返回、上传、对话框等
  7. // @author 杨永全
  8. // @match https://pan.baidu.com/*
  9. // @grant none
  10. // ==/UserScript==
  11.  
  12. (function() {
  13. cproc();
  14. acmo(cproc);
  15. aamo(aproc);
  16.  
  17. document.addEventListener('keydown', function(e) {
  18. let t = e.target || null;
  19. let xSelector = '.accjs-check-all, .file-name .text a';
  20. let xKey = e.keyCode == 88;
  21. let zSelector = 'a[node-type="upload"], a[data-deep="-1"], .dialog, .context-menu';
  22. let zKey = e.keyCode == 90;
  23. if (e.altKey && e.shiftKey && xKey) {
  24. e.preventDefault();
  25. previousFocus(xSelector);
  26. } else if (e.altKey && xKey) {
  27. e.preventDefault();
  28. nextFocus(xSelector);
  29. } else if (e.altKey && e.shiftKey && zKey) {
  30. e.preventDefault();
  31. previousFocus(zSelector);
  32. } else if (e.altKey && zKey) {
  33. e.preventDefault();
  34. nextFocus(zSelector);
  35. } else if(e.keyCode == 32 || e.keyCode == 13) {
  36. if(t.matches('.accjs-click')) {
  37. t.click();
  38. }
  39. }
  40. }, null);
  41.  
  42. function aproc(records) {
  43. document.querySelectorAll('.accjs-checkbox').forEach(function(el) {
  44. let p = el.parentNode;
  45. let checked = false;
  46. //p.matches('.EzubGg, .drys0wyn, .JS-item-active') || p.classList.contains('g-clearfix') && p.classList.length = 4 || p.classList.length == 3;
  47. if(p.classList.length == 4) {
  48. checked = true;
  49. } else if(p.classList.length == 3 && p.classList.contains('g-clearfix')) {
  50. checked = false;
  51. } else if(p.classList.length == 3) {
  52. checked = true;
  53. }
  54. el.setAttribute('aria-checked', checked);
  55. });
  56. document.querySelectorAll('.treeview-node').forEach(function(el) {
  57. let checked = el.matches('.treeview-node-on');
  58. el.setAttribute('aria-checked', checked);
  59. });
  60. }
  61.  
  62. function cproc() {
  63. //search input
  64. document.querySelectorAll('input[name="q"]:not(.accjs-has)').forEach(function(el) {
  65. el.setAttribute('aria-label', '搜索关键词');
  66. addClass(el, 'accjs-has');
  67. });
  68.  
  69. //dialog close button
  70. document.querySelectorAll('.dialog-close:not(.accjs-has)').forEach(function(el) {
  71. el.setAttribute('role', 'button');
  72. el.setAttribute('tabindex', '0');
  73. el.setAttribute('aria-label', '关闭');
  74. addClass(el, 'accjs-has accjs-click');
  75. });
  76.  
  77. //treview for files
  78. document.querySelectorAll('.treeview-node').forEach(function(el) {
  79. el.setAttribute('tabindex', '0');
  80. el.setAttribute('role', 'checkbox');
  81. let checked = el.matches('.treeview-node-on');
  82. el.setAttribute('aria-checked', checked);
  83. addClass(el, 'accjs-has accjs-click');
  84. });
  85.  
  86. //check all
  87. document.querySelectorAll('[data-key="name"] > div[node-type], [data-key="server_filename"] > div[node-type]').forEach(function(el) {
  88. el.setAttribute('role', 'checkbox');
  89. el.setAttribute('tabindex', '0');
  90. el.setAttribute('aria-label', '全选');
  91. addClass(el, 'accjs-has accjs-click accjs-checkbox accjs-check-all');
  92. });
  93.  
  94. //filelist
  95. document.querySelectorAll('dd[_installed]').forEach(function(el) {
  96. let _el = el.querySelector('[node-type]');
  97. if(_el !== null) {
  98. let checked = el.matches('.drys0wyn');
  99. let filename = el.querySelector('a').innerText || '';
  100. _el.setAttribute('role', 'checkbox');
  101. _el.setAttribute('tabindex', '0');
  102. _el.setAttribute('aria-label', filename);
  103. _el.setAttribute('aria-checked', checked);
  104. addClass(_el, 'accjs-has accjs-click accjs-checkbox');
  105. }
  106. });
  107.  
  108. //context-menu
  109. document.querySelectorAll('.context-menu').forEach(function(el) {
  110. el.setAttribute('aria-label', '上下文菜单');
  111. let mis = el.querySelectorAll('li[data-check-display]');
  112. mis.forEach(function(el) {
  113. el.setAttribute('tabindex', '0');
  114. el.setAttribute('role', 'link');
  115. addClass(el, 'accjs-has accjs-click');
  116. });
  117. if(mis.length > 0) {
  118. mis[0].focus();
  119. }
  120. addClass(el, 'accjs-has');
  121. });
  122.  
  123. //end cproc function
  124. }
  125.  
  126.  
  127. function addClass(el, classes) {
  128. classes.split(' ').forEach(function(className) {
  129. el.classList.add(className);
  130. });
  131. }
  132.  
  133. function isVisible(t) {
  134. return !! (!t.hasAttribute('disabled') && t.getAttribute('aria-hidden') !== 'true' && t.offsetParent !== null);
  135. }
  136.  
  137. function gi(i, len, op) {
  138. let n = op == '+' ? +1 : -1;
  139. i = i + n;
  140. if (i >= len) {
  141. i = 0;
  142. }
  143. if (i < 0) {
  144. i = len - 1;
  145. }
  146. return i;
  147. }
  148.  
  149. function _toFocus(el) {
  150. let tagName = el.tagName.toLowerCase();
  151. let tagNames = ['div', 'p', 'span', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'ul', 'ol', 'li', 'form', 'img', 'nav', 'header', 'main', 'footer', 'section', 'aside'];
  152. if (tagNames.includes(tagName) || (tagName == 'a' && !el.hasAttribute('href'))) {
  153. if (!el.hasAttribute('tabindex')) {
  154. el.setAttribute('tabindex', '-1');
  155. }
  156. }
  157. el.focus();
  158. }
  159.  
  160. function toFocus(focusSelector, op) {
  161. let els = Array.prototype.slice.call(document.body.querySelectorAll('*'));
  162. let len = els.length;
  163. let aeIndex = Math.max(0, els.indexOf(document.activeElement));
  164. let i = aeIndex == 0 ? 0 : gi(aeIndex, len, op);
  165. do {
  166. if (els[i].matches(focusSelector) && isVisible(els[i])) {
  167. _toFocus(els[i]);
  168. break;
  169. }
  170. i = gi(i, len, op);
  171. } while ( i != aeIndex );
  172. }
  173.  
  174. function nextFocus(selector) {
  175. toFocus(selector, '+');
  176. }
  177.  
  178. function previousFocus(selector) {
  179. toFocus(selector, '-');
  180. }
  181.  
  182. function aamo(proc) {
  183. var mo = new MutationObserver((records) => {
  184. proc(records);
  185. });
  186. mo.observe(document.body, {
  187. 'attributes': true,
  188. //'attributeOldValue': true,
  189. 'subtree': true,
  190. 'attributeFilter': ['class']
  191. });
  192. return mo;
  193. }
  194.  
  195. function acmo(proc) {
  196. let mo = new MutationObserver((records) => {
  197. proc();
  198. });
  199. mo.observe(document.body, {
  200. 'childList': true,
  201. 'subtree': true
  202. });
  203. return mo;
  204. }
  205.  
  206. })();