英文前部加粗

网页英文前部加粗脚本 Ctrl + B / ⌘ + B 开启关闭

目前为 2022-05-23 提交的版本。查看 最新版本

  1. // ==UserScript==
  2. // @name 英文前部加粗
  3. // @namespace https://github.com/itorr/bionic-reading.user.js
  4. // @version 0.8.3
  5. // @description 网页英文前部加粗脚本 Ctrl + B / ⌘ + B 开启关闭
  6. // @author itorr
  7. // @match *://*/*
  8. // @grant none
  9. // @license MIT
  10. // @run-at document-idle
  11. // @supportURL https://github.com/itorr/bionic-reading.user.js/issues
  12. // ==/UserScript==
  13.  
  14. let isBionic = false;
  15.  
  16. const enCodeHTML = s=> s.replace(/[\u00A0-\u9999<>\&]/g,w=>'&#'+w.charCodeAt(0)+';');
  17.  
  18. let body = document.body;
  19.  
  20. if(/weibo/.test(location.hostname)){
  21. const wbMainEl = document.querySelector('.WB_main');
  22. if(wbMainEl) body = wbMainEl;
  23.  
  24. // 修复旧版微博自定义样式失效 bug
  25. const customStyleEl = document.querySelector('#custom_style');
  26. if(customStyleEl) customStyleEl.removeAttribute('id');
  27. }
  28.  
  29. const styleEl = document.createElement('style');
  30. styleEl.innerHTML = `
  31. bbb{
  32. font-weight:bold;
  33. opacity: 1;
  34. }
  35. html[data-site="greasyfork"] a bionic{
  36. pointer-events: none;
  37. }
  38. `;
  39.  
  40. document.documentElement.setAttribute('data-site',location.hostname.replace(/\.\w+$|www\./ig,''))
  41.  
  42. const excludeTagNames = [
  43. 'script','style','xmp',
  44. 'input','textarea','select',
  45. 'pre','code',
  46. 'h1','h2','h3','h4',
  47. 'b','strong',
  48. 'svg','embed',
  49. 'img','audio','video',
  50. 'canvas',
  51. ];
  52.  
  53. const excludeClasses = [
  54. 'highlight',
  55. 'katex-display'
  56. ]
  57. const excludeClassesRegexi = new RegExp(excludeClasses.join('|'),'i');
  58.  
  59. const gather = el=>{
  60. let textEls = [];
  61. el.childNodes.forEach(el=>{
  62. if(el.isEnB) return;
  63. if(el.originEl) return;
  64.  
  65. if(el.nodeType === 3){
  66. textEls.push(el);
  67. }else if(el.childNodes){
  68. if(el.tagName && excludeTagNames.includes(el.tagName.toLowerCase())) return;
  69. if(el.getAttribute && el.getAttribute('class') && excludeClassesRegexi.test(el.getAttribute('class'))) return;
  70. textEls = textEls.concat(gather(el))
  71. }
  72. })
  73. return textEls;
  74. };
  75.  
  76. const engRegex = /[a-zA-Z][a-z]+/;
  77. const engRegexg = /[a-zA-Z][a-z]+/g;
  78. let replaceTextByEl = el=>{
  79. const text = el.data;
  80. if(!engRegex.test(text))return;
  81.  
  82. if(!el.replaceEl){
  83. const spanEl = document.createElement('bionic');
  84. spanEl.isEnB = true;
  85. spanEl.innerHTML = enCodeHTML(text).replace(engRegexg,word=>{
  86. let halfLength;
  87. if(/ing$/.test(word)){
  88. halfLength = word.length - 3;
  89. }else if(word.length<5){
  90. halfLength = Math.floor(word.length/2);
  91. }else{
  92. halfLength = Math.ceil(word.length/2);
  93. }
  94. return '<bbb>'+word.substr(0,halfLength)+'</bbb>'+word.substr(halfLength)
  95. })
  96. spanEl.originEl = el;
  97. el.replaceEl = spanEl;
  98. }
  99.  
  100. el.after(el.replaceEl);
  101. el.remove();
  102. };
  103.  
  104. // replaceTextByEl = el=>{
  105. // el.data = el.data.replace(engRegexg,word=>{
  106. // let halfLength;
  107. // if(/ing$/.test(word)){
  108. // halfLength = word.length - 3;
  109. // }else if(word.length<5){
  110. // halfLength = Math.floor(word.length/2);
  111. // }else{
  112. // halfLength = Math.ceil(word.length/2);
  113. // }
  114. // const a = word.substr(0,halfLength).
  115. // replace(/[a-z]/g,w=>'\uD835' + String.fromCharCode(w.charCodeAt(0)+56717)).
  116. // replace(/[A-Z]/g,w=>'\uD835' + String.fromCharCode(w.charCodeAt(0)+56723));
  117. // const b = word.substr(halfLength).
  118. // replace(/[a-z]/g,w=> String.fromCharCode(55349,w.charCodeAt(0)+56665)).
  119. // replace(/[A-Z]/g,w=> String.fromCharCode(55349,w.charCodeAt(0)+56671));
  120. // return a + b;
  121. // })
  122. // }
  123.  
  124. const bionic = _=>{
  125. const textEls = gather(body);
  126.  
  127. isBionic = true;
  128.  
  129. textEls.forEach(replaceTextByEl);
  130. document.head.appendChild(styleEl);
  131. }
  132.  
  133. const lazy = (func,ms = 15)=> {
  134. return _=>{
  135. clearTimeout(func.T)
  136. func.T = setTimeout(func,ms)
  137. }
  138. };
  139.  
  140. const listenerFunc = lazy(_=>{
  141. if(!isBionic) return;
  142.  
  143. bionic();
  144. });
  145.  
  146. if(window.MutationObserver){
  147. (new MutationObserver(listenerFunc)).observe(body,{
  148. childList: true,
  149. subtree: true,
  150. attributes: true,
  151. });
  152. }else{
  153. const {open,send} = XMLHttpRequest.prototype;
  154. XMLHttpRequest.prototype.open = function(){
  155. this.addEventListener('load',listenerFunc);
  156. return open.apply(this,arguments);
  157. };
  158. document.addEventListener('DOMContentLoaded',listenerFunc);
  159. document.addEventListener('DOMNodeInserted',listenerFunc);
  160. }
  161.  
  162.  
  163. window.addEventListener('load',bionic);
  164. // document.addEventListener('click',listenerFunc);
  165.  
  166.  
  167. const revoke = _=>{
  168. const els = [...document.querySelectorAll('bionic')];
  169.  
  170. els.forEach(el=>{
  171. const {originEl} = el;
  172. if(!originEl) return;
  173.  
  174. el.after(originEl);
  175. el.remove();
  176. })
  177.  
  178. isBionic = false;
  179. };
  180. // document.addEventListener('mousedown',revoke);
  181.  
  182. const redo = _=>{
  183. const textEls = gather(body);
  184.  
  185. textEls.forEach(el=>{
  186. const { replaceEl } = el;
  187.  
  188. if(!replaceEl) return;
  189.  
  190. el.after(replaceEl);
  191. el.remove();
  192. })
  193.  
  194. isBionic = false;
  195. };
  196.  
  197. document.addEventListener('keydown',e=>{
  198. const { ctrlKey , metaKey, key } = e;
  199.  
  200. if( ctrlKey || metaKey ){
  201. if(key === 'b'){
  202. if(isBionic){
  203. revoke();
  204. }else{
  205. bionic();
  206. }
  207. }
  208. }
  209. })
  210.  
  211. // document.addEventListener('mouseup',redo);

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址