“搞定”CJK!

中文字体和标点设定及修正脚本

目前为 2016-07-03 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name FixCJK!
  3. // @name:zh-CN “搞定”CJK!
  4. // @namespace https://github.com/stecue/fixcjk
  5. // @version 1.1.0
  6. // @description 1) Use real bold to replace synthetic SimSun bold; 2) Regular SimSun/中易宋体 can also be substituted; 3) Reassign font fallback list (Latin AND CJK). Browser serif/sans settings are overridden; 4) Use Latin fonts for Latin part in Latin/CJK mixed texts; 5) Fix fonts and letter-spacing for CJK punctuation marks.
  7. // @description:zh-cn 中文字体和标点设定及修正脚本
  8. // @author stecue@gmail.com
  9. // @license GPLv3
  10. // @match http://*/*
  11. // @match https://*/*
  12. // @match file:///*
  13. // @exclude https://*jsfiddle.net*/*
  14. // @grant GM_addStyle
  15. // ==/UserScript==
  16. (function () {
  17. 'use strict';
  18. // You can change the the following fonts/settings until the "var FixPunct=" line.
  19. var CJKdefault = '"Microsoft YaHei",SimSun,"WenQuanYi Zen Hei Sharp","WenQuanYi Micro Hei"'; //The default CJK font if no sans or serif is specified. Regular weight.
  20. var CJKSimSun= '"Microsoft YaHei","WenQuanYi Micro Hei"'; //Fonts to replace SimSun;
  21. var CJKserif = '"Microsoft YaHei","WenQuanYi Micro Hei"'; //Default serif fonts for CJK. Although It is intended for regular weight but some element with bold weight still use the font here. Therefore "SimSun" itself is not a good choice because it does not have a real bold font.
  22. var CJKsans = '"Microsoft YaHei","Noto Sans CJK SC","Noto Sans CJK SC Regular"'; //Sans-serif fonts for CJK. Regular weight.
  23. var CJKBold = '"Microsoft YaHei","WenQuanYi Micro Hei"'; //The "good CJK font" to replace SimSun bold. Note that some elements still use font in CJKserif defined above such as the menus on JD.com.
  24. var CJKPunct = 'Noto Sans CJK SC,Noto Sans CJK SC Regular,SimHei,SimSun'; //The font to use for CJK quotation marks.
  25. var LatinInSimSun = 'Ubuntu Mono'; //The Latin font in a paragraph whose font was specified to "SimSun" only.
  26. var LatinSans = 'Lato,"Open Sans",Arial'; //Sans-serif fonts for Latin script. It will be overridden by a non-virtual font in the CSS font list if present.
  27. var LatinSerif = 'Constantia,"Liberation Serif","Times New Roman"'; //Serif fonts for Latin script. It will be overridden by a non-virtual font in the CSS font list if present.
  28. var LatinMono = 'Consolas,"DejaVu Sans Mono"'; //Monospace fonts for Latin script. It will be overridden by a non-virtual font in the CSS font list if present.
  29. var FixRegular = true; //Also fix regular fonts. You need to keep this true if you want to use "LatinInSimSun" in Latin/CJK mixed context.
  30. var FixMore = true; //Appendent CJK fonts to all elements. No side effects found so far.
  31. var FixPunct = true; //If Latin punctions in CJK paragraph need to be fixed. Usually one needs full-width punctions in CJK context. Turn it off if the script runs too slow or HTML strings are adding to your editing area.
  32. var useJustify = true; //Make justify as the default alignment.
  33. ///=== "Safe" Zone Ends Here.Do not change following code unless you know the results! ===///
  34. var timeOut=3000; //allow maximum 3.0 seconds to run this script.
  35. var maxlength = 1100200; //maximum length of the page HTML to check for CJK punctuations.
  36. var maxNumElements = 81024; // maximum number of elements to process.
  37. var CJKOnlyThreshold = 11024; // Only CJK if the number of elements reaches this threshold.
  38. var noBonusLength = 11024; //no bonus functions such as fixing "reversed" pairs.
  39. var noBonusTimeout = 20; //Longest time (in ms) to run bonus functions for each element.
  40. var sqz_timeout=50; // 50ms per element seems long enough.
  41. var invForLimit=6; //the time limit factor (actual limit is timeOut/invForLimit) for the "for loop" in Round 2 & 3.
  42. var processedAll=true;
  43. var ifRound1=true;
  44. var ifRound2=true;
  45. var ifRound3=true;
  46. var debug_verbose = false; //show/hide more information on console.
  47. var debug_00 = false; //debug codes before Rounds 1/2/3/4.
  48. var debug_01 = false; //Turn on colors for Round 1.
  49. var debug_02 = false;
  50. var debug_03 = false;
  51. var debug_04 = false;
  52. var debug_labelCJK = false;
  53. var debug_re_to_check = false; //"true" might slow down a lot!
  54. var debug_spaces =false;
  55. var debug_wrap = false;
  56. var debug_tagSeeThrough = false;
  57. var debug_getBeforeTags = false;
  58. var debug_noWrapping = false;
  59. var debug_asyncTimers = true;
  60. var useWrap=true;
  61. var useRemoveSpacesForSimSun=false;
  62. var useFeedback=false;
  63. var useCSSforSimSun=false;
  64. var useDelayedFix=false;
  65. var useTimeout=false;
  66. var useSFTags=false; //FIXME: use tags may cause problems on jd.com.
  67. var re_to_check = /^\uEEEE/; //use ^\uEEEE for placeholder. Avoid using the "m" or "g" modifier for long document, but the difference seems small?
  68. ///=== The following variables should be strictly for internal use only.====///
  69. var refixing=false;
  70. var refixingFonts=false;
  71. var rspLength=3; //If the font-list reaches the length here, the author is probably responsible enough to cover most Latin/English environment.
  72. var waitForDoubleClick=200;
  73. var SkippedTagsForFonts=/^(HTML|TITLE|HEAD|LINK|BODY|SCRIPT|noscript|META|STYLE|AUDIO|video|source|AREA|BASE|canvas|figure|map|object|textarea)$/i;
  74. var SkippedTagsForMarks=/^(HTML|TITLE|HEAD|LINK|BODY|SCRIPT|noscript|META|STYLE|AUDIO|video|source|AREA|BASE|canvas|figure|map|object|textarea|input|code|pre|tt|BUTTON|select|option|label|fieldset|datalist|keygen|output)$/i;
  75. var SkippedTags=SkippedTagsForFonts;
  76. var pureLatinTags=/^(TITLE|HEAD|LINK|SCRIPT|META|STYLE|AUDIO|video|source|AREA|BASE|canvas|figure|map|object|textarea|svg)$/i; //No CJK labeling for the elements and their desedents.
  77. var stopTags=/^(SUB|SUP|BR|VR)$/i; //The "see-through" stops at these tags.
  78. var stopClasses='mw-editsection';
  79. var upEnoughTags=/^(address|article|aside|blockquote|canvas|dd|div|dl|dt|fieldset|figcaption|figure|footer|form|H[1-6]|header|hgroup|hr|li|main|nav|noscript|ol|output|p|pre|section|table|td|th|tr|tfoot|ul|video|BODY)$/ig; //"See-Through" stops here, the "block-lelvel" elements.
  80. var ignoredTags=/^(math)$/i;
  81. var preOrigPunctSpaceList='pl-c,toggle-comment,answer-date-link'; //Also known as "no wrapping list". Only wrapped CJK will be treated.
  82. var preSimSunList='c30,c31,c32,c33,c34,c35,c36,c37,c38,c39,c40,c41,c42,c43,c44,c45,c46';
  83. var preSimSunTags=/^(pre|code|tt)$/i;
  84. var CJKclassList='CJK2Fix,MarksFixedE135,FontsFixedE137,\uE211,\uE985,Safe2FixCJK\uE000,PunctSpace2Fix,CJKTestedAndLabeled,SimSun2Fix,SimSunFixedE137,LargeSimSun2Fix,\uE699,checkSpacedQM,wrappedCJK2Fix,preCode,preMath,SpacesFixedE133';
  85. var re_autospace_url=/zhihu\.com|guokr\.com|changhai\.org|wikipedia\.org|greasyfork\.org|github\.com/;
  86. var preCodeTags='code,pre,tt';
  87. var preMathTags='math'; //Do not change puncts as well as fonts. Just like "math".
  88. var t_start = performance.now();
  89. var t_stop = t_start;
  90. var re_simsun = / *simsun *| *宋体 *| *ËÎÌå *| *\5b8b\4f53 */i;
  91. var all = document.getElementsByTagName('*');
  92. var NumAllDOMs=all.length;
  93. var bodyhtml=document.getElementsByTagName("HTML");
  94. if (bodyhtml[0].innerHTML.length > maxlength) {
  95. console.log('FixCJK!: HTML too long, skip everything. Exiting now...');
  96. ifRound1=false;
  97. ifRound2=false;
  98. ifRound3=false;
  99. FixPunct=false;
  100. }
  101. else if (!(bodyhtml[0].innerHTML.match(/[\u3000-\u303F\u3400-\u9FBF\uFF00-\uFFEF]/))) {
  102. if (debug_verbose===true) {console.log('FixCJK!: Checking for CJK took '+((performance.now()-t_stop)/1000.0).toFixed(3)+' seconds. No CJK found.');}
  103. if (debug_verbose===true) {console.log('FixCJK!: No need to check CJK punctuations.');}
  104. FixPunct=false;
  105. }
  106. else {
  107. if (debug_verbose===true) {console.log('FixCJK!: Checking for CJK took '+((performance.now()-t_stop)/1000.0).toFixed(3)+' seconds. CJK found.');}
  108. FixPunct=true;
  109. }
  110. var sig_sim = 'RealCJKBold\u0020易'; //Just for SimSun;
  111. var sig_song = 'RealCJKBold\u0020宋'; // signature to check if change is sucssful or not.
  112. var sig_hei = 'RealCJKBold\u0020黑'; // signature to check if change is sucssful or not.
  113. var sig_bold = 'RealCJKBold\u0020粗'; // signature to check if change is sucssful or not.
  114. var sig_default = 'RealCJKBold\u0020默'; // signature to check if change is sucssful or not.
  115. var sig_mono= 'RealCJKBold\u0020均';
  116. var sig_punct = '\uE135'; //will be attached to CJKPunct; This is used in punct fixing not font fixing(?)
  117. var qsig_sim = '"' + sig_sim + '"'; //Quoted sinagure; Actually no need to quote.
  118. var qsig_song= '"'+sig_song+'"';
  119. var qsig_hei = '"' + sig_hei + '"'; //Quoted sinagure;
  120. var qsig_bold = '"' + sig_bold + '"';
  121. var qsig_default = '"' + sig_default + '"';
  122. var genPunct='General Punct \uE137'; //Different from sig_punct
  123. var qpreCJK = CJKdefault;
  124. var qCJK = LatinInSimSun + ',' + CJKdefault + ',' + qsig_default;
  125. var qSimSun = qsig_sim+','+LatinInSimSun + ',' + CJKSimSun;
  126. var qLargeSimSun = qsig_sim+','+ LatinSerif + ',' + 'SimSun';
  127. var qBold = LatinInSimSun + ',' + CJKBold + ',' + qsig_bold;
  128. var qsans = LatinSans + ',' + CJKsans + ',' + qsig_hei + ',' + 'sans-serif'; //To replace "sans-serif"
  129. var qserif = LatinSerif + ',' + CJKserif +','+qsig_song+ ',' + 'serif'; //To replace "serif"
  130. var qmono = sig_mono+','+LatinMono + ',' + CJKdefault + ',' + qsig_default + ',' + 'monospace'; //To replace "monospace".
  131. var i = 0;
  132. var max = all.length;
  133. var child = all[i].firstChild;
  134. var if_replace = false;
  135. var font_str = window.getComputedStyle(all[i], null).getPropertyValue('font-family');
  136. var fweight = window.getComputedStyle(all[i], null).getPropertyValue('font-weight');
  137. var re_sans0 = /^ ?sans ?$|^ ?sans-serif ?$/i;
  138. var re_serif = /^ ?serif ?$/i;
  139. var re_mono0 = /^ ?mono ?$|^ ?monospace ?$/i;
  140. //letter-spacing options
  141. var kern_consec_ll='-0.35em'; //。” or ))
  142. var kern_consec_rr='-0.35em'; //((
  143. var kern_consec_lr='-0.8em'; //)(
  144. var kern_ind_open='-0.22em';
  145. var kern_ind_close='-0.22em';
  146. //Check if the font definitions are valid
  147. if (check_fonts(CJKdefault, 'CJKdefault') === false)
  148. return false;
  149. else if (check_fonts(CJKserif, 'CJKserif') === false)
  150. return false;
  151. else if (check_fonts(CJKsans, 'CJKsans') === false)
  152. return false;
  153. else if (check_fonts(CJKBold, 'CJKBold') === false)
  154. return false;
  155. else if (check_fonts(LatinInSimSun, 'LatinInSimSun') === false)
  156. return false;
  157. else if (check_fonts(LatinSans, 'LatinSans') === false)
  158. return false;
  159. else if (check_fonts(LatinSerif, 'LatinSerif') === false)
  160. return false;
  161. else if (check_fonts(LatinMono, 'LatinMono') === false)
  162. return false;
  163. else {
  164. }
  165. if (debug_00===true) {console.log(dequote('"SimSun","Times New Roman"""""'));}
  166. //Assign fonts for puncts:
  167. var punctStyle='@font-face { font-family: '+genPunct+';\n src: '+AddLocal(CJKPunct)+';\n unicode-range: U+3000-303F,U+FF00-FFEF;}';
  168. punctStyle=punctStyle+'\n@font-face {font-family:RealCJKBold\u0020易;\n src:local(SimHei);\n unicode-range: U+A0-B6,U+B8-2FF,U+2000-2017,U+201E-2FFF;}';
  169. punctStyle=punctStyle+'\n@font-face {font-family:SimVecA;\n src:local(Ubuntu Mono);\n unicode-range: U+0-7F;}';
  170. punctStyle=punctStyle+'\n@font-face {font-family:SimVecS;\n src:local(SimHei);\n unicode-range: U+A0-33FF;}';
  171. punctStyle=punctStyle+'\n@font-face {font-family:SimVecC;\n src:local(Microsoft YaHei);\n unicode-range: U+3400-9FBF;}';
  172. if (useCSSforSimSun===true) {
  173. punctStyle=punctStyle+'\n @font-face { font-family: SimSun;\n src: local('+FirstFontOnly('SimSun')+');\n unicode-range: U+3400-9FBF;}';
  174. punctStyle=punctStyle+'\n @font-face { font-family: 宋体;\n src: local('+FirstFontOnly('SimSun')+');\n unicode-range: U+3400-9FBF;}';
  175. punctStyle=punctStyle+'\n @font-face { font-family: ËÎÌå;\n src: local('+FirstFontOnly('SimSun')+');\n unicode-range: U+3400-9FBF;}';
  176. punctStyle=punctStyle+'\n @font-face { font-family: 宋体;\n src: local('+FirstFontOnly(LatinInSimSun)+');\n unicode-range: U+0000-2C7F;}';
  177. }
  178. if (debug_00===true) console.log(punctStyle);
  179. GM_addStyle(punctStyle);
  180. ///----------------------------
  181. qpreCJK = dequote(qpreCJK);
  182. qCJK = dequote(qCJK);//LatinInSimSun + ',' + CJKdefault + ',' + qsig_default;
  183. qSimSun = dequote(qSimSun);//LatinInSimSun + ',' + CJKserif + ',' + qsig_sun;
  184. qLargeSimSun = dequote(qLargeSimSun);//LatinInSimSun + ',' + CJKserif + ',' + qsig_sun;
  185. qBold = dequote(qBold);//LatinInSimSun + ',' + CJKBold + ',' + qsig_bold;
  186. qsans = dequote(qsans);//LatinSans + ',' + CJKsans + ',' + qsig_hei + ',' + 'sans-serif'; //To replace "sans-serif"
  187. qserif = dequote(qserif);//LatinSerif + ',' + CJKserif + ',' + qsig_sun + ',' + 'serif'; //To replace "serif"
  188. qmono = dequote(qmono);//LatinMono + ',' + CJKdefault + ',' + qsig_default + ',' + 'monospace'; //To replace "monospace".
  189. CJKPunct=dequote(CJKPunct)+','+sig_punct;
  190. if (debug_00===true) {console.log('Entering Loops...');}
  191. /// ===== Labeling CJK elements === ///
  192. t_stop=performance.now();
  193. var debug_addTested=false;
  194. function addTested (node,currLevel) {
  195. if (currLevel > 5) {
  196. if (debug_addTested===true) console.log("TOO MANY LEVELS, exiting addTested()...");
  197. return false;
  198. }
  199. var child=node.firstChild;
  200. while (child) {
  201. if (child.nodeType===1) {
  202. addTested(child,currLevel+1);
  203. }
  204. child=child.nextSibling;
  205. }
  206. if (node.classList.contains("CJKTestedAndLabeled")) {
  207. if (debug_addTested===true) console.log("Labeled: "+node.nodeName);
  208. return true;
  209. }
  210. else {
  211. node.classList.add("CJKTestedAndLabeled");
  212. if (debug_addTested===true) console.log("Labeled: "+node.nodeName);
  213. return true;
  214. }
  215. }
  216. function labelCJKByNode(node,levelIndex) {
  217. var t_stop=performance.now();
  218. if (node instanceof SVGElement) {
  219. return false;
  220. }
  221. //One do need to recheck the textContent everytime "ReFix" is triggered.
  222. if ( (levelIndex < 2) && (!node.textContent.match(/[“”‘’\u3000-\u303F\u3400-\u9FBF\uFF00-\uFFEF]/)) ) {
  223. if (!node.classList.contains("CJKTestedAndLabeled")) {
  224. window.setTimeout(addTested,5,node,0);
  225. }
  226. return true;
  227. }
  228. var font_str=dequote(window.getComputedStyle(node, null).getPropertyValue('font-family'));
  229. var child=node.firstChild;
  230. while (child) {
  231. if (child.nodeType===3) {
  232. if (node.classList.contains("CJKTestedAndLabeled") || node.nodeName.match(SkippedTags)) {
  233. //Do nothing if already labeled.
  234. }
  235. else if (font_str.match(re_simsun)) {
  236. if (inTheClassOf(node,preSimSunList) || node.nodeName.match(preSimSunTags)) {
  237. node.style.fontFamily=font_str.replace(re_simsun,'SimVecA,SimVecS,SimVecC');
  238. node.classList.add("CJK2Fix");
  239. node.classList.add("CJKTestedAndLabeled");
  240. }
  241. else {
  242. var font_size=(window.getComputedStyle(node, null).getPropertyValue('font-size')).slice(0,-2);
  243. if (font_size < 18) {
  244. node.classList.add("CJK2Fix");
  245. node.classList.add("SimSun2Fix");
  246. if (!inTheClassOf(node,preOrigPunctSpaceList)) {
  247. node.classList.add("PunctSpace2Fix");
  248. }
  249. }
  250. else {
  251. node.style.fontFamily=font_str; //Is this to improve the speed?
  252. node.classList.add("CJK2Fix");
  253. node.classList.add("LargeSimSun2Fix");
  254. if (!inTheClassOf(node,preOrigPunctSpaceList)) {
  255. node.classList.add("PunctSpace2Fix");
  256. }
  257. }
  258. }
  259. }
  260. else if (child.data.match(/[“”‘’\u3000-\u303F\u3400-\u9FBF\uFF00-\uFFEF]/)) {
  261. node.classList.add("CJK2Fix");
  262. if (!inTheClassOf(node,preOrigPunctSpaceList)) {
  263. node.classList.add("PunctSpace2Fix");
  264. }
  265. }
  266. }
  267. else if (child.nodeType===1) {
  268. labelCJKByNode(child,levelIndex+1);
  269. }
  270. child=child.nextSibling;
  271. }
  272. node.classList.add("CJKTestedAndLabeled");
  273. return true;
  274. }
  275. function labelCJK(useCJKTimeOut) {
  276. var useBFS=false;
  277. var child=document.body.firstChild;
  278. var all='';
  279. if (useBFS===true) {
  280. while (child) {
  281. if (child.nodeType===1) {
  282. //The levelIndex of document.body is 0.
  283. labelCJKByNode(child,1);
  284. }
  285. child=child.nextSibling;
  286. }
  287. return true;
  288. }
  289. all=document.querySelectorAll(":not(.CJKTestedAndLabeled)");
  290. if (useCJKTimeOut===false) {
  291. console.log(all.length+" elements to check and label. From");
  292. console.log(all[0]);
  293. console.log('To');
  294. console.log(all[all.length-1]);
  295. }
  296. var t_stop=performance.now();
  297. var t_last=0;
  298. var t_init=t_stop;
  299. var t_overall=0;
  300. for (var i=0;i < all.length;i++) {
  301. if (useCJKTimeOut===true) { //useCJKTimeOut===false is the "Engineering mode".
  302. t_last=performance.now()-t_stop;
  303. t_stop=performance.now();
  304. t_overall=performance.now()-t_init;
  305. }
  306. if (i>0 && t_last>20) {
  307. if ( debug_labelCJK===true) {
  308. console.log("FIXME: Curr: ");
  309. console.log(all[i]);
  310. console.log("FIXME: Prev: ");
  311. console.log(all[i-1]);
  312. console.log("Labeling Last elemnent: <"+all[i-1].nodeName+">.("+all[i-1].className+") took "+t_last.toFixed(1)+" ms.");
  313. }
  314. if (t_last>100) {
  315. console.log("FIXME: Labeling last element took too much time. Too slow to labelCJK after "+t_overall.toFixed(1)+" ms.");
  316. console.log("FIXME: Only "+document.getElementsByClassName("CJKTestedAndLabeled").length+" tested in Total.");
  317. if (debug_labelCJK===true) {console.log(all[i-1]);}
  318. break;
  319. }
  320. }
  321. if (i%1===0 && t_overall>200) {
  322. console.log("FIXME: Too slow to labelCJK after "+t_overall.toFixed(1)+" ms.");
  323. console.log("FIXME: Only "+document.getElementsByClassName("CJKTestedAndLabeled").length+" tested in Total.");
  324. if (debug_labelCJK===true) {console.log(all[i-1]);}
  325. break;
  326. }
  327. if ((all[i].nodeName.match(SkippedTags)) || all[i] instanceof SVGElement || all[i].classList.contains("CJKTestedAndLabeled")){
  328. if (debug_labelCJK===true && t_last>10 ) console.log("SKIPPED: "+all[i].nodeName);
  329. window.setTimeout(function (node) {node.classList.add("CJKTestedAndLabeled");},1,all[i]); //This is the most time consuming part. Trying to use async i/o.
  330. if (all[i].nodeName.match(pureLatinTags)) {
  331. if (useCJKTimeOut===true) {
  332. window.setTimeout(addTested,5,all[i],0);
  333. }
  334. else {
  335. window.setTimeout(addTested,5,all[i],-1000); //Means no limits in actual webpages.
  336. }
  337. }
  338. continue;
  339. }
  340. font_str=dequote(window.getComputedStyle(all[i], null).getPropertyValue('font-family'));
  341. if (inTheClassOf(all[i],preSimSunList) || all[i].nodeName.match(preSimSunTags)) {
  342. all[i].style.fontFamily=font_str.replace(re_simsun,'SimVecA,SimVecS,SimVecC');
  343. all[i].classList.add("CJK2Fix");
  344. all[i].classList.add("CJKTestedAndLabeled");
  345. continue;
  346. }
  347. if (debug_01===true) console.log(font_str);
  348. if (font_str.match(re_simsun)) {
  349. var font_size=(window.getComputedStyle(all[i], null).getPropertyValue('font-size')).slice(0,-2);
  350. if (font_size < 18) {
  351. all[i].classList.add("CJK2Fix");
  352. all[i].classList.add("SimSun2Fix");
  353. if (!inTheClassOf(all[i],preOrigPunctSpaceList) && all[i].contentEditable==="true") {
  354. all[i].classList.add("PunctSpace2Fix");
  355. }
  356. }
  357. else {
  358. all[i].style.fontFamily=font_str;
  359. all[i].classList.add("CJK2Fix");
  360. all[i].classList.add("LargeSimSun2Fix");
  361. if (!inTheClassOf(all[i],preOrigPunctSpaceList) && all[i].contentEditable==="true") {
  362. all[i].classList.add("PunctSpace2Fix");
  363. }
  364. }
  365. all[i].classList.add("CJKTestedAndLabeled");
  366. continue;
  367. }
  368. if ( !(all[i].textContent.match(/[“”‘’\u3000-\u303F\u3400-\u9FBF\uFF00-\uFFEF]/)) ){
  369. if ( useCJKTimeOut===true && all[i].textContent.length > 20 && (font_str.split(',').length >= rspLength) ) { //20 is just to make sure they are actuall Latin elements,not just some place holder.
  370. window.setTimeout(function (node) {node.classList.add("CJKTestedAndLabeled");},1,all[i]); //This is the most time consuming part. Trying to use async i/o.
  371. window.setTimeout(addTested,5,all[i],0);//Still, it might cause some childs to be "unfixable", if the length of the place holder is longer than 100...
  372. continue;
  373. }
  374. else if (useCJKTimeOut===false && (font_str.split(',').length >= rspLength) ) {
  375. window.setTimeout(function (node) {node.classList.add("CJKTestedAndLabeled");},1,all[i]); //This is the most time consuming part. Trying to use async i/o.
  376. if (debug_labelCJK===true) {console.log("Labeling non-CJK element: ");console.log(all[i]);}
  377. window.setTimeout(addTested,5,all[i],-1000);//Still, it might cause some childs to be "unfixable", if the length of the place holder is longer than 100...
  378. continue;
  379. }
  380. else {
  381. //Just skip here. Might be important in the future.
  382. continue;
  383. }
  384. }
  385. child = all[i].firstChild;
  386. while (child) {
  387. var realSibling=child.nextSibling;
  388. if (child.nodeType == 3 && (child.data.match(/[“”‘’\u3000-\u303F\u3400-\u9FBF\uFF00-\uFFEF]/))) {
  389. all[i].classList.add("CJK2Fix");
  390. if (!inTheClassOf(all[i],preOrigPunctSpaceList) && all[i].contentEditable==="true") {
  391. all[i].classList.add("PunctSpace2Fix");
  392. }
  393. if (!(all[i].parentNode.nodeName.match(SkippedTags))) {
  394. all[i].parentNode.classList.add("CJK2Fix");
  395. if (!inTheClassOf(all[i].parentNode,preOrigPunctSpaceList) && !inTheClassOf(all[i],preOrigPunctSpaceList) && all[i].parentNode.contentEditable==="true") {
  396. all[i].parentNode.classList.add("PunctSpace2Fix");
  397. }
  398. }
  399. break;
  400. }
  401. child=realSibling;
  402. }
  403. all[i].classList.add("CJKTestedAndLabeled");
  404. }
  405. }
  406. //return true;
  407. //Do not try to fixpuncts if it is an English site. Just trying to save time.
  408. labelPreMath();
  409. labelCJK(true);
  410. if ((document.getElementsByClassName('CJK2Fix')).length < 1) {
  411. FixPunct=false;
  412. }
  413. if (debug_verbose===true) {console.log('FixCJK!: Labling took '+((performance.now()-t_stop)/1000).toFixed(3)+' seconds.');}
  414. ///===FixFonts, Rounds 1-3===///
  415. FixAllFonts();
  416. ///===Round 4, FixPunct===///
  417. if (debug_verbose===true) {console.log('FixCJK!: Labling and Fixing fonts took '+((t_stop-t_start)/1000).toFixed(3)+' seconds.');}
  418. if ((t_stop-t_start)*2 > timeOut || max > maxNumElements ) {
  419. console.log('FixCJK!: Too slow or too many elements.');
  420. FixPunct=false;
  421. }
  422. if (FixPunct===false) {
  423. if (debug_verbose===true) {console.log('FixCJK!: Skipping fixing punctuations...');}
  424. }
  425. var returnNow=true;
  426. var returnLater=false; //Do the actual fixing.
  427. var MaxNumLoops=1;
  428. if (document.URL.match(/zhihuxcom|sinaxcom/)) {
  429. seTimeout=true;
  430. }
  431. if (useDelayedFix===true) {
  432. var DelayedTimer=200;
  433. window.setTimeout(FunFixPunct(true,MaxNumLoops,returnLater),DelayedTimer);
  434. }
  435. else {
  436. window.setTimeout(function () {
  437. labelPreCode();
  438. labelNoWrappingList();
  439. if (useWrap===true) wrapCJK();
  440. FunFixPunct(true,MaxNumLoops,returnLater);
  441. },10);
  442. }
  443. ///===End of Solving the picture problem===///
  444. if (debug_verbose===true) {console.log('FixCJK!: Fixing punctuations took '+((performance.now()-t_stop)/1000).toFixed(3)+' seconds.');}
  445. ///===Add onClick listener before exiting===///
  446. var NumClicks=0;
  447. var t_last=performance.now();
  448. var t_interval=1000; //The interval between two checks.
  449. var NumAllCJKs=(document.getElementsByClassName('CJK2Fix')).length;
  450. var NumPureEng=0;
  451. var LastURL=document.URL;
  452. var LastMod=document.lastModified;
  453. var ItvScl=2.0; //Real "cooling down time" is t_interval/ItvScl
  454. if (NumAllCJKs*1.0/NumAllDOMs*100 < 1.0) {
  455. NumPureEng++;
  456. }
  457. //document.onClick will cause problems on some webpages on Firefox.
  458. var downtime=performance.now();
  459. var downX=0;
  460. var downY=0;
  461. document.body.addEventListener("mousedown",function (e){downtime=performance.now();downX=e.clientX;downY=e.clientY;},false);
  462. document.body.addEventListener("mouseup",function (e){
  463. if (e.button>0 ) {
  464. //do nothing if right button clicked.
  465. return true;
  466. }
  467. else if (((performance.now()-downtime) < 300) && (Math.abs(e.clientX-downX)+Math.abs(e.clientY-downY)) ===0 ) {
  468. //ReFix after other things are done.
  469. setTimeout(ReFixCJK,5,e);
  470. }
  471. else if (((performance.now()-downtime) > 1500) && (Math.abs(e.clientX-downX)+Math.abs(e.clientY-downY)) ===0 ) {
  472. //Force to labelCJK;
  473. var t_CJK=performance.now();
  474. labelPreMath();
  475. labelCJK(false);
  476. FixAllFonts(false);
  477. labelPreCode();
  478. labelNoWrappingList();
  479. if (useWrap===true) wrapCJK();
  480. FunFixPunct(false,5,false);
  481. addSpaces(false);
  482. t_CJK=performance.now()-t_CJK;
  483. console.log("Labeling and fixing all CJK elements took "+(t_CJK/1000).toFixed(1)+" seconds.");
  484. }
  485. },false);
  486. var fireReFix=false;
  487. window.addEventListener("scroll",function (e){
  488. fireReFix=false; //Prevent from firing ReFixCJK() while scrolling.
  489. setTimeout(function() {fireReFix=true;},t_interval/ItvScl/2); //Permit ReFixCJK after sometime of last scrolling.
  490. setTimeout(function() {
  491. if (fireReFix===true) {
  492. ReFixCJKFontsOnly();
  493. }
  494. },t_interval);
  495. },false);
  496. document.body.addEventListener("dblclick",function() {
  497. addSpaces(true);
  498. //Prevent ReFixing for a certain time;
  499. },false);
  500. ///===Time to exit the main function===///
  501. var t_fullstop=performance.now();
  502. if (processedAll===true) {
  503. console.log('FixCJK!: NORMAL TERMINATION: '+((t_fullstop-t_start)/1000).toFixed(3)+' seconds (Fixing PMs not included) is the overall execution time. No skipped step(s).');
  504. }
  505. else {
  506. console.log('FixCJK!: EXECUTION ABORTED: '+((t_fullstop-t_start)/1000).toFixed(3)+' seconds (Fixing PMs not included) is the overall execution time. Some step(s) were skipped due to performance issues.');
  507. }
  508. ////////////////////======== Main Function Ends Here ==============/////////////////////////////
  509. //===The actual listening functions===//
  510. function labelPreMath() {
  511. var bannedTagList=preMathTags.split(',');
  512. for (var itag=0;itag<bannedTagList.length;itag++) {
  513. var all2Ban=document.querySelectorAll(bannedTagList[itag]+":not(.preMath)");
  514. for (var iele=0;iele<all2Ban.length;iele++) {
  515. banMathHelper(all2Ban[iele]);
  516. }
  517. }
  518. }
  519. function labelPreCode() {
  520. var bannedTagList=preCodeTags.split(',');
  521. for (var itag=0;itag<bannedTagList.length;itag++) {
  522. var all2Ban=document.getElementsByTagName(bannedTagList[itag]);
  523. for (var iele=0;iele<all2Ban.length;iele++) {
  524. banHelper(all2Ban[iele]);
  525. }
  526. }
  527. }
  528. function labelNoWrappingList() {
  529. var ie=0;
  530. var bannedClassList=preOrigPunctSpaceList.split(',');
  531. for (var i=0;i<bannedClassList.length;i++) {
  532. var all2Ban=document.getElementsByClassName(bannedClassList[i]);
  533. for (ie=0;ie<all2Ban.length;ie++)
  534. banHelper(all2Ban[ie]);
  535. }
  536. var bannedElementList=document.querySelectorAll('[contenteditable="true"]');
  537. for (ie=0;ie<bannedElementList.length;ie++) {
  538. if (debug_noWrapping===true) console.log(bannedElementList[ie]);
  539. banHelper(bannedElementList[ie]);
  540. }
  541. }
  542. function banHelper(node) {
  543. var child=node.firstChild;
  544. while (child) {
  545. if ( child.nodeType===1 && !(child instanceof SVGElement) ) {
  546. banHelper(child);
  547. }
  548. child=child.nextSibling;
  549. }
  550. if (!node.classList.contains("preCode")) {
  551. node.classList.add("preCode");
  552. }
  553. }
  554. function banMathHelper(node) {
  555. var child=node.firstChild;
  556. while (child) {
  557. if ( child.nodeType===1 && !(child instanceof SVGElement) ) {
  558. banMathHelper(child);
  559. }
  560. child=child.nextSibling;
  561. }
  562. node.classList.add("CJKTestedAndLabeled");
  563. node.classList.add("FontsFixedE137");
  564. node.classList.add("MarksFixedE135");
  565. node.classList.add("preMath");
  566. }
  567. function addSpaces(useTimeout) {
  568. var t_spaces=performance.now();
  569. if (debug_spaces===true) console.log('FixCJK!: Adding spaces...');
  570. var allQ=document.querySelectorAll(".\uE985");
  571. for (var iq=0;iq<allQ.length;iq++) {
  572. allQ[iq].innerHTML=allQ[iq].innerHTML.replace(/\u2018/g,'\uEB18');
  573. allQ[iq].innerHTML=allQ[iq].innerHTML.replace(/\u2019/g,'\uEB19');
  574. allQ[iq].innerHTML=allQ[iq].innerHTML.replace(/\u201C/g,'\uEB1C');
  575. allQ[iq].innerHTML=allQ[iq].innerHTML.replace(/\u201D/g,'\uEB1D');
  576. }
  577. addSpacesHelper(document.querySelectorAll(".PunctSpace2Fix:not(.SpacesFixedE133)"),performance.now());
  578. allQ=document.querySelectorAll(".\uE985"); //I need to reselect because the "references" are changed?
  579. for (iq=0;iq<allQ.length;iq++) {
  580. allQ[iq].innerHTML=allQ[iq].innerHTML.replace(/\uEB18/g,'\u2018');
  581. allQ[iq].innerHTML=allQ[iq].innerHTML.replace(/\uEB19/g,'\u2019');
  582. allQ[iq].innerHTML=allQ[iq].innerHTML.replace(/\uEB1C/g,'\u201C');
  583. allQ[iq].innerHTML=allQ[iq].innerHTML.replace(/\uEB1D/g,'\u201D');
  584. }
  585. if (useRemoveSpacesForSimSun===true) {
  586. window.setTimeout(removeSpacesForSimSun,10);
  587. }
  588. console.log("FixCJK: Adding spaces took "+((performance.now()-t_spaces)/1000).toFixed(3)+" seconds.");
  589. function getAfterHTML(child) { //FIXME: A recursion block might be needed as getAfter(child)
  590. var toReturn='';
  591. var t_start=performance.now();
  592. var inputNode=child;
  593. child=child.nextSibling;
  594. while (child && (performance.now()-t_start)<2 ) {
  595. if (child.nodeType===3) {
  596. toReturn = toReturn + child.data;
  597. }
  598. else if (child.nodeType===1 && (window.getComputedStyle(child,null).getPropertyValue("display")!=='none') ) {
  599. if (child.nodeName.match(stopTags) || inTheClassOf(child,stopClasses) ) {
  600. return toReturn+"上下标";
  601. }
  602. toReturn = toReturn + displayedText(child);
  603. }
  604. if (toReturn.match(/[\w\u3400-\u9FBF]/)) {
  605. break;
  606. }
  607. child=child.nextSibling;
  608. }
  609. if (toReturn.length < 1 && !inputNode.parentNode.nodeName.match(upEnoughTags)) {
  610. return getAfterHTML(inputNode.parentNode);
  611. }
  612. else {
  613. return (toReturn.replace(/</,'&lt;')).replace(/>/,'&gt;');
  614. }
  615. }
  616. function getBeforeHTML(child) {
  617. var toReturn='';
  618. var t_start=performance.now();
  619. var inputNode=child;
  620. child=child.previousSibling;
  621. while (child && (performance.now()-t_start)<2 ) {
  622. if (child.nodeType === 3) {
  623. toReturn = child.data + toReturn;
  624. }
  625. else if (child.nodeType === 1 && (window.getComputedStyle(child,null).getPropertyValue("display")!=='none') ) {
  626. if (child.nodeName.match(stopTags) || inTheClassOf(child,stopClasses) ) {
  627. return "上下标"+toReturn;
  628. }
  629. toReturn = displayedText(child) + toReturn;
  630. }
  631. if (toReturn.match(/[\w\u3400-\u9FBF]/)) {
  632. break;
  633. }
  634. child=child.previousSibling;
  635. }
  636. if (toReturn.length < 1 && !inputNode.parentNode.nodeName.match(upEnoughTags)) {
  637. return getBeforeHTML(inputNode.parentNode);
  638. }
  639. else {
  640. return (toReturn.replace(/</,'&lt;')).replace(/>/,'&gt;');
  641. }
  642. }
  643. function addSpacesHelper(allE,t_substart) {
  644. for (var is=0;is<allE.length;is++) {
  645. if ( !(allE[is].nodeName.match(/FONT/)) || allE[is].classList.contains("SpacesFixedE133") ) {
  646. continue;
  647. }
  648. if ( useTimeout===true && (performance.now()-t_substart)> 800) {
  649. console.log("Timeout: exiting addSpaces()...");
  650. return false;
  651. }
  652. if (allE[is].classList.contains("wrappedCJK2Fix") ) {
  653. if ( !(allE[is].classList.contains("preCode")) ) {
  654. var tmp_str=allE[is].innerHTML;
  655. if (tmp_str.match(/^([\s\u0020\u00A0\u2009\u200B-\u200E]|&nbsp;|&thinsp;){0,5}[\u3400-\u9FBF]/)) {
  656. tmp_str=getBeforeHTML(allE[is])+'\uF203CJK\uF203'+tmp_str;
  657. }
  658. if (tmp_str.match(/[\u3400-\u9FBF][\s\u200B-\u200E\2060]{0,2}$/)) {
  659. tmp_str=tmp_str+'\uF204CJK\uF204'+getAfterHTML(allE[is]);
  660. }
  661. //protect the Latins in tags, no need in 1.0+ b/c no “”’‘ in CJK <span> tags.
  662. //en:zh; //why didn't I use "non-CJK" list for Latin?
  663. tmp_str=tmp_str.replace(/&nbsp;/,'\u00A0'); //Or, tmp_str=tmp_str.replace(/\u0026nbsp\u003B/,'\u00A0');
  664. tmp_str=tmp_str.replace(/&thinsp;/,'\u2009'); //Or, tmp_str=tmp_str.replace(/\u0026thinsp\u003B/,'\u2009');
  665. var re_enzh=/([\u0021\u0023-\u0026\u0029\u002A-\u003B\u003D\u003F-\u005A\u005C-\u007B\u007D-\u009F\u00A1-\u00FF\u0391-\u03FF\u2027\u2600-\u26FF’”])([\uF201-\uF204]CJK[\uF201-\uF204])?(?:[\u0020\u00A0\u2009\u200B-\u200E\u2060]){0,5}(\uF203CJK\uF203)?(?:[\u0020\u00A0\u200B-\u200E\u2060]){0,5}([\uF201-\uF204]CJK[\uF201-\uF204])?([\u3400-\u9FBF])/img;
  666. var space2BeAdded='<span class="CJKTestedAndLabeled MarksFixedE135 \uE699 FontsFixedE137" style="display:inline;padding-left:0px;padding-right:0px;float:none;font-family:Arial,Helvetica,sans-serif;font-size:80%;">\u0020</span>';
  667. if (useSFTags===false) {space2BeAdded='\u2009';} //\u2009 for thin space and \u200A for "hair space".
  668. var enzh_withSpace='$1$2$3$4'+space2BeAdded+'$5';
  669. tmp_str=tmp_str.replace(re_enzh,enzh_withSpace);
  670. //now zh:en
  671. var re_zhen=/([\u3400-\u9FBF])(?:[\u0020\u00A0\u2009\u200B-\u200E\u2060]|&nbsp;){0,5}([\uF201-\uF204]CJK[\uF201-\uF204])?(?:[\u0020\u00A0\u200B-\u200E\u2060]|&nbsp;){0,5}([\uF201-\uF204]CJK[\uF201-\uF204])?([‘“\u0021\u0023-\u0026\u0028\u002A-\u003B\u003D\u003F-\u005C\u005E-\u007B\u007D-\u009F\u00A1-\u00FF\u0391-\u03FF\u2027\u2600-\u26FF])/img;
  672. var zhen_withSpace='$1'+space2BeAdded+'$2$3$4';
  673. tmp_str=tmp_str.replace(re_zhen,zhen_withSpace);
  674. //now en["']zh (TODO in 1.x?)
  675. //now zh['"]en (TODO in 1.x?)
  676. tmp_str=tmp_str.replace(/\uED20/mg,'');
  677. tmp_str=tmp_str.replace(/^[^\u0000]*\uF203CJK\uF203([^\u0000]*)$/,'$1'); // '.' does not match \n in whatever mode.
  678. tmp_str=tmp_str.replace(/^([^\u0000]*)\uF204CJK\uF204[^\u0000]*$/,'$1');
  679. allE[is].innerHTML=tmp_str;
  680. allE[is].classList.add("SpacesFixedE133");
  681. }
  682. else {
  683. if (debug_spaces===true) {console.log("Skipping banned tags:"+allE[is].tagName);}
  684. }
  685. }
  686. }
  687. }
  688. }
  689. function removeSpacesForSimSun() { //Need more work.
  690. var allS=document.getElementsByClassName("\uE699");
  691. var font_str='';
  692. for (var i=0;i<allS.length;i++) {
  693. font_str=((dequote(window.getComputedStyle(allS[i].parentNode, null).getPropertyValue('font-family'))).split(','))[1];
  694. if (font_str.match(re_simsun)) {
  695. allS[i].innerHTML='';
  696. }
  697. else if (font_str.match(/RealCJKBold.易/)) {
  698. allS[i].parentNode.classList.add("checkSpacedQM");
  699. }
  700. }
  701. allS=document.getElementsByClassName("checkSpacedQM");
  702. for (i=0;i<allS.length;i++){
  703. var toRemoved=/(<span[^><]*\uE699[^><]*>\u0020<\/span>)((?:<[^><\uE985\uE211]*>)*[\u2018\u201C])/g;
  704. if (allS[i].innerHTML.match(toRemoved)) {
  705. allS[i].innerHTML=allS[i].innerHTML.replace(toRemoved,'$2');
  706. }
  707. //No closing tag: En"Zh
  708. toRemoved=/([\u2019\u201D])<span[^><]*\uE699[^><]*>\u0020<\/span>/g;
  709. if (allS[i].innerHTML.match(toRemoved)) {
  710. allS[i].innerHTML=allS[i].innerHTML.replace(toRemoved,'$1');
  711. }
  712. //With closing tag: En"Zh
  713. toRemoved=/((?:^|[^>]|<[^><\uE985\uE211]*>)[\u2019\u201D](?:<[^><\uE985\uE211]*>)+)(<span[^><]*\uE699[^><]*>\u0020<\/span>)/mg;
  714. if (allS[i].innerHTML.match(toRemoved)) {
  715. allS[i].innerHTML=allS[i].innerHTML.replace(toRemoved,'$1');
  716. }
  717. }
  718. }
  719. function ReFixCJKFontsOnly () {
  720. if (refixingFonts===true) {
  721. console.log("Refixing, skipping this refix...");
  722. window.setTimeout(function () {refixingFonts=false;},t_interval/ItvScl/2);
  723. return false;
  724. }
  725. refixingFonts=true;
  726. var bannedTagsInReFix=/^(A|BUTTON|TEXTAREA|AUDIO|VIDEO|SOURCE|FORM|INPUT|select|option|label|fieldset|datalist|keygen|output|canvas|nav|svg|img|figure|map|area|track|menu|menuitem)$/i;
  727. t_start=performance.now();
  728. if ( (t_start-t_last)*ItvScl > t_interval ) {
  729. FixRegular = true; //Also fix regular fonts. You need to keep this true if you want to use "LatinInSimSun" in Latin/CJK mixed context.
  730. FixMore = false; //Appendent CJK fonts to all elements. No side effects found so far.
  731. FixPunct = false; //If Latin punctions in CJK paragraph need to be fixed. Usually one needs full-width punctions in CJK context. Turn it off if the script runs too slow or HTML strings are adding to your editing area.
  732. ifRound1 = true;
  733. ifRound2 = true;
  734. ifRound3 = false;
  735. maxlength = 1100200; //maximum length of the page HTML to check for CJK punctuations.
  736. maxNumElements = 8000; // maximum number of elements to process.
  737. CJKOnlyThreshold = 2000; // Only CJK if the number of elements reaches this threshold.
  738. labelPreMath();
  739. labelCJK(true);
  740. FixAllFonts(true);
  741. console.log('FixCJK!: Fast ReFixing took '+((performance.now()-t_start)/1000).toFixed(3)+' seconds.');
  742. }
  743. t_last=performance.now();
  744. refixingFonts=false;
  745. }
  746. function ReFixCJK (e) {
  747. if (refixing===true) {
  748. if (debug_wrap===true) {console.log("Refixing, skipping this refix...");}
  749. window.setTimeout(function () {refixing=false;},t_interval/ItvScl);
  750. return false;
  751. }
  752. refixing=true;
  753. var bannedTagsInReFix=/^(A|BUTTON|TEXTAREA|AUDIO|VIDEO|SOURCE|FORM|INPUT|select|option|label|fieldset|datalist|keygen|output|canvas|nav|svg|img|figure|map|area|track|menu|menuitem)$/i;
  754. if (debug_verbose===true) {console.log(e.target.nodeName);}
  755. t_start=performance.now();
  756. if (document.URL!==LastURL) {
  757. NumPureEng = 0;
  758. LastURL=document.URL;
  759. }
  760. var clickedNode=e.target;
  761. while (clickedNode && clickedNode.nodeName!=="BODY") {
  762. if (clickedNode.nodeName.match(bannedTagsInReFix)) {
  763. console.log("FixCJK!: Not a valid click on DOM element \u201C"+clickedNode.nodeName+"."+clickedNode.className+"\u201D");
  764. refixing=false;
  765. return false;
  766. }
  767. if (debug_verbose===true) {console.log("Clicked: "+clickedNode.nodeName);}
  768. clickedNode=clickedNode.parentNode;
  769. }
  770. if ((document.lastModified===LastMod) && (NumClicks >2)) {
  771. if (debug_verbose===true) {console.log('FixCJK!: Document modified at '+document.lastModified+', no change?');}
  772. }
  773. else {
  774. if (debug_verbose===true) {console.log('FixCJK!: Document modified at '+document.lastModified);}
  775. }
  776. //NumPureEng method is still usefull because document.lastModified method is only partially reliable.
  777. if (NumPureEng >= 2) {
  778. console.log('Probably pure English/Latin site, re-checking skipped.');
  779. refixing=false;
  780. return true;
  781. }
  782. if (debug_verbose===true) {console.log('FixCJK!: NumClicks='+NumClicks.toString());}
  783. //First remove the "CJK2Fix" attibute for those already processed.
  784. var AllCJKFixed=document.getElementsByClassName("FontsFixedE137");
  785. for (i=0;i<AllCJKFixed.length;i++) {
  786. if (AllCJKFixed[i].classList.contains("wrappedCJK2Fix")) {
  787. continue;
  788. }
  789. if (debug_verbose===true) {console.log(AllCJKFixed[i].className);}
  790. if (AllCJKFixed[i].classList.contains("MarksFixedE135")) {
  791. AllCJKFixed[i].classList.remove("CJK2Fix");
  792. }
  793. }
  794. if ((NumClicks < 1) || (t_start-t_last)*ItvScl > t_interval ) {
  795. FixRegular = true; //Also fix regular fonts. You need to keep this true if you want to use "LatinInSimSun" in Latin/CJK mixed context.
  796. FixMore = true; //Appendent CJK fonts to all elements. No side effects found so far.
  797. FixPunct = true; //If Latin punctions in CJK paragraph need to be fixed. Usually one needs full-width punctions in CJK context. Turn it off if the script runs too slow or HTML strings are adding to your editing area.
  798. maxlength = 1100200; //maximum length of the page HTML to check for CJK punctuations.
  799. maxNumElements = 8000; // maximum number of elements to process.
  800. CJKOnlyThreshold = 2000; // Only CJK if the number of elements reaches this threshold.
  801. invForLimit=6; //the time limit factor (actual limit is timeOut/invForLimit) for the "for loop" in Round 2 & 3.
  802. processedAll=true;
  803. ifRound1=true;
  804. ifRound2=true;
  805. ifRound3=false;
  806. var ReFixAll=document.getElementsByTagName('*');
  807. var NumFixed=0;
  808. var NumReFix=0;
  809. labelPreMath();
  810. labelCJK(true);
  811. FixAllFonts(true);
  812. if (debug_verbose===true) {console.log('FixCJK!: '+NumFixed.toString()+' elements has been fixed.');}
  813. if (debug_verbose===true) {console.log('FixCJK!: '+NumReFix.toString()+' elements to Re-Fix.');}
  814. labelPreCode();
  815. labelNoWrappingList();
  816. if (useWrap===true) wrapCJK();
  817. FunFixPunct(true,2,returnLater);
  818. console.log('FixCJK!: ReFixing (Fixing PMs not included) took '+((performance.now()-t_start)/1000).toFixed(3)+' seconds.');
  819. NumAllCJKs=(document.getElementsByClassName('MarksFixedE135')).length;
  820. if (NumAllCJKs*1.0/NumAllDOMs*100 < 1.0) {
  821. NumPureEng++;
  822. }
  823. }
  824. else {
  825. console.log('FixCJK!: No need to rush. Just wait for '+(t_interval/1000/ItvScl).toFixed(1)+' seconds before clicking again.');
  826. }
  827. NumClicks++;
  828. LastMod=document.lastModified;
  829. t_last=performance.now();
  830. refixing=false;
  831. }
  832. ///===various aux functions===///
  833. function wrapCJK() {
  834. var wrap_start=performance.now();
  835. var allCJK=document.querySelectorAll(".CJK2Fix:not(.wrappedCJK2Fix)");
  836. for (var i=0;i<allCJK.length;i++) {
  837. if ( allCJK[i].classList.contains("\uE211") || allCJK[i].classList.contains("\uE985") ||allCJK[i].classList.contains("preCode")) {
  838. if (allCJK[i].classList.contains("wrappedCJK2Fix")) console.log("FIXME: "+allCJK[i].nodeName+" has already been wrapped.");
  839. continue;
  840. }
  841. else if (allCJK[i].nodeName.match(SkippedTagsForMarks)) {
  842. continue;
  843. }
  844. var child=allCJK[i].firstChild;
  845. while(child) {
  846. var realSibling=child.nextSibling;
  847. if (child.nodeType===3 && (child.data.match(/[“”‘’\u3000-\u303F\u3400-\u9FBF\uFF00-\uFFEF]/)) ) {
  848. wrapCJKHelper(child);
  849. }
  850. child=realSibling;
  851. }
  852. }
  853. if (debug_wrap===true) console.log("Wrapping took "+((performance.now()-wrap_start)/1000).toFixed(3)+" seconds.");
  854. function wrapCJKHelper(child) {
  855. var iNode=document.createElement("font");
  856. var iText=document.createTextNode(child.data);
  857. iNode.appendChild(iText);
  858. iNode.classList.add("wrappedCJK2Fix");
  859. iNode.classList.add("CJKTestedAndLabeled");
  860. iNode.classList.add("PunctSpace2Fix");
  861. iNode.classList.add("CJK2Fix");
  862. iNode.classList.add("Safe2FixCJK\uE000");
  863. child.parentNode.insertBefore(iNode,child.nextSibling);
  864. child.data=""; //or "\u200B?"
  865. }
  866. }
  867. function inTheClassOf(node,cList) {
  868. var classes=cList.split(',');
  869. for (var i=0;i<classes.length;i++) {
  870. if (node.classList.contains(classes[i])) {
  871. return true;
  872. }
  873. }
  874. return false;
  875. }
  876. function check_fonts(font_var, fvname) {
  877. var fl = font_var.split(',');
  878. for (i = 0; i < fl.length; i++) {
  879. if (!(fl[i].match(/^[^" ][^"]+[^" ]$|^"[^ ][^"]+[^ ]"$/))) {
  880. alert('Check your font definition: ' + fl[i] + ' in ' + fvname);
  881. return false;
  882. }
  883. }
  884. return true;
  885. }
  886. function list_has(font_str, family) {
  887. /// Fucntion to check matches
  888. var allfonts = font_str.split(',');
  889. for (var j = 0, maxl = allfonts.length; j < maxl; j++) {
  890. if (allfonts[j].match(family)) {
  891. return j;
  892. }
  893. }
  894. return false;
  895. }
  896. function replace_font(font_str, family, qBold) {
  897. var allfonts = font_str.split(',');
  898. var j = 0;
  899. var maxl = allfonts.length;
  900. for (j = 0; j < maxl; j++) {
  901. if (allfonts[j].match(family)) {
  902. allfonts[j] = qBold;
  903. }
  904. }
  905. var toReturn = allfonts[0];
  906. for (j = 1; j < maxl; j++) {
  907. toReturn = toReturn + ',' + allfonts[j];
  908. }
  909. return toReturn;
  910. }
  911. function has_genfam(font_str) {
  912. /// Test if font_str include general families.
  913. if (list_has(font_str, re_sans0)) {
  914. return true;
  915. }
  916. else if (list_has(font_str, re_serif)) {
  917. return true;
  918. }
  919. else if (list_has(font_str, re_mono0)) {
  920. return true;
  921. }
  922. return false;
  923. }
  924. function dequote(font_str) {
  925. /// Function to dequote non-standard font lists.
  926. var strl=font_str.split(','); //font list;
  927. for (var k=0;k < strl.length; k++) {
  928. while (strl[k].charAt(0).match(/["' ]/)) {
  929. strl[k]=strl[k].slice(1);
  930. }
  931. while (strl[k].charAt(strl[k].length-1).match(/["' ]/)) {
  932. strl[k]=strl[k].slice(0,-1);
  933. }
  934. }
  935. var dequoted=strl[0];
  936. for (k=1;k<strl.length;k++) {
  937. dequoted=dequoted+','+strl[k];
  938. }
  939. return dequoted;
  940. }
  941. function FirstFontOnly(font_str) {
  942. return ((dequote(font_str)).split(','))[0];
  943. }
  944. function AddLocal(font_str) {
  945. font_str=(dequote(font_str)).split(',');
  946. var localed='local("'+font_str[0]+'"), local("'+font_str[0]+' Regular")';
  947. for (var l=1;l<font_str.length;l++) {
  948. localed=localed+',\n'+'local("'+font_str[l]+'"),local("'+font_str[l]+' Regular")';
  949. }
  950. return localed;
  951. }
  952. /// ======================== FixAllFonts, 3 Rounds ==============================///
  953. function FixAllFonts (useTimeout) {
  954. var func_start=performance.now();
  955. if (debug_verbose===true) {
  956. console.log("Round 1: "+ifRound1.toString());
  957. console.log("Round 2: "+ifRound2.toString());
  958. console.log("Round 3: "+ifRound3.toString());
  959. }
  960. SkippedTags=SkippedTagsForFonts;
  961. /// ===== First round: Replace all bold fonts to CJKBold ===== ///
  962. t_stop=performance.now();
  963. //First fix all SimSun parts in Round 1&2. Original: var allSuns=document.getElementsByClassName("SimSun2Fix");
  964. var allSuns=document.querySelectorAll(".SimSun2Fix:not(.SimSunFixedE137)");
  965. for (var isun=0;isun< allSuns.length;isun++) {
  966. if (allSuns[isun].classList.contains("FontsFixedE137") || allSuns[isun].classList.contains("SimSunFixedE137")) {
  967. continue;
  968. }
  969. font_str = dequote(window.getComputedStyle(allSuns[isun], null).getPropertyValue('font-family'));
  970. if (font_str.match(re_simsun) && !(font_str.match(sig_sim)) ) {
  971. allSuns[isun].style.fontFamily = font_str.replace(re_simsun,qSimSun);
  972. allSuns[isun].classList.add("SimSunFixedE137");
  973. }
  974. }
  975. //Large fonts: allSuns=document.getElementsByClassName("LargeSimSun2Fix");
  976. allSuns=document.querySelectorAll(".LargeSimSun2Fix:not(.SimSunFixedE137)");
  977. for (isun=0;isun< allSuns.length;isun++) {
  978. if (allSuns[isun].classList.contains("FontsFixedE137") || allSuns[isun].classList.contains("SimSunFixedE137")) {
  979. continue;
  980. }
  981. font_str = dequote(window.getComputedStyle(allSuns[isun], null).getPropertyValue('font-family'));
  982. if (font_str.match(re_simsun) && !(font_str.match(sig_sim)) ) {
  983. allSuns[isun].style.fontFamily = font_str.replace(re_simsun,qLargeSimSun);
  984. allSuns[isun].classList.add("SimSunFixedE137");
  985. }
  986. }
  987. //All elements to fix fonts: all = document.getElementsByClassName('CJK2Fix');
  988. all=document.querySelectorAll(".CJK2Fix:not(.FontsFixedE137)");
  989. if (ifRound1===true) {
  990. for (i = 0; i < all.length; i++) {
  991. if (i % 500===0) { //Check every 500 elements.
  992. if ( useTimeout===true && (performance.now()-t_stop)*invForLimit > timeOut) {
  993. ifRound1=false;
  994. ifRound2=false;
  995. ifRound3=false;
  996. FixPunct=false;
  997. processedAll=false;
  998. console.log('FixCJK!: Round 1 itself has been running for '+((performance.now()-t_stop)/1000).toFixed(3)+' seconds. Too slow to continue.');
  999. break;
  1000. }
  1001. else {
  1002. if (debug_verbose===true) {console.log('FixCJK!: Round 1 itself has been running for '+((performance.now()-t_stop)/1000).toFixed(3)+' seconds.');}
  1003. }
  1004. }
  1005. if (all[i].classList.contains("FontsFixedE137")) {
  1006. continue;
  1007. }
  1008. child = all[i].firstChild;
  1009. if_replace = false;
  1010. //Only change if current node (not child node) contains CJK characters.
  1011. font_str = dequote(window.getComputedStyle(all[i], null).getPropertyValue('font-family'));
  1012. fweight = window.getComputedStyle(all[i], null).getPropertyValue('font-weight');
  1013. while (child) {
  1014. var realSibling=child.nextSibling;
  1015. if (child.nodeType == 3 && (child.data.match(/[“”‘’\u3000-\u303F\u3400-\u9FBF\uFF00-\uFFEF]/)) && (fweight == 'bold' || fweight > 500) && (!(font_str.match(sig_bold)))) {
  1016. //Test if contains SimSun
  1017. if (debug_01===true) {all[i].style.color="Blue";} //Bold-->Blue;
  1018. //Test if contains Sans
  1019. if (list_has(font_str, re_sans0) !== false) {
  1020. if (debug_01===true) all[i].style.color="Salmon";
  1021. all[i].style.fontFamily = genPunct+','+ replace_font(font_str, re_sans0, LatinSans+','+qBold) + ',sans-serif';
  1022. window.setTimeout(function(node) {node.classList.add("FontsFixedE137");},1,all[i]); //Slow and Use async I/O.
  1023. } //Test if contains serif
  1024. else if (list_has(font_str, re_serif) !== false) {
  1025. if (debug_01===true) all[i].style.color="SeaGreen";
  1026. all[i].style.fontFamily = genPunct+','+ replace_font(font_str, re_serif, LatinSerif + ',' +qBold) + ',serif';
  1027. window.setTimeout(function(node) {node.classList.add("FontsFixedE137");},1,all[i]); //Slow and Use async I/O.
  1028. } //Test if contains monospace
  1029. else if (list_has(font_str, re_mono0) !== false) {
  1030. if (debug_01===true) all[i].style.color="Maroon";
  1031. all[i].style.fontFamily = genPunct+','+ replace_font(font_str, re_mono0, LatinMono + ',' +qBold) + ',monospace';
  1032. window.setTimeout(function(node) {node.classList.add("FontsFixedE137");},1,all[i]); //Slow and Use async I/O.
  1033. } //Just append the fonts to the font preference list.
  1034. else {
  1035. all[i].style.fontFamily = genPunct+','+font_str + ',' + LatinSans + ',' + qBold + ',' + ' sans-serif';
  1036. window.setTimeout(function(node) {node.classList.add("FontsFixedE137");},1,all[i]); //Slow and Use async I/O.
  1037. }
  1038. }
  1039. child = realSibling;
  1040. }
  1041. }
  1042. }
  1043. if (FixRegular === false) {
  1044. return false;
  1045. }
  1046. /// ===== Second Round: Deal with regular weight. ===== ///
  1047. var tmp_idx=0;
  1048. max = all.length;
  1049. if (useTimeout===true && (performance.now()-t_stop)*4 > timeOut) {
  1050. ifRound2=false;
  1051. ifRound3=false;
  1052. FixPunct=false;
  1053. processedAll=false;
  1054. console.log('FixCJK!: Round 1 has been running for '+((performance.now()-t_stop)/1000).toFixed(3)+' seconds. Skipping following steps.');
  1055. }
  1056. t_stop=performance.now();
  1057. if (ifRound2===true) {
  1058. //Now fix the rest.
  1059. for (i = 0; i < all.length; i++) {
  1060. if (i % 500===0) { //Check every 500 elements.
  1061. if (useTimeout===true && (performance.now()-t_stop)*invForLimit > timeOut) {
  1062. ifRound2=false;
  1063. ifRound3=false;
  1064. FixPunct=false;
  1065. processedAll=false;
  1066. console.log('FixCJK!: Round 2 itself has been running for '+((performance.now()-t_stop)/1000).toFixed(3)+' seconds. Too slow to continue.');
  1067. break;
  1068. }
  1069. else {
  1070. if (debug_verbose===true) {console.log('FixCJK!: Round 2 itself has been running for '+((performance.now()-t_stop)/1000).toFixed(3)+' seconds.');}
  1071. }
  1072. }
  1073. if (all[i].classList.contains("FontsFixedE137") ) {
  1074. continue;
  1075. }
  1076. font_str = dequote(window.getComputedStyle(all[i], null).getPropertyValue('font-family'));
  1077. fweight = window.getComputedStyle(all[i], null).getPropertyValue('font-weight');
  1078. if (font_str.match(sig_hei) || font_str.match(sig_song) ||font_str.match(sig_bold) || font_str.match(sig_mono) || font_str.match(sig_default)) {
  1079. window.setTimeout(function(node) {node.classList.add("FontsFixedE137");},1,all[i]); //Slow and Use async I/O.
  1080. continue;
  1081. }
  1082. else {
  1083. if (debug_02===true) {all[i].style.color='Teal';} //Teal for true;
  1084. if (debug_02===true) {if (all[i].innerHTML.match(re_to_check)) {console.log('\\\\\\\\\\\\afterall:'+i.toString()+'::'+all[i].style.fontFamily+'\n-->if_replace:'+if_replace);}}
  1085. //Test if contains Sans
  1086. if (list_has(font_str, re_sans0) !== false) {
  1087. //all[i].style.color="Salmon";
  1088. all[i].style.fontFamily = genPunct+','+replace_font(font_str, re_sans0, qsans);
  1089. window.setTimeout(function(node) {node.classList.add("FontsFixedE137");},1,all[i]); //Slow and Use async I/O.
  1090. } //Test if contains serif
  1091. else if (list_has(font_str, re_serif) !== false) {
  1092. //all[i].style.color="SeaGreen";
  1093. all[i].style.fontFamily = genPunct+','+replace_font(font_str, re_serif, qserif);
  1094. window.setTimeout(function(node) {node.classList.add("FontsFixedE137");},1,all[i]); //Slow and Use async I/O.
  1095. } //Test if contains monospace
  1096. else if (list_has(font_str, re_mono0) !== false) {
  1097. //all[i].style.color="Maroon";
  1098. all[i].style.fontFamily = genPunct+','+replace_font(font_str, re_mono0, qmono);
  1099. window.setTimeout(function(node) {node.classList.add("FontsFixedE137");},1,all[i]); //Slow and Use async I/O.
  1100. }
  1101. else {
  1102. if (debug_02===true) {all[i].style.color='Fuchsia';}
  1103. if (font_str.match(re_simsun)) {
  1104. //Do nothing.
  1105. }
  1106. else {
  1107. all[i].style.fontFamily = genPunct+','+font_str + ',' + qCJK + ',' + 'sans-serif';
  1108. window.setTimeout(function(node) {node.classList.add("FontsFixedE137");},1,all[i]); //Slow and Use async I/O.
  1109. }
  1110. }
  1111. }
  1112. }
  1113. }
  1114. if (debug_verbose===true) {console.log('FixCJK!: Round 2 took '+((performance.now()-t_stop)/1000).toFixed(3)+' seconds.');}
  1115. t_stop=performance.now();
  1116. if (debug_02===true) console.log('Just before Round 3:'+tmp_idx.toString()+'::'+all[tmp_idx].innerHTML);
  1117. if (debug_02===true) console.log('Just before Round 3:'+tmp_idx.toString()+'::'+dequote(window.getComputedStyle(all[tmp_idx], null).getPropertyValue('font-family')));
  1118. /// ===== The Third round (Round 3): Add CJKdefault to all elements ===== ///
  1119. if (FixMore === false) {
  1120. t_stop=performance.now();
  1121. if (debug_verbose===true) {console.log('FixCJK!: FixMore/Round 3 is intentionally skipped.');}
  1122. return false;
  1123. }
  1124. all=document.querySelectorAll(":not(.FontsFixedE137)");
  1125. max = all.length;
  1126. if (max > maxNumElements) {
  1127. ifRound3=false;
  1128. FixPunct=false;
  1129. processedAll=false;
  1130. console.log('FixCJK!: '+max.toString()+' elements, too many. Skip Round 3 and punctuation fixing. Exiting now...');
  1131. }
  1132. else if (max > CJKOnlyThreshold) {
  1133. ifRound3=true;
  1134. FixPunct=true;
  1135. processedAll=true;
  1136. //Now get all elements to be fixed: all = document.getElementsByTagName('CJK2Fix');
  1137. all=document.querySelectorAll(".CJK2Fix:not(.FontsFixedE137)");
  1138. console.log('FixCJK!: '+max.toString()+' elements, too many. Only CJK elements will be processed in Round 3.');
  1139. }
  1140. else {
  1141. if (debug_verbose===true) {console.log('FixCJK!: All elements will be processed in Round 3.');}
  1142. }
  1143. if (ifRound3===true) {
  1144. for (i = 0; i < all.length; i++) {
  1145. if (i % 500===0) { //Check every 500 elements.
  1146. if (useTimeout===true && (performance.now()-t_stop)*invForLimit > timeOut) {
  1147. ifRound3=false;
  1148. FixPunct=false;
  1149. processedAll=false;
  1150. console.log('FixCJK!: Round 3 itself has been running for '+((performance.now()-t_stop)/1000).toFixed(3)+' seconds. '+i.toFixed(0)+' elements have been fixed, but too slow to continue. Stopped at:');
  1151. console.log(all[i]);
  1152. break;
  1153. }
  1154. else {
  1155. if (debug_verbose===true) {console.log('FixCJK!: Round 3 itself has been running for '+((performance.now()-t_stop)/1000).toFixed(3)+' seconds. ');}
  1156. }
  1157. }
  1158. if (all[i].nodeName.match(SkippedTags) || (all[i] instanceof SVGElement) ) {
  1159. if (debug_03===true) {console.log("Skipped:");console.log(all[i]);}
  1160. continue;
  1161. }
  1162. else if (all[i].classList.contains("FontsFixedE137")) {
  1163. if (debug_03===true) all[i].style.color="FireBrick"; //FireBrick <-- Fixed.
  1164. continue;
  1165. }
  1166. font_str = dequote(window.getComputedStyle(all[i], null).getPropertyValue('font-family'));
  1167. if (font_str.split(',').length >= rspLength) {
  1168. //continue if all[i] contains a list of fonts.
  1169. if (!all[i].classList.contains("CJK2Fix")) {
  1170. window.setTimeout(function(node) {node.classList.add("FontsFixedE137");},1,all[i]); //Slow and Use async I/O.
  1171. if (debug_03===true) {console.log(all[i]);all[i].style.color="FireBrick";} //FireBrick <-- Fixed.
  1172. continue;
  1173. }
  1174. }
  1175. if (!(font_str.match(sig_song) || font_str.match(sig_hei) || font_str.match(sig_bold) || font_str.match(sig_default) || font_str.match(/\uE137/))) {
  1176. if (list_has(font_str, re_sans0) !== false) {
  1177. if (debug_03 === true) all[i].style.color="Salmon";
  1178. all[i].style.fontFamily = genPunct+','+replace_font(font_str, re_sans0, qsans);
  1179. } //Test if contains serif
  1180. else if (list_has(font_str, re_serif) !== false) {
  1181. if (debug_03 === true) all[i].style.color="SeaGreen";
  1182. all[i].style.fontFamily = genPunct+','+replace_font(font_str, re_serif, qserif);
  1183. } //Test if contains monospace
  1184. else if (list_has(font_str, re_mono0) !== false) {
  1185. if (debug_03 === true) all[i].style.color="Maroon";
  1186. all[i].style.fontFamily = genPunct+','+replace_font(font_str, re_mono0, qmono);
  1187. }
  1188. else {
  1189. //SimSun should be taken care of throught the "SimSun2Fix" class.
  1190. if (debug_03 === true) { all[i].style.color='Olive';}
  1191. all[i].style.fontFamily = genPunct+','+font_str + ',' + qCJK + ',' + 'sans-serif';
  1192. }
  1193. }
  1194. else {
  1195. if (debug_03 === true) all[i].style.color="Silver"; //Signed-->Silver
  1196. }
  1197. window.setTimeout(function(node) {node.classList.add("FontsFixedE137");},1,all[i]); //Slow and Use async I/O.
  1198. if (debug_03===true) all[i].style.color="FireBrick"; //FireBrick <-- Fixed.
  1199. }
  1200. }
  1201. if (debug_verbose===true) {console.log('FixCJK!: Round 3 took '+((performance.now()-t_stop)/1000).toFixed(3)+' seconds.');}
  1202. t_stop=performance.now();
  1203. if (debug_wrap===true) console.log("Fixing Fonts took "+((performance.now()-func_start)/1000).toFixed(3)+" seconds.");
  1204. }
  1205. ///===The Actual Round 4===///
  1206. function FunFixPunct(useTimeout,MaxNumLoops,returnNow) {
  1207. SkippedTags=SkippedTagsForMarks;
  1208. var recursion_start=0;
  1209. var func_start=performance.now();
  1210. //Use Recursion instead of loop, should be put in the MaxNumLoops in production code.
  1211. if (returnNow===true) {
  1212. return true;
  1213. }
  1214. var allrecur=document.querySelectorAll(".PunctSpace2Fix:not(.MarksFixedE135)");
  1215. for (var ir=0; ir<allrecur.length; ir++) {
  1216. //Seems no need to add !(allrecur[ir].parentNode.classList.contains("CJK2Fix")). It might be faster to fix the deepest element first through looping.
  1217. recursion_start=performance.now();
  1218. if (allrecur[ir].nodeName.match(/FONT/)) {
  1219. FixPunctRecursion(allrecur[ir]);
  1220. }
  1221. if ( useTimeout===true && (performance.now()-t_start) > timeOut ) {
  1222. processedAll=false;
  1223. console.log("FixCJK!: Time out. Last fixing took "+((performance.now()-recursion_start)/1000).toFixed(3)+" seconds.");
  1224. console.log("FIXME:"+allrecur[ir].nodeName+"."+allrecur[ir].className);
  1225. break;
  1226. }
  1227. }
  1228. if (debug_wrap===true || debug_asyncTimers===true) console.log("FixCJK!: Fixing PMs took "+((performance.now()-func_start)/1000).toFixed(3)+" seconds.");
  1229. }
  1230. /////=====The Recursive Implementation=====/////
  1231. function FixPunctRecursion(node) {
  1232. if (node.classList.contains("MarksFixedE135")) {
  1233. return true;
  1234. }
  1235. else if ( !(node.tagName.match(/FONT/)) ) {
  1236. return false;
  1237. }
  1238. if (debug_re_to_check===true && (node.innerHTML.match(re_to_check))) {console.log("Checking node: "+node.nodeName+"."+node.className+"@"+node.parentNode.nodeName+":: "+node.innerHTML.slice(0,216));}
  1239. var tabooedTags=SkippedTagsForMarks;
  1240. var child=node.firstChild;
  1241. var currHTML="";
  1242. var allSubSafe=true;
  1243. var node2fix=true;
  1244. if ((node.nodeName.match(tabooedTags)) || inTheClassOf(node,preOrigPunctSpaceList)) {
  1245. //Although BODY is tabooed, this is OK because a loop is outside this recursive implementation.
  1246. node.classList.remove("Safe2FixCJK\uE000");
  1247. node.classList.remove("PunctSpace2Fix");
  1248. node.classList.add("MarksFixedE135");
  1249. return false;
  1250. }
  1251. //Add lang attibute. Firefox cannot detect lang=zh automatically and it will treat CJK characters as letters if no lang=zh. For example,
  1252. //the blank spaces will be streched but not the "character-spacing" if using align=justify.
  1253. if (window.getComputedStyle(node.parentNode,null).getPropertyValue('text-align').match(/start/) && useJustify===true) {
  1254. node.parentNode.style.textAlign="justify";
  1255. }
  1256. //node.lang="zh";
  1257. node.parentNode.lang="zh";
  1258. while (child) {
  1259. if (debug_re_to_check===true && (node.innerHTML.match(re_to_check))) {console.log("Checking subnode: "+child+"@"+node.nodeName);}
  1260. if ( child.nodeType === 3 && !(node.nodeName.match(tabooedTags)) ) {
  1261. if (debug_re_to_check===true && (node.innerHTML.match(re_to_check))) {console.log("Found as Type 3 subnode: "+child.nodeName+"."+child.className+"@"+node.nodeName+":: "+child.data);}
  1262. if (debug_verbose===true) {
  1263. console.log("Permitted to check: "+node.nodeName+"."+node.className);
  1264. }
  1265. if (debug_re_to_check===true && (node.innerHTML.match(re_to_check)) && node.nodeName.match(tabooedTags)) {
  1266. console.log("ERROR: Wrong Operation on: "+node.nodeName+"."+node.className+":: "+node.textContent);
  1267. console.log("ERROR: Wrong Operation because: "+child.data);
  1268. }
  1269. }
  1270. if (child.nodeType===1 && !(child instanceof SVGElement)) {
  1271. if ((child.nodeName.match(tabooedTags) ) || inTheClassOf(child,preOrigPunctSpaceList) ) {
  1272. //was like this: if (child.nodeName.match(tabooedTags) || child.classList.contains("MarksFixedE135")) {. I don't know why.
  1273. child.classList.remove("Safe2FixCJK\uE000");
  1274. child.classList.remove("CJK2Fix");
  1275. child.classList.add("MarksFixedE135");
  1276. node2fix=false;
  1277. }
  1278. else if (child.nodeName.match(ignoredTags)) {
  1279. //Simply do nothing. Such as <math> tag.
  1280. child.classList.add("Safe2FixCJK\uE000");
  1281. child.classList.add("MarksFixedE135");
  1282. }
  1283. else if (child.classList.contains("MarksFixedE135")) {
  1284. //Fixed, do nothing.
  1285. }
  1286. else if (child.nodeName.match(/FONT/)) {
  1287. FixPunctRecursion(child); //This is the recursion part. Not really recursion after 0.15....
  1288. }
  1289. else {
  1290. //do nothing;
  1291. }
  1292. //Test again after fixing child:
  1293. if (!(child.classList.contains("Safe2FixCJK\uE000"))) {allSubSafe=false;} //\uE000 is Tux in Linux Libertine.
  1294. }
  1295. child=child.nextSibling;
  1296. }
  1297. if (allSubSafe===true && (!(node instanceof SVGElement))) {
  1298. var orig_class=node.className;
  1299. var CJKclasses=CJKclassList.split(',');
  1300. for (var icl=0;icl<CJKclasses.length;icl++) {
  1301. node.classList.remove(CJKclasses[icl]);
  1302. }
  1303. if (node.classList.length===0 && node.id.length ===0 && !(node.nodeName.match(tabooedTags)) && !(inTheClassOf(node,preOrigPunctSpaceList))) {
  1304. //It would be crazy if add listeners just by tags.
  1305. node.className=orig_class;
  1306. node.classList.add("Safe2FixCJK\uE000");
  1307. }
  1308. else {
  1309. node.className=orig_class;
  1310. }
  1311. }
  1312. //Config and Filtering Done. Fix puncts if necessary.
  1313. if (allSubSafe===true && node2fix===true && !(node.nodeName.match(tabooedTags)) && !(inTheClassOf(node,preOrigPunctSpaceList)) && node.classList.contains("CJK2Fix") && !(node.classList.contains("MarksFixedE135"))) {
  1314. if (debug_verbose===true) console.log("USING Recursion: "+node.nodeName+'.'+node.className);
  1315. if (debug_verbose===true) { console.log("WARNING: Danger Operation on: "+node.nodeName+"."+node.className+":: "+node.innerHTML.slice(0,216)); }
  1316. if (debug_re_to_check===true && (node.innerHTML.match(re_to_check))) {console.log("Checking if contain punctuations to fix");}
  1317. if (node.innerHTML.match(/[“”‘’、,。:;!?)】〉》」』『「《〈【(]/m)) {
  1318. if (debug_re_to_check===true && (node.innerHTML.match(re_to_check))) { console.log("WARNING: Danger Operation on: "+node.nodeName+"."+node.className);}
  1319. if (node.classList.contains("preCode")) {
  1320. node.classList.remove("Safe2FixCJK\uE000"); //Do not performan fixing on "fully banned" tags.
  1321. node.classList.remove("PunctSpace2Fix");
  1322. }
  1323. else if (window.getComputedStyle(node, null).getPropertyValue("white-space").match(/pre/)){
  1324. node.innerHTML=FixMarksInCurrHTML(node.innerHTML,node,false);
  1325. }
  1326. else {
  1327. if (debug_re_to_check===true && (node.innerHTML.match(re_to_check))) {console.log("Now fixing --> "+node.nodeName+"."+node.className+":: "+node.innerHTML.slice(0,216));}
  1328. node.innerHTML=FixMarksInCurrHTML(node.innerHTML,node,true);
  1329. }
  1330. }
  1331. node.classList.add("MarksFixedE135");
  1332. return true;
  1333. }
  1334. else {
  1335. node.classList.add("MarksFixedE135");
  1336. return true;
  1337. }
  1338. }
  1339. ///==Fix punct in a currHTML===///
  1340. function FixMarksInCurrHTML(currHTML,node,delete_all_extra_spaces) {
  1341. //“<-->\u201C, ”<-->\u201D
  1342. //‘<-->\u2018, ’<-->\u2019
  1343. if (debug_04===true) console.log("Round 4: Fixing node <"+node.nodeName+">");
  1344. var changhai_style=false;
  1345. var Squeezing=true;
  1346. var SqueezeInd=true;
  1347. var tmp_str='';
  1348. var FixMarks_start=performance.now();
  1349. var inputHTML=currHTML;
  1350. var continuedHTML='';
  1351. if (changhai_style===true) {
  1352. //Simply inserting blanck space, like changhai.org.
  1353. currHTML=currHTML.replace(/([\u3000-\u303F\u3400-\u9FBF\uFF00-\uFFEF]?)([“‘])([\u3400-\u9FBF\u3000-\u303F\uFF00-\uFFEF]+)/g,'$1 $2$3');
  1354. currHTML=currHTML.replace(/([\u3000-\u303F\u3400-\u9FBF\uFF00-\uFFEF])([”’])([^,, ])/g,'$1$2 $3');
  1355. if (debug_04===true) {console.log(currHTML);}
  1356. node.innerHTML=currHTML;
  1357. return true;
  1358. }
  1359. //currHTML=currHTML.replace(/^([、,。:;!?)】〉》」』\u201D\u2019])/mg,'\u2060$1');//Remove the hanging puncts. Seems no use at all.
  1360. //It seems that I always needs to consider the context.
  1361. if ( currHTML.match(/[“”‘’]/) || currHTML.match(/^[、,。:;!?)】〉》」』『「《〈【(]/) || currHTML.match(/[、,。:;!?)】〉》」』『「《〈【(]$/) ) {
  1362. if (debug_tagSeeThrough===true) console.log("TO CHECK BEFORE: "+currHTML);
  1363. if (debug_tagSeeThrough===true) console.log("FULL PARENT: "+node.parentNode.textContent);
  1364. currHTML=getBefore(node)+'\uF201CJK\uF201'+currHTML+'\uF202CJK\uF202'+getAfter(node);
  1365. continuedHTML=currHTML;
  1366. if (debug_tagSeeThrough===true) console.log("FULL CLOSED FORM: "+currHTML);
  1367. if (debug_tagSeeThrough===true) console.log("Continuation took "+(performance.now()-FixMarks_start).toFixed(1)+" ms.");
  1368. }
  1369. if (useFeedback===true && currHTML.match(/[\uF201-\uF204]/)) {
  1370. if (!currHTML.match(/[\uF201-\uF204]CJK[\uF201-\uF204]/) || (currHTML.match(/[\uF201-\uF204]/g)).length !== ((currHTML.match(/[\uF201-\uF204]CJK[\uF201-\uF204]/g)).length*2)) {
  1371. alert("This page may conflict with FixCJK!. If you would like to help solve the problem, please email the URL to stecue@gmail.com. Thanks!\n本页面可能与FixCJK!冲突,如果您想帮忙解决此问题,请将本页面的URL电邮至stecue@gmail.com。多谢您的使用与反馈!");
  1372. }
  1373. }
  1374. function getBefore(child) {
  1375. var t_start=performance.now();
  1376. var toReturn='';
  1377. var inputNode=child;
  1378. if (debug_getBeforeTags===true) {
  1379. console.log("CURRENT: "+child.nodeName+"@<"+child.parentNode.nodeName+">");
  1380. console.log(child.parentNode.nodeName.match(upEnoughTags));
  1381. }
  1382. if (debug_tagSeeThrough===true) console.log('CHILD<'+child.nodeName+'>: '+child.textContent+'<--'+'PARENT<'+child.parentNode.nodeName+">: "+child.parentNode.textContent);
  1383. child=child.previousSibling;
  1384. while (child && (performance.now()-t_start<2) ) {
  1385. if (child.nodeType===3) {
  1386. if (debug_tagSeeThrough===true) console.log("T3: "+child.data);
  1387. toReturn = child.data + toReturn;
  1388. if (toReturn.length>1024 || toReturn.match(/[\u3400-\u9FBF]/) ) {
  1389. //Stop if to Return is already too long;
  1390. return toReturn;
  1391. }
  1392. }
  1393. else if (child.nodeType===1 && window.getComputedStyle(child,null).getPropertyValue("display")!=="none" ) {
  1394. if (child.nodeName.match(stopTags) || inTheClassOf(child,stopClasses) ) {
  1395. return '上下标'+toReturn;
  1396. }
  1397. if (debug_tagSeeThrough===true) console.log("T1: "+child.textContent);
  1398. toReturn = displayedText(child) + toReturn;
  1399. if (toReturn.length>1024 || toReturn.match(/[\u3400-\u9FBF]/) ) {
  1400. //Stop if to Return is already too long;
  1401. return toReturn;
  1402. }
  1403. }
  1404. child=child.previousSibling;
  1405. }
  1406. if (debug_tagSeeThrough===true) console.log("BEFORE: "+toReturn+"@"+performance.now());
  1407. if (debug_tagSeeThrough===true) console.log("CJK? "+toReturn.match(/[\u3400-\u9FBF]/)+'@'+toReturn);
  1408. if ((toReturn.length < 1 || !toReturn.match(/[\u3400-\u9FBF]/)) && (!inputNode.parentNode.nodeName.match(upEnoughTags)) ) {
  1409. return getBefore(inputNode.parentNode)+toReturn;
  1410. }
  1411. return (toReturn.replace(/</,'&lt;')).replace(/>/,'&gt;');
  1412. }
  1413. function getAfter(child) {
  1414. var toReturn='';
  1415. var t_start=performance.now();
  1416. var inputNode=child;
  1417. child=child.nextSibling;
  1418. while (child && (performance.now()-t_start<2) ) {
  1419. if (child.nodeType===3) {
  1420. toReturn = toReturn + child.data;
  1421. if (toReturn.length>1024 || toReturn.match(/[\u3400-\u9FBF]/) ) {
  1422. //Stop if to Return is already too long;
  1423. return toReturn;
  1424. }
  1425. }
  1426. else if (child.nodeType===1 && window.getComputedStyle(child,null).getPropertyValue("display")!=="none" ) {
  1427. if (child.nodeName.match(stopTags) || inTheClassOf(child,stopClasses) ) {
  1428. return toReturn+'上下标'; //I just need to add some CJK text. They will be "chopped" anyway.
  1429. }
  1430. toReturn = toReturn + displayedText(child);
  1431. if (toReturn.length>1024 || toReturn.match(/[\u3400-\u9FBF]/) ) {
  1432. //Stop if to Return is already too long;
  1433. return toReturn;
  1434. }
  1435. }
  1436. child=child.nextSibling;
  1437. }
  1438. if (debug_tagSeeThrough===true) console.log("AFTER: "+toReturn+"@"+performance.now());
  1439. if ((toReturn.length < 1 || !toReturn.match(/[\u3400-\u9FBF]/)) && (!inputNode.parentNode.nodeName.match(upEnoughTags)) ) {
  1440. return toReturn+getAfter(inputNode.parentNode);
  1441. }
  1442. return (toReturn.replace(/</,'&lt;')).replace(/>/,'&gt;');
  1443. }
  1444. //==We need to protect the quotation marks within tags first===//
  1445. // \uE862,\uE863 <==> ‘,’
  1446. // \uE972,\uE973 <==> “,”
  1447. while (currHTML.match(/<[^>]*[“”‘’、,。:;!?)】〉》」』『「《〈【(][^<]*>/m)) {
  1448. currHTML=currHTML.replace(/(<[^>]*)‘([^<]*>)/mg,'$1\uE862$2');
  1449. currHTML=currHTML.replace(/(<[^>]*)’([^<]*>)/mg,'$1\uE863$2');
  1450. currHTML=currHTML.replace(/(<[^>]*)“([^<]*>)/mg,'$1\uE972$2');
  1451. currHTML=currHTML.replace(/(<[^>]*)”([^<]*>)/mg,'$1\uE973$2');
  1452. currHTML=currHTML.replace(/(<[^>]*)、([^<]*>)/mg,'$1\uEA01$2');
  1453. currHTML=currHTML.replace(/(<[^>]*),([^<]*>)/mg,'$1\uEA02$2');
  1454. currHTML=currHTML.replace(/(<[^>]*)。([^<]*>)/mg,'$1\uEA03$2');
  1455. currHTML=currHTML.replace(/(<[^>]*):([^<]*>)/mg,'$1\uEA04$2');
  1456. currHTML=currHTML.replace(/(<[^>]*);([^<]*>)/mg,'$1\uEA05$2');
  1457. currHTML=currHTML.replace(/(<[^>]*)!([^<]*>)/mg,'$1\uEA06$2');
  1458. currHTML=currHTML.replace(/(<[^>]*)?([^<]*>)/mg,'$1\uEA07$2');
  1459. currHTML=currHTML.replace(/(<[^>]*))([^<]*>)/mg,'$1\uEA08$2');
  1460. currHTML=currHTML.replace(/(<[^>]*)】([^<]*>)/mg,'$1\uEA09$2');
  1461. currHTML=currHTML.replace(/(<[^>]*)〉([^<]*>)/mg,'$1\uEA10$2');
  1462. currHTML=currHTML.replace(/(<[^>]*)》([^<]*>)/mg,'$1\uEA11$2');
  1463. currHTML=currHTML.replace(/(<[^>]*)」([^<]*>)/mg,'$1\uEA12$2');
  1464. currHTML=currHTML.replace(/(<[^>]*)』([^<]*>)/mg,'$1\uEA13$2');
  1465. currHTML=currHTML.replace(/(<[^>]*)『([^<]*>)/mg,'$1\uEA14$2');
  1466. currHTML=currHTML.replace(/(<[^>]*)「([^<]*>)/mg,'$1\uEA15$2');
  1467. currHTML=currHTML.replace(/(<[^>]*)《([^<]*>)/mg,'$1\uEA16$2');
  1468. currHTML=currHTML.replace(/(<[^>]*)〈([^<]*>)/mg,'$1\uEA17$2');
  1469. currHTML=currHTML.replace(/(<[^>]*)【([^<]*>)/mg,'$1\uEA18$2');
  1470. currHTML=currHTML.replace(/(<[^>]*)(([^<]*>)/mg,'$1\uEA19$2');
  1471. }
  1472. var time2protect=performance.now()-FixMarks_start;
  1473. //Now let's fix the punctions.
  1474. //First we need to fix the "reverse-paired" punctuations.
  1475. var fixpair=false; //the current code has problems if unpaired quotation marks are present.
  1476. var fixpair_timeout = noBonusTimeout; //Don't spend too much time on this "bonus" function.
  1477. var fixpair_start=performance.now();
  1478. if ( currHTML.length > noBonusLength ) {fixpair=false;}
  1479. if (debug_re_to_check===true && (currHTML.match(re_to_check))) {console.log("Reversing "+currHTML);}
  1480. if (fixpair===true) { //[\w,./<>?;:[]\{}|`~!@#$%^&*()_+-=]*
  1481. var revpaired=/(^[^\u201C\u201D]?(?:[^\u201C\u201D]*\u201C[^\u201C\u201D]*\u201D)*[^\u201C\u201D]*)\u201D([^\u201C\u201D]{2,})\u201C/;
  1482. while (currHTML.match(revpaired) && (performance.now()-fixpair_start)<fixpair_timeout ) {
  1483. if (debug_re_to_check===true && currHTML.match(re_to_check)) {console.log("Pair reversed: "+(performance.now()-t_start).toString());}
  1484. currHTML=currHTML.replace(revpaired,'$1\u201C$2\u201D');
  1485. }
  1486. }
  1487. var fixpair_stop=performance.now()-fixpair_start;
  1488. var paired_start=performance.now();
  1489. //Find and preserve paired Latin marks.
  1490. var paired=/(\u201C)([^\u3000-\u303F\u3400-\u9FBF\uE000-\uED00\uFF00-\uFFEF]*)(\u201D)/mg;
  1491. while (currHTML.match(paired)) {
  1492. if (debug_re_to_check===true && currHTML.match(re_to_check)) console.log("Quotation mark pair found@"+currHTML);
  1493. currHTML=currHTML.replace(paired,'\uEC1C$2\uEC1D');
  1494. }
  1495. //Paired single quotations are diffcult because of the double-indentiy of '‘'.
  1496. paired=/(\u2018)([^\u3000-\u303F\u3400-\u9FBF\uE000-\uED00\uFF00-\uFFEF]{0,2})(\u2019)/mg;
  1497. while (currHTML.match(paired)) {
  1498. if (debug_re_to_check===true && currHTML.match(re_to_check)) console.log("Quotation mark pair found@"+currHTML);
  1499. currHTML=currHTML.replace(paired,'\uEC18$2\uEC19');
  1500. }
  1501. //Find paired CJK marks. Seems like O(n^2) without the "g" modifier?
  1502. paired=/(\u201C)([^\u201D]*[\u3000-\u303F\u3400-\u9FBF\uFF00-\uFFEF][^\u201D]*)(\u201D)/mg;
  1503. while (currHTML.match(paired)) {
  1504. currHTML=currHTML.replace(paired,'\uEB1C$2\uEB1D');
  1505. }
  1506. var paired_stop=performance.now()-paired_start;
  1507. //"unpaired \u201C or \u201D", not just use at the beginning of a paragraph.
  1508. var unpaired_timeout = noBonusTimeout; //not so important, therefore cannot spend too much time here.
  1509. var unpaired_start=performance.now();
  1510. var unpaired=/\u201C((?:[\uF201-\uF204]CJK[\uF201-\uF204])?[^\u201D\u3400-\u9FBF\uF201-\uF204]{0,3}[\u3000-\u303F\u3400-\u9FBF\uFF00-\uFFEF][^\u201C\u201D]*$)/m;
  1511. while ( currHTML.length< noBonusLength && currHTML.match(unpaired) && (performance.now()-unpaired_start)<unpaired_timeout) {
  1512. currHTML=currHTML.replace(unpaired,'\uEB1C$1'); //We need the greedy method to get the longest match.
  1513. }
  1514. unpaired=/(^[^\u201C\u201D]*[\u3000-\u303F\u3400-\u9FBF\uFF00-\uFFEF](?:[\uF201-\uF204]CJK[\uF201-\uF204])?[^\u201D\u3400-\u9FBF\uF201-\uF204]{0,3}(?:[\uF201-\uF204]CJK[\uF201-\uF204])?)\u201D/m;
  1515. while ( currHTML.length< noBonusLength && currHTML.match(unpaired) && (performance.now()-unpaired_start)<unpaired_timeout) {
  1516. currHTML=currHTML.replace(unpaired,'$1\uEB1D'); //We need the greedy method to get the longest match.
  1517. }
  1518. //For single quotations:
  1519. var paired_single_start=performance.now();
  1520. paired=/(\u2018)([^\u2019]*[\u3000-\u303F\u3400-\u9FBF\uFF00-\uFFEF][^\u2019]*)(\u2019)/mg;
  1521. while (currHTML.match(paired)) {
  1522. currHTML=currHTML.replace(paired,'\uEB18$2\uEB19');
  1523. }
  1524. var paired_single_stop=performance.now()-paired_single_start;
  1525. //"unpaired ‘ (\u2018)", not just use at the beginning of a paragraph.
  1526. unpaired_start=performance.now();
  1527. unpaired=/\u2018((?:[\uF201-\uF204]CJK[\uF201-\uF204])?[^\u201D\u3400-\u9FBF\uF201-\uF204]{0,3}(?:[\uF201-\uF204]CJK[\uF201-\uF204])?[\u3000-\u303F\u3400-\u9FBF\uFF00-\uFFEF][^\u2018\u2019]*$)/m;
  1528. while ( currHTML.length< noBonusLength && currHTML.match(unpaired) && (performance.now()-unpaired_start)<unpaired_timeout) {
  1529. currHTML=currHTML.replace(unpaired,'\uEB18$1'); //We need the greedy method to get the longest match.
  1530. }
  1531. //CJK’, otherwise words like it's might be affected.
  1532. unpaired=/(^[^\u2018\u2019]*[\u3000-\u303F\u3400-\u9FBF\uFF00-\uFFEF](?:[\uF201-\uF204]CJK[\uF201-\uF204])?)\u2019/m;
  1533. while ( currHTML.length< noBonusLength && currHTML.match(unpaired) && (performance.now()-unpaired_start)<unpaired_timeout) {
  1534. currHTML=currHTML.replace(unpaired,'$1\uEB19'); //We need the greedy method to get the longest match.
  1535. }
  1536. ///=== Unicode Shifting Ends ===///
  1537. var time2shift=performance.now()-FixMarks_start-time2protect;
  1538. //Remove extra spaces if necessary
  1539. if (delete_all_extra_spaces===true) {
  1540. //For changhai.org and similar sites.
  1541. currHTML=currHTML.replace(/&nbsp;/gi,'\u00A0');
  1542. currHTML=currHTML.replace(/([、,。:;!?)】〉》」』\uEB1D\uEB19]+)(?:[\r\n\u0020\u00A0]|&nbsp;){0,2}/g,'$1');
  1543. //'>' means there is a non-CJK tag(?)
  1544. currHTML=currHTML.replace(/([^\s\u00A0>])(?:[\r\n\u0020\u00A0]|&nbsp;){0,2}([『「《〈【(\uEB1C\uEB18]+)/g,'$1$2');
  1545. }
  1546. else {
  1547. //Delete at most 1 spaces before and after because of the wider CJK marks.
  1548. currHTML=currHTML.replace(/([\uEB1D\uEB19])[ ]?/mg,'$1');
  1549. currHTML=currHTML.replace(/[ ]?([\uEB1C\uEB18])/mg,'$1');
  1550. }
  1551. ///--Group Left: [、,。:;!?)】〉》」』\uEB1D\uEB19] //Occupies the left half width.
  1552. ///--Group Right:[『「《〈【(\uEB1C\uEB18] //Occupies the right half width.
  1553. ///=====Use \uE211 as the calss name for TWO-PUNCT RULES====//
  1554. ///===Do not use the "g" modefier because we are using loops===//
  1555. var reLL=/([\n]?[、,。:;!?)】〉》」』\uEB1D\uEB19][\n]?)([\uF201-\uF204]CJK[\uF201-\uF204])?([、,。:;!?)】〉》」』\uEB1D\uEB19])/m;
  1556. var reLR=/([\n]?[、,。:;!?)】〉》」』\uEB1D\uEB19][\n]?)([\uF201-\uF204]CJK[\uF201-\uF204])?([『「《〈【(\uEB1C\uEB18])/m;
  1557. var reRR=/([\n]?[『「《〈【(\uEB1C\uEB18][\n]?)([\uF201-\uF204]CJK[\uF201-\uF204])?([『「《〈【(\uEB1C\uEB18])/m;
  1558. var reRL=/([\n]?[『「《〈【(\uEB1C\uEB18][\n]?)([\uF201-\uF204]CJK[\uF201-\uF204])?([、,。:;!?)】〉》」』\uEB1D\uEB19])/m;
  1559. var sqz_start=performance.now();
  1560. while (currHTML.match(/(?:[、,。:;!?)】〉》」』\uEB1D\uEB19『「《〈【(\uEB1C\uEB18]([\uF201-\uF204]CJK[\uF201-\uF204])?){2,}/m) && (performance.now()-sqz_start)<sqz_timeout) {
  1561. if (currHTML.match(reLL)) {
  1562. //--TWO PUNCTS: {Left}{Left}--//
  1563. tmp_str='<span class="CJKTestedAndLabeled MarksFixedE135 \uE211" style="display:inline;padding-left:0px;padding-right:0px;float:none;letter-spacing:'+kern_consec_ll+';">$1</span>$2$3';
  1564. currHTML=currHTML.replace(reLL,tmp_str);
  1565. }
  1566. else if (currHTML.match(reLR)) {
  1567. //--TWO PUNCTS: {Left}{Right}--//
  1568. tmp_str='<span class="CJKTestedAndLabeled MarksFixedE135 \uE211" style="display:inline;padding-left:0px;padding-right:0px;float:none;letter-spacing:'+kern_consec_lr+';">$1</span>$2$3';
  1569. currHTML=currHTML.replace(reLR,tmp_str);
  1570. }
  1571. else if (currHTML.match(reRR)) {
  1572. //--TWO PUNCTS: {Right}{Right}--//
  1573. tmp_str='<span class="CJKTestedAndLabeled MarksFixedE135 \uE211" style="display:inline;padding-left:0px;padding-right:0px;float:none;letter-spacing:'+kern_consec_rr+';">$1</span>$2$3';
  1574. currHTML=currHTML.replace(reRR,tmp_str);
  1575. }
  1576. else if (currHTML.match(reRL)) {
  1577. //--TWO PUNCTS: no letter-spacing adjustment for {Right}-{Left}, just a "fake" element--//
  1578. currHTML=currHTML.replace(reRL,'$1<span class="CJKTestedAndLabeled MarksFixedE135 \uE211" style="display:inline;padding-left:0px;padding-right:0px;float:none;"></span>$2$3');
  1579. }
  1580. else {
  1581. console.log("FIXME: current combination of punctuations has not been considered!");
  1582. break;
  1583. }
  1584. }
  1585. ///---Done with conseqtive puncts--///
  1586. if (debug_04===true) {node.style.color="Pink";}
  1587. var SqueezeFirst=false;
  1588. if (SqueezeInd===true) {
  1589. //The punctuation marks is also the first char in a paragraph seems:
  1590. //In current model (1.0.x), all tags are added within this function (FixMarksInCurrHTML), and it should be safe to skip them.
  1591. //Seems no need to squeeze the puncts at the beginning of a paragraph. It may cause format changes.
  1592. if (SqueezeFirst===true) {
  1593. currHTML=currHTML.replace(/^([\uF201-\uF204]CJK[\uF201-\uF204])?(<[^><]*>)*([『「《〈【(\uEB1C\uEB18])/mg,'$1$2<span class="CJKTestedAndLabeled MarksFixedE135 \uE211" style="display:inline;padding-left:0px;padding-right:0px;float:none;margin-left:'+kern_ind_open+';">$3</span>');
  1594. }
  1595. //Then, not the first char. The re_ex also covers the first punct of a serise of puncts.
  1596. currHTML=currHTML.replace(/([^\n><、,。:;!?)】〉》」』\uEB1D\uEB19『「《〈【(\uEB1C\uEB18\uF201-\uF204])((?:<[^><]*>)*(?:[\uF201-\uF204]CJK[\uF201-\uF204])?(?:<[^><]*>)*)([『「《〈【(\uEB1C\uEB18])((?:<[^><]*>)*(?:[\uF201-\uF204]CJK[\uF201-\uF204])?(?:<[^><]*>)*(?:[\uF201-\uF204]CJK[\uF201-\uF204])?)([^><\uF201-\uF204]|$)/mg,'$1$2<span class="CJKTestedAndLabeled MarksFixedE135 \uE211" style="display:inline;padding-left:0px;padding-right:0px;float:none;margin-left:'+kern_ind_open+';">$3</span>$4$5');
  1597. //Do not squeeze the last punctuation marks in a paragraph. Too risky. $3 seems necessary?
  1598. currHTML=currHTML.replace(/([、,。:;!?)】〉》」』\uEB1D\uEB19])([\uF201-\uF204]CJK[\uF201-\uF204])?$/mg,'<span class="CJKTestedAndLabeled MarksFixedE135 \uE211" style="display:inline;padding-left:0px;padding-right:0px;float:none;margin-right:0px">$1</span>$2');
  1599. //Note that [\uF201-\uF204]CJK[\uF201-\uF204] might be added to the end and it must be excluded.
  1600. currHTML=currHTML.replace(/([、,。:;!?)】〉》」』\uEB1D\uEB19])([\uF201-\uF204]CJK[\uF201-\uF204])?((?:<[^><]*>)+[^><\n\uF201-\uF204、,。:;!?)】〉》」』\uEB1D\uEB19『「《〈【(\uEB1C\uEB18]|[^><\n\uF201-\uF204、,。:;!?)】〉》」』\uEB1D\uEB19『「《〈【(\uEB1C\uEB18])/mg,'<span class="CJKTestedAndLabeled MarksFixedE135 \uE211" style="display:inline;padding-left:0px;padding-right:0px;float:none;margin-right:'+kern_ind_close+';">$1</span>$2$3');
  1601. }
  1602. ///=== Squeezing Ends ===///
  1603. var time2squeeze=performance.now()-FixMarks_start-time2shift-time2protect;
  1604. ///=== Change the protected punctuations in tags back==///
  1605. currHTML=currHTML.replace(/\uE862/mg,'\u2018');
  1606. currHTML=currHTML.replace(/\uE863/mg,'\u2019');
  1607. currHTML=currHTML.replace(/\uE972/mg,'\u201C');
  1608. currHTML=currHTML.replace(/\uE973/mg,'\u201D');
  1609. currHTML=currHTML.replace(/\uEA01/mg,'、');
  1610. currHTML=currHTML.replace(/\uEA02/mg,',');
  1611. currHTML=currHTML.replace(/\uEA03/mg,'。');
  1612. currHTML=currHTML.replace(/\uEA04/mg,':');
  1613. currHTML=currHTML.replace(/\uEA05/mg,';');
  1614. currHTML=currHTML.replace(/\uEA06/mg,'!');
  1615. currHTML=currHTML.replace(/\uEA07/mg,'?');
  1616. currHTML=currHTML.replace(/\uEA08/mg,')');
  1617. currHTML=currHTML.replace(/\uEA09/mg,'】');
  1618. currHTML=currHTML.replace(/\uEA10/mg,'〉');
  1619. currHTML=currHTML.replace(/\uEA11/mg,'》');
  1620. currHTML=currHTML.replace(/\uEA12/mg,'」');
  1621. currHTML=currHTML.replace(/\uEA13/mg,'』');
  1622. currHTML=currHTML.replace(/\uEA14/mg,'『');
  1623. currHTML=currHTML.replace(/\uEA15/mg,'「');
  1624. currHTML=currHTML.replace(/\uEA16/mg,'《');
  1625. currHTML=currHTML.replace(/\uEA17/mg,'〈');
  1626. currHTML=currHTML.replace(/\uEA18/mg,'【');
  1627. currHTML=currHTML.replace(/\uEA19/mg,'(');
  1628. ///////==== Change quotation marks back =====/////
  1629. currHTML=currHTML.replace(/\uEC18/mg,'\u2018');
  1630. currHTML=currHTML.replace(/\uEC19/mg,'\u2019');
  1631. currHTML=currHTML.replace(/\uEC1C/mg,'\u201C');
  1632. currHTML=currHTML.replace(/\uEC1D/mg,'\u201D');
  1633. //Use '\uE985' as the class of CJK quotation marks.
  1634. currHTML=currHTML.replace(/\uEB1C/mg,'<span class="CJKTestedAndLabeled MarksFixedE135 \uE985" style="display:inline;padding-left:0px;padding-right:0px;float:none;font-family:'+dequote(CJKPunct)+';">\u201C</span>');
  1635. currHTML=currHTML.replace(/\uEB1D/mg,'<span class="CJKTestedAndLabeled MarksFixedE135 \uE985" style="display:inline;padding-left:0px;padding-right:0px;float:none;font-family:'+dequote(CJKPunct)+';">\u201D</span>');
  1636. currHTML=currHTML.replace(/\uEB18/mg,'<span class="CJKTestedAndLabeled MarksFixedE135 \uE985" style="display:inline;padding-left:0px;padding-right:0px;float:none;font-family:'+dequote(CJKPunct)+';">\u2018</span>');
  1637. currHTML=currHTML.replace(/\uEB19/mg,'<span class="CJKTestedAndLabeled MarksFixedE135 \uE985" style="display:inline;padding-left:0px;padding-right:0px;float:none;font-family:'+dequote(CJKPunct)+';">\u2019</span>');
  1638. ///=== Replacing and Restoring Ends ===///
  1639. var time2replace=performance.now()-FixMarks_start-time2squeeze-time2shift-time2protect;
  1640. if ( (performance.now()-FixMarks_start)>200 ) {
  1641. console.log("FIXME: String Operation Too Slow: "+(performance.now()-FixMarks_start).toFixed(0)+" ms.");
  1642. console.log("Protect: "+time2protect.toFixed(0)+" ms.");
  1643. console.log("Shift: "+time2shift.toFixed(0)+" ms.");
  1644. console.log(" ----->rev: "+fixpair_stop.toFixed(0)+" ms.");
  1645. console.log(" ----->\u201C,\u201D: "+paired_stop.toFixed(0)+" ms.");
  1646. console.log(" ----->\u2018,\u2019: "+paired_single_stop.toFixed(0)+" ms.");
  1647. console.log("Squeeze: "+time2squeeze.toFixed(0)+" ms.");
  1648. console.log("Replace: "+time2replace.toFixed(0)+" ms.");
  1649. console.log("String(Length): "+currHTML.slice(0,216)+"...("+currHTML.length+")");
  1650. console.log('Input As:\n'+inputHTML+'\n@<'+node.nodeName+'>');
  1651. }
  1652. if (debug_tagSeeThrough===true) {console.log("FIXED: "+currHTML+"@"+performance.now());}
  1653. currHTML=currHTML.replace(/^[^\u0000]*\uF201CJK\uF201([^\u0000]*)$/,'$1');
  1654. currHTML=currHTML.replace(/^([^\u0000]*)\uF202CJK\uF202[^\u0000]*$/,'$1');
  1655. if (debug_tagSeeThrough===true) console.log("AFTER TRIMMED:(@"+(performance.now()-FixMarks_start).toFixed(1)+" ms): "+node.nodeName+"#"+node.id+"::"+currHTML);
  1656. if ( (performance.now()-FixMarks_start)>10 ) {
  1657. console.log("Warning: Slow ("+(performance.now()-FixMarks_start).toFixed(1)+" ms) at <"+node.nodeName+">@<"+node.parentNode.nodeName+">: "+node.parentNode.innerHTML);
  1658. console.log("The Continued HTML is:\n"+continuedHTML);
  1659. }
  1660. return currHTML;
  1661. }
  1662. function displayedText(node) {
  1663. var child=node.firstChild;
  1664. var toReturn='';
  1665. while (child) {
  1666. if (child.nodeType===3) {
  1667. toReturn=toReturn+child.data;
  1668. }
  1669. else if (child.nodeType===1 && (window.getComputedStyle(child,null).getPropertyValue("display")!=='none') ) {
  1670. toReturn=toReturn+displayedText(child);
  1671. }
  1672. child=child.nextSibling;
  1673. }
  1674. return toReturn;
  1675. }
  1676. }
  1677. ) ();