refine markdown render logics

Add common templates markdown_template.html and
markdown_template.js.

Hoedow provides hoedown.js.
Marked provides marked.js.
This commit is contained in:
Le Tan 2017-03-20 20:44:31 +08:00
parent ee6fc69bb1
commit d47dd92f59
18 changed files with 372 additions and 504 deletions

41
src/resources/hoedown.js Normal file
View File

@ -0,0 +1,41 @@
var placeholder = document.getElementById('placeholder');
var scrollToAnchor = function(anchor) {
var anc = document.getElementById(anchor);
if (anc != null) {
anc.scrollIntoView();
}
};
var updateHtml = function(html) {
placeholder.innerHTML = html;
var codes = document.getElementsByTagName('code');
for (var i = 0; i < codes.length; ++i) {
if (codes[i].parentElement.tagName.toLowerCase() == 'pre') {
hljs.highlightBlock(codes[i]);
}
}
}
var onWindowScroll = function() {
var scrollTop = document.documentElement.scrollTop || document.body.scrollTop || window.pageYOffset;
var eles = document.querySelectorAll("h1, h2, h3, h4, h5, h6");
if (eles.length == 0) {
return;
}
var curIdx = 0;
var biaScrollTop = scrollTop + 50;
for (var i = 0; i < eles.length; ++i) {
if (biaScrollTop >= eles[i].offsetTop) {
curIdx = i;
} else {
break;
}
}
var curHeader = eles[curIdx].getAttribute("id");
if (curHeader != null) {
content.setHeader(curHeader);
}
}

View File

@ -0,0 +1,19 @@
<!doctype html>
<html lang="en">
<meta charset="utf-8">
<head>
<style type="text/css">
<!-- BACKGROUND_PLACE_HOLDER -->
</style>
<link rel="stylesheet" type="text/css" href="CSS_PLACE_HOLDER">
<link rel="stylesheet" type="text/css" href="qrc:/utils/highlightjs/styles/default.css">
<script src="qrc:/resources/qwebchannel.js"></script>
<script src="qrc:/utils/highlightjs/highlight.pack.js"></script>
<!-- EXTRA_PLACE_HOLDER -->
<script src="JS_PLACE_HOLDER" defer></script>
<script src="qrc:/resources/markdown_template.js" defer></script>
</head>
<body>
<div id="placeholder"></div>
</body>
</html>

View File

@ -0,0 +1,96 @@
var content;
var keyState = 0;
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);
});
window.onscroll = onWindowScroll;
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);
break;
}
return;
case 68: // D
keyState = 0;
if (ctrl) {
var clientHeight = document.documentElement.clientHeight;
window.scrollBy(0, clientHeight);
break;
}
return;
default:
content.keyPressEvent(key);
keyState = 0;
return;
}
e.preventDefault();
}

154
src/resources/marked.js Normal file
View File

