markdown_template.js 30 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174
  1. var content;
  2. // Current header index in all headers.
  3. var currentHeaderIdx = -1;
  4. // Pending keys for keydown.
  5. var pendingKeys = [];
  6. var VMermaidDivClass = 'mermaid-diagram';
  7. var VFlowchartDivClass = 'flowchart-diagram';
  8. if (typeof VEnableMermaid == 'undefined') {
  9. VEnableMermaid = false;
  10. } else if (VEnableMermaid) {
  11. mermaidAPI.initialize({
  12. startOnLoad: false
  13. });
  14. }
  15. if (typeof VEnableFlowchart == 'undefined') {
  16. VEnableFlowchart = false;
  17. }
  18. if (typeof VEnableMathjax == 'undefined') {
  19. VEnableMathjax = false;
  20. }
  21. if (typeof VEnableHighlightLineNumber == 'undefined') {
  22. VEnableHighlightLineNumber = false;
  23. }
  24. if (typeof VStylesToInline == 'undefined') {
  25. VStylesToInline = '';
  26. }
  27. // Add a caption (using alt text) under the image.
  28. var VImageCenterClass = 'img-center';
  29. var VImageCaptionClass = 'img-caption';
  30. var VImagePackageClass = 'img-package';
  31. if (typeof VEnableImageCaption == 'undefined') {
  32. VEnableImageCaption = false;
  33. }
  34. if (typeof VEnableFlashAnchor == 'undefined') {
  35. VEnableFlashAnchor = false;
  36. }
  37. if (typeof VRemoveMathjaxScript == 'undefined') {
  38. VRemoveMathjaxScript = false;
  39. }
  40. if (typeof VAddTOC == 'undefined') {
  41. VAddTOC = false;
  42. }
  43. var getUrlScheme = function(url) {
  44. var idx = url.indexOf(':');
  45. if (idx > -1) {
  46. return url.substr(0, idx);
  47. } else {
  48. return null;
  49. }
  50. };
  51. var replaceCssUrl = function(baseUrl, match, p1, offset, str) {
  52. if (getUrlScheme(p1)) {
  53. return match;
  54. }
  55. var url = baseUrl + '/' + p1;
  56. return "url(\"" + url + "\");";
  57. };
  58. var translateCssUrlToAbsolute = function(baseUrl, css) {
  59. return css.replace(/\burl\(\"([^\"\)]+)\"\);/g, replaceCssUrl.bind(undefined, baseUrl));
  60. };
  61. var styleContent = function() {
  62. var styles = "";
  63. for (var i = 0; i < document.styleSheets.length; ++i) {
  64. var styleSheet = document.styleSheets[i];
  65. if (styleSheet.cssRules) {
  66. var baseUrl = null;
  67. if (styleSheet.href) {
  68. var scheme = getUrlScheme(styleSheet.href);
  69. // We only translate local resources.
  70. if (scheme == 'file' || scheme == 'qrc') {
  71. baseUrl = styleSheet.href.substr(0, styleSheet.href.lastIndexOf('/'));
  72. }
  73. }
  74. for (var j = 0; j < styleSheet.cssRules.length; ++j) {
  75. var css = styleSheet.cssRules[j].cssText;
  76. if (baseUrl) {
  77. // Try to replace the url() with absolute path.
  78. css = translateCssUrlToAbsolute(baseUrl, css);
  79. }
  80. styles = styles + css + "\n";
  81. }
  82. }
  83. }
  84. return styles;
  85. };
  86. var htmlContent = function() {
  87. content.htmlContentCB("", styleContent(), placeholder.innerHTML);
  88. };
  89. new QWebChannel(qt.webChannelTransport,
  90. function(channel) {
  91. content = channel.objects.content;
  92. if (typeof updateHtml == "function") {
  93. updateHtml(content.html);
  94. content.htmlChanged.connect(updateHtml);
  95. }
  96. if (typeof updateText == "function") {
  97. content.textChanged.connect(updateText);
  98. content.updateText();
  99. }
  100. content.requestScrollToAnchor.connect(scrollToAnchor);
  101. if (typeof highlightText == "function") {
  102. content.requestHighlightText.connect(highlightText);
  103. content.noticeReadyToHighlightText();
  104. }
  105. if (typeof textToHtml == "function") {
  106. content.requestTextToHtml.connect(textToHtml);
  107. content.noticeReadyToTextToHtml();
  108. }
  109. if (typeof htmlContent == "function") {
  110. content.requestHtmlContent.connect(htmlContent);
  111. }
  112. });
  113. var VHighlightedAnchorClass = 'highlighted-anchor';
  114. var clearHighlightedAnchor = function() {
  115. var headers = document.getElementsByClassName(VHighlightedAnchorClass);
  116. while (headers.length > 0) {
  117. headers[0].classList.remove(VHighlightedAnchorClass);
  118. }
  119. };
  120. var flashAnchor = function(anchor) {
  121. if (!VEnableFlashAnchor) {
  122. return;
  123. }
  124. clearHighlightedAnchor();
  125. anchor.classList.add(VHighlightedAnchorClass);
  126. };
  127. var g_muteScroll = false;
  128. var scrollToAnchor = function(anchor) {
  129. g_muteScroll = true;
  130. currentHeaderIdx = -1;
  131. if (!anchor) {
  132. window.scrollTo(0, 0);
  133. g_muteScroll = false;
  134. return;
  135. }
  136. var anc = document.getElementById(anchor);
  137. if (anc != null) {
  138. anc.scrollIntoView();
  139. flashAnchor(anc);
  140. var headers = document.querySelectorAll("h1, h2, h3, h4, h5, h6");
  141. for (var i = 0; i < headers.length; ++i) {
  142. if (headers[i] == anc) {
  143. currentHeaderIdx = i;
  144. break;
  145. }
  146. }
  147. }
  148. // Disable scroll temporarily.
  149. setTimeout("g_muteScroll = false", 100);
  150. };
  151. window.onwheel = function(e) {
  152. e = e || window.event;
  153. var ctrl = !!e.ctrlKey;
  154. if (ctrl) {
  155. e.preventDefault();
  156. }
  157. }
  158. window.onscroll = function() {
  159. if (g_muteScroll) {
  160. return;
  161. }
  162. currentHeaderIdx = -1;
  163. var scrollTop = document.documentElement.scrollTop || document.body.scrollTop || window.pageYOffset;
  164. var eles = document.querySelectorAll("h1, h2, h3, h4, h5, h6");
  165. if (eles.length == 0) {
  166. content.setHeader("");
  167. return;
  168. }
  169. var biaScrollTop = scrollTop + 50;
  170. for (var i = 0; i < eles.length; ++i) {
  171. if (biaScrollTop >= eles[i].offsetTop) {
  172. currentHeaderIdx = i;
  173. } else {
  174. break;
  175. }
  176. }
  177. var curHeader = null;
  178. if (currentHeaderIdx != -1) {
  179. curHeader = eles[currentHeaderIdx].getAttribute("id");
  180. }
  181. content.setHeader(curHeader ? curHeader : "");
  182. };
  183. // Used to record the repeat token of user input.
  184. var repeatToken = 0;
  185. document.onkeydown = function(e) {
  186. // Need to clear pending kyes.
  187. var clear = true;
  188. // This even has been handled completely. No need to call the default handler.
  189. var accept = true;
  190. e = e || window.event;
  191. var key;
  192. var shift;
  193. var ctrl;
  194. if (e.which) {
  195. key = e.which;
  196. } else {
  197. key = e.keyCode;
  198. }
  199. shift = !!e.shiftKey;
  200. ctrl = !!e.ctrlKey;
  201. switch (key) {
  202. // Skip Ctrl, Shift, Alt, Supper.
  203. case 16:
  204. case 17:
  205. case 18:
  206. case 91:
  207. clear = false;
  208. break;
  209. // 0 - 9.
  210. case 48:
  211. case 49:
  212. case 50:
  213. case 51:
  214. case 52:
  215. case 53:
  216. case 54:
  217. case 55:
  218. case 56:
  219. case 57:
  220. case 96:
  221. case 97:
  222. case 98:
  223. case 99:
  224. case 100:
  225. case 101:
  226. case 102:
  227. case 103:
  228. case 104:
  229. case 105:
  230. {
  231. if (pendingKeys.length != 0 || ctrl || shift) {
  232. accept = false;
  233. break;
  234. }
  235. var num = key >= 96 ? key - 96 : key - 48;
  236. repeatToken = repeatToken * 10 + num;
  237. clear = false;
  238. break;
  239. }
  240. case 74: // J
  241. if (!ctrl && !shift) {
  242. window.scrollBy(0, 100);
  243. break;
  244. }
  245. accept = false;
  246. break;
  247. case 75: // K
  248. if (!ctrl && !shift) {
  249. window.scrollBy(0, -100);
  250. break;
  251. }
  252. accept = false;
  253. break;
  254. case 72: // H
  255. if (!ctrl && !shift) {
  256. window.scrollBy(-100, 0);
  257. break;
  258. }
  259. accept = false;
  260. break;
  261. case 76: // L
  262. if (!ctrl && !shift) {
  263. window.scrollBy(100, 0);
  264. break;
  265. }
  266. accept = false;
  267. break;
  268. case 71: // G
  269. if (shift) {
  270. if (pendingKeys.length == 0) {
  271. var scrollLeft = document.documentElement.scrollLeft || document.body.scrollLeft || window.pageXOffset;
  272. var scrollHeight = document.documentElement.scrollHeight || document.body.scrollHeight;
  273. window.scrollTo(scrollLeft, scrollHeight);
  274. break;
  275. }
  276. } else if (!ctrl) {
  277. if (pendingKeys.length == 0) {
  278. // First g, pend it.
  279. pendingKeys.push({
  280. key: key,
  281. ctrl: ctrl,
  282. shift: shift
  283. });
  284. clear = false;
  285. break;
  286. } else if (pendingKeys.length == 1) {
  287. var pendKey = pendingKeys[0];
  288. if (pendKey.key == key && !pendKey.shift && !pendKey.ctrl) {
  289. var scrollLeft = document.documentElement.scrollLeft
  290. || document.body.scrollLeft
  291. || window.pageXOffset;
  292. window.scrollTo(scrollLeft, 0);
  293. break;
  294. }
  295. }
  296. }
  297. accept = false;
  298. break;
  299. case 85: // U
  300. if (ctrl) {
  301. var clientHeight = document.documentElement.clientHeight;
  302. window.scrollBy(0, -clientHeight / 2);
  303. break;
  304. }
  305. accept = false;
  306. break;
  307. case 68: // D
  308. if (ctrl) {
  309. var clientHeight = document.documentElement.clientHeight;
  310. window.scrollBy(0, clientHeight / 2);
  311. break;
  312. }
  313. accept = false;
  314. break;
  315. case 219: // [ or {
  316. {
  317. var repeat = repeatToken < 1 ? 1 : repeatToken;
  318. if (shift) {
  319. // {
  320. if (pendingKeys.length == 1) {
  321. var pendKey = pendingKeys[0];
  322. if (pendKey.key == key && !pendKey.shift && !pendKey.ctrl) {
  323. // [{, jump to previous title at a higher level.
  324. jumpTitle(false, -1, repeat);
  325. break;
  326. }
  327. }
  328. } else if (!ctrl) {
  329. // [
  330. if (pendingKeys.length == 0) {
  331. // First [, pend it.
  332. pendingKeys.push({
  333. key: key,
  334. ctrl: ctrl,
  335. shift: shift
  336. });
  337. clear = false;
  338. break;
  339. } else if (pendingKeys.length == 1) {
  340. var pendKey = pendingKeys[0];
  341. if (pendKey.key == key && !pendKey.shift && !pendKey.ctrl) {
  342. // [[, jump to previous title.
  343. jumpTitle(false, 1, repeat);
  344. break;
  345. } else if (pendKey.key == 221 && !pendKey.shift && !pendKey.ctrl) {
  346. // ][, jump to next title at the same level.
  347. jumpTitle(true, 0, repeat);
  348. break;
  349. }
  350. }
  351. }
  352. accept = false;
  353. break;
  354. }
  355. case 221: // ] or }
  356. {
  357. var repeat = repeatToken < 1 ? 1 : repeatToken;
  358. if (shift) {
  359. // }
  360. if (pendingKeys.length == 1) {
  361. var pendKey = pendingKeys[0];
  362. if (pendKey.key == key && !pendKey.shift && !pendKey.ctrl) {
  363. // ]}, jump to next title at a higher level.
  364. jumpTitle(true, -1, repeat);
  365. break;
  366. }
  367. }
  368. } else if (!ctrl) {
  369. // ]
  370. if (pendingKeys.length == 0) {
  371. // First ], pend it.
  372. pendingKeys.push({
  373. key: key,
  374. ctrl: ctrl,
  375. shift: shift
  376. });
  377. clear = false;
  378. break;
  379. } else if (pendingKeys.length == 1) {
  380. var pendKey = pendingKeys[0];
  381. if (pendKey.key == key && !pendKey.shift && !pendKey.ctrl) {
  382. // ]], jump to next title.
  383. jumpTitle(true, 1, repeat);
  384. break;
  385. } else if (pendKey.key == 219 && !pendKey.shift && !pendKey.ctrl) {
  386. // [], jump to previous title at the same level.
  387. jumpTitle(false, 0, repeat);
  388. break;
  389. }
  390. }
  391. }
  392. accept = false;
  393. break;
  394. }
  395. default:
  396. accept = false;
  397. break;
  398. }
  399. if (clear) {
  400. repeatToken = 0;
  401. pendingKeys = [];
  402. }
  403. if (accept) {
  404. e.preventDefault();
  405. } else {
  406. content.keyPressEvent(key, ctrl, shift);
  407. }
  408. };
  409. var mermaidParserErr = false;
  410. var mermaidIdx = 0;
  411. if (VEnableMermaid) {
  412. mermaidAPI.parseError = function(err, hash) {
  413. content.setLog("err: " + err);
  414. mermaidParserErr = true;
  415. // Clean the container element, or mermaidAPI won't render the graph with
  416. // the same id.
  417. var errGraph = document.getElementById('mermaid-diagram-' + mermaidIdx);
  418. var parentNode = errGraph.parentElement;
  419. parentNode.outerHTML = '';
  420. delete parentNode;
  421. };
  422. }
  423. // @className, the class name of the mermaid code block, such as 'lang-mermaid'.
  424. var renderMermaid = function(className) {
  425. if (!VEnableMermaid) {
  426. return;
  427. }
  428. var codes = document.getElementsByTagName('code');
  429. mermaidIdx = 0;
  430. for (var i = 0; i < codes.length; ++i) {
  431. var code = codes[i];
  432. if (code.classList.contains(className)) {
  433. if (renderMermaidOne(code)) {
  434. // replaceChild() will decrease codes.length.
  435. --i;
  436. }
  437. }
  438. }
  439. };
  440. // Render @code as Mermaid graph.
  441. // Returns true if succeeded.
  442. var renderMermaidOne = function(code) {
  443. // Mermaid code block.
  444. mermaidParserErr = false;
  445. mermaidIdx++;
  446. try {
  447. // Do not increment mermaidIdx here.
  448. var graph = mermaidAPI.render('mermaid-diagram-' + mermaidIdx, code.textContent, function(){});
  449. } catch (err) {
  450. content.setLog("err: " + err);
  451. return false;
  452. }
  453. if (mermaidParserErr || typeof graph == "undefined") {
  454. return false;
  455. }
  456. var graphDiv = document.createElement('div');
  457. graphDiv.classList.add(VMermaidDivClass);
  458. graphDiv.innerHTML = graph;
  459. var preNode = code.parentNode;
  460. preNode.parentNode.replaceChild(graphDiv, preNode);
  461. return true;
  462. };
  463. var flowchartIdx = 0;
  464. // @className, the class name of the flowchart code block, such as 'lang-flowchart'.
  465. var renderFlowchart = function(classNames) {
  466. if (!VEnableFlowchart) {
  467. return;
  468. }
  469. var codes = document.getElementsByTagName('code');
  470. flowchartIdx = 0;
  471. for (var i = 0; i < codes.length; ++i) {
  472. var code = codes[i];
  473. var matched = false;
  474. for (var j = 0; j < classNames.length; ++j) {
  475. if (code.classList.contains(classNames[j])) {
  476. matched = true;
  477. break;
  478. }
  479. }
  480. if (matched) {
  481. if (renderFlowchartOne(code, flowchartIdx)) {
  482. // replaceChild() will decrease codes.length.
  483. --i;
  484. }
  485. }
  486. }
  487. };
  488. // Render @code as Flowchart.js graph.
  489. // Returns true if succeeded.
  490. var renderFlowchartOne = function(code) {
  491. // Flowchart code block.
  492. flowchartIdx++;
  493. try {
  494. var graph = flowchart.parse(code.textContent);
  495. } catch (err) {
  496. content.setLog("err: " + err);
  497. return false;
  498. }
  499. if (typeof graph == "undefined") {
  500. return false;
  501. }
  502. var graphDiv = document.createElement('div');
  503. graphDiv.id = 'flowchart-diagram-' + flowchartIdx;
  504. graphDiv.classList.add(VFlowchartDivClass);
  505. var preNode = code.parentNode;
  506. var preParentNode = preNode.parentNode;
  507. preParentNode.replaceChild(graphDiv, preNode);
  508. // Draw on it after adding it to page.
  509. try {
  510. graph.drawSVG(graphDiv.id);
  511. } catch (err) {
  512. content.setLog("err: " + err);
  513. preParentNode.replaceChild(preNode, graphDiv);
  514. delete graphDiv;
  515. return false;
  516. }
  517. return true;
  518. };
  519. var isImageBlock = function(img) {
  520. var pn = img.parentNode;
  521. return (pn.children.length == 1) && (pn.textContent == '');
  522. };
  523. var isImageWithBr = function(img) {
  524. var sibling = img.nextSibling;
  525. while (sibling) {
  526. if (sibling.nodeType == 8) {
  527. // Comment node.
  528. // Just continue.
  529. sibling = sibling.nextSibling;
  530. continue;
  531. } else if (sibling.nodeType == 1) {
  532. if (sibling.tagName == 'BR') {
  533. break;
  534. }
  535. }
  536. return false;
  537. }
  538. sibling = img.previousSibling;
  539. while (sibling) {
  540. if (sibling.nodeType == 8) {
  541. // Comment node.
  542. sibling = sibling.previousSibling;
  543. continue;
  544. } else if (sibling.nodeType == 1) {
  545. // Element node.
  546. if (sibling.tagName == 'BR') {
  547. break;
  548. }
  549. } else if (sibling.nodeType == 3) {
  550. // Text node.
  551. if (sibling.nodeValue == '\n') {
  552. var tmp = sibling.previousSibling;
  553. if (tmp && tmp.tagName == 'BR') {
  554. break;
  555. }
  556. }
  557. }
  558. return false;
  559. }
  560. return true;
  561. };
  562. var getImageType = function(img) {
  563. var type = -1;
  564. if (isImageBlock(img)) {
  565. type = 0;
  566. } else if (isImageWithBr(img)) {
  567. type = 1;
  568. }
  569. return type;
  570. };
  571. // Center the image block and insert the alt text as caption.
  572. var insertImageCaption = function() {
  573. if (!VEnableImageCaption) {
  574. return;
  575. }
  576. var imgs = document.getElementsByTagName('img');
  577. for (var i = 0; i < imgs.length; ++i) {
  578. var img = imgs[i];
  579. var type = getImageType(img);
  580. if (type == -1) {
  581. continue;
  582. } else if (type == 1) {
  583. // Insert a div as the parent of the img.
  584. var imgPack = document.createElement('div');
  585. img.insertAdjacentElement('afterend', imgPack);
  586. imgPack.appendChild(img);
  587. }
  588. // Make the parent img-package.
  589. img.parentNode.classList.add(VImagePackageClass);
  590. // Make it center.
  591. img.classList.add(VImageCenterClass);
  592. if (img.alt == '') {
  593. continue;
  594. }
  595. // Add caption.
  596. var captionDiv = document.createElement('div');
  597. captionDiv.classList.add(VImageCaptionClass);
  598. captionDiv.textContent = img.alt;
  599. img.insertAdjacentElement('afterend', captionDiv);
  600. }
  601. }
  602. // The renderer specific code should call this function once thay have finished
  603. // markdown-specifi handle logics, such as Mermaid, MathJax.
  604. var finishLogics = function() {
  605. content.finishLogics();
  606. calculateWordCount();
  607. };
  608. // Escape @text to Html.
  609. var escapeHtml = function(text) {
  610. var map = {
  611. '&': '&amp;',
  612. '<': '&lt;',
  613. '>': '&gt;',
  614. '"': '&quot;',
  615. "'": '&#039;'
  616. };
  617. return text.replace(/[&<>"']/g, function(m) { return map[m]; });
  618. };
  619. // Return the topest level of @toc, starting from 1.
  620. var baseLevelOfToc = function(p_toc) {
  621. var level = -1;
  622. for (i in p_toc) {
  623. if (level == -1) {
  624. level = p_toc[i].level;
  625. } else if (level > p_toc[i].level) {
  626. level = p_toc[i].level;
  627. }
  628. }
  629. if (level == -1) {
  630. level = 1;
  631. }
  632. return level;
  633. };
  634. // Handle wrong title levels, such as '#' followed by '###'
  635. var toPerfectToc = function(p_toc, p_baseLevel) {
  636. var i;
  637. var curLevel = p_baseLevel - 1;
  638. var perfToc = [];
  639. for (i in p_toc) {
  640. var item = p_toc[i];
  641. // Insert empty header.
  642. while (item.level > curLevel + 1) {
  643. curLevel += 1;
  644. var tmp = { level: curLevel,
  645. anchor: '',
  646. title: '[EMPTY]'
  647. };
  648. perfToc.push(tmp);
  649. }
  650. perfToc.push(item);
  651. curLevel = item.level;
  652. }
  653. return perfToc;
  654. };
  655. var itemToHtml = function(item) {
  656. return '<a href="#' + item.anchor + '">' + item.title + '</a>';
  657. };
  658. // Turn a perfect toc to a tree using <ul>
  659. var tocToTree = function(p_toc, p_baseLevel) {
  660. var i;
  661. var front = '<li>';
  662. var ending = ['</li>'];
  663. var curLevel = p_baseLevel;
  664. for (i in p_toc) {
  665. var item = p_toc[i];
  666. if (item.level == curLevel) {
  667. front += '</li>';
  668. front += '<li>';
  669. front += itemToHtml(item);
  670. } else if (item.level > curLevel) {
  671. // assert(item.level - curLevel == 1)
  672. front += '<ul>';
  673. ending.push('</ul>');
  674. front += '<li>';
  675. front += itemToHtml(item);
  676. ending.push('</li>');
  677. curLevel = item.level;
  678. } else {
  679. while (item.level < curLevel) {
  680. var ele = ending.pop();
  681. front += ele;
  682. if (ele == '</ul>') {
  683. curLevel--;
  684. }
  685. }
  686. front += '</li>';
  687. front += '<li>';
  688. front += itemToHtml(item);
  689. }
  690. }
  691. while (ending.length > 0) {
  692. front += ending.pop();
  693. }
  694. front = front.replace("<li></li>", "");
  695. front = '<ul>' + front + '</ul>';
  696. return front;
  697. };
  698. var handleToc = function(needToc) {
  699. var baseLevel = baseLevelOfToc(toc);
  700. var tocTree = tocToTree(toPerfectToc(toc, baseLevel), baseLevel);
  701. content.setToc(tocTree, baseLevel);
  702. var removeToc = toc.length == 0;
  703. // Add it to html
  704. if (needToc) {
  705. var eles = document.getElementsByClassName('vnote-toc');
  706. for (var i = 0; i < eles.length; ++i) {
  707. if (removeToc) {
  708. eles[i].parentNode.removeChild(eles[i]);
  709. } else {
  710. eles[i].innerHTML = tocTree;
  711. }
  712. }
  713. }
  714. };
  715. // Implement mouse drag with Ctrl and left button pressed to scroll.
  716. var vds_oriMouseClientX = 0;
  717. var vds_oriMouseClientY = 0;
  718. var vds_readyToScroll = false;
  719. var vds_scrolled = false;
  720. window.onmousedown = function(e) {
  721. e = e || window.event;
  722. // Left button and Ctrl key.
  723. if (e.buttons == 1
  724. && e.ctrlKey
  725. && window.getSelection().rangeCount == 0) {
  726. vds_oriMouseClientX = e.clientX;
  727. vds_oriMouseClientY = e.clientY;
  728. vds_readyToScroll = true;
  729. vds_scrolled = false;
  730. e.preventDefault();
  731. } else {
  732. vds_readyToScroll = false;
  733. vds_scrolled = false;
  734. }
  735. };
  736. window.onmouseup = function(e) {
  737. e = e || window.event;
  738. if (vds_scrolled || vds_readyToScroll) {
  739. // Have been scrolled, restore the cursor style.
  740. document.body.style.cursor = "auto";
  741. e.preventDefault();
  742. }
  743. vds_readyToScroll = false;
  744. vds_scrolled = false;
  745. };
  746. window.onmousemove = function(e) {
  747. e = e || window.event;
  748. if (vds_readyToScroll) {
  749. deltaX = e.clientX - vds_oriMouseClientX;
  750. deltaY = e.clientY - vds_oriMouseClientY;
  751. var threshold = 5;
  752. if (Math.abs(deltaX) >= threshold || Math.abs(deltaY) >= threshold) {
  753. vds_oriMouseClientX = e.clientX;
  754. vds_oriMouseClientY = e.clientY;
  755. if (!vds_scrolled) {
  756. vds_scrolled = true;
  757. document.body.style.cursor = "all-scroll";
  758. }
  759. var scrollX = -deltaX;
  760. var scrollY = -deltaY;
  761. window.scrollBy(scrollX, scrollY);
  762. }
  763. e.preventDefault();
  764. }
  765. };
  766. // @forward: jump forward or backward.
  767. // @relativeLevel: 0 for the same level as current header;
  768. // negative value for upper level;
  769. // positive value is ignored.
  770. var jumpTitle = function(forward, relativeLevel, repeat) {
  771. var headers = document.querySelectorAll("h1, h2, h3, h4, h5, h6");
  772. if (headers.length == 0) {
  773. return;
  774. }
  775. if (currentHeaderIdx == -1) {
  776. // At the beginning, before any headers.
  777. if (relativeLevel < 0 || !forward) {
  778. return;
  779. }
  780. }
  781. var targetIdx = -1;
  782. // -1: skip level check.
  783. var targetLevel = 0;
  784. var delta = 1;
  785. if (!forward) {
  786. delta = -1;
  787. }
  788. var scrollTop = document.documentElement.scrollTop || document.body.scrollTop || window.pageYOffset;
  789. var firstHeader = true;
  790. for (targetIdx = (currentHeaderIdx == -1 ? 0 : currentHeaderIdx);
  791. targetIdx >= 0 && targetIdx < headers.length;
  792. targetIdx += delta) {
  793. var header = headers[targetIdx];
  794. var level = parseInt(header.tagName.substr(1));
  795. if (targetLevel == 0) {
  796. targetLevel = level;
  797. if (relativeLevel < 0) {
  798. targetLevel += relativeLevel;
  799. if (targetLevel < 1) {
  800. // Invalid level.
  801. return false;
  802. }
  803. } else if (relativeLevel > 0) {
  804. targetLevel = -1;
  805. }
  806. }
  807. if (targetLevel == -1 || level == targetLevel) {
  808. if (targetIdx == currentHeaderIdx) {
  809. // If current header is visible, skip it.
  810. // Minus 2 to tolerate some margin.
  811. if (forward || scrollTop - 2 <= headers[targetIdx].offsetTop) {
  812. continue;
  813. }
  814. }
  815. if (--repeat == 0) {
  816. break;
  817. }
  818. } else if (level < targetLevel) {
  819. return;
  820. }
  821. firstHeader = false;
  822. }
  823. if (targetIdx < 0 || targetIdx >= headers.length) {
  824. return;
  825. }
  826. // Disable scroll temporarily.
  827. g_muteScroll = true;
  828. headers[targetIdx].scrollIntoView();
  829. flashAnchor(headers[targetIdx]);
  830. currentHeaderIdx = targetIdx;
  831. content.setHeader(headers[targetIdx].getAttribute("id"));
  832. setTimeout("g_muteScroll = false", 100);
  833. };
  834. var renderCodeBlockLineNumber = function() {
  835. if (!VEnableHighlightLineNumber) {
  836. return;
  837. }
  838. var codes = document.getElementsByTagName('code');
  839. for (var i = 0; i < codes.length; ++i) {
  840. var code = codes[i];
  841. var pare = code.parentElement;
  842. if (pare.tagName.toLowerCase() == 'pre') {
  843. if (VEnableMathjax && pare.classList.contains("lang-mathjax")) {
  844. continue;
  845. }
  846. hljs.lineNumbersBlock(code);
  847. }
  848. }
  849. // Delete the last extra row.
  850. var tables = document.getElementsByTagName('table');
  851. for (var i = 0; i < tables.length; ++i) {
  852. var table = tables[i];
  853. if (table.classList.contains("hljs-ln")) {
  854. var rowCount = table.rows.length;
  855. table.deleteRow(rowCount - 1);
  856. }
  857. }
  858. };
  859. var addClassToCodeBlock = function() {
  860. var hljsClass = 'hljs';
  861. var codes = document.getElementsByTagName('code');
  862. for (var i = 0; i < codes.length; ++i) {
  863. var code = codes[i];
  864. var pare = code.parentElement;
  865. if (pare.tagName.toLowerCase() == 'pre') {
  866. code.classList.add(hljsClass);
  867. if (VEnableMathjax
  868. && (code.classList.contains("lang-mathjax")
  869. || code.classList.contains("language-mathjax"))) {
  870. // Add the class to pre.
  871. pare.classList.add("lang-mathjax");
  872. pare.classList.add("language-mathjax");
  873. }
  874. }
  875. }
  876. };
  877. var listContainsRegex = function(strs, exp) {
  878. for (var i = 0, len = strs.length; i < len; ++i) {
  879. if (exp.test(strs[i])) {
  880. return true;
  881. }
  882. }
  883. return false;
  884. };
  885. var StylesToInline = null;
  886. var initStylesToInline = function() {
  887. console.log('initStylesToInline');
  888. StylesToInline = new Map();
  889. if (VStylesToInline.length == 0) {
  890. return;
  891. }
  892. var rules = VStylesToInline.split(',');
  893. for (var i = 0; i < rules.length; ++i) {
  894. var vals = rules[i].split('$');
  895. if (vals.length != 2) {
  896. continue;
  897. }
  898. var tags = vals[0].split(':');
  899. var pros = vals[1].split(':');
  900. for (var j = 0; j < tags.length; ++j) {
  901. StylesToInline.set(tags[j].toLowerCase(), pros);
  902. }
  903. }
  904. };
  905. // Embed the CSS styles of @ele and all its children.
  906. var embedInlineStyles = function(ele) {
  907. var tagName = ele.tagName.toLowerCase();
  908. var props = StylesToInline.get(tagName);
  909. if (!props) {
  910. props = StylesToInline.get('all');
  911. if (!props) {
  912. return;
  913. }
  914. }
  915. // Embed itself.
  916. var style = window.getComputedStyle(ele, null);
  917. for (var i = 0; i < props.length; ++i) {
  918. var pro = props[i];
  919. ele.style.setProperty(pro, style.getPropertyValue(pro));
  920. }
  921. // Embed children.
  922. var children = ele.children;
  923. for (var i = 0; i < children.length; ++i) {
  924. embedInlineStyles(children[i]);
  925. }
  926. };
  927. var getHtmlWithInlineStyles = function(container) {
  928. if (!StylesToInline) {
  929. initStylesToInline();
  930. }
  931. var children = container.children;
  932. for (var i = 0; i < children.length; ++i) {
  933. embedInlineStyles(children[i]);
  934. }
  935. return container.innerHTML;
  936. };
  937. // Will be called after MathJax rendering finished.
  938. // Make <pre><code>math</code></pre> to <p>math</p>
  939. var postProcessMathJax = function() {
  940. var all = MathJax.Hub.getAllJax();
  941. for (var i = 0; i < all.length; ++i) {
  942. var node = all[i].SourceElement().parentNode;
  943. if (VRemoveMathjaxScript) {
  944. // Remove the SourceElement.
  945. node.removeChild(all[i].SourceElement());
  946. }
  947. if (node.tagName.toLowerCase() == 'code') {
  948. var pre = node.parentNode;
  949. var p = document.createElement('p');
  950. p.innerHTML = node.innerHTML;
  951. pre.parentNode.replaceChild(p, pre);
  952. }
  953. }
  954. finishLogics();
  955. };
  956. function getNodeText(el) {
  957. ret = "";
  958. var length = el.childNodes.length;
  959. for(var i = 0; i < length; i++) {
  960. var node = el.childNodes[i];
  961. if(node.nodeType != 8) {
  962. ret += node.nodeType != 1 ? node.nodeValue : getNodeText(node);
  963. }
  964. }
  965. return ret;
  966. }
  967. var calculateWordCount = function() {
  968. var words = getNodeText(placeholder);
  969. // Char without spaces.
  970. var cns = 0;
  971. var wc = 0;
  972. var cc = words.length;
  973. // 0 - not in word;
  974. // 1 - in English word;
  975. // 2 - in non-English word;
  976. var state = 0;
  977. for (var i = 0; i < cc; ++i) {
  978. var ch = words[i];
  979. if (/\s/.test(ch)) {
  980. if (state != 0) {
  981. state = 0;
  982. }
  983. continue;
  984. } else if (ch.charCodeAt() < 128) {
  985. if (state != 1) {
  986. state = 1;
  987. ++wc;
  988. }
  989. } else {
  990. state = 2;
  991. ++wc;
  992. }
  993. ++cns;
  994. }
  995. content.updateWordCountInfo(wc, cns, cc);
  996. };
  997. // Whether it is a special code block, such as MathJax, Mermaid, or Flowchart.
  998. var specialCodeBlock = function(lang) {
  999. return (VEnableMathjax && lang == 'mathjax')
  1000. || (VEnableMermaid && lang == 'mermaid')
  1001. || (VEnableFlowchart && (lang == 'flowchart' || lang == 'flow'));
  1002. };