summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCheng-Han, Wu2016-06-21 21:42:03 +0800
committerCheng-Han, Wu2016-06-21 21:42:03 +0800
commit558304ff62a648e604b03afe3372ef9566aea850 (patch)
treef01521782685784508e4e5017d25a9c6b7768d06
parentad6982e77e1948bd7b74538af0c5a24120a38370 (diff)
Update to support new metadata: title, description, tags and google-analytics (GA) and refactor render publish slide response function
Diffstat (limited to '')
-rw-r--r--lib/models/note.js38
-rw-r--r--lib/response.js50
-rw-r--r--public/docs/yaml-metadata.md35
-rw-r--r--public/js/extra.js16
-rw-r--r--public/js/history.js32
-rw-r--r--public/views/ga.ejs18
-rw-r--r--public/views/pretty.ejs7
-rw-r--r--public/views/slide.ejs8
8 files changed, 160 insertions, 44 deletions
diff --git a/lib/models/note.js b/lib/models/note.js
index a442f889..db0493b2 100644
--- a/lib/models/note.js
+++ b/lib/models/note.js
@@ -5,6 +5,7 @@ var fs = require('fs');
var path = require('path');
var LZString = require('lz-string');
var marked = require('marked');
+var metaMarked = require('meta-marked');
var cheerio = require('cheerio');
var shortId = require('shortid');
var Sequelize = require("sequelize");
@@ -187,13 +188,24 @@ module.exports = function (sequelize, DataTypes) {
});
},
parseNoteTitle: function (body) {
- var $ = cheerio.load(marked(body));
- var h1s = $("h1");
var title = "";
- if (h1s.length > 0 && h1s.first().text().split('\n').length == 1)
- title = h1s.first().text();
- else
- title = "Untitled";
+ var meta = null;
+ try {
+ var obj = metaMarked(body);
+ body = obj.markdown;
+ meta = obj.meta;
+ } catch (err) {
+ //na
+ }
+ if (meta && meta.title && (typeof meta.title == "string" || typeof meta.title == "number")) {
+ title = meta.title;
+ } else {
+ var $ = cheerio.load(marked(body));
+ var h1s = $("h1");
+ if (h1s.length > 0 && h1s.first().text().split('\n').length == 1)
+ title = h1s.first().text();
+ }
+ if (!title) title = "Untitled";
return title;
},
decodeTitle: function (title) {
@@ -205,6 +217,20 @@ module.exports = function (sequelize, DataTypes) {
generateWebTitle: function (title) {
title = !title || title == "Untitled" ? "HackMD - Collaborative markdown notes" : title + " - HackMD";
return title;
+ },
+ parseMeta: function (meta) {
+ var _meta = {};
+ if (meta) {
+ if (meta.title && (typeof meta.title == "string" || typeof meta.title == "number"))
+ _meta.title = meta.title;
+ if (meta.description && (typeof meta.description == "string" || typeof meta.description == "number"))
+ _meta.description = meta.description;
+ if (meta.robots && (typeof meta.robots == "string" || typeof meta.robots == "number"))
+ _meta.robots = meta.robots;
+ if (meta.GA && (typeof meta.GA == "string" || typeof meta.GA == "number"))
+ _meta.GA = meta.GA;
+ }
+ return _meta;
}
},
hooks: {
diff --git a/lib/response.js b/lib/response.js
index 5d7fc0a1..133d7a37 100644
--- a/lib/response.js
+++ b/lib/response.js
@@ -21,7 +21,7 @@ var models = require("./models");
var md = require('reveal.js/plugin/markdown/markdown');
//reveal.js
-var opts = {
+var slideOptions = {
template: fs.readFileSync(config.slidepath).toString(),
theme: 'css/theme/black.css',
highlightTheme: 'zenburn',
@@ -107,7 +107,7 @@ function responseHackMD(res, note) {
var body = LZString.decompressFromBase64(note.content);
var meta = null;
try {
- meta = metaMarked(body).meta;
+ meta = models.Note.parseMeta(metaMarked(body).meta);
} catch(err) {
//na
}
@@ -121,7 +121,7 @@ function responseHackMD(res, note) {
var compiled = ejs.compile(fs.readFileSync(template, 'utf8'), options);
var html = compiled({
url: config.serverurl,
- title: title,
+ title: meta.title || title,
useCDN: config.usecdn,
facebook: config.facebook,
twitter: config.twitter,
@@ -212,7 +212,7 @@ function showPublishNote(req, res, next) {
var body = LZString.decompressFromBase64(note.content);
var meta = null;
try {
- meta = metaMarked(body).meta;
+ meta = models.Note.parseMeta(metaMarked(body).meta);
} catch(err) {
//na
}
@@ -223,7 +223,8 @@ function showPublishNote(req, res, next) {
title = models.Note.generateWebTitle(title);
var origin = config.serverurl;
var data = {
- title: title,
+ title: meta.title || title,
+ description: meta.description,
viewcount: note.viewcount,
createtime: createtime,
updatetime: updatetime,
@@ -231,7 +232,8 @@ function showPublishNote(req, res, next) {
body: text,
useCDN: config.usecdn,
lastchangeuserprofile: note.lastchangeuser ? models.User.parseProfile(note.lastchangeuser.profile) : null,
- robots: (meta && meta.robots) || false //default allow robots
+ robots: meta.robots || false, //default allow robots
+ GA: meta.GA
};
return renderPublish(data, res);
}).catch(function (err) {
@@ -527,14 +529,28 @@ function showPublishSlide(req, res, next) {
}
var body = LZString.decompressFromBase64(note.content);
try {
- body = metaMarked(body).markdown;
+ var obj = metaMarked(body);
+ body = obj.markdown;
+ meta = models.Note.parseMeta(obj.meta);
} catch(err) {
//na
}
+ var text = S(body).escapeHTML().s;
var title = models.Note.decodeTitle(note.title);
title = models.Note.generateWebTitle(title);
- var text = S(body).escapeHTML().s;
- render(res, title, text);
+ var slides = md.slidify(text, slideOptions);
+ var origin = config.serverurl;
+ var data = {
+ url: origin,
+ title: meta.title || title,
+ description: meta.description,
+ theme: slideOptions.theme,
+ highlightTheme: slideOptions.highlightTheme,
+ slides: slides,
+ options: JSON.stringify(slideOptions.revealOptions, null, 2),
+ GA: meta.GA
+ };
+ return renderPublishSlide(data, res);
}).catch(function (err) {
logger.error(err);
return response.errorInternalError(res);
@@ -542,24 +558,14 @@ function showPublishSlide(req, res, next) {
});
}
-//reveal.js render
-var render = function (res, title, markdown) {
- var slides = md.slidify(markdown, opts);
-
+function renderPublishSlide(data, res) {
var template = config.slidepath;
var options = {
cache: !config.debug,
filename: template
};
var compiled = ejs.compile(fs.readFileSync(template, 'utf8'), options);
- var html = compiled({
- url: config.serverurl,
- title: title,
- theme: opts.theme,
- highlightTheme: opts.highlightTheme,
- slides: slides,
- options: JSON.stringify(opts.revealOptions, null, 2)
- });
+ var html = compiled(data);
var buf = html;
res.writeHead(200, {
'Content-Type': 'text/html; charset=UTF-8',
@@ -567,6 +573,6 @@ var render = function (res, title, markdown) {
'Content-Length': buf.length
});
res.end(buf);
-};
+}
module.exports = response;
diff --git a/public/docs/yaml-metadata.md b/public/docs/yaml-metadata.md
index 5fc6e1b8..539410c8 100644
--- a/public/docs/yaml-metadata.md
+++ b/public/docs/yaml-metadata.md
@@ -18,6 +18,39 @@ YAML metas
Replace the "YAML metas" in this section with any YAML options as below.
You can also refer to this note's source code.
+title
+---
+This option will set the note title which prior than content title.
+
+> default: not set
+
+**Example**
+```xml
+title: meta title
+```
+
+description
+---
+This option will set the note description.
+
+> default: not set
+
+**Example**
+```xml
+description: meta description
+```
+
+tags
+---
+This option will set the tags which prior than content tags.
+
+> default: not set
+
+**Example**
+```xml
+tags: features, cool, updated
+```
+
robots
---
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:
```
So you can prevent any search engine index your note by set `noindex, nofollow`.
-> default: not
+> default: not set
**Example**
```xml
diff --git a/public/js/extra.js b/public/js/extra.js
index 953770be..e67eee53 100644
--- a/public/js/extra.js
+++ b/public/js/extra.js
@@ -43,12 +43,16 @@ function updateLastChangeUser() {
//get title
function getTitle(view) {
- var h1s = view.find("h1");
var title = "";
- if (h1s.length > 0) {
- title = h1s.first().text();
+ if (md && md.meta && md.meta.title && (typeof md.meta.title == "string" || typeof md.meta.title == "number")) {
+ title = md.meta.title;
} else {
- title = null;
+ var h1s = view.find("h1");
+ if (h1s.length > 0) {
+ title = h1s.first().text();
+ } else {
+ title = null;
+ }
}
return title;
}
@@ -93,7 +97,7 @@ function parseMeta(md, edit, view, toc, tocAffix) {
spellcheck = meta.spellcheck;
}
//text language
- if (lang) {
+ if (lang && typeof lang == "string") {
view.attr('lang', lang);
toc.attr('lang', lang);
tocAffix.attr('lang', lang);
@@ -107,7 +111,7 @@ function parseMeta(md, edit, view, toc, tocAffix) {
edit.removeAttr('lang', lang);
}
//text direction
- if (dir) {
+ if (dir && typeof dir == "string") {
view.attr('dir', dir);
toc.attr('dir', dir);
tocAffix.attr('dir', dir);
diff --git a/public/js/history.js b/public/js/history.js
index 9bdca709..0840580d 100644
--- a/public/js/history.js
+++ b/public/js/history.js
@@ -202,28 +202,44 @@ function writeHistoryToStorage(view) {
}
}
+if (!Array.isArray) {
+ Array.isArray = function(arg) {
+ return Object.prototype.toString.call(arg) === '[object Array]';
+ };
+}
+
function renderHistory(view) {
var title = renderFilename(view);
var tags = [];
var rawtags = [];
- view.find('h6').each(function (key, value) {
- if (/^tags/gmi.test($(value).text())) {
- var codes = $(value).find("code");
- for (var i = 0; i < codes.length; i++)
- rawtags.push(codes[i]);
+ if (md && md.meta && md.meta.tags && (typeof md.meta.tags == "string" || typeof md.meta.tags == "number")) {
+ var metaTags = ('' + md.meta.tags).split(',');
+ for (var i = 0; i < metaTags.length; i++) {
+ var text = metaTags[i].trim();
+ if (text) rawtags.push(text);
}
- });
+ } else {
+ view.find('h6').each(function (key, value) {
+ if (/^tags/gmi.test($(value).text())) {
+ var codes = $(value).find("code");
+ for (var i = 0; i < codes.length; i++) {
+ var text = codes[i].innerHTML.trim();
+ if (text) rawtags.push(text);
+ }
+ }
+ });
+ }
for (var i = 0; i < rawtags.length; i++) {
var found = false;
for (var j = 0; j < tags.length; j++) {
- if (tags[j] == rawtags[i].innerHTML) {
+ if (tags[j] == rawtags[i]) {
found = true;
break;
}
}
if (!found)
- tags.push(rawtags[i].innerHTML);
+ tags.push(rawtags[i]);
}
//console.debug(tags);
var id = urlpath ? location.pathname.slice(urlpath.length + 1, location.pathname.length).split('/')[1] : location.pathname.split('/')[1];
diff --git a/public/views/ga.ejs b/public/views/ga.ejs
new file mode 100644
index 00000000..180832d1
--- /dev/null
+++ b/public/views/ga.ejs
@@ -0,0 +1,18 @@
+<% if(typeof GA !== 'undefined' && GA) { %>
+<script>
+(function (i, s, o, g, r, a, m) {
+ i['GoogleAnalyticsObject'] = r;
+ i[r] = i[r] || function () {
+ (i[r].q = i[r].q || []).push(arguments)
+ }, i[r].l = 1 * new Date();
+ a = s.createElement(o),
+ m = s.getElementsByTagName(o)[0];
+ a.async = 1;
+ a.src = g;
+ m.parentNode.insertBefore(a, m)
+})(window, document, 'script', '//www.google-analytics.com/analytics.js', 'ga');
+
+ga('create', '<%- GA %>', 'auto');
+ga('send', 'pageview');
+</script>
+<% } %> \ No newline at end of file
diff --git a/public/views/pretty.ejs b/public/views/pretty.ejs
index 0a541107..5ba2e8e1 100644
--- a/public/views/pretty.ejs
+++ b/public/views/pretty.ejs
@@ -11,6 +11,9 @@
<% if(typeof robots !== 'undefined' && robots) { %>
<meta name="robots" content="<%- robots %>">
<% } %>
+ <% if(typeof description !== 'undefined' && description) { %>
+ <meta name="description" content="<%- description %>">
+ <% } %>
<title><%- title %></title>
<link rel="icon" type="image/png" href="<%- url %>/favicon.png">
<link rel="apple-touch-icon" href="<%- url %>/apple-touch-icon.png">
@@ -117,4 +120,6 @@
<script src="<%- url %>/js/common.js" defer></script>
<script src="<%- url %>/js/extra.js" defer></script>
<script src="<%- url %>/js/render.js" defer></script>
-<script src="<%- url %>/js/pretty.js" defer></script> \ No newline at end of file
+<script src="<%- url %>/js/pretty.js" defer></script>
+
+<%- include ga %> \ No newline at end of file
diff --git a/public/views/slide.ejs b/public/views/slide.ejs
index 27ccfdf8..3fe30944 100644
--- a/public/views/slide.ejs
+++ b/public/views/slide.ejs
@@ -5,6 +5,12 @@
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta name="mobile-web-app-capable" content="yes">
+ <% if(typeof robots !== 'undefined' && robots) { %>
+ <meta name="robots" content="<%- robots %>">
+ <% } %>
+ <% if(typeof description !== 'undefined' && description) { %>
+ <meta name="description" content="<%- description %>">
+ <% } %>
<title><%- title %></title>
<link rel="icon" type="image/png" href="<%- url %>/favicon.png">
<link rel="apple-touch-icon" href="<%- url %>/apple-touch-icon.png">
@@ -83,3 +89,5 @@
</script>
</body>
</html>
+
+<%- include ga %> \ No newline at end of file