Skip to content

Commit 3d0b6e4

Browse files
committed
add export html plugin
1 parent 95cf88c commit 3d0b6e4

File tree

4 files changed

+603
-0
lines changed

4 files changed

+603
-0
lines changed

src/public/config.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ var Config = {
33
"theme",
44
"import_evernote",
55
"export_pdf",
6+
"export_html",
67
"langs"
78
],
89
"langs": [
Lines changed: 224 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,224 @@
1+
/**
2+
* 导出HTML插件
3+
* @author life life@leanote.com
4+
* 选择目录, 将图片保存到文件夹中, 有个html文件(以笔记名命名)
5+
* 注意, fs.existsSync总返回false, readFileSync可用
6+
*/
7+
define(function() {
8+
var async = require('async');
9+
10+
var exportHTML = {
11+
langs: {
12+
'en-us': {
13+
'export': 'Export HTML',
14+
'exportSuccess': 'HTML saved successful!',
15+
'exportFailure': 'HTML saved failure!',
16+
'notExists': 'Please sync your note to ther server firslty.'
17+
},
18+
'zh-cn': {
19+
'export': '导出HTML',
20+
'exportSuccess': 'HTML导出成功!',
21+
'exportFailure': 'HTML导出失败!'
22+
},
23+
'zh-hk': {
24+
'export': '導出HTML',
25+
'exportSuccess': 'HTML導出成功!',
26+
'exportFailure': 'HTML導出失敗!'
27+
}
28+
},
29+
30+
_inited: false,
31+
init: function() {
32+
var me = this;
33+
me._inited = true;
34+
},
35+
36+
getPluginPath: function() {
37+
return Api.evtService.getProjectBasePath() + '/public/plugins/export_html' ;
38+
},
39+
40+
htmlTpl: '',
41+
markdownTpl: '',
42+
getTpl: function(isMarkdown) {
43+
var tpl = isMarkdown ? this.markdownTpl : this.htmlTpl;
44+
if(tpl) {
45+
return tpl;
46+
}
47+
var basePluginPath = this.getPluginPath();
48+
49+
var tplName = isMarkdown ? 'markdown' : 'html';
50+
var tplPath = basePluginPath + '/tpl/' + tplName + '.tpl';
51+
tpl = Api.nodeFs.readFileSync(tplPath, 'utf-8');
52+
isMarkdown ? (this.markdownTpl = tpl) : (this.htmlTpl = tpl);
53+
return tpl;
54+
},
55+
// 生成html或markdown
56+
render: function(note) {
57+
var tpl = this.getTpl(note.IsMarkdown);
58+
var title = note.Title || getMsg('Untitled');
59+
tpl = tpl.replace(/\{title\}/g, title);
60+
tpl = tpl.replace(/\{content\}/g, note.Content);
61+
return tpl;
62+
},
63+
64+
replaceAll: function(src, pattern, to) {
65+
if(!src) {
66+
return src;
67+
}
68+
while(true) {
69+
var oldSrc = src;
70+
src = src.replace(pattern, to);
71+
if(oldSrc === src) {
72+
return src;
73+
}
74+
}
75+
},
76+
77+
fixFilename: function(filename) {
78+
var reg = new RegExp("/|#|\\$|!|\\^|\\*|'| |\"|%|&|\\(|\\)|\\+|\\,|/|:|;|<|>|=|\\?|@|\\||\\\\", 'g');
79+
filename = filename.replace(reg, "-");
80+
// 防止出现两个连续的-
81+
while(filename.indexOf('--') != -1) {
82+
filename = this.replaceAll(filename, '--', '-');
83+
}
84+
return filename;
85+
},
86+
87+
// 写图片,并替换图片路径
88+
writeFiles: function(filesPath, content, callback) {
89+
var me = this;
90+
// http://127.0.0.1:8912/api/file/getImage?fileId=5581029f6289dc3301000000
91+
// 找到图片
92+
var reg = new RegExp(Api.evtService.localUrl + '/api/file/getImage\\?fileId=([0-9a-zA-Z]{24})', 'g');
93+
// console.log(Api.evtService.localUrl + '/api/file/getImage\\?fileId=([0-9a-zA-Z]{24})');
94+
var matches = content.match(reg);
95+
// content = content.replace(reg, Evt.leanoteUrl + '/api/file/getImage');
96+
97+
if(matches && matches.length) {
98+
Api.nodeFs.mkdirSync(filesPath);
99+
// 取最后一个名字
100+
var pathInfo = Api.commonService.splitFile(filesPath);
101+
var dirName = pathInfo.name;
102+
103+
async.eachSeries(matches, function(url, cb) {
104+
var fileId = url.substr(url.length - 24);
105+
Api.fileService.getImageLocalPath(fileId, function(err, imagePath) {
106+
// 将图片copy到filesPath下
107+
if(imagePath) {
108+
var distFileName = fileId + '.png';
109+
Api.commonService.copyFile(imagePath, filesPath + '/' + distFileName, function(ok) {
110+
content = me.replaceAll(content, url, dirName + '/' + distFileName);
111+
cb();
112+
});
113+
}
114+
else {
115+
cb();
116+
}
117+
});
118+
}, function() {
119+
callback(content);
120+
});
121+
122+
return;
123+
}
124+
callback(content);
125+
},
126+
127+
// 得到存放images, js, css的路径
128+
getFilesPath: function(basePath, nameNotExt, n, cb) {
129+
var me = this;
130+
var absPath = basePath + '/' + nameNotExt + '_files';
131+
if (n > 1) {
132+
absPath += '-' + n;
133+
}
134+
Api.nodeFs.exists(absPath, function(exists) {
135+
if(!exists) {
136+
cb(absPath);
137+
}
138+
else {
139+
me.getFilesPath(basePath, nameNotExt, n+1, cb);
140+
}
141+
});
142+
},
143+
144+
// 得到可用的文件名, 避免冲突
145+
getHtmlFilePath: function(pathInfo, n, cb) {
146+
var me = this;
147+
if(n > 1) {
148+
pathInfo.nameNotExt = pathInfo.nameNotExtRaw + '-' + n;
149+
}
150+
var absPath = pathInfo.getFullPath();
151+
// 总是覆盖
152+
return cb(absPath);
153+
154+
// Api.nodeFs.existsSync(absPath) 总是返回false, 不知道什么原因
155+
// 在控制台上是可以的
156+
Api.nodeFs.exists(absPath, function(exists) {
157+
if(!exists) {
158+
cb(absPath);
159+
}
160+
else {
161+
me.getHtmlFilePath(pathInfo, n+1, cb);
162+
}
163+
});
164+
},
165+
166+
exportHTML: function(note) {
167+
var me = this;
168+
if(!note) {
169+
return;
170+
}
171+
172+
var name = note.Title ? note.Title + '.html' : getMsg('Untitled') + '.html';
173+
name = me.fixFilename(name);
174+
175+
Api.gui.dialog.showSaveDialog(Api.gui.getCurrentWindow(), {title: name, defaultPath: name}, function(targetPath) {
176+
if(targetPath) {
177+
// 将路径和名字区分开
178+
var pathInfo = Api.commonService.splitFile(targetPath);
179+
pathInfo.nameNotExt = me.fixFilename(pathInfo.nameNotExt); // 重新修正一次
180+
var nameNotExt = pathInfo.nameNotExt;
181+
pathInfo.nameNotExtRaw = pathInfo.nameNotExt;
182+
// 得到可用文件的绝对路径
183+
me.getHtmlFilePath(pathInfo, 1, function(absHtmlFilePath) {
184+
me.getFilesPath(pathInfo.path, pathInfo.nameNotExt, 1, function(absFilesPath) {
185+
// alert(absHtmlFilePath + ' --- ' + absFilesPath);
186+
var html = me.render(note);
187+
// 写图片
188+
me.writeFiles(absFilesPath, html, function(html) {
189+
// 把文件写到
190+
Api.commonService.writeFile(absHtmlFilePath, html);
191+
Notify.show({title: 'Info', body: getMsg('plugin.export_html.exportSuccess')});
192+
});
193+
});
194+
});
195+
}
196+
});
197+
},
198+
199+
// 打开前要执行的
200+
onOpen: function() {
201+
var me = this;
202+
var gui = Api.gui;
203+
204+
var menu = {
205+
label: Api.getMsg('plugin.export_html.export'),
206+
click: (function() {
207+
return function(note) {
208+
me.exportHTML(note);
209+
}
210+
})()
211+
};
212+
Api.addExportMenu(menu);
213+
},
214+
// 打开后
215+
onOpenAfter: function() {
216+
},
217+
// 关闭时需要运行的
218+
onClose: function() {
219+
}
220+
};
221+
222+
return exportHTML;
223+
224+
});
Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<meta charset="utf-8">
5+
<meta name="tool" content="leanote-desktop-app">
6+
<title>{title}</title>
7+
<style>
8+
9+
*{font-family:"lucida grande","lucida sans unicode",lucida,helvetica,"Hiragino Sans GB","Microsoft YaHei","WenQuanYi Micro Hei",sans-serif;}
10+
11+
body {
12+
margin: 0;
13+
}
14+
15+
/*公用文字样式*/
16+
h1{font-size:30px}h2{font-size:24px}h3{font-size:18px}h4{font-size:14px}
17+
.note-container{
18+
width:850px;
19+
margin:auto;
20+
padding: 10px 20px;
21+
box-shadow: 1px 1px 10px #eee;
22+
}
23+
#title {
24+
margin: 0;
25+
}
26+
table {
27+
margin-bottom: 16px;
28+
border-collapse: collapse;
29+
}
30+
table th, table td {
31+
padding: 6px 13px;
32+
border: 1px solid #ddd;
33+
}
34+
table th {
35+
font-weight: bold;
36+
}
37+
38+
table tr {
39+
background-color: none;
40+
border-top: 1px solid #ccc;
41+
}
42+
table tr:nth-child(2n) {
43+
background-color: rgb(247, 247, 249);
44+
}
45+
.mce-item-table, .mce-item-table td, .mce-item-table th, .mce-item-table caption {
46+
border: 1px solid #ddd;
47+
border-collapse: collapse;
48+
padding: 6px 13px;
49+
}
50+
blockquote {
51+
border-left-width:10px;
52+
background-color:rgba(128,128,128,0.05);
53+
border-top-right-radius:5px;
54+
border-bottom-right-radius:5px;
55+
padding:15px 20px;
56+
border-left:5px solid rgba(128,128,128,0.075);
57+
}
58+
blockquote p {
59+
margin-bottom:1.1em;
60+
font-size:1em;
61+
line-height:1.45
62+
}
63+
blockquote ul:last-child,blockquote ol:last-child {
64+
margin-bottom:0
65+
}
66+
pre {
67+
padding: 18px;
68+
background-color: #f7f7f9;
69+
border: 1px solid #e1e1e8;
70+
border-radius: 3px;
71+
display: block;
72+
}
73+
code {
74+
padding: 2px 4px;
75+
font-size: 90%;
76+
color: #c7254e;
77+
white-space: nowrap;
78+
background-color: #f9f2f4;
79+
border-radius: 4px;
80+
}
81+
.footnote {
82+
vertical-align: top;
83+
position: relative;
84+
top: -0.5em;
85+
font-size: .8em;
86+
}
87+
88+
hr {
89+
margin:2em 0
90+
}
91+
img {
92+
max-width:100%
93+
}
94+
pre {
95+
word-break:break-word
96+
}
97+
p,pre,pre.prettyprint,blockquote {
98+
margin:0 0 1.1em
99+
}
100+
hr {
101+
margin:2em 0
102+
}
103+
img {
104+
max-width:100%
105+
}
106+
.sequence-diagram,.flow-chart {
107+
text-align:center;
108+
margin-bottom:1.1em
109+
}
110+
.sequence-diagram text,.flow-chart text {
111+
font-size:15px !important;
112+
font-family:"Source Sans Pro",sans-serif !important
113+
}
114+
.sequence-diagram [fill="#ffffff"],.flow-chart [fill="#ffffff"] {
115+
fill:#f6f6f6
116+
}
117+
.sequence-diagram [stroke="#000000"],.flow-chart [stroke="#000000"] {
118+
stroke:#3f3f3f
119+
}
120+
.sequence-diagram text[stroke="#000000"],.flow-chart text[stroke="#000000"] {
121+
stroke:none
122+
}
123+
.sequence-diagram [fill="#000"],.flow-chart [fill="#000"],.sequence-diagram [fill="#000000"],.flow-chart [fill="#000000"],.sequence-diagram [fill="black"],.flow-chart [fill="black"] {
124+
fill:#3f3f3f
125+
}
126+
ul,ol {
127+
margin-bottom:1.1em
128+
}
129+
ul ul,ol ul,ul ol,ol ol {
130+
margin-bottom:1.1em
131+
}
132+
kbd {
133+
padding:.1em .6em;
134+
border:1px solid rgba(63,63,63,0.25);
135+
-webkit-box-shadow:0 1px 0 rgba(63,63,63,0.25);
136+
box-shadow:0 1px 0 rgba(63,63,63,0.25);
137+
font-size:.7em;
138+
font-family:sans-serif;
139+
background-color:#fff;
140+
color:#333;
141+
border-radius:3px;
142+
display:inline-block;
143+
margin:0 .1em;
144+
white-space:nowrap
145+
}
146+
.toc ul {
147+
list-style-type:none;
148+
margin-bottom:15px
149+
}
150+
</style>
151+
<!-- 该css供自定义样式 -->
152+
<link href="../leanote-html.css" rel="stylesheet">
153+
</head>
154+
155+
<body>
156+
157+
<div class="note-container">
158+
<h1 class="title" id="leanote-title">{title}</h1>
159+
<div class="content-html" id="leanote-content">{content}</div>
160+
</div>
161+
162+
<!-- 该js供其它处理 -->
163+
<script src="../leanote-html.js"></script>
164+
</body>
165+
</html>

0 commit comments

Comments
 (0)