obcon-scada / app /include /markdown2.js
chanmin0723's picture
Initial obcon SCADA deploy
e4bf523
Raw
History Blame Contribute Delete
15.9 kB
'use strict'
/**
* Copyright (c) 2017~2021, OBCon Inc.
* All rights reserved.
*/
/**
* @file
* @copyright 2017~2021, OBCon Inc.
* @author gye hyun james kim [pnuskgh@gmail.com]
*/
let fs = require('fs');
let path = require('path');
let marked = require('marked');
let 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
//--- Page 모듈에서 사용중
class Markdown {
constructor() {
this._req = null; //--- HTTP request
this._language = 'ko_KR'; //--- 언어
this._page = config.theme.homepage; //--- Page
this._config = null; //--- Page에 해당하는 설정
this._file = null; //--- Page에 해당하는 파일
this._content = null; //--- Page에 해당하는 파일의 내용
this._folderUrl = null; //--- Page에 해당하는 파일이 있는 폴더의 URL
}
init(req, res) {
this._req = req || null;
this._language = req.query.language || 'ko_KR';
this.page = req.query.page || config.theme.homepage;
}
get language() {
return this._language;
}
set language(newValue) {
this._language = newValue;
}
get page() {
return this._page;
}
set page(newValue) {
this._page = newValue;
this._config = null;
this._file = null;
this._content = null;
this._folderUrl = null;
}
//--- Config 파일 구조
//--- 주의 : 적용할 설정 파일 : 발견한 Markdown 파일을 기준으로 설정을 적용 한다.
//--- folder(this.file)/config.js + ${file}.js
//--- config 파일 내용은 this._defaultConfig 참조
get config() {
if (this._config == null) {
this._config = this._getConfig();
}
return this._config;
}
set config(newValue) {
this._config = newValue;
}
get file() {
if (this._file == null) {
this._file = this._getFile();
}
return this._file;
}
set file(newValue) {
this._file = newValue;
}
get content() {
if (this._content == null) {
this._content = this._getContent();
}
return this._content;
}
set content(newValue) {
this._content = newValue;
}
get folderUrl() {
if (this._folderUrl == null) {
this._folderUrl = this._getFolderUrl();
}
return this._folderUrl;
}
set folderUrl(newValue) {
this._folderUrl = newValue;
}
_getConfig() {
let config = {
layout: 'layout.ejs', //--- layout
isPublic: false //--- false. 로그인 필요
};
let tmpFile = path.join(path.dirname(this.file), 'config.js');
if (fs.existsSync(tmpFile)) {
delete require.cache[tmpFile];
let conf = require(tmpFile);
Object.assign(config, conf);
}
tmpFile = path.join(path.dirname(this.file), path.basename(this.file, '.md') + '.js');
if (fs.existsSync(tmpFile)) {
delete require.cache[tmpFile];
let conf = require(tmpFile);
Object.assign(config, conf);
}
return config;
}
//--- Markdown 파일 구조
//--- this._page = /${folder}/${file}.md
//--- manual/${service}/site_${siteID}/${lang}/this._page
//--- manual/${service}/default/${lang}/this._page
//--- manual/${service}/site_${site}/ko_KR/this._page
//--- manual/${service}/default/ko_KR/this._page
//--- 적용할 설정 파일 : 발견한 Markdown 파일을 기준으로 설정을 적용 한다.
//--- 적용할 메뉴 파일 : 발견한 Markdown 파일을 기준으로 메뉴를 적용 한다.
_getFile() {
let files = [];
if (this._req.session.isLogined) {
files.push(utils.getPath('manual', config.service.name, 'site_' + this._req.session.site.id, this._language, this._page));
files.push(utils.getPath('manual', config.service.name, 'default', this._language, this._page));
files.push(utils.getPath('manual', config.service.name, 'site_' + this._req.session.site.id, 'ko_KR', this._page));
files.push(utils.getPath('manual', config.service.name, 'default', 'ko_KR', this._page));
} else {
files.push(utils.getPath('manual', config.service.name, 'default', this._language, this._page));
files.push(utils.getPath('manual', config.service.name, 'default', 'ko_KR', this._page));
}
for (let idx = 0; idx < files.length; idx++) {
if (fs.existsSync(files[idx])) {
return files[idx].replace(/\\/g, '/');
}
}
return files[0].replace(/\\/g, '/');
}
_getContent() {
let content = '';
if ((this.file == null) || (fs.existsSync(this.file) == false)) {
logger.error('File not found: ' + this.file);
} else {
content = fs.readFileSync(this.file, "utf8");
}
return content;
}
_getFolderUrl() {
let folderUrl = this.file;
folderUrl = folderUrl.substring(folderUrl.indexOf('/manual'));
folderUrl = folderUrl.substring(0, folderUrl.length - this._page.length);
return config.http.path + folderUrl;
}
//--- 일반적인 HTML 페이지를 반환 한다.
// options = {
// target: 표시할 Content 지정 (menu, source, content)
// format: 변환한 후의 양식 (html, pdf(Reserved))
// content: target이 content일 때, markdown 내용을 담은 문자열
// filename: format이 pdf일 때, 저장할 파일 이름
// }
getHtml() {
let content = this.content.replace(/\r\n/g, '\n').replace(/ \n/g, '<br/>\n');
let options = {
baseUrl: this.folderUrl,
folder: path.dirname(this._page)
};
marked.setOptions({
baseUrl: config.http.baseUrl + config.http.path.replace(/\//g, '') + '/manual/' + config.service.name + '/' + this._language,
breaks: false, //--- true. \n을 <br/>로 표시 한다.
gfm: true,
headerIds: true,
headerPrefix: '',
highlight: function(code) {
return highlightjs.highlightAuto(code).value;
},
langPrefix: '', //--- code에서 언어의 prefix
mangle: true,
pedantic: false,
renderer: this.renderHtml(options),
sanitize: false,
sanitizer: null,
silent: false,
smartLists: true,
smartypants: false,
xhtml: false
});
return (typeof(marked.marked) == 'undefined') ? marked(content):marked.marked(content);
}
renderHtml(options) {
let renderer = new marked.Renderer();
//--- 이미지 이름에서 __ 뒤에 있는 문자열로 넓이를 계산 한다.
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 + ';';
}
//--- Block level renderer methods
// code(string code, string infostring, boolean escaped)
renderer.code = function(code, infostring, escaped) {
///--- infostring : 사용 언어
return `<pre style="width: 100%; background-color: lightgray;"><code class="${infostring}">${code}</code></pre>`;
}
// blockquote(string quote)
renderer.blockquote = function(quote) {
return `<blockquote>${quote}</blockquote>`;
}
// html(string html) : Markdown에 직접 사용된 html 코드
renderer.html = function(html) {
return `${html}`;
}
// hr()
renderer.hr = function() {
return '<hr/>';
}
// checkbox(boolean checked)
renderer.checkbox = function(checked) {
if (checked) {
return '<input checked disabled type="checkbox">';
} else {
return '<input disabled type="checkbox">';
}
}
// paragraph(string text)
renderer.paragraph = function(text) {
return `<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>`;
}
// tablerow(string content)
renderer.tablerow = function(content) {
return `<tr>${content}</tr>`;
// return `<tr class="table-primary">${content}</tr>`;
}
// tablecell(string content, object flags)
// flags = {
// header: true || false,
// align: 'center' || 'left' || 'right'
// }
renderer.tablecell = function(content, flags) {
if (flags.header) {
return `<th style="text-align: center;">${content}</th>`;
} else {
if (flags.align == null) {
return `<td>${content}</td>`;
} else {
return `<td style="text-align: ${flags.align};">${content}</td>`;
}
}
}
//--- Inline level renderer methods
// strong(string text)
renderer.strong = function(text) {
return `<strong>${text}</strong>`;
}
// em(string text)
renderer.em = function(text) {
return `<em>${text}</em>`;
}
// codespan(string code)
renderer.codespan = function(code) {
return `<code>${code}</code>`;
}
// br()
renderer.br = function() {
return '<br/>';
}
// del(string text)
renderer.del = function(text) {
return `<del>${text}</del>`;
}
// heading(string text, number level, string raw, Slugger slugger)
renderer.heading = function(text, level, raw, slugger) {
let uniqueId = slugger.slug(text);
// 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}>`;
}
}
// list(string body, boolean ordered, number start)
renderer.list = function(body, ordered, start) {
if (ordered) {
//--- <ol type="1, I, i, A, a" reversed>
return `<ol start="${start}">${body}</ol>`;
} else {
//--- <ul type="square">
return `<ul>${body}</ul>`;
}
}
// listitem(string text, boolean task, boolean checked)
renderer.listitem = function(text, task, checked) {
return `<li>${text}</li>`;
}
// link(string href, string title, string text)
renderer.link = function(href, title, text) {
let txtParam = '';
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 {
let target = '';
if (text.endsWith(' ')) {
target = 'target="_blank"';
text = text.trim();
}
if (href.endsWith('.md')) {
let baseUrl = config.http.baseUrl + config.http.path.replace(/\//g, '');
if (href.startsWith('/')) {
return `<a ${txtParam} href="${baseUrl}${href}" ${target}>${text}</a>`;
} else {
return `<a ${txtParam} href="${baseUrl}${options.folder}/${href}" ${target}>${text}</a>`;
}
} else {
if (href.startsWith('record=')) {
return `<a ${txtParam} href="${config.http.baseUrl}${config.http.path.replace(/\//g, '')}/markdowns?action=convert&${href}" ${target}>${text}</a>`;
} else if (href.startsWith('/')) {
if (href.startsWith('/${config.http.path}')) {
return `<a ${txtParam} href="${config.http.path}${href.substring(20)}" ${target}>${text}</a>`;
} else {
return `<a ${txtParam} href="${options.baseUrl}${href}" ${target}>${text}</a>`;
}
} else {
return `<a ${txtParam} href="${options.baseUrl}${options.folder}/${href}" ${target}>${text}</a>`;
}
}
}
}
// image(string href, string title, string text)
renderer.image = function(href, title, text) {
if (href.startsWith('http')) {
return `<img src="${href}" alt="${text}">`;
} else {
let width = getWidth(href);
let 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}">`;
}
}
}
// text(string text)
renderer.text = function(text) {
return `${text}`;
}
return renderer;
}
}
module.exports = Markdown;