@ -0,0 +1,154 @@
var placeholder = document.getElementById('placeholder');
var renderer = new marked.Renderer();
var toc = []; // Table of contents as a list
var nameCounter = 0;
renderer.heading = function(text, level) {
// Use number to avoid issues with Chinese
var escapedText = 'toc_' + nameCounter++;
toc.push({
level: level,
anchor: escapedText,
title: text
});
return '<h' + level + ' id="' + escapedText + '">' + text + '</h' + level + '>';
};
// Highlight.js to highlight code block
marked.setOptions({
highlight: function(code) {
return hljs.highlightAuto(code).value;
}
});
var markdownToHtml = function(markdown, needToc) {
toc = [];
var html = marked(markdown, { renderer: renderer });
nameCounter = 0;
if (needToc) {
return html.replace(/<p>\[TOC\]<\/p>/ig, '<div class="vnote-toc"></div>');
} else {
return html;
}
};
// Handle wrong title levels, such as '#' followed by '###'
var toPerfectToc = function(toc) {
var i;
var curLevel = 1;
var perfToc = [];
for (i in toc) {
var item = toc[i];
while (item.level > curLevel + 1) {
curLevel += 1;
var tmp = { level: curLevel,
anchor: item.anchor,
title: '[EMPTY]'
};
perfToc.push(tmp);
}
perfToc.push(item);
curLevel = item.level;
}
return perfToc;
};
var itemToHtml = function(item) {
return '<a href="#' + item.anchor + '">' + item.title + '</a>';
};
// Turn a perfect toc to a tree using <ul>
var tocToTree = function(toc) {
var i;
var front = '<li>';
var ending = ['</li>'];
var curLevel = 1;
for (i in toc) {
var item = toc[i];
if (item.level == curLevel) {
front += '</li>';
front += '<li>';
front += itemToHtml(item);
} else if (item.level > curLevel) {
// assert(item.level - curLevel == 1)
front += '<ul>';
ending.push('</ul>');
front += '<li>';
front += itemToHtml(item);
ending.push('</li>');
curLevel = item.level;
} else {
while (item.level < curLevel) {
var ele = ending.pop();
front += ele;
if (ele == '</ul>') {
curLevel--;
}
}
front += '</li>';
front += '<li>';
front += itemToHtml(item);
}
}
while (ending.length > 0) {
front += ending.pop();
}
front = front.replace("<li></li>", "");
front = '<ul>' + front + '</ul>';
return front;
};
var handleToc = function(needToc) {
var tocTree = tocToTree(toPerfectToc(toc));
content.setToc(tocTree);
// Add it to html
if (needToc) {
var eles = document.getElementsByClassName('vnote-toc');
for (var i = 0; i < eles.length; ++i) {
eles[i].innerHTML = tocTree;
}
}
};
var mdHasTocSection = function(markdown) {
var n = markdown.search(/(\n|^)\[toc\]/i);
return n != -1;
};
var updateText = function(text) {
var needToc = mdHasTocSection(text);
var html = markdownToHtml(text, needToc);
placeholder.innerHTML = html;
handleToc(needToc);
};
var scrollToAnchor = function(anchor) {
var anc = document.getElementById(anchor);
if (anc != null) {
anc.scrollIntoView();
}
};
var onWindowScroll = function() {
var scrollTop = document.documentElement.scrollTop || document.body.scrollTop || window.pageYOffset;
var eles = document.querySelectorAll("h1, h2, h3, h4, h5, h6");
if (eles.length == 0) {
return;
}
var curIdx = 0;
var biaScrollTop = scrollTop + 50;
for (var i = 0; i < eles.length; ++i) {
if (biaScrollTop >= eles[i].offsetTop) {
curIdx = i;
} else {
break;
}
}
var curHeader = eles[curIdx].getAttribute("id");
if (curHeader != null) {
content.setHeader(curHeader);
}
}

View File

@ -1,2 +0,0 @@
</body>
</html>

View File

