mume-with-litvis
Version:
Fork of mume with added http://litvis.org/
969 lines • 104 kB
JavaScript
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.MarkdownEngine = void 0;
// tslint:disable no-var-requires member-ordering
const block_attributes_1 = require("block-attributes");
const block_info_1 = require("block-info");
const cheerio = require("cheerio");
const child_process_1 = require("child_process");
const fs = require("fs");
const litvis_integration_mume_1 = require("litvis-integration-mume");
const path = require("path");
const request = require("request");
const slash = require("slash");
const toVFile = require("to-vfile");
const YAML = require("yamljs");
const admonition_1 = require("./custom-markdown-it-features/admonition");
const code_fences_1 = require("./custom-markdown-it-features/code-fences");
const critic_markup_1 = require("./custom-markdown-it-features/critic-markup");
const emoji_1 = require("./custom-markdown-it-features/emoji");
const html5_embed_1 = require("./custom-markdown-it-features/html5-embed");
const math_1 = require("./custom-markdown-it-features/math");
const wikilink_1 = require("./custom-markdown-it-features/wikilink");
const ebook_convert_1 = require("./ebook-convert");
const heading_id_generator_1 = require("./heading-id-generator");
const markdown_convert_1 = require("./markdown-convert");
const markdown_engine_config_1 = require("./markdown-engine-config");
const pandoc_convert_1 = require("./pandoc-convert");
const prince_convert_1 = require("./prince-convert");
const code_block_styling_1 = require("./render-enhancers/code-block-styling");
const embedded_local_images_1 = require("./render-enhancers/embedded-local-images");
const embedded_svgs_1 = require("./render-enhancers/embedded-svgs");
// import enhanceWithEmojiToSvg from "./render-enhancers/emoji-to-svg";
const extended_table_syntax_1 = require("./render-enhancers/extended-table-syntax");
const fenced_code_chunks_1 = require("./render-enhancers/fenced-code-chunks");
const fenced_diagrams_1 = require("./render-enhancers/fenced-diagrams");
const fenced_math_1 = require("./render-enhancers/fenced-math");
const resolved_image_paths_1 = require("./render-enhancers/resolved-image-paths");
const toc_1 = require("./toc");
const transformer_1 = require("./transformer");
const utility = require("./utility");
const utility_1 = require("./utility");
const extensionDirectoryPath = utility.extensionDirectoryPath;
const MarkdownIt = require(path.resolve(extensionDirectoryPath, "./dependencies/markdown-it/markdown-it.min.js"));
const CryptoJS = require(path.resolve(extensionDirectoryPath, "./dependencies/crypto-js/crypto-js.js"));
const defaults = {
html: true,
xhtmlOut: false,
breaks: true,
langPrefix: "language-",
linkify: true,
linkTarget: "",
typographer: true, // Enable smartypants and other sweet transforms
};
let MODIFY_SOURCE = null;
const dependentLibraryConfigs = [
{
libraryName: "vega",
libraryVersion: "5",
buildPathForWebview: "build/vega.min.js",
},
{
libraryName: "vega-lite",
libraryVersion: "5",
buildPathForWebview: "build/vega-lite.min.js",
},
{
libraryName: "vega-embed",
libraryVersion: "6",
buildPathForWebview: "build/vega-embed.min.js",
},
{
libraryName: "apache-arrow",
libraryVersion: "4",
buildPathForWebview: "Arrow.es2015.min.js",
},
{
libraryName: "vega-loader-arrow",
libraryVersion: "0.0",
buildPathForWebview: "build/vega-loader-arrow.min.js",
},
];
let UPDATE_LINTING_REPORT = null;
/**
* The markdown engine that can be used to parse markdown and export files
*/
class MarkdownEngine {
/**
* Modify markdown source, append `result` after corresponding code chunk.
* @param codeChunkData
* @param result
*/
static modifySource(codeChunkData, result, filePath) {
return __awaiter(this, void 0, void 0, function* () {
if (MODIFY_SOURCE) {
yield MODIFY_SOURCE(codeChunkData, result, filePath);
}
else {
// TODO: directly modify the local file.
}
codeChunkData.running = false;
return result;
});
}
/**
* Bind cb to MODIFY_SOURCE
* @param cb
*/
static onModifySource(cb) {
MODIFY_SOURCE = cb;
}
static updateLintingReport(vFiles) {
return __awaiter(this, void 0, void 0, function* () {
if (UPDATE_LINTING_REPORT) {
yield UPDATE_LINTING_REPORT(vFiles);
}
});
}
static onUpdateLintingReport(cb) {
UPDATE_LINTING_REPORT = cb;
}
constructor(args) {
// caches
this.graphsCache = {};
// code chunks
this.codeChunksData = {};
// files cache
this.filesCache = {};
/**
* cachedHTML is the cache of html generated from the markdown file.
*/
// private cachedHTML:string = '';
/**
* Check whether the preview is in presentation mode.
*/
this.isPreviewInPresentationMode = false;
this.filePath = args.filePath;
this.fileDirectoryPath = path.dirname(this.filePath);
this.projectDirectoryPath =
args.projectDirectoryPath || this.fileDirectoryPath;
this.originalConfig = args.config;
this.resetConfig();
this.headings = [];
this.tocHTML = "";
this.md = new MarkdownIt(Object.assign(Object.assign({}, defaults), { typographer: this.enableTypographer, breaks: this.breakOnSingleNewLine, linkify: this.enableLinkify }));
// markdown-it extensions
const extensions = [
"./dependencies/markdown-it/extensions/markdown-it-footnote.min.js",
"./dependencies/markdown-it/extensions/markdown-it-sub.min.js",
"./dependencies/markdown-it/extensions/markdown-it-sup.min.js",
"./dependencies/markdown-it/extensions/markdown-it-deflist.min.js",
"./dependencies/markdown-it/extensions/markdown-it-abbr.min.js",
"./dependencies/markdown-it/extensions/markdown-it-mark.min.js",
];
for (const js of extensions) {
const fullPath = path.resolve(extensionDirectoryPath, js);
const plugin = require(fullPath);
this.md.use(plugin);
}
(0, code_fences_1.default)(this.md, this.config);
(0, critic_markup_1.default)(this.md, this.config);
(0, emoji_1.default)(this.md, this.config);
(0, litvis_integration_mume_1.useMarkdownItLitvisFeatures)(this.md, this.config);
(0, html5_embed_1.default)(this.md, this.config);
(0, math_1.default)(this.md, this.config);
(0, wikilink_1.default)(this.md, this.config);
(0, admonition_1.default)(this.md);
this.clearCaches();
}
/**
* Reset config
*/
resetConfig() {
// Please notice that ~/.config/mume/config.json has the highest priority.
this.config = Object.assign(Object.assign(Object.assign({}, markdown_engine_config_1.defaultMarkdownEngineConfig), (this.originalConfig || {})), (utility.configs.config || {}));
this.initConfig();
}
/**
* Set default values
*/
initConfig() {
this.interpolateConfig(this.config, this.projectDirectoryPath);
// break on single newline
this.breakOnSingleNewLine = this.config.breakOnSingleNewLine;
// enable typographer
this.enableTypographer = this.config.enableTypographer;
// enable linkify
this.enableLinkify = this.config.enableLinkify;
// protocal whitelist
const protocolsWhiteList = (this.config.protocolsWhiteList ||
markdown_engine_config_1.defaultMarkdownEngineConfig.protocolsWhiteList)
.split(",")
.map((x) => x.trim());
this.protocolsWhiteListRegExp = new RegExp("^(" + protocolsWhiteList.join("|") + ")"); // eg /^(http:\/\/|https:\/\/|atom:\/\/|file:\/\/|mailto:|tel:)/
}
interpolateConfig(config, projectDirectoryPath) {
var _a, _b, _c, _d;
const pattern = /\${\s*(\w+?)\s*}/g; // Replace ${property}
const replacements = {
projectDir: projectDirectoryPath,
workspaceFolder: projectDirectoryPath, // vscode abreviation
};
// Replace certains paths
config.configPath = (_a = config.configPath) === null || _a === void 0 ? void 0 : _a.replace(pattern, (match, token) => replacements[token] || match);
config.imageFolderPath = (_b = config.imageFolderPath) === null || _b === void 0 ? void 0 : _b.replace(pattern, (match, token) => replacements[token] || match);
config.imageMagickPath = (_c = config.imageMagickPath) === null || _c === void 0 ? void 0 : _c.replace(pattern, (match, token) => replacements[token] || match);
config.chromePath = (_d = config.chromePath) === null || _d === void 0 ? void 0 : _d.replace(pattern, (match, token) => replacements[token] || match);
}
updateConfiguration(config) {
this.config = Object.assign(Object.assign({}, this.config), config);
this.initConfig();
this.md.set({
breaks: this.breakOnSingleNewLine,
typographer: this.enableTypographer,
linkify: this.enableLinkify,
});
}
/*
public cacheSVG(code:string, svg:string) {
svg = CryptoJS.AES.decrypt(svg, 'markdown-preview-enhanced').toString(CryptoJS.enc.Utf8)
// const base64 = new Buffer(svg).toString('base64')
// const img = `<img src="data:image/svg+xml;charset=utf-8;base64,${base64}">`
this.graphsCache[md5(code)] = svg
}
*/
cacheCodeChunkResult(id, result) {
const codeChunkData = this.codeChunksData[id];
if (!codeChunkData) {
return;
}
codeChunkData.result = CryptoJS.AES.decrypt(result, "mume").toString(CryptoJS.enc.Utf8);
}
/**
* Generate scripts string for preview usage.
*/
generateScriptsForPreview(isForPresentation = false, yamlConfig = {}, vscodePreviewPanel = null) {
let scripts = "";
// prevent `id="exports"` element from linked to `window` object.
scripts += `<script>var exports = undefined</script>`;
// jquery
scripts += `<script type="text/javascript" src="${utility.addFileProtocol(path.resolve(utility.extensionDirectoryPath, "./dependencies/jquery/jquery.js"), vscodePreviewPanel)}" charset="UTF-8"></script>`;
// jquery contextmenu
scripts += `<script type="text/javascript" src="${utility.addFileProtocol(path.resolve(utility.extensionDirectoryPath, "./dependencies/jquery-contextmenu/jquery.ui.position.min.js"), vscodePreviewPanel)}" charset="UTF-8"></script>`;
scripts += `<script type="text/javascript" src="${utility.addFileProtocol(path.resolve(utility.extensionDirectoryPath, "./dependencies/jquery-contextmenu/jquery.contextMenu.min.js"), vscodePreviewPanel)}" charset="UTF-8"></script>`;
// jquery modal
scripts += `<script type="text/javascript" src="${utility.addFileProtocol(path.resolve(utility.extensionDirectoryPath, "./dependencies/jquery-modal/jquery.modal.min.js"), vscodePreviewPanel)}" charset="UTF-8"></script>`;
// crpto-js
scripts += `<script type="text/javascript" src="${utility.addFileProtocol(path.resolve(utility.extensionDirectoryPath, "./dependencies/crypto-js/crypto-js.js"), vscodePreviewPanel)}" charset="UTF-8"></script>`;
// mermaid
scripts += `<script type="text/javascript" src="${utility.addFileProtocol(path.resolve(utility.extensionDirectoryPath, `./dependencies/mermaid/mermaid.min.js`), vscodePreviewPanel)}" charset="UTF-8"></script>`;
// zenuml
scripts += `<script type="text/javascript" src="${utility.addFileProtocol(path.resolve(utility.extensionDirectoryPath, "./dependencies/sequence-diagram/vue.js"), vscodePreviewPanel)}" charset="UTF-8"></script>`;
scripts += `<script type="text/javascript" src="${utility.addFileProtocol(path.resolve(utility.extensionDirectoryPath, "./dependencies/sequence-diagram/sequence-diagram.min.js"), vscodePreviewPanel)}" charset="UTF-8"></script>`;
// wavedrome
scripts += `<script type="text/javascript" src="${utility.addFileProtocol(path.resolve(utility.extensionDirectoryPath, "./dependencies/wavedrom/default.js"), vscodePreviewPanel)}" charset="UTF-8"></script>`;
scripts += `<script type="text/javascript" src="${utility.addFileProtocol(path.resolve(utility.extensionDirectoryPath, "./dependencies/wavedrom/wavedrom.min.js"), vscodePreviewPanel)}" charset="UTF-8"></script>`;
// math
if (this.config.mathRenderingOption === "MathJax" ||
this.config.usePandocParser) {
const mathJaxConfig = utility.configs.mathjaxConfig;
mathJaxConfig["tex2jax"] = mathJaxConfig["tex2jax"] || {};
mathJaxConfig["tex2jax"]["inlineMath"] = this.config.mathInlineDelimiters;
mathJaxConfig["tex2jax"]["displayMath"] = this.config.mathBlockDelimiters;
mathJaxConfig["HTML-CSS"]["imageFont"] = null; // Disable image font, otherwise the preview will only display black color image.
mathJaxConfig["root"] = utility.addFileProtocol(slash(path.resolve(utility.extensionDirectoryPath, "./dependencies/mathjax")), vscodePreviewPanel);
scripts += `<script type="text/javascript" async src="${utility.addFileProtocol(path.resolve(utility.extensionDirectoryPath, "./dependencies/mathjax/MathJax.js"), vscodePreviewPanel)}" charset="UTF-8"></script>`;
scripts += `<script type="text/x-mathjax-config"> MathJax.Hub.Config(${JSON.stringify(mathJaxConfig)}); </script>`;
}
// reveal.js
if (isForPresentation) {
scripts += `<script src='${utility.addFileProtocol(path.resolve(utility.extensionDirectoryPath, "./dependencies/reveal/lib/js/head.min.js"), vscodePreviewPanel)}'></script>`;
scripts += `<script src='${utility.addFileProtocol(path.resolve(utility.extensionDirectoryPath, "./dependencies/reveal/js/reveal.js"), vscodePreviewPanel)}'></script>`;
let presentationConfig = yamlConfig["presentation"] || {};
if (typeof presentationConfig !== "object") {
presentationConfig = {};
}
let dependencies = presentationConfig["dependencies"] || [];
if (!(dependencies instanceof Array)) {
dependencies = [];
}
presentationConfig["dependencies"] = dependencies;
scripts += `
<script>
Reveal.initialize(${JSON.stringify(Object.assign({ margin: 0.1 }, presentationConfig))})
</script>
`;
}
// mermaid init
scripts += `<script>
${utility.configs.mermaidConfig}
if (window['MERMAID_CONFIG']) {
window['MERMAID_CONFIG'].startOnLoad = false
window['MERMAID_CONFIG'].cloneCssStyles = false
window['MERMAID_CONFIG'].theme = "${this.config.mermaidTheme}"
}
mermaid.initialize(window['MERMAID_CONFIG'] || {})
if (typeof(window['Reveal']) !== 'undefined') {
function mermaidRevealHelper(event) {
var currentSlide = event.currentSlide
var diagrams = currentSlide.querySelectorAll('.mermaid')
for (var i = 0; i < diagrams.length; i++) {
var diagram = diagrams[i]
if (!diagram.hasAttribute('data-processed')) {
mermaid.init(null, diagram, ()=> {
Reveal.slide(event.indexh, event.indexv)
})
}
}
}
Reveal.addEventListener('slidechanged', mermaidRevealHelper)
Reveal.addEventListener('ready', mermaidRevealHelper)
} else {
// The line below will cause mermaid bug in preview.
// mermaid.init(null, document.querySelectorAll('.mermaid'))
}
</script>`;
// wavedrom init script
if (isForPresentation) {
scripts += `<script>
WaveDrom.ProcessAll()
</script>`;
}
// flowchart.js
scripts += `<script src='${utility.addFileProtocol(path.resolve(utility.extensionDirectoryPath, "./dependencies/raphael/raphael.js"), vscodePreviewPanel)}'></script>`;
scripts += `<script src='${utility.addFileProtocol(path.resolve(utility.extensionDirectoryPath, "./dependencies/flowchart/flowchart.min.js"), vscodePreviewPanel)}'></script>`;
// flowchart init script
if (isForPresentation) {
scripts += `<script>
var flowcharts = document.getElementsByClassName('flow')
for (var i = 0; i < flowcharts.length; i++) {
var flow = flowcharts[i]
try {
var diagram = flowchart.parse(flow.textContent)
flow.id = 'flow_' + i
flow.innerHTML = ''
diagram.drawSVG(flow.id)
} catch (error) {
flow.innerHTML = '<pre class="language-text">' + error.toString() + '</pre>'
}
}
</script>`;
}
dependentLibraryConfigs.forEach(({ libraryName, buildPathForWebview }) => {
scripts += `<script src="${utility.addFileProtocol(utility.resolveBuildPathForWebview(libraryName, buildPathForWebview), vscodePreviewPanel)}" charset="UTF-8"></script>`;
});
if (isForPresentation) {
scripts += `<script>
var vegaEls = document.querySelectorAll('.vega, .vega-lite');
function reportVegaError(el, error) {
el.innerHTML = '<pre class="language-text">' + error.toString() + '</pre>'
}
for (var i = 0; i < vegaEls.length; i++) {
const vegaEl = vegaEls[i]
try {
var spec = JSON.parse(vegaEl.textContent);
vegaEmbed(vegaEl, spec, { actions: false, renderer: 'svg' })
.catch(function(error) {
reportVegaError(vegaEl, error);
})
} catch (error) {
reportVegaError(vegaEl, error);
}
}
</script>`;
}
// sequence diagram
scripts += `<script src='${utility.addFileProtocol(path.resolve(utility.extensionDirectoryPath, "./dependencies/webfont/webfontloader.js"), vscodePreviewPanel)}'></script>`;
scripts += `<script src='${utility.addFileProtocol(path.resolve(utility.extensionDirectoryPath, "./dependencies/underscore/underscore.js"), vscodePreviewPanel)}'></script>`;
scripts += `<script src='${utility.addFileProtocol(path.resolve(utility.extensionDirectoryPath, "./dependencies/js-sequence-diagrams/sequence-diagram-min.js"), vscodePreviewPanel)}'></script>`;
// sequence diagram init script
if (isForPresentation) {
scripts += `<script>
var sequenceDiagrams = document.getElementsByClassName('sequence')
for (var i = 0; i < sequenceDiagrams.length; i++) {
var sequence = sequenceDiagrams[i]
try {
var diagram = Diagram.parse(sequence.textContent)
var theme = sequence.getAttribute('theme') || 'simple'
sequence.id = 'sequence_' + i
sequence.innerHTML = ''
diagram.drawSVG(sequence.id, {theme: theme})
} catch (error) {
sequence.innerHTML = '<pre class="language-text">' + error.toString() + '</pre>'
}
}
</script>`;
}
return scripts;
}
/**
* Automatically pick code block theme for preview.
*/
getPrismTheme(isPresentationMode = false, yamlConfig = {}) {
if (this.config.codeBlockTheme === "auto.css") {
/**
* Automatically pick code block theme for preview.
*/
if (isPresentationMode) {
const presentationTheme = yamlConfig["presentation"] &&
typeof yamlConfig["presentation"] === "object" &&
yamlConfig["presentation"]["theme"]
? yamlConfig["presentation"]["theme"]
: this.config.revealjsTheme;
return (MarkdownEngine.AutoPrismThemeMapForPresentation[presentationTheme] ||
"default.css");
}
else {
return (MarkdownEngine.AutoPrismThemeMap[this.config.previewTheme] ||
"default.css");
}
}
else {
return this.config.codeBlockTheme;
}
}
/**
* Generate styles string for preview usage.
*/
generateStylesForPreview(isPresentationMode = false, yamlConfig = {}, vscodePreviewPanel = null) {
let styles = "";
// loading.css
styles += `<link rel="stylesheet" href="${utility.addFileProtocol(path.resolve(utility.extensionDirectoryPath, "./styles/loading.css"), vscodePreviewPanel)}">`;
// jquery-contextmenu
styles += `<link rel="stylesheet" href="${utility.addFileProtocol(path.resolve(utility.extensionDirectoryPath, `./dependencies/jquery-contextmenu/jquery.contextMenu.min.css`), vscodePreviewPanel)}">`;
// jquery-modal
styles += `<link rel="stylesheet" href="${utility.addFileProtocol(path.resolve(utility.extensionDirectoryPath, `./dependencies/jquery-modal/jquery.modal.min.css`), vscodePreviewPanel)}">`;
// check math
if (this.config.mathRenderingOption === "KaTeX" &&
!this.config.usePandocParser) {
styles += `<link rel="stylesheet" href="${utility.addFileProtocol(path.resolve(utility.extensionDirectoryPath, "./dependencies/katex/katex.min.css"), vscodePreviewPanel)}">`;
}
// check sequence diagram
styles += `<link rel="stylesheet" href="${utility.addFileProtocol(path.resolve(utility.extensionDirectoryPath, `./dependencies/js-sequence-diagrams/sequence-diagram-min.css`), vscodePreviewPanel)}">`;
// check font-awesome
styles += `<link rel="stylesheet" href="${utility.addFileProtocol(path.resolve(utility.extensionDirectoryPath, `./dependencies/font-awesome/css/font-awesome.min.css`), vscodePreviewPanel)}">`;
// check preview theme and revealjs theme
if (!isPresentationMode) {
styles += `<link rel="stylesheet" href="${utility.addFileProtocol(path.resolve(utility.extensionDirectoryPath, `./styles/preview_theme/${this.config.previewTheme}`), vscodePreviewPanel)}">`;
}
else {
styles += `<link rel="stylesheet" href="${utility.addFileProtocol(path.resolve(extensionDirectoryPath, "./dependencies/reveal/css/reveal.css"), vscodePreviewPanel)}" >`;
styles += `<link rel="stylesheet" href="${utility.addFileProtocol(path.resolve(extensionDirectoryPath, `./dependencies/reveal/css/theme/${yamlConfig["presentation"] &&
typeof yamlConfig["presentation"] === "object" &&
yamlConfig["presentation"]["theme"]
? yamlConfig["presentation"]["theme"]
: this.config.revealjsTheme}`), vscodePreviewPanel)}" >`;
}
// check prism
styles += `<link rel="stylesheet" href="${utility.addFileProtocol(path.resolve(utility.extensionDirectoryPath, `./styles/prism_theme/${this.getPrismTheme(isPresentationMode, yamlConfig)}`), vscodePreviewPanel)}">`;
// style template
styles += `<link rel="stylesheet" media="screen" href="${utility.addFileProtocol(path.resolve(utility.extensionDirectoryPath, "./styles/style-template.css"), vscodePreviewPanel)}">`;
// style markdown-it-admonition
styles += `<link rel="stylesheet" media="screen" href="${utility.addFileProtocol(path.resolve(utility.extensionDirectoryPath, "./styles/markdown-it-admonition.css"), vscodePreviewPanel)}">`;
// global styles
styles += `<style>${utility.configs.globalStyle}</style>`;
return styles;
}
/**
* Generate <style> and <link> string from an array of file paths.
* @param JSAndCssFiles
*/
generateJSAndCssFilesForPreview(JSAndCssFiles = [], vscodePreviewPanel = null) {
let output = "";
JSAndCssFiles.forEach((sourcePath) => {
let absoluteFilePath = sourcePath;
if (sourcePath[0] === "/") {
absoluteFilePath = utility.addFileProtocol(path.resolve(this.projectDirectoryPath, "." + sourcePath), vscodePreviewPanel);
}
else if (sourcePath.match(/^file:\/\//) ||
sourcePath.match(/^https?\:\/\//)) {
// do nothing
}
else {
absoluteFilePath = utility.addFileProtocol(path.resolve(this.fileDirectoryPath, sourcePath), vscodePreviewPanel);
}
if (absoluteFilePath.endsWith(".js")) {
output += `<script type="text/javascript" src="${absoluteFilePath}"></script>`;
}
else {
// css
output += `<link rel="stylesheet" href="${absoluteFilePath}">`;
}
});
return output;
}
/**
* Generate html template for preview.
*/
generateHTMLTemplateForPreview({ inputString = "", body = "", webviewScript = "", scripts = "", styles = "", head = `<base href="${this.filePath}">`, config = {}, vscodePreviewPanel = null, contentSecurityPolicy = "", }) {
return __awaiter(this, void 0, void 0, function* () {
if (!inputString) {
inputString = fs.readFileSync(this.filePath, { encoding: "utf-8" });
}
if (!webviewScript) {
webviewScript = utility.addFileProtocol(path.resolve(utility.extensionDirectoryPath, "./out/src/webview.js"), vscodePreviewPanel);
}
if (!body) {
// default body
body = `
<div class="refreshing-icon"></div>
<div id="md-toolbar">
<div class="back-to-top-btn btn"><span>⬆︎</span></div>
<div class="refresh-btn btn"><span>⟳︎</span></div>
<div class="sidebar-toc-btn btn"><span>§</span></div>
</div>
<div id="image-helper-view">
<h4>Image Helper</h4>
<div class="upload-div">
<label>Link</label>
<input type="text" class="url-editor" placeholder="enter image URL here, then press \'Enter\' to insert.">
<div class="splitter"></div>
<label class="copy-label">Copy image to root /assets folder</label>
<div class="drop-area paster">
<p class="paster"> Click me to browse image file </p>
<input class="file-uploader paster" type="file" style="display:none;" multiple="multiple" >
</div>
<div class="splitter"></div>
<label>Upload</label>
<div class="drop-area uploader">
<p class="uploader">Click me to browse image file</p>
<input class="file-uploader uploader" type="file" style="display:none;" multiple="multiple" >
</div>
<div class="uploader-choice">
<span>use</span>
<select class="uploader-select">
<option>imgur</option>
<option>sm.ms</option>
<option>qiniu</option>
</select>
<span> to upload images</span>
</div>
<a href="#" id="show-uploaded-image-history">Show history</a>
</div>
</div>
<!-- <div class="markdown-spinner"> Loading Markdown\u2026 </div> -->
`;
}
const { yamlConfig, JSAndCssFiles, html } = yield this.parseMD(inputString, {
isForPreview: true,
useRelativeFilePath: false,
hideFrontMatter: false,
vscodePreviewPanel,
});
const isPresentationMode = yamlConfig["isPresentationMode"];
const htmlTemplate = `<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-type" content="text/html;charset=UTF-8">
<meta id="mume-data" data-config="${utility.escapeString(JSON.stringify(Object.assign(Object.assign({}, this.config), config)))}" data-time="${Date.now()}">
<meta charset="UTF-8">
${contentSecurityPolicy
? `<meta
http-equiv="Content-Security-Policy"
content="${contentSecurityPolicy}"
/>`
: ""}
${this.generateStylesForPreview(isPresentationMode, yamlConfig, vscodePreviewPanel)}
${styles}
<link rel="stylesheet" href="${utility.addFileProtocol(path.resolve(utility.extensionDirectoryPath, "./styles/preview.css"), vscodePreviewPanel)}">
${this.generateJSAndCssFilesForPreview(JSAndCssFiles, vscodePreviewPanel)}
${head}
</head>
<body class="preview-container">
<div class="mume markdown-preview" for="preview" ${isPresentationMode ? "data-presentation-mode" : ""}>
${html}
</div>
${body}
</body>
${this.generateScriptsForPreview(isPresentationMode, yamlConfig, vscodePreviewPanel)}
${scripts}
<script src="${webviewScript}"></script>
</html>`;
return htmlTemplate;
});
}
/**
* Generate HTML content
* @param html: this is the final content you want to put.
* @param yamlConfig: this is the front matter.
* @param option: HTMLTemplateOption
*/
generateHTMLTemplateForExport(html, yamlConfig = {}, options) {
return __awaiter(this, void 0, void 0, function* () {
// get `id` and `class`
const elementId = yamlConfig["id"] || "";
let elementClass = yamlConfig["class"] || [];
if (typeof elementClass === "string") {
elementClass = [elementClass];
}
elementClass = elementClass.join(" ");
// math style and script
let mathStyle = "";
if (this.config.mathRenderingOption === "MathJax" ||
this.config.usePandocParser) {
// TODO
const mathJaxConfig = yield utility.getMathJaxConfig(this.config.configPath);
mathJaxConfig["tex2jax"]["inlineMath"] = this.config.mathInlineDelimiters;
mathJaxConfig["tex2jax"]["displayMath"] = this.config.mathBlockDelimiters;
if (options.offline) {
mathStyle = `
<script type="text/x-mathjax-config">
MathJax.Hub.Config(${JSON.stringify(mathJaxConfig)});
</script>
<script type="text/javascript" async src="file:///${path.resolve(extensionDirectoryPath, "./dependencies/mathjax/MathJax.js")}" charset="UTF-8"></script>
`;
}
else {
mathStyle = `
<script type="text/x-mathjax-config">
MathJax.Hub.Config(${JSON.stringify(mathJaxConfig)});
</script>
<script type="text/javascript" async src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/MathJax.js"></script>
`;
}
}
else if (this.config.mathRenderingOption === "KaTeX") {
if (options.offline) {
mathStyle = `<link rel="stylesheet" href="file:///${path.resolve(extensionDirectoryPath, "./dependencies/katex/katex.min.css")}">`;
}
else {
mathStyle = `<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.4/dist/katex.min.css">`;
}
}
else {
mathStyle = "";
}
// font-awesome
let fontAwesomeStyle = "";
if (html.indexOf('<i class="fa ') >= 0) {
if (options.offline) {
fontAwesomeStyle = `<link rel="stylesheet" href="file:///${path.resolve(extensionDirectoryPath, `./dependencies/font-awesome/css/font-awesome.min.css`)}">`;
}
else {
fontAwesomeStyle = `<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">`;
}
}
// mermaid
let mermaidScript = "";
let mermaidInitScript = "";
if (html.indexOf(' class="mermaid') >= 0) {
if (options.offline) {
mermaidScript = `<script type="text/javascript" src="file:///${path.resolve(extensionDirectoryPath, "./dependencies/mermaid/mermaid.min.js")}" charset="UTF-8"></script>`;
}
else {
mermaidScript = `<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/mermaid@9.4.0/dist/mermaid.min.js"></script>`;
}
const mermaidConfig = yield utility.getMermaidConfig(this.config.configPath);
mermaidInitScript += `<script>
${mermaidConfig}
if (window['MERMAID_CONFIG']) {
window['MERMAID_CONFIG'].startOnLoad = false
window['MERMAID_CONFIG'].cloneCssStyles = false
window['MERMAID_CONFIG'].theme = "${this.config.mermaidTheme}"
}
mermaid.initialize(window['MERMAID_CONFIG'] || {})
if (typeof(window['Reveal']) !== 'undefined') {
function mermaidRevealHelper(event) {
var currentSlide = event.currentSlide
var diagrams = currentSlide.querySelectorAll('.mermaid')
for (var i = 0; i < diagrams.length; i++) {
var diagram = diagrams[i]
if (!diagram.hasAttribute('data-processed')) {
mermaid.init(null, diagram, ()=> {
Reveal.slide(event.indexh, event.indexv)
})
}
}
}
Reveal.addEventListener('slidechanged', mermaidRevealHelper)
Reveal.addEventListener('ready', mermaidRevealHelper)
} else {
mermaid.init(null, document.querySelectorAll('.mermaid'))
}
</script>`;
}
// zenuml
let zenumlScript = ``;
if (html.indexOf(' class="zenuml') >= 0) {
if (options.offline) {
zenumlScript += `<script type="text/javascript" src="file:///${path.resolve(utility.extensionDirectoryPath, "./dependencies/sequence-diagram/vue.js")}" charset="UTF-8"></script>`;
zenumlScript += `<script type="text/javascript" src="file:///${path.resolve(utility.extensionDirectoryPath, "./dependencies/sequence-diagram/sequence-diagram.min.js")}" charset="UTF-8"></script>`;
}
else {
zenumlScript += `<script type="text/javascript" src="https://unpkg.com/vue"></script>`;
zenumlScript += `<script type="text/javascript" src="https://unpkg.com/sequence-diagram"></script>`;
}
}
// wavedrom
let wavedromScript = ``;
let wavedromInitScript = ``;
if (html.indexOf(' class="wavedrom') >= 0) {
if (options.offline) {
wavedromScript += `<script type="text/javascript" src="file:///${path.resolve(utility.extensionDirectoryPath, "./dependencies/wavedrom/default.js")}" charset="UTF-8"></script>`;
wavedromScript += `<script type="text/javascript" src="file:///${path.resolve(utility.extensionDirectoryPath, "./dependencies/wavedrom/wavedrom.min.js")}" charset="UTF-8"></script>`;
}
else {
wavedromScript += `<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/wavedrom/2.9.1/skins/default.js"></script>`;
wavedromScript += `<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/wavedrom/2.9.1/wavedrom.min.js"></script>`;
}
wavedromInitScript = `<script>WaveDrom.ProcessAll()</script>`;
}
// vega and vega-lite with vega-embed
// https://vega.github.io/vega/usage/#embed
let vegaScript = ``;
let vegaInitScript = ``;
if (html.indexOf(' class="vega') >= 0 ||
html.indexOf(' class="vega-lite') >= 0) {
dependentLibraryConfigs.forEach(({ libraryName, libraryVersion, buildPathForWebview }) => {
vegaScript += options.offline
? `<script type="text/javascript" src="file:///${utility.resolveBuildPathForWebview(libraryName, buildPathForWebview)}" charset="UTF-8"></script>`
: `<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/${libraryName}@${libraryVersion}/${buildPathForWebview}"></script>`;
});
vegaInitScript += `<script>
var vegaEls = document.querySelectorAll('.vega, .vega-lite');
function reportVegaError(el, error) {
el.innerHTML = '<pre class="language-text">' + error.toString() + '</pre>'
}
for (var i = 0; i < vegaEls.length; i++) {
const vegaEl = vegaEls[i]
try {
var spec = JSON.parse(vegaEl.textContent);
vegaEmbed(vegaEl, spec, { actions: false, renderer: 'svg' })
.catch(function(error) {
reportVegaError(vegaEl, error);
})
} catch (error) {
reportVegaError(vegaEl, error);
}
}
</script>`;
}
// flowchart
let flowchartScript = ``;
let flowchartInitScript = ``;
if (html.indexOf(' class="flow') >= 0) {
if (options.offline) {
flowchartScript += `<script type="text/javascript" src="file:///${path.resolve(utility.extensionDirectoryPath, "./dependencies/raphael/raphael.js")}" charset="UTF-8"></script>`;
flowchartScript += `<script type="text/javascript" src="file:///${path.resolve(utility.extensionDirectoryPath, "./dependencies/flowchart/flowchart.min.js")}" charset="UTF-8"></script>`;
}
else {
flowchartScript += `<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/raphael/2.2.7/raphael.min.js"></script>`;
flowchartScript += `<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/flowchart/1.7.0/flowchart.min.js"></script>`;
}
flowchartInitScript = `<script>
var flowcharts = document.getElementsByClassName('flow')
for (var i = 0; i < flowcharts.length; i++) {
var flow = flowcharts[i]
try {
var diagram = flowchart.parse(flow.textContent)
flow.id = 'flow_' + i
flow.innerHTML = ''
diagram.drawSVG(flow.id)
} catch (error) {
flow.innerHTML = '<pre class="language-text">' + error.toString() + '</pre>'
}
}
</script>`;
}
// sequence diagrams
let sequenceDiagramScript = ``;
let sequenceDiagramStyle = ``;
let sequenceDiagramInitScript = ``;
if (html.indexOf(' class="sequence') >= 0) {
if (options.offline) {
sequenceDiagramScript += `<script type="text/javascript" src="file:///${path.resolve(utility.extensionDirectoryPath, "./dependencies/webfont/webfontloader.js")}" charset="UTF-8"></script>`;
sequenceDiagramScript += `<script type="text/javascript" src="file:///${path.resolve(utility.extensionDirectoryPath, "./dependencies/raphael/raphael.js")}" charset="UTF-8"></script>`;
sequenceDiagramScript += `<script type="text/javascript" src="file:///${path.resolve(utility.extensionDirectoryPath, "./dependencies/underscore/underscore.js")}" charset="UTF-8"></script>`;
sequenceDiagramScript += `<script type="text/javascript" src="file:///${path.resolve(utility.extensionDirectoryPath, "./dependencies/js-sequence-diagrams/sequence-diagram-min.js")}" charset="UTF-8"></script>`;
sequenceDiagramStyle = `<link rel="stylesheet" href="file:///${path.resolve(extensionDirectoryPath, `./dependencies/js-sequence-diagrams/sequence-diagram-min.css`)}">`;
}
else {
sequenceDiagramScript += `<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/webfont/1.6.28/webfontloader.js"></script>`;
sequenceDiagramScript += `<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/raphael/2.2.7/raphael.min.js"></script>`;
sequenceDiagramScript += `<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>`;
sequenceDiagramScript += `<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/js-sequence-diagram@2.0.1/dist/sequence-diagram-min.js"></script>`;
sequenceDiagramStyle = `<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/js-sequence-diagram@2.0.1/dist/sequence-diagram-min.css">`;
}
sequenceDiagramInitScript = `<script>
var sequenceDiagrams = document.getElementsByClassName('sequence')
for (var i = 0; i < sequenceDiagrams.length; i++) {
var sequence = sequenceDiagrams[i]
try {
var diagram = Diagram.parse(sequence.textContent)
var theme = sequence.getAttribute('theme') || 'simple'
sequence.id = 'sequence_' + i
sequence.innerHTML = ''
diagram.drawSVG(sequence.id, {theme: theme})
} catch (error) {
sequence.innerHTML = '<pre class="language-text">' + error.toString() + '</pre>'
}
}
</script>`;
}
// presentation
let presentationScript = "";
let presentationStyle = "";
let presentationInitScript = "";
if (yamlConfig["isPresentationMode"]) {
if (options.offline) {
presentationScript = `
<script src='file:///${path.resolve(extensionDirectoryPath, "./dependencies/reveal/lib/js/head.min.js")}'></script>
<script src='file:///${path.resolve(extensionDirectoryPath, "./dependencies/reveal/js/reveal.js")}'></script>`;
}
else {
presentationScript = `
<script src='https://cdn.jsdelivr.net/npm/reveal.js@4.1.0/dist/reveal.js'></script>`;
}
const presentationConfig = yamlConfig["presentation"] || {};
const dependencies = presentationConfig["dependencies"] || [];
if (presentationConfig["enableSpeakerNotes"]) {
if (options.offline) {
dependencies.push({
src: path.resolve(extensionDirectoryPath, "./dependencies/reveal/plugin/notes/notes.js"),
async: true,
});
}
else {
dependencies.push({ src: "revealjs_deps/notes.js", async: true }); // TODO: copy notes.js file to corresponding folder
}
}
presentationConfig["dependencies"] = dependencies;
presentationStyle = `
<style>
${fs.readFileSync(path.resolve(extensionDirectoryPath, "./dependencies/reveal/css/reveal.css"))}
${options.isForPrint
? fs.readFileSync(path.resolve(extensionDirectoryPath, "./dependencies/reveal/css/print/pdf.css"))
: ""}
</style>
`;
presentationInitScript = `
<script>
Reveal.initialize(${JSON.stringify(Object.assign({ margin: 0.1 }, presentationConfig))})
</script>
`;
}
// prince
let princeClass = "";
if (options.isForPrince) {
princeClass = "prince";
}
let title = path.basename(this.filePath);
title = title.slice(0, title.length - path.extname(title).length); // remove '.md'
if (yamlConfig["title"]) {
title = yamlConfig["title"];
}
// prism and preview theme
let styleCSS = "";
try {
// prism *.css
styleCSS +=
!this.config.printBackground &&
!yamlConfig["print_background"] &&
!yamlConfig["isPresentationMode"]
? yield utility.readFile(path.resolve(extensionDirectoryPath, `./styles/prism_theme/github.css`), { encoding: "utf-8" })
: yield utility.readFile(path.resolve(extensionDirectoryPath, `./styles/prism_theme/${this.getPrismTheme(yamlConfig["isPresentationMode"], yamlConfig)}`), { encoding: "utf-8" });
if (yamlConfig["isPresentationMode"]) {
const theme = yamlConfig["presentation"] &&
typeof yamlConfig["presentation"] === "object" &&
yamlConfig["presentation"]["theme"]
? yamlConfig["presentation"]["theme"]
: this.config.revealjsTheme;
if (options.offline) {
presentationStyle += `<link rel="stylesheet" href="file:///${path.resolve(extensionDirectoryPath, `./dependencies/reveal/css/theme/${theme}`)}">`;
}
else {
presentationStyle += `<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/reveal.js@4.1.0/dist/theme/${theme}">`;
}
}
else {
// preview theme
styleCSS +=
!this.config.printBackground && !yamlConfig["print_background"]
? yield utility.readFile(path.resolve(extensionDirectoryPath, `./styles/preview_theme/github-light.css`), { encoding: "utf-8" })
: yield utility.readFile(path.resolve(extensionDirectoryPath, `./styles/preview_theme/${this.config.previewTheme}`), { encoding: "utf-8" });
}
// style template
styleCSS += yield utility.readFile(path.resolve(extensionDirectoryPath, "./styles/style-template.css"), { encoding: "utf-8" });
// markdown-it-admonition
if (html.indexOf("admonition") > 0) {
styleCSS += yield utility.readFile(path.resolve(extensionDirectoryPath, "./styles/markdown-it-admonition.css"), { encoding: "utf-8" });
}
}
catch (e) {
styleCSS = "";
}
// global styles
let globalStyles = "";
try {
globalStyles = yield utility.getGlobalStyles(this.config.configPath);
}
catch (error) {
// ignore it
}
// sidebar toc
let sidebarTOC = "";
let sidebarTOCScript = "";
let sidebarTOCBtn = "";
if (this.config.enableScriptExecution &&
!yamlConfig["isPresentationMode"] &&
!options.isForPrint &&
(!("html" in yamlConfig) ||
(yamlConfig["html"] && yamlConfig["html"]["toc"] !== false))) {
// enable sidebar toc by default
sidebarTOC = `<div class="md-sidebar-toc">${this.tocHTML}</div>`;
sidebarTOCBtn = '<a id="sidebar-toc-btn">≡</a>';
// toggle sidebar toc
// If yamlConfig['html']['toc'], then display sidebar TOC on startup.
sidebarTOCScript = `
<script>
${yamlConfig["html"] && yamlConfig["html"]["toc"]
? `document.body.setAttribute('html-show-sidebar-toc', true)`
: ""}
var sidebarTOCBtn = document.getElementById('sidebar-toc-btn')
sidebarTOCBtn.addEventListener('click', function(event) {
event.stopPropagation()
if (document.body.hasAttribute('html-show-sidebar-toc')) {
document.body.removeAttribute('html-show-sidebar-toc')
} else {
document.body.setAttribute('html-show-sidebar-toc', true)
}
})
</script>
`;
}
// task list script
if (html.indexOf("task-list-item-checkbox") >= 0) {
const $ = cheerio.load("<div>" + html + "</div>");
$(".task-list-item-checkbox").each((index, elem) => {
const $elem = $(elem);
let $li = $elem.parent();
if (!$li[0].name.match(/^li$/i)) {
$li = $li.parent();
}
if ($li[0].name.match(/^li$/i)) {
$li.addClass("task-list-item");
}
});
html = $.html();
}
// process styles
// move @import ''; to the very start.
let styles = styleCSS + "\n" + globalStyles;
let imports = "";
styles = styles.replace(/\@import\s+url\(([^)]+)\)\s*;/g, (whole, url) => {
imports += whole + "\n";
return "";
});
styles = imports + styles;
html = `
<!DOCTYPE html>
<html>
<head>
<title>${title}</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
${presentationStyle}
${mathStyle}
${sequenceDiagramStyle}
${fontAwesomeStyle}
${presentationScript}
${mer