var content; var keyState = 0; var VMermaidDivClass = 'mermaid-diagram'; var VFlowchartDivClass = 'flowchart-diagram'; if (typeof VEnableMermaid == 'undefined') { VEnableMermaid = false; } else if (VEnableMermaid) { mermaidAPI.initialize({ startOnLoad: false }); } if (typeof VEnableFlowchart == 'undefined') { VEnableFlowchart = false; } if (typeof VEnableMathjax == 'undefined') { VEnableMathjax = false; } // Add a caption (using alt text) under the image. var VImageCenterClass = 'img-center'; var VImageCaptionClass = 'img-caption'; var VImagePackageClass = 'img-package'; if (typeof VEnableImageCaption == 'undefined') { VEnableImageCaption = false; } new QWebChannel(qt.webChannelTransport, function(channel) { content = channel.objects.content; if (typeof updateHtml == "function") { updateHtml(content.html); content.htmlChanged.connect(updateHtml); } if (typeof updateText == "function") { content.textChanged.connect(updateText); content.updateText(); } content.requestScrollToAnchor.connect(scrollToAnchor); if (typeof highlightText == "function") { content.requestHighlightText.connect(highlightText); content.noticeReadyToHighlightText(); } }); var g_muteScroll = false; var scrollToAnchor = function(anchor) { g_muteScroll = true; if (!anchor) { window.scrollTo(0, 0); g_muteScroll = false; return; } var anc = document.getElementById(anchor); if (anc != null) { anc.scrollIntoView(); } // Disable scroll temporarily. setTimeout("g_muteScroll = false", 100); }; window.onwheel = function(e) { e = e || window.event; var ctrl = !!e.ctrlKey; if (ctrl) { e.preventDefault(); } } window.onscroll = function() { if (g_muteScroll) { return; } var scrollTop = document.documentElement.scrollTop || document.body.scrollTop || window.pageYOffset; var eles = document.querySelectorAll("h1, h2, h3, h4, h5, h6"); if (eles.length == 0) { content.setHeader(""); return; } var curIdx = -1; var biaScrollTop = scrollTop + 50; for (var i = 0; i < eles.length; ++i) { if (biaScrollTop >= eles[i].offsetTop) { curIdx = i; } else { break; } } var curHeader = null; if (curIdx != -1) { curHeader = eles[curIdx].getAttribute("id"); } content.setHeader(curHeader ? curHeader : ""); }; document.onkeydown = function(e) { e = e || window.event; var key; var shift; var ctrl; if (e.which) { key = e.which; } else { key = e.keyCode; } shift = !!e.shiftKey; ctrl = !!e.ctrlKey; switch (key) { case 74: // J window.scrollBy(0, 100); keyState = 0; break; case 75: // K window.scrollBy(0, -100); keyState = 0; break; case 72: // H window.scrollBy(-100, 0); keyState = 0; break; case 76: // L window.scrollBy(100, 0); keyState = 0; break; case 71: // G if (shift) { var scrollLeft = document.documentElement.scrollLeft || document.body.scrollLeft || window.pageXOffset; var scrollHeight = document.documentElement.scrollHeight || document.body.scrollHeight; window.scrollTo(scrollLeft, scrollHeight); keyState = 0; break; } else { if (keyState == 0) { keyState = 1; } else if (keyState == 1) { keyState = 0; var scrollLeft = document.documentElement.scrollLeft || document.body.scrollLeft || window.pageXOffset; window.scrollTo(scrollLeft, 0); } break; } return; case 85: // U keyState = 0; if (ctrl) { var clientHeight = document.documentElement.clientHeight; window.scrollBy(0, -clientHeight / 2); break; } return; case 68: // D keyState = 0; if (ctrl) { var clientHeight = document.documentElement.clientHeight; window.scrollBy(0, clientHeight / 2); break; } return; default: content.keyPressEvent(key, ctrl, shift); keyState = 0; return; } e.preventDefault(); }; var mermaidParserErr = false; var mermaidIdx = 0; if (VEnableMermaid) { mermaidAPI.parseError = function(err, hash) { content.setLog("err: " + err); mermaidParserErr = true; // Clean the container element, or mermaidAPI won't render the graph with // the same id. var errGraph = document.getElementById('mermaid-diagram-' + mermaidIdx); var parentNode = errGraph.parentElement; parentNode.outerHTML = ''; delete parentNode; }; } // @className, the class name of the mermaid code block, such as 'lang-mermaid'. var renderMermaid = function(className) { if (!VEnableMermaid) { return; } var codes = document.getElementsByTagName('code'); mermaidIdx = 0; for (var i = 0; i < codes.length; ++i) { var code = codes[i]; if (code.classList.contains(className)) { if (renderMermaidOne(code)) { // replaceChild() will decrease codes.length. --i; } } } }; // Render @code as Mermaid graph. // Returns true if succeeded. var renderMermaidOne = function(code) { // Mermaid code block. mermaidParserErr = false; mermaidIdx++; try { // Do not increment mermaidIdx here. var graph = mermaidAPI.render('mermaid-diagram-' + mermaidIdx, code.innerText, function(){}); } catch (err) { content.setLog("err: " + err); return false; } if (mermaidParserErr || typeof graph == "undefined") { return false; } var graphDiv = document.createElement('div'); graphDiv.classList.add(VMermaidDivClass); graphDiv.innerHTML = graph; var preNode = code.parentNode; preNode.classList.add(VMermaidDivClass); preNode.replaceChild(graphDiv, code); return true; }; var flowchartIdx = 0; // @className, the class name of the flowchart code block, such as 'lang-flowchart'. var renderFlowchart = function(className) { if (!VEnableFlowchart) { return; } var codes = document.getElementsByTagName('code'); flowchartIdx = 0; for (var i = 0; i < codes.length; ++i) { var code = codes[i]; if (code.classList.contains(className)) { if (renderFlowchartOne(code, flowchartIdx)) { // replaceChild() will decrease codes.length. --i; } } } }; // Render @code as Flowchart.js graph. // Returns true if succeeded. var renderFlowchartOne = function(code) { // Flowchart code block. flowchartIdx++; try { var graph = flowchart.parse(code.innerText); } catch (err) { content.setLog("err: " + err); return false; } if (typeof graph == "undefined") { return false; } var graphDiv = document.createElement('div'); graphDiv.id = 'flowchart-diagram-' + flowchartIdx; graphDiv.classList.add(VFlowchartDivClass); var preNode = code.parentNode; preNode.replaceChild(graphDiv, code); // Draw on it after adding it to page. try { graph.drawSVG(graphDiv.id); } catch (err) { content.setLog("err: " + err); preNode.replaceChild(code, graphDiv); delete graphDiv; return false; } preNode.classList.add(VMermaidDivClass); return true; }; var isImageBlock = function(img) { var pn = img.parentNode; return (pn.children.length == 1) && (pn.innerText == ''); }; var isImageWithBr = function(img) { var sibling = img.nextSibling; while (sibling) { if (sibling.nodeType == 8) { // Comment node. // Just continue. sibling = sibling.nextSibling; continue; } else if (sibling.nodeType == 1) { if (sibling.tagName == 'BR') { break; } } return false; } sibling = img.previousSibling; while (sibling) { if (sibling.nodeType == 8) { // Comment node. sibling = sibling.previousSibling; continue; } else if (sibling.nodeType == 1) { // Element node. if (sibling.tagName == 'BR') { break; } } else if (sibling.nodeType == 3) { // Text node. if (sibling.nodeValue == '\n') { var tmp = sibling.previousSibling; if (tmp && tmp.tagName == 'BR') { break; } } } return false; } return true; }; var getImageType = function(img) { var type = -1; if (isImageBlock(img)) { type = 0; } else if (isImageWithBr(img)) { type = 1; } return type; }; // Center the image block and insert the alt text as caption. var insertImageCaption = function() { if (!VEnableImageCaption) { return; } var imgs = document.getElementsByTagName('img'); for (var i = 0; i < imgs.length; ++i) { var img = imgs[i]; var type = getImageType(img); if (type == -1) { continue; } else if (type == 1) { // Insert a div as the parent of the img. var imgPack = document.createElement('div'); img.insertAdjacentElement('afterend', imgPack); imgPack.appendChild(img); } // Make the parent img-package. img.parentNode.classList.add(VImagePackageClass); // Make it center. img.classList.add(VImageCenterClass); if (img.alt == '') { continue; } // Add caption. var captionDiv = document.createElement('div'); captionDiv.classList.add(VImageCaptionClass); captionDiv.innerText = img.alt; img.insertAdjacentElement('afterend', captionDiv); } } // The renderer specific code should call this function once thay have finished // markdown-specifi handle logics, such as Mermaid, MathJax. var finishLogics = function() { content.finishLogics(); }; // Escape @text to Html. var escapeHtml = function(text) { var map = { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }; return text.replace(/[&<>"']/g, function(m) { return map[m]; }); }; // Return the topest level of @toc, starting from 1. var baseLevelOfToc = function(p_toc) { var level = -1; for (i in p_toc) { if (level == -1) { level = p_toc[i].level; } else if (level > p_toc[i].level) { level = p_toc[i].level; } } if (level == -1) { level = 1; } return level; }; // Handle wrong title levels, such as '#' followed by '###' var toPerfectToc = function(p_toc, p_baseLevel) { var i; var curLevel = p_baseLevel - 1; var perfToc = []; for (i in p_toc) { var item = p_toc[i]; // Insert empty header. while (item.level > curLevel + 1) { curLevel += 1; var tmp = { level: curLevel, anchor: '', title: '[EMPTY]' }; perfToc.push(tmp); } perfToc.push(item); curLevel = item.level; } return perfToc; }; var itemToHtml = function(item) { return '' + item.title + ''; }; // Turn a perfect toc to a tree using