@ -1,145 +0,0 @@
<!doctype html>
<html lang="en">
<meta charset="utf-8">
<head>
<style type="text/css">
<!-- BACKGROUND_PLACE_HOLDER -->
</style>
<link rel="stylesheet" type="text/css" href="CSS_PLACE_HOLDER">
<link rel="stylesheet" type="text/css" href="qrc:/utils/highlightjs/styles/default.css">
<script src="qrc:/resources/qwebchannel.js"></script>
<script src="qrc:/utils/highlightjs/highlight.pack.js"></script>
</head>
<body>
<div id="placeholder"></div>
<script>
'use strict';
var placeholder = document.getElementById('placeholder');
var content;
var keyState = 0;
var scrollToAnchor = function(anchor) {
document.getElementById(anchor).scrollIntoView();
};
var updateHtml = function(html) {
placeholder.innerHTML = html;
var codes = document.getElementsByTagName('code');
for (var i = 0; i < codes.length; ++i) {
if (codes[i].parentElement.tagName.toLowerCase() == 'pre') {
hljs.highlightBlock(codes[i]);
}
}
}
new QWebChannel(qt.webChannelTransport,
function(channel) {
content = channel.objects.content;
updateHtml(content.html);
content.htmlChanged.connect(updateHtml);
content.requestScrollToAnchor.connect(scrollToAnchor);
}
);
window.onscroll = function() {
var scrollTop = document.documentElement.scrollTop || document.body.scrollTop || window.pageYOffset;
var eles = document.querySelectorAll("h1, h2, h3, h4, h5, h6");
if (eles.length == 0) {
return;
}
var curIdx = 0;
var biaScrollTop = scrollTop + 50;
for (var i = 0; i < eles.length; ++i) {
if (biaScrollTop >= eles[i].offsetTop) {
curIdx = i;
} else {
break;
}
}
var curHeader = eles[curIdx].getAttribute("id");
if (curHeader != null) {
content.setHeader(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);
break;
}
return;
case 68: // D
keyState = 0;
if (ctrl) {
var clientHeight = document.documentElement.clientHeight;
window.scrollBy(0, clientHeight);
break;
}
return;
default:
content.keyPressEvent(key);
keyState = 0;
return;
}
e.preventDefault();
}
</script>

View File

@ -1,267 +0,0 @@
<!doctype html>
<html lang="en">
<meta charset="utf-8">
<head>
<style type="text/css">
<!-- BACKGROUND_PLACE_HOLDER -->
</style>
<link rel="stylesheet" type="text/css" href="CSS_PLACE_HOLDER">
<link rel="stylesheet" type="text/css" href="qrc:/utils/highlightjs/styles/default.css">
<script src="qrc:/utils/marked/marked.min.js"></script>
<script src="qrc:/resources/qwebchannel.js"></script>
<script src="qrc:/utils/highlightjs/highlight.pack.js"></script>
</head>
<body>
<div id="placeholder"></div>
<script>
'use strict';
var placeholder = document.getElementById('placeholder');
var renderer = new marked.Renderer();
var toc = []; // Table of contents as a list
var content; // Channel variable with content
var nameCounter = 0;
var keyState = 0;
renderer.heading = function (text, level) {
// Use number to avoid issues with Chinese
var escapedText = 'toc_' + nameCounter++;
toc.push({
level: level,
anchor: escapedText,
title: text
});
return '<h' + level + ' id="' +
escapedText + '">' +
text + '</h' + level + '>';
};
// Highlight.js to highlight code block
marked.setOptions({
highlight: function (code) {
return hljs.highlightAuto(code).value;
}
});
var markdownToHtml = function (markdown, needToc) {
toc = [];
var html = marked(markdown, { renderer: renderer });
nameCounter = 0;
if (needToc) {
return html.replace(/<p>\[TOC\]<\/p>/ig, '<div class="vnote-toc"></div>');
} else {
return html;
}
};
// Handle wrong title levels, such as '#' followed by '###'
var toPerfectToc = function (toc) {
var i;
var curLevel = 1;
var perfToc = [];
for (i in toc) {
var item = toc[i];
while (item.level > curLevel + 1) {
curLevel += 1;
var tmp = { level: curLevel,
anchor: item.anchor,
title: '[EMPTY]'
};
perfToc.push(tmp);
}
perfToc.push(item);
curLevel = item.level;
}
return perfToc;
};
var itemToHtml = function (item) {
return '<a href="#' + item.anchor + '">' + item.title + '</a>';
};
// Turn a perfect toc to a tree using <ul>
var tocToTree = function (toc) {
var i;
var front = '<li>';
var ending = ['</li>'];
var curLevel = 1;
for (i in toc) {
var item = toc[i];
if (item.level == curLevel) {
front += '</li>';
front += '<li>';
front += itemToHtml(item);
} else if (item.level > curLevel) {
// assert(item.level - curLevel == 1)
front += '<ul>';
ending.push('</ul>');
front += '<li>';
front += itemToHtml(item);
ending.push('</li>');
curLevel = item.level;
} else {
while (item.level < curLevel) {
var ele = ending.pop();
front += ele;
if (ele == '</ul>') {
curLevel--;
}
}
front += '</li>';
front += '<li>';
front += itemToHtml(item);
}
}
while (ending.length > 0) {
front += ending.pop();
}
front = front.replace("<li></li>", "");
front = '<ul>' + front + '</ul>';
return front;
};
var handleToc = function(needToc) {
var tocTree = tocToTree(toPerfectToc(toc));
content.setToc(tocTree);
// Add it to html
if (needToc) {
var eles = document.getElementsByClassName('vnote-toc');
for (var i = 0; i < eles.length; ++i) {
eles[i].innerHTML = tocTree;
}
}
};
var mdHasTocSection = function(markdown) {
var n = markdown.search(/(\n|^)\[toc\]/i);
return n != -1;
};
var updateText = function(text) {
var needToc = mdHasTocSection(text);
var html = markdownToHtml(text, needToc);
placeholder.innerHTML = html;
handleToc(needToc);
};
var scrollToAnchor = function(anchor) {
document.getElementById(anchor).scrollIntoView();
};
new QWebChannel(qt.webChannelTransport,
function(channel) {
content = channel.objects.content;
updateText(content.text);
content.textChanged.connect(updateText);
content.requestScrollToAnchor.connect(scrollToAnchor);
}
);
window.onscroll = function() {
var scrollTop = document.documentElement.scrollTop || document.body.scrollTop || window.pageYOffset;
var eles = document.querySelectorAll("h1, h2, h3, h4, h5, h6");
if (eles.length == 0) {
return;
}
var curIdx = 0;
var biaScrollTop = scrollTop + 50;
for (var i = 0; i < eles.length; ++i) {
if (biaScrollTop >= eles[i].offsetTop) {
curIdx = i;
} else {
break;
}
}
var curHeader = eles[curIdx].getAttribute("id");
if (curHeader != null) {
content.setHeader(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);
break;
}
return;
case 68: // D
keyState = 0;
if (ctrl) {
var clientHeight = document.documentElement.clientHeight;
window.scrollBy(0, clientHeight);
break;
}
return;
default:
content.keyPressEvent(key);
keyState = 0;
return;
}
e.preventDefault();
}
</script>
</body>
</html>

