summaryrefslogtreecommitdiff
path: root/public/js/extra.js
diff options
context:
space:
mode:
authorWu Cheng-Han2015-07-02 00:10:20 +0800
committerWu Cheng-Han2015-07-02 00:10:20 +0800
commit10c9811fc534a2738c19d8f74a447ed500b730a2 (patch)
tree8e46f99f36660d9c011d135fc6ce736733a5876b /public/js/extra.js
parentf7f8c901f4bc39c3ed0a2bdfe1cbaa1ee6957999 (diff)
Jump to 0.3.1
Diffstat (limited to 'public/js/extra.js')
-rw-r--r--public/js/extra.js293
1 files changed, 230 insertions, 63 deletions
diff --git a/public/js/extra.js b/public/js/extra.js
index 495c5677..f6d47647 100644
--- a/public/js/extra.js
+++ b/public/js/extra.js
@@ -1,43 +1,75 @@
+//auto update last change
+var lastchangetime = null;
+var lastchangeui = null;
+
+function updateLastChange() {
+ if (lastchangetime && lastchangeui) {
+ lastchangeui.html('&nbsp;<i class="fa fa-clock-o"></i> change ' + moment(lastchangetime).fromNow());
+ lastchangeui.attr('title', moment(lastchangetime).format('llll'));
+ }
+}
+setInterval(updateLastChange, 60000);
+
//get title
function getTitle(view) {
var h1s = view.find("h1");
var title = "";
- if (h1s.length > 0) {
+ if (h1s.length > 0) {
title = h1s.first().text();
} else {
title = null;
}
return title;
}
+
//render title
function renderTitle(view) {
var title = getTitle(view);
- if (title) {
+ if (title) {
title += ' - HackMD';
} else {
title = 'HackMD - Collaborative notes';
}
return title;
}
+
//render filename
function renderFilename(view) {
var filename = getTitle(view);
- if (!filename) {
+ if (!filename) {
filename = 'Untitled';
}
return filename;
}
+function slugifyWithUTF8(text) {
+ var newText = S(text.toLowerCase()).trim().stripTags().dasherize().s;
+ newText = newText.replace(/([\!\"\#\$\%\&\'\(\)\*\+\,\.\/\:\;\<\=\>\?\@\[\\\]\^\`\{\|\}\~])/g, '');
+ return newText;
+}
+
var viewAjaxCallback = null;
+//regex for blockquote
+var spaceregex = /\s*/;
+var notinhtmltagregex = /(?![^<]*>|[^<>]*<\/)/;
+var coloregex = /\[color=([#|\(|\)|\s|\,|\w]*?)\]/;
+coloregex = new RegExp(coloregex.source + notinhtmltagregex.source, "g");
+var nameregex = /\[name=(.*?)\]/;
+var timeregex = /\[time=([:|,|+|-|\(|\)|\s|\w]*?)\]/;
+var nameandtimeregex = new RegExp(nameregex.source + spaceregex.source + timeregex.source + notinhtmltagregex.source, "g");
+nameregex = new RegExp(nameregex.source + notinhtmltagregex.source, "g");
+timeregex = new RegExp(timeregex.source + notinhtmltagregex.source, "g");
+
//dynamic event or object binding here
function finishView(view) {
//youtube
- view.find(".youtube").click(function () {
- imgPlayiframe(this, '//www.youtube.com/embed/');
- });
+ view.find(".youtube.raw").removeClass("raw")
+ .click(function () {
+ imgPlayiframe(this, '//www.youtube.com/embed/');
+ });
//vimeo
- view.find(".vimeo")
+ view.find(".vimeo.raw").removeClass("raw")
.click(function () {
imgPlayiframe(this, '//player.vimeo.com/video/');
})
@@ -54,35 +86,32 @@ function finishView(view) {
});
});
//gist
- view.find("code[data-gist-id]").each(function(key, value) {
- if($(value).children().length == 0)
+ view.find("code[data-gist-id]").each(function (key, value) {
+ if ($(value).children().length == 0)
$(value).gist(viewAjaxCallback);
});
//emojify
emojify.run(view[0]);
//mathjax
- var mathjaxdivs = view.find('.mathjax').toArray();
+ var mathjaxdivs = view.find('.mathjax.raw').removeClass("raw").toArray();
try {
for (var i = 0; i < mathjaxdivs.length; i++) {
MathJax.Hub.Queue(["Typeset", MathJax.Hub, mathjaxdivs[i].innerHTML]);
MathJax.Hub.Queue(viewAjaxCallback);
- $(mathjaxdivs[i]).removeClass("mathjax");
}
- } catch(err) {
- }
+ } catch (err) {}
//sequence diagram
- var sequence = view.find(".sequence-diagram");
+ var sequence = view.find(".sequence-diagram.raw").removeClass("raw");
try {
sequence.sequenceDiagram({
theme: 'simple'
});
sequence.parent().parent().replaceWith(sequence);
- sequence.removeClass("sequence-diagram");
- } catch(err) {
+ } catch (err) {
console.error(err);
}
//flowchart
- var flow = view.find(".flow-chart");
+ var flow = view.find(".flow-chart.raw").removeClass("raw");
flow.each(function (key, value) {
try {
var chart = flowchart.parse($(value).text());
@@ -94,26 +123,41 @@ function finishView(view) {
'font-family': "'Andale Mono', monospace"
});
$(value).parent().parent().replaceWith(value);
- $(value).removeClass("flow-chart");
- } catch(err) {
+ } catch (err) {
console.error(err);
}
});
+ //image href new window(emoji not included)
+ var images = view.find("p > img[src]:not([class])");
+ images.each(function (key, value) {
+ var src = $(value).attr('src');
+ var a = $('<a>');
+ if (src) {
+ a.attr('href', src);
+ a.attr('target', "_blank");
+ }
+ a.html($(value).clone());
+ $(value).replaceWith(a);
+ });
+ //blockquote
+ var blockquote = view.find("blockquote.raw").removeClass("raw");
+ var blockquote_p = blockquote.find("p");
+ blockquote_p.each(function (key, value) {
+ var html = $(value).html();
+ html = html.replace(coloregex, '<span class="color" data-color="$1"></span>');
+ html = html.replace(nameandtimeregex, '<small><i class="fa fa-user"></i> $1 <i class="fa fa-clock-o"></i> $2</small>');
+ html = html.replace(nameregex, '<small><i class="fa fa-user"></i> $1</small>');
+ html = html.replace(timeregex, '<small><i class="fa fa-clock-o"></i> $1</small>');
+ $(value).html(html);
+ });
+ var blockquote_color = blockquote.find(".color");
+ blockquote_color.each(function (key, value) {
+ $(value).closest("blockquote").css('border-left-color', $(value).attr('data-color'));
+ });
//render title
document.title = renderTitle(view);
}
-//regex for blockquote
-var spaceregex = /\s*/;
-var notinhtmltagregex = /(?![^<]*>|[^<>]*<\/)/;
-var coloregex = /\[color=([#|\(|\)|\s|\,|\w]*)\]/;
-coloregex = new RegExp(coloregex.source + notinhtmltagregex.source, "g");
-var nameregex = /\[name=([-|_|\s|\w]*)\]/;
-var timeregex = /\[time=([:|,|+|-|\(|\)|\s|\w]*)\]/;
-var nameandtimeregex = new RegExp(nameregex.source + spaceregex.source + timeregex.source + notinhtmltagregex.source, "g");
-nameregex = new RegExp(nameregex.source + notinhtmltagregex.source, "g");
-timeregex = new RegExp(timeregex.source + notinhtmltagregex.source, "g");
-
//only static transform should be here
function postProcess(code) {
var result = $('<div>' + code + '</div>');
@@ -125,30 +169,105 @@ function postProcess(code) {
return "<noiframe>" + $(this).html() + "</noiframe>"
});
//todo list
- var lis = result[0].getElementsByTagName('li');
+ var lis = result.find('li.raw').removeClass("raw").sortByDepth().toArray();
for (var i = 0; i < lis.length; i++) {
- var html = lis[i].innerHTML;
- if (/^\s*\[[x ]\]\s+/.test(html)) {
- lis[i].innerHTML = html.replace(/^\s*\[ \]\s*/, '<input type="checkbox" class="task-list-item-checkbox" disabled>')
- .replace(/^\s*\[x\]\s*/, '<input type="checkbox" class="task-list-item-checkbox" checked disabled>');
+ var li = lis[i];
+ var html = $(li).clone()[0].innerHTML;
+ var p = $(li).children('p');
+ if (p.length == 1) {
+ html = p.html();
+ li = p[0];
+ }
+ if (/^\s*\[[x ]\]\s*/.test(html)) {
+ li.innerHTML = html.replace(/^\s*\[ \]\s*/, '<input type="checkbox" class="task-list-item-checkbox" disabled><label></label>')
+ .replace(/^\s*\[x\]\s*/, '<input type="checkbox" class="task-list-item-checkbox" checked disabled><label></label>');
lis[i].setAttribute('class', 'task-list-item');
}
}
- //blockquote
- var blockquote = result.find("blockquote");
- blockquote.each(function (key, value) {
- var html = $(value).html();
- html = html.replace(coloregex, '<span class="color" data-color="$1"></span>');
- html = html.replace(nameandtimeregex, '<small><i class="fa fa-user"></i> $1 <i class="fa fa-clock-o"></i> $2</small>');
- html = html.replace(nameregex, '<small><i class="fa fa-user"></i> $1</small>');
- html = html.replace(timeregex, '<small><i class="fa fa-clock-o"></i> $1</small>');
- $(value).html(html);
+ return result;
+}
+
+//jQuery sortByDepth
+$.fn.sortByDepth = function () {
+ var ar = this.map(function () {
+ return {
+ length: $(this).parents().length,
+ elt: this
+ }
+ }).get(),
+ result = [],
+ i = ar.length;
+ ar.sort(function (a, b) {
+ return a.length - b.length;
});
- var blockquotecolor = result.find("blockquote .color");
- blockquotecolor.each(function (key, value) {
- $(value).closest("blockquote").css('border-left-color', $(value).attr('data-color'));
+ while (i--) {
+ result.push(ar[i].elt);
+ }
+ return $(result);
+};
+
+//remove hash
+function removeHash() {
+ history.pushState("", document.title, window.location.pathname + window.location.search);
+}
+
+//toc
+function generateToc(id) {
+ var target = $('#' + id);
+ target.html('');
+ new Toc('doc', {
+ 'level': 3,
+ 'top': -1,
+ 'class': 'toc',
+ 'targetId': id
});
- return result;
+ if(target.text() == 'undefined')
+ target.html('');
+ var backtotop = $('<a class="back-to-top" href="#">Back to top</a>');
+ var gotobottom = $('<a class="go-to-bottom" href="#">Go to bottom</a>');
+ backtotop.click(function (e) {
+ e.preventDefault();
+ e.stopPropagation();
+ if (scrollToTop)
+ scrollToTop();
+ removeHash();
+ });
+ gotobottom.click(function (e) {
+ e.preventDefault();
+ e.stopPropagation();
+ if (scrollToBottom)
+ scrollToBottom();
+ removeHash();
+ });
+ target.append(backtotop).append(gotobottom);
+}
+
+//smooth all hash trigger scrolling
+function smoothHashScroll() {
+ var hashElements = $("a[href^='#']:not([smoothhashscroll])").toArray();
+ for (var i = 0; i < hashElements.length; i++) {
+ var element = hashElements[i];
+ var $element = $(element);
+ var hash = element.hash;
+ if (hash) {
+ $element.on('click', function (e) {
+ // store hash
+ var hash = this.hash;
+ if ($(hash).length <= 0) return;
+ // prevent default anchor click behavior
+ e.preventDefault();
+ // animate
+ $('html, body').animate({
+ scrollTop: $(hash).offset().top
+ }, 100, "linear", function () {
+ // when done, add hash to url
+ // (default click behaviour)
+ window.location.hash = hash;
+ });
+ });
+ $element.attr('smoothhashscroll', '');
+ }
+ }
}
function setSizebyAttr(element, target) {
@@ -168,10 +287,10 @@ function imgPlayiframe(element, src) {
var anchorForId = function (id) {
var anchor = document.createElement("a");
- anchor.className = "header-link";
+ anchor.className = "header-link hidden-xs";
anchor.href = "#" + id;
- anchor.innerHTML = "<span class=\"sr-only\">Permalink</span><i class=\"fa fa-link\"></i>";
- anchor.title = "Permalink";
+ anchor.innerHTML = "<span class=\"sr-only\"></span><i class=\"fa fa-link\"></i>";
+ anchor.title = id;
return anchor;
};
@@ -179,12 +298,14 @@ var linkifyAnchors = function (level, containingElement) {
var headers = containingElement.getElementsByTagName("h" + level);
for (var h = 0; h < headers.length; h++) {
var header = headers[h];
-
- if (typeof header.id == "undefined" || header.id == "") {
- var id = S(header.innerHTML.toLowerCase()).trim().stripTags().dasherize().s;
- header.id = encodeURIComponent(id);
+ if (header.getElementsByClassName("header-link").length == 0) {
+ if (typeof header.id == "undefined" || header.id == "") {
+ //to escape characters not allow in css and humanize
+ var id = slugifyWithUTF8(header.innerHTML);
+ header.id = id;
+ }
+ header.appendChild(anchorForId(header.id));
}
- header.appendChild(anchorForId(header.id));
}
};
@@ -207,10 +328,10 @@ function scrollToHash() {
function highlightRender(code, lang) {
if (!lang || /no(-?)highlight|plain|text/.test(lang))
return;
- if(lang == 'sequence') {
- return '<div class="sequence-diagram">' + code + '</div>';
- } else if(lang == 'flow') {
- return '<div class="flow-chart">' + code + '</div>';
+ if (lang == 'sequence') {
+ return '<div class="sequence-diagram raw">' + code + '</div>';
+ } else if (lang == 'flow') {
+ return '<div class="flow-chart raw">' + code + '</div>';
}
var reallang = lang.replace('=', '');
var languages = hljs.listLanguages();
@@ -238,10 +359,56 @@ emojify.setConfig({
var md = new Remarkable('full', {
html: true,
+ breaks: true,
+ langPrefix: "",
linkify: true,
typographer: true,
highlight: highlightRender
});
+md.renderer.rules.list_item_open = function (/* tokens, idx, options, env */) {
+ return '<li class="raw">';
+};
+md.renderer.rules.blockquote_open = function (tokens, idx /*, options, env */ ) {
+ return '<blockquote class="raw">\n';
+};
+md.renderer.rules.hardbreak = function (tokens, idx, options /*, env */ ) {
+ return md.options.xhtmlOut ? '<br /><br />' : '<br><br>';
+};
+md.renderer.rules.fence = function (tokens, idx, options, env, self) {
+ var token = tokens[idx];
+ var langClass = '';
+ var langPrefix = options.langPrefix;
+ var langName = '',
+ fenceName;
+ var highlighted;
+
+ if (token.params) {
+
+ //
+ // ```foo bar
+ //
+ // Try custom renderer "foo" first. That will simplify overwrite
+ // for diagrams, latex, and any other fenced block with custom look
+ //
+
+ fenceName = token.params.split(/\s+/g)[0];
+
+ if (Remarkable.utils.has(self.rules.fence_custom, fenceName)) {
+ return self.rules.fence_custom[fenceName](tokens, idx, options, env, self);
+ }
+
+ langName = Remarkable.utils.escapeHtml(Remarkable.utils.replaceEntities(Remarkable.utils.unescapeMd(fenceName)));
+ langClass = ' class="' + langPrefix + langName.replace('=', '') + ' hljs"';
+ }
+
+ if (options.highlight) {
+ highlighted = options.highlight(token.content, langName) || Remarkable.utils.escapeHtml(token.content);
+ } else {
+ highlighted = Remarkable.utils.escapeHtml(token.content);
+ }
+
+ return '<pre><code' + langClass + '>' + highlighted + '</code></pre>' + md.renderer.getBreak(tokens, idx);
+};
//youtube
var youtubePlugin = new Plugin(
// regexp to match
@@ -251,7 +418,7 @@ var youtubePlugin = new Plugin(
function (match, utils) {
var videoid = match[1];
if (!videoid) return;
- var div = $('<div class="youtube"></div>');
+ var div = $('<div class="youtube raw"></div>');
setSizebyAttr(div, div);
div.attr('videoid', videoid);
var icon = '<i class="icon fa fa-youtube-play fa-5x"></i>';
@@ -270,7 +437,7 @@ var vimeoPlugin = new Plugin(
function (match, utils) {
var videoid = match[1];
if (!videoid) return;
- var div = $('<div class="vimeo"></div>');
+ var div = $('<div class="vimeo raw"></div>');
setSizebyAttr(div, div);
div.attr('videoid', videoid);
var icon = '<i class="icon fa fa-vimeo-square fa-5x"></i>';
@@ -298,7 +465,7 @@ var mathjaxPlugin = new Plugin(
// this function will be called when something matches
function (match, utils) {
//var code = $(match).text();
- return '<span class="mathjax">' + match[0] + '</span>';
+ return '<span class="mathjax raw">' + match[0] + '</span>';
}
);
md.use(youtubePlugin);