“搞定”CJK!

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

  1. // ==UserScript==
  2. // @name FixCJK!
  3. // @name:zh-CN “搞定”CJK!
  4. // @namespace https://github.com/stecue/fixcjk
  5. // @version 1.3.9
  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. // @exclude https://*jsfiddle.net*/*
  13. // @exclude http://*stackexchange.com/*
  14. // @exclude http://*mathoverflow.net/*
  15. // @grant GM_addStyle
  16. // ==/UserScript==
  17. (function () {
  18. 'use strict';
  19. // You can change the the following fonts/settings until the "var FixPunct=" line.
  20. ///--CJK Fonts--///
  21. var CJKdefault = '"Microsoft YaHei",SimSun,Source Han Sans SC,Noto Sans CJK SC,"WenQuanYi Zen Hei Sharp","WenQuanYi Micro Hei"'; //The default CJK font if no sans or serif is specified. Regular weight.
  22. var CJKSimSun= '"Microsoft YaHei","Source Han Serif SC","Source Han Serif CN","Note Serif CJK SC","WenQuanYi Micro Hei"'; //Fonts to replace SimSun;
  23. var CJKserif = '"Microsoft YaHei","Source Han Serif SC","Source Han Serif CN","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.
  24. var CJKsans = '"Microsoft YaHei","Source Han Sans SC","Source Han Sans CN","Noto Sans CJK SC","Noto Sans CJK SC Regular"'; //Sans-serif fonts for CJK. Regular weight.
  25. var CJKBold = '"Microsoft YaHei","Noto Sans CJK SC Bold","Noto Sans CJK SC","Source Han Sans SC Bold","Source Han Sans SC Bold","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.
  26. var CJKPunct = 'Noto Sans CJK SC,Noto Serif CJK SC,Source Han Sans SC,Source Han Serif SC,Source Han Sans CN,Source Han Serif CN,SimHei,SimSun'; //The font to use for CJK quotation marks.
  27. var KanaSerif = 'Source Han Serif SC,Noto Serif CJK SC'; //The serif fonts for kana (假名) if no lang=ja is set.
  28. var KanaSans = 'Source Han Sans SC,Noto Sans CJK SC'; //The sans fonts for kana (假名) if no lang=ja is set.
  29. var JaSerif = 'Noto Serif CJK JP,Source Han Serif,Source Han Serif JP,Noto Serif CJK SC,Source Han Serif SC,MS Mincho'; //Used in lang=ja elements only. KanaSans will be overrided.
  30. var JaSans = 'Noto Sans CJK JP,Source Han Sans,Source Han Sans JP,Noto Sans CJK SC,Source Han Sans SC,Meiryo,MS Gothic'; //Used in lang=ja elements only. KanaSerif will be overrided.
  31. var JaDefault = JaSans; //Default fonts if no "sans" or "sans-serif" is set for lang=ja elements.
  32. ///---Latin Fonts. Note: *DO NOT* use CJK fonts for the following Latin* settings, otherwise the above CJK settings might be overwritten!---///
  33. var LatinInSimSun = 'Ubuntu Mono'; //The Latin font in a paragraph whose font was specified to "SimSun" only.
  34. var LatinSerif = '"PT Serif",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.
  35. var LatinSans = '"Open Sans","PT Sans",Lato,Verdana,Arial'; //Sans-serif fonts for Latin script. It will be overridden by a non-virtual font in the CSS font list if present.
  36. var LatinMono = '"DejaVu Sans Mono",Consolas'; //Monospace fonts for Latin script. It will be overridden by a non-virtual font in the CSS font list if present.
  37. var LatinDefault = LatinSans; //The default Latin fonts if no "serif" or "sans-serif" is provided. It is also the font that will be used if the specified fonts (by the webpage) cannot be found.
  38. ///---Choose what to fix---///
  39. var FixRegular = true; //Also fix regular fonts. You need to keep this true if you want to use "LatinInSimSun" in Latin/CJK mixed context.
  40. 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.
  41. ///=== Experimental Options. The following options are for experienced users.===///
  42. var usePaltForCJKText = true; //If apply "palt" to CJK text (not only puncts) as well.
  43. var usePaltForAll = false; //If apply "palt" to as much elements as possible.
  44. var useJustify = true; //Make justify as the default alignment.
  45. var forceAutoSpaces = true; //if enabled, no need to double-click to add spaces.
  46. var useBroaderSpaces = false; //Now It will NOT be set to true automatically if usePaltForCJKText===true.
  47. var useXBroaderSpaces = false; //It will override useBroaderSpaces.
  48. var use2XBroaderSpaces = false; //It will override useXBroaderSpaces.
  49. var use3XBroaderSpaces = false; //It will override use2XBroaderSpaces.
  50. var scrollToFixAll = false; //Scoll to FixAll,including PMs. Might slow down the browser.
  51. var skipJaLang = false; //Skip lang=ja elements and webpages (usually pure Japanese pages). Keep it true if you want to apply your brower's Japanese font settings.
  52. var unifiedCJK = false; // Use Chinese fonts for lang=ja if set to "true".
  53. var FixPureLatin = false; //Appendent the script to all elements, including pure latins. The option is here for historical reasons and usually you should use the built-in font settings of your browser.
  54. ///=== "Safe" Zone Ends Here.Do not change following code unless you know the results! ===///
  55. //--output the version info first--//
  56. console.log('FixCJK! version '+GM_info.script.version);
  57. // Global runtime flags
  58. var isScrolling = false;
  59. var SkipLabelCJK = false; //for internal use only. It will set to true if the page is pure Eng.
  60. if (usePaltForAll === true)
  61. usePaltForCJKText = true;
  62. //if (usePaltForCJKText === true)
  63. // useBroaderSpaces = true;
  64. var timeOut=3000; //allow maximum 3.0 seconds to run this script.
  65. var maxlength = 11002000; //maximum length of the page HTML to check for CJK punctuations.
  66. var maxNumElements = 810240; // maximum number of elements to process.
  67. var CJKOnlyThreshold = 110240; // Only CJK if the number of elements reaches this threshold.
  68. var noBonusLength = 110240; //no bonus functions such as fixing "reversed" pairs.
  69. var noBonusTimeout = 200; //Longest time (in ms) to run bonus functions for each element.
  70. var sqz_timeout=50; // 50ms per element seems long enough.
  71. var invForLimit=6; //the time limit factor (actual limit is timeOut/invForLimit) for the "for loop" in Round 2 & 3.
  72. var processedAll=true;
  73. var ifRound1=true;
  74. var ifRound2=true;
  75. var ifRound3=false;
  76. var RawFixPunct=FixPunct;
  77. var forceNoSimSun = false; //in case SimSun is the "!important" one. Note that other fixes will not be performed for applied tags.
  78. var debug_verbose = false; //show/hide more information on console.
  79. var debug_00 = false; //debug codes before Rounds 1/2/3/4.
  80. var debug_01 = false; //Turn on colors for Round 1.
  81. var debug_02 = false;
  82. var debug_03 = false;
  83. var debug_04 = false;
  84. var debug_labelCJK = false;
  85. var debug_re_to_check = false; //"true" might slow down a lot!
  86. var debug_spaces =false;
  87. var debug_wrap = false;
  88. var debug_tagSeeThrough = false;
  89. var debug_getBeforeTags = false;
  90. var debug_noWrapping = false;
  91. var debug_asyncTimers = true;
  92. var useWrap=true;
  93. var useRemoveSpacesForSimSun=false;
  94. var useFeedback=false;
  95. var useCSSforSimSun=false;
  96. var useDelayedFix=false;
  97. var useOverallTimeOut=false;
  98. var useSFTags=false; //FIXME: use tags may cause problems on jd.com.
  99. var re_allpuncts=/[、,。:;!?)】〉》」』『「《〈【(“”‘’]/;
  100. //var re_extCJK=/[“”‘’\u3000-\u30FF\u3400-\u9FBF\uFF00-\uFFEF]/;
  101. var re_extCJK=/[“”‘’\u3000-\u9FBF\uFF00-\uFFEF]/;
  102. var re_pureCJK=/[\u3000-\u30FF\u3400-\u9FBF\uFF00-\uFFEF]/;
  103. var re_to_check = /^\uEEEE/; //use ^\uEEEE for placeholder. Avoid using the "m" or "g" modifier for long document, but the difference seems small?
  104. ///=== The following variables should be strictly for internal use only.====///
  105. var refixing=false;
  106. var refixingFonts=false;
  107. var respacing=false;
  108. var lastspacing=0.0;
  109. var rspLength=3; //If the font-list reaches the length here, the author is probably responsible enough to cover most Latin/English environment.
  110. var waitForDoubleClick=200;
  111. var SkippedTagsForFonts=/^(HTML|TITLE|HEAD|LINK|BODY|SCRIPT|noscript|META|STYLE|AUDIO|video|source|AREA|BASE|canvas|figure|map|object|textarea)$/i;
  112. var SkippedTagsForMarks=/^(HTML|TITLE|HEAD|LINK|BODY|SCRIPT|noscript|META|STYLE|AUDIO|video|source|AREA|BASE|canvas|embed|figure|map|object|textarea|input|code|pre|time|tt|BUTTON|select|option|label|fieldset|datalist|keygen|output)$/i;
  113. var SkippedTags=SkippedTagsForFonts;
  114. //It seems that "lang" cannot be calculated. Just use node.getAttribute("lang") to get the lang of current elements.
  115. var SkippedLangs='(xa|en)';
  116. if (skipJaLang === true)
  117. SkippedLangs=RegExp(SkippedLangs.replace(/xa/,'ja'),'i');
  118. else
  119. SkippedLangs=RegExp(SkippedLangs,'i');
  120. 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.
  121. var stopTags=/^(SUB|SUP|BR|VR)$/i; //The "see-through" stops at these tags.
  122. var stopClasses='mw-editsection,date';
  123. 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.
  124. var ignoredTags=/^(math)$/i;
  125. var noWrappingClasses='pl-c,toggle-comment,answer-date-link'; //Also known as "no wrapping list". Only wrapped CJK will be treated.
  126. //noWrappingClasses=noWrappingClasses+',PollXChoice-choice--text'; //PollXChoice-choice--text from twitter, see issue #113.
  127. if ( document.URL.match(/(bgm\.tv|bangumi.tv)/) )
  128. noWrappingClasses=noWrappingClasses+',userInfo,userName';
  129. console.log('The following classes won\'t be treated:\n'+noWrappingClasses);
  130. //Just define a "dumb" noWrappingHRefs.
  131. var noWrappingHRefs=/^\uE000\uE000\uE000/;
  132. //The folloing noWraping HRefs is still for bgm.tv
  133. if ( document.URL.match(/(bgm\.tv|bangumi.tv)/) )
  134. noWrappingHRefs=/\/user\//;
  135. var preSimSunList='c30,c31,c32,c33,c34,c35,c36,c37,c38,c39,c40,c41,c42,c43,c44,c45,c46';
  136. var preSimSunTags=/^(pre|code|tt)$/i;
  137. //Safe2FixCJK\uE000,\uE211,\uE985,\uE699
  138. var CJKAttrList='CJK2Fix,MarksFixedE135,FontsFixedCJK,Safe2FixCJK,PunctSpace2Fix,CJKTestedAndLabeled,SimSun2Fix,SimSunFixedCJK,LargeSimSun2Fix,checkSpacedQM,wrappedCJK2Fix,preCode,preMath,SpacesFixedE133';
  139. var re_autospace_url=/zhihu\.com|guokr\.com|changhai\.org|wikipedia\.org|greasyfork\.org|github\.com/;
  140. var preCodeTags='userInfo,code,pre,tt'; //Is this the same as "SkippedTagsForMarks"?
  141. var preMathTags='math'; //Do not change puncts as well as fonts. Just like "math".
  142. var t_start = performance.now();
  143. var t_stop = t_start;
  144. var re_simsun = / *simsun *| *宋体 *| *ËÎÌå *| *\5b8b\4f53 */i;
  145. var sig_sim = 'FixedCJKFont\u0020易'; //Just for SimSun;
  146. var sig_song = 'FixedCJKFont\u0020宋'; // signature to check if change is sucssful or not.
  147. var sig_hei = 'FixedCJKFont\u0020黑'; // signature to check if change is sucssful or not.
  148. var sig_bold = 'FixedCJKFont\u0020粗'; // signature to check if change is sucssful or not.
  149. var sig_default = 'FixedCJKFont\u0020默'; // signature to check if change is sucssful or not.
  150. var sig_mono= 'FixedCJKFont\u0020均';
  151. var sig_punct = '\uE135'; //will be attached to CJKPunct; This is used in punct fixing not font fixing(?)
  152. var qsig_sim = '"' + sig_sim + '"'; //Quoted sinagure; Actually no need to quote.
  153. var qsig_song= '"'+sig_song+'"';
  154. var qsig_hei = '"' + sig_hei + '"'; //Quoted sinagure;
  155. var qsig_bold = '"' + sig_bold + '"';
  156. var qsig_default = '"' + sig_default + '"';
  157. var genPunct='FixedPMSans'; //Different from sig_punct
  158. var qpreCJK = CJKdefault;
  159. var qCJK = LatinDefault + ',' + CJKdefault + ',' + qsig_default;
  160. var qCJK_ja = LatinDefault + ',' + JaDefault + ',' + qsig_default;
  161. var qSimSun = qsig_sim+','+LatinInSimSun + ',' + CJKSimSun;
  162. var qLargeSimSun = qsig_sim+','+ LatinSerif + ',' + 'SimSun';
  163. var qBold = LatinInSimSun + ',' + CJKBold + ',' + qsig_bold;
  164. var qsans = LatinSans + ',FixKanaSans,'+CJKsans + ',' + qsig_hei + ',' + 'sans-serif'; //To replace "sans-serif"
  165. var qsans_ja = dequote(LatinSans + ',' + JaSans + ',' + qsig_hei + ',' + 'sans-serif');
  166. var qserif = LatinSerif + ',FixKanaSerif,'+CJKserif +','+qsig_song+ ',' + 'serif'; //To replace "serif"
  167. var qserif_ja = dequote(LatinSans + ',' + JaSerif + ',' + qsig_song + ',' + 'serif');
  168. var qmono = sig_mono+','+LatinMono + ',' + CJKdefault + ',' + qsig_default + ',' + 'monospace'; //To replace "monospace".
  169. //--Check the length of the webpage --//
  170. var all = document.getElementsByTagName('*');
  171. var NumAllDOMs=all.length;
  172. var bodyhtml=document.getElementsByTagName("HTML");
  173. if (bodyhtml[0].innerHTML.length > maxlength) {
  174. console.log('FixCJK!: HTML too long, skip everything. Exiting now...');
  175. ifRound1=false;
  176. ifRound2=false;
  177. ifRound3=false;
  178. FixPunct=false;
  179. }
  180. else if (!(bodyhtml[0].innerHTML.match(/[\u3000-\u30FF\u3400-\u9FBF\uFF00-\uFFEF]/))) {
  181. if (debug_verbose===true) {console.log('FixCJK!: Checking for CJK took '+((performance.now()-t_stop)/1000.0).toFixed(3)+' seconds. No CJK found.');}
  182. if (debug_verbose===true) {console.log('FixCJK!: No need to check CJK punctuations.');}
  183. FixPunct=false;
  184. }
  185. else {
  186. if (debug_verbose===true) {console.log('FixCJK!: Checking for CJK took '+((performance.now()-t_stop)/1000.0).toFixed(3)+' seconds. CJK found.');}
  187. //FixPunct=true;
  188. }
  189. var i = 0;
  190. var max = all.length;
  191. var child = all[i].firstChild;
  192. var if_replace = false;
  193. var font_str = ""; //window.getComputedStyle(all[i], null).getPropertyValue('font-family');
  194. var fweight = ""; //window.getComputedStyle(all[i], null).getPropertyValue('font-weight');
  195. var re_sans0 = /^ ?sans ?$|^ ?sans-serif ?$/i;
  196. var re_serif = /^ ?serif ?$/i;
  197. var re_mono0 = /^ ?mono ?$|^ ?monospace ?$/i;
  198. //letter-spacing options
  199. var kern_consec_ll='0.0em'; //。” or ))
  200. var kern_consec_rr='0.0em'; //((
  201. var kern_consec_lr='0.0em'; //)(
  202. var kern_ind_open='0.22em'; //margin-left for opening punct.
  203. var kern_ind_close='0.22em'; //margin-right closing punct.
  204. //Whether to use the native embeded OpenType kerning or not, see
  205. //https://helpx.adobe.com/typekit/using/open-type-syntax.html
  206. var useNativeKerning=false;
  207. if (useNativeKerning === true) {
  208. kern_ind_open='0.0em';
  209. kern_ind_close='0.0em';
  210. }
  211. //Check if the font definitions are valid
  212. if (check_fonts(CJKdefault, 'CJKdefault') === false)
  213. return false;
  214. else if (check_fonts(CJKserif, 'CJKserif') === false)
  215. return false;
  216. else if (check_fonts(CJKsans, 'CJKsans') === false)
  217. return false;
  218. else if (check_fonts(CJKBold, 'CJKBold') === false)
  219. return false;
  220. else if (check_fonts(LatinInSimSun, 'LatinInSimSun') === false)
  221. return false;
  222. else if (check_fonts(LatinSans, 'LatinSans') === false)
  223. return false;
  224. else if (check_fonts(LatinSerif, 'LatinSerif') === false)
  225. return false;
  226. else if (check_fonts(LatinMono, 'LatinMono') === false)
  227. return false;
  228. else {
  229. }
  230. //---Some inital checkup and output the version info--//
  231. if (debug_00 === true) {
  232. console.log(dequote('"SimSun","Times New Roman"""""'));
  233. console.log(qCJK);
  234. }
  235. //Assign fonts for puncts:
  236. var punctStyle='@font-face { font-family: '+genPunct+';\n src: '+AddLocal(CJKPunct)+';\n unicode-range: U+3000-303F,U+FF00-FFEF;}';
  237. //Use punct fonts of SimHei in SimSun;
  238. punctStyle=punctStyle+'\n@font-face {font-family:FixedCJKFont\u0020易;\n src:local(SimHei);\n unicode-range: U+A0-B6,U+B8-2FF,U+2000-2017,U+201E-2FFF;}';
  239. punctStyle=punctStyle+'\n@font-face {font-family:SimVecA;\n src:local(Ubuntu Mono);\n unicode-range: U+0-7F;}';
  240. punctStyle=punctStyle+'\n@font-face {font-family:SimVecS;\n src:local(SimHei);\n unicode-range: U+A0-33FF;}';
  241. punctStyle=punctStyle+'\n@font-face {font-family:SimVecC;\n src:local(Microsoft YaHei);\n unicode-range: U+3400-9FBF;}';
  242. //Make sure we only use the good "Chinese" part of YaHei.
  243. punctStyle=punctStyle+'\n@font-face { font-family: 微软雅黑;\n src:local(Microsoft YaHei);\n unicode-range: U+3400-9FBF;\n font-weight: normal;}';
  244. punctStyle=punctStyle+'\n@font-face { font-family: 微软雅黑;\n src:local(Microsoft YaHei Bold);\n unicode-range: U+3400-9FBF;\n font-weight: bold;}';
  245. punctStyle=punctStyle+'\n@font-face { font-family: 雅黑;\n src:local(Microsoft YaHei);\n unicode-range: U+3400-9FBF;\n font-weight: normal;}';
  246. punctStyle=punctStyle+'\n@font-face { font-family: 雅黑;\n src:local(Microsoft YaHei Bold);\n unicode-range: U+3400-9FBF;\n font-weight: bold;}';
  247. punctStyle=punctStyle+'\n@font-face { font-family: 黑体;\n src:local(Microsoft YaHei);\n unicode-range: U+3400-9FBF;\n font-weight: normal;}';
  248. punctStyle=punctStyle+'\n@font-face { font-family: 黑体;\n src:local(Microsoft YaHei Bold);\n unicode-range: U+3400-9FBF;\n font-weight: bold;}';
  249. punctStyle=punctStyle+'\n@font-face { font-family: Microsoft YaHei;\n src:local(Microsoft YaHei);\n unicode-range: U+3400-9FBF;\n font-weight: normal;}';
  250. punctStyle=punctStyle+'\n@font-face { font-family: Microsoft YaHei;\n src:local(Microsoft YaHei Bold);\n unicode-range: U+3400-9FBF;\n font-weight: bold;}';
  251. if (useCSSforSimSun===true) {
  252. punctStyle=punctStyle+'\n @font-face { font-family: SimSun;\n src: local('+FirstFontOnly('SimSun')+');\n unicode-range: U+3400-9FBF;}';
  253. punctStyle=punctStyle+'\n @font-face { font-family: 宋体;\n src: local('+FirstFontOnly('SimSun')+');\n unicode-range: U+3400-9FBF;}';
  254. punctStyle=punctStyle+'\n @font-face { font-family: ËÎÌå;\n src: local('+FirstFontOnly('SimSun')+');\n unicode-range: U+3400-9FBF;}';
  255. punctStyle=punctStyle+'\n @font-face { font-family: 宋体;\n src: local('+FirstFontOnly(LatinInSimSun)+');\n unicode-range: U+0000-2C7F;}';
  256. }
  257. punctStyle=punctStyle+'\n cjkpuns { -moz-font-feature-settings:"palt"; -webkit-font-feature-settings:"palt";font-feature-settings:"palt";}';
  258. if (useNativeKerning === true) {
  259. punctStyle=punctStyle+'\n cjkpuns { font-kerning: normal; }';
  260. }
  261. if (usePaltForCJKText === true) {
  262. punctStyle=punctStyle+'\n cjktext {-moz-font-feature-settings:"palt"; -webkit-font-feature-settings:"palt";font-feature-settings:"palt";}';
  263. }
  264. if (usePaltForAll === true) {
  265. punctStyle=punctStyle+'\n * {-moz-font-feature-settings:"palt"; -webkit-font-feature-settings:"palt";font-feature-settings:"palt";}';
  266. }
  267. if (debug_00===true)
  268. console.log(punctStyle);
  269. punctStyle=punctStyle+'\n @font-face { font-family:FixKanaSans;\n src:'+AddLocal(KanaSans)+';\n unicode-range: U+3040-30FF;}';
  270. punctStyle=punctStyle+'\n @font-face { font-family:FixKanaSerif;\n src:'+AddLocal(KanaSerif)+';\n unicode-range: U+3040-30FF;}';
  271. GM_addStyle(punctStyle);
  272. //--Style settings done. Now let's check if we need to continue--//
  273. var docLang = document.documentElement.getAttribute("lang")+' '; //make sure docLang is not "";
  274. if (debug_00 === true) {
  275. console.log(docLang);
  276. console.log(!(!docLang.match(SkippedLangs)));
  277. }
  278. if ( docLang.match(SkippedLangs) ) {
  279. if (debug_00 === true) {
  280. console.log(document.documentElement.innerText.match(re_pureCJK) );
  281. }
  282. if ( !document.documentElement.innerText.match(re_pureCJK) ) {
  283. console.log('Non-optimal lang attribute detected...Long-click or double-click to re-enable FixCJK!');
  284. SkipLabelCJK = true;
  285. }
  286. }
  287. ///----------------------------
  288. qpreCJK = dequote(qpreCJK);
  289. qCJK = dequote(qCJK);//LatinInSimSun + ',' + CJKdefault + ',' + qsig_default;
  290. qSimSun = dequote(qSimSun);//LatinInSimSun + ',' + CJKserif + ',' + qsig_sun;
  291. qLargeSimSun = dequote(qLargeSimSun);//LatinInSimSun + ',' + CJKserif + ',' + qsig_sun;
  292. qBold = dequote(qBold);//LatinInSimSun + ',' + CJKBold + ',' + qsig_bold;
  293. qsans = dequote(qsans);//LatinSans + ',' + CJKsans + ',' + qsig_hei + ',' + 'sans-serif'; //To replace "sans-serif"
  294. qserif = dequote(qserif);//LatinSerif + ',' + CJKserif + ',' + qsig_sun + ',' + 'serif'; //To replace "serif"
  295. qmono = dequote(qmono);//LatinMono + ',' + CJKdefault + ',' + qsig_default + ',' + 'monospace'; //To replace "monospace".
  296. CJKPunct=dequote(CJKPunct)+','+sig_punct;
  297. if (debug_00===true) {console.log('Entering Loops...');}
  298. /// ===== Labeling CJK elements === ///
  299. t_stop=performance.now();
  300. var debug_addTested=false;
  301. function addTested (node,currLevel) {
  302. if (currLevel > 5) {
  303. if (debug_addTested===true) console.log("TOO MANY LEVELS, exiting addTested()...");
  304. return false;
  305. }
  306. var child=node.firstChild;
  307. while (child) {
  308. if (child.nodeType===1) {
  309. addTested(child,currLevel+1);
  310. }
  311. child=child.nextSibling;
  312. }
  313. if (node.hasAttribute("data-CJKTestedAndLabeled")) {
  314. if (debug_addTested===true) console.log("Labeled: "+node.nodeName);
  315. return true;
  316. }
  317. else {
  318. node.setAttribute("data-CJKTestedAndLabeled","");
  319. if (debug_addTested===true) console.log("Labeled: "+node.nodeName);
  320. return true;
  321. }
  322. }
  323. /*
  324. function labelCJKByNode(node,levelIndex) {
  325. var t_stop=performance.now();
  326. if (node instanceof SVGElement) {
  327. return false;
  328. }
  329. //One do need to recheck the textContent everytime "ReFix" is triggered.
  330. if ( (levelIndex < 2) && (!node.textContent.match(re_extCJK)) ) {
  331. if (!node.hasAttribute("data-CJKTestedAndLabeled")) {
  332. window.setTimeout(addTested,5,node,0);
  333. }
  334. return true;
  335. }
  336. var font_str=dequote(window.getComputedStyle(node, null).getPropertyValue('font-family'));
  337. var child=node.firstChild;
  338. while (child) {
  339. if (child.nodeType===3) {
  340. if (node.hasAttribute("data-CJKTestedAndLabeled") ) {
  341. //Do nothing if already labeled.
  342. }
  343. else if (font_str.match(re_simsun)) {
  344. if (inTheClassOf(node,preSimSunList) || node.nodeName.match(preSimSunTags)) {
  345. node.style.fontFamily=font_str.replace(re_simsun,'SimVecA,SimVecS,SimVecC');
  346. node.setAttribute("data-CJK2Fix","");
  347. node.setAttribute("data-CJKTestedAndLabeled","");
  348. }
  349. else {
  350. var font_size=(window.getComputedStyle(node, null).getPropertyValue('font-size')).slice(0,-2);
  351. if (font_size < 18) {
  352. node.setAttribute("data-CJK2Fix","");
  353. node.setAttribute("data-SimSun2Fix","");
  354. if (!inTheClassOf(node,noWrappingClasses)) {
  355. node.setAttribute("data-PunctSpace2Fix","");
  356. }
  357. }
  358. else {
  359. //node.style.fontFamily=font_str; //Is this to improve the speed?
  360. node.setAttribute("data-CJK2Fix","");
  361. node.setAttribute("data-LargeSimSun2Fix","");
  362. if (!inTheClassOf(node,noWrappingClasses)) {
  363. node.setAttribute("data-PunctSpace2Fix","");
  364. }
  365. }
  366. }
  367. }
  368. else if (child.data.match(re_extCJK)) {
  369. node.setAttribute("data-CJK2Fix","");
  370. if (!inTheClassOf(node,noWrappingClasses)) {
  371. node.setAttribute("data-PunctSpace2Fix","");
  372. }
  373. }
  374. }
  375. else if (child.nodeType===1) {
  376. labelCJKByNode(child,levelIndex+1);
  377. }
  378. child=child.nextSibling;
  379. }
  380. node.setAttribute("data-CJKTestedAndLabeled","");
  381. return true;
  382. }
  383. */
  384. function labelCJK(useCJKTimeOut) {
  385. if (SkipLabelCJK === true) {
  386. console.log('Skipping labelCJK...');
  387. return false;
  388. }
  389. var useBFS=false;
  390. var child=document.body.firstChild;
  391. var maxLabelingTime=150
  392. var all='';
  393. /*
  394. if (useBFS===true) {
  395. while (child) {
  396. if (child.nodeType===1) {
  397. //The levelIndex of document.body is 0.
  398. labelCJKByNode(child,1);
  399. }
  400. child=child.nextSibling;
  401. }
  402. return true;
  403. }
  404. */
  405. //Skip wrapping CJK for anchors to javascripts, otherwise the anchors will break.
  406. all=document.querySelectorAll('a:not([data-preCode])');
  407. for (var ia=0;ia<all.length;ia++){
  408. //if (isScrolling == true) {alert('trying to label CJK, but in scrolling....');break;}
  409. if (all[ia].hasAttribute("data-CJKTestedAndLabeled")) {
  410. continue;
  411. }
  412. if (all[ia].hasAttribute("data-mathml")) {
  413. console.log(all[ia]);
  414. all[ia].setAttribute("data-preMath","");
  415. banMathHelper(all[ia]);
  416. }
  417. if(all[ia].nodeName.match(/^A$/i) && all[ia].href.match(/^javascript/i) && (all[ia].textContent.match(re_extCJK)) ) {
  418. all[ia].setAttribute("data-preCode",""); //No wrapping if in the "preCode" class.
  419. }
  420. }
  421. all=document.querySelectorAll(":not([data-CJKTestedAndLabeled])");
  422. if (useCJKTimeOut===false) {
  423. console.log(all.length+" elements to check and label. From");
  424. console.log(all[0]);
  425. console.log('To');
  426. console.log(all[all.length-1]);
  427. }
  428. var t_stop=performance.now();
  429. var t_last=0;
  430. var t_init=t_stop;
  431. var t_overall=0;
  432. for (var i=all.length-1;i >= 0;i--) {
  433. //if (isScrolling == true) {alert('trying to label CJK, but in scrolling....');break;}
  434. if (useCJKTimeOut===true && i%100 === 0) { //useCJKTimeOut===false is the "Engineering mode".
  435. t_last=performance.now()-t_stop;
  436. t_stop=performance.now();
  437. t_overall=performance.now()-t_init;
  438. }
  439. if (i>0 && t_last>20) {
  440. if ( debug_labelCJK===true) {
  441. console.log("FIXME: Curr: ");
  442. console.log(all[i]);
  443. console.log("FIXME: Prev: ");
  444. console.log(all[i-1]);
  445. console.log("Labeling Last elemnent: <"+all[i-1].nodeName+">.("+all[i-1].className+") took "+t_last.toFixed(1)+" ms.");
  446. }
  447. if (t_last>50) {
  448. console.log("FIXME: Labeling last element took too much time. Too slow to labelCJK after "+t_overall.toFixed(1)+" ms.");
  449. console.log("FIXME: Only "+document.querySelectorAll("[data-CJKTestedAndLabeled]").length+" tested in total on "+document.URL);
  450. if (debug_labelCJK===true) {console.log(all[i-1]);}
  451. break;
  452. }
  453. }
  454. if ( i%100 === 0 && t_overall > maxLabelingTime) {
  455. console.log("FIXME: Too slow to labelCJK after "+t_overall.toFixed(1)+" ms.");
  456. //console.log("FIXME: Only "+document.querySelectorAll("[data-CJKTestedAndLabeled]").length+" tested in total on "+document.URL);
  457. console.log("FIXME: Only "+i+" tested in total on "+document.URL);
  458. if (debug_labelCJK===true) {console.log(all[i-1]);}
  459. break;
  460. }
  461. if ((all[i].nodeName.match(SkippedTags)) || (!(!all[i].getAttribute("lang")) && all[i].getAttribute("lang").match(SkippedLangs) ) || all[i] instanceof SVGElement || all[i].hasAttribute("data-CJKTestedAndLabeled")){
  462. if (debug_labelCJK===true && t_last>10 ) console.log("SKIPPED: "+all[i].nodeName);
  463. //FIXME:HERE
  464. window.setTimeout(function (node) {node.setAttribute("data-CJKTestedAndLabeled","");
  465. },1,all[i]); //This is the most time consuming part. Trying to use async i/o.
  466. if (all[i].nodeName.match(pureLatinTags)) {
  467. if (useCJKTimeOut===true) {
  468. window.setTimeout(addTested,5,all[i],0);
  469. }
  470. else {
  471. window.setTimeout(addTested,5,all[i],-1000); //Means no limits in actual webpages.
  472. }
  473. }
  474. continue;
  475. }
  476. font_str=dequote(window.getComputedStyle(all[i], null).getPropertyValue('font-family'));
  477. if (inTheClassOf(all[i],preSimSunList) || all[i].nodeName.match(preSimSunTags)) {
  478. all[i].style.fontFamily=font_str.replace(re_simsun,'SimVecA,SimVecS,SimVecC');
  479. all[i].setAttribute("data-CJK2Fix","");
  480. all[i].setAttribute("data-CJKTestedAndLabeled","");
  481. continue;
  482. }
  483. if (debug_01===true) console.log(font_str);
  484. if (font_str.match(re_simsun)) {
  485. var font_size=(window.getComputedStyle(all[i], null).getPropertyValue('font-size')).slice(0,-2);
  486. if (font_size < 18) {
  487. all[i].setAttribute("data-CJK2Fix","");
  488. all[i].setAttribute("data-SimSun2Fix","");
  489. if (!inTheClassOf(all[i],noWrappingClasses) && all[i].contentEditable!=="true") {
  490. all[i].setAttribute("data-PunctSpace2Fix","");
  491. if ( all[i].textContent.match(/\w\s[\u3040-\u30FF\u3400-\u9FBF]|[\u3040-\u30FF\u3400-\u9FBF]\s\w/) && !all[i].textContent.match(re_allpuncts)){
  492. //Do not wrap if already using "spaces" and no puncts
  493. if (!all[i].textContent.match(/^([\s\u0020\u00A0\u2009\u200B-\u200E]|&nbsp;|&thinsp;)[^\s\u0020\u00A0\u2009\u200B-\u200E]/)) {
  494. all[i].removeAttribute("data-PunctSpace2Fix");
  495. all[i].setAttribute("data-preCode","");
  496. }
  497. }
  498. }
  499. }
  500. else {
  501. //all[i].style.fontFamily=font_str; //Is this to increase the speed?
  502. all[i].setAttribute("data-CJK2Fix","");
  503. all[i].setAttribute("data-LargeSimSun2Fix","");
  504. if (!inTheClassOf(all[i],noWrappingClasses) && all[i].contentEditable!=="true") {
  505. all[i].setAttribute("data-PunctSpace2Fix","");
  506. if ( all[i].textContent.match(/\w\s[\u3040-\u30FF\u3400-\u9FBF]|[\u3040-\u30FF\u3400-\u9FBF]\s\w/) && !all[i].textContent.match(re_allpuncts)){
  507. //Do not wrap if already using "spaces" and no puncts
  508. if (!all[i].textContent.match(/^([\s\u0020\u00A0\u2009\u200B-\u200E]|&nbsp;|&thinsp;)[^\s\u0020\u00A0\u2009\u200B-\u200E]/)) {
  509. all[i].removeAttribute("data-PunctSpace2Fix");
  510. all[i].setAttribute("data-preCode","");
  511. }
  512. }
  513. }
  514. }
  515. all[i].setAttribute("data-CJKTestedAndLabeled","");
  516. continue;
  517. }
  518. if ( !(all[i].textContent.match(/[“”‘’\u3000-\u30FF\u3400-\u9FBF\uFF00-\uFFEF]/)) ){
  519. 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.
  520. window.setTimeout(function (node) {node.setAttribute("data-CJKTestedAndLabeled","");},1,all[i]); //This is the most time consuming part. Trying to use async i/o.
  521. 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...
  522. continue;
  523. }
  524. else if (useCJKTimeOut===false && (font_str.split(',').length >= rspLength) ) {
  525. window.setTimeout(function (node) {node.setAttribute("data-CJKTestedAndLabeled","");},1,all[i]); //This is the most time consuming part. Trying to use async i/o.
  526. if (debug_labelCJK===true) {console.log("Labeling non-CJK element: ");console.log(all[i]);}
  527. 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...
  528. continue;
  529. }
  530. else {
  531. //Just skip here. Might be important in the future.
  532. continue;
  533. }
  534. }
  535. child = all[i].firstChild;
  536. while (child) {
  537. var realSibling=child.nextSibling;
  538. if (child.nodeType == 3 && (child.data.match(/[“”‘’\u3000-\u30FF\u3400-\u9FBF\uFF00-\uFFEF]/))) {
  539. all[i].setAttribute("data-CJK2Fix","");
  540. if (!inTheClassOf(all[i],noWrappingClasses) && all[i].contentEditable!=="true") {
  541. all[i].setAttribute("data-PunctSpace2Fix","");
  542. if ( all[i].textContent.match(/\w\s[\u3040-\u30FF\u3400-\u9FBF]|[\u3040-\u30FF\u3400-\u9FBF]\s\w/) && !all[i].textContent.match(re_allpuncts)){
  543. //Do not wrap if already using "spaces" and no puncts
  544. //If space at the beginning, it might the "extra space at the beginning but after PM in another node" case.
  545. if (!all[i].textContent.match(/^([\s\u0020\u00A0\u2009\u200B-\u200E]|&nbsp;|&thinsp;)[^\s\u0020\u00A0\u2009\u200B-\u200E]/)) {
  546. all[i].removeAttribute("data-PunctSpace2Fix");
  547. all[i].setAttribute("data-preCode","");
  548. }
  549. }
  550. }
  551. //Do I need to test the parentNode? I deleted them in 1.1.3
  552. break;
  553. }
  554. child=realSibling;
  555. }
  556. all[i].setAttribute("data-CJKTestedAndLabeled","");
  557. }
  558. }
  559. //return true;
  560. //Do not try to fixpuncts if it is an English site. Just trying to save time.
  561. labelPreMath();
  562. labelCJK(true);
  563. //The following is not needed and the manipulation of global variables should not be performed.
  564. //if ((document.querySelectorAll("[data-CJK2Fix]")).length < 1) {
  565. // FixPunct=false;
  566. // console.log("No puncts will be fixed.");
  567. //}
  568. if (debug_verbose===true) {console.log('FixCJK!: Labling took '+((performance.now()-t_stop)/1000).toFixed(3)+' seconds.');}
  569. ///===FixFonts, Rounds 1-3===///
  570. FixAllFonts();
  571. ///===Round 4, FixPunct===///
  572. if (debug_verbose===true) {console.log('FixCJK!: Labling and Fixing fonts took '+((t_stop-t_start)/1000).toFixed(3)+' seconds.');}
  573. if ((t_stop-t_start)*2 > timeOut || max > maxNumElements ) {
  574. console.log('FixCJK!: Too slow or too many elements.');
  575. //FixPunct=false; //This seems meaningless. There is a overal timeOut anyway.
  576. }
  577. if (FixPunct===false) {
  578. if (debug_verbose===true) {console.log('FixCJK!: Skipping fixing punctuations...');}
  579. }
  580. var returnNow=true;
  581. var returnLater=false; //Do the actual fixing.
  582. var MaxNumLoops=1;
  583. if (useDelayedFix===true) {
  584. var DelayedTimer=200;
  585. window.setTimeout(FunFixPunct(true,MaxNumLoops,returnLater),DelayedTimer);
  586. }
  587. else {
  588. window.setTimeout(function () {
  589. labelPreCode();
  590. labelNoWrappingList();
  591. if (useWrap===true) wrapCJK();
  592. FunFixPunct(true,MaxNumLoops,returnLater);
  593. },10);
  594. }
  595. ///===End of Solving the picture problem===///
  596. if (debug_verbose===true) {console.log('FixCJK!: Fixing punctuations took '+((performance.now()-t_stop)/1000).toFixed(3)+' seconds.');}
  597. ///===Try to fix spaces if forceAutoSpaces is set===///
  598. if (forceAutoSpaces === true)
  599. window.setTimeout(function (){addSpaces(true,100);},10);
  600. ///===Add onClick listener before exiting===///
  601. var NumClicks=0;
  602. var t_last=performance.now();
  603. var t_interval=1000; //The interval between two checks.
  604. var t_interSpacing=500;
  605. var NumAllCJKs=(document.querySelectorAll("[data-CJK2Fix]")).length;
  606. var NumPureEng=0;
  607. var LastURL=document.URL;
  608. var LastMod=document.lastModified;
  609. var ItvScl=2.0; //Real "cooling down time" is t_interval/ItvScl
  610. // NumPureEng++ will cause problems on kkj.cn.
  611. //if (NumAllCJKs*1.0/NumAllDOMs*100 < 1.0) {
  612. // NumPureEng++;
  613. //}
  614. //document.onClick will cause problems on some webpages on Firefox.
  615. var downtime=performance.now();
  616. var downX=0;
  617. var downY=0;
  618. document.body.addEventListener("mousedown",function (e){downtime=performance.now();downX=e.clientX;downY=e.clientY;},false);
  619. document.body.addEventListener("mouseup",function (e){
  620. if (e.button>0 ) {
  621. //do nothing if right button clicked.
  622. return true;
  623. }
  624. else if (((performance.now()-downtime) < 300) && (Math.abs(e.clientX-downX)+Math.abs(e.clientY-downY)) ===0 ) {
  625. //ReFix after other things are done.
  626. FixPunct=RawFixPunct;
  627. //Do not change SkipLabelCJK for single clicks.
  628. //SkipLabelCJK = false;
  629. setTimeout(ReFixCJK,5,e);
  630. if (forceAutoSpaces === true)
  631. setTimeout(function (){addSpaces(true,300);},5);
  632. }
  633. else if (((performance.now()-downtime) > 1500) && (Math.abs(e.clientX-downX)+Math.abs(e.clientY-downY)) ===0 ) {
  634. //Force to labelCJK for all elements;
  635. var t_CJK=performance.now();
  636. labelPreMath();
  637. SkipLabelCJK = false; //reset the variable which could be set b/c of the SkippedLangs.
  638. labelCJK(false);
  639. FixAllFonts(false);
  640. labelPreCode();
  641. labelNoWrappingList();
  642. if (useWrap===true) wrapCJK();
  643. FixPunct = true;
  644. FunFixPunct(false,5,false);
  645. FixPunct=RawFixPunct;
  646. addSpaces(false,10000);
  647. t_CJK=performance.now()-t_CJK;
  648. console.log("Labeling and fixing all CJK elements took "+(t_CJK/1000).toFixed(1)+" seconds.");
  649. }
  650. },false);
  651. //use named timers to keep track of refixes.
  652. var timerReFix= null;
  653. var timerSpaces = null;
  654. var waitAfterScolling=300;
  655. window.addEventListener("scroll",function (e){
  656. isScrolling = true;
  657. if(timerReFix !== null) {
  658. clearTimeout(timerReFix);
  659. }
  660. if(timerSpaces !== null) {
  661. clearTimeout(timerSpaces);
  662. }
  663. if (scrollToFixAll === true) {
  664. FixPunct=RawFixPunct;
  665. timerReFix=setTimeout(function (e) {
  666. isScrolling=false;
  667. ReFixCJK(e);
  668. addSpaces(true,300);
  669. },waitAfterScolling,e);
  670. //timerReFix=setTimeout(ReFixCJK,waitAfterScolling,e);
  671. //timerSpaces=setTimeout(addSpaces,waitAfterScolling,true,300);
  672. }
  673. else {
  674. //setTimeout(function() {fireReFix=true;},t_interval/ItvScl/2); //Permit ReFixCJK after sometime of last scrolling.
  675. timerReFix=setTimeout(function() {
  676. isScrolling = false;
  677. ReFixCJKFast();
  678. if (forceAutoSpaces === true) {
  679. addSpaces(true,30);
  680. }
  681. },waitAfterScolling);
  682. }
  683. },false);
  684. document.body.addEventListener("dblclick",function(e) {
  685. setTimeout(function (e) {
  686. SkipLabelCJK = false;
  687. FixPunct=RawFixPunct;
  688. ReFixCJK(e);
  689. addSpaces(true,300);
  690. },5,e);
  691. //setTimeout(function(){ fontsCheck(); }, 30);
  692. //Prevent ReFixing for a certain time;
  693. },false);
  694. ///===Time to exit the main function===///
  695. var t_fullstop=performance.now();
  696. if (processedAll===true) {
  697. 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).');
  698. }
  699. else {
  700. 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.');
  701. }
  702. ////////////////////======== Main Function Ends Here ==============/////////////////////////////
  703. //===The actual listening functions===//
  704. function labelPreMath() {
  705. var bannedTagList=preMathTags.split(',');
  706. for (var itag=0;itag<bannedTagList.length;itag++) {
  707. var all2Ban=document.querySelectorAll(bannedTagList[itag]+":not([data-preMath])");
  708. for (var iele=0;iele<all2Ban.length;iele++) {
  709. banMathHelper(all2Ban[iele]);
  710. }
  711. }
  712. }
  713. function labelPreCode() {
  714. var bannedTagList=preCodeTags.split(',');
  715. for (var itag=0;itag<bannedTagList.length;itag++) {
  716. var all2Ban=document.getElementsByTagName(bannedTagList[itag]);
  717. for (var iele=0;iele<all2Ban.length;iele++) {
  718. banHelper(all2Ban[iele]);
  719. }
  720. }
  721. }
  722. function labelNoWrappingList() {
  723. var ie=0;
  724. var bannedClassList=noWrappingClasses.split(',');
  725. for (var i=0;i<bannedClassList.length;i++) {
  726. var all2Ban=document.getElementsByClassName(bannedClassList[i]);
  727. for (ie=0;ie<all2Ban.length;ie++)
  728. banHelper(all2Ban[ie]);
  729. }
  730. var bannedElementList=document.querySelectorAll('[contenteditable="true"]');
  731. for (ie=0;ie<bannedElementList.length;ie++) {
  732. if (debug_noWrapping===true) console.log(bannedElementList[ie]);
  733. banHelper(bannedElementList[ie]);
  734. }
  735. var bannedHRefs=document.getElementsByTagName("A");
  736. for (var iA=0;iA<bannedHRefs.length;iA++) {
  737. if (bannedHRefs[iA].href.match(noWrappingHRefs) ) {
  738. banHelper(bannedHRefs[iA]);
  739. //console.log(bannedHRefs[iA]);
  740. }
  741. }
  742. }
  743. function banHelper(node) {
  744. var child=node.firstChild;
  745. while (child) {
  746. if ( child.nodeType===1 && !(child instanceof SVGElement) ) {
  747. banHelper(child);
  748. }
  749. child=child.nextSibling;
  750. }
  751. if (!node.hasAttribute("data-preCode")) {
  752. node.setAttribute("data-preCode","");
  753. }
  754. }
  755. function banMathHelper(node) {
  756. var child=node.firstChild;
  757. while (child) {
  758. if ( child.nodeType===1 && !(child instanceof SVGElement) ) {
  759. banMathHelper(child);
  760. }
  761. child=child.nextSibling;
  762. }
  763. node.setAttribute("data-CJKTestedAndLabeled","");
  764. node.setAttribute("data-FontsFixedCJK","");
  765. node.setAttribute("data-MarksFixedE135","");
  766. node.setAttribute("data-preMath","");
  767. }
  768. function addSpaces(useSpacingTimeout,spacingTimeOut) {
  769. if (isScrolling == true) {console.log('Trying to add space but in scrolling...'); return false;}
  770. if (respacing === true)
  771. return false;
  772. var t_spaces=performance.now();
  773. if (t_spaces-lastspacing < t_interSpacing ) {
  774. //console.log("Skiping spacing...");
  775. return false;
  776. }
  777. lastspacing=t_spaces;
  778. respacing=true;
  779. if (debug_spaces===true) console.log('FixCJK!: Adding spaces...');
  780. var allQ=document.querySelectorAll("[data-\uE985]");
  781. for (var iq=0;iq<allQ.length;iq++) {
  782. allQ[iq].innerHTML=allQ[iq].innerHTML.replace(/\u2018/g,'\uEB18');
  783. allQ[iq].innerHTML=allQ[iq].innerHTML.replace(/\u2019/g,'\uEB19');
  784. allQ[iq].innerHTML=allQ[iq].innerHTML.replace(/\u201C/g,'\uEB1C');
  785. allQ[iq].innerHTML=allQ[iq].innerHTML.replace(/\u201D/g,'\uEB1D');
  786. }
  787. addSpacesHelper(document.querySelectorAll("[data-PunctSpace2Fix]:not([data-SpacesFixedE133])"),useSpacingTimeout,spacingTimeOut);
  788. allQ=document.querySelectorAll("[data-\uE985]"); //I need to reselect because the "references" are changed?
  789. for (iq=0;iq<allQ.length;iq++) {
  790. allQ[iq].innerHTML=allQ[iq].innerHTML.replace(/\uEB18/g,'\u2018');
  791. allQ[iq].innerHTML=allQ[iq].innerHTML.replace(/\uEB19/g,'\u2019');
  792. allQ[iq].innerHTML=allQ[iq].innerHTML.replace(/\uEB1C/g,'\u201C');
  793. allQ[iq].innerHTML=allQ[iq].innerHTML.replace(/\uEB1D/g,'\u201D');
  794. }
  795. if (useRemoveSpacesForSimSun===true) {
  796. window.setTimeout(removeSpacesForSimSun,10);
  797. }
  798. respacing=false;
  799. console.log("FixCJK: Adding spaces took "+((performance.now()-t_spaces)/1000).toFixed(3)+" seconds.");
  800. function getAfterHTML(child) { //FIXME: A recursion block might be needed as getAfter(child)
  801. var toReturn='';
  802. var t_start=performance.now();
  803. var inputNode=child;
  804. child=child.nextSibling;
  805. while (child && (performance.now()-t_start)<2 ) {
  806. if (child.nodeType===3) {
  807. toReturn = toReturn + child.data;
  808. }
  809. else if (child.nodeType===1 && (window.getComputedStyle(child,null).getPropertyValue("display")!=='none') ) {
  810. if (child.nodeName.match(stopTags) || inTheClassOf(child,stopClasses) ) {
  811. return toReturn+"上下标";
  812. }
  813. toReturn = toReturn + displayedText(child);
  814. }
  815. if (toReturn.match(/[\w\u3400-\u9FBF]/)) {
  816. break;
  817. }
  818. child=child.nextSibling;
  819. }
  820. if (toReturn.length < 1 && !inputNode.parentNode.nodeName.match(upEnoughTags)) {
  821. return getAfterHTML(inputNode.parentNode);
  822. }
  823. else {
  824. return (toReturn.replace(/</,'&lt;')).replace(/>/,'&gt;');
  825. }
  826. }
  827. function getBeforeHTML(child) {
  828. var toReturn='';
  829. var t_start=performance.now();
  830. var inputNode=child;
  831. child=child.previousSibling;
  832. while (child && (performance.now()-t_start)<2 ) {
  833. if (child.nodeType === 3) {
  834. toReturn = child.data + toReturn;
  835. }
  836. else if (child.nodeType === 1 && (window.getComputedStyle(child,null).getPropertyValue("display")!=='none') ) {
  837. if (child.nodeName.match(stopTags) || inTheClassOf(child,stopClasses) ) {
  838. return "上下标"+toReturn;
  839. }
  840. toReturn = displayedText(child) + toReturn;
  841. }
  842. if (toReturn.match(/[\w\u3400-\u9FBF]/)) {
  843. break;
  844. }
  845. child=child.previousSibling;
  846. }
  847. if (toReturn.length < 1 && !inputNode.parentNode.nodeName.match(upEnoughTags)) {
  848. return getBeforeHTML(inputNode.parentNode);
  849. }
  850. else {
  851. return (toReturn.replace(/</,'&lt;')).replace(/>/,'&gt;');
  852. }
  853. }
  854. function addSpacesHelper(allE,useSpacingTimeout,spacingTimeOut) {
  855. var t_substart=performance.now();
  856. for (var is=0;is<allE.length;is++) {
  857. if ( !(allE[is].nodeName.match(/CJKTEXT/)) || allE[is].hasAttribute("data-SpacesFixedE133") ) {
  858. continue;
  859. }
  860. if ( useSpacingTimeout===true && (performance.now()-t_substart)> spacingTimeOut) {
  861. console.log("Timeout: exiting addSpaces()...");
  862. return false;
  863. }
  864. if (allE[is].hasAttribute("data-wrappedCJK2Fix") ) {
  865. if ( !(allE[is].hasAttribute("data-preCode")) ) {
  866. var tmp_str=allE[is].innerHTML;
  867. if (tmp_str.match(/^([\s\u0020\u00A0\u2009\u200B-\u200E]|&nbsp;|&thinsp;){0,5}[\u3040-\u30FF\u3400-\u9FBF]/)) {
  868. //Make sure no text will be prepended to the "left" floated elements.
  869. if (window.getComputedStyle(allE[is].parentNode, null).getPropertyValue('float')!=='left')
  870. tmp_str=getBeforeHTML(allE[is])+'\uF203CJK\uF203'+tmp_str;
  871. }
  872. if (tmp_str.match(/[\u3040-\u30FF\u3400-\u9FBF][\s\u200B-\u200E\2060]{0,2}$/)) {
  873. if (window.getComputedStyle(allE[is].parentNode, null).getPropertyValue('float')!=='right')
  874. tmp_str=tmp_str+'\uF204CJK\uF204'+getAfterHTML(allE[is]);
  875. }
  876. //protect the Latins in tags, no need in 1.0+ b/c no “”’‘ in CJK <cjkpuns> tags.
  877. //en:zh; //why didn't I use "non-CJK" list for Latin?
  878. tmp_str=tmp_str.replace(/&nbsp;/,'\u00A0'); //Or, tmp_str=tmp_str.replace(/\u0026nbsp\u003B/,'\u00A0');
  879. tmp_str=tmp_str.replace(/&thinsp;/,'\u2009'); //Or, tmp_str=tmp_str.replace(/\u0026thinsp\u003B/,'\u2009');
  880. 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])?([\u3040-\u30FF\u3400-\u9FBF])/img;
  881. var space2BeAdded='<cjktext data-CJKTestedAndLabeled data-MarksFixedE135 data-cjkpua=\uE699 class="FontsFixedCJK" style="display:inline;padding-left:0px;padding-right:0px;float:none;font-family:Arial,Helvetica,sans-serif;font-size:80%;">\u0020</cjktext>';
  882. if (useSFTags===false) {
  883. space2BeAdded='\u2009';
  884. if (useBroaderSpaces === true)
  885. space2BeAdded='\u0020';
  886. if (useXBroaderSpaces === true)
  887. space2BeAdded='\u2004'; // 1/3 EM SPACE;
  888. if (use2XBroaderSpaces === true)
  889. space2BeAdded='\u2002'; // 1/2 EM SPACE;
  890. if (use3XBroaderSpaces === true)
  891. space2BeAdded='\u2003'; // 1/1 EM SPACE;
  892. } //\u2009 for thin space and \u200A for "hair space".
  893. var enzh_withSpace='$1$2$3$4'+space2BeAdded+'$5';
  894. tmp_str=tmp_str.replace(re_enzh,enzh_withSpace);
  895. //now zh:en
  896. var re_zhen=/([\u3040-\u30FF\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;
  897. var zhen_withSpace='$1'+space2BeAdded+'$2$3$4';
  898. tmp_str=tmp_str.replace(re_zhen,zhen_withSpace);
  899. //now en["']zh (TODO in 1.x?)
  900. //now zh['"]en (TODO in 1.x?)
  901. tmp_str=tmp_str.replace(/\uED20/mg,'');
  902. tmp_str=tmp_str.replace(/^[^\u0000]*\uF203CJK\uF203([^\u0000]*)$/,'$1'); // '.' does not match \n in whatever mode.
  903. tmp_str=tmp_str.replace(/^([^\u0000]*)\uF204CJK\uF204[^\u0000]*$/,'$1');
  904. allE[is].innerHTML=tmp_str;
  905. allE[is].setAttribute("data-SpacesFixedE133","");
  906. }
  907. else {
  908. if (debug_spaces===true) {console.log("Skipping banned tags:"+allE[is].tagName);}
  909. }
  910. }
  911. }
  912. }
  913. respacing=false;
  914. }
  915. function removeSpacesForSimSun() { //Need more work.
  916. var allS=document.querySelectorAll("[data-\uE699]");
  917. var font_str='';
  918. for (var i=0;i<allS.length;i++) {
  919. font_str=((dequote(window.getComputedStyle(allS[i].parentNode, null).getPropertyValue('font-family'))).split(','))[1];
  920. if (font_str.match(re_simsun)) {
  921. allS[i].innerHTML='';
  922. }
  923. else if (font_str.match(/FixedCJKFont.易/)) {
  924. allS[i].parentNode.setAttribute("data-checkSpacedQM","");
  925. }
  926. }
  927. allS=document.querySelectorAll("[data-checkSpacedQM]");
  928. for (i=0;i<allS.length;i++){
  929. var toRemoved=/(<cjktext[^><]*\uE699[^><]*>\u0020<\/cjkpuns>)((?:<[^><\uE985\uE211]*>)*[\u2018\u201C])/g;
  930. if (allS[i].innerHTML.match(toRemoved)) {
  931. allS[i].innerHTML=allS[i].innerHTML.replace(toRemoved,'$2');
  932. }
  933. //No closing tag: En"Zh
  934. toRemoved=/([\u2019\u201D])<cjktext[^><]*\uE699[^><]*>\u0020<\/cjkpuns>/g;
  935. if (allS[i].innerHTML.match(toRemoved)) {
  936. allS[i].innerHTML=allS[i].innerHTML.replace(toRemoved,'$1');
  937. }
  938. //With closing tag: En"Zh
  939. toRemoved=/((?:^|[^>]|<[^><\uE985\uE211]*>)[\u2019\u201D](?:<[^><\uE985\uE211]*>)+)(<cjktext[^><]*\uE699[^><]*>\u0020<\/cjkpuns>)/mg;
  940. if (allS[i].innerHTML.match(toRemoved)) {
  941. allS[i].innerHTML=allS[i].innerHTML.replace(toRemoved,'$1');
  942. }
  943. }
  944. }
  945. function ReFixCJKFast () {
  946. //if (isScrolling == true) {alert('trying to label CJK, but in scrolling....');return;}
  947. if (refixingFonts===true) {
  948. console.log("Refixing, skipping this refix...");
  949. window.setTimeout(function () {refixingFonts=false;},t_interval/ItvScl/2);
  950. return false;
  951. }
  952. refixingFonts=true;
  953. 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;
  954. t_start=performance.now();
  955. if ( (t_start-t_last)*ItvScl > t_interval ) {
  956. FixRegular = true; //Also fix regular fonts. You need to keep this true if you want to use "LatinInSimSun" in Latin/CJK mixed context.
  957. FixPureLatin = false; //Appendent CJK fonts to all elements. No side effects found so far.
  958. //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.
  959. ifRound1 = true;
  960. ifRound2 = true;
  961. ifRound3 = false;
  962. maxlength = 1100200; //maximum length of the page HTML to check for CJK punctuations.
  963. maxNumElements = 8000; // maximum number of elements to process.
  964. CJKOnlyThreshold = 2000; // Only CJK if the number of elements reaches this threshold.
  965. labelPreMath();
  966. labelCJK(true);
  967. FixAllFonts(true);
  968. //FunFixPunct(true,2,returnLater); //No FixPunct unless "scrollToFixAll" is set to "true".
  969. console.log('FixCJK!: Fast ReFixing took '+((performance.now()-t_start)/1000).toFixed(3)+' seconds.');
  970. }
  971. t_last=performance.now();
  972. refixingFonts=false;
  973. }
  974. function ReFixCJK (e) {
  975. if (refixing===true) {
  976. if (debug_wrap===true) {console.log("Refixing, skipping this refix...");}
  977. window.setTimeout(function () {refixing=false;},t_interval/ItvScl);
  978. return false;
  979. }
  980. refixing=true;
  981. 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;
  982. if (debug_verbose===true) {console.log(e.target.nodeName);}
  983. t_start=performance.now();
  984. //The "LastURL" method is not reliable.
  985. //if (document.URL!==LastURL) {
  986. // NumPureEng = 0;
  987. // LastURL=document.URL;
  988. //}
  989. var clickedNode=e.target;
  990. while (clickedNode && clickedNode.nodeName!=="BODY") {
  991. if (clickedNode.nodeName.match(bannedTagsInReFix)) {
  992. console.log("FixCJK!: Not a valid click on DOM element \u201C"+clickedNode.nodeName+"."+clickedNode.className+"\u201D");
  993. refixing=false;
  994. return false;
  995. }
  996. if (debug_verbose===true) {console.log("Clicked: "+clickedNode.nodeName);}
  997. clickedNode=clickedNode.parentNode;
  998. }
  999. if ((document.lastModified===LastMod) && (NumClicks >2)) {
  1000. if (debug_verbose===true) {console.log('FixCJK!: Document modified at '+document.lastModified+', no change?');}
  1001. }
  1002. else {
  1003. if (debug_verbose===true) {console.log('FixCJK!: Document modified at '+document.lastModified);}
  1004. }
  1005. //Note that NumPureEng method is no accurate. It might be still usefull because document.lastModified method is only partially reliable.
  1006. //if (NumPureEng >= 2) {
  1007. // console.log('Probably pure English/Latin site, re-checking skipped.');
  1008. // refixing=false;
  1009. // return true;
  1010. //}
  1011. if (debug_verbose===true) {console.log('FixCJK!: NumClicks='+NumClicks.toString());}
  1012. //First remove the "CJK2Fix" attibute for those already processed.
  1013. var AllCJKFixed=document.querySelectorAll("[data-FontsFixedCJK]");
  1014. for (i=0;i<AllCJKFixed.length;i++) {
  1015. if (AllCJKFixed[i].hasAttribute("data-wrappedCJK2Fix")) {
  1016. continue;
  1017. }
  1018. if (debug_verbose===true) {console.log(AllCJKFixed[i].className);}
  1019. if (AllCJKFixed[i].hasAttribute("data-MarksFixedE135")) {
  1020. AllCJKFixed[i].removeAttribute("data-CJK2Fix");
  1021. }
  1022. }
  1023. if ((NumClicks < 1) || (t_start-t_last)*ItvScl > t_interval ) {
  1024. FixRegular = true; //Also fix regular fonts. You need to keep this true if you want to use "LatinInSimSun" in Latin/CJK mixed context.
  1025. //FixPureLatin = true; //Appendent CJK fonts to all elements. No side effects found so far.
  1026. //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.
  1027. maxlength = 1100200; //maximum length of the page HTML to check for CJK punctuations.
  1028. maxNumElements = 8000; // maximum number of elements to process.
  1029. CJKOnlyThreshold = 2000; // Only CJK if the number of elements reaches this threshold.
  1030. invForLimit=6; //the time limit factor (actual limit is timeOut/invForLimit) for the "for loop" in Round 2 & 3.
  1031. processedAll=true;
  1032. ifRound1=true;
  1033. ifRound2=true;
  1034. ifRound3=false;
  1035. //if ( scrollToFixAll === true ) {
  1036. // ifRound3=true;
  1037. //}
  1038. var ReFixAll=document.getElementsByTagName('*');
  1039. var NumFixed=0;
  1040. var NumReFix=0;
  1041. labelPreMath();
  1042. labelCJK(true);
  1043. FixAllFonts(true);
  1044. if (debug_verbose===true) {console.log('FixCJK!: '+NumFixed.toString()+' elements has been fixed.');}
  1045. if (debug_verbose===true) {console.log('FixCJK!: '+NumReFix.toString()+' elements to Re-Fix.');}
  1046. labelPreCode();
  1047. labelNoWrappingList();
  1048. if (useWrap===true) wrapCJK();
  1049. FunFixPunct(true,2,returnLater);
  1050. console.log('FixCJK!: ReFixing (Fixing PMs not included) took '+((performance.now()-t_start)/1000).toFixed(3)+' seconds.');
  1051. //The following is not needed b/c not Round3 is skipped by default.
  1052. //NumAllCJKs=(document.querySelectorAll("[data-MarksFixedE135]")).length;
  1053. //if (NumAllCJKs*1.0/NumAllDOMs*100 < 1.0) {
  1054. // NumPureEng++;
  1055. //}
  1056. }
  1057. else {
  1058. console.log('FixCJK!: No need to rush. Just wait for '+(t_interval/1000/ItvScl).toFixed(1)+' seconds before clicking again.');
  1059. }
  1060. NumClicks++;
  1061. LastMod=document.lastModified;
  1062. t_last=performance.now();
  1063. refixing=false;
  1064. }
  1065. ///===various aux functions===///
  1066. function wrapCJK() {
  1067. var wrap_start=performance.now();
  1068. var allCJK=document.querySelectorAll("[data-CJK2Fix]:not([data-wrappedCJK2Fix])");
  1069. for (var i=0;i<allCJK.length;i++) {
  1070. if ( allCJK[i].hasAttribute("data-\uE211") || allCJK[i].hasAttribute("data-\uE985") ||allCJK[i].hasAttribute("data-preCode")) {
  1071. if (allCJK[i].hasAttribute("data-wrappedCJK2Fix")) console.log("FIXME: "+allCJK[i].nodeName+" has already been wrapped.");
  1072. continue;
  1073. }
  1074. else if (allCJK[i].nodeName.match(SkippedTagsForMarks)) {
  1075. continue;
  1076. }
  1077. var child=allCJK[i].firstChild;
  1078. while(child) {
  1079. var realSibling=child.nextSibling;
  1080. if (child.nodeType===3 && (child.data.match(re_extCJK)) ) {
  1081. wrapCJKHelper(child);
  1082. }
  1083. child=realSibling;
  1084. }
  1085. }
  1086. if (debug_wrap===true) console.log("Wrapping took "+((performance.now()-wrap_start)/1000).toFixed(3)+" seconds.");
  1087. function wrapCJKHelper(child) {
  1088. var iNode=document.createElement("cjktext");
  1089. var iText=document.createTextNode(child.data);
  1090. iNode.appendChild(iText);
  1091. iNode.setAttribute("data-wrappedCJK2Fix","");
  1092. iNode.setAttribute("data-CJKTestedAndLabeled","");
  1093. iNode.setAttribute("data-PunctSpace2Fix","");
  1094. iNode.setAttribute("data-CJK2Fix","");
  1095. iNode.setAttribute("data-Safe2FixCJK","\uE000");
  1096. child.parentNode.insertBefore(iNode,child.nextSibling);
  1097. child.data=""; //or "\u200B?"
  1098. }
  1099. }
  1100. function inTheClassOf(node,cList) {
  1101. var classes=cList.split(',');
  1102. for (var i=0;i<classes.length;i++) {
  1103. if (node.hasAttribute(classes[i])) {
  1104. return true;
  1105. }
  1106. }
  1107. return false;
  1108. }
  1109. function check_fonts(font_var, fvname) {
  1110. var fl = font_var.split(',');
  1111. for (i = 0; i < fl.length; i++) {
  1112. if (!(fl[i].match(/^[^" ][^"]+[^" ]$|^"[^ ][^"]+[^ ]"$/))) {
  1113. alert('Check your font definition: ' + fl[i] + ' in ' + fvname);
  1114. return false;
  1115. }
  1116. }
  1117. return true;
  1118. }
  1119. function list_has(font_str, family) {
  1120. /// Fucntion to check matches
  1121. var allfonts = font_str.split(',');
  1122. for (var j = 0, maxl = allfonts.length; j < maxl; j++) {
  1123. if (allfonts[j].match(family)) {
  1124. return j;
  1125. }
  1126. }
  1127. return false;
  1128. }
  1129. function replace_font(font_str, family, qBold) {
  1130. var allfonts = font_str.split(',');
  1131. var j = 0;
  1132. var maxl = allfonts.length;
  1133. for (j = 0; j < maxl; j++) {
  1134. if (allfonts[j].match(family)) {
  1135. allfonts[j] = qBold;
  1136. }
  1137. }
  1138. var toReturn = allfonts[0];
  1139. for (j = 1; j < maxl; j++) {
  1140. toReturn = toReturn + ',' + allfonts[j];
  1141. }
  1142. return toReturn;
  1143. }
  1144. function has_genfam(font_str) {
  1145. /// Test if font_str include general families.
  1146. if (list_has(font_str, re_sans0)) {
  1147. return true;
  1148. }
  1149. else if (list_has(font_str, re_serif)) {
  1150. return true;
  1151. }
  1152. else if (list_has(font_str, re_mono0)) {
  1153. return true;
  1154. }
  1155. return false;
  1156. }
  1157. function dequote(font_str) {
  1158. /// Function to dequote non-standard font lists.
  1159. var strl=font_str.split(','); //font list;
  1160. for (var k=0;k < strl.length; k++) {
  1161. while (strl[k].charAt(0).match(/["' ]/)) {
  1162. strl[k]=strl[k].slice(1);
  1163. }
  1164. while (strl[k].charAt(strl[k].length-1).match(/["' ]/)) {
  1165. strl[k]=strl[k].slice(0,-1);
  1166. }
  1167. }
  1168. var dequoted=strl[0];
  1169. for (k=1;k<strl.length;k++) {
  1170. dequoted=dequoted+','+strl[k];
  1171. }
  1172. return dequoted;
  1173. }
  1174. function FirstFontOnly(font_str) {
  1175. return ((dequote(font_str)).split(','))[0];
  1176. }
  1177. function AddLocal(font_str) {
  1178. font_str=(dequote(font_str)).split(',');
  1179. var localed='local("'+font_str[0]+'"),\nlocal("'+font_str[0]+' Regular")';
  1180. for (var l=1;l<font_str.length;l++) {
  1181. localed=localed+',\n'+'local("'+font_str[l]+'"),\nlocal("'+font_str[l]+' Regular")';
  1182. }
  1183. return localed;
  1184. }
  1185. /// ======================== FixAllFonts, 3 Rounds ==============================///
  1186. function FixAllFonts (useOverallTimeOut) {
  1187. var func_start=performance.now();
  1188. if (debug_verbose===true) {
  1189. console.log("Round 1: "+ifRound1.toString());
  1190. console.log("Round 2: "+ifRound2.toString());
  1191. console.log("Round 3: "+ifRound3.toString());
  1192. }
  1193. SkippedTags=SkippedTagsForFonts;
  1194. /// ===== First round: Replace all bold fonts to CJKBold ===== ///
  1195. t_stop=performance.now();
  1196. //First fix all SimSun parts in Round 1&2. Original: var allSuns=document.querySelectorAll("[data-SimSun2Fix]");
  1197. var allSuns=document.querySelectorAll("[data-SimSun2Fix]:not([data-SimSunFixedCJK])");
  1198. for (var isun=0;isun< allSuns.length;isun++) {
  1199. //if (isScrolling == true) {alert('trying to fix fonts, but in scrolling....');break;}
  1200. if (allSuns[isun].hasAttribute("data-SimSunFixedCJK")) {
  1201. //was if (allSuns[isun].classList.contains(FontsFixedCJK) || allSuns[isun].hasAttribute("data-SimSunFixedCJK"))
  1202. continue;
  1203. }
  1204. font_str = dequote(window.getComputedStyle(allSuns[isun], null).getPropertyValue('font-family'));
  1205. if (font_str.match(re_simsun) && !(font_str.match(sig_sim)) ) {
  1206. if (forceNoSimSun === true ) {
  1207. allSuns[isun].style.setProperty("font-family",font_str.replace(re_simsun,qSimSun),"important");
  1208. allSuns[isun].setAttribute("data-FontsFixedCJK","");
  1209. }
  1210. else
  1211. allSuns[isun].style.fontFamily = font_str.replace(re_simsun,qSimSun);
  1212. allSuns[isun].setAttribute("data-SimSunFixedCJK","");
  1213. }
  1214. }
  1215. //Large fonts: allSuns=document.querySelectorAll("[data-LargeSimSun2Fix]");
  1216. allSuns=document.querySelectorAll("[data-LargeSimSun2Fix]:not([data-SimSunFixedCJK])");
  1217. for (isun=0;isun< allSuns.length;isun++) {
  1218. //if (isScrolling == true) {alert('trying to fix fonts, but in scrolling....');break;}
  1219. if (allSuns[isun].hasAttribute("data-SimSunFixedCJK")) {
  1220. continue;
  1221. }
  1222. font_str = dequote(window.getComputedStyle(allSuns[isun], null).getPropertyValue('font-family'));
  1223. if (font_str.match(re_simsun) && !(font_str.match(sig_sim)) ) {
  1224. allSuns[isun].style.fontFamily = font_str.replace(re_simsun,qLargeSimSun);
  1225. allSuns[isun].setAttribute("data-SimSunFixedCJK","");
  1226. }
  1227. }
  1228. //All elements to fix fonts: all = document.querySelectorAll("[data-CJK2Fix]");
  1229. all=document.querySelectorAll("[data-CJK2Fix]:not([data-FontsFixedCJK])");
  1230. if (ifRound1===true) {
  1231. for (i = 0; i < all.length; i++) {
  1232. //if (isScrolling == true) {alert('trying to fix fonts, but in scrolling....');break;}
  1233. if (i % 500===0) { //Check every 500 elements.
  1234. if ( useOverallTimeOut===true && (performance.now()-t_stop)*invForLimit > timeOut) {
  1235. ifRound1=false;
  1236. ifRound2=false;
  1237. ifRound3=false;
  1238. FixPunct=false;
  1239. processedAll=false;
  1240. console.log('FixCJK!: Round 1 itself has been running for '+((performance.now()-t_stop)/1000).toFixed(3)+' seconds. Too slow to continue.');
  1241. break;
  1242. }
  1243. else {
  1244. if (debug_verbose===true) {console.log('FixCJK!: Round 1 itself has been running for '+((performance.now()-t_stop)/1000).toFixed(3)+' seconds.');}
  1245. }
  1246. }
  1247. if (all[i].hasAttribute("data-FontsFixedCJK")) {
  1248. continue;
  1249. }
  1250. if ( ((all[i].closest('[lang]') && all[i].closest('[lang]').lang === 'ja') | docLang === 'ja') && unifiedCJK === false) {
  1251. continue;
  1252. }
  1253. child = all[i].firstChild;
  1254. if_replace = false;
  1255. //Only change if current node (not child node) contains CJK characters.
  1256. font_str = dequote(window.getComputedStyle(all[i], null).getPropertyValue('font-family'));
  1257. fweight = window.getComputedStyle(all[i], null).getPropertyValue('font-weight');
  1258. while (child) {
  1259. var realSibling=child.nextSibling;
  1260. if (child.nodeType == 3 && (child.data.match(re_extCJK)) && (fweight == 'bold' || fweight > 500) && (!(font_str.match(sig_bold)))) {
  1261. //Test if contains SimSun
  1262. if (debug_01===true) {all[i].style.color="Blue";} //Bold-->Blue;
  1263. //Test if contains Sans
  1264. if (list_has(font_str, re_sans0) !== false) {
  1265. if (debug_01===true) all[i].style.color="Salmon";
  1266. all[i].style.fontFamily = genPunct+','+ replace_font(font_str, re_sans0, LatinSans+','+qBold) + ',sans-serif';
  1267. window.setTimeout(function(node) {node.setAttribute("data-FontsFixedCJK","");},1,all[i]); //Slow and Use async I/O.
  1268. } //Test if contains serif
  1269. else if (list_has(font_str, re_serif) !== false) {
  1270. if (debug_01===true) all[i].style.color="SeaGreen";
  1271. all[i].style.fontFamily = genPunct+','+ replace_font(font_str, re_serif, LatinSerif + ',' +qBold) + ',serif';
  1272. window.setTimeout(function(node) {node.setAttribute("data-FontsFixedCJK","");},1,all[i]); //Slow and Use async I/O.
  1273. } //Test if contains monospace
  1274. else if (list_has(font_str, re_mono0) !== false) {
  1275. if (debug_01===true) all[i].style.color="Maroon";
  1276. all[i].style.fontFamily = genPunct+','+ replace_font(font_str, re_mono0, LatinMono + ',' +qBold) + ',monospace';
  1277. window.setTimeout(function(node) {node.setAttribute("data-FontsFixedCJK","");},1,all[i]); //Slow and Use async I/O.
  1278. } //Just append the fonts to the font preference list.
  1279. else {
  1280. all[i].style.fontFamily = genPunct+','+font_str + ',' + LatinSans + ',' + qBold + ',' + ' sans-serif';
  1281. window.setTimeout(function(node) {node.setAttribute("data-FontsFixedCJK","");},1,all[i]); //Slow and Use async I/O.
  1282. }
  1283. }
  1284. child = realSibling;
  1285. }
  1286. }
  1287. }
  1288. if (FixRegular === false) {
  1289. return false;
  1290. }
  1291. /// ===== Second Round: Deal with regular weight. ===== ///
  1292. var tmp_idx=0;
  1293. max = all.length;
  1294. if (useOverallTimeOut===true && (performance.now()-t_stop)*4 > timeOut) {
  1295. ifRound2=false;
  1296. ifRound3=false;
  1297. FixPunct=false;
  1298. processedAll=false;
  1299. console.log('FixCJK!: Round 1 has been running for '+((performance.now()-t_stop)/1000).toFixed(3)+' seconds. Skipping following steps.');
  1300. }
  1301. t_stop=performance.now();
  1302. if (ifRound2===true) {
  1303. //Now fix the rest.
  1304. for (i = 0; i < all.length; i++) {
  1305. //if (isScrolling == true) {alert('trying to fix fonts, but in scrolling....');break;}
  1306. if (i % 500===0) { //Check every 500 elements.
  1307. if (useOverallTimeOut===true && (performance.now()-t_stop)*invForLimit > timeOut) {
  1308. ifRound2=false;
  1309. ifRound3=false;
  1310. FixPunct=false;
  1311. processedAll=false;
  1312. console.log('FixCJK!: Round 2 itself has been running for '+((performance.now()-t_stop)/1000).toFixed(3)+' seconds. Too slow to continue.');
  1313. break;
  1314. }
  1315. else {
  1316. if (debug_verbose===true) {console.log('FixCJK!: Round 2 itself has been running for '+((performance.now()-t_stop)/1000).toFixed(3)+' seconds.');}
  1317. }
  1318. }
  1319. if (all[i].hasAttribute("data-FontsFixedCJK") ) {
  1320. continue;
  1321. }
  1322. font_str = dequote(window.getComputedStyle(all[i], null).getPropertyValue('font-family'));
  1323. fweight = window.getComputedStyle(all[i], null).getPropertyValue('font-weight');
  1324. 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)) {
  1325. window.setTimeout(function(node) {node.setAttribute("data-FontsFixedCJK","");},1,all[i]); //Slow and Use async I/O.
  1326. continue;
  1327. }
  1328. else {
  1329. if (debug_02===true) {all[i].style.color='Teal';} //Teal for true;
  1330. 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);}}
  1331. //Test if contains Sans
  1332. if (list_has(font_str, re_sans0) !== false) {
  1333. //all[i].style.color="Salmon";
  1334. if ( ((all[i].closest('[lang]') && all[i].closest('[lang]').lang === 'ja') | docLang === 'ja') && unifiedCJK === false)
  1335. all[i].style.fontFamily = genPunct+','+replace_font(font_str, re_sans0, qsans_ja);
  1336. else
  1337. all[i].style.fontFamily = genPunct+','+replace_font(font_str, re_sans0, qsans);
  1338. window.setTimeout(function(node) {node.setAttribute("data-FontsFixedCJK","");},1,all[i]); //Slow and Use async I/O.
  1339. } //Test if contains serif
  1340. else if (list_has(font_str, re_serif) !== false) {
  1341. //all[i].style.color="SeaGreen";
  1342. if ( ((all[i].closest('[lang]') && all[i].closest('[lang]').lang === 'ja') | docLang === 'ja') && unifiedCJK === false)
  1343. all[i].style.fontFamily = genPunct+','+replace_font(font_str, re_serif, qserif_ja);
  1344. else
  1345. all[i].style.fontFamily = genPunct+','+replace_font(font_str, re_serif, qserif);
  1346. window.setTimeout(function(node) {node.setAttribute("data-FontsFixedCJK","");},1,all[i]); //Slow and Use async I/O.
  1347. } //Test if contains monospace
  1348. else if (list_has(font_str, re_mono0) !== false) {
  1349. //all[i].style.color="Maroon";
  1350. all[i].style.fontFamily = genPunct+','+replace_font(font_str, re_mono0, qmono);
  1351. window.setTimeout(function(node) {node.setAttribute("data-FontsFixedCJK","");},1,all[i]); //Slow and Use async I/O.
  1352. }
  1353. else {
  1354. if (debug_02===true) {all[i].style.color='Fuchsia';}
  1355. if (font_str.match(re_simsun)) {
  1356. //Do nothing.
  1357. }
  1358. else {
  1359. if ( ((all[i].closest('[lang]') && all[i].closest('[lang]').lang === 'ja') | docLang === 'ja') && unifiedCJK === false)
  1360. all[i].style.fontFamily = genPunct+','+font_str + ',' + qCJK_ja + ',' + 'sans-serif';
  1361. else
  1362. all[i].style.fontFamily = genPunct+','+font_str + ',' + qCJK + ',' + 'sans-serif';
  1363. window.setTimeout(function(node) {node.setAttribute("data-FontsFixedCJK","");},1,all[i]); //Slow and Use async I/O.
  1364. }
  1365. }
  1366. }
  1367. }
  1368. }
  1369. if (debug_verbose===true) {console.log('FixCJK!: Round 2 took '+((performance.now()-t_stop)/1000).toFixed(3)+' seconds.');}
  1370. t_stop=performance.now();
  1371. if (debug_02===true) console.log('Just before Round 3:'+tmp_idx.toString()+'::'+all[tmp_idx].innerHTML);
  1372. if (debug_02===true) console.log('Just before Round 3:'+tmp_idx.toString()+'::'+dequote(window.getComputedStyle(all[tmp_idx], null).getPropertyValue('font-family')));
  1373. /// ===== The Third round (Round 3): Add CJKdefault to all elements ===== ///
  1374. if (FixPureLatin === false) {
  1375. t_stop=performance.now();
  1376. if (debug_verbose===true) {console.log('FixCJK!: FixPureLatin/Round 3 is intentionally skipped.');}
  1377. return false;
  1378. }
  1379. all=document.querySelectorAll(":not([data-FontsFixedCJK])");
  1380. max = all.length;
  1381. if (max > maxNumElements) {
  1382. ifRound3=false;
  1383. FixPunct=false;
  1384. processedAll=false;
  1385. console.log('FixCJK!: '+max.toString()+' elements, too many. Skip Round 3 and punctuation fixing. Exiting now...');
  1386. }
  1387. else if (max > CJKOnlyThreshold) {
  1388. if (FixPureLatin === true)
  1389. ifRound3=true;
  1390. //FixPunct=true;
  1391. processedAll=true;
  1392. //Now get all elements to be fixed: all = document.getElementsByTagName('CJK2Fix');
  1393. all=document.querySelectorAll("[data-CJK2Fix]:not([data-FontsFixedCJK])");
  1394. console.log('FixCJK!: '+max.toString()+' elements, too many. Only CJK elements will be processed in Round 3.');
  1395. }
  1396. else {
  1397. if (debug_verbose===true) {console.log('FixCJK!: All elements will be processed in Round 3.');}
  1398. }
  1399. /*
  1400. if (ifRound3===true) {
  1401. for (i = 0; i < all.length; i++) {
  1402. if (i % 500===0) { //Check every 500 elements.
  1403. if (useOverallTimeOut===true && (performance.now()-t_stop)*invForLimit > timeOut) {
  1404. ifRound3=false;
  1405. FixPunct=false;
  1406. processedAll=false;
  1407. 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:');
  1408. console.log(all[i]);
  1409. break;
  1410. }
  1411. else {
  1412. if (debug_verbose===true) {console.log('FixCJK!: Round 3 itself has been running for '+((performance.now()-t_stop)/1000).toFixed(3)+' seconds. ');}
  1413. }
  1414. }
  1415. if (all[i].nodeName.match(SkippedTags) || (!(!all[i].getAttribute("lang")) && all[i].getAttribute("lang").match(SkippedLangs) ) || (all[i] instanceof SVGElement) ) {
  1416. if (debug_03===true) {console.log("Skipped:");console.log(all[i]);}
  1417. continue;
  1418. }
  1419. else if (all[i].hasAttribute("data-FontsFixedCJK")) {
  1420. if (debug_03===true) all[i].style.color="FireBrick"; //FireBrick <-- Fixed.
  1421. continue;
  1422. }
  1423. font_str = dequote(window.getComputedStyle(all[i], null).getPropertyValue('font-family'));
  1424. if (font_str.split(',').length >= rspLength) {
  1425. //continue if all[i] contains a list of fonts.
  1426. if (all[i].hasAttribute("data-CJKTestedAndLabeled") && !all[i].hasAttribute("data-CJK2Fix")) {
  1427. window.setTimeout(function(node) {node.setAttribute("data-FontsFixedCJK","");},1,all[i]); //Slow and Use async I/O.
  1428. if (debug_03===true) {console.log(all[i]);all[i].style.color="FireBrick";} //FireBrick <-- Fixed.
  1429. continue;
  1430. }
  1431. }
  1432. if (!(font_str.match(sig_song) || font_str.match(sig_hei) || font_str.match(sig_bold) || font_str.match(sig_default) || font_str.match(/FixedPMSans/))) {
  1433. if (list_has(font_str, re_sans0) !== false) {
  1434. if (debug_03 === true) all[i].style.color="Salmon";
  1435. if ( ((all[i].closest('[lang]') && all[i].closest('[lang]').lang === 'ja') | docLang === 'ja') && unifiedCJK === false)
  1436. all[i].style.fontFamily = genPunct+','+replace_font(font_str, re_sans0, qsans_ja);
  1437. else
  1438. all[i].style.fontFamily = genPunct+','+replace_font(font_str, re_sans0, qsans);
  1439. } //Test if contains serif
  1440. else if (list_has(font_str, re_serif) !== false) {
  1441. if (debug_03 === true) all[i].style.color="SeaGreen";
  1442. if ( ((all[i].closest('[lang]') && all[i].closest('[lang]').lang === 'ja') | docLang === 'ja') && unifiedCJK === false)
  1443. all[i].style.fontFamily = genPunct+','+replace_font(font_str, re_serif, qserif_ja);
  1444. else
  1445. all[i].style.fontFamily = genPunct+','+replace_font(font_str, re_serif, qserif);
  1446. } //Test if contains monospace
  1447. else if (list_has(font_str, re_mono0) !== false) {
  1448. if (debug_03 === true) all[i].style.color="Maroon"; //Maroon
  1449. all[i].style.fontFamily = genPunct+','+replace_font(font_str, re_mono0, qmono);
  1450. }
  1451. else {
  1452. //SimSun should be taken care of throught the "SimSun2Fix" class.
  1453. if (debug_03 === true) { all[i].style.color='Olive';}
  1454. if ( ((all[i].closest('[lang]') && all[i].closest('[lang]').lang === 'ja') | docLang === 'ja') && unifiedCJK === false)
  1455. all[i].style.fontFamily = genPunct+','+font_str + ',' + qCJK_ja + ',' + 'sans-serif';
  1456. else
  1457. all[i].style.fontFamily = genPunct+','+font_str + ',' + qCJK + ',' + 'sans-serif';
  1458. }
  1459. }
  1460. else {
  1461. //font_str already contains signature.
  1462. if (debug_03 === true) all[i].style.color="Silver"; //Signed-->Silver
  1463. }
  1464. window.setTimeout(function(node) {node.setAttribute("data-FontsFixedCJK","");node.setAttribute("data-CJKTestedAndLabeled","");},1,all[i]); //Slow and Use async I/O.
  1465. if (debug_03===true)
  1466. all[i].style.color="FireBrick"; //FireBrick <-- Fixed.
  1467. }
  1468. }
  1469. */
  1470. //Skip Round3 (fix fonts for Latin as well) to increase speed and compatibility, now hard-coded.
  1471. //setTimeout(function(){ fontsCheck(); }, 30);
  1472. if (debug_verbose===true) {console.log('FixCJK!: Round 3 took '+((performance.now()-t_stop)/1000).toFixed(3)+' seconds.');}
  1473. t_stop=performance.now();
  1474. if (debug_wrap===true) console.log("Fixing Fonts took "+((performance.now()-func_start)/1000).toFixed(3)+" seconds.");
  1475. }
  1476. ///===The Actual Round 4===///
  1477. function FunFixPunct(useOverallTimeOut,MaxNumLoops,returnNow) {
  1478. var funcTimeOut = 300; //in ms.
  1479. if (FixPunct === false) {
  1480. console.log('No PM will be fixed.');
  1481. return true;
  1482. }
  1483. SkippedTags=SkippedTagsForMarks;
  1484. var recursion_start=0;
  1485. var func_start=performance.now();
  1486. //Use Recursion instead of loop, should be put in the MaxNumLoops in production code.
  1487. if (returnNow===true) {
  1488. return true;
  1489. }
  1490. var allrecur=document.querySelectorAll("[data-PunctSpace2Fix]:not([data-MarksFixedE135])");
  1491. for (var ir=0; ir<allrecur.length; ir++) {
  1492. //if (isScrolling == true) {alert('trying to fix punct, but in scrolling....');break;}
  1493. //Seems no need to add !(allrecur[ir].parentNode.hasAttribute("data-CJK2Fix")). It might be faster to fix the deepest element first through looping.
  1494. recursion_start=performance.now();
  1495. if (allrecur[ir].nodeName.match(/CJKTEXT/i)) {
  1496. FixPunctRecursion(allrecur[ir]);
  1497. }
  1498. if ( (useOverallTimeOut===true && (performance.now()-t_start) > timeOut) | (performance.now()-func_start) > funcTimeOut ) {
  1499. processedAll=false;
  1500. console.log("FIXME: FunFixPunct Time out. Last fixing took "+((performance.now()-recursion_start)/1000).toFixed(3)+" seconds.");
  1501. console.log("FIXME:"+allrecur[ir].nodeName+"."+allrecur[ir].className);
  1502. break;
  1503. }
  1504. }
  1505. if (debug_wrap===true || debug_asyncTimers===true) console.log("FixCJK!: Fixing PMs took "+((performance.now()-func_start)/1000).toFixed(3)+" seconds.");
  1506. }
  1507. /////=====The Recursive Implementation=====/////
  1508. function FixPunctRecursion(node) {
  1509. //if (isScrolling == true) {alert('trying to fix punct recursively, but in scrolling....');return false;}
  1510. if (node.hasAttribute("data-MarksFixedE135")) {
  1511. return true;
  1512. }
  1513. else if ( !(node.tagName.match(/CJKTEXT/i)) ) {
  1514. return false;
  1515. }
  1516. 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));}
  1517. var tabooedTags=SkippedTagsForMarks;
  1518. var child=node.firstChild;
  1519. var currHTML="";
  1520. var allSubSafe=true;
  1521. var node2fix=true;
  1522. if ((node.nodeName.match(tabooedTags)) || inTheClassOf(node,noWrappingClasses)) {
  1523. //Although BODY is tabooed, this is OK because a loop is outside this recursive implementation.
  1524. node.removeAttribute("data-Safe2FixCJK");
  1525. node.removeAttribute("data-PunctSpace2Fix");
  1526. node.setAttribute("data-MarksFixedE135","");
  1527. return false;
  1528. }
  1529. //Add lang attibute. Firefox cannot detect lang=zh automatically and it will treat CJK characters as letters if no lang=zh. For example,
  1530. //the blank spaces will be streched but not the "character-spacing" if using align=justify.
  1531. if (window.getComputedStyle(node.parentNode,null).getPropertyValue('text-align').match(/start/) && useJustify===true) {
  1532. node.parentNode.style.textAlign="justify";
  1533. if ( !node.parentNode.tagName.match(/(TD|TR|TBODY)/) && !(!document.URL.match(/zhihu.com/)) )
  1534. if ( node.parentNode.hasAttribute("data-RichText") )
  1535. node.parentNode.style.whiteSpace="normal"; //Need to reset white-space to make justify effective. However it will mess up the tables!
  1536. }
  1537. //node.lang="zh";
  1538. if (node.parentNode.getAttribute('lang') === 'zh') {
  1539. //do nothing if it is lang=zh;
  1540. }
  1541. else if ((node.closest('[lang]') && node.closest('[lang]').lang === 'ja') | docLang==='ja') {
  1542. // if the parentNode.parentNode of the <cjktext> node is 'ja', for example, <p lang='ja'><b><cjktext>.
  1543. // It won't affect fonts anyway.
  1544. node.parentNode.lang='ja';
  1545. }
  1546. else
  1547. node.parentNode.lang="zh";
  1548. while (child) {
  1549. if (debug_re_to_check===true && (node.innerHTML.match(re_to_check))) {console.log("Checking subnode: "+child+"@"+node.nodeName);}
  1550. if ( child.nodeType === 3 && !(node.nodeName.match(tabooedTags)) ) {
  1551. 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);}
  1552. if (debug_verbose===true) {
  1553. console.log("Permitted to check: "+node.nodeName+"."+node.className);
  1554. }
  1555. if (debug_re_to_check===true && (node.innerHTML.match(re_to_check)) && node.nodeName.match(tabooedTags)) {
  1556. console.log("ERROR: Wrong Operation on: "+node.nodeName+"."+node.className+":: "+node.textContent);
  1557. console.log("ERROR: Wrong Operation because: "+child.data);
  1558. }
  1559. }
  1560. if (child.nodeType===1 && !(child instanceof SVGElement)) {
  1561. if ((child.nodeName.match(tabooedTags) ) || inTheClassOf(child,noWrappingClasses) ) {
  1562. //was like this: if (child.nodeName.match(tabooedTags) || child.hasAttribute("data-MarksFixedE135")) {. I don't know why.
  1563. child.removeAttribute("data-Safe2FixCJK");
  1564. child.removeAttribute("data-CJK2Fix");
  1565. child.setAttribute("data-MarksFixedE135","");
  1566. node2fix=false;
  1567. }
  1568. else if (child.nodeName.match(ignoredTags)) {
  1569. //Simply do nothing. Such as <math> tag.
  1570. child.setAttribute("data-Safe2FixCJK","\uE000");
  1571. child.setAttribute("data-MarksFixedE135","");
  1572. }
  1573. else if (child.hasAttribute("data-MarksFixedE135")) {
  1574. //Fixed, do nothing.
  1575. }
  1576. else if (child.nodeName.match(/CJKTEXT/)) {
  1577. FixPunctRecursion(child); //This is the recursion part. Not really recursion after 0.15....
  1578. }
  1579. else {
  1580. //do nothing;
  1581. }
  1582. //Test again after fixing child:
  1583. if (!(child.hasAttribute("data-Safe2FixCJK"))) {allSubSafe=false;} //\uE000 is Tux in Linux Libertine.
  1584. }
  1585. child=child.nextSibling;
  1586. }
  1587. if (allSubSafe===true && (!(node instanceof SVGElement))) {
  1588. //label Safe to Fix
  1589. //var CJKclasses=CJKAttrList.split(',');
  1590. //for (var icl=0;icl<CJKclasses.length;icl++) {
  1591. // node.removeAttribute('data-'+CJKclasses[icl]);
  1592. //}
  1593. if (node.classList.length===0 && node.id.length ===0 && !(node.nodeName.match(tabooedTags)) && !(inTheClassOf(node,noWrappingClasses))) {
  1594. //It would be crazy if add listeners just by tags.
  1595. //node.className=orig_class;
  1596. node.setAttribute("data-Safe2FixCJK","");
  1597. }
  1598. //else {
  1599. // node.className=orig_class;
  1600. //}
  1601. }
  1602. //Config and Filtering Done. Fix puncts if necessary.
  1603. if (allSubSafe===true && node2fix===true && !(node.nodeName.match(tabooedTags)) && !(inTheClassOf(node,noWrappingClasses)) && node.hasAttribute("data-CJK2Fix") && !(node.hasAttribute("data-MarksFixedE135"))) {
  1604. if (debug_verbose===true) console.log("USING Recursion: "+node.nodeName+'.'+node.className);
  1605. if (debug_verbose===true) { console.log("WARNING: Danger Operation on: "+node.nodeName+"."+node.className+":: "+node.innerHTML.slice(0,216)); }
  1606. if (debug_re_to_check===true && (node.innerHTML.match(re_to_check))) {console.log("Checking if contain punctuations to fix");}
  1607. if (node.innerHTML.match(/[“”‘’、,。:;!?)】〉》」』『「《〈【(]/m)) {
  1608. if (debug_re_to_check===true && (node.innerHTML.match(re_to_check))) { console.log("WARNING: Danger Operation on: "+node.nodeName+"."+node.className);}
  1609. if (node.hasAttribute("data-preCode")) {
  1610. node.removeAttribute("data-Safe2FixCJK"); //Do not performan fixing on "fully banned" tags.
  1611. node.removeAttribute("data-PunctSpace2Fix");
  1612. }
  1613. else if (window.getComputedStyle(node, null).getPropertyValue("white-space").match(/pre/)){
  1614. node.innerHTML=FixMarksInCurrHTML(node.innerHTML,node,false);
  1615. }
  1616. else {
  1617. 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));}
  1618. node.innerHTML=FixMarksInCurrHTML(node.innerHTML,node,true);
  1619. }
  1620. }
  1621. node.setAttribute("data-MarksFixedE135","");
  1622. return true;
  1623. }
  1624. else {
  1625. node.setAttribute("data-MarksFixedE135","");
  1626. return true;
  1627. }
  1628. }
  1629. ///==Fix punct in a currHTML===///
  1630. function FixMarksInCurrHTML(currHTML,node,delete_all_extra_spaces) {
  1631. //“<-->\u201C, ”<-->\u201D
  1632. //‘<-->\u2018, ’<-->\u2019
  1633. if (debug_04===true) console.log("Round 4: Fixing node <"+node.nodeName+">");
  1634. var changhai_style=false;
  1635. var Squeezing=true;
  1636. var SqueezeInd=true;
  1637. var tmp_str='';
  1638. var FixMarks_start=performance.now();
  1639. var inputHTML=currHTML;
  1640. var continuedHTML='';
  1641. if (changhai_style===true) {
  1642. //Simply inserting blanck space, like changhai.org.
  1643. currHTML=currHTML.replace(/([\u3000-\u30FF\u3400-\u9FBF\uFF00-\uFFEF]?)([“‘])([\u3400-\u9FBF\u3000-\u303F\uFF00-\uFFEF]+)/g,'$1 $2$3');
  1644. currHTML=currHTML.replace(/([\u3000-\u30FF\u3400-\u9FBF\uFF00-\uFFEF])([”’])([^,, ])/g,'$1$2 $3');
  1645. if (debug_04===true) {console.log(currHTML);}
  1646. node.innerHTML=currHTML;
  1647. return true;
  1648. }
  1649. //currHTML=currHTML.replace(/^([、,。:;!?)】〉》」』\u201D\u2019])/mg,'\u2060$1');//Remove the hanging puncts. Seems no use at all.
  1650. //It seems that I always needs to consider the context.
  1651. if ( currHTML.match(/[“”‘’]/) || currHTML.match(/^[、,。:;!?)】〉》」』『「《〈【(]/) || currHTML.match(/[、,。:;!?)】〉》」』『「《〈【(]$/) ) {
  1652. if (debug_tagSeeThrough===true) console.log("TO CHECK BEFORE: "+currHTML);
  1653. if (debug_tagSeeThrough===true) console.log("FULL PARENT: "+node.parentNode.textContent);
  1654. currHTML=getBefore(node)+'\uF201CJK\uF201'+currHTML+'\uF202CJK\uF202'+getAfter(node);
  1655. continuedHTML=currHTML;
  1656. if (debug_tagSeeThrough===true) console.log("FULL CLOSED FORM: "+currHTML);
  1657. if (debug_tagSeeThrough===true) console.log("Continuation took "+(performance.now()-FixMarks_start).toFixed(1)+" ms.");
  1658. }
  1659. if (useFeedback===true && currHTML.match(/[\uF201-\uF204]/)) {
  1660. if (!currHTML.match(/[\uF201-\uF204]CJK[\uF201-\uF204]/) || (currHTML.match(/[\uF201-\uF204]/g)).length !== ((currHTML.match(/[\uF201-\uF204]CJK[\uF201-\uF204]/g)).length*2)) {
  1661. 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。多谢您的使用与反馈!");
  1662. }
  1663. }
  1664. function getBefore(child) {
  1665. var t_start=performance.now();
  1666. var toReturn='';
  1667. var inputNode=child;
  1668. if (debug_getBeforeTags===true) {
  1669. console.log("CURRENT: "+child.nodeName+"@<"+child.parentNode.nodeName+">");
  1670. console.log(child.parentNode.nodeName.match(upEnoughTags));
  1671. }
  1672. if (debug_tagSeeThrough===true) console.log('CHILD<'+child.nodeName+'>: '+child.textContent+'<--'+'PARENT<'+child.parentNode.nodeName+">: "+child.parentNode.textContent);
  1673. child=child.previousSibling;
  1674. while (child && (performance.now()-t_start<2) ) {
  1675. if (child.nodeType===3) {
  1676. if (debug_tagSeeThrough===true) console.log("T3: "+child.data);
  1677. toReturn = child.data + toReturn;
  1678. if (toReturn.length>1024 || toReturn.match(/[\u3040-\u30FF\u3400-\u9FBF]/) ) {
  1679. //Stop if to Return is already too long;
  1680. return toReturn;
  1681. }
  1682. }
  1683. else if (child.nodeType===1 && window.getComputedStyle(child,null).getPropertyValue("display")!=="none" ) {
  1684. if (child.nodeName.match(stopTags) || inTheClassOf(child,stopClasses) ) {
  1685. return '上下标'+toReturn;
  1686. }
  1687. if (debug_tagSeeThrough===true) console.log("T1: "+child.textContent);
  1688. toReturn = displayedText(child) + toReturn;
  1689. if (toReturn.length>1024 || toReturn.match(/[\u3040-\u30FF\u3400-\u9FBF]/) ) {
  1690. //Stop if to Return is already too long;
  1691. return toReturn;
  1692. }
  1693. }
  1694. child=child.previousSibling;
  1695. }
  1696. if (debug_tagSeeThrough===true) console.log("BEFORE: "+toReturn+"@"+performance.now());
  1697. if (debug_tagSeeThrough===true) console.log("CJK? "+toReturn.match(/[\u3040-\u30FF\u3400-\u9FBF]/)+'@'+toReturn);
  1698. if ((toReturn.length < 1 || !toReturn.match(/[\u3040-\u30FF\u3400-\u9FBF]/)) && (!inputNode.parentNode.nodeName.match(upEnoughTags)) ) {
  1699. return getBefore(inputNode.parentNode)+toReturn;
  1700. }
  1701. return (toReturn.replace(/</,'&lt;')).replace(/>/,'&gt;');
  1702. }
  1703. function getAfter(child) {
  1704. var toReturn='';
  1705. var t_start=performance.now();
  1706. var inputNode=child;
  1707. child=child.nextSibling;
  1708. while (child && (performance.now()-t_start<2) ) {
  1709. if (child.nodeType===3) {
  1710. toReturn = toReturn + child.data;
  1711. if (toReturn.length>1024 || toReturn.match(/[\u3040-\u30FF\u3400-\u9FBF]/) ) {
  1712. //Stop if to Return is already too long;
  1713. return toReturn;
  1714. }
  1715. }
  1716. else if (child.nodeType===1 && window.getComputedStyle(child,null).getPropertyValue("display")!=="none" ) {
  1717. if (child.nodeName.match(stopTags) || inTheClassOf(child,stopClasses) ) {
  1718. return toReturn+'上下标'; //I just need to add some CJK text. They will be "chopped" anyway.
  1719. }
  1720. toReturn = toReturn + displayedText(child);
  1721. if (toReturn.length>1024 || toReturn.match(/[\u3040-\u30FF\u3400-\u9FBF]/) ) {
  1722. //Stop if to Return is already too long;
  1723. return toReturn;
  1724. }
  1725. }
  1726. child=child.nextSibling;
  1727. }
  1728. if (debug_tagSeeThrough===true) console.log("AFTER: "+toReturn+"@"+performance.now());
  1729. if ((toReturn.length < 1 || !toReturn.match(/[\u3040-\u30FF\u3400-\u9FBF]/)) && (!inputNode.parentNode.nodeName.match(upEnoughTags)) ) {
  1730. return toReturn+getAfter(inputNode.parentNode);
  1731. }
  1732. return (toReturn.replace(/</,'&lt;')).replace(/>/,'&gt;');
  1733. }
  1734. //==We need to protect the quotation marks within tags first===//
  1735. // \uE862,\uE863 <==> ‘,’
  1736. // \uE972,\uE973 <==> “,”
  1737. while (currHTML.match(/<[^>]*[“”‘’、,。:;!?)】〉》」』『「《〈【(][^<]*>/m)) {
  1738. currHTML=currHTML.replace(/(<[^>]*)‘([^<]*>)/mg,'$1\uE862$2');
  1739. currHTML=currHTML.replace(/(<[^>]*)’([^<]*>)/mg,'$1\uE863$2');
  1740. currHTML=currHTML.replace(/(<[^>]*)“([^<]*>)/mg,'$1\uE972$2');
  1741. currHTML=currHTML.replace(/(<[^>]*)”([^<]*>)/mg,'$1\uE973$2');
  1742. currHTML=currHTML.replace(/(<[^>]*)、([^<]*>)/mg,'$1\uEA01$2');
  1743. currHTML=currHTML.replace(/(<[^>]*),([^<]*>)/mg,'$1\uEA02$2');
  1744. currHTML=currHTML.replace(/(<[^>]*)。([^<]*>)/mg,'$1\uEA03$2');
  1745. currHTML=currHTML.replace(/(<[^>]*):([^<]*>)/mg,'$1\uEA04$2');
  1746. currHTML=currHTML.replace(/(<[^>]*);([^<]*>)/mg,'$1\uEA05$2');
  1747. currHTML=currHTML.replace(/(<[^>]*)!([^<]*>)/mg,'$1\uEA06$2');
  1748. currHTML=currHTML.replace(/(<[^>]*)?([^<]*>)/mg,'$1\uEA07$2');
  1749. currHTML=currHTML.replace(/(<[^>]*))([^<]*>)/mg,'$1\uEA08$2');
  1750. currHTML=currHTML.replace(/(<[^>]*)】([^<]*>)/mg,'$1\uEA09$2');
  1751. currHTML=currHTML.replace(/(<[^>]*)〉([^<]*>)/mg,'$1\uEA10$2');
  1752. currHTML=currHTML.replace(/(<[^>]*)》([^<]*>)/mg,'$1\uEA11$2');
  1753. currHTML=currHTML.replace(/(<[^>]*)」([^<]*>)/mg,'$1\uEA12$2');
  1754. currHTML=currHTML.replace(/(<[^>]*)』([^<]*>)/mg,'$1\uEA13$2');
  1755. currHTML=currHTML.replace(/(<[^>]*)『([^<]*>)/mg,'$1\uEA14$2');
  1756. currHTML=currHTML.replace(/(<[^>]*)「([^<]*>)/mg,'$1\uEA15$2');
  1757. currHTML=currHTML.replace(/(<[^>]*)《([^<]*>)/mg,'$1\uEA16$2');
  1758. currHTML=currHTML.replace(/(<[^>]*)〈([^<]*>)/mg,'$1\uEA17$2');
  1759. currHTML=currHTML.replace(/(<[^>]*)【([^<]*>)/mg,'$1\uEA18$2');
  1760. currHTML=currHTML.replace(/(<[^>]*)(([^<]*>)/mg,'$1\uEA19$2');
  1761. }
  1762. var time2protect=performance.now()-FixMarks_start;
  1763. //Now let's fix the punctions.
  1764. //First we need to fix the "reverse-paired" punctuations.
  1765. var fixpair=false; //the current code has problems if unpaired quotation marks are present.
  1766. var fixpair_timeout = noBonusTimeout; //Don't spend too much time on this "bonus" function.
  1767. var fixpair_start=performance.now();
  1768. if ( currHTML.length > noBonusLength ) {fixpair=false;}
  1769. if (debug_re_to_check===true && (currHTML.match(re_to_check))) {console.log("Reversing "+currHTML);}
  1770. if (fixpair===true) { //[\w,./<>?;:[]\{}|`~!@#$%^&*()_+-=]*
  1771. var revpaired=/(^[^\u201C\u201D]?(?:[^\u201C\u201D]*\u201C[^\u201C\u201D]*\u201D)*[^\u201C\u201D]*)\u201D([^\u201C\u201D]{2,})\u201C/;
  1772. while (currHTML.match(revpaired) && (performance.now()-fixpair_start)<fixpair_timeout ) {
  1773. if (debug_re_to_check===true && currHTML.match(re_to_check)) {console.log("Pair reversed: "+(performance.now()-t_start).toString());}
  1774. currHTML=currHTML.replace(revpaired,'$1\u201C$2\u201D');
  1775. }
  1776. }
  1777. var fixpair_stop=performance.now()-fixpair_start;
  1778. var paired_start=performance.now();
  1779. //Find and preserve paired Latin marks.
  1780. var paired=/(\u201C)([^\u3000-\u30FF\u3400-\u9FBF\uE000-\uED00\uFF00-\uFFEF]*)(\u201D)/mg;
  1781. while (currHTML.match(paired)) {
  1782. if (debug_re_to_check===true && currHTML.match(re_to_check)) console.log("Quotation mark pair found@"+currHTML);
  1783. currHTML=currHTML.replace(paired,'\uEC1C$2\uEC1D');
  1784. }
  1785. //Paired single quotations are diffcult because of the double-indentiy of '‘'.
  1786. paired=/(\u2018)([^\u3000-\u30FF\u3400-\u9FBF\uE000-\uED00\uFF00-\uFFEF]{0,2})(\u2019)/mg;
  1787. while (currHTML.match(paired)) {
  1788. if (debug_re_to_check===true && currHTML.match(re_to_check)) console.log("Quotation mark pair found@"+currHTML);
  1789. currHTML=currHTML.replace(paired,'\uEC18$2\uEC19');
  1790. }
  1791. //Find paired CJK marks. Seems like O(n^2) without the "g" modifier?
  1792. paired=/(\u201C)([^\u201D]*[\u3000-\u30FF\u3400-\u9FBF\uFF00-\uFFEF][^\u201D]*)(\u201D)/mg;
  1793. while (currHTML.match(paired)) {
  1794. currHTML=currHTML.replace(paired,'\uEB1C$2\uEB1D');
  1795. }
  1796. var paired_stop=performance.now()-paired_start;
  1797. //"unpaired \u201C or \u201D", not just use at the beginning of a paragraph.
  1798. var unpaired_timeout = noBonusTimeout; //not so important, therefore cannot spend too much time here.
  1799. var unpaired_start=performance.now();
  1800. //Note that to make "delete_all_extra_spaces" possible, we must take "spaces before and after QMs" into account.
  1801. var unpaired=/\u201C((?:[\r\n\u0020\u00A0\u2009]|&nbsp;){0,1}(?:[\uF202\uF204]CJK[\uF202\uF204])?[^\u201D\u3400-\u9FBF\uF201-\uF204]{0,5}(?:[\uF202\uF204]CJK[\uF202\uF204])?[\u3000-\u30FF\u3400-\u9FBF\uFF00-\uFFEF][^u201D]*$)/m;
  1802. //Before 1.3.4. Why [^\u201C\u201D]*$ ?
  1803. //var unpaired=/\u201C((?:[\r\n\u0020\u00A0\u2009]|&nbsp;){0,1}(?:[\uF202\uF204]CJK[\uF202\uF204])?[^\u201D\u3400-\u9FBF\uF201-\uF204]{0,5}(?:[\uF202\uF204]CJK[\uF202\uF204])?[\u3000-\u30FF\u3400-\u9FBF\uFF00-\uFFEF][^\u201C\u201D]*$)/m;
  1804. while ( currHTML.length< noBonusLength && currHTML.match(unpaired) && (performance.now()-unpaired_start)<unpaired_timeout) {
  1805. currHTML=currHTML.replace(unpaired,'\uEB1C$1'); //We need the greedy method to get the longest match.
  1806. }
  1807. //Before 1.3.4. Why ^[^\u201C\u201D] ?
  1808. //unpaired=/(^[^\u201C\u201D]*[\u3000-\u30FF\u3400-\u9FBF\uFF00-\uFFEF](?:[\uF201\uF203]CJK[\uF201\uF203])?[^\u201D\u3400-\u9FBF\uF201-\uF204]{0,5}(?:[\uF201\uF203]CJK[\uF201\uF203])?(?:[\r\n\u0020\u00A0\u2009]|&nbsp;){0,1})\u201D/m;
  1809. unpaired=/(^[^\u201C]*[\u3000-\u30FF\u3400-\u9FBF\uFF00-\uFFEF](?:[\uF201\uF203]CJK[\uF201\uF203])?[^\u201D\u3400-\u9FBF\uF201-\uF204]{0,5}(?:[\uF201\uF203]CJK[\uF201\uF203])?(?:[\r\n\u0020\u00A0\u2009]|&nbsp;){0,1})\u201D/m;
  1810. while ( currHTML.length< noBonusLength && currHTML.match(unpaired) && (performance.now()-unpaired_start)<unpaired_timeout) {
  1811. currHTML=currHTML.replace(unpaired,'$1\uEB1D'); //We need the greedy method to get the longest match.
  1812. }
  1813. //For single quotations:
  1814. var paired_single_start=performance.now();
  1815. paired=/(\u2018)([^\u2019]*[\u3000-\u30FF\u3400-\u9FBF\uFF00-\uFFEF][^\u2019]*)(\u2019)/mg;
  1816. while (currHTML.match(paired)) {
  1817. currHTML=currHTML.replace(paired,'\uEB18$2\uEB19');
  1818. }
  1819. var paired_single_stop=performance.now()-paired_single_start;
  1820. //"unpaired ‘ (\u2018)", not just use at the beginning of a paragraph.
  1821. unpaired_start=performance.now();
  1822. unpaired=/\u2018((?:[\uF201-\uF204]CJK[\uF201-\uF204])?[^\u201D\u3400-\u9FBF\uF201-\uF204]{0,3}(?:[\uF201-\uF204]CJK[\uF201-\uF204])?[\u3000-\u30FF\u3400-\u9FBF\uFF00-\uFFEF][^\u2018\u2019]*$)/m;
  1823. while ( currHTML.length< noBonusLength && currHTML.match(unpaired) && (performance.now()-unpaired_start)<unpaired_timeout) {
  1824. currHTML=currHTML.replace(unpaired,'\uEB18$1'); //We need the greedy method to get the longest match.
  1825. }
  1826. //CJK’, otherwise words like it's might be affected.
  1827. unpaired=/(^[^\u2018\u2019]*[\u3000-\u30FF\u3400-\u9FBF\uFF00-\uFFEF](?:[\uF201-\uF204]CJK[\uF201-\uF204])?)\u2019/m;
  1828. while ( currHTML.length< noBonusLength && currHTML.match(unpaired) && (performance.now()-unpaired_start)<unpaired_timeout) {
  1829. currHTML=currHTML.replace(unpaired,'$1\uEB19'); //We need the greedy method to get the longest match.
  1830. }
  1831. ///=== Unicode Shifting Ends ===///
  1832. var time2shift=performance.now()-FixMarks_start-time2protect;
  1833. //Remove extra spaces if necessary
  1834. if (delete_all_extra_spaces===true) {
  1835. //For changhai.org and similar sites.
  1836. //Note that it is actually meaningless to chang text after \uF202 or \uF204CJK, because they belong to the "after" HTML and any change will be discarded once "trimmed" and I included them for simplicity.
  1837. //Note that it is actually meaningless to chang text before \uF201 or \uF203CJK, because they belong to the "before" HTML and any change will be discarded once "trimmed" and I just included them for simplicity.
  1838. currHTML=currHTML.replace(/&nbsp;/gi,'\u00A0');
  1839. currHTML=currHTML.replace(/([\r\n\u0020\u00A0\u2009]|&nbsp;){0,1}((?:[\uF201-\uF204]CJK[\uF201-\uF204])?[、,。:;!?)】〉》」』\uEB1D\uEB19]+(?:[\uF201-\uF204]CJK[\uF201-\uF204])?)(?:[\r\n\u0020\u00A0\u2009]|&nbsp;){0,2}/g,'$2');
  1840. //'>' means there is a non-CJK tag(?)
  1841. //currHTML=currHTML.replace(/([^\s\u00A0>])(?:[\r\n\u0020\u00A0\u2009]|&nbsp;){0,2}([『「《〈【(\uEB1C\uEB18]+)/g,'$1$2'); //before 1.3.2
  1842. currHTML=currHTML.replace(/([^\s\u00A0>])(?:[\r\n\u0020\u00A0\u2009]|&nbsp;){0,2}((?:[\uF201-\uF204]CJK[\uF201-\uF204])?[『「《〈【(\uEB1C\uEB18]+(?:[\uF201-\uF204]CJK[\uF201-\uF204])?)([\r\n\u0020\u00A0\u2009]|&nbsp;){0,1}/g,'$1$2');
  1843. }
  1844. else {
  1845. //Delete at most 1 spaces before and after because of the wider CJK marks.
  1846. currHTML=currHTML.replace(/([\uEB1D\uEB19])[ \u2009\u00A0]?/mg,'$1');
  1847. currHTML=currHTML.replace(/[ \u2009\u00A0]?([\uEB1C\uEB18])/mg,'$1');
  1848. }
  1849. ///--Group Left: [、,。:;!?)】〉》」』\uEB1D\uEB19] //Occupies the left half width.
  1850. ///--Group Right:[『「《〈【(\uEB1C\uEB18] //Occupies the right half width.
  1851. ///=====Use \uE211 as the calss name for TWO-PUNCT RULES====//
  1852. ///===Do not use the "g" modefier because we are using loops===//
  1853. var reLL=/([\n]?[、,。:;!?)】〉》」』\uEB1D\uEB19][\n]?)([\uF201-\uF204]CJK[\uF201-\uF204])?([、,。:;!?)】〉》」』\uEB1D\uEB19])/m;
  1854. var reLR=/([\n]?[、,。:;!?)】〉》」』\uEB1D\uEB19][\n]?)([\uF201-\uF204]CJK[\uF201-\uF204])?([『「《〈【(\uEB1C\uEB18])/m;
  1855. var reRR=/([\n]?[『「《〈【(\uEB1C\uEB18][\n]?)([\uF201-\uF204]CJK[\uF201-\uF204])?([『「《〈【(\uEB1C\uEB18])/m;
  1856. var reRL=/([\n]?[『「《〈【(\uEB1C\uEB18][\n]?)([\uF201-\uF204]CJK[\uF201-\uF204])?([、,。:;!?)】〉》」』\uEB1D\uEB19])/m;
  1857. var sqz_start=performance.now();
  1858. var debug_SeqPM = false;
  1859. if (debug_SeqPM === true)
  1860. if (currHTML.match(/此乃FixCJK标点测试/))
  1861. console.log('BEFORE:'+currHTML);
  1862. while (currHTML.match(/(?:[、,。:;!?)】〉》」』\uEB1D\uEB19『「《〈【(\uEB1C\uEB18]([\uF201-\uF204]CJK[\uF201-\uF204])?){2,}/m) && (performance.now()-sqz_start)<sqz_timeout) {
  1863. if (currHTML.match(reLL)) {
  1864. //--TWO PUNCTS: {Left}{Left}--//
  1865. tmp_str='<cjkpuns data-CJKTestedAndLabeled data-MarksFixedE135 data-cjkpua=\uE211 style="display:inline;padding-left:0px;padding-right:0px;float:none;letter-spacing:'+kern_consec_ll+';font-family:FixedPMSans !important;">$1</cjkpuns>$2$3';
  1866. currHTML=currHTML.replace(reLL,tmp_str);
  1867. }
  1868. else if (currHTML.match(reLR)) {
  1869. //--TWO PUNCTS: {Left}{Right}--//
  1870. tmp_str='<cjkpuns data-CJKTestedAndLabeled data-MarksFixedE135 data-cjkpua=\uE211 style="display:inline;padding-left:0px;padding-right:0px;float:none;letter-spacing:'+kern_consec_lr+';font-family:FixedPMSans !important;">$1</cjkpuns>$2$3';
  1871. currHTML=currHTML.replace(reLR,tmp_str);
  1872. }
  1873. else if (currHTML.match(reRR)) {
  1874. //--TWO PUNCTS: {Right}{Right}--//
  1875. tmp_str='<cjkpuns data-CJKTestedAndLabeled data-MarksFixedE135 data-cjkpua=\uE211 style="display:inline;padding-left:0px;padding-right:0px;float:none;letter-spacing:'+kern_consec_rr+';font-family:FixedPMSans !important;">$1</cjkpuns>$2$3';
  1876. currHTML=currHTML.replace(reRR,tmp_str);
  1877. }
  1878. else if (currHTML.match(reRL)) {
  1879. //--TWO PUNCTS: no letter-spacing adjustment for {Right}-{Left}, just a "fake" element--//
  1880. currHTML=currHTML.replace(reRL,'$1<cjkpuns data-CJKTestedAndLabeled data-MarksFixedE135 data-cjkpua=\uE211 style="display:inline;padding-left:0px;padding-right:0px;float:none;font-family:FixedPMSans !important;"></cjkpuns>$2$3');
  1881. }
  1882. else {
  1883. console.log("FIXME: current combination of punctuations has not been considered!");
  1884. break;
  1885. }
  1886. }
  1887. ///---The last punct in a seq is left in <cjktext> tag. Must be put in <cjkpuns> tag as well. (Same tag.) Only need to deal 『「《〈【(\uEB1C\uEB18 (openning marks).
  1888. var reLastP=/([^><]<.cjkpuns>)([\n]?<[^><\uE211]*>[\n]?)*([『「《〈【(\uEB1C\uEB18])(<[^><\uE211]*>)*([^><[、,。:;!?)】〉》」』\uEB1D\uEB19『「《〈【(\uEB1C\uEB18])/g;
  1889. currHTML=currHTML.replace(reLastP,'$1$2<cjkpuns data-CJKTestedAndLabeled data-MarksFixedE135 data-cjkpua=\uE211 style="display:inline;padding-left:0px;padding-right:0px;float:none;font-family:FixedPMSans !important;">$3</cjkpuns>$4$5');
  1890. //Now let's deal the over-tag marks. They are between two [\uF201-\uF204]CJK[\uF201-\uF204]
  1891. var overTagLastP=/([^><]<.cjkpuns>)([\uF201-\uF204]CJK[\uF201-\uF204])([『「《〈【(\uEB1C\uEB18])([^><]*[\uF201-\uF204]CJK[\uF201-\uF204])/g;
  1892. currHTML=currHTML.replace(overTagLastP,'$1$2<cjkpuns data-CJKTestedAndLabeled data-MarksFixedE135 data-cjkpua=\uE211 style="display:inline;padding-left:0px;padding-right:0px;float:none;font-family:FixedPMSans !important;">$3</cjkpuns>$4');
  1893.  
  1894. //currHTML=currHTML.replace(/([『「《〈【(\uEB1C\uEB18、,。:;!?)】〉》」』\uEB1D\uEB19])([\uF201-\uF204]CJK[\uF201-\uF204])*(<[^><]*>)*([『「《〈【(\uEB1C\uEB18])(<[^><]*>)*([^><])/g,'$1$2$3<cjkpuns data-CJKTestedAndLabeled data-MarksFixedE135 data-cjkpua=\uE211 style="display:inline;padding-left:0px;padding-right:0px;float:none;font-family:FixedPMSans !important;">$4</cjkpuns>$5$6');
  1895. ///---Done with conseqtive puncts--///
  1896. if (debug_SeqPM === true)
  1897. if (currHTML.match(/此乃FixCJK标点测试/))
  1898. console.log('BEFORE:'+currHTML);
  1899. if (debug_04===true) {node.style.color="Pink";}
  1900. var wrapFirst=true; //Safe after using the "palt" option. It is also the "lastP in another tag".
  1901. if (SqueezeInd===true) {
  1902. //The punctuation marks is also the first char in a paragraph seems:
  1903. //In current model (1.0.x), all tags are added within this function (FixMarksInCurrHTML), and it should be safe to skip them.
  1904. //Seems no need to squeeze the puncts at the beginning of a paragraph. It may cause format changes.
  1905. if (wrapFirst===true) {
  1906. currHTML=currHTML.replace(/^([\uF201-\uF204]CJK[\uF201-\uF204])*(<[^><\uE211]*>)*([『「《〈【(\uEB1C\uEB18])(<[^><]*>)*([^><])/mg,'$1$2<cjkpuns data-CJKTestedAndLabeled data-MarksFixedE135 data-cjkpua=\uE211 style="margin-left:'+kern_ind_open+';display:inline;padding-left:0px;padding-right:0px;float:none;font-family:FixedPMSans !important;">$3</cjkpuns>$4$5');
  1907. }
  1908. //Then, not the first char. The re_ex also covers the first punct of a serise of puncts. use \uE211 to make sure the first PMs will not be "double" fixed.
  1909. currHTML=currHTML.replace(/([^\n><、,。:;!?)】〉》」』\uEB1D\uEB19『「《〈【(\uEB1C\uEB18\uF201-\uF204])((?:<[^><\uE211]*>)*(?:[\uF201-\uF204]CJK[\uF201-\uF204])?(?:<[^><\uE211]*>)*)([『「《〈【(\uEB1C\uEB18])((?:<[^><]*>)*(?:[\uF201-\uF204]CJK[\uF201-\uF204])?(?:<[^><]*>)*(?:[\uF201-\uF204]CJK[\uF201-\uF204])?)([^><\uF201-\uF204]|$)/mg,'$1$2<cjkpuns data-CJKTestedAndLabeled data-MarksFixedE135 data-cjkpua=\uE211 style="display:inline;padding-left:0px;padding-right:0px;float:none;margin-left:'+kern_ind_open+';font-family:FixedPMSans !important;">$3</cjkpuns>$4$5');
  1910. //Previously: Do not squeeze the last punctuation marks in a paragraph. Too risky. $3 seems necessary?
  1911. //Now: Using positive margin seems safe.
  1912. currHTML=currHTML.replace(/([、,。:;!?)】〉》」』\uEB1D\uEB19])([\uF201-\uF204]CJK[\uF201-\uF204])?$/mg,'<cjkpuns data-CJKTestedAndLabeled data-MarksFixedE135 data-cjkpua=\uE211 style="display:inline;padding-left:0px;padding-right:0px;float:none;margin-right:'+kern_ind_close+';font-family:FixedPMSans !important;">$1</cjkpuns>$2');
  1913. //Note that [\uF201-\uF204]CJK[\uF201-\uF204] might be added to the end and it must be excluded.
  1914. 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,'<cjkpuns data-CJKTestedAndLabeled data-MarksFixedE135 data-cjkpua=\uE211 style="display:inline;padding-left:0px;padding-right:0px;float:none;margin-right:'+kern_ind_close+';font-family:FixedPMSans !important;">$1</cjkpuns>$2$3');
  1915. }
  1916. ///=== Squeezing Ends ===///
  1917. var time2squeeze=performance.now()-FixMarks_start-time2shift-time2protect;
  1918. ///=== Change the protected punctuations in tags back==///
  1919. currHTML=currHTML.replace(/\uE862/mg,'\u2018');
  1920. currHTML=currHTML.replace(/\uE863/mg,'\u2019');
  1921. currHTML=currHTML.replace(/\uE972/mg,'\u201C');
  1922. currHTML=currHTML.replace(/\uE973/mg,'\u201D');
  1923. currHTML=currHTML.replace(/\uEA01/mg,'、');
  1924. currHTML=currHTML.replace(/\uEA02/mg,',');
  1925. currHTML=currHTML.replace(/\uEA03/mg,'。');
  1926. currHTML=currHTML.replace(/\uEA04/mg,':');
  1927. currHTML=currHTML.replace(/\uEA05/mg,';');
  1928. currHTML=currHTML.replace(/\uEA06/mg,'!');
  1929. currHTML=currHTML.replace(/\uEA07/mg,'?');
  1930. currHTML=currHTML.replace(/\uEA08/mg,')');
  1931. currHTML=currHTML.replace(/\uEA09/mg,'】');
  1932. currHTML=currHTML.replace(/\uEA10/mg,'〉');
  1933. currHTML=currHTML.replace(/\uEA11/mg,'》');
  1934. currHTML=currHTML.replace(/\uEA12/mg,'」');
  1935. currHTML=currHTML.replace(/\uEA13/mg,'』');
  1936. currHTML=currHTML.replace(/\uEA14/mg,'『');
  1937. currHTML=currHTML.replace(/\uEA15/mg,'「');
  1938. currHTML=currHTML.replace(/\uEA16/mg,'《');
  1939. currHTML=currHTML.replace(/\uEA17/mg,'〈');
  1940. currHTML=currHTML.replace(/\uEA18/mg,'【');
  1941. currHTML=currHTML.replace(/\uEA19/mg,'(');
  1942. ///////==== Change Latin quotation marks back =====/////
  1943. currHTML=currHTML.replace(/\uEC18/mg,'\u2018');
  1944. currHTML=currHTML.replace(/\uEC19/mg,'\u2019');
  1945. currHTML=currHTML.replace(/\uEC1C/mg,'\u201C');
  1946. currHTML=currHTML.replace(/\uEC1D/mg,'\u201D');
  1947. //Use '\uE985' as the class of CJK quotation marks.
  1948. currHTML=currHTML.replace(/\uEB1C/mg,'<cjkpuns data-CJKTestedAndLabeled data-MarksFixedE135 data-cjkpua=\uE985 style="display:inline;padding-left:0px;padding-right:0px;float:none;font-family:'+dequote(CJKPunct)+' !important;">\u201C</cjkpuns>');
  1949. currHTML=currHTML.replace(/\uEB1D/mg,'<cjkpuns data-CJKTestedAndLabeled data-MarksFixedE135 data-cjkpua=\uE985 style="display:inline;padding-left:0px;padding-right:0px;float:none;font-family:'+dequote(CJKPunct)+' !important;">\u201D</cjkpuns>');
  1950. currHTML=currHTML.replace(/\uEB18/mg,'<cjkpuns data-CJKTestedAndLabeled data-MarksFixedE135 data-cjkpua=\uE985 style="display:inline;padding-left:0px;padding-right:0px;float:none;font-family:'+dequote(CJKPunct)+' !important;">\u2018</cjkpuns>');
  1951. currHTML=currHTML.replace(/\uEB19/mg,'<cjkpuns data-CJKTestedAndLabeled data-MarksFixedE135 data-cjkpua=\uE985 style="display:inline;padding-left:0px;padding-right:0px;float:none;font-family:'+dequote(CJKPunct)+' !important;">\u2019</cjkpuns>');
  1952. ///=== Replacing and Restoring Ends ===///
  1953. var time2replace=performance.now()-FixMarks_start-time2squeeze-time2shift-time2protect;
  1954. if ( (performance.now()-FixMarks_start)>200 ) {
  1955. console.log("FIXME: String Operation Too Slow: "+(performance.now()-FixMarks_start).toFixed(0)+" ms.");
  1956. console.log("Protect: "+time2protect.toFixed(0)+" ms.");
  1957. console.log("Shift: "+time2shift.toFixed(0)+" ms.");
  1958. console.log(" ----->rev: "+fixpair_stop.toFixed(0)+" ms.");
  1959. console.log(" ----->\u201C,\u201D: "+paired_stop.toFixed(0)+" ms.");
  1960. console.log(" ----->\u2018,\u2019: "+paired_single_stop.toFixed(0)+" ms.");
  1961. console.log("Squeeze: "+time2squeeze.toFixed(0)+" ms.");
  1962. console.log("Replace: "+time2replace.toFixed(0)+" ms.");
  1963. console.log("String(Length): "+currHTML.slice(0,216)+"...("+currHTML.length+")");
  1964. console.log('Input As:\n'+inputHTML+'\n@<'+node.nodeName+'>');
  1965. }
  1966. if (debug_tagSeeThrough===true) {console.log("FIXED: "+currHTML+"@"+performance.now());}
  1967. currHTML=currHTML.replace(/^[^\u0000]*\uF201CJK\uF201([^\u0000]*)$/,'$1');
  1968. currHTML=currHTML.replace(/^([^\u0000]*)\uF202CJK\uF202[^\u0000]*$/,'$1');
  1969. if (debug_tagSeeThrough===true) console.log("AFTER TRIMMED:(@"+(performance.now()-FixMarks_start).toFixed(1)+" ms): "+node.nodeName+"#"+node.id+"::"+currHTML);
  1970. if ( (performance.now()-FixMarks_start)>10 ) {
  1971. console.log("Warning: Slow ("+(performance.now()-FixMarks_start).toFixed(1)+" ms) at <"+node.nodeName+">@<"+node.parentNode.nodeName+">: "+node.parentNode.innerHTML);
  1972. console.log("The Continued HTML is:\n"+continuedHTML);
  1973. }
  1974. return currHTML;
  1975. }
  1976. function fontsCheck() {
  1977. //the finalCheck, e.g. check if "negative margin-right at the tail".
  1978. if (forceNoSimSun === true) {
  1979. var allCJK=document.getElementsByTagName("CJKTEXT");
  1980. console.log("Number of tag <cjktext>: "+allCJK.length);
  1981. for (var ifc=0;ifc<allCJK.length;ifc++) {
  1982. if (allCJK[ifc].style.cssText.match("font-family")) {
  1983. //Make sure just one important.
  1984. if (allCJK[ifc].style.cssText.match(/fonts-family[^;]*important/))
  1985. continue;
  1986. //console.log(allCJK[ifc].style.cssText.replace(/(font-family.[^;]*);/g,'$1 !important;'));
  1987. allCJK[ifc].style.cssText=allCJK[ifc].style.cssText.replace(/(font-family.[^;]*);/g,'$1 !important;');
  1988. }
  1989. else if (allCJK[ifc].parentNode.style.cssText.match("font-family")) {
  1990. allCJK[ifc].style.setProperty("font-family",allCJK[ifc].parentNode.style.fontFamily,"important");
  1991. }
  1992. else
  1993. console.log("FIXME: no font settings found.");
  1994. }
  1995. }
  1996. }
  1997. function displayedText(node) {
  1998. var child=node.firstChild;
  1999. var toReturn='';
  2000. while (child) {
  2001. if (child.nodeType===3) {
  2002. toReturn=toReturn+child.data;
  2003. }
  2004. else if (child.nodeType===1 && (window.getComputedStyle(child,null).getPropertyValue("display")!=='none') ) {
  2005. toReturn=toReturn+displayedText(child);
  2006. }
  2007. child=child.nextSibling;
  2008. }
  2009. return toReturn;
  2010. }
  2011. }
  2012. ) ();