View File

@ -1,8 +1,5 @@
[global] [global]
welcome_page_path=:/resources/welcome.html welcome_page_path=:/resources/welcome.html
template_path=:/resources/template.html
pre_template_path=:/resources/pre_template.html
post_template_path=:/resources/post_template.html
template_css_url=qrc:/resources/markdown.css template_css_url=qrc:/resources/markdown.css
current_notebook=0 current_notebook=0
tab_stop_width=4 tab_stop_width=4

View File

@ -43,9 +43,6 @@ void VConfigManager::initialize()
baseEditPalette = QTextEdit().palette(); baseEditPalette = QTextEdit().palette();
welcomePagePath = getConfigFromSettings("global", "welcome_page_path").toString(); welcomePagePath = getConfigFromSettings("global", "welcome_page_path").toString();
templatePath = getConfigFromSettings("global", "template_path").toString();
preTemplatePath = getConfigFromSettings("global", "pre_template_path").toString();
postTemplatePath = getConfigFromSettings("global", "post_template_path").toString();
templateCssUrl = getConfigFromSettings("global", "template_css_url").toString(); templateCssUrl = getConfigFromSettings("global", "template_css_url").toString();
curNotebookIndex = getConfigFromSettings("global", "current_notebook").toInt(); curNotebookIndex = getConfigFromSettings("global", "current_notebook").toInt();

View File

