obcon-scada / app /include /markdown.js
chanmin0723's picture
Initial obcon SCADA deploy
e4bf523
Raw
History Blame Contribute Delete
33.4 kB
'use strict'
/**
* Copyright (c) 2017~2022, OBCon Inc.
* All rights reserved.
*/
/**
* @file
* @copyright 2017~2022, OBCon Inc.
* @author gye hyun james kim [pnuskgh@gmail.com]
*/
const fs = require('fs');
const path = require('path');
const marked = require('marked');
const highlightjs = require('highlightjs');
//--- https://github.com/markedjs/marked
//--- https://marked.js.org/
//--- https://highlightjs.org/
//--- https://www.hahwul.com/2019/02/editorjs-markdown-javascript-library.html
//--- Markdown 모듈과 openapi/deploy.js 파일에서 사용중
class Markdown {
constructor() {
this._isDebug = false;
this._options = null; //--- 옵션
this._file = null; //--- Markdown 파일
this._config = null; //--- Markdown 파일을 위한 설정
this._content = null; //--- Markdown 파일 내용
this._baseUrl = null; //--- Markdown 파일의 baseUrl
this._service = null; //--- 강제로 지정한 서비스 이름
}
debug(...messages) {
if (this._isDebug) {
console.log(...messages)
}
}
initialize(options={}) {
this._options = Object.assign({
req: null, //--- Express.Request
res: null, //--- Express.Response
language: 'ko_KR', //--- 사용 언어
file: 'README.md', //--- Markdown 파일
service: null //--- 강제로 지정한 서비스 이름
}, options);
this._file = null; //--- Markdown 파일
this._config = null; //--- Markdown 파일을 위한 설정
this._content = null; //--- Markdown 파일 내용
this._baseUrl = null; //--- Markdown 파일의 baseUrl
this._service = null; //--- 강제로 지정한 서비스 이름
}
get options() {
return this._options;
}
get file() {
if (this._file == null) {
this._file = this._getFile();
}
return this._file;
}
//--- Markdown 파일 구조
//--- this.options.file = /${folder}/${file}.md
//--- manual/${service}/site_${siteID}/${lang}/this.options.file
//--- manual/${service}/site_${site}/ko_KR/this.options.file
//--- manual/${service}/default/${lang}/this.options.file
//--- manual/${service}/default/ko_KR/this.options.file
_getFile() {
const file = this.options.file;
const files = [];
if ((this.options.req) && (this.options.req.session) && (this.options.req.session.isLogined)) {
files.push(utils.getPath('manual', config.service.name, 'site_' + this.options.req.session.site.id, this.options.language, file));
files.push(utils.getPath('manual', config.service.name, 'site_' + this.options.req.session.site.id, 'ko_KR', file));
files.push(utils.getPath('manual', config.service.name, 'default', this.options.language, file));
files.push(utils.getPath('manual', config.service.name, 'default', 'ko_KR', file));
} else {
files.push(utils.getPath('manual', config.service.name, 'default', this.options.language, file));
files.push(utils.getPath('manual', config.service.name, 'default', 'ko_KR', file));
}
for (let idx = 0; idx < files.length; idx++) {
if (fs.existsSync(files[idx])) {
return files[idx].replace(/\\/g, '/');
}
}
return files[0].replace(/\\/g, '/');
}
//--- Menu 파일 종류
//--- 로그인 전 : MENU_PUBLIC.md
//--- 로그인 후 : MENU_PRIVATE.md
//--- Menu 파일 구조
//--- 적용할 메뉴 파일 : 발견한 Markdown 파일을 기준으로 메뉴를 적용 한다.
//--- folder(this._file)/${menufile}.md, 상위 폴더로 순환
_getMenuFile() {
let menuFile = ((this.options.req != null) && (this.options.req.session.isLogined)) ? 'MENU_PRIVATE.md':'MENU_PUBLIC.md';
menuFile = this.config.menuFile || menuFile;
const files = [];
const manualFolder = path.join(config.folder_root, 'manual', config.service.name);
let menuFolder = path.dirname(this.file);
while (manualFolder.length <= menuFolder.length) {
files.push(path.join(menuFolder, menuFile));
menuFolder = path.dirname(menuFolder);
}
let rtMenuFile = files[0];
for (let idx = 0; idx < files.length; idx++) {
if (fs.existsSync(files[idx])) {
rtMenuFile = files[idx]
break;
}
}
return rtMenuFile;
}
get config() {
if (this._config == null) {
this._config = this._getConfig_new();
// this._config = this._getConfig();
}
return this._config;
}
//--- 사용하지 않음
// _getConfig() {
// let rtConfig = this._getDefaultConfig();
// [
// path.join(path.dirname(this.file), 'config.js'),
// path.join(path.dirname(this.file), path.basename(this.file, '.md') + '.js')
// ].forEach(file => {
// if (fs.existsSync(file)) {
// console.log('getConfig: file exist');
// delete require.cache[file];
// const conf = require(file);
// Object.assign(rtConfig, conf);
// }
// });
// return rtConfig;
// }
_getConfig_new() {
const file = path.basename(this.options.file, '.md') + '.js';
const files = [];
if ((this.options.req) && (this.options.req.session) && (this.options.req.session.isLogined)) {
files.push(utils.getPath('manual', config.service.name, 'site_' + this.options.req.session.site.id, this.options.language, file));
files.push(utils.getPath('manual', config.service.name, 'site_' + this.options.req.session.site.id, this.options.language, 'config.js'));
files.push(utils.getPath('manual', config.service.name, 'site_' + this.options.req.session.site.id, 'ko_KR', file));
files.push(utils.getPath('manual', config.service.name, 'site_' + this.options.req.session.site.id, 'ko_KR', 'config.js'));
files.push(utils.getPath('manual', config.service.name, 'default', this.options.language, file));
files.push(utils.getPath('manual', config.service.name, 'default', this.options.language, 'config.js'));
files.push(utils.getPath('manual', config.service.name, 'default', 'ko_KR', file));
files.push(utils.getPath('manual', config.service.name, 'default', 'ko_KR', 'config.js'));
} else {
files.push(utils.getPath('manual', config.service.name, 'default', this.options.language, file));
files.push(utils.getPath('manual', config.service.name, 'default', this.options.language, 'config.js'));
files.push(utils.getPath('manual', config.service.name, 'default', 'ko_KR', file));
files.push(utils.getPath('manual', config.service.name, 'default', 'ko_KR', 'config.js'));
}
let rtConfig = this._getDefaultConfig();
for (let idx = 0; idx < files.length; idx++) {
const file = files[idx];
if (fs.existsSync(file)) {
// console.log('getConfig: file exist', file);
delete require.cache[file];
const conf = require(file);
Object.assign(rtConfig, conf);
break;
}
}
return rtConfig;
}
_getDefaultConfig() {
return {
layout: config.theme.layout, //--- layout
isPublic: false, //--- false. 로그인 필요
menuFile: null, //--- 메뉴 파일
showIndex: true, //--- Reserved: 페이지 상단에 목차 표시 (Default. true)
prev: null, //--- Reserved: 이전 페이지 (Default. null)
next: null //--- Reserved: 다음 페이지 (Default. null)
};
}
get content() {
if (this._content == null) {
this._content = this._getContent();
}
return this._content;
}
_getContent() {
if ((this._file == null) || (fs.existsSync(this._file) == false)) {
logger.error('File not found: ' + this._file);
return '';
} else {
return fs.readFileSync(this._file, "utf8");
}
}
get baseUrl() {
if (this._baseUrl == null) {
this._baseUrl = this._getBaseUrl();
}
return this._baseUrl;
}
_getBaseUrl() {
let baseUrl = this.file;
baseUrl = baseUrl.substring(baseUrl.indexOf('/manual'));
baseUrl = baseUrl.substring(0, baseUrl.length - this.options.file.length);
return utils.link.join(config.http.path, baseUrl);
}
get service() {
if (this._service == null) {
this._service = this._getService();
}
return this._service;
}
_getService() {
let service = this.options.service || '';
if (service == '') {
//--- Parameter로 전달된 servcie 값이 없으면 세션에 저장된 service 값을 사용 한다.
//--- service 값이 없어도 이전에 지정한 service 값으로 화면을 조회 한다.
if ((this.options.req) && (this.options.req.session) && (this.options.req.session.service)) {
service = this.options.req.session.service;
}
}
if (service == '') {
service = config.service.name;
}
//--- 세션에 service 이름을 저장 한다.
if ((this.options.req) && (this.options.req.session) && (this.options.req.session.service != service)) {
this.options.req.session.service = service;
this.options.req.session.save(function(err) { });
}
return service;
}
// //--- Markdown 파일을 HTML 페이지로 변환하여 반환 한다.
// // options = {
// // target: 표시할 Content 지정 (menu, source, content)
// // format: 변환한 후의 양식 (html, pdf(Reserved))
// // content: target이 content일 때, markdown 내용을 담은 문자열
// // filename: format이 pdf일 때, 저장할 파일 이름
// // }
// getHtml() {
// // const parserHtml = this._getHtmlParser({
// // baseUrl: this.baseUrl,
// // folder: path.dirname(this.options.file)
// // });
// // //--- http://local.bluestones.biz:90/cms/manual/service_cms_bluestone/default/ko_KR/${config.http.path}/invests
// // const baseUrl = config.http.baseUrl + config.http.path + `/manual/${this.service}/${this.options.language}`;
// // this._setMarkedOptions(baseUrl, parserHtml);
// // return this.parseMarkdown(this.content);
// return this.convert({
// baseUrl: this.baseUrl,
// folder: path.dirname(this.options.file)
// });
// }
// options = {
// source: ~
// content: ~
// target: ~
// }
convert(options={}) {
options = Object.assign({
language: this.options.language, //--- 언어 (ko_KR, en_US, ...)
folder: path.dirname(this.options.file), //--- Markdown 파일의 폴더
file: this.options.file, //--- Markdown 파일 이름
source: options.source || 'file', //--- 원본 : file. file 사용, menu. 메뉴 file 사용, content. content 사용
content: options.content || '', //--- target이 content인 경우 markdown 문서를 저장
//--- 변환 : html. HTML 반환, index. 인덱스 반환, menu. 메뉴 HTML 반환, info: 정보 반환
target: options.target || 'html',
baseUrl: options.baseUrl || this.baseUrl //--- link와 image에서 사용하여 prefix URL
// //--- Target 양식 : source. 원본 반환, index. 인덱스 반환, menu. 메뉴 반환, content. ???
// target: options.target || 'source',
//--- Output 포맷 : html. HTML로 반환. pdf. PDF로 변환 (Reserved)
// format: options.format || 'html',
// //--- format이 pdf일 때, 저장할 파일 이름 (Reserved)
// // filename: options.filename || 'noname.pdf',
}, options);
// const baseUrl = config.http.baseUrl + config.http.path + '/manual/' + config.service.name + '/' + this.options.language;
const baseUrl = utils.link.join(config.http.baseUrl, config.http.path, 'manual', config.service.name, + this.options.language);
switch(options.target) {
case 'index': //--- Markdown Page Index
this._setMarkedOptions(baseUrl, this._getIndexParser(options));
break;
case 'menu': //--- 문서 목차
this._setMarkedOptions(baseUrl, this._getMenuParser(options));
break;
case 'html': //--- Markdown을 HTML로 변환
this._setMarkedOptions(baseUrl, this._getHtmlParser(options));
break;
case 'info':
return {
filename: this.file
};
break;
default:
break;
}
const content = this.getContent(options);
return this._parseMarkdown(modules.markdowns.lang.replaceContent(content, this.service));
}
getContent(options) {
switch (options.source) {
case 'content':
return options.content;
case 'menu':
return this._getContentFromFile(this._getMenuFile());
case 'file':
default:
return this._getContentFromFile(this.file);
}
}
//--- https://marked.js.org/using_advanced
//--- https://marked.js.org/using_pro
_setMarkedOptions(baseUrl, renderer) {
marked.setOptions({
baseUrl: baseUrl.replace('//', '/'),
renderer: renderer,
highlight: function(code, lang) {
// const language = highlightjs.getLanguage(lang) ? lang : 'plaintext';
// return highlightjs.highlight(code, { language }).value;
return highlightjs.highlightAuto(code).value;
},
langPrefix: 'language-', //--- code에서 언어의 prefix
breaks: false, //--- true. \n을 <br/>로 표시 한다.
gfm: true,
headerIds: true,
headerPrefix: '',
mangle: true,
pedantic: false,
sanitize: false,
sanitizer: null,
silent: false,
smartLists: true,
smartypants: false,
xhtml: false
});
}
_parseMarkdown(content) {
let rtContent = content.replace(/\r\n/g, '\n');
rtContent = (typeof(marked.marked) == 'undefined') ? marked(rtContent):marked.marked(rtContent);
//--- To-Do : windows 환경에서 marked의 경우 다음 오류가 발생 한다.
//--- "## Sitemap\n \n" #--- 비정상 표시
//--- "## Sitemap\n\n" #--- 정상 표시
// const contentPre = (typeof(marked.marked) == 'undefined') ? marked(this._contentPre(content)):marked.marked(this._contentPre(content));
// if (format == 'html') {
// rtContent = rtContent.replace(/\n \n -/g, '\n -');
// //--- ' \n'을 '<br/>\n'으로 변환 한다.
// return rtContent.replace(/ \n/g, '<br/>\n');
// }
return rtContent;
}
_getNoneParser() {
const renderer = new marked.Renderer();
const funcNone = () => '';
renderer.code = funcNone;
renderer.blockquote = funcNone;
renderer.html = funcNone;
renderer.heading = funcNone;
renderer.hr = funcNone;
renderer.list = funcNone;
renderer.listitem = funcNone;
renderer.checkbox = funcNone;
renderer.paragraph = funcNone;
renderer.table = funcNone;
renderer.tablerow = funcNone;
renderer.tablecell = funcNone;
renderer.strong = (text) => `<strong>${text}</strong>`;
renderer.em = funcNone;
renderer.codespan = funcNone;
renderer.br = funcNone;
renderer.del = funcNone;
renderer.link = funcNone;
renderer.image = funcNone;
renderer.text = (text) => text;
return renderer;
}
//--- Web_Publishing.md 파일 참조
_getHtmlParser(options) {
const debug = this.debug.bind(this);
const renderer = new marked.Renderer();
//--- Block level renderer methods
// code(string code, string infostring, boolean escaped)
//--- infostring : 사용 언어
renderer.code = (code, infostring, escaped) => `<pre style="width: 100%; background-color: lightgray;"><code class="${infostring}">${code}</code></pre>`;
renderer.blockquote = (quote) => `<blockquote>${quote}</blockquote>`;
renderer.html = (html) => html;
let headingIndex = 0;
// heading(string text, number level, string raw, Slugger slugger)
renderer.heading = function(text, level, raw, slugger) {
headingIndex = headingIndex + 1;
// const uniqueId = slugger.slug(text);
const uniqueId = `heading_${headingIndex}`;
// return `<h${level} id="${uniqueId}">${text}</h${level}>`;
if (level <= 2) {
return `<br/><h${level} id="${uniqueId}">${text}</h${level}><hr/>`;
} else {
return `<br/><h${level} id="${uniqueId}">${text}</h${level}>`;
}
}
renderer.hr = () => '<hr/>';
// list(string body, boolean ordered, number start)
let index_list = 0;
renderer.list = function(body, ordered, start) {
index_list = index_list + 1;
if (ordered) {
//--- <ol type="1, I, i, A, a" reversed>
return `<ol id="list_${index_list}" start="${start}">${body}</ol>`;
} else {
//--- <ul type="square">
return `<ul id="list_${index_list}">${body}</ul>`;
}
}
// listitem(string text, boolean task, boolean checked)
renderer.listitem = function(text, task, checked) {
return `<li>${text}</li>`;
}
renderer.checkbox = (checked) => `<input ${(checked) ? 'checked':''} disabled type="checkbox">`;
renderer.paragraph = (text) => `<p>${text}</p>`;
// table(string header, string body)
renderer.table = function(header, body) {
// return `<table border="1"><thead>${header}</thead><tbody>${body}</tbody></table>`;
return `<table class="table table-bordered table-hover table-sm markdown" style="${config.theme.font.size}"><thead>${header}</thead><tbody>${body}</tbody></table>`;
// return `<table class="table table-hover table-sm markdown" style="${config.theme.font.size}"><thead>${header}</thead><tbody>${body}</tbody></table>`;
}
renderer.tablerow = (content) => `<tr>${content}</tr>`;
// tablecell(string content, object flags)
// flags = {
// header: true || false,
// align: 'center' || 'left' || 'right'
// }
renderer.tablecell = function(content, flags) {
//--- To-Do : content에 mardown 파싱 도입 검토
if (flags.header) {
return `<th style="text-align: center;">${content}</th>`;
} else {
const align = (flags.align == null) ? '': `style="text-align: ${flags.align};`;
return `<td ${align}">${content}</td>`;
}
}
//--- Inline level renderer methods
// strong(string text)
renderer.strong = (text) => `<strong>${text}</strong>`;
renderer.em = (text) => `<em>${text}</em>`;
renderer.codespan = (code) => `<code>${code}</code>`;
renderer.br = () => '<br/>';
renderer.del = (text) => `<del>${text}</del>`;
// link(string href, string title, string text)
//--- href 규칙
//--- ~' ' : 새창에서 표시
renderer.link = function(href, title, text) {
let attrs = '';
//--- herf | target
if (-1 < href.indexOf('|')) {
const data = href.split('|');
href = data[0];
attrs = `target="${data[1]}"`;
}
//--- ~' ' : 새창에서 표시
if (text.endsWith(' ')) {
text = text.trim();
attrs = 'target="_blank"';
}
// http://local.bluestones.biz:90/cms/manual/service_cms_bluestone/default/ko_KR/${config.http.path}/invests
href = href.replace(/ /g, '%20');
if (href.startsWith('http')) {
return `<a href="${href}" target="_blank">${text}</a>`;
} else if (href.startsWith('mailto:')) {
return `<a href="${href}">${text}</a>`;
} else if (href.startsWith('tel:')) {
return `<a href="${href}">${text}</a>`;
} else {
if (href.endsWith('.md')) {
if (href.startsWith('/')) {
return `<a href="${utils.link.join(config.http.baseUrl, config.http.path, href)}" ${attrs}>${text}</a>`;
} else {
return `<a href="${utils.link.join(config.http.baseUrl, config.http.path, options.folder, href)}" ${attrs}>${text}</a>`;
}
} else if (href.startsWith('record=')) {
return `<a href="${utils.link.join(config.http.baseUrl, config.http.path, '/markdowns?action=convert&', href)}" ${attrs}>${text}</a>`;
} else {
if (href.startsWith('/')) {
return `<a href="${utils.link.join(config.http.path, href)}" ${attrs}>${text}</a>`;
} else {
return `<a href="${utils.link.join(options.baseUrl, options.folder, href)}" ${attrs}>${text}</a>`;
}
}
}
}
//--- 이미지 이름에서 __ 뒤에 있는 문자열로 넓이를 계산 한다.
//--- ~__width : style="width: ${width}"
//--- ~__widthp : style="width: ${width}%"
// function _getWidth(href) {
// let tmpStr = path.basename(href);
// if (tmpStr.lastIndexOf('__') == -1) {
// return '';
// }
// tmpStr = (-1 < tmpStr.indexOf('.')) ? tmpStr.substring(0, tmpStr.indexOf('.')):tmpStr;
// tmpStr = tmpStr.substring(tmpStr.lastIndexOf('__') + 2);
// if (tmpStr.endsWith('p')) {
// tmpStr = tmpStr.substring(0, tmpStr.length - 1) + '%';
// }
// return `width: ${tmpStr};`;
// }
//--- 이미지 href 구성 : href | width
// function _parseImageHref(href) {
// const data = href.split('|');
// const myBaseUrl = config.http.baseUrl.substring(0, config.http.baseUrl.length - 1);
// const src = myBaseUrl + options.baseUrl + ((href.startsWith('/')) ? '':`${options.folder}/`) + data[0];
// return {
// src: (href.startsWith('http')) ? data[0]:src,
// attrs: `style="width: ${data[1]}"`
// };
// }
// image(string href, string title, string text)
renderer.image = function(href, title, text) {
let attrs = '';
//--- 이미지 href 구성 : href | width
if (-1 < href.indexOf('|')) {
const data = href.split('|');
href = data[0];
attrs = `style="width: ${data[1]}"`;
}
//--- 이미지 이름에서 __ 뒤에 있는 문자열로 넓이를 계산 한다.
//--- ~__width : style="width: ${width}"
//--- ~__widthp : style="width: ${width}%"
const basename = path.basename(href);
if (-1 < basename.lastIndexOf('__')) {
let width = (-1 < basename.indexOf('.')) ? basename.substring(0, basename.indexOf('.')):basename;
width = width.substring(width.lastIndexOf('__') + 2);
if (width.endsWith('p')) {
width = width.substring(0, width.length - 1) + '%';
}
attrs = `style="width: ${width}"`;
}
if (href.startsWith('http')) {
return `<img src="${href}" alt="${text}" ${attrs}>`;
} else {
const myBaseUrl = (config.http.baseUrl.endsWith('/')) ? config.http.baseUrl.substring(0, config.http.baseUrl.length - 1):config.http.baseUrl;
if (href.startsWith('/')) {
return `<img src="${myBaseUrl}${options.baseUrl}${href}" alt="${text}" ${attrs}>`;
} else {
return `<img src="${myBaseUrl}${options.baseUrl}${options.folder}/${href}" alt="${text}" ${attrs}>`;
}
}
// if (href.startsWith('http')) {
// return `<img src="${href}" alt="${text}">`;
// } else {
// const width = _getWidth(href);
// const myBaseUrl = config.http.baseUrl.substring(0, config.http.baseUrl.length - 1);
// if (href.startsWith('/')) {
// return `<img src="${myBaseUrl}${options.baseUrl}${href}" alt="${text}" style="${width}">`;
// } else {
// return `<img src="${myBaseUrl}${options.baseUrl}${options.folder}/${href}" alt="${text}" style="${width}">`;
// }
// }
}
return renderer;
}
_getIndexParser(options) {
const renderer = this._getNoneParser();
let headingIndex = 0;
renderer.heading = function(text, level, raw, slugger) {
headingIndex = headingIndex + 1;
const pageIndex = {
id: `heading_${headingIndex}`,
text: text,
level: level
};
return JSON.stringify(pageIndex) + ',';
}
return renderer;
}
_getMenuParser(options) {
const debug = this.debug.bind(this);
const renderer = this._getNoneParser();
let idxAll = 0;
let menuGroupIdx = 0; //--- 메뉴 그룹의 인덱스
let menuIdx = 0; //--- 메뉴의 인덱스
renderer.list = function(body, ordered, start) {
idxAll = idxAll + 1;
if (ordered) {
//--- 현재 사용하지 않음
return `<ol start="${start}" class="idxAll_${idxAll}_${menuGroupIdx}_${menuIdx + 1}">${body}</ol>`;
} else {
menuGroupIdx = menuGroupIdx + 1;
return `<ul class="nav flex-column nav-pills idxAll_${idxAll}_${menuGroupIdx}_${menuIdx + 1}" id="menuGroup_${menuGroupIdx}">${body}</ul>`;
}
}
renderer.listitem = function(text, task, checked) {
idxAll = idxAll + 1;
text = text.replace(/<p>/g, '').replace(/<\/p>/g, '');
if (text.startsWith('<a ')) {
return `<li class="nav-item idxAll_${idxAll}_${menuGroupIdx + 1}_${menuIdx}">${text}</li>`;
} else {
//--- 메뉴에는 되나 선택시 정상 동작하지 않음
return `<li class="nav-item idxAll_${idxAll}_${menuGroupIdx + 1}_${menuIdx}">${renderer.link('#', text, text)}</li>`;
}
}
renderer.link = function(href, title, text) {
idxAll = idxAll + 1;
let txtParam = '';
let txtClass = `nav-link idxAll_${idxAll}_${menuGroupIdx + 1}_${menuIdx + 1}`;
menuIdx = menuIdx + 1;
if ((href.startsWith('record=')) && (options.file.endsWith(href + '.md'))) {
txtClass = txtClass + ' active';
} else if (href == options.file) {
txtClass = txtClass + ' active';
}
txtParam = `class="${txtClass}" style="font-size: 14px;" id="menu_${menuIdx}"`;
href = href.replace(/ /g, '%20');
if (href.startsWith('http')) {
return `<a ${txtParam} href="${href}" target="_blank">${text}</a>`;
} else if (href.startsWith('mailto:')) {
return `<a ${txtParam} href="${href}">${text}</a>`;
} else if (href.startsWith('tel:')) {
return `<a ${txtParam} href="${href}">${text}</a>`;
} else if (href.startsWith('#')) {
return `<a ${txtParam} onclick="selectMenu(this);" href="${href}">${text}</a>`;
} else {
//--- To-Do : href="url|_blank" 형태로 처리
let target = '';
if (text.endsWith(' ')) {
target = 'target="_blank"';
text = text.trim();
}
if (href.endsWith('.md')) {
if (href.startsWith('/')) {
return `<a ${txtParam} href="${utils.link.join(config.http.baseUrl, config.http.path, href)}" ${target}>${text}</a>`;
} else {
return `<a ${txtParam} href="${utils.link.join(config.http.baseUrl, config.http.path, options.folder, href)}" ${target}>${text}</a>`;
}
} else if (href.startsWith('record=')) {
return `<a ${txtParam} href="${utils.link.join(config.http.baseUrl, config.http.path, '/markdowns?action=convert&', href)}" ${target}>${text}</a>`;
} else {
if (href.startsWith('/')) {
return `<a ${txtParam} href="${utils.link.join(config.http.path, href)}" ${target}>${text}</a>`;
} else {
return `<a ${txtParam} href="${utils.link.join(options.baseUrl, options.folder, href)}" ${target}>${text}</a>`;
}
}
}
}
let headingIndex = 0;
renderer.heading = function(text, level, raw, slugger) {
headingIndex = headingIndex + 1;
const uniqueId = `heading_${headingIndex}`;
if (level <= 2) {
return `<br/><h${level} id="menu_${uniqueId}">${text}</h${level}><hr/>`;
} else {
return `<h${level} id="menu_${uniqueId}">${text}</h${level}>`;
}
}
renderer.hr = () => '<hr/>';
renderer.paragraph = (text) => `<p>${text}</p>`;
return renderer;
}
_getContentFromFile(filepath) {
if ((filepath == null) || (fs.existsSync(filepath) == false)) {
logger.error('File not found: ' + filepath);
return '';
}
return fs.readFileSync(filepath, "utf8");
}
}
module.exports = Markdown;