Skip to content

Commit 558304f

Browse files
committed
Update to support new metadata: title, description, tags and google-analytics (GA) and refactor render publish slide response function
1 parent ad6982e commit 558304f

File tree

8 files changed

+160
-44
lines changed

8 files changed

+160
-44
lines changed

lib/models/note.js

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ var fs = require('fs');
55
var path = require('path');
66
var LZString = require('lz-string');
77
var marked = require('marked');
8+
var metaMarked = require('meta-marked');
89
var cheerio = require('cheerio');
910
var shortId = require('shortid');
1011
var Sequelize = require("sequelize");
@@ -187,13 +188,24 @@ module.exports = function (sequelize, DataTypes) {
187188
});
188189
},
189190
parseNoteTitle: function (body) {
190-
var $ = cheerio.load(marked(body));
191-
var h1s = $("h1");
192191
var title = "";
193-
if (h1s.length > 0 && h1s.first().text().split('\n').length == 1)
194-
title = h1s.first().text();
195-
else
196-
title = "Untitled";
192+
var meta = null;
193+
try {
194+
var obj = metaMarked(body);
195+
body = obj.markdown;
196+
meta = obj.meta;
197+
} catch (err) {
198+
//na
199+
}
200+
if (meta && meta.title && (typeof meta.title == "string" || typeof meta.title == "number")) {
201+
title = meta.title;
202+
} else {
203+
var $ = cheerio.load(marked(body));
204+
var h1s = $("h1");
205+
if (h1s.length > 0 && h1s.first().text().split('\n').length == 1)
206+
title = h1s.first().text();
207+
}
208+
if (!title) title = "Untitled";
197209
return title;
198210
},
199211
decodeTitle: function (title) {
@@ -205,6 +217,20 @@ module.exports = function (sequelize, DataTypes) {
205217
generateWebTitle: function (title) {
206218
title = !title || title == "Untitled" ? "HackMD - Collaborative markdown notes" : title + " - HackMD";
207219
return title;
220+
},
221+
parseMeta: function (meta) {
222+
var _meta = {};
223+
if (meta) {
224+
if (meta.title && (typeof meta.title == "string" || typeof meta.title == "number"))
225+
_meta.title = meta.title;
226+
if (meta.description && (typeof meta.description == "string" || typeof meta.description == "number"))
227+
_meta.description = meta.description;
228+
if (meta.robots && (typeof meta.robots == "string" || typeof meta.robots == "number"))
229+
_meta.robots = meta.robots;
230+
if (meta.GA && (typeof meta.GA == "string" || typeof meta.GA == "number"))
231+
_meta.GA = meta.GA;
232+
}
233+
return _meta;
208234
}
209235
},
210236
hooks: {

lib/response.js

Lines changed: 28 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ var models = require("./models");
2121
var md = require('reveal.js/plugin/markdown/markdown');
2222

2323
//reveal.js
24-
var opts = {
24+
var slideOptions = {
2525
template: fs.readFileSync(config.slidepath).toString(),
2626
theme: 'css/theme/black.css',
2727
highlightTheme: 'zenburn',
@@ -107,7 +107,7 @@ function responseHackMD(res, note) {
107107
var body = LZString.decompressFromBase64(note.content);
108108
var meta = null;
109109
try {
110-
meta = metaMarked(body).meta;
110+
meta = models.Note.parseMeta(metaMarked(body).meta);
111111
} catch(err) {
112112
//na
113113
}
@@ -121,7 +121,7 @@ function responseHackMD(res, note) {
121121
var compiled = ejs.compile(fs.readFileSync(template, 'utf8'), options);
122122
var html = compiled({
123123
url: config.serverurl,
124-
title: title,
124+
title: meta.title || title,
125125
useCDN: config.usecdn,
126126
facebook: config.facebook,
127127
twitter: config.twitter,
@@ -212,7 +212,7 @@ function showPublishNote(req, res, next) {
212212
var body = LZString.decompressFromBase64(note.content);
213213
var meta = null;
214214
try {
215-
meta = metaMarked(body).meta;
215+
meta = models.Note.parseMeta(metaMarked(body).meta);
216216
} catch(err) {
217217
//na
218218
}
@@ -223,15 +223,17 @@ function showPublishNote(req, res, next) {
223223
title = models.Note.generateWebTitle(title);
224224
var origin = config.serverurl;
225225
var data = {
226-
title: title,
226+
title: meta.title || title,
227+
description: meta.description,
227228
viewcount: note.viewcount,
228229
createtime: createtime,
229230
updatetime: updatetime,
230231
url: origin,
231232
body: text,
232233
useCDN: config.usecdn,
233234
lastchangeuserprofile: note.lastchangeuser ? models.User.parseProfile(note.lastchangeuser.profile) : null,
234-
robots: (meta && meta.robots) || false //default allow robots
235+
robots: meta.robots || false, //default allow robots
236+
GA: meta.GA
235237
};
236238
return renderPublish(data, res);
237239
}).catch(function (err) {
@@ -527,46 +529,50 @@ function showPublishSlide(req, res, next) {
527529
}
528530
var body = LZString.decompressFromBase64(note.content);
529531
try {
530-
body = metaMarked(body).markdown;
532+
var obj = metaMarked(body);
533+
body = obj.markdown;
534+
meta = models.Note.parseMeta(obj.meta);
531535
} catch(err) {
532536
//na
533537
}
538+
var text = S(body).escapeHTML().s;
534539
var title = models.Note.decodeTitle(note.title);
535540
title = models.Note.generateWebTitle(title);
536-
var text = S(body).escapeHTML().s;
537-
render(res, title, text);
541+
var slides = md.slidify(text, slideOptions);
542+
var origin = config.serverurl;
543+
var data = {
544+
url: origin,
545+
title: meta.title || title,
546+
description: meta.description,
547+
theme: slideOptions.theme,
548+
highlightTheme: slideOptions.highlightTheme,
549+
slides: slides,
550+
options: JSON.stringify(slideOptions.revealOptions, null, 2),
551+
GA: meta.GA
552+
};
553+
return renderPublishSlide(data, res);
538554
}).catch(function (err) {
539555
logger.error(err);
540556
return response.errorInternalError(res);
541557
});
542558
});
543559
}
544560

545-
//reveal.js render
546-
var render = function (res, title, markdown) {
547-
var slides = md.slidify(markdown, opts);
548-
561+
function renderPublishSlide(data, res) {
549562
var template = config.slidepath;
550563
var options = {
551564
cache: !config.debug,
552565
filename: template
553566
};
554567
var compiled = ejs.compile(fs.readFileSync(template, 'utf8'), options);
555-
var html = compiled({
556-
url: config.serverurl,
557-
title: title,
558-
theme: opts.theme,
559-
highlightTheme: opts.highlightTheme,
560-
slides: slides,
561-
options: JSON.stringify(opts.revealOptions, null, 2)
562-
});
568+
var html = compiled(data);
563569
var buf = html;
564570
res.writeHead(200, {
565571
'Content-Type': 'text/html; charset=UTF-8',
566572
'Cache-Control': 'private',
567573
'Content-Length': buf.length
568574
});
569575
res.end(buf);
570-
};
576+
}
571577

572578
module.exports = response;

public/docs/yaml-metadata.md

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,39 @@ YAML metas
1818
Replace the "YAML metas" in this section with any YAML options as below.
1919
You can also refer to this note's source code.
2020

21+
title
22+
---
23+
This option will set the note title which prior than content title.
24+
25+
> default: not set
26+
27+
**Example**
28+
```xml
29+
title: meta title
30+
```
31+
32+
description
33+
---
34+
This option will set the note description.
35+
36+
> default: not set
37+
38+
**Example**
39+
```xml
40+
description: meta description
41+
```
42+
43+
tags
44+
---
45+
This option will set the tags which prior than content tags.
46+
47+
> default: not set
48+
49+
**Example**
50+
```xml
51+
tags: features, cool, updated
52+
```
53+
2154
robots
2255
---
2356
This option will give below meta in the note head meta:
@@ -26,7 +59,7 @@ This option will give below meta in the note head meta:
2659
```
2760
So you can prevent any search engine index your note by set `noindex, nofollow`.
2861

29-
> default: not
62+
> default: not set
3063
3164
**Example**
3265
```xml

public/js/extra.js

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,12 +43,16 @@ function updateLastChangeUser() {
4343

4444
//get title
4545
function getTitle(view) {
46-
var h1s = view.find("h1");
4746
var title = "";
48-
if (h1s.length > 0) {
49-
title = h1s.first().text();
47+
if (md && md.meta && md.meta.title && (typeof md.meta.title == "string" || typeof md.meta.title == "number")) {
48+
title = md.meta.title;
5049
} else {
51-
title = null;
50+
var h1s = view.find("h1");
51+
if (h1s.length > 0) {
52+
title = h1s.first().text();
53+
} else {
54+
title = null;
55+
}
5256
}
5357
return title;
5458
}
@@ -93,7 +97,7 @@ function parseMeta(md, edit, view, toc, tocAffix) {
9397
spellcheck = meta.spellcheck;
9498
}
9599
//text language
96-
if (lang) {
100+
if (lang && typeof lang == "string") {
97101
view.attr('lang', lang);
98102
toc.attr('lang', lang);
99103
tocAffix.attr('lang', lang);
@@ -107,7 +111,7 @@ function parseMeta(md, edit, view, toc, tocAffix) {
107111
edit.removeAttr('lang', lang);
108112
}
109113
//text direction
110-
if (dir) {
114+
if (dir && typeof dir == "string") {
111115
view.attr('dir', dir);
112116
toc.attr('dir', dir);
113117
tocAffix.attr('dir', dir);

public/js/history.js

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -202,28 +202,44 @@ function writeHistoryToStorage(view) {
202202
}
203203
}
204204

205+
if (!Array.isArray) {
206+
Array.isArray = function(arg) {
207+
return Object.prototype.toString.call(arg) === '[object Array]';
208+
};
209+
}
210+
205211
function renderHistory(view) {
206212
var title = renderFilename(view);
207213

208214
var tags = [];
209215
var rawtags = [];
210-
view.find('h6').each(function (key, value) {
211-
if (/^tags/gmi.test($(value).text())) {
212-
var codes = $(value).find("code");
213-
for (var i = 0; i < codes.length; i++)
214-
rawtags.push(codes[i]);
216+
if (md && md.meta && md.meta.tags && (typeof md.meta.tags == "string" || typeof md.meta.tags == "number")) {
217+
var metaTags = ('' + md.meta.tags).split(',');
218+
for (var i = 0; i < metaTags.length; i++) {
219+
var text = metaTags[i].trim();
220+
if (text) rawtags.push(text);
215221
}
216-
});
222+
} else {
223+
view.find('h6').each(function (key, value) {
224+
if (/^tags/gmi.test($(value).text())) {
225+
var codes = $(value).find("code");
226+
for (var i = 0; i < codes.length; i++) {
227+
var text = codes[i].innerHTML.trim();
228+
if (text) rawtags.push(text);
229+
}
230+
}
231+
});
232+
}
217233
for (var i = 0; i < rawtags.length; i++) {
218234
var found = false;
219235
for (var j = 0; j < tags.length; j++) {
220-
if (tags[j] == rawtags[i].innerHTML) {
236+
if (tags[j] == rawtags[i]) {
221237
found = true;
222238
break;
223239
}
224240
}
225241
if (!found)
226-
tags.push(rawtags[i].innerHTML);
242+
tags.push(rawtags[i]);
227243
}
228244
//console.debug(tags);
229245
var id = urlpath ? location.pathname.slice(urlpath.length + 1, location.pathname.length).split('/')[1] : location.pathname.split('/')[1];

public/views/ga.ejs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<% if(typeof GA !== 'undefined' && GA) { %>
2+
<script>
3+
(function (i, s, o, g, r, a, m) {
4+
i['GoogleAnalyticsObject'] = r;
5+
i[r] = i[r] || function () {
6+
(i[r].q = i[r].q || []).push(arguments)
7+
}, i[r].l = 1 * new Date();
8+
a = s.createElement(o),
9+
m = s.getElementsByTagName(o)[0];
10+
a.async = 1;
11+
a.src = g;
12+
m.parentNode.insertBefore(a, m)
13+
})(window, document, 'script', '//www.google-analytics.com/analytics.js', 'ga');
14+
15+
ga('create', '<%- GA %>', 'auto');
16+
ga('send', 'pageview');
17+
</script>
18+
<% } %>

public/views/pretty.ejs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@
1111
<% if(typeof robots !== 'undefined' && robots) { %>
1212
<meta name="robots" content="<%- robots %>">
1313
<% } %>
14+
<% if(typeof description !== 'undefined' && description) { %>
15+
<meta name="description" content="<%- description %>">
16+
<% } %>
1417
<title><%- title %></title>
1518
<link rel="icon" type="image/png" href="<%- url %>/favicon.png">
1619
<link rel="apple-touch-icon" href="<%- url %>/apple-touch-icon.png">
@@ -117,4 +120,6 @@
117120
<script src="<%- url %>/js/common.js" defer></script>
118121
<script src="<%- url %>/js/extra.js" defer></script>
119122
<script src="<%- url %>/js/render.js" defer></script>
120-
<script src="<%- url %>/js/pretty.js" defer></script>
123+
<script src="<%- url %>/js/pretty.js" defer></script>
124+
125+
<%- include ga %>

public/views/slide.ejs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@
55
<meta name="apple-mobile-web-app-capable" content="yes">
66
<meta name="apple-mobile-web-app-status-bar-style" content="black">
77
<meta name="mobile-web-app-capable" content="yes">
8+
<% if(typeof robots !== 'undefined' && robots) { %>
9+
<meta name="robots" content="<%- robots %>">
10+
<% } %>
11+
<% if(typeof description !== 'undefined' && description) { %>
12+
<meta name="description" content="<%- description %>">
13+
<% } %>
814
<title><%- title %></title>
915
<link rel="icon" type="image/png" href="<%- url %>/favicon.png">
1016
<link rel="apple-touch-icon" href="<%- url %>/apple-touch-icon.png">
@@ -83,3 +89,5 @@
8389
</script>
8490
</body>
8591
</html>
92+
93+
<%- include ga %>

0 commit comments

Comments
 (0)