@ -51,8 +51,6 @@ public:
inline QString getWelcomePagePath() const; inline QString getWelcomePagePath() const;
inline QString getTemplatePath() const;
inline QString getTemplateCssUrl() const; inline QString getTemplateCssUrl() const;
inline QFont getBaseEditFont() const; inline QFont getBaseEditFont() const;
@ -68,9 +66,6 @@ public:
inline MarkdownConverterType getMdConverterType() const; inline MarkdownConverterType getMdConverterType() const;
inline void setMarkdownConverterType(MarkdownConverterType type); inline void setMarkdownConverterType(MarkdownConverterType type);
inline QString getPreTemplatePath() const;
inline QString getPostTemplatePath() const;
inline int getTabStopWidth() const; inline int getTabStopWidth() const;
inline void setTabStopWidth(int tabStopWidth); inline void setTabStopWidth(int tabStopWidth);
inline bool getIsExpandTab() const; inline bool getIsExpandTab() const;
@ -137,9 +132,6 @@ private:
QPalette mdEditPalette; QPalette mdEditPalette;
QVector<HighlightingStyle> mdHighlightingStyles; QVector<HighlightingStyle> mdHighlightingStyles;
QString welcomePagePath; QString welcomePagePath;
QString templatePath;
QString preTemplatePath;
QString postTemplatePath;
QString templateCssUrl; QString templateCssUrl;
int curNotebookIndex; int curNotebookIndex;
@ -212,11 +204,6 @@ inline QString VConfigManager::getWelcomePagePath() const
return welcomePagePath; return welcomePagePath;
} }
inline QString VConfigManager::getTemplatePath() const
{
return templatePath;
}
inline QString VConfigManager::getTemplateCssUrl() const inline QString VConfigManager::getTemplateCssUrl() const
{ {
return templateCssUrl; return templateCssUrl;
@ -266,16 +253,6 @@ inline MarkdownConverterType VConfigManager::getMdConverterType() const
return mdConverterType; return mdConverterType;
} }
inline QString VConfigManager::getPreTemplatePath() const
{
return preTemplatePath;
}
inline QString VConfigManager::getPostTemplatePath() const
{
return postTemplatePath;
}
inline void VConfigManager::setMarkdownConverterType(MarkdownConverterType type) inline void VConfigManager::setMarkdownConverterType(MarkdownConverterType type)
{ {
if (mdConverterType == type) { if (mdConverterType == type) {

View File

@ -1,29 +1,15 @@
#include "vdocument.h" #include "vdocument.h"
#include "vfile.h"
#include <QDebug> #include <QDebug>
VDocument::VDocument(QObject *parent) : QObject(parent) VDocument::VDocument(const VFile *v_file, QObject *p_parent)
: QObject(p_parent), m_file(v_file)
{ {
} }
VDocument::VDocument(const QString &text, QObject *parent) void VDocument::updateText()
: QObject(parent)
{ {
m_text = text; emit textChanged(m_file->getContent());
}
void VDocument::setText(const QString &text)
{
if (text == m_text) {
return;
}
m_text = text;
emit textChanged(m_text);
}
QString VDocument::getText()
{
return m_text;
} }
void VDocument::setToc(const QString &toc) void VDocument::setToc(const QString &toc)

View File

@ -4,6 +4,8 @@
#include <QObject> #include <QObject>
#include <QString> #include <QString>
class VFile;
class VDocument : public QObject class VDocument : public QObject
{ {
Q_OBJECT Q_OBJECT
@ -12,10 +14,7 @@ class VDocument : public QObject
Q_PROPERTY(QString html MEMBER m_html NOTIFY htmlChanged) Q_PROPERTY(QString html MEMBER m_html NOTIFY htmlChanged)
public: public:
explicit VDocument(QObject *parent = 0); VDocument(const VFile *p_file, QObject *p_parent = 0);
VDocument(const QString &text, QObject *parent = 0);
void setText(const QString &text);
QString getText();
QString getToc(); QString getToc();
void scrollToAnchor(const QString &anchor); void scrollToAnchor(const QString &anchor);
void setHtml(const QString &html); void setHtml(const QString &html);
@ -26,6 +25,7 @@ public slots:
void setHeader(const QString &anchor); void setHeader(const QString &anchor);
void setLog(const QString &p_log); void setLog(const QString &p_log);
void keyPressEvent(int p_key); void keyPressEvent(int p_key);
void updateText();
signals: signals:
void textChanged(const QString &text); void textChanged(const QString &text);
@ -37,10 +37,16 @@ signals:
void keyPressed(int p_key); void keyPressed(int p_key);
private: private:
QString m_text;
QString m_toc; QString m_toc;
QString m_header; QString m_header;
// m_text does NOT contain actual content.
QString m_text;
// When using Hoedown, m_html will contain the html content.
QString m_html; QString m_html;
const VFile *m_file;
}; };
#endif // VDOCUMENT_H #endif // VDOCUMENT_H

View File

@ -21,7 +21,7 @@
extern VConfigManager vconfig; extern VConfigManager vconfig;
VEditTab::VEditTab(VFile *p_file, OpenFileMode p_mode, QWidget *p_parent) VEditTab::VEditTab(VFile *p_file, OpenFileMode p_mode, QWidget *p_parent)
: QStackedWidget(p_parent), m_file(p_file), isEditMode(false), : QStackedWidget(p_parent), m_file(p_file), isEditMode(false), document(p_file, this),
mdConverterType(vconfig.getMdConverterType()), m_fileModified(false), mdConverterType(vconfig.getMdConverterType()), m_fileModified(false),
m_editArea(NULL) m_editArea(NULL)
{ {
@ -109,7 +109,7 @@ void VEditTab::showFileReadMode()
break; break;
case DocType::Markdown: case DocType::Markdown:
if (mdConverterType == MarkdownConverterType::Marked) { if (mdConverterType == MarkdownConverterType::Marked) {
document.setText(m_file->getContent()); document.updateText();
updateTocFromHtml(document.getToc()); updateTocFromHtml(document.getToc());
} else { } else {
previewByConverter(); previewByConverter();
@ -140,7 +140,7 @@ void VEditTab::scrollPreviewToHeader(int p_outlineIndex)
void VEditTab::previewByConverter() void VEditTab::previewByConverter()
{ {
VMarkdownConverter mdConverter; VMarkdownConverter mdConverter;
QString &content = m_file->getContent(); const QString &content = m_file->getContent();
QString html = mdConverter.generateHtml(content, vconfig.getMarkdownExtensions()); QString html = mdConverter.generateHtml(content, vconfig.getMarkdownExtensions());
QRegularExpression tocExp("<p>\\[TOC\\]<\\/p>", QRegularExpression::CaseInsensitiveOption); QRegularExpression tocExp("<p>\\[TOC\\]<\\/p>", QRegularExpression::CaseInsensitiveOption);
QString toc = mdConverter.generateToc(content, vconfig.getMarkdownExtensions()); QString toc = mdConverter.generateToc(content, vconfig.getMarkdownExtensions());
@ -259,6 +259,9 @@ bool VEditTab::saveFile()
void VEditTab::setupMarkdownPreview() void VEditTab::setupMarkdownPreview()
{ {
const QString jsHolder("JS_PLACE_HOLDER");
const QString extraHolder("<!-- EXTRA_PLACE_HOLDER -->");
webPreviewer = new QWebEngineView(this); webPreviewer = new QWebEngineView(this);
VPreviewPage *page = new VPreviewPage(this); VPreviewPage *page = new VPreviewPage(this);
webPreviewer->setPage(page); webPreviewer->setPage(page);
@ -273,14 +276,26 @@ void VEditTab::setupMarkdownPreview()
this, &VEditTab::handleWebKeyPressed); this, &VEditTab::handleWebKeyPressed);
page->setWebChannel(channel); page->setWebChannel(channel);
if (mdConverterType == MarkdownConverterType::Marked) { QString jsFile, extraFile;
webPreviewer->setHtml(VNote::templateHtml, switch (mdConverterType) {
QUrl::fromLocalFile(m_file->retriveBasePath() + QDir::separator())); case MarkdownConverterType::Marked:
} else { jsFile = "qrc" + VNote::c_markedJsFile;
webPreviewer->setHtml(VNote::preTemplateHtml + VNote::postTemplateHtml, extraFile = "<script src=\"qrc" + VNote::c_markedExtraFile + "\"></script>";
QUrl::fromLocalFile(m_file->retriveBasePath() + QDir::separator())); break;
}
case MarkdownConverterType::Hoedown:
jsFile = "qrc" + VNote::c_hoedownJsFile;
break;
default:
Q_ASSERT(false);
}
QString htmlTemplate = VNote::s_markdownTemplate;
htmlTemplate.replace(jsHolder, jsFile);
if (!extraFile.isEmpty()) {
htmlTemplate.replace(extraHolder, extraFile);
}
webPreviewer->setHtml(htmlTemplate, QUrl::fromLocalFile(m_file->retriveBasePath() + QDir::separator()));
addWidget(webPreviewer); addWidget(webPreviewer);
} }

View File

@ -25,7 +25,7 @@ public:
inline VDirectory *getDirectory(); inline VDirectory *getDirectory();
inline const VDirectory *getDirectory() const; inline const VDirectory *getDirectory() const;
inline DocType getDocType() const; inline DocType getDocType() const;
inline QString &getContent(); inline const QString &getContent() const;
inline void setContent(const QString &p_content); inline void setContent(const QString &p_content);
inline VNotebook *getNotebook(); inline VNotebook *getNotebook();
inline QString getNotebookName() const; inline QString getNotebookName() const;
@ -78,7 +78,7 @@ inline DocType VFile::getDocType() const
return m_docType; return m_docType;
} }
inline QString &VFile::getContent() inline const QString &VFile::getContent() const
{ {
return m_content; return m_content;
} }

View File

@ -85,7 +85,7 @@ void VMdEdit::saveFile()
void VMdEdit::reloadFile() void VMdEdit::reloadFile()
{ {
QString &content = m_file->getContent(); const QString &content = m_file->getContent();
Q_ASSERT(content.indexOf(QChar::ObjectReplacementCharacter) == -1); Q_ASSERT(content.indexOf(QChar::ObjectReplacementCharacter) == -1);
setPlainText(content); setPlainText(content);
setModified(false); setModified(false);

View File

@ -10,9 +10,10 @@
extern VConfigManager vconfig; extern VConfigManager vconfig;
QString VNote::templateHtml; QString VNote::s_markdownTemplate;
QString VNote::preTemplateHtml; const QString VNote::c_hoedownJsFile = ":/resources/hoedown.js";
QString VNote::postTemplateHtml; const QString VNote::c_markedJsFile = ":/resources/marked.js";
const QString VNote::c_markedExtraFile = ":/utils/marked/marked.min.js";
VNote::VNote(QObject *parent) VNote::VNote(QObject *parent)
: QObject(parent), m_mainWindow(dynamic_cast<VMainWindow *>(parent)) : QObject(parent), m_mainWindow(dynamic_cast<VMainWindow *>(parent))
@ -83,14 +84,15 @@ QString VNote::getColorFromPalette(const QString &p_name) const
void VNote::initTemplate() void VNote::initTemplate()
{ {
if (templateHtml.isEmpty() || preTemplateHtml.isEmpty() if (s_markdownTemplate.isEmpty()) {
|| postTemplateHtml.isEmpty()) {
updateTemplate(); updateTemplate();
} }
} }
void VNote::updateTemplate() void VNote::updateTemplate()
{ {
const QString c_markdownTemplatePath(":/resources/markdown_template.html");
// Get background color // Get background color
QString rgb; QString rgb;
const QString &curRenderBg = vconfig.getCurRenderBackgroundColor(); const QString &curRenderBg = vconfig.getCurRenderBackgroundColor();
@ -109,19 +111,12 @@ void VNote::updateTemplate()
} }
QString styleHolder("<!-- BACKGROUND_PLACE_HOLDER -->"); QString styleHolder("<!-- BACKGROUND_PLACE_HOLDER -->");
QString cssHolder("CSS_PLACE_HOLDER"); QString cssHolder("CSS_PLACE_HOLDER");
templateHtml = VUtils::readFileFromDisk(vconfig.getTemplatePath());
templateHtml.replace(cssHolder, vconfig.getTemplateCssUrl());
if (!cssStyle.isEmpty()) {
templateHtml.replace(styleHolder, cssStyle);
}
preTemplateHtml = VUtils::readFileFromDisk(vconfig.getPreTemplatePath()); s_markdownTemplate = VUtils::readFileFromDisk(c_markdownTemplatePath);
preTemplateHtml.replace(cssHolder, vconfig.getTemplateCssUrl()); s_markdownTemplate.replace(cssHolder, vconfig.getTemplateCssUrl());
if (!cssStyle.isEmpty()) { if (!cssStyle.isEmpty()) {
preTemplateHtml.replace(styleHolder, cssStyle); s_markdownTemplate.replace(styleHolder, cssStyle);
} }
postTemplateHtml = VUtils::readFileFromDisk(vconfig.getPostTemplatePath());
} }
const QVector<VNotebook *> &VNote::getNotebooks() const const QVector<VNotebook *> &VNote::getNotebooks() const

View File

@ -25,12 +25,10 @@ public:
void initTemplate(); void initTemplate();
// Used by Marked static QString s_markdownTemplate;
static QString templateHtml; static const QString c_hoedownJsFile;
static const QString c_markedJsFile;
// Used by other markdown converter static const QString c_markedExtraFile;
static QString preTemplateHtml;
static QString postTemplateHtml;
inline const QVector<QPair<QString, QString> > &getPalette() const; inline const QVector<QPair<QString, QString> > &getPalette() const;
void initPalette(QPalette palette); void initPalette(QPalette palette);

View File

@ -2,11 +2,8 @@
<qresource prefix="/"> <qresource prefix="/">
<file>resources/welcome.html</file> <file>resources/welcome.html</file>
<file>resources/qwebchannel.js</file> <file>resources/qwebchannel.js</file>
<file>resources/template.html</file>
<file>resources/markdown.css</file> <file>resources/markdown.css</file>
<file>utils/marked/marked.min.js</file> <file>utils/marked/marked.min.js</file>
<file>resources/post_template.html</file>
<file>resources/pre_template.html</file>
<file>utils/highlightjs/highlight.pack.js</file> <file>utils/highlightjs/highlight.pack.js</file>
<file>utils/highlightjs/styles/androidstudio.css</file> <file>utils/highlightjs/styles/androidstudio.css</file>
<file>utils/highlightjs/styles/atom-one-dark.css</file> <file>utils/highlightjs/styles/atom-one-dark.css</file>
@ -85,5 +82,9 @@
<file>resources/icons/find_replace.svg</file> <file>resources/icons/find_replace.svg</file>
<file>resources/icons/search_wrap.svg</file> <file>resources/icons/search_wrap.svg</file>
<file>resources/icons/settings.svg</file> <file>resources/icons/settings.svg</file>
<file>resources/markdown_template.html</file>
<file>resources/markdown_template.js</file>
<file>resources/hoedown.js</file>
<file>resources/marked.js</file>
</qresource> </qresource>
</RCC> </RCC>