diff --git a/css/viki.css b/css/viki.css index 83471ec..1dc6777 100644 --- a/css/viki.css +++ b/css/viki.css @@ -1,3 +1,13 @@ +body { + font-family: "Segoe UI", Helvetica, sans-serif, Tahoma, Arial, Geneva, Georgia, Palatino, "Times New Roman", "Hiragino Sans GB", "冬青黑体", "Microsoft YaHei", "微软雅黑", "Microsoft YaHei UI", "WenQuanYi Micro Hei", "文泉驿雅黑", Dengxian, "等线体", STXihei, "华文细黑", "Liberation Sans", "Droid Sans", NSimSun, "新宋体", SimSun, "宋体"; + color: #222222; + background: #F5F5F5; +} + +a { + word-break: break-all; +} + .viki-img-package { text-align: center; } @@ -35,8 +45,8 @@ span.viki-img-caption { width: 100%; height: 100%; overflow: hidden; - background-color: rgb(68, 68, 68); - background-color: rgba(68, 68, 68, 0.95); + background-color: rgb(234, 234, 234); + background-color: rgba(234, 234, 234, 0.95); z-index: 2000; } @@ -69,10 +79,10 @@ span.viki-img-caption { /* The Close Button */ span.viki-modal-close { position: absolute; - z-index: 1000; + z-index: 2002; top: 15px; right: 35px; - color: #DADADA; + color: #666666; font-size: 40px; font-weight: bold; transition: 0.3s; @@ -80,16 +90,50 @@ span.viki-modal-close { span.viki-modal-close:hover, span.viki-modal-close:focus { - color: #EEEEEE; + color: #222222; text-decoration: none; cursor: pointer; } /* View Image */ +pre { + display: block; + overflow-y: hidden; + overflow-x: auto; +} + +code { + font-family: Consolas, Monaco, Monospace, Courier; + color: #8E24AA; + word-break: break-all; +} + +pre code { + display: block; + overflow-x: auto; + padding: 0.5em; + color: #222222; + background-color: #E0E0E0; + border-left: .5em solid #00897B; + font-family: Consolas, Monaco, Monospace, Courier; + white-space: pre; +} + pre code.viki-markdown-metadata { border-left: .5rem solid #80CBC4; } +blockquote { + color: #666; + border-left: .5em solid #7a7a7a; + padding: 0 1em; + margin-left: 0; +} + +blockquote p { + color: #666; +} + div.viki-mermaid-diagram { margin: 16px 0px 16px 0px; overflow-y: hidden; @@ -109,6 +153,43 @@ div.viki-plantuml-diagram { overflow: hidden; } +table { + padding: 0; + border-collapse: collapse; +} + +table tr { + border-top: 2px solid #cccccc; + background-color: white; + margin: 0; + padding: 0; +} + +table tr:nth-child(2n) { + background-color: #f8f8f8; +} + +table tr th { + font-weight: bold; + border: 2px solid #cccccc; + margin: 0; + padding: 6px 13px; +} + +table tr td { + border: 2px solid #cccccc; + margin: 0; + padding: 6px 13px; +} + +table tr th :first-child, table tr td :first-child { + margin-top: 0; +} + +table tr th :last-child, table tr td :last-child { + margin-bottom: 0; +} + /* For Highlight.js Line Number */ table.hljs-ln tr { border: none; @@ -158,3 +239,26 @@ x-eqs > span { text-align:right; } /* Mathjax */ + +.bd-toc { + position: -webkit-sticky; + position: sticky; + top: 4rem; + height: calc(100vh - 4rem); + overflow-y: auto; + padding-top: 1.5rem; + padding-bottom: 1.5rem; + font-size: .875rem; +} + +.bd-toc > ul { + border-left: 1px solid #ddd; +} + +.bd-toc ul { + list-style-type: none; +} + +.bd-toc a { + color: #808080; +} diff --git a/js/contentworker.js b/js/contentworker.js index d18fc79..82d0180 100644 --- a/js/contentworker.js +++ b/js/contentworker.js @@ -1,6 +1,7 @@ import logger from "./logger.js"; import Worker from "./worker.js"; import MarkdownRenderer from "./markdownrenderer.js"; +import TocRenderer from "./tocrenderer.js"; // Render data from this.viki.info.data. // - Navigation Panel for notebook's note; @@ -19,9 +20,15 @@ class ContentWorker extends Worker { run() { this.renderSkelecton(); - if (this.isMarkdown(this.viki.info.target)) { - let mder = new MarkdownRenderer($('#' + this.viki.info.contentContainerId)); - mder.render(this.viki.config.markdown, this.viki.info.data); + let info = this.viki.info; + if (this.isMarkdown(info.target)) { + let mder = new MarkdownRenderer($('#' + info.contentContainerId)); + mder.render(this.viki.config.markdown, info.data); + } + + if (info.toc) { + let tocer = new TocRenderer($('#' + info.tocContainerId)); + tocer.render($('#' + info.contentContainerId)); } this.viki.scheduleNext(); @@ -29,19 +36,76 @@ class ContentWorker extends Worker { // Render the container to hold contents. renderSkelecton() { - this.viki.info.contentContainerId = 'viki-content'; - this.viki.info.tocContainerId = 'viki-toc'; + let info = this.viki.info; + info.contentContainerId = 'viki-content'; + if (info.toc) { + info.tocContainerId = 'viki-toc'; + } + + if (info.navi) { + info.naviContainerId = 'viki-navi'; + } let mainDiv = $(`
`); - let containerDiv = $(``); + + let containerDivClass = 'row flex-xl-nowrap'; + if (!info.navi) { + containerDivClass += ' justify-content-md-center'; + } + + let containerDiv = $(``); mainDiv.append(containerDiv); - let contentDiv = $(``); + let naviDivClass = null; + let contentDivClass = null; + let tocDivClass = null; + + if (info.toc) { + if (info.navi) { + // Three panels. + // TODO. + } else { + // Two panels. + contentDivClass = "col-12 col-md-9 col-lg-8 col-xl-8 py-md-3 pl-md-5 bd-content"; + tocDivClass = "d-none d-md-block col-md-3 col-lg-3 col-xl-2 bd-toc"; + } + } else { + if (info.navi) { + // Two panels. + // TODO. + } else { + // Single panels. + contentDivClass = "col-12 col-md-9 py-md-3 pl-md-5 bd-content"; + } + } + + let naviDiv = null; + let contentDiv = null; + let tocDiv = null; - let tocDiv = $(``); + if (naviDivClass) { - containerDiv.append(contentDiv); - containerDiv.append(tocDiv); + } + + if (contentDivClass) { + contentDiv = $(``); + } + + if (tocDivClass) { + tocDiv = $(``); + } + + if (naviDiv) { + containerDiv.append(naviDiv); + } + + if (contentDiv) { + containerDiv.append(contentDiv); + } + + if (tocDiv) { + containerDiv.append(tocDiv); + } $('body').append(mainDiv); } diff --git a/js/markdownit.js b/js/markdownit.js index 8f06155..34aa5a7 100644 --- a/js/markdownit.js +++ b/js/markdownit.js @@ -199,6 +199,8 @@ class MarkdownIt { this.renderPlantUML(p_containerNode, this.config.langPrefix + 'puml'); + this.makeImageFluid(p_containerNode); + this.addClassToCodeBlock(p_containerNode); if (this.config.codeBlockLineNumber) { @@ -554,6 +556,18 @@ class MarkdownIt { console.log("err", err); } } + + makeImageFluid(p_node) { + let imgs = p_node.find('img'); + for (let i = 0; i < imgs.length; ++i) { + let img = imgs[i]; + if (img.id === 'image-view') { + continue; + } + + img.classList.add('img-fluid'); + } + } } export default MarkdownIt; diff --git a/js/naviworker.js b/js/naviworker.js index cef37a2..46b744a 100644 --- a/js/naviworker.js +++ b/js/naviworker.js @@ -5,6 +5,7 @@ class NaviItem { constructor() { this.text = ""; this.target = ""; + this.toc = true; // Only 2 levels. this.children = []; @@ -14,6 +15,10 @@ class NaviItem { this.text = p_jobj.text; this.target = p_jobj.target; + if (p_jobj.toc != null) { + this.toc = p_jobj.toc; + } + if (!this.target) { if (!p_jobj.children || p_jobj.children.length == 0) { return false; @@ -27,6 +32,10 @@ class NaviItem { return false; } + if (p_jobj.children[i].toc != null) { + child.toc = p_jobj.children[i].toc; + } + this.children.push(child); } } @@ -148,6 +157,8 @@ class NaviWorker extends Worker { var it = items[i].matchTarget(this.viki.info.target); if (it) { activeItem = it; + + this.viki.info.toc = it.toc; } } diff --git a/js/tocrenderer.js b/js/tocrenderer.js new file mode 100644 index 0000000..2011b3f --- /dev/null +++ b/js/tocrenderer.js @@ -0,0 +1,269 @@ +import logger from "./logger.js"; +import Utils from "./utils.js"; + +class TocRenderer { + constructor(p_node) { + this.containerNode = p_node; + this.toc = []; + } + + // Render the toc of @p_contentNode into this.containerNode. + render(p_contentNode) { + this.containerNode.empty(); + this.toc = []; + + // Fetch the outline. + let headers = p_contentNode.find('h1, h2, h3, h4, h5, h6'); + for (let i = 0; i < headers.length; ++i) { + let header = headers[i]; + + this.toc.push({ + level: parseInt(header.tagName.substr(1)), + anchor: header.id, + title: header.textContent + }); + } + + if (this.toc.length === 0) { + return; + } + + let utils = new Utils(); + let tocTree = utils.tocToTree(this.toc); + this.containerNode.html(tocTree); + } + + /* +var toc = []; + +var setVisible = function(node, visible) { + var cl = 'hide-none'; + if (visible) { + node.classList.remove(cl); + } else { + node.classList.add(cl); + } +}; + +var isVisible = function(node) { + var cl = 'hide-none'; + return !node.classList.contains(cl); +}; + +var setPostContentExpanded = function(node, expanded) { + var cl = 'col-expand'; + if (expanded) { + node.classList.add(cl); + } else { + node.classList.remove(cl); + } +}; + +var setOutlinePanelVisible = function(visible) { + var outlinePanel = document.getElementById('outline-panel'); + var postContent = document.getElementById('post-content'); + + setVisible(outlinePanel, visible); + setPostContentExpanded(postContent, !visible); +}; + +var isOutlinePanelVisible = function() { + var outlinePanel = document.getElementById('outline-panel'); + return isVisible(outlinePanel); +}; + +window.addEventListener('load', function() { + var outlinePanel = document.getElementById('outline-panel'); + outlinePanel.style.display = 'initial'; + + var floatingContainer = document.getElementById('container-floating'); + floatingContainer.style.display = 'initial'; + + var outlineContent = document.getElementById('outline-content'); + var postContent = document.getElementById('post-content'); + + // Fetch the outline. + var headers = postContent.querySelectorAll("h1, h2, h3, h4, h5, h6"); + toc = []; + for (var i = 0; i < headers.length; ++i) { + var header = headers[i]; + + toc.push({ + level: parseInt(header.tagName.substr(1)), + anchor: header.id, + title: header.textContent + }); + } + + if (toc.length == 0) { + setOutlinePanelVisible(false); + setVisible(floatingContainer, false); + return; + } + + var baseLevel = baseLevelOfToc(toc); + var tocTree = tocToTree(toPerfectToc(toc, baseLevel), baseLevel); + + outlineContent.innerHTML = tocTree; + setOutlinePanelVisible(true); + setVisible(floatingContainer, true); +}); + +// 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