'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, '
\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을
로 표시 한다.
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 `
${code}`;
}
// blockquote(string quote)
renderer.blockquote = function(quote) {
return `${quote}`; } // html(string html) : Markdown에 직접 사용된 html 코드 renderer.html = function(html) { return `${html}`; } // hr() renderer.hr = function() { return '
${text}
`; } // table(string header, string body) renderer.table = function(header, body) { // return `${code}`;
}
// br()
renderer.br = function() {
return '